diff --git a/.clang-format b/.clang-format index 674e1494ecd..d5d12e7490c 100644 --- a/.clang-format +++ b/.clang-format @@ -47,11 +47,11 @@ IncludeCategories: Priority: 2 CaseSensitive: true # O2Physics, non-PWG - - Regex: ^(<|")(Common|ALICE3|DPG|EventFiltering|Tools|Tutorials)/.*\.h + - Regex: ^(<|")(Common|ALICE3|DPG|EventFiltering|PID|Tools|Tutorials)/.*\.h Priority: 3 CaseSensitive: true # O2 - - Regex: ^(<|")(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MFT|MCH|MID|PHOS|PID|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*/.*\.h + - Regex: ^(<|")(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|FDD|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MCH|MFT|MID|PHOS|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*/.*\.h Priority: 4 CaseSensitive: true # ROOT @@ -93,6 +93,7 @@ IncludeCategories: # rest - Regex: .* Priority: 100 + CaseSensitive: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false diff --git a/.clang-tidy b/.clang-tidy index 1d933b5937e..f7f00391f25 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,21 +1,198 @@ +--- +Checks: + # - clang-analyzer-* # enabled by default + # - clang-diagnostic-* # enabled by default + - -clang-diagnostic-unknown-pragmas + - bugprone-* + - -bugprone-assert-side-effect + - -bugprone-bad-signal-to-kill-thread + - -bugprone-bitwise-pointer-cast + - -bugprone-bool-pointer-implicit-conversion + - -bugprone-compare-pointer-to-member-virtual-function + - -bugprone-crtp-constructor-accessibility + - -bugprone-easily-swappable-parameters # Too sensitive + - -bugprone-forwarding-reference-overload + - -bugprone-implicit-widening-of-multiplication-result # Specific cases irrelevant in this repo + - -bugprone-lambda-function-name + - -bugprone-no-escape + - -bugprone-posix-return + - -bugprone-signal-handler + - -bugprone-signed-char-misuse # Specific use cases. Probably very rare. + - -bugprone-spuriously-wake-up-functions + - -bugprone-suspicious-stringview-data-usage # Probably harmless in this repo + - -bugprone-unused-raii + - cppcoreguidelines-avoid-const-or-ref-data-members + - cppcoreguidelines-avoid-goto + - cppcoreguidelines-avoid-non-const-global-variables + - cppcoreguidelines-init-variables + - cppcoreguidelines-macro-usage + - cppcoreguidelines-misleading-capture-default-by-value + - cppcoreguidelines-missing-std-forward + - cppcoreguidelines-prefer-member-initializer + - cppcoreguidelines-pro-bounds-array-to-pointer-decay + - cppcoreguidelines-pro-type-const-cast + - cppcoreguidelines-pro-type-cstyle-cast + - cppcoreguidelines-pro-type-member-init + - cppcoreguidelines-pro-type-reinterpret-cast + - cppcoreguidelines-pro-type-static-cast-downcast + - cppcoreguidelines-pro-type-union-access + - cppcoreguidelines-rvalue-reference-param-not-moved + - cppcoreguidelines-slicing + - cppcoreguidelines-virtual-class-destructor + - google-default-arguments + # - google-explicit-constructor # triggered for O2 columns + - google-global-names-in-headers + - google-readability-casting # same as cpplint:readability/casting + # - misc-const-correctness # to be checked + - misc-header-include-cycle + - misc-include-cleaner + - misc-misplaced-const + - misc-redundant-expression + - misc-unconventional-assign-operator + - misc-unused-alias-decls + - misc-unused-parameters + - misc-unused-using-decls + - modernize-avoid-bind + - modernize-avoid-c-arrays + - modernize-concat-nested-namespaces + - modernize-deprecated-headers + - modernize-make-shared + - modernize-make-unique + - modernize-redundant-void-arg + - modernize-return-braced-init-list + - modernize-use-auto + - modernize-use-default-member-init + - modernize-use-designated-initializers + - modernize-use-equals-default + - modernize-use-equals-delete + - modernize-use-nodiscard + - modernize-use-nullptr + - modernize-use-override + - modernize-use-starts-ends-with + - performance-for-range-copy + - performance-implicit-conversion-in-loop + - performance-inefficient-algorithm + - performance-inefficient-string-concatenation + - performance-inefficient-vector-operation + - performance-move-const-arg + - performance-no-automatic-move + - performance-trivially-destructible + - performance-type-promotion-in-math-fn + - performance-unnecessary-copy-initialization + - performance-unnecessary-value-param # slow + - readability-avoid-unconditional-preprocessor-if + - readability-braces-around-statements + - readability-const-return-type + - readability-container-contains + - readability-container-data-pointer + - readability-container-size-empty + - readability-delete-null-pointer + - readability-duplicate-include + - readability-else-after-return + - readability-enum-initial-value + - readability-implicit-bool-conversion + - readability-inconsistent-declaration-parameter-name + - readability-misplaced-array-index + - readability-non-const-parameter + - readability-redundant-access-specifiers + - readability-redundant-casting + - readability-redundant-control-flow + - readability-redundant-declaration + - readability-redundant-member-init + - readability-redundant-preprocessor + - readability-redundant-string-cstr + - readability-redundant-string-init + - readability-reference-to-constructed-temporary + - readability-simplify-boolean-expr + - readability-static-definition-in-anonymous-namespace + - readability-string-compare + - readability-suspicious-call-argument +# Select which of the enabled checks should be reported as errors instead of warnings. +WarningsAsErrors: >- + *, + -readability-braces-around-statements, + -readability-suspicious-call-argument, + -bugprone-assignment-in-if-condition, + -bugprone-branch-clone, + -bugprone-copy-constructor-init, + -bugprone-empty-catch, + -bugprone-exception-escape, + -bugprone-fold-init-type, + -bugprone-incorrect-roundings, + -bugprone-integer-division, + -bugprone-macro-parentheses, + -bugprone-narrowing-conversions, + -bugprone-non-zero-enum-to-bool-conversion, + -bugprone-redundant-branch-condition, + -bugprone-reserved-identifier, + -bugprone-suspicious-include, + -bugprone-suspicious-missing-comma, + -bugprone-switch-missing-default-case, + -bugprone-too-small-loop-variable, + -bugprone-unchecked-optional-access, + -bugprone-unsafe-functions, + -bugprone-unused-local-non-trivial-variable, + -cppcoreguidelines-avoid-const-or-ref-data-members, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-init-variables, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-missing-std-forward, + -cppcoreguidelines-prefer-member-initializer, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-type-member-init, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-slicing, + -cppcoreguidelines-virtual-class-destructor, + -modernize-*, + -readability-*, + readability-redundant-casting, +# Positives for the following checks are currently being fixed and should be reported as errors. +# clang-analyzer-* +# clang-diagnostic-* +# cppcoreguidelines-pro-type-const-cast +# cppcoreguidelines-pro-type-cstyle-cast +# cppcoreguidelines-pro-type-reinterpret-cast +# cppcoreguidelines-pro-type-static-cast-downcast +# google-default-arguments +# misc-include-cleaner +# misc-redundant-expression +# misc-unused-parameters +# performance-for-range-copy +# performance-inefficient-string-concatenation +# performance-inefficient-vector-operation +# performance-move-const-arg +# performance-no-automatic-move +# performance-type-promotion-in-math-fn +# performance-unnecessary-copy-initialization +# performance-unnecessary-value-param CheckOptions: + modernize-avoid-c-arrays.AllowStringArrays: true + # Common tolerated conversions + bugprone-narrowing-conversions.WarnOnFloatingPointNarrowingConversion: false + bugprone-narrowing-conversions.WarnOnIntegerNarrowingConversion: false + bugprone-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion: false + readability-implicit-bool-conversion.AllowLogicalOperatorConversion: true + readability-implicit-bool-conversion.AllowPointerConditions: true + # Some data model structs are missing some special functions by design. + cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions: true # Naming conventions - - { key: readability-identifier-naming.ClassCase, value: CamelCase } - - { key: readability-identifier-naming.ClassMemberPrefix, value: m } - - { key: readability-identifier-naming.ConceptCase, value: CamelCase } - - { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase } - - { key: readability-identifier-naming.EnumCase, value: CamelCase } - - { key: readability-identifier-naming.EnumConstantCase, value: CamelCase } - - { key: readability-identifier-naming.EnumConstantIgnoredRegexp, value: "^k?[A-Z][a-zA-Z0-9_]*$" } # Allow "k" prefix and non-trailing underscores in PDG names. - - { key: readability-identifier-naming.FunctionCase, value: camelBack } - - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } - - { key: readability-identifier-naming.MacroDefinitionIgnoredRegexp, value: "^[A-Z][A-Z0-9_]*_$" } # Allow the trailing underscore in header guards. - - { key: readability-identifier-naming.MemberCase, value: camelBack } - - { key: readability-identifier-naming.NamespaceCase, value: lower_case } - - { key: readability-identifier-naming.ParameterCase, value: camelBack } - - { key: readability-identifier-naming.StructCase, value: CamelCase } - - { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase } - - { key: readability-identifier-naming.TypeAliasCase, value: CamelCase } - - { key: readability-identifier-naming.TypedefCase, value: CamelCase } - - { key: readability-identifier-naming.TypeTemplateParameterCase, value: CamelCase } - - { key: readability-identifier-naming.VariableCase, value: camelBack } + readability-identifier-naming.ClassCase: CamelCase + readability-identifier-naming.ClassMemberPrefix: m + readability-identifier-naming.ConceptCase: CamelCase + readability-identifier-naming.ConstexprVariableCase: CamelCase + readability-identifier-naming.EnumCase: CamelCase + readability-identifier-naming.EnumConstantCase: CamelCase + readability-identifier-naming.EnumConstantIgnoredRegexp: "^k?[A-Z][a-zA-Z0-9_]*$" # Allow "k" prefix and non-trailing underscores in PDG names. + readability-identifier-naming.FunctionCase: camelBack + readability-identifier-naming.MacroDefinitionCase: UPPER_CASE + readability-identifier-naming.MacroDefinitionIgnoredRegexp: "^[A-Z][A-Z0-9_]*_$" # Allow the trailing underscore in header guards. + readability-identifier-naming.MemberCase: camelBack + readability-identifier-naming.NamespaceCase: lower_case + readability-identifier-naming.ParameterCase: camelBack + readability-identifier-naming.StructCase: CamelCase + readability-identifier-naming.TemplateParameterCase: CamelCase + readability-identifier-naming.TypeAliasCase: CamelCase + readability-identifier-naming.TypedefCase: CamelCase + readability-identifier-naming.TypeTemplateParameterCase: CamelCase + readability-identifier-naming.VariableCase: camelBack +... diff --git a/.claude/commands/migrate-ccdb.md b/.claude/commands/migrate-ccdb.md new file mode 100644 index 00000000000..15670bd84fd --- /dev/null +++ b/.claude/commands/migrate-ccdb.md @@ -0,0 +1,331 @@ +Migrate the specified file (or all files mentioned in the conversation) from `Service` to the declarative CCDB table approach. + +## Background + +The old approach uses `Service` and calls `ccdb->getForTimeStamp(path, timestamp)` at runtime. The new approach declares CCDB columns and timestamped tables using macros, so the framework fetches objects automatically and exposes them as columns on BC rows. + +**New API summary:** + +```cpp +// In namespace o2::aod (or a sub-namespace): +DECLARE_SOA_CCDB_COLUMN(StructName, getterName, ConcreteType, "CCDB/Object/Path"); + +// ... or, when the object needs fixing up after deserialisation, the _FULL form. The 0 is +// the run-dependence mode; the finaliser is the trailing argument (see the two sections +// "Objects needing post-deserialisation fixup" and "Run-dependent objects"): +DECLARE_SOA_CCDB_COLUMN_FULL(StructName, "fStructName", getterName, ConcreteType, "CCDB/Object/Path", 0, + [](ConcreteType* o) { return fixUp(o); }); + +DECLARE_SOA_TIMESTAMPED_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TABLEDESC", + ns::StructName, ns::OtherColumn); + +// ... or, when the object is constant across something coarser than a timestamp, the +// uniform form (see "Uniformity: how often the object can change"): +DECLARE_SOA_UNIFORM_TABLE(TableName, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "TABLEDESC", + ns::StructName); + +// In the task — basic usage: +using MyBCs = soa::Join; +void process(MyBCs const& bcs) { + for (auto const& bc : bcs) { + auto const& obj = bc.getterName(); // reference to cached deserialized object; treat as immutable + } +} +``` + +**Configurable CCDB paths** (`ConfigurableCCDBPath`): + +`ConfigurableCCDBPath` is a `Configurable` named `"ccdb:" + Column::mLabel` (where `mLabel = "f" + StructName`), defaulting to `Column::query`. Declare it **only** when a task consumes a *shared* column and needs a different default path than the shared declaration carries. It does not make the path settable from the task's config block — see Known gaps. No O2Physics task currently declares one; the model migration explains why it deliberately does not (`propagationServiceV2.cxx:70-73`). + +Why it is a default and not an override: `ArrowSupport.cxx:641-666` copies the `ConfigParamSpec` — `opt.defaultValue`, not a runtime value — from the task onto `internal-dpl-aod-ccdb` at topology-build time, before any config JSON is read. The task's own `ccdb:fXxx` option is auto-registered (`AnalysisTask.h:575`) but nothing reads it for path resolution. + +It is purely declarative: declaring it is sufficient, and the accessor stays `bc.getterName()`. Do **not** log `.value` as "the path in use" — it reflects the task device's own option, which the fetcher never reads, so it prints exactly the value that is being ignored. + +Required headers (add if missing): ``, ``, `` +Headers to remove (if no longer needed): `` + +## What to do + +Read the target file(s) and perform the following migration. Do NOT do a complete migration if the patterns are ambiguous or out of scope — instead note what was skipped and why. + +### Step 1 — Inventory + +Find every `ccdb->getForTimeStamp(path, ts)` call (and variants like `fCCDB->getForTimeStamp`, `mCcdb->getForTimeStamp`). For each call record: +- The concrete C++ type `T` +- The CCDB path string (may be a `Configurable` variable — record the default value and the Configurable's name) +- The timestamp source (BC timestamp, computed value, etc.) +- Where the result is used + +Also record the other call shapes, which each need a different decision: `getForRun` (see Known gaps — not the same query), `getSpecific(path, ts, metadata)` (only `runNumber` metadata is expressible, via run-dependence mode 1; anything else — skip), and `get(path)` relying on a previously-set manager timestamp (treat as global/init-time unless that timestamp came from a BC). + +**Deduplicate**: for the same (type, path) pair, declare only one CCDB column. Multiple call sites collapse into multiple uses of the same getter. + +### Step 2 — Identify scope + +Determine whether each fetch is: +- **Per-BC/per-collision** (called inside `process()` with a timestamp from a BC) — these can be migrated +- **Per-run** (called once when `runNumber` changes, guarded by `mRunNumber == ...`) — migratable, but declare the table with `DECLARE_SOA_UNIFORM_TABLE(..., aod::BCs, o2::aod::bc::RunNumber, ...)` so the fetcher queries once per run (see "Uniformity"). The timestamp default queries once per distinct BC timestamp, which is the opposite of what the `mRunNumber` guard was doing. +- **Optional** (the task calls `ccdb->setFatalWhenNull(false)` and branches on a null result — common for MC without a calibration) — a column cannot express absence; a missing object aborts the job. Leave these as-is and note it. +- **Global/init-time** (called in `init()` with a fixed timestamp, not keyed to a BC) — these **cannot** be migrated to CCDB tables; leave them as-is and note this + +Skip the migration for any global/init-time fetches. Skip the whole file if all fetches are global. + +### Step 3 — Declare CCDB columns and table + +**First check whether a shared column already exists**, and join it instead of redeclaring: `GLO/Config/GRPMagField`, `GLO/Calib/MeanVertex`, `GLO/Config/GRPECS`, `GLO/Config/GRPLHCIF` are in `Common/DataModel/GloCCDBObjects.h` (`aod::GloCCDBObjects`, `aod::GrpLHCIFCCDBObjects`); `GLO/Param/MatLUT` is in `aod::GeomCCDBObjects` in the same header; `TPC/Calib/VDriftTgl` in `Common/DataModel/TpcCCDBObjects.h`; TrackTuner calibrations in `Common/DataModel/TrackTunerCCDBObjects.h`. Declare a per-task table only for objects with no shared column. + +Otherwise, in the `o2::aod` namespace (or a private sub-namespace inside the file, before the task struct), declare: + +```cpp +namespace o2::aod +{ +namespace myccdbtask // use a short, unique snake_case name derived from the task name +{ +DECLARE_SOA_CCDB_COLUMN(StructName, getterName, fully::qualified::ConcreteType, "CCDB/Path"); //! +// one per unique (type, path) pair +} // namespace myccdbtask + +DECLARE_SOA_TIMESTAMPED_TABLE(MyTaskCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "MYTASKCCDB", //! + myccdbtask::StructName /*, ... */); +} // namespace o2::aod +``` + +Before writing the declaration, settle three things per column — each has its own section +below, and getting them wrong is silent rather than loud: + +1. **Does the object need fixing up after deserialisation?** If so use `DECLARE_SOA_CCDB_COLUMN_FULL` + with a finaliser — see "Objects needing post-deserialisation fixup". +2. **How often can the object change?** Timestamp (the default) or run — see "Uniformity: how + often the object can change". Choose from the object's validity, not from how the old code + happened to fetch it. +3. **Is the path the same for every run?** If it varies by period, declare the mapping in the + query string instead of porting the run-range `if/else` — see "Paths that vary by run". + +Rules for naming: +- `StructName` / `getterName`: derive from the type name, e.g. `GRPMagField` / `grpMagField`, `MeanVertex` / `meanVertex` +- **The label `f` is a workflow-global key**, not a per-table one: it names the path option on the fetcher, and two columns sharing a `StructName` share one path — first-seen wins, no warning (`AnalysisCCDBHelpers.cxx:91-100`, `ArrowSupport.cxx:665`). Harmless while the paths agree, silent divergence otherwise. If your path differs from an existing column of that name, pick a distinct `StructName`. +- Table name: `CCDBObjects`, e.g. `SkimmerDalitzEECCDBObjects` +- `_Desc_` string: short ALL-CAPS string unique within the binary (≤ 16 chars to fit the AOD descriptor), e.g. `"DALZECC"`, `"TOFCALIB"` +- Namespace: lowercase snake-case derived from the task name (avoid collisions with other CCDB column namespaces in the file) +- Use the **default value** of any `Configurable` path as the compile-time path in the `DECLARE_SOA_CCDB_COLUMN` macro; if the path has no obvious default, leave a `// TODO: verify path` comment + +### Step 4 — Update the task struct + +1. **Remove** `Service ccdb;` (and any variant field name) +2. **Remove** `int mRunNumber;` (or similar run-caching variables) **only if** their sole purpose was to guard CCDB re-fetches +3. **Remove** `ccdb->setURL(...)`, `ccdb->setCaching(...)`, `ccdb->setLocalObjectValidityChecking()`, `ccdb->setCreatedNotAfter(...)`, `ccdb->setFatalWhenNull(...)` from `init()` +4. **Remove** the entire `initCCDB()`/`initMagField()` helper method if it only did CCDB fetching; otherwise remove just the CCDB lines from it +5. **Handle path Configurables** — for each `Configurable` that held a CCDB path: + - If its default equals the compile-time path you put in the column (the normal case): **remove** it. `AnalysisTask.h:575` auto-registers `ccdb:fXxx` on every subscribing task with default `Column::query`, so a `ConfigurableCCDBPath` would only restate that default. Declare `ConfigurableCCDBPath` only when consuming a *shared* column whose declared path differs from this task's old default, and comment that it sets the fetcher's default, not a runtime value. + - If the path was never intended to be user-facing (e.g. internal fixed paths): **remove** it outright; the compile-time path in `DECLARE_SOA_CCDB_COLUMN` is sufficient. + - Always remove Configurables that were only used for CCDB manager setup and not for paths: `ccdb-url`, `ccdb-no-later-than`, `skipGRPOquery`, `d_bz_input` (if only used to bypass CCDB), etc. +6. **Remove** cached pointer member variables (e.g. `GRPMagField* grpmag = nullptr`) if they were only populated by CCDB fetches that are now replaced + +### Step 5 — Update process() signatures + +Define one alias near the top of the task or just below the table declaration: +```cpp +using MyBCs = soa::Join; +``` + +Then for each `process()` that used to call `getForTimeStamp`: + +- If `process()` already takes `aod::BCsWithTimestamps const&` directly: change it to `MyBCs const&`. +- If `process()` accesses BCs via `collision.bc_as()`: add `MyBCs const&` to the process signature (so the framework knows to provide it) and replace the `bc_as<>` type with `MyBCs`. +- If `process()` does not currently mention BCs but called `ccdb->getForTimeStamp(path, collision.bc_as<...>().timestamp())`: add `MyBCs const&` to the signature and obtain the BC via `collision.bc_as()`. +- Replace every `ccdb->getForTimeStamp(path, ts)` call with `bc.getterName()`. The returned reference is to a cached deserialized object; treat it as immutable. +- Null-pointer checks (`if (!grpmag)`) on the result become unnecessary — the framework guarantees the object is present (or the task fails early). Remove them. +- If a helper template like `initCCDB(collision)` was called per-collision, inline its remaining (non-CCDB) work or drop it. +- When configuring from the first BC, guard the empty table first: `if (bcs.size() == 0) { return; }` (`StandardCCDBLoader.h:69`). Empty BC tables occur, and `bcs.begin()` / `iteratorAt(0)` on one is undefined. + +### Step 6 — Fix includes + +- Remove `#include ` if no other code in the file still uses `BasicCCDBManager` +- Ensure `#include ` is present (may already be included transitively) +- Keep all type headers (e.g. ``) since they are still needed for the concrete type + +### Step 7 — Final review + +After making changes: +- Check that every remaining use of `ccdb` / `fCCDB` / `mCcdb` has been handled +- Check that `mRunNumber` (or similar) is fully removed if unused +- Check that any leftover `Configurable` for a path is either replaced by `ConfigurableCCDBPath<>` or removed +- Search for stale references to removed Configurables (e.g. `grpmagPath.value` lingering in log messages — switch to `grpMagFieldPath.value`) +- If `init()` is now empty, it can be removed +- Note any patterns that were intentionally skipped + +## Important limitations — tell the user if any apply + +- **Configurable paths**: after migration a path is a compile-time constant plus one option on the fetcher device. It is **not** settable from the task's config block or from a Hyperloop wagon parameter — read the Known gaps entry before telling the user a path is configurable. +- **`getRunDuration()` calls**: these use `BasicCCDBManager` statically and are unrelated to per-BC fetching — do not touch them. +- **`ctpRateFetcher` / other helpers**: out of scope. +- **Multiple tasks in one file**: tasks can share a single CCDB table declaration if they need the same objects; otherwise each task gets its own with a unique `_Desc_`. +- **Non-BC timestamps**: if the timestamp comes from something other than a BC, the migration is non-trivial — flag it instead of forcing it. This is the single most common blocker in practice. `Common/Tools/EventSelectionModule.h:244` computes `ts = sorTimestamp / 2 + eorTimestamp / 2` (mid-run, from `getRunDuration` / `AggregatedRunInfo`) and fetches `EventSelectionParams`, `ITS/Config/AlpideParam`, `TriggerAliases` and `ITS/Calib/TimeDeadMap` at it. A BC-keyed column fetches at each BC's own timestamp instead, so migrating these silently changes which object version is served whenever an object is revised mid-run. They need a run-uniform table before they can move — and note that one queries at the run's first BC, not mid-run, so it is equivalent only for objects with a single version per run. +- **Global/init-time fetches** (e.g. `efficiencyGlobal.cxx` style): not migratable — the timestamped-table mechanism requires a row in a BC-keyed table. +- **Magnetic-field side effects**: tasks that compute `d_bz` from a fetched `GRPMagField` and seed a propagator can keep that logic, just sourcing the object from `bc.grpMagField()` instead of `ccdb->getForTimeStamp(...)`. + +## Lessons learned (established in-tree, with references) + +### Why this migration matters beyond tidiness + +The old per-task path Configurable is device-name-scoped, which made it a silent-divergence trap: `propagationServiceV2` shared `ccdb.lutPath` with `propagationService` (`Common/Tools/StandardCCDBLoader.h:45`, default `GLO/Param/MatLUT`), but every config setting `GLO/Param/MatLUTInner` did so under a `propagation-service` block (8 of them, all under `Tutorials/`), so V2 silently used the full LUT — different material corrections, no warning. + +Migration makes the path one option on the fetcher device and turns two *tasks* declaring conflicting defaults into a warning (`ArrowSupport.cxx:641-666`). That is the real gain. It does not fix the scoping itself: **runtime** overrides remain device-scoped, just aimed at `internal-dpl-aod-ccdb` instead of the task — see Known gaps before telling a user a migrated path is "now configurable". + +### Objects needing post-deserialisation fixup + +Some objects are not usable straight out of the ROOT streamer. `MatLayerCylSet` is a `FlatObject`: its internal pointers are unfixed and its voxel lookup unbuilt until `MatLayerCylSet::rectifyPtrFromFile()` runs. Use the `_FULL` form, which carries the finaliser (the plain `DECLARE_SOA_CCDB_COLUMN` passes an identity one): + +```cpp +DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", 0, //! + [](o2::base::MatLayerCylSet* lut) { return o2::base::MatLayerCylSet::rectifyPtrFromFile(lut); }); +``` + +The argument before the finaliser is the run-dependence mode (`0` = query by timestamp, the default the plain macro passes; see "Run-dependent objects"). The finaliser must be the **last** macro argument (commas in a lambda body are absorbed by `__VA_ARGS__`), has signature `T* (*)(T*)`, and runs on the receiving device once per (re)deserialisation, before the object is ever handed out. Ownership contract: whatever it returns is what the column cache later `delete`s, so a finaliser returning a *different* instance must dispose of the one it was given. + +Do **not** put this fixup in the task. There is no `finaliseCCDB` hook on the analysis path (`adaptAnalysisTask` wires only `EndOfStream`, `AnalysisTask.h:610-619`; grep confirms zero uses of `finaliseCCDB` in O2Physics). + +### Uniformity: how often the object can change + +Every CCDB table declares a *uniformity column*: rows sharing its value resolve to the same +object, so the fetcher queries once per distinct value instead of once per row. +`DECLARE_SOA_TIMESTAMPED_TABLE` defaults it to the timestamp column, which is the +pre-existing behaviour — every distinct timestamp may yield a different object. + +Pick it from the object's real validity, and only then: + +| Object changes ... | Uniformity | Declare with | +| --- | --- | --- | +| within a run (calibrations, drift velocity) | timestamp (default) | `DECLARE_SOA_TIMESTAMPED_TABLE` | +| per run or per period (geometry, material, per-period calibrations) | `aod::BCs` / `aod::bc::RunNumber` | `DECLARE_SOA_UNIFORM_TABLE` | + +Worked examples in the tree: `aod::TpcCalibCCDBObjects` keeps the timestamp default because +the TPC drift velocity genuinely varies within a run; `aod::GeomCCDBObjects` and +`aod::TrackTunerCCDBObjects` are run-uniform. + +The run number lives on `aod::BCs` and the timestamp on `aod::Timestamps`; the macro takes both, +and the fetcher fatals if they are not row-aligned (anything joinable with the BCs is fine). The +uniformity column must be an int32/int64/uint64 (`AnalysisCCDBHelpers.cxx:246-260`). + +### Paths that vary by run: declare a mapping, not code + +A column's path may be a plain path, or a mapping from uniformity value to path: + +``` +"520259-529691=…/pp2023/pass4/vsPhi;559348-559387=…/ppRef/polarity_positive;fallback" +``` + +Ranges are inclusive; either bound may be omitted (`-hi=path`, `lo-=path`); entries are +separated by `;`; an entry without `=` is an explicit fallback. **A value matching no range +is fatal**, deliberately — silently substituting another period's calibration is the failure +mode this whole mechanism exists to prevent. A string with no `=` is a plain path, so +existing columns are unaffected. + +**The key matched against the ranges is the table's uniformity value**, so a run-range mapping +requires `DECLARE_SOA_UNIFORM_TABLE` on `aod::bc::RunNumber`. On a timestamp-uniform table the +ranges match nothing: fatal if there is no fallback entry, and — worse — every row silently +takes the fallback if there is one. + +The mapping is *data* in the schema metadata, so the run ranges stop being compiled in: the +whole mapping is replaceable through the `ccdb:fXxx` option, subject to the Known gaps scoping. + +This replaces hand-written run-range tables. `TrackTuner::getPathInputFileAutomaticFromCCDB()` +is the model case: ~50 lines of `else if (lo <= runNumber && runNumber <= hi)` became the +declaration in `Common/DataModel/TrackTunerCCDBObjects.h`. When porting one, **derive the +mapping mechanically and diff it against the source** — first-match-wins must reproduce the +`if/else` order, which matters whenever ranges overlap (in TrackTuner, one PbPb range sits +inside a pp range and must stay *after* it). + +### Serving migrated and un-migrated callers from one module + +Shared modules must keep working for tasks that have not migrated. Detect the capability +rather than adding a configuration flag: + +```cpp +auto const& bc = collision.template bc_as(); +if constexpr (requires { bc.vdriftTgl(); }) { + mVDriftMgr.update(bc.vdriftTgl()); // column path +} else { + mVDriftMgr.update(bc.timestamp()); // legacy CCDB query +} +``` + +The discarded branch is not instantiated, so an un-migrated caller compiles exactly as before +and a migrated one never references the CCDB manager. `strangenessBuilderModule::updateVDrift` +uses this. Where a whole function parameter falls away, add an overload of different arity +that forwards (see "Shared module signatures") and put a `static_assert` with a readable +message on the ccdb-free one, so calling it with an unjoined BC table names the missing table +instead of failing somewhere inside the template. + +### Two path settings must never both be live + +After migration the column is the single source of truth for a path. If the task still has an +old `Configurable` for the same object, **fail loudly when both are set** rather +than silently preferring one — that divergence is exactly the bug this migration exists to +kill. `TrackPropagationModule::init` fatals when `trackTuner.pathInputFile` is non-empty while +the calibrations come from columns, naming the option to use instead (`ccdb:fTrackTunerDca`). + +Caveat: this test only works for Configurables whose default is empty. One with a non-empty +default cannot be distinguished from an unset one, so that hole stays open until the framework +can report whether an option was explicitly set. + +### Grouping columns into tables + +One table per **family of objects used together with similar validity intervals** — not one per consuming task. Geometry and material description (`GLO/Param/MatLUT`, and later `GLO/Config/GeometryAligned`, `GLO/Config/Geometry`, `/Calib/Align`; see `GRPGeomRequest` in `O2/Detectors/Base/src/GRPGeomHelper.cxx:44-60`) is one family with essentially static validity. The GRP family changes per run, and `GRPMagField` is requested per timeframe in O2 (`GRPGeomHelper.cxx:72`). Splitting on that boundary keeps a task from fetching a multi-hundred-MB LUT it never asked for. + +**Several timestamped tables can be joined onto the same BCs.** `soa::Join` works: the duplicated `aod::Timestamps` is deduplicated when `originals` is merged (`ASoA.h:131-140`), giving 4 originals, and every accessor resolves. Do not invent per-use-case tables to work around a limitation that does not exist. + +### Global state is not a lookup + +Migrating removes CCDB *queries*, not side effects. Two things stay: + +- `Propagator::initFieldFromGRP()` rebuilds or rescales a `MagneticField`, attaches it to `TGeoGlobalMagField::Instance()` and locks it (`O2/Detectors/Base/src/Propagator.cxx:107-149`). Keep it guarded on run change. +- `Propagator::Instance()->setMatLUT()` is a pointer store, so it is cheaper to redo unconditionally every timeframe — and doing so picks up a relocated column buffer for free instead of dangling. + +Everything else (mean vertex, run number) should become a direct read at the point of use, with no cached member and no `initCCDB()` helper. A cached pointer plus a "did the buffer move?" check is strictly worse than reading the column fresh. + +`Propagator` cannot itself become a column value: private constructor, deleted copy/move, singleton `Instance()` (`Propagator.h:157-201`). + +### Shared module signatures + +If a shared module takes a `StandardCCDBLoader`, change it to take the values it actually uses (`int runNumber`, `MeanVertexObject const*`) and keep a thin forwarding overload for un-migrated callers, so V1 tasks stay byte-identical. `TrackPropagationModule::fillTrackTables` does this — the two overloads differ in arity, so overload resolution is unambiguous. + +### What the migration does and does not buy + +The fetcher downloads once into a shm cache and the column stores `(handle, segment, size)` (`AnalysisCCDBHelpers.cxx:320-330`). What is shared is the **serialised blob**; each consumer still streams its own heap copy in the column getter. So expect fewer downloads, one configuration point and cross-device consistency — but not a per-device RSS reduction. For a `FlatObject` like the LUT, real memory sharing needs a zero-copy path (`FlatObject::setActualBufferAddress`) that does not exist yet. + +Measured on `propagationServiceV2` (`HF_LHC23_pass4_Thin_small_2P3PDstar`, `daily-20260922-0000-1`), migrated wagon 61527 against un-migrated baseline wagon 61526: + +| | baseline 766059 | migrated 766456 | +| --- | --- | --- | +| propagation device `cpuUsedAbsolute` | 3,972,078 | 2,866,181 (**−28 %**) | +| propagation device peak PSS | 160 MB | 168 MB | +| train peak PSS | 1340 MB | 1366 MB | +| CCDB queries from the task device | 3.03 MB fetched | 0 — all via the fetcher | + +Quote the CPU number when justifying a migration; do not promise memory. + +### Known gaps in the mechanism + +- **Runtime path overrides are scoped to the fetcher device, so most existing config mechanisms cannot reach them.** `ccdb:fXxx` is registered as an option on `internal-dpl-aod-ccdb`, and DPL resolves options strictly per device: `DataProcessingDevice.cxx:2615` builds the retriever keyed on the device `name`, and `ConfigurationOptionsRetriever.cxx:47` does `getRecursive(mMainKey)` with no global or wildcard section. A value placed in any other device's block is unreachable. Hyperloop writes wagon parameters into the wagon's own device block, and the config JSONs in the tree are written the same way, so **the natural way to set a migrated path silently does nothing**. The four levers that do work today: pass `--ccdb:fXxx` on the workflow command line (DPL forwards CLI options to every device declaring them, which is what `propagationServiceV2.cxx:73` tells users to do); hand-write an `internal-dpl-aod-ccdb` block into the config JSON; edit the compile-time path in the column declaration; or declare `ConfigurableCCDBPath` in a task to change that default — but note that ArrowSupport takes the *first* device in workflow order carrying the option, and every subscriber carries it with the compile-time default, so a `ConfigurableCCDBPath` in a task that is not the first subscriber loses to that default with only a warning. Making this usable from Hyperloop needs a Hyperloop-side change (emit `ccdb:*` into an `internal-dpl-aod-ccdb` block); making it usable generally needs the fetcher to consult the declaring task's runtime value, which the build-time propagation in `ArrowSupport.cxx:641-666` cannot do. + + Observed end-to-end (Hyperloop test 766459): the wagon carried `ccdb:fMatLUT = GLO/Param/MatLUTInner` and `configuration.json` shipped it, inside the `propagation-service-v2` block; `dpl-config.json` then held the key **twice**, `MatLUTInner` under `propagation-service-v2` and `MatLUT` under `internal-dpl-aod-ccdb`. The job fetched the full LUT. + + **Verify a path override by payload, never by config** — reading it back from the wagon or from `configuration.json` only proves it was *stored*. Three checks prove it was *used*: the fetcher's `[internal-dpl-aod-ccdb] ccdb reads ` log lines, the object UUID in that URL, and `reader.ccdb-cache-fetched-bytes` in `performanceMetrics_processed.json`. Grepping for the path *name* is not one of them. + +- **Run-dependent objects need an explicit mode, and no in-tree column sets one.** The run-dependence argument of `DECLARE_SOA_CCDB_COLUMN_FULL` (`0` = by timestamp; `1` additionally sends the run as `runNumber` metadata; `2` queries by run instead of timestamp — `CCDBFetcherHelper.cxx:189-198`) *is* honoured: the fetcher reads it per field, takes the run from the uniformity value, and fatals if the table is not uniform in `fRunNumber` (`AnalysisCCDBHelpers.cxx:288-303`). What is missing is use: every in-tree column is mode 0. In particular `GLO/Config/GRPECS` is requested run-dependently in O2 (`GRPGeomHelper.cxx:66`) but `ccdbGlo::GRPECSObject` is mode 0 inside the timestamp-uniform `aod::GloCCDBObjects` — do not rely on it for run-keyed lookups until it moves to a run-uniform table with mode 1. +- **`getForRun` is not the same query.** `BasicCCDBManager::getForRun` resolves the run duration and queries at *mid-run* (`BasicCCDBManager.h:364-381`); a column queries at each BC's timestamp. Identical for objects with one version per run, divergent otherwise. +- **Row cardinality, not query count.** The uniformity column already collapses the *queries* to one per distinct value, but the table still carries one row per BC per column — a `FixedSizeList`, 24 B, rebuilt every timeframe. Collapsing the rows too needs a non-extension table plus lookup by value at the consumer, which does not exist yet. So a run-uniform table costs the same arrow memory as before; what it saves is the fetching. +- **Multi-run dataframes.** Skimmed datasets can span runs. Every existing consumer configures from `bcs.begin()` and applies it to the whole DF (`propagationServiceV2.cxx`, `StandardCCDBLoader.h:70-77`, `strangenessBuilderModule.h:884`), which is wrong for such a DF. Migrating preserves this bug unless it is fixed deliberately — do not claim the migration fixes it. + +### Practical gotchas + +- `DECLARE_SOA_CCDB_COLUMN` expands to a non-template member using `TClass::GetClass` and `TBufferFile`, and `ASoA.h` neither includes nor forward-declares them. The header that expands the macro must have `` and `` in scope — include them explicitly (`// IWYU pragma: keep`) rather than relying on `AnalysisDataModel.h` to drag them in transitively. +- The getter holds one deserialised object per column *type*, replaced (and the previous one `delete`d) as soon as a row with a different payload is read (`ASoA.h:2413-2432`). A reference from `bc1.x()` dangles after `bc2.x()` when the two rows resolve to different objects — another run in a multi-run DF, or a mid-run revision. Read at the point of use; never hold the reference across rows or timeframes. This is also why a pointer stored into global state (`setMatLUT`) must be refreshed every timeframe. +- A failed fetch is fatal, not silent: if `extractCCDBPayload` returns null the getter aborts naming the type, the path and the `ccdb:` option to check. A mistyped path therefore stops the job rather than dereferencing null. +- Do not add a `sources` member to a table's metadata struct. It makes the struct satisfy both `soa::with_sources` and `soa::with_sources_generator`, and `getInputMetadata` becomes ambiguous. +- Device options are matched by device *name*. Never look a task's own option up by a hardcoded name (`device.name == "propagation-service"` silently matched nothing in `propagation-service-v2`); take the running device from `initContext.services().get()`. Spell the type out rather than using `auto`, or the pre-existing `option.defaultValue.get()` becomes a dependent name and needs `template`. +- Verify with the *control*: when changing a shared header, compile an un-migrated consumer too. A new error appearing in both is yours; the same errors in both means you changed nothing for them. + +$ARGUMENTS diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml index c6c9bc12067..fcaa7084f8d 100644 --- a/.github/workflows/clean-test.yml +++ b/.github/workflows/clean-test.yml @@ -24,13 +24,18 @@ name: Clean PR checks type: boolean default: true - 'check_build/O2Physics/o2/macOS': - description: build/O2Physics/o2/macOS + 'check_build/O2Physics/o2': + description: build/O2Physics/o2 type: boolean default: true - 'check_build/O2Physics/o2': - description: build/O2Physics/o2 + 'check_build/O2Physics/staging': + description: build/O2Physics/staging + type: boolean + default: true + + 'check_build/O2Physics/code-check': + description: build/O2Physics/code-check type: boolean default: true diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 84e21c9be5e..4cbebc3d37a 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -16,7 +16,7 @@ jobs: steps: - name: Label the PR id: labeler - uses: actions/labeler@v6 + uses: actions/labeler@v7 with: repo-token: ${{ secrets.GITHUB_TOKEN }} sync-labels: true diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml index 7b558e9e825..3e0aebe587f 100644 --- a/.github/workflows/mega-linter.yml +++ b/.github/workflows/mega-linter.yml @@ -23,7 +23,7 @@ jobs: steps: # Git Checkout - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Checkout the HEAD of the PR instead of the merge commit. ref: ${{ github.event.pull_request.head.sha }} @@ -32,13 +32,14 @@ jobs: fetch-depth: 0 # So we can use secrets.ALIBUILD_GITHUB_TOKEN to push later. persist-credentials: false + allow-unsafe-pr-checkout: true # needed for pull_request_target # MegaLinter - name: MegaLinter id: ml # You can override MegaLinter flavor used to have faster performances # More info at https://megalinter.io/flavors/ - uses: oxsecurity/megalinter@v9.4.0 + uses: oxsecurity/megalinter@v10.1.0 env: # All available variables are described in documentation: # https://megalinter.io/configuration/ diff --git a/.github/workflows/o2-linter.yml b/.github/workflows/o2-linter.yml index f5a1b39a950..1af5915fa89 100644 --- a/.github/workflows/o2-linter.yml +++ b/.github/workflows/o2-linter.yml @@ -30,10 +30,11 @@ jobs: echo BRANCH_HEAD="$branch_head" >> "$GITHUB_ENV" echo BRANCH_BASE="$branch_base" >> "$GITHUB_ENV" - name: Checkout Code - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: ${{ env.BRANCH_HEAD }} fetch-depth: 0 # needed to get the full history + allow-unsafe-pr-checkout: true # needed for pull_request_target - name: Run tests id: linter run: | diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 62006bf7d90..5e57bf3cc86 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v10 + - uses: actions/stale@v11 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-pr-message: 'This PR has not been updated in the last 30 days. Is it still needed? Unless further action is taken, it will be closed in 5 days.' diff --git a/.mega-linter.yml b/.mega-linter.yml index b9a956bd20a..0accd24dd94 100644 --- a/.mega-linter.yml +++ b/.mega-linter.yml @@ -19,13 +19,13 @@ DISABLE_LINTERS: - PYTHON_FLAKE8 - PYTHON_ISORT - REPOSITORY_DEVSKIM - - REPOSITORY_GITLEAKS - - REPOSITORY_KICS + - REPOSITORY_OSV_SCANNER - REPOSITORY_SECRETLINT - REPOSITORY_TRIVY - YAML_PRETTIER - YAML_V8R DISABLE_ERRORS_LINTERS: # If errors are found by these linters, they will be considered as non blocking. + - ACTION_ZIZMOR - PYTHON_BANDIT # The bandit check is overly broad and complains about subprocess usage. SHOW_ELAPSED_TIME: true FILEIO_REPORTER: false @@ -40,5 +40,5 @@ PYTHON_RUFF_CONFIG_FILE: pyproject.toml CPP_CPPLINT_FILE_EXTENSIONS: [".C", ".c", ".c++", ".cc", ".cl", ".cpp", ".cu", ".cuh", ".cxx", ".cxx.in", ".h", ".h++", ".hh", ".h.in", ".hpp", ".hxx", ".inc", ".inl", ".macro"] CPP_CLANG_FORMAT_FILE_EXTENSIONS: [".C", ".c", ".c++", ".cc", ".cl", ".cpp", ".cu", ".cuh", ".cxx", ".cxx.in", ".h", ".h++", ".hh", ".h.in", ".hpp", ".hxx", ".inc", ".inl", ".macro"] CPP_CPPCHECK_FILE_EXTENSIONS: [".C", ".c", ".c++", ".cc", ".cl", ".cpp", ".cu", ".cuh", ".cxx", ".cxx.in", ".h", ".h++", ".hh", ".h.in", ".hpp", ".hxx", ".inc", ".inl", ".macro"] -CPP_CPPCHECK_ARGUMENTS: --language=c++ --std=c++20 --check-level=exhaustive --suppressions-list=cppcheck_config -REPOSITORY_GITLEAKS_PR_COMMITS_SCAN: true +CPP_CPPCHECK_ARGUMENTS: --language=c++ --std=c++20 --enable=style --check-level=exhaustive --suppressions-list=cppcheck_suppressions --inline-suppr --force +ACTION_ZIZMOR_UNSECURED_ENV_VARIABLES: [GITHUB_TOKEN] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f837e1ad5a0..cf0e0a1b341 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,15 +2,47 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v6.0.0 + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 hooks: - - id: trailing-whitespace + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: a6564f5d57444e123433a5c9af733e3975c77229 # frozen: v0.11.0.1-1 + hooks: + - id: shellcheck + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: f12be1ebaa5351c1fc76472de98db2c3446c8253 # frozen: v0.16.10 + hooks: + - id: ruff-check # linter + args: ["--fix"] + - id: ruff-format # formatter + - repo: local + hooks: + - id: format-includes + name: format includes + language: python + entry: Scripts/format_includes.py + types: [c++] - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v20.1.3 # clang-format version (keep synced with https://github.com/alisw/ali-bot/blob/master/.github/workflows/c++-code-formatting.yml) + # clang-format version (keep synced with https://github.com/alisw/ali-bot/blob/master/.github/workflows/c++-code-formatting.yml) + rev: 7ec07eeb4135dc6ecf410d24ac4ead986a5d413d # frozen: v20.1.3 hooks: - id: clang-format - repo: https://github.com/cpplint/cpplint - rev: 2.0.2 + rev: 350702a4d39395571bd118c9a3cff0a573488d61 # frozen: 2.0.2 hooks: - id: cpplint + - repo: local + hooks: + - id: O2 linter + name: O2 linter + language: python + entry: Scripts/o2_linter.py + types_or: [c++, cmake, python] diff --git a/ALICE3/CMakeLists.txt b/ALICE3/CMakeLists.txt index 469091ed219..ab5b6f1e78a 100644 --- a/ALICE3/CMakeLists.txt +++ b/ALICE3/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(Core) # add_subdirectory(DataModel) +add_subdirectory(ML) add_subdirectory(Tasks) add_subdirectory(TableProducer) add_subdirectory(Macros) diff --git a/ALICE3/Core/ALICE3CoreLinkDef.h b/ALICE3/Core/ALICE3CoreLinkDef.h index 076c21f8a79..10706fa14cc 100644 --- a/ALICE3/Core/ALICE3CoreLinkDef.h +++ b/ALICE3/Core/ALICE3CoreLinkDef.h @@ -9,10 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::pid::tof::TOFResoALICE3 + ; #pragma link C++ class std::vector < std::vector < unsigned int>> + ; #pragma link C++ class std::vector < std::vector < std::uint32_t>> + ; diff --git a/ALICE3/Core/CMakeLists.txt b/ALICE3/Core/CMakeLists.txt index 159ee735f92..de89ec6f1a4 100644 --- a/ALICE3/Core/CMakeLists.txt +++ b/ALICE3/Core/CMakeLists.txt @@ -10,18 +10,22 @@ # or submit itself to any jurisdiction. o2physics_add_library(ALICE3Core - SOURCES TOFResoALICE3.cxx - TrackUtilities.cxx + SOURCES TrackUtilities.cxx + ConfigurationParser.cxx FlatLutEntry.cxx + FlatLutRootWriter.cxx FlatTrackSmearer.cxx GeometryContainer.cxx PUBLIC_LINK_LIBRARIES O2::Framework - O2Physics::AnalysisCore) + O2Physics::AnalysisCore + PRIVATE_LINK_LIBRARIES ROOT::Core + ROOT::RIO + ROOT::Tree) o2physics_target_root_dictionary(ALICE3Core - HEADERS TOFResoALICE3.h - TrackUtilities.h + HEADERS TrackUtilities.h FlatLutEntry.h + OTFParticle.h GeometryContainer.h LINKDEF ALICE3CoreLinkDef.h) diff --git a/ALICE3/Core/ConfigurationParser.cxx b/ALICE3/Core/ConfigurationParser.cxx new file mode 100644 index 00000000000..0682090d08f --- /dev/null +++ b/ALICE3/Core/ConfigurationParser.cxx @@ -0,0 +1,211 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ConfigurationParser.cxx +/// \brief Utilities to access and parse TEnv configuration files for the ALICE3 fast simulation +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// + +#include "ConfigurationParser.h" + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace o2::fastsim +{ + +std::map> ConfigurationParser::parseTEnvConfiguration(std::string& filename, std::vector& entries) +{ + std::map> configMap; + filename = gSystem->ExpandPathName(filename.c_str()); + LOG(info) << "Parsing TEnv configuration file: " << filename; + TEnv env(filename.c_str()); + THashList* table = env.GetTable(); + entries.clear(); + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + // key should contain exactly one dot + if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { + LOG(fatal) << "Key " << key << " does not contain exactly one dot"; + continue; + } + const std::string firstPart = key.substr(0, key.find('.')); + if (std::find(entries.begin(), entries.end(), firstPart) == entries.end()) { + entries.push_back(firstPart); + } + } + env.Print(); + // Entries + for (const auto& entry : entries) { + LOG(info) << " Reading entry " << entry; + for (int i = 0; i < table->GetEntries(); ++i) { + const std::string key = table->At(i)->GetName(); + if (key.find(entry + ".") == 0) { + const std::string paramName = key.substr(key.find('.') + 1); + const std::string value = env.GetValue(key.c_str(), ""); + configMap[entry][paramName] = value; + } + } + } + return configMap; +} + +std::string ConfigurationParser::accessFile(const std::string& path, const std::string& downloadPath, o2::ccdb::BasicCCDBManager* ccdb, int timeoutSeconds) +{ + if (path.rfind("ccdb:", 0) == 0) { + const std::string ccdbPath = path.substr(5); // remove "ccdb:" prefix + const std::string localPath = Form("%s/%s/snapshot.root", downloadPath.c_str(), ccdbPath.c_str()); + const std::string lockFile = localPath + ".lock"; + const std::string doneFile = localPath + ".done"; + + // Create directory structure if it doesn't exist + std::string dirPath = localPath.substr(0, localPath.find_last_of('/')); + gSystem->mkdir(dirPath.c_str(), true); + + // Check if file is already fully downloaded + struct stat buffer; + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file already exists: " << localPath << ". Skipping download."; + return localPath; + } + + // Acquire file lock for inter-process synchronization + int lockFd = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); + if (lockFd == -1) { + LOG(error) << " --- Failed to create lock file: " << lockFile; + return localPath; + } + + // Try to acquire exclusive lock (non-blocking) + LOG(info) << " --- Attempting to acquire lock for: " << localPath; + int lockResult = flock(lockFd, LOCK_EX | LOCK_NB); + + if (lockResult == -1 && errno == EWOULDBLOCK) { + // Lock is held by another process - wait up to 10 minutes for download to complete + LOG(info) << " --- Lock is held by another process. Waiting for download to complete (up to 10 minutes)..."; + close(lockFd); + + const auto startTime = std::chrono::steady_clock::now(); + const auto timeout = std::chrono::minutes(10); + const auto checkInterval = std::chrono::seconds(5); + + while (true) { + // Check if download is complete + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; + return localPath; + } + + // Check timeout + auto elapsed = std::chrono::steady_clock::now() - startTime; + if (elapsed >= timeout) { + LOG(fatal) << " --- Timeout waiting for file download: " << localPath << ". Waited for 10 minutes."; + return localPath; + } + + // Wait before checking again + std::this_thread::sleep_for(checkInterval); + } + } else if (lockResult == -1) { + LOG(error) << " --- Failed to acquire lock for: " << lockFile; + close(lockFd); + return localPath; + } + + // Lock acquired successfully - double-check if file was downloaded while we were trying + if (stat(doneFile.c_str(), &buffer) == 0) { + LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; + flock(lockFd, LOCK_UN); + close(lockFd); + return localPath; + } + + // File does not exist, retrieve from CCDB + LOG(info) << " --- CCDB source detected for " << path; + std::map metadata; + bool status = ccdb->getCCDBAccessor().retrieveBlob(ccdbPath, downloadPath, metadata, 1); + if (!status) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Failed to retrieve configuration from CCDB for path: " << ccdbPath; + return ""; + } + LOG(info) << " --- Retrieved configuration from CCDB to: " << localPath; + + // Verify the integrity of the downloaded file + if (stat(localPath.c_str(), &buffer) != 0) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Downloaded file does not exist or is not accessible: " << localPath; + return ""; + } + if (buffer.st_size == 0) { + flock(lockFd, LOCK_UN); + close(lockFd); + LOG(fatal) << " --- Downloaded file is empty: " << localPath; + return ""; + } + LOG(info) << " --- File integrity verified. Size: " << buffer.st_size << " bytes"; + + // Create done marker file to indicate successful download + std::ofstream doneMarker(doneFile); + doneMarker.close(); + + // Release lock + flock(lockFd, LOCK_UN); + close(lockFd); + + // If timeout is specified, schedule file deletion after timeout + if (timeoutSeconds > 0) { + LOG(info) << " --- Deleting configuration file after timeout: " << localPath; + std::thread deletionThread([localPath, doneFile, timeoutSeconds]() { + LOG(info) << " --- Operating deletion of configuration file after timeout: " << localPath; + std::this_thread::sleep_for(std::chrono::seconds(timeoutSeconds)); + if (std::remove(localPath.c_str()) == 0) { + LOG(info) << " --- File deleted successfully: " << localPath; + } else { + LOG(warning) << " --- Failed to delete file: " << localPath; + } + // Also remove the done marker file + if (std::remove(doneFile.c_str()) == 0) { + LOG(info) << " --- Done marker deleted: " << doneFile; + } + }); + deletionThread.detach(); + } + + return localPath; + } + return path; +} + +} // namespace o2::fastsim diff --git a/ALICE3/Core/ConfigurationParser.h b/ALICE3/Core/ConfigurationParser.h new file mode 100644 index 00000000000..b92a02e59ec --- /dev/null +++ b/ALICE3/Core/ConfigurationParser.h @@ -0,0 +1,53 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ConfigurationParser.h +/// \brief Utilities to access and parse TEnv configuration files for the ALICE3 fast simulation +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// + +#ifndef ALICE3_CORE_CONFIGURATIONPARSER_H_ +#define ALICE3_CORE_CONFIGURATIONPARSER_H_ + +#include + +#include +#include +#include + +namespace o2::fastsim +{ + +class ConfigurationParser +{ + public: + /** + * @brief Parses a TEnv configuration file with keys of the form "." and returns the key-value pairs split per entry + * @param filename Path to the TEnv configuration file + * @param entries Vector to store the order of the entries as they appear in the file + * @return A map where each key is an entry name and the value is another map of key-value pairs for that entry + */ + static std::map> parseTEnvConfiguration(std::string& filename, std::vector& entries); + + /** + * @brief Accesses a file given its path, which can be either a local path or a ccdb path (starting with "ccdb:"). In the first case it returns the local path, in the second it retrieves the file from ccdb and returns the local path to the retrieved file. + * @param path The path to the file, either local or ccdb (starting with "ccdb:") + * @param downloadPath The local path where to download the file if it's a ccdb path. Default is "/tmp/GeometryContainer/" + * @param ccdb Pointer to the CCDB manager to use for retrieving the file if it's a ccdb path. Must be set when path is a ccdb path. + * @param timeoutSeconds If positive, then this function will wait for these seconds after download before removing the downloaded file. + * @return The local path to the file, either the original local path or the path to the retrieved file from ccdb + */ + static std::string accessFile(const std::string& path, const std::string& downloadPath = "/tmp/GeometryContainer/", o2::ccdb::BasicCCDBManager* ccdb = nullptr, int timeoutSeconds = 0); +}; + +} // namespace o2::fastsim + +#endif // ALICE3_CORE_CONFIGURATIONPARSER_H_ diff --git a/ALICE3/Core/Decayer.h b/ALICE3/Core/Decayer.h index 81f08f5412d..fb39a372cb2 100644 --- a/ALICE3/Core/Decayer.h +++ b/ALICE3/Core/Decayer.h @@ -19,9 +19,13 @@ #ifndef ALICE3_CORE_DECAYER_H_ #define ALICE3_CORE_DECAYER_H_ +#include "ALICE3/Core/ConfigurationParser.h" +#include "ALICE3/Core/OTFParticle.h" #include "ALICE3/Core/TrackUtilities.h" +#include #include +#include #include #include @@ -33,11 +37,13 @@ #include #include #include +#include +#include +#include +#include #include -namespace o2 -{ -namespace upgrade +namespace o2::upgrade { class Decayer @@ -46,63 +52,93 @@ class Decayer // Default constructor Decayer() = default; + static constexpr double BranchingRatioTolerance = 0.01; + + struct DecayChannel { + double branchingRatio{}; + std::vector daughters; + }; + + template + void loadDecayTable(const std::string& path, const TDatabase& pdgDB, o2::ccdb::BasicCCDBManager* ccdb = nullptr) + { + std::string fileName = o2::fastsim::ConfigurationParser::accessFile(path, "./.ALICE3/Configuration/", ccdb); + std::vector mothers; + const std::map> config = o2::fastsim::ConfigurationParser::parseTEnvConfiguration(fileName, mothers); + + mDecayTable.clear(); + for (const auto& [motherName, channels] : config) { + const int motherPdg = std::stoi(motherName); + if (!pdgDB->GetParticle(motherPdg)) { + LOG(fatal) << "Decay table: unknown mother PDG code " << motherPdg; + } + for (const auto& [channelName, value] : channels) { + DecayChannel channel; + std::istringstream stream(value); + stream >> channel.branchingRatio; + int daughterPdg{}; + while (stream >> daughterPdg) { + if (!pdgDB->GetParticle(daughterPdg)) { + LOG(fatal) << "Decay table: unknown daughter PDG code " << daughterPdg << " in " << motherName << "." << channelName; + } + channel.daughters.push_back(daughterPdg); + } + if (!stream.eof() || channel.branchingRatio <= 0. || channel.daughters.size() < 2) { + LOG(fatal) << "Decay table: invalid channel " << motherName << "." << channelName << ": \"" << value << "\""; + } + mDecayTable[motherPdg].push_back(channel); + } + double brTotal = 0.; + for (const auto& channel : mDecayTable[motherPdg]) { + brTotal += channel.branchingRatio; + } + if (std::abs(brTotal - 1.) > BranchingRatioTolerance) { + LOG(fatal) << "Decay table: branching ratios of PDG " << motherPdg << " sum to " << brTotal << ", expected 1 within " << BranchingRatioTolerance; + } + LOG(info) << "Decay table: overriding " << mDecayTable[motherPdg].size() << " decay channel(s) of PDG " << motherPdg; + } + } + template - std::vector decayParticle(const TDatabase& pdgDB, const OTFParticle& particle) + std::vector decayParticle(const OTFParticle& particle, const TDatabase& pdgDB) { - const auto& particleInfo = pdgDB->GetParticle(particle.pdgCode()); + auto particleInfo = pdgDB->GetParticle(particle.pdgCode()); if (!particleInfo) { return {}; } const int charge = particleInfo->Charge() / 3; const double mass = particleInfo->Mass(); - - const double u = mRand3.Uniform(0.001, 0.999); - const double ctau = o2::constants::physics::LightSpeedCm2S * particleInfo->Lifetime(); // cm - const double betaGamma = particle.p() / mass; - const double rxyz = -betaGamma * ctau * std::log(1 - u); - double vx, vy, vz; - double px, py, e; + std::array decayVtx = generateDecayVertex(particle, pdgDB); + mVx = decayVtx[0]; + mVy = decayVtx[1]; + mVz = decayVtx[2]; + double px{}, py{}, e{}; if (!charge) { - vx = particle.vx() + rxyz * (particle.px() / particle.p()); - vy = particle.vy() + rxyz * (particle.py() / particle.p()); - vz = particle.vz() + rxyz * (particle.pz() / particle.p()); px = particle.px(); py = particle.py(); } else { - o2::track::TrackParCov track; - o2::math_utils::CircleXYf_t circle; - o2::upgrade::convertOTFParticleToO2Track(particle, track, pdgDB); - - float sna, csa; - track.getCircleParams(mBz, circle, sna, csa); - const double rxy = rxyz / std::sqrt(1. + track.getTgl() * track.getTgl()); - const double theta = rxy / circle.rC; - - vx = ((particle.vx() - circle.xC) * std::cos(theta) - (particle.vy() - circle.yC) * std::sin(theta)) + circle.xC; - vy = ((particle.vy() - circle.yC) * std::cos(theta) + (particle.vx() - circle.xC) * std::sin(theta)) + circle.yC; - vz = particle.vz() + rxyz * (particle.pz() / track.getP()); - - px = particle.px() * std::cos(theta) - particle.py() * std::sin(theta); - py = particle.py() * std::cos(theta) + particle.px() * std::sin(theta); + px = particle.px() * std::cos(mTheta) - particle.py() * std::sin(mTheta); + py = particle.py() * std::cos(mTheta) + particle.px() * std::sin(mTheta); } - double brTotal = 0.; e = std::sqrt(mass * mass + px * px + py * py + particle.pz() * particle.pz()); - for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { - brTotal += particleInfo->DecayChannel(ch)->BranchingRatio(); + const std::vector channels = getDecayChannels(particle.pdgCode(), particleInfo); + + double brTotal = 0.; + for (const auto& channel : channels) { + brTotal += channel.branchingRatio; } double brSum = 0.; std::vector dauMasses; std::vector pdgCodesDaughters; const double randomChannel = mRand3.Uniform(0., brTotal); - for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { - brSum += particleInfo->DecayChannel(ch)->BranchingRatio(); + for (const auto& channel : channels) { + brSum += channel.branchingRatio; if (randomChannel < brSum) { - for (int dau = 0; dau < particleInfo->DecayChannel(ch)->NDaughters(); ++dau) { - const int pdgDau = particleInfo->DecayChannel(ch)->DaughterPdgCode(dau); + for (const int pdgDau : channel.daughters) { pdgCodesDaughters.push_back(pdgDau); const auto& dauInfo = pdgDB->GetParticle(pdgDau); dauMasses.push_back(dauInfo->Mass()); @@ -121,32 +157,97 @@ class Decayer decay.Generate(); std::vector decayProducts; + decayProducts.reserve(dauMasses.size()); for (size_t i = 0; i < dauMasses.size(); ++i) { - o2::upgrade::OTFParticle particle; - TLorentzVector dau = *decay.GetDecay(i); - particle.setPDG(pdgCodesDaughters[i]); - particle.setVxVyVz(vx, vy, vz); - particle.setPxPyPzE(dau.Px(), dau.Py(), dau.Pz(), dau.E()); - decayProducts.push_back(particle); + o2::upgrade::OTFParticle daughter; + const TLorentzVector& dau = *decay.GetDecay(i); + daughter.setPDG(pdgCodesDaughters[i]); + daughter.setVxVyVz(mVx, mVy, mVz); + daughter.setPxPyPzE(dau.Px(), dau.Py(), dau.Pz(), dau.E()); + daughter.setBitOn(o2::upgrade::DecayerBits::ProducedByDecayer); + decayProducts.push_back(daughter); } - return decayProducts; } + template + std::array generateDecayVertex(const TParticle& particle, const TDatabase& pdgDB) + { + std::array decayVertex{}; + auto particleInfo = pdgDB->GetParticle(particle.pdgCode()); + if (!particleInfo) { + return {}; + } + + const int charge = particleInfo->Charge() / 3; + const double mass = particleInfo->Mass(); + const double u = mRand3.Uniform(0.001, 0.999); + const double ctau = o2::constants::physics::LightSpeedCm2S * particleInfo->Lifetime(); // cm + const double betaGamma = particle.p() / mass; + const double rxyz = -betaGamma * ctau * std::log(1 - u); + + if (!charge) { + decayVertex[0] = particle.vx() + rxyz * (particle.px() / particle.p()); + decayVertex[1] = particle.vy() + rxyz * (particle.py() / particle.p()); + decayVertex[2] = particle.vz() + rxyz * (particle.pz() / particle.p()); + } else { + o2::math_utils::CircleXYf_t circle; + o2::track::TrackParCov track = o2::upgrade::convertMCParticleToO2Track(particle, pdgDB); + + float sna{}, csa{}; + track.getCircleParams(mBz, circle, sna, csa); + const double rxy = rxyz / std::sqrt(1. + track.getTgl() * track.getTgl()); + mTheta = rxy / circle.rC; + + decayVertex[0] = ((particle.vx() - circle.xC) * std::cos(mTheta) - (particle.vy() - circle.yC) * std::sin(mTheta)) + circle.xC; + decayVertex[1] = ((particle.vy() - circle.yC) * std::cos(mTheta) + (particle.vx() - circle.xC) * std::sin(mTheta)) + circle.yC; + decayVertex[2] = particle.vz() + rxyz * (particle.pz() / track.getP()); + } + return decayVertex; + } + + // Setters + void setBField(const double b) { mBz = b; } void setSeed(const int seed) { mRand3.SetSeed(seed); // For decay length sampling gRandom->SetSeed(seed); // For TGenPhaseSpace } - void setBField(const double b) { mBz = b; } + // Getters + [[nodiscard]] float getSecondaryVertexX() const { return static_cast(mVx); } + [[nodiscard]] float getSecondaryVertexY() const { return static_cast(mVy); } + [[nodiscard]] float getSecondaryVertexZ() const { return static_cast(mVz); } + [[nodiscard]] float getDecayRadius() const { return static_cast(std::hypot(mVx, mVy)); } private: + /// Decay channels from the loaded decay table if the mother is listed there, otherwise from TDatabasePDG + template + std::vector getDecayChannels(const int pdgCode, const TParticleInfo& particleInfo) const + { + if (const auto it = mDecayTable.find(pdgCode); it != mDecayTable.end()) { + return it->second; + } + + std::vector channels; + for (int ch = 0; ch < particleInfo->NDecayChannels(); ++ch) { + DecayChannel channel; + channel.branchingRatio = particleInfo->DecayChannel(ch)->BranchingRatio(); + for (int dau = 0; dau < particleInfo->DecayChannel(ch)->NDaughters(); ++dau) { + channel.daughters.push_back(particleInfo->DecayChannel(ch)->DaughterPdgCode(dau)); + } + channels.push_back(channel); + } + return channels; + } + + std::unordered_map> mDecayTable; + double mBz{20.}; // kG + double mVx{-1.}, mVy{-1.}, mVz{-1.}; + double mTheta{}; TRandom3 mRand3; - double mBz; }; -} // namespace upgrade -} // namespace o2 +} // namespace o2::upgrade #endif // ALICE3_CORE_DECAYER_H_ diff --git a/ALICE3/Core/DelphesO2LutWriter.cxx b/ALICE3/Core/DelphesO2LutWriter.cxx deleted file mode 100644 index c2df9431ef4..00000000000 --- a/ALICE3/Core/DelphesO2LutWriter.cxx +++ /dev/null @@ -1,551 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @file DelphesO2LutWriter.cxx -/// @brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutWrite.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutWrite.cc -/// @author: Roberto Preghenella -/// @email: preghenella@bo.infn.it -/// - -#include "ALICE3/Core/DelphesO2LutWriter.h" - -#include "ALICE3/Core/DelphesO2TrackSmearer.h" -#include "ALICE3/Core/FastTracker.h" -#include "ALICE3/Core/TrackUtilities.h" - -#include -#include - -#include -#include -#include -#include -#include // IWYU pragma: keep (do not replace with TMatrixDfwd.h) -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -// #define USE_FWD_PARAM -#ifdef USE_FWD_PARAM -#include "fwdRes.C" -#endif - -namespace o2::fastsim -{ - -void DelphesO2LutWriter::print() const -{ - LOG(info) << " --- Printing configuration of LUT writer --- "; - LOG(info) << " -> etaMaxBarrel = " << etaMaxBarrel; - LOG(info) << " -> usePara = " << usePara; - LOG(info) << " -> useDipole = " << useDipole; - LOG(info) << " -> useFlatDipole = " << useFlatDipole; - LOG(info) << " -> mAtLeastHits = " << mAtLeastHits; - LOG(info) << " -> mAtLeastCorr = " << mAtLeastCorr; - LOG(info) << " -> mAtLeastFake = " << mAtLeastFake; - LOG(info) << " -> Nch Binning: = " << mNchBinning.toString(); - LOG(info) << " -> Radius Binning: = " << mRadiusBinning.toString(); - LOG(info) << " -> Eta Binning: = " << mEtaBinning.toString(); - LOG(info) << " -> Pt Binning: = " << mPtBinning.toString(); - LOG(info) << " --- End of configuration --- "; -} - -std::string DelphesO2LutWriter::LutBinning::toString() const -{ - std::string str = ""; - str.append(log ? "log" : "lin"); - str.append(" nbins: "); - str.append(std::to_string(nbins)); - str.append(" min: "); - str.append(std::to_string(min)); - str.append(" max: "); - str.append(std::to_string(max)); - return str; -} - -bool DelphesO2LutWriter::fatSolve(lutEntry_t& lutEntry, - float pt, - float eta, - const float mass, - size_t itof, - size_t otof, - int q, - const float nch) -{ - lutEntry.valid = false; - - static TLorentzVector tlv; - tlv.SetPtEtaPhiM(pt, eta, 0., mass); - o2::track::TrackParCov trkIn; - o2::upgrade::convertTLorentzVectorToO2Track(q, tlv, {0., 0., 0.}, trkIn); - // tlv.Print(); - // return fmt::format("X:{:+.4e} Alp:{:+.3e} Par: {:+.4e} {:+.4e} {:+.4e} {:+.4e} {:+.4e} |Q|:{:d} {:s}\n", - // getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt(), getAbsCharge(), getPID().getName()); - // trkIn.print(); - o2::track::TrackParCov trkOut; - const int status = fat.FastTrack(trkIn, trkOut, nch); - if (status <= mAtLeastHits) { - LOGF(info, " --- fatSolve: FastTrack failed ---"); - // tlv.Print(); - return false; - } - LOGF(info, " --- fatSolve: FastTrack succeeded %d ---", status); - // trkOut.print(); - lutEntry.valid = true; - lutEntry.itof = fat.GetGoodHitProb(itof); - lutEntry.otof = fat.GetGoodHitProb(otof); - static constexpr int nCov = 15; - for (int i = 0; i < nCov; ++i) - lutEntry.covm[i] = trkOut.getCov()[i]; - - // define the efficiency - auto totfake = 0.; - lutEntry.eff = 1.; - for (size_t i = 1; i < fat.GetNLayers(); ++i) { - if (fat.IsLayerInert(i)) - continue; // skip inert layers - auto igoodhit = fat.GetGoodHitProb(i); - if (igoodhit <= 0. || i == itof || i == otof) - continue; - lutEntry.eff *= igoodhit; - auto pairfake = 0.; - for (size_t j = i + 1; j < fat.GetNLayers(); ++j) { - auto jgoodhit = fat.GetGoodHitProb(j); - if (jgoodhit <= 0. || j == itof || j == otof) - continue; - pairfake = (1. - igoodhit) * (1. - jgoodhit); - break; - } - totfake += pairfake; - } - lutEntry.eff2 = (1. - totfake); - - return true; -} - -#ifdef USE_FWD_PARAM -bool DelphesO2LutWriter::fwdSolve(float* covm, float pt, float eta, float mass) -{ - if (fwdRes(covm, pt, eta, mass) < 0) - return false; - return true; -} -#else -bool DelphesO2LutWriter::fwdSolve(float*, float, float, float) -{ - return false; -} -#endif - -bool DelphesO2LutWriter::fwdPara(lutEntry_t& lutEntry, float pt, float eta, float mass, float Bfield) -{ - lutEntry.valid = false; - - // parametrised forward response; interpolates between FAT at eta = 1.75 and a fixed parametrisation at eta = 4; only diagonal elements - static constexpr float etaLimit = 4.0f; - if (std::fabs(eta) < etaMaxBarrel || std::fabs(eta) > etaLimit) - return false; - - if (!fatSolve(lutEntry, pt, etaMaxBarrel, mass)) - return false; - static constexpr int nCov = 15; - float covmbarrel[nCov] = {0}; - for (int i = 0; i < nCov; ++i) { - covmbarrel[i] = lutEntry.covm[i]; - } - - // parametrisation at eta = 4 - const double beta = 1. / std::sqrt(1 + mass * mass / pt / pt / std::cosh(eta) / std::cosh(eta)); - const float dcaPos = 2.5e-4 / std::sqrt(3); // 2.5 micron/sqrt(3) - const float r0 = 0.5; // layer 0 radius [cm] - const float r1 = 1.3; - const float r2 = 2.5; - const float x0layer = 0.001; // material budget (rad length) per layer - const double sigmaAlpha = 0.0136 / beta / pt * std::sqrt(x0layer * std::cosh(eta)) * (1 + 0.038 * std::log(x0layer * std::cosh(eta))); - const double dcaxyMs = sigmaAlpha * r0 * std::sqrt(1 + r1 * r1 / (r2 - r0) / (r2 - r0)); - const double dcaxy2 = dcaPos * dcaPos + dcaxyMs * dcaxyMs; - - const double dcazMs = sigmaAlpha * r0 * std::cosh(eta); - const double dcaz2 = dcaPos * dcaPos + dcazMs * dcazMs; - - const float Leta = 2.8 / std::sinh(eta) - 0.01 * r0; // m - const double relmomresPos = 10e-6 * pt / 0.3 / Bfield / Leta / Leta * std::sqrt(720. / 15.); - - const float relmomresBarrel = std::sqrt(covmbarrel[14]) * pt; - const float rOuter = 1; // m - const float relmomresPosBarrel = 10e-6 * pt / 0.3 / Bfield / rOuter / rOuter / std::sqrt(720. / 15.); - const float relmomresMSBarrel = std::sqrt(relmomresBarrel * relmomresBarrel - relmomresPosBarrel * relmomresPosBarrel); - - // interpolate MS contrib (rel resolution 0.4 at eta = 4) - const float relmomresMSEta4 = 0.4 / beta * 0.5 / Bfield; - const float relmomresMS = relmomresMSEta4 * std::pow(relmomresMSEta4 / relmomresMSBarrel, (std::fabs(eta) - 4.) / (4. - etaMaxBarrel)); - const float momresTot = pt * std::sqrt(relmomresPos * relmomresPos + relmomresMS * relmomresMS); // total absolute mom reso - - // Fill cov matrix diag - for (int i = 0; i < 15; ++i) - lutEntry.covm[i] = 0; - - lutEntry.covm[0] = covmbarrel[0]; - if (dcaxy2 > lutEntry.covm[0]) - lutEntry.covm[0] = dcaxy2; - lutEntry.covm[2] = covmbarrel[2]; - if (dcaz2 > lutEntry.covm[2]) - lutEntry.covm[2] = dcaz2; - lutEntry.covm[5] = covmbarrel[5]; // sigma^2 sin(phi) - lutEntry.covm[9] = covmbarrel[9]; // sigma^2 tanl - lutEntry.covm[14] = momresTot * momresTot / pt / pt / pt / pt; // sigma^2 1/pt - // Check that all numbers are numbers - for (int i = 0; i < 15; ++i) { - if (std::isnan(lutEntry.covm[i])) { - LOGF(info, " --- lutEntry.covm[%d] is NaN", i); - return false; - } - } - return true; -} - -void DelphesO2LutWriter::lutWrite(const char* filename, int pdg, float field, size_t itof, size_t otof) -{ - - if (useFlatDipole && useDipole) { - LOGF(info, "Both dipole and dipole flat flags are on, please use only one of them"); - return; - } - - // output file - std::ofstream lutFile(filename, std::ofstream::binary); - if (!lutFile.is_open()) { - LOGF(info, "Did not manage to open output file!!"); - return; - } - - // write header - lutHeader_t lutHeader; - // pid - lutHeader.pdg = pdg; - const TParticlePDG* particle = TDatabasePDG::Instance()->GetParticle(pdg); - if (!particle) { - LOG(fatal) << "Cannot find particle with PDG code " << pdg; - return; - } - lutHeader.mass = particle->Mass(); - const int q = std::abs(particle->Charge()) / 3; - if (q <= 0) { - LOGF(info, "Negative or null charge (%f) for pdg code %i. Fix the charge!", particle->Charge(), pdg); - return; - } - lutHeader.field = field; - auto setMap = [](map_t& map, LutBinning b) { - map.log = b.log; - map.nbins = b.nbins; - map.min = b.min; - map.max = b.max; - }; - // nch - setMap(lutHeader.nchmap, mNchBinning); - // radius - setMap(lutHeader.radmap, mRadiusBinning); - // eta - setMap(lutHeader.etamap, mEtaBinning); - // pt - setMap(lutHeader.ptmap, mPtBinning); - - lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader)); - - // entries - const int nnch = lutHeader.nchmap.nbins; - const int nrad = lutHeader.radmap.nbins; - const int neta = lutHeader.etamap.nbins; - const int npt = lutHeader.ptmap.nbins; - lutEntry_t lutEntry; - - // write entries - int nCalls = 0; - int successfullCalls = 0; - int failedCalls = 0; - for (int inch = 0; inch < nnch; ++inch) { - LOGF(info, " --- writing nch = %d/%d", inch, nnch); - auto nch = lutHeader.nchmap.eval(inch); - lutEntry.nch = nch; - fat.SetdNdEtaCent(nch); - for (int irad = 0; irad < nrad; ++irad) { - LOGF(info, " --- writing irad = %d/%d", irad, nrad); - for (int ieta = 0; ieta < neta; ++ieta) { - LOGF(info, " --- writing ieta = %d/%d", ieta, neta); - auto eta = lutHeader.etamap.eval(ieta); - lutEntry.eta = lutHeader.etamap.eval(ieta); - for (int ipt = 0; ipt < npt; ++ipt) { - nCalls++; - LOGF(info, " --- writing ipt = %d/%d", ipt, npt); - lutEntry.pt = lutHeader.ptmap.eval(ipt); - lutEntry.valid = true; - if (std::fabs(eta) <= etaMaxBarrel) { // full lever arm ends at etaMaxBarrel - LOGF(info, "Solving in the barrel"); - // LOGF(info, " --- fatSolve: pt = %f, eta = %f, mass = %f, field=%f", lutEntry.pt, lutEntry.eta, lutHeader.mass, lutHeader.field); - successfullCalls++; - if (!fatSolve(lutEntry, lutEntry.pt, lutEntry.eta, lutHeader.mass, itof, otof, q)) { - // LOGF(info, " --- fatSolve: error"); - lutEntry.valid = false; - lutEntry.eff = 0.; - lutEntry.eff2 = 0.; - for (int i = 0; i < 15; ++i) { - lutEntry.covm[i] = 0.; - } - successfullCalls--; - failedCalls++; - } - } else { - LOGF(info, "Solving outside the barrel"); - // LOGF(info, " --- fwdSolve: pt = %f, eta = %f, mass = %f, field=%f", lutEntry.pt, lutEntry.eta, lutHeader.mass, lutHeader.field); - lutEntry.eff = 1.; - lutEntry.eff2 = 1.; - bool retval = true; - successfullCalls++; - if (useFlatDipole) { // Using the parametrization at the border of the barrel - retval = fatSolve(lutEntry, lutEntry.pt, etaMaxBarrel, lutHeader.mass, itof, otof, q); - } else if (usePara) { - retval = fwdPara(lutEntry, lutEntry.pt, lutEntry.eta, lutHeader.mass, field); - } else { - retval = fwdSolve(lutEntry.covm, lutEntry.pt, lutEntry.eta, lutHeader.mass); - } - if (useDipole) { // Using the parametrization at the border of the barrel only for efficiency and momentum resolution - lutEntry_t lutEntryBarrel; - retval = fatSolve(lutEntryBarrel, lutEntry.pt, etaMaxBarrel, lutHeader.mass, itof, otof, q); - lutEntry.valid = lutEntryBarrel.valid; - lutEntry.covm[14] = lutEntryBarrel.covm[14]; - lutEntry.eff = lutEntryBarrel.eff; - lutEntry.eff2 = lutEntryBarrel.eff2; - } - if (!retval) { - LOGF(info, " --- fwdSolve: error"); - lutEntry.valid = false; - for (int i = 0; i < 15; ++i) { - lutEntry.covm[i] = 0.; - } - successfullCalls--; - failedCalls++; - } - } - LOGF(info, "Diagonalizing"); - diagonalise(lutEntry); - LOGF(info, "Writing"); - lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); - } - } - } - } - LOGF(info, " --- finished writing LUT file %s", filename); - LOGF(info, " --- successfull calls: %d/%d, failed calls: %d/%d", successfullCalls, nCalls, failedCalls, nCalls); - lutFile.close(); -} - -void DelphesO2LutWriter::diagonalise(lutEntry_t& lutEntry) -{ - static constexpr int kEig = 5; - TMatrixDSym m(kEig); - for (int i = 0, k = 0; i < kEig; ++i) { - for (int j = 0; j < i + 1; ++j, ++k) { - m(i, j) = lutEntry.covm[k]; - m(j, i) = lutEntry.covm[k]; - } - } - - // m.Print(); - TMatrixDSymEigen eigen(m); - // eigenvalues vector - TVectorD eigenVal = eigen.GetEigenValues(); - for (int i = 0; i < kEig; ++i) - lutEntry.eigval[i] = eigenVal[i]; - // eigenvectors matrix - TMatrixD eigenVec = eigen.GetEigenVectors(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) - lutEntry.eigvec[i][j] = eigenVec[i][j]; - // inverse eigenvectors matrix - eigenVec.Invert(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) - lutEntry.eiginv[i][j] = eigenVec[i][j]; -} - -TGraph* DelphesO2LutWriter::lutRead(const char* filename, int pdg, int what, int vs, float nch, float radius, float eta, float pt) -{ - LOGF(info, " --- reading LUT file %s", filename); - // vs - static const int kNch = 0; - static const int kEta = 1; - static const int kPt = 2; - - // what - static const int kEfficiency = 0; - static const int kEfficiency2 = 1; - static const int kEfficiencyInnerTOF = 2; - static const int kEfficiencyOuterTOF = 3; - static const int kPtResolution = 4; - static const int kRPhiResolution = 5; - static const int kZResolution = 6; - - o2::delphes::DelphesO2TrackSmearer smearer; - smearer.loadTable(pdg, filename); - auto lutHeader = smearer.getLUTHeader(pdg); - lutHeader->print(); - map_t lutMap; - switch (vs) { - case kNch: - lutMap = lutHeader->nchmap; - break; - case kEta: - lutMap = lutHeader->etamap; - break; - case kPt: - lutMap = lutHeader->ptmap; - break; - } - auto nbins = lutMap.nbins; - auto g = new TGraph(); - g->SetName(Form("lut_%s_%d_vs_%d_what_%d", filename, pdg, vs, what)); - g->SetTitle(Form("LUT for %s, pdg %d, vs %d, what %d", filename, pdg, vs, what)); - switch (vs) { - case kNch: - LOGF(info, " --- vs = kNch"); - g->GetXaxis()->SetTitle("Nch"); - break; - case kEta: - LOGF(info, " --- vs = kEta"); - g->GetXaxis()->SetTitle("#eta"); - break; - case kPt: - LOGF(info, " --- vs = kPt"); - g->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - break; - default: - LOGF(info, " --- error: unknown vs %d", vs); - return nullptr; - } - switch (what) { - case kEfficiency: - LOGF(info, " --- what = kEfficiency"); - g->GetYaxis()->SetTitle("Efficiency (%)"); - break; - case kEfficiency2: - LOGF(info, " --- what = kEfficiency2"); - g->GetYaxis()->SetTitle("Efficiency2 (%)"); - break; - case kEfficiencyInnerTOF: - LOGF(info, " --- what = kEfficiencyInnerTOF"); - g->GetYaxis()->SetTitle("Inner TOF Efficiency (%)"); - break; - case kEfficiencyOuterTOF: - LOGF(info, " --- what = kEfficiencyOuterTOF"); - g->GetYaxis()->SetTitle("Outer TOF Efficiency (%)"); - break; - case kPtResolution: - LOGF(info, " --- what = kPtResolution"); - g->GetYaxis()->SetTitle("p_{T} Resolution (%)"); - break; - case kRPhiResolution: - LOGF(info, " --- what = kRPhiResolution"); - g->GetYaxis()->SetTitle("R#phi Resolution (#mum)"); - break; - case kZResolution: - LOGF(info, " --- what = kZResolution"); - g->GetYaxis()->SetTitle("Z Resolution (#mum)"); - break; - default: - LOGF(info, " --- error: unknown what %d", what); - return nullptr; - } - - bool canBeInvalid = true; - for (int i = 0; i < nbins; ++i) { - switch (vs) { - case kNch: - nch = lutMap.eval(i); - break; - case kEta: - eta = lutMap.eval(i); - break; - case kPt: - pt = lutMap.eval(i); - break; - } - float eff = 0.; - auto lutEntry = smearer.getLUTEntry(pdg, nch, radius, eta, pt, eff); - if (!lutEntry->valid || lutEntry->eff == 0.) { - if (!canBeInvalid) { - LOGF(info, " --- warning: it cannot be invalid"); - } - continue; - } - canBeInvalid = false; - - double cen = 0.; - switch (vs) { - case kNch: - cen = lutEntry->nch; - break; - case kEta: - cen = lutEntry->eta; - break; - case kPt: - cen = lutEntry->pt; - break; - } - double val = 0.; - switch (what) { - case kEfficiency: - val = lutEntry->eff * 100.; // efficiency (%) - break; - case kEfficiency2: - val = lutEntry->eff2 * 100.; // efficiency (%) - break; - case kEfficiencyInnerTOF: - val = lutEntry->itof * 100.; // efficiency (%) - break; - case kEfficiencyOuterTOF: - val = lutEntry->otof * 100.; // efficiency (%) - break; - case kPtResolution: - val = std::sqrt(lutEntry->covm[14]) * lutEntry->pt * 100.; // pt resolution (%) - break; - case kRPhiResolution: - val = std::sqrt(lutEntry->covm[0]) * 1.e4; // rphi resolution (um) - break; - case kZResolution: - val = std::sqrt(lutEntry->covm[1]) * 1.e4; // z resolution (um) - break; - default: - LOGF(info, " --- error: unknown what %d", what); - break; - } - g->AddPoint(cen, val); - } - - return g; -} -} // namespace o2::fastsim - -ClassImp(o2::fastsim::DelphesO2LutWriter); diff --git a/ALICE3/Core/DelphesO2LutWriter.h b/ALICE3/Core/DelphesO2LutWriter.h deleted file mode 100644 index e234efd3bdb..00000000000 --- a/ALICE3/Core/DelphesO2LutWriter.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// \file DelphesO2LutWriter.h -/// \brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutWrite.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutWrite.cc -/// \author: Roberto Preghenella -/// \email: preghenella@bo.infn.it -/// - -#ifndef ALICE3_CORE_DELPHESO2LUTWRITER_H_ -#define ALICE3_CORE_DELPHESO2LUTWRITER_H_ - -#include "ALICE3/Core/DelphesO2TrackSmearer.h" -#include "ALICE3/Core/FastTracker.h" - -#include - -#include - -#include - -#include -#include - -namespace o2::fastsim -{ -class DelphesO2LutWriter -{ - public: - DelphesO2LutWriter() = default; - virtual ~DelphesO2LutWriter() = default; - - // Setters - void setBinningNch(bool log, int nbins, float min, float max) { mNchBinning = {log, nbins, min, max}; } - void setBinningRadius(bool log, int nbins, float min, float max) { mRadiusBinning = {log, nbins, min, max}; } - void setBinningEta(bool log, int nbins, float min, float max) { mEtaBinning = {log, nbins, min, max}; } - void setBinningPt(bool log, int nbins, float min, float max) { mPtBinning = {log, nbins, min, max}; } - void setEtaMaxBarrel(float eta) { etaMaxBarrel = eta; } - void setAtLeastHits(int n) { mAtLeastHits = n; } - void setAtLeastCorr(int n) { mAtLeastCorr = n; } - void setAtLeastFake(int n) { mAtLeastFake = n; } - bool fatSolve(lutEntry_t& lutEntry, - float pt = 0.1, - float eta = 0.0, - const float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion], - size_t itof = 0, - size_t otof = 0, - int q = 1, - const float nch = 1); - - void print() const; - bool fwdSolve(float* covm, float pt = 0.1, float eta = 0.0, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion]); - bool fwdPara(lutEntry_t& lutEntry, float pt = 0.1, float eta = 0.0, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion], float Bfield = 0.5); - void lutWrite(const char* filename = "lutCovm.dat", int pdg = 211, float field = 0.2, size_t itof = 0, size_t otof = 0); - TGraph* lutRead(const char* filename, int pdg, int what, int vs, float nch = 0., float radius = 0., float eta = 0., float pt = 0.); - - o2::fastsim::FastTracker fat; - - private: - void diagonalise(lutEntry_t& lutEntry); - float etaMaxBarrel = 1.75f; - bool usePara = true; // use fwd parameterisation - bool useDipole = false; // use dipole i.e. flat parametrization for efficiency and momentum resolution - bool useFlatDipole = false; // use dipole i.e. flat parametrization outside of the barrel - - int mAtLeastHits = 4; - int mAtLeastCorr = 4; - int mAtLeastFake = 0; - - // Binning of the LUT to make - struct LutBinning { - bool log; - int nbins; - float min; - float max; - std::string toString() const; - }; - LutBinning mNchBinning = {true, 20, 0.5f, 3.5f}; - LutBinning mRadiusBinning = {false, 1, 0.0f, 100.0f}; - LutBinning mEtaBinning = {false, 80, -4.0f, 4.0f}; - LutBinning mPtBinning = {true, 200, -2.0f, 2.0f}; - - ClassDef(DelphesO2LutWriter, 1); -}; -} // namespace o2::fastsim - -#endif // ALICE3_CORE_DELPHESO2LUTWRITER_H_ diff --git a/ALICE3/Core/DelphesO2TrackSmearer.cxx b/ALICE3/Core/DelphesO2TrackSmearer.cxx deleted file mode 100644 index 7d7f6fc6077..00000000000 --- a/ALICE3/Core/DelphesO2TrackSmearer.cxx +++ /dev/null @@ -1,359 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file DelphesO2TrackSmearer.cxx -/// \author Roberto Preghenella -/// \brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutCovm.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutCovm.hh -/// DelphesO2/src/TrackSmearer.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.cc -/// DelphesO2/src/TrackSmearer.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.hh -/// @email: preghenella@bo.infn.it -/// - -////////////////////////////// -// DelphesO2/src/lutCovm.cc // -////////////////////////////// - -/// @author: Roberto Preghenella -/// @email: preghenella@bo.infn.it - -#include "ALICE3/Core/DelphesO2TrackSmearer.h" - -#include "ALICE3/Core/GeometryContainer.h" - -#include -#include - -#include - -#include -#include -#include -#include - -namespace o2 -{ -namespace delphes -{ - -/*****************************************************************/ - -bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) -{ - if (!filename || filename[0] == '\0') { - LOG(info) << " --- No LUT file provided for PDG " << pdg << ". Skipping load."; - return false; - } - const auto ipdg = getIndexPDG(pdg); - LOGF(info, "Will load %s lut file ..: '%s'", getParticleName(pdg), filename); - if (mLUTHeader[ipdg] && !forceReload) { - LOG(info) << " --- LUT table for PDG " << pdg << " has been already loaded with index " << ipdg << std::endl; - return false; - } - - const std::string localFilename = o2::fastsim::GeometryEntry::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, 10); - mLUTHeader[ipdg] = new lutHeader_t; - - std::ifstream lutFile(localFilename, std::ifstream::binary); - if (!lutFile.is_open()) { - LOG(info) << " --- cannot open covariance matrix file for PDG " << pdg << ": " << localFilename << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - lutFile.read(reinterpret_cast(mLUTHeader[ipdg]), sizeof(lutHeader_t)); - if (lutFile.gcount() != sizeof(lutHeader_t)) { - LOG(info) << " --- troubles reading covariance matrix header for PDG " << pdg << ": " << filename << std::endl; - LOG(info) << " --- expected/detected " << sizeof(lutHeader_t) << "/" << lutFile.gcount() << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - if (mLUTHeader[ipdg]->version != LUTCOVM_VERSION) { - LOG(info) << " --- LUT header version mismatch: expected/detected = " << LUTCOVM_VERSION << "/" << mLUTHeader[ipdg]->version << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - bool specialPdgCase = false; - switch (pdg) { // Handle special cases - case o2::constants::physics::kAlpha: // Special case: Allow Alpha particles to use He3 LUT - specialPdgCase = (mLUTHeader[ipdg]->pdg == o2::constants::physics::kHelium3); - if (specialPdgCase) - LOG(info) - << " --- Alpha particles (PDG " << pdg << ") will use He3 LUT data (PDG " << mLUTHeader[ipdg]->pdg << ")" << std::endl; - break; - default: - break; - } - if (mLUTHeader[ipdg]->pdg != pdg && !specialPdgCase) { - LOG(info) << " --- LUT header PDG mismatch: expected/detected = " << pdg << "/" << mLUTHeader[ipdg]->pdg << std::endl; - delete mLUTHeader[ipdg]; - mLUTHeader[ipdg] = nullptr; - return false; - } - const int nnch = mLUTHeader[ipdg]->nchmap.nbins; - const int nrad = mLUTHeader[ipdg]->radmap.nbins; - const int neta = mLUTHeader[ipdg]->etamap.nbins; - const int npt = mLUTHeader[ipdg]->ptmap.nbins; - mLUTEntry[ipdg] = new lutEntry_t****[nnch]; - for (int inch = 0; inch < nnch; ++inch) { - mLUTEntry[ipdg][inch] = new lutEntry_t***[nrad]; - for (int irad = 0; irad < nrad; ++irad) { - mLUTEntry[ipdg][inch][irad] = new lutEntry_t**[neta]; - for (int ieta = 0; ieta < neta; ++ieta) { - mLUTEntry[ipdg][inch][irad][ieta] = new lutEntry_t*[npt]; - for (int ipt = 0; ipt < npt; ++ipt) { - mLUTEntry[ipdg][inch][irad][ieta][ipt] = new lutEntry_t; - lutFile.read(reinterpret_cast(mLUTEntry[ipdg][inch][irad][ieta][ipt]), sizeof(lutEntry_t)); - if (lutFile.gcount() != sizeof(lutEntry_t)) { - LOG(info) << " --- troubles reading covariance matrix entry for PDG " << pdg << ": " << localFilename << std::endl; - LOG(info) << " --- expected/detected " << sizeof(lutHeader_t) << "/" << lutFile.gcount() << std::endl; - return false; - } - } - } - } - } - LOG(info) << " --- read covariance matrix table for PDG " << pdg << ": " << filename << std::endl; - mLUTHeader[ipdg]->print(); - - lutFile.close(); - return true; -} - -/*****************************************************************/ - -lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, const float radius, const float eta, const float pt, float& interpolatedEff) -{ - const int ipdg = getIndexPDG(pdg); - if (!mLUTHeader[ipdg]) { - return nullptr; - } - - auto inch = mLUTHeader[ipdg]->nchmap.find(nch); - auto irad = mLUTHeader[ipdg]->radmap.find(radius); - auto ieta = mLUTHeader[ipdg]->etamap.find(eta); - auto ipt = mLUTHeader[ipdg]->ptmap.find(pt); - - // Interpolate if requested - auto fraction = mLUTHeader[ipdg]->nchmap.fracPositionWithinBin(nch); - if (mInterpolateEfficiency) { - static constexpr float kFractionThreshold = 0.5f; - if (fraction > kFractionThreshold) { - switch (mWhatEfficiency) { - case 1: - if (inch < mLUTHeader[ipdg]->nchmap.nbins - 1) { - interpolatedEff = (1.5f - fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff + (-0.5f + fraction) * mLUTEntry[ipdg][inch + 1][irad][ieta][ipt]->eff; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff; - } - break; - case 2: - if (inch < mLUTHeader[ipdg]->nchmap.nbins - 1) { - interpolatedEff = (1.5f - fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2 + (-0.5f + fraction) * mLUTEntry[ipdg][inch + 1][irad][ieta][ipt]->eff2; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2; - } - break; - default: - LOG(fatal) << " --- getLUTEntry: unknown efficiency type " << mWhatEfficiency; - } - } else { - float comparisonValue = mLUTHeader[ipdg]->nchmap.log ? std::log10(nch) : nch; - switch (mWhatEfficiency) { - case 1: - if (inch > 0 && comparisonValue < mLUTHeader[ipdg]->nchmap.max) { - interpolatedEff = (0.5f + fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff + (0.5f - fraction) * mLUTEntry[ipdg][inch - 1][irad][ieta][ipt]->eff; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff; - } - break; - case 2: - if (inch > 0 && comparisonValue < mLUTHeader[ipdg]->nchmap.max) { - interpolatedEff = (0.5f + fraction) * mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2 + (0.5f - fraction) * mLUTEntry[ipdg][inch - 1][irad][ieta][ipt]->eff2; - } else { - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2; - } - break; - default: - LOG(fatal) << " --- getLUTEntry: unknown efficiency type " << mWhatEfficiency; - } - } - } else { - switch (mWhatEfficiency) { - case 1: - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff; - break; - case 2: - interpolatedEff = mLUTEntry[ipdg][inch][irad][ieta][ipt]->eff2; - break; - default: - LOG(fatal) << " --- getLUTEntry: unknown efficiency type " << mWhatEfficiency; - } - } - return mLUTEntry[ipdg][inch][irad][ieta][ipt]; -} //; - -/*****************************************************************/ - -bool TrackSmearer::smearTrack(O2Track& o2track, lutEntry_t* lutEntry, float interpolatedEff) -{ - bool isReconstructed = true; - // generate efficiency - if (mUseEfficiency) { - auto eff = 0.; - switch (mWhatEfficiency) { - case 1: - eff = lutEntry->eff; - break; - case 2: - eff = lutEntry->eff2; - break; - } - if (mInterpolateEfficiency) - eff = interpolatedEff; - if (gRandom->Uniform() > eff) - isReconstructed = false; - } - - // return false already now in case not reco'ed - if (!isReconstructed && mSkipUnreconstructed) - return false; - - // transform params vector and smear - static constexpr int kParSize = 5; - double params[kParSize]; - for (int i = 0; i < kParSize; ++i) { - double val = 0.; - for (int j = 0; j < kParSize; ++j) - val += lutEntry->eigvec[j][i] * o2track.getParam(j); - params[i] = gRandom->Gaus(val, std::sqrt(lutEntry->eigval[i])); - } - // transform back params vector - for (int i = 0; i < kParSize; ++i) { - double val = 0.; - for (int j = 0; j < kParSize; ++j) - val += lutEntry->eiginv[j][i] * params[j]; - o2track.setParam(val, i); - } - // should make a sanity check that par[2] sin(phi) is in [-1, 1] - if (std::fabs(o2track.getParam(2)) > 1.) { - LOG(info) << " --- smearTrack failed sin(phi) sanity check: " << o2track.getParam(2) << std::endl; - } - // set covariance matrix - static constexpr int kCovMatSize = 15; - for (int i = 0; i < kCovMatSize; ++i) - o2track.setCov(lutEntry->covm[i], i); - return isReconstructed; -} - -/*****************************************************************/ - -bool TrackSmearer::smearTrack(O2Track& o2track, int pdg, float nch) -{ - auto pt = o2track.getPt(); - switch (pdg) { - case o2::constants::physics::kHelium3: - case -o2::constants::physics::kHelium3: - pt *= 2.f; - break; - } - auto eta = o2track.getEta(); - float interpolatedEff = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, interpolatedEff); - if (!lutEntry || !lutEntry->valid) - return false; - return smearTrack(o2track, lutEntry, interpolatedEff); -} - -/*****************************************************************/ -// relative uncertainty on pt -double TrackSmearer::getPtRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto val = std::sqrt(lutEntry->covm[14]) * lutEntry->pt; - return val; -} - -/*****************************************************************/ -// relative uncertainty on eta -double TrackSmearer::getEtaRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto sigmatgl = std::sqrt(lutEntry->covm[9]); // sigmatgl2 - auto etaRes = std::fabs(std::sin(2.0 * std::atan(std::exp(-eta)))) * sigmatgl; // propagate tgl to eta uncertainty - etaRes /= lutEntry->eta; // relative uncertainty - return etaRes; -} -/*****************************************************************/ -// absolute uncertainty on pt -double TrackSmearer::getAbsPtRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto val = std::sqrt(lutEntry->covm[14]) * lutEntry->pt * lutEntry->pt; - return val; -} - -/*****************************************************************/ -// absolute uncertainty on eta -double TrackSmearer::getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt) -{ - float dummy = 0.0f; - lutEntry_t* lutEntry = getLUTEntry(pdg, nch, 0., eta, pt, dummy); - auto sigmatgl = std::sqrt(lutEntry->covm[9]); // sigmatgl2 - auto etaRes = std::fabs(std::sin(2.0 * std::atan(std::exp(-eta)))) * sigmatgl; // propagate tgl to eta uncertainty - return etaRes; -} -/*****************************************************************/ -// efficiency -double TrackSmearer::getEfficiency(const int pdg, const float nch, const float eta, const float pt) -{ - float efficiency = 0.0f; - getLUTEntry(pdg, nch, 0., eta, pt, efficiency); - return efficiency; -} -/*****************************************************************/ -// Only in DelphesO2 -// bool TrackSmearer::smearTrack(Track& track, bool atDCA) -// { - -// O2Track o2track; -// TrackUtils::convertTrackToO2Track(track, o2track, atDCA); -// int pdg = track.PID; -// float nch = mdNdEta; // use locally stored dNch/deta for the time being -// if (!smearTrack(o2track, pdg, nch)) -// return false; -// TrackUtils::convertO2TrackToTrack(o2track, track, atDCA); -// return true; - -// #if 0 -// lutEntry_t* lutEntry = getLUTEntry(track.PID, 0., 0., track.Eta, track.PT); -// if (!lutEntry) -// return; - -// O2Track o2track; -// TrackUtils::convertTrackToO2Track(track, o2track, atDCA); -// smearTrack(o2track, lutEntry); -// TrackUtils::convertO2TrackToTrack(o2track, track, atDCA); -// #endif -// } - -/*****************************************************************/ - -} // namespace delphes -} // namespace o2 diff --git a/ALICE3/Core/DelphesO2TrackSmearer.h b/ALICE3/Core/DelphesO2TrackSmearer.h deleted file mode 100644 index 6d941f336ca..00000000000 --- a/ALICE3/Core/DelphesO2TrackSmearer.h +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// @file DelphesO2TrackSmearer.h -/// @brief Porting to O2Physics of DelphesO2 code. -/// Minimal changes have been made to the original code for adaptation purposes, formatting and commented parts have been considered. -/// Relevant sources: -/// DelphesO2/src/lutCovm.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/lutCovm.hh -/// DelphesO2/src/TrackSmearer.cc https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.cc -/// DelphesO2/src/TrackSmearer.hh https://github.com/AliceO2Group/DelphesO2/blob/master/src/TrackSmearer.hh -/// @author: Roberto Preghenella -/// @email: preghenella@bo.infn.it -/// - -#ifndef ALICE3_CORE_DELPHESO2TRACKSMEARER_H_ -#define ALICE3_CORE_DELPHESO2TRACKSMEARER_H_ - -#include -#include - -#include -#include -#include - -/////////////////////////////// -/// DelphesO2/src/lutCovm.hh // -/////////////////////////////// - -#define LUTCOVM_VERSION 20210801 - -struct map_t { - int nbins = 1; - float min = 0.; - float max = 1.e6; - bool log = false; - float eval(int bin) - { - float width = (max - min) / nbins; - float val = min + (bin + 0.5) * width; - if (log) - return pow(10., val); - return val; - } - // function needed to interpolate some dimensions - float fracPositionWithinBin(float val) - { - float width = (max - min) / nbins; - int bin; - float returnVal = 0.5f; - if (log) { - bin = static_cast((log10(val) - min) / width); - returnVal = ((log10(val) - min) / width) - bin; - } else { - bin = static_cast((val - min) / width); - returnVal = val / width - bin; - } - return returnVal; - } - - int find(float val) - { - float width = (max - min) / nbins; - int bin; - if (log) - bin = static_cast((log10(val) - min) / width); // Changed due to MegaLinter error. - // bin = (int)((log10(val) - min) / width); // Original line. - else - bin = static_cast((val - min) / width); // Changed due to MegaLinter error. - // bin = (int)((val - min) / width); // Original line. - if (bin < 0) - return 0; - if (bin > nbins - 1) - return nbins - 1; - return bin; - } //; - void print() { printf("nbins = %d, min = %f, max = %f, log = %s \n", nbins, min, max, log ? "on" : "off"); } //; -}; - -struct lutHeader_t { - int version = LUTCOVM_VERSION; - int pdg = 0; - float mass = 0.; - float field = 0.; - map_t nchmap; - map_t radmap; - map_t etamap; - map_t ptmap; - bool check_version() - { - return (version == LUTCOVM_VERSION); - } //; - void print() - { - printf(" version: %d \n", version); - printf(" pdg: %d \n", pdg); - printf(" field: %f \n", field); - printf(" nchmap: "); - nchmap.print(); - printf(" radmap: "); - radmap.print(); - printf(" etamap: "); - etamap.print(); - printf(" ptmap: "); - ptmap.print(); - } //; -}; - -struct lutEntry_t { - float nch = 0.; - float eta = 0.; - float pt = 0.; - bool valid = false; - float eff = 0.; - float eff2 = 0.; - float itof = 0.; - float otof = 0.; - float covm[15] = {0.}; - float eigval[5] = {0.}; - float eigvec[5][5] = {{0.}}; - float eiginv[5][5] = {{0.}}; - void print() - { - printf(" --- lutEntry: pt = %f, eta = %f (%s)\n", pt, eta, valid ? "valid" : "not valid"); - printf(" efficiency: %f\n", eff); - printf(" covMatix: "); - int k = 0; - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < i + 1; ++j) - printf("% e ", covm[k++]); - printf("\n "); - } - printf("\n"); - } -}; - -//////////////////////////////////// -/// DelphesO2/src/TrackSmearer.hh // -//////////////////////////////////// - -using O2Track = o2::track::TrackParCov; - -namespace o2 -{ -namespace delphes -{ - -class TrackSmearer -{ - - public: - TrackSmearer() = default; - ~TrackSmearer() = default; - - /** LUT methods **/ - bool loadTable(int pdg, const char* filename, bool forceReload = false); - bool hasTable(int pdg) { return (mLUTHeader[getIndexPDG(pdg)] != nullptr); } //; - void useEfficiency(bool val) { mUseEfficiency = val; } //; - void interpolateEfficiency(bool val) { mInterpolateEfficiency = val; } //; - void skipUnreconstructed(bool val) { mSkipUnreconstructed = val; } //; - void setWhatEfficiency(int val) { mWhatEfficiency = val; } //; - lutHeader_t* getLUTHeader(int pdg) { return mLUTHeader[getIndexPDG(pdg)]; } //; - lutEntry_t* getLUTEntry(const int pdg, const float nch, const float radius, const float eta, const float pt, float& interpolatedEff); - - bool smearTrack(O2Track& o2track, lutEntry_t* lutEntry, float interpolatedEff); - bool smearTrack(O2Track& o2track, int pdg, float nch); - // bool smearTrack(Track& track, bool atDCA = true); // Only in DelphesO2 - double getPtRes(const int pdg, const float nch, const float eta, const float pt); - double getEtaRes(const int pdg, const float nch, const float eta, const float pt); - double getAbsPtRes(const int pdg, const float nch, const float eta, const float pt); - double getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt); - double getEfficiency(const int pdg, const float nch, const float eta, const float pt); - - int getIndexPDG(const int pdg) - { - switch (abs(pdg)) { - case 11: - return 0; // Electron - case 13: - return 1; // Muon - case 211: - return 2; // Pion - case 321: - return 3; // Kaon - case 2212: - return 4; // Proton - case 1000010020: - return 5; // Deuteron - case 1000010030: - return 6; // Triton - case 1000020030: - return 7; // Helium3 - case 1000020040: - return 8; // Alphas - default: - return 2; // Default: pion - } - } - - const char* getParticleName(int pdg) - { - switch (abs(pdg)) { - case 11: - return "electron"; - case 13: - return "muon"; - case 211: - return "pion"; - case 321: - return "kaon"; - case 2212: - return "proton"; - case 1000010020: - return "deuteron"; - case 1000010030: - return "triton"; - case 1000020030: - return "helium3"; - case 1000020040: - return "alpha"; - default: - return "pion"; // Default: pion - } - } - void setdNdEta(float val) { mdNdEta = val; } //; - void setCcdbManager(o2::ccdb::BasicCCDBManager* mgr) { mCcdbManager = mgr; } //; - - protected: - static constexpr unsigned int nLUTs = 9; // Number of LUT available - lutHeader_t* mLUTHeader[nLUTs] = {nullptr}; - lutEntry_t***** mLUTEntry[nLUTs] = {nullptr}; - bool mUseEfficiency = true; - bool mInterpolateEfficiency = false; - bool mSkipUnreconstructed = true; // don't smear tracks that are not reco'ed - int mWhatEfficiency = 1; - float mdNdEta = 1600.; - - private: - o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; -}; - -} // namespace delphes -} // namespace o2 - -namespace o2::delphes -{ -using DelphesO2TrackSmearer = TrackSmearer; -} - -#endif // ALICE3_CORE_DELPHESO2TRACKSMEARER_H_ diff --git a/ALICE3/Core/DetLayer.h b/ALICE3/Core/DetLayer.h index b3ed3ed6b34..eb5b1cf0550 100644 --- a/ALICE3/Core/DetLayer.h +++ b/ALICE3/Core/DetLayer.h @@ -108,25 +108,25 @@ struct DetLayer { TString mName; // position variables - float mR; // radius in centimeters - float mZ; // mZ dimension in centimeters + float mR{}; // radius in centimeters + float mZ{}; // mZ dimension in centimeters // material variables - float mX0; // radiation length - float mXrho; // density + float mX0{}; // radiation length + float mXrho{}; // density // resolution variables for active layers - float mResRPhi; // RPhi resolution in centimeters - float mResZ; // Z resolution in centimeters + float mResRPhi{}; // RPhi resolution in centimeters + float mResZ{}; // Z resolution in centimeters // efficiency - float mEff; // detection efficiency + float mEff{1.f}; // detection efficiency // dead regions in phi (in radians) TGraph* mDeadPhiRegions = nullptr; // layer type - int mType; // 0: undefined/inert, 1: silicon, 2: gas/tpc + int mType{0}; // 0: undefined/inert, 1: silicon, 2: gas/tpc }; } // namespace o2::fastsim diff --git a/ALICE3/Core/FastTracker.cxx b/ALICE3/Core/FastTracker.cxx index 254ab8d6643..5bf0bcd87e3 100644 --- a/ALICE3/Core/FastTracker.cxx +++ b/ALICE3/Core/FastTracker.cxx @@ -9,6 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file FastTracker.cxx +/// \brief On the fly implementation of DelphesO2 solveTrack +/// \author David Dobrigkeit Chinellato +/// \author Nicolò Jacazio +/// \author Jesper Karlsson Gumprecht + #include "FastTracker.h" #include "DetLayer.h" @@ -34,7 +40,6 @@ #include #include -#include #include #include @@ -44,14 +49,12 @@ #include #include -namespace o2 -{ -namespace fastsim +namespace o2::fastsim { // +-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+ -DetLayer* FastTracker::AddLayer(TString name, float r, float z, float x0, float xrho, float resRPhi, float resZ, float eff, int type) +DetLayer* FastTracker::addLayer(const TString& name, float r, float z, float x0, float xrho, float resRPhi, float resZ, float eff, int type) { LOG(debug) << "Adding layer " << name << " r=" << r << " z=" << z << " x0=" << x0 << " xrho=" << xrho << " resRPhi=" << resRPhi << " resZ=" << resZ << " eff=" << eff << " type=" << type; DetLayer newLayer(name, r, z, x0, xrho, resRPhi, resZ, eff, type); @@ -77,7 +80,7 @@ DetLayer* FastTracker::AddLayer(TString name, float r, float z, float x0, float void FastTracker::addDeadPhiRegionInLayer(const std::string& layerName, float phiStart, float phiEnd) { - const int layerIdx = GetLayerIndex(layerName); + const int layerIdx = getLayerIndex(layerName); if (layerIdx < 0) { LOG(fatal) << "Cannot add dead phi region to non-existing layer " << layerName; return; @@ -85,7 +88,7 @@ void FastTracker::addDeadPhiRegionInLayer(const std::string& layerName, float ph layers[layerIdx].addDeadPhiRegion(phiStart, phiEnd); } -int FastTracker::GetLayerIndex(const std::string& name) const +int FastTracker::getLayerIndex(const std::string& name) const { int i = 0; for (const auto& layer : layers) { @@ -98,7 +101,7 @@ int FastTracker::GetLayerIndex(const std::string& name) const return -1; } -void FastTracker::Print() +void FastTracker::print() { // print out layer setup LOG(info) << "+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+"; @@ -109,7 +112,7 @@ void FastTracker::Print() LOG(info) << "+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+"; } -void FastTracker::AddTPC(float phiResMean, float zResMean) +void FastTracker::addTPC(float phiResMean, float zResMean) { LOG(info) << " Adding standard time projection chamber"; @@ -118,12 +121,11 @@ void FastTracker::AddTPC(float phiResMean, float zResMean) // https://github.com/AliceO2Group/DelphesO2/blob/master/src/DetectorK/DetectorK.cxx#L522 // % Radiation Lengths ... Average per TPC row (i.e. total/159 ) const int kNPassiveBound = 2; - const float radLBoundary[kNPassiveBound] = {1.692612e-01, 8.711904e-02}; - const float xrhoBoundary[kNPassiveBound] = {6.795774e+00, 3.111401e+00}; - const float rBoundary[kNPassiveBound] = {50, 70.0}; // cm + const std::array radLBoundary = {1.692612e-01, 8.711904e-02}; + const std::array xrhoBoundary = {6.795774e+00, 3.111401e+00}; + const std::array rBoundary = {50, 70.0}; // cm float radLPerRow = 0.000036; - float tpcInnerRadialPitch = 0.75; // cm float tpcMiddleRadialPitch = 1.0; // cm float tpcOuterRadialPitch = 1.5; // cm @@ -139,22 +141,23 @@ void FastTracker::AddTPC(float phiResMean, float zResMean) // add boundaries between ITS and TPC for (int i = 0; i < kNPassiveBound; i++) { - AddLayer(Form("tpc_boundary%d", i), rBoundary[i], zLength, radLBoundary[i], xrhoBoundary[i], 0); // dummy errors + addLayer(Form("tpc_boundary%d", i), rBoundary[i], zLength, radLBoundary[i], xrhoBoundary[i], 0); // dummy errors } - for (Int_t k = 0; k < tpcRows; k++) { - Float_t rowRadius = 0; - if (k < innerRows) + for (int k = 0; k < tpcRows; k++) { + float rowRadius = 0; + if (k < innerRows) { rowRadius = rowOneRadius + k * tpcInnerRadialPitch; - else if (k >= innerRows && k < (innerRows + middleRows)) + } else if (k >= innerRows && k < (innerRows + middleRows)) { rowRadius = row64Radius + (k - innerRows + 1) * tpcMiddleRadialPitch; - else if (k >= (innerRows + middleRows) && k < tpcRows) + } else if (k >= (innerRows + middleRows) && k < tpcRows) { rowRadius = row128Radius + (k - innerRows - middleRows + 1) * tpcOuterRadialPitch; + } - AddLayer(Form("tpc_%d", k), rowRadius, zLength, radLPerRow, 0, phiResMean, zResMean, 1.0f, 2); + addLayer(Form("tpc_%d", k), rowRadius, zLength, radLPerRow, 0, phiResMean, zResMean, 1.0f, 2); } } -void FastTracker::AddGenericDetector(o2::fastsim::GeometryEntry configMap, o2::ccdb::BasicCCDBManager* ccdbManager) +void FastTracker::addGenericDetector(const o2::fastsim::GeometryEntry& configMap, o2::ccdb::BasicCCDBManager* ccdbManager) { // Layers for (const auto& layer : configMap.getLayerNames()) { @@ -175,19 +178,17 @@ void FastTracker::AddGenericDetector(o2::fastsim::GeometryEntry configMap, o2::c const int type = configMap.getIntValue(layer, "type"); const std::string deadPhiRegions = configMap.getValue(layer, "deadPhiRegions", false); - // void AddLayer(TString name, float r, float z, float x0, float xrho, float resRPhi = 0.0f, float resZ = 0.0f, float eff = 0.0f, int type = 0); LOG(info) << " Adding layer " << layer << " r=" << r << " z=" << z << " x0=" << x0 << " xrho=" << xrho << " resRPhi=" << resRPhi << " resZ=" << resZ << " eff=" << eff << " type=" << type << " deadPhiRegions=" << deadPhiRegions; - - DetLayer* addedLayer = AddLayer(layer.c_str(), r, z, x0, xrho, resRPhi, resZ, eff, type); + DetLayer* addedLayer = addLayer(layer.c_str(), r, z, x0, xrho, resRPhi, resZ, eff, type); if (!deadPhiRegions.empty()) { // Taking it as ccdb path or local file // Check if it begins with ccdb: - if (std::string(deadPhiRegions).rfind("ccdb:", 0) == 0) { - std::string ccdbPath = std::string(deadPhiRegions).substr(5); // remove "ccdb:" prefix + if (deadPhiRegions.starts_with("ccdb:")) { + std::string ccdbPath = deadPhiRegions.substr(5); // remove "ccdb:" prefix if (ccdbManager == nullptr) { LOG(fatal) << "CCDB manager is null, cannot retrieve file " << ccdbPath; return; } - TGraph* g = ccdbManager->getForTimeStamp(ccdbPath, -1); + auto g = ccdbManager->getForTimeStamp(ccdbPath, 1); addedLayer->setDeadPhiRegions(g); } else { // Taking it as local file @@ -196,7 +197,7 @@ void FastTracker::AddGenericDetector(o2::fastsim::GeometryEntry configMap, o2::c LOG(fatal) << "Cannot open dead phi regions file " << deadPhiRegions; return; } - TGraph* g = reinterpret_cast(infile.Get(infile.GetListOfKeys()->At(0)->GetName())); + auto g = dynamic_cast(infile.Get(infile.GetListOfKeys()->At(0)->GetName())); infile.Close(); addedLayer->setDeadPhiRegions(g); } @@ -206,32 +207,35 @@ void FastTracker::AddGenericDetector(o2::fastsim::GeometryEntry configMap, o2::c } } -float FastTracker::Dist(float z, float r) +float FastTracker::dist(float z, float r) { // porting of DetektorK::Dist // see here: // https://github.com/AliceO2Group/DelphesO2/blob/master/src/DetectorK/DetectorK.cxx#L743 int index = 1; int nSteps = 301; + const int nSigma = 4; float dist = 0.0; - float dz0 = (4 * sigmaD - (-4) * sigmaD / (nSteps = 1)); + float dz0 = (nSigma * sigmaD - (-nSigma) * sigmaD / (nSteps - 1)); float z0 = 0.0; for (int i = 0; i < nSteps; i++) { - if (i == nSteps - 1) + if (i == nSteps - 1) { index = 1; + } z0 = -4 * sigmaD + i * dz0; dist += index * (dz0 / 3.) * (1 / o2::math_utils::sqrt(o2::constants::math::TwoPI) / sigmaD) * std::exp(-z0 * z0 / 2. / sigmaD / sigmaD) * (1 / o2::math_utils::sqrt((z - z0) * (z - z0) + r * r)); - if (index != 4) - index = 4; - else + if (index != nSigma) { + index = nSigma; + } else { index = 2; + } } return dist; } -float FastTracker::OneEventHitDensity(float multiplicity, float radius) +float FastTracker::oneEventHitDensity(float multiplicity, float radius) { - // porting of DetektorK::OneEventHitDensity + // porting of DetektorK::oneEventHitDensity // see here: // https://github.com/AliceO2Group/DelphesO2/blob/master/src/DetectorK/DetectorK.cxx#L694 float den = multiplicity / (o2::constants::math::TwoPI * radius * radius); @@ -240,75 +244,77 @@ float FastTracker::OneEventHitDensity(float multiplicity, float radius) return den; } -float FastTracker::IntegratedHitDensity(float multiplicity, float radius) +float FastTracker::integratedHitDensity(float multiplicity, float radius) { // porting of DetektorK::IntegratedHitDensity // see here: // https://github.com/AliceO2Group/DelphesO2/blob/master/src/DetectorK/DetectorK.cxx#L712 float zdcHz = luminosity * 1.e24 * mCrossSectionMinB; - float den = zdcHz * integrationTime / 1000. * multiplicity * Dist(0., radius) / (o2::constants::math::TwoPI * radius); - if (den < OneEventHitDensity(multiplicity, radius)) - den = OneEventHitDensity(multiplicity, radius); + float den = zdcHz * integrationTime / 1000. * multiplicity * dist(0., radius) / (o2::constants::math::TwoPI * radius); + if (den < oneEventHitDensity(multiplicity, radius)) { + den = oneEventHitDensity(multiplicity, radius); + } return den; } -float FastTracker::UpcHitDensity(float radius) +float FastTracker::upcHitDensity(float radius) { // porting of DetektorK::UpcHitDensity // see here: // https://github.com/AliceO2Group/DelphesO2/blob/master/src/DetectorK/DetectorK.cxx#L727 - float mUPCelectrons = 0; - mUPCelectrons = lhcUPCScale * 5456 / (radius * radius) / dNdEtaMinB; - if (mUPCelectrons < 0) + float mUPCelectrons = lhcUPCScale * 5456 / (radius * radius) / dNdEtaMinB; + if (mUPCelectrons < 0) { mUPCelectrons = 0.0; - mUPCelectrons *= IntegratedHitDensity(dNdEtaMinB, radius); + } + mUPCelectrons *= integratedHitDensity(dNdEtaMinB, radius); mUPCelectrons *= upcBackgroundMultiplier; return mUPCelectrons; } -float FastTracker::HitDensity(float radius) +float FastTracker::hitDensity(float radius) { // porting of DetektorK::HitDensity // see here: // https://github.com/AliceO2Group/DelphesO2/blob/master/src/DetectorK/DetectorK.cxx#L663 float arealDensity = 0.; if (radius > maxRadiusSlowDet) { - arealDensity = OneEventHitDensity(dNdEtaCent, radius); - arealDensity += otherBackground * OneEventHitDensity(dNdEtaMinB, radius); + arealDensity = oneEventHitDensity(dNdEtaCent, radius); + arealDensity += otherBackground * oneEventHitDensity(dNdEtaMinB, radius); } // In the version of Delphes used to produce // Look-up tables, UpcHitDensity(radius) always returns 0, // hence it is left commented out for now if (radius < maxRadiusSlowDet) { - arealDensity = OneEventHitDensity(dNdEtaCent, radius); - arealDensity += otherBackground * OneEventHitDensity(dNdEtaMinB, radius) + IntegratedHitDensity(dNdEtaMinB, radius); + arealDensity = oneEventHitDensity(dNdEtaCent, radius); + arealDensity += otherBackground * oneEventHitDensity(dNdEtaMinB, radius) + integratedHitDensity(dNdEtaMinB, radius); // +UpcHitDensity(radius); } return arealDensity; } -float FastTracker::ProbGoodChiSqHit(float radius, float searchRadiusRPhi, float searchRadiusZ) +float FastTracker::probGoodChiSqHit(float radius, float searchRadiusRPhi, float searchRadiusZ) { // porting of DetektorK::ProbGoodChiSqHit // see here: // https://github.com/AliceO2Group/DelphesO2/blob/master/src/DetectorK/DetectorK.cxx#L629 - float sx, goodHit; - sx = o2::constants::math::TwoPI * searchRadiusRPhi * searchRadiusZ * HitDensity(radius); - goodHit = 1. / (1 + sx); + const float sx = o2::constants::math::TwoPI * searchRadiusRPhi * searchRadiusZ * hitDensity(radius); + const float goodHit = 1. / (1 + sx); return goodHit; } // function to provide a reconstructed track from a perfect input track // returns number of intercepts (generic for now) -int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackParCov& outputTrack, const float nch, const float maxRadius) +int FastTracker::fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackParCov& outputTrack, const float nch, const float maxRadius) { dNdEtaCent = nch; // set the number of charged particles per unit rapidity hits.clear(); nIntercepts = 0; nSiliconPoints = 0; nGasPoints = 0; - std::array posIni; // provision for != PV + trackIU = -1.f; + trackOU = -1.f; + std::array posIni{}; // provision for != PV inputTrack.getXYZGlo(posIni); const float initialRadius = std::hypot(posIni[0], posIni[1]); const float kTrackingMargin = 0.1; @@ -326,6 +332,7 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } const int xrhosteps = 100; const bool applyAngularCorrection = true; + static constexpr float InterceptFailed = 999.f; // Delphes sets this to 20 instead of the number of layers, // but does not count all points in the tpc as layers which we do here @@ -359,7 +366,7 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa // check if layer is reached float targetX = 1e+3; inputTrack.getXatLabR(layers[il].getRadius(), targetX, magneticField); - if (targetX > 999.f) { + if (targetX > InterceptFailed) { LOGF(debug, "Failed to find intercept for layer %d at radius %.2f cm", il, layers[il].getRadius()); break; // failed to find intercept } @@ -369,24 +376,24 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa ok = inputTrack.correctForMaterial(layers[il].getRadiationLength(), 0, applyAngularCorrection); } if (ok && mApplyElossCorrection && layers[il].getDensity() > 0) { // correct in small steps - for (int ise = xrhosteps; ise--;) { + for (int ise = xrhosteps; ise > 0; --ise) { ok = inputTrack.correctForMaterial(0, -layers[il].getDensity() / xrhosteps, applyAngularCorrection); - if (!ok) + if (!ok) { break; + } } } LOGF(debug, "Propagation was %s up to layer %d", ok ? "successful" : "unsuccessful", il); // was there a problem on this layer? if (!ok && il > 0) { // may fail to reach target layer due to the eloss - float rad2 = inputTrack.getX() * inputTrack.getX() + inputTrack.getY() * inputTrack.getY(); - float maxR = layers[il - 1].getRadius() + kTrackingMargin * 2; - float minRad = (fMinRadTrack > 0 && fMinRadTrack < maxR) ? fMinRadTrack : maxR; + const float rad2 = inputTrack.getX() * inputTrack.getX() + inputTrack.getY() * inputTrack.getY(); + const float maxR = layers[il - 1].getRadius() + kTrackingMargin * 2; + const float minRad = (fMinRadTrack > 0 && fMinRadTrack < maxR) ? fMinRadTrack : maxR; if (rad2 - minRad * minRad < kTrackingMargin * kTrackingMargin) { // check previously reached layer return -5; // did not reach min requested layer - } else { - break; } + break; } if (std::abs(inputTrack.getZ()) > layers[il].getZ() && mApplyZacceptance) { @@ -411,6 +418,9 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa firstLayerReached = il; } lastLayerReached = il; + if (layers[il].isTOF()) { + continue; // TOF layers are not used for tracking, but are kept in range for the innermost/outermost update + } nIntercepts++; } @@ -423,16 +433,16 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa for (int ip = 0; ip < o2::track::kNParams; ip++) { trPars[ip] = outputTrack.getParam(ip); } - static constexpr float kLargeErr2Coord = 5 * 5; - static constexpr float kLargeErr2Dir = 0.7 * 0.7; - static constexpr float kLargeErr2PtI = 30.5 * 30.5; + static constexpr float LargeErr2Coord = 5 * 5; + static constexpr float LargeErr2Dir = 0.7 * 0.7; + static constexpr float LargeErr2PtI = 30.5 * 30.5; std::array largeCov = {0.}; - for (int ic = o2::track::kCovMatSize; ic--;) { + for (int ic = o2::track::kCovMatSize; ic > 0; --ic) { largeCov[ic] = 0.; } - largeCov[o2::track::CovLabels::kSigY2] = largeCov[o2::track::CovLabels::kSigZ2] = kLargeErr2Coord; - largeCov[o2::track::CovLabels::kSigSnp2] = largeCov[o2::track::CovLabels::kSigTgl2] = kLargeErr2Dir; - largeCov[o2::track::CovLabels::kSigQ2Pt2] = kLargeErr2PtI * trPars[o2::track::ParLabels::kQ2Pt] * trPars[o2::track::ParLabels::kQ2Pt]; + largeCov[o2::track::CovLabels::kSigY2] = largeCov[o2::track::CovLabels::kSigZ2] = LargeErr2Coord; + largeCov[o2::track::CovLabels::kSigSnp2] = largeCov[o2::track::CovLabels::kSigTgl2] = LargeErr2Dir; + largeCov[o2::track::CovLabels::kSigQ2Pt2] = LargeErr2PtI * trPars[o2::track::ParLabels::kQ2Pt] * trPars[o2::track::ParLabels::kQ2Pt]; inwardTrack.setCov(largeCov); inwardTrack.checkCovariance(); @@ -443,8 +453,9 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa float targetX = 1e+3; inputTrack.getXatLabR(layers[il].getRadius(), targetX, magneticField); - if (targetX > 999) + if (targetX > InterceptFailed) { continue; // failed to find intercept + } if (!inputTrack.propagateTo(targetX, magneticField)) { continue; // failed to propagate @@ -455,25 +466,22 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } // get perfect data point position - std::array spacePoint; + std::array spacePoint{}; inputTrack.getXYZGlo(spacePoint); std::vector thisHit = {spacePoint[0], spacePoint[1], spacePoint[2]}; // towards adding cluster: move to track alpha float alpha = inwardTrack.getAlpha(); - float xyz1[3]{ - std::cos(alpha) * spacePoint[0] + std::sin(alpha) * spacePoint[1], - -std::sin(alpha) * spacePoint[0] + std::cos(alpha) * spacePoint[1], - spacePoint[2]}; + std::array xyz1 = {std::cos(alpha) * spacePoint[0] + std::sin(alpha) * spacePoint[1], + -std::sin(alpha) * spacePoint[0] + std::cos(alpha) * spacePoint[1], + spacePoint[2]}; if (!inwardTrack.propagateTo(xyz1[0], magneticField)) { continue; } - if (!layers[il].isInert()) { // only update covm for tracker hits - const o2::track::TrackParametrization::dim2_t hitpoint = { - static_cast(xyz1[1]), - static_cast(xyz1[2])}; + if (!layers[il].isInert() && !layers[il].isTOF()) { // only update covm for tracker hits + const o2::track::TrackParametrization::dim2_t hitpoint = {xyz1[1], xyz1[2]}; const o2::track::TrackParametrization::dim3_t hitpointcov = {layers[il].getResolutionRPhi() * layers[il].getResolutionRPhi(), 0.f, layers[il].getResolutionZ() * layers[il].getResolutionZ()}; inwardTrack.update(hitpoint, hitpointcov); @@ -489,7 +497,7 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } } if (mApplyElossCorrection && layers[il].getDensity() > 0) { - for (int ise = xrhosteps; ise--;) { // correct in small steps + for (int ise = xrhosteps; ise > 0; --ise) { // correct in small steps if (!inputTrack.correctForMaterial(0, layers[il].getDensity() / xrhosteps, applyAngularCorrection)) { return -7; } @@ -507,10 +515,17 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } hits.push_back(thisHit); - if (!layers[il].isInert()) { // good hit probability calculation + if (!layers[il].isInert()) { + trackIU = std::hypot(spacePoint[0], spacePoint[1]); + if (trackOU < 0.f) { + trackOU = trackIU; + } + } + + if (!layers[il].isInert() && !layers[il].isTOF()) { // good hit probability calculation float sigYCmb = o2::math_utils::sqrt(inwardTrack.getSigmaY2() + layers[il].getResolutionRPhi() * layers[il].getResolutionRPhi()); float sigZCmb = o2::math_utils::sqrt(inwardTrack.getSigmaZ2() + layers[il].getResolutionZ() * layers[il].getResolutionZ()); - goodHitProbability[il] = ProbGoodChiSqHit(layers[il].getRadius() * 100, sigYCmb * 100, sigZCmb * 100); + goodHitProbability[il] = probGoodChiSqHit(layers[il].getRadius() * 100, sigYCmb * 100, sigZCmb * 100); goodHitProbability[0] *= goodHitProbability[il]; } } @@ -518,7 +533,7 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa // backpropagate to original radius float finalX = 1e+3; bool inPropStatus = inwardTrack.getXatLabR(initialRadius, finalX, magneticField); - if (finalX > 999) { + if (finalX > InterceptFailed) { LOG(debug) << "Failed to find intercept for initial radius " << initialRadius << " cm, x = " << finalX << " and status " << inPropStatus << " and sn = " << inwardTrack.getSnp() << " r = " << inwardTrack.getY() * inwardTrack.getY(); return -3; // failed to find intercept } @@ -528,7 +543,8 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } // only attempt to continue if intercepts are at least four - if (nIntercepts < 4) { + static constexpr int MinIntercepts = 4; + if (nIntercepts < MinIntercepts) { return nIntercepts; } @@ -556,41 +572,44 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa for (int ii = 0; ii < o2::track::kCovMatSize; ii++) { covMat[ii] = outputTrack.getCov()[ii]; } - TMatrixDSym m(5); - double fcovm[5][5]; // double precision is needed for regularisation - for (int ii = 0, k = 0; ii < 5; ++ii) { - for (int j = 0; j < ii + 1; ++j, ++k) { - fcovm[ii][j] = covMat[k]; - fcovm[j][ii] = covMat[k]; + std::array, o2::track::kNParams> fcovm{}; // double precision is needed for regularisation + TMatrixDSym m(o2::track::kNParams); + for (int ii = 0, k = 0; ii < o2::track::kNParams; ++ii) { + for (int jj = 0; jj < ii + 1; ++jj, ++k) { + fcovm[ii][jj] = covMat[k]; + fcovm[jj][ii] = covMat[k]; } } // evaluate ruben's conditional, regularise - const bool makePositiveDefinite = (covMatFactor > -1e-5); // apply fix + static constexpr float CovMatFactorThreshold = -1e-5; + const bool makePositiveDefinite = (covMatFactor > CovMatFactorThreshold); // apply fix bool rubenConditional = false; - for (int ii = 0; ii < 5; ii++) { - for (int jj = 0; jj < 5; jj++) { - if (ii == jj) + for (int ii = 0; ii < o2::track::kNParams; ii++) { + for (int jj = 0; jj < o2::track::kNParams; jj++) { + if (ii == jj) { continue; // don't evaluate diagonals + } if (fcovm[ii][jj] * fcovm[ii][jj] > std::abs(fcovm[ii][ii] * fcovm[jj][jj])) { rubenConditional = true; if (makePositiveDefinite) { - fcovm[ii][jj] = TMath::Sign(1, fcovm[ii][jj]) * covMatFactor * sqrt(std::abs(fcovm[ii][ii] * fcovm[jj][jj])); + fcovm[ii][jj] = TMath::Sign(1, fcovm[ii][jj]) * covMatFactor * std::sqrt(std::abs(fcovm[ii][ii] * fcovm[jj][jj])); } } } } // Should have a valid cov matrix now - m.SetMatrixArray(reinterpret_cast(fcovm)); + m.SetMatrixArray(fcovm[0].data()); TMatrixDSymEigen eigen(m); TMatrixD eigVec = eigen.GetEigenVectors(); - TVectorD eigVal = eigen.GetEigenValues(); + const TVectorD& eigVal = eigen.GetEigenValues(); bool negEigVal = false; - for (int ii = 0; ii < 5; ii++) { - if (eigVal[ii] < 0.0f) + for (int ii = 0; ii < o2::track::kNParams; ii++) { + if (eigVal[ii] < 0.0f) { negEigVal = true; + } } if (negEigVal && rubenConditional && makePositiveDefinite) { @@ -608,26 +627,28 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa covMatOK++; // transform parameter vector and smear - float params_[5]; - for (int ii = 0; ii < 5; ++ii) { + std::array transformedParams{}; + for (int ii = 0; ii < o2::track::kNParams; ++ii) { float val = 0.; - for (int j = 0; j < 5; ++j) - val += eigVec[j][ii] * outputTrack.getParam(j); + for (int jj = 0; jj < o2::track::kNParams; ++jj) { + val += eigVec[jj][ii] * outputTrack.getParam(jj); + } // smear parameters according to eigenvalues - params_[ii] = gRandom->Gaus(val, sqrt(eigVal[ii])); + transformedParams[ii] = gRandom->Gaus(val, std::sqrt(eigVal[ii])); } // invert eigenvector matrix eigVec.Invert(); // transform back params vector - for (int ii = 0; ii < 5; ++ii) { + for (int ii = 0; ii < o2::track::kNParams; ++ii) { float val = 0.; - for (int j = 0; j < 5; ++j) - val += eigVec[j][ii] * params_[j]; + for (int jj = 0; jj < o2::track::kNParams; ++jj) { + val += eigVec[jj][ii] * transformedParams[jj]; + } outputTrack.setParam(val, ii); } // should make a sanity check that par[2] sin(phi) is in [-1, 1] - if (fabs(outputTrack.getParam(2)) > 1.) { + if (std::abs(outputTrack.getParam(2)) > 1.) { LOG(info) << " --- smearTrack failed sin(phi) sanity check: " << outputTrack.getParam(2); return -2; } @@ -636,7 +657,6 @@ int FastTracker::FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackPa } // +-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+ -} /* namespace fastsim */ -} /* namespace o2 */ +} // namespace o2::fastsim ClassImp(o2::fastsim::FastTracker); diff --git a/ALICE3/Core/FastTracker.h b/ALICE3/Core/FastTracker.h index d593c568fb9..1d272595bbb 100644 --- a/ALICE3/Core/FastTracker.h +++ b/ALICE3/Core/FastTracker.h @@ -9,6 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file FastTracker.h +/// \brief On the fly implementation of DelphesO2 solveTrack +/// \author David Dobrigkeit Chinellato +/// \author Nicolò Jacazio +/// \author Jesper Karlsson Gumprecht + #ifndef ALICE3_CORE_FASTTRACKER_H_ #define ALICE3_CORE_FASTTRACKER_H_ @@ -27,9 +33,7 @@ #include #include -namespace o2 -{ -namespace fastsim +namespace o2::fastsim { // +-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+ @@ -46,30 +50,30 @@ class FastTracker virtual ~FastTracker() {} // Layer and layer configuration - DetLayer* AddLayer(TString name, float r, float z, float x0, float xrho, float resRPhi = 0.0f, float resZ = 0.0f, float eff = 0.0f, int type = 0); + DetLayer* addLayer(const TString& name, float r, float z, float x0, float xrho, float resRPhi = 0.0f, float resZ = 0.0f, float eff = 0.0f, int type = 0); /// Add a dead region in phi for a specific layer /// \param layerName Name of the layer to modify /// \param phiStart Start angle of the dead region (in radians) /// \param phiEnd End angle of the dead region (in radians) void addDeadPhiRegionInLayer(const std::string& layerName, float phiStart, float phiEnd); - DetLayer GetLayer(const int layer) const { return layers[layer]; } - std::vector GetLayers() const { return layers; } - int GetLayerIndex(const std::string& name) const; - size_t GetNLayers() const { return layers.size(); } - bool IsLayerInert(const int layer) const { return layers[layer].isInert(); } - void ClearLayers() { layers.clear(); } - void SetRadiationLength(const std::string layerName, float x0) { layers[GetLayerIndex(layerName)].setRadiationLength(x0); } - void SetRadius(const std::string layerName, float r) { layers[GetLayerIndex(layerName)].setRadius(r); } - void SetResolutionRPhi(const std::string layerName, float resRPhi) { layers[GetLayerIndex(layerName)].setResolutionRPhi(resRPhi); } - void SetResolutionZ(const std::string layerName, float resZ) { layers[GetLayerIndex(layerName)].setResolutionZ(resZ); } - void SetResolution(const std::string layerName, float resRPhi, float resZ) + [[nodiscard]] DetLayer getLayer(const int layer) const { return layers[layer]; } + [[nodiscard]] const std::vector& getLayers() const { return layers; } + [[nodiscard]] int getLayerIndex(const std::string& name) const; + [[nodiscard]] size_t getNLayers() const { return layers.size(); } + [[nodiscard]] bool isLayerInert(const int layer) const { return layers[layer].isInert(); } + void clearLayers() { layers.clear(); } + void setRadiationLength(const std::string& layerName, float x0) { layers[getLayerIndex(layerName)].setRadiationLength(x0); } + void setRadius(const std::string& layerName, float r) { layers[getLayerIndex(layerName)].setRadius(r); } + void setResolutionRPhi(const std::string& layerName, float resRPhi) { layers[getLayerIndex(layerName)].setResolutionRPhi(resRPhi); } + void setResolutionZ(const std::string& layerName, float resZ) { layers[getLayerIndex(layerName)].setResolutionZ(resZ); } + void setResolution(const std::string& layerName, float resRPhi, float resZ) { - SetResolutionRPhi(layerName, resRPhi); - SetResolutionZ(layerName, resZ); + setResolutionRPhi(layerName, resRPhi); + setResolutionZ(layerName, resZ); } - void AddTPC(float phiResMean, float zResMean); + void addTPC(float phiResMean, float zResMean); /** * @brief Adds a generic detector configuration from the specified file. @@ -80,9 +84,9 @@ class FastTracker * * @param configMap Configuration map describing the detector. */ - void AddGenericDetector(o2::fastsim::GeometryEntry configMap, o2::ccdb::BasicCCDBManager* ccdbManager = nullptr); + void addGenericDetector(const o2::fastsim::GeometryEntry& configMap, o2::ccdb::BasicCCDBManager* ccdbManager = nullptr); - void Print(); + void print(); /** * @brief Performs fast tracking on the input track parameters. @@ -95,44 +99,46 @@ class FastTracker * @param nch Charged particle multiplicity (used for hit density calculations). * @return int i.e. number of intercepts (implementation-defined). */ - int FastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackParCov& outputTrack, const float nch, const float maxRadius = 100.f); + int fastTrack(o2::track::TrackParCov inputTrack, o2::track::TrackParCov& outputTrack, const float nch, const float maxRadius = 100.f); // For efficiency calculation - float Dist(float z, float radius); - float OneEventHitDensity(float multiplicity, float radius); - float IntegratedHitDensity(float multiplicity, float radius); - float UpcHitDensity(float radius); - float HitDensity(float radius); - float ProbGoodChiSqHit(float radius, float searchRadiusRPhi, float searchRadiusZ); + float dist(float z, float radius); + float oneEventHitDensity(float multiplicity, float radius); + float integratedHitDensity(float multiplicity, float radius); + float upcHitDensity(float radius); + float hitDensity(float radius); + float probGoodChiSqHit(float radius, float searchRadiusRPhi, float searchRadiusZ); // Setters and getters for configuration - void SetIntegrationTime(float t) { integrationTime = t; } - void SetMaxRadiusOfSlowDetectors(float r) { maxRadiusSlowDet = r; } - void SetAvgRapidity(float y) { avgRapidity = y; } - void SetdNdEtaCent(int d) { dNdEtaCent = d; } - void SetLhcUPCscale(float s) { lhcUPCScale = s; } - void SetBField(float b) { magneticField = b; } - void SetMinRadTrack(float r) { fMinRadTrack = r; } - void SetMagneticField(float b) { magneticField = b; } - void SetApplyZacceptance(bool b) { mApplyZacceptance = b; } - void SetApplyMSCorrection(bool b) { mApplyMSCorrection = b; } - void SetApplyElossCorrection(bool b) { mApplyElossCorrection = b; } - void SetApplyEffCorrection(bool b) { mApplyEffCorrection = b; } + void setIntegrationTime(float t) { integrationTime = t; } + void setMaxRadiusOfSlowDetectors(float r) { maxRadiusSlowDet = r; } + void setAvgRapidity(float y) { avgRapidity = y; } + void setdNdEtaCent(int d) { dNdEtaCent = d; } + void setLhcUPCscale(float s) { lhcUPCScale = s; } + void setBField(float b) { magneticField = b; } + void setMinRadTrack(float r) { fMinRadTrack = r; } + void setMagneticField(float b) { magneticField = b; } + void setApplyZacceptance(bool b) { mApplyZacceptance = b; } + void setApplyMSCorrection(bool b) { mApplyMSCorrection = b; } + void setApplyElossCorrection(bool b) { mApplyElossCorrection = b; } + void setApplyEffCorrection(bool b) { mApplyEffCorrection = b; } // Getters for the last track - int GetNIntercepts() const { return nIntercepts; } - int GetNSiliconPoints() const { return nSiliconPoints; } - int GetNGasPoints() const { return nGasPoints; } - float GetGoodHitProb(int layer) const + [[nodiscard]] int getNIntercepts() const { return nIntercepts; } + [[nodiscard]] int getNSiliconPoints() const { return nSiliconPoints; } + [[nodiscard]] int getNGasPoints() const { return nGasPoints; } + [[nodiscard]] float getTrackIU() const { return trackIU; } + [[nodiscard]] float getTrackOU() const { return trackOU; } + [[nodiscard]] float getGoodHitProb(int layer) const { return (layer >= 0 && static_cast(layer) < goodHitProbability.size()) ? goodHitProbability[layer] : 0.0f; } - std::size_t GetNHits() const { return hits.size(); } - float GetHitX(const int i) const { return hits[i][0]; } - float GetHitY(const int i) const { return hits[i][1]; } - float GetHitZ(const int i) const { return hits[i][2]; } - uint64_t GetCovMatOK() const { return covMatOK; } - uint64_t GetCovMatNotOK() const { return covMatNotOK; } + [[nodiscard]] std::size_t getNHits() const { return hits.size(); } + [[nodiscard]] float getHitX(const int i) const { return hits[i][0]; } + [[nodiscard]] float getHitY(const int i) const { return hits[i][1]; } + [[nodiscard]] float getHitZ(const int i) const { return hits[i][2]; } + uint64_t getCovMatOK() const { return covMatOK; } + uint64_t getCovMatNotOK() const { return covMatNotOK; } private: // Definition of detector layers @@ -146,8 +152,8 @@ class FastTracker bool mApplyEffCorrection = true; /// Apply correction for hit efficiency int mVerboseLevel = 0; /// 0: not verbose, >0 more verbose const float mCrossSectionMinB = 8; /// Minimum bias Cross section for event under study (PbPb MinBias ~ 8 Barns) - int dNdEtaCent = 2200; /// dN/deta e.g. at centrality 0-5% (for 5 TeV PbPb) - int dNdEtaMinB = 1; /// dN/deta for minimum bias events + float dNdEtaCent = 2200.f; /// dN/deta e.g. at centrality 0-5% (for 5 TeV PbPb) + float dNdEtaMinB = 1.f; /// dN/deta for minimum bias events float integrationTime = 0.02f; /// Integration time in ms float magneticField = 20.f; /// Magnetic field in kiloGauss (5 = 0.5T, 20 = 2T, etc) float covMatFactor = 0.99f; /// covmat off-diagonal factor to use for covmat fix (negative: no factor) @@ -168,6 +174,8 @@ class FastTracker int nIntercepts = 0; /// found in first outward propagation int nSiliconPoints = 0; /// silicon-based space points added to track int nGasPoints = 0; /// tpc-based space points added to track + float trackIU = -1.f; /// innermost update: radius of the innermost active-layer hit added to track + float trackOU = -1.f; /// outermost update: radius of the outermost active-layer hit added to track std::vector goodHitProbability; ClassDef(FastTracker, 1); @@ -175,7 +183,6 @@ class FastTracker // +-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+-~-<*>-~-+ -} // namespace fastsim -} // namespace o2 +} // namespace o2::fastsim #endif // ALICE3_CORE_FASTTRACKER_H_ diff --git a/ALICE3/Core/FastTrackerLinkDef.h b/ALICE3/Core/FastTrackerLinkDef.h index 9885d281a4d..03e3a3c3f86 100644 --- a/ALICE3/Core/FastTrackerLinkDef.h +++ b/ALICE3/Core/FastTrackerLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef ALICE3_CORE_FASTTRACKERLINKDEF_H_ -#define ALICE3_CORE_FASTTRACKERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -19,5 +18,3 @@ #pragma link C++ class o2::fastsim::GeometryContainer + ; #pragma link C++ class o2::fastsim::FastTracker + ; #pragma link C++ class o2::fastsim::FlatLutWriter + ; - -#endif // ALICE3_CORE_FASTTRACKERLINKDEF_H_ diff --git a/ALICE3/Core/FlatLutEntry.cxx b/ALICE3/Core/FlatLutEntry.cxx index 5110e9bff4c..66a6cedf860 100644 --- a/ALICE3/Core/FlatLutEntry.cxx +++ b/ALICE3/Core/FlatLutEntry.cxx @@ -9,6 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file FlatLutEntry.cxx +/// \brief Flat LUT implementation for compact helper tables used by the ALICE3 track smearing workflow. + #include "FlatLutEntry.h" #include @@ -22,28 +25,53 @@ #include #include -namespace o2::delphes +namespace o2::fastsim +{ + +void lutEntry_t::print() const { + LOGF(info, " nch = %f, eta = %f, pt = %f, valid = %s\n", nch, eta, pt, valid ? "true" : "false"); + LOGF(info, " eff = %f, eff2 = %f, itof = %f, otof = %f\n", eff, eff2, itof, otof); + LOGF(info, " covm: "); + for (int i = 0; i < kNumCovarianceTerms; ++i) { + LOGF(info, "%f ", covm[i]); + } + LOGF(info, "\n"); + LOGF(info, " eigval: "); + for (int i = 0; i < kNumEigenModes; ++i) { + LOGF(info, "%f ", eigval[i]); + } + LOGF(info, "\n"); + LOGF(info, " eigvec:\n"); + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { + LOGF(info, "%f ", eigvec[i][j]); + } + LOGF(info, "\n"); + } + LOGF(info, " eiginv:\n"); + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { + LOGF(info, "%f ", eiginv[i][j]); + } + LOGF(info, "\n"); + } +} float map_t::fracPositionWithinBin(float val) const { - float width = (max - min) / nbins; - int bin; - float returnVal = 0.5f; + const float width = (max - min) / nbins; + const int bin = find(val); if (log) { - bin = static_cast((std::log10(val) - min) / width); - returnVal = ((std::log10(val) - min) / width) - bin; - } else { - bin = static_cast((val - min) / width); - returnVal = val / width - bin; + return ((std::log10(val) - min) / width) - bin; } - return returnVal; + return val / width - bin; } int map_t::find(float val) const { - float width = (max - min) / nbins; - int bin; + const float width = (max - min) / nbins; + int bin = 0; if (log) { bin = static_cast((std::log10(val) - min) / width); } else { @@ -63,7 +91,7 @@ void map_t::print() const LOGF(info, "nbins = %d, min = %f, max = %f, log = %s \n", nbins, min, max, log ? "on" : "off"); } -bool lutHeader_t::check_version() const +bool lutHeader_t::checkVersion() const { return (version == LUTCOVM_VERSION); } @@ -90,10 +118,10 @@ void FlatLutData::initialize(const lutHeader_t& header) mEtaBins = header.etamap.nbins; mPtBins = header.ptmap.nbins; - size_t headerSize = sizeof(lutHeader_t); - size_t numEntries = static_cast(mNchBins) * mRadBins * mEtaBins * mPtBins; - size_t entriesSize = numEntries * sizeof(lutEntry_t); - size_t totalSize = headerSize + entriesSize; + constexpr size_t headerSize = sizeof(lutHeader_t); + const size_t numEntries = static_cast(mNchBins) * mRadBins * mEtaBins * mPtBins; + const size_t entriesSize = numEntries * sizeof(lutEntry_t); + const size_t totalSize = headerSize + entriesSize; mData.resize(totalSize); // Write header at the beginning @@ -103,35 +131,32 @@ void FlatLutData::initialize(const lutHeader_t& header) size_t FlatLutData::getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const { - size_t headerSize = sizeof(lutHeader_t); - - // Linear index: nch varies slowest, pt varies fastest - // idx = nch * (rad*eta*pt) + rad * (eta*pt) + eta * pt + pt - size_t linearIdx = static_cast(nch_bin) * (mRadBins * mEtaBins * mPtBins) + static_cast(rad_bin) * (mEtaBins * mPtBins) + static_cast(eta_bin) * mPtBins + static_cast(pt_bin); - - return headerSize + linearIdx * sizeof(lutEntry_t); + static constexpr size_t HeaderSize = sizeof(lutHeader_t); + const size_t linearIdx = getEntryIndex(nch_bin, rad_bin, eta_bin, pt_bin); + static constexpr size_t EntrySize = sizeof(lutEntry_t); + return HeaderSize + linearIdx * EntrySize; } const lutEntry_t* FlatLutData::getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const { size_t offset = getEntryOffset(nch_bin, rad_bin, eta_bin, pt_bin); - return reinterpret_cast(mDataRef.data() + offset); + return reinterpret_cast(mDataRef.data() + offset); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } lutEntry_t* FlatLutData::getEntry(int nch_bin, int rad_bin, int eta_bin, int pt_bin) { size_t offset = getEntryOffset(nch_bin, rad_bin, eta_bin, pt_bin); - return reinterpret_cast(mData.data() + offset); + return reinterpret_cast(mData.data() + offset); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } const lutHeader_t& FlatLutData::getHeaderRef() const { - return *reinterpret_cast(mDataRef.data()); + return *reinterpret_cast(mDataRef.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } lutHeader_t& FlatLutData::getHeader() { - return *reinterpret_cast(mData.data()); + return *reinterpret_cast(mData.data()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } void FlatLutData::updateRef() @@ -173,26 +198,26 @@ void FlatLutData::view(const uint8_t* buffer, size_t size) void FlatLutData::validateBuffer(const uint8_t* buffer, size_t size) { - auto header = PreviewHeader(buffer, size); - auto mNchBins = header.nchmap.nbins; - auto mRadBins = header.radmap.nbins; - auto mEtaBins = header.etamap.nbins; - auto mPtBins = header.ptmap.nbins; + auto header = previewHeader(buffer, size); + const auto nchBins = header.nchmap.nbins; + const auto radBins = header.radmap.nbins; + const auto etaBins = header.etamap.nbins; + const auto ptBins = header.ptmap.nbins; - size_t expectedSize = sizeof(lutHeader_t) + static_cast(mNchBins) * mRadBins * mEtaBins * mPtBins * sizeof(lutEntry_t); + const size_t expectedSize = sizeof(lutHeader_t) + static_cast(nchBins) * radBins * etaBins * ptBins * sizeof(lutEntry_t); if (size < expectedSize) { throw framework::runtime_error_f("Buffer size mismatch: expected %zu, got %zu", expectedSize, size); } } -lutHeader_t FlatLutData::PreviewHeader(const uint8_t* buffer, size_t size) +lutHeader_t FlatLutData::previewHeader(const uint8_t* buffer, size_t size) { if (size < sizeof(lutHeader_t)) { throw framework::runtime_error_f("Buffer too small for LUT header: expected at least %zu, got %zu", sizeof(lutHeader_t), size); } - const auto* header = reinterpret_cast(buffer); - if (!header->check_version()) { + const auto* header = reinterpret_cast(buffer); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) + if (!header->checkVersion()) { throw framework::runtime_error_f("LUT header version mismatch: expected %d, got %d", LUTCOVM_VERSION, header->version); } return *header; @@ -221,7 +246,7 @@ FlatLutData FlatLutData::ViewFromBuffer(const uint8_t* buffer, size_t size) FlatLutData FlatLutData::ViewFromBuffer(std::span const& span) { - return ViewFromBuffer(reinterpret_cast(span.data()), span.size_bytes()); + return ViewFromBuffer(reinterpret_cast(span.data()), span.size_bytes()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } bool FlatLutData::isLoaded() const @@ -229,14 +254,14 @@ bool FlatLutData::isLoaded() const return ((!mData.empty()) || (!mDataRef.empty())); } -lutHeader_t FlatLutData::PreviewHeader(std::ifstream& file, const char* filename) +lutHeader_t FlatLutData::previewHeader(std::ifstream& file, const char* filename) { lutHeader_t tempHeader; - file.read(reinterpret_cast(&tempHeader), sizeof(lutHeader_t)); + file.read(reinterpret_cast(&tempHeader), sizeof(lutHeader_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (file.gcount() != static_cast(sizeof(lutHeader_t))) { throw framework::runtime_error_f("Failed to read LUT header from %s", filename); } - if (!tempHeader.check_version()) { + if (!tempHeader.checkVersion()) { throw framework::runtime_error_f("LUT header version mismatch: expected %d, got %d", LUTCOVM_VERSION, tempHeader.version); } return tempHeader; @@ -245,7 +270,7 @@ lutHeader_t FlatLutData::PreviewHeader(std::ifstream& file, const char* filename FlatLutData FlatLutData::loadFromFile(std::ifstream& file, const char* filename) { // Read header first - lutHeader_t tempHeader = PreviewHeader(file, filename); + lutHeader_t tempHeader = previewHeader(file, filename); FlatLutData data; @@ -257,7 +282,7 @@ FlatLutData FlatLutData::loadFromFile(std::ifstream& file, const char* filename) size_t numEntries = static_cast(data.mNchBins) * data.mRadBins * data.mEtaBins * data.mPtBins; size_t entriesSize = numEntries * sizeof(lutEntry_t); - file.read(reinterpret_cast(data.data() + headerSize), entriesSize); + file.read(reinterpret_cast(data.data() + headerSize), entriesSize); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (file.gcount() != static_cast(entriesSize)) { throw framework::runtime_error_f("Failed to read LUT entries from %s: expected %zu bytes, got %zu", filename, entriesSize, static_cast(file.gcount())); } @@ -273,4 +298,4 @@ void FlatLutData::reset() resetDimensions(); } -} // namespace o2::delphes +} // namespace o2::fastsim diff --git a/ALICE3/Core/FlatLutEntry.h b/ALICE3/Core/FlatLutEntry.h index 7786523170b..7e095f989c1 100644 --- a/ALICE3/Core/FlatLutEntry.h +++ b/ALICE3/Core/FlatLutEntry.h @@ -9,9 +9,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file FlatLutEntry.h +/// \brief Flat LUT data structures and buffer handling for the ALICE3 fast smearing backend. + #ifndef ALICE3_CORE_FLATLUTENTRY_H_ #define ALICE3_CORE_FLATLUTENTRY_H_ +#include #include #include #include @@ -19,15 +23,22 @@ #include #include -#define LUTCOVM_VERSION 20210801 +static constexpr int LUTCOVM_VERSION = 20210801; -namespace o2::delphes +namespace o2::fastsim { +constexpr int kNumCovarianceTerms = 15; +constexpr int kNumEigenModes = 5; +typedef std::array CovarianceArray; +typedef std::array EigenArray; +typedef std::array EigenArrayDouble; +typedef std::array, kNumEigenModes> EigenMatrix; + /** - * @brief Flat LUT entry structure + * @brief Flat LUT entry structure. */ -struct lutEntry_t { +struct LutEntry { float nch = 0.f; float eta = 0.f; float pt = 0.f; @@ -36,41 +47,46 @@ struct lutEntry_t { float eff2 = 0.f; float itof = 0.f; float otof = 0.f; - float covm[15] = {0.f}; - float eigval[5] = {0.f}; - float eigvec[5][5] = {{0.f}}; - float eiginv[5][5] = {{0.f}}; + CovarianceArray covm = {0.f}; + EigenArray eigval = {0.f}; + EigenMatrix eigvec = {{{0.f}}}; + EigenMatrix eiginv = {{{0.f}}}; void print() const; }; +using lutEntry_t = LutEntry; + /** - * @brief Binning map + * @brief Binning map. */ -struct map_t { +struct Map { int nbins = 1; float min = 0.f; float max = 1.e6f; bool log = false; - float eval(int bin) const + [[nodiscard]] float eval(int bin) const { float width = (max - min) / nbins; float val = min + (bin + 0.5f) * width; - if (log) + if (log) { return std::pow(10.f, val); + } return val; } - float fracPositionWithinBin(float val) const; - int find(float val) const; + [[nodiscard]] float fracPositionWithinBin(float val) const; + [[nodiscard]] int find(float val) const; void print() const; }; +using map_t = Map; + /** - * @brief LUT header + * @brief LUT header. */ -struct lutHeader_t { +struct LutHeader { int version = LUTCOVM_VERSION; int pdg = 0; float mass = 0.f; @@ -80,12 +96,14 @@ struct lutHeader_t { map_t etamap; map_t ptmap; - bool check_version() const; + [[nodiscard]] bool checkVersion() const; void print() const; }; +using lutHeader_t = LutHeader; + /** - * @brief Flat LUT data container - single contiguous buffer + * @brief Flat LUT data container - single contiguous buffer. * Memory layout: [header][entry_0][entry_1]...[entry_N] * * All entries stored sequentially in a single allocation. @@ -104,10 +122,17 @@ class FlatLutData */ void initialize(const lutHeader_t& header); + [[nodiscard]] size_t getEntryIndex(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const + { + // Linear index: nch varies slowest, pt varies fastest + // idx = nch * (rad*eta*pt) + rad * (eta*pt) + eta * pt + pt + return static_cast(nch_bin) * (mRadBins * mEtaBins * mPtBins) + static_cast(rad_bin) * (mEtaBins * mPtBins) + static_cast(eta_bin) * mPtBins + static_cast(pt_bin); + } + /** * @brief Get LUT entry by bin indices (view) */ - const lutEntry_t* getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; + [[nodiscard]] const lutEntry_t* getEntryRef(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; /** * @brief Get LUT entry by bin indices (owned) @@ -117,7 +142,7 @@ class FlatLutData /** * @brief Get LUT header (view) */ - const lutHeader_t& getHeaderRef() const; + [[nodiscard]] const lutHeader_t& getHeaderRef() const; /** * @brief Get LUT header (owned) @@ -127,13 +152,13 @@ class FlatLutData /** * @brief Get raw data buffer */ - uint8_t* data() { return mData.data(); } // owned - const uint8_t* data() const { return mDataRef.data(); } // view + uint8_t* data() { return mData.data(); } // owned + [[nodiscard]] const uint8_t* data() const { return mDataRef.data(); } // view /** * @brief Total size in bytes */ - size_t bytes() const { return mDataRef.size(); } + [[nodiscard]] size_t bytes() const { return mDataRef.size(); } /** * @brief Construct a new FlatLutData from external buffer as a copy @@ -158,17 +183,17 @@ class FlatLutData /** * @brief Preview buffer header for version and other compatibility checks */ - static lutHeader_t PreviewHeader(const uint8_t* buffer, size_t size); + static lutHeader_t previewHeader(const uint8_t* buffer, size_t size); /** * @brief Preview file-stored header for version and other compatibility checks */ - static lutHeader_t PreviewHeader(std::ifstream& file, const char* filename); + static lutHeader_t previewHeader(std::ifstream& file, const char* filename); /** * @brief Check if the LUT is loaded */ - bool isLoaded() const; + [[nodiscard]] bool isLoaded() const; /** * @brief Reset LUT to empty @@ -179,7 +204,7 @@ class FlatLutData /** * @brief Linear index calculation for entry access */ - size_t getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; + [[nodiscard]] size_t getEntryOffset(int nch_bin, int rad_bin, int eta_bin, int pt_bin) const; /** * @brief Update dimensions from the current header @@ -217,6 +242,6 @@ class FlatLutData int mPtBins = 0; }; -} // namespace o2::delphes +} // namespace o2::fastsim #endif // ALICE3_CORE_FLATLUTENTRY_H_ diff --git a/ALICE3/Core/FlatLutRootWriter.cxx b/ALICE3/Core/FlatLutRootWriter.cxx new file mode 100644 index 00000000000..162084dcffe --- /dev/null +++ b/ALICE3/Core/FlatLutRootWriter.cxx @@ -0,0 +1,210 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ALICE3/Core/FlatLutRootWriter.h" + +#include +#include + +#include +#include +#include + +namespace o2::fastsim +{ + +FlatLutRootWriter::FlatLutRootWriter(const lutHeader_t& header, bool covarianceStoresSigma) + : mHeader(header), + mCovarianceStoresSigma(covarianceStoresSigma) +{ +} + +void FlatLutRootWriter::reserve(std::size_t n) +{ + mRows.reserve(n); +} + +void FlatLutRootWriter::addEntry(int entryIndex, + int nchBin, + int radBin, + int etaBin, + int ptBin, + const lutEntry_t& entry) +{ + FlatLutRootRow row; + row.entryIndex = entryIndex; + row.nchBin = nchBin; + row.radBin = radBin; + row.etaBin = etaBin; + row.ptBin = ptBin; + + row.nch = entry.nch; + row.eta = entry.eta; + row.pt = entry.pt; + + row.valid = entry.valid; + row.eff = entry.eff; + row.eff2 = entry.eff2; + row.itof = entry.itof; + row.otof = entry.otof; + + std::copy(entry.covm.begin(), entry.covm.end(), row.covm.begin()); + std::copy(entry.eigval.begin(), entry.eigval.end(), row.eigval.begin()); + row.eigvec = entry.eigvec; + row.eiginv = entry.eiginv; + + mRows.push_back(std::move(row)); +} + +bool FlatLutRootWriter::writeToFile(const std::string& rootFileName) const +{ + TFile output(rootFileName.c_str(), "RECREATE"); + if (output.IsZombie()) { + return false; + } + + int version = mHeader.version; + int pdg = mHeader.pdg; + float mass = mHeader.mass; + float field = mHeader.field; + + int nch_nbins = mHeader.nchmap.nbins; + float nch_min = mHeader.nchmap.min; + float nch_max = mHeader.nchmap.max; + bool nch_log = mHeader.nchmap.log; + + int rad_nbins = mHeader.radmap.nbins; + float rad_min = mHeader.radmap.min; + float rad_max = mHeader.radmap.max; + bool rad_log = mHeader.radmap.log; + + int eta_nbins = mHeader.etamap.nbins; + float eta_min = mHeader.etamap.min; + float eta_max = mHeader.etamap.max; + bool eta_log = mHeader.etamap.log; + + int pt_nbins = mHeader.ptmap.nbins; + float pt_min = mHeader.ptmap.min; + float pt_max = mHeader.ptmap.max; + bool pt_log = mHeader.ptmap.log; + + bool covarianceStoresSigma = mCovarianceStoresSigma; + + TTree headerTree("lutHeader", "LUT header"); + headerTree.Branch("version", &version, "version/I"); + headerTree.Branch("pdg", &pdg, "pdg/I"); + headerTree.Branch("mass", &mass, "mass/F"); + headerTree.Branch("field", &field, "field/F"); + + headerTree.Branch("nch_nbins", &nch_nbins, "nch_nbins/I"); + headerTree.Branch("nch_min", &nch_min, "nch_min/F"); + headerTree.Branch("nch_max", &nch_max, "nch_max/F"); + headerTree.Branch("nch_log", &nch_log, "nch_log/O"); + + headerTree.Branch("rad_nbins", &rad_nbins, "rad_nbins/I"); + headerTree.Branch("rad_min", &rad_min, "rad_min/F"); + headerTree.Branch("rad_max", &rad_max, "rad_max/F"); + headerTree.Branch("rad_log", &rad_log, "rad_log/O"); + + headerTree.Branch("eta_nbins", &eta_nbins, "eta_nbins/I"); + headerTree.Branch("eta_min", &eta_min, "eta_min/F"); + headerTree.Branch("eta_max", &eta_max, "eta_max/F"); + headerTree.Branch("eta_log", &eta_log, "eta_log/O"); + + headerTree.Branch("pt_nbins", &pt_nbins, "pt_nbins/I"); + headerTree.Branch("pt_min", &pt_min, "pt_min/F"); + headerTree.Branch("pt_max", &pt_max, "pt_max/F"); + headerTree.Branch("pt_log", &pt_log, "pt_log/O"); + + headerTree.Branch("covarianceStoresSigma", &covarianceStoresSigma, "covarianceStoresSigma/O"); + headerTree.Fill(); + + TTree entryTree("lutEntries", "LUT entries"); + + int entryIndex = -1; + int nchBin = -1; + int radBin = -1; + int etaBin = -1; + int ptBin = -1; + + float nch = 0.f; + float eta = 0.f; + float pt = 0.f; + + bool valid = false; + float eff = 0.f; + float eff2 = 0.f; + float itof = 0.f; + float otof = 0.f; + + float covm[kNumCovarianceTerms] = {0.f}; + float eigval[kNumEigenModes] = {0.f}; + float eigvec[kNumEigenModes * kNumEigenModes] = {0.f}; + float eiginv[kNumEigenModes * kNumEigenModes] = {0.f}; + + entryTree.Branch("entryIndex", &entryIndex, "entryIndex/I"); + entryTree.Branch("nchBin", &nchBin, "nchBin/I"); + entryTree.Branch("radBin", &radBin, "radBin/I"); + entryTree.Branch("etaBin", &etaBin, "etaBin/I"); + entryTree.Branch("ptBin", &ptBin, "ptBin/I"); + + entryTree.Branch("nch", &nch, "nch/F"); + entryTree.Branch("eta", &eta, "eta/F"); + entryTree.Branch("pt", &pt, "pt/F"); + + entryTree.Branch("valid", &valid, "valid/O"); + entryTree.Branch("eff", &eff, "eff/F"); + entryTree.Branch("eff2", &eff2, "eff2/F"); + entryTree.Branch("itof", &itof, "itof/F"); + entryTree.Branch("otof", &otof, "otof/F"); + + entryTree.Branch("covm", covm, "covm[15]/F"); + entryTree.Branch("eigval", eigval, "eigval[5]/F"); + entryTree.Branch("eigvec", eigvec, "eigvec[25]/F"); + entryTree.Branch("eiginv", eiginv, "eiginv[25]/F"); + + for (const auto& row : mRows) { + entryIndex = row.entryIndex; + nchBin = row.nchBin; + radBin = row.radBin; + etaBin = row.etaBin; + ptBin = row.ptBin; + + nch = row.nch; + eta = row.eta; + pt = row.pt; + + valid = row.valid; + eff = row.eff; + eff2 = row.eff2; + itof = row.itof; + otof = row.otof; + + std::copy(row.covm.begin(), row.covm.end(), covm); + std::copy(row.eigval.begin(), row.eigval.end(), eigval); + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { + eigvec[kNumEigenModes * i + j] = row.eigvec[i][j]; + eiginv[kNumEigenModes * i + j] = row.eiginv[i][j]; + } + } + + entryTree.Fill(); + } + + headerTree.Write(); + entryTree.Write(); + output.Close(); + + return true; +} + +} // namespace o2::fastsim diff --git a/ALICE3/Core/FlatLutRootWriter.h b/ALICE3/Core/FlatLutRootWriter.h new file mode 100644 index 00000000000..4360c7f72ae --- /dev/null +++ b/ALICE3/Core/FlatLutRootWriter.h @@ -0,0 +1,71 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICE3_CORE_FLATLUTROOTWRITER_H_ +#define ALICE3_CORE_FLATLUTROOTWRITER_H_ + +#include "ALICE3/Core/FlatLutEntry.h" + +#include +#include +#include +#include + +namespace o2::fastsim +{ + +struct FlatLutRootRow { + int entryIndex = -1; + int nchBin = -1; + int radBin = -1; + int etaBin = -1; + int ptBin = -1; + + float nch = 0.f; + float eta = 0.f; + float pt = 0.f; + + bool valid = false; + float eff = 0.f; + float eff2 = 0.f; + float itof = 0.f; + float otof = 0.f; + + std::array covm{}; + std::array eigval{}; + EigenMatrix eigvec{}; + EigenMatrix eiginv{}; +}; + +class FlatLutRootWriter +{ + public: + FlatLutRootWriter(const lutHeader_t& header, bool covarianceStoresSigma); + + void reserve(std::size_t n); + void addEntry(int entryIndex, + int nchBin, + int radBin, + int etaBin, + int ptBin, + const lutEntry_t& entry); + + bool writeToFile(const std::string& rootFileName) const; + + private: + lutHeader_t mHeader; + bool mCovarianceStoresSigma = false; + std::vector mRows; +}; + +} // namespace o2::fastsim + +#endif // ALICE3_CORE_FLATLUTROOTWRITER_H_ diff --git a/ALICE3/Core/FlatLutWriter.cxx b/ALICE3/Core/FlatLutWriter.cxx index c63c1c96dd3..a85c242a8b1 100644 --- a/ALICE3/Core/FlatLutWriter.cxx +++ b/ALICE3/Core/FlatLutWriter.cxx @@ -30,12 +30,11 @@ #include #include +#include #include #include #include -using namespace o2::delphes; - namespace o2::fastsim { void FlatLutWriter::print() const @@ -57,7 +56,7 @@ void FlatLutWriter::print() const std::string FlatLutWriter::LutBinning::toString() const { - std::string str = ""; + std::string str; str.append(log ? "log" : "lin"); str.append(" nbins: "); str.append(std::to_string(nbins)); @@ -85,7 +84,7 @@ bool FlatLutWriter::fatSolve(lutEntry_t& lutEntry, o2::upgrade::convertTLorentzVectorToO2Track(q, tlv, {0.f, 0.f, 0.f}, trkIn); o2::track::TrackParCov trkOut; - const int status = fat.FastTrack(trkIn, trkOut, nch); + const int status = fat.fastTrack(trkIn, trkOut, nch); if (status <= mAtLeastHits) { LOGF(debug, "fatSolve: FastTrack failed with status %d (threshold %d)", status, mAtLeastHits); return false; @@ -94,28 +93,31 @@ bool FlatLutWriter::fatSolve(lutEntry_t& lutEntry, LOGF(debug, "fatSolve: FastTrack succeeded with status %d", status); lutEntry.valid = true; - lutEntry.itof = fat.GetGoodHitProb(itof); - lutEntry.otof = fat.GetGoodHitProb(otof); + lutEntry.itof = fat.getGoodHitProb(itof); + lutEntry.otof = fat.getGoodHitProb(otof); - static constexpr int nCov = 15; - for (int i = 0; i < nCov; ++i) + for (int i = 0; i < kNumCovarianceTerms; ++i) { lutEntry.covm[i] = trkOut.getCov()[i]; + } // Define the efficiency auto totfake = 0.f; lutEntry.eff = 1.f; - for (size_t i = 1; i < fat.GetNLayers(); ++i) { - if (fat.IsLayerInert(i)) + for (size_t i = 1; i < fat.getNLayers(); ++i) { + if (fat.isLayerInert(i)) { continue; // skip inert layers - auto igoodhit = fat.GetGoodHitProb(i); - if (igoodhit <= 0.f || i == itof || i == otof) + } + auto igoodhit = fat.getGoodHitProb(i); + if (igoodhit <= 0.f || i == itof || i == otof) { continue; + } lutEntry.eff *= igoodhit; auto pairfake = 0.f; - for (size_t j = i + 1; j < fat.GetNLayers(); ++j) { - auto jgoodhit = fat.GetGoodHitProb(j); - if (jgoodhit <= 0.f || j == itof || j == otof) + for (size_t j = i + 1; j < fat.getNLayers(); ++j) { + auto jgoodhit = fat.getGoodHitProb(j); + if (jgoodhit <= 0.f || j == itof || j == otof) { continue; + } pairfake = (1.f - igoodhit) * (1.f - jgoodhit); break; } @@ -127,14 +129,14 @@ bool FlatLutWriter::fatSolve(lutEntry_t& lutEntry, } #ifdef USE_FWD_PARAM -bool FlatLutWriter::fwdSolve(float* covm, float pt, float eta, float mass) +bool FlatLutWriter::fwdSolve(CovarianceArray covm, float pt, float eta, float mass) { if (fwdRes(covm, pt, eta, mass) < 0) return false; return true; } #else -bool FlatLutWriter::fwdSolve(float*, float, float, float) +bool FlatLutWriter::fwdSolve(CovarianceArray, float, float, float) { return false; } @@ -155,9 +157,8 @@ bool FlatLutWriter::fwdPara(lutEntry_t& lutEntry, float pt, float eta, float mas return false; } - static constexpr int nCov = 15; - float covmbarrel[nCov] = {0.f}; - for (int i = 0; i < nCov; ++i) { + CovarianceArray covmbarrel = {0.f}; + for (int i = 0; i < kNumCovarianceTerms; ++i) { covmbarrel[i] = lutEntry.covm[i]; } @@ -242,7 +243,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t } lutHeader.mass = particle->Mass(); - const int q = std::abs(particle->Charge()) / 3; + const int q = std::abs(static_cast(particle->Charge())) / 3; if (q <= 0) { LOGF(error, "Negative or null charge (%f) for pdg code %d", particle->Charge(), pdg); lutFile.close(); @@ -265,7 +266,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t setMap(lutHeader.ptmap, mPtBinning); // Write header to file - lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader_t)); + lutFile.write(reinterpret_cast(&lutHeader), sizeof(lutHeader_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!lutFile.good()) { LOGF(error, "Failed to write LUT header to %s", filename); lutFile.close(); @@ -290,7 +291,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t LOGF(info, "Writing nch bin %d/%d", inch, nnch); auto nch = lutHeader.nchmap.eval(inch); lutEntry.nch = nch; - fat.SetdNdEtaCent(nch); + fat.setdNdEtaCent(static_cast(nch)); for (int irad = 0; irad < nrad; ++irad) { for (int ieta = 0; ieta < neta; ++ieta) { @@ -360,7 +361,7 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t diagonalise(lutEntry); // Write entry to file - lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); + lutFile.write(reinterpret_cast(&lutEntry), sizeof(lutEntry_t)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) if (!lutFile.good()) { LOGF(error, "Failed to write LUT entry at index [%d,%d,%d,%d]", inch, irad, ieta, ipt); lutFile.close(); @@ -379,10 +380,9 @@ void FlatLutWriter::lutWrite(const char* filename, int pdg, float field, size_t void FlatLutWriter::diagonalise(lutEntry_t& lutEntry) { - static constexpr int kEig = 5; - TMatrixDSym m(kEig); + TMatrixDSym m(kNumEigenModes); - for (int i = 0, k = 0; i < kEig; ++i) { + for (int i = 0, k = 0; i < kNumEigenModes; ++i) { for (int j = 0; j < i + 1; ++j, ++k) { m(i, j) = lutEntry.covm[k]; m(j, i) = lutEntry.covm[k]; @@ -392,41 +392,50 @@ void FlatLutWriter::diagonalise(lutEntry_t& lutEntry) TMatrixDSymEigen eigen(m); // Eigenvalues - TVectorD eigenVal = eigen.GetEigenValues(); - for (int i = 0; i < kEig; ++i) + const TVectorD& eigenVal = eigen.GetEigenValues(); + for (int i = 0; i < kNumEigenModes; ++i) { lutEntry.eigval[i] = eigenVal[i]; + } // Eigenvectors TMatrixD eigenVec = eigen.GetEigenVectors(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { lutEntry.eigvec[i][j] = eigenVec[i][j]; + } + } // Inverse eigenvectors eigenVec.Invert(); - for (int i = 0; i < kEig; ++i) - for (int j = 0; j < kEig; ++j) + for (int i = 0; i < kNumEigenModes; ++i) { + for (int j = 0; j < kNumEigenModes; ++j) { lutEntry.eiginv[i][j] = eigenVec[i][j]; + } + } } TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, float nch, float radius, float eta, float pt) { LOGF(info, "Reading LUT file: %s", filename); - static const int kNch = 0; - static const int kEta = 1; - static const int kPt = 2; + enum VsType : uint8_t { + kNch = 0, + kEta = 1, + kPt = 2 + }; - static const int kEfficiency = 0; - static const int kEfficiency2 = 1; - static const int kEfficiencyInnerTOF = 2; - static const int kEfficiencyOuterTOF = 3; - static const int kPtResolution = 4; - static const int kRPhiResolution = 5; - static const int kZResolution = 6; + enum WhatType : uint8_t { + kEfficiency = 0, + kEfficiency2 = 1, + kEfficiencyInnerTOF = 2, + kEfficiencyOuterTOF = 3, + kPtResolution = 4, + kRPhiResolution = 5, + kZResolution = 6 + }; // Use TrackSmearer to load and access the LUT - o2::delphes::TrackSmearer smearer; + o2::fastsim::TrackSmearer smearer; if (!smearer.loadTable(pdg, filename)) { LOGF(error, "Failed to load LUT from %s", filename); return nullptr; @@ -452,7 +461,7 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, lutMap = lutHeader->ptmap; break; default: - LOGF(error, "Unknown vs: %d", vs); + LOG(fatal) << "Unknown vs: " << vs; return nullptr; } @@ -475,6 +484,10 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, LOGF(info, "Plot versus Pt"); g->GetXaxis()->SetTitle("p_{T} (GeV/c)"); break; + default: + LOG(fatal) << "Unknown vs: " << vs; + delete g; + return nullptr; } switch (what) { @@ -507,7 +520,7 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, g->GetYaxis()->SetTitle("Z Resolution (#mum)"); break; default: - LOGF(error, "Unknown what: %d", what); + LOG(fatal) << "Unknown what: " << what; delete g; return nullptr; } @@ -525,6 +538,10 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, case kPt: pt = lutMap.eval(i); break; + default: + LOG(fatal) << "Unknown vs: " << vs; + delete g; + return nullptr; } float eff = 0.f; @@ -549,6 +566,10 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, case kPt: cen = lutEntry->pt; break; + default: + LOG(fatal) << "Unknown vs: " << vs; + delete g; + return nullptr; } double val = 0.f; @@ -575,8 +596,9 @@ TGraph* FlatLutWriter::lutRead(const char* filename, int pdg, int what, int vs, val = std::sqrt(lutEntry->covm[1]) * 1.e4f; // z resolution (um) break; default: - LOGF(error, "Unknown what: %d", what); - break; + LOG(fatal) << "Unknown what: " << what; + delete g; + return nullptr; } g->AddPoint(cen, val); } diff --git a/ALICE3/Core/FlatLutWriter.h b/ALICE3/Core/FlatLutWriter.h index fd29a174c86..33de44dc8f8 100644 --- a/ALICE3/Core/FlatLutWriter.h +++ b/ALICE3/Core/FlatLutWriter.h @@ -23,7 +23,7 @@ #include namespace o2::fastsim { -using lutEntry_t = o2::delphes::lutEntry_t; +using lutEntry_t = o2::fastsim::lutEntry_t; /** * @brief LUT writer using flat binary format * @@ -39,10 +39,10 @@ class FlatLutWriter FlatLutWriter() = default; // Setters for binning configuration - void setBinningNch(bool log, int nbins, float min, float max) { mNchBinning = {log, nbins, min, max}; } - void setBinningRadius(bool log, int nbins, float min, float max) { mRadiusBinning = {log, nbins, min, max}; } - void setBinningEta(bool log, int nbins, float min, float max) { mEtaBinning = {log, nbins, min, max}; } - void setBinningPt(bool log, int nbins, float min, float max) { mPtBinning = {log, nbins, min, max}; } + void setBinningNch(bool log, int nbins, float min, float max) { mNchBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } + void setBinningRadius(bool log, int nbins, float min, float max) { mRadiusBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } + void setBinningEta(bool log, int nbins, float min, float max) { mEtaBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } + void setBinningPt(bool log, int nbins, float min, float max) { mPtBinning = {.log = log, .nbins = nbins, .min = min, .max = max}; } void setEtaMaxBarrel(float eta) { etaMaxBarrel = eta; } void setAtLeastHits(int n) { mAtLeastHits = n; } @@ -59,7 +59,7 @@ class FlatLutWriter const float nch = 1.0f); void print() const; - bool fwdSolve(float* covm, float pt = 0.1f, float eta = 0.0f, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion]); + bool fwdSolve(o2::fastsim::CovarianceArray covm, float pt = 0.1f, float eta = 0.0f, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion]); bool fwdPara(lutEntry_t& lutEntry, float pt = 0.1f, float eta = 0.0f, float mass = o2::track::pid_constants::sMasses[o2::track::PID::Pion], float Bfield = 0.5f); void lutWrite(const char* filename = "lutCovm.dat", int pdg = 211, float field = 0.2f, size_t itof = 0, size_t otof = 0); TGraph* lutRead(const char* filename, int pdg, int what, int vs, float nch = 0.f, float radius = 0.f, float eta = 0.f, float pt = 0.f); @@ -84,13 +84,13 @@ class FlatLutWriter int nbins; float min; float max; - std::string toString() const; + [[nodiscard]] std::string toString() const; }; - LutBinning mNchBinning = {true, 20, 0.5f, 3.5f}; - LutBinning mRadiusBinning = {false, 1, 0.0f, 100.0f}; - LutBinning mEtaBinning = {false, 80, -4.0f, 4.0f}; - LutBinning mPtBinning = {true, 200, -2.0f, 2.0f}; + LutBinning mNchBinning = {.log = true, .nbins = 20, .min = 0.5f, .max = 3.5f}; + LutBinning mRadiusBinning = {.log = false, .nbins = 1, .min = 0.0f, .max = 100.0f}; + LutBinning mEtaBinning = {.log = false, .nbins = 80, .min = -4.0f, .max = 4.0f}; + LutBinning mPtBinning = {.log = true, .nbins = 200, .min = -2.0f, .max = 2.0f}; }; } // namespace o2::fastsim diff --git a/ALICE3/Core/FlatTrackSmearer.cxx b/ALICE3/Core/FlatTrackSmearer.cxx index d90c8a8997b..9617315d2e4 100644 --- a/ALICE3/Core/FlatTrackSmearer.cxx +++ b/ALICE3/Core/FlatTrackSmearer.cxx @@ -11,6 +11,7 @@ #include "FlatTrackSmearer.h" +#include "ALICE3/Core/ConfigurationParser.h" #include "ALICE3/Core/FlatLutEntry.h" #include "ALICE3/Core/GeometryContainer.h" @@ -28,9 +29,9 @@ #include #include -namespace o2::delphes +namespace o2::fastsim { -int TrackSmearer::getIndexPDG(int pdg) +int TrackSmearer::getIndexPDG(const int pdg) { switch (std::abs(pdg)) { case 11: @@ -56,7 +57,7 @@ int TrackSmearer::getIndexPDG(int pdg) } } -const char* TrackSmearer::getParticleName(int pdg) +const char* TrackSmearer::getParticleName(const int pdg) { switch (std::abs(pdg)) { case 11: @@ -82,15 +83,6 @@ const char* TrackSmearer::getParticleName(int pdg) } } -void TrackSmearer::setWhatEfficiency(int val) -{ - // FIXME: this really should be an enum - if (val > 2) { - throw framework::runtime_error_f("getLUTEntry: unknown efficiency type %d", mWhatEfficiency); - } - mWhatEfficiency = val; -} - bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) { if (!filename || filename[0] == '\0') { @@ -105,7 +97,7 @@ bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) } LOGF(info, "Loading %s LUT file: '%s'", getParticleName(pdg), filename); - const std::string localFilename = o2::fastsim::GeometryEntry::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, 10); + const std::string localFilename = o2::fastsim::ConfigurationParser::accessFile(filename, "./.ALICE3/LUTs/", mCcdbManager, o2::fastsim::GeometryContainer::cleanLutWhenLoaded() ? 10 : 0); std::ifstream lutFile(localFilename, std::ifstream::binary); if (!lutFile.is_open()) { @@ -121,7 +113,7 @@ bool TrackSmearer::loadTable(int pdg, const char* filename, bool forceReload) LOGF(error, "LUT header PDG mismatch: expected %d, got %d; not loading", pdg, header.pdg); return false; } - } catch (framework::RuntimeErrorRef ref) { + } catch (framework::RuntimeErrorRef& ref) { LOGF(error, "%s", framework::error_from_ref(ref).what); return false; } @@ -139,13 +131,13 @@ bool TrackSmearer::adoptTable(int pdg, const uint8_t* buffer, size_t size, bool return false; } try { - auto header = FlatLutData::PreviewHeader(buffer, size); + auto header = FlatLutData::previewHeader(buffer, size); if (header.pdg != pdg && !checkSpecialCase(pdg, header)) { LOGF(error, "LUT header PDG mismatch: expected %d, got %d", pdg, header.pdg); return false; } mLUTData[ipdg] = FlatLutData::AdoptFromBuffer(buffer, size); - } catch (framework::RuntimeErrorRef ref) { + } catch (framework::RuntimeErrorRef& ref) { LOGF(error, "%s", framework::error_from_ref(ref).what); } @@ -162,13 +154,13 @@ bool TrackSmearer::viewTable(int pdg, const uint8_t* buffer, size_t size, bool f return false; } try { - auto header = FlatLutData::PreviewHeader(buffer, size); + auto header = FlatLutData::previewHeader(buffer, size); if (header.pdg != pdg && !checkSpecialCase(pdg, header)) { LOGF(error, "LUT header PDG mismatch: expected %d, got %d", pdg, header.pdg); return false; } mLUTData[ipdg] = FlatLutData::ViewFromBuffer(buffer, size); - } catch (framework::RuntimeErrorRef ref) { + } catch (framework::RuntimeErrorRef& ref) { LOGF(error, "%s", framework::error_from_ref(ref).what); } @@ -179,7 +171,7 @@ bool TrackSmearer::viewTable(int pdg, const uint8_t* buffer, size_t size, bool f bool TrackSmearer::viewTable(int pdg, std::span const& span, bool forceReload) { - return viewTable(pdg, reinterpret_cast(span.data()), span.size_bytes(), forceReload); + return viewTable(pdg, reinterpret_cast(span.data()), span.size_bytes(), forceReload); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) } bool TrackSmearer::hasTable(int pdg) const @@ -188,22 +180,14 @@ bool TrackSmearer::hasTable(int pdg) const return mLUTData[ipdg].isLoaded(); } -bool TrackSmearer::checkSpecialCase(int pdg, lutHeader_t const& header) +bool TrackSmearer::checkSpecialCase(const int pdg, lutHeader_t const& header) { // Validate header - bool specialPdgCase = false; - switch (pdg) { - case o2::constants::physics::kAlpha: - // Special case: Allow Alpha particles to use He3 LUT - specialPdgCase = (header.pdg == o2::constants::physics::kHelium3); - if (specialPdgCase) { - LOGF(info, "Alpha particles (PDG %d) will use He3 LUT data (PDG %d)", pdg, header.pdg); - } - break; - default: - break; + if (pdg == o2::constants::physics::kAlpha && (header.pdg == o2::constants::physics::kHelium3)) { // Special case: Allow Alpha particles to use He3 LUT + LOGF(info, "Alpha particles (PDG %d) will use He3 LUT data (PDG %d)", pdg, header.pdg); + return true; } - return specialPdgCase; + return false; } const lutHeader_t* TrackSmearer::getLUTHeader(int pdg) const @@ -235,7 +219,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons static constexpr float kFractionThreshold = 0.5f; if (fraction > kFractionThreshold) { switch (mWhatEfficiency) { - case 1: { + case kWhatEfficiencyReco: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch < header.nchmap.nbins - 1) { const auto* entry_next = mLUTData[ipdg].getEntryRef(inch + 1, irad, ieta, ipt); @@ -245,7 +229,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } - case 2: { + case kWhatEfficiencyRecoAndTOF: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch < header.nchmap.nbins - 1) { const auto* entry_next = mLUTData[ipdg].getEntryRef(inch + 1, irad, ieta, ipt); @@ -255,11 +239,13 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } + default: + LOG(fatal) << "Unknown efficiency type: " << mWhatEfficiency; } } else { float comparisonValue = header.nchmap.log ? std::log10(nch) : nch; switch (mWhatEfficiency) { - case 1: { + case kWhatEfficiencyReco: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch > 0 && comparisonValue < header.nchmap.max) { const auto* entry_prev = mLUTData[ipdg].getEntryRef(inch - 1, irad, ieta, ipt); @@ -269,7 +255,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } - case 2: { + case kWhatEfficiencyRecoAndTOF: { const auto* entry_curr = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (inch > 0 && comparisonValue < header.nchmap.max) { const auto* entry_prev = mLUTData[ipdg].getEntryRef(inch - 1, irad, ieta, ipt); @@ -279,18 +265,22 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons } break; } + default: + LOG(fatal) << "Unknown efficiency type: " << mWhatEfficiency; } } } else { const auto* entry = mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); if (entry) { switch (mWhatEfficiency) { - case 1: + case kWhatEfficiencyReco: interpolatedEff = entry->eff; break; - case 2: + case kWhatEfficiencyRecoAndTOF: interpolatedEff = entry->eff2; break; + default: + LOG(fatal) << "Unknown efficiency type: " << mWhatEfficiency; } } } @@ -298,7 +288,7 @@ const lutEntry_t* TrackSmearer::getLUTEntry(const int pdg, const float nch, cons return mLUTData[ipdg].getEntryRef(inch, irad, ieta, ipt); } -bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, float interpolatedEff) +bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, const float interpolatedEff) { bool isReconstructed = true; @@ -306,10 +296,10 @@ bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, floa if (mUseEfficiency) { auto eff = 0.f; switch (mWhatEfficiency) { - case 1: + case kWhatEfficiencyReco: eff = lutEntry->eff; break; - case 2: + case kWhatEfficiencyRecoAndTOF: eff = lutEntry->eff2; break; } @@ -327,20 +317,19 @@ bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, floa } // Transform params vector and smear - static constexpr int kParSize = 5; - double params[kParSize]; - for (int i = 0; i < kParSize; ++i) { + EigenArrayDouble params; + for (int i = 0; i < kNumEigenModes; ++i) { double val = 0.; - for (int j = 0; j < kParSize; ++j) { + for (int j = 0; j < kNumEigenModes; ++j) { val += lutEntry->eigvec[j][i] * o2track.getParam(j); } params[i] = gRandom->Gaus(val, std::sqrt(lutEntry->eigval[i])); } // Transform back params vector - for (int i = 0; i < kParSize; ++i) { + for (int i = 0; i < kNumEigenModes; ++i) { double val = 0.; - for (int j = 0; j < kParSize; ++j) { + for (int j = 0; j < kNumEigenModes; ++j) { val += lutEntry->eiginv[j][i] * params[j]; } o2track.setParam(val, i); @@ -352,22 +341,21 @@ bool TrackSmearer::smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, floa } // Set covariance matrix - static constexpr int kCovMatSize = 15; - for (int i = 0; i < kCovMatSize; ++i) { + for (int i = 0; i < kNumCovarianceTerms; ++i) { o2track.setCov(lutEntry->covm[i], i); } return isReconstructed; } -bool TrackSmearer::smearTrack(O2Track& o2track, int pdg, float nch) +bool TrackSmearer::smearTrack(O2Track& o2track, const int pdg, const float nch) { auto pt = o2track.getPt(); - switch (pdg) { - case o2::constants::physics::kHelium3: - case -o2::constants::physics::kHelium3: - pt *= 2.f; - break; + if (pdg == o2::constants::physics::kHelium3 || + pdg == -o2::constants::physics::kHelium3 || + pdg == o2::constants::physics::kAlpha || + pdg == -o2::constants::physics::kAlpha) { + pt *= 2.f; } auto eta = o2track.getEta(); @@ -423,4 +411,4 @@ double TrackSmearer::getEfficiency(const int pdg, const float nch, const float e return efficiency; } -} // namespace o2::delphes +} // namespace o2::fastsim diff --git a/ALICE3/Core/FlatTrackSmearer.h b/ALICE3/Core/FlatTrackSmearer.h index e7943d7c1c0..6008a560e9b 100644 --- a/ALICE3/Core/FlatTrackSmearer.h +++ b/ALICE3/Core/FlatTrackSmearer.h @@ -17,11 +17,12 @@ #include #include +#include #include #include #include -namespace o2::delphes +namespace o2::fastsim { /** * @brief Track smearing with flat LUT backend @@ -37,47 +38,49 @@ class TrackSmearer bool adoptTable(int pdg, const uint8_t* buffer, size_t size, bool forceReload = false); bool viewTable(int pdg, const uint8_t* buffer, size_t size, bool forceReload = false); bool viewTable(int pdg, std::span const& span, bool forceReload = false); - bool hasTable(int pdg) const; + [[nodiscard]] bool hasTable(int pdg) const; void useEfficiency(bool val) { mUseEfficiency = val; } void interpolateEfficiency(bool val) { mInterpolateEfficiency = val; } void skipUnreconstructed(bool val) { mSkipUnreconstructed = val; } - void setWhatEfficiency(int val); + enum EfficiencyType : uint8_t { + kWhatEfficiencyReco = 0, + kWhatEfficiencyRecoAndTOF = 1 + }; + void setWhatEfficiency(EfficiencyType val) { mWhatEfficiency = val; } - const lutHeader_t* getLUTHeader(int pdg) const; - const lutEntry_t* getLUTEntry(int pdg, float nch, float radius, float eta, float pt, float& interpolatedEff) const; + [[nodiscard]] const lutHeader_t* getLUTHeader(const int pdg) const; + const lutEntry_t* getLUTEntry(const int pdg, const float nch, const float radius, const float eta, const float pt, float& interpolatedEff) const; - bool smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, float interpolatedEff); - bool smearTrack(O2Track& o2track, int pdg, float nch); + bool smearTrack(O2Track& o2track, const lutEntry_t* lutEntry, const float interpolatedEff); + bool smearTrack(O2Track& o2track, const int pdg, const float nch); - double getPtRes(const int pdg, const float nch, const float eta, const float pt) const; - double getEtaRes(const int pdg, const float nch, const float eta, const float pt) const; - double getAbsPtRes(const int pdg, const float nch, const float eta, const float pt) const; - double getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt) const; - double getEfficiency(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getPtRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getEtaRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getAbsPtRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getAbsEtaRes(const int pdg, const float nch, const float eta, const float pt) const; + [[nodiscard]] double getEfficiency(const int pdg, const float nch, const float eta, const float pt) const; - static int getIndexPDG(int pdg); - static const char* getParticleName(int pdg); + static int getIndexPDG(const int pdg); + static const char* getParticleName(const int pdg); - void setdNdEta(float val) { mdNdEta = val; } void setCcdbManager(o2::ccdb::BasicCCDBManager* mgr) { mCcdbManager = mgr; } protected: - static constexpr unsigned int nLUTs = 9; // Number of LUT available - FlatLutData mLUTData[nLUTs]; // Flat data storage + static constexpr unsigned int nLUTs = 9; // Number of LUT available, une per particle species (electron, muon, pion, kaon, proton, deuteron, triton, helium3, alpha) + std::array mLUTData; // Flat data storage bool mUseEfficiency = true; bool mInterpolateEfficiency = false; bool mSkipUnreconstructed = true; // don't smear tracks that are not reco'ed - int mWhatEfficiency = 1; - float mdNdEta = 1600.f; + EfficiencyType mWhatEfficiency = kWhatEfficiencyReco; private: o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; - static bool checkSpecialCase(int pdg, lutHeader_t const& header); + static bool checkSpecialCase(const int pdg, lutHeader_t const& header); }; -} // namespace o2::delphes +} // namespace o2::fastsim #endif // ALICE3_CORE_FLATTRACKSMEARER_H_ diff --git a/ALICE3/Core/GeometryContainer.cxx b/ALICE3/Core/GeometryContainer.cxx index aee8e36ebd8..130c3f9e1e9 100644 --- a/ALICE3/Core/GeometryContainer.cxx +++ b/ALICE3/Core/GeometryContainer.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \file GeometryContainer.h +/// \file GeometryContainer.cxx /// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) /// \brief Set of utilities for the ALICE3 geometry handling /// \since February 13, 2026 @@ -19,70 +19,16 @@ #include "Common/Core/TableHelper.h" -#include #include #include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include #include #include -#include #include -#include -#include - namespace o2::fastsim { -std::map> GeometryEntry::parseTEnvConfiguration(std::string& filename, std::vector& layers) -{ - std::map> configMap; - filename = gSystem->ExpandPathName(filename.c_str()); - LOG(info) << "Parsing TEnv configuration file: " << filename; - TEnv env(filename.c_str()); - THashList* table = env.GetTable(); - layers.clear(); - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - // key should contain exactly one dot - if (key.find('.') == std::string::npos || key.find('.') != key.rfind('.')) { - LOG(fatal) << "Key " << key << " does not contain exactly one dot"; - continue; - } - const std::string firstPart = key.substr(0, key.find('.')); - if (std::find(layers.begin(), layers.end(), firstPart) == layers.end()) { - layers.push_back(firstPart); - } - } - env.Print(); - // Layers - for (const auto& layer : layers) { - LOG(info) << " Reading layer " << layer; - for (int i = 0; i < table->GetEntries(); ++i) { - const std::string key = table->At(i)->GetName(); - if (key.find(layer + ".") == 0) { - const std::string paramName = key.substr(key.find('.') + 1); - const std::string value = env.GetValue(key.c_str(), ""); - configMap[layer][paramName] = value; - } - } - } - return configMap; -} - bool GeometryContainer::mCleanLutWhenLoaded = true; void GeometryContainer::init(o2::framework::InitContext& initContext) { @@ -94,15 +40,15 @@ void GeometryContainer::init(o2::framework::InitContext& initContext) } LOG(info) << "Size of detector configuration: " << detectorConfiguration.size(); - bool cleanLutWhenLoaded; - const bool foundCleanLutWhenLoaded = common::core::getTaskOptionValue(initContext, "on-the-fly-detector-geometry-provider", "cleanLutWhenLoaded", cleanLutWhenLoaded, false); + bool cleanLut; + const bool foundCleanLutWhenLoaded = common::core::getTaskOptionValue(initContext, "on-the-fly-detector-geometry-provider", "cleanLutWhenLoaded", cleanLut, false); if (!foundCleanLutWhenLoaded) { LOG(fatal) << "Could not retrieve foundCleanLutWhenLoaded option from OnTheFlyDetectorGeometryProvider task."; return; } - setLutCleanupSetting(cleanLutWhenLoaded); + setLutCleanupSetting(cleanLut); - for (std::string& configFile : detectorConfiguration) { + for (const std::string& configFile : detectorConfiguration) { LOG(info) << "Detector geometry configuration file used: " << configFile; addEntry(configFile); } @@ -159,134 +105,4 @@ void GeometryEntry::replaceValue(const std::string& layerName, const std::string setValue(layerName, key, value); } -std::string GeometryEntry::accessFile(const std::string& path, const std::string downloadPath, o2::ccdb::BasicCCDBManager* ccdb, int timeoutSeconds) -{ - - if (path.rfind("ccdb:", 0) == 0) { - const std::string ccdbPath = path.substr(5); // remove "ccdb:" prefix - const std::string localPath = Form("%s/%s/snapshot.root", downloadPath.c_str(), ccdbPath.c_str()); - const std::string lockFile = localPath + ".lock"; - const std::string doneFile = localPath + ".done"; - - // Create directory structure if it doesn't exist - std::string dirPath = localPath.substr(0, localPath.find_last_of('/')); - gSystem->mkdir(dirPath.c_str(), true); - - // Check if file is already fully downloaded - struct stat buffer; - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file already exists: " << localPath << ". Skipping download."; - return localPath; - } - - // Acquire file lock for inter-process synchronization - int lockFd = open(lockFile.c_str(), O_CREAT | O_RDWR, 0666); - if (lockFd == -1) { - LOG(error) << " --- Failed to create lock file: " << lockFile; - return localPath; - } - - // Try to acquire exclusive lock (non-blocking) - LOG(info) << " --- Attempting to acquire lock for: " << localPath; - int lockResult = flock(lockFd, LOCK_EX | LOCK_NB); - - if (lockResult == -1 && errno == EWOULDBLOCK) { - // Lock is held by another process - wait up to 10 minutes for download to complete - LOG(info) << " --- Lock is held by another process. Waiting for download to complete (up to 10 minutes)..."; - close(lockFd); - - const auto startTime = std::chrono::steady_clock::now(); - const auto timeout = std::chrono::minutes(10); - const auto checkInterval = std::chrono::seconds(5); - - while (true) { - // Check if download is complete - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; - return localPath; - } - - // Check timeout - auto elapsed = std::chrono::steady_clock::now() - startTime; - if (elapsed >= timeout) { - LOG(fatal) << " --- Timeout waiting for geometry file download: " << localPath << ". Waited for 10 minutes."; - return localPath; - } - - // Wait before checking again - std::this_thread::sleep_for(checkInterval); - } - } else if (lockResult == -1) { - LOG(error) << " --- Failed to acquire lock for: " << lockFile; - close(lockFd); - return localPath; - } - - // Lock acquired successfully - double-check if file was downloaded while we were trying - if (stat(doneFile.c_str(), &buffer) == 0) { - LOG(info) << " --- Geometry configuration file was downloaded by another process: " << localPath; - flock(lockFd, LOCK_UN); - close(lockFd); - return localPath; - } - - // File does not exist, retrieve from CCDB - LOG(info) << " --- CCDB source detected for detector geometry " << path; - std::map metadata; - bool status = ccdb->getCCDBAccessor().retrieveBlob(ccdbPath, downloadPath, metadata, 1); - if (!status) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Failed to retrieve geometry configuration from CCDB for path: " << ccdbPath; - return ""; - } - LOG(info) << " --- Retrieved geometry configuration from CCDB to: " << localPath; - - // Verify the integrity of the downloaded file - if (stat(localPath.c_str(), &buffer) != 0) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Downloaded file does not exist or is not accessible: " << localPath; - return ""; - } - if (buffer.st_size == 0) { - flock(lockFd, LOCK_UN); - close(lockFd); - LOG(fatal) << " --- Downloaded file is empty: " << localPath; - return ""; - } - LOG(info) << " --- File integrity verified. Size: " << buffer.st_size << " bytes"; - - // Create done marker file to indicate successful download - std::ofstream doneMarker(doneFile); - doneMarker.close(); - - // Release lock - flock(lockFd, LOCK_UN); - close(lockFd); - - // If timeout is specified, schedule file deletion after timeout - if (timeoutSeconds > 0 && GeometryContainer::cleanLutWhenLoaded()) { - LOG(info) << " --- Deleting geometry configuration file after timeout: " << localPath; - std::thread deletionThread([localPath, doneFile, timeoutSeconds]() { - LOG(info) << " --- Operating deletion of geometry configuration file after timeout: " << localPath; - std::this_thread::sleep_for(std::chrono::seconds(timeoutSeconds)); - if (std::remove(localPath.c_str()) == 0) { - LOG(info) << " --- File deleted successfully: " << localPath; - } else { - LOG(warning) << " --- Failed to delete file: " << localPath; - } - // Also remove the done marker file - if (std::remove(doneFile.c_str()) == 0) { - LOG(info) << " --- Done marker deleted: " << doneFile; - } - }); - deletionThread.detach(); - } - - return localPath; - } - return path; -} - } // namespace o2::fastsim diff --git a/ALICE3/Core/GeometryContainer.h b/ALICE3/Core/GeometryContainer.h index d5892b5b97b..deb69663e3b 100644 --- a/ALICE3/Core/GeometryContainer.h +++ b/ALICE3/Core/GeometryContainer.h @@ -18,6 +18,8 @@ #ifndef ALICE3_CORE_GEOMETRYCONTAINER_H_ #define ALICE3_CORE_GEOMETRYCONTAINER_H_ +#include "ALICE3/Core/ConfigurationParser.h" + #include #include #include @@ -33,37 +35,19 @@ namespace o2::fastsim struct GeometryEntry { // Default constructor GeometryEntry() = default; - explicit GeometryEntry(std::string filename, o2::ccdb::BasicCCDBManager* ccdb = nullptr) + explicit GeometryEntry(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdb = nullptr) + : mFileName(ConfigurationParser::accessFile(filename, "./.ALICE3/Configuration/", ccdb)) { - mFileName = accessFile(filename, "./.ALICE3/Configuration/", ccdb); - mConfigurations = GeometryEntry::parseTEnvConfiguration(mFileName, mLayerNames); + mConfigurations = ConfigurationParser::parseTEnvConfiguration(mFileName, mLayerNames); LOG(info) << "Loaded geometry configuration from file: " << mFileName << " with " << mLayerNames.size() << " layers."; if (mLayerNames.empty()) { LOG(warning) << "No layers found in geometry configuration file: " << filename; } } - /** - * @brief Parses a TEnv configuration file and returns the key-value pairs split per entry - * @param filename Path to the TEnv configuration file - * @param layers Vector to store the order of the layers as they appear in the file - * @return A map where each key is a layer name and the value is another map of key-value pairs for that layer - */ - static std::map> parseTEnvConfiguration(std::string& filename, std::vector& layers); - - /** - * @brief Accesses a file given its path, which can be either a local path or a ccdb path (starting with "ccdb:"). In the first case it returns the local path, in the second it retrieves the file from ccdb and returns the local path to the retrieved file. - * @param path The path to the file, either local or ccdb (starting with "ccdb:") - * @param downloadPath The local path where to download the file if it's a ccdb path. Default is "/tmp/GeometryContainer/" - * @param ccdb Pointer to the CCDB manager to use for retrieving the file if it's a ccdb path. If nullptr, the function will create a temporary CCDB manager instance. Default is nullptr. - * @param timeoutSeconds If positive, then this function will wait for these seconds after download before removing the downloaded file. - * @return The local path to the file, either the original local path or the path to the retrieved file from ccdb - */ - static std::string accessFile(const std::string& path, const std::string downloadPath = "/tmp/GeometryContainer/", o2::ccdb::BasicCCDBManager* ccdb = nullptr, int timeoutSeconds = 0); - - std::map> getConfigurations() const { return mConfigurations; } + const std::map>& getConfigurations() const { return mConfigurations; } std::map getConfiguration(const std::string& layerName) const; - std::vector getLayerNames() const { return mLayerNames; } + const std::vector& getLayerNames() const { return mLayerNames; } bool hasValue(const std::string& layerName, const std::string& key) const; std::string getValue(const std::string& layerName, const std::string& key, bool require = true) const; void setValue(const std::string& layerName, const std::string& key, const std::string& value) { mConfigurations[layerName][key] = value; } diff --git a/ALICE3/Core/OTFParticle.h b/ALICE3/Core/OTFParticle.h new file mode 100644 index 00000000000..f96e1e16df0 --- /dev/null +++ b/ALICE3/Core/OTFParticle.h @@ -0,0 +1,196 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file OTFParticle.h +/// \brief Basic class to hold information regarding a mc particle to be used in fast simulation +/// \author Jesper Karlsson Gumprecht +/// + +#ifndef ALICE3_CORE_OTFPARTICLE_H_ +#define ALICE3_CORE_OTFPARTICLE_H_ + +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::upgrade +{ + +enum class DecayerBits { ProducedByDecayer = 0, + IsPrimary, + IsAlive }; + +class OTFParticle +{ + public: + OTFParticle() = default; + + template + explicit OTFParticle(const TParticle& particle) : mPdgCode(particle.pdgCode()), + mGlobalIndex(particle.globalIndex()), + mCollisionId(particle.mcCollisionId()), + mVx(particle.vx()), + mVy(particle.vy()), + mVz(particle.vz()), + mVt(particle.vt()), + mPx(particle.px()), + mPy(particle.py()), + mPz(particle.pz()), + mE(particle.e()), + mStatusCode(particle.statusCode()), + mFlag(particle.flags()) + { + setBitOff(DecayerBits::ProducedByDecayer); + if (particle.has_mothers()) { + mIndicesMother = {particle.mothersIds().front(), particle.mothersIds().back()}; + } + if (particle.has_daughters()) { + mIndicesDaughter = {particle.daughtersIds().front(), particle.daughtersIds().back()}; + } + if constexpr (requires { particle.decayerBits(); }) { + mBits = particle.decayerBits(); + } else { + // If we are here, we created particle in the standard workflow -- without secondaries + // Then we should set all particles as physical primaries accordingly + setBitOn(DecayerBits::IsPrimary); + } + } + + // Setters + void setCollisionId(const int collisionId) { mCollisionId = collisionId; } + void setPDG(const int pdg) { mPdgCode = pdg; } + void setIndicesMother(const int start, const int stop) { mIndicesMother = {start, stop}; } + void setIndicesDaughter(const int start, const int stop) { mIndicesDaughter = {start, stop}; } + void setProductionTime(const float vt) { mVt = vt; } + void setFlags(uint8_t flag) { mFlag = flag; } + void setDecayRadius(const float decayRadius) { mDecayRadius = decayRadius; } + void setVxVyVz(const float vx, const float vy, const float vz) + { + mVx = vx; + mVy = vy; + mVz = vz; + } + void setPxPyPzE(const float px, const float py, const float pz, const float e) + { + mPx = px; + mPy = py; + mPz = pz; + mE = e; + } + void setIndexOffset(const std::size_t offset) + { + static constexpr int NotFound = -1; + mIndicesMother[0] = (mIndicesMother[0] >= 0) ? mIndicesMother[0] + static_cast(offset) : NotFound; + mIndicesMother[1] = (mIndicesMother[1] >= 0) ? mIndicesMother[1] + static_cast(offset) : NotFound; + mIndicesDaughter[0] = (mIndicesDaughter[0] >= 0) ? mIndicesDaughter[0] + static_cast(offset) : NotFound; + mIndicesDaughter[1] = (mIndicesDaughter[1] >= 0) ? mIndicesDaughter[1] + static_cast(offset) : NotFound; + } + + // Getters + [[nodiscard]] int pdgCode() const { return mPdgCode; } + [[nodiscard]] int globalIndex() const { return mGlobalIndex; } + [[nodiscard]] int collisionId() const { return mCollisionId; } + [[nodiscard]] bool isAlive() const { return checkBit(DecayerBits::IsAlive); } + [[nodiscard]] bool isPrimary() const { return checkBit(DecayerBits::IsPrimary); } + [[nodiscard]] bool isFromMcParticles() const { return !checkBit(DecayerBits::ProducedByDecayer); } + [[nodiscard]] float weight() const + { + static constexpr float Weight = 1.f; + return Weight; + } + [[nodiscard]] uint8_t flags() const { return mFlag; } + [[nodiscard]] int statusCode() const { return mStatusCode; } + [[nodiscard]] float vx() const { return mVx; } + [[nodiscard]] float vy() const { return mVy; } + [[nodiscard]] float vz() const { return mVz; } + [[nodiscard]] float vt() const { return mVt; } + [[nodiscard]] float px() const { return mPx; } + [[nodiscard]] float py() const { return mPy; } + [[nodiscard]] float pz() const { return mPz; } + [[nodiscard]] float e() const { return mE; } + [[nodiscard]] float radius() const { return std::hypot(mVx, mVy); } + [[nodiscard]] float decayRadius() const { return mDecayRadius; } + [[nodiscard]] float r() const { return radius(); } + [[nodiscard]] float pt() const { return std::hypot(mPx, mPy); } + [[nodiscard]] float p() const { return std::hypot(mPx, mPy, mPz); } + [[nodiscard]] float phi() const { return o2::constants::math::PI + std::atan2(-1.0f * py(), -1.0f * px()); } + [[nodiscard]] float eta() const + { + // Conditionally defined to avoid FPEs + // As https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1959 + static constexpr float Tolerance = 1e-7f; + if ((p() - mPz) < Tolerance) { + return (mPz < 0.0f) ? -100.0f : 100.0f; + } + return 0.5f * std::log((p() + mPz) / (p() - mPz)); + } + [[nodiscard]] float y() const + { + // Conditionally defined to avoid FPEs + // As https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1980 + static constexpr float Tolerance = 1e-7f; + if ((e() - mPz) < Tolerance) { + return (mPz < 0.0f) ? -100.0f : 100.0f; + } + return 0.5f * std::log((mE + mPz) / (mE - mPz)); + } + [[nodiscard]] int getMotherIndexStart() const { return mIndicesMother[0]; } + [[nodiscard]] int getMotherIndexStop() const { return mIndicesMother[1]; } + [[nodiscard]] int getDaughterIndexStart() const { return mIndicesDaughter[0]; } + [[nodiscard]] int getDaughterIndexStop() const { return mIndicesDaughter[1]; } + [[nodiscard]] const std::array& getMothers() const { return mIndicesMother; } + [[nodiscard]] const std::array& getDaughters() const { return mIndicesDaughter; } + [[nodiscard]] std::span getMotherSpan() const { return hasMothers() ? std::span(mIndicesMother.data(), 2) : std::span(); } + + // Checks + [[nodiscard]] bool hasDaughters() const { return (mIndicesDaughter[0] >= 0); } + [[nodiscard]] bool hasMothers() const { return (mIndicesMother[0] >= 0); } + [[nodiscard]] bool hasNaN() const + { + return std::isnan(mPx) || std::isnan(mPy) || std::isnan(mPz) || std::isnan(mE) || + std::isnan(mVx) || std::isnan(mVy) || std::isnan(mVz); + } + [[nodiscard]] bool hasIndex() const + { + return (mGlobalIndex != -1); + } + + // Bits + [[nodiscard]] bool checkBit(DecayerBits bit) const { return mBits.test(static_cast(bit)); } + void setBit(DecayerBits bit, bool value = true) { mBits.set(static_cast(bit), value); } + void setBitOn(DecayerBits bit) { mBits.set(static_cast(bit), true); } + void setBitOff(DecayerBits bit) { mBits.set(static_cast(bit), false); } + + [[nodiscard]] const std::bitset<8>& getBits() const { return mBits; } + [[nodiscard]] uint8_t getBitsValue() const { return static_cast(mBits.to_ulong()); } + void setBits(std::bitset<8> bits) { mBits = bits; } + + private: + int mPdgCode{}, mGlobalIndex{-1}; + int mCollisionId{-1}; + float mVx{}, mVy{}, mVz{}, mVt{}; + float mPx{}, mPy{}, mPz{}, mE{}; + float mDecayRadius{-1}; + + int mStatusCode{}; + uint8_t mFlag{}; + std::bitset<8> mBits; + std::array mIndicesMother{-1, -1}, mIndicesDaughter{-1, -1}; +}; + +} // namespace o2::upgrade +#endif // ALICE3_CORE_OTFPARTICLE_H_ diff --git a/ALICE3/Core/TOFResoALICE3.cxx b/ALICE3/Core/TOFResoALICE3.cxx deleted file mode 100644 index 6d3bdd40383..00000000000 --- a/ALICE3/Core/TOFResoALICE3.cxx +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file TOFResoALICE3.h -/// \author Nicolo' Jacazio -/// \since 11/03/2021 -/// \brief Implementation for the TOF PID response of the expected times resolution -/// - -#include "ALICE3/Core/TOFResoALICE3.h" - -#include - -#include - -namespace o2::pid::tof -{ - -float TOFResoALICE3Param(const float& momentum, const float& momentumError, const float& evtimereso, const float& length, const float& mass, const Parameters& parameters) -{ - if (momentum <= 0) { - return -999.f; - } - - const float p2 = momentum * momentum; - const float Lc = length / 0.0299792458f; - const float mass2 = mass * mass; - const float ep = momentumError * momentum; - // const float ep = momentumError * p2; - const float etexp = Lc * mass2 / p2 / sqrt(mass2 + p2) * ep; - return sqrt(etexp * etexp + parameters[0] * parameters[0] + evtimereso * evtimereso); -} - -} // namespace o2::pid::tof diff --git a/ALICE3/Core/TOFResoALICE3.h b/ALICE3/Core/TOFResoALICE3.h deleted file mode 100644 index e392d120ecf..00000000000 --- a/ALICE3/Core/TOFResoALICE3.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file TOFResoALICE3.h -/// \author Nicolo' Jacazio -/// \since 11/03/2021 -/// \brief Implementation for the TOF PID response of the expected times resolution -/// - -#ifndef ALICE3_CORE_TOFRESOALICE3_H_ -#define ALICE3_CORE_TOFRESOALICE3_H_ - -#include -#include - -#include - -#include -#include - -#include - -namespace o2::pid::tof -{ - -class TOFResoALICE3 : public Parametrization -{ - public: - TOFResoALICE3() : Parametrization("TOFResoALICE3", 1){}; - ~TOFResoALICE3() override = default; - float operator()(const float* x) const override - { - const float p = abs(x[0]); - if (p <= 0) { - return -999; - } - - /** get info **/ - // const float time = x[1]; - const float evtimereso = x[2]; - const float mass = x[3]; - const float L = x[4]; - const float p2 = p * p; - // const float ep = x[5] * x[6]; - const float ep = x[5] * p2; - const float Lc = L / 0.0299792458f; - // const float Lc = L / 29.9792458f; - - const float mass2 = mass * mass; - const float etexp = Lc * mass2 / p2 / sqrt(mass2 + p2) * ep; - return sqrt(etexp * etexp + mParameters[0] * mParameters[0] + evtimereso * evtimereso); - } - ClassDef(TOFResoALICE3, 1); -}; - -float TOFResoALICE3Param(const float& momentum, const float& momentumError, const float& evtimereso, const float& length, const float& mass, const Parameters& parameters); - -template -float TOFResoALICE3ParamTrack(const T& track, const Parameters& parameters) -{ - const float BETA = tan(0.25f * static_cast(M_PI) - 0.5f * atan(track.tgl())); - const float sigmaP = sqrt(track.pt() * track.pt() * track.sigma1Pt() * track.sigma1Pt() + (BETA * BETA - 1.f) / (BETA * (BETA * BETA + 1.f)) * (track.tgl() / sqrt(track.tgl() * track.tgl() + 1.f) - 1.f) * track.sigmaTgl() * track.sigmaTgl()); - // const float sigmaP = std::sqrt( track.getSigma1Pt2() ) * track.pt(); - return TOFResoALICE3Param(track.p(), sigmaP, track.collision().collisionTimeRes() * 1000.f, track.length(), o2::track::pid_constants::sMasses2Z[id], parameters); - // return TOFResoALICE3Param(track.p(), track.sigma1Pt(), collision.collisionTimeRes() * 1000.f, track.length(), o2::track::pid_constants::sMasses[id], parameters); -} - -} // namespace o2::pid::tof - -#endif // ALICE3_CORE_TOFRESOALICE3_H_ diff --git a/ALICE3/Core/TrackUtilities.cxx b/ALICE3/Core/TrackUtilities.cxx index 739e1df28ae..0b4d1dc08f7 100644 --- a/ALICE3/Core/TrackUtilities.cxx +++ b/ALICE3/Core/TrackUtilities.cxx @@ -17,6 +17,8 @@ #include "TrackUtilities.h" +#include +#include #include #include @@ -27,13 +29,13 @@ #include void o2::upgrade::convertTLorentzVectorToO2Track(const int charge, - const TLorentzVector particle, - const std::vector productionVertex, + const TLorentzVector& particle, + const std::vector& productionVertex, o2::track::TrackParCov& o2track) { - std::array params; + std::array params = {0.}; std::array covm = {0.}; - float s, c, x; + float s{}, c{}, x{}; o2::math_utils::sincos(static_cast(particle.Phi()), s, c); o2::math_utils::rotateZInv(static_cast(productionVertex[0]), static_cast(productionVertex[1]), x, params[0], s, c); params[1] = static_cast(productionVertex[2]); @@ -45,3 +47,69 @@ void o2::upgrade::convertTLorentzVectorToO2Track(const int charge, // Initialize TrackParCov in-place new (&o2track)(o2::track::TrackParCov)(x, particle.Phi(), params, covm); } + +float o2::upgrade::computeParticleVelocity(const float momentum, const float mass) +{ + const float a = momentum / mass; + // uses light speed in cm/ps so output is in those units + return o2::constants::physics::LightSpeedCm2PS * a / std::sqrt((1.f + a * a)); +} + +float o2::upgrade::computeTrackLength(const o2::track::TrackParCov& track, const float radius, const float magneticField) +{ + // don't make use of the track parametrization + float length = -100; + + o2::math_utils::CircleXYf_t trcCircle; + float sna{}, csa{}; + track.getCircleParams(magneticField, trcCircle, sna, csa); + + // distance between circle centers (one circle is at origin -> easy) + const float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); + + // condition of circles touching - if not satisfied returned length will be -100 + if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { + // base radical direction + const float ux = trcCircle.xC / centerDistance; + const float uy = trcCircle.yC / centerDistance; + // calculate perpendicular vector (normalized) for +/- displacement + const float vx = -uy; + const float vy = +ux; + // calculate coordinate for radical line + const float radical = (centerDistance * centerDistance - trcCircle.rC * trcCircle.rC + radius * radius) / (2.0f * centerDistance); + // calculate absolute displacement from center-to-center axis + const float displace = (0.5f / centerDistance) * std::sqrt( + (-centerDistance + trcCircle.rC - radius) * + (-centerDistance - trcCircle.rC + radius) * + (-centerDistance + trcCircle.rC + radius) * + (centerDistance + trcCircle.rC + radius)); + + // possible intercept points of track and TOF layer in 2D plane + const std::array point1 = {radical * ux + displace * vx, radical * uy + displace * vy}; + const std::array point2 = {radical * ux - displace * vx, radical * uy - displace * vy}; + + // decide on correct intercept point + std::array mom{}; + track.getPxPyPzGlo(mom); + const float scalarProduct1 = point1[0] * mom[0] + point1[1] * mom[1]; + const float scalarProduct2 = point2[0] * mom[0] + point2[1] * mom[1]; + + // get start point + std::array startPoint{}; + track.getXYZGlo(startPoint); + + float cosAngle = -1000, modulus = -1000; + + if (scalarProduct1 > scalarProduct2) { + modulus = std::hypot(point1[0] - trcCircle.xC, point1[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); + cosAngle = (point1[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point1[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); + } else { + modulus = std::hypot(point2[0] - trcCircle.xC, point2[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); + cosAngle = (point2[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point2[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); + } + cosAngle /= modulus; + length = trcCircle.rC * std::acos(cosAngle); + length *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); + } + return length; +} diff --git a/ALICE3/Core/TrackUtilities.h b/ALICE3/Core/TrackUtilities.h index bca0b82cf1a..f591cd05d0f 100644 --- a/ALICE3/Core/TrackUtilities.h +++ b/ALICE3/Core/TrackUtilities.h @@ -18,79 +18,26 @@ #ifndef ALICE3_CORE_TRACKUTILITIES_H_ #define ALICE3_CORE_TRACKUTILITIES_H_ +#include "ALICE3/Core/OTFParticle.h" + #include #include +#include #include namespace o2::upgrade { -/// Struct to store mc info for the otf decayer -struct OTFParticle { - OTFParticle() = default; - - template - explicit OTFParticle(const TParticle& particle) - { - mPdgCode = particle.pdgCode(); - mPx = particle.px(); - mPy = particle.py(); - mPz = particle.pz(); - mE = particle.e(); - mVx = particle.vx(); - mVy = particle.vy(); - mVz = particle.vz(); - } - - // Setters - void setIsAlive(bool isAlive) { mIsAlive = isAlive; } - void setPDG(int pdg) { mPdgCode = pdg; } - void setVxVyVz(float vx, float vy, float vz) - { - mVx = vx; - mVy = vy; - mVz = vz; - } - void setPxPyPzE(float px, float py, float pz, float e) - { - mPx = px; - mPy = py; - mPz = pz; - mE = e; - } - - // Getters - int pdgCode() const { return mPdgCode; } - bool isAlive() const { return mIsAlive; } - float vx() const { return mVx; } - float vy() const { return mVy; } - float vz() const { return mVz; } - float px() const { return mPx; } - float py() const { return mPy; } - float pz() const { return mPz; } - float e() const { return mE; } - float radius() const { return std::hypot(mVx, mVy); } - float pt() const { return std::hypot(mPx, mPy); } - float p() const { return std::hypot(mPx, mPy, mPz); } - - private: - int mPdgCode{}; - float mE{}; - float mVx{}, mVy{}, mVz{}; - float mPx{}, mPy{}, mPz{}; - bool mIsAlive{}; -}; - /// Function to convert a TLorentzVector into a perfect Track /// \param charge particle charge (integer) /// \param particle the particle to convert (TLorentzVector) /// \param productionVertex where the particle was produced /// \param o2track the address of the resulting TrackParCov void convertTLorentzVectorToO2Track(const int charge, - const TLorentzVector particle, - const std::vector productionVertex, + const TLorentzVector& particle, + const std::vector& productionVertex, o2::track::TrackParCov& o2track); /// Function to convert a TLorentzVector into a perfect Track @@ -100,9 +47,9 @@ void convertTLorentzVectorToO2Track(const int charge, /// \param o2track the address of the resulting TrackParCov /// \param pdg the pdg service template -void convertTLorentzVectorToO2Track(int pdgCode, - TLorentzVector particle, - std::vector productionVertex, +void convertTLorentzVectorToO2Track(const int pdgCode, + const TLorentzVector& particle, + const std::vector& productionVertex, o2::track::TrackParCov& o2track, const PdgService& pdg) { @@ -133,7 +80,7 @@ void convertOTFParticleToO2Track(const OTFParticle& particle, /// \param o2track the address of the resulting TrackParCov /// \param pdg the pdg service template -void convertMCParticleToO2Track(McParticleType& particle, +void convertMCParticleToO2Track(const McParticleType& particle, o2::track::TrackParCov& o2track, const PdgService& pdg) { @@ -147,7 +94,7 @@ void convertMCParticleToO2Track(McParticleType& particle, /// \param o2track the address of the resulting TrackParCov /// \param pdg the pdg service template -o2::track::TrackParCov convertMCParticleToO2Track(McParticleType& particle, +o2::track::TrackParCov convertMCParticleToO2Track(const McParticleType& particle, const PdgService& pdg) { o2::track::TrackParCov o2track; @@ -155,6 +102,17 @@ o2::track::TrackParCov convertMCParticleToO2Track(McParticleType& particle, return o2track; } +/// returns velocity in centimeters per picoseconds +/// \param momentum the momentum of the track +/// \param mass the mass of the particle +float computeParticleVelocity(const float momentum, const float mass); + +/// function to calculate track length of this track up to a certain radius +/// \param track the input track (TrackParCov) +/// \param radius the radius of the layer you're calculating the length to +/// \param magneticField the magnetic field to use when propagating +float computeTrackLength(const o2::track::TrackParCov& track, const float radius, const float magneticField); + } // namespace o2::upgrade #endif // ALICE3_CORE_TRACKUTILITIES_H_ diff --git a/ALICE3/DataModel/ECAL.h b/ALICE3/DataModel/ECAL.h deleted file mode 100644 index 9ef1e8a3391..00000000000 --- a/ALICE3/DataModel/ECAL.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file ECAL.h -/// \author Nicolo' Jacazio -/// \since 14/09/2021 -/// \brief Set of tables for the ALICE3 ECAL information -/// - -#ifndef ALICE3_DATAMODEL_ECAL_H_ -#define ALICE3_DATAMODEL_ECAL_H_ - -#include - -namespace o2::aod -{ -namespace alice3ecal -{ -DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Index to travel from ECAL to collision -DECLARE_SOA_INDEX_COLUMN(Track, track); //! Index to travel from ECAL to track -DECLARE_SOA_INDEX_COLUMN(McParticle, mcparticle); //! Index to travel from ECAL to particle -DECLARE_SOA_COLUMN(E, e, double); //! Signal in ECAL -DECLARE_SOA_COLUMN(Px, px, double); //! Px -DECLARE_SOA_COLUMN(Py, py, double); //! Py -DECLARE_SOA_COLUMN(Pz, pz, double); //! Pz -DECLARE_SOA_COLUMN(PosZ, posZ, float); //! Position in Z -DECLARE_SOA_COLUMN(PosPhi, posPhi, float); //! Position in phi -} // namespace alice3ecal - -DECLARE_SOA_TABLE(ECALs, "AOD", "A3ECAL", //! Table for the ALICE3 ECAL detector - o2::soa::Index<>, - alice3ecal::CollisionId, - alice3ecal::TrackId, - alice3ecal::McParticleId, - alice3ecal::E, - alice3ecal::Px, - alice3ecal::Py, - alice3ecal::Pz, - alice3ecal::PosPhi, - alice3ecal::PosZ); - -using ECAL = ECALs::iterator; - -} // namespace o2::aod - -#endif // ALICE3_DATAMODEL_ECAL_H_ diff --git a/ALICE3/DataModel/FTOF.h b/ALICE3/DataModel/FTOF.h deleted file mode 100644 index 3d047c21fd5..00000000000 --- a/ALICE3/DataModel/FTOF.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file FTOF.h -/// \author Nicolo' Jacazio -/// \since 27/05/2021 -/// \brief Set of tables for the ALICE3 FTOF information -/// - -#ifndef ALICE3_DATAMODEL_FTOF_H_ -#define ALICE3_DATAMODEL_FTOF_H_ - -#include - -namespace o2::aod -{ -namespace alice3ftof -{ -DECLARE_SOA_INDEX_COLUMN(Track, track); //! -DECLARE_SOA_COLUMN(FTOFLength, ftofLength, float); //! -DECLARE_SOA_COLUMN(FTOFSignal, ftofSignal, float); //! -DECLARE_SOA_COLUMN(FTOFDeltaEl, ftofDeltaEl, float); //! -DECLARE_SOA_COLUMN(FTOFDeltaMu, ftofDeltaMu, float); //! -DECLARE_SOA_COLUMN(FTOFDeltaPi, ftofDeltaPi, float); //! -DECLARE_SOA_COLUMN(FTOFDeltaKa, ftofDeltaKa, float); //! -DECLARE_SOA_COLUMN(FTOFDeltaPr, ftofDeltaPr, float); //! -DECLARE_SOA_COLUMN(FTOFNsigmaEl, ftofNsigmaEl, float); //! -DECLARE_SOA_COLUMN(FTOFNsigmaMu, ftofNsigmaMu, float); //! -DECLARE_SOA_COLUMN(FTOFNsigmaPi, ftofNsigmaPi, float); //! -DECLARE_SOA_COLUMN(FTOFNsigmaKa, ftofNsigmaKa, float); //! -DECLARE_SOA_COLUMN(FTOFNsigmaPr, ftofNsigmaPr, float); //! -} // namespace alice3ftof - -DECLARE_SOA_TABLE(FTOFs, "AOD", "FTOF", //! - o2::soa::Index<>, - alice3ftof::TrackId, - alice3ftof::FTOFLength, - alice3ftof::FTOFSignal, - alice3ftof::FTOFDeltaEl, - alice3ftof::FTOFDeltaMu, - alice3ftof::FTOFDeltaPi, - alice3ftof::FTOFDeltaKa, - alice3ftof::FTOFDeltaPr, - alice3ftof::FTOFNsigmaEl, - alice3ftof::FTOFNsigmaMu, - alice3ftof::FTOFNsigmaPi, - alice3ftof::FTOFNsigmaKa, - alice3ftof::FTOFNsigmaPr); - -using FTOF = FTOFs::iterator; - -} // namespace o2::aod - -#endif // ALICE3_DATAMODEL_FTOF_H_ diff --git a/ALICE3/DataModel/MID.h b/ALICE3/DataModel/MID.h deleted file mode 100644 index 7357e689eda..00000000000 --- a/ALICE3/DataModel/MID.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file MID.h -/// \author Vít Kučera -/// \note Based on RICH.h -/// \brief Set of tables for the ALICE3 MID information -/// - -#ifndef ALICE3_DATAMODEL_MID_H_ -#define ALICE3_DATAMODEL_MID_H_ - -#include - -#include - -namespace o2::aod -{ -namespace alice3mid -{ -DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! -DECLARE_SOA_INDEX_COLUMN(Track, track); //! -DECLARE_SOA_COLUMN(MIDIsMuon, midIsMuon, uint8_t); //! FIXME: To be changed to bool once bool columns are groupable. -} // namespace alice3mid - -DECLARE_SOA_TABLE(MIDs, "AOD", "MID", //! - o2::soa::Index<>, - alice3mid::CollisionId, - alice3mid::TrackId, - alice3mid::MIDIsMuon); - -using MID = MIDs::iterator; - -} // namespace o2::aod - -#endif // ALICE3_DATAMODEL_MID_H_ diff --git a/ALICE3/DataModel/OTFMCParticle.h b/ALICE3/DataModel/OTFMCParticle.h deleted file mode 100644 index 56a1400a2e9..00000000000 --- a/ALICE3/DataModel/OTFMCParticle.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file OTFMCParticle.h -/// \author Jesper Karlsson Gumprecht -/// \since 16/12/2025 -/// \brief Redefinition of the mcparticles table specifically for the fast sim -/// - -#ifndef ALICE3_DATAMODEL_OTFMCPARTICLE_H_ -#define ALICE3_DATAMODEL_OTFMCPARTICLE_H_ - -#include - -#include - -namespace o2::aod -{ - -namespace otfmcparticle -{ -DECLARE_SOA_COLUMN(Phi, phi, float); -DECLARE_SOA_COLUMN(Eta, eta, float); -DECLARE_SOA_COLUMN(Pt, pt, float); -DECLARE_SOA_COLUMN(P, p, float); -DECLARE_SOA_COLUMN(Y, y, float); -DECLARE_SOA_COLUMN(IsAlive, isAlive, bool); -DECLARE_SOA_COLUMN(IsPrimary, isPrimary, bool); - -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Mother0, mother0, int, "McPartsWithDau_Mother0"); //! Track index of the first mother -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Mother1, mother1, int, "McPartsWithDau_Mother1"); //! Track index of the last mother -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Daughter0, daughter0, int, "McPartsWithDau_Daughter0"); //! Track index of the first daugther -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Daughter1, daughter1, int, "McPartsWithDau_Daughter1"); //! Track index of the last daugther -DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(Mothers, mothers); //! Mother tracks (possible empty) array. Iterate over mcParticle.mothers_as()) -DECLARE_SOA_SELF_SLICE_INDEX_COLUMN(Daughters, daughters); //! Daughter tracks (possibly empty) slice. Check for non-zero with mcParticle.has_daughters(). Iterate over mcParticle.daughters_as()) -} // namespace otfmcparticle - -DECLARE_SOA_TABLE_FULL(McPartWithDaus, "McPartWithDaus", "AOD", "MCPARTSWITHDAU", - o2::soa::Index<>, - mcparticle::McCollisionId, - mcparticle::PdgCode, - mcparticle::StatusCode, - mcparticle::Flags, - otfmcparticle::MothersIds, - otfmcparticle::DaughtersIdSlice, - mcparticle::Weight, - mcparticle::Px, - mcparticle::Py, - mcparticle::Pz, - mcparticle::E, - mcparticle::Vx, - mcparticle::Vy, - mcparticle::Vz, - mcparticle::Vt, - otfmcparticle::Phi, - otfmcparticle::Eta, - otfmcparticle::Pt, - otfmcparticle::P, - otfmcparticle::Y, - otfmcparticle::IsAlive, - otfmcparticle::IsPrimary, - mcparticle::PVector, - mcparticle::ProducedByGenerator, - mcparticle::FromBackgroundEvent, - mcparticle::GetGenStatusCode, - mcparticle::GetHepMCStatusCode, - mcparticle::GetProcess, - mcparticle::IsPhysicalPrimary); - -using McPartWithDau = McPartWithDaus::iterator; - -namespace otfmctracklable -{ -DECLARE_SOA_INDEX_COLUMN(McPartWithDau, mcPartWithDau); //! MC particle -DECLARE_SOA_COLUMN(McMask, mcMask, uint16_t); //! Bit mask to indicate detector mismatches (bit ON means mismatch). Bit 0-6: mismatch at ITS layer. Bit 12: ITSAB tracklet mismatch. Bit 13: ITS-TPC mismatch. Bit 14: isNoise == True (global track), Bit 15: isFake == True (global track) -} // namespace otfmctracklable - -DECLARE_SOA_TABLE(McTrackWithDauLabels, "AOD", "MCTRACKWithDAULABEL", //! Table joined to the track table containing the MC index - otfmctracklable::McPartWithDauId, otfmctracklable::McMask); - -using McTrackWithDauLabel = McTrackWithDauLabels::iterator; - -} // namespace o2::aod - -#endif // ALICE3_DATAMODEL_OTFMCPARTICLE_H_ diff --git a/ALICE3/DataModel/OTFMID.h b/ALICE3/DataModel/OTFMID.h new file mode 100644 index 00000000000..e188f75fd14 --- /dev/null +++ b/ALICE3/DataModel/OTFMID.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file OTFMID.h +/// \author David Dobrigkeit Chinellato +/// \author Antonio Ortiz, UNAM +/// \since 11/05/2023 +/// \brief Set of tables for the ALICE3 OTFMID information +/// TODO: remove the columns that are not usefulf for me + +#ifndef ALICE3_DATAMODEL_OTFMID_H_ +#define ALICE3_DATAMODEL_OTFMID_H_ + +#include + +#include +#include + +namespace o2::aod +{ +namespace upgrade_mid +{ +DECLARE_SOA_COLUMN(MIDTrackTime, midTrackTime, float); //! Track time generated at the MID +DECLARE_SOA_COLUMN(MIDTrackLength, midTrackLength, float); //! track length for calculation of MID (generated) + +DECLARE_SOA_COLUMN(MIDEventTime, midEventTime, float); //! Event time reconstructed with the MID +DECLARE_SOA_COLUMN(MIDEventTimeErr, midEventTimeErr, float); //! Uncertainty on the event time reconstructed with the MID +DECLARE_SOA_COLUMN(MIDTrackPosReco, midTrackPosReco, std::vector); //! Measured position in MID +DECLARE_SOA_COLUMN(MIDTrackPos, midTrackPos, std::vector); //! Expected position in MID +DECLARE_SOA_COLUMN(MIDCandidate, midCandidate, bool); //! Muon candidate +DECLARE_SOA_COLUMN(MIDTrackTimeReco, midTrackTimeReco, float); //! Track time measured at MID +DECLARE_SOA_COLUMN(MIDTrackLengthReco, midTrackLengthReco, float); //! track length for calculation of MID (reconstructed) + +} // namespace upgrade_mid + +DECLARE_SOA_TABLE(UpgradeMidMCs, "AOD", "UPGRADEMIDMC", + upgrade_mid::MIDTrackTime, + upgrade_mid::MIDTrackLength); + +DECLARE_SOA_TABLE(UpgradeMids, "AOD", "UPGRADEMID", + upgrade_mid::MIDEventTime, + upgrade_mid::MIDEventTimeErr, + upgrade_mid::MIDTrackPosReco, + upgrade_mid::MIDTrackTimeReco, + upgrade_mid::MIDTrackLengthReco, + upgrade_mid::MIDCandidate); + +DECLARE_SOA_TABLE(UpgradeMidExpecteds, "AOD", "UPGRADEMIDEXPT", + upgrade_mid::MIDTrackPos, + upgrade_mid::MIDTrackTime, + upgrade_mid::MIDTrackLength); + +using UpgradeMidMC = UpgradeMidMCs::iterator; +using UpgradeMid = UpgradeMids::iterator; +using UpgradeMidExpected = UpgradeMidExpecteds::iterator; + +} // namespace o2::aod + +#endif // ALICE3_DATAMODEL_OTFMID_H_ diff --git a/ALICE3/DataModel/OTFMulticharm.h b/ALICE3/DataModel/OTFMulticharm.h index 643d36c0adb..74d1afc3888 100644 --- a/ALICE3/DataModel/OTFMulticharm.h +++ b/ALICE3/DataModel/OTFMulticharm.h @@ -35,13 +35,15 @@ DECLARE_SOA_INDEX_COLUMN_FULL(XiCCPion, xiCCPion, int, Tracks, "_PiXiCC"); DECLARE_SOA_COLUMN(XicMass, xicMass, float); DECLARE_SOA_COLUMN(XiccMass, xiccMass, float); -DECLARE_SOA_COLUMN(LUTConfigId, lutConfigId, int); //! Index for LUT configuration +DECLARE_SOA_COLUMN(LutConfigId, lutConfigId, int); //! Index for LUT configuration // kine vars DECLARE_SOA_COLUMN(XiccPt, xiccPt, float); DECLARE_SOA_COLUMN(XiccEta, xiccEta, float); DECLARE_SOA_COLUMN(XicPt, xicPt, float); DECLARE_SOA_COLUMN(XicEta, xicEta, float); +DECLARE_SOA_COLUMN(XiPt, xiPt, float); +DECLARE_SOA_COLUMN(XiEta, xiEta, float); // topo vars DECLARE_SOA_COLUMN(XiDCAz, xiDCAz, float); @@ -53,13 +55,6 @@ DECLARE_SOA_COLUMN(XiccDauDCA, xiccDauDCA, float); DECLARE_SOA_COLUMN(XiccDCAxy, xiccDCAxy, float); DECLARE_SOA_COLUMN(XiccDCAz, xiccDCAz, float); -DECLARE_SOA_COLUMN(BachDCAxy, bachDCAxy, float); -DECLARE_SOA_COLUMN(BachDCAz, bachDCAz, float); -DECLARE_SOA_COLUMN(PosDCAxy, posDCAxy, float); -DECLARE_SOA_COLUMN(PosDCAz, posDCAz, float); -DECLARE_SOA_COLUMN(NegDCAxy, negDCAxy, float); -DECLARE_SOA_COLUMN(NegDCAz, negDCAz, float); - DECLARE_SOA_COLUMN(Pi1cDCAxy, pi1cDCAxy, float); DECLARE_SOA_COLUMN(Pi1cDCAz, pi1cDCAz, float); DECLARE_SOA_COLUMN(Pi2cDCAxy, pi2cDCAxy, float); @@ -75,19 +70,9 @@ DECLARE_SOA_COLUMN(XicDistanceFromPV, xicDistanceFromPV, float); DECLARE_SOA_COLUMN(XiccProperLength, xiccProperLength, float); // Daughter info -DECLARE_SOA_COLUMN(PosPt, posPt, float); -DECLARE_SOA_COLUMN(PosEta, posEta, float); -DECLARE_SOA_COLUMN(NegPt, negPt, float); -DECLARE_SOA_COLUMN(NegEta, negEta, float); -DECLARE_SOA_COLUMN(BachPt, bachPt, float); -DECLARE_SOA_COLUMN(BachEta, bachEta, float); -DECLARE_SOA_COLUMN(BachPhi, bachPhi, float); DECLARE_SOA_COLUMN(Pi1cPt, pi1cPt, float); -DECLARE_SOA_COLUMN(Pi1cEta, pi1cEta, float); DECLARE_SOA_COLUMN(Pi2cPt, pi2cPt, float); -DECLARE_SOA_COLUMN(Pi2cEta, pi2cEta, float); DECLARE_SOA_COLUMN(PiccPt, piccPt, float); -DECLARE_SOA_COLUMN(PiccEta, piccEta, float); } // namespace otfmulticharm @@ -108,7 +93,8 @@ DECLARE_SOA_TABLE(MCharmCores, "AOD", "MCharmCores", otfmulticharm::XicPt, otfmulticharm::XicEta, otfmulticharm::XicDauDCA, - + otfmulticharm::XiPt, + otfmulticharm::XiEta, otfmulticharm::XiDCAxy, otfmulticharm::XiDCAz, otfmulticharm::XicDCAxy, @@ -131,27 +117,7 @@ DECLARE_SOA_TABLE(MCharmCores, "AOD", "MCharmCores", otfmulticharm::Pi1cPt, otfmulticharm::Pi2cPt, otfmulticharm::PiccPt, - otfmulticharm::LUTConfigId); - -DECLARE_SOA_TABLE(MCharmExtra, "AOD", "MCharmExtra", - otfmulticharm::BachPt, - otfmulticharm::BachEta, - otfmulticharm::BachDCAxy, - otfmulticharm::BachDCAz, - - otfmulticharm::PosPt, - otfmulticharm::PosEta, - otfmulticharm::PosDCAxy, - otfmulticharm::PosDCAz, - - otfmulticharm::NegPt, - otfmulticharm::NegEta, - otfmulticharm::NegDCAxy, - otfmulticharm::NegDCAz, - - otfmulticharm::Pi1cEta, - otfmulticharm::Pi2cEta, - otfmulticharm::PiccEta); + otfmulticharm::LutConfigId); } // namespace o2::aod diff --git a/ALICE3/DataModel/OTFStrangeness.h b/ALICE3/DataModel/OTFStrangeness.h index 395f1ab8681..2124b258724 100644 --- a/ALICE3/DataModel/OTFStrangeness.h +++ b/ALICE3/DataModel/OTFStrangeness.h @@ -27,6 +27,12 @@ #include #include +namespace o2::upgrade::pid +{ +static constexpr float NoPidSignal = -999.f; +static constexpr float NoPidSignalThreshold = -990.f; +} // namespace o2::upgrade::pid + namespace o2::aod { namespace otfcascade @@ -35,7 +41,9 @@ DECLARE_SOA_INDEX_COLUMN(Collision, collision); DECLARE_SOA_INDEX_COLUMN_FULL(CascadeTrack, cascadeTrack, int, Tracks, "_Cascade"); //! DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, Tracks, "_Pos"); //! DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, Tracks, "_Neg"); //! -DECLARE_SOA_INDEX_COLUMN_FULL(BachTrack, bachTrack, int, Tracks, "_Bach"); //! +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int, Tracks, "_Bach"); //! + +DECLARE_SOA_INDEX_COLUMN(McParticle, mcParticle); // topo vars DECLARE_SOA_COLUMN(DcaV0Daughters, dcaV0Daughters, float); @@ -45,6 +53,8 @@ DECLARE_SOA_COLUMN(CascRadius, cascRadius, float); DECLARE_SOA_COLUMN(CascRadiusMC, cascRadiusMC, float); DECLARE_SOA_COLUMN(MLambda, mLambda, float); DECLARE_SOA_COLUMN(MXi, mXi, float); +DECLARE_SOA_COLUMN(DcaXYCascToPV, dcaXYCascToPV, float); +DECLARE_SOA_COLUMN(DcaZCascToPV, dcaZCascToPV, float); // strangeness tracking DECLARE_SOA_COLUMN(FindableClusters, findableClusters, int); @@ -57,7 +67,7 @@ DECLARE_SOA_TABLE(UpgradeCascades, "AOD", "UPGRADECASCADES", otfcascade::CascadeTrackId, otfcascade::PosTrackId, otfcascade::NegTrackId, - otfcascade::BachTrackId, + otfcascade::BachelorId, otfcascade::DcaV0Daughters, otfcascade::DcaCascadeDaughters, otfcascade::V0Radius, @@ -66,10 +76,232 @@ DECLARE_SOA_TABLE(UpgradeCascades, "AOD", "UPGRADECASCADES", otfcascade::MLambda, otfcascade::MXi, otfcascade::FindableClusters, - otfcascade::FoundClusters); + otfcascade::FoundClusters, + otfcascade::DcaXYCascToPV, + otfcascade::DcaZCascToPV); using UpgradeCascade = UpgradeCascades::iterator; +DECLARE_SOA_TABLE(A3CascadeMcLabels, "AOD", "A3CASCADEMCLABELS", + o2::soa::Index<>, otfcascade::McParticleId); +DECLARE_SOA_TABLE(UpgradeCascadeMcLabels, "AOD", "UPGRADECASCMCLAB", + o2::soa::Index<>, otfcascade::McParticleId); + +namespace casc_pid +{ + +// Xi +DECLARE_SOA_COLUMN(NSigmaInnerTofXiBachPi, nSigmaInnerTofXiBachPi, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofXiPosPr, nSigmaInnerTofXiPosPr, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofXiPosPi, nSigmaInnerTofXiPosPi, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofXiNegPr, nSigmaInnerTofXiNegPr, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofXiNegPi, nSigmaInnerTofXiNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofXiBachPi, hasInnerTofXiBachPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofXiPosPr, hasInnerTofXiPosPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofXiPosPi, hasInnerTofXiPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofXiNegPr, hasInnerTofXiNegPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofXiNegPi, hasInnerTofXiNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(NSigmaOuterTofXiBachPi, nSigmaOuterTofXiBachPi, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofXiPosPr, nSigmaOuterTofXiPosPr, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofXiPosPi, nSigmaOuterTofXiPosPi, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofXiNegPr, nSigmaOuterTofXiNegPr, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofXiNegPi, nSigmaOuterTofXiNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofXiBachPi, hasOuterTofXiBachPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofXiPosPr, hasOuterTofXiPosPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofXiPosPi, hasOuterTofXiPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofXiNegPr, hasOuterTofXiNegPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofXiNegPi, hasOuterTofXiNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(ExpectedInnerTofXiBachPi, expectedInnerTofXiBachPi, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofXiPosPr, expectedInnerTofXiPosPr, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofXiPosPi, expectedInnerTofXiPosPi, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofXiNegPr, expectedInnerTofXiNegPr, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofXiNegPi, expectedInnerTofXiNegPi, float); + +DECLARE_SOA_COLUMN(ExpectedOuterTofXiBachPi, expectedOuterTofXiBachPi, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofXiPosPr, expectedOuterTofXiPosPr, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofXiPosPi, expectedOuterTofXiPosPi, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofXiNegPr, expectedOuterTofXiNegPr, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofXiNegPi, expectedOuterTofXiNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredInnerTofXiBachPi, measuredInnerTofXiBachPi, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofXiPosPr, measuredInnerTofXiPosPr, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofXiPosPi, measuredInnerTofXiPosPi, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofXiNegPr, measuredInnerTofXiNegPr, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofXiNegPi, measuredInnerTofXiNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredOuterTofXiBachPi, measuredOuterTofXiBachPi, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofXiPosPr, measuredOuterTofXiPosPr, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofXiPosPi, measuredOuterTofXiPosPi, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofXiNegPr, measuredOuterTofXiNegPr, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofXiNegPi, measuredOuterTofXiNegPi, float); + +// Omega +DECLARE_SOA_COLUMN(NSigmaInnerTofOmegaBachKa, nSigmaInnerTofOmegaBachKa, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofOmegaPosPr, nSigmaInnerTofOmegaPosPr, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofOmegaPosPi, nSigmaInnerTofOmegaPosPi, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofOmegaNegPr, nSigmaInnerTofOmegaNegPr, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofOmegaNegPi, nSigmaInnerTofOmegaNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofOmegaBachKa, hasInnerTofOmegaBachKa, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofOmegaPosPr, hasInnerTofOmegaPosPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofOmegaPosPi, hasInnerTofOmegaPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofOmegaNegPr, hasInnerTofOmegaNegPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofOmegaNegPi, hasInnerTofOmegaNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(NSigmaOuterTofOmegaBachKa, nSigmaOuterTofOmegaBachKa, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofOmegaPosPr, nSigmaOuterTofOmegaPosPr, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofOmegaPosPi, nSigmaOuterTofOmegaPosPi, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofOmegaNegPr, nSigmaOuterTofOmegaNegPr, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofOmegaNegPi, nSigmaOuterTofOmegaNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofOmegaBachKa, hasOuterTofOmegaBachKa, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofOmegaPosPr, hasOuterTofOmegaPosPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofOmegaPosPi, hasOuterTofOmegaPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofOmegaNegPr, hasOuterTofOmegaNegPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofOmegaNegPi, hasOuterTofOmegaNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(ExpectedInnerTofOmegaBachKa, expectedInnerTofOmegaBachKa, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofOmegaPosPr, expectedInnerTofOmegaPosPr, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofOmegaPosPi, expectedInnerTofOmegaPosPi, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofOmegaNegPr, expectedInnerTofOmegaNegPr, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofOmegaNegPi, expectedInnerTofOmegaNegPi, float); + +DECLARE_SOA_COLUMN(ExpectedOuterTofOmegaBachKa, expectedOuterTofOmegaBachKa, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofOmegaPosPr, expectedOuterTofOmegaPosPr, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofOmegaPosPi, expectedOuterTofOmegaPosPi, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofOmegaNegPr, expectedOuterTofOmegaNegPr, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofOmegaNegPi, expectedOuterTofOmegaNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredInnerTofOmegaBachKa, measuredInnerTofOmegaBachKa, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofOmegaPosPr, measuredInnerTofOmegaPosPr, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofOmegaPosPi, measuredInnerTofOmegaPosPi, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofOmegaNegPr, measuredInnerTofOmegaNegPr, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofOmegaNegPi, measuredInnerTofOmegaNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredOuterTofOmegaBachKa, measuredOuterTofOmegaBachKa, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofOmegaPosPr, measuredOuterTofOmegaPosPr, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofOmegaPosPi, measuredOuterTofOmegaPosPi, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofOmegaNegPr, measuredOuterTofOmegaNegPr, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofOmegaNegPi, measuredOuterTofOmegaNegPi, float); + +} // namespace casc_pid + +DECLARE_SOA_TABLE(A3XiInnerTofPid, "AOD", "A3XIITOFPID", + casc_pid::NSigmaInnerTofXiBachPi, + casc_pid::NSigmaInnerTofXiPosPr, + casc_pid::NSigmaInnerTofXiPosPi, + casc_pid::NSigmaInnerTofXiNegPr, + casc_pid::NSigmaInnerTofXiNegPi, + casc_pid::HasInnerTofXiBachPi, + casc_pid::HasInnerTofXiPosPr, + casc_pid::HasInnerTofXiPosPi, + casc_pid::HasInnerTofXiNegPr, + casc_pid::HasInnerTofXiNegPi); + +DECLARE_SOA_TABLE(A3XiOuterTofPid, "AOD", "A3XIOTOFPID", + casc_pid::NSigmaOuterTofXiBachPi, + casc_pid::NSigmaOuterTofXiPosPr, + casc_pid::NSigmaOuterTofXiPosPi, + casc_pid::NSigmaOuterTofXiNegPr, + casc_pid::NSigmaOuterTofXiNegPi, + casc_pid::HasOuterTofXiBachPi, + casc_pid::HasOuterTofXiPosPr, + casc_pid::HasOuterTofXiPosPi, + casc_pid::HasOuterTofXiNegPr, + casc_pid::HasOuterTofXiNegPi); + +DECLARE_SOA_TABLE(A3OmegaInnerTofPid, "AOD", "A3OMITOFPID", + casc_pid::NSigmaInnerTofOmegaBachKa, + casc_pid::NSigmaInnerTofOmegaPosPr, + casc_pid::NSigmaInnerTofOmegaPosPi, + casc_pid::NSigmaInnerTofOmegaNegPr, + casc_pid::NSigmaInnerTofOmegaNegPi, + casc_pid::HasInnerTofOmegaBachKa, + casc_pid::HasInnerTofOmegaPosPr, + casc_pid::HasInnerTofOmegaPosPi, + casc_pid::HasInnerTofOmegaNegPr, + casc_pid::HasInnerTofOmegaNegPi); + +DECLARE_SOA_TABLE(A3OmegaOuterTofPid, "AOD", "A3OMOTOFPID", + casc_pid::NSigmaOuterTofOmegaBachKa, + casc_pid::NSigmaOuterTofOmegaPosPr, + casc_pid::NSigmaOuterTofOmegaPosPi, + casc_pid::NSigmaOuterTofOmegaNegPr, + casc_pid::NSigmaOuterTofOmegaNegPi, + casc_pid::HasOuterTofOmegaBachKa, + casc_pid::HasOuterTofOmegaPosPr, + casc_pid::HasOuterTofOmegaPosPi, + casc_pid::HasOuterTofOmegaNegPr, + casc_pid::HasOuterTofOmegaNegPi); + +DECLARE_SOA_TABLE(A3XiExpectedInnerTimes, "AOD", "A3XIITIMES", + casc_pid::ExpectedInnerTofXiBachPi, + casc_pid::ExpectedInnerTofXiPosPr, + casc_pid::ExpectedInnerTofXiPosPi, + casc_pid::ExpectedInnerTofXiNegPr, + casc_pid::ExpectedInnerTofXiNegPi, + casc_pid::MeasuredInnerTofXiBachPi, + casc_pid::MeasuredInnerTofXiPosPr, + casc_pid::MeasuredInnerTofXiPosPi, + casc_pid::MeasuredInnerTofXiNegPr, + casc_pid::MeasuredInnerTofXiNegPi); + +DECLARE_SOA_TABLE(A3XiExpectedOuterTimes, "AOD", "A3XIOTIMES", + casc_pid::ExpectedOuterTofXiBachPi, + casc_pid::ExpectedOuterTofXiPosPr, + casc_pid::ExpectedOuterTofXiPosPi, + casc_pid::ExpectedOuterTofXiNegPr, + casc_pid::ExpectedOuterTofXiNegPi, + casc_pid::MeasuredOuterTofXiBachPi, + casc_pid::MeasuredOuterTofXiPosPr, + casc_pid::MeasuredOuterTofXiPosPi, + casc_pid::MeasuredOuterTofXiNegPr, + casc_pid::MeasuredOuterTofXiNegPi); + +DECLARE_SOA_TABLE(A3OmegaExpectedInnerTimes, "AOD", "A3OMITIMES", + casc_pid::ExpectedInnerTofOmegaBachKa, + casc_pid::ExpectedInnerTofOmegaPosPr, + casc_pid::ExpectedInnerTofOmegaPosPi, + casc_pid::ExpectedInnerTofOmegaNegPr, + casc_pid::ExpectedInnerTofOmegaNegPi, + casc_pid::MeasuredInnerTofOmegaBachKa, + casc_pid::MeasuredInnerTofOmegaPosPr, + casc_pid::MeasuredInnerTofOmegaPosPi, + casc_pid::MeasuredInnerTofOmegaNegPr, + casc_pid::MeasuredInnerTofOmegaNegPi); + +DECLARE_SOA_TABLE(A3OmegaExpectedOuterTimes, "AOD", "A3OMOTIMES", + casc_pid::ExpectedOuterTofOmegaBachKa, + casc_pid::ExpectedOuterTofOmegaPosPr, + casc_pid::ExpectedOuterTofOmegaPosPi, + casc_pid::ExpectedOuterTofOmegaNegPr, + casc_pid::ExpectedOuterTofOmegaNegPi, + casc_pid::MeasuredOuterTofOmegaBachKa, + casc_pid::MeasuredOuterTofOmegaPosPr, + casc_pid::MeasuredOuterTofOmegaPosPi, + casc_pid::MeasuredOuterTofOmegaNegPr, + casc_pid::MeasuredOuterTofOmegaNegPi); + namespace otfv0 { DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! @@ -82,7 +314,7 @@ DECLARE_SOA_COLUMN(DcaV0Daughters, dcaV0Daughters, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); DECLARE_SOA_COLUMN(MLambda, mLambda, float); DECLARE_SOA_COLUMN(MAntiLambda, mAntiLambda, float); -DECLARE_SOA_COLUMN(MK0, mK0, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); // kinematics DECLARE_SOA_COLUMN(Pt, pt, float); @@ -98,7 +330,7 @@ DECLARE_SOA_TABLE(UpgradeV0s, "AOD", "UPGRADEV0S", otfv0::V0Radius, otfv0::MLambda, otfv0::MAntiLambda, - otfv0::MK0, + otfv0::MK0Short, otfv0::Pt); using UpgradeV0 = UpgradeV0s::iterator; @@ -252,5 +484,148 @@ DECLARE_SOA_TABLE(V0CandidateCores, "AOD", "V0CANDIDATECORE", candidatev0::PositivePhi); using V0CandidateCore = V0CandidateCores::iterator; + +namespace v0_pid +{ + +// K0S +DECLARE_SOA_COLUMN(NSigmaInnerTofK0SPosPi, nSigmaInnerTofK0SPosPi, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofK0SNegPi, nSigmaInnerTofK0SNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofK0SPosPi, hasInnerTofK0SPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofK0SNegPi, hasInnerTofK0SNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(NSigmaOuterTofK0SPosPi, nSigmaOuterTofK0SPosPi, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofK0SNegPi, nSigmaOuterTofK0SNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofK0SPosPi, hasOuterTofK0SPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofK0SNegPi, hasOuterTofK0SNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(ExpectedInnerTofK0SPosPi, expectedInnerTofK0SPosPi, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofK0SNegPi, expectedInnerTofK0SNegPi, float); + +DECLARE_SOA_COLUMN(ExpectedOuterTofK0SPosPi, expectedOuterTofK0SPosPi, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofK0SNegPi, expectedOuterTofK0SNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredInnerTofK0SPosPi, measuredInnerTofK0SPosPi, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofK0SNegPi, measuredInnerTofK0SNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredOuterTofK0SPosPi, measuredOuterTofK0SPosPi, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofK0SNegPi, measuredOuterTofK0SNegPi, float); + +// Lambda +DECLARE_SOA_COLUMN(NSigmaInnerTofLambdaPosPr, nSigmaInnerTofLambdaPosPr, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofLambdaPosPi, nSigmaInnerTofLambdaPosPi, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofLambdaNegPr, nSigmaInnerTofLambdaNegPr, float); +DECLARE_SOA_COLUMN(NSigmaInnerTofLambdaNegPi, nSigmaInnerTofLambdaNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofLambdaPosPr, hasInnerTofLambdaPosPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofLambdaPosPi, hasInnerTofLambdaPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofLambdaNegPr, hasInnerTofLambdaNegPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasInnerTofLambdaNegPi, hasInnerTofLambdaNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(NSigmaOuterTofLambdaPosPr, nSigmaOuterTofLambdaPosPr, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofLambdaPosPi, nSigmaOuterTofLambdaPosPi, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofLambdaNegPr, nSigmaOuterTofLambdaNegPr, float); +DECLARE_SOA_COLUMN(NSigmaOuterTofLambdaNegPi, nSigmaOuterTofLambdaNegPi, float); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofLambdaPosPr, hasOuterTofLambdaPosPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofLambdaPosPi, hasOuterTofLambdaPosPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofLambdaNegPr, hasOuterTofLambdaNegPr, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasOuterTofLambdaNegPi, hasOuterTofLambdaNegPi, + [](float nSigma) -> bool { return (nSigma > o2::upgrade::pid::NoPidSignalThreshold); }); + +DECLARE_SOA_COLUMN(ExpectedInnerTofLambdaPosPr, expectedInnerTofLambdaPosPr, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofLambdaPosPi, expectedInnerTofLambdaPosPi, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofLambdaNegPr, expectedInnerTofLambdaNegPr, float); +DECLARE_SOA_COLUMN(ExpectedInnerTofLambdaNegPi, expectedInnerTofLambdaNegPi, float); + +DECLARE_SOA_COLUMN(ExpectedOuterTofLambdaPosPr, expectedOuterTofLambdaPosPr, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofLambdaPosPi, expectedOuterTofLambdaPosPi, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofLambdaNegPr, expectedOuterTofLambdaNegPr, float); +DECLARE_SOA_COLUMN(ExpectedOuterTofLambdaNegPi, expectedOuterTofLambdaNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredInnerTofLambdaPosPr, measuredInnerTofLambdaPosPr, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofLambdaPosPi, measuredInnerTofLambdaPosPi, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofLambdaNegPr, measuredInnerTofLambdaNegPr, float); +DECLARE_SOA_COLUMN(MeasuredInnerTofLambdaNegPi, measuredInnerTofLambdaNegPi, float); + +DECLARE_SOA_COLUMN(MeasuredOuterTofLambdaPosPr, measuredOuterTofLambdaPosPr, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofLambdaPosPi, measuredOuterTofLambdaPosPi, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofLambdaNegPr, measuredOuterTofLambdaNegPr, float); +DECLARE_SOA_COLUMN(MeasuredOuterTofLambdaNegPi, measuredOuterTofLambdaNegPi, float); +} // namespace v0_pid + +DECLARE_SOA_TABLE(A3K0SInnerTofPid, "AOD", "A3K0SITOFPID", + v0_pid::NSigmaInnerTofK0SPosPi, + v0_pid::NSigmaInnerTofK0SNegPi, + v0_pid::HasInnerTofK0SPosPi, + v0_pid::HasInnerTofK0SNegPi); + +DECLARE_SOA_TABLE(A3K0SOuterTofPid, "AOD", "A3K0SOTOFPID", + v0_pid::NSigmaOuterTofK0SPosPi, + v0_pid::NSigmaOuterTofK0SNegPi, + v0_pid::HasOuterTofK0SPosPi, + v0_pid::HasOuterTofK0SNegPi); + +DECLARE_SOA_TABLE(A3LambdaInnerTofPid, "AOD", "A3LAITOFPID", + v0_pid::NSigmaInnerTofLambdaPosPr, + v0_pid::NSigmaInnerTofLambdaPosPi, + v0_pid::NSigmaInnerTofLambdaNegPr, + v0_pid::NSigmaInnerTofLambdaNegPi, + v0_pid::HasInnerTofLambdaPosPr, + v0_pid::HasInnerTofLambdaPosPi, + v0_pid::HasInnerTofLambdaNegPr, + v0_pid::HasInnerTofLambdaNegPi); + +DECLARE_SOA_TABLE(A3LambdaOuterTofPid, "AOD", "A3LAOTOFPID", + v0_pid::NSigmaOuterTofLambdaPosPr, + v0_pid::NSigmaOuterTofLambdaPosPi, + v0_pid::NSigmaOuterTofLambdaNegPr, + v0_pid::NSigmaOuterTofLambdaNegPi, + v0_pid::HasOuterTofLambdaPosPr, + v0_pid::HasOuterTofLambdaPosPi, + v0_pid::HasOuterTofLambdaNegPr, + v0_pid::HasOuterTofLambdaNegPi); + +DECLARE_SOA_TABLE(A3K0SExpectedInnerTimes, "AOD", "A3K0SITIMES", + v0_pid::ExpectedInnerTofK0SPosPi, + v0_pid::ExpectedInnerTofK0SNegPi, + v0_pid::MeasuredInnerTofK0SPosPi, + v0_pid::MeasuredInnerTofK0SNegPi); + +DECLARE_SOA_TABLE(A3K0SExpectedOuterTimes, "AOD", "A3K0SOTIMES", + v0_pid::ExpectedOuterTofK0SPosPi, + v0_pid::ExpectedOuterTofK0SNegPi, + v0_pid::MeasuredOuterTofK0SPosPi, + v0_pid::MeasuredOuterTofK0SNegPi); + +DECLARE_SOA_TABLE(A3LambdaExpectedInnerTimes, "AOD", "A3LAITIMES", + v0_pid::ExpectedInnerTofLambdaPosPr, + v0_pid::ExpectedInnerTofLambdaPosPi, + v0_pid::ExpectedInnerTofLambdaNegPr, + v0_pid::ExpectedInnerTofLambdaNegPi, + v0_pid::MeasuredInnerTofLambdaPosPr, + v0_pid::MeasuredInnerTofLambdaPosPi, + v0_pid::MeasuredInnerTofLambdaNegPr, + v0_pid::MeasuredInnerTofLambdaNegPi); + +DECLARE_SOA_TABLE(A3LambdaExpectedOuterTimes, "AOD", "A3LAOTIMES", + v0_pid::ExpectedOuterTofLambdaPosPr, + v0_pid::ExpectedOuterTofLambdaPosPi, + v0_pid::ExpectedOuterTofLambdaNegPr, + v0_pid::ExpectedOuterTofLambdaNegPi, + v0_pid::MeasuredOuterTofLambdaPosPr, + v0_pid::MeasuredOuterTofLambdaPosPi, + v0_pid::MeasuredOuterTofLambdaNegPr, + v0_pid::MeasuredOuterTofLambdaNegPi); + } // namespace o2::aod #endif // ALICE3_DATAMODEL_OTFSTRANGENESS_H_ diff --git a/ALICE3/DataModel/OTFTOF.h b/ALICE3/DataModel/OTFTOF.h index b4bf1f26670..621e878a1e7 100644 --- a/ALICE3/DataModel/OTFTOF.h +++ b/ALICE3/DataModel/OTFTOF.h @@ -79,6 +79,8 @@ DECLARE_SOA_COLUMN(OuterTOFExpectedTimeDe, outerTOFExpectedTimeDe, float); //! DECLARE_SOA_COLUMN(OuterTOFExpectedTimeTr, outerTOFExpectedTimeTr, float); //! Reconstructed expected time at the OuterTOF for the Triton mass hypotheses DECLARE_SOA_COLUMN(OuterTOFExpectedTimeHe3, outerTOFExpectedTimeHe3, float); //! Reconstructed expected time at the OuterTOF for the Helium3 mass hypotheses DECLARE_SOA_COLUMN(OuterTOFExpectedTimeAl, outerTOFExpectedTimeAl, float); //! Reconstructed expected time at the OuterTOF for the Alpha mass hypotheses +DECLARE_SOA_COLUMN(HasInnerTOF, hasInnerTOF, bool); //! InnerTOF short-lived values are filled (false if dummy) +DECLARE_SOA_COLUMN(HasOuterTOF, hasOuterTOF, bool); //! OuterTOF short-lived values are filled (false if dummy) DECLARE_SOA_DYNAMIC_COLUMN(NSigmaInnerTOF, nSigmaInnerTOF, //! General function to get the nSigma for the InnerTOF [](const float el, const float mu, @@ -150,6 +152,26 @@ DECLARE_SOA_DYNAMIC_COLUMN(NSigmaOuterTOF, nSigmaOuterTOF, //! General function } }); +DECLARE_SOA_COLUMN(NSigmaSplusInnerTOF, nSigmaSplusInnerTOF, float); //! NSigma Sigma plus InnerTOF +DECLARE_SOA_COLUMN(NSigmaSminusInnerTOF, nSigmaSminusInnerTOF, float); //! NSigma Sigma minus InnerTOF +DECLARE_SOA_COLUMN(NSigmaXiInnerTOF, nSigmaXiInnerTOF, float); //! NSigma Xi InnerTOF +DECLARE_SOA_COLUMN(NSigmaOmegaInnerTOF, nSigmaOmegaInnerTOF, float); //! NSigma Omega InnerTOF + +DECLARE_SOA_COLUMN(NSigmaSplusOuterTOF, nSigmaSplusOuterTOF, float); //! NSigma Sigma plus OuterTOF +DECLARE_SOA_COLUMN(NSigmaSminusOuterTOF, nSigmaSminusOuterTOF, float); //! NSigma Sigma minus OuterTOF +DECLARE_SOA_COLUMN(NSigmaXiOuterTOF, nSigmaXiOuterTOF, float); //! NSigma Xi OuterTOF +DECLARE_SOA_COLUMN(NSigmaOmegaOuterTOF, nSigmaOmegaOuterTOF, float); //! NSigma Omega OuterTOF + +DECLARE_SOA_COLUMN(InnerTOFExpectedTimeSp, innerTOFExpectedTimeSp, float); //! Reconstructed expected time at the InnerTOF for the Sigma plus mass hypotheses +DECLARE_SOA_COLUMN(InnerTOFExpectedTimeSm, innerTOFExpectedTimeSm, float); //! Reconstructed expected time at the InnerTOF for the Sigma minus mass hypotheses +DECLARE_SOA_COLUMN(InnerTOFExpectedTimeXi, innerTOFExpectedTimeXi, float); //! Reconstructed expected time at the InnerTOF for the Xi mass hypotheses +DECLARE_SOA_COLUMN(InnerTOFExpectedTimeOm, innerTOFExpectedTimeOm, float); //! Reconstructed expected time at the InnerTOF for the Omega mass hypotheses + +DECLARE_SOA_COLUMN(OuterTOFExpectedTimeSp, outerTOFExpectedTimeSp, float); //! Reconstructed expected time at the OuterTOF for the Sigma plus mass hypotheses +DECLARE_SOA_COLUMN(OuterTOFExpectedTimeSm, outerTOFExpectedTimeSm, float); //! Reconstructed expected time at the OuterTOF for the Sigma minus mass hypotheses +DECLARE_SOA_COLUMN(OuterTOFExpectedTimeXi, outerTOFExpectedTimeXi, float); //! Reconstructed expected time at the OuterTOF for the Xi mass hypotheses +DECLARE_SOA_COLUMN(OuterTOFExpectedTimeOm, outerTOFExpectedTimeOm, float); //! Reconstructed expected time at the OuterTOF for the Omega mass hypotheses + } // namespace upgrade_tof DECLARE_SOA_TABLE(UpgradeTofMCs, "AOD", "UPGRADETOFMC", @@ -222,9 +244,33 @@ DECLARE_SOA_TABLE(UpgradeTofExpectedTimes, "AOD", "UPGRADETOFEXPT", upgrade_tof::OuterTOFExpectedTimeHe3, upgrade_tof::OuterTOFExpectedTimeAl); +DECLARE_SOA_TABLE(UpgradeTofShortLiveds, "AOD", "UPGRTOFSHRTLVD", + upgrade_tof::NSigmaSplusInnerTOF, + upgrade_tof::NSigmaSminusInnerTOF, + upgrade_tof::NSigmaXiInnerTOF, + upgrade_tof::NSigmaOmegaInnerTOF, + upgrade_tof::NSigmaSplusOuterTOF, + upgrade_tof::NSigmaSminusOuterTOF, + upgrade_tof::NSigmaXiOuterTOF, + upgrade_tof::NSigmaOmegaOuterTOF, + upgrade_tof::InnerTOFExpectedTimeSp, + upgrade_tof::InnerTOFExpectedTimeSm, + upgrade_tof::InnerTOFExpectedTimeXi, + upgrade_tof::InnerTOFExpectedTimeOm, + upgrade_tof::OuterTOFExpectedTimeSp, + upgrade_tof::OuterTOFExpectedTimeSm, + upgrade_tof::OuterTOFExpectedTimeXi, + upgrade_tof::OuterTOFExpectedTimeOm); + +DECLARE_SOA_TABLE(UpgradeTofShortLivedSignals, "AOD", "UPGRTOFSLSIGNAL", + upgrade_tof::HasInnerTOF, + upgrade_tof::HasOuterTOF); + using UpgradeTofMC = UpgradeTofMCs::iterator; using UpgradeTof = UpgradeTofs::iterator; using UpgradeTofExpectedTime = UpgradeTofExpectedTimes::iterator; +using UpgradeTofShortLived = UpgradeTofShortLiveds::iterator; +using UpgradeTofShortLivedSignal = UpgradeTofShortLivedSignals::iterator; } // namespace o2::aod diff --git a/ALICE3/DataModel/RICH.h b/ALICE3/DataModel/RICH.h deleted file mode 100644 index a1fc7c7c692..00000000000 --- a/ALICE3/DataModel/RICH.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file RICH.h -/// \author Nicolo' Jacazio -/// \since 25/02/2021 -/// \brief Set of tables for the ALICE3 RICH information -/// - -#ifndef ALICE3_DATAMODEL_RICH_H_ -#define ALICE3_DATAMODEL_RICH_H_ - -#include -#include - -namespace o2::aod -{ -namespace alice3rich -{ -DECLARE_SOA_INDEX_COLUMN(Track, track); //! Index to travel from track to RICH -DECLARE_SOA_COLUMN(RICHSignal, richSignal, float); //! Signal in RICH -DECLARE_SOA_COLUMN(RICHSignalError, richSignalError, float); //! Error on the RICH signal -DECLARE_SOA_COLUMN(RICHDeltaEl, richDeltaEl, float); //! signal - exp. signal for electrons -DECLARE_SOA_COLUMN(RICHDeltaMu, richDeltaMu, float); //! signal - exp. signal for muons -DECLARE_SOA_COLUMN(RICHDeltaPi, richDeltaPi, float); //! signal - exp. signal for pions -DECLARE_SOA_COLUMN(RICHDeltaKa, richDeltaKa, float); //! signal - exp. signal for kaons -DECLARE_SOA_COLUMN(RICHDeltaPr, richDeltaPr, float); //! signal - exp. signal for protons -DECLARE_SOA_COLUMN(RICHNsigmaEl, richNsigmaEl, float); //! nsigma separation for electrons -DECLARE_SOA_COLUMN(RICHNsigmaMu, richNsigmaMu, float); //! nsigma separation for muons -DECLARE_SOA_COLUMN(RICHNsigmaPi, richNsigmaPi, float); //! nsigma separation for pions -DECLARE_SOA_COLUMN(RICHNsigmaKa, richNsigmaKa, float); //! nsigma separation for kaons -DECLARE_SOA_COLUMN(RICHNsigmaPr, richNsigmaPr, float); //! nsigma separation for protons -DECLARE_SOA_DYNAMIC_COLUMN(RICHDelta, richDelta, //! Delta separation with the RICH detector for the combined species - [](const float& El, const float& Mu, const float& Pi, - const float& Ka, const float& Pr, const o2::track::PID::ID& index) -> float { - switch (index) { - case o2::track::PID::Electron: - return El; - case o2::track::PID::Muon: - return Mu; - case o2::track::PID::Pion: - return Pi; - case o2::track::PID::Kaon: - return Ka; - case o2::track::PID::Proton: - return Pr; - default: - return -999.f; - } - }); -DECLARE_SOA_DYNAMIC_COLUMN(RICHNsigma, richNsigma, //! Nsigma separation with the RICH detector for the combined species - [](const float& El, const float& Mu, const float& Pi, - const float& Ka, const float& Pr, const o2::track::PID::ID& index) -> float { - switch (index) { - case o2::track::PID::Electron: - return El; - case o2::track::PID::Muon: - return Mu; - case o2::track::PID::Pion: - return Pi; - case o2::track::PID::Kaon: - return Ka; - case o2::track::PID::Proton: - return Pr; - default: - return -999.f; - } - }); -} // namespace alice3rich - -namespace alice3frich -{ -DECLARE_SOA_INDEX_COLUMN(Track, track); //! Index to travel from track to FRICH -DECLARE_SOA_COLUMN(FRICHSignal, frichSignal, float); //! Signal in RICH -DECLARE_SOA_COLUMN(FRICHSignalError, frichSignalError, float); //! Error on the RICH signal -DECLARE_SOA_COLUMN(FRICHDeltaEl, frichDeltaEl, float); //! signal - exp. signal for electrons -DECLARE_SOA_COLUMN(FRICHDeltaMu, frichDeltaMu, float); //! signal - exp. signal for muons -DECLARE_SOA_COLUMN(FRICHDeltaPi, frichDeltaPi, float); //! signal - exp. signal for pions -DECLARE_SOA_COLUMN(FRICHDeltaKa, frichDeltaKa, float); //! signal - exp. signal for kaons -DECLARE_SOA_COLUMN(FRICHDeltaPr, frichDeltaPr, float); //! signal - exp. signal for protons -DECLARE_SOA_COLUMN(FRICHNsigmaEl, frichNsigmaEl, float); //! nsigma separation for electrons -DECLARE_SOA_COLUMN(FRICHNsigmaMu, frichNsigmaMu, float); //! nsigma separation for muons -DECLARE_SOA_COLUMN(FRICHNsigmaPi, frichNsigmaPi, float); //! nsigma separation for pions -DECLARE_SOA_COLUMN(FRICHNsigmaKa, frichNsigmaKa, float); //! nsigma separation for kaons -DECLARE_SOA_COLUMN(FRICHNsigmaPr, frichNsigmaPr, float); //! nsigma separation for protons -DECLARE_SOA_DYNAMIC_COLUMN(FRICHDelta, frichDelta, //! Delta separation with the FRICH detector for the combined species - [](const float& El, const float& Mu, const float& Pi, - const float& Ka, const float& Pr, const o2::track::PID::ID& index) -> float { - switch (index) { - case o2::track::PID::Electron: - return El; - case o2::track::PID::Muon: - return Mu; - case o2::track::PID::Pion: - return Pi; - case o2::track::PID::Kaon: - return Ka; - case o2::track::PID::Proton: - return Pr; - default: - return -999.f; - } - }); -DECLARE_SOA_DYNAMIC_COLUMN(FRICHNsigma, frichNsigma, //! Nsigma separation with the FRICH detector for the combined species - [](const float& El, const float& Mu, const float& Pi, - const float& Ka, const float& Pr, const o2::track::PID::ID& index) -> float { - switch (index) { - case o2::track::PID::Electron: - return El; - case o2::track::PID::Muon: - return Mu; - case o2::track::PID::Pion: - return Pi; - case o2::track::PID::Kaon: - return Ka; - case o2::track::PID::Proton: - return Pr; - default: - return -999.f; - } - }); -} // namespace alice3frich - -DECLARE_SOA_TABLE(RICHs, "AOD", "RICH", //! Table for the ALICE3 RICH detector - o2::soa::Index<>, - alice3rich::TrackId, - alice3rich::RICHSignal, - alice3rich::RICHSignalError, - alice3rich::RICHDeltaEl, - alice3rich::RICHDeltaMu, - alice3rich::RICHDeltaPi, - alice3rich::RICHDeltaKa, - alice3rich::RICHDeltaPr, - alice3rich::RICHNsigmaEl, - alice3rich::RICHNsigmaMu, - alice3rich::RICHNsigmaPi, - alice3rich::RICHNsigmaKa, - alice3rich::RICHNsigmaPr, - alice3rich::RICHDelta, - alice3rich::RICHNsigma); -using RICH = RICHs::iterator; - -DECLARE_SOA_TABLE(FRICHs, "AOD", "FRICH", //! Table for the ALICE3 Forward RICH detector - o2::soa::Index<>, - alice3frich::TrackId, - alice3frich::FRICHSignal, - alice3frich::FRICHSignalError, - alice3frich::FRICHDeltaEl, - alice3frich::FRICHDeltaMu, - alice3frich::FRICHDeltaPi, - alice3frich::FRICHDeltaKa, - alice3frich::FRICHDeltaPr, - alice3frich::FRICHNsigmaEl, - alice3frich::FRICHNsigmaMu, - alice3frich::FRICHNsigmaPi, - alice3frich::FRICHNsigmaKa, - alice3frich::FRICHNsigmaPr, - alice3frich::FRICHDelta, - alice3frich::FRICHNsigma); - -using FRICH = FRICHs::iterator; - -} // namespace o2::aod - -#endif // ALICE3_DATAMODEL_RICH_H_ diff --git a/PWGDQ/DataModel/ReducedTablesAlice3.h b/ALICE3/DataModel/ReducedTablesAlice3.h similarity index 76% rename from PWGDQ/DataModel/ReducedTablesAlice3.h rename to ALICE3/DataModel/ReducedTablesAlice3.h index 787d7d38793..7ff870f9308 100644 --- a/PWGDQ/DataModel/ReducedTablesAlice3.h +++ b/ALICE3/DataModel/ReducedTablesAlice3.h @@ -15,14 +15,18 @@ /// /// \author Alexander Tiekoetter (atiekoet@cern.ch) University of Muenster -#ifndef PWGDQ_DATAMODEL_REDUCEDTABLESALICE3_H_ -#define PWGDQ_DATAMODEL_REDUCEDTABLESALICE3_H_ +#ifndef ALICE3_DATAMODEL_REDUCEDTABLESALICE3_H_ +#define ALICE3_DATAMODEL_REDUCEDTABLESALICE3_H_ #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "ALICE3/DataModel/OTFMID.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include "ALICE3/DataModel/OTFRICH.h" #include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/collisionAlice3.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/Multiplicity.h" #include #include @@ -36,25 +40,21 @@ namespace o2::aod namespace reducedeventalice3 { DECLARE_SOA_COLUMN(MultDensity, multDensity, float); -DECLARE_SOA_COLUMN(MCPosX, mcPosX, float); //! MC event position X -DECLARE_SOA_COLUMN(MCPosY, mcPosY, float); //! MC event position Y -DECLARE_SOA_COLUMN(MCPosZ, mcPosZ, float); //! MC event position Z +DECLARE_SOA_COLUMN(McPosX, mcPosX, float); //! MC event position X +DECLARE_SOA_COLUMN(McPosY, mcPosY, float); //! MC event position Y +DECLARE_SOA_COLUMN(McPosZ, mcPosZ, float); //! MC event position Z } // namespace reducedeventalice3 -namespace reducedeventmcalice3 -{ -DECLARE_SOA_COLUMN(MultMCNParticlesEta05, multMCNParticlesEta05, float); -DECLARE_SOA_COLUMN(MultMCNParticlesEta08, multMCNParticlesEta08, float); -DECLARE_SOA_COLUMN(MultMCNParticlesEta10, multMCNParticlesEta10, float); -DECLARE_SOA_COLUMN(MultMCNParticlesEta20, multMCNParticlesEta20, float); -DECLARE_SOA_COLUMN(MultMCNParticlesEta40, multMCNParticlesEta40, float); -} // namespace reducedeventmcalice3 - -DECLARE_SOA_TABLE_STAGED(ReducedA3Events, "REA3EVENTS", //! Main event information table +DECLARE_SOA_TABLE_STAGED(ReA3Events, "REA3EVENT", //! Main event information table o2::soa::Index<>, collision::PosX, collision::PosY, collision::PosZ, collision::NumContrib, collision::CollisionTime, collision::CollisionTimeRes, reducedeventalice3::MultDensity); +DECLARE_SOA_TABLE(ReA3EventsExtended, "AOD", "REA3EVENTEXT", //! Event vertex covariance matrix + mult::MultNTracksPV, mult::MultNTracksPVeta1, mult::MultNTracksPVetaHalf, + mult::MultNTracksGlobal, mult::MultNGlobalTracksPV, + cent::CentRun2V0M); + DECLARE_SOA_TABLE(ReducedA3EventsVtxCov, "AOD", "REA3VTXCOV", //! Event vertex covariance matrix collision::CovXX, collision::CovXY, collision::CovXZ, collision::CovYY, collision::CovYZ, collision::CovZZ, collision::Chi2); @@ -62,19 +62,20 @@ DECLARE_SOA_TABLE(ReducedA3EventsVtxCov, "AOD", "REA3VTXCOV", //! Event verte DECLARE_SOA_TABLE(ReducedA3EventsInfo, "AOD", "REA3EVENTINFO", //! Main event index table reducedevent::CollisionId); -DECLARE_SOA_TABLE(ReducedA3MCEvents, "AOD", "REA3MCEVTS", //! Event level MC truth information +DECLARE_SOA_TABLE(ReA3MCEvents, "AOD", "REA3MCEVENT", //! Event level MC truth information o2::soa::Index<>, - mccollision::GeneratorsID, reducedeventalice3::MCPosX, reducedeventalice3::MCPosY, reducedeventalice3::MCPosZ, - mccollision::T, mccollision::Weight, mccollision::ImpactParameter); + mccollision::GeneratorsID, reducedeventalice3::McPosX, reducedeventalice3::McPosY, reducedeventalice3::McPosZ, + mccollision::T, mccollision::Weight, mccollision::ImpactParameter, + mcmult_alice3::MultMC, mcmult_alice3::MultMC25, mcmult_alice3::MultMC125, mcmult_alice3::MultMC09); -using ReducedA3MCEvent = ReducedA3MCEvents::iterator; -using ReducedA3Event = ReducedA3Events::iterator; +using ReducedA3MCEvent = ReA3MCEvents::iterator; +using ReA3Event = ReA3Events::iterator; namespace reducedtrackalice3 { // basic track information -DECLARE_SOA_INDEX_COLUMN(ReducedA3Event, reduceda3event); //! -DECLARE_SOA_INDEX_COLUMN(Track, track); //! +DECLARE_SOA_INDEX_COLUMN(ReA3Event, reA3Event); //! +DECLARE_SOA_INDEX_COLUMN(Track, track); //! // ---- flags reserved for storing various information during filtering DECLARE_SOA_BITMAP_COLUMN(FilteringFlags, filteringFlags, 64); //! // ----------------------------------------------------- @@ -111,8 +112,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! } // namespace reducedtrackalice3 // basic track information -DECLARE_SOA_TABLE(ReducedA3Tracks, "AOD", "REA3TRACK", //! - o2::soa::Index<>, reducedtrackalice3::ReducedA3EventId, reducedtrackalice3::FilteringFlags, +DECLARE_SOA_TABLE(ReA3Tracks, "AOD", "REA3TRACK", //! + o2::soa::Index<>, reducedtrackalice3::ReA3EventId, reducedtrackalice3::FilteringFlags, reducedtrackalice3::Pt, reducedtrackalice3::Eta, reducedtrackalice3::Phi, reducedtrackalice3::Sign, reducedtrackalice3::IsAmbiguous, reducedtrackalice3::Px, reducedtrackalice3::Py, @@ -124,21 +125,21 @@ DECLARE_SOA_TABLE(ReducedA3TracksBarrelCov, "AOD", "REA3BARRELCOV", //! track::CSnpSnp, track::CTglY, track::CTglZ, track::CTglSnp, track::CTglTgl, track::C1PtY, track::C1PtZ, track::C1PtSnp, track::C1PtTgl, track::C1Pt21Pt2); -namespace reducedA3trackMC +namespace reduceda3trackmc { -DECLARE_SOA_INDEX_COLUMN(ReducedA3MCEvent, reducedA3MCEvent); //! -DECLARE_SOA_COLUMN(McReducedFlags, mcReducedFlags, uint16_t); //! Flags to hold compressed MC selection information -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Mother0, mother0, int, "ReducedA3MCTracks_Mother0"); //! Track index of the first mother -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Mother1, mother1, int, "ReducedA3MCTracks_Mother1"); //! Track index of the last mother -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Daughter0, daughter0, int, "ReducedA3MCTracks_Daughter0"); //! Track index of the first daughter -DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Daughter1, daughter1, int, "ReducedA3MCTracks_Daughter1"); //! Track index of the last daughter -DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(Mothers, mothers); //! Mother tracks (possible empty) array. Iterate over mcParticle.mothers_as()) -DECLARE_SOA_SELF_SLICE_INDEX_COLUMN(Daughters, daughters); //! Daughter tracks (possibly empty) slice. Check for non-zero with mcParticle.has_daughters(). Iterate over mcParticle.daughters_as()) -DECLARE_SOA_COLUMN(Pt, pt, float); //! -DECLARE_SOA_COLUMN(Eta, eta, float); //! -DECLARE_SOA_COLUMN(Phi, phi, float); //! -DECLARE_SOA_COLUMN(E, e, float); //! -DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! +DECLARE_SOA_INDEX_COLUMN(ReA3MCEvent, reA3MCEvent); //! +DECLARE_SOA_COLUMN(McReducedFlags, mcReducedFlags, uint16_t); //! Flags to hold compressed MC selection information +DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Mother0, mother0, int, "ReA3MCTracks_Mother0"); //! Track index of the first mother +DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Mother1, mother1, int, "ReA3MCTracks_Mother1"); //! Track index of the last mother +DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Daughter0, daughter0, int, "ReA3MCTracks_Daughter0"); //! Track index of the first daughter +DECLARE_SOA_SELF_INDEX_COLUMN_FULL(Daughter1, daughter1, int, "ReA3MCTracks_Daughter1"); //! Track index of the last daughter +DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(Mothers, mothers); //! Mother tracks (possible empty) array. Iterate over mcParticle.mothers_as()) +DECLARE_SOA_SELF_SLICE_INDEX_COLUMN(Daughters, daughters); //! Daughter tracks (possibly empty) slice. Check for non-zero with mcParticle.has_daughters(). Iterate over mcParticle.daughters_as()) +DECLARE_SOA_COLUMN(Pt, pt, float); //! +DECLARE_SOA_COLUMN(Eta, eta, float); //! +DECLARE_SOA_COLUMN(Phi, phi, float); //! +DECLARE_SOA_COLUMN(E, e, float); //! +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! [](float pt, float phi) -> float { return pt * std::cos(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! [](float pt, float phi) -> float { return pt * std::sin(phi); }); @@ -151,27 +152,26 @@ DECLARE_SOA_DYNAMIC_COLUMN(Y, y, //! Particle rapidity float pz = pt * std::sinh(eta); if ((e - pz) > static_cast(1e-7)) { return 0.5f * std::log((e + pz) / (e - pz)); - } else { - return -999.0f; } + return -999.0f; }); -} // namespace reducedA3trackMC +} // namespace reduceda3trackmc // NOTE: This table is nearly identical to the one from Framework (except that it points to the event ID, not the BC id) // This table contains all MC truth tracks (both barrel and muon) -DECLARE_SOA_TABLE(ReducedA3MCTracks, "AOD", "REA3MCTRACK", //! MC track information (on disk) - o2::soa::Index<>, reducedA3trackMC::ReducedA3MCEventId, +DECLARE_SOA_TABLE(ReA3MCTracks, "AOD", "REA3MCTRACK", //! MC track information (on disk) + o2::soa::Index<>, reduceda3trackmc::ReA3MCEventId, mcparticle::PdgCode, mcparticle::StatusCode, mcparticle::Flags, - reducedA3trackMC::MothersIds, reducedA3trackMC::DaughtersIdSlice, + reduceda3trackmc::MothersIds, reduceda3trackmc::DaughtersIdSlice, mcparticle::Weight, - reducedA3trackMC::Pt, reducedA3trackMC::Eta, reducedA3trackMC::Phi, reducedA3trackMC::E, + reduceda3trackmc::Pt, reduceda3trackmc::Eta, reduceda3trackmc::Phi, reduceda3trackmc::E, mcparticle::Vx, mcparticle::Vy, mcparticle::Vz, mcparticle::Vt, - reducedA3trackMC::McReducedFlags, - reducedA3trackMC::Px, - reducedA3trackMC::Py, - reducedA3trackMC::Pz, - reducedA3trackMC::P, - reducedA3trackMC::Y, + reduceda3trackmc::McReducedFlags, + reduceda3trackmc::Px, + reduceda3trackmc::Py, + reduceda3trackmc::Pz, + reduceda3trackmc::P, + reduceda3trackmc::Y, mcparticle::ProducedByGenerator, mcparticle::FromBackgroundEvent, mcparticle::GetGenStatusCode, @@ -179,18 +179,18 @@ DECLARE_SOA_TABLE(ReducedA3MCTracks, "AOD", "REA3MCTRACK", //! MC track informa mcparticle::GetHepMCStatusCode, mcparticle::IsPhysicalPrimary); -using ReducedA3MCTrack = ReducedA3MCTracks::iterator; +using ReA3MCTrack = ReA3MCTracks::iterator; namespace reduceda3barreltracklabel { -DECLARE_SOA_INDEX_COLUMN(ReducedA3MCTrack, reducedA3MCTrack); //! +DECLARE_SOA_INDEX_COLUMN(ReA3MCTrack, reA3MCTrack); //! DECLARE_SOA_COLUMN(McMask, mcMask, uint16_t); } // namespace reduceda3barreltracklabel // NOTE: MC labels. This table has one entry for each reconstructed track (joinable with the track tables) // The McParticleId points to the position of the MC truth track from the ReducedTracksMC table DECLARE_SOA_TABLE(ReducedA3TracksBarrelLabels, "AOD", "REA3BARLA", //! - reduceda3barreltracklabel::ReducedA3MCTrackId, reduceda3barreltracklabel::McMask, reducedA3trackMC::McReducedFlags); + reduceda3barreltracklabel::ReA3MCTrackId, reduceda3barreltracklabel::McMask, reduceda3trackmc::McReducedFlags); using ReducedA3TrackBarrelLabel = ReducedA3TracksBarrelLabels::iterator; @@ -206,31 +206,31 @@ DECLARE_SOA_TABLE(ReducedA3TracksBarrel, "AOD", "REA3BARREL", DECLARE_SOA_TABLE(ReducedA3TracksBarrelInfo, "AOD", "REA3BARRELINFO", reducedtrackalice3::CollisionId, collision::PosX, collision::PosY, collision::PosZ, reducedtrackalice3::TrackId); -using ReducedA3Track = ReducedA3Tracks::iterator; +using ReducedA3Track = ReA3Tracks::iterator; using ReducedA3TrackBarrel = ReducedA3TracksBarrel::iterator; using ReducedA3TrackBarrelCov = ReducedA3TracksBarrelCov::iterator; using ReducedA3TrackBarrelInfo = ReducedA3TracksBarrelInfo::iterator; namespace reducedeventlabela3 { -DECLARE_SOA_INDEX_COLUMN(ReducedA3MCEvent, reducedA3MCEvent); //! MC collision -DECLARE_SOA_COLUMN(McMask, mcMask, uint16_t); //! Bit mask to indicate collision mismatches (bit ON means mismatch). Bit 15: indicates negative label +DECLARE_SOA_INDEX_COLUMN(ReA3MCEvent, reA3MCEvent); //! MC collision +DECLARE_SOA_COLUMN(McMask, mcMask, uint16_t); //! Bit mask to indicate collision mismatches (bit ON means mismatch). Bit 15: indicates negative label } // namespace reducedeventlabela3 DECLARE_SOA_TABLE(ReducedA3MCEventLabels, "AOD", "REA3MCCOLLBL", //! Table joined to the ReducedEvents table containing the MC index - reducedeventlabela3::ReducedA3MCEventId, reducedeventlabela3::McMask); + reducedeventlabela3::ReA3MCEventId, reducedeventlabela3::McMask); using ReducedA3MCEventLabel = ReducedA3MCEventLabels::iterator; -namespace reducedA3track_association +namespace reduceda3trackassociation { -DECLARE_SOA_INDEX_COLUMN(ReducedA3Event, reducedA3event); //! ReducedEvent index -DECLARE_SOA_INDEX_COLUMN(ReducedA3Track, reducedA3track); //! ReducedTrack index -} // namespace reducedA3track_association +DECLARE_SOA_INDEX_COLUMN(ReA3Event, reA3Event); //! ReducedEvent index +DECLARE_SOA_INDEX_COLUMN(ReA3Track, reA3Track); //! ReducedTrack index +} // namespace reduceda3trackassociation DECLARE_SOA_TABLE(ReducedA3TracksAssoc, "AOD", "REA3ASSOC", //! Table for reducedtrack-to-reducedcollision association - reducedA3track_association::ReducedA3EventId, - reducedA3track_association::ReducedA3TrackId); + reduceda3trackassociation::ReA3EventId, + reduceda3trackassociation::ReA3TrackId); DECLARE_SOA_TABLE(ReducedA3PIDTOF, "AOD", "REA3PIDTOF", upgrade_tof::TOFEventTime, @@ -276,6 +276,14 @@ DECLARE_SOA_TABLE(ReducedA3PIDTOF, "AOD", "REA3PIDTOF", upgrade_tof::NSigmaHelium3OuterTOF, upgrade_tof::NSigmaAlphaOuterTOF>); +DECLARE_SOA_TABLE(ReducedA3PIDMID, "AOD", "REA3PIDMID", + upgrade_mid::MIDEventTime, + upgrade_mid::MIDEventTimeErr, + upgrade_mid::MIDTrackPosReco, + upgrade_mid::MIDTrackTimeReco, + upgrade_mid::MIDTrackLengthReco, + upgrade_mid::MIDCandidate); + DECLARE_SOA_TABLE(ReducedA3PIDRich, "AOD", "REA3PIDRICH", upgrade_rich::NSigmaElectronRich, upgrade_rich::NSigmaMuonRich, @@ -332,4 +340,4 @@ DECLARE_SOA_TABLE(ReducedA3PIDOT, "AOD", "REA3PIDOT", } // namespace o2::aod -#endif // PWGDQ_DATAMODEL_REDUCEDTABLESALICE3_H_ +#endif // ALICE3_DATAMODEL_REDUCEDTABLESALICE3_H_ diff --git a/ALICE3/DataModel/collisionAlice3.h b/ALICE3/DataModel/collisionAlice3.h index a1238e9e1d9..e0a9a2f7a76 100644 --- a/ALICE3/DataModel/collisionAlice3.h +++ b/ALICE3/DataModel/collisionAlice3.h @@ -32,6 +32,20 @@ DECLARE_SOA_TABLE(CollisionsAlice3, "AOD", "COLLALICE3", using CollisionAlice3 = CollisionsAlice3::iterator; +namespace mcmult_alice3 +{ +DECLARE_SOA_COLUMN(MultMC, multMC, int); //! Total MCTruth multiplicity +DECLARE_SOA_COLUMN(MultMC25, multMC25, int); //! MCTruth multiplicity in |eta| < 2.5 +DECLARE_SOA_COLUMN(MultMC125, multMC125, int); //! MCTruth multiplicity in |eta| < 1.25 +DECLARE_SOA_COLUMN(MultMC09, multMC09, int); //! MCTruth multiplicity in |eta| < 0.9 +} // namespace mcmult_alice3 +DECLARE_SOA_TABLE(MultsMCAlice3, "AOD", "MULTMCALICE3", + mcmult_alice3::MultMC, + mcmult_alice3::MultMC25, + mcmult_alice3::MultMC125, + mcmult_alice3::MultMC09); + +using MultMCAlice3 = MultsMCAlice3::iterator; } // namespace o2::aod #endif // ALICE3_DATAMODEL_COLLISIONALICE3_H_ diff --git a/ALICE3/DataModel/prefilterDilepton.h b/ALICE3/DataModel/prefilterDilepton.h new file mode 100644 index 00000000000..520c023bdcb --- /dev/null +++ b/ALICE3/DataModel/prefilterDilepton.h @@ -0,0 +1,52 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file collisionExtra.h +/// \author David Dobrigkeit Chinellato +/// \since 11/05/2023 +/// \brief Table for ALICE 3 collision-related info +/// + +#ifndef ALICE3_DATAMODEL_PREFILTERDILEPTON_H_ +#define ALICE3_DATAMODEL_PREFILTERDILEPTON_H_ + +// O2 includes +#include + +namespace o2::aod +{ + +namespace dileptonanalysisflags +{ +// DECLARE_SOA_COLUMN(IsMCEventSelected, isMCEventSelected, int); +DECLARE_SOA_COLUMN(IsEventCentSelected, isEventCentSelected, int); +DECLARE_SOA_COLUMN(IsTrackPrefilter, isTrackPrefilter, int); +} // namespace dileptonanalysisflags + +// DECLARE_SOA_TABLE(EventMCCuts, "AOD", "EVENTMCCUTS", emanalysisflags::IsMCEventSelected); +DECLARE_SOA_TABLE(DiEventCentCuts, "AOD", "DIEVENTCENTCUTS", dileptonanalysisflags::IsEventCentSelected); +DECLARE_SOA_TABLE(DiTrackPrefilter, "AOD", "DITRACKPREFILTER", dileptonanalysisflags::IsTrackPrefilter); + +namespace dileptonsmearedtrack +{ +DECLARE_SOA_COLUMN(PtSmeared, ptSmeared, float); +DECLARE_SOA_COLUMN(EtaSmeared, etaSmeared, float); +DECLARE_SOA_COLUMN(PhiSmeared, phiSmeared, float); +DECLARE_SOA_COLUMN(Selected, selected, bool); +} // namespace dileptonsmearedtrack + +DECLARE_SOA_TABLE(SmearedAlice3Dilepton, "AOD", "SMEAREDALICE3DILEPTON", + dileptonsmearedtrack::PtSmeared, dileptonsmearedtrack::EtaSmeared, dileptonsmearedtrack::PhiSmeared, dileptonsmearedtrack::Selected); + +} // namespace o2::aod + +#endif // ALICE3_DATAMODEL_PREFILTERDILEPTON_H_ diff --git a/ALICE3/DataModel/tracksAlice3.h b/ALICE3/DataModel/tracksAlice3.h index 04cb2d1a9d4..327d0587b64 100644 --- a/ALICE3/DataModel/tracksAlice3.h +++ b/ALICE3/DataModel/tracksAlice3.h @@ -30,9 +30,15 @@ DECLARE_SOA_COLUMN(IsReconstructed, isReconstructed, bool); //! is reconstructed DECLARE_SOA_COLUMN(NSiliconHits, nSiliconHits, int); //! number of silicon hits DECLARE_SOA_COLUMN(NTPCHits, nTPCHits, int); //! number of tpc hits DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); //! PDG code of the linked truth MC particle +DECLARE_SOA_COLUMN(TrackType, trackType, int); //! Type of the track +DECLARE_SOA_COLUMN(IsPVContributor, isPVContributor, bool); //! Has track contributed to the PV fit? +DECLARE_SOA_COLUMN(TrackIU, trackIU, float); //! Innermost update: radius (cm) of the innermost hit assigned to the track +DECLARE_SOA_COLUMN(TrackOU, trackOU, float); //! Outermost update: radius (cm) of the outermost hit assigned to the track } // namespace track_alice3 DECLARE_SOA_TABLE(TracksAlice3, "AOD", "TRACKSALICE3", - track_alice3::IsReconstructed); + track_alice3::IsReconstructed, + track_alice3::TrackIU, + track_alice3::TrackOU); using TrackAlice3 = TracksAlice3::iterator; DECLARE_SOA_TABLE(TracksAlice3Pdg, "AOD", "TRACKSALICE3PDG", @@ -41,18 +47,28 @@ using TrackAlice3Pdg = TracksAlice3Pdg::iterator; DECLARE_SOA_TABLE(TracksExtraA3, "AOD", "TracksExtraA3", track_alice3::NSiliconHits, - track_alice3::NTPCHits); + track_alice3::NTPCHits, + track_alice3::TrackType, + track_alice3::IsPVContributor); using TrackExtraA3 = TracksExtraA3::iterator; namespace mcparticle_alice3 { -DECLARE_SOA_COLUMN(NHits, nHits, int); //! number of silicon hits -DECLARE_SOA_COLUMN(Charge, charge, float); //! particle charge +DECLARE_SOA_COLUMN(NHits, nHits, int); //! number of silicon hits +DECLARE_SOA_COLUMN(Charge, charge, float); //! particle charge +DECLARE_SOA_COLUMN(DecayRadius, decayRadius, float); //! Radius for decayed particle produced by decayer (-1 if not decayed) +DECLARE_SOA_BITMAP_COLUMN(DecayerBits, decayerBits, 8); //! Bit mask for particle produced by the OTF decayer } // namespace mcparticle_alice3 DECLARE_SOA_TABLE(MCParticlesExtraA3, "AOD", "MCParticlesExtraA3", mcparticle_alice3::NHits, mcparticle_alice3::Charge); using MCParticleExtraA3 = MCParticlesExtraA3::iterator; + +DECLARE_SOA_TABLE(OTFParticleExtras, "AOD", "OTFPARTICLEEXTRA", + mcparticle_alice3::DecayerBits, + mcparticle_alice3::DecayRadius); +using OTFParticleExtra = OTFParticleExtras::iterator; + } // namespace o2::aod #endif // ALICE3_DATAMODEL_TRACKSALICE3_H_ diff --git a/ALICE3/ML/CMakeLists.txt b/ALICE3/ML/CMakeLists.txt new file mode 100644 index 00000000000..5753ff2f23e --- /dev/null +++ b/ALICE3/ML/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_library(A3HfMlCore + SOURCES HfMlResponse3Prong.cxx + PUBLIC_LINK_LIBRARIES O2Physics::MLCore) diff --git a/PWGDQ/Core/PWGDQCoreLinkDef.h b/ALICE3/ML/HfMlResponse3Prong.cxx similarity index 59% rename from PWGDQ/Core/PWGDQCoreLinkDef.h rename to ALICE3/ML/HfMlResponse3Prong.cxx index 1f6a6f33685..9e2fa0c458a 100644 --- a/PWGDQ/Core/PWGDQCoreLinkDef.h +++ b/ALICE3/ML/HfMlResponse3Prong.cxx @@ -9,14 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; +/// \file HfMlResponse3Prong.cxx +/// \brief Helper file to provide compilation command for headers. +/// +/// \author Vít Kučera , Inha University -#pragma link C++ class VarManager + ; -#pragma link C++ class HistogramManager + ; -#pragma link C++ class MixingHandler + ; -#pragma link C++ class AnalysisCut + ; -#pragma link C++ class AnalysisCompositeCut + ; -#pragma link C++ class MCProng + ; -#pragma link C++ class MCSignal + ; +#include "HfMlResponse3Prong.h" // IWYU pragma: keep diff --git a/ALICE3/ML/MulticharmMlResponse.h b/ALICE3/ML/MulticharmMlResponse.h new file mode 100644 index 00000000000..f20c4b1ae28 --- /dev/null +++ b/ALICE3/ML/MulticharmMlResponse.h @@ -0,0 +1,206 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MulticharmMlResponse.h +/// \brief Class to compute the ML response for multi-charm candidates +/// \author Jesper Karlsson Gumprecht + +#ifndef ALICE3_ML_MULTICHARMMLRESPONSE_H_ +#define ALICE3_ML_MULTICHARMMLRESPONSE_H_ + +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include + +namespace multi_charm_ml +{ + +static constexpr int NBinsPt = 10; +static constexpr int NClasses = 2; +static constexpr std::array, NBinsPt> Cuts = {{{0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}}}; + +static const std::vector labelsPt = {"pT bin 0", + "pT bin 1", + "pT bin 2", + "pT bin 3", + "pT bin 4", + "pT bin 5", + "pT bin 6", + "pT bin 7", + "pT bin 8", + "pT bin 9"}; + +static const std::vector labelsCutScore = {"score class 1", "score class 2"}; +static const std::vector namesInputFeatures{"xicDauDCA", + "xiccDauDCA", + "xiDCAxy", + "xicDCAxy", + "xiccDCAxy", + "xiDCAz", + "xicDCAz", + "xiccDCAz", + "pi1cDCAxy", + "pi2cDCAxy", + "piccDCAxy", + "pi1cDCAz", + "pi2cDCAz", + "piccDCAz", + "xicDecayRadius2D", + "xiccDecayRadius2D", + "xicProperLength", + "xicDistanceFromPV", + "xiccProperLength"}; + +} // namespace multi_charm_ml + +namespace o2::analysis +{ +// list of input features that can be requested via the mlConfigurations.namesInputFeatures configurable +enum class InputFeaturesMulticharm : uint8_t { + xicDauDCA = 0, + xiccDauDCA, + xiDCAxy, + xicDCAxy, + xiccDCAxy, + xiDCAz, + xicDCAz, + xiccDCAz, + pi1cDCAxy, + pi2cDCAxy, + piccDCAxy, + pi1cDCAz, + pi2cDCAz, + piccDCAz, + xicDecayRadius2D, + xiccDecayRadius2D, + xicProperLength, + xicDistanceFromPV, + xiccProperLength +}; + +template +class MulticharmMlResponse : public MlResponse +{ + public: + MulticharmMlResponse() = default; + ~MulticharmMlResponse() override = default; + + template + std::vector getInputFeatures(TMulticharm const& multicharm) + { + std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); + + for (const auto& idx : MlResponse::mCachedIndices) { + switch (idx) { + case InputFeaturesMulticharm::xicDauDCA: + inputFeatures.emplace_back(multicharm.xicDauDCA()); + break; + case InputFeaturesMulticharm::xiccDauDCA: + inputFeatures.emplace_back(multicharm.xiccDauDCA()); + break; + case InputFeaturesMulticharm::xiDCAxy: + inputFeatures.emplace_back(multicharm.xiDCAxy()); + break; + case InputFeaturesMulticharm::xicDCAxy: + inputFeatures.emplace_back(multicharm.xicDCAxy()); + break; + case InputFeaturesMulticharm::xiccDCAxy: + inputFeatures.emplace_back(multicharm.xiccDCAxy()); + break; + case InputFeaturesMulticharm::xiDCAz: + inputFeatures.emplace_back(multicharm.xiDCAz()); + break; + case InputFeaturesMulticharm::xicDCAz: + inputFeatures.emplace_back(multicharm.xicDCAz()); + break; + case InputFeaturesMulticharm::xiccDCAz: + inputFeatures.emplace_back(multicharm.xiccDCAz()); + break; + case InputFeaturesMulticharm::pi1cDCAxy: + inputFeatures.emplace_back(multicharm.pi1cDCAxy()); + break; + case InputFeaturesMulticharm::pi2cDCAxy: + inputFeatures.emplace_back(multicharm.pi2cDCAxy()); + break; + case InputFeaturesMulticharm::piccDCAxy: + inputFeatures.emplace_back(multicharm.piccDCAxy()); + break; + case InputFeaturesMulticharm::pi1cDCAz: + inputFeatures.emplace_back(multicharm.pi1cDCAz()); + break; + case InputFeaturesMulticharm::pi2cDCAz: + inputFeatures.emplace_back(multicharm.pi2cDCAz()); + break; + case InputFeaturesMulticharm::piccDCAz: + inputFeatures.emplace_back(multicharm.piccDCAz()); + break; + case InputFeaturesMulticharm::xicDecayRadius2D: + inputFeatures.emplace_back(multicharm.xicDecayRadius2D()); + break; + case InputFeaturesMulticharm::xiccDecayRadius2D: + inputFeatures.emplace_back(multicharm.xiccDecayRadius2D()); + break; + case InputFeaturesMulticharm::xicProperLength: + inputFeatures.emplace_back(multicharm.xicProperLength()); + break; + case InputFeaturesMulticharm::xicDistanceFromPV: + inputFeatures.emplace_back(multicharm.xicDistanceFromPV()); + break; + case InputFeaturesMulticharm::xiccProperLength: + inputFeatures.emplace_back(multicharm.xiccProperLength()); + break; + } + } + return inputFeatures; + } + + protected: + void setAvailableInputFeatures() override + { + MlResponse::mAvailableInputFeatures = { + {"xicDauDCA", static_cast(InputFeaturesMulticharm::xicDauDCA)}, + {"xiccDauDCA", static_cast(InputFeaturesMulticharm::xiccDauDCA)}, + {"xiDCAxy", static_cast(InputFeaturesMulticharm::xiDCAxy)}, + {"xicDCAxy", static_cast(InputFeaturesMulticharm::xicDCAxy)}, + {"xiccDCAxy", static_cast(InputFeaturesMulticharm::xiccDCAxy)}, + {"xiDCAz", static_cast(InputFeaturesMulticharm::xiDCAz)}, + {"xicDCAz", static_cast(InputFeaturesMulticharm::xicDCAz)}, + {"xiccDCAz", static_cast(InputFeaturesMulticharm::xiccDCAz)}, + {"pi1cDCAxy", static_cast(InputFeaturesMulticharm::pi1cDCAxy)}, + {"pi2cDCAxy", static_cast(InputFeaturesMulticharm::pi2cDCAxy)}, + {"piccDCAxy", static_cast(InputFeaturesMulticharm::piccDCAxy)}, + {"pi1cDCAz", static_cast(InputFeaturesMulticharm::pi1cDCAz)}, + {"pi2cDCAz", static_cast(InputFeaturesMulticharm::pi2cDCAz)}, + {"piccDCAz", static_cast(InputFeaturesMulticharm::piccDCAz)}, + {"xicDecayRadius2D", static_cast(InputFeaturesMulticharm::xicDecayRadius2D)}, + {"xiccDecayRadius2D", static_cast(InputFeaturesMulticharm::xiccDecayRadius2D)}, + {"xicProperLength", static_cast(InputFeaturesMulticharm::xicProperLength)}, + {"xicDistanceFromPV", static_cast(InputFeaturesMulticharm::xicDistanceFromPV)}, + {"xiccProperLength", static_cast(InputFeaturesMulticharm::xiccProperLength)}}; + } +}; + +} // namespace o2::analysis + +#endif // ALICE3_ML_MULTICHARMMLRESPONSE_H_ diff --git a/ALICE3/Macros/Configuration/a3geometry_v3.ini b/ALICE3/Macros/Configuration/a3geometry_v3.ini index e4eb415c133..3381df08209 100644 --- a/ALICE3/Macros/Configuration/a3geometry_v3.ini +++ b/ALICE3/Macros/Configuration/a3geometry_v3.ini @@ -92,15 +92,6 @@ B05.resZ: 0.001 B05.eff: 1.00 B05.type: 1 -iTOF.r: 19 -iTOF.z: 250 -iTOF.x0: 0.01*3. -iTOF.xrho: 0.03 -iTOF.resRPhi: 0.001 -iTOF.resZ: 0.001 -iTOF.eff: 1.00 -iTOF.type: 0 - B06.r: 20. B06.z: 250 B06.x0: 0.01 @@ -110,6 +101,15 @@ B06.resZ: 0.001 B06.eff: 1.00 B06.type: 1 +iTOF.r: 21 +iTOF.z: 250 +iTOF.x0: 0.01*3. +iTOF.xrho: 0.03 +iTOF.resRPhi: 0.001 +iTOF.resZ: 0.001 +iTOF.eff: 1.00 +iTOF.type: 3 + B07.r: 30. B07.z: 250 B07.x0: 0.01 @@ -146,6 +146,15 @@ B10.resZ: 0.001 B10.eff: 1.00 B10.type: 1 +oTOF.r: 92 +oTOF.z: 250 +oTOF.x0: 0.01*3. +oTOF.xrho: 0.03 +oTOF.resRPhi: 0.001 +oTOF.resZ: 0.001 +oTOF.eff: 1.00 +oTOF.type: 3 + # Lookup tables global.lutEl: ccdb:/Users/j/jekarlss/LUTs/v3/b/el global.lutMu: ccdb:/Users/j/jekarlss/LUTs/v3/b/mu diff --git a/ALICE3/Macros/Configuration/decayTable_example.ini b/ALICE3/Macros/Configuration/decayTable_example.ini new file mode 100644 index 00000000000..777defd3570 --- /dev/null +++ b/ALICE3/Macros/Configuration/decayTable_example.ini @@ -0,0 +1,16 @@ +# Example decay table for the on-the-fly decayer (configurable "decayTable") +# Overrides the TDatabasePDG decay channels of the listed mothers; unlisted particles are unaffected. +# Format: .: ... +# Branching ratios of each mother should sum to 1 (within 0.01); small deviations are renormalised. + +# Lambda -> p pi- only +3122.ch0: 0.639 2212 -211 +-3122.ch0: 0.639 -2112 111 + +# Lambda -> n pi0 only +3122.ch1: 0.361 2112 111 +-3122.ch1: 0.361 -2112 111 + +# Xi- -> Lambda pi- +3312.ch0: 1.0 3122 -211 +-3312.ch0: 1.0 -3122 211 diff --git a/ALICE3/Macros/drawFastTracker.C b/ALICE3/Macros/drawFastTracker.C index 79978eac997..1faf09201ce 100644 --- a/ALICE3/Macros/drawFastTracker.C +++ b/ALICE3/Macros/drawFastTracker.C @@ -9,8 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "FastTracker.h" -#include "TrackUtilities.h" +/// \file drawFastTracker.C +/// \author Nicolò Jacazio nicolo.jacazio@cern.ch +/// \brief Draw FastTracker performace + +#include "ALICE3/Core/FastTracker.h" +#include "ALICE3/Core/TrackUtilities.h" #include #include @@ -26,6 +30,7 @@ #include #include #include +#include #include #include @@ -34,67 +39,64 @@ #include #include -void drawFastTracker(float magneticField = 5.f, // in units of kGauss - const int nch = 100, // number of charged particles per unit rapidity - const int pdg = 211) // PDG code of the particle to track +void drawFastTracker(const float magneticField = 20.f, // in units of kGauss + const int nch = 100, // number of charged particles per unit rapidity + const int pdg = PDG_t::kPiPlus) // PDG code of the particle to track { TDatabasePDG* db = TDatabasePDG::Instance(); - TParticlePDG* p = 0; + TParticlePDG* p = nullptr; p = db->GetParticle(pdg); if (!p) { LOG(fatal) << "Particle with PDG code " << pdg << " not found in TDatabasePDG"; return; } - const float mass = p->Mass(); // particle mass in GeV/c^2 - const float q = p->Charge() / 3.0; // charge in e + const float mass = p->Mass(); // particle mass in GeV/c^2 + const int q = static_cast(p->Charge() / 3); // charge in e o2::parameters::GRPMagField grpmag; grpmag.setFieldUniformity(true); grpmag.setL3Current(30000.f * (magneticField / 5.0f)); - auto field = grpmag.getNominalL3Field(); + // auto field = grpmag.getNominalL3Field(); o2::base::Propagator::initFieldFromGRP(&grpmag); fair::Logger::SetVerbosity(fair::Verbosity::verylow); o2::fastsim::FastTracker fastTracker = o2::fastsim::FastTracker(); - if (0) { - fastTracker.SetApplyEffCorrection(false); - Double_t x0IB = 0.001; - Double_t x0OB = 0.01; - Double_t xrhoIB = 2.3292e-02; // 100 mum Si - Double_t xrhoOB = 2.3292e-01; // 1000 mum Si - Double_t resRPhiIB = 0.00025; - Double_t resZIB = 0.00025; - Double_t resRPhiOB = 0.00100; - Double_t resZOB = 0.00100; - Double_t eff = 0.98; - // fastTracker.AddLayer("vertex", 0.0, 250, 0, 0); // dummy vertex for matrix calculation - fastTracker.AddLayer("bpipe0", 0.48, 250, 0.00042, 2.772e-02); // 150 mum Be - fastTracker.AddLayer("B00", 0.50, 250, x0IB, xrhoIB, resRPhiIB, resZIB, eff, 1); - fastTracker.AddLayer("B01", 1.20, 250, x0IB, xrhoIB, resRPhiIB, resZIB, eff, 1); - fastTracker.AddLayer("B02", 2.50, 250, x0IB, xrhoIB, resRPhiIB, resZIB, eff, 1); - fastTracker.AddLayer("bpipe1", 3.7, 250, 0.0014, 9.24e-02); // 500 mum Be - fastTracker.AddLayer("B03", 3.75, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B04", 7.00, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B05", 12.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B06", 20.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B07", 30.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B08", 45.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B09", 60.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B10", 80.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - fastTracker.AddLayer("B11", 100., 250, x0OB, xrhoOB, resRPhiOB, resZOB, eff, 1); - } else { - std::vector pixelRes{0.025, 0.025, 0.01, 0.01}; - // fastTracker.AddSiliconALICE3v4(pixelRes); // FIXME - } - - fastTracker.Print(); - fastTracker.SetMagneticField(magneticField); + fastTracker.setApplyEffCorrection(false); + Double_t x0IB = 0.001; + Double_t x0OB = 0.01; + Double_t xrhoIB = 2.3292e-02; // 100 mum Si + Double_t xrhoOB = 2.3292e-01; // 1000 mum Si + Double_t resRPhiIB = 0.00025; + Double_t resZIB = 0.00025; + Double_t resRPhiOB = 0.00100; + Double_t resZOB = 0.00100; + Double_t trackerEff = 0.98; + // fastTracker.AddLayer("vertex", 0.0, 250, 0, 0); // dummy vertex for matrix calculation + + // N.B. Tracker configuration outdated and is only meant to be used as a template + fastTracker.addLayer("bpipe0", 0.48, 250, 0.00042, 2.772e-02); // 150 mum Be + fastTracker.addLayer("B00", 0.50, 250, x0IB, xrhoIB, resRPhiIB, resZIB, trackerEff, 1); + fastTracker.addLayer("B01", 1.20, 250, x0IB, xrhoIB, resRPhiIB, resZIB, trackerEff, 1); + fastTracker.addLayer("B02", 2.50, 250, x0IB, xrhoIB, resRPhiIB, resZIB, trackerEff, 1); + fastTracker.addLayer("bpipe1", 3.7, 250, 0.0014, 9.24e-02); // 500 mum Be + fastTracker.addLayer("B03", 3.75, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B04", 7.00, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B05", 12.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B06", 20.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B07", 30.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B08", 45.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B09", 60.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B10", 80.0, 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + fastTracker.addLayer("B11", 100., 250, x0OB, xrhoOB, resRPhiOB, resZOB, trackerEff, 1); + + fastTracker.print(); + fastTracker.setMagneticField(magneticField); TAxis ptBinning(1000, 0., 10); - TGraph* gPt = new TGraph(); + auto gPt = new TGraph(); gPt->GetXaxis()->SetTitle("#it{p}_{T} (GeV/c)"); gPt->GetYaxis()->SetTitle("Efficiency"); - TEfficiency* hEfficiency = new TEfficiency("hEfficiency", ";#it{p}_{T} (GeV/c);Efficiency", ptBinning.GetNbins(), ptBinning.GetBinLowEdge(1), ptBinning.GetBinUpEdge(ptBinning.GetNbins())); + auto hEfficiency = new TEfficiency("hEfficiency", ";#it{p}_{T} (GeV/c);Efficiency", ptBinning.GetNbins(), ptBinning.GetBinLowEdge(1), ptBinning.GetBinUpEdge(ptBinning.GetNbins())); TH1F* hFastTrackerQA = new TH1F("hFastTrackerQA", ";#it{p}_{T} (GeV/c;Tracking code", ptBinning.GetNbins(), ptBinning.GetBinLowEdge(1), ptBinning.GetBinUpEdge(ptBinning.GetNbins())); TLorentzVector tlv; @@ -120,11 +122,12 @@ void drawFastTracker(float magneticField = 5.f, // in units of kGauss continue; } - for (int trial = 0; trial < 200; trial++) { - hEfficiency->Fill(fastTracker.FastTrack(trkIn, trkOut, nch) > 0, pt); + const int nTrials = 200; + for (int trial = 0; trial < nTrials; trial++) { + hEfficiency->Fill(fastTracker.fastTrack(trkIn, trkOut, nch) > 0, pt); } int status = 4; - status = fastTracker.FastTrack(trkIn, trkOut, nch); + status = fastTracker.fastTrack(trkIn, trkOut, nch); hFastTrackerQA->Fill(pt, status); if (status < 0) { LOG(debug) << " --- fatSolve: FastTrack failed with status " << status << " --- "; @@ -134,12 +137,12 @@ void drawFastTracker(float magneticField = 5.f, // in units of kGauss } // define the efficiency float eff = 1.; - for (size_t l = 1; l < fastTracker.GetNLayers(); ++l) { - if (fastTracker.IsLayerInert(l)) { + for (size_t l = 1; l < fastTracker.getNLayers(); ++l) { + if (fastTracker.isLayerInert(l)) { continue; // skip inert layers } float igoodhit = 0.f; - igoodhit = fastTracker.GetGoodHitProb(l); + igoodhit = fastTracker.getGoodHitProb(l); if (igoodhit <= 0.) { continue; } diff --git a/ALICE3/Macros/testFastTracker.C b/ALICE3/Macros/testFastTracker.C index 857ff416919..4010b32f4a1 100644 --- a/ALICE3/Macros/testFastTracker.C +++ b/ALICE3/Macros/testFastTracker.C @@ -14,20 +14,34 @@ /// \brief Test the FastTracker functionality #include "ALICE3/Core/FastTracker.h" +#include "ALICE3/Core/GeometryContainer.h" +#include #include #include -void testFastTracker(std::string geometryFile = "a3geo.ini") +void testFastTracker(const std::string& geometryFile = "Configuration/a3geo.ini") { fair::Logger::SetConsoleSeverity(fair::Severity::debug); - // auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); - // ccdb.setURL("http://alice-ccdb.cern.ch"); - o2::fastsim::FastTracker fastTracker; - // fastTracker.AddGenericDetector(geometryFile); // FIXME - // fastTracker.AddGenericDetector(geometryFile, &ccdb); - fastTracker.Print(); + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + ccdb.setURL("http://alice-ccdb.cern.ch"); + + o2::fastsim::GeometryContainer geometryContainer; + geometryContainer.setCcdbManager(&ccdb); + geometryContainer.addEntry(geometryFile); + const int nGeometries = geometryContainer.getNumberOfConfigurations(); + const float magneticField = geometryContainer.getFloatValue(0, "global", "magneticfield"); + + for (int icfg = 0; icfg < nGeometries; ++icfg) { + const o2::fastsim::GeometryEntry& geometry = geometryContainer.getEntry(icfg); + o2::fastsim::FastTracker fastTracker; + + fastTracker.setMagneticField(magneticField); + fastTracker.addGenericDetector(geometry, &ccdb); + + fastTracker.print(); + } } diff --git a/ALICE3/TableProducer/CMakeLists.txt b/ALICE3/TableProducer/CMakeLists.txt index 233088b6d5e..ce2e49bea57 100644 --- a/ALICE3/TableProducer/CMakeLists.txt +++ b/ALICE3/TableProducer/CMakeLists.txt @@ -22,7 +22,12 @@ o2physics_add_dpl_workflow(alice3-trackextension COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(alice3-centrality - SOURCES alice3-centrality.cxx + SOURCES alice3Centrality.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(alice3-multiplicity + SOURCES alice3Multiplicity.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -62,11 +67,21 @@ o2physics_add_dpl_workflow(alice3-tracking-translator COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(alice3-dq-table-maker - SOURCES alice3-dq-table-maker.cxx + SOURCES alice3DqTableMaker.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(alice3-dileptonsmearing + SOURCES alice3-dileptonsmearing.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGEMDileptonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(alice3strangenessfinder SOURCES alice3strangenessFinder.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::ALICE3Core COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(alice3-strangeness-tof-pid + SOURCES alice3StrangenessTofPid.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::ALICE3Core + COMPONENT_NAME Analysis) diff --git a/ALICE3/TableProducer/OTF/CMakeLists.txt b/ALICE3/TableProducer/OTF/CMakeLists.txt index 6fe3f41a02f..d92809cae76 100644 --- a/ALICE3/TableProducer/OTF/CMakeLists.txt +++ b/ALICE3/TableProducer/OTF/CMakeLists.txt @@ -24,6 +24,11 @@ o2physics_add_dpl_workflow(onthefly-tofpid PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(onthefly-midpid + SOURCES onTheFlyMidPid.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(onthefly-richpid SOURCES onTheFlyRichPid.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2Physics::ALICE3Core O2Physics::FastTracker diff --git a/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx b/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx index 614e0f6f84b..c6f587fdd3c 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyDecayer.cxx @@ -16,10 +16,12 @@ /// #include "ALICE3/Core/Decayer.h" +#include "ALICE3/Core/OTFParticle.h" #include "ALICE3/Core/TrackUtilities.h" -#include "ALICE3/DataModel/OTFMCParticle.h" +#include "ALICE3/DataModel/tracksAlice3.h" -#include +#include +#include #include #include #include @@ -30,35 +32,36 @@ #include #include #include +#include #include #include #include #include -#include - #include +#include #include #include -#include -#include -#include -#include #include #include using namespace o2; using namespace o2::framework; -static constexpr int NumDecays = 7; +static constexpr int NumDecays = 13; static constexpr int NumParameters = 1; -static constexpr int DefaultParameters[NumDecays][NumParameters]{{1}, {1}, {1}, {1}, {1}, {1}, {1}}; -static constexpr float Tolerance = 1e-7f; -static const std::vector ParameterNames{"enable"}; -static const std::vector ParticleNames{"K0s", +static constexpr std::array, NumDecays> DefaultParameters{{{1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}}}; +static const std::vector parameterNames{"enable"}; +static const std::vector particleNames{"K0s", "Lambda", "Anti-Lambda", + "SigmaPlus", + "Anti-SigmaPlus", + "SigmaMinus", + "Anti-SigmaMinus", + "Xi0", + "Anti-Xi0", "Xi", "Anti-Xi", "Omega", @@ -67,186 +70,57 @@ static const std::vector ParticleNames{"K0s", static const std::vector pdgCodes{PDG_t::kK0Short, PDG_t::kLambda0, PDG_t::kLambda0Bar, + PDG_t::kSigmaPlus, + PDG_t::kSigmaBarMinus, + PDG_t::kSigmaMinus, + PDG_t::kSigmaBarPlus, + o2::constants::physics::kXi0, + -o2::constants::physics::kXi0, PDG_t::kXiMinus, PDG_t::kXiPlusBar, PDG_t::kOmegaMinus, PDG_t::kOmegaPlusBar}; -enum class V0 { kNeg, - kPos, - kSize }; -enum class AntiV0 { kPos, - kNeg, - kSize }; -enum class Cascade { kBach, - kV0, - kNeg, - kPos, - kSize }; -enum class AntiCascade { kBach, - kV0, - kPos, - kNeg, - kSize }; +namespace o2::aod +{ +O2ORIGIN("TMP"); +} struct OnTheFlyDecayer { - Produces tableMcParticlesWithDau; + Produces tableMcCollisions; + Produces tableMcParticles; + Produces tableOTFParticleExtras; o2::upgrade::Decayer decayer; - Service pdgDB; - std::map> mDecayDaughters; + Service pdgDB{}; + Service ccdb{}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; Configurable seed{"seed", 0, "Set seed for particle decayer"}; Configurable magneticField{"magneticField", 20., "Magnetic field (kG)"}; - Configurable maxEta{"maxEta", 2.5, "Only decay particles that appear within selected eta range"}; + Configurable decayTable{"decayTable", "", "Decay table overriding the TDatabasePDG decay channels of the listed particles (local path or ccdb:), empty to disable"}; Configurable> enabledDecays{"enabledDecays", - {DefaultParameters[0], NumDecays, NumParameters, ParticleNames, ParameterNames}, + {DefaultParameters[0].data(), NumDecays, NumParameters, particleNames, parameterNames}, "Enable option for particle to be decayed: 0 - no, 1 - yes"}; - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - ConfigurableAxis axisRadius{"axisRadius", {200, 0, 100}, "Radial axis"}; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - - ConfigurableAxis axisLogRadius{"axisLogRadius", {VARIABLE_WIDTH, 0.0f, 0.01f, 0.0104713f, 0.0109648f, 0.0114815f, 0.0120226f, 0.0125893f, 0.0131826f, 0.0138038f, 0.0144544f, 0.0151356f, 0.0158489f, 0.0165959f, 0.017378f, 0.018197f, 0.0190546f, 0.0199526f, 0.020893f, 0.0218776f, 0.0229087f, 0.0239883f, 0.0251189f, 0.0263027f, 0.0275423f, 0.0288403f, 0.0301995f, 0.0316228f, 0.0331131f, 0.0346737f, 0.0363078f, 0.0380189f, 0.0398107f, 0.0416869f, 0.0436516f, 0.0457088f, 0.047863f, 0.0501187f, 0.0524807f, 0.0549541f, 0.057544f, 0.060256f, 0.0630957f, 0.0660693f, 0.0691831f, 0.0724436f, 0.0758578f, 0.0794328f, 0.0831764f, 0.0870964f, 0.0912011f, 0.0954993f, 0.1f, 0.104713f, 0.109648f, 0.114815f, 0.120226f, 0.125893f, 0.131826f, 0.138038f, 0.144544f, 0.151356f, 0.158489f, 0.165959f, 0.17378f, 0.18197f, 0.190546f, 0.199526f, 0.20893f, 0.218776f, 0.229087f, 0.239883f, 0.251189f, 0.263027f, 0.275423f, 0.288403f, 0.301995f, 0.316228f, 0.331131f, 0.346737f, 0.363078f, 0.380189f, 0.398107f, 0.416869f, 0.436516f, 0.457088f, 0.47863f, 0.501187f, 0.524807f, 0.549541f, 0.57544f, 0.60256f, 0.630957f, 0.660693f, 0.691831f, 0.724436f, 0.758578f, 0.794328f, 0.831764f, 0.870964f, 0.912011f, 0.954993f, 1.0f, 1.04713f, 1.09648f, 1.14815f, 1.20226f, 1.25893f, 1.31826f, 1.38038f, 1.44544f, 1.51356f, 1.58489f, 1.65959f, 1.7378f, 1.8197f, 1.90546f, 1.99526f, 2.0893f, 2.18776f, 2.29087f, 2.39883f, 2.51189f, 2.63027f, 2.75423f, 2.88403f, 3.01995f, 3.16228f, 3.31131f, 3.46737f, 3.63078f, 3.80189f, 3.98107f, 4.16869f, 4.36516f, 4.57088f, 4.7863f, 5.01187f, 5.24807f, 5.49541f, 5.7544f, 6.0256f, 6.30957f, 6.60693f, 6.91831f, 7.24436f, 7.58578f, 7.94328f, 8.31764f, 8.70964f, 9.12011f, 9.54993f, 10.0f, 10.4713f, 10.9648f, 11.4815f, 12.0226f, 12.5893f, 13.1826f, 13.8038f, 14.4544f, 15.1356f, 15.8489f, 16.5959f, 17.378f, 18.197f, 19.0546f, 19.9526f, 20.893f, 21.8776f, 22.9087f, 23.9883f, 25.1189f, 26.3027f, 27.5423f, 28.8403f, 30.1995f, 31.6228f, 33.1131f, 34.6737f, 36.3078f, 38.0189f, 39.8107f, 41.6869f, 43.6516f, 45.7088f, 47.863f, 50.1187f, 52.4807f, 54.9541f, 57.544f, 60.256f, 63.0957f, 66.0693f, 69.1831f, 72.4436f, 75.8578f, 79.4328f, 83.1764f, 87.0964f, 91.2011f, 95.4993f, 100.0f}, "Radial axis"}; - - ConfigurableAxis axisLogPt{"axisLogPt", {VARIABLE_WIDTH, 0.0f, 0.001f, 0.00104713f, 0.00109648f, 0.00114815f, 0.00120226f, 0.00125893f, 0.00131826f, 0.00138038f, 0.00144544f, 0.00151356f, 0.00158489f, 0.00165959f, 0.0017378f, 0.0018197f, 0.00190546f, 0.00199526f, 0.0020893f, 0.00218776f, 0.00229087f, 0.00239883f, 0.00251189f, 0.00263027f, 0.00275423f, 0.00288403f, 0.00301995f, 0.00316228f, 0.00331131f, 0.00346737f, 0.00363078f, 0.00380189f, 0.00398107f, 0.00416869f, 0.00436516f, 0.00457088f, 0.0047863f, 0.00501187f, 0.00524807f, 0.00549541f, 0.0057544f, 0.0060256f, 0.00630957f, 0.00660693f, 0.00691831f, 0.00724436f, 0.00758578f, 0.00794328f, 0.00831764f, 0.00870964f, 0.00912011f, 0.00954993f, 0.01f, 0.0104713f, 0.0109648f, 0.0114815f, 0.0120226f, 0.0125893f, 0.0131826f, 0.0138038f, 0.0144544f, 0.0151356f, 0.0158489f, 0.0165959f, 0.017378f, 0.018197f, 0.0190546f, 0.0199526f, 0.020893f, 0.0218776f, 0.0229087f, 0.0239883f, 0.0251189f, 0.0263027f, 0.0275423f, 0.0288403f, 0.0301995f, 0.0316228f, 0.0331131f, 0.0346737f, 0.0363078f, 0.0380189f, 0.0398107f, 0.0416869f, 0.0436516f, 0.0457088f, 0.047863f, 0.0501187f, 0.0524807f, 0.0549541f, 0.057544f, 0.060256f, 0.0630957f, 0.0660693f, 0.0691831f, 0.0724436f, 0.0758578f, 0.0794328f, 0.0831764f, 0.0870964f, 0.0912011f, 0.0954993f, 0.1f, 0.104713f, 0.109648f, 0.114815f, 0.120226f, 0.125893f, 0.131826f, 0.138038f, 0.144544f, 0.151356f, 0.158489f, 0.165959f, 0.17378f, 0.18197f, 0.190546f, 0.199526f, 0.20893f, 0.218776f, 0.229087f, 0.239883f, 0.251189f, 0.263027f, 0.275423f, 0.288403f, 0.301995f, 0.316228f, 0.331131f, 0.346737f, 0.363078f, 0.380189f, 0.398107f, 0.416869f, 0.436516f, 0.457088f, 0.47863f, 0.501187f, 0.524807f, 0.549541f, 0.57544f, 0.60256f, 0.630957f, 0.660693f, 0.691831f, 0.724436f, 0.758578f, 0.794328f, 0.831764f, 0.870964f, 0.912011f, 0.954993f, 1.0f, 1.04713f, 1.09648f, 1.14815f, 1.20226f, 1.25893f, 1.31826f, 1.38038f, 1.44544f, 1.51356f, 1.58489f, 1.65959f, 1.7378f, 1.8197f, 1.90546f, 1.99526f, 2.0893f, 2.18776f, 2.29087f, 2.39883f, 2.51189f, 2.63027f, 2.75423f, 2.88403f, 3.01995f, 3.16228f, 3.31131f, 3.46737f, 3.63078f, 3.80189f, 3.98107f, 4.16869f, 4.36516f, 4.57088f, 4.7863f, 5.01187f, 5.24807f, 5.49541f, 5.7544f, 6.0256f, 6.30957f, 6.60693f, 6.91831f, 7.24436f, 7.58578f, 7.94328f, 8.31764f, 8.70964f, 9.12011f, 9.54993f, 10.0f}, "pt axis for QA histograms"}; - - struct McParticleAlice3 { - McParticleAlice3() = default; - ~McParticleAlice3() = default; - McParticleAlice3(const McParticleAlice3& src) = default; - McParticleAlice3(int collisionId, - int pdgCode, - int statusCode, - int flags, - int mother0, - int mother1, - int daughter0, - int daughter1, - float weight, - float px, float py, float pz, float e, - float vx, float vy, float vz, float vt, - float phi, float eta, float pt, float p, float y, - bool isAlive, bool isPrimary) : collisionId(collisionId), - pdgCode(pdgCode), - statusCode(statusCode), - flags(flags), - mothersIds{mother0, mother1}, - daughtersIdSlice{daughter0, daughter1}, - weight(weight), - px(px), - py(py), - pz(pz), - e(e), - vx(vx), - vy(vy), - vz(vz), - vt(vt), - phi(phi), - eta(eta), - pt(pt), - p(p), - y(y), - isAlive(isAlive), - isPrimary(isPrimary) {} - - bool hasNaN() const - { - return std::isnan(px) || std::isnan(py) || std::isnan(pz) || std::isnan(e) || - std::isnan(vx) || std::isnan(vy) || std::isnan(vz) || std::isnan(vt) || - std::isnan(phi) || std::isnan(eta) || std::isnan(pt) || std::isnan(p) || - std::isnan(y) || std::isnan(weight); - } - - int collisionId; - int pdgCode; - int statusCode; - int flags; - int mothersIds[2]; - int daughtersIdSlice[2]; - float weight; - float px, py, pz, e; - float vx, vy, vz, vt; - float phi, eta, pt, p, y; - bool isAlive; - bool isPrimary; - }; - - template - int idx(TEnumerate enumerate) - { - return static_cast(enumerate); - } - - bool checkDecayChannel(const int pdgMother, const std::vector& daus) - { - switch (pdgMother) { - case PDG_t::kK0Short: - if (daus.size() != static_cast(V0::kSize)) { - return false; - } - return (daus[idx(V0::kNeg)].pdgCode() == PDG_t::kPiMinus && - daus[idx(V0::kPos)].pdgCode() == PDG_t::kPiPlus); - case PDG_t::kLambda0: - if (daus.size() != static_cast(V0::kSize)) { - return false; - } - return (daus[idx(V0::kNeg)].pdgCode() == PDG_t::kPiMinus && - daus[idx(V0::kPos)].pdgCode() == PDG_t::kProton); - case PDG_t::kLambda0Bar: - if (daus.size() != static_cast(AntiV0::kSize)) { - return false; - } - return (daus[idx(AntiV0::kNeg)].pdgCode() == PDG_t::kProtonBar && - daus[idx(AntiV0::kPos)].pdgCode() == PDG_t::kPiPlus); - case PDG_t::kXiMinus: - if (daus.size() != static_cast(Cascade::kSize)) { - return false; - } - return (daus[idx(Cascade::kBach)].pdgCode() == PDG_t::kPiMinus && - daus[idx(Cascade::kV0)].pdgCode() == PDG_t::kLambda0 && - daus[idx(Cascade::kNeg)].pdgCode() == PDG_t::kPiMinus && - daus[idx(Cascade::kPos)].pdgCode() == PDG_t::kProton); - case PDG_t::kXiPlusBar: - if (daus.size() != static_cast(AntiCascade::kSize)) { - return false; - } - return (daus[idx(AntiCascade::kBach)].pdgCode() == PDG_t::kPiPlus && - daus[idx(AntiCascade::kV0)].pdgCode() == PDG_t::kLambda0Bar && - daus[idx(AntiCascade::kPos)].pdgCode() == PDG_t::kPiPlus && - daus[idx(AntiCascade::kNeg)].pdgCode() == PDG_t::kProtonBar); - case PDG_t::kOmegaMinus: - if (daus.size() != static_cast(Cascade::kSize)) { - return false; - } - return (daus[idx(Cascade::kBach)].pdgCode() == PDG_t::kKMinus && - daus[idx(Cascade::kV0)].pdgCode() == PDG_t::kLambda0 && - daus[idx(Cascade::kNeg)].pdgCode() == PDG_t::kPiMinus && - daus[idx(Cascade::kPos)].pdgCode() == PDG_t::kProton); - case PDG_t::kOmegaPlusBar: - if (daus.size() != static_cast(AntiCascade::kSize)) { - return false; - } - return (daus[idx(AntiCascade::kBach)].pdgCode() == PDG_t::kKPlus && - daus[idx(AntiCascade::kV0)].pdgCode() == PDG_t::kLambda0Bar && - daus[idx(AntiCascade::kPos)].pdgCode() == PDG_t::kPiPlus && - daus[idx(AntiCascade::kNeg)].pdgCode() == PDG_t::kProtonBar); - default: - return false; - } - } - + std::size_t indexOffset = 0; + std::size_t particlesInDataframe = 0; + static constexpr float PicoToNano = 1.e-3f; std::vector mEnabledDecays; void init(o2::framework::InitContext&) { - LOG(info) << "Initializing on-the-fly-decayer."; - LOG(info) << "Using seed: " << seed; - LOG(info) << "Using magnetic field: " << magneticField; + LOG(info) << " --- Initializing on-the-fly-decayer."; + LOG(info) << " --- Using seed: " << seed; + LOG(info) << " --- Using magnetic field: " << magneticField; decayer.setSeed(seed); decayer.setBField(magneticField); + if (!decayTable.value.empty()) { + LOG(info) << " --- Using decay table: " << decayTable.value; + decayer.loadDecayTable(decayTable.value, pdgDB, ccdb.operator->()); + } for (int i = 0; i < NumDecays; ++i) { - if (enabledDecays->get(ParticleNames[i].c_str(), "enable")) { - LOG(info) << "Decay enabled: " << ParticleNames[i].c_str(); + if (enabledDecays->get(particleNames[i].c_str(), "enable") != 0) { + LOG(info) << " --- Decay enabled: " << pdgCodes[i]; mEnabledDecays.push_back(pdgCodes[i]); } } @@ -254,292 +128,128 @@ struct OnTheFlyDecayer { auto hNaNBookkeeping = histos.add("hNaNBookkeeping", "hNaNBookkeeping", kTH1D, {{2, -0.5, 1.5}}); hNaNBookkeeping->GetXaxis()->SetBinLabel(1, "OK"); hNaNBookkeeping->GetXaxis()->SetBinLabel(2, "NaN"); - - histos.add("K0S/hGenK0S", "hGenK0S;pT (GeV)", kTH1D, {axisPt}); - histos.add("K0S/hPosDauDecayRadius", "hPosDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("K0S/hNegDauDecayRadius", "hNegDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - - histos.add("Lambda/hGenLambda", "hGenLambda;pT (GeV)", kTH1D, {axisPt}); - histos.add("Lambda/hPosDauDecayRadius", "hPosDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("Lambda/hNegDauDecayRadius", "hNegDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - - histos.add("AntiLambda/hGenAntiLambda", "hGenAntiLambda;pT (GeV)", kTH1D, {axisPt}); - histos.add("AntiLambda/hPosDauDecayRadius", "hPosDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("AntiLambda/hNegDauDecayRadius", "hNegDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - - histos.add("Xi/hGenXi", "hGenXi;pT (GeV)", kTH1D, {axisPt}); - histos.add("Xi/hV0DauDecayRadius", "hV0DauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("Xi/hBachDauDecayRadius", "hBachDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("Xi/hPosDauDecayRadius", "hPosDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("Xi/hNegDauDecayRadius", "hNegDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - - histos.add("AntiXi/hGenAntiXi", "hGenAntiXi;pT (GeV)", kTH1D, {axisPt}); - histos.add("AntiXi/hV0DauDecayRadius", "hV0DauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("AntiXi/hBachDauDecayRadius", "hBachDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("AntiXi/hPosDauDecayRadius", "hPosDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("AntiXi/hNegDauDecayRadius", "hNegDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - - histos.add("Omega/hGenOmega", "hGenOmega;pT (GeV)", kTH1D, {axisPt}); - histos.add("Omega/hV0DauDecayRadius", "hV0DauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("Omega/hBachDauDecayRadius", "hBachDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("Omega/hPosDauDecayRadius", "hPosDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("Omega/hNegDauDecayRadius", "hNegDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - - histos.add("AntiOmega/hGenAntiOmega", "hGenAntiOmega;pT (GeV)", kTH1D, {axisPt}); - histos.add("AntiOmega/hV0DauDecayRadius", "hV0DauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("AntiOmega/hBachDauDecayRadius", "hBachDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("AntiOmega/hPosDauDecayRadius", "hPosDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - histos.add("AntiOmega/hNegDauDecayRadius", "hNegDauDecayRadius;Radius decay vtx 2D (cm); Daughter pT (GeV)", kTH2D, {axisLogRadius, axisLogPt}); - - histos.add("Secondaries/hGenEl", "hGenEl;pT (GeV)", kTH1D, {axisPt}); - histos.add("Secondaries/hGenMu", "hGenMu;pT (GeV)", kTH1D, {axisPt}); - histos.add("Secondaries/hGenPi", "hGenPi;pT (GeV)", kTH1D, {axisPt}); - histos.add("Secondaries/hGenKa", "hGenKa;pT (GeV)", kTH1D, {axisPt}); - histos.add("Secondaries/hGenPr", "hGenPr;pT (GeV)", kTH1D, {axisPt}); } - bool canDecay(const int pdgCode) + bool canDecay(const o2::upgrade::OTFParticle& particle) { - return std::find(mEnabledDecays.begin(), mEnabledDecays.end(), pdgCode) != mEnabledDecays.end(); + if (particle.hasDaughters()) { + return false; + } + + return std::find(mEnabledDecays.begin(), mEnabledDecays.end(), particle.pdgCode()) != mEnabledDecays.end(); } - std::vector mcParticlesAlice3; - void process(aod::McCollision const&, aod::McParticles const& mcParticles) + std::vector allParticles; + void decayParticles(const int start, const int stop) { - mDecayDaughters.clear(); - mcParticlesAlice3.clear(); - u_int64_t nStoredDaughters = 0; - for (int index{0}; index < static_cast(mcParticles.size()); ++index) { - const auto& particle = mcParticles.rawIteratorAt(index); - std::vector decayDaughters, decayStack; - if (canDecay(particle.pdgCode()) && std::abs(particle.eta()) < maxEta) { - o2::upgrade::OTFParticle mother(particle); - decayStack = decayer.decayParticle(pdgDB, mother); - while (!decayStack.empty()) { - o2::upgrade::OTFParticle otfParticle = decayStack.back(); - decayStack.pop_back(); - - const bool stable = !canDecay(otfParticle.pdgCode()); - otfParticle.setIsAlive(stable); - decayDaughters.push_back(otfParticle); - - if (stable) { - continue; - } - - std::vector daughters = decayer.decayParticle(pdgDB, otfParticle); - for (const o2::upgrade::OTFParticle& dau : daughters) { - decayStack.push_back(dau); - } - } - - if (decayDaughters.empty()) { - LOG(error) << "Attempted to decay " << particle.pdgCode() << " but resulting vector of daugthers were empty"; - continue; - } - - switch (particle.pdgCode()) { // Do QA on specific channels - case PDG_t::kK0Short: - if (!checkDecayChannel(PDG_t::kK0Short, decayDaughters)) { - break; - } - histos.fill(HIST("K0S/hGenK0S"), particle.pt()); - histos.fill(HIST("K0S/hPosDauDecayRadius"), decayDaughters[idx(V0::kPos)].radius(), decayDaughters[idx(V0::kPos)].pt()); - histos.fill(HIST("K0S/hNegDauDecayRadius"), decayDaughters[idx(V0::kNeg)].radius(), decayDaughters[idx(V0::kNeg)].pt()); - break; - - case PDG_t::kLambda0: - if (!checkDecayChannel(PDG_t::kLambda0, decayDaughters)) { - break; - } - histos.fill(HIST("Lambda/hGenLambda"), particle.pt()); - histos.fill(HIST("Lambda/hPosDauDecayRadius"), decayDaughters[idx(V0::kPos)].radius(), decayDaughters[idx(V0::kPos)].pt()); - histos.fill(HIST("Lambda/hNegDauDecayRadius"), decayDaughters[idx(V0::kNeg)].radius(), decayDaughters[idx(V0::kNeg)].pt()); - break; - - case PDG_t::kLambda0Bar: - if (!checkDecayChannel(PDG_t::kLambda0Bar, decayDaughters)) { - break; - } - histos.fill(HIST("AntiLambda/hGenAntiLambda"), particle.pt()); - histos.fill(HIST("AntiLambda/hPosDauDecayRadius"), decayDaughters[idx(AntiV0::kPos)].radius(), decayDaughters[idx(AntiV0::kPos)].pt()); - histos.fill(HIST("AntiLambda/hNegDauDecayRadius"), decayDaughters[idx(AntiV0::kNeg)].radius(), decayDaughters[idx(AntiV0::kNeg)].pt()); - break; - - case PDG_t::kXiMinus: - if (!checkDecayChannel(PDG_t::kXiMinus, decayDaughters)) { - break; - } - histos.fill(HIST("Xi/hGenXi"), particle.pt()); - histos.fill(HIST("Xi/hBachDauDecayRadius"), decayDaughters[idx(Cascade::kBach)].radius(), decayDaughters[idx(Cascade::kBach)].pt()); - histos.fill(HIST("Xi/hV0DauDecayRadius"), decayDaughters[idx(Cascade::kV0)].radius(), decayDaughters[idx(Cascade::kV0)].pt()); - histos.fill(HIST("Xi/hPosDauDecayRadius"), decayDaughters[idx(Cascade::kPos)].radius(), decayDaughters[idx(Cascade::kPos)].pt()); - histos.fill(HIST("Xi/hNegDauDecayRadius"), decayDaughters[idx(Cascade::kNeg)].radius(), decayDaughters[idx(Cascade::kNeg)].pt()); - break; - - case PDG_t::kXiPlusBar: - if (!checkDecayChannel(PDG_t::kXiPlusBar, decayDaughters)) { - break; - } - histos.fill(HIST("AntiXi/hGenAntiXi"), particle.pt()); - histos.fill(HIST("AntiXi/hBachDauDecayRadius"), decayDaughters[idx(AntiCascade::kBach)].radius(), decayDaughters[idx(AntiCascade::kBach)].pt()); - histos.fill(HIST("AntiXi/hV0DauDecayRadius"), decayDaughters[idx(AntiCascade::kV0)].radius(), decayDaughters[idx(AntiCascade::kV0)].pt()); - histos.fill(HIST("AntiXi/hPosDauDecayRadius"), decayDaughters[idx(AntiCascade::kPos)].radius(), decayDaughters[idx(AntiCascade::kPos)].pt()); - histos.fill(HIST("AntiXi/hNegDauDecayRadius"), decayDaughters[idx(AntiCascade::kNeg)].radius(), decayDaughters[idx(AntiCascade::kNeg)].pt()); - break; - - case PDG_t::kOmegaMinus: - if (!checkDecayChannel(PDG_t::kOmegaMinus, decayDaughters)) { - break; - } - histos.fill(HIST("Omega/hGenOmega"), particle.pt()); - histos.fill(HIST("Omega/hBachDauDecayRadius"), decayDaughters[idx(Cascade::kBach)].radius(), decayDaughters[idx(Cascade::kBach)].pt()); - histos.fill(HIST("Omega/hV0DauDecayRadius"), decayDaughters[idx(Cascade::kV0)].radius(), decayDaughters[idx(Cascade::kV0)].pt()); - histos.fill(HIST("Omega/hPosDauDecayRadius"), decayDaughters[idx(Cascade::kPos)].radius(), decayDaughters[idx(Cascade::kPos)].pt()); - histos.fill(HIST("Omega/hNegDauDecayRadius"), decayDaughters[idx(Cascade::kNeg)].radius(), decayDaughters[idx(Cascade::kNeg)].pt()); - break; - - case PDG_t::kOmegaPlusBar: - if (!checkDecayChannel(PDG_t::kOmegaPlusBar, decayDaughters)) { - break; - } - histos.fill(HIST("AntiOmega/hGenAntiOmega"), particle.pt()); - histos.fill(HIST("AntiOmega/hBachDauDecayRadius"), decayDaughters[idx(AntiCascade::kBach)].radius(), decayDaughters[idx(AntiCascade::kBach)].pt()); - histos.fill(HIST("AntiOmega/hV0DauDecayRadius"), decayDaughters[idx(AntiCascade::kV0)].radius(), decayDaughters[idx(AntiCascade::kV0)].pt()); - histos.fill(HIST("AntiOmega/hPosDauDecayRadius"), decayDaughters[idx(AntiCascade::kPos)].radius(), decayDaughters[idx(AntiCascade::kPos)].pt()); - histos.fill(HIST("AntiOmega/hNegDauDecayRadius"), decayDaughters[idx(AntiCascade::kNeg)].radius(), decayDaughters[idx(AntiCascade::kNeg)].pt()); - break; - - default: - break; - } - } + if (start >= stop) { + return; + } - int daughtersIdSlice[2]; - if (canDecay(particle.pdgCode())) { - daughtersIdSlice[0] = idx(mcParticles.size() + nStoredDaughters); - daughtersIdSlice[1] = idx(mcParticles.size() + nStoredDaughters + decayDaughters.size()); - } else { - daughtersIdSlice[0] = idx(particle.daughtersIds()[0]); - daughtersIdSlice[1] = idx(particle.daughtersIds()[1]); + int ndau = 0; + for (int i = start; i < stop; ++i) { + o2::upgrade::OTFParticle& particle = allParticles[i]; + if (particle.isFromMcParticles()) { + particle.setBitOn(o2::upgrade::DecayerBits::IsPrimary); + particle.setBitOn(o2::upgrade::DecayerBits::IsAlive); } - mDecayDaughters.emplace(index, decayDaughters); - nStoredDaughters += decayDaughters.size(); + if (!canDecay(particle)) { + continue; + } - const float phi = o2::constants::math::PI + std::atan2(-1.0f * particle.py(), -1.0f * particle.px()); - float eta; // As https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1922 - const float pt = std::sqrt(particle.px() * particle.px() + particle.py() * particle.py()); - const float p = std::sqrt(particle.px() * particle.px() + particle.py() * particle.py() + particle.pz() * particle.pz()); - float y; // As https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1943 + particle.setBitOff(o2::upgrade::DecayerBits::IsAlive); + std::vector decayStack = decayer.decayParticle(particle, pdgDB); + if (decayStack.empty()) { + continue; + } - if ((p - particle.pz()) < Tolerance) { - eta = (particle.pz() < 0.0f) ? -100.0f : 100.0f; + const float decayRadius = decayer.getDecayRadius(); + const float trackVelocity = o2::upgrade::computeParticleVelocity(particle.p(), pdgDB->GetParticle(particle.pdgCode())->Mass()); + const int charge = static_cast(pdgDB->GetParticle(particle.pdgCode())->Charge() / 3); + float trackLength{-1.f}; + if (charge == 0) { + const float dx = particle.vx() - decayer.getSecondaryVertexX(); + const float dy = particle.vy() - decayer.getSecondaryVertexY(); + const float dz = particle.vz() - decayer.getSecondaryVertexZ(); + trackLength = std::hypot(dx, dy, dz); } else { - eta = 0.5f * std::log((p + particle.pz()) / (p - particle.pz())); + o2::track::TrackParCov o2track; + o2::upgrade::convertOTFParticleToO2Track(particle, o2track, pdgDB); + trackLength = o2::upgrade::computeTrackLength(o2track, decayRadius, magneticField); } - if ((particle.e() - particle.pz()) < Tolerance) { - y = (particle.pz() < 0.0f) ? -100.0f : 100.0f; - } else { - y = 0.5f * std::log((particle.e() + particle.pz()) / (particle.e() - particle.pz())); + particle.setDecayRadius(std::hypot(decayer.getSecondaryVertexX(), decayer.getSecondaryVertexY())); + const float trackTimeNS = trackLength / trackVelocity * PicoToNano; + particle.setIndicesDaughter(particlesInDataframe - indexOffset + allParticles.size(), particlesInDataframe - indexOffset + allParticles.size() + (decayStack.size() - 1)); + for (auto& daughter : decayStack) { + daughter.setIndicesMother(particlesInDataframe - indexOffset + i, particlesInDataframe - indexOffset + i); + daughter.setCollisionId(particle.collisionId()); + daughter.setBitOn(o2::upgrade::DecayerBits::ProducedByDecayer); + daughter.setBitOn(o2::upgrade::DecayerBits::IsAlive); + daughter.setBitOff(o2::upgrade::DecayerBits::IsPrimary); + daughter.setProductionTime(particle.vt() + trackTimeNS); + allParticles.push_back(daughter); } + ndau += decayStack.size(); + } + + decayParticles(stop, stop + ndau); + } - // TODO: Particle status code - // TODO: Expression columns - auto mothers = particle.mothersIds(); - int mother0 = mothers.size() > 0 ? mothers[0] : -1; - int mother1 = mothers.size() > 1 ? mothers[1] : mother0; - mcParticlesAlice3.push_back(McParticleAlice3{particle.mcCollisionId(), particle.pdgCode(), particle.statusCode(), - particle.flags(), mother0, mother1, - daughtersIdSlice[0], daughtersIdSlice[1], particle.weight(), - particle.px(), particle.py(), particle.pz(), particle.e(), - particle.vx(), particle.vy(), particle.vz(), particle.vt(), - phi, eta, pt, p, y, !canDecay(particle.pdgCode()), true}); + void process(aod::McCollisions_001From>::iterator const& collision, aod::McParticles_001From> const& mcParticles) + { + allParticles.clear(); + allParticles.reserve(mcParticles.size() * 2); + if (collision.globalIndex() == 0) { + particlesInDataframe = 0; + indexOffset = 0; } - int daughtersIdSlice[2] = {-1, -1}; - for (const auto& [index, decayDaughters] : mDecayDaughters) { - for (const auto& dau : decayDaughters) { - if (index >= mcParticles.size()) { - LOG(error) << "--- Index " << index << " out of bounds for mcParticles table of size " << mcParticles.size() << std::endl; - continue; - } - - const float phi = o2::constants::math::PI + std::atan2(-1.0f * dau.py(), -1.0f * dau.px()); - float eta; // Conditional as https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1922 - const float pt = std::sqrt(dau.px() * dau.px() + dau.py() * dau.py()); - const float p = std::sqrt(dau.px() * dau.px() + dau.py() * dau.py() + dau.pz() * dau.pz()); - float y; // Conditional as https://github.com/AliceO2Group/AliceO2/blob/dev/Framework/Core/include/Framework/AnalysisDataModel.h#L1943 - - if ((p - dau.pz()) < Tolerance) { - eta = (dau.pz() < 0.0f) ? -100.0f : 100.0f; - } else { - eta = 0.5f * std::log((p + dau.pz()) / (p - dau.pz())); - } - - if ((dau.e() - dau.pz()) < Tolerance) { - y = (dau.pz() < 0.0f) ? -100.0f : 100.0f; - } else { - y = 0.5f * std::log((dau.e() + dau.pz()) / (dau.e() - dau.pz())); - } - - switch (dau.pdgCode()) { - case PDG_t::kElectron: - histos.fill(HIST("Secondaries/hGenEl"), pt); - break; - - case PDG_t::kMuonMinus: - histos.fill(HIST("Secondaries/hGenMu"), pt); - break; - - case PDG_t::kPiPlus: - histos.fill(HIST("Secondaries/hGenPi"), pt); - break; - - case PDG_t::kKPlus: - histos.fill(HIST("Secondaries/hGenKa"), pt); - break; - - case PDG_t::kProton: - histos.fill(HIST("Secondaries/hGenPr"), pt); - break; - - default: - break; - } - - // TODO: Particle status code - // TODO: Expression columns - // TODO: vt - auto mother = mcParticles.rawIteratorAt(index); - mcParticlesAlice3.push_back(McParticleAlice3{mother.mcCollisionId(), dau.pdgCode(), 1, - -1, index, index, daughtersIdSlice[0], daughtersIdSlice[1], mother.weight(), - dau.px(), dau.py(), dau.pz(), dau.e(), - dau.vx(), dau.vy(), dau.vz(), mother.vt(), - phi, eta, pt, p, y, dau.isAlive(), false}); - } + // Reproduce collision table to have AOD origin + tableMcCollisions(collision.bcId(), collision.generatorsID(), + collision.posX(), collision.posY(), collision.posZ(), collision.t(), + collision.weight(), collision.impactParameter(), collision.eventPlaneAngle()); + + // First we copy the particles from the table into a vector that is extendable + for (const auto& particle : mcParticles) { + allParticles.emplace_back(o2::upgrade::OTFParticle{particle}); } - for (const auto& particle : mcParticlesAlice3) { - if (particle.hasNaN()) { + // Do all decays + decayParticles(0, allParticles.size()); + + // Fill output table + for (auto& otfParticle : allParticles) { + otfParticle.setIndexOffset(indexOffset); + if (otfParticle.hasNaN()) { histos.fill(HIST("hNaNBookkeeping"), 1); - continue; + } else { + histos.fill(HIST("hNaNBookkeeping"), 0); } - histos.fill(HIST("hNaNBookkeeping"), 0); - std::span motherSpan(particle.mothersIds, 2); - tableMcParticlesWithDau(particle.collisionId, particle.pdgCode, particle.statusCode, - particle.flags, motherSpan, particle.daughtersIdSlice, particle.weight, - particle.px, particle.py, particle.pz, particle.e, - particle.vx, particle.vy, particle.vz, particle.vt, - particle.phi, particle.eta, particle.pt, particle.p, particle.y, - particle.isAlive, particle.isPrimary); + tableOTFParticleExtras(otfParticle.getBitsValue(), otfParticle.decayRadius()); + tableMcParticles(tableMcCollisions.lastIndex(), otfParticle.pdgCode(), otfParticle.statusCode(), otfParticle.flags(), + otfParticle.getMotherSpan(), otfParticle.getDaughters().data(), otfParticle.weight(), + otfParticle.px(), otfParticle.py(), otfParticle.pz(), otfParticle.e(), + otfParticle.vx(), otfParticle.vy(), otfParticle.vz(), otfParticle.vt()); } + + // Particles for later collisions in df's needs to have thier mother + // and daughter indices adjusted since their global index will be + // shifted due to the appending of decay products + indexOffset += (allParticles.size() - mcParticles.size()); + particlesInDataframe += allParticles.size(); } }; +struct OnTheFlyDecayerExtensionSpawner { + Spawns spawnMcParticlesExtensions; + void init(o2::framework::InitContext&) {} +}; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx new file mode 100644 index 00000000000..689da6f6e83 --- /dev/null +++ b/ALICE3/TableProducer/OTF/onTheFlyMidPid.cxx @@ -0,0 +1,1123 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file onTheFlyMidPid.cxx +/// +/// \brief This task goes straight from a combination of track table and mcParticles +/// and a custom MID configuration to a table of muon id for the particles +/// being analysed. +/// +/// \author David Dobrigkeit Chinellato, UNICAMP +/// \author Antonio Ortiz, UNAM +/// \since May 22, 2024 +/// + +#include "GeometryContainer.h" + +#include "ALICE3/Core/FlatTrackSmearer.h" +#include "ALICE3/Core/TrackUtilities.h" +#include "ALICE3/DataModel/OTFCollision.h" +#include "ALICE3/DataModel/OTFMID.h" +#include "Common/Core/trackUtilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; + +struct OnTheFlyMidPid { + Produces upgradeMidMC; + Produces upgradeMid; + Produces upgradeMidExpected; + + // necessary for particle charges + Service pdgDatabase; + // Necessary for LUTs + Service ccdb; + + // these are the settings governing the MID layer to be used + // in the evolution of this effort + struct : ConfigurableGroup { + Configurable considerEventTime{"considerEventTime", true, "flag to consider event time"}; + // Parametrization of muon/pion absortion in iron + Configurable magRadiusC{"magRadiusC", 160., "Magnet coil radius (cm)"}; + Configurable absRadius1{"absRadius1", 220., "Absorber inner radius (cm)"}; + Configurable absRadius2{"absRadius2", 290., "Absorber outer radius (cm)"}; + Configurable absDensity{"absDensity", 7.874, "iron density (g/cm3)"}; + Configurable absLambdaPi{"absLambdaPi", 20.41, "illustrative effective interaction length (cm)"}; + Configurable absDeDx{"absDeDxMIP", 1.7, "dE/(rho dx) MIP MeV/g/cm2"}; + Configurable absFakeAt3{"absFakeAt3", 0.032, "fake-muon efficiency at 3 GeV/c from GEANT4+BDT abs(eta)<1.24, 70 cm"}; + Configurable absFakeAt10{"absFakeAt10", 0.08, "fake-muon efficiency at 10 GeV/c from GEANT4+BDT abs(eta)<1.24, 70 cm"}; + Configurable absSigmaEta{"absSigmaEta", 10.65, "user-configurable phenomenological eta width"}; + Configurable absPionFractionAt3{"absPionFractionAt3", 0.5, "parameter from GEANT4+BDT"}; // assumed, NOT measured + Configurable absEtaMax{"absEtaMax", 1.24, "max eta covered by detector"}; + Configurable midRadius{"midRadius", 302.5, "MID radius (cm)"}; + Configurable midLength{"midLength", 1000.0, "MID length (cm)"}; + Configurable> midCellDimension{"midCellDimension", {5.1, 5.1}, "MID cell dimension in Z and RPhi (cm)"}; + Configurable midFractionOfInactiveArea{"midFractionOfInactiveArea", (0.1 / 5.1), "MID fraction of inactive area"}; + Configurable midTimeReso{"midTimeReso", 1500, "MID time error (ps)"}; + Configurable nStepsLIntegrator{"nStepsLIntegrator", 200, "number of steps in length integrator"}; + Configurable multiplicityEtaRange{"multiplicityEtaRange", 0.800000012, "eta range to compute the multiplicity"}; + Configurable flagIncludeTrackTimeRes{"flagIncludeTrackTimeRes", true, "flag to include or exclude track time resolution"}; + Configurable flagMIDLoadDelphesLUTs{"flagMIDLoadDelphesLUTs", false, "flag to load Delphes LUTs for tracking correction (use recoTrack parameters if false)"}; + } simConfig; + + struct : ConfigurableGroup { + Configurable doQAplots{"doQAplots", true, "do basic velocity plot qa"}; + Configurable doSeparationVsPt{"doSeparationVsPt", true, "Produce plots vs pt or p"}; + Configurable nBinsBeta{"nBinsBeta", 2200, "number of bins in beta"}; + Configurable nBinsP{"nBinsP", 80, "number of bins in momentum/pT depending on doSeparationVsPt"}; + Configurable nBinsTrackLength{"nBinsTrackLength", 500, "number of bins in track length"}; + Configurable nBinsNsigmaWrongSpecies{"nBinsNsigmaWrongSpecies", 200, "number of bins in Nsigma plot (wrong species)"}; + Configurable minNsigmaRange{"minNsigmaRange", -10, "lower limit for the nsigma axis"}; + Configurable maxNsigmaRange{"maxNsigmaRange", +10, "upper limit for the nsigma axis"}; + Configurable nBinsDeltaCorrectSpecies{"nBinsDeltaCorrectSpecies", 200, "number of bins in Delta plot (correct speies)"}; + Configurable nBinsDeltaWrongSpecies{"nBinsDeltaWrongSpecies", 200, "number of bins in Delta plot (wrong species)"}; + Configurable nBinsTrackDeltaLength{"nBinsTrackDeltaLength", 100, "number of bins in delta track length"}; + Configurable nBinsNsigmaCorrectSpecies{"nBinsNsigmaCorrectSpecies", 200, "number of bins in Nsigma plot (correct speies)"}; + Configurable minDeltaRange{"minDeltaRange", -100, "lower limit for the nsigma axis"}; + Configurable maxDeltaRange{"maxDeltaRange", +100, "upper limit for the nsigma axis"}; + Configurable nBinsTimeRes{"nBinsTimeRes", 400, "number of bins plots time resolution"}; + Configurable nBinsRelativeEtaPt{"nBinsRelativeEtaPt", 400, "number of bins plots pt and eta relative errors"}; + Configurable nBinsEta{"nBinsEta", 400, "number of bins plot relative eta error"}; + Configurable nBinsMult{"nBinsMult", 200, "number of bins in multiplicity"}; + Configurable maxMultRange{"maxMultRange", 1000.f, "upper limit in multiplicity plots"}; + } plotsConfig; + + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + + // Track smearer array, one per geometry + // std::vector> mSmearer; + std::vector> mSmearer; + // needed: random number generator for smearing + TRandom3 pRandomNumberGenerator; + + // for handling basic QA histograms if requested + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + OutputObj listEfficiency{"efficiency"}; + + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; + void init(o2::framework::InitContext& initContext) + { + mGeoContainer.setCcdbManager(ccdb.operator->()); + mGeoContainer.init(initContext); + + const int nGeometries = mGeoContainer.getNumberOfConfigurations(); + mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); + + pRandomNumberGenerator.SetSeed(0); // fully randomize + if (simConfig.flagMIDLoadDelphesLUTs) { + for (int icfg = 0; icfg < nGeometries; ++icfg) { + mSmearer.emplace_back(std::make_unique()); + mSmearer[icfg]->setCcdbManager(ccdb.operator->()); + std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); + for (const auto& entry : globalConfiguration) { + int pdg = 0; + if (entry.first.find("lut") != 0) { + continue; + } + if (entry.first.find("lutEl") != std::string::npos) { + pdg = kElectron; + } else if (entry.first.find("lutMu") != std::string::npos) { + pdg = kMuonMinus; + } else if (entry.first.find("lutPi") != std::string::npos) { + pdg = kPiPlus; + } else if (entry.first.find("lutKa") != std::string::npos) { + pdg = kKPlus; + } else if (entry.first.find("lutPr") != std::string::npos) { + pdg = kProton; + } else if (entry.first.find("lutDe") != std::string::npos) { + pdg = o2::constants::physics::kDeuteron; + } else if (entry.first.find("lutTr") != std::string::npos) { + pdg = o2::constants::physics::kTriton; + } else if (entry.first.find("lutHe3") != std::string::npos) { + pdg = o2::constants::physics::kHelium3; + } else if (entry.first.find("lutAl") != std::string::npos) { + pdg = o2::constants::physics::kAlpha; + } + + std::string filename = entry.second; + if (pdg == 0) { + LOG(fatal) << "Unknown LUT entry " << entry.first << " for global configuration"; + } + LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided file '" << filename << "'"; + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + // strip from leading/trailing spaces + filename.erase(0, filename.find_first_not_of(" ")); + filename.erase(filename.find_last_not_of(" ") + 1); + if (filename.empty()) { + LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; + } + bool success = mSmearer[icfg]->loadTable(pdg, filename.c_str()); + if (!success) { + LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << filename; + } + } + } + } + + if (plotsConfig.doQAplots) { + const AxisSpec axisdNdeta{plotsConfig.nBinsMult, 0.0f, plotsConfig.maxMultRange, Form("dN/d#eta in |#eta| < %f", simConfig.multiplicityEtaRange.value)}; + + histos.add("h1dNdeta", "h2dNdeta", kTH1F, {axisdNdeta}); + histos.add("h2dEventTime", "h2dEventTime", kTH2F, {{2000, -10000, 10000, "computed"}, {2000, -10000, 10000, "generated"}}); + histos.add("h1dEventTimegen", "h1dEventTimegen", kTH1F, {{200, -1000, 1000, "generated"}}); + histos.add("h1dEventTimerec", "h1dEventTimerec", kTH1F, {{2000, -10000, 10000, "computed"}}); + histos.add("h1dEventTimedelta", "h1dEventTimedelta", kTH1F, {{200, -1000, 1000, "generated - computed"}}); + histos.add("h2dEventTimeres", "h2dEventTimeres", kTH2F, {axisdNdeta, {300, 0, 300, "resolution"}}); + listEfficiency.setObject(new THashList); + listEfficiency->Add(new TEfficiency("effEventTime", "effEventTime", plotsConfig.nBinsMult, 0.0f, plotsConfig.maxMultRange)); + + const AxisSpec axisMomentum{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisRigidity{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p} / |#it{z}| (GeV/#it{c})"}; + const AxisSpec axisVelocity{static_cast(plotsConfig.nBinsBeta), 0.0f, +1.1f, "Measured #beta"}; + const AxisSpec axisMomentumSmall{static_cast(plotsConfig.nBinsP), 0.0f, +1.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisTrackLength{static_cast(plotsConfig.nBinsTrackLength), 0.0f, 1000.0f, "Track length (cm)"}; + const AxisSpec axisTrackDeltaLength{static_cast(plotsConfig.nBinsTrackDeltaLength), 0.0f, 30.0f, "Delta Track length (cm)"}; + + histos.add("h2dVelocityVsMomentumInner", "h2dVelocityVsMomentumInner", kTH2F, {axisMomentum, axisVelocity}); + histos.add("h2dVelocityVsRigidityInner", "h2dVelocityVsRigidityInner", kTH2F, {axisRigidity, axisVelocity}); + histos.add("h2dTrackLengthInnerVsPt", "h2dTrackLengthInnerVsPt", kTH2F, {axisMomentum, axisTrackLength}); + histos.add("h2dTrackLengthInnerRecoVsPt", "h2dTrackLengthInnerRecoVsPt", kTH2F, {axisMomentum, axisTrackLength}); + + histos.add("h2dTrackLengthB2vsB0", "h2dDeltaTrackLengthInnerVsPt; for B=0; for B=2 T", kTH2F, {axisTrackLength, axisTrackLength}); + histos.add("h2HitMap", "h2HitMap", kTH2F, {{200, -simConfig.midLength / 2, simConfig.midLength / 2}, {1000, 0, simConfig.midRadius * 2 * M_PI}}); + histos.add("h2HitMapRec", "h2HitMapRec", kTH2F, {{200, -simConfig.midLength / 2, simConfig.midLength / 2}, {1000, 0, simConfig.midRadius * 2 * M_PI}}); + histos.add("h2HitDeltaXY", "h2HitDeltaXY", kTH2F, {{200, -100, 100, "#Delta#it{x} (cm)"}, {200, -100, 100, "#Delta#it{y} (cm)"}}); + + const AxisSpec axisPt{static_cast(plotsConfig.nBinsP), 0.0f, +10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPtRes{1000, -1.0f, +1.0f, "(#it{p}_{T}^{gen}-#it{p}_{T}^{rec})/#it{p}_{T}^{gen}"}; + const AxisSpec axisEta{static_cast(plotsConfig.nBinsEta), -2.0f, +2.0f, "#eta"}; + const AxisSpec axisRelativePt{static_cast(plotsConfig.nBinsRelativeEtaPt), 0.0f, +10.0f, "#it{#sigma_{p_{T}}} / #it{p_{T}} (%)"}; + const AxisSpec axisRelativeEta{static_cast(plotsConfig.nBinsRelativeEtaPt), 0.0f, +10.0f, "#it{#sigma_{#eta}} / #it{#eta} (%)"}; + histos.add("h2dEtaVsPt", "h2dTrackLengthInnerRecoVsPt", kTH2F, {axisMomentum, axisEta}); + histos.add("h2dEtaVsP", "h2dTrackLengthInnerRecoVsP", kTH2F, {axisMomentum, axisEta}); + + const AxisSpec axisDelta{plotsConfig.nBinsDeltaCorrectSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta#{t} (ps)"}; + const AxisSpec axisNsigmaCorrect{plotsConfig.nBinsNsigmaCorrectSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, "N#sigma - True "}; + const AxisSpec axisDeltaWrong{plotsConfig.nBinsDeltaWrongSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta - True "}; + const AxisSpec axisX{plotsConfig.doSeparationVsPt.value ? axisPt : axisMomentum}; + // const AxisSpec axisNSigma{axisNsigmaCorrect}; + + const AxisSpec axisTrackTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +3000.0f, "Track time resolution (ps)"}; + const AxisSpec axisTotalTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +3000.0f, "Total time resolution (ps)"}; + + histos.add("h1PtRes", "", kTH2F, {axisMomentum, axisPtRes}); + histos.add("h2EkinVsEloss", "", kTH2F, {{200, 0.0f, 2000.f, "#it{E}_{loss} (MeV)"}, {200, 0.0f, 20000.f, "KE (MeV)"}}); + } + } + + struct MIDEfficiency { + private: + const float layerRadius; + const float layerLength; + const float cellDimensionZ; + const float cellDimensionRPhi; + const float fractionInactive; + const float magField; + + TAxis* axisZ = nullptr; + TAxis* axisRPhi = nullptr; + TAxis* axisInCellZ = nullptr; + TAxis* axisInCellRPhi = nullptr; + + TH2F* hHitMapInCell = nullptr; + TH2F* hHitMapInCellBefore = nullptr; + TH2F* hHitMap = nullptr; + + public: + ~MIDEfficiency() + { + if (0) { + hHitMap->SaveAs(Form("/tmp/%s.png", hHitMap->GetName())); + hHitMapInCell->SaveAs(Form("/tmp/%s.png", hHitMapInCell->GetName())); + hHitMapInCellBefore->SaveAs(Form("/tmp/%s.png", hHitMapInCellBefore->GetName())); + } + + delete axisZ; + delete axisRPhi; + delete axisInCellZ; + delete axisInCellRPhi; + delete hHitMap; + delete hHitMapInCell; + delete hHitMapInCellBefore; + } + + MIDEfficiency(const MIDEfficiency&) = delete; + MIDEfficiency& operator=(const MIDEfficiency&) = delete; + + MIDEfficiency(float r, float l, std::array pDimensions, float fIA, float m) + : layerRadius(r), layerLength(l), cellDimensionZ(pDimensions[0]), cellDimensionRPhi(pDimensions[1]), fractionInactive(fIA), magField(m) + { + // Assuming square cells for simplicity + const float circumference = 2.0f * M_PI * layerRadius; + axisZ = new TAxis(static_cast(layerLength / cellDimensionZ), -layerLength / 2, layerLength); + axisRPhi = new TAxis(static_cast(circumference / cellDimensionRPhi), 0.f, circumference); + + const float inactiveBorderRPhi = cellDimensionRPhi * std::sqrt(fractionInactive) / 2; + const float inactiveBorderZ = cellDimensionZ * std::sqrt(fractionInactive) / 2; + const double arrayRPhi[4] = {-cellDimensionRPhi / 2, -cellDimensionRPhi / 2 + inactiveBorderRPhi, cellDimensionRPhi / 2 - inactiveBorderRPhi, cellDimensionRPhi / 2}; + for (int i = 0; i < 4; i++) { + LOG(info) << "arrayRPhi[" << i << "] = " << arrayRPhi[i]; + } + axisInCellRPhi = new TAxis(3, arrayRPhi); + const double arrayZ[4] = {-cellDimensionZ / 2, -cellDimensionZ / 2 + inactiveBorderZ, cellDimensionZ / 2 - inactiveBorderZ, cellDimensionZ / 2}; + for (int i = 0; i < 4; i++) { + LOG(info) << "arrayZ[" << i << "] = " << arrayZ[i]; + } + axisInCellZ = new TAxis(3, arrayZ); + + hHitMap = new TH2F(Form("hHitMap_R%.0f", layerRadius), "HitMap;z (cm); r#phi (cm)", 1000, -1000, 1000, 1000, -1000, 1000); + hHitMapInCell = new TH2F(Form("hHitMapInCell_R%.0f", layerRadius), "HitMapInCell;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); + hHitMapInCellBefore = new TH2F(Form("hHitMapInCellBefore_R%.0f", layerRadius), "HitMapInCell;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); + } + + bool isInMIDActiveArea(std::array hitPosition) + { + if (fractionInactive <= 0.0f) { + return true; + } + if (fractionInactive >= 1.0f) { + return false; + } + + // Convert 3D position to cylindrical coordinates for area calculation + const float phi = std::atan2(hitPosition[1], hitPosition[0]); + const float rphi = phi * layerRadius; + const float z = hitPosition[2]; + const float r = std::sqrt(hitPosition[0] * hitPosition[0] + hitPosition[1] * hitPosition[1]); + + // Check if hit is within layer geometric acceptance + if (std::abs(layerRadius - r) > 10.f) { + LOG(debug) << "Hit out of MID layer acceptance: r=" << r << " cm with respect to the layer radius " << layerRadius; + return false; + } + if (std::abs(z) > layerLength / 2.0f) { + LOG(debug) << "Hit out of MID layer acceptance: z=" << z << " cm with respect to the layer length " << layerLength; + return false; + } + + const int cellIndexPhi = axisRPhi->FindBin(rphi); + const int cellIndexZ = axisZ->FindBin(z); + + if (cellIndexPhi <= 0 || cellIndexPhi > axisRPhi->GetNbins() || cellIndexZ <= 0 || cellIndexZ > axisZ->GetNbins()) { + return false; + } + // Calculate local position within the pixel + const float localRPhi = (rphi - axisRPhi->GetBinCenter(cellIndexPhi)); + const float localZ = (z - axisZ->GetBinCenter(cellIndexZ)); + + // The difference between the hit and the pixel position cannot be greater than the size of the pixel + if (std::abs(localRPhi - axisRPhi->GetBinCenter(cellIndexPhi)) > axisRPhi->GetBinWidth(cellIndexPhi)) { + // LOG(warning) << "Local hit difference in phi is bigger than the pixel size"; + } + if (std::abs(localZ - axisZ->GetBinCenter(cellIndexZ)) > axisZ->GetBinWidth(cellIndexZ)) { + // LOG(warning) << "Local hit difference in z is bigger than the pixel size"; + } + hHitMapInCellBefore->Fill(localZ, localRPhi); + switch (axisInCellRPhi->FindBin(localRPhi)) { + case 0: + case 1: + case 3: + case 4: + return false; + } + switch (axisInCellZ->FindBin(localZ)) { + case 0: + case 1: + case 3: + case 4: + return false; + } + hHitMapInCell->Fill(localZ, localRPhi); + hHitMap->Fill(z, rphi); + return true; + } + + /// Check if a track hits the active area (convenience function) + /// \param track the track parameters for automatic hit position calculation + /// \return true if the hit is in the active area + bool isTrackInActiveArea(const o2::track::TrackParCov& track) + { + if (fractionInactive <= 0.f) { + return true; + } + float x, y, z; + if (!track.getXatLabR(layerRadius, x, magField)) { + LOG(debug) << "Could not propagate track to TOF layer at radius " << layerRadius << " cm"; + return false; + } + bool b; + ROOT::Math::PositionVector3D hit = track.getXYZGloAt(x, magField, b); + if (!b) { + LOG(debug) << "Could not get hit position at radius " << layerRadius << " cm"; + return false; + } + hit.GetCoordinates(x, y, z); + return isInMIDActiveArea(std::array{x, y, z}); + } + }; + + bool isInMIDActiveArea(const o2::track::TrackParCov& track) + { + static MIDEfficiency midLayer(simConfig.midRadius, simConfig.midLength, simConfig.midCellDimension, simConfig.midFractionOfInactiveArea, mMagneticField); + return midLayer.isTrackInActiveArea(track); + } + + struct TracksWithTime { + TracksWithTime(int pdgCode, + std::pair midTime, + std::pair trackLengthMID, + std::pair trackLengthInIron, + std::pair, std::array> posInMid, + std::pair momentum, + std::pair pseudorapidity, + float noSmearingPt) + : mPdgCode(pdgCode), + mMIDTime(midTime), + mTrackLengthMID(trackLengthMID), + mTrackLengthInIron(trackLengthInIron), + mPosInMid(posInMid), + mMomentum(momentum), + mPseudorapidity{pseudorapidity}, + mNoSmearingPt{noSmearingPt} {} + + int mPdgCode; + std::pair mMIDTime; // Measured time and expected resolution at the MID [ps] + std::pair mTrackLengthMID; // Measured and generated length at the MID [cm] + std::pair mTrackLengthInIron; // Measured and generated length within absorber [cm] + std::pair, std::array> mPosInMid; + std::pair mMomentum; // Measured momentum and uncertainty on the momentum [GeV/c] + std::pair mPseudorapidity; // Measured pseudorapidity and uncertainty on the pseudorapidity + float mNoSmearingPt; // No smearing pt + }; + + std::vector tracksWithTime; + bool eventTime(const std::vector& tracks, + std::array& tzero) + { + + float sum = 0.; + float sumw = 0.; + + // Todo: check the different mass hypothesis iteratively + for (const auto& track : tracks) { + auto pdgInfo = pdgDatabase->GetParticle(track.mPdgCode); + if (pdgInfo == nullptr) { + continue; + } + const float mass = pdgInfo->Mass(); + const float mass2 = mass * mass; + const float tof = track.mMIDTime.first; // [ps] + if (tof <= 0) { + continue; + } + const float etof = track.mMIDTime.second; // [ps] + const float length = track.mTrackLengthMID.first; // [cm] + float p = track.mMomentum.first; // [GeV/c] + p *= std::abs(pdgInfo->Charge()) / 3.; // Total momentum + const float ep = track.mMomentum.second; // [GeV/c] + const float p2 = p * p; + const float lengthOverC = length * o2::constants::physics::invLightSpeedCm2PS; + const float texp = lengthOverC / p * std::sqrt(mass2 + p2); + // LOG(info) << "TOF: " << tof << " " << etof << " vs " << texp; + const float etexp = lengthOverC * mass2 / p2 / std::sqrt(mass2 + p2) * ep; + const float sigma = std::sqrt(etexp * etexp + etof * etof); + const float deltat = tof - texp; + + const float w = 1. / (sigma * sigma); + + sum += w * deltat; + sumw += w; + } + + static constexpr float kMaxEventTimeResolution = 200.f; + if (sumw <= 0. || tracks.size() <= 1 || std::sqrt(1. / sumw) > kMaxEventTimeResolution) { + tzero[0] = 0.; // [ps] + tzero[1] = kMaxEventTimeResolution; // [ps] + return false; + } + + tzero[0] = sum / sumw; + tzero[1] = std::sqrt(1. / sumw); + return true; + } + + /// returns track time resolution + /// \param pt the transverse momentum of the tarck + /// \param eta the pseudorapidity of the tarck + /// \param trackPtResolution the absolute resolution on pt + /// \param trackEtaResolution the absolute resolution on eta + /// \param mass the mass of the particle + /// \param detRadius the radius of the cylindrical layer + /// \param magneticField the magnetic field (along Z) + double calculateTrackTimeResolutionAdvanced(const float pt, + const float eta, + const float trackPtResolution, + const float trackEtaResolution, + const float mass, + const float detRadius, + const float magneticField) + { + // Compute tracking contribution to timing using the error propagation formula + // Uses light speed in m/ps, magnetic field in T (*0.1 for conversion kGauss -> T) + float a0 = mass * mass; + float a1 = 0.299792458 * (0.1 * magneticField) * (0.01 * o2::constants::physics::LightSpeedCm2NS / 1e+3); + float a2 = (detRadius * 0.01) * (detRadius * 0.01) * (0.299792458) * (0.299792458) * (0.1 * magneticField) * (0.1 * magneticField) / 2.0; + float dtofOndPt = (std::pow(pt, 4) * std::pow(std::cosh(eta), 2) * std::acos(1.0 - a2 / std::pow(pt, 2)) - 2.0 * a2 * std::pow(pt, 2) * (a0 + std::pow(pt * std::cosh(eta), 2)) / std::sqrt(a2 * (2.0 * std::pow(pt, 2) - a2))) / (a1 * std::pow(pt, 3) * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + float dtofOndEta = std::pow(pt, 2) * std::sinh(eta) * std::cosh(eta) * std::acos(1.0 - a2 / std::pow(pt, 2)) / (a1 * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + float trackTimeResolution = std::hypot(std::fabs(dtofOndPt) * trackPtResolution, std::fabs(dtofOndEta) * trackEtaResolution); + return trackTimeResolution; + } + + /* + // ============================================================ + // Track length in a B = 0 region + // + // Calculates the distance from the CURRENT track position + // to the cylinder of radius "radius". + // ============================================================ + */ + float computeTrackLengthB0(const o2::track::TrackParCov& track, + const float radius) + { + // Current global position + std::array pos{}; + track.getXYZGlo(pos); + + // Current global momentum + std::array mom{}; + if (!track.getPxPyPzGlo(mom)) { + return -100.f; + } + + const float px = mom[0]; + const float py = mom[1]; + // const float pz = mom[2]; + + const float pt = std::hypot(px, py); + + if (pt <= 0.f) { + return -100.f; + } + + // Unit vector in transverse plane + const float ux = px / pt; + const float uy = py / pt; + + const float b = pos[0] * ux + pos[1] * uy; + const float c = pos[0] * pos[0] + + pos[1] * pos[1] - + radius * radius; + + const float discriminant = b * b - c; + + if (discriminant < 0.f) { + return -100.f; + } + + const float sqrtDisc = std::sqrt(discriminant); + + const float t1 = -b + sqrtDisc; + const float t2 = -b - sqrtDisc; + + // We want the outward/forward intersection + float t = -1.f; + + if (t1 > 0.f && t2 > 0.f) { + t = std::min(t1, t2); + } else if (t1 > 0.f) { + t = t1; + } else if (t2 > 0.f) { + t = t2; + } else { + return -100.f; + } + + return t * std::sqrt(1.f + track.getTgl() * track.getTgl()); + } + + /* + // ============================================================ + // MID piecewise track length + // + // B = +20.0 kG R < R0 + // B = 0.0 kG R0 < R < R1 + // B = -14.3 kG R1 < R < R2 + // + // Returns total track length from current track position to R3. + // ============================================================ + */ + float computeTrackLengthMID(const o2::track::TrackParCov& inputTrack, + float R0, + float R1, + float R2, + float R3, + std::array& posMID, + std::array& deltaMID, + float& deltaposInIron) + { + float B0_m = +20.0f; // kG + float B1_m = 0.0f; // kG + float B2_m = 0.0f; + B2_m = -B0_m * pow(R0, 2) / (pow(R2, 2) - pow(R1, 2)); // kG + float B3_m = 0.0f; // kG + + // Work with a copy + auto track = inputTrack; + + float totalLength = 0.f; + float xAtR = 0.f; + + // ========================================================== + // REGION 1 + // current position -> R0 + // B = +20 kG + // ========================================================== + + const float L0 = + o2::upgrade::computeTrackLength(track, R0, B0_m); + + if (L0 < 0.f) { + return -100.f; + } + + totalLength += L0; + + // Find local x corresponding to global radius R0 + if (!track.getXatLabR(R0, + xAtR, + B0_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R0 + if (!track.propagateTo(xAtR, B0_m)) { + return -100.f; + } + + // ========================================================== + // REGION 2 + // R0 -> R1 + // B = 0 + // ========================================================== + + const float L01 = computeTrackLengthB0(track, R1); + + if (L01 < 0.f) { + return -100.f; + } + + totalLength += L01; + + // Find local x at R1 assuming B = 0 + if (!track.getXatLabR(R1, + xAtR, + B1_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Straight-line propagation to R1 + if (!track.propagateTo(xAtR, B1_m)) { + return -100.f; + } + + // ========================================================== + // REGION 3 (absorber) + // R1 -> R2 + // B = -14.3 kG + // ========================================================== + + std::array pos1{}; + track.getXYZGlo(pos1); + + float L12 = + o2::upgrade::computeTrackLength(track, R2, B2_m); + + if (L12 < 0.f) { + return -100.f; + } + totalLength += L12; + deltaposInIron = L12; + // Find local x corresponding to global radius R2 + if (!track.getXatLabR(R2, + xAtR, + B2_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R2 + if (!track.propagateTo(xAtR, B2_m)) { + return -100.f; + } + + // ========================================================== + // REGION 2 + // R2 -> R3 + // B = 0 + // ========================================================== + + const float L23 = computeTrackLengthB0(track, R3); + + if (L23 < 0.f) { + return -100.f; + } + + totalLength += L23; + + // Find local x corresponding to global radius R3 + if (!track.getXatLabR(R3, + xAtR, + B3_m, + o2::track::DirOutward)) { + return -100.f; + } + + // Propagate track to R3 + if (!track.propagateTo(xAtR, B3_m)) { + return -100.f; + } + + // ========================================================== + // GLOBAL POSITION AT R3 + // ========================================================== + + std::array pos3{}; + track.getXYZGlo(pos3); + + posMID[0] = pos3[0]; + posMID[1] = pos3[1]; + posMID[2] = pos3[2]; + + // Displacement projections + deltaMID[0] = posMID[0] - pos1[0]; + deltaMID[1] = posMID[1] - pos1[1]; + deltaMID[2] = posMID[2] - pos1[2]; + + return totalLength; + } + // Multiple Coulomb scattering angle in iron + // + // momentum : particle momentum [GeV/c] + // thickness : iron thickness [cm] + // + // returns theta0 [rad] + + float moliereThetaIron(float momentum, float mass, float thickness) + { + float X0 = 1.757f; // radiation length of iron [cm] + // constexpr float mMuon = 0.105658f; // muon mass [GeV/c^2] + + if (momentum <= 0.f || thickness <= 0.f) { + return 0.f; + } + + // beta = p/E + const float energy = std::sqrt(momentum * momentum + + mass * mass); + + const float beta = momentum / energy; + + // Thickness in radiation lengths + const float xOverX0 = thickness / X0; + + // Highland approximation + const float theta0 = (0.0136f / (beta * momentum)) * sqrt(xOverX0) * (1.f + 0.038f * log(xOverX0)); + + return theta0; + } + float fPi(double p) + { + return p > 1. ? 1. - std::exp(-(p - 1.) / 1.5) : 0.; + } + float fMu(double p) + { + return p > 0.7 ? (1. - std::exp(-(p - 0.7) / 0.8)) * std::exp(-p / 3.) : 0.; + } + float refPi(double p) + { + if (p <= 1.) + return 0.; + float f3 = fPi(3.); + float mu10 = simConfig.absFakeAt3 * (1. - simConfig.absPionFractionAt3) * fMu(10.) / fMu(3.); + float slope = ((simConfig.absFakeAt10 - mu10) / (simConfig.absFakeAt3 * simConfig.absPionFractionAt3) / (fPi(10.) / f3) - 1.) / 7.; + return std::max(0., simConfig.absFakeAt3 * simConfig.absPionFractionAt3) * fPi(p) / f3 * (1. + slope * (p - 3.)); + } + float refMu(double p) + { + return simConfig.absFakeAt3 * (1. - simConfig.absPionFractionAt3) * fMu(p) / fMu(3.); + } + float decayAir(double p, double airCm) + { + if (p <= 0.) + return 0.; + return -std::expm1(-airCm * 0.13957039 / (p * 780.45)); // assume pion mass, ctau=780.45 cm + } + float survivalAir(double p, double airCm) + { + return 1. - decayAir(p, airCm); + } + // Range proxy: kinetic energy minus mean ionization loss in iron. + // Smoothed only to represent missing fluctuations; NOT a Geant4 range calculation. + float rangeTurnOn(double p, double ironCm, double mass) + { + if (p <= 0.) + return 0.; + float kinetic = std::sqrt(p * p + mass * mass) - mass; + float deficit = kinetic - (simConfig.absDensity * simConfig.absDeDx / 1000.) * ironCm; + float width = 0.10; // GeV, phenomenological + return 1. / (1. + std::exp(-std::max(-60., std::min(60., deficit / width)))); + } + float etaShape(double eta) + { + return std::exp(-0.5 * eta * eta / (simConfig.absSigmaEta * simConfig.absSigmaEta)); + } + float pion(float p, float eta, float airCm, float ironCm) + { + if (p <= 0.) + return 0.; + const float c = std::cosh(eta); + const float survivalRatio = survivalAir(p, airCm * c) / survivalAir(p, simConfig.absRadius1 * c); + const float hadronRatio = std::exp(-(ironCm - (simConfig.absRadius2 - simConfig.absRadius1)) * c / simConfig.absLambdaPi); + // Ratio to reference range at the SAME eta; reference shape is phenomenological. + const float rangeRatio = rangeTurnOn(p, ironCm * c, 0.13957039) / rangeTurnOn(p, (simConfig.absRadius2 - simConfig.absRadius1) * c, 0.13957039); // assume pion mass + return std::max(0., refPi(p) * etaShape(eta) * survivalRatio * hadronRatio * rangeRatio); + } + float muon(float p, float eta, float airCm, float ironCm) + { + if (p <= 0.) + return 0.; + const float c = std::cosh(eta); + const float d0 = decayAir(p, simConfig.absRadius1 * c); + const float decayRatio = d0 > 0. ? decayAir(p, airCm * c) / d0 : 0.; + // Effective secondary-muon momentum proxy: 0.8 * pion momentum. + // Secondary muons born in iron are NOT modeled; this is a phenomenological air-decay term. + const float pMu = 0.8 * p; + const float rangeRatio = rangeTurnOn(pMu, ironCm * c, 0.1056583755) / + rangeTurnOn(pMu, (simConfig.absRadius2 - simConfig.absRadius1) * c, 0.1056583755); + return std::max(0., refMu(p) * etaShape(eta) * decayRatio * rangeRatio); + } + float total(float p, float eta, float airCm, float ironCm) + { + return std::min(1., pion(p, eta, airCm, ironCm) + muon(p, eta, airCm, ironCm)); + } + float meanEta(float p, float airCm, float ironCm) + { + const int n = 1000; + float s = 0.; + for (int i = 0; i < n; ++i) { + float eta = -simConfig.absEtaMax + (i + .5) * 2. * simConfig.absEtaMax / n; + s += total(p, eta, airCm, ironCm); + } + return s / n; + } + + void process(soa::Join::iterator const& collision, + soa::Join const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + o2::dataformats::VertexBase pvVtx({collision.posX(), collision.posY(), collision.posZ()}, + {collision.covXX(), collision.covXY(), collision.covYY(), + collision.covXZ(), collision.covYZ(), collision.covZZ()}); + + std::array mcPvCov = {0.}; + o2::dataformats::VertexBase mcPvVtx({0.0f, 0.0f, 0.0f}, mcPvCov); + const float eventCollisionTimePS = (simConfig.considerEventTime.value ? collision.collisionTime() * 1e3 : 0.f); // convert ns to ps + if (collision.has_mcCollision()) { + auto mcCollision = collision.mcCollision(); + mcPvVtx.setX(mcCollision.posX()); + mcPvVtx.setY(mcCollision.posY()); + mcPvVtx.setZ(mcCollision.posZ()); + } // else remains untreated for now + + // First we compute the number of charged particles in the event if LUTs are loaded + float dNdEta = 0.f; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) + continue; + auto mcParticle = track.mcParticle(); + if (std::abs(mcParticle.eta()) > simConfig.multiplicityEtaRange) { + continue; + } + if (mcParticle.has_daughters()) { + continue; + } + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (!pdgInfo) { + // LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; + continue; + } + if (pdgInfo->Charge() == 0) { + continue; + } + dNdEta += 1.f; + } + if (plotsConfig.doQAplots) { + histos.fill(HIST("h1dNdeta"), dNdEta); + } + + tracksWithTime.clear(); // clear the vector of tracks with time to prepare the cache for the next event + tracksWithTime.reserve(tracks.size()); + // First loop to generate the arrival time of the particles to the MID layers + for (const auto& track : tracks) { + // first step: find precise arrival time (if any) + // --- convert track into perfect track + if (!track.has_mcParticle()) { // should always be OK but check please + upgradeMidMC(-999.f, -999.f); + continue; + } else { + LOG(debug) << "Track without mcParticle found!"; + } + const auto& mcParticle = track.mcParticle(); + float true_pt = mcParticle.pt(); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); + // get mass to calculate velocity + auto pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (pdgInfo == nullptr) { + LOG(error) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; + upgradeMidMC(-999.f, -999.f); + continue; + } + + float xPv = -100.f; + static constexpr float kTrkXThreshold = -99.f; // Threshold to consider a good propagation of the track + if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { + xPv = o2track.getX(); + } + float trackLengthMID = -1; + float trackLengthMIDB2 = -1; + std::array posMid{-999., -999., -999.}; + std::array delaPosMid{-999., -999., -999.}; + float trackLengthInIron = -999.; + if (xPv > kTrkXThreshold) { + trackLengthMIDB2 = o2::upgrade::computeTrackLength(o2track, simConfig.magRadiusC, mMagneticField); + trackLengthMID = computeTrackLengthMID(o2track, simConfig.magRadiusC, simConfig.absRadius1, simConfig.absRadius2, simConfig.midRadius, posMid, delaPosMid, trackLengthInIron); + // thetamolInIron = moliereThetaIron(o2track.getP(), pdgInfo->Mass(), trackLengthInIron); + } + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2dTrackLengthB2vsB0"), trackLengthMIDB2, trackLengthMID); + } + + // Check if the track hit a sensitive area of the MID + const bool activeMID = isInMIDActiveArea(o2track); + if (!activeMID) { + trackLengthMID = -999.f; + } else { + if (plotsConfig.doQAplots) { + float phi_tmp = std::atan2(posMid[1], posMid[0]); + if (phi_tmp < 0.f) { + phi_tmp += 2.f * M_PI; + } + histos.fill(HIST("h2HitMap"), posMid[2], simConfig.midRadius * phi_tmp); + } + } + + const float v = o2::upgrade::computeParticleVelocity(o2track.getP(), pdgInfo->Mass()); + const float expectedTimeMID = trackLengthMID > 0 ? trackLengthMID / v + eventCollisionTimePS : -999.f; // arrival time to the MID in ps + + upgradeMidMC(expectedTimeMID, trackLengthMID); + + // Smear with expected resolutions + const float measuredTimeMID = pRandomNumberGenerator.Gaus(expectedTimeMID, simConfig.midTimeReso); + + // Now we calculate the expected arrival time following certain mass hypotheses + // and the (imperfect!) reconstructed track parametrizations + float trackLengthRecoMID = -1; + auto recoTrack = getTrackParCov(track); + xPv = -100.f; + std::array posRecMid{-999., -999., -999.}; + std::array deltaPosRecMid{-999., -999., -999.}; + float trackLengthRecoInIron = -1.; + trackLengthRecoInIron = -1.; + trackLengthRecoMID = -1.; + if (recoTrack.propagateToDCA(pvVtx, mMagneticField)) { + xPv = recoTrack.getX(); + } + if (xPv > kTrkXThreshold) { + trackLengthRecoMID = computeTrackLengthMID(recoTrack, simConfig.magRadiusC, simConfig.absRadius1, simConfig.absRadius2, simConfig.midRadius, posRecMid, deltaPosRecMid, trackLengthRecoInIron); + // mimic scattering in iron + std::array mom{-999., -999., -999.}; + recoTrack.getPxPyPzGlo(mom); + for (int jj = 0; jj < 3; ++jj) { + float posRectmp = 0.0f; + float molieretmp = 0.0f; + molieretmp = moliereThetaIron(mom[jj], pdgInfo->Mass(), deltaPosRecMid[jj]); + posRectmp = pRandomNumberGenerator.Gaus(posRecMid[jj], molieretmp); + posRecMid[jj] = posRectmp; + } + if (plotsConfig.doQAplots) { + float phi_tmp = std::atan2(posRecMid[1], posRecMid[0]); + if (phi_tmp < 0.f) { + phi_tmp += 2.f * M_PI; + } + histos.fill(HIST("h2HitMapRec"), posRecMid[2], simConfig.midRadius * phi_tmp); + histos.fill(HIST("h2HitDeltaXY"), posRecMid[0] - posMid[0], posRecMid[1] - posMid[1]); + histos.fill(HIST("h1PtRes"), true_pt, (true_pt - recoTrack.getPt()) / true_pt); + } + } + // cache the track info needed for the event time calculation + // Reuse or emplace a new object in the vector + tracksWithTime.emplace_back(TracksWithTime{mcParticle.pdgCode(), + {measuredTimeMID, expectedTimeMID}, + {trackLengthRecoMID, trackLengthMID}, + {trackLengthRecoInIron, trackLengthInIron}, + {posRecMid, posMid}, + {recoTrack.getP(), recoTrack.getSigma1Pt2()}, + {recoTrack.getEta(), recoTrack.getSigmaTgl2()}, + o2track.getPt()}); + } + // Now we compute the event time for the tracks + + std::array tzero = {0.f, 0.f}; + bool etStatus = false; + if (simConfig.considerEventTime.value) { + etStatus = eventTime(tracksWithTime, tzero); + if (!etStatus) { + LOG(debug) << "Event time calculation failed with " << tracksWithTime.size() << " tracks with time and " << dNdEta << " charged particles"; + } + } + + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2dEventTime"), tzero[0], eventCollisionTimePS); + histos.fill(HIST("h1dEventTimegen"), eventCollisionTimePS); + histos.fill(HIST("h1dEventTimerec"), tzero[0]); + histos.fill(HIST("h2dEventTimeres"), dNdEta, tzero[1]); + if (etStatus) { + histos.fill(HIST("h1dEventTimedelta"), eventCollisionTimePS - tzero[0]); + } + dynamic_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); + } + + // Then we do a second loop to compute the measured quantities with the measured event time + int trackWithTimeIndex = 0; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { // should always be OK but check please + continue; + } + const auto& mcParticle = track.mcParticle(); + + const auto& trkWithTime = tracksWithTime[trackWithTimeIndex++]; + const float trackLengthRecoMID = trkWithTime.mTrackLengthMID.first; + const float trackLengthMID = trkWithTime.mTrackLengthMID.second; + const float measuredTimeMID = trkWithTime.mMIDTime.first; + const float expecTimeMID = trkWithTime.mMIDTime.second; + const float momentum = trkWithTime.mMomentum.first; + const float pseudorapidity = trkWithTime.mPseudorapidity.first; + const float noSmearingPt = trkWithTime.mNoSmearingPt; + std::array expectedPosMid = trkWithTime.mPosInMid.second; + std::array measuredPosMid = trkWithTime.mPosInMid.first; + float trackLengthInIronRec = trkWithTime.mTrackLengthInIron.first; + bool isMuCandidate = false; + if (abs(pseudorapidity) < simConfig.absEtaMax) { + if (trackLengthInIronRec > 0) { + float kineticEnergy = std::sqrt(pow(momentum, 2) + pow(0.1057, 2)) - 0.1057; + float energyloss = trackLengthInIronRec * simConfig.absDeDx * simConfig.absDensity / 1000.; + if (abs(trkWithTime.mPdgCode) == 13) { + if ((kineticEnergy - energyloss) > 0.1) { + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2EkinVsEloss"), energyloss * 1000., kineticEnergy * 1000.); + } + isMuCandidate = true; + } + } else if (abs(trkWithTime.mPdgCode) == 11) { + isMuCandidate = false; + } else { // mostly hadrons, parametrization from simulations using pions, GEANT4+BDT + float eff_rndm = pRandomNumberGenerator.Uniform(0., 1.); + float eff_had = total(momentum, pseudorapidity, trackLengthRecoMID - trackLengthInIronRec, trackLengthInIronRec); + if (eff_rndm < eff_had && (kineticEnergy - energyloss) > 0.1) { + if (plotsConfig.doQAplots) { + histos.fill(HIST("h2EkinVsEloss"), energyloss * 1000., kineticEnergy * 1000.); + } + isMuCandidate = true; + } + } + } + } + auto truePdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + float rigidity = momentum; // fallback to momentum if charge unknown + + // Use MC truth charge for rigidity calculation + if (truePdgInfo) { + const float trueCharge = std::abs(truePdgInfo->Charge()) / 3.0f; + if (trueCharge > 0) { + rigidity = momentum / trueCharge; + } + } + + if (plotsConfig.doQAplots) { + // unit conversion: length in cm, time in ps + const float midBeta = (trackLengthRecoMID / measuredTimeMID) / o2::constants::physics::LightSpeedCm2PS; + if (trackLengthRecoMID > 0) { + histos.fill(HIST("h2dVelocityVsMomentumInner"), momentum, midBeta); + histos.fill(HIST("h2dVelocityVsRigidityInner"), rigidity, midBeta); + histos.fill(HIST("h2dTrackLengthInnerVsPt"), noSmearingPt, trackLengthMID); + histos.fill(HIST("h2dTrackLengthInnerRecoVsPt"), noSmearingPt, trackLengthRecoMID); + histos.fill(HIST("h2dEtaVsPt"), noSmearingPt, pseudorapidity); + histos.fill(HIST("h2dEtaVsP"), momentum, pseudorapidity); + } + } + std::vector measuredPosMidVec(measuredPosMid.begin(), measuredPosMid.end()); + upgradeMid(tzero[0], tzero[1], measuredPosMidVec, measuredTimeMID, trackLengthRecoMID, isMuCandidate); + + std::vector expectedPosMidVec(expectedPosMid.begin(), expectedPosMid.end()); + upgradeMidExpected(expectedPosMidVec, expecTimeMID, trackLengthMID); + } + + if (trackWithTimeIndex != tracks.size()) { + LOG(fatal) << "Track with time index " << trackWithTimeIndex << " does not match the number of tracks " << tracks.size(); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx index 7f27c2512ea..31a915fc00d 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyRichPid.cxx @@ -78,9 +78,9 @@ struct OnTheFlyRichPid { Produces upgradeRichSignal; // necessary for particle charges - Service pdg; + Service pdgDatabase{}; // Necessary for LUTs - Service ccdb; + Service ccdb{}; // add rich-specific configurables here Configurable bRichNumberOfSectors{"bRichNumberOfSectors", 21, "barrel RICH number of sectors"}; @@ -132,7 +132,7 @@ struct OnTheFlyRichPid { o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // Track smearer array, one per geometry - std::vector> mSmearer; + std::vector> mSmearer; // needed: random number generator for smearing TRandom3 pRandomNumberGenerator; @@ -296,12 +296,12 @@ struct OnTheFlyRichPid { if (flagRICHLoadDelphesLUTs) { for (int icfg = 0; icfg < nGeometries; ++icfg) { - mSmearer.emplace_back(std::make_unique()); + mSmearer.emplace_back(std::make_unique()); mSmearer[icfg]->setCcdbManager(ccdb.operator->()); std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); for (const auto& entry : globalConfiguration) { int pdg = 0; - if (entry.first.find("lut") != 0) { + if (!entry.first.starts_with("lut")) { continue; } if (entry.first.find("lutEl") != std::string::npos) { @@ -359,11 +359,11 @@ struct OnTheFlyRichPid { histos.add("hSectorID", "hSectorID", kTH1F, {axisSector}); const int kNspec = 9; // electron, muon, pion, kaon, proton, deuteron, triton, helium3, alpha - std::string particleNames1[kNspec] = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "^{3}He", "#it{#alpha}"}; - std::string particleNames2[kNspec] = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; + const std::array particleNames1 = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "^{3}He", "#it{#alpha}"}; + const std::array particleNames2 = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; for (int iTrue = 0; iTrue < kNspec; iTrue++) { - std::string nameTitleBarrelTrackRes = "h2dBarrelAngularResTrack" + particleNames2[iTrue] + "VsP"; - std::string nameTitleBarrelTotalRes = "h2dBarrelAngularResTotal" + particleNames2[iTrue] + "VsP"; + const std::string nameTitleBarrelTrackRes = "h2dBarrelAngularResTrack" + particleNames2[iTrue] + "VsP"; + const std::string nameTitleBarrelTotalRes = "h2dBarrelAngularResTotal" + particleNames2[iTrue] + "VsP"; const AxisSpec axisTrackAngularRes{static_cast(nBinsAngularRes), 0.0f, +5.0f, "Track angular resolution - " + particleNames1[iTrue] + " (mrad)"}; const AxisSpec axisTotalAngularRes{static_cast(nBinsAngularRes), 0.0f, +5.0f, "Total angular resolution - " + particleNames1[iTrue] + " (mrad)"}; histos.add(nameTitleBarrelTrackRes.c_str(), nameTitleBarrelTrackRes.c_str(), kTH2F, {axisMomentum, axisTrackAngularRes}); @@ -371,7 +371,7 @@ struct OnTheFlyRichPid { } for (int iTrue = 0; iTrue < kNspec; iTrue++) { for (int iHyp = 0; iHyp < kNspec; iHyp++) { - std::string nameTitle = "h2dBarrelNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; + const std::string nameTitle = "h2dBarrelNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; if (iTrue == iHyp) { const AxisSpec axisNsigmaCorrect{static_cast(nBinsNsigmaCorrectSpecies), -10.0f, +10.0f, "N#sigma - True " + particleNames1[iTrue] + " vs " + particleNames1[iHyp] + " hypothesis"}; histos.add(nameTitle.c_str(), nameTitle.c_str(), kTH2F, {axisMomentum, axisNsigmaCorrect}); @@ -437,18 +437,14 @@ struct OnTheFlyRichPid { bool checkMagfieldLimit(o2::track::TrackParCov track, const float radius, const float magneticField) { o2::math_utils::CircleXYf_t trcCircle; - float sna, csa; + float sna = NAN, csa = NAN; track.getCircleParams(magneticField, trcCircle, sna, csa); // distance between circle centers (one circle is at origin -> easy) float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); // condition of circles touching - if not satisfied returned value if false - if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { - return true; - } else { - return false; - } + return centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius); } /// returns sector hit by the track (if any), -1 otherwise @@ -476,9 +472,8 @@ struct OnTheFlyRichPid { const float rSecRichSquared = rSecRich * rSecRich; const float zSecRichSquared = zSecRich * zSecRich; return (rSecRichSquared + zSecRichSquared) / (rSecRich + zSecRich / std::tan(polar)); - } else { - return kErrorValue; } + return kErrorValue; } /// returns Cherenkov angle in rad (above threshold) or bad flag (below threshold) @@ -516,10 +511,7 @@ struct OnTheFlyRichPid { // Require at least 3 photons on average for real angle reconstruction static constexpr float kMinPhotons = 3.f; - if (meanNumberofDetectedPhotons <= kMinPhotons) { - return false; - } - return true; + return meanNumberofDetectedPhotons > kMinPhotons; } /// returns linear interpolation @@ -552,15 +544,13 @@ struct OnTheFlyRichPid { float interpolatedResRing = interpolate(eta, kEtaSampling[lowerIndex], kEtaSampling[upperIndex], kResRingSamplingWithAbsWalls[lowerIndex], kResRingSamplingWithAbsWalls[upperIndex]); // std::cout << "Interpolated y value: " << interpolatedY << std::endl; return interpolatedResRing; - } else { - float interpolatedResRing = interpolate(eta, kEtaSampling[lowerIndex], kEtaSampling[upperIndex], kResRingSamplingWithoutAbsWalls[lowerIndex], kResRingSamplingWithoutAbsWalls[upperIndex]); - // std::cout << "Interpolated y value: " << interpolatedY << std::endl; - return interpolatedResRing; } - } else { - // std::cout << "Unable to interpolate. Target x value is outside the range of available data." << std::endl; - return kErrorValue; - } + float interpolatedResRing = interpolate(eta, kEtaSampling[lowerIndex], kEtaSampling[upperIndex], kResRingSamplingWithoutAbsWalls[lowerIndex], kResRingSamplingWithoutAbsWalls[upperIndex]); + // std::cout << "Interpolated y value: " << interpolatedY << std::endl; + return interpolatedResRing; + + } // std::cout << "Unable to interpolate. Target x value is outside the range of available data." << std::endl; + return kErrorValue; } /// To account border effects in bRICH @@ -579,7 +569,7 @@ struct OnTheFlyRichPid { iSecor = jSector; } } - if (flagSector == false) { + if (!flagSector) { return kErrorValue; // <-- Returning negative value } float rSecRich = radCenters[iSecor].X(); @@ -588,9 +578,9 @@ struct OnTheFlyRichPid { // float zSecTof = detCenters[iSecor].Z(); const float rSecRichSquared = rSecRich * rSecRich; const float zSecRichSquared = zSecRich * zSecRich; - const float radiusRipple = (rSecRichSquared + zSecRichSquared) / (rSecRich + zSecRich / std::tan(polar)); - const float zRipple = radiusRipple / std::tan(polar); - const float absZ = std::hypot(radiusRipple - rSecRich, zRipple - zSecRich); + const float radRipple = (rSecRichSquared + zSecRichSquared) / (rSecRich + zSecRich / std::tan(polar)); + const float zRipple = radRipple / std::tan(polar); + const float absZ = std::hypot(radRipple - rSecRich, zRipple - zSecRich); float fraction = 1.; if (tileZlength / 2. - absZ < radius) { fraction = fraction - (1. / o2::constants::math::PI) * std::acos((tileZlength / 2. - absZ) / radius); @@ -613,8 +603,9 @@ struct OnTheFlyRichPid { float extractRingAngularResolution(const float eta, const float n, const float nGas, const float thicknessRad, const float thicknessGas, const float pixelSize, const float thetaCherenkov, const float tileZlength) { // Check if input angle is error value - if (thetaCherenkov <= kErrorValue + 1) + if (thetaCherenkov <= kErrorValue + 1) { return kErrorValue; + } // Parametrization variables (notation from https://doi.org/10.1016/0168-9002(94)90532-0) const float phiC = 0.; const float thetaP = 0.; @@ -686,8 +677,9 @@ struct OnTheFlyRichPid { // float nPhotons = (tileZlength / 2.0 > radius) ? n0Photons * multiplicitySpectrumFactor * (1.-(2.0*radius)/(o2::constants::math::PI*tileZlength)) : n0Photons * multiplicitySpectrumFactor * (1.-(2.0*radius)/(o2::constants::math::PI*tileZlength) - (2.0/(tileZlength*o2::constants::math::PI))*(-(tileZlength/(2.0))*std::acos(tileZlength/(2.0*radius)) + radius*std::sqrt(1.-std::pow(tileZlength/(2.0*radius),2.0)))); // Considering "exact" resolution (eta by eta) const float nPhotons = n0Photons * multiplicitySpectrumFactor * fractionPhotonsProjectiveRICH(eta, tileZlength, radius); - if (nPhotons <= kErrorValue + 1) + if (nPhotons <= kErrorValue + 1) { return kErrorValue; + } // Ring angular resolution const float ringAngularResolution = singlePhotonAngularResolution / std::sqrt(nPhotons); return ringAngularResolution; @@ -704,7 +696,7 @@ struct OnTheFlyRichPid { { // Compute tracking contribution to timing using the error propagation formula // Uses light speed in m/ps, magnetic field in T (*0.1 for conversion kGauss -> T) - const double a0 = mass * mass; + const double a0 = 1.0 * mass * mass; const double a1 = refractiveIndex; const double a1Squared = a1 * a1; const float ptCoshEta = pt * std::cosh(eta); @@ -735,9 +727,10 @@ struct OnTheFlyRichPid { // First we compute the number of charged particles in the event float dNdEta = 0.f; if (flagRICHLoadDelphesLUTs) { - for (const auto& track : tracks) { - if (!track.has_mcParticle()) + for (const auto& track : tracks) { // We first compute the number of charged particles in the event + if (!track.has_mcParticle()) { continue; + } auto mcParticle = track.mcParticle(); if (std::abs(mcParticle.eta()) > multiplicityEtaRange) { continue; @@ -745,7 +738,7 @@ struct OnTheFlyRichPid { if (mcParticle.has_daughters()) { continue; } - const auto& pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (!pdgInfo) { // LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; continue; @@ -771,7 +764,7 @@ struct OnTheFlyRichPid { } const auto& mcParticle = track.mcParticle(); - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdg); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); // float xPv = kErrorValue; if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { @@ -779,7 +772,7 @@ struct OnTheFlyRichPid { } // get particle to calculate Cherenkov angle and resolution - auto pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (pdgInfo == nullptr) { fillDummyValues(); continue; diff --git a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx index b327b098a06..f1ba29ab1d4 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTofPid.cxx @@ -30,9 +30,11 @@ #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFCollision.h" #include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/tracksAlice3.h" #include "Common/Core/trackUtilities.h" #include +#include #include #include #include @@ -45,7 +47,6 @@ #include #include #include -#include #include #include @@ -57,38 +58,32 @@ #include #include +#include #include #include #include +#include #include #include #include +#include #include #include -#include - using namespace o2; using namespace o2::framework; -std::array, 9> h2dInnerTimeResTrack; -std::array, 9> h2dInnerTimeResTotal; -std::array, 9> h2dOuterTimeResTrack; -std::array, 9> h2dOuterTimeResTotal; -std::array, 9>, 9> h2dInnerNsigmaTrue; -std::array, 9>, 9> h2dOuterNsigmaTrue; -std::array, 9>, 9> h2dInnerDeltaTrue; -std::array, 9>, 9> h2dOuterDeltaTrue; - struct OnTheFlyTofPid { Produces upgradeTofMC; Produces upgradeTof; Produces upgradeTofExpectedTime; + Produces upgradeTofShortLived; + Produces upgradeTofShortLivedSignal; // necessary for particle charges - Service pdg; + Service pdgDatabase{}; // Necessary for LUTs - Service ccdb; + Service ccdb{}; // these are the settings governing the TOF layers to be used // note that there are two layers foreseen for now: inner and outer TOF @@ -110,6 +105,7 @@ struct OnTheFlyTofPid { Configurable multiplicityEtaRange{"multiplicityEtaRange", 0.800000012, "eta range to compute the multiplicity"}; Configurable flagIncludeTrackTimeRes{"flagIncludeTrackTimeRes", true, "flag to include or exclude track time resolution"}; Configurable flagTOFLoadDelphesLUTs{"flagTOFLoadDelphesLUTs", false, "flag to load Delphes LUTs for tracking correction (use recoTrack parameters if false)"}; + Configurable shortLivedRadiusTolerance{"shortLivedRadiusTolerance", 0.1f, "tolerance (cm) when checking if a TOF layer is within the innermost and outermost track update of short-lived particles"}; } simConfig; struct : ConfigurableGroup { @@ -133,12 +129,13 @@ struct OnTheFlyTofPid { Configurable nBinsEta{"nBinsEta", 400, "number of bins plot relative eta error"}; Configurable nBinsMult{"nBinsMult", 200, "number of bins in multiplicity"}; Configurable maxMultRange{"maxMultRange", 1000.f, "upper limit in multiplicity plots"}; + Configurable> particlesForQa{"particlesForQa", {11, 13, 211, 321, 2212}, "pdgCodes for QA plots"}; } plotsConfig; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // Track smearer array, one per geometry - std::vector> mSmearer; + std::vector> mSmearer; // needed: random number generator for smearing TRandom3 pRandomNumberGenerator; @@ -146,13 +143,127 @@ struct OnTheFlyTofPid { // for handling basic QA histograms if requested HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; OutputObj listEfficiency{"efficiency"}; - static constexpr int kParticles = 9; + + enum ParticleId : int { El = 0, // electron + Mu, // muon + Pi, // pion + Ka, // kaon + Pr, // proton + Sp, // sigma plus + Sm, // sigma minus + Xi, // xi + Om, // omega + De, // deuteron + Tr, // triton + He, // helium 3 + Al, // alpha + NParticles }; + + std::array, NParticles> h2dInnerTimeResTrack; + std::array, NParticles> h2dInnerTimeResTotal; + std::array, NParticles> h2dOuterTimeResTrack; + std::array, NParticles> h2dOuterTimeResTotal; + std::array, NParticles>, NParticles> h2dInnerNsigmaTrue; + std::array, NParticles>, NParticles> h2dOuterNsigmaTrue; + std::array, NParticles>, NParticles> h2dInnerDeltaTrue; + std::array, NParticles>, NParticles> h2dOuterDeltaTrue; + + struct ParticleInfo { + std::string_view texName; + std::string_view name; + ParticleId type{}; + int pdgCode{}; + double mass{}; + float charge{}; + void set(const std::string_view texName_, const std::string_view name_, ParticleId type_, int pdgCode_, double mass_, float charge_) + { + texName = texName_; + name = name_; + type = type_; + pdgCode = pdgCode_; + mass = mass_; + charge = charge_; + } + }; + + static constexpr ParticleInfo particleEl{.texName = "#it{e}", .name = "Elec", .type = El, .pdgCode = PDG_t::kElectron, .mass = o2::constants::physics::MassElectron, .charge = 1.f}; + static constexpr ParticleInfo particleMu{.texName = "#it{#mu}", .name = "Muon", .type = Mu, .pdgCode = PDG_t::kMuonMinus, .mass = o2::constants::physics::MassMuon, .charge = 1.f}; + static constexpr ParticleInfo particlePi{.texName = "#it{#pi}", .name = "Pion", .type = Pi, .pdgCode = PDG_t::kPiPlus, .mass = o2::constants::physics::MassPionCharged, .charge = 1.f}; + static constexpr ParticleInfo particleKa{.texName = "#it{K}", .name = "Kaon", .type = Ka, .pdgCode = PDG_t::kKPlus, .mass = o2::constants::physics::MassKaonCharged, .charge = 1.f}; + static constexpr ParticleInfo particlePr{.texName = "#it{p}", .name = "Prot", .type = Pr, .pdgCode = PDG_t::kProton, .mass = o2::constants::physics::MassProton, .charge = 1.f}; + static constexpr ParticleInfo particleSp{.texName = "#it{#SigmaPlus}", .name = "Sigp", .type = Sp, .pdgCode = PDG_t::kSigmaPlus, .mass = o2::constants::physics::MassSigmaPlus, .charge = 1.f}; + static constexpr ParticleInfo particleSm{.texName = "#it{#SigmaMinus}", .name = "Sigm", .type = Sm, .pdgCode = PDG_t::kSigmaMinus, .mass = o2::constants::physics::MassSigmaMinus, .charge = 1.f}; + static constexpr ParticleInfo particleXi{.texName = "#it{#Xi}", .name = "Xi", .type = Xi, .pdgCode = PDG_t::kXiMinus, .mass = o2::constants::physics::MassXiMinus, .charge = 1.f}; + static constexpr ParticleInfo particleOm{.texName = "#it{#Omega}", .name = "Omeg", .type = Om, .pdgCode = PDG_t::kOmegaMinus, .mass = o2::constants::physics::MassOmegaMinus, .charge = 1.f}; + static constexpr ParticleInfo particleDe{.texName = "#it{d}", .name = "Deut", .type = De, .pdgCode = o2::constants::physics::kDeuteron, .mass = o2::constants::physics::MassDeuteron, .charge = 1.f}; + static constexpr ParticleInfo particleTr{.texName = "#it{t}", .name = "Trit", .type = Tr, .pdgCode = o2::constants::physics::kTriton, .mass = o2::constants::physics::MassTriton, .charge = 1.f}; + static constexpr ParticleInfo particleHe{.texName = "^{3}He", .name = "He", .type = He, .pdgCode = o2::constants::physics::kHelium3, .mass = o2::constants::physics::MassHelium3, .charge = 2.f}; + static constexpr ParticleInfo particleAl{.texName = "#it{#alpha}", .name = "Al", .type = Al, .pdgCode = o2::constants::physics::kAlpha, .mass = o2::constants::physics::MassAlpha, .charge = 2.f}; + + static constexpr std::array Particles = {particleEl, + particleMu, + particlePi, + particleKa, + particlePr, + particleSp, + particleSm, + particleXi, + particleOm, + particleDe, + particleTr, + particleHe, + particleAl}; + + template + void fillShortLivedTables(const TTrack& track, + std::array nSigmaInnerTOF, + std::array nSigmaOuterTOF, + std::array expectedTimeInnerTOF, + std::array expectedTimeOuterTOF, + const std::pair& trackLengthInnerTOF, + const std::pair& trackLengthOuterTOF) + { + const auto isWithinTrackUpdates = [&](const float tofRadius) { + return (track.trackIU() - simConfig.shortLivedRadiusTolerance <= tofRadius) && (tofRadius <= track.trackOU() + simConfig.shortLivedRadiusTolerance); + }; + + const bool hasInnerTOF = isWithinTrackUpdates(simConfig.innerTOFRadius) && trackLengthInnerTOF.first > 0 && trackLengthInnerTOF.second > 0; + const bool hasOuterTOF = isWithinTrackUpdates(simConfig.outerTOFRadius) && trackLengthOuterTOF.first > 0 && trackLengthOuterTOF.second > 0; + static constexpr float DummyValue = -999.f; + for (const int ii : {Sp, Sm, Xi, Om}) { + if (!hasInnerTOF) { + nSigmaInnerTOF[ii] = DummyValue; + expectedTimeInnerTOF[ii] = DummyValue; + } + if (!hasOuterTOF) { + nSigmaOuterTOF[ii] = DummyValue; + expectedTimeOuterTOF[ii] = DummyValue; + } + } + upgradeTofShortLived(nSigmaInnerTOF[Sp], nSigmaInnerTOF[Sm], nSigmaInnerTOF[Xi], nSigmaInnerTOF[Om], + nSigmaOuterTOF[Sp], nSigmaOuterTOF[Sm], nSigmaOuterTOF[Xi], nSigmaOuterTOF[Om], + expectedTimeInnerTOF[Sp], expectedTimeInnerTOF[Sm], expectedTimeInnerTOF[Xi], expectedTimeInnerTOF[Om], + expectedTimeOuterTOF[Sp], expectedTimeOuterTOF[Sm], expectedTimeOuterTOF[Xi], expectedTimeOuterTOF[Om]); + upgradeTofShortLivedSignal(hasInnerTOF, hasOuterTOF); + } + + bool doQaForParticle(const int pdgCode) + { + return std::find(plotsConfig.particlesForQa.value.begin(), plotsConfig.particlesForQa.value.end(), std::abs(pdgCode)) != plotsConfig.particlesForQa.value.end(); + } // Configuration defined at init time o2::fastsim::GeometryContainer mGeoContainer; float mMagneticField = 0.0f; void init(o2::framework::InitContext& initContext) { + // Check Particles: every row's declared type must match its array position. + for (int i = 0; i < NParticles; ++i) { + if (Particles[i].type != i) { + LOG(fatal) << "Particles in ParticleInfo not ordered according to enum!"; + } + } + mGeoContainer.setCcdbManager(ccdb.operator->()); mGeoContainer.init(initContext); @@ -162,13 +273,12 @@ struct OnTheFlyTofPid { pRandomNumberGenerator.SetSeed(0); // fully randomize if (simConfig.flagTOFLoadDelphesLUTs) { for (int icfg = 0; icfg < nGeometries; ++icfg) { - const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; - mSmearer.emplace_back(std::make_unique()); + mSmearer.emplace_back(std::make_unique()); mSmearer[icfg]->setCcdbManager(ccdb.operator->()); std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); for (const auto& entry : globalConfiguration) { int pdg = 0; - if (entry.first.find("lut") != 0) { + if (!entry.first.starts_with("lut")) { continue; } if (entry.first.find("lutEl") != std::string::npos) { @@ -237,14 +347,14 @@ struct OnTheFlyTofPid { histos.add("iTOF/h2dTrackLengthInnerVsPt", "h2dTrackLengthInnerVsPt", kTH2F, {axisMomentumSmall, axisTrackLengthInner}); histos.add("iTOF/h2dTrackLengthInnerRecoVsPt", "h2dTrackLengthInnerRecoVsPt", kTH2F, {axisMomentumSmall, axisTrackLengthInner}); histos.add("iTOF/h2dDeltaTrackLengthInnerVsPt", "h2dDeltaTrackLengthInnerVsPt", kTH2F, {axisMomentumSmall, axisTrackDeltaLength}); - histos.add("iTOF/h2HitMap", "h2HitMap", kTH2F, {{1000, -simConfig.innerTOFLength / 2, simConfig.innerTOFLength / 2}, {1000, 0, simConfig.innerTOFRadius * 2 * M_PI}}); + histos.add("iTOF/h2HitMap", "h2HitMap", kTH2F, {{1000, -simConfig.innerTOFLength / 2, simConfig.innerTOFLength / 2}, {1000, 0, simConfig.innerTOFRadius * o2::constants::math::TwoPI}}); histos.add("oTOF/h2dVelocityVsMomentumOuter", "h2dVelocityVsMomentumOuter", kTH2F, {axisMomentum, axisVelocity}); histos.add("oTOF/h2dVelocityVsRigidityOuter", "h2dVelocityVsRigidityOuter", kTH2F, {axisRigidity, axisVelocity}); histos.add("oTOF/h2dTrackLengthOuterVsPt", "h2dTrackLengthOuterVsPt", kTH2F, {axisMomentumSmall, axisTrackLengthOuter}); histos.add("oTOF/h2dTrackLengthOuterRecoVsPt", "h2dTrackLengthOuterRecoVsPt", kTH2F, {axisMomentumSmall, axisTrackLengthOuter}); histos.add("oTOF/h2dDeltaTrackLengthOuterVsPt", "h2dDeltaTrackLengthOuterVsPt", kTH2F, {axisMomentumSmall, axisTrackDeltaLength}); - histos.add("oTOF/h2HitMap", "h2HitMap", kTH2F, {{1000, -simConfig.outerTOFLength / 2, simConfig.outerTOFLength / 2}, {1000, 0, simConfig.outerTOFRadius * 2 * M_PI}}); + histos.add("oTOF/h2HitMap", "h2HitMap", kTH2F, {{1000, -simConfig.outerTOFLength / 2, simConfig.outerTOFLength / 2}, {1000, 0, simConfig.outerTOFRadius * o2::constants::math::TwoPI}}); const AxisSpec axisPt{static_cast(plotsConfig.nBinsP), 0.0f, +4.0f, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec axisEta{static_cast(plotsConfig.nBinsEta), -2.0f, +2.0f, "#eta"}; @@ -253,35 +363,43 @@ struct OnTheFlyTofPid { histos.add("h2dRelativePtResolution", "h2dRelativePtResolution", kTH2F, {axisPt, axisRelativePt}); histos.add("h2dRelativeEtaResolution", "h2dRelativeEtaResolution", kTH2F, {axisEta, axisRelativeEta}); - std::string particleNames[kParticles] = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "^{3}He", "#it{#alpha}"}; - std::string particleNames2[kParticles] = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; - for (int iTrue = 0; iTrue < kParticles; iTrue++) { - auto addHistogram = [&](const std::string& name, const AxisSpec& axis) { + for (int iTrue = 0; iTrue < NParticles; iTrue++) { + if (!doQaForParticle(Particles[iTrue].pdgCode)) { + continue; + } + + auto addHistoVsMomentum = [&](const std::string& name, const AxisSpec& axis) { return histos.add(name, "", kTH2F, {axisMomentum, axis}); }; + const auto& pNameTrue = Particles[iTrue].name; + + const AxisSpec axisTrackTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, std::format("Track time resolution - {} (ps)", Particles[iTrue].texName)}; + h2dInnerTimeResTrack[iTrue] = addHistoVsMomentum(std::format("iTOF/res/h2dInnerTimeResTrack{}VsP", pNameTrue), axisTrackTimeRes); + h2dOuterTimeResTrack[iTrue] = addHistoVsMomentum(std::format("oTOF/res/h2dOuterTimeResTrack{}VsP", pNameTrue), axisTrackTimeRes); + const AxisSpec axisTotalTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, std::format("Total time resolution - {} (ps)", Particles[iTrue].texName)}; + h2dInnerTimeResTotal[iTrue] = addHistoVsMomentum(std::format("iTOF/res/h2dInnerTimeResTotal{}VsP", pNameTrue), axisTotalTimeRes); + h2dOuterTimeResTotal[iTrue] = addHistoVsMomentum(std::format("oTOF/res/h2dOuterTimeResTotal{}VsP", pNameTrue), axisTotalTimeRes); + for (int iHyp = 0; iHyp < NParticles; iHyp++) { + if (!doQaForParticle(Particles[iHyp].pdgCode)) { + continue; + } + const auto& pNameHypo = Particles[iHyp].name; - const AxisSpec axisTrackTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, "Track time resolution - " + particleNames[iTrue] + " (ps)"}; - h2dInnerTimeResTrack[iTrue] = addHistogram("iTOF/res/h2dInnerTimeResTrack" + particleNames2[iTrue] + "VsP", axisTrackTimeRes); - h2dOuterTimeResTrack[iTrue] = addHistogram("oTOF/res/h2dOuterTimeResTrack" + particleNames2[iTrue] + "VsP", axisTrackTimeRes); - const AxisSpec axisTotalTimeRes{plotsConfig.nBinsTimeRes, 0.0f, +200.0f, "Total time resolution - " + particleNames[iTrue] + " (ps)"}; - h2dInnerTimeResTotal[iTrue] = addHistogram("iTOF/res/h2dInnerTimeResTotal" + particleNames2[iTrue] + "VsP", axisTotalTimeRes); - h2dOuterTimeResTotal[iTrue] = addHistogram("oTOF/res/h2dOuterTimeResTotal" + particleNames2[iTrue] + "VsP", axisTotalTimeRes); - for (int iHyp = 0; iHyp < kParticles; iHyp++) { - std::string nameTitleInner = "h2dInnerNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; - std::string nameTitleOuter = "h2dOuterNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; - std::string nameTitleInnerDelta = "h2dInnerDeltaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; - std::string nameTitleOuterDelta = "h2dOuterDeltaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis"; + const std::string nameTitleInner = std::format("h2dInnerNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); + const std::string nameTitleOuter = std::format("h2dOuterNsigmaTrue{}Vs{}", pNameTrue, pNameHypo); + const std::string nameTitleInnerDelta = std::format("h2dInnerDeltaTrue{}Vs{}", pNameTrue, pNameHypo); + const std::string nameTitleOuterDelta = std::format("h2dOuterDeltaTrue{}Vs{}", pNameTrue, pNameHypo); const AxisSpec axisX{plotsConfig.doSeparationVsPt.value ? axisPt : axisMomentum}; - const AxisSpec axisNsigmaCorrect{plotsConfig.nBinsNsigmaCorrectSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, "N#sigma - True " + particleNames[iTrue] + " vs " + particleNames[iHyp] + " hypothesis"}; - const AxisSpec axisDeltaCorrect{plotsConfig.nBinsDeltaCorrectSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta - True " + particleNames[iTrue] + " vs " + particleNames[iHyp] + " hypothesis"}; - const AxisSpec axisNsigmaWrong{plotsConfig.nBinsNsigmaWrongSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, "N#sigma - True " + particleNames[iTrue] + " vs " + particleNames[iHyp] + " hypothesis"}; - const AxisSpec axisDeltaWrong{plotsConfig.nBinsDeltaWrongSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, "#Delta - True " + particleNames[iTrue] + " vs " + particleNames[iHyp] + " hypothesis"}; + const AxisSpec axisNsigmaCorrect{plotsConfig.nBinsNsigmaCorrectSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, std::format("N#sigma - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; + const AxisSpec axisDeltaCorrect{plotsConfig.nBinsDeltaCorrectSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, std::format("#Delta - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; + const AxisSpec axisNsigmaWrong{plotsConfig.nBinsNsigmaWrongSpecies, plotsConfig.minNsigmaRange, plotsConfig.maxNsigmaRange, std::format("N#sigma - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; + const AxisSpec axisDeltaWrong{plotsConfig.nBinsDeltaWrongSpecies, plotsConfig.minDeltaRange, plotsConfig.maxDeltaRange, std::format("#Delta - True {} vs {} hypothesis", Particles[iTrue].texName, Particles[iHyp].texName)}; const AxisSpec axisNSigma{iTrue == iHyp ? axisNsigmaCorrect : axisNsigmaWrong}; const AxisSpec axisDelta{iTrue == iHyp ? axisDeltaCorrect : axisDeltaWrong}; - h2dInnerNsigmaTrue[iTrue][iHyp] = histos.add("iTOF/nsigma/h2dInnerNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis", "", kTH2F, {axisX, axisNSigma}); - h2dOuterNsigmaTrue[iTrue][iHyp] = histos.add("oTOF/nsigma/h2dOuterNsigmaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis", "", kTH2F, {axisX, axisNSigma}); - h2dInnerDeltaTrue[iTrue][iHyp] = histos.add("iTOF/delta/h2dInnerDeltaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis", "", kTH2F, {axisX, axisDelta}); - h2dOuterDeltaTrue[iTrue][iHyp] = histos.add("oTOF/delta/h2dOuterDeltaTrue" + particleNames2[iTrue] + "Vs" + particleNames2[iHyp] + "Hypothesis", "", kTH2F, {axisX, axisDelta}); + h2dInnerNsigmaTrue[iTrue][iHyp] = histos.add(std::format("iTOF/nsigma/h2dInnerNsigmaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleInner.c_str(), kTH2F, {axisX, axisNSigma}); + h2dOuterNsigmaTrue[iTrue][iHyp] = histos.add(std::format("oTOF/nsigma/h2dOuterNsigmaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleOuter.c_str(), kTH2F, {axisX, axisNSigma}); + h2dInnerDeltaTrue[iTrue][iHyp] = histos.add(std::format("iTOF/delta/h2dInnerDeltaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleInnerDelta.c_str(), kTH2F, {axisX, axisDelta}); + h2dOuterDeltaTrue[iTrue][iHyp] = histos.add(std::format("oTOF/delta/h2dOuterDeltaTrue{}Vs{}", pNameTrue, pNameHypo), nameTitleOuterDelta.c_str(), kTH2F, {axisX, axisDelta}); } } } @@ -289,12 +407,12 @@ struct OnTheFlyTofPid { struct TOFLayerEfficiency { private: - const float layerRadius; - const float layerLength; - const float pixelDimensionZ; - const float pixelDimensionRPhi; - const float fractionInactive; - const float magField; + float layerRadius; + float layerLength; + float pixelDimensionZ; + float pixelDimensionRPhi; + float fractionInactive; + float magField; TAxis* axisZ = nullptr; TAxis* axisRPhi = nullptr; @@ -308,9 +426,6 @@ struct OnTheFlyTofPid { public: ~TOFLayerEfficiency() { - hHitMap->SaveAs(Form("/tmp/%s.png", hHitMap->GetName())); - hHitMapInPixel->SaveAs(Form("/tmp/%s.png", hHitMapInPixel->GetName())); - hHitMapInPixelBefore->SaveAs(Form("/tmp/%s.png", hHitMapInPixelBefore->GetName())); delete axisZ; delete axisRPhi; @@ -321,26 +436,35 @@ struct OnTheFlyTofPid { delete hHitMapInPixelBefore; } - TOFLayerEfficiency(float r, float l, std::array pDimensions, float fIA, float m) - : layerRadius(r), layerLength(l), pixelDimensionZ(pDimensions[0]), pixelDimensionRPhi(pDimensions[1]), fractionInactive(fIA), magField(m) + TOFLayerEfficiency(const TOFLayerEfficiency&) = delete; + TOFLayerEfficiency& operator=(const TOFLayerEfficiency&) = delete; + + TOFLayerEfficiency(float r, float l, std::array pDimensions, float fIA, float m) : layerRadius(r), + layerLength(l), + pixelDimensionZ(pDimensions[0]), + pixelDimensionRPhi(pDimensions[1]), + fractionInactive(fIA), + magField(m), + axisZ(new TAxis(static_cast(layerLength / pixelDimensionZ), -layerLength / 2, layerLength)) { // Assuming square pixels for simplicity - const float circumference = 2.0f * M_PI * layerRadius; - axisZ = new TAxis(static_cast(layerLength / pixelDimensionZ), -layerLength / 2, layerLength); + const float circumference = o2::constants::math::TwoPI * layerRadius; + axisRPhi = new TAxis(static_cast(circumference / pixelDimensionRPhi), 0.f, circumference); const float inactiveBorderRPhi = pixelDimensionRPhi * std::sqrt(fractionInactive) / 2; const float inactiveBorderZ = pixelDimensionZ * std::sqrt(fractionInactive) / 2; - const double arrayRPhi[4] = {-pixelDimensionRPhi / 2, -pixelDimensionRPhi / 2 + inactiveBorderRPhi, pixelDimensionRPhi / 2 - inactiveBorderRPhi, pixelDimensionRPhi / 2}; - for (int i = 0; i < 4; i++) { + static constexpr int NDimBorderArray = 4; + const std::array arrayRPhi = {-pixelDimensionRPhi / 2, -pixelDimensionRPhi / 2 + inactiveBorderRPhi, pixelDimensionRPhi / 2 - inactiveBorderRPhi, pixelDimensionRPhi / 2}; + for (int i = 0; i < NDimBorderArray; i++) { LOG(info) << "arrayRPhi[" << i << "] = " << arrayRPhi[i]; } - axisInPixelRPhi = new TAxis(3, arrayRPhi); - const double arrayZ[4] = {-pixelDimensionZ / 2, -pixelDimensionZ / 2 + inactiveBorderZ, pixelDimensionZ / 2 - inactiveBorderZ, pixelDimensionZ / 2}; - for (int i = 0; i < 4; i++) { + axisInPixelRPhi = new TAxis(3, arrayRPhi.data()); + const std::array arrayZ = {-pixelDimensionZ / 2, -pixelDimensionZ / 2 + inactiveBorderZ, pixelDimensionZ / 2 - inactiveBorderZ, pixelDimensionZ / 2}; + for (int i = 0; i < NDimBorderArray; i++) { LOG(info) << "arrayZ[" << i << "] = " << arrayZ[i]; } - axisInPixelZ = new TAxis(3, arrayZ); + axisInPixelZ = new TAxis(3, arrayZ.data()); hHitMap = new TH2F(Form("hHitMap_R%.0f", layerRadius), "HitMap;z (cm); r#phi (cm)", 1000, -1000, 1000, 1000, -1000, 1000); hHitMapInPixel = new TH2F(Form("hHitMapInPixel_R%.0f", layerRadius), "HitMapInPixel;z (cm); r#phi (cm)", 1000, -10, 10, 1000, -10, 10); @@ -363,7 +487,8 @@ struct OnTheFlyTofPid { const float r = std::sqrt(hitPosition[0] * hitPosition[0] + hitPosition[1] * hitPosition[1]); // Check if hit is within layer geometric acceptance - if (std::abs(layerRadius - r) > 10.f) { + static constexpr float LayerGeometricAcceptance = 10.f; + if (std::abs(layerRadius - r) > LayerGeometricAcceptance) { LOG(debug) << "Hit out of TOF layer acceptance: r=" << r << " cm with respect to the layer radius " << layerRadius; return false; } @@ -393,19 +518,27 @@ struct OnTheFlyTofPid { // LOG(warning) << "Local hit difference in z is bigger than the pixel size"; } hHitMapInPixelBefore->Fill(localZ, localRPhi); + enum PixelBin : int { kInactiveLeft = 0, + kInactiveRight = 1, + kInactiveBottom = 3, + kInactiveTop = 4 }; switch (axisInPixelRPhi->FindBin(localRPhi)) { - case 0: - case 1: - case 3: - case 4: + case kInactiveLeft: + case kInactiveRight: + case kInactiveBottom: + case kInactiveTop: return false; + default: + break; } switch (axisInPixelZ->FindBin(localZ)) { - case 0: - case 1: - case 3: - case 4: + case kInactiveLeft: + case kInactiveRight: + case kInactiveBottom: + case kInactiveTop: return false; + default: + break; } hHitMapInPixel->Fill(localZ, localRPhi); hHitMap->Fill(z, rphi); @@ -420,12 +553,12 @@ struct OnTheFlyTofPid { if (fractionInactive <= 0.f) { return true; } - float x, y, z; + float x = NAN, y = NAN, z = NAN; if (!track.getXatLabR(layerRadius, x, magField)) { LOG(debug) << "Could not propagate track to TOF layer at radius " << layerRadius << " cm"; return false; } - bool b; + bool b = false; ROOT::Math::PositionVector3D hit = track.getXYZGloAt(x, magField, b); if (!b) { LOG(debug) << "Could not get hit position at radius " << layerRadius << " cm"; @@ -448,81 +581,6 @@ struct OnTheFlyTofPid { return outerTOFLayer.isTrackInActiveArea(track); } - /// function to calculate track length of this track up to a certain radius - /// \param track the input track - /// \param radius the radius of the layer you're calculating the length to - /// \param magneticField the magnetic field to use when propagating - static float computeTrackLength(o2::track::TrackParCov track, float radius, float magneticField) - { - // don't make use of the track parametrization - float length = -100; - - o2::math_utils::CircleXYf_t trcCircle; - float sna, csa; - track.getCircleParams(magneticField, trcCircle, sna, csa); - - // distance between circle centers (one circle is at origin -> easy) - const float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); - - // condition of circles touching - if not satisfied returned length will be -100 - if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { - length = 0.0f; - - // base radical direction - const float ux = trcCircle.xC / centerDistance; - const float uy = trcCircle.yC / centerDistance; - // calculate perpendicular vector (normalized) for +/- displacement - const float vx = -uy; - const float vy = +ux; - // calculate coordinate for radical line - const float radical = (centerDistance * centerDistance - trcCircle.rC * trcCircle.rC + radius * radius) / (2.0f * centerDistance); - // calculate absolute displacement from center-to-center axis - const float displace = (0.5f / centerDistance) * std::sqrt( - (-centerDistance + trcCircle.rC - radius) * - (-centerDistance - trcCircle.rC + radius) * - (-centerDistance + trcCircle.rC + radius) * - (centerDistance + trcCircle.rC + radius)); - - // possible intercept points of track and TOF layer in 2D plane - const float point1[2] = {radical * ux + displace * vx, radical * uy + displace * vy}; - const float point2[2] = {radical * ux - displace * vx, radical * uy - displace * vy}; - - // decide on correct intercept point - std::array mom; - track.getPxPyPzGlo(mom); - const float scalarProduct1 = point1[0] * mom[0] + point1[1] * mom[1]; - const float scalarProduct2 = point2[0] * mom[0] + point2[1] * mom[1]; - - // get start point - std::array startPoint; - track.getXYZGlo(startPoint); - - float cosAngle = -1000, modulus = -1000; - - if (scalarProduct1 > scalarProduct2) { - modulus = std::hypot(point1[0] - trcCircle.xC, point1[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point1[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point1[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } else { - modulus = std::hypot(point2[0] - trcCircle.xC, point2[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point2[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point2[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } - cosAngle /= modulus; - length = trcCircle.rC * std::acos(cosAngle); - length *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); - } - return length; - } - - /// returns velocity in centimeters per picoseconds - /// \param momentum the momentum of the tarck - /// \param mass the mass of the particle - float computeParticleVelocity(float momentum, float mass) - { - const float a = momentum / mass; - // uses light speed in cm/ps so output is in those units - return o2::constants::physics::LightSpeedCm2PS * a / std::sqrt((1.f + a * a)); - } - struct TracksWithTime { TracksWithTime(int pdgCode, std::pair innerTOFTime, @@ -552,7 +610,7 @@ struct OnTheFlyTofPid { }; std::vector tracksWithTime; - bool eventTime(std::vector& tracks, + bool eventTime(const std::vector& tracks, std::array& tzero) { @@ -561,7 +619,7 @@ struct OnTheFlyTofPid { // Todo: check the different mass hypothesis iteratively for (const auto& track : tracks) { - auto pdgInfo = pdg->GetParticle(track.mPdgCode); + const auto& pdgInfo = pdgDatabase->GetParticle(track.mPdgCode); if (pdgInfo == nullptr) { continue; } @@ -590,10 +648,10 @@ struct OnTheFlyTofPid { sumw += w; } - static constexpr float kMaxEventTimeResolution = 200.f; - if (sumw <= 0. || tracks.size() <= 1 || std::sqrt(1. / sumw) > kMaxEventTimeResolution) { - tzero[0] = 0.; // [ps] - tzero[1] = kMaxEventTimeResolution; // [ps] + static constexpr float MaxEventTimeResolution = 200.f; + if (sumw <= 0. || tracks.size() <= 1 || std::sqrt(1. / sumw) > MaxEventTimeResolution) { + tzero[0] = 0.; // [ps] + tzero[1] = MaxEventTimeResolution; // [ps] return false; } @@ -620,17 +678,17 @@ struct OnTheFlyTofPid { { // Compute tracking contribution to timing using the error propagation formula // Uses light speed in m/ps, magnetic field in T (*0.1 for conversion kGauss -> T) - double a0 = mass * mass; - double a1 = 0.299792458 * (0.1 * magneticField) * (0.01 * o2::constants::physics::LightSpeedCm2NS / 1e+3); - double a2 = (detRadius * 0.01) * (detRadius * 0.01) * (0.299792458) * (0.299792458) * (0.1 * magneticField) * (0.1 * magneticField) / 2.0; - double dtofOndPt = (std::pow(pt, 4) * std::pow(std::cosh(eta), 2) * std::acos(1.0 - a2 / std::pow(pt, 2)) - 2.0 * a2 * std::pow(pt, 2) * (a0 + std::pow(pt * std::cosh(eta), 2)) / std::sqrt(a2 * (2.0 * std::pow(pt, 2) - a2))) / (a1 * std::pow(pt, 3) * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); - double dtofOndEta = std::pow(pt, 2) * std::sinh(eta) * std::cosh(eta) * std::acos(1.0 - a2 / std::pow(pt, 2)) / (a1 * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); - double trackTimeResolution = std::hypot(std::fabs(dtofOndPt) * trackPtResolution, std::fabs(dtofOndEta) * trackEtaResolution); + const double a0 = 1.0 * mass * mass; + const double a1 = 0.299792458 * (0.1 * magneticField) * (0.01 * o2::constants::physics::LightSpeedCm2NS / 1e+3); + const double a2 = (detRadius * 0.01) * (detRadius * 0.01) * (0.299792458) * (0.299792458) * (0.1 * magneticField) * (0.1 * magneticField) / 2.0; + const double dtofOndPt = (std::pow(pt, 4) * std::pow(std::cosh(eta), 2) * std::acos(1.0 - a2 / std::pow(pt, 2)) - 2.0 * a2 * std::pow(pt, 2) * (a0 + std::pow(pt * std::cosh(eta), 2)) / std::sqrt(a2 * (2.0 * std::pow(pt, 2) - a2))) / (a1 * std::pow(pt, 3) * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + const double dtofOndEta = std::pow(pt, 2) * std::sinh(eta) * std::cosh(eta) * std::acos(1.0 - a2 / std::pow(pt, 2)) / (a1 * std::sqrt(a0 + std::pow(pt * std::cosh(eta), 2))); + const double trackTimeResolution = std::hypot(std::fabs(dtofOndPt) * trackPtResolution, std::fabs(dtofOndEta) * trackEtaResolution); return trackTimeResolution; } void process(soa::Join::iterator const& collision, - soa::Join const& tracks, + soa::Join const& tracks, aod::McParticles const&, aod::McCollisions const&) { @@ -651,8 +709,9 @@ struct OnTheFlyTofPid { // First we compute the number of charged particles in the event if LUTs are loaded float dNdEta = 0.f; for (const auto& track : tracks) { - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } auto mcParticle = track.mcParticle(); if (std::abs(mcParticle.eta()) > simConfig.multiplicityEtaRange) { continue; @@ -660,7 +719,7 @@ struct OnTheFlyTofPid { if (mcParticle.has_daughters()) { continue; } - const auto& pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (!pdgInfo) { // LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; continue; @@ -683,21 +742,21 @@ struct OnTheFlyTofPid { if (!track.has_mcParticle()) { // should always be OK but check please upgradeTofMC(-999.f, -999.f, -999.f, -999.f); continue; - } else { - LOG(debug) << "Track without mcParticle found!"; } + LOG(debug) << "Track without mcParticle found!"; + const auto& mcParticle = track.mcParticle(); - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdg); + o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDatabase); float xPv = -100.f; - static constexpr float kTrkXThreshold = -99.f; // Threshold to consider a good propagation of the track + static constexpr float TrkXThreshold = -99.f; // Threshold to consider a good propagation of the track if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { xPv = o2track.getX(); } float trackLengthInnerTOF = -1, trackLengthOuterTOF = -1; - if (xPv > kTrkXThreshold) { - trackLengthInnerTOF = computeTrackLength(o2track, simConfig.innerTOFRadius, mMagneticField); - trackLengthOuterTOF = computeTrackLength(o2track, simConfig.outerTOFRadius, mMagneticField); + if (xPv > TrkXThreshold) { + trackLengthInnerTOF = o2::upgrade::computeTrackLength(o2track, simConfig.innerTOFRadius, mMagneticField); + trackLengthOuterTOF = o2::upgrade::computeTrackLength(o2track, simConfig.outerTOFRadius, mMagneticField); } // Check if the track hit a sensitive area of the TOF @@ -707,7 +766,9 @@ struct OnTheFlyTofPid { } else { float x = 0.f; o2track.getXatLabR(simConfig.innerTOFRadius, x, mMagneticField); - histos.fill(HIST("iTOF/h2HitMap"), o2track.getZAt(x, mMagneticField), simConfig.innerTOFRadius * o2track.getPhiAt(x, mMagneticField)); + if (plotsConfig.doQAplots) { + histos.fill(HIST("iTOF/h2HitMap"), o2track.getZAt(x, mMagneticField), simConfig.innerTOFRadius * o2track.getPhiAt(x, mMagneticField)); + } } const bool activeOuterTOF = isInOuterTOFActiveArea(o2track); @@ -716,17 +777,19 @@ struct OnTheFlyTofPid { } else { float x = 0.f; o2track.getXatLabR(simConfig.outerTOFRadius, x, mMagneticField); - histos.fill(HIST("oTOF/h2HitMap"), o2track.getZAt(x, mMagneticField), simConfig.outerTOFRadius * o2track.getPhiAt(x, mMagneticField)); + if (plotsConfig.doQAplots) { + histos.fill(HIST("oTOF/h2HitMap"), o2track.getZAt(x, mMagneticField), simConfig.outerTOFRadius * o2track.getPhiAt(x, mMagneticField)); + } } // get mass to calculate velocity - auto pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + const auto& pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); if (pdgInfo == nullptr) { LOG(error) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; upgradeTofMC(-999.f, -999.f, -999.f, -999.f); continue; } - const float v = computeParticleVelocity(o2track.getP(), pdgInfo->Mass()); + const float v = o2::upgrade::computeParticleVelocity(o2track.getP(), pdgInfo->Mass()); const float expectedTimeInnerTOF = trackLengthInnerTOF > 0 ? trackLengthInnerTOF / v + eventCollisionTimePS : -999.f; // arrival time to the Inner TOF in ps const float expectedTimeOuterTOF = trackLengthOuterTOF > 0 ? trackLengthOuterTOF / v + eventCollisionTimePS : -999.f; // arrival time to the Outer TOF in ps upgradeTofMC(expectedTimeInnerTOF, trackLengthInnerTOF, expectedTimeOuterTOF, trackLengthOuterTOF); @@ -743,9 +806,9 @@ struct OnTheFlyTofPid { if (recoTrack.propagateToDCA(pvVtx, mMagneticField)) { xPv = recoTrack.getX(); } - if (xPv > kTrkXThreshold) { - trackLengthRecoInnerTOF = computeTrackLength(recoTrack, simConfig.innerTOFRadius, mMagneticField); - trackLengthRecoOuterTOF = computeTrackLength(recoTrack, simConfig.outerTOFRadius, mMagneticField); + if (xPv > TrkXThreshold) { + trackLengthRecoInnerTOF = o2::upgrade::computeTrackLength(recoTrack, simConfig.innerTOFRadius, mMagneticField); + trackLengthRecoOuterTOF = o2::upgrade::computeTrackLength(recoTrack, simConfig.outerTOFRadius, mMagneticField); } // cache the track info needed for the event time calculation @@ -779,7 +842,7 @@ struct OnTheFlyTofPid { if (etStatus) { histos.fill(HIST("h1dEventTimedelta"), eventCollisionTimePS - tzero[0]); } - static_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); + dynamic_cast(listEfficiency->At(0))->Fill(etStatus, dNdEta); } // Then we do a second loop to compute the measured quantities with the measured event time @@ -799,35 +862,15 @@ struct OnTheFlyTofPid { const float measuredTimeInnerTOF = trkWithTime.mInnerTOFTime.first - tzero[0]; const float measuredTimeOuterTOF = trkWithTime.mOuterTOFTime.first - tzero[0]; const float momentum = trkWithTime.mMomentum.first; - const float pseudorapidity = trkWithTime.mPseudorapidity.first; + const float eta = trkWithTime.mPseudorapidity.first; const float noSmearingPt = trkWithTime.mNoSmearingPt; // Straight to Nsigma - static std::array expectedTimeInnerTOF, expectedTimeOuterTOF; - static std::array deltaTimeInnerTOF, deltaTimeOuterTOF; - static std::array nSigmaInnerTOF, nSigmaOuterTOF; - static constexpr int kParticlePdgs[kParticles] = {kElectron, - kMuonMinus, - kPiPlus, - kKPlus, - kProton, - o2::constants::physics::kDeuteron, - o2::constants::physics::kTriton, - o2::constants::physics::kHelium3, - o2::constants::physics::kAlpha}; - static constexpr float kParticleMasses[kParticles] = {o2::constants::physics::MassElectron, - o2::constants::physics::MassMuon, - o2::constants::physics::MassPionCharged, - o2::constants::physics::MassKaonCharged, - o2::constants::physics::MassProton, - o2::constants::physics::MassDeuteron, - o2::constants::physics::MassTriton, - o2::constants::physics::MassHelium3, - o2::constants::physics::MassAlpha}; - static constexpr float kParticleCharges[kParticles] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 2.f, 2.f}; - float momentumHypotheses[kParticles]; // Store momentum hypothesis for each particle - - auto truePdgInfo = pdg->GetParticle(mcParticle.pdgCode()); + static std::array expectedTimeInnerTOF, expectedTimeOuterTOF; + static std::array deltaTimeInnerTOF, deltaTimeOuterTOF; + static std::array nSigmaInnerTOF, nSigmaOuterTOF; + std::array momentumHypotheses{}; // Store momentum hypothesis for each particle + const auto& truePdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); float rigidity = momentum; // fallback to momentum if charge unknown // Use MC truth charge for rigidity calculation @@ -857,7 +900,7 @@ struct OnTheFlyTofPid { } // For every mass hypothesis compute the expected time, the delta with respect to it and the nsigma - for (int ii = 0; ii < kParticles; ii++) { + for (int ii = 0; ii < NParticles; ii++) { expectedTimeInnerTOF[ii] = -100; expectedTimeOuterTOF[ii] = -100; deltaTimeInnerTOF[ii] = -100; @@ -865,8 +908,8 @@ struct OnTheFlyTofPid { nSigmaInnerTOF[ii] = -100; nSigmaOuterTOF[ii] = -100; - momentumHypotheses[ii] = rigidity * kParticleCharges[ii]; // Total momentum for this hypothesis - const float v = computeParticleVelocity(momentumHypotheses[ii], kParticleMasses[ii]); + momentumHypotheses[ii] = rigidity * Particles[ii].charge; // Total momentum for this hypothesis + const float v = o2::upgrade::computeParticleVelocity(momentumHypotheses[ii], Particles[ii].mass); expectedTimeInnerTOF[ii] = trackLengthInnerTOF / v; expectedTimeOuterTOF[ii] = trackLengthOuterTOF / v; @@ -878,22 +921,22 @@ struct OnTheFlyTofPid { float innerTotalTimeReso = simConfig.innerTOFTimeReso; float outerTotalTimeReso = simConfig.outerTOFTimeReso; if (simConfig.flagIncludeTrackTimeRes) { - const float transverseMomentum = momentumHypotheses[ii] / std::cosh(pseudorapidity); + const float transverseMomentum = momentumHypotheses[ii] / std::cosh(eta); double ptResolution = transverseMomentum * transverseMomentum * std::sqrt(trkWithTime.mMomentum.second); - double etaResolution = std::fabs(std::sin(2.0 * std::atan(std::exp(-pseudorapidity)))) * std::sqrt(trkWithTime.mPseudorapidity.second); + double etaResolution = std::fabs(std::sin(2.0 * std::atan(std::exp(-eta)))) * std::sqrt(trkWithTime.mPseudorapidity.second); if (simConfig.flagTOFLoadDelphesLUTs) { - if (mSmearer[collision.lutConfigId()]->hasTable(kParticlePdgs[ii])) { // Only if the LUT for this particle was loaded - ptResolution = mSmearer[collision.lutConfigId()]->getAbsPtRes(kParticlePdgs[ii], dNdEta, pseudorapidity, transverseMomentum); - etaResolution = mSmearer[collision.lutConfigId()]->getAbsEtaRes(kParticlePdgs[ii], dNdEta, pseudorapidity, transverseMomentum); + if (mSmearer[collision.lutConfigId()]->hasTable(Particles[ii].pdgCode)) { // Only if the LUT for this particle was loaded + ptResolution = mSmearer[collision.lutConfigId()]->getAbsPtRes(Particles[ii].pdgCode, dNdEta, eta, transverseMomentum); + etaResolution = mSmearer[collision.lutConfigId()]->getAbsEtaRes(Particles[ii].pdgCode, dNdEta, eta, transverseMomentum); } } - const float innerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, kParticleMasses[ii], simConfig.innerTOFRadius, mMagneticField); - const float outerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, pseudorapidity, ptResolution, etaResolution, kParticleMasses[ii], simConfig.outerTOFRadius, mMagneticField); + const float innerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, eta, ptResolution, etaResolution, Particles[ii].mass, simConfig.innerTOFRadius, mMagneticField); + const float outerTrackTimeReso = calculateTrackTimeResolutionAdvanced(transverseMomentum, eta, ptResolution, etaResolution, Particles[ii].mass, simConfig.outerTOFRadius, mMagneticField); innerTotalTimeReso = std::hypot(simConfig.innerTOFTimeReso, innerTrackTimeReso); outerTotalTimeReso = std::hypot(simConfig.outerTOFTimeReso, outerTrackTimeReso); if (plotsConfig.doQAplots) { - if (std::fabs(mcParticle.pdgCode()) == kParticlePdgs[ii]) { + if (doQaForParticle(Particles[ii].pdgCode) && std::fabs(mcParticle.pdgCode()) == Particles[ii].pdgCode) { if (trackLengthRecoInnerTOF > 0) { h2dInnerTimeResTrack[ii]->Fill(momentumHypotheses[ii], innerTrackTimeReso); h2dInnerTimeResTotal[ii]->Fill(momentumHypotheses[ii], innerTotalTimeReso); @@ -901,10 +944,9 @@ struct OnTheFlyTofPid { if (trackLengthRecoOuterTOF > 0) { h2dOuterTimeResTrack[ii]->Fill(momentumHypotheses[ii], outerTrackTimeReso); h2dOuterTimeResTotal[ii]->Fill(momentumHypotheses[ii], outerTotalTimeReso); - static constexpr int kIdPion = 2; - if (ii == kIdPion) { + if (ii == Pi) { histos.fill(HIST("h2dRelativePtResolution"), transverseMomentum, 100.0 * ptResolution / transverseMomentum); - histos.fill(HIST("h2dRelativeEtaResolution"), pseudorapidity, 100.0 * etaResolution / (std::fabs(pseudorapidity) + 1e-6)); + histos.fill(HIST("h2dRelativeEtaResolution"), eta, 100.0 * etaResolution / (std::fabs(eta) + 1e-6)); } } } @@ -914,49 +956,59 @@ struct OnTheFlyTofPid { // Fixme: assumes dominant resolution effect is the TOF resolution // and not the tracking itself. It's *probably* a fair assumption // but it should be tested further! --> FIXED IN THIS VERSION - if (trackLengthInnerTOF > 0 && trackLengthRecoInnerTOF > 0) + if (trackLengthInnerTOF > 0 && trackLengthRecoInnerTOF > 0) { nSigmaInnerTOF[ii] = deltaTimeInnerTOF[ii] / std::sqrt(innerTotalTimeReso * innerTotalTimeReso + tzero[1] * tzero[1]); - if (trackLengthOuterTOF > 0 && trackLengthRecoOuterTOF > 0) + } + if (trackLengthOuterTOF > 0 && trackLengthRecoOuterTOF > 0) { nSigmaOuterTOF[ii] = deltaTimeOuterTOF[ii] / std::sqrt(outerTotalTimeReso * outerTotalTimeReso + tzero[1] * tzero[1]); + } } if (plotsConfig.doQAplots) { - for (int ii = 0; ii < kParticles; ii++) { - if (std::fabs(mcParticle.pdgCode()) != pdg->GetParticle(kParticlePdgs[ii])->PdgCode()) { + for (int ii = 0; ii < NParticles; ii++) { + if (!doQaForParticle(Particles[ii].pdgCode) || std::fabs(mcParticle.pdgCode()) != pdgDatabase->GetParticle(Particles[ii].pdgCode)->PdgCode()) { continue; } if (trackLengthRecoInnerTOF > 0) { - for (int iii = 0; iii < kParticles; iii++) { + for (int iii = 0; iii < NParticles; iii++) { + if (!doQaForParticle(Particles[iii].pdgCode)) { + continue; + } h2dInnerNsigmaTrue[ii][iii]->Fill(momentumHypotheses[ii], nSigmaInnerTOF[iii]); h2dInnerDeltaTrue[ii][iii]->Fill(momentumHypotheses[ii], deltaTimeInnerTOF[iii]); } } if (trackLengthRecoOuterTOF > 0) { - for (int iii = 0; iii < kParticles; iii++) { + for (int iii = 0; iii < NParticles; iii++) { + if (!doQaForParticle(Particles[iii].pdgCode)) { + continue; + } h2dOuterNsigmaTrue[ii][iii]->Fill(momentumHypotheses[ii], nSigmaOuterTOF[iii]); h2dOuterDeltaTrue[ii][iii]->Fill(momentumHypotheses[ii], deltaTimeOuterTOF[iii]); } } } - } - const float deltaTrackLengthInnerTOF = std::abs(trackLengthInnerTOF - trackLengthRecoInnerTOF); - if (trackLengthInnerTOF > 0 && trackLengthRecoInnerTOF > 0) { - histos.fill(HIST("iTOF/h2dDeltaTrackLengthInnerVsPt"), noSmearingPt, deltaTrackLengthInnerTOF); - } - const float deltaTrackLengthOuterTOF = std::abs(trackLengthOuterTOF - trackLengthRecoOuterTOF); - if (trackLengthOuterTOF > 0 && trackLengthRecoOuterTOF > 0) { - histos.fill(HIST("oTOF/h2dDeltaTrackLengthOuterVsPt"), noSmearingPt, deltaTrackLengthOuterTOF); + const float deltaTrackLengthInnerTOF = std::abs(trackLengthInnerTOF - trackLengthRecoInnerTOF); + if (trackLengthInnerTOF > 0 && trackLengthRecoInnerTOF > 0) { + histos.fill(HIST("iTOF/h2dDeltaTrackLengthInnerVsPt"), noSmearingPt, deltaTrackLengthInnerTOF); + } + const float deltaTrackLengthOuterTOF = std::abs(trackLengthOuterTOF - trackLengthRecoOuterTOF); + if (trackLengthOuterTOF > 0 && trackLengthRecoOuterTOF > 0) { + histos.fill(HIST("oTOF/h2dDeltaTrackLengthOuterVsPt"), noSmearingPt, deltaTrackLengthOuterTOF); + } } // Sigmas have been fully calculated. Please populate the NSigma helper table (once per track) upgradeTof(tzero[0], tzero[1], - nSigmaInnerTOF[0], nSigmaInnerTOF[1], nSigmaInnerTOF[2], nSigmaInnerTOF[3], nSigmaInnerTOF[4], nSigmaInnerTOF[5], nSigmaInnerTOF[6], nSigmaInnerTOF[7], nSigmaInnerTOF[8], + nSigmaInnerTOF[El], nSigmaInnerTOF[Mu], nSigmaInnerTOF[Pi], nSigmaInnerTOF[Ka], nSigmaInnerTOF[Pr], nSigmaInnerTOF[De], nSigmaInnerTOF[Tr], nSigmaInnerTOF[He], nSigmaInnerTOF[Al], measuredTimeInnerTOF, trackLengthRecoInnerTOF, - nSigmaOuterTOF[0], nSigmaOuterTOF[1], nSigmaOuterTOF[2], nSigmaOuterTOF[3], nSigmaOuterTOF[4], nSigmaOuterTOF[5], nSigmaOuterTOF[6], nSigmaOuterTOF[7], nSigmaOuterTOF[8], + nSigmaOuterTOF[El], nSigmaOuterTOF[Mu], nSigmaOuterTOF[Pi], nSigmaOuterTOF[Ka], nSigmaOuterTOF[Pr], nSigmaOuterTOF[De], nSigmaOuterTOF[Tr], nSigmaOuterTOF[He], nSigmaOuterTOF[Al], measuredTimeOuterTOF, trackLengthRecoOuterTOF); - upgradeTofExpectedTime(expectedTimeInnerTOF[0], expectedTimeInnerTOF[1], expectedTimeInnerTOF[2], expectedTimeInnerTOF[3], expectedTimeInnerTOF[4], expectedTimeInnerTOF[5], expectedTimeInnerTOF[6], expectedTimeInnerTOF[7], expectedTimeInnerTOF[8], - expectedTimeOuterTOF[0], expectedTimeOuterTOF[1], expectedTimeOuterTOF[2], expectedTimeOuterTOF[3], expectedTimeOuterTOF[4], expectedTimeOuterTOF[5], expectedTimeOuterTOF[6], expectedTimeOuterTOF[7], expectedTimeOuterTOF[8]); + upgradeTofExpectedTime(expectedTimeInnerTOF[El], expectedTimeInnerTOF[Mu], expectedTimeInnerTOF[Pi], expectedTimeInnerTOF[Ka], expectedTimeInnerTOF[Pr], expectedTimeInnerTOF[De], expectedTimeInnerTOF[Tr], expectedTimeInnerTOF[He], expectedTimeInnerTOF[Al], + expectedTimeOuterTOF[El], expectedTimeOuterTOF[Mu], expectedTimeOuterTOF[Pi], expectedTimeOuterTOF[Ka], expectedTimeOuterTOF[Pr], expectedTimeOuterTOF[De], expectedTimeOuterTOF[Tr], expectedTimeOuterTOF[He], expectedTimeOuterTOF[Al]); + fillShortLivedTables(track, nSigmaInnerTOF, nSigmaOuterTOF, expectedTimeInnerTOF, expectedTimeOuterTOF, + trkWithTime.mTrackLengthInnerTOF, trkWithTime.mTrackLengthOuterTOF); } if (trackWithTimeIndex != tracks.size()) { diff --git a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx index 7cb3139aaf1..7e84ec9c223 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTracker.cxx @@ -23,14 +23,14 @@ /// \author Roberto Preghenella preghenella@bo.infn.it /// -#include "GeometryContainer.h" - +#include "ALICE3/Core/Decayer.h" #include "ALICE3/Core/DetLayer.h" #include "ALICE3/Core/FastTracker.h" #include "ALICE3/Core/FlatTrackSmearer.h" +#include "ALICE3/Core/GeometryContainer.h" +#include "ALICE3/Core/OTFParticle.h" #include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFCollision.h" -#include "ALICE3/DataModel/OTFMCParticle.h" #include "ALICE3/DataModel/OTFStrangeness.h" #include "ALICE3/DataModel/collisionAlice3.h" #include "ALICE3/DataModel/tracksAlice3.h" @@ -102,19 +102,22 @@ #include #include -#include - using namespace o2; using namespace o2::framework; using std::array; -#define getHist(type, name) \ - std::get>(histPointers[name]) -#define fillHist(type, name, ...) \ - if (histPointers.find(name) != histPointers.end()) \ - std::get>(histPointers[name])->Fill(__VA_ARGS__); \ - else \ - LOG(fatal) << "Histogram " << name << " not found!"; -#define insertHist(name, ...) histPointers[name] = histos.add((name).c_str(), __VA_ARGS__); + +enum TrackType { + kNone = 0, + kRecoPrimary, + kGhostPrimary, + kRecoV0Daug, + kGhostV0Daug, + kRecoCasc, + kGenCasc, + kRecoCascDaug, + kGenCascDaug, + kNTrackTypes, +}; struct OnTheFlyTracker { Produces tableCollisions; @@ -124,7 +127,6 @@ struct OnTheFlyTracker { Produces tableStoredTracksCov; Produces tableTracksCovExtension; Produces tableMcTrackLabels; - Produces tableMcTrackWithDauLabels; Produces tableTracksDCA; Produces tableTracksDCACov; Produces tableCollisionsAlice3; @@ -133,6 +135,7 @@ struct OnTheFlyTracker { Produces tableUpgradeCascades; Produces tableOTFLUTConfigId; Produces tableUpgradeV0s; + Produces tableUpgradeCascadeMcLabels; // optionally produced, empty (to be tuned later) Produces tableStoredTracksExtra; // base table, extend later @@ -180,6 +183,9 @@ struct OnTheFlyTracker { ConfigurableAxis axisRadius{"axisRadius", {2500, 0.0f, +250.0f}, "R (cm)"}; ConfigurableAxis axisZ{"axisZ", {100, -250.0f, +250.0f}, "Z (cm)"}; + + ConfigurableAxis axisNphotons{"axisNphotons", {10, 0.5f, 10.5f}, "N_{#gamma}"}; + ConfigurableAxis axisBRenergyLoss{"axisBRenergyLoss", {500, 0.0f, 1.0f}, "#Delta p / p"}; } axes; // for topo var QA @@ -196,6 +202,7 @@ struct OnTheFlyTracker { struct : ConfigurableGroup { std::string prefix = "fastPrimaryTrackerSettings"; + Configurable fastTrackShortLivedParticles{"fastTrackShortLivedParticles", false, "Use fasttracker for short lived tracks"}; Configurable fastTrackPrimaries{"fastTrackPrimaries", false, "Use fasttracker for primary tracks. Enable with care"}; Configurable minSiliconHits{"minSiliconHits", 4, "minimum number of silicon hits to accept track"}; Configurable applyZacceptance{"applyZacceptance", false, "apply z limits to detector layers or not"}; @@ -233,6 +240,15 @@ struct OnTheFlyTracker { Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; } cfgFitter; + struct : ConfigurableGroup { + std::string prefix = "cfgBR"; // configuration for bremsstrahlung + Configurable radiateBR{"radiateBR", false, "simulate bremsstrahlung"}; + Configurable doBRQA{"doBRQA", false, "Do QA for bremsstrahlung"}; + Configurable minEnergyFraction{"minEnergyFraction", 0.001f, "Minimum energy fraction a bremsstrahlung photon can carry"}; + Configurable maxEnergyFraction{"maxEnergyFraction", 0.95f, "Maximum energy fraction a bremsstrahlung photon can carry"}; + Configurable radiationStrength{"radiationStrength", 1e-6f, "Strenght of the bremsstrahlung radiation"}; + } cfgBR; + using PVertex = o2::dataformats::PrimaryVertex; // for secondary vertex finding @@ -258,29 +274,37 @@ struct OnTheFlyTracker { const float time = 0, const float timeError = 1, bool decayDauInput = false, + bool isPVContributorInput = false, bool weakDecayDauInput = false, int isUsedInCascadingInput = 0, int nSiliconHitsInput = 0, - int nTPCHitsInput = 0) : o2::track::TrackParCov(src), - mcLabel{label}, - timeEst{time, timeError}, - isDecayDau(decayDauInput), - isWeakDecayDau(weakDecayDauInput), - isUsedInCascading(isUsedInCascadingInput), - nSiliconHits(nSiliconHitsInput), - nTPCHits(nTPCHitsInput) {} - const TimeEst& getTimeMUS() const { return timeEst; } - int64_t mcLabel; - TimeEst timeEst; ///< time estimate in ns - bool isDecayDau; - bool isWeakDecayDau; - int isUsedInCascading; // 0: not at all, 1: is a cascade, 2: is a bachelor, 3: is a pion, 4: is a proton - int nSiliconHits; - int nTPCHits; + int nTPCHitsInput = 0, + TrackType trackTypeInput = TrackType::kRecoPrimary) : o2::track::TrackParCov(src), + mcLabel{label}, + timeEst{time, timeError}, + isDecayDau(decayDauInput), + isPVContributor(isPVContributorInput), + isWeakDecayDau(weakDecayDauInput), + isUsedInCascading(isUsedInCascadingInput), + nSiliconHits(nSiliconHitsInput), + nTPCHits(nTPCHitsInput), + trackType(trackTypeInput) {} + [[nodiscard]] const TimeEst& getTimeMUS() const { return timeEst; } + int64_t mcLabel = -1; ///< MC label of the track + TimeEst timeEst; ///< time estimate in ns + bool isDecayDau = false; ///< is a decay daughter + bool isPVContributor = false; ///< track is PV contributor + bool isWeakDecayDau = false; ///< is a weak decay daughter + int isUsedInCascading = 0; ///< 0: not at all, 1: is a cascade, 2: is a bachelor, 3: is a pion, 4: is a proton + int nSiliconHits = 0; ///< number of silicon hits + int nTPCHits = 0; ///< number of TPC hits + TrackType trackType = TrackType::kNone; + float trackIU = -1.f; ///< radius of the innermost hit (-1 if not available) + float trackOU = -1.f; ///< radius of the outermost hit (-1 if not available) }; // Helper struct to pass cascade information - struct cascadecandidate { + struct CascadeCandidate { int cascadeTrackId; // track index in the Tracks table int positiveId; // track index in the Tracks table int negativeId; // track index in the Tracks table @@ -301,10 +325,10 @@ struct OnTheFlyTracker { float mLambda; float mXi; }; - cascadecandidate thisCascade; + CascadeCandidate thisCascade{}; // Helper struct to pass V0 information - struct v0candidate { + struct V0Candidate { int positiveId; // track index in the Tracks table int negativeId; // track index in the Tracks table int mcParticleId; // mc particle index @@ -318,26 +342,37 @@ struct OnTheFlyTracker { float mAntiLambda; float mK0; }; - v0candidate thisV0; + V0Candidate thisV0{}; // Constants - static constexpr int kv0Prongs = 2; - static constexpr std::array v0PDGs = {kK0Short, - kLambda0, - kLambda0Bar}; - - static constexpr std::array longLivedHandledPDGs = {kElectron, - kMuonMinus, - kPiPlus, - kKPlus, - kProton}; - - static constexpr std::array nucleiPDGs = {o2::constants::physics::kDeuteron, + static constexpr int NDim3D = 3; + static constexpr int NV0Prongs = 2; + static constexpr int NCascProngs = 3; + + static constexpr std::array V0PDGs = {PDG_t::kK0Short, + PDG_t::kLambda0, + PDG_t::kLambda0Bar}; + + static constexpr std::array LongLivedHandledPDGs = {PDG_t::kElectron, + PDG_t::kMuonMinus, + PDG_t::kPiPlus, + PDG_t::kKPlus, + PDG_t::kProton}; + + static constexpr std::array ShortLivedHandledPDGs = {PDG_t::kSigmaPlus, + PDG_t::kSigmaMinus, + PDG_t::kXiMinus, + PDG_t::kOmegaMinus}; + + static constexpr std::array NucleiPDGs = {o2::constants::physics::kDeuteron, o2::constants::physics::kTriton, o2::constants::physics::kHelium3, o2::constants::physics::kAlpha}; + // Primary vertexing QA + std::pair vertexReconstructionEfficiencyCounters = {0, 0}; // {nVerticesWithMoreThan2Contributors, nVerticesReconstructed} + // necessary for particle charges - Service pdgDB; + Service pdgDB{}; // for handling basic QA histograms if requested HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -346,24 +381,56 @@ struct OnTheFlyTracker { o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; // Track smearer array, one per geometry - std::vector> mSmearer; + std::vector> mSmearer; + // Configuration defined at init time + o2::fastsim::GeometryContainer mGeoContainer; + float mMagneticField = 0.0f; + int icfg{}; // For processing and vertexing + std::vector recoPrimaries; + std::vector ghostPrimaries; std::vector tracksAlice3; std::vector ghostTracksAlice3; std::vector bcData; o2::steer::InteractionSampler irSampler; o2::vertexing::PVertexer vertexer; - std::vector cascadesAlice3; - std::vector v0sAlice3; + std::vector cascadesAlice3; + std::vector v0sAlice3; // For TGenPhaseSpace seed TRandom3 rand; - Service ccdb; + Service ccdb{}; + o2::upgrade::Decayer decayer; + + // Time resolution constants + static constexpr float TimeResolutionNs = 100.f; // ns + static constexpr float NsToMus = 1e-3f; + static constexpr float TimeResolutionUs = TimeResolutionNs * NsToMus; // us + + o2::dataformats::DCA dcaInfo; + + template + std::shared_ptr getHist(const std::string& name) + { + return std::get>(histPointers.at(name)); + } + + template + void fillHist(const std::string& name, Args&&... args) + { + const auto it = histPointers.find(name); + if (it == histPointers.end()) { + LOG(fatal) << "Histogram " << name << " not found!"; + } + std::get>(it->second)->Fill(std::forward(args)...); + } + + void insertHist(const std::string& name, const std::string& title, HistType type, const std::vector& axisSpecs) + { + histPointers[name] = histos.add(name.c_str(), title.c_str(), type, axisSpecs); + } - // Configuration defined at init time - o2::fastsim::GeometryContainer mGeoContainer; - float mMagneticField = 0.0f; void init(o2::framework::InitContext& initContext) { LOG(info) << "Initializing OnTheFlyTracker task"; @@ -374,16 +441,17 @@ struct OnTheFlyTracker { const int nGeometries = mGeoContainer.getNumberOfConfigurations(); mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); - for (int icfg = 0; icfg < nGeometries; ++icfg) { - const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; - mSmearer.emplace_back(std::make_unique()); - mSmearer[icfg]->setCcdbManager(ccdb.operator->()); - std::map globalConfiguration = mGeoContainer.getConfiguration(icfg, "global"); + decayer.setBField(mMagneticField); + for (int igeo = 0; igeo < nGeometries; ++igeo) { + const std::string histPath = "Configuration_" + std::to_string(igeo) + "/"; + mSmearer.emplace_back(std::make_unique()); + mSmearer[igeo]->setCcdbManager(ccdb.operator->()); + std::map globalConfiguration = mGeoContainer.getConfiguration(igeo, "global"); if (enablePrimarySmearing) { // load LUTs for primaries for (const auto& entry : globalConfiguration) { int pdg = 0; - if (entry.first.find("lut") != 0) { + if (!entry.first.starts_with("lut")) { continue; } if (entry.first.find("lutEl") != std::string::npos) { @@ -410,7 +478,7 @@ struct OnTheFlyTracker { if (pdg == 0) { LOG(fatal) << "Unknown LUT entry " << entry.first << " for global configuration"; } - LOG(info) << "Loading LUT for pdg " << pdg << " for config " << icfg << " from provided file '" << filename << "'"; + LOG(info) << "Loading LUT for pdg " << pdg << " for config " << igeo << " from provided file '" << filename << "'"; if (filename.empty()) { LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; } @@ -420,111 +488,150 @@ struct OnTheFlyTracker { if (filename.empty()) { LOG(warning) << "No LUT file passed for pdg " << pdg << ", skipping."; } - bool success = mSmearer[icfg]->loadTable(pdg, filename.c_str()); - if (!success) { + if (!mSmearer[igeo]->loadTable(pdg, filename.c_str())) { LOG(fatal) << "Having issue with loading the LUT " << pdg << " " << filename; } } // interpolate efficiencies if requested to do so - mSmearer[icfg]->interpolateEfficiency(interpolateLutEfficiencyVsNch.value); + mSmearer[igeo]->interpolateEfficiency(interpolateLutEfficiencyVsNch.value); // smear un-reco'ed tracks if asked to do so - mSmearer[icfg]->skipUnreconstructed(!processUnreconstructedTracks.value); - - insertHist(histPath + "hPtGenerated", "hPtGenerated;#it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPhiGenerated", "hPhiGenerated;#phi (rad);Counts", {kTH1D, {{100, 0.0f, 2 * M_PI, "#phi (rad)"}}}); - - insertHist(histPath + "hPtGeneratedEl", "hPtGeneratedEl;Gen #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtGeneratedPi", "hPtGeneratedPi;Gen #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtGeneratedKa", "hPtGeneratedKa;Gen #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtGeneratedPr", "hPtGeneratedPr;Gen #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtReconstructed", "hPtReconstructed;Reco #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtReconstructedEl", "hPtReconstructedEl;Reco #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtReconstructedPi", "hPtReconstructedPi;Reco #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtReconstructedKa", "hPtReconstructedKa;Reco #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); - insertHist(histPath + "hPtReconstructedPr", "hPtReconstructedPr;Reco #it{p}_{T} (GeV/c);Counts", {kTH1D, {{axes.axisMomentum}}}); + mSmearer[igeo]->skipUnreconstructed(!processUnreconstructedTracks.value); + + insertHist(histPath + "hPtGenerated", "hPtGenerated;#it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPhiGenerated", "hPhiGenerated;#phi (rad);Counts", kTH1D, {{100, 0.0f, o2::constants::math::TwoPI, "#phi (rad)"}}); + + insertHist(histPath + "hPtGeneratedEl", "hPtGeneratedEl;Gen #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtGeneratedPi", "hPtGeneratedPi;Gen #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtGeneratedKa", "hPtGeneratedKa;Gen #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtGeneratedPr", "hPtGeneratedPr;Gen #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtReconstructed", "hPtReconstructed;Reco #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtReconstructedEl", "hPtReconstructedEl;Reco #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtReconstructedPi", "hPtReconstructedPi;Reco #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtReconstructedKa", "hPtReconstructedKa;Reco #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); + insertHist(histPath + "hPtReconstructedPr", "hPtReconstructedPr;Reco #it{p}_{T} (GeV/c);Counts", kTH1D, {{axes.axisMomentum}}); } // Collision QA - insertHist(histPath + "hPVz", "hPVz;Primary Vertex Z (cm);Counts", {kTH1D, {{axes.axisVertexZ}}}); - insertHist(histPath + "hLUTMultiplicity", "hLUTMultiplicity;dN/d#eta;Counts", {kTH1D, {{axes.axisMultiplicity}}}); - insertHist(histPath + "hSimMultiplicity", "hSimMultiplicity;Gen. multiplicity;Counts", {kTH1D, {{axes.axisMultiplicity}}}); - insertHist(histPath + "hRecoMultiplicity", "hRecoMultiplicity;Reco Multiplicity;Counts", {kTH1D, {{axes.axisMultiplicity}}}); + insertHist(histPath + "hPVz", "hPVz;Primary Vertex Z (cm);Counts", kTH1D, {{axes.axisVertexZ}}); + insertHist(histPath + "hLUTMultiplicity", "hLUTMultiplicity;dN/d#eta;Counts", kTH1D, {{axes.axisMultiplicity}}); + insertHist(histPath + "hSimMultiplicity", "hSimMultiplicity;Gen. multiplicity;Counts", kTH1D, {{axes.axisMultiplicity}}); + insertHist(histPath + "hRecoMultiplicity", "hRecoMultiplicity;Reco Multiplicity;Counts", kTH1D, {{axes.axisMultiplicity}}); if (enablePrimaryVertexing) { - insertHist(histPath + "hDeltaXPVRecoGen", "hDeltaXPVRecoGen;Delta X (reco - gen), cm", {kTH1D, {{axes.axisDeltaVtxCoord}}}); - insertHist(histPath + "hDeltaYPVRecoGen", "hDeltaYPVRecoGen;Delta Y (reco - gen), cm", {kTH1D, {{axes.axisDeltaVtxCoord}}}); - insertHist(histPath + "hDeltaZPVRecoGen", "hDeltaZPVRecoGen;Delta Z (reco - gen), cm", {kTH1D, {{axes.axisDeltaVtxCoord}}}); - insertHist(histPath + "hDeltaMultPVRecoGen", "hDeltaMultPVRecoGen;Delta Multiplicity (reco - gen)", {kTH1D, {{axes.axisDeltaMultPVRecoGen}}}); - insertHist(histPath + "hVtxMultGen", "hVtxMultGen;Generated Vertex Multiplicity", {kTH1D, {{axes.axisVtxMult}}}); - insertHist(histPath + "hVtxMultReco", "hVtxMultReco;Reconstructed Vertex Multiplicity", {kTH1D, {{axes.axisVtxMult}}}); - insertHist(histPath + "hVtxTrials", "hVtxTrials;Vertex Reconstruction Trials", {kTH1D, {{2, -0.5, 1.5}}}); + insertHist(histPath + "hDeltaXPVRecoGen", "hDeltaXPVRecoGen;Delta X (reco - gen), cm", kTH2D, {{axes.axisDeltaVtxCoord, axes.axisMultiplicity}}); + insertHist(histPath + "hDeltaYPVRecoGen", "hDeltaYPVRecoGen;Delta Y (reco - gen), cm", kTH2D, {{axes.axisDeltaVtxCoord, axes.axisMultiplicity}}); + insertHist(histPath + "hDeltaZPVRecoGen", "hDeltaZPVRecoGen;Delta Z (reco - gen), cm", kTH2D, {{axes.axisDeltaVtxCoord, axes.axisMultiplicity}}); + insertHist(histPath + "hDeltaMultPVRecoGen", "hDeltaMultPVRecoGen;Delta Multiplicity (reco - gen)", kTH1D, {{axes.axisDeltaMultPVRecoGen}}); + insertHist(histPath + "hVtxMultGen", "hVtxMultGen;Generated Vertex Multiplicity", kTH1D, {{axes.axisVtxMult}}); + insertHist(histPath + "hVtxMultReco", "hVtxMultReco;Reconstructed Vertex Multiplicity", kTH1D, {{axes.axisVtxMult}}); + insertHist(histPath + "hVtxTrials", "hVtxTrials;Vertex Reconstruction Trials", kTH1D, {{2, -0.5, 1.5}}); // Set the bin labels - getHist(TH1, histPath + "hVtxTrials")->GetXaxis()->SetBinLabel(1, "Tried"); - getHist(TH1, histPath + "hVtxTrials")->GetXaxis()->SetBinLabel(2, "Succeeded"); + getHist(histPath + "hVtxTrials")->GetXaxis()->SetBinLabel(1, "Tried"); + getHist(histPath + "hVtxTrials")->GetXaxis()->SetBinLabel(2, "Succeeded"); } - if (enableSecondarySmearing) { + if (enableSecondarySmearing || + fastPrimaryTrackerSettings.fastTrackPrimaries || + fastPrimaryTrackerSettings.fastTrackShortLivedParticles) { fastTracker.emplace_back(std::make_unique()); - fastTracker[icfg]->SetMagneticField(mMagneticField); - fastTracker[icfg]->SetApplyZacceptance(fastTrackerSettings.applyZacceptance); - fastTracker[icfg]->SetApplyMSCorrection(fastTrackerSettings.applyMSCorrection); - fastTracker[icfg]->SetApplyElossCorrection(fastTrackerSettings.applyElossCorrection); - fastTracker[icfg]->AddGenericDetector(mGeoContainer.getEntry(icfg), ccdb.operator->()); - fastTracker[icfg]->Print(); // print fastTracker settings + fastTracker[igeo]->setMagneticField(mMagneticField); + fastTracker[igeo]->setApplyZacceptance(fastTrackerSettings.applyZacceptance); + fastTracker[igeo]->setApplyMSCorrection(fastTrackerSettings.applyMSCorrection); + fastTracker[igeo]->setApplyElossCorrection(fastTrackerSettings.applyElossCorrection); + fastTracker[igeo]->addGenericDetector(mGeoContainer.getEntry(igeo), ccdb.operator->()); + fastTracker[igeo]->print(); // print fastTracker settings if (cascadeDecaySettings.doXiQA) { - insertHist(histPath + "hXiBuilding", "hXiBuilding", {kTH1F, {{10, -0.5f, 9.5f}}}); - - insertHist(histPath + "hGenXi", "hGenXi", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); - insertHist(histPath + "hRecoXi", "hRecoXi", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); - - insertHist(histPath + "hGenPiFromXi", "hGenPiFromXi", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); - insertHist(histPath + "hGenPiFromLa", "hGenPiFromLa", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); - insertHist(histPath + "hGenPrFromLa", "hGenPrFromLa", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); - insertHist(histPath + "hRecoPiFromXi", "hRecoPiFromXi", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); - insertHist(histPath + "hRecoPiFromLa", "hRecoPiFromLa", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); - insertHist(histPath + "hRecoPrFromLa", "hRecoPrFromLa", {kTH2F, {axes.axisDecayRadius, axes.axisMomentum}}); + insertHist(histPath + "hXiBuilding", "hXiBuilding", kTH1F, {{10, -0.5f, 9.5f}}); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(1, "Generated"); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(2, "Secondary smearing prong 0"); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(3, "Secondary smearing prong 1"); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(4, "Secondary smearing prong 2"); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(5, "Not Nan"); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(6, "Start Reco"); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(7, "V0 fitter ok"); + getHist(histPath + "hXiBuilding")->GetXaxis()->SetBinLabel(8, "Kink fitter ok"); + + insertHist(histPath + "hGenXi", "hGenXi;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); + insertHist(histPath + "hRecoXi", "hRecoXi;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); + + insertHist(histPath + "hGenPiFromXi", "hGenPiFromXi;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); + insertHist(histPath + "hGenPiFromLa", "hGenPiFromLa;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); + insertHist(histPath + "hGenPrFromLa", "hGenPrFromLa;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); + insertHist(histPath + "hRecoPiFromXi", "hRecoPiFromXi;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); + insertHist(histPath + "hRecoPiFromLa", "hRecoPiFromLa;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); + insertHist(histPath + "hRecoPrFromLa", "hRecoPrFromLa;Decay Radius;#it{p}_{T}", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); // basic mass histograms to see if we're in business - insertHist(histPath + "hMassLambda", "hMassLambda", {kTH1F, {{axes.axisLambdaMass}}}); - insertHist(histPath + "hMassXi", "hMassXi", {kTH1F, {{axes.axisXiMass}}}); - insertHist(histPath + "h2dMassXi", "h2dMassXi", {kTH2F, {{axes.axisXiMass}, {axes.axisMomentum}}}); + insertHist(histPath + "hMassLambda", "hMassLambda;Mass;Counts", kTH1F, {{axes.axisLambdaMass}}); + insertHist(histPath + "hMassXi", "hMassXi;Mass;Counts", kTH1F, {{axes.axisXiMass}}); + insertHist(histPath + "h2dMassXi", "h2dMassXi;Mass;Counts", kTH2F, {{axes.axisXiMass}, {axes.axisMomentum}}); // OTF strangeness tracking QA - insertHist(histPath + "hFoundVsFindable", "hFoundVsFindable", {kTH2F, {{10, -0.5f, 9.5f}, {10, -0.5f, 9.5f}}}); - - insertHist(histPath + "h2dDCAxyCascade", "h2dDCAxyCascade", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - insertHist(histPath + "h2dDCAxyCascadeBachelor", "h2dDCAxyCascadeBachelor", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - insertHist(histPath + "h2dDCAxyCascadeNegative", "h2dDCAxyCascadeNegative", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - insertHist(histPath + "h2dDCAxyCascadePositive", "h2dDCAxyCascadePositive", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - - insertHist(histPath + "h2dDCAzCascade", "h2dDCAzCascade", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - insertHist(histPath + "h2dDCAzCascadeBachelor", "h2dDCAzCascadeBachelor", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - insertHist(histPath + "h2dDCAzCascadeNegative", "h2dDCAzCascadeNegative", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - insertHist(histPath + "h2dDCAzCascadePositive", "h2dDCAzCascadePositive", {kTH2F, {axes.axisMomentum, axes.axisDCA}}); - - insertHist(histPath + "h2dDeltaPtVsPt", "h2dDeltaPtVsPt;Gen p_{T};#Delta p_{T}", {kTH2F, {axes.axisMomentum, axes.axisDeltaPt}}); - insertHist(histPath + "h2dDeltaEtaVsPt", "h2dDeltaEtaVsPt;Gen p_{T};#Delta #eta", {kTH2F, {axes.axisMomentum, axes.axisDeltaEta}}); - - insertHist(histPath + "hFastTrackerHits", "hFastTrackerHits", {kTH2F, {axes.axisZ, axes.axisRadius}}); - insertHist(histPath + "hFastTrackerQA", "hFastTrackerQA", {kTH1F, {{8, -0.5f, 7.5f}}}); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(1, "Negative eigenvalue"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(2, "Failed sanity check"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(3, "intercept original radius"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(4, "propagate to original radius"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(5, "problematic layer"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(6, "multiple scattering"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(7, "energy loss"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(8, "efficiency"); - getHist(TH1, histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(9, "no layers hit"); + insertHist(histPath + "hFoundVsFindable", "hFoundVsFindable;Found;Findable", kTH2F, {{10, -0.5f, 9.5f}, {10, -0.5f, 9.5f}}); + + insertHist(histPath + "h2dDCAxyCascade", "h2dDCAxyCascade;#it{p}_{T};DCA_{xy}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + insertHist(histPath + "h2dDCAxyCascadeBachelor", "h2dDCAxyCascadeBachelor;#it{p}_{T};DCA_{xy}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + insertHist(histPath + "h2dDCAxyCascadeNegative", "h2dDCAxyCascadeNegative;#it{p}_{T};DCA_{xy}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + insertHist(histPath + "h2dDCAxyCascadePositive", "h2dDCAxyCascadePositive;#it{p}_{T};DCA_{xy}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + insertHist(histPath + "h2dDCAzCascade", "h2dDCAzCascade;#it{p}_{T};DCA_{z}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + insertHist(histPath + "h2dDCAzCascadeBachelor", "h2dDCAzCascadeBachelor;#it{p}_{T};DCA_{z}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + insertHist(histPath + "h2dDCAzCascadeNegative", "h2dDCAzCascadeNegative;#it{p}_{T};DCA_{z}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + insertHist(histPath + "h2dDCAzCascadePositive", "h2dDCAzCascadePositive;#it{p}_{T};DCA_{z}", kTH2F, {axes.axisMomentum, axes.axisDCA}); + + insertHist(histPath + "h2dDeltaPtVsPt", "h2dDeltaPtVsPt;Gen p_{T};#Delta p_{T}", kTH2F, {axes.axisMomentum, axes.axisDeltaPt}); + insertHist(histPath + "h2dDeltaEtaVsPt", "h2dDeltaEtaVsPt;Gen p_{T};#Delta #eta", kTH2F, {axes.axisMomentum, axes.axisDeltaEta}); + + insertHist(histPath + "nSiliconHitsCascadeProngs", "nSiliconHitsCascadeProngs", kTH1F, {{40, -0.5f, 39.5f}}); + insertHist(histPath + "nTPCHitsCascadeProngs", "nTPCHitsCascadeProngs", kTH1F, {{10, -0.5f, 9.5f}}); + insertHist(histPath + "hFastTrackerHits", "hFastTrackerHits", kTH2F, {axes.axisZ, axes.axisRadius}); + insertHist(histPath + "hFastTrackerQA", "hFastTrackerQA", kTH1F, {{8, -0.5f, 7.5f}}); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(1, "Negative eigenvalue"); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(2, "Failed sanity check"); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(3, "intercept original radius"); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(4, "propagate to original radius"); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(5, "problematic layer"); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(6, "multiple scattering"); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(7, "energy loss"); + getHist(histPath + "hFastTrackerQA")->GetXaxis()->SetBinLabel(8, "efficiency"); } } if (doExtraQA) { - insertHist(histPath + "h2dPtRes", "h2dPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", {kTH2D, {{axes.axisMomentum, axes.axisPtRes}}}); - insertHist(histPath + "h2dDCAxy", "h2dDCAxy;p_{T};DCA_{xy}", {kTH2D, {{axes.axisMomentum, axes.axisDCA}}}); - insertHist(histPath + "h2dDCAz", "h2dDCAz;p_{T};DCA_{z}", {kTH2D, {{axes.axisMomentum, axes.axisDCA}}}); + insertHist(histPath + "h2dPtRes", "h2dPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dPtResAbs", "h2dPtResAbs;Gen p_{T};#Delta p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dDCAxy", "h2dDCAxy;p_{T};DCA_{xy}", kTH2D, {{axes.axisMomentum, axes.axisDCA}}); + insertHist(histPath + "h2dDCAz", "h2dDCAz;p_{T};DCA_{z}", kTH2D, {{axes.axisMomentum, axes.axisDCA}}); + } + + if (cfgBR.doBRQA) { + insertHist(histPath + "h1dNBRPhotons", "h1dNBRPhotons;N_{#gamma};Counts", kTH1D, {{axes.axisNphotons}}); + insertHist(histPath + "h1dBREnergyLoss", "h1dBREnergyLoss;#Delta p / p;Counts", kTH1D, {{axes.axisBRenergyLoss}}); + + insertHist(histPath + "h2dBRPtRes", "h2dPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dBRPtResAbs", "h2dPtResAbs;Gen p_{T};#Delta p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + } + + if (doprocessDecayer) { + insertHist(histPath + "h2dPrimaryPtRes", "h2dPrimaryPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dSecondaryPtRes", "h2dSecondaryPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dPrimaryElPtRes", "h2dPrimaryElPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dSecondaryElPtRes", "h2dSecondaryElPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dPrimaryPiPtRes", "h2dPrimaryPiPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dSecondaryPiPtRes", "h2dSecondaryPiPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dPrimaryKaPtRes", "h2dPrimaryKaPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dSecondaryKaPtRes", "h2dSecondaryKaPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dPrimaryPrPtRes", "h2dPrimaryPrPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + insertHist(histPath + "h2dSecondaryPrPtRes", "h2dSecondaryPrPtRes;Gen p_{T};#Delta p_{T} / Reco p_{T}", kTH2D, {{axes.axisMomentum, axes.axisPtRes}}); + + if (fastPrimaryTrackerSettings.fastTrackShortLivedParticles) { + insertHist(histPath + "h2dGenShortLivedParticleRadius", "h2dGenShortLivedParticleRadius;Radius (cm);Momentum p_{T}", kTH2D, {{axes.axisDecayRadius, axes.axisMomentum}}); + insertHist(histPath + "h2dRecShortLivedParticleRadius", "h2dRecShortLivedParticleRadius;Radius (cm);Momentum p_{T}", kTH2D, {{axes.axisDecayRadius, axes.axisMomentum}}); + insertHist(histPath + "h2dGenRadiusIniVsDecay", "h2dGenRadiusIniVsDecay;Radius (cm);Radius (cm)", kTH2D, {{axes.axisDecayRadius, axes.axisDecayRadius}}); + insertHist(histPath + "h2dRecRadiusIniVsDecay", "h2dRecRadiusIniVsDecay;Radius (cm);Radius (cm)", kTH2D, {{axes.axisDecayRadius, axes.axisDecayRadius}}); + insertHist(histPath + "h2dDecayRadiusVsNhits", "h2dDecayRadiusVsNhits;Radius (cm);Nhits", kTH2D, {{axes.axisDecayRadius, {20, 0.5, 20}}}); + } } } // end config loop @@ -576,8 +683,8 @@ struct OnTheFlyTracker { } if (v0DecaySettings.doV0QA) { - for (int icfg = 0; icfg < nGeometries; icfg++) { - std::string v0histPath = "V0Building_Configuration_" + std::to_string(icfg) + "/"; + for (int igeo = 0; igeo < nGeometries; igeo++) { + std::string v0histPath = "V0Building_Configuration_" + std::to_string(igeo) + "/"; insertHist(v0histPath + "hV0Building", "hV0Building", kTH1F, {{10, -0.5f, 9.5f}}); insertHist(v0histPath + "hFastTrackerHits", "hV0Building", kTH2F, {{axes.axisZ, axes.axisRadius}}); auto h = histos.add(v0histPath + "hFastTrackerQA", "hFastTrackerQA", kTH1D, {{8, -0.5f, 7.5f}}); @@ -599,7 +706,6 @@ struct OnTheFlyTracker { insertHist(v0histPath + "K0/hRecoNegDaughterFromV0", "hRecoNegDaughterFromV0", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "K0/hRecoPosDaughterFromV0", "hRecoPosDaughterFromV0", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "K0/hMass", "hMass", kTH2F, {axes.axisK0Mass, axes.axisMomentum}); - insertHist(v0histPath + "K0/hFinalMass", "hFinalMass", kTH1F, {axes.axisK0Mass}); // Lambda insertHist(v0histPath + "Lambda/hGen", "hGen", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "Lambda/hReco", "hReco", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); @@ -608,8 +714,6 @@ struct OnTheFlyTracker { insertHist(v0histPath + "Lambda/hRecoNegDaughterFromV0", "hRecoNegDaughterFromV0", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "Lambda/hRecoPosDaughterFromV0", "hRecoPosDaughterFromV0", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "Lambda/hMass", "hMass", kTH2F, {axes.axisLambdaMass, axes.axisMomentum}); - insertHist(v0histPath + "Lambda/hFinalMass", "hFinalMass", kTH1F, {axes.axisLambdaMass}); - // AntiLambda insertHist(v0histPath + "AntiLambda/hGen", "hGen", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "AntiLambda/hReco", "hReco", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); @@ -618,7 +722,6 @@ struct OnTheFlyTracker { insertHist(v0histPath + "AntiLambda/hRecoNegDaughterFromV0", "hRecoNegDaughterFromV0", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "AntiLambda/hRecoPosDaughterFromV0", "hRecoPosDaughterFromV0", kTH2F, {axes.axisDecayRadius, axes.axisMomentum}); insertHist(v0histPath + "AntiLambda/hMass", "hMass", kTH2F, {axes.axisLambdaMass, axes.axisMomentum}); - insertHist(v0histPath + "AntiLambda/hFinalMass", "hFinalMass", kTH1F, {axes.axisLambdaMass}); } } @@ -629,7 +732,7 @@ struct OnTheFlyTracker { auto field = grpmag.getNominalL3Field(); o2::base::Propagator::initFieldFromGRP(&grpmag); - auto fieldInstance = static_cast(TGeoGlobalMagField::Instance()->GetField()); + auto fieldInstance = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); if (!fieldInstance) { LOGF(fatal, "Failed to set up magnetic field! Stopping now!"); } @@ -637,7 +740,7 @@ struct OnTheFlyTracker { // Cross-check LOGF(info, "Check field at (0, 0, 0): %.1f kG, nominal: %.1f", static_cast(fieldInstance->GetBz(0, 0, 0)), static_cast(field)); LOGF(info, "Initializing empty material cylinder LUT - could be better in the future"); - o2::base::MatLayerCylSet* lut = new o2::base::MatLayerCylSet(); + auto* lut = new o2::base::MatLayerCylSet(); lut->addLayer(200, 200.1, 2, 1.0f, 100.0f); LOGF(info, "MatLayerCylSet::optimizePhiSlices()"); lut->optimizePhiSlices(); @@ -679,24 +782,28 @@ struct OnTheFlyTracker { /// \param pdgCode pdg code for a common particle (particle handled by the tracker) int pdgCodeToPID(int pdgCode) const { - if (std::abs(pdgCode) == PDG_t::kElectron) { - return o2::track::PID::Electron; - } else if (std::abs(pdgCode) == PDG_t::kMuonMinus) { - return o2::track::PID::Muon; - } else if (std::abs(pdgCode) == PDG_t::kPiPlus) { - return o2::track::PID::Pion; - } else if (std::abs(pdgCode) == PDG_t::kKPlus) { - return o2::track::PID::Kaon; - } else if (std::abs(pdgCode) == PDG_t::kProton) { - return o2::track::PID::Proton; - } else if (std::abs(pdgCode) == PDG_t::kLambda0) { - return o2::track::PID::Lambda; - } else if (std::abs(pdgCode) == PDG_t::kXiMinus) { - return o2::track::PID::XiMinus; - } else if (std::abs(pdgCode) == PDG_t::kOmegaMinus) { - return o2::track::PID::OmegaMinus; - } else { - return o2::track::PID::Pion; // Default trackParCov assumption + switch (std::abs(pdgCode)) { + case PDG_t::kElectron: + return o2::track::PID::Electron; + case PDG_t::kMuonMinus: + return o2::track::PID::Muon; + case PDG_t::kPiPlus: + return o2::track::PID::Pion; + case PDG_t::kKPlus: + return o2::track::PID::Kaon; + case PDG_t::kProton: + return o2::track::PID::Proton; + case PDG_t::kLambda0: + return o2::track::PID::Lambda; + case PDG_t::kSigmaPlus: + case PDG_t::kSigmaMinus: + return o2::track::PID::XiMinus; // Close enough + case PDG_t::kXiMinus: + return o2::track::PID::XiMinus; + case PDG_t::kOmegaMinus: + return o2::track::PID::OmegaMinus; + default: + return o2::track::PID::Pion; // Default trackParCov assumption } } @@ -707,14 +814,14 @@ struct OnTheFlyTracker { /// \param xiDecayVertex the address of the xi decay vertex /// \param laDecayVertex the address of the la decay vertex template - void decayCascade(McParticleType particle, o2::track::TrackParCov track, std::vector& decayDaughters, std::vector& xiDecayVertex, std::vector& laDecayVertex) + void decayCascade(const McParticleType& particle, o2::track::TrackParCov track, std::vector& decayDaughters, std::vector& xiDecayVertex, std::vector& laDecayVertex) { const double uXi = rand.Uniform(0, 1); const double ctauXi = 4.91; // cm const double betaGammaXi = particle.p() / o2::constants::physics::MassXiMinus; const double rxyzXi = (-betaGammaXi * ctauXi * std::log(1 - uXi)); - float sna, csa; + float sna{0.f}, csa{0.f}; o2::math_utils::CircleXYf_t circleXi; track.getCircleParams(mMagneticField, circleXi, sna, csa); const double rxyXi = rxyzXi / std::sqrt(1. + track.getTgl() * track.getTgl()); @@ -758,7 +865,7 @@ struct OnTheFlyTracker { /// \param decayDaughters the address of resulting daughters /// \param v0DecayVertex the address of the la decay vertex template - void decayV0Particle(McParticleType particle, std::vector& decayDaughters, std::vector& v0DecayVertex, int pdgCode) + void decayV0Particle(const McParticleType& particle, std::vector& decayDaughters, std::vector& v0DecayVertex, int pdgCode) { double u = rand.Uniform(0, 1); double v0Mass = -1.; @@ -806,30 +913,13 @@ struct OnTheFlyTracker { decayDaughters.push_back(*v0Decay.GetDecay(1)); } - float dNdEta = 0.f; // Charged particle multiplicity to use in the efficiency evaluation - std::pair vertexReconstructionEfficiencyCounters = {0, 0}; // {nVerticesWithMoreThan2Contributors, nVerticesReconstructed} - void processWithLUTs(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles, const int icfg) + /// Function to compute dN/deta for a given set of MC particles + /// \param dNdEta the address of the variable to fill with the computed dN/deta value + /// \param mcParticles the set of MC particles to compute dN/deta from + /// \param histPath the path to the histogram where the computed dN/deta value will be stored for QA purposes + template + void computeDNDEta(float& dNdEta, McParticleType const& mcParticles, const std::string& histPath) { - vertexReconstructionEfficiencyCounters.first += 1; - const int lastTrackIndex = tableStoredTracksCov.lastIndex() + 1; // bookkeep the last added track - const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; - - tracksAlice3.clear(); - ghostTracksAlice3.clear(); - bcData.clear(); - cascadesAlice3.clear(); - v0sAlice3.clear(); - - o2::dataformats::DCA dcaInfo; - o2::dataformats::VertexBase vtx; - - // generate collision time - auto ir = irSampler.generateCollisionTime(); - const float eventCollisionTimeNS = ir.timeInBCNS; - - // First we compute the number of charged particles in the event - dNdEta = 0.f; - LOG(debug) << "Processing " << mcParticles.size() << " MC particles to compute dNch/deta"; for (const auto& mcParticle : mcParticles) { if (std::abs(mcParticle.eta()) > multEtaRange) { continue; @@ -840,9 +930,9 @@ struct OnTheFlyTracker { } const auto pdg = std::abs(mcParticle.pdgCode()); - const bool longLivedToBeHandled = std::find(longLivedHandledPDGs.begin(), longLivedHandledPDGs.end(), pdg) != longLivedHandledPDGs.end(); - const bool nucleiToBeHandled = std::find(nucleiPDGs.begin(), nucleiPDGs.end(), pdg) != nucleiPDGs.end(); - const bool pdgsToBeHandled = longLivedToBeHandled || (enableNucleiSmearing && nucleiToBeHandled) || (cascadeDecaySettings.decayXi && mcParticle.pdgCode() == kXiMinus); + const bool longLivedToBeHandled = std::find(LongLivedHandledPDGs.begin(), LongLivedHandledPDGs.end(), pdg) != LongLivedHandledPDGs.end(); + const bool nucleiToBeHandled = std::find(NucleiPDGs.begin(), NucleiPDGs.end(), pdg) != NucleiPDGs.end(); + const bool pdgsToBeHandled = longLivedToBeHandled || (enableNucleiSmearing && nucleiToBeHandled); if (!pdgsToBeHandled) { continue; } @@ -860,885 +950,857 @@ struct OnTheFlyTracker { LOG(debug) << "Computed dNch/deta before normalization: " << dNdEta; dNdEta /= (multEtaRange * 2.0f); - uint32_t multiplicityCounter = 0; - getHist(TH1, histPath + "hLUTMultiplicity")->Fill(dNdEta); + getHist(histPath + "hLUTMultiplicity")->Fill(dNdEta); + } + + /// Function to study the cascade decay and fill the relevant histograms and output track vector + /// \param trackTableOffset the offset to apply to the global track indices when filling the output track vector + /// \param mcParticle the cascade MC particle to study + /// \param tracksCascadeProngs the address of the vector of output tracks to fill with the cascade daughters + /// \param primaryVertex the primary vertex of the event, needed for the track propagation in the cascade decay + /// \param dNdEta the dN/deta of the event, needed for the track time smearing + /// \param eventCollisionTimeNS the collision time of the event in ns, needed for the track time smearing + template + void studyCascade(const int trackTableOffset, + const McParticleType& mcParticle, + std::vector& tracksCascadeProngs, + o2::vertexing::PVertex& primaryVertex, + float dNdEta, + float eventCollisionTimeNS) + { + enum CascadeRecoMode { + Topo = 0, + ComplKink, + KinkOnly + }; + + o2::track::TrackParCov trackParCov; + o2::upgrade::convertMCParticleToO2Track(mcParticle, trackParCov, pdgDB); + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; - // Now that the multiplicity is known, we can process the particles to smear them - double xiDecayRadius2D = 0; - double laDecayRadius2D = 0; - double v0DecayRadius2D = 0; std::vector cascadeDecayProducts; - std::vector v0DecayProducts; - std::vector xiDecayVertex, laDecayVertex, v0DecayVertex; - for (const auto& mcParticle : mcParticles) { - xiDecayRadius2D = 0; - laDecayRadius2D = 0; - v0DecayRadius2D = 0; - xiDecayVertex.clear(); - laDecayVertex.clear(); - v0DecayVertex.clear(); - - cascadeDecayProducts.clear(); - v0DecayProducts.clear(); - const bool isCascade = mcParticle.pdgCode() == kXiMinus; - if (cascadeDecaySettings.decayXi && isCascade) { - o2::track::TrackParCov xiTrackParCov; - o2::upgrade::convertMCParticleToO2Track(mcParticle, xiTrackParCov, pdgDB); - decayCascade(mcParticle, xiTrackParCov, cascadeDecayProducts, xiDecayVertex, laDecayVertex); - if (cascadeDecayProducts.size() != 3) { - LOG(fatal) << "Xi decay did not produce 3 daughters as expected!"; + std::vector xiDecayVertex, laDecayVertex; + std::array xiDaughterTrackParCovsPerfect; + std::array xiDaughterTrackParCovsTracked; + std::array isReco{}; + std::array nHitsCascadeProngs{}; // total + std::array nSiliconHitsCascadeProngs{}; // silicon type + std::array nTPCHitsCascadeProngs{}; // TPC type + + o2::track::TrackParCov xiTrackParCov; + o2::upgrade::convertMCParticleToO2Track(mcParticle, xiTrackParCov, pdgDB); + decayCascade(mcParticle, xiTrackParCov, cascadeDecayProducts, xiDecayVertex, laDecayVertex); + if (cascadeDecayProducts.size() != NCascProngs) { + LOG(fatal) << "Xi decay did not produce 3 daughters as expected!"; + } + double xiDecayRadius2D = std::hypot(xiDecayVertex[0], xiDecayVertex[1]); + double laDecayRadius2D = std::hypot(laDecayVertex[0], laDecayVertex[1]); + + if (cascadeDecaySettings.doXiQA) { + getHist(histPath + "hGenXi")->Fill(xiDecayRadius2D, mcParticle.pt()); + getHist(histPath + "hGenPiFromXi")->Fill(xiDecayRadius2D, cascadeDecayProducts[0].Pt()); + getHist(histPath + "hGenPiFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[1].Pt()); + getHist(histPath + "hGenPrFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[2].Pt()); + } + + if (cascadeDecaySettings.doXiQA) { + getHist(histPath + "hXiBuilding")->Fill(0.0f); + } + + o2::upgrade::convertTLorentzVectorToO2Track(PDG_t::kPiMinus, cascadeDecayProducts[0], xiDecayVertex, xiDaughterTrackParCovsPerfect[0], pdgDB); + xiDaughterTrackParCovsPerfect[0].setPID(pdgCodeToPID(PDG_t::kPiMinus)); + o2::upgrade::convertTLorentzVectorToO2Track(PDG_t::kPiMinus, cascadeDecayProducts[1], laDecayVertex, xiDaughterTrackParCovsPerfect[1], pdgDB); + xiDaughterTrackParCovsPerfect[1].setPID(pdgCodeToPID(PDG_t::kPiMinus)); + o2::upgrade::convertTLorentzVectorToO2Track(PDG_t::kProton, cascadeDecayProducts[2], laDecayVertex, xiDaughterTrackParCovsPerfect[2], pdgDB); + xiDaughterTrackParCovsPerfect[2].setPID(pdgCodeToPID(PDG_t::kProton)); + o2::track::TrackParCov perfectCascadeTrack; + o2::upgrade::convertMCParticleToO2Track(mcParticle, perfectCascadeTrack, pdgDB); + perfectCascadeTrack.setPID(pdgCodeToPID(PDG_t::kXiMinus)); // FIXME: not OK for omegas + + thisCascade.bachelorId = trackTableOffset + 1; + thisCascade.negativeId = trackTableOffset + 2; // Lambda daughters + thisCascade.positiveId = trackTableOffset + 3; // Lambda daughters + thisCascade.cascadeTrackId = trackTableOffset + 4; + + float trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksCascadeProngs.push_back(TrackAlice3{xiDaughterTrackParCovsPerfect[0], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 1, -1, -1, TrackType::kGenCascDaug}); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksCascadeProngs.push_back(TrackAlice3{xiDaughterTrackParCovsPerfect[1], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 2, -1, -1, TrackType::kGenCascDaug}); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksCascadeProngs.push_back(TrackAlice3{xiDaughterTrackParCovsPerfect[2], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 3, -1, -1, TrackType::kGenCascDaug}); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksCascadeProngs.push_back(TrackAlice3{perfectCascadeTrack, mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 3, -1, -1, TrackType::kGenCascDaug}); + + for (int i = 0; i < NCascProngs; i++) { + isReco[i] = false; + nHitsCascadeProngs[i] = 0; + nSiliconHitsCascadeProngs[i] = 0; + nTPCHitsCascadeProngs[i] = 0; + if (enableSecondarySmearing) { + nHitsCascadeProngs[i] = fastTracker[icfg]->fastTrack(xiDaughterTrackParCovsPerfect[i], xiDaughterTrackParCovsTracked[i], dNdEta); + nSiliconHitsCascadeProngs[i] = fastTracker[icfg]->getNSiliconPoints(); + nTPCHitsCascadeProngs[i] = fastTracker[icfg]->getNGasPoints(); + + if (nHitsCascadeProngs[i] < 0 && cascadeDecaySettings.doXiQA) { // QA + getHist(histPath + "hFastTrackerQA")->Fill(o2::math_utils::abs(nHitsCascadeProngs[i])); } - xiDecayRadius2D = std::hypot(xiDecayVertex[0], xiDecayVertex[1]); - laDecayRadius2D = std::hypot(laDecayVertex[0], laDecayVertex[1]); - } - const bool isV0 = std::find(v0PDGs.begin(), v0PDGs.end(), mcParticle.pdgCode()) != v0PDGs.end(); - if (v0DecaySettings.decayV0 && isV0) { - decayV0Particle(mcParticle, v0DecayProducts, v0DecayVertex, mcParticle.pdgCode()); - if (v0DecayProducts.size() != 2) { - LOG(fatal) << "V0 decay did not produce 2 daughters as expected!"; + getHist(histPath + "nSiliconHitsCascadeProngs")->Fill(nSiliconHitsCascadeProngs[i]); + getHist(histPath + "nTPCHitsCascadeProngs")->Fill(nTPCHitsCascadeProngs[i]); + if (nSiliconHitsCascadeProngs[i] >= fastTrackerSettings.minSiliconHits || + (nSiliconHitsCascadeProngs[i] >= fastTrackerSettings.minSiliconHitsIfTPCUsed && + nTPCHitsCascadeProngs[i] >= fastTrackerSettings.minTPCClusters)) { + isReco[i] = true; + } else { + continue; // extra sure } - v0DecayRadius2D = std::hypot(v0DecayVertex[0], v0DecayVertex[1]); - } + if (cascadeDecaySettings.doXiQA) { + getHist(histPath + "hXiBuilding")->Fill(static_cast(i + 1)); + } + isReco[i] = true; - if (!mcParticle.isPhysicalPrimary()) { - continue; + for (uint32_t ih = 0; ih < fastTracker[icfg]->getNHits() && cascadeDecaySettings.doXiQA; ih++) { + getHist(histPath + "hFastTrackerHits")->Fill(fastTracker[icfg]->getHitZ(ih), std::hypot(fastTracker[icfg]->getHitX(ih), fastTracker[icfg]->getHitY(ih))); + } + } else { + isReco[i] = true; + xiDaughterTrackParCovsTracked[i] = xiDaughterTrackParCovsPerfect[i]; + if (cascadeDecaySettings.doXiQA) { + getHist(histPath + "hXiBuilding")->Fill(static_cast(i + 1)); + } } - const bool longLivedToBeHandled = std::find(longLivedHandledPDGs.begin(), longLivedHandledPDGs.end(), std::abs(mcParticle.pdgCode())) != longLivedHandledPDGs.end(); - const bool nucleiToBeHandled = std::find(nucleiPDGs.begin(), nucleiPDGs.end(), std::abs(mcParticle.pdgCode())) != nucleiPDGs.end(); - const bool pdgsToBeHandled = longLivedToBeHandled || (enableNucleiSmearing && nucleiToBeHandled) || (cascadeDecaySettings.decayXi && isCascade) || (v0DecaySettings.decayV0 && isV0); - if (!pdgsToBeHandled) { + if (TMath::IsNaN(xiDaughterTrackParCovsTracked[i].getZ())) { + isReco[i] = false; continue; } + getHist(histPath + "hXiBuilding")->Fill(4.0f); + histos.fill(HIST("hNaNBookkeeping"), i + 1, 1.0f); - if (std::fabs(mcParticle.eta()) > maxEta) { - continue; - } - if (enablePrimarySmearing) { - getHist(TH1, histPath + "hPtGenerated")->Fill(mcParticle.pt()); - getHist(TH1, histPath + "hPhiGenerated")->Fill(mcParticle.phi()); - switch (std::abs(mcParticle.pdgCode())) { - case kElectron: - getHist(TH1, histPath + "hPtGeneratedEl")->Fill(mcParticle.pt()); - break; - case kPiPlus: - getHist(TH1, histPath + "hPtGeneratedPi")->Fill(mcParticle.pt()); - break; - case kKPlus: - getHist(TH1, histPath + "hPtGeneratedKa")->Fill(mcParticle.pt()); - break; - case kProton: - getHist(TH1, histPath + "hPtGeneratedPr")->Fill(mcParticle.pt()); - break; - } - } - if (cascadeDecaySettings.doXiQA && isCascade) { - getHist(TH2, histPath + "hGenXi")->Fill(xiDecayRadius2D, mcParticle.pt()); - getHist(TH2, histPath + "hGenPiFromXi")->Fill(xiDecayRadius2D, cascadeDecayProducts[0].Pt()); - getHist(TH2, histPath + "hGenPiFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[1].Pt()); - getHist(TH2, histPath + "hGenPrFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[2].Pt()); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + // TODO: add flag for whether it's a ghost track or not, currently assuming all are reconstructed tracks if they pass the fast tracker requirements + TrackType trackType = isReco[i] ? TrackType::kRecoCascDaug : TrackType::kGenCascDaug; + tracksCascadeProngs[i] = TrackAlice3{xiDaughterTrackParCovsTracked[i], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, i + 2, nSiliconHitsCascadeProngs[i], nTPCHitsCascadeProngs[i], trackType}; + } + + bool tryKinkReco = false; + if (!isReco[1] || !isReco[2]) { + tryKinkReco = true; // Lambda outside acceptance, set flag for kink reco to be used if mode 1 + } + + bool reconstructedCascade = false; + if (isReco[0] && isReco[1] && isReco[2]) { + reconstructedCascade = true; + } + + bool fillCascadeTable{false}; + + // +-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+ + // combine particles into actual Xi candidate + // cascade building starts here + if (cascadeDecaySettings.findXi && reconstructedCascade && cascadeDecaySettings.doKinkReco != KinkOnly) { + if (cascadeDecaySettings.doXiQA) { + getHist(histPath + "hXiBuilding")->Fill(3.0f); } - if (v0DecaySettings.doV0QA && isV0) { - for (size_t indexV0 = 0; indexV0 < v0PDGs.size(); indexV0++) { - if (mcParticle.pdgCode() != v0PDGs[indexV0]) { - continue; - } - for (int indexDetector = 0; indexDetector < mGeoContainer.getNumberOfConfigurations(); indexDetector++) { - std::string path = Form("V0Building_Configuration_%i/%s/", indexDetector, NameV0s[indexV0].data()); - fillHist(TH2, path + "hGen", v0DecayRadius2D, mcParticle.pt()); - fillHist(TH2, path + "hGenNegDaughterFromV0", v0DecayRadius2D, v0DecayProducts[0].Pt()); - fillHist(TH2, path + "hGenPosDaughterFromV0", v0DecayRadius2D, v0DecayProducts[1].Pt()); - } - } + + // use DCA fitters + int nCand = 0; + bool dcaFitterV0Status = true; + try { + nCand = fitter.process(xiDaughterTrackParCovsTracked[1], xiDaughterTrackParCovsTracked[2]); + } catch (...) { + LOG(error) << "Exception caught in DCA fitter for V0!"; + dcaFitterV0Status = false; } - if (mcParticle.pt() < minPt) { - continue; + if (nCand == 0) { + // LOG(info) << "No V0 candidate found in DCA fitter!"; + dcaFitterV0Status = false; } - o2::track::TrackParCov trackParCov; - o2::upgrade::convertMCParticleToO2Track(mcParticle, trackParCov, pdgDB); - - const bool isDecayDaughter = (mcParticle.getProcess() == TMCProcess::kPDecay); + fitter.propagateTracksToVertex(); + if (!fitter.isPropagateTracksToVertexDone()) { + dcaFitterV0Status = false; + } - multiplicityCounter++; - const float timeResolutionNs = 100.f; // ns - const float nsToMus = 1e-3f; - const float timeResolutionUs = timeResolutionNs * nsToMus; // us - const float time = (eventCollisionTimeNS + gRandom->Gaus(0., timeResolutionNs)) * nsToMus; - static constexpr int kCascProngs = 3; - std::array xiDaughterTrackParCovsPerfect; - std::array xiDaughterTrackParCovsTracked; - std::array isReco; - std::array nHitsCascadeProngs; // total - std::array nSiliconHitsCascadeProngs; // silicon type - std::array nTPCHitsCascadeProngs; // TPC type - - bool tryKinkReco = false; - if (cascadeDecaySettings.decayXi && isCascade) { - bool reconstructedCascade = false; + // V0 found successfully + if (dcaFitterV0Status) { if (cascadeDecaySettings.doXiQA) { - getHist(TH1, histPath + "hXiBuilding")->Fill(0.0f); + getHist(histPath + "hXiBuilding")->Fill(4.0f); } - o2::upgrade::convertTLorentzVectorToO2Track(PDG_t::kPiMinus, cascadeDecayProducts[0], xiDecayVertex, xiDaughterTrackParCovsPerfect[0], pdgDB); - xiDaughterTrackParCovsPerfect[0].setPID(pdgCodeToPID(PDG_t::kPiMinus)); - o2::upgrade::convertTLorentzVectorToO2Track(PDG_t::kPiMinus, cascadeDecayProducts[1], laDecayVertex, xiDaughterTrackParCovsPerfect[1], pdgDB); - xiDaughterTrackParCovsPerfect[1].setPID(pdgCodeToPID(PDG_t::kPiMinus)); - o2::upgrade::convertTLorentzVectorToO2Track(PDG_t::kProton, cascadeDecayProducts[2], laDecayVertex, xiDaughterTrackParCovsPerfect[2], pdgDB); - xiDaughterTrackParCovsPerfect[2].setPID(pdgCodeToPID(PDG_t::kProton)); - - for (int i = 0; i < kCascProngs; i++) { - isReco[i] = false; - nHitsCascadeProngs[i] = 0; - nSiliconHitsCascadeProngs[i] = 0; - nTPCHitsCascadeProngs[i] = 0; - if (enableSecondarySmearing) { - nHitsCascadeProngs[i] = fastTracker[icfg]->FastTrack(xiDaughterTrackParCovsPerfect[i], xiDaughterTrackParCovsTracked[i], dNdEta); - nSiliconHitsCascadeProngs[i] = fastTracker[icfg]->GetNSiliconPoints(); - nTPCHitsCascadeProngs[i] = fastTracker[icfg]->GetNGasPoints(); - - if (nHitsCascadeProngs[i] < 0 && cascadeDecaySettings.doXiQA) { // QA - getHist(TH1, histPath + "hFastTrackerQA")->Fill(o2::math_utils::abs(nHitsCascadeProngs[i])); - } - - if (nSiliconHitsCascadeProngs[i] >= fastTrackerSettings.minSiliconHits || (nSiliconHitsCascadeProngs[i] >= fastTrackerSettings.minSiliconHitsIfTPCUsed && nTPCHitsCascadeProngs[i] >= fastTrackerSettings.minTPCClusters)) { - isReco[i] = true; - } else { - continue; // extra sure - } - for (uint32_t ih = 0; ih < fastTracker[icfg]->GetNHits() && cascadeDecaySettings.doXiQA; ih++) { - getHist(TH2, histPath + "hFastTrackerHits")->Fill(fastTracker[icfg]->GetHitZ(ih), std::hypot(fastTracker[icfg]->GetHitX(ih), fastTracker[icfg]->GetHitY(ih))); - } - } else { - isReco[i] = true; - xiDaughterTrackParCovsTracked[i] = xiDaughterTrackParCovsPerfect[i]; - } + std::array pos{}; + std::array posCascade{}; + std::array posP{}; + std::array negP{}; + std::array bachP{}; + + o2::track::TrackParCov pTrackAtPCA = fitter.getTrack(1); // proton (positive) + o2::track::TrackParCov nTrackAtPCA = fitter.getTrack(0); // pion (negative) + pTrackAtPCA.getPxPyPzGlo(posP); + nTrackAtPCA.getPxPyPzGlo(negP); + + // get decay vertex coordinates + const auto& pcaCandidate = fitter.getPCACandidate(); + for (int i = 0; i < NDim3D; i++) { + pos[i] = pcaCandidate[i]; + } - if (TMath::IsNaN(xiDaughterTrackParCovsTracked[i].getZ())) { - continue; - } else { - histos.fill(HIST("hNaNBookkeeping"), i + 1, 1.0f); - } - if (isReco[i]) { - tracksAlice3.push_back(TrackAlice3{xiDaughterTrackParCovsTracked[i], mcParticle.globalIndex(), time, timeResolutionUs, true, true, i + 2, nSiliconHitsCascadeProngs[i], nTPCHitsCascadeProngs[i]}); - } else { - ghostTracksAlice3.push_back(TrackAlice3{xiDaughterTrackParCovsTracked[i], mcParticle.globalIndex(), time, timeResolutionUs, true, true, i + 2}); - } + // calculate basic V0 properties here + // DCA to PV taken care of in daughter tracks already, not necessary + thisCascade.dcaV0dau = std::sqrt(fitter.getChi2AtPCACandidate()); + thisCascade.v0radius = std::hypot(pos[0], pos[1]); + thisCascade.mLambda = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, + std::array{negP[0], negP[1], negP[2]}}, + std::array{o2::constants::physics::MassProton, + o2::constants::physics::MassPionCharged}); + + // go for cascade: create V0 (pseudo)track from reconstructed V0 + std::array covV = {0.}; + static constexpr std::array MomInd = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (size_t i = 0; i < MomInd.size(); i++) { + covV[MomInd[i]] = 1e-6; + covV[i] = 1e-6; } - if (!isReco[1] || !isReco[2]) { - tryKinkReco = true; // Lambda outside acceptance, set flag for kink reco to be used if mode 1 + o2::track::TrackParCov v0Track = o2::track::TrackParCov( + {pos[0], pos[1], pos[2]}, + {posP[0] + negP[0], posP[1] + negP[1], posP[2] + negP[2]}, + covV, 0, true); + v0Track.setAbsCharge(0); + v0Track.setPID(pdgCodeToPID(PDG_t::kLambda0)); + + // dca fitter step + nCand = 0; + bool dcaFitterCascadeStatus = true; + try { + nCand = fitter.process(v0Track, xiDaughterTrackParCovsTracked[0]); + } catch (...) { + // LOG(error) << "Exception caught in DCA fitter cascade!"; + dcaFitterCascadeStatus = false; + } + if (nCand == 0) { + dcaFitterCascadeStatus = false; } - if (isReco[0] && isReco[1] && isReco[2]) { - reconstructedCascade = true; + fitter.propagateTracksToVertex(); + if (!fitter.isPropagateTracksToVertexDone()) { + dcaFitterCascadeStatus = false; } - // +-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+ - // combine particles into actual Xi candidate - // cascade building starts here - if (cascadeDecaySettings.findXi && isReco[0] && isReco[1] && isReco[2] && cascadeDecaySettings.doKinkReco != 2) { + const u_int8_t fitterStatusCode = fitter.getFitStatus(); + histos.fill(HIST("hFitterStatusCode"), fitterStatusCode); + // Cascade found successfully + if (dcaFitterCascadeStatus) { if (cascadeDecaySettings.doXiQA) { - getHist(TH1, histPath + "hXiBuilding")->Fill(3.0f); - } - // assign indices of the particles we've used - // they should be the last ones to be filled, in order: - // n-1: proton from lambda - // n-2: pion from lambda - // n-3: pion from xi - thisCascade.positiveId = lastTrackIndex + tracksAlice3.size() - 1; - thisCascade.negativeId = lastTrackIndex + tracksAlice3.size() - 2; - thisCascade.bachelorId = lastTrackIndex + tracksAlice3.size() - 3; - - // use DCA fitters - int nCand = 0; - bool dcaFitterOK_V0 = true; - try { - nCand = fitter.process(xiDaughterTrackParCovsTracked[1], xiDaughterTrackParCovsTracked[2]); - } catch (...) { - // LOG(error) << "Exception caught in DCA fitter process call!"; - dcaFitterOK_V0 = false; - } - if (nCand == 0) { - dcaFitterOK_V0 = false; + getHist(histPath + "hXiBuilding")->Fill(6.0f); } - fitter.propagateTracksToVertex(); - if (!fitter.isPropagateTracksToVertexDone()) { - dcaFitterOK_V0 = false; + o2::track::TrackParCov bachelorTrackAtPCA = fitter.getTrack(1); + const auto& vtxCascade = fitter.getPCACandidate(); + for (int i = 0; i < NDim3D; i++) { + posCascade[i] = vtxCascade[i]; } - // V0 found successfully - if (dcaFitterOK_V0) { - if (cascadeDecaySettings.doXiQA) { - getHist(TH1, histPath + "hXiBuilding")->Fill(4.0f); - } + // basic properties of the cascade + thisCascade.dcacascdau = std::sqrt(fitter.getChi2AtPCACandidate()); + thisCascade.cascradius = std::hypot(posCascade[0], posCascade[1]); + bachelorTrackAtPCA.getPxPyPzGlo(bachP); + + thisCascade.mXi = RecoDecay::m(std::array{std::array{bachP[0], bachP[1], bachP[2]}, + std::array{posP[0] + negP[0], posP[1] + negP[1], posP[2] + negP[2]}}, + std::array{o2::constants::physics::MassPionCharged, + o2::constants::physics::MassLambda}); + + // initialize cascade track + o2::track::TrackParCov cascadeTrack = fitter.createParentTrackParCov(); + cascadeTrack.setAbsCharge(-1); // may require more adjustments + cascadeTrack.setPID(pdgCodeToPID(PDG_t::kXiMinus)); // FIXME: not OK for omegas + + thisCascade.cascradiusMC = xiDecayRadius2D; + thisCascade.pt = cascadeTrack.getPt(); + thisCascade.eta = cascadeTrack.getEta(); + thisCascade.findableClusters = 0; + thisCascade.foundClusters = 0; + + if (cascadeDecaySettings.trackXi) { + // optionally, add the points in the layers before the decay of the Xi + // will back-track the perfect MC cascade to relevant layers, find hit, smear and add to smeared cascade + for (int i = fastTracker[icfg]->getLayers().size() - 1; i >= 0; --i) { + o2::fastsim::DetLayer layer = fastTracker[icfg]->getLayer(i); + if (layer.isInert()) { + continue; // Not an active tracking layer + } - std::array pos; - std::array posCascade; - std::array posP; - std::array negP; - std::array bachP; - - o2::track::TrackParCov pTrackAtPCA = fitter.getTrack(1); // proton (positive) - o2::track::TrackParCov nTrackAtPCA = fitter.getTrack(0); // pion (negative) - pTrackAtPCA.getPxPyPzGlo(posP); - nTrackAtPCA.getPxPyPzGlo(negP); - - // get decay vertex coordinates - const auto& vtx = fitter.getPCACandidate(); - for (int i = 0; i < 3; i++) { - pos[i] = vtx[i]; - } + if (thisCascade.cascradiusMC < layer.getRadius()) { + continue; // Cascade did not reach this layer + } - // calculate basic V0 properties here - // DCA to PV taken care of in daughter tracks already, not necessary - thisCascade.dcaV0dau = TMath::Sqrt(fitter.getChi2AtPCACandidate()); - thisCascade.v0radius = std::hypot(pos[0], pos[1]); - thisCascade.mLambda = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, - std::array{negP[0], negP[1], negP[2]}}, - std::array{o2::constants::physics::MassProton, - o2::constants::physics::MassPionCharged}); - - // go for cascade: create V0 (pseudo)track from reconstructed V0 - std::array covV = {0.}; - constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component - for (int i = 0; i < 6; i++) { - covV[MomInd[i]] = 1e-6; - covV[i] = 1e-6; - } + // cascade decayed after the corresponding radius + thisCascade.findableClusters++; // add to findable - o2::track::TrackParCov v0Track = o2::track::TrackParCov( - {pos[0], pos[1], pos[2]}, - {posP[0] + negP[0], posP[1] + negP[1], posP[2] + negP[2]}, - covV, 0, true); - v0Track.setAbsCharge(0); - v0Track.setPID(pdgCodeToPID(PDG_t::kLambda0)); - - // dca fitter step - nCand = 0; - bool dcaFitterOK_Cascade = true; - try { - nCand = fitter.process(v0Track, xiDaughterTrackParCovsTracked[0]); - } catch (...) { - // LOG(error) << "Exception caught in DCA fitter process call!"; - dcaFitterOK_Cascade = false; - } - if (nCand == 0) { - dcaFitterOK_Cascade = false; - } + // find perfect intercept XYZ + float targetX = 1e+3; + static constexpr float InterceptFailed = 999.f; + xiTrackParCov.getXatLabR(layer.getRadius(), targetX, mMagneticField); + if (targetX > InterceptFailed) { + continue; // failed to find intercept + } - fitter.propagateTracksToVertex(); - if (!fitter.isPropagateTracksToVertexDone()) { - dcaFitterOK_Cascade = false; - } + if (!xiTrackParCov.propagateTo(targetX, mMagneticField)) { + continue; // failed to propagate + } - const u_int8_t fitterStatusCode = fitter.getFitStatus(); - histos.fill(HIST("hFitterStatusCode"), fitterStatusCode); - // Cascade found successfully - if (dcaFitterOK_Cascade) { - if (cascadeDecaySettings.doXiQA) { - getHist(TH1, histPath + "hXiBuilding")->Fill(5.0f); + // get potential cluster position + std::array posClusterCandidate{}; + xiTrackParCov.getXYZGlo(posClusterCandidate); + float r{std::hypot(posClusterCandidate[0], posClusterCandidate[1])}; + float phi{std::atan2(-posClusterCandidate[1], -posClusterCandidate[0]) + o2::constants::math::PI}; + + static constexpr float ResolutionThreshold = 1e-8; + if (layer.getResolutionRPhi() > ResolutionThreshold && layer.getResolutionZ() > ResolutionThreshold) { // catch zero (though should not really happen...) + phi = gRandom->Gaus(phi, std::asin(layer.getResolutionRPhi() / r)); + posClusterCandidate[0] = r * std::cos(phi); + posClusterCandidate[1] = r * std::sin(phi); + posClusterCandidate[2] = gRandom->Gaus(posClusterCandidate[2], layer.getResolutionZ()); } - o2::track::TrackParCov bachelorTrackAtPCA = fitter.getTrack(1); - const auto& vtxCascade = fitter.getPCACandidate(); - for (int i = 0; i < 3; i++) { - posCascade[i] = vtxCascade[i]; + if (std::isnan(phi)) { + continue; // Catch when getXatLabR misses layer[i] } - // basic properties of the cascade - thisCascade.dcacascdau = TMath::Sqrt(fitter.getChi2AtPCACandidate()); - thisCascade.cascradius = std::hypot(posCascade[0], posCascade[1]); - bachelorTrackAtPCA.getPxPyPzGlo(bachP); - - thisCascade.mXi = RecoDecay::m(std::array{std::array{bachP[0], bachP[1], bachP[2]}, - std::array{posP[0] + negP[0], posP[1] + negP[1], posP[2] + negP[2]}}, - std::array{o2::constants::physics::MassPionCharged, - o2::constants::physics::MassLambda}); - - // initialize cascade track - o2::track::TrackParCov cascadeTrack = fitter.createParentTrackParCov(); - cascadeTrack.setAbsCharge(-1); // may require more adjustments - cascadeTrack.setPID(pdgCodeToPID(PDG_t::kXiMinus)); // FIXME: not OK for omegas - - thisCascade.cascradiusMC = xiDecayRadius2D; - thisCascade.pt = cascadeTrack.getPt(); - thisCascade.eta = cascadeTrack.getEta(); - thisCascade.findableClusters = 0; - thisCascade.foundClusters = 0; - - if (cascadeDecaySettings.trackXi) { - // optionally, add the points in the layers before the decay of the Xi - // will back-track the perfect MC cascade to relevant layers, find hit, smear and add to smeared cascade - for (int i = fastTracker[icfg]->GetLayers().size() - 1; i >= 0; --i) { - o2::fastsim::DetLayer layer = fastTracker[icfg]->GetLayer(i); - if (layer.isInert()) { - continue; // Not an active tracking layer - } - - if (thisCascade.cascradiusMC < layer.getRadius()) { - continue; // Cascade did not reach this layer - } - - // cascade decayed after the corresponding radius - thisCascade.findableClusters++; // add to findable - - // find perfect intercept XYZ - float targetX = 1e+3; - trackParCov.getXatLabR(layer.getRadius(), targetX, mMagneticField); - if (targetX > 999) { - continue; // failed to find intercept - } - - if (!trackParCov.propagateTo(targetX, mMagneticField)) { - continue; // failed to propagate - } - - // get potential cluster position - std::array posClusterCandidate; - trackParCov.getXYZGlo(posClusterCandidate); - float r{std::hypot(posClusterCandidate[0], posClusterCandidate[1])}; - float phi{std::atan2(-posClusterCandidate[1], -posClusterCandidate[0]) + o2::constants::math::PI}; - - if (layer.getResolutionRPhi() > 1e-8 && layer.getResolutionZ() > 1e-8) { // catch zero (though should not really happen...) - phi = gRandom->Gaus(phi, std::asin(layer.getResolutionRPhi() / r)); - posClusterCandidate[0] = r * std::cos(phi); - posClusterCandidate[1] = r * std::sin(phi); - posClusterCandidate[2] = gRandom->Gaus(posClusterCandidate[2], layer.getResolutionZ()); - } - - if (std::isnan(phi)) { - continue; // Catch when getXatLabR misses layer[i] - } - - // towards adding cluster: move to track alpha - double alpha = cascadeTrack.getAlpha(); - double xyz1[3]{ - TMath::Cos(alpha) * posClusterCandidate[0] + TMath::Sin(alpha) * posClusterCandidate[1], - -TMath::Sin(alpha) * posClusterCandidate[0] + TMath::Cos(alpha) * posClusterCandidate[1], - posClusterCandidate[2]}; - - if (!(cascadeTrack.propagateTo(xyz1[0], mMagneticField))) { - continue; - } - - const o2::track::TrackParametrization::dim2_t hitpoint = {static_cast(xyz1[1]), static_cast(xyz1[2])}; - const o2::track::TrackParametrization::dim3_t hitpointcov = {layer.getResolutionRPhi() * layer.getResolutionRPhi(), 0.f, layer.getResolutionZ() * layer.getResolutionZ()}; - if (layer.isInDeadPhiRegion(phi)) { - continue; // No hit for strangeness tracking update - } - - cascadeTrack.update(hitpoint, hitpointcov); - thisCascade.foundClusters++; // add to findable - } - - if (thisCascade.foundClusters < cascadeDecaySettings.minStraTrackHits) { - continue; // We didn't find enough hits for strangeness tracking - } + // towards adding cluster: move to track alpha + double alpha = cascadeTrack.getAlpha(); + std::array xyz1 = { + std::cos(alpha) * posClusterCandidate[0] + std::sin(alpha) * posClusterCandidate[1], + -std::sin(alpha) * posClusterCandidate[0] + std::cos(alpha) * posClusterCandidate[1], + posClusterCandidate[2]}; + + if (!(cascadeTrack.propagateTo(xyz1[0], mMagneticField))) { + continue; } - // add cascade track - thisCascade.cascadeTrackId = lastTrackIndex + tracksAlice3.size(); // this is the next index to be filled -> should be it - tracksAlice3.push_back(TrackAlice3{cascadeTrack, mcParticle.globalIndex(), time, timeResolutionUs, false, false, 1, thisCascade.foundClusters}); + const o2::track::TrackParametrization::dim2_t hitpoint = {static_cast(xyz1[1]), static_cast(xyz1[2])}; + const o2::track::TrackParametrization::dim3_t hitpointcov = {layer.getResolutionRPhi() * layer.getResolutionRPhi(), 0.f, layer.getResolutionZ() * layer.getResolutionZ()}; + if (layer.isInDeadPhiRegion(phi)) { + continue; // No hit for strangeness tracking update + } - // add this cascade to vector (will fill cursor later with collision ID) - cascadesAlice3.push_back(thisCascade); - } - } - } // end cascade building - - if (isReco[0] && ((cascadeDecaySettings.doKinkReco == 1 && tryKinkReco) || cascadeDecaySettings.doKinkReco == 2)) { // mode 1 or 2 - o2::track::TrackParCov prefectCascadeTrack, trackedCascade; - const o2::track::TrackParCov& trackedBach = xiDaughterTrackParCovsTracked[0]; - o2::upgrade::convertMCParticleToO2Track(mcParticle, prefectCascadeTrack, pdgDB); - - // back track is already smeared - prefectCascadeTrack.setPID(pdgCodeToPID(PDG_t::kXiMinus)); // FIXME: not OK for omegas - const int nCascHits = fastTracker[icfg]->FastTrack(prefectCascadeTrack, trackedCascade, dNdEta, xiDecayRadius2D); - reconstructedCascade = (fastTrackerSettings.minSiliconHitsForKinkReco < nCascHits) ? true : false; - if (reconstructedCascade) { - std::array pCasc; - std::array pBach; - std::array pV0; - trackedCascade.getPxPyPzGlo(pCasc); - trackedBach.getPxPyPzGlo(pBach); - for (size_t i = 0; i < pCasc.size(); ++i) { - pV0[i] = pCasc[i] - pBach[i]; + cascadeTrack.update(hitpoint, hitpointcov); + thisCascade.foundClusters++; // add to findable } - if (isReco[1] && !isReco[2]) { - thisCascade.negativeId = lastTrackIndex + tracksAlice3.size() - 1; - thisCascade.positiveId = -1; - } else if (!isReco[1] && isReco[2]) { - thisCascade.negativeId = -1; - thisCascade.positiveId = lastTrackIndex + tracksAlice3.size() - 1; - } else if (isReco[1] && isReco[2]) { - thisCascade.positiveId = lastTrackIndex + tracksAlice3.size() - 1; - thisCascade.negativeId = lastTrackIndex + tracksAlice3.size() - 2; - } else { - thisCascade.positiveId = -1; - thisCascade.negativeId = -1; + if (thisCascade.foundClusters < cascadeDecaySettings.minStraTrackHits) { + return; // We didn't find enough hits for strangeness tracking } + } + tracksCascadeProngs[NCascProngs] = TrackAlice3{cascadeTrack, mcParticle.globalIndex(), trackTime, TimeResolutionUs, false, false, false, 1, thisCascade.foundClusters, TrackType::kGenCascDaug}; + fillCascadeTable = true; + } + } + } // end cascade building + + if (isReco[0] && ((cascadeDecaySettings.doKinkReco == ComplKink && tryKinkReco) || cascadeDecaySettings.doKinkReco == KinkOnly)) { // mode 1 or 2 + o2::track::TrackParCov trackedCascade; + const o2::track::TrackParCov& trackedBach = xiDaughterTrackParCovsTracked[0]; + const int nCascHits = fastTracker[icfg]->fastTrack(perfectCascadeTrack, trackedCascade, dNdEta, xiDecayRadius2D); + reconstructedCascade = fastTrackerSettings.minSiliconHitsForKinkReco < nCascHits; + if (reconstructedCascade) { + std::array pCasc{}; + std::array pBach{}; + std::array pV0{}; + trackedCascade.getPxPyPzGlo(pCasc); + trackedBach.getPxPyPzGlo(pBach); + for (size_t i = 0; i < pCasc.size(); ++i) { + pV0[i] = pCasc[i] - pBach[i]; + } - int nCand = 0; - bool kinkFitterOK = true; - try { - nCand = fitter.process(trackedCascade, trackedBach); - } catch (...) { - kinkFitterOK = false; - } + // Reset indices + if (!isReco[1]) { + thisCascade.negativeId = -1; + } + if (!isReco[2]) { + thisCascade.positiveId = -1; + } - if (nCand == 0) { - kinkFitterOK = false; - } + int nCand = 0; + bool kinkFitterOK = true; + try { + nCand = fitter.process(trackedCascade, trackedBach); + } catch (...) { + kinkFitterOK = false; + } - fitter.propagateTracksToVertex(); - if (!fitter.isPropagateTracksToVertexDone()) { - kinkFitterOK = false; - } + if (nCand == 0) { + kinkFitterOK = false; + } - const u_int8_t fitterStatusCode = fitter.getFitStatus(); - histos.fill(HIST("hFitterStatusCode"), fitterStatusCode); - if (kinkFitterOK) { - if (cascadeDecaySettings.doXiQA) { - getHist(TH1, histPath + "hXiBuilding")->Fill(6.0f); - } + fitter.propagateTracksToVertex(); + if (!fitter.isPropagateTracksToVertexDone()) { + kinkFitterOK = false; + } - o2::track::TrackParCov newCascadeTrack = fitter.getTrack(0); // (cascade) - std::array kinkVtx = {-999, -999, -999}; - kinkVtx = fitter.getPCACandidatePos(); - thisCascade.bachelorId = lastTrackIndex + tracksAlice3.size() - isReco.size(); - thisCascade.cascadeTrackId = lastTrackIndex + tracksAlice3.size(); // this should be ok - thisCascade.dcaV0dau = -1.f; // unknown - thisCascade.v0radius = -1.f; // unknown - thisCascade.dcacascdau = std::sqrt(fitter.getChi2AtPCACandidate()); - thisCascade.cascradius = std::hypot(kinkVtx[0], kinkVtx[1]); - thisCascade.cascradiusMC = xiDecayRadius2D; - thisCascade.mLambda = o2::constants::physics::MassLambda; - thisCascade.findableClusters = nCascHits; - thisCascade.foundClusters = nCascHits; - thisCascade.pt = newCascadeTrack.getPt(); - thisCascade.eta = newCascadeTrack.getEta(); - thisCascade.mXi = RecoDecay::m(std::array{std::array{pBach[0], pBach[1], pBach[2]}, std::array{pV0[0], pV0[1], pV0[2]}}, - std::array{o2::constants::physics::MassPionCharged, o2::constants::physics::MassLambda}); - - newCascadeTrack.setPID(pdgCodeToPID(PDG_t::kXiMinus)); // FIXME: not OK for omegas - tracksAlice3.push_back(TrackAlice3{newCascadeTrack, mcParticle.globalIndex(), time, timeResolutionUs, false, false, 1, thisCascade.foundClusters}); - - // add this cascade to vector (will fill cursor later with collision ID) - cascadesAlice3.push_back(thisCascade); - } // end fitter OK - } // end cascade found - } // end cascade kink building - - // +-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+ - if (cascadeDecaySettings.doXiQA) { - if (reconstructedCascade) { - getHist(TH2, histPath + "hRecoXi")->Fill(xiDecayRadius2D, mcParticle.pt()); - getHist(TH1, histPath + "hMassLambda")->Fill(thisCascade.mLambda); - getHist(TH1, histPath + "hMassXi")->Fill(thisCascade.mXi); - getHist(TH2, histPath + "h2dMassXi")->Fill(thisCascade.mXi, thisCascade.pt); - getHist(TH2, histPath + "h2dDeltaPtVsPt")->Fill(thisCascade.pt, (mcParticle.pt() - thisCascade.pt) / thisCascade.pt); - getHist(TH2, histPath + "h2dDeltaEtaVsPt")->Fill(thisCascade.pt, mcParticle.eta() - thisCascade.eta); - getHist(TH2, histPath + "hFoundVsFindable")->Fill(thisCascade.findableClusters, thisCascade.foundClusters); - } - if (isReco[0]) { - getHist(TH2, histPath + "hRecoPiFromXi")->Fill(xiDecayRadius2D, cascadeDecayProducts[0].Pt()); - } - if (isReco[1]) { - getHist(TH2, histPath + "hRecoPiFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[1].Pt()); - } - if (isReco[2]) { - getHist(TH2, histPath + "hRecoPrFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[2].Pt()); + const u_int8_t fitterStatusCode = fitter.getFitStatus(); + histos.fill(HIST("hFitterStatusCode"), fitterStatusCode); + if (kinkFitterOK) { + if (cascadeDecaySettings.doXiQA) { + getHist(histPath + "hXiBuilding")->Fill(7.0f); } - } - continue; // Cascade handling done, should not be considered anymore + o2::track::TrackParCov newCascadeTrack = fitter.getTrack(0); // (cascade) + std::array kinkVtx = fitter.getPCACandidatePos(); + thisCascade.dcaV0dau = -1.f; // unknown + thisCascade.v0radius = -1.f; // unknown + thisCascade.dcacascdau = std::sqrt(fitter.getChi2AtPCACandidate()); + thisCascade.cascradius = std::hypot(kinkVtx[0], kinkVtx[1]); + thisCascade.cascradiusMC = xiDecayRadius2D; + thisCascade.mLambda = o2::constants::physics::MassLambda; + thisCascade.findableClusters = nCascHits; + thisCascade.foundClusters = nCascHits; + thisCascade.pt = newCascadeTrack.getPt(); + thisCascade.eta = newCascadeTrack.getEta(); + thisCascade.mXi = RecoDecay::m(std::array{std::array{pBach[0], pBach[1], pBach[2]}, std::array{pV0[0], pV0[1], pV0[2]}}, + std::array{o2::constants::physics::MassPionCharged, o2::constants::physics::MassLambda}); + newCascadeTrack.setPID(pdgCodeToPID(PDG_t::kXiMinus)); // FIXME: not OK for omegas + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksCascadeProngs[NCascProngs + 1] = TrackAlice3{newCascadeTrack, mcParticle.globalIndex(), trackTime, TimeResolutionUs, false, false, false, 1, thisCascade.foundClusters, TrackType::kRecoCascDaug}; + + fillCascadeTable = true; + } // end fitter OK + } // end cascade found + } // end cascade kink building + + // +-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+ + double dcaXY{999.f}, dcaZ{999.f}; + o2::track::TrackParCov trackParametrization(xiTrackParCov); + if (cascadeDecaySettings.doXiQA) { + if (reconstructedCascade) { + getHist(histPath + "hRecoXi")->Fill(xiDecayRadius2D, mcParticle.pt()); + getHist(histPath + "hMassLambda")->Fill(thisCascade.mLambda); + getHist(histPath + "hMassXi")->Fill(thisCascade.mXi); + getHist(histPath + "h2dMassXi")->Fill(thisCascade.mXi, thisCascade.pt); + getHist(histPath + "h2dDeltaPtVsPt")->Fill(thisCascade.pt, (mcParticle.pt() - thisCascade.pt) / thisCascade.pt); + getHist(histPath + "h2dDeltaEtaVsPt")->Fill(thisCascade.pt, mcParticle.eta() - thisCascade.eta); + getHist(histPath + "hFoundVsFindable")->Fill(thisCascade.findableClusters, thisCascade.foundClusters); + getHist(histPath + "h2dDCAxyCascade")->Fill(trackParametrization.getPt(), dcaXY * 1e+4); // in microns, please + getHist(histPath + "h2dDCAzCascade")->Fill(trackParametrization.getPt(), dcaZ * 1e+4); // in microns, please + } + if (isReco[0]) { + getHist(histPath + "hRecoPiFromXi")->Fill(xiDecayRadius2D, cascadeDecayProducts[0].Pt()); + o2::track::TrackParCov trackParametrizationBachelor(xiTrackParCov); + if (populateTracksDCA && trackParametrizationBachelor.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { + dcaXY = dcaInfo.getY(); + dcaZ = dcaInfo.getZ(); + getHist(histPath + "h2dDCAxyCascadeBachelor")->Fill(trackParametrizationBachelor.getPt(), dcaXY * 1e+4); // in microns, please + getHist(histPath + "h2dDCAzCascadeBachelor")->Fill(trackParametrizationBachelor.getPt(), dcaZ * 1e+4); // in microns, please + } + } + if (isReco[1]) { + getHist(histPath + "hRecoPiFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[1].Pt()); + o2::track::TrackParCov trackParametrizationNegative(xiTrackParCov); + if (populateTracksDCA && trackParametrizationNegative.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { + dcaXY = dcaInfo.getY(); + dcaZ = dcaInfo.getZ(); + getHist(histPath + "h2dDCAxyCascadeNegative")->Fill(trackParametrizationNegative.getPt(), dcaXY * 1e+4); // in microns, please + getHist(histPath + "h2dDCAzCascadeNegative")->Fill(trackParametrizationNegative.getPt(), dcaZ * 1e+4); // in microns, please + } + } + if (isReco[2]) { + getHist(histPath + "hRecoPrFromLa")->Fill(laDecayRadius2D, cascadeDecayProducts[2].Pt()); + o2::track::TrackParCov trackParametrizationPositive(xiTrackParCov); + if (populateTracksDCA && trackParametrizationPositive.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { + dcaXY = dcaInfo.getY(); + dcaZ = dcaInfo.getZ(); + getHist(histPath + "h2dDCAxyCascadePositive")->Fill(trackParametrizationPositive.getPt(), dcaXY * 1e+4); // in microns, please + getHist(histPath + "h2dDCAzCascadePositive")->Fill(trackParametrizationPositive.getPt(), dcaZ * 1e+4); // in microns, please + } } + } - // V0 handling - std::vector v0DaughterTrackParCovsPerfect(2); - std::vector v0DaughterTrackParCovsTracked(2); - std::vector isV0Reco(kv0Prongs); - std::vector nV0Hits(kv0Prongs); // total - std::vector nV0SiliconHits(kv0Prongs); // silicon type - std::vector nV0TPCHits(kv0Prongs); // TPC type - if (v0DecaySettings.decayV0 && isV0) { - if (v0DecaySettings.doV0QA) { - fillHist(TH1, Form("V0Building_Configuration_%i/hV0Building", icfg), 0.0f); + if (!fillCascadeTable) { + tracksCascadeProngs.clear(); // clear the tracks added for this cascade since we won't be filling the table + return; + } + + // Make sure the xi variables are stored in dcaXY and dcaZ + trackParametrization.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo); + + // populate Cascades + tableUpgradeCascadeMcLabels(mcParticle.globalIndex()); + tableUpgradeCascades(tableCollisions.lastIndex(), + thisCascade.cascadeTrackId, + thisCascade.positiveId, + thisCascade.negativeId, + thisCascade.bachelorId, + thisCascade.dcaV0dau, + thisCascade.dcacascdau, + thisCascade.v0radius, + thisCascade.cascradius, + thisCascade.cascradiusMC, + thisCascade.mLambda, + thisCascade.mXi, + thisCascade.findableClusters, + thisCascade.foundClusters, + dcaXY, dcaZ); + } + + /// Function to study V0s and fill the relevant histograms + /// \param trackTableOffset offset to be added to the indices of the daughter tracks when filling the V0 table + /// \param mcParticle the MC particle corresponding to the V0 to be studied + /// \param tracksV0Daugs vector of tracks to which the V0 daughters will be added + /// \param dNdEta multiplicity of the event, used for fast tracker smearing + /// \param eventCollisionTimeNS collision time of the event in nanoseconds, used for track time smearing + template + void studyV0(const int trackTableOffset, + const McParticleType& mcParticle, + std::vector& tracksV0Daugs, + float dNdEta, + float eventCollisionTimeNS) + { + o2::track::TrackParCov trackParCov; + o2::upgrade::convertMCParticleToO2Track(mcParticle, trackParCov, pdgDB); + std::vector v0DecayProducts; + std::vector v0DecayVertex; + decayV0Particle(mcParticle, v0DecayProducts, v0DecayVertex, mcParticle.pdgCode()); + + if (v0DecayProducts.size() != NV0Prongs) { + LOG(fatal) << "V0 decay did not produce 2 daughters as expected!"; + } + double v0DecayRadius2D = std::hypot(v0DecayVertex[0], v0DecayVertex[1]); + + if (v0DecaySettings.doV0QA) { + for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { + if (mcParticle.pdgCode() != V0PDGs[indexV0]) { + continue; } - switch (mcParticle.pdgCode()) { - case kK0Short: - o2::upgrade::convertTLorentzVectorToO2Track(kPiMinus, v0DecayProducts[0], v0DecayVertex, v0DaughterTrackParCovsPerfect[0], pdgDB); - o2::upgrade::convertTLorentzVectorToO2Track(kPiPlus, v0DecayProducts[1], v0DecayVertex, v0DaughterTrackParCovsPerfect[1], pdgDB); - break; - case kLambda0: - o2::upgrade::convertTLorentzVectorToO2Track(kPiMinus, v0DecayProducts[0], v0DecayVertex, v0DaughterTrackParCovsPerfect[0], pdgDB); - o2::upgrade::convertTLorentzVectorToO2Track(kProton, v0DecayProducts[1], v0DecayVertex, v0DaughterTrackParCovsPerfect[1], pdgDB); - break; - case kLambda0Bar: - o2::upgrade::convertTLorentzVectorToO2Track(kProtonBar, v0DecayProducts[0], v0DecayVertex, v0DaughterTrackParCovsPerfect[0], pdgDB); - o2::upgrade::convertTLorentzVectorToO2Track(kPiPlus, v0DecayProducts[1], v0DecayVertex, v0DaughterTrackParCovsPerfect[1], pdgDB); - break; - default: - LOG(fatal) << "Unhandled V0 PDG code " << mcParticle.pdgCode(); + for (int indexDetector = 0; indexDetector < mGeoContainer.getNumberOfConfigurations(); indexDetector++) { + std::string path = Form("V0Building_Configuration_%i/%s/", indexDetector, NameV0s[indexV0].data()); + fillHist(path + "hGen", v0DecayRadius2D, mcParticle.pt()); + fillHist(path + "hGenNegDaughterFromV0", v0DecayRadius2D, v0DecayProducts[0].Pt()); + fillHist(path + "hGenPosDaughterFromV0", v0DecayRadius2D, v0DecayProducts[1].Pt()); } - for (int i = 0; i < kv0Prongs; i++) { - isV0Reco[i] = false; - nV0Hits[i] = 0; - nV0SiliconHits[i] = 0; - nV0TPCHits[i] = 0; - if (enableSecondarySmearing) { - nV0Hits[i] = fastTracker[icfg]->FastTrack(v0DaughterTrackParCovsPerfect[i], v0DaughterTrackParCovsTracked[i], dNdEta); - nV0SiliconHits[i] = fastTracker[icfg]->GetNSiliconPoints(); - nV0TPCHits[i] = fastTracker[icfg]->GetNGasPoints(); - - if (nV0Hits[i] < 0 && v0DecaySettings.doV0QA) { // QA - fillHist(TH1, Form("V0Building_Configuration_%i/hFastTrackerQA", icfg), o2::math_utils::abs(nV0Hits[i])); - } + } + } - if (nV0SiliconHits[i] >= fastTrackerSettings.minSiliconHits || (nV0SiliconHits[i] >= fastTrackerSettings.minSiliconHitsIfTPCUsed && nV0TPCHits[i] >= fastTrackerSettings.minTPCClusters)) { - isReco[i] = true; - } else { - continue; // extra sure - } - for (uint32_t ih = 0; ih < fastTracker[icfg]->GetNHits() && v0DecaySettings.doV0QA; ih++) { - fillHist(TH2, Form("V0Building_Configuration_%i/hFastTrackerHits", icfg), fastTracker[icfg]->GetHitZ(ih), std::hypot(fastTracker[icfg]->GetHitX(ih), fastTracker[icfg]->GetHitY(ih))); - } - } else { - isReco[i] = true; - v0DaughterTrackParCovsTracked[i] = v0DaughterTrackParCovsPerfect[i]; - } + // V0 handling + std::vector v0DaughterTrackParCovsPerfect(2); + std::vector v0DaughterTrackParCovsTracked(2); + std::vector isV0Reco(NV0Prongs); + std::vector nV0Hits(NV0Prongs); // total + std::vector nV0SiliconHits(NV0Prongs); // silicon type + std::vector nV0TPCHits(NV0Prongs); // TPC type + if (v0DecaySettings.doV0QA) { + fillHist(Form("V0Building_Configuration_%i/hV0Building", icfg), 0.0f); + } + switch (mcParticle.pdgCode()) { + case kK0Short: + o2::upgrade::convertTLorentzVectorToO2Track(kPiMinus, v0DecayProducts[0], v0DecayVertex, v0DaughterTrackParCovsPerfect[0], pdgDB); + o2::upgrade::convertTLorentzVectorToO2Track(kPiPlus, v0DecayProducts[1], v0DecayVertex, v0DaughterTrackParCovsPerfect[1], pdgDB); + break; + case kLambda0: + o2::upgrade::convertTLorentzVectorToO2Track(kPiMinus, v0DecayProducts[0], v0DecayVertex, v0DaughterTrackParCovsPerfect[0], pdgDB); + o2::upgrade::convertTLorentzVectorToO2Track(kProton, v0DecayProducts[1], v0DecayVertex, v0DaughterTrackParCovsPerfect[1], pdgDB); + break; + case kLambda0Bar: + o2::upgrade::convertTLorentzVectorToO2Track(kProtonBar, v0DecayProducts[0], v0DecayVertex, v0DaughterTrackParCovsPerfect[0], pdgDB); + o2::upgrade::convertTLorentzVectorToO2Track(kPiPlus, v0DecayProducts[1], v0DecayVertex, v0DaughterTrackParCovsPerfect[1], pdgDB); + break; + default: + LOG(fatal) << "Unhandled V0 PDG code " << mcParticle.pdgCode(); + } - // if (TMath::IsNaN(v0DaughterTrackParCovsTracked[i].getZ())) { - // continue; - // } else { - // histos.fill(HIST("hNaNBookkeeping"), i + 1, 1.0f); - // } - if (isReco[i]) { - tracksAlice3.push_back(TrackAlice3{v0DaughterTrackParCovsTracked[i], mcParticle.globalIndex(), time, timeResolutionUs, true, true, i + 2, nSiliconHitsCascadeProngs[i], nTPCHitsCascadeProngs[i]}); - } else { - ghostTracksAlice3.push_back(TrackAlice3{v0DaughterTrackParCovsTracked[i], mcParticle.globalIndex(), time, timeResolutionUs, true, true, i + 2}); - } + // Store not reconstructed daughters, will update them in case reconstruction is successful + float trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksV0Daugs.push_back(TrackAlice3{v0DaughterTrackParCovsPerfect[0], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 1, TrackType::kGhostV0Daug}); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksV0Daugs.push_back(TrackAlice3{v0DaughterTrackParCovsPerfect[1], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 1, TrackType::kGhostV0Daug}); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksV0Daugs.push_back(TrackAlice3{v0DaughterTrackParCovsPerfect[0], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 1, 0, 0, TrackType::kGhostV0Daug}); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + tracksV0Daugs.push_back(TrackAlice3{v0DaughterTrackParCovsPerfect[1], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, 1, 0, 0, TrackType::kGhostV0Daug}); + + bool fillV0Table{false}; + for (int i = 0; i < NV0Prongs; i++) { + isV0Reco[i] = false; + nV0Hits[i] = 0; + nV0SiliconHits[i] = 0; + nV0TPCHits[i] = 0; + if (enableSecondarySmearing) { + nV0Hits[i] = fastTracker[icfg]->fastTrack(v0DaughterTrackParCovsPerfect[i], v0DaughterTrackParCovsTracked[i], dNdEta); + nV0SiliconHits[i] = fastTracker[icfg]->getNSiliconPoints(); + nV0TPCHits[i] = fastTracker[icfg]->getNGasPoints(); + + if (nV0SiliconHits[i] >= fastTrackerSettings.minSiliconHits || + (nV0SiliconHits[i] >= fastTrackerSettings.minSiliconHitsIfTPCUsed && + nV0TPCHits[i] >= fastTrackerSettings.minTPCClusters)) { + isV0Reco[i] = true; + } else { + continue; // extra sure } - if (v0DecaySettings.doV0QA) { - if (isReco[0] && isReco[1]) { - fillHist(TH1, Form("V0Building_Configuration_%i/hV0Building", icfg), 1.0f); - for (size_t indexV0 = 0; indexV0 < v0PDGs.size(); indexV0++) { - if (mcParticle.pdgCode() == v0PDGs[indexV0]) { - fillHist(TH2, Form("V0Building_Configuration_%i/%s/hReco", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, mcParticle.pt()); - } - } - } - if (isReco[0]) { - for (size_t indexV0 = 0; indexV0 < v0PDGs.size(); indexV0++) { - if (mcParticle.pdgCode() == v0PDGs[indexV0]) { - fillHist(TH2, Form("V0Building_Configuration_%i/%s/hRecoNegDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[0].Pt()); - } - } + + if (v0DecaySettings.doV0QA) { // QA + if (nV0Hits[i] < 0) { + fillHist(Form("V0Building_Configuration_%i/hFastTrackerQA", icfg), o2::math_utils::abs(nV0Hits[i])); } - if (isReco[1]) { - for (size_t indexV0 = 0; indexV0 < v0PDGs.size(); indexV0++) { - if (mcParticle.pdgCode() == v0PDGs[indexV0]) { - fillHist(TH2, Form("V0Building_Configuration_%i/%s/hRecoPosDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[1].Pt()); - } - } + for (uint32_t ih = 0; ih < fastTracker[icfg]->getNHits(); ih++) { + fillHist(Form("V0Building_Configuration_%i/hFastTrackerHits", icfg), fastTracker[icfg]->getHitZ(ih), std::hypot(fastTracker[icfg]->getHitX(ih), fastTracker[icfg]->getHitY(ih))); } } + } else { + isV0Reco[i] = true; + v0DaughterTrackParCovsTracked[i] = v0DaughterTrackParCovsPerfect[i]; + } - // +-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+ - // combine particles into actual V0 candidate - // V0 building starts here - if (v0DecaySettings.findV0 && isReco[0] && isReco[1]) { - if (v0DecaySettings.doV0QA) { - fillHist(TH1, Form("V0Building_Configuration_%i/hV0Building", icfg), 2.0f); + trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + // TODO: flag to separate ghost and reco tracks + TrackType trackType = isV0Reco[i] ? TrackType::kRecoV0Daug : TrackType::kGhostV0Daug; + tracksV0Daugs[i] = TrackAlice3{v0DaughterTrackParCovsTracked[i], mcParticle.globalIndex(), trackTime, TimeResolutionUs, true, false, true, i + 2, trackType}; + } + if (v0DecaySettings.doV0QA) { + if (isV0Reco[0] && isV0Reco[1]) { + fillHist(Form("V0Building_Configuration_%i/hV0Building", icfg), 1.0f); + for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { + if (mcParticle.pdgCode() == V0PDGs[indexV0]) { + fillHist(Form("V0Building_Configuration_%i/%s/hReco", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, mcParticle.pt()); } - - // assign indices of the particles we've used - // they should be the last ones to be filled, in order: - // n-1: positive Track from V0 - // n-2: negative Track from V0 - thisV0.positiveId = lastTrackIndex + tracksAlice3.size() - 1; - thisV0.negativeId = lastTrackIndex + tracksAlice3.size() - 2; - thisV0.mcParticleId = mcParticle.globalIndex(); - // use DCA fitters - int nCand = 0; - bool dcaFitterOK_V0 = true; - try { - nCand = fitter.process(v0DaughterTrackParCovsTracked[0], v0DaughterTrackParCovsTracked[1]); - } catch (...) { - // LOG(error) << "Exception caught in DCA fitter process call!"; - dcaFitterOK_V0 = false; + } + } + if (isV0Reco[0]) { + for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { + if (mcParticle.pdgCode() == V0PDGs[indexV0]) { + fillHist(Form("V0Building_Configuration_%i/%s/hRecoNegDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[0].Pt()); } - if (nCand == 0) { - dcaFitterOK_V0 = false; + } + } + if (isV0Reco[1]) { + for (size_t indexV0 = 0; indexV0 < V0PDGs.size(); indexV0++) { + if (mcParticle.pdgCode() == V0PDGs[indexV0]) { + fillHist(Form("V0Building_Configuration_%i/%s/hRecoPosDaughterFromV0", icfg, NameV0s[indexV0].data()), v0DecayRadius2D, v0DecayProducts[1].Pt()); } + } + } + } - fitter.propagateTracksToVertex(); - if (!fitter.isPropagateTracksToVertexDone()) { - dcaFitterOK_V0 = false; - } + // +-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+-~-+ + // combine particles into actual V0 candidate + // V0 building starts here + if (v0DecaySettings.findV0 && isV0Reco[0] && isV0Reco[1]) { + if (v0DecaySettings.doV0QA) { + fillHist(Form("V0Building_Configuration_%i/hV0Building", icfg), 2.0f); + } - const u_int8_t fitterStatusCode = fitter.getFitStatus(); - histos.fill(HIST("hFitterStatusCode"), fitterStatusCode); - // V0 found successfully - if (dcaFitterOK_V0) { - if (v0DecaySettings.doV0QA) { - fillHist(TH1, Form("V0Building_Configuration_%i/hV0Building", icfg), 3.0f); - } + // assign indices of the daughter particles + thisV0.mcParticleId = mcParticle.globalIndex(); + thisV0.negativeId = trackTableOffset + 1; + thisV0.positiveId = trackTableOffset + 2; + + // use DCA fitters + int nCand = 0; + bool dcaFitterV0Status = true; + try { + nCand = fitter.process(v0DaughterTrackParCovsTracked[0], v0DaughterTrackParCovsTracked[1]); + } catch (...) { + // LOG(error) << "Exception caught in DCA fitter process call!"; + dcaFitterV0Status = false; + } + if (nCand == 0) { + dcaFitterV0Status = false; + } - std::array pos; - std::array posP; - std::array negP; + fitter.propagateTracksToVertex(); + if (!fitter.isPropagateTracksToVertexDone()) { + dcaFitterV0Status = false; + } - o2::track::TrackParCov pTrackAtPCA = fitter.getTrack(1); // (positive) - o2::track::TrackParCov nTrackAtPCA = fitter.getTrack(0); // (negative) - pTrackAtPCA.getPxPyPzGlo(posP); - nTrackAtPCA.getPxPyPzGlo(negP); + const u_int8_t fitterStatusCode = fitter.getFitStatus(); + histos.fill(HIST("hFitterStatusCode"), fitterStatusCode); + // V0 found successfully + if (dcaFitterV0Status) { + if (v0DecaySettings.doV0QA) { + fillHist(Form("V0Building_Configuration_%i/hV0Building", icfg), 3.0f); + } - // get decay vertex coordinates - const auto& vtx = fitter.getPCACandidate(); - for (int i = 0; i < 3; i++) { - pos[i] = vtx[i]; - } + std::array pos{}; + std::array posP{}; + std::array negP{}; - // calculate basic V0 properties here - // DCA to PV taken care of in daughter tracks already, not necessary - thisV0.dcaV0dau = TMath::Sqrt(fitter.getChi2AtPCACandidate()); - thisV0.v0radius = std::hypot(pos[0], pos[1]); - thisV0.pt = std::hypot(std::cos(v0DaughterTrackParCovsTracked[0].getPhi()) * v0DaughterTrackParCovsTracked[0].getPt() + std::cos(v0DaughterTrackParCovsTracked[1].getPhi()) * v0DaughterTrackParCovsTracked[1].getPt(), - std::sin(v0DaughterTrackParCovsTracked[0].getPhi()) * v0DaughterTrackParCovsTracked[0].getPt() + std::sin(v0DaughterTrackParCovsTracked[1].getPhi()) * v0DaughterTrackParCovsTracked[1].getPt()); - if (std::abs(mcParticle.pdgCode()) == kK0Short) { - thisV0.mK0 = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, + o2::track::TrackParCov pTrackAtPCA = fitter.getTrack(1); // (positive) + o2::track::TrackParCov nTrackAtPCA = fitter.getTrack(0); // (negative) + pTrackAtPCA.getPxPyPzGlo(posP); + nTrackAtPCA.getPxPyPzGlo(negP); + + // get decay vertex coordinates + const auto& pcaCandidate = fitter.getPCACandidate(); + for (int i = 0; i < NDim3D; i++) { + pos[i] = pcaCandidate[i]; + } + + // calculate basic V0 properties here + // DCA to PV taken care of in daughter tracks already, not necessary + thisV0.dcaV0dau = std::sqrt(fitter.getChi2AtPCACandidate()); + thisV0.v0radius = std::hypot(pos[0], pos[1]); + thisV0.pt = std::hypot(std::cos(v0DaughterTrackParCovsTracked[0].getPhi()) * v0DaughterTrackParCovsTracked[0].getPt() + std::cos(v0DaughterTrackParCovsTracked[1].getPhi()) * v0DaughterTrackParCovsTracked[1].getPt(), + std::sin(v0DaughterTrackParCovsTracked[0].getPhi()) * v0DaughterTrackParCovsTracked[0].getPt() + std::sin(v0DaughterTrackParCovsTracked[1].getPhi()) * v0DaughterTrackParCovsTracked[1].getPt()); + + thisV0.mK0 = -1, thisV0.mLambda = -1, thisV0.mAntiLambda = -1; // initialize to unphysical values + if (std::abs(mcParticle.pdgCode()) == kK0Short) { + thisV0.mK0 = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, + std::array{negP[0], negP[1], negP[2]}}, + std::array{o2::constants::physics::MassPionCharged, + o2::constants::physics::MassPionCharged}); + } + + if (mcParticle.pdgCode() == kLambda0) { + thisV0.mLambda = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, std::array{negP[0], negP[1], negP[2]}}, - std::array{o2::constants::physics::MassPionCharged, + std::array{o2::constants::physics::MassProton, o2::constants::physics::MassPionCharged}); - } else { - thisV0.mK0 = -1; - } + } - if (mcParticle.pdgCode() == kLambda0) { - thisV0.mLambda = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, + if (mcParticle.pdgCode() == kLambda0Bar) { + thisV0.mAntiLambda = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, std::array{negP[0], negP[1], negP[2]}}, - std::array{o2::constants::physics::MassProton, - o2::constants::physics::MassPionCharged}); - } else { - thisV0.mLambda = -1; - } - - if (mcParticle.pdgCode() == kLambda0Bar) { - thisV0.mAntiLambda = RecoDecay::m(std::array{std::array{posP[0], posP[1], posP[2]}, - std::array{negP[0], negP[1], negP[2]}}, - std::array{o2::constants::physics::MassPionCharged, - o2::constants::physics::MassProton}); - } else { - thisV0.mAntiLambda = -1; - } - - if (v0DecaySettings.doV0QA) { - fillHist(TH1, Form("V0Building_Configuration_%i/hV0Building", icfg), 4.0f); - if (std::abs(mcParticle.pdgCode()) == kK0Short) { - fillHist(TH2, Form("V0Building_Configuration_%i/K0/hMass", icfg), thisV0.mK0, thisV0.pt); - } - if (mcParticle.pdgCode() == kLambda0) { - fillHist(TH2, Form("V0Building_Configuration_%i/Lambda/hMass", icfg), thisV0.mLambda, thisV0.pt); - } - if (mcParticle.pdgCode() == kLambda0Bar) { - fillHist(TH2, Form("V0Building_Configuration_%i/AntiLambda/hMass", icfg), thisV0.mAntiLambda, thisV0.pt); - } - } + std::array{o2::constants::physics::MassPionCharged, + o2::constants::physics::MassProton}); + } - // add this V0 to vector (will fill cursor later with collision ID) - v0sAlice3.push_back(thisV0); + if (v0DecaySettings.doV0QA) { + fillHist(Form("V0Building_Configuration_%i/hV0Building", icfg), 4.0f); + if (std::abs(mcParticle.pdgCode()) == kK0Short) { + fillHist(Form("V0Building_Configuration_%i/K0/hMass", icfg), thisV0.mK0, thisV0.pt); + } + if (mcParticle.pdgCode() == kLambda0) { + fillHist(Form("V0Building_Configuration_%i/Lambda/hMass", icfg), thisV0.mLambda, thisV0.pt); + } + if (mcParticle.pdgCode() == kLambda0Bar) { + fillHist(Form("V0Building_Configuration_%i/AntiLambda/hMass", icfg), thisV0.mAntiLambda, thisV0.pt); } } - } - if (doExtraQA) { - histos.fill(HIST("hSimTrackX"), trackParCov.getX()); - } - if (isV0) { - continue; // V0 handling done, should not be considered anymore + LOG(info) << "Will fill V0 table for MC particle with PDG " << mcParticle.pdgCode() << " and global index " << mcParticle.globalIndex(); + fillV0Table = true; } + } - bool reconstructed = true; - int nTrkHits = 0; - if (enablePrimarySmearing && !fastPrimaryTrackerSettings.fastTrackPrimaries) { - reconstructed = mSmearer[icfg]->smearTrack(trackParCov, mcParticle.pdgCode(), dNdEta); - nTrkHits = fastTrackerSettings.minSiliconHits; - } else if (fastPrimaryTrackerSettings.fastTrackPrimaries) { - o2::track::TrackParCov o2Track; - o2::upgrade::convertMCParticleToO2Track(mcParticle, o2Track, pdgDB); - o2Track.setPID(pdgCodeToPID(mcParticle.pdgCode())); - nTrkHits = fastTracker[icfg]->FastTrack(o2Track, trackParCov, dNdEta); - if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { - reconstructed = false; - } - } + if (!fillV0Table) { + tracksV0Daugs.clear(); // clear the tracks added for this V0 since we won't be filling the table + return; // don't fill the table if we didn't find a V0 candidate + } - if (!reconstructed && !processUnreconstructedTracks) { - continue; - } - if (TMath::IsNaN(trackParCov.getZ())) { - // capture rare smearing mistakes / corrupted tracks - histos.fill(HIST("hNaNBookkeeping"), 0.0f, 0.0f); - continue; - } else { - histos.fill(HIST("hNaNBookkeeping"), 0.0f, 1.0f); // ok! - } + // populate V0s + tableUpgradeV0s(tableCollisions.lastIndex(), // now we know the collision index -> populate table + thisV0.mcParticleId, + thisV0.positiveId, + thisV0.negativeId, + thisV0.dcaV0dau, + thisV0.v0radius, + thisV0.mLambda, + thisV0.mAntiLambda, + thisV0.mK0, + thisV0.pt); + } - // Base QA (note: reco pT here) - if (enablePrimarySmearing) { - getHist(TH1, histPath + "hPtReconstructed")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kElectron) - getHist(TH1, histPath + "hPtReconstructedEl")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kPiPlus) - getHist(TH1, histPath + "hPtReconstructedPi")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kKPlus) - getHist(TH1, histPath + "hPtReconstructedKa")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kProton) - getHist(TH1, histPath + "hPtReconstructedPr")->Fill(trackParCov.getPt()); - } - if (doExtraQA) { - getHist(TH2, histPath + "h2dPtRes")->Fill(trackParCov.getPt(), (trackParCov.getPt() - mcParticle.pt()) / trackParCov.getPt()); - histos.fill(HIST("hRecoTrackX"), trackParCov.getX()); - } + /// Function to compute the primary vertex position using the provided tracks and MC collision information + /// \param mcCollision the MC collision information, used to get the true vertex position for comparison + /// \param prmTrks the vector of tracks to be used for vertex reconstruction + /// \param primaryVertex the output variable where the computed primary vertex will be stored + template + void computeVertex(McCollisionType& mcCollision, std::vector& prmTrks, o2::vertexing::PVertex& primaryVertex) + { - // populate vector with track if we reco-ed it - if (reconstructed) { - tracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, timeResolutionUs, isDecayDaughter, false, 0, nTrkHits}); - } else { - ghostTracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, timeResolutionUs, isDecayDaughter}); - } + if (!enablePrimaryVertexing) { + primaryVertex.setXYZ(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + return; + } + vertexReconstructionEfficiencyCounters.first += 1; + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; + fillHist(histPath + "hVtxMultGen", prmTrks.size()); + std::vector lblTracks; + std::vector vertices; + std::vector vertexTrackIDs; + std::vector v2tRefs; + std::vector lblVtx; + lblVtx.emplace_back(mcCollision.globalIndex(), 1); + std::vector idxVec; // store IDs + + idxVec.reserve(prmTrks.size()); + for (unsigned i = 0; i < prmTrks.size(); i++) { + lblTracks.emplace_back(prmTrks[i].mcLabel, mcCollision.globalIndex(), 1, false); + idxVec.emplace_back(i, o2::dataformats::GlobalTrackID::ITS); // let's say ITS } - // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* - // Calculate primary vertex with tracks from this collision - // data preparation - o2::vertexing::PVertex primaryVertex; - if (enablePrimaryVertexing) { - LOG(debug) << "Starting primary vertexing with " << tracksAlice3.size() << " tracks."; - fillHist(TH1, histPath + "hVtxMultGen", tracksAlice3.size()); - std::vector lblTracks; - std::vector vertices; - std::vector vertexTrackIDs; - std::vector v2tRefs; - std::vector lblVtx; - lblVtx.emplace_back(mcCollision.globalIndex(), 1); - std::vector idxVec; // store IDs - - idxVec.reserve(tracksAlice3.size()); - for (unsigned i = 0; i < tracksAlice3.size(); i++) { - lblTracks.emplace_back(tracksAlice3[i].mcLabel, mcCollision.globalIndex(), 1, false); - idxVec.emplace_back(i, o2::dataformats::GlobalTrackID::ITS); // let's say ITS - } + getHist(histPath + "hVtxTrials")->Fill(0); // Tried vertexing - getHist(TH1, histPath + "hVtxTrials")->Fill(0); // Tried vertexing - - // Calculate vertices - const int n_vertices = vertexer.process(tracksAlice3, // track array - idxVec, - gsl::span{bcData}, - vertices, - vertexTrackIDs, - v2tRefs, - gsl::span{lblTracks}, - lblVtx); - - LOG(debug) << "Vertex reconstruction efficiency " << vertexReconstructionEfficiencyCounters.second << "/" << vertexReconstructionEfficiencyCounters.first << "=" << vertexReconstructionEfficiencyCounters.second / vertexReconstructionEfficiencyCounters.first; - if (n_vertices < 1) { - LOG(debug) << "Vertexing completed, no vtx found."; - if (doExtraQA) { - histos.fill(HIST("h1dVerticesNotReco"), tracksAlice3.size()); - } - return; // primary vertex not reconstructed - } - vertexReconstructionEfficiencyCounters.second += 1; - getHist(TH1, histPath + "hVtxTrials")->Fill(1); // Succeeded vertexing - LOG(debug) << "Vertexing completed with " << n_vertices << " vertices found."; - - // Find largest vertex - int largestVertex = 0; - for (Int_t iv = 1; iv < n_vertices; iv++) { - if (vertices[iv].getNContributors() > vertices[largestVertex].getNContributors()) { - largestVertex = iv; - } - } - primaryVertex = vertices[largestVertex]; + // Calculate vertices + const int nVertices = vertexer.process(prmTrks, // track array + idxVec, + gsl::span{bcData}, + vertices, + vertexTrackIDs, + v2tRefs, + gsl::span{lblTracks}, + lblVtx); + + if (nVertices < 1) { if (doExtraQA) { - histos.fill(HIST("h2dVerticesVsContributors"), primaryVertex.getNContributors(), n_vertices); + histos.fill(HIST("h1dVerticesNotReco"), prmTrks.size()); } - fillHist(TH1, histPath + "hVtxMultReco", primaryVertex.getNContributors()); - fillHist(TH1, histPath + "hDeltaMultPVRecoGen", static_cast(primaryVertex.getNContributors()) - static_cast(tracksAlice3.size())); - fillHist(TH1, histPath + "hDeltaXPVRecoGen", primaryVertex.getX() - mcCollision.posX()); - fillHist(TH1, histPath + "hDeltaYPVRecoGen", primaryVertex.getY() - mcCollision.posY()); - fillHist(TH1, histPath + "hDeltaZPVRecoGen", primaryVertex.getZ() - mcCollision.posZ()); - } else { - primaryVertex.setXYZ(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + return; // primary vertex not reconstructed } - // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* + vertexReconstructionEfficiencyCounters.second += 1; + getHist(histPath + "hVtxTrials")->Fill(1); // Succeeded vertexing + + // Find largest vertex + int largestVertex = 0; + for (int iv = 1; iv < nVertices; iv++) { + if (vertices[iv].getNContributors() > vertices[largestVertex].getNContributors()) { + largestVertex = iv; + } + } + + primaryVertex = vertices[largestVertex]; + + const auto& contributorRef = v2tRefs[largestVertex]; + const int first = contributorRef.getFirstEntry(); + const int end = first + contributorRef.getEntries(); - // debug / informational - getHist(TH1, histPath + "hSimMultiplicity")->Fill(multiplicityCounter); - getHist(TH1, histPath + "hRecoMultiplicity")->Fill(tracksAlice3.size()); - getHist(TH1, histPath + "hPVz")->Fill(primaryVertex.getZ()); + for (int i = first; i < end; ++i) { + const auto trackIndex = vertexTrackIDs[i].getIndex(); + prmTrks[trackIndex].isPVContributor = true; + } if (doExtraQA) { - histos.fill(HIST("hRecoVsSimMultiplicity"), multiplicityCounter, tracksAlice3.size()); + histos.fill(HIST("h2dVerticesVsContributors"), primaryVertex.getNContributors(), nVertices); } + fillHist(histPath + "hVtxMultReco", primaryVertex.getNContributors()); + fillHist(histPath + "hDeltaMultPVRecoGen", static_cast(primaryVertex.getNContributors()) - static_cast(prmTrks.size())); + fillHist(histPath + "hDeltaXPVRecoGen", primaryVertex.getX() - mcCollision.posX(), primaryVertex.getNContributors()); + fillHist(histPath + "hDeltaYPVRecoGen", primaryVertex.getY() - mcCollision.posY(), primaryVertex.getNContributors()); + fillHist(histPath + "hDeltaZPVRecoGen", primaryVertex.getZ() - mcCollision.posZ(), primaryVertex.getNContributors()); + } - // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* - // populate collisions - tableCollisions(-1, // BC is irrelevant in synthetic MC tests for now, could be adjusted in future - primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), - primaryVertex.getSigmaX2(), primaryVertex.getSigmaXY(), primaryVertex.getSigmaY2(), - primaryVertex.getSigmaXZ(), primaryVertex.getSigmaYZ(), primaryVertex.getSigmaZ2(), - 0, primaryVertex.getChi2(), primaryVertex.getNContributors(), - eventCollisionTimeNS, 0.f); // For the moment the event collision time is taken as the "GEANT" time, the computation of the event time is done a posteriori from the tracks in the OTF TOF PID task - tableMcCollisionLabels(mcCollision.globalIndex(), 0); - tableCollisionsAlice3(dNdEta); - tableOTFLUTConfigId(icfg); // Populate OTF LUT configuration ID - // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* + /// Function to fill track information into the relevant tables and histograms + /// \param tracks the vector of tracks to be processed + /// \param primaryVertex the primary vertex position, used for DCA calculation + void fillTracksInfo(std::vector const& tracks, o2::vertexing::PVertex const& primaryVertex) + { + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; - // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* - // populate tracks - LOG(debug) << "Populating " << tracksAlice3.size() << " tracks."; - for (const auto& trackParCov : tracksAlice3) { + for (const auto& trackParCov : tracks) { // Fixme: collision index could be changeable aod::track::TrackTypeEnum trackType = aod::track::Track; if (populateTracksDCA) { float dcaXY = 1e+10, dcaZ = 1e+10; - o2::track::TrackParCov trackParametrization(trackParCov); + o2::track::TrackParCov trackParametrization(static_cast(trackParCov)); if (trackParametrization.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { dcaXY = dcaInfo.getY(); dcaZ = dcaInfo.getZ(); } - if (doExtraQA && (!extraQAwithoutDecayDaughters || (extraQAwithoutDecayDaughters && !trackParCov.isDecayDau))) { - getHist(TH2, histPath + "h2dDCAxy")->Fill(trackParametrization.getPt(), dcaXY * 1e+4); - getHist(TH2, histPath + "h2dDCAz")->Fill(trackParametrization.getPt(), dcaZ * 1e+4); + if (doExtraQA && (!extraQAwithoutDecayDaughters || !trackParCov.isDecayDau)) { + getHist(histPath + "h2dDCAxy")->Fill(trackParametrization.getPt(), dcaXY * 1e+4); + getHist(histPath + "h2dDCAz")->Fill(trackParametrization.getPt(), dcaZ * 1e+4); histos.fill(HIST("hTrackXatDCA"), trackParametrization.getX()); } - if (cascadeDecaySettings.doXiQA) { - if (trackParCov.isUsedInCascading == 1) { - getHist(TH2, histPath + "h2dDCAxyCascade")->Fill(trackParametrization.getPt(), dcaXY * 1e+4); // in microns, please - getHist(TH2, histPath + "h2dDCAzCascade")->Fill(trackParametrization.getPt(), dcaZ * 1e+4); // in microns, please - } - if (trackParCov.isUsedInCascading == 2) { - getHist(TH2, histPath + "h2dDCAxyCascadeBachelor")->Fill(trackParametrization.getPt(), dcaXY * 1e+4); // in microns, please - getHist(TH2, histPath + "h2dDCAzCascadeBachelor")->Fill(trackParametrization.getPt(), dcaZ * 1e+4); // in microns, please - } - if (trackParCov.isUsedInCascading == 3) { - getHist(TH2, histPath + "h2dDCAxyCascadeNegative")->Fill(trackParametrization.getPt(), dcaXY * 1e+4); // in microns, please - getHist(TH2, histPath + "h2dDCAzCascadeNegative")->Fill(trackParametrization.getPt(), dcaZ * 1e+4); // in microns, please - } - if (trackParCov.isUsedInCascading == 4) { - getHist(TH2, histPath + "h2dDCAxyCascadePositive")->Fill(trackParametrization.getPt(), dcaXY * 1e+4); // in microns, please - getHist(TH2, histPath + "h2dDCAzCascadePositive")->Fill(trackParametrization.getPt(), dcaZ * 1e+4); // in microns, please - } - } tableTracksDCA(dcaXY, dcaZ); if (populateTracksDCACov) { tableTracksDCACov(dcaInfo.getSigmaY2(), dcaInfo.getSigmaZ2()); @@ -1755,7 +1817,7 @@ struct OnTheFlyTracker { trackParCov.getSigmaTgl2(), trackParCov.getSigma1PtY(), trackParCov.getSigma1PtZ(), trackParCov.getSigma1PtSnp(), trackParCov.getSigma1PtTgl(), trackParCov.getSigma1Pt2()); tableMcTrackLabels(trackParCov.mcLabel, 0); - tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits); + tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits, trackParCov.trackType, trackParCov.isPVContributor); // populate extra tables if required to do so if (populateTracksExtra) { @@ -1768,113 +1830,294 @@ struct OnTheFlyTracker { tableTrackSelection(static_cast(0), false, false, false, false, false, false); tableTrackSelectionExtension(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false); } - tableTracksAlice3(true); + tableTracksAlice3(true, trackParCov.trackIU, trackParCov.trackOU); } + } - // populate ghost tracks - LOG(debug) << "Populating " << ghostTracksAlice3.size() << " ghost tracks."; - for (const auto& trackParCov : ghostTracksAlice3) { - // Fixme: collision index could be changeable - aod::track::TrackTypeEnum trackType = aod::track::Track; + /// Function to compute the bremsstrahlung loss of charged-particles for each layer of the current configuration + /// \param mcParticle true MC particle to identify particle and get the energy + /// \param trackParCov track of the particle to compute bremsstrahlung for + void computeBremsstrahlungLoss(const auto& mcParticle, o2::track::TrackParCov& trackParCov) + { + if (!cfgBR.radiateBR) { + return; + } + const o2::fastsim::GeometryEntry geoEntry = mGeoContainer.getEntry(icfg); - if (populateTracksDCA) { - float dcaXY = 1e+10, dcaZ = 1e+10; - o2::track::TrackParCov trackParametrization(trackParCov); - if (trackParametrization.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { - dcaXY = dcaInfo.getY(); - dcaZ = dcaInfo.getZ(); - } - if (doExtraQA && (!extraQAwithoutDecayDaughters || (extraQAwithoutDecayDaughters && !trackParCov.isDecayDau))) { - histos.fill(HIST("h2dDCAxy"), trackParametrization.getPt(), dcaXY * 1e+4); // in microns, please - histos.fill(HIST("h2dDCAz"), trackParametrization.getPt(), dcaZ * 1e+4); // in microns, please - histos.fill(HIST("hTrackXatDCA"), trackParametrization.getX()); + for (auto const& layerName : geoEntry.getLayerNames()) { + if (layerName.find("global") != std::string::npos) { // Layers with global tag are skipped + continue; + } + + float mass = o2::constants::physics::MassElectron; + + switch (std::abs(mcParticle.pdgCode())) { + case kElectron: + mass = o2::constants::physics::MassElectron; + break; + case kMuonMinus: + mass = o2::constants::physics::MassMuon; + break; + case kPiPlus: + mass = o2::constants::physics::MassPionCharged; + break; + case kKPlus: + mass = o2::constants::physics::MassKaonCharged; + break; + case kProton: + mass = o2::constants::physics::MassProton; + break; + default: + break; + } + + const float lambda = cfgBR.radiationStrength * mcParticle.e() * geoEntry.getFloatValue(layerName, "x0") / (mass * mass); + const uint64_t nPhotons = gRandom->Poisson(lambda); + const double initialMomentum = trackParCov.getP(); + for (uint64_t photon = 0; photon < nPhotons; ++photon) { + float radiativeLoss = 1.0f - cfgBR.minEnergyFraction * std::pow(cfgBR.maxEnergyFraction / cfgBR.minEnergyFraction, gRandom->Rndm()); + trackParCov.setQ2Pt(trackParCov.getQ2Pt() / radiativeLoss); + } + + double afterRadiationMomentum = trackParCov.getP(); + + if (cfgBR.doBRQA) { + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; + + getHist(histPath + "h1dNBRPhotons")->Fill(static_cast(nPhotons)); + getHist(histPath + "h1dBREnergyLoss")->Fill((initialMomentum - afterRadiationMomentum) / afterRadiationMomentum); + + getHist(histPath + "h2dBRPtRes")->Fill(trackParCov.getPt(), (trackParCov.getPt() - mcParticle.pt()) / trackParCov.getPt()); + getHist(histPath + "h2dBRPtResAbs")->Fill(trackParCov.getPt(), trackParCov.getPt() - mcParticle.pt()); + } + } + } + + void processWithLUTs(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; + + std::vector genCascades; + std::vector genV0s; + bcData.clear(); + recoPrimaries.clear(); + ghostPrimaries.clear(); + + // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* + // Study collision and perform vertexing + float dNdEta{0.f}; // Charged particle multiplicity to use in the efficiency evaluation + computeDNDEta(dNdEta, mcParticles, histPath); + auto ir = irSampler.generateCollisionTime(); + const float eventCollisionTimeNS = ir.timeInBCNS; + + uint32_t multiplicityCounter = 0; + // Now that the multiplicity is known, we can process the particles to smear them + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + + if ((std::fabs(mcParticle.eta()) > maxEta) || (mcParticle.pt() < minPt)) { + continue; + } + + const bool isCascadeToDecay = (mcParticle.pdgCode() == kXiMinus) && cascadeDecaySettings.decayXi; + const bool isV0ToDecay = std::find(V0PDGs.begin(), V0PDGs.end(), mcParticle.pdgCode()) != V0PDGs.end() && v0DecaySettings.decayV0; + const bool longLivedToBeHandled = std::find(LongLivedHandledPDGs.begin(), LongLivedHandledPDGs.end(), std::abs(mcParticle.pdgCode())) != LongLivedHandledPDGs.end(); + const bool shortLivedToBeHandled = std::find(ShortLivedHandledPDGs.begin(), ShortLivedHandledPDGs.end(), std::abs(mcParticle.pdgCode())) != ShortLivedHandledPDGs.end(); + const bool nucleiToBeHandled = std::find(NucleiPDGs.begin(), NucleiPDGs.end(), std::abs(mcParticle.pdgCode())) != NucleiPDGs.end(); + const bool pdgsToBeHandled = longLivedToBeHandled || + (enableNucleiSmearing && nucleiToBeHandled) || + (isCascadeToDecay) || (isV0ToDecay) || + (shortLivedToBeHandled && fastPrimaryTrackerSettings.fastTrackShortLivedParticles); + + if (!pdgsToBeHandled) { + continue; + } + + if (isCascadeToDecay) { + genCascades.push_back(mcParticle.globalIndex()); + } + if (isV0ToDecay) { + genV0s.push_back(mcParticle.globalIndex()); + } + + multiplicityCounter++; + o2::track::TrackParCov trackParCov; + const bool isDecayDaughter = (mcParticle.getProcess() == TMCProcess::kPDecay); + if (doExtraQA) { + histos.fill(HIST("hSimTrackX"), trackParCov.getX()); + } + + bool reconstructed = true; + int nTrkHits = 0; + std::array hitRadii = {-1.f, -1.f}; // {innermost, outermost} + if (enablePrimarySmearing) { + if (fastPrimaryTrackerSettings.fastTrackPrimaries && longLivedToBeHandled) { + o2::track::TrackParCov perfectTrackParCov = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDB); + perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); + computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); + nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; + if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { + reconstructed = false; + } + } else if (fastPrimaryTrackerSettings.fastTrackShortLivedParticles && shortLivedToBeHandled) { + o2::track::TrackParCov perfectTrackParCov = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDB); + perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); + computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); + const std::array decayVtx = decayer.generateDecayVertex(mcParticle, pdgDB); + const float decayRadius2D = std::hypot(decayVtx[0], decayVtx[1]); + nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta, decayRadius2D); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; + if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { + reconstructed = false; + } + } else { + o2::upgrade::convertMCParticleToO2Track(mcParticle, trackParCov, pdgDB); + computeBremsstrahlungLoss(mcParticle, trackParCov); + reconstructed = mSmearer[icfg]->smearTrack(trackParCov, mcParticle.pdgCode(), dNdEta); + nTrkHits = fastTrackerSettings.minSiliconHits; } - tableTracksDCA(dcaXY, dcaZ); - if (populateTracksDCACov) { - tableTracksDCACov(dcaInfo.getSigmaY2(), dcaInfo.getSigmaZ2()); + getHist(histPath + "hPtGenerated")->Fill(mcParticle.pt()); + getHist(histPath + "hPhiGenerated")->Fill(mcParticle.phi()); + switch (std::abs(mcParticle.pdgCode())) { + case kElectron: + getHist(histPath + "hPtGeneratedEl")->Fill(mcParticle.pt()); + break; + case kPiPlus: + getHist(histPath + "hPtGeneratedPi")->Fill(mcParticle.pt()); + break; + case kKPlus: + getHist(histPath + "hPtGeneratedKa")->Fill(mcParticle.pt()); + break; + case kProton: + getHist(histPath + "hPtGeneratedPr")->Fill(mcParticle.pt()); + break; + default: + break; } - } - tableStoredTracks(tableCollisions.lastIndex(), trackType, trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt()); - tableTracksExtension(trackParCov.getPt(), trackParCov.getP(), trackParCov.getEta(), trackParCov.getPhi()); + if (!reconstructed && !processUnreconstructedTracks) { + continue; + } - // TODO do we keep the rho as 0? Also the sigma's are duplicated information - tableStoredTracksCov(std::sqrt(trackParCov.getSigmaY2()), std::sqrt(trackParCov.getSigmaZ2()), std::sqrt(trackParCov.getSigmaSnp2()), - std::sqrt(trackParCov.getSigmaTgl2()), std::sqrt(trackParCov.getSigma1Pt2()), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - tableTracksCovExtension(trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), trackParCov.getSigmaSnpY(), - trackParCov.getSigmaSnpZ(), trackParCov.getSigmaSnp2(), trackParCov.getSigmaTglY(), trackParCov.getSigmaTglZ(), trackParCov.getSigmaTglSnp(), - trackParCov.getSigmaTgl2(), trackParCov.getSigma1PtY(), trackParCov.getSigma1PtZ(), trackParCov.getSigma1PtSnp(), trackParCov.getSigma1PtTgl(), - trackParCov.getSigma1Pt2()); - tableMcTrackLabels(trackParCov.mcLabel, 0); - tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits); + getHist(histPath + "hPtReconstructed")->Fill(trackParCov.getPt()); + switch (std::abs(mcParticle.pdgCode())) { + case kElectron: + getHist(histPath + "hPtReconstructedEl")->Fill(mcParticle.pt()); + break; + case kPiPlus: + getHist(histPath + "hPtReconstructedPi")->Fill(mcParticle.pt()); + break; + case kKPlus: + getHist(histPath + "hPtReconstructedKa")->Fill(mcParticle.pt()); + break; + case kProton: + getHist(histPath + "hPtReconstructedPr")->Fill(mcParticle.pt()); + break; + default: + break; + } + } + if (doExtraQA) { + getHist(histPath + "h2dPtRes")->Fill(trackParCov.getPt(), (trackParCov.getPt() - mcParticle.pt()) / trackParCov.getPt()); + getHist(histPath + "h2dPtResAbs")->Fill(trackParCov.getPt(), trackParCov.getPt() - mcParticle.pt()); + histos.fill(HIST("hRecoTrackX"), trackParCov.getX()); + } - // populate extra tables if required to do so - if (populateTracksExtra) { - tableStoredTracksExtra(0.0f, static_cast(0), static_cast(0), static_cast(0), static_cast(0), - static_cast(0), static_cast(0), static_cast(0), static_cast(0), - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + if (TMath::IsNaN(trackParCov.getZ())) { + histos.fill(HIST("hNaNBookkeeping"), 0.0f, 0.0f); + continue; // capture smearing mistakes } - if (populateTrackSelection) { - tableTrackSelection(static_cast(0), false, false, false, false, false, false); - tableTrackSelectionExtension(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false); + histos.fill(HIST("hNaNBookkeeping"), 0.0f, 1.0f); // ok! + + // Time associated to the mcParticle: collision time + smearing + float trackTime = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; + TrackType trackType = reconstructed ? TrackType::kRecoPrimary : TrackType::kGhostPrimary; + bool isPVContributor = trackType == TrackType::kRecoPrimary; + if (reconstructed) { + recoPrimaries.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), trackTime, TimeResolutionUs, isDecayDaughter, isPVContributor, false, 0, nTrkHits, trackType}); + recoPrimaries.back().trackIU = hitRadii[0]; + recoPrimaries.back().trackOU = hitRadii[1]; + } else { + ghostPrimaries.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), trackTime, TimeResolutionUs, isDecayDaughter, isPVContributor, false, 0, nTrkHits, trackType}); + ghostPrimaries.back().trackIU = hitRadii[0]; + ghostPrimaries.back().trackOU = hitRadii[1]; } - tableTracksAlice3(false); } - // populate Cascades - LOG(debug) << "Populating " << cascadesAlice3.size() << " cascades."; - for (const auto& cascade : cascadesAlice3) { - tableUpgradeCascades(tableCollisions.lastIndex(), // now we know the collision index -> populate table - cascade.cascadeTrackId, - cascade.positiveId, - cascade.negativeId, - cascade.bachelorId, - cascade.dcaV0dau, - cascade.dcacascdau, - cascade.v0radius, - cascade.cascradius, - cascade.cascradiusMC, - cascade.mLambda, - cascade.mXi, - cascade.findableClusters, - cascade.foundClusters); + // Compute primary vertex with primary tracks + o2::vertexing::PVertex primaryVertex; + static constexpr int MinTracksForReconstructingPV = 2; + if (enablePrimaryVertexing && recoPrimaries.size() <= MinTracksForReconstructingPV) { + LOG(info) << "Not enough primary tracks (" << recoPrimaries.size() << ") to compute vertex, skipping vertexing and collision population."; + return; } + computeVertex(mcCollision, recoPrimaries, primaryVertex); + getHist(histPath + "hPVz")->Fill(primaryVertex.getZ()); + // populate collisions + tableCollisions(-1, // BC is irrelevant in synthetic MC tests for now, could be adjusted in future + primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), + primaryVertex.getSigmaX2(), primaryVertex.getSigmaXY(), primaryVertex.getSigmaY2(), + primaryVertex.getSigmaXZ(), primaryVertex.getSigmaYZ(), primaryVertex.getSigmaZ2(), + 0, primaryVertex.getChi2(), primaryVertex.getNContributors(), + eventCollisionTimeNS, 0.f); // For the moment the event collision time is taken as the "GEANT" time, the computation of the event time is done a posteriori from the tracks in the OTF TOF PID task + tableMcCollisionLabels(mcCollision.globalIndex(), 0); + tableCollisionsAlice3(dNdEta); + tableOTFLUTConfigId(icfg); // Populate OTF LUT configuration ID - // populate V0s - LOG(debug) << "Populating " << v0sAlice3.size() << " V0s."; - for (const auto& v0 : v0sAlice3) { - tableUpgradeV0s(tableCollisions.lastIndex(), // now we know the collision index -> populate table - v0.mcParticleId, - v0.positiveId, - v0.negativeId, - v0.dcaV0dau, - v0.v0radius, - v0.mLambda, - v0.mAntiLambda, - v0.mK0, - v0.pt); + // Study V0s and cascades + int trackTableOffset = tableStoredTracks.lastIndex(); + std::vector tracksCascadeProngs; + for (size_t iCasc = 0; iCasc < genCascades.size(); iCasc++) { + auto genCasc = mcParticles.rawIteratorAt(genCascades[iCasc] - mcParticles.offset()); + studyCascade(trackTableOffset, genCasc, tracksCascadeProngs, primaryVertex, dNdEta, eventCollisionTimeNS); + fillTracksInfo(tracksCascadeProngs, primaryVertex); + trackTableOffset += tracksCascadeProngs.size(); // each cascade takes 4 tracks in the table (cascade + 3 daughters) + tracksCascadeProngs.clear(); + } + + std::vector tracksV0Daugs; + for (size_t iV0 = 0; iV0 < genV0s.size(); iV0++) { + auto genV0 = mcParticles.rawIteratorAt(genV0s[iV0] - mcParticles.offset()); + studyV0(trackTableOffset, genV0, tracksV0Daugs, dNdEta, eventCollisionTimeNS); + fillTracksInfo(tracksV0Daugs, primaryVertex); + trackTableOffset += tracksV0Daugs.size(); // each V0 takes 2 tracks in the table (2 daughters) + tracksV0Daugs.clear(); } + // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* + // populate tracks + fillTracksInfo(recoPrimaries, primaryVertex); + fillTracksInfo(ghostPrimaries, primaryVertex); + // do bookkeeping of fastTracker tracking if (enableSecondarySmearing) { - histos.fill(HIST("hCovMatOK"), 0.0f, fastTracker[icfg]->GetCovMatNotOK()); - histos.fill(HIST("hCovMatOK"), 1.0f, fastTracker[icfg]->GetCovMatOK()); + histos.fill(HIST("hCovMatOK"), 0.0f, fastTracker[icfg]->getCovMatNotOK()); + histos.fill(HIST("hCovMatOK"), 1.0f, fastTracker[icfg]->getCovMatOK()); + } + if (doExtraQA) { + histos.fill(HIST("hRecoVsSimMultiplicity"), multiplicityCounter, recoPrimaries.size()); + getHist(histPath + "hSimMultiplicity")->Fill(multiplicityCounter); + getHist(histPath + "hRecoMultiplicity")->Fill(recoPrimaries.size()); } + LOG(debug) << " <- Finished processing OTF tracking with LUT configuration ID " << icfg; } // end process void processOnTheFly(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) { - for (size_t icfg = 0; icfg < mSmearer.size(); ++icfg) { + for (size_t igeo = 0; igeo < mSmearer.size(); ++igeo) { LOG(debug) << " -> Processing OTF tracking with LUT configuration ID " << icfg; - processWithLUTs(mcCollision, mcParticles, static_cast(icfg)); + icfg = igeo; + processWithLUTs(mcCollision, mcParticles); } } - void processConfigurationDev(aod::McCollision const& mcCollision, aod::McPartWithDaus const& mcParticles, const int icfg) + template + void processConfigurationDev(aod::McCollision const& mcCollision, TMcParticles const& mcParticles) { - // const int lastTrackIndex = tableStoredTracksCov.lastIndex() + 1; // bookkeep the last added track const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; tracksAlice3.clear(); ghostTracksAlice3.clear(); @@ -1882,52 +2125,29 @@ struct OnTheFlyTracker { cascadesAlice3.clear(); v0sAlice3.clear(); - o2::dataformats::DCA dcaInfo; - o2::dataformats::VertexBase vtx; - // generate collision time auto ir = irSampler.generateCollisionTime(); const float eventCollisionTimeNS = ir.timeInBCNS; // First we compute the number of charged particles in the event - dNdEta = 0.f; - for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.eta()) > multEtaRange) { - continue; - } + float dNdEta{0.f}; + computeDNDEta(dNdEta, mcParticles, histPath); - if (!mcParticle.isPhysicalPrimary()) { - continue; - } - - const auto pdg = std::abs(mcParticle.pdgCode()); - const bool longLivedToBeHandled = std::find(longLivedHandledPDGs.begin(), longLivedHandledPDGs.end(), pdg) != longLivedHandledPDGs.end(); - const bool nucleiToBeHandled = std::find(nucleiPDGs.begin(), nucleiPDGs.end(), pdg) != nucleiPDGs.end(); - const bool pdgsToBeHandled = longLivedToBeHandled || (enableNucleiSmearing && nucleiToBeHandled); - if (!pdgsToBeHandled) { - continue; - } - - const auto& pdgInfo = pdgDB->GetParticle(mcParticle.pdgCode()); - if (!pdgInfo) { - LOG(warning) << "PDG code " << mcParticle.pdgCode() << " not found in the database"; - continue; - } - if (pdgInfo->Charge() == 0) { - continue; - } - dNdEta += 1.f; - } - - dNdEta /= (multEtaRange * 2.0f); uint32_t multiplicityCounter = 0; - getHist(TH1, histPath + "hLUTMultiplicity")->Fill(dNdEta); - // Now that the multiplicity is known, we can process the particles to smear them for (const auto& mcParticle : mcParticles) { - const bool longLivedToBeHandled = std::find(longLivedHandledPDGs.begin(), longLivedHandledPDGs.end(), std::abs(mcParticle.pdgCode())) != longLivedHandledPDGs.end(); - const bool nucleiToBeHandled = std::find(nucleiPDGs.begin(), nucleiPDGs.end(), std::abs(mcParticle.pdgCode())) != nucleiPDGs.end(); - const bool pdgsToBeHandled = longLivedToBeHandled || (enableNucleiSmearing && nucleiToBeHandled); + const bool longLivedToBeHandled = std::find(LongLivedHandledPDGs.begin(), LongLivedHandledPDGs.end(), std::abs(mcParticle.pdgCode())) != LongLivedHandledPDGs.end(); + const bool shortLivedToBeHandled = std::find(ShortLivedHandledPDGs.begin(), ShortLivedHandledPDGs.end(), std::abs(mcParticle.pdgCode())) != ShortLivedHandledPDGs.end(); + const bool nucleiToBeHandled = std::find(NucleiPDGs.begin(), NucleiPDGs.end(), std::abs(mcParticle.pdgCode())) != NucleiPDGs.end(); + const bool pdgsToBeHandled = longLivedToBeHandled || (enableNucleiSmearing && nucleiToBeHandled) || + (shortLivedToBeHandled && fastPrimaryTrackerSettings.fastTrackShortLivedParticles); + + o2::upgrade::OTFParticle otfParticle(mcParticle); + otfParticle.setBits(mcParticle.decayerBits_raw()); + if (otfParticle.hasNaN()) { + continue; + } + if (!pdgsToBeHandled) { continue; } @@ -1941,51 +2161,64 @@ struct OnTheFlyTracker { } if (enablePrimarySmearing) { - getHist(TH1, histPath + "hPtGenerated")->Fill(mcParticle.pt()); - getHist(TH1, histPath + "hPhiGenerated")->Fill(mcParticle.phi()); + getHist(histPath + "hPtGenerated")->Fill(mcParticle.pt()); + getHist(histPath + "hPhiGenerated")->Fill(mcParticle.phi()); switch (std::abs(mcParticle.pdgCode())) { case kElectron: - getHist(TH1, histPath + "hPtGeneratedEl")->Fill(mcParticle.pt()); + getHist(histPath + "hPtGeneratedEl")->Fill(mcParticle.pt()); break; case kPiPlus: - getHist(TH1, histPath + "hPtGeneratedPi")->Fill(mcParticle.pt()); + getHist(histPath + "hPtGeneratedPi")->Fill(mcParticle.pt()); break; case kKPlus: - getHist(TH1, histPath + "hPtGeneratedKa")->Fill(mcParticle.pt()); + getHist(histPath + "hPtGeneratedKa")->Fill(mcParticle.pt()); break; case kProton: - getHist(TH1, histPath + "hPtGeneratedPr")->Fill(mcParticle.pt()); + getHist(histPath + "hPtGeneratedPr")->Fill(mcParticle.pt()); break; } } multiplicityCounter++; o2::track::TrackParCov trackParCov; - const bool isDecayDaughter = (mcParticle.getProcess() == TMCProcess::kPDecay); - const float timeResolutionNs = 100.f; // ns - const float nsToMus = 1e-3f; - const float timeResolutionUs = timeResolutionNs * nsToMus; // us - const float time = (eventCollisionTimeNS + gRandom->Gaus(0., timeResolutionNs)) * nsToMus; + const float time = (eventCollisionTimeNS + gRandom->Gaus(0., TimeResolutionNs)) * NsToMus; bool reconstructed = false; int nTrkHits = 0; - if (enablePrimarySmearing && mcParticle.isPrimary()) { + std::array hitRadii = {-1.f, -1.f}; // {innermost, outermost} + const bool isSecondary = !otfParticle.isPrimary() && otfParticle.checkBit(o2::upgrade::DecayerBits::ProducedByDecayer) && otfParticle.isAlive(); + if (enablePrimarySmearing && longLivedToBeHandled && otfParticle.isPrimary()) { o2::upgrade::convertMCParticleToO2Track(mcParticle, trackParCov, pdgDB); + computeBremsstrahlungLoss(mcParticle, trackParCov); reconstructed = mSmearer[icfg]->smearTrack(trackParCov, mcParticle.pdgCode(), dNdEta); - nTrkHits = fastTrackerSettings.minSiliconHits; - } else if (enableSecondarySmearing) { - o2::track::TrackParCov perfectTrackParCov; - o2::upgrade::convertMCParticleToO2Track(mcParticle, perfectTrackParCov, pdgDB); + } else if (shortLivedToBeHandled && fastPrimaryTrackerSettings.fastTrackShortLivedParticles) { + o2::track::TrackParCov perfectTrackParCov = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdgDB); perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); - nTrkHits = fastTracker[icfg]->FastTrack(perfectTrackParCov, trackParCov, dNdEta); - if (nTrkHits < fastTrackerSettings.minSiliconHits) { + computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); + nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta, mcParticle.decayRadius()); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; + getHist(histPath + "h2dGenShortLivedParticleRadius")->Fill(mcParticle.decayRadius(), perfectTrackParCov.getPt()); + getHist(histPath + "h2dGenRadiusIniVsDecay")->Fill(std::hypot(perfectTrackParCov.getX(), perfectTrackParCov.getY()), mcParticle.decayRadius()); + if (nTrkHits < fastPrimaryTrackerSettings.minSiliconHits) { reconstructed = false; } else { reconstructed = true; + getHist(histPath + "h2dRecShortLivedParticleRadius")->Fill(mcParticle.decayRadius(), perfectTrackParCov.getPt()); + getHist(histPath + "h2dRecRadiusIniVsDecay")->Fill(std::hypot(perfectTrackParCov.getX(), perfectTrackParCov.getY()), mcParticle.decayRadius()); + getHist(histPath + "h2dDecayRadiusVsNhits")->Fill(mcParticle.decayRadius(), nTrkHits); + LOG(info) << mcParticle.decayRadius(); } + } else if (enableSecondarySmearing && isSecondary) { + o2::track::TrackParCov perfectTrackParCov; + o2::upgrade::convertMCParticleToO2Track(mcParticle, perfectTrackParCov, pdgDB); + computeBremsstrahlungLoss(mcParticle, perfectTrackParCov); + perfectTrackParCov.setPID(pdgCodeToPID(mcParticle.pdgCode())); + nTrkHits = fastTracker[icfg]->fastTrack(perfectTrackParCov, trackParCov, dNdEta); + hitRadii = {fastTracker[icfg]->getTrackIU(), fastTracker[icfg]->getTrackOU()}; + reconstructed = nTrkHits >= fastTrackerSettings.minSiliconHits; } - if (!reconstructed && processUnreconstructedTracks) { + if (!reconstructed && !processUnreconstructedTracks) { continue; // failed to reconstruct track } @@ -1996,34 +2229,70 @@ struct OnTheFlyTracker { histos.fill(HIST("hNaNBookkeeping"), 0.0f, 1.0f); if (enablePrimarySmearing) { - getHist(TH1, histPath + "hPtReconstructed")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kElectron) - getHist(TH1, histPath + "hPtReconstructedEl")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kPiPlus) - getHist(TH1, histPath + "hPtReconstructedPi")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kKPlus) - getHist(TH1, histPath + "hPtReconstructedKa")->Fill(trackParCov.getPt()); - if (std::abs(mcParticle.pdgCode()) == kProton) - getHist(TH1, histPath + "hPtReconstructedPr")->Fill(trackParCov.getPt()); + const float ptResolution = (trackParCov.getPt() - mcParticle.pt()) / trackParCov.getPt(); + getHist(histPath + "hPtReconstructed")->Fill(trackParCov.getPt()); + if (otfParticle.isPrimary()) { + getHist(histPath + "h2dPrimaryPtRes")->Fill(trackParCov.getPt(), ptResolution); + } else { + getHist(histPath + "h2dSecondaryPtRes")->Fill(trackParCov.getPt(), ptResolution); + } + if (std::abs(mcParticle.pdgCode()) == kElectron) { + getHist(histPath + "hPtReconstructedEl")->Fill(trackParCov.getPt()); + if (otfParticle.isPrimary()) { + getHist(histPath + "h2dPrimaryElPtRes")->Fill(trackParCov.getPt(), ptResolution); + } else { + getHist(histPath + "h2dSecondaryElPtRes")->Fill(trackParCov.getPt(), ptResolution); + } + } + if (std::abs(mcParticle.pdgCode()) == kPiPlus) { + getHist(histPath + "hPtReconstructedPi")->Fill(trackParCov.getPt()); + if (otfParticle.isPrimary()) { + getHist(histPath + "h2dPrimaryPiPtRes")->Fill(trackParCov.getPt(), ptResolution); + } else { + getHist(histPath + "h2dSecondaryPiPtRes")->Fill(trackParCov.getPt(), ptResolution); + } + } + if (std::abs(mcParticle.pdgCode()) == kKPlus) { + getHist(histPath + "hPtReconstructedKa")->Fill(trackParCov.getPt()); + if (otfParticle.isPrimary()) { + getHist(histPath + "h2dPrimaryKaPtRes")->Fill(trackParCov.getPt(), ptResolution); + } else { + getHist(histPath + "h2dSecondaryKaPtRes")->Fill(trackParCov.getPt(), ptResolution); + } + } + if (std::abs(mcParticle.pdgCode()) == kProton) { + getHist(histPath + "hPtReconstructedPr")->Fill(trackParCov.getPt()); + if (otfParticle.isPrimary()) { + getHist(histPath + "h2dPrimaryPrPtRes")->Fill(trackParCov.getPt(), ptResolution); + } else { + getHist(histPath + "h2dSecondaryPrPtRes")->Fill(trackParCov.getPt(), ptResolution); + } + } } if (reconstructed) { - tracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, timeResolutionUs, isDecayDaughter, false, 0, nTrkHits}); + tracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, TimeResolutionUs, isSecondary, true, false, 0, nTrkHits, kRecoPrimary}); + tracksAlice3.back().trackIU = hitRadii[0]; + tracksAlice3.back().trackOU = hitRadii[1]; + getHist(histPath + "hPtReconstructedPr")->Fill(trackParCov.getPt()); } else { - ghostTracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, timeResolutionUs, isDecayDaughter}); + ghostTracksAlice3.push_back(TrackAlice3{trackParCov, mcParticle.globalIndex(), time, TimeResolutionUs, isSecondary, false, false, false, 0, nTrkHits, kGhostPrimary}); + ghostTracksAlice3.back().trackIU = hitRadii[0]; + ghostTracksAlice3.back().trackOU = hitRadii[1]; } } o2::vertexing::PVertex primaryVertex; - if (enablePrimaryVertexing) { // disabled for now - primaryVertex.setXYZ(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); - } else { - primaryVertex.setXYZ(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + static constexpr int MinTracksForReconstructingPV = 2; + if (enablePrimaryVertexing && recoPrimaries.size() <= MinTracksForReconstructingPV) { + LOG(info) << "Not enough primary tracks (" << recoPrimaries.size() << ") to compute vertex, skipping vertexing and collision population."; + return; } + computeVertex(mcCollision, tracksAlice3, primaryVertex); - getHist(TH1, histPath + "hSimMultiplicity")->Fill(multiplicityCounter); - getHist(TH1, histPath + "hRecoMultiplicity")->Fill(tracksAlice3.size()); - getHist(TH1, histPath + "hPVz")->Fill(primaryVertex.getZ()); + getHist(histPath + "hSimMultiplicity")->Fill(multiplicityCounter); + getHist(histPath + "hRecoMultiplicity")->Fill(tracksAlice3.size()); + getHist(histPath + "hPVz")->Fill(primaryVertex.getZ()); // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* // populate collisions @@ -2039,101 +2308,16 @@ struct OnTheFlyTracker { // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* // populate tracks - for (const auto& trackParCov : tracksAlice3) { - aod::track::TrackTypeEnum trackType = aod::track::Track; - - if (populateTracksDCA) { - float dcaXY = 1e+10, dcaZ = 1e+10; - o2::track::TrackParCov trackParametrization(trackParCov); - if (trackParametrization.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { - dcaXY = dcaInfo.getY(); - dcaZ = dcaInfo.getZ(); - } - - tableTracksDCA(dcaXY, dcaZ); - if (populateTracksDCACov) { - tableTracksDCACov(dcaInfo.getSigmaY2(), dcaInfo.getSigmaZ2()); - } - } - - tableStoredTracks(tableCollisions.lastIndex(), trackType, trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt()); - tableTracksExtension(trackParCov.getPt(), trackParCov.getP(), trackParCov.getEta(), trackParCov.getPhi()); - - // TODO do we keep the rho as 0? Also the sigma's are duplicated information - tableStoredTracksCov(std::sqrt(trackParCov.getSigmaY2()), std::sqrt(trackParCov.getSigmaZ2()), std::sqrt(trackParCov.getSigmaSnp2()), - std::sqrt(trackParCov.getSigmaTgl2()), std::sqrt(trackParCov.getSigma1Pt2()), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - tableTracksCovExtension(trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), trackParCov.getSigmaSnpY(), - trackParCov.getSigmaSnpZ(), trackParCov.getSigmaSnp2(), trackParCov.getSigmaTglY(), trackParCov.getSigmaTglZ(), trackParCov.getSigmaTglSnp(), - trackParCov.getSigmaTgl2(), trackParCov.getSigma1PtY(), trackParCov.getSigma1PtZ(), trackParCov.getSigma1PtSnp(), trackParCov.getSigma1PtTgl(), - trackParCov.getSigma1Pt2()); - tableMcTrackWithDauLabels(trackParCov.mcLabel, 0); - tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits); - - // populate extra tables if required to do so - if (populateTracksExtra) { - tableStoredTracksExtra(0.0f, static_cast(0), static_cast(0), static_cast(0), static_cast(0), - static_cast(0), static_cast(0), static_cast(0), static_cast(0), - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - } - if (populateTrackSelection) { - tableTrackSelection(static_cast(0), false, false, false, false, false, false); - tableTrackSelectionExtension(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false); - } - tableTracksAlice3(true); - } - - // *+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+*+~+* - // populate ghost tracks - for (const auto& trackParCov : ghostTracksAlice3) { - aod::track::TrackTypeEnum trackType = aod::track::Track; - - if (populateTracksDCA) { - float dcaXY = 1e+10, dcaZ = 1e+10; - o2::track::TrackParCov trackParametrization(trackParCov); - if (trackParametrization.propagateToDCA(primaryVertex, mMagneticField, &dcaInfo)) { - dcaXY = dcaInfo.getY(); - dcaZ = dcaInfo.getZ(); - } - - tableTracksDCA(dcaXY, dcaZ); - if (populateTracksDCACov) { - tableTracksDCACov(dcaInfo.getSigmaY2(), dcaInfo.getSigmaZ2()); - } - } - - tableStoredTracks(tableCollisions.lastIndex(), trackType, trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt()); - tableTracksExtension(trackParCov.getPt(), trackParCov.getP(), trackParCov.getEta(), trackParCov.getPhi()); - - // TODO do we keep the rho as 0? Also the sigma's are duplicated information - tableStoredTracksCov(std::sqrt(trackParCov.getSigmaY2()), std::sqrt(trackParCov.getSigmaZ2()), std::sqrt(trackParCov.getSigmaSnp2()), - std::sqrt(trackParCov.getSigmaTgl2()), std::sqrt(trackParCov.getSigma1Pt2()), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - tableTracksCovExtension(trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), trackParCov.getSigmaSnpY(), - trackParCov.getSigmaSnpZ(), trackParCov.getSigmaSnp2(), trackParCov.getSigmaTglY(), trackParCov.getSigmaTglZ(), trackParCov.getSigmaTglSnp(), - trackParCov.getSigmaTgl2(), trackParCov.getSigma1PtY(), trackParCov.getSigma1PtZ(), trackParCov.getSigma1PtSnp(), trackParCov.getSigma1PtTgl(), - trackParCov.getSigma1Pt2()); - tableMcTrackWithDauLabels(trackParCov.mcLabel, 0); - tableTracksExtraA3(trackParCov.nSiliconHits, trackParCov.nTPCHits); - - // populate extra tables if required to do so - if (populateTracksExtra) { - tableStoredTracksExtra(0.0f, static_cast(0), static_cast(0), static_cast(0), static_cast(0), - static_cast(0), static_cast(0), static_cast(0), static_cast(0), - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - } - if (populateTrackSelection) { - tableTrackSelection(static_cast(0), false, false, false, false, false, false); - tableTrackSelectionExtension(false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false); - } - tableTracksAlice3(false); - } + fillTracksInfo(tracksAlice3, primaryVertex); + fillTracksInfo(ghostTracksAlice3, primaryVertex); } - void processDecayer(aod::McCollision const& mcCollision, aod::McPartWithDaus const& mcParticles) + void processDecayer(aod::McCollision const& mcCollision, soa::Join const& mcParticles) { - for (size_t icfg = 0; icfg < mSmearer.size(); ++icfg) { - processConfigurationDev(mcCollision, mcParticles, static_cast(icfg)); + for (size_t igeo = 0; igeo < mSmearer.size(); ++igeo) { + LOG(debug) << " -> Processing OTF tracking with LUT configuration ID " << igeo; + icfg = igeo; + processConfigurationDev(mcCollision, mcParticles); } } @@ -2142,7 +2326,7 @@ struct OnTheFlyTracker { }; /// Extends TracksExtra if necessary -struct onTheFlyTrackerInitializer { +struct OnTheFlyTrackerInitializer { Spawns tableStoredTracksExtra; void init(InitContext const&) {} }; @@ -2151,5 +2335,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx index fdb0e72afe2..51a8dcb95db 100644 --- a/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx +++ b/ALICE3/TableProducer/OTF/onTheFlyTrackerPid.cxx @@ -11,23 +11,20 @@ /// /// \file onTheFlyTrackerPid.cxx /// -/// \brief This task produces the PID information that can be obtained from the tracker layers (i.e. ToT and possibly cluster size). -/// So far only ToT implemented. It currently contemplates 5 (9) particle types: electrons, muons, pions, kaons and -/// protons (as well as deuterons, tritons, helium-3 and alphas/helium-4 if added via the event generator). +/// \brief This task simulates the PID signal that can be obtained from the tracker layers as ToT (Time-over-Threshold). +/// The dE/dx of every track is sampled from a per-track LUT made from the full simulation. +/// It contemplates 9 particle types: electrons, muons, pions, kaons, protons, deuterons, tritons, +/// helium-3 and alphas/helium-4. /// /// \author Henrik Fribert TUM /// \author Nicolò Jacazio Università del Piemonte Orientale /// \since May 22, 2025 /// -#include "GeometryContainer.h" - -#include "ALICE3/Core/TrackUtilities.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include #include -#include #include #include #include @@ -38,665 +35,402 @@ #include #include #include -#include -#include -#include -#include #include #include #include +#include +#include #include #include #include #include #include -#include +#include #include +#include #include #include #include #include -#include #include using namespace o2; using namespace o2::framework; -static constexpr std::array kTrackerRadii = {0.5f, 1.2f, 2.5f, 3.75f, 7.0f, 12.0f, 20.0f, 30.0f, 45.0f, 60.0f, 80.0f}; - -// Constants for magic numbers -static constexpr int kMinEntriesForProjection = 10; -static constexpr double kMinMomentumForLogBins = 0.05; -static constexpr double kMaxMomentumForLogBins = 10.0; -static constexpr int kMinLayerForTruncation = 3; - -// Constants for validation conditions -static constexpr size_t kMinValidHits = 1; -static constexpr size_t kLowValidHits = 2; -static constexpr size_t kMidLowValidHits = 3; -static constexpr size_t kMidHighValidHits = 4; -static constexpr size_t kHighValidHits1 = 5; -static constexpr size_t kHighValidHits2 = 6; -static constexpr size_t kMaxValidHits = 7; - -// Constants for truncated mean calculation -static constexpr size_t kMaxValidHitsForTruncation7Plus = 4; -static constexpr size_t kMaxValidHitsForTruncation56 = 3; -static constexpr size_t kMaxValidHitsForTruncation34 = 2; -static constexpr size_t kMaxValidHitsForTruncation12 = 1; - -// Constants for LUT binning -// To do: Include in LUT header or similar -static constexpr int kLUTEtaBins = 50; -static constexpr float kLUTEtaMin = -2.5f; -static constexpr float kLUTEtaMax = 2.5f; -static constexpr int kLUTPtBins = 500; -static constexpr float kLUTPtMin = 0.0f; -static constexpr float kLUTPtMax = 10.0f; - -class ToTLUT +static constexpr double MinMomentumForLogBins = 0.05; +static constexpr double MaxMomentumForLogBins = 10.0; + +/// Local path of a LUT file; "ccdb:" is downloaded once to /tmp/ToTLUTs//snapshot.root +std::string getLocalLutFile(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdbManager) { - public: - explicit ToTLUT(int maxLayers, float analysisEtaMinVal = 0.0f, float analysisEtaMaxVal = 1.0f, float analysisPtMinVal = 0.0f, float analysisPtMaxVal = 10.0f) - : mMaxLayers(maxLayers), - mAnalysisEtaMin(analysisEtaMinVal), - mAnalysisEtaMax(analysisEtaMaxVal), - mAnalysisPtMin(analysisPtMinVal), - mAnalysisPtMax(analysisPtMaxVal), - mEtaBins(kLUTEtaBins), - mEtaMin(kLUTEtaMin), - mEtaMax(kLUTEtaMax), - mPtBins(kLUTPtBins), - mPtMin(kLUTPtMin), - mPtMax(kLUTPtMax), - mEtaBinWidth((kLUTEtaMax - kLUTEtaMin) / kLUTEtaBins), - mPtBinWidth((kLUTPtMax - kLUTPtMin) / kLUTPtBins) - { - mPdgToIndexMap.reserve(10); - mIndexToPdgMap.reserve(10); + if (!filename.starts_with("ccdb:")) { + return filename; } - ToTLUT() = delete; - - ~ToTLUT() - { - for (const auto& hist_ptr : mLUTHistogramFlat) { - if (hist_ptr) { - delete hist_ptr; - } + const std::string ccdbPath = filename.substr(5); + const std::string outPath = "/tmp/ToTLUTs/"; + const std::string localFilename = outPath + ccdbPath + "/snapshot.root"; + if (!std::ifstream(localFilename).is_open()) { + if (!ccdbManager) { + LOG(fatal) << "CCDB manager not set. Please set it before loading LUT from CCDB."; + } + std::map metadata; + LOG(info) << "Retrieving " << localFilename << " from CCDB path: " << ccdbPath; + ccdbManager->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); + if (!std::ifstream(localFilename).is_open()) { + LOG(fatal) << "Could not find downloaded CCDB file " << localFilename; } } + return localFilename; +} - void setCcdbManager(o2::ccdb::BasicCCDBManager* mgr) { mCcdbManager = mgr; } - - bool load(int pdg, const std::string& filename) +/// Per-track dE/dx LUT; for every species, |eta| bin and rigidity bin the +/// distribution of the track dE/dx in e-, as a histogram. The dE/dx of a track is sampled from it, +/// interpolated linearly in log(rigidity) between the rigidity bin centres with the same random number. +class TrackDedxLut +{ + public: + template + bool load(const std::string& filename, o2::ccdb::BasicCCDBManager* ccdbManager, const std::array& pdgCodes) { - if (filename.empty()) { - LOG(warning) << "Provided filename is empty for PDG " << pdg; + std::unique_ptr f(TFile::Open(getLocalLutFile(filename, ccdbManager).c_str())); + if (!f || f->IsZombie()) { + LOG(fatal) << "Failed to open dE/dx LUT file: " << filename; return false; } - if (filename.rfind("ccdb:", 0) == 0) { // Check if filename starts with "ccdb:" - std::string ccdbPath = filename.substr(5); // remove "ccdb:" prefix - const std::string outPath = "/tmp/ToTLUTs/"; - const std::string localFilename = outPath + ccdbPath + "/snapshot.root"; - std::ifstream checkFile(localFilename); - if (!checkFile.is_open()) { // File is not found, need to download it from CCDB - if (!mCcdbManager) { - LOG(fatal) << "CCDB manager not set. Please set it before loading LUT from CCDB."; - } - std::map metadata; - LOG(info) << "Retrieving " << localFilename << " from CCDB path: " << ccdbPath; - mCcdbManager->getCCDBAccessor().retrieveBlob(ccdbPath, outPath, metadata, 1); - std::ifstream testFile(localFilename); - if (!testFile.is_open()) { - LOG(fatal) << "Could not find downloaded CCDB file for PDG " << pdg << " in file " << localFilename; - return false; - } - testFile.close(); - return load(pdg, localFilename); - } else { // File is found, proceed to load it - checkFile.close(); - return load(pdg, localFilename); - } - } - // In case the file is already available locally - TFile* f = TFile::Open(filename.c_str()); - if (!f || f->IsZombie()) { - LOG(fatal) << "Failed to open LUT file: " << filename; + const auto* etaBins = f->Get("etaBins"); + const auto* rigBins = f->Get("rigBins"); + const auto* dedxBins = f->Get("dedxBins"); + if (!etaBins || !rigBins || !dedxBins) { + LOG(fatal) << "No etaBins/rigBins/dedxBins in dE/dx LUT file: " << filename; return false; } - - int currentPdgIdx; - auto it = mPdgToIndexMap.find(pdg); - if (it == mPdgToIndexMap.end()) { - currentPdgIdx = mIndexToPdgMap.size(); - mPdgToIndexMap[pdg] = currentPdgIdx; - mIndexToPdgMap.push_back(pdg); - - size_t totalSize = (currentPdgIdx + 1) * mMaxLayers * mEtaBins * mPtBins; - if (mLUTHistogramFlat.size() < totalSize) { - mLUTHistogramFlat.resize(totalSize, nullptr); - } - } else { - currentPdgIdx = it->second; + mEtaEdges = binEdges(etaBins); + mRigEdges = binEdges(rigBins); + mDedxEdges = binEdges(dedxBins); + mLogRigCentres.clear(); + for (size_t i = 0; i + 1 < mRigEdges.size(); ++i) { + mLogRigCentres.push_back(0.5 * (std::log(mRigEdges[i]) + std::log(mRigEdges[i + 1]))); } - - bool success = true; - for (int layer = 0; layer < mMaxLayers; ++layer) { - for (int etaBin = 0; etaBin < mEtaBins; ++etaBin) { - float etaMinBin = mEtaMin + etaBin * mEtaBinWidth; - float etaMaxBin = etaMinBin + mEtaBinWidth; - - float etaCenter = (etaMinBin + etaMaxBin) / 2.0f; - if (std::abs(etaCenter) < mAnalysisEtaMin || std::abs(etaCenter) > mAnalysisEtaMax) { + const size_t nBins = (mEtaEdges.size() - 1) * (mRigEdges.size() - 1); + const size_t nDedx = mDedxEdges.size() - 1; + + bool allLoaded = true; + for (const auto& pdg : pdgCodes) { + std::unique_ptr> h(f->Get>(Form("h_%d", pdg))); + if (!h || h->size() != nBins * nDedx) { + LOG(warning) << "No dE/dx LUT for PDG " << pdg << " in " << filename; + allLoaded = false; + continue; + } + // cumulative distributions and raw moments, only for the filled bins + auto& offset = mOffset[pdg]; + auto& cdf = mCdf[pdg]; + auto& mean = mMean[pdg]; + auto& sigma = mSigma[pdg]; + offset.assign(nBins, -1); + mean.assign(nBins, -1.f); + sigma.assign(nBins, -1.f); + for (size_t b = 0; b < nBins; ++b) { + const float* hb = h->data() + b * nDedx; + double sum = 0., m1 = 0., m2 = 0.; + for (size_t k = 0; k < nDedx; ++k) { + const double x = 0.5 * (mDedxEdges[k] + mDedxEdges[k + 1]); + sum += hb[k]; + m1 += hb[k] * x; + m2 += hb[k] * x * x; + } + if (sum <= 0.) { continue; } - - for (int ptBin = 0; ptBin < mPtBins; ++ptBin) { - float ptMinBin = mPtMin + ptBin * mPtBinWidth; - float ptMaxBin = ptMinBin + mPtBinWidth; - float ptCenter = (ptMinBin + ptMaxBin) / 2.0f; - - if (ptCenter < mAnalysisPtMin || ptCenter >= mAnalysisPtMax) { - continue; - } - - TString histName = Form("tot_%d_barrel%d_eta%.2f-%.2f_pt%.2f-%.2f", pdg, layer, etaMinBin, etaMaxBin, ptMinBin, ptMaxBin); - - TH1F* histFromFile = dynamic_cast(f->Get(histName)); - if (histFromFile) { - TH1F* clonedHist = static_cast(histFromFile->Clone()); - clonedHist->SetDirectory(nullptr); - - size_t flatIdx = getFlatIndex(currentPdgIdx, layer, etaBin, ptBin); - mLUTHistogramFlat[flatIdx] = clonedHist; - } else { - size_t flatIdx = getFlatIndex(currentPdgIdx, layer, etaBin, ptBin); - mLUTHistogramFlat[flatIdx] = nullptr; - success = false; - } + offset[b] = cdf.size(); + double cum = 0.; + for (size_t k = 0; k < nDedx; ++k) { + cum += hb[k]; + cdf.push_back(cum / sum); } + const double mu = m1 / sum; + mean[b] = mu; + sigma[b] = std::sqrt(std::max(0., m2 / sum - mu * mu)); } } - - f->Close(); - delete f; - return success; + LOG(info) << "Loaded dE/dx LUT " << filename << " (" << mEtaEdges.size() - 1 << " |eta| x " << mRigEdges.size() - 1 + << " rigidity x " << mDedxEdges.size() - 1 << " dE/dx bins)"; + return allLoaded; } - TH1F* getHistogramForSampling(int pdgIdx, int layer, int etaBin, int ptBin) const + /// Sampled dE/dx for uniform random number u, or -1 if there is no LUT entry + float sample(int pdg, float eta, float rigidity, double u) const { - if (pdgIdx < 0 || static_cast(pdgIdx) >= getNumPdgTypes() || - layer < 0 || layer >= mMaxLayers || - etaBin < 0 || etaBin >= mEtaBins || ptBin < 0 || ptBin >= mPtBins) { - return nullptr; + int bin = -1, binNext = -1; + float weight = 0.f; + if (!locate(pdg, eta, rigidity, bin, binNext, weight)) { + return -1.f; } - size_t flatIdx = getFlatIndex(pdgIdx, layer, etaBin, ptBin); - return (flatIdx < mLUTHistogramFlat.size()) ? mLUTHistogramFlat[flatIdx] : nullptr; - } - - int getPdgIndex(int pdgCode) const - { - auto it = mPdgToIndexMap.find(pdgCode); - if (it != mPdgToIndexMap.end()) { - return it->second; + const auto& offset = mOffset.at(pdg); + const float* cdf = mCdf.at(pdg).data(); + float dedx = inverseCdf(cdf + offset[bin], u); + if (binNext >= 0) { + dedx += weight * (inverseCdf(cdf + offset[binNext], u) - dedx); } - return -1; + return dedx; } - inline int getEtaBin(float eta) const + bool getMeanSigma(int pdg, float eta, float rigidity, float& mean, float& sigma) const { - const float clampedEta = std::max(mEtaMin, std::min(eta, mEtaMax - 1e-6f)); - return std::min(static_cast((clampedEta - mEtaMin) / mEtaBinWidth), mEtaBins - 1); + int bin = -1, binNext = -1; + float weight = 0.f; + if (!locate(pdg, eta, rigidity, bin, binNext, weight)) { + return false; + } + const auto& m = mMean.at(pdg); + const auto& s = mSigma.at(pdg); + mean = m[bin]; + sigma = s[bin]; + if (binNext >= 0) { + mean += weight * (m[binNext] - mean); + sigma += weight * (s[binNext] - sigma); + } + return sigma > 0.f; } - inline int getPtBin(float pt) const + private: + /// LUT bin of the track and the neighbouring rigidity bin on its side (-1 if empty) + bool locate(int pdg, float eta, float rigidity, int& bin, int& binNext, float& weight) const { - const float clampedPt = std::max(mPtMin, std::min(pt, mPtMax - 1e-6f)); - return std::min(static_cast((clampedPt - mPtMin) / mPtBinWidth), mPtBins - 1); + const auto it = mOffset.find(pdg); + const float absEta = std::abs(eta); + if (it == mOffset.end() || mEtaEdges.empty() || absEta < mEtaEdges.front() || absEta >= mEtaEdges.back() || + rigidity < mRigEdges.front() || rigidity >= mRigEdges.back()) { + return false; + } + const int nRig = mRigEdges.size() - 1; + const int iEta = std::upper_bound(mEtaEdges.begin(), mEtaEdges.end(), absEta) - mEtaEdges.begin() - 1; + const int iRig = std::upper_bound(mRigEdges.begin(), mRigEdges.end(), rigidity) - mRigEdges.begin() - 1; + bin = iEta * nRig + iRig; + if (it->second[bin] < 0) { + return false; + } + binNext = -1; + weight = 0.f; + const double logRig = std::log(rigidity); + const int iNext = logRig < mLogRigCentres[iRig] ? iRig - 1 : iRig + 1; + if (iNext >= 0 && iNext < nRig && it->second[iEta * nRig + iNext] >= 0) { + binNext = iEta * nRig + iNext; + weight = (logRig - mLogRigCentres[iRig]) / (mLogRigCentres[iNext] - mLogRigCentres[iRig]); + } + return true; } - inline size_t getFlatIndex(int pdgIdx, int layer, int etaBin, int ptBin) const + /// dE/dx at which the cumulative distribution reaches u, uniform within the dE/dx bin + float inverseCdf(const float* cdf, double u) const { - return ((pdgIdx * mMaxLayers + layer) * mEtaBins + etaBin) * mPtBins + ptBin; + const int nDedx = mDedxEdges.size() - 1; + const int k = std::min(static_cast(std::upper_bound(cdf, cdf + nDedx, u) - cdf), nDedx - 1); + const double lo = k == 0 ? 0. : cdf[k - 1]; + const double frac = cdf[k] > lo ? (u - lo) / (cdf[k] - lo) : 0.5; + return mDedxEdges[k] + frac * (mDedxEdges[k + 1] - mDedxEdges[k]); } - size_t getNumPdgTypes() const + static std::vector binEdges(const TH1* h) { - return mIndexToPdgMap.size(); + std::vector edges; + for (int i = 1; i <= h->GetNbinsX() + 1; ++i) { + edges.push_back(h->GetXaxis()->GetBinLowEdge(i)); + } + return edges; } - std::vector mLUTHistogramFlat; - - std::unordered_map mPdgToIndexMap; - std::vector mIndexToPdgMap; - - int mMaxLayers; - float mAnalysisEtaMin; - float mAnalysisEtaMax; - float mAnalysisPtMin; - float mAnalysisPtMax; - int mEtaBins; - float mEtaMin; - float mEtaMax; - int mPtBins; - float mPtMin; - float mPtMax; - - float mEtaBinWidth; - float mPtBinWidth; - - private: - o2::ccdb::BasicCCDBManager* mCcdbManager = nullptr; + std::vector mEtaEdges; + std::vector mRigEdges; + std::vector mLogRigCentres; + std::vector mDedxEdges; + std::unordered_map> mOffset; // |pdg| -> start of the cdf of bin etaBin * nRig + rigBin, -1 if empty + std::unordered_map> mCdf; // |pdg| -> cumulative dE/dx distributions of the filled bins + std::unordered_map> mMean; // |pdg| -> mean dE/dx per bin, -1 if empty + std::unordered_map> mSigma; }; -static constexpr int kNumHypothesisParticles = 9; -std::array, kNumHypothesisParticles>, kNumHypothesisParticles> h2dBarrelNsigmaTrue; -std::array, kNumHypothesisParticles> h2dHitsPerTrackVsP; -std::array, kNumHypothesisParticles> h2dToTvsPperParticle; - struct OnTheFlyTrackerPid { - - float calculateNsigma(float measuredToT, float expectedToT, float resolution) - { - if (resolution <= 0) - return 999.f; - return (measuredToT - expectedToT) / resolution; - } - - float getToTMeanFromMomentumSlice(std::shared_ptr hist, float momentum) - { - if (!hist) - return -1.f; - int binX = hist->GetXaxis()->FindBin(momentum); - TH1D* proj = hist->ProjectionY("temp", binX, binX); - if (proj->GetEntries() < kMinEntriesForProjection) { - delete proj; - return -1.f; - } - float mean = proj->GetMean(); - delete proj; - return mean; - } - - float getToTResolutionFromMomentumSlice(std::shared_ptr hist, float momentum) - { - if (!hist) - return -1.f; - int binX = hist->GetXaxis()->FindBin(momentum); - TH1D* proj = hist->ProjectionY("temp", binX, binX); - if (proj->GetEntries() < kMinEntriesForProjection) { - delete proj; - return -1.f; - } - float stddev = proj->GetStdDev(); - delete proj; - return stddev; - } - - float computeTrackLength(o2::track::TrackParCov track, float radius, float magneticField) - { - float length = -100; - o2::math_utils::CircleXYf_t trcCircle; - float sna, csa; - track.getCircleParams(magneticField, trcCircle, sna, csa); - - const float centerDistance = std::hypot(trcCircle.xC, trcCircle.yC); - - if (centerDistance < trcCircle.rC + radius && centerDistance > std::fabs(trcCircle.rC - radius)) { - length = 0.0f; - const float ux = trcCircle.xC / centerDistance; - const float uy = trcCircle.yC / centerDistance; - const float vx = -uy; - const float vy = +ux; - const float radical = (centerDistance * centerDistance - trcCircle.rC * trcCircle.rC + radius * radius) / (2.0f * centerDistance); - const float displace = (0.5f / centerDistance) * std::sqrt( - (-centerDistance + trcCircle.rC - radius) * - (-centerDistance - trcCircle.rC + radius) * - (-centerDistance + trcCircle.rC + radius) * - (centerDistance + trcCircle.rC + radius)); - - const float point1[2] = {radical * ux + displace * vx, radical * uy + displace * vy}; - const float point2[2] = {radical * ux - displace * vx, radical * uy - displace * vy}; - - std::array mom; - track.getPxPyPzGlo(mom); - const float scalarProduct1 = point1[0] * mom[0] + point1[1] * mom[1]; - const float scalarProduct2 = point2[0] * mom[0] + point2[1] * mom[1]; - - std::array startPoint; - track.getXYZGlo(startPoint); - - float cosAngle = -1000, modulus = -1000; - - if (scalarProduct1 > scalarProduct2) { - modulus = std::hypot(point1[0] - trcCircle.xC, point1[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point1[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point1[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } else { - modulus = std::hypot(point2[0] - trcCircle.xC, point2[1] - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); - cosAngle = (point2[0] - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (point2[1] - trcCircle.yC) * (startPoint[1] - trcCircle.yC); - } - cosAngle /= modulus; - length = trcCircle.rC * std::acos(cosAngle); - length *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); - } - return length; - } + static constexpr int NumSpecies = 9; + static constexpr std::array PdgCodes = {11, 13, 211, 321, 2212, 1000010020, 1000010030, 1000020030, 1000020040}; + static constexpr std::array SpeciesNames = {"Elec", "Muon", "Pion", "Kaon", "Prot", "Deut", "Trit", "He3", "Al"}; + static constexpr std::array SpeciesLabels = {"#it{e}", "#it{#mu}", "#it{#pi}", "#it{K}", "#it{p}", "#it{d}", "#it{t}", "#it{^{3}He}", "#it{^{4}He}"}; + static constexpr float NoNsigma = 999.f; + + std::array, NumSpecies> h2dToTvsPperParticle; + std::array, NumSpecies> h2dToTvsPperParticleLin; + std::array, NumSpecies> h3dToTvsPEtaPerParticle; + std::array, NumSpecies> h2dTracks; + std::array, NumSpecies> h2dTracksWithDedx; Produces tableUpgradeTrkPidSignals; Produces tableUpgradeTrkPids; - Service pdg; - Service ccdb; - std::unique_ptr mToTLUT; + Service pdgDatabase{}; + Service ccdb{}; + TrackDedxLut mTrackLut; + TRandom3 mRandom; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable lutTotEl{"lutTotEl", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_11", "ToT LUT for electrons"}; - Configurable lutTotMu{"lutTotMu", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_13", "ToT LUT for muons"}; - Configurable lutTotPi{"lutTotPi", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_211", "ToT LUT for pions"}; - Configurable lutTotKa{"lutTotKa", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_321", "ToT LUT for kaons"}; - Configurable lutTotPr{"lutTotPr", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_2212", "ToT LUT for protons"}; - Configurable lutTotDe{"lutTotDe", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_1000010020", "ToT LUT for deuteron"}; - Configurable lutTotTr{"lutTotTr", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_1000010030", "ToT LUT for triton"}; - Configurable lutTotHe{"lutTotHe", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_1000020030", "ToT LUT for helium-3"}; - Configurable lutTotAl{"lutTotAl", "ccdb:Users/h/hfribert/ToT_LUTs/PDG_1000020040", "ToT LUT for alphas"}; - - Configurable maxBarrelLayers{"maxBarrelLayers", 11, "Maximum number of barrel layers"}; - Configurable numLogBins{"numLogBins", 200, "Number of logarithmic momentum bins"}; - Configurable analysisEtaMin{"analysisEtaMin", 0.0f, "Minimum |eta| for LUT loading optimization"}; - Configurable analysisEtaMax{"analysisEtaMax", 1.0f, "Maximum |eta| for LUT loading optimization"}; - Configurable analysisPtMin{"analysisPtMin", 0.0f, "Minimum pT (GeV/c) for LUT loading optimization"}; - Configurable analysisPtMax{"analysisPtMax", 10.0f, "Maximum pT (GeV/c) for LUT loading optimization"}; - - std::vector mLogBins; - - std::array mHypothesisPdgCodes = { - 11, // Electron - 13, // Muon - 211, // Pion - 321, // Kaon - 2212, // Proton - 1000010020, // Deuteron - 1000010030, // Triton - 1000020030, // Helium-3 - 1000020040 // Alpha - }; - - // Configuration defined at init time - o2::fastsim::GeometryContainer mGeoContainer; - float mMagneticField = 0.0f; - void init(o2::framework::InitContext& initContext) - { - ccdb->setURL("http://alice-ccdb.cern.ch"); - ccdb->setTimestamp(-1); - - mGeoContainer.setCcdbManager(ccdb.operator->()); - mGeoContainer.init(initContext); - mMagneticField = mGeoContainer.getFloatValue(0, "global", "magneticfield"); - - if (static_cast(maxBarrelLayers.value) > kTrackerRadii.size()) { - LOG(fatal) << "Configured maxBarrelLayers (" << maxBarrelLayers.value - << ") exceeds the size of kTrackerRadii (" << kTrackerRadii.size() - << "). Please adjust maxBarrelLayers."; - } + Configurable trackLut{"trackLut", "", "Per-track dE/dx LUT from MakeToTLUT.C (local file or ccdb:)"}; + Configurable seed{"seed", 0, "Seed for the dE/dx sampling (0: different in every job)"}; + Configurable primariesOnly{"primariesOnly", false, "Fill the histograms only with physical primaries (the tables are filled for all tracks)"}; + Configurable useTrueKinematics{"useTrueKinematics", false, "Fill the histograms vs the true instead of the reconstructed p/z and |eta| (the dE/dx is always sampled at the true ones)"}; - mToTLUT = std::make_unique(maxBarrelLayers.value, analysisEtaMin.value, analysisEtaMax.value, analysisPtMin.value, analysisPtMax.value); + // dE/dx axis, logarithmic + Configurable numDedxBins{"numDedxBins", 300, "Number of logarithmic dE/dx bins"}; + Configurable dedxMin{"dedxMin", 1e2f, "Minimum of the dE/dx axis in e-"}; + Configurable dedxMax{"dedxMax", 1e5f, "Maximum of the dE/dx axis in e-"}; - mToTLUT->setCcdbManager(ccdb.operator->()); + ConfigurableAxis axisEta{"axisEta", {VARIABLE_WIDTH, 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f}, "|#eta| binning of the eta-differential histograms"}; - bool loaded = true; - loaded &= mToTLUT->load(11, lutTotEl.value); - loaded &= mToTLUT->load(13, lutTotMu.value); - loaded &= mToTLUT->load(211, lutTotPi.value); - loaded &= mToTLUT->load(321, lutTotKa.value); - loaded &= mToTLUT->load(2212, lutTotPr.value); - loaded &= mToTLUT->load(1000010020, lutTotDe.value); - loaded &= mToTLUT->load(1000010030, lutTotTr.value); - loaded &= mToTLUT->load(1000020030, lutTotHe.value); - loaded &= mToTLUT->load(1000020040, lutTotAl.value); + // Momentum axis binning choice + Configurable numLogBins{"numLogBins", 200, "Number of logarithmic momentum bins"}; + Configurable enableLogPBins{"enableLogPBins", true, "Enable logarithmic momentum binning for dE/dx vs p histograms"}; + Configurable enableLinearPBins{"enableLinearPBins", false, "Enable linear momentum binning for dE/dx vs p histograms"}; + Configurable numLinearBins{"numLinearBins", 500, "Number of linear momentum bins"}; + Configurable linearPMin{"linearPMin", 0.0f, "Minimum momentum for linear-binned histograms (GeV/c)"}; + Configurable linearPMax{"linearPMax", 10.0f, "Maximum momentum for linear-binned histograms (GeV/c)"}; - if (!loaded) { - LOG(warning) << "Failed to load one or more ToT LUTs. PID results might be incomplete."; - } + void init(o2::framework::InitContext&) + { + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setTimestamp(-1); - // Logarithmic momentum bins - mLogBins.clear(); - double pMin = kMinMomentumForLogBins; - double pMax = kMaxMomentumForLogBins; - double logMin = std::log10(pMin); - double logMax = std::log10(pMax); - double dLog = (logMax - logMin) / numLogBins.value; - for (int i = 0; i <= numLogBins.value; ++i) { - mLogBins.push_back(std::pow(10, logMin + i * dLog)); + mRandom.SetSeed(seed.value); + if (!mTrackLut.load(trackLut.value, ccdb.operator->(), PdgCodes)) { + LOG(warning) << "dE/dx LUT incomplete, species without LUT get no tracker PID."; } + bookHistograms(); + } - const AxisSpec axisMomentum{mLogBins, "#it{p/z} (GeV/#it{c})"}; - const AxisSpec axisToT{600, 0., 300., "ToT (#mus/10#mum)"}; - const AxisSpec axisNsigma{200, -10., 10., "N#sigma"}; - const AxisSpec axisLayer{maxBarrelLayers.value, -0.5, static_cast(maxBarrelLayers.value) - 0.5, "Layer"}; - const AxisSpec axisHitsPerTrack{maxBarrelLayers.value + 1, -0.5, static_cast(maxBarrelLayers.value) + 0.5, "# Hits per track"}; - - histos.add("hToTvsP", "ToT vs #it{p/z}; #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {axisMomentum, axisToT}); - histos.add("hToTvsPt", "ToT vs #it{p}; #it{p} (GeV/#it{c}); ToT (#mus/10#mum)", kTH2F, {mLogBins, axisToT}); - histos.add("hHitLayers", "Number of hits on each detector layer;Layer;Counts", kTH1F, {axisLayer}); - histos.add("hHitMultiplicity", "Hit multiplicity along the track; # Hits per track;Counts", kTH1F, {axisHitsPerTrack}); - - std::vector> particleInfo = { - {11, "Elec"}, {13, "Muon"}, {211, "Pion"}, {321, "Kaon"}, {2212, "Prot"}, {1000010020, "Deut"}, {1000010030, "Trit"}, {1000020030, "He3"}, {1000020040, "Al"}}; - - for (size_t iTrue = 0; iTrue < particleInfo.size(); ++iTrue) { - std::string trueName = particleInfo[iTrue].second; - std::string trueNamePretty = trueName; // Fallback - if (trueName == "Elec") - trueNamePretty = "#it{e}"; - else if (trueName == "Muon") - trueNamePretty = "#it{#mu}"; - else if (trueName == "Pion") - trueNamePretty = "#it{#pi}"; - else if (trueName == "Kaon") - trueNamePretty = "#it{K}"; - else if (trueName == "Prot") - trueNamePretty = "#it{p}"; - else if (trueName == "Deut") - trueNamePretty = "#it{d}"; - else if (trueName == "Trit") - trueNamePretty = "#it{t}"; - else if (trueName == "He3") - trueNamePretty = "#it{^{3}He}"; - else if (trueName == "Al") - trueNamePretty = "#it{^{4}He}"; - - std::string hitsVsPName = "HitsPerTrack/hHitsPerTrackVsP_" + trueName; - std::string hitsVsPTitle = "N_hits vs #it{p/z} for " + trueNamePretty + "; #it{p/z} (GeV/#it{c}); N_hits"; - h2dHitsPerTrackVsP[iTrue] = histos.add(hitsVsPName.c_str(), hitsVsPTitle.c_str(), kTH2F, {axisMomentum, axisHitsPerTrack}); - - std::string totVsPName = "ToTvsP/hToTvsP_" + trueName; - std::string totVsPTitle = "ToT vs #it{p/z} for " + trueNamePretty + "; #it{p/z} (GeV/#it{c}); ToT (#mus/10#mum)"; - h2dToTvsPperParticle[iTrue] = histos.add(totVsPName.c_str(), totVsPTitle.c_str(), kTH2F, {axisMomentum, axisToT}); - - for (size_t iHyp = 0; iHyp < particleInfo.size(); ++iHyp) { - std::string hypName = particleInfo[iHyp].second; - std::string hypNamePretty = hypName; // Fallback - if (hypName == "Elec") - hypNamePretty = "#it{e}"; - else if (hypName == "Muon") - hypNamePretty = "#it{#mu}"; - else if (hypName == "Pion") - hypNamePretty = "#it{#pi}"; - else if (hypName == "Kaon") - hypNamePretty = "#it{K}"; - else if (hypName == "Prot") - hypNamePretty = "#it{p}"; - else if (hypName == "Deut") - hypNamePretty = "#it{d}"; - else if (hypName == "Trit") - hypNamePretty = "#it{t}"; - else if (hypName == "He3") - hypNamePretty = "#it{^{3}He}"; - else if (hypName == "Al") - hypNamePretty = "#it{^{4}He}"; - - std::string histName = "NSigma/BarrelNsigmaTrue" + trueName + "Vs" + hypName + "Hypothesis"; - std::string histTitle = "Nsigma (True " + trueNamePretty + " vs Hyp " + hypNamePretty + "); #it{p/z} (GeV/#it{c}); N#sigma"; - h2dBarrelNsigmaTrue[iTrue][iHyp] = histos.add(histName.c_str(), histTitle.c_str(), kTH2F, {axisMomentum, axisNsigma}); - } + static std::vector logBinning(int nBins, double min, double max) + { + std::vector edges; + const double logMin = std::log10(min); + const double dLog = (std::log10(max) - logMin) / nBins; + for (int i = 0; i <= nBins; ++i) { + edges.push_back(std::pow(10, logMin + i * dLog)); } + return edges; } - void process(soa::Join::iterator const& collision, - soa::Join const& tracks, - aod::McParticles const& /*mcParticles*/, - aod::McCollisions const& /*mcCollisions*/) + void bookHistograms() { - o2::dataformats::VertexBase mcPvVtx({0.0f, 0.0f, 0.0f}, {0.}); - - if (collision.has_mcCollision()) { - const auto& mcCollisionObject = collision.mcCollision(); - mcPvVtx.setX(mcCollisionObject.posX()); - mcPvVtx.setY(mcCollisionObject.posY()); - mcPvVtx.setZ(mcCollisionObject.posZ()); + if (!enableLogPBins.value && !enableLinearPBins.value) { + LOG(fatal) << "At least one of enableLogPBins or enableLinearPBins must be true."; } - for (const auto& track : tracks) { - float truncatedMeanToT = -1.0f; - std::array nSigmaValues; - nSigmaValues.fill(999.f); - - if (!track.has_mcParticle()) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const auto& mcParticle = track.mcParticle(); - - const auto& pdgInfo = pdg->GetParticle(mcParticle.pdgCode()); - if (!pdgInfo) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const float pt = mcParticle.pt(); - const float p = mcParticle.p(); - const float eta = mcParticle.eta(); - const int truePdgCode = std::abs(mcParticle.pdgCode()); - - float rigidity = p; - if (pdgInfo) { - const float trueCharge = std::abs(pdgInfo->Charge()) / 3.0f; - if (trueCharge > 0) { - rigidity = p / trueCharge; - } - } - - int truePdgIdx = mToTLUT->getPdgIndex(truePdgCode); - if (truePdgIdx == -1) { - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); - continue; - } - - const int binnedPt = mToTLUT->getPtBin(pt); - const int binnedEta = mToTLUT->getEtaBin(std::abs(eta)); + const std::vector logBins = logBinning(numLogBins.value, MinMomentumForLogBins, MaxMomentumForLogBins); + const std::string signalTitle = "d#it{E}/d#it{x} (e^{-})"; + const AxisSpec axisDedx{logBinning(numDedxBins.value, dedxMin.value, dedxMax.value), signalTitle}; + const AxisSpec axisMomentumLog{logBins, "#it{p/z} (GeV/#it{c})"}; + const AxisSpec axisMomentumLin{numLinearBins.value, static_cast(linearPMin.value), static_cast(linearPMax.value), "#it{p/z} (GeV/#it{c})"}; + const AxisSpec axisAbsEta{axisEta, "|#eta|"}; - uint16_t hitMap = 0; - int nHitLayers = 0; - o2::track::TrackParCov o2track = o2::upgrade::convertMCParticleToO2Track(mcParticle, pdg); + if (enableLogPBins.value) { + histos.add("hToTvsP", ("Signal vs #it{p/z}; #it{p/z} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLog, axisDedx}); + histos.add("hToTvsMom", ("Signal vs #it{p}; #it{p} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {logBins, axisDedx}); + } + if (enableLinearPBins.value) { + histos.add("hToTvsPLin", ("Signal vs #it{p/z} (linear); #it{p/z} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLin, axisDedx}); + histos.add("hToTvsMomLin", ("Signal vs #it{p} (linear); #it{p} (GeV/#it{c}); " + signalTitle).c_str(), kTH2F, {axisMomentumLin, axisDedx}); + } - float xPv = -100.f; - static constexpr float kTrkXThreshold = -99.f; - if (o2track.propagateToDCA(mcPvVtx, mMagneticField)) { - xPv = o2track.getX(); + for (int i = 0; i < NumSpecies; ++i) { + if (enableLogPBins.value) { + const std::string name = std::format("ToTvsP/hToTvsP_{}", SpeciesNames[i]); + const std::string title = std::format("Signal vs #it{{p/z}} for {}; #it{{p/z}} (GeV/#it{{c}}); {}", SpeciesLabels[i], signalTitle); + h2dToTvsPperParticle[i] = histos.add(name.c_str(), title.c_str(), kTH2F, {axisMomentumLog, axisDedx}); } - - if (xPv > kTrkXThreshold) { - for (int layer = 0; layer < maxBarrelLayers.value; ++layer) { - float layerRadius = kTrackerRadii[layer]; - float trackLength = computeTrackLength(o2track, layerRadius, mMagneticField); - - if (trackLength > 0) { - hitMap |= (1 << layer); - histos.fill(HIST("hHitLayers"), layer); - nHitLayers++; - } - } + if (enableLinearPBins.value) { + const std::string name = std::format("ToTvsPLin/hToTvsPLin_{}", SpeciesNames[i]); + const std::string title = std::format("Signal vs #it{{p/z}} for {} (linear); #it{{p/z}} (GeV/#it{{c}}); {}", SpeciesLabels[i], signalTitle); + h2dToTvsPperParticleLin[i] = histos.add(name.c_str(), title.c_str(), kTH2F, {axisMomentumLin, axisDedx}); } - histos.fill(HIST("hHitMultiplicity"), nHitLayers); - h2dHitsPerTrackVsP[truePdgIdx]->Fill(rigidity, nHitLayers); - - std::vector validToTs; + const std::string nameEta = std::format("ToTvsPEta/hToTvsPEta_{}", SpeciesNames[i]); + const std::string titleEta = std::format("Signal vs #it{{p/z}} and |#eta| for {}; #it{{p/z}} (GeV/#it{{c}}); |#eta|; {}", SpeciesLabels[i], signalTitle); + h3dToTvsPEtaPerParticle[i] = histos.add(nameEta.c_str(), titleEta.c_str(), kTH3F, {axisMomentumLog, axisAbsEta, axisDedx}); - for (int layer = kMinLayerForTruncation; layer < maxBarrelLayers.value; ++layer) { - if ((hitMap >> layer) & 0x1) { - TH1F* totHist = mToTLUT->getHistogramForSampling(truePdgIdx, layer, binnedEta, binnedPt); + const std::string nameTracks = std::format("Bookkeeping/hTracks_{}", SpeciesNames[i]); + const std::string titleTracks = std::format("Reconstructed {}; #it{{p/z}} (GeV/#it{{c}}); |#eta|", SpeciesLabels[i]); + h2dTracks[i] = histos.add(nameTracks.c_str(), titleTracks.c_str(), kTH2F, {axisMomentumLog, axisAbsEta}); - if (totHist && totHist->GetEntries() > 1) { - float sampledToT = totHist->GetRandom(); - validToTs.push_back(sampledToT); - } - } - } + const std::string nameWithDedx = std::format("Bookkeeping/hTracksWithDedx_{}", SpeciesNames[i]); + const std::string titleWithDedx = std::format("Reconstructed {} with dE/dx; #it{{p/z}} (GeV/#it{{c}}); |#eta|", SpeciesLabels[i]); + h2dTracksWithDedx[i] = histos.add(nameWithDedx.c_str(), titleWithDedx.c_str(), kTH2F, {axisMomentumLog, axisAbsEta}); + } + } - truncatedMeanToT = -1.0f; - const size_t nValid = validToTs.size(); - size_t nUse = 0; - - if (nValid == kMidLowValidHits || nValid == kMidHighValidHits) - nUse = kMaxValidHitsForTruncation34; - else if (nValid == kMinValidHits || nValid == kLowValidHits) - nUse = kMaxValidHitsForTruncation12; - else if (nValid == kHighValidHits1 || nValid == kHighValidHits2) - nUse = kMaxValidHitsForTruncation56; - else if (nValid >= kMaxValidHits) - nUse = kMaxValidHitsForTruncation7Plus; - - if (nUse > 0 && nValid >= nUse) { - std::sort(validToTs.begin(), validToTs.end()); - float sum = 0.0f; - for (size_t i = 0; i < nUse; ++i) { - sum += validToTs[i]; + /// dE/dx of the track (-1 if there is none) and its nsigma for every species + template + float processTrack(TTrack const& track, std::array& nSigma) + { + if (!track.has_mcParticle()) { + return -1.f; + } + const auto& mcParticle = track.mcParticle(); + const int pdg = std::abs(mcParticle.pdgCode()); + const auto itSpecies = std::find(PdgCodes.begin(), PdgCodes.end(), pdg); + if (itSpecies == PdgCodes.end()) { + return -1.f; + } + const auto* pdgInfo = pdgDatabase->GetParticle(mcParticle.pdgCode()); + if (!pdgInfo || pdgInfo->Charge() == 0) { + return -1.f; + } + const int iSpecies = std::distance(PdgCodes.begin(), itSpecies); + const float absCharge = std::abs(pdgInfo->Charge()) / 3.f; + const float rigidityTrue = mcParticle.p() / absCharge; + const float dedx = mTrackLut.sample(pdg, mcParticle.eta(), rigidityTrue, mRandom.Rndm()); + + if (dedx > 0.f) { + for (int i = 0; i < NumSpecies; ++i) { + float mean = 0.f, sigma = 0.f; + if (mTrackLut.getMeanSigma(PdgCodes[i], track.eta(), track.p(), mean, sigma)) { + nSigma[i] = (dedx - mean) / sigma; } - truncatedMeanToT = sum / static_cast(nUse); - - histos.fill(HIST("hToTvsPt"), p, truncatedMeanToT); - histos.fill(HIST("hToTvsP"), rigidity, truncatedMeanToT); - h2dToTvsPperParticle[truePdgIdx]->Fill(rigidity, truncatedMeanToT); } + } - nSigmaValues.fill(999.f); - - if (truncatedMeanToT > 0) { - for (size_t iHyp = 0; iHyp < mHypothesisPdgCodes.size(); ++iHyp) { - int hypPdgCode = mHypothesisPdgCodes[iHyp]; - int hypPdgIdx = mToTLUT->getPdgIndex(hypPdgCode); - - if (hypPdgIdx == -1) { - nSigmaValues[iHyp] = 999.f; - continue; - } - - float expectedToT = getToTMeanFromMomentumSlice(h2dToTvsPperParticle[hypPdgIdx], rigidity); - float resolution = getToTResolutionFromMomentumSlice(h2dToTvsPperParticle[hypPdgIdx], rigidity); - - if (expectedToT > 0 && resolution > 0) { - nSigmaValues[iHyp] = calculateNsigma(truncatedMeanToT, expectedToT, resolution); - h2dBarrelNsigmaTrue[truePdgIdx][iHyp]->Fill(rigidity, nSigmaValues[iHyp]); - } else { - nSigmaValues[iHyp] = 999.f; - } - } - } + if (primariesOnly.value && !mcParticle.isPhysicalPrimary()) { + return dedx; + } + const float rigidity = useTrueKinematics.value ? rigidityTrue : track.p(); + const float absEta = std::abs(useTrueKinematics.value ? mcParticle.eta() : track.eta()); + const float p = rigidity * absCharge; + h2dTracks[iSpecies]->Fill(rigidity, absEta); + if (dedx < 0.f) { + return dedx; + } + h2dTracksWithDedx[iSpecies]->Fill(rigidity, absEta); + h3dToTvsPEtaPerParticle[iSpecies]->Fill(rigidity, absEta, dedx); + if (enableLogPBins.value) { + histos.fill(HIST("hToTvsMom"), p, dedx); + histos.fill(HIST("hToTvsP"), rigidity, dedx); + h2dToTvsPperParticle[iSpecies]->Fill(rigidity, dedx); + } + if (enableLinearPBins.value) { + histos.fill(HIST("hToTvsMomLin"), p, dedx); + histos.fill(HIST("hToTvsPLin"), rigidity, dedx); + h2dToTvsPperParticleLin[iSpecies]->Fill(rigidity, dedx); + } + return dedx; + } - tableUpgradeTrkPidSignals(truncatedMeanToT); - tableUpgradeTrkPids(nSigmaValues[0], nSigmaValues[1], nSigmaValues[2], nSigmaValues[3], - nSigmaValues[4], nSigmaValues[5], nSigmaValues[6], nSigmaValues[7], nSigmaValues[8]); + void process(soa::Join const& tracks, + aod::McParticles const&) + { + std::array nSigma{}; + for (const auto& track : tracks) { + nSigma.fill(NoNsigma); + const float dedx = processTrack(track, nSigma); + tableUpgradeTrkPidSignals(dedx); + tableUpgradeTrkPids(nSigma[0], nSigma[1], nSigma[2], nSigma[3], nSigma[4], nSigma[5], nSigma[6], nSigma[7], nSigma[8]); } } }; diff --git a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx index 0e27bec4476..bb1b54bd03e 100644 --- a/ALICE3/TableProducer/alice3-correlatorDDbar.cxx +++ b/ALICE3/TableProducer/alice3-correlatorDDbar.cxx @@ -89,8 +89,6 @@ struct alice3correlatorddbar { Configurable> binsPt{"binsPt", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for candidate mass plots and efficiency"}; Configurable> efficiencyD{"efficiencyD", std::vector{efficiencyDmeson_v}, "Efficiency values for D0 meson"}; - // HfHelper hfHelper; //not needed for now - Partition> selectedCandidates = aod::a3D0meson::y > -yCandMax&& aod::a3D0meson::y ptCandMin && (aod::a3D0Selection::isSelD0 >= selectionFlagD0 || aod::a3D0Selection::isSelD0bar >= selectionFlagD0bar); Partition> selectedCandidatesMC = aod::a3D0meson::y > -yCandMax&& aod::a3D0meson::y ptCandMin && (aod::a3D0Selection::isSelD0 >= selectionFlagD0 || aod::a3D0Selection::isSelD0bar >= selectionFlagD0bar); @@ -115,7 +113,7 @@ struct alice3correlatorddbar { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/ALICE3/TableProducer/alice3-dileptonsmearing.cxx b/ALICE3/TableProducer/alice3-dileptonsmearing.cxx new file mode 100644 index 00000000000..4136e34da82 --- /dev/null +++ b/ALICE3/TableProducer/alice3-dileptonsmearing.cxx @@ -0,0 +1,517 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// +// Analysis task to produce smeared pt, eta, phi for electrons/muons in dilepton analysis +// Please write to: daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/Utils/MomentumSmearer.h" + +#include "ALICE3/DataModel/prefilterDilepton.h" +#include "ALICE3/DataModel/tracksAlice3.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod; + +using MyTracks = soa::Join; +using MyTracksWithSmearing = soa::Join; + +struct alice3dileptonsmearer { + + Produces smearedelectron; + + Configurable cfgFromCcdb{"cfgFromCcdb", false, "get resolution and efficiency histos from CCDB"}; + Configurable cfgCcdbUrl{"cfgCcdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgCcdbTimestamp{"cfgCcdbTimestamp", 10, "valid timestamp of CCDB object"}; + + struct : ConfigurableGroup { + std::string prefix = "electron_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } electron_filenames; + + struct : ConfigurableGroup { + std::string prefix = "pion_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } pion_filenames; + + struct : ConfigurableGroup { + std::string prefix = "kaon_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } kaon_filenames; + + struct : ConfigurableGroup { + std::string prefix = "proton_filename_group"; + Configurable cfgNDSmearing{"cfgNDSmearing", false, "apply ND-correlated smearing"}; + Configurable cfgResFileName{"cfgResFileName", "", "name of resolution file"}; + Configurable cfgResNDHistName{"cfgResNDHistName", "hs_reso", "name of ND resolution file"}; + Configurable cfgResPtHistName{"cfgResPtHistName", "RelPtResArrCocktail", "histogram name for pt in resolution file"}; + Configurable cfgResEtaHistName{"cfgResEtaHistName", "EtaResArr", "histogram name for eta in resolution file"}; + Configurable cfgResPhiPosHistName{"cfgResPhiPosHistName", "PhiPosResArr", "histogram name for phi pos in resolution file"}; + Configurable cfgResPhiNegHistName{"cfgResPhiNegHistName", "PhiEleResArr", "hisogram for phi neg in resolution file"}; + Configurable cfgEffFileName{"cfgEffFileName", "", "name of efficiency file"}; + Configurable cfgEffHistName{"cfgEffHistName", "fhwEffpT", "name of efficiency histogram"}; + Configurable cfgCcdbPathRes{"cfgCcdbPathRes", "", "path to the ccdb object for resolution"}; + Configurable cfgCcdbPathEff{"cfgCcdbPathEff", "", "path to the ccdb object for efficiency"}; + Configurable cfgMinPt{"cfgMinPt", -1, "if ptgen is smaller than this threshold, this value is used as input for ptgen."}; + } proton_filenames; + + MomentumSmearer smearer_Electron; + MomentumSmearer smearer_Pion; + MomentumSmearer smearer_Kaon; + MomentumSmearer smearer_Proton; + Service ccdb; + + void init(InitContext&) + { + if (cfgCcdbTimestamp < 0) { + LOG(fatal) << "Don't use time stamp = -1"; + } + initResolutionMap(cfgCcdbTimestamp); + } + + void initResolutionMap(const int64_t timestamp) + { + smearer_Electron.setNDSmearing(electron_filenames.cfgNDSmearing.value); + smearer_Electron.setResFileName(TString(electron_filenames.cfgResFileName)); + smearer_Electron.setResNDHistName(TString(electron_filenames.cfgResNDHistName)); + smearer_Electron.setResPtHistName(TString(electron_filenames.cfgResPtHistName)); + smearer_Electron.setResEtaHistName(TString(electron_filenames.cfgResEtaHistName)); + smearer_Electron.setResPhiPosHistName(TString(electron_filenames.cfgResPhiPosHistName)); + smearer_Electron.setResPhiNegHistName(TString(electron_filenames.cfgResPhiNegHistName)); + smearer_Electron.setEffFileName(TString(electron_filenames.cfgEffFileName)); + smearer_Electron.setEffHistName(TString(electron_filenames.cfgEffHistName)); + smearer_Electron.setDCAFileName(""); + smearer_Electron.setDCAHistName(""); + smearer_Electron.setMinPt(electron_filenames.cfgMinPt); + + smearer_Pion.setNDSmearing(pion_filenames.cfgNDSmearing.value); + smearer_Pion.setResFileName(TString(pion_filenames.cfgResFileName)); + smearer_Pion.setResNDHistName(TString(pion_filenames.cfgResNDHistName)); + smearer_Pion.setResPtHistName(TString(pion_filenames.cfgResPtHistName)); + smearer_Pion.setResEtaHistName(TString(pion_filenames.cfgResEtaHistName)); + smearer_Pion.setResPhiPosHistName(TString(pion_filenames.cfgResPhiPosHistName)); + smearer_Pion.setResPhiNegHistName(TString(pion_filenames.cfgResPhiNegHistName)); + smearer_Pion.setEffFileName(TString(pion_filenames.cfgEffFileName)); + smearer_Pion.setEffHistName(TString(pion_filenames.cfgEffHistName)); + smearer_Pion.setDCAFileName(""); + smearer_Pion.setDCAHistName(""); + smearer_Pion.setMinPt(pion_filenames.cfgMinPt); + + smearer_Kaon.setNDSmearing(kaon_filenames.cfgNDSmearing.value); + smearer_Kaon.setResFileName(TString(kaon_filenames.cfgResFileName)); + smearer_Kaon.setResNDHistName(TString(kaon_filenames.cfgResNDHistName)); + smearer_Kaon.setResPtHistName(TString(kaon_filenames.cfgResPtHistName)); + smearer_Kaon.setResEtaHistName(TString(kaon_filenames.cfgResEtaHistName)); + smearer_Kaon.setResPhiPosHistName(TString(kaon_filenames.cfgResPhiPosHistName)); + smearer_Kaon.setResPhiNegHistName(TString(kaon_filenames.cfgResPhiNegHistName)); + smearer_Kaon.setEffFileName(TString(kaon_filenames.cfgEffFileName)); + smearer_Kaon.setEffHistName(TString(kaon_filenames.cfgEffHistName)); + smearer_Kaon.setDCAFileName(""); + smearer_Kaon.setDCAHistName(""); + smearer_Kaon.setMinPt(kaon_filenames.cfgMinPt); + + smearer_Proton.setNDSmearing(proton_filenames.cfgNDSmearing.value); + smearer_Proton.setResFileName(TString(proton_filenames.cfgResFileName)); + smearer_Proton.setResNDHistName(TString(proton_filenames.cfgResNDHistName)); + smearer_Proton.setResPtHistName(TString(proton_filenames.cfgResPtHistName)); + smearer_Proton.setResEtaHistName(TString(proton_filenames.cfgResEtaHistName)); + smearer_Proton.setResPhiPosHistName(TString(proton_filenames.cfgResPhiPosHistName)); + smearer_Proton.setResPhiNegHistName(TString(proton_filenames.cfgResPhiNegHistName)); + smearer_Proton.setEffFileName(TString(proton_filenames.cfgEffFileName)); + smearer_Proton.setEffHistName(TString(proton_filenames.cfgEffHistName)); + smearer_Proton.setDCAFileName(""); + smearer_Proton.setDCAHistName(""); + smearer_Proton.setMinPt(proton_filenames.cfgMinPt); + + if (cfgFromCcdb) { + ccdb->setURL(cfgCcdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); // now + + smearer_Electron.setCcdbPathRes(TString(electron_filenames.cfgCcdbPathRes)); + smearer_Electron.setCcdbPathEff(TString(electron_filenames.cfgCcdbPathEff)); + // smearer_Electron.setCcdbPathDCA(TString(electron_filenames.fConfigCcdbPathDCA)); + smearer_Electron.setCcdbPathDCA(""); + smearer_Electron.setTimestamp(timestamp); + smearer_Electron.setCcdb(ccdb); + + smearer_Pion.setCcdbPathRes(TString(pion_filenames.cfgCcdbPathRes)); + smearer_Pion.setCcdbPathEff(TString(pion_filenames.cfgCcdbPathEff)); + // smearer_Pion.setCcdbPathDCA(TString(pion_filenames.fConfigCcdbPathDCA)); + smearer_Pion.setCcdbPathDCA(""); + smearer_Pion.setTimestamp(timestamp); + smearer_Pion.setCcdb(ccdb); + + smearer_Kaon.setCcdbPathRes(TString(kaon_filenames.cfgCcdbPathRes)); + smearer_Kaon.setCcdbPathEff(TString(kaon_filenames.cfgCcdbPathEff)); + // smearer_Kaon.setCcdbPathDCA(TString(kaon_filenames.fConfigCcdbPathDCA)); + smearer_Kaon.setCcdbPathDCA(""); + smearer_Kaon.setTimestamp(timestamp); + smearer_Kaon.setCcdb(ccdb); + + smearer_Proton.setCcdbPathRes(TString(proton_filenames.cfgCcdbPathRes)); + smearer_Proton.setCcdbPathEff(TString(proton_filenames.cfgCcdbPathEff)); + // smearer_Proton.setCcdbPathDCA(TString(proton_filenames.fConfigCcdbPathDCA)); + smearer_Proton.setCcdbPathDCA(""); + smearer_Proton.setTimestamp(timestamp); + smearer_Proton.setCcdb(ccdb); + } + + smearer_Electron.init(); + smearer_Pion.init(); + smearer_Kaon.init(); + smearer_Proton.init(); + } + + void processACTSHybrid(MyTracks const& tracks, const aod::McParticles& /*mcParticles*/) + { + for (const auto& track : tracks) { + float ptgen = track.pt(); + float etagen = track.eta(); + float phigen = track.phi(); + bool selected = true; + float centrality = -1.f; + if (track.has_mcParticle() && track.isReconstructed()) { + const auto mcParticle = track.mcParticle_as(); + ptgen = mcParticle.pt(); + etagen = mcParticle.eta(); + phigen = mcParticle.phi(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kElectron) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Electron.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency + float efficiency = smearer_Electron.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Pion.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency if there, otherwise applied zero + if ((TString(pion_filenames.cfgCcdbPathEff).CompareTo("") != 0) || (TString(pion_filenames.cfgEffFileName).CompareTo("") != 0)) { + float efficiency = smearer_Pion.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Kaon.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency if there, otherwise applied zero + if ((TString(kaon_filenames.cfgCcdbPathEff).CompareTo("") != 0) || (TString(kaon_filenames.cfgEffFileName).CompareTo("") != 0)) { + float efficiency = smearer_Kaon.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + int ch = -1; + if (mcParticle.pdgCode() < 0) { + ch = 1; + } + float ptsmeared = 0, etasmeared = 0, phismeared = 0; + // apply smearing for electrons or muons. + smearer_Proton.applySmearing(centrality, ch, ptgen, etagen, phigen, ptsmeared, etasmeared, phismeared); + // get the efficiency if there, otherwise applied zero + if ((TString(proton_filenames.cfgCcdbPathEff).CompareTo("") != 0) || (TString(proton_filenames.cfgEffFileName).CompareTo("") != 0)) { + float efficiency = smearer_Proton.getEfficiency(ptgen, etagen, phigen); + // Generate a random double between 0 and 1 + double myRandom = gRandom->Uniform(0, 1); + // Select + if (myRandom < efficiency) { + selected = true; + } else { + selected = false; + } + } else { + selected = false; + } + // fill the table + smearedelectron(ptsmeared, etasmeared, phismeared, selected); + } else { + // don't apply smearing and reject completely + smearedelectron(ptgen, etagen, phigen, false); + } + } else { + // don't apply smearing + smearedelectron(ptgen, etagen, phigen, false); + } + } // end of mc track loop + } + + void processDummyACTSHybrid(MyTracks const& tracks) + { + for (const auto& track : tracks) { + smearedelectron(track.pt(), track.eta(), track.phi(), true); + } + } + + PROCESS_SWITCH(alice3dileptonsmearer, processACTSHybrid, "Run for alice 3 ACTS hybrid", false); + PROCESS_SWITCH(alice3dileptonsmearer, processDummyACTSHybrid, "Dummy process function", true); +}; + +struct alice3dileptonchecksmearer { + // Resolution histos as cross check + Configurable cfgUsePtVecRes{"cfgUsePtVecRes", true, "If true, non-linear pt bins predefined in res histos"}; + ConfigurableAxis ptResBinsVec{"ptResBinsVec", {0., 0., 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.155, 0.16, 0.165, 0.17, 0.175, 0.18, 0.185, 0.19, 0.195, 0.20, 0.205, 0.21, 0.215, 0.22, 0.225, 0.23, 0.235, 0.24, 0.245, 0.25, 0.255, 0.26, 0.265, 0.27, 0.275, 0.28, 0.285, 0.29, 0.295, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.43, 0.46, 0.49, 0.52, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.90, 1.00, 1.10, 1.20, 1.40, 1.60, 1.80, 2.00, 2.40, 2.80, 3.20, 3.70, 4.50, 6.00, 8.00, 10., 12.0, 14., 16., 18., 20.}, "Pt binning vector for resolution"}; + ConfigurableAxis ptResBins{"ptResBins", {20, 0.f, 20.f}, "Pt binning for resolution"}; + ConfigurableAxis deltaptResBins{"deltaptResBins", {500, -1.f, 1.f}, "DeltaPt binning for resolution"}; + ConfigurableAxis deltaetaResBins{"deltaetaResBins", {500, -0.5f, 0.5f}, "DeltaEta binning for resolution"}; + ConfigurableAxis deltaphiResBins{"deltaphiResBins", {500, -0.5f, 0.5f}, "DeltaPhi binning for resolution"}; + ConfigurableAxis etaBins{"etaBins", {200, -1.f, 1.f}, "Eta binning for efficiency"}; + + HistogramRegistry registry{"output", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + if (doprocessCheckACTSHybrid) { + registry.add("Electron/hCorrelation_Pt", "pT correlation;p_{T,l}^{gen} (GeV/c);p_{T,l}^{smeared} (GeV/c)", {HistType::kTH2F, {{1000, 0.0f, 10.0f}, {1000, 0.0f, 10.0f}}}); + registry.add("Electron/hCorrelation_Eta", "eta correlation;#eta_{l}^{gen};#eta_{l}^{smeared}", {HistType::kTH2F, {{200, -1.0f, +1.0f}, {200, -1.0f, +1.0f}}}); + registry.add("Electron/hCorrelation_Phi", "phi correlation;#varphi_{l}^{gen} (rad.);#varphi_{l}^{smeared} (rad.)", {HistType::kTH2F, {{100, 0.0f, TMath::TwoPi()}, {100, 0.0f, TMath::TwoPi()}}}); + + // Binning for resolution + AxisSpec axisPtRes{ptResBins, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}; + AxisSpec axisDeltaptRes{deltaptResBins, "(p^{gen}_{T} - p^{rec}_{T}) / p^{gen}_{T}"}; + AxisSpec axisDeltaetaRes{deltaetaResBins, "#eta^{gen} - #eta^{rec}"}; + AxisSpec axisDeltaphiRes{deltaphiResBins, "#varphi^{gen} - #varphi^{rec} (rad.)"}; + // Binning for efficiency + AxisSpec axiseta{etaBins, "#eta"}; + + if (!cfgUsePtVecRes) { + registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {axisPtRes, axisDeltaptRes}, true); + registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {axisPtRes, axisDeltaetaRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {axisPtRes, axisDeltaphiRes}, true); + registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {axisPtRes, axiseta}, true); + registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {axisPtRes, axiseta}, true); + } else { + registry.add("Electron/PtGen_DeltaPtOverPtGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaptRes}, true); + registry.add("Electron/PtGen_DeltaEta", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaetaRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Neg", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); + registry.add("Electron/PtGen_DeltaPhi_Pos", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axisDeltaphiRes}, true); + registry.add("Electron/PtEtaGen", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); + registry.add("Electron/PtEtaRec", "", HistType::kTH2D, {{ptResBinsVec, "#it{p}^{gen}_{T,l} (GeV/#it{c})"}, axiseta}, true); + } + + registry.addClone("Electron/", "Other/"); + registry.addClone("Electron/", "Pion/"); + registry.addClone("Electron/", "Kaon/"); + registry.addClone("Electron/", "Proton/"); + } + } + + void processCheckACTSHybrid(MyTracksWithSmearing const& tracks, const aod::McParticles& /*mcParticles*/) + { + for (const auto& track : tracks) { + if (!track.has_mcParticle() || !track.isReconstructed()) { + continue; + } + const auto mcParticle = track.mcParticle_as(); + float deltaptoverpt = -1000.f; + if (mcParticle.pt() > 0.f) { + deltaptoverpt = (mcParticle.pt() - track.ptSmeared()) / mcParticle.pt(); + } + float deltaeta = mcParticle.eta() - track.etaSmeared(); + float deltaphi = mcParticle.phi() - track.phiSmeared(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kElectron) { + registry.fill(HIST("Electron/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Electron/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Electron/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Electron/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Electron/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Electron/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Electron/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Electron/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Electron/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { + registry.fill(HIST("Pion/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Pion/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Pion/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Pion/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Pion/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Pion/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Pion/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Pion/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Pion/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus) { + registry.fill(HIST("Kaon/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Kaon/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Kaon/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Kaon/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Kaon/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Kaon/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Kaon/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Kaon/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Kaon/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + registry.fill(HIST("Proton/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Proton/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Proton/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Proton/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Proton/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Proton/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Proton/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Proton/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Proton/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } else { + registry.fill(HIST("Other/PtGen_DeltaPtOverPtGen"), mcParticle.pt(), deltaptoverpt); + registry.fill(HIST("Other/PtGen_DeltaEta"), mcParticle.pt(), deltaeta); + if (mcParticle.pdgCode() < 0) { // e+ + registry.fill(HIST("Other/PtGen_DeltaPhi_Pos"), mcParticle.pt(), deltaphi); + } else { // e- + registry.fill(HIST("Other/PtGen_DeltaPhi_Neg"), mcParticle.pt(), deltaphi); + } + registry.fill(HIST("Other/hCorrelation_Pt"), mcParticle.pt(), track.ptSmeared()); + registry.fill(HIST("Other/hCorrelation_Eta"), mcParticle.eta(), track.etaSmeared()); + registry.fill(HIST("Other/hCorrelation_Phi"), mcParticle.phi(), track.phiSmeared()); + // efficiency + registry.fill(HIST("Other/PtEtaGen"), mcParticle.pt(), mcParticle.eta()); + if (track.selected()) { + registry.fill(HIST("Other/PtEtaRec"), mcParticle.pt(), mcParticle.eta()); + } + } + } // end of loop + } + + void processDummyACTSHybrid(aod::McParticles const&) {} + + PROCESS_SWITCH(alice3dileptonchecksmearer, processCheckACTSHybrid, "Run for alice3 analysis", false); + PROCESS_SWITCH(alice3dileptonchecksmearer, processDummyACTSHybrid, "Dummy process function", true); +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/ALICE3/TableProducer/alice3-centrality.cxx b/ALICE3/TableProducer/alice3Centrality.cxx similarity index 59% rename from ALICE3/TableProducer/alice3-centrality.cxx rename to ALICE3/TableProducer/alice3Centrality.cxx index bc06a838697..8400d6bca49 100644 --- a/ALICE3/TableProducer/alice3-centrality.cxx +++ b/ALICE3/TableProducer/alice3Centrality.cxx @@ -10,6 +10,8 @@ // or submit itself to any jurisdiction. /// \author Nicolo' Jacazio , CERN /// \author David Dobrigkeit Chinellato , UNICAMP/CERN +/// \brief Centrality producer for ALICE3 +/// \file alice3Centrality.cxx #include "Common/DataModel/Centrality.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -20,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -30,69 +33,67 @@ #include #include +#include #include +#include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -struct ALICE3Centrality { +struct Alice3Centrality { Produces cent; Service ccdb; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable MinEta{"MinEta", -4.0f, "Minimum eta in range"}; - Configurable MaxEta{"MaxEta", 4.0f, "Maximum eta in range"}; - Configurable MaxMult{"MaxMult", 10000.f, "Maximum multiplicity in range"}; - Configurable MaxDCA{"MaxDCA", 0.0025f, "Max DCAxy and DCAz for counted tracks"}; - Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbNoLaterThan{"ccdbNoLaterThan", 1, "latest acceptable timestamp of creation for the object"}; + Configurable minEta{"minEta", -4.0f, "Minimum eta in range"}; + Configurable maxEta{"maxEta", 4.0f, "Maximum eta in range"}; + Configurable maxDCA{"maxDCA", 0.0025f, "Max DCAxy and DCAz for counted tracks"}; + Configurable vtxZ{"vtxZ", 10.0f, "Max event vertex z position allowed"}; + Configurable minNumContrib{"minNumContrib", 1, "Minimum required number of primary vertex contributors"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPath{"ccdbPath", "Analysis/ALICE3/Centrality", "path to the ccdb object"}; + + ConfigurableAxis axisMult{"axisMult", {10000, 0, 10000}, "Reconstructed tracks"}; + ConfigurableAxis axisCent{"axisCent", {150, 0, 150}, "Percentile"}; + + Filter trackFilter = (aod::track::eta >= minEta) && (aod::track::eta <= maxEta) && (nabs(aod::track::dcaXY) <= maxDCA) && (nabs(aod::track::dcaZ) <= maxDCA); bool centralityLoaded = false; TH1D* hCumMultALICE3 = nullptr; void init(InitContext&) { - const AxisSpec axisMult{MaxMult.value > 10000.f ? 10000 : (int)MaxMult, 0, MaxMult, "Reconstructed tracks"}; - const AxisSpec axisCent{150, 0, 150, "Percentile"}; - TString tit = Form("%.3f < #it{#eta} < %.3f", MinEta.value, MaxEta.value); + TString tit = Form("%.3f < #it{#eta} < %.3f", minEta.value, maxEta.value); histos.add("centrality/numberOfTracks", tit, kTH1D, {axisMult}); histos.add("centrality/centralityDistribution", "Centrality test", kTH1D, {axisCent}); - ccdb->setURL(url.value); + ccdb->setURL(ccdbUrl.value); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); } - int nevs = 0; - void process(const o2::aod::Collision& collision, const soa::Join& tracks) + void process(const o2::aod::Collision& collision, const soa::Filtered>& tracks) { if (!centralityLoaded) { - hCumMultALICE3 = ccdb->getForTimeStamp("Analysis/ALICE3/Centrality", -1); + hCumMultALICE3 = ccdb->getForTimeStamp(ccdbPath.value, ccdbNoLaterThan.value); centralityLoaded = true; LOGF(info, "ALICE 3 centrality calibration loaded!"); } - int nTracks = 0; - if (collision.numContrib() < 1) { + if (collision.numContrib() < minNumContrib.value) { histos.fill(HIST("centrality/centralityDistribution"), 101); cent(101); return; } - if (fabs(collision.posZ()) > 10) { + if (std::fabs(collision.posZ()) > vtxZ.value) { histos.fill(HIST("centrality/centralityDistribution"), 102); cent(102); return; } - for (const auto& track : tracks) { - if (track.eta() < MinEta || track.eta() > MaxEta) { - continue; - } - if (abs(track.dcaXY()) > MaxDCA || abs(track.dcaZ()) > MaxDCA) { - continue; - } - nTracks++; - } - LOG(info) << nevs++ << ") Event " << collision.globalIndex() << " has " << nTracks << " tracks"; + + const auto nTracks = tracks.size(); histos.fill(HIST("centrality/numberOfTracks"), nTracks); float centALICE3 = hCumMultALICE3->GetBinContent(hCumMultALICE3->FindBin(nTracks)); @@ -103,5 +104,5 @@ struct ALICE3Centrality { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"alice3-centrality"})}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/TableProducer/alice3-dq-table-maker.cxx b/ALICE3/TableProducer/alice3DqTableMaker.cxx similarity index 72% rename from ALICE3/TableProducer/alice3-dq-table-maker.cxx rename to ALICE3/TableProducer/alice3DqTableMaker.cxx index a6f36151732..1cbbb4fa8a9 100644 --- a/ALICE3/TableProducer/alice3-dq-table-maker.cxx +++ b/ALICE3/TableProducer/alice3DqTableMaker.cxx @@ -8,11 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -// Contact: Ionut Cristian Arsene iarsene@cern.ch, i.c.arsene@fys.uio.no -// Alexander Tiekoetter (alexander.tiekoetter@cern.ch) -/// \file alice3-dq-table-maker.cxx -/// \brief DQ table maker for ALICE 3 +/// \author Ionut Cristian Arsene , Oslo +/// \author Alexander Tiekoetter , Muenster +/// \brief Skimming Task for DQ Table Maker +/// \file alice3DqTableMaker.cxx #include "PWGDQ/Core/AnalysisCompositeCut.h" #include "PWGDQ/Core/AnalysisCut.h" @@ -22,14 +21,16 @@ #include "PWGDQ/Core/MCSignal.h" #include "PWGDQ/Core/MCSignalLibrary.h" #include "PWGDQ/Core/VarManager.h" -#include "PWGDQ/DataModel/ReducedTablesAlice3.h" #include "ALICE3/DataModel/OTFRICH.h" #include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/ReducedTablesAlice3.h" #include "ALICE3/DataModel/collisionAlice3.h" #include "ALICE3/DataModel/tracksAlice3.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -64,26 +65,32 @@ using MyBarrelTracks = soa::Join; -using MyEvents = soa::Join; -using MyEventsMC = aod::McCollisions; +using MyEvents = soa::Join; +using MyEventsMC = soa::Join; + +constexpr static uint32_t GkEventFillMap = VarManager::ObjTypes::Collision; +constexpr static uint32_t GkEventMcFillMap = VarManager::ObjTypes::CollisionMC; -constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::Collision; -constexpr static uint32_t gkEventMcFillMap = VarManager::ObjTypes::CollisionMC; +constexpr static uint32_t GkTrackFillMapWithCov = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackCov | VarManager::ObjTypes::TrackPID; -constexpr static uint32_t gkTrackFillMapWithCov = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackCov | VarManager::ObjTypes::TrackPID; +namespace dqefficiency_helpers +{ +inline float* varValues() { return static_cast(VarManager::fgValues); } +inline TString* varNames() { return static_cast(VarManager::fgVariableNames); } +inline TString* varUnits() { return static_cast(VarManager::fgVariableUnits); } +} // namespace dqefficiency_helpers -struct Alice3DQTableMaker { +struct Alice3DqTableMaker { - Produces eventMC; - Produces trackMC; + Produces eventMC; + Produces trackMC; - Produces event; + Produces event; + Produces eventExtended; Produces eventVtxCov; - Produces eventInfo; Produces eventMClabels; - Produces trackBarrelInfo; - Produces trackBasic; + Produces trackBasic; Produces trackBarrel; Produces trackBarrelCov; Produces trackBarrelAssoc; @@ -96,33 +103,32 @@ struct Alice3DQTableMaker { OutputObj fOutputList{"output"}; OutputObj fStatsList{"Statistics"}; //! skimming statistics - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; // Event and track AnalysisCut configurables struct : ConfigurableGroup { - Configurable fConfigEventCuts{"cfgEventCuts", "", "Event selection"}; - Configurable fConfigTrackCuts{"cfgBarrelTrackCuts", "", "barrel track cut"}; - Configurable fConfigEventCutsJSON{"cfgEventCutsJSON", "", "Additional event selection in JSON format"}; - Configurable fConfigTrackCutsJSON{"cfgBarrelTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; + Configurable cfgEventCuts{"cfgEventCuts", "", "Event selection"}; + Configurable cfgBarrelTrackCuts{"cfgBarrelTrackCuts", "", "barrel track cut"}; + Configurable cfgEventCutsJSON{"cfgEventCutsJSON", "", "Additional event selection in JSON format"}; + Configurable cfgBarrelTrackCutsJSON{"cfgBarrelTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; } fConfigCuts; // MC signals to be skimmed - Configurable fConfigMCSignals{"cfgMCsignals", "", "Comma separated list of MC signals"}; - Configurable fConfigMCSignalsJSON{"cfgMCsignalsJSON", "", "Additional list of MC signals via JSON"}; + Configurable cfgMCsignals{"cfgMCsignals", "", "Comma separated list of MC signals"}; + Configurable cfgMCsignalsJSON{"cfgMCsignalsJSON", "", "Additional list of MC signals via JSON"}; // Steer QA output struct : ConfigurableGroup { - Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - Configurable fConfigDetailedQA{"cfgDetailedQA", false, "If true, include more QA histograms (BeforeCuts classes)"}; - Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddMCTruthHistogram{"cfgAddMCTruthHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + Configurable cfgQA{"cfgQA", false, "If true, fill QA histograms"}; + Configurable cfgDetailedQA{"cfgDetailedQA", false, "If true, include more QA histograms (BeforeCuts classes)"}; + Configurable cfgAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms"}; + Configurable cfgAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; + Configurable cfgAddMCTruthHistogram{"cfgAddMCTruthHistogram", "", "Comma separated list of histograms"}; + Configurable cfgAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; } fConfigHistOutput; - AnalysisCompositeCut* fEventCut; //! Event selection cut + AnalysisCompositeCut* fEventCut = nullptr; //! Event selection cut std::vector fTrackCuts; //! Barrel track cuts - std::vector fMuonCuts; //! Muon track cuts bool fDoDetailedQA = false; @@ -137,18 +143,19 @@ struct Alice3DQTableMaker { { bool isProcessSkimmingEnabled = context.mOptions.get("processSkimming"); - if (!isProcessSkimmingEnabled) + if (!isProcessSkimmingEnabled) { LOG(fatal) << "No process function was enabled ALICE 3 TableMaker"; + } - VarManager::SetDefaultVarNames(); // Important that this is called before DefineCuts() !!! + VarManager::SetDefaultVarNames(); // Important that this is called before defineCuts() !!! - DefineCuts(); + defineCuts(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); - if (fConfigHistOutput.fConfigQA && fConfigHistOutput.fConfigDetailedQA) { + if (fConfigHistOutput.cfgQA && fConfigHistOutput.cfgDetailedQA) { fDoDetailedQA = true; } @@ -158,7 +165,7 @@ struct Alice3DQTableMaker { histClasses += "Event_BeforeCuts;"; } - if (fConfigHistOutput.fConfigQA) { + if (fConfigHistOutput.cfgQA) { histClasses += "Event_AfterCuts;"; histClasses += "Event_MCTruth;"; } @@ -168,14 +175,14 @@ struct Alice3DQTableMaker { histClasses += "TrackBarrel_BeforeCuts;"; } - if (fConfigHistOutput.fConfigQA) { + if (fConfigHistOutput.cfgQA) { for (const auto& cut : fTrackCuts) { histClasses += Form("TrackBarrel_%s;", cut->GetName()); } } } - TString configNamesStr = fConfigMCSignals.value; + TString configNamesStr = cfgMCsignals.value; std::unique_ptr objArray(configNamesStr.Tokenize(",")); if (objArray->GetEntries() > 0) { @@ -187,7 +194,7 @@ struct Alice3DQTableMaker { } } - TString addMCSignalsStr = fConfigMCSignalsJSON.value; + TString addMCSignalsStr = cfgMCsignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); @@ -200,7 +207,7 @@ struct Alice3DQTableMaker { } for (const auto& mcIt : fMCSignals) { - if (fConfigHistOutput.fConfigQA) { + if (fConfigHistOutput.cfgQA) { histClasses += Form("MCTruth_%s;", mcIt->GetName()); } if (fDoDetailedQA) { @@ -212,10 +219,10 @@ struct Alice3DQTableMaker { } } - DefineHistograms(histClasses); + defineHistograms(histClasses); - TString addHistsStr = fConfigHistOutput.fConfigAddJSONHistograms.value; - if (fConfigHistOutput.fConfigQA && addHistsStr != "") { + TString addHistsStr = fConfigHistOutput.cfgAddJSONHistograms.value; + if (fConfigHistOutput.cfgQA && addHistsStr != "") { dqhistograms::AddHistogramsFromJSON(fHistMan, addHistsStr.Data()); } @@ -223,13 +230,13 @@ struct Alice3DQTableMaker { fOutputList.setObject(fHistMan->GetMainHistogramList()); } - void DefineCuts() + void defineCuts() { fEventCut = new AnalysisCompositeCut(true); - TString eventCutStr = fConfigCuts.fConfigEventCuts.value; + TString eventCutStr = fConfigCuts.cfgEventCuts.value; fEventCut->AddCut(dqcuts::GetAnalysisCut(eventCutStr.Data())); - TString addEvCutsStr = fConfigCuts.fConfigEventCutsJSON.value; + TString addEvCutsStr = fConfigCuts.cfgEventCutsJSON.value; if (addEvCutsStr != "") { std::vector addEvCuts = dqcuts::GetCutsFromJSON(addEvCutsStr.Data()); for (const auto& cutIt : addEvCuts) { @@ -238,7 +245,7 @@ struct Alice3DQTableMaker { } // Barrel track cuts - TString cutNamesStr = fConfigCuts.fConfigTrackCuts.value; + TString cutNamesStr = fConfigCuts.cfgBarrelTrackCuts.value; if (!cutNamesStr.IsNull()) { std::unique_ptr objArray(cutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { @@ -246,82 +253,82 @@ struct Alice3DQTableMaker { } } // Additional Barrel track cuts via JSON - TString addTrackCutsStr = fConfigCuts.fConfigTrackCutsJSON.value; + TString addTrackCutsStr = fConfigCuts.cfgBarrelTrackCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (const auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill } - void DefineHistograms(TString histClasses) + void defineHistograms(const TString& histClasses) { std::unique_ptr objArray(histClasses.Tokenize(";")); for (int iclass = 0; iclass < objArray->GetEntries(); ++iclass) { TString classStr = objArray->At(iclass)->GetName(); - if (fConfigHistOutput.fConfigQA) { + if (fConfigHistOutput.cfgQA) { fHistMan->AddHistClass(classStr.Data()); } - TString histEventName = fConfigHistOutput.fConfigAddEventHistogram.value; + TString histEventName = fConfigHistOutput.cfgAddEventHistogram.value; if (classStr.Contains("Event")) { - if (fConfigHistOutput.fConfigQA && !classStr.Contains("MCTruth")) { + if (fConfigHistOutput.cfgQA && !classStr.Contains("MCTruth")) { dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "event", histEventName); } else { dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "event", "generator"); } } - TString histTrackName = fConfigHistOutput.fConfigAddTrackHistogram.value; + TString histTrackName = fConfigHistOutput.cfgAddTrackHistogram.value; if (classStr.Contains("Track")) { - if (fConfigHistOutput.fConfigQA) { + if (fConfigHistOutput.cfgQA) { dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "track", histTrackName); } } - TString histMCTruthName = fConfigHistOutput.fConfigAddMCTruthHistogram.value; + TString histMCTruthName = fConfigHistOutput.cfgAddMCTruthHistogram.value; if (classStr.Contains("MCTruth") && !classStr.Contains("Event")) { - if (fConfigHistOutput.fConfigQA) { + if (fConfigHistOutput.cfgQA) { dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "mctruth_track", histMCTruthName); } } } - // create statistics histograms (event, tracks, muons, MCsignals) + // create statistics histograms (event, tracks, MCsignals) fStatsList.setObject(new TList()); fStatsList->SetOwner(true); std::vector eventLabels{"Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, (float)o2::aod::evsel::kNsel + 0.5); - int ib = 1; - for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { - histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, static_cast(o2::aod::evsel::kNsel) + 0.5); + int ibX = 1; + for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ibX++) { + histEvents->GetXaxis()->SetBinLabel(ibX, (*label).Data()); } - for (int ib = 1; ib <= o2::aod::evsel::kNsel; ib++) { - histEvents->GetYaxis()->SetBinLabel(ib, o2::aod::evsel::selectionLabels[ib - 1]); + for (int ibY = 1; ibY <= o2::aod::evsel::kNsel; ibY++) { + histEvents->GetYaxis()->SetBinLabel(ibY, o2::aod::evsel::selectionLabels[ibY - 1]); } histEvents->GetYaxis()->SetBinLabel(o2::aod::evsel::kNsel + 1, "Total"); fStatsList->Add(histEvents); // Track statistics: one bin for each track selection and 5 bins for V0 tags (gamma, K0s, Lambda, anti-Lambda, Omega) - TH1I* histTracks = new TH1I("TrackStats", "Track statistics", fTrackCuts.size() + 5.0, -0.5, fTrackCuts.size() - 0.5 + 5.0); - ib = 1; - for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, ib++) { - histTracks->GetXaxis()->SetBinLabel(ib, (*cut)->GetName()); + TH1I* histTracks = new TH1I("TrackStats", "Track statistics", static_cast(fTrackCuts.size() + 5.0), -0.5, fTrackCuts.size() - 0.5 + 5.0); + ibX = 1; + for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, ibX++) { + histTracks->GetXaxis()->SetBinLabel(ibX, (*cut)->GetName()); } - constexpr int nV0Tags = 5; - const char* v0TagNames[nV0Tags] = {"Photon conversion", "K^{0}_{s}", "#Lambda", "#bar{#Lambda}", "#Omega"}; - for (int ib = 0; ib < nV0Tags; ib++) { - histTracks->GetXaxis()->SetBinLabel(fTrackCuts.size() + 1 + ib, v0TagNames[ib]); + constexpr int NV0Tags = 5; + const std::array v0TagNames = {"Photon conversion", "K^{0}_{s}", "#Lambda", "#bar{#Lambda}", "#Omega"}; + for (int ibY = 0; ibY < NV0Tags; ibY++) { + histTracks->GetXaxis()->SetBinLabel(fTrackCuts.size() + 1 + ibY, v0TagNames[ibY].c_str()); } fStatsList->Add(histTracks); TH1I* histMCsignals = new TH1I("MCsignals", "MC signals", fMCSignals.size() + 1, -0.5, fMCSignals.size() - 0.5 + 1.0); - ib = 1; - for (auto signal = fMCSignals.begin(); signal != fMCSignals.end(); signal++, ib++) { - histMCsignals->GetXaxis()->SetBinLabel(ib, (*signal)->GetName()); + ibX = 1; + for (auto signal = fMCSignals.begin(); signal != fMCSignals.end(); signal++, ibX++) { + histMCsignals->GetXaxis()->SetBinLabel(ibX, (*signal)->GetName()); } histMCsignals->GetXaxis()->SetBinLabel(fMCSignals.size() + 1, "Others (matched to reco tracks)"); fStatsList->Add(histMCsignals); @@ -337,12 +344,13 @@ struct Alice3DQTableMaker { VarManager::ResetValues(0, VarManager::kNVars); for (const auto& mcCollision : mcCollisions) { - VarManager::FillEventAlice3(mcCollision); + VarManager::FillEventAlice3(mcCollision); - fHistMan->FillHistClass("Event_MCTruth", VarManager::fgValues); + fHistMan->FillHistClass("Event_MCTruth", dqefficiency_helpers::varValues()); eventMC(mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), - mcCollision.t(), mcCollision.weight(), mcCollision.impactParameter()); // TODO: Determine and fill multiplicity values + mcCollision.t(), mcCollision.weight(), mcCollision.impactParameter(), + mcCollision.multMC(), mcCollision.multMC25(), mcCollision.multMC125(), mcCollision.multMC09()); } } @@ -357,7 +365,7 @@ struct Alice3DQTableMaker { fLabelsMapReversed.clear(); fMCFlags.clear(); - uint16_t mcflags = static_cast(0); // flags which will hold the decisions for each MC signal + uint16_t mcflags{0}; // flags which will hold the decisions for each MC signal int trackCounter = 0; for (const auto& mctrack : mcTracks) { @@ -367,8 +375,8 @@ struct Alice3DQTableMaker { for (const auto& sig : fMCSignals) { bool checked = false; if constexpr (soa::is_soa_filtered_v) { - auto mctrack_raw = mcTracks.rawIteratorAt(mctrack.globalIndex()); - checked = sig->CheckSignal(true, mctrack_raw); + auto mcTrackRaw = mcTracks.rawIteratorAt(mctrack.globalIndex()); + checked = sig->CheckSignal(true, mcTrackRaw); } else { checked = sig->CheckSignal(true, mctrack); } @@ -384,21 +392,24 @@ struct Alice3DQTableMaker { } // If this MC track was not already added to the map, add it now - if (fLabelsMap.find(mctrack.globalIndex()) == fLabelsMap.end()) { - fLabelsMap[mctrack.globalIndex()] = trackCounter; - fLabelsMapReversed[trackCounter] = mctrack.globalIndex(); - fMCFlags[mctrack.globalIndex()] = mcflags; - trackCounter++; + const auto mcTrackIndex = mctrack.globalIndex(); + const bool inserted = fLabelsMap.try_emplace(mcTrackIndex, trackCounter).second; + + if (inserted) { + fLabelsMapReversed.try_emplace(trackCounter, mcTrackIndex); + fMCFlags.try_emplace(mcTrackIndex, mcflags); + ++trackCounter; // fill histograms for each of the signals, if found - if (fConfigHistOutput.fConfigQA) { + if (fConfigHistOutput.cfgQA) { VarManager::FillTrackMC(mcTracks, mctrack); auto mcCollision = mctrack.template mcCollision_as(); - VarManager::FillEvent(mcCollision); + VarManager::FillEvent(mcCollision); + int j = 0; - for (auto signal = fMCSignals.begin(); signal != fMCSignals.end(); signal++, j++) { - if (mcflags & (static_cast(1) << j)) { - fHistMan->FillHistClass(Form("MCTruth_%s", (*signal)->GetName()), VarManager::fgValues); + for (auto signal = fMCSignals.begin(); signal != fMCSignals.end(); ++signal, ++j) { + if ((mcflags & (static_cast(1) << j)) != 0u) { + fHistMan->FillHistClass(Form("MCTruth_%s", (*signal)->GetName()), dqefficiency_helpers::varValues()); } } } @@ -415,36 +426,39 @@ struct Alice3DQTableMaker { // Loop over collisions for (const auto& collision : collisions) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(o2::aod::evsel::kNsel)); VarManager::ResetValues(0, VarManager::kNEventWiseVariables); - VarManager::FillEventAlice3(collision); // extract event information and place it in the fValues array + VarManager::FillEventAlice3(collision); // extract event information and place it in the fValues array if (collision.has_mcCollision()) { auto mcCollision = collision.template mcCollision_as(); - VarManager::FillEventAlice3(mcCollision); + VarManager::FillEventAlice3(mcCollision); } if (fDoDetailedQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_BeforeCuts", dqefficiency_helpers::varValues()); } // Apply the user specified event selection - if (!fEventCut->IsSelected(VarManager::fgValues)) { + if (!fEventCut->IsSelected(dqefficiency_helpers::varValues())) { continue; } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(o2::aod::evsel::kNsel)); // Fill historams after event cuts - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", dqefficiency_helpers::varValues()); event(collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes(), collision.multDensity()); + eventExtended(collision.multNTracksPV(), collision.multNTracksPVeta1(), collision.multNTracksPVetaHalf(), + collision.multNTracksGlobal(), collision.multNGlobalTracksPV(), + collision.centRun2V0M()); + eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); eventMClabels(collision.mcCollisionId(), collision.mcMask()); - eventInfo(collision.globalIndex()); // add an element for this collision into the map fCollIndexMap[collision.globalIndex()] = event.lastIndex(); @@ -461,16 +475,16 @@ struct Alice3DQTableMaker { // so in case of multiple associations, the variables depending on the collision association (e.g. DCA, secondary vertexing, etc) // have to be recomputed at analysis time for each association. - uint64_t trackFilteringTag = static_cast(0); - uint32_t trackTempFilterMap = static_cast(0); - uint16_t mcflags = static_cast(0); + uint64_t trackFilteringTag{0}; + uint32_t trackTempFilterMap{0}; + uint16_t mcflags{0}; int trackCounter = fLabelsMap.size(); for (const auto& assoc : assocs) { auto track = assoc.template track_as(); - if (fCollIndexMap.find(track.collisionId()) == fCollIndexMap.end()) { + if (!fCollIndexMap.contains(track.collisionId())) { continue; } @@ -478,29 +492,29 @@ struct Alice3DQTableMaker { trackTempFilterMap = static_cast(0); // Compute track quantities and fill histograms - VarManager::FillTrackAlice3(track); + VarManager::FillTrackAlice3(track); if (fDoDetailedQA) { - fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", dqefficiency_helpers::varValues()); } - int i = 0; - for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (static_cast(1) << i); - if (fConfigHistOutput.fConfigQA) { - fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), VarManager::fgValues); + int n = 0; + for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, n++) { + if ((*cut)->IsSelected(dqefficiency_helpers::varValues())) { + trackTempFilterMap |= (static_cast(1) << n); + if (fConfigHistOutput.cfgQA) { + fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), dqefficiency_helpers::varValues()); } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(n)); } } - if (!trackTempFilterMap) { + if (trackTempFilterMap == 0u) { continue; } // If this track is already present in the index map, it means it was already skimmed, // so we just store the association and we skip the track - if (fTrackIndexMap.find(track.globalIndex()) != fTrackIndexMap.end()) { + if (fTrackIndexMap.contains(track.globalIndex())) { trackBarrelAssoc(fCollIndexMap[collision.globalIndex()], fTrackIndexMap[track.globalIndex()]); continue; } @@ -512,9 +526,6 @@ struct Alice3DQTableMaker { // In the case of Run2-like analysis, there will be no associations, so this ID will be the one originally assigned in the AO2Ds (updated for the skims) uint32_t reducedEventIdx = fCollIndexMap[track.collisionId()]; - // NOTE: trackBarrelInfo stores the index of the collision as in AO2D (for use in some cases where the analysis on skims is done - // in workflows where the original AO2Ds are also present) - // trackBarrelInfo(track.collisionId(), collision.posX(), collision.posY(), collision.posZ(), track.globalIndex()); trackBasic(reducedEventIdx, trackFilteringTag, track.pt(), track.eta(), track.phi(), track.sign(), 0); trackBarrel(track.x(), track.alpha(), track.y(), track.z(), track.snp(), track.tgl(), track.signed1Pt(), @@ -522,13 +533,13 @@ struct Alice3DQTableMaker { track.isReconstructed(), track.nSiliconHits(), track.nTPCHits(), track.length(), track.dcaXY(), track.dcaZ()); - if constexpr (static_cast(gkTrackFillMapWithCov & VarManager::ObjTypes::TrackCov)) { + if constexpr (static_cast(GkTrackFillMapWithCov & VarManager::ObjTypes::TrackCov)) { trackBarrelCov(track.cYY(), track.cZY(), track.cZZ(), track.cSnpY(), track.cSnpZ(), track.cSnpSnp(), track.cTglY(), track.cTglZ(), track.cTglSnp(), track.cTglTgl(), track.c1PtY(), track.c1PtZ(), track.c1PtSnp(), track.c1PtTgl(), track.c1Pt21Pt2()); } - if constexpr (static_cast(gkTrackFillMapWithCov & VarManager::ObjTypes::TrackPID)) { + if constexpr (static_cast(GkTrackFillMapWithCov & VarManager::ObjTypes::TrackPID)) { trackPIDTOF(track.tofEventTime(), track.tofEventTimeErr(), track.nSigmaElectronInnerTOF(), track.nSigmaMuonInnerTOF(), track.nSigmaPionInnerTOF(), @@ -572,8 +583,8 @@ struct Alice3DQTableMaker { if (fDoDetailedQA) { j = 0; for (const auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { - fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), VarManager::fgValues); // fill the reconstructed truth + if ((trackTempFilterMap & (static_cast(1) << j)) != 0u) { + fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), dqefficiency_helpers::varValues()); // fill the reconstructed truth } j++; } @@ -584,7 +595,7 @@ struct Alice3DQTableMaker { // if the MC truth particle corresponding to this reconstructed track is not already written, // add it to the skimmed stack - if (!(fLabelsMap.find(mctrack.globalIndex()) != fLabelsMap.end())) { + if (!(fLabelsMap.contains(mctrack.globalIndex()))) { fLabelsMap[mctrack.globalIndex()] = trackCounter; fLabelsMapReversed[trackCounter] = mctrack.globalIndex(); fMCFlags[mctrack.globalIndex()] = mcflags; @@ -612,16 +623,16 @@ struct Alice3DQTableMaker { event.reserve(collisions.size()); eventVtxCov.reserve(collisions.size()); eventMClabels.reserve(collisions.size()); - eventInfo.reserve(collisions.size()); skimCollisions(collisions); - if (fCollIndexMap.size() == 0) + if (fCollIndexMap.empty()) { return; + } skimMCParticles(mcParticles, mcCollisions); - if constexpr (static_cast(gkTrackFillMapWithCov)) { + if constexpr (static_cast(GkTrackFillMapWithCov)) { fTrackIndexMap.clear(); trackBasic.reserve(tracksBarrel.size()); trackBarrel.reserve(tracksBarrel.size()); @@ -633,12 +644,12 @@ struct Alice3DQTableMaker { trackBarrelLabels.reserve(tracksBarrel.size()); } - if (fCollIndexMap.size() > 0) { + if (!fCollIndexMap.empty()) { for (auto const& [origIdx, skimIdx] : fCollIndexMap) { auto collision = collisions.rawIteratorAt(origIdx); - if constexpr (static_cast(gkTrackFillMapWithCov)) { + if constexpr (static_cast(GkTrackFillMapWithCov)) { auto groupedTrackIndices = trackAssocs.sliceBy(trackIndicesPerCollision, origIdx); skimTracks(collision, tracksBarrel, groupedTrackIndices, mcParticles); @@ -655,7 +666,7 @@ struct Alice3DQTableMaker { if (mctrack.has_mothers()) { for (const auto& m : mctrack.mothersIds()) { if (m < mcParticles.size()) { // protect against bad mother indices - if (fLabelsMap.find(m) != fLabelsMap.end()) { + if (fLabelsMap.contains(m)) { mothers.push_back(fLabelsMap.find(m)->second); } } else { @@ -672,7 +683,7 @@ struct Alice3DQTableMaker { for (int d = mctrack.daughtersIds()[0]; d <= mctrack.daughtersIds()[1]; ++d) { // TODO: remove this check as soon as issues with MC production are fixed if (d < mcParticles.size()) { // protect against bad daughter indices - if (fLabelsMap.find(d) != fLabelsMap.end()) { + if (fLabelsMap.contains(d)) { daughters.push_back(fLabelsMap.find(d)->second); } } else { @@ -681,8 +692,9 @@ struct Alice3DQTableMaker { } } } - int daughterRange[2] = {-1, -1}; - if (daughters.size() > 0) { + constexpr int NDaughters = 2; + std::array daughterRange = {-1, -1}; + if (!daughters.empty()) { daughterRange[0] = daughters[0]; daughterRange[1] = daughters[daughters.size() - 1]; } @@ -690,17 +702,17 @@ struct Alice3DQTableMaker { // NOTE: Here we assume that MC collisions are not filtered, so there is no new vs old index map for translation auto mcCollision = mctrack.template mcCollision_as(); trackMC(mcCollision.globalIndex(), mctrack.pdgCode(), mctrack.statusCode(), mctrack.flags(), - mothers, daughterRange, + mothers, daughterRange.data(), mctrack.weight(), mctrack.pt(), mctrack.eta(), mctrack.phi(), mctrack.e(), mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { - if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(isig)); + if ((mcflags & (static_cast(1) << isig)) != 0u) { + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(fMCSignals.size())); } } } @@ -712,11 +724,11 @@ struct Alice3DQTableMaker { fullSkimming(collisions, tracksBarrel, trackAssocs, mcCollisions, mcParticles); } - PROCESS_SWITCH(Alice3DQTableMaker, processSkimming, "Build DQ skimmed data model for ALICE3", true); + PROCESS_SWITCH(Alice3DqTableMaker, processSkimming, "Build DQ skimmed data model for ALICE3", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx index 89c34ff1cf3..f502842ef02 100644 --- a/ALICE3/TableProducer/alice3HfSelector3Prong.cxx +++ b/ALICE3/TableProducer/alice3HfSelector3Prong.cxx @@ -93,7 +93,6 @@ struct Alice3HfSelector3Prong { Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - HfHelperAlice3 hfHelper; o2::analysis::HfMlResponse3Prong mlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -114,7 +113,7 @@ struct Alice3HfSelector3Prong { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -236,7 +235,7 @@ struct Alice3HfSelector3Prong { template bool selectionCandidateMass(int const ptBin, const TCandidate& cand) { - const float massCand = hfHelper.getCandMass(cand); + const float massCand = HfHelperAlice3::getCandMass(cand); // cut on mass window if (std::abs(massCand - MassReference) > cuts->get(ptBin, "m")) { return false; diff --git a/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx b/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx index 7a97f1420ff..bc456974afb 100644 --- a/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx +++ b/ALICE3/TableProducer/alice3HfTreeCreator3Prong.cxx @@ -272,8 +272,6 @@ struct Alice3HfTreeCreator3Prong { Configurable downSampleFactor{"downSampleFactor", 1., "Fraction of cands to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - HfHelperAlice3 hfHelper; - using CandsLcRec = soa::Filtered>; using CandsLcRecWMl = soa::Filtered>; using CandsMcGen = soa::Join; @@ -399,7 +397,7 @@ struct Alice3HfTreeCreator3Prong { rowCandKine3Ps( cand.ptProng0(), cand.ptProng1(), cand.ptProng2(), cand.pt(), RecoDecay::p(cand.px(), cand.py(), cand.pz()), cand.eta(), cand.phi(), - hfHelper.getCandMass(cand), hfHelper.getCandY(cand), hfHelper.getCandEnergy(cand)); + HfHelperAlice3::getCandMass(cand), HfHelperAlice3::getCandY(cand), HfHelperAlice3::getCandEnergy(cand)); } if (fillTables.fillDaugKineInfo) { rowDaugMoms( @@ -442,7 +440,7 @@ struct Alice3HfTreeCreator3Prong { { for (const auto& part : parts) { if (fillTables.fillCandGenKineInfo) { - rowCand3PGen(part.pt(), part.eta(), part.phi(), hfHelper.getCandY(part)); + rowCand3PGen(part.pt(), part.eta(), part.phi(), HfHelperAlice3::getCandY(part)); } if (fillTables.fillMcMatchGenInfo) { rowCand3PMcMatchGen(part.flagMcGen(), part.originMcGen()); diff --git a/ALICE3/TableProducer/alice3MulticharmFinder.cxx b/ALICE3/TableProducer/alice3MulticharmFinder.cxx index fd4a7a47881..b3efa1fe9b8 100644 --- a/ALICE3/TableProducer/alice3MulticharmFinder.cxx +++ b/ALICE3/TableProducer/alice3MulticharmFinder.cxx @@ -13,13 +13,15 @@ /// \brief produces table of xicc candidates /// \author Jesper Karlsson Gumprecht -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* +// *+-+*+-+*+-+*+-+*+-+*+-+*+-+* // Decay finder task for ALICE 3 -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* +// *+-+*+-+*+-+*+-+*+-+*+-+*+-+* // // Uses specific ALICE 3 PID and performance for studying // HF decays. Work in progress: use at your own risk! +#include "PWGLF/DataModel/LFStrangenessTables.h" + #include "ALICE3/DataModel/A3DecayFinderTables.h" #include "ALICE3/DataModel/OTFCollision.h" #include "ALICE3/DataModel/OTFMulticharm.h" @@ -67,30 +69,19 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -// simple checkers -// #define biton(var, nbit) ((var) |= (static_cast(1) << (nbit))) -// #define bitoff(var, nbit) ((var) &= ~(static_cast(1) << (nbit))) //((a) &= ~(1ULL<<(b))) -#define BIT_CHECK(var, nbit) ((var) & (static_cast(1) << (nbit))) -#define GET_HIST(type, name) std::get>(histPointers[name]) -#define INSERT_HIST(name, ...) histPointers[name] = histos.add((name).c_str(), __VA_ARGS__); - using Alice3Tracks = soa::Join; using Alice3Collision = soa::Join::iterator; +using FullCascadeCandidates = soa::Join; struct Alice3MulticharmFinder { - SliceCache cache; - Produces multiCharmIdx; Produces multiCharmCore; - Produces multiCharmExtra; - - // Operation and minimisation criteria + SliceCache cache; struct : ConfigurableGroup { std::string prefix = "derivedTable"; // JSON group name Configurable fillMCharmIdx{"fillMCharmIdx", true, "fill MCharmIdx[] tables (careful: memory)"}; Configurable fillMCharmCore{"fillMCharmCore", true, "fill MCharmCores[] tables (careful: memory)"}; - Configurable fillMCharmExtra{"fillMCharmExtra", false, "fill MCharmExtra[] tables (careful: memory)"}; } derivedTable; struct : ConfigurableGroup { @@ -109,44 +100,104 @@ struct Alice3MulticharmFinder { Configurable cfgMagneticField{"cfgMagneticField", 20.0f, "Magnetic field (in kilogauss) if value not found from geo provider"}; Configurable doDCAplots{"doDCAplots", true, "do daughter prong DCA plots for D mesons"}; Configurable mcSameMotherCheck{"mcSameMotherCheck", true, "check if tracks come from the same MC mother"}; - Configurable> minNTracks{"minNTracks", {-1}, "Minimum number of tracks"}; - - Configurable xiMinConstDCAxy{"xiMinConstDCAxy", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable xiMinConstDCAz{"xiMinConstDCAz", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable xiMinPtDepDCAxy{"xiMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; - Configurable xiMinPtDepDCAz{"xiMinPtDepDCAz", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; - Configurable xiMinDecayRadius{"xiMinDecayRadius", 0.5, "Minimum R2D for XiC decay (cm)"}; - Configurable xiMassWindow{"xiMassWindow", 0.005, "Mass window around Xi peak (GeV/c)"}; - - Configurable picMinConstDCAxy{"picMinConstDCAxy", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable picMinConstDCAz{"picMinConstDCAz", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable picMinPtDepDCAxy{"picMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; - Configurable picMinPtDepDCAz{"picMinPtDepDCAz", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; - Configurable picMinPt{"picMinPt", 0.15, "Minimum pT for XiC pions"}; - - Configurable xicMaxDauDCA{"xicMaxDauDCA", 0.005f, "DCA between XiC daughters (cm)"}; - Configurable xicMaxDCAxy{"xicMaxDCAxy", 0.0005f, "maxDCA"}; - Configurable xicMaxDCAz{"xicMaxDCAz", 0.0005f, "maxDCA"}; - Configurable xicMinDecayRadius{"xicMinDecayRadius", -1, "Minimum R2D for XiC decay (cm)"}; - Configurable xicMinDecayDistanceFromPV{"xicMinDecayDistanceFromPV", -1, "Minimum distance for XiC decay from PV (cm)"}; - Configurable xicMinProperLength{"xicMinProperLength", 0.002, "Minimum proper length for XiC decay (cm)"}; - Configurable xicMaxProperLength{"xicMaxProperLength", 0.1, "Minimum proper length for XiC decay (cm)"}; - Configurable xicMassWindow{"xicMassWindow", 0.012, "Mass window around XiC peak (GeV/c)"}; - - Configurable piccMinConstDCAxy{"piccMinConstDCAxy", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable piccMinConstDCAz{"piccMinConstDCAz", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable piccMinPtDepDCAxy{"piccMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; - Configurable piccMinPtDepDCAz{"piccMinPtDepDCAz", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; - Configurable piccMinPt{"piccMinPt", 0.3, "Minimum pT for XiCC pions"}; - - Configurable xiccMaxDauDCA{"xiccMaxDauDCA", 0.005f, "DCA between XiCC daughters (cm)"}; - Configurable xiccMaxDCAxy{"xiccMaxDCAxy", 0.005f, "maxDCA"}; - Configurable xiccMaxDCAz{"xiccMaxDCAz", 0.005f, "maxDCA"}; - Configurable xiccMaxEta{"xiccMaxEta", 1.5, "Max eta"}; - Configurable xiccMinDecayRadius{"xiccMinDecayRadius", -1, "Minimum R2D for XiCC decay (cm)"}; - Configurable xiccMinProperLength{"xiccMinProperLength", -1, "Minimum proper length for XiCC decay (cm)"}; - Configurable xiccMaxProperLength{"xiccMaxProperLength", 999, "Minimum proper length for XiCC decay (cm)"}; - Configurable xiccMassWindow{"xiccMassWindow", 0.25, "Mass window around XiCC peak (GeV/c). Make sure that bkg region is included in this window"}; + + struct : ConfigurableGroup { + std::string prefix = "selVals"; + Configurable posMinDCAxy{"posMinDCAxy", 0.005, "min dcaxy for positive"}; + Configurable negMinDCAxy{"negMinDCAxy", 0.005, "min dcaxy for negative"}; + Configurable bachMinDCAxy{"bachMinDCAxy", 0.005, "min dcaxy for bachelor"}; + + Configurable laMinCosPA{"laMinCosPA", 0.998, "Minimum cos(PA)"}; + Configurable laMinDecayRadius{"laMinDecayRadius", 0.5, "Minimum R2D for la decay (cm)"}; + Configurable laMassWindow{"laMassWindow", 0.012, "Mass window around La peak (GeV/c^2)"}; + Configurable laMaxDauDCA{"laMaxDauDCA", 1, "DCA between Xi daughters (cm)"}; + + Configurable xiMinConstDCAxy{"xiMinConstDCAxy", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable xiMinConstDCAz{"xiMinConstDCAz", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable xiMinPtDepDCAxy{"xiMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable xiMinPtDepDCAz{"xiMinPtDepDCAz", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable xiMinDecayRadius{"xiMinDecayRadius", 0.5, "Minimum R2D for XiC decay (cm)"}; + Configurable xiMassWindow{"xiMassWindow", 0.005, "Mass window around Xi peak (GeV/c^2)"}; + Configurable xiMaxDauDCA{"xiMaxDauDCA", 1, "DCA between Xi daughters (cm)"}; + Configurable xiMaxNormalizedDecayLength{"xiMaxNormalizedDecayLength", 5, "Max cascade nomralized decay length (ctau/)"}; + Configurable xiMinCosPA{"xiMinCosPA", 0.980000019, "Minimum cos(PA)"}; + + Configurable picMinConstDCAxy{"picMinConstDCAxy", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable picMinConstDCAz{"picMinConstDCAz", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable picMinPtDepDCAxy{"picMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable picMinPtDepDCAz{"picMinPtDepDCAz", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable picMinPt{"picMinPt", 0.15, "Minimum pT for XiC pions"}; + + Configurable xicMaxDauDCA{"xicMaxDauDCA", 0.005f, "DCA between XiC daughters (cm)"}; + Configurable xicMinDCAxy{"xicMinDCAxy", 0.0005f, "Minimum DCA"}; + Configurable xicMinDCAz{"xicMinDCAz", 0.0005f, "Minimum DCA"}; + Configurable xicMinDecayRadius{"xicMinDecayRadius", -1, "Minimum R2D for XiC decay (cm)"}; + Configurable xicMinDecayDistanceFromPV{"xicMinDecayDistanceFromPV", -1, "Minimum distance for XiC decay from PV (cm)"}; + Configurable xicMinProperLength{"xicMinProperLength", 0.002, "Minimum proper length for XiC decay (cm)"}; + Configurable xicMaxProperLength{"xicMaxProperLength", 0.1, "Minimum proper length for XiC decay (cm)"}; + Configurable xicMassWindow{"xicMassWindow", 0.012, "Mass window around XiC peak (GeV/c^2)"}; + + Configurable piccMinConstDCAxy{"piccMinConstDCAxy", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable piccMinConstDCAz{"piccMinConstDCAz", 0.0005f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable piccMinPtDepDCAxy{"piccMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable piccMinPtDepDCAz{"piccMinPtDepDCAz", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable piccMinPt{"piccMinPt", 0.3, "Minimum pT for XiCC pions"}; + + Configurable xiccMaxDauDCA{"xiccMaxDauDCA", 0.005f, "DCA between XiCC daughters (cm)"}; + Configurable xiccMaxDCAxy{"xiccMaxDCAxy", 0.005f, "maxDCA"}; + Configurable xiccMaxDCAz{"xiccMaxDCAz", 0.005f, "maxDCA"}; + Configurable xiccMaxEta{"xiccMaxEta", 1.5, "Max eta"}; + Configurable xiccMinDecayRadius{"xiccMinDecayRadius", -1, "Minimum R2D for XiCC decay (cm)"}; + Configurable xiccMinProperLength{"xiccMinProperLength", -1, "Minimum proper length for XiCC decay (cm)"}; + Configurable xiccMaxProperLength{"xiccMaxProperLength", 999, "Minimum proper length for XiCC decay (cm)"}; + Configurable xiccMassWindow{"xiccMassWindow", 0.25, "Mass window around XiCC peak (GeV/c). Make sure that bkg region is included in this window"}; + } selVals; + + struct : ConfigurableGroup { + std::string prefix = "selFlags"; + Configurable applyPosMinDCAxy{"applyPosMinDCAxy", false, "Apply min dcaxy for positive"}; + Configurable applyNegMinDCAxy{"applyNegMinDCAxy", false, "Apply min dcaxy for negative"}; + Configurable applyBachMinDCAxy{"applyBachMinDCAxy", false, "Apply min dcaxy for bachelor"}; + + Configurable applyLaMinCosPA{"applyLaMinCosPA", false, "Apply Minimum cos(PA)"}; + Configurable applyLaMinDecayRadius{"applyLaMinDecayRadius", false, "Apply Minimum R2D for la decay (cm)"}; + Configurable applyLaMassWindow{"applyLaMassWindow", true, "Apply Mass window around La peak (GeV/c^2)"}; + Configurable applyLaMaxDauDCA{"applyLaMaxDauDCA", false, "Apply DCA between Xi daughters (cm)"}; + + Configurable applyXiMinDCAxy{"applyXiMinDCAxy", true, "Apply [0] in |DCAxy| > [0]+[1]/pT"}; + Configurable applyXiMinDCAz{"applyXiMinDCAz", true, "Apply [0] in |DCAxy| > [0]+[1]/pT"}; + Configurable applyXiMinDecayRadius{"applyXiMinDecayRadius", true, "Apply Minimum R2D for XiC decay (cm)"}; + Configurable applyXiMassWindow{"applyXiMassWindow", true, "Apply Mass window around Xi peak (GeV/c^2)"}; + Configurable applyXiMaxDauDCA{"applyXiMaxDauDCA", false, "Apply DCA between Xi daughters (cm)"}; + Configurable applyXiMaxNormalizedDecayLength{"applyXiMaxNormalizedDecayLength", true, "Apply Max cascade nomralized decay length (ctau/)"}; + Configurable applyXiMinCosPA{"applyXiMinCosPA", false, "Apply Minimum cos(PA)"}; + + Configurable applyPicMinDCAxy{"applyPicMinDCAxy", true, "Apply [0] in |DCAxy| > [0]+[1]/pT"}; + Configurable applyPicMinDCAz{"applyPicMinDCAz", true, "Apply [0] in |DCAxy| > [0]+[1]/pT"}; + Configurable applyPicMinPt{"applyPicMinPt", true, "Apply Minimum pT for XiC pions"}; + + Configurable applyXicMaxDauDCA{"applyXicMaxDauDCA", true, "Apply DCA between XiC daughters (cm)"}; + Configurable applyXicMinDCAxy{"applyXicMinDCAxy", true, "Apply Minimum DCA"}; + Configurable applyXicMinDCAz{"applyXicMinDCAz", true, "Apply Minimum DCA"}; + Configurable applyXicMinDecayRadius{"applyXicMinDecayRadius", false, "Apply Minimum R2D for XiC decay (cm)"}; + Configurable applyXicMinDecayDistanceFromPV{"applyXicMinDecayDistanceFromPV", true, "Apply Minimum distance for XiC decay from PV (cm)"}; + Configurable applyXicMinProperLength{"applyXicMinProperLength", false, "Apply Minimum proper length for XiC decay (cm)"}; + Configurable applyXicMaxProperLength{"applyXicMaxProperLength", false, "Apply Minimum proper length for XiC decay (cm)"}; + Configurable applyXicMassWindow{"applyXicMassWindow", true, "Apply Mass window around XiC peak (GeV/c^2)"}; + + Configurable applyPiccMinDCAxy{"applyPiccMinDCAxy", true, "Apply [0] in |DCAxy| > [0]+[1]/pT"}; + Configurable applyPiccMinDCAz{"applyPiccMinDCAz", true, "Apply [0] in |DCAxy| > [0]+[1]/pT"}; + Configurable applyPiccMinPt{"applyPiccMinPt", true, "Apply Minimum pT for XiCC pions"}; + + Configurable applyXiccMaxDauDCA{"applyXiccMaxDauDCA", true, "Apply DCA between XiCC daughters (cm)"}; + Configurable applyXiccMaxDCAxy{"applyXiccMaxDCAxy", true, "Apply maxDCA"}; + Configurable applyXiccMaxDCAz{"applyXiccMaxDCAz", true, "Apply maxDCA"}; + Configurable applyXiccMaxEta{"applyXiccMaxEta", true, "Apply Max eta"}; + Configurable applyXiccMinDecayRadius{"applyXiccMinDecayRadius", false, "Apply Minimum R2D for XiCC decay (cm)"}; + Configurable applyXiccMinProperLength{"applyXiccMinProperLength", false, "Apply Minimum proper length for XiCC decay (cm)"}; + Configurable applyXiccMaxProperLength{"applyXiccMaxProperLength", false, "Apply Minimum proper length for XiCC decay (cm)"}; + Configurable applyXiccMassWindow{"applyXiccMassWindow", true, "Apply Mass window around XiCC peak (GeV/c). Make sure that bkg region is included in this window"}; + } selFlags; ConfigurableAxis axisEta{"axisEta", {80, -4.0f, +4.0f}, "#eta"}; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; @@ -172,6 +223,7 @@ struct Alice3MulticharmFinder { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; std::map histPointers; + std::string histPath; std::vector savedConfigs; // Constants @@ -187,47 +239,69 @@ struct Alice3MulticharmFinder { Partition trueXiC = aod::mcparticle::pdgCode == static_cast(o2::constants::physics::kXiCPlus); Partition trueXiCC = aod::mcparticle::pdgCode == static_cast(o2::constants::physics::kXiCCPlusPlus); + const float picMinConstDCAxy = selFlags.applyPicMinDCAxy ? selVals.picMinConstDCAxy : 0; + const float picMinPtDepDCAxy = selFlags.applyPicMinDCAxy ? selVals.picMinPtDepDCAxy : 0; + const float picMinConstDCAz = selFlags.applyPicMinDCAz ? selVals.picMinConstDCAz : 0; + const float picMinPtDepDCAz = selFlags.applyPicMinDCAz ? selVals.picMinPtDepDCAz : 0; + const float piccMinConstDCAxy = selFlags.applyPiccMinDCAxy ? selVals.piccMinConstDCAxy : 0; + const float piccMinPtDepDCAxy = selFlags.applyPiccMinDCAxy ? selVals.piccMinPtDepDCAxy : 0; + const float piccMinConstDCAz = selFlags.applyPiccMinDCAz ? selVals.piccMinConstDCAz : 0; + const float piccMinPtDepDCAz = selFlags.applyPiccMinDCAz ? selVals.piccMinPtDepDCAz : 0; + Partition picTracks = - ((aod::a3DecayMap::decayMap & TrackSelectionPic) == TrackSelectionPic) && aod::track::signed1Pt > 0.0f && 1.0f / nabs(aod::track::signed1Pt) > picMinPt&& nabs(aod::track::dcaXY) > picMinConstDCAxy + picMinPtDepDCAxy* nabs(aod::track::signed1Pt) && nabs(aod::track::dcaZ) > picMinConstDCAz + picMinPtDepDCAz* nabs(aod::track::signed1Pt); + ((aod::a3DecayMap::decayMap & TrackSelectionPic) == TrackSelectionPic) && aod::track::signed1Pt > 0.0f && nabs(aod::track::dcaXY) > picMinConstDCAxy + picMinPtDepDCAxy* nabs(aod::track::signed1Pt) && nabs(aod::track::dcaZ) > picMinConstDCAz + picMinPtDepDCAz* nabs(aod::track::signed1Pt); Partition piccTracks = - ((aod::a3DecayMap::decayMap & TrackSelectionPicc) == TrackSelectionPicc) && aod::track::signed1Pt > 0.0f && 1.0f / nabs(aod::track::signed1Pt) > piccMinPt&& nabs(aod::track::dcaXY) > piccMinConstDCAxy + piccMinPtDepDCAxy* nabs(aod::track::signed1Pt) && nabs(aod::track::dcaZ) > piccMinConstDCAz + piccMinPtDepDCAz* nabs(aod::track::signed1Pt); + ((aod::a3DecayMap::decayMap & TrackSelectionPicc) == TrackSelectionPicc) && aod::track::signed1Pt > 0.0f && nabs(aod::track::dcaXY) > piccMinConstDCAxy + piccMinPtDepDCAxy* nabs(aod::track::signed1Pt) && nabs(aod::track::dcaZ) > piccMinConstDCAz + piccMinPtDepDCAz* nabs(aod::track::signed1Pt); - // Helper struct to pass candidate information + // Helper structs to pass candidate information struct { - // decay properties - float dca; - float mass; - float pt; - float eta; - std::array xyz; - std::array prong0mom; - std::array prong1mom; - std::array prong2mom; - std::array parentTrackCovMatrix; + float radius{}; + float pt{}; + float eta{}; + o2::track::TrackParCov trackParCov; + } thisXiCandidate; + + struct { + float dca{}; + float mass{}; + float pt{}; + float eta{}; + std::array xyz{}; + std::array prong0mom{}; + std::array prong1mom{}; + std::array prong2mom{}; + std::array parentTrackCovMatrix{}; } thisXicCandidate; struct { - float dca; - float mass; - float pt; - float eta; - std::array xyz; - std::array prong0mom; - std::array prong1mom; - std::array parentTrackCovMatrix; - - // charm daughters - int nSiliconHitsPiCC; - int nTPCHitsPiCC; + float dca{}; + float mass{}; + float pt{}; + float eta{}; + std::array xyz{}; + std::array prong0mom{}; + std::array prong1mom{}; + std::array parentTrackCovMatrix{}; } thisXiccCandidate; - struct ProngInfo { - float pt = 1e+10; - float eta = 1e+10; - float dcaXY = 1e+10; - float dcaZ = 1e+10; - }; + template + std::shared_ptr& hist(const std::string& name) + { + return std::get>(histPointers[histPath + name]); + } + + template + void insertHist(const std::string& name, const std::string& title, HistType type, const std::vector& axes) + { + auto fullName = histPath + name; + histPointers[fullName] = histos.add(fullName.c_str(), title.c_str(), type, axes); + } + + bool bitCheck(uint32_t var, uint32_t nbit) + { + return (var & (static_cast(1) << nbit)) != 0; + } template bool buildDecayCandidateTwoBody(TTrackType const& t0, TTrackType const& t1, float mass0, float mass1) @@ -289,13 +363,13 @@ struct Alice3MulticharmFinder { // set relevant values thisXiccCandidate.dca = std::sqrt(fitter.getChi2AtPCACandidate()); - if (thisXiccCandidate.dca > xiccMaxDauDCA) { + if (selFlags.applyXiccMaxDauDCA && thisXiccCandidate.dca > selVals.xiccMaxDauDCA) { return false; } thisXiccCandidate.mass = RecoDecay::m(std::array{std::array{thisXiccCandidate.prong0mom[0], thisXiccCandidate.prong0mom[1], thisXiccCandidate.prong0mom[2]}, std::array{thisXiccCandidate.prong1mom[0], thisXiccCandidate.prong1mom[1], thisXiccCandidate.prong1mom[2]}}, std::array{mass0, mass1}); - if (std::fabs(thisXiccCandidate.mass - o2::constants::physics::MassXiCCPlusPlus) > xiccMassWindow) { + if (selFlags.applyXiccMassWindow && std::fabs(thisXiccCandidate.mass - o2::constants::physics::MassXiCCPlusPlus) > selVals.xiccMassWindow) { return false; } @@ -305,12 +379,8 @@ struct Alice3MulticharmFinder { } template - bool buildDecayCandidateThreeBody(TTrackType1 const& prong0, TTrackType2 const& prong1, TTrackType3 const& prong2, float p0mass, float p1mass, float p2mass) + bool buildDecayCandidateThreeBody(TTrackType1 t0, TTrackType2 t1, TTrackType3 t2, float p0mass, float p1mass, float p2mass) { - o2::track::TrackParCov t0 = getTrackParCov(prong0); - o2::track::TrackParCov t1 = getTrackParCov(prong1); - o2::track::TrackParCov t2 = getTrackParCov(prong2); - //}-{}-{}-{}-{}-{}-{}-{}-{}-{} // Move close to minima int nCand = 0; @@ -372,7 +442,7 @@ struct Alice3MulticharmFinder { // set relevant values thisXicCandidate.dca = std::sqrt(fitter3.getChi2AtPCACandidate()); - if (thisXicCandidate.dca > xicMaxDauDCA) { + if (selFlags.applyXicMaxDauDCA && thisXicCandidate.dca > selVals.xicMaxDauDCA) { return false; } thisXicCandidate.mass = RecoDecay::m(std::array{std::array{thisXicCandidate.prong0mom[0], thisXicCandidate.prong0mom[1], thisXicCandidate.prong0mom[2]}, std::array{thisXicCandidate.prong1mom[0], thisXicCandidate.prong1mom[1], thisXicCandidate.prong1mom[2]}, std::array{thisXicCandidate.prong2mom[0], thisXicCandidate.prong2mom[1], thisXicCandidate.prong2mom[2]}}, std::array{p0mass, p1mass, p2mass}); @@ -446,12 +516,13 @@ struct Alice3MulticharmFinder { fitter.setBz(cfgMagneticField); fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); + // initialize O2 3-prong fitter (only once) fitter3.setPropagateToPCA(cfgFitter.propagateToPCA); fitter3.setMaxR(cfgFitter.maxR); fitter3.setMinParamChange(cfgFitter.minParamChange); fitter3.setMinRelChi2Change(cfgFitter.minRelChi2Change); fitter3.setMaxDZIni(cfgFitter.maxDZIni); - fitter3.setMaxDZIni(cfgFitter.maxDXYIni); + fitter3.setMaxDXYIni(cfgFitter.maxDXYIni); fitter3.setMaxChi2(cfgFitter.maxVtxChi2); fitter3.setUseAbsDCA(cfgFitter.useAbsDCA); fitter3.setWeightedFinalPCA(cfgFitter.useWeightedFinalPCA); @@ -500,9 +571,9 @@ struct Alice3MulticharmFinder { hFitter3StatusCode->GetXaxis()->SetBinLabel(14, "FailCloserAlt"); // alternative PCA is closer hFitter3StatusCode->GetXaxis()->SetBinLabel(15, "NStatusesDefined"); - INSERT_HIST(std::string("h2dGenXi"), "h2dGenXi", {kTH2D, {{axisPt, axisEta}}}); - INSERT_HIST(std::string("h2dGenXiC"), "h2dGenXiC", {kTH2D, {{axisPt, axisEta}}}); - INSERT_HIST(std::string("h2dGenXiCC"), "h2dGenXiCC", {kTH2D, {{axisPt, axisEta}}}); + histos.add("h2dGenXi", "h2dGenXi", kTH2D, {{axisPt, axisEta}}); + histos.add("h2dGenXiC", "h2dGenXiC", kTH2D, {{axisPt, axisEta}}); + histos.add("h2dGenXiCC", "h2dGenXiCC", kTH2D, {{axisPt, axisEta}}); } void initDetectorConfiguration(const int icfg) @@ -512,81 +583,72 @@ struct Alice3MulticharmFinder { } savedConfigs.push_back(icfg); - const std::string histPath = "Configuration_" + std::to_string(icfg) + "/"; - - // This histogram bookkeeps the attempts at DCA minimization and their eventual - // failure rates. - // --- 0: attempt XiC, 1: success XiC - // --- 2: attempt XiCC, 3: success XiCC - INSERT_HIST(histPath + "hCharmBuilding", "hCharmBuilding", {kTH1D, {{10, -0.5, 9.5f}}}); - INSERT_HIST(histPath + "hMultiCharmBuilding", "hMultiCharmBuilding", {kTH1D, {{10, -0.5, 9.5f}}}); + insertHist("hCharmBuilding", "hCharmBuilding", kTH1D, {{10, -0.5, 9.5f}}); + insertHist("hMultiCharmBuilding", "hMultiCharmBuilding", kTH1D, {{10, -0.5, 9.5f}}); - INSERT_HIST(histPath + "hMassXi", "hMassXi", {kTH1D, {{axisXiMass}}}); - INSERT_HIST(histPath + "hMassXiC", "hMassXiC", {kTH1D, {{axisXiCMass}}}); + insertHist("hMassXi", "hMassXi", kTH1D, {{axisXiMass}}); + insertHist("hMassXiC", "hMassXiC", kTH1D, {{axisXiCMass}}); - INSERT_HIST(histPath + "hEtaXiCC", "hEtaXiCC", {kTH1D, {{axisEta}}}); - INSERT_HIST(histPath + "hPtXiCC", "hPtXiCC", {kTH1D, {{axisPt}}}); - INSERT_HIST(histPath + "h3dMassXiCC", "h3dMassXiCC", {kTH3D, {{axisPt, axisEta, axisXiCCMass}}}); + insertHist("hEtaXiCC", "hEtaXiCC", kTH1D, {{axisEta}}); + insertHist("hPtXiCC", "hPtXiCC", kTH1D, {{axisPt}}); + insertHist("h3dMassXiCC", "h3dMassXiCC", kTH3D, {{axisPt, axisEta, axisXiCCMass}}); - INSERT_HIST(histPath + "hDCAXiCDaughters", "hDCAXiCDaughters", {kTH1D, {{axisDCAXiCDaughters}}}); - INSERT_HIST(histPath + "hDCAXiCCDaughters", "hDCAXiCCDaughters", {kTH1D, {{axisDCAXiCCDaughters}}}); - INSERT_HIST(histPath + "hDCAxyXi", "hDCAxyXi", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hDCAzXi", "hDCAzXi", {kTH1D, {{axisDCA}}}); + insertHist("hDCAXiCDaughters", "hDCAXiCDaughters", kTH1D, {{axisDCAXiCDaughters}}); + insertHist("hDCAXiCCDaughters", "hDCAXiCCDaughters", kTH1D, {{axisDCAXiCCDaughters}}); + insertHist("hDCAxyXi", "hDCAxyXi", kTH1D, {{axisDCA}}); + insertHist("hDCAzXi", "hDCAzXi", kTH1D, {{axisDCA}}); - INSERT_HIST(histPath + "hDCAxyXiC", "hDCAxyXiC", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hDCAzXiC", "hDCAzXiC", {kTH1D, {{axisDCA}}}); + insertHist("hDCAxyXiC", "hDCAxyXiC", kTH1D, {{axisDCA}}); + insertHist("hDCAzXiC", "hDCAzXiC", kTH1D, {{axisDCA}}); - INSERT_HIST(histPath + "hDCAxyXiCC", "hDCAxyXiCC", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hDCAzXiCC", "hDCAzXiCC", {kTH1D, {{axisDCA}}}); + insertHist("hDCAxyXiCC", "hDCAxyXiCC", kTH1D, {{axisDCA}}); + insertHist("hDCAzXiCC", "hDCAzXiCC", kTH1D, {{axisDCA}}); - INSERT_HIST(histPath + "hPi1cPt", "hPi1cPt", {kTH1D, {{axisPt}}}); - INSERT_HIST(histPath + "hPi2cPt", "hPi2cPt", {kTH1D, {{axisPt}}}); - INSERT_HIST(histPath + "hPiccPt", "hPiccPt", {kTH1D, {{axisPt}}}); + insertHist("hPi1cPt", "hPi1cPt", kTH1D, {{axisPt}}); + insertHist("hPi2cPt", "hPi2cPt", kTH1D, {{axisPt}}); + insertHist("hPiccPt", "hPiccPt", kTH1D, {{axisPt}}); - INSERT_HIST(histPath + "hPi1cDCAxy", "hPi1cDCAxy", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hPi1cDCAz", "hPi1cDCAz", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hPi2cDCAxy", "hPi2cDCAxy", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hPi2cDCAz", "hPi2cDCAz", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hPiccDCAxy", "hPiccDCAxy", {kTH1D, {{axisDCA}}}); - INSERT_HIST(histPath + "hPiccDCAz", "hPiccDCAz", {kTH1D, {{axisDCA}}}); + insertHist("hPi1cDCAxy", "hPi1cDCAxy", kTH1D, {{axisDCA}}); + insertHist("hPi1cDCAz", "hPi1cDCAz", kTH1D, {{axisDCA}}); + insertHist("hPi2cDCAxy", "hPi2cDCAxy", kTH1D, {{axisDCA}}); + insertHist("hPi2cDCAz", "hPi2cDCAz", kTH1D, {{axisDCA}}); + insertHist("hPiccDCAxy", "hPiccDCAxy", kTH1D, {{axisDCA}}); + insertHist("hPiccDCAz", "hPiccDCAz", kTH1D, {{axisDCA}}); - INSERT_HIST(histPath + "hMinXiDecayRadius", "hMinXiDecayRadius", {kTH1D, {{axisRadius2DXi}}}); - INSERT_HIST(histPath + "hMinXiCDecayRadius", "hMinXiCDecayRadius", {kTH1D, {{axisRadius}}}); - INSERT_HIST(histPath + "hMinXiCCDecayRadius", "hMinXiCCDecayRadius", {kTH1D, {{axisRadius}}}); + insertHist("hMinXiDecayRadius", "hMinXiDecayRadius", kTH1D, {{axisRadius2DXi}}); + insertHist("hMinXiCDecayRadius", "hMinXiCDecayRadius", kTH1D, {{axisRadius}}); + insertHist("hMinXiCCDecayRadius", "hMinXiCCDecayRadius", kTH1D, {{axisRadius}}); - INSERT_HIST(histPath + "hMinXicDecayDistanceFromPV", "hMinXicDecayDistanceFromPV", {kTH1D, {{axisDecayLength}}}); - INSERT_HIST(histPath + "hProperLengthXiC", "hProperLengthXiC", {kTH1D, {{axisDecayLength}}}); - INSERT_HIST(histPath + "hProperLengthXiCC", "hProperLengthXiCC", {kTH1D, {{axisDecayLength}}}); + insertHist("hMinXicDecayDistanceFromPV", "hMinXicDecayDistanceFromPV", kTH1D, {{axisDecayLength}}); + insertHist("hProperLengthXiC", "hProperLengthXiC", kTH1D, {{axisDecayLength}}); + insertHist("hProperLengthXiCC", "hProperLengthXiCC", kTH1D, {{axisDecayLength}}); - INSERT_HIST(histPath + "hInnerTOFTrackTimeRecoPi1c", "hInnerTOFTrackTimeRecoPi1c", {kTH1D, {{axisTOFTrack}}}); - INSERT_HIST(histPath + "hInnerTOFTrackTimeRecoPi2c", "hInnerTOFTrackTimeRecoPi2c", {kTH1D, {{axisTOFTrack}}}); - INSERT_HIST(histPath + "hInnerTOFTrackTimeRecoPicc", "hInnerTOFTrackTimeRecoPicc", {kTH1D, {{axisTOFTrack}}}); + insertHist("hInnerTOFTrackTimeRecoPi1c", "hInnerTOFTrackTimeRecoPi1c", kTH1D, {{axisTOFTrack}}); + insertHist("hInnerTOFTrackTimeRecoPi2c", "hInnerTOFTrackTimeRecoPi2c", kTH1D, {{axisTOFTrack}}); + insertHist("hInnerTOFTrackTimeRecoPicc", "hInnerTOFTrackTimeRecoPicc", kTH1D, {{axisTOFTrack}}); - INSERT_HIST(histPath + "hOuterTOFTrackTimeRecoPi1c", "hOuterTOFTrackTimeRecoPi1c", {kTH1D, {{axisTOFTrack}}}); - INSERT_HIST(histPath + "hOuterTOFTrackTimeRecoPi2c", "hOuterTOFTrackTimeRecoPi2c", {kTH1D, {{axisTOFTrack}}}); - INSERT_HIST(histPath + "hOuterTOFTrackTimeRecoPicc", "hOuterTOFTrackTimeRecoPicc", {kTH1D, {{axisTOFTrack}}}); + insertHist("hOuterTOFTrackTimeRecoPi1c", "hOuterTOFTrackTimeRecoPi1c", kTH1D, {{axisTOFTrack}}); + insertHist("hOuterTOFTrackTimeRecoPi2c", "hOuterTOFTrackTimeRecoPi2c", kTH1D, {{axisTOFTrack}}); + insertHist("hOuterTOFTrackTimeRecoPicc", "hOuterTOFTrackTimeRecoPicc", kTH1D, {{axisTOFTrack}}); - INSERT_HIST(histPath + "hXiRadiusVsXicRadius", "hXiRadiusVsXicRadius", {kTH2D, {{axisRadius2D, axisRadius2D}}}); - INSERT_HIST(histPath + "hXicRadiusVsXiccRadius", "hXicRadiusVsXiccRadius", {kTH2D, {{axisRadius2D, axisRadius2D}}}); + insertHist("hXiRadiusVsXicRadius", "hXiRadiusVsXicRadius", kTH2D, {{axisRadius2D, axisRadius2D}}); + insertHist("hXicRadiusVsXiccRadius", "hXicRadiusVsXiccRadius", kTH2D, {{axisRadius2D, axisRadius2D}}); - INSERT_HIST(histPath + "hMassXiCC", "hMassXiCC", {kTH1D, {{axisXiCCMass}}}); - INSERT_HIST(histPath + "hNCollisions", "hNCollisions", {kTH1D, {{2, 0.5, 2.5}}}); - INSERT_HIST(histPath + "hNTracks", "hNTracks", {kTH1D, {{20000, 0, 20000}}}); + insertHist("hMassXiCC", "hMassXiCC", kTH1D, {{axisXiCCMass}}); + insertHist("hNCollisions", "hNCollisions", kTH1D, {{2, 0.5, 2.5}}); + insertHist("hNTracks", "hNTracks", kTH1D, {{20000, 0, 20000}}); - // These histograms bookkeep the exact number of combinations attempted - // CombinationsXiC: triplets Xi-pi-pi considered per Xi - // CombinationsXiCC: doublets XiC-pi considered per XiC - INSERT_HIST(histPath + "hCombinationsXiC", "hCombinationsXiC", {kTH1D, {{axisNConsidered}}}); - INSERT_HIST(histPath + "hCombinationsXiCC", "hCombinationsXiCC", {kTH1D, {{axisNConsidered}}}); + insertHist("hCombinationsXiC", "hCombinationsXiC", kTH1D, {{axisNConsidered}}); + insertHist("hCombinationsXiCC", "hCombinationsXiCC", kTH1D, {{axisNConsidered}}); if (doDCAplots) { - INSERT_HIST(histPath + "h2dDCAxyVsPtXiFromXiC", "h2dDCAxyVsPtXiFromXiC", {kTH2D, {{axisPt, axisDCA2D}}}); - INSERT_HIST(histPath + "h2dDCAxyVsPtPiFromXiC", "h2dDCAxyVsPtPiFromXiC", {kTH2D, {{axisPt, axisDCA2D}}}); - INSERT_HIST(histPath + "h2dDCAxyVsPtPiFromXiCC", "h2dDCAxyVsPtPiFromXiCC", {kTH2D, {{axisPt, axisDCA2D}}}); + insertHist("h2dDCAxyVsPtXiFromXiC", "h2dDCAxyVsPtXiFromXiC", kTH2D, {{axisPt, axisDCA2D}}); + insertHist("h2dDCAxyVsPtPiFromXiC", "h2dDCAxyVsPtPiFromXiC", kTH2D, {{axisPt, axisDCA2D}}); + insertHist("h2dDCAxyVsPtPiFromXiCC", "h2dDCAxyVsPtPiFromXiCC", kTH2D, {{axisPt, axisDCA2D}}); - INSERT_HIST(histPath + "h2dDCAzVsPtXiFromXiC", "h2dDCAzVsPtXiFromXiC", {kTH2D, {{axisPt, axisDCA2D}}}); - INSERT_HIST(histPath + "h2dDCAzVsPtPiFromXiC", "h2dDCAzVsPtPiFromXiC", {kTH2D, {{axisPt, axisDCA2D}}}); - INSERT_HIST(histPath + "h2dDCAzVsPtPiFromXiCC", "h2dDCAzVsPtPiFromXiCC", {kTH2D, {{axisPt, axisDCA2D}}}); + insertHist("h2dDCAzVsPtXiFromXiC", "h2dDCAzVsPtXiFromXiC", kTH2D, {{axisPt, axisDCA2D}}); + insertHist("h2dDCAzVsPtPiFromXiC", "h2dDCAzVsPtPiFromXiC", kTH2D, {{axisPt, axisDCA2D}}); + insertHist("h2dDCAzVsPtPiFromXiCC", "h2dDCAzVsPtPiFromXiCC", kTH2D, {{axisPt, axisDCA2D}}); } } @@ -594,343 +656,416 @@ struct Alice3MulticharmFinder { void processGenerated(aod::McParticles const&) { for (auto const& mcParticle : trueXi) { - GET_HIST(TH2, "h2dGenXi")->Fill(mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("h2dGenXi"), mcParticle.pt(), mcParticle.eta()); } for (auto const& mcParticle : trueXiC) { - GET_HIST(TH2, "h2dGenXiC")->Fill(mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("h2dGenXiC"), mcParticle.pt(), mcParticle.eta()); } for (auto const& mcParticle : trueXiCC) { - GET_HIST(TH2, "h2dGenXiCC")->Fill(mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("h2dGenXiCC"), mcParticle.pt(), mcParticle.eta()); } } //*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* - void processFindXicc(Alice3Collision const& collision, Alice3Tracks const& tracks, aod::McParticles const&, aod::UpgradeCascades const& cascades) + template + void processFindXicc(const TCollision& collision, const TCascadeCandidate& xi, const THfProngsGrouped& picTracksGrouped, const THfProngsGrouped& piccTracksGrouped) { - const std::string histPath = "Configuration_" + std::to_string(collision.lutConfigId()) + "/"; - initDetectorConfiguration(collision.lutConfigId()); - - GET_HIST(TH1, histPath + "hNCollisions")->Fill(1); - GET_HIST(TH1, histPath + "hNTracks")->Fill(tracks.size()); - if (tracks.size() < minNTracks.value[collision.lutConfigId()]) { - return; - } - - GET_HIST(TH1, histPath + "hNCollisions")->Fill(2); - // group with this collision - // n.b. cascades do not need to be grouped, being used directly in iterator-grouping - auto picTracksGrouped = picTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - auto piccTracksGrouped = piccTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - - for (auto const& track : tracks) { - if (BIT_CHECK(track.decayMap(), kTruePiFromXiC)) { - GET_HIST(TH2, histPath + "h2dDCAxyVsPtPiFromXiC")->Fill(track.pt(), track.dcaXY() * ToMicrons); - GET_HIST(TH2, histPath + "h2dDCAxyVsPtPiFromXiC")->Fill(track.pt(), track.dcaZ() * ToMicrons); - } - if (BIT_CHECK(track.decayMap(), kTruePiFromXiCC)) { - GET_HIST(TH2, histPath + "h2dDCAxyVsPtPiFromXiCC")->Fill(track.pt(), track.dcaXY() * ToMicrons); - GET_HIST(TH2, histPath + "h2dDCAxyVsPtPiFromXiCC")->Fill(track.pt(), track.dcaZ() * ToMicrons); + uint32_t nCombinationsC = 0; + for (auto const& pi1c : picTracksGrouped) { + if (mcSameMotherCheck && !checkSameMother(xi, pi1c)) { + continue; } - } - - for (auto const& xiCand : cascades) { - auto xi = xiCand.cascadeTrack_as(); // de-reference cascade track - GET_HIST(TH1, histPath + "hMassXi")->Fill(xiCand.mXi()); - GET_HIST(TH2, histPath + "h2dDCAxyVsPtXiFromXiC")->Fill(xi.pt(), xi.dcaXY() * ToMicrons); - GET_HIST(TH2, histPath + "h2dDCAzVsPtXiFromXiC")->Fill(xi.pt(), xi.dcaZ() * ToMicrons); - if (std::fabs(xiCand.mXi() - o2::constants::physics::MassXiMinus) > xiMassWindow) { - continue; // out of mass region + if (xi.posTrackId() == pi1c.globalIndex() || xi.negTrackId() == pi1c.globalIndex() || xi.bachelorId() == pi1c.globalIndex()) { + continue; } - uint32_t nCombinationsC = 0; - if (!BIT_CHECK(xi.decayMap(), kTrueXiFromXiC)) { + if (selFlags.applyPicMinPt && pi1c.pt() < selVals.picMinPt) { continue; } - if (std::fabs(xi.dcaXY()) < xiMinConstDCAxy || std::fabs(xi.dcaZ()) < xiMinConstDCAz) { - continue; // likely a primary xi - } + hist("hPi1cPt")->Fill(pi1c.pt()); + for (auto const& pi2c : picTracksGrouped) { + if (mcSameMotherCheck && !checkSameMother(xi, pi2c)) { + continue; + } - GET_HIST(TH1, histPath + "hDCAxyXi")->Fill(xi.dcaXY() * ToMicrons); - GET_HIST(TH1, histPath + "hDCAzXi")->Fill(xi.dcaZ() * ToMicrons); - if (xiCand.cascRadius() < xiMinDecayRadius) { - continue; - } + if (pi1c.globalIndex() >= pi2c.globalIndex()) { + continue; + } - GET_HIST(TH1, histPath + "hMinXiDecayRadius")->Fill(xiCand.cascRadius()); - for (auto const& pi1c : picTracksGrouped) { - if (mcSameMotherCheck && !checkSameMother(xi, pi1c)) { + if (xi.posTrackId() == pi2c.globalIndex() || xi.negTrackId() == pi2c.globalIndex() || xi.bachelorId() == pi2c.globalIndex()) { continue; } - if (xiCand.posTrackId() == pi1c.globalIndex() || xiCand.negTrackId() == pi1c.globalIndex() || xiCand.bachTrackId() == pi1c.globalIndex()) { - continue; // avoid using any track that was already used + if (selFlags.applyPicMinPt && pi2c.pt() < selVals.picMinPt) { + continue; + } + + nCombinationsC++; + hist("hCharmBuilding")->Fill(0.0f); + hist("hPi2cPt")->Fill(pi2c.pt()); + + o2::track::TrackParCov pi1cTrack = getTrackParCov(pi1c); + o2::track::TrackParCov pi2cTrack = getTrackParCov(pi2c); + if (!buildDecayCandidateThreeBody(thisXiCandidate.trackParCov, pi1cTrack, pi2cTrack, o2::constants::physics::MassXiMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassPionCharged)) { + continue; } - if (pi1c.pt() < picMinPt) { - continue; // too low momentum + hist("hDCAXiCDaughters")->Fill(thisXicCandidate.dca * ToMicrons); + if (selFlags.applyXicMassWindow && std::fabs(thisXicCandidate.mass - o2::constants::physics::MassXiCPlus) > selVals.xicMassWindow) { + continue; + } + + hist("hCharmBuilding")->Fill(1.0f); + const std::array momentumC = {thisXicCandidate.prong0mom[0] + thisXicCandidate.prong1mom[0] + thisXicCandidate.prong2mom[0], + thisXicCandidate.prong0mom[1] + thisXicCandidate.prong1mom[1] + thisXicCandidate.prong2mom[1], + thisXicCandidate.prong0mom[2] + thisXicCandidate.prong1mom[2] + thisXicCandidate.prong2mom[2]}; + + o2::track::TrackParCov xicTrack(thisXicCandidate.xyz, momentumC, thisXicCandidate.parentTrackCovMatrix, +1); + float xicDecayRadius2D = std::hypot(thisXicCandidate.xyz[0], thisXicCandidate.xyz[1]); + if (selFlags.applyXicMinDecayRadius && xicDecayRadius2D < selVals.xicMinDecayRadius) { + continue; + } + + hist("hCharmBuilding")->Fill(2.0f); + hist("hMinXiCDecayRadius")->Fill(xicDecayRadius2D * ToMicrons); + if (xicDecayRadius2D > thisXiCandidate.radius) { + continue; + } + + hist("hCharmBuilding")->Fill(3.0f); + hist("hXiRadiusVsXicRadius")->Fill(thisXiCandidate.radius * ToMicrons, xicDecayRadius2D * ToMicrons); + o2::dataformats::DCA dcaInfo; + float xicdcaXY = 1e+10, xicdcaZ = 1e+10; + o2::track::TrackParCov xicTrackCopy(xicTrack); + o2::vertexing::PVertex primaryVertex; + primaryVertex.setXYZ(collision.posX(), collision.posY(), collision.posZ()); + + if (xicTrackCopy.propagateToDCA(primaryVertex, cfgMagneticField, &dcaInfo)) { + xicdcaXY = dcaInfo.getY(); + xicdcaZ = dcaInfo.getZ(); + } + + if (selFlags.applyXicMinDCAxy && std::fabs(xicdcaXY) < selVals.xicMinDCAxy) { + continue; + } + + if (selFlags.applyXicMinDCAz && std::fabs(xicdcaZ) < selVals.xicMinDCAz) { + continue; } - GET_HIST(TH1, histPath + "hPi1cPt")->Fill(pi1c.pt()); - // second pion from XiC decay for starts here - for (auto const& pi2c : picTracksGrouped) { - if (mcSameMotherCheck && !checkSameMother(xi, pi2c)) { - continue; // keep only if same mother + hist("hCharmBuilding")->Fill(4.0f); + hist("hDCAxyXiC")->Fill(std::fabs(xicdcaXY * ToMicrons)); + hist("hDCAzXiC")->Fill(std::fabs(xicdcaZ * ToMicrons)); + hist("hMassXiC")->Fill(thisXicCandidate.mass); + + uint32_t nCombinationsCC = 0; + for (auto const& picc : piccTracksGrouped) { + if (mcSameMotherCheck && !checkSameMotherExtra(xi, picc)) { + continue; } - if (pi1c.globalIndex() >= pi2c.globalIndex()) { - continue; // avoid same-mother, avoid double-counting + if (xi.posTrackId() == picc.globalIndex() || xi.negTrackId() == picc.globalIndex() || xi.bachelorId() == picc.globalIndex()) { + continue; } - if (xiCand.posTrackId() == pi2c.globalIndex() || xiCand.negTrackId() == pi2c.globalIndex() || xiCand.bachTrackId() == pi2c.globalIndex()) { - continue; // avoid using any track that was already used + if (selFlags.applyPiccMinPt && picc.pt() < selVals.piccMinPt) { + continue; } - if (pi2c.pt() < picMinPt) { - continue; // too low momentum + hist("hMultiCharmBuilding")->Fill(0.0f); + hist("hPiccPt")->Fill(picc.pt()); + o2::track::TrackParCov piccTrack = getTrackParCov(picc); + nCombinationsCC++; + if (!buildDecayCandidateTwoBody(xicTrack, piccTrack, o2::constants::physics::MassXiCPlus, o2::constants::physics::MassPionCharged)) { + continue; } - GET_HIST(TH1, histPath + "hPi2cPt")->Fill(pi2c.pt()); - // if I am here, it means this is a triplet to be considered for XiC vertexing. - // will now attempt to build a three-body decay candidate with these three track rows. - nCombinationsC++; - GET_HIST(TH1, histPath + "hCharmBuilding")->Fill(0.0f); + hist("hMultiCharmBuilding")->Fill(1.0f); + hist("hDCAXiCCDaughters")->Fill(thisXiccCandidate.dca * ToMicrons); + const std::array momentumCC = { + thisXiccCandidate.prong0mom[0] + thisXiccCandidate.prong1mom[0], + thisXiccCandidate.prong0mom[1] + thisXiccCandidate.prong1mom[1], + thisXiccCandidate.prong0mom[2] + thisXiccCandidate.prong1mom[2]}; - if (!buildDecayCandidateThreeBody(xi, pi1c, pi2c, o2::constants::physics::MassXiMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassPionCharged)) { - continue; // failed at building candidate + o2::track::TrackParCov xiccTrack(thisXiccCandidate.xyz, momentumCC, thisXiccCandidate.parentTrackCovMatrix, +2); + float xiccDecayRadius2D = std::hypot(thisXiccCandidate.xyz[0], thisXiccCandidate.xyz[1]); + if (selFlags.applyXiccMinDecayRadius && xiccDecayRadius2D < selVals.xiccMinDecayRadius) { + continue; } - GET_HIST(TH1, histPath + "hDCAXiCDaughters")->Fill(thisXicCandidate.dca * ToMicrons); - if (std::fabs(thisXicCandidate.mass - o2::constants::physics::MassXiCPlus) > xicMassWindow) { - continue; // out of mass region + hist("hMultiCharmBuilding")->Fill(2.0f); + float totalMomentumC = std::hypot(momentumC[0], momentumC[1], momentumC[2]); + float decayLengthXiC = std::hypot(thisXicCandidate.xyz[0] - thisXiccCandidate.xyz[0], + thisXicCandidate.xyz[1] - thisXiccCandidate.xyz[1], + thisXicCandidate.xyz[2] - thisXiccCandidate.xyz[2]); + float xicProperLength = decayLengthXiC * thisXicCandidate.mass / totalMomentumC; + if (selFlags.applyXicMinProperLength && xicProperLength < selVals.xicMinProperLength) { + continue; + } + if (selFlags.applyXicMaxProperLength && xicProperLength > selVals.xicMaxProperLength) { + continue; } - GET_HIST(TH1, histPath + "hCharmBuilding")->Fill(1.0f); - const std::array momentumC = { - thisXicCandidate.prong0mom[0] + thisXicCandidate.prong1mom[0] + thisXicCandidate.prong2mom[0], - thisXicCandidate.prong0mom[1] + thisXicCandidate.prong1mom[1] + thisXicCandidate.prong2mom[1], - thisXicCandidate.prong0mom[2] + thisXicCandidate.prong1mom[2] + thisXicCandidate.prong2mom[2]}; + hist("hMultiCharmBuilding")->Fill(3.0f); + hist("hProperLengthXiC")->Fill(xicProperLength * ToMicrons); + float xicDistanceFromPV = std::hypot(thisXicCandidate.xyz[0] - collision.posX(), + thisXicCandidate.xyz[1] - collision.posY(), + thisXicCandidate.xyz[2] - collision.posZ()); + float xicDecayDistanceFromPV = xicDistanceFromPV * thisXicCandidate.mass / totalMomentumC; + if (selFlags.applyXicMinDecayDistanceFromPV && xicDecayDistanceFromPV < selVals.xicMinDecayDistanceFromPV) { + continue; + } - o2::track::TrackParCov xicTrack(thisXicCandidate.xyz, momentumC, thisXicCandidate.parentTrackCovMatrix, +1); - float xicDecayRadius2D = std::hypot(thisXicCandidate.xyz[0], thisXicCandidate.xyz[1]); - if (xicDecayRadius2D < xiccMinDecayRadius) { - continue; // do not take if radius too small, likely a primary combination + hist("hMultiCharmBuilding")->Fill(4.0f); + hist("hMinXicDecayDistanceFromPV")->Fill(xicDecayDistanceFromPV * ToMicrons); + float totalMomentumCC = std::hypot(momentumCC[0], momentumCC[1], momentumCC[2]); + float decayLengthXiCC = std::hypot(thisXiccCandidate.xyz[0] - collision.posX(), + thisXiccCandidate.xyz[1] - collision.posY(), + thisXiccCandidate.xyz[2] - collision.posZ()); + float xiccProperLength = decayLengthXiCC * thisXiccCandidate.mass / totalMomentumCC; + if (selFlags.applyXiccMinProperLength && xiccProperLength < selVals.xiccMinProperLength) { + continue; + } + if (selFlags.applyXiccMaxProperLength && xiccProperLength > selVals.xiccMaxProperLength) { + continue; } - GET_HIST(TH1, histPath + "hCharmBuilding")->Fill(2.0f); - GET_HIST(TH1, histPath + "hMinXiCDecayRadius")->Fill(xicDecayRadius2D * ToMicrons); - if (xicDecayRadius2D > xiCand.cascRadius()) { + hist("hMultiCharmBuilding")->Fill(5.0f); + hist("hProperLengthXiCC")->Fill(xiccProperLength * ToMicrons); + if (xiccDecayRadius2D > xicDecayRadius2D) { continue; } - GET_HIST(TH1, histPath + "hCharmBuilding")->Fill(3.0f); - GET_HIST(TH2, histPath + "hXiRadiusVsXicRadius")->Fill(xiCand.cascRadius() * ToMicrons, xicDecayRadius2D * ToMicrons); - o2::dataformats::DCA dcaInfo; - float xicdcaXY = 1e+10, xicdcaZ = 1e+10; - o2::track::TrackParCov xicTrackCopy(xicTrack); // paranoia - o2::vertexing::PVertex primaryVertex; - primaryVertex.setXYZ(collision.posX(), collision.posY(), collision.posZ()); - - if (xicTrackCopy.propagateToDCA(primaryVertex, cfgMagneticField, &dcaInfo)) { - xicdcaXY = dcaInfo.getY(); - xicdcaZ = dcaInfo.getZ(); + hist("hMultiCharmBuilding")->Fill(6.0f); + hist("hXicRadiusVsXiccRadius")->Fill(xicDecayRadius2D * ToMicrons, xiccDecayRadius2D * ToMicrons); + float xiccdcaXY = 1e+10, xiccdcaZ = 1e+10; + if (xiccTrack.propagateToDCA(primaryVertex, cfgMagneticField, &dcaInfo)) { + xiccdcaXY = dcaInfo.getY(); + xiccdcaZ = dcaInfo.getZ(); } - if (std::fabs(xicdcaXY) < xicMaxDCAxy || std::fabs(xicdcaZ) < xicMaxDCAz) { - continue; // likely a primary xic + if (selFlags.applyXiccMaxDCAxy && std::fabs(xiccdcaXY) > selVals.xiccMaxDCAxy) { + continue; + } + if (selFlags.applyXiccMaxDCAz && std::fabs(xiccdcaZ) > selVals.xiccMaxDCAz) { + continue; } - GET_HIST(TH1, histPath + "hCharmBuilding")->Fill(4.0f); - GET_HIST(TH1, histPath + "hDCAxyXiC")->Fill(std::fabs(xicdcaXY * ToMicrons)); - GET_HIST(TH1, histPath + "hDCAzXiC")->Fill(std::fabs(xicdcaZ * ToMicrons)); - GET_HIST(TH1, histPath + "hMassXiC")->Fill(thisXicCandidate.mass); + hist("hMultiCharmBuilding")->Fill(7.0f); + hist("hDCAxyXiCC")->Fill(xiccdcaXY * ToMicrons); + hist("hDCAzXiCC")->Fill(xiccdcaZ * ToMicrons); + if (selFlags.applyXiccMaxEta && std::fabs(thisXiccCandidate.eta) > selVals.xiccMaxEta) { + continue; + } - // attempt XiCC finding - uint32_t nCombinationsCC = 0; - for (auto const& picc : piccTracksGrouped) { - if (mcSameMotherCheck && !checkSameMotherExtra(xi, picc)) { - continue; - } + hist("hMultiCharmBuilding")->Fill(8.0f); + hist("hMassXiCC")->Fill(thisXiccCandidate.mass); + hist("hPtXiCC")->Fill(thisXiccCandidate.pt); + hist("hEtaXiCC")->Fill(thisXiccCandidate.eta); + hist("h3dMassXiCC")->Fill(thisXiccCandidate.pt, thisXiccCandidate.eta, thisXiccCandidate.mass); + + hist("hPi1cDCAxy")->Fill(std::abs(pi1c.dcaXY() * ToMicrons)); + hist("hPi1cDCAz")->Fill(std::abs(pi1c.dcaZ() * ToMicrons)); + hist("hPi2cDCAxy")->Fill(std::abs(pi2c.dcaXY() * ToMicrons)); + hist("hPi2cDCAz")->Fill(std::abs(pi2c.dcaZ() * ToMicrons)); + hist("hPiccDCAxy")->Fill(std::abs(picc.dcaXY() * ToMicrons)); + hist("hPiccDCAz")->Fill(std::abs(picc.dcaZ() * ToMicrons)); + + if (derivedTable.fillMCharmIdx) { + multiCharmIdx(xi.globalIndex(), + pi1c.globalIndex(), + pi2c.globalIndex(), + picc.globalIndex()); + } - if (xiCand.posTrackId() == picc.globalIndex() || xiCand.negTrackId() == picc.globalIndex() || xiCand.bachTrackId() == picc.globalIndex()) { - continue; // avoid using any track that was already used - } + if (derivedTable.fillMCharmCore) { + multiCharmCore(thisXiccCandidate.mass, thisXiccCandidate.pt, + thisXiccCandidate.eta, thisXiccCandidate.dca, + thisXicCandidate.mass, thisXicCandidate.pt, + thisXicCandidate.eta, thisXicCandidate.dca, + thisXiCandidate.pt, thisXiCandidate.eta, + xi.dcaXYCascToPV(), xi.dcaZCascToPV(), + xicdcaXY, xicdcaZ, + xiccdcaXY, xiccdcaZ, + pi1c.dcaXY(), pi1c.dcaZ(), + pi2c.dcaXY(), pi2c.dcaZ(), + picc.dcaXY(), picc.dcaZ(), + xicDecayRadius2D, xiccDecayRadius2D, + xicProperLength, + xicDecayDistanceFromPV, + xiccProperLength, + pi1c.pt(), pi2c.pt(), picc.pt(), + collision.lutConfigId()); + } + } + hist("hCombinationsXiCC")->Fill(nCombinationsCC); + } + } + hist("hCombinationsXiC")->Fill(nCombinationsC); + } + //*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* - if (picc.pt() < piccMinPt) { - continue; // too low momentum - } + template + void configureCollision(const TCollision& collision, const TTracks& tracks) + { + histPath = "Configuration_" + std::to_string(collision.lutConfigId()) + "/"; + initDetectorConfiguration(collision.lutConfigId()); - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(0.0f); - GET_HIST(TH1, histPath + "hPiccPt")->Fill(picc.pt()); - o2::track::TrackParCov piccTrack = getTrackParCov(picc); - nCombinationsCC++; - if (!buildDecayCandidateTwoBody(xicTrack, piccTrack, o2::constants::physics::MassXiCPlus, o2::constants::physics::MassPionCharged)) { - continue; // failed at building candidate - } + hist("hNCollisions")->Fill(1); + hist("hNCollisions")->Fill(2); + hist("hNTracks")->Fill(tracks.size()); - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(1.0f); - GET_HIST(TH1, histPath + "hDCAXiCCDaughters")->Fill(thisXiccCandidate.dca * ToMicrons); - const std::array momentumCC = { - thisXiccCandidate.prong0mom[0] + thisXiccCandidate.prong1mom[0], - thisXiccCandidate.prong0mom[1] + thisXiccCandidate.prong1mom[1], - thisXiccCandidate.prong0mom[2] + thisXiccCandidate.prong1mom[2]}; - - o2::track::TrackParCov xiccTrack(thisXiccCandidate.xyz, momentumCC, thisXiccCandidate.parentTrackCovMatrix, +2); - float xiccDecayRadius2D = std::hypot(thisXiccCandidate.xyz[0], thisXiccCandidate.xyz[1]); - if (xiccDecayRadius2D < xiccMinDecayRadius) { - continue; // do not take if radius too small, likely a primary combination - } + for (auto const& track : tracks) { + if (bitCheck(track.decayMap(), kTruePiFromXiC)) { + hist("h2dDCAxyVsPtPiFromXiC")->Fill(track.pt(), track.dcaXY() * ToMicrons); + hist("h2dDCAzVsPtPiFromXiC")->Fill(track.pt(), track.dcaZ() * ToMicrons); + } + if (bitCheck(track.decayMap(), kTruePiFromXiCC)) { + hist("h2dDCAxyVsPtPiFromXiCC")->Fill(track.pt(), track.dcaXY() * ToMicrons); + hist("h2dDCAzVsPtPiFromXiCC")->Fill(track.pt(), track.dcaZ() * ToMicrons); + } + } + } - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(2.0f); - GET_HIST(TH1, histPath + "hDCAXiCCDaughters")->Fill(thisXiccCandidate.dca * ToMicrons); - float totalMomentumC = std::hypot(momentumC[0], momentumC[1], momentumC[2]); - float decayLengthXiC = std::hypot( - thisXicCandidate.xyz[0] - thisXiccCandidate.xyz[0], - thisXicCandidate.xyz[1] - thisXiccCandidate.xyz[1], - thisXicCandidate.xyz[2] - thisXiccCandidate.xyz[2]); - float xicProperLength = decayLengthXiC * thisXicCandidate.mass / totalMomentumC; - - if (xicProperLength < xicMinProperLength || xicProperLength > xicMaxProperLength) { - continue; // likely background - } + void processPerfectBuilder(Alice3Collision const& collision, soa::Join const& cascades, Alice3Tracks const& tracks, aod::McParticles const&) + { + configureCollision(collision, tracks); + const auto picTracksGrouped = picTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + const auto piccTracksGrouped = piccTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(3.0f); - GET_HIST(TH1, histPath + "hProperLengthXiC")->Fill(xicProperLength * ToMicrons); - float xicDistanceFromPV = std::hypot( - thisXicCandidate.xyz[0] - collision.posX(), - thisXicCandidate.xyz[1] - collision.posY(), - thisXicCandidate.xyz[2] - collision.posZ()); - float xicDecayDistanceFromPV = xicDistanceFromPV * thisXicCandidate.mass / totalMomentumC; - if (xicDecayDistanceFromPV < xicMinDecayDistanceFromPV) { - continue; // too close to PV - } + for (const auto& xiCand : cascades) { + auto xi = xiCand.cascadeTrack_as(); + if (selFlags.applyXiMassWindow && std::fabs(xiCand.mXi() - o2::constants::physics::MassXiMinus) > selVals.xiMassWindow) { + continue; + } - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(4.0f); - GET_HIST(TH1, histPath + "hMinXicDecayDistanceFromPV")->Fill(xicDecayDistanceFromPV * ToMicrons); - float totalMomentumCC = std::hypot(momentumCC[0], momentumCC[1], momentumCC[2]); - float decayLengthXiCC = std::hypot( - thisXiccCandidate.xyz[0] - collision.posX(), - thisXiccCandidate.xyz[1] - collision.posY(), - thisXiccCandidate.xyz[2] - collision.posZ()); - float xiccProperLength = decayLengthXiCC * thisXiccCandidate.mass / totalMomentumCC; - if (xiccProperLength < xiccMinProperLength || xiccProperLength > xicMaxProperLength) { - continue; // likely background - } + if (!bitCheck(xi.decayMap(), kTrueXiFromXiC)) { + continue; + } - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(5.0f); - GET_HIST(TH1, histPath + "hProperLengthXiCC")->Fill(xiccProperLength * ToMicrons); - if (xiccDecayRadius2D > xicDecayRadius2D) { - continue; // XiCC should decay before XiC - } + if (selFlags.applyXiMinDCAxy && std::fabs(xi.dcaXY()) < selVals.xiMinConstDCAxy) { + continue; + } - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(6.0f); - GET_HIST(TH2, histPath + "hXicRadiusVsXiccRadius")->Fill(xicDecayRadius2D * ToMicrons, xiccDecayRadius2D * ToMicrons); - float xiccdcaXY = 1e+10, xiccdcaZ = 1e+10; - if (xiccTrack.propagateToDCA(primaryVertex, cfgMagneticField, &dcaInfo)) { - xiccdcaXY = dcaInfo.getY(); - xiccdcaZ = dcaInfo.getZ(); - } + if (selFlags.applyXiMinDCAz && std::fabs(xi.dcaZ()) < selVals.xiMinConstDCAz) { + continue; + } - if (std::fabs(xiccdcaXY) > xiccMaxDCAxy || std::fabs(xiccdcaZ) > xiccMaxDCAz) { - continue; // not pointing to PV - } + if (selFlags.applyXiMinDecayRadius && xiCand.cascRadius() < selVals.xiMinDecayRadius) { + continue; + } - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(7.0f); - GET_HIST(TH1, histPath + "hDCAxyXiCC")->Fill(xiccdcaXY * ToMicrons); - GET_HIST(TH1, histPath + "hDCAzXiCC")->Fill(xiccdcaZ * ToMicrons); - if (std::fabs(thisXiccCandidate.eta) > xiccMaxEta) { - continue; // not in central barrel - } + hist("hMassXi")->Fill(xiCand.mXi()); + hist("h2dDCAxyVsPtXiFromXiC")->Fill(xi.pt(), xi.dcaXY() * ToMicrons); + hist("h2dDCAzVsPtXiFromXiC")->Fill(xi.pt(), xi.dcaZ() * ToMicrons); + hist("hDCAxyXi")->Fill(xi.dcaXY() * ToMicrons); + hist("hDCAzXi")->Fill(xi.dcaZ() * ToMicrons); + hist("hMinXiDecayRadius")->Fill(xiCand.cascRadius()); + thisXiCandidate.radius = xiCand.cascRadius(); + thisXiCandidate.pt = xi.pt(); + thisXiCandidate.eta = xi.eta(); + thisXiCandidate.trackParCov = getTrackParCov(xi); + processFindXicc(collision, xiCand, picTracksGrouped, piccTracksGrouped); + } + } - GET_HIST(TH1, histPath + "hMultiCharmBuilding")->Fill(8.0f); - GET_HIST(TH1, histPath + "hMassXiCC")->Fill(thisXiccCandidate.mass); - GET_HIST(TH1, histPath + "hPtXiCC")->Fill(thisXiccCandidate.pt); - GET_HIST(TH1, histPath + "hEtaXiCC")->Fill(thisXiccCandidate.eta); - GET_HIST(TH3, histPath + "h3dMassXiCC")->Fill(thisXiccCandidate.pt, thisXiccCandidate.eta, thisXiccCandidate.mass); - - GET_HIST(TH1, histPath + "hPi1cDCAxy")->Fill(std::abs(pi1c.dcaXY() * ToMicrons)); - GET_HIST(TH1, histPath + "hPi1cDCAz")->Fill(std::abs(pi1c.dcaZ() * ToMicrons)); - GET_HIST(TH1, histPath + "hPi2cDCAxy")->Fill(std::abs(pi2c.dcaXY() * ToMicrons)); - GET_HIST(TH1, histPath + "hPi2cDCAz")->Fill(std::abs(pi2c.dcaZ() * ToMicrons)); - GET_HIST(TH1, histPath + "hPiccDCAxy")->Fill(std::abs(picc.dcaXY() * ToMicrons)); - GET_HIST(TH1, histPath + "hPiccDCAz")->Fill(std::abs(picc.dcaZ() * ToMicrons)); - - // produce multi-charm table for posterior analysis - if (derivedTable.fillMCharmIdx) { - multiCharmIdx( - xiCand.globalIndex(), - pi1c.globalIndex(), pi2c.globalIndex(), - picc.globalIndex()); - } + void processStraBuilder(Alice3Collision const& collision, FullCascadeCandidates const& cascadeCandidates, Alice3Tracks const& tracks, aod::McParticles const&) + { + configureCollision(collision, tracks); + const auto picTracksGrouped = picTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + const auto piccTracksGrouped = piccTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + for (const auto& xiCand : cascadeCandidates) { + const auto positive = xiCand.template posTrack_as(); + const auto negative = xiCand.template negTrack_as(); + const auto bachelor = xiCand.template bachelor_as(); + static constexpr float CtauXi = 4.91f; + const float distanceFromPV = std::hypot(xiCand.x() - collision.posX(), xiCand.y() - collision.posY(), xiCand.z() - collision.posZ()); + const float normalizedDecayLength = o2::constants::physics::MassXiMinus * distanceFromPV / (xiCand.p() * CtauXi); + + if (selFlags.applyPosMinDCAxy && std::abs(positive.dcaXY()) < selVals.posMinDCAxy) { + continue; + } - if (derivedTable.fillMCharmCore) { - multiCharmCore( - thisXiccCandidate.mass, thisXiccCandidate.pt, - thisXiccCandidate.eta, thisXiccCandidate.dca, - thisXicCandidate.mass, thisXicCandidate.pt, - thisXicCandidate.eta, thisXicCandidate.dca, - xi.dcaXY(), xi.dcaZ(), - xicdcaXY, xicdcaZ, - xiccdcaXY, xiccdcaZ, - pi1c.dcaXY(), pi1c.dcaZ(), - pi2c.dcaXY(), pi2c.dcaZ(), - picc.dcaXY(), picc.dcaZ(), - xicDecayRadius2D, xiccDecayRadius2D, - xicProperLength, - xicDecayDistanceFromPV, - xiccProperLength, - pi1c.pt(), pi2c.pt(), picc.pt(), - collision.lutConfigId()); - } + if (selFlags.applyNegMinDCAxy && std::abs(negative.dcaXY()) < selVals.negMinDCAxy) { + continue; + } - if (derivedTable.fillMCharmExtra) { - ProngInfo bachelor, positive, negative; - if (xiCand.has_bachTrack()) { - auto bach = xiCand.bachTrack_as(); // de-reference bach track - bachelor.pt = bach.pt(); - bachelor.eta = bach.eta(); - bachelor.dcaXY = bach.dcaXY(); - bachelor.dcaZ = bach.dcaZ(); - } + if (selFlags.applyBachMinDCAxy && std::abs(bachelor.dcaXY()) < selVals.bachMinDCAxy) { + continue; + } - if (xiCand.has_negTrack()) { - auto neg = xiCand.negTrack_as(); // de-reference neg track - negative.pt = neg.pt(); - negative.eta = neg.eta(); - negative.dcaXY = neg.dcaXY(); - negative.dcaZ = neg.dcaZ(); - } + if (selFlags.applyLaMaxDauDCA && xiCand.dcaV0daughters() > selVals.laMaxDauDCA) { + continue; + } - if (xiCand.has_posTrack()) { - auto pos = xiCand.posTrack_as(); // de-reference pos track - positive.pt = pos.pt(); - positive.eta = pos.eta(); - positive.dcaXY = pos.dcaXY(); - positive.dcaZ = pos.dcaZ(); - } + if (selFlags.applyLaMinDecayRadius && xiCand.v0radius() < selVals.laMinDecayRadius) { + continue; + } - multiCharmExtra( - bachelor.pt, bachelor.eta, - bachelor.dcaXY, bachelor.dcaZ, - positive.pt, positive.eta, - positive.dcaXY, positive.dcaZ, - negative.pt, negative.eta, - negative.dcaXY, negative.dcaZ, - pi1c.eta(), pi2c.eta(), picc.eta()); - } - } - GET_HIST(TH1, histPath + "hCombinationsXiCC")->Fill(nCombinationsCC); - } + if (selFlags.applyLaMinCosPA && xiCand.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) < selVals.laMinCosPA) { + continue; + } + + if (selFlags.applyLaMassWindow && std::abs(xiCand.mLambda() - o2::constants::physics::MassLambda0) > selVals.laMassWindow) { + continue; + } + + if (selFlags.applyXiMinCosPA && xiCand.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < selVals.xiMinCosPA) { + continue; } - GET_HIST(TH1, histPath + "hCombinationsXiC")->Fill(nCombinationsC); + + if (selFlags.applyXiMaxDauDCA && xiCand.dcacascdaughters() > selVals.xiMaxDauDCA) { + continue; + } + + if (selFlags.applyXiMaxNormalizedDecayLength && normalizedDecayLength > selVals.xiMaxNormalizedDecayLength) { + continue; + } + + if (selFlags.applyXiMassWindow && std::fabs(xiCand.mXi() - o2::constants::physics::MassXiMinus) > selVals.xiMassWindow) { + continue; + } + + if (selFlags.applyXiMinDCAxy && std::fabs(xiCand.dcaXYCascToPV()) < selVals.xiMinConstDCAxy) { + continue; + } + + if (selFlags.applyXiMinDCAz && std::fabs(xiCand.dcaZCascToPV()) < selVals.xiMinConstDCAz) { + continue; + } + + if (selFlags.applyXiMinDecayRadius && xiCand.cascradius() < selVals.xiMinDecayRadius) { + continue; + } + + hist("hMassXi")->Fill(xiCand.mXi()); + hist("h2dDCAxyVsPtXiFromXiC")->Fill(xiCand.pt(), xiCand.dcaXYCascToPV() * ToMicrons); + hist("h2dDCAzVsPtXiFromXiC")->Fill(xiCand.pt(), xiCand.dcaZCascToPV() * ToMicrons); + hist("hDCAxyXi")->Fill(xiCand.dcaXYCascToPV() * ToMicrons); + hist("hDCAzXi")->Fill(xiCand.dcaZCascToPV() * ToMicrons); + hist("hMinXiDecayRadius")->Fill(xiCand.cascradius()); + + const std::array cascSV = {xiCand.x(), xiCand.y(), xiCand.z()}; + const std::array cascP = {xiCand.px(), xiCand.py(), xiCand.pz()}; + const int chargeCascade = (xiCand.sign() > 0) ? 1 : -1; + static constexpr int NCovMatElements = 21; + std::array cascCovMat{}; + std::copy(xiCand.covMat(), xiCand.covMat() + NCovMatElements, cascCovMat.begin()); + thisXiCandidate.radius = xiCand.cascradius(); + thisXiCandidate.pt = xiCand.pt(); + thisXiCandidate.eta = xiCand.eta(); + thisXiCandidate.trackParCov = o2::track::TrackParCov(cascSV, cascP, cascCovMat, chargeCascade); + processFindXicc(collision, xiCand, picTracksGrouped, piccTracksGrouped); } } - //*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* //*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<* PROCESS_SWITCH(Alice3MulticharmFinder, processGenerated, "fill MC-only histograms", true); - PROCESS_SWITCH(Alice3MulticharmFinder, processFindXicc, "find XiCC baryons", true); + PROCESS_SWITCH(Alice3MulticharmFinder, processStraBuilder, "find XiCC baryons", false); + PROCESS_SWITCH(Alice3MulticharmFinder, processPerfectBuilder, "find XiCC baryons", true); //*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<*>-~-<* }; diff --git a/ALICE3/TableProducer/alice3Multiplicity.cxx b/ALICE3/TableProducer/alice3Multiplicity.cxx new file mode 100644 index 00000000000..bf614e500ad --- /dev/null +++ b/ALICE3/TableProducer/alice3Multiplicity.cxx @@ -0,0 +1,178 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \author Alexander Tiekoetter , Muenster +/// \brief Multiplicity task for ALICE3 +/// \file alice3Multiplicity.cxx + +#include "ALICE3/DataModel/collisionAlice3.h" +#include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using TracksAlice3 = soa::Join; + +struct Alice3Multiplicity { + Produces multPV; + Produces multGlobal; + Produces multMC; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Configurable minEta{"minEta", -2.5f, "Minimum eta in range for global track counting"}; + Configurable maxEta{"maxEta", 2.5f, "Maximum eta in range for global track counting"}; + Configurable maxDCAxy{"maxDCAxy", 0.0025f, "Max DCAxy for global track counting"}; + Configurable maxDCAz{"maxDCAz", 0.0025f, "Max DCAz for global track counting"}; + Configurable minSiliconHits{"minSiliconHits", 5, "Minimum number of hits in silicon detector for global track counting"}; + Configurable requireReconstructed{"requireReconstructed", false, "Require track to be reconstructed for global track counting"}; + Configurable doQA{"doQA", true, "Fill QA histograms"}; + + ConfigurableAxis axisMult{"axisMult", {10000, 0, 10000}, "Reconstructed tracks"}; + + Service pdg; + + Filter trackFilter = (aod::track::eta >= minEta) && (aod::track::eta <= maxEta) && (nabs(aod::track::dcaXY) <= maxDCAxy) && (nabs(aod::track::dcaZ) <= maxDCAz) && (aod::track_alice3::nSiliconHits >= minSiliconHits) && (!requireReconstructed || aod::track_alice3::isReconstructed); + + void init(InitContext&) + { + if (doQA) { + histos.add("multiplicity/nTracksPV", "nTracksPV", kTH1D, {axisMult}); + histos.add("multiplicity/nTracksPVeta1", "nTracksPVeta1", kTH1D, {axisMult}); + histos.add("multiplicity/nTracksPVetaHalf", "nTracksPVetaHalf", kTH1D, {axisMult}); + + histos.add("multiplicity/nTracksGlobal", "nTracksGlobal", kTH1D, {axisMult}); + histos.add("multiplicity/nTracksGlobalPV", "nTracksGlobalPV", kTH1D, {axisMult}); + + histos.add("multiplicity/nTracksMC", "nTracksMC", kTH1D, {axisMult}); + histos.add("multiplicity/nTracksMCEta25", "nTracksMCEta25", kTH1D, {axisMult}); + histos.add("multiplicity/nTracksMCEta125", "nTracksMCEta125", kTH1D, {axisMult}); + histos.add("multiplicity/nTracksMCEta09", "nTracksMCEta09", kTH1D, {axisMult}); + } + } + + void processGlobalTracks(const aod::Collision& /*collision*/, const soa::Filtered& tracks) + { + int globalTracks = 0; + int globalTracksPV = 0; + + for (const auto& track : tracks) { + ++globalTracks; + if (track.isPVContributor()) + ++globalTracksPV; + } + + if (doQA) { + histos.fill(HIST("multiplicity/nTracksGlobal"), globalTracks); + histos.fill(HIST("multiplicity/nTracksGlobalPV"), globalTracksPV); + } + + multGlobal(globalTracks, globalTracksPV, 0, 0); + } + + void processPV(const aod::Collision& /*collision*/, const TracksAlice3& tracks) + { + int numTracksPV = 0; + int numTracksPVeta1 = 0; + int numTracksPVetaHalf = 0; + + for (const auto& track : tracks) { + if (track.isPVContributor()) { + ++numTracksPV; + if (std::abs(track.eta()) < 1.0) + ++numTracksPVeta1; + if (std::abs(track.eta()) < 0.5) + ++numTracksPVetaHalf; + } + } + + if (doQA) { + histos.fill(HIST("multiplicity/nTracksPV"), numTracksPV); + histos.fill(HIST("multiplicity/nTracksPVeta1"), numTracksPVeta1); + histos.fill(HIST("multiplicity/nTracksPVetaHalf"), numTracksPVetaHalf); + } + + multPV(numTracksPV, numTracksPVeta1, numTracksPVetaHalf); + } + + void processMC(aod::McCollision const& /*mcCollision*/, aod::McParticles const& mcParticles) + { + int numMCParticles = 0; + int numMCParticlesEta25 = 0; + int numMCParticlesEta125 = 0; + int numMCParticlesEta09 = 0; + + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + + auto charge = 0.; + auto* p = pdg->GetParticle(mcParticle.pdgCode()); + if (p != nullptr) { + charge = p->Charge(); + } + if (std::abs(charge) < 1e-3) { + continue; + } + + ++numMCParticles; + if (std::abs(mcParticle.eta()) < 2.5) + ++numMCParticlesEta25; + if (std::abs(mcParticle.eta()) < 1.25) + ++numMCParticlesEta125; + if (std::abs(mcParticle.eta()) < 0.9) + ++numMCParticlesEta09; + } + + if (doQA) { + histos.fill(HIST("multiplicity/nTracksMC"), numMCParticles); + histos.fill(HIST("multiplicity/nTracksMCEta25"), numMCParticlesEta25); + histos.fill(HIST("multiplicity/nTracksMCEta125"), numMCParticlesEta125); + histos.fill(HIST("multiplicity/nTracksMCEta09"), numMCParticlesEta09); + } + + multMC(numMCParticles, numMCParticlesEta25, numMCParticlesEta125, numMCParticlesEta09); + } + + void processDummy(const aod::Collision&) + { + // do nothing + } + + PROCESS_SWITCH(Alice3Multiplicity, processGlobalTracks, "Process global track counter", false); + PROCESS_SWITCH(Alice3Multiplicity, processPV, "Process primary vertex contributor tracks", false); + PROCESS_SWITCH(Alice3Multiplicity, processMC, "Process MC truth information", false); + PROCESS_SWITCH(Alice3Multiplicity, processDummy, "Dummy proccess function", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/ALICE3/TableProducer/alice3StrangenessTofPid.cxx b/ALICE3/TableProducer/alice3StrangenessTofPid.cxx new file mode 100644 index 00000000000..29d3103887e --- /dev/null +++ b/ALICE3/TableProducer/alice3StrangenessTofPid.cxx @@ -0,0 +1,884 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file alice3StrangenessTofPid.cxx +/// \brief This task produces tof strangeness pid tables +/// \author Jesper Karlsson Gumprecht +/// + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "ALICE3/DataModel/OTFStrangeness.h" +#include "ALICE3/Utils/a3StrangenessTofPidHelper.h" +#include "Common/Core/TableHelper.h" +#include "Common/Core/trackUtilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::upgrade::stratofpid; + +using CascTofResults = StrangenessTofResults; +using V0TofResults = StrangenessTofResults; +using Alice3Tracks = soa::Join; +using FullV0Candidates = soa::Join; +using FullCascadeCandidates = soa::Join; +using Alice3Collision = soa::Join::iterator; + +struct Alice3StrangenessTofPid { + Produces tableA3K0SInnerTofPid; // contains K0S strangeness iTOF nsigma + Produces tableA3K0SOuterTofPid; // contains K0S strangeness oTOF nsigma + Produces tableA3LambdaInnerTofPid; // contains lambda strangeness iTOF nsigma + Produces tableA3LambdaOuterTofPid; // contains lambda strangeness oTOF nsigma + Produces tableA3K0SExpectedInnerTimes; // contains K0S strangeness iTOF info + Produces tableA3K0SExpectedOuterTimes; // contains K0S strangeness oTOF info + Produces tableA3LambdaExpectedInnerTimes; // contains lambda strangeness iTOF info + Produces tableA3LambdaExpectedOuterTimes; // contains lambda strangeness oTOF info + Produces tableA3XiInnerTofPid; // contains xi strangeness iTOF nsigma + Produces tableA3XiOuterTofPid; // contains xi strangeness oTOF nsigma + Produces tableA3OmegaInnerTofPid; // contains omega strangeness iTOF nsigma + Produces tableA3OmegaOuterTofPid; // contains omega strangeness oTOF nsigma + Produces tableA3XiExpectedInnerTimes; // contains xi strangeness iTOF info + Produces tableA3XiExpectedOuterTimes; // contains xi strangeness oTOF info + Produces tableA3OmegaExpectedInnerTimes; // contains omega strangeness iTOF info + Produces tableA3OmegaExpectedOuterTimes; // contains omega strangeness oTOF info + + struct : ConfigurableGroup { + ConfigurableAxis axisNSigma{"axisNSigma", {200, -10, 10}, "N sigma axis"}; + ConfigurableAxis axisCollisionTime{"axisCollisionTime", {200, -1e-3, 1e-3}, "Time delta axis"}; + ConfigurableAxis axisTimeDelta{"axisTimeDelta", {300, -1500, 1500}, "Time delta axis"}; + ConfigurableAxis axisTime{"axisTime", {1000, 0, 10000}, "Time axis"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; + } axes; + + struct : ConfigurableGroup { + std::string prefix = "cfgTof"; + + // Time Of Flight + Configurable innerRadius{"innerRadius", 21., "Radius of iTOF (cm)"}; + Configurable innerResolution{"innerResolution", 20., "Timing resolution of iTOF (ps)"}; + Configurable outerRadius{"outerRadius", 92., "Radius of oTOF (cm)"}; + Configurable outerResolution{"outerResolution", 20., "Timing resolution of oTOF (ps)"}; + } cfgTof; + + struct : ConfigurableGroup { + std::string prefix = "produce"; + + // Tables + Configurable tableInnerNSigmaK0S{"tableInnerNSigmaK0S", true, "Produce NSigma tables for K0S candidate"}; + Configurable tableOuterNSigmaK0S{"tableOuterNSigmaK0S", true, "Produce NSigma tables for K0S candidate"}; + Configurable tableInnerExpectedTimesK0S{"tableInnerExpectedTimesK0S", false, "Produce expected and measured times tables for K0S candidate"}; + Configurable tableOuterExpectedTimesK0S{"tableOuterExpectedTimesK0S", false, "Produce expected and measured times tables for K0S candidate"}; + Configurable tableInnerNSigmaLambda{"tableInnerNSigmaLambda", true, "Produce NSigma tables for Lambda candidate"}; + Configurable tableOuterNSigmaLambda{"tableOuterNSigmaLambda", true, "Produce NSigma tables for Lambda candidate"}; + Configurable tableInnerExpectedTimesLambda{"tableInnerExpectedTimesLambda", false, "Produce expected and measured times tables for Lambda candidate"}; + Configurable tableOuterExpectedTimesLambda{"tableOuterExpectedTimesLambda", false, "Produce expected and measured times tables for Lambda candidate"}; + Configurable tableInnerNSigmaXi{"tableInnerNSigmaXi", true, "Produce NSigma tables for xi candidate"}; + Configurable tableOuterNSigmaXi{"tableOuterNSigmaXi", true, "Produce NSigma tables for xi candidate"}; + Configurable tableInnerExpectedTimesXi{"tableInnerExpectedTimesXi", false, "Produce expected and measured times tables for xi candidate"}; + Configurable tableOuterExpectedTimesXi{"tableOuterExpectedTimesXi", false, "Produce expected and measured times tables for xi candidate"}; + Configurable tableInnerNSigmaOmega{"tableInnerNSigmaOmega", true, "Produce NSigma tables for omega candidate"}; + Configurable tableOuterNSigmaOmega{"tableOuterNSigmaOmega", true, "Produce NSigma tables for omega candidate"}; + Configurable tableInnerExpectedTimesOmega{"tableInnerExpectedTimesOmega", false, "Produce expected and measured times tables for omega candidate"}; + Configurable tableOuterExpectedTimesOmega{"tableOuterExpectedTimesOmega", false, "Produce expected and measured times tables for omega candidate"}; + + // Histograms + Configurable histosK0S{"histosK0S", false, "Produce histograms for xi candidates"}; + Configurable histosLambda{"histosLambda", false, "Produce histograms for xi candidates"}; + Configurable histosAntiLambda{"histosAntiLambda", false, "Produce histograms for xi candidates"}; + Configurable histosXi{"histosXi", false, "Produce histograms for xi candidates"}; + Configurable histosAntiXi{"histosAntiXi", false, "Produce histograms for anti xi candidates"}; + Configurable histosOmega{"histosOmega", false, "Produce histograms for omega candidates"}; + Configurable histosAntiOmega{"histosAntiOmega", false, "Produce histograms for anti omega candidates"}; + } produce; + + Configurable magneticField{"magneticField", 20.0f, "Magnetic field (in kilogauss)"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + StrangenessTofPid tofPidCasc; + StrangenessTofPid tofPidV0; + Service pdgDB{}; + static constexpr float NanoToPico = 1e+3; + static constexpr int ChargeV0 = 0; + static constexpr std::array ParentTrackCovMatrix{}; + + struct TimeOfFlight { + float iTofResolution{}, iTofRadius{}; + float oTofResolution{}, oTofRadius{}; + } tof; + + void init(InitContext& initContext) + { + auto tryGetCfgFromTask = [&](const char* targetCfg, float& localVariable, float localCfg) { + if (!common::core::getTaskOptionValue(initContext, "on-the-fly-tof-pid", targetCfg, localVariable, false)) { + LOG(info) << "Failed to get '" << targetCfg << "' from on-the-fly-tof-pid, using localCfg value " << localCfg; + localVariable = localCfg; + } else { + LOG(info) << "Got '" << targetCfg << "' = " << localVariable << " from on-the-fly-tof-pid"; + } + }; + + tryGetCfgFromTask("innerTOFTimeReso", tof.iTofResolution, cfgTof.innerResolution); + tryGetCfgFromTask("innerTOFRadius", tof.iTofRadius, cfgTof.innerRadius); + tryGetCfgFromTask("outerTOFTimeReso", tof.oTofResolution, cfgTof.outerResolution); + tryGetCfgFromTask("outerTOFRadius", tof.oTofRadius, cfgTof.outerRadius); + + tofPidCasc.setResolution(tof.iTofResolution, tof.oTofResolution); + tofPidCasc.setRadius(tof.iTofRadius, tof.oTofRadius); + tofPidCasc.setMagneticField(magneticField); + tofPidV0.setResolution(tof.iTofResolution, tof.oTofResolution); + tofPidV0.setRadius(tof.iTofRadius, tof.oTofRadius); + tofPidV0.setMagneticField(magneticField); + + if (doprocessV0s) { + if (produce.histosK0S) { + histos.add("K0S/hInnerArrivalTimeDeltaNeg", "hInnerArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("K0S/hInnerArrivalTimeDeltaPos", "hInnerArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("K0S/hOuterArrivalTimeDeltaNeg", "hOuterArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("K0S/hOuterArrivalTimeDeltaPos", "hOuterArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + + histos.add("K0S/hInnerNSigmaNeg", "hInnerNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("K0S/hInnerNSigmaPos", "hInnerNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("K0S/hOuterNSigmaNeg", "hOuterNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("K0S/hOuterNSigmaPos", "hOuterNSigmaPos", kTH1D, {{axes.axisNSigma}}); + + histos.add("K0S/hInnerExpectedArrivalTimeNeg", "hInnerExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("K0S/hInnerExpectedArrivalTimePos", "hInnerExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("K0S/hOuterExpectedArrivalTimeNeg", "hOuterExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("K0S/hOuterExpectedArrivalTimePos", "hOuterExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + + histos.add("K0S/hInnerMeasuredArrivalTimeNeg", "hInnerMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("K0S/hInnerMeasuredArrivalTimePos", "hInnerMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("K0S/hOuterMeasuredArrivalTimeNeg", "hOuterMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("K0S/hOuterMeasuredArrivalTimePos", "hOuterMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + + histos.add("K0S/hInnerMeasuredVsExpectedArrivalTimeNeg", "hInnerMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("K0S/hInnerMeasuredVsExpectedArrivalTimePos", "hInnerMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("K0S/hOuterMeasuredVsExpectedArrivalTimeNeg", "hOuterMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("K0S/hOuterMeasuredVsExpectedArrivalTimePos", "hOuterMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + } + if (produce.histosLambda) { + histos.add("Lambda/hInnerArrivalTimeDeltaNeg", "hInnerArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Lambda/hInnerArrivalTimeDeltaPos", "hInnerArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Lambda/hOuterArrivalTimeDeltaNeg", "hOuterArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Lambda/hOuterArrivalTimeDeltaPos", "hOuterArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + + histos.add("Lambda/hInnerNSigmaNeg", "hInnerNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("Lambda/hInnerNSigmaPos", "hInnerNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("Lambda/hOuterNSigmaNeg", "hOuterNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("Lambda/hOuterNSigmaPos", "hOuterNSigmaPos", kTH1D, {{axes.axisNSigma}}); + + histos.add("Lambda/hInnerExpectedArrivalTimeNeg", "hInnerExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Lambda/hInnerExpectedArrivalTimePos", "hInnerExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Lambda/hOuterExpectedArrivalTimeNeg", "hOuterExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Lambda/hOuterExpectedArrivalTimePos", "hOuterExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + + histos.add("Lambda/hInnerMeasuredArrivalTimeNeg", "hInnerMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Lambda/hInnerMeasuredArrivalTimePos", "hInnerMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Lambda/hOuterMeasuredArrivalTimeNeg", "hOuterMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Lambda/hOuterMeasuredArrivalTimePos", "hOuterMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + + histos.add("Lambda/hInnerMeasuredVsExpectedArrivalTimeNeg", "hInnerMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Lambda/hInnerMeasuredVsExpectedArrivalTimePos", "hInnerMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Lambda/hOuterMeasuredVsExpectedArrivalTimeNeg", "hOuterMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Lambda/hOuterMeasuredVsExpectedArrivalTimePos", "hOuterMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + } + if (produce.histosAntiLambda) { + histos.add("AntiLambda/hInnerArrivalTimeDeltaNeg", "hInnerArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiLambda/hInnerArrivalTimeDeltaPos", "hInnerArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiLambda/hOuterArrivalTimeDeltaNeg", "hOuterArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiLambda/hOuterArrivalTimeDeltaPos", "hOuterArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + + histos.add("AntiLambda/hInnerNSigmaNeg", "hInnerNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiLambda/hInnerNSigmaPos", "hInnerNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiLambda/hOuterNSigmaNeg", "hOuterNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiLambda/hOuterNSigmaPos", "hOuterNSigmaPos", kTH1D, {{axes.axisNSigma}}); + + histos.add("AntiLambda/hInnerExpectedArrivalTimeNeg", "hInnerExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiLambda/hInnerExpectedArrivalTimePos", "hInnerExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiLambda/hOuterExpectedArrivalTimeNeg", "hOuterExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiLambda/hOuterExpectedArrivalTimePos", "hOuterExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + + histos.add("AntiLambda/hInnerMeasuredArrivalTimeNeg", "hInnerMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiLambda/hInnerMeasuredArrivalTimePos", "hInnerMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiLambda/hOuterMeasuredArrivalTimeNeg", "hOuterMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiLambda/hOuterMeasuredArrivalTimePos", "hOuterMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + + histos.add("AntiLambda/hInnerMeasuredVsExpectedArrivalTimeNeg", "hInnerMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiLambda/hInnerMeasuredVsExpectedArrivalTimePos", "hInnerMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiLambda/hOuterMeasuredVsExpectedArrivalTimeNeg", "hOuterMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiLambda/hOuterMeasuredVsExpectedArrivalTimePos", "hOuterMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + } + } + + if (doprocessCascades) { + if (produce.histosXi) { + histos.add("Xi/hInnerArrivalTimeDeltaNeg", "hInnerArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Xi/hInnerArrivalTimeDeltaPos", "hInnerArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Xi/hInnerArrivalTimeDeltaBach", "hInnerArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Xi/hOuterArrivalTimeDeltaNeg", "hOuterArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Xi/hOuterArrivalTimeDeltaPos", "hOuterArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Xi/hOuterArrivalTimeDeltaBach", "hOuterArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + + histos.add("Xi/hInnerNSigmaNeg", "hInnerNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("Xi/hInnerNSigmaPos", "hInnerNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("Xi/hInnerNSigmaBach", "hInnerNSigmaBach", kTH1D, {{axes.axisNSigma}}); + histos.add("Xi/hOuterNSigmaNeg", "hOuterNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("Xi/hOuterNSigmaPos", "hOuterNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("Xi/hOuterNSigmaBach", "hOuterNSigmaBach", kTH1D, {{axes.axisNSigma}}); + + histos.add("Xi/hInnerExpectedArrivalTimeNeg", "hInnerExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hInnerExpectedArrivalTimePos", "hInnerExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hInnerExpectedArrivalTimeBach", "hInnerExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hOuterExpectedArrivalTimeNeg", "hOuterExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hOuterExpectedArrivalTimePos", "hOuterExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hOuterExpectedArrivalTimeBach", "hOuterExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("Xi/hInnerMeasuredArrivalTimeNeg", "hInnerMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hInnerMeasuredArrivalTimePos", "hInnerMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hInnerMeasuredArrivalTimeBach", "hInnerMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hOuterMeasuredArrivalTimeNeg", "hOuterMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hOuterMeasuredArrivalTimePos", "hOuterMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Xi/hOuterMeasuredArrivalTimeBach", "hOuterMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("Xi/hInnerMeasuredVsExpectedArrivalTimeNeg", "hInnerMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Xi/hInnerMeasuredVsExpectedArrivalTimePos", "hInnerMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Xi/hInnerMeasuredVsExpectedArrivalTimeBach", "hInnerMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Xi/hOuterMeasuredVsExpectedArrivalTimeNeg", "hOuterMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Xi/hOuterMeasuredVsExpectedArrivalTimePos", "hOuterMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Xi/hOuterMeasuredVsExpectedArrivalTimeBach", "hOuterMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + } + + if (produce.histosAntiXi) { + histos.add("AntiXi/hInnerArrivalTimeDeltaNeg", "hInnerArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiXi/hInnerArrivalTimeDeltaPos", "hInnerArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiXi/hInnerArrivalTimeDeltaBach", "hInnerArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiXi/hOuterArrivalTimeDeltaNeg", "hOuterArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiXi/hOuterArrivalTimeDeltaPos", "hOuterArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiXi/hOuterArrivalTimeDeltaBach", "hOuterArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + + histos.add("AntiXi/hInnerNSigmaNeg", "hInnerNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiXi/hInnerNSigmaPos", "hInnerNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiXi/hInnerNSigmaBach", "hInnerNSigmaBach", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiXi/hOuterNSigmaNeg", "hOuterNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiXi/hOuterNSigmaPos", "hOuterNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiXi/hOuterNSigmaBach", "hOuterNSigmaBach", kTH1D, {{axes.axisNSigma}}); + + histos.add("AntiXi/hInnerExpectedArrivalTimeNeg", "hInnerExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hInnerExpectedArrivalTimePos", "hInnerExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hInnerExpectedArrivalTimeBach", "hInnerExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hOuterExpectedArrivalTimeNeg", "hOuterExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hOuterExpectedArrivalTimePos", "hOuterExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hOuterExpectedArrivalTimeBach", "hOuterExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("AntiXi/hInnerMeasuredArrivalTimeNeg", "hInnerMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hInnerMeasuredArrivalTimePos", "hInnerMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hInnerMeasuredArrivalTimeBach", "hInnerMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hOuterMeasuredArrivalTimeNeg", "hOuterMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hOuterMeasuredArrivalTimePos", "hOuterMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiXi/hOuterMeasuredArrivalTimeBach", "hOuterMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("AntiXi/hInnerMeasuredVsExpectedArrivalTimeNeg", "hInnerMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiXi/hInnerMeasuredVsExpectedArrivalTimePos", "hInnerMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiXi/hInnerMeasuredVsExpectedArrivalTimeBach", "hInnerMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiXi/hOuterMeasuredVsExpectedArrivalTimeNeg", "hOuterMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiXi/hOuterMeasuredVsExpectedArrivalTimePos", "hOuterMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiXi/hOuterMeasuredVsExpectedArrivalTimeBach", "hOuterMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + } + + if (produce.histosOmega) { + histos.add("Omega/hInnerArrivalTimeDeltaNeg", "hInnerArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Omega/hInnerArrivalTimeDeltaPos", "hInnerArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Omega/hInnerArrivalTimeDeltaBach", "hInnerArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Omega/hOuterArrivalTimeDeltaNeg", "hOuterArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Omega/hOuterArrivalTimeDeltaPos", "hOuterArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("Omega/hOuterArrivalTimeDeltaBach", "hOuterArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + + histos.add("Omega/hInnerNSigmaNeg", "hInnerNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("Omega/hInnerNSigmaPos", "hInnerNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("Omega/hInnerNSigmaBach", "hInnerNSigmaBach", kTH1D, {{axes.axisNSigma}}); + histos.add("Omega/hOuterNSigmaNeg", "hOuterNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("Omega/hOuterNSigmaPos", "hOuterNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("Omega/hOuterNSigmaBach", "hOuterNSigmaBach", kTH1D, {{axes.axisNSigma}}); + + histos.add("Omega/hInnerExpectedArrivalTimeNeg", "hInnerExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hInnerExpectedArrivalTimePos", "hInnerExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hInnerExpectedArrivalTimeBach", "hInnerExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hOuterExpectedArrivalTimeNeg", "hOuterExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hOuterExpectedArrivalTimePos", "hOuterExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hOuterExpectedArrivalTimeBach", "hOuterExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("Omega/hInnerMeasuredArrivalTimeNeg", "hInnerMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hInnerMeasuredArrivalTimePos", "hInnerMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hInnerMeasuredArrivalTimeBach", "hInnerMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hOuterMeasuredArrivalTimeNeg", "hOuterMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hOuterMeasuredArrivalTimePos", "hOuterMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("Omega/hOuterMeasuredArrivalTimeBach", "hOuterMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("Omega/hInnerMeasuredVsExpectedArrivalTimeNeg", "hInnerMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Omega/hInnerMeasuredVsExpectedArrivalTimePos", "hInnerMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Omega/hInnerMeasuredVsExpectedArrivalTimeBach", "hInnerMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Omega/hOuterMeasuredVsExpectedArrivalTimeNeg", "hOuterMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Omega/hOuterMeasuredVsExpectedArrivalTimePos", "hOuterMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("Omega/hOuterMeasuredVsExpectedArrivalTimeBach", "hOuterMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + } + + if (produce.histosAntiOmega) { + histos.add("AntiOmega/hInnerArrivalTimeDeltaNeg", "hInnerArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiOmega/hInnerArrivalTimeDeltaPos", "hInnerArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiOmega/hInnerArrivalTimeDeltaBach", "hInnerArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiOmega/hOuterArrivalTimeDeltaNeg", "hOuterArrivalTimeDeltaNeg", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiOmega/hOuterArrivalTimeDeltaPos", "hOuterArrivalTimeDeltaPos", kTH1D, {{axes.axisTimeDelta}}); + histos.add("AntiOmega/hOuterArrivalTimeDeltaBach", "hOuterArrivalTimeDeltaBach", kTH1D, {{axes.axisTimeDelta}}); + + histos.add("AntiOmega/hInnerNSigmaNeg", "hInnerNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiOmega/hInnerNSigmaPos", "hInnerNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiOmega/hInnerNSigmaBach", "hInnerNSigmaBach", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiOmega/hOuterNSigmaNeg", "hOuterNSigmaNeg", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiOmega/hOuterNSigmaPos", "hOuterNSigmaPos", kTH1D, {{axes.axisNSigma}}); + histos.add("AntiOmega/hOuterNSigmaBach", "hOuterNSigmaBach", kTH1D, {{axes.axisNSigma}}); + + histos.add("AntiOmega/hInnerExpectedArrivalTimeNeg", "hInnerExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hInnerExpectedArrivalTimePos", "hInnerExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hInnerExpectedArrivalTimeBach", "hInnerExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hOuterExpectedArrivalTimeNeg", "hOuterExpectedArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hOuterExpectedArrivalTimePos", "hOuterExpectedArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hOuterExpectedArrivalTimeBach", "hOuterExpectedArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("AntiOmega/hInnerMeasuredArrivalTimeNeg", "hInnerMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hInnerMeasuredArrivalTimePos", "hInnerMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hInnerMeasuredArrivalTimeBach", "hInnerMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hOuterMeasuredArrivalTimeNeg", "hOuterMeasuredArrivalTimeNeg", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hOuterMeasuredArrivalTimePos", "hOuterMeasuredArrivalTimePos", kTH1D, {{axes.axisTime}}); + histos.add("AntiOmega/hOuterMeasuredArrivalTimeBach", "hOuterMeasuredArrivalTimeBach", kTH1D, {{axes.axisTime}}); + + histos.add("AntiOmega/hInnerMeasuredVsExpectedArrivalTimeNeg", "hInnerMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiOmega/hInnerMeasuredVsExpectedArrivalTimePos", "hInnerMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiOmega/hInnerMeasuredVsExpectedArrivalTimeBach", "hInnerMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiOmega/hOuterMeasuredVsExpectedArrivalTimeNeg", "hOuterMeasuredVsExpectedArrivalTimeNeg;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiOmega/hOuterMeasuredVsExpectedArrivalTimePos", "hOuterMeasuredVsExpectedArrivalTimePos;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + histos.add("AntiOmega/hOuterMeasuredVsExpectedArrivalTimeBach", "hOuterMeasuredVsExpectedArrivalTimeBach;Measured time (ps);Expected time (ps)", kTH2D, {{axes.axisTime}, {axes.axisTime}}); + } + } + histos.print(); + } + + void processV0s(Alice3Collision const& collision, FullV0Candidates const& v0Candidates, Alice3Tracks const&, aod::McParticles const&, aod::McCollisions const&) + { + const std::array vtx = {collision.posX(), collision.posY(), collision.posZ()}; + const auto mcCollision = collision.template mcCollision_as(); + for (const auto& v0 : v0Candidates) { + const std::array v0SV = {v0.x(), v0.y(), v0.z()}; + const std::array v0P = {v0.px(), v0.py(), v0.pz()}; + o2::track::TrackParCov trackParCovV0(v0SV, v0P, ParentTrackCovMatrix, ChargeV0); + + const auto posTrack = v0.template posTrack_as(); + const auto posParticle = posTrack.template mcParticle_as(); + o2::track::TrackParCov positive = getTrackParCov(posTrack); + + const auto negTrack = v0.template negTrack_as(); + const auto negParticle = negTrack.template mcParticle_as(); + o2::track::TrackParCov negative = getTrackParCov(negTrack); + + tofPidV0.reset(); + tofPidV0.setTrack(TrackType::Positive, positive, mcCollision.t() + posParticle.vt(), pdgDB->Mass(posParticle.pdgCode())); + tofPidV0.setTrack(TrackType::Negative, negative, mcCollision.t() + negParticle.vt(), pdgDB->Mass(negParticle.pdgCode())); + tofPidV0.setTrack(TrackType::V0, trackParCovV0); // no track time + + V0TofResults straTofResultsK0S{}, straTofResultsLambda{}, straTofResultsAntiLambda{}; + straTofResultsK0S = tofPidV0.findNSigmas(vtx, v0SV); + straTofResultsLambda = tofPidV0.findNSigmas(vtx, v0SV); + straTofResultsAntiLambda = tofPidV0.findNSigmas(vtx, v0SV); + + if (produce.histosK0S) { + if (straTofResultsK0S.pos.hasInnerTof) { + histos.fill(HIST("K0S/hInnerArrivalTimeDeltaPos"), (straTofResultsK0S.pos.measuredTimeInner - straTofResultsK0S.pos.expectedTimeInner) * NanoToPico); + histos.fill(HIST("K0S/hInnerNSigmaPos"), straTofResultsK0S.pos.nSigmaInner); + histos.fill(HIST("K0S/hInnerExpectedArrivalTimePos"), straTofResultsK0S.pos.expectedTimeInner * NanoToPico); + histos.fill(HIST("K0S/hInnerMeasuredArrivalTimePos"), straTofResultsK0S.pos.measuredTimeInner * NanoToPico); + histos.fill(HIST("K0S/hInnerMeasuredVsExpectedArrivalTimePos"), straTofResultsK0S.pos.measuredTimeInner * NanoToPico, straTofResultsK0S.pos.expectedTimeInner * NanoToPico); + } + if (straTofResultsK0S.neg.hasInnerTof) { + histos.fill(HIST("K0S/hInnerArrivalTimeDeltaNeg"), (straTofResultsK0S.neg.measuredTimeInner - straTofResultsK0S.neg.expectedTimeInner) * NanoToPico); + histos.fill(HIST("K0S/hInnerNSigmaNeg"), straTofResultsK0S.neg.nSigmaInner); + histos.fill(HIST("K0S/hInnerExpectedArrivalTimeNeg"), straTofResultsK0S.neg.expectedTimeInner * NanoToPico); + histos.fill(HIST("K0S/hInnerMeasuredArrivalTimeNeg"), straTofResultsK0S.neg.measuredTimeInner * NanoToPico); + histos.fill(HIST("K0S/hInnerMeasuredVsExpectedArrivalTimeNeg"), straTofResultsK0S.neg.measuredTimeInner * NanoToPico, straTofResultsK0S.neg.expectedTimeInner * NanoToPico); + } + if (straTofResultsK0S.pos.hasOuterTof) { + histos.fill(HIST("K0S/hOuterArrivalTimeDeltaPos"), (straTofResultsK0S.pos.measuredTimeOuter - straTofResultsK0S.pos.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("K0S/hOuterNSigmaPos"), straTofResultsK0S.pos.nSigmaOuter); + histos.fill(HIST("K0S/hOuterExpectedArrivalTimePos"), straTofResultsK0S.pos.expectedTimeOuter * NanoToPico); + histos.fill(HIST("K0S/hOuterMeasuredArrivalTimePos"), straTofResultsK0S.pos.measuredTimeOuter * NanoToPico); + histos.fill(HIST("K0S/hOuterMeasuredVsExpectedArrivalTimePos"), straTofResultsK0S.pos.measuredTimeOuter * NanoToPico, straTofResultsK0S.pos.expectedTimeOuter * NanoToPico); + } + if (straTofResultsK0S.neg.hasOuterTof) { + histos.fill(HIST("K0S/hOuterArrivalTimeDeltaNeg"), (straTofResultsK0S.neg.measuredTimeOuter - straTofResultsK0S.neg.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("K0S/hOuterNSigmaNeg"), straTofResultsK0S.neg.nSigmaOuter); + histos.fill(HIST("K0S/hOuterExpectedArrivalTimeNeg"), straTofResultsK0S.neg.expectedTimeOuter * NanoToPico); + histos.fill(HIST("K0S/hOuterMeasuredArrivalTimeNeg"), straTofResultsK0S.neg.measuredTimeOuter * NanoToPico); + histos.fill(HIST("K0S/hOuterMeasuredVsExpectedArrivalTimeNeg"), straTofResultsK0S.neg.measuredTimeOuter * NanoToPico, straTofResultsK0S.neg.expectedTimeOuter * NanoToPico); + } + } + if (produce.histosLambda) { + if (straTofResultsLambda.pos.hasInnerTof) { + histos.fill(HIST("Lambda/hInnerArrivalTimeDeltaPos"), (straTofResultsLambda.pos.measuredTimeInner - straTofResultsLambda.pos.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Lambda/hInnerNSigmaPos"), straTofResultsLambda.pos.nSigmaInner); + histos.fill(HIST("Lambda/hInnerExpectedArrivalTimePos"), straTofResultsLambda.pos.expectedTimeInner * NanoToPico); + histos.fill(HIST("Lambda/hInnerMeasuredArrivalTimePos"), straTofResultsLambda.pos.measuredTimeInner * NanoToPico); + histos.fill(HIST("Lambda/hInnerMeasuredVsExpectedArrivalTimePos"), straTofResultsLambda.pos.measuredTimeInner * NanoToPico, straTofResultsLambda.pos.expectedTimeInner * NanoToPico); + } + if (straTofResultsLambda.neg.hasInnerTof) { + histos.fill(HIST("Lambda/hInnerArrivalTimeDeltaNeg"), (straTofResultsLambda.neg.measuredTimeInner - straTofResultsLambda.neg.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Lambda/hInnerNSigmaNeg"), straTofResultsLambda.neg.nSigmaInner); + histos.fill(HIST("Lambda/hInnerExpectedArrivalTimeNeg"), straTofResultsLambda.neg.expectedTimeInner * NanoToPico); + histos.fill(HIST("Lambda/hInnerMeasuredArrivalTimeNeg"), straTofResultsLambda.neg.measuredTimeInner * NanoToPico); + histos.fill(HIST("Lambda/hInnerMeasuredVsExpectedArrivalTimeNeg"), straTofResultsLambda.neg.measuredTimeInner * NanoToPico, straTofResultsLambda.neg.expectedTimeInner * NanoToPico); + } + if (straTofResultsLambda.pos.hasOuterTof) { + histos.fill(HIST("Lambda/hOuterArrivalTimeDeltaPos"), (straTofResultsLambda.pos.measuredTimeOuter - straTofResultsLambda.pos.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Lambda/hOuterNSigmaPos"), straTofResultsLambda.pos.nSigmaOuter); + histos.fill(HIST("Lambda/hOuterExpectedArrivalTimePos"), straTofResultsLambda.pos.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Lambda/hOuterMeasuredArrivalTimePos"), straTofResultsLambda.pos.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Lambda/hOuterMeasuredVsExpectedArrivalTimePos"), straTofResultsLambda.pos.measuredTimeOuter * NanoToPico, straTofResultsLambda.pos.expectedTimeOuter * NanoToPico); + } + if (straTofResultsLambda.neg.hasOuterTof) { + histos.fill(HIST("Lambda/hOuterArrivalTimeDeltaNeg"), (straTofResultsLambda.neg.measuredTimeOuter - straTofResultsLambda.neg.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Lambda/hOuterNSigmaNeg"), straTofResultsLambda.neg.nSigmaOuter); + histos.fill(HIST("Lambda/hOuterExpectedArrivalTimeNeg"), straTofResultsLambda.neg.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Lambda/hOuterMeasuredArrivalTimeNeg"), straTofResultsLambda.neg.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Lambda/hOuterMeasuredVsExpectedArrivalTimeNeg"), straTofResultsLambda.neg.measuredTimeOuter * NanoToPico, straTofResultsLambda.neg.expectedTimeOuter * NanoToPico); + } + } + if (produce.histosAntiLambda) { + if (straTofResultsAntiLambda.pos.hasInnerTof) { + histos.fill(HIST("AntiLambda/hInnerArrivalTimeDeltaPos"), (straTofResultsAntiLambda.pos.measuredTimeInner - straTofResultsAntiLambda.pos.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiLambda/hInnerNSigmaPos"), straTofResultsAntiLambda.pos.nSigmaInner); + histos.fill(HIST("AntiLambda/hInnerExpectedArrivalTimePos"), straTofResultsAntiLambda.pos.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiLambda/hInnerMeasuredArrivalTimePos"), straTofResultsAntiLambda.pos.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiLambda/hInnerMeasuredVsExpectedArrivalTimePos"), straTofResultsAntiLambda.pos.measuredTimeInner * NanoToPico, straTofResultsAntiLambda.pos.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiLambda.neg.hasInnerTof) { + histos.fill(HIST("AntiLambda/hInnerArrivalTimeDeltaNeg"), (straTofResultsAntiLambda.neg.measuredTimeInner - straTofResultsAntiLambda.neg.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiLambda/hInnerNSigmaNeg"), straTofResultsAntiLambda.neg.nSigmaInner); + histos.fill(HIST("AntiLambda/hInnerExpectedArrivalTimeNeg"), straTofResultsAntiLambda.neg.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiLambda/hInnerMeasuredArrivalTimeNeg"), straTofResultsAntiLambda.neg.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiLambda/hInnerMeasuredVsExpectedArrivalTimeNeg"), straTofResultsAntiLambda.neg.measuredTimeInner * NanoToPico, straTofResultsAntiLambda.neg.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiLambda.pos.hasOuterTof) { + histos.fill(HIST("AntiLambda/hOuterArrivalTimeDeltaPos"), (straTofResultsAntiLambda.pos.measuredTimeOuter - straTofResultsAntiLambda.pos.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiLambda/hOuterNSigmaPos"), straTofResultsAntiLambda.pos.nSigmaOuter); + histos.fill(HIST("AntiLambda/hOuterExpectedArrivalTimePos"), straTofResultsAntiLambda.pos.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiLambda/hOuterMeasuredArrivalTimePos"), straTofResultsAntiLambda.pos.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiLambda/hOuterMeasuredVsExpectedArrivalTimePos"), straTofResultsAntiLambda.pos.measuredTimeOuter * NanoToPico, straTofResultsAntiLambda.pos.expectedTimeOuter * NanoToPico); + } + if (straTofResultsAntiLambda.neg.hasOuterTof) { + histos.fill(HIST("AntiLambda/hOuterArrivalTimeDeltaNeg"), (straTofResultsAntiLambda.neg.measuredTimeOuter - straTofResultsAntiLambda.neg.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiLambda/hOuterNSigmaNeg"), straTofResultsAntiLambda.neg.nSigmaOuter); + histos.fill(HIST("AntiLambda/hOuterExpectedArrivalTimeNeg"), straTofResultsAntiLambda.neg.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiLambda/hOuterMeasuredArrivalTimeNeg"), straTofResultsAntiLambda.neg.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiLambda/hOuterMeasuredVsExpectedArrivalTimeNeg"), straTofResultsAntiLambda.neg.measuredTimeOuter * NanoToPico, straTofResultsAntiLambda.neg.expectedTimeOuter * NanoToPico); + } + } + + if (produce.tableInnerNSigmaK0S) { + tableA3K0SInnerTofPid( + (straTofResultsK0S.pos.hasInnerTof) ? straTofResultsK0S.pos.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsK0S.neg.hasInnerTof) ? straTofResultsK0S.neg.nSigmaInner : o2::upgrade::pid::NoPidSignal); + } + if (produce.tableOuterNSigmaK0S) { + tableA3K0SOuterTofPid( + (straTofResultsK0S.pos.hasOuterTof) ? straTofResultsK0S.pos.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsK0S.neg.hasOuterTof) ? straTofResultsK0S.neg.nSigmaOuter : o2::upgrade::pid::NoPidSignal); + } + if (produce.tableInnerNSigmaLambda) { + tableA3LambdaInnerTofPid( + (straTofResultsLambda.pos.hasInnerTof) ? straTofResultsLambda.pos.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiLambda.pos.hasInnerTof) ? straTofResultsAntiLambda.pos.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiLambda.neg.hasInnerTof) ? straTofResultsAntiLambda.neg.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsLambda.neg.hasInnerTof) ? straTofResultsLambda.neg.nSigmaInner : o2::upgrade::pid::NoPidSignal); + } + if (produce.tableOuterNSigmaLambda) { + tableA3LambdaOuterTofPid( + (straTofResultsLambda.pos.hasOuterTof) ? straTofResultsLambda.pos.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiLambda.pos.hasOuterTof) ? straTofResultsAntiLambda.pos.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiLambda.neg.hasOuterTof) ? straTofResultsAntiLambda.neg.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsLambda.neg.hasOuterTof) ? straTofResultsLambda.neg.nSigmaOuter : o2::upgrade::pid::NoPidSignal); + } + if (produce.tableInnerExpectedTimesK0S) { + tableA3K0SExpectedInnerTimes( + straTofResultsK0S.pos.expectedTimeInner, + straTofResultsK0S.neg.expectedTimeInner, + straTofResultsK0S.pos.measuredTimeInner, + straTofResultsK0S.neg.measuredTimeInner); + } + if (produce.tableOuterExpectedTimesK0S) { + tableA3K0SExpectedOuterTimes( + straTofResultsK0S.pos.expectedTimeOuter, + straTofResultsK0S.neg.expectedTimeOuter, + straTofResultsK0S.pos.measuredTimeOuter, + straTofResultsK0S.neg.measuredTimeOuter); + } + if (produce.tableInnerExpectedTimesLambda) { + tableA3LambdaExpectedInnerTimes( + straTofResultsLambda.pos.expectedTimeInner, + straTofResultsAntiLambda.pos.expectedTimeInner, + straTofResultsAntiLambda.neg.expectedTimeInner, + straTofResultsLambda.neg.expectedTimeInner, + straTofResultsLambda.pos.measuredTimeInner, + straTofResultsAntiLambda.pos.measuredTimeInner, + straTofResultsAntiLambda.neg.measuredTimeInner, + straTofResultsLambda.neg.measuredTimeInner); + } + if (produce.tableOuterExpectedTimesLambda) { + tableA3LambdaExpectedOuterTimes( + straTofResultsLambda.pos.expectedTimeOuter, + straTofResultsAntiLambda.pos.expectedTimeOuter, + straTofResultsAntiLambda.neg.expectedTimeOuter, + straTofResultsLambda.neg.expectedTimeOuter, + straTofResultsLambda.pos.measuredTimeOuter, + straTofResultsAntiLambda.pos.measuredTimeOuter, + straTofResultsAntiLambda.neg.measuredTimeOuter, + straTofResultsLambda.neg.measuredTimeOuter); + } + } + } + + void processCascades(Alice3Collision const& collision, FullCascadeCandidates const& cascadeCandidates, Alice3Tracks const&, aod::McParticles const&, aod::McCollisions const&) + { + const std::array vtx = {collision.posX(), collision.posY(), collision.posZ()}; + const auto mcCollision = collision.template mcCollision_as(); + for (const auto& casc : cascadeCandidates) { + const std::array cascSV = {casc.x(), casc.y(), casc.z()}; + const std::array cascP = {casc.px(), casc.py(), casc.pz()}; + const int chargeCascade = (casc.sign() > 0) ? 1 : -1; + o2::track::TrackParCov trackParCovCasc(cascSV, cascP, ParentTrackCovMatrix, chargeCascade); + + const std::array v0SV = {casc.xlambda(), casc.ylambda(), casc.zlambda()}; + const std::array v0P = {casc.pxpos() + casc.pxneg(), casc.pypos() + casc.pyneg(), casc.pzpos() + casc.pzneg()}; + o2::track::TrackParCov trackParCovV0(v0SV, v0P, ParentTrackCovMatrix, ChargeV0); + + const auto bachTrack = casc.template bachelor_as(); + const auto bachParticle = bachTrack.template mcParticle_as(); + o2::track::TrackParCov bachelor = getTrackParCov(bachTrack); + + const auto posTrack = casc.template posTrack_as(); + const auto posParticle = posTrack.template mcParticle_as(); + o2::track::TrackParCov positive = getTrackParCov(posTrack); + + const auto negTrack = casc.template negTrack_as(); + const auto negParticle = negTrack.template mcParticle_as(); + o2::track::TrackParCov negative = getTrackParCov(negTrack); + + tofPidCasc.reset(); + tofPidCasc.setTrack(TrackType::Positive, positive, mcCollision.t() + posParticle.vt(), pdgDB->Mass(posParticle.pdgCode())); + tofPidCasc.setTrack(TrackType::Negative, negative, mcCollision.t() + negParticle.vt(), pdgDB->Mass(negParticle.pdgCode())); + tofPidCasc.setTrack(TrackType::Bachelor, bachelor, mcCollision.t() + bachParticle.vt(), pdgDB->Mass(bachParticle.pdgCode())); + tofPidCasc.setTrack(TrackType::V0, trackParCovV0); // no track time + tofPidCasc.setTrack(TrackType::Cascade, trackParCovCasc); // no track time + + CascTofResults straTofResultsXi{}, straTofResultsAntiXi{}, straTofResultsOmega{}, straTofResultsAntiOmega{}; + straTofResultsXi = tofPidCasc.findNSigmas(vtx, cascSV, v0SV); + straTofResultsAntiXi = tofPidCasc.findNSigmas(vtx, cascSV, v0SV); + straTofResultsOmega = tofPidCasc.findNSigmas(vtx, cascSV, v0SV); + straTofResultsAntiOmega = tofPidCasc.findNSigmas(vtx, cascSV, v0SV); + + if (produce.histosXi) { + if (straTofResultsXi.pos.hasInnerTof) { + histos.fill(HIST("Xi/hInnerArrivalTimeDeltaPos"), (straTofResultsXi.pos.measuredTimeInner - straTofResultsXi.pos.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Xi/hInnerNSigmaPos"), straTofResultsXi.pos.nSigmaInner); + histos.fill(HIST("Xi/hInnerExpectedArrivalTimePos"), straTofResultsXi.pos.expectedTimeInner * NanoToPico); + histos.fill(HIST("Xi/hInnerMeasuredArrivalTimePos"), straTofResultsXi.pos.measuredTimeInner * NanoToPico); + histos.fill(HIST("Xi/hInnerMeasuredVsExpectedArrivalTimePos"), straTofResultsXi.pos.measuredTimeInner * NanoToPico, straTofResultsXi.pos.expectedTimeInner * NanoToPico); + } + if (straTofResultsXi.neg.hasInnerTof) { + histos.fill(HIST("Xi/hInnerArrivalTimeDeltaNeg"), (straTofResultsXi.neg.measuredTimeInner - straTofResultsXi.neg.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Xi/hInnerNSigmaNeg"), straTofResultsXi.neg.nSigmaInner); + histos.fill(HIST("Xi/hInnerExpectedArrivalTimeNeg"), straTofResultsXi.neg.expectedTimeInner * NanoToPico); + histos.fill(HIST("Xi/hInnerMeasuredArrivalTimeNeg"), straTofResultsXi.neg.measuredTimeInner * NanoToPico); + histos.fill(HIST("Xi/hInnerMeasuredVsExpectedArrivalTimeNeg"), straTofResultsXi.neg.measuredTimeInner * NanoToPico, straTofResultsXi.neg.expectedTimeInner * NanoToPico); + } + if (straTofResultsXi.bach.hasInnerTof) { + histos.fill(HIST("Xi/hInnerArrivalTimeDeltaBach"), (straTofResultsXi.bach.measuredTimeInner - straTofResultsXi.bach.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Xi/hInnerNSigmaBach"), straTofResultsXi.bach.nSigmaInner); + histos.fill(HIST("Xi/hInnerExpectedArrivalTimeBach"), straTofResultsXi.bach.expectedTimeInner * NanoToPico); + histos.fill(HIST("Xi/hInnerMeasuredArrivalTimeBach"), straTofResultsXi.bach.measuredTimeInner * NanoToPico); + histos.fill(HIST("Xi/hInnerMeasuredVsExpectedArrivalTimeBach"), straTofResultsXi.bach.measuredTimeInner * NanoToPico, straTofResultsXi.bach.expectedTimeInner * NanoToPico); + } + if (straTofResultsXi.pos.hasOuterTof) { + histos.fill(HIST("Xi/hOuterArrivalTimeDeltaPos"), (straTofResultsXi.pos.measuredTimeOuter - straTofResultsXi.pos.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Xi/hOuterNSigmaPos"), straTofResultsXi.pos.nSigmaOuter); + histos.fill(HIST("Xi/hOuterExpectedArrivalTimePos"), straTofResultsXi.pos.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Xi/hOuterMeasuredArrivalTimePos"), straTofResultsXi.pos.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Xi/hOuterMeasuredVsExpectedArrivalTimePos"), straTofResultsXi.pos.measuredTimeOuter * NanoToPico, straTofResultsXi.pos.expectedTimeOuter * NanoToPico); + } + if (straTofResultsXi.neg.hasOuterTof) { + histos.fill(HIST("Xi/hOuterArrivalTimeDeltaNeg"), (straTofResultsXi.neg.measuredTimeOuter - straTofResultsXi.neg.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Xi/hOuterNSigmaNeg"), straTofResultsXi.neg.nSigmaOuter); + histos.fill(HIST("Xi/hOuterExpectedArrivalTimeNeg"), straTofResultsXi.neg.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Xi/hOuterMeasuredArrivalTimeNeg"), straTofResultsXi.neg.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Xi/hOuterMeasuredVsExpectedArrivalTimeNeg"), straTofResultsXi.neg.measuredTimeOuter * NanoToPico, straTofResultsXi.neg.expectedTimeOuter * NanoToPico); + } + if (straTofResultsXi.bach.hasOuterTof) { + histos.fill(HIST("Xi/hOuterArrivalTimeDeltaBach"), (straTofResultsXi.bach.measuredTimeOuter - straTofResultsXi.bach.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Xi/hOuterNSigmaBach"), straTofResultsXi.bach.nSigmaOuter); + histos.fill(HIST("Xi/hOuterExpectedArrivalTimeBach"), straTofResultsXi.bach.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Xi/hOuterMeasuredArrivalTimeBach"), straTofResultsXi.bach.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Xi/hOuterMeasuredVsExpectedArrivalTimeBach"), straTofResultsXi.bach.measuredTimeOuter * NanoToPico, straTofResultsXi.bach.expectedTimeOuter * NanoToPico); + } + } + if (produce.histosAntiXi) { + if (straTofResultsAntiXi.pos.hasInnerTof) { + histos.fill(HIST("AntiXi/hInnerArrivalTimeDeltaPos"), (straTofResultsAntiXi.pos.measuredTimeInner - straTofResultsAntiXi.pos.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiXi/hInnerNSigmaPos"), straTofResultsAntiXi.pos.nSigmaInner); + histos.fill(HIST("AntiXi/hInnerExpectedArrivalTimePos"), straTofResultsAntiXi.pos.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiXi/hInnerMeasuredArrivalTimePos"), straTofResultsAntiXi.pos.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiXi/hInnerMeasuredVsExpectedArrivalTimePos"), straTofResultsAntiXi.pos.measuredTimeInner * NanoToPico, straTofResultsAntiXi.pos.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiXi.neg.hasInnerTof) { + histos.fill(HIST("AntiXi/hInnerArrivalTimeDeltaNeg"), (straTofResultsAntiXi.neg.measuredTimeInner - straTofResultsAntiXi.neg.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiXi/hInnerNSigmaNeg"), straTofResultsAntiXi.neg.nSigmaInner); + histos.fill(HIST("AntiXi/hInnerExpectedArrivalTimeNeg"), straTofResultsAntiXi.neg.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiXi/hInnerMeasuredArrivalTimeNeg"), straTofResultsAntiXi.neg.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiXi/hInnerMeasuredVsExpectedArrivalTimeNeg"), straTofResultsAntiXi.neg.measuredTimeInner * NanoToPico, straTofResultsAntiXi.neg.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiXi.bach.hasInnerTof) { + histos.fill(HIST("AntiXi/hInnerArrivalTimeDeltaBach"), (straTofResultsAntiXi.bach.measuredTimeInner - straTofResultsAntiXi.bach.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiXi/hInnerNSigmaBach"), straTofResultsAntiXi.bach.nSigmaInner); + histos.fill(HIST("AntiXi/hInnerExpectedArrivalTimeBach"), straTofResultsAntiXi.bach.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiXi/hInnerMeasuredArrivalTimeBach"), straTofResultsAntiXi.bach.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiXi/hInnerMeasuredVsExpectedArrivalTimeBach"), straTofResultsAntiXi.bach.measuredTimeInner * NanoToPico, straTofResultsAntiXi.bach.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiXi.pos.hasOuterTof) { + histos.fill(HIST("AntiXi/hOuterArrivalTimeDeltaPos"), (straTofResultsAntiXi.pos.measuredTimeOuter - straTofResultsAntiXi.pos.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiXi/hOuterNSigmaPos"), straTofResultsAntiXi.pos.nSigmaOuter); + histos.fill(HIST("AntiXi/hOuterExpectedArrivalTimePos"), straTofResultsAntiXi.pos.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiXi/hOuterMeasuredArrivalTimePos"), straTofResultsAntiXi.pos.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiXi/hOuterMeasuredVsExpectedArrivalTimePos"), straTofResultsAntiXi.pos.measuredTimeOuter * NanoToPico, straTofResultsAntiXi.pos.expectedTimeOuter * NanoToPico); + } + if (straTofResultsAntiXi.neg.hasOuterTof) { + histos.fill(HIST("AntiXi/hOuterArrivalTimeDeltaNeg"), (straTofResultsAntiXi.neg.measuredTimeOuter - straTofResultsAntiXi.neg.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiXi/hOuterNSigmaNeg"), straTofResultsAntiXi.neg.nSigmaOuter); + histos.fill(HIST("AntiXi/hOuterExpectedArrivalTimeNeg"), straTofResultsAntiXi.neg.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiXi/hOuterMeasuredArrivalTimeNeg"), straTofResultsAntiXi.neg.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiXi/hOuterMeasuredVsExpectedArrivalTimeNeg"), straTofResultsAntiXi.neg.measuredTimeOuter * NanoToPico, straTofResultsAntiXi.neg.expectedTimeOuter * NanoToPico); + } + if (straTofResultsAntiXi.bach.hasOuterTof) { + histos.fill(HIST("AntiXi/hOuterArrivalTimeDeltaBach"), (straTofResultsAntiXi.bach.measuredTimeOuter - straTofResultsAntiXi.bach.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiXi/hOuterNSigmaBach"), straTofResultsAntiXi.bach.nSigmaOuter); + histos.fill(HIST("AntiXi/hOuterExpectedArrivalTimeBach"), straTofResultsAntiXi.bach.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiXi/hOuterMeasuredArrivalTimeBach"), straTofResultsAntiXi.bach.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiXi/hOuterMeasuredVsExpectedArrivalTimeBach"), straTofResultsAntiXi.bach.measuredTimeOuter * NanoToPico, straTofResultsAntiXi.bach.expectedTimeOuter * NanoToPico); + } + } + if (produce.histosOmega) { + if (straTofResultsOmega.pos.hasInnerTof) { + histos.fill(HIST("Omega/hInnerArrivalTimeDeltaPos"), (straTofResultsOmega.pos.measuredTimeInner - straTofResultsOmega.pos.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Omega/hInnerNSigmaPos"), straTofResultsOmega.pos.nSigmaInner); + histos.fill(HIST("Omega/hInnerExpectedArrivalTimePos"), straTofResultsOmega.pos.expectedTimeInner * NanoToPico); + histos.fill(HIST("Omega/hInnerMeasuredArrivalTimePos"), straTofResultsOmega.pos.measuredTimeInner * NanoToPico); + histos.fill(HIST("Omega/hInnerMeasuredVsExpectedArrivalTimePos"), straTofResultsOmega.pos.measuredTimeInner * NanoToPico, straTofResultsOmega.pos.expectedTimeInner * NanoToPico); + } + if (straTofResultsOmega.neg.hasInnerTof) { + histos.fill(HIST("Omega/hInnerArrivalTimeDeltaNeg"), (straTofResultsOmega.neg.measuredTimeInner - straTofResultsOmega.neg.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Omega/hInnerNSigmaNeg"), straTofResultsOmega.neg.nSigmaInner); + histos.fill(HIST("Omega/hInnerExpectedArrivalTimeNeg"), straTofResultsOmega.neg.expectedTimeInner * NanoToPico); + histos.fill(HIST("Omega/hInnerMeasuredArrivalTimeNeg"), straTofResultsOmega.neg.measuredTimeInner * NanoToPico); + histos.fill(HIST("Omega/hInnerMeasuredVsExpectedArrivalTimeNeg"), straTofResultsOmega.neg.measuredTimeInner * NanoToPico, straTofResultsOmega.neg.expectedTimeInner * NanoToPico); + } + if (straTofResultsOmega.bach.hasInnerTof) { + histos.fill(HIST("Omega/hInnerArrivalTimeDeltaBach"), (straTofResultsOmega.bach.measuredTimeInner - straTofResultsOmega.bach.expectedTimeInner) * NanoToPico); + histos.fill(HIST("Omega/hInnerNSigmaBach"), straTofResultsOmega.bach.nSigmaInner); + histos.fill(HIST("Omega/hInnerExpectedArrivalTimeBach"), straTofResultsOmega.bach.expectedTimeInner * NanoToPico); + histos.fill(HIST("Omega/hInnerMeasuredArrivalTimeBach"), straTofResultsOmega.bach.measuredTimeInner * NanoToPico); + histos.fill(HIST("Omega/hInnerMeasuredVsExpectedArrivalTimeBach"), straTofResultsOmega.bach.measuredTimeInner * NanoToPico, straTofResultsOmega.bach.expectedTimeInner * NanoToPico); + } + if (straTofResultsOmega.pos.hasOuterTof) { + histos.fill(HIST("Omega/hOuterArrivalTimeDeltaPos"), (straTofResultsOmega.pos.measuredTimeOuter - straTofResultsOmega.pos.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Omega/hOuterNSigmaPos"), straTofResultsOmega.pos.nSigmaOuter); + histos.fill(HIST("Omega/hOuterExpectedArrivalTimePos"), straTofResultsOmega.pos.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Omega/hOuterMeasuredArrivalTimePos"), straTofResultsOmega.pos.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Omega/hOuterMeasuredVsExpectedArrivalTimePos"), straTofResultsOmega.pos.measuredTimeOuter * NanoToPico, straTofResultsOmega.pos.expectedTimeOuter * NanoToPico); + } + if (straTofResultsOmega.neg.hasOuterTof) { + histos.fill(HIST("Omega/hOuterArrivalTimeDeltaNeg"), (straTofResultsOmega.neg.measuredTimeOuter - straTofResultsOmega.neg.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Omega/hOuterNSigmaNeg"), straTofResultsOmega.neg.nSigmaOuter); + histos.fill(HIST("Omega/hOuterExpectedArrivalTimeNeg"), straTofResultsOmega.neg.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Omega/hOuterMeasuredArrivalTimeNeg"), straTofResultsOmega.neg.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Omega/hOuterMeasuredVsExpectedArrivalTimeNeg"), straTofResultsOmega.neg.measuredTimeOuter * NanoToPico, straTofResultsOmega.neg.expectedTimeOuter * NanoToPico); + } + if (straTofResultsOmega.bach.hasOuterTof) { + histos.fill(HIST("Omega/hOuterArrivalTimeDeltaBach"), (straTofResultsOmega.bach.measuredTimeOuter - straTofResultsOmega.bach.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("Omega/hOuterNSigmaBach"), straTofResultsOmega.bach.nSigmaOuter); + histos.fill(HIST("Omega/hOuterExpectedArrivalTimeBach"), straTofResultsOmega.bach.expectedTimeOuter * NanoToPico); + histos.fill(HIST("Omega/hOuterMeasuredArrivalTimeBach"), straTofResultsOmega.bach.measuredTimeOuter * NanoToPico); + histos.fill(HIST("Omega/hOuterMeasuredVsExpectedArrivalTimeBach"), straTofResultsOmega.bach.measuredTimeOuter * NanoToPico, straTofResultsOmega.bach.expectedTimeOuter * NanoToPico); + } + } + + if (produce.histosAntiOmega) { + if (straTofResultsAntiOmega.pos.hasInnerTof) { + histos.fill(HIST("AntiOmega/hInnerArrivalTimeDeltaPos"), (straTofResultsAntiOmega.pos.measuredTimeInner - straTofResultsAntiOmega.pos.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerNSigmaPos"), straTofResultsAntiOmega.pos.nSigmaInner); + histos.fill(HIST("AntiOmega/hInnerExpectedArrivalTimePos"), straTofResultsAntiOmega.pos.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerMeasuredArrivalTimePos"), straTofResultsAntiOmega.pos.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerMeasuredVsExpectedArrivalTimePos"), straTofResultsAntiOmega.pos.measuredTimeInner * NanoToPico, straTofResultsAntiOmega.pos.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiOmega.neg.hasInnerTof) { + histos.fill(HIST("AntiOmega/hInnerArrivalTimeDeltaNeg"), (straTofResultsAntiOmega.neg.measuredTimeInner - straTofResultsAntiOmega.neg.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerNSigmaNeg"), straTofResultsAntiOmega.neg.nSigmaInner); + histos.fill(HIST("AntiOmega/hInnerExpectedArrivalTimeNeg"), straTofResultsAntiOmega.neg.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerMeasuredArrivalTimeNeg"), straTofResultsAntiOmega.neg.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerMeasuredVsExpectedArrivalTimeNeg"), straTofResultsAntiOmega.neg.measuredTimeInner * NanoToPico, straTofResultsAntiOmega.neg.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiOmega.bach.hasInnerTof) { + histos.fill(HIST("AntiOmega/hInnerArrivalTimeDeltaBach"), (straTofResultsAntiOmega.bach.measuredTimeInner - straTofResultsAntiOmega.bach.expectedTimeInner) * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerNSigmaBach"), straTofResultsAntiOmega.bach.nSigmaInner); + histos.fill(HIST("AntiOmega/hInnerExpectedArrivalTimeBach"), straTofResultsAntiOmega.bach.expectedTimeInner * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerMeasuredArrivalTimeBach"), straTofResultsAntiOmega.bach.measuredTimeInner * NanoToPico); + histos.fill(HIST("AntiOmega/hInnerMeasuredVsExpectedArrivalTimeBach"), straTofResultsAntiOmega.bach.measuredTimeInner * NanoToPico, straTofResultsAntiOmega.bach.expectedTimeInner * NanoToPico); + } + if (straTofResultsAntiOmega.pos.hasOuterTof) { + histos.fill(HIST("AntiOmega/hOuterArrivalTimeDeltaPos"), (straTofResultsAntiOmega.pos.measuredTimeOuter - straTofResultsAntiOmega.pos.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterNSigmaPos"), straTofResultsAntiOmega.pos.nSigmaOuter); + histos.fill(HIST("AntiOmega/hOuterExpectedArrivalTimePos"), straTofResultsAntiOmega.pos.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterMeasuredArrivalTimePos"), straTofResultsAntiOmega.pos.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterMeasuredVsExpectedArrivalTimePos"), straTofResultsAntiOmega.pos.measuredTimeOuter * NanoToPico, straTofResultsAntiOmega.pos.expectedTimeOuter * NanoToPico); + } + if (straTofResultsAntiOmega.neg.hasOuterTof) { + histos.fill(HIST("AntiOmega/hOuterArrivalTimeDeltaNeg"), (straTofResultsAntiOmega.neg.measuredTimeOuter - straTofResultsAntiOmega.neg.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterNSigmaNeg"), straTofResultsAntiOmega.neg.nSigmaOuter); + histos.fill(HIST("AntiOmega/hOuterExpectedArrivalTimeNeg"), straTofResultsAntiOmega.neg.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterMeasuredArrivalTimeNeg"), straTofResultsAntiOmega.neg.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterMeasuredVsExpectedArrivalTimeNeg"), straTofResultsAntiOmega.neg.measuredTimeOuter * NanoToPico, straTofResultsAntiOmega.neg.expectedTimeOuter * NanoToPico); + } + if (straTofResultsAntiOmega.bach.hasOuterTof) { + histos.fill(HIST("AntiOmega/hOuterArrivalTimeDeltaBach"), (straTofResultsAntiOmega.bach.measuredTimeOuter - straTofResultsAntiOmega.bach.expectedTimeOuter) * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterNSigmaBach"), straTofResultsAntiOmega.bach.nSigmaOuter); + histos.fill(HIST("AntiOmega/hOuterExpectedArrivalTimeBach"), straTofResultsAntiOmega.bach.expectedTimeOuter * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterMeasuredArrivalTimeBach"), straTofResultsAntiOmega.bach.measuredTimeOuter * NanoToPico); + histos.fill(HIST("AntiOmega/hOuterMeasuredVsExpectedArrivalTimeBach"), straTofResultsAntiOmega.bach.measuredTimeOuter * NanoToPico, straTofResultsAntiOmega.bach.expectedTimeOuter * NanoToPico); + } + } + + if (produce.tableInnerNSigmaXi) { + tableA3XiInnerTofPid( + (straTofResultsXi.bach.hasInnerTof) ? straTofResultsXi.bach.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsXi.pos.hasInnerTof) ? straTofResultsXi.pos.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiXi.pos.hasInnerTof) ? straTofResultsAntiXi.pos.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiXi.neg.hasInnerTof) ? straTofResultsAntiXi.neg.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsXi.neg.hasInnerTof) ? straTofResultsXi.neg.nSigmaInner : o2::upgrade::pid::NoPidSignal); + } + if (produce.tableOuterNSigmaXi) { + tableA3XiOuterTofPid( + (straTofResultsXi.bach.hasOuterTof) ? straTofResultsXi.bach.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsXi.pos.hasOuterTof) ? straTofResultsXi.pos.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiXi.pos.hasOuterTof) ? straTofResultsAntiXi.pos.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiXi.neg.hasOuterTof) ? straTofResultsAntiXi.neg.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsXi.neg.hasOuterTof) ? straTofResultsXi.neg.nSigmaOuter : o2::upgrade::pid::NoPidSignal); + } + if (produce.tableInnerNSigmaOmega) { + tableA3OmegaInnerTofPid( + (straTofResultsOmega.bach.hasInnerTof) ? straTofResultsOmega.bach.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsOmega.pos.hasInnerTof) ? straTofResultsOmega.pos.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiOmega.pos.hasInnerTof) ? straTofResultsAntiOmega.pos.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiOmega.neg.hasInnerTof) ? straTofResultsAntiOmega.neg.nSigmaInner : o2::upgrade::pid::NoPidSignal, + (straTofResultsOmega.neg.hasInnerTof) ? straTofResultsOmega.neg.nSigmaInner : o2::upgrade::pid::NoPidSignal); + } + if (produce.tableOuterNSigmaOmega) { + tableA3OmegaOuterTofPid( + (straTofResultsOmega.bach.hasOuterTof) ? straTofResultsOmega.bach.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsOmega.pos.hasOuterTof) ? straTofResultsOmega.pos.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiOmega.pos.hasOuterTof) ? straTofResultsAntiOmega.pos.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsAntiOmega.neg.hasOuterTof) ? straTofResultsAntiOmega.neg.nSigmaOuter : o2::upgrade::pid::NoPidSignal, + (straTofResultsOmega.neg.hasOuterTof) ? straTofResultsOmega.neg.nSigmaOuter : o2::upgrade::pid::NoPidSignal); + } + + if (produce.tableInnerExpectedTimesXi) { + tableA3XiExpectedInnerTimes( + straTofResultsXi.bach.expectedTimeInner, + straTofResultsXi.pos.expectedTimeInner, + straTofResultsAntiXi.pos.expectedTimeInner, + straTofResultsAntiXi.neg.expectedTimeInner, + straTofResultsXi.neg.expectedTimeInner, + straTofResultsXi.bach.measuredTimeInner, + straTofResultsXi.pos.measuredTimeInner, + straTofResultsAntiXi.pos.measuredTimeInner, + straTofResultsAntiXi.neg.measuredTimeInner, + straTofResultsXi.neg.measuredTimeInner); + } + if (produce.tableOuterExpectedTimesXi) { + tableA3XiExpectedOuterTimes( + straTofResultsXi.bach.expectedTimeOuter, + straTofResultsXi.pos.expectedTimeOuter, + straTofResultsAntiXi.pos.expectedTimeOuter, + straTofResultsAntiXi.neg.expectedTimeOuter, + straTofResultsXi.neg.expectedTimeOuter, + straTofResultsXi.bach.measuredTimeOuter, + straTofResultsXi.pos.measuredTimeOuter, + straTofResultsAntiXi.pos.measuredTimeOuter, + straTofResultsAntiXi.neg.measuredTimeOuter, + straTofResultsXi.neg.measuredTimeOuter); + } + if (produce.tableInnerExpectedTimesOmega) { + tableA3OmegaExpectedInnerTimes( + straTofResultsOmega.bach.expectedTimeInner, + straTofResultsOmega.pos.expectedTimeInner, + straTofResultsAntiOmega.pos.expectedTimeInner, + straTofResultsAntiOmega.neg.expectedTimeInner, + straTofResultsOmega.neg.expectedTimeInner, + straTofResultsOmega.bach.measuredTimeInner, + straTofResultsOmega.pos.measuredTimeInner, + straTofResultsAntiOmega.pos.measuredTimeInner, + straTofResultsAntiOmega.neg.measuredTimeInner, + straTofResultsOmega.neg.measuredTimeInner); + } + if (produce.tableOuterExpectedTimesOmega) { + tableA3OmegaExpectedOuterTimes( + straTofResultsOmega.bach.expectedTimeOuter, + straTofResultsOmega.pos.expectedTimeOuter, + straTofResultsAntiOmega.pos.expectedTimeOuter, + straTofResultsAntiOmega.neg.expectedTimeOuter, + straTofResultsOmega.neg.expectedTimeOuter, + straTofResultsOmega.bach.measuredTimeOuter, + straTofResultsOmega.pos.measuredTimeOuter, + straTofResultsAntiOmega.pos.measuredTimeOuter, + straTofResultsAntiOmega.neg.measuredTimeOuter, + straTofResultsOmega.neg.measuredTimeOuter); + } + } + } + + PROCESS_SWITCH(Alice3StrangenessTofPid, processV0s, "", true); + PROCESS_SWITCH(Alice3StrangenessTofPid, processCascades, "", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/ALICE3/TableProducer/alice3TrackingTranslator.cxx b/ALICE3/TableProducer/alice3TrackingTranslator.cxx index c68cb2adbe3..e0e742fc9d0 100644 --- a/ALICE3/TableProducer/alice3TrackingTranslator.cxx +++ b/ALICE3/TableProducer/alice3TrackingTranslator.cxx @@ -28,11 +28,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -40,19 +42,30 @@ #include #include +#include #include -#include #include #include #include #include +#include #include #include +#include #include TString inputPath; +using namespace o2::framework; + +namespace +{ +using Key = std::array; // barcode (vp, vs, particle, gen, sub) +Key keyOf(std::uint32_t vp, std::uint32_t vs, std::uint32_t pa, + std::uint32_t ge, std::uint32_t sp) { return Key{vp, vs, pa, ge, sp}; } +} // namespace + struct Alice3TrackingTranslator { o2::framework::Produces tableCollisions; o2::framework::Produces tableMcCollisionLabels; @@ -77,8 +90,10 @@ struct Alice3TrackingTranslator { o2::framework::Produces tableOTFLUTConfigId; o2::framework::Configurable maxCollisions{"maxCollisions", -1000, "Maximum number of collisions translated"}; - o2::framework::Configurable addDaughterInfo{"addDaughterInfo", false, "Add daughter particle information to the MC truth output tables"}; + o2::framework::Configurable useTrueInfoForRecoTracks{"useTrueInfoForRecoTracks", false, "Use true information for reconstructed tracks"}; + // o2::framework::Configurable addDaughterInfo{"addDaughterInfo", false, "Add daughter particle information to the MC truth output tables"}; + o2::framework::HistogramRegistry histos{"histos", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; void init(o2::framework::InitContext&) { // Initialization if needed @@ -91,7 +106,7 @@ struct Alice3TrackingTranslator { } struct FileStruct { - FileStruct(std::string filename, std::string treename) : mFile(filename.c_str(), "READ") + FileStruct(const std::string& filename, const std::string& treename) : mFile(filename.c_str(), "READ") { if (mFile.IsZombie()) { LOG(fatal) << "Could not open file '" << filename << "'"; @@ -115,7 +130,7 @@ struct Alice3TrackingTranslator { }; struct ParticleStruct : public FileStruct { - ParticleStruct(std::string filename, std::string treename) : FileStruct(filename, treename) + ParticleStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) { // mTree->Print(); SETADDRESS("particle_type", m_particle_type); @@ -130,10 +145,11 @@ struct Alice3TrackingTranslator { SETADDRESS("p", m_p); SETADDRESS("q", m_q); SETADDRESS("number_of_hits", m_number_of_hits); - SETADDRESS("particle_id", m_particleId); - if (mTree->GetBranchStatus("mother_particle_id")) { - SETADDRESS("mother_particle_id", m_motherId); - } + SETADDRESS("vertex_primary", m_vertex_primary); + SETADDRESS("vertex_secondary", m_vertex_secondary); + SETADDRESS("generation", m_generation); + SETADDRESS("sub_particle", m_sub_particle); + SETADDRESS("particle", m_particle); } std::vector* m_particle_type = nullptr; std::vector* m_vx = nullptr; @@ -148,11 +164,50 @@ struct Alice3TrackingTranslator { std::vector* m_number_of_hits = nullptr; std::vector* m_particleId = nullptr; std::vector* m_q = nullptr; - std::vector* m_motherId = nullptr; + std::vector* m_vertex_primary = nullptr; + std::vector* m_vertex_secondary = nullptr; + std::vector* m_generation = nullptr; + std::vector* m_sub_particle = nullptr; + std::vector* m_particle = nullptr; + // std::vector* m_motherId = nullptr; + }; + + struct VertexStruct : public FileStruct { + VertexStruct(const std::string& filename, const std::string& treename) : FileStruct(filename, treename) + { + SETADDRESS("vx", m_x); + SETADDRESS("vy", m_y); + SETADDRESS("vz", m_z); + SETADDRESS("vt", m_t); + SETADDRESS("incoming_particles_vertex_primary", m_incoming_particles_vertex_primary); + SETADDRESS("incoming_particles_vertex_secondary", m_incoming_particles_vertex_secondary); + SETADDRESS("incoming_particles_generation", m_incoming_particles_generation); + SETADDRESS("incoming_particles_sub_particle", m_incoming_particles_sub_particle); + SETADDRESS("incoming_particles_particle", m_incoming_particles_particle); + SETADDRESS("outgoing_particles_vertex_primary", m_outgoing_particles_vertex_primary); + SETADDRESS("outgoing_particles_vertex_secondary", m_outgoing_particles_vertex_secondary); + SETADDRESS("outgoing_particles_generation", m_outgoing_particles_generation); + SETADDRESS("outgoing_particles_sub_particle", m_outgoing_particles_sub_particle); + SETADDRESS("outgoing_particles_particle", m_outgoing_particles_particle); + } + std::vector* m_x = nullptr; + std::vector* m_y = nullptr; + std::vector* m_z = nullptr; + std::vector* m_t = nullptr; + std::vector>* m_incoming_particles_vertex_primary = nullptr; + std::vector>* m_incoming_particles_vertex_secondary = nullptr; + std::vector>* m_incoming_particles_generation = nullptr; + std::vector>* m_incoming_particles_sub_particle = nullptr; + std::vector>* m_incoming_particles_particle = nullptr; + std::vector>* m_outgoing_particles_vertex_primary = nullptr; + std::vector>* m_outgoing_particles_vertex_secondary = nullptr; + std::vector>* m_outgoing_particles_generation = nullptr; + std::vector>* m_outgoing_particles_sub_particle = nullptr; + std::vector>* m_outgoing_particles_particle = nullptr; }; struct TrackStruct : public FileStruct { - TrackStruct(std::string filename, std::string treename) : FileStruct(filename, treename) + TrackStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) { mTree->Print(); // Set branch addresses for ACTS track parameters @@ -163,28 +218,47 @@ struct Alice3TrackingTranslator { SETADDRESS("nHoles", m_nHoles); SETADDRESS("chi2Sum", m_chi2Sum); SETADDRESS("NDF", m_NDF); - SETADDRESS("eLOC0_fit", m_eLOC0_fit); - SETADDRESS("eLOC1_fit", m_eLOC1_fit); - SETADDRESS("ePHI_fit", m_ePHI_fit); - SETADDRESS("eTHETA_fit", m_eTHETA_fit); - SETADDRESS("eQOP_fit", m_eQOP_fit); - SETADDRESS("eT_fit", m_eT_fit); SETADDRESS("nMajorityHits", m_nMajorityHits); - SETADDRESS("majorityParticleId", m_majorityParticleId); - // mTree->SetBranchAddress("majorityParticleId", &m_majorityParticleId); SETADDRESS("t_charge", m_t_charge); SETADDRESS("t_vx", m_t_vx); SETADDRESS("t_vy", m_t_vy); SETADDRESS("t_vz", m_t_vz); - SETADDRESS("t_time", m_t_time); SETADDRESS("t_px", m_t_px); SETADDRESS("t_py", m_t_py); SETADDRESS("t_pz", m_t_pz); - SETADDRESS("t_theta", m_t_theta); - SETADDRESS("t_phi", m_t_phi); - SETADDRESS("t_pT", m_t_pT); - SETADDRESS("t_eta", m_t_eta); - SETADDRESS("majorityParticlePDG", m_majorityParticlePDG); + SETADDRESS("majorityParticleId_vertex_primary", m_majorityParticleId_vertex_primary); + SETADDRESS("majorityParticleId_vertex_secondary", m_majorityParticleId_vertex_secondary); + SETADDRESS("majorityParticleId_generation", m_majorityParticleId_generation); + SETADDRESS("majorityParticleId_sub_particle", m_majorityParticleId_sub_particle); + SETADDRESS("majorityParticleId_particle", m_majorityParticleId_particle); + SETADDRESS("eQOP_fit", m_eQOP_fit); + SETADDRESS("eX_fit", m_eX_fit); + SETADDRESS("eY_fit", m_eY_fit); + SETADDRESS("eZ_fit", m_eZ_fit); + SETADDRESS("ePX_fit", m_ePX_fit); + SETADDRESS("ePY_fit", m_ePY_fit); + SETADDRESS("ePZ_fit", m_ePZ_fit); + SETADDRESS("cov_eX_eX", m_cov_eX_eX); + // SETADDRESS("cov_eY_eY", m_cov_eY_eY); + SETADDRESS("cov_eZ_eZ", m_cov_eZ_eZ); + SETADDRESS("cov_ePX_ePX", m_cov_ePX_ePX); + SETADDRESS("cov_ePY_ePY", m_cov_ePY_ePY); + SETADDRESS("cov_ePZ_ePZ", m_cov_ePZ_ePZ); + SETADDRESS("cov_eX_ePX", m_cov_eX_ePX); + SETADDRESS("cov_eY_ePY", m_cov_eY_ePY); + SETADDRESS("cov_eZ_ePZ", m_cov_eZ_ePZ); + SETADDRESS("cov_eX_eY", m_cov_eX_eY); + SETADDRESS("cov_eX_eZ", m_cov_eX_eZ); + SETADDRESS("cov_eY_eZ", m_cov_eY_eZ); + SETADDRESS("cov_ePX_ePY", m_cov_ePX_ePY); + SETADDRESS("cov_ePX_ePZ", m_cov_ePX_ePZ); + SETADDRESS("cov_ePY_ePZ", m_cov_ePY_ePZ); + SETADDRESS("cov_eX_ePY", m_cov_eX_ePY); + SETADDRESS("cov_eX_ePZ", m_cov_eX_ePZ); + SETADDRESS("cov_eY_ePX", m_cov_eY_ePX); + SETADDRESS("cov_eY_ePZ", m_cov_eY_ePZ); + SETADDRESS("cov_eZ_ePX", m_cov_eZ_ePX); + SETADDRESS("cov_eZ_ePY", m_cov_eZ_ePY); } // Define track-related members here UInt_t* m_event_nr = nullptr; @@ -194,44 +268,68 @@ struct Alice3TrackingTranslator { std::vector* m_chi2Sum = nullptr; std::vector* m_NDF = nullptr; // Fitted track parameters - std::vector* m_eLOC0_fit = nullptr; // local position 0 (typically y in local frame) - std::vector* m_eLOC1_fit = nullptr; // local position 1 (typically z in local frame) - std::vector* m_ePHI_fit = nullptr; // azimuthal angle - std::vector* m_eTHETA_fit = nullptr; // polar angle - std::vector* m_eQOP_fit = nullptr; // q/m_p (charge over momentum) - std::vector* m_eT_fit = nullptr; // time + std::vector* m_eQOP_fit = nullptr; // q/m_p (charge over momentum) + + std::vector* m_eX_fit = nullptr; // global position x + std::vector* m_eY_fit = nullptr; // global position y + std::vector* m_eZ_fit = nullptr; // global position z + std::vector* m_ePX_fit = nullptr; // global momentum px + std::vector* m_ePY_fit = nullptr; // global momentum py + std::vector* m_ePZ_fit = nullptr; // global momentum pz + + // covariance matrices for fitted track parameters (if available) + std::vector* m_cov_eX_eX = nullptr; // covariance of global position x + // std::vector* m_cov_eY_eY = nullptr; // covariance of global position y + std::vector* m_cov_eZ_eZ = nullptr; // covariance of global position z + std::vector* m_cov_ePX_ePX = nullptr; // covariance of global momentum px + std::vector* m_cov_ePY_ePY = nullptr; // covariance of global momentum py + std::vector* m_cov_ePZ_ePZ = nullptr; // covariance of global momentum pz + std::vector* m_cov_eX_ePX = nullptr; // covariance between global position x and momentum px + std::vector* m_cov_eY_ePY = nullptr; // covariance between global position y and momentum py + std::vector* m_cov_eZ_ePZ = nullptr; // covariance between global position z and momentum pz + std::vector* m_cov_eX_eY = nullptr; // covariance between global position x and y + std::vector* m_cov_eX_eZ = nullptr; // covariance between global position x and z + std::vector* m_cov_eY_eZ = nullptr; // covariance between global position y and z + std::vector* m_cov_ePX_ePY = nullptr; // covariance between global momentum px and py + std::vector* m_cov_ePX_ePZ = nullptr; // covariance between global momentum px and pz + std::vector* m_cov_ePY_ePZ = nullptr; // covariance between global momentum py and pz + std::vector* m_cov_eX_ePY = nullptr; // covariance between global position x and momentum py + std::vector* m_cov_eX_ePZ = nullptr; // covariance between global position x and momentum pz + std::vector* m_cov_eY_ePX = nullptr; // covariance between global position y and momentum px + std::vector* m_cov_eY_ePZ = nullptr; // covariance between global position y and momentum pz + std::vector* m_cov_eZ_ePX = nullptr; // covariance between global position z and momentum px + std::vector* m_cov_eZ_ePY = nullptr; // covariance between global position z and momentum py // The majority truth particle info - std::vector* m_nMajorityHits = nullptr; /// The number of hits from majority particle - std::vector* m_majorityParticleId = nullptr; /// The particle Id of the majority particle - std::vector* m_t_charge = nullptr; /// Charge of majority particle - std::vector* m_t_time = nullptr; /// Time of majority particle - std::vector* m_t_vx = nullptr; /// Vertex x positions of majority particle - std::vector* m_t_vy = nullptr; /// Vertex y positions of majority particle - std::vector* m_t_vz = nullptr; /// Vertex z positions of majority particle - std::vector* m_t_px = nullptr; /// Initial momenta m_px of majority particle - std::vector* m_t_py = nullptr; /// Initial momenta m_py of majority particle - std::vector* m_t_pz = nullptr; /// Initial momenta m_pz of majority particle - std::vector* m_t_theta = nullptr; /// Initial momenta theta of majority particle - std::vector* m_t_phi = nullptr; /// Initial momenta phi of majority particle - std::vector* m_t_pT = nullptr; /// Initial momenta pT of majority particle - std::vector* m_t_eta = nullptr; /// Initial momenta eta of majority particle - - std::vector* m_majorityParticlePDG = nullptr; // IA + std::vector* m_nMajorityHits = nullptr; /// The number of hits from majority particle + std::vector* m_t_charge = nullptr; /// Charge of majority particle + std::vector* m_t_time = nullptr; /// Time of majority particle + std::vector* m_t_vx = nullptr; /// Vertex x positions of majority particle + std::vector* m_t_vy = nullptr; /// Vertex y positions of majority particle + std::vector* m_t_vz = nullptr; /// Vertex z positions of majority particle + std::vector* m_t_px = nullptr; /// Initial momenta m_px of majority particle + std::vector* m_t_py = nullptr; /// Initial momenta m_py of majority particle + std::vector* m_t_pz = nullptr; /// Initial momenta m_pz of majority particle + + std::vector* m_majorityParticleId_vertex_primary = nullptr; + std::vector* m_majorityParticleId_vertex_secondary = nullptr; + std::vector* m_majorityParticleId_generation = nullptr; + std::vector* m_majorityParticleId_sub_particle = nullptr; + std::vector* m_majorityParticleId_particle = nullptr; }; struct HitsStruct : public FileStruct { - HitsStruct(std::string filename, std::string treename) : FileStruct(filename, treename) + HitsStruct(const std::string& filename, const std::string& treename) : FileStruct(std::move(filename), std::move(treename)) { mTree->Print(); SETADDRESS("barcode", barcode); } std::vector* barcode = nullptr; }; - void addMCParticle(int collIndex, ParticleStruct& fileParticles, int iParticle, uint8_t flags, int firstMother, int firstDaughter, int numberOfHits) + void addMCParticle(int collIndex, ParticleStruct& fileParticles, int iParticle, uint8_t flags, int firstMother, int firstDaughter, int secondDaughter, int numberOfHits) { - int mothers[2] = {firstMother, -1}; - int daughters[2] = {firstDaughter, -1}; + int mothers[2] = {firstMother, firstMother}; + int daughters[2] = {firstDaughter, secondDaughter}; tableStoredMcParticles(collIndex, // mcCollisionId fileParticles.m_particle_type->at(iParticle), // pdgCode 0, // statusCode @@ -262,7 +360,7 @@ struct Alice3TrackingTranslator { if (filesList) { TIter next(filesList); TSystemFile* file; - while ((file = static_cast(next()))) { + while ((file = dynamic_cast(next()))) { TString fname = file->GetName(); LOG(debug) << "Found file: " << fname.Data(); if (!file->IsDirectory() && fname.EndsWith(".root")) { @@ -286,32 +384,21 @@ struct Alice3TrackingTranslator { files[justFilename.Data()] = filename; } LOG(info) << "All files loaded successfully"; - // Now open the files to translate and read the trees - ParticleStruct fileParticles(files["particles.root"], "particles"); - LOG(info) << "Particles loaded successfully"; ParticleStruct fileParticlesSim(files["particles_simulation.root"], "particles"); LOG(info) << "Particles Sim loaded successfully"; - std::string daughterFileName = addDaughterInfo ? "particles_decay.root" : "particles_simulation.root"; - ParticleStruct fileDaughterParticles(files[daughterFileName], "particles"); - LOG(info) << "Daughter particles loaded successfully from file " << daughterFileName; // FileStruct fileVertices(files["performance_vertexing.root"], "vertexing"); - TrackStruct fileTracksummary(files["tracksummary_ambi.root"], "tracksummary"); - // HitsStruct fileHits(files["hits.root"], "hits"); + TrackStruct fileTracksummary(files["tracksummary_ambi-tracks-merged.root"], "tracksummary"); + VertexStruct fileVertices(files["vertices_gen_and_geant.root"], "vertices"); LOG(info) << "Tracks loaded successfully"; - const Long64_t kEvents = fileParticles.getEntries(); - int indexOfLastParticleAfterEvent = -1; + const Long64_t kEvents = fileParticlesSim.getEntries(); for (Long64_t iEvent = 0; iEvent < kEvents; ++iEvent) { if (iEvent > 0 && maxCollisions.value > 0 && (iEvent % maxCollisions) == 0) { LOG(info) << "Stopping at event " << iEvent << "/" << kEvents; break; } - fileParticles.setEventEntry(iEvent); - // fileVertices.setEventEntry(iEvent); + fileVertices.setEventEntry(iEvent); fileTracksummary.setEventEntry(iEvent); - // fileHits.setEventEntry(iEvent); - if (addDaughterInfo) - fileDaughterParticles.setEventEntry(iEvent); fileParticlesSim.setEventEntry(iEvent); LOG(info) << "Processing event " << iEvent << "/" << kEvents; @@ -329,6 +416,106 @@ struct Alice3TrackingTranslator { // If it has entries, lastIndex() returns the index of the last element, so we use lastIndex() + 1. int collisionId = tableCollisions.lastIndex() + 1; + // Convert tracks from ACTS to ALICE format + const size_t nParticlesSim = fileParticlesSim.m_vx->size(); + const size_t nTracks = fileTracksummary.m_ePX_fit->size(); + // std::vector idMCparticles; // fixme: unused + + // local index k within this event -> global AO2D index + int firstIdxThisEvent = tableStoredMcParticles.lastIndex() + 1; + + // barcode -> global AO2D index (used later for track labels) + std::map barcodeToGlobalIdx; + std::map> barcodeToVertexPosition; + std::map GlobalIdxToMotherIdx; + std::map GlobalIdxToPDGCode; + std::map GlobalToLocalIdx; + std::map> GlobalIdxToDaughterIdxs; + for (size_t iPart = 0; iPart < nParticlesSim; ++iPart) { + Key key = keyOf( + fileParticlesSim.m_vertex_primary->at(iPart), + fileParticlesSim.m_vertex_secondary->at(iPart), + fileParticlesSim.m_particle->at(iPart), + fileParticlesSim.m_generation->at(iPart), + fileParticlesSim.m_sub_particle->at(iPart)); + barcodeToGlobalIdx[key] = firstIdxThisEvent + static_cast(iPart); + + if (iPart == 0) { + tableMcCollisions(0, // mccollision::BCId, + 0, // mccollision::GeneratorsID, + fileParticlesSim.m_vx->at(iPart), // mccollision::PosX, + fileParticlesSim.m_vy->at(iPart), // mccollision::PosY, + fileParticlesSim.m_vz->at(iPart), // mccollision::PosZ + fileParticlesSim.m_vt->at(iPart), // mccollision::T + 1.0f, // mccollision::Weight + 0.0f, // mccollision::ImpactParameter, + 0.f); // mccollision::EventPlaneAngle, + } + } + + for (size_t j = 0; j < fileVertices.m_incoming_particles_vertex_secondary->size(); ++j) { + for (size_t d = 0; d < (*fileVertices.m_incoming_particles_vertex_secondary)[j].size(); ++d) { + Key kd = keyOf( + (*fileVertices.m_incoming_particles_vertex_primary)[j][d], + (*fileVertices.m_incoming_particles_vertex_secondary)[j][d], + (*fileVertices.m_incoming_particles_particle)[j][d], + (*fileVertices.m_incoming_particles_generation)[j][d], + (*fileVertices.m_incoming_particles_sub_particle)[j][d]); + + auto iteratorMother = barcodeToGlobalIdx.find(kd); + if (iteratorMother != barcodeToGlobalIdx.end()) { + int motherIdx = iteratorMother->second; + for (size_t d2 = 0; d2 < (*fileVertices.m_outgoing_particles_vertex_secondary)[j].size(); ++d2) { + Key kd2 = keyOf( + (*fileVertices.m_outgoing_particles_vertex_primary)[j][d2], + (*fileVertices.m_outgoing_particles_vertex_secondary)[j][d2], + (*fileVertices.m_outgoing_particles_particle)[j][d2], + (*fileVertices.m_outgoing_particles_generation)[j][d2], + (*fileVertices.m_outgoing_particles_sub_particle)[j][d2]); + barcodeToVertexPosition[kd2].push_back(fileVertices.m_x->at(j)); + barcodeToVertexPosition[kd2].push_back(fileVertices.m_y->at(j)); + barcodeToVertexPosition[kd2].push_back(fileVertices.m_z->at(j)); + auto iteratorDaughter = barcodeToGlobalIdx.find(kd2); + if (iteratorDaughter != barcodeToGlobalIdx.end()) { + int daughterIdx = iteratorDaughter->second; + GlobalIdxToMotherIdx[daughterIdx] = motherIdx; + GlobalIdxToDaughterIdxs[motherIdx].push_back(daughterIdx); + } + } + } + } + } + for (size_t iPart = 0; iPart < nParticlesSim; ++iPart) { + int globalIdx = firstIdxThisEvent + static_cast(iPart); + int motherIdx = -1; + if (GlobalIdxToMotherIdx.find(globalIdx) != GlobalIdxToMotherIdx.end()) { + motherIdx = GlobalIdxToMotherIdx[globalIdx]; + } + int firstDaughter = -1; + int secondDaughter = -1; + if (GlobalIdxToDaughterIdxs.find(globalIdx) != GlobalIdxToDaughterIdxs.end()) { + const auto& daughters = GlobalIdxToDaughterIdxs[globalIdx]; + if (daughters.size() > 0) { + firstDaughter = daughters[0]; + } + if (daughters.size() > 1) { + secondDaughter = daughters[1]; + } + } + // Determine flags for the MC particle + // TODO: For now primary are only those without mother, but all based on PhysicalPrimary definition should be considered. + uint8_t flags = 0; + if (motherIdx == -1) { + collisionX = fileVertices.m_x->at(iPart); + collisionY = fileVertices.m_y->at(iPart); + collisionZ = fileVertices.m_z->at(iPart); + flags |= o2::aod::mcparticle::enums::PhysicalPrimary; + } + GlobalIdxToPDGCode[globalIdx] = fileParticlesSim.m_particle_type->at(iPart); + GlobalToLocalIdx[globalIdx] = iPart; + addMCParticle(tableMcCollisions.lastIndex(), fileParticlesSim, iPart, flags, motherIdx, firstDaughter, secondDaughter, fileParticlesSim.m_number_of_hits->at(iPart)); + } + tableCollisions(0, // bcId collisionX, // posX collisionY, // posY @@ -350,154 +537,79 @@ struct Alice3TrackingTranslator { tableCollisionsAlice3(0.f); // multDensity - struct addedParticle { - float px; - float py; - float pz; - float vx; - float vy; - float vz; - }; - // Convert tracks from ACTS to ALICE format - const size_t nParticlesGen = fileParticles.m_vx->size(); - const size_t nParticlesSim = fileParticlesSim.m_vx->size(); - const size_t nDaughterParticles = fileDaughterParticles.m_vx->size(); - const size_t nTracks = fileTracksummary.m_eLOC0_fit->size(); - std::vector idMCparticles; - + // Reconstructed information for (size_t iTrack = 0; iTrack < nTracks; ++iTrack) { - LOG(info) << "Processing track " << iTrack << "/" << nTracks << " (nParticlesSim=" << nParticlesSim << ") nParticlesGen=" << nParticlesGen; - const size_t iParticle = iTrack; - - if (iParticle == 0) { - tableMcCollisions(0, // mccollision::BCId, - 0, // mccollision::GeneratorsID, - fileParticles.m_vx->at(iParticle), // mccollision::PosX, - fileParticles.m_vy->at(iParticle), // mccollision::PosY, - fileParticles.m_vz->at(iParticle), // mccollision::PosZ - fileParticles.m_vt->at(iParticle), // mccollision::T - 1.0f, // mccollision::Weight - 0.0f, // mccollision::ImpactParameter, - 0.f); // mccollision::EventPlaneAngle, - } - uint8_t flags = 0; - - ULong64_t idMCTrueParticle = fileTracksummary.m_majorityParticleId->at(iParticle); - int32_t mcParticleId = -1; - int pdgCode = -1; - - for (size_t iMC = 0; iMC < nParticlesGen; ++iMC) { - if (fileParticles.m_particleId->at(iMC) == idMCTrueParticle) { - if (count(idMCparticles.begin(), idMCparticles.end(), fileParticles.m_particleId->at(iMC)) > 0) { - continue; - } - idMCparticles.push_back(fileParticles.m_particleId->at(iMC)); - flags |= o2::aod::mcparticle::enums::PhysicalPrimary; - int nHits = 0; - for (size_t iPartSim = 0; iPartSim < nParticlesSim; ++iPartSim) { - if (fileParticlesSim.m_particleId->at(iPartSim) == fileParticles.m_particleId->at(iMC)) { - nHits = fileParticlesSim.m_number_of_hits->at(iPartSim); - break; - } - } - addMCParticle(tableMcCollisions.lastIndex(), fileParticles, iMC, flags, -1, -1, nHits); - mcParticleId = tableStoredMcParticles.lastIndex(); - pdgCode = fileParticles.m_particle_type->at(iMC); - break; - } + std::cout << "Processing track " << iTrack << "/" << nTracks << std::endl; + + Key key = keyOf( + fileTracksummary.m_majorityParticleId_vertex_primary->at(iTrack), + fileTracksummary.m_majorityParticleId_vertex_secondary->at(iTrack), + fileTracksummary.m_majorityParticleId_particle->at(iTrack), + fileTracksummary.m_majorityParticleId_generation->at(iTrack), + fileTracksummary.m_majorityParticleId_sub_particle->at(iTrack)); + int mcParticleIdx = -1; + auto iterator = barcodeToGlobalIdx.find(key); + if (iterator != barcodeToGlobalIdx.end()) { + mcParticleIdx = iterator->second; } - if (addDaughterInfo) { - for (size_t iMC = 0; iMC < nDaughterParticles; ++iMC) { - if (fileDaughterParticles.m_particleId->at(iMC) == idMCTrueParticle) { - if (count(idMCparticles.begin(), idMCparticles.end(), fileDaughterParticles.m_particleId->at(iMC)) > 0) { - break; - } - - int nHits = 0; - for (size_t iPartSim = 0; iPartSim < nParticlesSim; ++iPartSim) { - if (fileParticlesSim.m_particleId->at(iPartSim) == fileDaughterParticles.m_particleId->at(iMC)) { - nHits = fileParticlesSim.m_number_of_hits->at(iPartSim); - break; - } - } - for (size_t iMother = 0; iMother < nParticlesGen; ++iMother) { - if (fileDaughterParticles.m_motherId->at(iMC) == fileParticles.m_particleId->at(iMother)) { - if (count(idMCparticles.begin(), idMCparticles.end(), fileParticles.m_particleId->at(iMother)) > 0) { - break; - } - idMCparticles.push_back(fileParticles.m_particleId->at(iMother)); - uint8_t flagsMother = o2::aod::mcparticle::enums::PhysicalPrimary; - addMCParticle(tableMcCollisions.lastIndex(), fileParticles, iMother, flagsMother, -1, tableStoredMcParticles.lastIndex() + 2, 0); - break; - } - } - int motherId = -1; - if (count(idMCparticles.begin(), idMCparticles.end(), fileDaughterParticles.m_motherId->at(iMC)) > 0) { - auto it = find(idMCparticles.begin(), idMCparticles.end(), fileDaughterParticles.m_motherId->at(iMC)); - motherId = it - idMCparticles.begin() + indexOfLastParticleAfterEvent + 1; - } - idMCparticles.push_back(fileDaughterParticles.m_particleId->at(iMC)); - addMCParticle(tableMcCollisions.lastIndex(), fileDaughterParticles, iMC, flags, motherId, -1, nHits); - mcParticleId = tableStoredMcParticles.lastIndex(); - pdgCode = fileDaughterParticles.m_particle_type->at(iMC); - break; - } + // Extract ACTS track parameters + float qOverP = fileTracksummary.m_eQOP_fit->at(iTrack); + + float x = fileTracksummary.m_eX_fit->at(iTrack); + float y = fileTracksummary.m_eY_fit->at(iTrack); + float z = fileTracksummary.m_eZ_fit->at(iTrack); + float px = fileTracksummary.m_ePX_fit->at(iTrack); + float py = fileTracksummary.m_ePY_fit->at(iTrack); + float pz = fileTracksummary.m_ePZ_fit->at(iTrack); + int localIdx = GlobalToLocalIdx[mcParticleIdx]; + if (useTrueInfoForRecoTracks) { + if (mcParticleIdx != -1) { + px = fileParticlesSim.m_px->at(localIdx); + py = fileParticlesSim.m_py->at(localIdx); + pz = fileParticlesSim.m_pz->at(localIdx); + x = fileParticlesSim.m_vx->at(localIdx); + y = fileParticlesSim.m_vy->at(localIdx); + z = fileParticlesSim.m_vz->at(localIdx); } } - // Extract ACTS track parameters - const float phi = fileTracksummary.m_ePHI_fit->at(iTrack); - const float theta = fileTracksummary.m_eTHETA_fit->at(iTrack); - const float qOverP = fileTracksummary.m_eQOP_fit->at(iTrack); - const float loc0 = fileTracksummary.m_eLOC0_fit->at(iTrack); - const float loc1 = fileTracksummary.m_eLOC1_fit->at(iTrack); - // Convert to ALICE track parameters - // ALICE uses: alpha, x, y, z, snp, tgl, signed1Pt - float alpha = phi; // Track angle in global frame - float x = loc0; // Local x position - float y = loc1; // Local y position - float z = 0.0f; // Will be set from DCA or collision vertex - - // Calculate snp (sin of track momentum azimuthal angle) - float snp = std::sin(phi); - - // Calculate tgl (tangent of track momentum dip angle) - float tgl = 1.0f / std::tan(theta); - - // Calculate signed1Pt (charge/pt) - const float m_p = (qOverP != 0) ? std::abs(1.0f / qOverP) : 0.0f; - const float pt = m_p * std::sin(theta); int8_t charge = (qOverP > 0) ? 1 : -1; - const float signed1Pt = (pt != 0) ? charge / pt : 0.0f; + if (qOverP == 0) { + charge = 0; + } // Track quality float m_chi2Sum = fileTracksummary.m_chi2Sum->at(iTrack); uint32_t m_nMeasurements = fileTracksummary.m_nMeasurements->at(iTrack); uint32_t m_NDF = fileTracksummary.m_NDF->at(iTrack); - // Fill covariance matrices (simplified - should be extracted from ACTS if available) - float cYY = 0.1f; - float cZY = 0.0f; - float cZZ = 0.1f; - float cSnpY = 0.0f; - float cSnpZ = 0.0f; - float cSnpSnp = 0.001f; - float cTglY = 0.0f; - float cTglZ = 0.0f; - float cTglSnp = 0.0f; - float cTglTgl = 0.001f; - float c1PtY = 0.0f; - float c1PtZ = 0.0f; - float c1PtSnp = 0.0f; - float c1PtTgl = 0.0f; - float c1Pt21Pt2 = 0.001f * signed1Pt * signed1Pt; - - // Create TrackParCov object with dummy covariance matrix - std::array trackParams = {y, z, snp, tgl, signed1Pt}; - std::array trackCov = {cYY, cZY, cZZ, cSnpY, cSnpZ, cSnpSnp, - cTglY, cTglZ, cTglSnp, cTglTgl, - c1PtY, c1PtZ, c1PtSnp, c1PtTgl, c1Pt21Pt2}; - o2::track::TrackParCov trackParCov(x, alpha, trackParams, trackCov, charge); + // Fill covariance matrices + float cxx = fileTracksummary.m_cov_eX_eX->at(iTrack); + float cyy = cxx; // fileTracksummary.cov_eY_eY->at(iTrack); + float czz = fileTracksummary.m_cov_eZ_eZ->at(iTrack); + float cxy = fileTracksummary.m_cov_eX_eY->at(iTrack); + float cxz = fileTracksummary.m_cov_eX_eZ->at(iTrack); + float cyz = fileTracksummary.m_cov_eY_eZ->at(iTrack); + float cpxpx = fileTracksummary.m_cov_ePX_ePX->at(iTrack); + float cpypy = fileTracksummary.m_cov_ePY_ePY->at(iTrack); + float cpzpz = fileTracksummary.m_cov_ePZ_ePZ->at(iTrack); + float cpxpy = fileTracksummary.m_cov_ePX_ePY->at(iTrack); + float cpxpz = fileTracksummary.m_cov_ePX_ePZ->at(iTrack); + float cpypz = fileTracksummary.m_cov_ePY_ePZ->at(iTrack); + float cxpx = fileTracksummary.m_cov_eX_ePX->at(iTrack); + float cxpy = fileTracksummary.m_cov_eX_ePY->at(iTrack); + float cxpz = fileTracksummary.m_cov_eX_ePZ->at(iTrack); + float cypx = fileTracksummary.m_cov_eY_ePX->at(iTrack); + float cypy = fileTracksummary.m_cov_eY_ePY->at(iTrack); + float cypz = fileTracksummary.m_cov_eY_ePZ->at(iTrack); + float czpx = fileTracksummary.m_cov_eZ_ePX->at(iTrack); + float czpy = fileTracksummary.m_cov_eZ_ePY->at(iTrack); + float czpz = fileTracksummary.m_cov_eZ_ePZ->at(iTrack); + // Create TrackParCov object with covariance matrix + std::array position = {x, y, z}; + std::array momentum = {px, py, pz}; + std::array trackCov = {cxx, cxy, cyy, cxz, cyz, czz, cxpx, cypx, czpx, cpxpx, cxpy, cypy, czpy, cpxpy, cpypy, cxpz, cypz, czpz, cpxpz, cpypz, cpzpz}; + o2::track::TrackParCov trackParCov(position, momentum, trackCov, charge); // Fill StoredTracks table (basic track parameters) tableStoredTracks(collisionId, // collisionId @@ -509,13 +621,11 @@ struct Alice3TrackingTranslator { trackParCov.getSnp(), // snp trackParCov.getTgl(), // tgl trackParCov.getQ2Pt()); // signed1Pt - // Fill TracksExtension table tableTracksExtension(trackParCov.getPt(), trackParCov.getP(), trackParCov.getEta(), trackParCov.getPhi()); - tableStoredTracksCov(std::sqrt(trackParCov.getSigmaY2()), // SigmaY std::sqrt(trackParCov.getSigmaZ2()), // SigmaZ std::sqrt(trackParCov.getSigmaSnp2()), // SigmaSnp @@ -531,7 +641,6 @@ struct Alice3TrackingTranslator { 0, // Rho1PtZ 0, // Rho1PtSnp 0); // Rho1PtTgl - // covariance matrix at collision vertex tableTracksCovExtension(trackParCov.getSigmaY2(), // sigmaY2 trackParCov.getSigmaZY(), // sigmaZY @@ -547,43 +656,28 @@ struct Alice3TrackingTranslator { trackParCov.getSigma1PtZ(), // sigma1PtZ trackParCov.getSigma1PtSnp(), // sigma1PtSnp trackParCov.getSigma1PtTgl(), // sigma1PtTgl - trackParCov.getSigma1Pt2()); // sigma1Pt2 - - // Fill MC track labels - // Get particle linkage from hits using the majority hit index - // if (fileTracksummary.nMajorityHits && iTrack < fileTracksummary.nMajorityHits->size()) { - // unsigned int hitIndex = fileTracksummary.nMajorityHits->at(iTrack); - // if (fileHits.barcode && hitIndex < fileHits.barcode->size()) { - // mcParticleId = static_cast(fileHits.barcode->at(hitIndex)); - // LOG(debug) << "Track " << iTrack << " linked to MC particle " << mcParticleId - // << " via hit index " << hitIndex; - // } else { - // LOG(warning) << "Hit index " << hitIndex << " out of range for track " << iTrack - // << " (barcode vector size: " << (fileHits.barcode ? fileHits.barcode->size() : 0) << ")"; - // } - // } else { - // LOG(warning) << "No majority hit information available for track " << iTrack; - // } - // for ( const auto &vv : fileTracksummary.majorityParticleId->at(iTrack) ){ - // LOG(info) << vv; - // } - tableMcTrackLabels(mcParticleId, // McParticleId - 0); // mcMask - - // Fill DCA info (simplified - should be calculated properly) - tableTracksDCA(0.0f, // dcaXY - 0.0f); // dcaZ - + trackParCov.getSigma1Pt2()); // sigma1Pt + // Fill MC label + tableMcTrackLabels(mcParticleIdx, // McParticleId + 0); // mcMask + // Fill DCA info TODO: should be calculated properly + tableTracksDCA(0.0f, // dcaXY + 0.0f); // dcaZ tableTracksDCACov(0.0f, // sigmaDcaXY2 0.0f); // sigmaDcaZ2 - // Fill ALICE3 specific tables - tableTracksAlice3(true); // isReconstructed - tableTracksAlice3Pdg(pdgCode); // PdgCode to the linked MC truth particle + tableTracksAlice3(true, // isReconstructed + -1.f, // todo: track innermost update + -1.f); // todo: track outermost update + if (mcParticleIdx > 0) + tableTracksAlice3Pdg(GlobalIdxToPDGCode[mcParticleIdx]); // PdgCode to the linked MC truth particle + else + tableTracksAlice3Pdg(0); // No linked MC truth particle tableTracksExtraA3(m_nMeasurements, // nSiliconHits (using m_nMeasurements as proxy) - 0); // nTPCHits - + 0, // nTPCHits + 0, // trackType + false); // isPVContributor // Fill extra track info tableStoredTracksExtra(0.f, // TPCInnerParam static_cast(0), // Flags @@ -606,16 +700,14 @@ struct Alice3TrackingTranslator { 0.f, // TrackPhiEMCAL 0.f, // TrackTime 0.f); // TrackTimeRes - // Fill track selection - tableTrackSelection(false, // IsGlobalTrackSDD, - false, // TrackCutFlag, - false, // TrackCutFlagFb1, - false, // TrackCutFlagFb2, - false, // TrackCutFlagFb3, - false, // TrackCutFlagFb4, - false); // TrackCutFlagFb5, - + tableTrackSelection(false, // IsGlobalTrackSDD, + false, // TrackCutFlag, + false, // TrackCutFlagFb1, + false, // TrackCutFlagFb2, + false, // TrackCutFlagFb3, + false, // TrackCutFlagFb4, + false); // TrackCutFlagFb5, tableTrackSelectionExtension(false, // PassedTrackType, false, // PassedPtRange, false, // PassedEtaRange, @@ -634,66 +726,6 @@ struct Alice3TrackingTranslator { false, // PassedITSHitsFB1, false); // PassedITSHitsFB2 } - - for (size_t iParticle = 0; iParticle < nParticlesGen; ++iParticle) { - if (iParticle == 0 && nTracks == 0) { - tableMcCollisions(0, // mccollision::BCId, - 0, // mccollision::GeneratorsID, - fileParticles.m_vx->at(iParticle), // mccollision::PosX, - fileParticles.m_vy->at(iParticle), // mccollision::PosY, - fileParticles.m_vz->at(iParticle), // mccollision::PosZ - fileParticles.m_vt->at(iParticle), // mccollision::T - 1.0f, // mccollision::Weight - 0.0f, // mccollision::ImpactParameter, - 0.f); // mccollision::EventPlaneAngle, - } - if (idMCparticles.end() != std::find(idMCparticles.begin(), idMCparticles.end(), fileParticles.m_particleId->at(iParticle))) { - // Already added via track - continue; - } - uint8_t flags = 0; - flags |= o2::aod::mcparticle::enums::PhysicalPrimary; - - int nHits = 0; - for (size_t iPartSim = 0; iPartSim < nParticlesSim; ++iPartSim) { - if (fileParticlesSim.m_particleId->at(iPartSim) == fileParticles.m_particleId->at(iParticle)) { - nHits = fileParticlesSim.m_number_of_hits->at(iPartSim); - break; - } - } - addMCParticle(tableMcCollisions.lastIndex(), fileParticles, iParticle, flags, -1, -1, nHits); - idMCparticles.push_back(fileParticles.m_particleId->at(iParticle)); - } - if (addDaughterInfo) { - for (size_t iParticle = 0; iParticle < nDaughterParticles; ++iParticle) { - if (idMCparticles.end() != std::find(idMCparticles.begin(), idMCparticles.end(), fileDaughterParticles.m_particleId->at(iParticle))) { - // Already added via track - continue; - } - uint8_t flags = 0; - int nHits = 0; - for (size_t iPartSim = 0; iPartSim < nParticlesSim; ++iPartSim) { - if (fileParticlesSim.m_particleId->at(iPartSim) == fileDaughterParticles.m_particleId->at(iParticle)) { - nHits = fileParticlesSim.m_number_of_hits->at(iPartSim); - break; - } - } - int motherId = -1; - for (size_t iMother = 0; iMother < nParticlesGen; ++iMother) { - if (fileDaughterParticles.m_motherId->at(iParticle) == fileParticles.m_particleId->at(iMother)) { - if (count(idMCparticles.begin(), idMCparticles.end(), fileDaughterParticles.m_motherId->at(iParticle)) > 0) { - auto it = find(idMCparticles.begin(), idMCparticles.end(), fileDaughterParticles.m_motherId->at(iParticle)); - motherId = it - idMCparticles.begin() + indexOfLastParticleAfterEvent + 1; - } - } - } - addMCParticle(tableMcCollisions.lastIndex(), fileDaughterParticles, iParticle, flags, motherId, -1, nHits); - } - } - - LOG(info) << "Event " << iEvent << ": has " << nTracks << " tracks, " << nParticlesGen << " particles " << nDaughterParticles << " daughter particles, " << nParticlesSim << " propagated particles."; - LOG(info) << "Total numbers of stored MC particles: " << tableStoredMcParticles.lastIndex() + 1; - indexOfLastParticleAfterEvent = tableStoredMcParticles.lastIndex(); } } }; diff --git a/ALICE3/TableProducer/alice3strangenessFinder.cxx b/ALICE3/TableProducer/alice3strangenessFinder.cxx index e96db585c25..8d8216c4c5d 100644 --- a/ALICE3/TableProducer/alice3strangenessFinder.cxx +++ b/ALICE3/TableProducer/alice3strangenessFinder.cxx @@ -22,7 +22,6 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" #include "ALICE3/Core/TrackUtilities.h" -#include "ALICE3/DataModel/OTFMCParticle.h" #include "ALICE3/DataModel/OTFPIDTrk.h" #include "ALICE3/DataModel/OTFRICH.h" #include "ALICE3/DataModel/OTFStrangeness.h" @@ -57,16 +56,16 @@ #include #include #include +#include #include using namespace o2; -// using namespace o2::analysis; using namespace o2::framework; using namespace o2::constants::physics; using Alice3TracksWPid = soa::Join; using Alice3TracksACTS = soa::Join; -using Alice3TracksOTF = soa::Join; +using Alice3TracksOTF = soa::Join; using Alice3MCParticles = soa::Join; struct Alice3strangenessFinder { @@ -74,33 +73,25 @@ struct Alice3strangenessFinder { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Produces v0CandidateIndices; // contains V0 candidate indices - Produces v0CandidateCores; // contains V0 candidate core information - Produces tableCascadeCores; - Produces tableCascadeIndices; + Produces v0CandidateIndices; // contains V0 candidate indices + Produces v0CandidateCores; // contains V0 candidate core information + Produces tableStoredCascCores; // contains stored cascade core information + Produces tableCascIndices; // contains cascade indices + Produces tableCascCovs; // contains cascade cov matrix + Produces tableA3CascadeMcLabels; // contains cascade core MC labels Configurable buildCascade{"buildCascade", false, "build cascade candidates"}; - Configurable nSigmaTOF{"nSigmaTOF", 5.0f, "Nsigma for TOF PID (if enabled)"}; - Configurable dcaXYconstant{"dcaXYconstant", -1.0f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable dcaXYpTdep{"dcaXYpTdep", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; ConfigurableAxis axisK0Mass{"axisK0Mass", {200, 0.4f, 0.6f}, "K0 mass axis"}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, "Lambda mass axis"}; ConfigurableAxis axisXiMass{"axisXiMass", {200, 1.22f, 1.42f}, "Xi mass axis"}; - ConfigurableAxis axisMassOmega{"axisMassOmega", {200, 1.57f, 1.77f}, "Omega mass axis"}; + ConfigurableAxis axisOmegaMass{"axisOmegaMass", {200, 1.57f, 1.77f}, "Omega mass axis"}; + ConfigurableAxis axisDCA{"axisDCA", {200, 0, 200}, "DCA axis"}; ConfigurableAxis axisEta{"axisEta", {80, -4.f, 4.f}, "Eta axis"}; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.025f, 0.05f, 0.075f, 0.1f, 0.125f, 0.15f, 0.175f, 0.2f, 0.225f, 0.25f, 0.275f, 0.3f, 0.325f, 0.35f, 0.375f, 0.4f, 0.425f, 0.45f, 0.475f, 0.5f, 0.525f, 0.55f, 0.575f, 0.6f, 0.625f, 0.65f, 0.675f, 0.7f, 0.725f, 0.75f, 0.775f, 0.8f, 0.82f, 0.85f, 0.875f, 0.9f, 0.925f, 0.95f, 0.975f, 1.0f, 1.05f, 1.1f}, "pt axis for QA histograms"}; - Configurable bachMinConstDCAxy{"bachMinConstDCAxy", -1.0f, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable bachMinPtDepDCAxy{"bachMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; - Configurable bachMinConstDCAz{"bachMinConstDCAz", -1.0f, "[0] in |DCAz| > [0]+[1]/pT"}; - Configurable bachMinPtDepDCAz{"bachMinPtDepDCAz", 0.0, "[1] in |DCAz| > [0]+[1]/pT"}; - - Configurable v0MaxDauDCA{"v0MaxDauDCA", 0.005f, "DCA between v0 daughters (cm)"}; - Configurable cascMaxDauDCA{"cascMaxDauDCA", 0.005f, "DCA between cascade daughters (cm)"}; - // DCA Fitter struct : ConfigurableGroup { std::string prefix = "cfgFitter"; @@ -123,7 +114,24 @@ struct Alice3strangenessFinder { Configurable maxIter{"maxIter", 30, "maximum number of iterations for vertex fitter"}; } cfgFitter; - Configurable acceptedLambdaMassWindow{"acceptedLambdaMassWindow", 0.2f, "accepted Lambda mass window around PDG mass"}; + // Pre-selections + struct : ConfigurableGroup { + std::string prefix = "presel"; + Configurable acceptedLambdaMassWindow{"acceptedLambdaMassWindow", 0.2f, "accepted Lambda mass window around PDG mass"}; + + Configurable posMinConstDCAxy{"posMinConstDCAxy", -1.0f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable posMinPtDepDCAxy{"posMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + + Configurable negMinConstDCAxy{"negMinConstDCAxy", -1.0f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable negMinPtDepDCAxy{"negMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + + Configurable bachMinConstDCAxy{"bachMinConstDCAxy", -1.0f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable bachMinPtDepDCAxy{"bachMinPtDepDCAxy", 0.0, "[1] in |DCAxy| > [0]+[1]/pT"}; + + Configurable v0MaxDauDCA{"v0MaxDauDCA", 0.005f, "DCA between v0 daughters (cm)"}; + Configurable cascMaxDauDCA{"cascMaxDauDCA", 0.005f, "DCA between cascade daughters (cm)"}; + Configurable cascMaxEta{"cascMaxEta", 2.5f, "DCA between cascade daughters (cm)"}; + } presel; // Operation Configurable magneticField{"magneticField", 20.0f, "Magnetic field (in kilogauss)"}; @@ -136,24 +144,23 @@ struct Alice3strangenessFinder { Configurable useOriginalTrackParams{"useOriginalTrackParams", false, "use original track parameters instead of the ones propagated to PCA (effective only if skipFitter is false) and for MC truth info"}; o2::vertexing::DCAFitterN<2> fitter; - o2::vertexing::DCAFitterN<3> fitter3; - - Service pdgDB; + Service pdgDB{}; + static constexpr float ToMicrons = 1e+4; // partitions for v0/casc dau tracks Partition positiveSecondaryTracksACTS = - aod::track::signed1Pt > 0.0f && nabs(aod::track::dcaXY) > dcaXYconstant + dcaXYpTdep* nabs(aod::track::signed1Pt); + aod::track::signed1Pt > 0.0f && nabs(aod::track::dcaXY) > presel.posMinConstDCAxy + presel.posMinPtDepDCAxy* nabs(aod::track::signed1Pt); Partition negativeSecondaryTracksACTS = - aod::track::signed1Pt < 0.0f && nabs(aod::track::dcaXY) > dcaXYconstant + dcaXYpTdep* nabs(aod::track::signed1Pt); + aod::track::signed1Pt < 0.0f && nabs(aod::track::dcaXY) > presel.negMinConstDCAxy + presel.negMinPtDepDCAxy* nabs(aod::track::signed1Pt); Partition bachelorTracksACTS = - nabs(aod::track::dcaXY) > bachMinConstDCAxy + bachMinPtDepDCAxy* nabs(aod::track::signed1Pt) && nabs(aod::track::dcaZ) > bachMinConstDCAz + bachMinPtDepDCAz* nabs(aod::track::signed1Pt); + nabs(aod::track::dcaXY) > presel.bachMinConstDCAxy + presel.bachMinPtDepDCAxy* nabs(aod::track::signed1Pt); Partition positiveSecondaryTracksOTF = - aod::track::signed1Pt > 0.0f && nabs(aod::track::dcaXY) > dcaXYconstant + dcaXYpTdep* nabs(aod::track::signed1Pt); + aod::track::signed1Pt > 0.0f && nabs(aod::track::dcaXY) > presel.posMinConstDCAxy + presel.posMinPtDepDCAxy* nabs(aod::track::signed1Pt); Partition negativeSecondaryTracksOTF = - aod::track::signed1Pt < 0.0f && nabs(aod::track::dcaXY) > dcaXYconstant + dcaXYpTdep* nabs(aod::track::signed1Pt); + aod::track::signed1Pt < 0.0f && nabs(aod::track::dcaXY) > presel.negMinConstDCAxy + presel.negMinPtDepDCAxy* nabs(aod::track::signed1Pt); Partition bachelorTracksOTF = - nabs(aod::track::dcaXY) > bachMinConstDCAxy + bachMinPtDepDCAxy* nabs(aod::track::signed1Pt) && nabs(aod::track::dcaZ) > bachMinConstDCAz + bachMinPtDepDCAz* nabs(aod::track::signed1Pt); + nabs(aod::track::dcaXY) > presel.bachMinConstDCAxy + presel.bachMinPtDepDCAxy* nabs(aod::track::signed1Pt); Partition positiveMCParticles = aod::mcparticle_alice3::charge > 0.0f; Partition negativeMCParticles = aod::mcparticle_alice3::charge < 0.0f; @@ -172,6 +179,8 @@ struct Alice3strangenessFinder { // Partition secondaryAntiProtons = nabs(aod::upgrade_tof::nSigmaProtonInnerTOF) < nSigmaTOF && nabs(aod::upgrade_tof::nSigmaProtonOuterTOF) < nSigmaTOF && aod::track::signed1Pt < 0.0f && nabs(aod::track::dcaXY) > dcaXYconstant + dcaXYpTdep* nabs(aod::track::signed1Pt); struct Candidate { + int index{-1}; + // decay properties float dcaDau{}; float eta{}; @@ -202,7 +211,12 @@ struct Alice3strangenessFinder { fitter.setMaxIter(cfgFitter.maxIter); fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); - histos.add("hFitterQA", "", kTH1D, {{10, 0, 10}}); // For QA reasons, counting found candidates at different stages + auto hFitterQA = histos.add("hFitterQA", "hFitterQA", kTH1D, {{10, 0, 10}}); + hFitterQA->GetXaxis()->SetBinLabel(1, "All"); // all fitter attempts + hFitterQA->GetXaxis()->SetBinLabel(2, "Processed"); // attempts that made the fitter.process() call + hFitterQA->GetXaxis()->SetBinLabel(3, "Found cands"); // nCand == 0 + hFitterQA->GetXaxis()->SetBinLabel(4, "Done"); // checks isPropagateTracksToVertexDone() + auto hFitterStatusCode = histos.add("hFitterStatusCode", "hFitterStatusCode", kTH1D, {{15, -0.5, 14.5}}); hFitterStatusCode->GetXaxis()->SetBinLabel(1, "None"); // no status set (should not be possible!) @@ -229,6 +243,7 @@ struct Alice3strangenessFinder { histos.add("hPtPosDauAfterV0Finding", "", kTH2D, {axisPt, axisPt}); histos.add("hPtNegDauAfterV0Finding", "", kTH2D, {axisPt, axisPt}); histos.add("hEventCounter", "", kTH1D, {{1, 0, 2}}); // counting processed events + auto hV0Counter = histos.add("hV0Counter", "hV0Counter", kTH1D, {{4, 0, 4}}); hV0Counter->GetXaxis()->SetBinLabel(1, "K0S"); hV0Counter->GetXaxis()->SetBinLabel(2, "Lambda"); @@ -246,18 +261,36 @@ struct Alice3strangenessFinder { auto hCascadeBuilding = histos.add("hCascadeBuilding", "hCascadeBuilding", kTH1D, {{10, 0.5, 10.5}}); hCascadeBuilding->GetXaxis()->SetBinLabel(1, "Attempts"); hCascadeBuilding->GetXaxis()->SetBinLabel(2, "La mass window"); - hCascadeBuilding->GetXaxis()->SetBinLabel(3, "DCA Fitter"); + hCascadeBuilding->GetXaxis()->SetBinLabel(3, "Mc check"); + hCascadeBuilding->GetXaxis()->SetBinLabel(4, "DCA Fitter"); if (doprocessGenerated) { - histos.add("hGeneratedK0s", "hGeneratedK0s", kTH2D, {{axisPt}, {axisEta}}); - histos.add("hGeneratedLambda", "hGeneratedLambda", kTH2D, {{axisPt}, {axisEta}}); - histos.add("hGeneratedAntiLambda", "hGeneratedAntiLambda", kTH2D, {{axisPt}, {axisEta}}); - histos.add("hGeneratedXi", "hGeneratedXi", kTH2D, {{axisPt}, {axisEta}}); - histos.add("hGeneratedAntiXi", "hGeneratedAntiXi", kTH2D, {{axisPt}, {axisEta}}); - histos.add("hGeneratedOmega", "hGeneratedOmega", kTH2D, {{axisPt}, {axisEta}}); - histos.add("hGeneratedAntiOmega", "hGeneratedAntiOmega", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Generated/hGeneratedK0s", "hGeneratedK0s", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Generated/hGeneratedLambda", "hGeneratedLambda", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Generated/hGeneratedAntiLambda", "hGeneratedAntiLambda", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Generated/hGeneratedXi", "hGeneratedXi", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Generated/hGeneratedAntiXi", "hGeneratedAntiXi", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Generated/hGeneratedOmega", "hGeneratedOmega", kTH2D, {{axisPt}, {axisEta}}); + histos.add("Generated/hGeneratedAntiOmega", "hGeneratedAntiOmega", kTH2D, {{axisPt}, {axisEta}}); } + if (buildCascade) { + histos.add("CascadeBuilding/hDcaBetweenDaus", "hDcaBetweenDaus", kTH1D, {{axisDCA}}); + histos.add("CascadeBuilding/hXiMass", "", kTH1D, {axisXiMass}); + histos.add("CascadeBuilding/hOmegaMass", "", kTH1D, {axisOmegaMass}); + } + histos.add("hPx", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPy", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPz", "", kTH1D, {{200, -10.f, 10.f}}); + + histos.add("hPxPairs", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPyPairs", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPzPairs", "", kTH1D, {{200, -10.f, 10.f}}); + + histos.add("hPxV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPyV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); + histos.add("hPzV0CandDaughter", "", kTH1D, {{200, -10.f, 10.f}}); + histos.print(); } @@ -270,34 +303,45 @@ struct Alice3strangenessFinder { template bool checkSameMother(TTrackType const& track1, TTrackType const& track2) { - // MC label points to McPartWithDaus - if constexpr (requires { track1.has_mcPartWithDau(); }) { - if (!track1.has_mcPartWithDau() || !track2.has_mcPartWithDau()) { - return false; - } - auto mcParticle1 = track1.template mcPartWithDau_as(); - auto mcParticle2 = track2.template mcPartWithDau_as(); - if (mcParticle1.mothersIds().empty() || mcParticle2.mothersIds().empty()) { - return false; + bool returnValue = false; + if (track1.has_mcParticle() && track2.has_mcParticle()) { + auto mcParticle1 = track1.template mcParticle_as(); + auto mcParticle2 = track2.template mcParticle_as(); + if (mcParticle1.has_mothers() && mcParticle2.has_mothers()) { + for (const auto& m1 : mcParticle1.template mothers_as()) { + for (const auto& m2 : mcParticle2.template mothers_as()) { + if (m1.globalIndex() == m2.globalIndex()) { + returnValue = true; + } + } + } } - return mcParticle1.mothersIds()[0] == mcParticle2.mothersIds()[0]; - } else { // MC label points directly to aod::McParticles - bool returnValue = false; - if (track1.has_mcParticle() && track2.has_mcParticle()) { - auto mcParticle1 = track1.template mcParticle_as(); - auto mcParticle2 = track2.template mcParticle_as(); - if (mcParticle1.has_mothers() && mcParticle2.has_mothers()) { - for (const auto& m1 : mcParticle1.template mothers_as()) { - for (const auto& m2 : mcParticle2.template mothers_as()) { - if (m1.globalIndex() == m2.globalIndex()) { - returnValue = true; + } + return returnValue; + } + + template + bool checkSameMotherExtra(TTrackType const& track1, TTrackType const& track2) + { + bool returnValue = false; + if (track1.has_mcParticle() && track2.has_mcParticle()) { + auto mcParticle1 = track1.template mcParticle_as(); + auto mcParticle2 = track2.template mcParticle_as(); + if (mcParticle1.has_mothers() && mcParticle2.has_mothers()) { + for (const auto& mcParticleMother1 : mcParticle1.template mothers_as()) { + if (mcParticleMother1.has_mothers()) { + for (const auto& mcParticleGrandMother1 : mcParticleMother1.template mothers_as()) { + for (const auto& mcParticleMother2 : mcParticle2.template mothers_as()) { + if (mcParticleGrandMother1.globalIndex() == mcParticleMother2.globalIndex()) { + returnValue = true; + } } } } } } - return returnValue; - } + } // end association check + return returnValue; } template @@ -308,7 +352,6 @@ struct Alice3strangenessFinder { histos.fill(HIST("hPtPosDau"), t0.getPt()); if (!skipFitter) { - histos.fill(HIST("hFitterQA"), 0.5); //}-{}-{}-{}-{}-{}-{}-{}-{}-{} // Move close to minima @@ -341,8 +384,8 @@ struct Alice3strangenessFinder { t0.getPxPyPzGlo(thisCandidate.pDau0); t1.getPxPyPzGlo(thisCandidate.pDau1); } - histos.fill(HIST("hPtNegDauAfterV0Finding"), std::sqrt(thisCandidate.pDau1[0] * thisCandidate.pDau1[0] + thisCandidate.pDau1[1] + thisCandidate.pDau1[1]), t1.getPt()); - histos.fill(HIST("hPtPosDauAfterV0Finding"), std::sqrt(thisCandidate.pDau0[0] * thisCandidate.pDau0[0] + thisCandidate.pDau0[1] + thisCandidate.pDau0[1]), t0.getPt()); + histos.fill(HIST("hPtNegDauAfterV0Finding"), std::hypot(thisCandidate.pDau1[0], thisCandidate.pDau1[1]), t1.getPt()); + histos.fill(HIST("hPtPosDauAfterV0Finding"), std::hypot(thisCandidate.pDau0[0], thisCandidate.pDau0[1]), t0.getPt()); thisCandidate.dcaDau = std::sqrt(fitter.getChi2AtPCACandidate()); thisCandidate.p[0] = thisCandidate.pDau0[0] + thisCandidate.pDau1[0]; @@ -384,8 +427,6 @@ struct Alice3strangenessFinder { thisCandidate.dcaToPV = calculateDCAStraightToPV(thisCandidate.posSV[0], thisCandidate.posSV[1], thisCandidate.posSV[2], thisCandidate.p[0], thisCandidate.p[1], thisCandidate.p[2], vtx[0], vtx[1], vtx[2]); - - return true; } else { t0.getPxPyPzGlo(thisCandidate.pDau0); t1.getPxPyPzGlo(thisCandidate.pDau1); @@ -402,37 +443,37 @@ struct Alice3strangenessFinder { thisCandidate.dcaToPV = calculateDCAStraightToPV(thisCandidate.posSV[0], thisCandidate.posSV[1], thisCandidate.posSV[2], thisCandidate.p[0], thisCandidate.p[1], thisCandidate.p[2], vtx[0], vtx[1], vtx[2]); - return true; } + return true; } void processGenerated(aod::McParticles const&) { for (const auto& mcParticle : trueK0s) { - histos.fill(HIST("hGeneratedK0s"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Generated/hGeneratedK0s"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueLambda) { - histos.fill(HIST("hGeneratedLambda"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Generated/hGeneratedLambda"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiLambda) { - histos.fill(HIST("hGeneratedAntiLambda"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Generated/hGeneratedAntiLambda"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueXi) { - histos.fill(HIST("hGeneratedXi"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Generated/hGeneratedXi"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiXi) { - histos.fill(HIST("hGeneratedAntiXi"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Generated/hGeneratedAntiXi"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueOmega) { - histos.fill(HIST("hGeneratedOmega"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Generated/hGeneratedOmega"), mcParticle.pt(), mcParticle.eta()); } for (const auto& mcParticle : trueAntiOmega) { - histos.fill(HIST("hGeneratedAntiOmega"), mcParticle.pt(), mcParticle.eta()); + histos.fill(HIST("Generated/hGeneratedAntiOmega"), mcParticle.pt(), mcParticle.eta()); } } template - void processFindV0CandidateNoPid(TCollision collision, TTracksGrouped negTracksGrouped, TTracksGrouped posTracksGrouped, TTracksGrouped bachTracksGrouped) + void processFindV0CandidateNoPid(const TCollision& collision, const TTracksGrouped& negTracksGrouped, const TTracksGrouped& posTracksGrouped, const TTracksGrouped& bachTracksGrouped) { const std::array vtx = {collision.posX(), collision.posY(), collision.posZ()}; histos.fill(HIST("hEventCounter"), 1.0); @@ -443,6 +484,13 @@ struct Alice3strangenessFinder { } o2::track::TrackParCov pos = getTrackParCov(posTrack); + // if constexpr (requires { posTrack.pdgCode(); }) { + // if (posTrack.pdgCode() == kPiPlus) { + histos.fill(HIST("hPx"), posTrack.px()); + histos.fill(HIST("hPy"), posTrack.py()); + histos.fill(HIST("hPz"), posTrack.pz()); + // } + // } for (auto const& negTrack : negTracksGrouped) { if (!negTrack.isReconstructed()) { @@ -454,35 +502,35 @@ struct Alice3strangenessFinder { continue; // keep only if same mother } - // ACTS: pdg code attached to track - if constexpr (requires { posTrack.pdgCode(); }) { - if ((posTrack.pdgCode() != kPiPlus && negTrack.pdgCode() != kPiMinus) && isK0Gun) { - continue; - } - if ((posTrack.pdgCode() != kProton && negTrack.pdgCode() != kPiMinus) && isLambdaGun) { - continue; - } - } - - // OTF: pdg code from mcParticle table - if constexpr (requires { posTrack.has_mcPartWithDau(); }) { - if (!posTrack.has_mcPartWithDau() && !negTrack.has_mcPartWithDau()) { - continue; - } - auto mcParticlePos = posTrack.template mcPartWithDau_as(); - auto mcParticleNeg = negTrack.template mcPartWithDau_as(); - if ((mcParticlePos.pdgCode() != kPiPlus && mcParticleNeg.pdgCode() != kPiMinus) && isK0Gun) { - continue; - } - if ((mcParticlePos.pdgCode() != kProton && mcParticleNeg.pdgCode() != kPiMinus) && isLambdaGun) { - continue; + if (isK0Gun || isLambdaGun) { + // ACTS: pdg code attached to track + if constexpr (requires { posTrack.pdgCode(); }) { + if ((posTrack.pdgCode() != kPiPlus && negTrack.pdgCode() != kPiMinus) && isK0Gun) { + continue; + } + if ((posTrack.pdgCode() != kProton && negTrack.pdgCode() != kPiMinus) && isLambdaGun) { + continue; + } + } else { + // OTF: pdg code from mcParticle table + if (!posTrack.has_mcParticle() && !negTrack.has_mcParticle()) { + continue; + } + auto mcParticlePos = posTrack.template mcParticle_as(); + auto mcParticleNeg = negTrack.template mcParticle_as(); + if ((mcParticlePos.pdgCode() != kPiPlus && mcParticleNeg.pdgCode() != kPiMinus) && isK0Gun) { + continue; + } + if ((mcParticlePos.pdgCode() != kProton && mcParticleNeg.pdgCode() != kPiMinus) && isLambdaGun) { + continue; + } } } + Candidate v0Cand; histos.fill(HIST("hV0Building"), 2.0); o2::track::TrackParCov neg = getTrackParCov(negTrack); - Candidate v0cand; - if (!buildDecayCandidateTwoBody(pos, neg, vtx, v0cand)) { + if (!buildDecayCandidateTwoBody(pos, neg, vtx, v0Cand)) { continue; // failed at building candidate } @@ -499,33 +547,39 @@ struct Alice3strangenessFinder { // } else { // histos.fill(HIST("hV0Counter"), 3.5); // } + histos.fill(HIST("hPxPairs"), posTrack.px()); + histos.fill(HIST("hPyPairs"), posTrack.py()); + histos.fill(HIST("hPzPairs"), posTrack.pz()); + + histos.fill(HIST("hPxV0CandDaughter"), v0Cand.pDau0[0]); + histos.fill(HIST("hPyV0CandDaughter"), v0Cand.pDau0[1]); + histos.fill(HIST("hPzV0CandDaughter"), v0Cand.pDau0[2]); v0CandidateIndices(collision.globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(), -1); - v0CandidateCores(v0cand.posSV[0], v0cand.posSV[1], v0cand.posSV[2], - v0cand.pDau0[0], v0cand.pDau0[1], v0cand.pDau0[2], - v0cand.pDau1[0], v0cand.pDau1[1], v0cand.pDau1[2], - v0cand.dcaDau, posTrack.dcaXY(), negTrack.dcaXY(), - v0cand.cosPA, v0cand.dcaToPV); + v0CandidateCores(v0Cand.posSV[0], v0Cand.posSV[1], v0Cand.posSV[2], + v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2], + v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2], + v0Cand.dcaDau, posTrack.dcaXY(), negTrack.dcaXY(), + v0Cand.cosPA, v0Cand.dcaToPV); - o2::track::TrackParCov v0(v0cand.posSV, v0cand.p, v0cand.parentTrackCovMatrix, 0); - const float lambdaMassHypothesis = RecoDecay::m(std::array{std::array{v0cand.pDau0[0], v0cand.pDau0[1], v0cand.pDau0[2]}, - std::array{v0cand.pDau1[0], v0cand.pDau1[1], v0cand.pDau1[2]}}, + const float lambdaMassHypothesis = RecoDecay::m(std::array{std::array{v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2]}, + std::array{v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2]}}, std::array{o2::constants::physics::MassProton, o2::constants::physics::MassPionCharged}); - const float antiLambdaMassHypothesis = RecoDecay::m(std::array{std::array{v0cand.pDau0[0], v0cand.pDau0[1], v0cand.pDau0[2]}, - std::array{v0cand.pDau1[0], v0cand.pDau1[1], v0cand.pDau1[2]}}, + const float antiLambdaMassHypothesis = RecoDecay::m(std::array{std::array{v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2]}, + std::array{v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2]}}, std::array{o2::constants::physics::MassPionCharged, o2::constants::physics::MassProton}); if (!buildCascade) { continue; // not building cascades, so skip the rest } - const bool inLambdaMassWindow = std::abs(lambdaMassHypothesis - o2::constants::physics::MassLambda0) < acceptedLambdaMassWindow; - const bool inAntiLambdaMassWindow = std::abs(antiLambdaMassHypothesis - o2::constants::physics::MassLambda0) < acceptedLambdaMassWindow; + const bool inLambdaMassWindow = std::abs(lambdaMassHypothesis - o2::constants::physics::MassLambda0) < presel.acceptedLambdaMassWindow; + const bool inAntiLambdaMassWindow = std::abs(antiLambdaMassHypothesis - o2::constants::physics::MassLambda0) < presel.acceptedLambdaMassWindow; if (!inLambdaMassWindow && !inAntiLambdaMassWindow) { continue; // Likely not a lambda, should not be considered for cascade building } @@ -536,24 +590,61 @@ struct Alice3strangenessFinder { } histos.fill(HIST("hCascadeBuilding"), 1.0); - if (inLambdaMassWindow && bachTrack.sign() > 0) { + if (!inLambdaMassWindow && bachTrack.sign() < 0) { continue; // only consider lambda and neg bach track } - if (inAntiLambdaMassWindow && bachTrack.sign() < 0) { + if (!inAntiLambdaMassWindow && bachTrack.sign() > 0) { continue; // only consider anti-lambda and pos bach track } histos.fill(HIST("hCascadeBuilding"), 2.0); + Candidate cascCand; + if (mcSameMotherCheck) { + if ((!checkSameMotherExtra(posTrack, bachTrack) || !checkSameMotherExtra(negTrack, bachTrack))) { + continue; + } - // TODO mc same mother check + if (bachTrack.has_mcParticle()) { + auto bachParticle = bachTrack.template mcParticle_as(); + cascCand.index = bachParticle.mothersIds().front(); + } + } - Candidate cascCand; + histos.fill(HIST("hCascadeBuilding"), 3.0); o2::track::TrackParCov bach = getTrackParCov(bachTrack); + o2::track::TrackParCov v0(v0Cand.posSV, v0Cand.p, v0Cand.parentTrackCovMatrix, 0); if (!buildDecayCandidateTwoBody(v0, bach, vtx, cascCand)) { continue; // failed at building candidate } - histos.fill(HIST("hCascadeBuilding"), 3.0); + + // Apply preselections + if (cascCand.dcaDau > presel.cascMaxDauDCA) { + continue; // combined tracks should be reasonanly close + } + + if (std::abs(cascCand.eta) > presel.cascMaxEta) { + continue; // candidate outside of acceptance + } + + const float radiusV0 = std::hypot(v0Cand.posSV[0], v0Cand.posSV[1]); + const float radiusCasc = std::hypot(cascCand.posSV[0], cascCand.posSV[1]); + // tracks without update information (IU < 0) are not checked + if (posTrack.trackIU() > 0.f && posTrack.trackIU() < radiusV0) { + continue; // positive track hit a layer before v0 decayed + } + + if (negTrack.trackIU() > 0.f && negTrack.trackIU() < radiusV0) { + continue; // negative track hit a layer before v0 decayed + } + + if (radiusV0 < radiusCasc) { + continue; // v0 decayed before cascade + } + + if (bachTrack.trackIU() > 0.f && bachTrack.trackIU() < radiusCasc) { + continue; // bachelor track hit a layer before v0 decayed + } const float massXi = RecoDecay::m(std::array{std::array{cascCand.pDau0[0], cascCand.pDau0[1], cascCand.pDau0[2]}, std::array{cascCand.pDau1[0], cascCand.pDau1[1], cascCand.pDau1[2]}}, @@ -575,22 +666,30 @@ struct Alice3strangenessFinder { bachTrack.px(), bachTrack.py(), bachTrack.pz(), vtx[0], vtx[1], vtx[2]); - tableCascadeIndices(0, // cascade index, dummy value - posTrack.globalIndex(), - negTrack.globalIndex(), - bachTrack.globalIndex(), - collision.globalIndex()); - - tableCascadeCores(bachTrack.sign(), massXi, massOm, - cascCand.posSV[0], cascCand.posSV[1], cascCand.posSV[2], - v0cand.posSV[0], v0cand.posSV[1], v0cand.posSV[2], - v0cand.pDau0[0], v0cand.pDau0[1], v0cand.pDau0[2], - v0cand.pDau1[0], v0cand.pDau1[1], v0cand.pDau1[2], - cascCand.pDau1[0], cascCand.pDau1[1], cascCand.pDau1[2], - cascCand.p[0], cascCand.p[1], cascCand.p[2], - v0cand.dcaDau, cascCand.dcaDau, - dcaPosToPV, dcaNegToPV, dcaBachToPV, - cascCand.dcaToPV, cascCand.dcaToPV); + histos.fill(HIST("hCascadeBuilding"), 4.0); + histos.fill(HIST("CascadeBuilding/hDcaBetweenDaus"), cascCand.dcaDau * ToMicrons); + histos.fill(HIST("CascadeBuilding/hXiMass"), massXi); + histos.fill(HIST("CascadeBuilding/hOmegaMass"), massOm); + + tableCascCovs(cascCand.parentTrackCovMatrix.data()); + tableA3CascadeMcLabels(cascCand.index); + tableCascIndices(0, // cascade index, dummy value + posTrack.globalIndex(), + negTrack.globalIndex(), + bachTrack.globalIndex(), + collision.globalIndex()); + + tableStoredCascCores(bachTrack.sign(), massXi, massOm, + cascCand.posSV[0], cascCand.posSV[1], cascCand.posSV[2], + v0Cand.posSV[0], v0Cand.posSV[1], v0Cand.posSV[2], + v0Cand.pDau0[0], v0Cand.pDau0[1], v0Cand.pDau0[2], + v0Cand.pDau1[0], v0Cand.pDau1[1], v0Cand.pDau1[2], + cascCand.pDau1[0], cascCand.pDau1[1], cascCand.pDau1[2], + cascCand.p[0], cascCand.p[1], cascCand.p[2], + v0Cand.dcaDau, cascCand.dcaDau, + dcaPosToPV, dcaNegToPV, dcaBachToPV, + cascCand.dcaToPV, cascCand.dcaToPV); // FIXME + } // end bachTrack } // end negTrack } // end posTrack @@ -625,10 +724,12 @@ struct Alice3strangenessFinder { isLambda = (posParticle.pdgCode() == kProton && negParticle.pdgCode() == kPiMinus); isAntiLambda = (posParticle.pdgCode() == kPiPlus && negParticle.pdgCode() == kProtonBar); if (isK0s || isLambda || isAntiLambda) { - if (!isK0s && isK0Gun) + if (!isK0s && isK0Gun) { continue; - if (!isLambda && isLambdaGun) + } + if (!isLambda && isLambdaGun) { continue; + } Candidate v0cand; std::vector v0DecayVertex; v0DecayVertex.push_back(negParticle.vx()); @@ -640,8 +741,9 @@ struct Alice3strangenessFinder { o2::track::TrackParCov negParCov; o2::upgrade::convertTLorentzVectorToO2Track(1, posLorVector, v0DecayVertex, posParCov); o2::upgrade::convertTLorentzVectorToO2Track(-1, negLorVector, v0DecayVertex, negParCov); - if (!buildDecayCandidateTwoBody(posParCov, negParCov, vtx, v0cand)) + if (!buildDecayCandidateTwoBody(posParCov, negParCov, vtx, v0cand)) { continue; + } v0CandidateIndices(collision.globalIndex(), posParticle.globalIndex(), negParticle.globalIndex(), @@ -682,7 +784,7 @@ struct Alice3strangenessFinder { processFindV0CandidateNoPid(collision, negTracksGrouped, posTracksGrouped, bachTracksGrouped); } - void processFindV0CandidateOTF(aod::Collision const& collision, Alice3TracksOTF const&, aod::McPartWithDaus const&) + void processFindV0CandidateOTF(aod::Collision const& collision, Alice3TracksOTF const&, aod::McParticles const&) { auto negTracksGrouped = negativeSecondaryTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); auto posTracksGrouped = positiveSecondaryTracksOTF->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); diff --git a/ALICE3/Tasks/CMakeLists.txt b/ALICE3/Tasks/CMakeLists.txt index 822d407221f..e86859ec374 100644 --- a/ALICE3/Tasks/CMakeLists.txt +++ b/ALICE3/Tasks/CMakeLists.txt @@ -24,21 +24,6 @@ o2physics_add_dpl_workflow(alice3-lutmaker PUBLIC_LINK_LIBRARIES O2::Framework FairRoot::Base O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(alice3-pid-rich-qa - SOURCES pidRICHqa.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(alice3-ecal-qa - SOURCES ECALqa.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(alice3-pid-ftof-qa - SOURCES pidFTOFqa.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(alice3-pid-separation-power SOURCES alice3SeparationPower.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -54,6 +39,16 @@ o2physics_add_dpl_workflow(alice3-dilepton PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(alice3-prefilterdilepton + SOURCES alice3-prefilterdilepton.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::FrameworkPhysicsSupport + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(alice3-prefilterdimuon + SOURCES alice3-prefilterdimuon.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::FrameworkPhysicsSupport + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(alice3-taskcorrelationddbar SOURCES alice3-taskcorrelationDDbar.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -90,7 +85,7 @@ o2physics_add_dpl_workflow(alice3strangeness COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(alice3-dq-efficiency - SOURCES alice3-dq-efficiency.cxx + SOURCES alice3DqEfficiency.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) diff --git a/ALICE3/Tasks/ECALqa.cxx b/ALICE3/Tasks/ECALqa.cxx deleted file mode 100644 index 73a73ca32a3..00000000000 --- a/ALICE3/Tasks/ECALqa.cxx +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file ECALqa.cxx -/// \author Nicolo' Jacazio -/// \since 14/09/2021 -/// \brief Task to use the ALICE3 ECAL table -/// - -#include "ALICE3/DataModel/ECAL.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -namespace o2::aod -{ - -namespace indices -{ -DECLARE_SOA_INDEX_COLUMN(Track, track); -DECLARE_SOA_INDEX_COLUMN(ECAL, ecal); - -DECLARE_SOA_INDEX_COLUMN(McParticle, mcparticle); - -} // namespace indices - -DECLARE_SOA_INDEX_TABLE_USER(ECALTracksIndex, Tracks, "ECALTRK", indices::TrackId, indices::ECALId); -DECLARE_SOA_INDEX_TABLE_USER(ECALMcPartIndex, McParticles, "ECALPART", indices::McParticleId, indices::ECALId); -} // namespace o2::aod - -struct ecalIndexBuilder { // Builder of the ECAL-track index linkage - Builds ind; - Builds indPart; - void init(o2::framework::InitContext&) - { - } -}; - -struct ecalQaMc { - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::QAObject}; - - void init(o2::framework::InitContext&) - { - histos.add("energy", ";Energy (GeV/#it{c});Entries", HistType::kTH1F, {{100, 0, 100}}); - histos.add("energyVsp", ";Energy (GeV/#it{c});#it{p} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("ecalpVsp", ";#it{p}_{ECAL} (GeV/#it{c});#it{p} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("ecalpxVspx", ";#it{p}_{ECAL x} (GeV/#it{c});#it{p} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("ecalpyVspy", ";#it{p}_{ECAL y} (GeV/#it{c});#it{p} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("ecalpzVspz", ";#it{p}_{ECAL z} (GeV/#it{c});#it{p} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("energyVsecalp", ";Energy (GeV/#it{c});#it{p}_{ECAL} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("energyVsecalpx", ";Energy (GeV/#it{c});#it{p}_{ECAL x} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("energyVsecalpy", ";Energy (GeV/#it{c});#it{p}_{ECAL y} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("energyVsecalpz", ";Energy (GeV/#it{c});#it{p}_{ECAL z} (GeV/#it{c});Entries", HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}); - histos.add("PDGs", "Particle PDGs;PDG Code", kTH1D, {{100, 0.f, 100.f}}); - } - - using Trks = soa::Join; - void process(const soa::Join& mcParticles, - const Trks&, - const aod::McTrackLabels&, - const aod::ECALs&, - const aod::Collisions&) - { - for (auto& particle : mcParticles) { - if (!particle.has_ecal()) - continue; - histos.fill(HIST("energy"), particle.ecal().e()); - const float ecalp = std::sqrt(particle.ecal().px() * particle.ecal().px() + particle.ecal().py() * particle.ecal().py() + particle.ecal().pz() * particle.ecal().pz()); - histos.fill(HIST("energyVsp"), particle.ecal().e(), particle.p()); - histos.fill(HIST("ecalpVsp"), ecalp, particle.p()); - histos.fill(HIST("ecalpxVspx"), particle.ecal().px(), particle.px()); - histos.fill(HIST("ecalpyVspy"), particle.ecal().py(), particle.py()); - histos.fill(HIST("ecalpzVspz"), particle.ecal().pz(), particle.pz()); - histos.fill(HIST("energyVsecalp"), particle.ecal().e(), ecalp); - histos.fill(HIST("energyVsecalpx"), particle.ecal().e(), particle.ecal().px()); - histos.fill(HIST("energyVsecalpy"), particle.ecal().e(), particle.ecal().py()); - histos.fill(HIST("energyVsecalpz"), particle.ecal().e(), particle.ecal().pz()); - histos.get(HIST("PDGs"))->Fill(Form("%i", particle.pdgCode()), 1.f); - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfg) -{ - auto workflow = WorkflowSpec{adaptAnalysisTask(cfg)}; - workflow.push_back(adaptAnalysisTask(cfg)); - return workflow; -} diff --git a/ALICE3/Tasks/alice3-cdeuteron.cxx b/ALICE3/Tasks/alice3-cdeuteron.cxx index 6dc574189f2..1c4ebe5b578 100644 --- a/ALICE3/Tasks/alice3-cdeuteron.cxx +++ b/ALICE3/Tasks/alice3-cdeuteron.cxx @@ -26,14 +26,13 @@ #include #include #include +#include #include #include #include #include -#include - #include #include diff --git a/ALICE3/Tasks/alice3-dilepton.cxx b/ALICE3/Tasks/alice3-dilepton.cxx index 099e6205262..e6fa5079cc3 100644 --- a/ALICE3/Tasks/alice3-dilepton.cxx +++ b/ALICE3/Tasks/alice3-dilepton.cxx @@ -17,7 +17,9 @@ #include "ALICE3/DataModel/OTFCollision.h" #include "ALICE3/DataModel/OTFRICH.h" #include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/prefilterDilepton.h" #include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -37,8 +39,8 @@ #include #include #include +#include -#include #include #include @@ -48,33 +50,25 @@ using namespace o2::soa; using namespace o2::framework; using namespace o2::framework::expressions; -struct Alice3Dilepton { - enum HFllType { +struct Alice3Lepton { + enum HFType { kUndef = -1, - kCe_Ce = 0, // ULS - kBe_Be = 1, // ULS - kBCe_BCe = 2, // ULS - kBCe_Be_SameB = 3, // ULS - kBCe_Be_DiffB = 4, // LS - }; - enum PairType { - kULS = 0, - kLSpp = 1, - kLSnn = 2, + kCe = 0, + kBe = 1, + kBCe = 2, + kPPi0 = 3, + kHFE = 4, }; - SliceCache cache_mc; - SliceCache cache_rec; - Service inspdg; Configurable pdg{"pdg", 11, "pdg code for analysis. dielectron:11, dimuon:13"}; - Configurable requireHFEid{"requireHFEid", true, "Require HFE identification for both leptons"}; - Configurable ptMin{"pt-min", 0.f, "Lower limit in pT"}; - Configurable ptMax{"pt-max", 5.f, "Upper limit in pT"}; - Configurable etaMin{"eta-min", -5.f, "Lower limit in eta"}; - Configurable etaMax{"eta-max", 5.f, "Upper limit in eta"}; - Configurable useGen{"use-gen", false, "Use generated (true) or smeared/reconstructed (false) values for fiducial cuts"}; + Configurable requireHFE{"requireHFE", -1, "-1: no selection, 0: charm, 1: direct beauty, 2: beauty->charm->e, 3: promptPi0, 4: HFE"}; + Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; + Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; + Configurable etaMin{"etaMin", -5.f, "Lower limit in eta"}; + Configurable etaMax{"etaMax", 5.f, "Upper limit in eta"}; + Configurable useGen{"useGen", false, "Use generated (true) or smeared/reconstructed (false) values for fiducial cuts"}; Configurable selectReconstructed{"selectReconstructed", true, "Select only reconstructed tracks (true) or ghosts (false)"}; Configurable nSigmaEleCutOuterTOF{"nSigmaEleCutOuterTOF", 3., "Electron inclusion in outer TOF"}; Configurable nSigmaEleCutInnerTOF{"nSigmaEleCutInnerTOF", 3., "Electron inclusion in inner TOF"}; @@ -83,6 +77,8 @@ struct Alice3Dilepton { Configurable nSigmaElectronRich{"nSigmaElectronRich", 3., "Electron inclusion RICH"}; Configurable nSigmaPionRich{"nSigmaPionRich", 3., "Pion exclusion RICH"}; Configurable otfConfig{"otfConfig", 0, "OTF configuration flag"}; + Configurable minFITPart{"minFITPart", 0.f, "Minimum number of charged particles in the FIT acceptance"}; + Configurable maxFITPart{"maxFITPart", 0.f, "Maximum number of charged particles in the FIT acceptance"}; HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -97,7 +93,8 @@ struct Alice3Dilepton { const AxisSpec axisSigmaEl{200, -10, 10, "n#sigma_{El}"}; const AxisSpec axisTrackLengthOuterTOF{300, 0., 300., "Track length (cm)"}; const AxisSpec axisEta{1000, -5, 5, "#it{#eta}"}; - const AxisSpec axisDCAxy{1000, 0, 20, "DCA_{xy,ll} (#sigma)"}; + const AxisSpec axisDCAxysigma{1000, -20, 20, "DCA_{xy} (#sigma)"}; + const AxisSpec axisDCAxy{1200, -300, 300, "DCA_{xy} (#micro m)"}; const AxisSpec axisPhi{360, 0, TMath::TwoPi(), "#it{#varphi} (rad.)"}; const AxisSpec axisProdx{2000, -100, 100, "Prod. Vertex X (cm)"}; const AxisSpec axisPrody{2000, -100, 100, "Prod. Vertex Y (cm)"}; @@ -107,6 +104,7 @@ struct Alice3Dilepton { registry.add("Generated/Event/VtxX", "Vertex X", kTH1F, {axisVx}); registry.add("Generated/Event/VtxY", "Vertex Y", kTH1F, {axisVy}); registry.add("Generated/Event/VtxZ", "Vertex Z", kTH1F, {axisVz}); + registry.add("Generated/Event/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); registry.add("Generated/Particle/Pt", "Particle Pt", kTH1F, {axisPt}); registry.add("Generated/Particle/Eta", "Particle Eta", kTH1F, {axisEta}); registry.add("Generated/Particle/Phi", "Particle Phi", kTH1F, {axisPhi}); @@ -114,18 +112,6 @@ struct Alice3Dilepton { registry.add("Generated/Particle/prodVx", "Particle Prod. Vertex X", kTH1F, {axisProdx}); registry.add("Generated/Particle/prodVy", "Particle Prod. Vertex Y", kTH1F, {axisPrody}); registry.add("Generated/Particle/prodVz", "Particle Prod. Vertex Z", kTH1F, {axisProdz}); - registry.add("Generated/Particle/ParticlesPerEvent", "Particles per event", kTH1F, {{100, 0, 100}}); - registry.add("Generated/Particle/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); - - registry.add("Generated/Pair/ULS/Tried", "Pair tries", kTH1F, {{10, -0.5, 9.5}}); - registry.add("Generated/Pair/ULS/Mass", "Pair Mass", kTH1F, {axisM}); - registry.add("Generated/Pair/ULS/Pt", "Pair Pt", kTH1F, {axisPt}); - registry.add("Generated/Pair/ULS/Eta", "Pair Eta", kTH1F, {axisEta}); - registry.add("Generated/Pair/ULS/Phi", "Pair Phi", kTH1F, {axisPhi}); - registry.add("Generated/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); - - registry.addClone("Generated/Pair/ULS/", "Generated/Pair/LSpp/"); - registry.addClone("Generated/Pair/ULS/", "Generated/Pair/LSnn/"); } if (doprocessRec) { @@ -137,13 +123,473 @@ struct Alice3Dilepton { registry.add("Reconstructed/Track/Eta", "Track Eta", kTH1F, {axisEta}); registry.add("Reconstructed/Track/Phi", "Track Phi", kTH1F, {axisPhi}); registry.add("Reconstructed/Track/Eta_Pt", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + registry.add("Reconstructed/Track/DCAxysigma", "Track DCAxy in #sigma", kTH2F, {axisPt, axisDCAxysigma}); + registry.add("Reconstructed/Track/DCAxy", "Track DCAxy in #micro m", kTH2F, {axisPt, axisDCAxy}); registry.add("Reconstructed/Track/SigmaOTofvspt", "Track #sigma oTOF", kTH2F, {axisPt, axisSigmaEl}); registry.add("Reconstructed/Track/SigmaITofvspt", "Track #sigma iTOF", kTH2F, {axisPt, axisSigmaEl}); registry.add("Reconstructed/Track/SigmaRichvspt", "Track #sigma RICH", kTH2F, {axisPt, axisSigmaEl}); registry.add("Reconstructed/Track/outerTOFTrackLength", "Track length outer TOF", kTH1F, {axisTrackLengthOuterTOF}); + registry.add("Reconstructed/Track/PtMC", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/EtaMC", "Track Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/PhiMC", "Track Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/EtaMC_PtMC", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + registry.addClone("Reconstructed/Track/", "Reconstructed/TrackPID/"); + registry.addClone("Reconstructed/Track/", "Reconstructed/TrackPIDPre/"); + } + + if (doprocessRecWithSmearing) { + registry.add("Reconstructed/Event/VtxX", "Vertex X", kTH1F, {axisVx}); + registry.add("Reconstructed/Event/VtxY", "Vertex Y", kTH1F, {axisVy}); + registry.add("Reconstructed/Event/VtxZ", "Vertex Z", kTH1F, {axisVz}); + + registry.add("Reconstructed/Track/Pt", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/Eta", "Track Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/Phi", "Track Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/Eta_Pt", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + registry.add("Reconstructed/Track/DCAxysigma", "Track DCAxy in #sigma", kTH2F, {axisPt, axisDCAxysigma}); + registry.add("Reconstructed/Track/DCAxy", "Track DCAxy in #micro m", kTH2F, {axisPt, axisDCAxy}); + + registry.add("Reconstructed/Track/PtMC", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/EtaMC", "Track Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/PhiMC", "Track Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/EtaMC_PtMC", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + + registry.addClone("Reconstructed/Track/", "Reconstructed/TrackPID/"); + registry.addClone("Reconstructed/Track/", "Reconstructed/TrackPIDPre/"); + } + } + + template + int IsHF(TMCParticle1 const& p1, TMCParticles const& mcparticles) + { + if (!p1.has_mothers()) { + return HFType::kUndef; + } + + int motherid_p1 = p1.mothersIds()[0]; + if (motherid_p1 > -1) { + auto mother_p1 = mcparticles.iteratorAt(motherid_p1); + int mother1_pdg = mother_p1.pdgCode(); + // direct beauty + if (((500 < std::abs(mother1_pdg) && std::abs(mother1_pdg) < 599) || (5000 < std::abs(mother1_pdg) && std::abs(mother1_pdg) < 5999))) { + return HFType::kBe; + } + // charm direct or from beauty + if (((400 < std::abs(mother1_pdg) && std::abs(mother1_pdg) < 499) || (4000 < std::abs(mother1_pdg) && std::abs(mother1_pdg) < 4999))) { + if (mother_p1.has_mothers()) { + int motherId = mother_p1.mothersIds()[0]; + while (motherId > -1) { + auto mp = mcparticles.rawIteratorAt(motherId); + if (((500 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 599) || (5000 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 5999))) { + return HFType::kBCe; + } + if (mp.has_mothers()) { + motherId = mp.mothersIds()[0]; + } else { + motherId = -999; + } + } + } + return HFType::kCe; + } + // pi0 not from HF + if (mother1_pdg == 111) { + if (mother_p1.has_mothers()) { + int motherId = mother_p1.mothersIds()[0]; + while (motherId > -1) { + auto mp = mcparticles.rawIteratorAt(motherId); + if (((500 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 599) || (5000 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 5999)) || ((400 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 499) || (4000 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 4999))) { + return HFType::kUndef; + } + if (mp.has_mothers()) { + motherId = mp.mothersIds()[0]; + } else { + motherId = -999; + } + } + } + return HFType::kPPi0; + } + return HFType::kUndef; + } + return HFType::kUndef; + } + + template + bool IsInAcceptance(TTrack const& track) + { + if constexpr (isWithSmearing) { + if (track.ptSmeared() < ptMin || ptMax < track.ptSmeared()) { + return false; + } + if (track.etaSmeared() < etaMin || etaMax < track.etaSmeared()) { + return false; + } + return true; + } else { + if (track.pt() < ptMin || ptMax < track.pt()) { + return false; + } + if (track.eta() < etaMin || etaMax < track.eta()) { + return false; + } + return true; + } + } + + // Functions for pid + template + bool electronIDTOF(TTrack const& track) + { + bool isElectron = false; + bool isEleOuterTOF = std::abs(track.nSigmaElectronOuterTOF()) < nSigmaEleCutOuterTOF; + bool isNotPionOuterTOF = std::abs(track.nSigmaPionOuterTOF()) > nSigmaPionCutOuterTOF; + isEleOuterTOF = isEleOuterTOF && isNotPionOuterTOF; + bool isEleInnerTOF = std::abs(track.nSigmaElectronInnerTOF()) < nSigmaEleCutInnerTOF; + bool isNotPionInnerTOF = std::abs(track.nSigmaPionInnerTOF()) > nSigmaPionCutInnerTOF; + isEleInnerTOF = isEleInnerTOF && isNotPionInnerTOF; + isElectron = (isEleOuterTOF || isEleInnerTOF); + return isElectron; + } + + template + bool electronIDRICH(TTrack const& track) + { + bool isElectron = false; + bool isEleRICH = std::abs(track.nSigmaElectronRich()) < nSigmaElectronRich; + bool isNotPionRICH = std::abs(track.nSigmaPionRich()) > nSigmaPionRich; + isElectron = isEleRICH && isNotPionRICH; + return isElectron; + } + + template + void FillRec(TTracks const& tracks, + const aod::McParticles& mcParticles) + { + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto mcParticle = track.template mcParticle_as(); + if (std::abs(mcParticle.pdgCode()) != pdg) { + continue; + } + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (useGen) { + if (!IsInAcceptance(mcParticle)) { + continue; + } + } else { + if (!IsInAcceptance(track)) { + continue; + } + } + if (requireHFE > -1) { + int typehfe = IsHF(mcParticle, mcParticles); + if (requireHFE < HFType::kHFE && typehfe != requireHFE) { + continue; + } + if (requireHFE == HFType::kHFE && (typehfe == HFType::kUndef || typehfe == kPPi0)) { + continue; + } + } + const float dcaXY = track.dcaXY(); + const float dcaXY_res = std::sqrt(track.cYY()); + float dcaXYinSigma = dcaXY / dcaXY_res; + float dcaXYinmicrom = dcaXY * 10000.; + registry.fill(HIST("Reconstructed/Track/DCAxy"), mcParticle.pt(), dcaXYinmicrom); + registry.fill(HIST("Reconstructed/Track/DCAxysigma"), mcParticle.pt(), dcaXYinSigma); + if constexpr (isWithSmearing) { + registry.fill(HIST("Reconstructed/Track/Pt"), track.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), track.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), track.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), track.ptSmeared(), track.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/Track/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/Track/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/Track/EtaMC_PtMC"), mcParticle.pt(), mcParticle.eta()); + } else { + registry.fill(HIST("Reconstructed/Track/SigmaOTofvspt"), track.pt(), track.nSigmaElectronOuterTOF()); + registry.fill(HIST("Reconstructed/Track/SigmaITofvspt"), track.pt(), track.nSigmaElectronInnerTOF()); + registry.fill(HIST("Reconstructed/Track/SigmaRichvspt"), track.pt(), track.nSigmaElectronRich()); + registry.fill(HIST("Reconstructed/Track/outerTOFTrackLength"), track.outerTOFTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), track.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), track.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), track.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), track.pt(), track.eta()); + registry.fill(HIST("Reconstructed/Track/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/Track/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/Track/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/Track/EtaMC_PtMC"), mcParticle.pt(), mcParticle.eta()); + } + // implement pid + if constexpr (isWithSmearing) { + if (track.selected()) { + registry.fill(HIST("Reconstructed/TrackPID/DCAxy"), mcParticle.pt(), dcaXYinmicrom); + registry.fill(HIST("Reconstructed/TrackPID/DCAxysigma"), mcParticle.pt(), dcaXYinSigma); + registry.fill(HIST("Reconstructed/TrackPID/Pt"), track.ptSmeared()); + registry.fill(HIST("Reconstructed/TrackPID/Eta"), track.etaSmeared()); + registry.fill(HIST("Reconstructed/TrackPID/Phi"), track.phiSmeared()); + registry.fill(HIST("Reconstructed/TrackPID/Eta_Pt"), track.ptSmeared(), track.etaSmeared()); + registry.fill(HIST("Reconstructed/TrackPID/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/TrackPID/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/TrackPID/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/TrackPID/EtaMC_PtMC"), mcParticle.pt(), mcParticle.eta()); + + if (track.isTrackPrefilter() == 0) { + registry.fill(HIST("Reconstructed/TrackPIDPre/DCAxy"), mcParticle.pt(), dcaXYinmicrom); + registry.fill(HIST("Reconstructed/TrackPIDPre/DCAxysigma"), mcParticle.pt(), dcaXYinSigma); + registry.fill(HIST("Reconstructed/TrackPIDPre/Pt"), track.ptSmeared()); + registry.fill(HIST("Reconstructed/TrackPIDPre/Eta"), track.etaSmeared()); + registry.fill(HIST("Reconstructed/TrackPIDPre/Phi"), track.phiSmeared()); + registry.fill(HIST("Reconstructed/TrackPIDPre/Eta_Pt"), track.ptSmeared(), track.etaSmeared()); + registry.fill(HIST("Reconstructed/TrackPIDPre/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/TrackPIDPre/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/TrackPIDPre/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/TrackPIDPre/EtaMC_PtMC"), mcParticle.pt(), mcParticle.eta()); + } + } + } else { + bool isElectronTOF = electronIDTOF(track); + bool isElectronRICH = electronIDRICH(track); + if (isElectronTOF || isElectronRICH) { + registry.fill(HIST("Reconstructed/TrackPID/SigmaOTofvspt"), track.pt(), track.nSigmaElectronOuterTOF()); + registry.fill(HIST("Reconstructed/TrackPID/SigmaITofvspt"), track.pt(), track.nSigmaElectronInnerTOF()); + registry.fill(HIST("Reconstructed/TrackPID/SigmaRichvspt"), track.pt(), track.nSigmaElectronRich()); + registry.fill(HIST("Reconstructed/TrackPID/outerTOFTrackLength"), track.outerTOFTrackLength()); + registry.fill(HIST("Reconstructed/TrackPID/DCAxy"), mcParticle.pt(), dcaXYinmicrom); + registry.fill(HIST("Reconstructed/TrackPID/DCAxysigma"), mcParticle.pt(), dcaXYinSigma); + registry.fill(HIST("Reconstructed/TrackPID/Pt"), track.pt()); + registry.fill(HIST("Reconstructed/TrackPID/Eta"), track.eta()); + registry.fill(HIST("Reconstructed/TrackPID/Phi"), track.phi()); + registry.fill(HIST("Reconstructed/TrackPID/Eta_Pt"), track.pt(), track.eta()); + registry.fill(HIST("Reconstructed/TrackPID/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/TrackPID/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/TrackPID/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/TrackPID/EtaMC_PtMC"), mcParticle.pt(), mcParticle.eta()); + + if (track.isTrackPrefilter() == 0) { + registry.fill(HIST("Reconstructed/TrackPIDPre/SigmaOTofvspt"), track.pt(), track.nSigmaElectronOuterTOF()); + registry.fill(HIST("Reconstructed/TrackPIDPre/SigmaITofvspt"), track.pt(), track.nSigmaElectronInnerTOF()); + registry.fill(HIST("Reconstructed/TrackPIDPre/SigmaRichvspt"), track.pt(), track.nSigmaElectronRich()); + registry.fill(HIST("Reconstructed/TrackPIDPre/outerTOFTrackLength"), track.outerTOFTrackLength()); + registry.fill(HIST("Reconstructed/TrackPIDPre/DCAxy"), mcParticle.pt(), dcaXYinmicrom); + registry.fill(HIST("Reconstructed/TrackPIDPre/DCAxysigma"), mcParticle.pt(), dcaXYinSigma); + registry.fill(HIST("Reconstructed/TrackPIDPre/Pt"), track.pt()); + registry.fill(HIST("Reconstructed/TrackPIDPre/Eta"), track.eta()); + registry.fill(HIST("Reconstructed/TrackPIDPre/Phi"), track.phi()); + registry.fill(HIST("Reconstructed/TrackPIDPre/Eta_Pt"), track.pt(), track.eta()); + registry.fill(HIST("Reconstructed/TrackPIDPre/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/TrackPIDPre/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/TrackPIDPre/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/TrackPIDPre/EtaMC_PtMC"), mcParticle.pt(), mcParticle.eta()); + } + } + } + } // end of track loop + } // end of FillRec + + Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; + + void processGen(o2::aod::McCollisions const& mccollisions, aod::McParticles const& mcParticles) + { + for (const auto& mccollision : mccollisions) { + auto mcParticles_per_coll = mcParticles.sliceBy(perMCCollision, mccollision.globalIndex()); + // int nParticlesInEvent = 0; + int nParticlesFIT = 0; + // Calculate the number of particles in the FIT acceptance + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + if ((2.2 < mcParticle.eta() && mcParticle.eta() < 5.0) || (-3.4 < mcParticle.eta() && mcParticle.eta() < -2.3)) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + nParticlesFIT++; + } + } + } + // nParticlesInEvent++; + } + } + if (nParticlesFIT > minFITPart && nParticlesFIT < maxFITPart) { + registry.fill(HIST("Generated/Event/VtxX"), mccollision.posX()); + registry.fill(HIST("Generated/Event/VtxY"), mccollision.posY()); + registry.fill(HIST("Generated/Event/VtxZ"), mccollision.posZ()); + + for (const auto& mcParticle : mcParticles_per_coll) { + if (std::abs(mcParticle.pdgCode()) != pdg) { + continue; + } + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (requireHFE > -1) { + int typehfe = IsHF(mcParticle, mcParticles); + if (requireHFE < HFType::kHFE && typehfe != requireHFE) { + continue; + } + if (requireHFE == HFType::kHFE && (typehfe == HFType::kUndef || typehfe == kPPi0)) { + continue; + } + } + if (!IsInAcceptance(mcParticle)) { + continue; + } + registry.fill(HIST("Generated/Particle/Pt"), mcParticle.pt()); + registry.fill(HIST("Generated/Particle/Eta"), mcParticle.eta()); + registry.fill(HIST("Generated/Particle/Phi"), mcParticle.phi()); + registry.fill(HIST("Generated/Particle/Eta_Pt"), mcParticle.pt(), mcParticle.eta()); + + registry.fill(HIST("Generated/Particle/prodVx"), mcParticle.vx()); + registry.fill(HIST("Generated/Particle/prodVy"), mcParticle.vy()); + registry.fill(HIST("Generated/Particle/prodVz"), mcParticle.vz()); + } // end of mc particle loop + registry.fill(HIST("Generated/Event/ParticlesFit"), nParticlesFIT); + } + } // end of mc collision loop + } // end of processGen + + using MyTracksMC = soa::Join; + using MyTracksWithSmearing = soa::Join; + using Alice3Collision = soa::Join; + + Filter trackFilter = o2::aod::track_alice3::isReconstructed == selectReconstructed; + + using MyFilteredTracksMC = soa::Filtered; + using MyFilteredTracksWithSmearing = soa::Filtered; + Filter configFilter = (aod::upgrade_collision::lutConfigId == otfConfig); + Filter CollisionFilter = o2::aod::dileptonanalysisflags::isEventCentSelected == 1; + using MyFilteredAlice3Collision = soa::Filtered; + Preslice perCollision = aod::track::collisionId; + Preslice perCollisionWithSmearing = aod::track::collisionId; + + void processRec(MyFilteredAlice3Collision const& collisions, + MyFilteredTracksMC const& tracks, + const o2::aod::McCollisions&, + const aod::McParticles& mcParticles) + { + for (const auto& collision : collisions) { + registry.fill(HIST("Reconstructed/Event/VtxX"), collision.posX()); + registry.fill(HIST("Reconstructed/Event/VtxY"), collision.posY()); + registry.fill(HIST("Reconstructed/Event/VtxZ"), collision.posZ()); + + auto tracks_coll = tracks.sliceBy(perCollision, collision.globalIndex()); + FillRec(tracks_coll, mcParticles); + } // end of collision loop + } // end of processRec + + void processRecWithSmearing(MyFilteredAlice3Collision const& collisions, + MyFilteredTracksWithSmearing const& tracks, + const o2::aod::McCollisions&, + const aod::McParticles& mcParticles) + { + for (const auto& collision : collisions) { + registry.fill(HIST("Reconstructed/Event/VtxX"), collision.posX()); + registry.fill(HIST("Reconstructed/Event/VtxY"), collision.posY()); + registry.fill(HIST("Reconstructed/Event/VtxZ"), collision.posZ()); + + auto tracks_coll = tracks.sliceBy(perCollisionWithSmearing, collision.globalIndex()); + FillRec(tracks_coll, mcParticles); + } // end of collision loop + } // end of processRec + + void processDummy(Alice3Collision const&) + { + } + + PROCESS_SWITCH(Alice3Lepton, processGen, "Run for generated particle", false); + PROCESS_SWITCH(Alice3Lepton, processRec, "Run for reconstructed track", false); + PROCESS_SWITCH(Alice3Lepton, processRecWithSmearing, "Run for track with smearing", false); + PROCESS_SWITCH(Alice3Lepton, processDummy, "Dummy run", true); +}; + +struct Alice3Dilepton { + enum HFllType { + kUndef = -1, + kCe_Ce = 0, // ULS + kBe_Be = 1, // ULS + kBCe_BCe = 2, // ULS + kBCe_Be_SameB = 3, // ULS + kBCe_Be_DiffB = 4, // LS + }; + enum PairType { + kULS = 0, + kLSpp = 1, + kLSnn = 2, + }; + + SliceCache cache_mc; + SliceCache cache_rec; + + Configurable pdg{"pdg", 11, "pdg code for analysis. dielectron:11, dimuon:13"}; + Configurable requireDCid{"requireDCid", false, "Require pi0 or photon as mother"}; + Configurable requireHFEid{"requireHFEid", false, "Require HFE identification"}; + Configurable contamination{"contamination", false, "Fill only pairs with one misidentifixed electrons"}; + Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; + Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; + Configurable etaMin{"etaMin", -5.f, "Lower limit in eta"}; + Configurable etaMax{"etaMax", 5.f, "Upper limit in eta"}; + Configurable useGen{"useGen", false, "Use generated (true) or smeared/reconstructed (false) values for fiducial cuts"}; + Configurable selectReconstructed{"selectReconstructed", true, "Select only reconstructed tracks (true) or ghosts (false)"}; + Configurable nSigmaEleCutOuterTOF{"nSigmaEleCutOuterTOF", 3., "Electron inclusion in outer TOF"}; + Configurable nSigmaEleCutInnerTOF{"nSigmaEleCutInnerTOF", 3., "Electron inclusion in inner TOF"}; + Configurable nSigmaPionCutOuterTOF{"nSigmaPionCutOuterTOF", 3., "Pion exclusion in outer TOF"}; + Configurable nSigmaPionCutInnerTOF{"nSigmaPionCutInnerTOF", 3., "Pion exclusion in inner TOF"}; + Configurable nSigmaElectronRich{"nSigmaElectronRich", 3., "Electron inclusion RICH"}; + Configurable nSigmaPionRich{"nSigmaPionRich", 3., "Pion exclusion RICH"}; + Configurable otfConfig{"otfConfig", 0, "OTF configuration flag"}; + Configurable cfg_apply_prefilter{"cfg_apply_prefilter", false, "flag to apply prefilter"}; + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + const AxisSpec axisVz{100, -20, 20, "Vtx_{z}"}; + + const AxisSpec axisM{500, 0, 5, "#it{m}_{ll} (GeV/#it{c}^{2})"}; + const AxisSpec axisPt{1000, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisSigmaEl{200, -10, 10, "n#sigma_{El}"}; + const AxisSpec axisEta{1000, -5, 5, "#it{#eta}"}; + const AxisSpec axisDCAxy{1000, 0, 20, "DCA_{xy,ll} (#sigma)"}; + const AxisSpec axisPhi{360, 0, TMath::TwoPi(), "#it{#varphi} (rad.)"}; + const AxisSpec axisPre{2, 0, 2, "Prefilter bit"}; + + if (doprocessRecAll || doprocessRecAllWithSmearing) { + registry.add("Reconstructed/Event/VtxZ", "Vertex Z", kTH1F, {axisVz}); + registry.add("Reconstructed/Track/Pt", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/Eta", "Particle Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/Phi", "Particle Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/Pre", "Particle Pre", kTH1F, {axisPre}); + registry.add("Reconstructed/Track/PtMC", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/EtaMC", "Particle Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/PhiMC", "Particle Phi", kTH1F, {axisPhi}); + } + + if (doprocessRecAll) { + registry.add("Reconstructed/Track/SigmaOTofvspt", "Track #sigma oTOF", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/SigmaITofvspt", "Track #sigma iTOF", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/SigmaRichvspt", "Track #sigma RICH", kTH2F, {axisPt, axisSigmaEl}); + } + + if (doprocessGen) { + registry.add("Generated/Pair/ULS/Tried", "Pair tries", kTH1F, {{10, -0.5, 9.5}}); + registry.add("Generated/Pair/ULS/Mass", "Pair Mass", kTH1F, {axisM}); + registry.add("Generated/Pair/ULS/Pt", "Pair Pt", kTH1F, {axisPt}); + registry.add("Generated/Pair/ULS/Eta", "Pair Eta", kTH1F, {axisEta}); + registry.add("Generated/Pair/ULS/Phi", "Pair Phi", kTH1F, {axisPhi}); + registry.add("Generated/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + + registry.addClone("Generated/Pair/ULS/", "Generated/Pair/LSpp/"); + registry.addClone("Generated/Pair/ULS/", "Generated/Pair/LSnn/"); + } + + if (doprocessRec || doprocessRecAll || doprocessRecWithSmearing || doprocessRecAllWithSmearing) { registry.add("Reconstructed/Pair/ULS/Mass", "Pair Mass", kTH1F, {axisM}); registry.add("Reconstructed/Pair/ULS/Pt", "Pair Pt", kTH1F, {axisPt}); registry.add("Reconstructed/Pair/ULS/Eta", "Pair Eta", kTH1F, {axisEta}); @@ -153,15 +599,6 @@ struct Alice3Dilepton { registry.addClone("Reconstructed/Pair/ULS/", "Reconstructed/Pair/LSpp/"); registry.addClone("Reconstructed/Pair/ULS/", "Reconstructed/Pair/LSnn/"); - registry.add("ReconstructedFiltered/Pair/ULS/Mass", "Pair Mass", kTH1F, {axisM}); - registry.add("ReconstructedFiltered/Pair/ULS/Pt", "Pair Pt", kTH1F, {axisPt}); - registry.add("ReconstructedFiltered/Pair/ULS/Eta", "Pair Eta", kTH1F, {axisEta}); - registry.add("ReconstructedFiltered/Pair/ULS/Phi", "Pair Phi", kTH1F, {axisPhi}); - registry.add("ReconstructedFiltered/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); - - registry.addClone("ReconstructedFiltered/Pair/ULS/", "ReconstructedFiltered/Pair/LSpp/"); - registry.addClone("ReconstructedFiltered/Pair/ULS/", "ReconstructedFiltered/Pair/LSnn/"); - HistogramConfigSpec hs_rec{HistType::kTHnSparseF, {axisM, axisPt, axisDCAxy}, 3}; registry.add("Reconstructed/Pair/ULS/hs_rec", "", hs_rec); registry.add("Reconstructed/Pair/LSpp/hs_rec", "", hs_rec); @@ -172,27 +609,82 @@ struct Alice3Dilepton { } } - template + template bool IsInAcceptance(TTrack const& track) { - if (track.pt() < ptMin || ptMax < track.pt()) { + if constexpr (isWithSmearing) { + if (track.ptSmeared() < ptMin || ptMax < track.ptSmeared()) { + return false; + } + if (track.etaSmeared() < etaMin || etaMax < track.etaSmeared()) { + return false; + } + return true; + } else { + if (track.pt() < ptMin || ptMax < track.pt()) { + return false; + } + if (track.eta() < etaMin || etaMax < track.eta()) { + return false; + } + return true; + } + } + + template + bool IsDC(TTrack const& track, TMCParticles const& mcparticles) + { + if (!track.has_mcParticle()) { return false; } - if (track.eta() < etaMin || etaMax < track.eta()) { + const auto p1 = track.template mcParticle_as(); + if (!p1.has_mothers()) { return false; } - return true; + int motherId = p1.mothersIds()[0]; + if (motherId > -1) { + auto mp = mcparticles.rawIteratorAt(motherId); + if ((std::abs(mp.pdgCode()) == PDG_t::kPi0) || (std::abs(mp.pdgCode()) == PDG_t::kGamma)) { + return true; + } + } + return false; + } + + template + bool IsHF(TTrack const& track, TMCParticles const& mcparticles) + { + if (!track.has_mcParticle()) { + return false; + } + const auto p1 = track.template mcParticle_as(); + if (!p1.has_mothers()) { + return false; + } + int motherId = p1.mothersIds()[0]; + while (motherId > -1) { + auto mp = mcparticles.rawIteratorAt(motherId); + if (((500 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 599) || (5000 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 5999)) || ((400 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 499) || (4000 < std::abs(mp.pdgCode()) && std::abs(mp.pdgCode()) < 4999))) { + return true; + } + if (mp.has_mothers()) { + motherId = mp.mothersIds()[0]; + } else { + motherId = -999; + } + } + return false; } template int IsSameMother(TMCParticle1 const& p1, TMCParticle2 const& p2, TMCParticles const& mcparticles) { - if (!p1.has_mothers()) + if (!p1.has_mothers()) { return -1; - if (!p2.has_mothers()) + } + if (!p2.has_mothers()) { return -1; - - // LOGF(info,"original motherid1 = %d , motherid2 = %d", p1.mothersIds()[0], p2.mothersIds()[0]); + } int motherid1 = p1.mothersIds()[0]; auto mother1 = mcparticles.iteratorAt(motherid1); @@ -202,27 +694,26 @@ struct Alice3Dilepton { auto mother2 = mcparticles.iteratorAt(motherid2); int mother2_pdg = mother2.pdgCode(); - // LOGF(info,"motherid1 = %d , motherid2 = %d", motherid1, motherid2); - - if (motherid1 != motherid2) + if (motherid1 != motherid2) { return -1; - if (mother1_pdg != mother2_pdg) + } + if (mother1_pdg != mother2_pdg) { return -1; + } - if (std::abs(mother1_pdg) != 22 // photon - && std::abs(mother1_pdg) != 111 // pi0 - && std::abs(mother1_pdg) != 221 // eta - && std::abs(mother1_pdg) != 331 // eta' - && std::abs(mother1_pdg) != 113 // rho - && std::abs(mother1_pdg) != 223 // omega - && std::abs(mother1_pdg) != 333 // phi - && std::abs(mother1_pdg) != 443 // Jpsi - && std::abs(mother1_pdg) != 100443 // psi2S + if (std::abs(mother1_pdg) != PDG_t::kGamma // photon + && std::abs(mother1_pdg) != PDG_t::kPi0 // pi0 + && std::abs(mother1_pdg) != o2::constants::physics::kEta // eta + && std::abs(mother1_pdg) != o2::constants::physics::kEtaPrime // eta' + && std::abs(mother1_pdg) != PDG_t::kRho770_0 // rho + && std::abs(mother1_pdg) != o2::constants::physics::kOmega // omega + && std::abs(mother1_pdg) != o2::constants::physics::kPhi // phi + && std::abs(mother1_pdg) != o2::constants::physics::kJPsi // Jpsi + && std::abs(mother1_pdg) != 100443 // psi2S ) { return -1; } - // LOGF(info, "%d is found.", mother1_pdg); return motherid1; } @@ -234,10 +725,12 @@ struct Alice3Dilepton { // 1. b->e- and bbar->e+ (different b and bbar) // 2. b->c->e+ and bbar->cbar->e- (different b and bbar) // 3. b->c->e+ and b->e- (1 same b (or bbar)) - if (!p1.has_mothers()) + if (!p1.has_mothers()) { return HFllType::kUndef; - if (!p2.has_mothers()) + } + if (!p2.has_mothers()) { return HFllType::kUndef; + } int motherid_p1 = p1.mothersIds()[0]; int motherid_p2 = p2.mothersIds()[0]; @@ -287,10 +780,12 @@ struct Alice3Dilepton { { // in total, 1 case for LS pairs // 4. b->c->e+ and bbar->e+ - if (!p1.has_mothers()) + if (!p1.has_mothers()) { return HFllType::kUndef; - if (!p2.has_mothers()) + } + if (!p2.has_mothers()) { return HFllType::kUndef; + } int motherid_p1 = p1.mothersIds()[0]; int motherid_p2 = p2.mothersIds()[0]; @@ -322,74 +817,123 @@ struct Alice3Dilepton { return HFllType::kUndef; } - template + template ROOT::Math::PtEtaPhiMVector buildPairDCA(T1 const& t1, T2 const& t2, float& pair_dca_xy) { const float dcaXY_t1 = t1.dcaXY(); const float dcaXY_t2 = t2.dcaXY(); - const float dcaXY_res_t1 = sqrt(t1.cYY()); - const float dcaXY_res_t2 = sqrt(t2.cYY()); - - pair_dca_xy = sqrt((dcaXY_t2 * dcaXY_t2 / dcaXY_res_t2 + dcaXY_t1 * dcaXY_t1 / dcaXY_res_t1) / 2.); - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), std::abs(pdg) == 11 ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); // reconstructed pt/eta/phi - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), std::abs(pdg) == 11 ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); // reconstructed pt/eta/phi - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - return v12; + const float dcaXY_res_t1 = std::sqrt(t1.cYY()); + const float dcaXY_res_t2 = std::sqrt(t2.cYY()); + float dcaXYinSigma_t1 = dcaXY_t1 / dcaXY_res_t1; + float dcaXYinSigma_t2 = dcaXY_t2 / dcaXY_res_t2; + + pair_dca_xy = std::sqrt((dcaXYinSigma_t1 * dcaXYinSigma_t1 + dcaXYinSigma_t2 * dcaXYinSigma_t2) / 2.); + if constexpr (isWithSmearing) { + ROOT::Math::PtEtaPhiMVector v1(t1.ptSmeared(), t1.etaSmeared(), t1.phiSmeared(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); // reconstructed pt/eta/phi + ROOT::Math::PtEtaPhiMVector v2(t2.ptSmeared(), t2.etaSmeared(), t2.phiSmeared(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); // reconstructed pt/eta/phi + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + return v12; + } else { + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); // reconstructed pt/eta/phi + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); // reconstructed pt/eta/phi + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + return v12; + } } - template - void FillPairRecWithPrefilter(TTracks const& tracks1, TTracks const& tracks2, TMCTracks const& /*mcParticles*/) + template + void FillPairRecAll(TTracks const& tracks1, TTracks const& tracks2, const aod::McParticles& mcParticles) { - std::vector prefilteredTracks; if constexpr (pairtype == PairType::kULS) { - for (auto& [t1, t2] : combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!t1.has_mcParticle() || !t2.has_mcParticle()) { + for (const auto& [t1, t2] : combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { continue; } - - float pair_dca_xy = 999.f; - ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); - // prefilter for low-mass pairs - if (v12.M() > 0.10) { - continue; + if (contamination) { + if (t1.has_mcParticle() && t2.has_mcParticle()) { + auto mct1 = t1.template mcParticle_as(); + auto mct2 = t2.template mcParticle_as(); + if (std::abs(mct1.pdgCode()) == pdg && std::abs(mct2.pdgCode()) == pdg) { + continue; + } + } } - // prefilter small opening angle pairs - if (std::cos(t1.phi() - t2.phi()) < 0.99) { - continue; + if (requireHFEid) { + if (!IsHF(t1, mcParticles) && !IsHF(t2, mcParticles)) { + continue; + } } - prefilteredTracks.push_back(t1.globalIndex()); - prefilteredTracks.push_back(t2.globalIndex()); + if (requireDCid) { + if (!IsDC(t1, mcParticles) && !IsDC(t2, mcParticles)) { + continue; + } + } + float pair_dca_xy = 999.f; + ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); + registry.fill(HIST("Reconstructed/Pair/ULS/Mass"), v12.M()); + registry.fill(HIST("Reconstructed/Pair/ULS/Pt"), v12.Pt()); + registry.fill(HIST("Reconstructed/Pair/ULS/Eta"), v12.Eta()); + registry.fill(HIST("Reconstructed/Pair/ULS/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + registry.fill(HIST("Reconstructed/Pair/ULS/hs_rec"), v12.M(), v12.Pt(), pair_dca_xy); } // end of unlike-sign pair loop - for (auto& [t1, t2] : combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - // Skipping tracks that are in the prefiltered list - if (std::find(prefilteredTracks.begin(), prefilteredTracks.end(), t1.globalIndex()) != prefilteredTracks.end()) { + } else if constexpr (pairtype == PairType::kLSpp || pairtype == PairType::kLSnn) { + for (const auto& [t1, t2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { + if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { continue; } - if (std::find(prefilteredTracks.begin(), prefilteredTracks.end(), t2.globalIndex()) != prefilteredTracks.end()) { - continue; + if (contamination) { + if (t1.has_mcParticle() && t2.has_mcParticle()) { + auto mct1 = t1.template mcParticle_as(); + auto mct2 = t2.template mcParticle_as(); + if (std::abs(mct1.pdgCode()) == pdg && std::abs(mct2.pdgCode()) == pdg) { + continue; + } + } + } + if (requireHFEid) { + if (!IsHF(t1, mcParticles) && !IsHF(t2, mcParticles)) { + continue; + } + } + if (requireDCid) { + if (!IsDC(t1, mcParticles) && !IsDC(t2, mcParticles)) { + continue; + } } - float pair_dca_xy = 999.f; - ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); - - registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass"), v12.M()); - registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Pt"), v12.Pt()); - registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Eta"), v12.Eta()); - registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Phi"), v12.Phi() < 0.f ? v12.Phi() + TMath::TwoPi() : v12.Phi()); - registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); - registry.fill(HIST("ReconstructedFiltered/Pair/ULS/hs_rec"), v12.M(), v12.Pt(), pair_dca_xy); - } + ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); + + if constexpr (pairtype == PairType::kLSpp) { + registry.fill(HIST("Reconstructed/Pair/LSpp/Mass"), v12.M()); + registry.fill(HIST("Reconstructed/Pair/LSpp/Pt"), v12.Pt()); + registry.fill(HIST("Reconstructed/Pair/LSpp/Eta"), v12.Eta()); + registry.fill(HIST("Reconstructed/Pair/LSpp/Mass_Pt"), v12.M(), v12.Pt()); + registry.fill(HIST("Reconstructed/Pair/LSpp/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); + registry.fill(HIST("Reconstructed/Pair/LSpp/hs_rec"), v12.M(), v12.Pt(), pair_dca_xy); + } else if constexpr (pairtype == PairType::kLSnn) { + registry.fill(HIST("Reconstructed/Pair/LSnn/Mass"), v12.M()); + registry.fill(HIST("Reconstructed/Pair/LSnn/Pt"), v12.Pt()); + registry.fill(HIST("Reconstructed/Pair/LSnn/Eta"), v12.Eta()); + registry.fill(HIST("Reconstructed/Pair/LSnn/Mass_Pt"), v12.M(), v12.Pt()); + registry.fill(HIST("Reconstructed/Pair/LSnn/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); + registry.fill(HIST("Reconstructed/Pair/LSnn/hs_rec"), v12.M(), v12.Pt(), pair_dca_xy); + } + } // end of like-sign pair loop } } - template - void FillPairRec(TTracks const& tracks1, TTracks const& tracks2, TMCTracks const& mcParticles) + template + void FillPairRec(TTracks const& tracks1, TTracks const& tracks2, const aod::McParticles& mcParticles) { if constexpr (pairtype == PairType::kULS) { - for (auto& [t1, t2] : combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + for (const auto& [t1, t2] : combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { + continue; + } if (!t1.has_mcParticle() || !t2.has_mcParticle()) { continue; } @@ -410,21 +954,23 @@ struct Alice3Dilepton { if (motherid < 0 && hfee_type == HFllType::kUndef) { continue; } - // auto mother = mcparticles.iteratorAt(motherid); float pair_dca_xy = 999.f; - ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); + ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); registry.fill(HIST("Reconstructed/Pair/ULS/Mass"), v12.M()); registry.fill(HIST("Reconstructed/Pair/ULS/Pt"), v12.Pt()); registry.fill(HIST("Reconstructed/Pair/ULS/Eta"), v12.Eta()); - registry.fill(HIST("Reconstructed/Pair/ULS/Phi"), v12.Phi() < 0.f ? v12.Phi() + TMath::TwoPi() : v12.Phi()); + registry.fill(HIST("Reconstructed/Pair/ULS/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); registry.fill(HIST("Reconstructed/Pair/ULS/hs_rec"), v12.M(), v12.Pt(), pair_dca_xy); } // end of unlike-sign pair loop } else if constexpr (pairtype == PairType::kLSpp || pairtype == PairType::kLSnn) { - for (auto& [t1, t2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { + for (const auto& [t1, t2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { + if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { + continue; + } if (!t1.has_mcParticle() || !t2.has_mcParticle()) { continue; } @@ -439,41 +985,39 @@ struct Alice3Dilepton { continue; } - int motherid = -1; int hfee_type = IsHFLS(mct1, mct2, mcParticles); - if (motherid < 0 && hfee_type == HFllType::kUndef) { + if (hfee_type == HFllType::kUndef) { continue; } - // auto mother = mcparticles.iteratorAt(motherid); float pair_dca_xy = 999.f; - ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); + ROOT::Math::PtEtaPhiMVector v12 = buildPairDCA(t1, t2, pair_dca_xy); if constexpr (pairtype == PairType::kLSpp) { registry.fill(HIST("Reconstructed/Pair/LSpp/Mass"), v12.M()); registry.fill(HIST("Reconstructed/Pair/LSpp/Pt"), v12.Pt()); registry.fill(HIST("Reconstructed/Pair/LSpp/Eta"), v12.Eta()); registry.fill(HIST("Reconstructed/Pair/LSpp/Mass_Pt"), v12.M(), v12.Pt()); - registry.fill(HIST("Reconstructed/Pair/LSpp/Phi"), v12.Phi() < 0.f ? v12.Phi() + TMath::TwoPi() : v12.Phi()); + registry.fill(HIST("Reconstructed/Pair/LSpp/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); registry.fill(HIST("Reconstructed/Pair/LSpp/hs_rec"), v12.M(), v12.Pt(), pair_dca_xy); } else if constexpr (pairtype == PairType::kLSnn) { registry.fill(HIST("Reconstructed/Pair/LSnn/Mass"), v12.M()); registry.fill(HIST("Reconstructed/Pair/LSnn/Pt"), v12.Pt()); registry.fill(HIST("Reconstructed/Pair/LSnn/Eta"), v12.Eta()); registry.fill(HIST("Reconstructed/Pair/LSnn/Mass_Pt"), v12.M(), v12.Pt()); - registry.fill(HIST("Reconstructed/Pair/LSnn/Phi"), v12.Phi() < 0.f ? v12.Phi() + TMath::TwoPi() : v12.Phi()); + registry.fill(HIST("Reconstructed/Pair/LSnn/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); registry.fill(HIST("Reconstructed/Pair/LSnn/hs_rec"), v12.M(), v12.Pt(), pair_dca_xy); } } // end of like-sign pair loop } } - template + template void FillPairGen(TTracks const& tracks1, TTracks const& tracks2, TMCTracks const& mcParticles) { if constexpr (pairtype == PairType::kULS) { - for (auto& [t1, t2] : combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + for (const auto& [t1, t2] : combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { registry.fill(HIST("Generated/Pair/ULS/Tried"), 0); if (std::abs(t1.pdgCode()) != pdg || std::abs(t2.pdgCode()) != pdg) { continue; @@ -485,7 +1029,7 @@ struct Alice3Dilepton { } registry.fill(HIST("Generated/Pair/ULS/Tried"), 2); - if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { + if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { continue; } registry.fill(HIST("Generated/Pair/ULS/Tried"), 3); @@ -497,21 +1041,20 @@ struct Alice3Dilepton { continue; } registry.fill(HIST("Generated/Pair/ULS/Tried"), 4); - // auto mother = mcparticles.iteratorAt(motherid); - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), std::abs(pdg) == 11 ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), std::abs(pdg) == 11 ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; registry.fill(HIST("Generated/Pair/ULS/Mass"), v12.M()); registry.fill(HIST("Generated/Pair/ULS/Pt"), v12.Pt()); registry.fill(HIST("Generated/Pair/ULS/Eta"), v12.Eta()); - registry.fill(HIST("Generated/Pair/ULS/Phi"), v12.Phi() < 0.f ? v12.Phi() + TMath::TwoPi() : v12.Phi()); + registry.fill(HIST("Generated/Pair/ULS/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); registry.fill(HIST("Generated/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); } // end of unlike-sign pair loop } else if constexpr (pairtype == PairType::kLSpp || pairtype == PairType::kLSnn) { - for (auto& [t1, t2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { + for (const auto& [t1, t2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { if constexpr (pairtype == PairType::kLSpp) { registry.fill(HIST("Generated/Pair/LSpp/Tried"), 0); } else if constexpr (pairtype == PairType::kLSnn) { @@ -535,7 +1078,7 @@ struct Alice3Dilepton { registry.fill(HIST("Generated/Pair/LSnn/Tried"), 2); } - if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { + if (!IsInAcceptance(t1) || !IsInAcceptance(t2)) { continue; } if constexpr (pairtype == PairType::kLSpp) { @@ -557,21 +1100,21 @@ struct Alice3Dilepton { } // auto mother = mcparticles.iteratorAt(motherid); - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), std::abs(pdg) == 11 ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), std::abs(pdg) == 11 ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), std::abs(pdg) == PDG_t::kElectron ? o2::constants::physics::MassElectron : o2::constants::physics::MassMuon); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; if constexpr (pairtype == PairType::kLSpp) { registry.fill(HIST("Generated/Pair/LSpp/Mass"), v12.M()); registry.fill(HIST("Generated/Pair/LSpp/Pt"), v12.Pt()); registry.fill(HIST("Generated/Pair/LSpp/Eta"), v12.Eta()); - registry.fill(HIST("Generated/Pair/LSpp/Phi"), v12.Phi() < 0.f ? v12.Phi() + TMath::TwoPi() : v12.Phi()); + registry.fill(HIST("Generated/Pair/LSpp/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); registry.fill(HIST("Generated/Pair/LSpp/Mass_Pt"), v12.M(), v12.Pt()); } else if constexpr (pairtype == PairType::kLSnn) { registry.fill(HIST("Generated/Pair/LSnn/Mass"), v12.M()); registry.fill(HIST("Generated/Pair/LSnn/Pt"), v12.Pt()); registry.fill(HIST("Generated/Pair/LSnn/Eta"), v12.Eta()); - registry.fill(HIST("Generated/Pair/LSnn/Phi"), v12.Phi() < 0.f ? v12.Phi() + TMath::TwoPi() : v12.Phi()); + registry.fill(HIST("Generated/Pair/LSnn/Phi"), RecoDecay::constrainAngle(v12.Phi(), 0.f)); registry.fill(HIST("Generated/Pair/LSnn/Mass_Pt"), v12.M(), v12.Pt()); } @@ -579,184 +1122,190 @@ struct Alice3Dilepton { } } - // Functions for pid - template - bool electronIDTOF(TTrack const& track) - { - bool isElectron = false; - bool isEleOuterTOF = std::abs(track.nSigmaElectronOuterTOF()) < nSigmaEleCutOuterTOF; - bool isNotPionOuterTOF = std::abs(track.nSigmaPionOuterTOF()) > nSigmaPionCutOuterTOF; - isEleOuterTOF = isEleOuterTOF && isNotPionOuterTOF; - bool isEleInnerTOF = std::abs(track.nSigmaElectronInnerTOF()) < nSigmaEleCutInnerTOF; - bool isNotPionInnerTOF = std::abs(track.nSigmaPionInnerTOF()) > nSigmaPionCutInnerTOF; - isEleInnerTOF = isEleInnerTOF && isNotPionInnerTOF; - isElectron = (isEleOuterTOF || isEleInnerTOF); - return isElectron; - } - - template - bool electronIDRICH(TTrack const& track) - { - bool isElectron = false; - bool isEleRICH = std::abs(track.nSigmaElectronRich()) < nSigmaElectronRich; - bool isNotPionRICH = std::abs(track.nSigmaPionRich()) > nSigmaPionRich; - isElectron = isEleRICH && isNotPionRICH; - return isElectron; - } - - Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; Partition pos_mcParticles = o2::aod::mcparticle::pdgCode == -pdg; //-11 or -13 Partition neg_mcParticles = o2::aod::mcparticle::pdgCode == pdg; // 11 or 13 void processGen(o2::aod::McCollisions const& mccollisions, aod::McParticles const& mcParticles) { for (const auto& mccollision : mccollisions) { - registry.fill(HIST("Generated/Event/VtxX"), mccollision.posX()); - registry.fill(HIST("Generated/Event/VtxY"), mccollision.posY()); - registry.fill(HIST("Generated/Event/VtxZ"), mccollision.posZ()); - - auto mcParticles_per_coll = mcParticles.sliceBy(perMCCollision, mccollision.globalIndex()); - int nParticlesInEvent = 0; - int nParticlesFIT = 0; - for (const auto& mcParticle : mcParticles_per_coll) { - if (mcParticle.isPhysicalPrimary()) { - if ((2.2 < mcParticle.eta() && mcParticle.eta() < 5.0) || (-3.4 < mcParticle.eta() && mcParticle.eta() < -2.3)) { - auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); - if (pdgParticle) { - float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| - if (std::abs(charge) >= 1.) { - nParticlesFIT++; - } - } - } - } - if (std::abs(mcParticle.pdgCode()) != pdg) { - continue; - } - if (!mcParticle.isPhysicalPrimary()) { - continue; - } - if (!IsInAcceptance(mcParticle)) { - continue; - } - nParticlesInEvent++; - - registry.fill(HIST("Generated/Particle/Pt"), mcParticle.pt()); - registry.fill(HIST("Generated/Particle/Eta"), mcParticle.eta()); - registry.fill(HIST("Generated/Particle/Phi"), mcParticle.phi()); - registry.fill(HIST("Generated/Particle/Eta_Pt"), mcParticle.pt(), mcParticle.eta()); - - registry.fill(HIST("Generated/Particle/prodVx"), mcParticle.vx()); - registry.fill(HIST("Generated/Particle/prodVy"), mcParticle.vy()); - registry.fill(HIST("Generated/Particle/prodVz"), mcParticle.vz()); - - } // end of mc particle loop - registry.fill(HIST("Generated/Particle/ParticlesPerEvent"), nParticlesInEvent); - registry.fill(HIST("Generated/Particle/ParticlesFit"), nParticlesFIT); - auto neg_mcParticles_coll = neg_mcParticles->sliceByCached(o2::aod::mcparticle::mcCollisionId, mccollision.globalIndex(), cache_mc); auto pos_mcParticles_coll = pos_mcParticles->sliceByCached(o2::aod::mcparticle::mcCollisionId, mccollision.globalIndex(), cache_mc); - FillPairGen(neg_mcParticles_coll, pos_mcParticles_coll, mcParticles); - FillPairGen(pos_mcParticles_coll, pos_mcParticles_coll, mcParticles); - FillPairGen(neg_mcParticles_coll, neg_mcParticles_coll, mcParticles); + FillPairGen(neg_mcParticles_coll, pos_mcParticles_coll, mcParticles); + FillPairGen(pos_mcParticles_coll, pos_mcParticles_coll, mcParticles); + FillPairGen(neg_mcParticles_coll, neg_mcParticles_coll, mcParticles); } // end of mc collision loop } // end of processGen - using MyTracksMC = soa::Join; - using Alice3Collision = soa::Join; + using MyTracksMC = soa::Join; + using MyTracksWithSmearing = soa::Join; + using Alice3Collision = soa::Join; - // Filter trackFilter = etaMin < o2::aod::track::eta && - // o2::aod::track::eta < etaMax && - // ptMin < o2::aod::track::pt && - // o2::aod::track::pt < ptMax && - // o2::aod::track_alice3::isReconstructed == selectReconstructed; + // Filter trackFilter = etaMin < o2::aod::track::eta && o2::aod::track::eta < etaMax && ptMin < o2::aod::track::pt && o2::aod::track::pt < ptMax && o2::aod::track_alice3::isReconstructed == selectReconstructed; Filter trackFilter = o2::aod::track_alice3::isReconstructed == selectReconstructed; + Filter pidFilter_withsmearing = o2::aod::dileptonsmearedtrack::selected == true; + Filter pidFilter_electron = (nabs(o2::aod::upgrade_rich::nSigmaElectronRich) < nSigmaElectronRich && nSigmaPionRich < nabs(o2::aod::upgrade_rich::nSigmaPionRich)) || (nabs(o2::aod::upgrade_tof::nSigmaElectronOuterTOF) < nSigmaEleCutOuterTOF && nSigmaPionCutOuterTOF < nabs(o2::aod::upgrade_tof::nSigmaPionOuterTOF)) || (nabs(o2::aod::upgrade_tof::nSigmaElectronInnerTOF) < nSigmaEleCutInnerTOF && nSigmaPionCutInnerTOF < nabs(o2::aod::upgrade_tof::nSigmaPionInnerTOF)); + Filter prefilter_electron = ifnode(cfg_apply_prefilter.node(), o2::aod::dileptonanalysisflags::isTrackPrefilter == 0, o2::aod::dileptonanalysisflags::isTrackPrefilter == 0 || o2::aod::dileptonanalysisflags::isTrackPrefilter == 1); + using MyFilteredTracksMC = soa::Filtered; + using MyFilteredTracksWithSmearing = soa::Filtered; Filter configFilter = (aod::upgrade_collision::lutConfigId == otfConfig); + Filter CollisionFilter = o2::aod::dileptonanalysisflags::isEventCentSelected == 1; using MyFilteredAlice3Collision = soa::Filtered; Preslice perCollision = aod::track::collisionId; Partition posTracks = o2::aod::track::signed1Pt > 0.f; Partition negTracks = o2::aod::track::signed1Pt < 0.f; + Partition posTracksWithSmearing = o2::aod::track::signed1Pt > 0.f; + Partition negTracksWithSmearing = o2::aod::track::signed1Pt < 0.f; void processRec(MyFilteredAlice3Collision const& collisions, - MyFilteredTracksMC const& tracks, + MyFilteredTracksMC const& /*tracks*/, const o2::aod::McCollisions&, const aod::McParticles& mcParticles) { for (const auto& collision : collisions) { - registry.fill(HIST("Reconstructed/Event/VtxX"), collision.posX()); - registry.fill(HIST("Reconstructed/Event/VtxY"), collision.posY()); + auto negTracks_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + FillPairRec(negTracks_coll, posTracks_coll, mcParticles); + FillPairRec(posTracks_coll, posTracks_coll, mcParticles); + FillPairRec(negTracks_coll, negTracks_coll, mcParticles); + + } // end of collision loop + } // end of processRec + + void processRecAll(MyFilteredAlice3Collision const& collisions, + MyFilteredTracksMC const&, + const aod::McParticles& mcParticles) + { + for (const auto& collision : collisions) { registry.fill(HIST("Reconstructed/Event/VtxZ"), collision.posZ()); + auto negTracks_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); - auto tracks_coll = tracks.sliceBy(perCollision, collision.globalIndex()); - for (const auto& track : tracks_coll) { - if (!track.has_mcParticle()) { + for (const auto& track : negTracks_coll) { + if (!IsInAcceptance(track)) { continue; } - const auto mcParticle = track.mcParticle_as(); - if (std::abs(mcParticle.pdgCode()) != pdg) { - continue; - } - if (!mcParticle.isPhysicalPrimary()) { + if (!track.has_mcParticle()) { continue; } - if (useGen) { - if (!IsInAcceptance(mcParticle)) { - continue; - } - } else { - if (!IsInAcceptance(track)) { - continue; - } - } - if (std::abs(mcParticle.pdgCode()) != pdg) { + const auto mcParticle = track.template mcParticle_as(); + registry.fill(HIST("Reconstructed/Track/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/Track/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/Track/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/Track/Pt"), track.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), track.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), track.phi()); + registry.fill(HIST("Reconstructed/Track/Pre"), track.isTrackPrefilter()); + } + + for (const auto& track : posTracks_coll) { + if (!IsInAcceptance(track)) { continue; } - if (!mcParticle.isPhysicalPrimary()) { + if (!track.has_mcParticle()) { continue; } - registry.fill(HIST("Reconstructed/Track/SigmaOTofvspt"), track.pt(), track.nSigmaElectronOuterTOF()); - registry.fill(HIST("Reconstructed/Track/SigmaITofvspt"), track.pt(), track.nSigmaElectronInnerTOF()); - registry.fill(HIST("Reconstructed/Track/SigmaRichvspt"), track.pt(), track.nSigmaElectronRich()); - registry.fill(HIST("Reconstructed/Track/outerTOFTrackLength"), track.outerTOFTrackLength()); + const auto mcParticle = track.template mcParticle_as(); + registry.fill(HIST("Reconstructed/Track/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/Track/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/Track/PhiMC"), mcParticle.phi()); registry.fill(HIST("Reconstructed/Track/Pt"), track.pt()); registry.fill(HIST("Reconstructed/Track/Eta"), track.eta()); registry.fill(HIST("Reconstructed/Track/Phi"), track.phi()); - registry.fill(HIST("Reconstructed/Track/Eta_Pt"), track.pt(), track.eta()); - // implement pid + registry.fill(HIST("Reconstructed/Track/Pre"), track.isTrackPrefilter()); + } - bool isElectronTOF = electronIDTOF(track); - bool isElectronRICH = electronIDRICH(track); + FillPairRecAll(negTracks_coll, posTracks_coll, mcParticles); + FillPairRecAll(posTracks_coll, posTracks_coll, mcParticles); + FillPairRecAll(negTracks_coll, negTracks_coll, mcParticles); - if (isElectronTOF || isElectronRICH) { - registry.fill(HIST("Reconstructed/TrackPID/SigmaOTofvspt"), track.pt(), track.nSigmaElectronOuterTOF()); - registry.fill(HIST("Reconstructed/TrackPID/SigmaITofvspt"), track.pt(), track.nSigmaElectronInnerTOF()); - registry.fill(HIST("Reconstructed/TrackPID/SigmaRichvspt"), track.pt(), track.nSigmaElectronRich()); - registry.fill(HIST("Reconstructed/TrackPID/outerTOFTrackLength"), track.outerTOFTrackLength()); - registry.fill(HIST("Reconstructed/TrackPID/Pt"), track.pt()); - registry.fill(HIST("Reconstructed/TrackPID/Eta"), track.eta()); - registry.fill(HIST("Reconstructed/TrackPID/Phi"), track.phi()); - registry.fill(HIST("Reconstructed/TrackPID/Eta_Pt"), track.pt(), track.eta()); + } // end of collision loop + } // end of processRec + + void processRecWithSmearing(MyFilteredAlice3Collision const& collisions, + MyFilteredTracksWithSmearing const& /*tracks*/, + const o2::aod::McCollisions&, + const aod::McParticles& mcParticles) + { + for (const auto& collision : collisions) { + auto negTracks_coll = negTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + FillPairRec(negTracks_coll, posTracks_coll, mcParticles); + FillPairRec(posTracks_coll, posTracks_coll, mcParticles); + FillPairRec(negTracks_coll, negTracks_coll, mcParticles); + + } // end of collision loop + } // end of processRec + + void processRecAllWithSmearing(MyFilteredAlice3Collision const& collisions, + MyFilteredTracksWithSmearing const&, + const aod::McParticles& mcParticles) + { + for (const auto& collision : collisions) { + registry.fill(HIST("Reconstructed/Event/VtxZ"), collision.posZ()); + auto negTracks_coll = negTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + for (const auto& track : negTracks_coll) { + if (!IsInAcceptance(track)) { + continue; } - } // end of track loop - // How is PID selection applied to tracks? - auto negTracks_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); - auto posTracks_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + if (!track.has_mcParticle()) { + continue; + } + const auto mcParticle = track.template mcParticle_as(); + registry.fill(HIST("Reconstructed/Track/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/Track/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/Track/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/Track/Pt"), track.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), track.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), track.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Pre"), track.isTrackPrefilter()); + } - FillPairRec(negTracks_coll, posTracks_coll, mcParticles); - FillPairRec(posTracks_coll, posTracks_coll, mcParticles); - FillPairRec(negTracks_coll, negTracks_coll, mcParticles); + for (const auto& track : posTracks_coll) { + if (!IsInAcceptance(track)) { + continue; + } + if (!track.has_mcParticle()) { + continue; + } + const auto mcParticle = track.template mcParticle_as(); + registry.fill(HIST("Reconstructed/Track/PtMC"), mcParticle.pt()); + registry.fill(HIST("Reconstructed/Track/EtaMC"), mcParticle.eta()); + registry.fill(HIST("Reconstructed/Track/PhiMC"), mcParticle.phi()); + registry.fill(HIST("Reconstructed/Track/Pt"), track.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), track.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), track.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Pre"), track.isTrackPrefilter()); + } + + FillPairRecAll(negTracks_coll, posTracks_coll, mcParticles); + FillPairRecAll(posTracks_coll, posTracks_coll, mcParticles); + FillPairRecAll(negTracks_coll, negTracks_coll, mcParticles); } // end of collision loop } // end of processRec - PROCESS_SWITCH(Alice3Dilepton, processGen, "Run for generated particle", true); + void processDummy(Alice3Collision const&) + { + } + + PROCESS_SWITCH(Alice3Dilepton, processGen, "Run for generated particle", false); PROCESS_SWITCH(Alice3Dilepton, processRec, "Run for reconstructed track", false); + PROCESS_SWITCH(Alice3Dilepton, processRecAll, "Run for reconstructed track", false); + PROCESS_SWITCH(Alice3Dilepton, processRecWithSmearing, "Run for smeared track", false); + PROCESS_SWITCH(Alice3Dilepton, processRecAllWithSmearing, "Run for smeared track", false); + PROCESS_SWITCH(Alice3Dilepton, processDummy, "Run dummy", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"alice3-dilepton"})}; + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/Tasks/alice3-prefilterdilepton.cxx b/ALICE3/Tasks/alice3-prefilterdilepton.cxx new file mode 100644 index 00000000000..4f45007107e --- /dev/null +++ b/ALICE3/Tasks/alice3-prefilterdilepton.cxx @@ -0,0 +1,422 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file alice3-dilepton.cxx +/// \author s.scheid@cern.ch, daiki.sekihata@cern.ch +/// + +#include "ALICE3/DataModel/OTFRICH.h" +#include "ALICE3/DataModel/OTFTOF.h" +#include "ALICE3/DataModel/prefilterDilepton.h" +#include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct Alice3DileptonEventCentSelection { + + Service inspdg; + + Produces eventCentSel; + Configurable minFITPart{"minFITPart", 0.f, "Minimum number of charged particles in the FIT acceptance"}; + Configurable maxFITPart{"maxFITPart", 0.f, "Maximum number of charged particles in the FIT acceptance"}; + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + registry.add("Generated/Before/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/After/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/Before/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + registry.add("Generated/After/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + } + + using MyEvents = soa::Join; + Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; + + void processGen(MyEvents::iterator const& event, o2::aod::McCollisions const&, aod::McParticles const& mcParticles) + { + if (!event.has_mcCollision()) { + eventCentSel(0); + } else { + auto mccollision = event.mcCollision(); + auto mcParticles_per_coll = mcParticles.sliceBy(perMCCollision, mccollision.globalIndex()); + int nParticlesFIT = 0; + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/Before/ParticlesEta"), mcParticle.eta()); + if ((2.2 < mcParticle.eta() && mcParticle.eta() < 5.0) || (-3.4 < mcParticle.eta() && mcParticle.eta() < -2.3)) { + nParticlesFIT++; + } + } + } + } + } // end of mc particle loop + registry.fill(HIST("Generated/Before/ParticlesFit"), nParticlesFIT); + if (nParticlesFIT > minFITPart && nParticlesFIT < maxFITPart) { + registry.fill(HIST("Generated/After/ParticlesFit"), nParticlesFIT); + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/After/ParticlesEta"), mcParticle.eta()); + } + } + } + } // end of mc particle loop + eventCentSel(1); + } else { + eventCentSel(0); + } + } // if mc collision loop + } // end of process + + void processDummy(MyEvents::iterator const&) + { + eventCentSel(1); + } + + PROCESS_SWITCH(Alice3DileptonEventCentSelection, processGen, "Select centrality", false); + PROCESS_SWITCH(Alice3DileptonEventCentSelection, processDummy, "Dummy", true); +}; + +struct Alice3DileptonPrefilter { + + Produces pfb_derived; + + SliceCache cache_mc; + SliceCache cache_rec; + + Service inspdg; + + Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; + Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; + Configurable etaMin{"etaMin", -5.f, "Lower limit in eta"}; + Configurable etaMax{"etaMax", 5.f, "Upper limit in eta"}; + Configurable maxMass{"maxMass", 5.f, "Upper limit in mass"}; + Configurable maxOp{"maxOp", 5.f, "Upper limit in opening angle"}; + Configurable nSigmaEleCutOuterTOF{"nSigmaEleCutOuterTOF", 3., "Electron inclusion in outer TOF"}; + Configurable nSigmaEleCutInnerTOF{"nSigmaEleCutInnerTOF", 3., "Electron inclusion in inner TOF"}; + Configurable nSigmaPionCutOuterTOF{"nSigmaPionCutOuterTOF", 3., "Pion exclusion in outer TOF"}; + Configurable nSigmaPionCutInnerTOF{"nSigmaPionCutInnerTOF", 3., "Pion exclusion in inner TOF"}; + Configurable nSigmaElectronRich{"nSigmaElectronRich", 3., "Electron inclusion RICH"}; + Configurable nSigmaPionRich{"nSigmaPionRich", 3., "Pion exclusion RICH"}; + + std::unordered_map map_pfb; // map track.globalIndex -> prefilter bit + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + using MyTracksWithSmearings = soa::Join; + using MyTracksWithSmearing = MyTracksWithSmearings::iterator; + using MyTracksMCs = soa::Join; + using MyTracksMC = MyTracksMCs::iterator; + using DileptonCollisions = soa::Join; + using DileptonCollision = DileptonCollisions::iterator; + + Preslice perCollision = aod::track::collisionId; + Partition posTracks = o2::aod::track::signed1Pt > 0.f; + Partition negTracks = o2::aod::track::signed1Pt < 0.f; + Partition posTracksWithSmearing = o2::aod::track::signed1Pt > 0.f; + Partition negTracksWithSmearing = o2::aod::track::signed1Pt < 0.f; + + void init(InitContext&) + { + const AxisSpec axisM{500, 0, 5, "#it{m}_{ll} (GeV/#it{c}^{2})"}; + const AxisSpec axisPt{1000, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisSigmaEl{200, -10, 10, "n#sigma_{El}"}; + const AxisSpec axisTrackLengthOuterTOF{300, 0., 300., "Track length (cm)"}; + const AxisSpec axisEta{1000, -5, 5, "#it{#eta}"}; + const AxisSpec axisPhi{360, 0, TMath::TwoPi(), "#it{#varphi} (rad.)"}; + const AxisSpec axisProdx{2000, -100, 100, "Prod. Vertex X (cm)"}; + const AxisSpec axisPrody{2000, -100, 100, "Prod. Vertex Y (cm)"}; + const AxisSpec axisProdz{2000, -100, 100, "Prod. Vertex Z (cm)"}; + + registry.add("Reconstructed/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("ReconstructedFiltered/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("Reconstructed/Track/Pt", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/Eta", "Track Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/Phi", "Track Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/Eta_Pt", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + + if (doprocessPreFilter) { + registry.add("Reconstructed/Track/SigmaOTofvspt", "Track #sigma oTOF", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/SigmaITofvspt", "Track #sigma iTOF", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/SigmaRichvspt", "Track #sigma RICH", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/outerTOFTrackLength", "Track length outer TOF", kTH1F, {axisTrackLengthOuterTOF}); + } + } + + template + void PreFilter(TTracks const& posTracks_coll, TTracks const& negTracks_coll) + { + for (const auto& pos : posTracks_coll) { + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), pos.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.ptSmeared(), pos.etaSmeared()); + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if ((std::abs(pos.nSigmaElectronRich()) < nSigmaElectronRich && nSigmaPionRich < std::abs(pos.nSigmaPionRich())) || (std::abs(pos.nSigmaElectronOuterTOF()) < nSigmaEleCutOuterTOF && nSigmaPionCutOuterTOF < std::abs(pos.nSigmaPionOuterTOF())) || (std::abs(pos.nSigmaElectronInnerTOF()) < nSigmaEleCutInnerTOF && nSigmaPionCutInnerTOF < std::abs(pos.nSigmaPionInnerTOF()))) { + registry.fill(HIST("Reconstructed/Track/SigmaOTofvspt"), pos.pt(), pos.nSigmaElectronOuterTOF()); + registry.fill(HIST("Reconstructed/Track/SigmaITofvspt"), pos.pt(), pos.nSigmaElectronInnerTOF()); + registry.fill(HIST("Reconstructed/Track/SigmaRichvspt"), pos.pt(), pos.nSigmaElectronRich()); + registry.fill(HIST("Reconstructed/Track/outerTOFTrackLength"), pos.outerTOFTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), pos.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.pt(), pos.eta()); + } + } + } + for (const auto& ele : negTracks_coll) { + if constexpr (isWithSmearing) { + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), ele.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.ptSmeared(), ele.etaSmeared()); + } else { + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + if ((std::abs(ele.nSigmaElectronRich()) < nSigmaElectronRich && nSigmaPionRich < std::abs(ele.nSigmaPionRich())) || (std::abs(ele.nSigmaElectronOuterTOF()) < nSigmaEleCutOuterTOF && nSigmaPionCutOuterTOF < std::abs(ele.nSigmaPionOuterTOF())) || (std::abs(ele.nSigmaElectronInnerTOF()) < nSigmaEleCutInnerTOF && nSigmaPionCutInnerTOF < std::abs(ele.nSigmaPionInnerTOF()))) { + registry.fill(HIST("Reconstructed/Track/SigmaOTofvspt"), ele.pt(), ele.nSigmaElectronOuterTOF()); + registry.fill(HIST("Reconstructed/Track/SigmaITofvspt"), ele.pt(), ele.nSigmaElectronInnerTOF()); + registry.fill(HIST("Reconstructed/Track/SigmaRichvspt"), ele.pt(), ele.nSigmaElectronRich()); + registry.fill(HIST("Reconstructed/Track/outerTOFTrackLength"), ele.outerTOFTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), ele.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.pt(), ele.eta()); + } + } + } + + for (const auto& [pos, ele] : combinations(CombinationsFullIndexPolicy(posTracks_coll, negTracks_coll))) { // ULS + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + } + if constexpr (isWithSmearing) { + + ROOT::Math::PtEtaPhiMVector v1(pos.ptSmeared(), pos.etaSmeared(), pos.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.ptSmeared(), ele.etaSmeared(), ele.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + + } else { + + if ((std::abs(pos.nSigmaElectronRich()) < nSigmaElectronRich && nSigmaPionRich < std::abs(pos.nSigmaPionRich())) || (std::abs(pos.nSigmaElectronOuterTOF()) < nSigmaEleCutOuterTOF && nSigmaPionCutOuterTOF < std::abs(pos.nSigmaPionOuterTOF())) || (std::abs(pos.nSigmaElectronInnerTOF()) < nSigmaEleCutInnerTOF && nSigmaPionCutInnerTOF < std::abs(pos.nSigmaPionInnerTOF()))) { + if ((std::abs(ele.nSigmaElectronRich()) < nSigmaElectronRich && nSigmaPionRich < std::abs(ele.nSigmaPionRich())) || (std::abs(ele.nSigmaElectronOuterTOF()) < nSigmaEleCutOuterTOF && nSigmaPionCutOuterTOF < std::abs(ele.nSigmaPionOuterTOF())) || (std::abs(ele.nSigmaElectronInnerTOF()) < nSigmaEleCutInnerTOF && nSigmaPionCutInnerTOF < std::abs(ele.nSigmaPionInnerTOF()))) { + + ROOT::Math::PtEtaPhiMVector v1(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.pt(), ele.eta(), ele.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + } + } + } + } // combination + } + + void processPreFilter(DileptonCollisions const& collisions, MyTracksMCs const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processPreFilterWithSmearing(DileptonCollisions const& collisions, MyTracksWithSmearings const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processDummy(MyTracksMCs const& tracks) + { + for (int i = 0; i < tracks.size(); i++) { + pfb_derived(0); + } + } + + PROCESS_SWITCH(Alice3DileptonPrefilter, processPreFilter, "Run prefilter", false); + PROCESS_SWITCH(Alice3DileptonPrefilter, processPreFilterWithSmearing, "Run prefilter with smearing", false); + PROCESS_SWITCH(Alice3DileptonPrefilter, processDummy, "Dummy", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/ALICE3/Tasks/alice3-prefilterdimuon.cxx b/ALICE3/Tasks/alice3-prefilterdimuon.cxx new file mode 100644 index 00000000000..c797296b4a6 --- /dev/null +++ b/ALICE3/Tasks/alice3-prefilterdimuon.cxx @@ -0,0 +1,418 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file alice3-dimuon.cxx +/// \author s.scheid@cern.ch, daiki.sekihata@cern.ch +/// + +#include "ALICE3/DataModel/OTFMID.h" +#include "ALICE3/DataModel/prefilterDilepton.h" +#include "ALICE3/DataModel/tracksAlice3.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::soa; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct Alice3DimuonEventCentSelection { + + Service inspdg; + + Produces eventCentSel; + Configurable minFITPart{"minFITPart", 0.f, "Minimum number of charged particles in the FIT acceptance"}; + Configurable maxFITPart{"maxFITPart", 0.f, "Maximum number of charged particles in the FIT acceptance"}; + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext&) + { + registry.add("Generated/Before/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/After/ParticlesFit", "Charged Particles in Fit acceptance per event", kTH1F, {{15000, 0, 15000}}); + registry.add("Generated/Before/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + registry.add("Generated/After/ParticlesEta", "Charged Particles per event", kTH1F, {{1200, -6, 6}}); + } + + using MyEvents = soa::Join; + Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; + + void processGen(MyEvents::iterator const& event, o2::aod::McCollisions const&, aod::McParticles const& mcParticles) + { + if (!event.has_mcCollision()) { + eventCentSel(0); + } else { + auto mccollision = event.mcCollision(); + auto mcParticles_per_coll = mcParticles.sliceBy(perMCCollision, mccollision.globalIndex()); + int nParticlesFIT = 0; + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/Before/ParticlesEta"), mcParticle.eta()); + if ((2.2 < mcParticle.eta() && mcParticle.eta() < 5.0) || (-3.4 < mcParticle.eta() && mcParticle.eta() < -2.3)) { + nParticlesFIT++; + } + } + } + } + } // end of mc particle loop + registry.fill(HIST("Generated/Before/ParticlesFit"), nParticlesFIT); + if (nParticlesFIT > minFITPart && nParticlesFIT < maxFITPart) { + registry.fill(HIST("Generated/After/ParticlesFit"), nParticlesFIT); + for (const auto& mcParticle : mcParticles_per_coll) { + if (mcParticle.isPhysicalPrimary()) { + auto pdgParticle = inspdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle) { + float charge = pdgParticle->Charge() / 3.f; // Charge in units of |e| + if (std::abs(charge) >= 1.) { + registry.fill(HIST("Generated/After/ParticlesEta"), mcParticle.eta()); + } + } + } + } // end of mc particle loop + eventCentSel(1); + } else { + eventCentSel(0); + } + } // if mc collision loop + } // end of process + + void processDummy(MyEvents::iterator const&) + { + eventCentSel(1); + } + + PROCESS_SWITCH(Alice3DimuonEventCentSelection, processGen, "Select centrality", false); + PROCESS_SWITCH(Alice3DimuonEventCentSelection, processDummy, "Dummy", true); +}; + +struct Alice3DimuonPrefilter { + + Produces pfb_derived; + + SliceCache cache_mc; + SliceCache cache_rec; + + Service inspdg; + + Configurable ptMin{"ptMin", 0.f, "Lower limit in pT"}; + Configurable ptMax{"ptMax", 5.f, "Upper limit in pT"}; + Configurable etaMin{"etaMin", -5.f, "Lower limit in eta"}; + Configurable etaMax{"etaMax", 5.f, "Upper limit in eta"}; + Configurable maxMass{"maxMass", 5.f, "Upper limit in mass"}; + Configurable maxOp{"maxOp", 5.f, "Upper limit in opening angle"}; + Configurable isMuMID{"isMuMID", true, "Muon candidate reaching MID"}; + Configurable nBinsP{"nBinsP", 80, "number of bins in momentum/pT depending on doSeparationVsPt"}; + Configurable nBinsEta{"nBinsEta", 400, "number of bins plot relative eta error"}; + std::unordered_map map_pfb; // map track.globalIndex -> prefilter bit + + HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + using MyTracksWithSmearings = soa::Join; + using MyTracksWithSmearing = MyTracksWithSmearings::iterator; + using MyTracksMCs = soa::Join; + using MyTracksMC = MyTracksMCs::iterator; + using DileptonCollisions = soa::Join; + using DileptonCollision = DileptonCollisions::iterator; + + Preslice perCollision = aod::track::collisionId; + Partition posTracks = o2::aod::track::signed1Pt > 0.f; + Partition negTracks = o2::aod::track::signed1Pt < 0.f; + Partition posTracksWithSmearing = o2::aod::track::signed1Pt > 0.f; + Partition negTracksWithSmearing = o2::aod::track::signed1Pt < 0.f; + + void init(InitContext&) + { + const AxisSpec axisM{500, 0, 5, "#it{m}_{ll} (GeV/#it{c}^{2})"}; + // const AxisSpec axisPt{1000, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPt{nBinsP, 0.0f, +10.0f, "#it{p}_{T} (GeV/#it{c})"}; + // const AxisSpec axisSigmaEl{200, -10, 10, "n#sigma_{El}"}; + const AxisSpec axisTrackLengthMID{310, 0., 310., "Track length (cm)"}; + // const AxisSpec axisEta{1000, -5, 5, "#it{#eta}"}; + const AxisSpec axisEta{static_cast(nBinsEta), -2.0f, +2.0f, "#eta"}; + const AxisSpec axisPhi{360, 0, TMath::TwoPi(), "#it{#varphi} (rad.)"}; + const AxisSpec axisProdx{2000, -100, 100, "Prod. Vertex X (cm)"}; + const AxisSpec axisPrody{2000, -100, 100, "Prod. Vertex Y (cm)"}; + const AxisSpec axisProdz{2000, -100, 100, "Prod. Vertex Z (cm)"}; + + registry.add("Reconstructed/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("ReconstructedFiltered/Pair/ULS/Mass_Pt", "Pair Mass vs. Pt", kTH2F, {axisM, axisPt}, true); + registry.add("Reconstructed/Track/Pt", "Track Pt", kTH1F, {axisPt}); + registry.add("Reconstructed/Track/Eta_P", "Track Eta vs Track P", kTH2F, {axisPt, axisEta}); + registry.add("Reconstructed/Track/Eta", "Track Eta", kTH1F, {axisEta}); + registry.add("Reconstructed/Track/Phi", "Track Phi", kTH1F, {axisPhi}); + registry.add("Reconstructed/Track/Eta_Pt", "Eta vs. Pt", kTH2F, {axisPt, axisEta}, true); + + if (doprocessPreFilter) { + // registry.add("Reconstructed/Track/SigmaOTofvspt", "Track #sigma oTOF", kTH2F, {axisPt, axisSigmaEl}); + // registry.add("Reconstructed/Track/SigmaITofvspt", "Track #sigma iTOF", kTH2F, {axisPt, axisSigmaEl}); + // registry.add("Reconstructed/Track/SigmaRichvspt", "Track #sigma RICH", kTH2F, {axisPt, axisSigmaEl}); + registry.add("Reconstructed/Track/midTrackLength", "Track length MID", kTH1F, {axisTrackLengthMID}); + } + } + + template + void PreFilter(TTracks const& posTracks_coll, TTracks const& negTracks_coll) + { + for (const auto& pos : posTracks_coll) { + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), pos.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.etaSmeared()); + // registry.fill(HIST("Reconstructed/Track/Eta_P"), pos.momentumSmeared(),pos.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.ptSmeared(), pos.etaSmeared()); + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if (pos.midCandidate()) { + registry.fill(HIST("Reconstructed/Track/midTrackLength"), pos.midTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), pos.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), pos.eta()); + registry.fill(HIST("Reconstructed/Track/Eta_P"), pos.p(), pos.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), pos.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), pos.pt(), pos.eta()); + } + } + } + for (const auto& ele : negTracks_coll) { + if constexpr (isWithSmearing) { + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + registry.fill(HIST("Reconstructed/Track/Pt"), ele.ptSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.etaSmeared()); + // registry.fill(HIST("Reconstructed/Track/Eta_P"), ele.pSmeared(),ele.etaSmeared()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phiSmeared()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.ptSmeared(), ele.etaSmeared()); + } else { + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + if (ele.midCandidate()) { + registry.fill(HIST("Reconstructed/Track/midTrackLength"), ele.midTrackLength()); + registry.fill(HIST("Reconstructed/Track/Pt"), ele.pt()); + registry.fill(HIST("Reconstructed/Track/Eta"), ele.eta()); + registry.fill(HIST("Reconstructed/Track/Eta_P"), ele.p(), ele.eta()); + registry.fill(HIST("Reconstructed/Track/Phi"), ele.phi()); + registry.fill(HIST("Reconstructed/Track/Eta_Pt"), ele.pt(), ele.eta()); + } + } + } + + for (const auto& [pos, ele] : combinations(CombinationsFullIndexPolicy(posTracks_coll, negTracks_coll))) { // ULS + if constexpr (isWithSmearing) { + if (!pos.isReconstructed() || !pos.selected()) { + continue; + } + if (pos.etaSmeared() < etaMin || etaMax < pos.etaSmeared()) { + continue; + } + if (pos.ptSmeared() < ptMin || ptMax < pos.ptSmeared()) { + continue; + } + if (!ele.isReconstructed() || !ele.selected()) { + continue; + } + if (ele.etaSmeared() < etaMin || etaMax < ele.etaSmeared()) { + continue; + } + if (ele.ptSmeared() < ptMin || ptMax < ele.ptSmeared()) { + continue; + } + } else { + if (!pos.isReconstructed()) { + continue; + } + if (pos.eta() < etaMin || etaMax < pos.eta()) { + continue; + } + if (pos.pt() < ptMin || ptMax < pos.pt()) { + continue; + } + if (!ele.isReconstructed()) { + continue; + } + if (ele.eta() < etaMin || etaMax < ele.eta()) { + continue; + } + if (ele.pt() < ptMin || ptMax < ele.pt()) { + continue; + } + } + if constexpr (isWithSmearing) { + + ROOT::Math::PtEtaPhiMVector v1(pos.ptSmeared(), pos.etaSmeared(), pos.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.ptSmeared(), ele.etaSmeared(), ele.phiSmeared(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + + } else { + + if (pos.midCandidate()) { + if (ele.midCandidate()) { + + ROOT::Math::PtEtaPhiMVector v1(pos.pt(), pos.eta(), pos.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(ele.pt(), ele.eta(), ele.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float angle = RecoDecay::constrainAngle(ROOT::Math::VectorUtil::Angle(v1, v2), -M_PI); + // o2::math_utils::bringToPMPi(angle); + + registry.fill(HIST("Reconstructed/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + + if (v12.M() < maxMass && std::abs(angle) < maxOp) { + map_pfb[pos.globalIndex()] = 1; + map_pfb[ele.globalIndex()] = 1; + registry.fill(HIST("ReconstructedFiltered/Pair/ULS/Mass_Pt"), v12.M(), v12.Pt()); + } + } + } + } + } // combination + } + + void processPreFilter(DileptonCollisions const& collisions, MyTracksMCs const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processPreFilterWithSmearing(DileptonCollisions const& collisions, MyTracksWithSmearings const& tracks) + { + for (const auto& track : tracks) { + map_pfb[track.globalIndex()] = 0; + } // end of track loop + + for (const auto& collision : collisions) { + auto negTracks_coll = negTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + auto posTracks_coll = posTracksWithSmearing->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache_rec); + + if (collision.isEventCentSelected() == 0) { + for (const auto& pos : posTracks_coll) { + map_pfb[pos.globalIndex()] = 0; + } + for (const auto& neg : negTracks_coll) { + map_pfb[neg.globalIndex()] = 0; + } + continue; + } + PreFilter(posTracks_coll, negTracks_coll); + } + for (const auto& track : tracks) { + pfb_derived(map_pfb[track.globalIndex()]); + } // end of track loop + map_pfb.clear(); + } + + void processDummy(MyTracksMCs const& tracks) + { + for (int i = 0; i < tracks.size(); i++) { + pfb_derived(0); + } + } + + PROCESS_SWITCH(Alice3DimuonPrefilter, processPreFilter, "Run prefilter", false); + PROCESS_SWITCH(Alice3DimuonPrefilter, processPreFilterWithSmearing, "Run prefilter with smearing", false); + PROCESS_SWITCH(Alice3DimuonPrefilter, processDummy, "Dummy", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/ALICE3/Tasks/alice3-qa-multiplicity.cxx b/ALICE3/Tasks/alice3-qa-multiplicity.cxx index a5440ff8d53..5e81cd8fd5a 100644 --- a/ALICE3/Tasks/alice3-qa-multiplicity.cxx +++ b/ALICE3/Tasks/alice3-qa-multiplicity.cxx @@ -41,7 +41,7 @@ struct ALICE3MultTask { void init(InitContext&) { - const AxisSpec axisMult{MaxMult.value > 10000.f ? 10000 : (int)MaxMult, 0, MaxMult, "Reconstructed tracks"}; + const AxisSpec axisMult{MaxMult.value > 10000.f ? 10000 : static_cast(MaxMult), 0, MaxMult, "Reconstructed tracks"}; TString tit = Form("%.3f < #it{#eta} < %.3f", MinEta.value, MaxEta.value); histos.add("multiplicity/numberOfTracksNoCut", tit, kTH1D, {axisMult}); histos.add("multiplicity/numberOfTracks", tit, kTH1D, {axisMult}); diff --git a/ALICE3/Tasks/alice3-qa-singleparticle.cxx b/ALICE3/Tasks/alice3-qa-singleparticle.cxx index 2a7fdc448ee..4fe232bf07f 100644 --- a/ALICE3/Tasks/alice3-qa-singleparticle.cxx +++ b/ALICE3/Tasks/alice3-qa-singleparticle.cxx @@ -10,11 +10,12 @@ // or submit itself to any jurisdiction. /// -/// \file qa-singleparticle.cxx +/// \file alice3-qa-singleparticle.cxx /// \author Nicolò Jacazio nicolo.jacazio@cern.ch /// \brief Task to monitor the single particle QA, at the particle and track level, showing the tracked and the origin of particles /// +#include #include #include #include @@ -27,7 +28,6 @@ #include #include -#include #include #include #include @@ -37,7 +37,9 @@ #include #include +#include #include +#include #include using namespace o2; @@ -45,26 +47,55 @@ using namespace o2::framework; using namespace o2::framework::expressions; struct Alice3SingleParticle { - Service pdg; - Configurable PDG{"PDG", 2212, "PDG code of the particle of interest"}; - Configurable IsStable{"IsStable", 0, "Flag to check stable particles"}; + Service pdg{}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable ptBins{"pt-bins", 500, "Number of pT bins"}; - Configurable ptMin{"pt-min", 0.f, "Lower limit in pT"}; - Configurable ptMax{"pt-max", 10.f, "Upper limit in pT"}; - Configurable etaBins{"eta-bins", 500, "Number of eta bins"}; - Configurable etaMin{"eta-min", -3.f, "Lower limit in eta"}; - Configurable etaMax{"eta-max", 3.f, "Upper limit in eta"}; - Configurable yMin{"y-min", -3.f, "Lower limit in y"}; - Configurable yMax{"y-max", 3.f, "Upper limit in y"}; - Configurable prodBins{"prod-bins", 100, "Number of production vertex bins"}; - Configurable prodMin{"prod-min", -1.f, "Lower limit in production vertex"}; - Configurable prodMax{"prod-max", 1.f, "Upper limit in production vertex"}; - Configurable prodBinsZ{"prod-bins-z", 100, "Number of production vertex bins"}; - Configurable prodMinZ{"prod-min-z", -10.f, "Lower limit in production vertex along Z"}; - Configurable prodMaxZ{"prod-max-z", 10.f, "Upper limit in production vertex along Z"}; - Configurable charge{"charge", 1.f, "Particle charge to scale the reconstructed momentum"}; - Configurable doPrint{"doPrint", false, "Flag to print debug messages"}; + Configurable pdgCode{"pdgCode", 2212, "PDG code of the particle of interest"}; + + struct : ConfigurableGroup { + std::string prefix = "settings"; + Configurable isStable{"isStable", 0, "Flag to check stable particles"}; + Configurable charge{"charge", 1.f, "Particle charge to scale the reconstructed momentum"}; + Configurable nchEtaMax{"nchEtaMax", 0.5f, "|eta| acceptance of the Nch estimator (use the Ditto tune mActivityEtaMax)"}; + Configurable doPrint{"doPrint", false, "Flag to print debug messages"}; + } settings; + + // The pT, eta, y and production-vertex-z axes also define the selection of the particles of interest + struct : ConfigurableGroup { + std::string prefix = "settingsAxes"; + ConfigurableAxis axisMomentum{"axisMomentum", {500, 0.f, 10.f}, "Binning for p and pT"}; + ConfigurableAxis axisMomentumComponent{"axisMomentumComponent", {500, -10.f, 10.f}, "Binning for px, py and pz"}; + ConfigurableAxis axisEnergy{"axisEnergy", {500, 0.f, 1000.f}, "Binning for the energy"}; + ConfigurableAxis axisEta{"axisEta", {500, -3.f, 3.f}, "Binning for eta"}; + ConfigurableAxis axisY{"axisY", {200, -3.f, 3.f}, "Binning for rapidity"}; + ConfigurableAxis axisVtxXY{"axisVtxXY", {100, -1.f, 1.f}, "Binning for the event vertex in X and Y"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {100, -20.f, 20.f}, "Binning for the event vertex in Z"}; + ConfigurableAxis axisProdXY{"axisProdXY", {100, -1.f, 1.f}, "Binning for the production vertex in X and Y"}; + ConfigurableAxis axisProdZ{"axisProdZ", {100, -10.f, 10.f}, "Binning for the production vertex in Z"}; + ConfigurableAxis axisProdRadius{"axisProdRadius", {100, 0.f, 2.f}, "Binning for the production vertex radius"}; + ConfigurableAxis axisNch{"axisNch", {150, 0.f, 150.f}, "Charged-particle multiplicity"}; + ConfigurableAxis axisNSignal{"axisNSignal", {20, -0.5f, 19.5f}, "Number of selected particles of interest per collision"}; + ConfigurableAxis distanceAxis{"distanceAxis", {1000, 0, 1e-10}, "Distance (cm)"}; + } settingsAxes; + + std::vector particlesOfInterest; // global indices of the MC particles selected in genericProcessParticles, per DF + + // Selection range [min, max) of the particles of interest, taken from an axis so that every selected particle is inside its histograms + struct Range { + Range() = default; + Range(double min_, double max_, int64_t bins_) : min(min_), max(max_), bins(bins_) {} + double min = 0.; + double max = 0.; + int64_t bins = 0; + int getBins() const { return static_cast(bins); } + [[nodiscard]] bool contains(double value) const { return value >= min && value < max; } + }; + Range ptRange, etaRange, yRange, prodXYrange, prodZRange; + + static Range getRange(const ConfigurableAxis& axis) + { + const AxisSpec spec{axis}; + return Range(spec.binEdges.front(), spec.binEdges.back(), spec.getNbins()); + } void init(InitContext&) { @@ -88,113 +119,137 @@ struct Alice3SingleParticle { pdg->AddParticle("triton", "triton", 2.8089218, kTRUE, 0.0, 3, "Nucleus", 1000010030); pdg->AddAntiParticle("anti-triton", -1000010030); - pdg->AddParticle("helium3", "helium3", 2.80839160743, kTRUE, 0.0, 6, "Nucleus", 1000020030); + pdg->AddParticle("helium3", "helium3", o2::constants::physics::MassHelium3, kTRUE, 0.0, 6, "Nucleus", 1000020030); pdg->AddAntiParticle("anti-helium3", -1000020030); - pdg->AddParticle("helium4", "helium4", 2.80839160743, kTRUE, 0.0, 6, "Nucleus", 1000020040); + pdg->AddParticle("helium4", "helium4", o2::constants::physics::MassAlpha, kTRUE, 0.0, 6, "Nucleus", 1000020040); pdg->AddAntiParticle("anti-helium4", -1000020040); - const TString tit = Form("%i", PDG.value); - const AxisSpec axisVx{100, -1, 1, "Vtx_{x}"}; - const AxisSpec axisVy{100, -1, 1, "Vtx_{y}"}; - const AxisSpec axisVz{100, -20, 20, "Vtx_{z}"}; + ptRange = getRange(settingsAxes.axisMomentum); + etaRange = getRange(settingsAxes.axisEta); + yRange = getRange(settingsAxes.axisY); + prodXYrange = getRange(settingsAxes.axisProdXY); + prodZRange = getRange(settingsAxes.axisProdZ); + LOG(info) << "Selecting particles of interest in pT [" << ptRange.min << ", " << ptRange.max << "), eta [" << etaRange.min << ", " << etaRange.max + << "), y [" << yRange.min << ", " << yRange.max << "), production z [" << prodZRange.min << ", " << prodZRange.max << ")"; + + const TString titlePdgCode = Form("%i", pdgCode.value); const AxisSpec axisPDGs{100, 0.f, 100.f, "PDG code"}; const AxisSpec axisCharge{21, -10.5f, 10.5f, "Charge (e/3)"}; - const AxisSpec axisP{ptBins, ptMax, ptMax, "#it{p} (GeV/#it{c})"}; - const AxisSpec axisPt{ptBins, ptMin, ptMax, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisPtMC{ptBins, ptMin, ptMax, "#it{p}_{T}^{MC} (GeV/#it{c})"}; - const AxisSpec axisPx{ptBins, -ptMax, ptMax, "#it{p}_{x} (GeV/#it{c})"}; - const AxisSpec axisPy{ptBins, -ptMax, ptMax, "#it{p}_{y} (GeV/#it{c})"}; - const AxisSpec axisPz{ptBins, -ptMax, ptMax, "#it{p}_{z} (GeV/#it{c})"}; - const AxisSpec axisEta{etaBins, etaMin, etaMax, "#it{#eta}"}; - const AxisSpec axisY{etaBins, yMin, yMax, "#it{y}"}; - const AxisSpec axisE{etaBins, 0, 1000, "E"}; - const AxisSpec axisProdx{prodBins, prodMin, prodMax, "Prod. Vertex X (cm)"}; - const AxisSpec axisPrody{prodBins, prodMin, prodMax, "Prod. Vertex Y (cm)"}; - const AxisSpec axisProdz{prodBinsZ, prodMinZ, prodMaxZ, "Prod. Vertex Z (cm)"}; - const AxisSpec axisProdRadius{prodBins, 0., 2. * prodMax, "Prod. Vertex Radius (cm)"}; + const AxisSpec axisP{settingsAxes.axisMomentum, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPt{settingsAxes.axisMomentum, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPtMC{settingsAxes.axisMomentum, "#it{p}_{T}^{MC} (GeV/#it{c})"}; + const AxisSpec axisPx{settingsAxes.axisMomentumComponent, "#it{p}_{x} (GeV/#it{c})"}; + const AxisSpec axisPy{settingsAxes.axisMomentumComponent, "#it{p}_{y} (GeV/#it{c})"}; + const AxisSpec axisPz{settingsAxes.axisMomentumComponent, "#it{p}_{z} (GeV/#it{c})"}; + const AxisSpec axisEta{settingsAxes.axisEta, "#it{#eta}"}; + const AxisSpec axisY{settingsAxes.axisY, "#it{y}"}; + const AxisSpec axisE{etaRange.getBins(), 0, 1000, "E"}; + const AxisSpec axisProdx{settingsAxes.axisProdXY, "Prod. Vertex X (cm)"}; + const AxisSpec axisPrody{settingsAxes.axisProdXY, "Prod. Vertex Y (cm)"}; + const AxisSpec axisProdz{settingsAxes.axisProdZ, "Prod. Vertex Z (cm)"}; + const AxisSpec axisProdRadius{prodXYrange.getBins(), 0., 2. * prodXYrange.max, "Prod. Vertex Radius (cm)"}; + const AxisSpec axisDistx{settingsAxes.distanceAxis, "Distance X (fm)"}; + const AxisSpec axisDisty{settingsAxes.distanceAxis, "Distance Y (fm)"}; + const AxisSpec axisDistz{settingsAxes.distanceAxis, "Distance Z (fm)"}; + const AxisSpec axisProdDistance{settingsAxes.distanceAxis, "Distance Radius (fm)"}; + const AxisSpec axisVx{settingsAxes.axisVtxXY, "Vertex X (cm)"}; + const AxisSpec axisVy{settingsAxes.axisVtxXY, "Vertex Y (cm)"}; + const AxisSpec axisVz{settingsAxes.axisVtxZ, "Vertex Z (cm)"}; + + histos.add("AnalysedParticlePdgCode", "Analysed particle PDG code", kTH1D, {{1, -0.5, 0.5}}); + histos.get(HIST("AnalysedParticlePdgCode"))->SetBinContent(1, pdgCode); + + const auto* particle = pdg->GetParticle(pdgCode); + histos.add("AnalysedParticleMass", "Analysed particle mass", kTH1D, {{1, -0.5, 0.5}}); + histos.get(HIST("AnalysedParticleMass"))->SetBinContent(1, particle ? particle->Mass() : -1.); histos.add("event/VtxX", "Vertex X", kTH1D, {axisVx}); histos.add("event/VtxY", "Vertex Y", kTH1D, {axisVy}); histos.add("event/VtxZ", "Vertex Z", kTH1D, {axisVz}); + histos.add("event/Nch", "Events;N_{ch};Events", kTH1D, {settingsAxes.axisNch}); + histos.add("particle/NSignalVsNch", "Particles of interest per event " + titlePdgCode + ";N_{ch};N_{signal}", kTH2D, {settingsAxes.axisNch, settingsAxes.axisNSignal}); histos.add("particle/PDGs", "Particle PDGs", kTH2D, {axisPDGs, axisCharge}); histos.add("particle/PDGsPrimaries", "Particle PDGs of Primaries", kTH2D, {axisPDGs, axisCharge}); histos.add("particle/PDGsSecondaries", "Particle PDGs of Secondaries", kTH2D, {axisPDGs, axisCharge}); - auto h = histos.add("particle/producedProcess", "Particle process of production " + tit, kTH1D, {{kMaxMCProcess, -0.5, -0.5 + kMaxMCProcess, "getProcess"}}); + auto h = histos.add("particle/producedProcess", "Particle process of production " + titlePdgCode, kTH1D, {{kMaxMCProcess, -0.5, -0.5 + kMaxMCProcess, "getProcess"}}); for (int i = 0; i < kMaxMCProcess; i++) { h->GetXaxis()->SetBinLabel(i + 1, TMCProcessName[i]); } - histos.add("particle/Pt", "Particle Pt " + tit, kTH1D, {axisPt}); - histos.add("particle/P", "Particle P " + tit, kTH1D, {axisP}); - histos.add("particle/primariesPt", "Particle Pt (primary) " + tit, kTH1D, {axisPt}); - histos.add("particle/primariesP", "Particle P (primary) " + tit, kTH1D, {axisP}); - histos.add("particle/secondariesPt", "Particle Pt (secondary) " + tit, kTH1D, {axisPt}); - histos.add("particle/secondariesP", "Particle P (secondary) " + tit, kTH1D, {axisP}); - histos.add("particle/prodVx", "Particle Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/prodVy", "Particle Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/prodVz", "Particle Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/prodRadius", "Particle Prod. Vertex Radius " + tit, kTH1D, {axisProdRadius}); - histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + tit, kTH2D, {axisProdx, axisPrody}); - histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + tit, kTH2D, {axisPt, axisProdx}); - histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + tit, kTH2D, {axisPt, axisPrody}); - histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + tit, kTH2D, {axisPt, axisProdz}); - histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius " + tit, kTH2D, {axisEta, axisProdRadius}); - histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/Eta", "Particle Eta " + tit, kTH1D, {axisEta}); - histos.add("particle/primariesEta", "Particle Eta (primary) " + tit, kTH1D, {axisEta}); - histos.add("particle/secondariesEta", "Particle Eta (secondary) " + tit, kTH1D, {axisEta}); - histos.add("particle/Y", "Particle Y " + tit, kTH1D, {axisY}); - histos.add("particle/primariesY", "Particle Y (primary)" + tit, kTH1D, {axisY}); - histos.add("particle/secondariesY", "Particle Y (secondary)" + tit, kTH1D, {axisY}); - histos.add("particle/EvsPz", "Particle E vs Pz " + tit, kTH2D, {axisE, axisPz}); - histos.add("particle/YvzPz", "Particle Y vs Pz " + tit, kTH2D, {axisY, axisPz}); - histos.add("particle/EtavzPz", "Particle Eta vs Pz " + tit, kTH2D, {axisEta, axisPz}); - histos.add("particle/PtvzPz", "Particle Pt vs Pz " + tit, kTH2D, {axisPt, axisPz}); - histos.add("particle/PvzPz", "Particle P vs Pz " + tit, kTH2D, {axisP, axisPz}); - histos.add("particle/Px", "Particle Px " + tit, kTH1D, {axisPx}); - histos.add("particle/Py", "Particle Py " + tit, kTH1D, {axisPy}); - histos.add("particle/Pz", "Particle Pz " + tit, kTH1D, {axisPz}); - histos.add("particle/decayDistanceX", "Particle decay distance " + tit, kTH1D, {axisProdx}); - histos.add("particle/decayDistanceY", "Particle decay distance " + tit, kTH1D, {axisPrody}); - histos.add("particle/decayDistanceZ", "Particle decay distance " + tit, kTH1D, {axisProdz}); - histos.add("particle/decayDistanceVsPt", "Particle decay distance " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/decayCtau", "Particle decay distance " + tit, kTH1D, {{1000, 0, 10000, "c#tau (#mum)"}}); - - histos.add("particle/daughters/Number", "Number of Daughters " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/daughters/PDGs", "Daughters PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/PDGsPrimaries", "Daughters PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/PDGsSecondaries", "Daughters PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); - - histos.add("particle/mothers/Number", "Number of Mothers " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/mothers/PDGs", "Mothers PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/PDGsPrimaries", "Mothers PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/PDGsSecondaries", "Mothers PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/Pt", "Particle Pt " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/P", "Particle P " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/primariesPt", "Particle Pt (primary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/primariesP", "Particle P (primary) " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/secondariesPt", "Particle Pt (secondary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("particle/secondariesP", "Particle P (secondary) " + titlePdgCode, kTH1D, {axisP}); + histos.add("particle/prodVx", "Particle Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/prodVy", "Particle Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/prodVz", "Particle Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/prodRadius", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH1D, {axisProdRadius}); + histos.add("particle/prodRadius3D", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH1D, {axisProdRadius}); + histos.add("particle/prodDistance", "Particle Prod. Vertex Distance XY " + titlePdgCode, kTH1D, {axisProdDistance}); + histos.add("particle/prodDistance3D", "Particle Prod. Vertex Distance XYZ " + titlePdgCode, kTH1D, {axisProdDistance}); + histos.add("particle/prodVxVy", "Particle Prod. Vertex X vs Vertex Y " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyPrm", "Particle Prod. Vertex X vs Vertex Y Primary " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyStr", "Particle Prod. Vertex X vs Vertex Y Decay " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVyMat", "Particle Prod. Vertex X vs Vertex Y Material " + titlePdgCode, kTH2D, {axisProdx, axisPrody}); + histos.add("particle/prodVxVsPt", "Particle Prod. Vertex X " + titlePdgCode, kTH2D, {axisPt, axisProdx}); + histos.add("particle/prodVyVsPt", "Particle Prod. Vertex Y " + titlePdgCode, kTH2D, {axisPt, axisPrody}); + histos.add("particle/prodVzVsPt", "Particle Prod. Vertex Z " + titlePdgCode, kTH2D, {axisPt, axisProdz}); + histos.add("particle/prodRadiusVsPt", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/prodRadiusVsEta", "Particle Prod. Vertex Radius XY " + titlePdgCode, kTH2D, {axisEta, axisProdRadius}); + histos.add("particle/prodRadius3DVsPt", "Particle Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/Eta", "Particle Eta " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/primariesEta", "Particle Eta (primary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/secondariesEta", "Particle Eta (secondary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("particle/Y", "Particle Y " + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/primariesY", "Particle Y (primary)" + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/secondariesY", "Particle Y (secondary)" + titlePdgCode, kTH1D, {axisY}); + histos.add("particle/EvsPz", "Particle E vs Pz " + titlePdgCode, kTH2D, {axisE, axisPz}); + histos.add("particle/YvzPz", "Particle Y vs Pz " + titlePdgCode, kTH2D, {axisY, axisPz}); + histos.add("particle/EtavzPz", "Particle Eta vs Pz " + titlePdgCode, kTH2D, {axisEta, axisPz}); + histos.add("particle/PtvzPz", "Particle Pt vs Pz " + titlePdgCode, kTH2D, {axisPt, axisPz}); + histos.add("particle/PvzPz", "Particle P vs Pz " + titlePdgCode, kTH2D, {axisP, axisPz}); + histos.add("particle/Px", "Particle Px " + titlePdgCode, kTH1D, {axisPx}); + histos.add("particle/Py", "Particle Py " + titlePdgCode, kTH1D, {axisPy}); + histos.add("particle/Pz", "Particle Pz " + titlePdgCode, kTH1D, {axisPz}); + histos.add("particle/decayDistanceX", "Particle decay distance " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/decayDistanceY", "Particle decay distance " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/decayDistanceZ", "Particle decay distance " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/decayDistanceVsPt", "Particle decay distance " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/decayCtau", "Particle decay distance " + titlePdgCode, kTH1D, {{1000, 0, 10000, "c#tau (#mum)"}}); + + histos.add("particle/daughters/Number", "Number of Daughters " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/daughters/PDGs", "Daughters PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/PDGsPrimaries", "Daughters PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/PDGsSecondaries", "Daughters PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/daughters/prodVx", "Daughters Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/daughters/prodVy", "Daughters Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/daughters/prodVz", "Daughters Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/daughters/prodRadiusVsPt", "Daughters Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/daughters/prodRadius3DVsPt", "Daughters Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + + histos.add("particle/mothers/Number", "Number of Mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/mothers/PDGs", "Mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/PDGsPrimaries", "Mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/PDGsSecondaries", "Mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/prodVx", "Mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/mothers/prodVy", "Mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/mothers/prodVz", "Mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/mothers/prodRadiusVsPt", "Mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/prodRadius3DVsPt", "Mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); // Go up one generation - histos.add("particle/mothers/mothers/Number", "Number of Mothers mothers " + tit, kTH1D, {{20, -0.5, 19.5}}); - histos.add("particle/mothers/mothers/PDGs", "Mothers mothers PDGs " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/PDGsPrimaries", "Mothers mothers PDGs Primaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/PDGsSecondaries", "Mothers mothers PDGs Secondaries of " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + tit, kTH1D, {axisProdx}); - histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + tit, kTH1D, {axisPrody}); - histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + tit, kTH1D, {axisProdz}); - histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + tit, kTH2D, {axisPt, axisProdRadius}); - histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + tit, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/mothers/Number", "Number of Mothers mothers " + titlePdgCode, kTH1D, {{20, -0.5, 19.5}}); + histos.add("particle/mothers/mothers/PDGs", "Mothers mothers PDGs " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/PDGsPrimaries", "Mothers mothers PDGs Primaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/PDGsSecondaries", "Mothers mothers PDGs Secondaries of " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("particle/mothers/mothers/prodVx", "Mothers mothers Prod. Vertex X " + titlePdgCode, kTH1D, {axisProdx}); + histos.add("particle/mothers/mothers/prodVy", "Mothers mothers Prod. Vertex Y " + titlePdgCode, kTH1D, {axisPrody}); + histos.add("particle/mothers/mothers/prodVz", "Mothers mothers Prod. Vertex Z " + titlePdgCode, kTH1D, {axisProdz}); + histos.add("particle/mothers/mothers/prodRadiusVsPt", "Mothers mothers Prod. Vertex Radius " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); + histos.add("particle/mothers/mothers/prodRadius3DVsPt", "Mothers mothers Prod. Vertex Radius XYZ " + titlePdgCode, kTH2D, {axisPt, axisProdRadius}); if (doprocessParticleOnly) { return; @@ -202,15 +257,15 @@ struct Alice3SingleParticle { histos.add("track/PDGs", "Track PDGs", kTH2D, {axisPDGs, axisCharge}); histos.add("track/withoutParticle", "Tracks without particles", kTH1D, {axisPt}); histos.add("track/tofPDGs", "Track wTOF PDGs", kTH2D, {axisPDGs, axisCharge}); - histos.add("track/Pt", "Track Pt " + tit, kTH1D, {axisPt}); - histos.add("track/primariesPt", "Track Pt (primary) " + tit, kTH1D, {axisPt}); - histos.add("track/secondariesPt", "Track Pt (secondary) " + tit, kTH1D, {axisPt}); - histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + tit, kTH2D, {axisPt, axisPtMC}); - histos.add("track/Eta", "Track Eta " + tit, kTH1D, {axisEta}); - histos.add("track/primariesEta", "Track Eta (primary) " + tit, kTH1D, {axisEta}); - histos.add("track/secondariesEta", "Track Eta (secondary) " + tit, kTH1D, {axisEta}); - histos.add("track/primaries", "Source for primaries " + tit, kTH2D, {axisPDGs, axisCharge}); - histos.add("track/secondaries", "Source for secondaries " + tit, kTH2D, {axisPDGs, axisCharge}); + histos.add("track/Pt", "Track Pt " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/primariesPt", "Track Pt (primary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/secondariesPt", "Track Pt (secondary) " + titlePdgCode, kTH1D, {axisPt}); + histos.add("track/PtvsMCPt", "Track Pt vs MC Pt " + titlePdgCode, kTH2D, {axisPt, axisPt}); + histos.add("track/Eta", "Track Eta " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/primariesEta", "Track Eta (primary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/secondariesEta", "Track Eta (secondary) " + titlePdgCode, kTH1D, {axisEta}); + histos.add("track/primaries", "Source for primaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); + histos.add("track/secondaries", "Source for secondaries " + titlePdgCode, kTH2D, {axisPDGs, axisCharge}); } template @@ -229,236 +284,101 @@ struct Alice3SingleParticle { return Form("%i", particle.pdgCode()); } - void processStandard(const o2::aod::McCollisions& colls, - const soa::Join& tracks, - const aod::McParticles& mcParticles) + // Nch estimator: charged physical primaries in |eta| < settings.nchEtaMax + template + bool isNchParticle(ParticleType const& particle) { - for (const auto& col : colls) { - histos.fill(HIST("event/VtxX"), col.posX()); - histos.fill(HIST("event/VtxY"), col.posY()); - histos.fill(HIST("event/VtxZ"), col.posZ()); + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= settings.nchEtaMax) { + return false; } - std::vector ParticlesOfInterest; - for (const auto& mcParticle : mcParticles) { - const auto& pdgString = getPdgCodeString(mcParticle); - const auto& pdgCharge = getCharge(mcParticle); - histos.get(HIST("particle/PDGs"))->Fill(pdgString, pdgCharge, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/PDGsPrimaries"))->Fill(pdgString, pdgCharge, 1.f); - } else { - histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); - } - if (mcParticle.pdgCode() != PDG) { - continue; - } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { - continue; - } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { - continue; - } - if (mcParticle.pt() < ptMin || mcParticle.pt() > ptMax) { - continue; - } - if (mcParticle.vz() < prodMinZ || mcParticle.vz() > prodMaxZ) { - continue; - } - histos.fill(HIST("particle/producedProcess"), mcParticle.getProcess()); - histos.fill(HIST("particle/Pt"), mcParticle.pt()); - histos.fill(HIST("particle/P"), mcParticle.p()); - histos.fill(HIST("particle/Eta"), mcParticle.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("particle/primariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/primariesP"), mcParticle.p()); - histos.fill(HIST("particle/primariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/primariesY"), mcParticle.y()); - } else { - histos.fill(HIST("particle/secondariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/secondariesP"), mcParticle.p()); - histos.fill(HIST("particle/secondariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/secondariesY"), mcParticle.y()); - } - histos.fill(HIST("particle/EvsPz"), mcParticle.e(), mcParticle.pz()); - histos.fill(HIST("particle/Y"), mcParticle.y()); - histos.fill(HIST("particle/YvzPz"), mcParticle.y(), mcParticle.pz()); - histos.fill(HIST("particle/EtavzPz"), mcParticle.eta(), mcParticle.pz()); - histos.fill(HIST("particle/PvzPz"), mcParticle.p(), mcParticle.pz()); - histos.fill(HIST("particle/PtvzPz"), mcParticle.pt(), mcParticle.pz()); - histos.fill(HIST("particle/Px"), mcParticle.px()); - histos.fill(HIST("particle/Py"), mcParticle.py()); - histos.fill(HIST("particle/Pz"), mcParticle.pz()); - histos.fill(HIST("particle/prodVx"), mcParticle.vx()); - histos.fill(HIST("particle/prodVy"), mcParticle.vy()); - histos.fill(HIST("particle/prodVz"), mcParticle.vz()); - if (mcParticle.has_daughters()) { - const auto& daughters = mcParticle.daughters_as(); - histos.fill(HIST("particle/daughters/Number"), daughters.size()); - bool filledDistance = false; - for (const auto& daughter : daughters) { - const auto& pdgStringDau = getPdgCodeString(daughter); - const auto& pdgChargeDau = getCharge(daughter); - - histos.get(HIST("particle/daughters/PDGs"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/daughters/PDGsPrimaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } else { - histos.get(HIST("particle/daughters/PDGsSecondaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } - - histos.fill(HIST("particle/daughters/prodVx"), daughter.vx()); - histos.fill(HIST("particle/daughters/prodVy"), daughter.vy()); - histos.fill(HIST("particle/daughters/prodVz"), daughter.vz()); - if (filledDistance) { - continue; - } - filledDistance = true; - histos.fill(HIST("particle/daughters/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(daughter.vx() * daughter.vx() + daughter.vy() * daughter.vy())); - histos.fill(HIST("particle/daughters/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(daughter.vx() * daughter.vx() + daughter.vy() * daughter.vy() + daughter.vz() * daughter.vz())); - - const float dx = daughter.vx() - mcParticle.vx(); - const float dy = daughter.vy() - mcParticle.vy(); - const float dz = daughter.vz() - mcParticle.vz(); - histos.fill(HIST("particle/decayDistanceX"), dx); - histos.fill(HIST("particle/decayDistanceY"), dy); - histos.fill(HIST("particle/decayDistanceZ"), dz); - histos.fill(HIST("particle/decayDistanceVsPt"), mcParticle.pt(), std::sqrt((dx) * (dx) + (dy) * (dy) + (dz) * (dz))); - if (pdg->GetParticle(mcParticle.pdgCode())) { - const float mass = pdg->GetParticle(mcParticle.pdgCode())->Mass(); - histos.fill(HIST("particle/decayCtau"), std::sqrt((dx) * (dx) + (dy) * (dy) + (dz) * (dz)) / mcParticle.pt() * mass * 10 * 1000); - } - } - } else { - histos.fill(HIST("particle/daughters/Number"), 0.f); - } - if (mcParticle.has_mothers()) { - const auto& mothers = mcParticle.mothers_as(); - for (const auto& mother : mothers) { - const auto& pdgStringMot = getPdgCodeString(mother); - const auto& pdgChargeMot = getCharge(mother); - - histos.get(HIST("particle/mothers/PDGs"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/PDGsPrimaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } else { - histos.get(HIST("particle/mothers/PDGsSecondaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } - - histos.fill(HIST("particle/mothers/prodVx"), mother.vx()); - histos.fill(HIST("particle/mothers/prodVy"), mother.vy()); - histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); - histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); - histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); - if (mother.has_mothers()) { - const auto& mothers2 = mother.mothers_as(); - for (const auto& mother2 : mothers2) { - const auto& pdgStringMot2 = getPdgCodeString(mother2); - const auto& pdgChargeMot2 = getCharge(mother2); - - histos.get(HIST("particle/mothers/mothers/PDGs"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/mothers/PDGsPrimaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - } else { - histos.get(HIST("particle/mothers/mothers/PDGsSecondaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); - } - - histos.fill(HIST("particle/mothers/mothers/prodVx"), mother2.vx()); - histos.fill(HIST("particle/mothers/mothers/prodVy"), mother2.vy()); - histos.fill(HIST("particle/mothers/mothers/prodVz"), mother2.vz()); - histos.fill(HIST("particle/mothers/mothers/prodRadiusVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy())); - histos.fill(HIST("particle/mothers/mothers/prodRadius3DVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy() + mother2.vz() * mother2.vz())); - } - } - } - } + const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + return pdgParticle && pdgParticle->Charge() != 0; + } - histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { - histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); - } else { - histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); - } - } else { - histos.fill(HIST("particle/prodVxVyPrm"), mcParticle.vx(), mcParticle.vy()); - } - histos.fill(HIST("particle/prodVxVsPt"), mcParticle.pt(), mcParticle.vx()); - histos.fill(HIST("particle/prodVyVsPt"), mcParticle.pt(), mcParticle.vy()); - histos.fill(HIST("particle/prodVzVsPt"), mcParticle.pt(), mcParticle.vz()); - histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - ParticlesOfInterest.push_back(mcParticle.globalIndex()); + void fillSignalMultiplicity(const std::vector& nchPerCollision, const std::vector& nSignalPerCollision) + { + for (std::size_t iCollision = 0; iCollision < nchPerCollision.size(); ++iCollision) { + histos.fill(HIST("event/Nch"), nchPerCollision[iCollision]); + histos.fill(HIST("particle/NSignalVsNch"), nchPerCollision[iCollision], nSignalPerCollision[iCollision]); } - + } + // Track histograms. Must be called after genericProcessParticles, which fills particlesOfInterest. + template + void genericProcessTracks(const TTracks& tracks) + { for (const auto& track : tracks) { if (!track.has_mcParticle()) { histos.fill(HIST("track/withoutParticle"), track.pt()); continue; } - const auto mcParticle = track.mcParticle_as(); + const auto mcParticle = track.template mcParticle_as(); histos.get(HIST("track/PDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); if (track.hasTOF()) { histos.get(HIST("track/tofPDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); } - if (IsStable.value == 0) { - LOG(info) << mcParticle.pdgCode() << " asked for " << PDG.value; + if (settings.isStable.value == 0) { + if (settings.doPrint) { + LOG(info) << mcParticle.pdgCode() << " asked for " << pdgCode.value; + } if (!mcParticle.has_mothers()) { continue; } // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); - const auto ParticleIsInteresting = std::find(ParticlesOfInterest.begin(), ParticlesOfInterest.end(), mothers[0].globalIndex()) != ParticlesOfInterest.end(); - if (!ParticleIsInteresting) { + auto mothers = mcParticle.template mothers_as(); + const auto particleIsInteresting = std::find(particlesOfInterest.begin(), particlesOfInterest.end(), mothers[0].globalIndex()) != particlesOfInterest.end(); + if (!particleIsInteresting) { continue; } - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " comes from a " << mothers[0].pdgCode() << " and is a " << mcParticle.pdgCode(); } } else { - if (mcParticle.pdgCode() != PDG) { + if (pdgCode != 0 && mcParticle.pdgCode() != pdgCode) { continue; } - histos.fill(HIST("track/Pt"), track.pt() * charge); - histos.fill(HIST("track/PtvsMCPt"), track.pt() * charge, mcParticle.pt()); + histos.fill(HIST("track/Pt"), track.pt() * settings.charge); + histos.fill(HIST("track/PtvsMCPt"), track.pt() * settings.charge, mcParticle.pt()); histos.fill(HIST("track/Eta"), track.eta()); if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("track/primariesPt"), track.pt() * charge); + histos.fill(HIST("track/primariesPt"), track.pt() * settings.charge); histos.fill(HIST("track/primariesEta"), track.eta()); } else { - histos.fill(HIST("track/secondariesPt"), track.pt() * charge); + histos.fill(HIST("track/secondariesPt"), track.pt() * settings.charge); histos.fill(HIST("track/secondariesEta"), track.eta()); } if (!mcParticle.has_mothers()) { - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode(); } continue; } // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); + auto mothers = mcParticle.template mothers_as(); if (mcParticle.isPhysicalPrimary()) { histos.get(HIST("track/primaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); } else { histos.get(HIST("track/secondaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); } - if (doPrint) { + if (settings.doPrint) { LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode() << " and comes from a " << mothers[0].pdgCode() << " and is " << (mcParticle.isPhysicalPrimary() ? "" : "not") << " a primary"; } } } } - PROCESS_SWITCH(Alice3SingleParticle, processStandard, "Process IU tracks", true); - void processParticleOnly(const o2::aod::McCollisions& colls, - const aod::McParticles& mcParticles) + // Event and MC particle histograms. Stores the selected MC particles in particlesOfInterest. + void genericProcessParticles(const o2::aod::McCollisions& colls, + const aod::McParticles& mcParticles) { for (const auto& col : colls) { histos.fill(HIST("event/VtxX"), col.posX()); histos.fill(HIST("event/VtxY"), col.posY()); histos.fill(HIST("event/VtxZ"), col.posZ()); } + std::vector nchPerCollision(colls.size(), 0); + std::vector nSignalPerCollision(colls.size(), 0); + particlesOfInterest.clear(); for (const auto& mcParticle : mcParticles) { const auto& pdgString = getPdgCodeString(mcParticle); const auto& pdgCharge = getCharge(mcParticle); @@ -468,18 +388,25 @@ struct Alice3SingleParticle { } else { histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); } - if (mcParticle.pdgCode() != PDG) { + if (isNchParticle(mcParticle)) { + ++nchPerCollision.at(mcParticle.mcCollisionId()); + } + if (pdgCode != 0 && mcParticle.pdgCode() != pdgCode) { continue; } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { + if (!yRange.contains(mcParticle.y())) { continue; } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { + if (!etaRange.contains(mcParticle.eta())) { continue; } - if (mcParticle.vz() < prodMinZ || mcParticle.vz() > prodMaxZ) { + if (!ptRange.contains(mcParticle.pt())) { continue; } + if (!prodZRange.contains(mcParticle.vz())) { + continue; + } + ++nSignalPerCollision.at(mcParticle.mcCollisionId()); histos.fill(HIST("particle/producedProcess"), mcParticle.getProcess()); histos.fill(HIST("particle/Pt"), mcParticle.pt()); histos.fill(HIST("particle/P"), mcParticle.p()); @@ -548,7 +475,7 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/daughters/Number"), 0.f); } if (mcParticle.has_mothers()) { - auto mothers = mcParticle.mothers_as(); + const auto& mothers = mcParticle.mothers_as(); for (const auto& mother : mothers) { const auto& pdgStringMot = getPdgCodeString(mother); const auto& pdgChargeMot = getCharge(mother); @@ -565,13 +492,33 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); + if (mother.has_mothers()) { + const auto& mothers2 = mother.mothers_as(); + for (const auto& mother2 : mothers2) { + const auto& pdgStringMot2 = getPdgCodeString(mother2); + const auto& pdgChargeMot2 = getCharge(mother2); + + histos.get(HIST("particle/mothers/mothers/PDGs"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + if (mcParticle.isPhysicalPrimary()) { + histos.get(HIST("particle/mothers/mothers/PDGsPrimaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + } else { + histos.get(HIST("particle/mothers/mothers/PDGsSecondaries"))->Fill(pdgStringMot2, pdgChargeMot2, 1.f); + } + + histos.fill(HIST("particle/mothers/mothers/prodVx"), mother2.vx()); + histos.fill(HIST("particle/mothers/mothers/prodVy"), mother2.vy()); + histos.fill(HIST("particle/mothers/mothers/prodVz"), mother2.vz()); + histos.fill(HIST("particle/mothers/mothers/prodRadiusVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy())); + histos.fill(HIST("particle/mothers/mothers/prodRadius3DVsPt"), mother2.pt(), std::sqrt(mother2.vx() * mother2.vx() + mother2.vy() * mother2.vy() + mother2.vz() * mother2.vz())); + } + } } } histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { + if (mcParticle.getProcess() == TMCProcess::kPDecay) { histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); } else { histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); @@ -585,187 +532,35 @@ struct Alice3SingleParticle { histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); + particlesOfInterest.push_back(mcParticle.globalIndex()); } + fillSignalMultiplicity(nchPerCollision, nSignalPerCollision); + } + + void processStandard(const o2::aod::McCollisions& colls, + const soa::Join& tracks, + const aod::McParticles& mcParticles) + { + genericProcessParticles(colls, mcParticles); + genericProcessTracks(tracks); + } + + void processParticleOnly(const o2::aod::McCollisions& colls, + const aod::McParticles& mcParticles) + { + genericProcessParticles(colls, mcParticles); } - PROCESS_SWITCH(Alice3SingleParticle, processParticleOnly, "Process Particle only", false); void processNonIU(const o2::aod::McCollisions& colls, const soa::Join& tracks, const aod::McParticles& mcParticles) { - for (const auto& col : colls) { - histos.fill(HIST("event/VtxX"), col.posX()); - histos.fill(HIST("event/VtxY"), col.posY()); - histos.fill(HIST("event/VtxZ"), col.posZ()); - } - std::vector ParticlesOfInterest; - for (const auto& mcParticle : mcParticles) { - const auto& pdgString = getPdgCodeString(mcParticle); - const auto& pdgCharge = getCharge(mcParticle); - histos.get(HIST("particle/PDGs"))->Fill(pdgString, pdgCharge, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/PDGsPrimaries"))->Fill(pdgString, pdgCharge, 1.f); - } else { - histos.get(HIST("particle/PDGsSecondaries"))->Fill(pdgString, pdgCharge, 1.f); - } - if (mcParticle.pdgCode() != PDG) { - continue; - } - if (mcParticle.y() < yMin || mcParticle.y() > yMax) { - continue; - } - if (mcParticle.eta() < etaMin || mcParticle.eta() > etaMax) { - continue; - } - if (mcParticle.pt() < ptMin || mcParticle.pt() > ptMax) { - continue; - } - histos.fill(HIST("particle/Pt"), mcParticle.pt()); - histos.fill(HIST("particle/P"), mcParticle.p()); - histos.fill(HIST("particle/Eta"), mcParticle.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("particle/primariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/primariesP"), mcParticle.p()); - histos.fill(HIST("particle/primariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/primariesY"), mcParticle.y()); - } else { - histos.fill(HIST("particle/secondariesPt"), mcParticle.pt()); - histos.fill(HIST("particle/secondariesP"), mcParticle.p()); - histos.fill(HIST("particle/secondariesEta"), mcParticle.eta()); - histos.fill(HIST("particle/secondariesY"), mcParticle.y()); - } - histos.fill(HIST("particle/EvsPz"), mcParticle.e(), mcParticle.pz()); - histos.fill(HIST("particle/Y"), mcParticle.y()); - histos.fill(HIST("particle/YvzPz"), mcParticle.y(), mcParticle.pz()); - histos.fill(HIST("particle/EtavzPz"), mcParticle.eta(), mcParticle.pz()); - histos.fill(HIST("particle/PvzPz"), mcParticle.p(), mcParticle.pz()); - histos.fill(HIST("particle/PtvzPz"), mcParticle.pt(), mcParticle.pz()); - histos.fill(HIST("particle/Px"), mcParticle.px()); - histos.fill(HIST("particle/Py"), mcParticle.py()); - histos.fill(HIST("particle/Pz"), mcParticle.pz()); - histos.fill(HIST("particle/prodVx"), mcParticle.vx()); - histos.fill(HIST("particle/prodVy"), mcParticle.vy()); - histos.fill(HIST("particle/prodVz"), mcParticle.vz()); - if (mcParticle.has_daughters()) { - auto daughters = mcParticle.daughters_as(); - histos.fill(HIST("particle/daughters/Number"), daughters.size()); - for (const auto& daughter : daughters) { - const auto& pdgStringDau = getPdgCodeString(daughter); - const auto& pdgChargeDau = getCharge(daughter); - - histos.get(HIST("particle/daughters/PDGs"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/daughters/PDGsPrimaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } else { - histos.get(HIST("particle/daughters/PDGsSecondaries"))->Fill(pdgStringDau, pdgChargeDau, 1.f); - } - - histos.fill(HIST("particle/daughters/prodVx"), daughter.vx()); - histos.fill(HIST("particle/daughters/prodVy"), daughter.vy()); - histos.fill(HIST("particle/daughters/prodVz"), daughter.vz()); - histos.fill(HIST("particle/daughters/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/daughters/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - } - } else { - histos.fill(HIST("particle/daughters/Number"), 0.f); - } - if (mcParticle.has_mothers()) { - auto mothers = mcParticle.mothers_as(); - for (const auto& mother : mothers) { - const auto& pdgStringMot = getPdgCodeString(mother); - const auto& pdgChargeMot = getCharge(mother); - - histos.get(HIST("particle/mothers/PDGs"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("particle/mothers/PDGsPrimaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } else { - histos.get(HIST("particle/mothers/PDGsSecondaries"))->Fill(pdgStringMot, pdgChargeMot, 1.f); - } - - histos.fill(HIST("particle/mothers/prodVx"), mother.vx()); - histos.fill(HIST("particle/mothers/prodVy"), mother.vy()); - histos.fill(HIST("particle/mothers/prodVz"), mother.vz()); - histos.fill(HIST("particle/mothers/prodRadiusVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy())); - histos.fill(HIST("particle/mothers/prodRadius3DVsPt"), mother.pt(), std::sqrt(mother.vx() * mother.vx() + mother.vy() * mother.vy() + mother.vz() * mother.vz())); - } - } - - histos.fill(HIST("particle/prodRadius"), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodVxVy"), mcParticle.vx(), mcParticle.vy()); - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == 4) { - histos.fill(HIST("particle/prodVxVyStr"), mcParticle.vx(), mcParticle.vy()); - } else { - histos.fill(HIST("particle/prodVxVyMat"), mcParticle.vx(), mcParticle.vy()); - } - } else { - histos.fill(HIST("particle/prodVxVyPrm"), mcParticle.vx(), mcParticle.vy()); - } - histos.fill(HIST("particle/prodVxVsPt"), mcParticle.pt(), mcParticle.vx()); - histos.fill(HIST("particle/prodVyVsPt"), mcParticle.pt(), mcParticle.vy()); - histos.fill(HIST("particle/prodVzVsPt"), mcParticle.pt(), mcParticle.vz()); - histos.fill(HIST("particle/prodRadiusVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadiusVsEta"), mcParticle.eta(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy())); - histos.fill(HIST("particle/prodRadius3DVsPt"), mcParticle.pt(), std::sqrt(mcParticle.vx() * mcParticle.vx() + mcParticle.vy() * mcParticle.vy() + mcParticle.vz() * mcParticle.vz())); - ParticlesOfInterest.push_back(mcParticle.globalIndex()); - } - - for (const auto& track : tracks) { - if (!track.has_mcParticle()) { - histos.fill(HIST("track/withoutParticle"), track.pt()); - continue; - } - const auto mcParticle = track.mcParticle_as(); - histos.get(HIST("track/PDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); - if (track.hasTOF()) { - histos.get(HIST("track/tofPDGs"))->Fill(getPdgCodeString(mcParticle), getCharge(mcParticle), 1.f); - } - if (IsStable.value == 0) { - if (!mcParticle.has_mothers()) { - continue; - } - const auto& mothers = mcParticle.mothers_as(); - const auto& ParticleIsInteresting = std::find(ParticlesOfInterest.begin(), ParticlesOfInterest.end(), mothers[0].globalIndex()) != ParticlesOfInterest.end(); - if (!ParticleIsInteresting) { - continue; - } - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " comes from a " << mothers[0].pdgCode() << " and is a " << mcParticle.pdgCode(); - } - } else { - if (mcParticle.pdgCode() != PDG) { - continue; - } - histos.fill(HIST("track/Pt"), track.pt() * charge); - histos.fill(HIST("track/PtvsMCPt"), track.pt() * charge, mcParticle.pt()); - histos.fill(HIST("track/Eta"), track.eta()); - if (mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("track/primariesPt"), track.pt() * charge); - histos.fill(HIST("track/primariesEta"), track.eta()); - } else { - histos.fill(HIST("track/secondariesPt"), track.pt() * charge); - histos.fill(HIST("track/secondariesEta"), track.eta()); - } - - if (!mcParticle.has_mothers()) { - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode(); - } - continue; - } - // auto mothers = mcParticle.mothers(); - auto mothers = mcParticle.mothers_as(); - if (mcParticle.isPhysicalPrimary()) { - histos.get(HIST("track/primaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); - } else { - histos.get(HIST("track/secondaries"))->Fill(getPdgCodeString(mothers[0]), getCharge(mothers[0]), 1.f); - } - if (doPrint) { - LOG(info) << "Track " << track.globalIndex() << " is a " << mcParticle.pdgCode() << " and comes from a " << mothers[0].pdgCode() << " and is " << (mcParticle.isPhysicalPrimary() ? "" : "not") << " a primary"; - } - } - } + genericProcessParticles(colls, mcParticles); + genericProcessTracks(tracks); } + + PROCESS_SWITCH(Alice3SingleParticle, processStandard, "Process IU tracks", false); + PROCESS_SWITCH(Alice3SingleParticle, processParticleOnly, "Process Particle only", true); PROCESS_SWITCH(Alice3SingleParticle, processNonIU, "Process non IU tracks", false); }; diff --git a/ALICE3/Tasks/alice3DecayerQa.cxx b/ALICE3/Tasks/alice3DecayerQa.cxx index cf1c369d17f..ceacc433c4b 100644 --- a/ALICE3/Tasks/alice3DecayerQa.cxx +++ b/ALICE3/Tasks/alice3DecayerQa.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \file alice3DecayerQA.cxx +/// \file alice3DecayerQa.cxx /// /// \brief QA task for otf decayer /// @@ -17,8 +17,6 @@ /// \since Dec 23, 2025 /// -#include "ALICE3/DataModel/OTFMCParticle.h" - #include #include #include @@ -29,155 +27,299 @@ #include #include +#include #include +#include #include using namespace o2; using namespace o2::framework; -struct Alice3DecayerQA { +struct Alice3DecayerQa { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + SliceCache cache; struct : ConfigurableGroup { ConfigurableAxis axisCollisionId{"axisCollisionId", {1000, 0, 999}, "CollisionId axis for QA histograms"}; ConfigurableAxis axisPdgCode{"axisPdgCode", {1000, 0, 999}, "PdgCode axis for QA histograms"}; ConfigurableAxis axisStatusCode{"axisStatusCode", {1000, 0, 999}, "StatusCode axis for QA histograms"}; - ConfigurableAxis axisFlags{"axisFlags", {10, 0, 9}, "Flags axis for QA histograms"}; + ConfigurableAxis axisFlags{"axisFlags", {256, 0, 255}, "Flags axis for QA histograms"}; ConfigurableAxis axisMothersIds{"axisMothersIds", {1000, 0, 999}, "MothersIds axis for QA histograms"}; ConfigurableAxis axisDaughtersIds{"axisDaughtersIds", {1000, 0, 999}, "DaughtersIds axis for QA histograms"}; ConfigurableAxis axisWeight{"axisWeight", {2, 0, 1}, "Weight axis for QA histograms"}; ConfigurableAxis axisPos{"axisPos", {1000, 0, 999}, "Position axis for QA histograms"}; + ConfigurableAxis axisEventTime{"axisEventTime", {1000, -1e-3, 1e-3}, "Event time axis for QA histograms"}; + ConfigurableAxis axisProductionTime{"axisProductionTime", {1000, 0, 10}, "Particle production time axis for QA histograms"}; ConfigurableAxis axisPhi{"axisPhi", {720, -360, 360}, "Phi axis for QA histograms"}; ConfigurableAxis axisEta{"axisEta", {80, -4, 4}, "Eta axis for QA histograms"}; ConfigurableAxis axisRapidity{"axisRapidity", {80, -4, 4}, "Rapidity axis for QA histograms"}; - ConfigurableAxis axisIsAlive{"axisIsAlive", {2, 0, 1}, "IsAlive axis for QA histograms"}; - ConfigurableAxis axisIsPrimary{"axisIsPrimary", {2, 0, 1}, "IsPrimary axis for QA histograms"}; + ConfigurableAxis axisImpactParameter{"axisImpactParameter", {200, 0, 20}, "Impact parameter axis for QA histograms"}; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; + ConfigurableAxis axisRadiusLog{"axisRadiusLog", {VARIABLE_WIDTH, 0.0f, 0.01f, 0.0104713f, 0.0109648f, 0.0114815f, 0.0120226f, 0.0125893f, 0.0131826f, 0.0138038f, 0.0144544f, 0.0151356f, 0.0158489f, 0.0165959f, 0.017378f, 0.018197f, 0.0190546f, 0.0199526f, 0.020893f, 0.0218776f, 0.0229087f, 0.0239883f, 0.0251189f, 0.0263027f, 0.0275423f, 0.0288403f, 0.0301995f, 0.0316228f, 0.0331131f, 0.0346737f, 0.0363078f, 0.0380189f, 0.0398107f, 0.0416869f, 0.0436516f, 0.0457088f, 0.047863f, 0.0501187f, 0.0524807f, 0.0549541f, 0.057544f, 0.060256f, 0.0630957f, 0.0660693f, 0.0691831f, 0.0724436f, 0.0758578f, 0.0794328f, 0.0831764f, 0.0870964f, 0.0912011f, 0.0954993f, 0.1f, 0.104713f, 0.109648f, 0.114815f, 0.120226f, 0.125893f, 0.131826f, 0.138038f, 0.144544f, 0.151356f, 0.158489f, 0.165959f, 0.17378f, 0.18197f, 0.190546f, 0.199526f, 0.20893f, 0.218776f, 0.229087f, 0.239883f, 0.251189f, 0.263027f, 0.275423f, 0.288403f, 0.301995f, 0.316228f, 0.331131f, 0.346737f, 0.363078f, 0.380189f, 0.398107f, 0.416869f, 0.436516f, 0.457088f, 0.47863f, 0.501187f, 0.524807f, 0.549541f, 0.57544f, 0.60256f, 0.630957f, 0.660693f, 0.691831f, 0.724436f, 0.758578f, 0.794328f, 0.831764f, 0.870964f, 0.912011f, 0.954993f, 1.0f, 1.04713f, 1.09648f, 1.14815f, 1.20226f, 1.25893f, 1.31826f, 1.38038f, 1.44544f, 1.51356f, 1.58489f, 1.65959f, 1.7378f, 1.8197f, 1.90546f, 1.99526f, 2.0893f, 2.18776f, 2.29087f, 2.39883f, 2.51189f, 2.63027f, 2.75423f, 2.88403f, 3.01995f, 3.16228f, 3.31131f, 3.46737f, 3.63078f, 3.80189f, 3.98107f, 4.16869f, 4.36516f, 4.57088f, 4.7863f, 5.01187f, 5.24807f, 5.49541f, 5.7544f, 6.0256f, 6.30957f, 6.60693f, 6.91831f, 7.24436f, 7.58578f, 7.94328f, 8.31764f, 8.70964f, 9.12011f, 9.54993f, 10.0f, 10.4713f, 10.9648f, 11.4815f, 12.0226f, 12.5893f, 13.1826f, 13.8038f, 14.4544f, 15.1356f, 15.8489f, 16.5959f, 17.378f, 18.197f, 19.0546f, 19.9526f, 20.893f, 21.8776f, 22.9087f, 23.9883f, 25.1189f, 26.3027f, 27.5423f, 28.8403f, 30.1995f, 31.6228f, 33.1131f, 34.6737f, 36.3078f, 38.0189f, 39.8107f, 41.6869f, 43.6516f, 45.7088f, 47.863f, 50.1187f, 52.4807f, 54.9541f, 57.544f, 60.256f, 63.0957f, 66.0693f, 69.1831f, 72.4436f, 75.8578f, 79.4328f, 83.1764f, 87.0964f, 91.2011f, 95.4993f, 100.0f}, "Radial axis"}; + ConfigurableAxis axisPtLog{"axisPtLog", {VARIABLE_WIDTH, 0.0f, 0.001f, 0.00104713f, 0.00109648f, 0.00114815f, 0.00120226f, 0.00125893f, 0.00131826f, 0.00138038f, 0.00144544f, 0.00151356f, 0.00158489f, 0.00165959f, 0.0017378f, 0.0018197f, 0.00190546f, 0.00199526f, 0.0020893f, 0.00218776f, 0.00229087f, 0.00239883f, 0.00251189f, 0.00263027f, 0.00275423f, 0.00288403f, 0.00301995f, 0.00316228f, 0.00331131f, 0.00346737f, 0.00363078f, 0.00380189f, 0.00398107f, 0.00416869f, 0.00436516f, 0.00457088f, 0.0047863f, 0.00501187f, 0.00524807f, 0.00549541f, 0.0057544f, 0.0060256f, 0.00630957f, 0.00660693f, 0.00691831f, 0.00724436f, 0.00758578f, 0.00794328f, 0.00831764f, 0.00870964f, 0.00912011f, 0.00954993f, 0.01f, 0.0104713f, 0.0109648f, 0.0114815f, 0.0120226f, 0.0125893f, 0.0131826f, 0.0138038f, 0.0144544f, 0.0151356f, 0.0158489f, 0.0165959f, 0.017378f, 0.018197f, 0.0190546f, 0.0199526f, 0.020893f, 0.0218776f, 0.0229087f, 0.0239883f, 0.0251189f, 0.0263027f, 0.0275423f, 0.0288403f, 0.0301995f, 0.0316228f, 0.0331131f, 0.0346737f, 0.0363078f, 0.0380189f, 0.0398107f, 0.0416869f, 0.0436516f, 0.0457088f, 0.047863f, 0.0501187f, 0.0524807f, 0.0549541f, 0.057544f, 0.060256f, 0.0630957f, 0.0660693f, 0.0691831f, 0.0724436f, 0.0758578f, 0.0794328f, 0.0831764f, 0.0870964f, 0.0912011f, 0.0954993f, 0.1f, 0.104713f, 0.109648f, 0.114815f, 0.120226f, 0.125893f, 0.131826f, 0.138038f, 0.144544f, 0.151356f, 0.158489f, 0.165959f, 0.17378f, 0.18197f, 0.190546f, 0.199526f, 0.20893f, 0.218776f, 0.229087f, 0.239883f, 0.251189f, 0.263027f, 0.275423f, 0.288403f, 0.301995f, 0.316228f, 0.331131f, 0.346737f, 0.363078f, 0.380189f, 0.398107f, 0.416869f, 0.436516f, 0.457088f, 0.47863f, 0.501187f, 0.524807f, 0.549541f, 0.57544f, 0.60256f, 0.630957f, 0.660693f, 0.691831f, 0.724436f, 0.758578f, 0.794328f, 0.831764f, 0.870964f, 0.912011f, 0.954993f, 1.0f, 1.04713f, 1.09648f, 1.14815f, 1.20226f, 1.25893f, 1.31826f, 1.38038f, 1.44544f, 1.51356f, 1.58489f, 1.65959f, 1.7378f, 1.8197f, 1.90546f, 1.99526f, 2.0893f, 2.18776f, 2.29087f, 2.39883f, 2.51189f, 2.63027f, 2.75423f, 2.88403f, 3.01995f, 3.16228f, 3.31131f, 3.46737f, 3.63078f, 3.80189f, 3.98107f, 4.16869f, 4.36516f, 4.57088f, 4.7863f, 5.01187f, 5.24807f, 5.49541f, 5.7544f, 6.0256f, 6.30957f, 6.60693f, 6.91831f, 7.24436f, 7.58578f, 7.94328f, 8.31764f, 8.70964f, 9.12011f, 9.54993f, 10.0f}, "pt axis for QA histograms"}; } axes; - Partition trueEl = aod::mcparticle::pdgCode == static_cast(kElectron); - Partition trueMu = aod::mcparticle::pdgCode == static_cast(kMuonMinus); - Partition truePi = aod::mcparticle::pdgCode == static_cast(kPiPlus); - Partition trueKa = aod::mcparticle::pdgCode == static_cast(kKMinus); - Partition truePr = aod::mcparticle::pdgCode == static_cast(kProton); + Partition trueElectrons = aod::mcparticle::pdgCode == static_cast(PDG_t::kElectron); + Partition trueMuons = aod::mcparticle::pdgCode == static_cast(PDG_t::kMuonMinus); + Partition truePions = aod::mcparticle::pdgCode == static_cast(PDG_t::kPiPlus); + Partition trueKaons = aod::mcparticle::pdgCode == static_cast(PDG_t::kKMinus); + Partition trueProtons = aod::mcparticle::pdgCode == static_cast(PDG_t::kProton); + Partition trueK0Short = aod::mcparticle::pdgCode == static_cast(PDG_t::kK0Short); + Partition trueLambdas = aod::mcparticle::pdgCode == static_cast(PDG_t::kLambda0); + Partition trueXiMinus = aod::mcparticle::pdgCode == static_cast(PDG_t::kXiMinus); - Partition trueElWithDau = aod::mcparticle::pdgCode == static_cast(kElectron); - Partition trueMuWithDau = aod::mcparticle::pdgCode == static_cast(kMuonMinus); - Partition truePiWithDau = aod::mcparticle::pdgCode == static_cast(kPiPlus); - Partition trueKaWithDau = aod::mcparticle::pdgCode == static_cast(kKMinus); - Partition truePrWithDau = aod::mcparticle::pdgCode == static_cast(kProton); + static constexpr size_t NCascadeDaughters = 2; + static constexpr size_t NV0Daughters = 2; - void init(o2::framework::InitContext&) + template + float radius(const TParticle& particle) const { - histos.add("DefaultMC/hElPt", "hElPt", kTH1D, {axes.axisPt}); - histos.add("DefaultMC/hMuPt", "hMuPt", kTH1D, {axes.axisPt}); - histos.add("DefaultMC/hPiPt", "hPiPt", kTH1D, {axes.axisPt}); - histos.add("DefaultMC/hKaPt", "hKaPt", kTH1D, {axes.axisPt}); - histos.add("DefaultMC/hPrPt", "hPrPt", kTH1D, {axes.axisPt}); - - histos.add("MCWithDau/hElPt", "hElPt", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hMuPt", "hMuPt", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hPiPt", "hPiPt", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hKaPt", "hKaPt", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hPrPt", "hPrPt", kTH1D, {axes.axisPt}); - - histos.add("MCWithDau/hCollisionId", "hCollisionId", kTH1D, {axes.axisCollisionId}); - histos.add("MCWithDau/hPdgCode", "hPdgCode", kTH1D, {axes.axisPdgCode}); - histos.add("MCWithDau/hStatusCode", "hStatusCode", kTH1D, {axes.axisStatusCode}); - histos.add("MCWithDau/hFlags", "hFlags", kTH1D, {axes.axisFlags}); - histos.add("MCWithDau/hMothersIds", "hMothersIds", kTH1D, {axes.axisMothersIds}); - histos.add("MCWithDau/hDaughtersIds", "hDaughtersIds", kTH1D, {axes.axisDaughtersIds}); - histos.add("MCWithDau/hWeight", "hWeight", kTH1D, {axes.axisWeight}); - histos.add("MCWithDau/hVx", "hVx", kTH1D, {axes.axisPos}); - histos.add("MCWithDau/hVy", "hVy", kTH1D, {axes.axisPos}); - histos.add("MCWithDau/hVz", "hVz", kTH1D, {axes.axisPos}); - histos.add("MCWithDau/hVt", "hVt", kTH1D, {axes.axisPos}); - histos.add("MCWithDau/hPhi", "hPhi", kTH1D, {axes.axisPhi}); - histos.add("MCWithDau/hEta", "hEta", kTH1D, {axes.axisEta}); - histos.add("MCWithDau/hRapidity", "hRapidity", kTH1D, {axes.axisRapidity}); - histos.add("MCWithDau/hIsAlive", "hIsAlive", kTH1D, {axes.axisIsAlive}); - histos.add("MCWithDau/hIsPrimary", "hIsPrimary", kTH1D, {axes.axisIsPrimary}); - histos.add("MCWithDau/hPx", "hPx", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hPy", "hPy", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hPz", "hPz", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hPt", "hPt", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hP", "hP", kTH1D, {axes.axisPt}); - histos.add("MCWithDau/hE", "hE", kTH1D, {axes.axisPt}); + return std::hypot(particle.vx(), particle.vy()); } - void processMC(const aod::McParticles&) + void init(o2::framework::InitContext&) { - for (const auto& particle : trueEl) { - histos.fill(HIST("DefaultMC/hElPt"), particle.pt()); - } - for (const auto& particle : trueMu) { - histos.fill(HIST("DefaultMC/hMuPt"), particle.pt()); - } - for (const auto& particle : truePi) { - histos.fill(HIST("DefaultMC/hPiPt"), particle.pt()); - } - for (const auto& particle : trueKa) { - histos.fill(HIST("DefaultMC/hKaPt"), particle.pt()); - } - for (const auto& particle : truePr) { - histos.fill(HIST("DefaultMC/hPrPt"), particle.pt()); - } + // QA with collision table entries + histos.add("McCollision/hBCId", "hBCId", kTH1D, {axes.axisCollisionId}); + histos.add("McCollision/hGeneratorsID", "hGeneratorsID", kTH1D, {axes.axisCollisionId}); + histos.add("McCollision/hPosX", "hPosX", kTH1D, {axes.axisPos}); + histos.add("McCollision/hPosY", "hPosY", kTH1D, {axes.axisPos}); + histos.add("McCollision/hPosZ", "hPosZ", kTH1D, {axes.axisPos}); + histos.add("McCollision/hT", "hT", kTH1D, {axes.axisEventTime}); + histos.add("McCollision/hWeight", "hWeight", kTH1D, {axes.axisWeight}); + histos.add("McCollision/hImpactParameter", "hImpactParameter", kTH1D, {axes.axisImpactParameter}); + histos.add("McCollision/hRventPlaneAngle", "hRventPlaneAngle", kTH1D, {axes.axisPhi}); + + // QA with particle table entries + histos.add("McParticle/hElPt", "hElPt", kTH1D, {axes.axisPt}); + histos.add("McParticle/hMuPt", "hMuPt", kTH1D, {axes.axisPt}); + histos.add("McParticle/hPiPt", "hPiPt", kTH1D, {axes.axisPt}); + histos.add("McParticle/hKaPt", "hKaPt", kTH1D, {axes.axisPt}); + histos.add("McParticle/hPrPt", "hPrPt", kTH1D, {axes.axisPt}); + histos.add("McParticle/hCollisionId", "hCollisionId", kTH1D, {axes.axisCollisionId}); + histos.add("McParticle/hPdgCode", "hPdgCode", kTH1D, {axes.axisPdgCode}); + histos.add("McParticle/hStatusCode", "hStatusCode", kTH1D, {axes.axisStatusCode}); + histos.add("McParticle/hFlags", "hFlags", kTH1D, {axes.axisFlags}); + histos.add("McParticle/hMothersIds", "hMothersIds", kTH1D, {axes.axisMothersIds}); + histos.add("McParticle/hDaughtersIds", "hDaughtersIds", kTH1D, {axes.axisDaughtersIds}); + histos.add("McParticle/hWeight", "hWeight", kTH1D, {axes.axisWeight}); + histos.add("McParticle/hVx", "hVx", kTH1D, {axes.axisPos}); + histos.add("McParticle/hVy", "hVy", kTH1D, {axes.axisPos}); + histos.add("McParticle/hVz", "hVz", kTH1D, {axes.axisPos}); + histos.add("McParticle/hVt", "hVt", kTH1D, {axes.axisProductionTime}); + histos.add("McParticle/hPhi", "hPhi", kTH1D, {axes.axisPhi}); + histos.add("McParticle/hEta", "hEta", kTH1D, {axes.axisEta}); + histos.add("McParticle/hRapidity", "hRapidity", kTH1D, {axes.axisRapidity}); + histos.add("McParticle/hPx", "hPx", kTH1D, {axes.axisPt}); + histos.add("McParticle/hPy", "hPy", kTH1D, {axes.axisPt}); + histos.add("McParticle/hPz", "hPz", kTH1D, {axes.axisPt}); + histos.add("McParticle/hPt", "hPt", kTH1D, {axes.axisPt}); + histos.add("McParticle/hP", "hP", kTH1D, {axes.axisPt}); + histos.add("McParticle/hE", "hE", kTH1D, {axes.axisPt}); + + // QA with daughters from Decayer + histos.add("K0S/hGeneratedPt", "hGeneratedPt;#it{p}_{T} (GeV/#it{c})", kTH1D, {axes.axisPt}); + histos.add("K0S/hPosDauDecayRadius", "hPosDauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("K0S/hNegDauDecayRadius", "hNegDauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("Lambda/hGeneratedPt", "hGeneratedPt;#it{p}_{T} (GeV/#it{c})", kTH1D, {axes.axisPt}); + histos.add("Lambda/hPosDauDecayRadius", "hPosDauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("Lambda/hNegDauDecayRadius", "hNegDauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("XiMinus/hGeneratedPt", "hGeneratedPt;#it{p}_{T} (GeV/#it{c})", kTH1D, {axes.axisPt}); + histos.add("XiMinus/hBachDauDecayRadius", "hBachDauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("XiMinus/hV0DauDecayRadius", "hV0DauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("XiMinus/hPosDauDecayRadius", "hPosDauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("XiMinus/hNegDauDecayRadius", "hNegDauDecayRadius;Decay radius 2D;#it{p}_{T} (GeV/#it{c})", kTH2D, {axes.axisRadiusLog, axes.axisPtLog}); + histos.add("XiMinus/hXiVt", "hXiVt;Production time (ns)", kTH1D, {axes.axisProductionTime}); + histos.add("XiMinus/hBachVt", "hBachVt;Production time (ns)", kTH1D, {axes.axisProductionTime}); + histos.add("XiMinus/hV0Vt", "hV0Vt;Production time (ns)", kTH1D, {axes.axisProductionTime}); + histos.add("XiMinus/hPosVt", "hPosVt;Production time (ns)", kTH1D, {axes.axisProductionTime}); + histos.add("XiMinus/hNegVt", "hNegVt;Production time (ns)", kTH1D, {axes.axisProductionTime}); + + auto hCheckHasK0SDecayed = histos.add("K0S/hHasDecayed", "hHasDecayed", kTH1D, {{2, -0.5, 1.5}}); + hCheckHasK0SDecayed->GetXaxis()->SetBinLabel(1, "No"); + hCheckHasK0SDecayed->GetXaxis()->SetBinLabel(2, "Yes"); + auto hCheckHasLambdaDecayed = histos.add("Lambda/hHasDecayed", "hHasDecayed", kTH1D, {{2, -0.5, 1.5}}); + hCheckHasLambdaDecayed->GetXaxis()->SetBinLabel(1, "No"); + hCheckHasLambdaDecayed->GetXaxis()->SetBinLabel(2, "Yes"); + auto hCheckHasXiMinusDecayed = histos.add("XiMinus/hHasDecayed", "hHasDecayed", kTH1D, {{2, -0.5, 1.5}}); + hCheckHasXiMinusDecayed->GetXaxis()->SetBinLabel(1, "No"); + hCheckHasXiMinusDecayed->GetXaxis()->SetBinLabel(2, "Yes"); } - void processMCWithDau(const aod::McCollision&, const aod::McPartWithDaus& particles) + void process(const aod::McCollision& collision, const aod::McParticles& particles) { - for (const auto& particle : trueElWithDau) { - histos.fill(HIST("MCWithDau/hElPt"), particle.pt()); + // Collision Qa + histos.fill(HIST("McCollision/hBCId"), collision.bcId()); + histos.fill(HIST("McCollision/hGeneratorsID"), collision.generatorsID()); + histos.fill(HIST("McCollision/hPosX"), collision.posX()); + histos.fill(HIST("McCollision/hPosY"), collision.posY()); + histos.fill(HIST("McCollision/hPosZ"), collision.posZ()); + histos.fill(HIST("McCollision/hT"), collision.t()); + histos.fill(HIST("McCollision/hWeight"), collision.weight()); + histos.fill(HIST("McCollision/hImpactParameter"), collision.impactParameter()); + histos.fill(HIST("McCollision/hRventPlaneAngle"), collision.eventPlaneAngle()); + + // Group with collision + auto trueElectronsGrouped = trueElectrons->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + auto trueMuonsGrouped = trueMuons->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + auto truePionsGrouped = truePions->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + auto trueKaonsGrouped = trueKaons->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + auto trueProtonsGrouped = trueProtons->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + auto trueK0ShortGrouped = trueK0Short->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + auto trueLambdasGrouped = trueLambdas->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + auto trueXiMinusGrouped = trueXiMinus->sliceByCached(aod::mcparticle::mcCollisionId, collision.globalIndex(), cache); + + // All Particles + for (const auto& particle : particles) { + histos.fill(HIST("McParticle/hCollisionId"), particle.mcCollisionId()); + histos.fill(HIST("McParticle/hPdgCode"), particle.pdgCode()); + histos.fill(HIST("McParticle/hStatusCode"), particle.statusCode()); + histos.fill(HIST("McParticle/hFlags"), particle.flags()); + histos.fill(HIST("McParticle/hWeight"), particle.weight()); + histos.fill(HIST("McParticle/hVx"), particle.vx()); + histos.fill(HIST("McParticle/hVy"), particle.vy()); + histos.fill(HIST("McParticle/hVz"), particle.vz()); + histos.fill(HIST("McParticle/hVt"), particle.vt()); + histos.fill(HIST("McParticle/hPhi"), particle.phi()); + histos.fill(HIST("McParticle/hEta"), particle.eta()); + histos.fill(HIST("McParticle/hRapidity"), particle.y()); + histos.fill(HIST("McParticle/hPx"), particle.px()); + histos.fill(HIST("McParticle/hPy"), particle.py()); + histos.fill(HIST("McParticle/hPz"), particle.pz()); + histos.fill(HIST("McParticle/hPt"), particle.pt()); + histos.fill(HIST("McParticle/hP"), particle.p()); + histos.fill(HIST("McParticle/hE"), particle.e()); + for (const auto& motherParticleId : particle.mothersIds()) { + histos.fill(HIST("McParticle/hMothersIds"), motherParticleId); + } + for (const auto& dauParticleId : particle.daughtersIds()) { + histos.fill(HIST("McParticle/hDaughtersIds"), dauParticleId); + } } - for (const auto& particle : trueMuWithDau) { - histos.fill(HIST("MCWithDau/hMuPt"), particle.pt()); + + // Particles processed in OTF + for (const auto& particle : trueElectronsGrouped) { + histos.fill(HIST("McParticle/hElPt"), particle.pt()); + } + for (const auto& particle : trueMuonsGrouped) { + histos.fill(HIST("McParticle/hMuPt"), particle.pt()); } - for (const auto& particle : truePiWithDau) { - histos.fill(HIST("MCWithDau/hPiPt"), particle.pt()); + for (const auto& particle : truePionsGrouped) { + histos.fill(HIST("McParticle/hPiPt"), particle.pt()); } - for (const auto& particle : trueKaWithDau) { - histos.fill(HIST("MCWithDau/hKaPt"), particle.pt()); + for (const auto& particle : trueKaonsGrouped) { + histos.fill(HIST("McParticle/hKaPt"), particle.pt()); } - for (const auto& particle : truePrWithDau) { - histos.fill(HIST("MCWithDau/hPrPt"), particle.pt()); + for (const auto& particle : trueProtonsGrouped) { + histos.fill(HIST("McParticle/hPrPt"), particle.pt()); } - for (const auto& particle : particles) { - histos.fill(HIST("MCWithDau/hCollisionId"), particle.mcCollisionId()); - histos.fill(HIST("MCWithDau/hPdgCode"), particle.pdgCode()); - histos.fill(HIST("MCWithDau/hStatusCode"), particle.statusCode()); - histos.fill(HIST("MCWithDau/hFlags"), particle.flags()); - histos.fill(HIST("MCWithDau/hWeight"), particle.weight()); - histos.fill(HIST("MCWithDau/hVx"), particle.vx()); - histos.fill(HIST("MCWithDau/hVy"), particle.vy()); - histos.fill(HIST("MCWithDau/hVz"), particle.vz()); - histos.fill(HIST("MCWithDau/hVt"), particle.vt()); - histos.fill(HIST("MCWithDau/hPhi"), particle.phi()); - histos.fill(HIST("MCWithDau/hEta"), particle.eta()); - histos.fill(HIST("MCWithDau/hRapidity"), particle.y()); - histos.fill(HIST("MCWithDau/hIsAlive"), particle.isAlive()); - histos.fill(HIST("MCWithDau/hIsPrimary"), particle.isPrimary()); - histos.fill(HIST("MCWithDau/hPx"), particle.px()); - histos.fill(HIST("MCWithDau/hPy"), particle.py()); - histos.fill(HIST("MCWithDau/hPz"), particle.pz()); - histos.fill(HIST("MCWithDau/hPt"), particle.pt()); - histos.fill(HIST("MCWithDau/hP"), particle.p()); - histos.fill(HIST("MCWithDau/hE"), particle.e()); - for (const auto& motherParticleId : particle.mothersIds()) { - histos.fill(HIST("MCWithDau/hMothersIds"), motherParticleId); + // Check topology from decayer + for (const auto& particle : trueK0ShortGrouped) { + histos.fill(HIST("K0S/hGeneratedPt"), particle.pt()); + if (particle.has_daughters()) { + histos.fill(HIST("K0S/hHasDecayed"), 1); + auto daughters = particle.daughtersIds(); + if (daughters.size() == NV0Daughters) { + auto dau0 = particles.rawIteratorAt(daughters.front() - particles.offset()); + auto dau1 = particles.rawIteratorAt(daughters.back() - particles.offset()); + + // K0S -> pi+ pi- + const bool k0sDecay = (dau0.pdgCode() == PDG_t::kPiPlus && dau1.pdgCode() == PDG_t::kPiMinus) || + (dau0.pdgCode() == PDG_t::kPiMinus && dau1.pdgCode() == PDG_t::kPiPlus); + + if (k0sDecay) { + auto& positive = dau0.pdgCode() == PDG_t::kPiPlus ? dau0 : dau1; + auto& negative = dau0.pdgCode() == PDG_t::kPiPlus ? dau1 : dau0; + histos.fill(HIST("K0S/hPosDauDecayRadius"), radius(positive), positive.pt()); + histos.fill(HIST("K0S/hNegDauDecayRadius"), radius(negative), negative.pt()); + } + } + } else { + histos.fill(HIST("K0S/hHasDecayed"), 0); + } + } + + // Check topology from decayer + for (const auto& particle : trueLambdasGrouped) { + histos.fill(HIST("Lambda/hGeneratedPt"), particle.pt()); + if (particle.has_daughters()) { + histos.fill(HIST("Lambda/hHasDecayed"), 1); + auto daughters = particle.daughtersIds(); + if (daughters.size() == NV0Daughters) { + auto dau0 = particles.rawIteratorAt(daughters[0] - particles.offset()); + auto dau1 = particles.rawIteratorAt(daughters[1] - particles.offset()); + + // Lambda -> p pi- + const bool lambdaDecay = (dau0.pdgCode() == PDG_t::kProton && dau1.pdgCode() == PDG_t::kPiMinus) || + (dau0.pdgCode() == PDG_t::kPiMinus && dau1.pdgCode() == PDG_t::kProton); + + if (lambdaDecay) { + auto& positive = dau0.pdgCode() == PDG_t::kProton ? dau0 : dau1; + auto& negative = dau0.pdgCode() == PDG_t::kProton ? dau1 : dau0; + histos.fill(HIST("Lambda/hPosDauDecayRadius"), radius(positive), positive.pt()); + histos.fill(HIST("Lambda/hNegDauDecayRadius"), radius(negative), negative.pt()); + } + } + } else { + histos.fill(HIST("Lambda/hHasDecayed"), 0); } - for (const auto& dauParticleId : particle.mothersIds()) { - histos.fill(HIST("MCWithDau/hDaughtersIds"), dauParticleId); + } + + // Check topology from decayer + for (const auto& particle : trueXiMinusGrouped) { + histos.fill(HIST("XiMinus/hGeneratedPt"), particle.pt()); + histos.fill(HIST("XiMinus/hXiVt"), particle.vt()); + if (particle.has_daughters()) { + histos.fill(HIST("XiMinus/hHasDecayed"), 1); + auto daughters = particle.daughtersIds(); + if (daughters.size() == NCascadeDaughters) { + auto dau0 = particles.rawIteratorAt(daughters.front() - particles.offset()); + auto dau1 = particles.rawIteratorAt(daughters.back() - particles.offset()); + + // Xi- -> Lambda pi- + const bool xiDecay = (dau0.pdgCode() == PDG_t::kLambda0 && dau1.pdgCode() == PDG_t::kPiMinus) || + (dau0.pdgCode() == PDG_t::kPiMinus && dau1.pdgCode() == PDG_t::kLambda0); + + if (xiDecay) { + auto& v0 = dau0.pdgCode() == PDG_t::kLambda0 ? dau0 : dau1; + auto& bachelor = dau0.pdgCode() == PDG_t::kLambda0 ? dau1 : dau0; + histos.fill(HIST("XiMinus/hBachDauDecayRadius"), radius(bachelor), bachelor.pt()); + histos.fill(HIST("XiMinus/hV0DauDecayRadius"), radius(v0), v0.pt()); + histos.fill(HIST("XiMinus/hBachVt"), v0.vt()); + histos.fill(HIST("XiMinus/hV0Vt"), bachelor.vt()); + + // Lambda -> p pi- + if (v0.has_daughters()) { + auto v0daughters = v0.daughtersIds(); + if (v0daughters.size() == NV0Daughters) { + auto v0dau0 = particles.rawIteratorAt(v0daughters.front() - particles.offset()); + auto v0dau1 = particles.rawIteratorAt(v0daughters.back() - particles.offset()); + const bool lambdaDecay = (v0dau0.pdgCode() == PDG_t::kProton && v0dau1.pdgCode() == PDG_t::kPiMinus) || + (v0dau0.pdgCode() == PDG_t::kPiMinus && v0dau1.pdgCode() == PDG_t::kProton); + if (lambdaDecay) { + auto& positive = v0dau0.pdgCode() == PDG_t::kProton ? v0dau0 : v0dau1; + auto& negative = v0dau0.pdgCode() == PDG_t::kProton ? v0dau1 : v0dau0; + histos.fill(HIST("XiMinus/hPosDauDecayRadius"), radius(positive), positive.pt()); + histos.fill(HIST("XiMinus/hNegDauDecayRadius"), radius(negative), negative.pt()); + histos.fill(HIST("XiMinus/hPosVt"), positive.vt()); + histos.fill(HIST("XiMinus/hNegVt"), negative.vt()); + } + } + } + } + } + } else { + histos.fill(HIST("XiMinus/hHasDecayed"), 0); } } } - PROCESS_SWITCH(Alice3DecayerQA, processMC, "fill MC-only histograms", false); - PROCESS_SWITCH(Alice3DecayerQA, processMCWithDau, "fill MC-with-dau histograms", true); + PROCESS_SWITCH(Alice3DecayerQa, process, "fill MC-with-dau histograms", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& ctx) { - return WorkflowSpec{adaptAnalysisTask(ctx)}; + return WorkflowSpec{adaptAnalysisTask(ctx)}; } diff --git a/ALICE3/Tasks/alice3-dq-efficiency.cxx b/ALICE3/Tasks/alice3DqEfficiency.cxx similarity index 64% rename from ALICE3/Tasks/alice3-dq-efficiency.cxx rename to ALICE3/Tasks/alice3DqEfficiency.cxx index 99dcd281727..d90a6f0b320 100644 --- a/ALICE3/Tasks/alice3-dq-efficiency.cxx +++ b/ALICE3/Tasks/alice3DqEfficiency.cxx @@ -8,10 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// alexander.tiekoetter@cern.ch -// Configurable workflow for running several DQ or other PWG analyses +/// \author Ionut Cristian Arsene , Oslo +/// \author Alexander Tiekoetter , Muenster +/// \brief Skimming Task for DQ Table Maker +/// \file alice3DqEfficiency.cxx #include "PWGDQ/Core/AnalysisCompositeCut.h" #include "PWGDQ/Core/AnalysisCut.h" @@ -23,12 +23,10 @@ #include "PWGDQ/Core/MixingHandler.h" #include "PWGDQ/Core/MixingLibrary.h" #include "PWGDQ/Core/VarManager.h" -#include "PWGDQ/DataModel/ReducedInfoTables.h" -#include "PWGDQ/DataModel/ReducedTablesAlice3.h" +#include "ALICE3/DataModel/ReducedTablesAlice3.h" #include "Common/Core/TableHelper.h" -#include #include #include #include @@ -41,8 +39,11 @@ #include #include +#include #include #include +#include +#include #include #include #include @@ -75,14 +76,14 @@ DECLARE_SOA_COLUMN(Rapee, rapee, float); DECLARE_SOA_COLUMN(Phiee, phiee, float); DECLARE_SOA_COLUMN(Ptee, ptee, float); DECLARE_SOA_COLUMN(Lxyee, lxyee, float); -DECLARE_SOA_COLUMN(LxyeePoleMass, lxyeepolemass, float); +DECLARE_SOA_COLUMN(LxyeePoleMass, lxyeePoleMass, float); DECLARE_SOA_COLUMN(Lzee, lzee, float); -DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); -DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); -DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); -DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContribJPsi2ee, float); -DECLARE_SOA_COLUMN(AmbiguousInBunchPairs, AmbiguousJpsiPairsInBunch, bool); -DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, AmbiguousJpsiPairsOutOfBunch, bool); +DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0A, float); +DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0C, float); +DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0M, float); +DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContrib, float); +DECLARE_SOA_COLUMN(AmbiguousInBunchPairs, ambiguousInBunchPairs, bool); +DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, ambiguousOutOfBunchPairs, bool); DECLARE_SOA_COLUMN(Corrassoc, corrassoc, bool); DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); @@ -107,55 +108,61 @@ DECLARE_SOA_TABLE(OniaMCTruth, "AOD", "MCTRUTHONIA", dqanalysisflags::OniaPt, dq // TODO: USE PROPER TABLES -using MyEvents = soa::Join; -using MyEventsSelected = soa::Join; -using MyEventsVtxCov = soa::Join; -using MyEventsVtxCovSelected = soa::Join; +using MyEvents = soa::Join; +using MyEventsVtxCov = soa::Join; +using MyEventsVtxCovSelected = soa::Join; using MyBarrelAssocs = soa::Join; using MyBarrelAssocsPrefilter = soa::Join; -using MyBarrelTracks = soa::Join; -using MyBarrelTracksWithCov = soa::Join; -using MyBarrelTracksWithCovWithAmbiguities = soa::Join; -constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::ReducedEvent; -constexpr static uint32_t gkEventFillMapWithCov = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventVtxCov; +constexpr static uint32_t GkEventFillMap = VarManager::ObjTypes::ReducedEvent; +constexpr static uint32_t GkEventFillMapWithCov = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventVtxCov; + +constexpr static uint32_t GkTrackFillMapWithCov = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID; +constexpr static uint32_t GkTrackFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelPID; -constexpr static uint32_t gkTrackFillMapWithCov = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID; -constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelPID; +namespace dqefficiency_helpers +{ +inline float* varValues() { return static_cast(VarManager::fgValues); } +inline TString* varNames() { return static_cast(VarManager::fgVariableNames); } +inline TString* varUnits() { return static_cast(VarManager::fgVariableUnits); } +} // namespace dqefficiency_helpers // Global function used to define needed histogram classes -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups); // defines histograms for all tasks +void defineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups); // defines histograms for all tasks -constexpr int TWO_PRONG = 2; -constexpr int THREE_PRONG = 3; +constexpr int TwoProng = 2; +constexpr int ThreeProng = 3; // Analysis task that produces event decisions and the Hash table used in event mixing -struct AnalysisEventSelection { +struct Alice3DqEfficiencyAnalysisEventSelection { Produces eventSel; Produces hash; OutputObj fOutputList{"output"}; - Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; - Configurable fConfigEventCuts{"cfgEventCuts", "eventStandard", "Event selection"}; - Configurable fConfigEventCutsJSON{"cfgEventCutsJSON", "", "Additional event cuts specified in JSON format"}; - Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddEventMCHistogram{"cfgAddEventMCHistogram", "generator", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Add event histograms defined via JSON formatting (see HistogramsLibrary)"}; + Configurable cfgMixingVars{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; + Configurable cfgEventCuts{"cfgEventCuts", "eventStandard", "Event selection"}; + Configurable cfgEventCutsJSON{"cfgEventCutsJSON", "", "Additional event cuts specified in JSON format"}; + Configurable cfgQA{"cfgQA", false, "If true, fill QA histograms"}; + Configurable cfgAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms"}; + Configurable cfgAddEventMCHistogram{"cfgAddEventMCHistogram", "generator", "Comma separated list of histograms"}; + Configurable cfgAddJSONHistograms{"cfgAddJSONHistograms", "", "Add event histograms defined via JSON formatting (see HistogramsLibrary)"}; HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; - AnalysisCompositeCut* fEventCut; + AnalysisCompositeCut* fEventCut = nullptr; std::map fSelMap; // key: reduced event global index, value: event selection decision @@ -167,7 +174,7 @@ struct AnalysisEventSelection { VarManager::SetDefaultVarNames(); fEventCut = new AnalysisCompositeCut(true); - TString eventCutStr = fConfigEventCuts.value; + TString eventCutStr = cfgEventCuts.value; if (eventCutStr != "") { AnalysisCut* cut = dqcuts::GetAnalysisCut(eventCutStr.Data()); if (cut != nullptr) { @@ -175,7 +182,7 @@ struct AnalysisEventSelection { } } // Additional cuts via JSON - TString eventCutJSONStr = fConfigEventCutsJSON.value; + TString eventCutJSONStr = cfgEventCutsJSON.value; if (eventCutJSONStr != "") { std::vector jsonCuts = dqcuts::GetCutsFromJSON(eventCutJSONStr.Data()); for (const auto& cutIt : jsonCuts) { @@ -185,18 +192,18 @@ struct AnalysisEventSelection { VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill - if (fConfigQA) { + if (cfgQA) { fHistMan = new HistogramManager("analysisHistos", "", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); - DefineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;", fConfigAddEventHistogram.value.data()); - DefineHistograms(fHistMan, "EventsMC", fConfigAddEventMCHistogram.value.data()); - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // aditional histograms via JSON + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); + defineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;", cfgAddEventHistogram.value.data()); + defineHistograms(fHistMan, "EventsMC", cfgAddEventMCHistogram.value.data()); + dqhistograms::AddHistogramsFromJSON(fHistMan, cfgAddJSONHistograms.value.c_str()); // aditional histograms via JSON VarManager::SetUseVars(fHistMan->GetUsedVars()); fOutputList.setObject(fHistMan->GetMainHistogramList()); } - TString mixVarsString = fConfigMixingVariables.value; + TString mixVarsString = cfgMixingVars.value; std::unique_ptr objArray(mixVarsString.Tokenize(",")); if (objArray->GetEntries() > 0) { fMixHandler = new MixingHandler("mixingHandler", "mixing handler"); @@ -207,32 +214,32 @@ struct AnalysisEventSelection { } } - void runEventSelection(MyEventsVtxCov const& events, ReducedA3MCEvents const& mcEvents) + void runEventSelection(MyEventsVtxCov const& events, ReA3MCEvents const& mcEvents) { fSelMap.clear(); for (const auto& event : events) { // Reset the fValues array and fill event observables VarManager::ResetValues(0, VarManager::kNEventWiseVariables); - VarManager::FillEventAlice3(event); - if (event.has_reducedA3MCEvent()) { - VarManager::FillEventAlice3(event.reducedA3MCEvent()); + VarManager::FillEventAlice3(event); + if (event.has_reA3MCEvent()) { + VarManager::FillEventAlice3(event.reA3MCEvent()); } bool decision = false; // if QA is requested fill histograms before event selections - if (fConfigQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); // automatically fill all the histograms in the class Event + if (cfgQA) { + fHistMan->FillHistClass("Event_BeforeCuts", dqefficiency_helpers::varValues()); // automatically fill all the histograms in the class Event } - if (fEventCut->IsSelected(VarManager::fgValues)) { - if (fConfigQA) { - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + if (fEventCut->IsSelected(dqefficiency_helpers::varValues())) { + if (cfgQA) { + fHistMan->FillHistClass("Event_AfterCuts", dqefficiency_helpers::varValues()); } decision = true; } fSelMap[event.globalIndex()] = decision; if (fMixHandler != nullptr) { - int hh = fMixHandler->FindEventCategory(VarManager::fgValues); + int hh = fMixHandler->FindEventCategory(dqefficiency_helpers::varValues()); hash(hh); } } @@ -241,8 +248,8 @@ struct AnalysisEventSelection { // Reset the fValues array and fill event observables VarManager::ResetValues(0, VarManager::kNEventWiseVariables); VarManager::FillEventAlice3(event); - if (fConfigQA) { - fHistMan->FillHistClass("EventsMC", VarManager::fgValues); + if (cfgQA) { + fHistMan->FillHistClass("EventsMC", dqefficiency_helpers::varValues()); } } } @@ -250,7 +257,7 @@ struct AnalysisEventSelection { void publishSelections(MyEventsVtxCov const& events) { // publish the table - uint32_t evSel = static_cast(0); + auto evSel = static_cast(0); for (const auto& event : events) { evSel = 0; if (fSelMap[event.globalIndex()]) { // event passed the user cuts @@ -260,7 +267,7 @@ struct AnalysisEventSelection { } } - void processSkimmed(MyEventsVtxCov const& events, aod::ReducedA3MCEvents const& mcEvents) + void processSkimmed(MyEventsVtxCov const& events, aod::ReA3MCEvents const& mcEvents) { runEventSelection(events, mcEvents); publishSelections(events); @@ -271,29 +278,29 @@ struct AnalysisEventSelection { // do nothing } - PROCESS_SWITCH(AnalysisEventSelection, processSkimmed, "Run event selection on DQ skimmed events", false); - PROCESS_SWITCH(AnalysisEventSelection, processDummy, "Dummy function", true); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisEventSelection, processSkimmed, "Run event selection on DQ skimmed events", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisEventSelection, processDummy, "Dummy function", true); }; // Produces a table with barrel track decisions (joinable to the ReducedA3TracksAssociations) // Here one should add all the track cuts needed through the workflow (e.g. cuts for same-even pairing, electron prefiltering, track for dilepton-track correlations) -struct AnalysisTrackSelection { +struct Alice3DqEfficiencyAnalysisTrackSelection { Produces trackSel; Produces trackAmbiguities; OutputObj fOutputList{"output"}; - Configurable fConfigCuts{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; - Configurable fConfigCutsJSON{"cfgBarrelTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; - Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; + Configurable cfgTrackCuts{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; + Configurable cfgBarrelTrackCutsJSON{"cfgBarrelTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; + Configurable cfgQA{"cfgQA", false, "If true, fill QA histograms"}; + Configurable cfgAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; + Configurable cfgAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + Configurable cfgPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; - Configurable fConfigMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; - Configurable fConfigMCSignalsJSON{"cfgTrackMCsignalsJSON", "", "Additional list of MC signals via JSON"}; + Configurable cfgTrackMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; + Configurable cfgTrackMCsignalsJSON{"cfgTrackMCsignalsJSON", "", "Additional list of MC signals via JSON"}; - HistogramManager* fHistMan; - std::vector fTrackCuts; + HistogramManager* fHistMan = nullptr; + std::vector fTrackCuts; std::vector fMCSignals; // list of signals to be checked std::vector fHistNamesReco; std::vector fHistNamesMCMatched; @@ -308,7 +315,7 @@ struct AnalysisTrackSelection { } VarManager::SetDefaultVarNames(); - TString cutNamesStr = fConfigCuts.value; + TString cutNamesStr = cfgTrackCuts.value; if (!cutNamesStr.IsNull()) { std::unique_ptr objArray(cutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { @@ -316,16 +323,16 @@ struct AnalysisTrackSelection { } } // add extra cuts from JSON - TString addTrackCutsStr = fConfigCutsJSON.value; + TString addTrackCutsStr = cfgBarrelTrackCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (const auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(t); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill - TString configSigNamesStr = fConfigMCSignals.value; + TString configSigNamesStr = cfgTrackMCSignals.value; std::unique_ptr sigNamesArray(configSigNamesStr.Tokenize(",")); // Setting the MC signals for (int isig = 0; isig < sigNamesArray->GetEntries(); ++isig) { @@ -338,7 +345,7 @@ struct AnalysisTrackSelection { } } // Add the MCSignals from the JSON config - TString addMCSignalsStr = fConfigMCSignalsJSON.value; + TString addMCSignalsStr = cfgTrackMCsignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); for (const auto& mcIt : addMCSignals) { @@ -349,10 +356,10 @@ struct AnalysisTrackSelection { } } - if (fConfigQA) { + if (cfgQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); // Configure histogram classes for each track cut; // Add histogram classes for each track cut and for each requested MC signal (reconstructed tracks with MC truth) @@ -371,17 +378,17 @@ struct AnalysisTrackSelection { } } - DefineHistograms(fHistMan, histClasses.Data(), fConfigAddTrackHistogram.value.data()); - if (fConfigPublishAmbiguity) { - DefineHistograms(fHistMan, "TrackBarrel_AmbiguityInBunch;TrackBarrel_AmbiguityOutOfBunch;", "ambiguity"); + defineHistograms(fHistMan, histClasses.Data(), cfgAddTrackHistogram.value.data()); + if (cfgPublishAmbiguity) { + defineHistograms(fHistMan, "TrackBarrel_AmbiguityInBunch;TrackBarrel_AmbiguityOutOfBunch;", "ambiguity"); } - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + dqhistograms::AddHistogramsFromJSON(fHistMan, cfgAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); } } - void runTrackSelection(ReducedA3TracksAssoc const& assocs, MyEventsVtxCovSelected const& /*events*/, MyBarrelTracksWithCov const& tracks, ReducedA3MCEvents const& /*eventsMC*/, ReducedA3MCTracks const& tracksMC) + void runTrackSelection(ReducedA3TracksAssoc const& assocs, MyEventsVtxCovSelected const& /*events*/, MyBarrelTracksWithCov const& tracks, ReA3MCEvents const& /*eventsMC*/, ReA3MCTracks const& tracksMC) { fNAssocsInBunch.clear(); fNAssocsOutOfBunch.clear(); @@ -391,7 +398,7 @@ struct AnalysisTrackSelection { // Loop over associations for (const auto& assoc : assocs) { - auto event = assoc.template reducedA3event_as(); + auto event = assoc.template reA3Event_as(); if (!event.isEventSelected_bit(0)) { trackSel(0); continue; @@ -399,38 +406,38 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); // fill event information which might be needed in histograms/cuts that combine track and event properties - VarManager::FillEventAlice3(event); - if (event.has_reducedA3MCEvent()) { - VarManager::FillEventAlice3(event.reducedA3MCEvent()); + VarManager::FillEventAlice3(event); + if (event.has_reA3MCEvent()) { + VarManager::FillEventAlice3(event.reA3MCEvent()); } - auto track = assoc.template reducedA3track_as(); - VarManager::FillTrackAlice3(track); + auto track = assoc.template reA3Track_as(); + VarManager::FillTrackAlice3(track); // compute quantities which depend on the associated collision, such as DCA - VarManager::FillTrackCollision(track, event); + VarManager::FillTrackCollision(track, event); bool isCorrectAssoc = false; - if (track.has_reducedA3MCTrack()) { - auto trackMC = track.reducedA3MCTrack(); - auto eventMCfromTrack = trackMC.reducedA3MCEvent(); - if (event.has_reducedA3MCEvent()) { - isCorrectAssoc = (eventMCfromTrack.globalIndex() == event.reducedA3MCEvent().globalIndex()); + if (track.has_reA3MCTrack()) { + auto trackMC = track.reA3MCTrack(); + auto eventMCfromTrack = trackMC.reA3MCEvent(); + if (event.has_reA3MCEvent()) { + isCorrectAssoc = (eventMCfromTrack.globalIndex() == event.reA3MCEvent().globalIndex()); } VarManager::FillTrackMC(tracksMC, trackMC); VarManager::FillResolutions(trackMC, track); } - if (fConfigQA) { - fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", VarManager::fgValues); + if (cfgQA) { + fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", dqefficiency_helpers::varValues()); } int iCut = 0; - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqefficiency_helpers::varValues())) { filterMap |= (static_cast(1) << iCut); - if (fConfigQA) { - fHistMan->FillHistClass(fHistNamesReco[iCut], VarManager::fgValues); + if (cfgQA) { + fHistMan->FillHistClass(fHistNamesReco[iCut], dqefficiency_helpers::varValues()); } } } // end loop over cuts @@ -438,19 +445,19 @@ struct AnalysisTrackSelection { // compute MC matching decisions and fill histograms for matched associations int isig = 0; - if (filterMap > 0 && track.has_reducedA3MCTrack()) { + if (filterMap > 0 && track.has_reA3MCTrack()) { // loop over all MC signals for (auto sig = fMCSignals.begin(); sig != fMCSignals.end(); sig++, isig++) { // check if this MC signal is matched - if ((*sig)->CheckSignal(true, track.reducedA3MCTrack())) { + if ((*sig)->CheckSignal(true, track.reA3MCTrack())) { // mcDecision |= (static_cast(1) << isig); // loop over cuts and fill histograms for the cuts that are fulfilled for (unsigned int icut = 0; icut < fTrackCuts.size(); icut++) { - if (filterMap & (static_cast(1) << icut)) { + if ((filterMap & (static_cast(1) << icut)) != 0u) { if (isCorrectAssoc) { - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), dqefficiency_helpers::varValues()); } } } // end loop over cuts @@ -459,10 +466,10 @@ struct AnalysisTrackSelection { } // end if (filterMap > 0) // count the number of associations per track - if (fConfigPublishAmbiguity && filterMap > 0) { + if (cfgPublishAmbiguity && filterMap > 0) { if (event.isEventSelected_bit(1)) { // for this track, count the number of associated collisions with in-bunch pileup and out of bunch associations - if (fNAssocsInBunch.find(track.globalIndex()) == fNAssocsInBunch.end()) { + if (!fNAssocsInBunch.contains(track.globalIndex())) { std::vector evVector = {event.globalIndex()}; fNAssocsInBunch[track.globalIndex()] = evVector; } else { @@ -470,7 +477,7 @@ struct AnalysisTrackSelection { evVector.push_back(event.globalIndex()); } } else { - if (fNAssocsOutOfBunch.find(track.globalIndex()) == fNAssocsOutOfBunch.end()) { + if (!fNAssocsOutOfBunch.contains(track.globalIndex())) { std::vector evVector = {event.globalIndex()}; fNAssocsOutOfBunch[track.globalIndex()] = evVector; } else { @@ -484,17 +491,17 @@ struct AnalysisTrackSelection { // QA the collision-track associations // TODO: some tracks can be associated to both collisions that have in bunch pileup and collisions from different bunches // So one could QA these tracks separately - if (fConfigPublishAmbiguity) { - if (fConfigQA) { + if (cfgPublishAmbiguity) { + if (cfgQA) { for (const auto& [trackIdx, evIndices] : fNAssocsInBunch) { if (evIndices.size() == 1) { continue; } auto track = tracks.rawIteratorAt(trackIdx); VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); - VarManager::FillTrackAlice3(track); + VarManager::FillTrackAlice3(track); VarManager::fgValues[VarManager::kBarrelNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", dqefficiency_helpers::varValues()); } // end loop over in-bunch ambiguous tracks for (const auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { @@ -503,20 +510,20 @@ struct AnalysisTrackSelection { } auto track = tracks.rawIteratorAt(trackIdx); VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); - VarManager::FillTrackAlice3(track); + VarManager::FillTrackAlice3(track); VarManager::fgValues[VarManager::kBarrelNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", dqefficiency_helpers::varValues()); } // end loop over out-of-bunch ambiguous tracks } // publish the ambiguity table for (const auto& track : tracks) { int8_t nInBunch = 0; - if (fNAssocsInBunch.find(track.globalIndex()) != fNAssocsInBunch.end()) { + if (!fNAssocsInBunch.contains(track.globalIndex())) { nInBunch = fNAssocsInBunch[track.globalIndex()].size(); } int8_t nOutOfBunch = 0; - if (fNAssocsOutOfBunch.find(track.globalIndex()) != fNAssocsOutOfBunch.end()) { + if (!fNAssocsOutOfBunch.contains(track.globalIndex())) { nOutOfBunch = fNAssocsOutOfBunch[track.globalIndex()].size(); } trackAmbiguities(nInBunch, nOutOfBunch); @@ -524,7 +531,7 @@ struct AnalysisTrackSelection { } } // end runTrackSelection() - void processSkimmedWithCov(ReducedA3TracksAssoc const& assocs, MyEventsVtxCovSelected const& events, MyBarrelTracksWithCov const& tracks, ReducedA3MCEvents const& eventsMC, ReducedA3MCTracks const& tracksMC) + void processSkimmedWithCov(ReducedA3TracksAssoc const& assocs, MyEventsVtxCovSelected const& events, MyBarrelTracksWithCov const& tracks, ReA3MCEvents const& eventsMC, ReA3MCTracks const& tracksMC) { runTrackSelection(assocs, events, tracks, eventsMC, tracksMC); } @@ -534,26 +541,26 @@ struct AnalysisTrackSelection { // do nothing } - PROCESS_SWITCH(AnalysisTrackSelection, processSkimmedWithCov, "Run barrel track selection on DQ skimmed tracks w/ cov matrix associations", false); - PROCESS_SWITCH(AnalysisTrackSelection, processDummy, "Dummy function", true); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisTrackSelection, processSkimmedWithCov, "Run barrel track selection on DQ skimmed tracks w/ cov matrix associations", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisTrackSelection, processDummy, "Dummy function", true); }; -struct AnalysisPrefilterSelection { +struct Alice3DqEfficiencyAnalysisPrefilterSelection { Produces prefilter; // joinable with ReducedA3TracksAssoc // Configurables - Configurable fConfigPrefilterTrackCut{"cfgPrefilterTrackCut", "", "Prefilter track cut"}; - Configurable fConfigPrefilterPairCut{"cfgPrefilterPairCut", "", "Prefilter pair cut"}; - Configurable fConfigTrackCuts{"cfgTrackCuts", "", "Track cuts for which to run the prefilter"}; + Configurable cfgPrefilterTrackCut{"cfgPrefilterTrackCut", "", "Prefilter track cut"}; + Configurable cfgPrefilterPairCut{"cfgPrefilterPairCut", "", "Prefilter pair cut"}; + Configurable cfgTrackCuts{"cfgTrackCuts", "", "Track cuts for which to run the prefilter"}; // Track related options - Configurable fPropTrack{"cfgPropTrack", false, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; + Configurable cfgPropTrack{"cfgPropTrack", false, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; std::map fPrefilterMap; - AnalysisCompositeCut* fPairCut; - uint32_t fPrefilterMask; - int fPrefilterCutBit; + AnalysisCompositeCut* fPairCut = nullptr; + uint32_t fPrefilterMask = 0; + int fPrefilterCutBit = -1; - PresliceUnsorted trackAssocsPerCollision = aod::reducedA3track_association::reducedA3eventId; + PresliceUnsorted trackAssocsPerCollision = aod::reduceda3trackassociation::reA3EventId; void init(o2::framework::InitContext& context) { @@ -563,7 +570,7 @@ struct AnalysisPrefilterSelection { bool runPrefilter = true; // get the list of track cuts to be prefiltered - TString trackCutsStr = fConfigTrackCuts.value; + TString trackCutsStr = cfgTrackCuts.value; TObjArray* objArrayTrackCuts = nullptr; if (!trackCutsStr.IsNull()) { objArrayTrackCuts = trackCutsStr.Tokenize(","); @@ -575,7 +582,7 @@ struct AnalysisPrefilterSelection { runPrefilter = false; } // get the cut to be used as loose selection - TString prefilterTrackCutStr = fConfigPrefilterTrackCut.value; + TString prefilterTrackCutStr = cfgPrefilterTrackCut.value; if (prefilterTrackCutStr.IsNull()) { LOG(warn) << " No prefilter loose selection specified! Prefilter will not be run"; runPrefilter = false; @@ -587,10 +594,10 @@ struct AnalysisPrefilterSelection { // get the list of cuts that were computed in the barrel track-selection task and create a bit mask // to mark just the ones we want to apply a prefilter on string trackCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", trackCuts, false); + getTaskOptionValue(context, "alice3-dq-efficiency-analysis-track-selection", "cfgTrackCuts", trackCuts, false); TString allTrackCutsStr = trackCuts; // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); + getTaskOptionValue(context, "alice3-dq-efficiency-analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); TString addTrackCutsStr = trackCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); @@ -611,13 +618,13 @@ struct AnalysisPrefilterSelection { if (objArrayTrackCuts->FindObject(tempStr.Data()) != nullptr) { fPrefilterMask |= (static_cast(1) << icut); } - if (tempStr.CompareTo(fConfigPrefilterTrackCut.value) == 0) { + if (tempStr.CompareTo(cfgPrefilterTrackCut.value) == 0) { fPrefilterCutBit = icut; } } // setup the prefilter pair cut fPairCut = new AnalysisCompositeCut(true); - TString pairCutStr = fConfigPrefilterPairCut.value; + TString pairCutStr = cfgPrefilterPairCut.value; if (!pairCutStr.IsNull()) { fPairCut = dqcuts::GetCompositeCut(pairCutStr.Data()); } @@ -640,8 +647,8 @@ struct AnalysisPrefilterSelection { } for (const auto& [assoc1, assoc2] : o2::soa::combinations(assocs, assocs)) { - auto track1 = assoc1.template reducedA3track_as(); - auto track2 = assoc2.template reducedA3track_as(); + auto track1 = assoc1.template reA3Track_as(); + auto track2 = assoc2.template reA3Track_as(); // NOTE: here we restrict to just pairs of opposite sign (conversions), but in principle this can be made // a configurable and check also same-sign pairs (track splitting) @@ -655,21 +662,21 @@ struct AnalysisPrefilterSelection { bool track1Loose = assoc1.isBarrelSelected_bit(fPrefilterCutBit); bool track2Loose = assoc2.isBarrelSelected_bit(fPrefilterCutBit); - if (!((track1Candidate > 0 && track2Loose) || (track2Candidate > 0 && track1Loose))) { + if ((track1Candidate == 0 || !track2Loose) && (track2Candidate == 0 || !track1Loose)) { continue; } // compute pair quantities - VarManager::FillPairAlice3(track1, track2); - if (fPropTrack) { - VarManager::FillPairCollision(event, track1, track2); + VarManager::FillPairAlice3(track1, track2); + if (cfgPropTrack) { + VarManager::FillPairCollision(event, track1, track2); } // if the pair fullfils the criteria, add an entry into the prefilter map for the two tracks - if (fPairCut->IsSelected(VarManager::fgValues)) { - if (fPrefilterMap.find(track1.globalIndex()) == fPrefilterMap.end() && track1Candidate > 0) { + if (fPairCut->IsSelected(dqefficiency_helpers::varValues())) { + if (!fPrefilterMap.contains(track1.globalIndex()) && track1Candidate > 0) { fPrefilterMap[track1.globalIndex()] = track1Candidate; } - if (fPrefilterMap.find(track2.globalIndex()) == fPrefilterMap.end() && track2Candidate > 0) { + if (!fPrefilterMap.contains(track2.globalIndex()) && track2Candidate > 0) { fPrefilterMap[track2.globalIndex()] = track2Candidate; } } @@ -697,9 +704,9 @@ struct AnalysisPrefilterSelection { } else { for (const auto& assoc : assocs) { // TODO: just use the index from the assoc (no need to cast the whole track) - auto track = assoc.template reducedA3track_as(); + auto track = assoc.template reA3Track_as(); mymap = -1; - if (fPrefilterMap.find(track.globalIndex()) != fPrefilterMap.end()) { + if (!fPrefilterMap.contains(track.globalIndex())) { // NOTE: publish the bitwise negated bits (~), so there will be zeroes for cuts that failed the prefiltering and 1 everywhere else mymap = ~fPrefilterMap[track.globalIndex()]; prefilter(mymap); @@ -715,57 +722,51 @@ struct AnalysisPrefilterSelection { // do nothing } - PROCESS_SWITCH(AnalysisPrefilterSelection, processBarrelSkimmed, "Run Prefilter selection on reduced tracks", false); - PROCESS_SWITCH(AnalysisPrefilterSelection, processDummy, "Do nothing", true); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisPrefilterSelection, processBarrelSkimmed, "Run Prefilter selection on reduced tracks", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisPrefilterSelection, processDummy, "Do nothing", true); }; // Run the same-event pairing // This task assumes that both legs of the resonance fulfill the same cuts (symmetric decay channel) // Runs combinatorics for barrel-barrel combinations // The task implements also process functions for running event mixing -struct AnalysisSameEventPairing { - - Produces dielectronList; - Produces dielectronsExtraList; - Produces dielectronAllList; - Produces MCTruthTableEffi; +struct Alice3DqEfficiencyAnalysisSameEventPairing { - o2::base::MatLayerCylSet* fLUT = nullptr; OutputObj fOutputList{"output"}; struct : ConfigurableGroup { - Configurable track{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; - Configurable pair{"cfgPairCuts", "", "Comma separated list of pair cuts, !!! Use only if you know what you are doing, otherwise leave empty"}; - Configurable MCgenAcc{"cfgMCGenAccCut", "", "cut for MC generated particles acceptance"}; + Configurable cfgTrackCuts{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; + Configurable cfgPairCuts{"cfgPairCuts", "", "Comma separated list of pair cuts, !!! Use only if you know what you are doing, otherwise leave empty"}; + Configurable cfgMCGenAccCut{"cfgMCGenAccCut", "", "cut for MC generated particles acceptance"}; // TODO: Add pair cuts via JSON - } fConfigCuts; + } cfgTrackCuts; - Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + Configurable cfgQA{"cfgQA", false, "If true, fill QA histograms"}; + Configurable cfgAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; + Configurable cfgAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; struct : ConfigurableGroup { - Configurable flatTables{"cfgFlatTables", false, "Produce a single flat tables with all relevant information of the pairs and single tracks"}; - Configurable propToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; - Configurable collisionSystem{"syst", "pp", "Collision system, pp or PbPb"}; - Configurable centerMassEnergy{"energy", 13600, "Center of mass energy in GeV"}; + Configurable cfgFlatTables{"cfgFlatTables", false, "Produce a single flat tables with all relevant information of the pairs and single tracks"}; + Configurable cfgPropToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; + Configurable syst{"syst", "pp", "Collision system, pp or PbPb"}; + Configurable energy{"energy", 13600, "Center of mass energy in GeV"}; } fConfigOptions; struct : ConfigurableGroup { - Configurable genSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; - Configurable genSignalsJSON{"cfgMCGenSignalsJSON", "", "Additional list of MC signals (generated) via JSON"}; - Configurable recSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; - Configurable recSignalsJSON{"cfgMCRecSignalsJSON", "", "Comma separated list of MC signals (reconstructed) via JSON"}; - Configurable skimSignalOnly{"cfgSkimSignalOnly", false, "Configurable to select only matched candidates"}; + Configurable cfgBarrelMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; + Configurable cfgMCGenSignalsJSON{"cfgMCGenSignalsJSON", "", "Additional list of MC signals (generated) via JSON"}; + Configurable cfgBarrelMCRecSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable cfgMCRecSignalsJSON{"cfgMCRecSignalsJSON", "", "Comma separated list of MC signals (reconstructed) via JSON"}; + Configurable cfgSkimSignalOnly{"cfgSkimSignalOnly", false, "Configurable to select only matched candidates"}; } fConfigMC; // Track related options - Configurable fPropTrack{"cfgPropTrack", true, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; + Configurable cfgPropTrack{"cfgPropTrack", true, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; // Filter filterEventSelected = aod::dqanalysisflags::isEventSelected & uint32_t(1); Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; // keep histogram class names in maps, so we don't have to buld their names in the pair loops std::map> fTrackHistNames; @@ -774,17 +775,17 @@ struct AnalysisSameEventPairing { std::vector fGenMCSignals; std::vector fPairCuts; - AnalysisCompositeCut fMCGenAccCut; + AnalysisCompositeCut fMCGenAccCut{}; bool fUseMCGenAccCut = false; - uint32_t fTrackFilterMask; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream - int fNCutsBarrel; - int fNPairCuts; + uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream + int fNCutsBarrel = 0; + int fNPairCuts = 0; bool fHasTwoProngGenMCsignals = false; - bool fEnableBarrelHistos; + bool fEnableBarrelHistos = false; - PresliceUnsorted trackAssocsPerCollision = aod::reducedA3track_association::reducedA3eventId; + PresliceUnsorted trackAssocsPerCollision = aod::reduceda3trackassociation::reA3EventId; void init(o2::framework::InitContext& context) { @@ -798,9 +799,9 @@ struct AnalysisSameEventPairing { // Keep track of all the histogram class names to avoid composing strings in the pairing loop TString histNames = ""; - TString cutNamesStr = fConfigCuts.pair.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); + TString pairCutNamesStr = cfgTrackCuts.cfgPairCuts.value; + if (!pairCutNamesStr.IsNull()) { + std::unique_ptr objArray(pairCutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { fPairCuts.push_back(*dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } @@ -808,19 +809,19 @@ struct AnalysisSameEventPairing { // get the list of cuts for tracks, check that they were played by the barrel selection tasks // and make a mask for active cuts (barrel selection tasks may run more cuts, needed for other analyses) - TString trackCutsStr = fConfigCuts.track.value; + TString trackCutsStr = cfgTrackCuts.cfgTrackCuts.value; TObjArray* objArrayTrackCuts = nullptr; if (!trackCutsStr.IsNull()) { objArrayTrackCuts = trackCutsStr.Tokenize(","); } // Setting the MC rec signal names - TString sigNamesStr = fConfigMC.recSignals.value; + TString sigNamesStr = fConfigMC.cfgBarrelMCRecSignals.value; std::unique_ptr objRecSigArray(sigNamesStr.Tokenize(",")); for (int isig = 0; isig < objRecSigArray->GetEntries(); ++isig) { MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objRecSigArray->At(isig)->GetName()); if (sig) { - if (sig->GetNProngs() != TWO_PRONG) { // NOTE: 2-prong signals required + if (sig->GetNProngs() != TwoProng) { // NOTE: 2-prong signals required continue; } fRecMCSignals.push_back(sig); @@ -828,11 +829,11 @@ struct AnalysisSameEventPairing { } // Add the MCSignals from the JSON config - TString addMCSignalsStr = fConfigMC.recSignalsJSON.value; + TString addMCSignalsStr = fConfigMC.cfgMCRecSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); for (const auto& mcIt : addMCSignals) { - if (mcIt->GetNProngs() != TWO_PRONG) { // NOTE: only 2 prong signals + if (mcIt->GetNProngs() != TwoProng) { // NOTE: only 2 prong signals continue; } fRecMCSignals.push_back(mcIt); @@ -841,10 +842,10 @@ struct AnalysisSameEventPairing { // get the barrel track selection cuts string tempCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + getTaskOptionValue(context, "alice3-dq-efficiency-analysis-track-selection", "cfgTrackCuts", tempCuts, false); TString tempCutsStr = tempCuts; // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + getTaskOptionValue(context, "alice3-dq-efficiency-analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); TString addTrackCutsStr = tempCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); @@ -854,7 +855,7 @@ struct AnalysisSameEventPairing { } // get the mc generated acceptance cut - TString mcGenAccCutStr = fConfigCuts.MCgenAcc.value; + TString mcGenAccCutStr = cfgTrackCuts.cfgMCGenAccCut.value; if (mcGenAccCutStr != "") { AnalysisCut* cut = dqcuts::GetAnalysisCut(mcGenAccCutStr.Data()); if (cut != nullptr) { @@ -881,7 +882,7 @@ struct AnalysisSameEventPairing { Form("PairsBarrelSEPM_%s", objArray->At(icut)->GetName()), Form("PairsBarrelSEPP_%s", objArray->At(icut)->GetName()), Form("PairsBarrelSEMM_%s", objArray->At(icut)->GetName())}; - if (fConfigQA) { + if (cfgQA) { // assign separate hist directories for ambiguous tracks names.push_back(Form("PairsBarrelSEPM_ambiguousInBunch_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelSEPP_ambiguousInBunch_%s", objArray->At(icut)->GetName())); @@ -898,9 +899,9 @@ struct AnalysisSameEventPairing { // if there are pair cuts specified, assign hist directories for each barrel cut - pair cut combination // NOTE: This could possibly lead to large histogram outputs. It is strongly advised to use pair cuts only // if you know what you are doing. - TString cutNamesStr = fConfigCuts.pair.value; - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + TString pairCutHistNamesStr = cfgTrackCuts.cfgPairCuts.value; + if (!pairCutHistNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutHistNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts names = { @@ -921,7 +922,7 @@ struct AnalysisSameEventPairing { Form("PairsBarrelSEPM_%s_%s", objArray->At(icut)->GetName(), sig->GetName()), Form("PairsBarrelSEPP_%s_%s", objArray->At(icut)->GetName(), sig->GetName()), Form("PairsBarrelSEMM_%s_%s", objArray->At(icut)->GetName(), sig->GetName())}; - if (fConfigQA) { + if (cfgQA) { names.push_back(Form("PairsBarrelSEPMCorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); names.push_back(Form("PairsBarrelSEPMIncorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); names.push_back(Form("PairsBarrelSEPM_ambiguousInBunch_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); @@ -944,7 +945,7 @@ struct AnalysisSameEventPairing { // Add histogram classes for each specified MCsignal at the generator level // TODO: create a std::vector of hist classes to be used at Fill time, to avoid using Form in the process function - TString sigGenNamesStr = fConfigMC.genSignals.value; + TString sigGenNamesStr = fConfigMC.cfgBarrelMCGenSignals.value; std::unique_ptr objGenSigArray(sigGenNamesStr.Tokenize(",")); for (int isig = 0; isig < objGenSigArray->GetEntries(); isig++) { MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objGenSigArray->At(isig)->GetName()); @@ -954,11 +955,11 @@ struct AnalysisSameEventPairing { } // Add the MCSignals from the JSON config - TString addMCSignalsGenStr = fConfigMC.genSignalsJSON.value; + TString addMCSignalsGenStr = fConfigMC.cfgMCGenSignalsJSON.value; if (addMCSignalsGenStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsGenStr.Data()); for (const auto& mcIt : addMCSignals) { - if (mcIt->GetNProngs() > TWO_PRONG) { // NOTE: only 2 prong signals + if (mcIt->GetNProngs() > TwoProng) { // NOTE: only 2 prong signals continue; } fGenMCSignals.push_back(mcIt); @@ -970,7 +971,7 @@ struct AnalysisSameEventPairing { if (sig->GetNProngs() == 1) { histNames += Form("MCTruthGen_%s;", sig->GetName()); // TODO: Add these names to a std::vector to avoid using Form in the process function histNames += Form("MCTruthGenSel_%s;", sig->GetName()); - } else if (sig->GetNProngs() == TWO_PRONG) { + } else if (sig->GetNProngs() == TwoProng) { histNames += Form("MCTruthGenPair_%s;", sig->GetName()); histNames += Form("MCTruthGenPairSel_%s;", sig->GetName()); fHasTwoProngGenMCsignals = true; @@ -980,41 +981,35 @@ struct AnalysisSameEventPairing { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.syst), fConfigOptions.energy); // set collision system and center of mass energy - DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + defineHistograms(fHistMan, histNames.Data(), cfgAddSEPHistogram.value.data()); // define all histograms + dqhistograms::AddHistogramsFromJSON(fHistMan, cfgAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); } // Template function to run same event pairing (barrel-barrel) - void runSameEventPairing(MyEventsVtxCovSelected const& events, PresliceUnsorted& preslice, MyBarrelAssocsPrefilter const& assocs, MyBarrelTracksWithCovWithAmbiguities const& /*tracks*/, ReducedA3MCEvents const& /*mcEvents*/, ReducedA3MCTracks const& /*mcTracks*/) + void runSameEventPairing(MyEventsVtxCovSelected const& events, PresliceUnsorted& preslice, MyBarrelAssocsPrefilter const& assocs, MyBarrelTracksWithCovWithAmbiguities const& /*tracks*/, ReA3MCEvents const& /*mcEvents*/, ReA3MCTracks const& /*mcTracks*/) { if (events.size() == 0) { LOG(warning) << "No events in this TF, going to the next one ..."; return; } - TString cutNames = fConfigCuts.track.value; + TString cutNames = cfgTrackCuts.cfgTrackCuts.value; std::map> histNames = fTrackHistNames; std::map> histNamesMC = fBarrelHistNamesMCmatched; int ncuts = fNCutsBarrel; - uint32_t twoTrackFilter = static_cast(0); + auto twoTrackFilter = static_cast(0); int sign1 = 0; int sign2 = 0; - uint32_t mcDecision = static_cast(0); - bool isCorrectAssoc_leg1 = false; - bool isCorrectAssoc_leg2 = false; - dielectronList.reserve(1); - dielectronsExtraList.reserve(1); - - if (fConfigOptions.flatTables.value) { - dielectronAllList.reserve(1); - } + auto mcDecision = static_cast(0); + bool isCorrectAssocLeg1 = false; + bool isCorrectAssocLeg2 = false; for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { @@ -1022,8 +1017,8 @@ struct AnalysisSameEventPairing { } // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEventAlice3(event, VarManager::fgValues); - VarManager::FillEventAlice3(event.reducedA3MCEvent(), VarManager::fgValues); + VarManager::FillEventAlice3(event, dqefficiency_helpers::varValues()); + VarManager::FillEventAlice3(event.reA3MCEvent(), dqefficiency_helpers::varValues()); auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); if (groupedAssocs.size() == 0) { @@ -1034,12 +1029,12 @@ struct AnalysisSameEventPairing { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; - if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue + if (twoTrackFilter == 0u) { // the tracks must have at least one filter bit in common to continue continue; } - auto t1 = a1.template reducedA3track_as(); - auto t2 = a2.template reducedA3track_as(); + auto t1 = a1.template reA3Track_as(); + auto t2 = a2.template reA3Track_as(); sign1 = t1.sign(); sign2 = t2.sign(); // store the ambiguity number of the two dilepton legs in the last 4 digits of the two-track filter @@ -1057,124 +1052,118 @@ struct AnalysisSameEventPairing { } // run MC matching for this pair - int isig = 0; + int iSigMc = 0; mcDecision = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { - if (t1.has_reducedA3MCTrack() && t2.has_reducedA3MCTrack()) { - if ((*sig)->CheckSignal(true, t1.reducedA3MCTrack(), t2.reducedA3MCTrack())) { - mcDecision |= (static_cast(1) << isig); + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { + if (t1.has_reA3MCTrack() && t2.has_reA3MCTrack()) { + if ((*sig)->CheckSignal(true, t1.reA3MCTrack(), t2.reA3MCTrack())) { + mcDecision |= (static_cast(1) << iSigMc); } } } // end loop over MC signals - if (t1.has_reducedA3MCTrack() && t2.has_reducedA3MCTrack()) { - isCorrectAssoc_leg1 = (t1.reducedA3MCTrack().reducedA3MCEvent() == event.reducedA3MCEvent()); - isCorrectAssoc_leg2 = (t2.reducedA3MCTrack().reducedA3MCEvent() == event.reducedA3MCEvent()); + if (t1.has_reA3MCTrack() && t2.has_reA3MCTrack()) { + isCorrectAssocLeg1 = (t1.reA3MCTrack().reA3MCEvent() == event.reA3MCEvent()); + isCorrectAssocLeg2 = (t2.reA3MCTrack().reA3MCEvent() == event.reA3MCEvent()); } - VarManager::FillPairAlice3(t1, t2); - if (fPropTrack) { - VarManager::FillPairCollision(event, t1, t2); - } - /* TODO: Reimplement Pair vertexing when secondary vertexing is available - if constexpr (TTwoProngFitter) { - // VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); - }*/ - if (!fConfigMC.skimSignalOnly || (fConfigMC.skimSignalOnly && mcDecision > 0)) { - dielectronList(event.globalIndex(), VarManager::fgValues[VarManager::kMass], - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], - t1.sign() + t2.sign(), twoTrackFilter, mcDecision); + VarManager::FillPairAlice3(t1, t2); + if (cfgPropTrack) { + VarManager::FillPairCollision(event, t1, t2); } + VarManager::FillPairVertexingAlice3(event, t1, t2, fConfigOptions.cfgPropToPCA); + // Fill histograms bool isAmbiInBunch = false; bool isAmbiOutOfBunch = false; for (int icut = 0; icut < ncuts; icut++) { - if (twoTrackFilter & (static_cast(1) << icut)) { - isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); - isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); - if (sign1 * sign2 < 0) { // +- pairs - fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); // reconstructed, unmatched - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), VarManager::fgValues); // matched signal - if (fConfigQA) { - if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { // correct track-collision association - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][3].Data(), VarManager::fgValues); + if ((twoTrackFilter & (static_cast(1) << icut)) != 0u) { + isAmbiInBunch = ((twoTrackFilter & (static_cast(1) << 28)) != 0u) || ((twoTrackFilter & (static_cast(1) << 29)) != 0u); + isAmbiOutOfBunch = ((twoTrackFilter & (static_cast(1) << 30)) != 0u) || ((twoTrackFilter & (static_cast(1) << 31)) != 0u); + if (sign1 * sign2 < 0) { // +- pairs + fHistMan->FillHistClass(histNames[icut][0].Data(), dqefficiency_helpers::varValues()); // reconstructed, unmatched + for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals + if ((mcDecision & (static_cast(1) << isig)) != 0u) { + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), dqefficiency_helpers::varValues()); // matched signal + if (cfgQA) { + if (isCorrectAssocLeg1 && isCorrectAssocLeg2) { // correct track-collision association + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][3].Data(), dqefficiency_helpers::varValues()); } else { // incorrect track-collision association - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][4].Data(), dqefficiency_helpers::varValues()); } if (isAmbiInBunch) { // ambiguous in bunch - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][5].Data(), VarManager::fgValues); - if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][6].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][5].Data(), dqefficiency_helpers::varValues()); + if (isCorrectAssocLeg1 && isCorrectAssocLeg2) { + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][6].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][7].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][7].Data(), dqefficiency_helpers::varValues()); } } if (isAmbiOutOfBunch) { // ambiguous out of bunch - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][8].Data(), VarManager::fgValues); - if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][9].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][8].Data(), dqefficiency_helpers::varValues()); + if (isCorrectAssocLeg1 && isCorrectAssocLeg2) { + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][9].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][10].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][10].Data(), dqefficiency_helpers::varValues()); } } } } - if (fConfigQA) { + if (cfgQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3].Data(), dqefficiency_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), dqefficiency_helpers::varValues()); } } } } else { if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(histNames[icut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][1].Data(), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][1].Data(), VarManager::fgValues); + if ((mcDecision & (static_cast(1) << isig)) != 0u) { + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][1].Data(), dqefficiency_helpers::varValues()); } } - if (fConfigQA) { + if (cfgQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4].Data(), dqefficiency_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), dqefficiency_helpers::varValues()); } } } else { // -- pairs - fHistMan->FillHistClass(histNames[icut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][2].Data(), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][2].Data(), VarManager::fgValues); + if ((mcDecision & (static_cast(1) << isig)) != 0u) { + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][2].Data(), dqefficiency_helpers::varValues()); } } - if (fConfigQA) { + if (cfgQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][5].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5].Data(), dqefficiency_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), dqefficiency_helpers::varValues()); } } } } for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!(cut.IsSelected(VarManager::fgValues))) // apply pair cuts + if (!(cut.IsSelected(dqefficiency_helpers::varValues()))) { // apply pair cuts continue; + } if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][0].Data(), dqefficiency_helpers::varValues()); } else { if (sign1 > 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][1].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][2].Data(), dqefficiency_helpers::varValues()); } } } // end loop (pair cuts) @@ -1184,18 +1173,15 @@ struct AnalysisSameEventPairing { } // end loop over events } - PresliceUnsorted perReducedMcEvent = aod::reducedA3trackMC::reducedA3MCEventId; + PresliceUnsorted perReducedMcEvent = aod::reduceda3trackmc::reA3MCEventId; - void runMCGenWithGrouping(MyEventsVtxCovSelected const& events, ReducedA3MCEvents const& /*mcEvents*/, ReducedA3MCTracks const& mcTracks) + void runMCGenWithGrouping(MyEventsVtxCovSelected const& events, ReA3MCEvents const& /*mcEvents*/, ReA3MCTracks const& mcTracks) { - [[maybe_unused]] uint32_t mcDecision = 0; - int isig = 0; - for (const auto& mctrack : mcTracks) { VarManager::FillTrackMC(mcTracks, mctrack); // if we have a mc generated acceptance cut, apply it here if (fUseMCGenAccCut) { - if (!fMCGenAccCut.IsSelected(VarManager::fgValues)) { + if (!fMCGenAccCut.IsSelected(dqefficiency_helpers::varValues())) { continue; } } @@ -1204,7 +1190,7 @@ struct AnalysisSameEventPairing { // TODO: Use the mcReducedFlags to select signals for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -1213,52 +1199,47 @@ struct AnalysisSameEventPairing { if (!event.isEventSelected_bit(0)) { continue; } - if (!event.has_reducedA3MCEvent()) { + if (!event.has_reA3MCEvent()) { continue; } for (const auto& track : mcTracks) { - if (track.reducedA3MCEventId() != event.reducedA3MCEventId()) { + if (track.reA3MCEventId() != event.reA3MCEventId()) { continue; } VarManager::FillTrackMC(mcTracks, track); // if we have a mc generated acceptance cut, apply it here if (fUseMCGenAccCut) { - if (!fMCGenAccCut.IsSelected(VarManager::fgValues)) { + if (!fMCGenAccCut.IsSelected(dqefficiency_helpers::varValues())) { continue; } } - auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); - mcDecision = 0; - isig = 0; + auto trackRaw = mcTracks.rawIteratorAt(track.globalIndex()); for (const auto& sig : fGenMCSignals) { - if (sig->CheckSignal(true, track_raw)) { - mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); - MCTruthTableEffi(VarManager::fgValues[VarManager::kMCPt], VarManager::fgValues[VarManager::kMCEta], VarManager::fgValues[VarManager::kMCY], VarManager::fgValues[VarManager::kMCPhi], VarManager::fgValues[VarManager::kMCVz], VarManager::fgValues[VarManager::kMCVtxZ], VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); + if (sig->CheckSignal(true, trackRaw)) { + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } - isig++; } } } // end loop over reconstructed events if (fHasTwoProngGenMCsignals) { for (const auto& [t1, t2] : combinations(mcTracks, mcTracks)) { - auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); - auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); - if (t1_raw.reducedA3MCEventId() == t2_raw.reducedA3MCEventId()) { + auto t1Raw = mcTracks.rawIteratorAt(t1.globalIndex()); + auto t2Raw = mcTracks.rawIteratorAt(t2.globalIndex()); + if (t1Raw.reA3MCEventId() == t2Raw.reA3MCEventId()) { for (const auto& sig : fGenMCSignals) { - if (sig->GetNProngs() != TWO_PRONG) { // NOTE: 2-prong signals required here + if (sig->GetNProngs() != TwoProng) { // NOTE: 2-prong signals required here continue; } - if (sig->CheckSignal(true, t1_raw, t2_raw)) { + if (sig->CheckSignal(true, t1Raw, t2Raw)) { VarManager::FillPairMC(t1, t2); if (fUseMCGenAccCut) { - if (!fMCGenAccCut.IsSelected(VarManager::fgValues)) { + if (!fMCGenAccCut.IsSelected(dqefficiency_helpers::varValues())) { continue; } } - fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -1268,34 +1249,30 @@ struct AnalysisSameEventPairing { if (!event.isEventSelected_bit(0)) { continue; } - if (!event.has_reducedA3MCEvent()) { + if (!event.has_reA3MCEvent()) { continue; } // CURRENTLY ONLY FOR 1-GENERATION 2-PRONG SIGNALS if (fHasTwoProngGenMCsignals) { - auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedA3MCEventId()); + auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reA3MCEventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); for (const auto& [t1, t2] : combinations(groupedMCTracks, groupedMCTracks)) { - auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); - auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); - if (t1_raw.reducedA3MCEventId() == t2_raw.reducedA3MCEventId()) { - mcDecision = 0; - isig = 0; + auto t1Raw = mcTracks.rawIteratorAt(t1.globalIndex()); + auto t2Raw = mcTracks.rawIteratorAt(t2.globalIndex()); + if (t1Raw.reA3MCEventId() == t2Raw.reA3MCEventId()) { for (const auto& sig : fGenMCSignals) { - if (sig->GetNProngs() != TWO_PRONG) { // NOTE: 2-prong signals required here + if (sig->GetNProngs() != TwoProng) { // NOTE: 2-prong signals required here continue; } - if (sig->CheckSignal(true, t1_raw, t2_raw)) { - mcDecision |= (static_cast(1) << isig); + if (sig->CheckSignal(true, t1Raw, t2Raw)) { VarManager::FillPairMC(t1, t2); if (fUseMCGenAccCut) { - if (!fMCGenAccCut.IsSelected(VarManager::fgValues)) { + if (!fMCGenAccCut.IsSelected(dqefficiency_helpers::varValues())) { continue; } } - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } - isig++; } } } @@ -1305,20 +1282,16 @@ struct AnalysisSameEventPairing { void processBarrelOnlySkimmed(MyEventsVtxCovSelected const& events, MyBarrelAssocsPrefilter const& barrelAssocs, - MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, ReducedA3MCEvents const& mcEvents, ReducedA3MCTracks const& mcTracks) + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, ReA3MCEvents const& mcEvents, ReA3MCTracks const& mcTracks) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks); runMCGenWithGrouping(events, mcEvents, mcTracks); } - PresliceUnsorted perReducedMcGenEvent = aod::reducedA3trackMC::reducedA3MCEventId; + PresliceUnsorted perReducedMcGenEvent = aod::reduceda3trackmc::reA3MCEventId; - void processMCGen(soa::Filtered const& events, ReducedA3MCEvents const& /*mcEvents*/, ReducedA3MCTracks const& mcTracks) + void processMCGen(soa::Filtered const& events, ReA3MCEvents const& /*mcEvents*/, ReA3MCTracks const& mcTracks) { - // Fill Generated histograms taking into account all generated tracks - [[maybe_unused]] uint32_t mcDecision = 0; - int isig = 0; - for (const auto& mctrack : mcTracks) { VarManager::FillTrackMC(mcTracks, mctrack); // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. @@ -1326,7 +1299,7 @@ struct AnalysisSameEventPairing { // TODO: Use the mcReducedFlags to select signals for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -1336,44 +1309,36 @@ struct AnalysisSameEventPairing { if (!event.isEventSelected_bit(0)) { continue; } - if (!event.has_reducedA3MCEvent()) { + if (!event.has_reA3MCEvent()) { continue; } - VarManager::FillEventAlice3(event, VarManager::fgValues); - VarManager::FillEventAlice3(event.reducedA3MCEvent(), VarManager::fgValues); - // auto groupedMCTracks = mcTracks.sliceBy(perReducedMcGenEvent, event.reducedA3MCEventId()); - // groupedMCTracks.bindInternalIndicesTo(&mcTracks); - // for (const auto& track : groupedMCTracks) { + VarManager::FillEventAlice3(event, dqefficiency_helpers::varValues()); + VarManager::FillEventAlice3(event.reA3MCEvent(), dqefficiency_helpers::varValues()); + for (const auto& track : mcTracks) { - if (track.reducedA3MCEventId() != event.reducedA3MCEventId()) { + if (track.reA3MCEventId() != event.reA3MCEventId()) { continue; } VarManager::FillTrackMC(mcTracks, track); - auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); - // auto track_raw = groupedMCTracks.rawIteratorAt(track.globalIndex()); - mcDecision = 0; - isig = 0; + auto trackRaw = mcTracks.rawIteratorAt(track.globalIndex()); for (const auto& sig : fGenMCSignals) { - if (sig->CheckSignal(true, track_raw)) { - mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); - MCTruthTableEffi(VarManager::fgValues[VarManager::kMCPt], VarManager::fgValues[VarManager::kMCEta], VarManager::fgValues[VarManager::kMCY], VarManager::fgValues[VarManager::kMCPhi], VarManager::fgValues[VarManager::kMCVz], VarManager::fgValues[VarManager::kMCVtxZ], VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); + if (sig->CheckSignal(true, trackRaw)) { + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } - isig++; } } } // end loop over reconstructed events if (fHasTwoProngGenMCsignals) { for (const auto& [t1, t2] : combinations(mcTracks, mcTracks)) { - auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); - auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); - if (t1_raw.reducedA3MCEventId() == t2_raw.reducedA3MCEventId()) { + auto t1Raw = mcTracks.rawIteratorAt(t1.globalIndex()); + auto t2Raw = mcTracks.rawIteratorAt(t2.globalIndex()); + if (t1Raw.reA3MCEventId() == t2Raw.reA3MCEventId()) { for (const auto& sig : fGenMCSignals) { - if (sig->GetNProngs() != TWO_PRONG) { // NOTE: 2-prong signals required here + if (sig->GetNProngs() != TwoProng) { // NOTE: 2-prong signals required here continue; } - if (sig->CheckSignal(true, t1_raw, t2_raw)) { - fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), VarManager::fgValues); + if (sig->CheckSignal(true, t1Raw, t2Raw)) { + fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -1384,32 +1349,28 @@ struct AnalysisSameEventPairing { if (!event.isEventSelected_bit(0)) { continue; } - if (!event.has_reducedA3MCEvent()) { + if (!event.has_reA3MCEvent()) { continue; } if (fHasTwoProngGenMCsignals) { for (const auto& [t1, t2] : combinations(mcTracks, mcTracks)) { - if (t1.reducedA3MCEventId() != event.reducedA3MCEventId()) { + if (t1.reA3MCEventId() != event.reA3MCEventId()) { continue; } - if (t2.reducedA3MCEventId() != event.reducedA3MCEventId()) { + if (t2.reA3MCEventId() != event.reA3MCEventId()) { continue; } - auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); - auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); - if (t1_raw.reducedA3MCEventId() == t2_raw.reducedA3MCEventId()) { - mcDecision = 0; - isig = 0; + auto t1Raw = mcTracks.rawIteratorAt(t1.globalIndex()); + auto t2Raw = mcTracks.rawIteratorAt(t2.globalIndex()); + if (t1Raw.reA3MCEventId() == t2Raw.reA3MCEventId()) { for (const auto& sig : fGenMCSignals) { - if (sig->GetNProngs() != TWO_PRONG) { // NOTE: 2-prong signals required here + if (sig->GetNProngs() != TwoProng) { // NOTE: 2-prong signals required here continue; } - if (sig->CheckSignal(true, t1_raw, t2_raw)) { - mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), VarManager::fgValues); + if (sig->CheckSignal(true, t1Raw, t2Raw)) { + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } - isig++; } } } @@ -1417,11 +1378,8 @@ struct AnalysisSameEventPairing { } // end loop over reconstructed events } - void processMCGenWithGrouping(soa::Filtered const& events, ReducedA3MCEvents const& /*mcEvents*/, ReducedA3MCTracks const& mcTracks) + void processMCGenWithGrouping(soa::Filtered const& events, ReA3MCEvents const& /*mcEvents*/, ReA3MCTracks const& mcTracks) { - [[maybe_unused]] uint32_t mcDecision = 0; - int isig = 0; - for (const auto& mctrack : mcTracks) { VarManager::FillTrackMC(mcTracks, mctrack); // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. @@ -1429,7 +1387,7 @@ struct AnalysisSameEventPairing { // TODO: Use the mcReducedFlags to select signals for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -1438,39 +1396,34 @@ struct AnalysisSameEventPairing { if (!event.isEventSelected_bit(0)) { continue; } - if (!event.has_reducedA3MCEvent()) { + if (!event.has_reA3MCEvent()) { continue; } for (const auto& track : mcTracks) { - if (track.reducedA3MCEventId() != event.reducedA3MCEventId()) { + if (track.reA3MCEventId() != event.reA3MCEventId()) { continue; } VarManager::FillTrackMC(mcTracks, track); - auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); - mcDecision = 0; - isig = 0; + auto trackRaw = mcTracks.rawIteratorAt(track.globalIndex()); for (const auto& sig : fGenMCSignals) { - if (sig->CheckSignal(true, track_raw)) { - mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); - MCTruthTableEffi(VarManager::fgValues[VarManager::kMCPt], VarManager::fgValues[VarManager::kMCEta], VarManager::fgValues[VarManager::kMCY], VarManager::fgValues[VarManager::kMCPhi], VarManager::fgValues[VarManager::kMCVz], VarManager::fgValues[VarManager::kMCVtxZ], VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); + if (sig->CheckSignal(true, trackRaw)) { + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } - isig++; } } } // end loop over reconstructed events if (fHasTwoProngGenMCsignals) { for (const auto& [t1, t2] : combinations(mcTracks, mcTracks)) { - auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); - auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); - if (t1_raw.reducedA3MCEventId() == t2_raw.reducedA3MCEventId()) { + auto t1Raw = mcTracks.rawIteratorAt(t1.globalIndex()); + auto t2Raw = mcTracks.rawIteratorAt(t2.globalIndex()); + if (t1Raw.reA3MCEventId() == t2Raw.reA3MCEventId()) { for (const auto& sig : fGenMCSignals) { - if (sig->GetNProngs() != TWO_PRONG) { // NOTE: 2-prong signals required here + if (sig->GetNProngs() != TwoProng) { // NOTE: 2-prong signals required here continue; } - if (sig->CheckSignal(true, t1_raw, t2_raw)) { - fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), VarManager::fgValues); + if (sig->CheckSignal(true, t1Raw, t2Raw)) { + fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -1480,28 +1433,24 @@ struct AnalysisSameEventPairing { if (!event.isEventSelected_bit(0)) { continue; } - if (!event.has_reducedA3MCEvent()) { + if (!event.has_reA3MCEvent()) { continue; } // CURRENTLY ONLY FOR 1-GENERATION 2-PRONG SIGNALS if (fHasTwoProngGenMCsignals) { - auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedA3MCEventId()); + auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reA3MCEventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); for (const auto& [t1, t2] : combinations(groupedMCTracks, groupedMCTracks)) { - auto t1_raw = groupedMCTracks.rawIteratorAt(t1.globalIndex()); - auto t2_raw = groupedMCTracks.rawIteratorAt(t2.globalIndex()); - if (t1_raw.reducedA3MCEventId() == t2_raw.reducedA3MCEventId()) { - mcDecision = 0; - isig = 0; + auto t1Raw = groupedMCTracks.rawIteratorAt(t1.globalIndex()); + auto t2Raw = groupedMCTracks.rawIteratorAt(t2.globalIndex()); + if (t1Raw.reA3MCEventId() == t2Raw.reA3MCEventId()) { for (const auto& sig : fGenMCSignals) { - if (sig->GetNProngs() != TWO_PRONG) { // NOTE: 2-prong signals required here + if (sig->GetNProngs() != TwoProng) { // NOTE: 2-prong signals required here continue; } - if (sig->CheckSignal(true, t1_raw, t2_raw)) { - mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), VarManager::fgValues); + if (sig->CheckSignal(true, t1Raw, t2Raw)) { + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } - isig++; } } } @@ -1514,65 +1463,62 @@ struct AnalysisSameEventPairing { // do nothing } - PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmed, "Run barrel only pairing, with skimmed tracks", false); - PROCESS_SWITCH(AnalysisSameEventPairing, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); - PROCESS_SWITCH(AnalysisSameEventPairing, processMCGenWithGrouping, "Loop over MC particle stack (grouped MCTracks) and fill generator level histograms", false); - PROCESS_SWITCH(AnalysisSameEventPairing, processDummy, "Dummy function, enabled only if none of the others are enabled", true); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisSameEventPairing, processBarrelOnlySkimmed, "Run barrel only pairing, with skimmed tracks", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisSameEventPairing, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisSameEventPairing, processMCGenWithGrouping, "Loop over MC particle stack (grouped MCTracks) and fill generator level histograms", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisSameEventPairing, processDummy, "Dummy function, enabled only if none of the others are enabled", true); }; -struct AnalysisAsymmetricPairing { - - Produces ditrackList; - Produces ditrackExtraList; +struct Alice3DqEfficiencyAnalysisAsymmetricPairing { // Output objects OutputObj fOutputList{"output"}; // Configurables - Configurable fConfigLegCuts{"cfgLegCuts", "", ":[:],[:[:],...]"}; - Configurable fConfigLegAFilterMask{"cfgLegAFilterMask", 0, "Filter mask corresponding to cuts in event-selection"}; - Configurable fConfigLegBFilterMask{"cfgLegBFilterMask", 0, "Filter mask corresponding to cuts in event-selection"}; - Configurable fConfigLegCFilterMask{"cfgLegCFilterMask", 0, "Filter mask corresponding to cuts in event-selection"}; - Configurable fConfigCommonTrackCuts{"cfgCommonTrackCuts", "", "Comma separated list of cuts to be applied to all legs"}; - Configurable fConfigPairCuts{"cfgPairCuts", "", "Comma separated list of pair cuts"}; - Configurable fConfigPairCutsJSON{"cfgPairCutsJSON", "", "Additional list of pair cuts in JSON format"}; - Configurable fConfigSkipAmbiguousIdCombinations{"cfgSkipAmbiguousIdCombinations", true, "Choose whether to skip pairs/triples which pass a stricter combination of cuts, e.g. KKPi triplets for D+ -> KPiPi"}; - - Configurable fConfigHistogramSubgroups{"cfgAsymmetricPairingHistogramsSubgroups", "barrel,vertexing", "Comma separated list of asymmetric-pairing histogram subgroups"}; - Configurable fConfigSameSignHistograms{"cfgSameSignHistograms", false, "Include same sign pair histograms for 2-prong decays"}; - Configurable fConfigReflectedHistograms{"cfgReflectedHistograms", false, "Include separate histograms for pairs which are reflections of previously counted pairs"}; - Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - - Configurable fConfigMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; - Configurable fConfigMCRecSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; - Configurable fConfigMCRecSignalsJSON{"cfgMCRecSignalsJSON", "", "Additional list of MC signals (reconstructed) via JSON"}; - Configurable fConfigMCGenSignalsJSON{"cfgMCGenSignalsJSON", "", "Comma separated list of MC signals (generated) via JSON"}; - - HistogramManager* fHistMan; - - std::vector fPairCuts; - int fNPairHistPrefixes; + Configurable cfgLegCuts{"cfgLegCuts", "", ":[:],[:[:],...]"}; + Configurable cfgLegAFilterMask{"cfgLegAFilterMask", 0, "Filter mask corresponding to cuts in event-selection"}; + Configurable cfgLegBFilterMask{"cfgLegBFilterMask", 0, "Filter mask corresponding to cuts in event-selection"}; + Configurable cfgLegCFilterMask{"cfgLegCFilterMask", 0, "Filter mask corresponding to cuts in event-selection"}; + Configurable cfgCommonTrackCuts{"cfgCommonTrackCuts", "", "Comma separated list of cuts to be applied to all legs"}; + Configurable cfgPairCuts{"cfgPairCuts", "", "Comma separated list of pair cuts"}; + Configurable cfgPairCutsJSON{"cfgPairCutsJSON", "", "Additional list of pair cuts in JSON format"}; + Configurable cfgSkipAmbiguousIdCombinations{"cfgSkipAmbiguousIdCombinations", true, "Choose whether to skip pairs/triples which pass a stricter combination of cuts, e.g. KKPi triplets for D+ -> KPiPi"}; + + Configurable cfgAsymmetricPairingHistogramsSubgroups{"cfgAsymmetricPairingHistogramsSubgroups", "barrel,vertexing", "Comma separated list of asymmetric-pairing histogram subgroups"}; + Configurable cfgSameSignHistograms{"cfgSameSignHistograms", false, "Include same sign pair histograms for 2-prong decays"}; + Configurable cfgReflectedHistograms{"cfgReflectedHistograms", false, "Include separate histograms for pairs which are reflections of previously counted pairs"}; + Configurable cfgQA{"cfgQA", false, "If true, fill QA histograms"}; + Configurable cfgAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + + Configurable cfgBarrelMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; + Configurable cfgBarrelMCRecSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable cfgMCRecSignalsJSON{"cfgMCRecSignalsJSON", "", "Additional list of MC signals (reconstructed) via JSON"}; + Configurable cfgMCGenSignalsJSON{"cfgMCGenSignalsJSON", "", "Comma separated list of MC signals (generated) via JSON"}; + + HistogramManager* fHistMan = nullptr; + + std::vector fPairCuts; + int fNPairHistPrefixes = 0; std::vector fRecMCSignals; std::vector fGenMCSignals; // Filter masks to find legs in BarrelTrackCuts table - uint32_t fLegAFilterMask; - uint32_t fLegBFilterMask; - uint32_t fLegCFilterMask; + uint32_t fLegAFilterMask = 0; + uint32_t fLegBFilterMask = 0; + uint32_t fLegCFilterMask = 0; // Maps tracking which combination of leg cuts the track cuts participate in std::map fConstructedLegAFilterMasksMap; std::map fConstructedLegBFilterMasksMap; std::map fConstructedLegCFilterMasksMap; // Filter map for common track cuts - uint32_t fCommonTrackCutMask; + uint32_t fCommonTrackCutMask = 0; // Map tracking which common track cut the track cuts correspond to std::map fCommonTrackCutFilterMasks; - int fNLegCuts; + int fNLegCuts = 0; int fNPairCuts = 0; - int fNCommonTrackCuts; + int fNCommonTrackCuts = 0; // vectors for cut names and signal names, for easy access when calling FillHistogramList() std::vector fLegCutNames; std::vector fPairCutNames; @@ -1581,13 +1527,13 @@ struct AnalysisAsymmetricPairing { Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); - PresliceUnsorted trackAssocsPerCollision = aod::reducedA3track_association::reducedA3eventId; - // PresliceUnsorted trackAssocsPerCollision = aod::reducedA3track_association::reducedA3eventId; + PresliceUnsorted trackAssocsPerCollision = aod::reduceda3trackassociation::reA3EventId; + // PresliceUnsorted trackAssocsPerCollision = aod::reduceda3trackassociation::reA3EventId; // Partitions for triplets and asymmetric pairs - Partition legACandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegAFilterMask) > static_cast(0); - Partition legBCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegBFilterMask) > static_cast(0); - Partition legCCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegCFilterMask) > static_cast(0); + Partition legACandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & cfgLegAFilterMask) > static_cast(0); + Partition legBCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & cfgLegBFilterMask) > static_cast(0); + Partition legCCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & cfgLegCFilterMask) > static_cast(0); // Map to track how many times a pair of tracks has been encountered std::map, int8_t> fPairCount; @@ -1602,38 +1548,38 @@ struct AnalysisAsymmetricPairing { VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); // Get the leg cut filter masks - fLegAFilterMask = fConfigLegAFilterMask.value; - fLegBFilterMask = fConfigLegBFilterMask.value; - fLegCFilterMask = fConfigLegCFilterMask.value; + fLegAFilterMask = cfgLegAFilterMask.value; + fLegBFilterMask = cfgLegBFilterMask.value; + fLegCFilterMask = cfgLegCFilterMask.value; // Get the pair cuts - TString cutNamesStr = fConfigPairCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); + TString pairCutNamesStr = cfgPairCuts.value; + if (!pairCutNamesStr.IsNull()) { + std::unique_ptr objArray(pairCutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { fPairCuts.push_back(dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } // Extra pair cuts via JSON - TString addPairCutsStr = fConfigPairCutsJSON.value; + TString addPairCutsStr = cfgPairCutsJSON.value; if (addPairCutsStr != "") { std::vector addPairCuts = dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); for (const auto& t : addPairCuts) { - fPairCuts.push_back(reinterpret_cast(t)); - cutNamesStr += Form(",%s", t->GetName()); + fPairCuts.push_back(t); + pairCutNamesStr += Form(",%s", t->GetName()); } } - std::unique_ptr objArrayPairCuts(cutNamesStr.Tokenize(",")); + std::unique_ptr objArrayPairCuts(pairCutNamesStr.Tokenize(",")); fNPairCuts = objArrayPairCuts->GetEntries(); for (int j = 0; j < fNPairCuts; j++) { fPairCutNames.push_back(objArrayPairCuts->At(j)->GetName()); } // Setting the MC rec signal names - TString sigNamesStr = fConfigMCRecSignals.value; + TString sigNamesStr = cfgBarrelMCRecSignals.value; std::unique_ptr objRecSigArray(sigNamesStr.Tokenize(",")); for (int isig = 0; isig < objRecSigArray->GetEntries(); ++isig) { MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objRecSigArray->At(isig)->GetName()); @@ -1642,11 +1588,11 @@ struct AnalysisAsymmetricPairing { } } // Add the reco MCSignals from the JSON config - TString addMCSignalsStr = fConfigMCRecSignalsJSON.value; + TString addMCSignalsStr = cfgMCRecSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); for (const auto& mcIt : addMCSignals) { - if (mcIt->GetNProngs() != TWO_PRONG && mcIt->GetNProngs() != THREE_PRONG) { + if (mcIt->GetNProngs() != TwoProng && mcIt->GetNProngs() != ThreeProng) { LOG(fatal) << "Signal at reconstructed level requested (" << mcIt->GetName() << ") " << "does not have 2 or 3 prongs! Fix it"; } fRecMCSignals.push_back(mcIt); @@ -1661,10 +1607,10 @@ struct AnalysisAsymmetricPairing { // Get the barrel track selection cuts string tempCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + getTaskOptionValue(context, "alice3-dq-efficiency-analysis-track-selection", "cfgTrackCuts", tempCuts, false); TString tempCutsStr = tempCuts; // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + getTaskOptionValue(context, "alice3-dq-efficiency-analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); TString addTrackCutsStr = tempCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); @@ -1674,8 +1620,8 @@ struct AnalysisAsymmetricPairing { } std::unique_ptr objArray(tempCutsStr.Tokenize(",")); // Get the common leg cuts - int commonCutIdx; - TString commonNamesStr = fConfigCommonTrackCuts.value; + int commonCutIdx = -1; + TString commonNamesStr = cfgCommonTrackCuts.value; if (!commonNamesStr.IsNull()) { // if common track cuts std::unique_ptr objArrayCommon(commonNamesStr.Tokenize(",")); fNCommonTrackCuts = objArrayCommon->GetEntries(); @@ -1692,36 +1638,36 @@ struct AnalysisAsymmetricPairing { } // Check that the leg cut masks make sense if (static_cast(std::floor(std::log2(fLegAFilterMask))) + 1 > objArray->GetEntries()) { - LOGF(fatal, "fConfigLegAFilterMask has highest bit at position %d, but track-selection only has %d cuts!", static_cast(std::floor(std::log2(fLegAFilterMask))) + 1, objArray->GetEntries()); + LOGF(fatal, "cfgLegAFilterMask has highest bit at position %d, but track-selection only has %d cuts!", static_cast(std::floor(std::log2(fLegAFilterMask))) + 1, objArray->GetEntries()); } if (static_cast(std::floor(std::log2(fLegBFilterMask))) + 1 > objArray->GetEntries()) { - LOGF(fatal, "fConfigLegBFilterMask has highest bit at position %d, but track-selection only has %d cuts!", static_cast(std::floor(std::log2(fLegBFilterMask))) + 1, objArray->GetEntries()); + LOGF(fatal, "cfgLegBFilterMask has highest bit at position %d, but track-selection only has %d cuts!", static_cast(std::floor(std::log2(fLegBFilterMask))) + 1, objArray->GetEntries()); } if (static_cast(std::floor(std::log2(fLegCFilterMask))) + 1 > objArray->GetEntries()) { - LOGF(fatal, "fConfigLegCFilterMask has highest bit at position %d, but track-selection only has %d cuts!", static_cast(std::floor(std::log2(fLegCFilterMask))) + 1, objArray->GetEntries()); + LOGF(fatal, "cfgLegCFilterMask has highest bit at position %d, but track-selection only has %d cuts!", static_cast(std::floor(std::log2(fLegCFilterMask))) + 1, objArray->GetEntries()); } // Get the cuts defining the legs uint32_t fConstructedLegAFilterMask = 0; uint32_t fConstructedLegBFilterMask = 0; uint32_t fConstructedLegCFilterMask = 0; - TString legCutsStr = fConfigLegCuts.value; + TString legCutsStr = cfgLegCuts.value; std::unique_ptr objArrayLegs(legCutsStr.Tokenize(",")); if (objArrayLegs->GetEntries() == 0 && !isMCGen) { LOG(fatal) << "No cuts defining legs. Check the config!"; } fNLegCuts = objArrayLegs->GetEntries(); std::vector isThreeProng; - int legAIdx; - int legBIdx; - int legCIdx; + int legAIdx = -1; + int legBIdx = -1; + int legCIdx = -1; // Loop over leg defining cuts for (int icut = 0; icut < fNLegCuts; ++icut) { TString legsStr = objArrayLegs->At(icut)->GetName(); std::unique_ptr legs(legsStr.Tokenize(":")); - if (legs->GetEntries() == THREE_PRONG) { + if (legs->GetEntries() == ThreeProng) { isThreeProng.push_back(true); - } else if (legs->GetEntries() == TWO_PRONG) { + } else if (legs->GetEntries() == TwoProng) { isThreeProng.push_back(false); } else { LOGF(fatal, "Leg cuts %s has the wrong format and could not be parsed!", legsStr.Data()); @@ -1759,24 +1705,24 @@ struct AnalysisAsymmetricPairing { // Define histogram and histogram directory names if (isThreeProng[icut]) { - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s", legsStr.Data()), fConfigHistogramSubgroups.value.data()); - if (fConfigQA) { - DefineHistograms(fHistMan, Form("TripletsBarrelSE_ambiguous_%s", legsStr.Data()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s", legsStr.Data()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); + if (cfgQA) { + defineHistograms(fHistMan, Form("TripletsBarrelSE_ambiguous_%s", legsStr.Data()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } std::unique_ptr objArrayCommon(commonNamesStr.Tokenize(",")); for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } - TString cutNamesStr = fConfigPairCuts.value; - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + TString pairCutHistNamesStr = cfgPairCuts.value; + if (!pairCutHistNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutHistNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), objArrayPair->At(iPairCut)->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), objArrayPair->At(iPairCut)->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } // end loop (common cuts) } // end loop (pair cuts) } // end if (pair cuts) @@ -1785,18 +1731,18 @@ struct AnalysisAsymmetricPairing { if (!sigNamesStr.IsNull()) { for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { auto sig = fRecMCSignals.at(isig); - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + if (!pairCutNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s", legsStr.Data(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s", legsStr.Data(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { - DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } // end loop (common cuts) } // end loop (pair cuts) } // end if (pair cuts) @@ -1804,43 +1750,43 @@ struct AnalysisAsymmetricPairing { } // end if (MC signals) } else { std::vector pairHistPrefixes = {"PairsBarrelSEPM"}; - if (fConfigSameSignHistograms.value) { + if (cfgSameSignHistograms.value) { pairHistPrefixes.push_back("PairsBarrelSEPP"); pairHistPrefixes.push_back("PairsBarrelSEMM"); } fNPairHistPrefixes = pairHistPrefixes.size(); for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } - if (fConfigQA) { + if (cfgQA) { for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_ambiguous_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_ambiguous_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } } - if (fConfigReflectedHistograms.value) { + if (cfgReflectedHistograms.value) { for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_reflected_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_reflected_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } } std::unique_ptr objArrayCommon(commonNamesStr.Tokenize(",")); for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } } - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + if (!pairCutNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayPair->At(iPairCut)->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayPair->At(iPairCut)->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } } // end loop (common cuts) } // end loop (pair cuts) @@ -1851,29 +1797,29 @@ struct AnalysisAsymmetricPairing { for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { auto sig = fRecMCSignals.at(isig); for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } - if (fConfigReflectedHistograms.value) { + if (cfgReflectedHistograms.value) { for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_reflected_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_reflected_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } } - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + if (!pairCutNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { for (int iPrefix = 0; iPrefix < fNPairHistPrefixes; ++iPrefix) { - DefineHistograms(fHistMan, Form("%s_%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), fConfigHistogramSubgroups.value.data()); + defineHistograms(fHistMan, Form("%s_%s_%s_%s_%s", pairHistPrefixes[iPrefix].Data(), legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName(), objArrayPair->At(iPairCut)->GetName(), sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); } } // end loop (common cuts) } // end loop (pair cuts) @@ -1885,28 +1831,28 @@ struct AnalysisAsymmetricPairing { // Add histogram classes for each specified MCsignal at the generator level // TODO: create a std::vector of hist classes to be used at Fill time, to avoid using Form in the process function - TString sigGenNamesStr = fConfigMCGenSignals.value; + TString sigGenNamesStr = cfgBarrelMCGenSignals.value; std::unique_ptr objGenSigArray(sigGenNamesStr.Tokenize(",")); for (int isig = 0; isig < objGenSigArray->GetEntries(); isig++) { MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objGenSigArray->At(isig)->GetName()); if (sig) { if (sig->GetNProngs() == 1) { // NOTE: 1-prong signals required fGenMCSignals.push_back(sig); - DefineHistograms(fHistMan, Form("MCTruthGen_%s;", sig->GetName()), fConfigHistogramSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function - DefineHistograms(fHistMan, Form("MCTruthGenSel_%s;", sig->GetName()), fConfigHistogramSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function + defineHistograms(fHistMan, Form("MCTruthGen_%s;", sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function + defineHistograms(fHistMan, Form("MCTruthGenSel_%s;", sig->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function } } } // Add the gen MCSignals from the JSON config - addMCSignalsStr = fConfigMCGenSignalsJSON.value; + addMCSignalsStr = cfgMCGenSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); for (const auto& mcIt : addMCSignals) { if (mcIt->GetNProngs() == 1) { fGenMCSignals.push_back(mcIt); - DefineHistograms(fHistMan, Form("MCTruthGen_%s;", mcIt->GetName()), fConfigHistogramSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function - DefineHistograms(fHistMan, Form("MCTruthGenSel_%s;", mcIt->GetName()), fConfigHistogramSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function + defineHistograms(fHistMan, Form("MCTruthGen_%s;", mcIt->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function + defineHistograms(fHistMan, Form("MCTruthGenSel_%s;", mcIt->GetName()), cfgAsymmetricPairingHistogramsSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function } } } @@ -1927,21 +1873,19 @@ struct AnalysisAsymmetricPairing { LOGF(fatal, "A mix of pairs and triplets was given as leg cuts. Check your config!"); } - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + dqhistograms::AddHistogramsFromJSON(fHistMan, cfgAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); } // Function to run same event pairing with asymmetric pairs (e.g. kaon-pion) - void runAsymmetricPairing(MyEventsVtxCovSelected const& events, PresliceUnsorted& preslice, MyBarrelAssocs const& /*assocs*/, MyBarrelTracksWithCovWithAmbiguities const& /*tracks*/, ReducedA3MCEvents const& /*mcEvents*/, ReducedA3MCTracks const& /*mcTracks*/) + void runAsymmetricPairing(MyEventsVtxCovSelected const& events, PresliceUnsorted& preslice, MyBarrelAssocs const& /*assocs*/, MyBarrelTracksWithCovWithAmbiguities const& /*tracks*/, ReA3MCEvents const& /*mcEvents*/, ReA3MCTracks const& /*mcTracks*/) { fPairCount.clear(); int sign1 = 0; int sign2 = 0; uint32_t mcDecision = 0; - ditrackList.reserve(1); - ditrackExtraList.reserve(1); for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { @@ -1949,7 +1893,7 @@ struct AnalysisAsymmetricPairing { } // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEventAlice3(event, VarManager::fgValues); + VarManager::FillEventAlice3(event, dqefficiency_helpers::varValues()); auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); if (groupedLegAAssocs.size() == 0) { @@ -1961,33 +1905,32 @@ struct AnalysisAsymmetricPairing { } for (const auto& [a1, a2] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs))) { - uint32_t twoTrackFilter = 0; uint32_t twoTrackCommonFilter = 0; - uint32_t pairFilter = 0; bool isPairIdWrong = false; + for (int icut = 0; icut < fNLegCuts; ++icut) { // Find leg pair definitions both candidates participate in - if ((a1.isBarrelSelected_raw() & fConstructedLegAFilterMasksMap[icut]) && (a2.isBarrelSelected_raw() & fConstructedLegBFilterMasksMap[icut])) { + if (((a1.isBarrelSelected_raw() & fConstructedLegAFilterMasksMap[icut]) != 0u) && ((a2.isBarrelSelected_raw() & fConstructedLegBFilterMasksMap[icut]) != 0u)) { twoTrackFilter |= static_cast(1) << icut; // If the supposed pion passes a kaon cut, this is a K+K-. Skip it. - if (fConfigSkipAmbiguousIdCombinations.value) { - if (a2.isBarrelSelected_raw() & fLegAFilterMask) { + if (cfgSkipAmbiguousIdCombinations.value) { + if ((a2.isBarrelSelected_raw() & fLegAFilterMask) != 0u) { isPairIdWrong = true; } } } } - if (!twoTrackFilter || isPairIdWrong) { + if (twoTrackFilter == 0u || isPairIdWrong) { continue; } // Find common track cuts both candidates pass twoTrackCommonFilter |= a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & fCommonTrackCutMask; - auto t1 = a1.template reducedA3track_as(); - auto t2 = a2.template reducedA3track_as(); + auto t1 = a1.template reA3Track_as(); + auto t2 = a2.template reA3Track_as(); // Avoid self-pairs if (t1.globalIndex() == t2.globalIndex()) { @@ -1996,12 +1939,12 @@ struct AnalysisAsymmetricPairing { bool isReflected = false; std::pair trackIds(t1.globalIndex(), t2.globalIndex()); - if (fPairCount.find(trackIds) != fPairCount.end()) { + if (fPairCount.contains(trackIds)) { // Double counting is possible due to track-collision ambiguity. Skip pairs which were counted before fPairCount[trackIds] += 1; continue; } - if (fPairCount.find(std::pair(trackIds.second, trackIds.first)) != fPairCount.end()) { + if (fPairCount.contains(std::pair(trackIds.second, trackIds.first))) { isReflected = true; } fPairCount[trackIds] += 1; @@ -2017,97 +1960,94 @@ struct AnalysisAsymmetricPairing { } // run MC matching for this pair - int isig = 0; + int iSigMc = 0; mcDecision = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { - if (t1.has_reducedA3MCTrack() && t2.has_reducedA3MCTrack()) { - VarManager::FillPairMC(t1.reducedA3MCTrack(), t2.reducedA3MCTrack()); - if ((*sig)->CheckSignal(true, t1.reducedA3MCTrack(), t2.reducedA3MCTrack())) { - mcDecision |= static_cast(1) << isig; + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { + if (t1.has_reA3MCTrack() && t2.has_reA3MCTrack()) { + VarManager::FillPairMC(t1.reA3MCTrack(), t2.reA3MCTrack()); + if ((*sig)->CheckSignal(true, t1.reA3MCTrack(), t2.reA3MCTrack())) { + mcDecision |= static_cast(1) << iSigMc; } } } // end loop over MC signals - VarManager::FillPairAlice3(t1, t2); - /*TODO: Reimplement when secondary vertexing is available - if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigPropToPCA); - }*/ + VarManager::FillPairAlice3(t1, t2); + VarManager::FillPairVertexingAlice3(event, t1, t2, true); // Fill histograms bool isAmbi = false; for (int icut = 0; icut < fNLegCuts; icut++) { - if (twoTrackFilter & (static_cast(1) << icut)) { - isAmbi = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); - if (sign1 * sign2 < 0) { // +- pairs - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s", fLegCutNames[icut].Data()), VarManager::fgValues); // reconstructed, unmatched - if (isAmbi && fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + if ((twoTrackFilter & (static_cast(1) << icut)) != 0u) { + isAmbi = ((twoTrackFilter & (static_cast(1) << 30)) != 0u) || ((twoTrackFilter & (static_cast(1) << 31)) != 0u); + if (sign1 * sign2 < 0) { // +- pairs + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); // reconstructed, unmatched + if (isAmbi && cfgQA) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } - if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + if (isReflected && cfgReflectedHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } - } else if (fConfigSameSignHistograms.value) { + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s", fLegCutNames[icut].Data()), VarManager::fgValues); - if (isAmbi && fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); + if (isAmbi && cfgQA) { + fHistMan->FillHistClass(Form("PairsBarrelSEPP_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } - if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + if (isReflected && cfgReflectedHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } } else { // -- pairs - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s", fLegCutNames[icut].Data()), VarManager::fgValues); - if (isAmbi && fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); + if (isAmbi && cfgQA) { + fHistMan->FillHistClass(Form("PairsBarrelSEMM_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } - if (isReflected && fConfigReflectedHistograms) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + if (isReflected && cfgReflectedHistograms) { + fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals - if (mcDecision & (static_cast(1) << isig)) { + if ((mcDecision & (static_cast(1) << isig)) != 0u) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); - if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); + if (isReflected && cfgReflectedHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } - } else if (fConfigSameSignHistograms.value) { + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); - if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); + if (isReflected && cfgReflectedHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); - if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); + if (isReflected && cfgReflectedHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } } } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { - if (twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { + if ((twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) != 0u) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); - } else if (fConfigSameSignHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals - if (mcDecision & (static_cast(1) << isig)) { + if ((mcDecision & (static_cast(1) << isig)) != 0u) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); - } else if (fConfigSameSignHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } @@ -2116,53 +2056,53 @@ struct AnalysisAsymmetricPairing { } // end loop (common cuts) int iPairCut = 0; for (auto cut = fPairCuts.begin(); cut != fPairCuts.end(); cut++, iPairCut++) { - if (!((*cut)->IsSelected(VarManager::fgValues))) // apply pair cuts + if (!((*cut)->IsSelected(dqefficiency_helpers::varValues()))) { // apply pair cuts continue; - pairFilter |= (static_cast(1) << iPairCut); + } // Histograms with pair cuts if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); - } else if (fConfigSameSignHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals - if (mcDecision & (static_cast(1) << isig)) { + if ((mcDecision & (static_cast(1) << isig)) != 0u) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); - } else if (fConfigSameSignHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } } // Histograms with pair cuts and common track cuts for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { - if (twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { + if ((twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) != 0u) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); - } else if (fConfigSameSignHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals - if (mcDecision & (static_cast(1) << isig)) { + if ((mcDecision & (static_cast(1) << isig)) != 0u) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); - } else if (fConfigSameSignHistograms.value) { + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); + } else if (cfgSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } @@ -2172,15 +2112,13 @@ struct AnalysisAsymmetricPairing { } // end loop (pair cuts) } } // end loop (cuts) - ditrackList(event.globalIndex(), VarManager::fgValues[VarManager::kMass], - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], - t1.sign() + t2.sign(), twoTrackFilter, pairFilter, twoTrackCommonFilter); } // end inner assoc loop (leg A) } // end event loop } // Function to run same event triplets (e.g. D+->K-pi+pi+) - void runThreeProng(MyEventsVtxCovSelected const& events, PresliceUnsorted& preslice, MyBarrelAssocs const& /*assocs*/, MyBarrelTracksWithCovWithAmbiguities const& tracks, ReducedA3MCEvents const& /*mcEvents*/, ReducedA3MCTracks const& /*mcTracks*/, VarManager::PairCandidateType tripletType) + template + void runThreeProng(TEvents const& events, PresliceUnsorted& preslice, TTrackAssocs const& /*assocs*/, TTracks const& tracks, ReA3MCEvents const& /*mcEvents*/, ReA3MCTracks const& /*mcTracks*/, VarManager::PairCandidateType tripletType) { for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { @@ -2188,7 +2126,7 @@ struct AnalysisAsymmetricPairing { } // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEventAlice3(event, VarManager::fgValues); + VarManager::FillEventAlice3(event, dqefficiency_helpers::varValues()); auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); if (groupedLegAAssocs.size() == 0) { @@ -2206,12 +2144,12 @@ struct AnalysisAsymmetricPairing { // Based on triplet type, make suitable combinations of the partitions if (tripletType == VarManager::kTripleCandidateToPKPi) { for (const auto& [a1, a2, a3] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs, groupedLegCAssocs))) { - readTriplet(a1, a2, a3, tracks, event, tripletType); + readTriplet(a1, a2, a3, tracks, event, tripletType); } } else if (tripletType == VarManager::kTripleCandidateToKPiPi) { for (const auto& a1 : groupedLegAAssocs) { for (const auto& [a2, a3] : combinations(groupedLegBAssocs, groupedLegCAssocs)) { - readTriplet(a1, a2, a3, tracks, event, tripletType); + readTriplet(a1, a2, a3, tracks, event, tripletType); } } } else { @@ -2220,8 +2158,8 @@ struct AnalysisAsymmetricPairing { } // end event loop } - // Helper function to process triplet - void readTriplet(MyBarrelAssocs::iterator const& a1, MyBarrelAssocs::iterator const& a2, MyBarrelAssocs::iterator const& a3, MyBarrelTracksWithCovWithAmbiguities const& /*tracks*/, MyEventsVtxCovSelected::iterator const& /*event*/, VarManager::PairCandidateType tripletType) + template + void readTriplet(TTrackAssoc const& a1, TTrackAssoc const& a2, TTrackAssoc const& a3, TTracks const& /*tracks*/, TEvent const& event, VarManager::PairCandidateType tripletType) { uint32_t mcDecision = 0; @@ -2231,7 +2169,7 @@ struct AnalysisAsymmetricPairing { // Find out which leg cut combinations the triplet passes if ((a1.isBarrelSelected_raw() & fConstructedLegAFilterMasksMap[icut]) && (a2.isBarrelSelected_raw() & fConstructedLegBFilterMasksMap[icut]) && (a3.isBarrelSelected_raw() & fConstructedLegCFilterMasksMap[icut])) { threeTrackFilter |= (static_cast(1) << icut); - if (tripletType == VarManager::kTripleCandidateToPKPi && fConfigSkipAmbiguousIdCombinations.value) { + if (tripletType == VarManager::kTripleCandidateToPKPi && cfgSkipAmbiguousIdCombinations.value) { // Check if the supposed pion passes as a proton or kaon, if so, skip this triplet. It is pKp or pKK. if ((a3.isBarrelSelected_raw() & fLegAFilterMask) || (a3.isBarrelSelected_raw() & fLegBFilterMask)) { return; @@ -2241,7 +2179,7 @@ struct AnalysisAsymmetricPairing { return; } } - if (tripletType == VarManager::kTripleCandidateToKPiPi && fConfigSkipAmbiguousIdCombinations.value) { + if (tripletType == VarManager::kTripleCandidateToKPiPi && cfgSkipAmbiguousIdCombinations.value) { // Check if one of the supposed pions pass as a kaon, if so, skip this triplet. It is KKPi. if ((a2.isBarrelSelected_raw() & fLegAFilterMask) || (a3.isBarrelSelected_raw() & fLegAFilterMask)) { return; @@ -2256,9 +2194,9 @@ struct AnalysisAsymmetricPairing { // Find common track cuts all candidates pass threeTrackCommonFilter |= a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a3.isBarrelSelected_raw() & fCommonTrackCutMask; - auto t1 = a1.template reducedA3track_as(); - auto t2 = a2.template reducedA3track_as(); - auto t3 = a3.template reducedA3track_as(); + auto t1 = a1.template reA3Track_as(); + auto t2 = a2.template reA3Track_as(); + auto t3 = a3.template reA3Track_as(); // Avoid self-pairs if (t1 == t2 || t1 == t3 || t2 == t3) { @@ -2288,64 +2226,64 @@ struct AnalysisAsymmetricPairing { } // run MC matching for this triplet - int isig = 0; + int iSigMc = 0; mcDecision = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { - if (t1.has_reducedA3MCTrack() && t2.has_reducedA3MCTrack() && t3.has_reducedA3MCTrack()) { - if ((*sig)->CheckSignal(true, t1.reducedA3MCTrack(), t2.reducedA3MCTrack(), t3.reducedA3MCTrack())) { - mcDecision |= (static_cast(1) << isig); + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { + if (t1.has_reA3MCTrack() && t2.has_reA3MCTrack() && t3.has_reA3MCTrack()) { + if ((*sig)->CheckSignal(true, t1.reA3MCTrack(), t2.reA3MCTrack(), t3.reA3MCTrack())) { + mcDecision |= (static_cast(1) << iSigMc); } } } // end loop over MC signals - VarManager::FillTriple(t1, t2, t3, VarManager::fgValues, tripletType); - /* TODO: Reimplement when secondary vertexing is available + VarManager::FillTriple(t1, t2, t3, dqefficiency_helpers::varValues(), tripletType); if constexpr (TThreeProngFitter) { - VarManager::FillTripletVertexing(event, t1, t2, t3, tripletType); - }*/ + VarManager::FillTripletVertexingALICE3(event, t1, t2, t3, tripletType); + } // Fill histograms bool isAmbi = false; for (int icut = 0; icut < fNLegCuts; icut++) { isAmbi = (threeTrackFilter & (static_cast(1) << 29)) || (threeTrackFilter & (static_cast(1) << 30)) || (threeTrackFilter & (static_cast(1) << 31)); if (threeTrackFilter & (static_cast(1) << icut)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) - if (fConfigQA && isAmbi) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + if (cfgQA && isAmbi) { + fHistMan->FillHistClass(Form("TripletsBarrelSE_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { if (threeTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) } } // end loop (common cuts) int iPairCut = 0; for (auto cut = fPairCuts.begin(); cut != fPairCuts.end(); cut++, iPairCut++) { - if (!((*cut)->IsSelected(VarManager::fgValues))) // apply pair cuts + if (!((*cut)->IsSelected(dqefficiency_helpers::varValues()))) { // apply pair cuts continue; + } // Histograms with pair cuts - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) // Histograms with pair cuts and common track cuts for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { if (threeTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) } @@ -2358,7 +2296,7 @@ struct AnalysisAsymmetricPairing { void processKaonPionSkimmed(MyEventsVtxCovSelected const& events, MyBarrelAssocs const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, - ReducedA3MCEvents const& mcEvents, ReducedA3MCTracks const& mcTracks) + ReA3MCEvents const& mcEvents, ReA3MCTracks const& mcTracks) { runAsymmetricPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks); } @@ -2366,16 +2304,24 @@ struct AnalysisAsymmetricPairing { void processKaonPionPionSkimmed(MyEventsVtxCovSelected const& events, MyBarrelAssocs const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, - ReducedA3MCEvents const& mcEvents, ReducedA3MCTracks const& mcTracks) + ReA3MCEvents const& mcEvents, ReA3MCTracks const& mcTracks) + { + runThreeProng(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks, VarManager::kTripleCandidateToKPiPi); + } + + void processProtonKaonPionSkimmed(MyEventsVtxCovSelected const& events, + MyBarrelAssocs const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, + ReA3MCEvents const& mcEvents, ReA3MCTracks const& mcTracks) { - runThreeProng(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks, VarManager::kTripleCandidateToKPiPi); + runThreeProng(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks, VarManager::kTripleCandidateToPKPi); } - void processMCGen(ReducedA3MCTracks const& mcTracks) + void processMCGen(ReA3MCTracks const& mcTracks) { // loop over mc stack and fill histograms for pure MC truth signals // group all the MC tracks which belong to the MC event corresponding to the current reconstructed event - // auto groupedMCTracks = tracksMC.sliceBy(aod::reducedA3trackMC::reducedA3MCEventId, event.reducedMCevent().globalIndex()); + // auto groupedMCTracks = tracksMC.sliceBy(aod::reduceda3trackmc::reA3MCEventId, event.reducedMCevent().globalIndex()); for (const auto& mctrack : mcTracks) { VarManager::FillTrackMC(mcTracks, mctrack); @@ -2384,35 +2330,35 @@ struct AnalysisAsymmetricPairing { // TODO: Use the mcReducedFlags to select signals for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } } - PresliceUnsorted perReducedMcEvent = aod::reducedA3trackMC::reducedA3MCEventId; + PresliceUnsorted perReducedMcEvent = aod::reduceda3trackmc::reA3MCEventId; void processMCGenWithEventSelection(soa::Filtered const& events, - ReducedA3MCEvents const& /*mcEvents*/, ReducedA3MCTracks const& mcTracks) + ReA3MCEvents const& /*mcEvents*/, ReA3MCTracks const& mcTracks) { for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { continue; } - if (!event.has_reducedA3MCEvent()) { + if (!event.has_reA3MCEvent()) { continue; } - auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedA3MCEventId()); + auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reA3MCEventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); for (const auto& track : groupedMCTracks) { VarManager::FillTrackMC(mcTracks, track); - auto track_raw = groupedMCTracks.rawIteratorAt(track.globalIndex()); + auto trackRaw = groupedMCTracks.rawIteratorAt(track.globalIndex()); for (const auto& sig : fGenMCSignals) { - if (sig->CheckSignal(true, track_raw)) { - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + if (sig->CheckSignal(true, trackRaw)) { + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -2424,24 +2370,25 @@ struct AnalysisAsymmetricPairing { // do nothing } - PROCESS_SWITCH(AnalysisAsymmetricPairing, processKaonPionSkimmed, "Run kaon pion pairing, with skimmed tracks", false); - PROCESS_SWITCH(AnalysisAsymmetricPairing, processKaonPionPionSkimmed, "Run kaon pion pion triplets, with skimmed tracks", false); - PROCESS_SWITCH(AnalysisAsymmetricPairing, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); - PROCESS_SWITCH(AnalysisAsymmetricPairing, processMCGenWithEventSelection, "Loop over MC particle stack and fill generator level histograms", false); - PROCESS_SWITCH(AnalysisAsymmetricPairing, processDummy, "Dummy function, enabled only if none of the others are enabled", true); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisAsymmetricPairing, processKaonPionSkimmed, "Run kaon pion pairing, with skimmed tracks", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisAsymmetricPairing, processKaonPionPionSkimmed, "Run kaon pion pion triplets, with skimmed tracks", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisAsymmetricPairing, processProtonKaonPionSkimmed, "Run proton kaon pion triplets, with skimmed tracks", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisAsymmetricPairing, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisAsymmetricPairing, processMCGenWithEventSelection, "Loop over MC particle stack and fill generator level histograms", false); + PROCESS_SWITCH(Alice3DqEfficiencyAnalysisAsymmetricPairing, processDummy, "Dummy function, enabled only if none of the others are enabled", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups) +void defineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) { // // Define here the histograms for all the classes required in analysis. diff --git a/ALICE3/Tasks/alice3HfTask3Prong.cxx b/ALICE3/Tasks/alice3HfTask3Prong.cxx index 4f21bada994..8901ae6e8a9 100644 --- a/ALICE3/Tasks/alice3HfTask3Prong.cxx +++ b/ALICE3/Tasks/alice3HfTask3Prong.cxx @@ -58,7 +58,6 @@ struct Alice3HfTask3Prong { Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; - HfHelperAlice3 hfHelper; SliceCache cache; Service ccdb; @@ -119,7 +118,7 @@ struct Alice3HfTask3Prong { registry.add(("MC/gen/nonprompt/" + histoName + "GenNonPrompt").c_str(), ("MC particles (matched, non-prompt);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); }; - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); /// Reconstructed Histograms addHistogramsRec("hMass", "inv. mass (p K #pi) (GeV/#it{c}^{2})", "", {HistType::kTH1F, {{600, 1.98, 2.58}}}); @@ -271,7 +270,7 @@ struct Alice3HfTask3Prong { for (const auto& candidate : candidates) { registry.fill(HIST("MC/rec/hCandidateCounter"), 1.); /// rapidity selection - if (yCandRecoMax >= 0. && std::abs(hfHelper.getCandY(candidate)) > yCandRecoMax) { + if (yCandRecoMax >= 0. && std::abs(HfHelperAlice3::getCandY(candidate)) > yCandRecoMax) { continue; } registry.fill(HIST("MC/rec/hCandidateCounter"), 2.); @@ -311,7 +310,7 @@ struct Alice3HfTask3Prong { if (candidate.isSelMassHypo0()) { registry.fill(HIST("hSelectionStatus"), 0., pt); - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); /// Fill histograms fillHistogramsRecSig(candidate, mass, false); if (originType == RecoDecay::OriginType::Prompt) { @@ -334,7 +333,7 @@ struct Alice3HfTask3Prong { if (fillSwapMassHypo) { if (candidate.isSelMassHypo1()) { registry.fill(HIST("hSelectionStatus"), 1., pt); - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); /// Fill histograms fillHistogramsRecSig(candidate, mass, true); if (originType == RecoDecay::OriginType::Prompt) { @@ -357,12 +356,12 @@ struct Alice3HfTask3Prong { } } else { // Background if (candidate.isSelMassHypo0()) { - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); fillHistogramsRecSig(candidate, mass, false); } if (fillSwapMassHypo) { if (candidate.isSelMassHypo1()) { - double mass = hfHelper.getCandMass(candidate); + double mass = HfHelperAlice3::getCandMass(candidate); fillHistogramsRecSig(candidate, mass, true); } } @@ -385,10 +384,10 @@ struct Alice3HfTask3Prong { static constexpr auto histoSuffix = HIST("Gen") + HIST(SignalSuffixes[SignalType]); registry.fill(histoPrefix + HIST("hPt") + histoSuffix, particle.pt()); registry.fill(histoPrefix + HIST("hEta") + histoSuffix, particle.eta()); - registry.fill(histoPrefix + HIST("hY") + histoSuffix, hfHelper.getCandY(particle)); + registry.fill(histoPrefix + HIST("hY") + histoSuffix, HfHelperAlice3::getCandY(particle)); registry.fill(histoPrefix + HIST("hPhi") + histoSuffix, particle.phi()); registry.fill(histoPrefix + HIST("hEtaVsPt") + histoSuffix, particle.eta(), particle.pt()); - registry.fill(histoPrefix + HIST("hYVsPt") + histoSuffix, hfHelper.getCandY(particle), particle.pt()); + registry.fill(histoPrefix + HIST("hYVsPt") + histoSuffix, HfHelperAlice3::getCandY(particle), particle.pt()); registry.fill(histoPrefix + HIST("hPhiVsPt") + histoSuffix, particle.phi(), particle.pt()); if (particle.has_daughters()) { const auto firstDauIdx = particle.daughtersIds().front(); @@ -424,7 +423,7 @@ struct Alice3HfTask3Prong { // MC gen. for (const auto& particle : mcParticles) { if (std::abs(particle.flagMcGen()) == selectedPdg) { - double yGen = hfHelper.getCandY(particle); + double yGen = HfHelperAlice3::getCandY(particle); if (yCandGenMax >= 0. && std::abs(yGen) > yCandGenMax) { continue; } diff --git a/ALICE3/Tasks/alice3Multicharm.cxx b/ALICE3/Tasks/alice3Multicharm.cxx index 56689b48530..b71a376ceed 100644 --- a/ALICE3/Tasks/alice3Multicharm.cxx +++ b/ALICE3/Tasks/alice3Multicharm.cxx @@ -21,13 +21,13 @@ // HF decays. Work in progress: use at your own risk! #include "ALICE3/DataModel/OTFMulticharm.h" -#include "Tools/ML/model.h" +#include "ALICE3/ML/MulticharmMlResponse.h" +#include "Tools/ML/MlResponse.h" -#include #include #include -#include #include +#include #include #include #include @@ -37,40 +37,38 @@ #include +#include #include #include #include -#include #include #include using namespace o2; -using namespace o2::ml; using namespace o2::framework; using namespace o2::framework::expressions; -using MultiCharmTracksPID = soa::Join; -using MultiCharmTracksFull = soa::Join; - struct Alice3Multicharm { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - std::map pdgToBin; - o2::ml::OnnxModel bdtMCharm; - std::map metadata; + o2::analysis::MulticharmMlResponse mlResponse; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; + static constexpr float ToMicrons = 1e+4; struct : ConfigurableGroup { - std::string prefix = "bdt"; // JSON group name + std::string prefix = "ml"; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable localPath{"localPath", "MCharm_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - Configurable pathCCDB{"pathCCDB", "Users/j/jekarlss/MLModels", "Path on CCDB"}; Configurable timestampCCDB{"timestampCCDB", 1695750420200, "timestamp of the ONNX file for ML model used to query in CCDB. Please use 1695750420200"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - Configurable enableML{"enableML", false, "Enables bdt model"}; - } bdt; + Configurable enableBDT{"enableBDT", false, "Enables bdt model"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/j/jekarlss/MLModels"}, "Paths of models on CCDB"}; + Configurable> ptBinEdges{"ptBinEdges", {0, 2, 4, 6, 8, 10, 15}, "Bin edges for pT dependant BDT"}; + Configurable> scoreCuts{"scoreCuts", {multi_charm_ml::Cuts[0].data(), multi_charm_ml::NBinsPt, multi_charm_ml::NClasses, multi_charm_ml::labelsPt, multi_charm_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutDir{"cutDir", std::vector{o2::cuts_ml::CutDirection::CutNot}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"MCharm_BDTModel.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector(multi_charm_ml::namesInputFeatures), "Names of ML model input features"}; + } ml; ConfigurableAxis axisEta{"axisEta", {80, -4.0f, +4.0f}, "#eta"}; ConfigurableAxis axisXicMass{"axisXicMass", {200, 2.368f, 2.568f}, "Xic Inv Mass (GeV/c^{2})"}; @@ -78,74 +76,113 @@ struct Alice3Multicharm { ConfigurableAxis axisDCA{"axisDCA", {400, 0, 400}, "DCA (#mum)"}; ConfigurableAxis axisRadiusLarge{"axisRadiusLarge", {1000, 0, 20}, "Decay radius (cm)"}; ConfigurableAxis axisRadius{"axisRadius", {10000, 0, 10000}, "Decay radius (#mum)"}; - ConfigurableAxis axisTofTrackDelta{"axisTofTrackDelta", {200, 0, 1000}, "TOF track time"}; ConfigurableAxis axisNSigma{"axisNSigma", {21, -10, 10}, "nsigma"}; ConfigurableAxis axisDecayLength{"axisDecayLength", {2000, 0, 2000}, "Decay lenght (#mum)"}; ConfigurableAxis axisDcaDaughters{"axisDcaDaughters", {200, 0, 100}, "DCA (mum)"}; ConfigurableAxis axisBDTScore{"axisBDTScore", {100, 0, 1}, "BDT Score"}; + ConfigurableAxis axisBDTScoreFine{"axisBDTScoreFine", {1000, 0, 1}, "BDT Score for 1D histogram"}; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - Configurable xiMinDCAxy{"xiMinDCAxy", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable xiMinDCAz{"xiMinDCAz", -1, "[0] in |DCAz| > [0]+[1]/pT"}; - Configurable xiMinRadius{"xiMinRadius", -1, "Minimum R2D for Xic decay (cm)"}; - - Configurable picMinDCAxy{"picMinDCAxy", -1, "[0] in |DCAz| > [0]+[1]/pT"}; - Configurable picMinDCAz{"picMinDCAz", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable picMinPt{"picMinPt", -1, "Minimum pT for Xic pions"}; - - Configurable piccMinDCAxy{"piccMinDCAxy", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable piccMinDCAz{"piccMinDCAz", -1, "[0] in |DCAz| > [0]+[1]/pT"}; - Configurable piccMinPt{"piccMinPt", -1, "Minimum pT for Xicc pions"}; - - Configurable xicMaxDauDCA{"xicMaxDauDCA", 1e+4, "DCA between Xic daughters (cm)"}; - Configurable xicMinDCAxy{"xicMinDCAxy", -1, "[0] in |DCAz| > [0]+[1]/pT"}; - Configurable xicMinDCAz{"xicMinDCAz", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; - Configurable xiccMaxDCAxy{"xiccMaxDCAxy", 1e+4, "Maximum DCAxy"}; - Configurable xiccMaxDCAz{"xiccMaxDCAz", 1e+4, "Maximum DCAz"}; - Configurable xicMinRadius{"xicMinRadius", -1, "Minimum R2D for Xic decay (cm)"}; - Configurable xicMinDecayDistanceFromPV{"xicMinDecayDistanceFromPV", -1, "Minimum distance for Xic decay from PV (cm)"}; - Configurable xicMinProperLength{"xicMinProperLength", -1, "Minimum proper length for Xic decay (cm)"}; - Configurable xicMaxProperLength{"xicMaxProperLength", 1e+4, "Minimum proper length for Xic decay (cm)"}; - - Configurable xiccMaxDauDCA{"xiccMaxDauDCA", 1e+4, "DCA between Xicc daughters (cm)"}; - Configurable xiccMinRadius{"xiccMinRadius", -1, "Minimum R2D for Xicc decay (cm)"}; - Configurable xiccMinProperLength{"xiccMinProperLength", -1, "Minimum proper length for Xicc decay (cm)"}; - Configurable xiccMaxProperLength{"xiccMaxProperLength", 1e+4, "Minimum proper length for Xicc decay (cm)"}; - Configurable otfConfig{"otfConfig", 0, "OTF configuration flag"}; + struct : ConfigurableGroup { + std::string prefix = "selVals"; + Configurable xiMinConstDCAxy{"xiMinConstDCAxy", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable xiMinPtDepDCAxy{"xiMinPtDepDCAxy", 0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable xiMinConstDCAz{"xiMinConstDCAz", -1, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable xiMinPtDepDCAz{"xiMinPtDepDCAz", 0, "[1] in |DCAz| > [0]+[1]/pT"}; + Configurable xiMinRadius{"xiMinRadius", -1, "Minimum R2D for Xi decay (cm)"}; + + Configurable picMinConstDCAxy{"picMinConstDCAxy", -1, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable picMinPtDepDCAxy{"picMinPtDepDCAxy", 0, "[1] in |DCAz| > [0]+[1]/pT"}; + Configurable picMinConstDCAz{"picMinConstDCAz", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable picMinPtDepDCAz{"picMinPtDepDCAz", 0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable picMinPt{"picMinPt", -1, "Minimum pT for Xic pions"}; + + Configurable piccMinConstDCAxy{"piccMinConstDCAxy", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable piccMinPtDepDCAxy{"piccMinPtDepDCAxy", 0, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable piccMinConstDCAz{"piccMinConstDCAz", -1, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable piccMinPtDepDCAz{"piccMinPtDepDCAz", 0, "[1] in |DCAz| > [0]+[1]/pT"}; + Configurable piccMinPt{"piccMinPt", -1, "Minimum pT for Xicc pions"}; + + Configurable xicMinConstDCAxy{"xicMinConstDCAxy", -1, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable xicMinConstDCAz{"xicMinConstDCAz", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable xicMinPtDepDCAxy{"xicMinPtDepDCAxy", -1, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable xicMinPtDepDCAz{"xicMinPtDepDCAz", -1, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable xicMaxDauDCA{"xicMaxDauDCA", 1e+4, "DCA between Xic daughters (cm)"}; + Configurable xicMinRadius{"xicMinRadius", -1, "Minimum R2D for Xic decay (cm)"}; + Configurable xicMinDecayDistanceFromPV{"xicMinDecayDistanceFromPV", -1, "Minimum distance for Xic decay from PV (cm)"}; + Configurable xicMinProperLength{"xicMinProperLength", -1, "Minimum proper length for Xic decay (cm)"}; + Configurable xicMaxProperLength{"xicMaxProperLength", 1e+4, "Minimum proper length for Xic decay (cm)"}; + + Configurable xiccMaxDCAxy{"xiccMaxDCAxy", 1e+4, "Maximum DCAxy"}; + Configurable xiccMaxDCAz{"xiccMaxDCAz", 1e+4, "Maximum DCAz"}; + Configurable xiccMaxDauDCA{"xiccMaxDauDCA", 1e+4, "DCA between Xicc daughters (cm)"}; + Configurable xiccMinRadius{"xiccMinRadius", -1, "Minimum R2D for Xicc decay (cm)"}; + Configurable xiccMinProperLength{"xiccMinProperLength", -1, "Minimum proper length for Xicc decay (cm)"}; + Configurable xiccMaxProperLength{"xiccMaxProperLength", 1e+4, "Minimum proper length for Xicc decay (cm)"}; + } selVals; + + struct : ConfigurableGroup { + std::string prefix = "selFlags"; + Configurable applyXiMinDCAxy{"applyXiMinDCAxy", false, "Apply |DCAxy| > [0]+[1]/pT"}; + Configurable applyXiMinDCAz{"applyXiMinDCAz", false, "Apply |DCAz| > [0]+[1]/pT"}; + Configurable applyXiMinRadius{"applyXiMinRadius", false, "Apply min radius"}; + + Configurable applyPicMinDCAxy{"applyPicMinDCAxy", false, "Apply |DCAxy| > [0]+[1]/pT"}; + Configurable applyPicMinDCAz{"applyPicMinDCAz", false, "Apply |DCAz| > [0]+[1]/pT"}; + Configurable applyPiccMinDCAxy{"applyPiccMinDCAxy", false, "Apply |DCAxy| > [0]+[1]/pT"}; + Configurable applyPiccMinDCAz{"applyPiccMinDCAz", false, "Apply |DCAz| > [0]+[1]/pT"}; + + Configurable applyXicMinDCAxy{"applyXicMinDCAxy", false, "Apply |DCAxy| > [0]+[1]/pT"}; + Configurable applyXicMinDCAz{"applyXicMinDCAz", false, "Apply |DCAz| > [0]+[1]/pT"}; + Configurable applyXicMinRadius{"applyXicMinRadius", false, "Apply min radius"}; + Configurable applyXicMaxDauDCA{"applyXicMaxDauDCA", false, "Apply max dau dca"}; + Configurable applyXicMinDistanceFromPV{"applyXicMinDistanceFromPV", false, "Apply min distance from PV (3D)"}; + Configurable applyXicMinProperLength{"applyXicMinProperLength", false, "Apply min proper length"}; + Configurable applyXicMaxProperLength{"applyXicMaxProperLength", false, "Apply max proper length"}; + + Configurable applyXiccMinDCAxy{"applyXiccMinDCAxy", false, "Apply |DCAxy| > [0]+[1]/pT"}; + Configurable applyXiccMinDCAz{"applyXiccMinDCAz", false, "Apply |DCAz| > [0]+[1]/pT"}; + Configurable applyXiccMinRadius{"applyXiccMinRadius", false, "Apply min radius"}; + Configurable applyXiccMaxDauDCA{"applyXiccMaxDauDCA", false, "Apply max dau dca"}; + Configurable applyXiccMinProperLength{"applyXiccMinProperLength", false, "Apply min proper length"}; + Configurable applyXiccMaxProperLength{"applyXiccMaxProperLength", false, "Apply max proper length"}; + } selFlags; + Configurable otfConfig{"otfConfig", 0, "OTF configuration flag"}; Filter configFilter = (aod::otfmulticharm::lutConfigId == otfConfig); void init(InitContext&) { - histos.add("SelectionQA/hDCAXicDaughters", "hDCAXicDaughters; DCA between Xic daughters (#mum)", kTH1D, {axisDcaDaughters}); - histos.add("SelectionQA/hDCAXiccDaughters", "hDCAXiccDaughters; DCA between Xicc daughters (#mum)", kTH1D, {axisDcaDaughters}); - histos.add("SelectionQA/hDCAxyXi", "hDCAxyXi; Xi DCAxy to PV (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hDCAzXi", "hDCAzXi; Xi DCAz to PV (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hDCAxyXic", "hDCAxyXic; Xic DCAxy to PV (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hDCAzXic", "hDCAzXic; Xic DCAz to PV (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hDCAxyXicc", "hDCAxyXicc; Xicc DCAxy to PV (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hDCAzXicc", "hDCAzXicc; Xicc DCAz to PV (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hDecayRadiusXic", "hDecayRadiusXic; Distance (#mum)", kTH1D, {axisRadius}); - histos.add("SelectionQA/hDecayRadiusXicc", "hDecayRadiusXicc; Distance (#mum)", kTH1D, {axisRadius}); - histos.add("SelectionQA/hDecayDistanceFromPVXic", "hDecayDistanceFromPVXic; Distance (#mum)", kTH1D, {axisDecayLength}); - histos.add("SelectionQA/hProperLengthXic", "hProperLengthXic; Distance (#mum)", kTH1D, {axisDecayLength}); - histos.add("SelectionQA/hProperLengthXicc", "hProperLengthXicc; Distance (#mum)", kTH1D, {axisDecayLength}); - histos.add("SelectionQA/hPi1cDCAxy", "hPi1cDCAxy; Pi1c DCAxy (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hPi1cDCAz", "hPi1cDCAz; Pi1c DCAz (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hPi2cDCAxy", "hPi2cDCAxy; Pi2c DCAxy (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hPi2cDCAz", "hPi2cDCAz; Pi2c DCAz (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hPiccDCAxy", "hPiccDCAxy; Picc DCAxy (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hPiccDCAz", "hPiccDCAz; Picc DCAz (#mum)", kTH1D, {axisDCA}); - histos.add("SelectionQA/hPi1cPt", "hPi1cPt; Pi1c pT (Gev/#it(c))", kTH1D, {axisPt}); - histos.add("SelectionQA/hPi2cPt", "hPi2cPt; Pi2c pT (Gev/#it(c))", kTH1D, {axisPt}); - histos.add("SelectionQA/hPiccPt", "hPiccPt; Picc pT (Gev/#it(c))", kTH1D, {axisPt}); + histos.add("BeforeSel/hDCAXicDaughters", "hDCAXicDaughters; DCA between Xic daughters (#mum)", kTH1D, {axisDcaDaughters}); + histos.add("BeforeSel/hDCAXiccDaughters", "hDCAXiccDaughters; DCA between Xicc daughters (#mum)", kTH1D, {axisDcaDaughters}); + histos.add("BeforeSel/hDCAxyXi", "hDCAxyXi; Xi DCAxy to PV (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hDCAzXi", "hDCAzXi; Xi DCAz to PV (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hDCAxyXic", "hDCAxyXic; Xic DCAxy to PV (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hDCAzXic", "hDCAzXic; Xic DCAz to PV (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hDCAxyXicc", "hDCAxyXicc; Xicc DCAxy to PV (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hDCAzXicc", "hDCAzXicc; Xicc DCAz to PV (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hDecayRadiusXic", "hDecayRadiusXic; Distance (#mum)", kTH1D, {axisRadius}); + histos.add("BeforeSel/hDecayRadiusXicc", "hDecayRadiusXicc; Distance (#mum)", kTH1D, {axisRadius}); + histos.add("BeforeSel/hDecayDistanceFromPVXic", "hDecayDistanceFromPVXic; Distance (#mum)", kTH1D, {axisDecayLength}); + histos.add("BeforeSel/hProperLengthXic", "hProperLengthXic; Distance (#mum)", kTH1D, {axisDecayLength}); + histos.add("BeforeSel/hProperLengthXicc", "hProperLengthXicc; Distance (#mum)", kTH1D, {axisDecayLength}); + histos.add("BeforeSel/hPi1cDCAxy", "hPi1cDCAxy; Pi1c DCAxy (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hPi1cDCAz", "hPi1cDCAz; Pi1c DCAz (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hPi2cDCAxy", "hPi2cDCAxy; Pi2c DCAxy (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hPi2cDCAz", "hPi2cDCAz; Pi2c DCAz (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hPiccDCAxy", "hPiccDCAxy; Picc DCAxy (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hPiccDCAz", "hPiccDCAz; Picc DCAz (#mum)", kTH1D, {axisDCA}); + histos.add("BeforeSel/hPi1cPt", "hPi1cPt; Pi1c pT (Gev/#it(c))", kTH1D, {axisPt}); + histos.add("BeforeSel/hPi2cPt", "hPi2cPt; Pi2c pT (Gev/#it(c))", kTH1D, {axisPt}); + histos.add("BeforeSel/hPiccPt", "hPiccPt; Picc pT (Gev/#it(c))", kTH1D, {axisPt}); + histos.addClone("BeforeSel/", "AfterSel/"); auto hMCharmBuilding = histos.add("hMCharmBuilding", "hMCharmBuilding", kTH1D, {{22, -0.5, 21.5}}); hMCharmBuilding->GetXaxis()->SetBinLabel(1, "nTotalCandidates"); hMCharmBuilding->GetXaxis()->SetBinLabel(2, "xicMaxDauDCA"); hMCharmBuilding->GetXaxis()->SetBinLabel(3, "xiccMaxDauDCA"); - hMCharmBuilding->GetXaxis()->SetBinLabel(4, "xiMinDCAxy"); - hMCharmBuilding->GetXaxis()->SetBinLabel(5, "xiMinDCAz"); + hMCharmBuilding->GetXaxis()->SetBinLabel(4, "xiMinConstDCAxy"); + hMCharmBuilding->GetXaxis()->SetBinLabel(5, "xiMinConstDCAz"); hMCharmBuilding->GetXaxis()->SetBinLabel(6, "pi1cMinDCAxy"); hMCharmBuilding->GetXaxis()->SetBinLabel(7, "pi1cMinDCAz"); hMCharmBuilding->GetXaxis()->SetBinLabel(8, "pi2cMinDCAxy"); @@ -164,15 +201,6 @@ struct Alice3Multicharm { hMCharmBuilding->GetXaxis()->SetBinLabel(21, "xiccMaxProperLength"); hMCharmBuilding->GetXaxis()->SetBinLabel(22, "xicMinDecayDistanceFromPV"); - if (doprocessXiccExtra) { - histos.add("XiccProngs/h3dPos", "h3dPos; Xicc pT (GeV/#it(c)); Pos pT (GeV/#it(c)); Pos #eta", kTH3D, {axisPt, axisPt, axisEta}); - histos.add("XiccProngs/h3dNeg", "h3dNeg; Xicc pT (GeV/#it(c)); Neg pT (GeV/#it(c)); Neg #eta", kTH3D, {axisPt, axisPt, axisEta}); - histos.add("XiccProngs/h3dBach", "h3dBach; Xicc pT (GeV/#it(c)); Bach pT (GeV/#it(c)); Bach #eta", kTH3D, {axisPt, axisPt, axisEta}); - histos.add("XiccProngs/h3dPi1c", "h3dPi1c; Xicc pT (GeV/#it(c)); Pi1c pT (GeV/#it(c)); Pi1c #eta", kTH3D, {axisPt, axisPt, axisEta}); - histos.add("XiccProngs/h3dPi2c", "h3dPi2c; Xicc pT (GeV/#it(c)); Pi2c pT (GeV/#it(c)); Pi2c #eta", kTH3D, {axisPt, axisPt, axisEta}); - histos.add("XiccProngs/h3dPicc", "h3dPicc; Xicc pT (GeV/#it(c)); Picc pT (GeV/#it(c)); Picc #eta", kTH3D, {axisPt, axisPt, axisEta}); - } - histos.add("hXiccMass", "hXiccMass", kTH1D, {axisXiccMass}); histos.add("hXicMass", "hXicMass", kTH1D, {axisXicMass}); histos.add("hXiccPt", "hXiccPt", kTH1D, {axisPt}); @@ -180,62 +208,88 @@ struct Alice3Multicharm { histos.add("h3dXicc", "h3dXicc; Xicc pT (GeV/#it(c)); Xicc #eta; Xicc mass (GeV/#it(c)^{2})", kTH3D, {axisPt, axisEta, axisXiccMass}); histos.add("hConfigId", "hConfigId", kTH1D, {{11, -0.5, 10.5}}); - if (bdt.enableML) { - ccdb->setURL(bdt.ccdbUrl.value); - if (bdt.loadModelsFromCCDB) { - ccdbApi.init(bdt.ccdbUrl); - LOG(info) << "Fetching model for timestamp: " << bdt.timestampCCDB.value; - bool retrieveSuccessMCharm = ccdbApi.retrieveBlob(bdt.pathCCDB.value, ".", metadata, bdt.timestampCCDB.value, false, bdt.localPath.value); - - if (retrieveSuccessMCharm) { - bdtMCharm.initModel(bdt.localPath.value, bdt.enableOptimizations.value); - } else { - LOG(fatal) << "Error encountered while fetching/loading the MCharm model from CCDB! Maybe the model doesn't exist yet for this runnumber/timestamp?"; - } + if (ml.enableBDT) { + mlResponse.configure(ml.ptBinEdges, ml.scoreCuts, ml.cutDir, multi_charm_ml::NClasses); + if (ml.loadModelsFromCCDB) { + ccdbApi.init(ml.ccdbUrl); + mlResponse.setModelPathsCCDB(ml.onnxFileNames, ccdbApi, ml.modelPathsCCDB, ml.timestampCCDB); } else { - bdtMCharm.initModel(bdt.localPath.value, bdt.enableOptimizations.value); + mlResponse.setModelPathsLocal(ml.onnxFileNames); } - histos.add("hBDTScore", "hBDTScore", kTH1D, {axisBDTScore}); - histos.add("hBDTScoreVsXiccMass", "hBDTScoreVsXiccMass", kTH2D, {axisXiccMass, axisBDTScore}); - histos.add("hBDTScoreVsXiccPt", "hBDTScoreVsXiccPt", kTH2D, {axisPt, axisBDTScore}); - histos.add("h3dBDTScore", "h3dBDTScore", kTH3D, {axisPt, axisXiccMass, axisBDTScore}); - histos.add("hDCAXicDaughters", "hDCAXicDaughters", kTH2D, {{axisBDTScore, axisDcaDaughters}}); - histos.add("hDCAXiccDaughters", "hDCAXiccDaughters", kTH2D, {{axisBDTScore, axisDcaDaughters}}); - histos.add("hDCAxyXi", "hDCAxyXi", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hDCAzXi", "hDCAzXi", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hDCAxyXic", "hDCAxyXic", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hDCAzXic", "hDCAzXic", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hDCAxyXicc", "hDCAxyXicc", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hDCAzXicc", "hDCAzXicc", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hDecayRadiusXic", "hDecayRadiusXic", kTH2D, {{axisBDTScore, axisRadius}}); - histos.add("hDecayRadiusXicc", "hDecayRadiusXicc", kTH2D, {{axisBDTScore, axisRadius}}); - histos.add("hDecayDistanceFromPVXic", "hDecayDistanceFromPVXic", kTH2D, {{axisBDTScore, axisDecayLength}}); - histos.add("hProperLengthXic", "hProperLengthXic", kTH2D, {{axisBDTScore, axisDecayLength}}); - histos.add("hProperLengthXicc", "hProperLengthXicc", kTH2D, {{axisBDTScore, axisDecayLength}}); - histos.add("hPi1cDCAxy", "hPi1cDCAxy", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hPi1cDCAz", "hPi1cDCAz", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hPi2cDCAxy", "hPi2cDCAxy", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hPi2cDCAz", "hPi2cDCAz", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hPiccDCAxy", "hPiccDCAxy", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hPiccDCAz", "hPiccDCAz", kTH2D, {{axisBDTScore, axisDCA}}); - histos.add("hPi1cPt", "hPi1cPt", kTH2D, {{axisBDTScore, axisPt}}); - histos.add("hPi2cPt", "hPi2cPt", kTH2D, {{axisBDTScore, axisPt}}); - histos.add("hPiccPt", "hPiccPt", kTH2D, {{axisBDTScore, axisPt}}); - histos.add("hXiccMass", "hXiccMass", kTH2D, {{axisBDTScore, axisXiccMass}}); - histos.add("hXicMass", "hXicMass", kTH2D, {{axisBDTScore, axisXicMass}}); - histos.add("hXiccPt", "hXiccPt", kTH2D, {{axisBDTScore, axisPt}}); - histos.add("hXicPt", "hXicPt", kTH2D, {{axisBDTScore, axisPt}}); + mlResponse.cacheInputFeaturesIndices(ml.namesInputFeatures); + mlResponse.init(); + + histos.add("BDT/BeforeSel/hBDTScoreSignalFine", "hBDTScoreSignalFine", kTH1D, {axisBDTScoreFine}); + histos.add("BDT/BeforeSel/hBDTScoreSignal", "hBDTScoreSignal", kTH1D, {axisBDTScore}); + histos.add("BDT/BeforeSel/hBDTScoreVsXiccMassSignal", "hBDTScoreVsXiccMassSignal", kTH2D, {axisXiccMass, axisBDTScore}); + histos.add("BDT/BeforeSel/hBDTScoreVsXiccPtSignal", "hBDTScoreVsXiccPtSignal", kTH2D, {axisPt, axisBDTScore}); + histos.add("BDT/BeforeSel/h3dBDTScoreSignal", "h3dBDTScoreSignal", kTH3D, {axisPt, axisXiccMass, axisBDTScore}); + histos.add("BDT/BeforeSel/hBDTScoreBackgroundFine", "hBDTScoreBackgroundFine", kTH1D, {axisBDTScoreFine}); + histos.add("BDT/BeforeSel/hBDTScoreBackground", "hBDTScoreBackground", kTH1D, {axisBDTScore}); + histos.add("BDT/BeforeSel/hBDTScoreVsXiccMassBackground", "hBDTScoreVsXiccMassBackground", kTH2D, {axisXiccMass, axisBDTScore}); + histos.add("BDT/BeforeSel/hBDTScoreVsXiccPtBackground", "hBDTScoreVsXiccPtBackground", kTH2D, {axisPt, axisBDTScore}); + histos.add("BDT/BeforeSel/h3dBDTScoreBackground", "h3dBDTScoreBackground", kTH3D, {axisPt, axisXiccMass, axisBDTScore}); + histos.add("BDT/BeforeSel/hDCAXicDaughters", "hDCAXicDaughters", kTH2D, {{axisBDTScore, axisDcaDaughters}}); + histos.add("BDT/BeforeSel/hDCAXiccDaughters", "hDCAXiccDaughters", kTH2D, {{axisBDTScore, axisDcaDaughters}}); + histos.add("BDT/BeforeSel/hDCAxyXi", "hDCAxyXi", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hDCAzXi", "hDCAzXi", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hDCAxyXic", "hDCAxyXic", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hDCAzXic", "hDCAzXic", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hDCAxyXicc", "hDCAxyXicc", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hDCAzXicc", "hDCAzXicc", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hDecayRadiusXic", "hDecayRadiusXic", kTH2D, {{axisBDTScore, axisRadius}}); + histos.add("BDT/BeforeSel/hDecayRadiusXicc", "hDecayRadiusXicc", kTH2D, {{axisBDTScore, axisRadius}}); + histos.add("BDT/BeforeSel/hDecayDistanceFromPVXic", "hDecayDistanceFromPVXic", kTH2D, {{axisBDTScore, axisDecayLength}}); + histos.add("BDT/BeforeSel/hProperLengthXic", "hProperLengthXic", kTH2D, {{axisBDTScore, axisDecayLength}}); + histos.add("BDT/BeforeSel/hProperLengthXicc", "hProperLengthXicc", kTH2D, {{axisBDTScore, axisDecayLength}}); + histos.add("BDT/BeforeSel/hPi1cDCAxy", "hPi1cDCAxy", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hPi1cDCAz", "hPi1cDCAz", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hPi2cDCAxy", "hPi2cDCAxy", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hPi2cDCAz", "hPi2cDCAz", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hPiccDCAxy", "hPiccDCAxy", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hPiccDCAz", "hPiccDCAz", kTH2D, {{axisBDTScore, axisDCA}}); + histos.add("BDT/BeforeSel/hPi1cPt", "hPi1cPt", kTH2D, {{axisBDTScore, axisPt}}); + histos.add("BDT/BeforeSel/hPi2cPt", "hPi2cPt", kTH2D, {{axisBDTScore, axisPt}}); + histos.add("BDT/BeforeSel/hPiccPt", "hPiccPt", kTH2D, {{axisBDTScore, axisPt}}); + histos.addClone("BDT/BeforeSel/", "BDT/AfterSel/"); } + + histos.print(); } - template - void genericProcessXicc(TMCharmCands const& xiccCands) + void processXicc(soa::Filtered const& xiccCands) { for (const auto& xiccCand : xiccCands) { - int icfg = xiccCand.lutConfigId(); - histos.fill(HIST("hConfigId"), icfg); - if (bdt.enableML) { + float bdtPredictedBackground = -999.f; + float bdtPredictedSignal = -999.f; + + histos.fill(HIST("hConfigId"), xiccCand.lutConfigId()); + histos.fill(HIST("BeforeSel/hDCAXicDaughters"), xiccCand.xicDauDCA() * ToMicrons); + histos.fill(HIST("BeforeSel/hDCAXiccDaughters"), xiccCand.xiccDauDCA() * ToMicrons); + histos.fill(HIST("BeforeSel/hDCAxyXi"), std::fabs(xiccCand.xiDCAxy() * ToMicrons)); + histos.fill(HIST("BeforeSel/hDCAzXi"), std::fabs(xiccCand.xiDCAz() * ToMicrons)); + histos.fill(HIST("BeforeSel/hDCAxyXic"), std::fabs(xiccCand.xicDCAxy() * ToMicrons)); + histos.fill(HIST("BeforeSel/hDCAzXic"), std::fabs(xiccCand.xicDCAz() * ToMicrons)); + histos.fill(HIST("BeforeSel/hDCAxyXicc"), std::fabs(xiccCand.xiccDCAxy() * ToMicrons)); + histos.fill(HIST("BeforeSel/hDCAzXicc"), std::fabs(xiccCand.xiccDCAz() * ToMicrons)); + histos.fill(HIST("BeforeSel/hDecayRadiusXic"), xiccCand.xicDecayRadius2D() * ToMicrons); + histos.fill(HIST("BeforeSel/hDecayRadiusXicc"), xiccCand.xiccDecayRadius2D() * ToMicrons); + histos.fill(HIST("BeforeSel/hDecayDistanceFromPVXic"), xiccCand.xicDistanceFromPV() * ToMicrons); + histos.fill(HIST("BeforeSel/hProperLengthXic"), xiccCand.xicProperLength() * ToMicrons); + histos.fill(HIST("BeforeSel/hProperLengthXicc"), xiccCand.xiccProperLength() * ToMicrons); + histos.fill(HIST("BeforeSel/hPi1cDCAxy"), xiccCand.pi1cDCAxy() * ToMicrons); + histos.fill(HIST("BeforeSel/hPi1cDCAz"), xiccCand.pi1cDCAz() * ToMicrons); + histos.fill(HIST("BeforeSel/hPi2cDCAxy"), xiccCand.pi2cDCAxy() * ToMicrons); + histos.fill(HIST("BeforeSel/hPi2cDCAz"), xiccCand.pi2cDCAz() * ToMicrons); + histos.fill(HIST("BeforeSel/hPiccDCAxy"), xiccCand.piccDCAxy() * ToMicrons); + histos.fill(HIST("BeforeSel/hPiccDCAz"), xiccCand.piccDCAz() * ToMicrons); + histos.fill(HIST("BeforeSel/hPi1cPt"), xiccCand.pi1cPt()); + histos.fill(HIST("BeforeSel/hPi2cPt"), xiccCand.pi2cPt()); + histos.fill(HIST("BeforeSel/hPiccPt"), xiccCand.piccPt()); + + if (ml.enableBDT) { + std::vector outputScore; std::vector inputFeatures{ xiccCand.xicDauDCA(), xiccCand.xiccDauDCA(), @@ -257,225 +311,218 @@ struct Alice3Multicharm { xiccCand.xicDistanceFromPV(), xiccCand.xiccProperLength()}; - float* probabilityMCharm = bdtMCharm.evalModel(inputFeatures); - float bdtScore = probabilityMCharm[1]; - - histos.fill(HIST("hBDTScore"), bdtScore); - histos.fill(HIST("hBDTScoreVsXiccMass"), xiccCand.xiccMass(), bdtScore); - histos.fill(HIST("hBDTScoreVsXiccPt"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("h3dBDTScore"), xiccCand.xiccPt(), xiccCand.xiccMass(), bdtScore); - histos.fill(HIST("hDCAXicDaughters"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDCAXiccDaughters"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDCAxyXi"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDCAzXi"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDCAxyXic"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDCAzXic"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDCAxyXicc"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDCAzXicc"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDecayRadiusXic"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDecayRadiusXicc"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hDecayDistanceFromPVXic"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hProperLengthXic"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hProperLengthXicc"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPi1cDCAxy"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPi1cDCAz"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPi2cDCAxy"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPi2cDCAz"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPiccDCAxy"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPiccDCAz"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPi1cPt"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPi2cPt"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hPiccPt"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hXiccMass"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hXicMass"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hXicPt"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("hXiccPt"), xiccCand.xiccPt(), bdtScore); - histos.fill(HIST("h3dXicc"), xiccCand.xiccPt(), bdtScore); + mlResponse.isSelectedMl(inputFeatures, xiccCand.xiccPt(), outputScore); + bdtPredictedBackground = static_cast(outputScore[0]); + bdtPredictedSignal = static_cast(outputScore[1]); + + histos.fill(HIST("BDT/BeforeSel/hBDTScoreSignal"), bdtPredictedSignal); + histos.fill(HIST("BDT/BeforeSel/hBDTScoreSignalFine"), bdtPredictedSignal); + histos.fill(HIST("BDT/BeforeSel/hBDTScoreVsXiccMassSignal"), xiccCand.xiccMass(), bdtPredictedSignal); + histos.fill(HIST("BDT/BeforeSel/hBDTScoreVsXiccPtSignal"), xiccCand.xiccPt(), bdtPredictedSignal); + histos.fill(HIST("BDT/BeforeSel/h3dBDTScoreSignal"), xiccCand.xiccPt(), xiccCand.xiccMass(), bdtPredictedSignal); + histos.fill(HIST("BDT/BeforeSel/hBDTScoreBackground"), bdtPredictedBackground); + histos.fill(HIST("BDT/BeforeSel/hBDTScoreBackgroundFine"), bdtPredictedBackground); + histos.fill(HIST("BDT/BeforeSel/hBDTScoreVsXiccMassBackground"), xiccCand.xiccMass(), bdtPredictedBackground); + histos.fill(HIST("BDT/BeforeSel/hBDTScoreVsXiccPtBackground"), xiccCand.xiccPt(), bdtPredictedBackground); + histos.fill(HIST("BDT/BeforeSel/h3dBDTScoreBackground"), xiccCand.xiccPt(), xiccCand.xiccMass(), bdtPredictedBackground); + histos.fill(HIST("BDT/BeforeSel/hDCAXicDaughters"), bdtPredictedSignal, xiccCand.xicDauDCA() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hDCAXiccDaughters"), bdtPredictedSignal, xiccCand.xiccDauDCA() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hDCAxyXi"), bdtPredictedSignal, std::fabs(xiccCand.xiDCAxy() * ToMicrons)); + histos.fill(HIST("BDT/BeforeSel/hDCAzXi"), bdtPredictedSignal, std::fabs(xiccCand.xiDCAz() * ToMicrons)); + histos.fill(HIST("BDT/BeforeSel/hDCAxyXic"), bdtPredictedSignal, std::fabs(xiccCand.xicDCAxy() * ToMicrons)); + histos.fill(HIST("BDT/BeforeSel/hDCAzXic"), bdtPredictedSignal, std::fabs(xiccCand.xicDCAz() * ToMicrons)); + histos.fill(HIST("BDT/BeforeSel/hDCAxyXicc"), bdtPredictedSignal, std::fabs(xiccCand.xiccDCAxy() * ToMicrons)); + histos.fill(HIST("BDT/BeforeSel/hDCAzXicc"), bdtPredictedSignal, std::fabs(xiccCand.xiccDCAz() * ToMicrons)); + histos.fill(HIST("BDT/BeforeSel/hDecayRadiusXic"), bdtPredictedSignal, xiccCand.xicDecayRadius2D() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hDecayRadiusXicc"), bdtPredictedSignal, xiccCand.xiccDecayRadius2D() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hDecayDistanceFromPVXic"), bdtPredictedSignal, xiccCand.xicDistanceFromPV() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hProperLengthXic"), bdtPredictedSignal, xiccCand.xicProperLength() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hProperLengthXicc"), bdtPredictedSignal, xiccCand.xiccProperLength() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hPi1cDCAxy"), bdtPredictedSignal, xiccCand.pi1cDCAxy() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hPi1cDCAz"), bdtPredictedSignal, xiccCand.pi1cDCAz() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hPi2cDCAxy"), bdtPredictedSignal, xiccCand.pi2cDCAxy() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hPi2cDCAz"), bdtPredictedSignal, xiccCand.pi2cDCAz() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hPiccDCAxy"), bdtPredictedSignal, xiccCand.piccDCAxy() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hPiccDCAz"), bdtPredictedSignal, xiccCand.piccDCAz() * ToMicrons); + histos.fill(HIST("BDT/BeforeSel/hPi1cPt"), bdtPredictedSignal, xiccCand.pi1cPt()); + histos.fill(HIST("BDT/BeforeSel/hPi2cPt"), bdtPredictedSignal, xiccCand.pi2cPt()); + histos.fill(HIST("BDT/BeforeSel/hPiccPt"), bdtPredictedSignal, xiccCand.piccPt()); } histos.fill(HIST("hMCharmBuilding"), 0); - if (xiccCand.xicDauDCA() > xicMaxDauDCA) { + if (selFlags.applyXicMaxDauDCA && xiccCand.xicDauDCA() > selVals.xicMaxDauDCA) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 1); } - if (xiccCand.xiccDauDCA() > xiccMaxDauDCA) { + histos.fill(HIST("hMCharmBuilding"), 1); + if (selFlags.applyXiccMaxDauDCA && xiccCand.xiccDauDCA() > selVals.xiccMaxDauDCA) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 2); } - if (std::fabs(xiccCand.xiDCAxy()) < xiMinDCAxy) { + histos.fill(HIST("hMCharmBuilding"), 2); + if (selFlags.applyXiMinDCAxy && std::fabs(xiccCand.xiDCAxy()) < selVals.xiMinConstDCAxy + (selVals.xiMinPtDepDCAxy / xiccCand.xiPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 3); } - if (std::fabs(xiccCand.xiDCAz()) < xiMinDCAz) { + histos.fill(HIST("hMCharmBuilding"), 3); + if (selFlags.applyXiMinDCAz && std::fabs(xiccCand.xiDCAz()) < selVals.xiMinConstDCAz + (selVals.xiMinPtDepDCAz / xiccCand.xiPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 4); } - if (std::fabs(xiccCand.pi1cDCAxy()) < picMinDCAxy) { + histos.fill(HIST("hMCharmBuilding"), 4); + if (selFlags.applyPicMinDCAxy && std::fabs(xiccCand.pi1cDCAxy()) < selVals.picMinConstDCAxy + (selVals.picMinPtDepDCAxy / xiccCand.pi1cPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 5); } - if (std::fabs(xiccCand.pi1cDCAz()) < picMinDCAz) { + histos.fill(HIST("hMCharmBuilding"), 5); + if (selFlags.applyPicMinDCAz && std::fabs(xiccCand.pi1cDCAz()) < selVals.picMinConstDCAz + (selVals.picMinPtDepDCAz / xiccCand.pi1cPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 6); } - if (std::fabs(xiccCand.pi2cDCAxy()) < picMinDCAxy) { + histos.fill(HIST("hMCharmBuilding"), 6); + if (selFlags.applyPicMinDCAxy && std::fabs(xiccCand.pi2cDCAxy()) < selVals.picMinConstDCAxy + (selVals.picMinPtDepDCAxy / xiccCand.pi2cPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 7); } - if (std::fabs(xiccCand.pi2cDCAz()) < picMinDCAz) { + histos.fill(HIST("hMCharmBuilding"), 7); + if (selFlags.applyPicMinDCAz && std::fabs(xiccCand.pi2cDCAz()) < selVals.picMinConstDCAz + (selVals.picMinPtDepDCAz / xiccCand.pi2cPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 8); } - if (std::fabs(xiccCand.piccDCAxy()) < piccMinDCAxy) { + histos.fill(HIST("hMCharmBuilding"), 8); + if (selFlags.applyPiccMinDCAxy && std::fabs(xiccCand.piccDCAxy()) < selVals.piccMinConstDCAxy + (selVals.piccMinPtDepDCAxy / xiccCand.piccPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 9); } - if (std::fabs(xiccCand.piccDCAz()) < piccMinDCAz) { + histos.fill(HIST("hMCharmBuilding"), 9); + if (selFlags.applyPiccMinDCAz && std::fabs(xiccCand.piccDCAz()) < selVals.piccMinConstDCAz + (selVals.piccMinPtDepDCAz / xiccCand.piccPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 10); } - if (std::fabs(xiccCand.xicDCAxy()) < xicMinDCAxy) { + histos.fill(HIST("hMCharmBuilding"), 10); + if (selFlags.applyXicMinDCAxy && std::fabs(xiccCand.xicDCAxy()) < selVals.xicMinConstDCAxy + (selVals.xicMinPtDepDCAxy / xiccCand.xicPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 11); } - if (std::fabs(xiccCand.xicDCAz()) < xicMinDCAz) { + histos.fill(HIST("hMCharmBuilding"), 11); + if (selFlags.applyXicMinDCAz && std::fabs(xiccCand.xicDCAz()) < selVals.xicMinConstDCAz + (selVals.xicMinPtDepDCAz / xiccCand.xicPt())) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 12); } - if (std::fabs(xiccCand.xiccDCAxy()) > xiccMaxDCAxy) { + histos.fill(HIST("hMCharmBuilding"), 12); + if (selFlags.applyXiccMinDCAxy && std::fabs(xiccCand.xiccDCAxy()) > selVals.xiccMaxDCAxy) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 13); } - if (std::fabs(xiccCand.xiccDCAz()) > xiccMaxDCAz) { + histos.fill(HIST("hMCharmBuilding"), 13); + if (selFlags.applyXiccMinDCAz && std::fabs(xiccCand.xiccDCAz()) > selVals.xiccMaxDCAz) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 14); } - if (xiccCand.xicDecayRadius2D() < xicMinRadius) { + histos.fill(HIST("hMCharmBuilding"), 14); + if (selFlags.applyXicMinRadius && xiccCand.xicDecayRadius2D() < selVals.xicMinRadius) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 15); } - if (xiccCand.xiccDecayRadius2D() < xiccMinRadius) { + histos.fill(HIST("hMCharmBuilding"), 15); + if (selFlags.applyXiccMinRadius && xiccCand.xiccDecayRadius2D() < selVals.xiccMinRadius) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 16); } - if (xiccCand.xicProperLength() < xicMinProperLength) { + histos.fill(HIST("hMCharmBuilding"), 16); + if (selFlags.applyXicMinProperLength && xiccCand.xicProperLength() < selVals.xicMinProperLength) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 17); } - if (xiccCand.xicProperLength() > xicMaxProperLength) { + histos.fill(HIST("hMCharmBuilding"), 17); + if (selFlags.applyXicMaxProperLength && xiccCand.xicProperLength() > selVals.xicMaxProperLength) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 18); } - if (xiccCand.xiccProperLength() < xiccMinProperLength) { + histos.fill(HIST("hMCharmBuilding"), 18); + if (selFlags.applyXiccMinProperLength && xiccCand.xiccProperLength() < selVals.xiccMinProperLength) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 19); } - if (xiccCand.xiccProperLength() > xiccMaxProperLength) { + histos.fill(HIST("hMCharmBuilding"), 19); + if (selFlags.applyXiccMaxProperLength && xiccCand.xiccProperLength() > selVals.xiccMaxProperLength) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 20); } - if (xiccCand.xicDistanceFromPV() < xicMinDecayDistanceFromPV) { + histos.fill(HIST("hMCharmBuilding"), 20); + if (selFlags.applyXicMinDistanceFromPV && xiccCand.xicDistanceFromPV() < selVals.xicMinDecayDistanceFromPV) { continue; - } else { - histos.fill(HIST("hMCharmBuilding"), 21); - } - - histos.fill(HIST("SelectionQA/hDCAXicDaughters"), xiccCand.xicDauDCA() * 1e+4); - histos.fill(HIST("SelectionQA/hDCAXiccDaughters"), xiccCand.xiccDauDCA() * 1e+4); - histos.fill(HIST("SelectionQA/hDCAxyXi"), std::fabs(xiccCand.xiDCAxy() * 1e+4)); - histos.fill(HIST("SelectionQA/hDCAzXi"), std::fabs(xiccCand.xiDCAz() * 1e+4)); - histos.fill(HIST("SelectionQA/hDCAxyXic"), std::fabs(xiccCand.xicDCAxy() * 1e+4)); - histos.fill(HIST("SelectionQA/hDCAzXic"), std::fabs(xiccCand.xicDCAz() * 1e+4)); - histos.fill(HIST("SelectionQA/hDCAxyXicc"), std::fabs(xiccCand.xiccDCAxy() * 1e+4)); - histos.fill(HIST("SelectionQA/hDCAzXicc"), std::fabs(xiccCand.xiccDCAz() * 1e+4)); - histos.fill(HIST("SelectionQA/hDecayRadiusXic"), xiccCand.xicDecayRadius2D() * 1e+4); - histos.fill(HIST("SelectionQA/hDecayRadiusXicc"), xiccCand.xiccDecayRadius2D() * 1e+4); - histos.fill(HIST("SelectionQA/hDecayDistanceFromPVXic"), xiccCand.xicDistanceFromPV() * 1e+4); - histos.fill(HIST("SelectionQA/hProperLengthXic"), xiccCand.xicProperLength() * 1e+4); - histos.fill(HIST("SelectionQA/hProperLengthXicc"), xiccCand.xiccProperLength() * 1e+4); - histos.fill(HIST("SelectionQA/hPi1cDCAxy"), xiccCand.pi1cDCAxy() * 1e+4); - histos.fill(HIST("SelectionQA/hPi1cDCAz"), xiccCand.pi1cDCAz() * 1e+4); - histos.fill(HIST("SelectionQA/hPi2cDCAxy"), xiccCand.pi2cDCAxy() * 1e+4); - histos.fill(HIST("SelectionQA/hPi2cDCAz"), xiccCand.pi2cDCAz() * 1e+4); - histos.fill(HIST("SelectionQA/hPiccDCAxy"), xiccCand.piccDCAxy() * 1e+4); - histos.fill(HIST("SelectionQA/hPiccDCAz"), xiccCand.piccDCAz() * 1e+4); - histos.fill(HIST("SelectionQA/hPi1cPt"), xiccCand.pi1cPt()); - histos.fill(HIST("SelectionQA/hPi2cPt"), xiccCand.pi2cPt()); - histos.fill(HIST("SelectionQA/hPiccPt"), xiccCand.piccPt()); - - if constexpr (requires { xiccCand.negPt(); }) { // if extra table - histos.fill(HIST("XiccProngs/h3dNeg"), xiccCand.xiccPt(), xiccCand.negPt(), xiccCand.negEta()); - histos.fill(HIST("XiccProngs/h3dPos"), xiccCand.xiccPt(), xiccCand.posPt(), xiccCand.posEta()); - histos.fill(HIST("XiccProngs/h3dBach"), xiccCand.xiccPt(), xiccCand.bachPt(), xiccCand.bachEta()); - histos.fill(HIST("XiccProngs/h3dPi1c"), xiccCand.xiccPt(), xiccCand.pi1cPt(), xiccCand.pi1cEta()); - histos.fill(HIST("XiccProngs/h3dPi2c"), xiccCand.xiccPt(), xiccCand.pi2cPt(), xiccCand.pi2cEta()); - histos.fill(HIST("XiccProngs/h3dPicc"), xiccCand.xiccPt(), xiccCand.piccPt(), xiccCand.piccEta()); } + histos.fill(HIST("hMCharmBuilding"), 21); + histos.fill(HIST("AfterSel/hDCAXicDaughters"), xiccCand.xicDauDCA() * ToMicrons); + histos.fill(HIST("AfterSel/hDCAXiccDaughters"), xiccCand.xiccDauDCA() * ToMicrons); + histos.fill(HIST("AfterSel/hDCAxyXi"), std::fabs(xiccCand.xiDCAxy() * ToMicrons)); + histos.fill(HIST("AfterSel/hDCAzXi"), std::fabs(xiccCand.xiDCAz() * ToMicrons)); + histos.fill(HIST("AfterSel/hDCAxyXic"), std::fabs(xiccCand.xicDCAxy() * ToMicrons)); + histos.fill(HIST("AfterSel/hDCAzXic"), std::fabs(xiccCand.xicDCAz() * ToMicrons)); + histos.fill(HIST("AfterSel/hDCAxyXicc"), std::fabs(xiccCand.xiccDCAxy() * ToMicrons)); + histos.fill(HIST("AfterSel/hDCAzXicc"), std::fabs(xiccCand.xiccDCAz() * ToMicrons)); + histos.fill(HIST("AfterSel/hDecayRadiusXic"), xiccCand.xicDecayRadius2D() * ToMicrons); + histos.fill(HIST("AfterSel/hDecayRadiusXicc"), xiccCand.xiccDecayRadius2D() * ToMicrons); + histos.fill(HIST("AfterSel/hDecayDistanceFromPVXic"), xiccCand.xicDistanceFromPV() * ToMicrons); + histos.fill(HIST("AfterSel/hProperLengthXic"), xiccCand.xicProperLength() * ToMicrons); + histos.fill(HIST("AfterSel/hProperLengthXicc"), xiccCand.xiccProperLength() * ToMicrons); + histos.fill(HIST("AfterSel/hPi1cDCAxy"), xiccCand.pi1cDCAxy() * ToMicrons); + histos.fill(HIST("AfterSel/hPi1cDCAz"), xiccCand.pi1cDCAz() * ToMicrons); + histos.fill(HIST("AfterSel/hPi2cDCAxy"), xiccCand.pi2cDCAxy() * ToMicrons); + histos.fill(HIST("AfterSel/hPi2cDCAz"), xiccCand.pi2cDCAz() * ToMicrons); + histos.fill(HIST("AfterSel/hPiccDCAxy"), xiccCand.piccDCAxy() * ToMicrons); + histos.fill(HIST("AfterSel/hPiccDCAz"), xiccCand.piccDCAz() * ToMicrons); + histos.fill(HIST("AfterSel/hPi1cPt"), xiccCand.pi1cPt()); + histos.fill(HIST("AfterSel/hPi2cPt"), xiccCand.pi2cPt()); + histos.fill(HIST("AfterSel/hPiccPt"), xiccCand.piccPt()); histos.fill(HIST("hXiccMass"), xiccCand.xiccMass()); histos.fill(HIST("hXicMass"), xiccCand.xicMass()); histos.fill(HIST("hXiccPt"), xiccCand.xiccPt()); histos.fill(HIST("hXicPt"), xiccCand.xicPt()); histos.fill(HIST("h3dXicc"), xiccCand.xiccPt(), xiccCand.xiccEta(), xiccCand.xiccMass()); + if (ml.enableBDT) { + histos.fill(HIST("BDT/AfterSel/hBDTScoreSignal"), bdtPredictedSignal); + histos.fill(HIST("BDT/AfterSel/hBDTScoreSignalFine"), bdtPredictedSignal); + histos.fill(HIST("BDT/AfterSel/hBDTScoreVsXiccMassSignal"), xiccCand.xiccMass(), bdtPredictedSignal); + histos.fill(HIST("BDT/AfterSel/hBDTScoreVsXiccPtSignal"), xiccCand.xiccPt(), bdtPredictedSignal); + histos.fill(HIST("BDT/AfterSel/h3dBDTScoreSignal"), xiccCand.xiccPt(), xiccCand.xiccMass(), bdtPredictedSignal); + histos.fill(HIST("BDT/AfterSel/hBDTScoreBackground"), bdtPredictedBackground); + histos.fill(HIST("BDT/AfterSel/hBDTScoreBackgroundFine"), bdtPredictedBackground); + histos.fill(HIST("BDT/AfterSel/hBDTScoreVsXiccMassBackground"), xiccCand.xiccMass(), bdtPredictedBackground); + histos.fill(HIST("BDT/AfterSel/hBDTScoreVsXiccPtBackground"), xiccCand.xiccPt(), bdtPredictedBackground); + histos.fill(HIST("BDT/AfterSel/h3dBDTScoreBackground"), xiccCand.xiccPt(), xiccCand.xiccMass(), bdtPredictedBackground); + histos.fill(HIST("BDT/AfterSel/hDCAXicDaughters"), bdtPredictedSignal, xiccCand.xicDauDCA() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hDCAXiccDaughters"), bdtPredictedSignal, xiccCand.xiccDauDCA() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hDCAxyXi"), bdtPredictedSignal, std::fabs(xiccCand.xiDCAxy() * ToMicrons)); + histos.fill(HIST("BDT/AfterSel/hDCAzXi"), bdtPredictedSignal, std::fabs(xiccCand.xiDCAz() * ToMicrons)); + histos.fill(HIST("BDT/AfterSel/hDCAxyXic"), bdtPredictedSignal, std::fabs(xiccCand.xicDCAxy() * ToMicrons)); + histos.fill(HIST("BDT/AfterSel/hDCAzXic"), bdtPredictedSignal, std::fabs(xiccCand.xicDCAz() * ToMicrons)); + histos.fill(HIST("BDT/AfterSel/hDCAxyXicc"), bdtPredictedSignal, std::fabs(xiccCand.xiccDCAxy() * ToMicrons)); + histos.fill(HIST("BDT/AfterSel/hDCAzXicc"), bdtPredictedSignal, std::fabs(xiccCand.xiccDCAz() * ToMicrons)); + histos.fill(HIST("BDT/AfterSel/hDecayRadiusXic"), bdtPredictedSignal, xiccCand.xicDecayRadius2D() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hDecayRadiusXicc"), bdtPredictedSignal, xiccCand.xiccDecayRadius2D() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hDecayDistanceFromPVXic"), bdtPredictedSignal, xiccCand.xicDistanceFromPV() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hProperLengthXic"), bdtPredictedSignal, xiccCand.xicProperLength() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hProperLengthXicc"), bdtPredictedSignal, xiccCand.xiccProperLength() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hPi1cDCAxy"), bdtPredictedSignal, xiccCand.pi1cDCAxy() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hPi1cDCAz"), bdtPredictedSignal, xiccCand.pi1cDCAz() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hPi2cDCAxy"), bdtPredictedSignal, xiccCand.pi2cDCAxy() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hPi2cDCAz"), bdtPredictedSignal, xiccCand.pi2cDCAz() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hPiccDCAxy"), bdtPredictedSignal, xiccCand.piccDCAxy() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hPiccDCAz"), bdtPredictedSignal, xiccCand.piccDCAz() * ToMicrons); + histos.fill(HIST("BDT/AfterSel/hPi1cPt"), bdtPredictedSignal, xiccCand.pi1cPt()); + histos.fill(HIST("BDT/AfterSel/hPi2cPt"), bdtPredictedSignal, xiccCand.pi2cPt()); + histos.fill(HIST("BDT/AfterSel/hPiccPt"), bdtPredictedSignal, xiccCand.piccPt()); + } } } - void processXicc(soa::Filtered const& multiCharmTracks) - { - genericProcessXicc(multiCharmTracks); - } - - void processXiccExtra(soa::Filtered const& multiCharmTracks) - { - genericProcessXicc(multiCharmTracks); - } - PROCESS_SWITCH(Alice3Multicharm, processXicc, "find Xicc baryons", true); - PROCESS_SWITCH(Alice3Multicharm, processXiccExtra, "find Xicc baryons with all QA", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/ALICE3/Tasks/alice3Strangeness.cxx b/ALICE3/Tasks/alice3Strangeness.cxx index 7527db49495..4733c899d16 100644 --- a/ALICE3/Tasks/alice3Strangeness.cxx +++ b/ALICE3/Tasks/alice3Strangeness.cxx @@ -20,7 +20,6 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" #include "ALICE3/DataModel/OTFCollision.h" -#include "ALICE3/DataModel/OTFMCParticle.h" #include "ALICE3/DataModel/OTFStrangeness.h" #include "ALICE3/DataModel/tracksAlice3.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -37,6 +36,9 @@ #include #include +#include +#include + #include #include @@ -50,9 +52,9 @@ using namespace o2; using namespace o2::framework; using namespace o2::constants::math; -using Alice3Tracks = soa::Join; +using Alice3Tracks = soa::Join; using FullV0Candidates = soa::Join; -using FullCascadeCandidates = soa::Join; +using FullCascadeCandidates = soa::Join; using FullCollisions = soa::Join; struct Alice3Strangeness { @@ -67,15 +69,17 @@ struct Alice3Strangeness { ConfigurableAxis axisOmegaMass{"axisOmegaMass", {200, 1.57f, 1.77f}, ""}; ConfigurableAxis axisVertexZ{"axisVertexZ", {40, -20, 20}, "vertex Z (cm)"}; ConfigurableAxis axisDCA{"axisDCA", {200, 0, 5}, "DCA (cm)"}; - ConfigurableAxis axisV0Radius{"axisV0Radius", {50, 0.0, 100}, "V0 radius (cm)"}; + ConfigurableAxis axisRadius{"axisRadius", {50, 0.0, 100}, "V0 radius (cm)"}; ConfigurableAxis axisDCAV0Daughters{"axisDCAV0Daughters", {20, 0, 5}, "DCA V0 daughters"}; ConfigurableAxis axisPointingAngle{"axisPointingAngle", {40, 0.0f, 0.4f}, "pointing angle "}; + ConfigurableAxis axisCosPA{"axisCosPA", {100, 0.91f, 1.01f}, "cos(pointing angle) "}; ConfigurableAxis axisProperLifeTime{"axisProperLifeTime", {100, 0.0f, 100.0f}, "proper lifetime (cm)"}; + ConfigurableAxis axisNormalizedDecayLength{"axisNormalizedDecayLength", {100, 0.0f, 20}, "Normalized decay length"}; ConfigurableAxis axisEta{"axisEta", {100, -5.0f, 5.0f}, "eta"}; } histAxes; struct : ConfigurableGroup { - std::string prefix = "selectionFlags"; + std::string prefix = "v0SelectionFlags"; Configurable applyRapiditySelection{"applyRapiditySelection", true, "apply rapidity selection"}; Configurable applyDCAdaughterSelection{"applyDCAdaughterSelection", true, "apply DCA daughter selection"}; Configurable applyCosOfPAngleSelection{"applyCosOfPAngleSelection", true, "apply cosine of pointing angle selection"}; @@ -86,11 +90,11 @@ struct Alice3Strangeness { Configurable applyLifetimeSelection{"applyLifetimeSelection", true, "apply lifetime selection"}; Configurable applyEtaDaughterSelection{"applyEtaDaughterSelection", true, "apply eta daughter selection"}; Configurable doQAforSelectionVariables{"doQAforSelectionVariables", false, "enable QA plots"}; - Configurable analyseOnlyTrueV0s{"analyseOnlyTrueV0s", false, "analyse only true V0s from MC"}; - } selectionFlags; + Configurable analyseRecoV0Candidates{"analyseRecoV0Candidates", false, "analyse reconstructed V0 candidates"}; + } v0SelectionFlags; struct : ConfigurableGroup { - std::string prefix = "selectionValues"; + std::string prefix = "v0SelectionValues"; Configurable yK0Selection{"yK0Selection", 0.5f, "rapidity selection for K0"}; Configurable yLambdaSelection{"yLambdaSelection", 0.5f, "rapidity selection for Lambda"}; Configurable dcaDaughterSelection{"dcaDaughterSelection", 1.0f, "DCA daughter selection"}; @@ -105,9 +109,62 @@ struct Alice3Strangeness { Configurable etaDaughterSelection{"etaDaughterSelection", 0.8f, "eta daughter selection"}; Configurable acceptedLambdaMassWindow{"acceptedLambdaMassWindow", 0.2f, "accepted Lambda mass window around PDG mass"}; Configurable acceptedK0MassWindow{"acceptedK0MassWindow", 0.3f, "accepted K0 mass window around PDG mass"}; - Configurable acceptedXiMassWindow{"acceptedXiMassWindow", 0.5f, "accepted Xi mass window around PDG mass"}; - Configurable acceptedOmegaMassWindow{"acceptedOmegaMassWindow", 0.5f, "accepted Omega mass window around PDG mass"}; - } selectionValues; + } v0SelectionValues; + + struct : ConfigurableGroup { + std::string prefix = "cascadeSelectionValues"; + Configurable posMinConstDCAxy{"posMinConstDCAxy", 0.5f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable posMinPtDepDCAxy{"posMinPtDepDCAxy", 0.f, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable posMinConstDCAz{"posMinConstDCAz", -1.f, "[1] in |DCAz| > [0]+[1]/pT"}; + Configurable posMinPtDepDCAz{"posMinPtDepDCAz", 0.f, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable posMaxNSigma{"posMaxNSigma", 5.f, "Max NSigma from strangeness tof pid"}; + Configurable negMinConstDCAxy{"negMinConstDCAxy", 0.5f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable negMinPtDepDCAxy{"negMinPtDepDCAxy", 0.f, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable negMinConstDCAz{"negMinConstDCAz", -1.f, "[1] in |DCAz| > [0]+[1]/pT"}; + Configurable negMinPtDepDCAz{"negMinPtDepDCAz", 0.f, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable negMaxNSigma{"negMaxNSigma", 5.f, "Max NSigma from strangeness tof pid"}; + Configurable bachMinConstDCAxy{"bachMinConstDCAxy", 0.5f, "[0] in |DCAxy| > [0]+[1]/pT"}; + Configurable bachMinPtDepDCAxy{"bachMinPtDepDCAxy", 0.f, "[1] in |DCAxy| > [0]+[1]/pT"}; + Configurable bachMinConstDCAz{"bachMinConstDCAz", -1.f, "[1] in |DCAz| > [0]+[1]/pT"}; + Configurable bachMinPtDepDCAz{"bachMinPtDepDCAz", 0.f, "[0] in |DCAz| > [0]+[1]/pT"}; + Configurable bachMaxNSigma{"bachMaxNSigma", 5.f, "Max NSigma from strangeness tof pid"}; + Configurable laMaxDauDCA{"laMaxDauDCA", 0.5f, "DCA (cm) between lambda daughters"}; + Configurable laMinDecayRadius{"laMinDecayRadius", 0.5f, "Minimum lambda radius"}; + Configurable laMassWindow{"laMassWindow", 0.5f, "accepted la mass window around PDG mass"}; + Configurable laMinCosPA{"laMinCosPA", 0.98, "Min Lambda CosPA"}; + Configurable cascMaxDauDCA{"cascMaxDauDCA", 0.5f, "DCA (cm) between cascade daughters"}; + Configurable cascMaxNormalizedDecayLength{"cascMaxNormalizedDecayLength", 3, "Max cascade nomralized decay length (ctau/)"}; + Configurable cascMinCosPA{"cascMinCosPA", 0.98, "Minimum cascade CosPA"}; + Configurable cascMinDecayRadius{"cascMinDecayRadius", 0.5f, "Minimum cascade decay radius"}; + Configurable cascMassWindow{"cascMassWindow", 0.5f, "accepted cascade mass window around PDG mass"}; + Configurable competingMassRejection{"competingMassRejection", 0.5f, "competing mass rejection"}; + } cascadeSelectionValues; + + struct : ConfigurableGroup { + std::string prefix = "cascadeFlags"; + Configurable analyseCascade{"analyseCascade", 0, "0: Xi, 1: AntiXi, 2: Omega, 3: AntiOmega"}; + Configurable analyseOnlyTrueCascades{"analyseOnlyTrueCascades", false, "analyse only true cascades from MC"}; + Configurable enable2dPlots{"enable2dPlots", false, "enable 2d plots"}; + Configurable posDCAxy{"posDCAxy", true, "enable posDCAxy selection"}; + Configurable posDCAz{"posDCAz", false, "enable posDCAz selection"}; + Configurable negDCAxy{"negDCAxy", true, "enable negDCAxy selection"}; + Configurable negDCAz{"negDCAz", false, "enable negDCAz selection"}; + Configurable bachDCAxy{"bachDCAxy", true, "enable bachDCAxy selection"}; + Configurable bachDCAz{"bachDCAz", false, "enable bachDCAz selection"}; + Configurable laMaxDauDCA{"laMaxDauDCA", true, "enable laMaxDauDCA selection"}; + Configurable laMinDecayRadius{"laMinDecayRadius", true, "enable laMinDecayRadius selection"}; + Configurable laMassWindow{"laMassWindow", true, "enable laMassWindow selection"}; + Configurable laMinCosPA{"laMinCosPA", true, "enable laMinCosPA selection"}; + Configurable cascMaxDauDCA{"cascMaxDauDCA", true, "enable cascMaxDauDCA selection"}; + Configurable cascMinDecayRadius{"cascMinDecayRadius", true, "enable cascMinDecayRadius selection"}; + Configurable cascMaxNormalizedDecayLength{"cascMaxNormalizedDecayLength", true, "enable cascMaxNormalizedDecayLength selection"}; + Configurable cascMinCosPA{"cascMinCosPA", true, "enable cascMinCosPA selection"}; + Configurable competingMassRejection{"competingMassRejection", false, "enable competingMassRejection selection"}; + Configurable rejectNoInnerTofSignal{"rejectNoInnerTofSignal", false, "reject candidate if track has no signal in tof"}; + Configurable rejectNoOuterTofSignal{"rejectNoOuterTofSignal", false, "reject candidate if track has no signal in tof"}; + Configurable tofInner{"tofInner", true, "enable particle identification selection"}; + Configurable tofOuter{"tofOuter", false, "enable particle identification selection"}; + } cascadeFlags; uint16_t appliedSelectionCheckMask; double selectionCheck; @@ -115,6 +172,114 @@ struct Alice3Strangeness { const int posDaugDCAselIDx = 3; static constexpr std::string_view KSelectionNames[] = {"DCAV0Daughters", "PointingAngle", "DCAtoPVNegDaughter", "DCAtoPVPosDaughter", "V0Radius", "ProperLifeTime"}; + static constexpr float ToMicrons = 1e+4f; + static constexpr float CtauXi = 4.91f; + static constexpr float CtauOmega = 2.461f; + float nSigmaBachInnerTof{}, nSigmaPosInnerTof{}, nSigmaNegInnerTof{}; + float nSigmaBachOuterTof{}, nSigmaPosOuterTof{}, nSigmaNegOuterTof{}; + bool hasBachInnerTof{}, hasPosInnerTof{}, hasNegInnerTof{}; + bool hasBachOuterTof{}, hasPosOuterTof{}, hasNegOuterTof{}; + + struct Cascade { + enum Type { Xi = 0, + AntiXi, + Omega, + AntiOmega }; + Type type{}; + + float pdgMass{}, pdgCompetingMass{}, ctau{}; + int sign{}, pdgCode{}; + + void setCascadeType(Type newType) + { + type = newType; + if (type == Xi || type == AntiXi) { + ctau = CtauXi; + pdgMass = o2::constants::physics::MassXiMinus; + pdgCompetingMass = o2::constants::physics::MassOmegaMinus; + pdgCode = (type == Xi) ? PDG_t::kXiMinus : kXiPlusBar; + sign = (type == Xi) ? -1 : 1; + } + + if (type == Omega || type == AntiOmega) { + ctau = CtauOmega; + pdgMass = o2::constants::physics::MassOmegaMinus; + pdgCompetingMass = o2::constants::physics::MassXiMinus; + pdgCode = (type == Omega) ? PDG_t::kOmegaMinus : kOmegaPlusBar; + sign = (type == Omega) ? -1 : 1; + } + } + } analysedCascade; + + template + void configureNSigmas(const TCascade& cascade) + { + const Cascade::Type analysedCascadeType = static_cast(cascadeFlags.analyseCascade.value); + switch (analysedCascadeType) { + case Cascade::Xi: + nSigmaBachInnerTof = cascade.nSigmaInnerTofXiBachPi(); + nSigmaBachOuterTof = cascade.nSigmaOuterTofXiBachPi(); + nSigmaPosInnerTof = cascade.nSigmaInnerTofXiPosPr(); + nSigmaPosOuterTof = cascade.nSigmaOuterTofXiPosPr(); + nSigmaNegInnerTof = cascade.nSigmaInnerTofXiNegPi(); + nSigmaNegOuterTof = cascade.nSigmaOuterTofXiNegPi(); + hasBachInnerTof = cascade.hasInnerTofXiBachPi(); + hasPosInnerTof = cascade.hasOuterTofXiBachPi(); + hasNegInnerTof = cascade.hasInnerTofXiPosPr(); + hasBachOuterTof = cascade.hasOuterTofXiPosPr(); + hasPosOuterTof = cascade.hasInnerTofXiNegPi(); + hasNegOuterTof = cascade.hasOuterTofXiNegPi(); + + break; + case Cascade::AntiXi: + nSigmaBachInnerTof = cascade.nSigmaInnerTofXiBachPi(); + nSigmaBachOuterTof = cascade.nSigmaOuterTofXiBachPi(); + nSigmaPosInnerTof = cascade.nSigmaInnerTofXiPosPi(); + nSigmaPosOuterTof = cascade.nSigmaOuterTofXiPosPi(); + nSigmaNegInnerTof = cascade.nSigmaInnerTofXiNegPr(); + nSigmaNegOuterTof = cascade.nSigmaOuterTofXiNegPr(); + hasBachInnerTof = cascade.hasInnerTofXiBachPi(); + hasPosInnerTof = cascade.hasOuterTofXiBachPi(); + hasNegInnerTof = cascade.hasInnerTofXiPosPi(); + hasBachOuterTof = cascade.hasOuterTofXiPosPi(); + hasPosOuterTof = cascade.hasInnerTofXiNegPr(); + hasNegOuterTof = cascade.hasOuterTofXiNegPr(); + + break; + case Cascade::Omega: + nSigmaBachInnerTof = cascade.nSigmaInnerTofOmegaBachKa(); + nSigmaBachOuterTof = cascade.nSigmaOuterTofOmegaBachKa(); + nSigmaPosInnerTof = cascade.nSigmaInnerTofOmegaPosPr(); + nSigmaPosOuterTof = cascade.nSigmaOuterTofOmegaPosPr(); + nSigmaNegInnerTof = cascade.nSigmaInnerTofOmegaNegPi(); + nSigmaNegOuterTof = cascade.nSigmaOuterTofOmegaNegPi(); + hasBachInnerTof = cascade.hasInnerTofOmegaBachKa(); + hasPosInnerTof = cascade.hasOuterTofOmegaBachKa(); + hasNegInnerTof = cascade.hasInnerTofOmegaPosPr(); + hasBachOuterTof = cascade.hasOuterTofOmegaPosPr(); + hasPosOuterTof = cascade.hasInnerTofOmegaNegPi(); + hasNegOuterTof = cascade.hasOuterTofOmegaNegPi(); + break; + case Cascade::AntiOmega: + nSigmaBachInnerTof = cascade.nSigmaInnerTofOmegaBachKa(); + nSigmaBachOuterTof = cascade.nSigmaOuterTofOmegaBachKa(); + nSigmaPosInnerTof = cascade.nSigmaInnerTofOmegaPosPi(); + nSigmaPosOuterTof = cascade.nSigmaOuterTofOmegaPosPi(); + nSigmaNegInnerTof = cascade.nSigmaInnerTofOmegaNegPr(); + nSigmaNegOuterTof = cascade.nSigmaOuterTofOmegaNegPr(); + hasBachInnerTof = cascade.hasInnerTofOmegaBachKa(); + hasPosInnerTof = cascade.hasOuterTofOmegaBachKa(); + hasNegInnerTof = cascade.hasInnerTofOmegaPosPi(); + hasBachOuterTof = cascade.hasOuterTofOmegaPosPi(); + hasPosOuterTof = cascade.hasInnerTofOmegaNegPr(); + hasNegOuterTof = cascade.hasOuterTofOmegaNegPr(); + break; + + default: + break; + } + } + void init(InitContext&) { histos.add("K0/hMassAllCandidates", "", kTH2D, {histAxes.axisK0Mass, histAxes.axisPt}); @@ -134,37 +299,103 @@ struct Alice3Strangeness { histos.add("reconstructedCandidates/Lambda/hMass1D", "hMass1D", kTH1D, {histAxes.axisLambdaMass}); histos.add("reconstructedCandidates/hArmeterosBeforeAllSelections", "hArmeterosBeforeAllSelections", kTH2D, {{100, -1.0f, 1.0f}, {200, 0.0f, 0.5f}}); histos.add("reconstructedCandidates/hArmeterosAfterAllSelections", "hArmeterosAfterAllSelections", kTH2D, {{100, -1.0f, 1.0f}, {200, 0.0f, 0.5f}}); + histos.add("reconstructedCandidates/hPtEtaPosDaughter", "hPtEtaPosDaughter", kTH2D, {histAxes.axisPt, histAxes.axisEta}); + histos.add("reconstructedCandidates/hPtEtaNegDaughter", "hPtEtaNegDaughter", kTH2D, {histAxes.axisPt, histAxes.axisEta}); if (doprocessFoundCascadeCandidates) { - histos.add("reconstructedCandidates/Xi/hMassAllCandidates", "hMassAllCandidates", kTH1D, {histAxes.axisXiMass}); - histos.add("reconstructedCandidates/Xi/hMassSelected", "hMassSelected", kTH1D, {histAxes.axisXiMass}); - histos.add("reconstructedCandidates/Omega/hMassAllCandidates", "hMassAllCandidates", kTH1D, {histAxes.axisOmegaMass}); - histos.add("reconstructedCandidates/Omega/hMassSelected", "hMassSelected", kTH1D, {histAxes.axisOmegaMass}); - - histos.addClone("reconstructedCandidates/Xi/", "reconstructedCandidates/AntiXi/"); - histos.addClone("reconstructedCandidates/Omega/", "reconstructedCandidates/AntiOmega/"); + analysedCascade.setCascadeType(static_cast(cascadeFlags.analyseCascade.value)); + histos.add("reconstructedCandidates/Cascade/hMassAllXiCandidates", "hMassAllXiCandidates", kTH1D, {histAxes.axisXiMass}); + histos.add("reconstructedCandidates/Cascade/hMassAllAntiXiCandidates", "hMassAllAntiXiCandidates", kTH1D, {histAxes.axisXiMass}); + histos.add("reconstructedCandidates/Cascade/hMassAllOmegaCandidates", "hMassAllOmegaCandidates", kTH1D, {histAxes.axisOmegaMass}); + histos.add("reconstructedCandidates/Cascade/hMassAllAntiOmegaCandidates", "hMassAllAntiOmegaCandidates", kTH1D, {histAxes.axisOmegaMass}); + + histos.add("reconstructedCandidates/Cascade/hMassSelectedXiCandidates", "hMassSelectedXiCandidates", kTH1D, {histAxes.axisXiMass}); + histos.add("reconstructedCandidates/Cascade/hMassSelectedAntiXiCandidates", "hMassSelectedAntiXiCandidates", kTH1D, {histAxes.axisXiMass}); + histos.add("reconstructedCandidates/Cascade/hMassSelectedOmegaCandidates", "hMassSelectedOmegaCandidates", kTH1D, {histAxes.axisOmegaMass}); + histos.add("reconstructedCandidates/Cascade/hMassSelectedAntiOmegaCandidates", "hMassSelectedAntiOmegaCandidates", kTH1D, {histAxes.axisOmegaMass}); + + histos.add("reconstructedCandidates/Cascade/h3dXiCandidates", "h3dXiCandidates", kTH3D, {histAxes.axisPt, histAxes.axisEta, histAxes.axisXiMass}); + histos.add("reconstructedCandidates/Cascade/h3dAntiXiCandidates", "h3dAntiXiCandidates", kTH3D, {histAxes.axisPt, histAxes.axisEta, histAxes.axisXiMass}); + histos.add("reconstructedCandidates/Cascade/h3dOmegaCandidates", "h3dOmegaCandidates", kTH3D, {histAxes.axisPt, histAxes.axisEta, histAxes.axisOmegaMass}); + histos.add("reconstructedCandidates/Cascade/h3dAntiOmegaCandidates", "h3dAntiOmegaCandidates", kTH3D, {histAxes.axisPt, histAxes.axisEta, histAxes.axisOmegaMass}); + + histos.add("reconstructedCandidates/Cascade/hSelectionQa", "hSelectionQa", kTH1D, {{25, 0.5, 25.5}}); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(1, "all candidates"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(2, "pos dcaXY"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(3, "neg dcaXY"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(4, "bach dcaXY"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(5, "pos dcaZ"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(6, "neg dcaZ"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(7, "bach dcaZ"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(8, "la dau dca"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(9, "la decay radius"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(10, "la mass window"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(11, "la cosPA"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(12, "casc dau dca"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(13, "casc norm decay length"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(14, "casc decay radius"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(15, "casc cosPA"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(16, "competing mass"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(17, "itof nsigma bach"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(18, "itof nsigma pos"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(19, "itof nsigma neg"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(20, "otof nsigma bach"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(21, "otof nsigma pos"); + histos.get(HIST("reconstructedCandidates/Cascade/hSelectionQa"))->GetXaxis()->SetBinLabel(22, "otof nsigma neg"); + + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAxy", "hPosDCAxy", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAxy", "hNegDCAxy", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAxy", "hBachDCAxy", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAz", "hPosDCAz", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAz", "hNegDCAz", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAz", "hBachDCAz", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaDauDCA", "hLaDauDCA", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaDecayRadius", "hLaDecayRadius", kTH1D, {histAxes.axisRadius}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaMassWindow", "hLaMassWindow", kTH1D, {histAxes.axisLambdaMass}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaCosPA", "hLaCosPA", kTH1D, {histAxes.axisCosPA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascDauDCA", "hCascDauDCA", kTH1D, {histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayLength", "hCascDecayLength", kTH1D, {histAxes.axisNormalizedDecayLength}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayRadius", "hCascDecayRadius", kTH1D, {histAxes.axisRadius}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascCosPA", "hCascCosPA", kTH1D, {histAxes.axisCosPA}); + if (cascadeFlags.enable2dPlots) { + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAxyVsPt", "hPosDCAxyVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAxyVsPt", "hNegDCAxyVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAxyVsPt", "hBachDCAxyVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAzVsPt", "hPosDCAzVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAzVsPt", "hNegDCAzVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAzVsPt", "hBachDCAzVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaDauDCAVsPt", "hLaDauDCAVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaDecayRadiusVsPt", "hLaDecayRadiusVsPt", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaMassWindowVsPt", "hLaMassWindowVsPt", kTH2D, {histAxes.axisPt, histAxes.axisLambdaMass}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hLaCosPAVsPt", "hLaCosPAVsPt", kTH2D, {histAxes.axisPt, histAxes.axisCosPA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascDauDCAVsPt", "hCascDauDCAVsPt", kTH2D, {histAxes.axisPt, histAxes.axisDCA}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayLengthVsPt", "hCascDecayLengthVsPt", kTH2D, {histAxes.axisPt, histAxes.axisNormalizedDecayLength}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayRadiusVsPt", "hCascDecayRadiusVsPt", kTH2D, {histAxes.axisPt, histAxes.axisRadius}); + histos.add("reconstructedCandidates/Cascade/BeforeSelection/hCascCosPAVsPt", "hCascCosPAVsPt", kTH2D, {histAxes.axisPt, histAxes.axisCosPA}); + } + histos.addClone("reconstructedCandidates/Cascade/BeforeSelection/", "reconstructedCandidates/Cascade/AfterSelection/"); } - if (selectionFlags.doQAforSelectionVariables) { - if (!selectionFlags.applyDCADaughtersToPVSelection) { + if (v0SelectionFlags.doQAforSelectionVariables) { + if (!v0SelectionFlags.applyDCADaughtersToPVSelection) { histos.add("reconstructedCandidates/K0/hDCAtoPVNegDaughter", "hDCAtoPVNegDaughter", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisDCA}); histos.add("reconstructedCandidates/K0/hDCAtoPVPosDaughter", "hDCAtoPVPosDaughter", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisDCA}); histos.add("reconstructedCandidates/Lambda/hDCAtoPVNegDaughter", "hDCAtoPVNegDaughter", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisDCA}); histos.add("reconstructedCandidates/Lambda/hDCAtoPVPosDaughter", "hDCAtoPVPosDaughter", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisDCA}); } - if (!selectionFlags.applyV0RadiusSelection) { - histos.add("reconstructedCandidates/K0/hV0Radius", "hV0Radius", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisV0Radius}); - histos.add("reconstructedCandidates/Lambda/hV0Radius", "hV0Radius", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisV0Radius}); + if (!v0SelectionFlags.applyV0RadiusSelection) { + histos.add("reconstructedCandidates/K0/hV0Radius", "hV0Radius", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisRadius}); + histos.add("reconstructedCandidates/Lambda/hV0Radius", "hV0Radius", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisRadius}); } - if (!selectionFlags.applyDCAdaughterSelection) { + if (!v0SelectionFlags.applyDCAdaughterSelection) { histos.add("reconstructedCandidates/K0/hDCAV0Daughters", "hDCAV0Daughters", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisDCAV0Daughters}); histos.add("reconstructedCandidates/Lambda/hDCAV0Daughters", "hDCAV0Daughters", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisDCAV0Daughters}); } - if (!selectionFlags.applyCosOfPAngleSelection) { + if (!v0SelectionFlags.applyCosOfPAngleSelection) { histos.add("reconstructedCandidates/K0/hPointingAngle", "hPointingAngle", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisPointingAngle}); histos.add("reconstructedCandidates/Lambda/hPointingAngle", "hPointingAngle", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisPointingAngle}); } - if (!selectionFlags.applyLifetimeSelection) { + if (!v0SelectionFlags.applyLifetimeSelection) { histos.add("reconstructedCandidates/K0/hProperLifeTime", "hProperLifeTime", kTH3D, {histAxes.axisK0Mass, histAxes.axisPt, histAxes.axisProperLifeTime}); histos.add("reconstructedCandidates/Lambda/hProperLifeTime", "hProperLifeTime", kTH3D, {histAxes.axisLambdaMass, histAxes.axisPt, histAxes.axisProperLifeTime}); } @@ -172,19 +403,25 @@ struct Alice3Strangeness { histos.addClone("reconstructedCandidates/Lambda/", "reconstructedCandidates/AntiLambda/"); appliedSelectionCheckMask = 0; - if (!selectionFlags.applyDCAdaughterSelection) + if (!v0SelectionFlags.applyDCAdaughterSelection) { SETBIT(appliedSelectionCheckMask, 0); - if (!selectionFlags.applyCosOfPAngleSelection) + } + if (!v0SelectionFlags.applyCosOfPAngleSelection) { SETBIT(appliedSelectionCheckMask, 1); - if (!selectionFlags.applyDCADaughtersToPVSelection) { + } + if (!v0SelectionFlags.applyDCADaughtersToPVSelection) { SETBIT(appliedSelectionCheckMask, 2); SETBIT(appliedSelectionCheckMask, 3); } - if (!selectionFlags.applyV0RadiusSelection) + if (!v0SelectionFlags.applyV0RadiusSelection) { SETBIT(appliedSelectionCheckMask, 4); - if (!selectionFlags.applyLifetimeSelection) + } + if (!v0SelectionFlags.applyLifetimeSelection) { SETBIT(appliedSelectionCheckMask, 5); + } + histos.print(); } + void processAllFindableCandidates(aod::Collisions const& collisions, aod::McCollisions const&, aod::UpgradeV0s const& v0Recos, aod::UpgradeCascades const& cascRecos, Alice3Tracks const&) { for (const auto& collision : collisions) { @@ -194,38 +431,38 @@ struct Alice3Strangeness { } for (const auto& v0Cand : v0Recos) { - auto negV0Daughter = v0Cand.negTrack_as(); // de-reference neg track - auto posV0Daughter = v0Cand.posTrack_as(); // de-reference pos track + auto negV0Daughter = v0Cand.negTrack_as(); // de-reference negative track + auto posV0Daughter = v0Cand.posTrack_as(); // de-reference positive track - bool isK0 = v0Cand.mK0() > 0; + bool isK0 = v0Cand.mK0Short() > 0; if (isK0) { - histos.fill(HIST("K0/hMassAllCandidates"), v0Cand.mK0(), v0Cand.pt()); + histos.fill(HIST("K0/hMassAllCandidates"), v0Cand.mK0Short(), v0Cand.pt()); histos.fill(HIST("K0/hSelections"), 0); // all candidates histos.fill(HIST("K0/hDCANegDaughter"), negV0Daughter.dcaXY()); histos.fill(HIST("K0/hDCAPosDaughter"), posV0Daughter.dcaXY()); - if (std::abs(negV0Daughter.dcaXY()) < selectionValues.dcaDaughtersToPVSelection) + if (std::abs(negV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) continue; histos.fill(HIST("K0/hSelections"), 1); // dcaXY cut - if (std::abs(posV0Daughter.dcaXY()) < selectionValues.dcaDaughtersToPVSelection) + if (std::abs(posV0Daughter.dcaXY()) < v0SelectionValues.dcaDaughtersToPVSelection) continue; histos.fill(HIST("K0/hSelections"), 2); // dcaXY cut - if (v0Cand.dcaV0Daughters() > selectionValues.dcaDaughterSelection) + if (v0Cand.dcaV0Daughters() > v0SelectionValues.dcaDaughterSelection) continue; histos.fill(HIST("K0/hSelections"), 3); // dca between daughters - if (v0Cand.v0Radius() < selectionValues.v0RadiusSelection) + if (v0Cand.v0Radius() < v0SelectionValues.v0RadiusSelection) continue; histos.fill(HIST("K0/hSelections"), 4); // radius cut - if (std::abs(negV0Daughter.eta()) > selectionValues.etaDaughterSelection || std::abs(posV0Daughter.eta()) > selectionValues.etaDaughterSelection) + if (std::abs(negV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(posV0Daughter.eta()) > v0SelectionValues.etaDaughterSelection) continue; histos.fill(HIST("K0/hSelections"), 5); // eta cut - histos.fill(HIST("K0/hMassSelected"), v0Cand.mK0(), v0Cand.pt()); + histos.fill(HIST("K0/hMassSelected"), v0Cand.mK0Short(), v0Cand.pt()); } } for (const auto& cascCand : cascRecos) { - // auto bach = cascCand.bachTrack_as(); // de-reference bach track - // auto neg = cascCand.negTrack_as(); // de-reference neg track - // auto pos = cascCand.posTrack_as(); // de-reference pos track + // auto bachelor = cascCand.bachTrack_as(); // de-reference bachelor track + // auto negative = cascCand.negTrack_as(); // de-reference negative track + // auto positive = cascCand.posTrack_as(); // de-reference positive track // Only XiMinus in the tracker for now histos.fill(HIST("Xi/hMassAllCandidates"), cascCand.mXi()); @@ -235,78 +472,78 @@ struct Alice3Strangeness { } } - void processFoundV0Candidates(aod::Collision const& collision, FullV0Candidates const& v0Candidates, Alice3Tracks const&, aod::McParticles const&) + void processFoundV0Candidates(aod::Collision const& collision, FullV0Candidates const& v0Candidates, Alice3Tracks const&, aod::McParticles const& mcParticles) { // if(collision.lutConfigId()!=idGeometry) // return; float collisionZ = collision.posZ(); histos.fill(HIST("hPVz"), collisionZ); for (auto const& v0 : v0Candidates) { - bool isK0 = std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < selectionValues.acceptedK0MassWindow; - bool isLambda = std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < selectionValues.acceptedLambdaMassWindow; - bool isAntiLambda = std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < selectionValues.acceptedLambdaMassWindow; + bool isK0 = std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0SelectionValues.acceptedK0MassWindow; + bool isLambda = std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.acceptedLambdaMassWindow; + bool isAntiLambda = std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.acceptedLambdaMassWindow; histos.fill(HIST("reconstructedCandidates/hArmeterosBeforeAllSelections"), v0.alpha(), v0.qtArm()); histos.fill(HIST("hV0CandidateCounter"), 0.5); - if (selectionFlags.applyRapiditySelection) { - if (isK0 && std::abs(v0.yK0Short()) > selectionValues.yK0Selection) + if (v0SelectionFlags.applyRapiditySelection) { + if (isK0 && std::abs(v0.yK0Short()) > v0SelectionValues.yK0Selection) continue; - if ((isLambda || isAntiLambda) && std::abs(v0.yLambda()) > selectionValues.yLambdaSelection) + if ((isLambda || isAntiLambda) && std::abs(v0.yLambda()) > v0SelectionValues.yLambdaSelection) continue; } histos.fill(HIST("hV0CandidateCounter"), 1.5); - if (selectionFlags.applyDCAdaughterSelection) { - if (std::abs(v0.dcaV0Daughters()) > selectionValues.dcaDaughterSelection) + if (v0SelectionFlags.applyDCAdaughterSelection) { + if (std::abs(v0.dcaV0Daughters()) > v0SelectionValues.dcaDaughterSelection) continue; } else { selectionCheck = v0.dcaV0Daughters(); } histos.fill(HIST("hV0CandidateCounter"), 2.5); - if (selectionFlags.applyCosOfPAngleSelection) { - if (v0.cosPA() < selectionValues.cosPAngleSelection) + if (v0SelectionFlags.applyCosOfPAngleSelection) { + if (v0.cosPA() < v0SelectionValues.cosPAngleSelection) continue; } else { selectionCheck = std::acos(v0.cosPA()); } histos.fill(HIST("hV0CandidateCounter"), 3.5); - if (selectionFlags.applyDCADaughtersToPVSelection) { - if ((std::abs(v0.dcaNegToPV()) < selectionValues.dcaDaughtersToPVSelection) || - (std::abs(v0.dcaPosToPV()) < selectionValues.dcaDaughtersToPVSelection)) + if (v0SelectionFlags.applyDCADaughtersToPVSelection) { + if ((std::abs(v0.dcaNegToPV()) < v0SelectionValues.dcaDaughtersToPVSelection) || + (std::abs(v0.dcaPosToPV()) < v0SelectionValues.dcaDaughtersToPVSelection)) continue; } else { selectionCheckPos = std::abs(v0.dcaPosToPV()); selectionCheck = std::abs(v0.dcaNegToPV()); } histos.fill(HIST("hV0CandidateCounter"), 4.5); - if (selectionFlags.applyV0RadiusSelection) { - if (v0.v0radius() < selectionValues.v0RadiusSelection) + if (v0SelectionFlags.applyV0RadiusSelection) { + if (v0.v0radius() < v0SelectionValues.v0RadiusSelection) continue; } else { selectionCheck = v0.v0radius(); } histos.fill(HIST("hV0CandidateCounter"), 5.5); if (isK0) { - if (selectionFlags.applyArmenterosSelection) { - if (v0.qtArm() < selectionValues.armenterosSelection * std::abs(v0.alpha())) + if (v0SelectionFlags.applyArmenterosSelection) { + if (v0.qtArm() < v0SelectionValues.armenterosSelection * std::abs(v0.alpha())) continue; } } histos.fill(HIST("hV0CandidateCounter"), 6.5); - if (isK0 && selectionFlags.applyCompetingMassRejection) { - if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < selectionValues.competingMassRejectionK0) + if (isK0 && v0SelectionFlags.applyCompetingMassRejection) { + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) continue; - if (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < selectionValues.competingMassRejectionK0) + if (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0SelectionValues.competingMassRejectionK0) continue; } - if ((isLambda || isAntiLambda) && selectionFlags.applyCompetingMassRejection) { - if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < selectionValues.competingMassRejectionLambda) + if ((isLambda || isAntiLambda) && v0SelectionFlags.applyCompetingMassRejection) { + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0SelectionValues.competingMassRejectionLambda) continue; } histos.fill(HIST("hV0CandidateCounter"), 7.5); - if (selectionFlags.applyLifetimeSelection) { - if (isK0 && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short > selectionValues.lifetimecutak0) + if (v0SelectionFlags.applyLifetimeSelection) { + if (isK0 && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short > v0SelectionValues.lifetimecutak0) continue; - if ((isLambda || isAntiLambda) && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > selectionValues.lifetimecutambda) + if ((isLambda || isAntiLambda) && v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0SelectionValues.lifetimecutambda) continue; } else { if (isK0) @@ -315,18 +552,32 @@ struct Alice3Strangeness { selectionCheck = v0.distOverTotMom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; } histos.fill(HIST("hV0CandidateCounter"), 8.5); - auto posTrack = v0.template posTrack_as(); - auto negTrack = v0.template negTrack_as(); - if (selectionFlags.applyEtaDaughterSelection) { - if (std::abs(posTrack.eta()) > selectionValues.etaDaughterSelection || std::abs(negTrack.eta()) > selectionValues.etaDaughterSelection) - continue; + if (v0SelectionFlags.analyseRecoV0Candidates) { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + if (v0SelectionFlags.applyEtaDaughterSelection) { + if (std::abs(posTrack.eta()) > v0SelectionValues.etaDaughterSelection || std::abs(negTrack.eta()) > v0SelectionValues.etaDaughterSelection) + continue; + } + histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), posTrack.eta()); + histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), negTrack.eta()); + histos.fill(HIST("hV0CandidateCounter"), 9.5); + histos.fill(HIST("reconstructedCandidates/hPtEtaPosDaughter"), posTrack.pt(), posTrack.eta()); + histos.fill(HIST("reconstructedCandidates/hPtEtaNegDaughter"), negTrack.pt(), negTrack.eta()); + } else { + int posDaugID = v0.posTrackId(); + int negDaugID = v0.negTrackId(); + auto posMcParticle = mcParticles.rawIteratorAt(posDaugID); + auto negMcParticle = mcParticles.rawIteratorAt(negDaugID); + // auto posTrack = v0.posTrack_as(); + // auto negTrack = v0.negTrack_as(); + + histos.fill(HIST("reconstructedCandidates/hPtEtaPosDaughter"), posMcParticle.pt(), posMcParticle.eta()); + histos.fill(HIST("reconstructedCandidates/hPtEtaNegDaughter"), negMcParticle.pt(), negMcParticle.eta()); } - histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), posTrack.eta()); - histos.fill(HIST("reconstructedCandidates/hEtaDaughters"), negTrack.eta()); - histos.fill(HIST("hV0CandidateCounter"), 9.5); histos.fill(HIST("reconstructedCandidates/hArmeterosAfterAllSelections"), v0.alpha(), v0.qtArm()); - if (selectionFlags.doQAforSelectionVariables) { + if (v0SelectionFlags.doQAforSelectionVariables) { static_for<0, 5>([&](auto i) { constexpr int In = i.value; if (TESTBIT(appliedSelectionCheckMask, In)) { @@ -341,6 +592,7 @@ struct Alice3Strangeness { } }); } + if (isK0) { histos.fill(HIST("reconstructedCandidates/K0/hMass"), v0.mK0Short(), v0.pt(), v0.eta()); histos.fill(HIST("reconstructedCandidates/K0/hMass1D"), v0.mK0Short()); @@ -356,45 +608,245 @@ struct Alice3Strangeness { } } - void processFoundCascadeCandidates(aod::Collision const&, FullCascadeCandidates const& cascadeCandidates, Alice3Tracks const&, aod::McParticles const&) + void processFoundCascadeCandidates(aod::Collision const& collision, FullCascadeCandidates const& cascadeCandidates, Alice3Tracks const&, aod::McParticles const&) { - for (const auto& casc : cascadeCandidates) { - const bool isXi = (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) < selectionValues.acceptedXiMassWindow) && casc.sign() > 0; - const bool isOm = (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) < selectionValues.acceptedOmegaMassWindow) && casc.sign() > 0; - const bool isAntiXi = (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) < selectionValues.acceptedXiMassWindow) && casc.sign() < 0; - const bool isAntiOm = (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) < selectionValues.acceptedOmegaMassWindow) && casc.sign() < 0; + for (const auto& cascade : cascadeCandidates) { + if (cascade.sign() < 0) { + histos.fill(HIST("reconstructedCandidates/Cascade/hMassAllXiCandidates"), cascade.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hMassAllOmegaCandidates"), cascade.mOmega()); + } else { + histos.fill(HIST("reconstructedCandidates/Cascade/hMassAllAntiXiCandidates"), cascade.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hMassAllAntiOmegaCandidates"), cascade.mOmega()); + } + + auto positive = cascade.template posTrack_as(); + auto negative = cascade.template negTrack_as(); + auto bachelor = cascade.template bachelor_as(); + + const float posDCAxyCut = cascadeSelectionValues.posMinConstDCAxy + cascadeSelectionValues.posMinPtDepDCAxy / positive.pt(); + const float negDCAxyCut = cascadeSelectionValues.negMinConstDCAxy + cascadeSelectionValues.negMinPtDepDCAxy / negative.pt(); + const float bachDCAxyCut = cascadeSelectionValues.bachMinConstDCAxy + cascadeSelectionValues.bachMinPtDepDCAxy / bachelor.pt(); + const float posDCAzCut = cascadeSelectionValues.posMinConstDCAz + cascadeSelectionValues.posMinPtDepDCAz / positive.pt(); + const float negDCAzCut = cascadeSelectionValues.negMinConstDCAz + cascadeSelectionValues.negMinPtDepDCAz / negative.pt(); + const float bachDCAzCut = cascadeSelectionValues.bachMinConstDCAz + cascadeSelectionValues.bachMinPtDepDCAz / bachelor.pt(); + const float distanceFromPV = std::hypot(cascade.x() - collision.posX(), cascade.y() - collision.posY(), cascade.z() - collision.posZ()); + const float normalizedDecayLength = analysedCascade.pdgMass * distanceFromPV / (cascade.p() * analysedCascade.ctau); + + const float cascadeCandidateMass = (analysedCascade.type == Cascade::Xi || analysedCascade.type == Cascade::AntiXi) ? cascade.mXi() : cascade.mOmega(); + const float cascadeCompetingCandidateMass = (analysedCascade.type == Cascade::Xi || analysedCascade.type == Cascade::AntiXi) ? cascade.mOmega() : cascade.mXi(); + bool isAnalysedCascade = (std::abs(cascadeCandidateMass - analysedCascade.pdgMass) < cascadeSelectionValues.cascMassWindow) && (analysedCascade.sign * cascade.sign() > 0); + if (!isAnalysedCascade) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAxy"), positive.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAxy"), negative.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAxy"), bachelor.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAz"), positive.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAz"), negative.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAz"), bachelor.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaDauDCA"), cascade.dcaV0daughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaDecayRadius"), cascade.v0radius()); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaMassWindow"), cascade.mLambda()); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaCosPA"), cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascDauDCA"), cascade.dcacascdaughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayLength"), normalizedDecayLength); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayRadius"), cascade.cascradius()); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascCosPA"), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + if (cascadeFlags.enable2dPlots) { + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAxyVsPt"), cascade.pt(), positive.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAxyVsPt"), cascade.pt(), negative.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAxyVsPt"), cascade.pt(), bachelor.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hPosDCAzVsPt"), cascade.pt(), positive.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hNegDCAzVsPt"), cascade.pt(), negative.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hBachDCAzVsPt"), cascade.pt(), bachelor.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaDauDCAVsPt"), cascade.pt(), cascade.dcaV0daughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaDecayRadiusVsPt"), cascade.pt(), cascade.v0radius()); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaMassWindowVsPt"), cascade.pt(), cascade.mLambda()); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hLaCosPAVsPt"), cascade.pt(), cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascDauDCAVsPt"), cascade.pt(), cascade.dcacascdaughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayLengthVsPt"), cascade.pt(), normalizedDecayLength); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascDecayRadiusVsPt"), cascade.pt(), cascade.cascradius()); + histos.fill(HIST("reconstructedCandidates/Cascade/BeforeSelection/hCascCosPAVsPt"), cascade.pt(), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + } + if (cascadeFlags.analyseOnlyTrueCascades) { + if (!cascade.has_mcParticle()) { + continue; + } + + auto mcCasc = cascade.template mcParticle_as(); + if (mcCasc.pdgCode() != analysedCascade.pdgCode) { + continue; + } + } - if (isXi) { - histos.fill(HIST("reconstructedCandidates/Xi/hMassAllCandidates"), casc.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 1 /* All candidates*/); + if (cascadeFlags.posDCAxy && std::abs(positive.dcaXY()) < posDCAxyCut) { + continue; } - if (isOm) { - histos.fill(HIST("reconstructedCandidates/Omega/hMassAllCandidates"), casc.mOmega()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 2 /* Pass pos dcaXY*/); + if (cascadeFlags.negDCAxy && std::abs(negative.dcaXY()) < negDCAxyCut) { + continue; } - if (isAntiXi) { - histos.fill(HIST("reconstructedCandidates/AntiXi/hMassAllCandidates"), casc.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 3 /* Pass neg dcaXY*/); + if (cascadeFlags.bachDCAxy && std::abs(bachelor.dcaXY()) < bachDCAxyCut) { + continue; } - if (isAntiOm) { - histos.fill(HIST("reconstructedCandidates/AntiOmega/hMassAllCandidates"), casc.mOmega()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 4 /* Pass bach dcaXY*/); + if (cascadeFlags.posDCAz && std::abs(positive.dcaZ()) < posDCAzCut) { + continue; } - // TODO Add selections - if (isXi) { - histos.fill(HIST("reconstructedCandidates/Xi/hMassSelected"), casc.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 5 /* Pass pos dcaZ*/); + if (cascadeFlags.negDCAz && std::abs(negative.dcaZ()) < negDCAzCut) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 6 /* Pass neg dcaZ*/); + if (cascadeFlags.bachDCAz && std::abs(bachelor.dcaZ()) < bachDCAzCut) { + continue; } - if (isOm) { - histos.fill(HIST("reconstructedCandidates/Omega/hMassSelected"), casc.mOmega()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 7 /* Pass bach dcaZ*/); + if (cascadeFlags.laMaxDauDCA && cascade.dcaV0daughters() > cascadeSelectionValues.laMaxDauDCA) { + continue; } - if (isAntiXi) { - histos.fill(HIST("reconstructedCandidates/AntiXi/hMassSelected"), casc.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 8 /* Pass la dau dca*/); + if (cascadeFlags.laMinDecayRadius && cascade.v0radius() < cascadeSelectionValues.laMinDecayRadius) { + continue; } - if (isAntiOm) { - histos.fill(HIST("reconstructedCandidates/AntiOmega/hMassSelected"), casc.mOmega()); + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 9 /* Pass la decay radius*/); + if (cascadeFlags.laMassWindow && std::abs(cascade.mLambda() - o2::constants::physics::MassLambda0) > cascadeSelectionValues.laMassWindow) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 10 /* Pass la mass window*/); + if (cascadeFlags.laMinCosPA && cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) < cascadeSelectionValues.laMinCosPA) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 11 /* Pass la cosPA*/); + if (cascadeFlags.cascMaxDauDCA && cascade.dcacascdaughters() > cascadeSelectionValues.cascMaxDauDCA) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 12 /* Pass casc dau dca*/); + if (cascadeFlags.cascMaxNormalizedDecayLength && normalizedDecayLength > cascadeSelectionValues.cascMaxNormalizedDecayLength) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 13 /* Pass casc norm decay length*/); + if (cascadeFlags.cascMinDecayRadius && cascade.cascradius() < cascadeSelectionValues.cascMinDecayRadius) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 14 /* Pass casc decay radius*/); + if (cascadeFlags.cascMinCosPA && cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < cascadeSelectionValues.cascMinCosPA) { + continue; + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 15 /* Pass casc cosPA*/); + if (cascadeFlags.competingMassRejection && std::abs(cascadeCompetingCandidateMass - analysedCascade.pdgCompetingMass) < cascadeSelectionValues.competingMassRejection) { + continue; + } + + if (cascadeFlags.tofInner || cascadeFlags.tofOuter) { + configureNSigmas(cascade); + } + + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 16 /* Pass casc competing mass rej*/); + if (cascadeFlags.tofInner) { + const bool applyNSigmaCut = cascadeFlags.rejectNoInnerTofSignal || hasBachInnerTof; + if (applyNSigmaCut && nSigmaBachInnerTof > cascadeSelectionValues.bachMaxNSigma) { + continue; // reject: nSigma cut applies (either TOF signal present, or no-signal tracks are required to pass it too) + } + } + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 17 /* Pass bach itof nsigma*/); + + if (cascadeFlags.tofInner) { + const bool applyNSigmaCut = cascadeFlags.rejectNoInnerTofSignal || hasPosInnerTof; + if (applyNSigmaCut && nSigmaPosInnerTof > cascadeSelectionValues.posMaxNSigma) { + continue; + } + } + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 18 /* Pass pos itof nsigma*/); + + if (cascadeFlags.tofInner) { + const bool applyNSigmaCut = cascadeFlags.rejectNoInnerTofSignal || hasNegInnerTof; + if (applyNSigmaCut && nSigmaNegInnerTof > cascadeSelectionValues.negMaxNSigma) { + continue; + } + } + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 19 /* Pass neg itof nsigma*/); + + if (cascadeFlags.tofOuter) { + const bool applyNSigmaCut = cascadeFlags.rejectNoOuterTofSignal || hasBachOuterTof; + if (applyNSigmaCut && nSigmaBachOuterTof > cascadeSelectionValues.bachMaxNSigma) { + continue; + } + } + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 20 /* Pass bach otof nsigma*/); + + if (cascadeFlags.tofOuter) { + const bool applyNSigmaCut = cascadeFlags.rejectNoOuterTofSignal || hasPosOuterTof; + if (applyNSigmaCut && nSigmaPosOuterTof > cascadeSelectionValues.posMaxNSigma) { + continue; + } + } + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 21 /* Pass pos otof nsigma*/); + + if (cascadeFlags.tofOuter) { + const bool applyNSigmaCut = cascadeFlags.rejectNoOuterTofSignal || hasNegOuterTof; + if (applyNSigmaCut && nSigmaNegOuterTof > cascadeSelectionValues.negMaxNSigma) { + continue; + } + } + histos.fill(HIST("reconstructedCandidates/Cascade/hSelectionQa"), 22 /* Pass neg otof nsigma*/); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hPosDCAxy"), positive.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hNegDCAxy"), negative.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hBachDCAxy"), bachelor.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hPosDCAz"), positive.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hNegDCAz"), negative.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hBachDCAz"), bachelor.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaDauDCA"), cascade.dcaV0daughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaDecayRadius"), cascade.v0radius()); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaMassWindow"), cascade.mLambda()); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaCosPA"), cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascDauDCA"), cascade.dcacascdaughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascDecayLength"), normalizedDecayLength); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascDecayRadius"), cascade.cascradius()); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascCosPA"), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + if (cascadeFlags.enable2dPlots) { + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hPosDCAxyVsPt"), cascade.pt(), positive.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hNegDCAxyVsPt"), cascade.pt(), negative.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hBachDCAxyVsPt"), cascade.pt(), bachelor.dcaXY() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hPosDCAzVsPt"), cascade.pt(), positive.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hNegDCAzVsPt"), cascade.pt(), negative.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hBachDCAzVsPt"), cascade.pt(), bachelor.dcaZ() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaDauDCAVsPt"), cascade.pt(), cascade.dcaV0daughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaDecayRadiusVsPt"), cascade.pt(), cascade.v0radius()); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaMassWindowVsPt"), cascade.pt(), cascade.mLambda()); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hLaCosPAVsPt"), cascade.pt(), cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascDauDCAVsPt"), cascade.pt(), cascade.dcacascdaughters() * ToMicrons); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascDecayLengthVsPt"), cascade.pt(), normalizedDecayLength); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascDecayRadiusVsPt"), cascade.pt(), cascade.cascradius()); + histos.fill(HIST("reconstructedCandidates/Cascade/AfterSelection/hCascCosPAVsPt"), cascade.pt(), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + } + if (cascade.sign() < 0) { + histos.fill(HIST("reconstructedCandidates/Cascade/hMassSelectedXiCandidates"), cascade.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hMassSelectedOmegaCandidates"), cascade.mOmega()); + histos.fill(HIST("reconstructedCandidates/Cascade/h3dXiCandidates"), cascade.pt(), cascade.eta(), cascade.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/h3dOmegaCandidates"), cascade.pt(), cascade.eta(), cascade.mOmega()); + } else { + histos.fill(HIST("reconstructedCandidates/Cascade/hMassSelectedAntiXiCandidates"), cascade.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/hMassSelectedAntiOmegaCandidates"), cascade.mOmega()); + histos.fill(HIST("reconstructedCandidates/Cascade/h3dAntiXiCandidates"), cascade.pt(), cascade.eta(), cascade.mXi()); + histos.fill(HIST("reconstructedCandidates/Cascade/h3dAntiOmegaCandidates"), cascade.pt(), cascade.eta(), cascade.mOmega()); } } } diff --git a/ALICE3/Tasks/alice3TrackingPerformance.cxx b/ALICE3/Tasks/alice3TrackingPerformance.cxx index 81470ee2d2e..fa69139b33a 100644 --- a/ALICE3/Tasks/alice3TrackingPerformance.cxx +++ b/ALICE3/Tasks/alice3TrackingPerformance.cxx @@ -34,39 +34,69 @@ #include #include +#include +#include #include #include #include -#include #include using namespace o2; using namespace o2::framework; -std::shared_ptr particlePdgCodes; -std::map> particlePtDistribution; -std::map> particleEtaDistribution; -std::map> ptDistribution; -std::map> ptResolutionVsPt; -std::map> ptResolutionVsEta; -std::map> invPtResolutionVsPt; -std::map> invPtResolutionVsEta; -std::map> dcaXyResolutionVsPt; -std::map> dcaZResolutionVsPt; struct Alice3TrackingPerformance { - Configurable> pdgCodes{"pdgCodes", {0, 211}, "List of PDG codes to consider for efficiency calculation. (0 means all)"}; + Configurable> pdgCodes{"pdgCodes", {211}, "List of signed PDG codes to consider for performance evaluation"}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable> etaRange{"etaRange", {-5.f, 5.f}, "Eta range for efficiency calculation"}; + struct : ConfigurableGroup { + std::string prefix = "Axes"; // JSON group name + ConfigurableAxis ptAxis{"ptAxis", {500, 0, 100}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis etaAxis{"etaAxis", {100, -5.f, 5.f}, "#eta"}; + ConfigurableAxis invPtDeltaAxis{"invPtDeltaAxis", {1000, -0.1f, 0.1f}, "1./#it{p}_{T}^{gen} - 1./#it{p}_{T}^{reco} (GeV/#it{c})^{-1}"}; + ConfigurableAxis dcaAxis{"dcaAxis", {1000, -0.1f, 0.1f}, "DCA (cm)"}; + } settingsAxes; + + struct : ConfigurableGroup { + std::string prefix = "Selections"; // JSON group name + Configurable vtxSelX{"vtxSelX", -1.f, "Vertex selection in X (cm), if negative no selection is applied"}; + Configurable vtxSelY{"vtxSelY", -1.f, "Vertex selection in Y (cm), if negative no selection is applied"}; + Configurable vtxSelZ{"vtxSelZ", -1.f, "Vertex selection in Z (cm), if negative no selection is applied"}; + } settingsSelections; + + // Histograms + std::shared_ptr particlePdgCodes; + std::map> particlePtDistribution; + std::map> particleEtaDistribution; + std::map> ptDistribution; + std::map> ptResolutionVsPt; + std::map> ptResolutionVsEta; + std::map> invPtResolutionVsPt; + std::map> invPtResolutionVsEta; + std::map> dcaXyResolutionVsPt; + std::map> dcaZResolutionVsPt; + std::map> covariancePtPtVsPt; + std::map> covarianceDcaXyDcaXyVsPt; + std::map> covarianceDcaZDcaZVsPt; void init(o2::framework::InitContext&) { - const AxisSpec axisPt{500, 0, 100, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisEta{100, etaRange.value.first, etaRange.value.second, "#eta"}; + // Check if pdgCodes has duplicates and remove them + std::sort(pdgCodes.value.begin(), pdgCodes.value.end()); + pdgCodes.value.erase(std::unique(pdgCodes.value.begin(), pdgCodes.value.end()), pdgCodes.value.end()); + // Add 0 to the list of pdgCodes if it is not already present + if (std::find(pdgCodes.value.begin(), pdgCodes.value.end(), 0) == pdgCodes.value.end()) { + pdgCodes.value.push_back(0); + } const AxisSpec axisPtDelta{100, -1, 1, "(#it{p}_{T}^{reco} - #it{p}_{T}^{gen}) / #it{p}_{T}^{gen}"}; - const AxisSpec axisInvPtDelta{100, -1, 1, "1./#it{p}_{T}^{gen} - 1./#it{p}_{T}^{reco} (GeV/#it{c})^{-1}"}; - const AxisSpec axisDcaXy{100, -1, 1, "DCA_{xy} (cm)"}; - const AxisSpec axisDcaZ{100, -1, 1, "DCA_{z} (cm)"}; - particlePdgCodes = histos.add("particlePdgCodes", "", kTH1D, {AxisSpec{100, -0.5, 99.5, "PDG Code"}}); + const AxisSpec axisPt{settingsAxes.ptAxis, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisInvPt{settingsAxes.invPtDeltaAxis, "1/#it{p}_{T} (GeV/#it{c})^{-1}"}; + const AxisSpec axisEta{settingsAxes.etaAxis, "#eta"}; + const AxisSpec axisDcaXy{settingsAxes.dcaAxis, "DCA_{xy} (cm)"}; + const AxisSpec axisDcaZ{settingsAxes.dcaAxis, "DCA_{z} (cm)"}; + const AxisSpec axisCovariancePtPt{100, 0, 10, "cov(#it{p}_{T}, #it{p}_{T}) ((GeV/#it{c})^{2})"}; + const AxisSpec axisCovarianceDcaXyDcaXy{100, 0, 1, "cov(DCA_{xy}, DCA_{xy}) (cm^{2})"}; + const AxisSpec axisCovarianceDcaZDcaZ{100, 0, 1, "cov(DCA_{z}, DCA_{z}) (cm^{2})"}; + const int nBinsPdgCodes = static_cast(pdgCodes.value.size()); + particlePdgCodes = histos.add("particlePdgCodes", "", kTH1D, {AxisSpec{nBinsPdgCodes, -0.5, -0.5 + nBinsPdgCodes, "PDG Code"}}); for (const int& pdg : pdgCodes.value) { std::string prefix = Form("%i", pdg); if (pdg < 0) { @@ -74,43 +104,56 @@ struct Alice3TrackingPerformance { } const std::string tag = "_" + prefix; prefix += "/"; - particlePtDistribution[pdg] = histos.add(prefix + "particlePtDistribution" + tag, "", kTH1D, {axisPt}); - particleEtaDistribution[pdg] = histos.add(prefix + "particleEtaDistribution" + tag, "", kTH1D, {axisEta}); - - ptDistribution[pdg] = histos.add(prefix + "ptDistribution" + tag, "", kTH1D, {axisPt}); - ptResolutionVsPt[pdg] = histos.add(prefix + "ptResolutionVsPt" + tag, "", kTH2D, {axisPt, axisPtDelta}); - ptResolutionVsEta[pdg] = histos.add(prefix + "ptResolutionVsEta" + tag, "", kTProfile2D, {axisPt, axisEta}); - invPtResolutionVsPt[pdg] = histos.add(prefix + "invPtResolutionVsPt" + tag, "", kTH2D, {axisPt, axisInvPtDelta}); - invPtResolutionVsEta[pdg] = histos.add(prefix + "invPtResolutionVsEta" + tag, "", kTProfile2D, {axisPt, axisEta}); - dcaXyResolutionVsPt[pdg] = histos.add(prefix + "dcaXyResolutionVsPt" + tag, "", kTH2D, {axisPt, axisDcaXy}); - dcaZResolutionVsPt[pdg] = histos.add(prefix + "dcaZResolutionVsPt" + tag, "", kTH2D, {axisPt, axisDcaZ}); + auto histoName = [&](const std::string& name) { return Form("%s%s%s", prefix.c_str(), name.c_str(), tag.c_str()); }; + particlePtDistribution[pdg] = histos.add(histoName("particlePtDistribution"), "", kTH1D, {axisPt}); + LOG(info) << "Added histogram: " << histoName("particlePtDistribution"); + particleEtaDistribution[pdg] = histos.add(histoName("particleEtaDistribution"), "", kTH1D, {axisEta}); + + ptDistribution[pdg] = histos.add(histoName("ptDistribution"), "", kTH1D, {axisPt}); + ptResolutionVsPt[pdg] = histos.add(histoName("ptResolutionVsPt"), "", kTH2D, {axisPt, axisPtDelta}); + ptResolutionVsEta[pdg] = histos.add(histoName("ptResolutionVsEta"), "", kTProfile2D, {axisPt, axisEta}); + invPtResolutionVsPt[pdg] = histos.add(histoName("invPtResolutionVsPt"), "", kTH2D, {axisPt, axisInvPt}); + invPtResolutionVsEta[pdg] = histos.add(histoName("invPtResolutionVsEta"), "", kTProfile2D, {axisPt, axisEta}); + dcaXyResolutionVsPt[pdg] = histos.add(histoName("dcaXyResolutionVsPt"), "", kTH2D, {axisPt, axisDcaXy}); + dcaZResolutionVsPt[pdg] = histos.add(histoName("dcaZResolutionVsPt"), "", kTH2D, {axisPt, axisDcaZ}); + covariancePtPtVsPt[pdg] = histos.add(histoName("covariancePtPtVsPt"), "", kTH2D, {axisPt, axisCovariancePtPt}); + covarianceDcaXyDcaXyVsPt[pdg] = histos.add(histoName("covarianceDcaXyDcaXyVsPt"), "", kTH2D, {axisPt, axisCovarianceDcaXyDcaXy}); + covarianceDcaZDcaZVsPt[pdg] = histos.add(histoName("covarianceDcaZDcaZVsPt"), "", kTH2D, {axisPt, axisCovarianceDcaZDcaZ}); } } - void process(soa::Join const& tracks, - aod::McParticles const& mcParticles) + void process(soa::Join const& tracks, + aod::McParticles const& mcParticles, + aod::McCollisions const&) { auto isParticleSelected = [&](const o2::aod::McParticle& p) { if (!p.isPhysicalPrimary()) { return false; } - if (p.eta() < etaRange.value.first) { - return false; - } - if (p.eta() > etaRange.value.second) { - return false; - } - return true; + const int etaBin = particleEtaDistribution[0]->GetXaxis()->FindBin(p.eta()); + return etaBin >= 1 && etaBin <= particleEtaDistribution[0]->GetXaxis()->GetNbins(); }; for (const auto& mcParticle : mcParticles) { + // Selecting collisions + const auto& collision = mcParticle.mcCollision(); + if (settingsSelections.vtxSelX >= 0.f && std::abs(collision.posX()) > settingsSelections.vtxSelX) { + continue; + } + if (settingsSelections.vtxSelY >= 0.f && std::abs(collision.posY()) > settingsSelections.vtxSelY) { + continue; + } + if (settingsSelections.vtxSelZ >= 0.f && std::abs(collision.posZ()) > settingsSelections.vtxSelZ) { + continue; + } + particlePdgCodes->Fill(Form("%d", mcParticle.pdgCode()), 1); if (!isParticleSelected(mcParticle)) { continue; } particlePtDistribution[0]->Fill(mcParticle.pt()); particleEtaDistribution[0]->Fill(mcParticle.eta()); - if (particlePtDistribution.find(mcParticle.pdgCode()) == particlePtDistribution.end()) { + if (!particlePtDistribution.contains(mcParticle.pdgCode())) { continue; } particlePtDistribution[mcParticle.pdgCode()]->Fill(mcParticle.pt()); @@ -122,7 +165,8 @@ struct Alice3TrackingPerformance { } const auto& mcParticle = track.mcParticle(); const float ptResolution = (track.pt() - mcParticle.pt()) / mcParticle.pt(); - const float invptResolution = 1.f / track.pt() - 1.f / mcParticle.pt(); + const float invptResolution = (track.pt() > 0.f ? 1.f / track.pt() : 0.f) - (mcParticle.pt() > 0.f ? 1.f / mcParticle.pt() : 0.f); + const float covariancePtPt = track.sigma1Pt() * track.sigma1Pt() * track.pt() * track.pt() * track.pt() * track.pt(); auto fillResolutionHistograms = [&](const int p) { ptDistribution[p]->Fill(track.pt()); @@ -132,6 +176,9 @@ struct Alice3TrackingPerformance { invPtResolutionVsEta[p]->Fill(mcParticle.pt(), mcParticle.eta(), invptResolution); dcaXyResolutionVsPt[p]->Fill(mcParticle.pt(), track.dcaXY()); dcaZResolutionVsPt[p]->Fill(mcParticle.pt(), track.dcaZ()); + covariancePtPtVsPt[p]->Fill(mcParticle.pt(), covariancePtPt); + // covarianceDcaXyDcaXyVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaXY2()); + // covarianceDcaZDcaZVsPt[p]->Fill(mcParticle.pt(), track.sigmaDcaZ2()); }; fillResolutionHistograms(0); @@ -139,7 +186,7 @@ struct Alice3TrackingPerformance { if (!isParticleSelected(mcParticle)) { continue; } - if (ptResolutionVsPt.find(mcParticle.pdgCode()) == ptResolutionVsPt.end()) { + if (!ptResolutionVsPt.contains(mcParticle.pdgCode())) { continue; } fillResolutionHistograms(mcParticle.pdgCode()); diff --git a/ALICE3/Tasks/pidFTOFqa.cxx b/ALICE3/Tasks/pidFTOFqa.cxx deleted file mode 100644 index ba899e4dc85..00000000000 --- a/ALICE3/Tasks/pidFTOFqa.cxx +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ALICE3/DataModel/FTOF.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -namespace o2::aod -{ - -namespace indices -{ -DECLARE_SOA_INDEX_COLUMN(Track, track); -DECLARE_SOA_INDEX_COLUMN(FTOF, ftof); -} // namespace indices - -DECLARE_SOA_INDEX_TABLE_USER(FTOFTracksIndex, Tracks, "FTOFTRK", indices::TrackId, indices::FTOFId); -} // namespace o2::aod - -struct ftofIndexBuilder { - Builds ind; - void init(o2::framework::InitContext&) - { - } -}; - -struct ftofPidQaMC { - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::QAObject}; - Configurable pdgCode{"pdgCode", 0, "pdg code of the particles to accept"}; - Configurable useOnlyPhysicsPrimary{"useOnlyPhysicsPrimary", 1, - "Whether to use only physical primary particles."}; - Configurable minLength{"minLength", 0, "Minimum length of accepted tracks (cm)"}; - Configurable maxLength{"maxLength", 1000, "Maximum length of accepted tracks (cm)"}; - Configurable minEta{"minEta", -1.4, "Minimum eta of accepted tracks"}; - Configurable maxEta{"maxEta", 1.4, "Maximum eta of accepted tracks"}; - Configurable nBinsP{"nBinsP", 500, "Number of momentum bins"}; - Configurable minP{"minP", 0.01, "Minimum momentum plotted (GeV/c)"}; - Configurable maxP{"maxP", 100, "Maximum momentum plotted (GeV/c)"}; - Configurable nBinsNsigma{"nBinsNsigma", 600, "Number of Nsigma bins"}; - Configurable minNsigma{"minNsigma", -100.f, "Minimum Nsigma plotted"}; - Configurable maxNsigma{"maxNsigma", 100.f, "Maximum Nsigma plotted"}; - Configurable nBinsDelta{"nBinsDelta", 100, "Number of delta bins"}; - Configurable minDelta{"minDelta", -1.f, "Minimum delta plotted"}; - Configurable maxDelta{"maxDelta", 1.f, "Maximum delta plotted"}; - Configurable logAxis{"logAxis", 1, "Flag to use a log momentum axis"}; - - void init(o2::framework::InitContext&) - { - AxisSpec momAxis{nBinsP, minP, maxP}; - if (logAxis) { - momAxis.makeLogarithmic(); - } - const AxisSpec nsigmaAxis{nBinsNsigma, minNsigma, maxNsigma}; - const AxisSpec deltaAxis{nBinsDelta, minDelta, maxDelta}; - - histos.add("event/vertexz", ";Vtx_{z} (cm);Entries", kTH1F, {{100, -20, 20}}); - histos.add("event/length", ";FTOF length (cm)", kTH1F, {{100, 0, 100}}); - histos.add("event/eta", ";#it{#eta}", kTH1F, {{100, -10, 10}}); - histos.add("p/Unselected", "Unselected;#it{p} (GeV/#it{c})", kTH1F, {momAxis}); - histos.add("p/Prim", "Primaries;#it{p} (GeV/#it{c})", kTH1F, {momAxis}); - histos.add("p/Sec", "Secondaries;#it{p} (GeV/#it{c})", kTH1F, {momAxis}); - histos.add("pt/Unselected", "Unselected;#it{p} (GeV/#it{c})", kTH1F, {momAxis}); - histos.add("qa/length", ";FTOF length (cm)", kTH1F, {{100, 0, 100}}); - histos.add("qa/signal", ";FTOF signal (ps)", kTH1F, {{100, 0, 1000}}); - histos.add("qa/signalvsP", ";#it{p} (GeV/#it{c});FTOF signal (ps)", kTH2F, {momAxis, {1000, 0, 0.3}}); - histos.add("qa/deltaEl", ";#it{p} (GeV/#it{c});#Delta(e) (ps)", kTH2F, {momAxis, deltaAxis}); - histos.add("qa/deltaMu", ";#it{p} (GeV/#it{c});#Delta(#mu) (ps)", kTH2F, {momAxis, deltaAxis}); - histos.add("qa/deltaPi", ";#it{p} (GeV/#it{c});#Delta(#pi) (ps)", kTH2F, {momAxis, deltaAxis}); - histos.add("qa/deltaKa", ";#it{p} (GeV/#it{c});#Delta(K) (ps)", kTH2F, {momAxis, deltaAxis}); - histos.add("qa/deltaPr", ";#it{p} (GeV/#it{c});#Delta(p) (ps)", kTH2F, {momAxis, deltaAxis}); - histos.add("qa/nsigmaEl", ";#it{p} (GeV/#it{c});N_{#sigma}^{FTOF}(e)", kTH2F, {momAxis, nsigmaAxis}); - histos.add("qa/nsigmaMu", ";#it{p} (GeV/#it{c});N_{#sigma}^{FTOF}(#mu)", kTH2F, {momAxis, nsigmaAxis}); - histos.add("qa/nsigmaPi", ";#it{p} (GeV/#it{c});N_{#sigma}^{FTOF}(#pi)", kTH2F, {momAxis, nsigmaAxis}); - histos.add("qa/nsigmaKa", ";#it{p} (GeV/#it{c});N_{#sigma}^{FTOF}(K)", kTH2F, {momAxis, nsigmaAxis}); - histos.add("qa/nsigmaPr", ";#it{p} (GeV/#it{c});N_{#sigma}^{FTOF}(p)", kTH2F, {momAxis, nsigmaAxis}); - } - - using Trks = soa::Join; - void process(const Trks& tracks, - const aod::McTrackLabels& labels, - const aod::FTOFs&, - const aod::McParticles_000&, - const aod::Collisions& colls) - { - for (const auto& col : colls) { - histos.fill(HIST("event/vertexz"), col.posZ()); - } - for (const auto& track : tracks) { - histos.fill(HIST("event/length"), track.ftof().ftofLength()); - histos.fill(HIST("event/eta"), track.eta()); - if (!track.has_ftof()) { - continue; - } - if (track.ftof().ftofLength() < minLength) { - continue; - } - if (track.ftof().ftofLength() > maxLength) { - continue; - } - if (track.eta() > maxEta || track.eta() < minEta) { - continue; - } - const auto mcParticle = labels.iteratorAt(track.globalIndex()).mcParticle_as(); - if (pdgCode != 0 && abs(mcParticle.pdgCode()) != pdgCode) { - continue; - } - if (useOnlyPhysicsPrimary == 1 && !mcParticle.isPhysicalPrimary()) { // Selecting primaries - histos.fill(HIST("p/Sec"), track.p()); - continue; - } - histos.fill(HIST("p/Prim"), track.p()); - histos.fill(HIST("p/Unselected"), track.p()); - histos.fill(HIST("pt/Unselected"), track.pt()); - histos.fill(HIST("qa/length"), track.ftof().ftofLength()); - histos.fill(HIST("qa/signal"), track.ftof().ftofSignal()); - histos.fill(HIST("qa/signalvsP"), track.p(), track.ftof().ftofSignal()); - histos.fill(HIST("qa/deltaEl"), track.p(), track.ftof().ftofDeltaEl()); - histos.fill(HIST("qa/deltaMu"), track.p(), track.ftof().ftofDeltaMu()); - histos.fill(HIST("qa/deltaPi"), track.p(), track.ftof().ftofDeltaPi()); - histos.fill(HIST("qa/deltaKa"), track.p(), track.ftof().ftofDeltaKa()); - histos.fill(HIST("qa/deltaPr"), track.p(), track.ftof().ftofDeltaPr()); - histos.fill(HIST("qa/nsigmaEl"), track.p(), track.ftof().ftofNsigmaEl()); - histos.fill(HIST("qa/nsigmaMu"), track.p(), track.ftof().ftofNsigmaMu()); - histos.fill(HIST("qa/nsigmaPi"), track.p(), track.ftof().ftofNsigmaPi()); - histos.fill(HIST("qa/nsigmaKa"), track.p(), track.ftof().ftofNsigmaKa()); - histos.fill(HIST("qa/nsigmaPr"), track.p(), track.ftof().ftofNsigmaPr()); - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfg) -{ - return WorkflowSpec{adaptAnalysisTask(cfg), adaptAnalysisTask(cfg)}; -} diff --git a/ALICE3/Tasks/pidRICHqa.cxx b/ALICE3/Tasks/pidRICHqa.cxx deleted file mode 100644 index 4a420ffe34f..00000000000 --- a/ALICE3/Tasks/pidRICHqa.cxx +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file qaTOFMC.cxx -/// \author Nicolò Jacazio -/// \brief Task to produce QA output of the PID with ALICE3 RICH running on the MC. -/// - -#include "ALICE3/DataModel/RICH.h" -#include "Common/DataModel/PIDResponseTOF.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -using namespace o2; -using namespace o2::track; -using namespace o2::framework; -using namespace o2::framework::expressions; - -void customize(std::vector& workflowOptions) -{ - std::vector options{ - {"qa-el", VariantType::Int, 1, {"Produce PID information for the electron mass hypothesis"}}, - {"qa-mu", VariantType::Int, 1, {"Produce PID information for the muon mass hypothesis"}}, - {"qa-pikapr", VariantType::Int, 1, {"Produce PID information for the Pion, Kaon, Proton mass hypothesis"}}}; - std::swap(workflowOptions, options); -} - -#include - -namespace o2::aod -{ - -namespace indices -{ -DECLARE_SOA_INDEX_COLUMN(Track, track); -DECLARE_SOA_INDEX_COLUMN(RICH, rich); -DECLARE_SOA_INDEX_COLUMN(FRICH, frich); -} // namespace indices - -DECLARE_SOA_INDEX_TABLE_USER(RICHTracksIndex, Tracks, "RICHTRK", indices::TrackId, indices::RICHId); -DECLARE_SOA_INDEX_TABLE_USER(FRICHTracksIndex, Tracks, "FRICHTRK", indices::TrackId, indices::FRICHId); -} // namespace o2::aod - -struct richIndexBuilder { // Builder of the RICH-track index linkage - Builds indB; - Builds indF; - void init(o2::framework::InitContext&) - { - } -}; - -template -struct richPidQaMc { - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::QAObject}; - Configurable pdgCode{"pdgCode", 0, "pdg code of the particles to accept"}; - Configurable useOnlyPhysicsPrimary{"useOnlyPhysicsPrimary", 1, - "Whether to use only physical primary particles."}; - Configurable useTOF{"useTOF", 0, - "Whether to use the TOF information"}; - Configurable minLength{"minLength", 0, "Minimum length of accepted tracks (cm)"}; - Configurable maxLength{"maxLength", 1000, "Maximum length of accepted tracks (cm)"}; - Configurable minEta{"minEta", -1.4, "Minimum eta of accepted tracks"}; - Configurable maxEta{"maxEta", 1.4, "Maximum eta of accepted tracks"}; - Configurable nBinsP{"nBinsP", 500, "Number of momentum bins"}; - Configurable minP{"minP", -2, "Minimum momentum plotted (GeV/c)"}; - Configurable maxP{"maxP", 2, "Maximum momentum plotted (GeV/c)"}; - Configurable nBinsNsigma{"nBinsNsigma", 600, "Number of Nsigma bins"}; - Configurable minNsigma{"minNsigma", -100.f, "Minimum Nsigma plotted"}; - Configurable maxNsigma{"maxNsigma", 100.f, "Maximum Nsigma plotted"}; - Configurable nBinsDelta{"nBinsDelta", 600, "Number of delta bins"}; - Configurable minDelta{"minDelta", -0.4f, "Minimum delta plotted (rad)"}; - Configurable maxDelta{"maxDelta", 0.4f, "Maximum delta plotted (rad)"}; - Configurable logAxis{"logAxis", 1, "Flag to use a log momentum axis"}; - Configurable nSigmaNorm{"nSigmaNorm", 0.7071067811865475f, "Normalization for the combined Nsigma"}; - - static constexpr int Np = 5; - static constexpr std::string_view hbRICHDelta[Np] = {"bRICH/delta/El", "bRICH/delta/Mu", "bRICH/delta/Pi", "bRICH/delta/Ka", "bRICH/delta/Pr"}; - static constexpr std::string_view hbRICHNSigma[Np] = {"bRICH/nsigma/El", "bRICH/nsigma/Mu", "bRICH/nsigma/Pi", "bRICH/nsigma/Ka", "bRICH/nsigma/Pr"}; - static constexpr std::string_view hbRICHNSigmaPrm[Np] = {"bRICH/nsigmaprm/El", "bRICH/nsigmaprm/Mu", "bRICH/nsigmaprm/Pi", "bRICH/nsigmaprm/Ka", "bRICH/nsigmaprm/Pr"}; - static constexpr std::string_view hbRICHNSigmaSec[Np] = {"bRICH/nsigmasec/El", "bRICH/nsigmasec/Mu", "bRICH/nsigmasec/Pi", "bRICH/nsigmasec/Ka", "bRICH/nsigmasec/Pr"}; - static constexpr std::string_view hbRICHNSigmaMC[Np] = {"bRICH/nsigmaMC/El", "bRICH/nsigmaMC/Mu", "bRICH/nsigmaMC/Pi", "bRICH/nsigmaMC/Ka", "bRICH/nsigmaMC/Pr"}; - static constexpr std::string_view hbRICHNSigmaMCSec[Np] = {"bRICH/nsigmaMCsec/El", "bRICH/nsigmaMCsec/Mu", "bRICH/nsigmaMCsec/Pi", "bRICH/nsigmaMCsec/Ka", "bRICH/nsigmaMCsec/Pr"}; - static constexpr std::string_view hbRICHNSigmaMCPrm[Np] = {"bRICH/nsigmaMCprm/El", "bRICH/nsigmaMCprm/Mu", "bRICH/nsigmaMCprm/Pi", "bRICH/nsigmaMCprm/Ka", "bRICH/nsigmaMCprm/Pr"}; - - static constexpr std::string_view hfRICHDelta[Np] = {"fRICH/delta/El", "fRICH/delta/Mu", "fRICH/delta/Pi", "fRICH/delta/Ka", "fRICH/delta/Pr"}; - static constexpr std::string_view hfRICHNSigma[Np] = {"fRICH/nsigma/El", "fRICH/nsigma/Mu", "fRICH/nsigma/Pi", "fRICH/nsigma/Ka", "fRICH/nsigma/Pr"}; - static constexpr std::string_view hfRICHNSigmaPrm[Np] = {"fRICH/nsigmaprm/El", "fRICH/nsigmaprm/Mu", "fRICH/nsigmaprm/Pi", "fRICH/nsigmaprm/Ka", "fRICH/nsigmaprm/Pr"}; - static constexpr std::string_view hfRICHNSigmaSec[Np] = {"fRICH/nsigmasec/El", "fRICH/nsigmasec/Mu", "fRICH/nsigmasec/Pi", "fRICH/nsigmasec/Ka", "fRICH/nsigmasec/Pr"}; - static constexpr std::string_view hfRICHNSigmaMC[Np] = {"fRICH/nsigmaMC/El", "fRICH/nsigmaMC/Mu", "fRICH/nsigmaMC/Pi", "fRICH/nsigmaMC/Ka", "fRICH/nsigmaMC/Pr"}; - static constexpr std::string_view hfRICHNSigmaMCSec[Np] = {"fRICH/nsigmaMCsec/El", "fRICH/nsigmaMCsec/Mu", "fRICH/nsigmaMCsec/Pi", "fRICH/nsigmaMCsec/Ka", "fRICH/nsigmaMCsec/Pr"}; - static constexpr std::string_view hfRICHNSigmaMCPrm[Np] = {"fRICH/nsigmaMCprm/El", "fRICH/nsigmaMCprm/Mu", "fRICH/nsigmaMCprm/Pi", "fRICH/nsigmaMCprm/Ka", "fRICH/nsigmaMCprm/Pr"}; - static constexpr std::string_view hfRICHNSigmaVsp[Np] = {"fRICH/nsigmavsp/El", "fRICH/nsigmavsp/Mu", "fRICH/nsigmavsp/Pi", "fRICH/nsigmavsp/Ka", "fRICH/nsigmavsp/Pr"}; - static constexpr std::string_view hfRICHNSigmaMCVsp[Np] = {"fRICH/nsigmaMCvsp/El", "fRICH/nsigmaMCvsp/Mu", "fRICH/nsigmaMCvsp/Pi", "fRICH/nsigmaMCvsp/Ka", "fRICH/nsigmaMCvsp/Pr"}; - static constexpr std::string_view hfRICHNSigmaMCSecVsp[Np] = {"fRICH/nsigmaMCsecvsp/El", "fRICH/nsigmaMCsecvsp/Mu", "fRICH/nsigmaMCsecvsp/Pi", "fRICH/nsigmaMCsecvsp/Ka", "fRICH/nsigmaMCsecvsp/Pr"}; - static constexpr std::string_view hfRICHNSigmaMCPrmVsp[Np] = {"fRICH/nsigmaMCprmvsp/El", "fRICH/nsigmaMCprmvsp/Mu", "fRICH/nsigmaMCprmvsp/Pi", "fRICH/nsigmaMCprmvsp/Ka", "fRICH/nsigmaMCprmvsp/Pr"}; - - static constexpr const char* pT[Np] = {"e", "#mu", "#pi", "K", "p"}; - static constexpr int PDGs[Np] = {11, 13, 211, 321, 2212}; - template - void addParticleHistos() - { - AxisSpec pAxis{nBinsP, minP, maxP, "#it{p} (GeV/#it{c})"}; - AxisSpec ptAxis{nBinsP, minP, maxP, "#it{p}_{T} (GeV/#it{c})"}; - if (logAxis) { - pAxis.makeLogarithmic(); - ptAxis.makeLogarithmic(); - } - const AxisSpec nsigmaAxis{nBinsNsigma, minNsigma, maxNsigma, Form("N_{#sigma}^{RICH}(%s)", pT[pid_type])}; - - TString tit = Form("%s", pT[i]); - if (useTOF) { - tit = Form("TOF Selected %s", pT[i]); - } - // NSigma - histos.add(hbRICHNSigmaMC[i].data(), "True " + tit, HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hbRICHNSigmaMCPrm[i].data(), "True Primary " + tit, HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hbRICHNSigmaMCSec[i].data(), "True Secondary " + tit, HistType::kTH2F, {ptAxis, nsigmaAxis}); - - histos.add(hfRICHNSigmaMC[i].data(), "True " + tit, HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hfRICHNSigmaMCPrm[i].data(), "True Primary " + tit, HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hfRICHNSigmaMCSec[i].data(), "True Secondary " + tit, HistType::kTH2F, {ptAxis, nsigmaAxis}); - - histos.add(hfRICHNSigmaMCVsp[i].data(), "True " + tit, HistType::kTH2F, {pAxis, nsigmaAxis}); - histos.add(hfRICHNSigmaMCPrmVsp[i].data(), "True Primary " + tit, HistType::kTH2F, {pAxis, nsigmaAxis}); - histos.add(hfRICHNSigmaMCSecVsp[i].data(), "True Secondary " + tit, HistType::kTH2F, {pAxis, nsigmaAxis}); - } - - void init(o2::framework::InitContext&) - { - AxisSpec pAxis{nBinsP, minP, maxP, "#it{p} (GeV/#it{c})"}; - AxisSpec ptAxis{nBinsP, minP, maxP, "#it{p}_{T} (GeV/#it{c})"}; - if (logAxis) { - pAxis.makeLogarithmic(); - ptAxis.makeLogarithmic(); - } - - const AxisSpec sigAxis{1000, 0, 0.3, "Cherenkov angle (rad)"}; - const AxisSpec lengthAxis{1000, 0, 3000, "Track length (cm)"}; - const AxisSpec sigErrAxis{100, 0, 1, "Cherenkov angle error (rad)"}; - const char* detName = useTOF ? "TOF-RICH" : "RICH"; - const AxisSpec nsigmaAxis{nBinsNsigma, minNsigma, maxNsigma, Form("N_{#sigma}^{%s}(%s)", detName, pT[pid_type])}; - const AxisSpec deltaAxis{nBinsDelta, minDelta, maxDelta, Form("#Delta(%s) (rad)", pT[pid_type])}; - const AxisSpec etaAxis{100, -4, 4, "#it{#eta}"}; - - histos.add("event/vertexz", ";Vtx_{z} (cm);Entries", kTH1F, {{100, -20, 20}}); - histos.add("particle/p", "", kTH1F, {pAxis}); - histos.add("particle/pt", "", kTH1F, {ptAxis}); - histos.add("particle/eta", "", kTH1F, {etaAxis}); - histos.add("tracks/p", "", kTH1F, {pAxis}); - histos.add("tracks/pt", "", kTH1F, {ptAxis}); - histos.add("tracks/eta", "", kTH1F, {etaAxis}); - histos.add("tracks/length", "", kTH1F, {lengthAxis}); - -#define MakeRICHHistos(rich) \ - histos.add(rich "/TrackSelection", "", kTH1F, {{10, 0.5, 10.5}}); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(1, "Tracks read"); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(2, "Passed RICH"); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(3, "Passed TOF"); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(4, Form("Passed minLength %.2f", minLength.value)); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(5, Form("Passed maxLength %.2f", maxLength.value)); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(6, Form("Passed minEta %.2f", minEta.value)); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(7, Form("Passed maxEta %.2f", maxEta.value)); \ - histos.get(HIST(rich "/TrackSelection"))->GetXaxis()->SetBinLabel(8, "Passed PDG"); \ - histos.add(rich "/pt", "Unselected", kTH1F, {ptAxis}); \ - histos.add(rich "/ptPrm", "Primaries", kTH1F, {ptAxis}); \ - histos.add(rich "/ptSec", "Secondaries", kTH1F, {ptAxis}); \ - histos.add(rich "/p", "Unselected", kTH1F, {pAxis}); \ - histos.add(rich "/pPrm", "Primaries", kTH1F, {pAxis}); \ - histos.add(rich "/pSec", "Secondaries", kTH1F, {pAxis}); \ - histos.add(rich "/signal", "", kTH1F, {sigAxis}); \ - histos.add(rich "/eta", "", kTH1F, {etaAxis}); \ - histos.add(rich "/signalerror", "", kTH1F, {sigErrAxis}); \ - histos.add(rich "/signalvsP", "Unselected", kTH2F, {pAxis, sigAxis}); \ - histos.add(rich "/signalvsPPrm", "Primaries", kTH2F, {pAxis, sigAxis}); \ - histos.add(rich "/signalvsPSec", "Secondaries", kTH2F, {pAxis, sigAxis}); - - MakeRICHHistos("bRICH"); - MakeRICHHistos("fRICH"); - -#undef MakeRICHHistos - - histos.add(hbRICHDelta[pid_type].data(), "", kTH2F, {pAxis, deltaAxis}); - histos.add(hbRICHNSigma[pid_type].data(), "", HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hbRICHNSigmaPrm[pid_type].data(), "Primary", HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hbRICHNSigmaSec[pid_type].data(), "Secondary", HistType::kTH2F, {ptAxis, nsigmaAxis}); - - histos.add(hfRICHDelta[pid_type].data(), "", kTH2F, {pAxis, deltaAxis}); - histos.add(hfRICHNSigma[pid_type].data(), "", HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hfRICHNSigmaPrm[pid_type].data(), "Primary", HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hfRICHNSigmaSec[pid_type].data(), "Secondary", HistType::kTH2F, {ptAxis, nsigmaAxis}); - histos.add(hfRICHNSigmaVsp[pid_type].data(), "", HistType::kTH2F, {pAxis, nsigmaAxis}); - - addParticleHistos<0>(); - addParticleHistos<1>(); - addParticleHistos<2>(); - addParticleHistos<3>(); - addParticleHistos<4>(); - } - - template - void fillNsigma(const T& track, const TT& particle, const float& nsigma) - { - if (abs(particle.pdgCode()) != PDGs[pidIndex]) { - return; - } - histos.fill(HIST(hbRICHNSigmaMC[pidIndex]), track.pt(), nsigma); - - if (particle.isPhysicalPrimary()) { // Selecting primaries - histos.fill(HIST(hbRICHNSigmaMCPrm[pidIndex]), track.pt(), nsigma); - } else { - histos.fill(HIST(hbRICHNSigmaMCSec[pidIndex]), track.pt(), nsigma); - } - } - - template - void fillNsigmafRICH(const T& track, const TT& particle, const float& nsigma) - { - if (abs(particle.pdgCode()) != PDGs[pidIndex]) { - return; - } - histos.fill(HIST(hfRICHNSigmaMC[pidIndex]), track.pt(), nsigma); - histos.fill(HIST(hfRICHNSigmaMCVsp[pidIndex]), track.p(), nsigma); - - if (particle.isPhysicalPrimary()) { // Selecting primaries - histos.fill(HIST(hfRICHNSigmaMCPrm[pidIndex]), track.pt(), nsigma); - histos.fill(HIST(hfRICHNSigmaMCPrmVsp[pidIndex]), track.p(), nsigma); - } else { - histos.fill(HIST(hfRICHNSigmaMCSec[pidIndex]), track.pt(), nsigma); - histos.fill(HIST(hfRICHNSigmaMCSecVsp[pidIndex]), track.p(), nsigma); - } - } - - using Trks = soa::Join; - using TrksfRICH = soa::Join; - void process(const Trks& tracks, - const aod::McParticles_000& mcParticles, - const TrksfRICH& tracksfrich, - const aod::RICHs&, - const aod::FRICHs&, - const aod::Collisions& colls) - { - for (const auto& col : colls) { - histos.fill(HIST("event/vertexz"), col.posZ()); - } - for (const auto& p : mcParticles) { - if (pdgCode != 0 && abs(p.pdgCode()) != pdgCode) { - continue; - } - histos.fill(HIST("particle/p"), p.p()); - histos.fill(HIST("particle/pt"), p.pt()); - histos.fill(HIST("particle/eta"), p.eta()); - } - - for (const auto& t : tracks) { - histos.fill(HIST("tracks/p"), t.p()); - histos.fill(HIST("tracks/pt"), t.pt()); - histos.fill(HIST("tracks/eta"), t.eta()); - histos.fill(HIST("tracks/length"), t.length()); - } - - auto rejectTrack = [&](const auto& t, auto h) { - if (t.length() < minLength) { - return true; - } - histos.fill(h, 4); - if (t.length() > maxLength) { - return true; - } - histos.fill(h, 5); - if (t.eta() < minEta) { - return true; - } - histos.fill(h, 6); - if (t.eta() > maxEta) { - return true; - } - histos.fill(h, 7); - const auto mcParticle = t.template mcParticle_as(); - if (pdgCode != 0 && abs(mcParticle.pdgCode()) != pdgCode) { - return true; - } - histos.fill(h, 8); - return false; - }; - - for (const auto& track : tracks) { // Barrel RICH - histos.fill(HIST("bRICH/TrackSelection"), 1); - if (!track.has_rich()) { - continue; - } - histos.fill(HIST("bRICH/TrackSelection"), 2); - if (useTOF && !track.hasTOF()) { - continue; - } - histos.fill(HIST("bRICH/TrackSelection"), 3); - if (rejectTrack(track, HIST("bRICH/TrackSelection"))) { - continue; - } - - const float delta = track.rich().richDelta(pid_type); - float nsigma = track.rich().richNsigma(pid_type); - if (useTOF) { - if constexpr (pid_type == 0) { // Electron - nsigma += track.tofNSigmaEl(); - } else if constexpr (pid_type == 1) { // Muon - nsigma += track.tofNSigmaMu(); - } else if constexpr (pid_type == 2) { // Pion - nsigma += track.tofNSigmaPi(); - } else if constexpr (pid_type == 3) { // Kaon - nsigma += track.tofNSigmaKa(); - } else if constexpr (pid_type == 4) { // Proton - nsigma += track.tofNSigmaPr(); - } - nsigma *= nSigmaNorm; // Normalize to 1 - } - - histos.fill(HIST("bRICH/p"), track.p()); - histos.fill(HIST("bRICH/pt"), track.pt()); - histos.fill(HIST("bRICH/eta"), track.eta()); - histos.fill(HIST("bRICH/signal"), track.rich().richSignal()); - histos.fill(HIST("bRICH/signalerror"), track.rich().richSignalError()); - histos.fill(HIST("bRICH/signalvsP"), track.p(), track.rich().richSignal()); - - histos.fill(HIST(hbRICHNSigma[pid_type]), track.pt(), nsigma); - histos.fill(HIST(hbRICHDelta[pid_type]), track.p(), delta); - // const auto mcParticle = labels.iteratorAt(track.globalIndex()).mcParticle_as(); - const auto mcParticle = track.mcParticle_as(); - if (mcParticle.isPhysicalPrimary()) { // Selecting primaries - histos.fill(HIST(hbRICHNSigmaPrm[pid_type]), track.pt(), nsigma); - histos.fill(HIST("bRICH/signalvsPPrm"), track.p(), track.rich().richSignal()); - histos.fill(HIST("bRICH/pPrm"), track.p()); - histos.fill(HIST("bRICH/ptPrm"), track.pt()); - } else { - histos.fill(HIST(hbRICHNSigmaSec[pid_type]), track.pt(), nsigma); - histos.fill(HIST("bRICH/signalvsPSec"), track.p(), track.rich().richSignal()); - histos.fill(HIST("bRICH/pSec"), track.p()); - histos.fill(HIST("bRICH/ptSec"), track.pt()); - } - fillNsigma<0>(track, mcParticle, nsigma); - fillNsigma<1>(track, mcParticle, nsigma); - fillNsigma<2>(track, mcParticle, nsigma); - fillNsigma<3>(track, mcParticle, nsigma); - fillNsigma<4>(track, mcParticle, nsigma); - } - - for (const auto& track : tracksfrich) { // Forward RICH - histos.fill(HIST("fRICH/TrackSelection"), 1); - if (!track.has_frich()) { - continue; - } - histos.fill(HIST("fRICH/TrackSelection"), 2); - if (rejectTrack(track, HIST("fRICH/TrackSelection"))) { - continue; - } - - const float delta = track.frich().frichDelta(pid_type); - const float nsigma = track.frich().frichNsigma(pid_type); - - histos.fill(HIST("fRICH/p"), track.p()); - histos.fill(HIST("fRICH/pt"), track.pt()); - histos.fill(HIST("fRICH/eta"), track.eta()); - histos.fill(HIST("fRICH/signal"), track.frich().frichSignal()); - histos.fill(HIST("fRICH/signalerror"), track.frich().frichSignalError()); - histos.fill(HIST("fRICH/signalvsP"), track.p(), track.frich().frichSignal()); - - histos.fill(HIST(hfRICHNSigma[pid_type]), track.pt(), nsigma); - histos.fill(HIST(hfRICHDelta[pid_type]), track.p(), delta); - const auto mcParticle = track.mcParticle_as(); - if (mcParticle.isPhysicalPrimary()) { // Selecting primaries - histos.fill(HIST(hfRICHNSigmaPrm[pid_type]), track.pt(), nsigma); - histos.fill(HIST("fRICH/signalvsPPrm"), track.p(), track.frich().frichSignal()); - histos.fill(HIST("fRICH/pPrm"), track.p()); - histos.fill(HIST("fRICH/ptPrm"), track.pt()); - } else { - histos.fill(HIST(hfRICHNSigmaSec[pid_type]), track.pt(), nsigma); - histos.fill(HIST("fRICH/signalvsPSec"), track.p(), track.frich().frichSignal()); - histos.fill(HIST("fRICH/pSec"), track.p()); - histos.fill(HIST("fRICH/ptSec"), track.pt()); - } - fillNsigmafRICH<0>(track, mcParticle, nsigma); - fillNsigmafRICH<1>(track, mcParticle, nsigma); - fillNsigmafRICH<2>(track, mcParticle, nsigma); - fillNsigmafRICH<3>(track, mcParticle, nsigma); - fillNsigmafRICH<4>(track, mcParticle, nsigma); - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfg) -{ - auto workflow = WorkflowSpec{adaptAnalysisTask(cfg)}; - if (cfg.options().get("qa-el")) { - workflow.push_back(adaptAnalysisTask>(cfg, TaskName{"pidRICH-qa-El"})); - } - if (cfg.options().get("qa-mu")) { - workflow.push_back(adaptAnalysisTask>(cfg, TaskName{"pidRICH-qa-Mu"})); - } - if (cfg.options().get("qa-pikapr")) { - workflow.push_back(adaptAnalysisTask>(cfg, TaskName{"pidRICH-qa-Pi"})); - workflow.push_back(adaptAnalysisTask>(cfg, TaskName{"pidRICH-qa-Ka"})); - workflow.push_back(adaptAnalysisTask>(cfg, TaskName{"pidRICH-qa-Pr"})); - } - return workflow; -} diff --git a/ALICE3/Utils/a3StrangenessTofPidHelper.h b/ALICE3/Utils/a3StrangenessTofPidHelper.h new file mode 100644 index 00000000000..64620419f43 --- /dev/null +++ b/ALICE3/Utils/a3StrangenessTofPidHelper.h @@ -0,0 +1,386 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file a3StrangenessTofPidHelper.h +/// \brief Helper to calculate expected arrival time for decay products of weakly decaying particles +/// \author Jesper Karlsson Gumprecht +/// + +#ifndef ALICE3_UTILS_A3STRANGENESSTOFPIDHELPER_H_ +#define ALICE3_UTILS_A3STRANGENESSTOFPIDHELPER_H_ + +#include "ALICE3/Core/TrackUtilities.h" + +#include +#include + +#include + +#include +#include + +namespace o2::upgrade::stratofpid +{ + +enum class Topology { V0, + Cascade }; + +enum class TrackType { Positive, + Negative, + V0, + Bachelor, + Cascade }; + +enum class CandidateType { K0S, + Lambda, + AntiLambda, + Xi, + AntiXi, + Omega, + AntiOmega }; + +struct TrackTofResult { + bool hasInnerTof, hasOuterTof; + float nSigmaInner, nSigmaOuter; + float expectedTimeInner, expectedTimeOuter; + float measuredTimeInner, measuredTimeOuter; +}; + +template +struct StrangenessTofResults; + +template <> +struct StrangenessTofResults { + TrackTofResult pos; + TrackTofResult neg; +}; + +template <> +struct StrangenessTofResults { + TrackTofResult pos; + TrackTofResult neg; + TrackTofResult bach; +}; + +template +struct CandidateInfo; + +template <> +struct CandidateInfo { + static constexpr float V0Mass = o2::constants::physics::MassKaonNeutral; + static constexpr float PosMass = o2::constants::physics::MassPionCharged; + static constexpr float NegMass = o2::constants::physics::MassPionCharged; +}; +template <> +struct CandidateInfo { + static constexpr float V0Mass = o2::constants::physics::MassLambda0; + static constexpr float PosMass = o2::constants::physics::MassProton; + static constexpr float NegMass = o2::constants::physics::MassPionCharged; +}; +template <> +struct CandidateInfo { + static constexpr float V0Mass = o2::constants::physics::MassLambda0; + static constexpr float PosMass = o2::constants::physics::MassPionCharged; + static constexpr float NegMass = o2::constants::physics::MassProton; +}; +template <> +struct CandidateInfo { + static constexpr float V0Mass = o2::constants::physics::MassLambda0; + static constexpr float PosMass = o2::constants::physics::MassProton; + static constexpr float NegMass = o2::constants::physics::MassPionCharged; + static constexpr float BachMass = o2::constants::physics::MassPionCharged; + static constexpr float CascMass = o2::constants::physics::MassXiMinus; +}; +template <> +struct CandidateInfo { + static constexpr float V0Mass = o2::constants::physics::MassLambda0; + static constexpr float PosMass = o2::constants::physics::MassPionCharged; + static constexpr float NegMass = o2::constants::physics::MassProton; + static constexpr float BachMass = o2::constants::physics::MassPionCharged; + static constexpr float CascMass = o2::constants::physics::MassXiMinus; +}; +template <> +struct CandidateInfo { + static constexpr float V0Mass = o2::constants::physics::MassLambda0; + static constexpr float PosMass = o2::constants::physics::MassProton; + static constexpr float NegMass = o2::constants::physics::MassPionCharged; + static constexpr float BachMass = o2::constants::physics::MassKaonCharged; + static constexpr float CascMass = o2::constants::physics::MassOmegaMinus; +}; +template <> +struct CandidateInfo { + static constexpr float V0Mass = o2::constants::physics::MassLambda0; + static constexpr float PosMass = o2::constants::physics::MassPionCharged; + static constexpr float NegMass = o2::constants::physics::MassProton; + static constexpr float BachMass = o2::constants::physics::MassKaonCharged; + static constexpr float CascMass = o2::constants::physics::MassOmegaMinus; +}; + +class StrangenessTofPidBase +{ + public: + StrangenessTofPidBase() : mRand(0) {} + void setRandomSeed(const int seed) { mRand.SetSeed(seed); } + void setMagneticField(const float b) { mMagneticField = b; } + void setInnerResolution(const float res) { mInnerResolution = res; } + void setOuterResolution(const float res) { mOuterResolution = res; } + void setResolution(const float ires, const float ores) + { + setInnerResolution(ires); + setOuterResolution(ores); + } + void setResolution(const float res) + { + setInnerResolution(res); + setOuterResolution(res); + } + void setInnerRadius(const float radius) { mInnerRadius = radius; } + void setOuterRadius(const float radius) { mOuterRadius = radius; } + void setRadius(const float iradius, const float oradius) + { + setInnerRadius(iradius); + setOuterRadius(oradius); + } + void setTrack(const TrackType type, const o2::track::TrackParCov& track, const float vt = -1.f, const float mass = -1.f) + { + switch (type) { + case TrackType::Positive: + mPosTrack = track; + mPosTrackTime = vt * NanoToPico; + mTruePosCandMass = mass; + break; + case TrackType::Negative: + mNegTrack = track; + mNegTrackTime = vt * NanoToPico; + mTrueNegCandMass = mass; + break; + case TrackType::V0: + mV0Track = track; + break; + + default: + break; + } + } + + bool propagateTrackToOriginalRadius(o2::track::TrackParCov& track, const std::array originalVtx) const + { + static constexpr float Tolerance = 1e-2; + std::array trackXYZ{}; + track.getXYZGlo(trackXYZ); + + // Check if track is already in the right place + if (std::abs(std::hypot(trackXYZ[0], trackXYZ[1]) - std::hypot(originalVtx[0], originalVtx[1])) < Tolerance) { + return true; + } + + float targetX = 1e+3; + if (!track.getXatLabR(std::hypot(originalVtx[0], originalVtx[1]), targetX, mMagneticField)) { + return false; + } + + return track.propagateTo(targetX, mMagneticField); + } + + [[nodiscard]] float calculateArrivalTime(const o2::track::TrackParCov& track, const std::array vtx, const float radius, const float mass) const + { + o2::track::TrackParCov propagatedTrack(track); + if (!propagateTrackToOriginalRadius(propagatedTrack, vtx)) { + return FailedToPropagate; + } + + const float trackLength = o2::upgrade::computeTrackLength(propagatedTrack, radius, mMagneticField); + if (trackLength < 0) { + return FailedToPropagate; + } + + const float velocity = o2::upgrade::computeParticleVelocity(propagatedTrack.getP(), mass); + return trackLength / velocity; + } + + // Special case for V0s + [[nodiscard]] float calculateArrivalTimeV0(const o2::track::TrackParCov& track, const std::array pv, const std::array sv, const float mass) const + { + o2::track::TrackParCov propagatedTrack(track); + if (!propagateTrackToOriginalRadius(propagatedTrack, pv)) { + return FailedToPropagate; + } + + const float dx = pv[0] - sv[0]; + const float dy = pv[1] - sv[1]; + const float dz = pv[2] - sv[2]; + const float trackLength = std::hypot(dx, dy, dz); + const float velocity = o2::upgrade::computeParticleVelocity(propagatedTrack.getP(), mass); + return trackLength / velocity; + } + + float smear(const float time, const float sigma) + { + return mRand.Gaus(time, sigma); + } + + template + StrangenessTofResults findNSigmas(const std::array vtx, const std::array vtxV0, const float timeOffset = 0) + { + const float v0ExpectedTime = timeOffset + calculateArrivalTimeV0(mV0Track, vtx, vtxV0, CandidateInfo::V0Mass); + const float posExpectedTimeInner = v0ExpectedTime + calculateArrivalTime(mPosTrack, vtxV0, mInnerRadius, CandidateInfo::PosMass); + const float posExpectedTimeOuter = v0ExpectedTime + calculateArrivalTime(mPosTrack, vtxV0, mOuterRadius, CandidateInfo::PosMass); + const float negExpectedTimeInner = v0ExpectedTime + calculateArrivalTime(mNegTrack, vtxV0, mInnerRadius, CandidateInfo::NegMass); + const float negExpectedTimeOuter = v0ExpectedTime + calculateArrivalTime(mNegTrack, vtxV0, mOuterRadius, CandidateInfo::NegMass); + + if (!mMeasuredTimeSmeared) { + mPosMeasuredTimeInner = smear(mPosTrackTime + calculateArrivalTime(mPosTrack, vtxV0, mInnerRadius, mTruePosCandMass), mInnerResolution); + mPosMeasuredTimeOuter = smear(mPosTrackTime + calculateArrivalTime(mPosTrack, vtxV0, mOuterRadius, mTruePosCandMass), mOuterResolution); + mNegMeasuredTimeInner = smear(mNegTrackTime + calculateArrivalTime(mNegTrack, vtxV0, mInnerRadius, mTrueNegCandMass), mInnerResolution); + mNegMeasuredTimeOuter = smear(mNegTrackTime + calculateArrivalTime(mNegTrack, vtxV0, mOuterRadius, mTrueNegCandMass), mOuterResolution); + mMeasuredTimeSmeared = true; + } + + return StrangenessTofResults{ + .pos = { + .hasInnerTof = (mPosMeasuredTimeInner > 0 && posExpectedTimeInner > 0), + .hasOuterTof = (mPosMeasuredTimeOuter > 0 && posExpectedTimeOuter > 0), + .nSigmaInner = (mPosMeasuredTimeInner - posExpectedTimeInner) / mInnerResolution, + .nSigmaOuter = (mPosMeasuredTimeOuter - posExpectedTimeOuter) / mOuterResolution, + .expectedTimeInner = posExpectedTimeInner * PicoToNano, + .expectedTimeOuter = posExpectedTimeOuter * PicoToNano, + .measuredTimeInner = mPosMeasuredTimeInner * PicoToNano, + .measuredTimeOuter = mPosMeasuredTimeOuter * PicoToNano, + }, + .neg = { + .hasInnerTof = (mNegMeasuredTimeInner > 0 && negExpectedTimeInner > 0), + .hasOuterTof = (mNegMeasuredTimeOuter > 0 && negExpectedTimeOuter > 0), + .nSigmaInner = (mNegMeasuredTimeInner - negExpectedTimeInner) / mInnerResolution, + .nSigmaOuter = (mNegMeasuredTimeOuter - negExpectedTimeOuter) / mOuterResolution, + .expectedTimeInner = negExpectedTimeInner * PicoToNano, + .expectedTimeOuter = negExpectedTimeOuter * PicoToNano, + .measuredTimeInner = mNegMeasuredTimeInner * PicoToNano, + .measuredTimeOuter = mNegMeasuredTimeOuter * PicoToNano, + }}; + } + + void reset() + { + mMeasuredTimeSmeared = false; + mPosTrackTime = 0.f; + mNegTrackTime = 0.f; + mPosMeasuredTimeInner = 0.f; + mPosMeasuredTimeOuter = 0.f; + mNegMeasuredTimeInner = 0.f; + mNegMeasuredTimeOuter = 0.f; + mTruePosCandMass = 0.f; + mTrueNegCandMass = 0.f; + mPosTrack = o2::track::TrackParCov(); + mNegTrack = o2::track::TrackParCov(); + mV0Track = o2::track::TrackParCov(); + } + + protected: + static constexpr float NanoToPico = 1e+3; + static constexpr float PicoToNano = 1e-3; + static constexpr float FailedToPropagate = -1e+8; + + // Detector config + float mInnerResolution = 20.f; // ps + float mOuterResolution = 20.f; // ps + float mInnerRadius = 21.f; // cm + float mOuterRadius = 92.f; // cm + float mMagneticField = 20.f; // kG + + // Track properties + bool mMeasuredTimeSmeared = false; + float mPosTrackTime{}, mNegTrackTime{}; + float mPosMeasuredTimeInner{}, mPosMeasuredTimeOuter{}; + float mNegMeasuredTimeInner{}, mNegMeasuredTimeOuter{}; + float mTruePosCandMass{}, mTrueNegCandMass{}; + o2::track::TrackParCov mPosTrack, mNegTrack, mV0Track; + + // For smearing + TRandom3 mRand; +}; + +template +class StrangenessTofPid; + +template <> +class StrangenessTofPid : public StrangenessTofPidBase +{ +}; + +template <> +class StrangenessTofPid : public StrangenessTofPidBase +{ + public: + void setTrack(const TrackType type, const o2::track::TrackParCov& track, const float vt = -1.f, const float mass = -1.f) + { + switch (type) { + case TrackType::Bachelor: + mBachTrack = track; + mBachTrackTime = vt * NanoToPico; + mTrueBachCandMass = mass; + break; + case TrackType::Cascade: + mCascTrack = track; + break; + default: + StrangenessTofPidBase::setTrack(type, track, vt, mass); + break; + } + } + + template + StrangenessTofResults findNSigmas(const std::array vtx, const std::array vtxCascade, const std::array vtxV0) + { + const float cascExpectedTime = calculateArrivalTime(mCascTrack, vtx, std::hypot(vtxCascade[0], vtxCascade[1]), CandidateInfo::CascMass); + const float bachExpectedTimeInner = cascExpectedTime + calculateArrivalTime(mBachTrack, vtxCascade, mInnerRadius, CandidateInfo::BachMass); + const float bachExpectedTimeOuter = cascExpectedTime + calculateArrivalTime(mBachTrack, vtxCascade, mOuterRadius, CandidateInfo::BachMass); + + if (!mMeasuredTimeSmeared) { + mBachMeasuredTimeInner = smear(mBachTrackTime + calculateArrivalTime(mBachTrack, vtxCascade, mInnerRadius, mTrueBachCandMass), mInnerResolution); + mBachMeasuredTimeOuter = smear(mBachTrackTime + calculateArrivalTime(mBachTrack, vtxCascade, mOuterRadius, mTrueBachCandMass), mOuterResolution); + } + + const auto straTofResultsV0 = StrangenessTofPidBase::findNSigmas(vtxCascade, vtxV0, cascExpectedTime); + return StrangenessTofResults{ + .pos = straTofResultsV0.pos, + .neg = straTofResultsV0.neg, + .bach = { + .hasInnerTof = (mBachMeasuredTimeInner > 0 && bachExpectedTimeInner > 0), + .hasOuterTof = (mBachMeasuredTimeOuter > 0 && bachExpectedTimeOuter > 0), + .nSigmaInner = (mBachMeasuredTimeInner - bachExpectedTimeInner) / mInnerResolution, + .nSigmaOuter = (mBachMeasuredTimeOuter - bachExpectedTimeOuter) / mOuterResolution, + .expectedTimeInner = bachExpectedTimeInner * PicoToNano, + .expectedTimeOuter = bachExpectedTimeOuter * PicoToNano, + .measuredTimeInner = mBachMeasuredTimeInner * PicoToNano, + .measuredTimeOuter = mBachMeasuredTimeOuter * PicoToNano, + }}; + } + + void reset() + { + mBachTrackTime = 0.f; + mTrueBachCandMass = 0.f; + mBachMeasuredTimeInner = 0.f; + mBachMeasuredTimeOuter = 0.f; + mBachTrack = o2::track::TrackParCov(); + mCascTrack = o2::track::TrackParCov(); + StrangenessTofPidBase::reset(); + } + + protected: + // Track properties + float mBachTrackTime{}, mTrueBachCandMass{}; + float mBachMeasuredTimeInner{}, mBachMeasuredTimeOuter{}; + o2::track::TrackParCov mBachTrack, mCascTrack; +}; + +} // namespace o2::upgrade::stratofpid +#endif // ALICE3_UTILS_A3STRANGENESSTOFPIDHELPER_H_ diff --git a/CMakeLists.txt b/CMakeLists.txt index af18a4a16f2..a61cc2f123b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,10 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -cmake_minimum_required(VERSION 3.23 FATAL_ERROR) +# 3.27 for $, which O2PhysicsAddWorkflow.cmake uses to give a +# workflow the precompiled header carrier's compile usage requirements without +# also putting it on the link line. Builds use CMake 4.1 (alidist cmake.sh). +cmake_minimum_required(VERSION 3.27 FATAL_ERROR) project(O2Physics VERSION 0.0.1 @@ -21,8 +24,46 @@ cmake_policy(SET CMP0144 NEW) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set_property(GLOBAL PROPERTY REPORT_UNDEFINED_PROPERTIES) +# How much memory this build may actually use, in MiB. +# +# cmake_host_system_information reads the HOST, which is wrong under a container +# memory limit: there is no lxcfs in the CI images, so on a 251 GiB builder +# capped at 50 GiB it reported 251 GiB and the pool below never bound. The +# result was an OOM-killed build that looked like a compiler crash: +# +# c++: fatal error: Killed signal terminated program cc1plus +# +# So prefer the cgroup limit when there is one. cgroup v2 first, then v1; both +# report bytes, and both say "no limit" with a sentinel ("max", or a number so +# large it is really 2^63 rounded down) rather than by being absent. cmake_host_system_information(RESULT _totalmem QUERY TOTAL_PHYSICAL_MEMORY) -math(EXPR _total_analysis_jobs "(${_totalmem}-2048)/2048") +foreach(_cg /sys/fs/cgroup/memory.max /sys/fs/cgroup/memory/memory.limit_in_bytes) + if(EXISTS ${_cg}) + file(READ ${_cg} _cglimit) + string(STRIP "${_cglimit}" _cglimit) + if(_cglimit MATCHES "^[0-9]+$") + math(EXPR _cgmib "${_cglimit}/1048576") + # Anything at or above the host's own memory is "unlimited" expressed as a + # number, not a smaller budget. + if(_cgmib GREATER 0 AND _cgmib LESS ${_totalmem}) + set(_totalmem ${_cgmib}) + message(STATUS "Memory limit from ${_cg}: ${_totalmem} MiB") + break() + endif() + endif() + endif() +endforeach() + +# 8 GiB per job, not 2. Measured on the CI builders with GCC 14.2: the heaviest +# analysis producers peak at 7.4-10.1 GiB each (derivedDataWriter 10.09, +# femtoProducer 8.27, candidateCreatorXic0Omegac0 7.36), and -fmem-report shows +# ~6.4 GB of that is frontend AST held live -- template instantiation over the +# distinct soa::Join types each task subscribes to. At 2048 the pool permitted +# four times what the memory allows, and ninja builds in declaration order, so +# the heavy producers arrive as a cluster rather than spread out. +set(ANALYSIS_COMPILE_MEMORY_MB 8192 CACHE STRING + "Assumed peak memory of one analysis compilation, in MiB") +math(EXPR _total_analysis_jobs "(${_totalmem}-2048)/${ANALYSIS_COMPILE_MEMORY_MB}") if(_total_analysis_jobs LESS_EQUAL 0) set(_total_analysis_jobs 1) endif() diff --git a/CODEOWNERS b/CODEOWNERS index a24c1eefbb0..7a7a8b840a3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -12,62 +12,68 @@ # @alibuild has to be in each line to allow self approval of PRs * @AliceO2Group/core @alibuild @jgrosseo @iarsene @ktf @ddobrigk @dsekihat -/Common/CCDB @alibuild @jgrosseo @iarsene @ekryshen @ddobrigk -/Common/Tools/Multiplicity @alibuild @ddobrigk @victor-gonzalez -/ALICE3 @alibuild @njacazio @hscheid +/Common/CCDB @alibuild @jgrosseo @iarsene @ekryshen @ddobrigk @dsekihat +/Common/Tools/Multiplicity @alibuild @ddobrigk @victor-gonzalez @dsekihat +/ALICE3 @alibuild @njacazio @hscheid @jesgum /DPG @alibuild @chiarazampolli @alcaliva @catalinristea /DPG/Tasks/AOTEvent @alibuild @ekryshen @strogolo @altsybee -/DPG/Tasks/AOTTrack @alibuild @mfaggin @iouribelikov @njacazio @lbariogl @f3sch +/DPG/Tasks/AOTTrack @alibuild @mfaggin @iouribelikov @njacazio @lbariogl /DPG/Tasks/TOF @alibuild @noferini @njacazio -/DPG/Tasks/FT0 @alibuild @jotwinow @sahilupadhyaya92 @andreasmolander @afurs -/DPG/Tasks/FV0 @alibuild @jotwinow @sahilupadhyaya92 @andreasmolander @afurs -/DPG/Tasks/FDD @alibuild @jotwinow @sahilupadhyaya92 @andreasmolander @afurs +/DPG/Tasks/FT0 @alibuild @sahilupadhyaya92 @afurs +/DPG/Tasks/FV0 @alibuild @sahilupadhyaya92 @afurs +/DPG/Tasks/FDD @alibuild @sahilupadhyaya92 @afurs /EventFiltering @alibuild @mpuccio @lietava @fgrosa @ariedel-cern /EventFiltering/PWGHF @alibuild @fgrosa @zhangbiao-phy @mpuccio @lietava @ariedel-cern /EventFiltering/PWGUD @alibuild @pbuehler @mpuccio @lietava @fgrosa @ariedel-cern /EventFiltering/PWGLF @alibuild @mpuccio @ercolessi @ChiaraDeMartin95 @lietava @fgrosa @ariedel-cern /EventFiltering/PWGCF @alibuild @lauraser @mpuccio @lietava @fgrosa @ariedel-cern /EventFiltering/PWGMM @alibuild @aortizve @mpuccio @lietava @fgrosa @ariedel-cern -/EventFiltering/PWGJE @alibuild @fkrizek @nzardosh @raymondEhlers @mpuccio @lietava @fgrosa @ariedel-cern +/EventFiltering/PWGJE @alibuild @fkrizek @nzardosh @jaimenorman @mpuccio @lietava @fgrosa @ariedel-cern /EventFiltering/PWGEM @alibuild @dsekihat @nstrangm @mpuccio @lietava @fgrosa @ariedel-cern -/PWGCF @alibuild @victor-gonzalez @otonvd @shouqiye -/PWGCF/Core @alibuild @jgrosseo -/PWGCF/DataModel @alibuild @jgrosseo @jaelpark @victor-gonzalez @otonvd @shouqiye -/PWGCF/TableProducer @alibuild @jgrosseo @jaelpark @victor-gonzalez @otonvd @shouqiye -/PWGCF/Tasks @alibuild @jgrosseo @jaelpark @victor-gonzalez @otonvd @shouqiye -/PWGCF/EbyEFluctuations @alibuild @SwatiSaha-1997 @isputows @victor-gonzalez @otonvd @shouqiye -/PWGCF/Femto @alibuild @lauraser @ariedel-cern @dimihayl @victor-gonzalez @otonvd @shouqiye -/PWGCF/FemtoDream @alibuild @lauraser @ariedel-cern @dimihayl @victor-gonzalez @otonvd @shouqiye -/PWGCF/Femto3D @alibuild @glromane @sofiatomassini @lauraser @dimihayl @victor-gonzalez @otonvd @shouqiye -/PWGCF/FemtoUniverse @alibuild @prchakra @lgraczykCern @majanik @lauraser @dimihayl @victor-gonzalez @otonvd @shouqiye -/PWGCF/FemtoWorld @alibuild @prchakra @lgraczykCern @majanik @lauraser @dimihayl @victor-gonzalez @otonvd @shouqiye -/PWGCF/Flow @alibuild @majanik @EmilGorm @jaelpark @wenyaCern @victor-gonzalez @otonvd @shouqiye -/PWGCF/GenericFramework @alibuild @EmilGorm @jaelpark @wenyaCern @victor-gonzalez @otonvd @shouqiye -/PWGCF/MultiparticleCorrelations @alibuild @abilandz @jaelpark @wenyaCern @victor-gonzalez @otonvd @shouqiye -/PWGCF/JCorran @alibuild @jaelpark @wenyaCern @victor-gonzalez @otonvd @shouqiye -/PWGCF/TwoParticleCorrelations @alibuild @Luzhiyongg @jaelpark @wenyaCern @victor-gonzalez @otonvd @shouqiye -/PWGCF/Tutorial @alibuild @ariedel-cern @victor-gonzalez @otonvd @shouqiye +/PWGCF @alibuild @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/Core @alibuild @jgrosseo @lauraser @dsarkaralice +/PWGCF/DataModel @alibuild @jgrosseo @jaelpark @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/TableProducer @alibuild @jgrosseo @jaelpark @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/Tasks @alibuild @jgrosseo @jaelpark @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/EbyEFluctuations @alibuild @SwatiSaha-1997 @isputows @victor-gonzalez @dsarkaralice @lauraser @fsii +/PWGCF/Femto @alibuild @lauraser @ariedel-cern @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN +/PWGCF/FemtoDream @alibuild @lauraser @ariedel-cern @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN +/PWGCF/Femto3D @alibuild @glromane @sofiatomassini @lauraser @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN +/PWGCF/FemtoUniverse @alibuild @prchakra @lgraczykCern @majanik @lauraser @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN +/PWGCF/FemtoWorld @alibuild @prchakra @lgraczykCern @majanik @lauraser @dimihayl @victor-gonzalez @dsarkaralice @wrzesaCERN +/PWGCF/Flow @alibuild @majanik @EmilGorm @jaelpark @wenyaCern @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/GenericFramework @alibuild @EmilGorm @jaelpark @wenyaCern @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/MultiparticleCorrelations @alibuild @abilandz @jaelpark @wenyaCern @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/JCorran @alibuild @jaelpark @wenyaCern @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/TwoParticleCorrelations @alibuild @jaelpark @wenyaCern @victor-gonzalez @dsarkaralice @lauraser +/PWGCF/Tutorial @alibuild @ariedel-cern @victor-gonzalez @dsarkaralice @lauraser -/PWGDQ @alibuild @iarsene @mcoquet642 @lucamicheletti93 @XiaozhiBai +#PWG-DQ +/PWGDQ @alibuild @iarsene @XiaozhiBai @mguilbau @axti98 @lucamicheletti93 @glegras @bhagyasahoo @csuire @shreyasiacharya + +#PWG-EM /PWGEM @alibuild @feisenhu @dsekihat @jokonig @hscheid /PWGEM/Dilepton @alibuild @mikesas @rbailhac @dsekihat @feisenhu @hscheid @jokonig -/PWGEM/PhotonMeson @alibuild @mhemmer-cern @dsekihat @jokonig @hscheid @amaringarcia +/PWGEM/PhotonMeson @alibuild @mhemmer-cern @dsekihat @jokonig @hscheid @amaringarcia @reygers + +#PWG-HF /PWGHF @alibuild @vkucera @fcolamar @fgrosa @fcatalan92 @mfaggin @mmazzilli @deepathoms @NicoleBastid @hahassan7 @jpxrk @apalasciano @zhangbiao-phy @gluparel @stefanopolitano @xinyepeng @singhra1994 + # PWG-LF /PWGLF @alibuild @omvazque @skundu692 @mpuccio -/PWGLF/DataModel @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @fmazzasc @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova +/PWGLF/DataModel @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @mario-krueger @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova /PWGLF/Tasks/GlobalEventProperties @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @omvazque /PWGLF/TableProducer/GlobalEventProperties @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @omvazque -/PWGLF/Tasks/Nuspex @alibuild @omvazque @skundu692 @mpuccio @fmazzasc @maciacco @HorstMa -/PWGLF/TableProducer/Nuspex @alibuild @omvazque @skundu692 @mpuccio @fmazzasc @maciacco @HorstMa +/PWGLF/Tasks/Nuspex @alibuild @omvazque @skundu692 @mpuccio @mario-krueger @maciacco @HorstMa +/PWGLF/TableProducer/Nuspex @alibuild @omvazque @skundu692 @mpuccio @mario-krueger @maciacco @HorstMa /PWGLF/Tasks/Resonances @alibuild @omvazque @skundu692 @mpuccio @dmallick2 @smaff92 @prottayCMT /PWGLF/TableProducer/Resonances @alibuild @omvazque @skundu692 @mpuccio @dmallick2 @smaff92 @prottayCMT /PWGLF/Tasks/Strangeness @alibuild @omvazque @skundu692 @mpuccio @ercolessi @romainschotter @lhusova /PWGLF/TableProducer/Strangeness @alibuild @omvazque @mpuccio @skundu692 @ercolessi @romainschotter @lhusova -/PWGLF/TableProducer/QC @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @fmazzasc @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova -/PWGLF/Tasks/QC @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @fmazzasc @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova -/PWGLF/Utils @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @fmazzasc @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova +/PWGLF/TableProducer/QC @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @mario-krueger @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova +/PWGLF/Tasks/QC @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @mario-krueger @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova +/PWGLF/Utils @alibuild @omvazque @skundu692 @mpuccio @gbencedi @abmodak @mario-krueger @maciacco @HorstMa @dmallick2 @smaff92 @ercolessi @romainschotter @prottayCMT @lhusova # PWG-MM (fused with LF, LF conveners included. Directories to be merged in the future) /PWGMM @alibuild @omvazque @mpuccio @skundu692 @aalkin @jgcn @@ -75,15 +81,15 @@ /PWGMM/Lumi @alibuild @omvazque @mpuccio @skundu692 @aalkin @jgcn @gbencedi @abmodak /PWGMM/UE @alibuild @omvazque @mpuccio @skundu692 @aalkin @aortizve @jgcn -/PWGUD @alibuild @amatyja @rolavick -/PWGJE @alibuild @lhavener @maoyx @nzardosh @raymondEhlers @fjonasALICE @mfasDa @mhemmer-cern +/PWGUD @alibuild @amatyja @ekryshen +/PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman @mhemmer-cern /Tools/PIDML @alibuild @saganatt -/Tools/ML @alibuild @fcatalan92 @fmazzasc +/Tools/ML @alibuild @fcatalan92 @fmazzasc @ChSonnabend /Tutorials/PWGCF @alibuild @jgrosseo @victor-gonzalez @zchochul -/Tutorials/PWGDQ @alibuild @iarsene @mcoquet @lucamicheletti93 +/Tutorials/PWGDQ @alibuild @iarsene @XiaozhiBai @mguilbau @axti98 @lucamicheletti93 @glegras @bhagyasahoo @csuire @shreyasiacharya /Tutorials/PWGEM @alibuild @mikesas @rbailhac @dsekihat @ivorobye @feisenhu /Tutorials/PWGHF @alibuild @vkucera @fcolamar @fgrosa @gluparel @xinyepeng -/Tutorials/PWGJE @alibuild @lhavener @maoyx @nzardosh @raymondEhlers @mfasDa @fjonasALICE -/Tutorials/PWGLF @alibuild @alcaliva @lbariogl @chiarapinto @BongHwi @lbarnby @ercolessi @iravasen @njacazio @romainschotter @skundu692 @lhusova +/Tutorials/PWGJE @alibuild @nzardosh @fjonasALICE @jaimenorman +/Tutorials/PWGLF @alibuild @alcaliva @lbariogl @BongHwi @lbarnby @ercolessi @iravasen @njacazio @romainschotter @skundu692 @lhusova /Tutorials/PWGMM @alibuild @aalkin @ddobrigk -/Tutorials/PWGUD @alibuild @pbuehler @amatyja +/Tutorials/PWGUD @alibuild @amatyja @ekryshen diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 96a8077f685..53dade3df6c 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -1 +1,2 @@ -filter=-build/c++11,-build/include_order,-build/namespaces,-readability/fn_size,-readability/todo,-runtime/references,-whitespace/blank_line,-whitespace/braces,-whitespace/comments,-whitespace/indent_namespace,-whitespace/line_length,-whitespace/semicolon,-whitespace/todo +filter=-build/c++11,-build/c++17,-build/include_order,-build/namespaces,-readability/alt_tokens,-readability/fn_size,-readability/nolint,-readability/todo,-runtime/references,-whitespace/blank_line,-whitespace/braces,-whitespace/comments,-whitespace/indent_namespace,-whitespace/line_length,-whitespace/newline,-whitespace/parens,-whitespace/semicolon,-whitespace/todo +extensions=C,c,c++,cc,cl,cpp,cu,cuh,cxx,cxx.in,h,h++,hh,h.in,hpp,hxx,inc,inl,macro diff --git a/Common/CCDB/AnalysisCCDBLinkDef.h b/Common/CCDB/AnalysisCCDBLinkDef.h index bb2da231284..b8253021f88 100644 --- a/Common/CCDB/AnalysisCCDBLinkDef.h +++ b/Common/CCDB/AnalysisCCDBLinkDef.h @@ -14,8 +14,7 @@ /// /// \author Evgeny Kryshen -#ifndef COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ -#define COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -24,5 +23,3 @@ #pragma link C++ class EventSelectionParams + ; #pragma link C++ class TriggerAliases + ; #pragma link C++ class std::map < uint64_t, uint32_t> + ; - -#endif // COMMON_CCDB_ANALYSISCCDBLINKDEF_H_ diff --git a/Common/CCDB/TriggerAliases.cxx b/Common/CCDB/TriggerAliases.cxx index c3d28889bca..5ad08da8798 100644 --- a/Common/CCDB/TriggerAliases.cxx +++ b/Common/CCDB/TriggerAliases.cxx @@ -13,10 +13,11 @@ #include +#include #include #include -std::string aliasLabels[kNaliases] = { +const std::array aliasLabels{ "kINT7", "kEMC7", "kINT7inMUON", diff --git a/Common/CCDB/TriggerAliases.h b/Common/CCDB/TriggerAliases.h index 60dbd965741..ffe00462a5b 100644 --- a/Common/CCDB/TriggerAliases.h +++ b/Common/CCDB/TriggerAliases.h @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -51,7 +52,7 @@ enum triggerAliases { kNaliases }; -extern std::string aliasLabels[kNaliases]; +extern const std::array aliasLabels; class TriggerAliases { diff --git a/Common/CCDB/ctpRateFetcher.cxx b/Common/CCDB/ctpRateFetcher.cxx index 6cf7213f2e7..57bc86446dc 100644 --- a/Common/CCDB/ctpRateFetcher.cxx +++ b/Common/CCDB/ctpRateFetcher.cxx @@ -47,7 +47,7 @@ double ctpRateFetcher::fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStam } else { double_t ret = fetchCTPratesClasses(ccdb, timeStamp, runNumber, "CMTVX-B-NOPF"); if (ret < 0.) { - LOG(info) << "Trying different class"; + LOG(debug) << "Trying different class"; ret = fetchCTPratesClasses(ccdb, timeStamp, runNumber, "CMTVX-NONE"); if ((ret < 0) && fCrashOnNull) { LOG(fatal) << "None of the classes used for lumi found"; @@ -72,7 +72,7 @@ double ctpRateFetcher::fetchCTPratesClasses(o2::ccdb::BasicCCDBManager* /*ccdb*/ } } if (classIndex == -1) { - LOG(warn) << "Trigger class " << className << " not found in CTPConfiguration"; + LOG(debug) << "Trigger class " << className << " not found in CTPConfiguration"; return -1.; } auto rate{mScalers->getRateGivenT(timeStamp * 1.e-3, classIndex, inputType, 1)}; @@ -95,9 +95,7 @@ double ctpRateFetcher::pileUpCorrection(double triggerRate) if (mLHCIFdata == nullptr) { LOG(fatal) << "No filling" << std::endl; } - auto bfilling = mLHCIFdata->getBunchFilling(); - std::vector bcs = bfilling.getFilledBCs(); - double nbc = bcs.size(); + double nbc = mLHCIFdata->getBunchFilling().getPattern().count(); double nTriggersPerFilledBC = triggerRate / nbc / constants::lhc::LHCRevFreq; double mu = -std::log(1 - nTriggersPerFilledBC); return mu * nbc * constants::lhc::LHCRevFreq; diff --git a/Common/CCDB/macros/ctpRateF.C b/Common/CCDB/macros/ctpRateF.C index 6840b3f4e36..2a6666b8bd6 100644 --- a/Common/CCDB/macros/ctpRateF.C +++ b/Common/CCDB/macros/ctpRateF.C @@ -29,12 +29,12 @@ struct ctpRateFetcher { ctpRateFetcher() = default; - double fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string sourceName); + double fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string& sourceName); void getCTPconfig(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber); void getCTPscalers(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber); void getLHCIFdata(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber); double fetchCTPratesInputs(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, int input); - double fetchCTPratesClasses(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string className, int inputType = 1); + double fetchCTPratesClasses(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string& className, int inputType = 1); double pileUpCorrection(double rate); int mRunNumber = -1; @@ -104,7 +104,7 @@ double ctpRateFetcher::fetchCTPratesInputs(o2::ccdb::BasicCCDBManager* ccdb, uin return -1.; } } -double ctpRateFetcher::fetchCTPratesClasses(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string className, int inputType) +double ctpRateFetcher::fetchCTPratesClasses(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string& className, int inputType) { getCTPscalers(ccdb, timeStamp, runNumber); getCTPconfig(ccdb, timeStamp, runNumber); @@ -125,7 +125,7 @@ double ctpRateFetcher::fetchCTPratesClasses(o2::ccdb::BasicCCDBManager* ccdb, ui getLHCIFdata(ccdb, timeStamp, runNumber); return pileUpCorrection(rate.second); } -double ctpRateFetcher::fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, std::string sourceName) +double ctpRateFetcher::fetch(o2::ccdb::BasicCCDBManager* ccdb, uint64_t timeStamp, int runNumber, const std::string& sourceName) { if (sourceName.find("ZNC") != std::string::npos) { if (runNumber < 544448) { diff --git a/Common/CCDB/macros/upload_event_selection_params.C b/Common/CCDB/macros/upload_event_selection_params.C index c6385c4d09d..f5995d730c4 100644 --- a/Common/CCDB/macros/upload_event_selection_params.C +++ b/Common/CCDB/macros/upload_event_selection_params.C @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -33,12 +34,12 @@ void upload_event_selection_params() // ccdb.truncate("EventSelection/EventSelectionParams"); ccdb.init("https://alice-ccdb.cern.ch"); - const int nPeriodsMax = 100; - EventSelectionParams* par[nPeriodsMax]; - string period[nPeriodsMax]; - int runFirst[nPeriodsMax]; - int runLast[nPeriodsMax]; - bool isNew[nPeriodsMax] = {0}; + constexpr int NPeriodsMax{100}; + std::array par{}; + std::array period{}; + std::array runFirst{}; + std::array runLast{}; + std::array isNew{}; int n = 0; period[n] = "pp2010"; @@ -256,7 +257,7 @@ void upload_event_selection_params() auto sor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdb, runFirst[i]).first; auto eor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdb, runLast[i]).second; - printf("sor=%llu eor=%llu\n", sor, eor); + printf("sor=%ld eor=%ld\n", sor, eor); metadata["period"] = period[i]; metadata["run_first"] = Form("%d", runFirst[i]); metadata["run_last"] = Form("%d", runLast[i]); diff --git a/Common/Core/AnalysisCoreLinkDef.h b/Common/Core/AnalysisCoreLinkDef.h index ae4c91e9589..21eb52ac586 100644 --- a/Common/Core/AnalysisCoreLinkDef.h +++ b/Common/Core/AnalysisCoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef COMMON_CORE_ANALYSISCORELINKDEF_H_ -#define COMMON_CORE_ANALYSISCORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -25,7 +24,3 @@ #pragma link C++ class o2::pid::tpc::Response + ; #pragma link C++ class OrbitRange + ; - -#pragma link C++ class FFitWeights + ; - -#endif // COMMON_CORE_ANALYSISCORELINKDEF_H_ diff --git a/Common/Core/CMakeLists.txt b/Common/Core/CMakeLists.txt index 6bf499cd3b7..8e9afaf06c9 100644 --- a/Common/Core/CMakeLists.txt +++ b/Common/Core/CMakeLists.txt @@ -21,9 +21,59 @@ o2physics_add_library(AnalysisCore TableHelper.cxx MetadataHelper.cxx CollisionTypeHelper.cxx - FFitWeights.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsParameters ROOT::EG O2::CCDB ROOT::Physics O2::FT0Base O2::FV0Base O2::DataFormatsParamTOF) +# Precompiled header shared by DPL analysis workflows. Parsing the framework +# headers dominates the cost of a workflow translation unit, and there are +# ~1500 of them; o2physics_add_dpl_workflow reuses this by default. +# +# Skipped under recc, which caches compilations remotely instead -- the two do +# not combine, as a PCH is a local artefact of one compiler invocation. +# +# The reusing targets must compile with the same preprocessor state as this +# carrier, which does not follow from the link list: AnalysisCore reaches +# O2::rANS and its INTERFACE -DRANS_ENABLE_JSON, whereas a bare workflow links +# only O2::Framework. o2physics_add_dpl_workflow therefore copies this target's +# compile definitions onto every consumer; keep that in mind when changing what +# is linked here. +add_library(AnalysisPCH OBJECT analysisPCH.cxx) +target_link_libraries(AnalysisPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not +# put it in the 'analysis' job pool the way o2physics_add_dpl_workflow() does +# for the workflows. Left out, it runs unthrottled next to the pooled +# compilations and blows past the per-job memory budget the pool exists to +# enforce. +set_property(TARGET AnalysisPCH PROPERTY JOB_POOL_COMPILE analysis) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(AnalysisPCH PRIVATE + + + + + # Measured over all 1463 workflow translation units in the tree: these are + # the framework headers a large majority include and that were not + # precompiled here. Percentages are of those 1463. + # 78% + # 78% + # 76% + # 70% + # 69% + # 67% + # NOT runDataProcessing.h. It states the contract itself: "we need to + # declare the customize method before include this file". Precompiling it + # makes it the first include in every workflow, so a source's customize() is + # never seen, its options are never registered, and the binary aborts at + # --dump-workflow with "missing option: ...". The link succeeds, so this + # surfaces as a runtime failure with no hint that a PCH caused it. + + # 51% + + + + ) +endif() + o2physics_target_root_dictionary(AnalysisCore HEADERS TrackSelection.h TrackSelectionDefaults.h @@ -37,7 +87,6 @@ o2physics_target_root_dictionary(AnalysisCore PID/TPCPIDResponse.h PID/PIDTOFParamService.h CollisionTypeHelper.h - FFitWeights.h LINKDEF AnalysisCoreLinkDef.h) o2physics_add_library(EventFilteringUtils @@ -52,3 +101,12 @@ o2physics_target_root_dictionary(EventFilteringUtils o2physics_add_header_only_library(TPCDriftManager HEADERS TPCVDriftManager.h INTERFACE_LINK_LIBRARIES) + +o2physics_add_library(FFitWeights + SOURCES FFitWeights.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::CCDB ROOT::Physics) + +o2physics_target_root_dictionary(FFitWeights + HEADERS FFitWeights.h + LINKDEF FFitWeightsLinkDef.h) + diff --git a/Common/Core/CollisionAssociation.h b/Common/Core/CollisionAssociation.h index 338a37022c9..b0b3748817a 100644 --- a/Common/Core/CollisionAssociation.h +++ b/Common/Core/CollisionAssociation.h @@ -46,6 +46,12 @@ enum TrackSelection { QualityTracksITS = 2 }; +enum PVContrReassocOpt { + Disabled = 0, + OnlySameBc = 1, + SameBcAndLowMult = 2 +}; + } // namespace track_association } // namespace o2::aod @@ -59,10 +65,11 @@ class CollisionAssociation void setNumSigmaForTimeCompat(float nSigma) { mNumSigmaForTimeCompat = nSigma; } void setTimeMargin(float timeMargin) { mTimeMargin = timeMargin; } void setTrackSelectionOptionForStdAssoc(int option) { mTrackSelection = option; } - void setUsePvAssociation(bool enable = true) { mUsePvAssociation = enable; } + void setUsePvAssociation(int option = o2::aod::track_association::PVContrReassocOpt::OnlySameBc) { mUsePvAssociation = option; } void setIncludeUnassigned(bool enable = true) { mIncludeUnassigned = enable; } void setFillTableOfCollIdsPerTrack(bool fill = true) { mFillTableOfCollIdsPerTrack = fill; } void setBcWindow(int bcWindow = 115) { mBcWindowForOneSigma = bcWindow; } + void setMaxPvContributorsForLowMultReassoc(int pvContributorsMax) { mMaxPvContributorsForLowMultReassoc = pvContributorsMax; } template void runStandardAssoc(o2::aod::Collisions const& collisions, @@ -234,7 +241,7 @@ class CollisionAssociation float trackTime = 0; float trackTimeRes = 0; if constexpr (isCentralBarrel) { - if (mUsePvAssociation && trackInWindow.isPVContributor()) { + if ((mUsePvAssociation == o2::aod::track_association::PVContrReassocOpt::OnlySameBc && trackInWindow.isPVContributor()) || (mUsePvAssociation == o2::aod::track_association::PVContrReassocOpt::SameBcAndLowMult && trackInWindow.isPVContributor() && trackInWindow.collision().numContrib() > mMaxPvContributorsForLowMultReassoc)) { trackTime = trackInWindow.collision().collisionTime(); // if PV contributor, we assume the time to be the one of the collision trackTimeRes = o2::constants::lhc::LHCBunchSpacingNS; // 1 BC } else { @@ -252,7 +259,7 @@ class CollisionAssociation float thresholdTime = 0.; if constexpr (isCentralBarrel) { - if (mUsePvAssociation && trackInWindow.isPVContributor()) { + if ((mUsePvAssociation == o2::aod::track_association::PVContrReassocOpt::OnlySameBc && trackInWindow.isPVContributor()) || (mUsePvAssociation == o2::aod::track_association::PVContrReassocOpt::SameBcAndLowMult && trackInWindow.isPVContributor() && trackInWindow.collision().numContrib() > mMaxPvContributorsForLowMultReassoc)) { thresholdTime = trackTimeRes; } else if (TESTBIT(trackInWindow.flags(), o2::aod::track::TrackTimeResIsRange)) { // the track time resolution is a range, not a gaussian resolution @@ -306,7 +313,8 @@ class CollisionAssociation float mNumSigmaForTimeCompat{4.}; // number of sigma for time compatibility float mTimeMargin{500.}; // additional time margin in ns int mTrackSelection{o2::aod::track_association::TrackSelection::GlobalTrackWoDCA}; // track selection for central barrel tracks (standard association only) - bool mUsePvAssociation{true}; // use the information of PV contributors + int mUsePvAssociation{1}; // use the information of PV contributors (0: off, 1: reassociate only with collisions in the same BC, 2: reassociate with collisions in the same BC, or within time resolution in case of low mult PVs) + int mMaxPvContributorsForLowMultReassoc{10}; // maximum value of PV contributors to reassociate tracks that are PV contributors to other vertices bool mIncludeUnassigned{true}; // include tracks that were originally not assigned to any collision bool mFillTableOfCollIdsPerTrack{false}; // fill additional table with vectors of compatible collisions per track int mBcWindowForOneSigma{115}; // BC window to be multiplied by the number of sigmas to define maximum window to be considered diff --git a/Common/Core/EventFilteringUtilsLinkDef.h b/Common/Core/EventFilteringUtilsLinkDef.h index 3f029b8aa9a..394c051784f 100644 --- a/Common/Core/EventFilteringUtilsLinkDef.h +++ b/Common/Core/EventFilteringUtilsLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Common/Core/EventPlaneHelper.cxx b/Common/Core/EventPlaneHelper.cxx index 9ffd39538a4..aaf839162e7 100644 --- a/Common/Core/EventPlaneHelper.cxx +++ b/Common/Core/EventPlaneHelper.cxx @@ -61,7 +61,7 @@ double EventPlaneHelper::GetPhiFV0(int chno, o2::fv0::Geometry* fv0geom) return TMath::ATan2(chPos.y + offsetY, chPos.x + offsetX); } -double EventPlaneHelper::GetPhiFT0(int chno, o2::ft0::Geometry ft0geom) +double EventPlaneHelper::GetPhiFT0(int chno, const o2::ft0::Geometry& ft0geom) { /* Calculate the azimuthal angle in FT0 for the channel number 'chno'. The offset of FT0-A is taken into account if chno is between 0 and 95. */ @@ -74,14 +74,12 @@ double EventPlaneHelper::GetPhiFT0(int chno, o2::ft0::Geometry ft0geom) offsetY = mOffsetFT0AY; } - ft0geom.calculateChannelCenter(); auto chPos = ft0geom.getChannelCenter(chno); - /// printf("Channel id: %d X: %.3f Y: %.3f\n", chno, chPos.X(), chPos.Y()); return TMath::ATan2(chPos.Y() + offsetY, chPos.X() + offsetX); } -void EventPlaneHelper::SumQvectors(int det, int chno, float ampl, int nmod, TComplex& Qvec, float& sum, o2::ft0::Geometry ft0geom, o2::fv0::Geometry* fv0geom) +void EventPlaneHelper::SumQvectors(int det, int chno, float ampl, int nmod, TComplex& Qvec, float& sum, const o2::ft0::Geometry& ft0geom, o2::fv0::Geometry* fv0geom) { /* Calculate the complex Q-vector for the provided detector and channel number, before adding it to the total Q-vector given as argument. */ @@ -166,19 +164,19 @@ void EventPlaneHelper::DoRescale(float& qx, float& qy, float ap, float am) qy /= am; } -void EventPlaneHelper::GetCorrRecentering(const std::shared_ptr histQ, float& meanX, float& meanY) +void EventPlaneHelper::GetCorrRecentering(const std::shared_ptr& histQ, float& meanX, float& meanY) { meanX = histQ->GetMean(1); meanY = histQ->GetMean(2); } -void EventPlaneHelper::GetCorrWidth(const std::shared_ptr histQ, float& stdX, float& stdY) +void EventPlaneHelper::GetCorrWidth(const std::shared_ptr& histQ, float& stdX, float& stdY) { stdX = histQ->GetStdDev(1); stdY = histQ->GetStdDev(2); } -void EventPlaneHelper::GetCorrTwistRecale(const std::shared_ptr histQ, +void EventPlaneHelper::GetCorrTwistRecale(const std::shared_ptr& histQ, float& aPlus, float& aMinus, float& lambdaPlus, float& lambdaMinus) { diff --git a/Common/Core/EventPlaneHelper.h b/Common/Core/EventPlaneHelper.h index 820af4336c2..74597969638 100644 --- a/Common/Core/EventPlaneHelper.h +++ b/Common/Core/EventPlaneHelper.h @@ -60,12 +60,12 @@ class EventPlaneHelper } // Methods to calculate the azimuthal angles for each part of FIT, given the channel number. - double GetPhiFT0(int chno, o2::ft0::Geometry ft0geom); + double GetPhiFT0(int chno, const o2::ft0::Geometry& ft0geom); double GetPhiFV0(int chno, o2::fv0::Geometry* fv0geom); // Method to get the Q-vector and sum of amplitudes for any channel in FIT, given // the detector and amplitude. - void SumQvectors(int det, int chno, float ampl, int nmod, TComplex& Qvec, float& sum, o2::ft0::Geometry ft0geom, o2::fv0::Geometry* fv0geom); + void SumQvectors(int det, int chno, float ampl, int nmod, TComplex& Qvec, float& sum, const o2::ft0::Geometry& ft0geom, o2::fv0::Geometry* fv0geom); // Method to get the bin corresponding to a centrality percentile, according to the // centClasses[] array defined in Tasks/qVectorsQA.cxx. @@ -81,13 +81,13 @@ class EventPlaneHelper void DoRescale(float& qx, float& qy, float ap, float am); // Method to get the recentering correction on the Qx-Qy distribution. - void GetCorrRecentering(const std::shared_ptr histQ, float& meanX, float& meanY); + void GetCorrRecentering(const std::shared_ptr& histQ, float& meanX, float& meanY); // Method to get the std. deviation on the Qx-Qy distribution. - void GetCorrWidth(const std::shared_ptr histQ, float& stdX, float& stdY); + void GetCorrWidth(const std::shared_ptr& histQ, float& stdX, float& stdY); // Method to get the twist and rescale correction on the Qx-Qy distribution. - void GetCorrTwistRecale(const std::shared_ptr histQ, + void GetCorrTwistRecale(const std::shared_ptr& histQ, float& aPlus, float& aMinus, float& lambdaPlus, float& lambdaMinus); diff --git a/Common/Core/FFitWeights.cxx b/Common/Core/FFitWeights.cxx index f502b4bc3c4..30d4922d13c 100644 --- a/Common/Core/FFitWeights.cxx +++ b/Common/Core/FFitWeights.cxx @@ -86,8 +86,8 @@ void FFitWeights::init() fW_data->Add(new TProfile("pMeanPt", "", centBin, 0, centBin)); fW_data->Add(new TH2D("hPtWeight", "", centBin, 0, centBin, ptBin, ptAxis->GetXmin(), ptAxis->GetXmax())); - ptProfCent = reinterpret_cast(fW_data->FindObject("pMeanPt")); - h2ptCent = reinterpret_cast(fW_data->FindObject("hPtWeight")); + ptProfCent = dynamic_cast(fW_data->FindObject("pMeanPt")); + h2ptCent = dynamic_cast(fW_data->FindObject("hPtWeight")); }; void FFitWeights::fillWeights(float centrality, float qn, int nh, const char* pf) @@ -98,10 +98,10 @@ void FFitWeights::fillWeights(float centrality, float qn, int nh, const char* pf if (!tar) return; - TH2D* th2 = reinterpret_cast(tar->FindObject(this->getQName(nh, pf))); + TH2D* th2 = dynamic_cast(tar->FindObject(this->getQName(nh, pf))); if (!th2) { tar->Add(new TH2D(this->getQName(nh, pf), this->getAxisName(nh, pf), centBin, 0, centBin, qAxis->GetNbins(), qAxis->GetXmin(), qAxis->GetXmax())); - th2 = reinterpret_cast(tar->At(tar->GetEntries() - 1)); + th2 = dynamic_cast(tar->At(tar->GetEntries() - 1)); } th2->Fill(centrality, qn); }; @@ -117,7 +117,7 @@ void FFitWeights::fillPt(float centrality, float pt, float weight, bool first) float FFitWeights::getPtMult(float centrality) { if (!ptProfCent) { - ptProfCent = reinterpret_cast(fW_data->FindObject("pMeanPt")); + ptProfCent = dynamic_cast(fW_data->FindObject("pMeanPt")); } return ptProfCent->GetBinContent(ptProfCent->FindBin(centrality)); }; @@ -132,7 +132,7 @@ Long64_t FFitWeights::Merge(TCollection* collist) } FFitWeights* lW = 0; TIter allW(collist); - while ((lW = (reinterpret_cast(allW())))) { + while ((lW = (dynamic_cast(allW())))) { addArray(fW_data, lW->getDataArray()); nmerged++; } @@ -150,7 +150,7 @@ void FFitWeights::addArray(TObjArray* targ, TObjArray* sour) auto* tObj = targ->FindObject(obj->GetName()); if (!tObj) { - auto* clone = static_cast(obj->Clone(obj->GetName())); + auto* clone = obj->Clone(obj->GetName()); if (auto* h = dynamic_cast(clone)) h->SetDirectory(0); targ->Add(clone); @@ -168,7 +168,7 @@ void FFitWeights::mptSel() if (!tar) return; - TH2D* th2 = reinterpret_cast(tar->FindObject("hPtWeight")); + TH2D* th2 = dynamic_cast(tar->FindObject("hPtWeight")); if (!th2) { return; } @@ -198,7 +198,7 @@ void FFitWeights::qSelection(const std::vector& nhv, const std::vector(tar->FindObject(this->getQName(nh, pf.c_str())))}; + TH2D* th2{dynamic_cast(tar->FindObject(this->getQName(nh, pf.c_str())))}; if (!th2) { LOGF(info, "FFitWeights qh not found!"); return; diff --git a/Common/Core/FFitWeightsLinkDef.h b/Common/Core/FFitWeightsLinkDef.h new file mode 100644 index 00000000000..3c604376da9 --- /dev/null +++ b/Common/Core/FFitWeightsLinkDef.h @@ -0,0 +1,19 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FFitWeightsLinkDef.h +/// \brief LinkDef for FFitWeights +/// +/// \author Joachim C. K. B. Hansen + +// NOLINT(build/header_guard) + +#pragma link C++ class FFitWeights + ; diff --git a/Common/Core/PID/PIDTOFParamService.cxx b/Common/Core/PID/PIDTOFParamService.cxx index 463ddd6ee48..e8138e21e28 100644 --- a/Common/Core/PID/PIDTOFParamService.cxx +++ b/Common/Core/PID/PIDTOFParamService.cxx @@ -20,6 +20,7 @@ #include "Common/Core/CollisionTypeHelper.h" #include "Common/Core/MetadataHelper.h" +#include "Common/Core/PID/PIDTOF.h" #include #include @@ -30,7 +31,6 @@ #include #include #include -#include #include @@ -46,7 +46,7 @@ o2::common::core::MetadataHelper o2::pid::tof::TOFResponseImpl::metadataInfo; bool o2::pid::tof::TOFResponseImpl::mIsInit = false; int o2::pid::tof::TOFResponseImpl::mLastRunNumber = -1; -void o2::pid::tof::TOFResponseImpl::inheritFromBaseTask(o2::framework::InitContext& initContext, const std::string task) +void o2::pid::tof::TOFResponseImpl::inheritFromBaseTask(o2::framework::InitContext& initContext, const std::string& task) { if (mIsInit) { LOG(fatal) << "TOFResponseImpl already initialized, cannot re-initialize"; @@ -69,7 +69,8 @@ void o2::pid::tof::TOFResponseImpl::inheritFromBaseTask(o2::framework::InitConte } void o2::pid::tof::TOFResponseImpl::initSetup(o2::ccdb::BasicCCDBManager* ccdb, - o2::framework::InitContext& initContext) + o2::framework::InitContext& initContext, + const std::string& task) { if (mIsInit) { LOG(fatal) << "TOFResponseImpl already initialized, cannot re-initialize"; @@ -78,8 +79,8 @@ void o2::pid::tof::TOFResponseImpl::initSetup(o2::ccdb::BasicCCDBManager* ccdb, if (!ccdb) { LOG(fatal) << "CCDB manager is not set, cannot initialize TOFResponseImpl"; } - inheritFromBaseTask(initContext); // Gets the configuration parameters from the base task (tof-signal) - mCcdb = ccdb; // Set the CCDB manager + inheritFromBaseTask(initContext, task); // Gets the configuration parameters from the base task (tof-signal) + mCcdb = ccdb; // Set the CCDB manager mCcdb->setURL(mUrl); mCcdb->setTimestamp(mTimestamp); mCcdb->setCaching(true); @@ -179,9 +180,10 @@ void o2::pid::tof::TOFResponseImpl::initSetup(o2::ccdb::BasicCCDBManager* ccdb, LOG(info) << "Initializing the time shift for " << (isPositive ? "positive" : "negative") << " from ccdb '" << nameShift << "' and timestamp " << mTimestamp << " and pass '" << mReconstructionPass << "'"; + bool fatalWhenNull = mCcdb->getFatalWhenNull(); mCcdb->setFatalWhenNull(false); parameters.setTimeShiftParameters(mCcdb->getSpecific(nameShift, mTimestamp, metadata), isPositive); - mCcdb->setFatalWhenNull(true); + mCcdb->setFatalWhenNull(fatalWhenNull); } LOG(info) << " test getTimeShift at 0 " << (isPositive ? "pos" : "neg") << ": " << parameters.getTimeShift(0, isPositive); @@ -268,9 +270,10 @@ void o2::pid::tof::TOFResponseImpl::processSetup(const int runNumber, const int6 LOG(info) << "Updating the time shift for " << (isPositive ? "positive" : "negative") << " from ccdb '" << nameShift << "' and timestamp " << mTimestamp << " and pass '" << mReconstructionPass << "'"; + bool fatalWhenNull = mCcdb->getFatalWhenNull(); mCcdb->setFatalWhenNull(false); parameters.setTimeShiftParameters(mCcdb->getSpecific(nameShift, mTimestamp, metadata), isPositive); - mCcdb->setFatalWhenNull(true); + mCcdb->setFatalWhenNull(fatalWhenNull); LOG(info) << " test getTimeShift at 0 " << (isPositive ? "pos" : "neg") << ": " << parameters.getTimeShift(0, isPositive); }; @@ -295,7 +298,7 @@ struct TOFSupport : o2::framework::ServicePlugin { }, .configure = CommonServices::noConfiguration(), .exit = [](ServiceRegistryRef, void* service) { - auto* resp = reinterpret_cast(service); + auto* resp = static_cast(service); delete resp; }, .kind = ServiceKind::Serial}; } diff --git a/Common/Core/PID/PIDTOFParamService.h b/Common/Core/PID/PIDTOFParamService.h index 69d8fbfed0d..dd33d250fa2 100644 --- a/Common/Core/PID/PIDTOFParamService.h +++ b/Common/Core/PID/PIDTOFParamService.h @@ -49,14 +49,14 @@ struct TOFResponseImpl { /// \note This function should be called in the init function of each task that uses the TOF response /// \note The parameters are loaded from the CCDB and stored in the static variable `parameters` /// \note The metadata information is also initialized in this function - void initSetup(o2::ccdb::BasicCCDBManager* ccdb, o2::framework::InitContext& initContext); + void initSetup(o2::ccdb::BasicCCDBManager* ccdb, o2::framework::InitContext& initContext, const std::string& task = "tof-signal"); /// Initialize the TOF response parameters in the init function of each task /// \param ccdb Service pointer to the CCDB manager template - void initSetup(T ccdb, o2::framework::InitContext& initContext) + void initSetup(T ccdb, o2::framework::InitContext& initContext, const std::string task = "tof-signal") { - initSetup(ccdb.operator->(), initContext); + initSetup(ccdb.operator->(), initContext, task); } /// Initialize the TOF response parameters in the process function of each task, should be called only at least once per run @@ -163,7 +163,7 @@ struct TOFResponseImpl { o2::common::core::CollisionSystemType::collType cfgCollisionType() const { return mCollisionSystem; } private: - void inheritFromBaseTask(o2::framework::InitContext& initContext, const std::string task = "tof-signal"); + void inheritFromBaseTask(o2::framework::InitContext& initContext, const std::string& task = "tof-signal"); static bool mIsInit; //! Flag to check if the parameters are initialized static int mLastRunNumber; //! Last run number for which the calibration was loaded diff --git a/Common/Core/PID/ParamBase.cxx b/Common/Core/PID/ParamBase.cxx index e1375b3916c..afd87ebff53 100644 --- a/Common/Core/PID/ParamBase.cxx +++ b/Common/Core/PID/ParamBase.cxx @@ -17,8 +17,9 @@ /// These are the basic storage elements to be kept in the CCDB /// +#include "ParamBase.h" + #include -#include #include #include diff --git a/Common/Core/PID/TPCPIDResponse.h b/Common/Core/PID/TPCPIDResponse.h index 7831039f4d8..a8b082cad54 100644 --- a/Common/Core/PID/TPCPIDResponse.h +++ b/Common/Core/PID/TPCPIDResponse.h @@ -25,6 +25,7 @@ #include #include +#include #include namespace o2::pid::tpc @@ -77,7 +78,7 @@ class Response float GetExpectedSigma(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id) const; /// Gets the expected resolution of the track with multTPC explicitly provided template - float GetExpectedSigmaAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id) const; + float GetExpectedSigmaAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id) const; /// Gets the number of sigmas with respect the expected value template float GetNumberOfSigma(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id) const; @@ -86,16 +87,21 @@ class Response float GetNumberOfSigmaMCTuned(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; // Number of sigmas with respect to expected for MC, defining a tune-on-data signal value, explicit multTPC template - float GetNumberOfSigmaMCTunedAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; + float GetNumberOfSigmaMCTunedAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const; /// Gets the deviation to the expected signal template float GetSignalDelta(const TrackType& trk, const o2::track::PID::ID id) const; + /// Gets relative dEdx resolution contribution due to relative pt resolution float GetRelativeResolutiondEdx(const float p, const float mass, const float charge, const float resol) const; void PrintAll() const; private: + /// Compute expected sigma given a pre-computed expected signal, avoiding a redundant Bethe-Bloch call. + template + float sigmaFromSignal(float expectedSignal, const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const; + std::array mBetheBlochParams = {0.03209809958934784, 19.9768009185791, 2.5266601063857674e-16, 2.7212300300598145, 6.080920219421387}; std::array mResolutionParamsDefault = {0.07, 0.0}; std::vector mResolutionParams = {5.43799e-7, 0.053044, 0.667584, 0.0142667, 0.00235175, 1.22482, 2.3501e-7, 0.031585}; @@ -130,18 +136,23 @@ inline float Response::GetExpectedSigma(const CollisionType& collision, const Tr /// Gets the expected resolution of the measurement template -inline float Response::GetExpectedSigmaAtMultiplicity(const long multTPC, const TrackType& track, const o2::track::PID::ID id) const +inline float Response::GetExpectedSigmaAtMultiplicity(const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const { if (!track.hasTPC()) { return -999.f; } - float resolution = 0.; + return sigmaFromSignal(GetExpectedSignal(track, id), multTPC, track, id); +} + +template +inline float Response::sigmaFromSignal(float expectedSignal, const int64_t multTPC, const TrackType& track, const o2::track::PID::ID id) const +{ + float resolution = 0.f; if (mUseDefaultResolutionParam) { - const float reso = GetExpectedSignal(track, id) * mResolutionParamsDefault[0] * (static_cast(track.tpcNClsFound()) > 0 ? std::sqrt(1. + mResolutionParamsDefault[1] / static_cast(track.tpcNClsFound())) : 1.f); + const float reso = expectedSignal * mResolutionParamsDefault[0] * (static_cast(track.tpcNClsFound()) > 0 ? std::sqrt(1. + mResolutionParamsDefault[1] / static_cast(track.tpcNClsFound())) : 1.f); reso >= 0.f ? resolution = reso : resolution = -999.f; } else { - - const double ncl = nClNorm / track.tpcNClsFound(); // + const double ncl = nClNorm / track.tpcNClsFound(); const double p = track.tpcInnerParam(); const double mass = o2::track::pid_constants::sMasses[id]; const double bg = p / mass; @@ -160,16 +171,15 @@ inline float Response::GetExpectedSigmaAtMultiplicity(const long multTPC, const template inline float Response::GetNumberOfSigma(const CollisionType& collision, const TrackType& trk, const o2::track::PID::ID id) const { - if (GetExpectedSigma(collision, trk, id) < 0.) { - return -999.f; - } - if (GetExpectedSignal(trk, id) < 0.) { + const float signal = GetExpectedSignal(trk, id); + if (signal < 0.f) { return -999.f; } - if (!trk.hasTPC()) { + const float sigma = sigmaFromSignal(signal, collision.multTPC(), trk, id); + if (sigma < 0.f) { return -999.f; } - return ((trk.tpcSignal() - GetExpectedSignal(trk, id)) / GetExpectedSigma(collision, trk, id)); + return (trk.tpcSignal() - signal) / sigma; } template @@ -179,31 +189,28 @@ inline float Response::GetNumberOfSigmaMCTuned(const CollisionType& collision, c } template -inline float Response::GetNumberOfSigmaMCTunedAtMultiplicity(const long multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const +inline float Response::GetNumberOfSigmaMCTunedAtMultiplicity(const int64_t multTPC, const TrackType& trk, const o2::track::PID::ID id, float mcTunedTPCSignal) const { - if (GetExpectedSigmaAtMultiplicity(multTPC, trk, id) < 0.) { + const float signal = GetExpectedSignal(trk, id); + if (signal < 0.f) { return -999.f; } - if (GetExpectedSignal(trk, id) < 0.) { + const float sigma = sigmaFromSignal(signal, multTPC, trk, id); + if (sigma < 0.f) { return -999.f; } - if (!trk.hasTPC()) { - return -999.f; - } - return ((mcTunedTPCSignal - GetExpectedSignal(trk, id)) / GetExpectedSigmaAtMultiplicity(multTPC, trk, id)); + return (mcTunedTPCSignal - signal) / sigma; } /// Gets the deviation between the actual signal and the expected signal template inline float Response::GetSignalDelta(const TrackType& trk, const o2::track::PID::ID id) const { - if (GetExpectedSignal(trk, id) < 0.) { - return -999.f; - } - if (!trk.hasTPC()) { + const float signal = GetExpectedSignal(trk, id); + if (signal < 0.f) { return -999.f; } - return (trk.tpcSignal() - GetExpectedSignal(trk, id)); + return trk.tpcSignal() - signal; } //// Gets relative dEdx resolution contribution due relative pt resolution diff --git a/Common/Core/RecoDecay.h b/Common/Core/RecoDecay.h index fcc50d23bf8..b8e8bedefdd 100644 --- a/Common/Core/RecoDecay.h +++ b/Common/Core/RecoDecay.h @@ -27,6 +27,7 @@ #include // std::abs, std::sqrt #include // std::size_t #include // intX_t +#include // std::iota #include // std::apply #include // std::decay_t #include // std::move @@ -49,6 +50,10 @@ struct RecoDecay { }; static constexpr int8_t StatusCodeAfterFlavourOscillation{92}; // decay products after B0(s) flavour oscillation + static constexpr int8_t StatusCodePrimaryMin{81}; // primary particles (after hadronization) have status code >= 81 + static constexpr int8_t StatusCodePrimaryMax{89}; // primary particles (after hadronization) have status code <= 89 + static constexpr int8_t StatusCodeRhadronMin{101}; // R-hadrons (after hadronization) have status code >= 101 + static constexpr int8_t StatusCodeRhadronMax{109}; // R-hadrons (after hadronization) have status code <= 109 static constexpr int PdgQuarkMax{8}; // largest quark PDG code; o2-linter: disable=pdg/explicit-code (t' does not have a named constant.) static constexpr int PdgBosonMin{PDG_t::kGluon}; // smallest boson (gauge or H) PDG code static constexpr int PdgBosonMax{37}; // largest boson (gauge or H) PDG code; o2-linter: disable=pdg/explicit-code (H+ does not have a named constant.) @@ -279,6 +284,27 @@ struct RecoDecay { return cos; } + /// Calculates cosine of pointing angle in the {r, z} plane. + /// \param posPV {x, y, z} position of the primary vertex + /// \param posSV {x, y, z} position of the secondary vertex + /// \param mom {x, y, z} momentum array + /// \return cosine of pointing angle in {r, z} + template + static double cpaRZ(const T& posPV, const U& posSV, const std::array& mom) + { + // CPARZ = (r . pz)/(|r| |pz|) + auto lineDecay = std::array{sqrtSumOfSquares(posSV[0] - posPV[0], posSV[1] - posPV[1]), static_cast(posSV[2] - posPV[2])}; + auto momRZ = std::array{sqrtSumOfSquares(mom[0], mom[1]), static_cast(mom[2])}; + auto cos = dotProd(lineDecay, momRZ) / std::sqrt(mag2(lineDecay) * mag2(momRZ)); + if (cos < -1.) { + return -1.; + } + if (cos > 1.) { + return 1.; + } + return cos; + } + /// Calculates proper lifetime times c. /// \note Promotes numbers to double before squaring to avoid precision loss in float multiplication. /// \param mom 3-momentum array @@ -540,6 +566,46 @@ struct RecoDecay { return maxNormDeltaIP; } + template + static void getOrderedMotherIds(const PPart& particle, + std::vector& motherIdsOrdered, + const bool searchUpToQuark = false) + { + if (!particle.has_mothers()) { + return; + } + + if (particle.mothersIds().front() == particle.globalIndex()) { + return; + } + + if (!searchUpToQuark) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } else { + if (particle.mothersIds().size() == 2) { + if (particle.mothersIds().front() == particle.mothersIds().back()) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } else if (particle.mothersIds().front() < particle.mothersIds().back()) { + auto absStatusCode = std::abs(particle.getGenStatusCode()); + if ((absStatusCode >= StatusCodePrimaryMin && absStatusCode <= StatusCodePrimaryMax) || (absStatusCode >= StatusCodeRhadronMin && absStatusCode <= StatusCodeRhadronMax)) { + motherIdsOrdered.resize(particle.mothersIds().back() - particle.mothersIds().front() + 1); + std::iota(motherIdsOrdered.begin(), motherIdsOrdered.end(), particle.mothersIds().front()); + } else { + motherIdsOrdered.push_back(particle.mothersIds().front()); + motherIdsOrdered.push_back(particle.mothersIds().back()); + } + } else if (particle.mothersIds().front() > particle.mothersIds().back()) { + if (particle.mothersIds().back() != 0) { + motherIdsOrdered.push_back(particle.mothersIds().back()); + } + motherIdsOrdered.push_back(particle.mothersIds().front()); + } + } else if (particle.mothersIds().size() == 1) { + motherIdsOrdered.push_back(particle.mothersIds().front()); + } + } + } + /// Finds the mother of an MC particle by looking for the expected PDG code in the mother chain. /// \tparam acceptFlavourOscillation switch to accept decays where the mother oscillated (e.g. B0 -> B0bar) /// \param particlesMC table with MC particles @@ -548,6 +614,7 @@ struct RecoDecay { /// \param acceptAntiParticles switch to accept the antiparticle of the expected mother /// \param sign antiparticle indicator of the found mother w.r.t. pdgMother; 1 if particle, -1 if antiparticle, 0 if mother not found /// \param depthMax maximum decay tree level to check; Mothers up to this level will be considered. If -1, all levels are considered. + /// \param searchUpToQuark switch to stop searching for mothers when a quark or boson is found /// \return index of the mother particle if found, -1 otherwise template static int getMother(const T& particlesMC, @@ -555,7 +622,8 @@ struct RecoDecay { int pdgMother, bool acceptAntiParticles = false, int8_t* sign = nullptr, - int8_t depthMax = -1) + int8_t depthMax = -1, + const bool searchUpToQuark = true) { int8_t sgn = 0; // 1 if the expected mother is particle, -1 if antiparticle (w.r.t. pdgMother) int indexMother = -1; // index of the final matched mother, if found @@ -576,13 +644,20 @@ struct RecoDecay { for (auto iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage, o2-linter: disable=const-ref-in-for-loop (int elements) auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { - for (auto iMother = particleMother.mothersIds().front(); iMother <= particleMother.mothersIds().back(); ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + + std::vector motherIdsOrdered{}; + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); + + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); // Check mother's PDG code. auto pdgParticleIMother = mother.pdgCode(); // PDG code of the mother + if (!searchUpToQuark && (std::abs(pdgParticleIMother) <= PdgQuarkMax || (std::abs(pdgParticleIMother) >= PdgBosonMin && std::abs(pdgParticleIMother) <= PdgBosonMax))) { + continue; + } // printf("getMother: "); // for (int i = stage; i < 0; i++) // Indent to make the tree look nice. // printf(" "); @@ -708,6 +783,7 @@ struct RecoDecay { /// \param nPiToMu number of pion prongs decayed to a muon /// \param nKaToPi number of kaon prongs decayed to a pion /// \param nInteractionsWithMaterial number of daughter particles that interacted with material + /// \param searchUpToQuark switch to search for the decay up to the quark level /// \return index of the mother particle if the mother and daughters are correct, -1 otherwise template static int getMatchedMCRec(const T& particlesMC, @@ -719,7 +795,8 @@ struct RecoDecay { int depthMax = 1, int8_t* nPiToMu = nullptr, int8_t* nKaToPi = nullptr, - int8_t* nInteractionsWithMaterial = nullptr) + int8_t* nInteractionsWithMaterial = nullptr, + bool searchUpToQuark = true) { // Printf("MC Rec: Expected mother PDG: %d", pdgMother); int8_t coefFlavourOscillation = 1; // 1 if no B0(s) flavour oscillation occured, -1 else @@ -794,7 +871,7 @@ struct RecoDecay { if (iProng == 0) { // Get the mother index and its sign. // PDG code of the first daughter's mother determines whether the expected mother is a particle or antiparticle. - indexMother = getMother(particlesMC, particleI, pdgMother, acceptAntiParticles, &sgn, depthMax); + indexMother = getMother(particlesMC, particleI, pdgMother, acceptAntiParticles, &sgn, depthMax, searchUpToQuark); // Check whether mother was found. if (indexMother <= -1) { // Printf("MC Rec: Rejected: bad mother index or PDG"); @@ -1029,6 +1106,7 @@ struct RecoDecay { while (arrayIds[-stage].size() > 0) { // vector of mother indices for the current stage std::vector arrayIdsStage{}; + std::vector motherIdsOrdered{}; for (auto iPart : arrayIds[-stage]) { // check all the particles that were the mothers at the previous stage, o2-linter: disable=const-ref-in-for-loop (int elements) auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { @@ -1042,8 +1120,9 @@ struct RecoDecay { } } - for (auto iMother = particleMother.mothersIds().front(); iMother <= particleMother.mothersIds().back(); ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); @@ -1131,30 +1210,11 @@ struct RecoDecay { auto particleMother = particlesMC.rawIteratorAt(iPart - particlesMC.offset()); if (particleMother.has_mothers()) { - // we break immediately if searchUpToQuark is false and the first mother is a quark or a boson (a hadron should never be the mother of a parton) - if (!searchUpToQuark) { - auto mother = particlesMC.rawIteratorAt(particleMother.mothersIds().front() - particlesMC.offset()); - auto pdgParticleIMother = std::abs(mother.pdgCode()); // PDG code of the mother - if (pdgParticleIMother <= PdgQuarkMax || (pdgParticleIMother >= PdgBosonMin && pdgParticleIMother <= PdgBosonMax)) { - // auto PDGPaticle = std::abs(particleMother.pdgCode()); - if ( - (pdgParticle / PdgDivisorMeson == PDG_t::kBottom || // b mesons - pdgParticle / PdgDivisorBaryon == PDG_t::kBottom) // b baryons - ) { - return OriginType::NonPrompt; // beauty - } - if ( - (pdgParticle / PdgDivisorMeson == PDG_t::kCharm || // c mesons - pdgParticle / PdgDivisorBaryon == PDG_t::kCharm) // c baryons - ) { - return OriginType::Prompt; // charm - } - break; - } - } + std::vector motherIdsOrdered{}; + getOrderedMotherIds(particleMother, motherIdsOrdered, searchUpToQuark); - for (auto iMother = particleMother.mothersIds().front(); iMother <= particleMother.mothersIds().back(); ++iMother) { // loop over the mother particles of the analysed particle - if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again + for (auto const& iMother : motherIdsOrdered) { // loop over the mother particles of the analysed particle + if (std::find(arrayIdsStage.begin(), arrayIdsStage.end(), iMother) != arrayIdsStage.end()) { // if a mother is still present in the vector, do not check it again continue; } auto mother = particlesMC.rawIteratorAt(iMother - particlesMC.offset()); diff --git a/Common/Core/TPCVDriftManager.h b/Common/Core/TPCVDriftManager.h index 3219b6189a0..9d8a9ae88ca 100644 --- a/Common/Core/TPCVDriftManager.h +++ b/Common/Core/TPCVDriftManager.h @@ -37,14 +37,24 @@ class TPCVDriftManager mCCDB = ccdb; } + // Use the TPC side flags (with legacy-data fallback) instead of the tgl-sign-based correction. + // Off by default so that existing analyses see no change in results until this is explicitly + // enabled for testing. Tasks using TPCVDriftManager can expose this via their own Configurable. + void setUseSideBasedCorrection(bool value) noexcept + { + mUseSideBasedCorrection = value; + } + void update(uint64_t timestamp) noexcept { - // Check validity of already present obj, otherwise update - if (mVD != nullptr && (timestamp > static_cast(mVD->firstTime) || timestamp < static_cast(mVD->lastTime))) { + // Keep the object we already have if it is still valid for this timestamp. + // firstTime/lastTime are the first and last timestamps of the TF the correction + // was derived from, so the validity range is closed on both ends. + if (mVD != nullptr && timestamp >= static_cast(mVD->firstTime) && timestamp <= static_cast(mVD->lastTime)) { return; } - // Update Obj + // Update Object mVD = mCCDB->getForTimeStamp("TPC/Calib/VDriftTgl", timestamp); if (mVD == nullptr || mVD->firstTime < 0 || mVD->lastTime < 0) { LOGP(error, "Got invalid VDriftCorrFact for {}", timestamp); @@ -58,7 +68,28 @@ class TPCVDriftManager mTPCVDriftNS = mVD->refVDrift * mVD->corrFact * 1e-3; mValid = true; - LOGP(info, "Updated VDrift for timestamp {} with vdrift={:.7f} (cm/ns)", mVD->creationTime, mTPCVDriftNS); + LOGP(debug, "Updated VDrift for timestamp {} with vdrift={:.7f} (cm/ns)", mVD->creationTime, mTPCVDriftNS); + } + + // Adopts a drift correction obtained elsewhere, typically straight from the + // aod::TpcCalibCCDBObjects column, so no CCDB manager is involved at all. + void update(const o2::tpc::VDriftCorrFact& vd) noexcept + { + if (mVD == &vd) { // same object as last time, nothing to recompute + return; + } + if (vd.firstTime < 0 || vd.lastTime < 0) { + LOGP(error, "Got invalid VDriftCorrFact created at {}", vd.creationTime); + mValid = false; + return; + } + mVD = &vd; + + // TODO account for laser calib + + mTPCVDriftNS = mVD->refVDrift * mVD->corrFact * 1e-3; + mValid = true; + LOGP(debug, "Updated VDrift for timestamp {} with vdrift={:.7f} (cm/ns)", mVD->creationTime, mTPCVDriftNS); } template @@ -113,7 +144,7 @@ class TPCVDriftManager float dTime = tTB - trackExtra.trackTime(); float dDrift = dTime * mTPCVDriftNS; float dDriftErr = tTBErr * mTPCVDriftNS; - if (dDriftErr < 0.f || dDrift > 250.f) { // we cannot move a track outside the drift volume + if (dDriftErr < 0.f || dDrift > mMaxDriftCm) { // we cannot move a track outside the drift volume if (mOutside < mWarningLimit) { LOGP(warn, "Skipping correction outside of tpc volume with dDrift={} +- {}", dDrift, dDriftErr); const auto trackBC = trackExtra.template collision_as().template foundBC_as().globalBC(); @@ -129,7 +160,54 @@ class TPCVDriftManager } // impose new Z coordinate - track.setZ(track.getZ() + ((track.getTgl() < 0.) ? -dDrift : dDrift)); + float zShift = 0.f; + if (!mUseSideBasedCorrection) { + // Legacy behaviour (default): infer the side from tgl alone. + zShift = (track.getTgl() < 0.f) ? -dDrift : dDrift; + } else { + const auto sides = (trackExtra.flags() & (o2::aod::track::TrackFlags::TPCSideA | o2::aod::track::TrackFlags::TPCSideC)); + if (sides == o2::aod::track::TrackFlags::TPCSideA) { + zShift = dDrift; + } else if (sides == o2::aod::track::TrackFlags::TPCSideC) { + zShift = -dDrift; + } else if (sides == 0) { + // Fallback for datasets produced before the TPC side flags were introduced (Feb. 2026). + o2::aod::track::extensions::TPCTimeErrEncoding tEnc; + tEnc.encoding.timeErr = trackExtra.trackTimeRes(); + const float dFwd = tEnc.getDeltaTFwd(); + const float dBwd = tEnc.getDeltaTBwd(); + // Equal, small forward/backward margins mean the track is bounded on both ends, + // i.e. it crosses the CE: it cannot be moved and is already corrected elsewhere. + const bool crossesCE = (dFwd == dBwd) && (dFwd < mMaxCECrossingDeltaTNS); + if (!crossesCE) { + const bool zPositive = track.getZ() > 0.f; + const bool tglPositive = track.getTgl() > 0.f; + int side = 0; // +1 = A, -1 = C, 0 = undetermined -> leave uncorrected + if (zPositive == tglPositive) { + // Consistent sign: the track converges to Z=0 at the beamline by construction. + side = tglPositive ? 1 : -1; + } else if (dBwd == 0.f && dFwd > 0.f) { + // Bounded backward at the CE with room forward: no clusters on the opposite + // side, so trust the measured Z rather than the (here inverted) tgl. + side = zPositive ? 1 : -1; + } else if (dBwd > 0.f) { + // Large tgl track bounded at the readout side instead: trust tgl. + side = tglPositive ? 1 : -1; + } + // else: degenerate case, track touches both CE and readout -> cannot be deduced/moved. + // (in practice unreachable here: dFwd==dBwd==0 would already satisfy crossesCE above, + // since dFwd/dBwd are always >= 0; kept explicit to mirror the reference logic 1:1.) + + if (side > 0) { + zShift = dDrift; + } else if (side < 0) { + zShift = -dDrift; + } + } + } + // else: track has clusters on both sides (crossed the CE) and is already corrected elsewhere + } + track.setZ(track.getZ() + zShift); if constexpr (std::is_base_of_v) { track.setCov(track.getSigmaZ2() + dDriftErr * dDriftErr, o2::track::kSigZ2); } @@ -146,6 +224,7 @@ class TPCVDriftManager private: bool mValid{false}; + bool mUseSideBasedCorrection{false}; // off by default: preserves legacy tgl-sign behaviour // Factors float mTPCVDriftNS{0.f}; // drift velocity in cm/ns @@ -154,6 +233,8 @@ class TPCVDriftManager o2::ccdb::BasicCCDBManager* mCCDB{}; // reference to initialized ccdb manager static constexpr unsigned int mWarningLimit{10}; + static constexpr float mMaxDriftCm{250.f}; // TPC drift volume half-length in cm + static constexpr float mMaxCECrossingDeltaTNS{1000.f}; // ~1 us, ballpark forward/backward time margin of a CE-crossing track // Counters unsigned int mCalls{0}; // total number of calls diff --git a/Common/Core/TrackSelection.cxx b/Common/Core/TrackSelection.cxx index 6fe7a2e162b..5ee1ce57b55 100644 --- a/Common/Core/TrackSelection.cxx +++ b/Common/Core/TrackSelection.cxx @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -27,19 +27,18 @@ bool TrackSelection::FulfillsITSHitRequirements(uint8_t itsClusterMap) const { - constexpr uint8_t bit = 1; - for (auto& itsRequirement : mRequiredITSHits) { - auto hits = std::count_if(itsRequirement.second.begin(), itsRequirement.second.end(), [&](auto&& requiredLayer) { return itsClusterMap & (bit << requiredLayer); }); - if ((itsRequirement.first == -1) && (hits > 0)) { + for (const auto& [minHits, layerMask] : mRequiredITSHits) { + int hits = __builtin_popcount(itsClusterMap & layerMask); + if ((minHits == -1) && (hits > 0)) { return false; // no hits were required in specified layers - } else if (hits < itsRequirement.first) { + } else if (hits < minHits) { return false; // not enough hits found in specified layers } } return true; } -const std::string TrackSelection::mCutNames[static_cast(TrackSelection::TrackCuts::kNCuts)] = {"TrackType", "PtRange", "EtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCRefit", "ITSNCls", "ITSChi2NDF", "ITSRefit", "ITSHits", "GoldenChi2", "DCAxy", "DCAz", "TPCFracSharedCls"}; +const std::array(TrackSelection::TrackCuts::kNCuts)> TrackSelection::mCutNames{"TrackType", "PtRange", "EtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCRefit", "ITSNCls", "ITSChi2NDF", "ITSRefit", "ITSHits", "GoldenChi2", "DCAxy", "DCAz", "TPCFracSharedCls"}; void TrackSelection::SetTrackType(o2::aod::track::TrackTypeEnum trackType) { @@ -121,19 +120,27 @@ void TrackSelection::SetMaxDcaZ(float maxDcaZ) void TrackSelection::SetMaxDcaXYPtDep(std::function ptDepCut) { - mMaxDcaXYPtDep = ptDepCut; + mMaxDcaXYPtDep = std::move(ptDepCut); LOG(info) << "Track selection, set max DCA xy pt dep: " << mMaxDcaXYPtDep(1.0); } -void TrackSelection::SetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) +void TrackSelection::SetRequireHitsInITSLayers(int8_t minNRequiredHits, const std::set& requiredLayers) { // layer 0 corresponds to the the innermost ITS layer - mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); + uint8_t mask = 0; + for (const auto& layer : requiredLayers) { + mask |= (1u << layer); + } + mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, mask)); LOG(info) << "Track selection, set require hits in ITS layers: " << static_cast(minNRequiredHits); } -void TrackSelection::SetRequireNoHitsInITSLayers(std::set excludedLayers) +void TrackSelection::SetRequireNoHitsInITSLayers(const std::set& excludedLayers) { - mRequiredITSHits.push_back(std::make_pair(-1, excludedLayers)); + uint8_t mask = 0; + for (const auto& layer : excludedLayers) { + mask |= (1u << layer); + } + mRequiredITSHits.push_back(std::make_pair(-1, mask)); LOG(info) << "Track selection, set require no hits in ITS layers"; } @@ -143,7 +150,7 @@ void TrackSelection::print() const for (int i = 0; i < static_cast(TrackCuts::kNCuts); i++) { switch (static_cast(i)) { case TrackCuts::kTrackType: - LOG(info) << mCutNames[i].data() << " == " << static_cast(mTrackType); + LOG(info) << mCutNames[i] << " == " << static_cast(mTrackType); break; case TrackCuts::kPtRange: LOG(info) << mCutNames[i] << " in [" << mMinPt << ", " << mMaxPt << "]"; diff --git a/Common/Core/TrackSelection.h b/Common/Core/TrackSelection.h index 5f8590cb85f..ff0c92b9baf 100644 --- a/Common/Core/TrackSelection.h +++ b/Common/Core/TrackSelection.h @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -68,58 +69,68 @@ class TrackSelection ppPass3 }; - static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; + static const std::array(TrackCuts::kNCuts)> mCutNames; + + // True for Run 2 track types. Hoisted out of the per-cut IsSelected() so that + // the trackType() column is dereferenced once per track instead of once per cut. + template + static bool IsRun2Track(T const& track) + { + const auto trackType = track.trackType(); + return trackType == o2::aod::track::Run2Track || trackType == o2::aod::track::Run2Tracklet; + } // Temporary function to check if track passes selection criteria. To be replaced by framework filters. template bool IsSelected(T const& track) const { - if (!IsSelected(track, TrackCuts::kTrackType)) { + const bool isRun2 = IsRun2Track(track); + if (!IsSelected(track, TrackCuts::kTrackType, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kPtRange)) { + if (!IsSelected(track, TrackCuts::kPtRange, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kEtaRange)) { + if (!IsSelected(track, TrackCuts::kEtaRange, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kTPCNCls)) { + if (!IsSelected(track, TrackCuts::kTPCNCls, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kTPCCrossedRows)) { + if (!IsSelected(track, TrackCuts::kTPCCrossedRows, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kTPCCrossedRowsOverNCls)) { + if (!IsSelected(track, TrackCuts::kTPCCrossedRowsOverNCls, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kTPCChi2NDF)) { + if (!IsSelected(track, TrackCuts::kTPCChi2NDF, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kTPCRefit)) { + if (!IsSelected(track, TrackCuts::kTPCRefit, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kITSNCls)) { + if (!IsSelected(track, TrackCuts::kITSNCls, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kITSChi2NDF)) { + if (!IsSelected(track, TrackCuts::kITSChi2NDF, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kITSRefit)) { + if (!IsSelected(track, TrackCuts::kITSRefit, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kITSHits)) { + if (!IsSelected(track, TrackCuts::kITSHits, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kGoldenChi2)) { + if (!IsSelected(track, TrackCuts::kGoldenChi2, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kDCAxy)) { + if (!IsSelected(track, TrackCuts::kDCAxy, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kDCAz)) { + if (!IsSelected(track, TrackCuts::kDCAz, isRun2)) { return false; } - if (!IsSelected(track, TrackCuts::kTPCFracSharedCls)) { + if (!IsSelected(track, TrackCuts::kTPCFracSharedCls, isRun2)) { return false; } return true; @@ -131,8 +142,9 @@ class TrackSelection { uint16_t flag = 0; + const bool isRun2 = IsRun2Track(track); auto setFlag = [&](const TrackCuts& cut) { - if (IsSelected(track, cut)) { + if (IsSelected(track, cut, isRun2)) { flag |= 1UL << static_cast(cut); } }; @@ -161,8 +173,14 @@ class TrackSelection template bool IsSelected(T const& track, const TrackCuts& cut) const { - const bool isRun2 = track.trackType() == o2::aod::track::Run2Track || track.trackType() == o2::aod::track::Run2Tracklet; + return IsSelected(track, cut, IsRun2Track(track)); + } + // Overload taking the pre-computed Run 2 flag, so that callers evaluating several + // cuts on the same track do not re-read trackType() for each of them. + template + bool IsSelected(T const& track, const TrackCuts& cut, bool isRun2) const + { switch (cut) { case TrackCuts::kTrackType: return track.trackType() == mTrackType; @@ -232,8 +250,8 @@ class TrackSelection void SetMaxDcaXY(float maxDcaXY); void SetMaxDcaZ(float maxDcaZ); void SetMaxDcaXYPtDep(std::function ptDepCut); - void SetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers); - void SetRequireNoHitsInITSLayers(std::set excludedLayers); + void SetRequireHitsInITSLayers(int8_t minNRequiredHits, const std::set& requiredLayers); + void SetRequireNoHitsInITSLayers(const std::set& excludedLayers); /// @brief Reset ITS requirements void ResetITSRequirements() { mRequiredITSHits.clear(); } void SetMaxTPCFractionSharedCls(float maxTPCFractionSharedCls); @@ -268,10 +286,10 @@ class TrackSelection bool mRequireTPCRefit{false}; // require refit in TPC bool mRequireGoldenChi2{false}; // require golden chi2 cut (Run 2 only) - // vector of ITS requirements (minNRequiredHits in specific requiredLayers) - std::vector>> mRequiredITSHits{}; + // vector of ITS requirements (minNRequiredHits, bitmask of requiredLayers) + std::vector> mRequiredITSHits{}; - ClassDefNV(TrackSelection, 1); + ClassDefNV(TrackSelection, 2); }; #endif // COMMON_CORE_TRACKSELECTION_H_ diff --git a/Common/Core/TrackSelectionDefaults.cxx b/Common/Core/TrackSelectionDefaults.cxx index e8476db5e17..4bd69ac33cf 100644 --- a/Common/Core/TrackSelectionDefaults.cxx +++ b/Common/Core/TrackSelectionDefaults.cxx @@ -39,7 +39,7 @@ TrackSelection getGlobalTrackSelection() selectedTracks.SetMaxChi2PerClusterTPC(4.f); selectedTracks.SetRequireHitsInITSLayers(1, {0, 1}); // one hit in any SPD layer selectedTracks.SetMaxChi2PerClusterITS(36.f); - selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / pow(pt, 1.1f); }); + selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / std::pow(pt, 1.1f); }); selectedTracks.SetMaxDcaZ(2.f); return selectedTracks; } @@ -115,7 +115,7 @@ TrackSelection getGlobalTrackSelectionRun3HF() selectedTracks.SetMaxChi2PerClusterTPC(4.f); selectedTracks.SetRequireHitsInITSLayers(1, {0, 1}); // one hit in any SPD layer selectedTracks.SetMaxChi2PerClusterITS(36.f); - // selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / pow(pt, 1.1f); }); + // selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / std::pow(pt, 1.1f); }); selectedTracks.SetMaxDcaZ(2.f); selectedTracks.SetMaxDcaXY(0.25); diff --git a/Common/Core/Zorro.cxx b/Common/Core/Zorro.cxx index 446e36e7f2b..f110b8a9a85 100644 --- a/Common/Core/Zorro.cxx +++ b/Common/Core/Zorro.cxx @@ -52,7 +52,7 @@ int findBin(TH1* hist, const std::string& label) } } // namespace -void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, int runNumber, std::string folderName) +void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, int runNumber, const std::string& folderName) { int runId{-1}; for (size_t i{0}; i < mRunNumberHistos.size(); ++i) { @@ -72,7 +72,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, for (int iBin{2}; iBin < mSelections->GetNbinsX(); ++iBin) { // Exclude first and last bins as they are total number of analysed and selected events, respectively mAnalysedTriggers->GetXaxis()->SetBinLabel(iBin - 1, mSelections->GetXaxis()->GetBinLabel(iBin)); } - std::shared_ptr selections = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Selections").data(), "", o2::framework::HistType::kTH1D, {{mSelections->GetNbinsX(), -0.5, static_cast(mSelections->GetNbinsX() - 0.5)}}); + std::shared_ptr selections = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Selections").data(), "", o2::framework::HistType::kTH1D, {{mSelections->GetNbinsX(), -0.5, (mSelections->GetNbinsX() - 0.5)}}); selections->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mSelections->GetNbinsX(); ++iBin) { selections->GetXaxis()->SetBinLabel(iBin, mSelections->GetXaxis()->GetBinLabel(iBin)); @@ -81,7 +81,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mScalers) { - std::shared_ptr scalers = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Scalers").data(), "", o2::framework::HistType::kTH1D, {{mScalers->GetNbinsX(), -0.5, static_cast(mScalers->GetNbinsX() - 0.5)}}); + std::shared_ptr scalers = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "Scalers").data(), "", o2::framework::HistType::kTH1D, {{mScalers->GetNbinsX(), -0.5, (mScalers->GetNbinsX() - 0.5)}}); scalers->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mScalers->GetNbinsX(); ++iBin) { scalers->GetXaxis()->SetBinLabel(iBin, mScalers->GetXaxis()->GetBinLabel(iBin)); @@ -90,7 +90,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mInspectedTVX) { - std::shared_ptr inspectedTVX = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "InspectedTVX").data(), "", o2::framework::HistType::kTH1D, {{mInspectedTVX->GetNbinsX(), -0.5, static_cast(mInspectedTVX->GetNbinsX() - 0.5)}}); + std::shared_ptr inspectedTVX = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "InspectedTVX").data(), "", o2::framework::HistType::kTH1D, {{mInspectedTVX->GetNbinsX(), -0.5, (mInspectedTVX->GetNbinsX() - 0.5)}}); inspectedTVX->SetBit(TH1::kIsAverage); for (int iBin{1}; iBin <= mInspectedTVX->GetNbinsX(); ++iBin) { inspectedTVX->GetXaxis()->SetBinLabel(iBin, mInspectedTVX->GetXaxis()->GetBinLabel(iBin)); @@ -99,7 +99,7 @@ void Zorro::populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, } } if (mTOIs.size()) { - mAnalysedTriggersOfInterest = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "AnalysedTriggersOfInterest").data(), "", o2::framework::HistType::kTH1D, {{static_cast(mTOIs.size()), -0.5, static_cast(mTOIs.size() - 0.5)}}).get(); + mAnalysedTriggersOfInterest = histRegistry.add((folderName + "/" + std::to_string(runNumber) + "/" + "AnalysedTriggersOfInterest").data(), "", o2::framework::HistType::kTH1D, {{static_cast(mTOIs.size()), -0.5, (mTOIs.size() - 0.5)}}).get(); for (size_t i{0}; i < mTOIs.size(); ++i) { mAnalysedTriggersOfInterest->GetXaxis()->SetBinLabel(i + 1, mTOIs[i].data()); } @@ -176,7 +176,7 @@ void Zorro::populateExternalHists(int runNumber, TH2* ZorroHisto, TH2* ToiHisto) mRunNumberHistos.push_back(runNumber); } -std::vector Zorro::initCCDB(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, uint64_t timestamp, std::string tois, int bcRange) +std::vector Zorro::initCCDB(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, uint64_t timestamp, const std::string& tois, int bcRange) { if (mRunNumber == runNumber) { return mTOIidx; diff --git a/Common/Core/Zorro.h b/Common/Core/Zorro.h index 29247b21c15..f200e099c44 100644 --- a/Common/Core/Zorro.h +++ b/Common/Core/Zorro.h @@ -46,12 +46,12 @@ class Zorro { public: Zorro() = default; - std::vector initCCDB(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, uint64_t timestamp, std::string tois, int bcTolerance = 500); + std::vector initCCDB(o2::ccdb::BasicCCDBManager* ccdb, int runNumber, uint64_t timestamp, const std::string& tois, int bcTolerance = 500); std::bitset<128> fetch(uint64_t bcGlobalId, uint64_t tolerance = 100); bool isSelected(uint64_t bcGlobalId, uint64_t tolerance = 100, TH2* toiHisto = nullptr); bool isNotSelectedByAny(uint64_t bcGlobalId, uint64_t tolerance = 100); - void populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, int runNumber, std::string folderName = "Zorro"); + void populateHistRegistry(o2::framework::HistogramRegistry& histRegistry, int runNumber, const std::string& folderName = "Zorro"); void populateExternalHists(int runNumber, TH2* zorroHisto = nullptr, TH2* toiHisto = nullptr); TH1D* getScalers() const { return mScalers; } diff --git a/Common/Core/ZorroSummary.cxx b/Common/Core/ZorroSummary.cxx index 0a1012c3edc..bb020dc0924 100644 --- a/Common/Core/ZorroSummary.cxx +++ b/Common/Core/ZorroSummary.cxx @@ -20,7 +20,7 @@ void ZorroSummary::Copy(TObject& c) const { - static_cast(c) = *this; + dynamic_cast(c) = *this; } Long64_t ZorroSummary::Merge(TCollection* list) diff --git a/Common/Core/analysisPCH.cxx b/Common/Core/analysisPCH.cxx new file mode 100644 index 00000000000..2ee8ee5867f --- /dev/null +++ b/Common/Core/analysisPCH.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the precompiled header shared by DPL analysis workflows; see +// AnalysisPCH in this directory's CMakeLists.txt. It exists only to own the +// PCH, so it has no code of its own. diff --git a/Common/Core/fwdtrackUtilities.h b/Common/Core/fwdtrackUtilities.h index 53d5f74f931..932a77d9ae3 100644 --- a/Common/Core/fwdtrackUtilities.h +++ b/Common/Core/fwdtrackUtilities.h @@ -18,7 +18,6 @@ #ifndef COMMON_CORE_FWDTRACKUTILITIES_H_ #define COMMON_CORE_FWDTRACKUTILITIES_H_ -#include #include #include #include @@ -28,7 +27,10 @@ #include #include +#include #include +#include +#include #include #include #include @@ -48,14 +50,24 @@ using SMatrix55 = ROOT::Math::SMatrix; using SMatrix5 = ROOT::Math::SVector; -/// Produce TrackParCovFwds for MFT and FwdTracks, w/ or w/o cov, with z shift +template +concept is_fwd_track = requires(T t) { + { t.rAtAbsorberEnd() } -> std::same_as; +}; + +template +concept is_fwd_cov = requires(T t) { + { t.sigmaX() } -> std::same_as; +}; + +/// Produce TrackParCovFwds for MFT and FwdTracks, w/ or w/o cov, with x, y and z shifts template -o2::track::TrackParCovFwd getTrackParCovFwdShift(TFwdTrack const& track, float zshift, TCovariance const&... covOpt) +o2::track::TrackParCovFwd getTrackParCovFwd3DShift(TFwdTrack const& track, float xshift, float yshift, float zshift, TCovariance const&... covOpt) { double chi2 = track.chi2(); if constexpr (sizeof...(covOpt) == 0) { // No covariance passed - if constexpr (std::is_same_v, aod::FwdTracks::iterator>) { + if constexpr (is_fwd_track) { if (track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { chi2 = track.chi2() * (2.f * track.nClusters() - 5.f); } @@ -63,14 +75,14 @@ o2::track::TrackParCovFwd getTrackParCovFwdShift(TFwdTrack const& track, float z } else { // Covariance passed using TCov = std::decay_t; - if constexpr (std::is_same_v) { + if constexpr (is_fwd_cov) { if (track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { chi2 = track.chi2() * (2.f * track.nClusters() - 5.f); } } } - SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); + SMatrix5 tpars(track.x() + xshift, track.y() + yshift, track.phi(), track.tgl(), track.signed1Pt()); SMatrix55 tcovs; if constexpr (sizeof...(covOpt) == 1) { @@ -89,6 +101,38 @@ o2::track::TrackParCovFwd getTrackParCovFwdShift(TFwdTrack const& track, float z return o2::track::TrackParCovFwd(track.z() + zshift, tpars, tcovs, chi2); } +/// Produce TrackParCovFwds for MFT and FwdTracks, w/ or w/o cov, with z shift +template +o2::track::TrackParCovFwd getTrackParCovFwdShift(TFwdTrack const& track, float zshift, TCovariance const&... covOpt) +{ + return getTrackParCovFwd3DShift(track, 0.f, 0.f, zshift, covOpt...); +} + +inline o2::track::TrackParCovFwd getTrackParCovFwd3DShiftManual( + const double x, const double y, const double phi, const double tgl, const double signed1Pt, + const double cXX, + const double cXY, const double cYY, + const double cPhiX, const double cPhiY, const double cPhiPhi, + const double cTglX, const double cTglY, const double cTglPhi, const double cTglTgl, + const double c1PtX, const double c1PtY, const double c1PtPhi, const double c1PtTgl, const double c1Pt21Pt2, + const float z, const float xshift, const float yshift, const float zshift, const float chi2) +{ + SMatrix5 tpars(x + xshift, y + yshift, phi, tgl, signed1Pt); + + SMatrix55 tcovs; + std::vector v1{ + cXX, + cXY, cYY, + cPhiX, cPhiY, cPhiPhi, + cTglX, cTglY, cTglPhi, cTglTgl, + c1PtX, c1PtY, c1PtPhi, c1PtTgl, c1Pt21Pt2}; + tcovs = SMatrix55(v1.begin(), v1.end()); + v1.clear(); + v1.shrink_to_fit(); + + return o2::track::TrackParCovFwd(z + zshift, tpars, tcovs, chi2); +} + template o2::track::TrackParCovFwd getTrackParCovFwd(TFwdTrack const& track, TFwdTrackCov const& cov) { @@ -120,15 +164,15 @@ o2::track::TrackParCovFwd getTrackParCovFwd(TFwdTrack const& track, TFwdTrackCov /// propagate fwdtrack to a certain point. template -o2::dataformats::GlobalFwdTrack propagateMuon(TFwdTrack const& muon, TFwdTrackCov const& cov, TCollision const& collision, const propagationPoint endPoint, const float matchingZ, const float bzkG, const float zshift = 0.f) +o2::dataformats::GlobalFwdTrack propagateMuon(TFwdTrack const& muon, TFwdTrackCov const& cov, TCollision const& collision, const propagationPoint endPoint, const float matchingZ, const float bzkG, const float xshift = 0.f, const float yshift = 0.f, const float zshift = 0.f) { o2::track::TrackParCovFwd trackParCovFwd; if (muon.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { - trackParCovFwd = getTrackParCovFwdShift(muon, zshift, cov); + trackParCovFwd = getTrackParCovFwd3DShift(muon, xshift, yshift, zshift, cov); } else if (muon.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { - trackParCovFwd = getTrackParCovFwdShift(muon, zshift, muon); + trackParCovFwd = getTrackParCovFwd3DShift(muon, xshift, yshift, zshift, muon); } else { - trackParCovFwd = getTrackParCovFwdShift(muon, zshift, muon); + trackParCovFwd = getTrackParCovFwd3DShift(muon, xshift, yshift, zshift, muon); } o2::dataformats::GlobalFwdTrack propmuon = propagateTrackParCovFwd(trackParCovFwd, muon.trackType(), collision, endPoint, matchingZ, bzkG); @@ -233,6 +277,130 @@ o2::dataformats::GlobalFwdTrack refitGlobalMuonCov(TFwdTrack const& muon, TMFTTr return globalTrack; } +template +float getFwdChi2IP(TTrackParCovFwd const& inputTrk, TCollision const& collision, const float bz) +{ + // this function returns imcompatibility of fwdtrack respect to a given PV. + // fwdtracks are never PV contributors in ALICE. + // chi2IP is defined as chi2^{PV}_{with fwdtrack} - chi2^{PV}_{without fwdtrack}. https://arxiv.org/abs/2604.11574 + // chi2IP cannot be used to decide the best fwdtrack-to-collision match or MFT-MCH match, because it gives biases toward small muon impact parameter. + // chi2IP should be used only after the best fwdtrack-to-collision association and the best MFT-MCH match are defined. + + auto trk = inputTrk; // mutable copy + + if (std::abs(bz) < 1e-12) { + trk.propagateToZlinear(collision.posZ()); + } else { + trk.propagateToZhelix(collision.posZ(), bz); + } + + float x = trk.getX(); + float y = trk.getY(); + float phi = trk.getPhi(); + float tgl = trk.getTanl(); + + if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(phi) || !std::isfinite(tgl) || std::abs(tgl) < 1e-8) { + return -1.f; + } + + float dx = x - collision.posX(); + float dy = y - collision.posY(); + + // dx/dz and dy/dz for TrackParFwd. + float tx = std::cos(phi) / tgl; + float ty = std::sin(phi) / tgl; + + const auto& ct = trk.getCovariances(); + + float trkXX = ct(0, 0); + float trkXY = ct(0, 1); + float trkYY = ct(1, 1); + + float pvXX = collision.covXX() - 2.0 * tx * collision.covXZ() + tx * tx * collision.covZZ(); + float pvXY = collision.covXY() - ty * collision.covXZ() - tx * collision.covYZ() + tx * ty * collision.covZZ(); + float pvYY = collision.covYY() - 2.0 * ty * collision.covYZ() + ty * ty * collision.covZZ(); + + float sXX = trkXX + pvXX; + float sXY = trkXY + pvXY; + float sYY = trkYY + pvYY; + + float det = sXX * sYY - sXY * sXY; + + if (!std::isfinite(det) || det <= 0.0) { + return -1.f; + } + + float chi2 = (sYY * dx * dx - 2.0 * sXY * dx * dy + sXX * dy * dy) / det; + + if (!std::isfinite(chi2) || chi2 < -1e-8) { + return -1.f; + } + + return std::max(0.f, chi2); +} + +template +float getFwdChi2IP(const TFullFwdTrack& fwdtrack, const TCollision& collision, const float bz, const float xShift, const float yShift, const float zShift) +{ + // this function returns imcompatibility of fwdtrack respect to a given PV. + // fwdtracks are never PV contributors in ALICE. + // chi2IP is defined as chi2^{PV}_{with fwdtrack} - chi2^{PV}_{without fwdtrack}. https://arxiv.org/abs/2604.11574 + // chi2IP cannot be used to decide the best fwdtrack-to-collision match or MFT-MCH match, because it gives biases toward small muon impact parameter. + // chi2IP should be used only after the best fwdtrack-to-collision association and the best MFT-MCH match are defined. + + o2::track::TrackParCovFwd trk = getTrackParCovFwd3DShift(fwdtrack, xShift, yShift, zShift, fwdtrack); + + if (std::abs(bz) < 1e-12) { + trk.propagateToZlinear(collision.posZ()); + } else { + trk.propagateToZhelix(collision.posZ(), bz); + } + + float x = trk.getX(); + float y = trk.getY(); + float phi = trk.getPhi(); + float tgl = trk.getTanl(); + + if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(phi) || !std::isfinite(tgl) || std::abs(tgl) < 1e-8) { + return -1.f; + } + + float dx = x - collision.posX(); + float dy = y - collision.posY(); + + // dx/dz and dy/dz for TrackParFwd. + float tx = std::cos(phi) / tgl; + float ty = std::sin(phi) / tgl; + + const auto& ct = trk.getCovariances(); + + float trkXX = ct(0, 0); + float trkXY = ct(0, 1); + float trkYY = ct(1, 1); + + float pvXX = collision.covXX() - 2.0 * tx * collision.covXZ() + tx * tx * collision.covZZ(); + float pvXY = collision.covXY() - ty * collision.covXZ() - tx * collision.covYZ() + tx * ty * collision.covZZ(); + float pvYY = collision.covYY() - 2.0 * ty * collision.covYZ() + ty * ty * collision.covZZ(); + + float sXX = trkXX + pvXX; + float sXY = trkXY + pvXY; + float sYY = trkYY + pvYY; + + float det = sXX * sYY - sXY * sXY; + + if (!std::isfinite(det) || det <= 0.0) { + return -1.f; + } + + float chi2 = (sYY * dx * dx - 2.0 * sXY * dx * dy + sXX * dy * dy) / det; + + if (!std::isfinite(chi2) || chi2 < -1e-8) { + return -1.f; + } + + return std::max(0.f, chi2); +} + } // namespace fwdtrackutils } // namespace o2::aod diff --git a/Common/Core/macros/testMetadataHelper.C b/Common/Core/macros/testMetadataHelper.C index 9eb3a7e1e6b..12b29935455 100644 --- a/Common/Core/macros/testMetadataHelper.C +++ b/Common/Core/macros/testMetadataHelper.C @@ -45,7 +45,7 @@ auto readMetadata(std::unique_ptr& currentFile) -> std::vector { // Get the metadata, if any - auto m = (TMap*)currentFile->Get("metaData"); + auto m = dynamic_cast(currentFile->Get("metaData")); if (!m) { return {}; } @@ -59,7 +59,7 @@ auto readMetadata(std::unique_ptr& currentFile) -> std::vectorGetName()); first = false; } - auto objString = (TObjString*)m->GetValue(obj); + auto objString = dynamic_cast(m->GetValue(obj)); std::string key = "aod-metadata-" + std::string(obj->GetName()); LOGP(info, "- {}: {} goes into key {}", obj->GetName(), objString->String().Data(), key); char const* value = strdup(objString->String()); @@ -128,9 +128,9 @@ std::map buildMapForCommitHash(const std::string& hash) return results; } -void populateCCDBWithCommitAvailability(std::map hasHashMap, - const std::string commitHash, - const std::string ccdbUrl = "http://ccdb-test.cern.ch:8080/") +void populateCCDBWithCommitAvailability(const std::map& hasHashMap, + const std::string& commitHash, + const std::string& ccdbUrl = "http://ccdb-test.cern.ch:8080/") { // First, init the CCDB manager to test if the ccdb is already populated o2::ccdb::CcdbApi api; @@ -157,7 +157,7 @@ void populateCCDBWithCommitAvailability(std::map hasHashMap, } } -void testMetadataHelper(std::string aod = "/tmp/AO2D.root") +void testMetadataHelper(const std::string& aod = "/tmp/AO2D.root") { createO2VersionFile(); const std::string commitHash = "63bc2e3893851ef0f849bb4c98c65eae1ba21e47"; diff --git a/Common/Core/trackUtilities.h b/Common/Core/trackUtilities.h index 26f55491f05..a713675c6a9 100644 --- a/Common/Core/trackUtilities.h +++ b/Common/Core/trackUtilities.h @@ -20,12 +20,11 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include -#include - #include #include // std::move diff --git a/Common/DataModel/Centrality.h b/Common/DataModel/Centrality.h index f3a21d61dda..a7f073fde92 100644 --- a/Common/DataModel/Centrality.h +++ b/Common/DataModel/Centrality.h @@ -26,16 +26,19 @@ DECLARE_SOA_COLUMN(CentRun2CL1, centRun2CL1, float); //! Run 2 DECLARE_SOA_COLUMN(CentRun2RefMult5, centRun2RefMult5, float); //! Run 2 cent. from ref. mult. estimator, eta 0.5 DECLARE_SOA_COLUMN(CentRun2RefMult8, centRun2RefMult8, float); //! Run 2 cent. from ref. mult. estimator, eta 0.8 -DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); //! Run 3 cent. from FV0A multiplicities -DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); //! Run 3 cent. from FT0A+FT0C multiplicities -DECLARE_SOA_COLUMN(CentFT0A, centFT0A, float); //! Run 3 cent. from FT0A multiplicity -DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); //! Run 3 cent. from FT0C multiplicity -DECLARE_SOA_COLUMN(CentFT0CVariant1, centFT0CVariant1, float); //! Run 3 cent. from FT0C multiplicity -DECLARE_SOA_COLUMN(CentFT0CVariant2, centFT0CVariant2, float); //! Run 3 cent. from FT0C multiplicity, uses classical truncated Nancestors (NOT recommended, cross-check only!) -DECLARE_SOA_COLUMN(CentFDDM, centFDDM, float); //! Run 3 cent. from FDDA+FDDC multiplicity -DECLARE_SOA_COLUMN(CentNTPV, centNTPV, float); //! Run 3 cent. from the number of tracks contributing to the PV -DECLARE_SOA_COLUMN(CentNGlobal, centNGlobal, float); //! Run 3 cent. from the number of global tracks -DECLARE_SOA_COLUMN(CentMFT, centMFT, float); //! Run 3 cent. from the number of tracks in the MFT +DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); //! Run 3 cent. from FV0A multiplicities +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); //! Run 3 cent. from FT0A+FT0C multiplicities +DECLARE_SOA_COLUMN(CentFT0MAnchorCol, centFT0MAnchorCol, float); //! Run 3 cent. from FT0A+FT0C multiplicities - anchored glauber - collision based (NOT recommended, cross-check only!) +DECLARE_SOA_COLUMN(CentFT0MAnchorBC, centFT0MAnchorBC, float); //! Run 3 cent. from FT0A+FT0C multiplicities - anchored glauber - bunch crossing based (NOT recommended, cross-check only!) +DECLARE_SOA_COLUMN(CentFT0A, centFT0A, float); //! Run 3 cent. from FT0A multiplicity +DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); //! Run 3 cent. from FT0C multiplicity +DECLARE_SOA_COLUMN(CentFT0CVariant1, centFT0CVariant1, float); //! Run 3 cent. from FT0C multiplicity +DECLARE_SOA_COLUMN(CentFT0CVariant2, centFT0CVariant2, float); //! Run 3 cent. from FT0C multiplicity, uses classical truncated Nancestors (NOT recommended, cross-check only!) +DECLARE_SOA_COLUMN(CentFDDM, centFDDM, float); //! Run 3 cent. from FDDA+FDDC multiplicity +DECLARE_SOA_COLUMN(CentNTPV, centNTPV, float); //! Run 3 cent. from the number of tracks contributing to the PV +DECLARE_SOA_COLUMN(CentNGlobal, centNGlobal, float); //! Run 3 cent. from the number of global tracks +DECLARE_SOA_COLUMN(CentMFT, centMFT, float); //! Run 3 cent. from the number of tracks in the MFT +DECLARE_SOA_COLUMN(CentFT0MOuterA, centFT0MOuterA, float); //! Run 3 cent. from FT0AOuter+FT0C multiplicity } // namespace cent // Run 2 tables @@ -59,8 +62,11 @@ DECLARE_SOA_TABLE(CentNGlobals, "AOD", "CENTNGLOBAL", cent::CentNGlobal); //! Ru DECLARE_SOA_TABLE(CentMFTs, "AOD", "CENTMFT", cent::CentMFT); //! Run 3 MFT tracks centrality table // Run 3 variant tables -DECLARE_SOA_TABLE(CentFT0CVariant1s, "AOD", "CENTFT0Cvar1", cent::CentFT0CVariant1); //! Run 3 FT0C variant 1 -DECLARE_SOA_TABLE(CentFT0CVariant2s, "AOD", "CENTFT0Cvar2", cent::CentFT0CVariant2); //! Run 3 FT0C variant 1 - uses truncated Nancestors in glauber fit. Not recommended! for cross-checks only +DECLARE_SOA_TABLE(CentFT0CVariant1s, "AOD", "CENTFT0Cvar1", cent::CentFT0CVariant1); //! Run 3 FT0C variant 1 +DECLARE_SOA_TABLE(CentFT0CVariant2s, "AOD", "CENTFT0Cvar2", cent::CentFT0CVariant2); //! Run 3 FT0C variant 2 - uses truncated Nancestors in glauber fit. Not recommended! for cross-checks only +DECLARE_SOA_TABLE(CentFT0MAnchorCols, "AOD", "CENTFT0MCOL", cent::CentFT0MAnchorCol); //! Run 3 FT0M with anchored Glauber. Colliison based calibration. Not recommended! for cross-checks only +DECLARE_SOA_TABLE(CentFT0MAnchorBCs, "AOD", "CENTFT0MBC", cent::CentFT0MAnchorBC); //! Run 3 FT0M with anchored Glauber. Bunch crossing based calibration. Not recommended! for cross-checks only +DECLARE_SOA_TABLE(CentFT0MOuterAs, "AOD", "CENTFT0Mvar3", cent::CentFT0MOuterA); //! Run 3 FT0MOuterA - excludes FT0A channels that saturates in PbPb (channel id < 31) // Run 3 centrality per BC (joinable with BC) DECLARE_SOA_TABLE(BCCentFT0Ms, "AOD", "BCCENTFT0M", cent::CentFT0M, o2::soa::Marker<1>); //! Run 3 FT0M BC centrality table @@ -84,24 +90,32 @@ using CentNTPV = CentNTPVs::iterator; using CentNGlobal = CentNGlobals::iterator; using CentMFT = CentMFTs::iterator; +using CentFT0CVariant1 = CentFT0CVariant1s::iterator; +using CentFT0CVariant2 = CentFT0CVariant2s::iterator; +using CentFT0MOuterA = CentFT0MOuterAs::iterator; + using BCCentFT0M = BCCentFT0Ms::iterator; using BCCentFT0A = BCCentFT0As::iterator; using BCCentFT0C = BCCentFT0Cs::iterator; template concept HasRun2Centrality = requires(T&& t) { + // NOLINTBEGIN(readability/braces) { t.centRun2V0M() }; { t.centRun2CL0() }; { t.centRun2CL1() }; + // NOLINTEND(readability/braces) }; template concept HasCentrality = requires(T&& t) { + // NOLINTBEGIN(readability/braces) { t.centFV0A() }; { t.centFT0M() }; { t.centFT0A() }; { t.centFT0C() }; { t.centNTPV() }; + // NOLINTEND(readability/braces) }; } // namespace o2::aod diff --git a/Common/DataModel/FwdTrackReAlignTables.h b/Common/DataModel/FwdTrackReAlignTables.h index 199a9c994c0..879d3fe7486 100644 --- a/Common/DataModel/FwdTrackReAlignTables.h +++ b/Common/DataModel/FwdTrackReAlignTables.h @@ -72,6 +72,19 @@ using FwdTrackRealign = FwdTracksReAlign::iterator; using FwdTrkCovRealign = FwdTrksCovReAlign::iterator; using FullFwdTracksRealign = soa::Join; using FullFwdTrackRealign = FullFwdTracksRealign::iterator; + +// ambiguity table for realigned muons +namespace fwdtrackrealignambiguous +{ +DECLARE_SOA_INDEX_COLUMN_FULL(FwdTrackRealign, fwdTrackRealign, int, FwdTracksReAlign, ""); //! FwdTracksReAlign index +DECLARE_SOA_SLICE_INDEX_COLUMN(BC, bc); + +} // namespace fwdtrackrealignambiguous + +DECLARE_SOA_TABLE(AmbiguousFwdTrksReAlign, "AOD", "AMBIFWDREALIGN", //! Table for FwdTracksReAlign which are not associated with a collision + o2::soa::Index<>, fwdtrackrealignambiguous::FwdTrackRealignId, fwdtrackrealignambiguous::BCIdSlice); + +using AmbiguousFwdTrkRealign = AmbiguousFwdTrksReAlign::iterator; } // namespace o2::aod #endif // COMMON_DATAMODEL_FWDTRACKREALIGNTABLES_H_ diff --git a/Common/DataModel/GloCCDBObjects.h b/Common/DataModel/GloCCDBObjects.h new file mode 100644 index 00000000000..21b8bb7d610 --- /dev/null +++ b/Common/DataModel/GloCCDBObjects.h @@ -0,0 +1,135 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GloCCDBObjects.h +/// \brief Declarative CCDB columns for the most-commonly needed GLO/ objects. +/// +/// Provides a single shared timestamped table (`aod::GloCCDBObjects`) that +/// tasks can join with `aod::BCsWithTimestamps` to obtain the four GLO/ +/// calibration objects without using `Service`. +/// +/// Usage: +/// \code +/// #include "Common/DataModel/GloCCDBObjects.h" +/// // ... +/// using BCsWithCCDB = soa::Join; +/// void process(BCsWithCCDB const& bcs) { +/// const auto& grpmag = bcs.begin().grpMagField(); +/// const auto& meanvtx = bcs.begin().meanVertex(); +/// const auto& grpecs = bcs.begin().grpECS(); +/// const auto& grplhcif= bcs.begin().grpLHCIF(); +/// } +/// \endcode +/// +/// If you need only a subset of the four objects, declare your own +/// `DECLARE_SOA_TIMESTAMPED_TABLE` with the relevant subset of columns from +/// the `o2::aod::ccdbGlo` namespace rather than joining `aod::GloCCDBObjects`. +/// +/// The material LUT lives in `aod::GeomCCDBObjects` rather than here: it belongs to +/// the geometry/material family, whose validity is essentially static, and keeping it +/// out means joining `aod::GloCCDBObjects` does not drag in a multi-hundred-MB object +/// nobody asked for. Join whichever tables you need — the duplicated `aod::Timestamps` +/// is deduplicated: +/// \code +/// using BCsWithLUT = soa::Join; +/// // rectifyPtrFromFile() is applied by the column's finaliser, so the object +/// // handed back is ready to use. Installing it is a pointer store, so it costs +/// // nothing to redo every timeframe — and doing so picks up a relocated column +/// // buffer for free instead of dangling. +/// o2::base::Propagator::Instance()->setMatLUT(&bcs.begin().matLUT()); +/// \endcode + +#ifndef COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ +#define COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + +#include +#include +#include + +namespace o2::aod +{ +namespace ccdbGlo +{ +DECLARE_SOA_CCDB_COLUMN(GRPMagField, grpMagField, o2::parameters::GRPMagField, "GLO/Config/GRPMagField"); //! +DECLARE_SOA_CCDB_COLUMN(MeanVertex, meanVertex, o2::dataformats::MeanVertexObject, "GLO/Calib/MeanVertex"); //! +DECLARE_SOA_CCDB_COLUMN(GRPECSObject, grpECS, o2::parameters::GRPECSObject, "GLO/Config/GRPECS"); //! +DECLARE_SOA_CCDB_COLUMN(GRPLHCIFData, grpLHCIF, o2::parameters::GRPLHCIFData, "GLO/Config/GRPLHCIF"); //! + +/// The material LUT is a FlatObject: straight out of the ROOT streamer its internal +/// pointers are unfixed and its voxel lookup is unbuilt, so it is finalised with +/// MatLayerCylSet::rectifyPtrFromFile() before ever being handed to a task. +DECLARE_SOA_CCDB_COLUMN_FULL(MatLUT, "fMatLUT", matLUT, o2::base::MatLayerCylSet, "GLO/Param/MatLUT", 0, //! + [](o2::base::MatLayerCylSet* lut) { return o2::base::MatLayerCylSet::rectifyPtrFromFile(lut); }); + +/// Returns the GRPLHCIF object for this BC: straight from the aod::GrpLHCIFCCDBObjects +/// column when the BC table carries it, else through a CCDB query at \p timestamp. Lets a +/// shared module serve migrated and un-migrated tasks from a single code path — the +/// discarded branch is not instantiated, so an un-migrated caller keeps its CCDB query and +/// a migrated one never names the CCDB manager. +template +inline o2::parameters::GRPLHCIFData* grpLHCIFFor(TBC const& bc, TCCDB& ccdb, std::string const& path, int64_t timestamp) +{ + if constexpr (requires { bc.grpLHCIF(); }) { + // The column is the single source of truth for the path; a caller that also configured + // one is telling us two different things, and silently honouring one is how conditions + // diverge unnoticed. + if (path != std::string_view{GRPLHCIFData::query}) { + LOGP(fatal, R"(A GRPLHCIF path "{}" is configured while the object is taken from the aod::GrpLHCIFCCDBObjects column, whose path is "{}". Set it through the "ccdb:{}" option instead.)", + path, GRPLHCIFData::query, GRPLHCIFData::mLabel); + } + return &bc.grpLHCIF(); + } else { + return ccdb->template getForTimeStamp(path, timestamp); + } +} +} // namespace ccdbGlo + +/// Full table — join with aod::BCsWithTimestamps to obtain all four objects. +DECLARE_SOA_TIMESTAMPED_TABLE(GloCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "GLOCCDBOBJ", //! + ccdbGlo::GRPMagField, ccdbGlo::MeanVertex, ccdbGlo::GRPECSObject, ccdbGlo::GRPLHCIFData); + +/// The LHC filling scheme and beam configuration on its own. Run-uniform: GRPLHCIF is set +/// per fill and a run lies within a fill, so the fetcher resolves it once per run rather +/// than once per BC. Kept apart from aod::GloCCDBObjects so that a task needing only this +/// does not also fetch GRPMagField, MeanVertex and GRPECS — GRPLHCIF is the most widely +/// used of the four (44 files reference the path) and is usually wanted alone. +DECLARE_SOA_UNIFORM_TABLE(GrpLHCIFCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "GRPLHCIFCCDB", //! + ccdbGlo::GRPLHCIFData); + +/// Geometry and material description: objects which describe where the detector material +/// is, and which share an essentially static interval of validity. Kept apart from the GRP +/// family above, which changes per run (and, for GRPMagField, per timeframe). +/// The aligned/ideal geometry and the per-detector alignment objects belong here too when +/// they get columns; see GRPGeomRequest in O2 (GLO/Config/GeometryAligned, GLO/Config/Geometry, +/// /Calib/Align) for the family. +/// Join it alongside aod::GloCCDBObjects when a task needs both — the duplicated +/// aod::Timestamps is deduplicated when the joined table's originals are merged. +/// Uniform in the run number: the geometry/material description does not change within a +/// run, so the fetcher queries once per distinct run rather than once per BC. +DECLARE_SOA_UNIFORM_TABLE(GeomCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "GEOMCCDBOBJ", //! + ccdbGlo::MatLUT); +} // namespace o2::aod + +#endif // COMMON_DATAMODEL_GLOCCDBOBJECTS_H_ diff --git a/Common/DataModel/Multiplicity.h b/Common/DataModel/Multiplicity.h index 0d04cfc2a14..df6dee0520e 100644 --- a/Common/DataModel/Multiplicity.h +++ b/Common/DataModel/Multiplicity.h @@ -100,8 +100,20 @@ DECLARE_SOA_COLUMN(TimeToNext, timeToNext, float); //! DECLARE_SOA_COLUMN(TimeToNeNext, timeToNeNext, float); //! // Extra information from FIT detectors -DECLARE_SOA_COLUMN(MultFV0AOuter, multFV0AOuter, float); //! FV0 without innermost ring DECLARE_SOA_COLUMN(FT0TriggerMask, ft0TriggerMask, uint8_t); //! +DECLARE_SOA_COLUMN(MultFV0AOuter, multFV0AOuter, float); //! FV0 without innermost ring +DECLARE_SOA_COLUMN(MultFT0AOuter, multFT0AOuter, float); //! FT0A without innermost ring +DECLARE_SOA_COLUMN(MultFT0COuter, multFT0COuter, float); //! FT0C without innermost ring + +// Timing information +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); //! +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); //! +DECLARE_SOA_COLUMN(TimeZPA, timeZPA, float); //! +DECLARE_SOA_COLUMN(TimeZPC, timeZPC, float); //! +DECLARE_SOA_COLUMN(TimeT0A, timeT0A, float); //! +DECLARE_SOA_COLUMN(TimeT0C, timeT0C, float); //! +DECLARE_SOA_COLUMN(TimeFDA, timeFDA, float); //! +DECLARE_SOA_COLUMN(TimeFDC, timeFDC, float); //! } // namespace mult DECLARE_SOA_TABLE(FV0Mults, "AOD", "FV0MULT", //! Multiplicity with the FV0 detector @@ -132,7 +144,8 @@ DECLARE_SOA_TABLE(MFTMults, "AOD", "MFTMULT", //! Multiplicity with MFT DECLARE_SOA_TABLE(FITExtraMults, "AOD", "FITEXTRAMULT", //! Extra information from FIT detectors mult::MultFV0AOuter, - mult::FT0TriggerMask); + mult::MultFT0AOuter, + mult::MultFT0COuter); using BarrelMults = soa::Join; using Mults = soa::Join; @@ -204,6 +217,8 @@ DECLARE_SOA_TABLE_VERSIONED(MultMCExtras_001, "AOD", "MULTMCEXTRA", 1, //! Table using MultMCExtras = MultMCExtras_001; using MultMCExtra = MultMCExtras::iterator; using MultsExtraMC = MultMCExtras; // for backwards compatibility with previous naming scheme +template +using MultsExtraMCFrom = MultMCExtras_001From; // crosslinks namespace mult @@ -260,7 +275,7 @@ DECLARE_SOA_COLUMN(MultCollidingBC, multCollidingBC, bool); //! CTP tri DECLARE_SOA_COLUMN(MultFT0PosZ, multFT0PosZ, float); //! Position along Z computed with the FT0 information within the BC DECLARE_SOA_COLUMN(MultFT0PosZValid, multFT0PosZValid, bool); //! Validity of the position along Z computed with the FT0 information } // namespace mult -DECLARE_SOA_TABLE(MultBCs, "AOD", "MULTBC", //! +DECLARE_SOA_TABLE(MultBCs_000, "AOD", "MULTBC", //! mult::MultFT0A, mult::MultFT0C, mult::MultFT0PosZ, @@ -283,11 +298,70 @@ DECLARE_SOA_TABLE(MultBCs, "AOD", "MULTBC", //! mult::MultCollidingBC, timestamp::Timestamp, bc::Flags); -using MultBC = MultBCs::iterator; -DECLARE_SOA_TABLE(MultBcSel, "AOD", "MULTBCSEL", //! BC selection bits joinable with multBCs +DECLARE_SOA_TABLE_VERSIONED(MultBCs_001, "AOD", "MULTBC", 1, //! + mult::MultFT0A, + mult::MultFT0C, + mult::MultFV0A, + mult::MultFDDA, + mult::MultFDDC, + mult::MultZNA, + mult::MultZNC, + mult::MultZEM1, + mult::MultZEM2, + mult::MultZPA, + mult::MultZPC, + mult::MultFV0AOuter, + mult::MultFT0AOuter); + +DECLARE_SOA_TABLE_VERSIONED(MultBCs_002, "AOD", "MULTBC", 2, //! + mult::MultFT0A, + mult::MultFT0C, + mult::MultFV0A, + mult::MultFDDA, + mult::MultFDDC, + mult::MultZNA, + mult::MultZNC, + mult::MultZEM1, + mult::MultZEM2, + mult::MultZPA, + mult::MultZPC, + mult::MultFV0AOuter, + mult::MultFT0AOuter, + mult::MultFT0COuter); + +DECLARE_SOA_TABLE(MultBcSel_000, "AOD", "MULTBCSEL", //! BC selection bits joinable with multBCs evsel::Selection); +DECLARE_SOA_TABLE_VERSIONED(MultBcSel_001, "AOD", "MULTBCSEL", 1, //! BC selection bits joinable with multBCs + evsel::Selection, + evsel::Rct, + bc::Flags, + timestamp::Timestamp, + mult::MultFT0PosZ, + mult::MultFT0PosZValid, + mult::MultV0triggerBits, + mult::MultT0triggerBits, + mult::MultFDDtriggerBits, + mult::MultTriggerMask, + mult::MultCollidingBC, + mult::MultTVX, + mult::MultFV0OrA); + +DECLARE_SOA_TABLE(TimeBCs, "AOD", "TIMEBC", //! + mult::TimeT0A, + mult::TimeT0C, + mult::TimeFDA, + mult::TimeFDC, + mult::TimeZNA, + mult::TimeZNC, + mult::TimeZPA, + mult::TimeZPC) + +using MultBCs = MultBCs_002; +using MultBcSel = MultBcSel_001; +using MultBC = MultBCs::iterator; + // crosslinks namespace mult { diff --git a/Common/DataModel/Qvectors.h b/Common/DataModel/Qvectors.h index d723d659bc5..e71cb7ed0cb 100644 --- a/Common/DataModel/Qvectors.h +++ b/Common/DataModel/Qvectors.h @@ -155,6 +155,95 @@ using QvectorBNegVec = QvectorBNegVecs::iterator; using QvectorBTotVec = QvectorBTotVecs::iterator; ///////////////////////////////////////////////////////////////// +namespace ese_qvec +{ +DECLARE_SOA_COLUMN(IsCalibrated, isCalibrated, bool); + +DECLARE_SOA_COLUMN(EseQvecRe, eseQvecRe, std::vector); +DECLARE_SOA_COLUMN(EseQvecIm, eseQvecIm, std::vector); +DECLARE_SOA_COLUMN(EseQvecAmp, eseQvecAmp, std::vector); + +DECLARE_SOA_COLUMN(EseQvecFT0CReVec, eseQvecFT0CReVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecFT0CImVec, eseQvecFT0CImVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecFT0AReVec, eseQvecFT0AReVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecFT0AImVec, eseQvecFT0AImVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecFT0MReVec, eseQvecFT0MReVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecFT0MImVec, eseQvecFT0MImVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecFV0AReVec, eseQvecFV0AReVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecFV0AImVec, eseQvecFV0AImVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecTPCposReVec, eseQvecTPCposReVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecTPCposImVec, eseQvecTPCposImVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecTPCnegReVec, eseQvecTPCnegReVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecTPCnegImVec, eseQvecTPCnegImVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecTPCallReVec, eseQvecTPCallReVec, std::vector); +DECLARE_SOA_COLUMN(EseQvecTPCallImVec, eseQvecTPCallImVec, std::vector); + +DECLARE_SOA_COLUMN(EseQvecFT0CRe, eseQvecFT0CRe, float); +DECLARE_SOA_COLUMN(EseQvecFT0CIm, eseQvecFT0CIm, float); +DECLARE_SOA_COLUMN(EseQvecFT0ARe, eseQvecFT0ARe, float); +DECLARE_SOA_COLUMN(EseQvecFT0AIm, eseQvecFT0AIm, float); +DECLARE_SOA_COLUMN(EseQvecFT0MRe, eseQvecFT0MRe, float); +DECLARE_SOA_COLUMN(EseQvecFT0MIm, eseQvecFT0MIm, float); +DECLARE_SOA_COLUMN(EseQvecFV0ARe, eseQvecFV0ARe, float); +DECLARE_SOA_COLUMN(EseQvecFV0AIm, eseQvecFV0AIm, float); +DECLARE_SOA_COLUMN(EseQvecTPCposRe, eseQvecTPCposRe, float); +DECLARE_SOA_COLUMN(EseQvecTPCposIm, eseQvecTPCposIm, float); +DECLARE_SOA_COLUMN(EseQvecTPCnegRe, eseQvecTPCnegRe, float); +DECLARE_SOA_COLUMN(EseQvecTPCnegIm, eseQvecTPCnegIm, float); +DECLARE_SOA_COLUMN(EseQvecTPCallRe, eseQvecTPCallRe, float); +DECLARE_SOA_COLUMN(EseQvecTPCallIm, eseQvecTPCallIm, float); + +DECLARE_SOA_COLUMN(EseRedQFT0C, eseRedQFT0C, float); +DECLARE_SOA_COLUMN(EseRedQFT0A, eseRedQFT0A, float); +DECLARE_SOA_COLUMN(EseRedQFT0M, eseRedQFT0M, float); +DECLARE_SOA_COLUMN(EseRedQFV0A, eseRedQFV0A, float); +///////////////////////////////////////////////////////////////// +} // namespace ese_qvec + +DECLARE_SOA_TABLE(EseQvectors, "AOD", "ESEQVECTORDEVS", //! Table with all Qvectors. + qvec::Cent, ese_qvec::IsCalibrated, ese_qvec::EseQvecRe, ese_qvec::EseQvecIm, qvec::QvecAmp); +using Qvector = Qvectors::iterator; + +DECLARE_SOA_TABLE(EseQvecFT0Cs, "AOD", "ESEQVECFT0C", ese_qvec::IsCalibrated, ese_qvec::EseQvecFT0CRe, ese_qvec::EseQvecFT0CIm, qvec::SumAmplFT0C); +DECLARE_SOA_TABLE(EseQvecFT0As, "AOD", "ESEQVECFT0A", ese_qvec::IsCalibrated, ese_qvec::EseQvecFT0ARe, ese_qvec::EseQvecFT0AIm, qvec::SumAmplFT0A); +DECLARE_SOA_TABLE(EseQvecFT0Ms, "AOD", "ESEQVECFT0M", ese_qvec::IsCalibrated, ese_qvec::EseQvecFT0MRe, ese_qvec::EseQvecFT0MIm, qvec::SumAmplFT0M); +DECLARE_SOA_TABLE(EseQvecFV0As, "AOD", "ESEQVECFV0A", ese_qvec::IsCalibrated, ese_qvec::EseQvecFV0ARe, ese_qvec::EseQvecFV0AIm, qvec::SumAmplFV0A); +DECLARE_SOA_TABLE(EseQvecTPCposs, "AOD", "ESEQVECTPCPOS", ese_qvec::IsCalibrated, ese_qvec::EseQvecTPCposRe, ese_qvec::EseQvecTPCposIm, qvec::NTrkTPCpos, qvec::LabelsTPCpos); +DECLARE_SOA_TABLE(EseQvecTPCnegs, "AOD", "ESEQVECTPCNEG", ese_qvec::IsCalibrated, ese_qvec::EseQvecTPCnegRe, ese_qvec::EseQvecTPCnegIm, qvec::NTrkTPCneg, qvec::LabelsTPCneg); +DECLARE_SOA_TABLE(EseQvecTPCalls, "AOD", "ESEQVECTPCALL", ese_qvec::IsCalibrated, ese_qvec::EseQvecTPCallRe, ese_qvec::EseQvecTPCallIm, qvec::NTrkTPCall, qvec::LabelsTPCall); + +DECLARE_SOA_TABLE(EseQvecFT0CVecs, "AOD", "ESEQVECFT0CVEC", ese_qvec::IsCalibrated, ese_qvec::EseQvecFT0CReVec, ese_qvec::EseQvecFT0CImVec, qvec::SumAmplFT0C); +DECLARE_SOA_TABLE(EseQvecFT0AVecs, "AOD", "ESEQVECFT0AVEC", ese_qvec::IsCalibrated, ese_qvec::EseQvecFT0AReVec, ese_qvec::EseQvecFT0AImVec, qvec::SumAmplFT0A); +DECLARE_SOA_TABLE(EseQvecFT0MVecs, "AOD", "ESEQVECFT0MVEC", ese_qvec::IsCalibrated, ese_qvec::EseQvecFT0MReVec, ese_qvec::EseQvecFT0MImVec, qvec::SumAmplFT0M); +DECLARE_SOA_TABLE(EseQvecFV0AVecs, "AOD", "ESEQVECFV0AVEC", ese_qvec::IsCalibrated, ese_qvec::EseQvecFV0AReVec, ese_qvec::EseQvecFV0AImVec, qvec::SumAmplFV0A); +DECLARE_SOA_TABLE(EseQvecTPCposVecs, "AOD", "ESEQVECTPCPVEC", ese_qvec::IsCalibrated, ese_qvec::EseQvecTPCposReVec, ese_qvec::EseQvecTPCposImVec, qvec::NTrkTPCpos, qvec::LabelsTPCpos); +DECLARE_SOA_TABLE(EseQvecTPCnegVecs, "AOD", "ESEQVECTPCNVEC", ese_qvec::IsCalibrated, ese_qvec::EseQvecTPCnegReVec, ese_qvec::EseQvecTPCnegImVec, qvec::NTrkTPCneg, qvec::LabelsTPCneg); +DECLARE_SOA_TABLE(EseQvecTPCallVecs, "AOD", "ESEQVECTPCAVEC", ese_qvec::IsCalibrated, ese_qvec::EseQvecTPCallReVec, ese_qvec::EseQvecTPCallImVec, qvec::NTrkTPCall, qvec::LabelsTPCall); + +DECLARE_SOA_TABLE(EseQvecPercs, "AOD", "ESEQVECPERC", ese_qvec::EseQvecFT0CRe, ese_qvec::EseQvecFT0CIm, qvec::SumAmplFT0C, + ese_qvec::EseQvecFT0ARe, ese_qvec::EseQvecFT0AIm, qvec::SumAmplFT0A, + ese_qvec::EseQvecFT0MRe, ese_qvec::EseQvecFT0MIm, qvec::SumAmplFT0M, + ese_qvec::EseQvecFV0ARe, ese_qvec::EseQvecFV0AIm, qvec::SumAmplFV0A, + ese_qvec::EseQvecTPCposRe, ese_qvec::EseQvecTPCposIm, qvec::NTrkTPCpos, + ese_qvec::EseQvecTPCnegRe, ese_qvec::EseQvecTPCnegIm, qvec::NTrkTPCneg, + ese_qvec::EseQvecTPCallRe, ese_qvec::EseQvecTPCallIm, qvec::NTrkTPCall); + +using EseQvectorFT0C = EseQvecFT0Cs::iterator; +using EseQvectorFT0A = EseQvecFT0As::iterator; +using EseQvectorFT0M = EseQvecFT0Ms::iterator; +using EseQvectorFV0A = EseQvecFV0As::iterator; +using EseQvectorTPCpos = EseQvecTPCposs::iterator; +using EseQvectorTPCneg = EseQvecTPCnegs::iterator; +using EseQvectorTPCall = EseQvecTPCalls::iterator; + +using EseQvectorFT0CVec = EseQvecFT0CVecs::iterator; +using EseQvectorFT0AVec = EseQvecFT0AVecs::iterator; +using EseQvectorFT0MVec = EseQvecFT0MVecs::iterator; +using EseQvectorFV0AVec = EseQvecFV0AVecs::iterator; +using EseQvectorTPCposVec = EseQvecTPCposVecs::iterator; +using EseQvectorTPCnegVec = EseQvecTPCnegVecs::iterator; +using EseQvectorTPCallVec = EseQvecTPCallVecs::iterator; +///////////////////////////////////////////////////////////////// } // namespace o2::aod #endif // COMMON_DATAMODEL_QVECTORS_H_ diff --git a/Common/DataModel/TpcCCDBObjects.h b/Common/DataModel/TpcCCDBObjects.h new file mode 100644 index 00000000000..9449946ff6c --- /dev/null +++ b/Common/DataModel/TpcCCDBObjects.h @@ -0,0 +1,46 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TpcCCDBObjects.h +/// \brief Declarative CCDB columns for TPC calibration objects. +/// +/// Unlike the geometry/material family in GloCCDBObjects.h, the drift velocity +/// genuinely varies within a run, so the table keeps the default uniformity — one +/// object per distinct timestamp — rather than collapsing per run. +/// +/// Usage: +/// \code +/// using BCsWithVDrift = soa::Join; +/// vdriftManager.update(bc.vdriftTgl()); +/// \endcode + +#ifndef COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ +#define COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ + +#include +#include +#include + +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + +namespace o2::aod +{ +namespace ccdbTpc +{ +DECLARE_SOA_CCDB_COLUMN(VDriftTgl, vdriftTgl, o2::tpc::VDriftCorrFact, "TPC/Calib/VDriftTgl"); //! +} // namespace ccdbTpc + +DECLARE_SOA_TIMESTAMPED_TABLE(TpcCalibCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 1, "TPCCALIBCCDB", //! + ccdbTpc::VDriftTgl); +} // namespace o2::aod + +#endif // COMMON_DATAMODEL_TPCCCDBOBJECTS_H_ diff --git a/Common/DataModel/TrackTunerCCDBObjects.h b/Common/DataModel/TrackTunerCCDBObjects.h new file mode 100644 index 00000000000..5c27ea2f9eb --- /dev/null +++ b/Common/DataModel/TrackTunerCCDBObjects.h @@ -0,0 +1,61 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TrackTunerCCDBObjects.h +/// \brief Declarative CCDB columns for the TrackTuner DCA / Q-over-pt calibrations. +/// +/// The DCA calibration is published under a different path per data-taking period, so +/// the column declares a uniformity-value-to-path mapping rather than a single path: +/// the fetcher picks the entry whose run range contains the row's run number. This +/// replaces TrackTuner::getPathInputFileAutomaticFromCCDB(), whose run ranges these are. +/// A run matching no range is a fatal error, as it was before — there is deliberately no +/// fallback entry, since silently using another period's calibration is worse than stopping. +/// +/// The ranges are the column's *default*; the whole mapping can be replaced at runtime +/// through the "ccdb:fTrackTunerDca" option, so adding a period need not be a code change. + +#ifndef COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ +#define COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ + +#include +#include + +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include + +namespace o2::aod +{ +namespace ccdbTrackTuner +{ +DECLARE_SOA_CCDB_COLUMN(TrackTunerDca, trackTunerDca, TList, //! + "520259-529691=Users/m/mfaggin/test/inputsTrackTuner/pp2023/pass4/vsPhi;" + "534998-543113=Users/m/mfaggin/test/inputsTrackTuner/pp2023/pass4/vsPhi;" + "529397-529418=Users/m/mfaggin/test/inputsTrackTuner/PbPb2023/apass4/vsPhi;" + "543437-545367=Users/m/mfaggin/test/inputsTrackTuner/PbPb2023/apass4/vsPhi;" + "549559-558807=Users/m/mfaggin/test/inputsTrackTuner/pp2024/pass1_minBias/vsPhi;" + "564356-564445=Users/m/mfaggin/test/inputsTrackTuner/OO/LHC25ae;" + "564468-564472=Users/m/mfaggin/test/inputsTrackTuner/OO/LHC25af;" + "559348-559387=Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_positive;" + "559408-559456=Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative"); + +DECLARE_SOA_CCDB_COLUMN(TrackTunerQOverPt, trackTunerQOverPt, TList, //! + "Users/h/hsharma/qOverPtGraphs"); +} // namespace ccdbTrackTuner + +/// Uniform in the run number: one calibration per data-taking period, so the fetcher +/// resolves the path and queries once per distinct run rather than once per BC. +DECLARE_SOA_UNIFORM_TABLE(TrackTunerCCDBObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, + aod::BCs, o2::aod::bc::RunNumber, 1, "TRKTUNERCCDB", //! + ccdbTrackTuner::TrackTunerDca, ccdbTrackTuner::TrackTunerQOverPt); +} // namespace o2::aod + +#endif // COMMON_DATAMODEL_TRACKTUNERCCDBOBJECTS_H_ diff --git a/Common/DataModel/ZDCLightIons.h b/Common/DataModel/ZDCLightIons.h index b0e8e247622..a3d6db2b207 100644 --- a/Common/DataModel/ZDCLightIons.h +++ b/Common/DataModel/ZDCLightIons.h @@ -56,7 +56,8 @@ DECLARE_SOA_COLUMN(CentralityFt0c, centralityFt0c, float); //! Centrality DECLARE_SOA_COLUMN(CentralityFt0a, centralityFt0a, float); //! Centrality DECLARE_SOA_COLUMN(CentralityFt0m, centralityFt0m, float); //! Centrality DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp -DECLARE_SOA_COLUMN(SelectionBits, selectionBits, uint8_t); //! Selection Flags +DECLARE_SOA_COLUMN(SelectionBits, selectionBits, uint8_t); //! Selection flags x collisions +DECLARE_SOA_COLUMN(BcMask, bcMask, uint8_t); //! Selection flags x BC } // namespace zdclightions DECLARE_SOA_TABLE(ZDCLightIons, "AOD", "ZDCTABLELIGHTIONS", @@ -92,7 +93,8 @@ DECLARE_SOA_TABLE(ZDCLightIons, "AOD", "ZDCTABLELIGHTIONS", zdclightions::CentralityFt0a, zdclightions::CentralityFt0m, zdclightions::Timestamp, - zdclightions::SelectionBits); + zdclightions::SelectionBits, + zdclightions::BcMask); } // namespace o2::aod #endif // COMMON_DATAMODEL_ZDCLIGHTIONS_H_ diff --git a/Common/TableProducer/CMakeLists.txt b/Common/TableProducer/CMakeLists.txt index 69cd6084a09..d01f137f339 100644 --- a/Common/TableProducer/CMakeLists.txt +++ b/Common/TableProducer/CMakeLists.txt @@ -34,11 +34,6 @@ o2physics_add_dpl_workflow(event-selection-service-run2 O2::DataFormatsITSMFT COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(multiplicity-table - SOURCES multiplicityTable.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(multcenttable SOURCES multCentTable.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -49,16 +44,6 @@ o2physics_add_dpl_workflow(multiplicity-extra-table PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(centrality-table - SOURCES centralityTable.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(timestamp - SOURCES timestamp.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(weak-decay-indices SOURCES weakDecayIndices.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -69,16 +54,16 @@ o2physics_add_dpl_workflow(ft0-corrected-table PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(track-propagation - SOURCES trackPropagation.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(propagationservice SOURCES propagationService.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(propagationservice-v2 + SOURCES propagationServiceV2.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(propagationservice-run2 SOURCES propagationServiceRun2.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager @@ -149,7 +134,7 @@ o2physics_add_dpl_workflow(zdc-extra-table-producer o2physics_add_dpl_workflow(ese-table-producer SOURCES eseTableProducer.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::FFitWeights COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mftmch-matching-data diff --git a/Common/TableProducer/Converters/CMakeLists.txt b/Common/TableProducer/Converters/CMakeLists.txt index 34ddc8efb3e..600dfea3d9b 100644 --- a/Common/TableProducer/Converters/CMakeLists.txt +++ b/Common/TableProducer/Converters/CMakeLists.txt @@ -113,3 +113,13 @@ o2physics_add_dpl_workflow(run2-tiny-to-full-pid SOURCES run2TinyToFullPID.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(mult-bcs-converter + SOURCES multBCsConverter.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(mult-bcs-002-converter + SOURCES multBCs002Converter.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/Common/TableProducer/Converters/bcConverter.cxx b/Common/TableProducer/Converters/bcConverter.cxx index a8d8ab7d6ed..c0dfb69ec7b 100644 --- a/Common/TableProducer/Converters/bcConverter.cxx +++ b/Common/TableProducer/Converters/bcConverter.cxx @@ -24,6 +24,7 @@ struct bcConverter { void process(aod::BCs_000 const& bcTable) { + bc_001.reserve(bcTable.size()); for (auto& bc : bcTable) { constexpr uint64_t lEmptyTriggerInputs = 0; bc_001(bc.runNumber(), bc.globalBC(), bc.triggerMask(), lEmptyTriggerInputs); diff --git a/Common/TableProducer/Converters/bcFlagsCreator.cxx b/Common/TableProducer/Converters/bcFlagsCreator.cxx index 4d10272e1d7..54b434ee139 100644 --- a/Common/TableProducer/Converters/bcFlagsCreator.cxx +++ b/Common/TableProducer/Converters/bcFlagsCreator.cxx @@ -25,6 +25,7 @@ struct bcFlagsCreator { void process(aod::BCs const& bcTable) { + bcFlags.reserve(bcTable.size()); for (int64_t i = 0; i < bcTable.size(); ++i) { bcFlags(0); } diff --git a/Common/TableProducer/Converters/caloLabelConverter.cxx b/Common/TableProducer/Converters/caloLabelConverter.cxx index 7716abf964f..03b4b1fbec1 100644 --- a/Common/TableProducer/Converters/caloLabelConverter.cxx +++ b/Common/TableProducer/Converters/caloLabelConverter.cxx @@ -27,6 +27,7 @@ struct caloLabelConverter { { std::vector amplitude = {0}; std::vector particleId = {0}; + McCaloLabels_001.reserve(mccalolabelTable.size()); for (auto& mccalolabel : mccalolabelTable) { particleId[0] = mccalolabel.mcParticleId(); // Repopulate new table diff --git a/Common/TableProducer/Converters/collisionConverter.cxx b/Common/TableProducer/Converters/collisionConverter.cxx index d6d27dcdaf3..0f92b09065f 100644 --- a/Common/TableProducer/Converters/collisionConverter.cxx +++ b/Common/TableProducer/Converters/collisionConverter.cxx @@ -41,6 +41,7 @@ struct collisionConverter { void process(aod::Collisions_000 const& collisionTable) { float negtolerance = -1.0f * tolerance; + Collisions_001.reserve(collisionTable.size()); for (auto& collision : collisionTable) { float lYY = collision.covXZ(); float lXZ = collision.covYY(); diff --git a/Common/TableProducer/Converters/fddConverter.cxx b/Common/TableProducer/Converters/fddConverter.cxx index d7f359e6f68..b3d0e42ab1c 100644 --- a/Common/TableProducer/Converters/fddConverter.cxx +++ b/Common/TableProducer/Converters/fddConverter.cxx @@ -25,6 +25,7 @@ struct FddConverter { void process(aod::FDDs_000 const& fdd_000) { + fdd_001.reserve(fdd_000.size()); for (auto& p : fdd_000) { int16_t chargeA[8] = {0u}; int16_t chargeC[8] = {0u}; diff --git a/Common/TableProducer/Converters/hmpConverter.cxx b/Common/TableProducer/Converters/hmpConverter.cxx index ae423c0c9e8..4c34c42eb6e 100644 --- a/Common/TableProducer/Converters/hmpConverter.cxx +++ b/Common/TableProducer/Converters/hmpConverter.cxx @@ -25,6 +25,7 @@ struct hmpConverter { void process(aod::HMPID_000 const& hmpLegacy, aod::Tracks const&) { + HMPID_001.reserve(hmpLegacy.size()); for (auto& hmpData : hmpLegacy) { float phots[] = {0., 0., 0., 0., 0., 0., 0., 0., 0., 0.}; diff --git a/Common/TableProducer/Converters/mcCollisionConverter.cxx b/Common/TableProducer/Converters/mcCollisionConverter.cxx index 290ce3d13dd..b37bd9da719 100644 --- a/Common/TableProducer/Converters/mcCollisionConverter.cxx +++ b/Common/TableProducer/Converters/mcCollisionConverter.cxx @@ -21,6 +21,7 @@ struct mcCollisionConverter { void process(aod::McCollisions_000 const& mcCollisionTable) { + mcCollisions_001.reserve(mcCollisionTable.size()); for (auto& mcCollision : mcCollisionTable) { // Repopulate new table diff --git a/Common/TableProducer/Converters/mcConverter.cxx b/Common/TableProducer/Converters/mcConverter.cxx index 4f18556f8a9..f166ef3d485 100644 --- a/Common/TableProducer/Converters/mcConverter.cxx +++ b/Common/TableProducer/Converters/mcConverter.cxx @@ -25,6 +25,7 @@ struct McConverter { void process(aod::StoredMcParticles_000 const& mcParticles_000) { + mcParticles_001.reserve(mcParticles_000.size()); for (auto& p : mcParticles_000) { std::vector mothers; diff --git a/Common/TableProducer/Converters/mftTracksConverter.cxx b/Common/TableProducer/Converters/mftTracksConverter.cxx index 175e9610f4f..4b1d9950b37 100644 --- a/Common/TableProducer/Converters/mftTracksConverter.cxx +++ b/Common/TableProducer/Converters/mftTracksConverter.cxx @@ -33,6 +33,7 @@ struct MftTracksConverter { Produces mftTracks_001; void process(aod::MFTTracks_000 const& mftTracks_000) { + mftTracks_001.reserve(mftTracks_000.size()); for (const auto& track0 : mftTracks_000) { uint64_t mftClusterSizesAndTrackFlags = 0; diff --git a/Common/TableProducer/Converters/multBCs002Converter.cxx b/Common/TableProducer/Converters/multBCs002Converter.cxx new file mode 100644 index 00000000000..dfed7182c1a --- /dev/null +++ b/Common/TableProducer/Converters/multBCs002Converter.cxx @@ -0,0 +1,51 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multBCs002Converter.cxx +/// \brief Converts MultBCs table from version 001 to 002 +/// \author Jesper Karlsson Gumrpecht + +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include + +struct MultBCs002Converter { + o2::framework::Produces multBC; + static constexpr float DummyValue = -1.f; + void process(o2::aod::MultBCs_001 const& multBCs) + { + multBC.reserve(multBCs.size()); + for (const auto& multbc : multBCs) { + multBC(multbc.multFT0A(), + multbc.multFT0C(), + multbc.multFV0A(), + multbc.multFDDA(), + multbc.multFDDC(), + multbc.multZNA(), + multbc.multZNC(), + multbc.multZEM1(), + multbc.multZEM2(), + multbc.multZPA(), + multbc.multZPC(), + multbc.multFV0AOuter(), + multbc.multFT0AOuter(), + DummyValue /* dummy amplitude for FT0C Outer */); + } + } +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + return o2::framework::WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Common/TableProducer/Converters/multBCsConverter.cxx b/Common/TableProducer/Converters/multBCsConverter.cxx new file mode 100644 index 00000000000..c660b759832 --- /dev/null +++ b/Common/TableProducer/Converters/multBCsConverter.cxx @@ -0,0 +1,74 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multBCsConverter.cxx +/// \brief Converts MultBCs and MultBcSel table from version 000 to 001 +/// \author Jesper Karlsson Gumrpecht + +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct MultBCsConverter { + Produces multBC; + Produces multBcSel; + + static constexpr float DummyValue = -1.f; + static constexpr int DummyRct = 0; + void process(soa::Join const& multBCs) + { + multBC.reserve(multBCs.size()); + multBcSel.reserve(multBCs.size()); + for (const auto& multbc : multBCs) { + multBC( + multbc.multFT0A(), + multbc.multFT0C(), + multbc.multFV0A(), + multbc.multFDDA(), + multbc.multFDDC(), + multbc.multZNA(), + multbc.multZNC(), + multbc.multZEM1(), + multbc.multZEM2(), + multbc.multZPA(), + multbc.multZPC(), + DummyValue, // dummy amplitude for FV0A Outer + DummyValue); // dummy amplitude for FT0A Outer + + multBcSel( + multbc.selection_raw(), + DummyRct, // all flags to false + multbc.flags(), + multbc.timestamp(), + multbc.multFT0PosZ(), + multbc.multFT0PosZValid(), + multbc.multV0triggerBits(), + multbc.multT0triggerBits(), + multbc.multFDDtriggerBits(), + multbc.multTriggerMask(), + multbc.multCollidingBC(), + multbc.multTVX(), + multbc.multFV0OrA()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/Common/TableProducer/Converters/multMCExtrasConverter.cxx b/Common/TableProducer/Converters/multMCExtrasConverter.cxx index 56d6233690d..bb65e450465 100644 --- a/Common/TableProducer/Converters/multMCExtrasConverter.cxx +++ b/Common/TableProducer/Converters/multMCExtrasConverter.cxx @@ -23,6 +23,7 @@ struct MultMCExtrasConverter { Produces multMCExtras_001; void process(aod::MultMCExtras_000 const& multMCExtras_000) { + multMCExtras_001.reserve(multMCExtras_000.size()); for (const auto& r : multMCExtras_000) { multMCExtras_001(r.multMCFT0A(), r.multMCFT0C(), 0, 0, 0, r.multMCNParticlesEta05(), diff --git a/Common/TableProducer/Converters/multsExtraConverter.cxx b/Common/TableProducer/Converters/multsExtraConverter.cxx index 36b7f70d33a..4f9aeb73f12 100644 --- a/Common/TableProducer/Converters/multsExtraConverter.cxx +++ b/Common/TableProducer/Converters/multsExtraConverter.cxx @@ -23,6 +23,7 @@ struct MultsExtraConverter { Produces multsExtra_001; void process(aod::MultsExtra_000 const& multsExtra_000) { + multsExtra_001.reserve(multsExtra_000.size()); for (const auto& r : multsExtra_000) { multsExtra_001(r.multPVTotalContributors(), r.multPVChi2(), r.multCollisionTimeRes(), r.multRunNumber(), r.multPVz(), r.multSel8(), diff --git a/Common/TableProducer/Converters/run2bcinfosConverter.cxx b/Common/TableProducer/Converters/run2bcinfosConverter.cxx index fd0f43f3fda..7f08c814df7 100644 --- a/Common/TableProducer/Converters/run2bcinfosConverter.cxx +++ b/Common/TableProducer/Converters/run2bcinfosConverter.cxx @@ -21,6 +21,7 @@ struct Run2BCInfosConverter { Produces Run2BCInfos_001; void process(aod::Run2BCInfos_000 const& Run2BCInfos_000) { + Run2BCInfos_001.reserve(Run2BCInfos_000.size()); for (const auto& entry : Run2BCInfos_000) { Run2BCInfos_001(entry.eventCuts(), diff --git a/Common/TableProducer/Converters/run2tracksExtraConverter.cxx b/Common/TableProducer/Converters/run2tracksExtraConverter.cxx index 2d321ee06fb..7b288c1f1d7 100644 --- a/Common/TableProducer/Converters/run2tracksExtraConverter.cxx +++ b/Common/TableProducer/Converters/run2tracksExtraConverter.cxx @@ -21,6 +21,7 @@ struct Run2TracksExtraConverter { Produces Run2TrackExtras_001; void process(aod::Run2TrackExtras_000 const& Run2TrackExtras_000) { + Run2TrackExtras_001.reserve(Run2TrackExtras_000.size()); for (const auto& track0 : Run2TrackExtras_000) { Run2TrackExtras_001(track0.itsSignal(), 0); diff --git a/Common/TableProducer/Converters/trackQA002Converter.cxx b/Common/TableProducer/Converters/trackQA002Converter.cxx index 34e1bac2363..f0b53d8b91f 100644 --- a/Common/TableProducer/Converters/trackQA002Converter.cxx +++ b/Common/TableProducer/Converters/trackQA002Converter.cxx @@ -25,6 +25,7 @@ struct TrackQAConverter002 { void process000(aod::TracksQA_000 const& tracksQA_000) { + tracksQA_002.reserve(tracksQA_000.size()); for (const auto& trackQA : tracksQA_000) { tracksQA_002( trackQA.trackId(), @@ -59,6 +60,7 @@ struct TrackQAConverter002 { void process001(aod::TracksQA_001 const& tracksQA_001) { + tracksQA_002.reserve(tracksQA_001.size()); for (const auto& trackQA : tracksQA_001) { tracksQA_002( trackQA.trackId(), diff --git a/Common/TableProducer/Converters/trackQA003Converter.cxx b/Common/TableProducer/Converters/trackQA003Converter.cxx index 4b6e0c7b2bd..407c37c6727 100644 --- a/Common/TableProducer/Converters/trackQA003Converter.cxx +++ b/Common/TableProducer/Converters/trackQA003Converter.cxx @@ -25,6 +25,7 @@ struct TrackQAConverter003 { void process000(aod::TracksQA_000 const& tracksQA_000) { + tracksQA_003.reserve(tracksQA_000.size()); for (const auto& trackQA : tracksQA_000) { tracksQA_003( trackQA.trackId(), @@ -60,6 +61,7 @@ struct TrackQAConverter003 { void process001(aod::TracksQA_001 const& tracksQA_001) { + tracksQA_003.reserve(tracksQA_001.size()); for (const auto& trackQA : tracksQA_001) { tracksQA_003( trackQA.trackId(), @@ -95,6 +97,7 @@ struct TrackQAConverter003 { void process002(aod::TracksQA_002 const& tracksQA_002) { + tracksQA_003.reserve(tracksQA_002.size()); for (const auto& trackQA : tracksQA_002) { tracksQA_003( trackQA.trackId(), diff --git a/Common/TableProducer/Converters/trackQAConverter.cxx b/Common/TableProducer/Converters/trackQAConverter.cxx index 25200812076..5a3b3cad4e0 100644 --- a/Common/TableProducer/Converters/trackQAConverter.cxx +++ b/Common/TableProducer/Converters/trackQAConverter.cxx @@ -24,6 +24,7 @@ struct trackQAConverter { void process(aod::TracksQA_000 const& tracksQA_000) { + tracksQA_001.reserve(tracksQA_000.size()); for (const auto& trackQA : tracksQA_000) { tracksQA_001( trackQA.trackId(), diff --git a/Common/TableProducer/Converters/tracksExtraConverter.cxx b/Common/TableProducer/Converters/tracksExtraConverter.cxx index 0366a84dd8f..5aced20d1de 100644 --- a/Common/TableProducer/Converters/tracksExtraConverter.cxx +++ b/Common/TableProducer/Converters/tracksExtraConverter.cxx @@ -33,6 +33,7 @@ struct TracksExtraConverter { Produces tracksExtra_001; void process(aod::TracksExtra_000 const& tracksExtra_000) { + tracksExtra_001.reserve(tracksExtra_000.size()); for (const auto& track0 : tracksExtra_000) { diff --git a/Common/TableProducer/Converters/tracksExtraV002Converter.cxx b/Common/TableProducer/Converters/tracksExtraV002Converter.cxx index d45eb6e2c09..b6070faa636 100644 --- a/Common/TableProducer/Converters/tracksExtraV002Converter.cxx +++ b/Common/TableProducer/Converters/tracksExtraV002Converter.cxx @@ -36,6 +36,7 @@ struct TracksExtraV002Converter { void processV000ToV002(aod::TracksExtra_000 const& tracksExtra_000) { + tracksExtra_002.reserve(tracksExtra_000.size()); for (const auto& track0 : tracksExtra_000) { @@ -75,6 +76,7 @@ struct TracksExtraV002Converter { void processV001ToV002(aod::TracksExtra_001 const& tracksExtra_001) { + tracksExtra_002.reserve(tracksExtra_001.size()); for (const auto& track1 : tracksExtra_001) { diff --git a/Common/TableProducer/Converters/v0converter.cxx b/Common/TableProducer/Converters/v0converter.cxx index 38c513681b4..ffe1fc35472 100644 --- a/Common/TableProducer/Converters/v0converter.cxx +++ b/Common/TableProducer/Converters/v0converter.cxx @@ -25,6 +25,7 @@ struct V0Converter { void process(aod::V0s_001 const& v0s) { + v0s_002.reserve(v0s.size()); for (auto& v0 : v0s) { uint8_t bitMask = static_cast(1); // first bit on v0s_002(v0.collisionId(), v0.posTrackId(), v0.negTrackId(), bitMask); diff --git a/Common/TableProducer/Converters/zdcConverter.cxx b/Common/TableProducer/Converters/zdcConverter.cxx index 99a88d2b6ca..b03707fe150 100644 --- a/Common/TableProducer/Converters/zdcConverter.cxx +++ b/Common/TableProducer/Converters/zdcConverter.cxx @@ -30,6 +30,7 @@ struct zdcConverter { void process(aod::Zdcs_000 const& zdcLegacy, aod::BCs const&) { + Zdcs_001.reserve(zdcLegacy.size()); for (auto& zdcData : zdcLegacy) { // Get legacy information, please auto bc = zdcData.bc(); diff --git a/Common/TableProducer/PID/CMakeLists.txt b/Common/TableProducer/PID/CMakeLists.txt index 975898966fe..462abb408b1 100644 --- a/Common/TableProducer/PID/CMakeLists.txt +++ b/Common/TableProducer/PID/CMakeLists.txt @@ -58,11 +58,6 @@ o2physics_add_dpl_workflow(pid-tpc-base PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(pid-tpc - SOURCES pidTPC.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::MLCore - COMPONENT_NAME Analysis) - # HMPID # BAYES diff --git a/Common/TableProducer/PID/pidBayes.cxx b/Common/TableProducer/PID/pidBayes.cxx index dd52d06e6cb..27aec0b67b5 100644 --- a/Common/TableProducer/PID/pidBayes.cxx +++ b/Common/TableProducer/PID/pidBayes.cxx @@ -639,7 +639,7 @@ struct bayesPidQa { histos.fill(HIST("event/vertexz"), collision.posZ()); histos.fill(HIST("event/colltime"), collisionTime_ps); - for (auto t : tracks) { + for (const auto& t : tracks) { // if (!t.hasTOF()) { // Skipping tracks without TOF continue; diff --git a/Common/TableProducer/PID/pidTOF.cxx b/Common/TableProducer/PID/pidTOF.cxx index 8fe33443239..2ce49d32369 100644 --- a/Common/TableProducer/PID/pidTOF.cxx +++ b/Common/TableProducer/PID/pidTOF.cxx @@ -17,7 +17,8 @@ /// #include "pidTOFBase.h" - +// +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -31,7 +32,6 @@ #include #include #include -#include #include #include diff --git a/Common/TableProducer/PID/pidTOFBase.cxx b/Common/TableProducer/PID/pidTOFBase.cxx index 796cc8c4058..257dbf5b6b9 100644 --- a/Common/TableProducer/PID/pidTOFBase.cxx +++ b/Common/TableProducer/PID/pidTOFBase.cxx @@ -17,6 +17,7 @@ #include "pidTOFBase.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" @@ -35,7 +36,6 @@ #include #include #include -#include #include #include @@ -358,7 +358,7 @@ struct tofEventTime { using ResponseImplementationEvTime = o2::pid::tof::ExpTimes; using EvTimeCollisions = soa::Join; void processNoFT0(TrksEvTime const& tracks, - EvTimeCollisions const&) + EvTimeCollisions const& collisions) { if (!enableTable) { return; @@ -370,13 +370,13 @@ struct tofEventTime { tableEvTimeTOFOnly.reserve(tracks.size()); } - int lastCollisionId = -1; // Last collision ID analysed - for (auto const& t : tracks) { // Loop on collisions - if (!t.has_collision() || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection + int lastCollisionId = -1; // Last collision ID analysed + for (auto const& t : tracks) { // Loop on collisions + if (!t.has_collision() || collisions.size() == 0 || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -408,7 +408,7 @@ struct tofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeTOF.mEventTimeMultiplicity); } } } @@ -420,7 +420,7 @@ struct tofEventTime { using EvTimeCollisionsFT0 = soa::Join; void processFT0(TrksEvTime const& tracks, aod::FT0s const&, - EvTimeCollisionsFT0 const&) + EvTimeCollisionsFT0 const& collisions) { if (!enableTable) { return; @@ -432,13 +432,13 @@ struct tofEventTime { tableEvTimeTOFOnly.reserve(tracks.size()); } - int lastCollisionId = -1; // Last collision ID analysed - for (auto const& t : tracks) { // Loop on collisions - if (!t.has_collision() || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection + int lastCollisionId = -1; // Last collision ID analysed + for (auto const& t : tracks) { // Loop on collisions + if (!t.has_collision() || collisions.size() == 0 || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -504,7 +504,7 @@ struct tofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeTOF.mEventTimeMultiplicity); } } } @@ -515,7 +515,7 @@ struct tofEventTime { /// Process function to prepare the event for each track on Run 3 data with only the FT0 void processOnlyFT0(TrksEvTime const& tracks, aod::FT0s const&, - EvTimeCollisionsFT0 const&) + EvTimeCollisionsFT0 const& collisions) { if (!enableTable) { return; @@ -529,9 +529,9 @@ struct tofEventTime { for (auto const& t : tracks) { // Loop on collisions if (enableTableTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } - if (!t.has_collision()) { // Track was not assigned, cannot compute event time + if (!t.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute event time tableFlags(0); tableEvTime(0.f, 999.f); continue; diff --git a/Common/TableProducer/PID/pidTOFFull.cxx b/Common/TableProducer/PID/pidTOFFull.cxx index 3e145a49d30..d76cbf3f91e 100644 --- a/Common/TableProducer/PID/pidTOFFull.cxx +++ b/Common/TableProducer/PID/pidTOFFull.cxx @@ -18,6 +18,7 @@ #include "pidTOFBase.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -31,7 +32,6 @@ #include #include #include -#include #include #include @@ -286,7 +286,7 @@ struct tofPidFull { Preslice perCollision = aod::track::collisionId; template using ResponseImplementation = o2::pid::tof::ExpTimes; - void processWSlice(Trks const& tracks, aod::Collisions const&, aod::BCsWithTimestamps const&) + void processWSlice(Trks const& tracks, aod::Collisions const& collisions, aod::BCsWithTimestamps const&) { constexpr auto responseEl = ResponseImplementation(); constexpr auto responseMu = ResponseImplementation(); @@ -302,10 +302,10 @@ struct tofPidFull { reserveTable(pidId, tracks.size()); } - int lastCollisionId = -1; // Last collision ID analysed - float resolution = 1.f; // Last resolution assigned - for (auto const& track : tracks) { // Loop on all tracks - if (!track.has_collision()) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table + int lastCollisionId = -1; // Last collision ID analysed + float resolution = 1.f; // Last resolution assigned + for (auto const& track : tracks) { // Loop on all tracks + if (!track.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table for (auto const& pidId : mEnabledParticles) { makeTableEmpty(pidId); } @@ -392,7 +392,7 @@ struct tofPidFull { using TrksIU = soa::Join; template using ResponseImplementationIU = o2::pid::tof::ExpTimes; - void processWoSlice(TrksIU const& tracks, aod::Collisions const&, aod::BCsWithTimestamps const&) + void processWoSlice(TrksIU const& tracks, aod::Collisions const& collisions, aod::BCsWithTimestamps const&) { constexpr auto responseEl = ResponseImplementationIU(); constexpr auto responseMu = ResponseImplementationIU(); @@ -407,9 +407,9 @@ struct tofPidFull { for (auto const& pidId : mEnabledParticles) { reserveTable(pidId, tracks.size()); } - float resolution = 1.f; // Last resolution assigned - for (auto const& track : tracks) { // Loop on all tracks - if (!track.has_collision()) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table + float resolution = 1.f; // Last resolution assigned + for (auto const& track : tracks) { // Loop on all tracks + if (!track.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table for (auto const& pidId : mEnabledParticles) { makeTableEmpty(pidId); } diff --git a/Common/TableProducer/PID/pidTOFMerge.cxx b/Common/TableProducer/PID/pidTOFMerge.cxx index 66ebaf20f45..8b1486f94d2 100644 --- a/Common/TableProducer/PID/pidTOFMerge.cxx +++ b/Common/TableProducer/PID/pidTOFMerge.cxx @@ -21,6 +21,7 @@ #include "Common/Core/CollisionTypeHelper.h" #include "Common/Core/MetadataHelper.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/PID/PIDTOFParamService.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/EventSelection.h" @@ -40,7 +41,6 @@ #include #include #include -#include #include #include @@ -334,7 +334,7 @@ struct tofEventTime { /// /// Process function to prepare the event for each track on Run 2 data void processRun2(aod::Tracks const& tracks, - aod::Collisions const&, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) { if (!enableTableTOFEvTime) { @@ -345,8 +345,8 @@ struct tofEventTime { tableEvTime.reserve(tracks.size()); tableFlags.reserve(tracks.size()); - for (auto const& t : tracks) { // Loop on collisions - if (!t.has_collision()) { // Track was not assigned, cannot compute event time + for (auto const& t : tracks) { // Loop on collisions + if (!t.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute event time tableFlags(0); tableEvTime(0.f, 999.f); continue; @@ -365,7 +365,7 @@ struct tofEventTime { using ResponseImplementationEvTime = o2::pid::tof::ExpTimes; void processRun3(Run3TrksWtof const& tracks, aod::FT0s const&, - EvTimeCollisionsFT0 const&, + EvTimeCollisionsFT0 const& collisions, aod::BCsWithTimestamps const& bcs) { if (!enableTableTOFEvTime) { @@ -399,13 +399,13 @@ struct tofEventTime { LOG(debug) << "Running on " << CollisionSystemType::getCollisionSystemName(tofResponse->cfgCollisionType()) << " mComputeEvTimeWithTOF " << mComputeEvTimeWithTOF.value << " mComputeEvTimeWithFT0 " << mComputeEvTimeWithFT0.value; if (mComputeEvTimeWithTOF == 1 && mComputeEvTimeWithFT0 == 1) { - int lastCollisionId = -1; // Last collision ID analysed - for (auto const& t : tracks) { // Loop on collisions - if (!t.has_collision() || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection + int lastCollisionId = -1; // Last collision ID analysed + for (auto const& t : tracks) { // Loop on collisions + if (!t.has_collision() || collisions.size() == 0 || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -470,18 +470,18 @@ struct tofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); } } } } else if (mComputeEvTimeWithTOF == 1 && mComputeEvTimeWithFT0 == 0) { - int lastCollisionId = -1; // Last collision ID analysed - for (auto const& t : tracks) { // Loop on collisions - if (!t.has_collision() || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection + int lastCollisionId = -1; // Last collision ID analysed + for (auto const& t : tracks) { // Loop on collisions + if (!t.has_collision() || collisions.size() == 0 || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -513,16 +513,16 @@ struct tofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); } } } } else if (mComputeEvTimeWithTOF == 0 && mComputeEvTimeWithFT0 == 1) { for (auto const& t : tracks) { // Loop on collisions if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } - if (!t.has_collision()) { // Track was not assigned, cannot compute event time + if (!t.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute event time tableFlags(0); tableEvTime(0.f, 999.f); continue; @@ -881,7 +881,7 @@ struct tofPidMerge { template using ResponseImplementation = o2::pid::tof::ExpTimes; void processRun3(Run3TrksWtofWevTime const& tracks, - aod::Collisions const&, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) { constexpr auto responseEl = ResponseImplementation(); @@ -906,8 +906,8 @@ struct tofPidMerge { float resolution = 1.f; // Last resolution assigned float nsigma = 0; - for (auto const& trk : tracks) { // Loop on all tracks - if (!trk.has_collision()) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table + for (auto const& trk : tracks) { // Loop on all tracks + if (!trk.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table for (auto const& pidId : mEnabledParticles) { makeTableEmpty(pidId, false); } @@ -1043,7 +1043,7 @@ struct tofPidMerge { template using ResponseImplementationRun2 = o2::pid::tof::ExpTimes; void processRun2(Run2TrksWtofWevTime const& tracks, - aod::Collisions const&, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) { constexpr auto responseEl = ResponseImplementationRun2(); @@ -1068,8 +1068,8 @@ struct tofPidMerge { float resolution = 1.f; // Last resolution assigned float nsigma = 0; - for (auto const& trk : tracks) { // Loop on all tracks - if (!trk.has_collision()) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table + for (auto const& trk : tracks) { // Loop on all tracks + if (!trk.has_collision() || collisions.size() == 0) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table for (auto const& pidId : mEnabledParticles) { makeTableEmpty(pidId, false); } diff --git a/Common/TableProducer/PID/pidTOFbeta.cxx b/Common/TableProducer/PID/pidTOFbeta.cxx index 1079532fe6a..bbd20541015 100644 --- a/Common/TableProducer/PID/pidTOFbeta.cxx +++ b/Common/TableProducer/PID/pidTOFbeta.cxx @@ -15,6 +15,7 @@ /// \brief Task to produce TOF beta and TOF mass tables /// +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -27,7 +28,6 @@ #include #include #include -#include #include #include diff --git a/Common/TableProducer/PID/pidTPC.cxx b/Common/TableProducer/PID/pidTPC.cxx deleted file mode 100644 index 6949c502736..00000000000 --- a/Common/TableProducer/PID/pidTPC.cxx +++ /dev/null @@ -1,804 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file pidTPC.cxx -/// \author Nicolò Jacazio nicolo.jacazio@cern.ch -/// \author Christian Sonnabend christian.sonnabend@cern.ch -/// \author Annalena Kalteyer annalena.sophie.kalteyer@cern.ch -/// \author Jeremy Wilkinson jeremy.wilkinson@cern.ch -/// \brief Task to produce PID tables for TPC split for each particle. -/// Only the tables for the mass hypotheses requested are filled, and only for the requested table size ("Full" or "Tiny"). The others are sent empty. -/// -#include "pidTPCBase.h" - -#include "Common/CCDB/ctpRateFetcher.h" -#include "Common/Core/MetadataHelper.h" -#include "Common/Core/PID/TPCPIDResponse.h" -#include "Common/Core/TableHelper.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Tools/ML/model.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::pid; -using namespace o2::pid::tpc; -using namespace o2::framework::expressions; -using namespace o2::track; -using namespace o2::ml; - -o2::common::core::MetadataHelper metadataInfo; // Metadata helper - -void customize(std::vector& workflowOptions) -{ - std::vector options{{"add-qa", VariantType::Int, 0, {"Legacy. No effect."}}}; - std::swap(workflowOptions, options); -} - -/// Task to produce the response table -struct tpcPid { - using Trks = soa::Join; - using Coll = soa::Join; - - using TrksMC = soa::Join; - using CollMC = soa::Join; - - // Tables to produce - Produces tablePIDFullEl; - Produces tablePIDFullMu; - Produces tablePIDFullPi; - Produces tablePIDFullKa; - Produces tablePIDFullPr; - Produces tablePIDFullDe; - Produces tablePIDFullTr; - Produces tablePIDFullHe; - Produces tablePIDFullAl; - - Produces tablePIDTinyEl; - Produces tablePIDTinyMu; - Produces tablePIDTinyPi; - Produces tablePIDTinyKa; - Produces tablePIDTinyPr; - Produces tablePIDTinyDe; - Produces tablePIDTinyTr; - Produces tablePIDTinyHe; - Produces tablePIDTinyAl; - Produces tableTuneOnData; - - // TPC PID Response - o2::pid::tpc::Response* response; - - // CCDB accessor - Service ccdb; - - // Network correction for TPC PID response - OnnxModel network; - std::map metadata; - std::map headers; - std::vector speciesNetworkFlags = std::vector(9); - std::string networkVersion; - - // Input parameters - Configurable paramfile{"param-file", "", "Path to the parametrization object, if empty the parametrization is not taken from file"}; - Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable ccdbPath{"ccdbPath", "Analysis/PID/TPC/Response", "Path of the TPC parametrization on the CCDB"}; - Configurable recoPass{"recoPass", "", "Reconstruction pass name for CCDB query (automatically takes latest object for timestamp if blank)"}; - Configurable ccdbTimestamp{"ccdb-timestamp", 0, "timestamp of the object used to query in CCDB the detector response. Exceptions: -1 gets the latest object, 0 gets the run dependent timestamp"}; - // Parameters for loading network from a file / downloading the file - Configurable useNetworkCorrection{"useNetworkCorrection", 0, "(bool) Wether or not to use the network correction for the TPC dE/dx signal"}; - Configurable autofetchNetworks{"autofetchNetworks", 1, "(bool) Automatically fetches networks from CCDB for the correct run number"}; - Configurable skipTPCOnly{"skipTPCOnly", false, "Flag to skip TPC only tracks (faster but affects the analyses that use TPC only tracks)"}; - Configurable networkPathLocally{"networkPathLocally", "network.onnx", "(std::string) Path to the local .onnx file. If autofetching is enabled, then this is where the files will be downloaded"}; - Configurable networkPathCCDB{"networkPathCCDB", "Analysis/PID/TPC/ML", "Path on CCDB"}; - Configurable enableNetworkOptimizations{"enableNetworkOptimizations", 1, "(bool) If the neural network correction is used, this enables GraphOptimizationLevel::ORT_ENABLE_EXTENDED in the ONNX session"}; - Configurable networkSetNumThreads{"networkSetNumThreads", 0, "Especially important for running on a SLURM cluster. Sets the number of threads used for execution."}; - // Configuration flags to include and exclude particle hypotheses - Configurable pidFullEl{"pid-full-el", -1, {"Produce PID information for the Electron mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullMu{"pid-full-mu", -1, {"Produce PID information for the Muon mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullPi{"pid-full-pi", -1, {"Produce PID information for the Pion mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullKa{"pid-full-ka", -1, {"Produce PID information for the Kaon mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullPr{"pid-full-pr", -1, {"Produce PID information for the Proton mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullDe{"pid-full-de", -1, {"Produce PID information for the Deuterons mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullTr{"pid-full-tr", -1, {"Produce PID information for the Triton mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullHe{"pid-full-he", -1, {"Produce PID information for the Helium3 mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidFullAl{"pid-full-al", -1, {"Produce PID information for the Alpha mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyEl{"pid-tiny-el", -1, {"Produce PID information for the Electron mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyMu{"pid-tiny-mu", -1, {"Produce PID information for the Muon mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyPi{"pid-tiny-pi", -1, {"Produce PID information for the Pion mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyKa{"pid-tiny-ka", -1, {"Produce PID information for the Kaon mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyPr{"pid-tiny-pr", -1, {"Produce PID information for the Proton mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyDe{"pid-tiny-de", -1, {"Produce PID information for the Deuterons mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyTr{"pid-tiny-tr", -1, {"Produce PID information for the Triton mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyHe{"pid-tiny-he", -1, {"Produce PID information for the Helium3 mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable pidTinyAl{"pid-tiny-al", -1, {"Produce PID information for the Alpha mass hypothesis, overrides the automatic setup: the corresponding table can be set off (0) or on (1)"}}; - Configurable enableTuneOnDataTable{"enableTuneOnDataTable", -1, {"Produce tuned dE/dx signal table for MC to be used as raw signal in other tasks (default -1, 'only if needed'"}}; - Configurable useNetworkEl{"useNetworkEl", 1, {"Switch for applying neural network on the electron mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkMu{"useNetworkMu", 1, {"Switch for applying neural network on the muon mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkPi{"useNetworkPi", 1, {"Switch for applying neural network on the pion mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkKa{"useNetworkKa", 1, {"Switch for applying neural network on the kaon mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkPr{"useNetworkPr", 1, {"Switch for applying neural network on the proton mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkDe{"useNetworkDe", 1, {"Switch for applying neural network on the deuteron mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkTr{"useNetworkTr", 1, {"Switch for applying neural network on the triton mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkHe{"useNetworkHe", 1, {"Switch for applying neural network on the helium3 mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable useNetworkAl{"useNetworkAl", 1, {"Switch for applying neural network on the alpha mass hypothesis (if network enabled) (set to 0 to disable)"}}; - Configurable networkBetaGammaCutoff{"networkBetaGammaCutoff", 0.45, {"Lower value of beta-gamma to override the NN application"}}; - Configurable networkInputBatchedMode{"networkInputBatchedMode", -1, {"-1: Takes all tracks, >0: Takes networkInputBatchedMode number of tracks at once"}}; - Configurable irSource{"irSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; - ctpRateFetcher mRateFetcher; - // Parametrization configuration - bool useCCDBParam = false; - std::vector track_properties; - - void init(o2::framework::InitContext& initContext) - { - // Protection for process flags - if (!((doprocessStandard && !doprocessStandard2 && !doprocessMcTuneOnData) || - (!doprocessStandard && doprocessStandard2 && !doprocessMcTuneOnData) || - (!doprocessStandard && !doprocessStandard2 && doprocessMcTuneOnData))) { - LOG(fatal) << "pid-tpc must have only one of the options 'processStandard', 'processStandard2', 'processMcTuneOnData' enabled. Please check your configuration."; - } - response = new o2::pid::tpc::Response(); - // Checking the tables are requested in the workflow and enabling them - auto enableFlag = [&](const std::string& particle, Configurable& flag) { - o2::common::core::enableFlagIfTableRequired(initContext, "pidTPC" + particle, flag); - }; - enableFlag("FullEl", pidFullEl); - enableFlag("FullMu", pidFullMu); - enableFlag("FullPi", pidFullPi); - enableFlag("FullKa", pidFullKa); - enableFlag("FullPr", pidFullPr); - enableFlag("FullDe", pidFullDe); - enableFlag("FullTr", pidFullTr); - enableFlag("FullHe", pidFullHe); - enableFlag("FullAl", pidFullAl); - - enableFlag("El", pidTinyEl); - enableFlag("Mu", pidTinyMu); - enableFlag("Pi", pidTinyPi); - enableFlag("Ka", pidTinyKa); - enableFlag("Pr", pidTinyPr); - enableFlag("De", pidTinyDe); - enableFlag("Tr", pidTinyTr); - enableFlag("He", pidTinyHe); - enableFlag("Al", pidTinyAl); - - if (doprocessMcTuneOnData) { - o2::common::core::enableFlagIfTableRequired(initContext, "mcTPCTuneOnData", enableTuneOnDataTable); - } - - speciesNetworkFlags[0] = useNetworkEl; - speciesNetworkFlags[1] = useNetworkMu; - speciesNetworkFlags[2] = useNetworkPi; - speciesNetworkFlags[3] = useNetworkKa; - speciesNetworkFlags[4] = useNetworkPr; - speciesNetworkFlags[5] = useNetworkDe; - speciesNetworkFlags[6] = useNetworkTr; - speciesNetworkFlags[7] = useNetworkHe; - speciesNetworkFlags[8] = useNetworkAl; - - // Initialise metadata object for CCDB calls from AO2D metadata - if (recoPass.value == "") { - if (metadataInfo.isFullyDefined()) { - metadata["RecoPassName"] = metadataInfo.get("RecoPassName"); - LOGP(info, "Automatically setting reco pass for TPC Response to {} from AO2D", metadata["RecoPassName"]); - } - } else { - LOGP(info, "Setting reco pass for TPC response to user-defined name {}", recoPass.value); - metadata["RecoPassName"] = recoPass.value; - } - - /// TPC PID Response - const TString fname = paramfile.value; - if (fname != "") { // Loading the parametrization from file - LOGP(info, "Loading TPC response from file {}", fname.Data()); - try { - std::unique_ptr f(TFile::Open(fname, "READ")); - f->GetObject("Response", response); - } catch (...) { - LOGF(fatal, "Loading the TPC PID Response from file {} failed!", fname.Data()); - } - response->PrintAll(); - } else { - useCCDBParam = true; - const std::string path = ccdbPath.value; - const auto time = ccdbTimestamp.value; - ccdb->setURL(url.value); - ccdb->setFatalWhenNull(false); // manual fallback in case ccdb entry empty - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); - if (time != 0) { - LOGP(info, "Initialising TPC PID response for fixed timestamp {} and reco pass {}:", time, recoPass.value); - ccdb->setTimestamp(time); - response = ccdb->getSpecific(path, time, metadata, &headers); - if (!response) { - LOGF(warning, "Unable to find TPC parametrisation for specified pass name - falling back to latest object"); - response = ccdb->getForTimeStamp(path, time, &headers); - if (!response) { - LOGF(fatal, "Unable to find any TPC object corresponding to timestamp {}!", time); - } - } - networkVersion = headers["NN-Version"]; - LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << time << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"]; - metadata["RecoPassName"] = headers["RecoPassName"]; // Force pass number for NN request to match retrieved BB - response->PrintAll(); - } - } - - /// Neural network init for TPC PID - - if (!useNetworkCorrection) { - return; - } else { - /// CCDB and auto-fetching - - if (!autofetchNetworks) { - if (ccdbTimestamp > 0) { - /// Fetching network for specific timestamp - LOG(info) << "Fetching network for timestamp: " << ccdbTimestamp.value; - bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(networkPathCCDB.value, ".", metadata, ccdbTimestamp.value, false, networkPathLocally.value, "", "", &headers); - networkVersion = headers["NN-Version"]; - if (retrieveSuccess) { - network.initModel(networkPathLocally.value, enableNetworkOptimizations.value, networkSetNumThreads.value, strtoul(headers["Valid-From"].c_str(), NULL, 0), strtoul(headers["Valid-Until"].c_str(), NULL, 0)); - std::vector dummyInput(network.getNumInputNodes(), 1.); - network.evalModel(dummyInput); /// Init the model evaluations - LOGP(info, "Retrieved NN corrections for production tag {}, pass number {}, and NN-Version {}", headers["LPMProductionTag"], headers["RecoPassName"], headers["NN-Version"]); - } else { - LOG(fatal) << "No valid NN object found matching retrieved Bethe-Bloch parametrisation for pass " << metadata["RecoPassName"] << ". Please ensure that the requested pass has dedicated NN corrections available"; - } - } else { - /// Taking the network from local file - if (networkPathLocally.value == "") { - LOG(fatal) << "Local path must be set (flag networkPathLocally)! Aborting..."; - } - LOG(info) << "Using local file [" << networkPathLocally.value << "] for the TPC PID response correction."; - network.initModel(networkPathLocally.value, enableNetworkOptimizations.value, networkSetNumThreads.value); - std::vector dummyInput(network.getNumInputNodes(), 1.); - network.evalModel(dummyInput); // This is an initialisation and might reduce the overhead of the model - } - } else { - return; - } - } - } - - Partition notTPCStandaloneTracks = (aod::track::tpcNClsFindable > static_cast(0)) && ((aod::track::itsClusterSizes > static_cast(0)) || (aod::track::trdPattern > static_cast(0)) || (aod::track::tofExpMom > 0.f && aod::track::tofChi2 > 0.f)); // To count number of tracks for use in NN array - Partition tracksWithTPC = (aod::track::tpcNClsFindable > (uint8_t)0); - - template - std::vector createNetworkPrediction(C const& collisions, T const& tracks, B const& bcs, const size_t size) - { - - auto start_network_total = std::chrono::high_resolution_clock::now(); - if (autofetchNetworks) { - const auto& bc = bcs.begin(); - // Initialise correct TPC response object before NN setup (for NCl normalisation) - if (useCCDBParam && ccdbTimestamp.value == 0 && !ccdb->isCachedObjectValid(ccdbPath.value, bc.timestamp())) { // Updating parametrisation only if the initial timestamp is 0 - if (recoPass.value == "") { - LOGP(info, "Retrieving latest TPC response object for timestamp {}:", bc.timestamp()); - } else { - LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value); - } - response = ccdb->getSpecific(ccdbPath.value, bc.timestamp(), metadata, &headers); - if (!response) { - LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]); - response = ccdb->getForTimeStamp(ccdbPath.value, bc.timestamp(), &headers); - if (!response) { - LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp()); - } - } - networkVersion = headers["NN-Version"]; - LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << bc.timestamp() << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"]; - metadata["RecoPassName"] = headers["RecoPassName"]; // Force pass number for NN request to match retrieved BB - response->PrintAll(); - } - - if (bc.timestamp() < network.getValidityFrom() || bc.timestamp() > network.getValidityUntil()) { // fetches network only if the runnumbers change - LOG(info) << "Fetching network for timestamp: " << bc.timestamp(); - bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(networkPathCCDB.value, ".", metadata, bc.timestamp(), false, networkPathLocally.value, "", "", &headers); - networkVersion = headers["NN-Version"]; - if (retrieveSuccess) { - network.initModel(networkPathLocally.value, enableNetworkOptimizations.value, networkSetNumThreads.value, strtoul(headers["Valid-From"].c_str(), NULL, 0), strtoul(headers["Valid-Until"].c_str(), NULL, 0)); - std::vector dummyInput(network.getNumInputNodes(), 1.); - network.evalModel(dummyInput); - LOGP(info, "Retrieved NN corrections for production tag {}, pass number {}, NN-Version number {}", headers["LPMProductionTag"], headers["RecoPassName"], headers["NN-Version"]); - } else { - LOG(fatal) << "No valid NN object found matching retrieved Bethe-Bloch parametrisation for pass " << metadata["RecoPassName"] << ". Please ensure that the requested pass has dedicated NN corrections available"; - } - } - } - - // Defining some network parameters - int input_dimensions = network.getNumInputNodes(); - int output_dimensions = network.getNumOutputNodes(); - const uint64_t prediction_size = output_dimensions * size; - const uint8_t numSpecies = 9; - const uint64_t total_eval_size = size * numSpecies; // 9 species - - const float nNclNormalization = response->GetNClNormalization(); - float duration_network = 0; - - uint64_t counter_track_props = 0, exec_counter = 0, in_batch_counter = 0, total_input_count = 0; - uint64_t track_prop_size = networkInputBatchedMode.value; - if (networkInputBatchedMode.value <= 0) { - track_prop_size = size; // If the networkInputBatchedMode is not set, we use all tracks at once - } - track_properties.resize(track_prop_size * input_dimensions); // If the networkInputBatchedMode is set, we use the number of tracks specified in the config - std::vector network_prediction(prediction_size * numSpecies); // For each mass hypotheses - - // Filling a std::vector to be evaluated by the network - // Evaluation on single tracks brings huge overhead: Thus evaluation is done on one large vector - for (int species = 0; species < numSpecies; species++) { // Loop over particle number for which network correction is used - for (auto const& trk : tracks) { - if (!trk.hasTPC()) { - continue; - } - if (skipTPCOnly) { - if (!trk.hasITS() && !trk.hasTRD() && !trk.hasTOF()) { - continue; - } - } - - if ((in_batch_counter == track_prop_size) || (total_input_count == total_eval_size)) { // If the batch size is reached, reset the counter - int32_t fill_shift = (exec_counter * track_prop_size - ((total_input_count == total_eval_size) ? (total_input_count % track_prop_size) : 0)) * output_dimensions; - auto start_network_eval = std::chrono::high_resolution_clock::now(); - const float* output_network = network.evalModel(track_properties); - auto stop_network_eval = std::chrono::high_resolution_clock::now(); - duration_network += std::chrono::duration>(stop_network_eval - start_network_eval).count(); - - for (uint64_t i = 0; i < (in_batch_counter * output_dimensions); i += output_dimensions) { - for (int j = 0; j < output_dimensions; j++) { - network_prediction[i + j + fill_shift] = output_network[i + j]; - } - } - counter_track_props = 0; - in_batch_counter = 0; - exec_counter++; - } - - // LOG(info) << "counter_tracks_props: " << counter_track_props << "; in_batch_counter: " << in_batch_counter << "; total_input_count: " << total_input_count << "; exec_counter: " << exec_counter << "; track_prop_size: " << track_prop_size << "; size: " << size << "; track_properties.size(): " << track_properties.size(); - track_properties[counter_track_props] = trk.tpcInnerParam(); // (tracks.asArrowTable()->GetColumn("tpcInnerParam")).GetData(); - track_properties[counter_track_props + 1] = trk.tgl(); - track_properties[counter_track_props + 2] = trk.signed1Pt(); - track_properties[counter_track_props + 3] = o2::track::pid_constants::sMasses[species]; - track_properties[counter_track_props + 4] = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).multTPC() / 11000. : 1.; - track_properties[counter_track_props + 5] = std::sqrt(nNclNormalization / trk.tpcNClsFound()); - if (input_dimensions == 7 && networkVersion == "2") { - track_properties[counter_track_props + 6] = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; - } - if (input_dimensions == 8 && networkVersion == "3") { - track_properties[counter_track_props + 6] = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; - if (trk.has_collision()) { - auto trk_bc = (collisions.iteratorAt(trk.collisionId())).template bc_as(); - float hadronicRate = mRateFetcher.fetch(ccdb.service, trk_bc.timestamp(), trk_bc.runNumber(), irSource) * 1.e-3; - track_properties[counter_track_props + 7] = hadronicRate / 50.; - } else { - track_properties[counter_track_props + 7] = 1; - } - } - - counter_track_props += input_dimensions; - in_batch_counter++; - total_input_count++; - } - } - track_properties.clear(); - - auto stop_network_total = std::chrono::high_resolution_clock::now(); - LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval ONNX): " << duration_network / (size * 9) << "ns ; Total time (eval ONNX): " << duration_network / 1000000000 << " s"; - LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval + overhead): " << std::chrono::duration>(stop_network_total - start_network_total).count() / (size * 9) << "ns ; Total time (eval + overhead): " << std::chrono::duration>(stop_network_total - start_network_total).count() / 1000000000 << " s"; - - return network_prediction; - } - - template - void makePidTables(const int flagFull, NSF& tableFull, const int flagTiny, NST& tableTiny, const o2::track::PID::ID pid, const float tpcSignal, const T& trk, const C& collisions, const std::vector& network_prediction, const int& count_tracks, const int& tracksForNet_size) - { - if (flagFull != 1 && flagTiny != 1) { - return; - } - if (!trk.hasTPC() || tpcSignal < 0.f) { - if (flagFull) - tableFull(-999.f, -999.f); - if (flagTiny) - tableTiny(aod::pidtpc_tiny::binning::underflowBin); - return; - } - if (skipTPCOnly) { - if (!trk.hasITS() && !trk.hasTRD() && !trk.hasTOF()) { - if (flagFull) - tableFull(-999.f, -999.f); - if (flagTiny) - tableTiny(aod::pidtpc_tiny::binning::underflowBin); - return; - } - } - auto expSignal = response->GetExpectedSignal(trk, pid); - auto expSigma = trk.has_collision() ? response->GetExpectedSigma(collisions.iteratorAt(trk.collisionId()), trk, pid) : 0.07f * expSignal; // use default sigma value of 7% if no collision information to estimate resolution - if (expSignal < 0. || expSigma < 0.) { // skip if expected signal invalid - if (flagFull) - tableFull(-999.f, -999.f); - if (flagTiny) - tableTiny(aod::pidtpc_tiny::binning::underflowBin); - return; - } - - float nSigma = -999.f; - int multTPC = 0; - if (trk.has_collision()) { - auto collision = collisions.rawIteratorAt(trk.collisionId()); - multTPC = collision.multTPC(); - } - float bg = trk.tpcInnerParam() / o2::track::pid_constants::sMasses[pid]; // estimated beta-gamma for network cutoff - if (useNetworkCorrection && speciesNetworkFlags[pid] && trk.has_collision() && bg > networkBetaGammaCutoff) { - - // Here comes the application of the network. The output--dimensions of the network determine the application: 1: mean, 2: sigma, 3: sigma asymmetric - // For now only the option 2: sigma will be used. The other options are kept if there would be demand later on - if (network.getNumOutputNodes() == 1) { // Expected mean correction; no sigma correction - nSigma = (tpcSignal - network_prediction[count_tracks + tracksForNet_size * pid] * expSignal) / expSigma; - } else if (network.getNumOutputNodes() == 2) { // Symmetric sigma correction - expSigma = (network_prediction[2 * (count_tracks + tracksForNet_size * pid) + 1] - network_prediction[2 * (count_tracks + tracksForNet_size * pid)]) * expSignal; - nSigma = (tpcSignal / expSignal - network_prediction[2 * (count_tracks + tracksForNet_size * pid)]) / (network_prediction[2 * (count_tracks + tracksForNet_size * pid) + 1] - network_prediction[2 * (count_tracks + tracksForNet_size * pid)]); - } else if (network.getNumOutputNodes() == 3) { // Asymmetric sigma corection - if (tpcSignal / expSignal >= network_prediction[3 * (count_tracks + tracksForNet_size * pid)]) { - expSigma = (network_prediction[3 * (count_tracks + tracksForNet_size * pid) + 1] - network_prediction[3 * (count_tracks + tracksForNet_size * pid)]) * expSignal; - nSigma = (tpcSignal / expSignal - network_prediction[3 * (count_tracks + tracksForNet_size * pid)]) / (network_prediction[3 * (count_tracks + tracksForNet_size * pid) + 1] - network_prediction[3 * (count_tracks + tracksForNet_size * pid)]); - } else { - expSigma = (network_prediction[3 * (count_tracks + tracksForNet_size * pid)] - network_prediction[3 * (count_tracks + tracksForNet_size * pid) + 2]) * expSignal; - nSigma = (tpcSignal / expSignal - network_prediction[3 * (count_tracks + tracksForNet_size * pid)]) / (network_prediction[3 * (count_tracks + tracksForNet_size * pid)] - network_prediction[3 * (count_tracks + tracksForNet_size * pid) + 2]); - } - } else { - LOGF(fatal, "Network output-dimensions incompatible!"); - } - } else { - nSigma = response->GetNumberOfSigmaMCTunedAtMultiplicity(multTPC, trk, pid, tpcSignal); - } - if (flagFull) - tableFull(expSigma, nSigma); - if (flagTiny) - aod::pidtpc_tiny::binning::packInTable(nSigma, tableTiny); - }; - - void processStandard(Coll const& collisions, Trks const& tracks, aod::BCsWithTimestamps const& bcs) - { - - const uint64_t outTable_size = tracks.size(); - - auto reserveTable = [&outTable_size](const Configurable& flag, auto& table) { - if (flag.value != 1) { - return; - } - table.reserve(outTable_size); - }; - - // Prepare memory for enabled tables - reserveTable(pidFullEl, tablePIDFullEl); - reserveTable(pidFullMu, tablePIDFullMu); - reserveTable(pidFullPi, tablePIDFullPi); - reserveTable(pidFullKa, tablePIDFullKa); - reserveTable(pidFullPr, tablePIDFullPr); - reserveTable(pidFullDe, tablePIDFullDe); - reserveTable(pidFullTr, tablePIDFullTr); - reserveTable(pidFullHe, tablePIDFullHe); - reserveTable(pidFullAl, tablePIDFullAl); - - reserveTable(pidTinyEl, tablePIDTinyEl); - reserveTable(pidTinyMu, tablePIDTinyMu); - reserveTable(pidTinyPi, tablePIDTinyPi); - reserveTable(pidTinyKa, tablePIDTinyKa); - reserveTable(pidTinyPr, tablePIDTinyPr); - reserveTable(pidTinyDe, tablePIDTinyDe); - reserveTable(pidTinyTr, tablePIDTinyTr); - reserveTable(pidTinyHe, tablePIDTinyHe); - reserveTable(pidTinyAl, tablePIDTinyAl); - - const uint64_t tracksForNet_size = (skipTPCOnly) ? notTPCStandaloneTracks.size() : tracksWithTPC.size(); - std::vector network_prediction; - - if (useNetworkCorrection) { - network_prediction = createNetworkPrediction(collisions, tracks, bcs, tracksForNet_size); - } - - uint64_t count_tracks = 0; - - for (auto const& trk : tracks) { - // Loop on Tracks - - const auto& bc = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).bc_as() : bcs.begin(); - if (useCCDBParam && ccdbTimestamp.value == 0 && !ccdb->isCachedObjectValid(ccdbPath.value, bc.timestamp())) { // Updating parametrisation only if the initial timestamp is 0 - if (recoPass.value == "") { - LOGP(info, "Retrieving latest TPC response object for timestamp {}:", bc.timestamp()); - } else { - LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value); - } - response = ccdb->getSpecific(ccdbPath.value, bc.timestamp(), metadata, &headers); - if (!response) { - LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]); - response = ccdb->getForTimeStamp(ccdbPath.value, bc.timestamp(), &headers); - if (!response) { - LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp()); - } - } - LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << bc.timestamp() << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"]; - response->PrintAll(); - } - - auto makePidTablesDefault = [&trk, &collisions, &network_prediction, &count_tracks, &tracksForNet_size, this](const int flagFull, auto& tableFull, const int flagTiny, auto& tableTiny, const o2::track::PID::ID pid) { - makePidTables(flagFull, tableFull, flagTiny, tableTiny, pid, trk.tpcSignal(), trk, collisions, network_prediction, count_tracks, tracksForNet_size); - }; - - makePidTablesDefault(pidFullEl, tablePIDFullEl, pidTinyEl, tablePIDTinyEl, o2::track::PID::Electron); - makePidTablesDefault(pidFullMu, tablePIDFullMu, pidTinyMu, tablePIDTinyMu, o2::track::PID::Muon); - makePidTablesDefault(pidFullPi, tablePIDFullPi, pidTinyPi, tablePIDTinyPi, o2::track::PID::Pion); - makePidTablesDefault(pidFullKa, tablePIDFullKa, pidTinyKa, tablePIDTinyKa, o2::track::PID::Kaon); - makePidTablesDefault(pidFullPr, tablePIDFullPr, pidTinyPr, tablePIDTinyPr, o2::track::PID::Proton); - makePidTablesDefault(pidFullDe, tablePIDFullDe, pidTinyDe, tablePIDTinyDe, o2::track::PID::Deuteron); - makePidTablesDefault(pidFullTr, tablePIDFullTr, pidTinyTr, tablePIDTinyTr, o2::track::PID::Triton); - makePidTablesDefault(pidFullHe, tablePIDFullHe, pidTinyHe, tablePIDTinyHe, o2::track::PID::Helium3); - makePidTablesDefault(pidFullAl, tablePIDFullAl, pidTinyAl, tablePIDTinyAl, o2::track::PID::Alpha); - - if (trk.hasTPC() && (!skipTPCOnly || trk.hasITS() || trk.hasTRD() || trk.hasTOF())) { - count_tracks++; // Increment network track counter only if track has TPC, and (not skipping TPConly) or (is not TPConly) - } - } - } - - PROCESS_SWITCH(tpcPid, processStandard, "Creating PID tables without MC TuneOnData", true); - - Partition mcnotTPCStandaloneTracks = (aod::track::tpcNClsFindable > static_cast(0)) && ((aod::track::itsClusterSizes > static_cast(0)) || (aod::track::trdPattern > static_cast(0)) || (aod::track::tofExpMom > 0.f && aod::track::tofChi2 > 0.f)); // To count number of tracks for use in NN array - Partition mctracksWithTPC = (aod::track::tpcNClsFindable > (uint8_t)0); - - void processStandard2(Coll const& collisions, Trks const& tracks, aod::DEdxsCorrected const& dedxscorrected, aod::BCsWithTimestamps const& bcs) - { - const uint64_t outTable_size = tracks.size(); - const uint64_t dedxscorrected_size = dedxscorrected.size(); - - if (dedxscorrected_size != outTable_size) { - LOG(fatal) << "Size of dEdx corrected table does not match size of tracks! dEdx size: " << dedxscorrected_size << ", tracks size: " << outTable_size; - } - - auto reserveTable = [&outTable_size](const Configurable& flag, auto& table) { - if (flag.value != 1) { - return; - } - table.reserve(outTable_size); - }; - - // Prepare memory for enabled tables - reserveTable(pidFullEl, tablePIDFullEl); - reserveTable(pidFullMu, tablePIDFullMu); - reserveTable(pidFullPi, tablePIDFullPi); - reserveTable(pidFullKa, tablePIDFullKa); - reserveTable(pidFullPr, tablePIDFullPr); - reserveTable(pidFullDe, tablePIDFullDe); - reserveTable(pidFullTr, tablePIDFullTr); - reserveTable(pidFullHe, tablePIDFullHe); - reserveTable(pidFullAl, tablePIDFullAl); - - reserveTable(pidTinyEl, tablePIDTinyEl); - reserveTable(pidTinyMu, tablePIDTinyMu); - reserveTable(pidTinyPi, tablePIDTinyPi); - reserveTable(pidTinyKa, tablePIDTinyKa); - reserveTable(pidTinyPr, tablePIDTinyPr); - reserveTable(pidTinyDe, tablePIDTinyDe); - reserveTable(pidTinyTr, tablePIDTinyTr); - reserveTable(pidTinyHe, tablePIDTinyHe); - reserveTable(pidTinyAl, tablePIDTinyAl); - - const uint64_t tracksForNet_size = (skipTPCOnly) ? notTPCStandaloneTracks.size() : tracksWithTPC.size(); - std::vector network_prediction; - - if (useNetworkCorrection) { - network_prediction = createNetworkPrediction(collisions, tracks, bcs, tracksForNet_size); - } - - uint64_t count_tracks = 0; - uint64_t count_tracks2 = 0; - - for (auto const& trk : tracks) { - // Loop on Tracks - - const auto& bc = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).bc_as() : bcs.begin(); - auto dedx_corr = dedxscorrected.iteratorAt(count_tracks2); - count_tracks2++; - if (useCCDBParam && ccdbTimestamp.value == 0 && !ccdb->isCachedObjectValid(ccdbPath.value, bc.timestamp())) { // Updating parametrisation only if the initial timestamp is 0 - if (recoPass.value == "") { - LOGP(info, "Retrieving latest TPC response object for timestamp {}:", bc.timestamp()); - } else { - LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value); - } - response = ccdb->getSpecific(ccdbPath.value, bc.timestamp(), metadata, &headers); - - if (!response) { - LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]); - response = ccdb->getForTimeStamp(ccdbPath.value, bc.timestamp(), &headers); - if (!response) { - LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp()); - } - } - LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << bc.timestamp() << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"]; - response->PrintAll(); - } - auto makePidTablesDefault = [&trk, &dedx_corr, &collisions, &network_prediction, &count_tracks, &tracksForNet_size, this](const int flagFull, auto& tableFull, const int flagTiny, auto& tableTiny, const o2::track::PID::ID pid) { - makePidTables(flagFull, tableFull, flagTiny, tableTiny, pid, dedx_corr.tpcSignalCorrected(), trk, collisions, network_prediction, count_tracks, tracksForNet_size); - }; - - makePidTablesDefault(pidFullEl, tablePIDFullEl, pidTinyEl, tablePIDTinyEl, o2::track::PID::Electron); - makePidTablesDefault(pidFullMu, tablePIDFullMu, pidTinyMu, tablePIDTinyMu, o2::track::PID::Muon); - makePidTablesDefault(pidFullPi, tablePIDFullPi, pidTinyPi, tablePIDTinyPi, o2::track::PID::Pion); - makePidTablesDefault(pidFullKa, tablePIDFullKa, pidTinyKa, tablePIDTinyKa, o2::track::PID::Kaon); - makePidTablesDefault(pidFullPr, tablePIDFullPr, pidTinyPr, tablePIDTinyPr, o2::track::PID::Proton); - makePidTablesDefault(pidFullDe, tablePIDFullDe, pidTinyDe, tablePIDTinyDe, o2::track::PID::Deuteron); - makePidTablesDefault(pidFullTr, tablePIDFullTr, pidTinyTr, tablePIDTinyTr, o2::track::PID::Triton); - makePidTablesDefault(pidFullHe, tablePIDFullHe, pidTinyHe, tablePIDTinyHe, o2::track::PID::Helium3); - makePidTablesDefault(pidFullAl, tablePIDFullAl, pidTinyAl, tablePIDTinyAl, o2::track::PID::Alpha); - - if (trk.hasTPC() && (!skipTPCOnly || trk.hasITS() || trk.hasTRD() || trk.hasTOF())) { - count_tracks++; // Increment network track counter only if track has TPC, and (not skipping TPConly) or (is not TPConly) - } - } - } - PROCESS_SWITCH(tpcPid, processStandard2, "Creating PID tables with Corrected dEdx", false); - void processMcTuneOnData(CollMC const& collisionsMc, TrksMC const& tracksMc, aod::BCsWithTimestamps const& bcs, aod::McParticles const&) - { - gRandom->SetSeed(0); // Ensure unique seed from UUID for each process call - const uint64_t outTable_size = tracksMc.size(); - - auto reserveTable = [&outTable_size](const Configurable& flag, auto& table) { - if (flag.value != 1) { - return; - } - table.reserve(outTable_size); - }; - - // Prepare memory for enabled tables - reserveTable(pidFullEl, tablePIDFullEl); - reserveTable(pidFullMu, tablePIDFullMu); - reserveTable(pidFullPi, tablePIDFullPi); - reserveTable(pidFullKa, tablePIDFullKa); - reserveTable(pidFullPr, tablePIDFullPr); - reserveTable(pidFullDe, tablePIDFullDe); - reserveTable(pidFullTr, tablePIDFullTr); - reserveTable(pidFullHe, tablePIDFullHe); - reserveTable(pidFullAl, tablePIDFullAl); - - reserveTable(pidTinyEl, tablePIDTinyEl); - reserveTable(pidTinyMu, tablePIDTinyMu); - reserveTable(pidTinyPi, tablePIDTinyPi); - reserveTable(pidTinyKa, tablePIDTinyKa); - reserveTable(pidTinyPr, tablePIDTinyPr); - reserveTable(pidTinyDe, tablePIDTinyDe); - reserveTable(pidTinyTr, tablePIDTinyTr); - reserveTable(pidTinyHe, tablePIDTinyHe); - reserveTable(pidTinyAl, tablePIDTinyAl); - - reserveTable(enableTuneOnDataTable, tableTuneOnData); // Only produce the table of tuned dE/dx if the signal is requested by another task - - const uint64_t tracksForNet_size = (skipTPCOnly) ? mcnotTPCStandaloneTracks.size() : mctracksWithTPC.size(); - std::vector network_prediction; - - if (useNetworkCorrection) { - network_prediction = createNetworkPrediction(collisionsMc, tracksMc, bcs, tracksForNet_size); - } - - uint64_t count_tracks = 0; - - for (auto const& trk : tracksMc) { - // Loop on Tracks - const auto& bc = trk.has_collision() ? collisionsMc.iteratorAt(trk.collisionId()).bc_as() : bcs.begin(); - if (useCCDBParam && ccdbTimestamp.value == 0 && !ccdb->isCachedObjectValid(ccdbPath.value, bc.timestamp())) { // Updating parametrisation only if the initial timestamp is 0 - if (recoPass.value == "") { - LOGP(info, "Retrieving latest TPC response object for timestamp {}:", bc.timestamp()); - } else { - LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value); - } - response = ccdb->getSpecific(ccdbPath.value, bc.timestamp(), metadata, &headers); - if (!response) { - LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]); - response = ccdb->getForTimeStamp(ccdbPath.value, bc.timestamp(), &headers); - if (!response) { - LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp()); - } - } - response->PrintAll(); - } - - // Perform TuneOnData sampling for MC dE/dx - float mcTunedTPCSignal = 0.; - if (!trk.hasTPC()) { - mcTunedTPCSignal = -999.f; - } else { - if (skipTPCOnly) { - if (!trk.hasITS() && !trk.hasTRD() && !trk.hasTOF()) { - mcTunedTPCSignal = -999.f; - } - } - int pid = getPIDIndex(trk.mcParticle().pdgCode()); - - auto expSignal = response->GetExpectedSignal(trk, pid); - auto expSigma = response->GetExpectedSigma(collisionsMc.iteratorAt(trk.collisionId()), trk, pid); - if (expSignal < 0. || expSigma < 0.) { // if expectation invalid then give undefined signal - mcTunedTPCSignal = -999.f; - } - float bg = trk.tpcInnerParam() / o2::track::pid_constants::sMasses[pid]; // estimated beta-gamma for network cutoff - - if (useNetworkCorrection && speciesNetworkFlags[pid] && trk.has_collision() && bg > networkBetaGammaCutoff) { - auto mean = network_prediction[2 * (count_tracks + tracksForNet_size * pid)] * expSignal; // Absolute mean, i.e. the mean dE/dx value of the data in that slice, not the mean of the NSigma distribution - auto sigma = (network_prediction[2 * (count_tracks + tracksForNet_size * pid) + 1] - network_prediction[2 * (count_tracks + tracksForNet_size * pid)]) * expSignal; - if (mean < 0.f || sigma < 0.f) { - mcTunedTPCSignal = -999.f; - } else { - mcTunedTPCSignal = gRandom->Gaus(mean, sigma); - } - } else { - mcTunedTPCSignal = gRandom->Gaus(expSignal, expSigma); - } - } - if (enableTuneOnDataTable) - tableTuneOnData(mcTunedTPCSignal); - - // Check and fill enabled nsigma tables - - auto makePidTablesMCTune = [&trk, &collisionsMc, &network_prediction, &count_tracks, &tracksForNet_size, &mcTunedTPCSignal, this](const int flagFull, auto& tableFull, const int flagTiny, auto& tableTiny, const o2::track::PID::ID pid) { - makePidTables(flagFull, tableFull, flagTiny, tableTiny, pid, mcTunedTPCSignal, trk, collisionsMc, network_prediction, count_tracks, tracksForNet_size); - }; - - makePidTablesMCTune(pidFullEl, tablePIDFullEl, pidTinyEl, tablePIDTinyEl, o2::track::PID::Electron); - makePidTablesMCTune(pidFullMu, tablePIDFullMu, pidTinyMu, tablePIDTinyMu, o2::track::PID::Muon); - makePidTablesMCTune(pidFullPi, tablePIDFullPi, pidTinyPi, tablePIDTinyPi, o2::track::PID::Pion); - makePidTablesMCTune(pidFullKa, tablePIDFullKa, pidTinyKa, tablePIDTinyKa, o2::track::PID::Kaon); - makePidTablesMCTune(pidFullPr, tablePIDFullPr, pidTinyPr, tablePIDTinyPr, o2::track::PID::Proton); - makePidTablesMCTune(pidFullDe, tablePIDFullDe, pidTinyDe, tablePIDTinyDe, o2::track::PID::Deuteron); - makePidTablesMCTune(pidFullTr, tablePIDFullTr, pidTinyTr, tablePIDTinyTr, o2::track::PID::Triton); - makePidTablesMCTune(pidFullHe, tablePIDFullHe, pidTinyHe, tablePIDTinyHe, o2::track::PID::Helium3); - makePidTablesMCTune(pidFullAl, tablePIDFullAl, pidTinyAl, tablePIDTinyAl, o2::track::PID::Alpha); - - if (trk.hasTPC() && (!skipTPCOnly || trk.hasITS() || trk.hasTRD() || trk.hasTOF())) { - count_tracks++; // Increment network track counter only if track has TPC, and (not skipping TPConly) or (is not TPConly) - } - } - } - - PROCESS_SWITCH(tpcPid, processMcTuneOnData, "Creating PID tables with MC TuneOnData", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - metadataInfo.initMetadata(cfgc); // Parse AO2D metadata - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -} diff --git a/Common/TableProducer/PID/pidTPCBase.cxx b/Common/TableProducer/PID/pidTPCBase.cxx index 1a86f0f70ec..422a6d7c435 100644 --- a/Common/TableProducer/PID/pidTPCBase.cxx +++ b/Common/TableProducer/PID/pidTPCBase.cxx @@ -61,7 +61,7 @@ struct PidMultiplicity { } using TrksIU = soa::Join; - Partition tracksWithTPCIU = (aod::track::tpcNClsFindable > (uint8_t)0); + Partition tracksWithTPCIU = (aod::track::tpcNClsFindable > static_cast(0)); void processIU(aod::Collision const& collision, TrksIU const&) { if (!enableTable) { @@ -73,7 +73,7 @@ struct PidMultiplicity { PROCESS_SWITCH(PidMultiplicity, processIU, "Process with IU tracks, faster but works on Run3 only", false); using Trks = soa::Join; - Partition tracksWithTPC = (aod::track::tpcNClsFindable > (uint8_t)0); + Partition tracksWithTPC = (aod::track::tpcNClsFindable > static_cast(0)); void processStandard(aod::Collision const& collision, Trks const&) { if (!enableTable) { diff --git a/Common/TableProducer/centralityTable.cxx b/Common/TableProducer/centralityTable.cxx deleted file mode 100644 index 26fc9dab09a..00000000000 --- a/Common/TableProducer/centralityTable.cxx +++ /dev/null @@ -1,795 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -/// \file centralityTable.cxx -/// \brief Task to produce the centrality tables associated to each of the required centrality estimators -/// -/// \author ALICE -// - -#include "Common/Core/MetadataHelper.h" -#include "Common/Core/TableHelper.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::framework; - -o2::common::core::MetadataHelper metadataInfo; // Metadata helper - -static constexpr int kCentRun2V0Ms = 0; -static constexpr int kCentRun2V0As = 1; -static constexpr int kCentRun2SPDTrks = 2; -static constexpr int kCentRun2SPDClss = 3; -static constexpr int kCentRun2CL0s = 4; -static constexpr int kCentRun2CL1s = 5; -static constexpr int kCentFV0As = 6; -static constexpr int kCentFT0Ms = 7; -static constexpr int kCentFT0As = 8; -static constexpr int kCentFT0Cs = 9; -static constexpr int kCentFT0CVariant1s = 10; -static constexpr int kCentFDDMs = 11; -static constexpr int kCentNTPVs = 12; -static constexpr int kCentNGlobals = 13; -static constexpr int kCentMFTs = 14; -static constexpr int NTables = 15; -static constexpr int NParameters = 1; -static const std::vector tableNames{"CentRun2V0Ms", - "CentRun2V0As", - "CentRun2SPDTrks", - "CentRun2SPDClss", - "CentRun2CL0s", - "CentRun2CL1s", - "CentFV0As", - "CentFT0Ms", - "CentFT0As", - "CentFT0Cs", - "CentFT0CVariant1s", - "CentFDDMs", - "CentNTPVs", - "CentNGlobals", - "CentMFTs"}; -static const std::vector parameterNames{"Enable"}; -static const int defaultParameters[NTables][NParameters]{{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}}; - -struct CentralityTable { - Produces centRun2V0M; - Produces centRun2V0A; - Produces centRun2SPDTracklets; - Produces centRun2SPDClusters; - Produces centRun2CL0; - Produces centRun2CL1; - Produces centFV0A; - Produces centFT0M; - Produces centFT0A; - Produces centFT0C; - Produces centFT0CVariant1; - Produces centFDDM; - Produces centNTPV; - Produces centNGlobals; - Produces centMFTs; - Service ccdb; - Configurable> enabledTables{"enabledTables", - {defaultParameters[0], NTables, NParameters, tableNames, parameterNames}, - "Produce tables depending on needs. Values different than -1 override the automatic setup: the corresponding table can be set off (0) or on (1)"}; - struct : ConfigurableGroup { - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "The CCDB endpoint url address"}; - Configurable ccdbPath{"ccdbPath", "Centrality/Estimators", "The CCDB path for centrality/multiplicity information"}; - Configurable genName{"genName", "", "Genearator name: HIJING, PYTHIA8, ... Default: \"\""}; - Configurable doNotCrashOnNull{"doNotCrashOnNull", false, {"Option to not crash on null and instead fill required tables with dummy info"}}; - Configurable reconstructionPass{"reconstructionPass", "", {"Apass to use when fetching the calibration tables. Empty (default) does not check for any pass. Use `metadata` to fetch it from the AO2D metadata. Otherwise it will override the metadata."}}; - } ccdbConfig; - - Configurable embedINELgtZEROselection{"embedINELgtZEROselection", false, {"Option to do percentile 100.5 if not INELgtZERO"}}; - Configurable produceHistograms{"produceHistograms", false, {"Option to produce debug histograms"}}; - ConfigurableAxis binsPercentile{"binsPercentile", {VARIABLE_WIDTH, 0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01, 0.011, 0.012, 0.013, 0.014, 0.015, 0.016, 0.017, 0.018, 0.019, 0.02, 0.021, 0.022, 0.023, 0.024, 0.025, 0.026, 0.027, 0.028, 0.029, 0.03, 0.031, 0.032, 0.033, 0.034, 0.035, 0.036, 0.037, 0.038, 0.039, 0.04, 0.041, 0.042, 0.043, 0.044, 0.045, 0.046, 0.047, 0.048, 0.049, 0.05, 0.051, 0.052, 0.053, 0.054, 0.055, 0.056, 0.057, 0.058, 0.059, 0.06, 0.061, 0.062, 0.063, 0.064, 0.065, 0.066, 0.067, 0.068, 0.069, 0.07, 0.071, 0.072, 0.073, 0.074, 0.075, 0.076, 0.077, 0.078, 0.079, 0.08, 0.081, 0.082, 0.083, 0.084, 0.085, 0.086, 0.087, 0.088, 0.089, 0.09, 0.091, 0.092, 0.093, 0.094, 0.095, 0.096, 0.097, 0.098, 0.099, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.7, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 48.0, 49.0, 50.0, 51.0, 52.0, 53.0, 54.0, 55.0, 56.0, 57.0, 58.0, 59.0, 60.0, 61.0, 62.0, 63.0, 64.0, 65.0, 66.0, 67.0, 68.0, 69.0, 70.0, 71.0, 72.0, 73.0, 74.0, 75.0, 76.0, 77.0, 78.0, 79.0, 80.0, 81.0, 82.0, 83.0, 84.0, 85.0, 86.0, 87.0, 88.0, 89.0, 90.0, 91.0, 92.0, 93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0, 100.0}, "Binning of the percentile axis"}; - ConfigurableAxis binsPVcontr{"binsPVcontr", {100, 0.f, 100.f}, "PV mult."}; - - int mRunNumber; - struct TagRun2V0MCalibration { - bool mCalibrationStored = false; - TFormula* mMCScale = nullptr; - float mMCScalePars[6] = {0.0}; - TH1* mhVtxAmpCorrV0A = nullptr; - TH1* mhVtxAmpCorrV0C = nullptr; - TH1* mhMultSelCalib = nullptr; - } Run2V0MInfo; - struct TagRun2V0ACalibration { - bool mCalibrationStored = false; - TH1* mhVtxAmpCorrV0A = nullptr; - TH1* mhMultSelCalib = nullptr; - } Run2V0AInfo; - struct TagRun2SPDTrackletsCalibration { - bool mCalibrationStored = false; - TH1* mhVtxAmpCorr = nullptr; - TH1* mhMultSelCalib = nullptr; - } Run2SPDTksInfo; - struct TagRun2SPDClustersCalibration { - bool mCalibrationStored = false; - TH1* mhVtxAmpCorrCL0 = nullptr; - TH1* mhVtxAmpCorrCL1 = nullptr; - TH1* mhMultSelCalib = nullptr; - } Run2SPDClsInfo; - struct TagRun2CL0Calibration { - bool mCalibrationStored = false; - TH1* mhVtxAmpCorr = nullptr; - TH1* mhMultSelCalib = nullptr; - } Run2CL0Info; - struct TagRun2CL1Calibration { - bool mCalibrationStored = false; - TH1* mhVtxAmpCorr = nullptr; - TH1* mhMultSelCalib = nullptr; - } Run2CL1Info; - struct CalibrationInfo { - std::string name = ""; - bool mCalibrationStored = false; - TH1* mhMultSelCalib = nullptr; - float mMCScalePars[6] = {0.0}; - TFormula* mMCScale = nullptr; - explicit CalibrationInfo(std::string name) - : name(name), - mCalibrationStored(false), - mhMultSelCalib(nullptr), - mMCScalePars{0.0}, - mMCScale(nullptr) - { - } - bool isSane(bool fatalize = false) - { - if (!mhMultSelCalib) { - return true; - } - for (int i = 1; i < mhMultSelCalib->GetNbinsX() + 1; i++) { - if (mhMultSelCalib->GetXaxis()->GetBinLowEdge(i) > mhMultSelCalib->GetXaxis()->GetBinUpEdge(i)) { - if (fatalize) { - LOG(fatal) << "Centrality calibration table " << name << " has bins with low edge > up edge"; - } - LOG(warning) << "Centrality calibration table " << name << " has bins with low edge > up edge"; - return false; - } - } - return true; - } - }; - CalibrationInfo fv0aInfo = CalibrationInfo("FV0"); - CalibrationInfo ft0mInfo = CalibrationInfo("FT0"); - CalibrationInfo ft0aInfo = CalibrationInfo("FT0A"); - CalibrationInfo ft0cInfo = CalibrationInfo("FT0C"); - CalibrationInfo ft0cVariant1Info = CalibrationInfo("FT0Cvar1"); - CalibrationInfo fddmInfo = CalibrationInfo("FDD"); - CalibrationInfo ntpvInfo = CalibrationInfo("NTracksPV"); - CalibrationInfo nGlobalInfo = CalibrationInfo("NGlobal"); - CalibrationInfo mftInfo = CalibrationInfo("MFT"); - std::vector mEnabledTables; // Vector of enabled tables - std::array isTableEnabled; - - // Debug output - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - OutputObj listCalib{"calib-list", OutputObjHandlingPolicy::QAObject}; - - void init(InitContext& context) - { - LOG(info) << "Initializing centrality table producer"; - if (doprocessRun3FT0 == true) { - LOG(fatal) << "FT0 only mode is automatically enabled in Run3 mode. Please disable it and enable processRun3."; - } - if (doprocessRun2 == false && doprocessRun3 == false && doprocessRun3Complete == false) { - LOGF(fatal, "Neither processRun2 nor processRun3 nor processRun3Complete enabled. Please choose one."); - } - if (doprocessRun2 == true && doprocessRun3 == true) { - LOGF(fatal, "Cannot enable processRun2 and processRun3 at the same time. Please choose one."); - } - - /* Checking the tables which are requested in the workflow and enabling them */ - for (int i = 0; i < NTables; i++) { - int f = enabledTables->get(tableNames[i].c_str(), "Enable"); - o2::common::core::enableFlagIfTableRequired(context, tableNames[i], f); - if (f == 1) { - if (tableNames[i].find("Run2") != std::string::npos) { - if (doprocessRun3) { - LOG(fatal) << "Cannot enable Run2 table `" << tableNames[i] << "` while running in Run3 mode. Please check and disable them."; - } - } else { - if (doprocessRun2) { - LOG(fatal) << "Cannot enable Run3 table `" << tableNames[i] << "` while running in Run2 mode. Please check and disable them."; - } - } - isTableEnabled[i] = true; - mEnabledTables.push_back(i); - } - } - - if (mEnabledTables.size() == 0) { - LOGF(fatal, "No table enabled. Please enable at least one table."); - } - std::sort(mEnabledTables.begin(), mEnabledTables.end()); - - // Check if FT0 is the only centrality needed - if (mEnabledTables.size() == 1 && isTableEnabled[kCentFT0Ms] == true) { - LOG(info) << "FT0 only mode is enabled"; - doprocessRun3FT0.value = true; - doprocessRun3.value = false; - } - - ccdb->setURL(ccdbConfig.ccdbUrl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - mRunNumber = 0; - listCalib.setObject(new TList); - if (!produceHistograms.value) { - return; - } - - histos.add("FT0M/percentile", "FT0M percentile.", HistType::kTH1D, {{binsPercentile, "FT0M percentile"}}); - histos.add("FT0M/percentilevsPV", "percentile vs PV mult.", HistType::kTH2D, {{binsPercentile, "FT0M percentile"}, {binsPVcontr, "PV mult."}}); - histos.add("FT0M/MultvsPV", "FT0M mult. vs PV mult.", HistType::kTH2D, {{1000, 0, 5000, "FT0M mult."}, {binsPVcontr, "PV mult."}}); - - histos.add("FT0A/percentile", "FT0A percentile.", HistType::kTH1D, {{binsPercentile, "FT0A percentile"}}); - histos.add("FT0A/percentilevsPV", "percentile vs PV mult.", HistType::kTH2D, {{binsPercentile, "FT0A percentile"}, {binsPVcontr, "PV mult."}}); - histos.add("FT0A/MultvsPV", "FT0A mult. vs PV mult.", HistType::kTH2D, {{1000, 0, 5000, "FT0A mult."}, {binsPVcontr, "PV mult."}}); - - histos.add("FT0C/percentile", "FT0C percentile.", HistType::kTH1D, {{binsPercentile, "FT0C percentile"}}); - histos.add("FT0C/percentilevsPV", "percentile vs PV mult.", HistType::kTH2D, {{binsPercentile, "FT0C percentile"}, {binsPVcontr, "PV mult."}}); - histos.add("FT0C/MultvsPV", "FT0C mult. vs PV mult.", HistType::kTH2D, {{1000, 0, 5000, "FT0C mult."}, {binsPVcontr, "PV mult."}}); - - histos.addClone("FT0M/", "sel8FT0M/"); - histos.addClone("FT0C/", "sel8FT0C/"); - histos.addClone("FT0A/", "sel8FT0A/"); - - histos.print(); - } - - using BCsWithTimestampsAndRun2Infos = soa::Join; - - void processRun2(soa::Join::iterator const& collision, BCsWithTimestampsAndRun2Infos const&) - { - /* check the previous run number */ - auto bc = collision.bc_as(); - if (bc.runNumber() != mRunNumber) { - mRunNumber = bc.runNumber(); // mark that this run has been attempted already regardless of outcome - LOGF(debug, "timestamp=%llu", bc.timestamp()); - TList* callst = nullptr; - if (ccdbConfig.reconstructionPass.value == "") { - callst = ccdb->getForRun(ccdbConfig.ccdbPath, bc.runNumber()); - } else if (ccdbConfig.reconstructionPass.value == "metadata") { - std::map metadata; - metadata["RecoPassName"] = metadataInfo.get("RecoPassName"); - LOGF(info, "Loading CCDB for reconstruction pass (from metadata): %s", metadataInfo.get("RecoPassName")); - callst = ccdb->getSpecificForRun(ccdbConfig.ccdbPath, bc.runNumber(), metadata); - } else { - std::map metadata; - metadata["RecoPassName"] = ccdbConfig.reconstructionPass.value; - LOGF(info, "Loading CCDB for reconstruction pass (from provided argument): %s", ccdbConfig.reconstructionPass.value); - callst = ccdb->getSpecificForRun(ccdbConfig.ccdbPath, bc.runNumber(), metadata); - } - - Run2V0MInfo.mCalibrationStored = false; - Run2V0AInfo.mCalibrationStored = false; - Run2SPDTksInfo.mCalibrationStored = false; - Run2SPDClsInfo.mCalibrationStored = false; - Run2CL0Info.mCalibrationStored = false; - Run2CL1Info.mCalibrationStored = false; - if (callst != nullptr) { - auto getccdb = [callst](const char* ccdbhname) { - TH1* h = reinterpret_cast(callst->FindObject(ccdbhname)); - return h; - }; - auto getformulaccdb = [callst](const char* ccdbhname) { - TFormula* f = reinterpret_cast(callst->FindObject(ccdbhname)); - return f; - }; - if (isTableEnabled[kCentRun2V0Ms]) { - LOGF(debug, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - Run2V0MInfo.mhVtxAmpCorrV0A = getccdb("hVtx_fAmplitude_V0A_Normalized"); - Run2V0MInfo.mhVtxAmpCorrV0C = getccdb("hVtx_fAmplitude_V0C_Normalized"); - Run2V0MInfo.mhMultSelCalib = getccdb("hMultSelCalib_V0M"); - Run2V0MInfo.mMCScale = getformulaccdb(TString::Format("%s-V0M", ccdbConfig.genName->c_str()).Data()); - if ((Run2V0MInfo.mhVtxAmpCorrV0A != nullptr) && (Run2V0MInfo.mhVtxAmpCorrV0C != nullptr) && (Run2V0MInfo.mhMultSelCalib != nullptr)) { - if (ccdbConfig.genName->length() != 0) { - if (Run2V0MInfo.mMCScale != nullptr) { - for (int ixpar = 0; ixpar < 6; ++ixpar) { - Run2V0MInfo.mMCScalePars[ixpar] = Run2V0MInfo.mMCScale->GetParameter(ixpar); - } - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "MC Scale information from V0M for run %d not available", bc.runNumber()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "MC Scale information from V0M for run %d not available", bc.runNumber()); - } - } - } - Run2V0MInfo.mCalibrationStored = true; - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Calibration information from V0M for run %d corrupted", bc.runNumber()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Calibration information from V0M for run %d corrupted, will fill V0M tables with dummy values", bc.runNumber()); - } - } - } - if (isTableEnabled[kCentRun2V0As]) { - LOGF(debug, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - Run2V0AInfo.mhVtxAmpCorrV0A = getccdb("hVtx_fAmplitude_V0A_Normalized"); - Run2V0AInfo.mhMultSelCalib = getccdb("hMultSelCalib_V0A"); - if ((Run2V0AInfo.mhVtxAmpCorrV0A != nullptr) && (Run2V0AInfo.mhMultSelCalib != nullptr)) { - Run2V0AInfo.mCalibrationStored = true; - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Calibration information from V0A for run %d corrupted", bc.runNumber()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Calibration information from V0A for run %d corrupted, will fill V0A tables with dummy values", bc.runNumber()); - } - } - } - if (isTableEnabled[kCentRun2SPDTrks]) { - LOGF(debug, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - Run2SPDTksInfo.mhVtxAmpCorr = getccdb("hVtx_fnTracklets_Normalized"); - Run2SPDTksInfo.mhMultSelCalib = getccdb("hMultSelCalib_SPDTracklets"); - if ((Run2SPDTksInfo.mhVtxAmpCorr != nullptr) && (Run2SPDTksInfo.mhMultSelCalib != nullptr)) { - Run2SPDTksInfo.mCalibrationStored = true; - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Calibration information from SPD tracklets for run %d corrupted", bc.runNumber()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Calibration information from SPD tracklets for run %d corrupted, will fill SPD tracklets tables with dummy values", bc.runNumber()); - } - } - } - if (isTableEnabled[kCentRun2SPDClss]) { - LOGF(debug, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - Run2SPDClsInfo.mhVtxAmpCorrCL0 = getccdb("hVtx_fnSPDClusters0_Normalized"); - Run2SPDClsInfo.mhVtxAmpCorrCL1 = getccdb("hVtx_fnSPDClusters1_Normalized"); - Run2SPDClsInfo.mhMultSelCalib = getccdb("hMultSelCalib_SPDClusters"); - if ((Run2SPDClsInfo.mhVtxAmpCorrCL0 != nullptr) && (Run2SPDClsInfo.mhVtxAmpCorrCL1 != nullptr) && (Run2SPDClsInfo.mhMultSelCalib != nullptr)) { - Run2SPDClsInfo.mCalibrationStored = true; - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Calibration information from SPD clusters for run %d corrupted", bc.runNumber()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Calibration information from SPD clusters for run %d corrupted, will fill SPD clusters tables with dummy values", bc.runNumber()); - } - } - } - if (isTableEnabled[kCentRun2CL0s]) { - LOGF(debug, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - Run2CL0Info.mhVtxAmpCorr = getccdb("hVtx_fnSPDClusters0_Normalized"); - Run2CL0Info.mhMultSelCalib = getccdb("hMultSelCalib_CL0"); - if ((Run2CL0Info.mhVtxAmpCorr != nullptr) && (Run2CL0Info.mhMultSelCalib != nullptr)) { - Run2CL0Info.mCalibrationStored = true; - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Calibration information from CL0 multiplicity for run %d corrupted", bc.runNumber()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Calibration information from CL0 multiplicity for run %d corrupted, will fill CL0 multiplicity tables with dummy values", bc.runNumber()); - } - } - } - if (isTableEnabled[kCentRun2CL1s]) { - LOGF(debug, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - Run2CL1Info.mhVtxAmpCorr = getccdb("hVtx_fnSPDClusters1_Normalized"); - Run2CL1Info.mhMultSelCalib = getccdb("hMultSelCalib_CL1"); - if ((Run2CL1Info.mhVtxAmpCorr != nullptr) && (Run2CL1Info.mhMultSelCalib != nullptr)) { - Run2CL1Info.mCalibrationStored = true; - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Calibration information from CL1 multiplicity for run %d corrupted", bc.runNumber()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Calibration information from CL1 multiplicity for run %d corrupted, will fill CL1 multiplicity tables with dummy values", bc.runNumber()); - } - } - } - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Centrality calibration is not available in CCDB for run=%d at timestamp=%llu", bc.runNumber(), bc.timestamp()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Centrality calibration is not available in CCDB for run=%d at timestamp=%llu, will fill tables with dummy values", bc.runNumber(), bc.timestamp()); - } - } - } - - auto scaleMC = [](float x, const float pars[6]) { - return std::pow(((pars[0] + pars[1] * std::pow(x, pars[2])) - pars[3]) / pars[4], 1.0f / pars[5]); - }; - - if (isTableEnabled[kCentRun2V0Ms]) { - float cV0M = 105.0f; - if (Run2V0MInfo.mCalibrationStored) { - float v0m; - if (Run2V0MInfo.mMCScale != nullptr) { - v0m = scaleMC(collision.multFV0M(), Run2V0MInfo.mMCScalePars); - LOGF(debug, "Unscaled v0m: %f, scaled v0m: %f", collision.multFV0M(), v0m); - } else { - v0m = collision.multFV0A() * Run2V0MInfo.mhVtxAmpCorrV0A->GetBinContent(Run2V0MInfo.mhVtxAmpCorrV0A->FindFixBin(collision.posZ())) + - collision.multFV0C() * Run2V0MInfo.mhVtxAmpCorrV0C->GetBinContent(Run2V0MInfo.mhVtxAmpCorrV0C->FindFixBin(collision.posZ())); - } - cV0M = Run2V0MInfo.mhMultSelCalib->GetBinContent(Run2V0MInfo.mhMultSelCalib->FindFixBin(v0m)); - } - LOGF(debug, "centRun2V0M=%.0f", cV0M); - // fill centrality columns - centRun2V0M(cV0M); - } - if (isTableEnabled[kCentRun2V0As]) { - float cV0A = 105.0f; - if (Run2V0AInfo.mCalibrationStored) { - float v0a = collision.multFV0A() * Run2V0AInfo.mhVtxAmpCorrV0A->GetBinContent(Run2V0AInfo.mhVtxAmpCorrV0A->FindFixBin(collision.posZ())); - cV0A = Run2V0AInfo.mhMultSelCalib->GetBinContent(Run2V0AInfo.mhMultSelCalib->FindFixBin(v0a)); - } - LOGF(debug, "centRun2V0A=%.0f", cV0A); - // fill centrality columns - centRun2V0A(cV0A); - } - if (isTableEnabled[kCentRun2SPDTrks]) { - float cSPD = 105.0f; - if (Run2SPDTksInfo.mCalibrationStored) { - float spdm = collision.multTracklets() * Run2SPDTksInfo.mhVtxAmpCorr->GetBinContent(Run2SPDTksInfo.mhVtxAmpCorr->FindFixBin(collision.posZ())); - cSPD = Run2SPDTksInfo.mhMultSelCalib->GetBinContent(Run2SPDTksInfo.mhMultSelCalib->FindFixBin(spdm)); - } - LOGF(debug, "centSPDTracklets=%.0f", cSPD); - centRun2SPDTracklets(cSPD); - } - if (isTableEnabled[kCentRun2SPDClss]) { - float cSPD = 105.0f; - if (Run2SPDClsInfo.mCalibrationStored) { - float spdm = bc.spdClustersL0() * Run2SPDClsInfo.mhVtxAmpCorrCL0->GetBinContent(Run2SPDClsInfo.mhVtxAmpCorrCL0->FindFixBin(collision.posZ())) + - bc.spdClustersL1() * Run2SPDClsInfo.mhVtxAmpCorrCL1->GetBinContent(Run2SPDClsInfo.mhVtxAmpCorrCL1->FindFixBin(collision.posZ())); - cSPD = Run2SPDClsInfo.mhMultSelCalib->GetBinContent(Run2SPDClsInfo.mhMultSelCalib->FindFixBin(spdm)); - } - LOGF(debug, "centSPDClusters=%.0f", cSPD); - centRun2SPDClusters(cSPD); - } - if (isTableEnabled[kCentRun2CL0s]) { - float cCL0 = 105.0f; - if (Run2CL0Info.mCalibrationStored) { - float cl0m = bc.spdClustersL0() * Run2CL0Info.mhVtxAmpCorr->GetBinContent(Run2CL0Info.mhVtxAmpCorr->FindFixBin(collision.posZ())); - cCL0 = Run2CL0Info.mhMultSelCalib->GetBinContent(Run2CL0Info.mhMultSelCalib->FindFixBin(cl0m)); - } - LOGF(debug, "centCL0=%.0f", cCL0); - centRun2CL0(cCL0); - } - if (isTableEnabled[kCentRun2CL1s]) { - float cCL1 = 105.0f; - if (Run2CL1Info.mCalibrationStored) { - float cl1m = bc.spdClustersL1() * Run2CL1Info.mhVtxAmpCorr->GetBinContent(Run2CL1Info.mhVtxAmpCorr->FindFixBin(collision.posZ())); - cCL1 = Run2CL1Info.mhMultSelCalib->GetBinContent(Run2CL1Info.mhMultSelCalib->FindFixBin(cl1m)); - } - LOGF(debug, "centCL1=%.0f", cCL1); - centRun2CL1(cCL1); - } - } - - using BCsWithTimestamps = soa::Join; - - template - void produceRun3Tables(CollisionType const& collisions) - { - // do memory reservation for the relevant tables only, please - for (auto const& table : mEnabledTables) { - switch (table) { - case kCentFV0As: - centFV0A.reserve(collisions.size()); - break; - case kCentFT0Ms: - centFT0M.reserve(collisions.size()); - break; - case kCentFT0As: - centFT0A.reserve(collisions.size()); - break; - case kCentFT0Cs: - centFT0C.reserve(collisions.size()); - break; - case kCentFT0CVariant1s: - centFT0CVariant1.reserve(collisions.size()); - break; - case kCentFDDMs: - centFDDM.reserve(collisions.size()); - break; - case kCentNTPVs: - centNTPV.reserve(collisions.size()); - break; - case kCentNGlobals: - centNGlobals.reserve(collisions.size()); - break; - case kCentMFTs: - centMFTs.reserve(collisions.size()); - break; - default: - LOGF(fatal, "Table %d not supported in Run3", table); - break; - } - } - - for (auto const& collision : collisions) { - /* check the previous run number */ - auto bc = collision.template bc_as(); - if (bc.runNumber() != mRunNumber) { - mRunNumber = bc.runNumber(); // mark that this run has been attempted already regardless of outcome - LOGF(info, "timestamp=%llu, run number=%d", bc.timestamp(), bc.runNumber()); - TList* callst = nullptr; - // Check if the ccdb path is a root file - if (ccdbConfig.ccdbPath.value.find(".root") != std::string::npos) { - TFile f(ccdbConfig.ccdbPath.value.c_str(), "READ"); - f.GetObject(ccdbConfig.reconstructionPass.value.c_str(), callst); - if (!callst) { - f.ls(); - LOG(fatal) << "No calibration list " << ccdbConfig.reconstructionPass.value << " found in the file " << ccdbConfig.ccdbPath.value; - } - } else { - if (ccdbConfig.reconstructionPass.value == "") { - callst = ccdb->getForRun(ccdbConfig.ccdbPath, bc.runNumber()); - } else if (ccdbConfig.reconstructionPass.value == "metadata") { - std::map metadata; - metadata["RecoPassName"] = metadataInfo.get("RecoPassName"); - LOGF(info, "Loading CCDB for reconstruction pass (from metadata): %s", metadataInfo.get("RecoPassName")); - callst = ccdb->getSpecificForRun(ccdbConfig.ccdbPath, bc.runNumber(), metadata); - } else { - std::map metadata; - metadata["RecoPassName"] = ccdbConfig.reconstructionPass.value; - LOGF(info, "Loading CCDB for reconstruction pass (from provided argument): %s", ccdbConfig.reconstructionPass.value); - callst = ccdb->getSpecificForRun(ccdbConfig.ccdbPath, bc.runNumber(), metadata); - } - } - - fv0aInfo.mCalibrationStored = false; - ft0mInfo.mCalibrationStored = false; - ft0aInfo.mCalibrationStored = false; - ft0cInfo.mCalibrationStored = false; - ft0cVariant1Info.mCalibrationStored = false; - fddmInfo.mCalibrationStored = false; - ntpvInfo.mCalibrationStored = false; - nGlobalInfo.mCalibrationStored = false; - mftInfo.mCalibrationStored = false; - if (callst != nullptr) { - if (produceHistograms) { - listCalib->Add(callst->Clone(Form("%i", bc.runNumber()))); - } - LOGF(info, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - auto getccdb = [callst, bc](struct CalibrationInfo& estimator, const Configurable generatorName) { // TODO: to consider the name inside the estimator structure - estimator.mhMultSelCalib = reinterpret_cast(callst->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); - estimator.mMCScale = reinterpret_cast(callst->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); - if (estimator.mhMultSelCalib != nullptr) { - if (generatorName->length() != 0) { - LOGF(info, "Retrieving MC calibration for %d, generator name: %s", bc.runNumber(), generatorName->c_str()); - if (estimator.mMCScale != nullptr) { - for (int ixpar = 0; ixpar < 6; ++ixpar) { - estimator.mMCScalePars[ixpar] = estimator.mMCScale->GetParameter(ixpar); - LOGF(info, "Parameter index %i value %.5f", ixpar, estimator.mMCScalePars[ixpar]); - } - } else { - LOGF(warning, "MC Scale information from %s for run %d not available", estimator.name.c_str(), bc.runNumber()); - } - } - estimator.mCalibrationStored = true; - estimator.isSane(); - } else { - LOGF(info, "Calibration information from %s for run %d not available, will fill this estimator with invalid values and continue (no crash).", estimator.name.c_str(), bc.runNumber()); - } - }; - - for (auto const& table : mEnabledTables) { - switch (table) { - case kCentFV0As: - getccdb(fv0aInfo, ccdbConfig.genName); - break; - case kCentFT0Ms: - getccdb(ft0mInfo, ccdbConfig.genName); - break; - case kCentFT0As: - getccdb(ft0aInfo, ccdbConfig.genName); - break; - case kCentFT0Cs: - getccdb(ft0cInfo, ccdbConfig.genName); - break; - case kCentFT0CVariant1s: - getccdb(ft0cVariant1Info, ccdbConfig.genName); - break; - case kCentFDDMs: - getccdb(fddmInfo, ccdbConfig.genName); - break; - case kCentNTPVs: - getccdb(ntpvInfo, ccdbConfig.genName); - break; - case kCentNGlobals: - getccdb(nGlobalInfo, ccdbConfig.genName); - break; - case kCentMFTs: - getccdb(mftInfo, ccdbConfig.genName); - break; - default: - LOGF(fatal, "Table %d not supported in Run3", table); - break; - } - } - } else { - if (!ccdbConfig.doNotCrashOnNull) { // default behaviour: crash - LOGF(fatal, "Centrality calibration is not available in CCDB for run=%d at timestamp=%llu", bc.runNumber(), bc.timestamp()); - } else { // only if asked: continue filling with non-valid values (105) - LOGF(info, "Centrality calibration is not available in CCDB for run=%d at timestamp=%llu, will fill tables with dummy values", bc.runNumber(), bc.timestamp()); - } - } - } - - /** - * @brief Populates a table with data based on the given calibration information and multiplicity. - * - * @param table The table to populate. - * @param estimator The calibration information. - * @param multiplicity The multiplicity value. - */ - - auto populateTable = [&](auto& table, struct CalibrationInfo& estimator, float multiplicity) { - const bool assignOutOfRange = embedINELgtZEROselection && !collision.isInelGt0(); - auto scaleMC = [](float x, const float pars[6]) { - return std::pow(((pars[0] + pars[1] * std::pow(x, pars[2])) - pars[3]) / pars[4], 1.0f / pars[5]); - }; - - float percentile = 105.0f; - float scaledMultiplicity = multiplicity; - if (estimator.mCalibrationStored) { - if (estimator.mMCScale != nullptr) { - scaledMultiplicity = scaleMC(multiplicity, estimator.mMCScalePars); - LOGF(debug, "Unscaled %s multiplicity: %f, scaled %s multiplicity: %f", estimator.name.c_str(), multiplicity, estimator.name.c_str(), scaledMultiplicity); - } - percentile = estimator.mhMultSelCalib->GetBinContent(estimator.mhMultSelCalib->FindFixBin(scaledMultiplicity)); - if (assignOutOfRange) - percentile = 100.5f; - } - LOGF(debug, "%s centrality/multiplicity percentile = %.0f for a zvtx eq %s value %.0f", estimator.name.c_str(), percentile, estimator.name.c_str(), scaledMultiplicity); - table(percentile); - return percentile; - }; - - for (auto const& table : mEnabledTables) { - switch (table) { - case kCentFV0As: - if constexpr (enableCentFV0) { - populateTable(centFV0A, fv0aInfo, collision.multZeqFV0A()); - } - break; - case kCentFT0Ms: - if constexpr (enableCentFT0) { - const float perC = populateTable(centFT0M, ft0mInfo, collision.multZeqFT0A() + collision.multZeqFT0C()); - if (produceHistograms.value) { - histos.fill(HIST("FT0M/percentile"), perC); - histos.fill(HIST("FT0M/percentilevsPV"), perC, collision.multNTracksPV()); - histos.fill(HIST("FT0M/MultvsPV"), collision.multZeqFT0A() + collision.multZeqFT0C(), collision.multNTracksPV()); - if (collision.sel8()) { - histos.fill(HIST("sel8FT0M/percentile"), perC); - histos.fill(HIST("sel8FT0M/percentilevsPV"), perC, collision.multNTracksPV()); - histos.fill(HIST("sel8FT0M/MultvsPV"), collision.multZeqFT0A() + collision.multZeqFT0C(), collision.multNTracksPV()); - } - } - } - break; - case kCentFT0As: - if constexpr (enableCentFT0) { - const float perC = populateTable(centFT0A, ft0aInfo, collision.multZeqFT0A()); - if (produceHistograms.value) { - histos.fill(HIST("FT0A/percentile"), perC); - histos.fill(HIST("FT0A/percentilevsPV"), perC, collision.multNTracksPV()); - histos.fill(HIST("FT0A/MultvsPV"), collision.multZeqFT0A() + collision.multZeqFT0C(), collision.multNTracksPV()); - if (collision.sel8()) { - histos.fill(HIST("sel8FT0A/percentile"), perC); - histos.fill(HIST("sel8FT0A/percentilevsPV"), perC, collision.multNTracksPV()); - histos.fill(HIST("sel8FT0A/MultvsPV"), collision.multZeqFT0A() + collision.multZeqFT0C(), collision.multNTracksPV()); - } - } - } - break; - case kCentFT0Cs: - if constexpr (enableCentFT0) { - const float perC = populateTable(centFT0C, ft0cInfo, collision.multZeqFT0C()); - if (produceHistograms.value) { - histos.fill(HIST("FT0C/percentile"), perC); - histos.fill(HIST("FT0C/percentilevsPV"), perC, collision.multNTracksPV()); - histos.fill(HIST("FT0C/MultvsPV"), collision.multZeqFT0A() + collision.multZeqFT0C(), collision.multNTracksPV()); - if (collision.sel8()) { - histos.fill(HIST("sel8FT0C/percentile"), perC); - histos.fill(HIST("sel8FT0C/percentilevsPV"), perC, collision.multNTracksPV()); - histos.fill(HIST("sel8FT0C/MultvsPV"), collision.multZeqFT0A() + collision.multZeqFT0C(), collision.multNTracksPV()); - } - } - } - break; - case kCentFT0CVariant1s: - if constexpr (enableCentFT0) { - populateTable(centFT0CVariant1, ft0cVariant1Info, collision.multZeqFT0C()); - } - break; - case kCentFDDMs: - if constexpr (enableCentFDD) { - populateTable(centFDDM, fddmInfo, collision.multZeqFDDA() + collision.multZeqFDDC()); - } - break; - case kCentNTPVs: - if constexpr (enableCentNTPV) { - populateTable(centNTPV, ntpvInfo, collision.multZeqNTracksPV()); - } - break; - case kCentNGlobals: - if constexpr (enableCentNGlobal) { - populateTable(centNGlobals, nGlobalInfo, collision.multNTracksGlobal()); - } - break; - case kCentMFTs: - if constexpr (enableCentMFT) { - populateTable(centMFTs, mftInfo, collision.mftNtracks()); - } - break; - default: - LOGF(fatal, "Table %d not supported in Run3", table); - break; - } - } - } - } - - void processRun3Complete(soa::Join const& collisions, BCsWithTimestamps const&) - { - produceRun3Tables(collisions); - } - - void processRun3(soa::Join const& collisions, BCsWithTimestamps const&) - { - produceRun3Tables(collisions); - } - - void processRun3FT0(soa::Join const& collisions, BCsWithTimestamps const&) - { - produceRun3Tables(collisions); - } - - // Process switches - PROCESS_SWITCH(CentralityTable, processRun2, "Provide Run2 calibrated centrality/multiplicity percentiles tables", true); - PROCESS_SWITCH(CentralityTable, processRun3Complete, "Provide Run3 calibrated centrality/multiplicity percentiles tables using MFT and global tracks (requires extra subscriptions)", false); - PROCESS_SWITCH(CentralityTable, processRun3, "Provide Run3 calibrated centrality/multiplicity percentiles tables", false); - PROCESS_SWITCH(CentralityTable, processRun3FT0, "Provide Run3 calibrated centrality/multiplicity percentiles tables for FT0 only", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - metadataInfo.initMetadata(cfgc); - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -} diff --git a/Common/TableProducer/eseTableProducer.cxx b/Common/TableProducer/eseTableProducer.cxx index d6915ab9419..897659b198e 100644 --- a/Common/TableProducer/eseTableProducer.cxx +++ b/Common/TableProducer/eseTableProducer.cxx @@ -405,7 +405,7 @@ struct EseTableProducer { } template - double getEfficiency(TTrack track) + double getEfficiency(const TTrack& track) { double eff = 1.; if (cfg.mEfficiency) @@ -453,7 +453,7 @@ struct EseTableProducer { }; template - bool eventSelected(TCollision collision, const int& multTrk, const float& centrality) + bool eventSelected(const TCollision& collision, const int& multTrk, const float& centrality) { if (cfgTVXinTRD) { if (collision.alias_bit(kTVXinTRD)) { diff --git a/Common/TableProducer/eventSelectionService.cxx b/Common/TableProducer/eventSelectionService.cxx index e93f3378730..4be5b73b5a5 100644 --- a/Common/TableProducer/eventSelectionService.cxx +++ b/Common/TableProducer/eventSelectionService.cxx @@ -9,16 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file eventSelectionTester.cxx +/// \file eventSelectionService.cxx /// \brief unified, self-configuring event selection task /// \author ALICE -//=============================================================== -// -// Unified, self-configuring event selection task -// -//=============================================================== - #include "Common/Core/MetadataHelper.h" #include "Common/DataModel/EventSelection.h" #include "Common/Tools/EventSelectionModule.h" @@ -50,14 +44,14 @@ using BCsWithRun3Matchings = soa::Join; using FullTracks = soa::Join; using FullTracksIU = soa::Join; -struct eventselectionRun2 { - o2::common::timestamp::timestampConfigurables timestampConfigurables; +struct EventselectionRun2 { // o2-linter: disable=name/workflow-file (exception due to metadata-driven topology) + o2::common::timestamp::TimestampConfigurables timestampConfigurables; o2::common::timestamp::TimestampModule timestampMod; - o2::common::eventselection::bcselConfigurables bcselOpts; + o2::common::eventselection::BcselConfigurables bcselOpts; o2::common::eventselection::BcSelectionModule bcselmodule; - o2::common::eventselection::evselConfigurables evselOpts; + o2::common::eventselection::EvselConfigurables evselOpts; o2::common::eventselection::EventSelectionModule evselmodule; Produces timestampTable; /// Table with SOR timestamps produced by the task @@ -75,7 +69,7 @@ struct eventselectionRun2 { // buffering intermediate results for passing std::vector timestamps; - std::vector bcselsbuffer; + std::vector bcselsbuffer; // auxiliary Partition tracklets = (aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Run2Tracklet)); @@ -109,17 +103,17 @@ struct eventselectionRun2 { } }; -struct eventselectionRun3 { - o2::common::timestamp::timestampConfigurables timestampConfigurables; +struct EventselectionRun3 { // o2-linter: disable=name/workflow-file (exception due to metadata-driven topology) + o2::common::timestamp::TimestampConfigurables timestampConfigurables; o2::common::timestamp::TimestampModule timestampMod; - o2::common::eventselection::bcselConfigurables bcselOpts; + o2::common::eventselection::BcselConfigurables bcselOpts; o2::common::eventselection::BcSelectionModule bcselmodule; - o2::common::eventselection::evselConfigurables evselOpts; + o2::common::eventselection::EvselConfigurables evselOpts; o2::common::eventselection::EventSelectionModule evselmodule; - o2::common::eventselection::lumiConfigurables lumiOpts; + o2::common::eventselection::LumiConfigurables lumiOpts; o2::common::eventselection::LumiModule lumimodule; Produces timestampTable; /// Table with SOR timestamps produced by the task @@ -138,7 +132,7 @@ struct eventselectionRun3 { // the best: have readable cursors // this: a stopgap solution to avoid spawning yet another device std::vector timestamps; - std::vector bcselsbuffer; + std::vector bcselsbuffer; // auxiliary Partition pvTracks = ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); @@ -173,7 +167,7 @@ struct eventselectionRun3 { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // o2-linter: disable=o2-workflow-options (metadata-driven topology selection) { // Parse the metadata for later too metadataInfo.initMetadata(cfgc); @@ -191,18 +185,18 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) LOGF(warn, "Metadata info missing or incomplete. Make sure --aod-file is provided at the end of the last workflow and that the AO2D has metadata stored."); LOGF(warn, "Initializing with Run 3 data as default. Please note you will not be able to change settings manually."); LOGF(warn, "You should instead make sure the metadata is read in correctly."); - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } else { LOGF(info, "Metadata successfully read in. Is this Run 3? %i - will self-configure.", isRun3); if (isRun3) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } else { LOGF(warn, "******************************************************************"); LOGF(warn, " Event selection service self-configuring for Run 2."); LOGF(warn, " WARNING: THIS HAS NOT BEEN VALIDATED YET, USE WITH CAUTION"); LOGF(warn, " If this fails, please use event-selection-service-run2 instead."); LOGF(warn, "******************************************************************"); - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } } throw std::runtime_error("Unsupported run type / problem when configuring event selection!"); diff --git a/Common/TableProducer/eventSelectionServiceRun2.cxx b/Common/TableProducer/eventSelectionServiceRun2.cxx index 2af25cb83a0..b291d41ea8e 100644 --- a/Common/TableProducer/eventSelectionServiceRun2.cxx +++ b/Common/TableProducer/eventSelectionServiceRun2.cxx @@ -9,16 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file eventSelectionTester.cxx -/// \brief unified, self-configuring event selection task +/// \file eventSelectionServiceRun2.cxx +/// \brief event selection task for Run 2 /// \author ALICE -//=============================================================== -// -// Unified, self-configuring event selection task -// -//=============================================================== - #include "Common/Core/MetadataHelper.h" #include "Common/DataModel/EventSelection.h" #include "Common/Tools/EventSelectionModule.h" @@ -49,17 +43,17 @@ using BCsWithRun3Matchings = soa::Join; using FullTracks = soa::Join; using FullTracksIU = soa::Join; -struct eventselectionRun2 { - o2::common::timestamp::timestampConfigurables timestampConfigurables; +struct EventselectionRun2 { // o2-linter: disable=name/workflow-file (exception due to metadata-driven topology) + o2::common::timestamp::TimestampConfigurables timestampConfigurables; o2::common::timestamp::TimestampModule timestampMod; - o2::common::eventselection::bcselConfigurables bcselOpts; + o2::common::eventselection::BcselConfigurables bcselOpts; o2::common::eventselection::BcSelectionModule bcselmodule; - o2::common::eventselection::evselConfigurables evselOpts; + o2::common::eventselection::EvselConfigurables evselOpts; o2::common::eventselection::EventSelectionModule evselmodule; - Produces timestampTable; /// Table with SOR timestamps produced by the task + Produces timestampTable; // Table with SOR timestamps produced by the task Produces bcsel; Produces evsel; @@ -74,7 +68,7 @@ struct eventselectionRun2 { // buffering intermediate results for passing std::vector timestamps; - std::vector bcselsbuffer; + std::vector bcselsbuffer; // auxiliary Partition tracklets = (aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Run2Tracklet)); @@ -108,17 +102,17 @@ struct eventselectionRun2 { } }; -struct eventselectionRun3 { - o2::common::timestamp::timestampConfigurables timestampConfigurables; +struct EventselectionRun3 { // o2-linter: disable=name/workflow-file (exception due to metadata-driven topology) + o2::common::timestamp::TimestampConfigurables timestampConfigurables; o2::common::timestamp::TimestampModule timestampMod; - o2::common::eventselection::bcselConfigurables bcselOpts; + o2::common::eventselection::BcselConfigurables bcselOpts; o2::common::eventselection::BcSelectionModule bcselmodule; - o2::common::eventselection::evselConfigurables evselOpts; + o2::common::eventselection::EvselConfigurables evselOpts; o2::common::eventselection::EventSelectionModule evselmodule; - o2::common::eventselection::lumiConfigurables lumiOpts; + o2::common::eventselection::LumiConfigurables lumiOpts; o2::common::eventselection::LumiModule lumimodule; Produces timestampTable; /// Table with SOR timestamps produced by the task @@ -137,7 +131,7 @@ struct eventselectionRun3 { // the best: have readable cursors // this: a stopgap solution to avoid spawning yet another device std::vector timestamps; - std::vector bcselsbuffer; + std::vector bcselsbuffer; // auxiliary Partition pvTracks = ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); @@ -181,5 +175,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) LOGF(info, "To be improved once metadata enabling in defineDataProcessing is worked out."); // force Run 2 mode - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/Common/TableProducer/fwdtrackToCollisionAssociator.cxx b/Common/TableProducer/fwdtrackToCollisionAssociator.cxx index 862cee97872..1de267d0e60 100644 --- a/Common/TableProducer/fwdtrackToCollisionAssociator.cxx +++ b/Common/TableProducer/fwdtrackToCollisionAssociator.cxx @@ -16,6 +16,7 @@ #include "Common/Core/CollisionAssociation.h" #include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/FwdTrackReAlignTables.h" #include #include @@ -44,6 +45,7 @@ struct FwdTrackToCollisionAssociation { CollisionAssociation collisionAssociator; Preslice muonsPerCollisions = aod::fwdtrack::collisionId; + Preslice realignmuonsPerCollisions = aod::fwdtrack::collisionId; Preslice mftsPerCollisions = aod::fwdtrack::collisionId; void init(InitContext const&) @@ -55,7 +57,7 @@ struct FwdTrackToCollisionAssociation { LOGP(fatal, "Exactly one process function between standard and time-based association should be enabled!"); } - if (!(doprocessMFTAssocWithTime || doprocessMFTStandardAssoc || doprocessFwdAssocWithTime || doprocessFwdStandardAssoc)) { + if (!(doprocessMFTAssocWithTime || doprocessMFTStandardAssoc || doprocessFwdAssocWithTime || doprocessFwdStandardAssoc || doprocessFwdRealignAssocWithTime || doprocessFwdRealignStandardAssoc)) { LOGP(fatal, "At least one process function should be enabled!"); } @@ -63,7 +65,7 @@ struct FwdTrackToCollisionAssociation { collisionAssociator.setNumSigmaForTimeCompat(nSigmaForTimeCompat); collisionAssociator.setTimeMargin(timeMargin); collisionAssociator.setTrackSelectionOptionForStdAssoc(track_association::TrackSelection::None); - collisionAssociator.setUsePvAssociation(false); + collisionAssociator.setUsePvAssociation(track_association::PVContrReassocOpt::Disabled); collisionAssociator.setIncludeUnassigned(includeUnassigned); collisionAssociator.setFillTableOfCollIdsPerTrack(fillTableOfCollIdsPerTrack); collisionAssociator.setBcWindow(bcWindowForOneSigma); @@ -85,6 +87,22 @@ struct FwdTrackToCollisionAssociation { } PROCESS_SWITCH(FwdTrackToCollisionAssociation, processFwdStandardAssoc, "Use standard fwdtrack-to-collision association", false); + void processFwdRealignAssocWithTime(Collisions const& collisions, + FwdTracksReAlign const& muons, + AmbiguousFwdTrksReAlign const& ambiTracksFwd, + BCs const& bcs) + { + collisionAssociator.runAssocWithTime(collisions, muons, muons, ambiTracksFwd, bcs, fwdassociation, fwdreverseIndices); + } + PROCESS_SWITCH(FwdTrackToCollisionAssociation, processFwdRealignAssocWithTime, "Use fwdrealigntrack-to-collision association based on time", false); + + void processFwdRealignStandardAssoc(Collisions const& collisions, + FwdTracksReAlign const& muons) + { + collisionAssociator.runStandardAssoc(collisions, muons, realignmuonsPerCollisions, fwdassociation, fwdreverseIndices); + } + PROCESS_SWITCH(FwdTrackToCollisionAssociation, processFwdRealignStandardAssoc, "Use standard fwdrealigntrack-to-collision association", false); + void processMFTAssocWithTime(Collisions const& collisions, MFTTracks const& tracks, AmbiguousMFTTracks const& ambiguousTracks, diff --git a/Common/TableProducer/match-mft-ft0.cxx b/Common/TableProducer/match-mft-ft0.cxx index a6160cabe4b..d282c75c6fe 100644 --- a/Common/TableProducer/match-mft-ft0.cxx +++ b/Common/TableProducer/match-mft-ft0.cxx @@ -124,11 +124,11 @@ T getCompatibleBCs(aod::AmbiguousMFTTrack const& atrack, aod::Collision const& c } if (bcIt != bcs.end() && maxBCId >= minBCId) { - T slice{{bcs.asArrowTable()->Slice(minBCId, maxBCId - minBCId + 1)}, (uint64_t)minBCId}; + auto slice = bcs.rawSlice(minBCId, maxBCId - minBCId + 1); bcs.copyIndexBindings(slice); return slice; } else { - T slice{{bcs.asArrowTable()->Slice(minBCId, maxBCId - minBCId)}, (uint64_t)minBCId}; + auto slice = bcs.rawSlice(minBCId, maxBCId - minBCId); bcs.copyIndexBindings(slice); return slice; } @@ -141,22 +141,14 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& // define firstBC and lastBC (globalBC of beginning and end of the range, when no shift is applied) auto bcIt = collOrig.bc_as(); - // auto timstp = bcIt.timestamp(); int64_t firstBC = bcIt.globalBC() + (track.trackTime() - track.trackTimeRes()) / o2::constants::lhc::LHCBunchSpacingNS; int64_t lastBC = firstBC + 2 * track.trackTimeRes() / o2::constants::lhc::LHCBunchSpacingNS + 1; // to have a delta = 198 BC - // printf(">>>>>>>>>>>>>>>>>>>>>>>>>>> last-first %lld\n", lastBC-firstBC); - - // int collTimeResInBC = collOrig.collisionTimeRes()/o2::constants::lhc::LHCBunchSpacingNS; - - // int64_t collFirstBC = bcIt.globalBC() + (collOrig.collisionTime() - collOrig.collisionTimeRes())/o2::constants::lhc::LHCBunchSpacingNS; - // int64_t collLastBC = collFirstBC + 2*collOrig.collisionTimeRes()/o2::constants::lhc::LHCBunchSpacingNS +1; - int64_t minBCId = bcIt.globalIndex(); - if ((int64_t)bcIt.globalBC() < firstBC + deltaBC) { - while (bcIt != bcs.end() && (int64_t)bcIt.globalBC() < firstBC + deltaBC) { + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) { + while (bcIt != bcs.end() && static_cast(bcIt.globalBC()) < firstBC + deltaBC) { minBCId = bcIt.globalIndex(); ++bcIt; @@ -171,11 +163,11 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& } else { // here bcIt.globalBC() >= firstBC + deltaBC - while (bcIt != bcs.begin() && (int64_t)bcIt.globalBC() >= (int64_t)firstBC + deltaBC) { + while (bcIt != bcs.begin() && static_cast(bcIt.globalBC()) >= firstBC + deltaBC) { minBCId = bcIt.globalIndex(); --bcIt; } - if (bcIt == bcs.begin() && (int64_t)bcIt.globalBC() >= (int64_t)firstBC + deltaBC) { + if (bcIt == bcs.begin() && static_cast(bcIt.globalBC()) >= firstBC + deltaBC) { minBCId = bcIt.globalIndex(); } ++bcIt; // retrieve the pointer which gave minBCId and minGlobalBC @@ -186,51 +178,42 @@ T getCompatibleBCs(aod::MFTTracks::iterator const& track, aod::Collision const& int64_t maxBCId = bcIt.globalIndex(); - if ((int64_t)bcIt.globalBC() > (int64_t)lastBC + deltaBC) { + if (static_cast(bcIt.globalBC()) > lastBC + deltaBC) { // the previous minimum is actually bigger than the right boundary if (bcIt != bcs.begin()) { - --bcIt; // let's check the previous element in the BC list - if ((int64_t)bcIt.globalBC() < (int64_t)firstBC + deltaBC) // if this previous element is smaller than the left boundary + --bcIt; // let's check the previous element in the BC list + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) // if this previous element is smaller than the left boundary { // means that the slice of compatible BCs is empty - T slice{{bcs.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; - // bcs.copyIndexBindings(slice); REMOVED IT BECAUSE I DON'T KNOW WHAT IT DOES HERE - return slice; // returns an empty slice + return bcs.emptySlice(); } } } - if ((int64_t)bcIt.globalBC() < (int64_t)firstBC + deltaBC) { + if (static_cast(bcIt.globalBC()) < firstBC + deltaBC) { // the previous minimum is actually smaller than the right boundary ++bcIt; - if (bcIt != bcs.end() && ((int64_t)bcIt.globalBC() > (int64_t)lastBC + deltaBC)) { + if (bcIt != bcs.end() && (static_cast(bcIt.globalBC()) > lastBC + deltaBC)) { // check the following element - T slice{{bcs.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; - // bcs.copyIndexBindings(slice); REMOVED IT BECAUSE I DON'T KNOW WHAT IT DOES HERE - return slice; // returns an empty slice + return bcs.emptySlice(); } } - while (bcIt != bcs.end() && (int64_t)bcIt.globalBC() <= (int64_t)lastBC + deltaBC) { + while (bcIt != bcs.end() && static_cast(bcIt.globalBC()) <= lastBC + deltaBC) { maxBCId = bcIt.globalIndex(); ++bcIt; } if (maxBCId < minBCId) { - if (bcIt == bcs.end()) { - printf("at the end of the bcs iterator %d\n", 1); - } - T slice{{bcs.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; - // bcs.copyIndexBindings(slice); REMOVED IT BECAUSE I DON'T KNOW WHAT IT DOES HERE - return slice; // returns an empty slice + return bcs.emptySlice(); } - T slice{{bcs.asArrowTable()->Slice(minBCId, maxBCId - minBCId + 1)}, (uint64_t)minBCId}; + auto slice = bcs.rawSlice(minBCId, maxBCId - minBCId + 1); bcs.copyIndexBindings(slice); return slice; } @@ -292,7 +275,7 @@ struct matchmftft0 { o2::base::Propagator::initFieldFromGRP(grpmag); // for some reason this is necessary for the next next line runNumber = bc.runNumber(); - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); Bz = field->getBz(centerMFT); // gives error if the propagator is not initFielded LOG(info) << "The field at the center of the MFT is Bz = " << Bz; diff --git a/Common/TableProducer/match-mft-mch-data-mc.cxx b/Common/TableProducer/match-mft-mch-data-mc.cxx index 2e801bc4040..df7a0846fd9 100644 --- a/Common/TableProducer/match-mft-mch-data-mc.cxx +++ b/Common/TableProducer/match-mft-mch-data-mc.cxx @@ -503,7 +503,7 @@ struct match_mft_mch_data_mc { }; template - bool isGoodMuonQuality(MUON muontrack) + bool isGoodMuonQuality(const MUON& muontrack) { if (!muontrack.has_collision()) return false; @@ -521,7 +521,7 @@ struct match_mft_mch_data_mc { } template - bool isGoodMuonKine(MUON muontrack) + bool isGoodMuonKine(const MUON& muontrack) { if (fEtaMchLow > muontrack.getEta() || muontrack.getEta() > fEtaMchUp) return false; @@ -529,7 +529,7 @@ struct match_mft_mch_data_mc { } template - bool isGoodMFTQuality(MFT mfttrack) + bool isGoodMFTQuality(const MFT& mfttrack) { if (!mfttrack.has_collision()) return false; @@ -541,7 +541,7 @@ struct match_mft_mch_data_mc { } template - bool isGoodMFTKine(MFT mfttrack) + bool isGoodMFTKine(const MFT& mfttrack) { if (fEtaMftLow > mfttrack.getEta() || mfttrack.getEta() > fEtaMftUp) return false; @@ -556,7 +556,7 @@ struct match_mft_mch_data_mc { template void setMUONs(MUONs const& muontracks, Collisions const& collisions) { - for (auto muontrack : muontracks) { + for (const auto& muontrack : muontracks) { if (!isGoodMuonQuality(muontrack)) continue; o2::dataformats::GlobalFwdTrack muontrack_at_pv = propagateMUONtoPV(muontrack, collisions); @@ -595,7 +595,7 @@ struct match_mft_mch_data_mc { template void setMFTs(MFTs const& mfttracks, Collisions const& collisions, o2::field::MagneticField* field) { - for (auto mfttrack : mfttracks) { + for (const auto& mfttrack : mfttracks) { if (!isGoodMFTQuality(mfttrack)) continue; @@ -643,7 +643,7 @@ struct match_mft_mch_data_mc { ccdbManager->get(geoPath); } o2::mch::TrackExtrap::setField(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); } void init(o2::framework::InitContext&) @@ -703,7 +703,7 @@ struct match_mft_mch_data_mc { tableMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), @@ -732,7 +732,7 @@ struct match_mft_mch_data_mc { tableMixMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), diff --git a/Common/TableProducer/match-mft-mch-data.cxx b/Common/TableProducer/match-mft-mch-data.cxx index 9541af4d5cc..c001315b2cd 100644 --- a/Common/TableProducer/match-mft-mch-data.cxx +++ b/Common/TableProducer/match-mft-mch-data.cxx @@ -503,7 +503,7 @@ struct match_mft_mch_data_mc { }; template - bool isGoodMuonQuality(MUON muontrack) + bool isGoodMuonQuality(const MUON& muontrack) { if (!muontrack.has_collision()) return false; @@ -521,7 +521,7 @@ struct match_mft_mch_data_mc { } template - bool isGoodMuonKine(MUON muontrack) + bool isGoodMuonKine(const MUON& muontrack) { if (fEtaMchLow > muontrack.getEta() || muontrack.getEta() > fEtaMchUp) return false; @@ -529,7 +529,7 @@ struct match_mft_mch_data_mc { } template - bool isGoodMFTQuality(MFT mfttrack) + bool isGoodMFTQuality(const MFT& mfttrack) { if (!mfttrack.has_collision()) return false; @@ -541,7 +541,7 @@ struct match_mft_mch_data_mc { } template - bool isGoodMFTKine(MFT mfttrack) + bool isGoodMFTKine(const MFT& mfttrack) { if (fEtaMftLow > mfttrack.getEta() || mfttrack.getEta() > fEtaMftUp) return false; @@ -556,7 +556,7 @@ struct match_mft_mch_data_mc { template void setMUONs(MUONs const& muontracks, Collisions const& collisions) { - for (auto muontrack : muontracks) { + for (const auto& muontrack : muontracks) { if (!isGoodMuonQuality(muontrack)) continue; o2::dataformats::GlobalFwdTrack muontrack_at_pv = propagateMUONtoPV(muontrack, collisions); @@ -595,7 +595,7 @@ struct match_mft_mch_data_mc { template void setMFTs(MFTs const& mfttracks, Collisions const& collisions, o2::field::MagneticField* field) { - for (auto mfttrack : mfttracks) { + for (const auto& mfttrack : mfttracks) { if (!isGoodMFTQuality(mfttrack)) continue; @@ -643,7 +643,7 @@ struct match_mft_mch_data_mc { ccdbManager->get(geoPath); } o2::mch::TrackExtrap::setField(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); } void init(o2::framework::InitContext&) @@ -703,7 +703,7 @@ struct match_mft_mch_data_mc { tableMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), @@ -732,7 +732,7 @@ struct match_mft_mch_data_mc { tableMixMatchingParams(matching.getGMPtAtDCA(), matching.getGMEtaAtDCA(), - static_cast(matching.getGMQ()), + matching.getGMQ(), matching.getDpt(), matching.getDx(), matching.getDy(), diff --git a/Common/TableProducer/mcCollsExtra.cxx b/Common/TableProducer/mcCollsExtra.cxx index b5588317894..4b7a0f8287e 100644 --- a/Common/TableProducer/mcCollsExtra.cxx +++ b/Common/TableProducer/mcCollsExtra.cxx @@ -50,7 +50,7 @@ struct mcCollisionExtra { Configurable poiEtaWindow{"poiEtaWindow", 0.8, "PDG code requirement within this eta window"}; // For manual sliceBy - Preslice perMcCollision = aod::mcparticle::mcCollisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; template std::vector sort_indices(const std::vector& v) diff --git a/Common/TableProducer/multiplicityExtraTable.cxx b/Common/TableProducer/multiplicityExtraTable.cxx index 7319a082c63..19a1280f60c 100644 --- a/Common/TableProducer/multiplicityExtraTable.cxx +++ b/Common/TableProducer/multiplicityExtraTable.cxx @@ -8,6 +8,13 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. + +/// +/// \file multiplicityExtraTable.cxx +/// \brief This tasks produces BC based multiplicity tables +/// \author David Dobrigkeit Chinellato and Jesper Karlsson Gumprecht +/// + #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -37,12 +44,12 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using BCPattern = std::bitset; -const int nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; struct MultiplicityExtraTable { Produces multBC; Produces multBcSel; Produces multNeigh; + Produces timeBC; Produces mult2bc; Produces bc2mult; @@ -54,13 +61,16 @@ struct MultiplicityExtraTable { Configurable bcDownscaleFactor{"bcDownscaleFactor", 2, "Downscale factor for BC table (0: save nothing, 1: save all)"}; Configurable minFT0CforBCTable{"minFT0CforBCTable", 25.0f, "Minimum FT0C amplitude to fill BC table to reduce data"}; Configurable saveOnlyBCsWithCollisions{"saveOnlyBCsWithCollisions", true, "save only BCs with collisions in them"}; + Configurable requestMinSignalForFT0C{"requestMinSignalForFT0C", true, "save only BCs with minimum FT0C signal"}; Configurable bcTableFloatPrecision{"bcTableFloatPrecision", 0.1, "float precision in bc table for data reduction"}; float tru(float value) { - if (bcTableFloatPrecision < 1e-4) + static constexpr float PrecisionThreshold = 1e-4; + if (bcTableFloatPrecision < PrecisionThreshold) { return value; // make sure nothing bad happens in case zero (best precision) + } return bcTableFloatPrecision * std::round(value / bcTableFloatPrecision) + 0.5f * bcTableFloatPrecision; }; @@ -68,8 +78,8 @@ struct MultiplicityExtraTable { unsigned int randomSeed = 0; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; - BCPattern CollidingBunch; + Service ccdb{}; + BCPattern collidingBunch; int newRunNumber = -999; int oldRunNumber = -999; @@ -116,14 +126,14 @@ struct MultiplicityExtraTable { float multFT0C = 0.f; if (bc.has_ft0()) { auto ft0 = bc.ft0(); - for (auto amplitude : ft0.amplitudeC()) { + for (const auto amplitude : ft0.amplitudeC()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFT0C += amplitude; } } else { multFT0C = -999.0f; } - if (multFT0C < minFT0CforBCTable) { + if (requestMinSignalForFT0C && multFT0C < minFT0CforBCTable) { continue; // skip this event } @@ -148,13 +158,16 @@ struct MultiplicityExtraTable { continue; // don't keep if low mult or downsampled out } - bool Tvx = false; + bool tvx = false; bool isFV0OrA = false; float multFT0C = 0.f; float multFT0A = 0.f; float multFV0A = 0.f; float multFDDA = 0.f; float multFDDC = 0.f; + float multFT0AOuter = 0.f; + float multFT0COuter = 0.f; + float multFV0AOuter = 0.f; // ZDC amplitudes float multZEM1 = -1.f; @@ -164,6 +177,16 @@ struct MultiplicityExtraTable { float multZPA = -1.f; float multZPC = -1.f; + // FT0 and ZDC time + float timeFT0A = -1e+3; + float timeFT0C = -1e+3; + float timeFDDA = -1e+3; + float timeFDDC = -1e+3; + float timeZNA = -1e+3; + float timeZNC = -1e+3; + float timeZPA = -1e+3; + float timeZPC = -1e+3; + float posZFT0 = -1e+3; bool posZFT0valid = false; @@ -174,7 +197,7 @@ struct MultiplicityExtraTable { // initialize - from Arvind newRunNumber = bc.runNumber(); - int localBC = bc.globalBC() % nBCsPerOrbit; + int localBC = bc.globalBC() % o2::constants::lhc::LHCMaxBunches; if (newRunNumber != oldRunNumber) { auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, newRunNumber); @@ -183,37 +206,55 @@ struct MultiplicityExtraTable { LOG(info) << " newRunNumber " << newRunNumber << " time stamp " << ts; oldRunNumber = newRunNumber; auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", ts); - CollidingBunch = grplhcif->getBunchFilling().getBCPattern(); + collidingBunch = grplhcif->getBunchFilling().getBCPattern(); } // new run number - bool collidingBC = CollidingBunch.test(localBC); + bool collidingBC = collidingBunch.test(localBC); if (bc.has_ft0()) { const auto& ft0 = bc.ft0(); std::bitset<8> triggers = ft0.triggerMask(); - Tvx = triggers[o2::fit::Triggers::bitVertex]; + tvx = triggers[o2::fit::Triggers::bitVertex]; multFT0TriggerBits = static_cast(triggers.to_ulong()); // calculate T0 charge - for (auto amplitude : ft0.amplitudeA()) { - multFT0A += amplitude; + for (size_t ii = 0; ii < ft0.amplitudeA().size(); ++ii) { + multFT0A += ft0.amplitudeA()[ii]; + static constexpr int MaxChannelIdInnerRingFT0A = 31; + if (ft0.channelA()[ii] > MaxChannelIdInnerRingFT0A) { + multFT0AOuter += ft0.amplitudeA()[ii]; + } } - for (auto amplitude : ft0.amplitudeC()) { - multFT0C += amplitude; + for (size_t ii = 0; ii < ft0.amplitudeC().size(); ++ii) { + multFT0C += ft0.amplitudeC()[ii]; + static constexpr int MaxChannelIdInnerRingFT0C = 48; + if (ft0.channelC()[ii] > MaxChannelIdInnerRingFT0C) { + multFT0COuter += ft0.amplitudeC()[ii]; + } } posZFT0 = ft0.posZ(); posZFT0valid = ft0.isValidTime(); + timeFT0A = ft0.timeC(); + timeFT0C = ft0.timeA(); } else { multFT0A = -999.0f; multFT0C = -999.0f; + timeFT0A = -999.0f; + timeFT0C = -999.0f; } if (bc.has_fv0a()) { auto fv0 = bc.fv0a(); std::bitset<8> fV0Triggers = fv0.triggerMask(); multFV0TriggerBits = static_cast(fV0Triggers.to_ulong()); - for (auto amplitude : fv0.amplitude()) { + for (size_t ii = 0; ii < fv0.amplitude().size(); ii++) { + auto amplitude = fv0.amplitude()[ii]; + auto channel = fv0.channel()[ii]; multFV0A += amplitude; + static constexpr int MaxChannelIdInnerRingFV0 = 7; + if (channel > MaxChannelIdInnerRingFV0) { + multFV0AOuter += amplitude; + } } isFV0OrA = fV0Triggers[o2::fit::Triggers::bitA]; } else { @@ -225,15 +266,20 @@ struct MultiplicityExtraTable { std::bitset<8> fFDDTriggers = fdd.triggerMask(); multFDDTriggerBits = static_cast(fFDDTriggers.to_ulong()); - for (auto amplitude : fdd.chargeA()) { + for (const auto amplitude : fdd.chargeA()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFDDA += amplitude; } - for (auto amplitude : fdd.chargeC()) { + for (const auto amplitude : fdd.chargeC()) { // o2-linter: disable=const-ref-in-for-loop (cheap float copy) multFDDC += amplitude; } + + timeFDDA = fdd.timeA(); + timeFDDC = fdd.timeC(); } else { multFDDA = -999.0f; multFDDC = -999.0f; + timeFDDA = -999.0f; + timeFDDC = -999.0f; } if (bc.has_zdc()) { @@ -243,6 +289,8 @@ struct MultiplicityExtraTable { multZEM2 = bc.zdc().amplitudeZEM2(); multZPA = bc.zdc().amplitudeZPA(); multZPC = bc.zdc().amplitudeZPC(); + timeZPA = bc.zdc().timeZPA(); + timeZPC = bc.zdc().timeZPC(); } else { multZNA = -999.f; multZNC = -999.f; @@ -250,19 +298,53 @@ struct MultiplicityExtraTable { multZEM2 = -999.f; multZPA = -999.f; multZPC = -999.f; + timeZNA = -999.f; + timeZNC = -999.f; + timeZPA = -999.f; + timeZPC = -999.f; } bc2mult(bc2multArray[bc.globalIndex()]); multBC( - tru(multFT0A), tru(multFT0C), - tru(posZFT0), posZFT0valid, tru(multFV0A), - tru(multFDDA), tru(multFDDC), tru(multZNA), tru(multZNC), tru(multZEM1), - tru(multZEM2), tru(multZPA), tru(multZPC), Tvx, isFV0OrA, - multFV0TriggerBits, multFT0TriggerBits, multFDDTriggerBits, multBCTriggerMask, collidingBC, + tru(multFT0A), + tru(multFT0C), + tru(multFV0A), + tru(multFDDA), + tru(multFDDC), + tru(multZNA), + tru(multZNC), + tru(multZEM1), + tru(multZEM2), + tru(multZPA), + tru(multZPC), + tru(multFV0AOuter), + tru(multFT0AOuter), + tru(multFT0COuter)); + + multBcSel( + bc.selection_raw(), + bc.rct_raw(), + bc.flags(), bc.timestamp(), - bc.flags()); - - multBcSel(bc.selection_raw()); + tru(posZFT0), + posZFT0valid, + multFV0TriggerBits, + multFT0TriggerBits, + multFDDTriggerBits, + multBCTriggerMask, + collidingBC, + tvx, + isFV0OrA); + + timeBC( + timeFT0A, + timeFT0C, + timeFDDA, + timeFDDC, + timeZNA, + timeZNC, + timeZPA, + timeZPC); } } @@ -278,16 +360,24 @@ struct MultiplicityExtraTable { float deltaPrevious = 1e+6, deltaPrePrevious = 1e+6; float deltaNext = 1e+6, deltaNeNext = 1e+6; for (const auto& collision : collisions) { - int ii = collision.globalIndex(); - - if (ii - 1 >= 0) - deltaPrevious = timeArray[ii] - timeArray[ii - 1]; - if (ii - 2 >= 0) - deltaPrePrevious = timeArray[ii] - timeArray[ii - 2]; - if (ii + 1 < collisions.size()) - deltaNext = timeArray[ii + 1] - timeArray[ii]; - if (ii + 2 < collisions.size()) - deltaNeNext = timeArray[ii + 2] - timeArray[ii]; + const int thisCollision = collision.globalIndex(); + const int prevCollision = thisCollision - 1; + const int prevPrevCollision = thisCollision - 2; + const int nextCollision = thisCollision + 1; + const int nextNextCollision = thisCollision + 2; + + if (prevCollision >= 0) { + deltaPrevious = timeArray[thisCollision] - timeArray[prevCollision]; + } + if (prevPrevCollision >= 0) { + deltaPrePrevious = timeArray[thisCollision] - timeArray[prevPrevCollision]; + } + if (nextCollision < collisions.size()) { + deltaNext = timeArray[nextCollision] - timeArray[thisCollision]; + } + if (nextNextCollision < collisions.size()) { + deltaNeNext = timeArray[nextNextCollision] - timeArray[thisCollision]; + } multNeigh(deltaPrePrevious, deltaPrevious, deltaNext, deltaNeNext); } @@ -313,5 +403,5 @@ struct MultiplicityExtraTable { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"multiplicity-extra-table"})}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/Common/TableProducer/multiplicityTable.cxx b/Common/TableProducer/multiplicityTable.cxx deleted file mode 100644 index 2c9ff5df025..00000000000 --- a/Common/TableProducer/multiplicityTable.cxx +++ /dev/null @@ -1,844 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -/// \file multiplicityTable.cxx -/// \brief Produces multiplicity tables -/// -/// \author ALICE -/// - -#include "PWGMM/Mult/DataModel/bestCollisionTable.h" - -#include "Common/Core/MetadataHelper.h" -#include "Common/Core/TableHelper.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -o2::common::core::MetadataHelper metadataInfo; // Metadata helper - -static constexpr int kFV0Mults = 0; -static constexpr int kFT0Mults = 1; -static constexpr int kFDDMults = 2; -static constexpr int kZDCMults = 3; -static constexpr int kTrackletMults = 4; -static constexpr int kTPCMults = 5; -static constexpr int kPVMults = 6; -static constexpr int kMultsExtra = 7; -static constexpr int kMultSelections = 8; -static constexpr int kFV0MultZeqs = 9; -static constexpr int kFT0MultZeqs = 10; -static constexpr int kFDDMultZeqs = 11; -static constexpr int kPVMultZeqs = 12; -static constexpr int kMultMCExtras = 13; -static constexpr int Ntables = 14; - -// Checking that the Zeq tables are after the normal ones -static_assert(kFV0Mults < kFV0MultZeqs); -static_assert(kFT0Mults < kFT0MultZeqs); -static_assert(kFDDMults < kFDDMultZeqs); -static_assert(kPVMults < kPVMultZeqs); - -static constexpr int Nparameters = 1; -static const std::vector tableNames{"FV0Mults", // 0 - "FT0Mults", // 1 - "FDDMults", // 2 - "ZDCMults", // 3 - "TrackletMults", // 4 - "TPCMults", // 5 - "PVMults", // 6 - "MultsExtra", // 7 - "MultSelections", // 8 - "FV0MultZeqs", // 9 - "FT0MultZeqs", // 10 - "FDDMultZeqs", // 11 - "PVMultZeqs", // 12 - "MultMCExtras"}; // 13 -static const std::vector parameterNames{"Enable"}; -static const int defaultParameters[Ntables][Nparameters]{{-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1}}; - -struct MultiplicityTable { - SliceCache cache; - Produces tableFV0; // 0 - Produces tableFV0AOuter; // 0-bis (produced with FV0) - Produces tableFT0; // 1 - Produces tableFDD; // 2 - Produces tableZDC; // 3 - Produces tableTracklet; // 4 - Produces tableTpc; // 5 - Produces tablePv; // 6 - Produces tableExtra; // 7 - Produces multSelections; // 8 - Produces tableFV0Zeqs; // 9 - Produces tableFT0Zeqs; // 10 - Produces tableFDDZeqs; // 11 - Produces tablePVZeqs; // 12 - Produces tableExtraMc; // 13 - Produces tableExtraMult2MCExtras; - Produces multHepMCHIs; // Not accounted for, produced using custom process function to avoid dependencies - Produces mftMults; // Not accounted for, produced using custom process function to avoid dependencies - Produces multsGlobal; // Not accounted for, produced based on process function processGlobalTrackingCounters - - // For vertex-Z corrections in calibration - Service ccdb; - Service pdg; - - using Run2Tracks = soa::Join; - Partition run2tracklets = (aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Run2Tracklet)); - Partition tracksWithTPC = (aod::track::tpcNClsFindable > (uint8_t)0); - Partition pvContribTracks = (nabs(aod::track::eta) < 0.8f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); - Partition pvContribTracksEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); - Preslice perCol = aod::track::collisionId; - Preslice perColIU = aod::track::collisionId; - Preslice perCollisionMFT = o2::aod::fwdtrack::collisionId; - - using BCsWithRun3Matchings = soa::Join; - - // Configurable - Configurable doVertexZeq{"doVertexZeq", 1, "if 1: do vertex Z eq mult table"}; - Configurable fractionOfEvents{"fractionOfEvents", 2.0, "Fractions of events to keep in case the QA is used"}; - Configurable> enabledTables{"enabledTables", - {defaultParameters[0], Ntables, Nparameters, tableNames, parameterNames}, - "Produce tables depending on needs. Values different than -1 override the automatic setup: the corresponding table can be set off (0) or on (1)"}; - - struct : ConfigurableGroup { - Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "The CCDB endpoint url address"}; - Configurable ccdbPath{"ccdbPath", "Centrality/Calibration", "The CCDB path for centrality/multiplicity information"}; - Configurable reconstructionPass{"reconstructionPass", "", {"Apass to use when fetching the calibration tables. Empty (default) does not check for any pass. Use `metadata` to fetch it from the AO2D metadata. Otherwise it will override the metadata."}}; - } ccdbConfig; - - Configurable produceHistograms{"produceHistograms", false, {"Option to produce debug histograms"}}; - Configurable autoSetupFromMetadata{"autoSetupFromMetadata", true, {"Autosetup the Run 2 and Run 3 processing from the metadata"}}; - - int mRunNumber; - bool lCalibLoaded; - TList* lCalibObjects; - TProfile* hVtxZFV0A; - TProfile* hVtxZFT0A; - TProfile* hVtxZFT0C; - TProfile* hVtxZFDDA; - - TProfile* hVtxZFDDC; - TProfile* hVtxZNTracks; - std::vector mEnabledTables; // Vector of enabled tables - - // Debug output - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::QAObject}; - OutputObj listCalib{"calib-list", OutputObjHandlingPolicy::QAObject}; - - unsigned int randomSeed = 0; - void init(InitContext& context) - { - // If both Run 2 and Run 3 data process flags are enabled then we check the metadata - if (autoSetupFromMetadata && metadataInfo.isFullyDefined()) { - LOG(info) << "Autosetting the processing from the metadata"; - if (doprocessRun2 == true && doprocessRun3 == true) { - if (metadataInfo.isRun3()) { - doprocessRun2.value = false; - } else { - doprocessRun3.value = false; - } - } - } - - randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); - if (doprocessRun2 == false && doprocessRun3 == false) { - LOGF(fatal, "Neither processRun2 nor processRun3 enabled. Please choose one."); - } - if (doprocessRun2 == true && doprocessRun3 == true) { - LOGF(fatal, "Cannot enable processRun2 and processRun3 at the same time. Please choose one."); - } - - bool tEnabled[Ntables] = {false}; - for (int i = 0; i < Ntables; i++) { - int f = enabledTables->get(tableNames[i].c_str(), "Enable"); - o2::common::core::enableFlagIfTableRequired(context, tableNames[i], f); - if (f == 1) { - tEnabled[i] = true; - mEnabledTables.push_back(i); - if (fractionOfEvents <= 1.f && (tableNames[i] != "MultsExtra")) { - LOG(fatal) << "Cannot have a fraction of events <= 1 and multiplicity table consumed."; - } - } - } - // Handle the custom cases. - if (tEnabled[kMultMCExtras]) { - if (enabledTables->get(tableNames[kMultMCExtras].c_str(), "Enable") == -1) { - doprocessMC.value = true; - LOG(info) << "Enabling MC processing due to " << tableNames[kMultMCExtras] << " table being enabled."; - } - } - - // Check that the tables are enabled consistenly - if (tEnabled[kFV0MultZeqs] && !tEnabled[kFV0Mults]) { // FV0 - mEnabledTables.push_back(kFV0Mults); - LOG(info) << "Cannot have the " << tableNames[kFV0MultZeqs] << " table enabled and not the one on " << tableNames[kFV0Mults] << ". Enabling it."; - } - if (tEnabled[kFT0MultZeqs] && !tEnabled[kFT0Mults]) { // FT0 - mEnabledTables.push_back(kFT0Mults); - LOG(info) << "Cannot have the " << tableNames[kFT0MultZeqs] << " table enabled and not the one on " << tableNames[kFT0Mults] << ". Enabling it."; - } - if (tEnabled[kFDDMultZeqs] && !tEnabled[kFDDMults]) { // FDD - mEnabledTables.push_back(kFDDMults); - LOG(info) << "Cannot have the " << tableNames[kFDDMultZeqs] << " table enabled and not the one on " << tableNames[kFDDMults] << ". Enabling it."; - } - if (tEnabled[kPVMultZeqs] && !tEnabled[kPVMults]) { // PV - mEnabledTables.push_back(kPVMults); - LOG(info) << "Cannot have the " << tableNames[kPVMultZeqs] << " table enabled and not the one on " << tableNames[kPVMults] << ". Enabling it."; - } - std::sort(mEnabledTables.begin(), mEnabledTables.end()); - - mRunNumber = 0; - lCalibLoaded = false; - lCalibObjects = nullptr; - hVtxZFV0A = nullptr; - hVtxZFT0A = nullptr; - hVtxZFT0C = nullptr; - hVtxZFDDA = nullptr; - hVtxZFDDC = nullptr; - hVtxZNTracks = nullptr; - - ccdb->setURL(ccdbConfig.ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); // don't fatal, please - exception is caught explicitly (as it should) - - listCalib.setObject(new TList); - if (!produceHistograms.value) { - return; - } - histos.add("FT0A", "FT0A vs FT0A eq.", HistType::kTH2D, {{1000, 0, 1000, "FT0A multiplicity"}, {1000, 0, 1000, "FT0A multiplicity eq."}}); - histos.add("FT0C", "FT0C vs FT0C eq.", HistType::kTH2D, {{1000, 0, 1000, "FT0C multiplicity"}, {1000, 0, 1000, "FT0C multiplicity eq."}}); - histos.add("FT0CMultvsPV", "FT0C vs mult.", HistType::kTH2D, {{1000, 0, 1000, "FT0C mult."}, {100, 0, 100, "PV mult."}}); - histos.add("FT0AMultvsPV", "FT0A vs mult.", HistType::kTH2D, {{1000, 0, 1000, "FT0A mult."}, {100, 0, 100, "PV mult."}}); - } - - /// Dummy process function for BCs, needed in case both Run2 and Run3 process functions are disabled - void process(aod::BCs const&) {} - - void processRun2(aod::Run2MatchedSparse::iterator const& collision, - Run2Tracks const&, - aod::BCs const&, - aod::Zdcs const&, - aod::FV0As const&, - aod::FV0Cs const&, - aod::FT0s const&) - { - float multFV0A = 0.f; - float multFV0C = 0.f; - float multFT0A = 0.f; - float multFT0C = 0.f; - float multFDDA = 0.f; - float multFDDC = 0.f; - float multZNA = 0.f; - float multZNC = 0.f; - - auto trackletsGrouped = run2tracklets->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - auto tracksGrouped = tracksWithTPC->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - int multTracklets = trackletsGrouped.size(); - int multTPC = tracksGrouped.size(); - int multNContribs = 0; - int multNContribsEta1 = 0; - int multNContribsEtaHalf = 0; - - if (collision.has_fv0a()) { - for (const auto& amplitude : collision.fv0a().amplitude()) { - multFV0A += amplitude; - } - } - if (collision.has_fv0c()) { - for (const auto& amplitude : collision.fv0c().amplitude()) { - multFV0C += amplitude; - } - } - if (collision.has_ft0()) { - auto ft0 = collision.ft0(); - for (const auto& amplitude : ft0.amplitudeA()) { - multFT0A += amplitude; - } - for (const auto& amplitude : ft0.amplitudeC()) { - multFT0C += amplitude; - } - } - if (collision.has_zdc()) { - auto zdc = collision.zdc(); - multZNA = zdc.energyCommonZNA(); - multZNC = zdc.energyCommonZNC(); - } - - // Try to do something Similar to https://github.com/alisw/AliPhysics/blob/22862a945004f719f8e9664c0264db46e7186a48/OADB/AliPPVsMultUtils.cxx#L541C26-L541C37 - for (const auto& tracklet : trackletsGrouped) { - if (std::abs(tracklet.eta()) < 1.0) { - multNContribsEta1++; - } - if (std::abs(tracklet.eta()) < 0.8) { - multNContribs++; - } - if (std::abs(tracklet.eta()) < 0.5) { - multNContribsEtaHalf++; - } - } - - LOGF(debug, "multFV0A=%5.0f multFV0C=%5.0f multFT0A=%5.0f multFT0C=%5.0f multFDDA=%5.0f multFDDC=%5.0f multZNA=%6.0f multZNC=%6.0f multTracklets=%i multTPC=%i multNContribsEta1=%i multNContribs=%i multNContribsEtaHalf=%i", multFV0A, multFV0C, multFT0A, multFT0C, multFDDA, multFDDC, multZNA, multZNC, multTracklets, multTPC, multNContribs, multNContribsEta1, multNContribsEtaHalf); - tableFV0(multFV0A, multFV0C); - tableFT0(multFT0A, multFT0C); - tableFDD(multFDDA, multFDDC); - tableZDC(multZNA, multZNC, 0.0f, 0.0f, 0.0f, 0.0f); - tableTracklet(multTracklets); - tableTpc(multTPC); - tablePv(multNContribs, multNContribsEta1, multNContribsEtaHalf); - } - - using Run3TracksIU = soa::Join; - Partition tracksIUWithTPC = (aod::track::tpcNClsFindable > (uint8_t)0); - Partition pvAllContribTracksIU = ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); - Partition pvContribTracksIU = (nabs(aod::track::eta) < 0.8f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); - Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); - Partition pvContribTracksIUEtaHalf = (nabs(aod::track::eta) < 0.5f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); - - void processRun3(soa::Join const& collisions, - Run3TracksIU const&, - BCsWithRun3Matchings const&, - aod::Zdcs const&, - aod::FV0As const&, - aod::FT0s const&, - aod::FDDs const&) - { - // reserve memory - for (const auto& i : mEnabledTables) { - switch (i) { - case kFV0Mults: // FV0 - tableFV0.reserve(collisions.size()); - tableFV0AOuter.reserve(collisions.size()); - break; - case kFT0Mults: // FT0 - tableFT0.reserve(collisions.size()); - break; - case kFDDMults: // FDD - tableFDD.reserve(collisions.size()); - break; - case kZDCMults: // ZDC - tableZDC.reserve(collisions.size()); - break; - case kTrackletMults: // Tracklets (Run 2 only, nothing to do) (to be removed!) - tableTracklet.reserve(collisions.size()); - break; - case kTPCMults: // TPC - tableTpc.reserve(collisions.size()); - break; - case kPVMults: // PV multiplicity - tablePv.reserve(collisions.size()); - break; - case kMultsExtra: // Extra information - tableExtra.reserve(collisions.size()); - break; - case kMultSelections: // Extra information - multSelections.reserve(collisions.size()); - break; - case kFV0MultZeqs: // Equalized multiplicity for FV0 - tableFV0Zeqs.reserve(collisions.size()); - break; - case kFT0MultZeqs: // Equalized multiplicity for FT0 - tableFT0Zeqs.reserve(collisions.size()); - break; - case kFDDMultZeqs: // Equalized multiplicity for FDD - tableFDDZeqs.reserve(collisions.size()); - break; - case kPVMultZeqs: // Equalized multiplicity for PV - tablePVZeqs.reserve(collisions.size()); - break; - case kMultMCExtras: // MC extra information (nothing to do in the data) - break; - default: - LOG(fatal) << "Unknown table requested: " << i; - break; - } - } - - // Initializing multiplicity values - float multFV0A = 0.f; - float multFV0AOuter = 0.f; - float multFV0C = 0.f; - float multFT0A = 0.f; - float multFT0C = 0.f; - float multFDDA = 0.f; - float multFDDC = 0.f; - float multZNA = -1.f; - float multZNC = -1.f; - float multZEM1 = -1.f; - float multZEM2 = -1.f; - float multZPA = -1.f; - float multZPC = -1.f; - - float multZeqFV0A = 0.f; - float multZeqFT0A = 0.f; - float multZeqFT0C = 0.f; - float multZeqFDDA = 0.f; - float multZeqFDDC = 0.f; - float multZeqNContribs = 0.f; - - for (auto const& collision : collisions) { - if ((fractionOfEvents < 1.f) && (static_cast(rand_r(&randomSeed)) / static_cast(RAND_MAX)) > fractionOfEvents) { // Skip events that are not sampled (only for the QA) - return; - } - int multNContribs = 0; - int multNContribsEta1 = 0; - int multNContribsEtaHalf = 0; - - /* check the previous run number */ - const auto& bc = collision.bc_as(); - if (doVertexZeq > 0) { - if (bc.runNumber() != mRunNumber) { - mRunNumber = bc.runNumber(); // mark this run as at least tried - if (ccdbConfig.reconstructionPass.value == "") { - lCalibObjects = ccdb->getForRun(ccdbConfig.ccdbPath, mRunNumber); - } else if (ccdbConfig.reconstructionPass.value == "metadata") { - std::map metadata; - metadata["RecoPassName"] = metadataInfo.get("RecoPassName"); - LOGF(info, "Loading CCDB for reconstruction pass (from metadata): %s", metadataInfo.get("RecoPassName")); - lCalibObjects = ccdb->getSpecificForRun(ccdbConfig.ccdbPath, mRunNumber, metadata); - } else { - std::map metadata; - metadata["RecoPassName"] = ccdbConfig.reconstructionPass.value; - LOGF(info, "Loading CCDB for reconstruction pass (from provided argument): %s", ccdbConfig.reconstructionPass.value); - lCalibObjects = ccdb->getSpecificForRun(ccdbConfig.ccdbPath, mRunNumber, metadata); - } - - if (lCalibObjects) { - if (produceHistograms) { - listCalib->Add(lCalibObjects->Clone(Form("%i", bc.runNumber()))); - } - - hVtxZFV0A = static_cast(lCalibObjects->FindObject("hVtxZFV0A")); - hVtxZFT0A = static_cast(lCalibObjects->FindObject("hVtxZFT0A")); - hVtxZFT0C = static_cast(lCalibObjects->FindObject("hVtxZFT0C")); - hVtxZFDDA = static_cast(lCalibObjects->FindObject("hVtxZFDDA")); - hVtxZFDDC = static_cast(lCalibObjects->FindObject("hVtxZFDDC")); - hVtxZNTracks = static_cast(lCalibObjects->FindObject("hVtxZNTracksPV")); - lCalibLoaded = true; - // Capture error - if (!hVtxZFV0A || !hVtxZFT0A || !hVtxZFT0C || !hVtxZFDDA || !hVtxZFDDC || !hVtxZNTracks) { - LOGF(error, "Problem loading CCDB objects! Please check"); - lCalibLoaded = false; - } - } else { - LOGF(error, "Problem loading CCDB object! Please check"); - lCalibLoaded = false; - } - } - } - - for (const auto& i : mEnabledTables) { - switch (i) { - case kFV0Mults: // FV0 - { - multFV0A = 0.f; - multFV0AOuter = 0.f; - multFV0C = 0.f; - // using FV0 row index from event selection task - if (collision.has_foundFV0()) { - const auto& fv0 = collision.foundFV0(); - for (size_t ii = 0; ii < fv0.amplitude().size(); ii++) { - auto amplitude = fv0.amplitude()[ii]; - auto channel = fv0.channel()[ii]; - multFV0A += amplitude; - if (channel > 7) { - multFV0AOuter += amplitude; - } - } - } else { - multFV0A = -999.f; - multFV0C = -999.f; - } - tableFV0(multFV0A, multFV0C); - tableFV0AOuter(multFV0AOuter); - LOGF(debug, "multFV0A=%5.0f multFV0C=%5.0f", multFV0A, multFV0C); - } break; - case kFT0Mults: // FT0 - { - multFT0A = 0.f; - multFT0C = 0.f; - // using FT0 row index from event selection task - if (collision.has_foundFT0()) { - const auto& ft0 = collision.foundFT0(); - for (const auto& amplitude : ft0.amplitudeA()) { - multFT0A += amplitude; - } - for (const auto& amplitude : ft0.amplitudeC()) { - multFT0C += amplitude; - } - } else { - multFT0A = -999.f; - multFT0C = -999.f; - } - tableFT0(multFT0A, multFT0C); - LOGF(debug, "multFT0A=%5.0f multFT0C=%5.0f", multFV0A, multFV0C); - } break; - case kFDDMults: // FDD - { - multFDDA = 0.f; - multFDDC = 0.f; - // using FDD row index from event selection task - if (collision.has_foundFDD()) { - const auto& fdd = collision.foundFDD(); - for (const auto& amplitude : fdd.chargeA()) { - multFDDA += amplitude; - } - for (const auto& amplitude : fdd.chargeC()) { - multFDDC += amplitude; - } - } else { - multFDDA = -999.f; - multFDDC = -999.f; - } - tableFDD(multFDDA, multFDDC); - LOGF(debug, "multFDDA=%5.0f multFDDC=%5.0f", multFDDA, multFDDC); - } break; - case kZDCMults: // ZDC - { - if (bc.has_zdc()) { - multZNA = bc.zdc().amplitudeZNA(); - multZNC = bc.zdc().amplitudeZNC(); - multZEM1 = bc.zdc().amplitudeZEM1(); - multZEM2 = bc.zdc().amplitudeZEM2(); - multZPA = bc.zdc().amplitudeZPA(); - multZPC = bc.zdc().amplitudeZPC(); - } else { - multZNA = -999.f; - multZNC = -999.f; - multZEM1 = -999.f; - multZEM2 = -999.f; - multZPA = -999.f; - multZPC = -999.f; - } - tableZDC(multZNA, multZNC, multZEM1, multZEM2, multZPA, multZPC); - LOGF(debug, "multZNA=%6.0f multZNC=%6.0f", multZNA, multZNC); - } break; - case kTrackletMults: // Tracklets (only Run2) nothing to do (to be removed!) - { - tableTracklet(0); - } break; - case kTPCMults: // TPC - { - const auto& tracksGrouped = tracksIUWithTPC->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - const int multTPC = tracksGrouped.size(); - tableTpc(multTPC); - LOGF(debug, "multTPC=%i", multTPC); - } break; - case kPVMults: // PV multiplicity - { - // use only one single grouping operation, then do loop - const auto& tracksThisCollision = pvContribTracksIUEta1.sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - multNContribsEta1 = tracksThisCollision.size(); - for (const auto& track : tracksThisCollision) { - if (std::abs(track.eta()) < 0.8) { - multNContribs++; - } - if (std::abs(track.eta()) < 0.5) { - multNContribsEtaHalf++; - } - } - - tablePv(multNContribs, multNContribsEta1, multNContribsEtaHalf); - LOGF(debug, "multNContribs=%i, multNContribsEta1=%i, multNContribsEtaHalf=%i", multNContribs, multNContribsEta1, multNContribsEtaHalf); - } break; - case kMultsExtra: // Extra - { - int nHasITS = 0, nHasTPC = 0, nHasTOF = 0, nHasTRD = 0; - int nITSonly = 0, nTPConly = 0, nITSTPC = 0; - const auto& pvAllContribsGrouped = pvAllContribTracksIU->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - const auto& tpcTracksGrouped = tracksIUWithTPC->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - - for (const auto& track : pvAllContribsGrouped) { - if (track.hasITS()) { - nHasITS++; - if (track.hasTPC()) - nITSTPC++; - if (!track.hasTPC() && !track.hasTOF() && !track.hasTRD()) - nITSonly++; - } - if (track.hasTPC()) { - nHasTPC++; - if (!track.hasITS() && !track.hasTOF() && !track.hasTRD()) - nTPConly++; - } - if (track.hasTOF()) - nHasTOF++; - if (track.hasTRD()) - nHasTRD++; - } - - int nAllTracksTPCOnly = 0; - int nAllTracksITSTPC = 0; - for (const auto& track : tpcTracksGrouped) { - if (track.hasITS()) { - nAllTracksITSTPC++; - } else { - nAllTracksTPCOnly++; - } - } - - tableExtra(collision.numContrib(), collision.chi2(), collision.collisionTimeRes(), - mRunNumber, collision.posZ(), collision.sel8(), - nHasITS, nHasTPC, nHasTOF, nHasTRD, nITSonly, nTPConly, nITSTPC, - nAllTracksTPCOnly, nAllTracksITSTPC, - collision.trackOccupancyInTimeRange(), - collision.ft0cOccupancyInTimeRange(), - collision.flags()); - } break; - case kMultSelections: // Multiplicity selections - { - multSelections(collision.selection_raw()); - } break; - case kFV0MultZeqs: // Z equalized FV0 - { - if (std::fabs(collision.posZ()) < 15.0f && lCalibLoaded) { - multZeqFV0A = hVtxZFV0A->Interpolate(0.0) * multFV0A / hVtxZFV0A->Interpolate(collision.posZ()); - } - tableFV0Zeqs(multZeqFV0A); - } break; - case kFT0MultZeqs: // Z equalized FT0 - { - if (std::fabs(collision.posZ()) < 15.0f && lCalibLoaded) { - multZeqFT0A = hVtxZFT0A->Interpolate(0.0) * multFT0A / hVtxZFT0A->Interpolate(collision.posZ()); - multZeqFT0C = hVtxZFT0C->Interpolate(0.0) * multFT0C / hVtxZFT0C->Interpolate(collision.posZ()); - } - if (produceHistograms.value) { - histos.fill(HIST("FT0A"), multFT0A, multZeqFT0A); - histos.fill(HIST("FT0C"), multFT0C, multZeqFT0C); - histos.fill(HIST("FT0AMultvsPV"), multZeqFT0A, multNContribs); - histos.fill(HIST("FT0CMultvsPV"), multZeqFT0C, multNContribs); - } - tableFT0Zeqs(multZeqFT0A, multZeqFT0C); - } break; - case kFDDMultZeqs: // Z equalized FDD - { - if (std::fabs(collision.posZ()) < 15.0f && lCalibLoaded) { - multZeqFDDA = hVtxZFDDA->Interpolate(0.0) * multFDDA / hVtxZFDDA->Interpolate(collision.posZ()); - multZeqFDDC = hVtxZFDDC->Interpolate(0.0) * multFDDC / hVtxZFDDC->Interpolate(collision.posZ()); - } - tableFDDZeqs(multZeqFDDA, multZeqFDDC); - } break; - case kPVMultZeqs: // Z equalized PV - { - if (std::fabs(collision.posZ()) < 15.0f && lCalibLoaded) { - multZeqNContribs = hVtxZNTracks->Interpolate(0.0) * multNContribs / hVtxZNTracks->Interpolate(collision.posZ()); - } - tablePVZeqs(multZeqNContribs); - } break; - case kMultMCExtras: // MC only (nothing to do) - { - } break; - default: // Default - { - LOG(fatal) << "Unknown table requested: " << i; - } break; - } - } - } - } - - // one loop better than multiple sliceby calls - // FIT FT0C: -3.3 < η < -2.1 - // FOT FT0A: 3.5 < η < 4.9 - Filter mcParticleFilter = (aod::mcparticle::eta < 7.0f) && (aod::mcparticle::eta > -7.0f); - using McParticlesFiltered = soa::Filtered; - - void processMC(aod::McCollision const& mcCollision, McParticlesFiltered const& mcParticles) - { - int multFT0A = 0; - int multFV0A = 0; - int multFT0C = 0; - int multFDDA = 0; - int multFDDC = 0; - int multBarrelEta05 = 0; - int multBarrelEta08 = 0; - int multBarrelEta10 = 0; - for (auto const& mcPart : mcParticles) { - if (!mcPart.isPhysicalPrimary()) { - continue; - } - - auto charge = 0.; - auto* p = pdg->GetParticle(mcPart.pdgCode()); - if (p != nullptr) { - charge = p->Charge(); - } - if (std::abs(charge) < 1e-3) { - continue; // reject neutral particles in counters - } - - if (std::abs(mcPart.eta()) < 1.0) { - multBarrelEta10++; - if (std::abs(mcPart.eta()) < 0.8) { - multBarrelEta08++; - if (std::abs(mcPart.eta()) < 0.5) { - multBarrelEta05++; - } - } - } - if (-3.3 < mcPart.eta() && mcPart.eta() < -2.1) - multFT0C++; - if (3.5 < mcPart.eta() && mcPart.eta() < 4.9) - multFT0A++; - if (2.2 < mcPart.eta() && mcPart.eta() < 5.0) - multFV0A++; - if (-6.9 < mcPart.eta() && mcPart.eta() < -4.9) - multFDDC++; - if (4.7 < mcPart.eta() && mcPart.eta() < 6.3) - multFDDA++; - } - tableExtraMc(multFT0A, multFT0C, multFV0A, multFDDA, multFDDC, multBarrelEta05, multBarrelEta08, multBarrelEta10, mcCollision.posZ()); - } - - void processMC2Mults(soa::Join::iterator const& collision) - { - tableExtraMult2MCExtras(collision.mcCollisionId()); // interlink - } - - Configurable minPtGlobalTrack{"minPtGlobalTrack", 0.15, "min. pT for global tracks"}; - Configurable maxPtGlobalTrack{"maxPtGlobalTrack", 1e+10, "max. pT for global tracks"}; - Configurable minNclsITSGlobalTrack{"minNclsITSGlobalTrack", 5, "min. number of ITS clusters for global tracks"}; - Configurable minNclsITSibGlobalTrack{"minNclsITSibGlobalTrack", 1, "min. number of ITSib clusters for global tracks"}; - - using Run3Tracks = soa::Join; - Partition pvContribGlobalTracksEta1 = (minPtGlobalTrack < aod::track::pt && aod::track::pt < maxPtGlobalTrack) && (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)) && requireQualityTracksInFilter(); - - void processHepMCHeavyIons(aod::HepMCHeavyIons const& hepmchis) - { - for (auto const& hepmchi : hepmchis) { - multHepMCHIs(hepmchi.mcCollisionId(), - hepmchi.ncollHard(), - hepmchi.npartProj(), - hepmchi.npartTarg(), - hepmchi.ncoll(), - hepmchi.impactParameter()); - } - } - - void processGlobalTrackingCounters(aod::Collision const& collision, soa::Join const& tracksIU, Run3Tracks const&) - { - // counter from Igor - int nGlobalTracks = 0; - int multNbrContribsEta05GlobalTrackWoDCA = 0; - int multNbrContribsEta08GlobalTrackWoDCA = 0; - int multNbrContribsEta10GlobalTrackWoDCA = 0; - - auto pvContribGlobalTracksEta1PerCollision = pvContribGlobalTracksEta1->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - - for (const auto& track : pvContribGlobalTracksEta1PerCollision) { - if (track.itsNCls() < minNclsITSGlobalTrack || track.itsNClsInnerBarrel() < minNclsITSibGlobalTrack) { - continue; - } - multNbrContribsEta10GlobalTrackWoDCA++; - - if (std::abs(track.eta()) < 0.8) { - multNbrContribsEta08GlobalTrackWoDCA++; - } - if (std::abs(track.eta()) < 0.5) { - multNbrContribsEta05GlobalTrackWoDCA++; - } - } - - for (const auto& track : tracksIU) { - if (std::fabs(track.eta()) < 0.8 && track.tpcNClsFound() >= 80 && track.tpcNClsCrossedRows() >= 100) { - if (track.isGlobalTrack()) { - nGlobalTracks++; - } - } - } - - LOGF(debug, "nGlobalTracks = %d, multNbrContribsEta08GlobalTrackWoDCA = %d, multNbrContribsEta10GlobalTrackWoDCA = %d, multNbrContribsEta05GlobalTrackWoDCA = %d", nGlobalTracks, multNbrContribsEta08GlobalTrackWoDCA, multNbrContribsEta10GlobalTrackWoDCA, multNbrContribsEta05GlobalTrackWoDCA); - - multsGlobal(nGlobalTracks, multNbrContribsEta08GlobalTrackWoDCA, multNbrContribsEta10GlobalTrackWoDCA, multNbrContribsEta05GlobalTrackWoDCA); - } - - void processRun3MFT(soa::Join::iterator const&, - o2::aod::MFTTracks const& mftTracks, - soa::SmallGroups const& retracks) - { - int nAllTracks = 0; - int nTracks = 0; - - for (const auto& track : mftTracks) { - if (track.nClusters() >= 5) { // hardcoded for now - nAllTracks++; - } - } - - if (retracks.size() > 0) { - for (const auto& retrack : retracks) { - auto track = retrack.mfttrack(); - if (track.nClusters() < 5) { - continue; // min cluster requirement - } - if ((track.eta() > -2.0f) && (track.eta() < -3.9f)) { - continue; // too far to be of true interest - } - if (std::abs(retrack.bestDCAXY()) > 2.0f) { - continue; // does not point to PV properly - } - nTracks++; - } - } - mftMults(nAllTracks, nTracks); - } - - // Process switches - PROCESS_SWITCH(MultiplicityTable, processRun2, "Produce Run 2 multiplicity tables. Autoset if both processRun2 and processRun3 are enabled", true); - PROCESS_SWITCH(MultiplicityTable, processRun3, "Produce Run 3 multiplicity tables. Autoset if both processRun2 and processRun3 are enabled", true); - PROCESS_SWITCH(MultiplicityTable, processGlobalTrackingCounters, "Produce Run 3 global counters", false); - PROCESS_SWITCH(MultiplicityTable, processMC, "Produce MC multiplicity tables", false); - PROCESS_SWITCH(MultiplicityTable, processMC2Mults, "Produce MC -> Mult map", false); - PROCESS_SWITCH(MultiplicityTable, processHepMCHeavyIons, "Produce MultHepMCHIs tables", false); - PROCESS_SWITCH(MultiplicityTable, processRun3MFT, "Produce MFT mult tables", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - // Parse the metadata - metadataInfo.initMetadata(cfgc); - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -} diff --git a/Common/TableProducer/muonRealignment.cxx b/Common/TableProducer/muonRealignment.cxx index 3fa9b462c0f..b0585f8ba9f 100644 --- a/Common/TableProducer/muonRealignment.cxx +++ b/Common/TableProducer/muonRealignment.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,6 @@ #include -#include #include #include @@ -104,7 +104,7 @@ struct MuonRealignment { TGeoManager* geoNew = nullptr; TGeoManager* geoRef = nullptr; - Preslice perMuon = aod::fwdtrkcl::fwdtrackId; + Preslice perMuon = aod::fwdtrkcl::fwdtrackId; int GetDetElemId(int iDetElemNumber) { @@ -289,16 +289,16 @@ struct MuonRealignment { fwdTrkCovRealignInfo.sigPhi = TMath::Sqrt(fwdtrack.getCovariances()(2, 2)); fwdTrkCovRealignInfo.sigTgl = TMath::Sqrt(fwdtrack.getCovariances()(3, 3)); fwdTrkCovRealignInfo.sig1Pt = TMath::Sqrt(fwdtrack.getCovariances()(4, 4)); - fwdTrkCovRealignInfo.rhoXY = (Char_t)(128. * fwdtrack.getCovariances()(0, 1) / (fwdTrkCovRealignInfo.sigX * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoPhiX = (Char_t)(128. * fwdtrack.getCovariances()(0, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rhoPhiY = (Char_t)(128. * fwdtrack.getCovariances()(1, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoTglX = (Char_t)(128. * fwdtrack.getCovariances()(0, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rhoTglY = (Char_t)(128. * fwdtrack.getCovariances()(1, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rhoTglPhi = (Char_t)(128. * fwdtrack.getCovariances()(2, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigPhi)); - fwdTrkCovRealignInfo.rho1PtX = (Char_t)(128. * fwdtrack.getCovariances()(0, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigX)); - fwdTrkCovRealignInfo.rho1PtY = (Char_t)(128. * fwdtrack.getCovariances()(1, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigY)); - fwdTrkCovRealignInfo.rho1PtPhi = (Char_t)(128. * fwdtrack.getCovariances()(2, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigPhi)); - fwdTrkCovRealignInfo.rho1PtTgl = (Char_t)(128. * fwdtrack.getCovariances()(3, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigTgl)); + fwdTrkCovRealignInfo.rhoXY = static_cast(128. * fwdtrack.getCovariances()(0, 1) / (fwdTrkCovRealignInfo.sigX * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoPhiX = static_cast(128. * fwdtrack.getCovariances()(0, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rhoPhiY = static_cast(128. * fwdtrack.getCovariances()(1, 2) / (fwdTrkCovRealignInfo.sigPhi * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoTglX = static_cast(128. * fwdtrack.getCovariances()(0, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rhoTglY = static_cast(128. * fwdtrack.getCovariances()(1, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rhoTglPhi = static_cast(128. * fwdtrack.getCovariances()(2, 3) / (fwdTrkCovRealignInfo.sigTgl * fwdTrkCovRealignInfo.sigPhi)); + fwdTrkCovRealignInfo.rho1PtX = static_cast(128. * fwdtrack.getCovariances()(0, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigX)); + fwdTrkCovRealignInfo.rho1PtY = static_cast(128. * fwdtrack.getCovariances()(1, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigY)); + fwdTrkCovRealignInfo.rho1PtPhi = static_cast(128. * fwdtrack.getCovariances()(2, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigPhi)); + fwdTrkCovRealignInfo.rho1PtTgl = static_cast(128. * fwdtrack.getCovariances()(3, 4) / (fwdTrkCovRealignInfo.sig1Pt * fwdTrkCovRealignInfo.sigTgl)); LOGF(debug, "TrackParm %d, x:%g y:%g z:%g phi:%g tgl:%g InvQPt:%g chi2:%g nClusters:%d", muon.globalIndex(), muon.x(), muon.y(), muon.z(), muon.phi(), muon.tgl(), muon.signed1Pt(), muon.chi2(), muon.nClusters()); LOGF(debug, "Re-aligned trackParm %d, x:%g y:%g z:%g phi:%g tgl:%g InvQPt:%g chi2:%g nClusters:%d removable:%d", muonRealignId, fwdtrack.getX(), fwdtrack.getY(), fwdtrack.getZ(), fwdtrack.getPhi(), fwdtrack.getTgl(), fwdtrack.getInvQPt(), fwdtrack.getTrackChi2(), convertedTrack.getNClusters(), removable); // Fill refitted track info diff --git a/Common/TableProducer/occupancyTableProducer.cxx b/Common/TableProducer/occupancyTableProducer.cxx index c3075e01d54..9289ccb7a16 100644 --- a/Common/TableProducer/occupancyTableProducer.cxx +++ b/Common/TableProducer/occupancyTableProducer.cxx @@ -433,51 +433,43 @@ struct OccupancyTableProducer { template void executeOccProducerProcessing(B const& BCs, C const& collisions, T const& tracks) { - if (tableMode == checkTableMode) { + if constexpr (tableMode == checkTableMode) { if (buildFlag00OccTable) { executeOccProducerProcessing(BCs, collisions, tracks); } else { executeOccProducerProcessing(BCs, collisions, tracks); } - } - if constexpr (tableMode == checkTableMode) { return; } - if (meanTableMode == checkTableMode) { + if constexpr (meanTableMode == checkTableMode) { if (buildFlag01OccMeanTable) { executeOccProducerProcessing(BCs, collisions, tracks); } else { executeOccProducerProcessing(BCs, collisions, tracks); } - } - if constexpr (meanTableMode == checkTableMode) { return; } - if (robustTableMode == checkTableMode) { + if constexpr (robustTableMode == checkTableMode) { if (buildFlag02OccRobustTable) { executeOccProducerProcessing(BCs, collisions, tracks); } else { executeOccProducerProcessing(BCs, collisions, tracks); } - } - if constexpr (robustTableMode == checkTableMode) { return; } - if (meanRobustTableMode == checkTableMode) { + if constexpr (meanRobustTableMode == checkTableMode) { if (buildFlag03OccMeanRobustTable) { executeOccProducerProcessing(BCs, collisions, tracks); } else { executeOccProducerProcessing(BCs, collisions, tracks); } - } - if constexpr (meanRobustTableMode == checkTableMode) { return; } - if constexpr (tableMode == checkTableMode || meanTableMode == checkTableMode || robustTableMode == checkTableMode || meanRobustTableMode == checkTableMode) { + if constexpr (tableMode == checkTableMode || meanTableMode == checkTableMode || robustTableMode == checkTableMode) { return; } else { @@ -802,7 +794,7 @@ struct OccupancyTableProducer { auto& vecOccPrimUnfm80 = occPrimUnfm80[i]; float meanOccPrimUnfm80 = TMath::Mean(vecOccPrimUnfm80.size(), vecOccPrimUnfm80.data()); - normalizeVector(vecOccPrimUnfm80, meanOccPrimUnfm80 / meanOccPrimUnfm80); + // normalizeVector(vecOccPrimUnfm80, meanOccPrimUnfm80 / meanOccPrimUnfm80); if constexpr (processMode == kProcessFullOccTableProducer || processMode == kProcessOnlyOccPrim || processMode == kProcessOnlyOccT0V0Prim || processMode == kProcessOnlyOccFDDT0V0Prim || processMode == kProcessOnlyOccNtrackDet || processMode == kProcessOnlyOccMultExtra) { if constexpr (tableMode == fillOccTable) { @@ -1699,20 +1691,22 @@ struct TrackMeanOccTableProducer { { occupancyQA.fill(HIST("occTrackQA/") + HIST(OccDire[occMode]) + HIST(OccNames[occName]), occValue); if (fillQA1) { - occupancyQA.fill(HIST("occTrackQA/LogRatio/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), std::log(std::abs(occValue / occRobustValue))); + float logRatio = std::log(occValue / occRobustValue); + float weighted = logRatio * std::sqrt(occValue + occRobustValue); + occupancyQA.fill(HIST("occTrackQA/LogRatio/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), logRatio); if (fillQA2) { int two = 2, twenty = 20, fifty = 50, twoHundred = 200; if (std::abs(std::log(occValue / occRobustValue)) < two) { // conditional filling start - occupancyQA.fill(HIST("occTrackQA/Condition1/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), (std::log(occValue / occRobustValue)) * std::sqrt(occValue + occRobustValue)); + occupancyQA.fill(HIST("occTrackQA/Condition1/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), weighted); if (std::abs(occRobustValue + occValue) > twoHundred) { - occupancyQA.fill(HIST("occTrackQA/Condition4/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), (std::log(occValue / occRobustValue)) * std::sqrt(occValue + occRobustValue)); - occupancyQA.fill(HIST("occTrackQA/Condition3/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), (std::log(occValue / occRobustValue)) * std::sqrt(occValue + occRobustValue)); - occupancyQA.fill(HIST("occTrackQA/Condition2/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), (std::log(occValue / occRobustValue)) * std::sqrt(occValue + occRobustValue)); + occupancyQA.fill(HIST("occTrackQA/Condition4/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), weighted); + occupancyQA.fill(HIST("occTrackQA/Condition3/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), weighted); + occupancyQA.fill(HIST("occTrackQA/Condition2/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), weighted); } else if (std::abs(occRobustValue + occValue) > fifty) { - occupancyQA.fill(HIST("occTrackQA/Condition3/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), (std::log(occValue / occRobustValue)) * std::sqrt(occValue + occRobustValue)); - occupancyQA.fill(HIST("occTrackQA/Condition2/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), (std::log(occValue / occRobustValue)) * std::sqrt(occValue + occRobustValue)); + occupancyQA.fill(HIST("occTrackQA/Condition3/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), weighted); + occupancyQA.fill(HIST("occTrackQA/Condition2/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), weighted); } else if (std::abs(occRobustValue + occValue) > twenty) { - occupancyQA.fill(HIST("occTrackQA/Condition2/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), (std::log(occValue / occRobustValue)) * std::sqrt(occValue + occRobustValue)); + occupancyQA.fill(HIST("occTrackQA/Condition2/") + HIST(OccDire[occRobustMode]) + HIST(OccDire[occMode]) + HIST(OccNames[occName]), weighted); } } // conditional filling end } @@ -1762,29 +1756,25 @@ struct TrackMeanOccTableProducer { return; } - if (meanTableMode == checkTableMode) { + if constexpr (meanTableMode == checkTableMode) { if (buildFlag00MeanTable) { executeTrackOccProducerProcessing(BCs, collisions, tracks, tracksQA, occsRobustT0V0Prim, occs, executeInThisBlock); } else { executeTrackOccProducerProcessing(BCs, collisions, tracks, tracksQA, occsRobustT0V0Prim, occs, executeInThisBlock); } - } - if constexpr (meanTableMode == checkTableMode) { return; } - if (weightMeanTableMode == checkTableMode) { + if constexpr (weightMeanTableMode == checkTableMode) { if (buildFlag01WeightMeanTable) { executeTrackOccProducerProcessing(BCs, collisions, tracks, tracksQA, occsRobustT0V0Prim, occs, executeInThisBlock); } else { executeTrackOccProducerProcessing(BCs, collisions, tracks, tracksQA, occsRobustT0V0Prim, occs, executeInThisBlock); } - } - if constexpr (weightMeanTableMode == checkTableMode) { return; } - if (qaMode == checkQAMode) { + if constexpr (qaMode == checkQAMode) { if (fillQA1 || fillQA2) { if (occsRobustT0V0Prim.size() == 0) { LOG(error) << "DEBUG :: ERROR ERROR ERROR :: OccsRobustT0V0Prim.size() == 0 :: Check \"occupancy-table-producer\" for \"buildOnlyOccsT0V0Prim == true\" & \"processOnlyOccT0V0PrimUnfm == true\""; @@ -1794,8 +1784,6 @@ struct TrackMeanOccTableProducer { } else { executeTrackOccProducerProcessing(BCs, collisions, tracks, tracksQA, occsRobustT0V0Prim, occs, executeInThisBlock); } - } - if constexpr (qaMode == checkQAMode) { return; } @@ -1803,7 +1791,7 @@ struct TrackMeanOccTableProducer { // BCs.bindExternalIndices(&occsNTrackDet); // BCs.bindExternalIndices(&occsRobust); - if constexpr (meanTableMode == checkTableMode || weightMeanTableMode == checkTableMode || qaMode == checkQAMode) { + if constexpr (meanTableMode == checkTableMode || weightMeanTableMode == checkTableMode) { return; } else { occupancyQA.fill(HIST("h_DFcount_Lvl2"), processMode); @@ -1958,16 +1946,16 @@ struct TrackMeanOccTableProducer { if (doAmbgUpdate) { // sKipping ambiguous tracks for now, will be updated in future continue; } - if (doCollisionUpdate || doAmbgUpdate) { // collision.globalIndex() != oldCollisionIndex){ //don't update if info is same as old collision + if (doCollisionUpdate) { // collision.globalIndex() != oldCollisionIndex){ //don't update if info is same as old collision if (doCollisionUpdate) { oldCollisionIndex = collision.globalIndex(); bc = collision.template bc_as(); } - if (doAmbgUpdate) { - // to be updated later - // bc = collisions.iteratorAt(2).bc_as(); - // bc = ambgTracks.iteratorAt(0).bc_as(); - } + // if (doAmbgUpdate) { + // to be updated later + // bc = collisions.iteratorAt(2).bc_as(); + // bc = ambgTracks.iteratorAt(0).bc_as(); + // } // LOG(info)<<" What happens in the case when the collision id is = -1 and it tries to obtain bc" getTimingInfo(bc, lastRun, nBCsPerTF, bcSOR, time, tfIdThis, bcInTF); } @@ -2421,7 +2409,6 @@ struct TrackMeanOccTableProducer { { occupancyQA.fill(HIST("h_DFcount_Lvl0"), kProcessNothing); return; - occupancyQA.fill(HIST("h_DFcount_Lvl1"), kProcessNothing); } PROCESS_SWITCH(TrackMeanOccTableProducer, processNothing, "process Nothing From Track Mean Occ Table Producer", true); diff --git a/Common/TableProducer/propagationService.cxx b/Common/TableProducer/propagationService.cxx index 25c951ef396..0c144bb43f9 100644 --- a/Common/TableProducer/propagationService.cxx +++ b/Common/TableProducer/propagationService.cxx @@ -77,6 +77,7 @@ struct propagationService { o2::pwglf::strangenessbuilder::v0Configurables v0BuilderOpts; o2::pwglf::strangenessbuilder::cascadeConfigurables cascadeBuilderOpts; o2::pwglf::strangenessbuilder::preSelectOpts preSelectOpts; + o2::pwglf::strangenessbuilder::eventSelectOpts eventSelectOpts; o2::pwglf::strangenessbuilder::BuilderModule strangenessBuilderModule; // the track tuner object -> needs to be here as it inherits from ConfigurableGroup (+ has its own copy of ccdbApi) @@ -99,7 +100,7 @@ struct propagationService { // task-specific trackPropagation.init(trackPropagationConfigurables, trackTunerObj, histos, initContext); - strangenessBuilderModule.init(baseOpts, v0BuilderOpts, cascadeBuilderOpts, preSelectOpts, histos, initContext); + strangenessBuilderModule.init(baseOpts, v0BuilderOpts, cascadeBuilderOpts, preSelectOpts, eventSelectOpts, histos, initContext); } void processRealData(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtIU const& tracks, aod::BCsWithTimestamps const& bcs) diff --git a/Common/TableProducer/propagationServiceRun2.cxx b/Common/TableProducer/propagationServiceRun2.cxx index 85dd2d0942b..4266acaaa0d 100644 --- a/Common/TableProducer/propagationServiceRun2.cxx +++ b/Common/TableProducer/propagationServiceRun2.cxx @@ -75,6 +75,7 @@ struct propagationServiceRun2 { o2::pwglf::strangenessbuilder::v0Configurables v0BuilderOpts; o2::pwglf::strangenessbuilder::cascadeConfigurables cascadeBuilderOpts; o2::pwglf::strangenessbuilder::preSelectOpts preSelectOpts; + o2::pwglf::strangenessbuilder::eventSelectOpts eventSelectOpts; o2::pwglf::strangenessbuilder::BuilderModule strangenessBuilderModule; // registry @@ -89,7 +90,7 @@ struct propagationServiceRun2 { ccdb->setFatalWhenNull(false); // task-specific - strangenessBuilderModule.init(baseOpts, v0BuilderOpts, cascadeBuilderOpts, preSelectOpts, histos, initContext); + strangenessBuilderModule.init(baseOpts, v0BuilderOpts, cascadeBuilderOpts, preSelectOpts, eventSelectOpts, histos, initContext); } void processRealData(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, FullTracksExt const& tracks, aod::BCsWithTimestamps const& bcs) diff --git a/Common/TableProducer/propagationServiceV2.cxx b/Common/TableProducer/propagationServiceV2.cxx new file mode 100644 index 00000000000..c22544027fa --- /dev/null +++ b/Common/TableProducer/propagationServiceV2.cxx @@ -0,0 +1,186 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file propagationServiceV2.cxx +/// \brief V2: every conditions object sourced from declarative CCDB tables, so the task +/// itself performs no CCDB query. +/// \author ALICE + +//=============================================================== +// +// Merged track propagation + strangeness building task +// +// Provides a common task to deal with track propagation and +// strangeness building in a single DPL device that is particularly +// adequate for pipelining. +// +//=============================================================== + +#include "PWGLF/Utils/strangenessBuilderModule.h" + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/GloCCDBObjects.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TpcCCDBObjects.h" +#include "Common/DataModel/TrackTunerCCDBObjects.h" +#include "Common/Tools/TrackPropagationModule.h" +#include "Common/Tools/TrackTuner.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; + +// use parameters + cov mat non-propagated, aux info + (extension propagated) +using FullTracksExt = soa::Join; +using FullTracksExtIU = soa::Join; +using FullTracksExtWithPID = soa::Join; +using FullTracksExtIUWithPID = soa::Join; +using FullTracksExtLabeled = soa::Join; +using FullTracksExtLabeledIU = soa::Join; +using FullTracksExtLabeledWithPID = soa::Join; +using FullTracksExtLabeledIUWithPID = soa::Join; +using TracksWithExtra = soa::Join; + +// For dE/dx association in pre-selection +using TracksExtraWithPID = soa::Join; + +struct propagationServiceV2 { + // No CCDB client of any kind: every conditions object this task consumes — magnetic + // field, mean vertex, material LUT, TPC drift correction and the two TrackTuner + // calibrations — arrives as a declarative CCDB column, so the task issues no query. + // + // No ConfigurableCCDBPath is declared either: ArrowSupport already registers a + // "ccdb:fXxx" option per declared column on the CCDB fetcher device, defaulting to the + // column's own query string. A task-side ConfigurableCCDBPath would only re-supply that + // identical default. Override a path with --ccdb:fMatLUT (and friends). + + // Everything this task needs is read straight off the CCDB columns at the point of + // use; no StandardCCDBLoader, and no CCDB query of its own. The single piece of + // retained state is the run number, needed only to avoid re-installing the magnetic + // field: that one is not a lookup but global state in the Propagator / + // TGeoGlobalMagField singletons, and installing it rebuilds or rescales the field map. + int mRunNumber = -1; + + // boilerplate: strangeness builder stuff + o2::pwglf::strangenessbuilder::products products; + o2::pwglf::strangenessbuilder::coreConfigurables baseOpts; + o2::pwglf::strangenessbuilder::v0Configurables v0BuilderOpts; + o2::pwglf::strangenessbuilder::cascadeConfigurables cascadeBuilderOpts; + o2::pwglf::strangenessbuilder::preSelectOpts preSelectOpts; + o2::pwglf::strangenessbuilder::eventSelectOpts eventSelectOpts; + o2::pwglf::strangenessbuilder::BuilderModule strangenessBuilderModule; + + // the track tuner object -> needs to be here as it inherits from ConfigurableGroup (+ has its own copy of ccdbApi) + TrackTuner trackTunerObj; + + // track propagation + o2::common::TrackPropagationProducts trackPropagationProducts; + o2::common::TrackPropagationConfigurables trackPropagationConfigurables; + o2::common::TrackPropagationModule trackPropagation; + + using BCsWithCCDB = soa::Join; + + // registry + HistogramRegistry histos{"histos"}; + + void init(o2::framework::InitContext& initContext) + { + // task-specific + trackPropagation.init(trackPropagationConfigurables, trackTunerObj, histos, initContext, /*calibFromCCDBColumns=*/true); + strangenessBuilderModule.init(baseOpts, v0BuilderOpts, cascadeBuilderOpts, preSelectOpts, eventSelectOpts, histos, initContext); + } + + /// Install into the Propagator the two things which are global state rather than + /// values: the magnetic field and the material LUT. + template + void initPropagator(TBC const& bc0) + { + if (mRunNumber != bc0.runNumber()) { + LOG(info) << "Setting B-field to current " << bc0.grpMagField().getL3Current() << " A for run " << bc0.runNumber() << " from GRPMagField CCDB column"; + o2::base::Propagator::initFieldFromGRP(&bc0.grpMagField()); + mRunNumber = bc0.runNumber(); + } + // A pointer store, so it costs nothing to redo every timeframe — and doing so + // means a relocated column buffer is picked up for free instead of dangling. + // The column's finaliser has already run MatLayerCylSet::rectifyPtrFromFile. + o2::base::Propagator::Instance()->setMatLUT(&bc0.matLUT()); + } + + void processRealData(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtIU const& tracks, BCsWithCCDB const& bcs) + { + if (bcs.size() == 0) { + return; + } + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); + } + + void processMonteCarlo(soa::Join const& collisions, aod::McCollisions const& mccollisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtLabeledIU const& tracks, BCsWithCCDB const& bcs, aod::McParticles const& mcParticles) + { + if (bcs.size() == 0) { + return; + } + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + } + + void processRealDataWithPID(soa::Join const& collisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtIUWithPID const& tracks, BCsWithCCDB const& bcs) + { + if (bcs.size() == 0) { + return; + } + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, static_cast(nullptr), v0s, cascades, trackedCascades, tracks, bcs, static_cast(nullptr), products); + } + + void processMonteCarloWithPID(soa::Join const& collisions, aod::McCollisions const& mccollisions, aod::V0s const& v0s, aod::Cascades const& cascades, aod::TrackedCascades const& trackedCascades, FullTracksExtLabeledIUWithPID const& tracks, BCsWithCCDB const& bcs, aod::McParticles const& mcParticles) + { + if (bcs.size() == 0) { + return; + } + auto bc0 = bcs.begin(); + initPropagator(bc0); + trackPropagation.fillTrackTables(trackPropagationConfigurables, trackTunerObj, bc0.runNumber(), &bc0.meanVertex(), &bc0.trackTunerDca(), &bc0.trackTunerQOverPt(), collisions, tracks, trackPropagationProducts, histos); + strangenessBuilderModule.dataProcess(histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + } + + PROCESS_SWITCH(propagationServiceV2, processRealData, "process real data", true); + PROCESS_SWITCH(propagationServiceV2, processMonteCarlo, "process monte carlo", false); + PROCESS_SWITCH(propagationServiceV2, processRealDataWithPID, "process real data", false); + PROCESS_SWITCH(propagationServiceV2, processMonteCarloWithPID, "process monte carlo", false); +}; + +//**************************************************************************************** +/** + * Workflow definition. + */ +//**************************************************************************************** +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + return workflow; +} diff --git a/Common/TableProducer/qVectorsTable.cxx b/Common/TableProducer/qVectorsTable.cxx index 273394a8e67..b4d9e1cb7de 100644 --- a/Common/TableProducer/qVectorsTable.cxx +++ b/Common/TableProducer/qVectorsTable.cxx @@ -19,6 +19,7 @@ /// #include "Common/Core/EventPlaneHelper.h" +#include "Common/Core/MetadataHelper.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" @@ -45,11 +46,11 @@ #include #include -#include #include #include #include +#include #include #include #include @@ -59,33 +60,45 @@ using namespace o2; using namespace o2::framework; +o2::common::core::MetadataHelper metadataInfo; // Metadata helper + using MyCollisions = soa::Join; using MyTracks = soa::Join; -struct qVectorsTable { - enum { +struct QVectorsTable { + enum Detectors { kFT0C = 0, kFT0A = 1, kFT0M, kFV0A, - kTPCpos, - kTPCneg, - kTPCall + kTPCPos, + kTPCNeg, + kTPCAll, + kNDetectors + }; + enum Corrections { + kNoCorr = 0, + kRecenter, + kTwist, + kRescale, + kNCorrections + }; + enum MultNorms { + kNoNorm = 0, + kScalarProd, + kEsE, + kMultNormTypes }; // Configurables. struct : ConfigurableGroup { - Configurable cfgURL{"cfgURL", - "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; - Configurable nolaterthan{"ccdb-no-later-than", - std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), - "Latest acceptable timestamp of creation for the object"}; + Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; + Configurable ccdbNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; } cfgCcdbParam; - Configurable cfgCentEsti{"cfgCentEsti", - 2, "Centrality estimator (Run3): 0 = FT0M, 1 = FT0A, 2 = FT0C, 3 = FV0A"}; + Configurable cfgCentEsti{"cfgCentEsti", 2, "Centrality estimator (Run3): 0 = FT0M, 1 = FT0A, 2 = FT0C, 3 = FV0A"}; Configurable cfgMinPtOnTPC{"cfgMinPtOnTPC", 0.15, "minimum transverse momentum selection for TPC tracks participating in Q-vector reconstruction"}; Configurable cfgMaxPtOnTPC{"cfgMaxPtOnTPC", 5., "maximum transverse momentum selection for TPC tracks participating in Q-vector reconstruction"}; @@ -96,9 +109,10 @@ struct qVectorsTable { Configurable> cfgnMods{"cfgnMods", {2, 3}, "Modulation of interest"}; Configurable cfgMaxCentrality{"cfgMaxCentrality", 100.f, "max. centrality for Q vector calibration"}; + Configurable autodetectApass{"autodetectApass", false, "Automatically detect the analysis pass of the Qvector corrections"}; Configurable useCorrectionForRun{"useCorrectionForRun", true, "Get Qvector corrections based on run number instead of timestamp"}; Configurable cfgGainEqPath{"cfgGainEqPath", "Users/j/junlee/Qvector/GainEq", "CCDB path for gain equalization constants"}; - Configurable cfgQvecCalibPath{"cfgQvecCalibPath", "Analysis/EventPlane/QVecCorrections", "CCDB pasth for Q-vecteor calibration constants"}; + Configurable cfgQvecCalibPath{"cfgQvecCalibPath", "Analysis/EventPlane/QVecCorrections", "CCDB path for Q-vector calibration constants"}; Configurable cfgShiftCorr{"cfgShiftCorr", false, "configurable flag for shift correction"}; Configurable cfgShiftPath{"cfgShiftPath", "", "CCDB path for shift correction"}; @@ -109,9 +123,10 @@ struct qVectorsTable { Configurable cfgUseFT0A{"cfgUseFT0A", false, "Initial value for using FT0A. By default obtained from DataModel."}; Configurable cfgUseFT0M{"cfgUseFT0M", false, "Initial value for using FT0M. By default obtained from DataModel."}; Configurable cfgUseFV0A{"cfgUseFV0A", false, "Initial value for using FV0A. By default obtained from DataModel."}; - Configurable cfgUseTPCpos{"cfgUseTPCpos", false, "Initial value for using TPCpos. By default obtained from DataModel."}; - Configurable cfgUseTPCneg{"cfgUseTPCneg", false, "Initial value for using TPCneg. By default obtained from DataModel."}; - Configurable cfgUseTPCall{"cfgUseTPCall", false, "Initial value for using TPCall. By default obtained from DataModel."}; + Configurable cfgUseTPCpos{"cfgUseTPCpos", false, "Initial value for using TPCPos. By default obtained from DataModel."}; + Configurable cfgUseTPCneg{"cfgUseTPCneg", false, "Initial value for using TPCNeg. By default obtained from DataModel."}; + Configurable cfgUseTPCall{"cfgUseTPCall", false, "Initial value for using TPCAll. By default obtained from DataModel."}; + Configurable cfgProduceRedQVecs{"cfgProduceRedQVecs", false, "Produce reduced Q-vectors for Event-Shape Engineering"}; // Table. Produces qVector; @@ -119,27 +134,45 @@ struct qVectorsTable { Produces qVectorFT0A; Produces qVectorFT0M; Produces qVectorFV0A; - Produces qVectorTPCpos; - Produces qVectorTPCneg; - Produces qVectorTPCall; + Produces qVectorTPCPos; + Produces qVectorTPCNeg; + Produces qVectorTPCAll; Produces qVectorFT0CVec; Produces qVectorFT0AVec; Produces qVectorFT0MVec; Produces qVectorFV0AVec; - Produces qVectorTPCposVec; - Produces qVectorTPCnegVec; - Produces qVectorTPCallVec; - - std::vector FT0RelGainConst{}; - std::vector FV0RelGainConst{}; + Produces qVectorTPCPosVec; + Produces qVectorTPCNegVec; + Produces qVectorTPCAllVec; + + Produces eseQVector; + Produces eseQVectorFT0C; + Produces eseQVectorFT0A; + Produces eseQVectorFT0M; + Produces eseQVectorFV0A; + Produces eseQVectorTPCPos; + Produces eseQVectorTPCNeg; + Produces eseQVectorTPCAll; + + Produces eseQVectorFT0CVec; + Produces eseQVectorFT0AVec; + Produces eseQVectorFT0MVec; + Produces eseQVectorFV0AVec; + Produces eseQVectorTPCPosVec; + Produces eseQVectorTPCNegVec; + Produces eseQVectorTPCAllVec; + Produces eseQVectorPerc; + + std::vector ft0RelGainConst{}; + std::vector fv0RelGainConst{}; // Enable access to the CCDB for the offset and correction constants and save them // in dedicated variables. Service ccdb; // geometry instances for V0 and T0 - o2::fv0::Geometry* fv0geom; + o2::fv0::Geometry* fv0geom{nullptr}; o2::ft0::Geometry ft0geom; // Variables for other classes. @@ -147,11 +180,16 @@ struct qVectorsTable { HistogramRegistry histosQA{"histosQA", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + const float minAmplitude = 1.0e-8f; int runNumber{-1}; - float cent; + float cent{-999.f}; + const int nShiftIndex = 10; + const float trackEtaMin = 0.1; - std::vector objQvec{}; - std::vector shiftprofile{}; + std::vector corrsQvecSp{}; + std::vector corrsQvecEse{}; + std::vector shiftProfileSp{}; + std::vector shiftProfileEse{}; // Deprecated, will be removed in future after transition time // Configurable cfgUseBPos{"cfgUseBPos", false, "Initial value for using BPos. By default obtained from DataModel."}; @@ -193,8 +231,8 @@ struct qVectorsTable { goto allDetectorsInUse; // Added to break from nested loop if all detectors are in use. } for (auto const& det : useDetector) { - std::string table_name_with_vector = det.first; // for replacing s with Vecs at the end. - if (input.matcher.binding == det.first || input.matcher.binding == table_name_with_vector.replace(table_name_with_vector.size() - 1, 1, "Vecs")) { + std::string tableNameWithVector = det.first; // for replacing s with Vecs at the end. + if (input.matcher.binding == det.first || input.matcher.binding == tableNameWithVector.replace(tableNameWithVector.size() - 1, 1, "Vecs")) { useDetector[det.first.data()] = true; LOGF(info, Form("Using detector: %s.", det.first.data())); } @@ -208,7 +246,7 @@ struct qVectorsTable { ccdb->setURL(cfgCcdbParam.cfgURL); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(cfgCcdbParam.nolaterthan.value); + ccdb->setCreatedNotAfter(cfgCcdbParam.ccdbNoLaterThan.value); ccdb->setFatalWhenNull(false); AxisSpec axisPt = {40, 0.0, 4.0}; @@ -220,6 +258,7 @@ struct qVectorsTable { AxisSpec axisChID = {220, 0, 220}; fv0geom = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); + ft0geom.calculateChannelCenter(); histosQA.add("ChTracks", "", {HistType::kTHnSparseF, {axisPt, axisEta, axisPhi, axixCent}}); histosQA.add("FT0Amp", "", {HistType::kTH2F, {axisFITamp, axisChID}}); @@ -230,10 +269,10 @@ struct qVectorsTable { void initCCDB(aod::BCsWithTimestamps::iterator const& bc) { - FT0RelGainConst.clear(); - FV0RelGainConst.clear(); - FT0RelGainConst = {}; - FV0RelGainConst = {}; + ft0RelGainConst.clear(); + fv0RelGainConst.clear(); + ft0RelGainConst = {}; + fv0RelGainConst = {}; std::string fullPath; @@ -257,58 +296,136 @@ struct qVectorsTable { LOGF(fatal, "Could not get the alignment parameters for FV0."); } - objQvec.clear(); + corrsQvecSp.clear(); + std::string periodFolder; + if (autodetectApass) { + // Construct a subfolder like LHCXX_apassY + int maxCharactersLHCYear = 5; // LHCXX + periodFolder = "/" + metadataInfo.get("LPMProductionTag").substr(0, maxCharactersLHCYear) + + "_" + metadataInfo.get("RecoPassName"); + } for (std::size_t i = 0; i < cfgnMods->size(); i++) { int ind = cfgnMods->at(i); fullPath = cfgQvecCalibPath; + if (autodetectApass) { + fullPath += periodFolder; + } fullPath += "/v"; fullPath += std::to_string(ind); - auto objqvec = getForTsOrRun(fullPath, timestamp, runnumber); - if (!objqvec) { + auto modeCorrQvecSp = getForTsOrRun(fullPath, timestamp, runnumber); + if (!modeCorrQvecSp) { fullPath = cfgQvecCalibPath; + if (autodetectApass) { + fullPath += periodFolder; + } fullPath += "/v2"; - objqvec = getForTsOrRun(fullPath, timestamp, runnumber); + modeCorrQvecSp = getForTsOrRun(fullPath, timestamp, runnumber); + } + if (!modeCorrQvecSp) { + LOGF(info, "Could not get the correction histograms for Q-vectors for mode %d. Setting to no correction.", ind); + modeCorrQvecSp = new TH3F("modeCorrQvecSp", "modeCorrQvecSp", 1, 0, 1, 1, 0, 1, 1, 0, 1); + } + corrsQvecSp.push_back(modeCorrQvecSp); + } + + if (cfgProduceRedQVecs) { + corrsQvecEse.clear(); + for (std::size_t i = 0; i < cfgnMods->size(); i++) { + int ind = cfgnMods->at(i); + fullPath = cfgQvecCalibPath; + if (autodetectApass) { + fullPath += periodFolder; + } + fullPath += "/eseq"; + fullPath += std::to_string(ind); + auto modeCorrQvecEse = getForTsOrRun(fullPath, timestamp, runnumber); + if (!modeCorrQvecEse) { + fullPath = cfgQvecCalibPath; + if (autodetectApass) { + fullPath += periodFolder; + } + fullPath += "/eseq2"; + modeCorrQvecEse = getForTsOrRun(fullPath, timestamp, runnumber); + } + if (!modeCorrQvecEse) { + LOGF(info, "Could not get the correction histograms for Q-vectors for mode %d. Setting to no correction.", ind); + modeCorrQvecEse = new TH3F("modeCorrQvecEse", "modeCorrQvecEse", 1, 0, 1, 1, 0, 1, 1, 0, 1); + } + corrsQvecEse.push_back(modeCorrQvecEse); } - objQvec.push_back(objqvec); } if (cfgShiftCorr) { - shiftprofile.clear(); + shiftProfileSp.clear(); for (std::size_t i = 0; i < cfgnMods->size(); i++) { int ind = cfgnMods->at(i); fullPath = cfgShiftPath; + if (autodetectApass) { + fullPath += periodFolder; + } fullPath += "/v"; fullPath += std::to_string(ind); auto objshift = getForTsOrRun(fullPath, timestamp, runnumber); - shiftprofile.push_back(objshift); + if (!objshift) { + LOGF(info, "Could not get the shift profile for Q-vectors for mode %d. Setting to no shift correction.", ind); + objshift = new TProfile3D("shiftProfileSp", "shiftProfileSp", 1, 0, 1, 1, 0, 1, 1, 0, 1); + } + shiftProfileSp.push_back(objshift); + } + + if (cfgProduceRedQVecs) { + shiftProfileEse.clear(); + for (std::size_t i = 0; i < cfgnMods->size(); i++) { + int ind = cfgnMods->at(i); + fullPath = cfgShiftPath; + if (autodetectApass) { + fullPath += periodFolder; + } + fullPath += "/eseq"; + fullPath += std::to_string(ind); + auto objshift = getForTsOrRun(fullPath, timestamp, runnumber); + if (!objshift) { + LOGF(info, "Could not get the shift profile for Q-vectors for mode %d. Setting to no shift correction.", ind); + objshift = new TProfile3D("shiftProfileEse", "shiftProfileEse", 1, 0, 1, 1, 0, 1, 1, 0, 1); + } + shiftProfileEse.push_back(objshift); + } } } fullPath = cfgGainEqPath; + if (autodetectApass) { + fullPath += periodFolder + "/GainEq"; + } fullPath += "/FT0"; - const auto objft0Gain = getForTsOrRun>(fullPath, timestamp, runnumber); + const int nPixelsFT0 = 208; + auto const* objft0Gain = getForTsOrRun>(fullPath, timestamp, runnumber); if (!objft0Gain || cfgCorrLevel == 0) { - for (auto i{0u}; i < 208; i++) { - FT0RelGainConst.push_back(1.); + for (auto i{0u}; i < nPixelsFT0; i++) { + ft0RelGainConst.push_back(1.); } } else { - FT0RelGainConst = *(objft0Gain); + ft0RelGainConst = *(objft0Gain); } fullPath = cfgGainEqPath; + if (autodetectApass) { + fullPath += periodFolder + "/GainEq"; + } fullPath += "/FV0"; - const auto objfv0Gain = getForTsOrRun>(fullPath, timestamp, runnumber); + const int nChannelsFV0 = 48; + auto const* objfv0Gain = getForTsOrRun>(fullPath, timestamp, runnumber); if (!objfv0Gain || cfgCorrLevel == 0) { - for (auto i{0u}; i < 48; i++) { - FV0RelGainConst.push_back(1.); + for (auto i{0u}; i < nChannelsFV0; i++) { + fv0RelGainConst.push_back(1.); } } else { - FV0RelGainConst = *(objfv0Gain); + fv0RelGainConst = *(objfv0Gain); } } template - bool SelTrack(const TrackType track) + bool selTrack(const TrackType& track) { if (track.pt() < cfgMinPtOnTPC) return false; @@ -335,31 +452,189 @@ struct qVectorsTable { /// Function to get corrections from CCDB eithr using the timestamp or the runnumber /// \param fullPath is the path to correction in CCDB /// \param timestamp is the collision timestamp - /// \param runNumber is the collision run number + /// \param runNb is the collision run number /// \return CCDB correction template - CorrectionType* getForTsOrRun(std::string const& fullPath, int64_t timestamp, int runNumber) + CorrectionType* getForTsOrRun(std::string const& fullPath, int64_t timestamp, int runNb) { if (useCorrectionForRun) { - return ccdb->getForRun(fullPath, runNumber); + return ccdb->getForRun(fullPath, runNb); } else { return ccdb->getForTimeStamp(fullPath, timestamp); } } + /// Function to normalize the q-vectors + /// \param qVecReNorm is the vector with the normalized real part of the q-vector for each detector and correction step + /// \param qVecImNorm is the vector with the normalized imaginary part of the q-vector for each detector and correction step + /// \param qVecReRaw is the vector with the raw real part of the q-vector for each detector and correction step + /// \param qVecImRaw is the vector with the raw imaginary part of the q-vector for each detector and correction step + /// \param qVecAmp is the vector with the amplitude of the q-vector for each detector and correction step + /// \param normType is the type of normalization to apply to the q-vectors + void normalizeQVec(std::vector& qVecReNorm, + std::vector& qVecImNorm, + std::vector const& qVecReRaw, + std::vector const& qVecImRaw, + std::vector const& qVecAmp, + MultNorms normType) + { + for (std::size_t i = 0; i < kNDetectors; i++) { + float qVecDetReNorm{-999.}, qVecDetImNorm{-999.}; + if (qVecAmp[i] > minAmplitude) { + switch (normType) { + case MultNorms::kScalarProd: + qVecDetReNorm = qVecReRaw[i] / qVecAmp[i]; + qVecDetImNorm = qVecImRaw[i] / qVecAmp[i]; + break; + case MultNorms::kEsE: + qVecDetReNorm = qVecReRaw[i] / std::sqrt(qVecAmp[i]); + qVecDetImNorm = qVecImRaw[i] / std::sqrt(qVecAmp[i]); + break; + default: + LOGP(fatal, "Undefined normalization type for Q-vector amplitude. Check the configuration."); + break; + } + } + for (int iCorr = 0; iCorr < kNCorrections; iCorr++) { + qVecReNorm.push_back(qVecDetReNorm); + qVecImNorm.push_back(qVecDetImNorm); + } + } + } + + /// Function to calculate the un-normalized q-vectors + /// \param centrality is the collision centrality + /// \param qVecRe is the vector with the real part of the q-vector for each detector and correction step + /// \param qVecIm is the vector with the imaginary part of the q-vector for each detector and correction step + /// \param histsCorrs is the vector with the histograms with the correction constants for each detector and correction step + /// \param nMode is the modulation of interest + void correctQVec(float centrality, std::vector& qVecRe, std::vector& qVecIm, TH3F* histsCorrs, std::vector& shiftProfile, int nMode) + { + int nCorrections = static_cast(kNCorrections); + if (centrality < cfgMaxCentrality) { + for (auto i{0u}; i < kTPCAll + 1; i++) { + int idxDet = i * kNCorrections; + helperEP.DoRecenter(qVecRe[idxDet + kRecenter], qVecIm[idxDet + kRecenter], + histsCorrs->GetBinContent(static_cast(centrality) + 1, 1, i + 1), histsCorrs->GetBinContent(static_cast(centrality) + 1, 2, i + 1)); + + helperEP.DoRecenter(qVecRe[idxDet + kTwist], qVecIm[idxDet + kTwist], + histsCorrs->GetBinContent(static_cast(centrality) + 1, 1, i + 1), histsCorrs->GetBinContent(static_cast(centrality) + 1, 2, i + 1)); + helperEP.DoTwist(qVecRe[idxDet + kTwist], qVecIm[idxDet + kTwist], + histsCorrs->GetBinContent(static_cast(centrality) + 1, 3, i + 1), histsCorrs->GetBinContent(static_cast(centrality) + 1, 4, i + 1)); + + helperEP.DoRecenter(qVecRe[idxDet + kRescale], qVecIm[idxDet + kRescale], + histsCorrs->GetBinContent(static_cast(centrality) + 1, 1, i + 1), histsCorrs->GetBinContent(static_cast(centrality) + 1, 2, i + 1)); + helperEP.DoTwist(qVecRe[idxDet + kRescale], qVecIm[idxDet + kRescale], + histsCorrs->GetBinContent(static_cast(centrality) + 1, 3, i + 1), histsCorrs->GetBinContent(static_cast(centrality) + 1, 4, i + 1)); + helperEP.DoRescale(qVecRe[idxDet + kRescale], qVecIm[idxDet + kRescale], + histsCorrs->GetBinContent(static_cast(centrality) + 1, 5, i + 1), histsCorrs->GetBinContent(static_cast(centrality) + 1, 6, i + 1)); + } + if (cfgShiftCorr) { + auto deltaPsiFT0C = 0.0; + auto deltaPsiFT0A = 0.0; + auto deltaPsiFT0M = 0.0; + auto deltaPsiFV0A = 0.0; + auto deltaPsiTPCPos = 0.0; + auto deltaPsiTPCNeg = 0.0; + auto deltaPsiTPCAll = 0.0; + + auto psiDefFT0C = std::atan2(qVecIm[kFT0C * nCorrections + kRescale], qVecRe[kFT0C * nCorrections + kRescale]) / static_cast(nMode); + auto psiDefFT0A = std::atan2(qVecIm[kFT0A * nCorrections + kRescale], qVecRe[kFT0A * nCorrections + kRescale]) / static_cast(nMode); + auto psiDefFT0M = std::atan2(qVecIm[kFT0M * nCorrections + kRescale], qVecRe[kFT0M * nCorrections + kRescale]) / static_cast(nMode); + auto psiDefFV0A = std::atan2(qVecIm[kFV0A * nCorrections + kRescale], qVecRe[kFV0A * nCorrections + kRescale]) / static_cast(nMode); + auto psiDefTPCPos = std::atan2(qVecIm[kTPCPos * nCorrections + kRescale], qVecRe[kTPCPos * nCorrections + kRescale]) / static_cast(nMode); + auto psiDefTPCNeg = std::atan2(qVecIm[kTPCNeg * nCorrections + kRescale], qVecRe[kTPCNeg * nCorrections + kRescale]) / static_cast(nMode); + auto psiDefTPCAll = std::atan2(qVecIm[kTPCAll * nCorrections + kRescale], qVecRe[kTPCAll * nCorrections + kRescale]) / static_cast(nMode); + + for (int iShift = 1; iShift <= nShiftIndex; iShift++) { + auto coeffShiftXFT0C = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFT0C, iShift - 0.5)); + auto coeffShiftYFT0C = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFT0C + 1, iShift - 0.5)); + auto coeffShiftXFT0A = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFT0A, iShift - 0.5)); + auto coeffShiftYFT0A = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFT0A + 1, iShift - 0.5)); + auto coeffShiftXFT0M = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFT0M, iShift - 0.5)); + auto coeffShiftYFT0M = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFT0M + 1, iShift - 0.5)); + auto coeffShiftXFV0A = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFV0A, iShift - 0.5)); + auto coeffShiftYFV0A = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kFV0A + 1, iShift - 0.5)); + auto coeffShiftXTPCPos = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kTPCPos, iShift - 0.5)); + auto coeffShiftYTPCPos = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kTPCPos + 1, iShift - 0.5)); + auto coeffShiftXTPCNeg = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kTPCNeg, iShift - 0.5)); + auto coeffShiftYTPCNeg = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kTPCNeg + 1, iShift - 0.5)); + auto coeffShiftXTPCAll = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kTPCAll, iShift - 0.5)); + auto coeffShiftYTPCAll = shiftProfile.at(nMode - 2)->GetBinContent(shiftProfile.at(nMode - 2)->FindBin(centrality, 2 * kTPCAll + 1, iShift - 0.5)); + + deltaPsiFT0C += ((2. / (1.0 * iShift)) * (-coeffShiftXFT0C * std::cos(iShift * static_cast(nMode) * psiDefFT0C) + coeffShiftYFT0C * std::sin(iShift * static_cast(nMode) * psiDefFT0C))) / static_cast(nMode); + deltaPsiFT0A += ((2. / (1.0 * iShift)) * (-coeffShiftXFT0A * std::cos(iShift * static_cast(nMode) * psiDefFT0A) + coeffShiftYFT0A * std::sin(iShift * static_cast(nMode) * psiDefFT0A))) / static_cast(nMode); + deltaPsiFT0M += ((2. / (1.0 * iShift)) * (-coeffShiftXFT0M * std::cos(iShift * static_cast(nMode) * psiDefFT0M) + coeffShiftYFT0M * std::sin(iShift * static_cast(nMode) * psiDefFT0M))) / static_cast(nMode); + deltaPsiFV0A += ((2. / (1.0 * iShift)) * (-coeffShiftXFV0A * std::cos(iShift * static_cast(nMode) * psiDefFV0A) + coeffShiftYFV0A * std::sin(iShift * static_cast(nMode) * psiDefFV0A))) / static_cast(nMode); + deltaPsiTPCPos += ((2. / (1.0 * iShift)) * (-coeffShiftXTPCPos * std::cos(iShift * static_cast(nMode) * psiDefTPCPos) + coeffShiftYTPCPos * std::sin(iShift * static_cast(nMode) * psiDefTPCPos))) / static_cast(nMode); + deltaPsiTPCNeg += ((2. / (1.0 * iShift)) * (-coeffShiftXTPCNeg * std::cos(iShift * static_cast(nMode) * psiDefTPCNeg) + coeffShiftYTPCNeg * std::sin(iShift * static_cast(nMode) * psiDefTPCNeg))) / static_cast(nMode); + deltaPsiTPCAll += ((2. / (1.0 * iShift)) * (-coeffShiftXTPCAll * std::cos(iShift * static_cast(nMode) * psiDefTPCAll) + coeffShiftYTPCAll * std::sin(iShift * static_cast(nMode) * psiDefTPCAll))) / static_cast(nMode); + } + + deltaPsiFT0C *= static_cast(nMode); + deltaPsiFT0A *= static_cast(nMode); + deltaPsiFT0M *= static_cast(nMode); + deltaPsiFV0A *= static_cast(nMode); + deltaPsiTPCPos *= static_cast(nMode); + deltaPsiTPCNeg *= static_cast(nMode); + deltaPsiTPCAll *= static_cast(nMode); + + float qVecReShiftedFT0C = qVecRe[kFT0C * nCorrections + kRescale] * std::cos(deltaPsiFT0C) - qVecIm[kFT0C * nCorrections + kRescale] * std::sin(deltaPsiFT0C); + float qVecImShiftedFT0C = qVecRe[kFT0C * nCorrections + kRescale] * std::sin(deltaPsiFT0C) + qVecIm[kFT0C * nCorrections + kRescale] * std::cos(deltaPsiFT0C); + float qVecReShiftedFT0A = qVecRe[kFT0A * nCorrections + kRescale] * std::cos(deltaPsiFT0A) - qVecIm[kFT0A * nCorrections + kRescale] * std::sin(deltaPsiFT0A); + float qVecImShiftedFT0A = qVecRe[kFT0A * nCorrections + kRescale] * std::sin(deltaPsiFT0A) + qVecIm[kFT0A * nCorrections + kRescale] * std::cos(deltaPsiFT0A); + float qVecReShiftedFT0M = qVecRe[kFT0M * nCorrections + kRescale] * std::cos(deltaPsiFT0M) - qVecIm[kFT0M * nCorrections + kRescale] * std::sin(deltaPsiFT0M); + float qVecImShiftedFT0M = qVecRe[kFT0M * nCorrections + kRescale] * std::sin(deltaPsiFT0M) + qVecIm[kFT0M * nCorrections + kRescale] * std::cos(deltaPsiFT0M); + float qVecReShiftedFV0A = qVecRe[kFV0A * nCorrections + kRescale] * std::cos(deltaPsiFV0A) - qVecIm[kFV0A * nCorrections + kRescale] * std::sin(deltaPsiFV0A); + float qVecImShiftedFV0A = qVecRe[kFV0A * nCorrections + kRescale] * std::sin(deltaPsiFV0A) + qVecIm[kFV0A * nCorrections + kRescale] * std::cos(deltaPsiFV0A); + float qVecReShiftedTPCPos = qVecRe[kTPCPos * nCorrections + kRescale] * std::cos(deltaPsiTPCPos) - qVecIm[kTPCPos * nCorrections + kRescale] * std::sin(deltaPsiTPCPos); + float qVecImShiftedTPCPos = qVecRe[kTPCPos * nCorrections + kRescale] * std::sin(deltaPsiTPCPos) + qVecIm[kTPCPos * nCorrections + kRescale] * std::cos(deltaPsiTPCPos); + float qVecReShiftedTPCNeg = qVecRe[kTPCNeg * nCorrections + kRescale] * std::cos(deltaPsiTPCNeg) - qVecIm[kTPCNeg * nCorrections + kRescale] * std::sin(deltaPsiTPCNeg); + float qVecImShiftedTPCNeg = qVecRe[kTPCNeg * nCorrections + kRescale] * std::sin(deltaPsiTPCNeg) + qVecIm[kTPCNeg * nCorrections + kRescale] * std::cos(deltaPsiTPCNeg); + float qVecReShiftedTPCAll = qVecRe[kTPCAll * nCorrections + kRescale] * std::cos(deltaPsiTPCAll) - qVecIm[kTPCAll * nCorrections + kRescale] * std::sin(deltaPsiTPCAll); + float qVecImShiftedTPCAll = qVecRe[kTPCAll * nCorrections + kRescale] * std::sin(deltaPsiTPCAll) + qVecIm[kTPCAll * nCorrections + kRescale] * std::cos(deltaPsiTPCAll); + + qVecRe[kFT0C * nCorrections + kRescale] = qVecReShiftedFT0C; + qVecIm[kFT0C * nCorrections + kRescale] = qVecImShiftedFT0C; + qVecRe[kFT0A * nCorrections + kRescale] = qVecReShiftedFT0A; + qVecIm[kFT0A * nCorrections + kRescale] = qVecImShiftedFT0A; + qVecRe[kFT0M * nCorrections + kRescale] = qVecReShiftedFT0M; + qVecIm[kFT0M * nCorrections + kRescale] = qVecImShiftedFT0M; + qVecRe[kFV0A * nCorrections + kRescale] = qVecReShiftedFV0A; + qVecIm[kFV0A * nCorrections + kRescale] = qVecImShiftedFV0A; + qVecRe[kTPCPos * nCorrections + kRescale] = qVecReShiftedTPCPos; + qVecIm[kTPCPos * nCorrections + kRescale] = qVecImShiftedTPCPos; + qVecRe[kTPCNeg * nCorrections + kRescale] = qVecReShiftedTPCNeg; + qVecIm[kTPCNeg * nCorrections + kRescale] = qVecImShiftedTPCNeg; + qVecRe[kTPCAll * nCorrections + kRescale] = qVecReShiftedTPCAll; + qVecIm[kTPCAll * nCorrections + kRescale] = qVecImShiftedTPCAll; + } + } + } + + /// Function to calculate the un-normalized q-vectors + /// \param nMode is the harmonic number of the q-vector + /// \param coll is the collision object + /// \param tracks are the tracks associated to the collision + /// \param qVecRe is the vector with the real part of the q-vector for each detector + /// \param qVecIm is the vector with the imaginary part of the q-vector for each detector + /// \param qVecAmp is the vector with the amplitude of the signal in each detector + /// \param trkTPCPosLabel is the vector with the number of TPC tracks with positive eta + /// \param trkTPCNegLabel is the vector with the number of TPC tracks with negative eta + /// \param trkTPCAllLabel is the vector with the number of TPC tracks with any eta template - void CalQvec(const Nmode nmode, const CollType& coll, const TrackType& track, std::vector& QvecRe, std::vector& QvecIm, std::vector& QvecAmp, std::vector& TrkTPCposLabel, std::vector& TrkTPCnegLabel, std::vector& TrkTPCallLabel) + void calcQVec(const Nmode nMode, const CollType& coll, const TrackType& tracks, std::vector& qVecRe, std::vector& qVecIm, std::vector& qVecAmp, std::vector& trkTPCPosLabel, std::vector& trkTPCNegLabel, std::vector& trkTPCAllLabel) { - float qVectFT0A[2] = {0.}; - float qVectFT0C[2] = {0.}; - float qVectFT0M[2] = {0.}; - float qVectFV0A[2] = {0.}; - float qVectTPCpos[2] = {0.}; - float qVectTPCneg[2] = {0.}; - float qVectTPCall[2] = {0.}; - - TComplex QvecDet(0); - TComplex QvecFT0M(0); + float qVectFT0A[2] = {-999., -999.}; + float qVectFT0C[2] = {-999., -999.}; + float qVectFT0M[2] = {-999., -999.}; + float qVectFV0A[2] = {-999., -999.}; + float qVectTPCPos[2] = {0., 0.}; // Always computed + float qVectTPCNeg[2] = {0., 0.}; // Always computed + float qVectTPCAll[2] = {0., 0.}; // Always computed + + TComplex qVecDet(0); + TComplex qVecFT0M(0); float sumAmplFT0A = 0.; float sumAmplFT0C = 0.; float sumAmplFT0M = 0.; @@ -371,100 +646,70 @@ struct qVectorsTable { if (useDetector["QvectorFT0As"]) { for (std::size_t iChA = 0; iChA < ft0.channelA().size(); iChA++) { float ampl = ft0.amplitudeA()[iChA]; - int FT0AchId = ft0.channelA()[iChA]; + int ft0AchId = ft0.channelA()[iChA]; - histosQA.fill(HIST("FT0Amp"), ampl, FT0AchId); - histosQA.fill(HIST("FT0AmpCor"), ampl / FT0RelGainConst[FT0AchId], FT0AchId); + histosQA.fill(HIST("FT0Amp"), ampl, ft0AchId); + histosQA.fill(HIST("FT0AmpCor"), ampl / ft0RelGainConst[ft0AchId], ft0AchId); - helperEP.SumQvectors(0, FT0AchId, ampl / FT0RelGainConst[FT0AchId], nmode, QvecDet, sumAmplFT0A, ft0geom, fv0geom); - helperEP.SumQvectors(0, FT0AchId, ampl / FT0RelGainConst[FT0AchId], nmode, QvecFT0M, sumAmplFT0M, ft0geom, fv0geom); + helperEP.SumQvectors(0, ft0AchId, ampl / ft0RelGainConst[ft0AchId], nMode, qVecDet, sumAmplFT0A, ft0geom, fv0geom); + helperEP.SumQvectors(0, ft0AchId, ampl / ft0RelGainConst[ft0AchId], nMode, qVecFT0M, sumAmplFT0M, ft0geom, fv0geom); } - if (sumAmplFT0A > 1e-8) { - QvecDet /= sumAmplFT0A; - qVectFT0A[0] = QvecDet.Re(); - qVectFT0A[1] = QvecDet.Im(); + if (sumAmplFT0A > minAmplitude) { + qVectFT0A[0] = qVecDet.Re(); + qVectFT0A[1] = qVecDet.Im(); } - } else { - qVectFT0A[0] = 999.; - qVectFT0A[1] = 999.; } if (useDetector["QvectorFT0Cs"]) { - QvecDet = TComplex(0., 0.); + qVecDet = TComplex(0., 0.); for (std::size_t iChC = 0; iChC < ft0.channelC().size(); iChC++) { float ampl = ft0.amplitudeC()[iChC]; - int FT0CchId = ft0.channelC()[iChC] + 96; + int ft0CchId = ft0.channelC()[iChC] + 96; - histosQA.fill(HIST("FT0Amp"), ampl, FT0CchId); - histosQA.fill(HIST("FT0AmpCor"), ampl / FT0RelGainConst[FT0CchId], FT0CchId); + histosQA.fill(HIST("FT0Amp"), ampl, ft0CchId); + histosQA.fill(HIST("FT0AmpCor"), ampl / ft0RelGainConst[ft0CchId], ft0CchId); - helperEP.SumQvectors(0, FT0CchId, ampl / FT0RelGainConst[FT0CchId], nmode, QvecDet, sumAmplFT0C, ft0geom, fv0geom); - helperEP.SumQvectors(0, FT0CchId, ampl / FT0RelGainConst[FT0CchId], nmode, QvecFT0M, sumAmplFT0M, ft0geom, fv0geom); + helperEP.SumQvectors(0, ft0CchId, ampl / ft0RelGainConst[ft0CchId], nMode, qVecDet, sumAmplFT0C, ft0geom, fv0geom); + helperEP.SumQvectors(0, ft0CchId, ampl / ft0RelGainConst[ft0CchId], nMode, qVecFT0M, sumAmplFT0M, ft0geom, fv0geom); } - if (sumAmplFT0C > 1e-8) { - QvecDet /= sumAmplFT0C; - qVectFT0C[0] = QvecDet.Re(); - qVectFT0C[1] = QvecDet.Im(); - } else { - qVectFT0C[0] = 999.; - qVectFT0C[1] = 999.; + if (sumAmplFT0C > minAmplitude) { + qVectFT0C[0] = qVecDet.Re(); + qVectFT0C[1] = qVecDet.Im(); + } + if (sumAmplFT0M > minAmplitude && useDetector["QvectorFT0Ms"]) { + qVectFT0M[0] = qVecFT0M.Re(); + qVectFT0M[1] = qVecFT0M.Im(); } - } else { - qVectFT0C[0] = -999.; - qVectFT0C[1] = -999.; } - if (sumAmplFT0M > 1e-8 && useDetector["QvectorFT0Ms"]) { - QvecFT0M /= sumAmplFT0M; - qVectFT0M[0] = QvecFT0M.Re(); - qVectFT0M[1] = QvecFT0M.Im(); - } else { - qVectFT0M[0] = 999.; - qVectFT0M[1] = 999.; - } - } else { - qVectFT0A[0] = -999.; - qVectFT0A[1] = -999.; - qVectFT0C[0] = -999.; - qVectFT0C[1] = -999.; - qVectFT0M[0] = -999.; - qVectFT0M[1] = -999.; - } + qVecDet = TComplex(0., 0.); + sumAmplFV0A = 0; + if (coll.has_foundFV0() && useDetector["QvectorFV0As"]) { + auto fv0 = coll.foundFV0(); - QvecDet = TComplex(0., 0.); - sumAmplFV0A = 0; - if (coll.has_foundFV0() && useDetector["QvectorFV0As"]) { - auto fv0 = coll.foundFV0(); + for (std::size_t iCh = 0; iCh < fv0.channel().size(); iCh++) { + float ampl = fv0.amplitude()[iCh]; + int fv0AchId = fv0.channel()[iCh]; + histosQA.fill(HIST("FV0Amp"), ampl, fv0AchId); + histosQA.fill(HIST("FV0AmpCor"), ampl / fv0RelGainConst[fv0AchId], fv0AchId); - for (std::size_t iCh = 0; iCh < fv0.channel().size(); iCh++) { - float ampl = fv0.amplitude()[iCh]; - int FV0AchId = fv0.channel()[iCh]; - histosQA.fill(HIST("FV0Amp"), ampl, FV0AchId); - histosQA.fill(HIST("FV0AmpCor"), ampl / FV0RelGainConst[FV0AchId], FV0AchId); - - helperEP.SumQvectors(1, FV0AchId, ampl / FV0RelGainConst[FV0AchId], nmode, QvecDet, sumAmplFV0A, ft0geom, fv0geom); - } + helperEP.SumQvectors(1, fv0AchId, ampl / fv0RelGainConst[fv0AchId], nMode, qVecDet, sumAmplFV0A, ft0geom, fv0geom); + } - if (sumAmplFV0A > 1e-8) { - QvecDet /= sumAmplFV0A; - qVectFV0A[0] = QvecDet.Re(); - qVectFV0A[1] = QvecDet.Im(); - } else { - qVectFV0A[0] = 999.; - qVectFV0A[1] = 999.; + if (sumAmplFV0A > minAmplitude) { + qVectFV0A[0] = qVecDet.Re(); + qVectFV0A[1] = qVecDet.Im(); + } } - } else { - qVectFV0A[0] = -999.; - qVectFV0A[1] = -999.; } - int nTrkTPCpos = 0; - int nTrkTPCneg = 0; - int nTrkTPCall = 0; + int nTrkTPCPos = 0; + int nTrkTPCNeg = 0; + int nTrkTPCAll = 0; - for (auto const& trk : track) { - if (!SelTrack(trk)) { + for (auto const& trk : tracks) { + if (!selTrack(trk)) { continue; } histosQA.fill(HIST("ChTracks"), trk.pt(), trk.eta(), trk.phi(), cent); @@ -474,110 +719,90 @@ struct qVectorsTable { if (trk.eta() < cfgEtaMin) { continue; } - qVectTPCall[0] += trk.pt() * std::cos(trk.phi() * nmode); - qVectTPCall[1] += trk.pt() * std::sin(trk.phi() * nmode); - TrkTPCallLabel.push_back(trk.globalIndex()); - nTrkTPCall++; - if (std::abs(trk.eta()) < 0.1) { + qVectTPCAll[0] += trk.pt() * std::cos(trk.phi() * nMode); + qVectTPCAll[1] += trk.pt() * std::sin(trk.phi() * nMode); + trkTPCAllLabel.push_back(trk.globalIndex()); + nTrkTPCAll++; + if (std::abs(trk.eta()) < trackEtaMin) { continue; } if (trk.eta() > 0 && (useDetector["QvectorTPCposs"] || useDetector["QvectorBPoss"])) { - qVectTPCpos[0] += trk.pt() * std::cos(trk.phi() * nmode); - qVectTPCpos[1] += trk.pt() * std::sin(trk.phi() * nmode); - TrkTPCposLabel.push_back(trk.globalIndex()); - nTrkTPCpos++; + qVectTPCPos[0] += trk.pt() * std::cos(trk.phi() * nMode); + qVectTPCPos[1] += trk.pt() * std::sin(trk.phi() * nMode); + trkTPCPosLabel.push_back(trk.globalIndex()); + nTrkTPCPos++; } else if (trk.eta() < 0 && (useDetector["QvectorTPCnegs"] || useDetector["QvectorBNegs"])) { - qVectTPCneg[0] += trk.pt() * std::cos(trk.phi() * nmode); - qVectTPCneg[1] += trk.pt() * std::sin(trk.phi() * nmode); - TrkTPCnegLabel.push_back(trk.globalIndex()); - nTrkTPCneg++; + qVectTPCNeg[0] += trk.pt() * std::cos(trk.phi() * nMode); + qVectTPCNeg[1] += trk.pt() * std::sin(trk.phi() * nMode); + trkTPCNegLabel.push_back(trk.globalIndex()); + nTrkTPCNeg++; } } - if (nTrkTPCpos > 0) { - qVectTPCpos[0] /= nTrkTPCpos; - qVectTPCpos[1] /= nTrkTPCpos; - } else { - qVectTPCpos[0] = 999.; - qVectTPCpos[1] = 999.; - } - if (nTrkTPCneg > 0) { - qVectTPCneg[0] /= nTrkTPCneg; - qVectTPCneg[1] /= nTrkTPCneg; - } else { - qVectTPCneg[0] = 999.; - qVectTPCneg[1] = 999.; - } - - if (nTrkTPCall > 0) { - qVectTPCall[0] /= nTrkTPCall; - qVectTPCall[1] /= nTrkTPCall; - } else { - qVectTPCall[0] = 999.; - qVectTPCall[1] = 999.; - } - - for (auto i{0u}; i < 4; i++) { - QvecRe.push_back(qVectFT0C[0]); - QvecIm.push_back(qVectFT0C[1]); - } - for (auto i{0u}; i < 4; i++) { - QvecRe.push_back(qVectFT0A[0]); - QvecIm.push_back(qVectFT0A[1]); - } - for (auto i{0u}; i < 4; i++) { - QvecRe.push_back(qVectFT0M[0]); - QvecIm.push_back(qVectFT0M[1]); - } - for (auto i{0u}; i < 4; i++) { - QvecRe.push_back(qVectFV0A[0]); - QvecIm.push_back(qVectFV0A[1]); - } - for (auto i{0u}; i < 4; i++) { - QvecRe.push_back(qVectTPCpos[0]); - QvecIm.push_back(qVectTPCpos[1]); - } - for (auto i{0u}; i < 4; i++) { - QvecRe.push_back(qVectTPCneg[0]); - QvecIm.push_back(qVectTPCneg[1]); - } - for (auto i{0u}; i < 4; i++) { - QvecRe.push_back(qVectTPCall[0]); - QvecIm.push_back(qVectTPCall[1]); - } - - QvecAmp.push_back(sumAmplFT0C); - QvecAmp.push_back(sumAmplFT0A); - QvecAmp.push_back(sumAmplFT0M); - QvecAmp.push_back(sumAmplFV0A); - QvecAmp.push_back(static_cast(nTrkTPCpos)); - QvecAmp.push_back(static_cast(nTrkTPCneg)); - QvecAmp.push_back(static_cast(nTrkTPCall)); + qVecRe.push_back(qVectFT0C[0]); + qVecIm.push_back(qVectFT0C[1]); + qVecRe.push_back(qVectFT0A[0]); + qVecIm.push_back(qVectFT0A[1]); + qVecRe.push_back(qVectFT0M[0]); + qVecIm.push_back(qVectFT0M[1]); + qVecRe.push_back(qVectFV0A[0]); + qVecIm.push_back(qVectFV0A[1]); + qVecRe.push_back(qVectTPCPos[0]); + qVecIm.push_back(qVectTPCPos[1]); + qVecRe.push_back(qVectTPCNeg[0]); + qVecIm.push_back(qVectTPCNeg[1]); + qVecRe.push_back(qVectTPCAll[0]); + qVecIm.push_back(qVectTPCAll[1]); + + qVecAmp.push_back(sumAmplFT0C); + qVecAmp.push_back(sumAmplFT0A); + qVecAmp.push_back(sumAmplFT0M); + qVecAmp.push_back(sumAmplFV0A); + qVecAmp.push_back(static_cast(nTrkTPCPos)); + qVecAmp.push_back(static_cast(nTrkTPCNeg)); + qVecAmp.push_back(static_cast(nTrkTPCAll)); } void process(MyCollisions::iterator const& coll, aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, MyTracks const& tracks) { - std::vector TrkTPCposLabel{}; - std::vector TrkTPCnegLabel{}; - std::vector TrkTPCallLabel{}; - std::vector qvecRe{}; - std::vector qvecIm{}; - std::vector qvecAmp{}; - - std::vector qvecReFT0C{}; - std::vector qvecImFT0C{}; - std::vector qvecReFT0A{}; - std::vector qvecImFT0A{}; - std::vector qvecReFT0M{}; - std::vector qvecImFT0M{}; - std::vector qvecReFV0A{}; - std::vector qvecImFV0A{}; - std::vector qvecReTPCpos{}; - std::vector qvecImTPCpos{}; - std::vector qvecReTPCneg{}; - std::vector qvecImTPCneg{}; - std::vector qvecReTPCall{}; - std::vector qvecImTPCall{}; + std::vector trkTPCPosLabel{}; + std::vector trkTPCNegLabel{}; + std::vector trkTPCAllLabel{}; + std::vector qVecAmp{}; + + std::vector qVecReSp{}; + std::vector qVecImSp{}; + std::vector qVecReFT0CSp{}; + std::vector qVecImFT0CSp{}; + std::vector qVecReFT0ASp{}; + std::vector qVecImFT0ASp{}; + std::vector qVecReFT0MSp{}; + std::vector qVecImFT0MSp{}; + std::vector qVecReFV0ASp{}; + std::vector qVecImFV0ASp{}; + std::vector qVecReTPCPosSp{}; + std::vector qVecImTPCPosSp{}; + std::vector qVecReTPCNegSp{}; + std::vector qVecImTPCNegSp{}; + std::vector qVecReTPCAllSp{}; + std::vector qVecImTPCAllSp{}; + + std::vector qVecReEse{}; + std::vector qVecImEse{}; + std::vector qVecReFT0CEse{}; + std::vector qVecImFT0CEse{}; + std::vector qVecReFT0AEse{}; + std::vector qVecImFT0AEse{}; + std::vector qVecReFT0MEse{}; + std::vector qVecImFT0MEse{}; + std::vector qVecReFV0AEse{}; + std::vector qVecImFV0AEse{}; + std::vector qVecReTPCPosEse{}; + std::vector qVecImTPCPosEse{}; + std::vector qVecReTPCNegEse{}; + std::vector qVecImTPCNegEse{}; + std::vector qVecReTPCAllEse{}; + std::vector qVecImTPCAllEse{}; auto bc = coll.bc_as(); int currentRun = bc.runNumber(); @@ -590,172 +815,144 @@ struct qVectorsTable { coll.centFT0M(), coll.centFT0A(), coll.centFT0C(), coll.centFV0A()}; cent = centAllEstim[cfgCentEsti]; - bool IsCalibrated = true; + bool isCalibrated = true; if (cent < 0. || cent > cfgMaxCentrality) { cent = 110.; - IsCalibrated = false; + isCalibrated = false; } - for (std::size_t id = 0; id < cfgnMods->size(); id++) { - int nmode = cfgnMods->at(id); - CalQvec(nmode, coll, tracks, qvecRe, qvecIm, qvecAmp, TrkTPCposLabel, TrkTPCnegLabel, TrkTPCallLabel); - if (cent < cfgMaxCentrality) { - for (auto i{0u}; i < kTPCall + 1; i++) { - helperEP.DoRecenter(qvecRe[(kTPCall + 1) * 4 * id + i * 4 + 1], qvecIm[(kTPCall + 1) * 4 * id + i * 4 + 1], - objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 1, i + 1), objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 2, i + 1)); - - helperEP.DoRecenter(qvecRe[(kTPCall + 1) * 4 * id + i * 4 + 2], qvecIm[(kTPCall + 1) * 4 * id + i * 4 + 2], - objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 1, i + 1), objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 2, i + 1)); - helperEP.DoTwist(qvecRe[(kTPCall + 1) * 4 * id + i * 4 + 2], qvecIm[(kTPCall + 1) * 4 * id + i * 4 + 2], - objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 3, i + 1), objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 4, i + 1)); - - helperEP.DoRecenter(qvecRe[(kTPCall + 1) * 4 * id + i * 4 + 3], qvecIm[(kTPCall + 1) * 4 * id + i * 4 + 3], - objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 1, i + 1), objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 2, i + 1)); - helperEP.DoTwist(qvecRe[(kTPCall + 1) * 4 * id + i * 4 + 3], qvecIm[(kTPCall + 1) * 4 * id + i * 4 + 3], - objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 3, i + 1), objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 4, i + 1)); - helperEP.DoRescale(qvecRe[(kTPCall + 1) * 4 * id + i * 4 + 3], qvecIm[(kTPCall + 1) * 4 * id + i * 4 + 3], - objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 5, i + 1), objQvec.at(id)->GetBinContent(static_cast(cent) + 1, 6, i + 1)); - } - if (cfgShiftCorr) { - auto deltapsiFT0C = 0.0; - auto deltapsiFT0A = 0.0; - auto deltapsiFT0M = 0.0; - auto deltapsiFV0A = 0.0; - auto deltapsiTPCpos = 0.0; - auto deltapsiTPCneg = 0.0; - auto deltapsiTPCall = 0.0; - - auto psidefFT0C = TMath::ATan2(qvecIm[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3], qvecRe[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3]) / static_cast(nmode); - auto psidefFT0A = TMath::ATan2(qvecIm[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3], qvecRe[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3]) / static_cast(nmode); - auto psidefFT0M = TMath::ATan2(qvecIm[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3], qvecRe[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3]) / static_cast(nmode); - auto psidefFV0A = TMath::ATan2(qvecIm[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3], qvecRe[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3]) / static_cast(nmode); - auto psidefTPCpos = TMath::ATan2(qvecIm[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3], qvecRe[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3]) / static_cast(nmode); - auto psidefTPCneg = TMath::ATan2(qvecIm[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3], qvecRe[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3]) / static_cast(nmode); - auto psidefTPCall = TMath::ATan2(qvecIm[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3], qvecRe[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3]) / static_cast(nmode); - - for (int ishift = 1; ishift <= 10; ishift++) { - auto coeffshiftxFT0C = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFT0C, ishift - 0.5)); - auto coeffshiftyFT0C = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFT0C + 1, ishift - 0.5)); - auto coeffshiftxFT0A = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFT0A, ishift - 0.5)); - auto coeffshiftyFT0A = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFT0A + 1, ishift - 0.5)); - auto coeffshiftxFT0M = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFT0M, ishift - 0.5)); - auto coeffshiftyFT0M = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFT0M + 1, ishift - 0.5)); - auto coeffshiftxFV0A = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFV0A, ishift - 0.5)); - auto coeffshiftyFV0A = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kFV0A + 1, ishift - 0.5)); - auto coeffshiftxTPCpos = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kTPCpos, ishift - 0.5)); - auto coeffshiftyTPCpos = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kTPCpos + 1, ishift - 0.5)); - auto coeffshiftxTPCneg = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kTPCneg, ishift - 0.5)); - auto coeffshiftyTPCneg = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kTPCneg + 1, ishift - 0.5)); - auto coeffshiftxTPCall = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kTPCall, ishift - 0.5)); - auto coeffshiftyTPCall = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(cent, 2 * kTPCall + 1, ishift - 0.5)); - - deltapsiFT0C += ((2. / (1.0 * ishift)) * (-coeffshiftxFT0C * TMath::Cos(ishift * static_cast(nmode) * psidefFT0C) + coeffshiftyFT0C * TMath::Sin(ishift * static_cast(nmode) * psidefFT0C))) / static_cast(nmode); - deltapsiFT0A += ((2. / (1.0 * ishift)) * (-coeffshiftxFT0A * TMath::Cos(ishift * static_cast(nmode) * psidefFT0A) + coeffshiftyFT0A * TMath::Sin(ishift * static_cast(nmode) * psidefFT0A))) / static_cast(nmode); - deltapsiFT0M += ((2. / (1.0 * ishift)) * (-coeffshiftxFT0M * TMath::Cos(ishift * static_cast(nmode) * psidefFT0M) + coeffshiftyFT0M * TMath::Sin(ishift * static_cast(nmode) * psidefFT0M))) / static_cast(nmode); - deltapsiFV0A += ((2. / (1.0 * ishift)) * (-coeffshiftxFV0A * TMath::Cos(ishift * static_cast(nmode) * psidefFV0A) + coeffshiftyFV0A * TMath::Sin(ishift * static_cast(nmode) * psidefFV0A))) / static_cast(nmode); - deltapsiTPCpos += ((2. / (1.0 * ishift)) * (-coeffshiftxTPCpos * TMath::Cos(ishift * static_cast(nmode) * psidefTPCpos) + coeffshiftyTPCpos * TMath::Sin(ishift * static_cast(nmode) * psidefTPCpos))) / static_cast(nmode); - deltapsiTPCneg += ((2. / (1.0 * ishift)) * (-coeffshiftxTPCneg * TMath::Cos(ishift * static_cast(nmode) * psidefTPCneg) + coeffshiftyTPCneg * TMath::Sin(ishift * static_cast(nmode) * psidefTPCneg))) / static_cast(nmode); - deltapsiTPCall += ((2. / (1.0 * ishift)) * (-coeffshiftxTPCall * TMath::Cos(ishift * static_cast(nmode) * psidefTPCall) + coeffshiftyTPCall * TMath::Sin(ishift * static_cast(nmode) * psidefTPCall))) / static_cast(nmode); - } - deltapsiFT0C *= static_cast(nmode); - deltapsiFT0A *= static_cast(nmode); - deltapsiFT0M *= static_cast(nmode); - deltapsiFV0A *= static_cast(nmode); - deltapsiTPCpos *= static_cast(nmode); - deltapsiTPCneg *= static_cast(nmode); - deltapsiTPCall *= static_cast(nmode); - - float qvecReShiftedFT0C = qvecRe[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3] * TMath::Cos(deltapsiFT0C) - qvecIm[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3] * TMath::Sin(deltapsiFT0C); - float qvecImShiftedFT0C = qvecRe[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3] * TMath::Sin(deltapsiFT0C) + qvecIm[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3] * TMath::Cos(deltapsiFT0C); - float qvecReShiftedFT0A = qvecRe[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3] * TMath::Cos(deltapsiFT0A) - qvecIm[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3] * TMath::Sin(deltapsiFT0A); - float qvecImShiftedFT0A = qvecRe[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3] * TMath::Sin(deltapsiFT0A) + qvecIm[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3] * TMath::Cos(deltapsiFT0A); - float qvecReShiftedFT0M = qvecRe[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3] * TMath::Cos(deltapsiFT0M) - qvecIm[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3] * TMath::Sin(deltapsiFT0M); - float qvecImShiftedFT0M = qvecRe[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3] * TMath::Sin(deltapsiFT0M) + qvecIm[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3] * TMath::Cos(deltapsiFT0M); - float qvecReShiftedFV0A = qvecRe[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3] * TMath::Cos(deltapsiFV0A) - qvecIm[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3] * TMath::Sin(deltapsiFV0A); - float qvecImShiftedFV0A = qvecRe[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3] * TMath::Sin(deltapsiFV0A) + qvecIm[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3] * TMath::Cos(deltapsiFV0A); - float qvecReShiftedTPCpos = qvecRe[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3] * TMath::Cos(deltapsiTPCpos) - qvecIm[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3] * TMath::Sin(deltapsiTPCpos); - float qvecImShiftedTPCpos = qvecRe[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3] * TMath::Sin(deltapsiTPCpos) + qvecIm[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3] * TMath::Cos(deltapsiTPCpos); - float qvecReShiftedTPCneg = qvecRe[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3] * TMath::Cos(deltapsiTPCneg) - qvecIm[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3] * TMath::Sin(deltapsiTPCneg); - float qvecImShiftedTPCneg = qvecRe[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3] * TMath::Sin(deltapsiTPCneg) + qvecIm[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3] * TMath::Cos(deltapsiTPCneg); - float qvecReShiftedTPCall = qvecRe[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3] * TMath::Cos(deltapsiTPCall) - qvecIm[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3] * TMath::Sin(deltapsiTPCall); - float qvecImShiftedTPCall = qvecRe[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3] * TMath::Sin(deltapsiTPCall) + qvecIm[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3] * TMath::Cos(deltapsiTPCall); - - qvecRe[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3] = qvecReShiftedFT0C; - qvecIm[(kTPCall + 1) * 4 * id + kFT0C * 4 + 3] = qvecImShiftedFT0C; - qvecRe[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3] = qvecReShiftedFT0A; - qvecIm[(kTPCall + 1) * 4 * id + kFT0A * 4 + 3] = qvecImShiftedFT0A; - qvecRe[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3] = qvecReShiftedFT0M; - qvecIm[(kTPCall + 1) * 4 * id + kFT0M * 4 + 3] = qvecImShiftedFT0M; - qvecRe[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3] = qvecReShiftedFV0A; - qvecIm[(kTPCall + 1) * 4 * id + kFV0A * 4 + 3] = qvecImShiftedFV0A; - qvecRe[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3] = qvecReShiftedTPCpos; - qvecIm[(kTPCall + 1) * 4 * id + kTPCpos * 4 + 3] = qvecImShiftedTPCpos; - qvecRe[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3] = qvecReShiftedTPCneg; - qvecIm[(kTPCall + 1) * 4 * id + kTPCneg * 4 + 3] = qvecImShiftedTPCneg; - qvecRe[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3] = qvecReShiftedTPCall; - qvecIm[(kTPCall + 1) * 4 * id + kTPCall * 4 + 3] = qvecImShiftedTPCall; - } + for (std::size_t id = 0; id < cfgnMods->size(); id++) { + int nMode = cfgnMods->at(id); + + // Raw Q-vectors, no multiplicity normalization and no corrections + std::vector qVecReRaw{}; + std::vector qVecImRaw{}; + calcQVec(nMode, coll, tracks, qVecReRaw, qVecImRaw, qVecAmp, trkTPCPosLabel, trkTPCNegLabel, trkTPCAllLabel); + + // Scalar Product Q-vectors, normalization by multiplicity/amplitude + std::vector nModeQVecReSp{}; + std::vector nModeQVecImSp{}; + normalizeQVec(nModeQVecReSp, nModeQVecImSp, qVecReRaw, qVecImRaw, qVecAmp, MultNorms::kScalarProd); + correctQVec(cent, nModeQVecReSp, nModeQVecImSp, corrsQvecSp[id], shiftProfileSp, nMode); + // Add to summary vector + qVecReSp.insert(qVecReSp.end(), nModeQVecReSp.begin(), nModeQVecReSp.end()); + qVecImSp.insert(qVecImSp.end(), nModeQVecImSp.begin(), nModeQVecImSp.end()); + + // Pick the desired correction level for the Q-vectors to be stored in the analysis table + int corrLevel = cfgCorrLevel == 0 ? 0 : cfgCorrLevel - 1; + int nCorrections = static_cast(kNCorrections); + + qVecReFT0CSp.push_back(nModeQVecReSp[kFT0C * nCorrections + corrLevel]); + qVecImFT0CSp.push_back(nModeQVecImSp[kFT0C * nCorrections + corrLevel]); + qVecReFT0ASp.push_back(nModeQVecReSp[kFT0A * nCorrections + corrLevel]); + qVecImFT0ASp.push_back(nModeQVecImSp[kFT0A * nCorrections + corrLevel]); + qVecReFT0MSp.push_back(nModeQVecReSp[kFT0M * nCorrections + corrLevel]); + qVecImFT0MSp.push_back(nModeQVecImSp[kFT0M * nCorrections + corrLevel]); + qVecReFV0ASp.push_back(nModeQVecReSp[kFV0A * nCorrections + corrLevel]); + qVecImFV0ASp.push_back(nModeQVecImSp[kFV0A * nCorrections + corrLevel]); + qVecReTPCPosSp.push_back(nModeQVecReSp[kTPCPos * nCorrections + corrLevel]); + qVecImTPCPosSp.push_back(nModeQVecImSp[kTPCPos * nCorrections + corrLevel]); + qVecReTPCNegSp.push_back(nModeQVecReSp[kTPCNeg * nCorrections + corrLevel]); + qVecImTPCNegSp.push_back(nModeQVecImSp[kTPCNeg * nCorrections + corrLevel]); + qVecReTPCAllSp.push_back(nModeQVecReSp[kTPCAll * nCorrections + corrLevel]); + qVecImTPCAllSp.push_back(nModeQVecImSp[kTPCAll * nCorrections + corrLevel]); + + if (cfgProduceRedQVecs) { + + // Ese Q-vectors, normalization by sqrt(multiplicity/amplitude) + std::vector nModeQVecReEse{}; + std::vector nModeQVecImEse{}; + normalizeQVec(nModeQVecReEse, nModeQVecImEse, qVecReRaw, qVecImRaw, qVecAmp, MultNorms::kEsE); + correctQVec(cent, nModeQVecReEse, nModeQVecImEse, corrsQvecEse[id], shiftProfileEse, nMode); + // Add to summary vector + qVecReEse.insert(qVecReEse.end(), nModeQVecReEse.begin(), nModeQVecReEse.end()); + qVecImEse.insert(qVecImEse.end(), nModeQVecImEse.begin(), nModeQVecImEse.end()); + + qVecReFT0CEse.push_back(nModeQVecReEse[kFT0C * nCorrections + corrLevel]); + qVecImFT0CEse.push_back(nModeQVecImEse[kFT0C * nCorrections + corrLevel]); + qVecReFT0AEse.push_back(nModeQVecReEse[kFT0A * nCorrections + corrLevel]); + qVecImFT0AEse.push_back(nModeQVecImEse[kFT0A * nCorrections + corrLevel]); + qVecReFT0MEse.push_back(nModeQVecReEse[kFT0M * nCorrections + corrLevel]); + qVecImFT0MEse.push_back(nModeQVecImEse[kFT0M * nCorrections + corrLevel]); + qVecReFV0AEse.push_back(nModeQVecReEse[kFV0A * nCorrections + corrLevel]); + qVecImFV0AEse.push_back(nModeQVecImEse[kFV0A * nCorrections + corrLevel]); + qVecReTPCPosEse.push_back(nModeQVecReEse[kTPCPos * nCorrections + corrLevel]); + qVecImTPCPosEse.push_back(nModeQVecImEse[kTPCPos * nCorrections + corrLevel]); + qVecReTPCNegEse.push_back(nModeQVecReEse[kTPCNeg * nCorrections + corrLevel]); + qVecImTPCNegEse.push_back(nModeQVecImEse[kTPCNeg * nCorrections + corrLevel]); + qVecReTPCAllEse.push_back(nModeQVecReEse[kTPCAll * nCorrections + corrLevel]); + qVecImTPCAllEse.push_back(nModeQVecImEse[kTPCAll * nCorrections + corrLevel]); } - int CorrLevel = cfgCorrLevel == 0 ? 0 : cfgCorrLevel - 1; - qvecReFT0C.push_back(qvecRe[(kTPCall + 1) * 4 * id + kFT0C * 4 + CorrLevel]); - qvecImFT0C.push_back(qvecIm[(kTPCall + 1) * 4 * id + kFT0C * 4 + CorrLevel]); - qvecReFT0A.push_back(qvecRe[(kTPCall + 1) * 4 * id + kFT0A * 4 + CorrLevel]); - qvecImFT0A.push_back(qvecIm[(kTPCall + 1) * 4 * id + kFT0A * 4 + CorrLevel]); - qvecReFT0M.push_back(qvecRe[(kTPCall + 1) * 4 * id + kFT0M * 4 + CorrLevel]); - qvecImFT0M.push_back(qvecIm[(kTPCall + 1) * 4 * id + kFT0M * 4 + CorrLevel]); - qvecReFV0A.push_back(qvecRe[(kTPCall + 1) * 4 * id + kFV0A * 4 + CorrLevel]); - qvecImFV0A.push_back(qvecIm[(kTPCall + 1) * 4 * id + kFV0A * 4 + CorrLevel]); - qvecReTPCpos.push_back(qvecRe[(kTPCall + 1) * 4 * id + kTPCpos * 4 + CorrLevel]); - qvecImTPCpos.push_back(qvecIm[(kTPCall + 1) * 4 * id + kTPCpos * 4 + CorrLevel]); - qvecReTPCneg.push_back(qvecRe[(kTPCall + 1) * 4 * id + kTPCneg * 4 + CorrLevel]); - qvecImTPCneg.push_back(qvecIm[(kTPCall + 1) * 4 * id + kTPCneg * 4 + CorrLevel]); - qvecReTPCall.push_back(qvecRe[(kTPCall + 1) * 4 * id + kTPCall * 4 + CorrLevel]); - qvecImTPCall.push_back(qvecIm[(kTPCall + 1) * 4 * id + kTPCall * 4 + CorrLevel]); } // Fill the columns of the Qvectors table. - qVector(cent, IsCalibrated, qvecRe, qvecIm, qvecAmp); + qVector(cent, isCalibrated, qVecReSp, qVecImSp, qVecAmp); if (useDetector["QvectorFT0Cs"]) - qVectorFT0C(IsCalibrated, qvecReFT0C.at(0), qvecImFT0C.at(0), qvecAmp[kFT0C]); + qVectorFT0C(isCalibrated, qVecReFT0CSp.at(0), qVecImFT0CSp.at(0), qVecAmp[kFT0C]); if (useDetector["QvectorFT0As"]) - qVectorFT0A(IsCalibrated, qvecReFT0A.at(0), qvecImFT0A.at(0), qvecAmp[kFT0A]); + qVectorFT0A(isCalibrated, qVecReFT0ASp.at(0), qVecImFT0ASp.at(0), qVecAmp[kFT0A]); if (useDetector["QvectorFT0Ms"]) - qVectorFT0M(IsCalibrated, qvecReFT0M.at(0), qvecImFT0M.at(0), qvecAmp[kFT0M]); + qVectorFT0M(isCalibrated, qVecReFT0MSp.at(0), qVecImFT0MSp.at(0), qVecAmp[kFT0M]); if (useDetector["QvectorFV0As"]) - qVectorFV0A(IsCalibrated, qvecReFV0A.at(0), qvecImFV0A.at(0), qvecAmp[kFV0A]); + qVectorFV0A(isCalibrated, qVecReFV0ASp.at(0), qVecImFV0ASp.at(0), qVecAmp[kFV0A]); if (useDetector["QvectorTPCposs"]) - qVectorTPCpos(IsCalibrated, qvecReTPCpos.at(0), qvecImTPCpos.at(0), qvecAmp[kTPCpos], TrkTPCposLabel); + qVectorTPCPos(isCalibrated, qVecReTPCPosSp.at(0), qVecImTPCPosSp.at(0), qVecAmp[kTPCPos], trkTPCPosLabel); if (useDetector["QvectorTPCnegs"]) - qVectorTPCneg(IsCalibrated, qvecReTPCneg.at(0), qvecImTPCneg.at(0), qvecAmp[kTPCneg], TrkTPCnegLabel); + qVectorTPCNeg(isCalibrated, qVecReTPCNegSp.at(0), qVecImTPCNegSp.at(0), qVecAmp[kTPCNeg], trkTPCNegLabel); if (useDetector["QvectorTPCalls"]) - qVectorTPCall(IsCalibrated, qvecReTPCall.at(0), qvecImTPCall.at(0), qvecAmp[kTPCall], TrkTPCallLabel); + qVectorTPCAll(isCalibrated, qVecReTPCAllSp.at(0), qVecImTPCAllSp.at(0), qVecAmp[kTPCAll], trkTPCAllLabel); - qVectorFT0CVec(IsCalibrated, qvecReFT0C, qvecImFT0C, qvecAmp[kFT0C]); - qVectorFT0AVec(IsCalibrated, qvecReFT0A, qvecImFT0A, qvecAmp[kFT0A]); - qVectorFT0MVec(IsCalibrated, qvecReFT0M, qvecImFT0M, qvecAmp[kFT0M]); - qVectorFV0AVec(IsCalibrated, qvecReFV0A, qvecImFV0A, qvecAmp[kFV0A]); - qVectorTPCposVec(IsCalibrated, qvecReTPCpos, qvecImTPCpos, qvecAmp[kTPCpos], TrkTPCposLabel); - qVectorTPCnegVec(IsCalibrated, qvecReTPCneg, qvecImTPCneg, qvecAmp[kTPCneg], TrkTPCnegLabel); - qVectorTPCallVec(IsCalibrated, qvecReTPCall, qvecImTPCall, qvecAmp[kTPCall], TrkTPCallLabel); + qVectorFT0CVec(isCalibrated, qVecReFT0CSp, qVecImFT0CSp, qVecAmp[kFT0C]); + qVectorFT0AVec(isCalibrated, qVecReFT0ASp, qVecImFT0ASp, qVecAmp[kFT0A]); + qVectorFT0MVec(isCalibrated, qVecReFT0MSp, qVecImFT0MSp, qVecAmp[kFT0M]); + qVectorFV0AVec(isCalibrated, qVecReFV0ASp, qVecImFV0ASp, qVecAmp[kFV0A]); + qVectorTPCPosVec(isCalibrated, qVecReTPCPosSp, qVecImTPCPosSp, qVecAmp[kTPCPos], trkTPCPosLabel); + qVectorTPCNegVec(isCalibrated, qVecReTPCNegSp, qVecImTPCNegSp, qVecAmp[kTPCNeg], trkTPCNegLabel); + qVectorTPCAllVec(isCalibrated, qVecReTPCAllSp, qVecImTPCAllSp, qVecAmp[kTPCAll], trkTPCAllLabel); // Deprecated, will be removed in future after transition time // if (useDetector["QvectorBPoss"]) - qVectorBPos(IsCalibrated, qvecReTPCpos.at(0), qvecImTPCpos.at(0), qvecAmp[kTPCpos], TrkTPCposLabel); + qVectorBPos(isCalibrated, qVecReTPCPosSp.at(0), qVecImTPCPosSp.at(0), qVecAmp[kTPCPos], trkTPCPosLabel); if (useDetector["QvectorBNegs"]) - qVectorBNeg(IsCalibrated, qvecReTPCneg.at(0), qvecImTPCneg.at(0), qvecAmp[kTPCneg], TrkTPCnegLabel); + qVectorBNeg(isCalibrated, qVecReTPCNegSp.at(0), qVecImTPCNegSp.at(0), qVecAmp[kTPCNeg], trkTPCNegLabel); if (useDetector["QvectorBTots"]) - qVectorBTot(IsCalibrated, qvecReTPCall.at(0), qvecImTPCall.at(0), qvecAmp[kTPCall], TrkTPCallLabel); + qVectorBTot(isCalibrated, qVecReTPCAllSp.at(0), qVecImTPCAllSp.at(0), qVecAmp[kTPCAll], trkTPCAllLabel); - qVectorBPosVec(IsCalibrated, qvecReTPCpos, qvecImTPCpos, qvecAmp[kTPCpos], TrkTPCposLabel); - qVectorBNegVec(IsCalibrated, qvecReTPCneg, qvecImTPCneg, qvecAmp[kTPCneg], TrkTPCnegLabel); - qVectorBTotVec(IsCalibrated, qvecReTPCall, qvecImTPCall, qvecAmp[kTPCall], TrkTPCallLabel); + qVectorBPosVec(isCalibrated, qVecReTPCPosSp, qVecImTPCPosSp, qVecAmp[kTPCPos], trkTPCPosLabel); + qVectorBNegVec(isCalibrated, qVecReTPCNegSp, qVecImTPCNegSp, qVecAmp[kTPCNeg], trkTPCNegLabel); + qVectorBTotVec(isCalibrated, qVecReTPCAllSp, qVecImTPCAllSp, qVecAmp[kTPCAll], trkTPCAllLabel); ///////////////////////////////////////////////////////////////// + if (cfgProduceRedQVecs) { + eseQVector(cent, isCalibrated, qVecReEse, qVecImEse, qVecAmp); + eseQVectorFT0C(isCalibrated, qVecReFT0CEse.at(0), qVecImFT0CEse.at(0), qVecAmp[kFT0C]); + eseQVectorFT0A(isCalibrated, qVecReFT0AEse.at(0), qVecImFT0AEse.at(0), qVecAmp[kFT0A]); + eseQVectorFT0M(isCalibrated, qVecReFT0MEse.at(0), qVecImFT0MEse.at(0), qVecAmp[kFT0M]); + eseQVectorFV0A(isCalibrated, qVecReFV0AEse.at(0), qVecImFV0AEse.at(0), qVecAmp[kFV0A]); + eseQVectorTPCPos(isCalibrated, qVecReTPCPosEse.at(0), qVecImTPCPosEse.at(0), qVecAmp[kTPCPos], trkTPCPosLabel); + eseQVectorTPCNeg(isCalibrated, qVecReTPCNegEse.at(0), qVecImTPCNegEse.at(0), qVecAmp[kTPCNeg], trkTPCNegLabel); + eseQVectorTPCAll(isCalibrated, qVecReTPCAllEse.at(0), qVecImTPCAllEse.at(0), qVecAmp[kTPCAll], trkTPCAllLabel); + eseQVectorFT0CVec(isCalibrated, qVecReFT0CEse, qVecImFT0CEse, qVecAmp[kFT0C]); + eseQVectorFT0AVec(isCalibrated, qVecReFT0AEse, qVecImFT0AEse, qVecAmp[kFT0A]); + eseQVectorFT0MVec(isCalibrated, qVecReFT0MEse, qVecImFT0MEse, qVecAmp[kFT0M]); + eseQVectorFV0AVec(isCalibrated, qVecReFV0AEse, qVecImFV0AEse, qVecAmp[kFV0A]); + eseQVectorTPCPosVec(isCalibrated, qVecReTPCPosEse, qVecImTPCPosEse, qVecAmp[kTPCPos], trkTPCPosLabel); + eseQVectorTPCNegVec(isCalibrated, qVecReTPCNegEse, qVecImTPCNegEse, qVecAmp[kTPCNeg], trkTPCNegLabel); + eseQVectorTPCAllVec(isCalibrated, qVecReTPCAllEse, qVecImTPCAllEse, qVecAmp[kTPCAll], trkTPCAllLabel); + eseQVectorPerc(qVecReFT0CEse.at(0), qVecImFT0CEse.at(0), qVecAmp[kFT0C], + qVecReFT0AEse.at(0), qVecImFT0AEse.at(0), qVecAmp[kFT0A], + qVecReFT0MEse.at(0), qVecImFT0MEse.at(0), qVecAmp[kFT0M], + qVecReFV0AEse.at(0), qVecImFV0AEse.at(0), qVecAmp[kFV0A], + qVecReTPCPosEse.at(0), qVecImTPCPosEse.at(0), qVecAmp[kTPCPos], + qVecReTPCNegEse.at(0), qVecImTPCNegEse.at(0), qVecAmp[kTPCNeg], + qVecReTPCAllEse.at(0), qVecImTPCAllEse.at(0), qVecAmp[kTPCAll]); + } } // End process. }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { + metadataInfo.initMetadata(cfgc); return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/Common/TableProducer/timestamp.cxx b/Common/TableProducer/timestamp.cxx deleted file mode 100644 index 30ad84150c5..00000000000 --- a/Common/TableProducer/timestamp.cxx +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file timestamp.cxx -/// \author Nicolò Jacazio -/// \since 2020-06-22 -/// \brief A task to fill the timestamp table from run number. -/// Uses headers from CCDB -/// -#include "Common/Core/MetadataHelper.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -using namespace o2::framework; -using namespace o2::header; -using namespace o2; - -o2::common::core::MetadataHelper metadataInfo; // Metadata helper - -struct TimestampTask { - Produces timestampTable; /// Table with SOR timestamps produced by the task - Service ccdb; /// CCDB manager to access orbit-reset timestamp - o2::ccdb::CcdbApi ccdb_api; /// API to access CCDB headers - Configurable fatalOnInvalidTimestamp{"fatalOnInvalidTimestamp", false, "Generate fatal error for invalid timestamps"}; - std::map mapRunToOrbitReset; /// Cache of orbit reset timestamps - std::map> mapRunToRunDuration; /// Cache of run duration timestamps - int lastRunNumber = 0; /// Last run number processed - int64_t orbitResetTimestamp = 0; /// Orbit-reset timestamp in us - std::pair runDuration; /// Pair of SOR and EOR timestamps - - // Configurables - Configurable verbose{"verbose", false, "verbose mode"}; - Configurable rct_path{"rct-path", "RCT/Info/RunInformation", "path to the ccdb RCT objects for the SOR timestamps"}; - Configurable orbit_reset_path{"orbit-reset-path", "CTP/Calib/OrbitReset", "path to the ccdb orbit-reset objects"}; - Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "URL of the CCDB database"}; - Configurable isRun2MC{"isRun2MC", -1, "Running mode: enable only for Run 2 MC. Timestamps are set to SOR timestamp. Default: -1 (autoset from metadata) 0 (Standard) 1 (Run 2 MC)"}; - - void init(o2::framework::InitContext&) - { - LOGF(info, "Initializing TimestampTask"); - ccdb->setURL(url.value); // Setting URL of CCDB manager from configuration - ccdb_api.init(url.value); - if (!ccdb_api.isHostReachable()) { - LOGF(fatal, "CCDB host %s is not reacheable, cannot go forward", url.value.data()); - } - if (isRun2MC.value == -1) { - if ((!metadataInfo.isRun3()) && metadataInfo.isMC()) { - isRun2MC.value = 1; - LOG(info) << "Autosetting the Run2 MC mode based on metadata"; - } else { - isRun2MC.value = 0; - } - } - } - - void process(aod::BC const& bc) - { - int runNumber = bc.runNumber(); - // We need to set the orbit-reset timestamp for the run number. - // This is done with caching if the run number was already processed before. - // If not the orbit-reset timestamp for the run number is queried from CCDB and added to the cache - if (runNumber == lastRunNumber) { // The run number coincides to the last run processed - LOGF(debug, "Using orbit-reset timestamp from last call"); - } else if (mapRunToOrbitReset.count(runNumber)) { // The run number was already requested before: getting it from cache! - LOGF(debug, "Getting orbit-reset timestamp from cache"); - orbitResetTimestamp = mapRunToOrbitReset[runNumber]; - runDuration = mapRunToRunDuration[runNumber]; - } else { // The run was not requested before: need to acccess CCDB! - LOGF(debug, "Getting start-of-run and end-of-run timestamps from CCDB"); - runDuration = ccdb->getRunDuration(runNumber, true); /// fatalise if timestamps are not found - int64_t sorTimestamp = runDuration.first; // timestamp of the SOR/SOX/STF in ms - int64_t eorTimestamp = runDuration.second; // timestamp of the EOR/EOX/ETF in ms - - const bool isUnanchoredRun3MC = runNumber >= 300000 && runNumber < 500000; - if (isRun2MC.value == 1 || isUnanchoredRun3MC) { - // isRun2MC: bc/orbit distributions are not simulated in Run2 MC. All bcs are set to 0. - // isUnanchoredRun3MC: assuming orbit-reset is done in the beginning of each run - // Setting orbit-reset timestamp to start-of-run timestamp - orbitResetTimestamp = sorTimestamp * 1000; // from ms to us - } else if (runNumber < 300000) { // Run 2 - LOGF(debug, "Getting orbit-reset timestamp using start-of-run timestamp from CCDB"); - auto ctp = ccdb->getForTimeStamp>(orbit_reset_path.value.data(), sorTimestamp); - orbitResetTimestamp = (*ctp)[0]; - } else { - // sometimes orbit is reset after SOR. Using EOR timestamps for orbitReset query is more reliable - LOGF(debug, "Getting orbit-reset timestamp using end-of-run timestamp from CCDB"); - auto ctp = ccdb->getForTimeStamp>(orbit_reset_path.value.data(), eorTimestamp / 2 + sorTimestamp / 2); - orbitResetTimestamp = (*ctp)[0]; - } - - // Adding the timestamp to the cache map - std::pair::iterator, bool> check; - check = mapRunToOrbitReset.insert(std::pair(runNumber, orbitResetTimestamp)); - if (!check.second) { - LOGF(fatal, "Run number %i already existed with a orbit-reset timestamp of %llu", runNumber, check.first->second); - } - mapRunToRunDuration[runNumber] = runDuration; - LOGF(info, "Add new run number %i with orbit-reset timestamp %llu, SOR: %llu, EOR: %llu to cache", runNumber, orbitResetTimestamp, runDuration.first, runDuration.second); - } - - if (verbose.value) { - LOGF(info, "Orbit-reset timestamp for run number %i found: %llu us", runNumber, orbitResetTimestamp); - } - int64_t timestamp{(orbitResetTimestamp + int64_t(bc.globalBC() * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000}; // us -> ms - if (timestamp < runDuration.first || timestamp > runDuration.second) { - if (fatalOnInvalidTimestamp.value) { - LOGF(fatal, "Timestamp %llu us is out of run duration [%llu, %llu] ms", timestamp, runDuration.first, runDuration.second); - } else { - LOGF(debug, "Timestamp %llu us is out of run duration [%llu, %llu] ms", timestamp, runDuration.first, runDuration.second); - } - } - timestampTable(timestamp); - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - // Parse the metadata - metadataInfo.initMetadata(cfgc); - - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -} diff --git a/Common/TableProducer/trackPropagation.cxx b/Common/TableProducer/trackPropagation.cxx deleted file mode 100644 index cb27b54210f..00000000000 --- a/Common/TableProducer/trackPropagation.cxx +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// -// Task to add a table of track parameters propagated to the primary vertex -// - -#include "Common/Core/TableHelper.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/TrackSelectionTables.h" -#include "Common/Tools/TrackTuner.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -// The Run 3 AO2D stores the tracks at the point of innermost update. For a track with ITS this is the innermost (or second innermost) -// ITS layer. For a track without ITS, this is the TPC inner wall or for loopers in the TPC even a radius beyond that. -// In order to use the track parameters, the tracks have to be propagated to the collision vertex which is done by this task. -// The task consumes the TracksIU and TracksCovIU tables and produces Tracks and TracksCov to which then the user analysis can subscribe. -// -// This task is not needed for Run 2 converted data. -// There are two versions of the task (see process flags), one producing also the covariance matrix and the other only the tracks table. - -using namespace o2; -using namespace o2::framework; -// using namespace o2::framework::expressions; - -struct TrackPropagation { - Produces tracksParPropagated; - Produces tracksParExtensionPropagated; - - Produces tracksParCovPropagated; - Produces tracksParCovExtensionPropagated; - - Produces tracksDCA; - Produces tracksDCACov; - - Produces tunertable; - - Service ccdb; - - bool fillTracksDCA = false; - bool fillTracksDCACov = false; - int runNumber = -1; - - o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - - const o2::dataformats::MeanVertexObject* mMeanVtx = nullptr; - o2::parameters::GRPMagField* grpmag = nullptr; - o2::base::MatLayerCylSet* lut = nullptr; - TrackTuner trackTunerObj; - - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; - Configurable minPropagationRadius{"minPropagationDistance", o2::constants::geom::XTPCInnerRef + 0.1, "Only tracks which are at a smaller radius will be propagated, defaults to TPC inner wall"}; - // for TrackTuner only (MC smearing) - Configurable useTrackTuner{"useTrackTuner", false, "Apply track tuner corrections to MC"}; - Configurable fillTrackTunerTable{"fillTrackTunerTable", false, "flag to fill track tuner table"}; - Configurable trackTunerConfigSource{"trackTunerConfigSource", aod::track_tuner::InputString, "1: input string; 2: TrackTuner Configurables"}; - Configurable trackTunerParams{"trackTunerParams", "debugInfo=0|updateTrackDCAs=1|updateTrackCovMat=1|updateCurvature=0|updateCurvatureIU=0|updatePulls=0|isInputFileFromCCDB=1|pathInputFile=Users/m/mfaggin/test/inputsTrackTuner/PbPb2022|nameInputFile=trackTuner_DataLHC22sPass5_McLHC22l1b2_run529397.root|pathFileQoverPt=Users/h/hsharma/qOverPtGraphs|nameFileQoverPt=D0sigma_Data_removal_itstps_MC_LHC22b1b.root|usePvRefitCorrections=0|qOverPtMC=-1.|qOverPtData=-1.", "TrackTuner parameter initialization (format: =|=)"}; - ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - OutputObj trackTunedTracks{TH1D("trackTunedTracks", "", 1, 0.5, 1.5), OutputObjHandlingPolicy::AnalysisObject}; - - // OutputObj hDCAxyVsPtRec{TH2F("hDCAxyVsPtRec", ";DCAxy;PtRec", 600, -0.15, 0.15, axisPtQA)}; - // OutputObj hDCAxyVsPtMC{TH2F("hDCAxyVsPtMC", ";DCAxy;PtMC", 600, -0.15, 0.15, axisPtQA)}; - - using TracksIUWithMc = soa::Join; - - HistogramRegistry registry{"registry"}; - - void init(o2::framework::InitContext& initContext) - { - int nEnabledProcesses = 0; - if (doprocessStandard) { - LOG(info) << "Enabling processStandard"; - nEnabledProcesses++; - } - if (doprocessCovarianceMc) { - LOG(info) << "Enabling processCovarianceMc"; - nEnabledProcesses++; - } - - if (doprocessCovariance) { - LOG(info) << "Enabling processCovariance"; - nEnabledProcesses++; - } - - if (doprocessStandardWithPID) { - LOG(info) << "Enabling processStandardWithPID"; - nEnabledProcesses++; - } - if (doprocessCovarianceWithPID) { - LOG(info) << "Enabling processCovarianceWithPID"; - nEnabledProcesses++; - } - if (nEnabledProcesses != 1) { - LOG(fatal) << "Exactly one process flag must be set to true. Please choose one."; - } - // Checking if the tables are requested in the workflow and enabling them - fillTracksDCA = o2::common::core::isTableRequiredInWorkflow(initContext, "TracksDCA"); - fillTracksDCACov = o2::common::core::isTableRequiredInWorkflow(initContext, "TracksDCACov"); - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - - // Histograms for track tuner - AxisSpec axisBinsDCA = {600, -0.15f, 0.15f, "#it{dca}_{xy} (cm)"}; - registry.add("hDCAxyVsPtRec", "hDCAxyVsPtRec", kTH2F, {axisBinsDCA, axisPtQA}); - registry.add("hDCAxyVsPtMC", "hDCAxyVsPtMC", kTH2F, {axisBinsDCA, axisPtQA}); - registry.add("hDCAzVsPtRec", "hDCAzVsPtRec", kTH2F, {axisBinsDCA, axisPtQA}); - registry.add("hDCAzVsPtMC", "hDCAzVsPtMC", kTH2F, {axisBinsDCA, axisPtQA}); - - /// TrackTuner initialization - if (useTrackTuner) { - std::string outputStringParams = ""; - switch (trackTunerConfigSource) { - case aod::track_tuner::InputString: - outputStringParams = trackTunerObj.configParams(trackTunerParams); - break; - case aod::track_tuner::Configurables: - outputStringParams = trackTunerObj.configParams(); - break; - - default: - LOG(fatal) << "TrackTuner configuration source not defined. Fix it! (Supported options: input string (1); Configurables (2))"; - break; - } - - trackTunerObj.getDcaGraphs(); - trackTunedTracks->SetTitle(outputStringParams.c_str()); - trackTunedTracks->GetXaxis()->SetBinLabel(1, "all tracks"); - } - } - - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) - { - if (runNumber == bc.runNumber()) { - return; - } - - // load matLUT for this timestamp - if (!lut) { - LOG(info) << "Loading material look-up table for timestamp: " << bc.timestamp(); - lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp(lutPath, bc.timestamp())); - } else { - LOG(info) << "Material look-up table already in place. Not reloading."; - } - - grpmag = ccdb->getForTimeStamp(grpmagPath, bc.timestamp()); - LOG(info) << "Setting magnetic field to current " << grpmag->getL3Current() << " A for run " << bc.runNumber() << " from its GRPMagField CCDB object"; - o2::base::Propagator::initFieldFromGRP(grpmag); - o2::base::Propagator::Instance()->setMatLUT(lut); - mMeanVtx = ccdb->getForTimeStamp(mVtxPath, bc.timestamp()); - runNumber = bc.runNumber(); - } - - // Running variables - std::array mDcaInfo; - o2::dataformats::DCA mDcaInfoCov; - o2::dataformats::VertexBase mVtx; - o2::track::TrackParametrization mTrackPar; - o2::track::TrackParametrizationWithError mTrackParCov; - - template - void fillTrackTables(TTrack const& tracks, - TParticle const&, - aod::Collisions const&, - aod::BCsWithTimestamps const& bcs) - { - if (bcs.size() == 0) { - return; - } - initCCDB(bcs.begin()); - - if constexpr (fillCovMat) { - tracksParCovPropagated.reserve(tracks.size()); - tracksParCovExtensionPropagated.reserve(tracks.size()); - if (fillTracksDCACov) { - tracksDCACov.reserve(tracks.size()); - } - } else { - tracksParPropagated.reserve(tracks.size()); - tracksParExtensionPropagated.reserve(tracks.size()); - if (fillTracksDCA) { - tracksDCA.reserve(tracks.size()); - } - } - - for (auto& track : tracks) { - if constexpr (fillCovMat) { - if (fillTracksDCA || fillTracksDCACov) { - mDcaInfoCov.set(999, 999, 999, 999, 999); - } - setTrackParCov(track, mTrackParCov); - if constexpr (useTrkPid) { - mTrackParCov.setPID(track.pidForTracking()); - } - } else { - if (fillTracksDCA) { - mDcaInfo[0] = 999; - mDcaInfo[1] = 999; - } - setTrackPar(track, mTrackPar); - if constexpr (useTrkPid) { - mTrackPar.setPID(track.pidForTracking()); - } - } - // auto trackParCov = getTrackParCov(track); - aod::track::TrackTypeEnum trackType = (aod::track::TrackTypeEnum)track.trackType(); - // std::array trackPxPyPz; - // std::array trackPxPyPzTuned = {0.0, 0.0, 0.0}; - double q2OverPtNew = -9999.; - // Only propagate tracks which have passed the innermost wall of the TPC (e.g. skipping loopers etc). Others fill unpropagated. - if (track.trackType() == aod::track::TrackIU && track.x() < minPropagationRadius) { - if constexpr (isMc && fillCovMat) { // checking MC and fillCovMat block begins - // bool hasMcParticle = track.has_mcParticle(); - if (useTrackTuner) { - trackTunedTracks->Fill(1); // all tracks - bool hasMcParticle = track.has_mcParticle(); - if (hasMcParticle) { - auto mcParticle = track.mcParticle(); - trackTunerObj.tuneTrackParams(mcParticle, mTrackParCov, matCorr, &mDcaInfoCov, trackTunedTracks); - q2OverPtNew = mTrackParCov.getQ2Pt(); - } - } - } // MC and fillCovMat block ends - bool isPropagationOK = true; - - if (track.has_collision()) { - auto const& collision = track.collision(); - if constexpr (fillCovMat) { - mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); - mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); - isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, mTrackParCov, 2.f, matCorr, &mDcaInfoCov); - } else { - isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, mTrackPar, 2.f, matCorr, &mDcaInfo); - } - } else { - if constexpr (fillCovMat) { - mVtx.setPos({mMeanVtx->getX(), mMeanVtx->getY(), mMeanVtx->getZ()}); - mVtx.setCov(mMeanVtx->getSigmaX() * mMeanVtx->getSigmaX(), 0.0f, mMeanVtx->getSigmaY() * mMeanVtx->getSigmaY(), 0.0f, 0.0f, mMeanVtx->getSigmaZ() * mMeanVtx->getSigmaZ()); - isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, mTrackParCov, 2.f, matCorr, &mDcaInfoCov); - } else { - isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz({mMeanVtx->getX(), mMeanVtx->getY(), mMeanVtx->getZ()}, mTrackPar, 2.f, matCorr, &mDcaInfo); - } - } - if (isPropagationOK) { - trackType = aod::track::Track; - } - // filling some QA histograms for track tuner test purpose - if constexpr (isMc && fillCovMat) { // checking MC and fillCovMat block begins - if (track.has_mcParticle() && isPropagationOK) { - auto mcParticle1 = track.mcParticle(); - // && abs(mcParticle1.pdgCode())==211 - if (mcParticle1.isPhysicalPrimary()) { - registry.fill(HIST("hDCAxyVsPtRec"), mDcaInfoCov.getY(), mTrackParCov.getPt()); - registry.fill(HIST("hDCAxyVsPtMC"), mDcaInfoCov.getY(), mcParticle1.pt()); - registry.fill(HIST("hDCAzVsPtRec"), mDcaInfoCov.getZ(), mTrackParCov.getPt()); - registry.fill(HIST("hDCAzVsPtMC"), mDcaInfoCov.getZ(), mcParticle1.pt()); - } - } - } // MC and fillCovMat block ends - } - // Filling modified Q/Pt values at IU/production point by track tuner in track tuner table - if (useTrackTuner && fillTrackTunerTable) { - tunertable(q2OverPtNew); - } - // LOG(info) << " trackPropagation (this value filled in tuner table)--> " << q2OverPtNew; - if constexpr (fillCovMat) { - tracksParPropagated(track.collisionId(), trackType, mTrackParCov.getX(), mTrackParCov.getAlpha(), mTrackParCov.getY(), mTrackParCov.getZ(), mTrackParCov.getSnp(), mTrackParCov.getTgl(), mTrackParCov.getQ2Pt()); - tracksParExtensionPropagated(mTrackParCov.getPt(), mTrackParCov.getP(), mTrackParCov.getEta(), mTrackParCov.getPhi()); - // TODO do we keep the rho as 0? Also the sigma's are duplicated information - tracksParCovPropagated(std::sqrt(mTrackParCov.getSigmaY2()), std::sqrt(mTrackParCov.getSigmaZ2()), std::sqrt(mTrackParCov.getSigmaSnp2()), - std::sqrt(mTrackParCov.getSigmaTgl2()), std::sqrt(mTrackParCov.getSigma1Pt2()), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - tracksParCovExtensionPropagated(mTrackParCov.getSigmaY2(), mTrackParCov.getSigmaZY(), mTrackParCov.getSigmaZ2(), mTrackParCov.getSigmaSnpY(), - mTrackParCov.getSigmaSnpZ(), mTrackParCov.getSigmaSnp2(), mTrackParCov.getSigmaTglY(), mTrackParCov.getSigmaTglZ(), mTrackParCov.getSigmaTglSnp(), - mTrackParCov.getSigmaTgl2(), mTrackParCov.getSigma1PtY(), mTrackParCov.getSigma1PtZ(), mTrackParCov.getSigma1PtSnp(), mTrackParCov.getSigma1PtTgl(), - mTrackParCov.getSigma1Pt2()); - if (fillTracksDCA) { - tracksDCA(mDcaInfoCov.getY(), mDcaInfoCov.getZ()); - } - if (fillTracksDCACov) { - tracksDCACov(mDcaInfoCov.getSigmaY2(), mDcaInfoCov.getSigmaZ2()); - } - } else { - tracksParPropagated(track.collisionId(), trackType, mTrackPar.getX(), mTrackPar.getAlpha(), mTrackPar.getY(), mTrackPar.getZ(), mTrackPar.getSnp(), mTrackPar.getTgl(), mTrackPar.getQ2Pt()); - tracksParExtensionPropagated(mTrackPar.getPt(), mTrackPar.getP(), mTrackPar.getEta(), mTrackPar.getPhi()); - if (fillTracksDCA) { - tracksDCA(mDcaInfo[0], mDcaInfo[1]); - } - } - } - } - - void processStandard(aod::StoredTracksIU const& tracks, aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) - { - fillTrackTables(tracks, tracks, collisions, bcs); - } - PROCESS_SWITCH(TrackPropagation, processStandard, "Process without covariance", true); - - void processStandardWithPID(soa::Join const& tracks, aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) - { - fillTrackTables, /*Particle*/ soa::Join, /*isMc = */ false, /*fillCovMat =*/false, /*useTrkPid =*/true>(tracks, tracks, collisions, bcs); - } - PROCESS_SWITCH(TrackPropagation, processStandardWithPID, "Process without covariance and with PID in tracking", false); - - // ----------------------- - void processCovarianceMc(TracksIUWithMc const& tracks, aod::McParticles const& mcParticles, aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) - { - // auto table_extension = soa::Extend(tracks); - fillTrackTables(tracks, mcParticles, collisions, bcs); - } - PROCESS_SWITCH(TrackPropagation, processCovarianceMc, "Process with covariance on MC", false); - - void processCovariance(soa::Join const& tracks, aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) - { - fillTrackTables, /*Particle*/ soa::Join, /*isMc = */ false, /*fillCovMat =*/true, /*useTrkPid =*/false>(tracks, tracks, collisions, bcs); - } - PROCESS_SWITCH(TrackPropagation, processCovariance, "Process with covariance", false); - // ------------------------ - - void processCovarianceWithPID(soa::Join const& tracks, aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs) - { - fillTrackTables, /*Particle*/ soa::Join, /*isMc = */ false, /*fillCovMat =*/true, /*useTrkPid =*/false>(tracks, tracks, collisions, bcs); - } - PROCESS_SWITCH(TrackPropagation, processCovarianceWithPID, "Process with covariance and with PID in tracking", false); -}; - -//**************************************************************************************** -/** - * Workflow definition. - */ -//**************************************************************************************** -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; - return workflow; -} diff --git a/Common/TableProducer/trackToCollisionAssociator.cxx b/Common/TableProducer/trackToCollisionAssociator.cxx index f789c250cac..5c4bd3690b9 100644 --- a/Common/TableProducer/trackToCollisionAssociator.cxx +++ b/Common/TableProducer/trackToCollisionAssociator.cxx @@ -39,10 +39,11 @@ struct TrackToCollisionAssociation { Configurable nSigmaForTimeCompat{"nSigmaForTimeCompat", 4.f, "number of sigmas for time compatibility"}; Configurable timeMargin{"timeMargin", 0.f, "time margin in ns added to uncertainty because of uncalibrated TPC"}; Configurable setTrackSelections{"setTrackSelections", 1, "flag to apply track selections: -1=minimal track selection for Run 2 (standard association); 0=none; 1=global track w/o DCA selection; 2=only ITS quality"}; - Configurable usePVAssociation{"usePVAssociation", true, "if the track is a PV contributor, use the collision time for it"}; + Configurable usePVAssociation{"usePVAssociation", 1, "use information of PV contribution in reassociation; 0 -> off, 1 -> reassociate only with collisions in the same BC, 2 -> as 1 only for tracks associated to high multiplicity vertices"}; Configurable includeUnassigned{"includeUnassigned", false, "consider also tracks which are not assigned to any collision"}; Configurable fillTableOfCollIdsPerTrack{"fillTableOfCollIdsPerTrack", false, "fill additional table with vector of collision ids per track"}; Configurable bcWindowForOneSigma{"bcWindowForOneSigma", 60, "BC window to be multiplied by the number of sigmas to define maximum window to be considered"}; + Configurable maxPvContributorsForLowMultReassoc{"maxPvContributorsForLowMultReassoc", 10, "Maximum number of PV contributors to consider a collision at low multiplicity and reassociate tracks even if PV contributors if enabled"}; CollisionAssociation collisionAssociator; @@ -67,6 +68,7 @@ struct TrackToCollisionAssociation { collisionAssociator.setIncludeUnassigned(includeUnassigned); collisionAssociator.setFillTableOfCollIdsPerTrack(fillTableOfCollIdsPerTrack); collisionAssociator.setBcWindow(bcWindowForOneSigma); + collisionAssociator.setMaxPvContributorsForLowMultReassoc(maxPvContributorsForLowMultReassoc); } void processAssocWithTime(Collisions const& collisions, TracksWithSel const& tracksUnfiltered, TracksWithSelFilter const& tracks, AmbiguousTracks const& ambiguousTracks, BCs const& bcs) diff --git a/Common/TableProducer/trackselection.cxx b/Common/TableProducer/trackselection.cxx index 2681c503ad5..4cd90b0c1c7 100644 --- a/Common/TableProducer/trackselection.cxx +++ b/Common/TableProducer/trackselection.cxx @@ -157,10 +157,11 @@ struct TrackSelectionTask { } if (isRun3) { for (const auto& track : tracks) { + const o2::aod::track::TrackSelectionFlags::flagtype trackflagGlob = globalTracks.IsSelectedMask(track); if (produceTable == 1) { - filterTable((uint8_t)0, - globalTracks.IsSelectedMask(track), + filterTable(static_cast(0), + trackflagGlob, filtBit1.IsSelected(track), filtBit2.IsSelected(track), filtBit3.IsSelected(track), @@ -168,7 +169,6 @@ struct TrackSelectionTask { filtBit5.IsSelected(track)); } if (produceFBextendedTable == 1) { - o2::aod::track::TrackSelectionFlags::flagtype trackflagGlob = globalTracks.IsSelectedMask(track); o2::aod::track::TrackSelectionFlags::flagtype trackflagFB1 = filtBit1.IsSelectedMask(track); o2::aod::track::TrackSelectionFlags::flagtype trackflagFB2 = filtBit2.IsSelectedMask(track); // o2::aod::track::TrackSelectionFlags::flagtype trackflagFB3 = filtBit3.IsSelectedMask(track); // only temporarily commented, will be used @@ -200,8 +200,8 @@ struct TrackSelectionTask { for (const auto& track : tracks) { o2::aod::track::TrackSelectionFlags::flagtype trackflagGlob = globalTracks.IsSelectedMask(track); if (produceTable == 1) { - filterTable((uint8_t)globalTracksSDD.IsSelected(track), - globalTracks.IsSelectedMask(track), + filterTable(static_cast(globalTracksSDD.IsSelected(track)), + trackflagGlob, filtBit1.IsSelected(track), filtBit2.IsSelected(track), filtBit3.IsSelected(track), diff --git a/Common/TableProducer/zdcExtraTableProducer.cxx b/Common/TableProducer/zdcExtraTableProducer.cxx index 58098f02576..e04c55f30d7 100644 --- a/Common/TableProducer/zdcExtraTableProducer.cxx +++ b/Common/TableProducer/zdcExtraTableProducer.cxx @@ -33,6 +33,9 @@ #include #include +#include + +#include #include #include @@ -52,10 +55,12 @@ struct ZdcExtraTableProducer { // Configurable nBins{"nBins", 400, "n bins"}; Configurable maxZN{"maxZN", 399.5, "Max ZN signal"}; - Configurable tdcCut{"tdcCut", false, "Flag for TDC cut"}; - Configurable tdcZNmincut{"tdcZNmincut", -2.5, "Min ZN TDC cut"}; - Configurable tdcZNmaxcut{"tdcZNmaxcut", 2.5, "Max ZN TDC cut"}; - Configurable cfgUsePMC{"cfgUsePMC", true, "Use common PM (true) or sum of PMs (false) "}; + Configurable applyTdcCut{"applyTdcCut", false, "Flag for TDC cut"}; + Configurable tdcZnMin{"tdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable tdcZnMax{"tdcZnMax", 2.5, "Max ZN TDC cut"}; + Configurable useCfactor{"useCfactor", true, "Use C normalization factor (depends on multiplicity) for centroid calculation"}; + Configurable usePMC{"usePMC", true, "Use common PM (true) or sum of PMs (false) "}; + // Event selections Configurable cfgEvSelSel8{"cfgEvSelSel8", true, "Event selection: sel8"}; Configurable cfgEvSelVtxZ{"cfgEvSelVtxZ", 10, "Event selection: zVtx"}; @@ -66,23 +71,22 @@ struct ZdcExtraTableProducer { Configurable cfgEvSelsNoCollInTimeRangeStandard{"cfgEvSelsNoCollInTimeRangeStandard", false, "Event selection: no collision in time range standard"}; Configurable cfgEvSelsIsVertexITSTPC{"cfgEvSelsIsVertexITSTPC", false, "Event selection: is vertex ITSTPC"}; Configurable cfgEvSelsIsGoodITSLayersAll{"cfgEvSelsIsGoodITSLayersAll", false, "Event selection: is good ITS layers all"}; - // Calibration settings - Configurable cfgCalibrationDownscaling{"cfgCalibrationDownscaling", 1.f, "Percentage of events to be saved to derived table"}; // Output settings - Configurable cfgSaveQaHistos{"cfgSaveQaHistos", false, "Flag to save QA histograms"}; + Configurable calibrationDownscaling{"calibrationDownscaling", 1.f, "Percentage of events to be saved to derived table"}; + Configurable saveQaHistos{"saveQaHistos", false, "Flag to save QA histograms"}; enum SelectionCriteria { - evSel_zvtx, - evSel_sel8, - evSel_occupancy, - evSel_kNoSameBunchPileup, - evSel_kIsGoodZvtxFT0vsPV, - evSel_kNoCollInTimeRangeStandard, - evSel_kIsVertexITSTPC, - evSel_kIsGoodITSLayersAll, - evSel_allEvents, - nEventSelections + ZVtxCut, + Sel8, + OccupancyCut, + NoSameBunchPileup, + IsGoodZvtxFT0vsPV, + NoCollInTimeRangeStandard, + IsVertexITSTPC, + IsGoodITSLayersAll, + AllEvents, + NEventSelections }; HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -90,19 +94,19 @@ struct ZdcExtraTableProducer { void init(InitContext const&) { - registry.add("hEventCount", "Number of Event; Cut; #Events Passed Cut", {HistType::kTH1D, {{nEventSelections, 0, nEventSelections}}}); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_allEvents + 1, "All events"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_zvtx + 1, "vtxZ"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_sel8 + 1, "Sel8"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_occupancy + 1, "kOccupancy"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_kNoSameBunchPileup + 1, "kNoSameBunchPileup"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_kIsGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_kNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_kIsVertexITSTPC + 1, "kIsVertexITSTPC"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_kIsGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + registry.add("hEventCount", "Number of Event; Cut; #Events Passed Cut", {HistType::kTH1D, {{NEventSelections, 0, NEventSelections}}}); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AllEvents + 1, "AllEvents"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(ZVtxCut + 1, "ZVtxCut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(Sel8 + 1, "Sel8"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(OccupancyCut + 1, "OccupancyCut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoSameBunchPileup + 1, "NoSameBunchPileup"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV + 1, "IsGoodZvtxFT0vsPV"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard + 1, "NoCollInTimeRangeStandard"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsVertexITSTPC + 1, "IsVertexITSTPC"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodITSLayersAll + 1, "IsGoodITSLayersAll"); // Skip histogram registration if QA flag is false - if (!cfgSaveQaHistos) { + if (!saveQaHistos) { return; } @@ -124,60 +128,60 @@ struct ZdcExtraTableProducer { } template - uint8_t eventSelected(TCollision collision) + uint8_t eventSelected(TCollision const& collision) { uint8_t selectionBits = 0; - bool selected; + bool selected = false; - registry.fill(HIST("hEventCount"), evSel_allEvents); + registry.fill(HIST("hEventCount"), AllEvents); selected = std::fabs(collision.posZ()) < cfgEvSelVtxZ; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_zvtx); - registry.fill(HIST("hEventCount"), evSel_zvtx); + SETBIT(selectionBits, ZVtxCut); + registry.fill(HIST("hEventCount"), ZVtxCut); } selected = collision.sel8(); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_sel8); - registry.fill(HIST("hEventCount"), evSel_sel8); + SETBIT(selectionBits, Sel8); + registry.fill(HIST("hEventCount"), Sel8); } auto occupancy = collision.trackOccupancyInTimeRange(); selected = occupancy <= cfgEvSelsMaxOccupancy; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_occupancy); - registry.fill(HIST("hEventCount"), evSel_occupancy); + SETBIT(selectionBits, OccupancyCut); + registry.fill(HIST("hEventCount"), OccupancyCut); } selected = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoSameBunchPileup); - registry.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); + SETBIT(selectionBits, NoSameBunchPileup); + registry.fill(HIST("hEventCount"), NoSameBunchPileup); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodZvtxFT0vsPV); - registry.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); + SETBIT(selectionBits, IsGoodZvtxFT0vsPV); + registry.fill(HIST("hEventCount"), IsGoodZvtxFT0vsPV); } selected = collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoCollInTimeRangeStandard); - registry.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); + SETBIT(selectionBits, NoCollInTimeRangeStandard); + registry.fill(HIST("hEventCount"), NoCollInTimeRangeStandard); } selected = collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsVertexITSTPC); - registry.fill(HIST("hEventCount"), evSel_kIsVertexITSTPC); + SETBIT(selectionBits, IsVertexITSTPC); + registry.fill(HIST("hEventCount"), IsVertexITSTPC); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodITSLayersAll); - registry.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); + SETBIT(selectionBits, IsGoodITSLayersAll); + registry.fill(HIST("hEventCount"), IsGoodITSLayersAll); } return selectionBits; @@ -185,6 +189,7 @@ struct ZdcExtraTableProducer { void process(ColEvSels const& cols, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcs*/) { + // collision-based event selection constexpr int NTowers = 4; // number of ZDC towers @@ -195,23 +200,30 @@ struct ZdcExtraTableProducer { uint8_t evSelection = eventSelected(collision); - // add event selection - if (cfgEvSelSel8 && !(evSelection & (1 << evSel_sel8))) + if (cfgEvSelSel8 && !TESTBIT(evSelection, Sel8)) { continue; - if (!(evSelection & (1 << evSel_zvtx))) + } + if (!TESTBIT(evSelection, ZVtxCut)) { continue; - if (cfgEvSelsDoOccupancySel && !(evSelection & (1 << evSel_occupancy))) + } + if (cfgEvSelsDoOccupancySel && !TESTBIT(evSelection, OccupancyCut)) { continue; - if (cfgEvSelsNoSameBunchPileupCut && !(evSelection & (1 << evSel_kNoSameBunchPileup))) + } + if (cfgEvSelsNoSameBunchPileupCut && !TESTBIT(evSelection, NoSameBunchPileup)) { continue; - if (cfgEvSelsIsGoodZvtxFT0vsPV && !(evSelection & (1 << evSel_kIsGoodZvtxFT0vsPV))) + } + if (cfgEvSelsIsGoodZvtxFT0vsPV && !TESTBIT(evSelection, IsGoodZvtxFT0vsPV)) { continue; - if (cfgEvSelsNoCollInTimeRangeStandard && !(evSelection & (1 << evSel_kNoCollInTimeRangeStandard))) + } + if (cfgEvSelsNoCollInTimeRangeStandard && !TESTBIT(evSelection, NoCollInTimeRangeStandard)) { continue; - if (cfgEvSelsIsVertexITSTPC && !(evSelection & (1 << evSel_kIsVertexITSTPC))) + } + if (cfgEvSelsIsVertexITSTPC && !TESTBIT(evSelection, IsVertexITSTPC)) { continue; - if (cfgEvSelsIsGoodITSLayersAll && !(evSelection & (1 << evSel_kIsGoodITSLayersAll))) + } + if (cfgEvSelsIsGoodITSLayersAll && !TESTBIT(evSelection, IsGoodITSLayersAll)) { continue; + } float centrality = collision.centFT0C(); @@ -226,11 +238,11 @@ struct ZdcExtraTableProducer { double tdcZNA = zdc.timeZNA(); // OR we can select a narrow window in both ZN TDCs using the configurable parameters - if (tdcCut) { // a narrow TDC window is set - if ((tdcZNC >= tdcZNmincut) && (tdcZNC <= tdcZNmaxcut)) { + if (applyTdcCut) { // a narrow TDC window is set + if ((tdcZNC >= tdcZnMin) && (tdcZNC <= tdcZnMax)) { isZNChit = true; } - if ((tdcZNA >= tdcZNmincut) && (tdcZNA <= tdcZNmaxcut)) { + if ((tdcZNA >= tdcZnMin) && (tdcZNA <= tdcZnMax)) { isZNAhit = true; } } else { // if no window on TDC is set @@ -244,8 +256,8 @@ struct ZdcExtraTableProducer { // double sumZNC = 0; double sumZNA = 0; - double pmqZNC[4] = {}; - double pmqZNA[4] = {}; + std::array pmqZNC = {}; + std::array pmqZNA = {}; // if (isZNChit) { for (int it = 0; it < NTowers; it++) { @@ -253,7 +265,7 @@ struct ZdcExtraTableProducer { sumZNC += pmqZNC[it]; } - if (cfgSaveQaHistos) { + if (saveQaHistos) { registry.get(HIST("ZNCpmc"))->Fill(pmcZNC); registry.get(HIST("ZNCpm1"))->Fill(pmqZNC[0]); registry.get(HIST("ZNCpm2"))->Fill(pmqZNC[1]); @@ -268,7 +280,7 @@ struct ZdcExtraTableProducer { sumZNA += pmqZNA[it]; } // - if (cfgSaveQaHistos) { + if (saveQaHistos) { registry.get(HIST("ZNApmc"))->Fill(pmcZNA); registry.get(HIST("ZNApm1"))->Fill(pmqZNA[0]); registry.get(HIST("ZNApm2"))->Fill(pmqZNA[1]); @@ -283,8 +295,8 @@ struct ZdcExtraTableProducer { constexpr float kBeamEne = 5.36 * 0.5; // Provide coordinates of centroid over ZN (side C) front face - constexpr float X[4] = {-1.75, 1.75, -1.75, 1.75}; - constexpr float Y[4] = {-1.75, -1.75, 1.75, 1.75}; + constexpr std::array X = {-1.75, 1.75, -1.75, 1.75}; + constexpr std::array Y = {-1.75, -1.75, 1.75, 1.75}; constexpr float kAlpha = 0.395; // saturation correction float numXZNC = 0., numYZNC = 0., denZNC = 0.; @@ -310,8 +322,8 @@ struct ZdcExtraTableProducer { float zncCommon = 0; float znaCommon = 0; - // Use sum of PMTs (cfgUsePMC == false) when common PMT is saturated - if (cfgUsePMC) { + // Use sum of PMTs (usePMC == false) when common PMT is saturated + if (usePMC) { zncCommon = pmcZNC; znaCommon = pmcZNA; } else { @@ -319,11 +331,17 @@ struct ZdcExtraTableProducer { znaCommon = sumZNA; } - float centroidZNC[2], centroidZNA[2]; + std::array centroidZNC = {}; + std::array centroidZNA = {}; if (denZNC != 0.) { - float nSpecnC = zncCommon / kBeamEne; - float cZNC = 1.89358 - 0.71262 / (nSpecnC + 0.71789); + float cZNC = 1.0; + + if (useCfactor) { + float nSpecnC = zncCommon / kBeamEne; + cZNC = 1.89358 - 0.71262 / (nSpecnC + 0.71789); + } + centroidZNC[0] = cZNC * numXZNC / denZNC; centroidZNC[1] = cZNC * numYZNC / denZNC; } else { @@ -332,15 +350,19 @@ struct ZdcExtraTableProducer { } // if (denZNA != 0.) { - float nSpecnA = znaCommon / kBeamEne; - float cZNA = 1.89358 - 0.71262 / (nSpecnA + 0.71789); + float cZNA = 1.0; + if (useCfactor) { + float nSpecnA = znaCommon / kBeamEne; + cZNA = 1.89358 - 0.71262 / (nSpecnA + 0.71789); + } + centroidZNA[0] = cZNA * numXZNA / denZNA; centroidZNA[1] = cZNA * numYZNA / denZNA; } else { centroidZNA[0] = 999.; centroidZNA[1] = 999.; } - if (cfgSaveQaHistos) { + if (saveQaHistos) { if (isZNChit) { registry.get(HIST("ZNCCentroid"))->Fill(centroidZNC[0], centroidZNC[1]); } @@ -353,7 +375,7 @@ struct ZdcExtraTableProducer { auto vx = collision.posX(); auto vy = collision.posY(); - if ((isZNAhit || isZNChit) && (gRandom->Uniform() < cfgCalibrationDownscaling)) { + if ((isZNAhit || isZNChit) && (gRandom->Uniform() < calibrationDownscaling)) { zdcextras(pmcZNA, pmqZNA[0], pmqZNA[1], pmqZNA[2], pmqZNA[3], tdcZNA, centroidZNA[0], centroidZNA[1], pmcZNC, pmqZNC[0], pmqZNC[1], pmqZNC[2], pmqZNC[3], tdcZNC, centroidZNC[0], centroidZNC[1], centrality, vx, vy, vz, foundBC.timestamp(), foundBC.runNumber(), evSelection); } } diff --git a/Common/TableProducer/zdcTaskLightIons.cxx b/Common/TableProducer/zdcTaskLightIons.cxx index 3e188bb2dc3..7681551328c 100644 --- a/Common/TableProducer/zdcTaskLightIons.cxx +++ b/Common/TableProducer/zdcTaskLightIons.cxx @@ -18,6 +18,9 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/ZDCLightIons.h" +#include +#include +#include #include #include #include @@ -31,7 +34,10 @@ #include #include +#include #include +#include +#include using namespace o2; using namespace o2::aod; @@ -45,6 +51,14 @@ using ColEvSels = soa::Join zdcTableLI; + Service ccdb; + static const int nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; + std::bitset beamPatternA; + std::bitset beamPatternC; + std::bitset bcPatternB; + std::bitset bcPatternA; + std::bitset bcPatternC; + std::bitset bcPatternE; // Configurable parameters Configurable nBinsTiming{"nBinsTiming", 200, "n bins for debunching histo"}; @@ -78,6 +92,14 @@ struct ZdcTaskLightIons { nEventSelections }; + enum bcMaskBits { + bcMask_beamB, + bcMask_beamA, + bcMask_beamC, + bcMask_beamE, + nBits + }; + void init(InitContext const&) { registry.add("zdcDebunchHist", "ZN sum vs. diff; ZNA-ZNC (ns); ZNA+ZNC (ns)", {HistType::kTH2D, {{nBinsTiming, -20., 20.}, {nBinsTiming, -20., 20.}}}); @@ -97,7 +119,7 @@ struct ZdcTaskLightIons { } template - uint8_t eventSelected(TCollision collision) + uint8_t eventSelected(const TCollision& collision) { uint8_t selectionBits = 0; bool selected; @@ -106,50 +128,50 @@ struct ZdcTaskLightIons { selected = std::fabs(collision.posZ()) < cfgEvSelVtxZ; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_zvtx); + selectionBits |= static_cast(0x1u << evSel_zvtx); registry.fill(HIST("hEventCount"), evSel_zvtx); } selected = collision.sel8(); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_sel8); + selectionBits |= static_cast(0x1u << evSel_sel8); registry.fill(HIST("hEventCount"), evSel_sel8); } auto occupancy = collision.trackOccupancyInTimeRange(); selected = occupancy <= cfgEvSelsMaxOccupancy; if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_occupancy); + selectionBits |= static_cast(0x1u << evSel_occupancy); registry.fill(HIST("hEventCount"), evSel_occupancy); } selected = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoSameBunchPileup); + selectionBits |= static_cast(0x1u << evSel_kNoSameBunchPileup); registry.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodZvtxFT0vsPV); + selectionBits |= static_cast(0x1u << evSel_kIsGoodZvtxFT0vsPV); registry.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); } selected = collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoCollInTimeRangeStandard); + selectionBits |= static_cast(0x1u << evSel_kNoCollInTimeRangeStandard); registry.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); } selected = collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kNoITSROFrameBorder); + selectionBits |= static_cast(0x1u << evSel_kNoITSROFrameBorder); registry.fill(HIST("hEventCount"), evSel_kNoITSROFrameBorder); } selected = collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); if (selected) { - selectionBits |= (uint8_t)(0x1u << evSel_kIsGoodITSLayersAll); + selectionBits |= static_cast(0x1u << evSel_kIsGoodITSLayersAll); registry.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); } @@ -158,9 +180,46 @@ struct ZdcTaskLightIons { void processZDCBC(BCsRun3 const& bcs, aod::Zdcs const& /*zdcs*/) { + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + uint64_t timeStamp = bcs.iteratorAt(0).timestamp(); + std::map metadata; + // use getSpecific to set metadata to avoid crashes related to specific run number + auto grplhcif = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); + // auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", timeStamp); + if (grplhcif == nullptr) { + LOG(fatal) << "GRPLHCIFData not in database, timestamp:" << timeStamp; + } + + beamPatternA = grplhcif->getBunchFilling().getBeamPattern(0); + beamPatternC = grplhcif->getBunchFilling().getBeamPattern(1); + bcPatternB = grplhcif->getBunchFilling().getBCPattern(); + bcPatternA = beamPatternA & ~beamPatternC; + bcPatternC = ~beamPatternA & beamPatternC; + bcPatternE = ~beamPatternA & ~beamPatternC; + for (const auto& bc : bcs) { + if (bc.has_zdc()) { + // int64_t timestamp = bc.timestamp(); + auto timestampFromSOR = (bc.timestamp() - grplhcif->getFillNumberTime()) / 1e3; // Convert to seconds + + int bcInOrbit = bc.globalBC() % nBCsPerOrbit; + + uint8_t maskSel = 0; + if (bcPatternB[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamB); + } + if (bcPatternA[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamA); + } + if (bcPatternC[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamC); + } + if (bcPatternE[bcInOrbit]) { + maskSel |= static_cast(0x1u << bcMask_beamE); + } + auto tdcZNA = bc.zdc().timeZNA(); auto tdcZNC = bc.zdc().timeZNC(); auto tdcZPA = bc.zdc().timeZPA(); @@ -225,8 +284,9 @@ struct ZdcTaskLightIons { -1, -1, -1, -1., -1, -1, -1, - bc.timestamp(), - -1); + timestampFromSOR, + 0, + maskSel); } } } @@ -333,7 +393,8 @@ struct ZdcTaskLightIons { zv, centralityFT0C, centralityFT0A, centralityFT0M, foundBC.timestamp(), - evSelection); + evSelection, + 0); } } } diff --git a/Common/Tasks/CMakeLists.txt b/Common/Tasks/CMakeLists.txt index e60d7838882..bc40024c43e 100644 --- a/Common/Tasks/CMakeLists.txt +++ b/Common/Tasks/CMakeLists.txt @@ -84,6 +84,11 @@ o2physics_add_dpl_workflow(centrality-study PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(centrality-study-pp + SOURCES centralityStudypp.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(flow-test SOURCES flowTest.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -97,4 +102,9 @@ o2physics_add_dpl_workflow(muon-qa o2physics_add_dpl_workflow(zdc-table-reader SOURCES zdcTableReader.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore - COMPONENT_NAME Analysis) \ No newline at end of file + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(zdc-extra-table-reader + SOURCES zdcExtraTableReader.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/Common/Tasks/centralityQa.cxx b/Common/Tasks/centralityQa.cxx index 51dc8875e08..2bf1fd28e77 100644 --- a/Common/Tasks/centralityQa.cxx +++ b/Common/Tasks/centralityQa.cxx @@ -8,181 +8,924 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. + +/// +/// \file centralityQa.cxx +/// \brief This task does dedicated centrality QA +/// \author ALICE Collaboration +/// + +#include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include +#include #include +#include #include +#include #include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace o2; using namespace o2::framework; +using CentRun3 = soa::Join; +using BCsWithMatching = soa::Join; + struct CentralityQa { - Configurable nBins{"nBins", 1050, "number of bins"}; - Configurable INELgtZERO{"INELgtZERO", 1, "0 - no, 1 - yes"}; - OutputObj hCentRun2V0M{TH1F("hCentRun2V0M", "V0M", nBins, 0, 105.)}; - OutputObj hCentRun2V0A{TH1F("hCentRun2V0A", "V0A", nBins, 0, 105.)}; - OutputObj hCentRun2SPDTks{TH1F("hCentRun2SPDTks", "SPD Tracklets", nBins, 0, 105.)}; - OutputObj hCentRun2SPDCls{TH1F("hCentRun2SPDCls", "SPD Clusters", nBins, 0, 105.)}; - OutputObj hCentRun2CL0{TH1F("hCentRun2CL0", "CL0", nBins, 0, 105.)}; - OutputObj hCentRun2CL1{TH1F("hCentRun2CL1", "CL1", nBins, 0, 105.)}; - OutputObj hCentFV0A{TH1F("hCentFV0A", "FV0A", nBins, 0, 105.)}; - OutputObj hCentFT0M{TH1F("hCentFT0M", "FT0M", nBins, 0, 105.)}; - OutputObj hCentFT0A{TH1F("hCentFT0A", "FT0A", nBins, 0, 105.)}; - OutputObj hCentFT0C{TH1F("hCentFT0C", "FT0C", nBins, 0, 105.)}; - OutputObj hCentFDDM{TH1F("hCentFDDM", "FDDM", nBins, 0, 105.)}; - OutputObj hCentNTPV{TH1F("hCentNTPV", "NTPV", nBins, 0, 105.)}; - - // profiles of midrapidity multiplicity density - OutputObj hCentProfileFV0A{TProfile("hCentProfileFV0A", "FV0A", nBins, 0, 105.)}; - OutputObj hCentProfileFT0M{TProfile("hCentProfileFT0M", "FT0M", nBins, 0, 105.)}; - OutputObj hCentProfileFT0A{TProfile("hCentProfileFT0A", "FT0A", nBins, 0, 105.)}; - OutputObj hCentProfileFT0C{TProfile("hCentProfileFT0C", "FT0C", nBins, 0, 105.)}; - OutputObj hCentProfileFDDM{TProfile("hCentProfileFDDM", "FDDM", nBins, 0, 105.)}; - OutputObj hCentProfileNTPV{TProfile("hCentProfileNTPV", "NTPV", nBins, 0, 105.)}; - - void processRun2PP(soa::Join::iterator const& col) + HistogramRegistry histos{"histos"}; + std::map histPointers; + Service ccdb{}; + std::bitset collidingBunch; + + int runNumber{}; + uint64_t startOfRunTimestamp{}; + TList* hCentralityObjects = nullptr; + + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {1000, 0, 1000}, "Multiplicity"}; + ConfigurableAxis axisMultiplicityPV{"axisMultiplicityPV", {1000, 0, 1000}, "Multiplicity PV"}; + ConfigurableAxis axisChannelAmplitude{"axisChannelAmplitude", {5000, 0, 5000}, "Channel Amplitude"}; + ConfigurableAxis axisCentrality{"axisCentrality", {101, 0.0f, 101.0f}, "Centrality (%)"}; + + Configurable loopOverMcCollisionsForMcHist{"loopOverMcCollisionsForMcHist", false, "Fill MC histograms in a loop over MC collisions? If no, they will be filled in a loop over reconstructed collisions"}; + + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track (Run 3 only)"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference (Run 3 only)"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF (Run 3 only)"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD (Run 3 only)"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC (Run 3 only)"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds (Run 3 only)"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds (Run 3 only)"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds (Run 3 only)"}; + Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold (Run 3 only)"}; + Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF (Run 3 only)"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; + // fast check on occupancy + Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; + Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; + + Configurable requireIsBBT0A{"requireIsBBT0A", false, "Require beam-beam collisions based on timing information in FT0A"}; + Configurable requireIsBBT0C{"requireIsBBT0C", false, "Require beam-beam collisions based on timing information in FT0C"}; + + Configurable rejectMismatchedBCs{"rejectMismatchedBCs", false, "Reject collision with BC different from MC BC"}; + Configurable rejectMismatchedFoundBCs{"rejectMismatchedFoundBCs", false, "Reject collision with found BC different from MC BC"}; + + // Run 2 specific event selections + Configurable requireSel7{"requireSel7", true, "require sel7 event selection (Run 2 only: event selection decision based on V0A & V0C)"}; + Configurable requireINT7{"requireINT7", true, "require INT7 trigger selection (Run 2 only)"}; + Configurable rejectIncompleteDAQ{"rejectIncompleteDAQ", true, "reject events with incomplete DAQ (Run 2 only)"}; + Configurable requireConsistentSPDAndTrackVtx{"requireConsistentSPDAndTrackVtx", true, "reject events with inconsistent in SPD and Track vertices (Run 2 only)"}; + Configurable rejectPileupFromSPD{"rejectPileupFromSPD", true, "reject events with pileup according to SPD vertexer (Run 2 only)"}; + Configurable rejectV0PFPileup{"rejectV0PFPileup", false, "reject events tagged as OOB pileup according to V0 past-future info (Run 2 only)"}; + Configurable rejectPileupInMultBins{"rejectPileupInMultBins", true, "reject events tagged as pileup according to multiplicity-differential pileup checks (Run 2 only)"}; + Configurable rejectPileupMV{"rejectPileupMV", true, "reject events tagged as pileup according to according to multi-vertexer (Run 2 only)"}; + Configurable rejectTPCPileup{"rejectTPCPileup", false, "reject events tagged as pileup according to pileup in TPC (Run 2 only)"}; + Configurable requireNoV0MOnVsOffPileup{"requireNoV0MOnVsOffPileup", false, "reject events tagged as OOB pileup according to online-vs-offline VOM correlation (Run 2 only)"}; + Configurable requireNoSPDOnVsOffPileup{"requireNoSPDOnVsOffPileup", false, "reject events tagged as pileup according to online-vs-offline SPD correlation (Run 2 only)"}; + Configurable requireNoSPDClsVsTklBG{"requireNoSPDClsVsTklBG", true, "reject events tagged as beam-gas and pileup according to cluster-vs-tracklet correlation (Run 2 only)"}; + } eventSelections; + + struct : ConfigurableGroup { + std::string prefix = "bcsel"; + Configurable selectCollidingBCs{"selectCollidingBCs", true, "select colliding BCs"}; + Configurable selectTVX{"selectTVX", true, "select TVX"}; + Configurable selectFV0OrA{"selectFV0OrA", true, "select FV0 or A"}; + Configurable selectVertexZwithT0{"selectVertexZwithT0", false, "select vertex Z with T0"}; + Configurable vertexZwithT0{"vertexZwithT0", 1000, "vertex Z with T0"}; + Configurable selectBBT0{"selectBBT0", false, "select BBT0"}; + Configurable rejectZNAC{"rejectZNAC", false, "reject ZNAC"}; + Configurable rejectIsFlangeEvent{"rejectIsFlangeEvent", false, "reject is flange event"}; + } bcsel; + + struct : ConfigurableGroup { + std::string prefix = "studies"; + Configurable fv0a{"fv0a", true, "Enable centrality QA for fv0a"}; + Configurable ft0m{"ft0m", true, "Enable centrality QA for ft0m"}; + Configurable ft0mOuterA{"ft0mOuterA", true, "Enable centrality QA for ft0m"}; + Configurable ft0mAnchorCol{"ft0mAnchorCol", true, "Enable centrality QA for ft0m"}; + Configurable ft0mAnchorBc{"ft0mAnchorBc", true, "Enable centrality QA for ft0m"}; + Configurable ft0a{"ft0a", false, "Enable centrality QA for ft0a"}; + Configurable ft0c{"ft0c", true, "Enable centrality QA for ft0c"}; + Configurable ft0cVar1{"ft0cVar1", false, "Enable centrality QA for ft0cVar1"}; + Configurable ft0cVar2{"ft0cVar2", false, "Enable centrality QA for ft0cVar2"}; + Configurable fddm{"fddm", false, "Enable centrality QA for fddm"}; + Configurable ntpv{"ntpv", false, "Enable centrality QA for ntpv"}; + Configurable nGlo{"nGlo", true, "Enable centrality QA for nGlo"}; + Configurable mft{"mft", true, "Enable centrality QA for mft"}; + } studies; + + struct : ConfigurableGroup { + std::string prefix = "centrality"; + Configurable useCustomCalibration{"useCustomCalibration", false, "override the centrality from the central calibration with a different calibration provided in pathCentrality"}; + Configurable ccdbURL{"ccdbURL", "http://alice-ccdb.cern.ch", "ccdb url"}; + Configurable pathCentrality{"pathCentrality", "Centrality/Estimators", "path to centrality calibration if useCustomCalibration is enabled"}; + Configurable generator{"generator", "", "E.g. PYTHIA"}; + } centrality; + + enum EstimatorIndex { FV0A, + FT0M, + FT0MAnchorCol, + FT0MAnchorBC, + FT0MOuterA, + FT0A, + FT0C, + FT0CVar1, + FT0CVar2, + FDDM, + NTPV, + NGlobal, + MFT, + NEstimators }; + + static constexpr int NSuperCalibPars = 6; + static constexpr float CentralityNotFound = 105.f; + + struct Estimator { + CentralityQa* outer = nullptr; + bool doStudy = false; + std::string estName; + std::string histName; + std::array mcScalePars{}; + TH1* hCentrality = nullptr; + TFormula* mcScale = nullptr; + Estimator() = default; + explicit Estimator(CentralityQa* o, bool b, std::string s0, std::string s1) : outer(o), doStudy(b), estName(std::move(s0)), histName(std::move(s1)) {} + float getCentrality(const float mult, const float centTable) + { + if (outer->centrality.useCustomCalibration) { + float lMult = mult; + if (outer->doprocessRun3MonteCarlo && outer->hCentralityObjects != nullptr) { + mcScale = dynamic_cast(outer->hCentralityObjects->FindObject(TString::Format("%s-%s", outer->centrality.generator.value.c_str(), estName.c_str()).Data())); + if (!mcScale) { + return CentralityNotFound; + } + + for (int ixpar = 0; ixpar < NSuperCalibPars; ++ixpar) { + mcScalePars[ixpar] = mcScale->GetParameter(ixpar); + } + + auto scaleMC = [](float x, const std::array& pars) { + float core = ((pars[0] + pars[1] * std::pow(x, pars[2])) - pars[3]) / pars[4]; + if (core < 0.0f) { + return 0.0f; // this should be marked as low multiplicity and not mapped, core^pars[5] would be NaN + } + return std::pow(core, 1.0f / pars[5]); + }; + + lMult = scaleMC(mult, mcScalePars); + } + return hCentrality ? hCentrality->GetBinContent(hCentrality->FindBin(lMult)) : CentralityNotFound; + } + return centTable; + } + template + void configure(const TCollision& col) + { + if (!outer->centrality.useCustomCalibration) { + return; + } + + if (!col.has_foundBC()) { + return; + } + + const auto& bc = col.template foundBC_as(); + if (bc.runNumber() != outer->runNumber) { + outer->runNumber = bc.runNumber(); + LOGF(info, "Acquiring centrality calibration for run %i", outer->runNumber); + outer->hCentralityObjects = outer->ccdb->getForRun(outer->centrality.pathCentrality, outer->runNumber); + if (!outer->hCentralityObjects) { + LOGF(info, "No centrality calibration list found for run %i", outer->runNumber); + } + } + + if (!outer->hCentralityObjects) { + hCentrality = nullptr; + return; + } + + hCentrality = dynamic_cast(outer->hCentralityObjects->FindObject(Form("hCalibZeq%s", estName.c_str()))); + if (!hCentrality) { + LOGF(debug, "Calibration missing for %s", estName.c_str()); + } else { + LOGF(debug, "Calibration loaded for %s", estName.c_str()); + } + } + }; + + std::vector estimators; + Estimator initEstimator(const bool doStudy, const std::string& estName, const std::string& histName) { - if (!col.alias_bit(kINT7)) { - return; + return Estimator(this, doStudy, estName, histName); + } + + template + void insertHist(const std::string& name, const std::string& title, HistType type, const std::vector& axes) + { + histPointers[name] = histos.add(name.c_str(), title.c_str(), type, axes); + } + + template + std::shared_ptr& getHist(const std::string& name) + { + return std::get>(histPointers[name]); + } + + PresliceUnsorted> perMcCollision = aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted> perMcCollisionNGlobal = aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted> perMcCollisionMFT = aod::mccollisionlabel::mcCollisionId; + + void init(o2::framework::InitContext& /*initContext*/) + { + ccdb->setURL(centrality.ccdbURL); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + estimators.resize(NEstimators); + estimators[FV0A] = initEstimator(studies.fv0a.value, "FV0", "FV0A"); + estimators[FT0M] = initEstimator(studies.ft0m.value, "FT0", "FT0M"); + estimators[FT0MAnchorCol] = initEstimator(studies.ft0mAnchorCol.value, "FT0MAnchorCol", "FT0MAnchorCol"); + estimators[FT0MAnchorBC] = initEstimator(studies.ft0mAnchorBc.value, "FT0MAnchorBc", "FT0MAnchorBc"); + estimators[FT0MOuterA] = initEstimator(studies.ft0mOuterA.value, "FT0MOuterA", "FT0MOuterA"); + estimators[FT0A] = initEstimator(studies.ft0a.value, "FT0A", "FT0A"); + estimators[FT0C] = initEstimator(studies.ft0c.value, "FT0C", "FT0C"); + estimators[FT0CVar1] = initEstimator(studies.ft0cVar1.value, "FT0CVariant1", "FT0CVariant1"); + estimators[FT0CVar2] = initEstimator(studies.ft0cVar2.value, "FT0CVariant2", "FT0CVariant2"); + estimators[FDDM] = initEstimator(studies.fddm.value, "FDDM", "FDDM"); + estimators[NTPV] = initEstimator(studies.ntpv.value, "NTracksPV", "NTPV"); + estimators[NGlobal] = initEstimator(studies.nGlo.value, "NGlobal", "NGlobal"); + estimators[MFT] = initEstimator(studies.mft.value, "MFT", "MFT"); + + if (doprocessRun2) { + insertHist("hCentRun2V0M", ";V0M centrality (%)", kTH1D, {axisCentrality}); + insertHist("hCentRun2V0A", ";V0A centrality (%)", kTH1D, {axisCentrality}); + insertHist("hCentRun2SPDTks", ";SPD tracklet centrality (%)", kTH1D, {axisCentrality}); + insertHist("hCentRun2SPDCls", ";SPD cluster centrality (%)", kTH1D, {axisCentrality}); + insertHist("hCentRun2CL0", ";CL0 centrality (%)", kTH1D, {axisCentrality}); + insertHist("hCentRun2CL1", ";CL1 centrality (%)", kTH1D, {axisCentrality}); } - if (!col.sel7()) { - return; + + if (doprocessRun3 || doprocessRun3MonteCarlo) { + for (int iEst = 0; iEst < NEstimators; ++iEst) { + const Estimator& est = estimators[iEst]; + if (!est.doStudy) { + continue; + } + insertHist("hCent" + est.histName, ";" + est.histName + " centrality (%)", kTH1D, {axisCentrality}); + insertHist("hCentProfile" + est.histName, ";" + est.histName + " centrality (%)", kTProfile, {axisCentrality}); + insertHist("hMultEta05VsCent" + est.histName, ";" + est.histName + " Multiplicity PV contributors (|#it{#eta}| < 0.5)", kTH2D, {axisCentrality, axisMultiplicityPV}); + if (doprocessRun3MonteCarlo) { + insertHist("hMultEta05VsGenMult" + est.histName, ";Multiplicity " + est.histName + ";Multiplicity PV contributors (|#it{#eta}| < 0.5)", kTH2D, {axisMultiplicity, axisMultiplicityPV}); + insertHist("hGenMultEta05VsCentrality" + est.histName, ";" + est.histName + " Centrality (%); Generated multiplicity (|#it{#eta}| < 0.5)", kTH2D, {axisCentrality, axisMultiplicityPV}); + insertHist("hGenMultVsCentrality" + est.histName, ";" + est.histName + " Centrality (%); Generated multiplicity " + est.estName, kTH2D, {axisCentrality, axisMultiplicityPV}); + } + } } - if (INELgtZERO && col.multNTracksPVeta1() < 1) { - return; + + if (doprocessBunchCrossings) { + insertHist("hBCSelection", "hBCSelection", kTH1D, {{20, -0.5, 19.5f}}); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(1, "All BCs"); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(2, "Colliding BCs"); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(3, "TVX"); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(4, "FV0OrA"); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(5, "FT0PosZ"); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(6, "BB with FT0"); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(7, "zdc rej"); + getHist("hBCSelection")->GetXaxis()->SetBinLabel(8, "isFlangeEvent"); + insertHist("hAmpVsChFT0A", "hAmpVsChFT0A;Channel; Amplitude", kTH2D, {{96, -0.5, 95.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFT0C", "hAmpVsChFT0C;Channel; Amplitude", kTH2D, {{112, -0.5, 111.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFV0A", "hAmpVsChFV0A;Channel; Amplitude", kTH2D, {{48, -0.5, 47.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFDDA", "hAmpVsChFDDA;Channel; Amplitude", kTH2D, {{8, -0.5, 7.5}, axisChannelAmplitude}); + insertHist("hAmpVsChFDDC", "hAmpVsChFDDC;Channel; Amplitude", kTH2D, {{8, -0.5, 7.5}, axisChannelAmplitude}); } - LOGF(debug, "centV0M=%.0f", col.centRun2V0M()); - LOGF(debug, "centSPDTracklets=%.0f", col.centRun2SPDTracklets()); - LOGF(debug, "centSPDClusters=%.0f", col.centRun2SPDClusters()); - // fill centrality histos - hCentRun2V0M->Fill(col.centRun2V0M()); - hCentRun2SPDTks->Fill(col.centRun2SPDTracklets()); - hCentRun2SPDCls->Fill(col.centRun2SPDClusters()); + + histos.print(); } - PROCESS_SWITCH(CentralityQa, processRun2PP, "Process with Run2 SPD clusters centrality/multiplicity estimation", false); - void processRun2PbPb(soa::Join::iterator const& col) + template + bool isCollisionAccepted(TCollision const& collision) + // check whether the collision passes our collision selections { - if (!col.alias_bit(kINT7)) { - return; + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; } - if (!col.sel7()) { - return; + + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; } - if (INELgtZERO && col.multNTracksPVeta1() < 1) { - return; + + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; } - LOGF(debug, "centV0M=%.0f", col.centRun2V0M()); - LOGF(debug, "centSPDTracklets=%.0f", col.centRun2SPDTracklets()); - LOGF(debug, "centCL0=%.0f", col.centRun2CL0()); - LOGF(debug, "centCL1=%.0f", col.centRun2CL1()); - // fill centrality histos - hCentRun2V0M->Fill(col.centRun2V0M()); - hCentRun2SPDTks->Fill(col.centRun2SPDTracklets()); - hCentRun2CL0->Fill(col.centRun2CL0()); - hCentRun2CL1->Fill(col.centRun2CL1()); + + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + + if (eventSelections.requireIsBBT0A && !collision.selection_bit(aod::evsel::kIsBBT0A)) { + return false; + } + + if (eventSelections.requireIsBBT0C && !collision.selection_bit(aod::evsel::kIsBBT0C)) { + return false; + } + + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return false; + } + + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + + static constexpr int OneTrackInEta1 = 1; + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < OneTrackInEta1) { + return false; + } + + static constexpr int TwoTracksInEta1 = 2; + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < TwoTracksInEta1) { + return false; + } + + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + + if constexpr (requires { collision.has_mcCollision(); }) { // check if we are in MC + if (!collision.has_mcCollision()) { + return false; + } + + const auto& mcCollision = collision.template mcCollision_as>(); + const auto& recoBC = collision.template bc_as(); + const auto& foundBC = collision.template foundBC_as(); + const auto& mcBC = mcCollision.template bc_as(); + + // Check that the BC in data and MC is the same + if (eventSelections.rejectMismatchedBCs && recoBC.globalBC() != mcBC.globalBC()) { + return false; + } + if (eventSelections.rejectMismatchedFoundBCs && foundBC.globalBC() != mcBC.globalBC()) { + return false; + } + } + + return true; } - PROCESS_SWITCH(CentralityQa, processRun2PbPb, "Process with Run2 CL0 and CL1 multiplicities centrality/multiplicity estimation", false); - void processRun2PPb(soa::Join::iterator const& col) + template + bool isCollisionAcceptedRun2(TCollision const& collision) + // check whether the collision passes our collision selections { - if (!col.alias_bit(kINT7)) { - return; + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; } - if (!col.sel7()) { - return; + + if (eventSelections.requireSel7 && !collision.sel7()) { + return false; } - if (INELgtZERO && col.multNTracksPVeta1() < 1) { - return; + + if (eventSelections.requireINT7 && !collision.alias_bit(kINT7)) { + return false; } - LOGF(debug, "centV0A=%.0f", col.centRun2V0A()); - // fill centrality histos - hCentRun2V0A->Fill(col.centRun2V0A()); + + if (eventSelections.requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + + if (eventSelections.rejectIncompleteDAQ && !collision.selection_bit(o2::aod::evsel::kNoIncompleteDAQ)) { + return false; + } + + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + + if (eventSelections.requireConsistentSPDAndTrackVtx && !collision.selection_bit(o2::aod::evsel::kNoInconsistentVtx)) { + return false; + } + + if (eventSelections.rejectPileupFromSPD && !collision.selection_bit(o2::aod::evsel::kNoPileupFromSPD)) { + return false; + } + + if (eventSelections.rejectV0PFPileup && !collision.selection_bit(o2::aod::evsel::kNoV0PFPileup)) { + return false; + } + + if (eventSelections.rejectPileupInMultBins && !collision.selection_bit(o2::aod::evsel::kNoPileupInMultBins)) { + return false; + } + + if (eventSelections.rejectPileupMV && !collision.selection_bit(o2::aod::evsel::kNoPileupMV)) { + return false; + } + + if (eventSelections.rejectTPCPileup && !collision.selection_bit(o2::aod::evsel::kNoPileupTPC)) { + return false; + } + + if (eventSelections.requireNoV0MOnVsOffPileup && !collision.selection_bit(o2::aod::evsel::kNoV0MOnVsOfPileup)) { + return false; + } + + if (eventSelections.requireNoSPDOnVsOffPileup && !collision.selection_bit(o2::aod::evsel::kNoSPDOnVsOfPileup)) { + return false; + } + + if (eventSelections.requireNoSPDClsVsTklBG && !collision.selection_bit(o2::aod::evsel::kNoSPDClsVsTklBG)) { + return false; + } + + static constexpr int OneTrackInEta1 = 1; + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < OneTrackInEta1) { + return false; + } + + static constexpr int TwoTracksInEta1 = 2; + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < TwoTracksInEta1) { + return false; + } + + return true; } - PROCESS_SWITCH(CentralityQa, processRun2PPb, "Process with Run2 V0A multiplicitY centrality/multiplicity estimation", false); - void processRun3_FV0A(soa::Join::iterator const& col) + template + bool isBunchCrossingAccepted(const TBunchCrossing& bc, bool fillHistograms = false) { - if (INELgtZERO && col.multNTracksPVeta1() < 1) { - return; + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 0); // all BCs } - if (!col.sel8()) - return; - LOGF(debug, "centFV0A=%.0f", col.centFV0A()); - hCentFV0A->Fill(col.centFV0A()); - hCentProfileFV0A->Fill(col.centFV0A(), col.multNTracksPVetaHalf()); + + if (bc.runNumber() != runNumber) { + runNumber = bc.runNumber(); + auto grpo = ccdb->getForRun("GLO/Config/GRPECS", runNumber); + startOfRunTimestamp = grpo->getTimeStart(); + auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", startOfRunTimestamp); + collidingBunch = grplhcif->getBunchFilling().getBCPattern(); + } + + const int localBC = bc.globalBC() % o2::constants::lhc::LHCMaxBunches; + const bool collidingBC = collidingBunch.test(localBC); + + if (bcsel.selectCollidingBCs && !collidingBC) { + return false; + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 1); // colliding + } + + if (bcsel.selectTVX && !bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 2); // TVX + } + + bool isFV0OrA = false; + if (bc.has_fv0a()) { + const auto& fv0 = bc.fv0a(); + std::bitset<8> fv0TriggerMask = fv0.triggerMask(); + isFV0OrA = fv0TriggerMask[o2::fit::Triggers::bitA]; + } + + if (bcsel.selectFV0OrA && !isFV0OrA) { + return false; + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 3); // FV0OrA + } + + const float largeVertexZ = 100.0f; + if (bcsel.selectVertexZwithT0 && bcsel.vertexZwithT0 < largeVertexZ) { + if (bc.has_ft0()) { + const auto& ft0 = bc.ft0(); + if (!ft0.isValidTime()) { + return false; + } + if (std::abs(ft0.posZ()) > bcsel.vertexZwithT0) { + return false; + } + } + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 4); // FT0PosZ + } + + if (bcsel.selectBBT0 && !bc.selection_bit(o2::aod::evsel::kIsBBT0A) && !bc.selection_bit(o2::aod::evsel::kIsBBT0C)) { + return false; + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 5); // t0ac time + } + + if (bcsel.rejectZNAC && !bc.selection_bit(o2::aod::evsel::kIsBBZNA) && !bc.selection_bit(o2::aod::evsel::kIsBBZNC)) { + return false; + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 6); // znac time + } + + if (bcsel.rejectIsFlangeEvent) { + if (bc.has_ft0()) { + const auto& ft0 = bc.ft0(); + constexpr int IsFlangeEventId = 7; + std::bitset<8> ft0TriggerMask = ft0.triggerMask(); + if (ft0TriggerMask[IsFlangeEventId]) { + return false; + } + } + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 7); // isFlangeEvent + } + + return true; } - PROCESS_SWITCH(CentralityQa, processRun3_FV0A, "Process with Run 3 FV0A estimator", false); - void processRun3_FT0M(soa::Join::iterator const& col) + template + void fillEstimatorHistos(const TCollision& col, Estimator est, const float mult, const float refCent) { - if (INELgtZERO && col.multNTracksPVeta1() < 1) { + if (!est.doStudy) { return; } - if (!col.sel8()) - return; - LOGF(debug, "centFT0M=%.0f", col.centFT0M()); - hCentFT0M->Fill(col.centFT0M()); - hCentProfileFT0M->Fill(col.centFT0M(), col.multNTracksPVetaHalf()); + + est.configure(col); + const auto cent = est.getCentrality(mult, refCent); + LOGF(debug, "cent%s=%.0f", est.histName.c_str(), cent); + getHist("hCent" + est.histName)->Fill(cent); + getHist("hMultEta05VsCent" + est.histName)->Fill(cent, col.multNTracksPVetaHalf()); + getHist("hCentProfile" + est.histName)->Fill(cent, col.multNTracksPVetaHalf()); } - PROCESS_SWITCH(CentralityQa, processRun3_FT0M, "Process with Run 3 FT0M estimator", false); - void processRun3_FT0A(soa::Join::iterator const& col) + template + void fillEstimatorMonteCarloHistos(const TMcCollision& mcCol, const Estimator& est, const float multMC, const float cent, const float nPVContribsInEta05) { - if (INELgtZERO && col.multNTracksPVeta1() < 1) { + if (!est.doStudy) { return; } - if (!col.sel8()) - return; - hCentFT0A->Fill(col.centFT0A()); - hCentProfileFT0A->Fill(col.centFT0A(), col.multNTracksPVetaHalf()); + + getHist("hMultEta05VsGenMult" + est.histName)->Fill(multMC, nPVContribsInEta05); + getHist("hGenMultEta05VsCentrality" + est.histName)->Fill(cent, mcCol.multMCNParticlesEta05()); + getHist("hGenMultVsCentrality" + est.histName)->Fill(cent, multMC); } - PROCESS_SWITCH(CentralityQa, processRun3_FT0A, "Process with Run 3 FT0A estimator", false); - void processRun3_FT0C(soa::Join::iterator const& col) + void processRun2(soa::Join::iterator const& col) { - if (INELgtZERO && col.multNTracksPVeta1() < 1) { + if (!isCollisionAcceptedRun2(col)) { return; } - if (!col.sel8()) - return; - hCentFT0C->Fill(col.centFT0C()); - hCentProfileFT0C->Fill(col.centFT0C(), col.multNTracksPVetaHalf()); + LOGF(debug, "centV0M=%.0f", col.centRun2V0M()); + LOGF(debug, "centSPDTracklets=%.0f", col.centRun2SPDTracklets()); + LOGF(debug, "centSPDClusters=%.0f", col.centRun2SPDClusters()); + LOGF(debug, "centCL0=%.0f", col.centRun2CL0()); + LOGF(debug, "centCL1=%.0f", col.centRun2CL1()); + LOGF(debug, "centV0A=%.0f", col.centRun2V0A()); + + getHist("hCentRun2V0M")->Fill(col.centRun2V0M()); + getHist("hCentRun2SPDTks")->Fill(col.centRun2SPDTracklets()); + getHist("hCentRun2SPDCls")->Fill(col.centRun2SPDClusters()); + getHist("hCentRun2CL0")->Fill(col.centRun2CL0()); + getHist("hCentRun2CL1")->Fill(col.centRun2CL1()); + getHist("hCentRun2V0A")->Fill(col.centRun2V0A()); } - PROCESS_SWITCH(CentralityQa, processRun3_FT0C, "Process with Run 3 FT0A estimator", false); - void processRun3_FDDM(soa::Join::iterator const& col) + void processRun3(soa::Join::iterator const& col, + soa::Join const& nGlobals, + soa::Join const& nMFTs, + BCsWithMatching const&) { - if (INELgtZERO && col.multNTracksPVeta1() < 1) { + if (!isCollisionAccepted(col)) { return; } - if (!col.sel8()) - return; - hCentFDDM->Fill(col.centFDDM()); - hCentProfileFDDM->Fill(col.centFDDM(), col.multNTracksPVetaHalf()); + + fillEstimatorHistos(col, estimators[FV0A], col.multFV0A(), col.centFV0A()); + fillEstimatorHistos(col, estimators[FT0M], col.multFT0M(), col.centFT0M()); + fillEstimatorHistos(col, estimators[FT0MAnchorCol], col.multFT0M(), col.centFT0MAnchorCol()); + fillEstimatorHistos(col, estimators[FT0MAnchorBC], col.multFT0M(), col.centFT0MAnchorBC()); + fillEstimatorHistos(col, estimators[FT0MOuterA], col.multFT0AOuter() + col.multFT0C(), col.centFT0MOuterA()); + fillEstimatorHistos(col, estimators[FT0A], col.multFT0A(), col.centFT0A()); + fillEstimatorHistos(col, estimators[FT0C], col.multFT0C(), col.centFT0C()); + fillEstimatorHistos(col, estimators[FT0CVar1], col.multFT0C(), col.centFT0CVariant1()); + fillEstimatorHistos(col, estimators[FT0CVar2], col.multFT0C(), col.centFT0CVariant2()); + fillEstimatorHistos(col, estimators[FDDM], col.multFDDM(), col.centFDDM()); + fillEstimatorHistos(col, estimators[NTPV], col.multNTracksPV(), col.centNTPV()); + + if (nGlobals.size() > 0) { + const auto& multCentNGlo = nGlobals.rawIteratorAt(col.globalIndex()); + fillEstimatorHistos(col, estimators[NGlobal], multCentNGlo.multNTracksGlobal(), multCentNGlo.centNGlobal()); + } + + if (nMFTs.size() > 0) { + const auto& multCentNMFT = nMFTs.rawIteratorAt(col.globalIndex()); + fillEstimatorHistos(col, estimators[MFT], multCentNMFT.mftNtracks(), multCentNMFT.centMFT()); + } } - PROCESS_SWITCH(CentralityQa, processRun3_FDDM, "Process with Run 3 FDDM estimator", false); - void processRun3_NTPV(soa::Join::iterator const& col) + void processRun3MonteCarlo(soa::Join const& collisions, + soa::Join const& nGlobals, + soa::Join const& nMFTs, + soa::Join const& mcCollisions, + BCsWithMatching const& /*bcs*/) { - if (INELgtZERO && col.multNTracksPVeta1() < 1) { - return; + for (const auto& col : collisions) { + if (!isCollisionAccepted(col)) { + continue; + } + + fillEstimatorHistos(col, estimators[FV0A], col.multFV0A(), col.centFV0A()); + fillEstimatorHistos(col, estimators[FT0M], col.multFT0M(), col.centFT0M()); + fillEstimatorHistos(col, estimators[FT0MAnchorCol], col.multFT0M(), col.centFT0MAnchorCol()); + fillEstimatorHistos(col, estimators[FT0MAnchorBC], col.multFT0M(), col.centFT0MAnchorBC()); + fillEstimatorHistos(col, estimators[FT0MOuterA], col.multFT0AOuter() + col.multFT0C(), col.centFT0MOuterA()); + fillEstimatorHistos(col, estimators[FT0A], col.multFT0A(), col.centFT0A()); + fillEstimatorHistos(col, estimators[FT0C], col.multFT0C(), col.centFT0C()); + fillEstimatorHistos(col, estimators[FT0CVar1], col.multFT0C(), col.centFT0CVariant1()); + fillEstimatorHistos(col, estimators[FT0CVar2], col.multFT0C(), col.centFT0CVariant2()); + fillEstimatorHistos(col, estimators[FDDM], col.multFDDM(), col.centFDDM()); + fillEstimatorHistos(col, estimators[NTPV], col.multNTracksPV(), col.centNTPV()); + + if (nGlobals.size() > 0) { + const auto& multCentNGlo = nGlobals.rawIteratorAt(col.globalIndex()); + fillEstimatorHistos(col, estimators[NGlobal], multCentNGlo.multNTracksGlobal(), multCentNGlo.centNGlobal()); + } + + if (nMFTs.size() > 0) { + const auto& multCentNMFT = nMFTs.rawIteratorAt(col.globalIndex()); + fillEstimatorHistos(col, estimators[MFT], multCentNMFT.mftNtracks(), multCentNMFT.centMFT()); + } + + if (!loopOverMcCollisionsForMcHist) { + const auto& mcCol = col.mcCollision_as>(); + std::vector cent; + cent.resize(NEstimators, CentralityNotFound); + + cent[FV0A] = estimators[FV0A].getCentrality(col.multFV0A(), col.centFV0A()); + cent[FT0M] = estimators[FT0M].getCentrality(col.multFT0M(), col.centFT0M()); + cent[FT0MAnchorCol] = estimators[FT0MAnchorCol].getCentrality(col.multFT0M(), col.centFT0MAnchorCol()); + cent[FT0MAnchorBC] = estimators[FT0MAnchorBC].getCentrality(col.multFT0M(), col.centFT0MAnchorBC()); + cent[FT0MOuterA] = estimators[FT0MOuterA].getCentrality(col.multFT0AOuter() + col.multFT0C(), col.centFT0MOuterA()); + cent[FT0A] = estimators[FT0A].getCentrality(col.multFT0A(), col.centFT0A()); + cent[FT0C] = estimators[FT0C].getCentrality(col.multFT0C(), col.centFT0C()); + cent[FT0CVar1] = estimators[FT0CVar1].getCentrality(col.multFT0C(), col.centFT0CVariant1()); + cent[FT0CVar2] = estimators[FT0CVar2].getCentrality(col.multFT0C(), col.centFT0CVariant2()); + cent[FDDM] = estimators[FDDM].getCentrality(col.multFDDM(), col.centFDDM()); + cent[NTPV] = estimators[NTPV].getCentrality(col.multNTracksPV(), col.centNTPV()); + + fillEstimatorMonteCarloHistos(mcCol, estimators[FV0A], mcCol.multMCFV0A(), cent[FV0A], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0M], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0M], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0MAnchorCol], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0MAnchorCol], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0MAnchorBC], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0MAnchorBC], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0MOuterA], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0MOuterA], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0A], mcCol.multMCFT0A(), cent[FT0A], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0C], mcCol.multMCFT0C(), cent[FT0C], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0CVar1], mcCol.multMCFT0C(), cent[FT0CVar1], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0CVar2], mcCol.multMCFT0C(), cent[FT0CVar2], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[FDDM], mcCol.multMCFDDA() + mcCol.multMCFDDC(), cent[FDDM], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[NTPV], mcCol.multMCNParticlesEta08(), cent[NTPV], col.multNTracksPVetaHalf()); + fillEstimatorMonteCarloHistos(mcCol, estimators[NGlobal], mcCol.multMCNParticlesEta08(), cent[NGlobal], col.multNTracksPVetaHalf()); + + if (nGlobals.size() > 0) { + const auto& multCentNGlo = nGlobals.rawIteratorAt(col.globalIndex()); + cent[NGlobal] = estimators[NGlobal].getCentrality(multCentNGlo.multNTracksGlobal(), multCentNGlo.centNGlobal()); + fillEstimatorMonteCarloHistos(mcCol, estimators[NGlobal], mcCol.multMCNParticlesEta08(), cent[NGlobal], col.multNTracksPVetaHalf()); + } + + if (nMFTs.size() > 0) { + const auto& multCentNMFT = nMFTs.rawIteratorAt(col.globalIndex()); + cent[MFT] = estimators[MFT].getCentrality(multCentNMFT.mftNtracks(), multCentNMFT.centMFT()); + // fillEstimatorMonteCarloHistos(mcCol, estimators[MFT], mcCol.multMCMFT(), cent[MFT], col.multNTracksPVetaHalf()); // FIXME: uncomment when MC MFT mult is added in aod::MultMCExtras + } + } + } + + if (loopOverMcCollisionsForMcHist) { + for (const auto& mcCol : mcCollisions) { + auto groupedCollisions = collisions.sliceBy(perMcCollision, mcCol.globalIndex()); + auto groupedCollisionsNGlobal = collisions.sliceBy(perMcCollisionNGlobal, mcCol.globalIndex()); + auto groupedCollisionsMFT = collisions.sliceBy(perMcCollisionMFT, mcCol.globalIndex()); + + // Check if there is at least one of the reconstructed collisions associated to this MC collision + // If so, we consider it + int biggestNContribs = -1; + int nContribsInEta05 = -1; + std::vector cent; + cent.resize(NEstimators, CentralityNotFound); + + for (auto const& col : groupedCollisions) { + if (!isCollisionAccepted(col)) { + continue; + } + + for (int iEst = 0; iEst < NEstimators; ++iEst) { + if (iEst == NGlobal || iEst == MFT) { + continue; + } + estimators[iEst].configure(col); + } + + const float recoColCentFV0A = estimators[FV0A].getCentrality(col.multFV0A(), col.centFV0A()); + const float recoColCentFT0M = estimators[FT0M].getCentrality(col.multFT0M(), col.centFT0M()); + const float recoColCentFT0MAnchorCol = estimators[FT0MAnchorCol].getCentrality(col.multFT0M(), col.centFT0MAnchorCol()); + const float recoColCentFT0MAnchorBC = estimators[FT0MAnchorBC].getCentrality(col.multFT0M(), col.centFT0MAnchorBC()); + const float recoColCentFT0MOuterA = estimators[FT0MOuterA].getCentrality(col.multFT0AOuter() + col.multFT0C(), col.centFT0MOuterA()); + const float recoColCentFT0A = estimators[FT0A].getCentrality(col.multFT0A(), col.centFT0A()); + const float recoColCentFT0C = estimators[FT0C].getCentrality(col.multFT0C(), col.centFT0C()); + const float recoColCentFT0CVar1 = estimators[FT0CVar1].getCentrality(col.multFT0C(), col.centFT0CVariant1()); + const float recoColCentFT0CVar2 = estimators[FT0CVar2].getCentrality(col.multFT0C(), col.centFT0CVariant2()); + const float recoColCentFDDM = estimators[FDDM].getCentrality(col.multFDDM(), col.centFDDM()); + const float recoColCentNTPV = estimators[NTPV].getCentrality(col.multNTracksPV(), col.centNTPV()); + + // One McCollision can be reconstructed multiple times + // Use centrality from reconstructed collision with most PV contributors + if (biggestNContribs < col.multPVTotalContributors()) { + biggestNContribs = col.multPVTotalContributors(); + nContribsInEta05 = col.multNTracksPVetaHalf(); + cent[FV0A] = recoColCentFV0A; + cent[FT0M] = recoColCentFT0M; + cent[FT0MAnchorCol] = recoColCentFT0MAnchorCol; + cent[FT0MAnchorBC] = recoColCentFT0MAnchorBC; + cent[FT0MOuterA] = recoColCentFT0MOuterA; + cent[FT0A] = recoColCentFT0A; + cent[FT0C] = recoColCentFT0C; + cent[FT0CVar1] = recoColCentFT0CVar1; + cent[FT0CVar2] = recoColCentFT0CVar2; + cent[FDDM] = recoColCentFDDM; + cent[NTPV] = recoColCentNTPV; + } + } + + if (nGlobals.size() > 0 && studies.nGlo.value) { + for (auto const& col : groupedCollisionsNGlobal) { + estimators[NGlobal].configure(col); + const auto& multCentNGlo = nGlobals.rawIteratorAt(col.globalIndex()); + const float recoColCentNGlo = estimators[NGlobal].getCentrality(multCentNGlo.multNTracksGlobal(), multCentNGlo.centNGlobal()); + if (biggestNContribs < col.multPVTotalContributors()) { + biggestNContribs = col.multPVTotalContributors(); + nContribsInEta05 = col.multNTracksPVetaHalf(); + cent[NGlobal] = recoColCentNGlo; + } + } + } + + if (nMFTs.size() > 0 && studies.mft.value) { + for (auto const& col : groupedCollisionsMFT) { + estimators[MFT].configure(col); + const auto& multCentNMFT = nMFTs.rawIteratorAt(col.globalIndex()); + const float recoColCentNMFT = estimators[MFT].getCentrality(multCentNMFT.mftNtracks(), multCentNMFT.centMFT()); + if (biggestNContribs < col.multPVTotalContributors()) { + biggestNContribs = col.multPVTotalContributors(); + nContribsInEta05 = col.multNTracksPVetaHalf(); + cent[MFT] = recoColCentNMFT; + } + } + } + + fillEstimatorMonteCarloHistos(mcCol, estimators[FV0A], mcCol.multMCFV0A(), cent[FV0A], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0M], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0M], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0MAnchorCol], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0MAnchorCol], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0MAnchorBC], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0MAnchorBC], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0MOuterA], mcCol.multMCFT0A() + mcCol.multMCFT0C(), cent[FT0MOuterA], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0A], mcCol.multMCFT0A(), cent[FT0A], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0C], mcCol.multMCFT0C(), cent[FT0C], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0CVar1], mcCol.multMCFT0C(), cent[FT0CVar1], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FT0CVar2], mcCol.multMCFT0C(), cent[FT0CVar2], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[FDDM], mcCol.multMCFDDA() + mcCol.multMCFDDC(), cent[FDDM], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[NTPV], mcCol.multMCNParticlesEta08(), cent[NTPV], nContribsInEta05); + fillEstimatorMonteCarloHistos(mcCol, estimators[NGlobal], mcCol.multMCNParticlesEta08(), cent[NGlobal], nContribsInEta05); + // fillEstimatorMonteCarloHistos(mcCol, estimators[MFT], mcCol.multMCMFT(), cent[MFT], nContribsInEta05); // FIXME: uncomment when MC MFT mult is added in aod::MultMCExtras + } } - if (!col.sel8()) + } + + using BCsWithRun3Matchings = soa::Join; + void processBunchCrossings(soa::Join::iterator const& bc, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&) + { + if (!isBunchCrossingAccepted(bc, true)) { return; - hCentNTPV->Fill(col.centNTPV()); - hCentProfileNTPV->Fill(col.centNTPV(), col.multNTracksPVetaHalf()); + } + + if (bc.has_ft0()) { + const auto& ft0 = bc.ft0(); + for (size_t ii{0}; ii < ft0.channelA().size(); ++ii) { + histos.fill(HIST("hAmpVsChFT0A"), ft0.channelA()[ii], ft0.amplitudeA()[ii]); + } + + for (size_t ii{0}; ii < ft0.channelC().size(); ++ii) { + histos.fill(HIST("hAmpVsChFT0C"), ft0.channelC()[ii], ft0.amplitudeC()[ii]); + } + } + + if (bc.has_fv0a()) { + const auto& fv0a = bc.fv0a(); + for (size_t ii{0}; ii < fv0a.channel().size(); ++ii) { + histos.fill(HIST("hAmpVsChFV0A"), fv0a.channel()[ii], fv0a.amplitude()[ii]); + } + } + + if (bc.has_fdd()) { + const auto& fdd = bc.fdd(); + for (size_t ii{0}; ii < std::size(fdd.chargeA()); ++ii) { + histos.fill(HIST("hAmpVsChFDDA"), ii, fdd.chargeA()[ii]); + } + + for (size_t ii{0}; ii < std::size(fdd.chargeC()); ++ii) { + histos.fill(HIST("hAmpVsChFDDC"), ii, fdd.chargeC()[ii]); + } + } } - PROCESS_SWITCH(CentralityQa, processRun3_NTPV, "Process with Run 3 NTPV estimator", false); + + PROCESS_SWITCH(CentralityQa, processRun2, "Process with Run 2 V0A", false); + PROCESS_SWITCH(CentralityQa, processRun3, "Process with Run 3", true); + PROCESS_SWITCH(CentralityQa, processRun3MonteCarlo, "Process with Run 3 with MC", false); + PROCESS_SWITCH(CentralityQa, processBunchCrossings, "Process with Run 3 BC table", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/Common/Tasks/centralityStudy.cxx b/Common/Tasks/centralityStudy.cxx index 20ab15ba873..7168d78b5cc 100644 --- a/Common/Tasks/centralityStudy.cxx +++ b/Common/Tasks/centralityStudy.cxx @@ -8,10 +8,13 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -// This task does dedicated centrality studies for understanding the -// Run 3 Pb-Pb centrality selections in 2023 data. It is compatible with -// derived data. + +/// +/// \file centralityStudy.cxx +/// \brief This task does dedicated centrality studies for understanding the Run 3 Pb-Pb +/// centrality selections in 2023 data. It is compatible with derived data +/// \author David Dobrigkeit Chinellato and Jesper Karlsson Gumprecht +/// #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" @@ -35,6 +38,7 @@ #include #include +#include #include #include #include @@ -45,94 +49,147 @@ using namespace o2; using namespace o2::framework; -using BCsWithRun3Matchings = soa::Join; -#define getHist(type, name) std::get>(histPointers[name]) - -struct centralityStudy { +struct CentralityStudy { // Raw multiplicities HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; std::map histPointers; std::string histPath; - Service ccdb; + Service ccdb{}; ctpRateFetcher mRateFetcher; - int mRunNumber; - uint64_t startOfRunTimestamp; + int mRunNumber{}; + uint64_t startOfRunTimestamp{}; // vertex Z equalization - TList* hCalibObjects; - TProfile* hVtxZFV0A; - TProfile* hVtxZFT0A; - TProfile* hVtxZFT0C; - TProfile* hVtxZNTracks; - TProfile* hVtxZNGlobals; - TProfile* hVtxZMFT; - TProfile* hVtxZFDDA; - TProfile* hVtxZFDDC; + TProfile* hVtxZFV0A = nullptr; + TProfile* hVtxZFT0A = nullptr; + TProfile* hVtxZFT0C = nullptr; + TProfile* hVtxZNTracks = nullptr; + TProfile* hVtxZNGlobals = nullptr; + TProfile* hVtxZMFT = nullptr; + TProfile* hVtxZFDDA = nullptr; + TProfile* hVtxZFDDC = nullptr; + + // calibration histograms + TH1* hCentralityFV0A = nullptr; + TH1* hCentralityFT0A = nullptr; + TH1* hCentralityFT0C = nullptr; + TH1* hCentralityFT0M = nullptr; + TH1* hCentralityFDDM = nullptr; + TH1* hCentralityNTPV = nullptr; + TH1* hCentralityNGlo = nullptr; + TH1* hCentralityMFT = nullptr; + + float centFV0A = 105.f; + float centFT0A = 105.f; + float centFT0C = 105.f; + float centFT0M = 105.f; + float centFDDM = 105.f; + float centNTPV = 105.f; + float centNGlo = 105.f; + float centMFT = 105.f; // Configurables Configurable applyVertexZEqualization{"applyVertexZEqualization", false, "0 - no, 1 - yes"}; + Configurable minTimeDelta{"minTimeDelta", -1.0f, "reject collision if another collision is this close or less in time"}; - Configurable do2DPlots{"do2DPlots", true, "0 - no, 1 - yes"}; - Configurable doOccupancyStudyVsCentrality2d{"doOccupancyStudyVsCentrality2d", true, "0 - no, 1 - yes"}; - Configurable doOccupancyStudyVsRawValues2d{"doOccupancyStudyVsRawValues2d", true, "0 - no, 1 - yes"}; - Configurable doOccupancyStudyVsCentrality3d{"doOccupancyStudyVsCentrality3d", false, "0 - no, 1 - yes"}; - Configurable doOccupancyStudyVsRawValues3d{"doOccupancyStudyVsRawValues3d", false, "0 - no, 1 - yes"}; - Configurable doTimeStudies{"doTimeStudies", true, "0 - no, 1 - yes"}; - Configurable doTimeStudyFV0AOuterVsFT0A3d{"doTimeStudyFV0AOuterVsFT0A3d", false, "0 - no, 1 - yes"}; - Configurable doNGlobalTracksVsRawSignals{"doNGlobalTracksVsRawSignals", true, "0 - no, 1 - yes"}; - Configurable applySel8{"applySel8", true, "0 - no, 1 - yes"}; - Configurable applyVtxZ{"applyVtxZ", true, "0 - no, 1 - yes"}; - - // Apply extra event selections - Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border"}; - Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border"}; - Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track"}; - Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", false, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; - Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF"}; - Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD"}; - Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; - Configurable rejectIsFlangeEvent{"rejectIsFlangeEvent", false, "At least one channel with -350 TDC < time < -450 TDC"}; - - Configurable rejectITSinROFpileupStandard{"rejectITSinROFpileupStandard", false, "reject collisions in case of in-ROF ITS pileup (standard)"}; - Configurable rejectITSinROFpileupStrict{"rejectITSinROFpileupStrict", false, "reject collisions in case of in-ROF ITS pileup (strict)"}; - Configurable rejectCollInTimeRangeNarrow{"rejectCollInTimeRangeNarrow", false, "reject if extra colls in time range (narrow)"}; - Configurable rejectZNAC{"rejectZNAC", false, "reject if !(kIsBBZNA && kIsBBZNC)"}; - - Configurable selectUPCcollisions{"selectUPCcollisions", false, "select collisions tagged with UPC flag"}; - - Configurable selectCollidingBCs{"selectCollidingBCs", true, "BC analysis: select colliding BCs"}; - Configurable selectTVX{"selectTVX", true, "BC analysis: select TVX"}; - Configurable selectFV0OrA{"selectFV0OrA", true, "BC analysis: select FV0OrA"}; - Configurable vertexZwithT0{"vertexZwithT0", 1000.0f, "require a certain vertex-Z in BC analysis"}; + struct : ConfigurableGroup { + std::string prefix = "ccdbSettings"; + Configurable ccdbURL{"ccdbURL", "http://alice-ccdb.cern.ch", "ccdb url"}; + Configurable pathCentrality{"pathCentrality", "Centrality/Estimators", "path to centrality calibration if fetchCentralityCalibration is enabled"}; + Configurable pathGRPECSObject{"pathGRPECSObject", "GLO/Config/GRPECS", "Path to GRPECS object"}; + Configurable pathVertexZ{"pathVertexZ", "Users/d/ddobrigk/Centrality/Calibration", "Path to vertexZ profiles"}; + Configurable irSource{"irSource", "ZNC hadronic", "Source of the interaction rate: (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + Configurable irCrashOnNull{"irCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; + Configurable fetchCentralityCalibration{"fetchCentralityCalibration", false, "Flag to fetch the centrality calibration within the task instead of the centrality table"}; + } ccdbSettings; - Configurable minTimeDelta{"minTimeDelta", -1.0f, "reject collision if another collision is this close or less in time"}; - Configurable minFT0CforVertexZ{"minFT0CforVertexZ", -1.0f, "minimum FT0C for vertex-Z profile calculation"}; + // _______________________________________ + // Configurable group + struct : ConfigurableGroup { + std::string prefix = "studies"; + Configurable do2DPlots{"do2DPlots", true, "0 - no, 1 - yes"}; + Configurable doRunByRunHistograms{"doRunByRunHistograms", false, "0 - no, 1 - yes"}; + Configurable doOccupancyStudyVsCentrality2d{"doOccupancyStudyVsCentrality2d", true, "0 - no, 1 - yes"}; + Configurable doOccupancyStudyVsRawValues2d{"doOccupancyStudyVsRawValues2d", true, "0 - no, 1 - yes"}; + Configurable doOccupancyStudyVsCentrality3d{"doOccupancyStudyVsCentrality3d", false, "0 - no, 1 - yes"}; + Configurable doOccupancyStudyVsRawValues3d{"doOccupancyStudyVsRawValues3d", false, "0 - no, 1 - yes"}; + Configurable doTimeStudies{"doTimeStudies", false, "0 - no, 1 - yes"}; + Configurable doNGlobalTracksVsRawSignals{"doNGlobalTracksVsRawSignals", true, "0 - no, 1 - yes"}; + Configurable doNTracksPVeta05VsRawSignals{"doNTracksPVeta05VsRawSignals", false, "0 - no, 1 - yes"}; + Configurable doNTracksPVeta08VsRawSignals{"doNTracksPVeta08VsRawSignals", false, "0 - no, 1 - yes"}; + Configurable doNTracksPVeta10VsRawSignals{"doNTracksPVeta10VsRawSignals", false, "0 - no, 1 - yes"}; + } studies; - Configurable scaleSignalFT0A{"scaleSignalFT0A", 1.00f, "scale FT0A signal for convenience"}; - Configurable scaleSignalFT0C{"scaleSignalFT0C", 1.00f, "scale FT0C signal for convenience"}; - Configurable scaleSignalFT0M{"scaleSignalFT0M", 1.00f, "scale FT0M signal for convenience"}; - Configurable scaleSignalFV0A{"scaleSignalFV0A", 1.00f, "scale FV0A signal for convenience"}; + // _______________________________________ + // Event selection + struct : ConfigurableGroup { + std::string prefix = "evsel"; + Configurable applySel8{"applySel8", true, "0 - no, 1 - yes"}; + Configurable applyVtxZ{"applyVtxZ", true, "0 - no, 1 - yes"}; + Configurable requireINELgtZERO{"requireINELgtZERO", true, "0 no, 1 - yes"}; + Configurable selectUPCcollisions{"selectUPCcollisions", false, "select collisions tagged with UPC flag"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", false, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable rejectIsFlangeEvent{"rejectIsFlangeEvent", false, "At least one channel with -350 TDC < time < -450 TDC"}; + Configurable rejectITSinROFpileupStandard{"rejectITSinROFpileupStandard", false, "reject collisions in case of in-ROF ITS pileup (standard)"}; + Configurable rejectITSinROFpileupStrict{"rejectITSinROFpileupStrict", false, "reject collisions in case of in-ROF ITS pileup (strict)"}; + Configurable rejectUpc{"rejectUpc", false, "Reject upc events based on forward signals. Configurable group: cfgFwd"}; + Configurable rejectCollInTimeRangeNarrow{"rejectCollInTimeRangeNarrow", false, "reject if extra colls in time range (narrow)"}; + Configurable maxVtxZ{"maxVtxZ", 10.0f, "max vertex z distance from ip"}; + Configurable applyBcSel{"applyBcSel", false, "For each collision; de-reference the bc and apply the bc selections"}; + Configurable rejectNoFoundBC{"rejectNoFoundBC", true, "if applyBcSel; reject if no BC found when de-referencing"}; + + } evsel; - Configurable ccdbURL{"ccdbURL", "http://alice-ccdb.cern.ch", "ccdb url"}; - Configurable pathGRPECSObject{"pathGRPECSObject", "GLO/Config/GRPECS", "Path to GRPECS object"}; - Configurable pathVertexZ{"pathVertexZ", "Users/d/ddobrigk/Centrality/Calibration", "Path to vertexZ profiles"}; - Configurable irSource{"irSource", "ZNC hadronic", "Source of the interaction rate: (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; - Configurable irCrashOnNull{"irCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; - Configurable irDoRateVsTime{"irDoRateVsTime", true, "Do IR plots"}; + // _______________________________________ + // BC Selections + struct : ConfigurableGroup { + std::string prefix = "bcsel"; + Configurable rejectZNAC{"rejectZNAC", false, "reject if !(kIsBBZNA && kIsBBZNC)"}; + Configurable rejectZPAC{"rejectZPAC", false, "reject if !(kIsBBZPA && kIsBBZPC)"}; + Configurable selectCollidingBCs{"selectCollidingBCs", true, "BC analysis: select colliding BCs"}; + Configurable selectTVX{"selectTVX", true, "BC analysis: select TVX"}; + Configurable selectFV0OrA{"selectFV0OrA", true, "BC analysis: select FV0OrA"}; + Configurable rejectUpc{"rejectUpc", false, "Reject upc events based on forward signals. Configurable group: cfgFwd"}; + Configurable vertexZwithT0{"vertexZwithT0", 1000.0f, "require a certain vertex-Z in BC analysis"}; + Configurable rejectIsFlangeEvent{"rejectIsFlangeEvent", false, "At least one channel with -350 TDC < time < -450 TDC"}; + Configurable minFT0CforVertexZ{"minFT0CforVertexZ", -1.0f, "minimum FT0C for vertex-Z profile calculation"}; + } bcsel; // _______________________________________ - // upc rejection criteria - // reject low zna/c + // Limits for selections using forward detectors struct : ConfigurableGroup { + std::string prefix = "cfgFwd"; Configurable minZNACsignal{"minZNACsignal", -999999.0f, "min zna/c signal"}; - Configurable maxFT0CforZNACselection{"maxFT0CforZNACselection", -99999.0f, "max ft0c signal for minZNACsignal to work"}; - Configurable minFV0Asignal{"minFV0Asignal", -999999.0f, "min fv0a signal"}; - Configurable maxFT0CforFV0Aselection{"maxFT0CforFV0Aselection", -99999.0f, "max ft0c signal for minFV0Asignal to work"}; - Configurable minFDDAsignal{"minFDDAsignal", -999999.0f, "min fdda signal"}; + Configurable maxFT0CforZNACselection{"maxFT0CforZNACselection", -99999.0f, "max ft0c signal for minZNACsignal to work"}; + Configurable maxFT0CforFV0Aselection{"maxFT0CforFV0Aselection", -99999.0f, "max ft0c signal for minFV0Asignal to work"}; Configurable maxFT0CforFDDAselection{"maxFT0CforFDDAselection", -99999.0f, "max ft0c signal for minFDDAsignal to work"}; - } upcRejection; + Configurable fZPABBlower{"fZPABBlower", -2.f, "lower time limit ZPA (ns)"}; + Configurable fZPCBBlower{"fZPCBBlower", -2.f, "lower time limit ZPC (ns)"}; + Configurable fZPABBupper{"fZPABBupper", 2.f, "upper time limit ZPA (ns)"}; + Configurable fZPCBBupper{"fZPCBBupper", 2.f, "upper time limit ZPC (ns)"}; + } cfgFwd; + + // _______________________________________ + // Scaling + struct : ConfigurableGroup { + std::string prefix = "scale"; + Configurable factorFT0A{"factorFT0A", 1.00f, "scale the raw FT0A signal for convenience"}; + Configurable factorFT0C{"factorFT0C", 1.00f, "scale the raw FT0C signal for convenience"}; + Configurable factorFV0A{"factorFV0A", 1.00f, "scale the raw FV0A signal for convenience"}; + Configurable factorFT0M{"factorFT0M", 1.00f, "scale the raw FT0M signal for convenience"}; + Configurable factorFIT{"factorFIT", 1.00f, "scale the raw FV0A+FT0C signal for convenience"}; + Configurable normFT0A{"normFT0A", 1.00f, "scale the self-normalised FT0A signal; scale.normFT0A * FT0A / "}; + Configurable normFT0C{"normFT0C", 1.00f, "scale the self-normalised FT0C signal; scale.normFT0C * FT0C / "}; + Configurable normFV0A{"normFV0A", 1.00f, "scale the self-normalised FV0A signal; scale.normFV0A * FV0A / "}; + } scale; // Configurable Axes for 2d plots, etc ConfigurableAxis axisMultFV0A{"axisMultFV0A", {1000, 0, 100000}, "FV0A amplitude"}; @@ -145,15 +202,18 @@ struct centralityStudy { ConfigurableAxis axisMultGlobalTracks{"axisMultGlobalTracks", {500, 0, 5000}, "Number of global tracks"}; ConfigurableAxis axisMultMFTTracks{"axisMultMFTTracks", {500, 0, 5000}, "Number of MFT tracks"}; ConfigurableAxis axisMultMCCounts{"axisMultMCCounts", {1000, 0, 5000}, "N_{ch}"}; - ConfigurableAxis axisTrackOccupancy{"axisTrackOccupancy", {50, 0, 5000}, "Track occupancy"}; ConfigurableAxis axisFT0COccupancy{"axisFT0COccupancy", {50, 0, 80000}, "FT0C occupancy"}; // For one-dimensional plots, where binning is no issue + ConfigurableAxis axisMultUltraFineFIT{"axisMultUltraFineFIT", {60000, 0, 60000}, "FT0M + FV0 amplitude"}; ConfigurableAxis axisMultUltraFineFV0A{"axisMultUltraFineFV0A", {60000, 0, 60000}, "FV0A amplitude"}; - ConfigurableAxis axisMultUltraFineFT0M{"axisMultUltraFineFT0M", {50000, 0, 200000}, "FT0M amplitude"}; ConfigurableAxis axisMultUltraFineFT0C{"axisMultUltraFineFT0C", {60000, 0, 60000}, "FT0C amplitude"}; ConfigurableAxis axisMultUltraFineFT0A{"axisMultUltraFineFT0A", {60000, 0, 60000}, "FT0A amplitude"}; + ConfigurableAxis axisMultUltraFineFT0M{"axisMultUltraFineFT0M", {50000, 0, 200000}, "FT0M amplitude"}; + ConfigurableAxis axisMultUltraFineFV0AT0C{"axisMultUltraFineFV0AT0C", {60000, 0, 60000}, "FV0A + FT0C amplitude"}; + ConfigurableAxis axisMultUltraFineScaledFT0M{"axisMultUltraFineScaledFT0M", {40000, 0, 40}, "a*FT0A/ + b*FT0C/"}; + ConfigurableAxis axisMultUltraFineScaledFV0AT0C{"axisMultUltraFineScaledFV0AT0C", {40000, 0, 40}, "a*FV0A/ + b*FT0C/"}; ConfigurableAxis axisMultUltraFinePVContributors{"axisMultUltraFinePVContributors", {10000, 0, 10000}, "Number of PV Contributors"}; ConfigurableAxis axisMultUltraFineGlobalTracks{"axisMultUltraFineGlobalTracks", {5000, 0, 5000}, "Number of global tracks"}; ConfigurableAxis axisMultUltraFineMFTTracks{"axisMultUltraFineMFTTracks", {5000, 0, 5000}, "Number of MFT tracks"}; @@ -162,7 +222,7 @@ struct centralityStudy { ConfigurableAxis axisMultITSTPC{"axisMultITSTPC", {200, 0, 6000}, "Number of ITSTPC matched tracks"}; // For centrality studies if requested - ConfigurableAxis axisCentrality{"axisCentrality", {100, 0, 100}, "FT0C percentile"}; + ConfigurableAxis axisCentrality{"axisCentrality", {100, 0, 100}, "FT0C percentile (%)"}; ConfigurableAxis axisImpactParameter{"axisImpactParameter", {200, 0.0f, 20.0f}, "b (fm)"}; ConfigurableAxis axisPVChi2{"axisPVChi2", {300, 0, 30}, "FT0C percentile"}; ConfigurableAxis axisDeltaTime{"axisDeltaTime", {300, 0, 300}, "#Delta time"}; @@ -175,20 +235,30 @@ struct centralityStudy { ConfigurableAxis axisPVz{"axisPVz", {400, -20.0f, +20.0f}, "PVz (cm)"}; ConfigurableAxis axisZN{"axisZN", {1100, -50.0f, +500.0f}, "ZN"}; + template + std::shared_ptr& getHist(const std::string& name) + { + return std::get>(histPointers[name]); + } + + // fill the global histogram and, if enabled, its run-by-run copy with the same values + template + void fillHist(const TName& name, Ts... values) + { + histos.fill(name, values...); + if (studies.doRunByRunHistograms) { + getHist(histPath + TName::str)->Fill(values...); + } + } + void init(InitContext&) { - hCalibObjects = nullptr; - hVtxZFV0A = nullptr; - hVtxZFT0A = nullptr; - hVtxZFT0C = nullptr; - hVtxZNTracks = nullptr; - hVtxZNGlobals = nullptr; - hVtxZMFT = nullptr; - hVtxZFDDA = nullptr; - hVtxZFDDC = nullptr; + ccdb->setURL(ccdbSettings.ccdbURL); + // ccdb->setCaching(true); + // ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); if (doprocessCollisions || doprocessCollisionsWithCentrality) { - const AxisSpec axisCollisions{100, -0.5f, 99.5f, "Number of collisions"}; histos.add("hCollisionSelection", "hCollisionSelection", kTH1D, {{20, -0.5f, +19.5f}}); histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); @@ -207,64 +277,180 @@ struct centralityStudy { histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(15, "rejectCollInTimeRangeNarrow"); histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(16, "em/upc rejection"); histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(17, "isFlangeEvent"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(18, "INEL>0"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(19, "bcsel"); histos.add("hFT0A_Collisions", "hFT0A_Collisions", kTH1D, {axisMultUltraFineFT0A}); histos.add("hFT0C_Collisions", "hFT0C_Collisions", kTH1D, {axisMultUltraFineFT0C}); histos.add("hFT0M_Collisions", "hFT0M_Collisions", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFV0A_Collisions", "hFV0A_Collisions", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hFV0AOuter_Collisions", "hFV0AOuter_Collisions", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hFT0AOuter_Collisions", "hFT0AOuter_Collisions", kTH1D, {axisMultUltraFineFT0A}); + histos.add("hFT0COuter_Collisions", "hFT0COuter_Collisions", kTH1D, {axisMultUltraFineFT0C}); + histos.add("hFT0MOuterA_Collisions", "hFT0MOuterA_Collisions", kTH1D, {axisMultUltraFineFT0M}); + histos.add("hFIT_Collisions", "hFIT_Collisions", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_FullV0_Collisions", "hFIT_FullV0_Collisions", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_All_Collisions", "hFIT_All_Collisions", kTH1D, {axisMultUltraFineFIT}); + histos.add("hNGlobalTracks", "hNGlobalTracks", kTH1D, {axisMultUltraFineGlobalTracks}); histos.add("hNMFTTracks", "hNMFTTracks", kTH1D, {axisMultUltraFineMFTTracks}); histos.add("hNPVContributors", "hNPVContributors", kTH1D, {axisMultUltraFinePVContributors}); + histos.add("hInteractionRate", "hInteractionRate", kTH1D, {axisInteractionRate}); - histos.add("hFT0CvsPVz_Collisions_All", "hFT0CvsPVz_Collisions_All", kTProfile, {axisPVz}); histos.add("hFT0CvsPVz_Collisions", "hFT0CvsPVz_Collisions", kTProfile, {axisPVz}); + histos.add("hFT0AvsPVz_Collisions", "hFT0AvsPVz_Collisions", kTProfile, {axisPVz}); histos.add("hFV0AvsPVz_Collisions", "hFV0AvsPVz_Collisions", kTProfile, {axisPVz}); histos.add("hNGlobalTracksvsPVz_Collisions", "hNGlobalTracksvsPVz_Collisions", kTProfile, {axisPVz}); histos.add("hNMFTTracksvsPVz_Collisions", "hNMFTTracksvsPVz_Collisions", kTProfile, {axisPVz}); + + if (evsel.applyBcSel) { + histos.add("hCollToBcQa", "hCollToBcQa", kTH1D, {{3, -0.5, 2.5}}); + histos.get(HIST("hCollToBcQa"))->GetXaxis()->SetBinLabel(1, "Found"); + histos.get(HIST("hCollToBcQa"))->GetXaxis()->SetBinLabel(2, "Not found"); + histos.get(HIST("hCollToBcQa"))->GetXaxis()->SetBinLabel(3, "Rejected"); + } + + if (studies.do2DPlots) { + histos.add("hNContribsVsFT0C", "hNContribsVsFT0C", kTH2F, {axisMultFT0C, axisMultPVContributors}); + histos.add("hNContribsVsFV0A", "hNContribsVsFV0A", kTH2F, {axisMultFV0A, axisMultPVContributors}); + histos.add("hMatchedVsITSOnly", "hMatchedVsITSOnly", kTH2F, {axisMultITSOnly, axisMultITSTPC}); + + // 2d correlation of fit signals + histos.add("hFT0AVsFT0C", "hFT0AVsFT0C", kTH2F, {axisMultFT0C, axisMultFT0A}); + histos.add("hFV0AVsFT0C", "hFV0AVsFT0C", kTH2F, {axisMultFT0C, axisMultFV0A}); + histos.add("hFT0AOuterVsFT0C", "hFT0AOuterVsFT0C", kTH2F, {axisMultFT0C, axisMultFT0A}); + histos.add("hFDDAVsFT0C", "hFDDAVsFT0C", kTH2F, {axisMultFT0C, axisMultFDDA}); + histos.add("hFDDCVsFT0C", "hFDDCVsFT0C", kTH2F, {axisMultFT0C, axisMultFDDC}); + } + + if (studies.doNTracksPVeta05VsRawSignals) { + histos.add("hNTracksPVeta05VsFT0A", "hNTracksPVeta05VsFT0A", kTH2F, {axisMultFT0A, axisMultPVContributors}); + histos.add("hNTracksPVeta05VsFT0C", "hNTracksPVeta05VsFT0C", kTH2F, {axisMultFT0C, axisMultPVContributors}); + histos.add("hNTracksPVeta05VsFT0M", "hNTracksPVeta05VsFT0M", kTH2F, {axisMultFT0M, axisMultPVContributors}); + histos.add("hNTracksPVeta05VsFV0A", "hNTracksPVeta05VsFV0A", kTH2F, {axisMultFV0A, axisMultPVContributors}); + histos.add("hNTracksPVeta05VsFDDA", "hNTracksPVeta05VsFDDA", kTH2F, {axisMultFDDA, axisMultPVContributors}); + histos.add("hNTracksPVeta05VsFDDC", "hNTracksPVeta05VsFDDC", kTH2F, {axisMultFDDC, axisMultPVContributors}); + histos.add("hNTracksPVeta05VsNMFTTracks", "hNTracksPVeta05VsNMFTTracks", kTH2F, {axisMultMFTTracks, axisMultPVContributors}); + histos.add("hNTracksPVeta05VsNTPV", "hNTracksPVeta05VsNTPV", kTH2F, {axisMultPVContributors, axisMultPVContributors}); + } + + if (studies.doNTracksPVeta08VsRawSignals) { + histos.add("hNTracksPVeta08VsFT0A", "hNTracksPVeta08VsFT0A", kTH2F, {axisMultFT0A, axisMultPVContributors}); + histos.add("hNTracksPVeta08VsFT0C", "hNTracksPVeta08VsFT0C", kTH2F, {axisMultFT0C, axisMultPVContributors}); + histos.add("hNTracksPVeta08VsFT0M", "hNTracksPVeta08VsFT0M", kTH2F, {axisMultFT0M, axisMultPVContributors}); + histos.add("hNTracksPVeta08VsFV0A", "hNTracksPVeta08VsFV0A", kTH2F, {axisMultFV0A, axisMultPVContributors}); + histos.add("hNTracksPVeta08VsFDDA", "hNTracksPVeta08VsFDDA", kTH2F, {axisMultFDDA, axisMultPVContributors}); + histos.add("hNTracksPVeta08VsFDDC", "hNTracksPVeta08VsFDDC", kTH2F, {axisMultFDDC, axisMultPVContributors}); + histos.add("hNTracksPVeta08VsNMFTTracks", "hNTracksPVeta08VsNMFTTracks", kTH2F, {axisMultMFTTracks, axisMultPVContributors}); + histos.add("hNTracksPVeta08VsNTPV", "hNTracksPVeta08VsNTPV", kTH2F, {axisMultPVContributors, axisMultPVContributors}); + } + + if (studies.doNTracksPVeta10VsRawSignals) { + histos.add("hNTracksPVeta10VsFT0A", "hNTracksPVeta10VsFT0A", kTH2F, {axisMultFT0A, axisMultPVContributors}); + histos.add("hNTracksPVeta10VsFT0C", "hNTracksPVeta10VsFT0C", kTH2F, {axisMultFT0C, axisMultPVContributors}); + histos.add("hNTracksPVeta10VsFT0M", "hNTracksPVeta10VsFT0M", kTH2F, {axisMultFT0M, axisMultPVContributors}); + histos.add("hNTracksPVeta10VsFV0A", "hNTracksPVeta10VsFV0A", kTH2F, {axisMultFV0A, axisMultPVContributors}); + histos.add("hNTracksPVeta10VsFDDA", "hNTracksPVeta10VsFDDA", kTH2F, {axisMultFDDA, axisMultPVContributors}); + histos.add("hNTracksPVeta10VsFDDC", "hNTracksPVeta10VsFDDC", kTH2F, {axisMultFDDC, axisMultPVContributors}); + histos.add("hNTracksPVeta10VsNMFTTracks", "hNTracksPVeta10VsNMFTTracks", kTH2F, {axisMultMFTTracks, axisMultPVContributors}); + histos.add("hNTracksPVeta10VsNTPV", "hNTracksPVeta10VsNTPV", kTH2F, {axisMultPVContributors, axisMultPVContributors}); + } + + if (studies.doNGlobalTracksVsRawSignals) { + histos.add("hNGlobalTracksVsFT0A", "hNGlobalTracksVsFT0A", kTH2F, {axisMultFT0A, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFT0C", "hNGlobalTracksVsFT0C", kTH2F, {axisMultFT0C, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFT0M", "hNGlobalTracksVsFT0M", kTH2F, {axisMultFT0M, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFT0MOuterA", "hNGlobalTracksVsFT0MOuterA", kTH2F, {axisMultFT0M, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFV0A", "hNGlobalTracksVsFV0A", kTH2F, {axisMultFV0A, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFDDA", "hNGlobalTracksVsFDDA", kTH2F, {axisMultFDDA, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsFDDC", "hNGlobalTracksVsFDDC", kTH2F, {axisMultFDDC, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsZNA", "hNGlobalTracksVsZNA", kTH2F, {axisZN, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsZNC", "hNGlobalTracksVsZNC", kTH2F, {axisZN, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsNMFTTracks", "hNGlobalTracksVsNMFTTracks", kTH2F, {axisMultMFTTracks, axisMultGlobalTracks}); + histos.add("hNGlobalTracksVsNTPV", "hNGlobalTracksVsNTPV", kTH2F, {axisMultPVContributors, axisMultGlobalTracks}); + } + + if (studies.doOccupancyStudyVsRawValues2d) { + histos.add("hNcontribsProfileVsTrackOccupancyVsFT0C", "hNcontribsProfileVsTrackOccupancyVsFT0C", kTProfile2D, {axisTrackOccupancy, axisMultFT0C}); + histos.add("hNGlobalTracksProfileVsTrackOccupancyVsFT0C", "hNGlobalTracksProfileVsTrackOccupancyVsFT0C", kTProfile2D, {axisTrackOccupancy, axisMultFT0C}); + histos.add("hNcontribsProfileVsFT0COccupancyVsFT0C", "hNcontribsProfileVsFT0COccupancyVsFT0C", kTProfile2D, {axisFT0COccupancy, axisMultFT0C}); + histos.add("hNGlobalTracksProfileVsFT0COccupancyVsFT0C", "hNGlobalTracksProfileVsFT0COccupancyVsFT0C", kTProfile2D, {axisFT0COccupancy, axisMultFT0C}); + } + + if (studies.doOccupancyStudyVsRawValues3d) { + histos.add("hTrackOccupancyVsNContribsVsFT0C", "hTrackOccupancyVsNContribsVsFT0C", kTH3F, {axisTrackOccupancy, axisMultPVContributors, axisMultFT0C}); + histos.add("hTrackOccupancyVsNGlobalTracksVsFT0C", "hTrackOccupancyVsNGlobalTracksVsFT0C", kTH3F, {axisTrackOccupancy, axisMultGlobalTracks, axisMultFT0C}); + histos.add("hFT0COccupancyVsNContribsVsFT0C", "hFT0COccupancyVsNContribsVsFT0C", kTH3F, {axisFT0COccupancy, axisMultPVContributors, axisMultFT0C}); + histos.add("hFT0COccupancyVsNGlobalTracksVsFT0C", "hFT0COccupancyVsNGlobalTracksVsFT0C", kTH3F, {axisFT0COccupancy, axisMultGlobalTracks, axisMultFT0C}); + } + + if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { + // in case requested: do vs centrality debugging + histos.add("hCentrality", "hCentrality", kTH1F, {axisCentrality}); + histos.add("hNContribsVsCentrality", "hNContribsVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); + histos.add("hNITSTPCTracksVsCentrality", "hNITSTPCTracksVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); + histos.add("hNITSOnlyTracksVsCentrality", "hNITSOnlyTracksVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); + histos.add("hNGlobalTracksVsCentrality", "hNGlobalTracksVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); + histos.add("hNMFTTracksVsCentrality", "hNMFTTracksVsCentrality", kTH2F, {axisCentrality, axisMultMFTTracks}); + histos.add("hPVChi2VsCentrality", "hPVChi2VsCentrality", kTH2F, {axisCentrality, axisPVChi2}); + histos.add("hDeltaTimeVsCentrality", "hDeltaTimeVsCentrality", kTH2F, {axisCentrality, axisDeltaTime}); + histos.add("hInteractionRateVsCentrality", "hInteractionRateVsCentrality", kTH2F, {axisCentrality, axisInteractionRate}); + + if (studies.doOccupancyStudyVsCentrality2d) { + histos.add("hNcontribsProfileVsTrackOccupancyVsCentrality", "hNcontribsProfileVsTrackOccupancyVsCentrality", kTProfile2D, {axisTrackOccupancy, axisCentrality}); + histos.add("hNGlobalTracksProfileVsTrackOccupancyVsCentrality", "hNGlobalTracksProfileVsTrackOccupancyVsCentrality", kTProfile2D, {axisTrackOccupancy, axisCentrality}); + histos.add("hNcontribsProfileVsFT0COccupancyVsCentrality", "hNcontribsProfileVsFT0COccupancyVsCentrality", kTProfile2D, {axisFT0COccupancy, axisCentrality}); + histos.add("hNGlobalTracksProfileVsFT0COccupancyVsCentrality", "hNGlobalTracksProfileVsFT0COccupancyVsCentrality", kTProfile2D, {axisFT0COccupancy, axisCentrality}); + } + + if (studies.doOccupancyStudyVsCentrality3d) { + histos.add("hTrackOccupancyVsNContribsVsCentrality", "hTrackOccupancyVsNContribsVsCentrality", kTH3F, {axisTrackOccupancy, axisMultPVContributors, axisCentrality}); + histos.add("hTrackOccupancyVsNGlobalTracksVsCentrality", "hTrackOccupancyVsNGlobalTracksVsCentrality", kTH3F, {axisTrackOccupancy, axisMultGlobalTracks, axisCentrality}); + histos.add("hFT0COccupancyVsNContribsVsCentrality", "hFT0COccupancyVsNContribsVsCentrality", kTH3F, {axisFT0COccupancy, axisMultPVContributors, axisCentrality}); + histos.add("hFT0COccupancyVsNGlobalTracksVsCentrality", "hFT0COccupancyVsNGlobalTracksVsCentrality", kTH3F, {axisFT0COccupancy, axisMultGlobalTracks, axisCentrality}); + } + } } - if (doprocessBCs) { + if (doprocessBCs || doprocessBCsWithTime) { histos.add("hBCSelection", "hBCSelection", kTH1D, {{20, -0.5, 19.5f}}); histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(1, "All BCs"); histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(2, "Colliding BCs"); histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(3, "TVX"); histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(4, "FV0OrA"); - histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(5, "upc rej"); - histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(6, "zdc rej"); - + histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(5, "FT0PosZ"); + histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(6, "upc rej"); + histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(7, "znac time"); + histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(8, "zpac time"); + histos.get(HIST("hBCSelection"))->GetXaxis()->SetBinLabel(9, "isFlangeEvent"); + + histos.add("hFIT_BCs", "hFIT_BCs", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_FullV0_BCs", "hFIT_FullV0_BCs", kTH1D, {axisMultUltraFineFIT}); + histos.add("hFIT_All_BCs", "hFIT_All_BCs", kTH1D, {axisMultUltraFineFIT}); histos.add("hFT0C_BCs", "hFT0C_BCs", kTH1D, {axisMultUltraFineFT0C}); + histos.add("hFT0COuter_BCs", "hFT0COuter_BCs", kTH1D, {axisMultUltraFineFT0C}); + histos.add("hFT0A_BCs", "hFT0A_BCs", kTH1D, {axisMultUltraFineFT0A}); + histos.add("hFT0AOuter_BCs", "hFT0AOuter_BCs", kTH1D, {axisMultUltraFineFT0A}); histos.add("hFT0M_BCs", "hFT0M_BCs", kTH1D, {axisMultUltraFineFT0M}); + histos.add("hFT0MOuterA_BCs", "hFT0MOuterA_BCs", kTH1D, {axisMultUltraFineFT0M}); histos.add("hFV0A_BCs", "hFV0A_BCs", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hFV0AOuter_BCs", "hFV0AOuter_BCs", kTH1D, {axisMultUltraFineFV0A}); + + histos.add("hFV0AT0C_BCs", "hFV0AT0C_BCs", kTH1D, {axisMultUltraFineFV0AT0C}); + histos.add("hFV0AOuterT0C_BCs", "hFV0AOuterT0C_BCs", kTH1D, {axisMultUltraFineFV0AT0C}); + histos.add("hScaledFT0M_BCs", "hScaledFT0M_BCs", kTH1D, {axisMultUltraFineScaledFT0M}); + histos.add("hScaledFV0AT0C_BCs", "hScaledFV0AT0C_BCs", kTH1D, {axisMultUltraFineScaledFV0AT0C}); + histos.add("hFT0CvsPVz_BCs_All", "hFT0CvsPVz_BCs_All", kTProfile, {axisPVz}); histos.add("hFT0CvsPVz_BCs", "hFT0CvsPVz_BCs", kTProfile, {axisPVz}); histos.add("hVertexZ_BCvsCO", "hVertexZ_BCvsCO", kTH2F, {axisPVz, axisPVz}); histos.add("hZNAvsFT0C_BCs", "hZNAvsFT0C_BCs", kTH2F, {axisMultFT0C, axisZN}); histos.add("hZNCvsFT0C_BCs", "hZNCvsFT0C_BCs", kTH2F, {axisMultFT0C, axisZN}); - } - if (do2DPlots) { - histos.add("hNContribsVsFT0C", "hNContribsVsFT0C", kTH2F, {axisMultFT0C, axisMultPVContributors}); - histos.add("hNContribsVsFV0A", "hNContribsVsFV0A", kTH2F, {axisMultFV0A, axisMultPVContributors}); - histos.add("hMatchedVsITSOnly", "hMatchedVsITSOnly", kTH2F, {axisMultITSOnly, axisMultITSTPC}); - - // 2d correlation of fit signals - histos.add("hFT0AVsFT0C", "hFT0AVsFT0C", kTH2F, {axisMultFT0C, axisMultFT0A}); - histos.add("hFV0AVsFT0C", "hFV0AVsFT0C", kTH2F, {axisMultFT0C, axisMultFV0A}); - histos.add("hFDDAVsFT0C", "hFDDAVsFT0C", kTH2F, {axisMultFT0C, axisMultFDDA}); - histos.add("hFDDCVsFT0C", "hFDDCVsFT0C", kTH2F, {axisMultFT0C, axisMultFDDC}); - } - - if (doNGlobalTracksVsRawSignals) { - histos.add("hNGlobalTracksVsFT0A", "hNGlobalTracksVsFT0A", kTH2F, {axisMultFT0A, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsFT0C", "hNGlobalTracksVsFT0C", kTH2F, {axisMultFT0C, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsFT0M", "hNGlobalTracksVsFT0M", kTH2F, {axisMultFT0M, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsFV0A", "hNGlobalTracksVsFV0A", kTH2F, {axisMultFV0A, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsFDDA", "hNGlobalTracksVsFDDA", kTH2F, {axisMultFDDA, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsFDDC", "hNGlobalTracksVsFDDC", kTH2F, {axisMultFDDC, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsZNA", "hNGlobalTracksVsZNA", kTH2F, {axisZN, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsZNC", "hNGlobalTracksVsZNC", kTH2F, {axisZN, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsNMFTTracks", "hNGlobalTracksVsNMFTTracks", kTH2F, {axisMultMFTTracks, axisMultGlobalTracks}); - histos.add("hNGlobalTracksVsNTPV", "hNGlobalTracksVsNTPV", kTH2F, {axisMultPVContributors, axisMultGlobalTracks}); + if (studies.do2DPlots) { + histos.add("hFT0AVsFT0C_BCs", "hFT0AVsFT0C_BCs", kTH2F, {axisMultFT0C, axisMultFT0A}); + histos.add("hFV0AVsFT0C_BCs", "hFV0AVsFT0C_BCs", kTH2F, {axisMultFT0C, axisMultFV0A}); + } } if (doprocessCollisionsWithResolutionStudy) { @@ -282,56 +468,6 @@ struct centralityStudy { histos.add("hImpactParameterVsMCFT0M", "hImpactParameterVsMCFT0M", kTH2F, {axisMultMCCounts, axisImpactParameter}); histos.add("hImpactParameterVsMCFV0A", "hImpactParameterVsMCFV0A", kTH2F, {axisMultMCCounts, axisImpactParameter}); } - - if (doOccupancyStudyVsRawValues2d) { - histos.add("hNcontribsProfileVsTrackOccupancyVsFT0C", "hNcontribsProfileVsTrackOccupancyVsFT0C", kTProfile2D, {axisTrackOccupancy, axisMultFT0C}); - histos.add("hNGlobalTracksProfileVsTrackOccupancyVsFT0C", "hNGlobalTracksProfileVsTrackOccupancyVsFT0C", kTProfile2D, {axisTrackOccupancy, axisMultFT0C}); - histos.add("hNcontribsProfileVsFT0COccupancyVsFT0C", "hNcontribsProfileVsFT0COccupancyVsFT0C", kTProfile2D, {axisFT0COccupancy, axisMultFT0C}); - histos.add("hNGlobalTracksProfileVsFT0COccupancyVsFT0C", "hNGlobalTracksProfileVsFT0COccupancyVsFT0C", kTProfile2D, {axisFT0COccupancy, axisMultFT0C}); - } - - if (doOccupancyStudyVsRawValues3d) { - histos.add("hTrackOccupancyVsNContribsVsFT0C", "hTrackOccupancyVsNContribsVsFT0C", kTH3F, {axisTrackOccupancy, axisMultPVContributors, axisMultFT0C}); - histos.add("hTrackOccupancyVsNGlobalTracksVsFT0C", "hTrackOccupancyVsNGlobalTracksVsFT0C", kTH3F, {axisTrackOccupancy, axisMultGlobalTracks, axisMultFT0C}); - histos.add("hFT0COccupancyVsNContribsVsFT0C", "hFT0COccupancyVsNContribsVsFT0C", kTH3F, {axisFT0COccupancy, axisMultPVContributors, axisMultFT0C}); - histos.add("hFT0COccupancyVsNGlobalTracksVsFT0C", "hFT0COccupancyVsNGlobalTracksVsFT0C", kTH3F, {axisFT0COccupancy, axisMultGlobalTracks, axisMultFT0C}); - } - - if (doprocessCollisionsWithCentrality) { - // in case requested: do vs centrality debugging - histos.add("hCentrality", "hCentrality", kTH1F, {axisCentrality}); - histos.add("hNContribsVsCentrality", "hNContribsVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); - histos.add("hNITSTPCTracksVsCentrality", "hNITSTPCTracksVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); - histos.add("hNITSOnlyTracksVsCentrality", "hNITSOnlyTracksVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); - histos.add("hNGlobalTracksVsCentrality", "hNGlobalTracksVsCentrality", kTH2F, {axisCentrality, axisMultPVContributors}); - histos.add("hNMFTTracksVsCentrality", "hNMFTTracksVsCentrality", kTH2F, {axisCentrality, axisMultMFTTracks}); - histos.add("hPVChi2VsCentrality", "hPVChi2VsCentrality", kTH2F, {axisCentrality, axisPVChi2}); - histos.add("hDeltaTimeVsCentrality", "hDeltaTimeVsCentrality", kTH2F, {axisCentrality, axisDeltaTime}); - - if (doOccupancyStudyVsCentrality2d) { - histos.add("hNcontribsProfileVsTrackOccupancyVsCentrality", "hNcontribsProfileVsTrackOccupancyVsCentrality", kTProfile2D, {axisTrackOccupancy, axisCentrality}); - histos.add("hNGlobalTracksProfileVsTrackOccupancyVsCentrality", "hNGlobalTracksProfileVsTrackOccupancyVsCentrality", kTProfile2D, {axisTrackOccupancy, axisCentrality}); - histos.add("hNcontribsProfileVsFT0COccupancyVsCentrality", "hNcontribsProfileVsFT0COccupancyVsCentrality", kTProfile2D, {axisFT0COccupancy, axisCentrality}); - histos.add("hNGlobalTracksProfileVsFT0COccupancyVsCentrality", "hNGlobalTracksProfileVsFT0COccupancyVsCentrality", kTProfile2D, {axisFT0COccupancy, axisCentrality}); - } - - if (doOccupancyStudyVsCentrality3d) { - histos.add("hTrackOccupancyVsNContribsVsCentrality", "hTrackOccupancyVsNContribsVsCentrality", kTH3F, {axisTrackOccupancy, axisMultPVContributors, axisCentrality}); - histos.add("hTrackOccupancyVsNGlobalTracksVsCentrality", "hTrackOccupancyVsNGlobalTracksVsCentrality", kTH3F, {axisTrackOccupancy, axisMultGlobalTracks, axisCentrality}); - histos.add("hFT0COccupancyVsNContribsVsCentrality", "hFT0COccupancyVsNContribsVsCentrality", kTH3F, {axisFT0COccupancy, axisMultPVContributors, axisCentrality}); - histos.add("hFT0COccupancyVsNGlobalTracksVsCentrality", "hFT0COccupancyVsNGlobalTracksVsCentrality", kTH3F, {axisFT0COccupancy, axisMultGlobalTracks, axisCentrality}); - } - } - - if (doTimeStudies) { - ccdb->setURL(ccdbURL); - // ccdb->setCaching(true); - // ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - if (doTimeStudyFV0AOuterVsFT0A3d) { - histos.add((histPath + "h3dFV0AVsTime").c_str(), "", {kTH3F, {{axisDeltaTimestamp, axisMultCoarseFV0A, axisMultCoarseFV0A}}}); - } - } } template @@ -345,22 +481,22 @@ struct centralityStudy { LOGF(info, "Setting up for run: %i", mRunNumber); // only get object when switching runs - o2::parameters::GRPECSObject* grpo = ccdb->getForRun(pathGRPECSObject, mRunNumber); + auto grpo = ccdb->getForRun(ccdbSettings.pathGRPECSObject, mRunNumber); startOfRunTimestamp = grpo->getTimeStart(); if (applyVertexZEqualization.value) { // acquire vertex-Z equalization histograms if requested LOGF(info, "Acquiring vertex-Z profiles for run %i", mRunNumber); - hCalibObjects = ccdb->getForRun(pathVertexZ, mRunNumber); + auto hCalibObjects = ccdb->getForRun(ccdbSettings.pathVertexZ, mRunNumber); - hVtxZFV0A = static_cast(hCalibObjects->FindObject("hVtxZFV0A")); - hVtxZFT0A = static_cast(hCalibObjects->FindObject("hVtxZFT0A")); - hVtxZFT0C = static_cast(hCalibObjects->FindObject("hVtxZFT0C")); - // hVtxZFDDA = static_cast(hCalibObjects->FindObject("hVtxZFDDA")); - // hVtxZFDDC = static_cast(hCalibObjects->FindObject("hVtxZFDDC")); - hVtxZNTracks = static_cast(hCalibObjects->FindObject("hVtxZNTracksPV")); - hVtxZNGlobals = static_cast(hCalibObjects->FindObject("hVtxZNGlobals")); - hVtxZMFT = static_cast(hCalibObjects->FindObject("hVtxZMFT")); + hVtxZFV0A = dynamic_cast(hCalibObjects->FindObject("hVtxZFV0A")); + hVtxZFT0A = dynamic_cast(hCalibObjects->FindObject("hVtxZFT0A")); + hVtxZFT0C = dynamic_cast(hCalibObjects->FindObject("hVtxZFT0C")); + // hVtxZFDDA = dynamic_cast(hCalibObjects->FindObject("hVtxZFDDA")); + // hVtxZFDDC = dynamic_cast(hCalibObjects->FindObject("hVtxZFDDC")); + hVtxZNTracks = dynamic_cast(hCalibObjects->FindObject("hVtxZNTracksPV")); + hVtxZNGlobals = dynamic_cast(hCalibObjects->FindObject("hVtxZNGlobals")); + hVtxZMFT = dynamic_cast(hCalibObjects->FindObject("hVtxZMFT")); // Capture error if (!hVtxZFV0A || !hVtxZFT0A || !hVtxZFT0C || !hVtxZNTracks || !hVtxZNGlobals || !hVtxZMFT) { @@ -368,35 +504,81 @@ struct centralityStudy { } } - histPath = std::format("Run_{}/", mRunNumber); + if (ccdbSettings.fetchCentralityCalibration) { + LOGF(info, "Acquiring centrality calibration for run %i", mRunNumber); + auto hCentralityObjects = ccdb->getForRun(ccdbSettings.pathCentrality, mRunNumber); + hCentralityFV0A = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFV0")); + hCentralityFT0A = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFT0A")); + hCentralityFT0C = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFT0C")); + hCentralityFT0M = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFT0")); + hCentralityFDDM = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFDD")); + hCentralityNTPV = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqNTracksPV")); + hCentralityNGlo = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqNGlobal")); + hCentralityMFT = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqMFT")); + + // won't capture null pointers -> explicitly check for those when attempting to evaluate + auto reportSuccess = [](TH1* hist, const std::string& name) { + if (!hist) { + LOGF(info, "Calibration missing for %s", name); + } else { + LOGF(info, "Calibration loaded for %s", name); + } + }; + + reportSuccess(hCentralityFV0A, "FV0A"); + reportSuccess(hCentralityFT0A, "FT0A"); + reportSuccess(hCentralityFT0C, "FT0C"); + reportSuccess(hCentralityFT0M, "FT0M"); + reportSuccess(hCentralityFDDM, "FDDM"); + reportSuccess(hCentralityNTPV, "NTPV"); + reportSuccess(hCentralityNGlo, "NGlobals"); + reportSuccess(hCentralityMFT, "MFT"); + LOGF(info, "Centrality calibration loading done."); + } + if (!studies.doRunByRunHistograms) { + return; + } + + histPath = std::format("Run_{}/", mRunNumber); if (doprocessCollisions || doprocessCollisionsWithCentrality) { histPointers.insert({histPath + "hCollisionSelection", histos.add((histPath + "hCollisionSelection").c_str(), "hCollisionSelection", {kTH1D, {{20, -0.5f, +19.5f}}})}); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(1, "All collisions"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(2, "sel8 cut"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(3, "posZ cut"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(6, "kIsVertexITSTPC"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(7, "kIsGoodZvtxFT0vsPV"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(11, "Neighbour rejection"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(12, "no ITS in-ROF pileup (standard)"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(13, "no ITS in-ROF pileup (strict)"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(14, "is UPC event"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(15, "rejectCollInTimeRangeNarrow"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(16, "em/upc rejection"); - getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(17, "isFlangeEvent"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(1, "All collisions"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(2, "sel8 cut"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(3, "posZ cut"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(6, "kIsVertexITSTPC"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(7, "kIsGoodZvtxFT0vsPV"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(11, "Neighbour rejection"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(12, "no ITS in-ROF pileup (standard)"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(13, "no ITS in-ROF pileup (strict)"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(14, "is UPC event"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(15, "rejectCollInTimeRangeNarrow"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(16, "em/upc rejection"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(17, "isFlangeEvent"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(18, "INEL>0"); + getHist(histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(19, "bcsel"); histPointers.insert({histPath + "hFT0C_Collisions", histos.add((histPath + "hFT0C_Collisions").c_str(), "hFT0C_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); + histPointers.insert({histPath + "hFT0COuter_Collisions", histos.add((histPath + "hFT0COuter_Collisions").c_str(), "hFT0COuter_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); histPointers.insert({histPath + "hFT0A_Collisions", histos.add((histPath + "hFT0A_Collisions").c_str(), "hFT0A_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); + histPointers.insert({histPath + "hFT0AOuter_Collisions", histos.add((histPath + "hFT0AOuter_Collisions").c_str(), "hFT0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); + histPointers.insert({histPath + "hFV0AOuter_Collisions", histos.add((histPath + "hFV0AOuter_Collisions").c_str(), "hFV0AOuter_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); + histPointers.insert({histPath + "hFT0MOuterA_Collisions", histos.add((histPath + "hFT0MOuterA_Collisions").c_str(), "hFT0MOuterA_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); histPointers.insert({histPath + "hFT0M_Collisions", histos.add((histPath + "hFT0M_Collisions").c_str(), "hFT0M_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); histPointers.insert({histPath + "hFV0A_Collisions", histos.add((histPath + "hFV0A_Collisions").c_str(), "hFV0A_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); + histPointers.insert({histPath + "hFIT_Collisions", histos.add((histPath + "hFIT_Collisions").c_str(), "hFIT_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hFIT_FullV0_Collisions", histos.add((histPath + "hFIT_FullV0_Collisions").c_str(), "hFIT_FullV0_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hFIT_All_Collisions", histos.add((histPath + "hFIT_All_Collisions").c_str(), "hFIT_All_Collisions", {kTH1D, {{axisMultUltraFineFIT}}})}); + histPointers.insert({histPath + "hNGlobalTracks", histos.add((histPath + "hNGlobalTracks").c_str(), "hNGlobalTracks", {kTH1D, {{axisMultUltraFineGlobalTracks}}})}); histPointers.insert({histPath + "hNMFTTracks", histos.add((histPath + "hNMFTTracks").c_str(), "hNMFTTracks", {kTH1D, {{axisMultUltraFineMFTTracks}}})}); histPointers.insert({histPath + "hNPVContributors", histos.add((histPath + "hNPVContributors").c_str(), "hNPVContributors", {kTH1D, {{axisMultUltraFinePVContributors}}})}); + histPointers.insert({histPath + "hInteractionRate", histos.add((histPath + "hInteractionRate").c_str(), "hInteractionRate", {kTH1D, {{axisInteractionRate}}})}); if (applyVertexZEqualization) { histPointers.insert({histPath + "hFT0C_Collisions_Unequalized", histos.add((histPath + "hFT0C_Collisions_Unequalized").c_str(), "hFT0C_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFT0C}}})}); @@ -407,16 +589,15 @@ struct centralityStudy { histPointers.insert({histPath + "hNPVContributors_Unequalized", histos.add((histPath + "hNPVContributors_Unequalized").c_str(), "hNPVContributors_Unequalized", {kTH1D, {{axisMultUltraFinePVContributors}}})}); } - histPointers.insert({histPath + "hFT0CvsPVz_Collisions_All", histos.add((histPath + "hFT0CvsPVz_Collisions_All").c_str(), "hFT0CvsPVz_Collisions_All", {kTProfile, {{axisPVz}}})}); - histPointers.insert({histPath + "hFT0AvsPVz_Collisions", histos.add((histPath + "hFT0AvsPVz_Collisions").c_str(), "hFT0AvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); histPointers.insert({histPath + "hFT0CvsPVz_Collisions", histos.add((histPath + "hFT0CvsPVz_Collisions").c_str(), "hFT0CvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hFT0AvsPVz_Collisions", histos.add((histPath + "hFT0AvsPVz_Collisions").c_str(), "hFT0AvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); histPointers.insert({histPath + "hFV0AvsPVz_Collisions", histos.add((histPath + "hFV0AvsPVz_Collisions").c_str(), "hFV0AvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); histPointers.insert({histPath + "hNGlobalTracksvsPVz_Collisions", histos.add((histPath + "hNGlobalTracksvsPVz_Collisions").c_str(), "hNGlobalTracksvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); histPointers.insert({histPath + "hNMFTTracksvsPVz_Collisions", histos.add((histPath + "hNMFTTracksvsPVz_Collisions").c_str(), "hNMFTTracksvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); histPointers.insert({histPath + "hNTPVvsPVz_Collisions", histos.add((histPath + "hNTPVvsPVz_Collisions").c_str(), "hNTPVvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); } - if (do2DPlots) { + if (studies.do2DPlots) { histPointers.insert({histPath + "hNContribsVsFT0C", histos.add((histPath + "hNContribsVsFT0C").c_str(), "hNContribsVsFT0C", {kTH2F, {{axisMultFT0C, axisMultPVContributors}}})}); histPointers.insert({histPath + "hNContribsVsFV0A", histos.add((histPath + "hNContribsVsFV0A").c_str(), "hNContribsVsFV0A", {kTH2F, {{axisMultFV0A, axisMultPVContributors}}})}); histPointers.insert({histPath + "hMatchedVsITSOnly", histos.add((histPath + "hMatchedVsITSOnly").c_str(), "hMatchedVsITSOnly", {kTH2F, {{axisMultITSOnly, axisMultITSTPC}}})}); @@ -426,9 +607,10 @@ struct centralityStudy { histPointers.insert({histPath + "hFV0AVsFT0C", histos.add((histPath + "hFV0AVsFT0C").c_str(), "hFV0AVsFT0C", {kTH2F, {{axisMultFT0C, axisMultFV0A}}})}); histPointers.insert({histPath + "hFDDAVsFT0C", histos.add((histPath + "hFDDAVsFT0C").c_str(), "hFDDAVsFT0C", {kTH2F, {{axisMultFT0C, axisMultFDDA}}})}); histPointers.insert({histPath + "hFDDCVsFT0C", histos.add((histPath + "hFDDCVsFT0C").c_str(), "hFDDCVsFT0C", {kTH2F, {{axisMultFT0C, axisMultFDDC}}})}); + histPointers.insert({histPath + "hFT0AOuterVsFT0C", histos.add((histPath + "hFT0AOuterVsFT0C").c_str(), "hFT0AOuterVsFT0C", {kTH2F, {{axisMultFT0C, axisMultFT0A}}})}); } - if (doprocessCollisionsWithCentrality) { + if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { // in case requested: do vs centrality debugging histPointers.insert({histPath + "hCentrality", histos.add((histPath + "hCentrality").c_str(), "hCentrality", {kTH1F, {{axisCentrality}}})}); histPointers.insert({histPath + "hNContribsVsCentrality", histos.add((histPath + "hNContribsVsCentrality").c_str(), "hNContribsVsCentrality", {kTH2F, {{axisCentrality, axisMultPVContributors}}})}); @@ -438,18 +620,53 @@ struct centralityStudy { histPointers.insert({histPath + "hNMFTTracksVsCentrality", histos.add((histPath + "hNMFTTracksVsCentrality").c_str(), "hNMFTTracksVsCentrality", {kTH2F, {{axisCentrality, axisMultMFTTracks}}})}); histPointers.insert({histPath + "hPVChi2VsCentrality", histos.add((histPath + "hPVChi2VsCentrality").c_str(), "hPVChi2VsCentrality", {kTH2F, {{axisCentrality, axisPVChi2}}})}); histPointers.insert({histPath + "hDeltaTimeVsCentrality", histos.add((histPath + "hDeltaTimeVsCentrality").c_str(), "hDeltaTimeVsCentrality", {kTH2F, {{axisCentrality, axisDeltaTime}}})}); + histPointers.insert({histPath + "hInteractionRateVsCentrality", histos.add((histPath + "hInteractionRateVsCentrality").c_str(), "hInteractionRateVsCentrality", {kTH2F, {{axisCentrality, axisInteractionRate}}})}); } - if (doNGlobalTracksVsRawSignals) { + if (studies.doNGlobalTracksVsRawSignals) { histPointers.insert({histPath + "hNGlobalTracksVsFT0A", histos.add((histPath + "hNGlobalTracksVsFT0A").c_str(), "hNGlobalTracksVsFT0A", {kTH2F, {{axisMultFT0A, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFT0C", histos.add((histPath + "hNGlobalTracksVsFT0C").c_str(), "hNGlobalTracksVsFT0C", {kTH2F, {{axisMultFT0C, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFT0M", histos.add((histPath + "hNGlobalTracksVsFT0M").c_str(), "hNGlobalTracksVsFT0M", {kTH2F, {{axisMultFT0M, axisMultGlobalTracks}}})}); + histPointers.insert({histPath + "hNGlobalTracksVsFT0MOuterA", histos.add((histPath + "hNGlobalTracksVsFT0MOuterA").c_str(), "hNGlobalTracksVsFT0MOuterA", {kTH2F, {{axisMultFT0M, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsFV0A", histos.add((histPath + "hNGlobalTracksVsFV0A").c_str(), "hNGlobalTracksVsFV0A", {kTH2F, {{axisMultFV0A, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsNMFTTracks", histos.add((histPath + "hNGlobalTracksVsNMFTTracks").c_str(), "hNGlobalTracksVsNMFTTracks", {kTH2F, {{axisMultMFTTracks, axisMultGlobalTracks}}})}); histPointers.insert({histPath + "hNGlobalTracksVsNTPV", histos.add((histPath + "hNGlobalTracksVsNTPV").c_str(), "hNGlobalTracksVsNTPV", {kTH2F, {{axisMultPVContributors, axisMultGlobalTracks}}})}); } - if (doTimeStudies) { + if (studies.doNTracksPVeta05VsRawSignals) { + histPointers.insert({histPath + "hNTracksPVeta05VsFT0A", histos.add((histPath + "hNTracksPVeta05VsFT0A").c_str(), "hNTracksPVeta05VsFT0A", {kTH2F, {{axisMultFT0A, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta05VsFT0C", histos.add((histPath + "hNTracksPVeta05VsFT0C").c_str(), "hNTracksPVeta05VsFT0C", {kTH2F, {{axisMultFT0C, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta05VsFT0M", histos.add((histPath + "hNTracksPVeta05VsFT0M").c_str(), "hNTracksPVeta05VsFT0M", {kTH2F, {{axisMultFT0M, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta05VsFV0A", histos.add((histPath + "hNTracksPVeta05VsFV0A").c_str(), "hNTracksPVeta05VsFV0A", {kTH2F, {{axisMultFV0A, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta05VsFDDA", histos.add((histPath + "hNTracksPVeta05VsFDDA").c_str(), "hNTracksPVeta05VsFDDA", {kTH2F, {{axisMultFDDA, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta05VsFDDC", histos.add((histPath + "hNTracksPVeta05VsFDDC").c_str(), "hNTracksPVeta05VsFDDC", {kTH2F, {{axisMultFDDC, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta05VsNMFTTracks", histos.add((histPath + "hNTracksPVeta05VsNMFTTracks").c_str(), "hNTracksPVeta05VsNMFTTracks", {kTH2F, {{axisMultMFTTracks, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta05VsNTPV", histos.add((histPath + "hNTracksPVeta05VsNTPV").c_str(), "hNTracksPVeta05VsNTPV", {kTH2F, {{axisMultPVContributors, axisMultPVContributors}}})}); + } + + if (studies.doNTracksPVeta08VsRawSignals) { + histPointers.insert({histPath + "hNTracksPVeta08VsFT0A", histos.add((histPath + "hNTracksPVeta08VsFT0A").c_str(), "hNTracksPVeta08VsFT0A", {kTH2F, {{axisMultFT0A, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta08VsFT0C", histos.add((histPath + "hNTracksPVeta08VsFT0C").c_str(), "hNTracksPVeta08VsFT0C", {kTH2F, {{axisMultFT0C, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta08VsFT0M", histos.add((histPath + "hNTracksPVeta08VsFT0M").c_str(), "hNTracksPVeta08VsFT0M", {kTH2F, {{axisMultFT0M, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta08VsFV0A", histos.add((histPath + "hNTracksPVeta08VsFV0A").c_str(), "hNTracksPVeta08VsFV0A", {kTH2F, {{axisMultFV0A, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta08VsFDDA", histos.add((histPath + "hNTracksPVeta08VsFDDA").c_str(), "hNTracksPVeta08VsFDDA", {kTH2F, {{axisMultFDDA, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta08VsFDDC", histos.add((histPath + "hNTracksPVeta08VsFDDC").c_str(), "hNTracksPVeta08VsFDDC", {kTH2F, {{axisMultFDDC, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta08VsNMFTTracks", histos.add((histPath + "hNTracksPVeta08VsNMFTTracks").c_str(), "hNTracksPVeta08VsNMFTTracks", {kTH2F, {{axisMultMFTTracks, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta08VsNTPV", histos.add((histPath + "hNTracksPVeta08VsNTPV").c_str(), "hNTracksPVeta08VsNTPV", {kTH2F, {{axisMultPVContributors, axisMultPVContributors}}})}); + } + + if (studies.doNTracksPVeta10VsRawSignals) { + histPointers.insert({histPath + "hNTracksPVeta10VsFT0A", histos.add((histPath + "hNTracksPVeta10VsFT0A").c_str(), "hNTracksPVeta10VsFT0A", {kTH2F, {{axisMultFT0A, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta10VsFT0C", histos.add((histPath + "hNTracksPVeta10VsFT0C").c_str(), "hNTracksPVeta10VsFT0C", {kTH2F, {{axisMultFT0C, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta10VsFT0M", histos.add((histPath + "hNTracksPVeta10VsFT0M").c_str(), "hNTracksPVeta10VsFT0M", {kTH2F, {{axisMultFT0M, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta10VsFV0A", histos.add((histPath + "hNTracksPVeta10VsFV0A").c_str(), "hNTracksPVeta10VsFV0A", {kTH2F, {{axisMultFV0A, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta10VsFDDA", histos.add((histPath + "hNTracksPVeta10VsFDDA").c_str(), "hNTracksPVeta10VsFDDA", {kTH2F, {{axisMultFDDA, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta10VsFDDC", histos.add((histPath + "hNTracksPVeta10VsFDDC").c_str(), "hNTracksPVeta10VsFDDC", {kTH2F, {{axisMultFDDC, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta10VsNMFTTracks", histos.add((histPath + "hNTracksPVeta10VsNMFTTracks").c_str(), "hNTracksPVeta10VsNMFTTracks", {kTH2F, {{axisMultMFTTracks, axisMultPVContributors}}})}); + histPointers.insert({histPath + "hNTracksPVeta10VsNTPV", histos.add((histPath + "hNTracksPVeta10VsNTPV").c_str(), "hNTracksPVeta10VsNTPV", {kTH2F, {{axisMultPVContributors, axisMultPVContributors}}})}); + } + + if (studies.doTimeStudies) { histPointers.insert({histPath + "hFT0AVsTime", histos.add((histPath + "hFT0AVsTime").c_str(), "hFT0AVsTime", {kTH2F, {{axisDeltaTimestamp, axisMultFT0A}}})}); histPointers.insert({histPath + "hFT0CVsTime", histos.add((histPath + "hFT0CVsTime").c_str(), "hFT0CVsTime", {kTH2F, {{axisDeltaTimestamp, axisMultFT0C}}})}); histPointers.insert({histPath + "hFT0MVsTime", histos.add((histPath + "hFT0MVsTime").c_str(), "hFT0MVsTime", {kTH2F, {{axisDeltaTimestamp, axisMultFT0M}}})}); @@ -460,8 +677,51 @@ struct centralityStudy { histPointers.insert({histPath + "hNTPVContributorsVsTime", histos.add((histPath + "hNTPVContributorsVsTime").c_str(), "hNTPVContributorsVsTime", {kTH2F, {{axisDeltaTimestamp, axisMultPVContributors}}})}); histPointers.insert({histPath + "hPVzProfileCoVsTime", histos.add((histPath + "hPVzProfileCoVsTime").c_str(), "hPVzProfileCoVsTime", {kTProfile, {{axisDeltaTimestamp}}})}); histPointers.insert({histPath + "hPVzProfileBcVsTime", histos.add((histPath + "hPVzProfileBcVsTime").c_str(), "hPVzProfileBcVsTime", {kTProfile, {{axisDeltaTimestamp}}})}); - if (irDoRateVsTime) { - histPointers.insert({histPath + "hIRProfileVsTime", histos.add((histPath + "hIRProfileVsTime").c_str(), "hIRProfileVsTime", {kTProfile, {{axisDeltaTimestamp}}})}); + histPointers.insert({histPath + "hIRProfileVsTime", histos.add((histPath + "hIRProfileVsTime").c_str(), "hIRProfileVsTime", {kTProfile, {{axisDeltaTimestamp}}})}); + } + } + + template + void configureCentrality(const TCollision& collision) + { + if (ccdbSettings.fetchCentralityCalibration) { + auto getCent = [](TH1* hist, float mult) -> float { + static constexpr float CentralityNotFound = 105.f; + return hist ? hist->GetBinContent(hist->FindBin(mult)) : CentralityNotFound; + }; + + centFV0A = getCent(hCentralityFV0A, collision.multFV0A()); + centFT0A = getCent(hCentralityFT0A, collision.multFT0A()); + centFT0C = getCent(hCentralityFT0C, collision.multFT0C()); + centFT0M = getCent(hCentralityFT0M, collision.multFT0A() + collision.multFT0C()); + centFDDM = getCent(hCentralityFDDM, collision.multFDDA() + collision.multFDDC()); + centNTPV = getCent(hCentralityNTPV, collision.multNTracksPV()); + centNGlo = getCent(hCentralityNGlo, collision.multNTracksGlobal()); + centMFT = getCent(hCentralityMFT, collision.mftNtracks()); + } else if (doprocessCollisionsWithCentrality) { + if constexpr (requires { collision.centFV0A(); }) { + centFV0A = collision.centFV0A(); + } + if constexpr (requires { collision.centFT0A(); }) { + centFT0A = collision.centFT0A(); + } + if constexpr (requires { collision.centFT0C(); }) { + centFT0C = collision.centFT0C(); + } + if constexpr (requires { collision.centFT0M(); }) { + centFT0M = collision.centFT0M(); + } + if constexpr (requires { collision.centFDDM(); }) { + centFDDM = collision.centFDDM(); + } + if constexpr (requires { collision.centNTPV(); }) { + centNTPV = collision.centNTPV(); + } + if constexpr (requires { collision.centNGlobal(); }) { + centNGlo = collision.centNGlobal(); + } + if constexpr (requires { collision.centMFT(); }) { + centMFT = collision.centMFT(); } } } @@ -471,13 +731,15 @@ struct centralityStudy { // process this collisions { initRun(collision); - histos.fill(HIST("hCollisionSelection"), 0); // all collisions - getHist(TH1, histPath + "hCollisionSelection")->Fill(0); + configureCentrality(collision); - if (applySel8 && !collision.multSel8()) + fillHist(HIST("hCollisionSelection"), 0); // all collisions + + if (evsel.applySel8 && !collision.multSel8()) { return; - histos.fill(HIST("hCollisionSelection"), 1); - getHist(TH1, histPath + "hCollisionSelection")->Fill(1); + } + + fillHist(HIST("hCollisionSelection"), 1); // calculate vertex-Z-equalized quantities if desired float multFV0A = collision.multFV0A(); @@ -486,6 +748,10 @@ struct centralityStudy { float multNTracksGlobal = collision.multNTracksGlobal(); float mftNtracks = collision.mftNtracks(); float multNTracksPV = collision.multNTracksPV(); + float multNTracksPV05 = collision.multNTracksPVetaHalf(); + float multNTracksPV10 = collision.multNTracksPVeta1(); + float multFDDA = collision.multFDDA(); + float multFDDC = collision.multFDDC(); if (applyVertexZEqualization) { float epsilon = 1e-2; // average value after which this collision will be disregarded multFV0A = -1.0f; @@ -494,248 +760,279 @@ struct centralityStudy { multNTracksGlobal = -1.0f; mftNtracks = -1.0f; multNTracksPV = -1.0f; - - if (hVtxZFV0A->Interpolate(collision.multPVz()) > epsilon) { - multFV0A = hVtxZFV0A->Interpolate(0.0) * collision.multFV0A() / hVtxZFV0A->Interpolate(collision.multPVz()); - } - if (hVtxZFT0A->Interpolate(collision.multPVz()) > epsilon) { - multFT0A = hVtxZFT0A->Interpolate(0.0) * collision.multFT0A() / hVtxZFT0A->Interpolate(collision.multPVz()); - } - if (hVtxZFT0C->Interpolate(collision.multPVz()) > epsilon) { - multFT0C = hVtxZFT0C->Interpolate(0.0) * collision.multFT0C() / hVtxZFT0C->Interpolate(collision.multPVz()); - } - if (hVtxZNGlobals->Interpolate(collision.multPVz()) > epsilon) { - multNTracksGlobal = hVtxZNGlobals->Interpolate(0.0) * collision.multNTracksGlobal() / hVtxZNGlobals->Interpolate(collision.multPVz()); - } - if (hVtxZMFT->Interpolate(collision.multPVz()) > epsilon) { - mftNtracks = hVtxZMFT->Interpolate(0.0) * collision.mftNtracks() / hVtxZMFT->Interpolate(collision.multPVz()); - } - if (hVtxZNTracks->Interpolate(collision.multPVz()) > epsilon) { - multNTracksPV = hVtxZNTracks->Interpolate(0.0) * collision.multNTracksPV() / hVtxZNTracks->Interpolate(collision.multPVz()); + if (std::abs(collision.multPVz()) < evsel.maxVtxZ) { + if (hVtxZFV0A && hVtxZFV0A->Interpolate(collision.multPVz()) > epsilon) { + multFV0A = hVtxZFV0A->Interpolate(0.0) * collision.multFV0A() / hVtxZFV0A->Interpolate(collision.multPVz()); + } + if (hVtxZFT0A && hVtxZFT0A->Interpolate(collision.multPVz()) > epsilon) { + multFT0A = hVtxZFT0A->Interpolate(0.0) * collision.multFT0A() / hVtxZFT0A->Interpolate(collision.multPVz()); + } + if (hVtxZFT0C && hVtxZFT0C->Interpolate(collision.multPVz()) > epsilon) { + multFT0C = hVtxZFT0C->Interpolate(0.0) * collision.multFT0C() / hVtxZFT0C->Interpolate(collision.multPVz()); + } + if (hVtxZNGlobals && hVtxZNGlobals->Interpolate(collision.multPVz()) > epsilon) { + multNTracksGlobal = hVtxZNGlobals->Interpolate(0.0) * collision.multNTracksGlobal() / hVtxZNGlobals->Interpolate(collision.multPVz()); + } + if (hVtxZMFT && hVtxZMFT->Interpolate(collision.multPVz()) > epsilon) { + mftNtracks = hVtxZMFT->Interpolate(0.0) * collision.mftNtracks() / hVtxZMFT->Interpolate(collision.multPVz()); + } + if (hVtxZNTracks && hVtxZNTracks->Interpolate(collision.multPVz()) > epsilon) { + multNTracksPV = hVtxZNTracks->Interpolate(0.0) * collision.multNTracksPV() / hVtxZNTracks->Interpolate(collision.multPVz()); + } } } - bool passRejectITSROFBorder = !(rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)); - bool passRejectTFBorder = !(rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)); - bool passRequireIsVertexITSTPC = !(requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)); - bool passRequireIsGoodZvtxFT0VsPV = !(requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)); - bool passRequireIsVertexTOFmatched = !(requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)); - bool passRequireIsVertexTRDmatched = !(requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)); - bool passRejectSameBunchPileup = !(rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)); - bool passRejectITSinROFpileupStandard = !(rejectITSinROFpileupStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)); - bool passRejectITSinROFpileupStrict = !(rejectITSinROFpileupStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)); - bool passSelectUPCcollisions = !(selectUPCcollisions && collision.flags() < 1); - bool passRejectCollInTimeRangeNarrow = !(rejectCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)); + bool passRejectITSROFBorder = !(evsel.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)); + bool passRejectTFBorder = !(evsel.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)); + bool passRequireIsVertexITSTPC = !(evsel.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)); + bool passRequireIsGoodZvtxFT0VsPV = !(evsel.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)); + bool passRequireIsVertexTOFmatched = !(evsel.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)); + bool passRequireIsVertexTRDmatched = !(evsel.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)); + bool passRejectSameBunchPileup = !(evsel.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)); + bool passRejectITSinROFpileupStandard = !(evsel.rejectITSinROFpileupStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)); + bool passRejectITSinROFpileupStrict = !(evsel.rejectITSinROFpileupStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)); + bool passSelectUPCcollisions = !(evsel.selectUPCcollisions && collision.flags() < 1); + bool passRejectCollInTimeRangeNarrow = !(evsel.rejectCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)); + bool passINELgtZERO = !(evsel.requireINELgtZERO && !collision.isInelGt0()); + // _______________________________________________________ // sidestep vertex-Z rejection for vertex-Z profile histograms - if (passRejectITSROFBorder && passRejectTFBorder && passRequireIsVertexITSTPC && passRequireIsGoodZvtxFT0VsPV && - passRequireIsVertexTOFmatched && passRequireIsVertexTRDmatched && passRejectSameBunchPileup && passRejectITSinROFpileupStandard && passRejectITSinROFpileupStrict && - passSelectUPCcollisions && passRejectCollInTimeRangeNarrow) { - getHist(TProfile, histPath + "hFT0CvsPVz_Collisions_All")->Fill(collision.multPVz(), multFT0C * scaleSignalFT0C); - getHist(TProfile, histPath + "hFT0CvsPVz_Collisions")->Fill(collision.multPVz(), multFT0C * scaleSignalFT0C); - getHist(TProfile, histPath + "hFT0AvsPVz_Collisions")->Fill(collision.multPVz(), multFT0A * scaleSignalFT0C); - getHist(TProfile, histPath + "hFV0AvsPVz_Collisions")->Fill(collision.multPVz(), multFV0A * scaleSignalFV0A); - getHist(TProfile, histPath + "hNGlobalTracksvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksGlobal); - getHist(TProfile, histPath + "hNMFTTracksvsPVz_Collisions")->Fill(collision.multPVz(), mftNtracks); - getHist(TProfile, histPath + "hNTPVvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksPV); + if (studies.doRunByRunHistograms) { + if (passRejectITSROFBorder && passRejectTFBorder && passRequireIsVertexITSTPC && passRequireIsGoodZvtxFT0VsPV && + passRequireIsVertexTOFmatched && passRequireIsVertexTRDmatched && passRejectSameBunchPileup && passRejectITSinROFpileupStandard && passRejectITSinROFpileupStrict && + passSelectUPCcollisions && passRejectCollInTimeRangeNarrow && passINELgtZERO) { + getHist(histPath + "hFT0CvsPVz_Collisions")->Fill(collision.multPVz(), multFT0C * scale.factorFT0C); + getHist(histPath + "hFT0AvsPVz_Collisions")->Fill(collision.multPVz(), multFT0A * scale.factorFT0A); + getHist(histPath + "hFV0AvsPVz_Collisions")->Fill(collision.multPVz(), multFV0A * scale.factorFV0A); + getHist(histPath + "hNGlobalTracksvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksGlobal); + getHist(histPath + "hNMFTTracksvsPVz_Collisions")->Fill(collision.multPVz(), mftNtracks); + getHist(histPath + "hNTPVvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksPV); + } } // _______________________________________________________ - - if (applyVtxZ && TMath::Abs(collision.multPVz()) > 10) + if (evsel.applyVtxZ && std::abs(collision.multPVz()) > evsel.maxVtxZ) { return; - histos.fill(HIST("hCollisionSelection"), 2); - getHist(TH1, histPath + "hCollisionSelection")->Fill(2); + } + + fillHist(HIST("hCollisionSelection"), 2); // _______________________________________________________ // Extra event selections start here if (!passRejectITSROFBorder) { return; } - histos.fill(HIST("hCollisionSelection"), 3 /* Not at ITS ROF border */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(3); + + fillHist(HIST("hCollisionSelection"), 3 /* Not at ITS ROF border */); if (!passRejectTFBorder) { return; } - histos.fill(HIST("hCollisionSelection"), 4 /* Not at TF border */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(4); - + fillHist(HIST("hCollisionSelection"), 4 /* Not at TF border */); if (!passRequireIsVertexITSTPC) { return; } - histos.fill(HIST("hCollisionSelection"), 5 /* Contains at least one ITS-TPC track */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(5); - + fillHist(HIST("hCollisionSelection"), 5 /* Contains at least one ITS-TPC track */); if (!passRequireIsGoodZvtxFT0VsPV) { return; } - histos.fill(HIST("hCollisionSelection"), 6 /* PV position consistency check */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(6); - + fillHist(HIST("hCollisionSelection"), 6 /* PV position consistency check */); if (!passRequireIsVertexTOFmatched) { return; } - histos.fill(HIST("hCollisionSelection"), 7 /* PV with at least one contributor matched with TOF */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(7); - + fillHist(HIST("hCollisionSelection"), 7 /* PV with at least one contributor matched with TOF */); if (!passRequireIsVertexTRDmatched) { return; } - histos.fill(HIST("hCollisionSelection"), 8 /* PV with at least one contributor matched with TRD */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(8); - + fillHist(HIST("hCollisionSelection"), 8 /* PV with at least one contributor matched with TRD */); if (!passRejectSameBunchPileup) { return; } - histos.fill(HIST("hCollisionSelection"), 9 /* Not at same bunch pile-up */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(9); - + fillHist(HIST("hCollisionSelection"), 9 /* Not at same bunch pile-up */); // do this only if information is available if constexpr (requires { collision.timeToNext(); }) { - float timeToNeighbour = TMath::Min( + float timeToNeighbour = std::min( std::abs(collision.timeToNext()), std::abs(collision.timeToPrevious())); - histos.fill(HIST("hDeltaTimeVsCentrality"), collision.centFT0C(), timeToNeighbour); + histos.fill(HIST("hDeltaTimeVsCentrality"), centFT0C, timeToNeighbour); if (timeToNeighbour < minTimeDelta) { return; } - histos.fill(HIST("hCollisionSelection"), 10 /* has suspicious neighbour */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(10); } + fillHist(HIST("hCollisionSelection"), 10 /* has suspicious neighbour */); if (!passRejectITSinROFpileupStandard) { return; } - histos.fill(HIST("hCollisionSelection"), 11 /* Not ITS ROF pileup (standard) */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(11); - + fillHist(HIST("hCollisionSelection"), 11 /* Not ITS ROF pileup (standard) */); if (!passRejectITSinROFpileupStrict) { return; } - histos.fill(HIST("hCollisionSelection"), 12 /* Not ITS ROF pileup (strict) */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(12); - + fillHist(HIST("hCollisionSelection"), 12 /* Not ITS ROF pileup (strict) */); if (!passSelectUPCcollisions) { // if zero then NOT upc, otherwise UPC return; } - histos.fill(HIST("hCollisionSelection"), 13 /* is UPC event */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(13); - + fillHist(HIST("hCollisionSelection"), 13 /* is UPC event */); if (!passRejectCollInTimeRangeNarrow) { return; } - histos.fill(HIST("hCollisionSelection"), 14 /* Reject collision in narrow time range */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(14); + fillHist(HIST("hCollisionSelection"), 14 /* Reject collision in narrow time range */); - if (collision.multFT0C() < upcRejection.maxFT0CforZNACselection && - collision.multZNA() < upcRejection.minZNACsignal && - collision.multZNC() < upcRejection.minZNACsignal) { - return; + if (evsel.rejectUpc) { + if (collision.multFT0C() < cfgFwd.maxFT0CforZNACselection && + collision.multZNA() < cfgFwd.minZNACsignal && + collision.multZNC() < cfgFwd.minZNACsignal) { + return; + } + if (collision.multFT0C() < cfgFwd.maxFT0CforFV0Aselection && + collision.multFV0A() < cfgFwd.minFV0Asignal) { + return; + } + if (collision.multFT0C() < cfgFwd.maxFT0CforFDDAselection && + collision.multFDDA() < cfgFwd.minFDDAsignal) { + return; + } } - if (collision.multFT0C() < upcRejection.maxFT0CforFV0Aselection && - collision.multFV0A() < upcRejection.minFV0Asignal) { - return; + fillHist(HIST("hCollisionSelection"), 15 /* pass em/upc rejection */); + if (evsel.rejectIsFlangeEvent) { + if constexpr (requires { collision.has_multBC(); }) { + if (collision.has_multBC()) { + auto multbc = collision.template multBC_as>(); + constexpr int IsFlangeEventId = 7; + std::bitset<8> ft0TriggerMask = multbc.multT0triggerBits(); + if (ft0TriggerMask[IsFlangeEventId]) { + return; + } + } + } } - if (collision.multFT0C() < upcRejection.maxFT0CforFDDAselection && - collision.multFDDA() < upcRejection.minFDDAsignal) { + + fillHist(HIST("hCollisionSelection"), 16 /* reject flange events */); + + if (!passINELgtZERO) { return; } - histos.fill(HIST("hCollisionSelection"), 15 /* pass em/upc rejection */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(15); - - if (rejectIsFlangeEvent) { - if constexpr (requires { collision.ft0TriggerMask(); }) { - constexpr int IsFlangeEventId = 7; - std::bitset<8> ft0TriggerMask = collision.ft0TriggerMask(); - if (ft0TriggerMask[IsFlangeEventId]) { - return; + fillHist(HIST("hCollisionSelection"), 17 /* is INEL > 0 */); + + if (evsel.applyBcSel) { + if constexpr (requires { collision.has_multBC(); }) { + if (collision.has_multBC()) { + auto multbc = collision.template multBC_as>(); + histos.fill(HIST("hCollToBcQa"), 0 /* found */); + if (!selectedBC(multbc)) { + return; + } + } else { + histos.fill(HIST("hCollToBcQa"), 1 /* not found */); + if (evsel.rejectNoFoundBC) { + histos.fill(HIST("hCollToBcQa"), 2 /* rejected */); + return; + } } } } - histos.fill(HIST("hCollisionSelection"), 16 /* reject flange events */); - getHist(TH1, histPath + "hCollisionSelection")->Fill(16); + + fillHist(HIST("hCollisionSelection"), 18 /* bc selections */); // if we got here, we also finally fill the FT0C histogram, please histos.fill(HIST("hNPVContributors"), collision.multNTracksPV()); - histos.fill(HIST("hFT0A_Collisions"), collision.multFT0A() * scaleSignalFT0C); - histos.fill(HIST("hFT0C_Collisions"), collision.multFT0C() * scaleSignalFT0C); - histos.fill(HIST("hFT0M_Collisions"), (collision.multFT0A() + collision.multFT0C()) * scaleSignalFT0M); - histos.fill(HIST("hFV0A_Collisions"), collision.multFV0A() * scaleSignalFV0A); + histos.fill(HIST("hFT0A_Collisions"), collision.multFT0A() * scale.factorFT0A); + histos.fill(HIST("hFT0C_Collisions"), collision.multFT0C() * scale.factorFT0C); + histos.fill(HIST("hFT0M_Collisions"), (collision.multFT0A() + collision.multFT0C()) * scale.factorFT0M); + histos.fill(HIST("hFV0A_Collisions"), collision.multFV0A() * scale.factorFV0A); histos.fill(HIST("hNGlobalTracks"), collision.multNTracksGlobal()); histos.fill(HIST("hNMFTTracks"), collision.mftNtracks()); - histos.fill(HIST("hFT0CvsPVz_Collisions_All"), collision.multPVz(), collision.multFT0C() * scaleSignalFT0C); - histos.fill(HIST("hFV0AvsPVz_Collisions"), collision.multPVz(), collision.multFV0A() * scaleSignalFV0A); + histos.fill(HIST("hFT0CvsPVz_Collisions"), collision.multPVz(), collision.multFT0C() * scale.factorFT0C); + histos.fill(HIST("hFT0AvsPVz_Collisions"), collision.multPVz(), collision.multFT0A() * scale.factorFT0A); + histos.fill(HIST("hFV0AvsPVz_Collisions"), collision.multPVz(), collision.multFV0A() * scale.factorFV0A); histos.fill(HIST("hNGlobalTracksvsPVz_Collisions"), collision.multPVz(), collision.multNTracksGlobal()); histos.fill(HIST("hNMFTTracksvsPVz_Collisions"), collision.multPVz(), collision.mftNtracks()); // save vertex-Z equalized - getHist(TH1, histPath + "hNPVContributors")->Fill(multNTracksPV); - getHist(TH1, histPath + "hFT0A_Collisions")->Fill(multFT0A * scaleSignalFT0A); - getHist(TH1, histPath + "hFT0C_Collisions")->Fill(multFT0C * scaleSignalFT0C); - getHist(TH1, histPath + "hFT0M_Collisions")->Fill((multFT0A + multFT0C) * scaleSignalFT0M); - getHist(TH1, histPath + "hFV0A_Collisions")->Fill(multFV0A * scaleSignalFV0A); - getHist(TH1, histPath + "hNGlobalTracks")->Fill(multNTracksGlobal); - getHist(TH1, histPath + "hNMFTTracks")->Fill(mftNtracks); - - if (applyVertexZEqualization.value) { - // save unequalized for cross-checks - getHist(TH1, histPath + "hNPVContributors_Unequalized")->Fill(collision.multNTracksPV()); - getHist(TH1, histPath + "hFT0C_Collisions_Unequalized")->Fill(collision.multFT0C() * scaleSignalFT0C); - getHist(TH1, histPath + "hFT0M_Collisions_Unequalized")->Fill((collision.multFT0A() + collision.multFT0C()) * scaleSignalFT0M); - getHist(TH1, histPath + "hFV0A_Collisions_Unequalized")->Fill(collision.multFV0A() * scaleSignalFV0A); - getHist(TH1, histPath + "hNGlobalTracks_Unequalized")->Fill(collision.multNTracksGlobal()); - getHist(TH1, histPath + "hNMFTTracks_Unequalized")->Fill(collision.mftNtracks()); - } - - if (do2DPlots) { - histos.fill(HIST("hNContribsVsFT0C"), collision.multFT0C() * scaleSignalFT0C, collision.multPVTotalContributors()); - histos.fill(HIST("hNContribsVsFV0A"), collision.multFV0A() * scaleSignalFV0A, collision.multPVTotalContributors()); - histos.fill(HIST("hMatchedVsITSOnly"), collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); - getHist(TH2, histPath + "hNContribsVsFT0C")->Fill(collision.multFT0C() * scaleSignalFT0C, collision.multPVTotalContributors()); - getHist(TH2, histPath + "hNContribsVsFV0A")->Fill(collision.multFV0A() * scaleSignalFV0A, collision.multPVTotalContributors()); - getHist(TH2, histPath + "hMatchedVsITSOnly")->Fill(collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); + if (studies.doRunByRunHistograms) { + getHist(histPath + "hNPVContributors")->Fill(multNTracksPV); + getHist(histPath + "hFT0A_Collisions")->Fill(multFT0A * scale.factorFT0A); + getHist(histPath + "hFT0C_Collisions")->Fill(multFT0C * scale.factorFT0C); + getHist(histPath + "hFT0M_Collisions")->Fill((multFT0A + multFT0C) * scale.factorFT0M); + getHist(histPath + "hFV0A_Collisions")->Fill(multFV0A * scale.factorFV0A); + getHist(histPath + "hNGlobalTracks")->Fill(multNTracksGlobal); + getHist(histPath + "hNMFTTracks")->Fill(mftNtracks); + if (applyVertexZEqualization.value) { + // save unequalized for cross-checks + getHist(histPath + "hNPVContributors_Unequalized")->Fill(collision.multNTracksPV()); + getHist(histPath + "hFT0C_Collisions_Unequalized")->Fill(collision.multFT0C() * scale.factorFT0C); + getHist(histPath + "hFT0M_Collisions_Unequalized")->Fill((collision.multFT0A() + collision.multFT0C()) * scale.factorFT0M); + getHist(histPath + "hFV0A_Collisions_Unequalized")->Fill(collision.multFV0A() * scale.factorFV0A); + getHist(histPath + "hNGlobalTracks_Unequalized")->Fill(collision.multNTracksGlobal()); + getHist(histPath + "hNMFTTracks_Unequalized")->Fill(collision.mftNtracks()); + } + } + if (studies.do2DPlots) { + fillHist(HIST("hNContribsVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multPVTotalContributors()); + fillHist(HIST("hNContribsVsFV0A"), collision.multFV0A() * scale.factorFV0A, collision.multPVTotalContributors()); + fillHist(HIST("hMatchedVsITSOnly"), collision.multNTracksITSOnly(), collision.multNTracksITSTPC()); // correlate also FIT detector signals - histos.fill(HIST("hFT0AVsFT0C"), collision.multFT0C() * scaleSignalFT0C, collision.multFT0A()); - histos.fill(HIST("hFV0AVsFT0C"), collision.multFT0C() * scaleSignalFT0C, collision.multFV0A()); - histos.fill(HIST("hFDDAVsFT0C"), collision.multFT0C() * scaleSignalFT0C, collision.multFDDA()); - histos.fill(HIST("hFDDCVsFT0C"), collision.multFT0C() * scaleSignalFT0C, collision.multFDDC()); - getHist(TH2, histPath + "hFT0AVsFT0C")->Fill(collision.multFT0C() * scaleSignalFT0C, collision.multFT0A()); - getHist(TH2, histPath + "hFV0AVsFT0C")->Fill(collision.multFT0C() * scaleSignalFT0C, collision.multFV0A()); - getHist(TH2, histPath + "hFDDAVsFT0C")->Fill(collision.multFT0C() * scaleSignalFT0C, collision.multFDDA()); - getHist(TH2, histPath + "hFDDCVsFT0C")->Fill(collision.multFT0C() * scaleSignalFT0C, collision.multFDDC()); + fillHist(HIST("hFT0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFT0A()); + fillHist(HIST("hFV0AVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFV0A()); + fillHist(HIST("hFDDAVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDA()); + fillHist(HIST("hFDDCVsFT0C"), collision.multFT0C() * scale.factorFT0C, collision.multFDDC()); } - if (doOccupancyStudyVsCentrality2d) { + if (studies.doOccupancyStudyVsCentrality2d) { histos.fill(HIST("hNcontribsProfileVsTrackOccupancyVsFT0C"), collision.trackOccupancyInTimeRange(), collision.multFT0C(), collision.multPVTotalContributors()); histos.fill(HIST("hNGlobalTracksProfileVsTrackOccupancyVsFT0C"), collision.trackOccupancyInTimeRange(), collision.multFT0C(), collision.multNTracksGlobal()); histos.fill(HIST("hNcontribsProfileVsFT0COccupancyVsFT0C"), collision.ft0cOccupancyInTimeRange(), collision.multFT0C(), collision.multPVTotalContributors()); histos.fill(HIST("hNGlobalTracksProfileVsFT0COccupancyVsFT0C"), collision.ft0cOccupancyInTimeRange(), collision.multFT0C(), collision.multNTracksGlobal()); } - if (doOccupancyStudyVsRawValues3d) { + if (studies.doOccupancyStudyVsRawValues3d) { histos.fill(HIST("hTrackOccupancyVsNContribsVsFT0C"), collision.trackOccupancyInTimeRange(), collision.multPVTotalContributors(), collision.multFT0C()); histos.fill(HIST("hTrackOccupancyVsNGlobalTracksVsFT0C"), collision.trackOccupancyInTimeRange(), collision.multNTracksGlobal(), collision.multFT0C()); histos.fill(HIST("hFT0COccupancyVsNContribsVsFT0C"), collision.ft0cOccupancyInTimeRange(), collision.multPVTotalContributors(), collision.multFT0C()); histos.fill(HIST("hFT0COccupancyVsNGlobalTracksVsFT0C"), collision.ft0cOccupancyInTimeRange(), collision.multNTracksGlobal(), collision.multFT0C()); } - if (doNGlobalTracksVsRawSignals) { - histos.fill(HIST("hNGlobalTracksVsFT0A"), multFT0A, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFT0C"), multFT0C, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFT0M"), (multFT0A + multFT0C), multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsFV0A"), multFV0A, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsNMFTTracks"), mftNtracks, multNTracksGlobal); - histos.fill(HIST("hNGlobalTracksVsNTPV"), multNTracksPV, multNTracksGlobal); + if (studies.doNGlobalTracksVsRawSignals) { + fillHist(HIST("hNGlobalTracksVsFT0A"), multFT0A, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFT0C"), multFT0C, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFT0M"), (multFT0A + multFT0C), multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsFV0A"), multFV0A, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsNMFTTracks"), mftNtracks, multNTracksGlobal); + fillHist(HIST("hNGlobalTracksVsNTPV"), multNTracksPV, multNTracksGlobal); + } + + if (studies.doNTracksPVeta05VsRawSignals) { + fillHist(HIST("hNTracksPVeta05VsFT0A"), multFT0A, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFT0C"), multFT0C, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFT0M"), (multFT0A + multFT0C), multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFV0A"), multFV0A, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFDDA"), multFDDA, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsFDDC"), multFDDC, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsNMFTTracks"), mftNtracks, multNTracksPV05); + fillHist(HIST("hNTracksPVeta05VsNTPV"), multNTracksPV, multNTracksPV05); + } - // per run - getHist(TH2, histPath + "hNGlobalTracksVsFT0A")->Fill(multFT0A, multNTracksGlobal); - getHist(TH2, histPath + "hNGlobalTracksVsFT0C")->Fill(multFT0C, multNTracksGlobal); - getHist(TH2, histPath + "hNGlobalTracksVsFT0M")->Fill(multFT0A + multFT0C, multNTracksGlobal); - getHist(TH2, histPath + "hNGlobalTracksVsFV0A")->Fill(multFV0A, multNTracksGlobal); - getHist(TH2, histPath + "hNGlobalTracksVsNMFTTracks")->Fill(mftNtracks, multNTracksGlobal); - getHist(TH2, histPath + "hNGlobalTracksVsNTPV")->Fill(multNTracksPV, multNTracksGlobal); + if (studies.doNTracksPVeta08VsRawSignals) { + fillHist(HIST("hNTracksPVeta08VsFT0A"), multFT0A, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFT0C"), multFT0C, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFT0M"), (multFT0A + multFT0C), multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFV0A"), multFV0A, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFDDA"), multFDDA, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsFDDC"), multFDDC, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsNMFTTracks"), mftNtracks, multNTracksPV); + fillHist(HIST("hNTracksPVeta08VsNTPV"), multNTracksPV, multNTracksPV); + } + + if (studies.doNTracksPVeta10VsRawSignals) { + fillHist(HIST("hNTracksPVeta10VsFT0A"), multFT0A, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFT0C"), multFT0C, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFT0M"), (multFT0A + multFT0C), multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFV0A"), multFV0A, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFDDA"), multFDDA, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsFDDC"), multFDDC, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsNMFTTracks"), mftNtracks, multNTracksPV10); + fillHist(HIST("hNTracksPVeta10VsNTPV"), multNTracksPV, multNTracksPV10); } if constexpr (requires { collision.multMCExtraId(); }) { @@ -757,165 +1054,273 @@ struct centralityStudy { } // if the table has centrality information - if constexpr (requires { collision.centFT0C(); }) { - // process FT0C centrality plots - histos.fill(HIST("hCentrality"), collision.centFT0C()); - histos.fill(HIST("hNContribsVsCentrality"), collision.centFT0C(), collision.multPVTotalContributors()); - histos.fill(HIST("hNITSTPCTracksVsCentrality"), collision.centFT0C(), collision.multNTracksITSTPC()); - histos.fill(HIST("hNITSOnlyTracksVsCentrality"), collision.centFT0C(), collision.multNTracksITSOnly()); - histos.fill(HIST("hNGlobalTracksVsCentrality"), collision.centFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("hNMFTTracksVsCentrality"), collision.centFT0C(), collision.mftNtracks()); - histos.fill(HIST("hPVChi2VsCentrality"), collision.centFT0C(), collision.multPVChi2()); - getHist(TH1, histPath + "hCentrality")->Fill(collision.centFT0C()); - getHist(TH2, histPath + "hNContribsVsCentrality")->Fill(collision.centFT0C(), collision.multPVTotalContributors()); - getHist(TH2, histPath + "hNITSTPCTracksVsCentrality")->Fill(collision.centFT0C(), collision.multNTracksITSTPC()); - getHist(TH2, histPath + "hNITSOnlyTracksVsCentrality")->Fill(collision.centFT0C(), collision.multNTracksITSOnly()); - getHist(TH2, histPath + "hNGlobalTracksVsCentrality")->Fill(collision.centFT0C(), collision.multNTracksGlobal()); - getHist(TH2, histPath + "hNMFTTracksVsCentrality")->Fill(collision.centFT0C(), collision.mftNtracks()); - getHist(TH2, histPath + "hPVChi2VsCentrality")->Fill(collision.centFT0C(), collision.multPVChi2()); - - if (doOccupancyStudyVsCentrality2d) { - histos.fill(HIST("hNcontribsProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), collision.centFT0C(), collision.multPVTotalContributors()); - histos.fill(HIST("hNGlobalTracksProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), collision.centFT0C(), collision.multNTracksGlobal()); - histos.fill(HIST("hNcontribsProfileVsFT0COccupancyVsCentrality"), collision.ft0cOccupancyInTimeRange(), collision.centFT0C(), collision.multPVTotalContributors()); - histos.fill(HIST("hNGlobalTracksProfileVsFT0COccupancyVsCentrality"), collision.ft0cOccupancyInTimeRange(), collision.centFT0C(), collision.multNTracksGlobal()); - } - - if (doOccupancyStudyVsCentrality3d) { - histos.fill(HIST("hTrackOccupancyVsNContribsVsCentrality"), collision.trackOccupancyInTimeRange(), collision.multPVTotalContributors(), collision.centFT0C()); - histos.fill(HIST("hTrackOccupancyVsNGlobalTracksVsCentrality"), collision.trackOccupancyInTimeRange(), collision.multNTracksGlobal(), collision.centFT0C()); - histos.fill(HIST("hFT0COccupancyVsNContribsVsCentrality"), collision.ft0cOccupancyInTimeRange(), collision.multPVTotalContributors(), collision.centFT0C()); - histos.fill(HIST("hFT0COccupancyVsNGlobalTracksVsCentrality"), collision.ft0cOccupancyInTimeRange(), collision.multNTracksGlobal(), collision.centFT0C()); + // process FT0C centrality plots + if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { + fillHist(HIST("hCentrality"), centFT0C); + fillHist(HIST("hNContribsVsCentrality"), centFT0C, collision.multPVTotalContributors()); + fillHist(HIST("hNITSTPCTracksVsCentrality"), centFT0C, collision.multNTracksITSTPC()); + fillHist(HIST("hNITSOnlyTracksVsCentrality"), centFT0C, collision.multNTracksITSOnly()); + fillHist(HIST("hNGlobalTracksVsCentrality"), centFT0C, collision.multNTracksGlobal()); + fillHist(HIST("hNMFTTracksVsCentrality"), centFT0C, collision.mftNtracks()); + fillHist(HIST("hPVChi2VsCentrality"), centFT0C, collision.multPVChi2()); + if (studies.doOccupancyStudyVsCentrality2d) { + histos.fill(HIST("hNcontribsProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), centFT0C, collision.multPVTotalContributors()); + histos.fill(HIST("hNGlobalTracksProfileVsTrackOccupancyVsCentrality"), collision.trackOccupancyInTimeRange(), centFT0C, collision.multNTracksGlobal()); + histos.fill(HIST("hNcontribsProfileVsFT0COccupancyVsCentrality"), collision.ft0cOccupancyInTimeRange(), centFT0C, collision.multPVTotalContributors()); + histos.fill(HIST("hNGlobalTracksProfileVsFT0COccupancyVsCentrality"), collision.ft0cOccupancyInTimeRange(), centFT0C, collision.multNTracksGlobal()); + } + + if (studies.doOccupancyStudyVsCentrality3d) { + histos.fill(HIST("hTrackOccupancyVsNContribsVsCentrality"), collision.trackOccupancyInTimeRange(), collision.multPVTotalContributors(), centFT0C); + histos.fill(HIST("hTrackOccupancyVsNGlobalTracksVsCentrality"), collision.trackOccupancyInTimeRange(), collision.multNTracksGlobal(), centFT0C); + histos.fill(HIST("hFT0COccupancyVsNContribsVsCentrality"), collision.ft0cOccupancyInTimeRange(), collision.multPVTotalContributors(), centFT0C); + histos.fill(HIST("hFT0COccupancyVsNGlobalTracksVsCentrality"), collision.ft0cOccupancyInTimeRange(), collision.multNTracksGlobal(), centFT0C); } } if constexpr (requires { collision.has_multBC(); }) { - if (doTimeStudies && collision.has_multBC()) { - initRun(collision); - auto multbc = collision.template multBC_as(); - uint64_t bcTimestamp = multbc.timestamp(); - float hoursAfterStartOfRun = static_cast(bcTimestamp - startOfRunTimestamp) / 3600000.0; - - getHist(TH2, histPath + "hFT0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0A()); - getHist(TH2, histPath + "hFT0CVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0C()); - getHist(TH2, histPath + "hFT0MVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0M()); - getHist(TH2, histPath + "hFV0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFV0A()); - getHist(TH2, histPath + "hFV0AOuterVsTime")->Fill(hoursAfterStartOfRun, collision.multFV0AOuter()); - getHist(TH2, histPath + "hMFTTracksVsTime")->Fill(hoursAfterStartOfRun, collision.mftNtracks()); - getHist(TH2, histPath + "hNGlobalVsTime")->Fill(hoursAfterStartOfRun, collision.multNTracksGlobal()); - getHist(TH2, histPath + "hNTPVContributorsVsTime")->Fill(hoursAfterStartOfRun, collision.multPVTotalContributors()); - getHist(TProfile, histPath + "hPVzProfileCoVsTime")->Fill(hoursAfterStartOfRun, collision.multPVz()); - getHist(TProfile, histPath + "hPVzProfileBcVsTime")->Fill(hoursAfterStartOfRun, multbc.multFT0PosZ()); - if (doTimeStudyFV0AOuterVsFT0A3d) { - histos.fill(HIST("h3dFV0AVsTime"), hoursAfterStartOfRun, collision.multFV0A(), collision.multFV0AOuter()); + if (collision.has_multBC()) { + auto multbc = collision.template multBC_as>(); + fillHist(HIST("hFV0AOuter_Collisions"), multbc.multFV0AOuter() * scale.factorFV0A); + fillHist(HIST("hFT0AOuter_Collisions"), multbc.multFT0AOuter() * scale.factorFT0A); + fillHist(HIST("hFT0MOuterA_Collisions"), (multbc.multFT0AOuter() + multbc.multFT0C()) * scale.factorFT0M); + + fillHist(HIST("hFT0COuter_Collisions"), multbc.multFT0COuter() * scale.factorFT0C); + fillHist(HIST("hFIT_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); + fillHist(HIST("hFIT_FullV0_Collisions"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); + fillHist(HIST("hFIT_All_Collisions"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); + if (studies.do2DPlots) { + fillHist(HIST("hFT0AOuterVsFT0C"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0AOuter() * scale.factorFT0A); + } + if (studies.doNGlobalTracksVsRawSignals) { + fillHist(HIST("hNGlobalTracksVsFT0MOuterA"), multbc.multFT0AOuter() + multbc.multFT0C(), multNTracksGlobal); } - if (irDoRateVsTime) { - float interactionRate = mRateFetcher.fetch(ccdb.service, bcTimestamp, mRunNumber, irSource.value, irCrashOnNull) / 1000.; // kHz - getHist(TProfile, histPath + "hIRProfileVsTime")->Fill(hoursAfterStartOfRun, interactionRate); + const uint64_t bcTimestamp = multbc.timestamp(); + const float interactionRate = mRateFetcher.fetch(ccdb.service, bcTimestamp, mRunNumber, ccdbSettings.irSource.value, ccdbSettings.irCrashOnNull) / 1000.; // kHz + fillHist(HIST("hInteractionRate"), interactionRate); + if (doprocessCollisionsWithCentrality || ccdbSettings.fetchCentralityCalibration) { + fillHist(HIST("hInteractionRateVsCentrality"), centFT0C, interactionRate); + } + if (studies.doRunByRunHistograms && studies.doTimeStudies) { + const float hoursAfterStartOfRun = static_cast(bcTimestamp - startOfRunTimestamp) / 3600000.0; + getHist(histPath + "hFT0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0A()); + getHist(histPath + "hFT0CVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0C()); + getHist(histPath + "hFT0MVsTime")->Fill(hoursAfterStartOfRun, collision.multFT0M()); + getHist(histPath + "hFV0AVsTime")->Fill(hoursAfterStartOfRun, collision.multFV0A()); + getHist(histPath + "hFV0AOuterVsTime")->Fill(hoursAfterStartOfRun, multbc.multFV0AOuter()); + getHist(histPath + "hMFTTracksVsTime")->Fill(hoursAfterStartOfRun, collision.mftNtracks()); + getHist(histPath + "hNGlobalVsTime")->Fill(hoursAfterStartOfRun, collision.multNTracksGlobal()); + getHist(histPath + "hNTPVContributorsVsTime")->Fill(hoursAfterStartOfRun, collision.multPVTotalContributors()); + getHist(histPath + "hPVzProfileCoVsTime")->Fill(hoursAfterStartOfRun, collision.multPVz()); + getHist(histPath + "hPVzProfileBcVsTime")->Fill(hoursAfterStartOfRun, multbc.multFT0PosZ()); + getHist(histPath + "hIRProfileVsTime")->Fill(hoursAfterStartOfRun, interactionRate); } } } } - void processCollisions(soa::Join::iterator const& collision, aod::MultBCs const&) + void processCollisions(soa::Join::iterator const& collision, soa::Join const&) { genericProcessCollision(collision); } - void processCollisionsWithResolutionStudy(soa::Join::iterator const& collision, soa::Join const&) + void processCollisionsWithResolutionStudy(soa::Join::iterator const& collision, soa::Join const&) { genericProcessCollision(collision); } - void processCollisionsWithCentrality(soa::Join::iterator const& collision, aod::MultBCs const&) + void processCollisionsWithCentrality(soa::Join::iterator const& collision, soa::Join const&) { genericProcessCollision(collision); } - void processCollisionsWithCentralityWithNeighbours(soa::Join::iterator const& collision) + void processCollisionsWithCentralityWithNeighbours(soa::Join::iterator const& collision) { genericProcessCollision(collision); } - void processBCs(soa::Join::iterator const& multbc, soa::Join const&) + template + bool selectedBC(const TBunchCrossing& bc, bool fillHistograms = false) { - // process BCs, calculate FT0C distribution // conditionals suggested by FIT team (Jacek O. et al) - histos.fill(HIST("hBCSelection"), 0); // all BCs + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 0); // all BCs + } - if (selectCollidingBCs && !multbc.multCollidingBC()) - return; - histos.fill(HIST("hBCSelection"), 1); // colliding + if (bcsel.selectCollidingBCs && !bc.multCollidingBC()) { + return false; + } - if (selectTVX && !multbc.multTVX()) - return; - histos.fill(HIST("hBCSelection"), 2); // TVX + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 1); // colliding + } - if (selectFV0OrA && !multbc.multFV0OrA()) - return; - histos.fill(HIST("hBCSelection"), 3); // FV0OrA + if (bcsel.selectTVX && !bc.multTVX()) { + return false; + } - if (vertexZwithT0 < 100.0f) { - if (!multbc.multFT0PosZValid()) - return; - if (TMath::Abs(multbc.multFT0PosZ()) > vertexZwithT0) - return; + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 2); // TVX } - histos.fill(HIST("hBCSelection"), 4); // FT0PosZ - if (multbc.multFT0C() < upcRejection.maxFT0CforZNACselection && - multbc.multZNA() < upcRejection.minZNACsignal && - multbc.multZNC() < upcRejection.minZNACsignal) { - return; + if (bcsel.selectFV0OrA && !bc.multFV0OrA()) { + return false; } - if (multbc.multFT0C() < upcRejection.maxFT0CforFV0Aselection && - multbc.multFV0A() < upcRejection.minFV0Asignal) { - return; + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 3); // FV0OrA } - if (multbc.multFT0C() < upcRejection.maxFT0CforFDDAselection && - multbc.multFDDA() < upcRejection.minFDDAsignal) { - return; + + static constexpr float TooLargeVertexZ = 100.0f; + if (bcsel.vertexZwithT0 < TooLargeVertexZ) { + if (!bc.multFT0PosZValid()) { + return false; + } + if (std::abs(bc.multFT0PosZ()) > bcsel.vertexZwithT0) { + return false; + } } - histos.fill(HIST("hBCSelection"), 5); // znac amp - if (rejectZNAC && !multbc.selection_bit(o2::aod::evsel::kIsBBZNA) && !multbc.selection_bit(o2::aod::evsel::kIsBBZNC)) { - return; + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 4); // FT0PosZ } - histos.fill(HIST("hBCSelection"), 6); // znac time - // if we got here, we also finally fill the FT0C histogram, please - histos.fill(HIST("hFT0C_BCs"), multbc.multFT0C() * scaleSignalFT0C); + if (bcsel.rejectUpc) { + if (bc.multFT0C() < cfgFwd.maxFT0CforZNACselection && + bc.multZNA() < cfgFwd.minZNACsignal && + bc.multZNC() < cfgFwd.minZNACsignal) { + return false; + } + if (bc.multFT0C() < cfgFwd.maxFT0CforFV0Aselection && + bc.multFV0A() < cfgFwd.minFV0Asignal) { + return false; + } + if (bc.multFT0C() < cfgFwd.maxFT0CforFDDAselection && + bc.multFDDA() < cfgFwd.minFDDAsignal) { + return false; + } + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 5); // znac amp + } + + if (bcsel.rejectZNAC && !bc.selection_bit(o2::aod::evsel::kIsBBZNA) && !bc.selection_bit(o2::aod::evsel::kIsBBZNC)) { + return false; + } - // ZN signals - histos.fill(HIST("hZNAvsFT0C_BCs"), multbc.multFT0C() * scaleSignalFT0C, multbc.multZNA()); - histos.fill(HIST("hZNCvsFT0C_BCs"), multbc.multFT0C() * scaleSignalFT0C, multbc.multZNC()); + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 6); // znac time + } - histos.fill(HIST("hFT0M_BCs"), (multbc.multFT0A() + multbc.multFT0C()) * scaleSignalFT0M); - histos.fill(HIST("hFV0A_BCs"), multbc.multFV0A() * scaleSignalFV0A); - if (multbc.multFT0PosZValid()) { - histos.fill(HIST("hFT0CvsPVz_BCs_All"), multbc.multFT0PosZ(), multbc.multFT0C() * scaleSignalFT0C); - if (multbc.multFT0C() > minFT0CforVertexZ) { - histos.fill(HIST("hFT0CvsPVz_BCs"), multbc.multFT0PosZ(), multbc.multFT0C() * scaleSignalFT0C); + if constexpr (requires { bc.timeZPA(); }) { + const bool kIsBBZPA = bc.timeZPA() > cfgFwd.fZPABBlower && bc.timeZPA() < cfgFwd.fZPABBupper; + const bool kIsBBZPC = bc.timeZPC() > cfgFwd.fZPCBBlower && bc.timeZPC() < cfgFwd.fZPCBBupper; + if (bcsel.rejectZPAC && !(kIsBBZPA && kIsBBZPC)) { + return false; } } - if (multbc.has_ft0Mult()) { - auto multco = multbc.ft0Mult_as>(); + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 7); // zpac time + } + + if (bcsel.rejectIsFlangeEvent) { + constexpr int IsFlangeEventId = 7; + std::bitset<8> ft0TriggerMask = bc.multT0triggerBits(); + if (ft0TriggerMask[IsFlangeEventId]) { + return false; + } + } + + if (fillHistograms) { + histos.fill(HIST("hBCSelection"), 8); // isFlangeEvent + } + + return true; + } + + template + void genericProcessBCs(const TBunchCrossing& multbcs) + { + // process BCs, calculate FT0C distribution + for (const auto& multbc : multbcs) { + if (!selectedBC(multbc, true)) { + continue; + } + + // if we got here, we also finally fill the FT0C histogram, please + histos.fill(HIST("hFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C); + histos.fill(HIST("hFT0COuter_BCs"), multbc.multFT0COuter() * scale.factorFT0C); + histos.fill(HIST("hFT0A_BCs"), multbc.multFT0A() * scale.factorFT0A); + histos.fill(HIST("hFT0AOuter_BCs"), multbc.multFT0AOuter() * scale.factorFT0A); + histos.fill(HIST("hFV0AOuter_BCs"), multbc.multFV0AOuter() * scale.factorFV0A); + histos.fill(HIST("hFT0M_BCs"), (multbc.multFT0A() + multbc.multFT0C()) * scale.factorFT0M); + histos.fill(HIST("hFT0MOuterA_BCs"), (multbc.multFT0AOuter() + multbc.multFT0C()) * scale.factorFT0M); + histos.fill(HIST("hFV0A_BCs"), multbc.multFV0A() * scale.factorFV0A); + histos.fill(HIST("hFV0AT0C_BCs"), (multbc.multFV0A() + multbc.multFT0C()) * scale.factorFIT); + histos.fill(HIST("hFV0AOuterT0C_BCs"), (multbc.multFV0AOuter() + multbc.multFT0C()) * scale.factorFIT); + histos.fill(HIST("hFIT_BCs"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0AOuter()) * scale.factorFIT); + histos.fill(HIST("hFIT_FullV0_BCs"), (multbc.multFT0C() + multbc.multFT0AOuter() + multbc.multFV0A()) * scale.factorFIT); + histos.fill(HIST("hFIT_All_BCs"), (multbc.multFT0C() + multbc.multFT0A() + multbc.multFV0A()) * scale.factorFIT); + if (studies.do2DPlots) { + histos.fill(HIST("hFT0AVsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multFT0A() * scale.factorFT0A); + histos.fill(HIST("hFV0AVsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multFV0A() * scale.factorFV0A); + } + + // ZN signals + histos.fill(HIST("hZNAvsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multZNA()); + histos.fill(HIST("hZNCvsFT0C_BCs"), multbc.multFT0C() * scale.factorFT0C, multbc.multZNC()); + if (multbc.multFT0PosZValid()) { - histos.fill(HIST("hVertexZ_BCvsCO"), multco.multPVz(), multbc.multFT0PosZ()); + histos.fill(HIST("hFT0CvsPVz_BCs_All"), multbc.multFT0PosZ(), multbc.multFT0C() * scale.factorFT0C); + if (multbc.multFT0C() > bcsel.minFT0CforVertexZ) { + histos.fill(HIST("hFT0CvsPVz_BCs"), multbc.multFT0PosZ(), multbc.multFT0C() * scale.factorFT0C); + } } + + if (multbc.has_ft0Mult()) { + auto multco = multbc.template ft0Mult_as>(); + if (multbc.multFT0PosZValid()) { + histos.fill(HIST("hVertexZ_BCvsCO"), multco.multPVz(), multbc.multFT0PosZ()); + } + } + } + + for (const auto& multbc : multbcs) { + if (!selectedBC(multbc, false)) { + continue; + } + + const float selfNormFV0A = scale.normFV0A * multbc.multFV0A() / histos.get(HIST("hFV0A_BCs"))->GetMean(); + const float selfNormFT0A = scale.normFT0A * multbc.multFT0A() / histos.get(HIST("hFT0A_BCs"))->GetMean(); + const float selfNormFT0C = scale.normFT0C * multbc.multFT0C() / histos.get(HIST("hFT0C_BCs"))->GetMean(); + histos.fill(HIST("hScaledFT0M_BCs"), selfNormFT0A + selfNormFT0C); + histos.fill(HIST("hScaledFV0AT0C_BCs"), selfNormFV0A + selfNormFT0C); } } - PROCESS_SWITCH(centralityStudy, processCollisions, "per-collision analysis", false); - PROCESS_SWITCH(centralityStudy, processCollisionsWithResolutionStudy, "per-collision analysis, with reso study", false); - PROCESS_SWITCH(centralityStudy, processCollisionsWithCentrality, "per-collision analysis", true); - PROCESS_SWITCH(centralityStudy, processCollisionsWithCentralityWithNeighbours, "per-collision analysis", false); - PROCESS_SWITCH(centralityStudy, processBCs, "per-BC analysis", true); + void processBCs(soa::Join const& multbcs, soa::Join const&) + { + genericProcessBCs(multbcs); + } + + void processBCsWithTime(soa::Join const& multbcs, soa::Join const&) + { + genericProcessBCs(multbcs); + } + + PROCESS_SWITCH(CentralityStudy, processCollisions, "per-collision analysis", false); + PROCESS_SWITCH(CentralityStudy, processCollisionsWithResolutionStudy, "per-collision analysis, with reso study", false); + PROCESS_SWITCH(CentralityStudy, processCollisionsWithCentrality, "per-collision analysis", true); + PROCESS_SWITCH(CentralityStudy, processCollisionsWithCentralityWithNeighbours, "per-collision analysis", false); + PROCESS_SWITCH(CentralityStudy, processBCs, "per-BC analysis", true); + PROCESS_SWITCH(CentralityStudy, processBCsWithTime, "per-BC analysis with extra detector information", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/Common/Tasks/centralityStudypp.cxx b/Common/Tasks/centralityStudypp.cxx new file mode 100644 index 00000000000..f0a6ed54c33 --- /dev/null +++ b/Common/Tasks/centralityStudypp.cxx @@ -0,0 +1,541 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// This task is dedicated to percentile calibration and event +// selection studies in pp collisions using derived data based on the +// multCentTable output + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using BCsWithRun3Matchings = soa::Join; +#define getHist(type, name) std::get>(histPointers[name]) + +struct centralityStudypp { + // Raw multiplicities + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::map histPointers; + std::string histPath; + Service ccdb; + int mRunNumber = -1; + + // vertex Z equalization + TList* hCalibObjects = nullptr; + TProfile* hVtxZFV0A = nullptr; + TProfile* hVtxZFT0A = nullptr; + TProfile* hVtxZFT0C = nullptr; + TProfile* hVtxZNTracks = nullptr; + TProfile* hVtxZNGlobals = nullptr; + TProfile* hVtxZMFT = nullptr; + TProfile* hVtxZFDDA = nullptr; + TProfile* hVtxZFDDC = nullptr; + + // calibration histograms + TH1* hCentralityFV0A = nullptr; + TH1* hCentralityFT0A = nullptr; + TH1* hCentralityFT0C = nullptr; + TH1* hCentralityFT0M = nullptr; + TH1* hCentralityFDDM = nullptr; + TH1* hCentralityNTPV = nullptr; + TH1* hCentralityNGlo = nullptr; + TH1* hCentralityMFT = nullptr; + + // Configurables + Configurable do2DPlots{"do2DPlots", true, "0 - no, 1 - yes"}; + // _______________________________________ + // event selection criteria + Configurable applyVertexZEqualization{"applyVertexZEqualization", false, "0 - no, 1 - yes"}; + Configurable saveUnequalized{"saveUnequalized", false, "save unequalized raw: 0 - no, 1 - yes"}; + Configurable doCentralityQA{"doCentralityQA", false, "do centrality QA: 0 - no, 1 - yes"}; + Configurable applySel8{"applySel8", true, "0 - no, 1 - yes"}; + Configurable applyVtxZ{"applyVtxZ", true, "0 - no, 1 - yes"}; + Configurable requireINELgtZERO{"requireINELgtZERO", true, "0 no, 1 - yes"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", false, "reject events at ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", false, "reject events at TF border"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", false, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", false, "reject collisions in case of pileup with another collision in the same foundBC"}; + + Configurable vertexZcut{"vertexZcut", 10.0f, "vertex Z cut (cm)"}; + + // For one-dimensional plots, where binning is no issue + ConfigurableAxis axisMultUltraFineFV0A{"axisMultUltraFineFV0A", {60000, 0, 60000}, "FV0A amplitude"}; + ConfigurableAxis axisMultUltraFineFT0A{"axisMultUltraFineFT0A", {60000, 0, 60000}, "FT0A amplitude"}; + ConfigurableAxis axisMultUltraFineFT0C{"axisMultUltraFineFT0C", {60000, 0, 60000}, "FT0C amplitude"}; + ConfigurableAxis axisMultUltraFineFT0M{"axisMultUltraFineFT0M", {50000, 0, 200000}, "FT0M amplitude"}; + ConfigurableAxis axisMultUltraFineFDDA{"axisMultUltraFineFDDA", {60000, 0, 60000}, "FDDA amplitude"}; + ConfigurableAxis axisMultUltraFineFDDC{"axisMultUltraFineFDDC", {60000, 0, 60000}, "FDDC amplitude"}; + ConfigurableAxis axisMultUltraFinePVContributors{"axisMultUltraFinePVContributors", {10000, 0, 10000}, "Number of PV Contributors"}; + ConfigurableAxis axisMultUltraFineGlobalTracks{"axisMultUltraFineGlobalTracks", {5000, 0, 5000}, "Number of global tracks"}; + ConfigurableAxis axisMultUltraFineMFTTracks{"axisMultUltraFineMFTTracks", {5000, 0, 5000}, "Number of MFT tracks"}; + // For profile Z + ConfigurableAxis axisPVz{"axisPVz", {400, -20.0f, +20.0f}, "PVz (cm)"}; + ConfigurableAxis axisZN{"axisZN", {1100, -50.0f, +500.0f}, "ZN"}; + // For centrality QA + ConfigurableAxis axisCentrality{"axisCentrality", {10000, 0, 100}, "centrality percentile"}; + + // ccdb matters + Configurable ccdbURL{"ccdbURL", "http://alice-ccdb.cern.ch", "ccdb url"}; + Configurable pathGRPECSObject{"pathGRPECSObject", "GLO/Config/GRPECS", "Path to GRPECS object"}; + Configurable pathVertexZ{"pathVertexZ", "Users/d/ddobrigk/Centrality/Calibration", "Path to vertexZ profiles"}; + Configurable pathCentrality{"pathCentrality", "Users/d/ddobrigk/Centrality/Estimators", "Path to centrality calibration"}; + + void init(InitContext&) + { + hCalibObjects = nullptr; + hVtxZFV0A = nullptr; + hVtxZFT0A = nullptr; + hVtxZFT0C = nullptr; + hVtxZNTracks = nullptr; + hVtxZNGlobals = nullptr; + hVtxZMFT = nullptr; + hVtxZFDDA = nullptr; + hVtxZFDDC = nullptr; + + hCentralityFV0A = nullptr; + hCentralityFT0A = nullptr; + hCentralityFT0C = nullptr; + hCentralityFT0M = nullptr; + hCentralityFDDM = nullptr; + hCentralityNTPV = nullptr; + hCentralityNGlo = nullptr; + hCentralityMFT = nullptr; + + const AxisSpec axisCollisions{100, -0.5f, 99.5f, "Number of collisions"}; + histos.add("hCollisionSelection", "hCollisionSelection", kTH1D, {{20, -0.5f, +19.5f}}); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(3, "posZ cut"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(6, "kIsVertexITSTPC"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(7, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(11, "Neighbour rejection"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(12, "no ITS in-ROF pileup (standard)"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(13, "no ITS in-ROF pileup (strict)"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(14, "is UPC event"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(15, "rejectCollInTimeRangeNarrow"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(16, "em/upc rejection"); + histos.get(HIST("hCollisionSelection"))->GetXaxis()->SetBinLabel(17, "isFlangeEvent"); + + histos.add("hFT0A_Collisions", "hFT0A_Collisions", kTH1D, {axisMultUltraFineFT0A}); + histos.add("hFT0C_Collisions", "hFT0C_Collisions", kTH1D, {axisMultUltraFineFT0C}); + histos.add("hFT0M_Collisions", "hFT0M_Collisions", kTH1D, {axisMultUltraFineFT0M}); + + histos.add("hFDDA_Collisions", "hFDDA_Collisions", kTH1D, {axisMultUltraFineFDDA}); + histos.add("hFDDC_Collisions", "hFDDC_Collisions", kTH1D, {axisMultUltraFineFDDC}); + + histos.add("hFV0A_Collisions", "hFV0A_Collisions", kTH1D, {axisMultUltraFineFV0A}); + histos.add("hNGlobalTracks", "hNGlobalTracks", kTH1D, {axisMultUltraFineGlobalTracks}); + histos.add("hNMFTTracks", "hNMFTTracks", kTH1D, {axisMultUltraFineMFTTracks}); + histos.add("hNPVContributors", "hNPVContributors", kTH1D, {axisMultUltraFinePVContributors}); + + histos.add("hFT0AvsPVz_Collisions", "hFT0AvsPVz_Collisions", kTProfile, {axisPVz}); + histos.add("hFT0CvsPVz_Collisions", "hFT0CvsPVz_Collisions", kTProfile, {axisPVz}); + histos.add("hFDDAvsPVz_Collisions", "hFDDAvsPVz_Collisions", kTProfile, {axisPVz}); + histos.add("hFDDCvsPVz_Collisions", "hFDDCvsPVz_Collisions", kTProfile, {axisPVz}); + histos.add("hFV0AvsPVz_Collisions", "hFV0AvsPVz_Collisions", kTProfile, {axisPVz}); + histos.add("hNGlobalTracksvsPVz_Collisions", "hNGlobalTracksvsPVz_Collisions", kTProfile, {axisPVz}); + histos.add("hNMFTTracksvsPVz_Collisions", "hNMFTTracksvsPVz_Collisions", kTProfile, {axisPVz}); + } + + template + void initRun(const TCollision& collision) + { + if (mRunNumber == collision.multRunNumber()) { + return; + } + + mRunNumber = collision.multRunNumber(); + LOGF(info, "Setting up for run: %i", mRunNumber); + + if (applyVertexZEqualization.value) { + // acquire vertex-Z equalization histograms if requested + LOGF(info, "Acquiring vertex-Z profiles for run %i", mRunNumber); + hCalibObjects = ccdb->getForRun(pathVertexZ, mRunNumber); + + hVtxZFV0A = dynamic_cast(hCalibObjects->FindObject("hVtxZFV0A")); + hVtxZFT0A = dynamic_cast(hCalibObjects->FindObject("hVtxZFT0A")); + hVtxZFT0C = dynamic_cast(hCalibObjects->FindObject("hVtxZFT0C")); + hVtxZFDDA = dynamic_cast(hCalibObjects->FindObject("hVtxZFDDA")); + hVtxZFDDC = dynamic_cast(hCalibObjects->FindObject("hVtxZFDDC")); + hVtxZNTracks = dynamic_cast(hCalibObjects->FindObject("hVtxZNTracksPV")); + hVtxZNGlobals = dynamic_cast(hCalibObjects->FindObject("hVtxZNGlobals")); + hVtxZMFT = dynamic_cast(hCalibObjects->FindObject("hVtxZMFT")); + + // Capture error + if (!hVtxZFV0A || !hVtxZFT0A || !hVtxZFT0C || !hVtxZFDDA || !hVtxZFDDC || !hVtxZNTracks || !hVtxZNGlobals || !hVtxZMFT) { + LOGF(error, "Problem loading CCDB objects! Please check"); + } + } + + if (doCentralityQA.value) { + LOGF(info, "Acquiring centrality calibration for run %i", mRunNumber); + TList* hCentralityObjects = nullptr; + hCentralityObjects = ccdb->getForRun(pathCentrality, mRunNumber); + + hCentralityFV0A = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFV0")); + hCentralityFT0A = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFT0A")); + hCentralityFT0C = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFT0C")); + hCentralityFT0M = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFT0")); + hCentralityFDDM = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqFDD")); + hCentralityNTPV = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqNTracksPV")); + hCentralityNGlo = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqNGlobal")); + hCentralityMFT = dynamic_cast(hCentralityObjects->FindObject("hCalibZeqMFT")); + + // won't capture null pointers -> explicitly check for those when attempting to evaluate + auto reportSuccess = [](TH1* a, const std::string& name) { + if (!a) { + LOGF(info, "Calibration missing for %s", name); + } else { + LOGF(info, "Calibration loaded for %s", name); + } + }; + + reportSuccess(hCentralityFV0A, "FV0A"); + reportSuccess(hCentralityFT0A, "FT0A"); + reportSuccess(hCentralityFT0C, "FT0C"); + reportSuccess(hCentralityFT0M, "FT0M"); + reportSuccess(hCentralityFDDM, "FDDM"); + reportSuccess(hCentralityNTPV, "NTPV"); + reportSuccess(hCentralityNGlo, "NGlobals"); + reportSuccess(hCentralityMFT, "MFT"); + + LOGF(info, "Centrality calibration loading done."); + } + + histPath = std::format("Run_{}/", mRunNumber); + + histPointers.insert({histPath + "hCollisionSelection", histos.add((histPath + "hCollisionSelection").c_str(), "hCollisionSelection", {kTH1D, {{20, -0.5f, +19.5f}}})}); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(1, "All collisions"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(2, "sel8 cut"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(3, "posZ cut"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(6, "kIsVertexITSTPC"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(7, "kIsGoodZvtxFT0vsPV"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); + getHist(TH1, histPath + "hCollisionSelection")->GetXaxis()->SetBinLabel(11, "Pass INEL > 0"); + + histPointers.insert({histPath + "hFT0A_Collisions", histos.add((histPath + "hFT0A_Collisions").c_str(), "hFT0A_Collisions", {kTH1D, {{axisMultUltraFineFT0A}}})}); + histPointers.insert({histPath + "hFT0C_Collisions", histos.add((histPath + "hFT0C_Collisions").c_str(), "hFT0C_Collisions", {kTH1D, {{axisMultUltraFineFT0C}}})}); + histPointers.insert({histPath + "hFT0M_Collisions", histos.add((histPath + "hFT0M_Collisions").c_str(), "hFT0M_Collisions", {kTH1D, {{axisMultUltraFineFT0M}}})}); + + histPointers.insert({histPath + "hFDDA_Collisions", histos.add((histPath + "hFDDA_Collisions").c_str(), "hFDDA_Collisions", {kTH1D, {{axisMultUltraFineFDDA}}})}); + histPointers.insert({histPath + "hFDDC_Collisions", histos.add((histPath + "hFDDC_Collisions").c_str(), "hFDDC_Collisions", {kTH1D, {{axisMultUltraFineFDDC}}})}); + histPointers.insert({histPath + "hFDDM_Collisions", histos.add((histPath + "hFDDM_Collisions").c_str(), "hFDDM_Collisions", {kTH1D, {{axisMultUltraFineFDDC}}})}); + + histPointers.insert({histPath + "hFV0A_Collisions", histos.add((histPath + "hFV0A_Collisions").c_str(), "hFV0A_Collisions", {kTH1D, {{axisMultUltraFineFV0A}}})}); + histPointers.insert({histPath + "hNGlobalTracks", histos.add((histPath + "hNGlobalTracks").c_str(), "hNGlobalTracks", {kTH1D, {{axisMultUltraFineGlobalTracks}}})}); + histPointers.insert({histPath + "hNMFTTracks", histos.add((histPath + "hNMFTTracks").c_str(), "hNMFTTracks", {kTH1D, {{axisMultUltraFineMFTTracks}}})}); + histPointers.insert({histPath + "hNPVContributors", histos.add((histPath + "hNPVContributors").c_str(), "hNPVContributors", {kTH1D, {{axisMultUltraFinePVContributors}}})}); + + if (applyVertexZEqualization && saveUnequalized) { + histPointers.insert({histPath + "hFT0A_Collisions_Unequalized", histos.add((histPath + "hFT0A_Collisions_Unequalized").c_str(), "hFT0A_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFT0A}}})}); + histPointers.insert({histPath + "hFT0C_Collisions_Unequalized", histos.add((histPath + "hFT0C_Collisions_Unequalized").c_str(), "hFT0C_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFT0C}}})}); + histPointers.insert({histPath + "hFT0M_Collisions_Unequalized", histos.add((histPath + "hFT0M_Collisions_Unequalized").c_str(), "hFT0M_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFT0M}}})}); + + histPointers.insert({histPath + "hFDDA_Collisions_Unequalized", histos.add((histPath + "hFDDA_Collisions_Unequalized").c_str(), "hFDDA_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFDDA}}})}); + histPointers.insert({histPath + "hFDDC_Collisions_Unequalized", histos.add((histPath + "hFDDC_Collisions_Unequalized").c_str(), "hFDDC_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFDDC}}})}); + histPointers.insert({histPath + "hFDDM_Collisions_Unequalized", histos.add((histPath + "hFDDM_Collisions_Unequalized").c_str(), "hFDDM_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFDDC}}})}); + + histPointers.insert({histPath + "hFV0A_Collisions_Unequalized", histos.add((histPath + "hFV0A_Collisions_Unequalized").c_str(), "hFV0A_Collisions_Unequalized", {kTH1D, {{axisMultUltraFineFV0A}}})}); + histPointers.insert({histPath + "hNGlobalTracks_Unequalized", histos.add((histPath + "hNGlobalTracks_Unequalized").c_str(), "hNGlobalTracks_Unequalized", {kTH1D, {{axisMultUltraFineGlobalTracks}}})}); + histPointers.insert({histPath + "hNMFTTracks_Unequalized", histos.add((histPath + "hNMFTTracks_Unequalized").c_str(), "hNMFTTracks_Unequalized", {kTH1D, {{axisMultUltraFineMFTTracks}}})}); + histPointers.insert({histPath + "hNPVContributors_Unequalized", histos.add((histPath + "hNPVContributors_Unequalized").c_str(), "hNPVContributors_Unequalized", {kTH1D, {{axisMultUltraFinePVContributors}}})}); + } + + if (doCentralityQA.value) { + histPointers.insert({histPath + "hCentralityDistributionFV0A", histos.add((histPath + "hCentralityDistributionFV0A").c_str(), "hCentralityDistributionFV0A", {kTH1D, {{axisCentrality}}})}); + histPointers.insert({histPath + "hCentralityDistributionFT0A", histos.add((histPath + "hCentralityDistributionFT0A").c_str(), "hCentralityDistributionFT0A", {kTH1D, {{axisCentrality}}})}); + histPointers.insert({histPath + "hCentralityDistributionFT0C", histos.add((histPath + "hCentralityDistributionFT0C").c_str(), "hCentralityDistributionFT0C", {kTH1D, {{axisCentrality}}})}); + histPointers.insert({histPath + "hCentralityDistributionFT0M", histos.add((histPath + "hCentralityDistributionFT0M").c_str(), "hCentralityDistributionFT0M", {kTH1D, {{axisCentrality}}})}); + histPointers.insert({histPath + "hCentralityDistributionFDDM", histos.add((histPath + "hCentralityDistributionFDDM").c_str(), "hCentralityDistributionFDDM", {kTH1D, {{axisCentrality}}})}); + histPointers.insert({histPath + "hCentralityDistributionNTPV", histos.add((histPath + "hCentralityDistributionNTPV").c_str(), "hCentralityDistributionNTPV", {kTH1D, {{axisCentrality}}})}); + histPointers.insert({histPath + "hCentralityDistributionNGlobal", histos.add((histPath + "hCentralityDistributionNGlobal").c_str(), "hCentralityDistributionNGlobal", {kTH1D, {{axisCentrality}}})}); + histPointers.insert({histPath + "hCentralityDistributionMFT", histos.add((histPath + "hCentralityDistributionMFT").c_str(), "hCentralityDistributionMFT", {kTH1D, {{axisCentrality}}})}); + } + + histPointers.insert({histPath + "hFT0AvsPVz_Collisions", histos.add((histPath + "hFT0AvsPVz_Collisions").c_str(), "hFT0AvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hFT0CvsPVz_Collisions", histos.add((histPath + "hFT0CvsPVz_Collisions").c_str(), "hFT0CvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hFDDAvsPVz_Collisions", histos.add((histPath + "hFDDAvsPVz_Collisions").c_str(), "hFDDAvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hFDDCvsPVz_Collisions", histos.add((histPath + "hFDDCvsPVz_Collisions").c_str(), "hFDDCvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hFV0AvsPVz_Collisions", histos.add((histPath + "hFV0AvsPVz_Collisions").c_str(), "hFV0AvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hNGlobalTracksvsPVz_Collisions", histos.add((histPath + "hNGlobalTracksvsPVz_Collisions").c_str(), "hNGlobalTracksvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hNMFTTracksvsPVz_Collisions", histos.add((histPath + "hNMFTTracksvsPVz_Collisions").c_str(), "hNMFTTracksvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + histPointers.insert({histPath + "hNTPVvsPVz_Collisions", histos.add((histPath + "hNTPVvsPVz_Collisions").c_str(), "hNTPVvsPVz_Collisions", {kTProfile, {{axisPVz}}})}); + } + + template + void genericProcessCollision(const TCollision& collision) + // process this collisions + { + initRun(collision); + histos.fill(HIST("hCollisionSelection"), 0); // all collisions + getHist(TH1, histPath + "hCollisionSelection")->Fill(0); + + if (applySel8 && !collision.multSel8()) { + return; + } + histos.fill(HIST("hCollisionSelection"), 1); + getHist(TH1, histPath + "hCollisionSelection")->Fill(1); + + // calculate vertex-Z-equalized quantities if desired + float multFV0A = collision.multFV0A(); + float multFT0A = collision.multFT0A(); + float multFT0C = collision.multFT0C(); + float multFDDA = collision.multFDDA(); + float multFDDC = collision.multFDDC(); + float multNTracksGlobal = collision.multNTracksGlobal(); + float mftNtracks = collision.mftNtracks(); + float multNTracksPV = collision.multNTracksPV(); + + if (applyVertexZEqualization) { + float epsilon = 1e-2; // average value after which this collision will be disregarded + float maxVertexZ = 15.0f; // max value for any equalization attempt + + // same defaults as multCentTable in case of unhealthy signals + multFV0A = 0.0f; + multFT0A = 0.0f; + multFT0C = 0.0f; + multFDDA = 0.0f; + multFDDC = 0.0f; + multNTracksGlobal = 0.0f; + mftNtracks = 0.0f; + multNTracksPV = 0.0f; + + if (std::abs(collision.multPVz()) < maxVertexZ) { // operate consistently vs multCentTable + if (hVtxZFV0A->Interpolate(collision.multPVz()) > epsilon && collision.multFV0A() > -1.0f) { + multFV0A = hVtxZFV0A->Interpolate(0.0) * collision.multFV0A() / hVtxZFV0A->Interpolate(collision.multPVz()); + } + if (hVtxZFT0A->Interpolate(collision.multPVz()) > epsilon && collision.multFT0A() > -1.0f) { + multFT0A = hVtxZFT0A->Interpolate(0.0) * collision.multFT0A() / hVtxZFT0A->Interpolate(collision.multPVz()); + } + if (hVtxZFT0C->Interpolate(collision.multPVz()) > epsilon && collision.multFT0C() > -1.0f) { + multFT0C = hVtxZFT0C->Interpolate(0.0) * collision.multFT0C() / hVtxZFT0C->Interpolate(collision.multPVz()); + } + if (hVtxZFDDA->Interpolate(collision.multPVz()) > epsilon && collision.multFDDA() > -1.0f) { + multFDDA = hVtxZFDDA->Interpolate(0.0) * collision.multFDDA() / hVtxZFDDA->Interpolate(collision.multPVz()); + } + if (hVtxZFDDC->Interpolate(collision.multPVz()) > epsilon && collision.multFDDC() > -1.0f) { + multFDDC = hVtxZFDDC->Interpolate(0.0) * collision.multFDDC() / hVtxZFDDC->Interpolate(collision.multPVz()); + } + if (hVtxZNGlobals->Interpolate(collision.multPVz()) > epsilon && collision.multNTracksGlobal() > -1.0f) { + multNTracksGlobal = hVtxZNGlobals->Interpolate(0.0) * collision.multNTracksGlobal() / hVtxZNGlobals->Interpolate(collision.multPVz()); + } + if (hVtxZMFT->Interpolate(collision.multPVz()) > epsilon && collision.mftNtracks() > -1.0f) { + mftNtracks = hVtxZMFT->Interpolate(0.0) * collision.mftNtracks() / hVtxZMFT->Interpolate(collision.multPVz()); + } + if (hVtxZNTracks->Interpolate(collision.multPVz()) > epsilon && collision.multNTracksPV() > -1.0f) { + multNTracksPV = hVtxZNTracks->Interpolate(0.0) * collision.multNTracksPV() / hVtxZNTracks->Interpolate(collision.multPVz()); + } + } + } + + bool passRejectITSROFBorder = !(rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)); + bool passRejectTFBorder = !(rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)); + bool passRequireIsVertexITSTPC = !(requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)); + bool passRequireIsGoodZvtxFT0VsPV = !(requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)); + bool passRequireIsVertexTOFmatched = !(requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)); + bool passRequireIsVertexTRDmatched = !(requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)); + bool passRejectSameBunchPileup = !(rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)); + bool passINELgtZERO = !(requireINELgtZERO && !collision.isInelGt0()); + + // _______________________________________________________ + // sidestep vertex-Z rejection for vertex-Z profile histograms + if (passRejectITSROFBorder && passRejectTFBorder && passRequireIsVertexITSTPC && passRequireIsGoodZvtxFT0VsPV && + passRequireIsVertexTOFmatched && passRequireIsVertexTRDmatched && passRejectSameBunchPileup && passINELgtZERO) { + // all runs + histos.fill(HIST("hFT0AvsPVz_Collisions"), collision.multPVz(), collision.multFT0A()); + histos.fill(HIST("hFT0CvsPVz_Collisions"), collision.multPVz(), collision.multFT0C()); + histos.fill(HIST("hFDDAvsPVz_Collisions"), collision.multPVz(), collision.multFDDA()); + histos.fill(HIST("hFDDCvsPVz_Collisions"), collision.multPVz(), collision.multFDDC()); + histos.fill(HIST("hFV0AvsPVz_Collisions"), collision.multPVz(), collision.multFV0A()); + histos.fill(HIST("hNGlobalTracksvsPVz_Collisions"), collision.multPVz(), collision.multNTracksGlobal()); + histos.fill(HIST("hNMFTTracksvsPVz_Collisions"), collision.multPVz(), collision.mftNtracks()); + + // per run + getHist(TProfile, histPath + "hFT0CvsPVz_Collisions")->Fill(collision.multPVz(), multFT0C); + getHist(TProfile, histPath + "hFT0AvsPVz_Collisions")->Fill(collision.multPVz(), multFT0A); + getHist(TProfile, histPath + "hFDDAvsPVz_Collisions")->Fill(collision.multPVz(), multFDDA); + getHist(TProfile, histPath + "hFDDCvsPVz_Collisions")->Fill(collision.multPVz(), multFDDC); + getHist(TProfile, histPath + "hFV0AvsPVz_Collisions")->Fill(collision.multPVz(), multFV0A); + getHist(TProfile, histPath + "hNGlobalTracksvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksGlobal); + getHist(TProfile, histPath + "hNMFTTracksvsPVz_Collisions")->Fill(collision.multPVz(), mftNtracks); + getHist(TProfile, histPath + "hNTPVvsPVz_Collisions")->Fill(collision.multPVz(), multNTracksPV); + } + + // _______________________________________________________ + + if (applyVtxZ && TMath::Abs(collision.multPVz()) > vertexZcut.value) { + return; + } + histos.fill(HIST("hCollisionSelection"), 2); + getHist(TH1, histPath + "hCollisionSelection")->Fill(2); + + // _______________________________________________________ + // Extra event selections start here + if (!passRejectITSROFBorder) { + return; + } + histos.fill(HIST("hCollisionSelection"), 3 /* Not at ITS ROF border */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(3); + + if (!passRejectTFBorder) { + return; + } + histos.fill(HIST("hCollisionSelection"), 4 /* Not at TF border */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(4); + + if (!passRequireIsVertexITSTPC) { + return; + } + histos.fill(HIST("hCollisionSelection"), 5 /* Contains at least one ITS-TPC track */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(5); + + if (!passRequireIsGoodZvtxFT0VsPV) { + return; + } + histos.fill(HIST("hCollisionSelection"), 6 /* PV position consistency check */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(6); + + if (!passRequireIsVertexTOFmatched) { + return; + } + histos.fill(HIST("hCollisionSelection"), 7 /* PV with at least one contributor matched with TOF */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(7); + + if (!passRequireIsVertexTRDmatched) { + return; + } + histos.fill(HIST("hCollisionSelection"), 8 /* PV with at least one contributor matched with TRD */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(8); + + if (!passRejectSameBunchPileup) { + return; + } + histos.fill(HIST("hCollisionSelection"), 9 /* Not at same bunch pile-up */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(9); + + if (!passINELgtZERO) { + return; + } + histos.fill(HIST("hCollisionSelection"), 11 /* is INEL > 0 */); + getHist(TH1, histPath + "hCollisionSelection")->Fill(11); + + // if we got here, we also finally fill the FT0C histogram, please + histos.fill(HIST("hNPVContributors"), multNTracksPV); + histos.fill(HIST("hFT0A_Collisions"), multFT0A); + histos.fill(HIST("hFT0C_Collisions"), multFT0C); + histos.fill(HIST("hFT0M_Collisions"), multFT0A + multFT0C); + histos.fill(HIST("hFDDA_Collisions"), multFDDA); + histos.fill(HIST("hFDDC_Collisions"), multFDDC); + histos.fill(HIST("hFV0A_Collisions"), multFV0A); + histos.fill(HIST("hNGlobalTracks"), collision.multNTracksGlobal()); + histos.fill(HIST("hNMFTTracks"), collision.mftNtracks()); + + // save vertex-Z equalized + getHist(TH1, histPath + "hNPVContributors")->Fill(multNTracksPV); + getHist(TH1, histPath + "hFT0A_Collisions")->Fill(multFT0A); + getHist(TH1, histPath + "hFT0C_Collisions")->Fill(multFT0C); + getHist(TH1, histPath + "hFT0M_Collisions")->Fill((multFT0A + multFT0C)); + getHist(TH1, histPath + "hFDDA_Collisions")->Fill(multFDDA); + getHist(TH1, histPath + "hFDDC_Collisions")->Fill(multFDDC); + getHist(TH1, histPath + "hFDDM_Collisions")->Fill(multFDDA + multFDDC); + getHist(TH1, histPath + "hFV0A_Collisions")->Fill(multFV0A); + getHist(TH1, histPath + "hNGlobalTracks")->Fill(multNTracksGlobal); + getHist(TH1, histPath + "hNMFTTracks")->Fill(mftNtracks); + + if (applyVertexZEqualization.value && saveUnequalized.value) { + // save unequalized for cross-checks + getHist(TH1, histPath + "hNPVContributors_Unequalized")->Fill(collision.multNTracksPV()); + getHist(TH1, histPath + "hFT0A_Collisions_Unequalized")->Fill(collision.multFT0A()); + getHist(TH1, histPath + "hFT0C_Collisions_Unequalized")->Fill(collision.multFT0C()); + getHist(TH1, histPath + "hFT0M_Collisions_Unequalized")->Fill((collision.multFT0A() + collision.multFT0C())); + getHist(TH1, histPath + "hFDDA_Collisions_Unequalized")->Fill(collision.multFDDA()); + getHist(TH1, histPath + "hFDDC_Collisions_Unequalized")->Fill(collision.multFDDC()); + getHist(TH1, histPath + "hFV0A_Collisions_Unequalized")->Fill(collision.multFV0A()); + getHist(TH1, histPath + "hNGlobalTracks_Unequalized")->Fill(collision.multNTracksGlobal()); + getHist(TH1, histPath + "hNMFTTracks_Unequalized")->Fill(collision.mftNtracks()); + } + + if (doCentralityQA.value) { + // generate centralities on the spot in case the centrality histograms are in memory + if (hCentralityFV0A) { + getHist(TH1, histPath + "hCentralityDistributionFV0A")->Fill(hCentralityFV0A->GetBinContent(hCentralityFV0A->FindBin(multFV0A))); + } + if (hCentralityFT0A) { + getHist(TH1, histPath + "hCentralityDistributionFT0A")->Fill(hCentralityFT0A->GetBinContent(hCentralityFT0A->FindBin(multFT0A))); + } + if (hCentralityFT0C) { + getHist(TH1, histPath + "hCentralityDistributionFT0C")->Fill(hCentralityFT0C->GetBinContent(hCentralityFT0C->FindBin(multFT0C))); + } + if (hCentralityFT0M) { + getHist(TH1, histPath + "hCentralityDistributionFT0M")->Fill(hCentralityFT0M->GetBinContent(hCentralityFT0M->FindBin(multFT0A + multFT0C))); + } + if (hCentralityFDDM) { + getHist(TH1, histPath + "hCentralityDistributionFDDM")->Fill(hCentralityFDDM->GetBinContent(hCentralityFDDM->FindBin(multFDDA + multFDDC))); + } + if (hCentralityNTPV) { + // note: not vertex-Z-equalized to match what is done in central framework for this estimator + getHist(TH1, histPath + "hCentralityDistributionNTPV")->Fill(hCentralityNTPV->GetBinContent(hCentralityNTPV->FindBin(collision.multNTracksPV()))); + } + if (hCentralityNGlo) { + getHist(TH1, histPath + "hCentralityDistributionNGlobal")->Fill(hCentralityNGlo->GetBinContent(hCentralityNGlo->FindBin(multNTracksGlobal))); + } + if (hCentralityMFT) { + getHist(TH1, histPath + "hCentralityDistributionMFT")->Fill(hCentralityMFT->GetBinContent(hCentralityMFT->FindBin(mftNtracks))); + } + } + } + + void process(soa::Join::iterator const& collision) + { + genericProcessCollision(collision); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/Common/Tasks/checkDataModelMC.cxx b/Common/Tasks/checkDataModelMC.cxx index 612f2b8da1e..105ada03c72 100644 --- a/Common/Tasks/checkDataModelMC.cxx +++ b/Common/Tasks/checkDataModelMC.cxx @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include +#include #include #include @@ -38,12 +40,12 @@ using namespace o2::framework::expressions; /// \param particle MC particle /// \param debugMode flag for debug mode /// \param debugHisto histo to fill for debug -template +template void checkDaughters(const T& particlesMC, const typename T::iterator& particle, - long unsigned int offset, - bool& debugMode, - H debugHisto) + uint64_t offset, + bool debugMode, + std::shared_ptr& debugHisto) { auto firstDauIdx = particle.daughtersIds().front(); auto lastDauIdx = particle.daughtersIds().back(); @@ -55,7 +57,7 @@ void checkDaughters(const T& particlesMC, } } for (const auto& idxDau : particle.daughtersIds()) { - if (idxDau >= 0 && ((unsigned long int)idxDau > offset + particlesMC.size() || (unsigned long int)idxDau < offset)) { + if (idxDau >= 0 && (static_cast(idxDau) > offset + particlesMC.size() || static_cast(idxDau) < offset)) { if (debugMode) { debugHisto->Fill(1); } else { @@ -71,7 +73,49 @@ void checkDaughters(const T& particlesMC, } } -template +/// Check for cycles in the decay chain +/// \param particlesMC table with MC particles +template +bool checkCycles(T const& particlesMC) +{ + auto slow = particlesMC.begin(); + auto fast = particlesMC.begin(); + auto probe = particlesMC.begin(); + bool hasCycle = false; + for (auto start = 0U; start < particlesMC.size(); ++start) { + probe.setCursor(start); + if (!probe.has_mothers() || probe.mothersIds()[0] == -1) { + continue; + } + slow.setCursor(start); + fast.setCursor(start); + + while (true) { + if (!slow.has_mothers() || slow.mothersIds()[0] < 0) { + break; + } + slow.setCursor(slow.mothersIds()[0]); + + if (!fast.has_mothers() || fast.mothersIds()[0] < 0) { + break; + } + fast.setCursor(fast.mothersIds()[0]); + if (!fast.has_mothers() || fast.mothersIds()[0] < 0) { + break; + } + fast.setCursor(fast.mothersIds()[0]); + + if (slow.globalIndex() == fast.globalIndex()) { + LOGP(error, "Cycle found: particle: {} | cycle starts at: {}", start, slow.globalIndex()); + hasCycle = true; + break; + } + } + } + return hasCycle; +} + +template struct LoadTable { OutputObj counter{TH1F("counter", "counter", 2, 0., 2)}; void process(Table const& table) @@ -82,6 +126,16 @@ struct LoadTable { } }; +std::shared_ptr defineHistogram(HistogramRegistry& registry) +{ + std::shared_ptr hDebug; + hDebug = registry.add("hDebug", "debug histo;;counts", HistType::kTH1F, {{3, -0.5, 2.5}}); + hDebug->GetXaxis()->SetBinLabel(1, "(-X, Y) or (X, -Y)"); + hDebug->GetXaxis()->SetBinLabel(2, "out of range"); + hDebug->GetXaxis()->SetBinLabel(3, "#minus max integer"); + return hDebug; +} + struct CheckMcParticlesIndices { Configurable debugMode{"debugMode", false, "flag to enable debug mode"}; @@ -91,15 +145,15 @@ struct CheckMcParticlesIndices { void init(o2::framework::InitContext&) { - hDebug = registry.add("hDebug", "debug histo;;counts", HistType::kTH1F, {{3, -0.5, 2.5}}); - hDebug->GetXaxis()->SetBinLabel(1, "(-X, Y) or (X, -Y)"); - hDebug->GetXaxis()->SetBinLabel(2, "out of range"); - hDebug->GetXaxis()->SetBinLabel(3, "#minus max integer"); + hDebug = defineHistogram(registry); } void process(aod::McParticles const& particlesMC) { - long unsigned int offset = 0; + if (checkCycles(particlesMC)) { + LOG(fatal) << "Cycles found, aborting."; + } + uint64_t offset = 0; for (const auto& particle : particlesMC) { checkDaughters(particlesMC, particle, offset, debugMode.value, hDebug); } @@ -115,10 +169,7 @@ struct CheckMcParticlesIndicesGrouped { void init(o2::framework::InitContext&) { - hDebug = registry.add("hDebug", "debug histo;;counts", HistType::kTH1F, {{3, -0.5, 2.5}}); - hDebug->GetXaxis()->SetBinLabel(1, "(-X, Y) or (X, -Y)"); - hDebug->GetXaxis()->SetBinLabel(2, "out of range"); - hDebug->GetXaxis()->SetBinLabel(3, "#minus max integer"); + hDebug = defineHistogram(registry); } void process(aod::McCollision const&, diff --git a/Common/Tasks/flowTest.cxx b/Common/Tasks/flowTest.cxx index bfb42acff04..0e3334e91f1 100644 --- a/Common/Tasks/flowTest.cxx +++ b/Common/Tasks/flowTest.cxx @@ -30,6 +30,8 @@ #include #include +#include + using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -100,7 +102,7 @@ struct flowTest { if (evPhi < 0) evPhi += 2. * TMath::Pi(); - long nCh = 0; + int64_t nCh = 0; if (imp > minB && imp < maxB) { // event within range diff --git a/Common/Tasks/multiplicityQa.cxx b/Common/Tasks/multiplicityQa.cxx index 2178164854c..6a6c09188cf 100644 --- a/Common/Tasks/multiplicityQa.cxx +++ b/Common/Tasks/multiplicityQa.cxx @@ -290,8 +290,8 @@ struct MultiplicityQa { return; } - long NcontribsTOF = 0; - long NcontribsTRD = 0; + int64_t NcontribsTOF = 0; + int64_t NcontribsTRD = 0; for (auto& track : tracks) { if (track.isPVContributor()) { if (track.hasTRD()) @@ -347,7 +347,7 @@ struct MultiplicityQa { Preslice perColIU = aod::track::collisionId; using Run3Tracks = soa::Join; - Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & (uint32_t)o2::aod::track::PVContributor) == (uint32_t)o2::aod::track::PVContributor); + Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); void processMCCollisions(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles, soa::SmallGroups> const& collisions, Run3Tracks const&) { diff --git a/Common/Tasks/qVectorsCorrection.cxx b/Common/Tasks/qVectorsCorrection.cxx index f54bcfd29f4..600fac9ba47 100644 --- a/Common/Tasks/qVectorsCorrection.cxx +++ b/Common/Tasks/qVectorsCorrection.cxx @@ -39,13 +39,15 @@ #include +#include #include #include using namespace o2; using namespace o2::framework; -using MyCollisions = soa::Join; +using MySpCollisions = soa::Join; +using MyEseCollisions = soa::Join; using MyTracks = soa::Join; struct qVectorsCorrection { @@ -101,6 +103,26 @@ struct qVectorsCorrection { int RefAId; int RefBId; + template + float getQVecRe(TColl const& collision, int detId) + { + if constexpr (std::derived_from) { + return collision.eseQvecRe()[detId]; + } else { + return collision.qvecRe()[detId]; + } + } + + template + float getQVecIm(TColl const& collision, int detId) + { + if constexpr (std::derived_from) { + return collision.eseQvecIm()[detId]; + } else { + return collision.qvecIm()[detId]; + } + } + template int GetDetId(const T& name) { @@ -127,7 +149,7 @@ struct qVectorsCorrection { } template - bool SelTrack(const TrackType track) + bool SelTrack(const TrackType& track) { if (track.pt() < 0.15) return false; @@ -178,9 +200,9 @@ struct qVectorsCorrection { AxisSpec axisBasis = {20, 0, 20, "basis"}; AxisSpec axisVertex = {220, -11, 11, "vertex"}; - histosQA.add("histCentFull", "Centrality distribution for valid events", HistType::kTH1F, {axisCent}); - histosQA.add("histCentSelected", "Centrality distribution for valid events", HistType::kTH1F, {axisCent}); - histosQA.add("histVtxSelected", "Centrality distribution for valid events", HistType::kTH1F, {axisVertex}); + histosQA.add("histCentFull", "Centrality distribution for all events", HistType::kTH1F, {axisCent}); + histosQA.add("histCentSelected", "Centrality distribution for selected events", HistType::kTH1F, {axisCent}); + histosQA.add("histVtxSelected", "Z vtx coordinate distribution for valid events", HistType::kTH1F, {axisVertex}); for (uint i = 0; i < cfgnMods->size(); i++) { histosQA.add(Form("histQvecUncorV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); @@ -270,97 +292,42 @@ struct qVectorsCorrection { return; } + auto aTan2DetInd = TMath::ATan2(getQVecIm(vec, DetInd), getQVecRe(vec, DetInd)); + auto aTan2RefAInd = TMath::ATan2(getQVecIm(vec, RefAInd), getQVecRe(vec, RefAInd)); + auto aTan2RefBInd = TMath::ATan2(getQVecIm(vec, RefBInd), getQVecRe(vec, RefBInd)); + if (nmode == 2) { for (int ishift = 1; ishift <= 10; ishift++) { - histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * DetId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[DetInd], vec.qvecRe()[DetInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * DetId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[DetInd], vec.qvecRe()[DetInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * DetId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2DetInd)); + histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * DetId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2DetInd)); - histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefAId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefAInd], vec.qvecRe()[RefAInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefAId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefAInd], vec.qvecRe()[RefAInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefAId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2RefAInd)); + histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefAId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2RefAInd)); - histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefBId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefBInd], vec.qvecRe()[RefBInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefBId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefBInd], vec.qvecRe()[RefBInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefBId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2RefBInd)); + histosQA.fill(HIST("histShiftV2"), vec.cent(), 2.0 * RefBId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2RefBInd)); } } else if (nmode == 3) { for (int ishift = 1; ishift <= 10; ishift++) { - histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * DetId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[DetInd], vec.qvecRe()[DetInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * DetId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[DetInd], vec.qvecRe()[DetInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * DetId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2DetInd)); + histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * DetId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2DetInd)); - histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefAId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefAInd], vec.qvecRe()[RefAInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefAId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefAInd], vec.qvecRe()[RefAInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefAId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2RefAInd)); + histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefAId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2RefAInd)); - histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefBId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefBInd], vec.qvecRe()[RefBInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefBId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefBInd], vec.qvecRe()[RefBInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefBId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2RefBInd)); + histosQA.fill(HIST("histShiftV3"), vec.cent(), 2.0 * RefBId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2RefBInd)); } } else if (nmode == 4) { for (int ishift = 1; ishift <= 10; ishift++) { - histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * DetId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[DetInd], vec.qvecRe()[DetInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * DetId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[DetInd], vec.qvecRe()[DetInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * DetId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2DetInd)); + histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * DetId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2DetInd)); - histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefAId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefAInd], vec.qvecRe()[RefAInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefAId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefAInd], vec.qvecRe()[RefAInd]) / static_cast(nmode))); + histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefAId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2RefAInd)); + histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefAId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2RefAInd)); - histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefBId + 0.5, ishift - 0.5, TMath::Sin(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefBInd], vec.qvecRe()[RefBInd]) / static_cast(nmode))); - histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefBId + 1.5, ishift - 0.5, TMath::Cos(ishift * static_cast(nmode) * TMath::ATan2(vec.qvecIm()[RefBInd], vec.qvecRe()[RefBInd]) / static_cast(nmode))); - } - } - } - - template - void fillHistosFlowWithSC(const CollType& coll, const TrackType& track, int nmode) - { - int DetInd = DetId + cfgnTotalSystem * (nmode - 2); - int RefAInd = RefAId + cfgnTotalSystem * (nmode - 2); - int RefBInd = RefBId + cfgnTotalSystem * (nmode - 2); - - if (coll.qvecAmp()[DetId] < 1e-8 || coll.qvecAmp()[RefAId] < 1e-8 || coll.qvecAmp()[RefBId] < 1e-8) { - return; - } - - for (auto& trk : track) { - if (!SelTrack(trk)) { - continue; - } - - if (std::abs(trk.eta()) > 0.8) { - continue; - } - if (nmode == 2) { - histosQA.fill(HIST("hist_EP_cos_Det_v2"), coll.cent(), trk.pt(), std::cos(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_Det_v2"), coll.cent(), trk.pt(), std::sin(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_Det_v2"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - - histosQA.fill(HIST("hist_EP_cos_RefA_v2"), coll.cent(), trk.pt(), std::cos(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefA_v2"), coll.cent(), trk.pt(), std::sin(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefA_v2"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - - histosQA.fill(HIST("hist_EP_cos_RefB_v2"), coll.cent(), trk.pt(), std::cos(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefB_v2"), coll.cent(), trk.pt(), std::sin(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefB_v2"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - } else if (nmode == 3) { - histosQA.fill(HIST("hist_EP_cos_Det_v3"), coll.cent(), trk.pt(), std::cos(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_Det_v3"), coll.cent(), trk.pt(), std::sin(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_Det_v3"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - - histosQA.fill(HIST("hist_EP_cos_RefA_v3"), coll.cent(), trk.pt(), std::cos(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefA_v3"), coll.cent(), trk.pt(), std::sin(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefA_v3"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - - histosQA.fill(HIST("hist_EP_cos_RefB_v3"), coll.cent(), trk.pt(), std::cos(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefB_v3"), coll.cent(), trk.pt(), std::sin(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefB_v3"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - } else if (nmode == 4) { - histosQA.fill(HIST("hist_EP_cos_Det_v4"), coll.cent(), trk.pt(), std::cos(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_Det_v4"), coll.cent(), trk.pt(), std::sin(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_Det_v4"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[DetInd], coll.qvecShiftedIm()[DetInd], nmode)))); - - histosQA.fill(HIST("hist_EP_cos_RefA_v4"), coll.cent(), trk.pt(), std::cos(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefA_v4"), coll.cent(), trk.pt(), std::sin(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefA_v4"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefAInd], coll.qvecShiftedIm()[RefAInd], nmode)))); - - histosQA.fill(HIST("hist_EP_cos_RefB_v4"), coll.cent(), trk.pt(), std::cos(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefB_v4"), coll.cent(), trk.pt(), std::sin(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefB_v4"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecShiftedRe()[RefBInd], coll.qvecShiftedIm()[RefBInd], nmode)))); + histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefBId + 0.5, ishift - 0.5, TMath::Sin(ishift * aTan2RefBInd)); + histosQA.fill(HIST("histShiftV4"), vec.cent(), 2.0 * RefBId + 1.5, ishift - 0.5, TMath::Cos(ishift * aTan2RefBInd)); } } } @@ -384,99 +351,53 @@ struct qVectorsCorrection { continue; } + auto trkPhi = trk.phi(); + auto trkPt = trk.pt(); + auto cent = coll.cent(); + auto epDetInd = helperEP.GetEventPlane(getQVecRe(coll, DetInd + 3), getQVecIm(coll, DetInd + 3), nmode); + auto epRefAInd = helperEP.GetEventPlane(getQVecRe(coll, RefAInd + 3), getQVecIm(coll, RefAInd + 3), nmode); + auto epRefBInd = helperEP.GetEventPlane(getQVecRe(coll, RefBInd + 3), getQVecIm(coll, RefBInd + 3), nmode); + if (nmode == 2) { - histosQA.fill(HIST("hist_EP_cos_Det_v2"), coll.cent(), trk.pt(), std::cos(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_Det_v2"), coll.cent(), trk.pt(), std::sin(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_Det_v2"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_Det_v2"), cent, trkPt, std::cos(2.0 * (trkPhi - epDetInd))); + histosQA.fill(HIST("hist_EP_sin_Det_v2"), cent, trkPt, std::sin(2.0 * (trkPhi - epDetInd))); + histosQA.fill(HIST("hist_EP_azimuth_Det_v2"), cent, trkPt, TVector2::Phi_0_2pi(2.0 * (trkPhi - epDetInd))); - histosQA.fill(HIST("hist_EP_cos_RefA_v2"), coll.cent(), trk.pt(), std::cos(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefA_v2"), coll.cent(), trk.pt(), std::sin(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefA_v2"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_RefA_v2"), cent, trkPt, std::cos(2.0 * (trkPhi - epRefAInd))); + histosQA.fill(HIST("hist_EP_sin_RefA_v2"), cent, trkPt, std::sin(2.0 * (trkPhi - epRefAInd))); + histosQA.fill(HIST("hist_EP_azimuth_RefA_v2"), cent, trkPt, TVector2::Phi_0_2pi(2.0 * (trkPhi - epRefAInd))); - histosQA.fill(HIST("hist_EP_cos_RefB_v2"), coll.cent(), trk.pt(), std::cos(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefB_v2"), coll.cent(), trk.pt(), std::sin(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefB_v2"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(2.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_RefB_v2"), cent, trkPt, std::cos(2.0 * (trkPhi - epRefBInd))); + histosQA.fill(HIST("hist_EP_sin_RefB_v2"), cent, trkPt, std::sin(2.0 * (trkPhi - epRefBInd))); + histosQA.fill(HIST("hist_EP_azimuth_RefB_v2"), cent, trkPt, TVector2::Phi_0_2pi(2.0 * (trkPhi - epRefBInd))); } else if (nmode == 3) { - histosQA.fill(HIST("hist_EP_cos_Det_v3"), coll.cent(), trk.pt(), std::cos(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_Det_v3"), coll.cent(), trk.pt(), std::sin(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_Det_v3"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_Det_v3"), cent, trkPt, std::cos(3.0 * (trkPhi - epDetInd))); + histosQA.fill(HIST("hist_EP_sin_Det_v3"), cent, trkPt, std::sin(3.0 * (trkPhi - epDetInd))); + histosQA.fill(HIST("hist_EP_azimuth_Det_v3"), cent, trkPt, TVector2::Phi_0_2pi(3.0 * (trkPhi - epDetInd))); - histosQA.fill(HIST("hist_EP_cos_RefA_v3"), coll.cent(), trk.pt(), std::cos(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefA_v3"), coll.cent(), trk.pt(), std::sin(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefA_v3"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_RefA_v3"), cent, trkPt, std::cos(3.0 * (trkPhi - epRefAInd))); + histosQA.fill(HIST("hist_EP_sin_RefA_v3"), cent, trkPt, std::sin(3.0 * (trkPhi - epRefAInd))); + histosQA.fill(HIST("hist_EP_azimuth_RefA_v3"), cent, trkPt, TVector2::Phi_0_2pi(3.0 * (trkPhi - epRefAInd))); - histosQA.fill(HIST("hist_EP_cos_RefB_v3"), coll.cent(), trk.pt(), std::cos(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefB_v3"), coll.cent(), trk.pt(), std::sin(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefB_v3"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(3.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_RefB_v3"), cent, trkPt, std::cos(3.0 * (trkPhi - epRefBInd))); + histosQA.fill(HIST("hist_EP_sin_RefB_v3"), cent, trkPt, std::sin(3.0 * (trkPhi - epRefBInd))); + histosQA.fill(HIST("hist_EP_azimuth_RefB_v3"), cent, trkPt, TVector2::Phi_0_2pi(3.0 * (trkPhi - epRefBInd))); } else if (nmode == 4) { - histosQA.fill(HIST("hist_EP_cos_Det_v4"), coll.cent(), trk.pt(), std::cos(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_Det_v4"), coll.cent(), trk.pt(), std::sin(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_Det_v4"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[DetInd + 3], coll.qvecIm()[DetInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_Det_v4"), cent, trkPt, std::cos(4.0 * (trkPhi - epDetInd))); + histosQA.fill(HIST("hist_EP_sin_Det_v4"), cent, trkPt, std::sin(4.0 * (trkPhi - epDetInd))); + histosQA.fill(HIST("hist_EP_azimuth_Det_v4"), cent, trkPt, TVector2::Phi_0_2pi(4.0 * (trkPhi - epDetInd))); - histosQA.fill(HIST("hist_EP_cos_RefA_v4"), coll.cent(), trk.pt(), std::cos(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefA_v4"), coll.cent(), trk.pt(), std::sin(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefA_v4"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefAInd + 3], coll.qvecIm()[RefAInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_RefA_v4"), cent, trkPt, std::cos(4.0 * (trkPhi - epRefAInd))); + histosQA.fill(HIST("hist_EP_sin_RefA_v4"), cent, trkPt, std::sin(4.0 * (trkPhi - epRefAInd))); + histosQA.fill(HIST("hist_EP_azimuth_RefA_v4"), cent, trkPt, TVector2::Phi_0_2pi(4.0 * (trkPhi - epRefAInd))); - histosQA.fill(HIST("hist_EP_cos_RefB_v4"), coll.cent(), trk.pt(), std::cos(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_sin_RefB_v4"), coll.cent(), trk.pt(), std::sin(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); - histosQA.fill(HIST("hist_EP_azimuth_RefB_v4"), coll.cent(), trk.pt(), TVector2::Phi_0_2pi(4.0 * (trk.phi() - helperEP.GetEventPlane(coll.qvecRe()[RefBInd + 3], coll.qvecIm()[RefBInd + 3], nmode)))); + histosQA.fill(HIST("hist_EP_cos_RefB_v4"), cent, trkPt, std::cos(4.0 * (trkPhi - epRefBInd))); + histosQA.fill(HIST("hist_EP_sin_RefB_v4"), cent, trkPt, std::sin(4.0 * (trkPhi - epRefBInd))); + histosQA.fill(HIST("hist_EP_azimuth_RefB_v4"), cent, trkPt, TVector2::Phi_0_2pi(4.0 * (trkPhi - epRefBInd))); } } } - template - void fillHistosQvecWithSC(const T& vec, int nmode) - { - int DetInd = DetId + cfgnTotalSystem * (nmode - 2); - int RefAInd = RefAId + cfgnTotalSystem * (nmode - 2); - int RefBInd = RefBId + cfgnTotalSystem * (nmode - 2); - - if (vec.qvecAmp()[DetId] < 1e-8 || vec.qvecAmp()[RefAId] < 1e-8 || vec.qvecAmp()[RefBId] < 1e-8) { - return; - } - - if (nmode == 2) { - histosQA.fill(HIST("histQvecFinalV2"), vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], vec.cent()); - histosQA.fill(HIST("histEvtPlFinalV2"), helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), vec.cent()); - - histosQA.fill(HIST("histQvecRefAFinalV2"), vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAFinalV2"), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), vec.cent()); - - histosQA.fill(HIST("histQvecRefBFinalV2"), vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBFinalV2"), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), vec.cent()); - - histosQA.fill(HIST("histEvtPlRes_SigRefAV2"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_SigRefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), nmode), vec.cent()); - } else if (nmode == 3) { - histosQA.fill(HIST("histQvecFinalV3"), vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], vec.cent()); - histosQA.fill(HIST("histEvtPlFinalV3"), helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), vec.cent()); - - histosQA.fill(HIST("histQvecRefAFinalV3"), vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAFinalV3"), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), vec.cent()); - - histosQA.fill(HIST("histQvecRefBFinalV3"), vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBFinalV3"), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), vec.cent()); - - histosQA.fill(HIST("histEvtPlRes_SigRefAV3"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_SigRefBV3"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_RefARefBV3"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), nmode), vec.cent()); - } else if (nmode == 4) { - histosQA.fill(HIST("histQvecFinalV4"), vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], vec.cent()); - histosQA.fill(HIST("histEvtPlFinalV4"), helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), vec.cent()); - - histosQA.fill(HIST("histQvecRefAFinalV4"), vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAFinalV4"), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), vec.cent()); - - histosQA.fill(HIST("histQvecRefBFinalV4"), vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBFinalV4"), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), vec.cent()); - - histosQA.fill(HIST("histEvtPlRes_SigRefAV4"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_SigRefBV4"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[DetInd], vec.qvecShiftedIm()[DetInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_RefARefBV4"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecShiftedRe()[RefAInd], vec.qvecShiftedIm()[RefAInd], nmode), helperEP.GetEventPlane(vec.qvecShiftedRe()[RefBInd], vec.qvecShiftedIm()[RefBInd], nmode), nmode), vec.cent()); - } - } - // Definition of all the needed template functions. template void fillHistosQvec(const T& vec, int nmode) @@ -484,275 +405,305 @@ struct qVectorsCorrection { int DetInd = DetId * 4 + cfgnTotalSystem * 4 * (nmode - 2); int RefAInd = RefAId * 4 + cfgnTotalSystem * 4 * (nmode - 2); int RefBInd = RefBId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + + auto epDetInd = helperEP.GetEventPlane(getQVecRe(vec, DetInd), getQVecIm(vec, DetInd), nmode); + auto epDetIndRectr = helperEP.GetEventPlane(getQVecRe(vec, DetInd + 1), getQVecIm(vec, DetInd + 1), nmode); + auto epDetIndTwist = helperEP.GetEventPlane(getQVecRe(vec, DetInd + 2), getQVecIm(vec, DetInd + 2), nmode); + auto epDetIndFinal = helperEP.GetEventPlane(getQVecRe(vec, DetInd + 3), getQVecIm(vec, DetInd + 3), nmode); + auto epRefAInd = helperEP.GetEventPlane(getQVecRe(vec, RefAInd), getQVecIm(vec, RefAInd), nmode); + auto epRefARectrInd = helperEP.GetEventPlane(getQVecRe(vec, RefAInd + 1), getQVecIm(vec, RefAInd + 1), nmode); + auto epRefATwistInd = helperEP.GetEventPlane(getQVecRe(vec, RefAInd + 2), getQVecIm(vec, RefAInd + 2), nmode); + auto epRefAFinalInd = helperEP.GetEventPlane(getQVecRe(vec, RefAInd + 3), getQVecIm(vec, RefAInd + 3), nmode); + auto epRefBInd = helperEP.GetEventPlane(getQVecRe(vec, RefBInd), getQVecIm(vec, RefBInd), nmode); + auto epRefBRectrInd = helperEP.GetEventPlane(getQVecRe(vec, RefBInd + 1), getQVecIm(vec, RefBInd + 1), nmode); + auto epRefBTwistInd = helperEP.GetEventPlane(getQVecRe(vec, RefBInd + 2), getQVecIm(vec, RefBInd + 2), nmode); + auto epRefBFinalInd = helperEP.GetEventPlane(getQVecRe(vec, RefBInd + 3), getQVecIm(vec, RefBInd + 3), nmode); + + auto occ = vec.trackOccupancyInTimeRange(); + auto cent = vec.cent(); + if (nmode == 2) { if (vec.qvecAmp()[DetId] > 1e-8) { - histosQA.fill(HIST("histQvecUncorV2"), vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], vec.cent()); - histosQA.fill(HIST("histEvtPlUncorV2"), helperEP.GetEventPlane(vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecUncorV2"), getQVecRe(vec, DetInd), getQVecIm(vec, DetInd), cent); + histosQA.fill(HIST("histEvtPlUncorV2"), epDetInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecOccUncorV2"), vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecOccUncorV2"), getQVecRe(vec, DetInd), getQVecIm(vec, DetInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecFinalV2"), vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlFinalV2"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecFinalV2"), getQVecRe(vec, DetInd + 3), getQVecIm(vec, DetInd + 3), cent); + histosQA.fill(HIST("histEvtPlFinalV2"), epDetIndFinal, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecOccFinalV2"), vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecOccFinalV2"), getQVecRe(vec, DetInd + 3), getQVecIm(vec, DetInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRectrV2"), vec.qvecRe()[DetInd + 1], vec.qvecIm()[DetInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecTwistV2"), vec.qvecRe()[DetInd + 2], vec.qvecIm()[DetInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRectrV2"), getQVecRe(vec, DetInd + 1), getQVecIm(vec, DetInd + 1), cent); + histosQA.fill(HIST("histQvecTwistV2"), getQVecRe(vec, DetInd + 2), getQVecIm(vec, DetInd + 2), cent); - histosQA.fill(HIST("histEvtPlRectrV2"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 1], vec.qvecIm()[DetInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlTwistV2"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 2], vec.qvecIm()[DetInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRectrV2"), epDetIndRectr, cent); + histosQA.fill(HIST("histEvtPlTwistV2"), epDetIndTwist, cent); } } } if (vec.qvecAmp()[RefAId] > 1e-8) { - histosQA.fill(HIST("histQvecRefAUncorV2"), vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAUncorV2"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefAUncorV2"), getQVecRe(vec, RefAInd), getQVecIm(vec, RefAInd), cent); + histosQA.fill(HIST("histEvtPlRefAUncorV2"), epRefAInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefAOccUncorV2"), vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefAOccUncorV2"), getQVecRe(vec, RefAInd), getQVecIm(vec, RefAInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecRefAFinalV2"), vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAFinalV2"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefAFinalV2"), getQVecRe(vec, RefAInd + 3), getQVecIm(vec, RefAInd + 3), cent); + histosQA.fill(HIST("histEvtPlRefAFinalV2"), epRefAFinalInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefAOccFinalV2"), vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefAOccFinalV2"), getQVecRe(vec, RefAInd + 3), getQVecIm(vec, RefAInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRefARectrV2"), vec.qvecRe()[RefAInd + 1], vec.qvecIm()[RefAInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecRefATwistV2"), vec.qvecRe()[RefAInd + 2], vec.qvecIm()[RefAInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRefARectrV2"), getQVecRe(vec, RefAInd + 1), getQVecIm(vec, RefAInd + 1), cent); + histosQA.fill(HIST("histQvecRefATwistV2"), getQVecRe(vec, RefAInd + 2), getQVecIm(vec, RefAInd + 2), cent); - histosQA.fill(HIST("histEvtPlRefARectrV2"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 1], vec.qvecIm()[RefAInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRefATwistV2"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 2], vec.qvecIm()[RefAInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRefARectrV2"), epRefARectrInd, cent); + histosQA.fill(HIST("histEvtPlRefATwistV2"), epRefATwistInd, cent); } } } if (vec.qvecAmp()[RefBId] > 1e-8) { - histosQA.fill(HIST("histQvecRefBUncorV2"), vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBUncorV2"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefBUncorV2"), getQVecRe(vec, RefBInd), getQVecIm(vec, RefBInd), cent); + histosQA.fill(HIST("histEvtPlRefBUncorV2"), epRefBInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefBOccUncorV2"), vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefBOccUncorV2"), getQVecRe(vec, RefBInd), getQVecIm(vec, RefBInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecRefBFinalV2"), vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBFinalV2"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefBFinalV2"), getQVecRe(vec, RefBInd + 3), getQVecIm(vec, RefBInd + 3), cent); + histosQA.fill(HIST("histEvtPlRefBFinalV2"), epRefBFinalInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefBOccFinalV2"), vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefBOccFinalV2"), getQVecRe(vec, RefBInd + 3), getQVecIm(vec, RefBInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRefBRectrV2"), vec.qvecRe()[RefBInd + 1], vec.qvecIm()[RefBInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecRefBTwistV2"), vec.qvecRe()[RefBInd + 2], vec.qvecIm()[RefBInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRefBRectrV2"), getQVecRe(vec, RefBInd + 1), getQVecIm(vec, RefBInd + 1), cent); + histosQA.fill(HIST("histQvecRefBTwistV2"), getQVecRe(vec, RefBInd + 2), getQVecIm(vec, RefBInd + 2), cent); - histosQA.fill(HIST("histEvtPlRefBRectrV2"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 1], vec.qvecIm()[RefBInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRefBTwistV2"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 2], vec.qvecIm()[RefBInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRefBRectrV2"), epRefBRectrInd, cent); + histosQA.fill(HIST("histEvtPlRefBTwistV2"), epRefBTwistInd, cent); } } } if (vec.qvecAmp()[DetId] > 1e-8 && vec.qvecAmp()[RefAId] > 1e-8 && vec.qvecAmp()[RefBId] > 1e-8 && cfgQAFinal) { - histosQA.fill(HIST("histQvecRes_SigRefAV2"), vec.qvecRe()[DetInd + 3] * vec.qvecRe()[RefAInd + 3] + vec.qvecIm()[DetInd + 3] * vec.qvecIm()[RefAInd + 3], vec.cent()); - histosQA.fill(HIST("histQvecRes_SigRefBV2"), vec.qvecRe()[DetInd + 3] * vec.qvecRe()[RefBInd + 3] + vec.qvecIm()[DetInd + 3] * vec.qvecIm()[RefBInd + 3], vec.cent()); - histosQA.fill(HIST("histQvecRes_RefARefBV2"), vec.qvecRe()[RefAInd + 3] * vec.qvecRe()[RefBInd + 3] + vec.qvecIm()[RefAInd + 3] * vec.qvecIm()[RefBInd + 3], vec.cent()); + histosQA.fill(HIST("histQvecRes_SigRefAV2"), getQVecRe(vec, DetInd + 3) * getQVecRe(vec, RefAInd + 3) + getQVecIm(vec, DetInd + 3) * getQVecIm(vec, RefAInd + 3), cent); + histosQA.fill(HIST("histQvecRes_SigRefBV2"), getQVecRe(vec, DetInd + 3) * getQVecRe(vec, RefBInd + 3) + getQVecIm(vec, DetInd + 3) * getQVecIm(vec, RefBInd + 3), cent); + histosQA.fill(HIST("histQvecRes_RefARefBV2"), getQVecRe(vec, RefAInd + 3) * getQVecRe(vec, RefBInd + 3) + getQVecIm(vec, RefAInd + 3) * getQVecIm(vec, RefBInd + 3), cent); - histosQA.fill(HIST("histEvtPlRes_SigRefAV2"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_SigRefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRes_SigRefAV2"), helperEP.GetResolution(epDetIndFinal, epRefAFinalInd, nmode), cent); + histosQA.fill(HIST("histEvtPlRes_SigRefBV2"), helperEP.GetResolution(epDetIndFinal, epRefBFinalInd, nmode), cent); + histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(epRefAFinalInd, epRefBFinalInd, nmode), cent); } } else if (nmode == 3) { if (vec.qvecAmp()[DetId] > 1e-8) { - histosQA.fill(HIST("histQvecUncorV3"), vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], vec.cent()); - histosQA.fill(HIST("histEvtPlUncorV3"), helperEP.GetEventPlane(vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecUncorV3"), getQVecRe(vec, DetInd), getQVecIm(vec, DetInd), cent); + histosQA.fill(HIST("histEvtPlUncorV3"), epDetInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecOccUncorV3"), vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecOccUncorV3"), getQVecRe(vec, DetInd), getQVecIm(vec, DetInd), cent, occ); } if (cfgQAFinal) { if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecOccFinalV3"), vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecOccFinalV3"), getQVecRe(vec, DetInd + 3), getQVecIm(vec, DetInd + 3), cent, occ); } - histosQA.fill(HIST("histQvecFinalV3"), vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlFinalV3"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecFinalV3"), getQVecRe(vec, DetInd + 3), getQVecIm(vec, DetInd + 3), cent); + histosQA.fill(HIST("histEvtPlFinalV3"), epDetIndFinal, cent); if (cfgQAAll) { - histosQA.fill(HIST("histQvecRectrV3"), vec.qvecRe()[DetInd + 1], vec.qvecIm()[DetInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecTwistV3"), vec.qvecRe()[DetInd + 2], vec.qvecIm()[DetInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRectrV3"), getQVecRe(vec, DetInd + 1), getQVecIm(vec, DetInd + 1), cent); + histosQA.fill(HIST("histQvecTwistV3"), getQVecRe(vec, DetInd + 2), getQVecIm(vec, DetInd + 2), cent); - histosQA.fill(HIST("histEvtPlRectrV3"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 1], vec.qvecIm()[DetInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlTwistV3"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 2], vec.qvecIm()[DetInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRectrV3"), epDetIndRectr, cent); + histosQA.fill(HIST("histEvtPlTwistV3"), epDetIndTwist, cent); } } } if (vec.qvecAmp()[RefAId] > 1e-8) { - histosQA.fill(HIST("histQvecRefAUncorV3"), vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAUncorV3"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefAUncorV3"), getQVecRe(vec, RefAInd), getQVecIm(vec, RefAInd), cent); + histosQA.fill(HIST("histEvtPlRefAUncorV3"), epRefAInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefAOccUncorV3"), vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefAOccUncorV3"), getQVecRe(vec, RefAInd), getQVecIm(vec, RefAInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecRefAFinalV3"), vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAFinalV3"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefAFinalV3"), getQVecRe(vec, RefAInd + 3), getQVecIm(vec, RefAInd + 3), cent); + histosQA.fill(HIST("histEvtPlRefAFinalV3"), epRefAFinalInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefAOccFinalV3"), vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefAOccFinalV3"), getQVecRe(vec, RefAInd + 3), getQVecIm(vec, RefAInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRefARectrV3"), vec.qvecRe()[RefAInd + 1], vec.qvecIm()[RefAInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecRefATwistV3"), vec.qvecRe()[RefAInd + 2], vec.qvecIm()[RefAInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRefARectrV3"), getQVecRe(vec, RefAInd + 1), getQVecIm(vec, RefAInd + 1), cent); + histosQA.fill(HIST("histQvecRefATwistV3"), getQVecRe(vec, RefAInd + 2), getQVecIm(vec, RefAInd + 2), cent); - histosQA.fill(HIST("histEvtPlRefARectrV3"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 1], vec.qvecIm()[RefAInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRefATwistV3"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 2], vec.qvecIm()[RefAInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRefARectrV3"), epRefARectrInd, cent); + histosQA.fill(HIST("histEvtPlRefATwistV3"), epRefATwistInd, cent); } } } if (vec.qvecAmp()[RefBId] > 1e-8) { - histosQA.fill(HIST("histQvecRefBUncorV3"), vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBUncorV3"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefBUncorV3"), getQVecRe(vec, RefBInd), getQVecIm(vec, RefBInd), cent); + histosQA.fill(HIST("histEvtPlRefBUncorV3"), epRefBInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefBOccUncorV3"), vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefBOccUncorV3"), getQVecRe(vec, RefBInd), getQVecIm(vec, RefBInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecRefBFinalV3"), vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBFinalV3"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefBFinalV3"), getQVecRe(vec, RefBInd + 3), getQVecIm(vec, RefBInd + 3), cent); + histosQA.fill(HIST("histEvtPlRefBFinalV3"), epRefBFinalInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefBOccFinalV3"), vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefBOccFinalV3"), getQVecRe(vec, RefBInd + 3), getQVecIm(vec, RefBInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRefBRectrV3"), vec.qvecRe()[RefBInd + 1], vec.qvecIm()[RefBInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecRefBTwistV3"), vec.qvecRe()[RefBInd + 2], vec.qvecIm()[RefBInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRefBRectrV3"), getQVecRe(vec, RefBInd + 1), getQVecIm(vec, RefBInd + 1), cent); + histosQA.fill(HIST("histQvecRefBTwistV3"), getQVecRe(vec, RefBInd + 2), getQVecIm(vec, RefBInd + 2), cent); - histosQA.fill(HIST("histEvtPlRefBRectrV3"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 1], vec.qvecIm()[RefBInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRefBTwistV3"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 2], vec.qvecIm()[RefBInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRefBRectrV3"), epRefBRectrInd, cent); + histosQA.fill(HIST("histEvtPlRefBTwistV3"), epRefBTwistInd, cent); } } } if (vec.qvecAmp()[DetId] > 1e-8 && vec.qvecAmp()[RefAId] > 1e-8 && vec.qvecAmp()[RefBId] > 1e-8 && cfgQAFinal) { - histosQA.fill(HIST("histQvecRes_SigRefAV3"), vec.qvecRe()[DetInd + 3] * vec.qvecRe()[RefAInd + 3] + vec.qvecIm()[DetInd + 3] * vec.qvecIm()[RefAInd + 3], vec.cent()); - histosQA.fill(HIST("histQvecRes_SigRefBV3"), vec.qvecRe()[DetInd + 3] * vec.qvecRe()[RefBInd + 3] + vec.qvecIm()[DetInd + 3] * vec.qvecIm()[RefBInd + 3], vec.cent()); - histosQA.fill(HIST("histQvecRes_RefARefBV3"), vec.qvecRe()[RefAInd + 3] * vec.qvecRe()[RefBInd + 3] + vec.qvecIm()[RefAInd + 3] * vec.qvecIm()[RefBInd + 3], vec.cent()); + histosQA.fill(HIST("histQvecRes_SigRefAV3"), getQVecRe(vec, DetInd + 3) * getQVecRe(vec, RefAInd + 3) + getQVecIm(vec, DetInd + 3) * getQVecIm(vec, RefAInd + 3), cent); + histosQA.fill(HIST("histQvecRes_SigRefBV3"), getQVecRe(vec, DetInd + 3) * getQVecRe(vec, RefBInd + 3) + getQVecIm(vec, DetInd + 3) * getQVecIm(vec, RefBInd + 3), cent); + histosQA.fill(HIST("histQvecRes_RefARefBV3"), getQVecRe(vec, RefAInd + 3) * getQVecRe(vec, RefBInd + 3) + getQVecIm(vec, RefAInd + 3) * getQVecIm(vec, RefBInd + 3), cent); - histosQA.fill(HIST("histEvtPlRes_SigRefAV3"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_SigRefBV3"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_RefARefBV3"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRes_SigRefAV3"), helperEP.GetResolution(epDetIndFinal, epRefAFinalInd, nmode), cent); + histosQA.fill(HIST("histEvtPlRes_SigRefBV3"), helperEP.GetResolution(epDetIndFinal, epRefBFinalInd, nmode), cent); + histosQA.fill(HIST("histEvtPlRes_RefARefBV3"), helperEP.GetResolution(epRefAFinalInd, epRefBFinalInd, nmode), cent); } } else if (nmode == 4) { if (vec.qvecAmp()[DetId] > 1e-8) { - histosQA.fill(HIST("histQvecUncorV4"), vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], vec.cent()); - histosQA.fill(HIST("histEvtPlUncorV4"), helperEP.GetEventPlane(vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecUncorV4"), getQVecRe(vec, DetInd), getQVecIm(vec, DetInd), cent); + histosQA.fill(HIST("histEvtPlUncorV4"), epDetInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecOccUncorV4"), vec.qvecRe()[DetInd], vec.qvecIm()[DetInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecOccUncorV4"), getQVecRe(vec, DetInd), getQVecIm(vec, DetInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecFinalV4"), vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlFinalV4"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecFinalV4"), getQVecRe(vec, DetInd + 3), getQVecIm(vec, DetInd + 3), cent); + histosQA.fill(HIST("histEvtPlFinalV4"), epDetIndFinal, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecOccFinalV4"), vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecOccFinalV4"), getQVecRe(vec, DetInd + 3), getQVecIm(vec, DetInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRectrV4"), vec.qvecRe()[DetInd + 1], vec.qvecIm()[DetInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecTwistV4"), vec.qvecRe()[DetInd + 2], vec.qvecIm()[DetInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRectrV4"), getQVecRe(vec, DetInd + 1), getQVecIm(vec, DetInd + 1), cent); + histosQA.fill(HIST("histQvecTwistV4"), getQVecRe(vec, DetInd + 2), getQVecIm(vec, DetInd + 2), cent); - histosQA.fill(HIST("histEvtPlRectrV4"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 1], vec.qvecIm()[DetInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlTwistV4"), helperEP.GetEventPlane(vec.qvecRe()[DetInd + 2], vec.qvecIm()[DetInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRectrV4"), epDetIndRectr, cent); + histosQA.fill(HIST("histEvtPlTwistV4"), epDetIndTwist, cent); } } } if (vec.qvecAmp()[RefAId] > 1e-8) { - histosQA.fill(HIST("histQvecRefAUncorV4"), vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAUncorV4"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefAUncorV4"), getQVecRe(vec, RefAInd), getQVecIm(vec, RefAInd), cent); + histosQA.fill(HIST("histEvtPlRefAUncorV4"), epRefAInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefAOccUncorV4"), vec.qvecRe()[RefAInd], vec.qvecIm()[RefAInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefAOccUncorV4"), getQVecRe(vec, RefAInd), getQVecIm(vec, RefAInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecRefAFinalV4"), vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlRefAFinalV4"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefAFinalV4"), getQVecRe(vec, RefAInd + 3), getQVecIm(vec, RefAInd + 3), cent); + histosQA.fill(HIST("histEvtPlRefAFinalV4"), epRefAFinalInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefAOccFinalV4"), vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefAOccFinalV4"), getQVecRe(vec, RefAInd + 3), getQVecIm(vec, RefAInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRefARectrV4"), vec.qvecRe()[RefAInd + 1], vec.qvecIm()[RefAInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecRefATwistV4"), vec.qvecRe()[RefAInd + 2], vec.qvecIm()[RefAInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRefARectrV4"), getQVecRe(vec, RefAInd + 1), getQVecIm(vec, RefAInd + 1), cent); + histosQA.fill(HIST("histQvecRefATwistV4"), getQVecRe(vec, RefAInd + 2), getQVecIm(vec, RefAInd + 2), cent); - histosQA.fill(HIST("histEvtPlRefARectrV4"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 1], vec.qvecIm()[RefAInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRefATwistV4"), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 2], vec.qvecIm()[RefAInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRefARectrV4"), epRefARectrInd, cent); + histosQA.fill(HIST("histEvtPlRefATwistV4"), epRefATwistInd, cent); } } } if (vec.qvecAmp()[RefBId] > 1e-8) { - histosQA.fill(HIST("histQvecRefBUncorV4"), vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBUncorV4"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefBUncorV4"), getQVecRe(vec, RefBInd), getQVecIm(vec, RefBInd), cent); + histosQA.fill(HIST("histEvtPlRefBUncorV4"), epRefBInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefBOccUncorV4"), vec.qvecRe()[RefBInd], vec.qvecIm()[RefBInd], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefBOccUncorV4"), getQVecRe(vec, RefBInd), getQVecIm(vec, RefBInd), cent, occ); } if (cfgQAFinal) { - histosQA.fill(HIST("histQvecRefBFinalV4"), vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], vec.cent()); - histosQA.fill(HIST("histEvtPlRefBFinalV4"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), vec.cent()); + histosQA.fill(HIST("histQvecRefBFinalV4"), getQVecRe(vec, RefBInd + 3), getQVecIm(vec, RefBInd + 3), cent); + histosQA.fill(HIST("histEvtPlRefBFinalV4"), epRefBFinalInd, cent); if (cfgQAOccupancyStudy) { - histosQA.fill(HIST("histQvecRefBOccFinalV4"), vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], vec.cent(), vec.trackOccupancyInTimeRange()); + histosQA.fill(HIST("histQvecRefBOccFinalV4"), getQVecRe(vec, RefBInd + 3), getQVecIm(vec, RefBInd + 3), cent, occ); } if (cfgQAAll) { - histosQA.fill(HIST("histQvecRefBRectrV4"), vec.qvecRe()[RefBInd + 1], vec.qvecIm()[RefBInd + 1], vec.cent()); - histosQA.fill(HIST("histQvecRefBTwistV4"), vec.qvecRe()[RefBInd + 2], vec.qvecIm()[RefBInd + 2], vec.cent()); + histosQA.fill(HIST("histQvecRefBRectrV4"), getQVecRe(vec, RefBInd + 1), getQVecIm(vec, RefBInd + 1), cent); + histosQA.fill(HIST("histQvecRefBTwistV4"), getQVecRe(vec, RefBInd + 2), getQVecIm(vec, RefBInd + 2), cent); - histosQA.fill(HIST("histEvtPlRefBRectrV4"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 1], vec.qvecIm()[RefBInd + 1], nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRefBTwistV4"), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 2], vec.qvecIm()[RefBInd + 2], nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRefBRectrV4"), epRefBRectrInd, cent); + histosQA.fill(HIST("histEvtPlRefBTwistV4"), epRefBTwistInd, cent); } } } if (vec.qvecAmp()[DetId] > 1e-8 && vec.qvecAmp()[RefAId] > 1e-8 && vec.qvecAmp()[RefBId] > 1e-8 && cfgQAFinal) { - histosQA.fill(HIST("histQvecRes_SigRefAV4"), vec.qvecRe()[DetInd + 3] * vec.qvecRe()[RefAInd + 3] + vec.qvecIm()[DetInd + 3] * vec.qvecIm()[RefAInd + 3], vec.cent()); - histosQA.fill(HIST("histQvecRes_SigRefBV4"), vec.qvecRe()[DetInd + 3] * vec.qvecRe()[RefBInd + 3] + vec.qvecIm()[DetInd + 3] * vec.qvecIm()[RefBInd + 3], vec.cent()); - histosQA.fill(HIST("histQvecRes_RefARefBV4"), vec.qvecRe()[RefAInd + 3] * vec.qvecRe()[RefBInd + 3] + vec.qvecIm()[RefAInd + 3] * vec.qvecIm()[RefBInd + 3], vec.cent()); + histosQA.fill(HIST("histQvecRes_SigRefAV4"), getQVecRe(vec, DetInd + 3) * getQVecRe(vec, RefAInd + 3) + getQVecIm(vec, DetInd + 3) * getQVecIm(vec, RefAInd + 3), cent); + histosQA.fill(HIST("histQvecRes_SigRefBV4"), getQVecRe(vec, DetInd + 3) * getQVecRe(vec, RefBInd + 3) + getQVecIm(vec, DetInd + 3) * getQVecIm(vec, RefBInd + 3), cent); + histosQA.fill(HIST("histQvecRes_RefARefBV4"), getQVecRe(vec, RefAInd + 3) * getQVecRe(vec, RefBInd + 3) + getQVecIm(vec, RefAInd + 3) * getQVecIm(vec, RefBInd + 3), cent); - histosQA.fill(HIST("histEvtPlRes_SigRefAV4"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_SigRefBV4"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[DetInd + 3], vec.qvecIm()[DetInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), nmode), vec.cent()); - histosQA.fill(HIST("histEvtPlRes_RefARefBV4"), helperEP.GetResolution(helperEP.GetEventPlane(vec.qvecRe()[RefAInd + 3], vec.qvecIm()[RefAInd + 3], nmode), helperEP.GetEventPlane(vec.qvecRe()[RefBInd + 3], vec.qvecIm()[RefBInd + 3], nmode), nmode), vec.cent()); + histosQA.fill(HIST("histEvtPlRes_SigRefAV4"), helperEP.GetResolution(epDetIndFinal, epRefAFinalInd, nmode), cent); + histosQA.fill(HIST("histEvtPlRes_SigRefBV4"), helperEP.GetResolution(epDetIndFinal, epRefBFinalInd, nmode), cent); + histosQA.fill(HIST("histEvtPlRes_RefARefBV4"), helperEP.GetResolution(epRefAFinalInd, epRefBFinalInd, nmode), cent); } } } - void processDefault(MyCollisions::iterator const& qVec, MyTracks const& tracks) + + template + void processColls(TCollision const& flowQVec, MyTracks const& tracks) { - histosQA.fill(HIST("histCentFull"), qVec.cent()); + histosQA.fill(HIST("histCentFull"), flowQVec.cent()); if (cfgAddEvtSel) { - if (std::abs(qVec.posZ()) > 10.) + if (std::abs(flowQVec.posZ()) > 10.) return; switch (cfgEvtSel) { case 0: // Sel8 - if (!qVec.sel8()) + if (!flowQVec.sel8()) return; break; case 1: // PbPb standard - if (!qVec.sel8() || !qVec.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !qVec.selection_bit(aod::evsel::kNoSameBunchPileup)) + if (!flowQVec.sel8() || !flowQVec.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !flowQVec.selection_bit(aod::evsel::kNoSameBunchPileup)) return; break; case 2: // PbPb with pileup - if (!qVec.sel8() || !qVec.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard) || - !qVec.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !qVec.selection_bit(aod::evsel::kNoSameBunchPileup)) + if (!flowQVec.sel8() || !flowQVec.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard) || + !flowQVec.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !flowQVec.selection_bit(aod::evsel::kNoSameBunchPileup)) return; break; case 3: // Small systems (OO, NeNe, pp) - if (!qVec.sel8() || !qVec.selection_bit(aod::evsel::kNoSameBunchPileup)) + if (!flowQVec.sel8() || !flowQVec.selection_bit(aod::evsel::kNoSameBunchPileup)) return; break; default: LOGF(warning, "Event selection flag was not found, continuing without basic event selections!\n"); } // Check occupancy - if (qVec.trackOccupancyInTimeRange() > cfgMaxOccupancy || qVec.trackOccupancyInTimeRange() < cfgMinOccupancy) + if (flowQVec.trackOccupancyInTimeRange() > cfgMaxOccupancy || flowQVec.trackOccupancyInTimeRange() < cfgMinOccupancy) return; } - histosQA.fill(HIST("histCentSelected"), qVec.cent()); - histosQA.fill(HIST("histVtxSelected"), qVec.posZ()); + histosQA.fill(HIST("histCentSelected"), flowQVec.cent()); + histosQA.fill(HIST("histVtxSelected"), flowQVec.posZ()); if (cfgShiftCorPrep) { for (uint i = 0; i < cfgnMods->size(); i++) { - fillHistosShiftCor(qVec, cfgnMods->at(i)); + fillHistosShiftCor(flowQVec, cfgnMods->at(i)); } } - for (uint i = 0; i < cfgnMods->size(); i++) { - fillHistosQvec(qVec, cfgnMods->at(i)); + fillHistosQvec(flowQVec, cfgnMods->at(i)); if (cfgQAFinal && cfgQAFlowStudy) { - fillHistosFlow(qVec, tracks, cfgnMods->at(i)); + fillHistosFlow(flowQVec, tracks, cfgnMods->at(i)); } } } - PROCESS_SWITCH(qVectorsCorrection, processDefault, "default process", true); + + void processSp(MySpCollisions::iterator const& spQVec, MyTracks const& tracks) + { + processColls(spQVec, tracks); + } + PROCESS_SWITCH(qVectorsCorrection, processSp, "process SP", true); + + void processEse(MyEseCollisions::iterator const& eseQVec, MyTracks const& tracks) + { + processColls(eseQVec, tracks); + } + + PROCESS_SWITCH(qVectorsCorrection, processEse, "process Ese", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/Common/Tasks/qaMuon.cxx b/Common/Tasks/qaMuon.cxx index d96f05fc2a6..2e5aa56c8e4 100644 --- a/Common/Tasks/qaMuon.cxx +++ b/Common/Tasks/qaMuon.cxx @@ -223,6 +223,7 @@ struct muonQa { Configurable fChamberResolutionX{"cfgChamberResolutionX", 0.4, "Chamber resolution along X configuration for refit"}; // 0.4cm pp, 0.2cm PbPb Configurable fChamberResolutionY{"cfgChamberResolutionY", 0.4, "Chamber resolution along Y configuration for refit"}; // 0.4cm pp, 0.2cm PbPb Configurable fSigmaCutImprove{"cfgSigmaCutImprove", 6., "Sigma cut for track improvement"}; + Configurable fDipoleZcorr{"cfgDipoleZcorr", 0.0f, "Correction to the dipole z position"}; } configRealign; /// Variables to event mixing criteria @@ -278,7 +279,7 @@ struct muonQa { TGeoManager* geoNew = nullptr; TGeoManager* geoRef = nullptr; - Preslice perMuon = aod::fwdtrkcl::fwdtrackId; + Preslice perMuon = aod::fwdtrkcl::fwdtrackId; Preslice fwdtracksPerCollision = aod::fwdtrack::collisionId; Preslice mftPerCollision = aod::fwdtrack::collisionId; @@ -371,7 +372,7 @@ struct muonQa { // Global muons plots // ====================== int nTrackTypes = static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MCHStandaloneTrack) + 1; - AxisSpec trackTypeAxis = {static_cast(nTrackTypes), 0.0, static_cast(nTrackTypes), "track type"}; + AxisSpec trackTypeAxis = {nTrackTypes, 0.0, static_cast(nTrackTypes), "track type"}; registry.add("global-muons/nTracksPerType", "Number of tracks per type", {HistType::kTH1F, {trackTypeAxis}}); AxisSpec nCandidatesAxis = {static_cast(fNCandidatesMax), 0.0, static_cast(fNCandidatesMax), "match candidate rank"}; @@ -1103,7 +1104,7 @@ struct muonQa { template void TransformMFT(TTrack& track) { - if constexpr (static_cast(GlobalFwdFillMap)) { + if constexpr (GlobalFwdFillMap) { auto mchTrack = mMatching.FwdtoMCH(track); doTransformMFT(mchTrack); @@ -1603,7 +1604,7 @@ struct muonQa { fgValues.nClusters = muon.nClusters(); fgValues.sign = muon.sign(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // Direct info from AO2D without re-propagation fgValues.pDca = muon.pDca(); fgValues.rabs = muon.rAtAbsorberEnd(); @@ -1651,6 +1652,11 @@ struct muonQa { fgValues.errorClusters.emplace_back(eCls); fgValues.DEIDs.emplace_back(cluster.deId()); + // subtract the dipole shift correction from the cluster z position + if (configRealign.fDipoleZcorr != 0) { + clusterMCH->z -= configRealign.fDipoleZcorr; + } + // Add transformed cluster into temporary variable convertedTrack.createParamAtCluster(*clusterMCH); } @@ -1663,6 +1669,12 @@ struct muonQa { LOGF(fatal, "Muon track %d has no associated clusters.", muon.globalIndex()); } + // add back the dipole shift correction to the track z + if (configRealign.fDipoleZcorr != 0) { + auto& trackParam = *(convertedTrack.begin()); + trackParam.setZ(trackParam.getZ() + configRealign.fDipoleZcorr); + } + for (auto it = convertedTrack.begin(); it != convertedTrack.end(); it++) { std::vector pos = {static_cast(it->getNonBendingCoor()), static_cast(it->getBendingCoor()), static_cast(it->getZ())}; fgValues.posClusters.emplace_back(pos); @@ -1760,7 +1772,7 @@ struct muonQa { fgValues.nClusters = muon.nClusters(); fgValues.sign = muon.sign(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { o2::dataformats::GlobalFwdTrack track; SMatrix5 tpars(muon.x(), muon.y(), muon.phi(), muon.tgl(), muon.signed1Pt()); @@ -1797,7 +1809,7 @@ struct muonQa { o2::dataformats::GlobalFwdTrack track; - if constexpr (static_cast(Scaled)) { + if constexpr (Scaled) { double pMCH = fgValuesMCH.p; int sign = fgValuesMCH.sign; @@ -1885,7 +1897,7 @@ struct muonQa { float dcaXY = std::sqrt(fgValues.dcaX * fgValues.dcaX + fgValues.dcaY * fgValues.dcaY); fgValues.dcaXY = dcaXY; - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { float p = muon.p(); fgValues.pDca = p * dcaXY; } @@ -1908,7 +1920,7 @@ struct muonQa { template void FillMuonHistograms(Var const& fgValuesMCH, Var const& fgValuesMCHpv, Var const& fgValuesMFT, Var const& fgValuesGlobal, VarVector const& fgValuesCandidates) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // Muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMuons.fEtaMchLow, configMuons.fEtaMchUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { registry.get(HIST("muons/TrackChi2"))->Fill(fgValuesMCH.chi2); @@ -1984,7 +1996,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { // Global muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { registry.get(HIST("global-muons/TrackChi2"))->Fill(fgValuesMCH.chi2); @@ -2036,7 +2048,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { // Global muon histograms if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, 1.E10, configMuons.fPMchLow, configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (IsGoodGlobalMatching(fgValuesMFT, configMFTs.fTrackChi2MftUp, configMFTs.fTrackNClustMftLow, fMatchingChi2MftMchUp)) { @@ -2143,7 +2155,7 @@ struct muonQa { template void FillDCAHistograms(VarT const& fgValuesMCH, VarT const& fgValuesMCHpv, VarT const& fgValuesMFT, VarC const& fgValuesColl, int sign, int quadrant, bool same, bool mixed) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { auto phiDeg = fgValuesMCH.phi * 180.f / TMath::Pi(); if (same) { std::get>(dcaHistos[1][quadrant][0]["DCA_x"])->Fill(fgValuesMCH.dcaX); @@ -2172,7 +2184,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { auto phiDeg = fgValuesMFT.phi * 180.f / TMath::Pi(); if (same) { std::get>(dcaHistos[0][quadrant][0]["DCA_x"])->Fill(fgValuesMFT.dcaX); @@ -2200,7 +2212,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { auto phiDegMCH = fgValuesMCH.phi * 180.f / TMath::Pi(); auto phiDegMFT = fgValuesMFT.phi * 180.f / TMath::Pi(); if (same) { @@ -2278,7 +2290,7 @@ struct muonQa { std::array yPos{yCls, fgValuesProp.y}; double phiClus = std::atan2(yCls, xCls) * 180 / TMath::Pi(); - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 20., configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (same) { std::get>(mchResidualsHistosPerDE[topBottom][posNeg][chamber]["dx_vs_de"])->Fill(deIndex, xPos[0] - xPos[1]); @@ -2291,7 +2303,7 @@ struct muonQa { } } - if constexpr (static_cast(MFTFillMap)) { + if constexpr (MFTFillMap) { if (IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 20., configMuons.fPtMchLow, configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { if (IsGoodMFT(fgValuesMFT, configMFTs.fTrackChi2MftUp, configMFTs.fTrackNClustMftLow)) { if (same) { @@ -2344,9 +2356,9 @@ struct muonQa { base::Propagator::initFieldFromGRP(grpmag); TrackExtrap::setField(); TrackExtrap::useExtrapV2(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT - double centerMFT[3] = {0, 0, -61.4}; // or use middle point between Vtx and MFT? - Bz = fieldB->getBz(centerMFT); // Get field at centre of MFT + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT + double centerMFT[3] = {0, 0, -61.4}; // or use middle point between Vtx and MFT? + Bz = fieldB->getBz(centerMFT); // Get field at centre of MFT } else { LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", bcs.begin().timestamp()); } @@ -2412,7 +2424,7 @@ struct muonQa { typename TMCHTrack, typename VarC, typename VarT> void runDCA(TEventMap const& collisions, TMFTTracks const& mfts, TMFTTrack const& mfttrack, TTrack const& muon, TMCHTrack const& mchtrack, mch::Track const& mchrealigned, VarC& fgValuesColl, VarT& fgValuesMCH, VarT& fgValuesMCHpv, VarT& fgValuesMFT) { - if constexpr (static_cast(MuonFillMap)) { + if constexpr (MuonFillMap) { // track selection if (!IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 30., 4., configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { @@ -2444,7 +2456,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMuonFillMap)) { + if constexpr (GlobalMuonFillMap) { auto mftsThisCollision = mfts.sliceBy(mftPerCollision, fgValuesColl.globalIndex); for (auto const& mft : mftsThisCollision) { @@ -2487,7 +2499,7 @@ struct muonQa { } } - if constexpr (static_cast(GlobalMatchingFillMap)) { + if constexpr (GlobalMatchingFillMap) { // track selection if (!IsGoodMuon(fgValuesMCH, fgValuesMCHpv, configMuons.fTrackChi2MchUp, 30., 4., configMFTs.fEtaMftLow, configMFTs.fEtaMftUp, configMuons.fRabsLow, configMuons.fRabsUp, configMuons.fSigmaPdcaUp)) { diff --git a/Common/Tasks/zdcExtraTableReader.cxx b/Common/Tasks/zdcExtraTableReader.cxx new file mode 100644 index 00000000000..6fc573631b5 --- /dev/null +++ b/Common/Tasks/zdcExtraTableReader.cxx @@ -0,0 +1,1052 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file zdcExtraTableReader.cxx +/// \brief Task reading AOD/ZDCEXTRA table +/// \author Uliana Dmitrieva , INFN Torino + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/ZDCExtra.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +namespace +{ + +std::unordered_map gEventCounter; +std::unordered_map gCentroidZNA; +std::unordered_map gCentroidZNC; +std::unordered_map gPmcZNA; +std::unordered_map gPm1ZNA; +std::unordered_map gPm2ZNA; +std::unordered_map gPm3ZNA; +std::unordered_map gPm4ZNA; +std::unordered_map gSumZNA; +std::unordered_map gPmcZNC; +std::unordered_map gPm1ZNC; +std::unordered_map gPm2ZNC; +std::unordered_map gPm3ZNC; +std::unordered_map gPm4ZNC; +std::unordered_map gSumZNC; +std::unordered_map gQxVsCentZNA; +std::unordered_map gQyVsCentZNA; +std::unordered_map gQxVsVxZNA; +std::unordered_map gQyVsVxZNA; +std::unordered_map gQxVsVyZNA; +std::unordered_map gQyVsVyZNA; +std::unordered_map gQxVsVzZNA; +std::unordered_map gQyVsVzZNA; +std::unordered_map gQxVsCentZNC; +std::unordered_map gQyVsCentZNC; +std::unordered_map gQxVsVxZNC; +std::unordered_map gQyVsVxZNC; +std::unordered_map gQxVsVyZNC; +std::unordered_map gQyVsVyZNC; +std::unordered_map gQxVsVzZNC; +std::unordered_map gQyVsVzZNC; +std::unordered_map gQxQyVsCent; +std::unordered_map gQyQxVsCent; +std::unordered_map gQxQxVsCent; +std::unordered_map gQyQyVsCent; + +std::unordered_map gQx5DZNA; +std::unordered_map gQy5DZNA; +std::unordered_map gQx5DZNC; +std::unordered_map gQy5DZNC; + +std::unordered_map gPsiZNA; +std::unordered_map gPsiZNC; + +std::unordered_map gVx; +std::unordered_map gVy; + +// centroid stability vs timestamp +std::unordered_map gQxVsTimeZNA; +std::unordered_map gQyVsTimeZNA; +std::unordered_map gQxVsTimeZNC; +std::unordered_map gQyVsTimeZNC; + +std::unordered_map gShiftProfileZNA; +std::unordered_map gShiftProfileZNC; + +TH1* gCurrentEventCounter{nullptr}; +TH2* gCurrentCentroidZNA{nullptr}; +TH2* gCurrentCentroidZNC{nullptr}; +TH1* gCurrentPmcZNA{nullptr}; +TH1* gCurrentPm1ZNA{nullptr}; +TH1* gCurrentPm2ZNA{nullptr}; +TH1* gCurrentPm3ZNA{nullptr}; +TH1* gCurrentPm4ZNA{nullptr}; +TH1* gCurrentSumZNA{nullptr}; +TH1* gCurrentPmcZNC{nullptr}; +TH1* gCurrentPm1ZNC{nullptr}; +TH1* gCurrentPm2ZNC{nullptr}; +TH1* gCurrentPm3ZNC{nullptr}; +TH1* gCurrentPm4ZNC{nullptr}; +TH1* gCurrentSumZNC{nullptr}; +TH2* gCurrentQxVsCentZNA{nullptr}; +TH2* gCurrentQyVsCentZNA{nullptr}; +TH2* gCurrentQxVsVxZNA{nullptr}; +TH2* gCurrentQyVsVxZNA{nullptr}; +TH2* gCurrentQxVsVyZNA{nullptr}; +TH2* gCurrentQyVsVyZNA{nullptr}; +TH2* gCurrentQxVsVzZNA{nullptr}; +TH2* gCurrentQyVsVzZNA{nullptr}; +TH2* gCurrentQxVsCentZNC{nullptr}; +TH2* gCurrentQyVsCentZNC{nullptr}; +TH2* gCurrentQxVsVxZNC{nullptr}; +TH2* gCurrentQyVsVxZNC{nullptr}; +TH2* gCurrentQxVsVyZNC{nullptr}; +TH2* gCurrentQyVsVyZNC{nullptr}; +TH2* gCurrentQxVsVzZNC{nullptr}; +TH2* gCurrentQyVsVzZNC{nullptr}; +TH2* gCurrentQxQyVsCent{nullptr}; +TH2* gCurrentQyQxVsCent{nullptr}; +TH2* gCurrentQxQxVsCent{nullptr}; +TH2* gCurrentQyQyVsCent{nullptr}; + +THn* gCurrentQxZNA{nullptr}; +THn* gCurrentQyZNA{nullptr}; +THn* gCurrentQxZNC{nullptr}; +THn* gCurrentQyZNC{nullptr}; + +TH1* gCurrentPsiZNA{nullptr}; +TH1* gCurrentPsiZNC{nullptr}; + +TH1* gCurrentVx{nullptr}; +TH1* gCurrentVy{nullptr}; + +TH2* gCurrentQxVsTimeZNA{nullptr}; +TH2* gCurrentQyVsTimeZNA{nullptr}; +TH2* gCurrentQxVsTimeZNC{nullptr}; +TH2* gCurrentQyVsTimeZNC{nullptr}; + +TProfile3D* gCurrentShiftProfileZNA{nullptr}; +TProfile3D* gCurrentShiftProfileZNC{nullptr}; + +} // namespace + +// Helper for 4D recentering maps +double getMeanQFromMap(THn* h, double cent, double vx, double vy, double vz) +{ + if (!h) { + LOGF(fatal, "[MeanQ] Null THn pointer"); + } + + TAxis* axCent = h->GetAxis(0); + TAxis* axVx = h->GetAxis(1); + TAxis* axVy = h->GetAxis(2); + TAxis* axVz = h->GetAxis(3); + + if (!axCent || !axVx || !axVy || !axVz) { + LOGF(fatal, "[MeanQ] One of THn axes is null"); + } + + int binCent = axCent->FindFixBin(cent); + int binVx = axVx->FindFixBin(vx); + int binVy = axVy->FindFixBin(vy); + int binVz = axVz->FindFixBin(vz); + + std::array idx = {binCent, binVx, binVy, binVz}; + return h->GetBinContent(idx.data()); +} + +// Helper for 1D recentering maps: returns mean Q for coordinate x +// If bin out of range, returns 0.0 +double getMeanQ1D(TH1* h, double x) +{ + if (!h) { + LOGF(fatal, "[MeanQ1D] Null TH1 pointer"); + } + int bin = h->FindFixBin(x); + if (bin < 1 || bin > h->GetNbinsX()) { + return 0.0; + } + return h->GetBinContent(bin); +} + +struct ZdcExtraTableReader { + + Configurable nBinsZN{"nBinsZN", 2000, "n bins for ZN histograms"}; + Configurable maxZN{"maxZN", 399.5, "Max ZN signal"}; + Configurable applyTdcCut{"applyTdcCut", true, "Flag for TDC cut"}; + Configurable tdcZnMin{"tdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable tdcZnMax{"tdcZnMax", 2.5, "Max ZN TDC cut"}; + Configurable plotPMs{"plotPMs", false, "Flag to plot individual PMs"}; + + ConfigurableAxis qxyAxis{"qxyAxis", {100, -2.0f, 2.0f}, ""}; + + Configurable vxNbins{"vxNbins", 50, "Bins in Vx"}; + Configurable vxMin{"vxMin", -0.1f, "Vx lower edge"}; + Configurable vxMax{"vxMax", 0.1f, "Vx upper edge"}; + + Configurable vyNbins{"vyNbins", 50, "Bins in Vy"}; + Configurable vyMin{"vyMin", -0.1f, "Vy lower edge"}; + Configurable vyMax{"vyMax", 0.1f, "Vy upper edge"}; + + Configurable vzNbins{"vzNbins", 50, "Bins in Vz"}; + Configurable vzMin{"vzMin", -10.0f, "Vz lower edge"}; + Configurable vzMax{"vzMax", 10.0f, "Vz upper edge"}; + + Configurable centNbins{"centNbins", 16, "Bins in centrality"}; + Configurable centMin{"centMin", 0.0f, "Centrality lower edge"}; + Configurable centMax{"centMax", 80.0f, "Centrality upper edge"}; + + Configurable phiNbins{"phiNbins", 60, "Bins in phi"}; + + Configurable minNTowersFired{"minNTowersFired", 2, "Minimum number of towers fired for Q-vector determination"}; + + Configurable qNbins5D{"qNbins5D", 4, "Bins in each dimension for 5D histograms"}; + Configurable plot5D{"plot5D", false, "Flag to plot 5D histograms"}; + + Configurable calibrationStep{"calibrationStep", 1, "Calibration step"}; + Configurable isFineCalibrationStep{"isFineCalibrationStep", false, "Calibration: base or refine"}; + + Configurable applyBeamSpotCorrection{"applyBeamSpotCorrection", true, "Beam spot correction"}; + + Configurable applySel8{"applySel8", true, "Event selection: Sel8"}; + Configurable applyZVtxCut{"applyZVtxCut", true, "Event selection: zVtx cut set in producer (tipically < 10 cm)"}; + Configurable applyOccupancyCut{"applyOccupancyCut", false, "Event selection: occupancy cut set in producer"}; + Configurable selectNoSameBunchPileupEvents{"selectNoSameBunchPileupEvents", false, "Event selection: no same bunch pileup"}; + Configurable selectGoodZvtxFT0vsPV{"selectGoodZvtxFT0vsPV", false, "Event selection: good Zvtx FT0 vs PV"}; + Configurable applyNoCollInTimeRangeStandard{"applyNoCollInTimeRangeStandard", false, "Event selection: no collision in time range standard"}; + Configurable selectVertexITSTPC{"selectVertexITSTPC", false, "Event selection: vertex ITS TPC"}; + Configurable selectGoodITSLayersAll{"selectGoodITSLayersAll", false, "Event selection: good ITS layers all"}; + + Configurable applyShiftCorrection{"applyShiftCorrection", false, "Apply shift correction (Read from CCDB)"}; + Configurable fillShiftHistos{"fillShiftHistos", false, "Fill shift profiles (Write to output)"}; + Configurable nHarmonics{"nHarmonics", 10, "Number of harmonics"}; + + Configurable qRecenteringCcdb{"qRecenteringCcdb", "Users/u/udmitrie/ZDC/LHC24ar_apass2", "Recentering maps containing step folder"}; + + // CCDB + Service ccdb{}; + + // Struct to hold calibration data for a single step + struct CalibStepData { + // 5D maps (Base) + THn* hMeanQxZNA{nullptr}; + THn* hMeanQyZNA{nullptr}; + THn* hMeanQxZNC{nullptr}; + THn* hMeanQyZNC{nullptr}; + + // 1D maps (Refine) + TH1* hMeanQxCentZNA{nullptr}; + TH1* hMeanQyCentZNA{nullptr}; + TH1* hMeanQxCentZNC{nullptr}; + TH1* hMeanQyCentZNC{nullptr}; + + TH1* hMeanQxVzZNA{nullptr}; + TH1* hMeanQyVzZNA{nullptr}; + TH1* hMeanQxVzZNC{nullptr}; + TH1* hMeanQyVzZNC{nullptr}; + + TH1* hMeanQxVxZNA{nullptr}; + TH1* hMeanQyVxZNA{nullptr}; + TH1* hMeanQxVxZNC{nullptr}; + TH1* hMeanQyVxZNC{nullptr}; + + TH1* hMeanQxVyZNA{nullptr}; + TH1* hMeanQyVyZNA{nullptr}; + TH1* hMeanQxVyZNC{nullptr}; + TH1* hMeanQyVyZNC{nullptr}; + + // Destructor to handle cleanup automatically + ~CalibStepData() + { + delete hMeanQxZNA; + delete hMeanQyZNA; + delete hMeanQxZNC; + delete hMeanQyZNC; + + delete hMeanQxCentZNA; + delete hMeanQyCentZNA; + delete hMeanQxCentZNC; + delete hMeanQyCentZNC; + + delete hMeanQxVzZNA; + delete hMeanQyVzZNA; + delete hMeanQxVzZNC; + delete hMeanQyVzZNC; + + delete hMeanQxVxZNA; + delete hMeanQyVxZNA; + delete hMeanQxVxZNC; + delete hMeanQyVxZNC; + + delete hMeanQxVyZNA; + delete hMeanQyVyZNA; + delete hMeanQxVyZNC; + delete hMeanQyVyZNC; + } + }; + + // Cache container: Vector index = Step index (0-based, so step 1 is at index 0) + std::vector calibCache; + + // Vertex correction cache + TH1* hMeanVx{nullptr}; + TH1* hMeanVy{nullptr}; + + // Phase shift correction cache + TProfile3D* shiftProfileZNA{nullptr}; + TProfile3D* shiftProfileZNC{nullptr}; + + HistogramRegistry histos{"histos"}; + + enum EvSelBits { // same bits as in zdcExtraTableProducer.cxx + ZVtxCut, + Sel8, + OccupancyCut, + NoSameBunchPileup, + IsGoodZvtxFT0vsPV, + NoCollInTimeRangeStandard, + IsVertexITSTPC, + IsGoodITSLayersAll, + AllEvents, + NEventSelections + }; + + int currentRunNumber{-1}; + + // Helper to safely clone a histogram and detach from file + template + T* safeClone(TObject* obj) + { + if (!obj) { + return nullptr; + } + T* cloned = dynamic_cast(obj->Clone()); + if (cloned) { + + if (dynamic_cast(cloned)) { + dynamic_cast(cloned)->SetDirectory(nullptr); + } + } + return cloned; + } + + void clearCache() + { + delete hMeanVx; + hMeanVx = nullptr; + + delete hMeanVy; + hMeanVy = nullptr; + + delete shiftProfileZNA; + shiftProfileZNA = nullptr; + + delete shiftProfileZNC; + shiftProfileZNC = nullptr; + + calibCache.clear(); + } + + void initHistos(const int runNumber) + { + if (runNumber == currentRunNumber) { + return; + } + currentRunNumber = runNumber; + + if (!gEventCounter.contains(runNumber)) { + // if new run, initialize histograms + + const AxisSpec axisCounter{1, 0, +1, ""}; + const AxisSpec axisZN{nBinsZN, -0.5, maxZN, "(a.u.)"}; + const AxisSpec axisCent = {centNbins, centMin, centMax, "Centrality (%)"}; + const AxisSpec axisVx = {vxNbins, vxMin, vxMax, "V_{x} (cm)"}; + const AxisSpec axisVy = {vyNbins, vyMin, vyMax, "V_{y} (cm)"}; + const AxisSpec axisVz = {vzNbins, vzMin, vzMax, "V_{z} (cm)"}; + + const AxisSpec axisCent5D = {qNbins5D, centMin, centMax, "Centrality (%)"}; + const AxisSpec axisVx5D = {qNbins5D, vxMin, vxMax, "V_{x} (cm)"}; + const AxisSpec axisVy5D = {qNbins5D, vyMin, vyMax, "V_{y} (cm)"}; + const AxisSpec axisVz5D = {qNbins5D, vzMin, vzMax, "V_{z} (cm)"}; + + const AxisSpec axisQx{qxyAxis, "Q_{x}"}; + const AxisSpec axisQy{qxyAxis, "Q_{y}"}; + const AxisSpec axisQxQy = {qxyAxis, ""}; + + const AxisSpec axisPhi = {phiNbins, -1.0f * o2::constants::math::PI, 1.0f * o2::constants::math::PI, "#phi"}; + + const AxisSpec axisTime = {90, 0, 90, "Time (minutes)"}; // 90 minutes + + gEventCounter[runNumber] = histos.add(Form("%i/eventCounter", runNumber), "Number of Event; ; #Events Passed Cut", kTH1D, {{NEventSelections, 0, NEventSelections}}).get(); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(AllEvents + 1, "allEvents"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(ZVtxCut + 1, "zVtxCut"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(Sel8 + 1, "Sel8"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(OccupancyCut + 1, "occupancyCut"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(NoSameBunchPileup + 1, "NoSameBunchPileup"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV + 1, "isGoodZvtxFT0vsPV"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard + 1, "noCollInTimeRangeStandard"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(IsVertexITSTPC + 1, "isVertexITSTPC"); + gEventCounter[runNumber]->GetXaxis()->SetBinLabel(IsGoodITSLayersAll + 1, "isGoodITSLayersAll"); + + gCentroidZNA[runNumber] = histos.add(Form("%i/CentroidZNA", runNumber), "ZNA Centroid; Q_{X}; Q_{Y}", kTH2F, {{50, -1.5, 1.5}, {50, -1.5, 1.5}}).get(); + gCentroidZNC[runNumber] = histos.add(Form("%i/CentroidZNC", runNumber), "ZNC Centroid; Q_{X}; Q_{Y}", kTH2F, {{50, -1.5, 1.5}, {50, -1.5, 1.5}}).get(); + gPmcZNA[runNumber] = histos.add(Form("%i/pmcZNA", runNumber), "; E_{PMC}^{ZNA} (TeV);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm1ZNA[runNumber] = histos.add(Form("%i/pm1ZNA", runNumber), "; E_{PM1}^{ZNA} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm2ZNA[runNumber] = histos.add(Form("%i/pm2ZNA", runNumber), "; E_{PM2}^{ZNA} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm3ZNA[runNumber] = histos.add(Form("%i/pm3ZNA", runNumber), "; E_{PM3}^{ZNA} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm4ZNA[runNumber] = histos.add(Form("%i/pm4ZNA", runNumber), "; E_{PM4}^{ZNA} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gSumZNA[runNumber] = histos.add(Form("%i/sumZNA", runNumber), "; E_{sum PMs}^{ZNA} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPmcZNC[runNumber] = histos.add(Form("%i/pmcZNC", runNumber), "; E_{PMC}^{ZNC} (TeV);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm1ZNC[runNumber] = histos.add(Form("%i/pm1ZNC", runNumber), "; E_{PM1}^{ZNC} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm2ZNC[runNumber] = histos.add(Form("%i/pm2ZNC", runNumber), "; E_{PM2}^{ZNC} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm3ZNC[runNumber] = histos.add(Form("%i/pm3ZNC", runNumber), "; E_{PM3}^{ZNC} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gPm4ZNC[runNumber] = histos.add(Form("%i/pm4ZNC", runNumber), "; E_{PM4}^{ZNC} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gSumZNC[runNumber] = histos.add(Form("%i/sumZNC", runNumber), "; E_{sum PMs}^{ZNC} (a.u.);", kTH1F, {{nBinsZN, -0.5, maxZN}}).get(); + gQxVsCentZNA[runNumber] = histos.add(Form("%i/QxVsCentZNA", runNumber), "Q_{x}^{ZNA} vs Centrality", kTH2F, {axisCent, axisQx}).get(); + gQyVsCentZNA[runNumber] = histos.add(Form("%i/QyVsCentZNA", runNumber), "Q_{y}^{ZNA} vs Centrality", kTH2F, {axisCent, axisQy}).get(); + gQxVsVxZNA[runNumber] = histos.add(Form("%i/QxVsVxZNA", runNumber), "Q_{x}^{ZNA} vs V_{x}; V_{x} (cm); Q_{x}", kTH2F, {axisVx, axisQx}).get(); + gQyVsVxZNA[runNumber] = histos.add(Form("%i/QyVsVxZNA", runNumber), "Q_{y}^{ZNA} vs V_{x}; V_{x} (cm); Q_{y}", kTH2F, {axisVx, axisQy}).get(); + gQxVsVyZNA[runNumber] = histos.add(Form("%i/QxVsVyZNA", runNumber), "Q_{x}^{ZNA} vs V_{y}; V_{y} (cm); Q_{x}", kTH2F, {axisVy, axisQx}).get(); + gQyVsVyZNA[runNumber] = histos.add(Form("%i/QyVsVyZNA", runNumber), "Q_{y}^{ZNA} vs V_{y}; V_{y} (cm); Q_{y}", kTH2F, {axisVy, axisQy}).get(); + gQxVsVzZNA[runNumber] = histos.add(Form("%i/QxVsVzZNA", runNumber), "Q_{x}^{ZNA} vs V_{z}; V_{z} (cm); Q_{x}", kTH2F, {axisVz, axisQx}).get(); + gQyVsVzZNA[runNumber] = histos.add(Form("%i/QyVsVzZNA", runNumber), "Q_{y}^{ZNA} vs V_{z}; V_{z} (cm); Q_{y}", kTH2F, {axisVz, axisQy}).get(); + gQxVsCentZNC[runNumber] = histos.add(Form("%i/QxVsCentZNC", runNumber), "Q_{x}^{ZNC} vs Centrality; Centrality (%); Q_{x}", kTH2F, {axisCent, axisQx}).get(); + gQyVsCentZNC[runNumber] = histos.add(Form("%i/QyVsCentZNC", runNumber), "Q_{y}^{ZNC} vs Centrality; Centrality (%); Q_{y}", kTH2F, {axisCent, axisQy}).get(); + gQxVsVxZNC[runNumber] = histos.add(Form("%i/QxVsVxZNC", runNumber), "Q_{x}^{ZNC} vs V_{x}; V_{x} (cm); Q_{x}", kTH2F, {axisVx, axisQx}).get(); + gQyVsVxZNC[runNumber] = histos.add(Form("%i/QyVsVxZNC", runNumber), "Q_{y}^{ZNC} vs V_{x}; V_{x} (cm); Q_{y}", kTH2F, {axisVx, axisQy}).get(); + gQxVsVyZNC[runNumber] = histos.add(Form("%i/QxVsVyZNC", runNumber), "Q_{x}^{ZNC} vs V_{y}; V_{y} (cm); Q_{x}", kTH2F, {axisVy, axisQx}).get(); + gQyVsVyZNC[runNumber] = histos.add(Form("%i/QyVsVyZNC", runNumber), "Q_{y}^{ZNC} vs V_{y}; V_{y} (cm); Q_{y}", kTH2F, {axisVy, axisQy}).get(); + gQxVsVzZNC[runNumber] = histos.add(Form("%i/QxVsVzZNC", runNumber), "Q_{x}^{ZNC} vs V_{z}; V_{z} (cm); Q_{x}", kTH2F, {axisVz, axisQx}).get(); + gQyVsVzZNC[runNumber] = histos.add(Form("%i/QyVsVzZNC", runNumber), "Q_{y}^{ZNC} vs V_{z}; V_{z} (cm); Q_{y}", kTH2F, {axisVz, axisQy}).get(); + gQxQyVsCent[runNumber] = histos.add(Form("%i/QxQyVsCent", runNumber), "Q_{x}^{ZNC}Q_{y}^{ZNC} vs Centrality; Centrality (%); Q_{x}^{ZNA}Q_{y}^{ZNC}", kTH2F, {axisCent, {50, -1.5, 1.5}}).get(); + gQyQxVsCent[runNumber] = histos.add(Form("%i/QyQxVsCent", runNumber), "Q_{y}^{ZNC}Q_{x}^{ZNC} vs Centrality; Centrality (%); Q_{y}^{ZNA}Q_{x}^{ZNC}", kTH2F, {axisCent, {50, -1.5, 1.5}}).get(); + gQxQxVsCent[runNumber] = histos.add(Form("%i/QxQxVsCent", runNumber), "Q_{x}^{ZNC}Q_{x}^{ZNC} vs Centrality; Centrality (%); Q_{x}^{ZNA}Q_{x}^{ZNC}", kTH2F, {axisCent, {50, -1.5, 1.5}}).get(); + gQyQyVsCent[runNumber] = histos.add(Form("%i/QyQyVsCent", runNumber), "Q_{y}^{ZNC}Q_{y}^{ZNC} vs Centrality; Centrality (%); Q_{y}^{ZNA}Q_{y}^{ZNC}", kTH2F, {axisCent, {50, -1.5, 1.5}}).get(); + + gQx5DZNA[runNumber] = histos.add(Form("%i/Qx5DZNA", runNumber), "Qx recenter map ZNA", kTHnF, {axisCent5D, axisVx5D, axisVy5D, axisVz5D, axisQx}, true).get(); + gQy5DZNA[runNumber] = histos.add(Form("%i/Qy5DZNA", runNumber), "Qy recenter map ZNA", kTHnF, {axisCent5D, axisVx5D, axisVy5D, axisVz5D, axisQy}, true).get(); + gQx5DZNC[runNumber] = histos.add(Form("%i/Qx5DZNC", runNumber), "Qx recenter map ZNC", kTHnF, {axisCent5D, axisVx5D, axisVy5D, axisVz5D, axisQx}, true).get(); + gQy5DZNC[runNumber] = histos.add(Form("%i/Qy5DZNC", runNumber), "Qy recenter map ZNC", kTHnF, {axisCent5D, axisVx5D, axisVy5D, axisVz5D, axisQy}, true).get(); + + gPsiZNA[runNumber] = histos.add(Form("%i/PsiZNA", runNumber), ";#Phi_{ZNA} (rad)", kTH1F, {axisPhi}).get(); + gPsiZNC[runNumber] = histos.add(Form("%i/PsiZNC", runNumber), ";#Phi_{ZNC} (rad)", kTH1F, {axisPhi}).get(); + + gVx[runNumber] = histos.add(Form("%i/Vx", runNumber), "V_{x} distribution; V_{x} (cm); Entries", kTH1F, {axisVx}).get(); + gVy[runNumber] = histos.add(Form("%i/Vy", runNumber), "V_{y} distribution; V_{y} (cm); Entries", kTH1F, {axisVy}).get(); + + gQxVsTimeZNA[runNumber] = histos.add(Form("%i/QxVsTimeZNA", runNumber), "Q_{x}^{ZNA} vs Time; Time (minutes); Q_{x}", kTH2F, {axisTime, axisQx}).get(); + gQyVsTimeZNA[runNumber] = histos.add(Form("%i/QyVsTimeZNA", runNumber), "Q_{y}^{ZNA} vs Time; Time (minutes); Q_{y}", kTH2F, {axisTime, axisQy}).get(); + gQxVsTimeZNC[runNumber] = histos.add(Form("%i/QxVsTimeZNC", runNumber), "Q_{x}^{ZNC} vs Time; Time (minutes); Q_{x}", kTH2F, {axisTime, axisQx}).get(); + gQyVsTimeZNC[runNumber] = histos.add(Form("%i/QyVsTimeZNC", runNumber), "Q_{y}^{ZNC} vs Time; Time (minutes); Q_{y}", kTH2F, {axisTime, axisQy}).get(); + + gShiftProfileZNA[runNumber] = histos.add(Form("%i/ShiftProfileZNA", runNumber), "ZNA Shift Coeffs;Cent;Type;Harmonic", kTProfile3D, {axisCent, {2, 0, 2}, {nHarmonics, 0, static_cast(nHarmonics)}}).get(); + gShiftProfileZNC[runNumber] = histos.add(Form("%i/ShiftProfileZNC", runNumber), "ZNC Shift Coeffs;Cent;Type;Harmonic", kTProfile3D, {axisCent, {2, 0, 2}, {nHarmonics, 0, static_cast(nHarmonics)}}).get(); + } + + gCurrentEventCounter = gEventCounter[currentRunNumber]; + gCurrentCentroidZNA = gCentroidZNA[currentRunNumber]; + gCurrentCentroidZNC = gCentroidZNC[currentRunNumber]; + gCurrentPmcZNA = gPmcZNA[currentRunNumber]; + gCurrentPm1ZNA = gPm1ZNA[currentRunNumber]; + gCurrentPm2ZNA = gPm2ZNA[currentRunNumber]; + gCurrentPm3ZNA = gPm3ZNA[currentRunNumber]; + gCurrentPm4ZNA = gPm4ZNA[currentRunNumber]; + gCurrentSumZNA = gSumZNA[currentRunNumber]; + gCurrentPmcZNC = gPmcZNC[currentRunNumber]; + gCurrentPm1ZNC = gPm1ZNC[currentRunNumber]; + gCurrentPm2ZNC = gPm2ZNC[currentRunNumber]; + gCurrentPm3ZNC = gPm3ZNC[currentRunNumber]; + gCurrentPm4ZNC = gPm4ZNC[currentRunNumber]; + gCurrentSumZNC = gSumZNC[currentRunNumber]; + gCurrentQxVsCentZNA = gQxVsCentZNA[currentRunNumber]; + gCurrentQyVsCentZNA = gQyVsCentZNA[currentRunNumber]; + gCurrentQxVsVxZNA = gQxVsVxZNA[currentRunNumber]; + gCurrentQyVsVxZNA = gQyVsVxZNA[currentRunNumber]; + gCurrentQxVsVyZNA = gQxVsVyZNA[currentRunNumber]; + gCurrentQyVsVyZNA = gQyVsVyZNA[currentRunNumber]; + gCurrentQxVsVzZNA = gQxVsVzZNA[currentRunNumber]; + gCurrentQyVsVzZNA = gQyVsVzZNA[currentRunNumber]; + gCurrentQxVsCentZNC = gQxVsCentZNC[currentRunNumber]; + gCurrentQyVsCentZNC = gQyVsCentZNC[currentRunNumber]; + gCurrentQxVsVxZNC = gQxVsVxZNC[currentRunNumber]; + gCurrentQyVsVxZNC = gQyVsVxZNC[currentRunNumber]; + gCurrentQxVsVyZNC = gQxVsVyZNC[currentRunNumber]; + gCurrentQyVsVyZNC = gQyVsVyZNC[currentRunNumber]; + gCurrentQxVsVzZNC = gQxVsVzZNC[currentRunNumber]; + gCurrentQyVsVzZNC = gQyVsVzZNC[currentRunNumber]; + gCurrentQxQyVsCent = gQxQyVsCent[currentRunNumber]; + gCurrentQyQxVsCent = gQyQxVsCent[currentRunNumber]; + gCurrentQxQxVsCent = gQxQxVsCent[currentRunNumber]; + gCurrentQyQyVsCent = gQyQyVsCent[currentRunNumber]; + + gCurrentQxZNA = gQx5DZNA[currentRunNumber]; + gCurrentQyZNA = gQy5DZNA[currentRunNumber]; + gCurrentQxZNC = gQx5DZNC[currentRunNumber]; + gCurrentQyZNC = gQy5DZNC[currentRunNumber]; + + gCurrentPsiZNA = gPsiZNA[currentRunNumber]; + gCurrentPsiZNC = gPsiZNC[currentRunNumber]; + + gCurrentVx = gVx[currentRunNumber]; + gCurrentVy = gVy[currentRunNumber]; + + gCurrentQxVsTimeZNA = gQxVsTimeZNA[currentRunNumber]; + gCurrentQyVsTimeZNA = gQyVsTimeZNA[currentRunNumber]; + gCurrentQxVsTimeZNC = gQxVsTimeZNC[currentRunNumber]; + gCurrentQyVsTimeZNC = gQyVsTimeZNC[currentRunNumber]; + + gCurrentShiftProfileZNA = gShiftProfileZNA[currentRunNumber]; + gCurrentShiftProfileZNC = gShiftProfileZNC[currentRunNumber]; + } + + // Optimized method to load ALL calibrations for the new run at once + void loadCalibrations(int runNumber) + { + clearCache(); + + // Vertex Calibration + if (applyBeamSpotCorrection) { + std::string folder = Form("%s/step0", qRecenteringCcdb.value.c_str()); + auto* lst = ccdb->getForRun(folder, runNumber); + if (lst) { + hMeanVx = safeClone(lst->FindObject("hMeanVx")); + hMeanVy = safeClone(lst->FindObject("hMeanVy")); + } else { + LOGF(error, " >> CCDB TList is NULL for path: %s. Check object type (TList vs TFile).", folder.c_str()); + } + } + + // Step Calibrations + std::size_t targetSteps = (calibrationStep > 0) ? static_cast(calibrationStep.value) : 0; + calibCache.resize(targetSteps); + + for (std::size_t stepIdx = 0; stepIdx < targetSteps; ++stepIdx) { + int step = static_cast(stepIdx + 1); + + // Load 5D (Base) + std::string folderBase = Form("%s/step%d_base", qRecenteringCcdb.value.c_str(), step); + auto* lstBase = ccdb->getForRun(folderBase, runNumber); + + if (!lstBase) { + LOGF(error, " >> CCDB TList is NULL for path: %s. Check object type (TList vs TFile).", folderBase.c_str()); + continue; + } + + calibCache[stepIdx].hMeanQxZNA = safeClone(lstBase->FindObject("hMeanQxZNA")); + calibCache[stepIdx].hMeanQyZNA = safeClone(lstBase->FindObject("hMeanQyZNA")); + calibCache[stepIdx].hMeanQxZNC = safeClone(lstBase->FindObject("hMeanQxZNC")); + calibCache[stepIdx].hMeanQyZNC = safeClone(lstBase->FindObject("hMeanQyZNC")); + + // Load 1D (Refine) + if ((step != calibrationStep) || isFineCalibrationStep) { + std::string folderRefine = Form("%s/step%d_refine", qRecenteringCcdb.value.c_str(), step); + auto* lstRefine = ccdb->getForRun(folderRefine, runNumber); + + if (!lstRefine) { + LOGF(error, " >> CCDB TList is NULL for path: %s. Check object type (TList vs TFile).", folderRefine.c_str()); + continue; + } + + calibCache[stepIdx].hMeanQxCentZNA = safeClone(lstRefine->FindObject("hMeanQxCentZNA")); + calibCache[stepIdx].hMeanQyCentZNA = safeClone(lstRefine->FindObject("hMeanQyCentZNA")); + calibCache[stepIdx].hMeanQxCentZNC = safeClone(lstRefine->FindObject("hMeanQxCentZNC")); + calibCache[stepIdx].hMeanQyCentZNC = safeClone(lstRefine->FindObject("hMeanQyCentZNC")); + + calibCache[stepIdx].hMeanQxVzZNA = safeClone(lstRefine->FindObject("hMeanQxVzZNA")); + calibCache[stepIdx].hMeanQyVzZNA = safeClone(lstRefine->FindObject("hMeanQyVzZNA")); + calibCache[stepIdx].hMeanQxVzZNC = safeClone(lstRefine->FindObject("hMeanQxVzZNC")); + calibCache[stepIdx].hMeanQyVzZNC = safeClone(lstRefine->FindObject("hMeanQyVzZNC")); + + calibCache[stepIdx].hMeanQxVxZNA = safeClone(lstRefine->FindObject("hMeanQxVxZNA")); + calibCache[stepIdx].hMeanQyVxZNA = safeClone(lstRefine->FindObject("hMeanQyVxZNA")); + calibCache[stepIdx].hMeanQxVxZNC = safeClone(lstRefine->FindObject("hMeanQxVxZNC")); + calibCache[stepIdx].hMeanQyVxZNC = safeClone(lstRefine->FindObject("hMeanQyVxZNC")); + + calibCache[stepIdx].hMeanQxVyZNA = safeClone(lstRefine->FindObject("hMeanQxVyZNA")); + calibCache[stepIdx].hMeanQyVyZNA = safeClone(lstRefine->FindObject("hMeanQyVyZNA")); + calibCache[stepIdx].hMeanQxVyZNC = safeClone(lstRefine->FindObject("hMeanQxVyZNC")); + calibCache[stepIdx].hMeanQyVyZNC = safeClone(lstRefine->FindObject("hMeanQyVyZNC")); + } + } // end of step loop + + if (applyShiftCorrection) { + std::string folder = Form("%s/psiHarm", qRecenteringCcdb.value.c_str()); + + // LOGF(info, "Loading Shift Correction from %s for runNumber %d", folder.c_str(), runNumber); + + // Attempt to fetch TList from CCDB + auto* lst = ccdb->getForRun(folder, runNumber); + + if (!lst) { + LOGF(error, " >> CCDB TList is NULL for path: %s. Check object type (TList vs TFile).", folder.c_str()); + return; + } + + // Important: Object names must match exactly what was saved + shiftProfileZNA = safeClone(lst->FindObject("ShiftProfileZNA")); + shiftProfileZNC = safeClone(lst->FindObject("ShiftProfileZNC")); + + if (shiftProfileZNA) { + shiftProfileZNA->SetDirectory(nullptr); // Detach from file + // LOGF(info, " >> ShiftProfileZNA found! Entries: %.0f, Mean: %f", shiftProfileZNA->GetEntries(), shiftProfileZNA->GetMean()); + } else { + LOGF(error, " >> ShiftProfileZNA NOT found in TList! Content follows:"); + lst->Print(); + } + + if (shiftProfileZNC) { + shiftProfileZNC->SetDirectory(nullptr); + // LOGF(info, " >> ShiftProfileZNC found! Entries: %.0f", shiftProfileZNC->GetEntries()); + } else { + LOGF(error, " >> ShiftProfileZNC NOT found in TList!"); + } + } + } // end of loadCalibrations() + + ~ZdcExtraTableReader() + { + clearCache(); + } + + /// Initializes histograms and other resources before event processing. + void init(InitContext&) + { + const AxisSpec axisCounter{1, 0, +1, ""}; + histos.add("eventCounter", "eventCounter", kTH1F, {axisCounter}); + + // CCDB + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setFatalWhenNull(false); + } // end of init() + + void process(aod::ZdcExtras::iterator const& zdc) + { + + // Apply event selection + + if (applySel8 && !TESTBIT(zdc.selectionBits(), Sel8)) { + return; + } + if (applyZVtxCut && !TESTBIT(zdc.selectionBits(), ZVtxCut)) { + return; + } + if (applyOccupancyCut && !TESTBIT(zdc.selectionBits(), OccupancyCut)) { + return; + } + if (selectNoSameBunchPileupEvents && !TESTBIT(zdc.selectionBits(), NoSameBunchPileup)) { + return; + } + if (selectGoodZvtxFT0vsPV && !TESTBIT(zdc.selectionBits(), IsGoodZvtxFT0vsPV)) { + return; + } + if (applyNoCollInTimeRangeStandard && !TESTBIT(zdc.selectionBits(), NoCollInTimeRangeStandard)) { + return; + } + if (selectVertexITSTPC && !TESTBIT(zdc.selectionBits(), IsVertexITSTPC)) { + return; + } + if (selectGoodITSLayersAll && !TESTBIT(zdc.selectionBits(), IsGoodITSLayersAll)) { + return; + } + + const int runNumber = zdc.runNumber(); + + const uint64_t timestamp = zdc.timestamp(); // in milliseconds + + // Convert timestamp to hours from run start (approximate) + // Store first timestamp of run to calculate relative time + static std::unordered_map runStartTime; + if (!runStartTime.contains(runNumber)) { + runStartTime[runNumber] = timestamp; + } + + double timeInMinutes = (timestamp - runStartTime[runNumber]) / 60000.0; // ms -> minutes + + // Initialization block if Run Number changes + if (runNumber != currentRunNumber) { + initHistos(runNumber); // Init output histograms + loadCalibrations(runNumber); // Load all steps from CCDB once + currentRunNumber = runNumber; + } + + histos.fill(HIST("eventCounter"), 0.5); + + gCurrentEventCounter->Fill(AllEvents); + + // Fill histogram for all bits that passed + for (int bit = 0; bit < NEventSelections + 1; bit++) { + if (TESTBIT(zdc.selectionBits(), bit)) { + gCurrentEventCounter->Fill(bit); + } + } + + bool isZNChit = false, isZNAhit = false; + + double tdcZNC = zdc.zncTdc(); + double tdcZNA = zdc.znaTdc(); + + if (applyTdcCut) { // TDC window is set + if ((tdcZNC >= tdcZnMin) && (tdcZNC <= tdcZnMax)) { + isZNChit = true; + } + if ((tdcZNA >= tdcZnMin) && (tdcZNA <= tdcZnMax)) { + isZNAhit = true; + } + } else { // if no window on TDC is set + if (zdc.zncTowC() > -1.) { + isZNChit = true; + } + if (zdc.znaTowC() > -1.) { + isZNAhit = true; + } + } + + bool isZNASpDeterminable = false; + bool isZNCSpDeterminable = false; + + constexpr float QvectorMaxValue = 990.0; + + if (isZNAhit) { + int activeTowersZNA = static_cast(zdc.znaTow1() > 0.) + static_cast(zdc.znaTow2() > 0.) + static_cast(zdc.znaTow3() > 0.) + static_cast(zdc.znaTow4() > 0.); + float znaSum = zdc.znaTow1() + zdc.znaTow2() + zdc.znaTow3() + zdc.znaTow4(); + if (activeTowersZNA >= minNTowersFired && znaSum > 0 && zdc.znaQx() < QvectorMaxValue) { + isZNASpDeterminable = true; + } + } + + if (isZNChit) { + int activeTowersZNC = static_cast(zdc.zncTow1() > 0.) + static_cast(zdc.zncTow2() > 0.) + static_cast(zdc.zncTow3() > 0.) + static_cast(zdc.zncTow4() > 0.); + float zncSum = zdc.zncTow1() + zdc.zncTow2() + zdc.zncTow3() + zdc.zncTow4(); + if (activeTowersZNC >= minNTowersFired && zncSum > 0 && zdc.zncQx() < QvectorMaxValue) { + isZNCSpDeterminable = true; + } + } + + if (plotPMs) { + if (isZNAhit) { + gCurrentPmcZNA->Fill(zdc.znaTowC()); + gCurrentPm1ZNA->Fill(zdc.znaTow1()); + gCurrentPm2ZNA->Fill(zdc.znaTow2()); + gCurrentPm3ZNA->Fill(zdc.znaTow3()); + gCurrentPm4ZNA->Fill(zdc.znaTow4()); + + float znaSum = zdc.znaTow1() + zdc.znaTow2() + zdc.znaTow3() + zdc.znaTow4(); + gCurrentSumZNA->Fill(znaSum); + } + + if (isZNChit) { + gCurrentPmcZNC->Fill(zdc.zncTowC()); + gCurrentPm1ZNC->Fill(zdc.zncTow1()); + gCurrentPm2ZNC->Fill(zdc.zncTow2()); + gCurrentPm3ZNC->Fill(zdc.zncTow3()); + gCurrentPm4ZNC->Fill(zdc.zncTow4()); + + float zncSum = zdc.zncTow1() + zdc.zncTow2() + zdc.zncTow3() + zdc.zncTow4(); + gCurrentSumZNC->Fill(zncSum); + } + } // end of if (plotPMs) + + double qxZNArec = 0., qyZNArec = 0.; + double qxZNCrec = 0., qyZNCrec = 0.; + + double cent = zdc.centrality(); + double vx = zdc.vx(); + double vy = zdc.vy(); + double vz = zdc.vz(); + + if (applyBeamSpotCorrection) { + // Use cached vertex pointers + if (hMeanVx && hMeanVy) { + vx -= hMeanVx->GetBinContent(1); + vy -= hMeanVy->GetBinContent(1); + } + } + + // -------- ZNA -------- + if (isZNASpDeterminable) { + double qx = zdc.znaQx(); + double qy = zdc.znaQy(); + + qxZNArec = qx; + qyZNArec = qy; + + for (int step = 1; step <= calibrationStep; step++) { + + int cacheIdx = step - 1; + // Check if index is valid within cached vector + if (cacheIdx >= static_cast(calibCache.size())) { + continue; + } + + const auto& calib = calibCache[cacheIdx]; + + // Apply 5D Base calibration + if (calib.hMeanQxZNA && calib.hMeanQyZNA) { + qxZNArec -= getMeanQFromMap(calib.hMeanQxZNA, cent, vx, vy, vz); + qyZNArec -= getMeanQFromMap(calib.hMeanQyZNA, cent, vx, vy, vz); + } + + if ((step != calibrationStep) || isFineCalibrationStep) { + // Apply 1D Refine calibration + qxZNArec -= getMeanQ1D(calib.hMeanQxCentZNA, cent); + qyZNArec -= getMeanQ1D(calib.hMeanQyCentZNA, cent); + + qxZNArec -= getMeanQ1D(calib.hMeanQxVzZNA, vz); + qyZNArec -= getMeanQ1D(calib.hMeanQyVzZNA, vz); + + qxZNArec -= getMeanQ1D(calib.hMeanQxVxZNA, vx); + qyZNArec -= getMeanQ1D(calib.hMeanQyVxZNA, vx); + + qxZNArec -= getMeanQ1D(calib.hMeanQxVyZNA, vy); + qyZNArec -= getMeanQ1D(calib.hMeanQyVyZNA, vy); + } + } + + std::array valuesQxZNA = {cent, vx, vy, vz, qxZNArec}; + std::array valuesQyZNA = {cent, vx, vy, vz, qyZNArec}; + + gCurrentCentroidZNA->Fill(qxZNArec, qyZNArec); + + gCurrentQxVsCentZNA->Fill(cent, qxZNArec); + gCurrentQyVsCentZNA->Fill(cent, qyZNArec); + gCurrentQxVsVxZNA->Fill(vx, qxZNArec); + gCurrentQyVsVxZNA->Fill(vx, qyZNArec); + gCurrentQxVsVyZNA->Fill(vy, qxZNArec); + gCurrentQyVsVyZNA->Fill(vy, qyZNArec); + gCurrentQxVsVzZNA->Fill(vz, qxZNArec); + gCurrentQyVsVzZNA->Fill(vz, qyZNArec); + + // Fill time-dependent plots + gCurrentQxVsTimeZNA->Fill(timeInMinutes, qxZNArec); + gCurrentQyVsTimeZNA->Fill(timeInMinutes, qyZNArec); + + if (plot5D) { + gCurrentQxZNA->Fill(valuesQxZNA.data()); + gCurrentQyZNA->Fill(valuesQyZNA.data()); + } + + // Calculate raw/recentered angle + double psiZNA = std::atan2(qyZNArec, qxZNArec); + + // Apply Correction (Read Mode) + // Checks if correction is enabled AND if the map from CCDB was loaded successfully + if (applyShiftCorrection && shiftProfileZNA) { + double deltaPsi = 0.0; + + // Loop over harmonics (usually 1 to 10) + for (int iHarm = 1; iHarm <= nHarmonics; iHarm++) { + // Retrieve coefficients from TProfile3D + // Axis mapping: + // X: Centrality + // Y: Type (0.5 for Sin, 1.5 for Cos) + // Z: Harmonic index (iHarm - 0.5 maps to bin 1, 2, etc.) + + int binSin = shiftProfileZNA->FindFixBin(cent, 0.5, static_cast(iHarm) - 0.5); + int binCos = shiftProfileZNA->FindFixBin(cent, 1.5, static_cast(iHarm) - 0.5); + + double coeffSin = shiftProfileZNA->GetBinContent(binSin); + double coeffCos = shiftProfileZNA->GetBinContent(binCos); + + // Fourier flattening formula: + // DeltaPsi = sum( (2/k) * ( *sin(k*psi) - *cos(k*psi) ) ) + // Note: signs depend on definition, this matches the standard correction logic + deltaPsi += (2.0 / iHarm) * (-coeffSin * std::cos(iHarm * psiZNA) + coeffCos * std::sin(iHarm * psiZNA)); + } + + // DEBUG: Print only if shift is actually happening for first few events + static int debugPrintCount = 0; + constexpr int MaxDebugPrints = 10; + constexpr double PsiTolerance = 1e-6; + + if (debugPrintCount < MaxDebugPrints && std::abs(deltaPsi) > PsiTolerance) { + LOGF(info, "ZNA Shift: Cent %.1f, Raw %.3f (Delta %.4f)", cent, psiZNA, deltaPsi); + debugPrintCount++; + } + + // Apply the calculated shift + psiZNA += deltaPsi; + + // Wrap angle to [-pi, pi] range + psiZNA = RecoDecay::constrainAngle(psiZNA, -o2::constants::math::PI); + } + + // Fill Shift Profiles (Write Mode) + // Used to generate calibration for the next step or to verify correction (QA) + if (fillShiftHistos && gCurrentShiftProfileZNA) { + for (int iHarm = 1; iHarm <= nHarmonics; iHarm++) { + // Fill Sin component (Y = 0.5) + gCurrentShiftProfileZNA->Fill(cent, 0.5, static_cast(iHarm) - 0.5, std::sin(iHarm * psiZNA)); + // Fill Cos component (Y = 1.5) + gCurrentShiftProfileZNA->Fill(cent, 1.5, static_cast(iHarm) - 0.5, std::cos(iHarm * psiZNA)); + } + } + + // Fill final analysis histogram with the best available Psi (Raw or Corrected) + gCurrentPsiZNA->Fill(psiZNA); + } + + // -------- ZNC -------- + if (isZNCSpDeterminable) { + double qx = zdc.zncQx(); + double qy = zdc.zncQy(); + + qxZNCrec = qx; + qyZNCrec = qy; + + // Iterate through steps using cached vector + for (int step = 1; step <= calibrationStep; step++) { + + int cacheIdx = step - 1; + if (cacheIdx >= static_cast(calibCache.size())) + continue; + + const auto& calib = calibCache[cacheIdx]; + + // Apply 5D Base calibration + if (calib.hMeanQxZNC && calib.hMeanQyZNC) { + qxZNCrec -= getMeanQFromMap(calib.hMeanQxZNC, cent, vx, vy, vz); + qyZNCrec -= getMeanQFromMap(calib.hMeanQyZNC, cent, vx, vy, vz); + } + + if ((step != calibrationStep) || isFineCalibrationStep) { + + // Apply 1D Refine calibration + qxZNCrec -= getMeanQ1D(calib.hMeanQxCentZNC, cent); + qyZNCrec -= getMeanQ1D(calib.hMeanQyCentZNC, cent); + + qxZNCrec -= getMeanQ1D(calib.hMeanQxVzZNC, vz); + qyZNCrec -= getMeanQ1D(calib.hMeanQyVzZNC, vz); + + qxZNCrec -= getMeanQ1D(calib.hMeanQxVxZNC, vx); + qyZNCrec -= getMeanQ1D(calib.hMeanQyVxZNC, vx); + + qxZNCrec -= getMeanQ1D(calib.hMeanQxVyZNC, vy); + qyZNCrec -= getMeanQ1D(calib.hMeanQyVyZNC, vy); + } + } + + std::array valuesQxZNC = {cent, vx, vy, vz, qxZNCrec}; + std::array valuesQyZNC = {cent, vx, vy, vz, qyZNCrec}; + + gCurrentCentroidZNC->Fill(qxZNCrec, qyZNCrec); + + gCurrentQxVsCentZNC->Fill(cent, qxZNCrec); + gCurrentQyVsCentZNC->Fill(cent, qyZNCrec); + gCurrentQxVsVxZNC->Fill(vx, qxZNCrec); + gCurrentQyVsVxZNC->Fill(vx, qyZNCrec); + gCurrentQxVsVyZNC->Fill(vy, qxZNCrec); + gCurrentQyVsVyZNC->Fill(vy, qyZNCrec); + gCurrentQxVsVzZNC->Fill(vz, qxZNCrec); + gCurrentQyVsVzZNC->Fill(vz, qyZNCrec); + + // Fill time-dependent plots + gCurrentQxVsTimeZNC->Fill(timeInMinutes, qxZNCrec); + gCurrentQyVsTimeZNC->Fill(timeInMinutes, qyZNCrec); + + if (plot5D) { + gCurrentQxZNC->Fill(valuesQxZNC.data()); + gCurrentQyZNC->Fill(valuesQyZNC.data()); + } + + // Calculate raw/recentered angle + double psiZNC = std::atan2(qyZNCrec, qxZNCrec); + + // Apply Correction (Read Mode) + // Checks if correction is enabled AND if the map from CCDB was loaded successfully + if (applyShiftCorrection && shiftProfileZNC) { + double deltaPsi = 0.0; + + // Loop over harmonics (usually 1 to 10) + for (int iHarm = 1; iHarm <= nHarmonics; iHarm++) { + // Retrieve coefficients from TProfile3D + // Axis mapping: + // X: Centrality + // Y: Type (0.5 for Sin, 1.5 for Cos) + // Z: Harmonic index (iHarm - 0.5 maps to bin 1, 2, etc.) + + int binSin = shiftProfileZNC->FindFixBin(cent, 0.5, static_cast(iHarm) - 0.5); + int binCos = shiftProfileZNC->FindFixBin(cent, 1.5, static_cast(iHarm) - 0.5); + + double coeffSin = shiftProfileZNC->GetBinContent(binSin); + double coeffCos = shiftProfileZNC->GetBinContent(binCos); + + // Fourier flattening formula: + // DeltaPsi = sum( (2/k) * ( *sin(k*psi) - *cos(k*psi) ) ) + // Note: signs depend on definition, this matches the standard correction logic + deltaPsi += (2.0 / iHarm) * (-coeffSin * std::cos(iHarm * psiZNC) + coeffCos * std::sin(iHarm * psiZNC)); + } + + // Apply the calculated shift + psiZNC += deltaPsi; + + // Wrap angle to [-pi, pi] range + psiZNC = RecoDecay::constrainAngle(psiZNC, -o2::constants::math::PI); + } + + // Fill Shift Profiles (Write Mode) + // Used to generate calibration for the next step or to verify correction (QA) + if (fillShiftHistos && gCurrentShiftProfileZNC) { + for (int iHarm = 1; iHarm <= nHarmonics; iHarm++) { + // Fill Sin component (Y = 0.5) + gCurrentShiftProfileZNC->Fill(cent, 0.5, static_cast(iHarm) - 0.5, std::sin(iHarm * psiZNC)); + // Fill Cos component (Y = 1.5) + gCurrentShiftProfileZNC->Fill(cent, 1.5, static_cast(iHarm) - 0.5, std::cos(iHarm * psiZNC)); + } + } + + // Fill final analysis histogram with the best available Psi (Raw or Corrected) + gCurrentPsiZNC->Fill(psiZNC); + } + + if (isZNASpDeterminable && isZNCSpDeterminable) { + gCurrentQxQyVsCent->Fill(cent, qxZNArec * qyZNCrec); + gCurrentQyQxVsCent->Fill(cent, qyZNArec * qxZNCrec); + gCurrentQxQxVsCent->Fill(cent, qxZNArec * qxZNCrec); + gCurrentQyQyVsCent->Fill(cent, qyZNArec * qyZNCrec); + } + + gCurrentVx->Fill(vx); + gCurrentVy->Fill(vy); + + } // end of process() (= end of loop through collisions with ZDC) +}; // end of struct ZdcExtraTableReader + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Common/Tasks/zdcTableReader.cxx b/Common/Tasks/zdcTableReader.cxx index 07a581564cd..6efececddab 100644 --- a/Common/Tasks/zdcTableReader.cxx +++ b/Common/Tasks/zdcTableReader.cxx @@ -27,21 +27,35 @@ #include #include +#include + using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::aod; +#define CHECK_BIT(var, pos) (((var) >> (pos)) & 1) + struct ZDCLIAnalysis { - // Configurable number of bins - Configurable useZvtx{"useZvtx", false, "If true uses Z_vertex"}; - Configurable zVval{"zVval", 10., "Z_vertex cut value"}; - Configurable tStampMin{"tStampMin", 100000., "minimum value for timestamp"}; - Configurable tStampMax{"tStampMax", 100000., ",maximum value for timestamp"}; + // Configurable + Configurable selectBC{"selectBC", 0, "Select BC events"}; + Configurable selectOnlyB{"selectOnlyB", 0, "Select BC with A && C"}; + Configurable selectColl{"selectColl", 0, "Select ALICE collision events"}; + // + Configurable tStampOffset{"tStampOffset", 0, "offset value for timestamp"}; + Configurable nBinstStamp{"nBinstStamp", 1000, "no. bins in histo vs. timestamp"}; + Configurable tStampMax{"tStampMax", 1000, ", maximum value for timestamp"}; + // + Configurable tdcCut{"tdcCut", false, "Flag for TDC cut"}; + Configurable tdcZNmincut{"tdcZNmincut", -1.5, "Min. ZN TDC cut value"}; + Configurable tdcZNmaxcut{"tdcZNmaxcut", 1.5, "Max. ZN TDC cut value"}; + Configurable tdcZPmincut{"tdcZPmincut", -1.5, "Min. ZP TDC cut value"}; + Configurable tdcZPmaxcut{"tdcZPmaxcut", 1.5, "Max. ZP TDC cut value"}; // Configurable nBinsADC{"nBinsADC", 1000, "n bins 4 ZDC ADCs"}; - Configurable nBinsAmp{"nBinsAmp", 1025, "n bins 4 ZDC amplitudes"}; + Configurable nBinsAmpZN{"nBinsAmpZN", 1025, "n bins 4 ZN amplitudes"}; + Configurable nBinsAmpZP{"nBinsAmpZP", 1025, "n bins 4 ZP amplitudes"}; Configurable nBinsTDC{"nBinsTDC", 480, "n bins 4 TDCs"}; Configurable nBinsFit{"nBinsFit", 1000, "n bins 4 FIT"}; Configurable MaxZN{"MaxZN", 4099.5, "Max 4 ZN histos"}; @@ -51,54 +65,110 @@ struct ZDCLIAnalysis { Configurable MaxMultFV0{"MaxMultFV0", 3000, "Max 4 FV0 histos"}; Configurable MaxMultFT0{"MaxMultFT0", 3000, "Max 4 FT0 histos"}; // + Configurable enCalibZNA{"enCalibZNA", 1.0, "Energy calibration ZNA"}; + Configurable enCalibZNC{"enCalibZNC", 1.0, "Energy calibration ZNC"}; + Configurable enCalibZPA{"enCalibZPA", 1.0, "Energy calibration ZPA"}; + Configurable enCalibZPC{"enCalibZPC", 1.0, "Energy calibration ZPC"}; + // + Configurable applyZDCcut{"applyZDCcut", false, "Apply ZDC cut for light ion analysis"}; + Configurable zdcaCutLow{"zdcaCutLow", 12., "ZDCA cut for light ion analysis"}; + Configurable zdccCutLow{"zdccCutLow", 12., "ZDCC cut for light ion analysis"}; + // + Configurable selectZvtx{"selectZvtx", true, "Activate Z vertex selection"}; + Configurable sel8{"sel8", true, "Activate sel8 selection"}; + Configurable triggerTVX{"triggerTVX", true, "Activate trigger TVX selection"}; + Configurable doOccupancySel{"doOccupancySel", false, "Activate occupancy selection"}; + Configurable noSameBunchPileupCut{"noSameBunchPileupCut", true, "Activate no same bunch pileup selection"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", true, "Activate is good Z vertex FT0 vs PV selection"}; + Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", true, "Activate no collision in time range standard selection"}; + Configurable noTimeFrameBorder{"noTimeFrameBorder", true, "Activate no time frame border selection"}; + Configurable noITSROFFrameBorder{"noITSROFFrameBorder", true, "Activate no ITS ROF frame border selection"}; + Configurable isGoodITSLayersAll{"isGoodITSLayersAll", false, "Activate is good ITS layers all selection"}; + // HistogramRegistry registry{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext const&) { - registry.add("hZNApmc", "ZNApmc; ZNA amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZN}}}); - registry.add("hZPApmc", "ZPApmc; ZPA amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZP}}}); - registry.add("hZNCpmc", "ZNCpmc; ZNC amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZN}}}); - registry.add("hZPCpmc", "ZPCpmc; ZPC amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZP}}}); - registry.add("hZEM", "ZEM; ZEM1+ZEM2 amplitude; Entries", {HistType::kTH1F, {{nBinsAmp, -0.5, MaxZEM}}}); - registry.add("hZNAamplvsADC", "ZNA amplitude vs. ADC; ZNA ADC; ZNA amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCamplvsADC", "ZNC amplitude vs. ADC; ZNC ADC; ZNC amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAamplvsADC", "ZPA amplitude vs. ADC; ZPA ADC; ZPA amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCamplvsADC", "ZPC amplitude vs. ADC; ZPC ADC; ZPC amplitude", {HistType::kTH2F, {{{nBinsAmp, -0.5, 3. * MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNvsZEM", "ZN vs ZEM; ZEM; ZNA+ZNC", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZEM}, {nBinsAmp, -0.5, 2. * MaxZN}}}}); - registry.add("hZNAvsZNC", "ZNA vs ZNC; ZNC; ZNA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZN}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvsZPC", "ZPA vs ZPC; ZPC; ZPA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNAvsZPA", "ZNA vs ZPA; ZPA; ZNA", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvsZPC", "ZNC vs ZPC; ZPC; ZNC", {HistType::kTH2F, {{{nBinsAmp, -0.5, MaxZP}, {nBinsAmp, -0.5, MaxZN}}}}); + registry.add("hBCmask", "mask; BC; counts", {HistType::kTH1F, {{5, 0., 5.}}}); + registry.add("hcounts", "counts; selections; counts", {HistType::kTH1F, {{14, 0., 14.}}}); + registry.add("hzvertex", "z vertex; z_vertex (cm); Entries", {HistType::kTH1F, {{200, -20., 20.}}}); + // + registry.add("hZNApmc", "ZNA pmc; ZNA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN, -0.5, MaxZN}}}); + registry.add("hZPApmc", "ZPA pmc; ZPA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZP}}}); + registry.add("hZNCpmc", "ZNC pmc; ZNC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN, -0.5, MaxZN}}}); + registry.add("hZPCpmc", "ZPC pmc; ZPC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZP}}}); + registry.add("hZEM", "ZEM; ZEM1+ZEM2 amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZP, -0.5, MaxZEM}}}); + registry.add("hZDCA", "ZNA+ZPA; ZNA+ZPA; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + registry.add("hZDCC", "ZNC+ZPC; ZNC+ZPC; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + // + registry.add("hZDCApmcwZDCCcut", "ZDCA w. ZDCC cut; ZDCA amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + registry.add("hZDCCpmcwZDCAcut", "ZDCC w. ZDCA cut; ZDCC amplitude; Entries", {HistType::kTH1F, {{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}); + // + registry.add("hZNAtdc", "ZNA tdc; ZNA tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZPAtdc", "ZPA tdc; ZPA tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZNCtdc", "ZNC tdc; ZNC tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + registry.add("hZPCtdc", "ZPC tdc; ZPC tdc; Entries", {HistType::kTH1F, {{520, -14., 12.}}}); + // + registry.add("hZDCCvsA", "ZDC side C vs. side A; ZDCA; ZDCC", {HistType::kTH2F, {{{nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}, {nBinsAmpZN + nBinsAmpZP, -0.5, MaxZN + MaxZP}}}}); + // + registry.add("hZNAamplvsADC", "ZNA amplitude vs. ADC; ZNA ADC; ZNA amplitude", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, 3. * MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCamplvsADC", "ZNC amplitude vs. ADC; ZNC ADC; ZNC amplitude", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, 3. * MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAamplvsADC", "ZPA amplitude vs. ADC; ZPA ADC; ZPA amplitude", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, 3. * MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCamplvsADC", "ZPC amplitude vs. ADC; ZPC ADC; ZPC amplitude", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, 3. * MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNCcvsZNCsum", "ZNC PMC vs PMsum; ZNCC ADC; ZNCsum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); - registry.add("hZNAcvsZNAsum", "ZNA PMC vs PMsum; ZNAsum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); + registry.add("hZNvsZEM", "ZN vs ZEM; ZEM; ZNA+ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, 2. * MaxZN}}}}); + registry.add("hZPvsZEM", "ZP vs ZEM; ZEM; ZPA+ZPC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, 2. * MaxZP}}}}); + registry.add("hZNAvsZEM", "ZNA vs ZEM; ZEM; ZNA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsZEM", "ZNC vs ZEM; ZEM; ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvsZEM", "ZPA vs ZEM; ZEM; ZPA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvsZEM", "ZPC vs ZEM; ZEM; ZPC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZEM}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNCvstdc", "ZNC vs tdc; ZNC amplitude; ZNC TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvstdc", "ZNA vs tdc; ZNA amplitude; ZNA TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPCvstdc", "ZPC vs tdc; ZPC amplitude; ZPC TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvstdc", "ZPA vs tdc; ZPA amplitude; ZPA TDC", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNAvsZNC", "ZNA vs ZNC; ZNC; ZNA", {HistType::kTH2F, {{{nBinsAmpZN, -0.5, MaxZN}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvsZPC", "ZPA vs ZPC; ZPC; ZPA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZNAvsZPA", "ZNA vs ZPA; ZPA; ZNA", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsZPC", "ZNC vs ZPC; ZPC; ZNC", {HistType::kTH2F, {{{nBinsAmpZP, -0.5, MaxZP}, {nBinsAmpZN, -0.5, MaxZN}}}}); // - registry.add("hZNvsV0A", "ZN vs V0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFV0}, {nBinsAmp, -0.5, 2. * MaxZN}}}}); - registry.add("hZNAvsFT0A", "ZNA vs FT0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvsFT0C", "ZNC vs FT0C", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmp, -0.5, MaxZN}}}}); + registry.add("hZNCcvsZNCsum", "ZNC PMC vs PMsum; ZNCC ADC; ZNC sum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); + registry.add("hZNAcvsZNAsum", "ZNA PMC vs PMsum; ZNAC ADC; ZNA sum", {HistType::kTH2F, {{{nBinsADC, -0.5, 3. * MaxZN}, {nBinsADC, -0.5, 3. * MaxZN}}}}); // - registry.add("hZNAvscentrFT0A", "ZNA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvscentrFT0C", "ZNA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNAvscentrFT0M", "ZNA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvscentrFT0A", "ZPA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvscentrFT0C", "ZPA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPAvscentrFT0M", "ZPA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZNCvscentrFT0A", "ZNC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvscentrFT0C", "ZNC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvscentrFT0M", "ZNC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPCvscentrFT0A", "ZPC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvscentrFT0C", "ZPC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvscentrFT0M", "ZPC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNCvstdc", "ZNC vs tdc; ZNC TDC (ns); ZNC amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvstdc", "ZNA vs tdc; ZNA TDC (ns); ZNA amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPCvstdc", "ZPC vs tdc; ZPC TDC (ns); ZPC amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvstdc", "ZPA vs tdc; ZPA TDC (ns); ZPA amplitude", {HistType::kTH2F, {{{480, -13.5, 11.45}, {nBinsAmpZP, -0.5, MaxZP}}}}); // - registry.add("hZNAvstimestamp", "ZNA vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZNCvstimestamp", "ZNC vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZN}}}}); - registry.add("hZPAvstimestamp", "ZPA vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZP}}}}); - registry.add("hZPCvstimestamp", "ZPC vs timestamp", {HistType::kTH2F, {{{100, tStampMin, tStampMax}, {nBinsAmp, -0.5, MaxZP}}}}); + registry.add("hZNvsV0A", "ZN vs V0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFV0}, {nBinsAmpZN, -0.5, 2. * MaxZN}}}}); + registry.add("hZNAvsFT0A", "ZNA vs FT0A", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvsFT0C", "ZNC vs FT0C", {HistType::kTH2F, {{{nBinsFit, 0., MaxMultFT0}, {nBinsAmpZN, -0.5, MaxZN}}}}); + // + registry.add("hZNAvscentrFT0A", "ZNA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvscentrFT0C", "ZNA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNAvscentrFT0M", "ZNA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvscentrFT0A", "ZPA vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvscentrFT0C", "ZPA vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPAvscentrFT0M", "ZPA vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZNCvscentrFT0A", "ZNC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvscentrFT0C", "ZNC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvscentrFT0M", "ZNC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPCvscentrFT0A", "ZPC vs centrality FT0A", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvscentrFT0C", "ZPC vs centrality FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvscentrFT0M", "ZPC vs centrality FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {nBinsAmpZP, -0.5, MaxZP}}}}); + // + registry.add("hNnZNAvscentrFT0C", "N_{neutrons} in ZNA vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNCvscentrFT0C", "N_{neutrons} in ZNC vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPAvscentrFT0C", "N_{protons} in ZPA vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPCvscentrFT0C", "N_{protons} in ZPC vs FT0C", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNAvscentrFT0M", "N_{neutrons} in ZNA vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNnZNCvscentrFT0M", "N_{neutrons} in ZNC vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPAvscentrFT0M", "N_{protons} in ZPA vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + registry.add("hNpZPCvscentrFT0M", "N_{protons} in ZPC vs FT0M", {HistType::kTH2F, {{{100, 0., 100.}, {12, -0.5, 11.5}}}}); + // + registry.add("hNpvsNnZNA", "N_{protons} vs N_{neutrons} in ZNA", {HistType::kTH2F, {{{12, -0.5, 11.5}, {12, -0.5, 11.5}}}}); + registry.add("hNpvsNnZNC", "N_{protons} vs N_{neutrons} in ZNC", {HistType::kTH2F, {{{12, -0.5, 11.5}, {12, -0.5, 11.5}}}}); + // + registry.add("hZNAvstimestamp", "ZNA vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZNCvstimestamp", "ZNC vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZN, -0.5, MaxZN}}}}); + registry.add("hZPAvstimestamp", "ZPA vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZP, -0.5, MaxZP}}}}); + registry.add("hZPCvstimestamp", "ZPC vs timestamp", {HistType::kTH2F, {{{nBinstStamp, 0., tStampMax}, {nBinsAmpZP, -0.5, MaxZP}}}}); } void process(aod::ZDCLightIons const& zdclightions) @@ -108,6 +178,8 @@ struct ZDCLIAnalysis { auto tdcznc = zdc.zncTdc(); auto tdczpa = zdc.zpaTdc(); auto tdczpc = zdc.zpcTdc(); + auto tdczem1 = zdc.zem1Tdc(); + auto tdczem2 = zdc.zem2Tdc(); auto zna = zdc.znaAmpl(); auto znaADC = zdc.znaPmc(); auto znapm1 = zdc.znaPm1(); @@ -134,55 +206,416 @@ struct ZDCLIAnalysis { auto centrFT0A = zdc.centralityFt0a(); auto centrFT0M = zdc.centralityFt0m(); auto timestamp = zdc.timestamp(); - // auto selectionBits = zdc.selectionBits(); - - if ((useZvtx && (zvtx < zVval)) || !useZvtx) { - registry.get(HIST("hZNApmc"))->Fill(zna); - registry.get(HIST("hZNCpmc"))->Fill(znc); - registry.get(HIST("hZPApmc"))->Fill(zpa); - registry.get(HIST("hZPCpmc"))->Fill(zpc); - registry.get(HIST("hZEM"))->Fill(zem1 + zem2); + auto selectionBits = zdc.selectionBits(); + auto bcMask = zdc.bcMask(); + + bool isZNAtdc = false; + bool isZNCtdc = false; + bool isZPAtdc = false; + bool isZPCtdc = false; + bool isZEMtdc = false; + + if (tdczna > -99999.0) { + isZNAtdc = true; + } + if (tdcznc > -99999.0) { + isZNCtdc = true; + } + if (tdczpa > -99999.0) { + isZPAtdc = true; + } + if (tdczpc > -99999.0) { + isZPCtdc = true; + } + if (tdczem1 > -99999.0 || tdczem2 > -99999.0) { + isZEMtdc = true; + } + + if (tdcCut) { // TDC cuts applied + if ((tdczna < tdcZNmincut) || (tdczna > tdcZNmaxcut)) { + isZNAtdc = false; + } + if ((tdcznc < tdcZNmincut) || (tdcznc > tdcZNmaxcut)) { + isZNCtdc = false; + } + if ((tdczpa < tdcZPmincut) || (tdczpa > tdcZPmaxcut)) { + isZPAtdc = false; + } + if ((tdczpc < tdcZPmincut) || (tdczpc > tdcZPmaxcut)) { + isZPCtdc = false; + } + } + + bool eventSelected = false; + + // for BC events ------- + if (selectBC) { + // printf("BCmask: %x \n", bcMask); + + registry.get(HIST("hBCmask"))->Fill(0., 1.); + auto isB = CHECK_BIT(bcMask, 0); + if (isB) { + registry.get(HIST("hBCmask"))->Fill(1., 1.); + } + if (CHECK_BIT(bcMask, 1)) { + registry.get(HIST("hBCmask"))->Fill(2., 1.); + } + if (CHECK_BIT(bcMask, 2)) { + registry.get(HIST("hBCmask"))->Fill(3., 1.); + } + if (CHECK_BIT(bcMask, 3)) { + registry.get(HIST("hBCmask"))->Fill(4., 1.); + } + // + if (selectOnlyB && isB) { + eventSelected = true; + } else if (!selectOnlyB) { + eventSelected = true; + } + } + + // for collision events ------- + if (selectColl) { + bool zvtxSel = false; + if (selectZvtx && CHECK_BIT(selectionBits, 0)) { + zvtxSel = true; + } else if (!selectZvtx) { + zvtxSel = true; + } + // + bool ottoSel = false; + if (sel8 && CHECK_BIT(selectionBits, 1)) { + ottoSel = true; + } else if (!sel8) { + ottoSel = true; + } + // + bool isdoOccupancySel = false; + if (doOccupancySel && CHECK_BIT(selectionBits, 2)) + isdoOccupancySel = true; + else if (!doOccupancySel) + isdoOccupancySel = true; + // + bool isnoSameBunchPileupCut = false; + if (noSameBunchPileupCut && CHECK_BIT(selectionBits, 3)) + isnoSameBunchPileupCut = true; + else if (!noSameBunchPileupCut) + isnoSameBunchPileupCut = true; + // + bool isGoodZvtxFT0vsPVsel = false; + if (isGoodZvtxFT0vsPV && CHECK_BIT(selectionBits, 4)) + isGoodZvtxFT0vsPVsel = true; + else if (!isGoodZvtxFT0vsPV) + isGoodZvtxFT0vsPVsel = true; // - registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); - registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); - registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); - registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + bool isnoCollInTimeRangeStandard = false; + if (noCollInTimeRangeStandard && CHECK_BIT(selectionBits, 5)) + isnoCollInTimeRangeStandard = true; + else if (!noCollInTimeRangeStandard) + isnoCollInTimeRangeStandard = true; // - registry.get(HIST("hZNvsZEM"))->Fill(zem1 + zem2, zna + znc); - registry.get(HIST("hZNAvsZNC"))->Fill(znc, zna); - registry.get(HIST("hZPAvsZPC"))->Fill(zpc, zpa); - registry.get(HIST("hZNAvsZPA"))->Fill(zpa, zna); - registry.get(HIST("hZNCvsZPC"))->Fill(zpc, znc); + bool isnoTimeFrameBorder = false; + if (noTimeFrameBorder && CHECK_BIT(selectionBits, 6)) + isnoTimeFrameBorder = true; + else if (!noTimeFrameBorder) + isnoTimeFrameBorder = true; // - registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); - registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); - registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); - registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + bool isnoITSROFFrameBorder = false; + if (noITSROFFrameBorder && CHECK_BIT(selectionBits, 7)) + isnoITSROFFrameBorder = true; + else if (!noITSROFFrameBorder) + isnoITSROFFrameBorder = true; // - registry.get(HIST("hZNAcvsZNAsum"))->Fill(znapm1 + znapm2 + znapm3 + znapm4, zna); - registry.get(HIST("hZNCcvsZNCsum"))->Fill(zncpm1 + zncpm2 + zncpm3 + zncpm4, znc); + bool isGoodITSLayersAllsel = false; + if (isGoodITSLayersAll && CHECK_BIT(selectionBits, 8)) + isGoodITSLayersAllsel = true; + else if (!isGoodITSLayersAll) + isGoodITSLayersAllsel = true; + // + bool istriggerTVX = false; + if (triggerTVX && CHECK_BIT(selectionBits, 9)) + istriggerTVX = true; + else if (!triggerTVX) + istriggerTVX = true; + + if (zvtxSel && ottoSel && istriggerTVX && isdoOccupancySel && isnoSameBunchPileupCut && isGoodZvtxFT0vsPVsel && isnoCollInTimeRangeStandard && isnoTimeFrameBorder && isnoITSROFFrameBorder && isGoodITSLayersAllsel) + eventSelected = true; + // if (zvtxSel && ottoSel && isnoSameBunchPileupCut) eventSelected = true; + + if (eventSelected) { + registry.get(HIST("hcounts"))->Fill(0., 1.); + if (isZNAtdc) + registry.get(HIST("hcounts"))->Fill(1., 1.); + if (isZPAtdc) + registry.get(HIST("hcounts"))->Fill(2., 1.); + if (isZNCtdc) + registry.get(HIST("hcounts"))->Fill(3., 1.); + if (isZPCtdc) + registry.get(HIST("hcounts"))->Fill(4., 1.); + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hcounts"))->Fill(5., 1.); + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hcounts"))->Fill(6., 1.); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hcounts"))->Fill(7., 1.); + if (isZNAtdc || isZPAtdc || isZNCtdc || isZPCtdc) + registry.get(HIST("hcounts"))->Fill(8., 1.); + if (isZNAtdc && isZNCtdc) + registry.get(HIST("hcounts"))->Fill(9., 1.); + if (isZNAtdc && isZPAtdc) + registry.get(HIST("hcounts"))->Fill(10., 1.); + if (isZNCtdc && isZPCtdc) + registry.get(HIST("hcounts"))->Fill(11., 1.); + if (isZEMtdc) + registry.get(HIST("hcounts"))->Fill(12., 1.); + } + } + + if (eventSelected) { + + if (enCalibZNA > 0.) { + zna *= enCalibZNA; + znaADC *= enCalibZNA; + znapm1 *= enCalibZNA; + znapm2 *= enCalibZNA; + znapm3 *= enCalibZNA; + znapm4 *= enCalibZNA; + } + if (enCalibZNC > 0.) { + znc *= enCalibZNC; + zncADC *= enCalibZNC; + zncpm1 *= enCalibZNC; + zncpm2 *= enCalibZNC; + zncpm3 *= enCalibZNC; + zncpm4 *= enCalibZNC; + } + if (enCalibZPA > 0.) { + zpa *= enCalibZPA; + zpaADC *= enCalibZPA; + } + if (enCalibZPC > 0.) { + zpc *= enCalibZPC; + zpcADC *= enCalibZPC; + } + + registry.get(HIST("hzvertex"))->Fill(zvtx); + + if (!applyZDCcut) { + + if (isZNAtdc) + registry.get(HIST("hZNApmc"))->Fill(zna); + if (isZNCtdc) + registry.get(HIST("hZNCpmc"))->Fill(znc); + if (isZPAtdc) + registry.get(HIST("hZPApmc"))->Fill(zpa); + if (isZPCtdc) + registry.get(HIST("hZPCpmc"))->Fill(zpc); + // + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hZDCA"))->Fill(zna + zpa); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hZDCC"))->Fill(znc + zpc); + // + if (isZNAtdc) + registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); + if (isZNCtdc) + registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); + if (isZPAtdc) + registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + // + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNAvsZNC"))->Fill(znc, zna); + if (isZPAtdc || isZPCtdc) + registry.get(HIST("hZPAvsZPC"))->Fill(zpc, zpa); + if (isZNAtdc || isZPAtdc) + registry.get(HIST("hZNAvsZPA"))->Fill(zpa, zna); + if (isZNCtdc || isZPCtdc) + registry.get(HIST("hZNCvsZPC"))->Fill(zpc, znc); + // + if (isZNAtdc) + registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); + if (isZNCtdc) + registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); + if (isZPAtdc) + registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + // + if (isZNAtdc) + registry.get(HIST("hZNAcvsZNAsum"))->Fill(0.25 * (znapm1 + znapm2 + znapm3 + znapm4), zna); + if (isZNCtdc) + registry.get(HIST("hZNCcvsZNCsum"))->Fill(0.25 * (zncpm1 + zncpm2 + zncpm3 + zncpm4), znc); + // + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNvsV0A"))->Fill(multV0A / 100., zna + znc); + if (isZNAtdc) + registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); + if (isZNCtdc) + registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + } else { + bool isZNChigh = false; + if (znc >= zdccCutLow) + isZNChigh = true; + bool isZNAhigh = false; + if (zna >= zdcaCutLow) + isZNAhigh = true; + bool isZPAhigh = false; + if (zpa >= zdcaCutLow) + isZPAhigh = true; + bool isZPChigh = false; + if (zpc >= zdccCutLow) + isZPChigh = true; + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNApmc"))->Fill(zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCpmc"))->Fill(znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPApmc"))->Fill(zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCpmc"))->Fill(zpc); + // + if ((isZNAtdc || isZPAtdc) && (isZNChigh && isZPChigh)) + registry.get(HIST("hZDCA"))->Fill(zna + zpa); + if ((isZNCtdc || isZPCtdc) && (isZNAhigh && isZPAhigh)) + registry.get(HIST("hZDCC"))->Fill(znc + zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAamplvsADC"))->Fill(znaADC, zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCamplvsADC"))->Fill(zncADC, znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPAamplvsADC"))->Fill(zpaADC, zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCamplvsADC"))->Fill(zpcADC, zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAvstdc"))->Fill(tdczna, zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCvstdc"))->Fill(tdcznc, znc); + if (isZPAtdc && isZPChigh) + registry.get(HIST("hZPAvstdc"))->Fill(tdczpa, zpa); + if (isZPCtdc && isZPAhigh) + registry.get(HIST("hZPCvstdc"))->Fill(tdczpc, zpc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAcvsZNAsum"))->Fill(0.25 * (znapm1 + znapm2 + znapm3 + znapm4), zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCcvsZNCsum"))->Fill(0.25 * (zncpm1 + zncpm2 + zncpm3 + zncpm4), znc); + // + if (isZNAtdc && isZNChigh) + registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); + if (isZNCtdc && isZNAhigh) + registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + } + + // Timestamp + /*if (tStampOffset > timestamp) { + printf("\n\n ################# OFFSET timestamp too large!!!!!!!!!!!!!!!!!!!!!!!!!! > timestamp %lu \n\n", timestamp); + return; + }*/ + // float tsh = (timestamp / 1000.) - (tStampOffset / 1000.); // in hours + /*if (timestamp > tStampMax) { + printf("\n\n MAXIMUM timestamp too small!!!!!!!!!!!!!!!!!!!!!!!!!! > timestamp-offset %f \n\n", timestamp); + return; + }*/ + + if (!applyZDCcut) { + if (isZNAtdc) + registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); + if (isZNCtdc) + registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); + if (isZPAtdc) + registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); + if (isZPCtdc) + registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + } else { + if (isZNAtdc && znc >= zdccCutLow) { + registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); + } + if (isZPAtdc && zpc >= zdccCutLow) { + registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); + } + if ((isZNAtdc || isZPAtdc) && (znc >= zdccCutLow && zpc >= zdccCutLow)) { + registry.get(HIST("hZDCApmcwZDCCcut"))->Fill(zna + zpa); + } + if (isZNCtdc && zna >= zdcaCutLow) { + registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); + } + if (isZPCtdc && zpa >= zdcaCutLow) { + registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + } + if ((isZNCtdc || isZPCtdc) && (zna >= zdcaCutLow && zpa >= zdcaCutLow)) { + registry.get(HIST("hZDCCpmcwZDCAcut"))->Fill(znc + zpc); + } + } + // TDCs + if (isZNAtdc) + registry.get(HIST("hZNAtdc"))->Fill(tdczna); + if (isZNCtdc) + registry.get(HIST("hZNCtdc"))->Fill(tdcznc); + if (isZPAtdc) + registry.get(HIST("hZPAtdc"))->Fill(tdczpa); + if (isZPCtdc) + registry.get(HIST("hZPCtdc"))->Fill(tdczpc); // - registry.get(HIST("hZNvsV0A"))->Fill(multV0A / 100., zna + znc); - registry.get(HIST("hZNAvsFT0A"))->Fill((multFT0A) / 100., zna); - registry.get(HIST("hZNCvsFT0C"))->Fill((multFT0C) / 100., znc); + if (isZEMtdc) { + registry.get(HIST("hZEM"))->Fill(zem1 + zem2); + registry.get(HIST("hZNAvsZEM"))->Fill(zem1 + zem2, zna); + registry.get(HIST("hZNCvsZEM"))->Fill(zem1 + zem2, znc); + registry.get(HIST("hZPAvsZEM"))->Fill(zem1 + zem2, zpa); + registry.get(HIST("hZPCvsZEM"))->Fill(zem1 + zem2, zpc); + } + if (isZNAtdc || isZNCtdc) + registry.get(HIST("hZNvsZEM"))->Fill(zem1 + zem2, zna + znc); + if (isZPAtdc || isZPCtdc) + registry.get(HIST("hZPvsZEM"))->Fill(zem1 + zem2, zpa + zpc); // - registry.get(HIST("hZNAvscentrFT0A"))->Fill(centrFT0A, zna); - registry.get(HIST("hZNAvscentrFT0C"))->Fill(centrFT0C, zna); - registry.get(HIST("hZNAvscentrFT0M"))->Fill(centrFT0M, zna); - registry.get(HIST("hZPAvscentrFT0A"))->Fill(centrFT0A, zpa); - registry.get(HIST("hZPAvscentrFT0C"))->Fill(centrFT0C, zpa); - registry.get(HIST("hZPAvscentrFT0M"))->Fill(centrFT0M, zpa); - registry.get(HIST("hZNCvscentrFT0A"))->Fill(centrFT0A, znc); - registry.get(HIST("hZNCvscentrFT0C"))->Fill(centrFT0C, znc); - registry.get(HIST("hZNCvscentrFT0M"))->Fill(centrFT0M, znc); - registry.get(HIST("hZPCvscentrFT0A"))->Fill(centrFT0A, zpc); - registry.get(HIST("hZPCvscentrFT0C"))->Fill(centrFT0C, zpc); - registry.get(HIST("hZPCvscentrFT0M"))->Fill(centrFT0M, zpc); + if (isZNAtdc || isZNCtdc || isZPAtdc || isZPCtdc) + registry.get(HIST("hZDCCvsA"))->Fill(zna + zpa, znc + zpc); // - registry.get(HIST("hZNAvstimestamp"))->Fill(timestamp, zna); - registry.get(HIST("hZNCvstimestamp"))->Fill(timestamp, znc); - registry.get(HIST("hZPAvstimestamp"))->Fill(timestamp, zpa); - registry.get(HIST("hZPCvstimestamp"))->Fill(timestamp, zpc); + if (centrFT0C > -1. && centrFT0C < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0C"))->Fill(centrFT0C, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0C"))->Fill(centrFT0C, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0C"))->Fill(centrFT0C, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0C"))->Fill(centrFT0C, zpc); + registry.get(HIST("hNnZNAvscentrFT0C"))->Fill(centrFT0C, zna / 2.680); + registry.get(HIST("hNnZNCvscentrFT0C"))->Fill(centrFT0C, znc / 2.680); + registry.get(HIST("hNpZPAvscentrFT0C"))->Fill(centrFT0C, zpa / 2.680); + registry.get(HIST("hNpZPCvscentrFT0C"))->Fill(centrFT0C, zpc / 2.680); + } + if (centrFT0A > -1. && centrFT0A < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0A"))->Fill(centrFT0A, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0A"))->Fill(centrFT0A, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0A"))->Fill(centrFT0A, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0A"))->Fill(centrFT0A, zpc); + } + if (centrFT0M > -1. && centrFT0M < 101.) { + if (isZNAtdc) + registry.get(HIST("hZNAvscentrFT0M"))->Fill(centrFT0M, zna); + if (isZPAtdc) + registry.get(HIST("hZPAvscentrFT0M"))->Fill(centrFT0M, zpa); + if (isZNCtdc) + registry.get(HIST("hZNCvscentrFT0M"))->Fill(centrFT0M, znc); + if (isZPCtdc) + registry.get(HIST("hZPCvscentrFT0M"))->Fill(centrFT0M, zpc); + registry.get(HIST("hNnZNAvscentrFT0M"))->Fill(centrFT0M, zna / 2.680); + registry.get(HIST("hNnZNCvscentrFT0M"))->Fill(centrFT0M, znc / 2.680); + registry.get(HIST("hNpZPAvscentrFT0M"))->Fill(centrFT0M, zpa / 2.680); + registry.get(HIST("hNpZPCvscentrFT0M"))->Fill(centrFT0M, zpc / 2.680); + registry.get(HIST("hNpvsNnZNA"))->Fill(zna / 2.680, zpa / 2.680); + registry.get(HIST("hNpvsNnZNC"))->Fill(znc / 2.680, zpc / 2.680); + } } } } diff --git a/Common/Tools/EventSelectionModule.h b/Common/Tools/EventSelectionModule.h index 76e4cde6bd5..09f114364c7 100644 --- a/Common/Tools/EventSelectionModule.h +++ b/Common/Tools/EventSelectionModule.h @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file EventSelectionModule.h -/// \brief +/// \brief Event selection module /// \author ALICE #ifndef COMMON_TOOLS_EVENTSELECTIONMODULE_H_ @@ -59,8 +59,8 @@ #include #include -#define bitcheck(var, nbit) ((var) & (static_cast(1) << (nbit))) -#define bitcheck64(var, nbit) ((var) & (static_cast(1) << (nbit))) +#define BITCHECK(var, nbit) ((var) & (static_cast(1) << (nbit))) +#define BITCHECK64(var, nbit) ((var) & (static_cast(1) << (nbit))) //__________________________________________ // MultModule @@ -77,7 +77,7 @@ static const int32_t nBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; // for providing temporary buffer // FIXME ideally cursors could be readable // to avoid duplicate memory allocation but ok -struct bcselEntry { +struct BcselEntry { uint32_t alias{0}; uint64_t selection{0}; uint32_t rct{0}; @@ -88,23 +88,22 @@ struct bcselEntry { }; // bc selection configurables -struct bcselConfigurables : o2::framework::ConfigurableGroup { +struct BcselConfigurables : o2::framework::ConfigurableGroup { std::string prefix = "bcselOpts"; - o2::framework::Configurable amIneeded{"amIneeded", -1, "run BC selection or not. -1: automatic; 0: no; 1: yes"}; // o2-linter: disable=name/configurable (temporary fix) - o2::framework::Configurable confTriggerBcShift{"triggerBcShift", 0, "set either custom shift or 999 for apass2/apass3 in LHC22o-t"}; // o2-linter: disable=name/configurable (temporary fix) - o2::framework::Configurable confITSROFrameStartBorderMargin{"ITSROFrameStartBorderMargin", -1, "Number of bcs at the start of ITS RO Frame border. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) - o2::framework::Configurable confITSROFrameEndBorderMargin{"ITSROFrameEndBorderMargin", -1, "Number of bcs at the end of ITS RO Frame border. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) - o2::framework::Configurable confTimeFrameStartBorderMargin{"TimeFrameStartBorderMargin", -1, "Number of bcs to cut at the start of the Time Frame. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) - o2::framework::Configurable confTimeFrameEndBorderMargin{"TimeFrameEndBorderMargin", -1, "Number of bcs to cut at the end of the Time Frame. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) - o2::framework::Configurable confCheckRunDurationLimits{"checkRunDurationLimits", false, "Check if the BCs are within the run duration limits"}; // o2-linter: disable=name/configurable (temporary fix) - o2::framework::Configurable> confMaxInactiveChipsPerLayer{"maxInactiveChipsPerLayer", {8, 8, 8, 111, 111, 195, 195}, "Maximum allowed number of inactive ITS chips per layer"}; - o2::framework::Configurable confNumberOfOrbitsPerTF{"NumberOfOrbitsPerTF", -1, "Number of orbits per Time Frame. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable amIneeded{"amIneeded", -1, "run BC selection or not. -1: automatic; 0: no; 1: yes"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable confITSROFrameStartBorderMargin{"ITSROFrameStartBorderMargin", -1, "Number of bcs at the start of ITS RO Frame border. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable confITSROFrameEndBorderMargin{"ITSROFrameEndBorderMargin", -1, "Number of bcs at the end of ITS RO Frame border. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable confTimeFrameStartBorderMargin{"TimeFrameStartBorderMargin", -1, "Number of bcs to cut at the start of the Time Frame. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable confTimeFrameEndBorderMargin{"TimeFrameEndBorderMargin", -1, "Number of bcs to cut at the end of the Time Frame. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable confCheckRunDurationLimits{"checkRunDurationLimits", false, "Check if the BCs are within the run duration limits"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable> confMaxInactiveChipsPerLayer{"maxInactiveChipsPerLayer", {8, 8, 8, 111, 111, 195, 195}, "Maximum allowed number of inactive ITS chips per layer"}; // o2-linter: disable=name/configurable (temporary fix) + o2::framework::Configurable confNumberOfOrbitsPerTF{"NumberOfOrbitsPerTF", -1, "Number of orbits per Time Frame. Take from CCDB if -1"}; // o2-linter: disable=name/configurable (temporary fix) }; // event selection configurables -struct evselConfigurables : o2::framework::ConfigurableGroup { +struct EvselConfigurables : o2::framework::ConfigurableGroup { std::string prefix = "evselOpts"; - bool isMC_metadata = false; + bool isMC_metadata = false; // o2-linter: disable=name/function-variable (temporary fix) o2::framework::Configurable amIneeded{"amIneeded", -1, "run event selection or not. -1: automatic; 0: no; 1: yes"}; // o2-linter: disable=name/configurable (temporary fix) o2::framework::Configurable muonSelection{"muonSelection", 0, "0 - barrel, 1 - muon selection with pileup cuts, 2 - muon selection without pileup cuts"}; o2::framework::Configurable maxDiffZvtxFT0vsPV{"maxDiffZvtxFT0vsPV", 1., "maximum difference (in cm) between z-vertex from FT0 and PV"}; @@ -130,7 +129,7 @@ struct evselConfigurables : o2::framework::ConfigurableGroup { }; // luminosity configurables -struct lumiConfigurables : o2::framework::ConfigurableGroup { +struct LumiConfigurables : o2::framework::ConfigurableGroup { std::string prefix = "lumiOpts"; o2::framework::Configurable amIneeded{"amIneeded", -1, "run BC selection or not. -1: automatic; 0: no; 1: yes"}; // o2-linter: disable=name/configurable (temporary fix) }; @@ -144,7 +143,7 @@ class BcSelectionModule } // declaration of structs here // (N.B.: will be invisible to the outside, create your own copies) - o2::common::eventselection::bcselConfigurables bcselOpts; + o2::common::eventselection::BcselConfigurables bcselOpts; int lastRun = -1; int64_t lastTF = -1; @@ -311,6 +310,7 @@ class BcSelectionModule return; } bcselbuffer.clear(); + bcselbuffer.reserve(bcs.size()); for (const auto& bc : bcs) { uint64_t timestamp = timestamps[bc.globalIndex()]; par = ccdb->template getForTimeStamp("EventSelection/EventSelectionParams", timestamp); @@ -425,7 +425,7 @@ class BcSelectionModule uint32_t rct = 0; // initialize properties - o2::common::eventselection::bcselEntry entry; + o2::common::eventselection::BcselEntry entry; entry.alias = alias; entry.selection = selection; entry.rct = rct; @@ -453,17 +453,8 @@ class BcSelectionModule return; // don't do anything in case configuration reported not ok int run = bcs.iteratorAt(0).runNumber(); - // map from GlobalBC to BcId needed to find triggerBc - std::map mapGlobalBCtoBcId; - for (const auto& bc : bcs) { - mapGlobalBCtoBcId[bc.globalBC()] = bc.globalIndex(); - } - - int triggerBcShift = bcselOpts.confTriggerBcShift; - if (bcselOpts.confTriggerBcShift == 999) { // o2-linter: disable=magic-number (special shift for early 2022 data) - triggerBcShift = (run <= 526766 || (run >= 526886 && run <= 527237) || (run >= 527259 && run <= 527518) || run == 527523 || run == 527734 || run >= 534091) ? 0 : 294; // o2-linter: disable=magic-number (magic list of runs) - } + bcselbuffer.reserve(bcs.size()); // bc loop for (auto bc : bcs) { // o2-linter: disable=const-ref-in-for-loop (use bc as nonconst iterator) uint64_t timestamp = timestamps[bc.globalIndex()]; @@ -481,11 +472,8 @@ class BcSelectionModule } uint32_t alias{0}; - // workaround for pp2022 (trigger info is shifted by -294 bcs) - int32_t triggerBcId = mapGlobalBCtoBcId[bc.globalBC() + triggerBcShift]; - if (triggerBcId && aliases) { - auto triggerBc = bcs.iteratorAt(triggerBcId); - uint64_t triggerMask = triggerBc.triggerMask(); + if (aliases) { + uint64_t triggerMask = bc.triggerMask(); for (const auto& al : aliases->GetAliasToTriggerMaskMap()) { if (triggerMask & al.second) { alias |= BIT(al.first); @@ -611,7 +599,7 @@ class BcSelectionModule } // initialize properties - o2::common::eventselection::bcselEntry entry; + o2::common::eventselection::BcselEntry entry; entry.alias = alias; entry.selection = selection; entry.rct = rct; @@ -707,20 +695,20 @@ class EventSelectionModule float calcWeightForOccupancy(float dt) { float wOccup = 0; - if (dt >= -40 && dt < -5) // collisions in the past // o2-linter: disable=magic-number - wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number - else if (dt >= -5 && dt < 15) // collisions near a given one // o2-linter: disable=magic-number + if (dt >= -40 && dt < -5) // o2-linter: disable=magic-number (collisions in the past) + wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) + else if (dt >= -5 && dt < 15) // o2-linter: disable=magic-number (collisions near a given one) wOccup = 1; - else if (dt >= 15 && dt < 40) // collisions from the future // o2-linter: disable=magic-number - wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number - else if (dt >= 40 && dt < 100) // collisions from the distant future // o2-linter: disable=magic-number - wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number + else if (dt >= 15 && dt < 40) // o2-linter: disable=magic-number (collisions from the future) + wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) + else if (dt >= 40 && dt < 100) // o2-linter: disable=magic-number (collisions from the distant future) + wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) return wOccup; } // declaration of structs here // (N.B.: will be invisible to the outside, create your own copies) - o2::common::eventselection::evselConfigurables evselOpts; + o2::common::eventselection::EvselConfigurables evselOpts; template void init(TContext& context, TEvSelOpts const& external_evselopts, THistoRegistry& histos, TMetadataInfo const& metadataInfo) @@ -794,7 +782,7 @@ class EventSelectionModule LOGP(debug, "ITS ROF Offset={} ITS ROF Length={}", rofOffset, rofLength); // special treatment of light ion runs - if (lastRun >= 564356 && lastRun <= 564472) { + if (lastRun >= 564356 && lastRun <= 564472) { // o2-linter: disable=magic-number (light ion run range) for (uint32_t i = 0; i < sizeof(runListLightIons) / sizeof(*runListLightIons); i++) { if (runListLightIons[i] == lastRun) { runLightIons = lastRun; @@ -804,9 +792,9 @@ class EventSelectionModule diffVzParMean = *parMeans; diffVzParSigma = *parSigmas; LOGP(info, ">>> special treatment for diffVz for light ion run {}", runLightIons); - for (int j = 0; j < 5; j++) + for (size_t j = 0; j < diffVzParMean.size(); j++) LOGP(info, " mean par {} = {}", j, diffVzParMean[j]); - for (int j = 0; j < 5; j++) + for (size_t j = 0; j < diffVzParSigma.size(); j++) LOGP(info, " sigma par {} = {}", j, diffVzParSigma[j]); break; } @@ -877,18 +865,18 @@ class EventSelectionModule } // TODO introduce array of sel[0]... sel[8] or similar? - bool sel8 = bitcheck64(selection, aod::evsel::kIsBBT0A) && bitcheck64(selection, aod::evsel::kIsBBT0C); // TODO apply other cuts for sel8 - bool sel1 = bitcheck64(selection, aod::evsel::kIsINT1); - sel1 = sel1 && bitcheck64(selection, aod::evsel::kNoBGV0A); - sel1 = sel1 && bitcheck64(selection, aod::evsel::kNoBGV0C); - sel1 = sel1 && bitcheck64(selection, aod::evsel::kNoTPCLaserWarmUp); - sel1 = sel1 && bitcheck64(selection, aod::evsel::kNoTPCHVdip); + bool sel8 = BITCHECK64(selection, aod::evsel::kIsBBT0A) && BITCHECK64(selection, aod::evsel::kIsBBT0C); // TODO apply other cuts for sel8 + bool sel1 = BITCHECK64(selection, aod::evsel::kIsINT1); + sel1 = sel1 && BITCHECK64(selection, aod::evsel::kNoBGV0A); + sel1 = sel1 && BITCHECK64(selection, aod::evsel::kNoBGV0C); + sel1 = sel1 && BITCHECK64(selection, aod::evsel::kNoTPCLaserWarmUp); + sel1 = sel1 && BITCHECK64(selection, aod::evsel::kNoTPCHVdip); // INT1 (SPDFO>0 | V0A | V0C) minimum bias trigger logic used in pp2010 and pp2011 bool isINT1period = bc.runNumber() <= 136377 || (bc.runNumber() >= 144871 && bc.runNumber() <= 159582); // o2-linter: disable=magic-number (magic run numbers) // fill counters - if (evselOpts.isMC == 1 || (!isINT1period && bitcheck(alias, kINT7)) || (isINT1period && bitcheck(alias, kINT1))) { + if (evselOpts.isMC == 1 || (!isINT1period && BITCHECK(alias, kINT7)) || (isINT1period && BITCHECK(alias, kINT1))) { histos.template get(HIST("eventselection/hColCounterAll"))->Fill(Form("%d", bc.runNumber()), 1); if ((!isINT1period && sel7) || (isINT1period && sel1)) { histos.template get(HIST("eventselection/hColCounterAcc"))->Fill(Form("%d", bc.runNumber()), 1); @@ -927,7 +915,7 @@ class EventSelectionModule } auto selection = bcselbuffer[bc.globalIndex()].selection; - if (bitcheck64(selection, aod::evsel::kIsTriggerTVX)) { + if (BITCHECK64(selection, aod::evsel::kIsTriggerTVX)) { mapGlobalBcWithTVX[globalBC] = bc.globalIndex(); mapGlobalBcVtxZ[globalBC] = bc.has_ft0() ? bc.ft0().posZ() : 0; } @@ -1036,7 +1024,7 @@ class EventSelectionModule int32_t localBC = globalBC % nBCsPerOrbit; int32_t bcFromPattern = bcsPattern.at(i); int64_t bcDiff = bcFromPattern - localBC; - if (std::abs(bcDiff) <= 20) { + if (std::abs(bcDiff) <= 20) { // o2-linter: disable=magic-number (max BC difference to match pattern) foundGlobalBC = (globalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPattern; break; // the bc in pattern is found } @@ -1073,7 +1061,7 @@ class EventSelectionModule for (uint32_t j = 0; j < bcsPattern.size(); j++) { int32_t bcFromPatternBest = bcsPattern.at(j); int64_t bcDiff = bcFromPatternBest - (bestGlobalBC % nBCsPerOrbit); - if (std::abs(bcDiff) <= 20) { + if (std::abs(bcDiff) <= 20) { // o2-linter: disable=magic-number (max BC difference to match pattern) foundGlobalBC = (bestGlobalBC / nBCsPerOrbit) * nBCsPerOrbit + bcFromPatternBest; break; // the bc in pattern is found } @@ -1177,12 +1165,12 @@ class EventSelectionModule auto bcselEntry = bcselbuffer[foundBC]; // check if we are close to ITS ROF borders => N ITS tracks is not reliable, and FT0C ampl can be used for occupancy estimation // denominator for vAmpFT0CperColl is the approximate conversion factor b/n FT0C ampl and number of PV tracks after cuts - vProxyForCollNtracks[colIndex] = bitcheck64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder) ? vTracksITS567perColl[colIndex] : vAmpFT0CperColl[colIndex] / 10.; + vProxyForCollNtracks[colIndex] = BITCHECK64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder) ? vTracksITS567perColl[colIndex] : vAmpFT0CperColl[colIndex] / 10.; - if (!bitcheck64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder)) { + if (!BITCHECK64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder)) { vIsCollRejectedByTFborderCut[colIndex] = true; } - if (nBCsPerTF - bcInTF < 4000 * 2) { + if (nBCsPerTF - bcInTF < 4000 * 2) { // o2-linter: disable=magic-number (BCs within last drift time window) vCanHaveAssocCollsWithinLastDriftTime[colIndex] = true; } } @@ -1335,7 +1323,7 @@ class EventSelectionModule float vZ = col.posZ(); // ### in-ROF occupancy - std::vector vAssocCollInSameROF = vCollsInSameITSROF[colIndex]; + const std::vector& vAssocCollInSameROF = vCollsInSameITSROF[colIndex]; int nITS567tracksForSameRofVetoStrict = 0; // to veto events with other collisions in the same ITS ROF int nCollsInRofWithFT0CAboveVetoStandard = 0; // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold int nITS567tracksForRofVetoOnCloseVz = 0; // to veto events with nearby collisions with close vZ @@ -1353,7 +1341,7 @@ class EventSelectionModule vNoCollInSameRofWithCloseVz[colIndex] = (nITS567tracksForRofVetoOnCloseVz == 0); // ### occupancy in previous ROF - std::vector vAssocCollInPrevROF = vCollsInPrevITSROF[colIndex]; + const std::vector& vAssocCollInPrevROF = vCollsInPrevITSROF[colIndex]; float totalFT0amplInPrevROF = 0; for (uint32_t iCol = 0; iCol < vAssocCollInPrevROF.size(); iCol++) { int thisColIndex = vAssocCollInPrevROF[iCol]; @@ -1363,8 +1351,8 @@ class EventSelectionModule vNoHighMultCollInPrevRof[colIndex] = (totalFT0amplInPrevROF < evselOpts.confFT0CamplCutVetoOnCollInROF); // ### occupancy in time windows - std::vector vAssocToThisCol = vCollsInTimeWin[colIndex]; - std::vector vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; + const std::vector& vAssocToThisCol = vCollsInTimeWin[colIndex]; + const std::vector& vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; int nITS567tracksInFullTimeWindow = 0; float sumAmpFT0CInFullTimeWindow = 0; int nITS567tracksForVetoNarrow = 0; // to veto events with nearby collisions (narrow range) with per-collision multiplicity above threshold @@ -1381,7 +1369,7 @@ class EventSelectionModule nITS567tracksForVetoStrict += vProxyForCollNtracks[thisColIndex]; // veto on high-mult collisions nearby, where artificial structures in the dt-occupancy plots are observed - if (dt > -4.0 && dt < 2.0 && vAmpFT0CperColl[thisColIndex] > evselOpts.confFT0CamplCutVetoOnCollInTimeRange) { // dt in us // o2-linter: disable=magic-number + if (dt > -4.0 && dt < 2.0 && vAmpFT0CperColl[thisColIndex] > evselOpts.confFT0CamplCutVetoOnCollInTimeRange) { // o2-linter: disable=magic-number (dt in us, time window for high-mult veto) nCollsWithFT0CAboveVetoStandard++; } @@ -1428,7 +1416,7 @@ class EventSelectionModule if (evselOpts.confUseWeightsForOccupancyVariable) { wOccup = calcWeightForOccupancy(dt / 1e3); // ns -> us } - if (multT0C > 50.) // multiplicity in TVX is non-negligible, take it into occupancy calc + if (multT0C > 50.) // o2-linter: disable=magic-number (multiplicity threshold for occupancy calc) { nITS567tracksInFullTimeWindow += wOccup * multT0C / 10.; sumAmpFT0CInFullTimeWindow += wOccup * multT0C; @@ -1472,13 +1460,13 @@ class EventSelectionModule } else { // special treatment of light ion runs float multT0A = bc.ft0().sumAmpA(); float multT0C = bc.ft0().sumAmpC(); - float T0M = multT0A + multT0C; + float multT0M = multT0A + multT0C; // calc mean at this T0 ampl. - float x = (T0M < 50 ? 50 : T0M); - double diffMean = diffVzParMean[0] + diffVzParMean[1] * pow(x, diffVzParMean[2]) + diffVzParMean[3] * pow(x, diffVzParMean[4]); + float x = (multT0M < 50 ? 50 : multT0M); // o2-linter: disable=magic-number (min T0M for mean parametrization) + double diffMean = diffVzParMean[0] + diffVzParMean[1] * std::pow(x, diffVzParMean[2]) + diffVzParMean[3] * std::pow(x, diffVzParMean[4]); // calc sigma at this T0 ampl. - x = (T0M < 20 ? 20 : (T0M > 1.2e4 ? 1.2e4 : T0M)); - double diffSigma = diffVzParSigma[0] + diffVzParSigma[1] * pow(x, diffVzParSigma[2]) + diffVzParSigma[3] * pow(x, diffVzParSigma[4]); + x = (multT0M < 20 ? 20 : (multT0M > 1.2e4 ? 1.2e4 : multT0M)); // o2-linter: disable=magic-number (min/max T0M for sigma parametrization) + double diffSigma = diffVzParSigma[0] + diffVzParSigma[1] * std::pow(x, diffVzParSigma[2]) + diffVzParSigma[3] * std::pow(x, diffVzParSigma[4]); float nSigma = evselOpts.confLightIonsNsigmaOnVzDiff; float margin = evselOpts.confLightIonsMarginVzDiff; isGoodZvtxFT0vsPV = (diffVz > diffMean - nSigma * diffSigma - margin && diffVz < diffMean + nSigma * diffSigma + margin); @@ -1519,14 +1507,14 @@ class EventSelectionModule // TODO apply other cuts for sel8? // TODO introduce array of sel[0]... sel[8] or similar? bool sel8 = false; - if (lastRun < 568873) // pre-2026 data & MC: require all three bits: TVX, TF and ROF border cuts - sel8 = bitcheck64(bcselEntry.selection, aod::evsel::kIsTriggerTVX) && bitcheck64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder) && bitcheck64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder); + if (lastRun < 568873 || lastRun >= 572103) // o2-linter: disable=magic-number (pre-2026 and Pb-Pb 2026 (data & MC): require all three bits: TVX, TF and ROF border cuts) + sel8 = BITCHECK64(bcselEntry.selection, aod::evsel::kIsTriggerTVX) && BITCHECK64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder) && BITCHECK64(bcselEntry.selection, aod::evsel::kNoITSROFrameBorder); else // for pp 2026: sel8 without kNoITSROFrameBorder bit, because the cross-ROF reconstruction for ITS will be On (the switch by a runNumber is a temporary solution) - sel8 = bitcheck64(bcselEntry.selection, aod::evsel::kIsTriggerTVX) && bitcheck64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder); + sel8 = BITCHECK64(bcselEntry.selection, aod::evsel::kIsTriggerTVX) && BITCHECK64(bcselEntry.selection, aod::evsel::kNoTimeFrameBorder); // fill counters histos.template get(HIST("eventselection/hColCounterAll"))->Fill(Form("%d", bc.runNumber()), 1); - if (bitcheck64(bcselEntry.selection, aod::evsel::kIsTriggerTVX)) { + if (BITCHECK64(bcselEntry.selection, aod::evsel::kIsTriggerTVX)) { histos.template get(HIST("eventselection/hColCounterTVX"))->Fill(Form("%d", bc.runNumber()), 1); } if (sel8) { @@ -1567,7 +1555,7 @@ class LumiModule // declaration of structs here // (N.B.: will be invisible to the outside, create your own copies) - o2::common::eventselection::lumiConfigurables lumiOpts; + o2::common::eventselection::LumiConfigurables lumiOpts; template void init(TContext& context, TLumiOpts const& external_lumiopts, THistoRegistry& histos) @@ -1672,12 +1660,12 @@ class LumiModule csZEM = -1; csZNC = -1; // Temporary workaround to get visible cross section. TODO: store run-by-run visible cross sections in CCDB - if (beamZ1 == 8 && beamZ2 == 1) { - csTVX = 0.3874e6; // eff(TVX) = 0.807 (based on LHC25e6f); sigma(INEL)=0.48b; arxiv:2507.05853 - } else if (beamZ1 == 8 && beamZ2 == 8) { - csTVX = 1.2050e6; // eff(TVX) = 0.886 (based on LHC25e6b); sigma(INEL)=1.36b; arxiv:2507.05853 - } else if (beamZ1 == 10 && beamZ2 == 10) { - csTVX = 1.5411e6; // eff(TVX) = 0.896 (based on LHC25e6g); sigma(INEL)=1.72b; arxiv:2507.05853 + if (beamZ1 == 8 && beamZ2 == 1) { // o2-linter: disable=magic-number (O beam on p, Z=8 and Z=1) + csTVX = 0.3874e6; // eff(TVX) = 0.807 (based on LHC25e6f); sigma(INEL)=0.48b; arxiv:2507.05853 + } else if (beamZ1 == 8 && beamZ2 == 8) { // o2-linter: disable=magic-number (O-O collisions, Z=8) + csTVX = 1.2050e6; // eff(TVX) = 0.886 (based on LHC25e6b); sigma(INEL)=1.36b; arxiv:2507.05853 + } else if (beamZ1 == 10 && beamZ2 == 10) { // o2-linter: disable=magic-number (Ne-Ne collisions, Z=10) + csTVX = 1.5411e6; // eff(TVX) = 0.896 (based on LHC25e6g); sigma(INEL)=1.72b; arxiv:2507.05853 } else if (beamZ1 == 1 && beamZ2 == 1) { if (std::fabs(sqrts - 900.) < 100.) { // o2-linter: disable=magic-number (TODO store and extract cross sections from ccdb) csTVX = 0.0357e6; // ub @@ -1771,15 +1759,17 @@ class LumiModule double perBcRateTCE = static_cast(mCounterTCE[i + 1] - mCounterTCE[i]) / nOrbits / nCollidingBCs; double perBcRateZNC = static_cast(mCounterZNC[i + 1] - mCounterZNC[i]) / nOrbits / nCollidingBCs; double perBcRateZEM = static_cast(mCounterZEM[i + 1] - mCounterZEM[i]) / nOrbits / nCollidingBCs; - double muTVX = (perBcRateTVX < 1 && perBcRateTVX > 1e-10) ? -std::log(1 - perBcRateTVX) : 0; - double muTCE = (perBcRateTCE < 1 && perBcRateTCE > 1e-10) ? -std::log(1 - perBcRateTCE) : 0; - double muZNC = (perBcRateZNC < 1 && perBcRateZNC > 1e-10) ? -std::log(1 - perBcRateZNC) : 0; - double muZEM = (perBcRateZEM < 1 && perBcRateZEM > 1e-10) ? -std::log(1 - perBcRateZEM) : 0; + const float rateMin = 1.e-10; + double muTVX = (perBcRateTVX < 1 && perBcRateTVX > rateMin) ? -std::log(1 - perBcRateTVX) : 0; + double muTCE = (perBcRateTCE < 1 && perBcRateTCE > rateMin) ? -std::log(1 - perBcRateTCE) : 0; + double muZNC = (perBcRateZNC < 1 && perBcRateZNC > rateMin) ? -std::log(1 - perBcRateZNC) : 0; + double muZEM = (perBcRateZEM < 1 && perBcRateZEM > rateMin) ? -std::log(1 - perBcRateZEM) : 0; LOGP(debug, "orbit={} muTVX={} muTCE={} muZNC={} muZEM={}", mOrbits[i], muTVX, muTCE, muZNC, muZEM); - mPileupCorrectionTVX.push_back(muTVX > 1e-10 ? muTVX / (1 - std::exp(-muTVX)) : 1); - mPileupCorrectionTCE.push_back(muTCE > 1e-10 ? muTCE / (1 - std::exp(-muTCE)) : 1); - mPileupCorrectionZNC.push_back(muZNC > 1e-10 ? muZNC / (1 - std::exp(-muZNC)) : 1); - mPileupCorrectionZEM.push_back(muZEM > 1e-10 ? muZEM / (1 - std::exp(-muZEM)) : 1); + const float muMin = 1.e-10; + mPileupCorrectionTVX.push_back(muTVX > muMin ? muTVX / (1 - std::exp(-muTVX)) : 1); + mPileupCorrectionTCE.push_back(muTCE > muMin ? muTCE / (1 - std::exp(-muTCE)) : 1); + mPileupCorrectionZNC.push_back(muZNC > muMin ? muZNC / (1 - std::exp(-muZNC)) : 1); + mPileupCorrectionZEM.push_back(muZEM > muMin ? muZEM / (1 - std::exp(-muZEM)) : 1); } // filling last orbit range using previous orbit range mPileupCorrectionTVX.push_back(mPileupCorrectionTVX.back()); diff --git a/Common/Tools/Multiplicity/MultModule.h b/Common/Tools/Multiplicity/MultModule.h index 5b13392e06c..e9a64798915 100644 --- a/Common/Tools/Multiplicity/MultModule.h +++ b/Common/Tools/Multiplicity/MultModule.h @@ -11,7 +11,7 @@ /// \file MultModule.h /// \brief combined multiplicity + centrality module with autodetect features -/// \author ALICE +/// \author ALICE Collaboration #ifndef COMMON_TOOLS_MULTIPLICITY_MULTMODULE_H_ #define COMMON_TOOLS_MULTIPLICITY_MULTMODULE_H_ @@ -36,11 +36,13 @@ #include #include +#include #include #include #include #include #include +#include #include //__________________________________________ @@ -58,7 +60,7 @@ static constexpr int nParameters = 1; static const std::vector tableNames{ // multiplicity subcomponent "FV0Mults", - "FV0AOuterMults", + "FITExtraMults", "FT0Mults", "FDDMults", "ZDCMults", @@ -97,10 +99,12 @@ static const std::vector tableNames{ "CentMFTs", "BCCentFT0Ms", "BCCentFT0As", - "BCCentFT0Cs"}; - -static constexpr int nTablesConst = 39; + "BCCentFT0Cs", + "CentFT0MAnchorCols", + "CentFT0MAnchorBCs", + "CentFT0MOuterAs"}; +static constexpr int nTablesConst = 42; static const std::vector parameterNames{"enable"}; static const int defaultParameters[nTablesConst][nParameters]{ {-1}, @@ -141,6 +145,9 @@ static const int defaultParameters[nTablesConst][nParameters]{ {-1}, {-1}, {-1}, + {-1}, + {-1}, + {-1}, {-1}}; // table index : match order above @@ -166,25 +173,28 @@ enum tableIndex { kFV0Mults, // standard kMultsGlobal, // requires track selection task // centrality subcomponent - kCentRun2V0Ms, // Run 2 - kCentRun2V0As, // Run 2 - kCentRun2SPDTrks, // Run 2 - kCentRun2SPDClss, // Run 2 - kCentRun2CL0s, // Run 2 - kCentRun2CL1s, // Run 2 - kCentFV0As, // standard Run 3 - kCentFT0Ms, // standard Run 3 - kCentFT0As, // standard Run 3 - kCentFT0Cs, // standard Run 3 - kCentFT0CVariant1s, // standard Run 3 - kCentFT0CVariant2s, // standard Run 3 - kCentFDDMs, // standard Run 3 - kCentNTPVs, // standard Run 3 - kCentNGlobals, // requires track selection task - kCentMFTs, // requires MFT task - kBCCentFT0Ms, // bc centrality - kBCCentFT0As, // bc centrality - kBCCentFT0Cs, // bc centrality + kCentRun2V0Ms, // Run 2 + kCentRun2V0As, // Run 2 + kCentRun2SPDTrks, // Run 2 + kCentRun2SPDClss, // Run 2 + kCentRun2CL0s, // Run 2 + kCentRun2CL1s, // Run 2 + kCentFV0As, // standard Run 3 + kCentFT0Ms, // standard Run 3 + kCentFT0As, // standard Run 3 + kCentFT0Cs, // standard Run 3 + kCentFT0CVariant1s, // standard Run 3 + kCentFT0CVariant2s, // standard Run 3 + kCentFDDMs, // standard Run 3 + kCentNTPVs, // standard Run 3 + kCentNGlobals, // requires track selection task + kCentMFTs, // requires MFT task + kBCCentFT0Ms, // bc centrality + kBCCentFT0As, // bc centrality + kBCCentFT0Cs, // bc centrality + kCentFT0MAnchorCols, // standard Run 3 + kCentFT0MAnchorBCs, // standard Run 3 + kCentFT0MOuterAs, // standard Run 3 kNTables }; struct products : o2::framework::ProducesGroup { @@ -222,6 +232,7 @@ struct products : o2::framework::ProducesGroup { o2::framework::Produces centRun2CL1; o2::framework::Produces centFV0A; o2::framework::Produces centFT0M; + o2::framework::Produces centFT0MOuterA; o2::framework::Produces centFT0A; o2::framework::Produces centFT0C; o2::framework::Produces centFT0CVariant1; @@ -233,6 +244,8 @@ struct products : o2::framework::ProducesGroup { o2::framework::Produces bcCentFT0A; o2::framework::Produces bcCentFT0C; o2::framework::Produces bcCentFT0M; + o2::framework::Produces centFT0MAnchorCol; + o2::framework::Produces centFT0MAnchorBC; //__________________________________________________ // centrality tables per BC @@ -247,6 +260,7 @@ struct multEntry { float multFV0C = 0.0f; float multFV0AOuter = 0.0f; float multFT0A = 0.0f; + float multFT0AOuter = 0.0f; float multFT0C = 0.0f; float multFDDA = 0.0f; float multFDDC = 0.0f; @@ -276,6 +290,7 @@ struct multEntry { float multFV0AZeq = -999.0f; float multFV0CZeq = -999.0f; float multFT0AZeq = -999.0f; + float multFT0AOuterZeq = -999.0f; float multFT0CZeq = -999.0f; float multFDDAZeq = -999.0f; float multFDDCZeq = -999.0f; @@ -308,6 +323,8 @@ struct standardConfigurables : o2::framework::ConfigurableGroup { // Autoconfigure process functions o2::framework::Configurable autoConfigureProcess{"autoConfigureProcess", false, "if true, will configure process function switches based on metadata"}; + o2::framework::Configurable doNTrackStudies{"doNTrackStudies", true, "if true, will fill Ntracks in MultsExtra"}; + // do vertex-Z equalized or not o2::framework::Configurable doVertexZeq{"doVertexZeq", 1, "if 1: do vertex Z eq mult table"}; @@ -317,6 +334,12 @@ struct standardConfigurables : o2::framework::ConfigurableGroup { o2::framework::Configurable minNclsITSGlobalTrack{"minNclsITSGlobalTrack", 5, "min. number of ITS clusters for global tracks"}; o2::framework::Configurable minNclsITSibGlobalTrack{"minNclsITSibGlobalTrack", 1, "min. number of ITSib clusters for global tracks"}; + // MFT track counter configurables + o2::framework::Configurable minNclsMFTTrack{"minNclsMFTTrack", 5, "min. number of MFT clusters for MFT tracks"}; + o2::framework::Configurable maxDCAxyToPVMFTTrack{"maxDCAxyToPVMFTTrack", 2.0f, "max DCAxy to PV for MFT tracks (cm)"}; + o2::framework::Configurable minEtaMFTTrack{"minEtaMFTTrack", -3.9f, "min. pseudorapidity for MFT tracks (nominal: -3.6)"}; + o2::framework::Configurable maxEtaMFTTrack{"maxEtaMFTTrack", -2.0f, "max. pseudorapidity for MFT tracks (nominal: -2.45)"}; + // ccdb information o2::framework::Configurable ccdbPathVtxZ{"ccdbPathVtxZ", "Centrality/Calibration", "The CCDB path for vertex-Z calibration"}; o2::framework::Configurable ccdbPathCentrality{"ccdbPathCentrality", "Centrality/Estimators", "The CCDB path for centrality information"}; @@ -408,7 +431,7 @@ class MultModule float mMCScalePars[6] = {0.0}; TFormula* mMCScale = nullptr; explicit CalibrationInfo(std::string name) - : name(name), + : name(std::move(name)), mCalibrationStored(false), mhMultSelCalib(nullptr), mMCScalePars{0.0}, @@ -435,6 +458,9 @@ class MultModule CalibrationInfo fv0aInfo = CalibrationInfo("FV0"); CalibrationInfo ft0mInfo = CalibrationInfo("FT0"); + CalibrationInfo ft0mColInfo = CalibrationInfo("FT0AnchorCol"); + CalibrationInfo ft0mBcInfo = CalibrationInfo("FT0AnchorBc"); + CalibrationInfo FT0MOuterAInfo = CalibrationInfo("FT0MOuterA"); CalibrationInfo ft0aInfo = CalibrationInfo("FT0A"); CalibrationInfo ft0cInfo = CalibrationInfo("FT0C"); CalibrationInfo ft0cVariant1Info = CalibrationInfo("FT0Cvar1"); @@ -455,7 +481,7 @@ class MultModule LOGF(info, "Metadata information: isMC? %i", metadataInfo.isMC()); const auto& workflows = context.services().template get(); - TString listOfRequestors[nTablesConst]; + std::array listOfRequestors{}; for (int i = 0; i < nTablesConst; i++) { int f = internalOpts.enabledTables->get(tableNames[i].c_str(), "enable"); if (f == 1) { @@ -662,14 +688,14 @@ class MultModule } if (lCalibObjects) { - hVtxZFV0A = static_cast(lCalibObjects->FindObject("hVtxZFV0A")); - hVtxZFT0A = static_cast(lCalibObjects->FindObject("hVtxZFT0A")); - hVtxZFT0C = static_cast(lCalibObjects->FindObject("hVtxZFT0C")); - hVtxZFDDA = static_cast(lCalibObjects->FindObject("hVtxZFDDA")); - hVtxZFDDC = static_cast(lCalibObjects->FindObject("hVtxZFDDC")); - hVtxZNTracks = static_cast(lCalibObjects->FindObject("hVtxZNTracksPV")); - hVtxZNMFTTracks = static_cast(lCalibObjects->FindObject("hVtxZMFT")); - hVtxZNGlobalTracks = static_cast(lCalibObjects->FindObject("hVtxZNGlobals")); + hVtxZFV0A = dynamic_cast(lCalibObjects->FindObject("hVtxZFV0A")); + hVtxZFT0A = dynamic_cast(lCalibObjects->FindObject("hVtxZFT0A")); + hVtxZFT0C = dynamic_cast(lCalibObjects->FindObject("hVtxZFT0C")); + hVtxZFDDA = dynamic_cast(lCalibObjects->FindObject("hVtxZFDDA")); + hVtxZFDDC = dynamic_cast(lCalibObjects->FindObject("hVtxZFDDC")); + hVtxZNTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZNTracksPV")); + hVtxZNMFTTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZMFT")); + hVtxZNGlobalTracks = dynamic_cast(lCalibObjects->FindObject("hVtxZNGlobals")); lCalibLoaded = true; // Capture error if (!hVtxZFV0A || !hVtxZFT0A || !hVtxZFT0C || !hVtxZFDDA || !hVtxZFDDC || !hVtxZNTracks) { @@ -697,7 +723,7 @@ class MultModule auto amplitude = fv0.amplitude()[ii]; auto channel = fv0.channel()[ii]; mults.multFV0A += amplitude; - if (channel > 7) { + if (channel > 7) { // Outer ring only mults.multFV0AOuter += amplitude; } } @@ -708,8 +734,11 @@ class MultModule if (collision.has_foundFT0()) { const auto& ft0 = collision.foundFT0(); mults.fitTriggerMask = ft0.triggerMask(); - for (const auto& amplitude : ft0.amplitudeA()) { - mults.multFT0A += amplitude; + for (size_t ii = 0; ii < ft0.amplitudeA().size(); ii++) { + mults.multFT0A += ft0.amplitudeA()[ii]; + if (ft0.channelA()[ii] > 31) { // Outer ring only + mults.multFT0AOuter += ft0.amplitudeA()[ii]; + } } for (const auto& amplitude : ft0.amplitudeC()) { mults.multFT0C += amplitude; @@ -717,6 +746,7 @@ class MultModule } else { mults.multFT0A = -999.f; mults.multFT0C = -999.f; + mults.multFT0AOuter = -999.f; } if (collision.has_foundFDD()) { const auto& fdd = collision.foundFDD(); @@ -754,7 +784,7 @@ class MultModule cursors.tableFV0(mults.multFV0A, mults.multFV0C); } if (internalOpts.mEnabledTables[kFITExtraMults]) { - cursors.tableFITExtraMults(mults.multFV0AOuter, mults.fitTriggerMask); + cursors.tableFITExtraMults(mults.multFV0AOuter, mults.multFT0AOuter, mults.fitTriggerMask); cursors.tableFV0AOuterMults(mults.multFV0AOuter); // Keep for backwards compatibility } if (internalOpts.mEnabledTables[kFT0Mults]) { @@ -789,6 +819,11 @@ class MultModule } else { mults.multFT0AZeq = 0.0f; } + if (mults.multFT0AOuter > -1.0f && std::fabs(collision.posZ()) < 15.0f && lCalibLoaded) { + mults.multFT0AOuterZeq = hVtxZFT0A->Interpolate(0.0) * mults.multFT0AOuter / hVtxZFT0A->Interpolate(collision.posZ()); + } else { + mults.multFT0AOuterZeq = 0.0f; + } if (mults.multFT0C > -1.0f && std::fabs(collision.posZ()) < 15.0f && lCalibLoaded) { mults.multFT0CZeq = hVtxZFT0C->Interpolate(0.0) * mults.multFT0C / hVtxZFT0C->Interpolate(collision.posZ()); } else { @@ -903,9 +938,15 @@ class MultModule if (internalOpts.mEnabledTables[kMultsExtra]) { cursors.tableExtra(collision.numContrib(), collision.chi2(), collision.collisionTimeRes(), bc.runNumber(), collision.posZ(), collision.sel8(), - mults.multHasITS, mults.multHasTPC, mults.multHasTOF, mults.multHasTRD, - mults.multITSOnly, mults.multTPCOnly, mults.multITSTPC, - mults.multAllTracksTPCOnly, mults.multAllTracksITSTPC, + static_cast(internalOpts.doNTrackStudies) * mults.multHasITS, + static_cast(internalOpts.doNTrackStudies) * mults.multHasTPC, + static_cast(internalOpts.doNTrackStudies) * mults.multHasTOF, + static_cast(internalOpts.doNTrackStudies) * mults.multHasTRD, + static_cast(internalOpts.doNTrackStudies) * mults.multITSOnly, + static_cast(internalOpts.doNTrackStudies) * mults.multTPCOnly, + static_cast(internalOpts.doNTrackStudies) * mults.multITSTPC, + static_cast(internalOpts.doNTrackStudies) * mults.multAllTracksTPCOnly, + static_cast(internalOpts.doNTrackStudies) * mults.multAllTracksITSTPC, collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), collision.flags()); @@ -980,7 +1021,7 @@ class MultModule int nTracks = 0; for (const auto& track : mfttracks) { - if (track.nClusters() >= 5) { // hardcoded for now + if (track.nClusters() >= internalOpts.minNclsMFTTrack.value) { nAllTracks++; } } @@ -988,13 +1029,13 @@ class MultModule if (retracks.size() > 0) { for (const auto& retrack : retracks) { auto track = retrack.mfttrack(); - if (track.nClusters() < 5) { + if (track.nClusters() < internalOpts.minNclsMFTTrack.value) { continue; // min cluster requirement } - if ((track.eta() > -2.0f) && (track.eta() < -3.9f)) { + if (track.eta() > internalOpts.maxEtaMFTTrack.value || track.eta() < internalOpts.minEtaMFTTrack.value) { continue; // too far to be of true interest } - if (std::abs(retrack.bestDCAXY()) > 2.0f) { + if (std::abs(retrack.bestDCAXY()) > internalOpts.maxDCAxyToPVMFTTrack.value) { continue; // does not point to PV properly } nTracks++; @@ -1057,11 +1098,11 @@ class MultModule Run2CL1Info.mCalibrationStored = false; if (callst != nullptr) { auto getccdb = [callst](const char* ccdbhname) { - TH1* h = reinterpret_cast(callst->FindObject(ccdbhname)); + TH1* h = dynamic_cast(callst->FindObject(ccdbhname)); return h; }; auto getformulaccdb = [callst](const char* ccdbhname) { - TFormula* f = reinterpret_cast(callst->FindObject(ccdbhname)); + TFormula* f = dynamic_cast(callst->FindObject(ccdbhname)); return f; }; @@ -1159,13 +1200,13 @@ class MultModule LOGF(info, "centrality loading procedure for timestamp=%llu, run number=%d", bc.timestamp(), bc.runNumber()); // capture the need for PYTHIA calibration in Pb-Pb runs - if (metadataInfo.isMC() && mRunNumber >= 544013 && mRunNumber <= 545367) { + if (metadataInfo.isMC() && mRunNumber >= 544013 && mRunNumber <= 545367 && internalOpts.generatorName.value.empty()) { LOGF(info, "This is MC for Pb-Pb. Setting generatorName automatically to PYTHIA"); internalOpts.generatorName.value = "PYTHIA"; } // capture the need for PYTHIA calibration in light ion runs automatically - if (metadataInfo.isMC() && mRunNumber >= 564250 && mRunNumber <= 564472) { + if (metadataInfo.isMC() && mRunNumber >= 564250 && mRunNumber <= 564472 && internalOpts.generatorName.value.empty()) { LOGF(info, "This is MC for light ion runs. Setting generatorName automatically to PYTHIA"); internalOpts.generatorName.value = "PYTHIA"; } @@ -1198,6 +1239,9 @@ class MultModule fv0aInfo.mCalibrationStored = false; ft0mInfo.mCalibrationStored = false; + ft0mColInfo.mCalibrationStored = false; + ft0mBcInfo.mCalibrationStored = false; + FT0MOuterAInfo.mCalibrationStored = false; ft0aInfo.mCalibrationStored = false; ft0cInfo.mCalibrationStored = false; ft0cVariant1Info.mCalibrationStored = false; @@ -1208,9 +1252,9 @@ class MultModule mftInfo.mCalibrationStored = false; if (callst != nullptr) { LOGF(info, "Getting new histograms with %d run number for %d run number", mRunNumber, bc.runNumber()); - auto getccdb = [callst, bc](struct CalibrationInfo& estimator, const o2::framework::Configurable generatorName) { // TODO: to consider the name inside the estimator structure - estimator.mhMultSelCalib = reinterpret_cast(callst->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); - estimator.mMCScale = reinterpret_cast(callst->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); + auto getccdb = [callst, bc](struct CalibrationInfo& estimator, const o2::framework::Configurable& generatorName) { // TODO: to consider the name inside the estimator structure + estimator.mhMultSelCalib = dynamic_cast(callst->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); + estimator.mMCScale = dynamic_cast(callst->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); if (estimator.mhMultSelCalib != nullptr) { if (generatorName->length() != 0) { LOGF(info, "Retrieving MC calibration for %d, generator name: %s", bc.runNumber(), generatorName->c_str()); @@ -1235,6 +1279,12 @@ class MultModule getccdb(fv0aInfo, internalOpts.generatorName); if (internalOpts.mEnabledTables[kCentFT0Ms] || internalOpts.mEnabledTables[kBCCentFT0Ms]) getccdb(ft0mInfo, internalOpts.generatorName); + if (internalOpts.mEnabledTables[kCentFT0MAnchorCols]) + getccdb(ft0mColInfo, internalOpts.generatorName); + if (internalOpts.mEnabledTables[kCentFT0MAnchorBCs]) + getccdb(ft0mBcInfo, internalOpts.generatorName); + if (internalOpts.mEnabledTables[kCentFT0MOuterAs]) + getccdb(FT0MOuterAInfo, internalOpts.generatorName); if (internalOpts.mEnabledTables[kCentFT0As] || internalOpts.mEnabledTables[kBCCentFT0As]) getccdb(ft0aInfo, internalOpts.generatorName); if (internalOpts.mEnabledTables[kCentFT0Cs] || internalOpts.mEnabledTables[kBCCentFT0Cs]) @@ -1272,7 +1322,8 @@ class MultModule internalOpts.mEnabledTables[kCentFDDMs] || internalOpts.mEnabledTables[kCentNTPVs] || internalOpts.mEnabledTables[kCentNGlobals] || internalOpts.mEnabledTables[kCentMFTs] || internalOpts.mEnabledTables[kBCCentFT0Ms] || - internalOpts.mEnabledTables[kBCCentFT0As] || internalOpts.mEnabledTables[kBCCentFT0Cs]) { + internalOpts.mEnabledTables[kBCCentFT0As] || internalOpts.mEnabledTables[kBCCentFT0Cs] || + internalOpts.mEnabledTables[kCentFT0MAnchorCols] || internalOpts.mEnabledTables[kCentFT0MAnchorBCs]) { // check and update centrality calibration objects for Run 3 const auto& firstbc = bcs.begin(); ConfigureCentralityRun3(ccdb, metadataInfo, firstbc); @@ -1318,6 +1369,12 @@ class MultModule populateTable(cursors.centFV0A, fv0aInfo, mults[iEv].multFV0AZeq, isInelGt0); if (internalOpts.mEnabledTables[kCentFT0Ms]) populateTable(cursors.centFT0M, ft0mInfo, mults[iEv].multFT0AZeq + mults[iEv].multFT0CZeq, isInelGt0); + if (internalOpts.mEnabledTables[kCentFT0Ms]) + populateTable(cursors.centFT0MAnchorCol, ft0mColInfo, mults[iEv].multFT0AZeq + mults[iEv].multFT0CZeq, isInelGt0); + if (internalOpts.mEnabledTables[kCentFT0Ms]) + populateTable(cursors.centFT0MAnchorBC, ft0mBcInfo, mults[iEv].multFT0AZeq + mults[iEv].multFT0CZeq, isInelGt0); + if (internalOpts.mEnabledTables[kCentFT0MOuterAs]) + populateTable(cursors.centFT0MOuterA, FT0MOuterAInfo, mults[iEv].multFT0AOuterZeq + mults[iEv].multFT0CZeq, isInelGt0); if (internalOpts.mEnabledTables[kCentFT0As]) populateTable(cursors.centFT0A, ft0aInfo, mults[iEv].multFT0AZeq, isInelGt0); if (internalOpts.mEnabledTables[kCentFT0Cs]) diff --git a/Common/Tools/Multiplicity/macros/runCalibration.C b/Common/Tools/Multiplicity/macros/runCalibration.C index e442e0ef401..1e1d4f96b5f 100644 --- a/Common/Tools/Multiplicity/macros/runCalibration.C +++ b/Common/Tools/Multiplicity/macros/runCalibration.C @@ -33,17 +33,17 @@ /// @param anchorPointPercentage anchor point percentage to use /// @param matchRange width of region in which data/glauber matching is to be done in rolling anchoring test /// @param doNpartNcoll wether or not to attempt calculating Npart, Ncoll in centrality bins -void runCalibration(TString lInputFileName = "results/AR_544122_glauberNBD_ancestorMode2_hFT0C_BCs.root", double anchorPointPercentage = 90.0, double matchRange = 200.0, bool doNpartNcoll = false) +void runCalibration(const TString& lInputFileName = "results/AR_544122_glauberNBD_ancestorMode2_hFT0C_BCs.root", double anchorPointPercentage = 90.0, double matchRange = 200.0, bool doNpartNcoll = false) { TFile* file = new TFile(lInputFileName.Data(), "READ"); file->ls(); - TH1F* hData = (TH1F*)file->Get("hV0MUltraFine"); - TH1F* hGlauberParameters = (TH1F*)file->Get("hGlauberParameters"); - TH1F* hGlauberFitRange = (TH1F*)file->Get("hGlauberFitRange"); + TH1F* hData = dynamic_cast(file->Get("hV0MUltraFine")); + TH1F* hGlauberParameters = dynamic_cast(file->Get("hGlauberParameters")); + TH1F* hGlauberFitRange = dynamic_cast(file->Get("hGlauberFitRange")); hData->SetName("hData"); - TH1F* hStitched = (TH1F*)hData->Clone("hStitched"); - TH1F* hFit = (TH1F*)file->Get("hGlauber"); + TH1F* hStitched = dynamic_cast(hData->Clone("hStitched")); + TH1F* hFit = dynamic_cast(file->Get("hGlauber")); TCanvas* c1 = new TCanvas("c1", "", 800, 600); c1->SetLeftMargin(0.17); @@ -160,7 +160,7 @@ void runCalibration(TString lInputFileName = "results/AR_544122_glauberNBD_ances // Step 1: open the (Npart, Ncoll) pair information, provide TFile* fbasefile = new TFile("basehistos.root", "READ"); - TH2D* hNpNc = (TH2D*)fbasefile->Get("hNpNc"); + TH2D* hNpNc = dynamic_cast(fbasefile->Get("hNpNc")); g->SetNpartNcollCorrelation(hNpNc); g->InitializeNpNc(); diff --git a/Common/Tools/Multiplicity/macros/runGlauberFit.C b/Common/Tools/Multiplicity/macros/runGlauberFit.C index e1b3fd10944..9524985b1ff 100644 --- a/Common/Tools/Multiplicity/macros/runGlauberFit.C +++ b/Common/Tools/Multiplicity/macros/runGlauberFit.C @@ -39,9 +39,9 @@ Double_t FastIntegrate(TF1* f1, Double_t a, Double_t b, Int_t n = 5) // Do fast integration with N sampling points const Int_t nc = n; Double_t x[nc], y[nc]; - Double_t lWidth = (b - a) / ((double)(n - 1)); + Double_t lWidth = (b - a) / (static_cast(n - 1)); for (Int_t ii = 0; ii < n; ii++) { - x[ii] = a + ((double)(ii)) * lWidth; + x[ii] = a + (static_cast(ii)) * lWidth; y[ii] = f1->Eval(x[ii]); } // Now go via trapezoids, please (this probably has a name) @@ -87,7 +87,7 @@ Double_t GetBoundaryForPercentile(TH1* histo, Double_t lPercentileRequested) /// @param lFreef free f: keep f value free (default Pb-Pb: fixed at 0.8) /// @param lfvalue f value: the value to use for fixed f /// @param outputFile name of output file -int runGlauberFit(TString lInputFileName = "AnalysisResultsLHC24ar.root", TString histogramName = "hFT0C_BCs", int ancestorMode = 2, Bool_t lFreek = kFALSE, Bool_t use_dMu_dNanc = kFALSE, Bool_t lFreef = kFALSE, Float_t lfvalue = 0.800, TString outputFile = "output.root") +int runGlauberFit(const TString& lInputFileName = "AnalysisResultsLHC24ar.root", const TString& histogramName = "hFT0C_BCs", int ancestorMode = 2, Bool_t lFreek = kFALSE, Bool_t use_dMu_dNanc = kFALSE, Bool_t lFreef = kFALSE, Float_t lfvalue = 0.800, const TString& outputFile = "output.root") { gStyle->SetLineScalePS(1); gStyle->SetOptStat(0); @@ -98,7 +98,7 @@ int runGlauberFit(TString lInputFileName = "AnalysisResultsLHC24ar.root", TStrin std::cout << "Problem with file!" << std::endl; TH1F* hV0Mfine = 0x0; - hV0Mfine = (TH1F*)file->Get(Form("centrality-study/%s", histogramName.Data())); + hV0Mfine = dynamic_cast(file->Get(Form("centrality-study/%s", histogramName.Data()))); // disregard bin zero std::cout << "Received bin zero content: " << hV0Mfine->GetBinContent(0) << ", will set to zero..." << std::endl; @@ -124,8 +124,8 @@ int runGlauberFit(TString lInputFileName = "AnalysisResultsLHC24ar.root", TStrin TFile* fOutput = new TFile(outputFile.Data(), "RECREATE"); - TH1F* hV0M = (TH1F*)hV0Mfine->Clone("hV0M"); - TH1F* hV0MUltraFine = (TH1F*)hV0Mfine->Clone("hV0MUltraFine"); + TH1F* hV0M = dynamic_cast(hV0Mfine->Clone("hV0M")); + TH1F* hV0MUltraFine = dynamic_cast(hV0Mfine->Clone("hV0MUltraFine")); hV0M->SetName("hV0M"); hV0M->SetTitle(""); @@ -210,7 +210,7 @@ int runGlauberFit(TString lInputFileName = "AnalysisResultsLHC24ar.root", TStrin // Step 1: open the (Npart, Ncoll) pair information, provide TFile* fbasefile = new TFile("basehistos.root", "READ"); - TH2D* hNpNc = (TH2D*)fbasefile->Get("hNpNc"); + TH2D* hNpNc = dynamic_cast(fbasefile->Get("hNpNc")); // return to proper scope fOutput->cd(); g->SetNpartNcollCorrelation(hNpNc); @@ -291,8 +291,8 @@ int runGlauberFit(TString lInputFileName = "AnalysisResultsLHC24ar.root", TStrin gStyle->SetOptStat(0); // Do a ratio plot - TH1D* hGlauber = (TH1D*)hV0MUltraFine->Clone("hGlauber"); - TH1D* hRatio = (TH1D*)hV0MUltraFine->Clone("hRatio"); + TH1D* hGlauber = dynamic_cast(hV0MUltraFine->Clone("hGlauber")); + TH1D* hRatio = dynamic_cast(hV0MUltraFine->Clone("hRatio")); hGlauber->Reset(); c1->cd(1); @@ -345,7 +345,7 @@ int runGlauberFit(TString lInputFileName = "AnalysisResultsLHC24ar.root", TStrin lFitRangeLine->SetLineStyle(2); lFitRangeLine->Draw(); - TH1D* hRatioGrayed = (TH1D*)hRatio->Clone("hRatioGrayed"); + TH1D* hRatioGrayed = dynamic_cast(hRatio->Clone("hRatioGrayed")); hRatioGrayed->SetMarkerColor(kGray + 2); hRatioGrayed->SetLineColor(kGray + 2); hRatioGrayed->Draw("same"); diff --git a/Common/Tools/Multiplicity/macros/saveCorrelation.C b/Common/Tools/Multiplicity/macros/saveCorrelation.C index 8525aeed889..04b27fc6c1b 100644 --- a/Common/Tools/Multiplicity/macros/saveCorrelation.C +++ b/Common/Tools/Multiplicity/macros/saveCorrelation.C @@ -26,26 +26,26 @@ /// @brief function to save Npart x Ncoll correlation to file for glauber fits /// @param filename input TGlauberMC ntuple file /// @param outputFile output file for Npart x Ncoll correlation TH2D -void saveCorrelation(TString filename = "gmc-PbPb-snn68.21-md0.40-nd-1.0-rc1-smax99.0.root", TString outputFile = "basehistos.root") +void saveCorrelation(const TString& filename = "gmc-PbPb-snn68.21-md0.40-nd-1.0-rc1-smax99.0.root", const TString& outputFile = "basehistos.root") { TFile* fin = new TFile(filename.Data(), "READ"); - TNtuple* ntup = (TNtuple*)fin->Get("nt_Pb_Pb"); + TNtuple* ntup = dynamic_cast(fin->Get("nt_Pb_Pb")); // try other Pb nuclear profiles in case "Pb" - "Pb" not found if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_Pbpn_Pbpn"); + ntup = dynamic_cast(fin->Get("nt_Pbpn_Pbpn")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar1_PbpnVar1"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar1_PbpnVar1")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar2_PbpnVar2"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar2_PbpnVar2")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar3_PbpnVar3"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar3_PbpnVar3")); } if (!ntup) { - ntup = (TNtuple*)fin->Get("nt_PbpnVar4_PbpnVar4"); + ntup = dynamic_cast(fin->Get("nt_PbpnVar4_PbpnVar4")); } if (!ntup) { diff --git a/Common/Tools/Multiplicity/multCalibrator.cxx b/Common/Tools/Multiplicity/multCalibrator.cxx index e54a3d5a0aa..8d25df8e32e 100644 --- a/Common/Tools/Multiplicity/multCalibrator.cxx +++ b/Common/Tools/Multiplicity/multCalibrator.cxx @@ -30,6 +30,7 @@ #include #include // FIXME +#include using namespace std; @@ -40,7 +41,7 @@ const TString multCalibrator::fCentEstimName[kNCentEstim] = { multCalibrator::multCalibrator() : TNamed(), lDesiredBoundaries(0), lNDesiredBoundaries(0), - fkPrecisionWarningThreshold(1.0), + fkPrecisionWarningThreshold(1000.0), fInputFileName("AnalysisResults.root"), fOutputFileName("CCDB-objects.root"), fAnchorPointValue(-1), @@ -57,7 +58,7 @@ multCalibrator::multCalibrator() : TNamed(), multCalibrator::multCalibrator(const char* name, const char* title) : TNamed(name, title), lDesiredBoundaries(0), lNDesiredBoundaries(0), - fkPrecisionWarningThreshold(1.0), + fkPrecisionWarningThreshold(1000.0), fInputFileName("AnalysisResults.root"), fOutputFileName("CCDB-objects.root"), fAnchorPointValue(-1), @@ -107,7 +108,7 @@ Bool_t multCalibrator::Calibrate() // Step 1: verify if input file contains desired histograms TH1D* hRaw[kNCentEstim]; for (Int_t iv = 0; iv < kNCentEstim; iv++) { - hRaw[iv] = reinterpret_cast(fileInput->Get(Form("multiplicity-qa/multiplicityQa/h%s", fCentEstimName[iv].Data()))); + hRaw[iv] = dynamic_cast(fileInput->Get(Form("multiplicity-qa/multiplicityQa/h%s", fCentEstimName[iv].Data()))); if (!hRaw[iv]) { cout << Form("File does not contain histogram h%s, which is necessary for calibration!", fCentEstimName[iv].Data()) << endl; return kFALSE; @@ -230,6 +231,54 @@ void multCalibrator::SetStandardAdaptiveBoundaries() cout << "Set standard adaptive percentile boundaries! Nboundaries: " << lNDesiredBoundaries << endl; } +//________________________________________________________________ +void multCalibrator::SetRun3AdaptiveBoundaries() +{ + // Function to set standard adaptive boundaries + // Run 3 exclusive: goes to 0.00001% binning for highest multiplicity + // Warning: this setting requires well filled input histograms + // + // -> at 0.00001%, one needs 10 million events to get a single one in the highest + // category. In Run 3, at 500 kHz, this corresponds to 20 seconds of dataking; + // any run lasting over 5 minutes should have enough to calibrate to the highest + // boundary. + // + // QA when using hyperfine binning is still advised: + // Selecting very fine percentages may select on spurious situations + // such as beam-background, pileup, etc. + + lNDesiredBoundaries = 0; + lDesiredBoundaries = new Double_t[1100]; + lDesiredBoundaries[0] = 100; + // From Low To High Multiplicity + for (Int_t ib = 1; ib < 91; ib++) { + lNDesiredBoundaries++; + lDesiredBoundaries[lNDesiredBoundaries] = lDesiredBoundaries[lNDesiredBoundaries - 1] - 1.0; + } + for (Int_t ib = 1; ib < 91; ib++) { + lNDesiredBoundaries++; + lDesiredBoundaries[lNDesiredBoundaries] = lDesiredBoundaries[lNDesiredBoundaries - 1] - 0.1; + } + for (Int_t ib = 1; ib < 91; ib++) { + lNDesiredBoundaries++; + lDesiredBoundaries[lNDesiredBoundaries] = lDesiredBoundaries[lNDesiredBoundaries - 1] - 0.01; + } + for (Int_t ib = 1; ib < 91; ib++) { + lNDesiredBoundaries++; + lDesiredBoundaries[lNDesiredBoundaries] = lDesiredBoundaries[lNDesiredBoundaries - 1] - 0.001; + } + for (Int_t ib = 1; ib < 91; ib++) { + lNDesiredBoundaries++; + lDesiredBoundaries[lNDesiredBoundaries] = lDesiredBoundaries[lNDesiredBoundaries - 1] - 0.0001; + } + for (Int_t ib = 1; ib < 101; ib++) { + lNDesiredBoundaries++; + lDesiredBoundaries[lNDesiredBoundaries] = lDesiredBoundaries[lNDesiredBoundaries - 1] - 0.00001; + } + lNDesiredBoundaries++; + cout << "Set run 3 adaptive percentile boundaries! Nboundaries: " << lNDesiredBoundaries << endl; +} + //________________________________________________________________ void multCalibrator::SetStandardOnePercentBoundaries() { @@ -245,7 +294,33 @@ void multCalibrator::SetStandardOnePercentBoundaries() } //________________________________________________________________ -TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, TString lHistoName) +bool multCalibrator::IsBinningSane(TH1* histogram) +{ + // check if binning that will be attempted is too fine for this histogram + double minimumBinWidth = 1000.0f; + for (int ib = 0; ib < lNDesiredBoundaries - 1; ib++) { + if (std::abs(lDesiredBoundaries[ib + 1] - lDesiredBoundaries[ib]) < minimumBinWidth) { + minimumBinWidth = std::abs(lDesiredBoundaries[ib + 1] - lDesiredBoundaries[ib]); + } + } + if (minimumBinWidth < 1e-9) { + cout << "Excessively fine binning requested: minimum bin width registers as " << minimumBinWidth << endl; + return false; // not reasonable + } + if (histogram->GetEntries() < 1000.0 / minimumBinWidth) { + cout << "Histogram " << histogram->GetName() << " does not have enough entries (" << histogram->GetEntries() << ") to calibrate!" << endl; + return false; + } + if (std::abs(histogram->GetMean()) < 1e-9) { + cout << "Histogram " << histogram->GetName() << " has suspicious mean: " << histogram->GetMean() << endl; + return false; + } + cout << "Sanity check: " << histogram->GetName() << ", entries = " << histogram->GetEntries() << ", 1/(min bin width) = " << 100.0 / minimumBinWidth << " -> OK ! " << endl; + return true; +} + +//________________________________________________________________ +TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, const TString& lHistoName) { // This function returns a calibration histogram //(pp or p-Pb like, no anchor point considered) @@ -260,14 +335,27 @@ TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, TString lHistoName) cout << "Last boundary: " << lDesiredBoundaries[0] << endl; } + // binning check + if (!IsBinningSane(histoRaw)) { + cout << "Requested binning is not viable for input histogram named " << histoRaw->GetName() << "! Will return 100.5 for all requests." << endl; + Double_t lDummyBounds[2]; + lDummyBounds[0] = 0; + lDummyBounds[1] = 1.0; + TH1F* hCalib = new TH1F(lHistoName.Data(), "", 1, lDummyBounds); + hCalib->SetBinContent(0, 100.5); + hCalib->SetBinContent(1, 100.5); + hCalib->SetBinContent(2, 100.5); + return hCalib; + } + // Aux vars Double_t lMiddleOfBins[1000]; for (Long_t lB = 1; lB < lNDesiredBoundaries; lB++) { // place squarely at the middle to ensure it's all fine lMiddleOfBins[lB - 1] = 0.5 * (lDesiredBoundaries[lB] + lDesiredBoundaries[lB - 1]); } - Double_t lBounds[lNDesiredBoundaries + 1]; - Double_t lPrecision[lNDesiredBoundaries + 1]; + std::vector lBounds(lNDesiredBoundaries + 1); + std::vector lPrecision(lNDesiredBoundaries + 1); if (fAnchorPointValue > 0) { lBounds[0] = 0; @@ -278,17 +366,24 @@ TH1F* multCalibrator::GetCalibrationHistogram(TH1* histoRaw, TString lHistoName) if (fAnchorPointValue > 0) lDisplacedii++; lBounds[lDisplacedii] = GetBoundaryForPercentile(histoRaw, lDesiredBoundaries[ii], lPrecision[ii]); + bool warnUser = false; TString lPrecisionString = "(Precision OK)"; if (ii != 0 && ii != lNDesiredBoundaries - 1) { // check precision, please - if (lPrecision[ii] / TMath::Abs(lDesiredBoundaries[ii + 1] - lDesiredBoundaries[ii]) > fkPrecisionWarningThreshold) + if ((lPrecision[ii] / TMath::Abs(lDesiredBoundaries[ii + 1] - lDesiredBoundaries[ii])) > fkPrecisionWarningThreshold) { lPrecisionString = "(WARNING: BINNING MAY LEAD TO IMPRECISION!)"; - if (lPrecision[ii] / TMath::Abs(lDesiredBoundaries[ii - 1] - lDesiredBoundaries[ii]) > fkPrecisionWarningThreshold) + warnUser = true; + } + if ((lPrecision[ii] / TMath::Abs(lDesiredBoundaries[ii - 1] - lDesiredBoundaries[ii])) > fkPrecisionWarningThreshold) { lPrecisionString = "(WARNING: BINNING MAY LEAD TO IMPRECISION!)"; + warnUser = true; + } + } + if (warnUser) { + cout << histoRaw->GetName() << " boundaries, percentile: " << lDesiredBoundaries[ii] << "%\t Signal value = " << lBounds[lDisplacedii] << "\tprecision = " << lPrecision[ii] << "% " << lPrecisionString.Data() << endl; } - cout << histoRaw->GetName() << " boundaries, percentile: " << lDesiredBoundaries[ii] << "%\t Signal value = " << lBounds[lDisplacedii] << "\tprecision = " << lPrecision[ii] << "% " << lPrecisionString.Data() << endl; } - TH1F* hCalib = new TH1F(lHistoName.Data(), "", fAnchorPointValue < 0 ? lNDesiredBoundaries - 1 : lNDesiredBoundaries, lBounds); + TH1F* hCalib = new TH1F(lHistoName.Data(), "", fAnchorPointValue < 0 ? lNDesiredBoundaries - 1 : lNDesiredBoundaries, lBounds.data()); hCalib->SetDirectory(0); hCalib->SetBinContent(0, 100.5); hCalib->SetBinContent(1, 100.5); @@ -311,7 +406,6 @@ void multCalibrator::ResetPrecisionHistogram() Double_t lInverseDesiredBoundaries[1100]; for (Int_t ii = 0; ii < lNDesiredBoundaries; ii++) { lInverseDesiredBoundaries[ii] = lDesiredBoundaries[lNDesiredBoundaries - (ii + 1)]; - cout << "Boundary " << ii << " is " << lInverseDesiredBoundaries[ii] << endl; } fPrecisionHistogram = new TH1D("hPrecisionHistogram", "", lNDesiredBoundaries - 1, lInverseDesiredBoundaries); } diff --git a/Common/Tools/Multiplicity/multCalibrator.h b/Common/Tools/Multiplicity/multCalibrator.h index a4e38ae1fd2..50e306c6d6e 100644 --- a/Common/Tools/Multiplicity/multCalibrator.h +++ b/Common/Tools/Multiplicity/multCalibrator.h @@ -43,8 +43,8 @@ class multCalibrator : public TNamed // Interface: steering functions to be used in calibration macro // Set Filenames - void SetInputFile(TString lFile) { fInputFileName = lFile.Data(); } - void SetOutputFile(TString lFile) { fOutputFileName = lFile.Data(); } + void SetInputFile(const TString& lFile) { fInputFileName = lFile.Data(); } + void SetOutputFile(const TString& lFile) { fOutputFileName = lFile.Data(); } // Set Boundaries to find void SetBoundaries(Long_t lNB, Double_t* lB) { @@ -60,13 +60,15 @@ class multCalibrator : public TNamed void SetAnchorPointPercentage(Float_t lPer) { fAnchorPointPercentage = lPer; } void SetStandardAdaptiveBoundaries(); // standard adaptive (pp-like) + void SetRun3AdaptiveBoundaries(); // Run 3 adaptive (down to 0.00001%) void SetStandardOnePercentBoundaries(); // standard 1% (Pb-Pb like) + bool IsBinningSane(TH1* histogram); // for safety // Master Function in this Class: To be called once filenames are set Bool_t Calibrate(); // Aux function. Keep public, accessible outside as rather useful utility - TH1F* GetCalibrationHistogram(TH1* histoRaw, TString lHistoName = "hCalib"); + TH1F* GetCalibrationHistogram(TH1* histoRaw, const TString& lHistoName = "hCalib"); // Auxiliary functions Double_t GetRawMax(TH1* histo); diff --git a/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx b/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx index ebabca167a1..c5d067c7e22 100644 --- a/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx +++ b/Common/Tools/Multiplicity/multGlauberNBDFitter.cxx @@ -8,7 +8,11 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// + +/// \file multGlauberNBDFitter.cxx +/// \brief Helper class to perform Glauber fits +/// \author David Dobrigkeit Chinellato + /********************************************** * * Class meant to do Glauber+NBD fits @@ -27,6 +31,8 @@ #include "multGlauberNBDFitter.h" +#include + #include #include #include @@ -34,8 +40,6 @@ #include #include #include -#include -#include #include #include #include @@ -43,73 +47,28 @@ #include #include -#include - -#include // FIXME -using namespace std; +#include +#include ClassImp(multGlauberNBDFitter); -multGlauberNBDFitter::multGlauberNBDFitter() : TNamed(), - fNBD(0x0), - fhNanc(0x0), - fhNpNc(0x0), - fhV0M(0x0), - ffChanged(kTRUE), - fCurrentf(-1), - fAncestorMode(2), - fNpart(0x0), - fNcoll(0x0), - fContent(0x0), - fNNpNcPairs(-1), - fMaxNpNcPairs(1000000), - fMu(45), - fdMu(0.0), - fk(1.5), - ff(0.8), - fnorm(100), - fFitOptions("R0"), - fFitNpx(5000) -{ - // Constructor - fNpart = new Double_t[fMaxNpNcPairs]; - fNcoll = new Double_t[fMaxNpNcPairs]; - fContent = new Long_t[fMaxNpNcPairs]; - - // Ancestor histo - fhNanc = new TH1D("fhNanc", "", 1000, -0.5, 999.5); - - // NBD - fNBD = new TF1("fNBD", "ROOT::Math::negative_binomial_pdf(x,[0],[1])", 0, 45000); - fNBD->SetNpx(45000); - - // master function - fGlauberNBD = new TF1("fGlauberNBD", this, &multGlauberNBDFitter::ProbDistrib, - 0, 50000, 4, "multGlauberNBDFitter", "ProbDistrib"); - fGlauberNBD->SetParameter(0, fMu); - fGlauberNBD->SetParameter(1, fk); - fGlauberNBD->SetParameter(2, ff); - fGlauberNBD->SetParameter(3, fnorm); - - fGlauberNBD->SetParName(0, "mu"); - fGlauberNBD->SetParName(1, "k"); - fGlauberNBD->SetParName(2, "f"); - fGlauberNBD->SetParName(3, "norm"); - fGlauberNBD->SetParName(4, "dMu/dNanc"); -} - +multGlauberNBDFitter::multGlauberNBDFitter() : multGlauberNBDFitter("multGlauberNBDFitter") {} multGlauberNBDFitter::multGlauberNBDFitter(const char* name, const char* title) : TNamed(name, title), - fNBD(0x0), - fhNanc(0x0), - fhNpNc(0x0), - fhV0M(0x0), - ffChanged(kTRUE), + fNBD(new TF1("fNBD", "ROOT::Math::negative_binomial_pdf(x,[0],[1])", 0, 45000)), + fGlauberNBD(nullptr), + fTrentoNBD(nullptr), + fNBDFitterMode(NBDFitterMode::Glauber), + fhNanc(nullptr), + fhNpNc(nullptr), + fhNSources(nullptr), + fhV0M(nullptr), + ffChanged(true), fCurrentf(-1), - fAncestorMode(2), - fNpart(0x0), - fNcoll(0x0), - fContent(0x0), + fAncestorMode(AncestorMode::Continuous), + fNpart(nullptr), + fNcoll(nullptr), + fContent(nullptr), fNNpNcPairs(-1), fMaxNpNcPairs(1000000), fMu(45), @@ -121,134 +80,181 @@ multGlauberNBDFitter::multGlauberNBDFitter(const char* name, const char* title) fFitNpx(5000) { // Named constructor - fNpart = new Double_t[fMaxNpNcPairs]; - fNcoll = new Double_t[fMaxNpNcPairs]; - fContent = new Long_t[fMaxNpNcPairs]; - - // Ancestor histo - // fhNanc = new TH1D("fhNanc", "", fAncestorMode==2?10000:1000, -0.5, 999.5); + fNpart = new double[fMaxNpNcPairs]; + fNcoll = new double[fMaxNpNcPairs]; + fContent = new int64_t[fMaxNpNcPairs]; // NBD - fNBD = new TF1("fNBD", "ROOT::Math::negative_binomial_pdf(x,[0],[1])", 0, 45000); fNBD->SetNpx(45000); // master function - fGlauberNBD = new TF1("fGlauberNBD", this, &multGlauberNBDFitter::ProbDistrib, - 0, 50000, 5, "multGlauberNBDFitter", "ProbDistrib"); - fGlauberNBD->SetParameter(0, fMu); - fGlauberNBD->SetParameter(1, fk); - fGlauberNBD->SetParameter(2, ff); - fGlauberNBD->SetParameter(3, fnorm); - - fGlauberNBD->SetParName(0, "mu"); - fGlauberNBD->SetParName(1, "k"); - fGlauberNBD->SetParName(2, "f"); - fGlauberNBD->SetParName(3, "norm"); - fGlauberNBD->SetParName(4, "dMu/dNanc"); + InitGlauberNBD(fMu, fk, ff, fnorm); } + //________________________________________________________________ multGlauberNBDFitter::~multGlauberNBDFitter() { // Destructor - if (fNBD) { - delete fNBD; - fNBD = 0x0; - } - if (fhNanc) { - delete fhNanc; - fhNanc = 0x0; - } - if (fhNpNc) { - delete fhNpNc; - fhNpNc = 0x0; - } - if (fNpart) - delete[] fNpart; - if (fNcoll) - delete[] fNcoll; - if (fContent) - delete[] fContent; + delete fNBD; + delete fhNanc; + delete fhNpNc; + delete[] fNpart; + delete[] fNcoll; + delete[] fContent; +} + +void multGlauberNBDFitter::InitGlauberNBD(const float mu, const float k, const float f, const float norm) +{ + fNBDFitterMode = NBDFitterMode::Glauber; + fGlauberNBD = new TF1("fGlauberNBD", this, &multGlauberNBDFitter::GlauberProbDistrib, + 0, 50000, 5); + fGlauberNBD->SetParameter(Index(FitPar::mu), mu); + fGlauberNBD->SetParameter(Index(FitPar::k), k); + fGlauberNBD->SetParameter(Index(FitPar::f), f); + fGlauberNBD->SetParameter(Index(FitPar::norm), norm); + + fGlauberNBD->SetParName(Index(FitPar::mu), "mu"); + fGlauberNBD->SetParName(Index(FitPar::k), "k"); + fGlauberNBD->SetParName(Index(FitPar::f), "f"); + fGlauberNBD->SetParName(Index(FitPar::norm), "norm"); + fGlauberNBD->SetParName(Index(FitPar::dMu), "dMu/dNanc"); +} + +void multGlauberNBDFitter::InitTrentoNBD(const float mu, const float k, const float norm) +{ + fNBDFitterMode = NBDFitterMode::Trento; + fGlauberNBD = nullptr; + fTrentoNBD = new TF1("fTrentoNBD", this, &multGlauberNBDFitter::TrentoProbDistrib, + 0, 50000, 5); + fTrentoNBD->SetParameter(Index(FitPar::mu), mu); + fTrentoNBD->SetParameter(Index(FitPar::k), k); + fTrentoNBD->SetParameter(Index(FitPar::norm), norm); + + fTrentoNBD->SetParName(Index(FitPar::mu), "mu"); + fTrentoNBD->SetParName(Index(FitPar::k), "k"); + fTrentoNBD->SetParName(Index(FitPar::f), "f"); + fTrentoNBD->SetParName(Index(FitPar::norm), "norm"); + fTrentoNBD->SetParName(Index(FitPar::dMu), "dMu/dNanc"); } //______________________________________________________ -Double_t multGlauberNBDFitter::ProbDistrib(Double_t* x, Double_t* par) +double multGlauberNBDFitter::GlauberProbDistrib(const double* x, const double* par) // Master fitter function { - Double_t lMultValue = x[0]; - Double_t lProbability = 0.0; - ffChanged = kTRUE; - const Double_t lAlmost0 = 1.e-13; + double lMultValue = x[0]; + double lProbability = 0.0; + ffChanged = true; + static constexpr double Almost0 = 1.e-13; // Comment this line in order to make the code evaluate Nancestor all the time - if (TMath::Abs(fCurrentf - par[2]) < lAlmost0) - ffChanged = kFALSE; + if (std::abs(fCurrentf - par[Index(FitPar::f)]) < Almost0) { + ffChanged = false; + } //______________________________________________________ // Recalculate the ancestor distribution in case f changed if (ffChanged) { - fCurrentf = par[2]; + fCurrentf = par[Index(FitPar::f)]; fhNanc->Reset(); for (int ibin = 0; ibin < fNNpNcPairs; ibin++) { - Double_t lOption0 = (Int_t)(fNpart[ibin] * par[2] + fNcoll[ibin] * (1.0 - par[2])); - Double_t lOption1 = TMath::Floor(fNpart[ibin] * par[2] + fNcoll[ibin] * (1.0 - par[2]) + 0.5); - Double_t lOption2 = (fNpart[ibin] * par[2] + fNcoll[ibin] * (1.0 - par[2])); - if (fAncestorMode == 0) + double lOption0 = static_cast(fNpart[ibin] * par[Index(FitPar::f)] + fNcoll[ibin] * (1.0 - par[Index(FitPar::f)])); + double lOption1 = std::floor(fNpart[ibin] * par[Index(FitPar::f)] + fNcoll[ibin] * (1.0 - par[Index(FitPar::f)]) + 0.5); + double lOption2 = (fNpart[ibin] * par[Index(FitPar::f)] + fNcoll[ibin] * (1.0 - par[Index(FitPar::f)])); + if (fAncestorMode == AncestorMode::Truncated) { fhNanc->Fill(lOption0, fContent[ibin]); - if (fAncestorMode == 1) + } + if (fAncestorMode == AncestorMode::Rounded) { fhNanc->Fill(lOption1, fContent[ibin]); - if (fAncestorMode == 2) + } + if (fAncestorMode == AncestorMode::Continuous) { fhNanc->Fill(lOption2, fContent[ibin]); + } } if (fhNanc->Integral() < 1) { - cout << "ERROR: ANCESTOR HISTOGRAM EMPTY" << endl; - cout << "Will not do anything. Call InitializeNpNc if you want to plot without fitting" << endl; + LOG(info) << "ERROR: ANCESTOR HISTOGRAM EMPTY"; + LOG(info) << "Will not do anything. Call InitNpNc if you want to plot without fitting"; return 0; } fhNanc->Scale(1. / fhNanc->Integral()); } //______________________________________________________ // Actually evaluate function - Int_t lStartBin = fhNanc->FindBin(0.0) + 1; - for (Long_t iNanc = lStartBin; iNanc < fhNanc->GetNbinsX() + 1; iNanc++) { - Double_t lNancestors = fhNanc->GetBinCenter(iNanc); - Double_t lNancestorCount = fhNanc->GetBinContent(iNanc); - // if(lNancestorCount<1e-12&&lNancestors>10) break; + int lStartBin = fhNanc->FindBin(0.0) + 1; + for (int64_t iNanc = lStartBin; iNanc < fhNanc->GetNbinsX() + 1; ++iNanc) { + double lNancestors = fhNanc->GetBinCenter(iNanc); + double lNancestorCount = fhNanc->GetBinContent(iNanc); // allow for variable mu in case requested - Double_t lThisMu = (((Double_t)lNancestors)) * (par[0] + par[4] * lNancestors); - Double_t lThisk = (((Double_t)lNancestors)) * par[1]; - Double_t lpval = TMath::Power(1.0 + lThisMu / lThisk, -1); - fNBD->SetParameter(1, lThisk); - fNBD->SetParameter(0, lpval); - Double_t lMult = 0.0; - if (lMultValue > 1e-6) - lMult = fAncestorMode != 2 ? fNBD->Eval(lMultValue) : ContinuousNBD(lMultValue, lThisMu, lThisk); + double lThisMu = lNancestors * (par[Index(FitPar::mu)] + par[Index(FitPar::dMu)] * lNancestors); + double lThisk = lNancestors * par[Index(FitPar::k)]; + double lpval = std::pow(1.0 + lThisMu / lThisk, -1); + fNBD->SetParameter(Index(NBDPar::k), lThisk); + fNBD->SetParameter(Index(NBDPar::p), lpval); + + double lMult = 0.0; + static constexpr double MultTolerance = 1e-6; + if (lMultValue > MultTolerance) { + lMult = fAncestorMode != AncestorMode::Continuous ? fNBD->Eval(lMultValue) : ContinuousNBD(lMultValue, lThisMu, lThisk); + } lProbability += lNancestorCount * lMult; } //______________________________________________________ - return par[3] * lProbability; + return par[Index(FitPar::norm)] * lProbability; +} + +double multGlauberNBDFitter::TrentoProbDistrib(const double* x, const double* par) +// Master fitter function +{ + double lMultValue = x[0]; + double lProbability = 0.0; + //______________________________________________________ + // Actually ealuate function + for (int64_t iNSrc = 1; iNSrc < fhNSources->GetNbinsX() + 1; ++iNSrc) { + double lNsources = fhNSources->GetBinCenter(iNSrc); + double lThisMu = lNsources * par[Index(FitPar::mu)]; + double lThisk = lNsources * par[Index(FitPar::k)]; + double lpval = std::pow(1 + lThisMu / lThisk, -1); + fNBD->SetParameter(Index(NBDPar::k), lThisk); + fNBD->SetParameter(Index(NBDPar::p), lpval); + double lMult = fNBD->Eval(lMultValue); + lProbability += fhNSources->GetBinContent(iNSrc) * lMult; + } + //______________________________________________________ + return par[Index(FitPar::norm)] * lProbability; } //________________________________________________________________ -Bool_t multGlauberNBDFitter::SetNpartNcollCorrelation(TH2* hNpNc) +bool multGlauberNBDFitter::SetNpartNcollCorrelation(TH2* hNpNc) { - Bool_t lReturnValue = kTRUE; + bool lReturnValue = true; if (hNpNc) { - fhNpNc = reinterpret_cast(hNpNc); + fhNpNc = hNpNc; + } else { + lReturnValue = false; + } + return lReturnValue; +} + +//________________________________________________________________ +bool multGlauberNBDFitter::SetNSources(TH1* hNSources) +{ + bool lReturnValue = true; + if (hNSources) { + fhNSources = hNSources; } else { - lReturnValue = kFALSE; + lReturnValue = false; } return lReturnValue; } //________________________________________________________________ -Bool_t multGlauberNBDFitter::SetInputV0M(TH1* hV0M) +bool multGlauberNBDFitter::SetInputV0M(TH1* hV0M) { - Bool_t lReturnValue = kTRUE; + bool lReturnValue = true; if (hV0M) { - fhV0M = reinterpret_cast(hV0M); + fhV0M = hV0M; } else { - lReturnValue = kFALSE; + lReturnValue = false; } return lReturnValue; } @@ -265,20 +271,30 @@ TF1* multGlauberNBDFitter::GetGlauberNBD() return fGlauberNBD; } +TF1* multGlauberNBDFitter::GetTrentoNBD() +{ + return fTrentoNBD; +} + //________________________________________________________________ -void multGlauberNBDFitter::SetFitRange(Double_t lMin, Double_t lMax) +void multGlauberNBDFitter::SetFitRange(const double lMin, const double lMax) { - fGlauberNBD->SetRange(lMin, lMax); + if (fGlauberNBD) { + fGlauberNBD->SetRange(lMin, lMax); + } + if (fTrentoNBD) { + fTrentoNBD->SetRange(lMin, lMax); + } } //________________________________________________________________ -void multGlauberNBDFitter::SetFitOptions(TString lOpt) +void multGlauberNBDFitter::SetFitOptions(const TString& lOpt) { fFitOptions = lOpt; } //________________________________________________________________ -void multGlauberNBDFitter::SetFitNpx(Long_t lNpx) +void multGlauberNBDFitter::SetFitNpx(const int64_t lNpx) { fFitNpx = lNpx; } @@ -286,79 +302,120 @@ void multGlauberNBDFitter::SetFitNpx(Long_t lNpx) //________________________________________________________________ void multGlauberNBDFitter::InitAncestor() { - if (!fhNanc) - fhNanc = new TH1D("fhNanc", "", fAncestorMode == 2 ? 10000 : 1000, -0.5, 999.5); + if (!fhNanc) { + fhNanc = new TH1D("fhNanc", "", fAncestorMode == AncestorMode::Continuous ? 10000 : 1000, -0.5, 999.5); + } } //________________________________________________________________ -Bool_t multGlauberNBDFitter::DoFit() +bool multGlauberNBDFitter::DoFit() { - InitAncestor(); // Try very hard, please TVirtualFitter::SetMaxIterations(5000000); + auto timer = new TStopwatch(); + timer->Start(true); + + bool lReturnValue = false; + switch (fNBDFitterMode) { + case NBDFitterMode::Glauber: + LOG(info) << "Doing Glauber fit!!"; + lReturnValue = DoGlauberFit(); + break; + case NBDFitterMode::Trento: + LOG(info) << "Doing Trento fit!!"; + lReturnValue = DoTrentoFit(); + break; + + default: + break; + } + + timer->Stop(); + double lTotalTime = timer->RealTime(); + if (lReturnValue) { + LOG(info) << "---> Fitting succeeded after " << lTotalTime << " seconds"; + } else { + LOG(info) << "---> Fitting failed after " << lTotalTime << " seconds"; + } + return lReturnValue; +} + +bool multGlauberNBDFitter::DoGlauberFit() +{ + InitAncestor(); if (!InitializeNpNc()) { - cout << "---> Initialization of Npart x Ncoll correlation info failed!" << endl; - return kFALSE; + LOG(info) << "---> Initialization of Npart x Ncoll correlation info failed!"; + return false; } - TStopwatch* timer = new TStopwatch(); - timer->Start(kTRUE); - if (fAncestorMode == 0) - cout << "---> Config: Nancestors will be truncated" << endl; - if (fAncestorMode == 1) - cout << "---> Config: Nancestors will be rounded" << endl; - if (fAncestorMode == 2) - cout << "---> Config: Nancestors will be taken as float" << endl; - cout << "---> Now fitting, please wait..." << endl; + if (fAncestorMode == AncestorMode::Truncated) { + LOG(info) << "---> Config: Nancestors will be truncated"; + } + if (fAncestorMode == AncestorMode::Rounded) { + LOG(info) << "---> Config: Nancestors will be rounded"; + } + if (fAncestorMode == AncestorMode::Continuous) { + LOG(info) << "---> Config: Nancestors will be taken as float"; + } + LOG(info) << "---> Now fitting, please wait..."; fGlauberNBD->SetNpx(fFitNpx); TFitResultPtr fitptr; fFitOptions.Append("S"); fitptr = fhV0M->Fit("fGlauberNBD", fFitOptions.Data()); - timer->Stop(); - Double_t lTotalTime = timer->RealTime(); - cout << "---> Fitting took " << lTotalTime << " seconds" << endl; - - fMu = fGlauberNBD->GetParameter(0); - fk = fGlauberNBD->GetParameter(1); - ff = fGlauberNBD->GetParameter(2); - fnorm = fGlauberNBD->GetParameter(3); - fdMu = fGlauberNBD->GetParameter(4); + fMu = fGlauberNBD->GetParameter(Index(FitPar::mu)); + fk = fGlauberNBD->GetParameter(Index(FitPar::k)); + ff = fGlauberNBD->GetParameter(Index(FitPar::f)); + fnorm = fGlauberNBD->GetParameter(Index(FitPar::norm)); + fdMu = fGlauberNBD->GetParameter(Index(FitPar::dMu)); + return fitptr.Get()->IsValid(); +} +bool multGlauberNBDFitter::DoTrentoFit() +{ + fTrentoNBD->SetNpx(fFitNpx); + TFitResultPtr fitptr; + fFitOptions.Append("S"); + fitptr = fhV0M->Fit("fTrentoNBD", fFitOptions.Data()); + fMu = fTrentoNBD->GetParameter(Index(FitPar::mu)); + fk = fTrentoNBD->GetParameter(Index(FitPar::k)); + fnorm = fTrentoNBD->GetParameter(Index(FitPar::norm)); return fitptr.Get()->IsValid(); } //________________________________________________________________ -Bool_t multGlauberNBDFitter::InitializeNpNc() +bool multGlauberNBDFitter::InitializeNpNc() { // This function initializes fhNpNc // Warning: X == Npart, Y == Ncoll - Bool_t lReturnValue = kFALSE; + bool lReturnValue = false; if (fhNpNc) { fNNpNcPairs = 0; // Sweep all allowed values of Npart, Ncoll; find counters - for (int xbin = 1; xbin < 500; xbin++) { - for (int ybin = 1; ybin < 3000; ybin++) { + static constexpr int NBinsX = 500; + static constexpr int NBinsY = 3000; + for (int xbin = 1; xbin < NBinsX; xbin++) { + for (int ybin = 1; ybin < NBinsY; ybin++) { if (fhNpNc->GetBinContent(fhNpNc->FindBin(xbin, ybin)) != 0) { fNpart[fNNpNcPairs] = xbin; fNcoll[fNNpNcPairs] = ybin; - fContent[fNNpNcPairs] = fhNpNc->GetBinContent(fhNpNc->FindBin(xbin, ybin)); + fContent[fNNpNcPairs] = static_cast(fhNpNc->GetBinContent(fhNpNc->FindBin(xbin, ybin))); fNNpNcPairs++; } } } - cout << "Initialized with number of (Npart, Ncoll) pairs: " << fNNpNcPairs << endl; - lReturnValue = kTRUE; + LOG(info) << "Initialized with number of (Npart, Ncoll) pairs: " << fNNpNcPairs; + lReturnValue = true; } else { - cout << "Failed to initialize! Please provide input histogram with (Npart, Ncoll) info!" << endl; - cout << "Please remember to call SetNpartNcollCorrelation before doing fit!" << endl; + LOG(info) << "Failed to initialize! Please provide input histogram with (Npart, Ncoll) info!"; + LOG(info) << "Please remember to call SetNpartNcollCorrelation before doing fit!"; } return lReturnValue; } //________________________________________________________________ -Double_t multGlauberNBDFitter::ContinuousNBD(Double_t n, Double_t mu, Double_t k) +double multGlauberNBDFitter::ContinuousNBD(const double n, const double mu, const double k) { // Adaptation of the negative binomial distribution // for non-integer arguments: analytical continuation @@ -367,43 +424,57 @@ Double_t multGlauberNBDFitter::ContinuousNBD(Double_t n, Double_t mu, Double_t k // in fact it is equivalent to that if 'n' is typecast as // an integer prior to use - Double_t F; - Double_t f; + double F{}; + double f{}; - if (n + k > 100.0) { + static constexpr double NumStabilityThreshold = 100.0; + if (n + k > NumStabilityThreshold) { // log method for handling large numbers - F = TMath::LnGamma(n + k) - TMath::LnGamma(n + 1.) - TMath::LnGamma(k); - f = n * TMath::Log(mu / k) - (n + k) * TMath::Log(1.0 + mu / k); + F = std::lgamma(n + k) - std::lgamma(n + 1.) - std::lgamma(k); + f = n * std::log(mu / k) - (n + k) * std::log1p(mu / k); F = F + f; - F = TMath::Exp(F); + F = std::exp(F); } else { - F = TMath::Gamma(n + k) / (TMath::Gamma(n + 1.) * TMath::Gamma(k)); - f = n * TMath::Log(mu / k) - (n + k) * TMath::Log(1.0 + mu / k); - f = TMath::Exp(f); + F = std::tgamma(n + k) / (std::tgamma(n + 1.) * std::tgamma(k)); + f = n * std::log(mu / k) - (n + k) * std::log1p(mu / k); + f = std::exp(f); F *= f; } return F; } -void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCollProf, TH2F* lNPart2DPlot, TH2F* lNColl2DPlot, TH1F* hPercentileMap, Double_t lLoRange, Double_t lHiRange, TH3D* lNpNcEcc, TH2F* lEcc2DPlot, TH3D* lNpNcB, TH2F* lB2DPlot, TH2F* lNancestor2DPlot, Double_t fProbabilityCutoff) +void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, + TProfile* lNCollProf, + TH2F* lNPart2DPlot, + TH2F* lNColl2DPlot, + TH1F* hPercentileMap, + double lLoRange, + double lHiRange, + TH3D* lNpNcEcc, + TH2F* lEcc2DPlot, + TH3D* lNpNcB, + TH2F* lB2DPlot, + TH2F* lNancestor2DPlot, + TH2F* lMult2DPlot, + double fProbabilityCutoff) { - cout << "Calculating , in centrality bins..." << endl; - cout << "Range to calculate: " << lLoRange << " to " << lHiRange << endl; + LOG(info) << "Calculating , in centrality bins..."; + LOG(info) << "Range to calculate: " << lLoRange << " to " << lHiRange; - cout << "Acquiring values from the fit function..." << endl; + LOG(info) << "Acquiring values from the fit function..."; - fMu = fGlauberNBD->GetParameter(0); - fk = fGlauberNBD->GetParameter(1); - ff = fGlauberNBD->GetParameter(2); - fnorm = fGlauberNBD->GetParameter(3); - fdMu = fGlauberNBD->GetParameter(4); + fMu = fGlauberNBD->GetParameter(Index(FitPar::mu)); + fk = fGlauberNBD->GetParameter(Index(FitPar::k)); + ff = fGlauberNBD->GetParameter(Index(FitPar::f)); + fnorm = fGlauberNBD->GetParameter(Index(FitPar::norm)); + fdMu = fGlauberNBD->GetParameter(Index(FitPar::dMu)); - cout << "Please inspect now: " << endl; - cout << "Glauber NBD mu ............: " << fMu << endl; - cout << "Glauber NBD k .............: " << fk << endl; - cout << "Glauber NBD f .............: " << ff << endl; - cout << "Glauber NBD norm ..........: " << fnorm << endl; - cout << "Glauber NBD dmu/dNanc .....: " << fdMu << endl; + LOG(info) << "Please inspect now: "; + LOG(info) << "Glauber NBD mu ............: " << fMu; + LOG(info) << "Glauber NBD k .............: " << fk; + LOG(info) << "Glauber NBD f .............: " << ff; + LOG(info) << "Glauber NBD norm ..........: " << fnorm; + LOG(info) << "Glauber NBD dmu/dNanc .....: " << fdMu; // 2-fold nested loop: // + looping over all Nancestor combinations @@ -416,31 +487,36 @@ void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCol } // bypass to zero for (int ibin = 0; ibin < fNNpNcPairs; ibin++) { - if (ibin % 200 == 0) - cout << "At NpNc pair #" << ibin << " of " << fNNpNcPairs << "..." << endl; - Double_t lNAncestors0 = (Int_t)(fNpart[ibin] * ff + fNcoll[ibin] * (1.0 - ff)); - Double_t lNAncestors1 = TMath::Floor(fNpart[ibin] * ff + fNcoll[ibin] * (1.0 - ff) + 0.5); - Double_t lNAncestors2 = (fNpart[ibin] * ff + fNcoll[ibin] * (1.0 - ff)); + static constexpr int LogInterval = 200; + if (ibin % LogInterval == 0) { + LOG(info) << "At NpNc pair #" << ibin << " of " << fNNpNcPairs << "..."; + } + double lNAncestors0 = static_cast(fNpart[ibin] * ff + fNcoll[ibin] * (1.0 - ff)); + double lNAncestors1 = std::floor(fNpart[ibin] * ff + fNcoll[ibin] * (1.0 - ff) + 0.5); + double lNAncestors2 = (fNpart[ibin] * ff + fNcoll[ibin] * (1.0 - ff)); // define ancestors officially - Double_t lNancestors = lNAncestors0; - if (fAncestorMode == 1) + double lNancestors = lNAncestors0; + if (fAncestorMode == AncestorMode::Rounded) { lNancestors = lNAncestors1; - if (fAncestorMode == 2) + } + if (fAncestorMode == AncestorMode::Continuous) { lNancestors = lNAncestors2; + } // eccentricity handling - TH1D* hEccentricity = 0x0; + TH1D* hEccentricity = nullptr; if (lNpNcEcc) { // locate the histogram that corresponds to the eccentricity distribution in this NpNc pair lNpNcEcc->GetXaxis()->SetRange(lNpNcEcc->GetXaxis()->FindBin(fNpart[ibin]), lNpNcEcc->GetXaxis()->FindBin(fNpart[ibin])); lNpNcEcc->GetYaxis()->SetRange(lNpNcEcc->GetYaxis()->FindBin(fNcoll[ibin]), lNpNcEcc->GetYaxis()->FindBin(fNcoll[ibin])); - hEccentricity = reinterpret_cast(lNpNcEcc->Project3D("z")); + hEccentricity = dynamic_cast(lNpNcEcc->Project3D("z")); hEccentricity->SetName(Form("hEccentricity_%i", ibin)); // normalize into unitary fractions - Double_t eccIntegral = hEccentricity->Integral(1, hEccentricity->GetNbinsX() + 1); - if (eccIntegral > 1e-6) { // no counts + double eccIntegral = hEccentricity->Integral(1, hEccentricity->GetNbinsX() + 1); + static constexpr double EccTolerance = 1e-6; + if (eccIntegral > EccTolerance) { // no counts hEccentricity->Scale(1. / eccIntegral); } else { hEccentricity->Scale(0.0); @@ -448,52 +524,61 @@ void multGlauberNBDFitter::CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCol } // impact parameter handling - TH1D* hImpactParameter = 0x0; + TH1D* hImpactParameter = nullptr; if (lNpNcB) { // locate the histogram that corresponds to the eccentricity distribution in this NpNc pair lNpNcB->GetXaxis()->SetRange(lNpNcB->GetXaxis()->FindBin(fNpart[ibin]), lNpNcB->GetXaxis()->FindBin(fNpart[ibin])); lNpNcB->GetYaxis()->SetRange(lNpNcB->GetYaxis()->FindBin(fNcoll[ibin]), lNpNcB->GetYaxis()->FindBin(fNcoll[ibin])); - hImpactParameter = reinterpret_cast(lNpNcB->Project3D("z")); + hImpactParameter = dynamic_cast(lNpNcB->Project3D("z")); hImpactParameter->SetName(Form("hImpactParameter_%i", ibin)); // normalize into unitary fractions - Double_t bIntegral = hImpactParameter->Integral(1, hImpactParameter->GetNbinsX() + 1); - if (bIntegral > 1e-6) { // no counts + double bIntegral = hImpactParameter->Integral(1, hImpactParameter->GetNbinsX() + 1); + static constexpr double ImpactParTolerance = 1e-6; + if (bIntegral > ImpactParTolerance) { // no counts hImpactParameter->Scale(1. / bIntegral); } else { hImpactParameter->Scale(0.0); } } - for (Long_t lMultValue = 1; lMultValue < lHiRange; lMultValue++) { - Double_t lNancestorCount = fContent[ibin]; - Double_t lThisMu = (((Double_t)lNancestors)) * fMu; - Double_t lThisk = (((Double_t)lNancestors)) * fk; - Double_t lpval = TMath::Power(1 + lThisMu / lThisk, -1); + for (int64_t lMultValue = 1; lMultValue < lHiRange; lMultValue++) { + double lNancestorCount = fContent[ibin]; + double lThisMu = lNancestors * fMu; + double lThisk = lNancestors * fk; + double lpval = std::pow(1 + lThisMu / lThisk, -1); fNBD->SetParameter(1, lThisk); fNBD->SetParameter(0, lpval); - Double_t lMult = 0.0; - if (lMultValue > 1e-6) - lMult = fAncestorMode != 2 ? fNBD->Eval(lMultValue) : ContinuousNBD(lMultValue, lThisMu, lThisk); - Double_t lProbability = lNancestorCount * lMult; + double lMult = 0.0; + static constexpr double MultTolerance = 1e-6; + if (lMultValue > MultTolerance) { + lMult = fAncestorMode != AncestorMode::Continuous ? fNBD->Eval(lMultValue) : ContinuousNBD(lMultValue, lThisMu, lThisk); + } + double lProbability = lNancestorCount * lMult; if (lProbability < fProbabilityCutoff) { continue; // skip if probability of contributing too small } - Double_t lMultValueToFill = lMultValue; - if (hPercentileMap) + double lMultValueToFill = lMultValue; + if (hPercentileMap) { lMultValueToFill = hPercentileMap->GetBinContent(hPercentileMap->FindBin(lMultValue)); + } lNPartProf->Fill(lMultValueToFill, fNpart[ibin], lProbability); lNCollProf->Fill(lMultValueToFill, fNcoll[ibin], lProbability); if (lNancestor2DPlot) { // fill cross-check histogram with lNancestorCount at lNancestors value lNancestor2DPlot->Fill(lMultValueToFill, lNancestors, lProbability); } - if (lNPart2DPlot) + if (lMult2DPlot) { + lMult2DPlot->Fill(lMultValueToFill, lMultValue, lProbability); + } + if (lNPart2DPlot) { lNPart2DPlot->Fill(lMultValueToFill, fNpart[ibin], lProbability); - if (lNColl2DPlot) + } + if (lNColl2DPlot) { lNColl2DPlot->Fill(lMultValueToFill, fNcoll[ibin], lProbability); + } if (lNpNcEcc) { // collapse the entire eccentricity distribution for this combo for (int ib = 1; ib < hEccentricity->GetNbinsX() + 1; ib++) { diff --git a/Common/Tools/Multiplicity/multGlauberNBDFitter.h b/Common/Tools/Multiplicity/multGlauberNBDFitter.h index 598ef312ce9..3c5f6370722 100644 --- a/Common/Tools/Multiplicity/multGlauberNBDFitter.h +++ b/Common/Tools/Multiplicity/multGlauberNBDFitter.h @@ -8,10 +8,16 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// + +/// \file multGlauberNBDFitter.h +/// \brief Helper class to perform Glauber fits +/// \author David Dobrigkeit Chinellato + #ifndef COMMON_TOOLS_MULTIPLICITY_MULTGLAUBERNBDFITTER_H_ #define COMMON_TOOLS_MULTIPLICITY_MULTGLAUBERNBDFITTER_H_ +#include + #include #include #include @@ -21,67 +27,124 @@ #include #include -#include + +#include class multGlauberNBDFitter : public TNamed { - public: // basic functionality multGlauberNBDFitter(); explicit multGlauberNBDFitter(const char* name, const char* title = "Glauber+NBD fitter"); - ~multGlauberNBDFitter(); + ~multGlauberNBDFitter() override; + + enum class AncestorMode { + Truncated = 0, // Nancestors = (int) truncation + Rounded, // Nancestors = rounded to nearest integer + Continuous // Nancestors = kept as float, uses ContinuousNBD + }; + + enum class NBDFitterMode { + Glauber = 0, // Uses Npart & Ncoll from TGlauberMC + Trento // Uses trento entropy + }; + + enum class FitPar { + mu = 0, + k, + f, + norm, + dMu + }; + + enum class NBDPar { + p = 0, + k + }; + + template + static constexpr int Index(TEnum e) + { + return static_cast(e); + } // Master fitter function - Double_t ProbDistrib(Double_t* x, Double_t* par); + double GlauberProbDistrib(const double* x, const double* par); + double TrentoProbDistrib(const double* x, const double* par); void InitAncestor(); // Do Fit: where everything happens - Bool_t DoFit(); + bool DoFit(); + bool DoGlauberFit(); + bool DoTrentoFit(); // Set input characteristics: the 2D plot with Npart, Nanc - Bool_t SetNpartNcollCorrelation(TH2* hNpNc); + bool SetNpartNcollCorrelation(TH2* hNpNc); + // Set input characteristics: the 1D plot with trento entropy + bool SetNSources(TH1* hNSources); // Set main input to be fitted (the V0M distribution) - Bool_t SetInputV0M(TH1* hV0M); + bool SetInputV0M(TH1* hV0M); // Interface to get funtions if asked to TF1* GetNBD(); TF1* GetGlauberNBD(); + TF1* GetTrentoNBD(); // Helper - Bool_t InitializeNpNc(); + bool InitializeNpNc(); + void InitGlauberNBD(const float mu, const float k, const float f, const float norm); + void InitTrentoNBD(const float mu = 45, const float k = 1.5, const float norm = 100); // Interface for debug - void SetAncestorMode(Int_t lAncMode = 0) { fAncestorMode = lAncMode; } - Int_t GetAncestorMode() { return fAncestorMode; } + void SetAncestorMode(AncestorMode lAncMode = AncestorMode::Truncated) { fAncestorMode = lAncMode; } + void SetAncestorMode(int lAncMode = Index(AncestorMode::Truncated)) + { + if (lAncMode < Index(AncestorMode::Truncated) || lAncMode > Index(AncestorMode::Continuous)) { + LOG(fatal) << "Invalid ancestor mode: " << lAncMode; + return; + } + fAncestorMode = static_cast(lAncMode); + } + int GetAncestorMode() { return static_cast(fAncestorMode); } TH1D* GetAncestorHistogram() { return fhNanc; } // Interface to set vals - void SetMu(Double_t lVal) { fMu = lVal; } - void Setk(Double_t lVal) { fk = lVal; } - void Setf(Double_t lVal) { ff = lVal; } - void SetNorm(Double_t lVal) { fnorm = lVal; } + void SetMu(const double lVal) { fMu = lVal; } + void Setk(const double lVal) { fk = lVal; } + void Setf(const double lVal) { ff = lVal; } + void SetNorm(const double lVal) { fnorm = lVal; } // Interface to get vals - Double_t GetMu() { return fMu; } - Double_t Getk() { return fk; } - Double_t Getf() { return ff; } - Double_t GetNorm() { return fnorm; } + double GetMu() { return fMu; } + double Getk() { return fk; } + double Getf() { return ff; } + double GetNorm() { return fnorm; } - void SetFitRange(Double_t lMin, Double_t lMax); - void SetFitOptions(TString lOpt); - void SetFitNpx(Long_t lNpx); + void SetFitRange(const double lMin, const double lMax); + void SetFitOptions(const TString& lOpt); + void SetFitNpx(const int64_t lNpx); // For ancestor mode 2 - Double_t ContinuousNBD(Double_t n, Double_t mu, Double_t k); + double ContinuousNBD(const double n, const double mu, const double k); // For estimating Npart, Ncoll in multiplicity bins // also viable: eccentricity, impact parameter, ancestor cross-check plot - void CalculateAvNpNc(TProfile* lNPartProf, TProfile* lNCollProf, TH2F* lNPart2DPlot, TH2F* lNColl2DPlot, TH1F* hPercentileMap, Double_t lLoRange = -1, Double_t lHiRange = -1, TH3D* lNpNcEcc = 0x0, TH2F* lEcc2DPlot = 0x0, TH3D* lNpNcB = 0x0, TH2F* lB2DPlot = 0x0, TH2F* lNancestor2DPlot = 0x0, Double_t fProbabilityCutoff = -1); - - // void Print(Option_t *option="") const; + void CalculateAvNpNc(TProfile* lNPartProf, + TProfile* lNCollProf, + TH2F* lNPart2DPlot, + TH2F* lNColl2DPlot, + TH1F* hPercentileMap, + double lLoRange = -1, + double lHiRange = -1, + TH3D* lNpNcEcc = nullptr, + TH2F* lEcc2DPlot = nullptr, + TH3D* lNpNcB = nullptr, + TH2F* lB2DPlot = nullptr, + TH2F* lNancestor2DPlot = nullptr, + TH2F* lMult2DPlot = nullptr, + double fProbabilityCutoff = -1); private: // This function serves as the (analytical) NBD @@ -89,36 +152,41 @@ class multGlauberNBDFitter : public TNamed // This function is the key fitting function TF1* fGlauberNBD; + TF1* fTrentoNBD; + + // This is the fitting mode that we use + NBDFitterMode fNBDFitterMode; // Reference histo - TH1D* fhNanc; // basic ancestor distribution - TH2* fhNpNc; // correlation between Npart and Ncoll - TH1* fhV0M; // basic ancestor distribution + TH1D* fhNanc; // basic ancestor distribution + TH2* fhNpNc; // correlation between Npart and Ncoll + TH1* fhNSources; // Trento entropy + TH1* fhV0M; // basic ancestor distribution // Fitting utilities - Bool_t ffChanged; - Double_t fCurrentf; + bool ffChanged; + double fCurrentf; // 0: truncation, 1: rounding, 2: analytical continuation - Int_t fAncestorMode; + AncestorMode fAncestorMode; // Buffer for (Npart, Ncoll) pairs in memory - Double_t* fNpart; - Double_t* fNcoll; - Long_t* fContent; - Long_t fNNpNcPairs; // number of pairs to use - Long_t fMaxNpNcPairs; + double* fNpart; + double* fNcoll; + int64_t* fContent; + int64_t fNNpNcPairs; // number of pairs to use + int64_t fMaxNpNcPairs; // The actual output: mu, k, f, norm - Double_t fMu; - Double_t fdMu; // variable mu option - Double_t fk; - Double_t ff; - Double_t fnorm; + double fMu; + double fdMu; // variable mu option + double fk; + double ff; + double fnorm; TString fFitOptions; - Long_t fFitNpx; + int64_t fFitNpx; - ClassDef(multGlauberNBDFitter, 1); + ClassDefOverride(multGlauberNBDFitter, 1); }; #endif // COMMON_TOOLS_MULTIPLICITY_MULTGLAUBERNBDFITTER_H_ diff --git a/Common/Tools/Multiplicity/multMCCalibrator.cxx b/Common/Tools/Multiplicity/multMCCalibrator.cxx index be3bd71596f..9567200ed84 100644 --- a/Common/Tools/Multiplicity/multMCCalibrator.cxx +++ b/Common/Tools/Multiplicity/multMCCalibrator.cxx @@ -91,13 +91,13 @@ Bool_t multMCCalibrator::Calibrate() TProfile* hProfSim[multCalibrator::kNCentEstim]; cout << " * acquiring input profiles..." << endl; for (Int_t iv = 0; iv < multCalibrator::kNCentEstim; iv++) { - hProfData[iv] = reinterpret_cast(fileData->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); + hProfData[iv] = dynamic_cast(fileData->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); if (!hProfData[iv]) { cout << Form("Data file does not contain histogram h%s, which is necessary for calibration!", multCalibrator::fCentEstimName[iv].Data()) << endl; return kFALSE; } hProfData[iv]->SetName(Form("hProfData_%s", multCalibrator::fCentEstimName[iv].Data())); - hProfSim[iv] = reinterpret_cast(fileSim->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); + hProfSim[iv] = dynamic_cast(fileSim->Get(Form("multiplicity-qa/multiplicityQa/hProf%s", multCalibrator::fCentEstimName[iv].Data()))); if (!hProfSim[iv]) { cout << Form("Sim file does not contain histogram h%s, which is necessary for calibration!", multCalibrator::fCentEstimName[iv].Data()) << endl; return kFALSE; diff --git a/Common/Tools/Multiplicity/multMCCalibrator.h b/Common/Tools/Multiplicity/multMCCalibrator.h index c89fe728328..7f31d0bc600 100644 --- a/Common/Tools/Multiplicity/multMCCalibrator.h +++ b/Common/Tools/Multiplicity/multMCCalibrator.h @@ -41,9 +41,9 @@ class multMCCalibrator : public TNamed // Interface: steering functions to be used in calibration macro // Set Filenames - void SetDataInputFile(TString lFile) { fDataInputFileName = lFile.Data(); } - void SetSimInputFile(TString lFile) { fSimInputFileName = lFile.Data(); } - void SetOutputFile(TString lFile) { fOutputFileName = lFile.Data(); } + void SetDataInputFile(const TString& lFile) { fDataInputFileName = lFile.Data(); } + void SetSimInputFile(const TString& lFile) { fSimInputFileName = lFile.Data(); } + void SetOutputFile(const TString& lFile) { fOutputFileName = lFile.Data(); } // Master Function in this Class: To be called once filenames are set Bool_t Calibrate(); diff --git a/Common/Tools/Multiplicity/multToolsLinkDef.h b/Common/Tools/Multiplicity/multToolsLinkDef.h index f8740f5afcb..2f2cd969c20 100644 --- a/Common/Tools/Multiplicity/multToolsLinkDef.h +++ b/Common/Tools/Multiplicity/multToolsLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Common/Tools/PID/CMakeLists.txt b/Common/Tools/PID/CMakeLists.txt index 3d9bd387cfe..e7443bf5944 100644 --- a/Common/Tools/PID/CMakeLists.txt +++ b/Common/Tools/PID/CMakeLists.txt @@ -16,3 +16,7 @@ o2physics_add_executable(pidparam-tpc-response o2physics_add_executable(check-pid-packing SOURCES checkPidPacking.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore) + +o2physics_add_library(pidTPCModule + SOURCES pidTPCModule.cxx + PUBLIC_LINK_LIBRARIES O2Physics::MLCore) diff --git a/Common/Tools/PID/checkPidPacking.cxx b/Common/Tools/PID/checkPidPacking.cxx index dc497240ef3..1147f27be98 100644 --- a/Common/Tools/PID/checkPidPacking.cxx +++ b/Common/Tools/PID/checkPidPacking.cxx @@ -49,14 +49,14 @@ bool process(std::string outputName, int nevents = 100000) LOG(info) << "Bin width = " << T::bin_width << " vs histo " << hgaus->GetXaxis()->GetBinWidth(1); hgaus->SetLineColor(2); hgaus->SetLineStyle(1); - TH1F* hgausPacked = static_cast(hgaus->Clone("hgausPacked")); + TH1F* hgausPacked = dynamic_cast(hgaus->Clone("hgausPacked")); hgausPacked->SetLineColor(4); hgausPacked->SetLineStyle(2); - TH1F* huniform = static_cast(hgaus->Clone("huniform")); + TH1F* huniform = dynamic_cast(hgaus->Clone("huniform")); huniform->SetLineColor(2); huniform->SetLineStyle(1); - TH1F* huniformPacked = static_cast(hgaus->Clone("huniformPacked")); + TH1F* huniformPacked = dynamic_cast(hgaus->Clone("huniformPacked")); huniformPacked->SetLineColor(4); huniformPacked->SetLineStyle(2); diff --git a/Common/Tools/PID/pidTPCModule.cxx b/Common/Tools/PID/pidTPCModule.cxx new file mode 100644 index 00000000000..0b85bcad88c --- /dev/null +++ b/Common/Tools/PID/pidTPCModule.cxx @@ -0,0 +1,17 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pidTPCModule.cxx +/// \brief Helper file to provide compilation command for headers. +/// +/// \author Vít Kučera , Inha University + +#include "pidTPCModule.h" // IWYU pragma: keep diff --git a/Common/Tools/PID/pidTPCModule.h b/Common/Tools/PID/pidTPCModule.h index 24c7683b70c..802d17a9b06 100644 --- a/Common/Tools/PID/pidTPCModule.h +++ b/Common/Tools/PID/pidTPCModule.h @@ -24,25 +24,33 @@ #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/CollisionTypeHelper.h" #include "Common/Core/PID/TPCPIDResponse.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/TableHelper.h" +#include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/TableProducer/PID/pidTPCBase.h" // IWYU pragma: keep #include "Tools/ML/model.h" +#include +#include #include #include #include +#include #include #include +#include #include -#include #include #include #include // IWYU pragma: keep (do not replace with TMatrixDfwd.h) #include #include +#include +#include +#include #include #include #include @@ -51,6 +59,7 @@ #include #include #include +#include #include #include @@ -136,6 +145,9 @@ struct pidTPCConfigurables : o2::framework::ConfigurableGroup { o2::framework::Configurable useNetworkAl{"useNetworkAl", 1, {"Switch for applying neural network on the alpha mass hypothesis (if network enabled) (set to 0 to disable)"}}; o2::framework::Configurable networkBetaGammaCutoff{"networkBetaGammaCutoff", 0.45, {"Lower value of beta-gamma to override the NN application"}}; o2::framework::Configurable cfgPathGrpLhcIf{"ccdb-path-grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + + o2::framework::Configurable phiEntranceCoeff1{"phiEntranceCoeff1", 1.026f, "phiEntrance = phi + phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * phiEntranceCoeff2 * 1/pT[GeV/c]"}; + o2::framework::Configurable phiEntranceCoeff2{"phiEntranceCoeff2", 85.f, "phiEntrance = phi + phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * phiEntranceCoeff2 * 1/pT[GeV/c]"}; }; // helper getter - FIXME should be separate @@ -306,7 +318,7 @@ class pidTPCModule o2::common::core::enableFlagIfTableRequired(context, "DEdxsCorrected", pidTPCopts.savedEdxsCorrected); // Checking the tables are requested in the workflow and enabling them - auto enableFlag = [&](const std::string particle, o2::framework::Configurable& flag) { + auto enableFlag = [&](const std::string& particle, o2::framework::Configurable& flag) { o2::common::core::enableFlagIfTableRequired(context, "pidTPC" + particle, flag); }; enableFlag("FullEl", pidTPCopts.pidFullEl); @@ -440,12 +452,50 @@ class pidTPCModule //__________________________________________________ template - std::vector createNetworkPrediction(TCCDB& ccdb, soa::Join const& collisions, M const& mults, T const& tracks, B const& bcs, const size_t size) + std::unique_ptr createNetworkPrediction(TCCDB& ccdb, soa::Join const& collisions, M const& mults, T const& tracks, B const& bcs, const size_t size) { + constexpr int NParticleTypes = 9; + constexpr double OneToKilo = 1.e-3; + constexpr int NanoToOne = 1000000000; + constexpr double MultiplicityNorm = 11000.; + constexpr double HadronicRateNormPp = 1500.; + constexpr double HadronicRateNormAa = 50.; + constexpr double Ft0cOccupancyNorm = 60000.; + constexpr float LightSpeedDm2PS = o2::constants::physics::LightSpeedCm2PS / 10.f; + + struct NNVersionEntry { + std::string_view versionName{}; + int numberOfFeatures{}; + int versionNumber{}; + }; + + constexpr std::array NNVersionsDictionary{ + {{"", 6, 1}, + {"1", 6, 1}, + {"2", 7, 2}, + {"3", 8, 3}, + {"4", 9, 4}, + {"5", 9, 5}}}; + + enum IndexNnFeature : int { + IdxTpcInnerParam = 0, + IdxTgl, + IdxSigned1Pt, + IdxMass, + IdxMultiplicity, + IdxNClusters, + IdxFt0cOcc, + IdxHadronicRate, + IdxModPhi + }; - std::vector network_prediction; + constexpr int OldestNNVersionWithFt0c{2}; + constexpr int OldestNNVersionWithHadronicRate{3}; + constexpr int OldestNNVersionWithModPhi{4}; + constexpr int NNVersionWithModPhiEntrance{5}; + + const auto startNetworkTotal = std::chrono::high_resolution_clock::now(); - auto start_network_total = std::chrono::high_resolution_clock::now(); if (pidTPCopts.autofetchNetworks) { const auto& bc = bcs.begin(); // Initialise correct TPC response object before NN setup (for NCl normalisation) @@ -483,7 +533,7 @@ class pidTPCModule if (bc.timestamp() < network.getValidityFrom() || bc.timestamp() > network.getValidityUntil()) { // fetches network only if the runnumbers change LOG(info) << "Fetching network for timestamp: " << bc.timestamp(); - bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(pidTPCopts.networkPathCCDB.value, ".", metadata, bc.timestamp(), false, pidTPCopts.networkPathLocally.value, "", "", &headers); + const bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(pidTPCopts.networkPathCCDB.value, ".", metadata, bc.timestamp(), false, pidTPCopts.networkPathLocally.value, "", "", &headers); networkVersion = headers["NN-Version"]; if (retrieveSuccess) { network.initModel(pidTPCopts.networkPathLocally.value, pidTPCopts.enableNetworkOptimizations.value, pidTPCopts.networkSetNumThreads.value, strtoul(headers["Valid-From"].c_str(), NULL, 0), strtoul(headers["Valid-Until"].c_str(), NULL, 0)); @@ -497,49 +547,53 @@ class pidTPCModule } // Defining some network parameters - int input_dimensions = network.getNumInputNodes(); - int output_dimensions = network.getNumOutputNodes(); - const uint64_t track_prop_size = input_dimensions * size; - const uint64_t prediction_size = output_dimensions * size; + const int inputDimensions = network.getNumInputNodes(); + const int outputDimensions = network.getNumOutputNodes(); + const uint64_t trackPropSize = inputDimensions * size; + const uint64_t predictionSize = outputDimensions * size; + + int nnVersion{0}; + for (const auto& nnVersionEntry : NNVersionsDictionary) { + if (networkVersion == nnVersionEntry.versionName && inputDimensions == nnVersionEntry.numberOfFeatures) { + nnVersion = nnVersionEntry.versionNumber; + break; + } + } + if (nnVersion == 0) { + LOG(fatal) << "createNetworkPrediction(): networkVersion '" << networkVersion << "' and number of features " << inputDimensions << " are not compatible according to NNVersionsDictionary"; + } + + const int hadronicRateNorm = collsys == CollisionSystemType::kCollSyspp ? HadronicRateNormPp : HadronicRateNormAa; + + // Deliberately uninitialised: the evaluation loop below writes every element + // (one block per mass hypothesis), so zero-initialising would only touch + // every page of an O(100 MB) buffer twice. + std::unique_ptr networkPrediction(new float[predictionSize * NParticleTypes]); // For each mass hypotheses - network_prediction = std::vector(prediction_size * 9); // For each mass hypotheses const float nNclNormalization = response->GetNClNormalization(); - float duration_network = 0; + float durationNetwork = 0.f; - std::vector track_properties(track_prop_size); - uint64_t counter_track_props = 0; - int loop_counter = 0; + std::vector trackProperties(trackPropSize); + std::vector outputNetwork; // output buffer, allocation is reused for all mass hypotheses + uint64_t counterTrackProps = 0; + uint64_t loopCounter = 0; // To load the Hadronic rate once for each collision - float hadronicRateBegin = 0.; std::vector hadronicRateForCollision(collisions.size(), 0.0f); - size_t i = 0; + size_t iCollision = 0; for (const auto& collision : collisions) { const auto& bc = collision.template bc_as(); if (irSource.compare("") != 0) { - hadronicRateForCollision[i] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; - } else { - hadronicRateForCollision[i] = 0.0f; + hadronicRateForCollision[iCollision] = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo; } - i++; - } - auto bc = bcs.begin(); - if (irSource.compare("") != 0) { - hadronicRateBegin = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; // kHz - } else { - hadronicRateBegin = 0.0f; + ++iCollision; } + const auto bc = bcs.begin(); + const float hadronicRateBegin = irSource.compare("") != 0 ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * OneToKilo : 0.f; // Filling a std::vector to be evaluated by the network // Evaluation on single tracks brings huge overhead: Thus evaluation is done on one large vector - static constexpr int NParticleTypes = 9; - constexpr int ExpectedInputDimensionsNNV2 = 7; - constexpr int ExpectedInputDimensionsNNV3 = 8; - constexpr int ExpectedInputDimensionsNNV4 = 9; - constexpr auto NetworkVersionV2 = "2"; - constexpr auto NetworkVersionV3 = "3"; - constexpr auto NetworkVersionV4 = "4"; - for (int j = 0; j < NParticleTypes; j++) { // Loop over particle number for which network correction is used + for (int jParticleType = 0; jParticleType < NParticleTypes; ++jParticleType) { // Loop over particle number for which network correction is used for (auto const& trk : tracks) { if (!trk.hasTPC()) { continue; @@ -549,79 +603,54 @@ class pidTPCModule continue; } } - track_properties[counter_track_props] = trk.tpcInnerParam(); - track_properties[counter_track_props + 1] = trk.tgl(); - track_properties[counter_track_props + 2] = trk.signed1Pt(); - track_properties[counter_track_props + 3] = o2::track::pid_constants::sMasses[j]; - track_properties[counter_track_props + 4] = trk.has_collision() ? mults[trk.collisionId()] / 11000. : 1.; - track_properties[counter_track_props + 5] = std::sqrt(nNclNormalization / trk.tpcNClsFound()); - if (input_dimensions == ExpectedInputDimensionsNNV2 && networkVersion == NetworkVersionV2) { - track_properties[counter_track_props + 6] = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; + const bool isGoodTrack = trk.has_collision() && mults.size() > 0; + trackProperties[counterTrackProps + IdxTpcInnerParam] = trk.tpcInnerParam(); + trackProperties[counterTrackProps + IdxTgl] = trk.tgl(); + trackProperties[counterTrackProps + IdxSigned1Pt] = trk.signed1Pt(); + trackProperties[counterTrackProps + IdxMass] = o2::track::pid_constants::sMasses[jParticleType]; + trackProperties[counterTrackProps + IdxMultiplicity] = isGoodTrack ? mults[trk.collisionId()] / MultiplicityNorm : 1.; + trackProperties[counterTrackProps + IdxNClusters] = std::sqrt(nNclNormalization / trk.tpcNClsFound()); + if (nnVersion >= OldestNNVersionWithFt0c) { + trackProperties[counterTrackProps + IdxFt0cOcc] = isGoodTrack ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / Ft0cOccupancyNorm : 1.; } - if (input_dimensions == ExpectedInputDimensionsNNV3 && networkVersion == NetworkVersionV3) { - track_properties[counter_track_props + 6] = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; - if (trk.has_collision()) { - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 50.; - } - } else { - // asign Hadronic Rate at beginning of run if track does not belong to a collision - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateBegin / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateBegin / 50.; - } - } + if (nnVersion >= OldestNNVersionWithHadronicRate) { + const float hadronicRate = isGoodTrack ? hadronicRateForCollision[trk.collisionId()] : hadronicRateBegin; + trackProperties[counterTrackProps + IdxHadronicRate] = hadronicRate / hadronicRateNorm; } - - if (input_dimensions == ExpectedInputDimensionsNNV4 && networkVersion == NetworkVersionV4) { - track_properties[counter_track_props + 6] = trk.has_collision() ? collisions.iteratorAt(trk.collisionId()).ft0cOccupancyInTimeRange() / 60000. : 1.; - if (trk.has_collision()) { - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateForCollision[trk.collisionId()] / 50.; - } - } else { - // asign Hadronic Rate at beginning of run if track does not belong to a collision - if (collsys == CollisionSystemType::kCollSyspp) { - track_properties[counter_track_props + 7] = hadronicRateBegin / 1500.; - } else { - track_properties[counter_track_props + 7] = hadronicRateBegin / 50.; - } + if (nnVersion >= OldestNNVersionWithModPhi) { + float phi = trk.phi(); + if (nnVersion == NNVersionWithModPhiEntrance) { + phi += pidTPCopts.phiEntranceCoeff1 * LightSpeedDm2PS * 0.5 * pidTPCopts.phiEntranceCoeff2 * trk.signed1Pt(); } - track_properties[counter_track_props + 8] = std::fmod(std::fmod(trk.phi(), 2 * M_PI) + 2 * M_PI, M_PI / 9.0); + trackProperties[counterTrackProps + IdxModPhi] = RecoDecay::constrainAngle(phi, 0.f, o2::constants::math::NSectors); } - counter_track_props += input_dimensions; + counterTrackProps += inputDimensions; } - auto start_network_eval = std::chrono::high_resolution_clock::now(); - float* output_network = network.evalModel(track_properties); - auto stop_network_eval = std::chrono::high_resolution_clock::now(); - duration_network += std::chrono::duration>(stop_network_eval - start_network_eval).count(); - for (uint64_t k = 0; k < prediction_size; k += output_dimensions) { - for (int l = 0; l < output_dimensions; l++) { - network_prediction[k + l + prediction_size * loop_counter] = output_network[k + l]; - } + const auto startNetworkEval = std::chrono::high_resolution_clock::now(); + network.evalModel(trackProperties, outputNetwork); + const auto stopNetworkEval = std::chrono::high_resolution_clock::now(); + durationNetwork += std::chrono::duration>(stopNetworkEval - startNetworkEval).count(); + if (outputNetwork.size() != predictionSize) { + LOG(fatal) << "Network output size (" << outputNetwork.size() << ") does not match the expected prediction size (" << predictionSize << ")"; } + std::copy(outputNetwork.begin(), outputNetwork.end(), networkPrediction.get() + predictionSize * loopCounter); - counter_track_props = 0; - loop_counter += 1; + counterTrackProps = 0; + ++loopCounter; } - track_properties.clear(); + trackProperties.clear(); - auto stop_network_total = std::chrono::high_resolution_clock::now(); - LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval ONNX): " << duration_network / (size * 9) << "ns ; Total time (eval ONNX): " << duration_network / 1000000000 << " s"; - LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval + overhead): " << std::chrono::duration>(stop_network_total - start_network_total).count() / (size * 9) << "ns ; Total time (eval + overhead): " << std::chrono::duration>(stop_network_total - start_network_total).count() / 1000000000 << " s"; + const auto stopNetworkTotal = std::chrono::high_resolution_clock::now(); + LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval ONNX): " << durationNetwork / (size * NParticleTypes) << "ns ; Total time (eval ONNX): " << durationNetwork / NanoToOne << " s"; + LOG(debug) << "Neural Network for the TPC PID response correction: Time per track (eval + overhead): " << std::chrono::duration>(stopNetworkTotal - startNetworkTotal).count() / (size * NParticleTypes) << "ns ; Total time (eval + overhead): " << std::chrono::duration>(stopNetworkTotal - startNetworkTotal).count() / NanoToOne << " s"; - return network_prediction; + return networkPrediction; } //__________________________________________________ template - void makePidTables(const int flagFull, NSF& tableFull, const int flagTiny, NST& tableTiny, const o2::track::PID::ID pid, const float tpcSignal, const T& trk, const int64_t multTPC, const std::vector& network_prediction, const int& count_tracks, const int& tracksForNet_size) + void makePidTables(const int flagFull, NSF& tableFull, const int flagTiny, NST& tableTiny, const o2::track::PID::ID pid, const float tpcSignal, const T& trk, const int64_t multTPC, const float* network_prediction, const int& count_tracks, const int& tracksForNet_size) { if (flagFull != 1 && flagTiny != 1) { return; @@ -706,7 +735,7 @@ class pidTPCModule // faster counting for (const auto& track : tracks) { if (track.hasTPC()) { - if (track.collisionId() > -1) { + if (track.has_collision() && cols.size() > 0) { pidmults[track.collisionId()]++; } totalTPCtracks++; @@ -747,7 +776,7 @@ class pidTPCModule reserveTable(pidTPCopts.pidTinyAl, products.tablePIDTinyAl); const uint64_t tracksForNet_size = (pidTPCopts.skipTPCOnly) ? totalTPCnotStandalone : totalTPCtracks; - std::vector network_prediction; + std::unique_ptr network_prediction; if (pidTPCopts.useNetworkCorrection) { network_prediction = createNetworkPrediction(ccdb, cols, pidmults, tracks, bcs, tracksForNet_size); @@ -792,7 +821,7 @@ class pidTPCModule float tpcSignalToEvaluatePID = trk.tpcSignal(); int64_t multTPC = 0; - if (trk.has_collision()) { + if (trk.has_collision() && cols.size() > 0) { multTPC = pidmults[trk.collisionId()]; } @@ -812,7 +841,7 @@ class pidTPCModule double hadronicRate; int occupancy; - if (trk.has_collision()) { + if (trk.has_collision() && cols.size() > 0) { auto collision = cols.iteratorAt(trk.collisionId()); hadronicRate = hadronicRateForCollision[trk.collisionId()]; occupancy = collision.trackOccupancyInTimeRange(); @@ -873,7 +902,7 @@ class pidTPCModule } } - const auto& bc = trk.has_collision() ? cols.rawIteratorAt(trk.collisionId()).template bc_as() : bcs.begin(); + const auto& bc = trk.has_collision() && cols.size() > 0 ? cols.rawIteratorAt(trk.collisionId()).template bc_as() : bcs.begin(); if (useCCDBParam && pidTPCopts.ccdbTimestamp.value == 0 && !ccdb->isCachedObjectValid(pidTPCopts.ccdbPath.value, bc.timestamp())) { // Updating parametrisation only if the initial timestamp is 0 if (pidTPCopts.recoPass.value == "") { LOGP(info, "Retrieving latest TPC response object for timestamp {}:", bc.timestamp()); @@ -948,7 +977,7 @@ class pidTPCModule } auto makePidTablesDefault = [&trk, &tpcSignalToEvaluatePID, &multTPC, &network_prediction, &count_tracks, &tracksForNet_size, this](const int flagFull, auto& tableFull, const int flagTiny, auto& tableTiny, const o2::track::PID::ID pid) { - this->makePidTables(flagFull, tableFull, flagTiny, tableTiny, pid, tpcSignalToEvaluatePID, trk, multTPC, network_prediction, count_tracks, tracksForNet_size); + this->makePidTables(flagFull, tableFull, flagTiny, tableTiny, pid, tpcSignalToEvaluatePID, trk, multTPC, network_prediction.get(), count_tracks, tracksForNet_size); }; makePidTablesDefault(pidTPCopts.pidFullEl, products.tablePIDFullEl, pidTPCopts.pidTinyEl, products.tablePIDTinyEl, o2::track::PID::Electron); diff --git a/Common/Tools/StandardCCDBLoader.h b/Common/Tools/StandardCCDBLoader.h index 058342ce527..0b9f221b9f3 100644 --- a/Common/Tools/StandardCCDBLoader.h +++ b/Common/Tools/StandardCCDBLoader.h @@ -90,7 +90,7 @@ class StandardCCDBLoader } else { LOGF(info, "GRPMagField object returned nullptr, will attempt alternate method"); - o2::parameters::GRPObject* grpo = 0x0; + o2::parameters::GRPObject* grpo = nullptr; grpo = ccdb->template getForRun(cGroup.grpPath.value, currentRunNumber); if (!grpo) { LOG(fatal) << "Alternate path failed! Got nullptr from CCDB for path " << cGroup.grpPath << " of object GRPObject for run " << currentRunNumber; @@ -105,12 +105,8 @@ class StandardCCDBLoader } // load matLUT for this timestamp - if (!lut) { - LOG(info) << "Loading material look-up table for timestamp: " << currentRunNumber; - lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->template getForRun(cGroup.lutPath.value, currentRunNumber)); - } else { - LOG(info) << "Material look-up table already in place. Not reloading."; - } + LOG(info) << "Loading material look-up table for timestamp: " << currentRunNumber; + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->template getForRun(cGroup.lutPath.value, currentRunNumber)); LOG(info) << "Setting global propagator material propagation LUT"; o2::base::Propagator::Instance()->setMatLUT(lut); diff --git a/Common/Tools/TrackPropagationModule.h b/Common/Tools/TrackPropagationModule.h index 702d5c0968b..2583b2c9bed 100644 --- a/Common/Tools/TrackPropagationModule.h +++ b/Common/Tools/TrackPropagationModule.h @@ -22,6 +22,7 @@ #include "Common/Tools/TrackTuner.h" #include +#include #include #include #include @@ -31,13 +32,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include @@ -109,7 +110,9 @@ class TrackPropagationModule bool autoDetectDcaCalib = false; // track tuner setting template - void init(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, THistoRegistry& registry, TInitContext& initContext) + /// \param calibFromCCDBColumns the task supplies the TrackTuner calibrations from the + /// aod::TrackTunerCCDBObjects columns, so nothing is fetched from CCDB here. + void init(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, THistoRegistry& registry, TInitContext& initContext, bool calibFromCCDBColumns = false) { // Checking if the tables are requested in the workflow and enabling them fillTracks = o2::common::core::isTableRequiredInWorkflow(initContext, "Tracks"); @@ -176,23 +179,31 @@ class TrackPropagationModule /// read the track tuner instance configurations, /// to understand whether the TrackTuner::getDcaGraphs function can be called here (input path from string/configurables) /// or inside the process function, to "auto-detect" the input file based on the run number - const auto& workflows = initContext.services().template get(); - for (const o2::framework::DeviceSpec& device : workflows.devices) { /// loop over devices - if (device.name == "propagation-service") { - // loop over the options - // to find the value of TrackTuner::autoDetectDcaCalib - for (const auto& option : device.options) { /// loop over options - if (option.name == "trackTuner.autoDetectDcaCalib") { - // found it! - autoDetectDcaCalib = option.defaultValue.get(); - break; - } - } /// end loop over options + // Read the option off the device we are actually running in. This used to search + // the workflow for a device literally named "propagation-service", which silently + // matched nothing in any other task (propagation-service-v2, -run2, ...), leaving + // autoDetectDcaCalib at its default no matter how the task was configured. + o2::framework::DeviceSpec const& device = initContext.services().template get(); + for (const auto& option : device.options) { /// loop over options + if (option.name == "trackTuner.autoDetectDcaCalib") { + // found it! + autoDetectDcaCalib = option.defaultValue.get(); break; } - } /// end loop over devices + } /// end loop over options LOG(info) << "[TrackPropagationModule] trackTuner.autoDetectDcaCalib it's equal to " << autoDetectDcaCalib; - if (!autoDetectDcaCalib) { + if (calibFromCCDBColumns && trackTunerObj.isInputFileFromCCDB) { + // The column is the single source of truth for the path: its default carries the + // per-period mapping and it is overridden through "ccdb:fTrackTunerDca". Silently + // preferring one of two path settings is how calibrations diverge unnoticed, so a + // leftover trackTuner.pathInputFile is an error rather than a shadowed value. + if (!trackTunerObj.pathInputFile.empty()) { + LOG(fatal) << "[TrackPropagationModule] trackTuner.pathInputFile is set to '" << trackTunerObj.pathInputFile + << "' while the TrackTuner calibrations are taken from the aod::TrackTunerCCDBObjects columns. " + << "Set the path through the \"ccdb:fTrackTunerDca\" option instead, or unset trackTuner.pathInputFile."; + } + LOG(info) << "[TrackPropagationModule] TrackTuner calibrations come from CCDB columns; graphs retrieved in the process function"; + } else if (!autoDetectDcaCalib) { LOG(info) << "[TrackPropagationModule] retrieve the graphs already (we are in propagationService::Init() function)"; trackTunerObj.getDcaGraphs(); } else { @@ -208,44 +219,81 @@ class TrackPropagationModule registry.template add("hDCAxyVsPtMC", "hDCAxyVsPtMC", o2::framework::HistType::kTH2F, {axisBinsDCA, cGroup.axisPtQA}); registry.template add("hDCAzVsPtRec", "hDCAzVsPtRec", o2::framework::HistType::kTH2F, {axisBinsDCA, cGroup.axisPtQA}); registry.template add("hDCAzVsPtMC", "hDCAzVsPtMC", o2::framework::HistType::kTH2F, {axisBinsDCA, cGroup.axisPtQA}); + + registry.template add("hPropagation", "hPropagation", o2::framework::HistType::kTH1D, {{3, 0.f, 3.f}}); + registry.template get(HIST("hPropagation"))->GetXaxis()->SetBinLabel(1, "All"); + registry.template get(HIST("hPropagation"))->GetXaxis()->SetBinLabel(2, Form("TracksIU, x < %g cm", cGroup.minPropagationRadius.value)); + registry.template get(HIST("hPropagation"))->GetXaxis()->SetBinLabel(3, "Propagation OK"); } + /// Legacy overload for callers still holding a StandardCCDBLoader; forwards the two + /// run-scoped values actually used. Prefer the overload below, which lets the caller + /// source them from CCDB columns instead of a CCDB query. template void fillTrackTables(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, TCCDBLoader const& ccdbLoader, TCollisions const& collisions, TTracks const& tracks, TOutputGroup& cursors, THistoRegistry& registry) + { + fillTrackTables(cGroup, trackTunerObj, ccdbLoader.runNumber, ccdbLoader.mMeanVtx, collisions, tracks, cursors, registry); + } + + /// Takes the run-scoped conditions it actually needs (run number for the TrackTuner + /// path, mean vertex for the DCA reference) rather than a CCDB loader object, so that + /// callers are free to source them from CCDB columns instead of a CCDB query. + template + void fillTrackTables(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, int currentRunNumber, o2::dataformats::MeanVertexObject const* meanVtx, TCollisions const& collisions, TTracks const& tracks, TOutputGroup& cursors, THistoRegistry& registry) + { + fillTrackTables(cGroup, trackTunerObj, currentRunNumber, meanVtx, nullptr, nullptr, collisions, tracks, cursors, registry); + } + + /// As above, plus the TrackTuner calibration lists taken from the + /// aod::TrackTunerCCDBObjects columns. When they are given, the run-range table that + /// getPathInputFileAutomaticFromCCDB() would have walked has already been applied by + /// the CCDB fetcher, so no CCDB query happens here at all. + template + void fillTrackTables(TConfigurableGroup const& cGroup, TrackTuner& trackTunerObj, int currentRunNumber, o2::dataformats::MeanVertexObject const* meanVtx, TList* dcaCalib, TList* qOverPtCalib, TCollisions const& collisions, TTracks const& tracks, TOutputGroup& cursors, THistoRegistry& registry) { /// retrieve the TrackTuner calibration graphs *if not done yet* /// i.e. if autodetect is required - if (cGroup.useTrackTuner.value && autoDetectDcaCalib && !trackTunerObj.areGraphsConfigured) { + if (cGroup.useTrackTuner.value && !trackTunerObj.areGraphsConfigured && (autoDetectDcaCalib || dcaCalib != nullptr)) { - /// get the run number from the ccdb loader, already initialized - const int runNumber = ccdbLoader.runNumber; - trackTunerObj.setRunNumber(runNumber); + trackTunerObj.setRunNumber(currentRunNumber); - /// setup the "auto-detected" path based on the run number - trackTunerObj.getPathInputFileAutomaticFromCCDB(); - trackTunedTracks->SetTitle(trackTunerObj.outputString.c_str()); + if (dcaCalib != nullptr) { + /// the path was resolved by the CCDB fetcher from the column's run-range mapping + trackTunedTracks->SetTitle(trackTunerObj.outputString.c_str()); + trackTunerObj.getDcaGraphs(dcaCalib, qOverPtCalib); + } else { + /// setup the "auto-detected" path based on the run number + trackTunerObj.getPathInputFileAutomaticFromCCDB(); + trackTunedTracks->SetTitle(trackTunerObj.outputString.c_str()); - /// now that the path is ok, retrieve the graphs - trackTunerObj.getDcaGraphs(); + /// now that the path is ok, retrieve the graphs + trackTunerObj.getDcaGraphs(); + } } if (!fillTracks) { return; // suppress everything } + // Reserve every cursor filled in the loop below: reserve() switches the + // cursor to the unsafe (no bounds check) append, so a cursor filled + // without it pays the safe per-row append. The Par/ParExtension/DCA + // tables are filled in the covariance branch as well. + cursors.tracksParPropagated.reserve(tracks.size()); + cursors.tracksParExtensionPropagated.reserve(tracks.size()); + if (fillTracksDCA) { + cursors.tracksDCA.reserve(tracks.size()); + } if (fillTracksCov) { cursors.tracksParCovPropagated.reserve(tracks.size()); cursors.tracksParCovExtensionPropagated.reserve(tracks.size()); if (fillTracksDCACov) { cursors.tracksDCACov.reserve(tracks.size()); } - } else { - cursors.tracksParPropagated.reserve(tracks.size()); - cursors.tracksParExtensionPropagated.reserve(tracks.size()); - if (fillTracksDCA) { - cursors.tracksDCA.reserve(tracks.size()); - } + } + if (cGroup.useTrackTuner.value && cGroup.fillTrackTunerTable.value) { + cursors.tunertable.reserve(tracks.size()); } for (const auto& track : tracks) { @@ -268,11 +316,12 @@ class TrackPropagationModule } } // auto trackParCov = getTrackParCov(track); - o2::aod::track::TrackTypeEnum trackType = (o2::aod::track::TrackTypeEnum)track.trackType(); + o2::aod::track::TrackTypeEnum trackType{track.trackType()}; // std::array trackPxPyPz; // std::array trackPxPyPzTuned = {0.0, 0.0, 0.0}; double q2OverPtNew = -9999.; // Only propagate tracks which have passed the innermost wall of the TPC (e.g. skipping loopers etc). Others fill unpropagated. + registry.fill(HIST("hPropagation"), 0.5); if (track.trackType() == o2::aod::track::TrackIU && track.x() < cGroup.minPropagationRadius.value) { if (fillTracksCov) { if constexpr (isMc) { // checking MC and fillCovMat block begins @@ -289,6 +338,7 @@ class TrackPropagationModule } // MC and fillCovMat block ends } bool isPropagationOK = true; + registry.fill(HIST("hPropagation"), 1.5); if (track.has_collision()) { auto const& collision = collisions.rawIteratorAt(track.collisionId()); @@ -301,15 +351,16 @@ class TrackPropagationModule } } else { if (fillTracksCov) { - mVtx.setPos({ccdbLoader.mMeanVtx->getX(), ccdbLoader.mMeanVtx->getY(), ccdbLoader.mMeanVtx->getZ()}); - mVtx.setCov(ccdbLoader.mMeanVtx->getSigmaX() * ccdbLoader.mMeanVtx->getSigmaX(), 0.0f, ccdbLoader.mMeanVtx->getSigmaY() * ccdbLoader.mMeanVtx->getSigmaY(), 0.0f, 0.0f, ccdbLoader.mMeanVtx->getSigmaZ() * ccdbLoader.mMeanVtx->getSigmaZ()); + mVtx.setPos({meanVtx->getX(), meanVtx->getY(), meanVtx->getZ()}); + mVtx.setCov(meanVtx->getSigmaX() * meanVtx->getSigmaX(), 0.0f, meanVtx->getSigmaY() * meanVtx->getSigmaY(), 0.0f, 0.0f, meanVtx->getSigmaZ() * meanVtx->getSigmaZ()); isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, mTrackParCov, 2.f, matCorr, &mDcaInfoCov); } else { - isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz({ccdbLoader.mMeanVtx->getX(), ccdbLoader.mMeanVtx->getY(), ccdbLoader.mMeanVtx->getZ()}, mTrackPar, 2.f, matCorr, &mDcaInfo); + isPropagationOK = o2::base::Propagator::Instance()->propagateToDCABxByBz({meanVtx->getX(), meanVtx->getY(), meanVtx->getZ()}, mTrackPar, 2.f, matCorr, &mDcaInfo); } } if (isPropagationOK) { trackType = o2::aod::track::Track; + registry.fill(HIST("hPropagation"), 2.5); } // filling some QA histograms for track tuner test purpose if (fillTracksCov) { diff --git a/Common/Tools/TrackTuner.h b/Common/Tools/TrackTuner.h index a9c90ab828d..48ed49cdab8 100644 --- a/Common/Tools/TrackTuner.h +++ b/Common/Tools/TrackTuner.h @@ -19,7 +19,6 @@ #define COMMON_TOOLS_TRACKTUNER_H_ #include -#include #include #include #include @@ -86,10 +85,10 @@ struct TrackTuner : o2::framework::ConfigurableGroup { bool updateCurvatureIU = false; // To update the track parameter Q/Pt in trackIU table, particularly used for V0 mass width dependence on Q/Pt bool updatePulls = false; bool isInputFileFromCCDB = false; // query input file from CCDB or local folder - std::string pathInputFile = ""; // Path to file containing DCAxy, DCAz graphs from data and MC - std::string nameInputFile = ""; // Name of the file containing DCAxy, DCAz graphs from data and MC - std::string pathFileQoverPt = ""; // Path to file containing Q/Pt correction graphs from data and MC (only one proxy provided, i.e. D0 sigma graphs from data and MC) - std::string nameFileQoverPt = ""; // file name containing Q/Pt correction graphs from data and MC + std::string pathInputFile{}; // Path to file containing DCAxy, DCAz graphs from data and MC + std::string nameInputFile{}; // Name of the file containing DCAxy, DCAz graphs from data and MC + std::string pathFileQoverPt{}; // Path to file containing Q/Pt correction graphs from data and MC (only one proxy provided, i.e. D0 sigma graphs from data and MC) + std::string nameFileQoverPt{}; // file name containing Q/Pt correction graphs from data and MC bool usePvRefitCorrections = false; // establish whether to use corrections obtained with or w/o PV refit float qOverPtMC = -1.; // 1/pt MC float qOverPtData = -1.; // 1/pt data @@ -100,7 +99,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { int nPhiBins = 1; int runNumber = 0; // first run number considered in analysis (useful only if autoDetectDcaCalib = true) bool areGraphsConfigured = false; - std::string outputString = ""; + std::string outputString{}; std::map metadata; @@ -192,6 +191,10 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 559348 <= runNumber && runNumber <= 559387 +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 7]: pp, 5.36 TeV 2024, period LHC24aq: CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 559408 <= runNumber && runNumber <= 559456 +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 8]: pp, 13.6 TeV 2025, 2026, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ [CASE 9]: p-O, 9.62 TeV 2025, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pO +++"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++ Run list: 564250 <= runNumber <= 564297 +++"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"; LOG(info) << ""; @@ -253,6 +256,22 @@ struct TrackTuner : o2::framework::ConfigurableGroup { pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pp2024/ppRef/polarity_negative"; LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> pp, 5.36 TeV 2024, period LHC24aq: CCDB path " << pathInputFile; LOG(info) << " >>> Run list: 559408 <= runNumber && runNumber <= 559456"; + } else if ((563182 <= runNumber && runNumber <= 567592) || (569945 <= runNumber && runNumber <= 571787)) { + /// + /// [CASE 8]: pp, 13.6 TeV 2025, 2026, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi + /// Run list: 563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026) + /// + pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pp2026/pass1/vsPhi"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> pp, 13.6 TeV 2025, 2026, CCDB path " << pathInputFile; + LOG(info) << " >>> Run list: (563182 <= runNumber <= 567592 (2025)) || (569945 <= runNumber <= 571787 (2026)) "; + } else if (564250 <= runNumber && runNumber <= 564297) { + /// + /// [CASE 9]: p-O, 9.62 TeV 2025, CCDB path Users/m/mfaggin/test/inputsTrackTuner/pO + /// Run list: 564250 <= runNumber <= 564297 + /// + pathInputFile = "Users/m/mfaggin/test/inputsTrackTuner/pO"; + LOG(info) << "[TrackTuner::getPathInputFileAutomaticFromCCDB]: >>> p-O, 9.62 TeV 2025, CCDB path " << pathInputFile; + LOG(info) << " >>> Run list: 564250 <= runNumber <= 564297 "; } else { LOG(fatal) << "runNumber " << runNumber << " not supported for the autodetection. Please switch to manual configuration of the TrackTuner object. Aborting..."; } @@ -371,8 +390,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner]"; LOG(info) << "[TrackTuner] >>> String slices:"; - for (const std::string& s : slices) + for (const std::string& s : slices) { LOG(info) << "[TrackTuner] " << s; + } /// check if the number of input parameters is correct if (static_cast(slices.size()) != NPars) { @@ -390,7 +410,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // parameter not found LOG(fatal) << "\"" << mapParNames[iPar] << "\" not found in the configuration string"; } - std::string str = *it; + const std::string& str = *it; if (str.find('=') == std::string::npos || str.back() == '=') { // value of the parameter missing in the configuration string LOG(fatal) << "Missing value for \"" << mapParNames[iPar] << "\" in the configuration string"; @@ -400,9 +420,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { /// further lambda expression to handle bool initialization auto setBoolFromString = [=](bool& b, const std::string& str) { - if (!str.compare("1") || str.find("true") != std::string::npos || str.find("True") != std::string::npos || str.find("TRUE") != std::string::npos) { + if (str == "1" || str.find("true") != std::string::npos || str.find("True") != std::string::npos || str.find("TRUE") != std::string::npos) { b = true; - } else if (!str.compare("0") || str.find("false") != std::string::npos || str.find("False") != std::string::npos || str.find("FALSE") != std::string::npos) { + } else if (str == "0" || str.find("false") != std::string::npos || str.find("False") != std::string::npos || str.find("FALSE") != std::string::npos) { b = false; } else { LOG(fatal) << "[TrackTuner] Wrong bool initialization from configuration "; @@ -413,36 +433,36 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] >>> Parameters after the custom settings"; // Configure autoDetectDcaCalib setBoolFromString(autoDetectDcaCalib, getValueString(AutoDetectDcaCalib)); - outputString += "autoDetectDcaCalib=" + std::to_string(autoDetectDcaCalib); + outputString += "autoDetectDcaCalib=" + std::to_string(static_cast(autoDetectDcaCalib)); LOG(info) << "[TrackTuner] autoDetectDcaCalib = " << autoDetectDcaCalib; // Configure debugInfo setBoolFromString(debugInfo, getValueString(DebugInfo)); LOG(info) << "[TrackTuner] debugInfo = " << debugInfo; - outputString += ", debugInfo=" + std::to_string(debugInfo); + outputString += ", debugInfo=" + std::to_string(static_cast(debugInfo)); // Configure updateTrackDCAs setBoolFromString(updateTrackDCAs, getValueString(UpdateTrackDCAs)); LOG(info) << "[TrackTuner] updateTrackDCAs = " << updateTrackDCAs; - outputString += ", updateTrackDCAs=" + std::to_string(updateTrackDCAs); + outputString += ", updateTrackDCAs=" + std::to_string(static_cast(updateTrackDCAs)); // Configure updateTrackCovMat setBoolFromString(updateTrackCovMat, getValueString(UpdateTrackCovMat)); LOG(info) << "[TrackTuner] updateTrackCovMat = " << updateTrackCovMat; - outputString += ", updateTrackCovMat=" + std::to_string(updateTrackCovMat); + outputString += ", updateTrackCovMat=" + std::to_string(static_cast(updateTrackCovMat)); // Configure updateCurvature setBoolFromString(updateCurvature, getValueString(UpdateCurvature)); LOG(info) << "[TrackTuner] updateCurvature = " << updateCurvature; - outputString += ", updateCurvature=" + std::to_string(updateCurvature); + outputString += ", updateCurvature=" + std::to_string(static_cast(updateCurvature)); // Configure updateCurvatureIU setBoolFromString(updateCurvatureIU, getValueString(UpdateCurvatureIU)); LOG(info) << "[TrackTuner] updateCurvatureIU = " << updateCurvatureIU; - outputString += ", updateCurvatureIU=" + std::to_string(updateCurvatureIU); + outputString += ", updateCurvatureIU=" + std::to_string(static_cast(updateCurvatureIU)); // Configure updatePulls setBoolFromString(updatePulls, getValueString(UpdatePulls)); LOG(info) << "[TrackTuner] updatePulls = " << updatePulls; - outputString += ", updatePulls=" + std::to_string(updatePulls); + outputString += ", updatePulls=" + std::to_string(static_cast(updatePulls)); // Configure isInputFileFromCCDB setBoolFromString(isInputFileFromCCDB, getValueString(IsInputFileFromCCDB)); LOG(info) << "[TrackTuner] isInputFileFromCCDB = " << isInputFileFromCCDB; - outputString += ", isInputFileFromCCDB=" + std::to_string(isInputFileFromCCDB); + outputString += ", isInputFileFromCCDB=" + std::to_string(static_cast(isInputFileFromCCDB)); // Configure pathInputFile pathFileQoverPt = getValueString(PathFileQoverPt); outputString += ", pathFileQoverPt=" + pathFileQoverPt; @@ -457,7 +477,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] nameFileQoverPt = " << nameFileQoverPt; // Configure usePvRefitCorrections setBoolFromString(usePvRefitCorrections, getValueString(UsePvRefitCorrections)); - outputString += ", usePvRefitCorrections=" + std::to_string(usePvRefitCorrections); + outputString += ", usePvRefitCorrections=" + std::to_string(static_cast(usePvRefitCorrections)); LOG(info) << "[TrackTuner] usePvRefitCorrections = " << usePvRefitCorrections; // Configure qOverPtMC qOverPtMC = std::stof(getValueString(QOverPtMC)); @@ -470,8 +490,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // Configure nPhiBins nPhiBins = std::stoi(getValueString(NPhiBins)); outputString += ", nPhiBins=" + std::to_string(nPhiBins); - if (nPhiBins < 0) + if (nPhiBins < 0) { LOG(fatal) << "[TrackTuner] negative nPhiBins!" << nPhiBins; + } LOG(info) << "[TrackTuner] nPhiBins = " << nPhiBins; // Configure pathInputFile if (!autoDetectDcaCalib) { @@ -506,36 +527,36 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] >>> Parameters after the custom settings"; // Configure autoDetectDcaCalib autoDetectDcaCalib = cfgAutoDetectDcaCalib; - outputString += "autoDetectDcaCalib=" + std::to_string(autoDetectDcaCalib); + outputString += "autoDetectDcaCalib=" + std::to_string(static_cast(autoDetectDcaCalib)); LOG(info) << "[TrackTuner] autoDetectDcaCalib = " << autoDetectDcaCalib; // Configure debugInfo debugInfo = cfgDebugInfo; LOG(info) << "[TrackTuner] debugInfo = " << debugInfo; - outputString += ", debugInfo=" + std::to_string(debugInfo); + outputString += ", debugInfo=" + std::to_string(static_cast(debugInfo)); // Configure updateTrackDCAs updateTrackDCAs = cfgUpdateTrackDCAs; LOG(info) << "[TrackTuner] updateTrackDCAs = " << updateTrackDCAs; - outputString += ", updateTrackDCAs=" + std::to_string(updateTrackDCAs); + outputString += ", updateTrackDCAs=" + std::to_string(static_cast(updateTrackDCAs)); // Configure updateTrackCovMat updateTrackCovMat = cfgUpdateTrackCovMat; LOG(info) << "[TrackTuner] updateTrackCovMat = " << updateTrackCovMat; - outputString += ", updateTrackCovMat=" + std::to_string(updateTrackCovMat); + outputString += ", updateTrackCovMat=" + std::to_string(static_cast(updateTrackCovMat)); // Configure updateCurvature updateCurvature = cfgUpdateCurvature; LOG(info) << "[TrackTuner] updateCurvature = " << updateCurvature; - outputString += ", updateCurvature=" + std::to_string(updateCurvature); + outputString += ", updateCurvature=" + std::to_string(static_cast(updateCurvature)); // Configure updateCurvatureIU updateCurvatureIU = cfgUpdateCurvatureIU; LOG(info) << "[TrackTuner] updateCurvatureIU = " << updateCurvatureIU; - outputString += ", updateCurvatureIU=" + std::to_string(updateCurvatureIU); + outputString += ", updateCurvatureIU=" + std::to_string(static_cast(updateCurvatureIU)); // Configure updatePulls updatePulls = cfgUpdatePulls; LOG(info) << "[TrackTuner] updatePulls = " << updatePulls; - outputString += ", updatePulls=" + std::to_string(updatePulls); + outputString += ", updatePulls=" + std::to_string(static_cast(updatePulls)); // Configure isInputFileFromCCDB isInputFileFromCCDB = cfgIsInputFileFromCCDB; LOG(info) << "[TrackTuner] isInputFileFromCCDB = " << isInputFileFromCCDB; - outputString += ", isInputFileFromCCDB=" + std::to_string(isInputFileFromCCDB); + outputString += ", isInputFileFromCCDB=" + std::to_string(static_cast(isInputFileFromCCDB)); // Configure pathQoverPt pathFileQoverPt = cfgPathFileQoverPt; outputString += ", pathFileQoverPt=" + pathFileQoverPt; @@ -550,7 +571,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { LOG(info) << "[TrackTuner] nameFileQoverPt = " << nameFileQoverPt; // Configure usePvRefitCorrections usePvRefitCorrections = cfgUsePvRefitCorrections; - outputString += ", usePvRefitCorrections=" + std::to_string(usePvRefitCorrections); + outputString += ", usePvRefitCorrections=" + std::to_string(static_cast(usePvRefitCorrections)); LOG(info) << "[TrackTuner] usePvRefitCorrections = " << usePvRefitCorrections; // Configure qOverPtMC qOverPtMC = cfgQOverPtMC; @@ -563,8 +584,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // Configure nPhiBins nPhiBins = cfgNPhiBins; outputString += ", nPhiBins=" + std::to_string(nPhiBins); - if (nPhiBins < 0) + if (nPhiBins < 0) { LOG(fatal) << "[TrackTuner] negative nPhiBins!" << nPhiBins; + } LOG(info) << "[TrackTuner] nPhiBins = " << nPhiBins; // Configure pathInputFile if (!autoDetectDcaCalib) { @@ -597,9 +619,6 @@ struct TrackTuner : o2::framework::ConfigurableGroup { TList* ccdb_object_dca = nullptr; TList* ccdb_object_qoverpt = nullptr; - std::string grOneOverPtPionNameMC = "sigmaVsPtMc"; - std::string grOneOverPtPionNameData = "sigmaVsPtData"; - if (isInputFileFromCCDB) { /// use input correction file from CCDB @@ -624,25 +643,43 @@ struct TrackTuner : o2::framework::ConfigurableGroup { /// open the input correction file - q/pt correction TFile* inputFileQoverPt = TFile::Open(fullNameFileQoverPt.c_str(), "READ"); - if (!inputFileQoverPt && (updateCurvature || updateCurvatureIU)) { - LOG(fatal) << "Something wrong with the Q/Pt input file" << fullNameFileQoverPt << " for Q/Pt correction. Fix it!"; + if (!inputFileQoverPt) { + if (updateCurvature || updateCurvatureIU) { + LOG(fatal) << "Something wrong with the Q/Pt input file" << fullNameFileQoverPt << " for Q/Pt correction. Fix it!"; + } + } else { + ccdb_object_qoverpt = dynamic_cast(inputFileQoverPt->Get("ccdb_object")); } - ccdb_object_qoverpt = dynamic_cast(inputFileQoverPt->Get("ccdb_object")); } + getDcaGraphs(ccdb_object_dca, ccdb_object_qoverpt); + } + + /// \brief Builds the correction graphs from lists obtained elsewhere, typically straight + /// from the aod::TrackTunerCCDBObjects columns, so no CCDB client is involved. + void getDcaGraphs(TList* ccdb_object_dca, TList* ccdb_object_qoverpt) + { + /// abort if the graphs were already loaded + if (areGraphsConfigured) { + LOG(fatal) << "[TrackTuner::getDcaGraphs()] Function already called, i.e. the calibrations are already loaded. This further call should never happen. Aborting..."; + } + std::string grOneOverPtPionNameMC = "sigmaVsPtMc"; + std::string grOneOverPtPionNameData = "sigmaVsPtData"; + // choose wheter to use corrections w/ PV refit or w/o it, and retrieve the proper TList std::string dir = "woPvRefit"; if (usePvRefitCorrections) { dir = "withPvRefit"; } - TList* td = dynamic_cast(ccdb_object_dca->FindObject(dir.c_str())); + auto td = dynamic_cast(ccdb_object_dca->FindObject(dir.c_str())); if (!td) { LOG(fatal) << "[TrackTuner] TList " << td << " not found in ccdb_object_dca. Fix it!"; } int inputNphiBins = nPhiBins; - if (inputNphiBins == 0) + if (inputNphiBins == 0) { nPhiBins = 1; // old phi_independent settings + } // reserve memory and initialize vector for needed number of graphs grDcaXYResVsPtPionMC.resize(nPhiBins); @@ -744,8 +781,9 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // get phibin double phiMC = mcparticle.phi(); - if (phiMC < 0.) + if (phiMC < 0.) { phiMC += o2::constants::math::TwoPI; // 2 * std::numbers::pi;// + } int phiBin = phiMC / (o2::constants::math::TwoPI + 0.0000001) * nPhiBins; // 0.0000001 just a numerical protection dcaXYResMC = evalGraph(ptMC, grDcaXYResVsPtPionMC[phiBin].get()); @@ -940,7 +978,7 @@ struct TrackTuner : o2::framework::ConfigurableGroup { // double dd0mrpn=std::abs(sd0mrpn)-std::abs(sd0mrpo); // double deltaDcaXYmean = std::abs(dcaXYMeanData) - std::abs(dcaXYMeanMC) ; - double deltaDcaXYmean = dcaXYMeanData - dcaXYMeanMC; + double deltaDcaXYmean = dcaXYMeanData; // - dcaXYMeanMC; // Remove the shift of the mean, already introduced in deltaDcaXYTuned // double d0rpn =d0rpmc+dd0rpn-dd0mrpn; double trackParDcaXYTuned = trackParDcaXYMC + deltaDcaXYTuned + deltaDcaXYmean; @@ -1181,10 +1219,12 @@ struct TrackTuner : o2::framework::ConfigurableGroup { int nPoints = graph->GetN(); double xMin = graph->GetX()[0]; double xMax = graph->GetX()[nPoints - 1]; - if (x > xMax) + if (x > xMax) { return graph->Eval(xMax); - if (x < xMin) + } + if (x < xMin) { return graph->Eval(xMin); + } return graph->Eval(x); } }; diff --git a/Common/Tools/timestampModule.h b/Common/Tools/timestampModule.h index 03981beccaf..2ecc7e8148a 100644 --- a/Common/Tools/timestampModule.h +++ b/Common/Tools/timestampModule.h @@ -9,6 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file timestampModule.h +/// \brief Timestamp module +/// \author ALICE + #ifndef COMMON_TOOLS_TIMESTAMPMODULE_H_ #define COMMON_TOOLS_TIMESTAMPMODULE_H_ @@ -32,12 +36,11 @@ namespace timestamp { // timestamp configurables -struct timestampConfigurables : o2::framework::ConfigurableGroup { +struct TimestampConfigurables : o2::framework::ConfigurableGroup { std::string prefix = "timestamp"; o2::framework::Configurable verbose{"verbose", false, "verbose mode"}; o2::framework::Configurable fatalOnInvalidTimestamp{"fatalOnInvalidTimestamp", false, "Generate fatal error for invalid timestamps"}; - o2::framework::Configurable rct_path{"rct-path", "RCT/Info/RunInformation", "path to the ccdb RCT objects for the SOR timestamps"}; - o2::framework::Configurable orbit_reset_path{"orbit-reset-path", "CTP/Calib/OrbitReset", "path to the ccdb orbit-reset objects"}; + o2::framework::Configurable orbitResetPath{"orbit-reset-path", "CTP/Calib/OrbitReset", "path to the ccdb orbit-reset objects"}; // o2-linter: disable=name/configurable (temporary fix) o2::framework::Configurable isRun2MC{"isRun2MC", -1, "Running mode: enable only for Run 2 MC. Timestamps are set to SOR timestamp. Default: -1 (autoset from metadata) 0 (Standard) 1 (Run 2 MC)"}; // o2-linter: disable=name/configurable (temporary fix) }; @@ -57,7 +60,7 @@ class TimestampModule orbitResetTimestamp = 0; }; - o2::common::timestamp::timestampConfigurables timestampOpts; + o2::common::timestamp::TimestampConfigurables timestampOpts; // objects necessary during processing std::map mapRunToOrbitReset; /// Cache of orbit reset timestamps @@ -104,22 +107,24 @@ class TimestampModule // clear cache to prevent interference with orbit reset queries from other code // FIXME this should not have been a problem, to be investigated - ccdb->clearCache(timestampOpts.orbit_reset_path.value.data()); + ccdb->clearCache(timestampOpts.orbitResetPath.value.data()); - const bool isUnanchoredRun3MC = runNumber >= 300000 && runNumber < 500000; + const int maxRunNumberUnanchored = 499999; + const int minRunNumberUnanchored = 300000; + const bool isUnanchoredRun3MC = runNumber >= minRunNumberUnanchored && runNumber <= maxRunNumberUnanchored; if (timestampOpts.isRun2MC.value == 1 || isUnanchoredRun3MC) { // isRun2MC: bc/orbit distributions are not simulated in Run2 MC. All bcs are set to 0. // isUnanchoredRun3MC: assuming orbit-reset is done in the beginning of each run // Setting orbit-reset timestamp to start-of-run timestamp - orbitResetTimestamp = sorTimestamp * 1000; // from ms to us - } else if (runNumber < 300000) { // Run 2 + orbitResetTimestamp = sorTimestamp * 1000; // from ms to us + } else if (runNumber < minRunNumberUnanchored) { // Run 2 LOGF(debug, "Getting orbit-reset timestamp using start-of-run timestamp from CCDB"); - auto ctp = ccdb->template getSpecific>(timestampOpts.orbit_reset_path.value.data(), sorTimestamp); + auto ctp = ccdb->template getSpecific>(timestampOpts.orbitResetPath.value.data(), sorTimestamp); orbitResetTimestamp = (*ctp)[0]; } else { // sometimes orbit is reset after SOR. Using EOR timestamps for orbitReset query is more reliable LOGF(debug, "Getting orbit-reset timestamp using end-of-run timestamp from CCDB"); - auto ctp = ccdb->template getSpecific>(timestampOpts.orbit_reset_path.value.data(), eorTimestamp / 2 + sorTimestamp / 2); + auto ctp = ccdb->template getSpecific>(timestampOpts.orbitResetPath.value.data(), eorTimestamp / 2 + sorTimestamp / 2); orbitResetTimestamp = (*ctp)[0]; } @@ -136,7 +141,7 @@ class TimestampModule if (timestampOpts.verbose) { LOGF(info, "Orbit-reset timestamp for run number %i found: %llu us", runNumber, orbitResetTimestamp); } - int64_t timestamp{(orbitResetTimestamp + int64_t(bc.globalBC() * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000}; // us -> ms + int64_t timestamp{(orbitResetTimestamp + static_cast(bc.globalBC() * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000}; // us -> ms if (timestamp < runDuration.first || timestamp > runDuration.second) { if (timestampOpts.fatalOnInvalidTimestamp.value) { LOGF(fatal, "Timestamp %llu us is out of run duration [%llu, %llu] ms", timestamp, runDuration.first, runDuration.second); diff --git a/Common/Tools/trackSelectionRequestLinkDef.h b/Common/Tools/trackSelectionRequestLinkDef.h index ae9e95b415b..93b201ce15a 100644 --- a/Common/Tools/trackSelectionRequestLinkDef.h +++ b/Common/Tools/trackSelectionRequestLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/DPG/Tasks/AOTEvent/CMakeLists.txt b/DPG/Tasks/AOTEvent/CMakeLists.txt index 59e049e6356..202543fa7c2 100644 --- a/DPG/Tasks/AOTEvent/CMakeLists.txt +++ b/DPG/Tasks/AOTEvent/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(event-selection-qa SOURCES eventSelectionQa.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::DetectorsBase O2::DataFormatsITSMFT + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::DetectorsBase O2::DataFormatsITSMFT O2::TPCBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lumi-qa diff --git a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx index a79bd82a5c3..9da260edcec 100644 --- a/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx +++ b/DPG/Tasks/AOTEvent/detectorOccupancyQa.cxx @@ -162,7 +162,7 @@ struct DetectorOccupancyQaTask { } // track QA counters histos.add("nTrackCounter_after_cuts_QA", "", kTH1D, {{12, -0.5, 11.5, "track QA"}}); - TAxis* axTrackCounters = reinterpret_cast(histos.get(HIST("nTrackCounter_after_cuts_QA"))->GetXaxis()); + TAxis* axTrackCounters = histos.get(HIST("nTrackCounter_after_cuts_QA"))->GetXaxis(); axTrackCounters->SetBinLabel(1, "all"); axTrackCounters->SetBinLabel(2, "PVcontrib"); axTrackCounters->SetBinLabel(3, "ptCut"); @@ -779,8 +779,8 @@ struct DetectorOccupancyQaTask { if (!col.selection_bit(kNoITSROFrameBorder)) continue; - std::vector vCollsAssocToGivenColl = vCollsInTimeWin[colIndex]; - std::vector vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; + const std::vector& vCollsAssocToGivenColl = vCollsInTimeWin[colIndex]; + const std::vector& vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; LOGP(debug, " >> vCollsAssocToGivenColl.size={}", vCollsAssocToGivenColl.size()); @@ -1087,7 +1087,6 @@ struct DetectorOccupancyQaTask { // July 2025: more for data vs MC: if (track.hasTPC() && occupancy >= 0 && confAddBasicQAhistos) { float pt = track.pt(); - // pt 0.2-0.5 if (pt > 0.2 && pt < 0.5) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_02_05"), track.tpcNClsFindable(), occupancy); @@ -1098,9 +1097,7 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcNClsFound_vs_occup_pt_02_05"), track.tpcNClsFound(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcChi2NCl_vs_occup_pt_02_05"), track.tpcChi2NCl(), occupancy); } - } - // pt 0.5-1.0 - else if (pt > 0.5 && pt < 1.0) { + } else if (pt > 0.5 && pt < 1.0) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_05_10"), track.tpcNClsFindable(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFound_vs_occup_pt_05_10"), track.tpcNClsFound(), occupancy); @@ -1110,9 +1107,7 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcNClsFound_vs_occup_pt_05_10"), track.tpcNClsFound(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_above2000/tpcChi2NCl_vs_occup_pt_05_10"), track.tpcChi2NCl(), occupancy); } - } - // pt > 1.0 - else if (pt > 1.0) { + } else if (pt > 1.0) { if (nPV >= 10 && nPV < 400) { histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFindable_vs_occup_pt_above1_0"), track.tpcNClsFindable(), occupancy); histos.fill(HIST("QA_noTPCcuts/nPV_10_200/tpcNClsFound_vs_occup_pt_above1_0"), track.tpcNClsFound(), occupancy); @@ -1383,8 +1378,8 @@ struct DetectorOccupancyQaTask { int tpcNClsFound = track.tpcNClsFound(); int tpcNClsCrossedRows = track.tpcNClsCrossedRows(); - if (sign > 0) // positive tracks - { + if (sign > 0) { + // positive tracks histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPt_pos"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hEta_pos"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPhi_pos"), phi, occupancy, pt); @@ -1401,8 +1396,8 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/QA_tpcNClsFound_pos"), tpcNClsFound); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/QA_tpcNClsCrossedRows_pos"), tpcNClsCrossedRows); } - } else // negative tracks - { + } else { + // negative tracks histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPt_neg"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hEta_neg"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_10_200/kine_vs_weighted_occup/PV_hPhi_neg"), phi, occupancy, pt); @@ -1449,8 +1444,8 @@ struct DetectorOccupancyQaTask { } // end of TPC good global // July 2025: for data vs MC kine distr comparison - if (sign > 0) // positive tracks - { + if (sign > 0) { + // positive tracks histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPt_pos"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hEta_pos"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPhi_pos"), phi, occupancy, pt); @@ -1463,8 +1458,8 @@ struct DetectorOccupancyQaTask { histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/hPhi_posModifiedQA"), phi); } } - } else // negative tracks - { + } else { + // negative tracks histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPt_neg"), pt, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hEta_neg"), eta, occupancy); histos.fill(HIST("track_distr_nITStrThisEv_above_2000/kine_vs_weighted_occup/PV_hPhi_neg"), phi, occupancy, pt); diff --git a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx index b9789edaab8..eb2896efb16 100644 --- a/DPG/Tasks/AOTEvent/eventSelectionQa.cxx +++ b/DPG/Tasks/AOTEvent/eventSelectionQa.cxx @@ -16,6 +16,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" +#include "Common/Core/MetadataHelper.h" #include "Common/DataModel/EventSelection.h" #include @@ -43,6 +44,7 @@ #include #include +#include #include #include @@ -59,6 +61,8 @@ using namespace o2::framework; using namespace o2; using namespace o2::aod::evsel; +o2::common::core::MetadataHelper metadataInfo; + using BCsRun2 = soa::Join; using BCsRun3 = soa::Join; using ColEvSels = soa::Join; @@ -66,11 +70,11 @@ using FullTracksIU = soa::Join; using FullTracksIUwithLabels = soa::Join; struct EventSelectionQaTask { - Configurable isMC{"isMC", 0, "0 - data, 1 - MC"}; Configurable nGlobalBCs{"nGlobalBCs", 100000, "number of global bcs for detailed monitoring"}; Configurable isLowFlux{"isLowFlux", 1, "1 - low flux (pp, pPb), 0 - high flux (PbPb)"}; Configurable fillITSdeadStaveHists{"fillITSdeadStaveHists", 0, "0 - no, 1 - yes"}; - Configurable fillTPCnClsVsOccupancyHists{"fillTPCnClsVsOccupancyHists", 0, "0 - no, 1 - yes"}; + Configurable useTestWithTrackTools{"useTestWithTrackTools", 0, "0 - no, 1 - yes"}; // experimental + Configurable thresholdNrowsForTrackToolTest{"thresholdNrowsForTrackToolTest", 120, "threshold for Nrows for TrackTool test"}; // minimum number of TPC rows (experimental) Service ccdb; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -85,6 +89,12 @@ struct EventSelectionQaTask { int rofOffset = -1; // ITS ROF offset, in bc int rofLength = -1; // ITS ROF length, in bc + std::string strLPMProductionTag = ""; // MC production tag to be retrieved from AO2D metadata + std::string strPassName = ""; // RecoPassName (for data) or AnchorPassName (for MC) from metadata + bool isMC = false; + + o2::tpc::recohelpers::TPCSectorEdgeConfig tpcSectorEdgeConfig; // for special selection of tracks within TPC + std::bitset bcPatternA; std::bitset bcPatternC; std::bitset bcPatternB; @@ -111,9 +121,19 @@ struct EventSelectionQaTask { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - const AxisSpec axisMultV0M{1000, 0., isLowFlux ? 40000. : 40000., "V0M multiplicity"}; + isMC = metadataInfo.isMC(); + strLPMProductionTag = metadataInfo.get("LPMProductionTag"); // to extract info from ccdb by the tag + strPassName = metadataInfo.get(isMC ? "AnchorPassName" : "RecoPassName"); + + // for special selection of tracks within TPC (Oct 2026) + tpcSectorEdgeConfig.guardLowerCm = 0.5f; + tpcSectorEdgeConfig.guardUpperCm = 0.5f; + tpcSectorEdgeConfig.zMaxCm = 250.f; + tpcSectorEdgeConfig.maxSnp = 0.9f; + + const AxisSpec axisMultV0M{1000, 0., 40000., "V0M multiplicity"}; const AxisSpec axisMultV0A{1000, 0., isLowFlux ? 40000. : 200000., "V0A multiplicity"}; - const AxisSpec axisMultV0C{1000, 0., isLowFlux ? 30000. : 30000., "V0C multiplicity"}; + const AxisSpec axisMultV0C{1000, 0., 30000., "V0C multiplicity"}; const AxisSpec axisMultT0A{1000, 0., isLowFlux ? 10000. : 200000., "T0A multiplicity"}; const AxisSpec axisMultT0C{1000, 0., isLowFlux ? 2000. : 70000., "T0C multiplicity"}; const AxisSpec axisMultT0M{1000, 0., isLowFlux ? 12000. : 270000., "T0M multiplicity"}; @@ -364,35 +384,20 @@ struct EventSelectionQaTask { histos.add("occupancyQA/hOccupancyByFT0CvsByTracks", "", kTH2D, {{150, 0, 15000}, {150, 0, 150000}}); // 3D histograms: nGlobalTracks with cls567 as y-axis, V0A as x-axis: - const AxisSpec axisNtracksPV{200, -0.5, 5000 - 0.5, "n ITS PV tracks"}; - const AxisSpec axisNtracksPVTPC{160, -0.5, 4000 - 0.5, "n ITS-TPC PV tracks"}; + const AxisSpec axisNtracks{240, -0.5, 6000 - 0.5, "n ITS PV tracks"}; const AxisSpec axisNtracksTPConly{160, -0.5, 8000 - 0.5, "n TPC-only tracks"}; const AxisSpec axisMultV0AForOccup{20, 0., static_cast(200000), "mult V0A"}; const AxisSpec axisOccupancyTracks{150, 0., 15000, "occupancy (n ITS tracks weighted)"}; - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPCLooseCuts_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_NarrowDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_NarrowDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_StandardDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_StandardDeltaTimeCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPV, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_GoodITSLayersAllCut", "", kTH3F, {axisMultV0AForOccup, axisNtracksPVTPC, axisOccupancyTracks}); - // requested by TPC experts: nTPConly tracks vs occupancy - histos.add("occupancyQA/hNumTracksTPConly_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); - histos.add("occupancyQA/hNumTracksTPConlyNoITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracksTPConly, axisOccupancyTracks}); - // request from experts to add track properties vs occupancy, to compare data vs MC - if (fillTPCnClsVsOccupancyHists) { - const AxisSpec axisOccupancyForTrackQA{60, 0., 15000, "occupancy (n ITS tracks weighted)"}; - const AxisSpec axisNTPCcls{150, 0, 150, "n TPC clusters"}; - histos.add("occupancyQA/tpcNClsFound_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNClsFindable_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNClsShared_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - histos.add("occupancyQA/tpcNCrossedRows_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNTPCcls, axisOccupancyForTrackQA}); - const AxisSpec axisChi2TPC{150, 0, 15, "chi2Ncl TPC"}; - histos.add("occupancyQA/tpcChi2_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisChi2TPC, axisOccupancyForTrackQA}); + histos.add("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPVhasTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + + // requested by TPC experts: check with tracks at given NrowsThreshold + if (useTestWithTrackTools) { + histos.add("occupancyQA/hNumTracksITS_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); + histos.add("occupancyQA/hNumTracksPV_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy", "", kTH3F, {axisMultV0AForOccup, axisNtracks, axisOccupancyTracks}); } // ITS in-ROF occupancy @@ -402,7 +407,7 @@ struct EventSelectionQaTask { histos.add("occupancyQA/dEdx_vs_centr_vs_occup_narrow_p_win", "dE/dx", kTH3F, {{20, 0, 4000, "n PV tracks"}, {60, 0, 15000, "occupancy"}, {800, 0.0, 800.0, "dE/dx (a. u.)"}}); } - } + } // end of init() void processRun2( ColEvSels const& cols, @@ -568,9 +573,7 @@ struct EventSelectionQaTask { histos.fill(HIST("hV0C012vsTklCol"), nTracklets, multRingV0C012); // filling plots for accepted events - bool accepted = 0; - accepted |= !isINT1period & col.sel7(); - accepted |= isINT1period & sel1; + bool accepted = (!isINT1period && col.sel7()) || (isINT1period && sel1); if (!accepted) { continue; } @@ -622,8 +625,13 @@ struct EventSelectionQaTask { lastRun = run; int64_t tsSOR = 0; // dummy start-of-run timestamp for unanchored MC int64_t tsEOR = 1; // dummy end-of-run timestamp for unanchored MC - if (run >= 500000) { - auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run); + const int run3min = 500000; + if (run >= run3min) { + auto runInfo = (!isMC) ? o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run) + : o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), run, strLPMProductionTag); + LOGP(info, "eventSelectionQA: isMC = {}, NumberOfOrbitsPerTF extracted from AggregatedRunInfo = {}", isMC, runInfo.orbitsPerTF); + LOGP(info, "eventSelectionQA: strLPMProductionTag = {}, strPassName = {}", strLPMProductionTag, strPassName); + // first bc of the first orbit bcSOR = runInfo.orbitSOR * nBCsPerOrbit; // number of orbits per TF @@ -786,13 +794,13 @@ struct EventSelectionQaTask { const auto& bcPast = bcs.iteratorAt(bc.globalIndex() - deltaIndex); deltaBC = globalBC - bcPast.globalBC(); if (deltaBC < maxDeltaBC) { - pastActivityFT0 |= bcPast.has_ft0(); - pastActivityFV0 |= bcPast.has_fv0a(); - pastActivityFDD |= bcPast.has_fdd(); + pastActivityFT0 = pastActivityFT0 || bcPast.has_ft0(); + pastActivityFV0 = pastActivityFV0 || bcPast.has_fv0a(); + pastActivityFDD = pastActivityFDD || bcPast.has_fdd(); } } - bool pastActivity = pastActivityFT0 | pastActivityFV0 | pastActivityFDD; + bool pastActivity = pastActivityFT0 || pastActivityFV0 || pastActivityFDD; int localBC = bc.globalBC() % nBCsPerOrbit; float timeV0A = bc.has_fv0a() ? bc.fv0a().time() : -999.f; @@ -1009,8 +1017,12 @@ struct EventSelectionQaTask { // special check to avoid crashes (in particular, on some MC Pb-Pb datasets) // (related to shifts in ambiguous tracks association to bc slices (off by 1) - see https://mattermost.web.cern.ch/alice/pl/g9yaaf3tn3g4pgn7c1yex9copy - if (ambTrId >= 0 && (ambTracks.iteratorAt(ambTrId).bcIds()[0] >= bcs.size())) - continue; + // an empty slice (-1, -1) passes the upper check, so test it as well + if (ambTrId >= 0) { + const auto& ambTrack = ambTracks.iteratorAt(ambTrId); + if (!ambTrack.has_bc() || ambTrack.bcIds()[0] >= bcs.size() || ambTrack.bcIds()[1] >= bcs.size()) + continue; + } int indexBc = ambTrId < 0 ? track.collision_as().bc_as().globalIndex() : ambTracks.iteratorAt(ambTrId).bc_as().begin().globalIndex(); auto bc = bcs.iteratorAt(indexBc); @@ -1160,14 +1172,13 @@ struct EventSelectionQaTask { // count tracks of different types auto tracksGrouped = tracks.sliceBy(perCollision, colIndex); int nPV = 0; - int nTPConly = 0; - // int nTPConlyWithDeDxCut = 0; - int nTPConlyNoITS = 0; - int nContributorsAfterEtaTPCCuts = 0; - int nContributorsAfterEtaTPCLooseCuts = 0; + int nPVafterTPCcuts = 0; + int nPVhasTPC = 0; - int nTracksITS = 0; - int nTracksITSTPC = 0; + int nITS = 0; + int nITSTPC = 0; + int nITSatNTPCrowsThrsh = 0; + int nPVatNTPCrowsThrsh = 0; bool isTVX = col.selection_bit(kIsTriggerTVX); @@ -1177,53 +1188,61 @@ struct EventSelectionQaTask { for (const auto& track : tracksGrouped) { int trackBcDiff = bcDiff + track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS; - if (track.hasTPC() && std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 50 && track.tpcChi2NCl() < 4) { - nTPConly++; - // if (track.tpcSignal() > 20) - // nTPConlyWithDeDxCut++; - if (!track.hasITS()) - nTPConlyNoITS++; + // Oct 2026: test presence of track at 85 cm radius using new tools - https://github.com/AliceO2Group/AliceO2/tree/dev/Common/TrackTools + bool isTrackWithNrowsAvailable = false; + if (useTestWithTrackTools) { + const float bzKG = 5.f; + const float radiusOfInterest = 85.f; // cm + auto trackParLocal = o2::recohelpers::makeTrackParFromLocalParameters( + track.x(), + track.alpha(), + track.y(), + track.z(), + track.snp(), + track.tgl(), + track.signed1Pt()); + auto trackAt85 = o2::recohelpers::getParametersAtRadiusH(trackParLocal, radiusOfInterest, bzKG); + auto l0 = o2::tpc::recohelpers::sectorEdgeStackLinear(trackParLocal, bzKG, tpcSectorEdgeConfig); + + isTrackWithNrowsAvailable = (trackAt85.valid() && l0.nRowsAvailable > thresholdNrowsForTrackToolTest); } if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2) { if (track.hasITS()) { - nTracksITS++; + nITS++; if (track.hasTPC()) - nTracksITSTPC++; + nITSTPC++; + if (useTestWithTrackTools && isTrackWithNrowsAvailable) { + nITSatNTPCrowsThrsh++; + } } } if (!track.isPVContributor()) continue; + // high-quality contributors for ROF border QA and occupancy study + if (col.sel8() && fabs(col.posZ()) < 10) { + if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { + nPV++; + if (track.hasTPC()) { + nPVhasTPC++; + if (track.tpcNClsFound() > 50) { + nPVafterTPCcuts++; + // ROF border QA + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); + histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); + } + if (useTestWithTrackTools && isTrackWithNrowsAvailable) { + nPVatNTPCrowsThrsh++; + } + } // end of hasTPC + } + } // end of sel8 and vZ cuts + if (track.itsNCls() >= 5) vTracksITS567perColl[colIndex]++; - // high-quality contributors for ROF border QA and occupancy study - if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { - nPV++; - if (track.hasTPC()) { - nContributorsAfterEtaTPCLooseCuts++; - - if (!isLowFlux && fillTPCnClsVsOccupancyHists && col.sel8() && col.selection_bit(kNoSameBunchPileup) && fabs(col.posZ()) < 10 && occupancyByTracks >= 0) { - histos.fill(HIST("occupancyQA/tpcNClsFound_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFound(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcNClsFindable_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFindable(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcNClsShared_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsShared(), occupancyByTracks); - histos.fill(HIST("occupancyQA/tpcChi2_vs_V0A_vs_occupancy"), multV0A, track.tpcChi2NCl(), occupancyByTracks); - int tpcNClsFindableMinusCrossedRowsCorrected = track.tpcNClsFindableMinusCrossedRows(); - // correct for a buggy behaviour due to int8 and uint8 difference: - if (tpcNClsFindableMinusCrossedRowsCorrected < -70) - tpcNClsFindableMinusCrossedRowsCorrected += 256; - histos.fill(HIST("occupancyQA/tpcNCrossedRows_vs_V0A_vs_occupancy"), multV0A, track.tpcNClsFindable() - tpcNClsFindableMinusCrossedRowsCorrected, occupancyByTracks); - } - } // end of hasTPC - if (col.sel8() && fabs(col.posZ()) < 10 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { - nContributorsAfterEtaTPCCuts++; - // ROF border QA - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_nITSlayers_for_ITSTPCtracks"), localBC, track.itsNCls()); - histos.fill(HIST("ITSROFborderQA/hFoundBC_kTVX_counter_ITSTPCtracks"), localBC); - } - } if (!track.hasTPC()) histos.fill(HIST("hITStrackBcDiff"), trackBcDiff); if (track.hasTOF()) { @@ -1239,32 +1258,23 @@ struct EventSelectionQaTask { histos.fill(HIST("hSecondsTVXvsBcDif"), bc.timestamp() / 1000., trackBcDiff); } // end of track loop - histos.fill(HIST("hNcontribAfterCutsVsBcInTF"), bcInTF, nContributorsAfterEtaTPCCuts); + histos.fill(HIST("hNcontribAfterCutsVsBcInTF"), bcInTF, nPVafterTPCcuts); if (!isLowFlux && col.sel8() && col.selection_bit(kNoSameBunchPileup) && fabs(col.posZ()) < 10) { histos.fill(HIST("occupancyQA/hOccupancyByTracks"), occupancyByTracks); histos.fill(HIST("occupancyQA/hOccupancyByFT0C"), occupancyByFT0C); if (occupancyByTracks >= 0) { histos.fill(HIST("occupancyQA/hOccupancyByFT0CvsByTracks"), occupancyByTracks, occupancyByFT0C); + histos.fill(HIST("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy"), multV0A, nITS, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy"), multV0A, nITSTPC, occupancyByTracks); histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPCLooseCuts_vs_V0A_vs_occupancy"), multV0A, nContributorsAfterEtaTPCLooseCuts, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksITS_vs_V0A_vs_occupancy"), multV0A, nTracksITS, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksITSTPC_vs_V0A_vs_occupancy"), multV0A, nTracksITSTPC, occupancyByTracks); - if (col.selection_bit(kNoCollInTimeRangeNarrow)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_NarrowDeltaTimeCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - } - if (col.selection_bit(kNoCollInTimeRangeStandard)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_StandardDeltaTimeCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_StandardDeltaTimeCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); - } - if (col.selection_bit(kIsGoodITSLayersAll)) { - histos.fill(HIST("occupancyQA/hNumTracksPV_vs_V0A_vs_occupancy_GoodITSLayersAllCut"), multV0A, nPV, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy_GoodITSLayersAllCut"), multV0A, nContributorsAfterEtaTPCCuts, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPVhasTPC_vs_V0A_vs_occupancy"), multV0A, nPVhasTPC, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPVTPC_vs_V0A_vs_occupancy"), multV0A, nPVafterTPCcuts, occupancyByTracks); + + if (useTestWithTrackTools) { + histos.fill(HIST("occupancyQA/hNumTracksITS_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy"), multV0A, nITSatNTPCrowsThrsh, occupancyByTracks); + histos.fill(HIST("occupancyQA/hNumTracksPV_nTpcRowsAvailableAboveThrsh_vs_V0A_vs_occupancy"), multV0A, nPVatNTPCrowsThrsh, occupancyByTracks); } - histos.fill(HIST("occupancyQA/hNumTracksTPConly_vs_V0A_vs_occupancy"), multV0A, nTPConly, occupancyByTracks); - histos.fill(HIST("occupancyQA/hNumTracksTPConlyNoITS_vs_V0A_vs_occupancy"), multV0A, nTPConlyNoITS, occupancyByTracks); // dE/dx QA for a narrow pT bin for (const auto& track : tracksGrouped) { @@ -1272,7 +1282,7 @@ struct EventSelectionQaTask { continue; if (std::fabs(track.eta()) < 0.8 && track.pt() > 0.2 && track.itsNCls() >= 5) { float signedP = track.sign() * track.tpcInnerParam(); - if (std::fabs(signedP) > 0.38 && std::fabs(signedP) < 0.4 && track.tpcNClsFound() > 50 && track.tpcNClsCrossedRows() > 80 && track.itsChi2NCl() < 36 && track.tpcChi2NCl() < 4) { + if (std::fabs(signedP) > 0.38 && std::fabs(signedP) < 0.4 && track.tpcNClsFound() > 50) { float dEdx = track.tpcSignal(); histos.fill(HIST("occupancyQA/dEdx_vs_centr_vs_occup_narrow_p_win"), nPV, occupancyByTracks, dEdx); } @@ -1415,7 +1425,7 @@ struct EventSelectionQaTask { bool isVertexUPC = flags & dataformats::Vertex>::Flags::UPCMode; // is vertex with UPC settings // the second collision in ROF - std::vector vAssocToSameROF = vCollsInSameITSROF[colIndex]; + const std::vector& vAssocToSameROF = vCollsInSameITSROF[colIndex]; int thisColIndex = vAssocToSameROF[0]; float vZassoc = vCollVz[thisColIndex]; // vZ of the second collision in the same ROF float nPVassoc = vTracksITS567perColl[thisColIndex]; // n PV tracks of the second collision in the same ROF @@ -1504,6 +1514,7 @@ struct EventSelectionQaTask { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { + metadataInfo.initMetadata(cfgc); return WorkflowSpec{ adaptAnalysisTask(cfgc)}; } diff --git a/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx b/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx index b85b1d3b42c..3a5579ebe51 100644 --- a/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx +++ b/DPG/Tasks/AOTEvent/lightIonsEvSelQa.cxx @@ -991,9 +991,9 @@ struct LightIonsEvSelQa { // vZ diff (FT0 vs by tracks) bool badVzDiff = 0; - if (confUseDiffVzCutFromEvSel) + if (confUseDiffVzCutFromEvSel) { badVzDiff = !col.selection_bit(kIsGoodZvtxFT0vsPV); - else { // tune by hand + } else { // tune by hand float meanDiff = 0.0; // cm // O-O if (lastRunNumber == 564356) diff --git a/DPG/Tasks/AOTEvent/lumiQa.cxx b/DPG/Tasks/AOTEvent/lumiQa.cxx index f03419fff52..6402fc52769 100644 --- a/DPG/Tasks/AOTEvent/lumiQa.cxx +++ b/DPG/Tasks/AOTEvent/lumiQa.cxx @@ -162,13 +162,13 @@ struct LumiQaTask { return; } - hCalibV0A = reinterpret_cast(callst->FindObject("hCalibZeqFV0")); + hCalibV0A = dynamic_cast(callst->FindObject("hCalibZeqFV0")); if (hCalibV0A == nullptr) { LOGF(info, "hCalibZeqFV0 histogram is not available for run=%d at timestamp=%llu", runNumber, ts); return; } - hCalibT0C = reinterpret_cast(callst->FindObject("hCalibZeqFT0C")); + hCalibT0C = dynamic_cast(callst->FindObject("hCalibZeqFT0C")); if (hCalibT0C == nullptr) { LOGF(info, "hCalibZeqFT0C histogram is not available for run=%d at timestamp=%llu", runNumber, ts); return; diff --git a/DPG/Tasks/AOTEvent/rofOccupancyQa.cxx b/DPG/Tasks/AOTEvent/rofOccupancyQa.cxx index c9658dce198..40cd2429928 100644 --- a/DPG/Tasks/AOTEvent/rofOccupancyQa.cxx +++ b/DPG/Tasks/AOTEvent/rofOccupancyQa.cxx @@ -754,7 +754,7 @@ struct RofOccupancyQaTask { // LOGP(info, "#### starting new coll: bc={} bcInTF={} bcInITSROF={} rofId={}; noROFborder={}; rofOffset={} rofLength={}", vFoundGlobalBC[colIndex], bcInTF, bcInITSROF, rofId, bc.selection_bit(kNoITSROFrameBorder), rofOffset, rofLength); // LOGP(info, "#### starting new coll: bcInTF={} bcInITSROF={} rofIdInTF={}; noROFborder={}, vZ={} mult={}; rofOffset={} rofLength={}", bcInTF, bcInITSROF, rofIdInTF, bc.selection_bit(kNoITSROFrameBorder), vZ, vTracksITS567perColl[colIndex], rofOffset, rofLength); - std::vector vAssocToSameROF = vCollsInSameITSROF[colIndex]; + const std::vector& vAssocToSameROF = vCollsInSameITSROF[colIndex]; int nITS567tracksForRofVetoStrict = 0; // to veto events with other collisions in the same ITS ROF float nSumAmplFT0CforRofVetoStrict = 0; // to veto events with other collisions in the same ITS ROF // int nITS567tracksForRofVetoStandard = 0; // to veto events with other collisions in the same ITS ROF, with per-collision multiplicity above threshold @@ -836,8 +836,8 @@ struct RofOccupancyQaTask { vArrNoCollInSameRofWithCloseVz.push_back(vVzCutThisColl); continue; } - std::vector vAssocToThisCol = vCollsInTimeWin[colIndex]; - std::vector vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; + const std::vector& vAssocToThisCol = vCollsInTimeWin[colIndex]; + const std::vector& vCollsTimeDeltaWrtGivenColl = vTimeDeltaForColls[colIndex]; int nITS567tracksInFullTimeWindow = 0; int sumAmpFT0CInFullTimeWindow = 0; int nITS567tracksForVetoNarrow = 0; // to veto events with nearby collisions (narrower range) diff --git a/DPG/Tasks/AOTEvent/timeDependentQa.cxx b/DPG/Tasks/AOTEvent/timeDependentQa.cxx index 03f9850a1b5..194b9f49803 100644 --- a/DPG/Tasks/AOTEvent/timeDependentQa.cxx +++ b/DPG/Tasks/AOTEvent/timeDependentQa.cxx @@ -227,7 +227,12 @@ struct TimeDependentQaTask { histos.add("hSecondsCollisions", "", kTH1D, {axisSeconds}); histos.add("hSecondsCollisionsNoPileup", "", kTH1D, {axisSeconds}); histos.add("hSecondsIR", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVzBeforeAllCuts", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVxBeforeAllCuts", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVyBeforeAllCuts", "", kTH1D, {axisSeconds}); histos.add("hSecondsVz", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVx", "", kTH1D, {axisSeconds}); + histos.add("hSecondsVy", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0Camlp", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0CamlpByColMult", "", kTH1D, {axisSeconds}); histos.add("hSecondsFT0AamlpByColMult", "", kTH1D, {axisSeconds}); @@ -289,7 +294,7 @@ struct TimeDependentQaTask { // ### QA event selection bits int nEvSelBits = enNumEvSelBits; histos.add("hSecondsEventSelBits", "", kTH2F, {axisSecondsWideBins, {nEvSelBits, -0.5, nEvSelBits - 0.5, "Monitoring of event selection bits"}}); - TAxis* axSelBits = reinterpret_cast(histos.get(HIST("hSecondsEventSelBits"))->GetYaxis()); + TAxis* axSelBits = histos.get(HIST("hSecondsEventSelBits"))->GetYaxis(); axSelBits->SetBinLabel(1 + enCollisionsAll, "collisionsAll"); axSelBits->SetBinLabel(1 + enIsTriggerTVX, "IsTriggerTVX"); axSelBits->SetBinLabel(1 + enNoTimeFrameBorder, "NoTimeFrameBorder"); @@ -325,7 +330,7 @@ struct TimeDependentQaTask { // ### QA RCT flags int nRctFlagsTotal = enNumRctFlagsTotal; histos.add("hSecondsRCTflags", "", kTH2F, {axisSecondsWideBins, {nRctFlagsTotal + 2, -0.5, nRctFlagsTotal + 2 - 0.5, "Monitoring of RCT flags"}}); - axRctFlags = reinterpret_cast(histos.get(HIST("hSecondsRCTflags"))->GetYaxis()); + axRctFlags = histos.get(HIST("hSecondsRCTflags"))->GetYaxis(); axRctFlags->SetBinLabel(1, "NcollisionsSel8"); axRctFlags->SetBinLabel(2, "CcdbNotFound"); axRctFlags->SetBinLabel(3 + kCPVBad, "CPVBad"); @@ -527,6 +532,10 @@ struct TimeDependentQaTask { histos.fill(HIST("hSecondsUPCverticesBeforeAllCuts"), secFromSOR, isVertexUPC ? 1 : 0); + histos.fill(HIST("hSecondsVzBeforeAllCuts"), secFromSOR, col.posZ()); + histos.fill(HIST("hSecondsVxBeforeAllCuts"), secFromSOR, col.posX()); + histos.fill(HIST("hSecondsVyBeforeAllCuts"), secFromSOR, col.posY()); + if (std::fabs(col.posZ()) > 10) continue; @@ -546,6 +555,8 @@ struct TimeDependentQaTask { if (col.selection_bit(kNoSameBunchPileup)) histos.fill(HIST("hSecondsCollisionsNoPileup"), secFromSOR); histos.fill(HIST("hSecondsVz"), secFromSOR, col.posZ()); + histos.fill(HIST("hSecondsVx"), secFromSOR, col.posX()); + histos.fill(HIST("hSecondsVy"), secFromSOR, col.posY()); histos.fill(HIST("hSecondsFT0Camlp"), secFromSOR, bc.foundFT0().sumAmpC()); histos.fill(HIST("hSecondsFT0CamlpByColMult"), secFromSOR, col.multFT0C()); histos.fill(HIST("hSecondsFT0AamlpByColMult"), secFromSOR, col.multFT0A()); diff --git a/DPG/Tasks/AOTTrack/D0CalibTables.h b/DPG/Tasks/AOTTrack/D0CalibTables.h index 81e16201b63..f868e08d3dc 100644 --- a/DPG/Tasks/AOTTrack/D0CalibTables.h +++ b/DPG/Tasks/AOTTrack/D0CalibTables.h @@ -242,6 +242,20 @@ static const std::vector labelsPtCand = { // column labels static const std::vector labelsCutVarCand = {"delta inv. mass", "max d0d0", "max pointing angle", "max pointing angle XY", "min cos pointing angle", "min cos pointing angle XY", "min norm decay length", "min norm decay length XY", "min decay length", "min decay length XY"}; +// var indices for the cuts +enum CutsIds : uint8_t { + DeltaMass = 0, + MaxD0D0 = 1, + MaxPointingAngle = 2, + MaxPointingAngleXY = 3, + MinCosPointingAngle = 4, + MinCosPointingAngleXY = 5, + MinNormDecayLength = 6, + MinNormDecayLengthXY = 7, + MinDecayLength = 8, + MinDecayLengthXY = 9 +}; + static constexpr int NBinsPtMl = 10; // default values for the pT bin edges (can be used to configure histogram axis) // offset by 1 from the bin numbers in cuts array @@ -324,10 +338,10 @@ DECLARE_SOA_COLUMN(TpcNumSigmaPi, tpcNumSigmaPi, int8_t); //! DECLARE_SOA_COLUMN(TpcNumSigmaKa, tpcNumSigmaKa, int8_t); //! compressed NsigmaTPC for kaons DECLARE_SOA_COLUMN(TofNumSigmaPi, tofNumSigmaPi, int8_t); //! compressed NsigmaTOF for pions DECLARE_SOA_COLUMN(TofNumSigmaKa, tofNumSigmaKa, int8_t); //! compressed NsigmaTOF for kaons -DECLARE_SOA_COLUMN(ITSChi2NCl, itsChi2NCl, uint8_t); //! compressed NsigmaTOF for kaons // o2-linter: disable=name/o2-column -DECLARE_SOA_COLUMN(TPCChi2NCl, tpcChi2NCl, uint8_t); //! compressed NsigmaTOF for kaons // o2-linter: disable=name/o2-column -DECLARE_SOA_COLUMN(TRDChi2, trdChi2, uint8_t); //! compressed NsigmaTOF for kaons // o2-linter: disable=name/o2-column -DECLARE_SOA_COLUMN(TOFChi2, tofChi2, uint8_t); //! compressed NsigmaTOF for kaons // o2-linter: disable=name/o2-column +DECLARE_SOA_COLUMN(ITSChi2NCl, itsChi2NCl, uint8_t); // o2-linter: disable=name/o2-column +DECLARE_SOA_COLUMN(TPCChi2NCl, tpcChi2NCl, uint8_t); // o2-linter: disable=name/o2-column +DECLARE_SOA_COLUMN(TRDChi2, trdChi2, uint8_t); // o2-linter: disable=name/o2-column +DECLARE_SOA_COLUMN(TOFChi2, tofChi2, uint8_t); // o2-linter: disable=name/o2-column DECLARE_SOA_COLUMN(CmoPrimUnfm80, cmoPrimUnfm80, uint8_t); DECLARE_SOA_COLUMN(CmoFV0AUnfm80, cmoFV0AUnfm80, uint8_t); DECLARE_SOA_COLUMN(CmoFT0AUnfm80, cmoFT0AUnfm80, uint8_t); diff --git a/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx b/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx index 648c1258127..2b00c8a2599 100644 --- a/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx +++ b/DPG/Tasks/AOTTrack/MonitorFilterBit.cxx @@ -103,15 +103,15 @@ struct CheckFilterBit { using Tracksextension = soa::Join; using TracksextensionMC = soa::Join; SliceCache cache; - Partition positiveTPConlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition negativeTPConlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition positiveITSonlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl<(float_t)0 && o2::aod::track::itsChi2NCl>(float_t) 0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - Partition negativeITSonlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < (float_t)0 && o2::aod::track::itsChi2NCl > (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - - Partition positiveTPConlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition negativeTPConlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > (uint8_t)0 && o2::aod::track::itsChi2NCl < (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; - Partition positiveITSonlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl<(float_t)0 && o2::aod::track::itsChi2NCl>(float_t) 0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; - Partition negativeITSonlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < (float_t)0 && o2::aod::track::itsChi2NCl > (float_t)0 && o2::aod::track::trackType == (uint8_t)o2::aod::track::TrackTypeEnum::Track; // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition positiveTPConlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition negativeTPConlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition positiveITSonlyTracks = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl(0) && o2::aod::track::itsChi2NCl> static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition negativeITSonlyTracks = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < static_cast(0) && o2::aod::track::itsChi2NCl > static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + + Partition positiveTPConlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition negativeTPConlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcNClsFindable > static_cast(0) && o2::aod::track::itsChi2NCl < static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); //&& (o2::aod::track::detectorMap & o2::aod::track::TPC) ==o2::aod::track::TPC && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==0; + Partition positiveITSonlyTracksMC = o2::aod::track::signed1Pt > fzero&& o2::aod::track::tpcChi2NCl(0) && o2::aod::track::itsChi2NCl> static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 && (o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; + Partition negativeITSonlyTracksMC = o2::aod::track::signed1Pt < fzero && o2::aod::track::tpcChi2NCl < static_cast(0) && o2::aod::track::itsChi2NCl > static_cast(0) && o2::aod::track::trackType == static_cast(o2::aod::track::TrackTypeEnum::Track); // && (o2::aod::track::detectorMap & o2::aod::track::TPC) ==0 &&(o2::aod::track::detectorMap & o2::aod::track::ITS) ==o2::aod::track::ITS && o2::aod::track::passedITSNCls==true;// && o2::aod::track::itsNCls==7; void init(InitContext const&) { diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt b/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt index 96bb9ab18c4..748576f57eb 100644 --- a/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt +++ b/DPG/Tasks/AOTTrack/PID/HMPID/CMakeLists.txt @@ -17,10 +17,16 @@ o2physics_add_dpl_workflow(hmpid-qa o2physics_add_dpl_workflow(hmpid-table-producer SOURCES hmpidTableProducer.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::HMPIDBase COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(hmpid-deuteron - SOURCES hmpidDeuteron.cxx +o2physics_add_dpl_workflow(hmpid-abs-cross-section + SOURCES hmpidAbsCrossSection.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::ReconstructionDataFormats O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hmpid-cross-section-corrections + SOURCES hmpidCrossSectionCorrections.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2::ReconstructionDataFormats O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx new file mode 100644 index 00000000000..aa8a79e6bc2 --- /dev/null +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidAbsCrossSection.cxx @@ -0,0 +1,389 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \brief Analysis task for the absorption cross section using HMPID (de,pi,ka,pr) +// \authors rocco.liotino@cern.ch + +#include "tableHMPID.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct HmpidAbsCrossSection { + + HistogramRegistry registryDA{"registryDA", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; + Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; + Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; + Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; + Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; + + // Configurables for PbPb collisions + Configurable collisionsPbPb{"collisionsPbPb", false, "Require PbPb collisions"}; + Configurable centralityCut{"centralityCut", 30.0f, "centrality cut for PbPb collisions"}; + + // Configurables for PID cuts + Configurable nsigmaTPCMin{"nsigmaTPCMin", -3.0, "nsigmaTPCMin"}; + Configurable nsigmaTPCMax{"nsigmaTPCMax", +3.0, "nsigmaTPCMax"}; + Configurable nsigmaTOFMin{"nsigmaTOFMin", -3.0, "nsigmaTOFMin"}; + Configurable nsigmaTOFMax{"nsigmaTOFMax", +3.0, "nsigmaTOFMax"}; + + // Particle species selection + Configurable particleSpecies{"particleSpecies", "de", "Particle species: de (deuteron), pr (proton), pi (pion), ka (kaon)"}; + + // Configurable offset for residuals distributions + Configurable offsetX{"offsetX", 0.696712f, "Residuals offset - X direction"}; // mean value (old) + Configurable offsetY{"offsetY", 2.32849f, "Residuals offset - Y direction"}; // mean value (old) + + static constexpr float UnsetOffset = -999.f; + static constexpr int MaxOffsetBins = 35; + static constexpr int OffsetColumns = 8; + + static std::vector makeOffsetRowLabels() + { + std::vector labels; + labels.reserve(MaxOffsetBins); + for (int bin = 0; bin < MaxOffsetBins; ++bin) { + labels.emplace_back("pBin" + std::to_string(bin)); + } + return labels; + } + + Configurable offsetMomMin{"offsetMomMin", 0.5f, "Minimum momentum for residual offset correction"}; + Configurable offsetMomMax{"offsetMomMax", 4.0f, "Maximum momentum for residual offset correction"}; + Configurable offsetMomStep{"offsetMomStep", 0.5f, "Momentum bin width for residual offset correction"}; + + Configurable> residualOffsets{"residualOffsets", LabeledArray{Array2D{std::vector(MaxOffsetBins * OffsetColumns, UnsetOffset), MaxOffsetBins, OffsetColumns}, makeOffsetRowLabels(), {"xPosR2", "yPosR2", "xNegR2", "yNegR2", "xPosR4", "yPosR4", "xNegR4", "yNegR4"}}, "Residual offsets by momentum bin, chamber and charge"}; + + // Configurables for plotting + Configurable momNBins{"momNBins", 450, "Momentum bins"}; + Configurable momMin{"momMin", 0.5f, "Momentum min"}; + Configurable momMax{"momMax", 5.0f, "Momentum max"}; + + Configurable deltaRNBins{"deltaRNBins", 100, "DeltaR bins"}; + Configurable deltaRMin{"deltaRMin", 0.0f, "DeltaR min"}; + Configurable deltaRMax{"deltaRMax", 50.0f, "DeltaR max"}; + + Configurable nSigmaTPCNBins{"nSigmaTPCNBins", 100, "nSigmaTPC bins"}; + Configurable nSigmaTPCMinPlot{"nSigmaTPCMinPlot", -5.0f, "nSigmaTPC min"}; + Configurable nSigmaTPCMaxPlot{"nSigmaTPCMaxPlot", 5.0f, "nSigmaTPC max"}; + + Configurable nSigmaTOFNBins{"nSigmaTOFNBins", 100, "nSigmaTOF bins"}; + Configurable nSigmaTOFMinPlot{"nSigmaTOFMinPlot", -5.0f, "nSigmaTOF min"}; + Configurable nSigmaTOFMaxPlot{"nSigmaTOFMaxPlot", 5.0f, "nSigmaTOF max"}; + + static constexpr int Rich2 = 2, Rich4 = 4; + // static constexpr Rich0 = 0, Rich1 = 1, Rich3 = 3, Rich5 = 5, Rich6 = 6; + // static constexpr int Nch = 7; + + void init(o2::framework::InitContext const&) + { + // Axes for histograms + AxisSpec axisMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisMom"}; + AxisSpec axisDeltaR{deltaRNBins, deltaRMin, deltaRMax, "#Delta R (cm)", "axisDeltaR"}; + AxisSpec axisNsigmaTOF{nSigmaTOFNBins, nSigmaTOFMinPlot, nSigmaTOFMaxPlot, "n#sigma_{TOF}", "axisNsigmaTOF"}; + AxisSpec axisNsigmaTPC{nSigmaTPCNBins, nSigmaTPCMinPlot, nSigmaTPCMaxPlot, "n#sigma_{TPC}", "axisNsigmaTPC"}; + AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; + + // 1D integrated histograms (quick view) + registryDA.add("incoming_Pos_8cm", "incoming_Pos_8cm", HistType::kTH1F, {axisMom}); + registryDA.add("incoming_Pos_4cm", "incoming_Pos_4cm", HistType::kTH1F, {axisMom}); + + registryDA.add("DeltaR_Pos_8cm", "DeltaR_Pos_8cm", HistType::kTH1F, {axisDeltaR}); + registryDA.add("DeltaR_Pos_4cm", "DeltaR_Pos_4cm", HistType::kTH1F, {axisDeltaR}); + + registryDA.add("incoming_Neg_8cm", "incoming_Neg_8cm", HistType::kTH1F, {axisMom}); + registryDA.add("incoming_Neg_4cm", "incoming_Neg_4cm", HistType::kTH1F, {axisMom}); + + registryDA.add("DeltaR_Neg_8cm", "DeltaR_Neg_8cm", HistType::kTH1F, {axisDeltaR}); + registryDA.add("DeltaR_Neg_4cm", "DeltaR_Neg_4cm", HistType::kTH1F, {axisDeltaR}); + + // Positive candidates histograms + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm", "nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm", "nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm", "nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm", "nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // TPC only candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // after TPC+TOF cuts candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_Pos_8cm", "nSigmaTOF_vs_momHMPID_Pos_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_Pos_4cm", "nSigmaTOF_vs_momHMPID_Pos_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("DeltaR_vs_momHMPID_Pos_8cm", "DeltaR_vs_momHMPID_Pos_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); + registryDA.add("DeltaR_vs_momHMPID_Pos_4cm", "DeltaR_vs_momHMPID_Pos_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); + + // corrected residuals distributions + registryDA.add("residualsX_Vs_momentum_Pos_8cm", "residualsX_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Pos_8cm", "residualsY_Vs_momentum_Pos_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + registryDA.add("residualsX_Vs_momentum_Pos_4cm", "residualsX_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Pos_4cm", "residualsY_Vs_momentum_Pos_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + + // Negative candidates histograms + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm", "nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm", "nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm", "nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTPC}); + registryDA.add("nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm", "nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // TPC only candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm", "nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + // after TPC+TOF cuts candidates histograms + registryDA.add("nSigmaTOF_vs_momHMPID_Neg_8cm", "nSigmaTOF_vs_momHMPID_Neg_8cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + registryDA.add("nSigmaTOF_vs_momHMPID_Neg_4cm", "nSigmaTOF_vs_momHMPID_Neg_4cm", HistType::kTH2F, {axisMom, axisNsigmaTOF}); + + registryDA.add("DeltaR_vs_momHMPID_Neg_8cm", "DeltaR_vs_momHMPID_Neg_8cm", HistType::kTH2F, {axisMom, axisDeltaR}); + registryDA.add("DeltaR_vs_momHMPID_Neg_4cm", "DeltaR_vs_momHMPID_Neg_4cm", HistType::kTH2F, {axisMom, axisDeltaR}); + + // corrected residuals distributions + registryDA.add("residualsX_Vs_momentum_Neg_8cm", "residualsX_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Neg_8cm", "residualsY_Vs_momentum_Neg_8cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + registryDA.add("residualsX_Vs_momentum_Neg_4cm", "residualsX_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "x_{mip} - x_{track} (cm)"}}); + registryDA.add("residualsY_Vs_momentum_Neg_4cm", "residualsY_Vs_momentum_Neg_4cm", HistType::kTH2F, {axisMom, {400, -20.0f, 20.0f, "y_{mip} - y_{track} (cm)"}}); + + // nsigma TPC vs nsigma TOF for candidates + registryDA.add("pidTPC_TOF_p_ch_Pos", "pidTPC_TOF_p_ch_Pos", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + registryDA.add("pidTPC_TOF_p_ch_Neg", "pidTPC_TOF_p_ch_Neg", HistType::kTHnSparseF, {axisMom, axisNsigmaTPC, axisNsigmaTOF, axisChamber}); + } // end init + + void process(aod::HmpidAnalysis const& hmpidTable) + { + for (const auto& hmpid : hmpidTable) { + + // ------------------------- + // track filters + // ------------------------- + if (hmpid.itsNCluster() < minReqClusterITS) { + continue; + } + if (hmpid.tpcNCluster() < minTPCnClsFound) { + continue; + } + if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { + continue; + } + if (hmpid.tpcChi2() > maxChi2TPC) { + continue; + } + if (hmpid.itsChi2() > maxChi2ITS) { + continue; + } + if (std::abs(hmpid.dcaXY()) > maxDCAxy) { + continue; + } + if (std::abs(hmpid.dcaZ()) > maxDCAz) { + continue; + } + + // ------------------------- + // derived quantities + // ------------------------- + const float momHmpid = hmpid.momentumHmpid(); + const float momAbs = std::abs(momHmpid); + + const int chamber = static_cast(hmpid.chamber()); + const bool isPos = momHmpid > 0; + const bool isNeg = momHmpid < 0; + + // Select chambers with absorber + if (chamber != Rich2 && chamber != Rich4) { + continue; + } + + // Default residual offsets + float usedOffsetX = offsetX.value; + float usedOffsetY = offsetY.value; + + // Momentum-dependent offset correction + if (momAbs >= offsetMomMin.value && momAbs < offsetMomMax.value && (isPos || isNeg)) { + const int pBin = static_cast((momAbs - offsetMomMin.value) / offsetMomStep.value); + if (pBin >= 0 && pBin < static_cast(residualOffsets->rows())) { + const int col = (chamber == Rich2 ? 0 : 4) + (isPos ? 0 : 2); + const float binnedOffsetX = residualOffsets->get(pBin, col); + const float binnedOffsetY = residualOffsets->get(pBin, col + 1); + + if (binnedOffsetX != UnsetOffset) { + usedOffsetX = binnedOffsetX; + } + if (binnedOffsetY != UnsetOffset) { + usedOffsetY = binnedOffsetY; + } + } + } + + // Corrected residuals + const float dxCorr = (hmpid.xMip() - hmpid.xTrack()) - usedOffsetX; + const float dyCorr = (hmpid.yMip() - hmpid.yTrack()) - usedOffsetY; + const float drCorr = std::hypot(dxCorr, dyCorr); + + // check only HMPID chamber with an absorber (only RICH 2 (4cm abs) and RICH4 (8cm abs)) + if (chamber != Rich2 && chamber != Rich4) { + continue; + } + + // select particle species + + float nSigmaTPC = 0.f; + float nSigmaTOF = 0.f; + + // Select particle species + if (particleSpecies.value == "de") { + nSigmaTPC = hmpid.tpcNSigmaDe(); + nSigmaTOF = hmpid.tofNSigmaDe(); + } else if (particleSpecies.value == "pr") { + nSigmaTPC = hmpid.tpcNSigmaPr(); + nSigmaTOF = hmpid.tofNSigmaPr(); + } else if (particleSpecies.value == "pi") { + nSigmaTPC = hmpid.tpcNSigmaPi(); + nSigmaTOF = hmpid.tofNSigmaPi(); + } else if (particleSpecies.value == "ka") { + nSigmaTPC = hmpid.tpcNSigmaKa(); + nSigmaTOF = hmpid.tofNSigmaKa(); + } else { + LOGF(fatal, "Invalid particle species: %s", + particleSpecies.value.c_str()); + } + + // ------------------------- + // nsigma distributions pre-cuts + // ------------------------- + if (isPos) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_Pos"), momAbs, nSigmaTPC, nSigmaTOF, chamber); + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Pos_4cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Pos_4cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Pos_8cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Pos_8cm"), momAbs, nSigmaTOF); + } + } + if (isNeg) { + registryDA.fill(HIST("pidTPC_TOF_p_ch_Neg"), momAbs, nSigmaTPC, nSigmaTOF, chamber); + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Neg_4cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Neg_4cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_PreCut_Neg_8cm"), momAbs, nSigmaTPC); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_PreCut_Neg_8cm"), momAbs, nSigmaTOF); + } + } + + // ------------------------- + // deuteron candidate cuts - TPC + // ------------------------- + if (nSigmaTPC < nsigmaTPCMin || nSigmaTPC > nsigmaTPCMax) { + continue; + } + + // ------------------------- + // post TPC cut + // ------------------------- + if (isPos) { + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_8cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Pos_4cm"), momAbs, nSigmaTOF); + } + } + if (isNeg) { + if (chamber == Rich2) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_8cm"), momAbs, nSigmaTOF); + } + if (chamber == Rich4) { + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_TPCOnly_Neg_4cm"), momAbs, nSigmaTOF); + } + } + + // ------------------------- + // deuteron candidate cuts - TOF + // ------------------------- + if (nSigmaTOF < nsigmaTOFMin || nSigmaTOF > nsigmaTOFMax) { + continue; + } + + // Pb-Pb collisions centrality requirement + if (collisionsPbPb) { + if (hmpid.centrality() < centralityCut) { + continue; + } + } + + // Fill distributions for absorption cross section measurement + if (isPos && chamber == Rich4) { + registryDA.fill(HIST("incoming_Pos_8cm"), momAbs); + registryDA.fill(HIST("DeltaR_Pos_8cm"), drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Pos_8cm"), momAbs, nSigmaTOF); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Pos_8cm"), momAbs, drCorr); + registryDA.fill(HIST("residualsX_Vs_momentum_Pos_8cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Pos_8cm"), momAbs, dyCorr); + } + + if (isNeg && chamber == Rich4) { + registryDA.fill(HIST("incoming_Neg_8cm"), momAbs); + registryDA.fill(HIST("DeltaR_Neg_8cm"), drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Neg_8cm"), momAbs, nSigmaTOF); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Neg_8cm"), momAbs, drCorr); + + registryDA.fill(HIST("residualsX_Vs_momentum_Neg_8cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Neg_8cm"), momAbs, dyCorr); + } + + if (isPos && chamber == Rich2) { + registryDA.fill(HIST("incoming_Pos_4cm"), momAbs); + registryDA.fill(HIST("DeltaR_Pos_4cm"), drCorr); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Pos_4cm"), momAbs, drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Pos_4cm"), momAbs, nSigmaTOF); + + registryDA.fill(HIST("residualsX_Vs_momentum_Pos_4cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Pos_4cm"), momAbs, dyCorr); + } + + if (isNeg && chamber == Rich2) { + registryDA.fill(HIST("incoming_Neg_4cm"), momAbs); + registryDA.fill(HIST("DeltaR_Neg_4cm"), drCorr); + registryDA.fill(HIST("DeltaR_vs_momHMPID_Neg_4cm"), momAbs, drCorr); + registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Neg_4cm"), momAbs, nSigmaTOF); + + registryDA.fill(HIST("residualsX_Vs_momentum_Neg_4cm"), momAbs, dxCorr); + registryDA.fill(HIST("residualsY_Vs_momentum_Neg_4cm"), momAbs, dyCorr); + } + } // end of loop over hmpidTable + } // end of process +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx new file mode 100644 index 00000000000..8ca94444d60 --- /dev/null +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidCrossSectionCorrections.cxx @@ -0,0 +1,374 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// \brief Analysis task for the (anti-)deuteron absorption cross section using HMPID +// \authors rocco.liotino@cern.ch + +#include "tableHMPID.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace o2; +using namespace o2::framework; + +struct HmpidCrossSectionCorrections { + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Configurables for main track quality criteria + Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; + Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; + Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; + Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; + Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; + + // Configurables for QA selection + Configurable qMipMin{"qMipMin", 200.0f, "minimum MIP charge for QA selection"}; + Configurable qMipMax{"qMipMax", 2000.0f, "maximum MIP charge for QA selection"}; + Configurable cutDistanceMipTrack{"cutDistanceMipTrack", 10.0f, "maximum raw MIP-track distance for QA selection"}; + + // Configurables for plotting + Configurable momNBins{"momNBins", 490, "Momentum bins"}; + Configurable momMin{"momMin", 0.1f, "Momentum min"}; + Configurable momMax{"momMax", 5.0f, "Momentum max"}; + + Configurable etaNBins{"etaNBins", 100, "Eta bins"}; + Configurable etaMin{"etaMin", -5.0f, "Eta min"}; + Configurable etaMax{"etaMax", 5.0f, "Eta max"}; + + Configurable phiNBins{"phiNBins", 100, "Phi bins"}; + Configurable phiMin{"phiMin", -10.0f, "Phi min"}; + Configurable phiMax{"phiMax", 10.0f, "Phi max"}; + + Configurable mapNBins{"mapNBins", 135, "Map bins (x or y)"}; + Configurable mapMin{"mapMin", 0.0f, "Map min (x or y) in cm"}; + Configurable mapMax{"mapMax", 135.0f, "Map max (x or y) in cm"}; + + Configurable residualsNBins{"residualsNBins", 400, "Residuals bins"}; + Configurable residualsMin{"residualsMin", -20.0f, "Residuals min (x or y) in cm"}; + Configurable residualsMax{"residualsMax", 20.0f, "Residuals max (x or y) in cm"}; + + static constexpr int Rich0 = 0, Rich1 = 1, Rich2 = 2, Rich3 = 3, Rich4 = 4, Rich5 = 5, Rich6 = 6; + static constexpr int Nch = 7; + + void init(o2::framework::InitContext const&) + { + // Axes for histograms + AxisSpec axisHmpidMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisHmpidMom"}; + AxisSpec axisGlobalMom{momNBins, momMin, momMax, "#it{p} (GeV/#it{c})", "axisGlobalMom"}; + AxisSpec axisMipX{mapNBins, mapMin, mapMax, "X_{MIP} (cm)", "axisMipX"}; + AxisSpec axisMipY{mapNBins, mapMin, mapMax, "Y_{MIP} (cm)", "axisMipY"}; + AxisSpec axisGlobalTrackX{mapNBins, mapMin, mapMax, "X_{track} (cm)", "axisGlobalTrackX"}; + AxisSpec axisGlobalTrackY{mapNBins, mapMin, mapMax, "Y_{track} (cm)", "axisGlobalTrackY"}; + AxisSpec axisEta{etaNBins, etaMin, etaMax, "#eta", "axisEta"}; + AxisSpec axisPhi{phiNBins, phiMin, phiMax, "#phi", "axisPhi"}; + AxisSpec axisResiduals{residualsNBins, residualsMin, residualsMax, "Residuals (cm)", "axisResiduals"}; + AxisSpec axisChamber{10, -1.5, 8.5, "Chamber number", "axisChamber"}; + + for (int iCh = 0; iCh < Nch; iCh++) { + registry.add(Form("hEtaRich%d", iCh), Form("hEtaRich%d", iCh), HistType::kTH1F, {axisEta}); + registry.add(Form("hPhiRich%d", iCh), Form("hPhiRich%d", iCh), HistType::kTH1F, {axisPhi}); + + registry.add(Form("hmpidXYHmpidMipRich%dPos", iCh), "MIP XY - Q_{MIP} cut;X_{MIP} (cm);Y_{MIP} (cm)", HistType::kTHnSparseF, {axisHmpidMom, axisMipX, axisMipY}); + registry.add(Form("hmpidXYGlobalTrackRich%dPos", iCh), "Global track XY - all;X_{track} (cm);Y_{track} (cm)", HistType::kTHnSparseF, {axisGlobalMom, axisGlobalTrackX, axisGlobalTrackY}); + registry.add(Form("hmpidXYHmpidMipRich%dNeg", iCh), "MIP XY - Q_{MIP} cut;X_{MIP} (cm);Y_{MIP} (cm)", HistType::kTHnSparseF, {axisHmpidMom, axisMipX, axisMipY}); + registry.add(Form("hmpidXYGlobalTrackRich%dNeg", iCh), "Global track XY - all;X_{track} (cm);Y_{track} (cm)", HistType::kTHnSparseF, {axisGlobalMom, axisGlobalTrackX, axisGlobalTrackY}); + + registry.add(Form("residualsXVsMomentumRich%dPos", iCh), Form("residualsXVsMomentumRich%dPos", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsYVsMomentumRich%dPos", iCh), Form("residualsYVsMomentumRich%dPos", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsXVsMomentumRich%dNeg", iCh), Form("residualsXVsMomentumRich%dNeg", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + registry.add(Form("residualsYVsMomentumRich%dNeg", iCh), Form("residualsYVsMomentumRich%dNeg", iCh), HistType::kTH2F, {axisHmpidMom, axisResiduals}); + + // Momentum and Position correlations + registry.add(Form("hMomTrackVsHmpidRich%dPos", iCh), Form("hMomTrackVsHmpidRich%dPos", iCh), HistType::kTH2F, {axisGlobalMom, axisHmpidMom}); + registry.add(Form("hXTrackVsXMipRich%dPos", iCh), Form("hXTrackVsXMipRich%dPos", iCh), HistType::kTH2F, {axisGlobalTrackX, axisMipX}); + registry.add(Form("hYTrackVsYMipRich%dPos", iCh), Form("hYTrackVsYMipRich%dPos", iCh), HistType::kTH2F, {axisGlobalTrackY, axisMipY}); + registry.add(Form("hMomTrackVsHmpidRich%dNeg", iCh), Form("hMomTrackVsHmpidRich%dNeg", iCh), HistType::kTH2F, {axisGlobalMom, axisHmpidMom}); + registry.add(Form("hXTrackVsXMipRich%dNeg", iCh), Form("hXTrackVsXMipRich%dNeg", iCh), HistType::kTH2F, {axisGlobalTrackX, axisMipX}); + registry.add(Form("hYTrackVsYMipRich%dNeg", iCh), Form("hYTrackVsYMipRich%dNeg", iCh), HistType::kTH2F, {axisGlobalTrackY, axisMipY}); + } // end loop over chambers + + } // end init + + void process(aod::HmpidAnalysis const& hmpidTable) + { + for (const auto& hmpid : hmpidTable) { + if (hmpid.itsNCluster() < minReqClusterITS) { + continue; + } + if (hmpid.tpcNCluster() < minTPCnClsFound) { + continue; + } + if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) { + continue; + } + if (hmpid.tpcChi2() > maxChi2TPC) { + continue; + } + if (hmpid.itsChi2() > maxChi2ITS) { + continue; + } + if (std::abs(hmpid.dcaXY()) > maxDCAxy) { + continue; + } + if (std::abs(hmpid.dcaZ()) > maxDCAz) { + continue; + } + + // ------------------------- + // derived quantities + // ------------------------- + const int chamber = static_cast(hmpid.chamber()); + + const float momHmpid = hmpid.momentumHmpid(); + const float momTrackAbs = hmpid.momentumTrack(); + const float momHmpidAbs = std::abs(momHmpid); + + const bool isPos = momHmpid > 0; + const bool isNeg = momHmpid < 0; + const bool passQ = hmpid.chargeMip() > qMipMin && hmpid.chargeMip() < qMipMax; + + switch (chamber) { + case Rich0: + registry.fill(HIST("hEtaRich0"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich0"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich0Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich0Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich0Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich0Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich0Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich0Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich0Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich0Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich0Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich0Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich0Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich0Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich0Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich0Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich1: + registry.fill(HIST("hEtaRich1"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich1"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich1Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich1Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich1Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich1Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich1Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich1Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich1Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich1Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich1Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich1Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich1Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich1Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich1Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich1Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich2: + registry.fill(HIST("hEtaRich2"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich2"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich2Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich2Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich2Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich2Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich2Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich2Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich2Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich2Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich2Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich2Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich2Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich2Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich2Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich2Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich3: + registry.fill(HIST("hEtaRich3"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich3"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich3Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich3Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich3Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich3Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich3Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich3Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich3Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich3Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich3Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich3Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich3Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich3Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich3Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich3Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich4: + registry.fill(HIST("hEtaRich4"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich4"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich4Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich4Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich4Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich4Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich4Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich4Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich4Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich4Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich4Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich4Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich4Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich4Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich4Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich4Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich5: + registry.fill(HIST("hEtaRich5"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich5"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich5Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich5Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich5Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich5Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich5Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich5Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich5Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich5Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich5Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich5Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich5Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich5Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich5Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich5Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + case Rich6: + registry.fill(HIST("hEtaRich6"), hmpid.etaTrack()); + registry.fill(HIST("hPhiRich6"), hmpid.phiTrack()); + if (isPos) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich6Pos"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich6Pos"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich6Pos"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich6Pos"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich6Pos"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich6Pos"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich6Pos"), hmpid.yTrack(), hmpid.yMip()); + } + if (isNeg) { + if (passQ) { + registry.fill(HIST("hmpidXYHmpidMipRich6Neg"), momHmpidAbs, hmpid.xMip(), hmpid.yMip()); + } + registry.fill(HIST("hmpidXYGlobalTrackRich6Neg"), momHmpidAbs, hmpid.xTrack(), hmpid.yTrack()); + + registry.fill(HIST("residualsXVsMomentumRich6Neg"), momHmpidAbs, hmpid.xTrack() - hmpid.xMip()); + registry.fill(HIST("residualsYVsMomentumRich6Neg"), momHmpidAbs, hmpid.yTrack() - hmpid.yMip()); + + registry.fill(HIST("hMomTrackVsHmpidRich6Neg"), momTrackAbs, momHmpidAbs); + registry.fill(HIST("hXTrackVsXMipRich6Neg"), hmpid.xTrack(), hmpid.xMip()); + registry.fill(HIST("hYTrackVsYMipRich6Neg"), hmpid.yTrack(), hmpid.yMip()); + } + break; + default: + break; + } // end switch on chamber + } // end of loop over hmpidTable + } // end process +}; // end struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx deleted file mode 100644 index 27451cff7e3..00000000000 --- a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidDeuteron.cxx +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "tableHMPID.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -using namespace o2; -using namespace o2::framework; - -struct HmpidDeuteron { - HistogramRegistry registryDA{"registryDA", {}, OutputObjHandlingPolicy::AnalysisObject}; - - Configurable nsigmaTPCMin{"nsigmaTPCMin", -3.0, "nsigmaTPCMin"}; - Configurable nsigmaTPCMax{"nsigmaTPCMax", +3.0, "nsigmaTPCMax"}; - Configurable nsigmaTOFMin{"nsigmaTOFMin", -3.0, "nsigmaTOFMin"}; - Configurable nsigmaTOFMax{"nsigmaTOFMax", +3.5, "nsigmaTOFMax"}; - Configurable minReqClusterITS{"minReqClusterITS", 4.0, "min number of clusters required in ITS"}; - Configurable minTPCnClsFound{"minTPCnClsFound", 50.0f, "minTPCnClsFound"}; - Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.0f, "min number of crossed rows TPC"}; - Configurable maxChi2ITS{"maxChi2ITS", 36.0f, "max chi2 per cluster ITS"}; - Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; - Configurable maxDCAxy{"maxDCAxy", 0.5f, "maxDCAxy"}; - Configurable maxDCAz{"maxDCAz", 0.5f, "maxDCAz"}; - - void init(InitContext const&) - { - // Deuteron Pos - registryDA.add("incomingDe_Pos_8cm", "incomingDe_Pos_8cm", HistType::kTH1F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}}); - registryDA.add("incomingDe_Pos_4cm", "incomingDe_Pos_4cm", HistType::kTH1F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}}); - - registryDA.add("De_Pos_deltaR_8cm", "De_Pos_deltaR_8cm", HistType::kTH1F, {{300, 0.0, 30.0, "#Delta R (cm)"}}); - registryDA.add("De_Pos_deltaR_4cm", "De_Pos_deltaR_4cm", HistType::kTH1F, {{300, 0.0, 30.0, "#Delta R (cm)"}}); - - registryDA.add("survivingDe_Pos_8cm", "survivingDe_Pos_8cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {300, 0.0, 30.0, "#Delta R (cm)"}}); - registryDA.add("survivingDe_Pos_4cm", "survivingDe_Pos_4cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {300, 0.0, 30.0, "#Delta R (cm)"}}); - registryDA.add("De_Pos_Q_8cm", "De_Pos_Q_8cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {200, 0.0, 2000.0, "Q (ADC)"}}); - registryDA.add("De_Pos_Q_4cm", "De_Pos_Q_4cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {200, 0.0, 2000.0, "Q (ADC)"}}); - registryDA.add("De_Pos_ClsSize_8cm", "De_Pos_ClsSize_8cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, 0.0, 20., "Cls size"}}); - registryDA.add("De_Pos_ClsSize_4cm", "De_Pos_ClsSize_4cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, 0.0, 20.0, "Cls size"}}); - registryDA.add("De_Pos_momentum", "De_Pos_momentum", HistType::kTH2F, {{100, 0.0, 5.0, "#it{p}_{vtx} (GeV/#it{c})"}, {100, 0.0, 5.0, "#it{p}_{hmpid} (GeV/#it{c})"}}); - - registryDA.add("nSigmaTPC_vs_momHMPID_noCut_DePos", "nSigmaTPC_vs_momHMPID_noCut_DePos", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5., 5.0, "n#sigma_TPC"}}); - registryDA.add("nSigmaTOF_vs_momHMPID_noCut_DePos", "nSigmaTOF_vs_momHMPID_noCut_DePos", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5., 5.0, "n#sigma_TOF"}}); - registryDA.add("nSigmaTPC_vs_momHMPID_Cut_DePos", "nSigmaTPC_vs_momHMPID_Cut_DePos", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5., 5.0, "n#sigma_TPC"}}); - registryDA.add("nSigmaTOF_vs_momHMPID_Cut_DePos", "nSigmaTOF_vs_momHMPID_Cut_DePos", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5., 5.0, "n#sigma_TOF"}}); - - // Deuteron Neg - registryDA.add("incomingDe_Neg_8cm", "incomingDe_Neg_8cm", HistType::kTH1F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}}); - registryDA.add("incomingDe_Neg_4cm", "incomingDe_Neg_4cm", HistType::kTH1F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}}); - - // plot aggiunti - registryDA.add("De_Neg_deltaR_8cm", "De_Neg_deltaR_8cm", HistType::kTH1F, {{300, 0.0, 30.0, "#Delta R (cm)"}}); - registryDA.add("De_Neg_deltaR_4cm", "De_Neg_deltaR_4cm", HistType::kTH1F, {{300, 0.0, 30.0, "#Delta R (cm)"}}); - - registryDA.add("survivingDe_Neg_8cm", "survivingDe_Neg_8cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {300, 0.0, 30.0, "#Delta R (cm)"}}); - registryDA.add("survivingDe_Neg_4cm", "survivingDe_Neg_4cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {300, 0.0, 30.0, "#Delta R (cm)"}}); - registryDA.add("De_Neg_Q_8cm", "De_Neg_Q_8cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {200, 0.0, 2000.0, "Q (ADC)"}}); - registryDA.add("De_Neg_Q_4cm", "De_Neg_Q_4cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {200, 0.0, 2000.0, "Q (ADC)"}}); - registryDA.add("De_Neg_ClsSize_8cm", "De_Neg_ClsSize_8cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, 0.0, 20.0, "Cls size"}}); - registryDA.add("De_Neg_ClsSize_4cm", "De_Neg_ClsSize_4cm", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, 0.0, 20.0, "Cls size"}}); - registryDA.add("De_Neg_momentum", "De_Neg_momentum", HistType::kTH2F, {{100, 0.0, 5.0, "#it{p}_{vtx} (GeV/#it{c})"}, {100, 0.0, 5.0, "#it{p}_{hmpid} (GeV/#it{c})"}}); - - registryDA.add("nSigmaTPC_vs_momHMPID_noCut_DeNeg", "nSigmaTPC_vs_momHMPID_noCut_DeNeg", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5.0, 5.0, "n#sigma_TPC"}}); - registryDA.add("nSigmaTOF_vs_momHMPID_noCut_DeNeg", "nSigmaTOF_vs_momHMPID_noCut_DeNeg", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5., 5.0, "n#sigma_TOF"}}); - registryDA.add("nSigmaTPC_vs_momHMPID_Cut_DeNeg", "nSigmaTPC_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5., 5.0, "n#sigma_TPC"}}); - registryDA.add("nSigmaTOF_vs_momHMPID_Cut_DeNeg", "nSigmaTOF_vs_momHMPID_Cut_DeNeg", HistType::kTH2F, {{490, 0.1, 5.0, "#it{p} (GeV/#it{c})"}, {20, -5., 5.0, "n#sigma_TOF"}}); - - registryDA.add("hmpidCkovvsMom", "hmpidCkovvsMom", kTH2F, {{500, 0, 10., "#it{p} (GeV/#it{c})"}, {800, 0., 0.8, "#theta_{Ch} (rad)"}}); - } - - void process(aod::HmpidAnalysis const& hmpidTable) - { - for (const auto& hmpid : hmpidTable) { - - // filters on primary tracks - if (hmpid.itsNCluster() < minReqClusterITS) - continue; - if (hmpid.tpcNCluster() < minTPCnClsFound) - continue; - if (hmpid.tpcNClsCrossedRows() < minNCrossedRowsTPC) - continue; - if (hmpid.tpcChi2() > maxChi2TPC) - continue; - if (hmpid.itsChi2() > maxChi2ITS) - continue; - if (TMath::Abs(hmpid.dcaXY()) > maxDCAxy) - continue; - if (TMath::Abs(hmpid.dcaZ()) > maxDCAz) - continue; - - // plots nsigma before cuts - if (hmpid.momentumHmpid() > 0) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_noCut_DePos"), std::fabs(hmpid.momentumHmpid()), hmpid.tpcNSigmaDe()); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_noCut_DePos"), std::fabs(hmpid.momentumHmpid()), hmpid.tofNSigmaDe()); - } - - if (hmpid.momentumHmpid() < 0) { - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_noCut_DeNeg"), std::fabs(hmpid.momentumHmpid()), hmpid.tpcNSigmaDe()); - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_noCut_DeNeg"), std::fabs(hmpid.momentumHmpid()), hmpid.tofNSigmaDe()); - } - - // deuteron candidate cuts - if (hmpid.tpcNSigmaDe() < nsigmaTPCMin || hmpid.tpcNSigmaDe() > nsigmaTPCMax) - continue; - - if (hmpid.momentumHmpid() > 0) - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DePos"), std::fabs(hmpid.momentumHmpid()), hmpid.tpcNSigmaDe()); - else - registryDA.fill(HIST("nSigmaTPC_vs_momHMPID_Cut_DeNeg"), std::fabs(hmpid.momentumHmpid()), hmpid.tpcNSigmaDe()); - - if (hmpid.tofNSigmaDe() < nsigmaTOFMin || hmpid.tofNSigmaDe() > nsigmaTOFMax) - continue; - - if (hmpid.momentumHmpid() > 0) - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Cut_DePos"), std::fabs(hmpid.momentumHmpid()), hmpid.tofNSigmaDe()); - else - registryDA.fill(HIST("nSigmaTOF_vs_momHMPID_Cut_DeNeg"), std::fabs(hmpid.momentumHmpid()), hmpid.tofNSigmaDe()); - - // plot changle vs p - registryDA.fill(HIST("hmpidCkovvsMom"), hmpid.momentumHmpid(), hmpid.chAngle()); - - // absorbers - const float dx = hmpid.xTrack() - hmpid.xMip(); - const float dy = hmpid.yTrack() - hmpid.yMip(); - const float dr = TMath::Sqrt(dx * dx + dy * dy); - - int abs4cm = 2, abs8cm = 4; - bool hmpidAbs8cm = true, hmpidAbs4cm = true; - - if (hmpid.momentumHmpid() > 0) { - registryDA.fill(HIST("De_Pos_momentum"), hmpid.momentumTrack(), std::fabs(hmpid.momentumHmpid())); - - if (hmpidAbs8cm && hmpid.chamber() == abs8cm) { - registryDA.fill(HIST("incomingDe_Pos_8cm"), std::fabs(hmpid.momentumHmpid())); - registryDA.fill(HIST("survivingDe_Pos_8cm"), std::fabs(hmpid.momentumHmpid()), dr); - registryDA.fill(HIST("De_Pos_Q_8cm"), std::fabs(hmpid.momentumHmpid()), hmpid.chargeMip()); - registryDA.fill(HIST("De_Pos_ClsSize_8cm"), std::fabs(hmpid.momentumHmpid()), hmpid.clusterSize()); - registryDA.fill(HIST("De_Pos_deltaR_8cm"), dr); - } - if (hmpidAbs4cm && hmpid.chamber() == abs4cm) { - registryDA.fill(HIST("incomingDe_Pos_4cm"), std::fabs(hmpid.momentumHmpid())); - registryDA.fill(HIST("survivingDe_Pos_4cm"), std::fabs(hmpid.momentumHmpid()), dr); - registryDA.fill(HIST("De_Pos_Q_4cm"), std::fabs(hmpid.momentumHmpid()), hmpid.chargeMip()); - registryDA.fill(HIST("De_Pos_ClsSize_4cm"), std::fabs(hmpid.momentumHmpid()), hmpid.clusterSize()); - registryDA.fill(HIST("De_Pos_deltaR_4cm"), dr); - } - } - - if (hmpid.momentumHmpid() < 0) { - registryDA.fill(HIST("De_Neg_momentum"), hmpid.momentumTrack(), std::fabs(hmpid.momentumHmpid())); - - if (hmpidAbs8cm && hmpid.chamber() == abs8cm) { - registryDA.fill(HIST("incomingDe_Neg_8cm"), std::fabs(hmpid.momentumHmpid())); - registryDA.fill(HIST("survivingDe_Neg_8cm"), std::fabs(hmpid.momentumHmpid()), dr); - registryDA.fill(HIST("De_Neg_Q_8cm"), std::fabs(hmpid.momentumHmpid()), hmpid.chargeMip()); - registryDA.fill(HIST("De_Neg_ClsSize_8cm"), std::fabs(hmpid.momentumHmpid()), hmpid.clusterSize()); - registryDA.fill(HIST("De_Neg_deltaR_8cm"), dr); - } - if (hmpidAbs4cm && hmpid.chamber() == abs4cm) { - registryDA.fill(HIST("incomingDe_Neg_4cm"), std::fabs(hmpid.momentumHmpid())); - registryDA.fill(HIST("survivingDe_Neg_4cm"), std::fabs(hmpid.momentumHmpid()), dr); - registryDA.fill(HIST("De_Neg_Q_4cm"), std::fabs(hmpid.momentumHmpid()), hmpid.chargeMip()); - registryDA.fill(HIST("De_Neg_ClsSize_4cm"), std::fabs(hmpid.momentumHmpid()), hmpid.clusterSize()); - registryDA.fill(HIST("De_Neg_deltaR_4cm"), dr); - } - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; -} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidTableProducer.cxx b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidTableProducer.cxx index 1b6cea53ef5..1074a071b60 100644 --- a/DPG/Tasks/AOTTrack/PID/HMPID/hmpidTableProducer.cxx +++ b/DPG/Tasks/AOTTrack/PID/HMPID/hmpidTableProducer.cxx @@ -19,20 +19,32 @@ #include "Common/DataModel/TrackSelectionTables.h" #include -#include +#include +#include +#include +#include #include #include #include #include #include #include -#include #include +#include #include +#include +#include +#include + +#include + +#include +#include #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -40,50 +52,339 @@ using namespace o2::framework::expressions; using namespace o2::constants::physics; struct HmpidTableProducer { + + Produces hmpidAnalysis; + Produces hmpidAnalysisMC; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - const AxisSpec axisEvtCounter{1, 0, +1, ""}; + AxisSpec axisEvtCounter{1, 0, +1, ""}; - // CCDB configurable - Service ccdb; + Service ccdb{}; struct : ConfigurableGroup { Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "URL of the CCDB repository"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the material LUT"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the aligned geometry"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of GRPMagField"}; } ccdbConfig; - Produces hmpidAnalysis; - - // configurable for quality requirements + // ----------------------------------------------------------------------- + // Quality configurables + // ----------------------------------------------------------------------- Configurable requireITS{"requireITS", true, "Require ITS track"}; Configurable requireTPC{"requireTPC", true, "Require TPC track"}; Configurable requireTOF{"requireTOF", true, "Require TOF track"}; + Configurable useInAbsorberGeomMethod{"useInAbsorberGeomMethod", false, "Use geometrical method to check if daughters are born in absorber"}; + + // (reference) 473 cm - was the legacy value in run2 simulation + Configurable survivalThresholdRich2{"survivalThresholdRich2", 437.5f, "survivalThresholdRich2"}; + Configurable survivalThresholdRich4{"survivalThresholdRich4", 439.0f, "survivalThresholdRich4"}; + using CollisionCandidates = o2::soa::Join; - using TrackCandidates = soa::Join; + using TrackCandidates = soa::Join; + + using TrackCandidatesMC = soa::Join; + + std::unordered_set mCollisionsWithHmpid; + + static constexpr int Rich2 = 2, Rich4 = 4; + + // (reference) HMPID Detector class in O2 + static constexpr double AbsThetaDeg = 33.5; + double mAbsCosT = 0., mAbsSinT = 0.; + + // Rich2 absorber: trans2 = {435.5, 0., -155.}, thickness 40mm -> halfX = 2cm + static constexpr double AbsRich2CenterX = 435.5, AbsRich2CenterZ = -155.; + static constexpr double AbsRich2HalfX = 2.0; + + // Rich4 absorber: trans4 = {435., 0., 155.}, thickness 80mm -> halfX = 4cm + static constexpr double AbsRich4CenterX = 435.0, AbsRich4CenterZ = 155.; + static constexpr double AbsRich4HalfX = 4.0; + + // common to both absorbers: box is 1300x1300mm -> halfY = halfZ = 65cm + static constexpr double AbsHalfY = 65.0, AbsHalfZ = 65.0; void init(o2::framework::InitContext&) { - // Configure CCDB + mAbsCosT = std::cos(AbsThetaDeg * TMath::DegToRad()); + mAbsSinT = std::sin(AbsThetaDeg * TMath::DegToRad()); + ccdb->setURL(ccdbConfig.ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); - histos.add("eventCounter", "eventCounter", kTH1F, {axisEvtCounter}); - histos.add("goodEventCounter", "goodEventCounter", kTH1F, {axisEvtCounter}); - histos.add("eventsHmpid", "eventsWithHmpid", kTH1F, {axisEvtCounter}); + // event counters + histos.add("eventCounter", "All events", kTH1F, {axisEvtCounter}); + histos.add("goodEventCounter", "Events passing sel8", kTH1F, {axisEvtCounter}); + histos.add("eventsHmpid", "Events with HMPID track", kTH1F, {axisEvtCounter}); + + // chamber extraction quality checks - M1 propagation, M2 clusSize encoding, M3 hybrid configuration + const AxisSpec axisCh{10, -1.5, 8.5, "Chamber"}; + histos.add("hChamberM1", "Chamber M1 (propagation)", kTH1F, {axisCh}); + histos.add("hChamberM2", "Chamber M2 (clusSize encoding)", kTH1F, {axisCh}); + histos.add("hChamberM3", "Chamber M3 (hybrid, in table)", kTH1F, {axisCh}); + + histos.add("hChamberM1vsM2", "M1 vs M2; M2; M1", kTH2F, {axisCh, axisCh}); + histos.add("hChamberM3vsM2", "M3 vs M2; M2; M3", kTH2F, {axisCh, axisCh}); + + histos.add("hClusSize", "Raw hmpidClusSize", kTH1F, {{500, -1.1e6, 1e6, "clusSize"}}); + histos.add("hClusSizeCorrupt", "Corrupt entries (<=0)", kTH1F, {{200, -1.1e6, 1., "clusSize"}}); + + histos.add("hChamberAssignment", + "Chamber assignment outcome; category; counts", + kTH1F, {{4, -0.5, 3.5, ""}}); + + histos.add("hProdVertex", ";X (cm);Y (cm);Z (cm)", HistType::kTH3F, {{500, -500., 500.}, {500, -500., 500.}, {500, -500., 500.}}); + histos.add("hDaughterRCyl_Rich2", "hDaughterRCyl_Rich2", kTH1F, {{600, 0., 600.}}); + histos.add("hDaughterRCyl_Rich4", "hDaughterRCyl_Rich4", kTH1F, {{600, 0., 600.}}); + histos.add("hDaughterRSph_Rich2", "hDaughterRSph_Rich2", kTH1F, {{600, 0., 600.}}); + histos.add("hDaughterRSph_Rich4", "hDaughterRSph_Rich4", kTH1F, {{600, 0., 600.}}); } - // function to manage ccdb + // ----------------------------------------------------------------------- + // CCDB initialisation per run + // ----------------------------------------------------------------------- int mCCDBRunNumber = 0; + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) { if (mCCDBRunNumber == bc.runNumber()) { return; } mCCDBRunNumber = bc.runNumber(); + + auto grpMag = ccdb->getForTimeStamp( + ccdbConfig.grpmagPath, bc.timestamp()); + if (!grpMag) { + LOGF(fatal, "initCCDB: GRPMagField not found at %s", + ccdbConfig.grpmagPath.value.c_str()); + return; + } + o2::base::Propagator::initFieldFromGRP(grpMag); + + auto lut = o2::base::MatLayerCylSet::rectifyPtrFromFile( + ccdb->get(ccdbConfig.lutPath)); + o2::base::Propagator::Instance()->setMatLUT(lut); + + // Geometry needed by o2::hmpid::Param for mars2Lors + if (!gGeoManager) { + ccdb->get(ccdbConfig.geoPath); + } + + LOGF(info, "initCCDB: run %d initialised (mag + LUT + geo)", bc.runNumber()); + } + + void propagateHelix( + double len, + double bz, + int charge, + std::array& x, + std::array& p) + { + // Extrapolate track along simple helix in magnetic field + + constexpr double KB2C = 0.299792458e-3; + + double px = p[0]; + double py = p[1]; + double pz = p[2]; + + double pmod = std::hypot(px, py, pz); + double pt = std::hypot(px, py); + + if (pt < o2::constants::math::Almost0 || std::abs(bz) < o2::constants::math::Almost0) // straight-line tracks + { + x[0] += px / pmod * len; + x[1] += py / pmod * len; + x[2] += pz / pmod * len; + + return; + } + + double a = -KB2C * bz * charge; + double rho = a / pmod; + + double sinr = std::sin(rho * len); + double cosr = std::cos(rho * len); + + x[0] += px * sinr / a - py * (1. - cosr) / a; + x[1] += py * sinr / a + px * (1. - cosr) / a; + x[2] += pz * len / pmod; + + double px0 = px; + + p[0] = px0 * cosr - py * sinr; + p[1] = py * cosr + px0 * sinr; + p[2] = pz; + } + + bool intersectHelixPlane( + double bz, + int charge, + const std::array& x0, + const std::array& p0, + const std::array& planePoint, + const std::array& planeNormal, + std::array& xOut, + std::array& pOut) + { + // Intersect a helix with a plane defined by normals and points, using an iterative approach. + + double s = + (planePoint[0] - x0[0]) * planeNormal[0] + + (planePoint[1] - x0[1]) * planeNormal[1] + + (planePoint[2] - x0[2]) * planeNormal[2]; + + double dist = 99999., distPrev = dist; + + const double kConvThr = 0.00001; + const int kMaxIter = 100; + + std::array x{}; + std::array p{}; + + for (int iter = 0; iter < kMaxIter; ++iter) { + + x = x0; + p = p0; + propagateHelix(s, bz, charge, x, p); + + dist = + (x[0] - planePoint[0]) * planeNormal[0] + + (x[1] - planePoint[1]) * planeNormal[1] + + (x[2] - planePoint[2]) * planeNormal[2]; + + if (std::abs(dist) >= std::abs(distPrev)) { + return false; + } + + distPrev = dist; + s -= dist; + + if (std::abs(dist) < kConvThr) { + xOut = x; + pOut = p; + return true; + } + } + + return false; + } + + int getHmpidChamber( + const std::array& xIn, + const std::array& pIn, + double bz, + int charge) + { + std::array x{}; + std::array p{}; + + x = xIn; + p = pIn; + + auto* param = o2::hmpid::Param::instance(); + + for (int ch = o2::hmpid::Param::kMinCh; ch <= o2::hmpid::Param::kMaxCh; ++ch) { + + // Chamber module geometry: points and normals for radiators and PCs + std::array pRad{}, pPc{}; + std::array nRad{}, nPc{}; + + param->point(ch, pRad.data(), o2::hmpid::Param::kRad); + param->norm(ch, nRad.data()); + param->point(ch, pPc.data(), o2::hmpid::Param::kPc); + param->norm(ch, nPc.data()); + + // Intersection track - radiator plane + std::array xRad{}, pAtRad{}; + + if (!intersectHelixPlane(bz, charge, x, p, pRad, nRad, xRad, pAtRad)) { + continue; + } + + // Intersection track - PC plane + std::array xPc{}, pAtPc{}; + + if (!intersectHelixPlane(bz, charge, xRad, pAtRad, pPc, nPc, xPc, pAtPc)) { + continue; + } + + double theta = 0., phi = 0.; + param->mars2LorsVec(ch, pAtRad.data(), theta, phi); + + double xL = 0., yL = 0.; + param->mars2Lors(ch, xPc.data(), xL, yL); + + // Use isInside to check Chamber intersected + if (param->isInside(xL, yL, param->distCut())) { + return ch; + } + } + + // No chamber intersected + return -1; + } + + // Checks whether a point in global (MARS/ALICE) coordinates lies inside the + // absorber box in front of the given chamber (Rich2 or Rich4). See the + // geometry block above for the derivation of the transform used here. + bool isInAbsorber(double vx, double vy, double vz, int chamber) const + { + double centerX = 0., centerZ = 0., halfX = 0.; + + if (chamber == Rich2) { + centerX = AbsRich2CenterX; + centerZ = AbsRich2CenterZ; + halfX = AbsRich2HalfX; + } else if (chamber == Rich4) { + centerX = AbsRich4CenterX; + centerZ = AbsRich4CenterZ; + halfX = AbsRich4HalfX; + } else { + return false; + } + + // translate to box center + const double lx = vx - centerX; + const double ly = vy; // centerY = 0 + const double lz = vz - centerZ; + + // rotate by -theta into the box local frame + const double rx = lx * mAbsCosT + ly * mAbsSinT; + const double ry = -lx * mAbsSinT + ly * mAbsCosT; + const double rz = lz; + + return std::abs(rx) <= halfX && std::abs(ry) <= AbsHalfY && std::abs(rz) <= AbsHalfZ; + } + + bool survivedAbsorber(double vx, double vy, int chamber) + { + float thresholdR = 0.; + if (chamber == Rich2) { + thresholdR = survivalThresholdRich2; + } else if (chamber == Rich4) { + thresholdR = survivalThresholdRich4; + } else { + return false; + } + + const float r = std::hypot(vx, vy); + return r > thresholdR; } void processEvent(CollisionCandidates::iterator const& col, @@ -93,72 +394,247 @@ struct HmpidTableProducer { if (col.sel8()) { histos.fill(HIST("goodEventCounter"), 0.5); } - - // initialize CCDB for current BC initCCDB(col.bc_as()); } - PROCESS_SWITCH(HmpidTableProducer, processEvent, "Process event level - collisions", true); + PROCESS_SWITCH(HmpidTableProducer, processEvent, "Process event level", true); - void processHmpid( + template + void runHmpidAnalysis( aod::HMPIDs const& hmpids, - TrackCandidates const&, + TTrackTable const&, CollisionCandidates const&, - aod::BCsWithTimestamps const&) + aod::BCsWithTimestamps const&, + TMcParticles const& mcParticles = 0) { - // --- Static set to track unique collisions with HMPID tracks --- - static std::unordered_set collisionsWithHmpid; - - for (auto const& t : hmpids) { + for (auto const& t : hmpids) { // begin loop over hmpids - // Access the global track associated to the HMPID track - const auto& globalTrack = t.track_as(); + const auto& globalTrack = t.template track_as(); - if (!globalTrack.has_collision()) + if (!globalTrack.has_collision()) { continue; + } - // Access the associated collision - const auto& col = globalTrack.collision_as(); - initCCDB(col.bc_as()); + const auto& col = globalTrack.template collision_as(); + initCCDB(col.template bc_as()); uint32_t collId = col.globalIndex(); - // --- Track quality selection --- if ((requireITS && !globalTrack.hasITS()) || (requireTPC && !globalTrack.hasTPC()) || (requireTOF && !globalTrack.hasTOF())) { continue; } - // Count collisions with at least one valid HMPID track - if (collisionsWithHmpid.insert(collId).second) { + if (mCollisionsWithHmpid.insert(collId).second) { histos.fill(HIST("eventsHmpid"), 0.5); } - float centrality = col.centFV0A(); + // clusSize diagnostics + histos.fill(HIST("hClusSize"), t.hmpidClusSize()); + bool isCorrupt = (t.hmpidClusSize() <= 0); + if (isCorrupt) { + histos.fill(HIST("hClusSizeCorrupt"), t.hmpidClusSize()); + } + + // --- M2: clusSize encoding --- + int chamberM2 = t.hmpidClusSize() / 1000000; + histos.fill(HIST("hChamberM2"), chamberM2); + + // --- M1: propagation + // obtain global coordinates + double sinA = std::sin(globalTrack.alpha()); + double cosA = std::cos(globalTrack.alpha()); + + std::array x = { + globalTrack.x() * cosA - globalTrack.y() * sinA, + globalTrack.x() * sinA + globalTrack.y() * cosA, + static_cast(globalTrack.z())}; + + std::array p = { + static_cast(globalTrack.px()), + static_cast(globalTrack.py()), + static_cast(globalTrack.pz())}; + + int16_t charge = globalTrack.sign(); + + auto prop = o2::base::Propagator::Instance(); + auto bz = static_cast(prop->getNominalBz()); + + int chamberM1 = getHmpidChamber(x, p, bz, charge); + + histos.fill(HIST("hChamberM1"), chamberM1); + + if (!isCorrupt) { // begin if(!isCorrupt) - fill M1vsM2 + histos.fill(HIST("hChamberM1vsM2"), chamberM2, chamberM1); + } // end if(!isCorrupt) - fill M1vsM2 + + // --- M3: hybrid --- + int chamberM3 = -1; + if (!isCorrupt) { // begin if/else - M3 assignment + chamberM3 = chamberM2; + } else { + chamberM3 = chamberM1; + } // end if/else - M3 assignment + + // Legend: + // bin 0 = clusSize > 0, chamber found (M2 ok) + // bin 1 = clusSize > 0, chamber not found + // bin 2 = clusSize <= 0, M1 recovery (corrupt, M1 ok) + // bin 3 = clusSize <= 0, M1 fails (corrupt, skipped) + + if (!isCorrupt && chamberM3 >= 0) { + histos.fill(HIST("hChamberAssignment"), 0.); + } else if (!isCorrupt && chamberM3 < 0) { + histos.fill(HIST("hChamberAssignment"), 1.); + } else if (isCorrupt && chamberM3 >= 0) { + histos.fill(HIST("hChamberAssignment"), 2.); + } else { + histos.fill(HIST("hChamberAssignment"), 3.); + } + + histos.fill(HIST("hChamberM3"), chamberM3); + histos.fill(HIST("hChamberM3vsM2"), chamberM2, chamberM3); - // check cluster size sign - if (t.hmpidClusSize() <= 0) + if (chamberM3 < 0) { continue; + } - float hmpidPhotsCharge2[o2::aod::kDimPhotonsCharge]; + std::vector hmpidPhotsCharge2(o2::aod::kDimPhotonsCharge, 0.f); for (int i = 0; i < o2::aod::kDimPhotonsCharge; i++) { hmpidPhotsCharge2[i] = t.hmpidPhotsCharge()[i]; } - /////FILL HMPID CUSTOM TABLE - hmpidAnalysis(t.hmpidSignal(), t.hmpidMom(), - globalTrack.p(), t.hmpidXTrack(), t.hmpidYTrack(), t.hmpidXMip(), - t.hmpidYMip(), t.hmpidNPhotons(), t.hmpidQMip(), (t.hmpidClusSize() % 1000000) / 1000, - t.hmpidClusSize() / 1000000, hmpidPhotsCharge2, globalTrack.eta(), globalTrack.phi(), - globalTrack.px(), globalTrack.py(), globalTrack.pz(), globalTrack.itsNCls(), - globalTrack.tpcNClsFound(), globalTrack.tpcNClsCrossedRows(), globalTrack.tpcChi2NCl(), globalTrack.itsChi2NCl(), - globalTrack.dcaXY(), globalTrack.dcaZ(), globalTrack.tpcNSigmaPi(), globalTrack.tofNSigmaPi(), - globalTrack.tpcNSigmaKa(), globalTrack.tofNSigmaKa(), globalTrack.tpcNSigmaPr(), globalTrack.tofNSigmaPr(), - globalTrack.tpcNSigmaDe(), globalTrack.tofNSigmaDe(), centrality); - } // end loop on hmpid table entries + // fill hmpid table + hmpidAnalysis( + t.hmpidSignal(), t.hmpidMom(), + globalTrack.p(), t.hmpidXTrack(), t.hmpidYTrack(), + t.hmpidXMip(), t.hmpidYMip(), + t.hmpidNPhotons(), t.hmpidQMip(), + (t.hmpidClusSize() % 1000000) / 1000, + chamberM3, + hmpidPhotsCharge2, + globalTrack.eta(), globalTrack.phi(), + globalTrack.px(), globalTrack.py(), globalTrack.pz(), + globalTrack.itsNCls(), globalTrack.tpcNClsFound(), globalTrack.tpcNClsCrossedRows(), + globalTrack.tpcChi2NCl(), globalTrack.itsChi2NCl(), + globalTrack.dcaXY(), globalTrack.dcaZ(), + globalTrack.tpcNSigmaPi(), globalTrack.tofNSigmaPi(), + globalTrack.tpcNSigmaKa(), globalTrack.tofNSigmaKa(), + globalTrack.tpcNSigmaPr(), globalTrack.tofNSigmaPr(), + globalTrack.tpcNSigmaDe(), globalTrack.tofNSigmaDe(), + col.centFV0A()); + + // fill hmpid table for mc tracks if running on MC + if constexpr (isMC) { + if (globalTrack.has_mcParticle()) { + const auto& mc = globalTrack.mcParticle(); + + bool interactionInAbsorber = false; + + if ((chamberM3 == Rich2 || chamberM3 == Rich4) && mc.has_daughters()) { + auto dIds = mc.daughtersIds(); + bool foundRelevantDaughter = false; // true if at least one non-delta/photon daughter was examined + + if (useInAbsorberGeomMethod) { + for (int32_t idx = dIds.front(); idx <= dIds.back(); ++idx) { + auto daughter = mcParticles.rawIteratorAt(idx); + + int absPdg = std::abs(daughter.pdgCode()); + if (absPdg == kElectron || absPdg == kGamma) { + continue; + } + + foundRelevantDaughter = true; + + // diagnostics on daughters distribution + histos.fill(HIST("hProdVertex"), daughter.vx(), daughter.vy(), daughter.vz()); + + double rCyl = std::hypot(daughter.vx(), daughter.vy()); + double rSph = std::hypot(daughter.vx(), daughter.vy(), daughter.vz()); + if (chamberM3 == Rich2) { + histos.fill(HIST("hDaughterRCyl_Rich2"), rCyl); + histos.fill(HIST("hDaughterRSph_Rich2"), rSph); + } else { + histos.fill(HIST("hDaughterRCyl_Rich4"), rCyl); + histos.fill(HIST("hDaughterRSph_Rich4"), rSph); + } + + if (isInAbsorber(daughter.vx(), daughter.vy(), daughter.vz(), chamberM3)) { + interactionInAbsorber = true; + } + } // end loop daughters + } else { + bool survived = false; + for (int32_t idx = dIds.front(); idx <= dIds.back(); ++idx) { + auto daughter = mcParticles.rawIteratorAt(idx); + + // skip delta rays (e-/e+), photons, and HMPID Cherenkov/feedback + int absPdg = std::abs(daughter.pdgCode()); + if (absPdg == kElectron || absPdg == kGamma) { + continue; + } + + foundRelevantDaughter = true; + + histos.fill(HIST("hProdVertex"), daughter.vx(), daughter.vy(), daughter.vz()); + + double rCyl = std::hypot(daughter.vx(), daughter.vy()); + double rSph = std::hypot(daughter.vx(), daughter.vy(), daughter.vz()); + if (chamberM3 == Rich2) { + histos.fill(HIST("hDaughterRCyl_Rich2"), rCyl); + histos.fill(HIST("hDaughterRSph_Rich2"), rSph); + } else { + histos.fill(HIST("hDaughterRCyl_Rich4"), rCyl); + histos.fill(HIST("hDaughterRSph_Rich4"), rSph); + } + + if (survivedAbsorber(daughter.vx(), daughter.vy(), chamberM3)) { + survived = true; + } + } // end loop daughters + + // No relevant daughter found (only delta rays/photons, or no + // daughters at all): no evidence of a genuine interaction -> treat + // as primary/survived, consistent with the "no daughters" case. + interactionInAbsorber = foundRelevantDaughter ? !survived : false; + } + } // end if has_daughters + + hmpidAnalysisMC(mc.pdgCode(), mc.vx(), mc.vy(), mc.vz(), + mc.isPhysicalPrimary(), mc.getProcess(), interactionInAbsorber); + } else { + // No MC truth associated to this track + hmpidAnalysisMC(-999, -999.f, -999.f, -999.f, false, -100, false); + } + } // end if constexpr (isMC) + + } // end for - loop over hmpids + } // end runHmpidAnalysis + + // process real data + void processHmpid(aod::HMPIDs const& hmpids, + TrackCandidates const& tracks, + CollisionCandidates const& cols, + aod::BCsWithTimestamps const& bcs) + { + // isMC False + runHmpidAnalysis(hmpids, tracks, cols, bcs); } + PROCESS_SWITCH(HmpidTableProducer, processHmpid, "Process HMPID entries", true); - PROCESS_SWITCH(HmpidTableProducer, processHmpid, "Process hmpid entries - tracks", true); + // process MC + void processHmpidMC(aod::HMPIDs const& hmpids, + TrackCandidatesMC const& tracks, + CollisionCandidates const& cols, + aod::BCsWithTimestamps const& bcs, + aod::McParticles const& mcParticles) + { + runHmpidAnalysis(hmpids, tracks, cols, bcs, mcParticles); + } + PROCESS_SWITCH(HmpidTableProducer, processHmpidMC, "Process HMPID MC entries", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfg) { return WorkflowSpec{adaptAnalysisTask(cfg)}; } +WorkflowSpec defineDataProcessing(ConfigContext const& cfg) +{ + return WorkflowSpec{adaptAnalysisTask(cfg)}; +} diff --git a/DPG/Tasks/AOTTrack/PID/HMPID/tableHMPID.h b/DPG/Tasks/AOTTrack/PID/HMPID/tableHMPID.h index 6844da6c519..1e2445d5b05 100644 --- a/DPG/Tasks/AOTTrack/PID/HMPID/tableHMPID.h +++ b/DPG/Tasks/AOTTrack/PID/HMPID/tableHMPID.h @@ -15,6 +15,8 @@ #include #include +#include + namespace o2::aod { @@ -22,6 +24,7 @@ inline constexpr int kDimPhotonsCharge = 10; namespace variables_table { + DECLARE_SOA_COLUMN(ChAngle, chAngle, float); DECLARE_SOA_COLUMN(MomentumHmpid, momentumHmpid, float); DECLARE_SOA_COLUMN(MomentumTrack, momentumTrack, float); @@ -33,23 +36,19 @@ DECLARE_SOA_COLUMN(NPhotons, nPhotons, float); DECLARE_SOA_COLUMN(ChargeMip, chargeMip, float); DECLARE_SOA_COLUMN(ClusterSize, clusterSize, float); DECLARE_SOA_COLUMN(Chamber, chamber, float); -DECLARE_SOA_COLUMN(PhotonsCharge, photonsCharge, float[kDimPhotonsCharge]); - +DECLARE_SOA_COLUMN(PhotonsCharge, photonsCharge, std::vector); DECLARE_SOA_COLUMN(EtaTrack, etaTrack, float); DECLARE_SOA_COLUMN(PhiTrack, phiTrack, float); DECLARE_SOA_COLUMN(Px, px, float); DECLARE_SOA_COLUMN(Py, py, float); DECLARE_SOA_COLUMN(Pz, pz, float); - DECLARE_SOA_COLUMN(ItsNCluster, itsNCluster, float); DECLARE_SOA_COLUMN(TpcNCluster, tpcNCluster, float); DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, float); DECLARE_SOA_COLUMN(TpcChi2, tpcChi2, float); DECLARE_SOA_COLUMN(ItsChi2, itsChi2, float); - DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); - DECLARE_SOA_COLUMN(TpcNSigmaPi, tpcNSigmaPi, float); DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); DECLARE_SOA_COLUMN(TpcNSigmaKa, tpcNSigmaKa, float); @@ -59,7 +58,6 @@ DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); DECLARE_SOA_COLUMN(TpcNSigmaDe, tpcNSigmaDe, float); DECLARE_SOA_COLUMN(TofNSigmaDe, tofNSigmaDe, float); DECLARE_SOA_COLUMN(Centrality, centrality, float); - } // namespace variables_table DECLARE_SOA_TABLE(HmpidAnalysis, "AOD", "HMPIDANALYSIS", @@ -97,6 +95,30 @@ DECLARE_SOA_TABLE(HmpidAnalysis, "AOD", "HMPIDANALYSIS", variables_table::TofNSigmaDe, variables_table::Centrality); +// ----------------------------------------------------------------------- +// MC truth table +// ----------------------------------------------------------------------- +namespace hmpid_mc +{ +DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); +DECLARE_SOA_COLUMN(McVx, mcVx, float); +DECLARE_SOA_COLUMN(McVy, mcVy, float); +DECLARE_SOA_COLUMN(McVz, mcVz, float); +DECLARE_SOA_COLUMN(IsPhysPrimary, isPhysPrimary, bool); +DECLARE_SOA_COLUMN(ProcessCode, processCode, int); +DECLARE_SOA_COLUMN(HasInteractedInAbsorber, hasInteractedInAbsorber, bool); + +} // namespace hmpid_mc + +DECLARE_SOA_TABLE(HmpidAnalysisMC, "AOD", "HMPIDANALYSISMC", + hmpid_mc::PdgCode, + hmpid_mc::McVx, + hmpid_mc::McVy, + hmpid_mc::McVz, + hmpid_mc::IsPhysPrimary, + hmpid_mc::ProcessCode, + hmpid_mc::HasInteractedInAbsorber); + } // namespace o2::aod #endif // DPG_TASKS_AOTTRACK_PID_HMPID_TABLEHMPID_H_ diff --git a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOF.cxx b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOF.cxx index bbe18f83730..bb6afc2a437 100644 --- a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOF.cxx +++ b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOF.cxx @@ -379,7 +379,7 @@ struct tofPidQa { int evtimeflag = 0; if constexpr (fillHistograms) { - for (auto t : tracks) { + for (const auto& t : tracks) { if (!t.hasTOF()) { // Skipping tracks without TOF continue; } @@ -529,7 +529,7 @@ struct tofPidQa { soa::Filtered const& tracks) { isEventSelected(collision, tracks); - for (auto t : tracks) { + for (const auto& t : tracks) { isTrackSelected(collision, t); } } @@ -543,7 +543,7 @@ struct tofPidQa { return; } - for (auto t : tracks) { + for (const auto& t : tracks) { if (!isTrackSelected(collision, t)) { continue; } diff --git a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFDynamic.cxx b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFDynamic.cxx index 67695354168..bec2f335512 100644 --- a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFDynamic.cxx +++ b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFDynamic.cxx @@ -15,6 +15,7 @@ /// \brief Implementation for QA tasks of the TOF PID quantities /// +#include "Common/Core/PID/PIDTOFParamService.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -32,7 +33,6 @@ #include #include #include -#include #include #include @@ -401,7 +401,7 @@ struct tofPidQaDynamic { int evtimeflag = 0; if constexpr (fillHistograms) { - for (auto t : tracks) { + for (const auto& t : tracks) { if (!t.hasTOF()) { // Skipping tracks without TOF continue; } @@ -554,7 +554,7 @@ struct tofPidQaDynamic { tofResponse->processSetup(collision.bc_as()); isEventSelected(collision, tracks); - for (auto t : tracks) { + for (const auto& t : tracks) { isTrackSelected(collision, t); } } @@ -568,7 +568,7 @@ struct tofPidQaDynamic { return; } - for (auto t : tracks) { + for (const auto& t : tracks) { if (!isTrackSelected(collision, t)) { continue; } diff --git a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx index c0b4e628fe2..eaf04ba94da 100644 --- a/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx +++ b/DPG/Tasks/AOTTrack/PID/TOF/qaPIDTOFEvTime.cxx @@ -15,6 +15,7 @@ /// \brief Tasks of the TOF PID quantities for the event times /// +#include "Common/Core/PID/PIDTOF.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -33,7 +34,6 @@ #include #include #include -#include #include #include @@ -167,7 +167,7 @@ struct tofPidCollisionTimeQa { } listEfficiency.setObject(new THashList); - auto makeEfficiency = [&](TString effname, TString efftitle) { + auto makeEfficiency = [&](const TString& effname, const TString& efftitle) { listEfficiency->Add(new TEfficiency(effname, efftitle + ";TOF multiplicity;Efficiency", nBinsMultiplicity, 0, rangeMultiplicity)); }; @@ -436,11 +436,11 @@ struct tofPidCollisionTimeQa { histos.fill(HIST("goodforevtime/tofSignalPerCollision"), ncolls % 6000, trk.tofSignal()); } } - static_cast(listEfficiency->FindObject("effTOFEvTime"))->Fill(t.isEvTimeTOF(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0AEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0CEvTime"))->Fill(collision.has_foundFT0() && collision.t0CCorrectedValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effT0ACEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACValid(), nTracksWithTOF); - static_cast(listEfficiency->FindObject("effTOFT0ACEvTime"))->Fill(t.isEvTimeTOF() && collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effTOFEvTime"))->Fill(t.isEvTimeTOF(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0AEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0CEvTime"))->Fill(collision.has_foundFT0() && collision.t0CCorrectedValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effT0ACEvTime"))->Fill(collision.has_foundFT0() && collision.t0ACValid(), nTracksWithTOF); + dynamic_cast(listEfficiency->FindObject("effTOFT0ACEvTime"))->Fill(t.isEvTimeTOF() && collision.has_foundFT0() && collision.t0ACorrectedValid(), nTracksWithTOF); } } PROCESS_SWITCH(tofPidCollisionTimeQa, processData, "Process data", true); diff --git a/DPG/Tasks/AOTTrack/V0Cascades/perfK0sResolution.cxx b/DPG/Tasks/AOTTrack/V0Cascades/perfK0sResolution.cxx index aa2f13ba744..dac01d5f092 100644 --- a/DPG/Tasks/AOTTrack/V0Cascades/perfK0sResolution.cxx +++ b/DPG/Tasks/AOTTrack/V0Cascades/perfK0sResolution.cxx @@ -345,7 +345,7 @@ struct perfK0sResolution { } template - bool isEventAccepted(TCollision collision, bool fillHists) + bool isEventAccepted(const TCollision& collision, bool fillHists) // check whether the collision passes our collision selections { if (fillHists) diff --git a/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx b/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx index ff6236ec9a9..8a5a06a9b07 100644 --- a/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx +++ b/DPG/Tasks/AOTTrack/V0Cascades/qaEfficiencyV0s.cxx @@ -125,12 +125,12 @@ struct QaEfficiencyV0s { } for (int i = 0; i < nSpecies; i++) { - static_cast(listEfficiencyMC->At(i * nSpecies))->SetPassedHistogram(*histograms[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies))->SetTotalHistogram(*histograms[i][kHistoPtDen].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetPassedHistogram(*histogramsPrm[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetTotalHistogram(*histogramsPrm[i][kHistoPtDen].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetPassedHistogram(*histogramsPrmRap[i][kHistoPtNum].get(), "f"); - static_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetTotalHistogram(*histogramsPrmRap[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies))->SetPassedHistogram(*histograms[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies))->SetTotalHistogram(*histograms[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetPassedHistogram(*histogramsPrm[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 1))->SetTotalHistogram(*histogramsPrm[i][kHistoPtDen].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetPassedHistogram(*histogramsPrmRap[i][kHistoPtNum].get(), "f"); + dynamic_cast(listEfficiencyMC->At(i * nSpecies + 2))->SetTotalHistogram(*histogramsPrmRap[i][kHistoPtDen].get(), "f"); } } }; diff --git a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx index 33a4a2a9ed7..4d1f8c3b0f1 100644 --- a/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx +++ b/DPG/Tasks/AOTTrack/derivedDataCreatorD0Calibration.cxx @@ -60,8 +60,8 @@ #include #include #include -#include #include +#include #include using namespace o2; @@ -117,8 +117,17 @@ struct DerivedDataCreatorD0Calibration { std::string prefix = "ml"; } cfgMl; - using TracksWCovExtraPid = soa::Join; - using TracksWCovExtraPidAndQa = soa::Join; + struct SelectedTrack { + int globalIndex; + o2::dataformats::DCA dca; + o2::track::TrackParametrizationWithError trackParCov; + int pidPion; + int pidKaon; + }; + + using TracksWCovExtraPid = soa::Join; + using TracksWCovExtraTmoPid = soa::Join; + using TracksWCovExtraTmoPidAndQa = soa::Join; using CollisionsWEvSel = soa::Join; using TrackMeanOccs = soa::Join; @@ -151,7 +160,7 @@ struct DerivedDataCreatorD0Calibration { ccdb->setLocalObjectValidityChecking(); if (cfgDownsampling.apply) { - histDownSampl.setObject(reinterpret_cast(ccdb->getSpecific(cfgDownsampling.pathCcdbWeights))); + histDownSampl.setObject(static_cast(ccdb->getSpecific(cfgDownsampling.pathCcdbWeights))); } if (cfgMl.apply) { @@ -183,16 +192,26 @@ struct DerivedDataCreatorD0Calibration { } // main function - template + template void runDataCreation(CollisionsWEvSel const& collisions, aod::TrackAssoc const& trackIndices, - TTracks const&, + TTracks const& tracks, aod::BCsWithTimestamps const&, - TrackMeanOccs const&, + TTrackMeanOccs const&, TTrackQa const&) { - std::map selectedCollisions; // map with indices of selected collisions (key: original AOD Collision table index, value: D0 collision index) - std::map selectedTracks; // map with indices of selected tracks (key: original AOD Track table index, value: D0 daughter track index) + std::unordered_map selectedCollisions; // map with indices of selected collisions (key: original AOD Collision table index, value: D0 collision index) + std::unordered_map selectedTracks; // map with indices of selected tracks (key: original AOD Track table index, value: D0 daughter track index) + + std::vector bdtScoresD0; + std::vector bdtScoresD0bar; + std::vector featuresCandD0; + std::vector featuresCandD0bar; + bdtScoresD0.resize(3); + bdtScoresD0bar.resize(3); + featuresCandD0.resize(16); + featuresCandD0bar.resize(16); + auto const& cuts = cfgCandCuts.topologicalCuts; for (auto const& collision : collisions) { @@ -216,117 +235,121 @@ struct DerivedDataCreatorD0Calibration { eventIR.setFromLong(bc.globalBC()); auto groupedTrackIndices = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); - for (auto const& trackIndexPos : groupedTrackIndices) { - auto trackPos = trackIndexPos.template track_as(); + // we cache tracks + std::vector tracksSelectedPos; + std::vector tracksSelectedNeg; + tracksSelectedPos.reserve(groupedTrackIndices.size() / 2); + tracksSelectedNeg.reserve(groupedTrackIndices.size() / 2); + + // first loop on tracks to find the selected ones + for (auto const& trackIndex : groupedTrackIndices) { + auto track = trackIndex.template track_as(); // track selections - if (trackPos.sign() < 0) { // first positive track + if (!track.isGlobalTrackWoDCA()) { continue; } - if (!trackPos.isGlobalTrackWoDCA()) { + if (track.pt() < cfgTrackCuts.ptMin) { continue; } - if (trackPos.pt() < cfgTrackCuts.ptMin) { + if (std::abs(track.eta()) > cfgTrackCuts.absEtaMax) { continue; } - if (std::abs(trackPos.eta()) > cfgTrackCuts.absEtaMax) { - continue; + auto trackParCov = getTrackParCov(track); + auto dcaTmp = o2::dataformats::DCA(track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ()); + if (track.collisionId() != collision.globalIndex()) { + trackParCov.propagateToDCA(primaryVertex, bz, &dcaTmp); } - auto trackParCovPos = getTrackParCov(trackPos); - o2::dataformats::DCA dcaPos; - trackParCovPos.propagateToDCA(primaryVertex, bz, &dcaPos); - if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCovPos.getPt(), dcaPos.getY(), dcaPos.getZ())) { + if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCov.getPt(), dcaTmp.getY(), dcaTmp.getZ())) { continue; } - - int pidTrackPosKaon{-1}; - int pidTrackPosPion{-1}; + int pidTrackKaon{-1}; + int pidTrackPion{-1}; if (cfgTrackCuts.usePidTpcOnly) { /// kaon TPC PID positive daughter - pidTrackPosKaon = selectorKaon.statusTpc(trackPos); + pidTrackKaon = selectorKaon.statusTpc(track); /// pion TPC PID positive daughter - pidTrackPosPion = selectorPion.statusTpc(trackPos); + pidTrackPion = selectorPion.statusTpc(track); } else { /// kaon TPC, TOF PID positive daughter - pidTrackPosKaon = selectorKaon.statusTpcAndTof(trackPos); + pidTrackKaon = selectorKaon.statusTpcAndTof(track); /// pion TPC, TOF PID positive daughter - pidTrackPosPion = selectorPion.statusTpcAndTof(trackPos); + pidTrackPion = selectorPion.statusTpcAndTof(track); } + if (track.sign() > 0) { // first positive track + tracksSelectedPos.push_back({trackIndex.trackId(), + dcaTmp, + trackParCov, + pidTrackPion, + pidTrackKaon}); + } else if (track.sign() < 0) { + tracksSelectedNeg.push_back({trackIndex.trackId(), + dcaTmp, + trackParCov, + pidTrackPion, + pidTrackKaon}); + } + } - for (auto const& trackIndexNeg : groupedTrackIndices) { - auto trackNeg = trackIndexNeg.template track_as(); - // track selections - if (trackNeg.sign() > 0) { // second negative track - continue; - } - if (!trackNeg.isGlobalTrackWoDCA()) { - continue; - } - if (trackNeg.pt() < cfgTrackCuts.ptMin) { - continue; - } - if (std::abs(trackNeg.eta()) > cfgTrackCuts.absEtaMax) { + for (auto const& trackSelPos : tracksSelectedPos) { + auto trackPos = tracks.rawIteratorAt(trackSelPos.globalIndex); + for (auto const& trackSelNeg : tracksSelectedNeg) { + auto trackNeg = tracks.rawIteratorAt(trackSelNeg.globalIndex); + + // preselections + // pt + std::array pVecNoVtxD0 = RecoDecay::pVec(trackPos.pVector(), trackNeg.pVector()); + float ptNoVtxD0 = RecoDecay::pt(pVecNoVtxD0); + if (ptNoVtxD0 - ptTolerance < cfgCandCuts.ptMin) { continue; } - auto trackParCovNeg = getTrackParCov(trackNeg); - o2::dataformats::DCA dcaNeg; - trackParCovNeg.propagateToDCA(primaryVertex, bz, &dcaNeg); - if (!isSelectedTrackDca(cfgTrackCuts.binsPt, cfgTrackCuts.limitsDca, trackParCovNeg.getPt(), dcaNeg.getY(), dcaNeg.getZ())) { + int ptBinNoVtxD0 = findBin(cfgTrackCuts.binsPt, ptNoVtxD0 + ptTolerance); // assuming tighter selections at lower pT + if (ptBinNoVtxD0 < 0) { continue; } - int pidTrackNegKaon{-1}; - int pidTrackNegPion{-1}; - if (cfgTrackCuts.usePidTpcOnly) { - /// kaon TPC PID negative daughter - pidTrackNegKaon = selectorKaon.statusTpc(trackNeg); - /// pion TPC PID negative daughter - pidTrackNegPion = selectorPion.statusTpc(trackNeg); - } else { - /// kaon TPC, TOF PID negative daughter - pidTrackNegKaon = selectorKaon.statusTpcAndTof(trackNeg); - /// pion TPC, TOF PID negative daughter - pidTrackNegPion = selectorPion.statusTpcAndTof(trackNeg); + // random downsampling already here + if (cfgDownsampling.apply) { + int ptBinWeights{0}; + if (ptNoVtxD0 < histDownSampl->GetBinLowEdge(1)) { + ptBinWeights = 1; + } else if (ptNoVtxD0 > histDownSampl->GetXaxis()->GetBinUpEdge(histDownSampl->GetNbinsX())) { + ptBinWeights = histDownSampl->GetNbinsX(); + } else { + ptBinWeights = histDownSampl->GetXaxis()->FindBin(ptNoVtxD0); + } + float weight = histDownSampl->GetBinContent(ptBinWeights); + if (gRandom->Rndm() > weight) { + continue; + } } - // preselections // PID uint8_t massHypo{D0MassHypo::D0AndD0Bar}; // both mass hypotheses a priori - if (pidTrackPosPion == TrackSelectorPID::Rejected || pidTrackNegKaon == TrackSelectorPID::Rejected) { + if (trackSelPos.pidPion == TrackSelectorPID::Rejected || trackSelNeg.pidKaon == TrackSelectorPID::Rejected) { massHypo -= D0MassHypo::D0; // exclude D0 } - if (pidTrackNegPion == TrackSelectorPID::Rejected || pidTrackPosKaon == TrackSelectorPID::Rejected) { + if (trackSelNeg.pidPion == TrackSelectorPID::Rejected || trackSelPos.pidKaon == TrackSelectorPID::Rejected) { massHypo -= D0MassHypo::D0Bar; // exclude D0Bar } if (massHypo == 0) { continue; } - // pt - std::array pVecNoVtxD0 = RecoDecay::pVec(trackPos.pVector(), trackNeg.pVector()); - float ptNoVtxD0 = RecoDecay::pt(pVecNoVtxD0); - if (ptNoVtxD0 - ptTolerance < cfgCandCuts.ptMin) { - continue; - } - int ptBinNoVtxD0 = findBin(cfgTrackCuts.binsPt, ptNoVtxD0 + ptTolerance); // assuming tighter selections at lower pT - if (ptBinNoVtxD0 < 0) { - continue; - } - // d0xd0 - if (dcaPos.getY() * dcaNeg.getY() > cfgCandCuts.topologicalCuts->get(ptBinNoVtxD0, "max d0d0")) { + if (trackSelPos.dca.getY() * trackSelNeg.dca.getY() > cuts->get(ptBinNoVtxD0, CutsIds::MaxD0D0)) { continue; } // invariant mass if (massHypo == D0MassHypo::D0 || massHypo == D0MassHypo::D0AndD0Bar) { float invMassNoVtxD0 = RecoDecay::m(std::array{trackPos.pVector(), trackNeg.pVector()}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); - if (std::abs(invMassNoVtxD0 - o2::constants::physics::MassD0) > cfgCandCuts.topologicalCuts->get(ptBinNoVtxD0, "delta inv. mass") + invMassTolerance) { + if (std::abs(invMassNoVtxD0 - o2::constants::physics::MassD0) > cuts->get(ptBinNoVtxD0, CutsIds::DeltaMass) + invMassTolerance) { massHypo -= D0MassHypo::D0; } } if (massHypo >= D0MassHypo::D0Bar) { float invMassNoVtxD0bar = RecoDecay::m(std::array{trackNeg.pVector(), trackPos.pVector()}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); - if (std::abs(invMassNoVtxD0bar - o2::constants::physics::MassD0) > cfgCandCuts.topologicalCuts->get(ptBinNoVtxD0, "delta inv. mass") + invMassTolerance) { + if (std::abs(invMassNoVtxD0bar - o2::constants::physics::MassD0) > cuts->get(ptBinNoVtxD0, CutsIds::DeltaMass) + invMassTolerance) { massHypo -= D0MassHypo::D0Bar; } } @@ -335,7 +358,7 @@ struct DerivedDataCreatorD0Calibration { } // reconstruct vertex - if (df.process(trackParCovPos, trackParCovNeg) == 0) { + if (df.process(trackSelPos.trackParCov, trackSelNeg.trackParCov) == 0) { continue; } const auto& secondaryVertex = df.getPCACandidate(); @@ -361,81 +384,73 @@ struct DerivedDataCreatorD0Calibration { continue; } - // random downsampling already here - if (cfgDownsampling.apply) { - int ptBinWeights{0}; - if (ptD0 < histDownSampl->GetBinLowEdge(1)) { - ptBinWeights = 1; - } else if (ptD0 > histDownSampl->GetXaxis()->GetBinUpEdge(histDownSampl->GetNbinsX())) { - ptBinWeights = histDownSampl->GetNbinsX(); - } else { - ptBinWeights = histDownSampl->GetXaxis()->FindBin(ptD0); - } - float weight = histDownSampl->GetBinContent(ptBinWeights); - if (gRandom->Rndm() > weight) { - continue; - } - } - // d0xd0 - if (dcaPos.getY() * dcaNeg.getY() > cfgCandCuts.topologicalCuts->get(ptBinD0, "max d0d0")) { + if (trackSelPos.dca.getY() * trackSelNeg.dca.getY() > cuts->get(ptBinD0, CutsIds::MaxD0D0)) { continue; } // cospa float cosPaD0 = RecoDecay::cpa(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, pVecD0); - if (cosPaD0 < cfgCandCuts.topologicalCuts->get(ptBinD0, "min cos pointing angle")) { + if (cosPaD0 < cuts->get(ptBinD0, CutsIds::MinCosPointingAngle)) { continue; } // pointing angle float paD0 = std::acos(cosPaD0); - if (paD0 > cfgCandCuts.topologicalCuts->get(ptBinD0, "max pointing angle")) { + if (paD0 > cuts->get(ptBinD0, CutsIds::MaxPointingAngle)) { continue; } // cospa XY float cosPaXYD0 = RecoDecay::cpaXY(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, pVecD0); - if (cosPaXYD0 < cfgCandCuts.topologicalCuts->get(ptBinD0, "min cos pointing angle XY")) { + if (cosPaXYD0 < cuts->get(ptBinD0, CutsIds::MinCosPointingAngleXY)) { continue; } // pointing angle XY float paXYD0 = std::acos(cosPaXYD0); - if (paXYD0 > cfgCandCuts.topologicalCuts->get(ptBinD0, "max pointing angle XY")) { + if (paXYD0 > cuts->get(ptBinD0, CutsIds::MaxPointingAngleXY)) { continue; } // decay length float decLenD0 = RecoDecay::distance(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex); - if (decLenD0 < cfgCandCuts.topologicalCuts->get(ptBinD0, "min decay length")) { + if (decLenD0 < cuts->get(ptBinD0, CutsIds::MinDecayLength)) { continue; } // decay length XY float decLenXYD0 = RecoDecay::distanceXY(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex); - if (decLenXYD0 < cfgCandCuts.topologicalCuts->get(ptBinD0, "min decay length XY")) { + if (decLenXYD0 < cuts->get(ptBinD0, CutsIds::MinDecayLengthXY)) { continue; } // normalised decay length float phi{0.f}, theta{0.f}; getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, secondaryVertex, phi, theta); float errorDecayLengthD0 = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); - if (decLenD0 / errorDecayLengthD0 < cfgCandCuts.topologicalCuts->get(ptBinD0, "min norm decay length")) { + if (decLenD0 / errorDecayLengthD0 < cuts->get(ptBinD0, CutsIds::MinNormDecayLength)) { continue; } // normalised decay length XY float errorDecayLengthXYD0 = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.f) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.f)); - if (decLenXYD0 / errorDecayLengthXYD0 < cfgCandCuts.topologicalCuts->get(ptBinD0, "min norm decay length XY")) { + if (decLenXYD0 / errorDecayLengthXYD0 < cuts->get(ptBinD0, CutsIds::MinNormDecayLengthXY)) { continue; } float invMassD0{0.f}, invMassD0bar{0.f}; float cosThetaStarD0{0.f}, cosThetaStarD0bar{0.f}; - std::vector bdtScoresD0{0.f, 1.f, 1.f}, bdtScoresD0bar{0.f, 1.f, 1.f}; // always selected a priori + // always selected a priori + bdtScoresD0[0] = 0.f; + bdtScoresD0[1] = 1.f; + bdtScoresD0[2] = 1.f; + bdtScoresD0bar[0] = 0.f; + bdtScoresD0bar[1] = 1.f; + bdtScoresD0bar[2] = 1.f; if (massHypo == D0MassHypo::D0 || massHypo == D0MassHypo::D0AndD0Bar) { invMassD0 = RecoDecay::m(std::array{pVecPos, pVecNeg}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); - if (std::abs(invMassD0 - o2::constants::physics::MassD0) > cfgCandCuts.topologicalCuts->get(ptBinD0, "delta inv. mass")) { + if (std::abs(invMassD0 - o2::constants::physics::MassD0) > cuts->get(ptBinD0, CutsIds::DeltaMass)) { massHypo -= D0MassHypo::D0; - bdtScoresD0 = std::vector{1.f, 0.f, 0.f}; + bdtScoresD0[0] = 1.f; + bdtScoresD0[1] = 0.f; + bdtScoresD0[2] = 0.f; } else { // apply BDT models if (cfgMl.apply) { - std::vector featuresCandD0 = {dcaPos.getY(), dcaNeg.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, dcaPos.getY() * dcaNeg.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; + featuresCandD0 = {trackSelPos.dca.getY(), trackSelNeg.dca.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, trackSelPos.dca.getY() * trackSelNeg.dca.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; if (!mlResponse.isSelectedMl(featuresCandD0, ptD0, bdtScoresD0)) { massHypo -= D0MassHypo::D0; } else { // selected, we compute cost* @@ -452,13 +467,15 @@ struct DerivedDataCreatorD0Calibration { } if (massHypo >= D0MassHypo::D0Bar) { invMassD0bar = RecoDecay::m(std::array{pVecNeg, pVecPos}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); - if (std::abs(invMassD0bar - o2::constants::physics::MassD0) > cfgCandCuts.topologicalCuts->get(ptBinD0, "delta inv. mass")) { + if (std::abs(invMassD0bar - o2::constants::physics::MassD0) > cuts->get(ptBinD0, CutsIds::DeltaMass)) { massHypo -= D0MassHypo::D0Bar; - bdtScoresD0bar = std::vector{1.f, 0.f, 0.f}; + bdtScoresD0bar[0] = 1.f; + bdtScoresD0bar[1] = 0.f; + bdtScoresD0bar[2] = 0.f; } else { // apply BDT models if (cfgMl.apply) { - std::vector featuresCandD0bar = {dcaPos.getY(), dcaNeg.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, dcaPos.getY() * dcaNeg.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi())}; + featuresCandD0bar = {trackSelPos.dca.getY(), trackSelNeg.dca.getY(), chi2PCA, cosPaD0, cosPaXYD0, decLenXYD0, decLenD0, trackSelPos.dca.getY() * trackSelNeg.dca.getY(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaPi(), trackPos.tofNSigmaPi()), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaKa(), trackNeg.tofNSigmaKa()), trackPos.tpcNSigmaPi(), trackPos.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackPos.tpcNSigmaKa(), trackPos.tofNSigmaKa()), trackNeg.tpcNSigmaPi(), trackNeg.tpcNSigmaKa(), aod::pid_tpc_tof_utils::combineNSigma(trackNeg.tpcNSigmaPi(), trackNeg.tofNSigmaPi())}; if (!mlResponse.isSelectedMl(featuresCandD0bar, ptD0, bdtScoresD0bar)) { massHypo -= D0MassHypo::D0Bar; } else { // selected, we compute cost* @@ -494,7 +511,7 @@ struct DerivedDataCreatorD0Calibration { collision.covYZ(), collision.covZZ(), collision.numContrib(), - uint8_t(std::round(collision.centFT0C())), + static_cast(std::round(collision.centFT0C())), getCompressedOccupancy(collision.trackOccupancyInTimeRange()), getCompressedOccupancy(collision.ft0cOccupancyInTimeRange()), eventIR.orbit, @@ -514,18 +531,20 @@ struct DerivedDataCreatorD0Calibration { uint8_t twmoFT0CUnfm80{0u}; uint8_t tmoRobustT0V0PrimUnfm80{0u}; uint8_t twmoRobustT0V0PrimUnfm80{0u}; - if (trackPos.has_tmo()) { - auto tmoFromTrack = trackPos.template tmo_as(); // obtain track mean occupancies - tmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoPrimUnfm80()); - tmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFV0AUnfm80()); - tmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0AUnfm80()); - tmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0CUnfm80()); - twmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoPrimUnfm80()); - twmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFV0AUnfm80()); - twmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0AUnfm80()); - twmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0CUnfm80()); - tmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoRobustT0V0PrimUnfm80()); - twmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoRobustT0V0PrimUnfm80()); + if constexpr (withTrackOcc) { + if (trackPos.has_tmo()) { + auto tmoFromTrack = trackPos.template tmo_as(); // obtain track mean occupancies + tmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoPrimUnfm80()); + tmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFV0AUnfm80()); + tmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0AUnfm80()); + tmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0CUnfm80()); + twmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoPrimUnfm80()); + twmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFV0AUnfm80()); + twmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0AUnfm80()); + twmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0CUnfm80()); + tmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoRobustT0V0PrimUnfm80()); + twmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoRobustT0V0PrimUnfm80()); + } } float tpcTime0{0.f}; float tpcdEdxNorm{0.f}; @@ -649,8 +668,8 @@ struct DerivedDataCreatorD0Calibration { deltaRefGloParamQ2Pt, deltaTOFdX, deltaTOFdZ, - o2::math_utils::detail::truncateFloatFraction(dcaPos.getY(), precisionDcaMask), - o2::math_utils::detail::truncateFloatFraction(dcaPos.getZ(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelPos.dca.getY(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelPos.dca.getZ(), precisionDcaMask), getCompressedNumSigmaPid(trackPos.tpcNSigmaPi()), getCompressedNumSigmaPid(trackPos.tpcNSigmaKa()), getCompressedNumSigmaPid(trackPos.tofNSigmaPi()), @@ -679,18 +698,20 @@ struct DerivedDataCreatorD0Calibration { uint8_t twmoFT0CUnfm80{0u}; uint8_t tmoRobustT0V0PrimUnfm80{0u}; uint8_t twmoRobustT0V0PrimUnfm80{0u}; - if (trackNeg.has_tmo()) { - auto tmoFromTrack = trackNeg.template tmo_as(); // obtain track mean occupancies - tmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoPrimUnfm80()); - tmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFV0AUnfm80()); - tmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0AUnfm80()); - tmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0CUnfm80()); - twmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoPrimUnfm80()); - twmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFV0AUnfm80()); - twmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0AUnfm80()); - twmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0CUnfm80()); - tmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoRobustT0V0PrimUnfm80()); - twmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoRobustT0V0PrimUnfm80()); + if constexpr (withTrackOcc) { + if (trackNeg.has_tmo()) { + auto tmoFromTrack = trackNeg.template tmo_as(); // obtain track mean occupancies + tmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoPrimUnfm80()); + tmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFV0AUnfm80()); + tmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0AUnfm80()); + tmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoFT0CUnfm80()); + twmoPrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoPrimUnfm80()); + twmoFV0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFV0AUnfm80()); + twmoFT0AUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0AUnfm80()); + twmoFT0CUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoFT0CUnfm80()); + tmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.tmoRobustT0V0PrimUnfm80()); + twmoRobustT0V0PrimUnfm80 = getCompressedOccupancy(tmoFromTrack.twmoRobustT0V0PrimUnfm80()); + } } float tpcTime0{0.f}; float tpcdEdxNorm{0.f}; @@ -814,8 +835,8 @@ struct DerivedDataCreatorD0Calibration { deltaRefGloParamQ2Pt, deltaTOFdX, deltaTOFdZ, - o2::math_utils::detail::truncateFloatFraction(dcaNeg.getY(), precisionDcaMask), - o2::math_utils::detail::truncateFloatFraction(dcaNeg.getZ(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelNeg.dca.getY(), precisionDcaMask), + o2::math_utils::detail::truncateFloatFraction(trackSelNeg.dca.getZ(), precisionDcaMask), getCompressedNumSigmaPid(trackNeg.tpcNSigmaPi()), getCompressedNumSigmaPid(trackNeg.tpcNSigmaKa()), getCompressedNumSigmaPid(trackNeg.tofNSigmaPi()), @@ -868,24 +889,33 @@ struct DerivedDataCreatorD0Calibration { // process functions void processWithTrackQa(CollisionsWEvSel const& collisions, aod::TrackAssoc const& trackIndices, - TracksWCovExtraPidAndQa const& tracks, + TracksWCovExtraTmoPidAndQa const& tracks, aod::BCsWithTimestamps const& bcs, TrackMeanOccs const& occ, aod::TracksQAVersion const& trackQa) { - runDataCreation(collisions, trackIndices, tracks, bcs, occ, trackQa); + runDataCreation(collisions, trackIndices, tracks, bcs, occ, trackQa); } PROCESS_SWITCH(DerivedDataCreatorD0Calibration, processWithTrackQa, "Process with trackQA enabled", false); void processNoTrackQa(CollisionsWEvSel const& collisions, aod::TrackAssoc const& trackIndices, - TracksWCovExtraPid const& tracks, + TracksWCovExtraTmoPid const& tracks, aod::BCsWithTimestamps const& bcs, TrackMeanOccs const& occ) { - runDataCreation(collisions, trackIndices, tracks, bcs, occ, nullptr); + runDataCreation(collisions, trackIndices, tracks, bcs, occ, nullptr); + } + PROCESS_SWITCH(DerivedDataCreatorD0Calibration, processNoTrackQa, "Process without trackQA enabled", false); + + void processNoTrackQaAndOcc(CollisionsWEvSel const& collisions, + aod::TrackAssoc const& trackIndices, + TracksWCovExtraPid const& tracks, + aod::BCsWithTimestamps const& bcs) + { + runDataCreation(collisions, trackIndices, tracks, bcs, nullptr, nullptr); } - PROCESS_SWITCH(DerivedDataCreatorD0Calibration, processNoTrackQa, "Process without trackQA enabled", true); + PROCESS_SWITCH(DerivedDataCreatorD0Calibration, processNoTrackQaAndOcc, "Process without trackQA and trackMeanOcc enabled", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/DPG/Tasks/AOTTrack/qaEfficiency.cxx b/DPG/Tasks/AOTTrack/qaEfficiency.cxx index 20abc845190..22d7b6b5497 100644 --- a/DPG/Tasks/AOTTrack/qaEfficiency.cxx +++ b/DPG/Tasks/AOTTrack/qaEfficiency.cxx @@ -110,6 +110,9 @@ std::array, nParticles> hPtGeneratedRecoEv; std::array, nParticles> hPtItsPrm; std::array, nParticles> hPtItsTpcPrm; std::array, nParticles> hPtTrkItsTpcPrm; +std::array, nParticles> hDeltaPtVsPtTrkItsTpcPrm; +std::array, nParticles> hPtGenVsPtTrkItsTpcPrm; +std::array, nParticles> hPtGenVsPIDTrkItsTpcPrm; std::array, nParticles> hPtItsTpcTofPrm; std::array, nParticles> hPtTrkItsTpcTofPrm; std::array, nParticles> hPtGeneratedPrm; @@ -325,6 +328,7 @@ struct QaEfficiency { const AxisSpec axisPhi{phiBins, "#it{#varphi} (rad)"}; const AxisSpec axisRadius{radiusBins, "Radius (cm)"}; const AxisSpec axisOcc{occBins, "Occupancy"}; + const AxisSpec axisPIDFlag{16, -0.5, 15.5, "PID flag (top 4 bits)"}; const char* partName = particleName(pdgSign, id); LOG(info) << "Preparing histograms for particle: " << partName << " pdgSign " << pdgSign; @@ -376,6 +380,9 @@ struct QaEfficiency { hPtItsPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/its", PDGs[histogramIndex]), "ITS tracks (primaries) " + tagPt, kTH1D, {axisPt}); hPtItsTpcPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/its_tpc", PDGs[histogramIndex]), "ITS-TPC tracks (primaries) " + tagPt, kTH1D, {axisPt}); hPtTrkItsTpcPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/trk/its_tpc", PDGs[histogramIndex]), "ITS-TPC tracks (reco primaries) " + tagPt, kTH1D, {axisPt}); + hDeltaPtVsPtTrkItsTpcPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/generated_vs_reco_delta", PDGs[histogramIndex]), "Abs(Gen - Reco) pT vs Gen pT (primaries) " + tagPt, kTH2D, {axisPt, axisPt}); + hPtGenVsPtTrkItsTpcPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/generated_vs_reco", PDGs[histogramIndex]), "Reco pT vs Gen pT (primaries) " + tagPt, kTH2D, {axisPt, axisPt}); + hPtGenVsPIDTrkItsTpcPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/generated_vs_reco_pid_tracking", PDGs[histogramIndex]), "PID for tracking vs Gen pT (primaries) " + tagPt, kTH2D, {axisPt, axisPIDFlag}); hPtItsTpcTofPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/its_tpc_tof", PDGs[histogramIndex]), "ITS-TPC-TOF tracks (primaries) " + tagPt, kTH1D, {axisPt}); hPtTrkItsTpcTofPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/trk/its_tpc_tof", PDGs[histogramIndex]), "ITS-TPC-TOF tracks (reco primaries) " + tagPt, kTH1D, {axisPt}); hPtGeneratedPrm[histogramIndex] = histos.add(Form("MC/pdg%i/pt/prm/generated", PDGs[histogramIndex]), "Generated (primaries) " + tagPt, kTH1D, {axisPt}); @@ -493,7 +500,7 @@ struct QaEfficiency { subList->SetName(partName); listEfficiencyMC->Add(subList); - auto makeEfficiency = [&](const TString effname, auto h) { // 1D efficiencies + auto makeEfficiency = [&](const TString& effname, const auto& h) { // 1D efficiencies LOG(debug) << " Making 1D TEfficiency " << effname << " from " << h->GetName(); const TAxis* axis = h->GetXaxis(); TString efftitle = h->GetTitle(); @@ -556,7 +563,7 @@ struct QaEfficiency { makeEfficiency("ITS-TPC_vsPhi_Prm_Trk", hPhiTrkItsTpcPrm[histogramIndex]); makeEfficiency("ITS-TPC-TOF_vsPhi_Prm", hPhiItsTpcTofPrm[histogramIndex]); - auto makeEfficiency2D = [&](const TString effname, auto h) { // 2D efficiencies + auto makeEfficiency2D = [&](const TString& effname, const auto& h) { // 2D efficiencies LOG(debug) << " Making 2D TEfficiency " << effname << " from " << h->GetName(); const TAxis* axisX = h->GetXaxis(); const TAxis* axisY = h->GetYaxis(); @@ -891,7 +898,7 @@ struct QaEfficiency { listEfficiencyData.setObject(new THashList); if (makeEff) { LOG(debug) << "Making TEfficiency for Data"; - auto makeEfficiency = [&](TString effname, TString efftitle, auto templateHisto, TEfficiency*& eff) { + auto makeEfficiency = [&](const TString& effname, const TString& efftitle, auto templateHisto, TEfficiency*& eff) { TAxis* axis = histos.get(templateHisto)->GetXaxis(); if (axis->IsVariableBinSize()) { eff = new TEfficiency(effname, efftitle, axis->GetNbins(), axis->GetXbins()->GetArray()); @@ -920,7 +927,7 @@ struct QaEfficiency { "TPC-TOF M.E. in data " + tagPhi + ";#it{#varphi} (rad);Efficiency", HIST("Data/pos/phi/its_tpc_tof"), effTPCTOFMatchingVsPhi); - auto makeEfficiency2D = [&](TString effname, TString efftitle, auto templateHistoX, auto templateHistoY, TEfficiency*& eff) { + auto makeEfficiency2D = [&](const TString& effname, const TString& efftitle, auto templateHistoX, auto templateHistoY, TEfficiency*& eff) { TAxis* axisX = histos.get(templateHistoX)->GetXaxis(); TAxis* axisY = histos.get(templateHistoY)->GetYaxis(); if (axisX->IsVariableBinSize() || axisY->IsVariableBinSize()) { @@ -1156,6 +1163,9 @@ struct QaEfficiency { if (passedITS && passedTPC) { hPtItsTpcPrm[histogramIndex]->Fill(mcParticle.pt()); hPtTrkItsTpcPrm[histogramIndex]->Fill(track.pt()); + hDeltaPtVsPtTrkItsTpcPrm[histogramIndex]->Fill(mcParticle.pt(), abs(track.pt() - mcParticle.pt())); + hPtGenVsPtTrkItsTpcPrm[histogramIndex]->Fill(mcParticle.pt(), track.pt()); + hPtGenVsPIDTrkItsTpcPrm[histogramIndex]->Fill(mcParticle.pt(), track.pidForTracking()); hEtaItsTpcPrm[histogramIndex]->Fill(mcParticle.eta()); hEtaTrkItsTpcPrm[histogramIndex]->Fill(track.eta()); hPhiItsTpcPrm[histogramIndex]->Fill(mcParticle.phi()); @@ -1349,15 +1359,15 @@ struct QaEfficiency { const char* partName = particleName(pdgSign, id); LOG(debug) << "Filling efficiency for particle " << static_cast(id) << " " << partName; - THashList* subList = static_cast(listEfficiencyMC->FindObject(partName)); + THashList* subList = dynamic_cast(listEfficiencyMC->FindObject(partName)); if (!subList) { LOG(warning) << "Cannot find list of efficiency objects for particle " << partName; return; } // Filling 1D efficiencies - auto doFillEfficiency = [&](const TString effname, auto num, auto den) { - TEfficiency* eff = static_cast(subList->FindObject(effname)); + auto doFillEfficiency = [&](const TString& effname, const auto& num, const auto& den) { + TEfficiency* eff = dynamic_cast(subList->FindObject(effname)); if (!eff) { LOG(warning) << "Cannot find TEfficiency " << effname; return; @@ -1426,8 +1436,8 @@ struct QaEfficiency { } // Filling 2D efficiencies - auto fillEfficiency2D = [&](const TString effname, auto num, auto den) { - TEfficiency* eff = static_cast(subList->FindObject(effname)); + auto fillEfficiency2D = [&](const TString& effname, const auto& num, const auto& den) { + TEfficiency* eff = dynamic_cast(subList->FindObject(effname)); if (!eff) { LOG(warning) << "Cannot find TEfficiency " << effname; return; diff --git a/DPG/Tasks/AOTTrack/qaEventTrack.cxx b/DPG/Tasks/AOTTrack/qaEventTrack.cxx index b4baddb2685..57e901ee54f 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrack.cxx +++ b/DPG/Tasks/AOTTrack/qaEventTrack.cxx @@ -21,8 +21,6 @@ /// \brief Task to produce QA objects for the track and the event properties in the AOD. /// -#include "qaEventTrack.h" - #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/MetadataHelper.h" #include "Common/DataModel/EventSelection.h" diff --git a/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx b/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx index 61938e1d348..a77a17c11d1 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx +++ b/DPG/Tasks/AOTTrack/qaEventTrackLite.cxx @@ -18,7 +18,7 @@ /// This task runs on prefiltered data /// -#include "qaEventTrack.h" +#include "qaEventTrackLite.h" #include #include @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -153,7 +154,7 @@ struct qaEventTrackLite { /// return initBBok ? mMip * o2::common::BetheBlochAleph(x[0] / par[0], mBetheBlockAleph[0], mBetheBlockAleph[1], mBetheBlockAleph[2], mBetheBlockAleph[3], mBetheBlockAleph[4]) * std::pow(par[1], mChargeFactor) : 0.; } - void setUpBetheBlockAleph(std::string str_case) + void setUpBetheBlockAleph(const std::string& str_case) { if (str_case.find("LHC22c") != std::string::npos) { // From A. Kalteyer (2022 Jul 18) @@ -335,8 +336,8 @@ struct qaEventTrackLite { Filter itsChi2 = (bTpcOnly.node() == true) || (chi2ItsMin < o2::aod::track::itsChi2NCl && o2::aod::track::itsChi2NCl < chi2ItsMax); // TPC Filter tpcChi2s = (bItsStandalone.node() == true) || (chi2TpcMin < o2::aod::track::tpcChi2NCl && o2::aod::track::tpcChi2NCl < chi2TpcMax); - Filter tpcNclusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsFound > (int16_t)nClusterTpcMin); - Filter tpcNcrossedRows = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsCrossedRows > (int16_t)nCrossedRowsTpcMin); + Filter tpcNclusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsFound > static_cast(nClusterTpcMin)); + Filter tpcNcrossedRows = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcNClsCrossedRows > static_cast(nCrossedRowsTpcMin)); Filter tpcNcrossedRowsOverFindableClusters = (bItsStandalone.node() == true) || (o2::aod::dpgtrack::tpcCrossedRowsOverFindableCls > nCrossedRowsTpcOverFindableClustersTpcMin); // TOF Filter tofChi = o2::aod::track::tofChi2 > chi2TofMin; @@ -417,7 +418,7 @@ struct qaEventTrackLite { histos.fill(HIST("Tracks/TPC/dEdxvsP"), p, track.tpcSignal()); histos.fill(HIST("Tracks/TPC/dEdxvsPvsEta"), p, track.eta(), track.tpcSignal()); if (betheBlock.initBBok) { - auto tpcdEdxRes = [&](TF1 func) { return track.tpcSignal() - func.Eval(p); }; + auto tpcdEdxRes = [&](const TF1& func) { return track.tpcSignal() - func.Eval(p); }; if (b_tpcResProton) { histos.fill(HIST("Tracks/TPC/dEdxvsPproton"), p, tpcdEdxRes(funcBBproton)); histos.fill(HIST("Tracks/TPC/dEdxvsPprotonvsEta"), p, track.eta(), tpcdEdxRes(funcBBproton)); diff --git a/DPG/Tasks/AOTTrack/qaEventTrack.h b/DPG/Tasks/AOTTrack/qaEventTrackLite.h similarity index 97% rename from DPG/Tasks/AOTTrack/qaEventTrack.h rename to DPG/Tasks/AOTTrack/qaEventTrackLite.h index edd9512c5ec..9ea778698b7 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrack.h +++ b/DPG/Tasks/AOTTrack/qaEventTrackLite.h @@ -10,15 +10,15 @@ // or submit itself to any jurisdiction. /// -/// \file qaEventTrack.h +/// \file qaEventTrackLite.h /// \author Mario Krüger /// \author Mattia Faggin /// \author Nicolò Jacazio /// \brief Header file for QA tasks for the track and the event properties. /// -#ifndef DPG_TASKS_AOTTRACK_QAEVENTTRACK_H_ -#define DPG_TASKS_AOTTRACK_QAEVENTTRACK_H_ +#ifndef DPG_TASKS_AOTTRACK_QAEVENTTRACKLITE_H_ +#define DPG_TASKS_AOTTRACK_QAEVENTTRACKLITE_H_ #include "Common/DataModel/TrackSelectionTables.h" @@ -106,7 +106,7 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); //! Eta DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi DECLARE_SOA_COLUMN(PtReso, ptReso, float); //! Pt resolution -DECLARE_SOA_COLUMN(Sign, sign, short); //! Sign +DECLARE_SOA_COLUMN(Sign, sign, int16_t); //! Sign DECLARE_SOA_COLUMN(HasITS, hasITS, bool); //! Track has the ITS DECLARE_SOA_COLUMN(HasTPC, hasTPC, bool); //! Track has the TPC DECLARE_SOA_COLUMN(HasTRD, hasTRD, bool); //! Track has the TRD @@ -170,4 +170,4 @@ DECLARE_SOA_TABLE(DPGNonRecoParticles, "AOD", "DPGNonRecoPart", //! Table of the mcparticle::Vx, mcparticle::Vy, mcparticle::Vz); } // namespace o2::aod -#endif // DPG_TASKS_AOTTRACK_QAEVENTTRACK_H_ +#endif // DPG_TASKS_AOTTRACK_QAEVENTTRACKLITE_H_ diff --git a/DPG/Tasks/AOTTrack/qaEventTrackLiteProducer.cxx b/DPG/Tasks/AOTTrack/qaEventTrackLiteProducer.cxx index 8e41e7a24cc..9c5285ca358 100644 --- a/DPG/Tasks/AOTTrack/qaEventTrackLiteProducer.cxx +++ b/DPG/Tasks/AOTTrack/qaEventTrackLiteProducer.cxx @@ -17,7 +17,7 @@ /// \brief Task to produce a table with reduced information used for correlation studies for track selection, ideally used with qaEventTrackite /// -#include "qaEventTrack.h" +#include "qaEventTrackLite.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" diff --git a/DPG/Tasks/AOTTrack/qaImpPar.cxx b/DPG/Tasks/AOTTrack/qaImpPar.cxx index 7e758dd7ec2..28d1db77bd9 100644 --- a/DPG/Tasks/AOTTrack/qaImpPar.cxx +++ b/DPG/Tasks/AOTTrack/qaImpPar.cxx @@ -116,6 +116,7 @@ struct QaImpactPar { Configurable nCustomMinITShits{"n_customMinITShits", 0, "Minimum number of layers crossed by a track among those in \"customITShitmap\""}; Configurable customForceITSTPCmatching{"custom_forceITSTPCmatching", false, "Consider or not only ITS-TPC macthed tracks when using custom ITS hitmap"}; Configurable downsamplingFraction{"downsamplingFraction", 1.1, "Fraction of tracks to be used to fill the output objects"}; + Configurable eventGeneratorHF{"eventGeneratorHF", -1, "If positive, enable event selection using subGeneratorId information (HF). The value indicates which events to keep (0 = MB, 4 = charm triggered, 5 = beauty triggered)"}; /// Custom cut selection objects TrackSelection selector_ITShitmap; @@ -201,6 +202,12 @@ struct QaImpactPar { const o2::aod::McCollisions&, o2::aod::BCsWithTimestamps const&) { + /// SubgeneratorID check for HF MC + /// Useful to select MB gap events in HF-dedicated MC productions + if (eventGeneratorHF >= 0 && collision.mcCollision().getSubGeneratorId() != eventGeneratorHF) { + return; + } + /// here call the template processReco function auto bc = collision.bc_as(); processReco(collision, tracksUnfiltered, tracks, tracksIU, mcParticles, bc); @@ -216,6 +223,12 @@ struct QaImpactPar { const o2::aod::McCollisions&, o2::aod::BCsWithTimestamps const&) { + /// SubgeneratorID check for HF MC + /// Useful to select MB gap events in HF-dedicated MC productions + if (eventGeneratorHF >= 0 && collision.mcCollision().getSubGeneratorId() != eventGeneratorHF) { + return; + } + /// here call the template processReco function auto bc = collision.bc_as(); processReco(collision, tracksUnfiltered, tracks, tracksIU, mcParticles, bc); diff --git a/DPG/Tasks/AOTTrack/qaMatchEff.cxx b/DPG/Tasks/AOTTrack/qaMatchEff.cxx index d34c897ac55..09090795713 100644 --- a/DPG/Tasks/AOTTrack/qaMatchEff.cxx +++ b/DPG/Tasks/AOTTrack/qaMatchEff.cxx @@ -1565,7 +1565,7 @@ struct qaMatchEff { // // here n of clusters of TPC assigned to float for histos (and to hack it if needed) :) // - Float_t clustpc = (Float_t)track.tpcNClsFound(); + Float_t clustpc = static_cast(track.tpcNClsFound()); // Float_t findcltpc = (Float_t)track.tpcNClsFindable(); // Float_t crowstpc = (Float_t)track.tpcNClsCrossedRows(); // Float_t finclusmincrotpc = (Float_t)track.tpcNClsFindableMinusCrossedRows(); diff --git a/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx b/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx index 680cb819b34..97ba21592f9 100644 --- a/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx +++ b/DPG/Tasks/AOTTrack/qaTrackSplitting.cxx @@ -99,8 +99,8 @@ struct qaTrackSplitting { using TrackCandidates = o2::soa::Join; Filter trackFilterEta = nabs(aod::track::eta - cfgCustomTrackCuts.windowEta) < cfgCustomTrackCuts.windowEtaWidth; Filter trackFilterPhi = nabs(aod::track::phi - cfgCustomTrackCuts.windowPhi) < cfgCustomTrackCuts.windowPhiWidth; - Filter trackFilterITS = (aod::track::itsClusterSizes > (uint32_t)0); - Filter trackFilterTPC = (aod::track::tpcNClsFindable > (uint8_t)0); + Filter trackFilterITS = (aod::track::itsClusterSizes > static_cast(0)); + Filter trackFilterTPC = (aod::track::tpcNClsFindable > static_cast(0)); // Filter trackFilterType = (aod::track::TrackType == aod::track::Track); // Filter filterPt = nabs(aod::track::pt - cfgCustomTrackCuts.windowPt) < cfgCustomTrackCuts.windowPtWidth; void processData(CollisionCandidates const& collisions, @@ -134,10 +134,11 @@ struct qaTrackSplitting { if (!collision.sel8()) { return; } - typedef std::shared_ptr trkType; + using TrackType = const TrackCandidatesMC::iterator; + using TrackTypePtr = std::shared_ptr; - std::map> particleUsageCounter; - for (auto track : tracks) { + std::map> particleUsageCounter; + for (const auto& track : tracks) { histos.fill(HIST("tracks"), 0); if (!track.has_mcParticle()) { continue; @@ -156,7 +157,7 @@ struct qaTrackSplitting { continue; } histos.fill(HIST("tracks"), 4); - particleUsageCounter[track.mcParticleId()].push_back(std::make_shared(track)); + particleUsageCounter[track.mcParticleId()].push_back(std::make_shared(track)); } for (const auto& [mcId, tracksMatched] : particleUsageCounter) { histos.fill(HIST("numberOfRecoed"), tracksMatched.size()); diff --git a/DPG/Tasks/AOTTrack/tagAndProbeDmesons.cxx b/DPG/Tasks/AOTTrack/tagAndProbeDmesons.cxx index 842f167d7d2..7fbc609af28 100644 --- a/DPG/Tasks/AOTTrack/tagAndProbeDmesons.cxx +++ b/DPG/Tasks/AOTTrack/tagAndProbeDmesons.cxx @@ -1296,7 +1296,7 @@ struct ProbeThirdTrack { } template - void loopOverThirdTrack(TTrackIndices const& groupedTrackThirdIndices, TTracks const& /*tracks*/, TTrack const& trackFirst, TTrack const& trackSecond, PParticles const mcParticles, const int motherIdxTag, const float radius) + void loopOverThirdTrack(TTrackIndices const& groupedTrackThirdIndices, TTracks const& /*tracks*/, TTrack const& trackFirst, TTrack const& trackSecond, PParticles const& mcParticles, const int motherIdxTag, const float radius) { for (const auto& trackIndex : groupedTrackThirdIndices) { auto trackThird = trackIndex.template track_as(); diff --git a/DPG/Tasks/CMakeLists.txt b/DPG/Tasks/CMakeLists.txt index 642f8d6e772..8f58550868f 100644 --- a/DPG/Tasks/CMakeLists.txt +++ b/DPG/Tasks/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(AOTTrack) add_subdirectory(AOTEvent) add_subdirectory(TPC) add_subdirectory(TOF) +add_subdirectory(FIT) add_subdirectory(FV0) add_subdirectory(FDD) add_subdirectory(MFT) diff --git a/DPG/Tasks/FIT/CMakeLists.txt b/DPG/Tasks/FIT/CMakeLists.txt new file mode 100644 index 00000000000..db76da4e4aa --- /dev/null +++ b/DPG/Tasks/FIT/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(fit-all + SOURCES fitAll.cxx + PUBLIC_LINK_LIBRARIES O2Physics::DataModel O2::Framework O2::FV0Base O2::FDDBase + COMPONENT_NAME Analysis) diff --git a/DPG/Tasks/FIT/fitAll.cxx b/DPG/Tasks/FIT/fitAll.cxx new file mode 100644 index 00000000000..48084c73e94 --- /dev/null +++ b/DPG/Tasks/FIT/fitAll.cxx @@ -0,0 +1,482 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file fitAll.cxx +/// \brief FITAll table definition and producer. Standalone derived data used for FIT studies. +/// +/// \author Andreas Molander andreas.molander@cern.ch + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FT0Corrected.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using BCsWithTimestamps = soa::Join; + +namespace o2::aod +{ +namespace fit +{ + +// Constants +static constexpr int NchFt0 = 208; ///< Number of FT0 channels +static constexpr int NchFt0A = 96; ///< Number of FT0A channels +static constexpr int NchFv0 = o2::fv0::Constants::nFv0Channels; ///< Number of FV0 channels +static constexpr int NchFdd = o2::fdd::Nchannels; ///< Number of FDD channels + +// BCs +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); +DECLARE_SOA_COLUMN(GlobalBc, globalBc, uint64_t); +DECLARE_SOA_COLUMN(CtpTriggerMask, ctpTriggerMask, uint64_t); +DECLARE_SOA_COLUMN(CtpInputMask, ctpInputMask, uint64_t); + +// Timestamps +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); + +// EvSels +DECLARE_SOA_BITMAP_COLUMN(TriggerAlias, triggerAlias, 32); +DECLARE_SOA_BITMAP_COLUMN(SelectionFlags, selectionFlags, 64); +DECLARE_SOA_BITMAP_COLUMN(RctFlags, rctFlags, 32); +DECLARE_SOA_COLUMN(Sel8, sel8, bool); +DECLARE_SOA_COLUMN(HasFt0, hasFt0, bool); +DECLARE_SOA_COLUMN(HasFv0, hasFv0, bool); +DECLARE_SOA_COLUMN(HasFdd, hasFdd, bool); +DECLARE_SOA_COLUMN(HasZdc, hasZdc, bool); + +// Collisions +DECLARE_SOA_COLUMN(BcId, bcId, int32_t); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(Flags, flags, uint16_t); +DECLARE_SOA_COLUMN(NumContrib, numContrib, uint16_t); +DECLARE_SOA_COLUMN(CollisionTime, collisionTime, float); +DECLARE_SOA_COLUMN(CollisionTimeRes, collisionTimeRes, float); + +// Mults +DECLARE_SOA_COLUMN(MultFt0A, multFt0A, float); +DECLARE_SOA_COLUMN(MultFt0C, multFt0C, float); +DECLARE_SOA_COLUMN(MultFv0A, multFv0A, float); +DECLARE_SOA_COLUMN(MultFddA, multFddA, float); +DECLARE_SOA_COLUMN(MultFddC, multFddC, float); +DECLARE_SOA_COLUMN(MultZnA, multZnA, float); +DECLARE_SOA_COLUMN(MultZnC, multZnC, float); +DECLARE_SOA_COLUMN(MultZem1, multZem1, float); +DECLARE_SOA_COLUMN(MultZem2, multZem2, float); +DECLARE_SOA_COLUMN(MultZpA, multZpA, float); +DECLARE_SOA_COLUMN(MultZpC, multZpC, float); + +// FT0s +DECLARE_SOA_COLUMN(Ft0BcId, ft0BcId, int32_t); +DECLARE_SOA_COLUMN(Ft0AmplitudeA, ft0AmplitudeA, std::vector); +DECLARE_SOA_COLUMN(Ft0ChannelA, ft0ChannelA, std::vector); +DECLARE_SOA_COLUMN(Ft0AmplitudeC, ft0AmplitudeC, std::vector); +DECLARE_SOA_COLUMN(Ft0ChannelC, ft0ChannelC, std::vector); +DECLARE_SOA_COLUMN(Ft0TimeA, ft0TimeA, float); +DECLARE_SOA_COLUMN(Ft0TimeC, ft0TimeC, float); +DECLARE_SOA_COLUMN(Ft0TriggerMask, ft0TriggerMask, uint8_t); +DECLARE_SOA_COLUMN(Ft0PosZ, ft0PosZ, float); +DECLARE_SOA_COLUMN(Ft0CollTime, ft0CollTime, float); +DECLARE_SOA_COLUMN(Ft0SumAmpA, ft0SumAmpA, float); +DECLARE_SOA_COLUMN(Ft0SumAmpC, ft0SumAmpC, float); + +// FT0sCorrected +DECLARE_SOA_COLUMN(T0aCorrected, t0aCorrected, float); +DECLARE_SOA_COLUMN(T0cCorrected, t0cCorrected, float); +DECLARE_SOA_COLUMN(T0ac, t0ac, float); +DECLARE_SOA_COLUMN(T0resolution, t0resolution, float); + +// FT0 derived quantities +DECLARE_SOA_COLUMN(Ft0ChAmpl, ft0ChAmpl, std::vector); ///< FT0 channel amplitudes, vector idx = ch ID +DECLARE_SOA_COLUMN(Ft0TotAmplA, ft0TotAmplA, float); ///< FT0-A total amplitude computed from channel amplitudes (for cross check) +DECLARE_SOA_COLUMN(Ft0TotAmplC, ft0TotAmplC, float); ///< FT0-C total amplitude computed from channel amplitudes (for cross check) + +// FV0As +DECLARE_SOA_COLUMN(Fv0BcId, fv0BcId, int32_t); +DECLARE_SOA_COLUMN(Fv0Amplitude, fv0Amplitude, std::vector); +DECLARE_SOA_COLUMN(Fv0Channel, fv0Channel, std::vector); +DECLARE_SOA_COLUMN(Fv0Time, fv0Time, float); +DECLARE_SOA_COLUMN(Fv0TriggerMask, fv0TriggerMask, uint8_t); + +// FV0 derived quantities +DECLARE_SOA_COLUMN(Fv0ChAmpl, fv0ChAmpl, std::vector); ///< FV0 channel amplitudes, vector idx = ch ID +DECLARE_SOA_COLUMN(Fv0TotAmpl, fv0TotAmpl, float); ///< FV0 total amplitude computed from channel amplitudes (for cross check) + +// FDDs +DECLARE_SOA_COLUMN(FddBcId, fddBcId, int32_t); +DECLARE_SOA_COLUMN(FddChargeA, fddChargeA, int16_t[8]); +DECLARE_SOA_COLUMN(FddChargeC, fddChargeC, int16_t[8]); +DECLARE_SOA_COLUMN(FddTimeA, fddTimeA, float); +DECLARE_SOA_COLUMN(FddTimeC, fddTimeC, float); +DECLARE_SOA_COLUMN(FddTriggerMask, fddTriggerMask, uint8_t); + +// FDD derived quantities +DECLARE_SOA_COLUMN(FddChAmpl, fddChAmpl, std::vector); ///< FDD channel amplitudes, vector idx = ch ID +DECLARE_SOA_COLUMN(FddTotAmplA, fddTotAmplA, float); ///< FDD-A total amplitude computed from channel amplitudes (for cross check) +DECLARE_SOA_COLUMN(FddTotAmplC, fddTotAmplC, float); ///< FDD-C total amplitude computed from channel amplitudes (for cross check) + +// ZDCs +DECLARE_SOA_COLUMN(EnergyCommonZnA, energyCommonZnA, float); +DECLARE_SOA_COLUMN(EnergyCommonZnC, energyCommonZnC, float); +DECLARE_SOA_COLUMN(TimeZem1, timeZem1, float); +DECLARE_SOA_COLUMN(TimeZem2, timeZem2, float); + +} // namespace fit + +DECLARE_SOA_TABLE(FITsAll, "AOD", "FITALL", ///< Standalone derived data used for FIT studies. + fit::RunNumber, fit::GlobalBc, fit::CtpTriggerMask, fit::CtpInputMask, + fit::Timestamp, + fit::TriggerAlias, fit::SelectionFlags, fit::RctFlags, fit::Sel8, fit::HasFt0, fit::HasFv0, fit::HasFdd, fit::HasZdc, + fit::BcId, fit::PosX, fit::PosY, fit::PosZ, fit::Flags, fit::NumContrib, fit::CollisionTime, fit::CollisionTimeRes, + fit::MultFt0A, fit::MultFt0C, fit::MultFv0A, fit::MultFddA, fit::MultFddC, fit::MultZnA, fit::MultZnC, fit::MultZem1, fit::MultZem2, fit::MultZpA, fit::MultZpC, + fit::Ft0BcId, fit::Ft0AmplitudeA, fit::Ft0ChannelA, fit::Ft0AmplitudeC, fit::Ft0ChannelC, + fit::Ft0TimeA, fit::Ft0TimeC, fit::Ft0TriggerMask, fit::Ft0PosZ, fit::Ft0CollTime, fit::Ft0SumAmpA, fit::Ft0SumAmpC, + fit::T0aCorrected, fit::T0cCorrected, fit::T0ac, fit::T0resolution, + fit::Ft0ChAmpl, fit::Ft0TotAmplA, fit::Ft0TotAmplC, + fit::Fv0BcId, fit::Fv0Amplitude, fit::Fv0Channel, fit::Fv0Time, fit::Fv0TriggerMask, + fit::Fv0ChAmpl, fit::Fv0TotAmpl, + fit::FddBcId, fit::FddChargeA, fit::FddChargeC, fit::FddTimeA, fit::FddTimeC, fit::FddTriggerMask, + fit::FddChAmpl, fit::FddTotAmplA, fit::FddTotAmplC, + fit::EnergyCommonZnA, fit::EnergyCommonZnC, fit::TimeZem1, fit::TimeZem2); + +using FITAll = FITsAll::iterator; + +} // namespace o2::aod + +struct FitAll { + // Producer + Produces table; + + void init(InitContext const&) + { + } + + void process(soa::Join const& collisions, + aod::BCsWithTimestamps const&, + aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&) + { + table.reserve(collisions.size()); + + // BC + int runNumber = -1; + uint64_t globalBC = 0; + uint64_t ctpTriggerMask = 0; + uint64_t ctpInputMask = 0; + + // Timestamp + uint64_t timestamp = 0; + + // EvSel + uint32_t triggerAlias = 0; + uint64_t selectionFlags = 0; + uint32_t rctFlags = 0; + bool sel8 = false; + bool hasFT0 = false; + bool hasFV0 = false; + bool hasFDD = false; + bool hasZDC = false; + + // Collision + int32_t bcId = -1; + float posX = -200; + float posY = -200; + float posZ = -200; + uint16_t flags = 0; + uint16_t numContrib = 0; + float collisionTime = -200; + float collisionTimeRes = -200; + + // Mult + float multFT0A = 0; + float multFT0C = 0; + float multFV0A = 0; + float multFDDA = 0; + float multFDDC = 0; + float multZNA = 0; + float multZNC = 0; + float multZEM1 = 0; + float multZEM2 = 0; + float multZPA = 0; + float multZPC = 0; + + // FT0 + int32_t ft0BCId = -1; + std::vector ft0AmplitudeA; + std::vector ft0ChannelA; + std::vector ft0AmplitudeC; + std::vector ft0ChannelC; + float ft0TimeA = -200; + float ft0TimeC = -200; + uint8_t ft0TriggerMask = 0; + float ft0PosZ = -200; + float ft0CollTime = -200; + float ft0SumAmpA = 0; + float ft0SumAmpC = 0; + + // FT0Corrected + float t0ACorrected = -200; + float t0CCorrected = -200; + float t0AC = -200; + float t0resolution = -200; + + // FT0 derived quantities + std::vector ft0ChAmpl(o2::aod::fit::NchFt0, 0); + float ft0TotAmplA = 0; + float ft0TotAmplC = 0; + + // FV0A + int32_t fv0BCId = -1; + std::vector fv0Amplitude; + std::vector fv0Channel; + float fv0Time = -200; + uint8_t fv0TriggerMask = 0; + + // FV0 derived quantities + std::vector fv0ChAmpl(o2::aod::fit::NchFv0, 0); + float fv0TotAmpl = 0; + + // FDD + int32_t fddBCId = -1; + int16_t fddChargeA[8] = {0}; + int16_t fddChargeC[8] = {0}; + float fddTimeA = -200; + float fddTimeC = -200; + uint8_t fddTriggerMask = 0; + + // FDD derived quantities + std::vector fddChAmpl(o2::aod::fit::NchFdd, 0); + float fddTotAmplA = 0; + float fddTotAmplC = 0; + + // ZDC + float energyCommonZNA = -std::numeric_limits::infinity(); + float energyCommonZNC = -std::numeric_limits::infinity(); + float timeZEM1 = -std::numeric_limits::infinity(); + float timeZEM2 = -std::numeric_limits::infinity(); + + for (const auto& collision : collisions) { + auto bc = collision.bc_as(); + + // BC + runNumber = bc.runNumber(); + globalBC = bc.globalBC(); + ctpTriggerMask = bc.triggerMask(); + ctpInputMask = bc.inputMask(); + + // Timestamp + timestamp = bc.timestamp(); + + // EvSel + triggerAlias = collision.alias_raw(); + selectionFlags = collision.selection_raw(); + rctFlags = collision.rct_raw(); + sel8 = collision.sel8(); + hasFT0 = collision.has_foundFT0(); + hasFV0 = collision.has_foundFV0(); + hasFDD = collision.has_foundFDD(); + hasZDC = collision.has_foundZDC(); + + // Collision + bcId = collision.bcId(); + posX = collision.posX(); + posY = collision.posY(); + posZ = collision.posZ(); + flags = collision.flags(); + numContrib = collision.numContrib(); + collisionTime = collision.collisionTime(); + collisionTimeRes = collision.collisionTimeRes(); + + // Multiplicities + multFT0A = collision.multFT0A(); + multFT0C = collision.multFT0C(); + multFV0A = collision.multFV0A(); + multFDDA = collision.multFDDA(); + multFDDC = collision.multFDDC(); + multZNA = collision.multZNA(); + multZNC = collision.multZNC(); + multZEM1 = collision.multZEM1(); + multZEM2 = collision.multZEM2(); + multZPA = collision.multZPA(); + multZPC = collision.multZPC(); + + // FT0 + ft0BCId = -1; + ft0AmplitudeA.clear(); + ft0ChannelA.clear(); + ft0AmplitudeC.clear(); + ft0ChannelC.clear(); + ft0TimeA = -200; + ft0TimeC = -200; + ft0TriggerMask = 0; + ft0PosZ = -200; + ft0CollTime = -200; + ft0SumAmpA = 0; + ft0SumAmpC = 0; + + // FT0Corrected + t0ACorrected = collision.t0ACorrected(); + t0CCorrected = collision.t0CCorrected(); + t0AC = collision.t0AC(); + t0resolution = collision.t0resolution(); + + // FT0 derived quantities + ft0ChAmpl.assign(o2::aod::fit::NchFt0, 0); + ft0TotAmplA = 0; + ft0TotAmplC = 0; + + // FV0A + fv0BCId = -1; + fv0Amplitude.clear(); + fv0Channel.clear(); + fv0Time = -200; + fv0TriggerMask = 0; + + // FV0 derived quantities + fv0ChAmpl.assign(o2::aod::fit::NchFv0, 0); + fv0TotAmpl = 0; + + // FDD + fddBCId = -1; + std::fill(std::begin(fddChargeA), std::end(fddChargeA), 0); + std::fill(std::begin(fddChargeC), std::end(fddChargeC), 0); + fddTimeA = -200; + fddTimeC = -200; + fddTriggerMask = 0; + + // FDD derived quantities + fddChAmpl.assign(o2::aod::fit::NchFdd, 0); + fddTotAmplA = 0; + fddTotAmplC = 0; + + // ZDC + energyCommonZNA = -std::numeric_limits::infinity(); + energyCommonZNC = -std::numeric_limits::infinity(); + timeZEM1 = -std::numeric_limits::infinity(); + timeZEM2 = -std::numeric_limits::infinity(); + + if (hasFT0) { + // FT0 + auto ft0 = collision.foundFT0(); + ft0BCId = ft0.bcId(); + ft0TimeA = ft0.timeA(); + ft0TimeC = ft0.timeC(); + ft0TriggerMask = ft0.triggerMask(); + ft0PosZ = ft0.posZ(); + ft0CollTime = ft0.collTime(); + ft0SumAmpA = ft0.sumAmpA(); + ft0SumAmpC = ft0.sumAmpC(); + + for (size_t i = 0; i < ft0.amplitudeA().size(); i++) { + // FT0 + ft0AmplitudeA.push_back(ft0.amplitudeA()[i]); + ft0ChannelA.push_back(ft0.channelA()[i]); + + // FT0 derived quantities + ft0ChAmpl[ft0.channelA()[i]] = ft0.amplitudeA()[i]; + ft0TotAmplA += ft0.amplitudeA()[i]; + } + for (size_t i = 0; i < ft0.amplitudeC().size(); i++) { + // FT0 + ft0AmplitudeC.push_back(ft0.amplitudeC()[i]); + ft0ChannelC.push_back(ft0.channelC()[i]); + + // FT0 derived quantities + ft0ChAmpl[ft0.channelC()[i] + o2::aod::fit::NchFt0A] = ft0.amplitudeC()[i]; // Channel IDs in the C-side array start from zero in AO2D (JIRA AFIT-129) + ft0TotAmplC += ft0.amplitudeC()[i]; + } + } + + if (hasFV0) { + // FV0A + auto fv0 = collision.foundFV0(); + fv0BCId = fv0.bcId(); + fv0Time = fv0.time(); + fv0TriggerMask = fv0.triggerMask(); + + for (size_t i = 0; i < fv0.amplitude().size(); i++) { + // FV0A + fv0Amplitude.push_back(fv0.amplitude()[i]); + fv0Channel.push_back(fv0.channel()[i]); + + // FV0 derived quantities + fv0ChAmpl[fv0.channel()[i]] = fv0.amplitude()[i]; + fv0TotAmpl += fv0.amplitude()[i]; + } + } + + if (hasFDD) { + // FDD + auto fdd = collision.foundFDD(); + fddBCId = fdd.bcId(); + fddTimeA = fdd.timeA(); + fddTimeC = fdd.timeC(); + fddTriggerMask = fdd.triggerMask(); + + for (size_t i = 0; i < o2::aod::fit::NchFdd / 2; i++) { + // FDD + fddChargeA[i] = fdd.chargeA()[i]; + fddChargeC[i] = fdd.chargeC()[i]; + + // FDD derived quantities + fddChAmpl[i + o2::aod::fit::NchFdd / 2] = fdd.chargeA()[i]; + fddChAmpl[i] = fdd.chargeC()[i]; + fddTotAmplA += fdd.chargeA()[i]; + fddTotAmplC += fdd.chargeC()[i]; + } + } + + if (hasZDC) { + // ZDC + auto zdc = collision.foundZDC(); + energyCommonZNA = zdc.energyCommonZNA(); + energyCommonZNC = zdc.energyCommonZNC(); + timeZEM1 = zdc.timeZEM1(); + timeZEM2 = zdc.timeZEM2(); + } + + table(runNumber, globalBC, ctpTriggerMask, ctpInputMask, + timestamp, + triggerAlias, selectionFlags, rctFlags, sel8, hasFT0, hasFV0, hasFDD, hasZDC, + bcId, posX, posY, posZ, flags, numContrib, collisionTime, collisionTimeRes, + multFT0A, multFT0C, multFV0A, multFDDA, multFDDC, multZNA, multZNC, multZEM1, multZEM2, multZPA, multZPC, + ft0BCId, ft0AmplitudeA, ft0ChannelA, ft0AmplitudeC, ft0ChannelC, + ft0TimeA, ft0TimeC, ft0TriggerMask, ft0PosZ, ft0CollTime, ft0SumAmpA, ft0SumAmpC, + t0ACorrected, t0CCorrected, t0AC, t0resolution, + ft0ChAmpl, ft0TotAmplA, ft0TotAmplC, + fv0BCId, fv0Amplitude, fv0Channel, fv0Time, fv0TriggerMask, + fv0ChAmpl, fv0TotAmpl, + fddBCId, fddChargeA, fddChargeC, fddTimeA, fddTimeC, fddTriggerMask, + fddChAmpl, fddTotAmplA, fddTotAmplC, + energyCommonZNA, energyCommonZNC, timeZEM1, timeZEM2); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + return workflow; +} diff --git a/DPG/Tasks/ITS/filterTracks.cxx b/DPG/Tasks/ITS/filterTracks.cxx index 6a9ee148142..9af02996211 100644 --- a/DPG/Tasks/ITS/filterTracks.cxx +++ b/DPG/Tasks/ITS/filterTracks.cxx @@ -225,7 +225,7 @@ struct FilterTracks { { } - void fillTableData(auto track) + void fillTableData(const auto& track) { filteredTracksCollIdx(track.collisionId()); @@ -235,7 +235,7 @@ struct FilterTracks { filteredTracksTableExtraDet(track.itsClusterSizes(), track.itsChi2NCl(), track.tpcChi2NCl(), track.tpcNClsFound(), track.trackTime()); } - void fillTableDataMC(auto track, aod::McParticles const& mcParticles) + void fillTableDataMC(const auto& track, aod::McParticles const& mcParticles) { fillTableData(track); diff --git a/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx b/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx index fc38e305938..f17df748019 100644 --- a/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx +++ b/DPG/Tasks/Monitor/MC/taskMcSimpleQC.cxx @@ -56,7 +56,7 @@ struct mcSimpleQc { McParticles const&) { // checks on collisions - registry.fill(HIST("histRecoOverGenCollisions"), float(collisions.size()) / mcCollisions.size()); + registry.fill(HIST("histRecoOverGenCollisions"), static_cast(collisions.size()) / mcCollisions.size()); for (const auto& collision : collisions) { if (collision.has_mcCollision()) { @@ -79,7 +79,7 @@ struct mcSimpleQc { } } registry.fill(HIST("histNumGlobalTracks"), nTracks); - registry.fill(HIST("histFracAmbiguousTracks"), float(nAmbTracks) / nTracks); + registry.fill(HIST("histFracAmbiguousTracks"), static_cast(nAmbTracks) / nTracks); } } }; diff --git a/DPG/Tasks/TOF/tofOfflineCalib.cxx b/DPG/Tasks/TOF/tofOfflineCalib.cxx index f3b6aed8ab8..1516c438228 100644 --- a/DPG/Tasks/TOF/tofOfflineCalib.cxx +++ b/DPG/Tasks/TOF/tofOfflineCalib.cxx @@ -18,6 +18,7 @@ #include "tofSkimsTableCreator.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" #include "Common/DataModel/Multiplicity.h" @@ -37,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/DPG/Tasks/TOF/tofSkimsTableCreator.h b/DPG/Tasks/TOF/tofSkimsTableCreator.h index f3a6f63bb9b..80db220c030 100644 --- a/DPG/Tasks/TOF/tofSkimsTableCreator.h +++ b/DPG/Tasks/TOF/tofSkimsTableCreator.h @@ -46,7 +46,7 @@ DECLARE_SOA_COLUMN(DeltaTPi, deltaTPi, float); //! Difference betw DECLARE_SOA_COLUMN(DeltaTKa, deltaTKa, float); //! Difference between the tofSignal and the kaon expected signal DECLARE_SOA_COLUMN(DeltaTPr, deltaTPr, float); //! Difference between the tofSignal and the proton expected signal DECLARE_SOA_COLUMN(DoubleDelta, doubleDelta, float); //! Double difference between DeltaT -DECLARE_SOA_COLUMN(RefSign, refSign, short); //! Sign of the reference track +DECLARE_SOA_COLUMN(RefSign, refSign, int16_t); //! Sign of the reference track DECLARE_SOA_COLUMN(PIDForTracking, pidForTracking, uint8_t); //! Index for mass hypothesis used in tracking see PID.h for definition DECLARE_SOA_COLUMN(EvTimeT0AC, evTimeT0AC, float); //! Event time of the track computed with the T0AC DECLARE_SOA_COLUMN(EvTimeT0ACErr, evTimeT0ACErr, float); //! Resolution of the event time of the track computed with the T0AC diff --git a/DPG/Tasks/TPC/CMakeLists.txt b/DPG/Tasks/TPC/CMakeLists.txt index b8c68ba36fc..12030997bc6 100644 --- a/DPG/Tasks/TPC/CMakeLists.txt +++ b/DPG/Tasks/TPC/CMakeLists.txt @@ -17,4 +17,9 @@ o2physics_add_dpl_workflow(pid-tpc-skimscreation o2physics_add_dpl_workflow(pid-tpc-tree-creator-light SOURCES tpcTreeCreatorLight.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore - COMPONENT_NAME Analysis) \ No newline at end of file + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(tree-creator-pid-tpc-diagnostics + SOURCES treeCreatorPidTpcDiagnostics.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) diff --git a/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx b/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx index 85607d41636..3aaa024874e 100644 --- a/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx +++ b/DPG/Tasks/TPC/tpcSkimsTableCreator.cxx @@ -25,6 +25,7 @@ #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -41,6 +42,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,13 +90,11 @@ struct TreeWriterTpcV0 { Configurable nClNorm{"nClNorm", 152., "Number of cluster normalization. Run 2: 159, Run 3 152"}; Configurable applyEvSel{"applyEvSel", 2, "Flag to apply rapidity cut: 0 -> no event selection, 1 -> Run 2 event selection, 2 -> Run 3 event selection"}; Configurable trackSelection{"trackSelection", 0, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; - Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; /// Configurables downsampling Configurable dwnSmplFactorPi{"dwnSmplFactorPi", 1., "downsampling factor for pions, default fraction to keep is 1."}; Configurable dwnSmplFactorPr{"dwnSmplFactorPr", 1., "downsampling factor for protons, default fraction to keep is 1."}; Configurable dwnSmplFactorEl{"dwnSmplFactorEl", 1., "downsampling factor for electrons, default fraction to keep is 1."}; Configurable dwnSmplFactorKa{"dwnSmplFactorKa", 1., "downsampling factor for kaons, default fraction to keep is 1."}; - Configurable sqrtSNN{"sqrtSNN", 5360., "sqrt(s_NN), used for downsampling with the Tsallis distribution"}; Configurable downsamplingTsalisPions{"downsamplingTsalisPions", -1., "Downsampling factor to reduce the number of pions"}; Configurable downsamplingTsalisProtons{"downsamplingTsalisProtons", -1., "Downsampling factor to reduce the number of protons"}; Configurable downsamplingTsalisElectrons{"downsamplingTsalisElectrons", -1., "Downsampling factor to reduce the number of electrons"}; @@ -102,6 +103,19 @@ struct TreeWriterTpcV0 { Configurable maxPt4dwnsmplTsalisProtons{"maxPt4dwnsmplTsalisProtons", 100., "Maximum Pt for applying downsampling factor of protons"}; Configurable maxPt4dwnsmplTsalisElectrons{"maxPt4dwnsmplTsalisElectrons", 100., "Maximum Pt for applying downsampling factor of electrons"}; Configurable maxPt4dwnsmplTsalisKaons{"maxPt4dwnsmplTsalisKaons", 100., "Maximum Pt for applying downsampling factor of kaons"}; + // Configurables for output tables reservation size + Configurable reserveV0Ratio{"reserveV0Ratio", 0.05, "Ratio of how many tracks from V0s are expected in the output table to the input V0 table size"}; + Configurable reserveCascRatio{"reserveCascRatio", 0.0025, "Ratio of how many tracks from cascades are expected in the output table to the input Cascade table size"}; + Configurable saveReserveQaHisto{"saveReserveQaHisto", true, "Flag to save the DF-wise ratio of output table size to that of input table"}; + // Configurables for run condtion table + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable checkZdc{"checkZdc", false, "set ZDC flag for PbPb"}; + Configurable treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + Configurable requireGoodRct{"requireGoodRct", false, "require good detector flag in run condtion table"}; + // Configurable for the path of CCDB General Run Parameters LHC Interface information + Configurable ccdbPathGrpLhcIf{"ccdbPathGrpLhcIf", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + + HistogramRegistry registry{"registry", {}}; // an arbitrary value of N sigma TOF assigned by TOF task to tracks which are not matched to TOF hits constexpr static float NSigmaTofUnmatched{o2::aod::v0data::kNoTOFValue}; @@ -146,9 +160,11 @@ struct TreeWriterTpcV0 { int negDaughterId{UndefValueInt}; }; - Service ccdb; + Service ccdb{}; - ctpRateFetcher mRateFetcher; + ctpRateFetcher mRateFetcher{}; + + o2::aod::rctsel::RCTFlagsChecker rctChecker{}; TRandom3* fRndm = new TRandom3(0); @@ -172,6 +188,13 @@ struct TreeWriterTpcV0 { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setFatalWhenNull(false); + + rctChecker.init(rctLabel, checkZdc, treatLimitedAcceptanceAsBad); + + if (saveReserveQaHisto) { + registry.add("hV0OutputRatio", "V0 out/in ratio;V0 out/in ratio;Entries", {HistType::kTH1F, {{100, 0, reserveV0Ratio}}}); + registry.add("hCascOutputRatio", "Casc out/in ratio;Casc out/in ratio;Entries", {HistType::kTH1F, {{100, 0, reserveCascRatio}}}); + } } template @@ -250,10 +273,10 @@ struct TreeWriterTpcV0 { } template - void fillSkimmedV0Table(V0Casc const& v0casc, T const& track, aod::TracksQA const& trackQA, const bool existTrkQA, C const& collision, const float nSigmaTPC, const float nSigmaTOF, const float nSigmaITS, const float dEdxExp, const o2::track::PID::ID id, const int runnumber, const double dwnSmplFactor, const float hadronicRate, const int bcGlobalIndex, const int bcTimeFrameId, const int bcBcInTimeFrame, const OccupancyValues& occValues) + void fillSkimmedV0Table(V0Casc const& v0casc, T const& track, aod::TracksQA const& trackQA, const bool existTrkQA, C const& collision, const float nSigmaTPC, const float nSigmaTOF, const float nSigmaITS, const float dEdxExp, const o2::track::PID::ID id, const int runnumber, const double dwnSmplFactor, const float hadronicRate, const int bcGlobalIndex, const int bcTimeFrameId, const int bcBcInTimeFrame, const OccupancyValues& occValues, const bool isGoodRctEvent) { const double ncl = track.tpcNClsFound(); - const double nclPID = track.tpcNClsFindableMinusPID(); + const double nclPID = track.tpcNClsPID(); const double p = track.tpcInnerParam(); const double mass = o2::track::pid_constants::sMasses[id]; const double bg = p / mass; @@ -277,7 +300,8 @@ struct TreeWriterTpcV0 { tpcdEdxNorm = existTrkQA ? trackQA.tpcdEdxNorm() : UndefValueFloat; } if constexpr (ModeId == ModeStandard || ModeId == ModeWithdEdxTrkQA) { - rowTPCTree(usedDedx, + rowTPCTree(isGoodRctEvent, + usedDedx, 1. / dEdxExp, track.tpcInnerParam(), track.tgl(), @@ -307,7 +331,8 @@ struct TreeWriterTpcV0 { v0radius, gammapsipair); } else { - rowTPCTreeWithTrkQA(usedDedx, + rowTPCTreeWithTrkQA(isGoodRctEvent, + usedDedx, 1. / dEdxExp, track.tpcInnerParam(), track.tgl(), @@ -406,29 +431,49 @@ struct TreeWriterTpcV0 { } } - const auto& tracksWithITSPid = soa::Attach(myTracks); + const auto tracksWithITSPid = soa::Attach(myTracks); + + int nV0Entries{0}; + int nCascEntries{0}; + + const int64_t expectedOutputTableSize = static_cast(reserveV0Ratio * myV0s.size() + reserveCascRatio * myCascs.size()); + if constexpr (ModeId == ModeWithdEdxTrkQA || ModeId == ModeStandard) { + rowTPCTree.reserve(expectedOutputTableSize); + } else { + rowTPCTreeWithTrkQA.reserve(expectedOutputTableSize); + } + std::string irSource{}; + float sqrtSNN{}; + bool isFirstCollision{true}; for (const auto& collision : collisions) { if (!isEventSelected(collision, applyEvSel)) { continue; } - const auto& v0s = myV0s.sliceBy(perCollisionV0s, static_cast(collision.globalIndex())); - const auto& cascs = myCascs.sliceBy(perCollisionCascs, static_cast(collision.globalIndex())); - const auto& bc = collision.bc_as(); + const bool isGoodRctEvent = rctChecker.checkTable(collision); + if (requireGoodRct && !isGoodRctEvent) { + continue; + } + + const auto v0s = myV0s.sliceBy(perCollisionV0s, static_cast(collision.globalIndex())); + const auto cascs = myCascs.sliceBy(perCollisionCascs, static_cast(collision.globalIndex())); + const auto bc = collision.bc_as(); + if (isFirstCollision) { + evaluateIrSourceAndSqrtSnn(ccdb, ccdbPathGrpLhcIf, bc.timestamp(), irSource, sqrtSNN); + } + isFirstCollision = false; const int runnumber = bc.runNumber(); - const auto hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), runnumber, irSource) * OneToKilo; + const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), runnumber, irSource) * OneToKilo : 0.; const int bcGlobalIndex = bc.globalIndex(); int bcTimeFrameId{}, bcBcInTimeFrame{}; if constexpr (ModeId == ModeWithdEdxTrkQA || ModeId == ModeStandard) { bcTimeFrameId = UndefValueInt; bcBcInTimeFrame = UndefValueInt; - rowTPCTree.reserve(2 * v0s.size() + cascs.size()); } else if constexpr (ModeId == ModeWithTrkQA) { bcTimeFrameId = bc.tfId(); bcBcInTimeFrame = bc.bcInTF(); - rowTPCTreeWithTrkQA.reserve(2 * v0s.size() + cascs.size()); } auto getTrackQA = [&](const TrksType::iterator& track) { @@ -448,11 +493,11 @@ struct TreeWriterTpcV0 { auto fillDaughterTrack = [&](const auto& mother, const TrksType::iterator& dauTrack, const auto& v0, const bool isPositive) { const auto [trackQAInstance, existTrkQA] = getTrackQA(dauTrack); const auto trackId = dauTrack.globalIndex(); - const auto& dauTrackWithITSPid = tracksWithITSPid.rawIteratorAt(trackId); + const auto dauTrackWithITSPid = tracksWithITSPid.rawIteratorAt(trackId); const auto v0Id = getAddId(v0); const V0Mother v0Mother = createV0Mother(v0Id); - const auto daighterId = isPositive ? v0Mother.posDaughterId : v0Mother.negDaughterId; - const V0Daughter daughter = createV0Daughter(v0, dauTrackWithITSPid, v0Id, daighterId, isPositive); + const auto daughterId = isPositive ? v0Mother.posDaughterId : v0Mother.negDaughterId; + const V0Daughter daughter = createV0Daughter(v0, dauTrackWithITSPid, v0Id, daughterId, isPositive); const bool passTrackSelection = isTrackSelected(dauTrack, trackSelection); const bool passDownsamplig = downsampleTsalisCharged(fRndm, dauTrack.pt(), daughter.downsamplingTsalis, daughter.mass, sqrtSNN, daughter.maxPt4dwnsmplTsalis); @@ -463,8 +508,10 @@ struct TreeWriterTpcV0 { if constexpr (ModeId == ModeWithTrkQA) { evaluateOccupancyVariables(dauTrack, occValues); } - fillSkimmedV0Table(mother, dauTrack, trackQAInstance, existTrkQA, collision, daughter.tpcNSigma, daughter.tofNSigma, daughter.itsNSigma, daughter.tpcExpSignal, daughter.id, runnumber, daughter.dwnSmplFactor, hadronicRate, bcGlobalIndex, bcTimeFrameId, bcBcInTimeFrame, occValues); + fillSkimmedV0Table(mother, dauTrack, trackQAInstance, existTrkQA, collision, daughter.tpcNSigma, daughter.tofNSigma, daughter.itsNSigma, daughter.tpcExpSignal, daughter.id, runnumber, daughter.dwnSmplFactor, hadronicRate, bcGlobalIndex, bcTimeFrameId, bcBcInTimeFrame, occValues, isGoodRctEvent); + return true; } + return false; }; /// Loop over v0 candidates @@ -473,11 +520,15 @@ struct TreeWriterTpcV0 { if (v0Id == MotherUndef) { continue; } - const auto& posTrack = v0.posTrack_as(); - const auto& negTrack = v0.negTrack_as(); + const auto posTrack = v0.posTrack_as(); + const auto negTrack = v0.negTrack_as(); - fillDaughterTrack(v0, posTrack, v0, true); - fillDaughterTrack(v0, negTrack, v0, false); + if (fillDaughterTrack(v0, posTrack, v0, true)) { + ++nV0Entries; + } + if (fillDaughterTrack(v0, negTrack, v0, false)) { + ++nV0Entries; + } } /// Loop over cascade candidates @@ -486,12 +537,26 @@ struct TreeWriterTpcV0 { if (cascId == MotherUndef) { continue; } - const auto& bachTrack = casc.bachelor_as(); + const auto bachTrack = casc.bachelor_as(); // Omega and antiomega const auto isDaughterPositive = cascId == MotherAntiOmega ? true : false; - fillDaughterTrack(casc, bachTrack, casc, isDaughterPositive); + if (fillDaughterTrack(casc, bachTrack, casc, isDaughterPositive)) { + ++nCascEntries; + } } } + LOG(info) << "runV0() summary:"; + LOG(info) << "V0 table size = " << myV0s.size(); + LOG(info) << "Cascade table size = " << myCascs.size(); + LOG(info) << "nV0Entries = " << nV0Entries; + LOG(info) << "nCascEntries = " << nCascEntries; + LOG(info) << "nV0Entries / V0 table size = " << static_cast(nV0Entries) / myV0s.size(); + LOG(info) << "nCascEntries / Cascade table size = " << static_cast(nCascEntries) / myCascs.size(); + + if (saveReserveQaHisto) { + registry.fill(HIST("hV0OutputRatio"), static_cast(nV0Entries) / myV0s.size()); + registry.fill(HIST("hCascOutputRatio"), static_cast(nCascEntries) / myCascs.size()); + } } /// runV0 void processStandard(Colls const& collisions, @@ -574,7 +639,6 @@ struct TreeWriterTpcTof { Configurable nClNorm{"nClNorm", 152., "Number of cluster normalization. Run 2: 159, Run 3 152"}; Configurable applyEvSel{"applyEvSel", 2, "Flag to apply rapidity cut: 0 -> no event selection, 1 -> Run 2 event selection, 2 -> Run 3 event selection"}; Configurable trackSelection{"trackSelection", 1, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; - Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; /// Triton Configurable maxMomTPCOnlyTr{"maxMomTPCOnlyTr", 1.5, "Maximum momentum for TPC only cut triton"}; Configurable maxMomHardCutOnlyTr{"maxMomHardCutOnlyTr", 50, "Maximum TPC inner momentum for triton"}; @@ -609,12 +673,23 @@ struct TreeWriterTpcTof { Configurable nSigmaTofTpctofPi{"nSigmaTofTpctofPi", 4., "number of sigma for TOF cut for TPC and TOF combined pion"}; Configurable dwnSmplFactorPi{"dwnSmplFactorPi", 1., "downsampling factor for pions, default fraction to keep is 1."}; /// pT dependent downsampling - Configurable sqrtSNN{"sqrtSNN", 5360., "sqrt(s_NN), used for downsampling with the Tsallis distribution"}; Configurable downsamplingTsalisTritons{"downsamplingTsalisTritons", -1., "Downsampling factor to reduce the number of tritons"}; Configurable downsamplingTsalisDeuterons{"downsamplingTsalisDeuterons", -1., "Downsampling factor to reduce the number of deuterons"}; Configurable downsamplingTsalisProtons{"downsamplingTsalisProtons", -1., "Downsampling factor to reduce the number of protons"}; Configurable downsamplingTsalisKaons{"downsamplingTsalisKaons", -1., "Downsampling factor to reduce the number of kaons"}; Configurable downsamplingTsalisPions{"downsamplingTsalisPions", -1., "Downsampling factor to reduce the number of pions"}; + // Configurable for output table reservation size + Configurable reserveTrackRatio{"reserveTrackRatio", 0.003, "Ratio of how many tracks are expected in the output table to the input Tracks table size"}; + Configurable saveReserveQaHisto{"saveReserveQaHisto", true, "Flag to save the DF-wise ratio of output table size to that of input table"}; + // Configurables for run condtion table + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable checkZdc{"checkZdc", false, "set ZDC flag for PbPb"}; + Configurable treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + Configurable requireGoodRct{"requireGoodRct", false, "require good detector flag in run condtion table"}; + // Configurable for the path of CCDB General Run Parameters LHC Interface information + Configurable ccdbPathGrpLhcIf{"ccdbPathGrpLhcIf", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + + HistogramRegistry registry{"registry", {}}; struct TofTrack { bool isApplyHardCutOnly; @@ -633,9 +708,11 @@ struct TreeWriterTpcTof { double nSigmaTpcTpctof; }; - Service ccdb; + Service ccdb{}; + + ctpRateFetcher mRateFetcher{}; - ctpRateFetcher mRateFetcher; + o2::aod::rctsel::RCTFlagsChecker rctChecker{}; TRandom3* fRndm = new TRandom3(0); @@ -666,13 +743,19 @@ struct TreeWriterTpcTof { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setFatalWhenNull(false); + + rctChecker.init(rctLabel, checkZdc, treatLimitedAcceptanceAsBad); + + if (saveReserveQaHisto) { + registry.add("hTrackOutputRatio", "Track out/in ratio;Track out/in ratio;Entries", {HistType::kTH1F, {{100, 0, reserveTrackRatio}}}); + } } template - void fillSkimmedTpcTofTable(T const& track, aod::TracksQA const& trackQA, const bool existTrkQA, C const& collision, const float nSigmaTPC, const float nSigmaTOF, const float nSigmaITS, const float dEdxExp, const o2::track::PID::ID id, const int runnumber, const double dwnSmplFactor, const double hadronicRate, const int bcGlobalIndex, const int bcTimeFrameId, const int bcBcInTimeFrame, const OccupancyValues& occValues) + void fillSkimmedTpcTofTable(T const& track, aod::TracksQA const& trackQA, const bool existTrkQA, C const& collision, const float nSigmaTPC, const float nSigmaTOF, const float nSigmaITS, const float dEdxExp, const o2::track::PID::ID id, const int runnumber, const double dwnSmplFactor, const double hadronicRate, const int bcGlobalIndex, const int bcTimeFrameId, const int bcBcInTimeFrame, const OccupancyValues& occValues, const bool isGoodRctEvent) { const double ncl = track.tpcNClsFound(); - const double nclPID = track.tpcNClsFindableMinusPID(); + const double nclPID = track.tpcNClsPID(); const double p = track.tpcInnerParam(); const double mass = o2::track::pid_constants::sMasses[id]; const double bg = p / mass; @@ -689,7 +772,8 @@ struct TreeWriterTpcTof { tpcdEdxNorm = existTrkQA ? trackQA.tpcdEdxNorm() : UndefValueFloat; } if (ModeId == ModeStandard || ModeId == ModeWithdEdxTrkQA) { - rowTPCTOFTree(usedEdx, + rowTPCTOFTree(isGoodRctEvent, + usedEdx, 1. / dEdxExp, track.tpcInnerParam(), track.tgl(), @@ -713,7 +797,8 @@ struct TreeWriterTpcTof { hadronicRate, tpcdEdxNorm); } else { - rowTPCTOFTreeWithTrkQA(usedEdx, + rowTPCTOFTreeWithTrkQA(isGoodRctEvent, + usedEdx, 1. / dEdxExp, track.tpcInnerParam(), track.tgl(), @@ -775,11 +860,27 @@ struct TreeWriterTpcTof { labelTrack2TrackQA.at(trackId) = trackQA.globalIndex(); } } + + const int64_t expectedOutputTableSize = static_cast(reserveTrackRatio * myTracks.size()); + if constexpr (ModeId == ModeWithdEdxTrkQA || ModeId == ModeStandard) { + rowTPCTOFTree.reserve(expectedOutputTableSize); + } else { + rowTPCTOFTreeWithTrkQA.reserve(expectedOutputTableSize); + } + + std::string irSource{}; + float sqrtSNN{}; + bool isFirstCollision{true}; for (const auto& collision : collisions) { - const auto& tracks = myTracks.sliceBy(perCollisionTracksType, collision.globalIndex()); + const auto tracks = myTracks.sliceBy(perCollisionTracksType, collision.globalIndex()); if (!isEventSelected(collision, applyEvSel)) { continue; } + const bool isGoodRctEvent = rctChecker.checkTable(collision); + if (requireGoodRct && !isGoodRctEvent) { + continue; + } + auto tracksWithITSPid = soa::Attach(tracks); @@ -788,19 +889,21 @@ struct TreeWriterTpcTof { tracksWithITSPid.bindExternalIndices(&trackMeanOccs); } - const auto& bc = collision.bc_as(); + const auto bc = collision.bc_as(); + if (isFirstCollision) { + evaluateIrSourceAndSqrtSnn(ccdb, ccdbPathGrpLhcIf, bc.timestamp(), irSource, sqrtSNN); + } + isFirstCollision = false; const int runnumber = bc.runNumber(); - const auto hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), runnumber, irSource) * OneToKilo; + const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), runnumber, irSource) * OneToKilo : 0.; const int bcGlobalIndex = bc.globalIndex(); int bcTimeFrameId{}, bcBcInTimeFrame{}; if constexpr (ModeId == ModeStandard || ModeId == ModeWithdEdxTrkQA) { bcTimeFrameId = UndefValueInt; bcBcInTimeFrame = UndefValueInt; - rowTPCTOFTree.reserve(tracks.size()); } else { bcTimeFrameId = bc.tfId(); bcBcInTimeFrame = bc.bcInTF(); - rowTPCTOFTreeWithTrkQA.reserve(tracks.size()); } for (auto const& trk : tracksWithITSPid) { if (!isTrackSelected(trk, trackSelection)) { @@ -826,21 +929,30 @@ struct TreeWriterTpcTof { TofTrack tofPion(false, UndefValueDouble, maxMomTPCOnlyPi, trk.tpcNSigmaPi(), nSigmaTPCOnlyPi, downsamplingTsalisPions, MassPiPlus, trk.tofNSigmaPi(), trk.itsNSigmaPi(), trk.tpcExpSignalPi(tpcSignalGeneric(trk)), PidPion, dwnSmplFactorPi, nSigmaTofTpctofPi, nSigmaTpcTpctofPi); - OccupancyValues occValues; + OccupancyValues occValues{}; if constexpr (ModeId == ModeWithTrkQA) { evaluateOccupancyVariables(trk, occValues); } for (const auto& tofTrack : {&tofTriton, &tofDeuteron, &tofProton, &tofKaon, &tofPion}) { - if ((!tofTrack->isApplyHardCutOnly || trk.tpcInnerParam() < tofTrack->maxMomHardCutOnly) && - ((trk.tpcInnerParam() <= tofTrack->maxMomTPCOnly && std::fabs(tofTrack->tpcNSigma) < tofTrack->nSigmaTPCOnly) || - (trk.tpcInnerParam() > tofTrack->maxMomTPCOnly && std::fabs(tofTrack->tofNSigma) < tofTrack->nSigmaTofTpctof && std::fabs(tofTrack->tpcNSigma) < tofTrack->nSigmaTpcTpctof)) && - downsampleTsalisCharged(fRndm, trk.pt(), tofTrack->downsamplingTsalis, tofTrack->mass, sqrtSNN)) { - fillSkimmedTpcTofTable(trk, trackQA, existTrkQA, collision, tofTrack->tpcNSigma, tofTrack->tofNSigma, tofTrack->itsNSigma, tofTrack->tpcExpSignal, tofTrack->pid, runnumber, tofTrack->dwnSmplFactor, hadronicRate, bcGlobalIndex, bcTimeFrameId, bcBcInTimeFrame, occValues); + const bool passMomHardCut = !tofTrack->isApplyHardCutOnly || trk.tpcInnerParam() < tofTrack->maxMomHardCutOnly; + const bool passMomTpcOnly = trk.tpcInnerParam() <= tofTrack->maxMomTPCOnly && std::fabs(tofTrack->tpcNSigma) < tofTrack->nSigmaTPCOnly; + const bool passMomTpcTof = trk.tpcInnerParam() > tofTrack->maxMomTPCOnly && std::fabs(tofTrack->tofNSigma) < tofTrack->nSigmaTofTpctof && std::fabs(tofTrack->tpcNSigma) < tofTrack->nSigmaTpcTpctof; + const bool passDownsamplig = downsampleTsalisCharged(fRndm, trk.pt(), tofTrack->downsamplingTsalis, tofTrack->mass, sqrtSNN); + if (passMomHardCut && (passMomTpcOnly || passMomTpcTof) && passDownsamplig) { + fillSkimmedTpcTofTable(trk, trackQA, existTrkQA, collision, tofTrack->tpcNSigma, tofTrack->tofNSigma, tofTrack->itsNSigma, tofTrack->tpcExpSignal, tofTrack->pid, runnumber, tofTrack->dwnSmplFactor, hadronicRate, bcGlobalIndex, bcTimeFrameId, bcBcInTimeFrame, occValues, isGoodRctEvent); } } } /// Loop tracks } + LOG(info) << "runTof() summary:"; + LOG(info) << "Track table size = " << myTracks.size(); + LOG(info) << "nTrackEntries = " << rowTPCTOFTree.lastIndex() + 1; + LOG(info) << "nTrackEntries / Track table size = " << static_cast((rowTPCTOFTree.lastIndex() + 1)) / myTracks.size(); + + if (saveReserveQaHisto) { + registry.fill(HIST("hTrackOutputRatio"), static_cast((rowTPCTOFTree.lastIndex() + 1)) / myTracks.size()); + } } /// runTof void processStandard(Colls const& collisions, diff --git a/DPG/Tasks/TPC/tpcSkimsTableCreator.h b/DPG/Tasks/TPC/tpcSkimsTableCreator.h index 61017c27d1c..9172d8d2447 100644 --- a/DPG/Tasks/TPC/tpcSkimsTableCreator.h +++ b/DPG/Tasks/TPC/tpcSkimsTableCreator.h @@ -31,6 +31,7 @@ namespace o2::aod { namespace tpcskims { +DECLARE_SOA_COLUMN(IsGoodRct, isGoodRct, bool); DECLARE_SOA_COLUMN(InvDeDxExpTPC, invDeDxExpTPC, float); DECLARE_SOA_COLUMN(Mass, mass, float); DECLARE_SOA_COLUMN(BetaGamma, betaGamma, float); @@ -58,7 +59,8 @@ DECLARE_SOA_COLUMN(BcBcInTimeFrame, bcBcInTimeFrame, int); } // namespace tpcskims #define TPCSKIMS_COLUMNS_BASE \ - o2::aod::track::TPCSignal, \ + tpcskims::IsGoodRct, \ + o2::aod::track::TPCSignal, \ tpcskims::InvDeDxExpTPC, \ o2::aod::track::TPCInnerParam, \ o2::aod::track::Tgl, \ diff --git a/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx b/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx index 7d9ae11ea1b..1d44db1e78d 100644 --- a/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx +++ b/DPG/Tasks/TPC/tpcTreeCreatorLight.cxx @@ -87,7 +87,7 @@ struct TreeWriterTPCTOF { const double p = track.tpcInnerParam(); const int multTPC = collision.multTPC(); - const double pseudoRndm = track.pt() * 1000. - (int64_t)(track.pt() * 1000); + const double pseudoRndm = track.pt() * 1000. - static_cast(track.pt() * 1000); if (pseudoRndm < dwnSmplFactor) { rowTPCTOFTree(track.tpcSignal(), p, diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx new file mode 100644 index 00000000000..8822995d239 --- /dev/null +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.cxx @@ -0,0 +1,329 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file treeCreatorPidTpcDiagnostics.cxx +/// \brief Creates trees with PID QA variables along with variables used for NN training +/// +/// \author Ana Marin +/// \author Oleksii Lubynets + +#include "treeCreatorPidTpcDiagnostics.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include "DPG/Tasks/TPC/tpcSkimsTableCreator.h" +#include "DPG/Tasks/TPC/utilsTpcSkimsTableCreator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::track; +using namespace o2::dpg_tpcskimstablecreator; + +#define DO_FOR_ALL_PARTICLES(MACRO, ARG) \ + MACRO(El, Electron, ARG) \ + MACRO(Mu, Muon, ARG) \ + MACRO(Pi, Pion, ARG) \ + MACRO(Ka, Kaon, ARG) \ + MACRO(Pr, Proton, ARG) \ + MACRO(De, Deuteron, ARG) \ + MACRO(Tr, Triton, ARG) \ + MACRO(He, Helium3, ARG) \ + MACRO(Al, Alpha, ARG) + +struct TreeCreatorPidTpcDiagnostics { + Produces rowPidTpcDiagnostics; + + Configurable applyEvSel{"applyEvSel", 2, "Flag to apply event selection: 0 -> no event selection, 1 -> Run 2 event selection, 2 -> Run 3 event selection"}; + Configurable cutVtxZ{"cutVtxZ", 10.f, "Cut on vertex Z position [cm]"}; + Configurable trackSelection{"trackSelection", 1, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Skip non-global tracks"}; + Configurable requireIts{"requireIts", true, "Skip tracks without ITS"}; + Configurable cutMinTPCNcls{"cutMinTPCNcls", 0, "Minimum number or TPC Clusters for tracks"}; + Configurable cutRapidity{"cutRapidity", 999.f, "Rapidity cut"}; + Configurable nClNorm{"nClNorm", 152.f, "Number of cluster normalization. Run 2: 159, Run 3 152"}; + // Configurable for the path of CCDB General Run Parameters LHC Interface information + Configurable ccdbPathGrpLhcIf{"ccdbPathGrpLhcIf", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + // Configurables for output tables reservation size + Configurable reserveRatio{"reserveRatio", 1.f, "Ratio of how many rows expected in the output table to the input Tracks table size"}; + Configurable saveReserveQaHisto{"saveReserveQaHisto", true, "Flag to save the DF-wise ratio of output table size to that of input table"}; + // Configurables for run condtion table + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable checkZdc{"checkZdc", false, "set ZDC flag for PbPb"}; + Configurable treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + Configurable requireGoodRct{"requireGoodRct", false, "require good detector flag in run condtion table"}; + + struct ParticleWiseCfgs : ConfigurableGroup { + std::string prefix = "particleWiseCfgs"; // JSON group name + +#define DECLARE_PARTICLE_WISE_CONFIGURABLES(Unused1, ParticleNameLong, Unused2) \ + Configurable cutTpcInnerParameterMin##ParticleNameLong{"cutTpcInnerParameterMin" #ParticleNameLong, 0.f, "Lower-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutTpcInnerParameterMax##ParticleNameLong{"cutTpcInnerParameterMax" #ParticleNameLong, 999.f, "Upper-value cut on tpcInnerParam for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutExpSigmaMax##ParticleNameLong{"cutExpSigmaMax" #ParticleNameLong, 1e9f, "Upper-value cut on expected sigma for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxExpectedMin##ParticleNameLong{"cutDeDxExpectedMin" #ParticleNameLong, -1e9f, "Lower-value cut on expected dE/dx for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxExpectedMax##ParticleNameLong{"cutDeDxExpectedMax" #ParticleNameLong, 1e9f, "Upper-value cut on expected dE/dx for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxDiffMin##ParticleNameLong{"cutDeDxDiffMin" #ParticleNameLong, -1e9f, "Lower-value cut on real - expected dE/dx difference for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutDeDxDiffMax##ParticleNameLong{"cutDeDxDiffMax" #ParticleNameLong, 1e9f, "Upper-value cut on real - expected dE/dx difference for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable cutNSigmaTpcAbs##ParticleNameLong{"cutNSigmaTpcAbs" #ParticleNameLong, 999.f, "Cut on absolute value of nSigmaTpc for " #ParticleNameLong}; /* o2-linter: disable=name/configurable (Configurable defined in macro)*/ \ + Configurable downsampleFactor##ParticleNameLong{"downsampleFactor" #ParticleNameLong, 1.f, "Downsampling factor for " #ParticleNameLong}; // o2-linter: disable=name/configurable (Configurable defined in macro) + + DO_FOR_ALL_PARTICLES(DECLARE_PARTICLE_WISE_CONFIGURABLES, _) +#undef DECLARE_PARTICLE_WISE_CONFIGURABLES + } particleWiseCfgs; + +#define PARTICLE_WISE_CFG_NAMES \ + (cutTpcInnerParameterMin)(cutTpcInnerParameterMax)(cutExpSigmaMax)(cutDeDxExpectedMin)(cutDeDxExpectedMax)(cutDeDxDiffMin)(cutDeDxDiffMax)(cutNSigmaTpcAbs)(downsampleFactor) + +#define MAKE_PARTICLE_CUT_POINTER(Unused, ParticleNameLong, CfgName) \ + &particleWiseCfgs.BOOST_PP_CAT(CfgName, ParticleNameLong), + +#define PACK_CONFIGURABLES_TO_ARRAY(Unused1, Unused2, CfgName) \ + std::array*, PID::Alpha + 1> CfgName{ \ + DO_FOR_ALL_PARTICLES(MAKE_PARTICLE_CUT_POINTER, CfgName)}; + + BOOST_PP_SEQ_FOR_EACH(PACK_CONFIGURABLES_TO_ARRAY, _, PARTICLE_WISE_CFG_NAMES) + +#undef PACK_CONFIGURABLES_TO_ARRAY +#undef MAKE_PARTICLE_CUT_POINTER +#undef PARTICLE_WISE_CFG_NAMES + + HistogramRegistry registry{"registry", {}}; + + Service ccdb{}; + + ctpRateFetcher mRateFetcher; + + o2::aod::rctsel::RCTFlagsChecker rctChecker; + + TRandom3 mRnd; + + using CollisionsExtra = soa::Join; + using TrackCandidates = soa::Join; + + Preslice perCollisionTracks = aod::track::collisionId; + + int mEnabledParticles{0}; + int mProcessedParticles{0}; + + template + bool initPerParticle() + { + static_assert(ParticleId >= 0 && ParticleId <= PID::Alpha && "Particle index outside limits"); + int enabledProcesses{0}; + + switch (ParticleId) { +#define INIT_PARTICLE(Unused1, ParticleNameLong, Unused2) \ + case PID::ParticleNameLong: \ + if (!doprocess##ParticleNameLong && !doprocessFull##ParticleNameLong && !doprocessFullWithTOF##ParticleNameLong) { \ + return false; \ + } \ + if (doprocess##ParticleNameLong) { \ + ++enabledProcesses; \ + } \ + if (doprocessFull##ParticleNameLong) { \ + ++enabledProcesses; \ + } \ + if (doprocessFullWithTOF##ParticleNameLong) { \ + ++enabledProcesses; \ + } \ + LOG(info) << "Enabled TPC QA for " << #ParticleNameLong; \ + break; + + DO_FOR_ALL_PARTICLES(INIT_PARTICLE, _) +#undef INIT_PARTICLE + } + if (enabledProcesses != 1) { + LOG(fatal) << "Cannot enable more than one process function per particle, check and retry!"; + } + return true; + } + + void init(o2::framework::InitContext&) + { + static_for<0, PID::Alpha>([&](auto ParticleId) { + mEnabledParticles += static_cast(initPerParticle()); + }); + + if (mEnabledParticles == 0) { + LOG(fatal) << "At least one process function should be enabled, check and retry!"; + } + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + + rctChecker.init(rctLabel, checkZdc, treatLimitedAcceptanceAsBad); + + if (saveReserveQaHisto) { + registry.add("hOutputRatio", "Table out/in ratio;Table out/in ratio;Entries", {HistType::kTH1F, {{100, 0, reserveRatio}}}); + } + } + + template + void processSingleParticle(CollisionsExtra const& collisions, + TrackType const& tracks) + { + if (mProcessedParticles == 0) { + rowPidTpcDiagnostics.reserve(tracks.size() * reserveRatio); + } + + std::string irSource{}; + float sqrtSNN{}; // placeholder to satisfy evaluateIrSourceAndSqrtSnn's signature + bool isFirstCollision{true}; + for (const auto& collision : collisions) { + if (!isEventSelected(collision, applyEvSel) || (std::abs(collision.posZ()) > cutVtxZ)) { + continue; + } + + const bool isGoodRctEvent = rctChecker.checkTable(collision); + if (requireGoodRct && !isGoodRctEvent) { + continue; + } + + const auto bc = collision.bc_as(); + if (isFirstCollision) { + evaluateIrSourceAndSqrtSnn(ccdb, ccdbPathGrpLhcIf, bc.timestamp(), irSource, sqrtSNN); + } + isFirstCollision = false; + const float ft0Occ = collision.ft0cOccupancyInTimeRange(); + const float multTPC = collision.multTPC() / MultiplicityNorm; + const int runnumber = bc.runNumber(); + const auto hadronicRate = !irSource.empty() ? mRateFetcher.fetch(ccdb.service, bc.timestamp(), runnumber, irSource) * OneToKilo : 0.; + + const auto tracksFromCollision = tracks.sliceBy(perCollisionTracks, static_cast(collision.globalIndex())); + + for (const auto& track : tracksFromCollision) { + if (*downsampleFactor.at(ParticleId) < 1.f && mRnd.Uniform(1) > *downsampleFactor.at(ParticleId)) { + continue; + } + + bool isGoodTrack = isTrackSelected(track, trackSelection); + isGoodTrack &= (!requireGlobalTrack || track.isGlobalTrack()); + isGoodTrack &= (!requireIts || track.hasITS()); + isGoodTrack &= track.hasTPC(); + isGoodTrack &= (track.tpcNClsFound() >= cutMinTPCNcls); + + const float rapidity = track.rapidity(PID::getMass(ParticleId)); + const float momentum = track.p(); + const float nClNormalized = std::sqrt(nClNorm / track.tpcNClsFound()); + const auto nclPID = static_cast(track.tpcNClsPID()); + const float phi = track.phi(); + const float tgl = track.tgl(); + const float tpcInnerParam = track.tpcInnerParam(); + const float signed1Pt = track.signed1Pt(); + const float nSigmaTpc = o2::aod::pidutils::tpcNSigma(track); + + isGoodTrack &= (std::fabs(rapidity) <= cutRapidity); + isGoodTrack &= (tpcInnerParam >= *cutTpcInnerParameterMin.at(ParticleId)); + isGoodTrack &= (tpcInnerParam <= *cutTpcInnerParameterMax.at(ParticleId)); + isGoodTrack &= (std::fabs(nSigmaTpc) <= *cutNSigmaTpcAbs.at(ParticleId)); + + float dedxDiff{UndefValueFloat}; + float dedxExpected{UndefValueFloat}; + float expSigma{UndefValueFloat}; + + if constexpr (IsFullTable) { + dedxDiff = o2::aod::pidutils::tpcExpSignalDiff(track); + dedxExpected = track.tpcSignal() - dedxDiff; + expSigma = o2::aod::pidutils::tpcExpSigma(track); + + isGoodTrack &= (expSigma <= *cutExpSigmaMax.at(ParticleId)); + isGoodTrack &= (dedxExpected >= *cutDeDxExpectedMin.at(ParticleId)); + isGoodTrack &= (dedxExpected <= *cutDeDxExpectedMax.at(ParticleId)); + isGoodTrack &= (dedxDiff >= *cutDeDxDiffMin.at(ParticleId)); + isGoodTrack &= (dedxDiff <= *cutDeDxDiffMax.at(ParticleId)); + } + + if (!isGoodTrack) { + continue; + } + + float nSigmaTof{UndefValueFloat}; + + if constexpr (IsTofTable) { + nSigmaTof = o2::aod::pidutils::tofNSigma(track); + } + + rowPidTpcDiagnostics(isGoodRctEvent, ParticleId, ft0Occ, hadronicRate, multTPC, nClNormalized, nclPID, runnumber, phi, tgl, tpcInnerParam, rapidity, momentum, signed1Pt, nSigmaTpc, dedxExpected, dedxDiff, expSigma, nSigmaTof); + } // tracksFromCollision + } // collisions + ++mProcessedParticles; + if (mProcessedParticles == mEnabledParticles) { + mProcessedParticles = 0; + if (saveReserveQaHisto) { + registry.fill(HIST("hOutputRatio"), static_cast((rowPidTpcDiagnostics.lastIndex() + 1)) / tracks.size()); + } + } + } + +#define MAKE_PROCESS_FUNCTIONS(ParticleNameShort, ParticleNameLong, Unused) \ + void process##ParticleNameLong(CollisionsExtra const& collisions, \ + soa::Join const& tracks, \ + aod::BCsWithTimestamps const&) \ + { \ + processSingleParticle(collisions, tracks); \ + } \ + PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, process##ParticleNameLong, Form("Process for the %s hypothesis for TPC NSigma QA", #ParticleNameLong), false); \ + \ + void processFull##ParticleNameLong(CollisionsExtra const& collisions, \ + soa::Join const& tracks, \ + aod::BCsWithTimestamps const&) \ + { \ + processSingleParticle(collisions, tracks); \ + } \ + PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, processFull##ParticleNameLong, Form("Process for the %s hypothesis for full TPC PID QA", #ParticleNameLong), false); \ + \ + void processFullWithTOF##ParticleNameLong(CollisionsExtra const& collisions, \ + soa::Join const& tracks, \ + aod::BCsWithTimestamps const&) \ + { \ + processSingleParticle(collisions, tracks); \ + } \ + PROCESS_SWITCH(TreeCreatorPidTpcDiagnostics, processFullWithTOF##ParticleNameLong, Form("Process for the %s hypothesis for full TPC PID QA with the TOF info added", #ParticleNameLong), false); + + DO_FOR_ALL_PARTICLES(MAKE_PROCESS_FUNCTIONS, _) +#undef MAKE_PROCESS_FUNCTIONS +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} +#undef DO_FOR_ALL_PARTICLES diff --git a/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h new file mode 100644 index 00000000000..235a802aa54 --- /dev/null +++ b/DPG/Tasks/TPC/treeCreatorPidTpcDiagnostics.h @@ -0,0 +1,58 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file treeCreatorPidTpcDiagnostics.h +/// \author Ana Marin +/// \author Oleksii Lubynets +/// \brief Creates trees with PID QA variables along with variables used for NN training + +#ifndef DPG_TASKS_TPC_TREECREATORPIDTPCDIAGNOSTICS_H_ +#define DPG_TASKS_TPC_TREECREATORPIDTPCDIAGNOSTICS_H_ + +#include "DPG/Tasks/TPC/tpcSkimsTableCreator.h" + +#include +#include + +namespace o2::aod +{ +namespace dpg_tpcpiddiagnostics +{ +DECLARE_SOA_COLUMN(NSigmaTpc, nSigmaTpc, float); +DECLARE_SOA_COLUMN(DedxExpected, dedxExpected, float); +DECLARE_SOA_COLUMN(DedxDiff, dedxDiff, float); +DECLARE_SOA_COLUMN(ExpSigma, expSigma, float); +DECLARE_SOA_COLUMN(NSigmaTof, nSigmaTof, float); +} // namespace dpg_tpcpiddiagnostics + +DECLARE_SOA_TABLE(PidTpcDiagnostics, "AOD", "PIDTPCDIAG", + tpcskims::IsGoodRct, + tpcskims::PidIndex, + tpcskims::Ft0Occ, + tpcskims::HadronicRate, + tpcskims::NormMultTPC, + tpcskims::NormNClustersTPC, + tpcskims::NormNClustersTPCPID, + tpcskims::RunNumber, + o2::aod::track::Phi, + o2::aod::track::Tgl, + o2::aod::track::TPCInnerParam, + o2::aod::track::Y, + o2::aod::track::P, + o2::aod::track::Signed1Pt, + dpg_tpcpiddiagnostics::NSigmaTpc, + dpg_tpcpiddiagnostics::DedxExpected, + dpg_tpcpiddiagnostics::DedxDiff, + dpg_tpcpiddiagnostics::ExpSigma, + dpg_tpcpiddiagnostics::NSigmaTof) +} // namespace o2::aod + +#endif // DPG_TASKS_TPC_TREECREATORPIDTPCDIAGNOSTICS_H_ diff --git a/DPG/Tasks/TPC/utilsTpcSkimsTableCreator.h b/DPG/Tasks/TPC/utilsTpcSkimsTableCreator.h index 32c27233067..a3b3dcddc17 100644 --- a/DPG/Tasks/TPC/utilsTpcSkimsTableCreator.h +++ b/DPG/Tasks/TPC/utilsTpcSkimsTableCreator.h @@ -22,13 +22,20 @@ #include "tpcSkimsTableCreator.h" +#include "Common/Core/CollisionTypeHelper.h" #include "Common/DataModel/OccupancyTables.h" +#include +#include #include +#include +#include #include #include +#include +#include namespace o2::dpg_tpcskimstablecreator { @@ -49,7 +56,7 @@ enum { /// Event selection template -inline bool isEventSelected(const CollisionType& collision, const int applyEvSel) +bool isEventSelected(const CollisionType& collision, const int applyEvSel) { if ((applyEvSel == EventSelectionRun2 && !collision.sel7()) || (applyEvSel == EventSelectionRun3 && !collision.sel8())) { return false; @@ -88,7 +95,7 @@ inline bool downsampleTsalisCharged(TRandom3* fRndm, const double pt, const doub // Track selection template -inline bool isTrackSelected(const TrackType& track, const int trackSelection) +bool isTrackSelected(const TrackType& track, const int trackSelection) { bool isSelected{false}; isSelected |= trackSelection == TrackSelectionNoCut; @@ -103,7 +110,7 @@ inline bool isTrackSelected(const TrackType& track, const int trackSelection) /// Evaluate tpcSignal with or without dEdx correction template -inline double tpcSignalGeneric(const TrkType& track) +double tpcSignalGeneric(const TrkType& track) { if constexpr (IsCorrectedDeDx) { return track.tpcSignalCorrected(); @@ -112,6 +119,27 @@ inline double tpcSignalGeneric(const TrkType& track) } } +/// Determine interaction rate source and sqrtSNN from CCDB +void evaluateIrSourceAndSqrtSnn(const o2::framework::Service& ccdb, const std::string& ccdbPathGrpLhcIf, const uint64_t timestamp, std::string& irSource, float& sqrtSNN) +{ + o2::parameters::GRPLHCIFData* genRunParams = ccdb->template getForTimeStamp(ccdbPathGrpLhcIf, timestamp); + if (genRunParams != nullptr) { + const auto collSys = CollisionSystemType::getCollisionTypeFromGrp(genRunParams); + if (collSys == CollisionSystemType::kCollSyspp) { + irSource = "T0VTX"; + } else { + irSource = "ZNC hadronic"; + } + sqrtSNN = genRunParams->getSqrtS(); + LOG(info) << "irSource determined from General Run Parameters: " << irSource; + LOG(info) << "sqrtSNN determined from General Run Parameters: " << sqrtSNN << " GeV"; + } else { + irSource = ""; + sqrtSNN = 5360.f; + LOG(warning) << "No General Run Parameters object found. irSource will remain undefined, sqrtSNN defaulted to 5360 GeV"; + } +} + struct OccupancyValues { float tmoPrimUnfm80{UndefValueFloat}; float tmoFV0AUnfm80{UndefValueFloat}; @@ -129,12 +157,12 @@ using TrackMeanOccs = soa::Join -inline void evaluateOccupancyVariables(const TrkType& track, OccupancyValues& occValues) +void evaluateOccupancyVariables(const TrkType& track, OccupancyValues& occValues) { if (track.tmoId() == -1) { return; } - const auto& tmoFromTrack = track.template tmo_as(); + const auto tmoFromTrack = track.template tmo_as(); occValues.tmoPrimUnfm80 = tmoFromTrack.tmoPrimUnfm80(); occValues.tmoFV0AUnfm80 = tmoFromTrack.tmoFV0AUnfm80(); occValues.tmoFT0AUnfm80 = tmoFromTrack.tmoFT0AUnfm80(); diff --git a/EventFiltering/PWGCF/CFFilterAll.cxx b/EventFiltering/PWGCF/CFFilterAll.cxx index 3f2540f27b0..97095703f2a 100644 --- a/EventFiltering/PWGCF/CFFilterAll.cxx +++ b/EventFiltering/PWGCF/CFFilterAll.cxx @@ -1087,7 +1087,7 @@ struct CFFilterAll { } template - bool checkTrack(T const& track, std::string trackName) + bool checkTrack(T const& track, const std::string& trackName) { if (track.pt() < TrackSelections.momentum->get(trackName.c_str(), "PtMin")) { return false; @@ -1135,7 +1135,7 @@ struct CFFilterAll { } template - bool checkTrackPid(T const& track, std::string trackName) + bool checkTrackPid(T const& track, const std::string& trackName) { float momentum = -99; @@ -1236,8 +1236,8 @@ struct CFFilterAll { return true; } - float getkstar(const ROOT::Math::PtEtaPhiMVector part1, - const ROOT::Math::PtEtaPhiMVector part2) + float getkstar(const ROOT::Math::PtEtaPhiMVector& part1, + const ROOT::Math::PtEtaPhiMVector& part2) { const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; const float beta = trackSum.Beta(); @@ -1257,7 +1257,7 @@ struct CFFilterAll { } ROOT::Math::PxPyPzEVector - getqij(const ROOT::Math::PtEtaPhiMVector parti, const ROOT::Math::PtEtaPhiMVector partj) + getqij(const ROOT::Math::PtEtaPhiMVector& parti, const ROOT::Math::PtEtaPhiMVector& partj) { ROOT::Math::PxPyPzEVector vecparti(parti); ROOT::Math::PxPyPzEVector vecpartj(partj); @@ -1266,7 +1266,7 @@ struct CFFilterAll { float scaling = trackDifference.Dot(trackSum) / trackSum.Dot(trackSum); return trackDifference - scaling * trackSum; } - float getQ3(const ROOT::Math::PtEtaPhiMVector part1, const ROOT::Math::PtEtaPhiMVector part2, const ROOT::Math::PtEtaPhiMVector part3) + float getQ3(const ROOT::Math::PtEtaPhiMVector& part1, const ROOT::Math::PtEtaPhiMVector& part2, const ROOT::Math::PtEtaPhiMVector& part3) { ROOT::Math::PxPyPzEVector q12 = getqij(part1, part2); ROOT::Math::PxPyPzEVector q23 = getqij(part2, part3); diff --git a/EventFiltering/PWGEM/HeavyNeutralMesonFilter.cxx b/EventFiltering/PWGEM/HeavyNeutralMesonFilter.cxx index 763b3a70bb2..bb03a3fea79 100644 --- a/EventFiltering/PWGEM/HeavyNeutralMesonFilter.cxx +++ b/EventFiltering/PWGEM/HeavyNeutralMesonFilter.cxx @@ -289,8 +289,8 @@ struct HeavyNeutralMesonFilter { return true; } - float getkstar(const ROOT::Math::PtEtaPhiMVector part1, - const ROOT::Math::PtEtaPhiMVector part2) + float getkstar(const ROOT::Math::PtEtaPhiMVector& part1, + const ROOT::Math::PtEtaPhiMVector& part2) { const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; const float beta = trackSum.Beta(); @@ -306,8 +306,8 @@ struct HeavyNeutralMesonFilter { return 0.5 * trackRelK.P(); } - ROOT::Math::PxPyPzEVector getqij(const ROOT::Math::PtEtaPhiMVector parti, - const ROOT::Math::PtEtaPhiMVector partj) + ROOT::Math::PxPyPzEVector getqij(const ROOT::Math::PtEtaPhiMVector& parti, + const ROOT::Math::PtEtaPhiMVector& partj) { ROOT::Math::PxPyPzEVector vecparti(parti); ROOT::Math::PxPyPzEVector vecpartj(partj); @@ -316,9 +316,9 @@ struct HeavyNeutralMesonFilter { float scaling = trackDifference.Dot(trackSum) / trackSum.Dot(trackSum); return trackDifference - scaling * trackSum; } - float getQ3(const ROOT::Math::PtEtaPhiMVector part1, - const ROOT::Math::PtEtaPhiMVector part2, - const ROOT::Math::PtEtaPhiMVector part3) + float getQ3(const ROOT::Math::PtEtaPhiMVector& part1, + const ROOT::Math::PtEtaPhiMVector& part2, + const ROOT::Math::PtEtaPhiMVector& part3) { ROOT::Math::PxPyPzEVector q12 = getqij(part1, part2); ROOT::Math::PxPyPzEVector q23 = getqij(part2, part3); diff --git a/EventFiltering/PWGEM/globalDimuonFilter.cxx b/EventFiltering/PWGEM/globalDimuonFilter.cxx index be71fc72cd6..f152dd22f6d 100644 --- a/EventFiltering/PWGEM/globalDimuonFilter.cxx +++ b/EventFiltering/PWGEM/globalDimuonFilter.cxx @@ -13,6 +13,7 @@ // \author daiki.sekihata@cern.ch #include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/fwdtrackUtilities.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" @@ -37,7 +38,6 @@ #include #include #include -#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) @@ -46,7 +46,9 @@ #include #include +#include #include +#include #include #include #include @@ -57,103 +59,111 @@ #include -using namespace o2; -using namespace o2::soa; -using namespace o2::aod; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace o2::constants::physics; -using namespace o2::aod::fwdtrackutils; - struct globalDimuonFilter { - Produces tags; + o2::framework::Produces tags; // Configurables - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable minNmuon{"minNmuon", 2, "min number of global muon candidates per collision"}; + o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + o2::framework::Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + o2::framework::Configurable minNmuon{"minNmuon", 2, "min number of global muon candidates per collision"}; - struct : ConfigurableGroup { + struct : o2::framework::ConfigurableGroup { std::string prefix = "eventCutGroup"; - Configurable minZvtx{"minZvtx", -11.f, "min. Zvtx of collision"}; - Configurable maxZvtx{"maxZvtx", +11.f, "max. Zvtx of collision"}; - Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND"}; - Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border"}; - Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border"}; + o2::framework::Configurable cfgNcontribMin{"cfgNcontribMin", 0, "min. Ncontributors to PV"}; + o2::framework::Configurable cfgChi2PerNcontribMax{"cfgChi2PerNcontribMax", 1e+10, "max. chi2/Ncontrib of PV"}; + o2::framework::Configurable minZvtx{"minZvtx", -11.f, "min. Zvtx of collision"}; + o2::framework::Configurable maxZvtx{"maxZvtx", +11.f, "max. Zvtx of collision"}; + o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND"}; + o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border"}; + o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border"}; + o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. + o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; } eventCutGroup; - struct : ConfigurableGroup { + struct : o2::framework::ConfigurableGroup { std::string prefix = "glMuonCutGroup"; - Configurable minPt{"minPt", 0.01, "min pt for muon"}; - Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; - Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; - Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; - Configurable minRabs{"minRabs", 17.6, "min. R at absorber end for global muon (min. eta = -3.6)"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 - Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; - Configurable maxDCAxy{"maxDCAxy", 0.2, "max. DCAxy for global muons"}; - Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 100.f, "max. chi2 for MCH-MFT matching"}; - Configurable maxChi2{"maxChi2", 20, "max. chi2/ndf for global muon"}; - Configurable maxChi2MFT{"maxChi2MFT", 1e+10, "max. chi2/ndf for MFTsa"}; - Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; - Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; - Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; - Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; - Configurable maxDEta{"maxDEta", 0.15, "max. deta between MFT-MCH-MID and MCH-MID"}; - Configurable maxDPhi{"maxDPhi", 0.15, "max. dphi between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable minPt{"minPt", 0.55, "min pt for muon"}; + o2::framework::Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; + o2::framework::Configurable minEta{"minEta", -3.65, "min. eta acceptance for MFT-MCH-MID"}; + o2::framework::Configurable maxEta{"maxEta", -2.45, "max. eta acceptance for MFT-MCH-MID"}; + o2::framework::Configurable minRabs{"minRabs", 17.6, "min. R at absorber end for global muon"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 + o2::framework::Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; + o2::framework::Configurable maxDCAxy{"maxDCAxy", 0.5, "max. DCAxy for global muons"}; + o2::framework::Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 100.f, "max. chi2 for MCH-MFT matching"}; + o2::framework::Configurable maxChi2{"maxChi2", 1e+10, "max. chi2/ndf for global muon"}; + o2::framework::Configurable maxChi2MFT{"maxChi2MFT", 1e+10, "max. chi2/ndf for MFTsa"}; + o2::framework::Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; + o2::framework::Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; + o2::framework::Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; + o2::framework::Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; + o2::framework::Configurable maxDEta{"maxDEta", 1e+10, "max. deta between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable maxDPhi{"maxDPhi", 1e+10, "max. dphi between MFT-MCH-MID and MCH-MID"}; } glMuonCutGroup; - struct : ConfigurableGroup { // tight cut + struct : o2::framework::ConfigurableGroup { // tight cut std::string prefix = "tagMuonCutGroup"; - Configurable minPt{"minPt", 0.4, "min pt for muon"}; - Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; - Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; - Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; - Configurable minRabs{"minRabs", 27.6, "min. R at absorber end for global muon (min. eta = -3.6)"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 - Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; - Configurable maxDCAxy{"maxDCAxy", 0.06, "max. DCAxy for global muons"}; - Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 40.f, "max. chi2 for MCH-MFT matching"}; - Configurable maxChi2{"maxChi2", 4.f, "max. chi2/ndf for global muon"}; - Configurable maxChi2MFT{"maxChi2MFT", 4.f, "max. chi2/ndf for MFTsa"}; - Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; - Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; - Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; - Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; - Configurable maxDEta{"maxDEta", 0.08, "max. deta between MFT-MCH-MID and MCH-MID"}; - Configurable maxDPhi{"maxDPhi", 0.08, "max. dphi between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable minPt{"minPt", 0.8, "min pt for muon"}; + o2::framework::Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; + o2::framework::Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; + o2::framework::Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; + o2::framework::Configurable minRabs{"minRabs", 27.6, "min. R at absorber end for global muon (min. eta = -3.6)"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 + o2::framework::Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; + o2::framework::Configurable maxDCAxy{"maxDCAxy", 0.06, "max. DCAxy for global muons"}; + o2::framework::Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 40.f, "max. chi2 for MCH-MFT matching"}; + o2::framework::Configurable maxChi2{"maxChi2", 4.f, "max. chi2/ndf for global muon"}; + o2::framework::Configurable maxChi2MFT{"maxChi2MFT", 4.f, "max. chi2/ndf for MFTsa"}; + o2::framework::Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; + o2::framework::Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; + o2::framework::Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; + o2::framework::Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; + o2::framework::Configurable maxDEta{"maxDEta", 0.08, "max. deta between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable maxDPhi{"maxDPhi", 0.08, "max. dphi between MFT-MCH-MID and MCH-MID"}; } tagMuonCutGroup; - struct : ConfigurableGroup { // loose cut + struct : o2::framework::ConfigurableGroup { // loose cut std::string prefix = "probeMuonCutGroup"; - Configurable minPt{"minPt", 0.01, "min pt for muon"}; - Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; - Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; - Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; - Configurable minRabs{"minRabs", 17.6, "min. R at absorber end for global muon (min. eta = -3.6)"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 - Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; - Configurable maxDCAxy{"maxDCAxy", 1e+10, "max. DCAxy for global muons"}; - Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 1e+10, "max. chi2 for MCH-MFT matching"}; - Configurable maxChi2{"maxChi2", 1e+10, "max. chi2/ndf for global muon"}; - Configurable maxChi2MFT{"maxChi2MFT", 1e+10, "max. chi2/ndf for MFTsa"}; - Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; - Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; - Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; - Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; - Configurable maxDEta{"maxDEta", 1e+10, "max. deta between MFT-MCH-MID and MCH-MID"}; - Configurable maxDPhi{"maxDPhi", 1e+10, "max. dphi between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable minPt{"minPt", 0.01, "min pt for muon"}; + o2::framework::Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; + o2::framework::Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; + o2::framework::Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; + o2::framework::Configurable minRabs{"minRabs", 17.6, "min. R at absorber end for global muon (min. eta = -3.6)"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 + o2::framework::Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; + o2::framework::Configurable maxDCAxy{"maxDCAxy", 1e+10, "max. DCAxy for global muons"}; + o2::framework::Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 1e+10, "max. chi2 for MCH-MFT matching"}; + o2::framework::Configurable maxChi2{"maxChi2", 1e+10, "max. chi2/ndf for global muon"}; + o2::framework::Configurable maxChi2MFT{"maxChi2MFT", 1e+10, "max. chi2/ndf for MFTsa"}; + o2::framework::Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; + o2::framework::Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; + o2::framework::Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; + o2::framework::Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; + o2::framework::Configurable maxDEta{"maxDEta", 1e+10, "max. deta between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable maxDPhi{"maxDPhi", 1e+10, "max. dphi between MFT-MCH-MID and MCH-MID"}; } probeMuonCutGroup; // for z shift for propagation - Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift"}; - Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; - Configurable cfgManualZShift{"cfgManualZShift", 0, "manual z-shift for propagation of global muon to PV"}; - - HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + o2::framework::Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift"}; + o2::framework::Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; + o2::framework::Configurable cfgManualXShiftMFTtop{"cfgManualXShiftMFTtop", 0, "manual x shift for propagation of global muon to PV"}; + o2::framework::Configurable cfgManualYShiftMFTtop{"cfgManualYShiftMFTtop", 0, "manual y shift for propagation of global muon to PV"}; + o2::framework::Configurable cfgManualZShiftMFTtop{"cfgManualZShiftMFTtop", 0, "manual z shift for propagation of global muon to PV"}; + o2::framework::Configurable cfgManualXShiftMFTbottom{"cfgManualXShiftMFTbottom", 0, "manual x shift for propagation of global muon to PV"}; + o2::framework::Configurable cfgManualYShiftMFTbottom{"cfgManualYShiftMFTbottom", 0, "manual y shift for propagation of global muon to PV"}; + o2::framework::Configurable cfgManualZShiftMFTbottom{"cfgManualZShiftMFTbottom", 0, "manual z shift for propagation of global muon to PV"}; + + o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; + o2::framework::Service ccdb; int mRunNumber = 0; float mBz = 0; - float mZShift = 0; + float mXShiftMFTtop = 0; + float mYShiftMFTtop = 0; + float mZShiftMFTtop = 0; + float mXShiftMFTbottom = 0; + float mYShiftMFTbottom = 0; + float mZShiftMFTbottom = 0; void init(o2::framework::InitContext&) { @@ -164,7 +174,12 @@ struct globalDimuonFilter { ccdbApi.init(ccdburl); mRunNumber = 0; mBz = 0; - mZShift = 0; + mXShiftMFTtop = 0; + mYShiftMFTtop = 0; + mZShiftMFTtop = 0; + mXShiftMFTbottom = 0; + mYShiftMFTbottom = 0; + mZShiftMFTbottom = 0; addHistograms(); } @@ -187,7 +202,7 @@ struct globalDimuonFilter { } o2::mch::TrackExtrap::setField(); const double centerMFT[3] = {0, 0, -61.4}; - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); mBz = field->getBz(centerMFT); // Get field at centre of MFT LOGF(info, "Bz at center of MFT = %f kZG", mBz); @@ -195,20 +210,32 @@ struct globalDimuonFilter { auto* zShift = ccdb->getForTimeStamp>(cfgZShiftPath, bc.timestamp()); if (zShift != nullptr && !zShift->empty()) { LOGF(info, "reading z shift %f from %s", (*zShift)[0], cfgZShiftPath.value); - mZShift = (*zShift)[0]; + mZShiftMFTtop = (*zShift)[0]; + mZShiftMFTbottom = (*zShift)[0]; } else { LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", cfgZShiftPath.value); - mZShift = 0; + mZShiftMFTtop = 0; + mZShiftMFTbottom = 0; } } else { - LOGF(info, "z shift is manually set to %f cm", cfgManualZShift.value); - mZShift = cfgManualZShift; + LOGF(info, "X shift for MFT top is manually set to %f cm", cfgManualXShiftMFTtop.value); + LOGF(info, "X shift for MFT bottom is manually set to %f cm", cfgManualXShiftMFTbottom.value); + LOGF(info, "Y shift for MFT top is manually set to %f cm", cfgManualYShiftMFTtop.value); + LOGF(info, "Y shift for MFT bottom is manually set to %f cm", cfgManualYShiftMFTbottom.value); + LOGF(info, "Z shift for MFT top is manually set to %f cm", cfgManualZShiftMFTtop.value); + LOGF(info, "Z shift for MFT bottom is manually set to %f cm", cfgManualZShiftMFTbottom.value); + mXShiftMFTtop = cfgManualXShiftMFTtop; + mYShiftMFTtop = cfgManualYShiftMFTtop; + mZShiftMFTtop = cfgManualZShiftMFTtop; + mXShiftMFTbottom = cfgManualXShiftMFTbottom; + mYShiftMFTbottom = cfgManualYShiftMFTbottom; + mZShiftMFTbottom = cfgManualZShiftMFTbottom; } } void addHistograms() { - auto hCollisionCounter{std::get>(fRegistry.add("hCollisionCounter", "Number of collisions", HistType::kTH1D, {{10, 0.5, 10.5}}))}; + auto hCollisionCounter{std::get>(fRegistry.add("hCollisionCounter", "Number of collisions", o2::framework::HistType::kTH1D, {{10, 0.5, 10.5}}))}; hCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); hCollisionCounter->GetXaxis()->SetBinLabel(2, "TVX"); hCollisionCounter->GetXaxis()->SetBinLabel(3, "No TFB"); @@ -219,73 +246,79 @@ struct globalDimuonFilter { hCollisionCounter->GetXaxis()->SetBinLabel(8, "MB && global dimuon"); hCollisionCounter->GetXaxis()->SetBinLabel(9, "MB && global dimuon for QA"); - fRegistry.add("hNmu", "#mu multiplicity;N_{#mu}^{global} candidates per collision", kTH1D, {{11, -0.5, 10.5}}, false); - - fRegistry.add("MFTMCHMID/hPt", "pT;p_{T} (GeV/c)", kTH1D, {{200, 0.0f, 10}}, false); - fRegistry.add("MFTMCHMID/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2D, {{180, 0, 2 * M_PI}, {200, -4.f, -2.f}}, false); - fRegistry.add("MFTMCHMID/hEtaPhi_MatchedMCHMID", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2D, {{180, 0, 2 * M_PI}, {200, -4.f, -2.f}}, false); - fRegistry.add("MFTMCHMID/hDEtaDPhi", "#Delta#eta vs. #Delta#varphi;#Delta#varphi (rad.);#Delta#eta", kTH2D, {{90, -M_PI / 4, M_PI / 4}, {100, -0.5, +0.5}}, false); - fRegistry.add("MFTMCHMID/hSign", "sign;sign", kTH1D, {{3, -1.5, +1.5}}, false); - fRegistry.add("MFTMCHMID/hNclusters", "Nclusters;Nclusters", kTH1D, {{21, -0.5f, 20.5}}, false); - fRegistry.add("MFTMCHMID/hNclustersMFT", "NclustersMFT;Nclusters MFT", kTH1D, {{11, -0.5f, 10.5}}, false); - fRegistry.add("MFTMCHMID/hRatAbsorberEnd", "R at absorber end;R at absorber end (cm)", kTH1D, {{200, 0, 100}}, false); - fRegistry.add("MFTMCHMID/hPDCA_Rabs", "pDCA vs. Rabs;R at absorber end (cm);p #times DCA (GeV/c #upoint cm)", kTH2D, {{200, 0, 100}, {100, 0, 1000}}, false); - fRegistry.add("MFTMCHMID/hChi2", "chi2;#chi^{2}/ndf", kTH1D, {{100, 0.0f, 10}}, false); - fRegistry.add("MFTMCHMID/hChi2MFT", "chi2 MFT;#chi^{2} MFT/ndf", kTH1D, {{100, 0.0f, 10}}, false); - fRegistry.add("MFTMCHMID/hChi2MatchMCHMID", "chi2 match MCH-MID;matching #chi^{2}/ndf between MCH-MID", kTH1D, {{200, 0.0f, 20}}, false); - fRegistry.add("MFTMCHMID/hChi2MatchMCHMFT", "chi2 match MCH-MFT;matching #chi^{2}/ndf between MFT-MCH", kTH1D, {{100, 0.0f, 100}}, false); - fRegistry.add("MFTMCHMID/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", kTH2D, {{400, -1, 1}, {400, -1, +1}}, false); - fRegistry.add("MFTMCHMID/hDCAxy2DinSigma", "DCA x vs. y in sigma;DCA_{x} (#sigma);DCA_{y} (#sigma)", kTH2D, {{200, -10, 10}, {200, -10, +10}}, false); - fRegistry.add("MFTMCHMID/hDCAxy", "DCAxy;DCA_{xy} (cm);", kTH1D, {{100, 0, 1}}, false); - fRegistry.add("MFTMCHMID/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2D, {{100, 0, 1}, {200, -0.1, 0.1}}, false); - fRegistry.add("MFTMCHMID/hDCAxyinSigma", "DCAxy in sigma;DCA_{xy} (#sigma);", kTH1D, {{100, 0, 10}}, false); - fRegistry.add("MFTMCHMID/hDCAxResolutionvsPt", "DCA_{x} resolution vs. p_{T};p_{T} (GeV/c);DCA_{x} resolution (#mum);", kTH2D, {{100, 0, 10.f}, {500, 0, 500}}, false); - fRegistry.add("MFTMCHMID/hDCAyResolutionvsPt", "DCA_{y} resolution vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2D, {{100, 0, 10.f}, {500, 0, 500}}, false); - fRegistry.add("MFTMCHMID/hDCAxyResolutionvsPt", "DCA_{xy} resolution vs. p_{T};p_{T} (GeV/c);DCA_{xy} resolution (#mum);", kTH2D, {{100, 0, 10.f}, {500, 0, 500}}, false); - fRegistry.add("MFTMCHMID/hDCAx_PosZ", "DCAx vs. posZ;Z_{vtx} (cm);DCA_{x} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hDCAy_PosZ", "DCAy vs. posZ;Z_{vtx} (cm);DCA_{y} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hDCAx_Phi", "DCAx vs. #varphi;#varphi (rad.);DCA_{x} (cm)", kTH2F, {{90, 0, 2 * M_PI}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hDCAy_Phi", "DCAy vs. #varphi;#varphi (rad.);DCA_{y} (cm)", kTH2F, {{90, 0, 2 * M_PI}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hdR_Chi2MatchMCHMFT", "dr vs. matching chi2 MCH-MFT;chi2 match MCH-MFT;#DeltaR", kTH2F, {{200, 0, 50}, {200, 0, 0.5}}, false); - fRegistry.add("MFTMCHMID/hdR_Chi2", "dr vs. chi2;global chi2/ndf;#DeltaR", kTH2F, {{100, 0, 10}, {200, 0, 0.5}}, false); - fRegistry.add("MFTMCHMID/hChi2_Chi2MatchMCHMFT", "chi2 vs. matching chi2 MCH-MFT;chi2 match MCH-MFT;global chi2/ndf", kTH2F, {{200, 0, 50}, {100, 0, 10}}, false); - - const AxisSpec axisMll{{0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.50, 5.00, 5.50, 6.00, 6.50, 7.00, 7.50, 8.00, 8.1, 8.2, 8.3, 8.4, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0}, "m_{#mu#mu} (GeV/c^{2})"}; - const AxisSpec axisPtll{{0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10}, "p_{T,#mu#mu} (GeV/c)"}; - const AxisSpec axisYll{40, -4.0, -2.0, "y_{#mu#mu}"}; - - fRegistry.add("Pair/same/uls/hs", "dimuon", kTHnSparseD, {axisMll, axisPtll, axisYll}, false); - fRegistry.add("Pair/same/lspp/hs", "dimuon", kTHnSparseD, {axisMll, axisPtll, axisYll}, false); - fRegistry.add("Pair/same/lsmm/hs", "dimuon", kTHnSparseD, {axisMll, axisPtll, axisYll}, false); - - const AxisSpec axisMllLMR{{0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.01, 2.02, 2.03, 2.04, 2.05, 2.06, 2.07, 2.08, 2.09, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18, 2.19, 2.20, 2.21, 2.22, 2.23, 2.24, 2.25, 2.26, 2.27, 2.28, 2.29, 2.30, 2.31, 2.32, 2.33, 2.34, 2.35, 2.36, 2.37, 2.38, 2.39, 2.40, 2.41, 2.42, 2.43, 2.44, 2.45, 2.46, 2.47, 2.48, 2.49, 2.50, 2.51, 2.52, 2.53, 2.54, 2.55, 2.56, 2.57, 2.58, 2.59, 2.60, 2.61, 2.62, 2.63, 2.64, 2.65, 2.66, 2.67, 2.68, 2.69, 2.70, 2.71, 2.72, 2.73, 2.74, 2.75, 2.76, 2.77, 2.78, 2.79, 2.80, 2.81, 2.82, 2.83, 2.84, 2.85, 2.86, 2.87, 2.88, 2.89, 2.90, 2.91, 2.92, 2.93, 2.94, 2.95, 2.96, 2.97, 2.98, 2.99, 3.00, 3.01, 3.02, 3.03, 3.04, 3.05, 3.06, 3.07, 3.08, 3.09, 3.10, 3.11, 3.12, 3.13, 3.14, 3.15, 3.16, 3.17, 3.18, 3.19, 3.20, 3.21, 3.22, 3.23, 3.24, 3.25, 3.26, 3.27, 3.28, 3.29, 3.30, 3.31, 3.32, 3.33, 3.34, 3.35, 3.36, 3.37, 3.38, 3.39, 3.40, 3.41, 3.42, 3.43, 3.44, 3.45, 3.46, 3.47, 3.48, 3.49, 3.50, 3.51, 3.52, 3.53, 3.54, 3.55, 3.56, 3.57, 3.58, 3.59, 3.60, 3.61, 3.62, 3.63, 3.64, 3.65, 3.66, 3.67, 3.68, 3.69, 3.70, 3.71, 3.72, 3.73, 3.74, 3.75, 3.76, 3.77, 3.78, 3.79, 3.80, 3.81, 3.82, 3.83, 3.84, 3.85, 3.86, 3.87, 3.88, 3.89, 3.90, 3.91, 3.92, 3.93, 3.94, 3.95, 3.96, 3.97, 3.98, 3.99, 4.00}, "m_{#mu#mu} (GeV/c^{2})"}; - const AxisSpec axisPt{{0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10}, "p_{T,#mu} (GeV/c)"}; - const AxisSpec axisEta{40, -4.0, -2.0, "#eta_{#mu}"}; - const AxisSpec axisPhi{36, 0, 2 * M_PI, "#varphi_{#mu} (rad.)"}; - const AxisSpec axisDEta{100, -0.5, 0.5, "#Delta#eta"}; - const AxisSpec axisDPhi{90, -M_PI / 4, M_PI / 4, "#Delta#varphi (rad.)"}; - const AxisSpec axisDCAxy{100, 0, 1, "DCA_{xy} (cm)"}; - const AxisSpec axisChi2{200, 0, 20, "global #chi^{2}/ndf"}; - const AxisSpec axisChi2MFT{100, 0, 10, "MFTsa #chi^{2}/ndf"}; - const AxisSpec axisChi2MFTMCH{100, 0, 100, "matching #chi^{2}/ndf between MFT-MCH"}; - const AxisSpec axisPDCA{100, 0, 1000, "p #times DCA_{xy} (GeV/c #upoint cm)"}; - const AxisSpec axisRabs{150, 15, 90, "R_{xy} at absorber end (cm)"}; - const AxisSpec axisNclsMCH{21, -0.5, 20.5, "N_{cls}^{MCH}"}; - const AxisSpec axisNclsMFT{11, -0.5, 10.5, "N_{cls}^{MFT}"}; + fRegistry.add("hNmu", "#mu multiplicity;N_{#mu}^{global} candidates per collision", o2::framework::HistType::kTH1D, {{11, -0.5, 10.5}}, false); + + fRegistry.add("Vertex/MB/hZvtx", "vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{40, -20, +20}}, false); + fRegistry.add("Vertex/MB/hChi2vsNContrib", "vertex #chi^{2}/N_{contrib} vs. N_{contrib};N_{contrib};#chi^{2}/N_{contrib}", o2::framework::HistType::kTH2D, {{200, 0.5, 200.5}, {100, 0, 10}}, false); + fRegistry.add("Vertex/MB/hSigmaX", "vertex #sigma_{X} vs. N_{contrib};N_{contrib};#sigma_{X} (#mum)", o2::framework::HistType::kTH2D, {{200, 0.5, 200.5}, {1000, 0, 100}}, false); + fRegistry.add("Vertex/MB/hSigmaY", "vertex #sigma_{Y} vs. N_{contrib};N_{contrib};#sigma_{Y} (#mum)", o2::framework::HistType::kTH2D, {{200, 0.5, 200.5}, {1000, 0, 100}}, false); + fRegistry.add("Vertex/MB/hSigmaZ", "vertex #sigma_{Z} vs. N_{contrib};N_{contrib};#sigma_{Z} (#mum)", o2::framework::HistType::kTH2D, {{200, 0.5, 200.5}, {1000, 0, 100}}, false); + fRegistry.addClone("Vertex/MB/", "Vertex/Triggered/"); + + fRegistry.add("MFTMCHMID/positive/hPt", "pT;p_{T} (GeV/c)", o2::framework::HistType::kTH1D, {{200, 0.0f, 10}}, false); + fRegistry.add("MFTMCHMID/positive/hPtResolution", "pT;p_{T} (GeV/c);#Deltap_{T}/p_{T}", o2::framework::HistType::kTH2D, {{200, 0.0f, 10}, {200, 0, 0.1}}, false); + fRegistry.add("MFTMCHMID/positive/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", o2::framework::HistType::kTH2D, {{180, 0, 2 * M_PI}, {200, -4.f, -2.f}}, false); + fRegistry.add("MFTMCHMID/positive/hEtaPhi_MatchedMCHMID", "#eta vs. #varphi;#varphi (rad.);#eta", o2::framework::HistType::kTH2D, {{180, 0, 2 * M_PI}, {200, -4.f, -2.f}}, false); + fRegistry.add("MFTMCHMID/positive/hDEtaDPhi", "#Delta#eta vs. #Delta#varphi;#Delta#varphi (rad.);#Delta#eta", o2::framework::HistType::kTH2D, {{90, -M_PI / 4, M_PI / 4}, {100, -0.5, +0.5}}, false); + fRegistry.add("MFTMCHMID/positive/hSign", "sign;sign", o2::framework::HistType::kTH1D, {{3, -1.5, +1.5}}, false); + fRegistry.add("MFTMCHMID/positive/hNclusters", "Nclusters;Nclusters", o2::framework::HistType::kTH1D, {{21, -0.5f, 20.5}}, false); + fRegistry.add("MFTMCHMID/positive/hNclustersMFT", "NclustersMFT;Nclusters MFT", o2::framework::HistType::kTH1D, {{11, -0.5f, 10.5}}, false); + fRegistry.add("MFTMCHMID/positive/hRatAbsorberEnd", "R at absorber end;R at absorber end (cm)", o2::framework::HistType::kTH1D, {{200, 0, 100}}, false); + fRegistry.add("MFTMCHMID/positive/hPDCA_Rabs", "pDCA vs. Rabs;R at absorber end (cm);p #times DCA (GeV/c #upoint cm)", o2::framework::HistType::kTH2D, {{200, 0, 100}, {100, 0, 1000}}, false); + fRegistry.add("MFTMCHMID/positive/hChi2_Pt", "chi2;p_{T,#mu} (GeV/c);#chi^{2}/ndf", o2::framework::HistType::kTH2D, {{200, 0, 10}, {100, 0.0f, 10}}, false); + fRegistry.add("MFTMCHMID/positive/hChi2MFT_Pt", "chi2 MFT;p_{T,#mu} (GeV/c);#chi^{2} MFT/ndf", o2::framework::HistType::kTH2D, {{200, 0, 10}, {100, 0.0f, 10}}, false); + fRegistry.add("MFTMCHMID/positive/hChi2MatchMCHMID_Pt", "chi2 match MCH-MID;p_{T,#mu} (GeV/c);matching #chi^{2}/ndf between MCH-MID", o2::framework::HistType::kTH2D, {{200, 0, 10}, {200, 0.0f, 20}}, false); + fRegistry.add("MFTMCHMID/positive/hChi2MatchMCHMFT_Pt", "chi2 match MCH-MFT;p_{T,#mu} (GeV/c);matching #chi^{2}/ndf between MFT-MCH", o2::framework::HistType::kTH2D, {{200, 0, 10}, {100, 0.0f, 100}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", o2::framework::HistType::kTH2D, {{400, -1, 1}, {400, -1, +1}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAxy2DinSigma", "DCA x vs. y in sigma;DCA_{x} (#sigma);DCA_{y} (#sigma)", o2::framework::HistType::kTH2D, {{200, -10, 10}, {200, -10, +10}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAxy", "DCAxy;DCA_{xy} (cm);", o2::framework::HistType::kTH1D, {{1000, 0, 1}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAxyinSigma", "DCAxy in sigma;DCA_{xy} (#sigma);", o2::framework::HistType::kTH1D, {{100, 0, 10}}, false); + fRegistry.add("MFTMCHMID/positive/hLog10Chi2IP", "chi2IP;log_{10}(#chi^{2}_{IP})", o2::framework::HistType::kTH1D, {{1000, -5, 5}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAxResolutionvsPt", "DCA_{x} resolution vs. p_{T};p_{T} (GeV/c);DCA_{x} resolution (#mum);", o2::framework::HistType::kTH2D, {{100, 0, 10.f}, {500, 0, 500}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAyResolutionvsPt", "DCA_{y} resolution vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", o2::framework::HistType::kTH2D, {{100, 0, 10.f}, {500, 0, 500}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAxyResolutionvsPt", "DCA_{xy} resolution vs. p_{T};p_{T} (GeV/c);DCA_{xy} resolution (#mum);", o2::framework::HistType::kTH2D, {{100, 0, 10.f}, {500, 0, 500}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAx_Phi", "DCAx vs. #varphi;#varphi (rad.);DCA_{x} (cm)", o2::framework::HistType::kTH2D, {{180, -M_PI, M_PI}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAy_Phi", "DCAy vs. #varphi;#varphi (rad.);DCA_{y} (cm)", o2::framework::HistType::kTH2D, {{180, -M_PI, M_PI}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAx_PosZ_MFTtop", "DCAx vs. posZ for MFT top;Z_{vtx} (cm);DCA_{x} (cm)", o2::framework::HistType::kTH2D, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAy_PosZ_MFTtop", "DCAy vs. posZ for MFT top;Z_{vtx} (cm);DCA_{y} (cm)", o2::framework::HistType::kTH2D, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAx_PosZ_MFTbottom", "DCAx vs. posZ for MFT bottom;Z_{vtx} (cm);DCA_{x} (cm)", o2::framework::HistType::kTH2D, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/positive/hDCAy_PosZ_MFTbottom", "DCAy vs. posZ for MFT bottom;Z_{vtx} (cm);DCA_{y} (cm)", o2::framework::HistType::kTH2D, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.addClone("MFTMCHMID/positive/", "MFTMCHMID/negative/"); + + const o2::framework::AxisSpec axisMll{{0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.505, 0.51, 0.515, 0.52, 0.525, 0.53, 0.535, 0.54, 0.545, 0.55, 0.555, 0.56, 0.565, 0.57, 0.575, 0.58, 0.585, 0.59, 0.595, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.50, 5.00, 5.50, 6.00, 6.50, 7.00, 7.50, 8.00, 8.1, 8.2, 8.3, 8.4, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0}, "m_{#mu#mu} (GeV/c^{2})"}; + const o2::framework::AxisSpec axisPtll{{0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10}, "p_{T,#mu#mu} (GeV/c)"}; + const o2::framework::AxisSpec axisYll{40, -4.0, -2.0, "y_{#mu#mu}"}; + + fRegistry.add("Pair/same/uls/hs", "dimuon", o2::framework::HistType::kTHnSparseD, {axisMll, axisPtll, axisYll}, false); + fRegistry.add("Pair/same/lspp/hs", "dimuon", o2::framework::HistType::kTHnSparseD, {axisMll, axisPtll, axisYll}, false); + fRegistry.add("Pair/same/lsmm/hs", "dimuon", o2::framework::HistType::kTHnSparseD, {axisMll, axisPtll, axisYll}, false); + + const o2::framework::AxisSpec axisMllLMR{{0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.01, 2.02, 2.03, 2.04, 2.05, 2.06, 2.07, 2.08, 2.09, 2.10, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18, 2.19, 2.20, 2.21, 2.22, 2.23, 2.24, 2.25, 2.26, 2.27, 2.28, 2.29, 2.30, 2.31, 2.32, 2.33, 2.34, 2.35, 2.36, 2.37, 2.38, 2.39, 2.40, 2.41, 2.42, 2.43, 2.44, 2.45, 2.46, 2.47, 2.48, 2.49, 2.50, 2.51, 2.52, 2.53, 2.54, 2.55, 2.56, 2.57, 2.58, 2.59, 2.60, 2.61, 2.62, 2.63, 2.64, 2.65, 2.66, 2.67, 2.68, 2.69, 2.70, 2.71, 2.72, 2.73, 2.74, 2.75, 2.76, 2.77, 2.78, 2.79, 2.80, 2.81, 2.82, 2.83, 2.84, 2.85, 2.86, 2.87, 2.88, 2.89, 2.90, 2.91, 2.92, 2.93, 2.94, 2.95, 2.96, 2.97, 2.98, 2.99, 3.00, 3.01, 3.02, 3.03, 3.04, 3.05, 3.06, 3.07, 3.08, 3.09, 3.10, 3.11, 3.12, 3.13, 3.14, 3.15, 3.16, 3.17, 3.18, 3.19, 3.20, 3.21, 3.22, 3.23, 3.24, 3.25, 3.26, 3.27, 3.28, 3.29, 3.30, 3.31, 3.32, 3.33, 3.34, 3.35, 3.36, 3.37, 3.38, 3.39, 3.40, 3.41, 3.42, 3.43, 3.44, 3.45, 3.46, 3.47, 3.48, 3.49, 3.50, 3.51, 3.52, 3.53, 3.54, 3.55, 3.56, 3.57, 3.58, 3.59, 3.60, 3.61, 3.62, 3.63, 3.64, 3.65, 3.66, 3.67, 3.68, 3.69, 3.70, 3.71, 3.72, 3.73, 3.74, 3.75, 3.76, 3.77, 3.78, 3.79, 3.80, 3.81, 3.82, 3.83, 3.84, 3.85, 3.86, 3.87, 3.88, 3.89, 3.90, 3.91, 3.92, 3.93, 3.94, 3.95, 3.96, 3.97, 3.98, 3.99, 4.00}, "m_{#mu#mu} (GeV/c^{2})"}; + const o2::framework::AxisSpec axisPt{{0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10}, "p_{T,#mu} (GeV/c)"}; + const o2::framework::AxisSpec axisEta{40, -4.0, -2.0, "#eta_{#mu}"}; + const o2::framework::AxisSpec axisPhi{36, 0, 2 * M_PI, "#varphi_{#mu} (rad.)"}; + const o2::framework::AxisSpec axisDEta{100, -0.5, 0.5, "#Delta#eta"}; + const o2::framework::AxisSpec axisDPhi{90, -M_PI / 4, M_PI / 4, "#Delta#varphi (rad.)"}; + const o2::framework::AxisSpec axisDCAxy{100, 0, 1, "DCA_{xy} (cm)"}; + const o2::framework::AxisSpec axisChi2{200, 0, 20, "global #chi^{2}/ndf"}; + const o2::framework::AxisSpec axisChi2MFT{100, 0, 10, "MFTsa #chi^{2}/ndf"}; + const o2::framework::AxisSpec axisChi2MFTMCH{100, 0, 100, "matching #chi^{2}/ndf between MFT-MCH"}; + const o2::framework::AxisSpec axisPDCA{100, 0, 1000, "p #times DCA_{xy} (GeV/c #upoint cm)"}; + const o2::framework::AxisSpec axisRabs{150, 15, 90, "R_{xy} at absorber end (cm)"}; + const o2::framework::AxisSpec axisNclsMCH{21, -0.5, 20.5, "N_{cls}^{MCH}"}; + const o2::framework::AxisSpec axisNclsMFT{11, -0.5, 10.5, "N_{cls}^{MFT}"}; // for track cut efficiency with tag and probe - fRegistry.add("TAP/same/uls/hs", "dimuon for T&P", kTHnSparseD, {axisMllLMR, axisPt, axisEta, axisPhi, axisDEta, axisDPhi, axisDCAxy, axisChi2, axisChi2MFT, axisChi2MFTMCH, axisPDCA, axisRabs, axisNclsMCH, axisNclsMFT}, false); - fRegistry.add("TAP/same/lspp/hs", "dimuon for T&P", kTHnSparseD, {axisMllLMR, axisPt, axisEta, axisPhi, axisDEta, axisDPhi, axisDCAxy, axisChi2, axisChi2MFT, axisChi2MFTMCH, axisPDCA, axisRabs, axisNclsMCH, axisNclsMFT}, false); - fRegistry.add("TAP/same/lsmm/hs", "dimuon for T&P", kTHnSparseD, {axisMllLMR, axisPt, axisEta, axisPhi, axisDEta, axisDPhi, axisDCAxy, axisChi2, axisChi2MFT, axisChi2MFTMCH, axisPDCA, axisRabs, axisNclsMCH, axisNclsMFT}, false); + fRegistry.add("TAP/same/uls/hs", "dimuon for T&P", o2::framework::HistType::kTHnSparseD, {axisMllLMR, axisPt, axisEta, axisPhi, axisDEta, axisDPhi, axisDCAxy, axisChi2, axisChi2MFT, axisChi2MFTMCH, axisPDCA, axisRabs, axisNclsMCH, axisNclsMFT}, false); + fRegistry.add("TAP/same/lspp/hs", "dimuon for T&P", o2::framework::HistType::kTHnSparseD, {axisMllLMR, axisPt, axisEta, axisPhi, axisDEta, axisDPhi, axisDCAxy, axisChi2, axisChi2MFT, axisChi2MFTMCH, axisPDCA, axisRabs, axisNclsMCH, axisNclsMFT}, false); + fRegistry.add("TAP/same/lsmm/hs", "dimuon for T&P", o2::framework::HistType::kTHnSparseD, {axisMllLMR, axisPt, axisEta, axisPhi, axisDEta, axisDPhi, axisDCAxy, axisChi2, axisChi2MFT, axisChi2MFTMCH, axisPDCA, axisRabs, axisNclsMCH, axisNclsMFT}, false); } - std::vector> vec_min_chi2MatchMCHMFT; // std::pair -> chi2MatchMCHMFT; + std::vector> vec_min_chi2MatchMCHMFT; template void findBestMatchPerMCHMID(TCollision const&, TFwdTrack const& fwdtrack, TFwdTracks const& fwdtracks, TMFTTracks const&) { - // make a unordered_map : globalIndex of global muon -> boolean isSelected. - if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { return; } @@ -294,30 +327,10 @@ struct globalDimuonFilter { // LOGF(info, "stanadalone: muon.globalIndex() = %d, muon.chi2MatchMCHMFT() = %f", muon.globalIndex(), muon.chi2MatchMCHMFT()); // LOGF(info, "muons_per_MCHMID.size() = %d", muons_per_MCHMID.size()); - // o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mZShift); - // float etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); - // float phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); - // o2::math_utils::bringTo02Pi(phiMatchedMCHMID); - - float min_chi2MatchMCHMFT = 1e+10; + float min_chi2MatchMCHMFT = FLT_MAX; std::tuple tupleIds_at_min_chi2mftmch; for (const auto& muon_tmp : muons_per_MCHMID) { if (muon_tmp.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { - // auto tupleId = std::make_tuple(muon_tmp.globalIndex(), muon_tmp.matchMCHTrackId(), muon_tmp.matchMFTTrackId()); - // auto mchtrack = muon_tmp.template matchMCHTrack_as(); // MCH-MID - // auto mfttrack = muon_tmp.template matchMFTTrack_as(); // MFTsa - - // o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(muon_tmp, muon_tmp, collision, propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mZShift); - // float eta = propmuonAtPV.getEta(); - // float phi = propmuonAtPV.getPhi(); - // o2::math_utils::bringTo02Pi(phi); - - // float deta = etaMatchedMCHMID - eta; - // float dphi = phiMatchedMCHMID - phi; - // o2::math_utils::bringToPMPi(dphi); - // float dr = std::sqrt(deta * deta + dphi * dphi); - - // LOGF(info, "muon_tmp.globalIndex() = %d, muon_tmp.matchMCHTrackId() = %d, muon_tmp.matchMFTTrackId() = %d, muon_tmp.chi2MatchMCHMFT() = %f", muon_tmp.globalIndex(), muon_tmp.matchMCHTrackId(), muon_tmp.matchMFTTrackId(), muon_tmp.chi2MatchMCHMFT()); if (0.f < muon_tmp.chi2MatchMCHMFT() && muon_tmp.chi2MatchMCHMFT() < min_chi2MatchMCHMFT) { min_chi2MatchMCHMFT = muon_tmp.chi2MatchMCHMFT(); tupleIds_at_min_chi2mftmch = std::make_tuple(muon_tmp.globalIndex(), muon_tmp.matchMCHTrackId(), muon_tmp.matchMFTTrackId()); @@ -325,7 +338,6 @@ struct globalDimuonFilter { } } vec_min_chi2MatchMCHMFT.emplace_back(tupleIds_at_min_chi2mftmch); - // LOGF(info, "min: muon_tmp.globalIndex() = %d, muon_tmp.matchMCHTrackId() = %d, muon_tmp.matchMFTTrackId() = %d, muon_tmp.chi2MatchMCHMFT() = %f", std::get<0>(tupleIds_at_min), std::get<1>(tupleIds_at_min), std::get<2>(tupleIds_at_min), min_chi2MatchMCHMFT); } template @@ -348,7 +360,6 @@ struct globalDimuonFilter { return nGoodGLPerSA; } - PresliceUnsorted perMFTTrack = o2::aod::fwdtrack::matchMFTTrackId; template bool isBestMatch(TCollision const& collision, TFwdTrack const& fwdtrack, TFwdTracks const& fwdtracks, TMFTTracks const& mfttracks) { @@ -394,7 +405,7 @@ struct globalDimuonFilter { return false; } - if (fwdtrack.chi2MatchMCHMFT() > glMuonCutGroup.maxMatchingChi2MCHMFT) { + if (fwdtrack.chi2MatchMCHMFT() < 0.f || glMuonCutGroup.maxMatchingChi2MCHMFT < fwdtrack.chi2MatchMCHMFT()) { return false; } @@ -408,6 +419,9 @@ struct globalDimuonFilter { auto mchtrack = fwdtrack.template matchMCHTrack_as(); // MCH-MID auto mfttrack = fwdtrack.template matchMFTTrack_as(); // MFTsa + float xMFT = mfttrack.x(); + float yMFT = mfttrack.y(); + float relPtResolution = 0.f; if (mfttrack.nClusters() < glMuonCutGroup.minNclsMFT) { return false; @@ -419,52 +433,64 @@ struct globalDimuonFilter { float rAtAbsorberEnd = fwdtrack.rAtAbsorberEnd(); // this works only for GlobalMuonTrack int nClustersMFT = mfttrack.nClusters(); int ndf_mchmft = 2.f * (mchtrack.nClusters() + nClustersMFT) - 5.f; - float chi2 = fwdtrack.chi2() / ndf_mchmft; - if (glMuonCutGroup.maxChi2 < chi2) { + float chi2 = fwdtrack.chi2() / static_cast(ndf_mchmft); + if (chi2 < 0.f || glMuonCutGroup.maxChi2 < chi2) { return false; } int ndf_mft = 2.f * nClustersMFT - 5.f; - float chi2mft = mfttrack.chi2() / ndf_mft; - if (glMuonCutGroup.maxChi2MFT < chi2mft) { + float chi2mft = mfttrack.chi2() / static_cast(ndf_mft); + if (chi2mft < 0.f || glMuonCutGroup.maxChi2MFT < chi2mft) { return false; } - o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mZShift); - // float ptMatchedMCHMID = propmuonAtPV_Matched.getPt(); + o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = o2::aod::fwdtrackutils::propagateMuon(mchtrack, mchtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, 0.f, 0.f, 0.f); float etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); float phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMID); + phiMatchedMCHMID = RecoDecay::constrainAngle(phiMatchedMCHMID, 0, 1U); - o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtPV = mfttrack.y() > 0.f ? o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); pt = propmuonAtPV.getPt(); eta = propmuonAtPV.getEta(); phi = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phi); + phi = RecoDecay::constrainAngle(phi, 0, 1U); if (glMuonCutGroup.refitGlobalMuon) { pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); } + relPtResolution = std::sqrt(propmuonAtPV.getSigma2InvQPt()) / std::fabs(propmuonAtPV.getInvQPt()); float deta = etaMatchedMCHMID - eta; float dphi = phiMatchedMCHMID - phi; - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); if (std::sqrt(std::pow(deta / glMuonCutGroup.maxDEta, 2) + std::pow(dphi / glMuonCutGroup.maxDPhi, 2)) > 1.f) { return false; } - float dr = std::sqrt(deta * deta + dphi * dphi); - - float dcaX = propmuonAtPV.getX() - collision.posX(); - float dcaY = propmuonAtPV.getY() - collision.posY(); - float dcaZ = propmuonAtPV.getZ() - collision.posZ(); + o2::dataformats::GlobalFwdTrack propmuonAtDCA = mfttrack.y() > 0.f ? o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToDCA, glMuonCutGroup.matchingZ, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToDCA, glMuonCutGroup.matchingZ, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); + float dcaX = propmuonAtDCA.getX() - collision.posX(); + float dcaY = propmuonAtDCA.getY() - collision.posY(); float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - float cXX = propmuonAtPV.getSigma2X(); - float cYY = propmuonAtPV.getSigma2Y(); - float cXY = propmuonAtPV.getSigmaXY(); + float cXXatDCA = propmuonAtDCA.getSigma2X(); + float cYYatDCA = propmuonAtDCA.getSigma2Y(); + float cXYatDCA = propmuonAtDCA.getSigmaXY(); if (glMuonCutGroup.maxDCAxy < dcaXY) { return false; } + // add convariance matrix of vertex + float tx = std::cos(propmuonAtDCA.getPhi()) / propmuonAtDCA.getTgl(); // dx/dz + float ty = std::sin(propmuonAtDCA.getPhi()) / propmuonAtDCA.getTgl(); // dy/dz + + // PV covariance projected into the (dx, dy) residual space. + float resPVXX = collision.covXX() - 2.0 * tx * collision.covXZ() + tx * tx * collision.covZZ(); + float resPVXY = collision.covXY() - ty * collision.covXZ() - tx * collision.covYZ() + tx * ty * collision.covZZ(); + float resPVYY = collision.covYY() - 2.0 * ty * collision.covYZ() + ty * ty * collision.covZZ(); + + // Full residual covariance. + float cXX = cXXatDCA + resPVXX; + float cXY = cXYatDCA + resPVXY; + float cYY = cYYatDCA + resPVYY; + float det = cXX * cYY - cXY * cXY; // determinanat float dcaXYinSigma = 999.f; if (det < 0) { @@ -472,9 +498,9 @@ struct globalDimuonFilter { } else { dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYY + dcaY * dcaY * cXX - 2.f * dcaX * dcaY * cXY) / det / 2.f)); // dca xy in sigma } - float sigma_dcaXY = dcaXY / dcaXYinSigma; + float sigma_dcaXY = dcaXY / dcaXYinSigma / std::sqrt(2.f); - o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToDCA, glMuonCutGroup.matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = o2::aod::fwdtrackutils::propagateMuon(mchtrack, mchtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToDCA, glMuonCutGroup.matchingZ, mBz, 0.f, 0.f, 0.f); float dcaX_Matched = propmuonAtDCA_Matched.getX() - collision.posX(); float dcaY_Matched = propmuonAtDCA_Matched.getY() - collision.posY(); float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); @@ -488,37 +514,75 @@ struct globalDimuonFilter { return false; } + float chi2IP = mfttrack.y() > 0.f ? o2::aod::fwdtrackutils::getFwdChi2IP(fwdtrack, collision, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : o2::aod::fwdtrackutils::getFwdChi2IP(fwdtrack, collision, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); + if constexpr (fillHistograms) { - fRegistry.fill(HIST("MFTMCHMID/hPt"), pt); - fRegistry.fill(HIST("MFTMCHMID/hEtaPhi"), phi, eta); - fRegistry.fill(HIST("MFTMCHMID/hEtaPhi_MatchedMCHMID"), phiMatchedMCHMID, etaMatchedMCHMID); - fRegistry.fill(HIST("MFTMCHMID/hDEtaDPhi"), dphi, deta); - fRegistry.fill(HIST("MFTMCHMID/hSign"), fwdtrack.sign()); - fRegistry.fill(HIST("MFTMCHMID/hNclusters"), fwdtrack.nClusters()); - fRegistry.fill(HIST("MFTMCHMID/hNclustersMFT"), nClustersMFT); - fRegistry.fill(HIST("MFTMCHMID/hPDCA_Rabs"), rAtAbsorberEnd, pDCA); - fRegistry.fill(HIST("MFTMCHMID/hRatAbsorberEnd"), rAtAbsorberEnd); - fRegistry.fill(HIST("MFTMCHMID/hChi2"), chi2); - fRegistry.fill(HIST("MFTMCHMID/hChi2MFT"), chi2mft); - fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMID"), fwdtrack.chi2MatchMCHMID()); - fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMFT"), fwdtrack.chi2MatchMCHMFT()); - fRegistry.fill(HIST("MFTMCHMID/hDCAxy2D"), dcaX, dcaY); - fRegistry.fill(HIST("MFTMCHMID/hDCAxy2DinSigma"), dcaX / std::sqrt(cXX), dcaY / std::sqrt(cYY)); - fRegistry.fill(HIST("MFTMCHMID/hDCAxy"), dcaXY); - fRegistry.fill(HIST("MFTMCHMID/hDCAxyz"), dcaXY, dcaZ); - fRegistry.fill(HIST("MFTMCHMID/hDCAxyinSigma"), dcaXYinSigma); - fRegistry.fill(HIST("MFTMCHMID/hDCAxResolutionvsPt"), pt, std::sqrt(cXX) * 1e+4); // convert cm to um - fRegistry.fill(HIST("MFTMCHMID/hDCAyResolutionvsPt"), pt, std::sqrt(cYY) * 1e+4); // convert cm to um - fRegistry.fill(HIST("MFTMCHMID/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um - fRegistry.fill(HIST("MFTMCHMID/hDCAx_PosZ"), collision.posZ(), dcaX); - fRegistry.fill(HIST("MFTMCHMID/hDCAy_PosZ"), collision.posZ(), dcaY); - fRegistry.fill(HIST("MFTMCHMID/hDCAx_Phi"), phi, dcaX); - fRegistry.fill(HIST("MFTMCHMID/hDCAy_Phi"), phi, dcaY); - fRegistry.fill(HIST("MFTMCHMID/hdR_Chi2MatchMCHMFT"), fwdtrack.chi2MatchMCHMFT(), dr); - fRegistry.fill(HIST("MFTMCHMID/hdR_Chi2"), chi2, dr); - fRegistry.fill(HIST("MFTMCHMID/hChi2_Chi2MatchMCHMFT"), fwdtrack.chi2MatchMCHMFT(), chi2); + if (fwdtrack.sign() > 0) { + fRegistry.fill(HIST("MFTMCHMID/positive/hPt"), pt); + fRegistry.fill(HIST("MFTMCHMID/positive/hPtResolution"), pt, relPtResolution); + fRegistry.fill(HIST("MFTMCHMID/positive/hEtaPhi"), phi, eta); + fRegistry.fill(HIST("MFTMCHMID/positive/hEtaPhi_MatchedMCHMID"), phiMatchedMCHMID, etaMatchedMCHMID); + fRegistry.fill(HIST("MFTMCHMID/positive/hDEtaDPhi"), dphi, deta); + fRegistry.fill(HIST("MFTMCHMID/positive/hSign"), fwdtrack.sign()); + fRegistry.fill(HIST("MFTMCHMID/positive/hNclusters"), fwdtrack.nClusters()); + fRegistry.fill(HIST("MFTMCHMID/positive/hNclustersMFT"), nClustersMFT); + fRegistry.fill(HIST("MFTMCHMID/positive/hPDCA_Rabs"), rAtAbsorberEnd, pDCA); + fRegistry.fill(HIST("MFTMCHMID/positive/hRatAbsorberEnd"), rAtAbsorberEnd); + fRegistry.fill(HIST("MFTMCHMID/positive/hChi2_Pt"), pt, chi2); + fRegistry.fill(HIST("MFTMCHMID/positive/hChi2MFT_Pt"), pt, chi2mft); + fRegistry.fill(HIST("MFTMCHMID/positive/hChi2MatchMCHMID_Pt"), pt, fwdtrack.chi2MatchMCHMID()); + fRegistry.fill(HIST("MFTMCHMID/positive/hChi2MatchMCHMFT_Pt"), pt, fwdtrack.chi2MatchMCHMFT()); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAxy2D"), dcaX, dcaY); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAxy2DinSigma"), dcaX / std::sqrt(cXX), dcaY / std::sqrt(cYY)); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAxy"), dcaXY); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAxyinSigma"), dcaXYinSigma); + fRegistry.fill(HIST("MFTMCHMID/positive/hLog10Chi2IP"), std::log10(chi2IP)); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAxResolutionvsPt"), pt, std::sqrt(cXX) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAyResolutionvsPt"), pt, std::sqrt(cYY) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAx_Phi"), std::atan2(yMFT, xMFT), dcaX); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAy_Phi"), std::atan2(yMFT, xMFT), dcaY); + if (std::atan2(yMFT, xMFT) > 0.f) { + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAx_PosZ_MFTtop"), collision.posZ(), dcaX); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAy_PosZ_MFTtop"), collision.posZ(), dcaY); + } else { + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAx_PosZ_MFTbottom"), collision.posZ(), dcaX); + fRegistry.fill(HIST("MFTMCHMID/positive/hDCAy_PosZ_MFTbottom"), collision.posZ(), dcaY); + } + } else { + fRegistry.fill(HIST("MFTMCHMID/negative/hPt"), pt); + fRegistry.fill(HIST("MFTMCHMID/negative/hPtResolution"), pt, relPtResolution); + fRegistry.fill(HIST("MFTMCHMID/negative/hEtaPhi"), phi, eta); + fRegistry.fill(HIST("MFTMCHMID/negative/hEtaPhi_MatchedMCHMID"), phiMatchedMCHMID, etaMatchedMCHMID); + fRegistry.fill(HIST("MFTMCHMID/negative/hDEtaDPhi"), dphi, deta); + fRegistry.fill(HIST("MFTMCHMID/negative/hSign"), fwdtrack.sign()); + fRegistry.fill(HIST("MFTMCHMID/negative/hNclusters"), fwdtrack.nClusters()); + fRegistry.fill(HIST("MFTMCHMID/negative/hNclustersMFT"), nClustersMFT); + fRegistry.fill(HIST("MFTMCHMID/negative/hPDCA_Rabs"), rAtAbsorberEnd, pDCA); + fRegistry.fill(HIST("MFTMCHMID/negative/hRatAbsorberEnd"), rAtAbsorberEnd); + fRegistry.fill(HIST("MFTMCHMID/negative/hChi2_Pt"), pt, chi2); + fRegistry.fill(HIST("MFTMCHMID/negative/hChi2MFT_Pt"), pt, chi2mft); + fRegistry.fill(HIST("MFTMCHMID/negative/hChi2MatchMCHMID_Pt"), pt, fwdtrack.chi2MatchMCHMID()); + fRegistry.fill(HIST("MFTMCHMID/negative/hChi2MatchMCHMFT_Pt"), pt, fwdtrack.chi2MatchMCHMFT()); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAxy2D"), dcaX, dcaY); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAxy2DinSigma"), dcaX / std::sqrt(cXX), dcaY / std::sqrt(cYY)); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAxy"), dcaXY); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAxyinSigma"), dcaXYinSigma); + fRegistry.fill(HIST("MFTMCHMID/negative/hLog10Chi2IP"), std::log10(chi2IP)); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAxResolutionvsPt"), pt, std::sqrt(cXX) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAyResolutionvsPt"), pt, std::sqrt(cYY) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAx_Phi"), std::atan2(yMFT, xMFT), dcaX); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAy_Phi"), std::atan2(yMFT, xMFT), dcaY); + if (std::atan2(yMFT, xMFT) > 0.f) { + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAx_PosZ_MFTtop"), collision.posZ(), dcaX); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAy_PosZ_MFTtop"), collision.posZ(), dcaY); + } else { + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAx_PosZ_MFTbottom"), collision.posZ(), dcaX); + fRegistry.fill(HIST("MFTMCHMID/negative/hDCAy_PosZ_MFTbottom"), collision.posZ(), dcaY); + } + } } - return true; } @@ -556,34 +620,34 @@ struct globalDimuonFilter { int nClustersMFT = mfttrack.nClusters(); int ndf_mchmft = 2.f * (mchtrack.nClusters() + nClustersMFT) - 5.f; - float chi2 = fwdtrack.chi2() / ndf_mchmft; + float chi2 = fwdtrack.chi2() / static_cast(ndf_mchmft); if (tagMuonCutGroup.maxChi2 < chi2) { return false; } int ndf_mft = 2.f * nClustersMFT - 5.f; - float chi2mft = mfttrack.chi2() / ndf_mft; + float chi2mft = mfttrack.chi2() / static_cast(ndf_mft); if (tagMuonCutGroup.maxChi2MFT < chi2mft) { return false; } - o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToVertex, tagMuonCutGroup.matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = o2::aod::fwdtrackutils::propagateMuon(mchtrack, mchtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, tagMuonCutGroup.matchingZ, mBz, 0.f, 0.f, 0.f); float etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); float phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMID); + phiMatchedMCHMID = RecoDecay::constrainAngle(phiMatchedMCHMID, 0, 1U); - o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, tagMuonCutGroup.matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtPV = mfttrack.y() > 0.f ? o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, tagMuonCutGroup.matchingZ, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, tagMuonCutGroup.matchingZ, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); pt = propmuonAtPV.getPt(); eta = propmuonAtPV.getEta(); phi = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phi); + phi = RecoDecay::constrainAngle(phi, 0, 1U); if (tagMuonCutGroup.refitGlobalMuon) { pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); } float deta = etaMatchedMCHMID - eta; float dphi = phiMatchedMCHMID - phi; - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); if (std::sqrt(std::pow(deta / tagMuonCutGroup.maxDEta, 2) + std::pow(dphi / tagMuonCutGroup.maxDPhi, 2)) > 1.f) { return false; @@ -595,24 +659,6 @@ struct globalDimuonFilter { if (tagMuonCutGroup.maxDCAxy < dcaXY) { return false; } - // float cXX = propmuonAtPV.getSigma2X(); - // float cYY = propmuonAtPV.getSigma2Y(); - // float cXY = propmuonAtPV.getSigmaXY(); - - // float det = cXX * cYY - cXY * cXY; // determinanat - // float dcaXYinSigma = 999.f; - // if (det < 0) { - // dcaXYinSigma = 999.f; - // } else { - // dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYY + dcaY * dcaY * cXX - 2.f * dcaX * dcaY * cXY) / det / 2.f)); // dca xy in sigma - // } - // float sigma_dcaXY = dcaXY / dcaXYinSigma; - - // o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToDCA, tagMuonCutGroup.matchingZ, mBz); - // float dcaX_Matched = propmuonAtDCA_Matched.getX() - collision.posX(); - // float dcaY_Matched = propmuonAtDCA_Matched.getY() - collision.posY(); - // float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); - // float pDCA = mchtrack.p() * dcaXY_Matched; if (pt < tagMuonCutGroup.minPt || tagMuonCutGroup.maxPt < pt) { return false; @@ -662,34 +708,34 @@ struct globalDimuonFilter { nclsMCH = mchtrack.nClusters(); nclsMFT = mfttrack.nClusters(); int ndf_mchmft = 2.f * (nclsMCH + nclsMFT) - 5.f; - chi2 = fwdtrack.chi2() / ndf_mchmft; + chi2 = fwdtrack.chi2() / static_cast(ndf_mchmft); if (probeMuonCutGroup.maxChi2 < chi2) { return false; } int ndf_mft = 2.f * nclsMFT - 5.f; - chi2mft = mfttrack.chi2() / ndf_mft; + chi2mft = mfttrack.chi2() / static_cast(ndf_mft); if (probeMuonCutGroup.maxChi2MFT < chi2mft) { return false; } - o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToVertex, probeMuonCutGroup.matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = o2::aod::fwdtrackutils::propagateMuon(mchtrack, mchtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, probeMuonCutGroup.matchingZ, mBz, 0.f, 0.f, 0.f); float etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); float phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMID); + phiMatchedMCHMID = RecoDecay::constrainAngle(phiMatchedMCHMID, 0, 1U); - o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, probeMuonCutGroup.matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtPV = mfttrack.y() > 0.f ? o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, probeMuonCutGroup.matchingZ, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : o2::aod::fwdtrackutils::propagateMuon(fwdtrack, fwdtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, probeMuonCutGroup.matchingZ, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); pt = propmuonAtPV.getPt(); eta = propmuonAtPV.getEta(); phi = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phi); + phi = RecoDecay::constrainAngle(phi, 0, 1U); if (probeMuonCutGroup.refitGlobalMuon) { pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); } deta = etaMatchedMCHMID - eta; dphi = phiMatchedMCHMID - phi; - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); if (std::sqrt(std::pow(deta / probeMuonCutGroup.maxDEta, 2) + std::pow(dphi / probeMuonCutGroup.maxDPhi, 2)) > 1.f) { return false; @@ -701,20 +747,8 @@ struct globalDimuonFilter { if (probeMuonCutGroup.maxDCAxy < dcaXY) { return false; } - // float cXX = propmuonAtPV.getSigma2X(); - // float cYY = propmuonAtPV.getSigma2Y(); - // float cXY = propmuonAtPV.getSigmaXY(); - - // float det = cXX * cYY - cXY * cXY; // determinanat - // float dcaXYinSigma = 999.f; - // if (det < 0) { - // dcaXYinSigma = 999.f; - // } else { - // dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYY + dcaY * dcaY * cXX - 2.f * dcaX * dcaY * cXY) / det / 2.f)); // dca xy in sigma - // } - // float sigma_dcaXY = dcaXY / dcaXYinSigma; - o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToDCA, probeMuonCutGroup.matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = o2::aod::fwdtrackutils::propagateMuon(mchtrack, mchtrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToDCA, probeMuonCutGroup.matchingZ, mBz, 0.f, 0.f, 0.f); float dcaX_Matched = propmuonAtDCA_Matched.getX() - collision.posX(); float dcaY_Matched = propmuonAtDCA_Matched.getY() - collision.posY(); float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); @@ -872,7 +906,7 @@ struct globalDimuonFilter { } // end end of negative muon loop } // end end of positive muon loop - for (const auto pos1 : posMuons) { + for (const auto& pos1 : posMuons) { auto fwdtrack1 = fwdtracks.rawIteratorAt(pos1); if (!isBestMatch(collision, fwdtrack1, fwdtracks, mfttracks)) { continue; @@ -883,7 +917,7 @@ struct globalDimuonFilter { } ROOT::Math::PtEtaPhiMVector v1(pt1, eta1, phi1, o2::constants::physics::MassMuon); - for (const auto pos2 : posMuons) { + for (const auto& pos2 : posMuons) { auto fwdtrack2 = fwdtracks.rawIteratorAt(pos2); if (pos1 == pos2) { continue; @@ -905,7 +939,7 @@ struct globalDimuonFilter { } // end end of positive muon loop } // end end of positive muon loop - for (const auto neg1 : negMuons) { + for (const auto& neg1 : negMuons) { auto fwdtrack1 = fwdtracks.rawIteratorAt(neg1); if (!isBestMatch(collision, fwdtrack1, fwdtracks, mfttracks)) { continue; @@ -916,7 +950,7 @@ struct globalDimuonFilter { } ROOT::Math::PtEtaPhiMVector v1(pt1, eta1, phi1, o2::constants::physics::MassMuon); - for (const auto neg2 : negMuons) { + for (const auto& neg2 : negMuons) { auto fwdtrack2 = fwdtracks.rawIteratorAt(neg2); if (neg1 == neg2) { continue; @@ -954,32 +988,51 @@ struct globalDimuonFilter { if (eventCutGroup.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { return false; } + if (eventCutGroup.cfgRequireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (eventCutGroup.cfgRequireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (eventCutGroup.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (eventCutGroup.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (collision.chi2() / collision.numContrib() > eventCutGroup.cfgChi2PerNcontribMax) { + return false; + } + if (collision.numContrib() < eventCutGroup.cfgNcontribMin) { + return false; + } return true; } - using MyCollisions = soa::Join; + using MyCollisions = o2::soa::Join; using MyCollision = MyCollisions::iterator; - using MyBCs = soa::Join; + using MyBCs = o2::soa::Join; using MyBC = MyBCs::iterator; - using MyFwdTracks = soa::Join; // muon tracks are repeated. i.e. not exclusive. + using MyFwdTracks = o2::soa::Join; // muon tracks are repeated. i.e. not exclusive. using MyFwdTrack = MyFwdTracks::iterator; - SliceCache cache; - Preslice perCollision = o2::aod::fwdtrack::collisionId; - Preslice fwdtrackIndicesPerCollision = aod::track_association::collisionId; - PresliceUnsorted fwdtrackIndicesPerFwdTrack = aod::track_association::fwdtrackId; - PresliceUnsorted fwdtracksPerMCHTrack = aod::fwdtrack::matchMCHTrackId; + o2::framework::SliceCache cache; + o2::framework::Preslice perCollision = o2::aod::fwdtrack::collisionId; + o2::framework::Preslice fwdtrackIndicesPerCollision = o2::aod::track_association::collisionId; + o2::framework::PresliceUnsorted fwdtrackIndicesPerFwdTrack = o2::aod::track_association::fwdtrackId; + o2::framework::PresliceUnsorted fwdtracksPerMCHTrack = o2::aod::fwdtrack::matchMCHTrackId; + o2::framework::PresliceUnsorted perMFTTrack = o2::aod::fwdtrack::matchMFTTrackId; - std::unordered_map map_mfttrackcovs; + // std::unordered_map map_mfttrackcovs; std::unordered_map mapSelectedCollisions; - void processSA(MyCollisions const& collisions, MyBCs const&, MyFwdTracks const& fwdtracks, aod::MFTTracks const& mfttracks, aod::MFTTracksCov const& mftCovs) + void processSA(MyCollisions const& collisions, MyBCs const&, MyFwdTracks const& fwdtracks, o2::aod::MFTTracks const& mfttracks, o2::aod::MFTTracksCov const& mftCovs) { - for (const auto& mfttrackConv : mftCovs) { - map_mfttrackcovs[mfttrackConv.matchMFTTrackId()] = mfttrackConv.globalIndex(); - } + // for (const auto& mfttrackConv : mftCovs) { + // map_mfttrackcovs[mfttrackConv.matchMFTTrackId()] = mfttrackConv.globalIndex(); + // } vec_min_chi2MatchMCHMFT.reserve(fwdtracks.size()); for (const auto& collision : collisions) { @@ -1014,16 +1067,16 @@ struct globalDimuonFilter { if (isSelectedCollision(collision)) { fRegistry.fill(HIST("hCollisionCounter"), 7); + fRegistry.fill(HIST("Vertex/MB/hZvtx"), collision.posZ()); + fRegistry.fill(HIST("Vertex/MB/hChi2vsNContrib"), collision.numContrib(), collision.chi2() / collision.numContrib()); + fRegistry.fill(HIST("Vertex/MB/hSigmaX"), collision.numContrib(), std::sqrt(collision.covXX()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/MB/hSigmaY"), collision.numContrib(), std::sqrt(collision.covYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/MB/hSigmaZ"), collision.numContrib(), std::sqrt(collision.covZZ()) * 1e+4); // convert cm to um int nGlobalMuon = 0; // LOGF(info, "collision.globalIndex() = %d, fwdtrackIdsThisCollision = %d", collision.globalIndex(), fwdtrackIdsThisCollision.size()); auto fwdtracks_per_coll = fwdtracks.sliceBy(perCollision, collision.globalIndex()); for (const auto& fwdtrack : fwdtracks_per_coll) { - // float pt = 999.f, eta = 999.f, phi = 999.f; - // if (isSelectedGlobalMuon(collision, fwdtrack, fwdtracks, mfttracks, pt, eta, phi)) { - // nGlobalMuon++; - // } - int nGoodGLPerSA = countMatchedCandidatesPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks); if (nGoodGLPerSA > 0) { nGlobalMuon++; @@ -1035,6 +1088,12 @@ struct globalDimuonFilter { if (nGlobalMuon >= minNmuon) { fRegistry.fill(HIST("hCollisionCounter"), 8); + fRegistry.fill(HIST("Vertex/Triggered/hZvtx"), collision.posZ()); + fRegistry.fill(HIST("Vertex/Triggered/hChi2vsNContrib"), collision.numContrib(), collision.chi2() / collision.numContrib()); + fRegistry.fill(HIST("Vertex/Triggered/hSigmaX"), collision.numContrib(), std::sqrt(collision.covXX()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/Triggered/hSigmaY"), collision.numContrib(), std::sqrt(collision.covYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/Triggered/hSigmaZ"), collision.numContrib(), std::sqrt(collision.covZZ()) * 1e+4); // convert cm to um + mapSelectedCollisions[collision.globalIndex()] = true; tags(true); } else { @@ -1117,18 +1176,18 @@ struct globalDimuonFilter { negProbeMuons.shrink_to_fit(); } // end of collision loop - map_mfttrackcovs.clear(); + // map_mfttrackcovs.clear(); mapSelectedCollisions.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); } PROCESS_SWITCH(globalDimuonFilter, processSA, "process without FTTCA", false); - void processFTTCA(MyCollisions const& collisions, MyBCs const&, MyFwdTracks const& fwdtracks, aod::MFTTracks const& mfttracks, aod::MFTTracksCov const& mftCovs, aod::FwdTrackAssoc const& fwdtrackIndices) + void processFTTCA(MyCollisions const& collisions, MyBCs const&, MyFwdTracks const& fwdtracks, o2::aod::MFTTracks const& mfttracks, o2::aod::MFTTracksCov const& mftCovs, o2::aod::FwdTrackAssoc const& fwdtrackIndices) { - for (const auto& mfttrackConv : mftCovs) { - map_mfttrackcovs[mfttrackConv.matchMFTTrackId()] = mfttrackConv.globalIndex(); - } + // for (const auto& mfttrackConv : mftCovs) { + // map_mfttrackcovs[mfttrackConv.matchMFTTrackId()] = mfttrackConv.globalIndex(); + // } vec_min_chi2MatchMCHMFT.reserve(fwdtracks.size()); for (const auto& collision : collisions) { @@ -1165,21 +1224,17 @@ struct globalDimuonFilter { if (isSelectedCollision(collision)) { fRegistry.fill(HIST("hCollisionCounter"), 7); + fRegistry.fill(HIST("Vertex/MB/hZvtx"), collision.posZ()); + fRegistry.fill(HIST("Vertex/MB/hChi2vsNContrib"), collision.numContrib(), collision.chi2() / collision.numContrib()); + fRegistry.fill(HIST("Vertex/MB/hSigmaX"), collision.numContrib(), std::sqrt(collision.covXX()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/MB/hSigmaY"), collision.numContrib(), std::sqrt(collision.covYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/MB/hSigmaZ"), collision.numContrib(), std::sqrt(collision.covZZ()) * 1e+4); // convert cm to um int nGlobalMuon = 0; auto fwdtrackIdsThisCollision = fwdtrackIndices.sliceBy(fwdtrackIndicesPerCollision, collision.globalIndex()); // LOGF(info, "collision.globalIndex() = %d, fwdtrackIdsThisCollision = %d", collision.globalIndex(), fwdtrackIdsThisCollision.size()); for (const auto& fwdtrackId : fwdtrackIdsThisCollision) { auto fwdtrack = fwdtrackId.template fwdtrack_as(); - - // if (std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // continue; - // } - // float pt = 999.f, eta = 999.f, phi = 999.f; - // if (isSelectedGlobalMuon(collision, fwdtrack, fwdtracks, mfttracks, pt, eta, phi)) { - // nGlobalMuon++; - // } - int nGoodGLPerSA = countMatchedCandidatesPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks); if (nGoodGLPerSA > 0) { nGlobalMuon++; @@ -1191,6 +1246,12 @@ struct globalDimuonFilter { if (nGlobalMuon >= minNmuon) { fRegistry.fill(HIST("hCollisionCounter"), 8); + fRegistry.fill(HIST("Vertex/Triggered/hZvtx"), collision.posZ()); + fRegistry.fill(HIST("Vertex/Triggered/hChi2vsNContrib"), collision.numContrib(), collision.chi2() / collision.numContrib()); + fRegistry.fill(HIST("Vertex/Triggered/hSigmaX"), collision.numContrib(), std::sqrt(collision.covXX()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/Triggered/hSigmaY"), collision.numContrib(), std::sqrt(collision.covYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/Triggered/hSigmaZ"), collision.numContrib(), std::sqrt(collision.covZZ()) * 1e+4); // convert cm to um + mapSelectedCollisions[collision.globalIndex()] = true; tags(true); } else { @@ -1279,7 +1340,7 @@ struct globalDimuonFilter { negProbeMuons.shrink_to_fit(); } // end of collision loop - map_mfttrackcovs.clear(); + // map_mfttrackcovs.clear(); mapSelectedCollisions.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); @@ -1287,8 +1348,8 @@ struct globalDimuonFilter { PROCESS_SWITCH(globalDimuonFilter, processFTTCA, "process with FTTCA", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfg) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfg) { - return WorkflowSpec{ - adaptAnalysisTask(cfg, TaskName{"em-global-dimuon-filter"})}; + return o2::framework::WorkflowSpec{ + adaptAnalysisTask(cfg, o2::framework::TaskName{"em-global-dimuon-filter"})}; } diff --git a/EventFiltering/PWGHF/HFFilter.cxx b/EventFiltering/PWGHF/HFFilter.cxx index aedd6d3c7ce..158a358d4ef 100644 --- a/EventFiltering/PWGHF/HFFilter.cxx +++ b/EventFiltering/PWGHF/HFFilter.cxx @@ -250,6 +250,7 @@ struct HfFilter { // Main struct for HF triggers std::array, kNCharmParticles> hBDTScoreNonPrompt{}; std::array, kNV0> hArmPod{}; std::shared_ptr hV0Selected; + std::array, 2> hMassVsCtCharmBaryonToXi{}; std::array, 2> hMassXi{}; // not tracked and tracked std::array, kNBeautyParticles> hCpaVsPtB{}; std::array, kNBeautyParticles> hDecayLengthVsPtB{}; @@ -390,6 +391,8 @@ struct HfFilter { // Main struct for HF triggers hMassVsPtC[kNCharmParticles + 15] = registry.add("fMassVsPtCharmBaryonToXiPi", "#it{M} vs. #it{p}_{T} distribution of triggered #Xi+#pi candidates;#it{p}_{T} (GeV/#it{c});#it{M} (GeV/#it{c}^{2});counts", HistType::kTH2D, {ptAxis, massAxisC[kNCharmParticles + 15]}); hMassVsPtC[kNCharmParticles + 16] = registry.add("fMassVsPtCharmBaryonToXiKa", "#it{M} vs. #it{p}_{T} distribution of triggered #Xi+K candidates;#it{p}_{T} (GeV/#it{c});#it{M} (GeV/#it{c}^{2});counts", HistType::kTH2D, {ptAxis, massAxisC[kNCharmParticles + 16]}); hMassVsPtC[kNCharmParticles + 17] = registry.add("fMassVsPtCharmBaryonToXiPiPi", "#it{M} vs. #it{p}_{T} distribution of triggered #Xi+#pi+#pi candidates;#it{p}_{T} (GeV/#it{c});#it{M} (GeV/#it{c}^{2});counts", HistType::kTH2D, {ptAxis, massAxisC[kNCharmParticles + 17]}); + hMassVsCtCharmBaryonToXi[0] = registry.add("fMassVsCtCharmBaryonToXiPi", "#it{M} vs. ct distribution of triggered #Xi+#pi candidates;ct (cm);#it{M} (GeV/#it{c}^{2});counts", HistType::kTH2D, {ctAxis, massAxisC[kNCharmParticles + 15]}); + hMassVsCtCharmBaryonToXi[1] = registry.add("fMassVsCtCharmBaryonToXiPiPi", "#it{M} vs. ct distribution of triggered #Xi+#pi+#pi candidates;ct (cm);#it{M} (GeV/#it{c}^{2});counts", HistType::kTH2D, {ctAxis, massAxisC[kNCharmParticles + 17]}); // JPsi hMassVsPtC[kNCharmParticles + 18] = registry.add("fMassVsPtJPsiToMuMu", "#it{M} vs. #it{p}_{T} distribution of triggered J/#psi to #mu#mu candidates;#it{p}_{T} (GeV/#it{c});#it{M} (GeV/#it{c}^{2});counts", HistType::kTH2D, {ptAxis, massAxisC[kNCharmParticles + 18]}); // Lc resonances @@ -492,6 +495,7 @@ struct HfFilter { // Main struct for HF triggers bool isSelectedPvZ = (std::fabs(collision.posZ()) < evSel.maxPvZ); if (!isSelectedTVX || !isSelectedTFBorder || !isSelectedITSROFBorder || !isSelectedPvZ) { tags(keepEvent[kHighPt2P], keepEvent[kHighPt3P], keepEvent[kBeauty3P], keepEvent[kBeauty4P], keepEvent[kFemto2P], keepEvent[kFemto3P], keepEvent[kDoubleCharm2P], keepEvent[kDoubleCharm3P], keepEvent[kDoubleCharmMix], keepEvent[kV0Charm2P], keepEvent[kV0Charm3P], keepEvent[kCharmBarToXiBach], keepEvent[kSigmaCPPK], keepEvent[kSigmaC0K0], keepEvent[kPhotonCharm2P], keepEvent[kPhotonCharm3P], keepEvent[kSingleCharm2P], keepEvent[kSingleCharm3P], keepEvent[kSingleNonPromptCharm2P], keepEvent[kSingleNonPromptCharm3P], keepEvent[kCharmBarToXi2Bach], keepEvent[kPrCharm2P], keepEvent[kBtoJPsiKa], keepEvent[kBtoJPsiKstar], keepEvent[kBtoJPsiPhi], keepEvent[kBtoJPsiPrKa], keepEvent[kBtoJPsiPi], keepEvent[kSigmaCPr]); + hProcessedEvents->Fill(1); // rejected continue; } @@ -1947,10 +1951,10 @@ struct HfFilter { // Main struct for HF triggers bool isSelXiBach{false}; if (requireStrangenessTracking->get(0u, 0u) > 0) { if (hasStrangeTrack) { - isSelXiBach = helper.isSelectedXiBach(trackParCascTrack, trackParBachelor, isSelBachelor, collision, dfStrangeness, activateQA, hMassVsPtC[kNCharmParticles + 15], hMassVsPtC[kNCharmParticles + 16]); + isSelXiBach = helper.isSelectedXiBach(trackParCascTrack, trackParBachelor, isSelBachelor, collision, dfStrangeness, activateQA, hMassVsPtC[kNCharmParticles + 15], hMassVsPtC[kNCharmParticles + 16], hMassVsCtCharmBaryonToXi[0]); } } else { - isSelXiBach = helper.isSelectedXiBach(trackParCasc, trackParBachelor, isSelBachelor, collision, dfStrangeness, activateQA, hMassVsPtC[kNCharmParticles + 15], hMassVsPtC[kNCharmParticles + 16]); + isSelXiBach = helper.isSelectedXiBach(trackParCasc, trackParBachelor, isSelBachelor, collision, dfStrangeness, activateQA, hMassVsPtC[kNCharmParticles + 15], hMassVsPtC[kNCharmParticles + 16], hMassVsCtCharmBaryonToXi[0]); } if (isSelXiBach) { keepEvent[kCharmBarToXiBach] = true; @@ -1990,10 +1994,10 @@ struct HfFilter { // Main struct for HF triggers bool isSelXiBachBach{false}; if (requireStrangenessTracking->get(0u, 1u) > 0) { if (hasStrangeTrack) { - isSelXiBachBach = helper.isSelectedXiBachBach<3>(trackParCascTrack, {trackParBachelor, trackParBachelorSecond}, collision, dfStrangeness3, activateQA, hMassVsPtC[kNCharmParticles + 17]); + isSelXiBachBach = helper.isSelectedXiBachBach<3>(trackParCascTrack, {trackParBachelor, trackParBachelorSecond}, collision, dfStrangeness3, activateQA, hMassVsPtC[kNCharmParticles + 17], hMassVsCtCharmBaryonToXi[1]); } } else { // vertex with only the two bachelors - isSelXiBachBach = helper.isSelectedXiBachBach<2>(trackParCasc, {trackParBachelor, trackParBachelorSecond}, collision, df2, activateQA, hMassVsPtC[kNCharmParticles + 17]); + isSelXiBachBach = helper.isSelectedXiBachBach<2>(trackParCasc, {trackParBachelor, trackParBachelorSecond}, collision, df2, activateQA, hMassVsPtC[kNCharmParticles + 17], hMassVsCtCharmBaryonToXi[1]); } if (isSelXiBachBach) { keepEvent[kCharmBarToXi2Bach] = true; diff --git a/EventFiltering/PWGHF/HFFilterHelpers.h b/EventFiltering/PWGHF/HFFilterHelpers.h index d215fb0df8d..288cdf8fac5 100644 --- a/EventFiltering/PWGHF/HFFilterHelpers.h +++ b/EventFiltering/PWGHF/HFFilterHelpers.h @@ -274,6 +274,7 @@ constexpr float massDPlus = o2::constants::physics::MassDPlus; constexpr float massDs = o2::constants::physics::MassDS; constexpr float massLc = o2::constants::physics::MassLambdaCPlus; constexpr float massXic = o2::constants::physics::MassXiCPlus; +constexpr float massXic0 = o2::constants::physics::MassXiC0; constexpr float massDStar = o2::constants::physics::MassDStar; constexpr float massBPlus = o2::constants::physics::MassBPlus; constexpr float massB0 = o2::constants::physics::MassB0; @@ -288,6 +289,7 @@ constexpr float massJPsi = o2::constants::physics::MassJPsi; static const o2::framework::AxisSpec ptAxis{50, 0.f, 50.f}; static const o2::framework::AxisSpec pAxis{50, 0.f, 10.f}; +static const o2::framework::AxisSpec ctAxis{300, 0.f, 0.3f}; static const o2::framework::AxisSpec kstarAxis{200, 0.f, 2.f}; static const o2::framework::AxisSpec etaAxis{30, -1.5f, 1.5f}; static const o2::framework::AxisSpec nSigmaAxis{100, -10.f, 10.f}; @@ -716,9 +718,9 @@ class HfFilterHelper template bool isCharmHadronMassInSbRegions(T1 const& massHypo1, T1 const& massHypo2, const float& lowLimitSB, const float& upLimitSB); template - bool isSelectedXiBach(T const& trackParCasc, T const& trackParBachelor, int8_t isSelBachelor, C const& collision, o2::vertexing::DCAFitterN<2>& dcaFitter, const int& activateQA, H2 hMassVsPtXiPi, H2 hMassVsPtXiKa); + bool isSelectedXiBach(T const& trackParCasc, T const& trackParBachelor, int8_t isSelBachelor, C const& collision, o2::vertexing::DCAFitterN<2>& dcaFitter, const int& activateQA, H2 hMassVsPtXiPi, H2 hMassVsPtXiKa, H2 hMassVsCtXiPi); template - bool isSelectedXiBachBach(T const& trackParCasc, std::array const& trackParBachelor, C const& collision, o2::vertexing::DCAFitterN& dcaFitter, const int& activateQA, H2 hMassVsPtXiPiPi); + bool isSelectedXiBachBach(T const& trackParCasc, std::array const& trackParBachelor, C const& collision, o2::vertexing::DCAFitterN& dcaFitter, const int& activateQA, H2 hMassVsPtXiPiPi, H2 hMassVsCtXiPiPi); template bool isSelectedProtonFromLcResoOrThetaC(const T& track); // helpers @@ -2002,7 +2004,7 @@ inline void HfFilterHelper::setTpcRecalibMaps(o2::framework::Service(calibList->FindObject(mapNames[iMap].data())); + mHistMapPiPrKaDe[iMap] = dynamic_cast(calibList->FindObject(mapNames[iMap].data())); if (!mHistMapPiPrKaDe[iMap]) { LOG(fatal) << "Cannot find histogram: " << mapNames[iMap].data(); return; @@ -2422,8 +2424,9 @@ inline bool HfFilterHelper::isCharmHadronMassInSbRegions(T1 const& massHypo1, T1 /// \param activateQA is the flag to activate the QA /// \param hMassVsPtXiPi is the 2D histogram with pT vs mass(XiPi) /// \param hMassVsPtXiKa is the 2D histogram with pT vs mass(XiKa) +/// \param hMassVsCtXiPi is the 2D histogram with ct vs mass(XiPi) template -inline bool HfFilterHelper::isSelectedXiBach(T const& trackParCasc, T const& trackParBachelor, int8_t isSelBachelor, C const& collision, o2::vertexing::DCAFitterN<2>& dcaFitter, const int& activateQA, H2 hMassVsPtXiPi, H2 hMassVsPtXiKa) +inline bool HfFilterHelper::isSelectedXiBach(T const& trackParCasc, T const& trackParBachelor, int8_t isSelBachelor, C const& collision, o2::vertexing::DCAFitterN<2>& dcaFitter, const int& activateQA, H2 hMassVsPtXiPi, H2 hMassVsPtXiKa, H2 hMassVsCtXiPi) { bool isSelectedXiPi{false}, isSelectedXiKa{false}; @@ -2482,13 +2485,16 @@ inline bool HfFilterHelper::isSelectedXiBach(T const& trackParCasc, T const& tra return false; } - if (RecoDecay::distance(primVtx, vtx) < mDecLenMinXiBach[0]) { + auto decLenXiBach = RecoDecay::distance(primVtx, vtx); + if (decLenXiBach < mDecLenMinXiBach[0]) { return false; } if (activateQA) { if (isSelectedXiPi) { + auto ctXiBach = RecoDecay::ct(momXiBach, decLenXiBach, massXic0); hMassVsPtXiPi->Fill(ptXiBach, massXiPi); + hMassVsCtXiPi->Fill(ctXiBach, massXiPi); } if (isSelectedXiKa) { hMassVsPtXiKa->Fill(ptXiBach, massXiKa); @@ -2506,8 +2512,9 @@ inline bool HfFilterHelper::isSelectedXiBach(T const& trackParCasc, T const& tra /// \param dcaFitter is the DCAFitter /// \param activateQA is the flag to activate the QA /// \param hMassVsPtXiPiPi is the 2D histogram with pT vs mass(XiPiPi) +/// \param hMassVsCtXiPiPi is the 2D histogram with ct vs mass(XiPiPi) template -inline bool HfFilterHelper::isSelectedXiBachBach(T const& trackParCasc, std::array const& trackParBachelor, C const& collision, o2::vertexing::DCAFitterN& dcaFitter, const int& activateQA, H2 hMassVsPtXiPiPi) +inline bool HfFilterHelper::isSelectedXiBachBach(T const& trackParCasc, std::array const& trackParBachelor, C const& collision, o2::vertexing::DCAFitterN& dcaFitter, const int& activateQA, H2 hMassVsPtXiPiPi, H2 hMassVsCtXiPiPi) { // compute pT std::array pVecBachelorFirst{}, pVecBachelorSecond{}, pVecCascade{}; @@ -2570,13 +2577,15 @@ inline bool HfFilterHelper::isSelectedXiBachBach(T const& trackParCasc, std::arr if (RecoDecay::cpa(primVtx, std::array{vtx[0], vtx[1], vtx[2]}, momXiBachBach) < mCosPaMinXiBach[1]) { return false; } - - if (RecoDecay::distance(primVtx, vtx) < mDecLenMinXiBach[1]) { + auto decLenXiBachBach = RecoDecay::distance(primVtx, vtx); + if (decLenXiBachBach < mDecLenMinXiBach[1]) { return false; } + auto ctXiBachBach = RecoDecay::ct(momXiBachBach, decLenXiBachBach, massXic); if (activateQA) { hMassVsPtXiPiPi->Fill(ptXiBachBach, massXiPiPi); + hMassVsCtXiPiPi->Fill(ctXiBachBach, massXiPiPi); } } diff --git a/EventFiltering/PWGJE/jetFilter.cxx b/EventFiltering/PWGJE/jetFilter.cxx index d9f9f153333..a74c146e86b 100644 --- a/EventFiltering/PWGJE/jetFilter.cxx +++ b/EventFiltering/PWGJE/jetFilter.cxx @@ -98,7 +98,7 @@ struct jetFilter { void init(o2::framework::InitContext&) { triggerJetR = TMath::Nint(cfgJetR * 100.0f); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); spectra.add("fCollZpos", "collision z position", HistType::kTH1F, diff --git a/EventFiltering/PWGLF/filterdoublephi.cxx b/EventFiltering/PWGLF/filterdoublephi.cxx index ac4b0fe5327..edc498111a7 100644 --- a/EventFiltering/PWGLF/filterdoublephi.cxx +++ b/EventFiltering/PWGLF/filterdoublephi.cxx @@ -188,7 +188,7 @@ struct filterdoublephi { if (collision.sel8()) { auto posThisColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); auto negThisColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - for (auto track1 : posThisColl) { + for (const auto& track1 : posThisColl) { // track selection if (!selectionTrack(track1)) { continue; @@ -209,7 +209,7 @@ struct filterdoublephi { qaRegistry.fill(HIST("hNsigmaPtkaonTOF"), track1.tofNSigmaKa(), track1.pt()); } auto track1ID = track1.globalIndex(); - for (auto track2 : negThisColl) { + for (const auto& track2 : negThisColl) { // track selection if (!selectionTrack(track2)) { continue; diff --git a/EventFiltering/PWGLF/filterf1proton.cxx b/EventFiltering/PWGLF/filterf1proton.cxx index 9d345cc9194..f690380f535 100644 --- a/EventFiltering/PWGLF/filterf1proton.cxx +++ b/EventFiltering/PWGLF/filterf1proton.cxx @@ -419,8 +419,8 @@ struct filterf1proton { return true; } - float getkstar(const ROOT::Math::PtEtaPhiMVector part1, - const ROOT::Math::PtEtaPhiMVector part2) + float getkstar(const ROOT::Math::PtEtaPhiMVector& part1, + const ROOT::Math::PtEtaPhiMVector& part2) { const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; const float beta = trackSum.Beta(); @@ -439,7 +439,7 @@ struct filterf1proton { return 0.5 * trackRelK.P(); } - std::vector setValuesBB(o2::ccdb::CcdbApi& ccdbApi, aod::BCsWithTimestamps::iterator const& bunchCrossing, const std::string ccdbPath) + std::vector setValuesBB(o2::ccdb::CcdbApi& ccdbApi, aod::BCsWithTimestamps::iterator const& bunchCrossing, const std::string& ccdbPath) { std::map metadata; auto h = ccdbApi.retrieveFromTFileAny(ccdbPath, metadata, bunchCrossing.timestamp()); diff --git a/EventFiltering/PWGLF/nucleiFilter.cxx b/EventFiltering/PWGLF/nucleiFilter.cxx index 0a50f17cd0a..8ca29f18080 100644 --- a/EventFiltering/PWGLF/nucleiFilter.cxx +++ b/EventFiltering/PWGLF/nucleiFilter.cxx @@ -413,7 +413,7 @@ struct nucleiFilter { continue; } const auto& h3vector = h3vectors[iH3]; - auto pivector = trackVector; + const auto& pivector = trackVector; auto cm = h3vector + trackVector; const ROOT::Math::Boost boost(cm.BoostToCM()); boost(pivector); diff --git a/EventFiltering/PWGLF/strangenessFilter.cxx b/EventFiltering/PWGLF/strangenessFilter.cxx index dde0d18f4c8..b17a6353f83 100644 --- a/EventFiltering/PWGLF/strangenessFilter.cxx +++ b/EventFiltering/PWGLF/strangenessFilter.cxx @@ -266,23 +266,23 @@ struct strangenessFilter { { return track.pt() > cfgHMOmegaCuts.hMinPtHM && std::abs(track.eta()) < cfgHMOmegaCuts.hEtaHM && track.tpcNClsCrossedRows() >= tpcmincrossedrows && track.tpcChi2NCl() <= 4.f && track.itsChi2NCl() <= 36.f && (track.itsClusterMap() & 0x7) != 0; } - float getV0V0DCA(TVector3 v01pos, TVector3 v01mom, TVector3 v02pos, TVector3 v02mom) + float getV0V0DCA(const TVector3& v01pos, const TVector3& v01mom, const TVector3& v02pos, const TVector3& v02mom) { TVector3 posdiff = v02pos - v01pos; TVector3 cross = v01mom.Cross(v02mom); TVector3 dcaVec = (posdiff.Dot(cross) / cross.Mag2()) * cross; return dcaVec.Mag(); } - float getV0V0CPA(TVector3 v01mom, TVector3 v02mom) + float getV0V0CPA(const TVector3& v01mom, const TVector3& v02mom) { return v01mom.Dot(v02mom) / (v01mom.Mag() * v02mom.Mag()); } - float getV0V0Distance(TVector3 v01pos, TVector3 v02pos) + float getV0V0Distance(const TVector3& v01pos, const TVector3& v02pos) { TVector3 posdiff = v02pos - v01pos; return posdiff.Mag(); } - float getV0V0Radius(TVector3 v01pos, TVector3 v01mom, TVector3 v02pos, TVector3 v02mom) + float getV0V0Radius(const TVector3& v01pos, TVector3 v01mom, const TVector3& v02pos, TVector3 v02mom) { TVector3 posdiff = v02pos - v01pos; v01mom *= 1. / v01mom.Mag(); @@ -296,7 +296,7 @@ struct strangenessFilter { radVec *= 0.5; return radVec.Mag(); } - bool isSelectedV0V0(TVector3 v01pos, TVector3 v01mom, TVector3 v02pos, TVector3 v02mom) + bool isSelectedV0V0(const TVector3& v01pos, const TVector3& v01mom, const TVector3& v02pos, const TVector3& v02mom) { if (getV0V0DCA(v01pos, v01mom, v02pos, v02mom) > cfgLLCuts.cfgMaxDCAV0V0) return false; @@ -1217,7 +1217,7 @@ struct strangenessFilter { // QA tracks int triggcounterAllEv = 0; - for (auto track : tracks) { // start loop over tracks + for (const auto& track : tracks) { // start loop over tracks if (cfgTrackCuts.isTrackFilter && !selectTrack(track)) { continue; } @@ -1524,7 +1524,7 @@ struct strangenessFilter { QAHistosSigma.fill(HIST("hDecayRadiusSigma"), decRad); // pair a proton bool isProtonPaired = false; - for (auto track : tracks) { + for (const auto& track : tracks) { if (track.globalIndex() == dauTrack.globalIndex()) { continue; } diff --git a/EventFiltering/PWGUD/diffractionBCFilter.cxx b/EventFiltering/PWGUD/diffractionBCFilter.cxx index a2c8f8c2ed9..74d4672f981 100644 --- a/EventFiltering/PWGUD/diffractionBCFilter.cxx +++ b/EventFiltering/PWGUD/diffractionBCFilter.cxx @@ -112,11 +112,11 @@ struct tracksWGTInBCs { LOGF(debug, " Number of BCs %d", atr.bc().size()); if (atr.bc().size() > 0) { auto firstCompatibleBC = atr.bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); // update tracksInBCList LOGF(debug, "closestBC %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } else { // registry.get(HIST("barrelTracks"))->Fill(2., 1.); @@ -128,7 +128,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "closestBC %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } LOGF(debug, "track finished.\n"); @@ -195,12 +195,12 @@ struct tracksWGTInBCs { LOGF(debug, " Number of BCs %d", aftr.bc().size()); if (aftr.bc().size() > 0) { auto firstCompatibleBC = aftr.bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); // update fwdTracksWGTInBCList LOGF(debug, "closestBC %d", closestBC); - fwdTracksWGTInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksWGTInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } else { // registry.get(HIST("forwardTracks"))->Fill(2., 1.); @@ -212,7 +212,7 @@ struct tracksWGTInBCs { // update fwdTracksWGTInBCList LOGF(debug, "closestBC %d", closestBC); - fwdTracksWGTInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksWGTInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } LOGF(debug, "FwdTrack finished.\n"); @@ -282,7 +282,7 @@ struct DGBCFilterRun3 { void init(InitContext&) { - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); } // some general Collisions and Tracks filter @@ -321,7 +321,7 @@ struct DGBCFilterRun3 { // loop over bcs int isDGBC; - for (auto bc : bcs) { + for (const auto& bc : bcs) { auto bcnum = bc.globalBC(); auto ccs = false; @@ -348,7 +348,7 @@ struct DGBCFilterRun3 { auto fwdTracksArray = ftibc.fwdtrack_as(); isDGBC = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } else { - auto fwdTracksArray = FTCs{{fwdtracks.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; + auto fwdTracksArray = fwdtracks.emptySlice(); isDGBC = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } diff --git a/EventFiltering/PWGUD/diffractionFilter.cxx b/EventFiltering/PWGUD/diffractionFilter.cxx index 946f95026e0..83bc848612e 100644 --- a/EventFiltering/PWGUD/diffractionFilter.cxx +++ b/EventFiltering/PWGUD/diffractionFilter.cxx @@ -89,13 +89,13 @@ struct DGFilterRun3 { { // reset collision counter collCounter = 0; - verbose = (int64_t)Verbose; + verbose = static_cast(Verbose); if (verbose <= 0) { verbose = 1000000000000; } // cut holder - diffCuts = (DGCutparHolder)diffCutsHolder; + diffCuts = DGCutparHolder(diffCutsHolder); // create histograms // stat @@ -284,7 +284,7 @@ struct DGFilterRun3 { // forward tracks int nforwardTracks[5] = {0}; - for (auto track : fwdtracks) { + for (const auto& track : fwdtracks) { nforwardTracks[track.trackType()]++; registry.fill(HIST("forwardTracks/timeResolution"), track.trackType(), track.trackTimeRes()); } diff --git a/EventFiltering/macros/cefpOutputChecker.C b/EventFiltering/macros/cefpOutputChecker.C index 48295a1746e..f6c4ffd78e5 100644 --- a/EventFiltering/macros/cefpOutputChecker.C +++ b/EventFiltering/macros/cefpOutputChecker.C @@ -21,18 +21,18 @@ void cefpOutputChecker(std::string histoFile = "AnalysisResults.root", std::string treeFile = "AO2D.root") { TFile referenceFile(histoFile.data()); - TH1* refScalers = (TH1*)referenceFile.Get("central-event-filter-task/scalers/mScalers"); - TH1* refFilters = (TH1*)referenceFile.Get("central-event-filter-task/scalers/mFiltered"); + TH1* refScalers = dynamic_cast(referenceFile.Get("central-event-filter-task/scalers/mScalers")); + TH1* refFilters = dynamic_cast(referenceFile.Get("central-event-filter-task/scalers/mFiltered")); TFile outputFile("cefpOutputChecker.root", "recreate"); - TH1* newFilters = (TH1*)refFilters->Clone("newFilters"); - TH1* newScalers = (TH1*)refScalers->Clone("newScalers"); + TH1* newFilters = dynamic_cast(refFilters->Clone("newFilters")); + TH1* newScalers = dynamic_cast(refScalers->Clone("newScalers")); newFilters->Reset(); newScalers->Reset(); TFile inputFile(treeFile.data()); for (auto key : *(inputFile.GetListOfKeys())) { - TTree* cefpTree = (TTree*)inputFile.Get(Form("%s/O2cefpdecision", key->GetName())); + TTree* cefpTree = dynamic_cast(inputFile.Get(Form("%s/O2cefpdecision", key->GetName()))); if (!cefpTree) continue; ULong64_t fCefpSelected, fCefpTriggered; diff --git a/EventFiltering/macros/checkBCrangesSkimming.C b/EventFiltering/macros/checkBCrangesSkimming.C index db58d7d9848..4224297f570 100644 --- a/EventFiltering/macros/checkBCrangesSkimming.C +++ b/EventFiltering/macros/checkBCrangesSkimming.C @@ -181,7 +181,7 @@ void checkNearbyBCs(std::vector& frames, ULong64_t bcDiffToleran } // Get RunNumber -std::string getRunNumber(std::string fileName) +std::string getRunNumber(const std::string& fileName) { std::string runNumber = ""; std::regex re("/5[0-9]*"); @@ -620,28 +620,28 @@ void checkBCrangesSkimming(std::string AnaFileName = "AnalysisResults.root", std TH1D *hOriginalSinglesRatio, *hOriginalDoublesRatio, *hOriginalMultiplesRatio; TH1D *hSkimmedSinglesRatio, *hSkimmedDoublesRatio, *hSkimmedMultiplesRatio; - hTriggerEff = reinterpret_cast(hSkimmedTotal.Clone("hTriggerEff")); + hTriggerEff = dynamic_cast(hSkimmedTotal.Clone("hTriggerEff")); hTriggerEff->SetTitle((runNumber + " skimmed efficiency;; Skimmed / Original").data()); hTriggerEff->Divide(&hOriginalTotal); hTriggerMatchesRatio.Divide(&hOriginalSingles); hTriggerSingleMatchesRatio.Divide(&hOriginalSingles); - hOriginalSinglesRatio = reinterpret_cast(hOriginalSingles.Clone("hOriginalSinglesRatio")); + hOriginalSinglesRatio = dynamic_cast(hOriginalSingles.Clone("hOriginalSinglesRatio")); hOriginalSinglesRatio->SetTitle((runNumber + " Original;;Singles / Total").data()); hOriginalSinglesRatio->Divide(&hOriginalTotal); - hOriginalDoublesRatio = reinterpret_cast(hOriginalDoubles.Clone("hOriginalDoublesRatio")); + hOriginalDoublesRatio = dynamic_cast(hOriginalDoubles.Clone("hOriginalDoublesRatio")); hOriginalDoublesRatio->SetTitle((runNumber + " Original;;Doubles / Total").data()); hOriginalDoublesRatio->Divide(&hOriginalTotal); - hOriginalMultiplesRatio = reinterpret_cast(hOriginalMultiples.Clone("hOriginalMultiplesRatio")); + hOriginalMultiplesRatio = dynamic_cast(hOriginalMultiples.Clone("hOriginalMultiplesRatio")); hOriginalMultiplesRatio->SetTitle((runNumber + " Original;;Multiples / Total").data()); hOriginalMultiplesRatio->Divide(&hOriginalTotal); - hSkimmedSinglesRatio = reinterpret_cast(hSkimmedSingles.Clone("hSkimmedSinglesRatio")); + hSkimmedSinglesRatio = dynamic_cast(hSkimmedSingles.Clone("hSkimmedSinglesRatio")); hSkimmedSinglesRatio->SetTitle((runNumber + " Skimmed;;Singles / Total").data()); hSkimmedSinglesRatio->Divide(&hSkimmedTotal); - hSkimmedDoublesRatio = reinterpret_cast(hSkimmedDoubles.Clone("hSkimmedDoublesRatio")); + hSkimmedDoublesRatio = dynamic_cast(hSkimmedDoubles.Clone("hSkimmedDoublesRatio")); hSkimmedDoublesRatio->SetTitle((runNumber + " Skimmed;;Doubles / Total").data()); hSkimmedDoublesRatio->Divide(&hSkimmedTotal); - hSkimmedMultiplesRatio = reinterpret_cast(hSkimmedMultiples.Clone("hSkimmedMultiplesRatio")); + hSkimmedMultiplesRatio = dynamic_cast(hSkimmedMultiples.Clone("hSkimmedMultiplesRatio")); hSkimmedMultiplesRatio->SetTitle((runNumber + " Skimmed;;Multiples / Total").data()); hSkimmedMultiplesRatio->Divide(&hSkimmedTotal); diff --git a/EventFiltering/macros/checkSkimming.C b/EventFiltering/macros/checkSkimming.C index a69f9d5d129..6f1961fb7bd 100644 --- a/EventFiltering/macros/checkSkimming.C +++ b/EventFiltering/macros/checkSkimming.C @@ -22,7 +22,7 @@ #include #include -void checkSkimming(std::string original_path = "AnalysisResults.root", std::string skimmed_path = "AnalysisResults_skimmed.root", TFile* outputFile = nullptr, bool skipDownscaled = true) +void checkSkimming(const std::string& original_path = "AnalysisResults.root", const std::string& skimmed_path = "AnalysisResults_skimmed.root", TFile* outputFile = nullptr, bool skipDownscaled = true) { gStyle->SetOptStat(0); std::string runNumber = ""; @@ -57,7 +57,7 @@ void checkSkimming(std::string original_path = "AnalysisResults.root", std::stri // Find the bins corresponding to the desired labels std::vector selected_bins1, selected_bins2; - for (auto lab : labels) { + for (const auto& lab : labels) { int bin1 = hist1->GetXaxis()->FindBin(lab.c_str()); if (bin1 == -1) { std::cerr << "Error: Label " << lab << " not found in histogram 1." << std::endl; @@ -104,7 +104,7 @@ void checkSkimming(std::string original_path = "AnalysisResults.root", std::stri } } -void checkSkimming(std::string listName = "period.txt", bool skipDownscaled = true) +void checkSkimming(const std::string& listName = "period.txt", bool skipDownscaled = true) { std::string periodName = listName.substr(0, listName.find_last_of('.')); std::ifstream file(listName); @@ -124,7 +124,7 @@ void checkSkimming(std::string listName = "period.txt", bool skipDownscaled = tr std::string original_path = line.substr(0, pos); std::string skimmed_path = line.substr(pos + 1); checkSkimming(original_path, skimmed_path, outputFile, skipDownscaled); - TH1* hRatio = (TH1*)gDirectory->Get("hRatio"); + TH1* hRatio = dynamic_cast(gDirectory->Get("hRatio")); hRatio->Draw(); std::string suffix = counter == 0 ? "(" : (counter == lineCounter - 1 ? ")" : ""); c1.Print((periodName + ".pdf" + suffix).data()); diff --git a/EventFiltering/macros/getMenu.C b/EventFiltering/macros/getMenu.C index f1674d14539..2166cf4635d 100644 --- a/EventFiltering/macros/getMenu.C +++ b/EventFiltering/macros/getMenu.C @@ -27,7 +27,7 @@ #include #include -void getMenu(int runNumber, std::string baseCCDBPath = "Users/m/mpuccio/EventFiltering/OTS/Chunked/") +void getMenu(int runNumber, const std::string& baseCCDBPath = "Users/m/mpuccio/EventFiltering/OTS/Chunked/") { auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); TH1* counters = ccdb.getForRun(baseCCDBPath + "FilterCounters", runNumber); @@ -69,7 +69,7 @@ std::vector getMenuForPeriod(std::string period) TGrid::Connect("alien://"); TFile* scalersFile = TFile::Open((std::string("alien://") + firstLine).data(), "READ"); - TH1D* counters = (TH1D*)scalersFile->Get("central-event-filter-task/scalers/mFiltered"); + TH1D* counters = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); TAxis* axis = counters->GetXaxis(); std::vector binLabels(axis->GetNbins() - 2); @@ -84,7 +84,7 @@ std::vector getMenuForPeriod(std::string period) return binLabels; } -void getMenu(std::string periods) +void getMenu(const std::string& periods) { std::stringstream ss(periods); std::string period; diff --git a/EventFiltering/macros/selectivityPlot.C b/EventFiltering/macros/selectivityPlot.C index 344101a0984..7847c320744 100644 --- a/EventFiltering/macros/selectivityPlot.C +++ b/EventFiltering/macros/selectivityPlot.C @@ -23,7 +23,7 @@ #include -void selectivityPlot(int runNumber = 550781, TString inputfile = "AnalysisResults_550781.root", TString outputfolder = "") +void selectivityPlot(int runNumber = 550781, const TString& inputfile = "AnalysisResults_550781.root", const TString& outputfolder = "") { TCanvas* Canvas_1 = new TCanvas("Canvas_1", "Canvas_1", 928, 592); gStyle->SetOptFit(0); @@ -45,7 +45,7 @@ void selectivityPlot(int runNumber = 550781, TString inputfile = "AnalysisResult Canvas_1->SetFrameBorderMode(0); TFile* file = new TFile(inputfile.Data()); - TH1D* mFiltered = (TH1D*)file->Get("central-event-filter-task/scalers/mFiltered"); + TH1D* mFiltered = dynamic_cast(file->Get("central-event-filter-task/scalers/mFiltered")); double nEvents = mFiltered->GetBinContent(1); mFiltered->Scale(1. / nEvents); mFiltered->SetLineColor(2); diff --git a/EventFiltering/macros/uploadOTSobjects.C b/EventFiltering/macros/uploadOTSobjects.C index a44583ea618..378526c7a3f 100644 --- a/EventFiltering/macros/uploadOTSobjects.C +++ b/EventFiltering/macros/uploadOTSobjects.C @@ -39,7 +39,7 @@ constexpr uint32_t chunkSize = 1000000; -void uploadOTSobjects(std::string inputList, std::string passName, bool useAlien, bool chunkedProcessing = true) +void uploadOTSobjects(std::string inputList, const std::string& passName, bool useAlien, bool chunkedProcessing = true) { const std::string kBaseCCDBPath = "EventFiltering/Zorro/"; std::string baseCCDBpath = passName.empty() ? kBaseCCDBPath : kBaseCCDBPath + passName + "/"; @@ -67,15 +67,15 @@ void uploadOTSobjects(std::string inputList, std::string passName, bool useAlien auto orbitResetTimestamp = (*ctp)[0]; path = useAlien ? "alien://" + path : path; std::unique_ptr scalersFile{TFile::Open((path + "/AnalysisResults_fullrun.root").data(), "READ")}; - TH1* scalers = static_cast(scalersFile->Get("central-event-filter-task/scalers/mScalers")); - TH1* filters = static_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); + TH1* scalers = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mScalers")); + TH1* filters = dynamic_cast(scalersFile->Get("central-event-filter-task/scalers/mFiltered")); api.storeAsTFile(scalers, baseCCDBpath + "FilterCounters", metadata, duration.first, duration.second + 1); api.storeAsTFile(filters, baseCCDBpath + "SelectionCounters", metadata, duration.first, duration.second + 1); - TH1* hCounterTVX = static_cast(scalersFile->Get("bc-selection-task/hCounterTVX")); + TH1* hCounterTVX = dynamic_cast(scalersFile->Get("bc-selection-task/hCounterTVX")); if (!hCounterTVX) { - hCounterTVX = static_cast(scalersFile->Get("lumi-task/hCounterTVX")); + hCounterTVX = dynamic_cast(scalersFile->Get("lumi-task/hCounterTVX")); if (!hCounterTVX) { - hCounterTVX = static_cast(scalersFile->Get("eventselection-run3/luminosity/hCounterTVX")); + hCounterTVX = dynamic_cast(scalersFile->Get("eventselection-run3/luminosity/hCounterTVX")); if (!hCounterTVX) { std::cout << "No hCounterTVX histogram found in the file, skipping upload for run " << runString << std::endl; continue; @@ -88,7 +88,7 @@ void uploadOTSobjects(std::string inputList, std::string passName, bool useAlien std::unique_ptr bcRangesFile{TFile::Open((path + "/bcRanges_fullrun.root").data(), "READ")}; int Nmax = 0; for (auto key : *(bcRangesFile->GetListOfKeys())) { - TTree* cefpTree = static_cast(bcRangesFile->Get(Form("%s/selectedBC", key->GetName()))); + TTree* cefpTree = dynamic_cast(bcRangesFile->Get(Form("%s/selectedBC", key->GetName()))); if (!cefpTree) continue; ZorroHelper bci; @@ -134,7 +134,7 @@ void uploadOTSobjects(std::string inputList, std::string passName, bool useAlien auto bcNext{nextHelper.bcAOD < nextHelper.bcEvSel ? nextHelper.bcAOD : nextHelper.bcEvSel}; if (bcNext - bcEnd > 2001 / o2::constants::lhc::LHCBunchSpacingMUS) { /// ensure a gap of 2ms between chunks chunk.insert(chunk.begin(), zorroHelpers.begin() + helperIndex, zorroHelpers.begin() + endIndex + 1); - endTS = (orbitResetTimestamp + int64_t(bcEnd * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000 + 1; + endTS = (orbitResetTimestamp + static_cast(bcEnd * o2::constants::lhc::LHCBunchSpacingNS * 1e-3)) / 1000 + 1; break; } bcEnd = nextHelper.bcAOD > nextHelper.bcEvSel ? nextHelper.bcAOD : nextHelper.bcEvSel; diff --git a/PWGCF/Core/AnalysisConfigurableCuts.h b/PWGCF/Core/AnalysisConfigurableCuts.h index 76b7b759f07..aebb6b2f407 100644 --- a/PWGCF/Core/AnalysisConfigurableCuts.h +++ b/PWGCF/Core/AnalysisConfigurableCuts.h @@ -151,7 +151,7 @@ class SimpleInclusiveCut : public TNamed SimpleInclusiveCut& operator=(const SimpleInclusiveCut&); private: - ClassDef(SimpleInclusiveCut, 1); + ClassDefOverride(SimpleInclusiveCut, 1); }; } // namespace analysis diff --git a/PWGCF/Core/CorrelationContainer.cxx b/PWGCF/Core/CorrelationContainer.cxx index 8cbd834ec8f..4590d9403d8 100644 --- a/PWGCF/Core/CorrelationContainer.cxx +++ b/PWGCF/Core/CorrelationContainer.cxx @@ -24,11 +24,12 @@ #include #include #include +#include +#include +#include #include #include #include -#include -#include #include #include #include @@ -36,6 +37,9 @@ #include #include +#include +#include +#include #include #include @@ -45,7 +49,7 @@ using namespace o2::constants::math; ClassImp(CorrelationContainer); -const Int_t CorrelationContainer::fgkCFSteps = 11; +const int CorrelationContainer::fgkCFSteps = 11; CorrelationContainer::CorrelationContainer() : TNamed(), mPairHist(nullptr), @@ -63,9 +67,9 @@ CorrelationContainer::CorrelationContainer() : TNamed(), mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // Default constructor @@ -90,9 +94,9 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // correlationAxis has to provide a 6 length list of AxisSpec which contain: @@ -101,6 +105,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl // // efficiencyAxis has to provide a 3 length list of AxisSpec which contains: // eta (for efficiency), pt (for efficiency), vertex (for efficiency) + // and can provide a fourth one, e.g. phi (for efficiency) // The third axis of correlationAxis (multiplicity/centrality) is used also for the efficiency // // (optional) userAxis can contain optional user axes which are added to pair and trigger histogram and are @@ -112,7 +117,7 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl std::vector pairAxis(correlationAxis); pairAxis.insert(pairAxis.end(), userAxis.begin(), userAxis.end()); - long bins = 1; + int64_t bins = 1; for (const auto& axis : pairAxis) { bins *= axis.getNbins(); } @@ -124,7 +129,11 @@ CorrelationContainer::CorrelationContainer(const char* name, const char* objTitl triggerAxis.insert(triggerAxis.end(), userAxis.begin(), userAxis.end()); mTriggerHist = HistFactory::createHist({"mTriggerHist", "d^{2}N_{ch}/d#varphid#eta", {HistType::kStepTHnF, triggerAxis, fgkCFSteps}}).release(); - mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, {efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2]}, fgkCFSteps}}).release(); + std::vector trackEfficiencyAxis({efficiencyAxis[0], efficiencyAxis[1], {5, -0.5, 4.5, "species"}, correlationAxis[3], efficiencyAxis[2]}); + if (efficiencyAxis.size() > 3) { + trackEfficiencyAxis.push_back(efficiencyAxis[3]); + } + mTrackHistEfficiency = HistFactory::createHist({"mTrackHistEfficiency", "Tracking efficiency", {HistType::kStepTHnF, trackEfficiencyAxis, fgkCFSteps}}).release(); mEventCount = HistFactory::createHist({"mEventCount", ";step;centrality;count", {HistType::kTH2F, {{fgkCFSteps + 2, -2.5, -0.5 + fgkCFSteps, "step"}, correlationAxis[3]}}}).release(); } @@ -145,16 +154,16 @@ CorrelationContainer::CorrelationContainer(const CorrelationContainer& c) : TNam mZVtxMax(0), mTrackEtaCut(0), mWeightPerEvent(0), - mSkipScaleMixedEvent(kFALSE), + mSkipScaleMixedEvent(false), mCache(nullptr), - mGetMultCacheOn(kFALSE), + mGetMultCacheOn(false), mGetMultCache(nullptr) { // // CorrelationContainer copy constructor // - ((CorrelationContainer&)c).Copy(*this); + c.Copy(*this); } //____________________________________________________________________ @@ -194,7 +203,7 @@ CorrelationContainer& CorrelationContainer::operator=(const CorrelationContainer // assigment operator if (this != &c) { - ((CorrelationContainer&)c).Copy(*this); + c.Copy(*this); } return *this; @@ -205,7 +214,7 @@ void CorrelationContainer::Copy(TObject& c) const { // copy function - CorrelationContainer& target = (CorrelationContainer&)c; + auto& target = dynamic_cast(c); if (mPairHist) { target.mPairHist = dynamic_cast(mPairHist->Clone()); @@ -252,14 +261,14 @@ Long64_t CorrelationContainer::Merge(TCollection* list) TObject* obj = nullptr; // collections of objects - const UInt_t kMaxLists = 4; + const unsigned int kMaxLists = 4; TList** lists = new TList*[kMaxLists]; - for (UInt_t i = 0; i < kMaxLists; i++) { + for (unsigned int i = 0; i < kMaxLists; i++) { lists[i] = new TList; } - Int_t count = 0; + int count = 0; while ((obj = iter->Next())) { CorrelationContainer* entry = dynamic_cast(obj); @@ -285,7 +294,7 @@ Long64_t CorrelationContainer::Merge(TCollection* list) mTrackHistEfficiency->Merge(lists[2]); mEventCount->Merge(lists[3]); - for (UInt_t i = 0; i < kMaxLists; i++) { + for (unsigned int i = 0; i < kMaxLists; i++) { delete lists[i]; } @@ -316,7 +325,7 @@ void CorrelationContainer::resetBinLimits(THnBase* grid, int max_dimension) max_dimension = grid->GetNdimensions(); } - for (Int_t i = 0; i < max_dimension; i++) { + for (int i = 0; i < max_dimension; i++) { if (grid->GetAxis(i)->TestBit(TAxis::kAxisRange)) { grid->GetAxis(i)->SetRange(0, 0); // reset range } @@ -324,14 +333,14 @@ void CorrelationContainer::resetBinLimits(THnBase* grid, int max_dimension) } //____________________________________________________________________ -void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) +void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax) { // prints the number of empty bins in the track end event histograms in the given step - Int_t binBegin[4]; - Int_t binEnd[4]; + int binBegin[4]; + int binEnd[4]; - for (Int_t i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { binBegin[i] = 1; binEnd[i] = mPairHist->getTHn(step)->GetAxis(i)->GetNbins(); } @@ -354,11 +363,11 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo // start from multiplicity 1 binBegin[3] = mPairHist->getTHn(step)->GetAxis(3)->FindBin(1); - Int_t total = 0; - Int_t count = 0; - Int_t vars[4]; + int total = 0; + int count = 0; + int vars[4]; - for (Int_t i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { vars[i] = binBegin[i]; } @@ -374,7 +383,7 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } vars[3]++; - for (Int_t i = 3; i > 0; i--) { + for (int i = 3; i > 0; i--) { if (vars[i] == binEnd[i] + 1) { vars[i] = binBegin[i]; vars[i - 1]++; @@ -391,7 +400,7 @@ void CorrelationContainer::countEmptyBins(CorrelationContainer::CFStep step, Flo } //____________________________________________________________________ -void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, THnBase** trackHist, TH2** eventHist) +void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, THnBase** trackHist, TH2** eventHist) { // Calculates a 4d histogram with deltaphi, deltaeta, zvtx, multiplicity on track level and // a 2d histogram on event level (as fct of zvtx, multiplicity) @@ -414,15 +423,15 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F sparse->GetAxis(1)->SetRangeUser(mPtMin, mPtMax); } - Int_t firstBin = sparse->GetAxis(2)->FindBin(ptTriggerMin); - Int_t lastBin = sparse->GetAxis(2)->FindBin(ptTriggerMax); + int firstBin = sparse->GetAxis(2)->FindBin(ptTriggerMin); + int lastBin = sparse->GetAxis(2)->FindBin(ptTriggerMax); LOGF(info, "Using trigger pT range %d --> %d", firstBin, lastBin); sparse->GetAxis(2)->SetRange(firstBin, lastBin); mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); - Int_t dimensions[] = {4, 0, 5, 3}; + int dimensions[] = {4, 0, 5, 3}; THnBase* tmpTrackHist = sparse->ProjectionND(4, dimensions, "E"); - *eventHist = (TH2*)mTriggerHist->getTHn(step)->Projection(1, 2); // NOTE the syntax is Projection(Y, X) --> produces a histogram where x = axis 2 (vertex) and y = axis 1 (multiplicity) + *eventHist = static_cast(mTriggerHist->getTHn(step)->Projection(1, 2)); // NOTE the syntax is Projection(Y, X) --> produces a histogram where x = axis 2 (vertex) and y = axis 1 (multiplicity) // convert to THn *trackHist = changeToThn(tmpTrackHist); delete tmpTrackHist; @@ -431,7 +440,7 @@ void CorrelationContainer::getHistsZVtxMult(CorrelationContainer::CFStep step, F resetBinLimits(mTriggerHist->getTHn(step), 3); } -TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger) +TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, bool normalizePerTrigger) { // Calculate per trigger yield without considering mixed event // Sums over all vertex bins, and respects the pT and centrality limits set in the class @@ -471,7 +480,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, } TH2* yield = trackSameAll->Projection(1, 0, "E"); - Float_t triggers = eventSameAll->Integral(vertexBinBegin, vertexBinEnd, multBinBegin, multBinEnd); + float triggers = eventSameAll->Integral(vertexBinBegin, vertexBinEnd, multBinBegin, multBinEnd); if (normalizePerTrigger) { LOGF(info, "Dividing %f tracks by %f triggers", yield->Integral(), triggers); @@ -481,7 +490,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, } // normalizate to dphi width - Float_t normalization = yield->GetXaxis()->GetBinWidth(1); + float normalization = yield->GetXaxis()->GetBinWidth(1); yield->Scale(1.0 / normalization); delete trackSameAll; @@ -490,7 +499,7 @@ TH2* CorrelationContainer::getPerTriggerYield(CorrelationContainer::CFStep step, return yield; } -TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax, Bool_t normalizePerTrigger, Int_t stepForMixed, Int_t* trigger) +TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax, bool normalizePerTrigger, int stepForMixed, int* trigger) { // Extract 2D per trigger yield with mixed event correction. The quantity is calculated for *each* vertex bin and multiplicity bin and then a sum of ratios is performed: // 1_N [ (same/mixed)_1 + (same/mixed)_2 + (same/mixed)_3 + ... ] @@ -506,8 +515,8 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // normalizePerTrigger: divide through number of triggers // do not add this hists to the directory - Bool_t oldStatus = TH1::AddDirectoryStatus(); - TH1::AddDirectory(kFALSE); + bool oldStatus = TH1::AddDirectoryStatus(); + TH1::AddDirectory(false); TH2* totalTracks = nullptr; @@ -518,11 +527,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati TH2* eventMixedAll = nullptr; TH2* eventMixedAllStep6 = nullptr; - Long64_t totalEvents = 0; - Int_t nCorrelationFunctions = 0; + int64_t totalEvents = 0; + int nCorrelationFunctions = 0; getHistsZVtxMult(step, ptTriggerMin, ptTriggerMax, &trackSameAll, &eventSameAll); - mixed->getHistsZVtxMult((stepForMixed == -1) ? step : (CFStep)stepForMixed, ptTriggerMin, ptTriggerMax, &trackMixedAll, &eventMixedAll); + mixed->getHistsZVtxMult((stepForMixed == -1) ? step : static_cast(stepForMixed), ptTriggerMin, ptTriggerMax, &trackMixedAll, &eventMixedAll); // If we ask for histograms from step8 (TTR cut applied) there is a hole at 0,0; so this cannot be used for the // mixed-event normalization. If step6 is available, the normalization factor is read out from that one. @@ -548,15 +557,15 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati multBinEnd = multAxis->FindBin(mCentralityMax); } - for (Int_t multBin = TMath::Max(1, multBinBegin); multBin <= TMath::Min(multAxis->GetNbins(), multBinEnd); multBin++) { + for (int multBin = std::max(1, multBinBegin); multBin <= std::min(multAxis->GetNbins(), multBinEnd); multBin++) { trackSameAll->GetAxis(3)->SetRange(multBin, multBin); trackMixedAll->GetAxis(3)->SetRange(multBin, multBin); if (trackMixedAllStep6) { trackMixedAllStep6->GetAxis(3)->SetRange(multBin, multBin); } - Double_t mixedNorm = 1; - Double_t mixedNormError = 0; + double mixedNorm = 1; + double mixedNormError = 0; if (!mSkipScaleMixedEvent) { // get mixed normalization correction factor: is independent of vertex bin if scaled with number of triggers @@ -569,40 +578,40 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati tracksMixed = trackMixedAll->Projection(1, 0, "E"); } // Printf("%f", tracksMixed->Integral()); - Float_t binWidthEta = tracksMixed->GetYaxis()->GetBinWidth(1); + float binWidthEta = tracksMixed->GetYaxis()->GetBinWidth(1); if (stepForMixed == -1 && step == kCFStepBiasStudy && !trackMixedAllStep6) { // get mixed event normalization by assuming full acceptance at deta at 0 (integrate over dphi), excluding (0, 0) - Float_t phiExclude = 0.41; + float phiExclude = 0.41; mixedNorm = tracksMixed->IntegralAndError(1, tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1, tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError); - Double_t mixedNormError2 = 0; - Double_t mixedNorm2 = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(phiExclude) + 1, tracksMixed->GetNbinsX(), tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError2); + double mixedNormError2 = 0; + double mixedNorm2 = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(phiExclude) + 1, tracksMixed->GetNbinsX(), tracksMixed->GetYaxis()->FindBin(-0.01), tracksMixed->GetYaxis()->FindBin(0.01), mixedNormError2); if (mixedNormError == 0 || mixedNormError2 == 0) { LOGF(error, "ERROR: Skipping multiplicity %d because mixed event is empty %f %f %f %f", multBin, mixedNorm, mixedNormError, mixedNorm2, mixedNormError2); continue; } - Int_t nBinsMixedNorm = (tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1 - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); + int nBinsMixedNorm = (tracksMixed->GetXaxis()->FindBin(-phiExclude) - 1 - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); mixedNorm /= nBinsMixedNorm; mixedNormError /= nBinsMixedNorm; - Int_t nBinsMixedNorm2 = (tracksMixed->GetNbinsX() - tracksMixed->GetXaxis()->FindBin(phiExclude) - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); + int nBinsMixedNorm2 = (tracksMixed->GetNbinsX() - tracksMixed->GetXaxis()->FindBin(phiExclude) - 1 + 1) * (tracksMixed->GetYaxis()->FindBin(0.01) - tracksMixed->GetYaxis()->FindBin(-0.01) + 1); mixedNorm2 /= nBinsMixedNorm2; mixedNormError2 /= nBinsMixedNorm2; mixedNorm = mixedNorm / mixedNormError / mixedNormError + mixedNorm2 / mixedNormError2 / mixedNormError2; - mixedNormError = TMath::Sqrt(1.0 / (1.0 / mixedNormError / mixedNormError + 1.0 / mixedNormError2 / mixedNormError2)); + mixedNormError = std::sqrt(1.0 / (1.0 / mixedNormError / mixedNormError + 1.0 / mixedNormError2 / mixedNormError2)); mixedNorm *= mixedNormError * mixedNormError; } else { // get mixed event normalization at (0,0) // NOTE if variable bin size is used around (0,0) to reduce two-track effect to limited bins, the normalization gets a bit tricky here (finite bin correction and normalization are made only for fixed size bins). // The normalization factor has to determined in a bin as large as the normal bin size, as a proxy the bin with index (1, 1) is used - Float_t binWidthPhi = tracksMixed->GetXaxis()->GetBinWidth(1); + float binWidthPhi = tracksMixed->GetXaxis()->GetBinWidth(1); mixedNorm = tracksMixed->IntegralAndError(tracksMixed->GetXaxis()->FindBin(-binWidthPhi + 1e-4), tracksMixed->GetXaxis()->FindBin(binWidthPhi - 1e-4), tracksMixed->GetYaxis()->FindBin(-binWidthEta + 1e-4), tracksMixed->GetYaxis()->FindBin(binWidthEta - 1e-4), mixedNormError); - Int_t nBinsMixedNorm = 4; // NOTE this is fixed on purpose, even if binning is made finer around (0,0), this corresponds to the equivalent of four "large" bins around (0,0) + int nBinsMixedNorm = 4; // NOTE this is fixed on purpose, even if binning is made finer around (0,0), this corresponds to the equivalent of four "large" bins around (0,0) mixedNorm /= nBinsMixedNorm; mixedNormError /= nBinsMixedNorm; @@ -614,7 +623,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // finite bin correction if (mTrackEtaCut > 0) { - Double_t finiteBinCorrection = -1.0 / (2 * mTrackEtaCut) * binWidthEta / 2 + 1; + double finiteBinCorrection = -1.0 / (2 * mTrackEtaCut) * binWidthEta / 2 + 1; LOGF(info, "Finite bin correction: %f", finiteBinCorrection); mixedNorm /= finiteBinCorrection; mixedNormError /= finiteBinCorrection; @@ -622,7 +631,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati LOGF(error, "ERROR: mTrackEtaCut not set. Finite bin correction cannot be applied. Continuing anyway..."); } - Float_t triggers = eventMixedAll->Integral(1, eventMixedAll->GetNbinsX(), multBin, multBin); + float triggers = eventMixedAll->Integral(1, eventMixedAll->GetNbinsX(), multBin, multBin); // Printf("%f +- %f | %f | %f", mixedNorm, mixedNormError, triggers, mixedNorm / triggers); if (triggers <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d because mixed event is empty", multBin); @@ -630,7 +639,6 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } mixedNorm /= triggers; - mixedNormError /= triggers; delete tracksMixed; } else { @@ -647,15 +655,15 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // normParameters->Fill(mixedNorm); TAxis* vertexAxis = trackSameAll->GetAxis(2); - Int_t vertexBinBegin = 1; - Int_t vertexBinEnd = vertexAxis->GetNbins(); + int vertexBinBegin = 1; + int vertexBinEnd = vertexAxis->GetNbins(); if (mZVtxMax > mZVtxMin) { vertexBinBegin = vertexAxis->FindBin(mZVtxMin); vertexBinEnd = vertexAxis->FindBin(mZVtxMax); } - for (Int_t vertexBin = vertexBinBegin; vertexBin <= vertexBinEnd; vertexBin++) { + for (int vertexBin = vertexBinBegin; vertexBin <= vertexBinEnd; vertexBin++) { trackSameAll->GetAxis(2)->SetRange(vertexBin, vertexBin); trackMixedAll->GetAxis(2)->SetRange(vertexBin, vertexBin); @@ -666,13 +674,13 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // TH1* histMixedproj = mixedTwoD->ProjectionY(); // histMixedproj->Scale(1.0 / mixedTwoD->GetNbinsX()); // - // for (Int_t x=1; x<=mixedTwoD->GetNbinsX(); x++) - // for (Int_t y=1; y<=mixedTwoD->GetNbinsY(); y++) + // for (int x=1; x<=mixedTwoD->GetNbinsX(); x++) + // for (int y=1; y<=mixedTwoD->GetNbinsY(); y++) // mixedTwoD->SetBinContent(x, y, histMixedproj->GetBinContent(y)); // delete histMixedproj; - Float_t triggers2 = eventMixedAll->Integral(vertexBin, vertexBin, multBin, multBin); + float triggers2 = eventMixedAll->Integral(vertexBin, vertexBin, multBin, multBin); if (triggers2 <= 0) { LOGF(error, "ERROR: Skipping multiplicity %d vertex bin %d because mixed event is empty", multBin, vertexBin); } else { @@ -687,14 +695,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati // new TCanvas; tracksMixed->DrawClone("SURF1"); // some code to judge the relative contribution of the different correlation functions to the overall uncertainty - Double_t sums[] = {0, 0, 0}; - Double_t errors[] = {0, 0, 0}; + double sums[] = {0, 0, 0}; + double errors[] = {0, 0, 0}; - Int_t checkBinYBegin = 1; // tracksSame->GetXaxis()->FindBin(-0.79); - Int_t checkBinYEnd = tracksSame->GetNbinsY(); // tracksSame->GetXaxis()->FindBin(0.79); + int checkBinYBegin = 1; // tracksSame->GetXaxis()->FindBin(-0.79); + int checkBinYEnd = tracksSame->GetNbinsY(); // tracksSame->GetXaxis()->FindBin(0.79); - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { - for (Int_t y = checkBinYBegin; y <= checkBinYEnd; y++) { + for (int x = 1; x <= tracksSame->GetNbinsX(); x++) { + for (int y = checkBinYBegin; y <= checkBinYEnd; y++) { sums[0] += tracksSame->GetBinContent(x, y); errors[0] += tracksSame->GetBinError(x, y); sums[1] += tracksMixed->GetBinContent(x, y); @@ -704,14 +712,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati tracksSame->Divide(tracksMixed); - for (Int_t x = 1; x <= tracksSame->GetNbinsX(); x++) { - for (Int_t y = checkBinYBegin; y <= checkBinYEnd; y++) { + for (int x = 1; x <= tracksSame->GetNbinsX(); x++) { + for (int y = checkBinYBegin; y <= checkBinYEnd; y++) { sums[2] += tracksSame->GetBinContent(x, y); errors[2] += tracksSame->GetBinError(x, y); } } - for (Int_t x = 0; x < 3; x++) { + for (int x = 0; x < 3; x++) { if (sums[x] > 0) { errors[x] /= sums[x]; } @@ -727,7 +735,7 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati */ if (!totalTracks) { - totalTracks = (TH2*)tracksSame->Clone("totalTracks"); + totalTracks = dynamic_cast(tracksSame->Clone("totalTracks")); } else { totalTracks->Add(tracksSame); } @@ -745,11 +753,11 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } if (totalTracks) { - Double_t sums[] = {0, 0, 0}; - Double_t errors[] = {0, 0, 0}; + double sums[] = {0, 0, 0}; + double errors[] = {0, 0, 0}; - for (Int_t x = 1; x <= totalTracks->GetNbinsX(); x++) { - for (Int_t y = 1; y <= totalTracks->GetNbinsY(); y++) { + for (int x = 1; x <= totalTracks->GetNbinsX(); x++) { + for (int y = 1; y <= totalTracks->GetNbinsY(); y++) { sums[0] += totalTracks->GetBinContent(x, y); errors[0] += totalTracks->GetBinError(x, y); } @@ -759,17 +767,17 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati } if (normalizePerTrigger) { - LOGF(info, "Dividing %f tracks by %lld events (%d correlation function(s)) (error %f)", totalTracks->Integral(), totalEvents, nCorrelationFunctions, errors[0]); + LOGF(info, "Dividing %f tracks by %ld events (%d correlation function(s)) (error %f)", totalTracks->Integral(), totalEvents, nCorrelationFunctions, errors[0]); if (totalEvents > 0) { totalTracks->Scale(1.0 / totalEvents); } } if (trigger != nullptr) { - *trigger = (Int_t)totalEvents; + *trigger = static_cast(totalEvents); } // normalizate to dphi width - Float_t normalization = totalTracks->GetXaxis()->GetBinWidth(1); + float normalization = totalTracks->GetXaxis()->GetBinWidth(1); totalTracks->Scale(1.0 / normalization); } @@ -787,14 +795,14 @@ TH2* CorrelationContainer::getSumOfRatios(CorrelationContainer* mixed, Correlati return totalTracks; } -TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, Float_t ptTriggerMin, Float_t ptTriggerMax) +TH1* CorrelationContainer::getTriggersAsFunctionOfMultiplicity(CorrelationContainer::CFStep step, float ptTriggerMin, float ptTriggerMax) { // returns the distribution of triggers as function of centrality/multiplicity resetBinLimits(mTriggerHist->getTHn(step), 3); - Int_t firstBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMin); - Int_t lastBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMax); + int firstBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMin); + int lastBin = mTriggerHist->getTHn(step)->GetAxis(0)->FindBin(ptTriggerMax); LOGF(info, "Using pT range %d --> %d", firstBin, lastBin); mTriggerHist->getTHn(step)->GetAxis(0)->SetRange(firstBin, lastBin); @@ -826,7 +834,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici sourceContainer->getTHn(step2)->GetAxis(0)->SetRangeUser(mEtaMin, mEtaMax); } - Int_t dimensions[] = {0, 1, 3, 4}; + int dimensions[] = {0, 1, 3, 4}; THnBase* generated = sourceContainer->getTHn(step1)->ProjectionND(4, dimensions); THnBase* measured = sourceContainer->getTHn(step2)->ProjectionND(4, dimensions); @@ -835,7 +843,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici resetBinLimits(sourceContainer->getTHn(step1)); resetBinLimits(sourceContainer->getTHn(step2)); - THnBase* clone = (THnBase*)measured->Clone(); + auto* clone = dynamic_cast(measured->Clone()); clone->Divide(measured, generated, 1, 1, "B"); @@ -846,7 +854,7 @@ THnBase* CorrelationContainer::getTrackEfficiencyND(EfficiencyStep step1, Effici } //____________________________________________________________________ -TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencyStep step2, Int_t axis1, Int_t axis2, Int_t source, Int_t axis3) +TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencyStep step2, int axis1, int axis2, int source, int axis3) { // creates a track-level efficiency by dividing step2 by step1 // projected to axis1 and axis2 (optional if >= 0) @@ -907,8 +915,8 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } // check for bins with less than 50 entries, print warning - Int_t binBegin[3]; - Int_t binEnd[3]; + int binBegin[3]; + int binEnd[3]; binBegin[0] = 1; binBegin[1] = 1; @@ -935,7 +943,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt if (mPtMax > mPtMin) { // TODO this is just checking up to 15 for now - Float_t ptMax = TMath::Min((Float_t)15., mPtMax); + float ptMax = std::min(static_cast(15.), mPtMax); if (axis1 == 1) { binBegin[0] = generated->GetXaxis()->FindBin(mPtMin); binEnd[0] = generated->GetXaxis()->FindBin(ptMax); @@ -950,15 +958,15 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } } - Int_t total = 0; - Int_t count = 0; - Int_t vars[3]; + int total = 0; + int count = 0; + int vars[3]; - for (Int_t i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { vars[i] = binBegin[i]; } - const Int_t limit = 50; + const int limit = 50; while (1) { if (generated->GetDimension() == 1 && generated->GetBinContent(vars[0]) < limit) { LOGF(info, "Empty bin at %s=%.2f (%.2f entries)", generated->GetXaxis()->GetTitle(), generated->GetXaxis()->GetBinCenter(vars[0]), generated->GetBinContent(vars[0])); @@ -1006,23 +1014,25 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // 2d rebin with variable axis does not exist in root TH1* tmp = measured; - measured = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { - for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { - ((TH2*)measured)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); - measured->SetBinError(x, y, 0); // cannot trust bin error, set to 0 + auto* measuredRebinned = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); + for (int x = 1; x <= tmp->GetNbinsX(); x++) { + for (int y = 1; y <= tmp->GetNbinsY(); y++) { + measuredRebinned->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); + measuredRebinned->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } } + measured = measuredRebinned; delete tmp; tmp = generated; - generated = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); - for (Int_t x = 1; x <= tmp->GetNbinsX(); x++) { - for (Int_t y = 1; y <= tmp->GetNbinsY(); y++) { - ((TH2*)generated)->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); - generated->SetBinError(x, y, 0); // cannot trust bin error, set to 0 + auto* generatedRebinned = new TH2D(Form("%s_rebinned", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetXbins()->GetArray(), tmp->GetNbinsY(), tmp->GetYaxis()->GetXbins()->GetArray()); + for (int x = 1; x <= tmp->GetNbinsX(); x++) { + for (int y = 1; y <= tmp->GetNbinsY(); y++) { + generatedRebinned->Fill(tmp->GetXaxis()->GetBinCenter(x), tmp->GetYaxis()->GetBinCenter(y), tmp->GetBinContent(x, y)); + generatedRebinned->SetBinError(x, y, 0); // cannot trust bin error, set to 0 } } + generated = generatedRebinned; delete tmp; } else { TH1* tmp = measured; @@ -1046,7 +1056,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt LOGF(fatal, "Invalid binning --> %d %d", axis->GetNbins(), measured->GetNbinsX()); } - Double_t newBins[] = {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 100.0}; + double newBins[] = {0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 100.0}; // reduce binning below 5 GeV/c TH1* tmp = measured; @@ -1056,7 +1066,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // increase binning above 5 GeV/c tmp = measured; measured = new TH1F(Form("%s_rebinned2", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetBinLowEdge(1), axis->GetBinUpEdge(axis->GetNbins())); - for (Int_t bin = 1; bin <= measured->GetNbinsX(); bin++) { + for (int bin = 1; bin <= measured->GetNbinsX(); bin++) { measured->SetBinContent(bin, tmp->GetBinContent(tmp->FindBin(measured->GetBinCenter(bin)))); measured->SetBinError(bin, tmp->GetBinError(tmp->FindBin(measured->GetBinCenter(bin)))); } @@ -1070,7 +1080,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt // increase binning above 5 GeV/c tmp = generated; generated = new TH1F(Form("%s_rebinned2", tmp->GetName()), tmp->GetTitle(), axis->GetNbins(), axis->GetBinLowEdge(1), axis->GetBinUpEdge(axis->GetNbins())); - for (Int_t bin = 1; bin <= generated->GetNbinsX(); bin++) { + for (int bin = 1; bin <= generated->GetNbinsX(); bin++) { generated->SetBinContent(bin, tmp->GetBinContent(tmp->FindBin(generated->GetBinCenter(bin)))); generated->SetBinError(bin, tmp->GetBinError(tmp->FindBin(generated->GetBinCenter(bin)))); } @@ -1089,7 +1099,7 @@ TH1* CorrelationContainer::getTrackEfficiency(EfficiencyStep step1, EfficiencySt } //____________________________________________________________________ -TH1* CorrelationContainer::getEventEfficiency(EfficiencyStep step1, EfficiencyStep step2, Int_t axis1, Int_t axis2, Float_t ptTriggerMin, Float_t ptTriggerMax) +TH1* CorrelationContainer::getEventEfficiency(EfficiencyStep step1, EfficiencyStep step2, int axis1, int axis2, float ptTriggerMin, float ptTriggerMax) { // creates a event-level efficiency by dividing step2 by step1 // projected to axis1 and axis2 (optional if >= 0) @@ -1132,9 +1142,9 @@ void CorrelationContainer::weightHistogram(TH3* hist1, TH1* hist2) LOGF(fatal, "Inconsistent binning %d %d", hist1->GetNbinsZ(), hist2->GetNbinsX()); } - for (Int_t x = 1; x <= hist1->GetNbinsX(); x++) { - for (Int_t y = 1; y <= hist1->GetNbinsY(); y++) { - for (Int_t z = 1; z <= hist1->GetNbinsZ(); z++) { + for (int x = 1; x <= hist1->GetNbinsX(); x++) { + for (int y = 1; y <= hist1->GetNbinsY(); y++) { + for (int z = 1; z <= hist1->GetNbinsZ(); z++) { if (hist2->GetBinContent(z) > 0) { hist1->SetBinContent(x, y, z, hist1->GetBinContent(x, y, z) / hist2->GetBinContent(z)); hist1->SetBinError(x, y, z, hist1->GetBinError(x, y, z) / hist2->GetBinContent(z)); @@ -1148,7 +1158,7 @@ void CorrelationContainer::weightHistogram(TH3* hist1, TH1* hist2) } //____________________________________________________________________ -TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, const char* axis, Float_t leadPtMin, Float_t leadPtMax, Int_t weighting) +TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, const char* axis, float leadPtMin, float leadPtMax, int weighting) { // extracts the track-level bias (integrating out the multiplicity) between two steps (dividing step2 by step1) // done by weighting the track-level distribution with the number of events as function of leading pT @@ -1169,14 +1179,14 @@ TH1* CorrelationContainer::getBias(EfficiencyStep step1, EfficiencyStep step2, c resetBinLimits(mTriggerHist->getTHn(step2), 3); setBinLimits(tmp->getTHn(step2)); - TH1D* events1 = (TH1D*)mTriggerHist->getTHn(step1)->Projection(0); - TH3D* hist1 = (TH3D*)tmp->getTHn(step1)->Projection(0, tmp->getNVar() - 1, 2); + TH1D* events1 = mTriggerHist->getTHn(step1)->Projection(0); + TH3D* hist1 = tmp->getTHn(step1)->Projection(0, tmp->getNVar() - 1, 2); if (weighting == 0) { weightHistogram(hist1, events1); } - TH1D* events2 = (TH1D*)mTriggerHist->getTHn(step2)->Projection(0); - TH3D* hist2 = (TH3D*)tmp->getTHn(step2)->Projection(0, tmp->getNVar() - 1, 2); + TH1D* events2 = mTriggerHist->getTHn(step2)->Projection(0); + TH3D* hist2 = tmp->getTHn(step2)->Projection(0, tmp->getNVar() - 1, 2); if (weighting == 0) { weightHistogram(hist2, events2); } @@ -1252,7 +1262,7 @@ TH2D* CorrelationContainer::getTrackingEfficiencyCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingEfficiency(Int_t axis) +TH1D* CorrelationContainer::getTrackingEfficiency(int axis) { // extracts the tracking efficiency for primary particles // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1261,7 +1271,7 @@ TH1D* CorrelationContainer::getTrackingEfficiency(Int_t axis) } //____________________________________________________________________ -TH1D* CorrelationContainer::getFakeRate(Int_t axis) +TH1D* CorrelationContainer::getFakeRate(int axis) { return dynamic_cast(getTrackEfficiency(EfficiencyStep::RecoAll, EfficiencyStep::Fake, axis)); } @@ -1277,7 +1287,7 @@ TH2D* CorrelationContainer::getTrackingCorrection() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingCorrection(Int_t axis) +TH1D* CorrelationContainer::getTrackingCorrection(int axis) { // extracts the correction for tracking efficiency and secondaries // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1308,7 +1318,7 @@ TH2D* CorrelationContainer::getTrackingEfficiencyCorrectionCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingEfficiencyCorrection(Int_t axis) +TH1D* CorrelationContainer::getTrackingEfficiencyCorrection(int axis) { // extracts the correction for tracking efficiency // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1339,7 +1349,7 @@ TH2D* CorrelationContainer::getTrackingContaminationCentrality() } //____________________________________________________________________ -TH1D* CorrelationContainer::getTrackingContamination(Int_t axis) +TH1D* CorrelationContainer::getTrackingContamination(int axis) { // extracts the tracking contamination by secondaries // integrates over the regions and all other variables than pT (axis == 0) and eta (axis == 1) to increase the statistics @@ -1385,7 +1395,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) // copies the entries of this object's members from the object to this object // fills using the fill function and thus allows that the objects have different binning - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); THnBase* target = mPairHist->getTHn(step); THnBase* source = from->mPairHist->getTHn(step); @@ -1394,7 +1404,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) target->RebinnedAdd(source); } - for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { + for (int step = 0; step < mTriggerHist->getNSteps(); step++) { LOGF(info, "Ev: Copying step %d", step); THnBase* target = mTriggerHist->getTHn(step); THnBase* source = from->mTriggerHist->getTHn(step); @@ -1403,7 +1413,7 @@ void CorrelationContainer::deepCopy(CorrelationContainer* from) target->RebinnedAdd(source); } - for (Int_t step = 0; step < TMath::Min(mTrackHistEfficiency->getNSteps(), from->mTrackHistEfficiency->getNSteps()); step++) { + for (int step = 0; step < std::min(mTrackHistEfficiency->getNSteps(), from->mTrackHistEfficiency->getNSteps()); step++) { if (!from->mTrackHistEfficiency->getTHn(step)) { continue; } @@ -1421,7 +1431,7 @@ void CorrelationContainer::symmetrizepTBins() { // copy pt,a < pt,t bins to pt,a > pt,t (inverting deltaphi and delta eta as it should be) including symmetric bins - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { LOGF(info, "Copying step %d", step); THnBase* target = mPairHist->getTHn(step); if (target->GetEntries() == 0) { @@ -1429,47 +1439,47 @@ void CorrelationContainer::symmetrizepTBins() } // for symmetric bins - THnBase* source = (THnBase*)target->Clone(); + auto* source = dynamic_cast(target->Clone()); // axes: 0 delta eta; 1 pT,a; 2 pT,t; 3 centrality; 4 delta phi; 5 vtx-z - for (Int_t i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) { - for (Int_t i5 = 1; i5 <= target->GetAxis(5)->GetNbins(); i5++) { - for (Int_t i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) { - for (Int_t i2 = 1; i2 <= target->GetAxis(2)->GetNbins(); i2++) { + for (int i3 = 1; i3 <= target->GetAxis(3)->GetNbins(); i3++) { + for (int i5 = 1; i5 <= target->GetAxis(5)->GetNbins(); i5++) { + for (int i1 = 1; i1 <= target->GetAxis(1)->GetNbins(); i1++) { + for (int i2 = 1; i2 <= target->GetAxis(2)->GetNbins(); i2++) { // find source bin - Int_t binA = target->GetAxis(1)->FindBin(target->GetAxis(2)->GetBinCenter(i2)); - Int_t binT = target->GetAxis(2)->FindBin(target->GetAxis(1)->GetBinCenter(i1)); + int binA = target->GetAxis(1)->FindBin(target->GetAxis(2)->GetBinCenter(i2)); + int binT = target->GetAxis(2)->FindBin(target->GetAxis(1)->GetBinCenter(i1)); LOGF(info, "(%d %d) Copying from %d %d to %d %d", i3, i5, binA, binT, i1, i2); - for (Int_t i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) { - for (Int_t i4 = 1; i4 <= target->GetAxis(4)->GetNbins(); i4++) { - Int_t binEta = target->GetAxis(0)->FindBin(-target->GetAxis(0)->GetBinCenter(i0)); - Double_t phi = -target->GetAxis(4)->GetBinCenter(i4); + for (int i0 = 1; i0 <= target->GetAxis(0)->GetNbins(); i0++) { + for (int i4 = 1; i4 <= target->GetAxis(4)->GetNbins(); i4++) { + int binEta = target->GetAxis(0)->FindBin(-target->GetAxis(0)->GetBinCenter(i0)); + double phi = -target->GetAxis(4)->GetBinCenter(i4); if (phi < -PIHalf) { phi += TwoPI; } - Int_t binPhi = target->GetAxis(4)->FindBin(phi); + int binPhi = target->GetAxis(4)->FindBin(phi); - Int_t binSource[] = {binEta, binA, binT, i3, binPhi, i5}; - Int_t binTarget[] = {i0, i1, i2, i3, i4, i5}; + int binSource[] = {binEta, binA, binT, i3, binPhi, i5}; + int binTarget[] = {i0, i1, i2, i3, i4, i5}; - Double_t value = source->GetBinContent(binSource); - Double_t error = source->GetBinError(binSource); + double value = source->GetBinContent(binSource); + double error = source->GetBinError(binSource); if (error == 0) { continue; } - Double_t value2 = target->GetBinContent(binTarget); - Double_t error2 = target->GetBinError(binTarget); + double value2 = target->GetBinContent(binTarget); + double error2 = target->GetBinError(binTarget); - Double_t sum = value; - Double_t err = error; + double sum = value; + double err = error; if (error2 > 0) { sum = value + value2; - err = TMath::Sqrt(error * error + error2 * error2); + err = std::sqrt(error * error + error2 * error2); } // Printf(" Values: %f +- %f; %f +- %f --> %f +- %f", value, error, value2, error2, sum, err); @@ -1488,13 +1498,13 @@ void CorrelationContainer::symmetrizepTBins() } //____________________________________________________________________ -void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) +void CorrelationContainer::extendTrackingEfficiency(bool verbose) { // fits the tracking efficiency at high pT with a constant and fills all bins with this tracking efficiency - Float_t fitRangeBegin = 5.01; - Float_t fitRangeEnd = 14.99; - Float_t extendRangeBegin = 10.01; + float fitRangeBegin = 5.01; + float fitRangeEnd = 14.99; + float extendRangeBegin = 10.01; if (mTrackHistEfficiency->getNVar() == 3) { TH1* obj = getTrackingEfficiency(1); @@ -1506,7 +1516,7 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) obj->Fit("pol0", (verbose) ? "+" : "0+", "SAME", fitRangeBegin, fitRangeEnd); - Float_t trackingEff = obj->GetFunction("pol0")->GetParameter(0); + float trackingEff = obj->GetFunction("pol0")->GetParameter(0); obj = getTrackingContamination(1); @@ -1517,17 +1527,17 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) obj->Fit("pol0", (verbose) ? "+" : "0+", "SAME", fitRangeBegin, fitRangeEnd); - Float_t trackingCont = obj->GetFunction("pol0")->GetParameter(0); + float trackingCont = obj->GetFunction("pol0")->GetParameter(0); LOGF(info, "CorrelationContainer::extendTrackingEfficiency: Fitted efficiency between %f and %f and got %f tracking efficiency and %f tracking contamination correction. Extending from %f onwards (within %f < eta < %f)", fitRangeBegin, fitRangeEnd, trackingEff, trackingCont, extendRangeBegin, mEtaMin, mEtaMax); // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { - for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis + for (int x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (int y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { + for (int z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { - Int_t bins[3]; + int bins[3]; bins[0] = x; bins[1] = y; bins[2] = z; @@ -1540,28 +1550,28 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } } else if (mTrackHistEfficiency->getNVar() == 4) { // fit in centrality intervals of 20% for efficiency, one bin for contamination - Float_t* trackingEff = nullptr; - Float_t* trackingCont = nullptr; - Float_t centralityBins[] = {0, 10, 20, 40, 60, 100}; - Int_t nCentralityBins = 5; + float* trackingEff = nullptr; + float* trackingCont = nullptr; + float centralityBins[] = {0, 10, 20, 40, 60, 100}; + int nCentralityBins = 5; LOGF(info, "CorrelationContainer::extendTrackingEfficiency: Fitting efficiencies between %f and %f. Extending from %f onwards (within %f < eta < %f)", fitRangeBegin, fitRangeEnd, extendRangeBegin, mEtaMin, mEtaMax); // 0 = eff; 1 = cont - for (Int_t caseNo = 0; caseNo < 2; caseNo++) { - Float_t* target = nullptr; - Int_t centralityBinsLocal = nCentralityBins; + for (int caseNo = 0; caseNo < 2; caseNo++) { + float* target = nullptr; + int centralityBinsLocal = nCentralityBins; if (caseNo == 0) { - trackingEff = new Float_t[centralityBinsLocal]; + trackingEff = new float[centralityBinsLocal]; target = trackingEff; } else { centralityBinsLocal = 1; - trackingCont = new Float_t[centralityBinsLocal]; + trackingCont = new float[centralityBinsLocal]; target = trackingCont; } - for (Int_t i = 0; i < centralityBinsLocal; i++) { + for (int i = 0; i < centralityBinsLocal; i++) { if (centralityBinsLocal == 1) { setCentralityRange(centralityBins[0] + 0.1, centralityBins[nCentralityBins] - 0.1); } else { @@ -1572,7 +1582,7 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) new TCanvas; proj->DrawCopy(); } - if ((Int_t)proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd) == 0) { + if (static_cast(proj->Fit("pol0", (verbose) ? "+" : "Q0+", "SAME", fitRangeBegin, fitRangeEnd)) == 0) { target[i] = proj->GetFunction("pol0")->GetParameter(0); } else { target[i] = 0; @@ -1582,20 +1592,20 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } // extend for full pT range - for (Int_t x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { - for (Int_t y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { - for (Int_t z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis + for (int x = mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMin); x <= mTrackHistEfficiency->getTHn(0)->GetAxis(0)->FindBin(mEtaMax); x++) { + for (int y = mTrackHistEfficiency->getTHn(0)->GetAxis(1)->FindBin(extendRangeBegin); y <= mTrackHistEfficiency->getTHn(0)->GetAxis(1)->GetNbins(); y++) { + for (int z = 1; z <= mTrackHistEfficiency->getTHn(0)->GetAxis(2)->GetNbins(); z++) // particle type axis { - for (Int_t z2 = 1; z2 <= mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetNbins(); z2++) // centrality axis + for (int z2 = 1; z2 <= mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetNbins(); z2++) // centrality axis { - Int_t bins[4]; + int bins[4]; bins[0] = x; bins[1] = y; bins[2] = z; bins[3] = z2; - Int_t z2Bin = 0; + int z2Bin = 0; while (centralityBins[z2Bin + 1] < mTrackHistEfficiency->getTHn(0)->GetAxis(3)->GetBinCenter(z2)) { z2Bin++; } @@ -1622,11 +1632,11 @@ void CorrelationContainer::extendTrackingEfficiency(Bool_t verbose) } /* -void CorrelationContainer::Scale(Double_t factor) +void CorrelationContainer::Scale(double factor) { // scales all contained histograms by the given factor - for (Int_t i=0; i<4; i++) + for (int i=0; i<4; i++) if (mTrackHist[i]) mTrackHist[i]->Scale(factor); @@ -1638,15 +1648,15 @@ void CorrelationContainer::Reset() { // resets all contained histograms - for (Int_t step = 0; step < mPairHist->getNSteps(); step++) { + for (int step = 0; step < mPairHist->getNSteps(); step++) { mPairHist->getTHn(step)->Reset(); } - for (Int_t step = 0; step < mTriggerHist->getNSteps(); step++) { + for (int step = 0; step < mTriggerHist->getNSteps(); step++) { mTriggerHist->getTHn(step)->Reset(); } - for (Int_t step = 0; step < mTrackHistEfficiency->getNSteps(); step++) { + for (int step = 0; step < mTrackHistEfficiency->getNSteps(); step++) { mTrackHistEfficiency->getTHn(step)->Reset(); } } @@ -1658,7 +1668,7 @@ THnBase* CorrelationContainer::changeToThn(THnBase* sparse) return THn::CreateHn(Form("%s_thn", sparse->GetName()), sparse->GetTitle(), sparse); } -void CorrelationContainer::fillEvent(Float_t centrality, CFStep step) +void CorrelationContainer::fillEvent(float centrality, CFStep step) { // Fill per-event information mEventCount->Fill(step, centrality); diff --git a/PWGCF/Core/PWGCFCoreLinkDef.h b/PWGCF/Core/PWGCFCoreLinkDef.h index fcc73500be2..235b4a7d2b7 100644 --- a/PWGCF/Core/PWGCFCoreLinkDef.h +++ b/PWGCF/Core/PWGCFCoreLinkDef.h @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_CORE_PWGCFCORELINKDEF_H_ -#define PWGCF_CORE_PWGCFCORELINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -23,4 +23,3 @@ #pragma link C++ class o2::analysis::TrackSelectionPIDCfg + ; #pragma link C++ class o2::analysis::SimpleInclusiveCut + ; #pragma link C++ class CorrelationContainer + ; -#endif // PWGCF_CORE_PWGCFCORELINKDEF_H_ diff --git a/PWGCF/DataModel/CorrelationsDerived.h b/PWGCF/DataModel/CorrelationsDerived.h index 09f41d5ebfc..4401f9cb14d 100644 --- a/PWGCF/DataModel/CorrelationsDerived.h +++ b/PWGCF/DataModel/CorrelationsDerived.h @@ -28,9 +28,19 @@ DECLARE_SOA_TABLE(CFMcCollisions, "AOD", "CFMCCOLLISION", //! Reduced MC collisi mccollision::PosZ, cfmccollision::Multiplicity); using CFMcCollision = CFMcCollisions::iterator; +namespace cfmccollisionextra +{ +DECLARE_SOA_COLUMN(NMPI, nMPI, int); //! Number of multi-parton interactions from HepMC +} // namespace cfmccollisionextra +DECLARE_SOA_TABLE(CFMcCollisionExtras, "AOD", "CFMCCOLLEXTRA", //! Row-aligned extension of CFMcCollisions + cfmccollisionextra::NMPI); +using CFMcCollisionExtra = CFMcCollisionExtras::iterator; +using CFMcCollisionsWithExtra = soa::Join; +using CFMcCollisionWithExtra = CFMcCollisionsWithExtra::iterator; + namespace cfmcparticle { -DECLARE_SOA_INDEX_COLUMN(CFMcCollision, cfMcCollision); //! Index to reduced MC collision +DECLARE_SOA_INDEX_COLUMN(CFMcCollision, cfMcCollision); //! Index to reduced MC collision; o2-linter: disable=name/o2-column (preserve the established derived-table API) DECLARE_SOA_COLUMN(Pt, pt, float); //! pT (GeV/c) DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi angle @@ -48,31 +58,42 @@ using CFMcParticle = CFMcParticles::iterator; namespace cfmultiplicity { DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); -} +} // namespace cfmultiplicity DECLARE_SOA_TABLE(CFMultiplicities, "AOD", "CFMULTIPLICITY", cfmultiplicity::Multiplicity); using CFMultiplicity = CFMultiplicities::iterator; namespace cfcollision { -DECLARE_SOA_INDEX_COLUMN(CFMcCollision, cfMcCollision); //! Index to reduced MC collision -DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Centrality/multiplicity value +DECLARE_SOA_INDEX_COLUMN(CFMcCollision, cfMcCollision); //! Index to reduced MC collision; o2-linter: disable=name/o2-column (preserve the established derived-table API) +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Centrality/multiplicity value +DECLARE_SOA_COLUMN(BestRecoCollision, bestRecoCollision, bool); //! Whether this is the best reconstructed collision for the associated MC collision (largest number of contributors) } // namespace cfcollision DECLARE_SOA_TABLE(CFCollisions, "AOD", "CFCOLLISION", //! Reduced collision table o2::soa::Index<>, bc::RunNumber, collision::PosZ, cfcollision::Multiplicity, timestamp::Timestamp); DECLARE_SOA_TABLE(CFCollLabels, "AOD", "CFCOLLLABEL", //! Labels for reduced collision table - cfcollision::CFMcCollisionId); + cfcollision::CFMcCollisionId, cfcollision::BestRecoCollision); using CFCollision = CFCollisions::iterator; using CFCollLabel = CFCollLabels::iterator; using CFCollisionsWithLabel = soa::Join; using CFCollisionWithLabel = CFCollisionsWithLabel::iterator; +namespace cfcollisionextra +{ +DECLARE_SOA_COLUMN(MultiplicityCorrected, multiplicityCorrected, float); //! Efficiency-corrected track count +} // namespace cfcollisionextra +DECLARE_SOA_TABLE(CFCollisionsExtra, "AOD", "CFCOLLSEXTRA", //! Row-aligned extension of CFCollisions; filled only when multiplicity efficiency is configured + cfcollisionextra::MultiplicityCorrected); +using CFCollisionExtra = CFCollisionsExtra::iterator; +using CFCollisionsWithExtra = soa::Join; +using CFCollisionWithExtra = CFCollisionsWithExtra::iterator; + namespace cftrack { -DECLARE_SOA_INDEX_COLUMN(CFCollision, cfCollision); //! Index to collision -DECLARE_SOA_INDEX_COLUMN(CFMcParticle, cfMCParticle); //! Index to MC particle +DECLARE_SOA_INDEX_COLUMN(CFCollision, cfCollision); //! Index to collision; o2-linter: disable=name/o2-column (preserve the established derived-table API) +DECLARE_SOA_INDEX_COLUMN(CFMcParticle, cfMCParticle); //! Index to MC particle; o2-linter: disable=name/o2-column (preserve the established derived-table API) DECLARE_SOA_COLUMN(Pt, pt, float); //! pT (GeV/c) DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi angle @@ -104,7 +125,6 @@ enum MultiplicityEstimators : uint8_t { MultNTracksGlobal = 0x8, CentFT0M = 0x10, }; - inline constexpr uint32_t NMultiplicityEstimators = __builtin_ctz(CentFT0M) + 1; } // namespace cfmultset @@ -137,8 +157,8 @@ using CFMcParticleRef = CFMcParticleRefs::iterator; namespace cf2prongtrack { -DECLARE_SOA_INDEX_COLUMN_FULL(CFTrackProng0, cfTrackProng0, int, CFTracks, "_0"); //! Index to prong 1 CFTrack -DECLARE_SOA_INDEX_COLUMN_FULL(CFTrackProng1, cfTrackProng1, int, CFTracks, "_1"); //! Index to prong 2 CFTrack +DECLARE_SOA_INDEX_COLUMN_FULL(CFTrackProng0, cfTrackProng0, int, CFTracks, "_0"); //! Index to prong 1 CFTrack; o2-linter: disable=name/o2-column (preserve the established derived-table API) +DECLARE_SOA_INDEX_COLUMN_FULL(CFTrackProng1, cfTrackProng1, int, CFTracks, "_1"); //! Index to prong 2 CFTrack; o2-linter: disable=name/o2-column (preserve the established derived-table API) DECLARE_SOA_COLUMN(Pt, pt, float); //! pT (GeV/c) DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi angle @@ -191,8 +211,8 @@ using CF2ProngTrackml = CF2ProngTrackmls::iterator; namespace cf2prongmcpart { -DECLARE_SOA_INDEX_COLUMN_FULL(CFParticleDaugh0, cfParticleDaugh0, int, CFMcParticles, "_0"); //! Index to prong 1 CFMcParticle -DECLARE_SOA_INDEX_COLUMN_FULL(CFParticleDaugh1, cfParticleDaugh1, int, CFMcParticles, "_1"); //! Index to prong 2 CFMcParticle +DECLARE_SOA_INDEX_COLUMN_FULL(CFParticleDaugh0, cfParticleDaugh0, int, CFMcParticles, "_0"); //! Index to prong 1 CFMcParticle; o2-linter: disable=name/o2-column (preserve the established derived-table API) +DECLARE_SOA_INDEX_COLUMN_FULL(CFParticleDaugh1, cfParticleDaugh1, int, CFMcParticles, "_1"); //! Index to prong 2 CFMcParticle; o2-linter: disable=name/o2-column (preserve the established derived-table API) DECLARE_SOA_COLUMN(Decay, decay, uint8_t); //! Particle decay and flags DECLARE_SOA_DYNAMIC_COLUMN(McDecay, mcDecay, [](uint8_t decay) -> uint8_t { return decay & 0x3f; }); //! MC particle decay enum ParticleDecayFlags { diff --git a/PWGCF/DataModel/FemtoDerived.h b/PWGCF/DataModel/FemtoDerived.h index 4f15d00a342..cac62250bbe 100644 --- a/PWGCF/DataModel/FemtoDerived.h +++ b/PWGCF/DataModel/FemtoDerived.h @@ -245,6 +245,7 @@ DECLARE_SOA_COLUMN(Charge, charge, int8_t); //! Charge of c DECLARE_SOA_COLUMN(Prong0Id, prong0Id, int); //! Track id of charm hadron prong0 DECLARE_SOA_COLUMN(Prong1Id, prong1Id, int); //! Track id of charm hadron prong1 DECLARE_SOA_COLUMN(Prong2Id, prong2Id, int); //! Track id of charm hadron prong2 +DECLARE_SOA_COLUMN(CascId, cascId, int); //! Cascade id of the Ξ prong in Ξc → Ξππ candidates DECLARE_SOA_COLUMN(Prong0Pt, prong0Pt, float); //! Track pT of charm hadron prong0 DECLARE_SOA_COLUMN(Prong1Pt, prong1Pt, float); //! Track pT of charm hadron prong1 DECLARE_SOA_COLUMN(Prong2Pt, prong2Pt, float); //! Track pT of charm hadron prong2 @@ -258,7 +259,19 @@ DECLARE_SOA_COLUMN(CandidateSelFlag, candidateSelFlag, int); //! Selection o DECLARE_SOA_COLUMN(BDTBkg, bdtBkg, float); //! Background score using Boosted Decision Tree for charm hadron DECLARE_SOA_COLUMN(BDTPrompt, bdtPrompt, float); //! Prompt signal score using Boosted Decision Tree for charm hadron DECLARE_SOA_COLUMN(BDTFD, bdtFD, float); //! Feed-down score using Boosted Decision Tree for charm hadron -DECLARE_SOA_COLUMN(FlagMc, flagMc, int); //! To select MC particle among charm hadrons, { DplusToPiKPi = 1, LcToPKPi = 17, DsToKKPi = 6, XicToPKPi = 21, N3ProngD = 2ecays }; +DECLARE_SOA_COLUMN(CascBachelorTrackId, cascBachelorTrackId, int); //! Bachelor track ID from Ξ cascade (Ξc± → Ξππ) +DECLARE_SOA_COLUMN(CascPosTrackId, cascPosTrackId, int); //! Positive track ID from Λ in Ξ cascade (Ξc± → Ξππ) +DECLARE_SOA_COLUMN(CascNegTrackId, cascNegTrackId, int); //! Negative track ID from Λ in Ξ cascade (Ξc± → Ξππ) +DECLARE_SOA_COLUMN(CascBachelorPt, cascBachelorPt, float); //! pT of the bachelor track from the Xi cascade +DECLARE_SOA_COLUMN(CascBachelorPhi, cascBachelorPhi, float); //! phi of the bachelor track from the Xi cascade +DECLARE_SOA_COLUMN(CascBachelorEta, cascBachelorEta, float); //! eta of the bachelor track from the Xi cascade +DECLARE_SOA_COLUMN(CascPosPt, cascPosPt, float); //! pT of the positive Lambda daughter track from the Xi cascade +DECLARE_SOA_COLUMN(CascPosPhi, cascPosPhi, float); //! phi of the positive Lambda daughter track from the Xi cascade +DECLARE_SOA_COLUMN(CascPosEta, cascPosEta, float); //! eta of the positive Lambda daughter track from the Xi cascade +DECLARE_SOA_COLUMN(CascNegPt, cascNegPt, float); //! pT of the negative Lambda daughter track from the Xi cascade +DECLARE_SOA_COLUMN(CascNegPhi, cascNegPhi, float); //! phi of the negative Lambda daughter track from the Xi cascade +DECLARE_SOA_COLUMN(CascNegEta, cascNegEta, float); //! eta of the negative Lambda daughter track from the Xi cascade +DECLARE_SOA_COLUMN(FlagMc, flagMc, int); //! MC matching flag for the selected charm hadron decay channel DECLARE_SOA_COLUMN(OriginMcRec, originMcRec, int); //! flag for reconstruction level matching (1 for prompt, 2 for non-prompt) DECLARE_SOA_COLUMN(OriginMcGen, originMcGen, int); //! flag for generator level matching (1 for prompt, 2 for non-prompt) DECLARE_SOA_COLUMN(IsCandidateSwapped, isCandidateSwapped, int); //! swapping of the prongs order (0 for Lc -> pkpi, 1 for Lc -> pikp) @@ -270,12 +283,12 @@ DECLARE_SOA_COLUMN(KT, kT, float); //! kT distribu DECLARE_SOA_COLUMN(MT, mT, float); //! Transverse mass distribution DECLARE_SOA_COLUMN(CharmM, charmM, float); //! Charm hadron mass DECLARE_SOA_COLUMN(CharmDaughM, charmDaughM, float); //! Charm hadron daughter mass -DECLARE_SOA_COLUMN(CharmTrkM, charmtrkM, float); //! Charm hadron track mass +DECLARE_SOA_COLUMN(CharmTrkM, charmtrkM, float); //! Invariant-mass difference of the charm-track pair DECLARE_SOA_COLUMN(CharmPt, charmPt, float); //! Transverse momentum of charm hadron for result task DECLARE_SOA_COLUMN(CharmEta, charmEta, float); //! Eta of charm hadron for result task DECLARE_SOA_COLUMN(CharmPhi, charmPhi, float); //! Phi of charm hadron for result task DECLARE_SOA_COLUMN(Mult, mult, int); //! Charge particle multiplicity -DECLARE_SOA_COLUMN(MultPercentile, multPercentile, float); //! Multiplicity precentile +DECLARE_SOA_COLUMN(MultPercentile, multPercentile, float); //! Multiplicity percentile DECLARE_SOA_COLUMN(PairSign, pairSign, int8_t); //! Selection between like sign (1) and unlike sign pair (2) DECLARE_SOA_COLUMN(ProcessType, processType, int64_t); //! Selection between same-event (1), and mixed-event (2) DECLARE_SOA_DYNAMIC_COLUMN(M, m, //! @@ -388,6 +401,16 @@ DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, RecoDecayPtEtaPhi::pVector(pt2, eta2, phi2))); }); //! Eta distribution of charm hadron } // namespace fdhf_dstar +namespace fdhf_xic +{ +DECLARE_SOA_DYNAMIC_COLUMN(Y, y, //! + [](float pt0, float phi0, float eta0, float pt1, float phi1, float eta1, float pt2, float phi2, float eta2) -> float { return RecoDecay::y(RecoDecay::pVec( + RecoDecayPtEtaPhi::pVector(pt0, eta0, phi0), + RecoDecayPtEtaPhi::pVector(pt1, eta1, phi1), + RecoDecayPtEtaPhi::pVector(pt2, eta2, phi2)), + o2::constants::physics::MassXiCPlus); }); //! Rapidity distribution of Xic candidates +} // namespace fdhf_xic + DECLARE_SOA_TABLE(FDHfCand3Prong, "AOD", "FDHFCAND3PRONG", //! Table to store the derived data for charm 3prong candidates o2::soa::Index<>, femtodreamparticle::FDCollisionId, @@ -416,6 +439,49 @@ DECLARE_SOA_TABLE(FDHfCand3Prong, "AOD", "FDHFCAND3PRONG", //! Table to store th fdhf::Phi, fdhf::Pt); +DECLARE_SOA_TABLE(FDHfCand3ProngXic, "AOD", "FDHFXIC3PRONG", //! Table to store the derived data for Ξc → Ξππ candidates + o2::soa::Index<>, + femtodreamparticle::FDCollisionId, + fdhf::TimeStamp, + fdhf::Charge, + fdhf::CascId, + fdhf::Prong1Id, + fdhf::Prong2Id, + fdhf::CascBachelorTrackId, + fdhf::CascPosTrackId, + fdhf::CascNegTrackId, + fdhf::Prong0Pt, + fdhf::Prong1Pt, + fdhf::Prong2Pt, + fdhf::Prong0Eta, + fdhf::Prong1Eta, + fdhf::Prong2Eta, + fdhf::Prong0Phi, + fdhf::Prong1Phi, + fdhf::Prong2Phi, + fdhf::CandidateSelFlag, + fdhf::BDTBkg, + fdhf::BDTPrompt, + fdhf::BDTFD, + fdhf::M, + fdhf::P, + fdhf_xic::Y, + fdhf::Eta, + fdhf::Phi, + fdhf::Pt); + +DECLARE_SOA_TABLE(FDHfCand3ProngXicQa, "AOD", "FDHFXIC3PQA", //! QA extension table for Ξ daughters in Ξc → Ξππ candidates + o2::soa::Index<>, + fdhf::CascBachelorPt, + fdhf::CascBachelorPhi, + fdhf::CascBachelorEta, + fdhf::CascPosPt, + fdhf::CascPosPhi, + fdhf::CascPosEta, + fdhf::CascNegPt, + fdhf::CascNegPhi, + fdhf::CascNegEta); + DECLARE_SOA_TABLE(FDHfCand2Prong, "AOD", "FDHFCAND2PRONG", //! Table to store the derived data for charm 3prong candidates o2::soa::Index<>, femtodreamparticle::FDCollisionId, @@ -501,7 +567,9 @@ DECLARE_SOA_TABLE(FDHfCharm3Prong, "AOD", "FDHFCHARM3PRONG", //! table to store fdhf::Charge, fdhf::BDTBkg, fdhf::BDTPrompt, - fdhf::BDTFD); + fdhf::BDTFD, + fdhf::FlagMc, + fdhf::OriginMcRec); DECLARE_SOA_TABLE(FDHfCharm2Prong, "AOD", "FDHFCHARM2PRONG", //! table to store results for HF femtoscopy fdhf::GIndexCol, @@ -515,7 +583,9 @@ DECLARE_SOA_TABLE(FDHfCharm2Prong, "AOD", "FDHFCHARM2PRONG", //! table to store fdhf::Charge, fdhf::BDTBkg, fdhf::BDTPrompt, - fdhf::BDTFD); + fdhf::BDTFD, + fdhf::FlagMc, + fdhf::OriginMcRec); DECLARE_SOA_TABLE(FDHfCharmDstar, "AOD", "FDHFCHARMDSTAR", //! table to store results for HF femtoscopy fdhf::GIndexCol, @@ -531,7 +601,9 @@ DECLARE_SOA_TABLE(FDHfCharmDstar, "AOD", "FDHFCHARMDSTAR", //! table to store re fdhf::Charge, fdhf::BDTBkg, fdhf::BDTPrompt, - fdhf::BDTFD); + fdhf::BDTFD, + fdhf::FlagMc, + fdhf::OriginMcRec); DECLARE_SOA_TABLE(FDHfTrk, "AOD", "FDHFTRK", //! table to store results for HF femtoscopy fdhf::GIndexCol, @@ -671,6 +743,8 @@ enum ParticleOriginMCTruth { kSecondaryDaughterXistar0, //! Daughter from a Xi*^0 decay kSecondaryDaughterXistarMinus, //! Daughter from a Xi*^- decay kElse, //! none of the above; (NOTE: used to catch bugs. will be removed once MC usage is properly validated) + kSecondaryFromHF, //! Kaon from a charm- or beauty-hadron decay chain + kSecondaryFromLF, //! Kaon from a light-flavour decay without a heavy-flavour ancestor kNOriginMCTruthTypes }; @@ -681,8 +755,18 @@ static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes "_Material", "_NotPrimary", "_Fake", + "_WrongCollision", "_SecondaryDaughterLambda", - "_SecondaryDaughterSigmaPlus"}; + "_SecondaryDaughterSigmaPlus", + "_SecondaryDaughterSigma0", + "_SecondaryDaughterXiMinus", + "_SecondaryDaughterXi0", + "_SecondaryDaughterOmegaMinus", + "_SecondaryDaughterXistar0", + "_SecondaryDaughterXistarMinus", + "_Else", + "_SecondaryFromHF", + "_SecondaryFromLF"}; /// Distinguished between reconstructed and truth enum MCType { diff --git a/PWGCF/EbyEFluctuations/Tasks/CMakeLists.txt b/PWGCF/EbyEFluctuations/Tasks/CMakeLists.txt index a10cc46db09..2ca1ae99968 100644 --- a/PWGCF/EbyEFluctuations/Tasks/CMakeLists.txt +++ b/PWGCF/EbyEFluctuations/Tasks/CMakeLists.txt @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. o2physics_add_dpl_workflow(meanpt-fluctuations - SOURCES MeanptFluctuations.cxx + SOURCES meanptFluctuations.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) @@ -83,3 +83,14 @@ o2physics_add_dpl_workflow(net-prot-cumulants SOURCES netprotcumulants.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(strongly-intensive-corr + SOURCES stronglyIntensiveCorr.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(mult-and-pt-fluctuations + SOURCES multAndPtFluctuations.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + diff --git a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx index fca2ef50d8f..e34f44ecf46 100644 --- a/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/FactorialMomentsTask.cxx @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// -/// \brief This task is for Normalized Factorial Moments Analysis: PRC 85, 044914 (2012), nucl-ex:1411.6083 +/// \file FactorialMomentsTask.cxx +/// \brief This task is for Normalized Factorial Moments Analysis: Hwa and Yang, Phys. Rev. C 85, 044914 (2012) /// \author Salman Malik /// \author Balwan Singh @@ -29,30 +29,28 @@ #include #include #include +#include #include #include #include #include #include -#include +#include #include #include #include -#include #include #include -#include +#include #include -#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -TH1D* tmpFqErr[6][5][52]; struct FactorialMomentsTask { Configurable useITS{"useITS", false, "Select tracks with ITS"}; @@ -60,68 +58,52 @@ struct FactorialMomentsTask { Configurable useGlobal{"useGlobal", true, "Select global tracks"}; Configurable applyCheckPtForRec{"applyCheckPtForRec", false, "Apply checkpT for reconstructed tracks"}; Configurable applyCheckPtForMC{"applyCheckPtForMC", true, "Apply checkpT for MC-generated tracks"}; + Configurable smearPhi{"smearPhi", false, "Randomise track phi with a Gaussian of sigma=2pi before filling the eta-phi lattices"}; + Configurable evSelNoITSROFrameBorder{"evSelNoITSROFrameBorder", true, "ITSROFrame border event selection cut"}; + Configurable evSelNoTimeFrameBorder{"evSelNoTimeFrameBorder", true, "TimeFrame border event selection cut"}; Configurable centralEta{"centralEta", 0.9, "eta limit for tracks"}; Configurable numPt{"numPt", 5, "number of pT bins"}; Configurable ptMin{"ptMin", 0.2f, "lower pT cut"}; - Configurable dcaXY{"dcaXY", 2.4f, "DCA xy cut"}; - Configurable dcaZ{"dcaZ", 2.0f, "DCA z cut"}; - Configurable mintPCCls{"mintPCCls", 70.0f, "minimum number of TPC clusters"}; + Configurable dcaXY{"dcaXY", 0.1f, "DCA xy cut"}; + Configurable dcaZ{"dcaZ", 1.0f, "DCA z cut"}; + Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 2.5f, "Maximum TPCchi2NCl"}; + Configurable maxITSChi2NCl{"maxITSChi2NCl", 40.0f, "Maximum ITSchi2NCl"}; + Configurable minTPCNCls{"minTPCNCls", 70.0f, "minimum number of TPC clusters"}; Configurable> centLimits{"centLimits", {0, 5}, "centrality min and max"}; Configurable> vertexXYZ{"vertexXYZ", {0.3f, 0.4f, 10.0f}, "vertex cuts"}; Configurable> ptCuts{"ptCuts", {0.2f, 2.0f}, "pT cuts"}; - Configurable isApplySameBunchPileup{"isApplySameBunchPileup", true, "Enable SameBunchPileup cut"}; - Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", true, "Enable GoodZvtxFT0vsPV cut"}; - Configurable isApplyVertexITSTPC{"isApplyVertexITSTPC", true, "Enable VertexITSTPC cut"}; - Configurable isApplyVertexTOFmatched{"isApplyVertexTOFmatched", true, "Enable VertexTOFmatched cut"}; - Configurable isApplyVertexTRDmatched{"isApplyVertexTRDmatched", true, "Enable VertexTRDmatched cut"}; - Configurable isApplyExtraCorrCut{"isApplyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; - Configurable isApplyExtraPhiCut{"isApplyExtraPhiCut", false, "Enable extra phi cut"}; + Configurable evSelNoSameBunchPileup{"evSelNoSameBunchPileup", true, "Enable SameBunchPileup cut"}; + Configurable evSelGoodZvtxFT0vsPV{"evSelGoodZvtxFT0vsPV", true, "Enable GoodZvtxFT0vsPV cut"}; + Configurable evSelGoodITSLayersAll{"evSelGoodITSLayersAll", true, "Remove time interval with dead ITS zone"}; + Configurable evSelVertexITSTPC{"evSelVertexITSTPC", true, "Enable VertexITSTPC cut"}; + Configurable evSelVertexTOFmatched{"evSelVertexTOFmatched", true, "Enable VertexTOFmatched cut"}; + Configurable evSelVertexTRDmatched{"evSelVertexTRDmatched", true, "Enable VertexTRDmatched cut"}; + Configurable applyExtraCorrCut{"applyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; + Configurable applyExtraPhiCut{"applyExtraPhiCut", false, "Enable extra phi cut"}; Configurable includeGlobalTracks{"includeGlobalTracks", false, "Enable Global Tracks"}; Configurable includeTPCTracks{"includeTPCTracks", false, "TPC Tracks"}; Configurable includeITSTracks{"includeITSTracks", false, "ITS Tracks"}; - Configurable samplesize{"samplesize", 100, "Sample size"}; + Configurable nSlots{"nSlots", 1250, "Number of hash slots for merge-safe subsampling (slot = splitmix64(globalBC) % nSlots); must be identical for every job"}; Configurable useMC{"useMC", false, "Use MC information"}; + Configurable useGlobalTrack{"useGlobalTrack", true, "Require global track in filter"}; + Configurable minITSClusters{"minITSClusters", 6, "Minimum Number of ITS cluster"}; + Configurable minTPCClusters{"minTPCClusters", 80, "Minimum Number of TPC cluster"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; + Configurable minTPCCrossedRowsOverFindableCls{"minTPCCrossedRowsOverFindableCls", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; Configurable reduceOutput{"reduceOutput", 0, "Suppress info level output (0 = all output, 1 = per collision, 2 = none)"}; - Filter filterTracks = (nabs(aod::track::eta) < centralEta) && (aod::track::pt >= ptMin) && (nabs(aod::track::dcaXY) < dcaXY) && (nabs(aod::track::dcaZ) < dcaZ); + Filter filterTracks = (nabs(aod::track::eta) < centralEta) && (aod::track::pt >= ptMin); Filter filterCollisions = (nabs(aod::collision::posZ) < vertexXYZ.value[2]) && (nabs(aod::collision::posX) < vertexXYZ.value[0]) && (nabs(aod::collision::posY) < vertexXYZ.value[1]); - Service pdg; + Service pdg{}; // Histograms - HistogramRegistry histos1{ - "histos1", - { - {"hRecoPtBefore", "Reco pT before cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hGenPtBefore", "Gen pT before cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hRecoPtAfter", "Reco pT after cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hGenPtAfter", "Gen pT after cuts;pt (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 20.0}}}}, - {"hRecoEtaBefore", "Reco #eta before cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"mCentMCImpactFull", "Impact Parameter (MC All Events);Impact Parameter b [fm];Counts", {HistType::kTH1F, {{200, 0.0, 50.0}}}}, - {"mCentMCImpact005", "Impact Parameter (MC 0–5%);Impact Parameter b [fm];Counts", {HistType::kTH1F, {{200, 0.0, 20.0}}}}, - {"hGenEtaBefore", "Gen #eta before cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hRecoEtaAfter", "Reco #eta after cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hGenEtaAfter", "Gen #eta after cuts;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, - {"hRecoPhiBefore", "Reco #phi before cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hGenPhiBefore", "Gen #phi before cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hRecoPhiAfter", "Reco #phi after cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"hGenCharge", "Gen particle charge;Charge;Counts", {HistType::kTH1F, {{13, -6.5, 6.5}}}}, - {"hGenPhiAfter", "Gen #phi after cuts;#phi;Counts", {HistType::kTH1F, {{100, 0, 6.3}}}}, - {"mChargeDist", "MC particle charge;Charge;Counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, - {"mDiffPhi", "dphi between selected MC particles;dphi;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mPrimariesPerEvent", "Primary MC particles per event;N_{primaries};Counts", {HistType::kTH1I, {{20000, 0, 20000}}}}, - {"mDiffPt", "dpt between selected MC particles;dpt (GeV/c);Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mDiffEta", "deta between selected MC particles;deta;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffPhi", "dphi between selected MC particles;dphi;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffPt", "dpt between selected MC particles;dpt (GeV/c);Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - {"mADiffEta", "deta between selected MC particles;deta;Counts", {HistType::kTH1F, {{2000, -0.01, 0.01}}}}, - }, - }; - HistogramRegistry histos{ "histos", { + + {"mtpcsignalvspt", "tpcsignal vs #pt", {HistType::kTH2F, {{900, 0, 10}, {1400, 0, 1400}}}}, {"mChargeBefore", "Charge before MC cuts;charge;entries", {HistType::kTH1F, {{7, -3.5, 3.5}}}}, {"mChargeAfter", "Charge after MC cuts;charge;entries", {HistType::kTH1F, {{7, -3.5, 3.5}}}}, {"mCollID", "collisionID", {HistType::kTH1I, {{1000, -10000, 10000}}}}, - {"mCentFT0M", "centFT0C_0A", {HistType::kTH1F, {{100, 0, 100}}}}, + {"mCentFT0M", "centFT0M / Run2 V0M", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFV0A", "centFV0A", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFT0A", "centFT0A", {HistType::kTH1F, {{100, 0, 100}}}}, {"mCentFT0C", "centFT0C", {HistType::kTH1F, {{100, 0, 100}}}}, @@ -131,7 +113,6 @@ struct FactorialMomentsTask { {"mEta", "#eta", {HistType::kTH1F, {{1000, -2, 2}}}}, {"mPt", "#pt", {HistType::kTH1F, {{1000, -0.01, 50}}}}, {"mPhi", "#phi", {HistType::kTH1F, {{100, 0, o2::constants::math::TwoPI}}}}, - {"mEvents", "events", {HistType::kTH1D, {{5, -0.5, 4.5}}}}, {"mNFindableClsTPC", "findable TPC clusters;findable clusters", {HistType::kTH1F, {{100, 0, 200}}}}, {"mNClsTPC", "number of clusters TPC; nClusters TPC", {HistType::kTH1F, {{100, 0, 200}}}}, {"mNClsITS", "number of clusters ITS; nClusters ITS", {HistType::kTH1F, {{100, 0, 10}}}}, @@ -139,10 +120,8 @@ struct FactorialMomentsTask { {"mChi2ITS", "chi2 ITS", {HistType::kTH1F, {{100, 0, 10}}}}, {"mChi2TRD", "chi2 TRD", {HistType::kTH1F, {{100, 0, 100}}}}, {"mDCAxy", "DCA xy", {HistType::kTH1F, {{500, -0.8, 0.8}}}}, - {"mDCAx", "DCA z", {HistType::kTH1F, {{500, -2.0, 2.0}}}}, + {"mDCAz", "DCA z", {HistType::kTH1F, {{500, -2.0, 2.0}}}}, {"mDCAxyPt", "DCA xy vs #pt;#pt;DCAxy", {HistType::kTH2F, {{100, 0, 20}, {500, -0.5, 0.5}}}}, - {"mDCAxyPtbcut", "DCA xy vs #pt;#pt;DCAxycut", {HistType::kTH2F, {{100, 0, 20}, {500, -0.5, 0.5}}}}, - {"mDCAzPtbcut", "DCA z vs #pt;#pt;DCAzcut", {HistType::kTH2F, {{100, 0, 20}, {100, -2.0, 2.0}}}}, {"mDCAzPt", "DCA z vs #pt;#pt;DCAz", {HistType::kTH2F, {{100, 0, 20}, {100, -2.0, 2.0}}}}, {"mNSharedClsTPC", "shared clusters in TPC", {HistType::kTH1F, {{100, 0, 10}}}}, {"mCrossedRowsTPC", "crossedrows in TPC", {HistType::kTH1F, {{100, 0, 200}}}}, @@ -153,185 +132,351 @@ struct FactorialMomentsTask { }, OutputObjHandlingPolicy::AnalysisObject, true}; + float collisionZ = 0.f; + static constexpr double kDcaXY0 = 0.0105; + static constexpr double kDcaXY1 = 0.035; + static constexpr double kDcaXY2 = 1.1; static const int nBins = 52; - double kMinCharge = 1e-6; + static constexpr double kMinCharge = 1e-6; static const int nfqOrder = 6; - int countSamples = 0; - int testc1 = 0, testc2 = 0, testc3 = 0; - std::array binningM; - std::array countTracks{0, 0, 0, 0, 0}; - std::array, 5>, 6> fqEvent; - std::array, 5>, 6> fqEventSampled; - std::array, 5> binConEvent; - std::array, 5>, 6> binConEventSampled; - std::array, 5>, 6> errorFq = {{{{{0, 0, 0, 0, 0}}}}}; + static constexpr int kEvAll = 1; + static constexpr int kEvSel8 = 2; + static constexpr int kEvITSROFrame = 3; + static constexpr int kEvTimeFrame = 4; + static constexpr int kEvSameBunch = 5; + static constexpr int kEvGoodITS = 6; + static constexpr int kEvGoodZvtx = 7; + static constexpr int kEvVertexITSTPC = 8; + static constexpr int kEvCent = 9; + static constexpr int kEvAccepted = 10; + static constexpr int kNEventSteps = 10; + std::array binningM{}; + std::array countTracks{}; + std::array, 5>, nfqOrder> fqEvent{}; + std::array, 5> binConEvent{}; std::vector> mHistArrReset; - std::vector> mHistArrQA; + std::vector> mHistEtaQA; + std::vector> mHistPtQA; + std::vector> mHistPhiQA; + std::vector> mHistMultQA; + std::vector> mHistArrEff; std::vector> mFqBinFinal; std::vector> mBinConFinal; - std::vector> mFqBinFinalSampled; - std::vector> mBinConFinalSampled; - std::vector> mFqError; - // max number of bins restricted to 5 - static constexpr std::array - mbinNames{"bin1/", "bin2/", "bin3/", "bin4/", "bin5/"}; + // merge-safe slot output: additive per-slot sums, hadd-exact + std::shared_ptr mSlotNev; + std::vector> mSlotFq; + std::vector> mSlotOcc; + uint64_t mEventGlobalBC = 0; + void init(o2::framework::InitContext&) { - // NOTE: check to make number of pt and the vector consistent - if (numPt != static_cast(ptCuts.value.size()) / 2) { - for (int i = numPt; i < static_cast(ptCuts.value.size() / 2); i++) { - ptCuts.value[2 * i] = 0; - ptCuts.value[2 * i + 1] = 0; + if (numPt.value < 1 || numPt.value > 5) { + LOG(fatal) << "numPt must be between 1 and 5, got " << numPt.value; + } + if (static_cast(ptCuts.value.size()) < 2 * numPt.value) { + LOG(fatal) << "ptCuts needs " << 2 * numPt.value << " values for numPt=" << numPt.value + << ", got " << ptCuts.value.size(); + } + if (nSlots.value < 1 || nSlots.value > 1000000) { + LOG(fatal) << "nSlots must be in [1, 1000000], got " << nSlots.value; + } + if (centLimits.value.size() != 2 || vertexXYZ.value.size() != 3) { + LOG(fatal) << "centLimits needs 2 and vertexXYZ needs 3 values"; + } + if (applyCheckPtForRec.value == applyCheckPtForMC.value) { + LOG(fatal) << "exactly one of applyCheckPtForRec / applyCheckPtForMC must be enabled"; + } + TString cfg = Form("FMtask centLimits=%d,%d numPt=%d centralEta=%g ptMin=%g dcaXY=%g dcaZ=%g useMC=%d nfqOrder=%d smearPhi=%d nSlots=%d", + centLimits.value[0], centLimits.value[1], numPt.value, centralEta.value, + ptMin.value, dcaXY.value, dcaZ.value, useMC.value ? 1 : 0, nfqOrder, smearPhi.value ? 1 : 0, nSlots.value); + cfg += Form(" vertexXYZ=%g,%g,%g", vertexXYZ.value[0], vertexXYZ.value[1], vertexXYZ.value[2]); + cfg += " ptCuts="; + for (int i = 0; i < static_cast(ptCuts.value.size()); i++) { + cfg += Form("%s%g", i > 0 ? "," : "", ptCuts.value[i]); + } + histos.add("metaConfig", cfg.Data(), HistType::kTH1D, {{1, 0.5, 1.5}}); + auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{kNEventSteps, 0.5, kNEventSteps + 0.5}})); + const std::array evLabels = {"all", "sel8", "kNoITSROFrameBorder", "kNoTimeFrameBorder", "kNoSameBunchPileup", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "kIsVertexITSTPC", "centrality", "accepted"}; + for (int iStep = 0; iStep < kNEventSteps; ++iStep) { + mEventSelected->GetXaxis()->SetBinLabel(iStep + 1, evLabels[iStep]); + } + // merge-safe slot output: additive per-slot sums, hadd-exact (see slotpool.C) + const AxisSpec slotAxis{nSlots.value, -0.5, nSlots.value - 0.5, ""}; + const AxisSpec slotMAxis{nBins, -0.5, nBins - 0.5, ""}; + mSlotNev = std::get>(histos.add("slots/nEv", "events per slot;slot;events", HistType::kTH1F, {slotAxis})); + // bin1 = merged jobs, bin2 = sum of their nSlots: mismatch witness (hadd keeps the first axis silently) + auto slotParams = std::get>(histos.add("slots/params", "slot campaign parameters;jobs;entries", HistType::kTH1D, {{2, 0.5, 2.5}})); + slotParams->SetBinContent(1, 1.0); + slotParams->SetBinContent(2, static_cast(nSlots.value)); + for (int iPt = 0; iPt < numPt; ++iPt) { + mSlotOcc.push_back(std::get>(histos.add(Form("slots/sumOcc_pt%d", iPt + 1), + Form("sum of occupancy per slot for bin %.2f-%.2f;slot;M", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), + HistType::kTH2F, {slotAxis, slotMAxis}))); + for (int iq = 0; iq < nfqOrder; ++iq) { + mSlotFq.push_back(std::get>(histos.add(Form("slots/sumFq_pt%d_o%d", iPt + 1, iq + 2), + Form("sum of f_%d per slot for bin %.2f-%.2f;slot;M", iq + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), + HistType::kTH2F, {slotAxis, slotMAxis}))); } } - AxisSpec axisPt[5] = {{100, -0.01, 3 * ptCuts.value[1], ""}, {100, -0.01, 3 * ptCuts.value[3], ""}, {100, -0.01, 3 * ptCuts.value[5], ""}, {100, -0.01, 3 * ptCuts.value[7], ""}, {100, -0.01, 3 * ptCuts.value[9], ""}}; // pT axis - auto mEventSelected = std::get>(histos.add("mEventSelected", "eventSelected", HistType::kTH1D, {{8, 0.5, 8.5}})); - mEventSelected->GetXaxis()->SetBinLabel(1, "all"); - mEventSelected->GetXaxis()->SetBinLabel(2, "sel8"); - mEventSelected->GetXaxis()->SetBinLabel(3, "sameBunchPileup"); - mEventSelected->GetXaxis()->SetBinLabel(4, "goodZvtxFT0vsPV"); - mEventSelected->GetXaxis()->SetBinLabel(5, "vertexITSTPC"); - mEventSelected->GetXaxis()->SetBinLabel(6, "centrality"); - mEventSelected->GetXaxis()->SetBinLabel(7, "final"); - auto mTrackSelected = std::get>(histos.add( - "mTrackSelected", "Track Selection Steps", HistType::kTH1D, {{5, 0.5, 5.5}})); - mTrackSelected->GetXaxis()->SetBinLabel(1, "all"); - mTrackSelected->GetXaxis()->SetBinLabel(2, "charge"); - mTrackSelected->GetXaxis()->SetBinLabel(3, "PIDs"); - mTrackSelected->GetXaxis()->SetBinLabel(4, "Pids exclude pions"); - mTrackSelected->GetXaxis()->SetBinLabel(5, "Pids exclude pions + kions"); - if (useMC) { - auto mMcTrackSelected = std::get>(histos.add("mMcTrackSelected", "mcTrackSelection", HistType::kTH1D, {{5, 0.5, 5.5}})); - } for (int iM = 0; iM < nBins; ++iM) { binningM[iM] = 2 * (iM + 2); } for (int iPt = 0; iPt < numPt; ++iPt) { - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mEta", iPt + 1), Form("#eta for bin %.2f-%.2f;#eta", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, -2, 2}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mPt", iPt + 1), Form("pT for bin %.2f-%.2f;pT", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {axisPt[iPt]}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mPhi", iPt + 1), Form("#phi for bin %.2f-%.2f;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, o2::constants::math::TwoPI}}))); - mHistArrQA.push_back(std::get>(histos.add(Form("bin%i/mMultiplicity", iPt + 1), Form("Multiplicity for bin %.2f-%.2f;Multiplicity", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, 15000}}))); + const AxisSpec ptAxis{100, -0.01, 3 * ptCuts.value[2 * iPt + 1], ""}; + mHistArrEff.push_back(std::get>(histos.add(Form("bin%i/m3DVtxZetaPhi", iPt + 1), Form("#eta #phi #vtxz for bin %.2f-%.2f;vz;#eta;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH3F, {{20, -10, 10}, {16, -0.8, +0.8}, {100, 0., o2::constants::math::TwoPI}}))); + mHistEtaQA.push_back(std::get>(histos.add(Form("bin%i/mEta", iPt + 1), Form("#eta for bin %.2f-%.2f;#eta", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, -2, 2}}))); + mHistPtQA.push_back(std::get>(histos.add(Form("bin%i/mPt", iPt + 1), Form("pT for bin %.2f-%.2f;pT", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {ptAxis}))); + mHistPhiQA.push_back(std::get>(histos.add(Form("bin%i/mPhi", iPt + 1), Form("#phi for bin %.2f-%.2f;#phi", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, o2::constants::math::TwoPI}}))); + mHistMultQA.push_back(std::get>(histos.add(Form("bin%i/mMultiplicity", iPt + 1), Form("Multiplicity for bin %.2f-%.2f;Multiplicity", ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{1000, 0, 15000}}))); for (int iM = 0; iM < nBins; ++iM) { auto mHistsR = std::get>(histos.add(Form("bin%i/Reset/mEtaPhi%i", iPt + 1, iM), Form("#eta#phi_%i for bin %.2f-%.2f;#eta;#phi", iM, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH2F, {{binningM[iM], -0.8, 0.8}, {binningM[iM], 0, o2::constants::math::TwoPI}})); mHistArrReset.push_back(mHistsR); - for (int iq = 0; iq < nfqOrder; ++iq) { - tmpFqErr[iq][iPt][iM] = new TH1D(Form("tmpFqErr%i%i%i", iq, iPt, iM), Form("tmpFqErr%i%i%i", iq, iPt, iM), 100, 0, 10); - } } for (int i = 0; i < nfqOrder; ++i) { auto mHistFq = std::get>(histos.add(Form("mFinalFq%i_bin%i", i + 2, iPt + 1), Form("Final F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); mFqBinFinal.push_back(mHistFq); auto mHistAv = std::get>(histos.add(Form("mFinalAvBin%i_bin%i", i + 2, iPt + 1), Form("Final AvBin_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); mBinConFinal.push_back(mHistAv); - auto mHistFqSampled = std::get>(histos.add(Form("mFinalFq%iSampled_bin%i", i + 2, iPt + 1), Form("Final F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mFqBinFinalSampled.push_back(mHistFqSampled); - auto mHistAvSampled = std::get>(histos.add(Form("mFinalAvBin%iSampled_bin%i", i + 2, iPt + 1), Form("Final AvBin_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mBinConFinalSampled.push_back(mHistAvSampled); - - auto mHistError = std::get>(histos.add(Form("mFqError%i_bin%i", i + 2, iPt + 1), Form("Error for F_%i for bin %.2f-%.2f;M", i + 2, ptCuts.value[2 * iPt], ptCuts.value[2 * iPt + 1]), HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}})); - mFqError.push_back(mHistError); } } } template void checkpT(const T& track) { - for (int iPt = 0; iPt < numPt; ++iPt) { + for (auto iPt = 0; iPt < numPt; ++iPt) { if (track.pt() > ptCuts.value[2 * iPt] && track.pt() < ptCuts.value[2 * iPt + 1]) { float iphi = track.phi(); - iphi = gRandom->Gaus(iphi, o2::constants::math::TwoPI); - iphi = RecoDecay::constrainAngle(iphi); - - mHistArrQA[iPt * 4]->Fill(track.eta()); - mHistArrQA[iPt * 4 + 1]->Fill(track.pt()); - mHistArrQA[iPt * 4 + 2]->Fill(track.phi()); + if (smearPhi) { + iphi = gRandom->Gaus(iphi, o2::constants::math::TwoPI); + iphi = RecoDecay::constrainAngle(iphi, 0.); + } + mHistArrEff[iPt]->Fill(collisionZ, track.eta(), iphi); + mHistEtaQA[iPt]->Fill(track.eta()); + mHistPtQA[iPt]->Fill(track.pt()); + mHistPhiQA[iPt]->Fill(iphi); countTracks[iPt]++; - - for (int iM = 0; iM < nBins; ++iM) { - mHistArrReset[iPt * nBins + iM]->Fill(track.eta(), track.phi()); + for (auto iM = 0; iM < nBins; ++iM) { + mHistArrReset[iPt * nBins + iM]->Fill(track.eta(), iphi); } } } } - - void calculateMoments(std::vector> hist) + // splitmix64: deterministic stateless hash of the global BC + static uint64_t slotOf(uint64_t x) + { + x += 0x9e3779b97f4a7c15ULL; + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + return x ^ (x >> 31); + } + // additive fold of the current accepted event into its slot (never Fill with weights: no fSumw2, hadd-exact) + void accumulateSlot() + { + const int slot = static_cast(slotOf(mEventGlobalBC) % static_cast(nSlots.value)); + mSlotNev->SetBinContent(slot + 1, mSlotNev->GetBinContent(slot + 1) + 1.); + for (int iPt = 0; iPt < numPt; ++iPt) { + auto* occ = mSlotOcc[iPt].get(); + for (int iM = 0; iM < nBins; ++iM) { + occ->SetBinContent(slot + 1, iM + 1, occ->GetBinContent(slot + 1, iM + 1) + binConEvent[iPt][iM]); + for (int iq = 0; iq < nfqOrder; ++iq) { + auto* fq = mSlotFq[iPt * nfqOrder + iq].get(); + fq->SetBinContent(slot + 1, iM + 1, fq->GetBinContent(slot + 1, iM + 1) + fqEvent[iq][iPt][iM]); + } + } + } + } + void calculateMoments(const std::vector>& hist) { - double binContent = 0; - countSamples++; - bool compSample = kFALSE; - if (countSamples == samplesize) { - compSample = kTRUE; - countSamples = 0; - } - // Calculate the normalized factorial moments + // normalized factorial moments per accepted event, per pT bin and M for (int iPt = 0; iPt < numPt; ++iPt) { for (int iM = 0; iM < nBins; ++iM) { - binContent = 0; - double sumfqBin[6] = {0}; + double binContent = 0; + std::array sumfqBin{0.}; + const double m2 = static_cast(binningM[iM]) * static_cast(binningM[iM]); + const auto& histM = hist[iPt * nBins + iM]; - for (int iEta = 1; iEta <= hist[iPt * nBins + iM]->GetNbinsX(); ++iEta) { - for (int iPhi = 1; iPhi <= hist[iPt * nBins + iM]->GetNbinsY(); ++iPhi) { - double binconVal = 0; - binconVal = hist[iPt * nBins + iM]->GetBinContent(iEta, iPhi); + for (int iEta = 1; iEta <= histM->GetNbinsX(); ++iEta) { + for (int iPhi = 1; iPhi <= histM->GetNbinsY(); ++iPhi) { + const double binconVal = histM->GetBinContent(iEta, iPhi); binContent += binconVal; for (int iq = 0; iq < nfqOrder; ++iq) { double fqBin = 0; if (binconVal >= iq + 2) { - fqBin = TMath::Factorial(binconVal) / (TMath::Factorial(binconVal - (iq + 2))); - } - if (std::isnan(fqBin)) { - break; + const int nOcc = static_cast(binconVal); + fqBin = 1.0; + for (int k = 0; k < iq + 2; ++k) { + fqBin *= (nOcc - k); + } } sumfqBin[iq] += fqBin; } } } - binConEvent[iPt][iM] = binContent / (std::pow(binningM[iM], 2)); + binConEvent[iPt][iM] = binContent / m2; for (int iq = 0; iq < nfqOrder; ++iq) { - if (sumfqBin[iq] > 0) { - fqEvent[iq][iPt][iM] = sumfqBin[iq] / (std::pow(binningM[iM], 2)); - fqEventSampled[iq][iPt][iM] += fqEvent[iq][iPt][iM]; - } - binConEventSampled[iq][iPt][iM] += binConEvent[iPt][iM]; - mFqBinFinal[iPt * 6 + iq]->Fill(iM, fqEvent[iq][iPt][iM]); - mBinConFinal[iPt * 6 + iq]->Fill(iM, binConEvent[iPt][iM]); - if (compSample) { - mBinConFinalSampled[iPt * 6 + iq]->Fill(iM, binConEventSampled[iq][iPt][iM] / samplesize); + fqEvent[iq][iPt][iM] = sumfqBin[iq] / m2; + mFqBinFinal[iPt * nfqOrder + iq]->Fill(iM, fqEvent[iq][iPt][iM]); + mBinConFinal[iPt * nfqOrder + iq]->Fill(iM, binConEvent[iPt][iM]); + } + } + } + accumulateSlot(); + } - double tmp = (fqEventSampled[iq][iPt][iM] / (samplesize)) / (std::pow(binConEventSampled[iq][iPt][iM] / (samplesize), (iq + 2))); - mFqBinFinalSampled[iPt * 6 + iq]->Fill(iM, tmp); - tmpFqErr[iq][iPt][iM]->Fill(tmp); - errorFq[iq][iPt][iM] += std::pow(fqEventSampled[iq][iPt][iM] / (samplesize), 2); - mFqError[iPt * 6 + iq]->SetBinContent(iM + 1, 0); - mFqError[iPt * 6 + iq]->Fill(iM, tmpFqErr[iq][iPt][iM]->GetStdDev()); + template + bool passDcaCut(const T& track) const + { + return std::fabs(track.dcaXY()) < (kDcaXY0 + kDcaXY1 / std::pow(track.pt(), kDcaXY2)); + } - fqEventSampled[iq][iPt][iM] = 0; - binConEventSampled[iq][iPt][iM] = 0; - } - } // end of loop over order of F - } // end of loop over M bins - } // end of loop over pT bins + template + void fillTrackQA(const T& track) + { + histos.fill(HIST("mCollID"), track.collisionId()); + histos.fill(HIST("mEta"), track.eta()); + histos.fill(HIST("mPt"), track.pt()); + histos.fill(HIST("mPhi"), track.phi()); + histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); + histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); + histos.fill(HIST("mNClsITS"), track.itsNCls()); + histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); + histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); + histos.fill(HIST("mChi2TRD"), track.trdChi2()); + histos.fill(HIST("mDCAxy"), track.dcaXY()); + histos.fill(HIST("mDCAz"), track.dcaZ()); + histos.fill(HIST("mtpcsignalvspt"), track.pt(), track.tpcSignal()); + histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); + histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); + histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); + histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); + histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); + histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); + histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); + const double crossedRows = track.tpcNClsCrossedRows(); + histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), crossedRows > 0. ? track.tpcFractionSharedCls() / crossedRows : 0.); } - // write a template that takes coll and tracks from processRun3 and processMCRec - using TracksFMs = soa::Filtered>; - Preslice perMcCollision = aod::mcparticle::mcCollisionId; - void processRun3(soa::Filtered>::iterator const& coll, TracksFMs const& tracks) + template + void fillMcQA(const T& mcParts) + { + for (auto const& mc : mcParts) { + int pdgCode = mc.pdgCode(); + auto pdgInfo = pdg->GetParticle(pdgCode); + if (!pdgInfo) { + continue; + } + double charge = pdgInfo->Charge(); + double physCharge = charge / 3.0; + histos.fill(HIST("mChargeBefore"), physCharge); + if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { + histos.fill(HIST("mChargeAfter"), physCharge); + histos.fill(HIST("mEta"), mc.eta()); + histos.fill(HIST("mPt"), mc.pt()); + histos.fill(HIST("mPhi"), mc.phi()); + if (applyCheckPtForMC && !applyCheckPtForRec) { + checkpT(mc); + } + } + } + } + + template + bool selectEventRun3(const T& coll) { - // selection of events + histos.fill(HIST("mEventSelected"), kEvAll); if (!coll.sel8()) { - return; + return false; } - if (isApplySameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return; + histos.fill(HIST("mEventSelected"), kEvSel8); + if (evSelNoITSROFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { + return false; } - if (isApplyGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return; + histos.fill(HIST("mEventSelected"), kEvITSROFrame); + if (evSelNoTimeFrameBorder && !(coll.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { + return false; } - if (isApplyVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - return; + histos.fill(HIST("mEventSelected"), kEvTimeFrame); + if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvSameBunch); + if (evSelGoodITSLayersAll && !(coll.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvGoodITS); + if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvGoodZvtx); + if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { + return false; + } + collisionZ = coll.posZ(); + histos.fill(HIST("mEventSelected"), kEvCent); + histos.fill(HIST("mEventSelected"), kEvAccepted); + return true; + } + + template + bool selectEventMCRec(const T& coll) + { + histos.fill(HIST("mEventSelected"), kEvAll); + if (!coll.sel8()) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvSel8); + if (evSelNoSameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvSameBunch); + if (evSelGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; } + histos.fill(HIST("mEventSelected"), kEvGoodZvtx); + if (evSelVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + histos.fill(HIST("mEventSelected"), kEvVertexITSTPC); if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { + return false; + } + collisionZ = coll.posZ(); + histos.fill(HIST("mEventSelected"), kEvCent); + histos.fill(HIST("mEventSelected"), kEvAccepted); + return true; + } + + void beginEvent() + { + for (int iPt = 0; iPt < numPt; ++iPt) { + if (countTracks[iPt] == 0) { + continue; + } + for (int iM = 0; iM < nBins; ++iM) { + mHistArrReset[iPt * nBins + iM]->Reset(); + } + } + countTracks.fill(0); + fqEvent = {}; + binConEvent = {}; + } + + void endEvent() + { + for (int iPt = 0; iPt < numPt; ++iPt) { + if (countTracks[iPt] > 0) { + mHistMultQA[iPt]->Fill(countTracks[iPt]); + } + } + calculateMoments(mHistArrReset); + } + + using TracksFMs = soa::Filtered>; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + void processRun3(soa::Filtered>::iterator const& coll, TracksFMs const& tracks, aod::BCs const&) + { + mEventGlobalBC = coll.bc_as().globalBC(); + if (!selectEventRun3(coll)) { return; } histos.fill(HIST("mVertexX"), coll.posX()); @@ -341,146 +486,63 @@ struct FactorialMomentsTask { histos.fill(HIST("mCentFV0A"), coll.centFV0A()); histos.fill(HIST("mCentFT0A"), coll.centFT0A()); histos.fill(HIST("mCentFT0C"), coll.centFT0C()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { - if (useITS && !track.hasITS()) + if (useITS && !track.hasITS()) { + continue; + } + if (useTPC && !track.hasTPC()) { continue; - if (useTPC && !track.hasTPC()) + } + if (useGlobal && !track.isGlobalTrack()) { continue; - if (useGlobal && !track.isGlobalTrack()) + } + if (!passDcaCut(track)) { continue; - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mDCAx"), track.dcaZ()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); - checkpT(track); - } - - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); } + fillTrackQA(track); + checkpT(track); } - calculateMoments(mHistArrReset); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processRun3, "main process function", false); using CollisionCandidateMCRec = soa::Join; using TracksMc = soa::Filtered>; - void processMCRec(soa::Filtered::iterator const& coll, TracksMc const& colltracks, aod::McParticles const& mcParticles, aod::McCollisions const&) + void processMCRec(soa::Filtered::iterator const& coll, TracksMc const& colltracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCs const&) { + mEventGlobalBC = coll.bc_as().globalBC(); if (!coll.has_mcCollision()) { return; } - histos.fill(HIST("mEventSelected"), 0); - if (!coll.sel8()) { - return; - } - histos.fill(HIST("mEventSelected"), 1); - if (isApplySameBunchPileup && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (!selectEventMCRec(coll)) { return; } - histos.fill(HIST("mEventSelected"), 2); - if (isApplyGoodZvtxFT0vsPV && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return; - } - histos.fill(HIST("mEventSelected"), 3); - if (isApplyVertexITSTPC && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - return; - } - histos.fill(HIST("mEventSelected"), 4); - if (coll.centFT0C() < centLimits.value[0] || coll.centFT0C() > centLimits.value[1]) { - return; - } - histos.fill(HIST("mEventSelected"), 5); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0C"), coll.centFT0C()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : colltracks) { - if (useITS && !track.hasITS()) - continue; - if (useTPC && !track.hasTPC()) + if (useITS && !track.hasITS()) { continue; - if (useGlobal && !track.isGlobalTrack()) + } + if (useTPC && !track.hasTPC()) { continue; - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mDCAx"), track.dcaZ()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); - if (applyCheckPtForRec && !applyCheckPtForMC) { - checkpT(track); } - } - auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); - for (auto const& mc : mcParts) { - int pdgCode = mc.pdgCode(); - auto pdgInfo = pdg->GetParticle(pdgCode); - if (!pdgInfo) { + if (useGlobal && !track.isGlobalTrack()) { continue; } - double charge = pdgInfo->Charge(); - double physCharge = charge / 3.0; - histos.fill(HIST("mChargeBefore"), physCharge); - if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { - histos.fill(HIST("mChargeAfter"), physCharge); - histos.fill(HIST("mEta"), mc.eta()); - histos.fill(HIST("mPt"), mc.pt()); - histos.fill(HIST("mPhi"), mc.phi()); - if (applyCheckPtForMC && !applyCheckPtForRec) { - checkpT(mc); - } + if (!passDcaCut(track)) { + continue; } - } - for (auto iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); + fillTrackQA(track); + if (applyCheckPtForRec && !applyCheckPtForMC) { + checkpT(track); } } - histos.fill(HIST("mEventSelected"), 6); - calculateMoments(mHistArrReset); + auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); + fillMcQA(mcParts); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processMCRec, "main process function", false); using EventSelectionrun2 = soa::Join; @@ -495,132 +557,64 @@ struct FactorialMomentsTask { BCsWithRun2Info const&) { auto bc = coll.bc_as(); - if (!(bc.eventCuts() & BIT(aod::Run2EventCuts::kAliEventCutsAccepted))) { + mEventGlobalBC = bc.globalBC(); + if (!(static_cast(bc.eventCuts() & BIT(aod::Run2EventCuts::kAliEventCutsAccepted)))) { return; } if (coll.centRun2V0M() < centLimits.value[0] || coll.centRun2V0M() > centLimits.value[1]) { return; } + collisionZ = coll.posZ(); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0M"), coll.centRun2V0M()); - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { double recoCharge = (track.sign() != 0) ? track.sign() : 0.; if (std::abs(track.eta()) < centralEta && track.isGlobalTrack() && std::abs(recoCharge) >= kMinCharge) { - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); + fillTrackQA(track); if (applyCheckPtForRec && !applyCheckPtForMC) { checkpT(track); } } } auto mcParts = mcParticles.sliceBy(perMcCollision, coll.mcCollision().globalIndex()); - for (auto const& mc : mcParts) { - int pdgCode = mc.pdgCode(); - auto pdgInfo = pdg->GetParticle(pdgCode); - if (!pdgInfo) { - continue; - } - double charge = pdgInfo->Charge(); - double physCharge = charge / 3.0; - histos.fill(HIST("mChargeBefore"), physCharge); - if (mc.isPhysicalPrimary() && std::abs(mc.eta()) < centralEta && std::abs(physCharge) >= kMinCharge) { - histos.fill(HIST("mChargeAfter"), physCharge); - histos.fill(HIST("mEta"), mc.eta()); - histos.fill(HIST("mPt"), mc.pt()); - histos.fill(HIST("mPhi"), mc.phi()); - if (applyCheckPtForMC && !applyCheckPtForRec) { - checkpT(mc); - } - } - } - - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } - } - - calculateMoments(mHistArrReset); + fillMcQA(mcParts); + endEvent(); } - PROCESS_SWITCH(FactorialMomentsTask, processMcRun2, "process MC Run2", true); - void processRun2(soa::Filtered>::iterator const& coll, TracksFMs const& tracks) + PROCESS_SWITCH(FactorialMomentsTask, processMcRun2, "process MC Run2", false); + void processRun2(soa::Filtered>::iterator const& coll, TracksFMs const& tracks, aod::BCs const&) { + mEventGlobalBC = coll.bc_as().globalBC(); if ((!coll.alias_bit(kINT7)) || (!coll.sel7())) { return; } if (coll.centRun2V0M() < centLimits.value[0] || coll.centRun2V0M() > centLimits.value[1]) { return; } + collisionZ = coll.posZ(); histos.fill(HIST("mVertexX"), coll.posX()); histos.fill(HIST("mVertexY"), coll.posY()); histos.fill(HIST("mVertexZ"), coll.posZ()); histos.fill(HIST("mCentFT0M"), coll.centRun2V0M()); - - for (auto const& h : mHistArrReset) { - h->Reset(); - } - countTracks = {0, 0, 0, 0, 0}; - fqEvent = {{{{{0, 0, 0, 0, 0, 0}}}}}; - binConEvent = {{{0, 0, 0, 0, 0}}}; + beginEvent(); for (auto const& track : tracks) { - if ((track.pt() < ptMin) || (!track.isGlobalTrack()) || (track.tpcNClsFindable() < mintPCCls)) { + if ((track.pt() < ptMin) || (!track.isGlobalTrack()) || (track.tpcNClsFindable() < minTPCNCls)) { continue; } - histos.fill(HIST("mCollID"), track.collisionId()); - histos.fill(HIST("mEta"), track.eta()); - histos.fill(HIST("mPt"), track.pt()); - histos.fill(HIST("mPhi"), track.phi()); - histos.fill(HIST("mNFindableClsTPC"), track.tpcNClsFindable()); - histos.fill(HIST("mNClsTPC"), track.tpcNClsFound()); - histos.fill(HIST("mNClsITS"), track.itsNCls()); - histos.fill(HIST("mChi2TPC"), track.tpcChi2NCl()); - histos.fill(HIST("mChi2ITS"), track.itsChi2NCl()); - histos.fill(HIST("mChi2TRD"), track.trdChi2()); - histos.fill(HIST("mDCAxy"), track.dcaXY()); - histos.fill(HIST("mDCAx"), track.dcaZ()); - histos.fill(HIST("mDCAxyPt"), track.pt(), track.dcaXY()); - histos.fill(HIST("mDCAzPt"), track.pt(), track.dcaZ()); - histos.fill(HIST("mNSharedClsTPC"), track.tpcNClsShared()); - histos.fill(HIST("mCrossedRowsTPC"), track.tpcNClsCrossedRows()); - histos.fill(HIST("mNFinClsminusCRows"), track.tpcNClsFindableMinusCrossedRows()); - histos.fill(HIST("mNFractionShClsTPC"), track.tpcFractionSharedCls()); - histos.fill(HIST("mSharedClsvsPt"), track.pt(), track.tpcNClsShared()); - histos.fill(HIST("mSharedClsProbvsPt"), track.pt(), track.tpcFractionSharedCls() / track.tpcNClsCrossedRows()); + fillTrackQA(track); checkpT(track); } - for (int iPt = 0; iPt < numPt; ++iPt) { - if (countTracks[iPt] > 0) { - mHistArrQA[iPt * 4 + 3]->Fill(countTracks[iPt]); - } - } - // Calculate the normalized factorial moments - calculateMoments(mHistArrReset); + endEvent(); } PROCESS_SWITCH(FactorialMomentsTask, processRun2, "for RUN2", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) + +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; } diff --git a/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx b/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx index d9e1f132bfa..4f7c7de7d4c 100644 --- a/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/NetProtonCumulants.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -69,7 +70,7 @@ struct NetProtonCumulants_Table_QA { // Filter command*********** Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < 0.8f) && (aod::track::pt > cfgCutPtLower) && (aod::track::pt < 5.0f) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutTpcChi2NCl); + Filter trackFilter = (nabs(aod::track::eta) < 0.8f) && (aod::track::pt > cfgCutPtLower) && (aod::track::pt < 5.0f) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutTpcChi2NCl); // Connect to ccdb Service ccdb; @@ -126,7 +127,7 @@ struct NetProtonCumulants_Table_QA { //! centrality cut if (cent > 0.0f && cent < 90.0f) { - for (auto track : inputTracks) { //! Loop over tracks + for (const auto& track : inputTracks) { //! Loop over tracks histos.fill(HIST("hPtAll"), track.pt()); histos.fill(HIST("hEtaAll"), track.eta()); histos.fill(HIST("hPhiAll"), track.phi()); diff --git a/PWGCF/EbyEFluctuations/Tasks/RobustFluctuationObservables.cxx b/PWGCF/EbyEFluctuations/Tasks/RobustFluctuationObservables.cxx index ef3bc2bfeec..c1bc5143b58 100644 --- a/PWGCF/EbyEFluctuations/Tasks/RobustFluctuationObservables.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/RobustFluctuationObservables.cxx @@ -2309,7 +2309,7 @@ struct RobustFluctuationObservables { } // end of processRobustFluctuationObservables() // shortcut function to fill 2D histograms - void fillHistForThisCut(string cutName, int multNTracksPV, int multTrk, int nTracksGlobalAccepted, double multT0A, double multT0C, double multV0A, double /*t0cCentr*/, int bc) + void fillHistForThisCut(const string& cutName, int multNTracksPV, int multTrk, int nTracksGlobalAccepted, double multT0A, double multT0C, double multV0A, double /*t0cCentr*/, int bc) { // registry.get(HIST("eta"))->Fill(track.eta()); // arrPointers[histId][cutId]->Fill(xval, yval, weight); @@ -2348,7 +2348,7 @@ struct RobustFluctuationObservables { // } } - void fillPtHistos(string strTrackType, float pt, int charge, float w, bool noTF, bool noROF) + void fillPtHistos(const string& strTrackType, float pt, int charge, float w, bool noTF, bool noROF) { fillPtHistosThisCut(strTrackType, "allBC", pt, charge, w); @@ -2369,7 +2369,7 @@ struct RobustFluctuationObservables { fillPtHistosThisCut(strTrackType, "noTFandROFborder", pt, charge, w); } - void fillPtHistosThisCut(string strTrackType, string strEvSelType, float pt, int charge, float w) + void fillPtHistosThisCut(const string& strTrackType, const string& strEvSelType, float pt, int charge, float w) { string strPre = strTrackType + "/" + strEvSelType; diff --git a/PWGCF/EbyEFluctuations/Tasks/eventMeanPtId.cxx b/PWGCF/EbyEFluctuations/Tasks/eventMeanPtId.cxx index 205ec98bf88..ff63af24227 100644 --- a/PWGCF/EbyEFluctuations/Tasks/eventMeanPtId.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/eventMeanPtId.cxx @@ -185,7 +185,7 @@ struct EventMeanPtId { ccdb->setLocalObjectValidityChecking(); // ccdb->setCreatedNotAfter(ccdbNoLaterThan.value); // LOGF(info, "Getting object %s", ccdbPath.value.data()); - TList* lst = ccdb->getForTimeStamp(cfgPathCCDB.value, -1); + TList* lst = ccdb->getForTimeStamp(cfgPathCCDB.value, 1); ptHistogramAllchargeRec = reinterpret_cast(lst->FindObject("hPtEta_rec")); } std::vector ptBinning = {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0}; diff --git a/PWGCF/EbyEFluctuations/Tasks/kaonIsospinFluctuations.cxx b/PWGCF/EbyEFluctuations/Tasks/kaonIsospinFluctuations.cxx index 0f3a0a3c0d7..14ff6c33833 100644 --- a/PWGCF/EbyEFluctuations/Tasks/kaonIsospinFluctuations.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/kaonIsospinFluctuations.cxx @@ -1016,7 +1016,7 @@ struct KaonIsospinFluctuations { //_______________________________Identification Funtions Depending on the tpcInnerParam _______________________________ // tpc Selections template - bool selPionTPCInnerParam(T track) + bool selPionTPCInnerParam(const T& track) { if (vetoIdOthersTPC(track)) { if (0.05 <= track.tpcInnerParam() && track.tpcInnerParam() < 0.70 && std::abs(track.tpcNSigmaPi()) < cfgPiNSigmaTPCLowP) { @@ -1030,7 +1030,7 @@ struct KaonIsospinFluctuations { } template - bool selKaonTPCInnerParam(T track) + bool selKaonTPCInnerParam(const T& track) { if (vetoIdOthersTPC(track)) { if (0.05 <= track.tpcInnerParam() && track.tpcInnerParam() < 0.70 && std::abs(track.tpcNSigmaKa()) < cfgKaNSigmaTPCLowP) { @@ -1044,7 +1044,7 @@ struct KaonIsospinFluctuations { } template - bool selProtonTPCInnerParam(T track) + bool selProtonTPCInnerParam(const T& track) { if (vetoIdOthersTPC(track)) { if (0.05 <= track.tpcInnerParam() && track.tpcInnerParam() < 1.60 && std::abs(track.tpcNSigmaPr()) < cfgPrNSigmaTPCLowP) { @@ -1058,7 +1058,7 @@ struct KaonIsospinFluctuations { } template - bool selDeuteronTPCInnerParam(T track) + bool selDeuteronTPCInnerParam(const T& track) { if (vetoIdOthersTPC(track)) { if (0.05 <= track.tpcInnerParam() && track.tpcInnerParam() < 1.80 && std::abs(track.tpcNSigmaDe()) < 3.0) { @@ -1072,7 +1072,7 @@ struct KaonIsospinFluctuations { } template - bool selElectronTPCInnerParam(T track) + bool selElectronTPCInnerParam(const T& track) { if (track.tpcNSigmaEl() < 3.0 && track.tpcNSigmaPi() > 3.0 && track.tpcNSigmaKa() > 3.0 && track.tpcNSigmaPr() > 3.0 && track.tpcNSigmaDe() > 3.0) { return true; @@ -1084,7 +1084,7 @@ struct KaonIsospinFluctuations { // TOF Selections // Pion template - bool selPionTOF(T track) + bool selPionTOF(const T& track) { if (vetoIdOthersTOF(track)) { if (track.p() <= 0.75 && std::abs(track.tpcNSigmaPi()) < cfgPiNSigmaTPCLowP && std::abs(track.tofNSigmaPi()) < cfgPiNSigmaTOFLowP) { @@ -1099,7 +1099,7 @@ struct KaonIsospinFluctuations { // Kaon template - bool selKaonTOF(T track) + bool selKaonTOF(const T& track) { if (vetoIdOthersTOF(track)) { if (track.p() <= 0.75 && std::abs(track.tpcNSigmaKa()) < cfgKaNSigmaTPCLowP && std::abs(track.tofNSigmaKa()) < cfgKaNSigmaTOFLowP) { @@ -1119,7 +1119,7 @@ struct KaonIsospinFluctuations { // Proton template - bool selProtonTOF(T track) + bool selProtonTOF(const T& track) { if (vetoIdOthersTOF(track)) { if (track.p() <= 1.30 && std::abs(track.tpcNSigmaPr()) < cfgPrNSigmaTPCLowP && std::abs(track.tofNSigmaPr()) < cfgPrNSigmaTOFLowP) { @@ -1140,7 +1140,7 @@ struct KaonIsospinFluctuations { // Deuteron template - bool selDeuteronTOF(T track) + bool selDeuteronTOF(const T& track) { if (vetoIdOthersTOF(track)) { if (track.p() <= 3.10 && std::abs(track.tpcNSigmaDe()) < 3.0 && std::abs(track.tofNSigmaDe()) < 3.0) { @@ -1156,7 +1156,7 @@ struct KaonIsospinFluctuations { // Electron template - bool selElectronTOF(T track) + bool selElectronTOF(const T& track) { if ((std::pow(track.tpcNSigmaEl(), 2) + std::pow(track.tofNSigmaEl(), 2)) < 9.00 && vetoIdOthersTOF(track)) { return true; @@ -1168,7 +1168,7 @@ struct KaonIsospinFluctuations { //______________________________Identification Functions________________________________________________________________ // Pion template - bool selPion(T track, int& IdMethod) + bool selPion(const T& track, int& IdMethod) { if (cfgDoPdependentId) { return selPiPdependent(track, IdMethod); @@ -1187,7 +1187,7 @@ struct KaonIsospinFluctuations { // Kaon template - bool selKaon(T track, int& IdMethod) + bool selKaon(const T& track, int& IdMethod) { if (cfgDoPdependentId) { return selKaPdependent(track, IdMethod); @@ -1206,7 +1206,7 @@ struct KaonIsospinFluctuations { // Proton template - bool selProton(T track, int& IdMethod) + bool selProton(const T& track, int& IdMethod) { if (cfgDoPdependentId) { return selPrPdependent(track, IdMethod); @@ -1225,7 +1225,7 @@ struct KaonIsospinFluctuations { // Deuteron template - bool selDeuteron(T track, int& IdMethod) + bool selDeuteron(const T& track, int& IdMethod) { if (cfgDoPdependentId) { return false; @@ -1244,7 +1244,7 @@ struct KaonIsospinFluctuations { // Electron template - bool selElectron(T track, int& IdMethod) + bool selElectron(const T& track, int& IdMethod) { if (cfgDoPdependentId) { return false; @@ -1310,7 +1310,7 @@ struct KaonIsospinFluctuations { } template - bool selK0s(T v0) + bool selK0s(const T& v0) { if (k0sSelCut.cfgK0sMLow < v0.mK0Short() && v0.mK0Short() < k0sSelCut.cfgK0sMHigh && k0sSelCut.cfgK0sLowPt < v0.pt() && v0.pt() < k0sSelCut.cfgK0sHighPt && @@ -1323,7 +1323,7 @@ struct KaonIsospinFluctuations { } template - void findRepeatedEntries(std::vector ParticleList, T hist) + void findRepeatedEntries(std::vector ParticleList, const T& hist) { for (uint ii = 0; ii < ParticleList.size(); ii++) { int nCommonCount = 0; // checking the repeat number of track @@ -1441,7 +1441,7 @@ struct KaonIsospinFluctuations { } template - void fillTrackQA(T track) + void fillTrackQA(const T& track) { // FullTrack recoTracks.fill(HIST(HistRegDire[Mode]) + HIST("h01_p"), track.p()); diff --git a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx index e5d0739e8d3..3605e90661d 100644 --- a/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/meanPtFlucId.cxx @@ -38,8 +38,15 @@ #include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include @@ -52,65 +59,78 @@ using namespace std; struct MeanPtFlucId { Configurable nPBins{"nPBins", 300, ""}; - Configurable nPartBins{"nPartBins", 100, ""}; Configurable nPhiBins{"nPhiBins", 100, ""}; + Configurable nPtBins{"nPtBins", 50, "Number of pT bins"}; Configurable cfgCutPtMax{"cfgCutPtMax", 2.0, "maximum pT"}; Configurable cfgCutPtMin{"cfgCutPtMin", 0.2, "minimum pT"}; Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut"}; - Configurable cfgCutRap{"cfgCutRap", 0.5, "Rapidity Cut"}; - Configurable cfgCutDcaZ{"cfgCutDcaZ", 0.15, "DCAz cut"}; + Configurable cfgDcaPtDepOffset{"cfgDcaPtDepOffset", 0.004, "Offset of the pT-dependent DCA cut (cm)"}; + Configurable cfgDcaPtDepScale{"cfgDcaPtDepScale", 0.013, "1/pT coefficient of the pT-dependent DCA cut (cm GeV/c)"}; Configurable cfgCutPosZ{"cfgCutPosZ", 7.0, "cut for vertex Z"}; Configurable cfgPosZ{"cfgPosZ", true, "Position Z"}; Configurable cfgCutNSig2{"cfgCutNSig2", 2.0, "nSigma cut: 2"}; Configurable cfgCutNSig3{"cfgCutNSig3", 3.0, "nSigma cut: 3"}; Configurable cfgCutNSig5{"cfgCutNSig5", 5.0, "nSigma cut: 5"}; - Configurable cfgSelCutNSigPi{"cfgSelCutNSigPi", 2.0, "nSigma cut for pion selection"}; - Configurable cfgSelCutNSigKa{"cfgSelCutNSigKa", 3.0, "nSigma cut for kaon selection"}; - Configurable cfgSelCutNSigPr{"cfgSelCutNSigPr", 2.0, "nSigma cut for proton selection"}; - Configurable cfgRejCutNSigPi{"cfgRejCutNSigPi", 3.0, "nSigma cut for rejection of other particles while selecting pion"}; + Configurable cfgCutNSigTpcTof{"cfgCutNSigTpcTof", 3.0, "nSigma TPC cut when TPC + TOF "}; + Configurable cfgSelCutNSigTpcPi{"cfgSelCutNSigTpcPi", 3.0, "nSigma TPC cut for pion selection"}; + Configurable cfgSelCutNSigTpcKa{"cfgSelCutNSigTpcKa", 3.0, "nSigma TPC cut for kaon selection"}; + Configurable cfgSelCutNSigTpcPr{"cfgSelCutNSigTpcPr", 3.0, "nSigma TPC cut for proton selection"}; + Configurable cfgSelCutNSigTofPi{"cfgSelCutNSigTofPi", 3.0, "nSigma TOF cut for pion selection"}; + Configurable cfgSelCutNSigTofKa{"cfgSelCutNSigTofKa", 3.0, "nSigma TOF cut for kaon selection"}; + Configurable cfgSelCutNSigTofPr{"cfgSelCutNSigTofPr", 3.0, "nSigma TOF cut for proton selection"}; + // Signed TOF nSigma offsets: corrected = raw - shift. Zero preserves the original PID. + Configurable cfgTofShiftPi{"cfgTofShiftPi", 0.f, "Pion TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftKa{"cfgTofShiftKa", 0.f, "Kaon TOF nSigma offset to subtract (not a time shift in ps)"}; + Configurable cfgTofShiftPr{"cfgTofShiftPr", 0.f, "Proton TOF nSigma offset to subtract (not a time shift in ps)"}; Configurable cfgCutPiPtMin{"cfgCutPiPtMin", 0.2, "Minimum pion p_{T} cut"}; Configurable cfgCutKaPtMin{"cfgCutKaPtMin", 0.3, "Minimum kaon p_{T} cut"}; Configurable cfgCutPrPtMin{"cfgCutPrPtMin", 0.5, "Minimum proton p_{T} cut"}; - Configurable cfgCutPiThrsldP{"cfgCutPiThrsldP", 0.6, "Threshold p cut pion"}; - Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.6, "Threshold p cut kaon"}; - Configurable cfgCutPrThrsldP{"cfgCutPrThrsldP", 1.0, "Threshold p cut proton "}; + Configurable cfgCutPiThrsldP{"cfgCutPiThrsldP", 0.7, "Momentum threshold for requiring pion TOF PID"}; + Configurable cfgCutKaThrsldP{"cfgCutKaThrsldP", 0.55, "Momentum threshold for requiring kaon TOF PID"}; + Configurable cfgCutPrThrsldP{"cfgCutPrThrsldP", 0.8, "Momentum threshold for requiring proton TOF PID"}; + Configurable cfgP0U{"cfgP0U", 6.36269, "p_{0} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP1U{"cfgP1U", 2.22108, "p_{1} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP2U{"cfgP2U", -0.0118451, "p_{2} for upper cut for N_{TPC} vs N{sim}"}; + Configurable cfgP0L{"cfgP0L", 23.8213, "p_{0} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgP1L{"cfgP1L", -0.402328, "p_{1} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgP2L{"cfgP2L", 0.0128423, "p_{2} for lower cut for N_{TPC} vs N{sim}"}; + Configurable cfgP0corr{"cfgP0corr", 0.0001, "p_{0} for corrected N_{TPC} "}; + Configurable cfgP1corr{"cfgP1corr", 0.0001, "p_{1} for corrected N_{TPC} "}; + Configurable cfgP2corr{"cfgP2corr", 0.0001, "p_{2} for corrected N_{TPC} "}; Configurable cfgSel8{"cfgSel8", true, "Sel8 trigger"}; - Configurable cfgMinWeight{"cfgMinWeight", 1e-6, "Minimum weight for efficiency correction"}; + Configurable cfgCentFT0C{"cfgCentFT0C", false, "Use FT0C for centrality instead of FT0M"}; Configurable cfgNoSameBunchPileup{"cfgNoSameBunchPileup", true, "kNoSameBunchPileup"}; Configurable cfgIsVertexITSTPC{"cfgIsVertexITSTPC", true, "kIsVertexITSTPC"}; + Configurable cfgLightIonCuts{"cfgLightIonCuts", false, "Light Ion Cuts"}; + Configurable cfgIsGoodITSLayersAll{"cfgIsGoodITSLayersAll", true, "kIsGoodITSLayersAll"}; + Configurable cfgIsGoodZvtxFT0vsPV{"cfgIsGoodZvtxFT0vsPV", true, "kIsGoodZvtxFT0vsPV"}; Configurable cfgRejTrk{"cfgRejTrk", true, "Rejected Tracks"}; - Configurable cfgLoadEff{"cfgLoadEff", true, "Load efficiency"}; - Configurable cfgCorrection{"cfgCorrection", true, "Correction"}; - Configurable cfgWeightPtCh{"cfgWeightPtCh", true, "Efficiency correction (pT) for charged particles"}; - Configurable cfgWeightPtId{"cfgWeightPtId", false, "Efficiency correction (pT) "}; - Configurable cfgWeightPtEtaId{"cfgWeightPtEtaId", false, "Efficiency correction (pT) "}; - Configurable cfgPurityId{"cfgPurityId", false, "Purity correction"}; + Configurable cfgNtpcEventCut{"cfgNtpcEventCut", true, "N_{TPC} Event Cut for reco"}; + Configurable cfgApplyEfficiencyCorrection{"cfgApplyEfficiencyCorrection", false, "Apply measured track-by-track efficiency weights"}; + Configurable cfgUseCombinedPurityEfficiencyWeight{"cfgUseCombinedPurityEfficiencyWeight", false, "Use the legacy Ngen/Nselected weight instead of the efficiency-only 1/epsilon weight"}; + Configurable cfgEfficiencyCCDBUrl{"cfgEfficiencyCCDBUrl", "http://ccdb-test.cern.ch:8080", "Efficiency CCDB URL"}; + Configurable cfgEfficiencyCCDBPath{"cfgEfficiencyCCDBPath", "Users/t/tgahlaut/weightCorr", "CCDB path containing ccdb_object"}; + Configurable cfgEfficiencyTimestamp{"cfgEfficiencyTimestamp", -1, "Efficiency CCDB timestamp; -1 selects the latest object"}; ConfigurableAxis multTPCBins{"multTPCBins", {150, 0, 150}, "TPC Multiplicity bins"}; + ConfigurableAxis multMCBins{"multMCBins", {300, 0, 300}, "MC Multiplicity bins"}; + ConfigurableAxis multCorrBins{"multCorrBins", {100, 0., 150.}, "Corrected TPC Multiplicity bins"}; ConfigurableAxis multFT0MBins{"multFT0MBins", {1000, 0, 5000}, "Forward Multiplicity bins"}; - ConfigurableAxis qNBins{"qNBins", {1000, 0., 100.}, "nth moments bins"}; - ConfigurableAxis nPairBins{"nPairBins", {2000, 0, 10000}, "nPair bins"}; ConfigurableAxis dcaXYBins{"dcaXYBins", {100, -0.15, 0.15}, "dcaXY bins"}; ConfigurableAxis dcaZBins{"dcaZBins", {500, -1.2, 1.2}, "dcaZ bins"}; + ConfigurableAxis centBins{"centBins", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100}, "Centrality bins"}; - Configurable> ptBins{"ptBins", {0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00}, "p_{T} bins"}; - Configurable> etaBins{"etaBins", {-0.8, -0.75, -0.7, -0.65, -0.6, -0.55, -0.5, -0.45, -0.4, -0.35, -0.3, -0.25, -0.2, -0.15, -0.1, -0.05, 0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8}, "#eta bins"}; + Configurable> ptBins{"ptBins", {0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15, 1.20, 1.25, 1.30, 1.35, 1.40, 1.45, 1.50, 1.55, 1.60, 1.65, 1.70, 1.75, 1.80, 1.85, 1.90, 1.95, 2.00}, "p_{T} bins"}; + Configurable> etaBins{"etaBins", {-0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}, "#eta bins"}; - Configurable cfgUrlCCDB{"cfgUrlCCDB", "http://ccdb-test.cern.ch:8080", "url of ccdb"}; - Configurable cfgPathCCDB{"cfgPathCCDB", "Users/t/tgahlaut/weightCorr/", "Path for ccdb-object"}; - - Service ccdb; - Service pdg; + Service pdg{}; + Service ccdb{}; HistogramRegistry hist{"hist", {}, OutputObjHandlingPolicy::AnalysisObject}; - TH1D* hWeightPt = nullptr; - TH1D* hPurePt = nullptr; - TH1D* hWeightPtPi = nullptr; - TH1D* hWeightPtKa = nullptr; - TH1D* hWeightPtPr = nullptr; - TH1D* hPurePtPi = nullptr; - TH1D* hPurePtKa = nullptr; - TH1D* hPurePtPr = nullptr; + TH3* efficiencyWeightCh = nullptr; + TH3* efficiencyWeightPi = nullptr; + TH3* efficiencyWeightKa = nullptr; + TH3* efficiencyWeightPr = nullptr; enum CollisionLabels { kTotCol = 1, @@ -129,6 +149,8 @@ struct MeanPtFlucId { kSelColSel8, kSelColNoSameBunchPileup, kSelColIsVertexITSTPC, + kSelColIsGoodITSLayersAll, + kSelColIsGoodZvtxFT0vsPV }; enum class PIDType { @@ -153,7 +175,7 @@ struct MeanPtFlucId { Gen_Proton }; - static constexpr std::string_view Dire[] = { + static constexpr std::array Directories{ "QA/after/", "QA/Pion/", "QA/Kaon/", @@ -166,28 +188,130 @@ struct MeanPtFlucId { "Gen/Pion/", "Gen/Kaon/", "Gen/Proton/"}; + static constexpr auto const& dire = Directories; + + enum CorrectedMode { + Corrected_Charged = 0, + Corrected_Pion, + Corrected_Kaon, + Corrected_Proton + }; + + static constexpr std::array CorrectedDirectories{ + "Corrected/Charged/", + "Corrected/Pion/", + "Corrected/Kaon/", + "Corrected/Proton/"}; + + static constexpr std::array TruthMatchedCorrectedDirectories{ + "CorrectedTruthMatched/Charged/", + "CorrectedTruthMatched/Pion/", + "CorrectedTruthMatched/Kaon/", + "CorrectedTruthMatched/Proton/"}; + + struct TrackInfo { + double pt; + }; + + struct WeightedTrackInfo { + double pt; + double weight; + }; + + std::vector chargedTracks; + std::vector pionTracks; + std::vector kaonTracks; + std::vector protonTracks; + + std::vector chargedTracksGen; + std::vector pionTracksGen; + std::vector kaonTracksGen; + std::vector protonTracksGen; + + std::vector chargedTracksCorrected; + std::vector pionTracksCorrected; + std::vector kaonTracksCorrected; + std::vector protonTracksCorrected; + std::vector chargedTracksTruthMatchedCorrected; + std::vector pionTracksTruthMatchedCorrected; + std::vector kaonTracksTruthMatchedCorrected; + std::vector protonTracksTruthMatchedCorrected; + + // Reused event-wise storage for the factorial-moment matrices. + std::vector matrixNInBin; + std::vector matrixOccupiedBins; + std::vector matrixWInBin; + std::vector matrixW2InBin; void init(InitContext const&) { - if (cfgLoadEff) { - // Set CCDB url - ccdb->setURL(cfgUrlCCDB.value); - ccdb->setCaching(true); + if (!std::isfinite(cfgTofShiftPi.value) || !std::isfinite(cfgTofShiftKa.value) || !std::isfinite(cfgTofShiftPr.value)) { + LOGF(fatal, "TOF nSigma shifts must be finite: pion=%g, kaon=%g, proton=%g", + cfgTofShiftPi.value, cfgTofShiftKa.value, cfgTofShiftPr.value); + } + + if (nPtBins <= 0 || cfgCutPtMin >= cfgCutPtMax || cfgCutPiPtMin >= cfgCutPtMax || + cfgCutKaPtMin >= cfgCutPtMax || cfgCutPrPtMin >= cfgCutPtMax) { + LOGF(fatal, "Invalid pT matrix binning: nPtBins=%d, max=%g, minima=(%g,%g,%g,%g)", + static_cast(nPtBins), static_cast(cfgCutPtMax), + static_cast(cfgCutPtMin), static_cast(cfgCutPiPtMin), + static_cast(cfgCutKaPtMin), static_cast(cfgCutPrPtMin)); + } - TList* lst = ccdb->getForTimeStamp(cfgPathCCDB.value, -1); - hWeightPt = reinterpret_cast(lst->FindObject("hWeightPt")); - hWeightPtPi = reinterpret_cast(lst->FindObject("hWeightPtPi")); - hWeightPtKa = reinterpret_cast(lst->FindObject("hWeightPtKa")); - hWeightPtPr = reinterpret_cast(lst->FindObject("hWeightPtPr")); - hPurePtPi = reinterpret_cast(lst->FindObject("hPurePtPi")); - hPurePtKa = reinterpret_cast(lst->FindObject("hPurePtKa")); - hPurePtPr = reinterpret_cast(lst->FindObject("hPurePtPr")); + if (cfgApplyEfficiencyCorrection) { + ccdb->setURL(cfgEfficiencyCCDBUrl.value); + ccdb->setCaching(true); + auto* efficiencyList = ccdb->getForTimeStamp(cfgEfficiencyCCDBPath.value, cfgEfficiencyTimestamp.value); + if (!efficiencyList) { + LOGF(fatal, "Cannot load ccdb_object containing the efficiency maps"); + } - if (!hWeightPt || !hWeightPtPi || !hWeightPtKa || !hWeightPtPr || !hPurePtPi || !hPurePtKa || !hPurePtPr) { - LOGF(info, "FATAL!! Could not find required histograms in CCDB"); + const char* chargedMapName = cfgUseCombinedPurityEfficiencyWeight ? "hWeightPtEtaCent" : "hInverseEfficiencyPtEtaCent"; + const char* pionMapName = cfgUseCombinedPurityEfficiencyWeight ? "hWeightPtEtaCentPi" : "hInverseEfficiencyPtEtaCentPi"; + const char* kaonMapName = cfgUseCombinedPurityEfficiencyWeight ? "hWeightPtEtaCentKa" : "hInverseEfficiencyPtEtaCentKa"; + const char* protonMapName = cfgUseCombinedPurityEfficiencyWeight ? "hWeightPtEtaCentPr" : "hInverseEfficiencyPtEtaCentPr"; + efficiencyWeightCh = dynamic_cast(efficiencyList->FindObject(chargedMapName)); + efficiencyWeightPi = dynamic_cast(efficiencyList->FindObject(pionMapName)); + efficiencyWeightKa = dynamic_cast(efficiencyList->FindObject(kaonMapName)); + efficiencyWeightPr = dynamic_cast(efficiencyList->FindObject(protonMapName)); + if (!efficiencyWeightCh || !efficiencyWeightPi || !efficiencyWeightKa || !efficiencyWeightPr) { + LOGF(fatal, "Missing efficiency map(s): charged=%s, pion=%s, kaon=%s, proton=%s", + chargedMapName, pionMapName, kaonMapName, protonMapName); } + + auto validateEfficiencyMap = [&](const TH3* map, const char* name, double minPt) { + constexpr double AxisTolerance = 1.e-6; + constexpr double MaximumCentrality = 100.; + const bool coversEta = map->GetXaxis()->GetXmin() <= -cfgCutEta + AxisTolerance && + map->GetXaxis()->GetXmax() >= cfgCutEta - AxisTolerance; + const bool coversPt = map->GetYaxis()->GetXmin() <= minPt + AxisTolerance && + map->GetYaxis()->GetXmax() >= cfgCutPtMax - AxisTolerance; + const bool coversCentrality = map->GetZaxis()->GetXmin() <= AxisTolerance && + map->GetZaxis()->GetXmax() >= MaximumCentrality - AxisTolerance; + if (!coversEta || !coversPt || !coversCentrality) { + LOGF(fatal, + "Efficiency map %s does not cover the analysis phase space: " + "eta=[%g,%g], pT=[%g,%g], centrality=[%g,%g]. " + "Expected eta=[%g,%g], pT=[%g,%g], centrality=[0,100].", + name, + map->GetXaxis()->GetXmin(), map->GetXaxis()->GetXmax(), + map->GetYaxis()->GetXmin(), map->GetYaxis()->GetXmax(), + map->GetZaxis()->GetXmin(), map->GetZaxis()->GetXmax(), + -static_cast(cfgCutEta), static_cast(cfgCutEta), + minPt, static_cast(cfgCutPtMax)); + } + }; + validateEfficiencyMap(efficiencyWeightCh, chargedMapName, cfgCutPtMin); + validateEfficiencyMap(efficiencyWeightPi, pionMapName, cfgCutPiPtMin); + validateEfficiencyMap(efficiencyWeightKa, kaonMapName, cfgCutKaPtMin); + validateEfficiencyMap(efficiencyWeightPr, protonMapName, cfgCutPrPtMin); } + const AxisSpec axisPtCh{nPtBins, cfgCutPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtPi{nPtBins, cfgCutPiPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtKa{nPtBins, cfgCutKaPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisPtPr{nPtBins, cfgCutPrPtMin, cfgCutPtMax, "p_{T} (GeV/c)"}; + const AxisSpec axisCol{3, 1, 4, ""}; const AxisSpec axisTrack{5, 1, 6, ""}; const AxisSpec axisEvents{10, 1, 11, "Counts"}; @@ -196,15 +320,13 @@ struct MeanPtFlucId { const AxisSpec axisY{100, -0.6, 0.6, "y"}; const AxisSpec axisPt{ptBins, "p_{T} (GeV/c)"}; const AxisSpec axisP{nPBins, 0., 3., "p (GeV/c)"}; - const AxisSpec axisInnerParam{nPBins, 0., 3., "p_{InnerParam } (GeV/c)"}; - const AxisSpec axisPart{nPartBins, 0., 18., " "}; - const AxisSpec axisQn{qNBins, ""}; - const AxisSpec axisNpair{nPairBins, "N_{pairs}"}; - const AxisSpec axisMeanPt{100, 0., 3., "M(p_{T}) (GeV/c)"}; - const AxisSpec axisMult{100, 0, 100, "N_{ch}"}; + const AxisSpec axisInnerParam{nPBins, 0., 3., "p_{InnerParam} (GeV/c)"}; const AxisSpec axisMultTPC{multTPCBins, "N_{TPC} "}; + const AxisSpec axisMultCorr{multCorrBins, "N_{Corr} "}; + const AxisSpec axisMultMC{multMCBins, "N_{Gen} "}; const AxisSpec axisMultFT0M{multFT0MBins, "N_{FT0M}"}; const AxisSpec axisCentFT0M{101, 0, 101, "FT0M (%)"}; + const AxisSpec axisCent{centBins, "FT0M (%)"}; const AxisSpec axisVtxZ{80, -20., 20., "V_{Z} (cm)"}; const AxisSpec axisDCAz{dcaZBins, "DCA_{Z} (cm)"}; const AxisSpec axisDCAxy{dcaXYBins, "DCA_{XY} (cm)"}; @@ -217,10 +339,6 @@ struct MeanPtFlucId { const AxisSpec axisM2{100, 0., 1.4, "#it{m}^{2} (GeV/#it{c}^{2})^{2}"}; const AxisSpec axisPid{300, 0, 3000, "PID"}; - HistogramConfigSpec qNHist({HistType::kTHnSparseD, {axisCentFT0M, axisQn}}); - HistogramConfigSpec partHist({HistType::kTHnSparseD, {axisCentFT0M, axisPart}}); - HistogramConfigSpec qNMCHist({HistType::kTHnSparseD, {axisCentFT0M, axisQn}}); - HistogramConfigSpec partMCHist({HistType::kTHnSparseD, {axisCentFT0M, axisPart}}); HistogramConfigSpec tofNSigmaHist({HistType::kTH2D, {axisP, axisTOFNsigma}}); HistogramConfigSpec tofSignalHist({HistType::kTH2D, {axisP, axisTOFSignal}}); HistogramConfigSpec tpcNSigmaHist({HistType::kTH2D, {axisP, axisTPCNsigma}}); @@ -228,129 +346,226 @@ struct MeanPtFlucId { HistogramConfigSpec tpcTofHist({HistType::kTH2D, {axisTPCNsigma, axisTOFNsigma}}); HistogramConfigSpec pvsM2Hist({HistType::kTH2D, {axisM2, axisP}}); HistogramConfigSpec tpcSignalHist1({HistType::kTH2D, {axisInnerParam, axisTPCSignal}}); - HistogramConfigSpec pvsM2Hist1({HistType::kTH2D, {axisM2, axisInnerParam}}); - - // QA Plots - hist.add("QA/before/h_Counts", "Counts", kTH1D, {axisEvents}); - hist.add("QA/before/h_VtxZ", "V_{Z}", kTH1D, {axisVtxZ}); - hist.add("QA/before/h_NTPC", "N_{TPC}", kTH1D, {axisMultTPC}); - hist.add("QA/before/h_NFT0M", "FT0M Multiplicity", kTH1D, {axisMultFT0M}); - hist.add("QA/before/h_CentM", "FT0M (%)", kTH1D, {axisCentFT0M}); - - hist.add("QA/before/h2_TPCSignal", "TPC Signal", tpcSignalHist); - hist.add("QA/before/h2_TOFSignal", "TOF Signal", tofSignalHist); - hist.add("QA/before/h2_pvsm2", "p vs m^{2}", pvsM2Hist); - - hist.addClone("QA/before/", "QA/after/"); - - hist.add("QA/before/h_Pt", "p_{T}", kTH1D, {axisPt}); - hist.add("QA/before/h_Eta", "#eta ", kTH1D, {axisEta}); - hist.add("QA/before/h_Phi", "#phi ", kTH1D, {axisPhi}); - hist.add("QA/before/h_DcaZ", "DCA_{Z}", kTH1D, {axisDCAz}); - hist.add("QA/before/h_DcaXY", "DCA_{XY}", kTH1D, {axisDCAxy}); - hist.add("QA/before/h2_DcaZ", "DCA_{Z}", kTH2D, {{axisPt}, {axisDCAz}}); - hist.add("QA/before/h2_DcaXY", "DCA_{XY}", kTH2D, {{axisPt}, {axisDCAxy}}); - - hist.add("QA/after/h_counts_evSelCuts", "Event selection cuts", kTH1D, {axisEvents}); - hist.add("QA/after/h_TPCChi2perCluster", "TPC #Chi^{2}/Cluster", kTH1D, {axisChi2}); - hist.add("QA/after/h_ITSChi2perCluster", "ITS #Chi^{2}/Cluster", kTH1D, {axisChi2}); - hist.add("QA/after/h_crossedTPC", "Crossed TPC", kTH1D, {axisCrossedTPC}); - hist.add("QA/after/h2_NTPC_CentM", "N_{TPC} vs FT0M(%)", kTH2D, {{axisCentFT0M}, {axisMultTPC}}); - hist.add("QA/after/h2_NTPC_NFT0M", "N_{TPC} vs N_{FT0M}", kTH2D, {{axisMultFT0M}, {axisMultTPC}}); - hist.add("QA/after/p_NTPC_NFT0M", "N_{TPC} vs N_{FT0M} (Profile)", kTProfile, {axisMultFT0M}); - hist.add("QA/after/p_NTPC_CentM", "N_{TPC} vs FT0M(%) (Profile)", kTProfile, {axisCentFT0M}); - hist.add("QA/after/h_DCAxy_primary", "DCA_{XY} (Primary)", kTH1D, {axisDCAxy}); - hist.add("QA/after/h_DCAz_primary", "DCA_{Z} (Primary)", kTH1D, {axisDCAz}); - hist.add("QA/after/h_DCAxy_secondary", "DCA_{XY} (Secondary)", kTH1D, {axisDCAxy}); - hist.add("QA/after/h_DCAz_secondary", "DCA_{Z} (Secondary)", kTH1D, {axisDCAz}); - hist.add("QA/after/innerParam/h2_TPCSignal", "TPC Signal", tpcSignalHist1); - - hist.add("QA/Charged/h_Pt", "p_{T}", kTH1D, {axisPt}); - hist.add("QA/Charged/h_Eta", "#eta ", kTH1D, {axisEta}); - hist.add("QA/Charged/h_Phi", "#phi ", kTH1D, {axisPhi}); - hist.add("QA/Charged/h_DcaZ", "DCA_{Z}", kTH1D, {axisDCAz}); - hist.add("QA/Charged/h_DcaXY", "DCA_{XY}", kTH1D, {axisDCAxy}); - hist.add("QA/Charged/h2_DcaZ", "DCA_{Z}", kTH2D, {{axisPt}, {axisDCAz}}); - hist.add("QA/Charged/h2_DcaXY", "DCA_{XY}", kTH2D, {{axisPt}, {axisDCAxy}}); - hist.add("QA/Charged/h_Pt_weighted", "weighted pT distribution", kTH1D, {axisPt}); - hist.add("QA/Charged/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPt}}); - hist.add("QA/Charged/h2_Pt_centFT0M", "p_{T} in centrality Classes ", kTH2D, {{axisCentFT0M}, {axisPt}}); - hist.add("QA/Charged/h3_PtEtaPhi", "p_{T}, #eta, #phi ", kTHnSparseD, {{axisPt}, {axisEta}, {axisPhi}}); - - hist.addClone("QA/Charged/", "QA/Pion/"); - - hist.add("QA/Pion/before/h2_TPCNsigma", "n #sigma_{TPC}", tpcNSigmaHist); - hist.add("QA/Pion/before/h2_TPCNsigma_tof", "n #sigma_{TPC}", tpcNSigmaHist); - hist.add("QA/Pion/before/h2_TOFNsigma", "n #sigma_{TOF}", tofNSigmaHist); - hist.add("QA/Pion/before/h2_TpcTofNsigma", "n #sigma_{TPC} vs n #sigma_{TOF}", tpcTofHist); - - hist.add("QA/Pion/h_Rap", "y ", kTH1D, {axisY}); - hist.add("QA/Pion/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPt}); - hist.add("QA/Pion/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPt}); - hist.add("QA/Pion/h_PtTruth", "p_{T} (Truth)", kTH1D, {axisPt}); - hist.add("QA/Pion/h_PtPosTruth", "p_{T} (positive) (Truth)", kTH1D, {axisPt}); - hist.add("QA/Pion/h_PtNegTruth", "p_{T} (negative) (Truth) ", kTH1D, {axisPt}); - hist.add("QA/Pion/h2_TPCNsigma", "n #sigma_{TPC}", tpcNSigmaHist); - hist.add("QA/Pion/h2_TOFNsigma", "n #sigma_{TOF}", tofNSigmaHist); - hist.add("QA/Pion/h2_TpcTofNsigma", "n #sigma_{TPC} vs n #sigma_{TOF}", tpcTofHist); - hist.add("QA/Pion/h2_TPCSignal", "TPC Signal ", tpcSignalHist); - hist.add("QA/Pion/h2_TOFSignal", "TOF Signal", tofSignalHist); - hist.add("QA/Pion/innerParam/h2_TPCSignal", "TPC Signal", tpcSignalHist1); - hist.add("QA/Pion/h2_pvsm2", "p vs m^{2}", pvsM2Hist); - hist.add("QA/Pion/h_PtTruth_primary", "p_{T} (Truth Primary)", kTH1D, {axisPt}); - hist.add("QA/Pion/h_PtTruth_secondary", "p_{T} (Truth Secondary)", kTH1D, {axisPt}); - hist.addClone("QA/Pion/", "QA/Kaon/"); - hist.addClone("QA/Pion/", "QA/Proton/"); - - // AnalysisPlots - hist.add("Analysis/Charged/h_Mult", "Multiplicity", kTH1D, {axisMult}); - hist.add("Analysis/Charged/h_N_CentFT0M", "Multiplicity vs CentFT0M", kTHnSparseD, {axisCentFT0M, axisMult}); - hist.add("Analysis/Charged/h_Npair_CentFT0M", "Npair vs CentFT0M", kTHnSparseD, {axisCentFT0M, axisNpair}); - hist.add("Analysis/Charged/h_Q1_CentFT0M", "Q1 vs CentFT0M", qNHist); - hist.add("Analysis/Charged/h_Q2_CentFT0M", "Q2 vs CentFT0M", qNHist); - hist.add("Analysis/Charged/h_twopart1_CentFT0M", "twopart (neum)", qNMCHist); - hist.add("Analysis/Charged/h_mean_pT_Mult_var", " vs N_{TPC} ", partHist); - hist.add("Analysis/Charged/h_twopart_Mult_var", "Twopart vs N_{TPC} ", partHist); - hist.add("Analysis/Charged/p_twopart_CentFT0M", "Twopart vs cent_{FT0M} ", kTProfile, {axisCentFT0M}); - hist.add("Analysis/Charged/p_mean_pT_CentFT0M", " vs cent_{FT0M} ", kTProfile, {axisCentFT0M}); - - hist.addClone("Analysis/Charged/", "Analysis/Pion/"); - hist.addClone("Analysis/Charged/", "Analysis/Kaon/"); - hist.addClone("Analysis/Charged/", "Analysis/Proton/"); - - // MC Generated - hist.add("Gen/h_Counts", "Counts", kTH1D, {axisEvents}); - hist.add("Gen/h_VtxZ", "Vertex Z ", kTH1D, {axisVtxZ}); - hist.add("Gen/h_VtxZ_b", "Vertex Z ", kTH1D, {axisVtxZ}); - hist.add("Gen/h_NSim", "Truth Multiplicity TPC", kTH1D, {axisMultTPC}); - hist.add("Gen/h2_NTPC_NSim", "Reco vs Truth Multiplicty TPC", kTH2D, {{axisMultTPC}, {axisMultTPC}}); - - hist.add("Gen/Charged/h_EtaTruth", "#eta ", kTH1D, {axisEta}); - hist.add("Gen/Charged/h_PhiTruth", "#phi ", kTH1D, {axisPhi}); - hist.add("Gen/Charged/h_PtTruth", "p_{T} ", kTH1D, {axisPt}); - hist.add("Gen/Charged/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPt}}); - hist.add("Gen/Charged/h2_PtTruth_centFT0M", "p_{T} in centrality Classes ", kTH2D, {{axisCentFT0M}, {axisPt}}); - hist.add("Gen/Charged/h_PtEtaPhiTruth", "p_{T}, #eta, #phi ", kTHnSparseD, {{axisPt}, {axisEta}, {axisPhi}}); - - hist.add("Gen/Charged/h_Mult", "Multiplicity", kTH1D, {axisMult}); - hist.add("Gen/Charged/h_N_CentFT0M", "Multiplicity vs CentFT0M", kTHnSparseD, {axisCentFT0M, axisMult}); - hist.add("Gen/Charged/h_Npair_CentFT0M", "Npair vs CentFT0M", kTHnSparseD, {axisCentFT0M, axisNpair}); - hist.add("Gen/Charged/h_Q1_CentFT0M", "Q1", qNMCHist); - hist.add("Gen/Charged/h_Q2_CentFT0M", "Q2", qNMCHist); - hist.add("Gen/Charged/h_twopart1_CentFT0M", "twopart (neum)", qNMCHist); - hist.add("Gen/Charged/h_mean_pT_Mult_var", " vs N_{TPC} ", partMCHist); - hist.add("Gen/Charged/h_twopart_Mult_var", "Twopart vs N_{TPC} ", partMCHist); - hist.add("Gen/Charged/p_twopart_CentFT0M", "Twopart vs CentFT0M ", kTProfile, {axisCentFT0M}); - hist.add("Gen/Charged/p_mean_pT_CentFT0M", " vs CentFT0M ", kTProfile, {axisCentFT0M}); - - hist.addClone("Gen/Charged/", "Gen/Pion/"); - - hist.add("Gen/Pion/h_RapTruth", "y", kTH1D, {axisY}); - hist.add("Gen/Pion/h_PtPosTruth", "p_{T} (positive) ", kTH1D, {axisPt}); - hist.add("Gen/Pion/h_PtNegTruth", "p_{T} (negative) ", kTH1D, {axisPt}); - - hist.addClone("Gen/Pion/", "Gen/Kaon/"); - hist.addClone("Gen/Pion/", "Gen/Proton/"); + + auto addEventQAHistograms = [&]() { + // Event QA plots + hist.add("QA/before/h_Counts", "Counts", kTH1D, {axisEvents}); + hist.add("QA/before/h_VtxZ", "V_{Z}", kTH1D, {axisVtxZ}); + hist.add("QA/before/h_NTPC", "N_{TPC}", kTH1D, {axisMultTPC}); + hist.add("QA/before/h_NFT0M", "FT0M Multiplicity", kTH1D, {axisMultFT0M}); + hist.add("QA/before/h_CentM", "FT0M (%)", kTH1D, {axisCentFT0M}); + + hist.add("QA/before/h2_TPCSignal", "TPC Signal", tpcSignalHist); + hist.add("QA/before/h2_TOFSignal", "TOF Signal", tofSignalHist); + hist.add("QA/before/h2_pvsm2", "p vs m^{2}", pvsM2Hist); + + hist.addClone("QA/before/", "QA/after/"); + + hist.add("QA/before/h_Pt", "p_{T}", kTH1D, {axisPt}); + hist.add("QA/before/h_Eta", "#eta ", kTH1D, {axisEta}); + hist.add("QA/before/h_Phi", "#phi ", kTH1D, {axisPhi}); + hist.add("QA/before/h_DcaZ", "DCA_{Z}", kTH1D, {axisDCAz}); + hist.add("QA/before/h_DcaXY", "DCA_{XY}", kTH1D, {axisDCAxy}); + hist.add("QA/before/h2_DcaZ", "DCA_{Z}", kTH2D, {{axisPt}, {axisDCAz}}); + hist.add("QA/before/h2_DcaXY", "DCA_{XY}", kTH2D, {{axisPt}, {axisDCAxy}}); + + hist.add("QA/after/h_Ncorr", "N_{corr}", kTH1D, {axisMultCorr}); + hist.add("QA/after/h_counts_evSelCuts", "Event selection cuts", kTH1D, {axisEvents}); + hist.add("QA/after/h_TPCChi2perCluster", "TPC #Chi^{2}/Cluster", kTH1D, {axisChi2}); + hist.add("QA/after/h_ITSChi2perCluster", "ITS #Chi^{2}/Cluster", kTH1D, {axisChi2}); + hist.add("QA/after/h_crossedTPC", "Crossed TPC", kTH1D, {axisCrossedTPC}); + hist.add("QA/after/h2_NTPC_CentM", "N_{TPC} vs FT0M(%)", kTH2D, {{axisCentFT0M}, {axisMultTPC}}); + hist.add("QA/after/h2_NTPC_NFT0M", "N_{TPC} vs N_{FT0M}", kTH2D, {{axisMultFT0M}, {axisMultTPC}}); + hist.add("QA/after/p_NTPC_NFT0M", "N_{TPC} vs N_{FT0M} (Profile)", kTProfile, {axisMultFT0M}); + hist.add("QA/after/p_NTPC_CentM", "N_{TPC} vs FT0M(%) (Profile)", kTProfile, {axisCentFT0M}); + hist.add("QA/after/h_DCAxy_primary", "DCA_{XY} (Primary)", kTH1D, {axisDCAxy}); + hist.add("QA/after/h_DCAz_primary", "DCA_{Z} (Primary)", kTH1D, {axisDCAz}); + hist.add("QA/after/h_DCAxy_secondary", "DCA_{XY} (Secondary)", kTH1D, {axisDCAxy}); + hist.add("QA/after/h_DCAz_secondary", "DCA_{Z} (Secondary)", kTH1D, {axisDCAz}); + hist.add("QA/after/innerParam/h2_TPCSignal", "TPC Signal", tpcSignalHist1); + hist.add("QA/after/h2_NSim_NTPC", "Reco vs Truth Multiplicty TPC", kTH2D, {{axisMultMC}, {axisMultTPC}}); + hist.add("QA/after/h2_NTPC_NSim", "Truth vs Reco Multiplicty TPC", kTH2D, {{axisMultTPC}, {axisMultMC}}); + hist.add("QA/after/p_NTPC_NSim", "Truth vs Reco Multiplicty TPC", kTProfile, {axisMultTPC}); + hist.add("QA/after/h2_Ncorr_NSim", "Truth vs Corrected Multiplicty TPC", kTH2D, {{axisMultCorr}, {axisMultMC}}); + hist.add("QA/after/h2_Ncorr_CentM", "N_{corr} vs FT0M(%)", kTH2D, {{axisCentFT0M}, {axisMultCorr}}); + }; + addEventQAHistograms(); + + auto addTrackQAHistograms = [&]() { + hist.add("QA/Charged/h_Pt", "p_{T}", kTH1D, {axisPt}); + hist.add("QA/Charged/h_Eta", "#eta ", kTH1D, {axisEta}); + hist.add("QA/Charged/h_Phi", "#phi ", kTH1D, {axisPhi}); + hist.add("QA/Charged/h_DcaZ", "DCA_{Z}", kTH1D, {axisDCAz}); + hist.add("QA/Charged/h_DcaXY", "DCA_{XY}", kTH1D, {axisDCAxy}); + hist.add("QA/Charged/h2_Pt_PtMC", "p_{T} vs p_{T} (MC)", kTH2D, {{axisPt}, {axisPt}}); + hist.add("QA/Charged/h2_DcaZ", "DCA_{Z}", kTH2D, {{axisPt}, {axisDCAz}}); + hist.add("QA/Charged/h2_DcaXY", "DCA_{XY}", kTH2D, {{axisPt}, {axisDCAxy}}); + hist.add("QA/Charged/h2_Pt_centFT0M", "p_{T} in centrality Classes ", kTH2D, {{axisCentFT0M}, {axisPt}}); + hist.add("QA/Charged/h3_Pt_Eta_Phi", "p_{T}, #eta, #phi ", kTHnSparseD, {{axisPt}, {axisEta}, {axisPhi}}); + + hist.addClone("QA/Charged/", "QA/Pion/"); + + hist.add("QA/Pion/before/h2_TPCNsigma", "n #sigma_{TPC}", tpcNSigmaHist); + hist.add("QA/Pion/before/h2_TPCNsigma_nottof", "n #sigma_{TPC}", tpcNSigmaHist); + hist.add("QA/Pion/before/h2_TPCNsigma_tof", "n #sigma_{TPC}", tpcNSigmaHist); + hist.add("QA/Pion/before/h2_TOFNsigma", "n #sigma_{TOF}", tofNSigmaHist); + hist.add("QA/Pion/before/h2_TpcTofNsigma", "n #sigma_{TPC} vs n #sigma_{TOF}", tpcTofHist); + hist.add("QA/Pion/h_Rap", "y ", kTH1D, {axisY}); + hist.add("QA/Pion/h2_TPCNsigma", "n #sigma_{TPC}", tpcNSigmaHist); + hist.add("QA/Pion/h2_TOFNsigma", "n #sigma_{TOF}", tofNSigmaHist); + hist.add("QA/Pion/h2_TpcTofNsigma", "n #sigma_{TPC} vs n #sigma_{TOF}", tpcTofHist); + hist.add("QA/Pion/h2_TPCSignal", "TPC Signal ", tpcSignalHist); + hist.add("QA/Pion/h2_TOFSignal", "TOF Signal", tofSignalHist); + hist.add("QA/Pion/innerParam/h2_TPCSignal", "TPC Signal", tpcSignalHist1); + hist.add("QA/Pion/h2_pvsm2", "p vs m^{2}", pvsM2Hist); + hist.addClone("QA/Pion/", "QA/Kaon/"); + hist.addClone("QA/Pion/", "QA/Proton/"); + + hist.add("QA/Charged/h_Pt_e", "p_{T}", kTH1D, {axisPtCh}); + hist.add("QA/Charged/h_PtMC", "p_{T} (MC)", kTH1D, {axisPtCh}); + hist.add("QA/Charged/h_PtTruth_primary", "p_{T} (Truth Primary)", kTH1D, {axisPtCh}); + hist.add("QA/Charged/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtCh}}); + hist.add("QA/Charged/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); + hist.add("QA/Charged/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); + hist.add("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); + hist.add("QA/Charged/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtCh}}); + }; + addTrackQAHistograms(); + + auto addEfficiencyHistograms = [&]() { + hist.add("QA/Pion/h_Pt_e", "p_{T}", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h_PtMC", "p_{T} (MC)", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPi}}); + hist.add("QA/Pion/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); + hist.add("QA/Pion/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); + hist.add("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); + hist.add("QA/Pion/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPi}}); + hist.add("QA/Pion/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h_PtTruth", "p_{T} (Truth)", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h_PtPosTruth", "p_{T} (positive) (Truth)", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h_PtNegTruth", "p_{T} (negative) (Truth) ", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h_PtTruth_primary", "p_{T} (Truth Primary)", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h_PtTruth_secondary", "p_{T} (Truth Secondary)", kTH1D, {axisPtPi}); + hist.add("QA/Pion/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPtPi}}); + hist.add("QA/Pion/h2_Pt_EtaTruth_primary", "p_{T} vs #eta (Truth Primary)", kTH2D, {{axisEta}, {axisPtPi}}); + + hist.add("QA/Kaon/h_Pt_e", "p_{T}", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h_PtMC", "p_{T} (MC)", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtKa}}); + hist.add("QA/Kaon/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); + hist.add("QA/Kaon/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); + hist.add("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); + hist.add("QA/Kaon/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtKa}}); + hist.add("QA/Kaon/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h_PtTruth", "p_{T} (Truth)", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h_PtPosTruth", "p_{T} (positive) (Truth)", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h_PtNegTruth", "p_{T} (negative) (Truth) ", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h_PtTruth_primary", "p_{T} (Truth Primary)", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h_PtTruth_secondary", "p_{T} (Truth Secondary)", kTH1D, {axisPtKa}); + hist.add("QA/Kaon/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPtKa}}); + hist.add("QA/Kaon/h2_Pt_EtaTruth_primary", "p_{T} vs #eta (Truth Primary)", kTH2D, {{axisEta}, {axisPtKa}}); + + hist.add("QA/Proton/h_Pt_e", "p_{T}", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h_PtMC", "p_{T} (MC)", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h2_Pt_Eta", "p_{T} vs #eta ", kTH2D, {{axisEta}, {axisPtPr}}); + hist.add("QA/Proton/h3_Pt_Eta_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); + hist.add("QA/Proton/h3_Pt_EtaMC_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); + hist.add("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M", "Selected truth primary tracks in reconstructed coordinates;#eta;p_{T};FT0M (%)", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); + hist.add("QA/Proton/h2_Pt_EtaMC", "p_{T} vs #eta (MC)", kTH2D, {{axisEta}, {axisPtPr}}); + hist.add("QA/Proton/h_PtPos", "p_{T} (positive) ", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h_PtNeg", "p_{T} (negative) ", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h_PtTruth", "p_{T} (Truth)", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h_PtPosTruth", "p_{T} (positive) (Truth)", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h_PtNegTruth", "p_{T} (negative) (Truth) ", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h_PtTruth_primary", "p_{T} (Truth Primary)", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h_PtTruth_secondary", "p_{T} (Truth Secondary)", kTH1D, {axisPtPr}); + hist.add("QA/Proton/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPtPr}}); + hist.add("QA/Proton/h2_Pt_EtaTruth_primary", "p_{T} vs #eta (Truth Primary)", kTH2D, {{axisEta}, {axisPtPr}}); + }; + addEfficiencyHistograms(); + + auto addMatrixHistograms = [&]() { + hist.add("Analysis/Charged/hN1Matrix", "Single-particle density", kTH2D, {{axisCent}, {axisPtCh}}); + hist.add("Analysis/Charged/hN2Matrix", "Ordered-pair density", kTH3D, {{axisCent}, {axisPtCh}, {axisPtCh}}); + + hist.add("Analysis/Pion/hN1Matrix", "Single-particle density", kTH2D, {{axisCent}, {axisPtPi}}); + hist.add("Analysis/Pion/hN2Matrix", "Ordered-pair density", kTH3D, {{axisCent}, {axisPtPi}, {axisPtPi}}); + + hist.add("Analysis/Kaon/hN1Matrix", "Single-particle density", kTH2D, {{axisCent}, {axisPtKa}}); + hist.add("Analysis/Kaon/hN2Matrix", "Ordered-pair density", kTH3D, {{axisCent}, {axisPtKa}, {axisPtKa}}); + + hist.add("Analysis/Proton/hN1Matrix", "Single-particle density", kTH2D, {{axisCent}, {axisPtPr}}); + hist.add("Analysis/Proton/hN2Matrix", "Ordered-pair density", kTH3D, {{axisCent}, {axisPtPr}, {axisPtPr}}); + }; + addMatrixHistograms(); + + if (cfgApplyEfficiencyCorrection) { + auto addCorrectedHistograms = [&](const std::string& directory, const AxisSpec& axisPtId) { + hist.add(directory + "hN1Matrix", "Efficiency-corrected Single-particle density", kTH2D, {{axisCent}, {axisPtId}}); + hist.add(directory + "hN2Matrix", "Efficiency-corrected Ordered-pair density", kTH3D, {{axisCent}, {axisPtId}, {axisPtId}}); + }; + addCorrectedHistograms("Corrected/Charged/", axisPtCh); + addCorrectedHistograms("Corrected/Pion/", axisPtPi); + addCorrectedHistograms("Corrected/Kaon/", axisPtKa); + addCorrectedHistograms("Corrected/Proton/", axisPtPr); + addCorrectedHistograms("CorrectedTruthMatched/Charged/", axisPtCh); + addCorrectedHistograms("CorrectedTruthMatched/Pion/", axisPtPi); + addCorrectedHistograms("CorrectedTruthMatched/Kaon/", axisPtKa); + addCorrectedHistograms("CorrectedTruthMatched/Proton/", axisPtPr); + } + + auto addGeneratedHistograms = [&]() { + // MC generated + hist.add("Gen/h_Counts", "Counts", kTH1D, {axisEvents}); + hist.add("Gen/h_VtxZ", "Vertex Z ", kTH1D, {axisVtxZ}); + hist.add("Gen/h_VtxZ_b", "Vertex Z ", kTH1D, {axisVtxZ}); + hist.add("Gen/h_NSim", "Truth Multiplicity TPC", kTH1D, {axisMultMC}); + hist.add("Gen/h2_NSim_NTPC", "Reco vs Truth Multiplicty TPC", kTH2D, {{axisMultMC}, {axisMultTPC}}); + hist.add("Gen/h2_NTPC_NSim", "Truth vs Reco Multiplicty TPC", kTH2D, {{axisMultTPC}, {axisMultMC}}); + + hist.add("Gen/Charged/h_PtTruth", "p_{T} ", kTH1D, {axisPt}); + hist.add("Gen/Charged/h_EtaTruth", "#eta ", kTH1D, {axisEta}); + hist.add("Gen/Charged/h_PhiTruth", "#phi ", kTH1D, {axisPhi}); + hist.add("Gen/Charged/h2_PtTruth_centFT0M", "p_{T} in centrality Classes ", kTH2D, {{axisCentFT0M}, {axisPt}}); + hist.add("Gen/Charged/h3_Pt_Eta_PhiTruth", "p_{T}, #eta, #phi ", kTHnSparseD, {{axisPt}, {axisEta}, {axisPhi}}); + + hist.addClone("Gen/Charged/", "Gen/Pion/"); + + hist.add("Gen/Pion/h_RapTruth", "y", kTH1D, {axisY}); + + hist.addClone("Gen/Pion/", "Gen/Kaon/"); + hist.addClone("Gen/Pion/", "Gen/Proton/"); + + hist.addClone("Analysis/Charged/", "Gen/Charged/"); + hist.addClone("Analysis/Pion/", "Gen/Pion/"); + hist.addClone("Analysis/Kaon/", "Gen/Kaon/"); + hist.addClone("Analysis/Proton/", "Gen/Proton/"); + + hist.add("Gen/Charged/h_PtTruth_e", "p_{T} ", kTH1D, {axisPtCh}); + hist.add("Gen/Charged/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPtCh}}); + hist.add("Gen/Charged/h3_Pt_EtaTruth_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtCh}, {axisCent}}); + + hist.add("Gen/Pion/h_PtTruth_e", "p_{T} ", kTH1D, {axisPtPi}); + hist.add("Gen/Pion/h_PtPosTruth", "p_{T} (positive) ", kTH1D, {axisPtPi}); + hist.add("Gen/Pion/h_PtNegTruth", "p_{T} (negative) ", kTH1D, {axisPtPi}); + hist.add("Gen/Pion/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPtPi}}); + hist.add("Gen/Pion/h3_Pt_EtaTruth_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPi}, {axisCent}}); + + hist.add("Gen/Kaon/h_PtTruth_e", "p_{T} ", kTH1D, {axisPtKa}); + hist.add("Gen/Kaon/h_PtPosTruth", "p_{T} (positive) ", kTH1D, {axisPtKa}); + hist.add("Gen/Kaon/h_PtNegTruth", "p_{T} (negative) ", kTH1D, {axisPtKa}); + hist.add("Gen/Kaon/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPtKa}}); + hist.add("Gen/Kaon/h3_Pt_EtaTruth_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtKa}, {axisCent}}); + + hist.add("Gen/Proton/h_PtTruth_e", "p_{T} ", kTH1D, {axisPtPr}); + hist.add("Gen/Proton/h_PtPosTruth", "p_{T} (positive) ", kTH1D, {axisPtPr}); + hist.add("Gen/Proton/h_PtNegTruth", "p_{T} (negative) ", kTH1D, {axisPtPr}); + hist.add("Gen/Proton/h2_Pt_EtaTruth", "p_{T} vs #eta", kTH2D, {{axisEta}, {axisPtPr}}); + hist.add("Gen/Proton/h3_Pt_EtaTruth_centFT0M", "p_{T} vs #eta in centrality classes", kTH3D, {{axisEta}, {axisPtPr}, {axisCent}}); + }; + addGeneratedHistograms(); hist.add("QA/h_collisions_info", "Collisions info", kTH1D, {axisCol}); hist.add("Gen/h_collisions_info", "Collisions info", kTH1D, {axisCol}); @@ -371,15 +586,27 @@ struct MeanPtFlucId { hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColSel8, "kSelColSel8"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColNoSameBunchPileup, "kSelColNoSameBunchPileup"); hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsVertexITSTPC, "kSelColIsVertexITSTPC"); + if (cfgLightIonCuts) { + hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsGoodITSLayersAll, "kSelColIsGoodITSLayersAll"); + hist.get(HIST("QA/after/h_counts_evSelCuts"))->GetXaxis()->SetBinLabel(SelCollisionLabels::kSelColIsGoodZvtxFT0vsPV, "kSelColIsGoodZvtxFT0vsPV"); + } } float centFT0M = 0.; int nTPC = 0, nFT0M = 0; + int nSim = 0; // Event selection cuts: template bool selRun3Col(T const& col) { + if (cfgCentFT0C) { + centFT0M = col.centFT0C(); + } else { + centFT0M = col.centFT0M(); + } + nTPC = col.multNTracksHasTPC(); + nFT0M = col.multFT0M(); hist.fill(HIST("QA/after/h_counts_evSelCuts"), kBeforeSelCol); if (cfgPosZ) { @@ -389,10 +616,6 @@ struct MeanPtFlucId { hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColPosZ); } - centFT0M = col.centFT0M(); - nTPC = col.multNTracksHasTPC(); - nFT0M = col.multFT0M(); - if (cfgSel8) { if (!col.sel8()) { return false; @@ -412,6 +635,21 @@ struct MeanPtFlucId { } hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColIsVertexITSTPC); } + if (cfgLightIonCuts) { + if (cfgIsGoodITSLayersAll) { + if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColIsGoodITSLayersAll); + } + + if (cfgIsGoodZvtxFT0vsPV) { + if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + hist.fill(HIST("QA/after/h_counts_evSelCuts"), kSelColIsGoodZvtxFT0vsPV); + } + } return true; } @@ -420,121 +658,113 @@ struct MeanPtFlucId { template bool selTrack(T const& track) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { return false; + } - if (track.pt() < cfgCutPtMin) + if (track.pt() < cfgCutPtMin) { return false; + } - if (track.pt() >= cfgCutPtMax) + if (track.pt() >= cfgCutPtMax) { return false; + } - if (track.sign() == 0) + if (track.sign() == 0) { return false; + } - if (std::fabs(track.dcaZ()) > cfgCutDcaZ) - return false; + const float maxDca = cfgDcaPtDepOffset + cfgDcaPtDepScale / track.pt(); + // if (std::abs(track.dcaXY()) >= maxDca) { + // return false; + // } - if (std::fabs(track.dcaZ()) > (0.0105 + 0.035 / std::pow(track.p(), 1.1))) + if (std::abs(track.dcaZ()) >= maxDca) { return false; + } - if (std::abs(track.eta()) >= cfgCutEta) + if (std::abs(track.eta()) >= cfgCutEta) { return false; + } return true; } - // Cuts to reject the tracks + // Rejection cuts to reject the tracks template bool rejectTracks(T const& track) { - if (((track.tpcNSigmaEl()) > -cfgCutNSig3 && - (track.tpcNSigmaEl()) < cfgCutNSig5) && - (std::fabs(track.tpcNSigmaPi()) > cfgCutNSig3 && - std::fabs(track.tpcNSigmaKa()) > cfgCutNSig3 && - std::fabs(track.tpcNSigmaPr()) > cfgCutNSig3)) { - return true; - } - - return false; + return ((track.tpcNSigmaEl()) > -cfgCutNSig3 && + (track.tpcNSigmaEl()) < cfgCutNSig5) && + (std::fabs(track.tpcNSigmaPi()) > cfgCutNSig3 && + std::fabs(track.tpcNSigmaKa()) > cfgCutNSig3 && + std::fabs(track.tpcNSigmaPr()) > cfgCutNSig3); } - template - bool identifyParticle(T const& track, float momThreshold) + // Share the corrected hypotheses between PID selection and selected-particle QA. + // The before-PID histograms intentionally retain raw values for calibration. + template + std::array getTofNSigmaForPid(T const& track) const { - - const int sp = static_cast(s1); - const int sq = static_cast(s2); - const int sr = static_cast(s3); - std::vector vTpcNSigma = {-999., track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; - std::vector vTofNSigma = {-999., track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; - bool isTofPidFlag = false, isTpcPidFlag = false; - - float nSigmaSelCut = 0.; - float nSigmaTofRejCut = 0.; - float nSigmaTpcRejCut = 0.; - if constexpr (s1 == PIDType::kProtons) { - nSigmaSelCut = cfgSelCutNSigPr; - nSigmaTofRejCut = std::fabs(vTofNSigma[sp]); - nSigmaTpcRejCut = std::fabs(vTpcNSigma[sp]); - } else if constexpr (s1 == PIDType::kKaons) { - nSigmaSelCut = cfgSelCutNSigKa; - nSigmaTofRejCut = std::fabs(vTofNSigma[sp]); - nSigmaTpcRejCut = std::fabs(vTpcNSigma[sp]); - } else if constexpr (s1 == PIDType::kPions) { - nSigmaSelCut = cfgSelCutNSigPi; - nSigmaTofRejCut = cfgRejCutNSigPi; - nSigmaTpcRejCut = cfgRejCutNSigPi; - } - + std::array values = {-999.f, track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; if (track.hasTOF()) { - if (std::fabs(vTofNSigma[sp]) < nSigmaSelCut && - std::fabs(vTofNSigma[sq]) > nSigmaTofRejCut && - std::fabs(vTofNSigma[sr]) > nSigmaTofRejCut) { - isTofPidFlag = true; - } - if (std::fabs(vTpcNSigma[sp]) < cfgCutNSig2) { - isTpcPidFlag = true; - } - } else { // select from TPC Only - if (track.p() >= momThreshold) { - return false; - } - if (std::fabs(vTpcNSigma[sp]) < nSigmaSelCut && - std::fabs(vTpcNSigma[sq]) > nSigmaTpcRejCut && - std::fabs(vTpcNSigma[sr]) > nSigmaTpcRejCut) { - isTofPidFlag = true; - isTpcPidFlag = true; - } + values[static_cast(PIDType::kPions)] -= cfgTofShiftPi.value; + values[static_cast(PIDType::kKaons)] -= cfgTofShiftKa.value; + values[static_cast(PIDType::kProtons)] -= cfgTofShiftPr.value; } - - if (isTofPidFlag && isTpcPidFlag) { - return true; // Track is identified as one of the particles - } - - return false; // Track is not identified as any of the particles + return values; } - // Get corrected weight for the track: - template - float getCorrectedWeight(T1 hWeightPt, T1 hPurePt, float pt, bool cfgWeightPt, bool cfgPurity) + // PID cuts for identified particles + template + bool identifyParticle(T const& track, PIDType species, PIDType reject1, PIDType reject2, float p, float momThreshold, const std::array& tofNSigma) { - float weight = 1.0; - float purity = 1.0; + const std::array tpcNSigma = {-999.f, track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + + const int selSpecies = static_cast(species); + const int rejSpecies1 = static_cast(reject1); + const int rejSpecies2 = static_cast(reject2); + + float tpcSelectionCut = 0.f; + float tofSelectionCut = 0.f; + + switch (species) { + case PIDType::kPions: + tpcSelectionCut = cfgSelCutNSigTpcPi; + tofSelectionCut = cfgSelCutNSigTofPi; + break; + case PIDType::kKaons: + tpcSelectionCut = cfgSelCutNSigTpcKa; + tofSelectionCut = cfgSelCutNSigTofKa; + break; + case PIDType::kProtons: + tpcSelectionCut = cfgSelCutNSigTpcPr; + tofSelectionCut = cfgSelCutNSigTofPr; + break; + default: + return false; + } - if (cfgPurity) { - purity = hPurePt->GetBinContent(hPurePt->FindBin(pt)); + if (track.hasTOF()) { + const float selectedTofNSigma = std::abs(tofNSigma[selSpecies]); + const bool passesTOF = selectedTofNSigma < tofSelectionCut && + std::abs(tofNSigma[rejSpecies1]) > selectedTofNSigma && + std::abs(tofNSigma[rejSpecies2]) > selectedTofNSigma; + const bool passesTPC = std::abs(tpcNSigma[selSpecies]) < cfgCutNSigTpcTof; + return passesTPC && passesTOF; } - if (cfgWeightPt) { - float weightPt = hWeightPt->GetBinContent(hWeightPt->FindBin(pt)); - weight = purity * weightPt; + if (p >= momThreshold) { + return false; } - return weight; + const float selTpcNSigma = std::abs(tpcNSigma[selSpecies]); + return selTpcNSigma < tpcSelectionCut && + std::abs(tpcNSigma[rejSpecies1]) > selTpcNSigma && + std::abs(tpcNSigma[rejSpecies2]) > selTpcNSigma; } - // Fill hist before selection cuts: + // Fill QA histograms before selection cuts: template void fillBeforeQAHistos(T const& col, U const& tracks) { @@ -554,7 +784,7 @@ struct MeanPtFlucId { hist.fill(HIST("QA/before/h_NFT0M"), col.multFT0M()); } - // Fill hist after selection cuts: + // Fill QA histograms after selection cuts: template void fillAfterQAHistos(T const& col) { @@ -567,16 +797,20 @@ struct MeanPtFlucId { hist.fill(HIST("QA/after/h2_NTPC_CentM"), centFT0M, nTPC); hist.fill(HIST("QA/after/p_NTPC_CentM"), centFT0M, nTPC); hist.fill(HIST("QA/after/p_NTPC_NFT0M"), nFT0M, nTPC); + hist.fill(HIST("QA/after/h2_NSim_NTPC"), nSim, nTPC); + hist.fill(HIST("QA/after/h2_NTPC_NSim"), nTPC, nSim); + hist.fill(HIST("QA/after/p_NTPC_NSim"), nTPC, nSim); } - // Fill Charged particles QA: + // Fill Charged particles QA histograms after selection cuts: template - void fillChargedQAHistos(T const& track, float centFT0M) + void fillChargedQAHistos(T const& track) { hist.fill(HIST("QA/Charged/h_Eta"), track.eta()); hist.fill(HIST("QA/Charged/h_Phi"), track.phi()); hist.fill(HIST("QA/Charged/h2_Pt_centFT0M"), centFT0M, track.pt()); - hist.fill(HIST("QA/Charged/h3_PtEtaPhi"), track.pt(), track.eta(), track.phi()); + hist.fill(HIST("QA/Charged/h3_Pt_Eta_Phi"), track.pt(), track.eta(), track.phi()); + hist.fill(HIST("QA/Charged/h3_Pt_Eta_centFT0M"), track.eta(), track.pt(), centFT0M); hist.fill(HIST("QA/Charged/h2_Pt_Eta"), track.eta(), track.pt()); hist.fill(HIST("QA/Charged/h_DcaZ"), track.dcaZ()); hist.fill(HIST("QA/Charged/h_DcaXY"), track.dcaXY()); @@ -588,159 +822,279 @@ struct MeanPtFlucId { hist.fill(HIST("QA/after/h_crossedTPC"), track.tpcNClsCrossedRows()); } - // Fill before PID cut QA hist: + // Fill identified QA histograms before PID cuts: template void fillBeforePIDQAHistos(T const& track) { - hist.fill(HIST("QA/before/h2_TOFSignal"), track.p(), track.beta()); hist.fill(HIST("QA/before/h2_TPCSignal"), track.p(), track.tpcSignal()); - hist.fill(HIST("QA/before/h2_pvsm2"), track.mass() * track.mass(), track.p()); + if (track.hasTOF()) { + hist.fill(HIST("QA/before/h2_TOFSignal"), track.p(), track.beta()); + hist.fill(HIST("QA/before/h2_pvsm2"), track.mass() * track.mass(), track.p()); + } - if (!track.hasTOF()) - hist.fill(HIST("QA/Pion/before/h2_TPCNsigma"), track.p(), track.tpcNSigmaPi()); - if (track.hasTOF()) + hist.fill(HIST("QA/Pion/before/h2_TPCNsigma"), track.p(), track.tpcNSigmaPi()); + if (!track.hasTOF()) { + hist.fill(HIST("QA/Pion/before/h2_TPCNsigma_nottof"), track.p(), track.tpcNSigmaPi()); + } + if (track.hasTOF()) { hist.fill(HIST("QA/Pion/before/h2_TPCNsigma_tof"), track.p(), track.tpcNSigmaPi()); - hist.fill(HIST("QA/Pion/before/h2_TOFNsigma"), track.p(), track.tofNSigmaPi()); - hist.fill(HIST("QA/Pion/before/h2_TpcTofNsigma"), track.tpcNSigmaPi(), track.tofNSigmaPi()); - if (!track.hasTOF()) - hist.fill(HIST("QA/Proton/before/h2_TPCNsigma"), track.p(), track.tpcNSigmaPr()); - if (track.hasTOF()) - hist.fill(HIST("QA/Proton/before/h2_TPCNsigma_tof"), track.p(), track.tpcNSigmaPr()); - hist.fill(HIST("QA/Proton/before/h2_TOFNsigma"), track.p(), track.tofNSigmaPr()); - hist.fill(HIST("QA/Proton/before/h2_TpcTofNsigma"), track.tpcNSigmaPr(), track.tofNSigmaPr()); - if (!track.hasTOF()) - hist.fill(HIST("QA/Kaon/before/h2_TPCNsigma"), track.p(), track.tpcNSigmaKa()); - if (track.hasTOF()) + hist.fill(HIST("QA/Pion/before/h2_TOFNsigma"), track.p(), track.tofNSigmaPi()); + hist.fill(HIST("QA/Pion/before/h2_TpcTofNsigma"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + + hist.fill(HIST("QA/Kaon/before/h2_TPCNsigma"), track.p(), track.tpcNSigmaKa()); + if (!track.hasTOF()) { + hist.fill(HIST("QA/Kaon/before/h2_TPCNsigma_nottof"), track.p(), track.tpcNSigmaKa()); + } + if (track.hasTOF()) { hist.fill(HIST("QA/Kaon/before/h2_TPCNsigma_tof"), track.p(), track.tpcNSigmaKa()); - hist.fill(HIST("QA/Kaon/before/h2_TOFNsigma"), track.p(), track.tofNSigmaKa()); - hist.fill(HIST("QA/Kaon/before/h2_TpcTofNsigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); - } + hist.fill(HIST("QA/Kaon/before/h2_TOFNsigma"), track.p(), track.tofNSigmaKa()); + hist.fill(HIST("QA/Kaon/before/h2_TpcTofNsigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } - // Moments Calculation: - void moments(float pt, float weight, double& Q1, double& Q2) - { - Q1 += pt * weight; - Q2 += pt * pt * weight * weight; + hist.fill(HIST("QA/Proton/before/h2_TPCNsigma"), track.p(), track.tpcNSigmaPr()); + if (!track.hasTOF()) { + hist.fill(HIST("QA/Proton/before/h2_TPCNsigma_nottof"), track.p(), track.tpcNSigmaPr()); + } + if (track.hasTOF()) { + hist.fill(HIST("QA/Proton/before/h2_TPCNsigma_tof"), track.p(), track.tpcNSigmaPr()); + hist.fill(HIST("QA/Proton/before/h2_TOFNsigma"), track.p(), track.tofNSigmaPr()); + hist.fill(HIST("QA/Proton/before/h2_TpcTofNsigma"), track.tpcNSigmaPr(), track.tofNSigmaPr()); + } } - template - void fillIdParticleQAHistos(T const& track, float rap, float nSigmaTPC, float nSigmaTOF, float centFT0M, T1 hWeightPt, T1 hPurePt, bool cfgWeightPtId, bool cfgPurityId, double& NW, double& NW2, double& Q1, double& Q2) + // Fill identified QA histograms after PID cuts: + template + void fillIdParticleQAHistos(T const& track, float ptMC, float etaMC, float rap, float nSigmaTPC, float nSigmaTOF, std::vector& tracks) { + float pt = track.pt(); float eta = track.eta(); float phi = track.phi(); - float weight = getCorrectedWeight(hWeightPt, hPurePt, pt, cfgWeightPtId, cfgPurityId); - - if (std::abs(weight) < cfgMinWeight) - return; - NW += weight; - NW2 += weight * weight; - moments(pt, weight, Q1, Q2); + tracks.push_back({pt}); if (track.sign() > 0) { - hist.fill(HIST(Dire[Mode]) + HIST("h_PtPos"), pt); + hist.fill(HIST(dire[Mode]) + HIST("h_PtPos"), pt); } if (track.sign() < 0) { - hist.fill(HIST(Dire[Mode]) + HIST("h_PtNeg"), pt); - } - - if (cfgWeightPtEtaId) - hist.fill(HIST(Dire[Mode]) + HIST("h2_Pt_Eta_weighted"), eta, pt, weight); - - hist.fill(HIST(Dire[Mode]) + HIST("h_Pt_weighted"), pt, weight); - hist.fill(HIST(Dire[Mode]) + HIST("h2_Pt_centFT0M"), centFT0M, pt); - hist.fill(HIST(Dire[Mode]) + HIST("h3_PtEtaPhi"), pt, eta, phi); - hist.fill(HIST(Dire[Mode]) + HIST("h2_Pt_Eta"), eta, pt); - hist.fill(HIST(Dire[Mode]) + HIST("h_Eta"), eta); - hist.fill(HIST(Dire[Mode]) + HIST("h_Phi"), phi); - hist.fill(HIST(Dire[Mode]) + HIST("h_Rap"), rap); - hist.fill(HIST(Dire[Mode]) + HIST("h_DcaZ"), track.dcaZ()); - hist.fill(HIST(Dire[Mode]) + HIST("h_DcaXY"), track.dcaXY()); - hist.fill(HIST(Dire[Mode]) + HIST("h2_DcaZ"), pt, track.dcaZ()); - hist.fill(HIST(Dire[Mode]) + HIST("h2_DcaXY"), pt, track.dcaXY()); - - hist.fill(HIST(Dire[Mode]) + HIST("h2_TPCNsigma"), track.p(), nSigmaTPC); - hist.fill(HIST(Dire[Mode]) + HIST("h2_TOFNsigma"), track.p(), nSigmaTOF); - hist.fill(HIST(Dire[Mode]) + HIST("h2_TpcTofNsigma"), nSigmaTPC, nSigmaTOF); - hist.fill(HIST(Dire[Mode]) + HIST("h2_TPCSignal"), track.p(), track.tpcSignal()); - hist.fill(HIST(Dire[Mode]) + HIST("innerParam/h2_TPCSignal"), track.tpcInnerParam(), track.tpcSignal()); - hist.fill(HIST(Dire[Mode]) + HIST("h2_TOFSignal"), track.p(), track.beta()); - hist.fill(HIST(Dire[Mode]) + HIST("h2_pvsm2"), track.mass() * track.mass(), track.p()); + hist.fill(HIST(dire[Mode]) + HIST("h_PtNeg"), pt); + } + + hist.fill(HIST(dire[Mode]) + HIST("h_Pt"), pt); + hist.fill(HIST(dire[Mode]) + HIST("h_Pt_e"), pt); + hist.fill(HIST(dire[Mode]) + HIST("h_PtMC"), ptMC); + hist.fill(HIST(dire[Mode]) + HIST("h2_Pt_PtMC"), ptMC, pt); + hist.fill(HIST(dire[Mode]) + HIST("h2_Pt_centFT0M"), centFT0M, pt); + hist.fill(HIST(dire[Mode]) + HIST("h3_Pt_Eta_Phi"), pt, eta, phi); + hist.fill(HIST(dire[Mode]) + HIST("h2_Pt_Eta"), eta, pt); + hist.fill(HIST(dire[Mode]) + HIST("h3_Pt_Eta_centFT0M"), eta, pt, centFT0M); + hist.fill(HIST(dire[Mode]) + HIST("h2_Pt_EtaMC"), etaMC, ptMC); + hist.fill(HIST(dire[Mode]) + HIST("h3_Pt_EtaMC_centFT0M"), etaMC, ptMC, centFT0M); + hist.fill(HIST(dire[Mode]) + HIST("h_Eta"), eta); + hist.fill(HIST(dire[Mode]) + HIST("h_Phi"), phi); + hist.fill(HIST(dire[Mode]) + HIST("h_Rap"), rap); + hist.fill(HIST(dire[Mode]) + HIST("h_DcaZ"), track.dcaZ()); + hist.fill(HIST(dire[Mode]) + HIST("h_DcaXY"), track.dcaXY()); + hist.fill(HIST(dire[Mode]) + HIST("h2_DcaZ"), pt, track.dcaZ()); + hist.fill(HIST(dire[Mode]) + HIST("h2_DcaXY"), pt, track.dcaXY()); + + hist.fill(HIST(dire[Mode]) + HIST("h2_TPCNsigma"), track.p(), nSigmaTPC); + hist.fill(HIST(dire[Mode]) + HIST("h2_TPCSignal"), track.p(), track.tpcSignal()); + hist.fill(HIST(dire[Mode]) + HIST("innerParam/h2_TPCSignal"), track.tpcInnerParam(), track.tpcSignal()); + if (track.hasTOF()) { + hist.fill(HIST(dire[Mode]) + HIST("h2_TOFNsigma"), track.p(), nSigmaTOF); + hist.fill(HIST(dire[Mode]) + HIST("h2_TpcTofNsigma"), nSigmaTPC, nSigmaTOF); + hist.fill(HIST(dire[Mode]) + HIST("h2_TOFSignal"), track.p(), track.beta()); + hist.fill(HIST(dire[Mode]) + HIST("h2_pvsm2"), track.mass() * track.mass(), track.p()); + } hist.fill(HIST("QA/after/h2_TPCSignal"), track.p(), track.tpcSignal()); hist.fill(HIST("QA/after/innerParam/h2_TPCSignal"), track.tpcInnerParam(), track.tpcSignal()); - hist.fill(HIST("QA/after/h2_TOFSignal"), track.p(), track.beta()); - hist.fill(HIST("QA/after/h2_pvsm2"), track.mass() * track.mass(), track.p()); + if (track.hasTOF()) { + hist.fill(HIST("QA/after/h2_TOFSignal"), track.p(), track.beta()); + hist.fill(HIST("QA/after/h2_pvsm2"), track.mass() * track.mass(), track.p()); + } } + // Fill Truth identified particles histograms template - void fillPtMCHist(bool cfgGen, float pt, float eta, float rap, float phi, float centFT0M, int pid, int pdgCodePos, int pdgCodeNeg) + void fillPtMCHist(bool cfgGen, float pt, float eta, float rap, float phi, int pid, int pdgCodePos, int pdgCodeNeg) { if (cfgGen) { - hist.fill(HIST(Dire[Mode]) + HIST("h_EtaTruth"), eta); - hist.fill(HIST(Dire[Mode]) + HIST("h_RapTruth"), rap); - hist.fill(HIST(Dire[Mode]) + HIST("h2_PtTruth_centFT0M"), centFT0M, pt); - hist.fill(HIST(Dire[Mode]) + HIST("h2_Pt_EtaTruth"), eta, pt); - hist.fill(HIST(Dire[Mode]) + HIST("h_PhiTruth"), phi); - hist.fill(HIST(Dire[Mode]) + HIST("h_PtEtaPhiTruth"), pt, eta, phi); + hist.fill(HIST(dire[Mode]) + HIST("h_PtTruth"), pt); + hist.fill(HIST(dire[Mode]) + HIST("h_PtTruth_e"), pt); + hist.fill(HIST(dire[Mode]) + HIST("h_EtaTruth"), eta); + hist.fill(HIST(dire[Mode]) + HIST("h_RapTruth"), rap); + hist.fill(HIST(dire[Mode]) + HIST("h_PhiTruth"), phi); + hist.fill(HIST(dire[Mode]) + HIST("h2_PtTruth_centFT0M"), centFT0M, pt); + hist.fill(HIST(dire[Mode]) + HIST("h3_Pt_EtaTruth_centFT0M"), eta, pt, centFT0M); + hist.fill(HIST(dire[Mode]) + HIST("h3_Pt_Eta_PhiTruth"), pt, eta, phi); } + hist.fill(HIST(dire[Mode]) + HIST("h2_Pt_EtaTruth"), eta, pt); + if (pid == pdgCodePos) { - hist.fill(HIST(Dire[Mode]) + HIST("h_PtPosTruth"), pt); + hist.fill(HIST(dire[Mode]) + HIST("h_PtPosTruth"), pt); } if (pid == pdgCodeNeg) { - hist.fill(HIST(Dire[Mode]) + HIST("h_PtNegTruth"), pt); + hist.fill(HIST(dire[Mode]) + HIST("h_PtNegTruth"), pt); } } template - void fillAnalysisHistos(bool cfgCorrection, float centFT0M, double nW, double nW2, double Q1, double Q2) + void fillMatrices(const std::vector& tracks, double ptMin) { - if (nW == 0) { - return; + const int nBins = nPtBins; + const double binWidth = (cfgCutPtMax - ptMin) / nBins; + matrixNInBin.assign(nBins, 0); + matrixOccupiedBins.clear(); + matrixOccupiedBins.reserve(std::min(static_cast(nBins), tracks.size())); + + for (const auto& track : tracks) { + const int ptBin = static_cast((track.pt - ptMin) / binWidth); + if (ptBin < 0 || ptBin >= nBins) { + continue; + } + if (matrixNInBin[ptBin] == 0) { + matrixOccupiedBins.push_back(ptBin); + } + ++matrixNInBin[ptBin]; } - hist.fill(HIST(Dire[Mode]) + HIST("h_Mult"), nW); + for (const int& ptBin : matrixOccupiedBins) { + const double ptCenter = ptMin + (ptBin + 0.5) * binWidth; + hist.fill(HIST(dire[Mode]) + HIST("hN1Matrix"), centFT0M, ptCenter, static_cast(matrixNInBin[ptBin])); + } - double nPair = 0., meanPt = 0., twopart1 = 0., twopart = 0.; + for (size_t i = 0; i < matrixOccupiedBins.size(); ++i) { + const int ptBin1 = matrixOccupiedBins[i]; + const double ptCenter1 = ptMin + (ptBin1 + 0.5) * binWidth; + const double nPairDiagonal = static_cast(matrixNInBin[ptBin1]) * (matrixNInBin[ptBin1] - 1); + if (nPairDiagonal > 0.0) { + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter1, nPairDiagonal); + } - if (cfgCorrection) { - nPair = nW * nW - nW2; - } else { - if (nW > 1) { - nPair = nW * (nW - 1); + for (size_t j = i + 1; j < matrixOccupiedBins.size(); ++j) { + const int ptBin2 = matrixOccupiedBins[j]; + const double ptCenter2 = ptMin + (ptBin2 + 0.5) * binWidth; + const double nPair = static_cast(matrixNInBin[ptBin1]) * matrixNInBin[ptBin2]; + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter2, nPair); + hist.fill(HIST(dire[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter2, ptCenter1, nPair); } } + } + + bool getEfficiencyWeight(PIDType species, double pt, double eta, double& weight) const + { + TH3* weightMap = nullptr; + switch (species) { + case PIDType::kNone: + weightMap = efficiencyWeightCh; + break; + case PIDType::kPions: + weightMap = efficiencyWeightPi; + break; + case PIDType::kKaons: + weightMap = efficiencyWeightKa; + break; + case PIDType::kProtons: + weightMap = efficiencyWeightPr; + break; + } + if (!weightMap) { + return false; + } + + const int etaBin = weightMap->GetXaxis()->FindFixBin(eta); + const int ptBin = weightMap->GetYaxis()->FindFixBin(pt); + const int centBin = weightMap->GetZaxis()->FindFixBin(centFT0M); + if (etaBin < 1 || etaBin > weightMap->GetNbinsX() || + ptBin < 1 || ptBin > weightMap->GetNbinsY() || + centBin < 1 || centBin > weightMap->GetNbinsZ()) { + return false; + } + + weight = weightMap->GetBinContent(etaBin, ptBin, centBin); + return std::isfinite(weight) && weight > 0.; + } - if (nPair > 0) { - meanPt = Q1 / nW; - twopart1 = (Q1 * Q1 - Q2); - twopart = twopart1 / nPair; + template + void fillCorrectedMatrices(const std::vector& tracks, double ptMin) + { + const int nBins = nPtBins; + const double binWidth = (cfgCutPtMax - ptMin) / nBins; + matrixWInBin.assign(nBins, 0.); + matrixW2InBin.assign(nBins, 0.); + matrixOccupiedBins.clear(); + matrixOccupiedBins.reserve(std::min(static_cast(nBins), tracks.size())); - hist.fill(HIST(Dire[Mode]) + HIST("h_mean_pT_Mult_var"), centFT0M, meanPt); - hist.fill(HIST(Dire[Mode]) + HIST("h_twopart_Mult_var"), centFT0M, twopart); - hist.fill(HIST(Dire[Mode]) + HIST("p_mean_pT_CentFT0M"), centFT0M, meanPt); - hist.fill(HIST(Dire[Mode]) + HIST("p_twopart_CentFT0M"), centFT0M, twopart); - hist.fill(HIST(Dire[Mode]) + HIST("h_twopart1_CentFT0M"), centFT0M, twopart1); + for (const auto& track : tracks) { + const int ptBin = static_cast((track.pt - ptMin) / binWidth); + if (ptBin < 0 || ptBin >= nBins) { + continue; + } + if (matrixWInBin[ptBin] == 0.) { + matrixOccupiedBins.push_back(ptBin); + } + matrixWInBin[ptBin] += track.weight; + matrixW2InBin[ptBin] += track.weight * track.weight; } - hist.fill(HIST(Dire[Mode]) + HIST("h_Mult"), nW); - hist.fill(HIST(Dire[Mode]) + HIST("h_N_CentFT0M"), centFT0M, nW); - hist.fill(HIST(Dire[Mode]) + HIST("h_Npair_CentFT0M"), centFT0M, nPair); - hist.fill(HIST(Dire[Mode]) + HIST("h_Q1_CentFT0M"), centFT0M, Q1); - hist.fill(HIST(Dire[Mode]) + HIST("h_Q2_CentFT0M"), centFT0M, Q2); + for (const int& ptBin : matrixOccupiedBins) { + const double ptCenter = ptMin + (ptBin + 0.5) * binWidth; + if constexpr (TruthMatched) { + hist.fill(HIST(TruthMatchedCorrectedDirectories[Mode]) + HIST("hN1Matrix"), centFT0M, ptCenter, matrixWInBin[ptBin]); + } else { + hist.fill(HIST(CorrectedDirectories[Mode]) + HIST("hN1Matrix"), centFT0M, ptCenter, matrixWInBin[ptBin]); + } + } + + for (size_t i = 0; i < matrixOccupiedBins.size(); ++i) { + const int ptBin1 = matrixOccupiedBins[i]; + const double ptCenter1 = ptMin + (ptBin1 + 0.5) * binWidth; + const double diagonalPairDensity = matrixWInBin[ptBin1] * matrixWInBin[ptBin1] - matrixW2InBin[ptBin1]; + if (diagonalPairDensity > 0.) { + if constexpr (TruthMatched) { + hist.fill(HIST(TruthMatchedCorrectedDirectories[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter1, diagonalPairDensity); + } else { + hist.fill(HIST(CorrectedDirectories[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter1, diagonalPairDensity); + } + } + + for (size_t j = i + 1; j < matrixOccupiedBins.size(); ++j) { + const int ptBin2 = matrixOccupiedBins[j]; + const double ptCenter2 = ptMin + (ptBin2 + 0.5) * binWidth; + const double pairDensity = matrixWInBin[ptBin1] * matrixWInBin[ptBin2]; + if constexpr (TruthMatched) { + hist.fill(HIST(TruthMatchedCorrectedDirectories[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter2, pairDensity); + hist.fill(HIST(TruthMatchedCorrectedDirectories[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter2, ptCenter1, pairDensity); + } else { + hist.fill(HIST(CorrectedDirectories[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter1, ptCenter2, pairDensity); + hist.fill(HIST(CorrectedDirectories[Mode]) + HIST("hN2Matrix"), centFT0M, ptCenter2, ptCenter1, pairDensity); + } + } + } } + // Fill reconstructed histograms for charged and identified particles template void fillRecoHistos(C const& col, T const& tracks) { - double nChW = 0., nChW2 = 0., q1Ch = 0., q2Ch = 0.; - float wghtCh = 1.0; - double nPiW = 0., nPiW2 = 0., q1Pi = 0., q2Pi = 0.; - double nKaW = 0., nKaW2 = 0., q1Ka = 0., q2Ka = 0.; - double nPrW = 0., nPrW2 = 0., q1Pr = 0., q2Pr = 0.; - float pt = 0., eta = 0., phi = 0.; + chargedTracks.clear(); + pionTracks.clear(); + kaonTracks.clear(); + protonTracks.clear(); + chargedTracksCorrected.clear(); + pionTracksCorrected.clear(); + kaonTracksCorrected.clear(); + protonTracksCorrected.clear(); + chargedTracksTruthMatchedCorrected.clear(); + pionTracksTruthMatchedCorrected.clear(); + kaonTracksTruthMatchedCorrected.clear(); + protonTracksTruthMatchedCorrected.clear(); + + float pt = 0., eta = 0., phi = 0., p = 0.; hist.fill(HIST("QA/h_collisions_info"), kTotCol); @@ -754,13 +1108,19 @@ struct MeanPtFlucId { fillAfterQAHistos(col); + double nCorr = cfgP2corr * nTPC * nTPC + cfgP1corr * nTPC + cfgP0corr; + hist.fill(HIST("QA/after/h_Ncorr"), nCorr); + hist.fill(HIST("QA/after/h2_Ncorr_NSim"), nCorr, nSim); + hist.fill(HIST("QA/after/h2_Ncorr_CentM"), centFT0M, nCorr); + for (auto const& track : tracks) { float nSigmaTPCPi = track.tpcNSigmaPi(); float nSigmaTPCKa = track.tpcNSigmaKa(); float nSigmaTPCPr = track.tpcNSigmaPr(); - float nSigmaTOFPi = track.tofNSigmaPi(); - float nSigmaTOFKa = track.tofNSigmaKa(); - float nSigmaTOFPr = track.tofNSigmaPr(); + const auto tofNSigma = getTofNSigmaForPid(track); + const float nSigmaTOFPi = tofNSigma[static_cast(PIDType::kPions)]; + const float nSigmaTOFKa = tofNSigma[static_cast(PIDType::kKaons)]; + const float nSigmaTOFPr = tofNSigma[static_cast(PIDType::kProtons)]; float rapPi = track.rapidity(MassPiPlus); float rapKa = track.rapidity(MassKPlus); float rapPr = track.rapidity(MassProton); @@ -779,172 +1139,282 @@ struct MeanPtFlucId { hist.fill(HIST("Tracks/h_tracks_info"), kAllSelPassed); pt = track.pt(); + eta = track.eta(); + phi = track.phi(); + p = track.p(); + float ptMC = pt; + float etaMC = eta; if constexpr (RecoFlag) { hist.fill(HIST("Tracks/h2_tracks_pid_before_sel"), track.mcParticle().pdgCode(), track.pt()); auto mc = track.template mcParticle_as(); - pt = mc.pt(); - eta = mc.eta(); - phi = mc.phi(); + ptMC = mc.pt(); + etaMC = mc.eta(); if (mc.isPhysicalPrimary()) { hist.fill(HIST("QA/after/h_DCAxy_primary"), track.dcaXY()); hist.fill(HIST("QA/after/h_DCAz_primary"), track.dcaZ()); + hist.fill(HIST("QA/Charged/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Charged/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kNone, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched charged efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + chargedTracksTruthMatchedCorrected.push_back({pt, weight}); + } } else { hist.fill(HIST("QA/after/h_DCAxy_secondary"), track.dcaXY()); hist.fill(HIST("QA/after/h_DCAz_secondary"), track.dcaZ()); } } - // Charged particles: - wghtCh = getCorrectedWeight(hWeightPt, hPurePt, pt, cfgWeightPtCh, false); + if (cfgApplyEfficiencyCorrection) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kNone, pt, eta, weight)) { + LOGF(fatal, "Undefined charged-particle efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + chargedTracksCorrected.push_back({pt, weight}); + } + chargedTracks.push_back({pt}); - if (std::abs(wghtCh) < cfgMinWeight) - return; + hist.fill(HIST("QA/Charged/h_Pt"), pt); + hist.fill(HIST("QA/Charged/h_Pt_e"), pt); + hist.fill(HIST("QA/Charged/h_PtMC"), ptMC); + hist.fill(HIST("QA/Charged/h2_Pt_PtMC"), ptMC, pt); + hist.fill(HIST("QA/Charged/h2_Pt_EtaMC"), etaMC, ptMC); + hist.fill(HIST("QA/Charged/h3_Pt_EtaMC_centFT0M"), etaMC, ptMC, centFT0M); - nChW += wghtCh; - nChW2 += wghtCh * wghtCh; - moments(pt, wghtCh, q1Ch, q2Ch); - hist.fill(HIST("QA/Charged/h_Pt"), track.pt()); - fillChargedQAHistos(track, centFT0M); + fillChargedQAHistos(track); fillBeforePIDQAHistos(track); - // identified particles: if (cfgRejTrk && rejectTracks(track)) { continue; } - auto selIDPion = identifyParticle(track, cfgCutPiThrsldP); - auto selIDKaon = identifyParticle(track, cfgCutKaThrsldP); - auto selIDProton = identifyParticle(track, cfgCutPrThrsldP); - if (selIDPion && pt >= cfgCutPiPtMin) { - hist.fill(HIST("QA/Pion/h_Pt"), track.pt()); - fillIdParticleQAHistos(track, rapPi, nSigmaTPCPi, nSigmaTOFPi, centFT0M, hWeightPtPi, hPurePtPi, cfgWeightPtId, cfgPurityId, nPiW, nPiW2, q1Pi, q2Pi); + auto selIDPion = identifyParticle(track, PIDType::kPions, PIDType::kKaons, PIDType::kProtons, p, cfgCutPiThrsldP, tofNSigma); + auto selIDKaon = identifyParticle(track, PIDType::kKaons, PIDType::kPions, PIDType::kProtons, p, cfgCutKaThrsldP, tofNSigma); + auto selIDProton = identifyParticle(track, PIDType::kProtons, PIDType::kPions, PIDType::kKaons, p, cfgCutPrThrsldP, tofNSigma); + + if (selIDPion && track.pt() >= cfgCutPiPtMin) { + if (cfgApplyEfficiencyCorrection) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kPions, pt, eta, weight)) { + LOGF(fatal, "Undefined pion efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + pionTracksCorrected.push_back({pt, weight}); + } + fillIdParticleQAHistos(track, ptMC, etaMC, rapPi, nSigmaTPCPi, nSigmaTOFPi, pionTracks); } - if (selIDKaon && pt >= cfgCutKaPtMin) { - hist.fill(HIST("QA/Kaon/h_Pt"), track.pt()); - fillIdParticleQAHistos(track, rapKa, nSigmaTPCKa, nSigmaTOFKa, centFT0M, hWeightPtKa, hPurePtKa, cfgWeightPtId, cfgPurityId, nKaW, nKaW2, q1Ka, q2Ka); + if (selIDKaon && track.pt() >= cfgCutKaPtMin) { + if (cfgApplyEfficiencyCorrection) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kKaons, pt, eta, weight)) { + LOGF(fatal, "Undefined kaon efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + kaonTracksCorrected.push_back({pt, weight}); + } + fillIdParticleQAHistos(track, ptMC, etaMC, rapKa, nSigmaTPCKa, nSigmaTOFKa, kaonTracks); } - if (selIDProton && pt >= cfgCutPrPtMin) { - hist.fill(HIST("QA/Proton/h_Pt"), track.pt()); - fillIdParticleQAHistos(track, rapPr, nSigmaTPCPr, nSigmaTOFPr, centFT0M, hWeightPtPr, hPurePtPr, cfgWeightPtId, cfgPurityId, nPrW, nPrW2, q1Pr, q2Pr); + if (selIDProton && track.pt() >= cfgCutPrPtMin) { + if (cfgApplyEfficiencyCorrection) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kProtons, pt, eta, weight)) { + LOGF(fatal, "Undefined proton efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + protonTracksCorrected.push_back({pt, weight}); + } + fillIdParticleQAHistos(track, ptMC, etaMC, rapPr, nSigmaTPCPr, nSigmaTOFPr, protonTracks); } if constexpr (RecoFlag) { auto mc = track.template mcParticle_as(); int pid = mc.pdgCode(); - if (selIDPion && pt >= cfgCutPiPtMin) { + if (selIDPion && track.pt() >= cfgCutPiPtMin) { if (std::abs(pid) == kPiPlus) { - hist.fill(HIST("QA/Pion/h_PtTruth"), pt); - fillPtMCHist(false, pt, eta, rapPi, phi, centFT0M, pid, kPiPlus, kPiMinus); + hist.fill(HIST("QA/Pion/h_PtTruth"), track.pt()); + fillPtMCHist(false, pt, eta, rapPi, phi, pid, kPiPlus, kPiMinus); if (mc.isPhysicalPrimary()) { - hist.fill(HIST("QA/Pion/h_PtTruth_primary"), pt); + hist.fill(HIST("QA/Pion/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Pion/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); + hist.fill(HIST("QA/Pion/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kPions, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched pion efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + pionTracksTruthMatchedCorrected.push_back({pt, weight}); + } } else { - hist.fill(HIST("QA/Pion/h_PtTruth_secondary"), pt); + hist.fill(HIST("QA/Pion/h_PtTruth_secondary"), track.pt()); } } } - if (selIDKaon && pt >= cfgCutKaPtMin) { + if (selIDKaon && track.pt() >= cfgCutKaPtMin) { if (std::abs(pid) == kKPlus) { - hist.fill(HIST("QA/Kaon/h_PtTruth"), pt); - fillPtMCHist(false, pt, eta, rapKa, phi, centFT0M, pid, kKPlus, kKMinus); + hist.fill(HIST("QA/Kaon/h_PtTruth"), track.pt()); + fillPtMCHist(false, pt, eta, rapKa, phi, pid, kKPlus, kKMinus); if (mc.isPhysicalPrimary()) { - hist.fill(HIST("QA/Kaon/h_PtTruth_primary"), pt); + hist.fill(HIST("QA/Kaon/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Kaon/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); + hist.fill(HIST("QA/Kaon/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kKaons, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched kaon efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + kaonTracksTruthMatchedCorrected.push_back({pt, weight}); + } } else { - hist.fill(HIST("QA/Kaon/h_PtTruth_secondary"), pt); + hist.fill(HIST("QA/Kaon/h_PtTruth_secondary"), track.pt()); } } } - if (selIDProton && pt >= cfgCutPrPtMin) { + if (selIDProton && track.pt() >= cfgCutPrPtMin) { if (std::abs(pid) == kProton) { - hist.fill(HIST("QA/Proton/h_PtTruth"), pt); - fillPtMCHist(false, pt, eta, rapPr, phi, centFT0M, pid, kProton, kProtonBar); + hist.fill(HIST("QA/Proton/h_PtTruth"), track.pt()); + fillPtMCHist(false, pt, eta, rapPr, phi, pid, kProton, kProtonBar); if (mc.isPhysicalPrimary()) { - hist.fill(HIST("QA/Proton/h_PtTruth_primary"), pt); + hist.fill(HIST("QA/Proton/h_PtTruth_primary"), track.pt()); + hist.fill(HIST("QA/Proton/h3_Pt_EtaTruth_primary_centFT0M"), track.eta(), track.pt(), centFT0M); + hist.fill(HIST("QA/Proton/h2_Pt_EtaTruth_primary"), track.eta(), track.pt()); + if (cfgApplyEfficiencyCorrection && !cfgUseCombinedPurityEfficiencyWeight) { + double weight = 0.; + if (!getEfficiencyWeight(PIDType::kProtons, pt, eta, weight)) { + LOGF(fatal, "Undefined truth-matched proton efficiency weight at pT=%g, eta=%g, centrality=%g", pt, eta, centFT0M); + } + protonTracksTruthMatchedCorrected.push_back({pt, weight}); + } } else { - hist.fill(HIST("QA/Proton/h_PtTruth_secondary"), pt); + hist.fill(HIST("QA/Proton/h_PtTruth_secondary"), track.pt()); } } } } } - fillAnalysisHistos(cfgCorrection, centFT0M, nChW, nChW2, q1Ch, q2Ch); - fillAnalysisHistos(cfgCorrection, centFT0M, nPiW, nPiW2, q1Pi, q2Pi); - fillAnalysisHistos(cfgCorrection, centFT0M, nKaW, nKaW2, q1Ka, q2Ka); - fillAnalysisHistos(cfgCorrection, centFT0M, nPrW, nPrW2, q1Pr, q2Pr); + fillMatrices(chargedTracks, cfgCutPtMin); + fillMatrices(pionTracks, cfgCutPiPtMin); + fillMatrices(kaonTracks, cfgCutKaPtMin); + fillMatrices(protonTracks, cfgCutPrPtMin); + + if (cfgApplyEfficiencyCorrection) { + fillCorrectedMatrices(chargedTracksCorrected, cfgCutPtMin); + fillCorrectedMatrices(pionTracksCorrected, cfgCutPiPtMin); + fillCorrectedMatrices(kaonTracksCorrected, cfgCutKaPtMin); + fillCorrectedMatrices(protonTracksCorrected, cfgCutPrPtMin); + if constexpr (RecoFlag) { + if (!cfgUseCombinedPurityEfficiencyWeight) { + fillCorrectedMatrices(chargedTracksTruthMatchedCorrected, cfgCutPtMin); + fillCorrectedMatrices(pionTracksTruthMatchedCorrected, cfgCutPiPtMin); + fillCorrectedMatrices(kaonTracksTruthMatchedCorrected, cfgCutKaPtMin); + fillCorrectedMatrices(protonTracksTruthMatchedCorrected, cfgCutPrPtMin); + } + } + } + } + + template + int countSimMultiplicity(M const& mcParticles) + { + int multiplicity = 0; + for (auto const& mcPart : mcParticles) { + if (!mcPart.isPhysicalPrimary() || std::abs(mcPart.eta()) >= cfgCutEta) { + continue; + } + auto* particle = pdg->GetParticle(mcPart.pdgCode()); + if (particle && particle->Charge() != 0.) { + ++multiplicity; + } + } + return multiplicity; + } + + bool passesNtpcEventCut() + { + if (!cfgNtpcEventCut) { + return true; + } + const float lower = cfgP2L * nSim * nSim + cfgP1L * nSim - cfgP0L; + const float upper = cfgP2U * nSim * nSim + cfgP1U * nSim + cfgP0U; + return nTPC >= lower && nTPC <= upper; } template void fillGenHistos(C const& mcCol, M const& mcParticles) { - int nSim = 0; - double nChSim = 0., q1ChSim = 0., q2ChSim = 0.; - double nPiSim = 0., q1PiSim = 0., q2PiSim = 0.; - double nKaSim = 0., q1KaSim = 0., q2KaSim = 0.; - double nPrSim = 0., q1PrSim = 0., q2PrSim = 0.; - float pt = 0, eta = 0, phi = 0, rap = 0; + chargedTracksGen.clear(); + pionTracksGen.clear(); + kaonTracksGen.clear(); + protonTracksGen.clear(); + + nSim = 0; + float pt = 0., eta = 0, phi = 0., rap = 0.; for (auto const& mcPart : mcParticles) { if (!mcPart.isPhysicalPrimary()) { continue; } - auto pid = mcPart.pdgCode(); - if (std::abs(pid) != kElectron && std::abs(pid) != kMuonMinus && std::abs(pid) != kPiPlus && std::abs(pid) != kKPlus && std::abs(pid) != kProton) { + auto* particle = pdg->GetParticle(mcPart.pdgCode()); + + if (!particle) { + continue; + } + + if (particle->Charge() == 0) { continue; } pt = mcPart.pt(); eta = mcPart.eta(); phi = mcPart.phi(); + if (std::abs(eta) < cfgCutEta) { nSim++; } if (pt >= cfgCutPtMin && pt < cfgCutPtMax && std::abs(eta) < cfgCutEta) { - nChSim++; - moments(pt, 1.0, q1ChSim, q2ChSim); + chargedTracksGen.push_back({pt}); hist.fill(HIST("Gen/Charged/h_PtTruth"), pt); + hist.fill(HIST("Gen/Charged/h_PtTruth_e"), pt); hist.fill(HIST("Gen/Charged/h_EtaTruth"), eta); hist.fill(HIST("Gen/Charged/h_PhiTruth"), phi); hist.fill(HIST("Gen/Charged/h2_Pt_EtaTruth"), eta, pt); hist.fill(HIST("Gen/Charged/h2_PtTruth_centFT0M"), centFT0M, pt); - hist.fill(HIST("Gen/Charged/h_PtEtaPhiTruth"), pt, eta, phi); + hist.fill(HIST("Gen/Charged/h3_Pt_EtaTruth_centFT0M"), eta, pt, centFT0M); + hist.fill(HIST("Gen/Charged/h3_Pt_Eta_PhiTruth"), pt, eta, phi); rap = mcPart.y(); - if (std::abs(pid) == kPiPlus && mcPart.pt() > cfgCutPiPtMin) { - nPiSim++; - moments(pt, 1.0, q1PiSim, q2PiSim); - hist.fill(HIST("Gen/Pion/h_PtTruth"), pt); - fillPtMCHist(true, pt, eta, rap, phi, centFT0M, pid, kPiMinus, kPiMinus); + auto pid = mcPart.pdgCode(); + if (std::abs(pid) == kPiPlus && pt >= cfgCutPiPtMin) { + pionTracksGen.push_back({pt}); + fillPtMCHist(true, pt, eta, rap, phi, pid, kPiPlus, kPiMinus); } - if (std::abs(pid) == kKPlus && mcPart.pt() > cfgCutKaPtMin) { - nKaSim++; - moments(pt, 1.0, q1KaSim, q2KaSim); - hist.fill(HIST("Gen/Kaon/h_PtTruth"), pt); - fillPtMCHist(true, pt, eta, rap, phi, centFT0M, pid, kKMinus, kKMinus); + if (std::abs(pid) == kKPlus && pt >= cfgCutKaPtMin) { + kaonTracksGen.push_back({pt}); + fillPtMCHist(true, pt, eta, rap, phi, pid, kKPlus, kKMinus); } - if (std::abs(pid) == kProton && mcPart.pt() > cfgCutPrPtMin) { - nPrSim++; - moments(pt, 1.0, q1PrSim, q2PrSim); - hist.fill(HIST("Gen/Proton/h_PtTruth"), pt); - fillPtMCHist(true, pt, eta, rap, phi, centFT0M, pid, kProtonBar, kProtonBar); + if (std::abs(pid) == kProton && pt >= cfgCutPrPtMin) { + protonTracksGen.push_back({pt}); + fillPtMCHist(true, pt, eta, rap, phi, pid, kProton, kProtonBar); } } } - fillAnalysisHistos(false, centFT0M, nChSim, nChSim, q1ChSim, q2ChSim); - fillAnalysisHistos(false, centFT0M, nPiSim, nPiSim, q1PiSim, q2PiSim); - fillAnalysisHistos(false, centFT0M, nKaSim, nKaSim, q1KaSim, q2KaSim); - fillAnalysisHistos(false, centFT0M, nPrSim, nPrSim, q1PrSim, q2PrSim); + + fillMatrices(chargedTracksGen, cfgCutPtMin); + fillMatrices(pionTracksGen, cfgCutPiPtMin); + fillMatrices(kaonTracksGen, cfgCutKaPtMin); + fillMatrices(protonTracksGen, cfgCutPrPtMin); + hist.fill(HIST("Gen/h_Counts"), 2); hist.fill(HIST("Gen/h_VtxZ"), mcCol.posZ()); hist.fill(HIST("Gen/h_NSim"), nSim); - hist.fill(HIST("Gen/h2_NTPC_NSim"), nSim, nTPC); + hist.fill(HIST("Gen/h2_NSim_NTPC"), nSim, nTPC); + hist.fill(HIST("Gen/h2_NTPC_NSim"), nTPC, nSim); } template void analyzeMC(M const& mcCol, C const& cols, T const& tracks, P const& mcParts) { - // fillBeforeQAHistos(cols.begin(), tracks); hist.fill(HIST("Gen/h_VtxZ_b"), mcCol.posZ()); int nRecCols = cols.size(); if (nRecCols == 0) { @@ -964,17 +1434,25 @@ struct MeanPtFlucId { hist.fill(HIST("Gen/h_collisions_info"), kPassSelCol); hist.fill(HIST("Gen/h2_collision_posZ"), mcCol.posZ(), cols.begin().posZ()); + // The generated and reconstructed moments must be evaluated for the same + // event ensemble. Determine nSim and apply the correlated multiplicity cut + // before filling either level. + nSim = countSimMultiplicity(mcParts); + if (!passesNtpcEventCut()) { + return; + } + auto sTracks = tracks.sliceBy(perCollision, cols.begin().globalIndex()); - fillRecoHistos(cols.begin(), sTracks); fillGenHistos(mcCol, mcParts); + fillRecoHistos(cols.begin(), sTracks); } using MyAllTracks = soa::Join; - using MyRun3Collisions = soa::Join; - using MyRun3MCCollisions = soa::Join; + using MyRun3Collisions = soa::Join; + using MyRun3MCCollisions = soa::Join; using MyMCTracks = soa::Join(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGCF/EbyEFluctuations/Tasks/MeanptFluctuations.cxx b/PWGCF/EbyEFluctuations/Tasks/meanptFluctuations.cxx similarity index 86% rename from PWGCF/EbyEFluctuations/Tasks/MeanptFluctuations.cxx rename to PWGCF/EbyEFluctuations/Tasks/meanptFluctuations.cxx index e6a2d4c1225..6181d4a8b3f 100644 --- a/PWGCF/EbyEFluctuations/Tasks/MeanptFluctuations.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/meanptFluctuations.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file MeanptFluctuations.cxx +/// \file meanptFluctuations.cxx /// \brief Task for analyzing fluctuation upto fourth order of inclusive hadrons /// \author Swati Saha @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -56,9 +55,9 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; -struct MeanptFluctuationsAnalysis { +struct MeanptFluctuations { // MC Configurable cfgIsMC{"cfgIsMC", true, "Run MC"}; @@ -89,6 +88,7 @@ struct MeanptFluctuationsAnalysis { Configurable cfgEvSelkNoTimeFrameBorder{"cfgEvSelkNoTimeFrameBorder", true, "TimeFrame border event selection cut"}; Configurable cfgEvSelUseGoodZvtxFT0vsPV{"cfgEvSelUseGoodZvtxFT0vsPV", true, "GoodZvertex and FT0 vs PV cut"}; Configurable cfgCentralityEstimator{"cfgCentralityEstimator", 1, "Centrlaity estimatore choice: 1-->FT0C, 2-->FT0A; 3-->FT0M, 4-->FV0A"}; + Configurable cfgUseParticlePDGsInProcessMcReco{"cfgUseParticlePDGsInProcessMcReco", true, "Check partcile PDG codes in reco"}; // pT dep DCAxy and DCAz cuts Configurable cfgUsePtDepDCAxy{"cfgUsePtDepDCAxy", true, "Use pt-dependent DCAxy cut"}; @@ -144,7 +144,7 @@ struct MeanptFluctuationsAnalysis { Filter trackFilter = (nabs(aod::track::eta) < cfgCutPreSelEta) && (aod::track::pt > cfgCutPtLower) && (aod::track::pt < cfgCutPreSelPt) && (requireGlobalTrackInFilter()) && (aod::track::tpcChi2NCl < cfgCutTpcChi2NCl) && (aod::track::itsChi2NCl < cfgCutItsChi2NCl) && (nabs(aod::track::dcaZ) < cfgCutTrackDcaZ); // Connect to ccdb - Service ccdb; + Service ccdb{}; Configurable ccdbnolaterthan{"ccdbnolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable ccdburl{"ccdburl", "http://ccdb-test.cern.ch:8080", "url of the ccdb repository"}; @@ -225,6 +225,7 @@ struct MeanptFluctuationsAnalysis { histos.add("AnalysisProfiles/Prof_skew_t1", "", {HistType::kTProfile2D, {centAxis, multAxis}}); histos.add("AnalysisProfiles/Prof_kurt_t1", "", {HistType::kTProfile2D, {centAxis, multAxis}}); histos.add("AnalysisProfiles/Hist2D_Nch_centrality", "", {HistType::kTH2D, {centAxis, multAxis}}); + histos.add("AnalysisProfiles/Hist2D_Ngen_centrality", "", {HistType::kTH2D, {centAxis, multAxis}}); histos.add("AnalysisProfiles/Hist2D_meanpt_centrality", "", {HistType::kTH2D, {centAxis, meanpTAxis}}); // Analysis Profiles for error (reconstructed data) @@ -319,21 +320,21 @@ struct MeanptFluctuationsAnalysis { cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutLow->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutHigh->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutLow->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutHigh->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutLow->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutLow->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutHigh->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); @@ -350,7 +351,7 @@ struct MeanptFluctuationsAnalysis { } //! end init function template - bool eventSelected(TCollision collision, const int& multTrk, const float& centrality) + bool eventSelected(TCollision const& collision, const int multTrk, const float centrality) { if (collision.alias_bit(kTVXinTRD)) { // TRD triggered @@ -362,71 +363,87 @@ struct MeanptFluctuationsAnalysis { float zRes = std::sqrt(collision.covZZ()); float zResMax = 0.25; float numContribMin = 20; - if (zRes > zResMax && collision.numContrib() < numContribMin) + if (zRes > zResMax && collision.numContrib() < numContribMin) { vtxz = -999; + } } auto multNTracksPV = collision.multNTracksPV(); - if ((vtxz > cfgCutVertex) || (vtxz < -1.0 * cfgCutVertex)) + if ((vtxz > cfgCutVertex) || (vtxz < -1.0 * cfgCutVertex)) { return 0; - if (multNTracksPV < fMultPVCutLow->Eval(centrality)) + } + if (multNTracksPV < fMultPVCutLow->Eval(centrality)) { return 0; - if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) + } + if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) { return 0; - if (multTrk < fMultCutLow->Eval(centrality)) + } + if (multTrk < fMultCutLow->Eval(centrality)) { return 0; - if (multTrk > fMultCutHigh->Eval(centrality)) + } + if (multTrk > fMultCutHigh->Eval(centrality)) { return 0; - if (multTrk > fMultMultPVCut->Eval(multNTracksPV)) + } + if (multTrk > fMultMultPVCut->Eval(multNTracksPV)) { return 0; + } return 1; } template - bool eventSelectedSmallion(TCollision collision, const int multTrk, const float centrality) + bool eventSelectedSmallion(TCollision const& collision, const int multTrk, const float centrality) { auto multNTracksPV = collision.multNTracksPV(); if (cfgEvSelMultCorrelation) { if (cfgFuncParas.cfgMultPVT0CCutEnabled) { - if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(centrality)) + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(centrality)) { return 0; - if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(centrality)) + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(centrality)) { return 0; + } } if (cfgFuncParas.cfgMultT0CCutEnabled) { - if (multTrk < cfgFuncParas.fMultT0CCutLow->Eval(centrality)) + if (multTrk < cfgFuncParas.fMultT0CCutLow->Eval(centrality)) { return 0; - if (multTrk > cfgFuncParas.fMultT0CCutHigh->Eval(centrality)) + } + if (multTrk > cfgFuncParas.fMultT0CCutHigh->Eval(centrality)) { return 0; + } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { - if (multTrk < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) + if (multTrk < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { return 0; - if (multTrk > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) + } + if (multTrk > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { return 0; + } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { - if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(multTrk)) + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(multTrk)) { return 0; - if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(multTrk)) + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(multTrk)) { return 0; + } } } float sigma = 5.0; - if (cfgEvSelV0AT0ACut && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) + if (cfgEvSelV0AT0ACut && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { return 0; + } return 1; } template - bool eventSelectionDefaultCuts(TCollision coll) + bool eventSelectionDefaultCuts(TCollision const& coll) { histos.fill(HIST("hEventStatData"), 0.5); if (!coll.sel8()) { @@ -490,7 +507,7 @@ struct MeanptFluctuationsAnalysis { if (std::abs(mcCollision.posZ()) < cfgCutVertex) { histos.fill(HIST("MCGenerated/hMC"), 1.5); } - auto cent = 0; + auto cent = 0.0f; int nchInel = 0; for (const auto& mcParticle : mcParticles) { @@ -501,8 +518,9 @@ struct MeanptFluctuationsAnalysis { } } } - if (nchInel > 0 && std::abs(mcCollision.posZ()) < cfgCutVertex) + if (nchInel > 0 && std::abs(mcCollision.posZ()) < cfgCutVertex) { histos.fill(HIST("MCGenerated/hMC"), 2.5); + } std::vector selectedEvents(collisions.size()); int nevts = 0; @@ -557,15 +575,19 @@ struct MeanptFluctuationsAnalysis { float nChgen = 0.0; for (const auto& mcParticle : mcParticles) { - if (!mcParticle.has_mcCollision()) + if (!mcParticle.has_mcCollision()) { continue; + } // charged check - auto pdgEntry = TDatabasePDG::Instance()->GetParticle(mcParticle.pdgCode()); - if (!pdgEntry) - continue; - if (pdgEntry->Charge() == 0) - continue; + auto pdgcode = std::abs(mcParticle.pdgCode()); + if (pdgcode != PDG_t::kPiPlus && + pdgcode != PDG_t::kKPlus && + pdgcode != PDG_t::kProton && + pdgcode != PDG_t::kElectron && + pdgcode != PDG_t::kMuonMinus) { + continue; // skip this track + } if (mcParticle.isPhysicalPrimary()) { if ((mcParticle.pt() > cfgCutPtLower) && (mcParticle.pt() < cfgCutPreSelPt) && (std::abs(mcParticle.eta()) < cfgCutPreSelEta)) { @@ -589,8 +611,9 @@ struct MeanptFluctuationsAnalysis { } //! end particle loop // Generated MeanPt distribution - if (nNgen > 0.0f) + if (nNgen > 0.0f) { histos.fill(HIST("MCGenerated/hMeanPt"), cent, pTsumgen / nNgen); + } // calculating observables if (nChgen > cfgMinNch) { @@ -627,72 +650,120 @@ struct MeanptFluctuationsAnalysis { } //------------------------------------------------------------------------------------------- } - PROCESS_SWITCH(MeanptFluctuationsAnalysis, processMCGen, "Process Generated MC data", true); + PROCESS_SWITCH(MeanptFluctuations, processMCGen, "Process Generated MC data", true); - void processMCRec(MyMCRecCollisions::iterator const& collision, MyMCTracks const& tracks, aod::McCollisions const&, aod::McParticles const& mcParticles) + void processMCRec(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions, MyMCTracks const& tracks) { histos.fill(HIST("MCGenerated/hMC"), 5.5); - if (!collision.has_mcCollision()) { - return; + if (collisions.size() == 0) { + return; // this generated event was never reconstructed at all } + histos.fill(HIST("MCGenerated/hMC"), 6.5); - if (!eventSelectionDefaultCuts(collision)) { + std::vector selectedEvents(collisions.size()); + int nevts = 0; + + for (const auto& collision : collisions) { + if (!collision.sel8() || std::abs(collision.mcCollision().posZ()) > cfgCutVertex) { + continue; + } + if (cfgUseGoodITSLayerAllCut && !(collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { + continue; + } + if (cfgEvSelkNoSameBunchPileup && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { + continue; + } + if (cfgEvSelkNoITSROFrameBorder && !(collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { + continue; + } + if (cfgEvSelkNoTimeFrameBorder && !(collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { + continue; + } + if (cfgEvSelUseGoodZvtxFT0vsPV && !(collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV))) { + continue; + } + + auto rectrackspart = tracks.sliceBy(perCollision, collision.globalIndex()); + auto cent = collision.centFT0C(); + + fillMultCorrPlotsBeforeSel(collision, rectrackspart); + + if (cfgUseSmallIonAdditionalEventCutInMC && !eventSelectedSmallion(collision, rectrackspart.size(), cent)) { + continue; + } + + if (cfgUseSmallIonAdditionalEventCutInMC) { + fillMultCorrPlotsAfterSel(collision, rectrackspart); + } + + selectedEvents[nevts++] = collision.mcCollision_as().globalIndex(); + } + selectedEvents.resize(nevts); + const auto evtReconstructedAndSelected = std::find(selectedEvents.begin(), selectedEvents.end(), mcCollision.globalIndex()) != selectedEvents.end(); + + if (!evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection return; } histos.fill(HIST("MCGenerated/hMC"), 7.5); - fillMultCorrPlotsBeforeSel(collision, tracks); - - const auto centralityFT0C = collision.centFT0C(); - if (cfgUse22sEventCut && !eventSelected(collision, tracks.size(), centralityFT0C)) - return; - if (cfgUseSmallIonAdditionalEventCut && !eventSelectedSmallion(collision, tracks.size(), centralityFT0C)) + auto bestColl = collisions.begin(); + bool foundValidColl = false; + for (auto const& coll : collisions) { + if (!foundValidColl || coll.numContrib() > bestColl.numContrib()) { + bestColl = coll; + foundValidColl = true; + } + } + if (!foundValidColl) { return; - - if (cfgUseSmallIonAdditionalEventCut) { - fillMultCorrPlotsAfterSel(collision, tracks); } - histos.fill(HIST("MCGenerated/hMC"), 8.5); - histos.fill(HIST("hZvtx_after_sel"), collision.posZ()); + + if (!bestColl.has_mcCollision()) { + return; + } + histos.fill(HIST("MCGenerated/hMC"), 9.5); double cent = 0.0; int centChoiceFT0C = 1; int centChoiceFT0A = 2; int centChoiceFT0M = 3; int centChoiceFV0A = 4; - if (cfgCentralityEstimator == centChoiceFT0C) - cent = collision.centFT0C(); - else if (cfgCentralityEstimator == centChoiceFT0A) - cent = collision.centFT0A(); - else if (cfgCentralityEstimator == centChoiceFT0M) - cent = collision.centFT0M(); - else if (cfgCentralityEstimator == centChoiceFV0A) - cent = collision.centFV0A(); + if (cfgCentralityEstimator == centChoiceFT0C) { + cent = bestColl.centFT0C(); + } else if (cfgCentralityEstimator == centChoiceFT0A) { + cent = bestColl.centFT0A(); + } else if (cfgCentralityEstimator == centChoiceFT0M) { + cent = bestColl.centFT0M(); + } else if (cfgCentralityEstimator == centChoiceFV0A) { + cent = bestColl.centFV0A(); + } histos.fill(HIST("hCentrality"), cent); + histos.fill(HIST("hZvtx_after_sel"), bestColl.posZ()); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), centralityFT0C); + auto tracksThisCollision = tracks.sliceBy(perCollision, bestColl.globalIndex()); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), bestColl.multNTracksPV(), tracksThisCollision.size()); + histos.fill(HIST("Hist2D_cent_nch"), tracksThisCollision.size(), cent); // Calculating generated no of particles for the collision event double noGen = 0.0; - auto mcColl = collision.mcCollision(); - // Slice particles belonging only to this MC collision - auto particlesThisEvent = mcParticles.sliceBy(perMcCollision, mcColl.globalIndex()); - - for (const auto& mcParticle : particlesThisEvent) { - if (!mcParticle.has_mcCollision()) + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.has_mcCollision()) { continue; + } // charged check - auto pdgEntry = TDatabasePDG::Instance()->GetParticle(mcParticle.pdgCode()); - if (!pdgEntry) - continue; - if (pdgEntry->Charge() == 0) - continue; + auto pdgcode = std::abs(mcParticle.pdgCode()); + if (pdgcode != PDG_t::kPiPlus && + pdgcode != PDG_t::kKPlus && + pdgcode != PDG_t::kProton && + pdgcode != PDG_t::kElectron && + pdgcode != PDG_t::kMuonMinus) { + continue; // skip this track + } if (mcParticle.isPhysicalPrimary()) { if ((mcParticle.pt() > cfgCutPtLower) && (mcParticle.pt() < cfgCutPreSelPt) && (std::abs(mcParticle.eta()) < cfgCutPreSelEta)) { @@ -706,14 +777,13 @@ struct MeanptFluctuationsAnalysis { // variables double pTsum = 0.0; double nN = 0.0; - float q1 = 0.0; float q2 = 0.0; float q3 = 0.0; float q4 = 0.0; float nCh = 0.0; - for (const auto& track : tracks) { // Loop over tracks + for (const auto& track : tracksThisCollision) { // Loop over tracks if (!track.has_collision()) { continue; @@ -727,6 +797,21 @@ struct MeanptFluctuationsAnalysis { continue; } + if (particle.mcCollisionId() != mcCollision.globalIndex()) { // reject tracks whose true particle belongs to a DIFFERENT generated collision (pileup contamination in bestColl) + continue; + } + + if (cfgUseParticlePDGsInProcessMcReco) { + auto pdgPart = std::abs(particle.pdgCode()); + if (pdgPart != PDG_t::kPiPlus && + pdgPart != PDG_t::kKPlus && + pdgPart != PDG_t::kProton && + pdgPart != PDG_t::kElectron && + pdgPart != PDG_t::kMuonMinus) { + continue; // skip this track + } + } + if (!track.isPVContributor()) { continue; } @@ -777,8 +862,9 @@ struct MeanptFluctuationsAnalysis { histos.fill(HIST("MCGenerated/hNgenVsNrec"), noGen, nCh); // MeanPt - if (nN > 0.0f) + if (nN > 0.0f) { histos.fill(HIST("hMeanPt"), cent, pTsum / nN); + } // calculating observables if (nCh > cfgMinNch) { @@ -799,6 +885,7 @@ struct MeanptFluctuationsAnalysis { histos.get(HIST("AnalysisProfilesV2/Prof_var_t1"))->Fill(noGen, nCh, varianceTerm1); histos.get(HIST("AnalysisProfilesV2/Prof_skew_t1"))->Fill(noGen, nCh, skewnessTerm1); histos.get(HIST("AnalysisProfilesV2/Prof_kurt_t1"))->Fill(noGen, nCh, kurtosisTerm1); + histos.fill(HIST("AnalysisProfiles/Hist2D_Ngen_centrality"), cent, noGen); // selecting subsample and filling profiles float lRandom = fRndm->Rndm(); @@ -815,7 +902,7 @@ struct MeanptFluctuationsAnalysis { } //------------------------------------------------------------------------------------------- } - PROCESS_SWITCH(MeanptFluctuationsAnalysis, processMCRec, "Process MC Reconstructed Data", true); + PROCESS_SWITCH(MeanptFluctuations, processMCRec, "Process MC Reconstructed Data", true); // void process(aod::Collision const& coll, aod::Tracks const& inputTracks) void processData(AodCollisions::iterator const& coll, aod::BCsWithTimestamps const&, AodTracks const& inputTracks) @@ -827,10 +914,12 @@ struct MeanptFluctuationsAnalysis { fillMultCorrPlotsBeforeSel(coll, inputTracks); const auto centralityFT0C = coll.centFT0C(); - if (cfgUse22sEventCut && !eventSelected(coll, inputTracks.size(), centralityFT0C)) + if (cfgUse22sEventCut && !eventSelected(coll, inputTracks.size(), centralityFT0C)) { return; - if (cfgUseSmallIonAdditionalEventCut && !eventSelectedSmallion(coll, inputTracks.size(), centralityFT0C)) + } + if (cfgUseSmallIonAdditionalEventCut && !eventSelectedSmallion(coll, inputTracks.size(), centralityFT0C)) { return; + } if (cfgUseSmallIonAdditionalEventCut) { fillMultCorrPlotsAfterSel(coll, inputTracks); @@ -845,14 +934,15 @@ struct MeanptFluctuationsAnalysis { int centChoiceFT0A = 2; int centChoiceFT0M = 3; int centChoiceFV0A = 4; - if (cfgCentralityEstimator == centChoiceFT0C) + if (cfgCentralityEstimator == centChoiceFT0C) { cent = coll.centFT0C(); - else if (cfgCentralityEstimator == centChoiceFT0A) + } else if (cfgCentralityEstimator == centChoiceFT0A) { cent = coll.centFT0A(); - else if (cfgCentralityEstimator == centChoiceFT0M) + } else if (cfgCentralityEstimator == centChoiceFT0M) { cent = coll.centFT0M(); - else if (cfgCentralityEstimator == centChoiceFV0A) + } else if (cfgCentralityEstimator == centChoiceFV0A) { cent = coll.centFV0A(); + } histos.fill(HIST("hCentrality"), cent); @@ -916,8 +1006,9 @@ struct MeanptFluctuationsAnalysis { } } // MeanPt - if (nN > 0.0f) + if (nN > 0.0f) { histos.fill(HIST("hMeanPt"), cent, pTsum / nN); + } // calculating observables if (nCh > cfgMinNch) { @@ -944,12 +1035,12 @@ struct MeanptFluctuationsAnalysis { } //------------------------------------------------------------------------------------------- } - PROCESS_SWITCH(MeanptFluctuationsAnalysis, processData, "Process Real Data", true); + PROCESS_SWITCH(MeanptFluctuations, processData, "Process Real Data", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { // Equivalent to the AddTask in AliPhysics return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGCF/EbyEFluctuations/Tasks/multAndPtFluctuations.cxx b/PWGCF/EbyEFluctuations/Tasks/multAndPtFluctuations.cxx new file mode 100644 index 00000000000..2032d0e252a --- /dev/null +++ b/PWGCF/EbyEFluctuations/Tasks/multAndPtFluctuations.cxx @@ -0,0 +1,232 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multAndPtFluctuations.cxx +/// \brief Calculate multiplicity and transverse momentum fluctuations using strongly intensive observables +/// \author Omama Rubza +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod; +using namespace o2::framework::expressions; + +struct MultAndPtFluctuations { + + // ------ Histogram binning + + Configurable nBinsPt{"nBinsPt", 100, "pT bins"}; + Configurable ndcaXY{"ndcaXY", 100, "DCAxy bins"}; + Configurable ndcaZ{"ndcaZ", 100, "DCAz bins"}; + Configurable nCentBins{"nCentBins", 100, "Number of centrality bins"}; + + // ------ Event Cuts + Configurable vtxZcut{"vtxZcut", 10.0f, "vertex Z"}; + Configurable cfgNoSameBunchPileup{"cfgNoSameBunchPileup", true, "kNoSameBunchPileup"}; + Configurable cfgEvSelUseGoodZvtxFT0vsPV{"cfgEvSelUseGoodZvtxFT0vsPV", true, "Good Zvtx FT0 vs PV"}; + Configurable cfgUseGoodITSLayerAllCut{"cfgUseGoodITSLayerAllCut", true, "Good ITS Layers"}; + + // ----- Centrality estimator + Configurable cFT0M{"cFT0M", false, "FT0M centrality"}; // for pp + Configurable cFT0C{"cFT0C", true, "Use FT0C centrality"}; + + // ------ Track cuts + Configurable ptMinCut{"ptMinCut", 0.2f, "Minimum pT"}; + Configurable ptMaxCut{"ptMaxCut", 5.0f, "Maximum pT"}; + Configurable etaCut{"etaCut", 0.8f, "Maximum |eta|"}; + Configurable itsNClsCut{"itsNClsCut", 4, "Minimum ITS clusters"}; + Configurable tpcCrossCut{"tpcCrossCut", 70.0f, "TPC crossed rows"}; + Configurable crossedRowsOverFindableCut{"crossedRowsOverFindableCut", 0.8f, "TPC crossed rows over findable"}; + Configurable dcaZCut{"dcaZCut", 2.0f, "Maximum DCAz"}; + Configurable dcaXYCut{"dcaXYCut", 0.2f, "Maximum DCAxy"}; + Configurable tpcChiCut{"tpcChiCut", 4.0f, "TPC chi2/NCls"}; + Configurable itsChiCut{"itsChiCut", 36.0f, "ITS chi2/NCls"}; + Configurable requireITS{"requireITS", true, "Require ITS hit"}; + Configurable requireTPC{"requireTPC", true, "Require TPC hit"}; + Configurable requireInnerITS{"requireInnerITS", true, "At least one hit in ITS layers 0,1,2"}; + + //----- Histogram Registry + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + void init(InitContext const&) + { + const AxisSpec axisEta{30, -1.5, +1.5, "#eta"}; + const AxisSpec axisPt{nBinsPt, 0.15, 10.0, "p_{T}"}; + const AxisSpec axisVtxz{80, -20, 20, "V_{Z} (cm)"}; + const AxisSpec axisPhi{40, -1, 7, "#phi"}; + const AxisSpec axisdcaXY{ndcaXY, -0.3, 0.3, "DCAxy"}; + const AxisSpec axisdcaZ{ndcaZ, -3.0, 3.0, "DCAz"}; + const AxisSpec axisNch{500, 0, 500, "Nch"}; + const AxisSpec axisCent{nCentBins, 0.0, 100.0, "Centrality (%)"}; // ------------- centrality bins + histos.add("QA/BeforeCut/VtxZ", "Vertex Z", kTH1F, {axisVtxz}); + histos.add("QA/AfterCut/VtxZ", "Vertex Z", kTH1F, {axisVtxz}); + histos.add("QA/BeforeCut/Cent", "Centrality", kTH1F, {axisCent}); + histos.add("QA/AfterCut/Cent", "Centrality", kTH1F, {axisCent}); + histos.add("QA/BeforeCut/Eta", "Eta", kTH1F, {axisEta}); + histos.add("QA/AfterCut/Eta", "Eta", kTH1F, {axisEta}); + histos.add("QA/BeforeCut/Pt", "Pt", kTH1F, {axisPt}); + histos.add("QA/AfterCut/Pt", "Pt", kTH1F, {axisPt}); + histos.add("QA/BeforeCut/Phi", "Phi", kTH1F, {axisPhi}); + histos.add("QA/AfterCut/Phi", "Phi", kTH1F, {axisPhi}); + histos.add("QA/BeforeCut/DcaXY", "DCAxy", kTH1F, {axisdcaXY}); + histos.add("QA/AfterCut/DcaXY", "DCAxy", kTH1F, {axisdcaXY}); + histos.add("QA/BeforeCut/DcaZ", "DCAz", kTH1F, {axisdcaZ}); + histos.add("QA/AfterCut/DcaZ", "DCAz", kTH1F, {axisdcaZ}); + histos.add("hNch", "Nch", kTH1F, {axisNch}); + histos.add("h2_DcaZ", "DCA_{Z}", kTH2D, {{axisPt}, {axisdcaZ}}); + histos.add("h2_DcaXY", "DCA_{XY}", kTH2D, {{axisPt}, {axisdcaXY}}); + histos.add("hEventCounter", "Number of events vs centrality", kTH1F, {axisCent}); + histos.add("p_a", " vs centrality", kTProfile, {axisCent}); + histos.add("p_b", " vs centrality", kTProfile, {axisCent}); + histos.add("p_a2", " vs centrality", kTProfile, {axisCent}); + histos.add("p_b2", " vs centrality", kTProfile, {axisCent}); + histos.add("p_ab", " vs centrality", kTProfile, {axisCent}); + histos.add("p_asumb", " vs centrality", kTProfile, {axisCent}); + } + + //---------- Filters + Filter ptFilter = (aod::track::pt > ptMinCut) && (aod::track::pt < ptMaxCut); + Filter etaFilter = nabs(aod::track::eta) < etaCut; + Filter posZFilter = nabs(aod::collision::posZ) < vtxZcut; + + using MyColsData = soa::Join; + using MyTracksData = soa::Join; + using MyFilteredTracksData = soa::Filtered; + + void process(MyColsData::iterator const& col, MyFilteredTracksData const& tracks) + { + histos.fill(HIST("QA/BeforeCut/VtxZ"), col.posZ()); + float cent = -1.0f; + if (cFT0M) { + cent = col.centFT0M(); + } + if (cFT0C) { + cent = col.centFT0C(); + // if (!track.isGlobalTrack()) continue; + } + if (cent < 0) { + return; + } + + histos.fill(HIST("QA/BeforeCut/Cent"), cent); + if (!col.sel8()) { + return; + } + if (std::abs(col.posZ()) > vtxZcut) { + return; + } + if (cfgNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return; + } + if (cfgEvSelUseGoodZvtxFT0vsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return; + } + if (cfgUseGoodITSLayerAllCut && !col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return; + } + + histos.fill(HIST("hEventCounter"), cent); + histos.fill(HIST("QA/AfterCut/VtxZ"), col.posZ()); + histos.fill(HIST("QA/AfterCut/Cent"), cent); + + double a = 0.0; + double b = 0.0; + + // if (!track.isGlobalTrack()) continue; + for (const auto& track : tracks) { + + histos.fill(HIST("QA/BeforeCut/Eta"), track.eta()); + histos.fill(HIST("QA/BeforeCut/Pt"), track.pt()); + histos.fill(HIST("QA/BeforeCut/Phi"), track.phi()); + histos.fill(HIST("QA/BeforeCut/DcaXY"), track.dcaXY()); + histos.fill(HIST("QA/BeforeCut/DcaZ"), track.dcaZ()); + + // Track quality cuts + + if (requireITS && !track.hasITS()) { + continue; + } + if (requireTPC && !track.hasTPC()) { + continue; + } + if (track.itsNCls() < itsNClsCut) { + continue; + } + if (track.tpcNClsCrossedRows() < tpcCrossCut) { + continue; + } + if (track.tpcCrossedRowsOverFindableCls() < crossedRowsOverFindableCut) { + continue; + } + if (std::abs(track.dcaZ()) > dcaZCut) { + continue; + } + if (std::abs(track.dcaXY()) > dcaXYCut) { + continue; + } + if (track.tpcChi2NCl() > tpcChiCut) { + continue; + } + if (track.itsChi2NCl() > itsChiCut) { + continue; + } + if (std::abs(track.eta()) > etaCut) { + continue; + } + if (requireInnerITS) { + auto itsMap = track.itsClusterMap(); + if (((itsMap & (1 << 0)) == 0) && + ((itsMap & (1 << 1)) == 0) && + ((itsMap & (1 << 2)) == 0)) { + continue; + } + } + + // After cuts QA + histos.fill(HIST("QA/AfterCut/Eta"), track.eta()); + histos.fill(HIST("QA/AfterCut/Pt"), track.pt()); + histos.fill(HIST("QA/AfterCut/Phi"), track.phi()); + histos.fill(HIST("QA/AfterCut/DcaXY"), track.dcaXY()); + histos.fill(HIST("QA/AfterCut/DcaZ"), track.dcaZ()); + histos.fill(HIST("h2_DcaZ"), track.pt(), track.dcaZ()); + histos.fill(HIST("h2_DcaXY"), track.pt(), track.dcaXY()); + a++; // A-nch, B =pt + b += track.pt(); + } + + // ---- Fill TProfiles (once per event) + histos.fill(HIST("p_a"), cent, a); + histos.fill(HIST("p_b"), cent, b); + histos.fill(HIST("p_a2"), cent, a * a); + histos.fill(HIST("p_b2"), cent, b * b); + histos.fill(HIST("p_ab"), cent, a * b); + histos.fill(HIST("p_asumb"), cent, a + b); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/EbyEFluctuations/Tasks/nchCumulantsId.cxx b/PWGCF/EbyEFluctuations/Tasks/nchCumulantsId.cxx index 32800bfb680..d19f975b2e3 100644 --- a/PWGCF/EbyEFluctuations/Tasks/nchCumulantsId.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/nchCumulantsId.cxx @@ -9,10 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file nchCumulantsId.cxx +/// \file NchCumulantsId.cxx /// \brief Event by Event conserved charges fluctuations /// \author Pravata Panigrahi :: Sadhana Dash(sadhana@phy.iitb.ac.in) +#include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -22,20 +23,25 @@ #include #include +// #include #include -#include +// #include #include #include #include #include #include -#include +// #include #include #include #include #include +// #include +// #include +#include +// #include #include #include #include @@ -50,14 +56,27 @@ using namespace o2::framework::expressions; using namespace o2::constants::physics; // for constants using namespace std; -#define ID_BIT_PI 0 // Identificationi bits for PID checks -#define ID_BIT_KA 1 -#define ID_BIT_PR 2 -#define ID_BIT_EL 3 -#define ID_BIT_DE 4 +constexpr int IdBitPI = 0; // Identificationi bits for PID checks +constexpr int IdBitKA = 1; +constexpr int IdBitPR = 2; +// constexpr int IdBitEL = 3; +// constexpr int IdBitDE = 4; -#define BITSET(mask, ithBit) ((mask) |= (1 << (ithBit))) // avoid name bitset as std::bitset is already there -#define BITCHECK(mask, ithBit) ((mask) & (1 << (ithBit))) // bit check will return int value, not bool, use BITCHECK != 0 in Analysi +constexpr int McBitPI = 0; // MC particle identification bits +constexpr int McBitKA = 1; +constexpr int McBitPR = 2; +constexpr int McBitEL = 3; +constexpr int McBitDE = 4; + +constexpr void BITSET(int& mask, int ithBit) +{ + mask |= (1 << ithBit); +} + +constexpr bool BITCHECK(int mask, int ithBit) +{ + return (mask & (1 << ithBit)) != 0; +} enum PidEnum { kCh = 0, @@ -73,7 +92,50 @@ enum ChargeEnum { kNeg = 1 }; -static constexpr std::string_view PidDire[] = { +enum class FCPrefixEnum { + Pr = 0, + APr = 1, + PiPos = 2, + PiNeg = 3, + KaPos = 4, + KaNeg = 5, + Pos = 6, + Neg = 7, + NetPi = 8, + NetKa = 9, + NetPr = 10, + NetCh = 11 +}; + +static constexpr std::array FCRecoDir = { + "Reco/Pr/", + "Reco/APr/", + "Reco/PiPos/", + "Reco/PiNeg/", + "Reco/KaPos/", + "Reco/KaNeg/", + "Reco/Pos/", + "Reco/Neg/", + "Reco/NetPi/", + "Reco/NetKa/", + "Reco/NetPr/", + "Reco/NetCh/"}; + +static constexpr std::array FCGenDir = { + "Gen/Pr/", + "Gen/APr/", + "Gen/PiPos/", + "Gen/PiNeg/", + "Gen/KaPos/", + "Gen/KaNeg/", + "Gen/Pos/", + "Gen/Neg/", + "Gen/NetPi/", + "Gen/NetKa/", + "Gen/NetPr/", + "Gen/NetCh/"}; + +static constexpr std::array PidDire = { "Ch/", "Pi/", "Ka/", @@ -81,7 +143,7 @@ static constexpr std::string_view PidDire[] = { "El/", "De/"}; -static constexpr std::string_view ChargeDire[] = { +static constexpr std::array ChargeDire = { "Pos/", "Neg/"}; @@ -91,21 +153,20 @@ std::string getModifiedStr(const std::string& myString) if (pos != std::string::npos) { std::string subString = myString.substr(0, pos); // remove "/" from end of the string return subString; - } else { - return myString; } + return myString; } struct NchCumulantsId { HistogramRegistry hist{"hist", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry recoTracks{"recoTracks", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry genAnalysis{"genAnalysis", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry recoAnalysis{"recoAnalysis", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry purityAnalysis{"purityAnalysis", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; // PDG data base - Service pdgDB; + // Service pdgDB; Configurable cfgCutPosZ{"cfgCutPosZ", 10.0, "cut for vertex Z"}; Configurable cfgCutDcaXY{"cfgCutDcaXY", 0.12, "cut for dcaXY"}; @@ -113,55 +174,100 @@ struct NchCumulantsId { Configurable cfgCutEta{"cfgCutEta", 0.8, "cut for eta"}; Configurable cfgCutPtMax{"cfgCutPtMax", 3.0, "max cut for pT"}; Configurable cfgCutPtMin{"cfgCutPtMin", 0.15, "min cut for pT"}; - Configurable netChBin{"netChBin", 160, "bins for netch"}; - Configurable netChMax{"netChMax", 80.0, "max for netch"}; - Configurable netChMin{"netChMin", -80.0, "min for netch"}; - Configurable chBins{"chBins", 501, "bins for ch"}; - Configurable chMax{"chMax", 500.0, "max for ch"}; - Configurable chMin{"chMin", -1.0, "min for ch"}; - Configurable midRapMax{"midRapMax", 1000.0, "max for midrapmult"}; - Configurable midRapBins{"midRapBins", 1001, "bins for midrapmult"}; - - Configurable checkCollPosZMc{"checkCollPosZMc", false, "checkCollPosZMc"}; - Configurable flagUnusedVariableError{"flagUnusedVariableError", false, "flagUnusedVariableError"}; + + ConfigurableAxis axisNch{"axisNch", {1200, -60.0, 60.0}, "Net_charge_dN"}; + ConfigurableAxis axisPosCh{"axisPosCh", {3010, -0.5, 300.5}, "Pos_charge"}; + ConfigurableAxis axisNegCh{"axisNegCh", {3010, -0.5, 300.5}, "Neg_charge"}; + ConfigurableAxis axisNt{"axisNt", {8010, -0.5, 800.5}, "Mult_midRap_Nch"}; + ConfigurableAxis axisPrCh{"axisPrCh", {3010, -0.5, 300.5}, "Pr_charge"}; + ConfigurableAxis axisAPrCh{"axisAPrCh", {3010, -0.5, 300.5}, "APr_charge"}; + ConfigurableAxis axisKaCh{"axisKaCh", {3010, -0.5, 300.5}, "Ka_charge"}; + ConfigurableAxis axisAKaCh{"axisAKaCh", {3010, -0.5, 300.5}, "AKa_charge"}; + ConfigurableAxis axisPiCh{"axisPiCh", {3010, -0.5, 300.5}, "Pion_Positive"}; + ConfigurableAxis axisAPiCh{"axisAPiCh", {3010, -0.5, 300.5}, "Pion_Negative"}; + // QA axes as ConfigurableAxis + ConfigurableAxis axisEvents{"axisEvents", {1, 0, 1}, "Counts"}; + ConfigurableAxis axisEta{"axisEta", {100, -1., +1.}, "#eta"}; + ConfigurableAxis axisRapidity{"axisRapidity", {200, -5, 5}, "Rapidity (y)"}; + ConfigurableAxis axisPt{"axisPt", {100, 0., 5.}, "p_{T} (GeV/c)"}; + ConfigurableAxis axisP{"axisP", {100, 0., 5.}, "p (GeV/c)"}; + ConfigurableAxis axisTPCInnerParam{"axisTPCInnerParam", {100, 0, 3}, "P_innerParam_Gev"}; + ConfigurableAxis axisdEdx{"axisdEdx", {100, 20, 500}, "#frac{dE}{dx}"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {80, -20., 20.}, "V_{Z} (cm)"}; + ConfigurableAxis axisDCAz{"axisDCAz", {200, -3., 3.}, "DCA_{Z} (cm)"}; + ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -3., 3.}, "DCA_{XY} (cm)"}; + ConfigurableAxis axisMultFT0{"axisMultFT0", {150, 0, 1500}, "MultFT0"}; + ConfigurableAxis axisCent{"axisCent", {103, -1., 102.}, "FT0C(%)"}; + ConfigurableAxis axisPhi{"axisPhi", {80, -1, 7}, "phi"}; + ConfigurableAxis axisTOFBeta{"axisTOFBeta", {40, -2.0, 2.0}, "tofBeta"}; + ConfigurableAxis axisTPCSignal{"axisTPCSignal", {100, -1, 1000}, "tpcSignal"}; + ConfigurableAxis axisTPCNSigma{"axisTPCNSigma", {200, -10.0, 10.0}, "n#sigma_{TPC}"}; + ConfigurableAxis axisTOFNSigma{"axisTOFNSigma", {200, -10.0, 10.0}, "n#sigma_{TOF}"}; + ConfigurableAxis axisTOFExpMom{"axisTOFExpMom", {200, 0.0f, 10.0f}, "#it{p}_{tofExpMom} (GeV/#it{c})"}; + + struct : ConfigurableGroup { + Configurable checkCollPosZMc{"checkCollPosZMc", false, "checkCollPosZMc"}; + Configurable flagUnusedVariableError{"flagUnusedVariableError", false, "flagUnusedVariableError"}; + Configurable cfgDoRejectionForId{"cfgDoRejectionForId", false, "Apply rejection cut before PID selection (selTrackForId)"}; + Configurable fillSparseForReco{"fillSparseForReco", false, "Fill sparse for reconstructed tracks"}; + Configurable fillSparseForGen{"fillSparseForGen", false, "Fill sparse for generated tracks"}; + Configurable fillSparseForPurity{"fillSparseForPurity", false, "Fill sparse for purity tracks"}; + Configurable cfgEvSel01doNoSameBunchPileup{"cfgEvSel01doNoSameBunchPileup", true, "apply kNoSameBunchPileup"}; + Configurable cfgEvSel02doIsGoodZvtxFT0vsPV{"cfgEvSel02doIsGoodZvtxFT0vsPV", true, "apply kIsGoodZvtxFT0vsPV"}; + Configurable cfgEvSel03doIsGoodITSLayersAll{"cfgEvSel03doIsGoodITSLayersAll", true, "apply kIsGoodITSLayersAll"}; + Configurable cfgDoSubsampling{"cfgDoSubsampling", true, "do subsampling for error estimation"}; + ConfigurableAxis subSampleAxis{"subSampleAxis", {10, 0., 10.}, "Subsample"}; + TRandom3* fRandom = new TRandom3(0); // Random number generator for subsampling + int currentSubsample = 0; + Configurable cfgCutPtMinKa{"cfgCutPtMinKa", 0.30, "min pT for kaon eff-correction inclusion"}; + Configurable cfgCutPtMaxKa{"cfgCutPtMaxKa", 1.20, "max pT for kaon eff-correction inclusion (purity cutoff)"}; + Configurable cfgCutPtMinPr{"cfgCutPtMinPr", 0.40, "min pT for proton eff-correction inclusion"}; + Configurable cfgCutPtMaxPr{"cfgCutPtMaxPr", 1.50, "max pT for proton eff-correction inclusion (purity cutoff)"}; + } cfgEventSelection; // Configurables for particle Identification - Configurable cfgId01CheckVetoCut{"cfgId01CheckVetoCut", false, "cfgId01CheckVetoCut"}; + Configurable cfgId01CheckVetoCut{"cfgId01CheckVetoCut", true, "cfgId01CheckVetoCut"}; Configurable cfgId02DoElRejection{"cfgId02DoElRejection", true, "cfgId02DoElRejection"}; Configurable cfgId03DoDeRejection{"cfgId03DoDeRejection", false, "cfgId03DoDeRejection"}; Configurable cfgId04DoPdependentId{"cfgId04DoPdependentId", true, "cfgId04DoPdependentId"}; Configurable cfgId05DoTpcInnerParamId{"cfgId05DoTpcInnerParamId", false, "cfgId05DoTpcInnerParamId"}; Configurable cfgIdPi01ThrPforTOF{"cfgIdPi01ThrPforTOF", 0.7, "cfgIdPi01ThrPforTOF"}; - Configurable cfgIdPi02IdCutTypeLowP{"cfgIdPi02IdCutTypeLowP", 0, "cfgIdPi02IdCutTypeLowP"}; + Configurable cfgIdPi02IdCutTypeLowP{"cfgIdPi02IdCutTypeLowP", 1, "cfgIdPi02IdCutTypeLowP"}; Configurable cfgIdPi03NSigmaTPCLowP{"cfgIdPi03NSigmaTPCLowP", 2.0, "cfgIdPi03NSigmaTPCLowP"}; Configurable cfgIdPi04NSigmaTOFLowP{"cfgIdPi04NSigmaTOFLowP", 2.0, "cfgIdPi04NSigmaTOFLowP"}; Configurable cfgIdPi05NSigmaRadLowP{"cfgIdPi05NSigmaRadLowP", 4.0, "cfgIdPi05NSigmaRadLowP"}; - Configurable cfgIdPi06IdCutTypeHighP{"cfgIdPi06IdCutTypeHighP", 0, "cfgIdPi06IdCutTypeHighP"}; + Configurable cfgIdPi06IdCutTypeHighP{"cfgIdPi06IdCutTypeHighP", 1, "cfgIdPi06IdCutTypeHighP"}; Configurable cfgIdPi07NSigmaTPCHighP{"cfgIdPi07NSigmaTPCHighP", 2.0, "cfgIdPi07NSigmaTPCHighP"}; Configurable cfgIdPi08NSigmaTOFHighP{"cfgIdPi08NSigmaTOFHighP", 2.0, "cfgIdPi08NSigmaTOFHighP"}; Configurable cfgIdPi09NSigmaRadHighP{"cfgIdPi09NSigmaRadHighP", 4.0, "cfgIdPi09NSigmaRadHighP"}; Configurable cfgIdKa01ThrPforTOF{"cfgIdKa01ThrPforTOF", 0.8, "cfgIdKa01ThrPforTOF"}; - Configurable cfgIdKa02IdCutTypeLowP{"cfgIdKa02IdCutTypeLowP", 0, "cfgIdKa02IdCutTypeLowP"}; + Configurable cfgIdKa02IdCutTypeLowP{"cfgIdKa02IdCutTypeLowP", 1, "cfgIdKa02IdCutTypeLowP"}; Configurable cfgIdKa03NSigmaTPCLowP{"cfgIdKa03NSigmaTPCLowP", 2.0, "cfgIdKa03NSigmaTPCLowP"}; Configurable cfgIdKa04NSigmaTOFLowP{"cfgIdKa04NSigmaTOFLowP", 2.0, "cfgIdKa04NSigmaTOFLowP"}; Configurable cfgIdKa05NSigmaRadLowP{"cfgIdKa05NSigmaRadLowP", 4.0, "cfgIdKa05NSigmaRadLowP"}; - Configurable cfgIdKa06IdCutTypeHighP{"cfgIdKa06IdCutTypeHighP", 0, "cfgIdKa06IdCutTypeHighP"}; + Configurable cfgIdKa06IdCutTypeHighP{"cfgIdKa06IdCutTypeHighP", 1, "cfgIdKa06IdCutTypeHighP"}; Configurable cfgIdKa07NSigmaTPCHighP{"cfgIdKa07NSigmaTPCHighP", 2.0, "cfgIdKa07NSigmaTPCHighP"}; Configurable cfgIdKa08NSigmaTOFHighP{"cfgIdKa08NSigmaTOFHighP", 2.0, "cfgIdKa08NSigmaTOFHighP"}; Configurable cfgIdKa09NSigmaRadHighP{"cfgIdKa09NSigmaRadHighP", 4.0, "cfgIdKa09NSigmaRadHighP"}; Configurable cfgIdPr01ThrPforTOF{"cfgIdPr01ThrPforTOF", 0.8, "cfgIdPr01ThrPforTOF"}; - Configurable cfgIdPr02IdCutTypeLowP{"cfgIdPr02IdCutTypeLowP", 0, "cfgIdPr02IdCutTypeLowP"}; + Configurable cfgIdPr02IdCutTypeLowP{"cfgIdPr02IdCutTypeLowP", 1, "cfgIdPr02IdCutTypeLowP"}; Configurable cfgIdPr03NSigmaTPCLowP{"cfgIdPr03NSigmaTPCLowP", 2.0, "cfgIdPr03NSigmaTPCLowP"}; Configurable cfgIdPr04NSigmaTOFLowP{"cfgIdPr04NSigmaTOFLowP", 2.0, "cfgIdPr04NSigmaTOFLowP"}; Configurable cfgIdPr05NSigmaRadLowP{"cfgIdPr05NSigmaRadLowP", 4.0, "cfgIdPr05NSigmaRadLowP"}; - Configurable cfgIdPr06IdCutTypeHighP{"cfgIdPr06IdCutTypeHighP", 0, "cfgIdPr06IdCutTypeHighP"}; + Configurable cfgIdPr06IdCutTypeHighP{"cfgIdPr06IdCutTypeHighP", 1, "cfgIdPr06IdCutTypeHighP"}; Configurable cfgIdPr07NSigmaTPCHighP{"cfgIdPr07NSigmaTPCHighP", 2.0, "cfgIdPr07NSigmaTPCHighP"}; Configurable cfgIdPr08NSigmaTOFHighP{"cfgIdPr08NSigmaTOFHighP", 2.0, "cfgIdPr08NSigmaTOFHighP"}; Configurable cfgIdPr09NSigmaRadHighP{"cfgIdPr09NSigmaRadHighP", 4.0, "cfgIdPr09NSigmaRadHighP"}; + Configurable cfgIdElRejLowNSigma{"cfgIdElRejLowNSigma", -3.0, "cfgIdElRejLowNSigma"}; + Configurable cfgIdElRejHighNSigma{"cfgIdElRejHighNSigma", 5.0, "cfgIdElRejHighNSigma"}; + Configurable cfgIdPiRejNSigma{"cfgIdPiRejNSigma", 3.0, "cfgIdPiRejNSigma"}; + Configurable cfgIdKaRejNSigma{"cfgIdKaRejNSigma", 3.0, "cfgIdKaRejNSigma"}; + Configurable cfgIdPrRejNSigma{"cfgIdPrRejNSigma", 3.0, "cfgIdPrRejNSigma"}; + struct : ConfigurableGroup { Configurable cfgVetoId01PiTPC{"cfgVetoId01PiTPC", 3.0, "cfgVetoId01PiTPC"}; Configurable cfgVetoId02PiTOF{"cfgVetoId02PiTOF", 3.0, "cfgVetoId02PiTOF"}; @@ -193,6 +299,265 @@ struct NchCumulantsId { TH2F* hPtEtaForBinSearch = nullptr; std::vector> hPtEtaForEffCorrection{kDe + 1, std::array{}}; + // AxisSpec subSampleAxis = {static_cast(cfgNSubsamples), 0., static_cast(cfgNSubsamples), "Subsample"}; + + struct EffPowerSums { + double q1 = 0.; + double q2 = 0.; + double q3 = 0.; + double q4 = 0.; + }; + + static constexpr float KmaxWeight = 10.0f; // secondary safeguard for pr and kaon eff fix + + inline void fillEffPower(EffPowerSums& p, float weight) + { + if (weight <= 0.f || weight > KmaxWeight) { + return; + } + + p.q1 += weight; + p.q2 += weight * weight; + p.q3 += weight * weight * weight; + p.q4 += weight * weight * weight * weight; + } + + // NEW TEMPLATE FUNCTION FOR RECO PROFILES for individual species + template + void addFCRecoProfiles() + { + constexpr std::string_view Dir = FCRecoDir[static_cast(Prefix)]; + + registry.add(std::string(Dir) + "Q1", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q1Sq", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q2", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q1Cube", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q1Q2", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q3", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q1Pow4", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q1SqQ2", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q2Sq", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q1Q3", "", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q4", "", kTProfile, {axisCent}); + + if (cfgEventSelection.cfgDoSubsampling) { + registry.add(std::string(Dir) + "Q1_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q1Sq_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q2_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q1Cube_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q1Q2_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q3_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q1Pow4_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q1SqQ2_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q2Sq_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q1Q3_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q4_subsample", "", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + } + } + + template + void addNetQVectorProfileHistograms() + { + constexpr std::string_view Dir = FCRecoDir[static_cast(Prefix)]; + + registry.add(std::string(Dir) + "Q_net_1", "Net Q1", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_1Sq", "Net Q1²", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_2", "Net Q2", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_1Cube", "Net Q1³", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_1Q_net_2", "Net Q1·Q2", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_3", "Net Q3", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_1Pow4", "Net Q1⁴", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_1SqQ_net_2", "Net Q1²·Q2", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_2Sq", "Net Q2²", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_1Q_net_3", "Net Q1·Q3", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "Q_net_4", "Net Q4", kTProfile, {axisCent}); + + // joint pos-neg correction profiles, they should be stored here. F11 needed for F3 and F21 and F12 for F4 correction + registry.add(std::string(Dir) + "JointF11", "Joint ", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "JointF12", "Joint ", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "JointF21", "Joint ", kTProfile, {axisCent}); + + // Factorial moments + if (cfgEventSelection.cfgDoSubsampling) { + registry.add(std::string(Dir) + "Q_net_1_subsample", "Net Q1 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_1Sq_subsample", "Net Q1² Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_2_subsample", "Net Q2 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_1Cube_subsample", "Net Q1³ Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_1Q_net_2_subsample", "Net Q1·Q2 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_3_subsample", "Net Q3 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_1Pow4_subsample", "Net Q1⁴ Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_1SqQ_net_2_subsample", "Net Q1²·Q2 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_2Sq_subsample", "Net Q2² Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_1Q_net_3_subsample", "Net Q1·Q3 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "Q_net_4_subsample", "Net Q4 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + + registry.add(std::string(Dir) + "JointF11_subsample", "Joint Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "JointF12_subsample", "Joint Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "JointF21_subsample", "Joint Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + } + } + + // NEW TEMPLATE FUNCTION FOR GEN PROFILES + template + void addFCGenProfiles() + { + constexpr std::string_view Dir = FCGenDir[static_cast(Prefix)]; + + registry.add(std::string(Dir) + "F1", "Gen FactorialMoment1", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "F2", "Gen FactorialMoment2", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "F3", "Gen FactorialMoment3", kTProfile, {axisCent}); + registry.add(std::string(Dir) + "F4", "Gen FactorialMoment4", kTProfile, {axisCent}); + + if (cfgEventSelection.cfgDoSubsampling) { + registry.add(std::string(Dir) + "F1_subsample", "Gen FactorialMoment1 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "F2_subsample", "Gen FactorialMoment2 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "F3_subsample", "Gen FactorialMoment3 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + registry.add(std::string(Dir) + "F4_subsample", "Gen FactorialMoment4 Subsample", kTProfile2D, {axisCent, cfgEventSelection.subSampleAxis}); + } + } + + template + void fillFCBasis(const EffPowerSums& p, float cent, Registry& hReg) + { + auto base = HIST(FCRecoDir[static_cast(Prefix)]); + + // Fill Factorial Moments for Reco directly to ensure Reco stores the same type of data as Gen + // Compute Efficiency Corrected Factorial Moments from power sums + // These formulas come from elementary symmetric polynomials and their relation to power sums + hReg.fill(base + HIST("Q1"), cent, p.q1); + hReg.fill(base + HIST("Q1Sq"), cent, p.q1 * p.q1); + hReg.fill(base + HIST("Q2"), cent, p.q2); + hReg.fill(base + HIST("Q1Cube"), cent, p.q1 * p.q1 * p.q1); + hReg.fill(base + HIST("Q1Q2"), cent, p.q1 * p.q2); + hReg.fill(base + HIST("Q3"), cent, p.q3); + hReg.fill(base + HIST("Q1Pow4"), cent, p.q1 * p.q1 * p.q1 * p.q1); + hReg.fill(base + HIST("Q1SqQ2"), cent, p.q1 * p.q1 * p.q2); + hReg.fill(base + HIST("Q2Sq"), cent, p.q2 * p.q2); + hReg.fill(base + HIST("Q1Q3"), cent, p.q1 * p.q3); + hReg.fill(base + HIST("Q4"), cent, p.q4); + if (cfgEventSelection.cfgDoSubsampling) { + hReg.fill(base + HIST("Q1_subsample"), cent, cfgEventSelection.currentSubsample, p.q1); + hReg.fill(base + HIST("Q1Sq_subsample"), cent, cfgEventSelection.currentSubsample, p.q1 * p.q1); + hReg.fill(base + HIST("Q2_subsample"), cent, cfgEventSelection.currentSubsample, p.q2); + hReg.fill(base + HIST("Q1Cube_subsample"), cent, cfgEventSelection.currentSubsample, p.q1 * p.q1 * p.q1); + hReg.fill(base + HIST("Q1Q2_subsample"), cent, cfgEventSelection.currentSubsample, p.q1 * p.q2); + hReg.fill(base + HIST("Q3_subsample"), cent, cfgEventSelection.currentSubsample, p.q3); + hReg.fill(base + HIST("Q1Pow4_subsample"), cent, cfgEventSelection.currentSubsample, p.q1 * p.q1 * p.q1 * p.q1); + hReg.fill(base + HIST("Q1SqQ2_subsample"), cent, cfgEventSelection.currentSubsample, p.q1 * p.q1 * p.q2); + hReg.fill(base + HIST("Q2Sq_subsample"), cent, cfgEventSelection.currentSubsample, p.q2 * p.q2); + hReg.fill(base + HIST("Q1Q3_subsample"), cent, cfgEventSelection.currentSubsample, p.q1 * p.q3); + hReg.fill(base + HIST("Q4_subsample"), cent, cfgEventSelection.currentSubsample, p.q4); + } + } + + // fill function for reco net species + template + void fillNetQVectorProfileHistograms(const EffPowerSums& posPow, const EffPowerSums& negPow, float cent, Registry& hReg) + { + // Construct base directory path + auto base = HIST(FCRecoDir[static_cast(Prefix)]); + + // ─── Compute NET q-vectors (sign alternation by power) ─── + // Odd powers: alternate sign (Q_pos - Q_neg) + // Even powers: same sign (Q_pos + Q_neg) + + double qNet1 = posPow.q1 - negPow.q1; // Odd + double qNet2 = posPow.q2 + negPow.q2; // Even + double qNet3 = posPow.q3 - negPow.q3; // Odd + double qNet4 = posPow.q4 + negPow.q4; // Even + + // Fill joint pos-neg correction profiles + // capture inline from pos and neg + double jointF11 = posPow.q1 * negPow.q1; + double cf2pos = posPow.q1 * posPow.q1 - posPow.q2; + double cf2neg = negPow.q1 * negPow.q1 - negPow.q2; + double jointF12 = posPow.q1 * cf2neg; + double jointF21 = negPow.q1 * cf2pos; + // ─── Fill raw q-vectors (for cross-verification) + hReg.fill(base + HIST("Q_net_1"), cent, qNet1); + hReg.fill(base + HIST("Q_net_1Sq"), cent, qNet1 * qNet1); + hReg.fill(base + HIST("Q_net_2"), cent, qNet2); + hReg.fill(base + HIST("Q_net_1Cube"), cent, qNet1 * qNet1 * qNet1); + hReg.fill(base + HIST("Q_net_1Q_net_2"), cent, qNet1 * qNet2); + hReg.fill(base + HIST("Q_net_3"), cent, qNet3); + hReg.fill(base + HIST("Q_net_1Pow4"), cent, qNet1 * qNet1 * qNet1 * qNet1); + hReg.fill(base + HIST("Q_net_1SqQ_net_2"), cent, qNet1 * qNet1 * qNet2); + hReg.fill(base + HIST("Q_net_2Sq"), cent, qNet2 * qNet2); + hReg.fill(base + HIST("Q_net_1Q_net_3"), cent, qNet1 * qNet3); + hReg.fill(base + HIST("Q_net_4"), cent, qNet4); + + // ─── Fill factorial moments ─── + hReg.fill(base + HIST("JointF11"), cent, jointF11); + hReg.fill(base + HIST("JointF12"), cent, jointF12); + hReg.fill(base + HIST("JointF21"), cent, jointF21); + + if (cfgEventSelection.cfgDoSubsampling) { + hReg.fill(base + HIST("Q_net_1_subsample"), cent, cfgEventSelection.currentSubsample, qNet1); + hReg.fill(base + HIST("Q_net_1Sq_subsample"), cent, cfgEventSelection.currentSubsample, qNet1 * qNet1); + hReg.fill(base + HIST("Q_net_2_subsample"), cent, cfgEventSelection.currentSubsample, qNet2); + hReg.fill(base + HIST("Q_net_1Cube_subsample"), cent, cfgEventSelection.currentSubsample, qNet1 * qNet1 * qNet1); + hReg.fill(base + HIST("Q_net_1Q_net_2_subsample"), cent, cfgEventSelection.currentSubsample, qNet1 * qNet2); + hReg.fill(base + HIST("Q_net_3_subsample"), cent, cfgEventSelection.currentSubsample, qNet3); + hReg.fill(base + HIST("Q_net_1Pow4_subsample"), cent, cfgEventSelection.currentSubsample, qNet1 * qNet1 * qNet1 * qNet1); + hReg.fill(base + HIST("Q_net_1SqQ_net_2_subsample"), cent, cfgEventSelection.currentSubsample, qNet1 * qNet1 * qNet2); + hReg.fill(base + HIST("Q_net_2Sq_subsample"), cent, cfgEventSelection.currentSubsample, qNet2 * qNet2); + hReg.fill(base + HIST("Q_net_1Q_net_3_subsample"), cent, cfgEventSelection.currentSubsample, qNet1 * qNet3); + hReg.fill(base + HIST("Q_net_4_subsample"), cent, cfgEventSelection.currentSubsample, qNet4); + + hReg.fill(base + HIST("JointF11_subsample"), cent, cfgEventSelection.currentSubsample, jointF11); + hReg.fill(base + HIST("JointF12_subsample"), cent, cfgEventSelection.currentSubsample, jointF12); + hReg.fill(base + HIST("JointF21_subsample"), cent, cfgEventSelection.currentSubsample, jointF21); + } + } + + template + void fillGenFactorialMoments(float n, float cent, Registry& hReg) + { + auto base = HIST(FCGenDir[static_cast(Prefix)]); + + double f1 = n; + double f2 = n * (n - 1.); + double f3 = n * (n - 1.) * (n - 2.); + double f4 = n * (n - 1.) * (n - 2.) * (n - 3.); + + hReg.fill(base + HIST("F1"), cent, f1); + hReg.fill(base + HIST("F2"), cent, f2); + hReg.fill(base + HIST("F3"), cent, f3); + hReg.fill(base + HIST("F4"), cent, f4); + + if (cfgEventSelection.cfgDoSubsampling) { + hReg.fill(base + HIST("F1_subsample"), cent, cfgEventSelection.currentSubsample, f1); + hReg.fill(base + HIST("F2_subsample"), cent, cfgEventSelection.currentSubsample, f2); + hReg.fill(base + HIST("F3_subsample"), cent, cfgEventSelection.currentSubsample, f3); + hReg.fill(base + HIST("F4_subsample"), cent, cfgEventSelection.currentSubsample, f4); + } + } + // ─── GEN level (net speciestruth) ─── + template + void fillGenNetFactorialMoments(float nPos, float nNeg, float cent, Registry& hReg) + { + auto base = HIST(FCGenDir[static_cast(Prefix)]); + + double nNet = nPos - nNeg; + + double f1 = nNet; + double f2 = nNet * (nNet - 1.0); + double f3 = nNet * (nNet - 1.0) * (nNet - 2.0); + double f4 = nNet * (nNet - 1.0) * (nNet - 2.0) * (nNet - 3.0); + + hReg.fill(base + HIST("F1"), cent, f1); + hReg.fill(base + HIST("F2"), cent, f2); + hReg.fill(base + HIST("F3"), cent, f3); + hReg.fill(base + HIST("F4"), cent, f4); + + if (cfgEventSelection.cfgDoSubsampling) { + hReg.fill(base + HIST("F1_subsample"), cent, cfgEventSelection.currentSubsample, f1); + hReg.fill(base + HIST("F2_subsample"), cent, cfgEventSelection.currentSubsample, f2); + hReg.fill(base + HIST("F3_subsample"), cent, cfgEventSelection.currentSubsample, f3); + hReg.fill(base + HIST("F4_subsample"), cent, cfgEventSelection.currentSubsample, f4); + } + } + void init(InitContext const&) { auto& mgr = o2::ccdb::BasicCCDBManager::instance(); @@ -200,64 +565,41 @@ struct NchCumulantsId { mgr.setCaching(true); auto ccdbObj = mgr.getForTimeStamp(cfgCCDB.cfgCCDB02Path, cfgCCDB.cfgCCDB03SOR); if (!ccdbObj) { - if (cfgDebug.printDebugMessages) + if (cfgDebug.printDebugMessages) { LOG(info) << "DEBUG :: CCDB OBJECT NOT FOUND"; + } } else { - if (cfgDebug.printDebugMessages) + if (cfgDebug.printDebugMessages) { LOG(info) << "DEBUG :: CCDB OBJECT FOUND"; + } } ccdbObj->Print(); - hPtEtaForBinSearch = reinterpret_cast(ccdbObj->FindObject("hPtEta")); - if (cfgDebug.printDebugMessages) + hPtEtaForBinSearch = dynamic_cast(ccdbObj->FindObject("hPtEta")); + if (cfgDebug.printDebugMessages) { LOG(info) << "DEBUG :: Obj Name = " << hPtEtaForBinSearch->GetName() << " :: entries = " << hPtEtaForBinSearch->GetEntries(); - std::string name = ""; + } + std::string name; for (int i = 0; i <= kDe; i++) { for (int j = 0; j < (kNeg + 1); j++) { name = "hPtEta" + getModifiedStr(static_cast(PidDire[i])) + getModifiedStr(static_cast(ChargeDire[j])); - hPtEtaForEffCorrection[i][j] = reinterpret_cast(ccdbObj->FindObject(name.c_str())); - if (cfgDebug.printDebugMessages) + hPtEtaForEffCorrection[i][j] = dynamic_cast(ccdbObj->FindObject(name.c_str())); + if (cfgDebug.printDebugMessages) { LOG(info) << "DEBUG :: Obj Name = " << hPtEtaForEffCorrection[i][j]->GetName() << " :: entries = " << hPtEtaForBinSearch->GetEntries(); + } } } mgr.setURL("http://alice-ccdb.cern.ch"); // RESET the URL otherwise the other process functions which contains ccdb lookups will fail - // QA check axes - const AxisSpec axisEvents{1, 0, 1, "Counts"}; - const AxisSpec axisEta{100, -1., +1., "#eta"}; - const AxisSpec axisRapidity{200, -5, 5, "Rapidity (y)"}; - const AxisSpec axisPt{100, 0., 5., "p_{T} (GeV/c)"}; - const AxisSpec axisP{100, 0., 5., "p (GeV/c)"}; - const AxisSpec axisTPCInnerParam{100, 0, 3, "P_innerParam_Gev"}; - const AxisSpec axisdEdx(100, 20, 500, {"#frac{dE}{dx}"}); - const AxisSpec axisVtxZ{80, -20., 20., "V_{Z} (cm)"}; - const AxisSpec axisDCAz{200, -3., 3., "DCA_{Z} (cm)"}; - const AxisSpec axisDCAxy{200, -3., 3., "DCA_{XY} (cm)"}; - const AxisSpec axisMultFT0(150, 0, 1500, "MultFT0"); - const AxisSpec axisCent(103, -1., 102., "FT0C(%)"); - const AxisSpec axisPhi(80, -1, 7, "phi"); - - const AxisSpec axisTOFBeta = {40, -2.0, 2.0, "tofBeta"}; - const AxisSpec axisTPCSignal = {100, -1, 1000, "tpcSignal"}; - const AxisSpec axisTPCNSigma = {200, -10.0, 10.0, "n#sigma_{TPC}"}; - const AxisSpec axisTOFNSigma = {200, -10.0, 10.0, "n#sigma_{TOF}"}; - const AxisSpec axisTOFExpMom = {200, 0.0f, 10.0f, "#it{p}_{tofExpMom} (GeV/#it{c})"}; - - const AxisSpec axisNch(netChBin, netChMin, netChMax, "Net_charge_dN"); - const AxisSpec axisPosCh(chBins, chMin, chMax, "Pos_charge"); - const AxisSpec axisNegCh(chBins, chMin, chMax, "Neg_charge"); - const AxisSpec axisNt(midRapBins, chMin, midRapMax, "Mult_midRap_Nch"); - const AxisSpec axisPrCh(chBins, chMin, chMax, "Pr_charge"); - const AxisSpec axisAPrCh(chBins, chMin, chMax, "APr_charge"); - const AxisSpec axisKaCh(chBins, chMin, chMax, "Ka_charge"); - const AxisSpec axisAKaCh(chBins, chMin, chMax, "AKa_charge"); - const AxisSpec axisPiCh(chBins, chMin, chMax, "Pion_Positive"); - const AxisSpec axisAPiCh(chBins, chMin, chMax, "Pion_Negative"); + const AxisSpec axisIdTag = {32, -0.5f, 31.5f, "idTag"}; + const AxisSpec axisMcTag = {32, -0.5f, 31.5f, "mcTag"}; HistogramConfigSpec qnHist1({HistType::kTHnSparseD, {axisNch, axisPosCh, axisNegCh, axisPrCh, axisAPrCh, axisKaCh, axisAKaCh, axisNt, axisCent}}); HistogramConfigSpec qnHist2({HistType::kTHnSparseD, {axisNch, axisPosCh, axisNegCh, axisPiCh, axisAPiCh, axisKaCh, axisAKaCh, axisNt, axisCent}}); + HistogramConfigSpec histTPCPIDSparse({HistType::kTHnSparseD, {axisP, axisTPCNSigma, axisIdTag, axisMcTag}}); + HistogramConfigSpec histTOFPIDSparse({HistType::kTHnSparseD, {axisP, axisTOFNSigma, axisIdTag, axisMcTag}}); HistogramConfigSpec histPPt({HistType::kTH2F, {axisP, axisPt}}); HistogramConfigSpec histPTpcInnerParam({HistType::kTH2F, {axisP, axisTPCInnerParam}}); @@ -276,6 +618,16 @@ struct NchCumulantsId { HistogramConfigSpec histTpcNSigmaTofNSigma({HistType::kTH2F, {axisTPCNSigma, axisTOFNSigma}}); HistogramConfigSpec histPtMc({HistType::kTH1F, {axisPt}}); + // Register histograms for PID validation + // Register TPC spares per species + hist.add("PIDValidation/tpcSparse_Pi", "p vs tpcNSigmaPi vs idTag vs mcTag (Pion)", histTPCPIDSparse); + hist.add("PIDValidation/tpcSparse_Ka", "p vs tpcNSigmaKa vs idTag vs mcTag (Kaon)", histTPCPIDSparse); + hist.add("PIDValidation/tpcSparse_Pr", "p vs tpcNSigmaPr vs idTag vs mcTag (Proton)", histTPCPIDSparse); + + // Register TOF spares per species + hist.add("PIDValidation/tofSparse_Pi", "p vs tofNSigmaPi vs idTag vs mcTag (Pion)", histTOFPIDSparse); + hist.add("PIDValidation/tofSparse_Ka", "p vs tofNSigmaKa vs idTag vs mcTag (Kaon)", histTOFPIDSparse); + hist.add("PIDValidation/tofSparse_Pr", "p vs tofNSigmaPr vs idTag vs mcTag (Proton)", histTOFPIDSparse); // QA check histos @@ -448,9 +800,39 @@ struct NchCumulantsId { // purityAnalysis.addClone("purityAnalysis/Pi/", "purityAnalysis/El/"); // purityAnalysis.addClone("purityAnalysis/Pi/", "purityAnalysis/De/"); + // profiles adding for Reco + addFCRecoProfiles(); + addFCRecoProfiles(); + addFCRecoProfiles(); + addFCRecoProfiles(); + addFCRecoProfiles(); + addFCRecoProfiles(); + addFCRecoProfiles(); + addFCRecoProfiles(); + + addNetQVectorProfileHistograms(); + addNetQVectorProfileHistograms(); + addNetQVectorProfileHistograms(); + addNetQVectorProfileHistograms(); + + // profiles adding for Gen + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + addFCGenProfiles(); + } // init ends - static constexpr std::string_view HistRegDire2[] = { + static constexpr std::array HistRegDire2 = { "v0Table/Full/", "v0Table/postK0sCheck/", "v0Table/postMassCut/", @@ -497,7 +879,7 @@ struct NchCumulantsId { NoId }; - static constexpr std::string_view DetDire[] = { + static constexpr std::array DetDire = { "tpcId/", "tofId/", "tpctofId/", @@ -511,7 +893,7 @@ struct NchCumulantsId { qaTracksIdfd, }; - static constexpr std::string_view HistRegDire[] = { + static constexpr std::array HistRegDire = { "QA/events/preSel/", "QA/events/postSel/", "QA/tracks/preSel/", @@ -533,27 +915,32 @@ struct NchCumulantsId { bool vetoIdOthersTPC(const T& track) { if (pidMode != kPi) { - if (std::fabs(track.tpcNSigmaPi()) < cfgVetoIdCut.cfgVetoId01PiTPC) + if (std::fabs(track.tpcNSigmaPi()) < cfgVetoIdCut.cfgVetoId01PiTPC) { return false; + } } if (pidMode != kKa) { - if (std::fabs(track.tpcNSigmaKa()) < cfgVetoIdCut.cfgVetoId03KaTPC) + if (std::fabs(track.tpcNSigmaKa()) < cfgVetoIdCut.cfgVetoId03KaTPC) { return false; + } } if (pidMode != kPr) { - if (std::fabs(track.tpcNSigmaPr()) < cfgVetoIdCut.cfgVetoId05PrTPC) + if (std::fabs(track.tpcNSigmaPr()) < cfgVetoIdCut.cfgVetoId05PrTPC) { return false; + } } if (cfgId02DoElRejection) { if (pidMode != kEl) { - if (std::fabs(track.tpcNSigmaEl()) < cfgVetoIdCut.cfgVetoId07ElTPC) + if (std::fabs(track.tpcNSigmaEl()) < cfgVetoIdCut.cfgVetoId07ElTPC) { return false; + } } } if (cfgId03DoDeRejection) { if (pidMode != kDe) { - if (std::fabs(track.tpcNSigmaDe()) < cfgVetoIdCut.cfgVetoId09DeTPC) + if (std::fabs(track.tpcNSigmaDe()) < cfgVetoIdCut.cfgVetoId09DeTPC) { return false; + } } } return true; @@ -563,27 +950,32 @@ struct NchCumulantsId { bool vetoIdOthersTOF(const T& track) { if (pidMode != kPi) { - if (std::fabs(track.tofNSigmaPi()) < cfgVetoIdCut.cfgVetoId02PiTOF) + if (std::fabs(track.tofNSigmaPi()) < cfgVetoIdCut.cfgVetoId02PiTOF) { return false; + } } if (pidMode != kKa) { - if (std::fabs(track.tofNSigmaKa()) < cfgVetoIdCut.cfgVetoId04KaTOF) + if (std::fabs(track.tofNSigmaKa()) < cfgVetoIdCut.cfgVetoId04KaTOF) { return false; + } } if (pidMode != kPr) { - if (std::fabs(track.tofNSigmaPr()) < cfgVetoIdCut.cfgVetoId06PrTOF) + if (std::fabs(track.tofNSigmaPr()) < cfgVetoIdCut.cfgVetoId06PrTOF) { return false; + } } if (cfgId02DoElRejection) { if (pidMode != kEl) { - if (std::fabs(track.tofNSigmaEl()) < cfgVetoIdCut.cfgVetoId08ElTOF) + if (std::fabs(track.tofNSigmaEl()) < cfgVetoIdCut.cfgVetoId08ElTOF) { return false; + } } } if (cfgId03DoDeRejection) { if (pidMode != kDe) { - if (std::fabs(track.tofNSigmaDe()) < cfgVetoIdCut.cfgVetoId10DeTOF) + if (std::fabs(track.tofNSigmaDe()) < cfgVetoIdCut.cfgVetoId10DeTOF) { return false; + } } } return true; @@ -593,27 +985,32 @@ struct NchCumulantsId { bool vetoIdOthersTPCTOF(const T& track) { if (pidMode != kPi) { - if (std::fabs(track.tpcNSigmaPi()) < cfgVetoIdCut.cfgVetoId01PiTPC && std::fabs(track.tofNSigmaPi()) < cfgVetoIdCut.cfgVetoId02PiTOF) + if (std::fabs(track.tpcNSigmaPi()) < cfgVetoIdCut.cfgVetoId01PiTPC && std::fabs(track.tofNSigmaPi()) < cfgVetoIdCut.cfgVetoId02PiTOF) { return false; + } } if (pidMode != kKa) { - if (std::fabs(track.tpcNSigmaKa()) < cfgVetoIdCut.cfgVetoId03KaTPC && std::fabs(track.tofNSigmaKa()) < cfgVetoIdCut.cfgVetoId04KaTOF) + if (std::fabs(track.tpcNSigmaKa()) < cfgVetoIdCut.cfgVetoId03KaTPC && std::fabs(track.tofNSigmaKa()) < cfgVetoIdCut.cfgVetoId04KaTOF) { return false; + } } if (pidMode != kPr) { - if (std::fabs(track.tpcNSigmaPr()) < cfgVetoIdCut.cfgVetoId05PrTPC && std::fabs(track.tofNSigmaPr()) < cfgVetoIdCut.cfgVetoId06PrTOF) + if (std::fabs(track.tpcNSigmaPr()) < cfgVetoIdCut.cfgVetoId05PrTPC && std::fabs(track.tofNSigmaPr()) < cfgVetoIdCut.cfgVetoId06PrTOF) { return false; + } } if (cfgId02DoElRejection) { if (pidMode != kEl) { - if (std::fabs(track.tpcNSigmaEl()) < cfgVetoIdCut.cfgVetoId07ElTPC && std::fabs(track.tofNSigmaEl()) < cfgVetoIdCut.cfgVetoId08ElTOF) + if (std::fabs(track.tpcNSigmaEl()) < cfgVetoIdCut.cfgVetoId07ElTPC && std::fabs(track.tofNSigmaEl()) < cfgVetoIdCut.cfgVetoId08ElTOF) { return false; + } } } if (cfgId03DoDeRejection) { if (pidMode != kDe) { - if (std::fabs(track.tpcNSigmaDe()) < cfgVetoIdCut.cfgVetoId09DeTPC && std::fabs(track.tofNSigmaDe()) < cfgVetoIdCut.cfgVetoId10DeTOF) + if (std::fabs(track.tpcNSigmaDe()) < cfgVetoIdCut.cfgVetoId09DeTPC && std::fabs(track.tofNSigmaDe()) < cfgVetoIdCut.cfgVetoId10DeTOF) { return false; + } } } return true; @@ -624,28 +1021,20 @@ struct NchCumulantsId { { switch (pidMode) { case kPi: - if (std::fabs(track.tpcNSigmaPi()) < nSigmaTPC && - std::fabs(track.tofNSigmaPi()) < nSigmaTOF) { - return true; - } - break; + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC && + std::fabs(track.tofNSigmaPi()) < nSigmaTOF; + case kKa: - if (std::fabs(track.tpcNSigmaKa()) < nSigmaTPC && - std::fabs(track.tofNSigmaKa()) < nSigmaTOF) { - return true; - } - break; + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC && + std::fabs(track.tofNSigmaKa()) < nSigmaTOF; + case kPr: - if (std::fabs(track.tpcNSigmaPr()) < nSigmaTPC && - std::fabs(track.tofNSigmaPr()) < nSigmaTOF) { - return true; - } - break; + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC && + std::fabs(track.tofNSigmaPr()) < nSigmaTOF; + default: return false; - break; } - return false; } template @@ -653,22 +1042,23 @@ struct NchCumulantsId { { switch (pidMode) { case kPi: - if (std::pow(track.tpcNSigmaPi() / nSigmaTPC, 2) + std::pow(track.tofNSigmaPi() / nSigmaTOF, 2) < 1.0) - return true; - break; + return std::pow(track.tpcNSigmaPi() / nSigmaTPC, 2) + + std::pow(track.tofNSigmaPi() / nSigmaTOF, 2) < + 1.0; + case kKa: - if (std::pow(track.tpcNSigmaKa() / nSigmaTPC, 2) + std::pow(track.tofNSigmaKa() / nSigmaTOF, 2) < 1.0) - return true; - break; + return std::pow(track.tpcNSigmaKa() / nSigmaTPC, 2) + + std::pow(track.tofNSigmaKa() / nSigmaTOF, 2) < + 1.0; + case kPr: - if (std::pow(track.tpcNSigmaPr() / nSigmaTPC, 2) + std::pow(track.tofNSigmaPr() / nSigmaTOF, 2) < 1.0) - return true; - break; + return std::pow(track.tpcNSigmaPr() / nSigmaTPC, 2) + + std::pow(track.tofNSigmaPr() / nSigmaTOF, 2) < + 1.0; + default: return false; - break; } - return false; } template @@ -676,16 +1066,19 @@ struct NchCumulantsId { { switch (pidMode) { case kPi: - if (std::pow(track.tpcNSigmaPi(), 2) + std::pow(track.tofNSigmaPi(), 2) < nSigmaSquaredRad) + if (std::pow(track.tpcNSigmaPi(), 2) + std::pow(track.tofNSigmaPi(), 2) < nSigmaSquaredRad) { return true; + } break; case kKa: - if (std::pow(track.tpcNSigmaKa(), 2) + std::pow(track.tofNSigmaKa(), 2) < nSigmaSquaredRad) + if (std::pow(track.tpcNSigmaKa(), 2) + std::pow(track.tofNSigmaKa(), 2) < nSigmaSquaredRad) { return true; + } break; case kPr: - if (std::pow(track.tpcNSigmaPr(), 2) + std::pow(track.tofNSigmaPr(), 2) < nSigmaSquaredRad) + if (std::pow(track.tpcNSigmaPr(), 2) + std::pow(track.tofNSigmaPr(), 2) < nSigmaSquaredRad) { return true; + } break; default: return false; @@ -697,29 +1090,30 @@ struct NchCumulantsId { template bool checkReliableTOF(const T& track) { - if (track.hasTOF()) - return true; // which check makes the information of TOF relaiable? should track.beta() be checked? - else - return false; + return track.hasTOF(); // which check makes the information of TOF relaiable? should track.beta() be checked? } template bool idTPC(const T& track, const float& nSigmaTPC) { - if (cfgId01CheckVetoCut && !vetoIdOthersTPC(track)) + if (cfgId01CheckVetoCut && !vetoIdOthersTPC(track)) { return false; + } switch (pidMode) { case kPi: - if (std::fabs(track.tpcNSigmaPi()) < nSigmaTPC) + if (std::fabs(track.tpcNSigmaPi()) < nSigmaTPC) { return true; + } break; case kKa: - if (std::fabs(track.tpcNSigmaKa()) < nSigmaTPC) + if (std::fabs(track.tpcNSigmaKa()) < nSigmaTPC) { return true; + } break; case kPr: - if (std::fabs(track.tpcNSigmaPr()) < nSigmaTPC) + if (std::fabs(track.tpcNSigmaPr()) < nSigmaTPC) { return true; + } break; default: return false; @@ -731,8 +1125,9 @@ struct NchCumulantsId { template bool idTPCTOF(const T& track, const int& pidCutType, const float& nSigmaTPC, const float& nSigmaTOF, const float& nSigmaSquaredRad) { - if (cfgId01CheckVetoCut && !vetoIdOthersTPCTOF(track)) + if (cfgId01CheckVetoCut && !vetoIdOthersTPCTOF(track)) { return false; + } if (pidCutType == kRectangularCut) { return selIdRectangularCut(track, nSigmaTPC, nSigmaTOF); } else if (pidCutType == kCircularCut) { @@ -774,8 +1169,42 @@ struct NchCumulantsId { } } + template + int getMCTag(const T& track) + { + int mcTag = 0; + if (!track.has_mcParticle()) { + return mcTag; + } + auto mcPart = track.mcParticle(); + int pdgCode = std::abs(mcPart.pdgCode()); + + if (pdgCode == kPiPlus || pdgCode == kPiMinus) { + BITSET(mcTag, McBitPI); + } else if (pdgCode == kKPlus || pdgCode == kKMinus) { + BITSET(mcTag, McBitKA); + } else if (pdgCode == kProton || pdgCode == kProtonBar) { + BITSET(mcTag, McBitPR); + } else if (pdgCode == kElectron || pdgCode == kPositron) { + BITSET(mcTag, McBitEL); + } else if (pdgCode == kDeuteron || pdgCode == -kDeuteron) { + BITSET(mcTag, McBitDE); + } + + return mcTag; + } // //______________________________Identification Functions________________________________________________________________ + // Victor slection cuts for electron and other rejections + template + bool selTrackForId(const T& track) + { + return !(cfgIdElRejLowNSigma < track.tpcNSigmaEl() && track.tpcNSigmaEl() < cfgIdElRejHighNSigma && + std::fabs(track.tpcNSigmaPi()) > cfgIdPiRejNSigma && + std::fabs(track.tpcNSigmaKa()) > cfgIdKaRejNSigma && + std::fabs(track.tpcNSigmaPr()) > cfgIdPrRejNSigma); + } + // Pion template bool selPion(const T& track, int& IdMethod) @@ -855,15 +1284,15 @@ struct NchCumulantsId { } template - void fillCollQA(const T& col, const int& nCh, const int& nT) + void fillCollQA(const T& coll, const float& nCh, const float& nT) { - hist.fill(HIST(HistRegDire[mode]) + HIST("h_VtxZ"), col.posZ()); + hist.fill(HIST(HistRegDire[mode]) + HIST("h_VtxZ"), coll.posZ()); hist.fill(HIST(HistRegDire[mode]) + HIST("h_Counts"), 0.5); - hist.fill(HIST(HistRegDire[mode]) + HIST("multFT0"), col.multFT0C()); - hist.fill(HIST(HistRegDire[mode]) + HIST("centFT0"), col.centFT0M()); + hist.fill(HIST(HistRegDire[mode]) + HIST("multFT0"), coll.multFT0C()); + hist.fill(HIST(HistRegDire[mode]) + HIST("centFT0"), coll.centFT0M()); if (mode == qaEventPostSel) { hist.fill(HIST(HistRegDire[mode]) + HIST("net_charge"), nCh); - hist.fill(HIST(HistRegDire[mode]) + HIST("Nt_centFT"), col.centFT0M(), nT); + hist.fill(HIST(HistRegDire[mode]) + HIST("Nt_centFT"), coll.centFT0M(), nT); } } @@ -894,6 +1323,28 @@ struct NchCumulantsId { fillIdentificationQA(hist, track); // Look at Proton } + // template + // void fillGenTrackQA(H& histReg, const T& mcTrack) + // { + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h12_p"), mcTrack.p()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h13_pt"), mcTrack.pt()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h14_eta"), mcTrack.eta()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h15_phi"), mcTrack.phi()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h16_rapidity"), mcTrack.y()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h20_pt_eta"), mcTrack.pt(), mcTrack.eta()); + // } + // + // template + // void fillRecoTrackQA(H& histReg, const T& track) + // { + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h12_p"), track.p()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h13_pt"), track.pt()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h14_eta"), track.eta()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h15_phi"), track.phi()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h16_rapidity"), track.y()); + // histReg.fill(HIST(HistRegDire2[Mode]) + HIST(PidDire[pidMode]) + HIST("h20_pt_eta"), track.pt(), track.eta()); + // } + template void fillGenTrackQA(HistogramRegistry& r, const TrackType& t) { @@ -943,34 +1394,35 @@ struct NchCumulantsId { void executeTrackAnalysisPart(const T& track, const int& trackIdTag, float& nP, float& nM, const int& idMethodPi, const bool& trackIsPion, float& nAPi, float& nPi, const int& idMethodKa, const bool& trackIsKaon, float& nAKa, float& nKa, - const int& idMethodPr, const bool& trackIsProton, float& nPr, float& nAPr, H& recoAnalysis) + const int& idMethodPr, const bool& trackIsProton, float& nPr, float& nAPr, H& hReg) { - if (flagUnusedVariableError) + if (cfgEventSelection.flagUnusedVariableError) { LOG(info) << trackIdTag << idMethodPi << ":" << idMethodKa << ":" << idMethodPr; + } if (track.sign() > 0) { - // fillRecoTrackQA(recoAnalysis, track); + // fillRecoTrackQA(hReg, track); nP++; - recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h12_p"), track.p()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h13_pt"), track.pt()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h14_eta"), track.eta()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h15_phi"), track.phi()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h16_rapidity"), track.y()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h20_pt_eta"), track.pt(), track.eta()); + hReg.fill(HIST("recoAnalysis/Charge/Pos/h12_p"), track.p()); + hReg.fill(HIST("recoAnalysis/Charge/Pos/h13_pt"), track.pt()); + hReg.fill(HIST("recoAnalysis/Charge/Pos/h14_eta"), track.eta()); + hReg.fill(HIST("recoAnalysis/Charge/Pos/h15_phi"), track.phi()); + hReg.fill(HIST("recoAnalysis/Charge/Pos/h16_rapidity"), track.y()); + hReg.fill(HIST("recoAnalysis/Charge/Pos/h20_pt_eta"), track.pt(), track.eta()); } if (track.sign() < 0) { - // fillRecoTrackQA(recoAnalysis, track); + // fillRecoTrackQA(hReg, track); nM++; - recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h12_p"), track.p()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h13_pt"), track.pt()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h14_eta"), track.eta()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h15_phi"), track.phi()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h16_rapidity"), track.y()); - recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h20_pt_eta"), track.pt(), track.eta()); + hReg.fill(HIST("recoAnalysis/Charge/Neg/h12_p"), track.p()); + hReg.fill(HIST("recoAnalysis/Charge/Neg/h13_pt"), track.pt()); + hReg.fill(HIST("recoAnalysis/Charge/Neg/h14_eta"), track.eta()); + hReg.fill(HIST("recoAnalysis/Charge/Neg/h15_phi"), track.phi()); + hReg.fill(HIST("recoAnalysis/Charge/Neg/h16_rapidity"), track.y()); + hReg.fill(HIST("recoAnalysis/Charge/Neg/h20_pt_eta"), track.pt(), track.eta()); } if (trackIsPion) { // if (idMethodPi == kTPCidentified) { - // fillIdentificationQA(hist, track); // set hist as recoAnalysis after tpcId etc add true + // fillIdentificationQA(hist, track); // set hist as hReg after tpcId etc add true // } else if (idMethodPi == kTPCTOFidentified) { // fillIdentificationQA(hist, track); // } else if (idMethodPi == kUnidentified) { @@ -978,12 +1430,12 @@ struct NchCumulantsId { // } if (track.sign() > 0) { nPi++; - fillRecoTrackQA(recoAnalysis, track); + fillRecoTrackQA(hReg, track); } else if (track.sign() < 0) { nAPi++; - fillRecoTrackQA(recoAnalysis, track); + fillRecoTrackQA(hReg, track); } - // fillRecoTrackQA(recoAnalysis, track); + // fillRecoTrackQA(hReg, track); } if (trackIsKaon) { // if (idMethodKa == kTPCidentified) { @@ -995,12 +1447,12 @@ struct NchCumulantsId { // } if (track.sign() > 0) { nKa++; - fillRecoTrackQA(recoAnalysis, track); + fillRecoTrackQA(hReg, track); } else if (track.sign() < 0) { nAKa++; - fillRecoTrackQA(recoAnalysis, track); + fillRecoTrackQA(hReg, track); } - // fillRecoTrackQA(recoAnalysis, track); + // fillRecoTrackQA(hReg, track); } if (trackIsProton) { // if (idMethodPr == kTPCidentified) { @@ -1012,29 +1464,48 @@ struct NchCumulantsId { // } if (track.sign() > 0) { nPr++; - fillRecoTrackQA(recoAnalysis, track); + fillRecoTrackQA(hReg, track); } else if (track.sign() < 0) { nAPr++; - fillRecoTrackQA(recoAnalysis, track); + fillRecoTrackQA(hReg, track); } - // fillRecoTrackQA(recoAnalysis, track); + // fillRecoTrackQA(hReg, track); } - // recoAnalysis.fill(HIST("recoAnalysis/SelectedTrack_IdentificationTag"), trackIdTag); + // hReg.fill(HIST("recoAnalysis/SelectedTrack_IdentificationTag"), trackIdTag); } + // fill the basis for factorial cumulants using MyAllTracks = soa::Join; - using MyCollisions = soa::Join; + using MyCollisions = soa::Join; using MyTracksWithMclabels = soa::Join; - using MyCollisionsWithMcLabels = soa::Join; + using MyCollisionsWithMcLabels = soa::Join; + + template + bool isEventSelected(const CollisionType& coll) + { + if (cfgEventSelection.cfgEvSel01doNoSameBunchPileup && + !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (cfgEventSelection.cfgEvSel02doIsGoodZvtxFT0vsPV && + !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (cfgEventSelection.cfgEvSel03doIsGoodITSLayersAll && + !coll.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + return true; + } // tracks and collision filters - Filter col = aod::evsel::sel8 == true; + Filter colSel8 = aod::evsel::sel8 == true; Filter colFilter = nabs(aod::collision::posZ) < cfgCutPosZ; Filter trackFilter = requireGlobalTrackInFilter(); Filter trackPt = (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); @@ -1081,6 +1552,9 @@ struct NchCumulantsId { if constexpr (analysisType == doDataProcessing) { for (const auto& col : collisions) { + if (!isEventSelected(col)) { + continue; + } nP = 0; nM = 0; nCh = 0; @@ -1110,7 +1584,12 @@ struct NchCumulantsId { nM += hPtEtaForEffCorrection[kCh][kNeg]->GetBinContent(ptEtaBin); } - int idMethod; + // Reject electrons first + if (cfgEventSelection.cfgDoRejectionForId && !selTrackForId(track)) { + continue; + } + + int idMethod = -1; // pion if (selPion(track, idMethod)) { if (track.sign() == 1) { @@ -1120,10 +1599,12 @@ struct NchCumulantsId { nAPi += hPtEtaForEffCorrection[kPi][kNeg]->GetBinContent(ptEtaBin); } - if (idMethod == kTPCidentified) + if (idMethod == kTPCidentified) { fillIdentificationQA(hist, track); - if (idMethod == kTPCTOFidentified) + } + if (idMethod == kTPCTOFidentified) { fillIdentificationQA(hist, track); + } } // kaon if (selKaon(track, idMethod)) { @@ -1134,10 +1615,12 @@ struct NchCumulantsId { nAKa += hPtEtaForEffCorrection[kKa][kNeg]->GetBinContent(ptEtaBin); } - if (idMethod == kTPCidentified) + if (idMethod == kTPCidentified) { fillIdentificationQA(hist, track); - if (idMethod == kTPCTOFidentified) + } + if (idMethod == kTPCTOFidentified) { fillIdentificationQA(hist, track); + } } // proton if (selProton(track, idMethod)) { @@ -1148,10 +1631,12 @@ struct NchCumulantsId { nAPr += hPtEtaForEffCorrection[kPr][kNeg]->GetBinContent(ptEtaBin); } - if (idMethod == kTPCidentified) + if (idMethod == kTPCidentified) { fillIdentificationQA(hist, track); - if (idMethod == kTPCTOFidentified) + } + if (idMethod == kTPCTOFidentified) { fillIdentificationQA(hist, track); + } } } // track loop ends nCh = nP - nM; @@ -1171,16 +1656,19 @@ struct NchCumulantsId { LOG(warning) << "No MC collision for this collision, skip..."; continue; } - float nP = 0; - float nM = 0; - float nCh = 0; - float nT = 0; - float nPr = 0; - float nAPr = 0; - float nKa = 0; - float nAKa = 0; - float nPi = 0; - float nAPi = 0; + if (!isEventSelected(col)) { + continue; + } + nP = 0; + nM = 0; + nCh = 0; + nT = 0; + nPr = 0; + nAPr = 0; + nKa = 0; + nAKa = 0; + nPi = 0; + nAPi = 0; // group tracks manually with corresponding collision using col id; const uint64_t collIdx = col.globalIndex(); const auto tracksTablePerColl = tracks.sliceBy(mctracksPerCollisionPreslice, collIdx); @@ -1206,17 +1694,37 @@ struct NchCumulantsId { idMethodKa = kUnidentified; idMethodPr = kUnidentified; + // Get MC tag + int mcTag = getMCTag(track); + + // Reject electrons first + if (cfgEventSelection.cfgDoRejectionForId && !selTrackForId(track)) { + continue; + } + if (selPion(track, idMethodPi)) { trackIsPion = true; - BITSET(trackIdTag, ID_BIT_PI); + BITSET(trackIdTag, IdBitPI); + hist.fill(HIST("PIDValidation/tpcSparse_Pi"), track.p(), track.tpcNSigmaPi(), trackIdTag, mcTag); + if (track.hasTOF()) { + hist.fill(HIST("PIDValidation/tofSparse_Pi"), track.p(), track.tofNSigmaPi(), trackIdTag, mcTag); + } } if (selKaon(track, idMethodKa)) { trackIsKaon = true; - BITSET(trackIdTag, ID_BIT_KA); + BITSET(trackIdTag, IdBitKA); + hist.fill(HIST("PIDValidation/tpcSparse_Ka"), track.p(), track.tpcNSigmaKa(), trackIdTag, mcTag); + if (track.hasTOF()) { + hist.fill(HIST("PIDValidation/tofSparse_Ka"), track.p(), track.tofNSigmaKa(), trackIdTag, mcTag); + } } if (selProton(track, idMethodPr)) { trackIsProton = true; - BITSET(trackIdTag, ID_BIT_PR); + BITSET(trackIdTag, IdBitPR); + hist.fill(HIST("PIDValidation/tpcSparse_Pr"), track.p(), track.tpcNSigmaPr(), trackIdTag, mcTag); + if (track.hasTOF()) { + hist.fill(HIST("PIDValidation/tofSparse_Pr"), track.p(), track.tofNSigmaPr(), trackIdTag, mcTag); + } } if constexpr (analysisType == doPurityProcessing) { @@ -1297,6 +1805,9 @@ struct NchCumulantsId { LOG(warning) << "No MC collision for this event, skip..."; continue; } + if (!isEventSelected(col)) { + continue; + } const auto& mcColl = col.mcCollision(); // ---- apply same Vz cut as data/reco ---- @@ -1330,6 +1841,7 @@ struct NchCumulantsId { int pdg = mcTrack.pdgCode(); if (pdg == kPiPlus || pdg == kKPlus || pdg == kProton || pdg == kPositron || pdg == kMuonPlus || pdg == kDeuteron) { + // fillGenTrackQA(genAnalysis, mcTrack); nP++; genAnalysis.fill(HIST("genAnalysis/Charge/Pos/h12_p"), mcTrack.p()); @@ -1339,6 +1851,7 @@ struct NchCumulantsId { genAnalysis.fill(HIST("genAnalysis/Charge/Pos/h16_rapidity"), mcTrack.y()); genAnalysis.fill(HIST("genAnalysis/Charge/Pos/h20_pt_eta"), mcTrack.pt(), mcTrack.eta()); } else if (pdg == kPiMinus || pdg == kKMinus || pdg == kProtonBar || pdg == kElectron || pdg == kMuonMinus || pdg == -kDeuteron) { + // fillGenTrackQA(genAnalysis, mcTrack); nM++; genAnalysis.fill(HIST("genAnalysis/Charge/Neg/h12_p"), mcTrack.p()); @@ -1350,21 +1863,27 @@ struct NchCumulantsId { } // ----- Pions ----- if (pdg == kPiPlus) { + // fillGenTrackQA(genAnalysis, mcTrack); nPi++; fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kPiMinus) { + // fillGenTrackQA(genAnalysis, mcTrack); nAPi++; fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kKPlus) { + // fillGenTrackQA(genAnalysis, mcTrack); nKa++; fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kKMinus) { + // fillGenTrackQA(genAnalysis, mcTrack); nAKa++; fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kProton) { + // fillGenTrackQA(genAnalysis, mcTrack); nPr++; fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kProtonBar) { + // fillGenTrackQA(genAnalysis, mcTrack); nAPr++; fillGenTrackQA(genAnalysis, mcTrack); } @@ -1380,8 +1899,10 @@ struct NchCumulantsId { void processSim(MyFilteredColsWithMcLabels const& collisions, MyFilteredTracksWithMcLabels const& tracks, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) { - if (flagUnusedVariableError) + + if (cfgEventSelection.flagUnusedVariableError) { LOG(info) << mcCollisions.size(); + } bool trackIsPion = false; bool trackIsKaon = false; bool trackIsProton = false; @@ -1399,10 +1920,14 @@ struct NchCumulantsId { LOG(warning) << "No MC collision for this collision, skip..."; continue; } + if (!isEventSelected(col)) { + continue; + } auto mcCollision = col.mcCollision(); - if (checkCollPosZMc && std::abs(mcCollision.posZ()) > cfgCutPosZ) + if (cfgEventSelection.checkCollPosZMc && std::abs(mcCollision.posZ()) > cfgCutPosZ) { continue; + } // slice reco tracks to this collision const uint64_t collIdx = col.globalIndex(); @@ -1410,6 +1935,18 @@ struct NchCumulantsId { // slice mc particles to mc collisions const auto mcTracksTablePerMcColl = mcParticles.sliceBy(mcTracksPerMcCollisionPreslice, mcCollision.globalIndex()); + float cent = col.centFT0M(); + + EffPowerSums prPow; + EffPowerSums aprPow; + EffPowerSums pipPow; + EffPowerSums pimPow; + EffPowerSums kapPow; + EffPowerSums kamPow; + EffPowerSums posPow; + EffPowerSums negPow; + + cfgEventSelection.currentSubsample = static_cast(cfgEventSelection.fRandom->Uniform(0, cfgEventSelection.subSampleAxis.value[0])); // Denominator -- Generator level(truth) @@ -1419,16 +1956,19 @@ struct NchCumulantsId { float nPiGen = 0, nAPiGen = 0; for (const auto& mcTrack : mcTracksTablePerMcColl) { - if (!mcTrack.isPhysicalPrimary()) + if (!mcTrack.isPhysicalPrimary()) { continue; + } if (mcTrack.pt() <= cfgCutPtMin || mcTrack.pt() >= cfgCutPtMax || - std::abs(mcTrack.eta()) >= cfgCutEta) + std::abs(mcTrack.eta()) >= cfgCutEta) { continue; + } int pdg = mcTrack.pdgCode(); if (pdg == kPiPlus || pdg == kKPlus || pdg == kProton || pdg == kPositron || pdg == kMuonPlus || pdg == kDeuteron) { + // fillGenTrackQA(genAnalysis, mcTrack); nPGen++; genAnalysis.fill(HIST("genAnalysis/Charge/Pos/h12_p"), mcTrack.p()); @@ -1438,6 +1978,7 @@ struct NchCumulantsId { genAnalysis.fill(HIST("genAnalysis/Charge/Pos/h16_rapidity"), mcTrack.y()); genAnalysis.fill(HIST("genAnalysis/Charge/Pos/h20_pt_eta"), mcTrack.pt(), mcTrack.eta()); } else if (pdg == kPiMinus || pdg == kKMinus || pdg == kProtonBar || pdg == kElectron || pdg == kMuonMinus || pdg == -kDeuteron) { + // fillGenTrackQA(genAnalysis, mcTrack); nMGen++; genAnalysis.fill(HIST("genAnalysis/Charge/Neg/h12_p"), mcTrack.p()); @@ -1447,24 +1988,38 @@ struct NchCumulantsId { genAnalysis.fill(HIST("genAnalysis/Charge/Neg/h16_rapidity"), mcTrack.y()); genAnalysis.fill(HIST("genAnalysis/Charge/Neg/h20_pt_eta"), mcTrack.pt(), mcTrack.eta()); } - + // ----- Pions ----- if (pdg == kPiPlus) { + // fillGenTrackQA(genAnalysis, mcTrack); nPiGen++; fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kPiMinus) { + // fillGenTrackQA(genAnalysis, mcTrack); nAPiGen++; fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kKPlus) { - nKaGen++; + // fillGenTrackQA(genAnalysis, mcTrack); + if (mcTrack.pt() >= cfgEventSelection.cfgCutPtMinKa && mcTrack.pt() <= cfgEventSelection.cfgCutPtMaxKa) { + nKaGen++; + } fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kKMinus) { - nAKaGen++; + // fillGenTrackQA(genAnalysis, mcTrack); + if (mcTrack.pt() >= cfgEventSelection.cfgCutPtMinKa && mcTrack.pt() <= cfgEventSelection.cfgCutPtMaxKa) { + nAKaGen++; + } fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kProton) { - nPrGen++; + // fillGenTrackQA(genAnalysis, mcTrack); + if (mcTrack.pt() >= cfgEventSelection.cfgCutPtMinPr && mcTrack.pt() <= cfgEventSelection.cfgCutPtMaxPr) { + nPrGen++; + } fillGenTrackQA(genAnalysis, mcTrack); } else if (pdg == kProtonBar) { - nAPrGen++; + // fillGenTrackQA(genAnalysis, mcTrack); + if (mcTrack.pt() >= cfgEventSelection.cfgCutPtMinPr && mcTrack.pt() <= cfgEventSelection.cfgCutPtMaxPr) { + nAPrGen++; + } fillGenTrackQA(genAnalysis, mcTrack); } @@ -1473,12 +2028,28 @@ struct NchCumulantsId { nTGen = nPGen + nMGen; // ── Fill GEN sparse (denominator) ──────────────────────── - hist.fill(HIST("sim/gen/sparse1"), nChGen, nPGen, nMGen, - nPrGen, nAPrGen, nKaGen, nAKaGen, nTGen, - col.centFT0M()); - hist.fill(HIST("sim/gen/sparse2"), nChGen, nPGen, nMGen, - nPiGen, nAPiGen, nKaGen, nAKaGen, nTGen, - col.centFT0M()); + if (cfgEventSelection.fillSparseForGen) { + hist.fill(HIST("sim/gen/sparse1"), nChGen, nPGen, nMGen, + nPrGen, nAPrGen, nKaGen, nAKaGen, nTGen, + cent); + hist.fill(HIST("sim/gen/sparse2"), nChGen, nPGen, nMGen, + nPiGen, nAPiGen, nKaGen, nAKaGen, nTGen, + cent); + } + fillGenFactorialMoments(nPrGen, cent, registry); + fillGenFactorialMoments(nAPrGen, cent, registry); + fillGenFactorialMoments(nPiGen, cent, registry); + fillGenFactorialMoments(nAPiGen, cent, registry); + fillGenFactorialMoments(nKaGen, cent, registry); + fillGenFactorialMoments(nAKaGen, cent, registry); + fillGenFactorialMoments(nPGen, cent, registry); + fillGenFactorialMoments(nMGen, cent, registry); + + fillGenNetFactorialMoments(nPGen, nMGen, cent, registry); + fillGenNetFactorialMoments(nPrGen, nAPrGen, cent, registry); + fillGenNetFactorialMoments(nPiGen, nAPiGen, cent, registry); + fillGenNetFactorialMoments(nKaGen, nAKaGen, cent, registry); + // ── End of GEN level filling // // Numerator - Reconstructed + truth matched // reco->selFunc passed, no pdg @@ -1502,10 +2073,13 @@ struct NchCumulantsId { continue; } auto mcPart = track.mcParticle(); - if (!mcPart.isPhysicalPrimary()) + if (!mcPart.isPhysicalPrimary()) { continue; + } int pdg = mcPart.pdgCode(); + int mcTag = getMCTag(track); + fillTrackQA(track); trackIsPion = false; @@ -1516,17 +2090,41 @@ struct NchCumulantsId { idMethodKa = kUnidentified; idMethodPr = kUnidentified; + // Reject electrons first + if (cfgEventSelection.cfgDoRejectionForId && !selTrackForId(track)) { + continue; + } + + // Fill separate spares for each species if it passes the cut if (selPion(track, idMethodPi)) { trackIsPion = true; - BITSET(trackIdTag, ID_BIT_PI); + BITSET(trackIdTag, IdBitPI); + // Fill TPC sparse for pion + hist.fill(HIST("PIDValidation/tpcSparse_Pi"), track.p(), track.tpcNSigmaPi(), trackIdTag, mcTag); + // Fill TOF sparse for pion if has TOF + if (track.hasTOF()) { + hist.fill(HIST("PIDValidation/tofSparse_Pi"), track.p(), track.tofNSigmaPi(), trackIdTag, mcTag); + } } if (selKaon(track, idMethodKa)) { trackIsKaon = true; - BITSET(trackIdTag, ID_BIT_KA); + BITSET(trackIdTag, IdBitKA); + // Fill TPC sparse for kaon + hist.fill(HIST("PIDValidation/tpcSparse_Ka"), track.p(), track.tpcNSigmaKa(), trackIdTag, mcTag); + // Fill TOF sparse for kaon if has TOF + if (track.hasTOF()) { + hist.fill(HIST("PIDValidation/tofSparse_Ka"), track.p(), track.tofNSigmaKa(), trackIdTag, mcTag); + } } if (selProton(track, idMethodPr)) { trackIsProton = true; - BITSET(trackIdTag, ID_BIT_PR); + BITSET(trackIdTag, IdBitPR); + // Fill TPC sparse for proton + hist.fill(HIST("PIDValidation/tpcSparse_Pr"), track.p(), track.tpcNSigmaPr(), trackIdTag, mcTag); + // Fill TOF sparse for proton if has TOF + if (track.hasTOF()) { + hist.fill(HIST("PIDValidation/tofSparse_Pr"), track.p(), track.tofNSigmaPr(), trackIdTag, mcTag); + } } // bool isKnownCharged = (std::abs(pdg) == kPiPlus || @@ -1546,6 +2144,9 @@ struct NchCumulantsId { recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h15_phi"), track.phi()); recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h16_rapidity"), track.y()); recoAnalysis.fill(HIST("recoAnalysis/Charge/Pos/h20_pt_eta"), track.pt(), track.eta()); + + float weight = hPtEtaForEffCorrection[kCh][kPos]->GetBinContent(ptEtaBin); + fillEffPower(posPow, weight); } else if (track.sign() < 0) { nMRec += hPtEtaForEffCorrection[kCh][kNeg]->GetBinContent(ptEtaBin); recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h12_p"), track.p()); @@ -1554,6 +2155,9 @@ struct NchCumulantsId { recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h15_phi"), track.phi()); recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h16_rapidity"), track.y()); recoAnalysis.fill(HIST("recoAnalysis/Charge/Neg/h20_pt_eta"), track.pt(), track.eta()); + + float weight = hPtEtaForEffCorrection[kCh][kNeg]->GetBinContent(ptEtaBin); + fillEffPower(negPow, weight); } // species reco — sel passes, PDG not checked (raw reco) @@ -1561,50 +2165,96 @@ struct NchCumulantsId { if (track.sign() > 0) { nPiRec += hPtEtaForEffCorrection[kPi][kPos]->GetBinContent(ptEtaBin); fillRecoTrackQA(recoAnalysis, track); + + float weight = hPtEtaForEffCorrection[kPi][kPos]->GetBinContent(ptEtaBin); + fillEffPower(pipPow, weight); } else if (track.sign() < 0) { nAPiRec += hPtEtaForEffCorrection[kPi][kNeg]->GetBinContent(ptEtaBin); fillRecoTrackQA(recoAnalysis, track); + + float weight = hPtEtaForEffCorrection[kPi][kNeg]->GetBinContent(ptEtaBin); + fillEffPower(pimPow, weight); + } + // PID band QA for pions + if (idMethodPi == kTPCidentified) { + fillIdentificationQA(hist, track); + } + if (idMethodPi == kTPCTOFidentified) { + fillIdentificationQA(hist, track); } } else if (trackIsKaon) { if (track.sign() > 0) { - nKaRec += hPtEtaForEffCorrection[kKa][kPos]->GetBinContent(ptEtaBin); fillRecoTrackQA(recoAnalysis, track); + if (track.pt() >= cfgEventSelection.cfgCutPtMinKa && track.pt() <= cfgEventSelection.cfgCutPtMaxKa) { + nKaRec += hPtEtaForEffCorrection[kKa][kPos]->GetBinContent(ptEtaBin); + float weight = hPtEtaForEffCorrection[kKa][kPos]->GetBinContent(ptEtaBin); + fillEffPower(kapPow, weight); + } } else if (track.sign() < 0) { - nAKaRec += hPtEtaForEffCorrection[kKa][kNeg]->GetBinContent(ptEtaBin); fillRecoTrackQA(recoAnalysis, track); + if (track.pt() >= cfgEventSelection.cfgCutPtMinKa && track.pt() <= cfgEventSelection.cfgCutPtMaxKa) { + nAKaRec += hPtEtaForEffCorrection[kKa][kNeg]->GetBinContent(ptEtaBin); + float weight = hPtEtaForEffCorrection[kKa][kNeg]->GetBinContent(ptEtaBin); + fillEffPower(kamPow, weight); + } + } + // PID band QA for kaons + if (idMethodKa == kTPCidentified) { + fillIdentificationQA(hist, track); + } + if (idMethodKa == kTPCTOFidentified) { + fillIdentificationQA(hist, track); } } else if (trackIsProton) { if (track.sign() > 0) { - nPrRec += hPtEtaForEffCorrection[kPr][kPos]->GetBinContent(ptEtaBin); fillRecoTrackQA(recoAnalysis, track); + if (track.pt() >= cfgEventSelection.cfgCutPtMinPr && track.pt() <= cfgEventSelection.cfgCutPtMaxPr) { + nPrRec += hPtEtaForEffCorrection[kPr][kPos]->GetBinContent(ptEtaBin); + float weight = hPtEtaForEffCorrection[kPr][kPos]->GetBinContent(ptEtaBin); + fillEffPower(prPow, weight); + } } else if (track.sign() < 0) { - nAPrRec += hPtEtaForEffCorrection[kPr][kNeg]->GetBinContent(ptEtaBin); fillRecoTrackQA(recoAnalysis, track); + if (track.pt() >= cfgEventSelection.cfgCutPtMinPr && track.pt() <= cfgEventSelection.cfgCutPtMaxPr) { + nAPrRec += hPtEtaForEffCorrection[kPr][kNeg]->GetBinContent(ptEtaBin); + float weight = hPtEtaForEffCorrection[kPr][kNeg]->GetBinContent(ptEtaBin); + fillEffPower(aprPow, weight); + } + } + // PID band QA for protons + if (idMethodPr == kTPCidentified) { + fillIdentificationQA(hist, track); + } + if (idMethodPr == kTPCTOFidentified) { + fillIdentificationQA(hist, track); } } // purity check - check pdg aginst sign bool purityPion = false; + bool purityKaon = false; + bool purityProton = false; + if (trackIsPion) { - if (track.sign() > 0 && pdg == kPiPlus) + if (track.sign() > 0 && pdg == kPiPlus) { purityPion = true; - if (track.sign() < 0 && pdg == kPiMinus) + } + if (track.sign() < 0 && pdg == kPiMinus) { purityPion = true; - } - - bool purityKaon = false; - if (trackIsKaon) { - if (track.sign() > 0 && pdg == kKPlus) + } + } else if (trackIsKaon) { + if (track.sign() > 0 && pdg == kKPlus) { purityKaon = true; - if (track.sign() < 0 && pdg == kKMinus) + } + if (track.sign() < 0 && pdg == kKMinus) { purityKaon = true; - } - - bool purityProton = false; - if (trackIsProton) { - if (track.sign() > 0 && pdg == kProton) + } + } else if (trackIsProton) { + if (track.sign() > 0 && pdg == kProton) { purityProton = true; - if (track.sign() < 0 && pdg == kProtonBar) + } + if (track.sign() < 0 && pdg == kProtonBar) { purityProton = true; + } } // charge purity — track.sign() + isKnownCharged + PDG sign consistency @@ -1657,24 +2307,42 @@ struct NchCumulantsId { fillPurityTrackQA(purityAnalysis, track); } } - } + } // reconstructed track loop ends nChRec = nPRec - nMRec; nTRec = nPRec + nMRec; nChPur = nPPur - nMPur; nTPur = nPPur + nMPur; // ── fill reco histos ───────────────────────────────────── - hist.fill(HIST("sim/reco/sparse1"), nChRec, nPRec, nMRec, - nPrRec, nAPrRec, nKaRec, nAKaRec, nTRec, col.centFT0M()); - hist.fill(HIST("sim/reco/sparse2"), nChRec, nPRec, nMRec, - nPiRec, nAPiRec, nKaRec, nAKaRec, nTRec, col.centFT0M()); + if (cfgEventSelection.fillSparseForReco) { + hist.fill(HIST("sim/reco/sparse1"), nChRec, nPRec, nMRec, + nPrRec, nAPrRec, nKaRec, nAKaRec, nTRec, cent); + hist.fill(HIST("sim/reco/sparse2"), nChRec, nPRec, nMRec, + nPiRec, nAPiRec, nKaRec, nAKaRec, nTRec, cent); + } // ── fill purity histos ─────────────────────────────────── - hist.fill(HIST("sim/purity/sparse1"), nChPur, nPPur, nMPur, - nPrPur, nAPrPur, nKaPur, nAKaPur, nTPur, col.centFT0M()); - hist.fill(HIST("sim/purity/sparse2"), nChPur, nPPur, nMPur, - nPiPur, nAPiPur, nKaPur, nAKaPur, nTPur, col.centFT0M()); - + if (cfgEventSelection.fillSparseForPurity) { + hist.fill(HIST("sim/purity/sparse1"), nChPur, nPPur, nMPur, + nPrPur, nAPrPur, nKaPur, nAKaPur, nTPur, cent); + hist.fill(HIST("sim/purity/sparse2"), nChPur, nPPur, nMPur, + nPiPur, nAPiPur, nKaPur, nAKaPur, nTPur, cent); + } + fillCollQA(col, nChRec, nTRec); + + fillFCBasis(prPow, cent, registry); + fillFCBasis(aprPow, cent, registry); + fillFCBasis(pipPow, cent, registry); + fillFCBasis(pimPow, cent, registry); + fillFCBasis(kapPow, cent, registry); + fillFCBasis(kamPow, cent, registry); + fillFCBasis(posPow, cent, registry); + fillFCBasis(negPow, cent, registry); + + fillNetQVectorProfileHistograms(posPow, negPow, cent, registry); + fillNetQVectorProfileHistograms(prPow, aprPow, cent, registry); + fillNetQVectorProfileHistograms(pipPow, pimPow, cent, registry); + fillNetQVectorProfileHistograms(kapPow, kamPow, cent, registry); } // common collision loop ends } // process sim ends PROCESS_SWITCH(NchCumulantsId, processSim, "Process Sim: Gen + Reco + Purity", true); diff --git a/PWGCF/EbyEFluctuations/Tasks/netchargeFluctuations.cxx b/PWGCF/EbyEFluctuations/Tasks/netchargeFluctuations.cxx index 7b5cf8ad25d..a60f36a8c3a 100644 --- a/PWGCF/EbyEFluctuations/Tasks/netchargeFluctuations.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/netchargeFluctuations.cxx @@ -60,13 +60,9 @@ enum RunType { // Structure to handle net charge fluctuation analysis struct NetchargeFluctuations { - // Macro to define configurable parameters with default values and help text - -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; - // Services for PDG and CCDB (Calibration and Condition Database) - Service pdgService; // Particle data group service - Service ccdb; // CCDB manager service + Service pdgService{}; // Particle data group service + Service ccdb{}; // CCDB manager service // Random number generator for statistical fluctuations, initialized with seed 0 TRandom3* fRndm = new TRandom3(0); @@ -80,10 +76,22 @@ struct NetchargeFluctuations { // CCDB related configurations Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable cfgUrlCCDB{"cfgUrlCCDB", "http://alice-ccdb.cern.ch", "url of ccdb"}; - Configurable cfgPathCCDB{"cfgPathCCDB", "Users/n/nimalik/My/Object/pn", "Path for ccdb-object"}; + Configurable cfgPathCCDB{"cfgPathCCDB", "Users/n/nimalik/PosNeg_cent/PbPb/LHC24g3_medium", "Path for ccdb-object"}; Configurable cfgLoadEff{"cfgLoadEff", true, "Load efficiency"}; Configurable cfgEffNue{"cfgEffNue", false, "efficiency correction to nu_dyn"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0=FT0C, 1=FT0A, 2=FT0M"}; + Configurable cut05{"cut05", 155, "0-5% boundary"}; + Configurable cut10{"cut10", 130, "5-10% boundary"}; + Configurable cut20{"cut20", 97, "10-20% boundary"}; + Configurable cut30{"cut30", 71, "20-30% boundary"}; + Configurable cut40{"cut40", 52, "30-40% boundary"}; + Configurable cut50{"cut50", 36, "40-50% boundary"}; + Configurable cut60{"cut60", 26, "50-60% boundary"}; + Configurable cut70{"cut70", 19, "60-70% boundary"}; + Configurable cut80{"cut80", 11, "70-80% boundary"}; + Configurable cut90{"cut90", 5, "80-90% boundary"}; + // Track and event selection cuts Configurable vertexZcut{"vertexZcut", 10.f, "Vertex Z"}; Configurable etaCut{"etaCut", 0.8f, "Eta cut"}; @@ -95,10 +103,14 @@ struct NetchargeFluctuations { Configurable itsChiCut{"itsChiCut", 36., "ITS chi2 cluster cut"}; Configurable tpcChiCut{"tpcChiCut", 4., "TPC chi2 cluster cut"}; Configurable centMin{"centMin", 0.0f, "cenrality min for delta eta"}; - Configurable centMax{"centMax", 10.0f, "cenrality max for delta eta"}; + Configurable centMax{"centMax", 5.0f, "cenrality max for delta eta"}; Configurable cfgNSubsample{"cfgNSubsample", 30, "Number of subsamples for Error"}; Configurable deltaEta{"deltaEta", 8, "Delta eta bin count"}; Configurable threshold{"threshold", 1e-6, "Delta eta bin count"}; + Configurable ft0Cmin{"ft0Cmin", -3.3, "FT0C min"}; + Configurable ft0Cmax{"ft0Cmax", -2.1, "FT0C max"}; + Configurable ft0Amin{"ft0Amin", 3.5, "FT0A min"}; + Configurable ft0Amax{"ft0Amax", 4.9, "FT0A max"}; // Event selections Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; // sel8 @@ -123,15 +135,17 @@ struct NetchargeFluctuations { Configurable cPVcont{"cPVcont", false, "primary vertex contributor"}; // Configurable to enable multiplicity correlation cuts - O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, false, "Multiplicity correlation cut") + Configurable cfgEvSelMultCorrelation{"cfgEvSelMultCorrelation", false, "Multiplicity correlation cut"}; // Struct grouping multiplicity vs centrality/vertex cuts and related parameters struct : ConfigurableGroup { // Flags to enable specific multiplicity correlation cuts - O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, true, "Enable PV multiplicity vs T0C centrality cut") - O2_DEFINE_CONFIGURABLE(cfgMultGlobalFT0CCutEnabled, bool, true, "Enable globalTracks vs FT0C multiplicity cut") - O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, true, "Enable globalTracks vs PV multiplicity cut") + Configurable cfgMultPVT0CCutEnabled{"cfgMultPVT0CCutEnabled", true, "Enable PV multiplicity vs T0C centrality cut"}; + + Configurable cfgMultGlobalFT0CCutEnabled{"cfgMultGlobalFT0CCutEnabled", true, "Enable globalTracks vs FT0C multiplicity cut"}; + + Configurable cfgMultGlobalPVCutEnabled{"cfgMultGlobalPVCutEnabled", true, "Enable globalTracks vs PV multiplicity cut"}; // Parameter values for PV multiplicity vs FT0C centrality cut (polynomial coefficients, etc.) Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", @@ -166,8 +180,8 @@ struct NetchargeFluctuations { // Histogram pointer for CCDB efficiency // TH1D* efficiency = nullptr; - TH1D* efficiencyPos = nullptr; - TH1D* efficiencyNeg = nullptr; + TH2F* efficiencyPos = nullptr; + TH2F* efficiencyNeg = nullptr; // Filters for selecting collisions and tracks Filter collisionFilter = nabs(aod::collision::posZ) <= vertexZcut; @@ -246,6 +260,10 @@ struct NetchargeFluctuations { histogramRegistry.add("QA/hCentrality", "", kTH1F, {centAxis}); histogramRegistry.add("QA/hMultiplicity", "", kTH1F, {multAxis}); + histogramRegistry.add("cent/multFT0C", "", kTH1F, {nchAxis}); + histogramRegistry.add("cent/multFT0A", "", kTH1F, {nchAxis}); + histogramRegistry.add("cent/multFT0M", "", kTH1F, {nchAxis}); + histogramRegistry.add("gen/hVtxZ_before", "", kTH1F, {vtxzAxis}); histogramRegistry.add("gen/hVtxZ_after", "", kTH1F, {vtxzAxis}); histogramRegistry.add("gen/hPt", "", kTH1F, {ptAxis}); @@ -347,9 +365,21 @@ struct NetchargeFluctuations { histogramRegistry.add("QA/hNchPV", "", kTH1F, {nchAxis}); histogramRegistry.add("eff/hPt_np_gen", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/hPt_hEta_np_gen", "", kTH2F, {ptAxis, etaAxis}); histogramRegistry.add("eff/hPt_nm_gen", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/hPt_hEta_nm_gen", "", kTH2F, {ptAxis, etaAxis}); + histogramRegistry.add("eff/cent/hPt_np_gen", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/cent/hPt_hEta_np_gen", "", kTH2F, {ptAxis, etaAxis}); + histogramRegistry.add("eff/cent/hPt_nm_gen", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/cent/hPt_hEta_nm_gen", "", kTH2F, {ptAxis, etaAxis}); histogramRegistry.add("eff/hPt_np", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/hPt_hEta_np", "", kTH2F, {ptAxis, etaAxis}); histogramRegistry.add("eff/hPt_nm", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/hPt_hEta_nm", "", kTH2F, {ptAxis, etaAxis}); + histogramRegistry.add("eff/cent/hPt_np", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/cent/hPt_hEta_np", "", kTH2F, {ptAxis, etaAxis}); + histogramRegistry.add("eff/cent/hPt_nm", "", kTH1F, {ptAxis}); + histogramRegistry.add("eff/cent/hPt_hEta_nm", "", kTH2F, {ptAxis, etaAxis}); // QA histograms for multiplicity correlations histogramRegistry.add("MultCorrelationPlots/globalTracks_PV_bef", "", {HistType::kTH2D, {nchAxis, nchAxis}}); @@ -369,16 +399,15 @@ struct NetchargeFluctuations { cfgFunCoeff.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", - "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x - 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", - 0, 100); - cfgFunCoeff.fMultPVT0CCutLow->SetParameters(&(cfgFunCoeff.multPVT0CCutPars[0])); + "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x - 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); + cfgFunCoeff.fMultPVT0CCutLow->SetParameters(cfgFunCoeff.multPVT0CCutPars.data()); // Upper cut function: 4th-order polynomial plus 3.5 sigma deviation cfgFunCoeff.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x + 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); - cfgFunCoeff.fMultPVT0CCutHigh->SetParameters(&(cfgFunCoeff.multPVT0CCutPars[0])); + cfgFunCoeff.fMultPVT0CCutHigh->SetParameters(cfgFunCoeff.multPVT0CCutPars.data()); // --- Initialize globalTracks vs FT0C multiplicity cut functions --- // Lower cut function @@ -386,14 +415,14 @@ struct NetchargeFluctuations { new TF1("fMultGlobalFT0CCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x - 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); - cfgFunCoeff.fMultGlobalFT0CCutLow->SetParameters(&(cfgFunCoeff.multGlobalFT0CPars[0])); + cfgFunCoeff.fMultGlobalFT0CCutLow->SetParameters(cfgFunCoeff.multGlobalFT0CPars.data()); // Upper cut function cfgFunCoeff.fMultGlobalFT0CCutHigh = new TF1("fMultGlobalFT0CCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x + 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); - cfgFunCoeff.fMultGlobalFT0CCutHigh->SetParameters(&(cfgFunCoeff.multGlobalFT0CPars[0])); + cfgFunCoeff.fMultGlobalFT0CCutHigh->SetParameters(cfgFunCoeff.multGlobalFT0CPars.data()); // --- Initialize globalTracks vs PV multiplicity cut functions --- // Lower cut: linear + cubic term minus 3.5 sigma @@ -402,14 +431,14 @@ struct NetchargeFluctuations { new TF1("fMultGlobalPVCutLow", "[0]+[1]*x - 3.5*([2]+[3]*x+[4]*x*x+[5]*x*x*x)", 0, 100); - cfgFunCoeff.fMultGlobalPVCutLow->SetParameters(&(cfgFunCoeff.multGlobalPVCutPars[0])); + cfgFunCoeff.fMultGlobalPVCutLow->SetParameters(cfgFunCoeff.multGlobalPVCutPars.data()); // Upper cut: linear + cubic term plus 3.5 sigma cfgFunCoeff.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", "[0]+[1]*x + 3.5*([2]+[3]*x+[4]*x*x+[5]*x*x*x)", 0, 100); - cfgFunCoeff.fMultGlobalPVCutHigh->SetParameters(&(cfgFunCoeff.multGlobalPVCutPars[0])); + cfgFunCoeff.fMultGlobalPVCutHigh->SetParameters(cfgFunCoeff.multGlobalPVCutPars.data()); // --- Load efficiency histogram from CCDB if (cfgLoadEff) { @@ -417,9 +446,10 @@ struct NetchargeFluctuations { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - TList* list = ccdb->getForTimeStamp(cfgPathCCDB.value, -1); - efficiencyPos = reinterpret_cast(list->FindObject("efficiency_Pos_Run3")); - efficiencyNeg = reinterpret_cast(list->FindObject("efficiency_Neg_Run3")); + auto* list = ccdb->getForTimeStamp(cfgPathCCDB.value, 1); + efficiencyPos = dynamic_cast(list->FindObject("efficiency_Pos")); + efficiencyNeg = dynamic_cast(list->FindObject("efficiency_Neg")); + // Log fatal error if efficiency histogram is not found if (!efficiencyPos || !efficiencyNeg) { LOGF(info, "FATAL!! Could not find required histograms in CCDB"); @@ -431,26 +461,31 @@ struct NetchargeFluctuations { { if (cfgFunCoeff.cfgMultPVT0CCutEnabled) { - if (pvTrack < cfgFunCoeff.fMultPVT0CCutLow->Eval(centrality)) + if (pvTrack < cfgFunCoeff.fMultPVT0CCutLow->Eval(centrality)) { return false; - if (pvTrack > cfgFunCoeff.fMultPVT0CCutHigh->Eval(centrality)) + } + if (pvTrack > cfgFunCoeff.fMultPVT0CCutHigh->Eval(centrality)) { return false; + } } - if (cfgFunCoeff.cfgMultGlobalFT0CCutEnabled) { - if (globalNch < cfgFunCoeff.fMultGlobalFT0CCutLow->Eval(centrality)) + if (globalNch < cfgFunCoeff.fMultGlobalFT0CCutLow->Eval(centrality)) { return false; - if (globalNch > cfgFunCoeff.fMultGlobalFT0CCutHigh->Eval(centrality)) + } + if (globalNch > cfgFunCoeff.fMultGlobalFT0CCutHigh->Eval(centrality)) { return false; + } } if (cfgFunCoeff.cfgMultGlobalPVCutEnabled) { - if (globalNch < cfgFunCoeff.fMultGlobalPVCutLow->Eval(pvTrack)) + if (globalNch < cfgFunCoeff.fMultGlobalPVCutLow->Eval(pvTrack)) { return false; - if (globalNch > cfgFunCoeff.fMultGlobalPVCutHigh->Eval(pvTrack)) + } + if (globalNch > cfgFunCoeff.fMultGlobalPVCutHigh->Eval(pvTrack)) { return false; + } } return true; @@ -460,8 +495,9 @@ struct NetchargeFluctuations { bool selCollision(C const& coll, float& cent, float& mult) { - if (std::abs(coll.posZ()) >= vertexZcut) + if (std::abs(coll.posZ()) >= vertexZcut) { return false; + } if constexpr (run == kRun3) { if (cSel8Trig && !coll.sel8()) { return false; @@ -485,19 +521,24 @@ struct NetchargeFluctuations { mult = coll.multFV0M(); // multiplicity for run2 } - if (cNoItsROBorder && !coll.selection_bit(aod::evsel::kNoITSROFrameBorder)) + if (cNoItsROBorder && !coll.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; - if (cTFBorder && !coll.selection_bit(aod::evsel::kNoTimeFrameBorder)) + } + if (cTFBorder && !coll.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; - if (cPileupReject && !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) + } + if (cPileupReject && !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; - if (cZVtxTimeDiff && !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) + } + if (cZVtxTimeDiff && !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; - if (cItsTpcVtx && !coll.selection_bit(aod::evsel::kIsVertexITSTPC)) + } + if (cItsTpcVtx && !coll.selection_bit(aod::evsel::kIsVertexITSTPC)) { return false; - if (cfgUseGoodItsLayerAllCut && !(coll.selection_bit(aod::evsel::kIsGoodITSLayersAll))) + } + if (cfgUseGoodItsLayerAllCut && !(coll.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { return false; - + } return true; } @@ -532,33 +573,43 @@ struct NetchargeFluctuations { template bool selTrack(T const& track) { - if (!track.isGlobalTrack()) + if (!track.isGlobalTrack()) { return false; - if (cPVcont && !track.isPVContributor()) + } + if (cPVcont && !track.isPVContributor()) { return false; - if (std::fabs(track.eta()) >= etaCut) + } + if (std::fabs(track.eta()) >= etaCut) { return false; - if (track.pt() <= ptMinCut || track.pt() >= ptMaxCut) + } + if (track.pt() <= ptMinCut || track.pt() >= ptMaxCut) { return false; - if (track.sign() == 0) + } + if (track.sign() == 0) { return false; - if (cDcaXy && std::fabs(track.dcaXY()) >= dcaXYCut) + } + if (cDcaXy && std::fabs(track.dcaXY()) >= dcaXYCut) { return false; - if (cDcaZ && std::fabs(track.dcaZ()) >= dcaZCut) + } + if (cDcaZ && std::fabs(track.dcaZ()) >= dcaZCut) { return false; - if (cTpcCr && track.tpcNClsCrossedRows() <= tpcCrossCut) + } + if (cTpcCr && track.tpcNClsCrossedRows() <= tpcCrossCut) { return false; - if (cItsChi && track.itsChi2NCl() >= itsChiCut) + } + if (cItsChi && track.itsChi2NCl() >= itsChiCut) { return false; - if (cTpcChi && track.tpcChi2NCl() >= tpcChiCut) + } + if (cTpcChi && track.tpcChi2NCl() >= tpcChiCut) { return false; + } return true; } - double getEfficiency(float pt, int sign) + double getEfficiency(float pt, float eta, int sign) { - TH1D* hEff = nullptr; + TH2F* hEff = nullptr; if (sign > 0) { hEff = efficiencyPos; @@ -569,11 +620,15 @@ struct NetchargeFluctuations { if (!hEff) { return 1e-6; } - int bin = hEff->GetXaxis()->FindBin(pt); - if (bin < 1 || bin > hEff->GetNbinsX()) { - return 1e-6; + int binx = hEff->GetXaxis()->FindBin(pt); + int biny = hEff->GetYaxis()->FindBin(eta); + + if (binx < 1 || binx > hEff->GetNbinsX() || + biny < 1 || biny > hEff->GetNbinsY()) { + return -1; } - double eff = hEff->GetBinContent(bin); + + double eff = hEff->GetBinContent(binx, biny); return eff; } @@ -617,7 +672,7 @@ struct NetchargeFluctuations { return; } - float globalNch = tracks.size(); + auto globalNch = tracks.size(); float pvTrack = coll.multNTracksPV(); histogramRegistry.fill(HIST("QA/hCentFT0C"), cent); @@ -646,10 +701,11 @@ struct NetchargeFluctuations { for (const auto& track : tracks) { fillBeforeQA(track); - if (!selTrack(track)) + if (!selTrack(track)) { continue; + } - double eff = getEfficiency(track.pt(), track.sign()); + double eff = getEfficiency(track.pt(), track.eta(), track.sign()); if (eff < threshold) { continue; } @@ -715,8 +771,8 @@ struct NetchargeFluctuations { return; } - int globalNch = inputTracks.size(); - int pvTrack = coll.multNTracksPV(); + auto globalNch = inputTracks.size(); + float pvTrack = coll.multNTracksPV(); histogramRegistry.fill(HIST("QA/hCentFT0C"), cent); histogramRegistry.fill(HIST("QA/hNchGlobal"), globalNch); @@ -745,23 +801,35 @@ struct NetchargeFluctuations { for (const auto& track : inputTracks) { fillBeforeQA(track); - if (!selTrack(track)) + if (!selTrack(track)) { continue; + } nch += 1; fillAfterQA(track); if (track.sign() == 1) { histogramRegistry.fill(HIST("eff/hPt_np"), track.pt()); + histogramRegistry.fill(HIST("eff/hPt_hEta_np"), track.pt(), track.eta()); + if (cent >= centMin && cent < centMax) { + histogramRegistry.fill(HIST("eff/cent/hPt_np"), track.pt()); + histogramRegistry.fill(HIST("eff/cent/hPt_hEta_np"), track.pt(), track.eta()); + } } else if (track.sign() == -1) { histogramRegistry.fill(HIST("eff/hPt_nm"), track.pt()); + histogramRegistry.fill(HIST("eff/hPt_hEta_nm"), track.pt(), track.eta()); + if (cent >= centMin && cent < centMax) { + histogramRegistry.fill(HIST("eff/cent/hPt_nm"), track.pt()); + histogramRegistry.fill(HIST("eff/cent/hPt_hEta_nm"), track.pt(), track.eta()); + } } histogramRegistry.fill(HIST("QA/cent_hEta"), cent, track.eta()); histogramRegistry.fill(HIST("QA/cent_hPt"), cent, track.pt()); - double eff = getEfficiency(track.pt(), track.sign()); - if (eff < threshold) + double eff = getEfficiency(track.pt(), track.eta(), track.sign()); + if (eff < threshold) { continue; + } double weight = 1.0 / eff; histogramRegistry.fill(HIST("data/hPt_cor"), track.pt(), weight); histogramRegistry.fill(HIST("data/hEta_cor"), track.eta(), weight); @@ -801,40 +869,56 @@ struct NetchargeFluctuations { int posGen = 0, negGen = 0, posNegGen = 0, termNGen = 0, termPGen = 0, nchGen = 0; const auto& mccolgen = coll.template mcCollision_as(); - if (std::abs(mccolgen.posZ()) >= vertexZcut) + if (std::abs(mccolgen.posZ()) >= vertexZcut) { return; + } const auto& mcpartgen = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolgen.globalIndex(), cache); histogramRegistry.fill(HIST("gen/hVtxZ_after"), mccolgen.posZ()); for (const auto& mcpart : mcpartgen) { - if (std::fabs(mcpart.eta()) >= etaCut) + if (std::fabs(mcpart.eta()) >= etaCut) { continue; - if (!mcpart.isPhysicalPrimary()) + } + if (!mcpart.isPhysicalPrimary()) { continue; + } int pid = mcpart.pdgCode(); - auto sign = 0; + int sign = 0; auto* pd = pdgService->GetParticle(pid); if (pd != nullptr) { - sign = pd->Charge() / 3.; + sign = static_cast(pd->Charge() / 3.0); } - if (sign == 0) + if (sign == 0) { continue; + } if (std::abs(pid) != kElectron && std::abs(pid) != kMuonMinus && std::abs(pid) != kPiPlus && std::abs(pid) != kKPlus && - std::abs(pid) != kProton) + std::abs(pid) != kProton) { continue; - if (std::fabs(mcpart.eta()) >= etaCut) + } + if (std::fabs(mcpart.eta()) >= etaCut) { continue; + } if ((mcpart.pt() <= ptMinCut) || (mcpart.pt() >= ptMaxCut)) { continue; } if (sign == 1) { histogramRegistry.fill(HIST("eff/hPt_np_gen"), mcpart.pt()); + histogramRegistry.fill(HIST("eff/hPt_hEta_np_gen"), mcpart.pt(), mcpart.eta()); + if (cent >= centMin && cent < centMax) { + histogramRegistry.fill(HIST("eff/cent/hPt_np_gen"), mcpart.pt()); + histogramRegistry.fill(HIST("eff/cent/hPt_hEta_np_gen"), mcpart.pt(), mcpart.eta()); + } + } else if (sign == -1) { histogramRegistry.fill(HIST("eff/hPt_nm_gen"), mcpart.pt()); + histogramRegistry.fill(HIST("eff/hPt_hEta_nm_gen"), mcpart.pt(), mcpart.eta()); + if (cent >= centMin && cent < centMax) { + histogramRegistry.fill(HIST("eff/cent/hPt_nm_gen"), mcpart.pt()); + histogramRegistry.fill(HIST("eff/cent/hPt_hEta_nm_gen"), mcpart.pt(), mcpart.eta()); + } } - histogramRegistry.fill(HIST("gen/hPt"), mcpart.pt()); histogramRegistry.fill(HIST("gen/cent_hPt"), cent, mcpart.pt()); histogramRegistry.fill(HIST("gen/hEta"), mcpart.eta()); @@ -879,16 +963,19 @@ struct NetchargeFluctuations { void calculationDeltaEta(C const& coll, T const& tracks, float deta1, float deta2) { float cent = -1, mult = -1; - if (!selCollision(coll, cent, mult)) + if (!selCollision(coll, cent, mult)) { return; + } - int globalNch = tracks.size(); - int pvTrack = coll.multNTracksPV(); - if (cfgEvSelMultCorrelation && !eventSelected(globalNch, pvTrack, cent)) + auto globalNch = tracks.size(); + float pvTrack = coll.multNTracksPV(); + if (cfgEvSelMultCorrelation && !eventSelected(globalNch, pvTrack, cent)) { return; + } - if (!(cent >= centMin && cent < centMax)) + if (cent < centMin || cent >= centMax) { return; + } histogramRegistry.fill(HIST("data/delta_eta_cent"), cent); int fpos = 0, fneg = 0, termp = 0, termn = 0, posneg = 0; @@ -901,16 +988,19 @@ struct NetchargeFluctuations { for (const auto& track : tracks) { - if (!selTrack(track)) + if (!selTrack(track)) { continue; + } double eta = track.eta(); - if (eta < deta1 || eta > deta2) + if (eta < deta1 || eta > deta2) { continue; + } - double eff = getEfficiency(track.pt(), track.sign()); - if (eff < threshold) + double eff = getEfficiency(track.pt(), track.eta(), track.sign()); + if (eff < threshold) { continue; + } double weight = 1.0 / eff; @@ -990,20 +1080,25 @@ struct NetchargeFluctuations { { (void)mcCollisions; - if (!coll.has_mcCollision()) + if (!coll.has_mcCollision()) { return; + } float cent = -1, mult = -1; - if (!selCollision(coll, cent, mult)) + if (!selCollision(coll, cent, mult)) { return; + } - int globalNch = inputTracks.size(); - int pvTrack = coll.multNTracksPV(); - if (cfgEvSelMultCorrelation && !eventSelected(globalNch, pvTrack, cent)) + auto globalNch = inputTracks.size(); + float pvTrack = coll.multNTracksPV(); + if (cfgEvSelMultCorrelation && !eventSelected(globalNch, pvTrack, cent)) { return; + } - if (!(cent >= centMin && cent < centMax)) + if (cent < centMin || cent >= centMax) { return; + } + histogramRegistry.fill(HIST("data/delta_eta_cent"), cent); float deltaEtaWidth = deta2 - deta1 + 1e-5f; @@ -1015,16 +1110,20 @@ struct NetchargeFluctuations { double nchCor = 0.0, termpW = 0.0, termnW = 0.0, posnegW = 0.0; for (const auto& track : inputTracks) { - if (!selTrack(track)) + if (!selTrack(track)) { continue; + } double eta = track.eta(); - if (eta < deta1 || eta > deta2) + if (eta < deta1 || eta > deta2) { continue; + } histogramRegistry.fill(HIST("data/delta_eta_eta"), eta); - double eff = getEfficiency(track.pt(), track.sign()); - if (eff < threshold) + double eff = getEfficiency(track.pt(), track.eta(), track.sign()); + + if (eff < threshold) { continue; + } double weight = 1.0 / eff; nch += 1; nchCor += weight; @@ -1073,39 +1172,267 @@ struct NetchargeFluctuations { const auto& mccolgen = coll.template mcCollision_as(); - if (std::abs(mccolgen.posZ()) >= vertexZcut) + if (std::abs(mccolgen.posZ()) >= vertexZcut) { return; + } const auto& mcpartgen = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolgen.globalIndex(), cache); int posGen = 0, negGen = 0, posNegGen = 0, termNGen = 0, termPGen = 0, nchGen = 0; for (const auto& mcpart : mcpartgen) { - if (!mcpart.isPhysicalPrimary()) + if (!mcpart.isPhysicalPrimary()) { + continue; + } + + int pid = mcpart.pdgCode(); + auto sign = 0; + auto* pd = pdgService->GetParticle(pid); + if (pd != nullptr) { + sign = static_cast(pd->Charge() / 3.0); + } + if (sign == 0) { + continue; + } + if (std::abs(pid) != kElectron && + std::abs(pid) != kMuonMinus && + std::abs(pid) != kPiPlus && + std::abs(pid) != kKPlus && + std::abs(pid) != kProton) { + continue; + } + + if (std::fabs(mcpart.eta()) >= etaCut) { + continue; + } + if ((mcpart.pt() <= ptMinCut) || (mcpart.pt() >= ptMaxCut)) { + continue; + } + + double mcEta = mcpart.eta(); + if (mcEta < deta1 || mcEta > deta2) { + continue; + } + + histogramRegistry.fill(HIST("gen/delta_eta_eta"), mcpart.eta()); + + nchGen += 1; + if (sign == 1) { + posGen += 1; + } + if (sign == -1) { + negGen += 1; + } + } + + termPGen = posGen * (posGen - 1); + termNGen = negGen * (negGen - 1); + posNegGen = posGen * negGen; + + histogramRegistry.fill(HIST("gen/delta_eta_pos"), deltaEtaWidth, posGen); + histogramRegistry.fill(HIST("gen/delta_eta_neg"), deltaEtaWidth, negGen); + histogramRegistry.fill(HIST("gen/delta_eta_termp"), deltaEtaWidth, termPGen); + histogramRegistry.fill(HIST("gen/delta_eta_termn"), deltaEtaWidth, termNGen); + histogramRegistry.fill(HIST("gen/delta_eta_pos_sq"), deltaEtaWidth, posGen * posGen); + histogramRegistry.fill(HIST("gen/delta_eta_neg_sq"), deltaEtaWidth, negGen * negGen); + histogramRegistry.fill(HIST("gen/delta_eta_posneg"), deltaEtaWidth, posNegGen); + histogramRegistry.fill(HIST("gen/delta_eta_nch"), deltaEtaWidth, nchGen); + + float lRandom = fRndm->Rndm(); + int sampleIndex = static_cast(cfgNSubsample * lRandom); + + histogramRegistry.fill(HIST("subsample/delta_eta/gen/pos"), deltaEtaWidth, sampleIndex, posGen); + histogramRegistry.fill(HIST("subsample/delta_eta/gen/neg"), deltaEtaWidth, sampleIndex, negGen); + histogramRegistry.fill(HIST("subsample/delta_eta/gen/termp"), deltaEtaWidth, sampleIndex, termPGen); + histogramRegistry.fill(HIST("subsample/delta_eta/gen/termn"), deltaEtaWidth, sampleIndex, termNGen); + histogramRegistry.fill(HIST("subsample/delta_eta/gen/pos_sq"), deltaEtaWidth, sampleIndex, posGen * posGen); + histogramRegistry.fill(HIST("subsample/delta_eta/gen/neg_sq"), deltaEtaWidth, sampleIndex, negGen * negGen); + histogramRegistry.fill(HIST("subsample/delta_eta/gen/posneg"), deltaEtaWidth, sampleIndex, posNegGen); + + } // void + + float getCentrality(aod::McParticles const& particles) + { + int multFT0A = 0; + int multFT0C = 0; + + for (auto const& part : particles) { + + if (!part.isPhysicalPrimary()) { continue; + } + + // FT0C + if (part.eta() > ft0Cmin && part.eta() < ft0Cmax) { + multFT0C++; + } + + // FT0A + if (part.eta() > ft0Amin && part.eta() < ft0Amax) { + multFT0A++; + } + } + + int multFT0M = multFT0A + multFT0C; + // LOGF(info, "multFT0C = %d", multFT0C); + histogramRegistry.fill(HIST("cent/multFT0C"), multFT0C); + histogramRegistry.fill(HIST("cent/multFT0A"), multFT0A); + histogramRegistry.fill(HIST("cent/multFT0M"), multFT0M); + + int multEstimator = 0; + + if (cfgCentEstimator == 0) { + multEstimator = multFT0C; + } else if (cfgCentEstimator == 1) { + multEstimator = multFT0A; + } else { + multEstimator = multFT0M; + } + // centrality cuts + if (multEstimator >= cut05) + return 2.5; + if (multEstimator >= cut10) + return 7.5; + if (multEstimator >= cut20) + return 15; + if (multEstimator >= cut30) + return 25; + if (multEstimator >= cut40) + return 35; + if (multEstimator >= cut50) + return 45; + if (multEstimator >= cut60) + return 55; + if (multEstimator >= cut70) + return 65; + if (multEstimator >= cut80) + return 75; + if (multEstimator >= cut90) + return 85; + + return -1; + } + + void mcPredictionsCent(aod::McCollision const& collision, aod::McParticles const& particles) + { + (void)collision; + int posGen = 0, negGen = 0, posNegGen = 0, termNGen = 0, termPGen = 0, nchGen = 0; + auto cent = getCentrality(particles); + + if (cent < 0) { + return; + } + + for (auto const& mcpart : particles) { + + if (!mcpart.isPhysicalPrimary()) { + continue; + } int pid = mcpart.pdgCode(); auto sign = 0; auto* pd = pdgService->GetParticle(pid); if (pd != nullptr) { - sign = pd->Charge() / 3.; + sign = static_cast(pd->Charge() / 3.0); } - if (sign == 0) + if (sign == 0) { continue; + } if (std::abs(pid) != kElectron && std::abs(pid) != kMuonMinus && std::abs(pid) != kPiPlus && std::abs(pid) != kKPlus && - std::abs(pid) != kProton) + std::abs(pid) != kProton) { continue; + } - if (std::fabs(mcpart.eta()) >= etaCut) + if (std::fabs(mcpart.eta()) >= etaCut) { continue; - if ((mcpart.pt() <= ptMinCut) || (mcpart.pt() >= ptMaxCut)) + } + + if (mcpart.pt() <= ptMinCut || mcpart.pt() >= ptMaxCut) { continue; + } + + histogramRegistry.fill(HIST("gen/hPt"), mcpart.pt()); + histogramRegistry.fill(HIST("gen/cent_hPt"), cent, mcpart.pt()); + histogramRegistry.fill(HIST("gen/hEta"), mcpart.eta()); + histogramRegistry.fill(HIST("gen/cent_hEta"), cent, mcpart.eta()); + histogramRegistry.fill(HIST("gen/hSign"), sign); + histogramRegistry.fill(HIST("gen/hPt_eta"), mcpart.pt(), mcpart.eta()); + + nchGen += 1; + if (sign == 1) { + posGen += 1; + } + if (sign == -1) { + negGen += 1; + } + } + // LOGF(info, "cent = %d nch = %d", cent, nchGen); + termPGen = posGen * (posGen - 1); + termNGen = negGen * (negGen - 1); + posNegGen = posGen * negGen; + histogramRegistry.fill(HIST("gen/cent_pos"), cent, posGen); + histogramRegistry.fill(HIST("gen/cent_neg"), cent, negGen); + histogramRegistry.fill(HIST("gen/cent_termp"), cent, termPGen); + histogramRegistry.fill(HIST("gen/cent_termn"), cent, termNGen); + histogramRegistry.fill(HIST("gen/cent_pos_sq"), cent, posGen * posGen); + histogramRegistry.fill(HIST("gen/cent_neg_sq"), cent, negGen * negGen); + histogramRegistry.fill(HIST("gen/cent_posneg"), cent, posNegGen); + histogramRegistry.fill(HIST("gen/cent_nch"), cent, nchGen); + histogramRegistry.fill(HIST("gen/nch"), nchGen); + + float lRandom = fRndm->Rndm(); + int sampleIndex = static_cast(cfgNSubsample * lRandom); + + histogramRegistry.fill(HIST("subsample/gen/pos"), cent, sampleIndex, posGen); + histogramRegistry.fill(HIST("subsample/gen/neg"), cent, sampleIndex, negGen); + histogramRegistry.fill(HIST("subsample/gen/termp"), cent, sampleIndex, termPGen); + histogramRegistry.fill(HIST("subsample/gen/termn"), cent, sampleIndex, termNGen); + histogramRegistry.fill(HIST("subsample/gen/pos_sq"), cent, sampleIndex, posGen * posGen); + histogramRegistry.fill(HIST("subsample/gen/neg_sq"), cent, sampleIndex, negGen * negGen); + histogramRegistry.fill(HIST("subsample/gen/posneg"), cent, sampleIndex, posNegGen); + } // void + + void mcPredictionsDeltaEta(aod::McCollision const& collision, aod::McParticles const& particles, float deta1, float deta2) + { + (void)collision; + + float deltaEtaWidth = deta2 - deta1 + 1e-5f; + + int posGen = 0, negGen = 0, posNegGen = 0, termNGen = 0, termPGen = 0, nchGen = 0; + for (const auto& mcpart : particles) { + if (!mcpart.isPhysicalPrimary()) { + continue; + } + int pid = mcpart.pdgCode(); + auto sign = 0; + auto* pd = pdgService->GetParticle(pid); + if (pd != nullptr) { + sign = static_cast(pd->Charge() / 3.0); + } + if (sign == 0) { + continue; + } + if (std::abs(pid) != kElectron && + std::abs(pid) != kMuonMinus && + std::abs(pid) != kPiPlus && + std::abs(pid) != kKPlus && + std::abs(pid) != kProton) { + continue; + } + + if (std::fabs(mcpart.eta()) >= etaCut) { + continue; + } + if ((mcpart.pt() <= ptMinCut) || (mcpart.pt() >= ptMaxCut)) { + continue; + } double mcEta = mcpart.eta(); - if (mcEta < deta1 || mcEta > deta2) + if (mcEta < deta1 || mcEta > deta2) { continue; + } histogramRegistry.fill(HIST("gen/delta_eta_eta"), mcpart.eta()); @@ -1203,6 +1530,18 @@ struct NetchargeFluctuations { } PROCESS_SWITCH(NetchargeFluctuations, processMcRun2, "Process reconstructed", false); + + void processMcPrediction(aod::McCollision const& collision, aod::McParticles const& particles) + { + mcPredictionsCent(collision, particles); + for (int ii = 0; ii < deltaEta; ii++) { + float etaMin = -0.1f * (ii + 1); + float etaMax = 0.1f * (ii + 1); + mcPredictionsDeltaEta(collision, particles, etaMin, etaMax); + } + } + + PROCESS_SWITCH(NetchargeFluctuations, processMcPrediction, "Process Prediction", false); }; // struct diff --git a/PWGCF/EbyEFluctuations/Tasks/netprotcumulants.cxx b/PWGCF/EbyEFluctuations/Tasks/netprotcumulants.cxx index 5c44d407590..2d9ee21339d 100644 --- a/PWGCF/EbyEFluctuations/Tasks/netprotcumulants.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/netprotcumulants.cxx @@ -53,6 +53,41 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +namespace o2::aod +{ + +// ===== Event columns ===== +DECLARE_SOA_COLUMN(EventId, eventId, int); +DECLARE_SOA_COLUMN(MyCent, myCent, float); +DECLARE_SOA_COLUMN(Vz, vz, float); +DECLARE_SOA_COLUMN(Ntrk, ntrk, int); +DECLARE_SOA_COLUMN(Np, np, int); +DECLARE_SOA_COLUMN(Npbar, npbar, int); + +// ===== Track columns ===== +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Pid, pid, int); +DECLARE_SOA_COLUMN(Eff, eff, float); + +// ===== Tables ===== +DECLARE_SOA_TABLE(EventTable, "AOD", "MYEVENT", + EventId, + MyCent, + Vz, + Ntrk, + Np, + Npbar); + +DECLARE_SOA_TABLE(MYTrackTable, "AOD", "MYTRACK", + EventId, + Pt, + Eta, + Pid, + Eff); + +} // namespace o2::aod + struct NetProtCumulants { // events Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; @@ -110,6 +145,26 @@ struct NetProtCumulants { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Produces eventTable; + Produces trackTable; + + int eventCounter = 0; + int eventId = 0; + + // event variables + int Ntrk_event = 0; + int Np_event = 0; + int Npbar_event = 0; + + float cent_event = 0; + float vz_event = 0; + + // track variables + float pt_track = 0; + float eta_track = 0; + int pid_track = 0; + float eff_track = 0; + TRandom3* fRndm = new TRandom3(0); // Eff histograms 2d: eff(pT, eta) @@ -855,6 +910,7 @@ struct NetProtCumulants { if (cfgUsePtDepDCAz) { fPtDepDCAz = new TF1("ptDepDCAz", cfgDCAzFunc->c_str(), 0.001, 10.0); } + } // end init() template @@ -2134,6 +2190,18 @@ struct NetProtCumulants { void processDataRec(AodCollisions::iterator const& coll, aod::BCsWithTimestamps const&, AodTracks const& inputTracks) { + + // reset per event + Ntrk_event = 0; + Np_event = 0; + Npbar_event = 0; + + // assign event ID + eventId = eventCounter++; + + // store event info + cent_event = coll.centFT0C(); + vz_event = coll.posZ(); if (!coll.sel8()) { return; } @@ -2263,6 +2331,7 @@ struct NetProtCumulants { if (cfgIfMandatoryTOF && !track.hasTOF()) { continue; } + Ntrk_event++; bool trackSelected = false; if (cfgPIDchoice == 0) @@ -2293,11 +2362,18 @@ struct NetProtCumulants { if (track.pt() < cfgCutPtUpper) { nProt = nProt + 1.0; + Np_event++; float pEff = getEfficiency(track); // get efficiency of track if (pEff != 0) { for (int i = 1; i < 7; i++) { powerEffProt[i] += std::pow(1.0 / pEff, i); } + pt_track = track.pt(); + eta_track = track.eta(); + pid_track = +1; + eff_track = pEff; + + trackTable(eventId, pt_track, eta_track, pid_track, eff_track); } } } @@ -2311,11 +2387,18 @@ struct NetProtCumulants { histos.fill(HIST("hrecDcaZAntiproton"), track.dcaZ()); if (track.pt() < cfgCutPtUpper) { nAntiprot = nAntiprot + 1.0; + Npbar_event++; float pEff = getEfficiency(track); // get efficiency of track if (pEff != 0) { for (int i = 1; i < 7; i++) { powerEffAntiprot[i] += std::pow(1.0 / pEff, i); } + pt_track = track.pt(); + eta_track = track.eta(); + pid_track = -1; + eff_track = pEff; + + trackTable(eventId, pt_track, eta_track, pid_track, eff_track); } } } @@ -2323,6 +2406,8 @@ struct NetProtCumulants { } //! checking PID } //! end track loop + eventTable(eventId, cent_event, vz_event, Ntrk_event, Np_event, Npbar_event); + float netProt = nProt - nAntiprot; float nTracks = nProt + nAntiprot; histos.fill(HIST("hNTracksVsCent"), cent, nTracks); diff --git a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx index aa9fbb19a37..5f2ee67ac07 100644 --- a/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/partNumFluc.cxx @@ -15,6 +15,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" @@ -30,12 +31,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -45,815 +48,1214 @@ #include #include #include -#include -#include #include #include -#include +#include #include +#include +#include #include -#include +#include +#include #include #include #include +#include +#include +#include #include +#include #include +#include #include +#include #include #include +#define C_CS(cs) /* NOLINT(cppcoreguidelines-macro-usage) */ \ + [](const std::index_sequence&) constexpr noexcept -> ConstStr<(cs)[indices]...> { \ + static_assert(std::is_array_v> && \ + std::same_as>, char>); \ + return ConstStr<(cs)[indices]...>{}; \ + }(std::make_index_sequence{}) +#define C_SV(sv) /* NOLINT(cppcoreguidelines-macro-usage) */ \ + [](const std::index_sequence&) constexpr noexcept -> ConstStr<(sv)[indices]...> { \ + static_assert(std::same_as, std::string_view>); \ + return ConstStr<(sv)[indices]...>{}; \ + }(std::make_index_sequence<(sv).size()>{}) + using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -namespace o2 +namespace o2::aod +{ +using JoinedMcCollisions = soa::Join; +using JoinedCollisions = soa::Join; +using JoinedCollisionsWithMc = soa::Join; +using JoinedTracks = soa::Join; +using JoinedTracksWithMc = soa::Join; + +namespace mini_collision +{ +DECLARE_SOA_COLUMN(Code, code, std::uint16_t); +} // namespace mini_collision + +DECLARE_SOA_TABLE(MiniCollisions, "AOD", "MINICOLLISION", soa::Index<>, mini_collision::Code); +using MiniCollision = MiniCollisions::iterator; + +namespace mini_mc_particle { -namespace aod +DECLARE_SOA_INDEX_COLUMN(MiniCollision, miniCollision); +DECLARE_SOA_COLUMN(Code, code, std::uint16_t); +} // namespace mini_mc_particle + +DECLARE_SOA_TABLE(MiniMcParticles, "AOD", "MINIMCPARTICLE", soa::Index<>, mini_mc_particle::MiniCollisionId, mini_mc_particle::Code); +using MiniMcParticle = MiniMcParticles::iterator; + +namespace mini_track { -using JoinedCollisions = soa::Join; -using JoinedTracks = soa::Join; -using JoinedCollisionsWithMc = soa::Join; -using JoinedTracksWithMc = soa::Join; -using JoinedMcCollisions = soa::Join; -} // namespace aod -} // namespace o2 +DECLARE_SOA_INDEX_COLUMN(MiniCollision, miniCollision); +DECLARE_SOA_COLUMN(CodeLow, codeLow, std::uint16_t); +DECLARE_SOA_COLUMN(CodeHigh, codeHigh, std::uint8_t); +} // namespace mini_track + +DECLARE_SOA_TABLE(MiniTracks, "AOD", "MINITRACK", soa::Index<>, mini_track::MiniCollisionId, mini_track::CodeLow, mini_track::CodeHigh); +using MiniTrack = MiniTracks::iterator; + +namespace tiny_mc_collision +{ +DECLARE_SOA_COLUMN(CodeLow, codeLow, std::uint16_t); +DECLARE_SOA_COLUMN(CodeHigh, codeHigh, std::uint8_t); +} // namespace tiny_mc_collision + +DECLARE_SOA_TABLE(TinyMcCollisions, "AOD", "TINYMCCOLLISION", soa::Index<>, tiny_mc_collision::CodeLow, tiny_mc_collision::CodeHigh); +using TinyMcCollision = TinyMcCollisions::iterator; + +namespace tiny_collision +{ +DECLARE_SOA_COLUMN(CodeLow, codeLow, std::uint32_t); +DECLARE_SOA_COLUMN(CodeHigh, codeHigh, std::uint8_t); +} // namespace tiny_collision + +DECLARE_SOA_TABLE(TinyCollisions, "AOD", "TINYCOLLISION", soa::Index<>, tiny_collision::CodeLow, tiny_collision::CodeHigh); +using TinyCollision = TinyCollisions::iterator; +namespace tiny_mc_particle +{ +DECLARE_SOA_INDEX_COLUMN(TinyMcCollision, tinyMcCollision); +DECLARE_SOA_COLUMN(SignedEfficiency, signedEfficiency, std::int16_t); +} // namespace tiny_mc_particle + +DECLARE_SOA_TABLE(TinyMcParticles, "AOD", "TINYMCPARTICLE", soa::Index<>, tiny_mc_particle::TinyMcCollisionId, tiny_mc_particle::SignedEfficiency); +using TinyMcParticle = TinyMcParticles::iterator; + +namespace tiny_track +{ +DECLARE_SOA_INDEX_COLUMN(TinyCollision, tinyCollision); +DECLARE_SOA_COLUMN(SignedEfficiency, signedEfficiency, std::int16_t); +} // namespace tiny_track + +DECLARE_SOA_TABLE(TinyTracks, "AOD", "TINYTRACK", soa::Index<>, tiny_track::TinyCollisionId, tiny_track::SignedEfficiency); +using TinyTrack = TinyTracks::iterator; +} // namespace o2::aod + +namespace +{ namespace fluctuation_calculator_base { -inline constexpr std::int8_t MaxOrder = 8; -inline constexpr std::uint8_t NExponentPairs = 36; -inline constexpr std::uint16_t NOrderVectors = 342; -inline constexpr std::array, NExponentPairs> ExponentPairs = {{{1, 1}, {2, 1}, {2, 2}, {3, 1}, {3, 2}, {3, 3}, {4, 1}, {4, 2}, {4, 3}, {4, 4}, {5, 1}, {5, 2}, {5, 3}, {5, 4}, {5, 5}, {6, 1}, {6, 2}, {6, 3}, {6, 4}, {6, 5}, {6, 6}, {7, 1}, {7, 2}, {7, 3}, {7, 4}, {7, 5}, {7, 6}, {7, 7}, {8, 1}, {8, 2}, {8, 3}, {8, 4}, {8, 5}, {8, 6}, {8, 7}, {8, 8}}}; -inline constexpr std::array, NOrderVectors> OrderVectors = {{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {4, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {5, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {5, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {6, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}}; +constexpr std::int8_t OrderMax{8}; +constexpr std::int32_t NExponentKeys{OrderMax * (OrderMax + 1) / 2}; +constexpr std::array, NExponentKeys> ExponentKeys{[]() consteval noexcept -> std::array, NExponentKeys> { + std::array, NExponentKeys> result{}; + std::int32_t index{}; + for (std::int32_t const& iExponent : std::views::iota(1, OrderMax + 1)) { + for (std::int32_t const& jExponent : std::views::iota(1, iExponent + 1)) { + result[index++] = {static_cast(iExponent), static_cast(jExponent)}; + } + } + return result; +}()}; +constexpr std::int32_t NOrderKeys{[]() consteval noexcept -> std::int32_t { + std::array counts{1}; + for (std::pair const& exponentKey /* o2-linter: disable=const-ref-in-for-loop */ : ExponentKeys) { + const std::int32_t weight{exponentKey.first}; + for (std::int32_t const& sum : std::views::iota(weight, OrderMax + 1)) { + counts[sum] += counts[sum - weight]; + } + } + return std::accumulate(counts.begin(), counts.end(), 0); +}()}; +constexpr std::array, NOrderKeys> OrderKeys{[]() consteval noexcept -> std::array, NOrderKeys> { + constexpr auto FillOrderKeys{[](const auto& self, const std::span& current, std::int32_t& index, const std::span, NOrderKeys>& output, const std::int32_t indexPosition, const std::int32_t sum, const std::int32_t target) consteval noexcept -> void { + if (std::cmp_equal(sum, target)) { + std::ranges::fill(std::views::drop(current, indexPosition), 0); + std::ranges::copy(current, output[index++].begin()); + return; + } + if (std::cmp_equal(indexPosition, NExponentKeys)) { + return; + } + + const std::int32_t weight{ExponentKeys[indexPosition].first}; + if (std::cmp_greater(weight, target - sum)) { + return; + } + for (std::int32_t const& power : std::views::iota(0, (target - sum) / weight + 1)) { + current[indexPosition] = static_cast(power); + self(self, current, index, output, indexPosition + 1, sum + power * weight, target); + } + }}; + + std::array, NOrderKeys> result{}; + std::array current{}; + std::int32_t index{}; + for (std::int32_t const& target : std::views::iota(0, OrderMax + 1)) { + FillOrderKeys(FillOrderKeys, current, index, result, 0, 0, target); + } + return result; +}()}; +constexpr std::array, NOrderKeys> OrderKeyProductLinks{[]() consteval noexcept -> std::array, NOrderKeys> { + std::array, NOrderKeys> result{}; + for (std::int32_t const& iOrderKey : std::views::iota(1, NOrderKeys)) { + result[iOrderKey].first = std::inner_product(OrderKeys[iOrderKey].begin(), OrderKeys[iOrderKey].end(), ExponentKeys.begin(), 0, std::plus{}, [](const std::int8_t power, const std::pair& exponentKey) consteval noexcept -> std::int32_t { return power * exponentKey.first; }); + } + for (std::int32_t const& iOrderKey : std::views::iota(1, NOrderKeys) | std::views::reverse) { + std::array orderKeyParent{OrderKeys[iOrderKey]}; + const std::int32_t indexExponentKey{NExponentKeys - 1 - static_cast(std::ranges::find_if_not(std::views::reverse(orderKeyParent), std::logical_not{}) - orderKeyParent.rbegin())}; + --orderKeyParent[indexExponentKey]; + const std::int32_t order{result[iOrderKey].first - ExponentKeys[indexExponentKey].first}; + const std::ranges::subrange, NOrderKeys>::iterator> orderKeyParents{std::ranges::equal_range(std::views::take(result, iOrderKey), order, {}, &std::pair::first)}; + result[iOrderKey] = {static_cast(std::ranges::lower_bound(std::views::take(OrderKeys, orderKeyParents.end() - result.begin()) | std::views::drop(orderKeyParents.begin() - result.begin()), orderKeyParent) - OrderKeys.begin()), indexExponentKey}; + } + return result; +}()}; +static_assert([]() consteval noexcept -> bool { + if (OrderKeys[0] != std::array{}) { + return false; + } + for (std::int32_t const& iOrderKey : std::views::iota(1, NOrderKeys)) { + const auto& [indexOrderKeyParent, indexExponentKey]{OrderKeyProductLinks[iOrderKey]}; + if (std::cmp_less(indexOrderKeyParent, 0) || std::cmp_greater_equal(indexOrderKeyParent, iOrderKey) || std::cmp_less(indexExponentKey, 0) || std::cmp_greater_equal(indexExponentKey, NExponentKeys)) { + return false; + } + for (std::int32_t const& iExponentKeyAfter : std::views::iota(indexExponentKey + 1, NExponentKeys)) { + if (std::cmp_greater(OrderKeys[iOrderKey][iExponentKeyAfter], 0)) { + return false; + } + } + std::array orderKeyReconstructed{OrderKeys[indexOrderKeyParent]}; + ++orderKeyReconstructed[indexExponentKey]; + if (orderKeyReconstructed != OrderKeys[iOrderKey]) { + return false; + } + } + return true; +}()); } // namespace fluctuation_calculator_base class FluctuationCalculatorTrack { public: - FluctuationCalculatorTrack() : mQs{} {} - virtual ~FluctuationCalculatorTrack() {} - - double getProductFast(const std::int32_t orderVectorIndex, const double weight = 1.) const + constexpr FluctuationCalculatorTrack() noexcept = default; + constexpr FluctuationCalculatorTrack(const FluctuationCalculatorTrack&) noexcept = default; + constexpr FluctuationCalculatorTrack(FluctuationCalculatorTrack&&) noexcept = default; + constexpr FluctuationCalculatorTrack& operator=(const FluctuationCalculatorTrack&) noexcept = default; + constexpr FluctuationCalculatorTrack& operator=(FluctuationCalculatorTrack&&) noexcept = default; + virtual constexpr ~FluctuationCalculatorTrack() noexcept = default; + + [[nodiscard]] constexpr double getQ(const std::int32_t indexExponentKey) const noexcept { return mQs[indexExponentKey]; } + [[nodiscard]] constexpr const std::array& getQs() const noexcept { return mQs; } + [[nodiscard]] constexpr std::array getProducts(const double weight = 1.) const noexcept { - double product = 1.; - for (std::int32_t const& iExponentPair : std::views::iota(0, static_cast(fluctuation_calculator_base::NExponentPairs))) { - if (fluctuation_calculator_base::OrderVectors[orderVectorIndex][iExponentPair]) { - product *= std::pow(mQs[iExponentPair], fluctuation_calculator_base::OrderVectors[orderVectorIndex][iExponentPair]); - } + std::array products{weight}; + for (std::int32_t const& iOrderKey : std::views::iota(1, fluctuation_calculator_base::NOrderKeys)) { + const auto& [indexOrderKeyParent, indexExponentKey]{fluctuation_calculator_base::OrderKeyProductLinks[iOrderKey]}; + products[iOrderKey] = products[indexOrderKeyParent] * mQs[indexExponentKey]; + } + return products; + } + constexpr void setQ(const std::int32_t indexExponentKey, const double q) noexcept { mQs[indexExponentKey] = q; } + constexpr void setQs(const std::span& qs) noexcept + { + if (qs.data() != mQs.data()) { + std::ranges::copy(qs, mQs.begin()); } - return product * weight; } - void init() { mQs.fill(0.); } - void fill(const double charge, const double efficiency, const double weight = 1.) + constexpr void addQ(const std::int32_t indexExponentKey, const double q) noexcept { mQs[indexExponentKey] += q; } + constexpr void addQs(const std::span& qs) noexcept { - for (std::int32_t const& iExponentPair : std::views::iota(0, static_cast(fluctuation_calculator_base::NExponentPairs))) { - mQs[iExponentPair] += std::pow(charge, fluctuation_calculator_base::ExponentPairs[iExponentPair].first) / std::pow(efficiency, fluctuation_calculator_base::ExponentPairs[iExponentPair].second) * weight; + for (std::int32_t const& iExponentKey : std::views::iota(0, fluctuation_calculator_base::NExponentKeys)) { + mQs[iExponentKey] += qs[iExponentKey]; + } + } + constexpr void clear() noexcept { mQs.fill({}); } + constexpr void fill(const double charge, const double efficiency, const double weight = 1.) noexcept + { + const double efficiencyInverse{1. / efficiency}; + double powerCharge{weight}; + std::int32_t indexExponentKey{}; + for (std::int32_t const& exponentCharge : std::views::iota(1, fluctuation_calculator_base::OrderMax + 1)) { + powerCharge *= charge; + double powerEfficiencyInverse{powerCharge}; + for ([[maybe_unused]] std::int32_t const& exponentEfficiency : std::views::iota(1, exponentCharge + 1)) { + powerEfficiencyInverse *= efficiencyInverse; + addQ(indexExponentKey++, powerEfficiencyInverse); + } } } - protected: - std::array mQs; + private: + std::array mQs{}; }; -struct PartNumFluc { - enum class CentralityDefinition { kFt0a = 0, - kFt0c, - kFt0m, - kNDefinitions }; - enum class ParticleSpecies { kPi = 0, - kKa, - kPr, - kNSpecies }; - enum class PidStrategy { kTpc = 0, - kTof, - kTpcAndTof, - kTpcTof, - kNStrategies }; +template +concept IsValidEnum = ((std::is_enum_v> && requires { std::remove_cvref_t::N; }) && ...); +template +concept IsValidEnumValue = IsValidEnum...> && ((EValues != std::remove_cvref_t::N) && ...); - struct : ConfigurableGroup { - Configurable cfgCcdbUrl{"cfgCcdbUrl", "http://ccdb-test.cern.ch:8080", "Url of CCDB"}; - Configurable cfgCcdbPath{"cfgCcdbPath", "Users/f/fasi/test", "Path in CCDB"}; - Configurable cfgCcdbTimestampLatest{"cfgCcdbTimestampLatest", 0, "Latest timestamp in CCDB"}; - } groupCcdb; +template + requires IsValidEnum +constexpr std::int32_t toI(const E e) noexcept +{ + return static_cast(e); +} +template + requires IsValidEnum +constexpr std::int32_t NEs{toI(E::N)}; + +enum class NameKind : std::int8_t { + Default = 0, + Lower, + Display, + DisplayLower, + N +}; +enum class DataMode : std::int8_t { + McMcParticle = 0, + McTrack, + RawTrack, + N +}; +enum class Selection : std::int8_t { + Loose = 0, + Default, + Tight, + N +}; +enum class RangeEdge : std::int8_t { + Min = 0, + Max, + N +}; +enum class McEventSelection : std::int8_t { + All = 0, + Good, + Inel, + Vz, + NRecoCollisions, + N +}; +enum class EventSelection : std::int8_t { + All = 0, + Good, + Run, + Rct, + Inel, + Bits, + Centrality, + Vz, + Occupancy, + NPvContributors, + N +}; +enum class CentralityDefinition : std::int8_t { + Ft0a = 0, + Ft0c, + Ft0m, + N +}; +enum class DcaMeasure : std::int8_t { + Mean = 0, + Sigma, + N +}; +enum class DcaAxis : std::int8_t { + Xy = 0, + Z, + N +}; +enum class Detector : std::int8_t { + Tpc = 0, + Tof, + N +}; +enum class PidStrategy : std::int8_t { + Tpc = 0, + TpcTof, + N +}; +enum class PidStrategyAll : std::int8_t { + Tpc = 0, + Tof, + TpcTofSeparated, + TpcTofCombined, + N +}; +enum class ParticleSpecies : std::int8_t { + Pion = 0, + Kaon, + Proton, + N +}; +enum class ParticleSpeciesAll : std::int8_t { + All = 0, + Pion, + Kaon, + Proton, + N +}; +enum class ChargeSpecies : std::int8_t { + Plus = 0, + Minus, + N +}; +enum class ParticleNumber : std::int8_t { + Charge = 0, + Kaon, + Proton, + N +}; +enum class ChargeNumber : std::int8_t { + Plus = 0, + Minus, + Total, + Net, + N +}; - struct : ConfigurableGroup { - Configurable cfgFlagQaRun{"cfgFlagQaRun", false, "Run QA flag"}; - Configurable cfgFlagQaEvent{"cfgFlagQaEvent", false, "Event QA flag"}; - Configurable cfgFlagQaCentrality{"cfgFlagQaCentrality", false, "Centrality QA flag"}; - Configurable cfgFlagQaTrack{"cfgFlagQaTrack", false, "Track QA flag"}; - Configurable cfgFlagQaDca{"cfgFlagQaDca", false, "DCA QA flag"}; - Configurable cfgFlagQaAcceptance{"cfgFlagQaAcceptance", false, "Acceptance QA flag"}; - Configurable cfgFlagQaAcceptancePi{"cfgFlagQaAcceptancePi", false, "Pion acceptance QA flag"}; - Configurable cfgFlagQaAcceptanceKa{"cfgFlagQaAcceptanceKa", false, "Kaon acceptance QA flag"}; - Configurable cfgFlagQaAcceptancePr{"cfgFlagQaAcceptancePr", false, "(Anti)proton acceptance QA flag"}; - Configurable cfgFlagQaPhi{"cfgFlagQaPhi", false, "Phi QA flag"}; - Configurable cfgFlagQaPhiPi{"cfgFlagQaPhiPi", false, "Pion Phi QA flag"}; - Configurable cfgFlagQaPhiKa{"cfgFlagQaPhiKa", false, "Kaon Phi QA flag"}; - Configurable cfgFlagQaPhiPr{"cfgFlagQaPhiPr", false, "(Anti)proton Phi QA flag"}; - Configurable cfgFlagQaPid{"cfgFlagQaPid", false, "PID QA flag"}; - Configurable cfgFlagQaPidPi{"cfgFlagQaPidPi", false, "Pion PID QA flag"}; - Configurable cfgFlagQaPidKa{"cfgFlagQaPidKa", false, "Kaon PID QA flag"}; - Configurable cfgFlagQaPidPr{"cfgFlagQaPidPr", false, "(Anti)proton PID QA flag"}; - Configurable cfgFlagQaMc{"cfgFlagQaMc", false, "MC QA flag"}; - Configurable cfgFlagCalculationYieldPi{"cfgFlagCalculationYieldPi", false, "Pion yield calculation flag"}; - Configurable cfgFlagCalculationYieldKa{"cfgFlagCalculationYieldKa", false, "Kaon yield calculation flag"}; - Configurable cfgFlagCalculationYieldPr{"cfgFlagCalculationYieldPr", false, "(Anti)proton yield calculation flag"}; - Configurable cfgFlagCalculationPurityPi{"cfgFlagCalculationPurityPi", false, "Pion purity calculation flag"}; - Configurable cfgFlagCalculationPurityKa{"cfgFlagCalculationPurityKa", false, "Kaon purity calculation flag"}; - Configurable cfgFlagCalculationPurityPr{"cfgFlagCalculationPurityPr", false, "(Anti)proton purity calculation flag"}; - Configurable cfgFlagCalculationFractionPrimaryPi{"cfgFlagCalculationFractionPrimaryPi", false, "Pion primary fraction calculation flag"}; - Configurable cfgFlagCalculationFractionPrimaryKa{"cfgFlagCalculationFractionPrimaryKa", false, "Kaon primary fraction calculation flag"}; - Configurable cfgFlagCalculationFractionPrimaryPr{"cfgFlagCalculationFractionPrimaryPr", false, "(Anti)proton primary fraction calculation flag"}; - Configurable cfgFlagCalculationFluctuationCh{"cfgFlagCalculationFluctuationCh", false, "Charge number fluctuation calculation flag"}; - Configurable cfgFlagCalculationFluctuationKa{"cfgFlagCalculationFluctuationKa", false, "Kaon number fluctuation calculation flag"}; - Configurable cfgFlagCalculationFluctuationPr{"cfgFlagCalculationFluctuationPr", false, "(Anti)proton number fluctuation calculation flag"}; - } groupAnalysis; +template + requires IsValidEnum +struct EnumInfo; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Loose", "Default", "Tight"}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Min", "Max"}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Mean", "Sigma"}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Xy", "Z"}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Tpc", "Tof"}; + static constexpr std::array> NamesLower{"tpc", "tof"}; + static constexpr std::array> DisplayNames{"TPC", "TOF"}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Tpc", "TpcTof"}; + static constexpr std::array> NamesLower{"tpc", "tpcTof"}; + static constexpr std::array> DisplayNames{"TPC", "TPC+TOF"}; + static constexpr std::array> PidStrategyAllValues{PidStrategyAll::Tpc, PidStrategyAll::TpcTofCombined}; +}; +template <> +struct EnumInfo { + static constexpr std::array> DetectorValues{Detector::Tpc, Detector::Tof, Detector::N, Detector::N}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Pi", "Ka", "Pr"}; + static constexpr std::array> DisplayNames{"Pion", "Kaon", "Proton"}; + static constexpr std::array> DisplayNamesLower{"pion", "kaon", "proton"}; + static constexpr std::array> ParticleSpeciesAllValues{ParticleSpeciesAll::Pion, ParticleSpeciesAll::Kaon, ParticleSpeciesAll::Proton}; + static constexpr std::array>, NEs> PdgCodes{{{PDG_t::kPiPlus, PDG_t::kPiMinus}, {PDG_t::kKPlus, PDG_t::kKMinus}, {PDG_t::kProton, PDG_t::kProtonBar}}}; + static constexpr std::array> Masses{constants::physics::MassPiPlus, constants::physics::MassKPlus, constants::physics::MassProton}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"", "Pi", "Ka", "Pr"}; + static constexpr std::array> DisplayNames{"All", "Pion", "Kaon", "Proton"}; + static constexpr std::array> DisplayNamesLower{"all", "pion", "kaon", "proton"}; + static constexpr std::array> Titles{"", "#pi", "K", "p"}; + static constexpr std::array> ParticleSpeciesValues{ParticleSpecies::N, ParticleSpecies::Pion, ParticleSpecies::Kaon, ParticleSpecies::Proton}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"P", "M"}; + static constexpr std::array> NamesLower{"p", "m"}; + static constexpr std::array> Titles{"+", "#minus"}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"Ch", "Ka", "Pr"}; + static constexpr std::array> DisplayNames{"Charge", "Kaon", "Proton"}; + static constexpr std::array> DisplayNamesLower{"charge", "kaon", "proton"}; + static constexpr std::array> Titles{"h", "K", "p"}; + static constexpr std::array> ParticleSpeciesAllValues{ParticleSpeciesAll::All, ParticleSpeciesAll::Kaon, ParticleSpeciesAll::Proton}; +}; +template <> +struct EnumInfo { + static constexpr std::array> Names{"P", "M", "T", "N"}; +}; - struct : ConfigurableGroup { - Configurable cfgFlagRejectionRunBad{"cfgFlagRejectionRunBad", true, "Bad run rejection flag"}; - Configurable cfgFlagRejectionRunBadMc{"cfgFlagRejectionRunBadMc", false, "MC bad run rejection flag"}; - Configurable cfgLabelFlagsRct{"cfgLabelFlagsRct", "CBT_hadronPID", "RCT flags label"}; - Configurable cfgBitsSelectionEvent{"cfgBitsSelectionEvent", 0b10000000001101000000000000000000000000000000000000ULL, "Event selection bits"}; - Configurable cfgFlagInelEvent{"cfgFlagInelEvent", true, "Flag of requiring inelastic event"}; - Configurable cfgFlagInelEventMc{"cfgFlagInelEventMc", false, "Flag of requiring inelastic MC event"}; - Configurable cfgCutMaxAbsVertexZ{"cfgCutMaxAbsVertexZ", 6., "Maximum absolute vertex z position (cm)"}; - Configurable cfgCutMaxAbsVertexZMc{"cfgCutMaxAbsVertexZMc", 999., "Maximum absolute MC vertex z position (cm) for MC"}; - Configurable cfgCutMinDeviationNPvContributors{"cfgCutMinDeviationNPvContributors", -4, "Minimum nPvContributors deviation from nGlobalTracks"}; - Configurable cfgIndexDefinitionCentrality{"cfgIndexDefinitionCentrality", 2, "Centrality definition index"}; - ConfigurableAxis cfgAxisCentrality{"cfgAxisCentrality", {VARIABLE_WIDTH, 0., 5., 10., 15., 20., 25., 30., 35., 40., 45., 50., 55., 60., 65., 70., 75., 80., 85., 90.}, "Centrality axis in fluctuation calculation"}; - Configurable cfgNSubgroups{"cfgNSubgroups", 20, "Number of subgroups in fluctuation calculation"}; - } groupEvent; +template + requires IsValidEnum +constexpr std::string_view getName(const std::int32_t index) +{ + if constexpr (NameKindValue == NameKind::Lower) { + return EnumInfo>::NamesLower.at(index); + } else if constexpr (NameKindValue == NameKind::Display) { + return EnumInfo>::DisplayNames.at(index); + } else if constexpr (NameKindValue == NameKind::DisplayLower) { + return EnumInfo>::DisplayNamesLower.at(index); + } else { // NameKindValue == NameKind::Default + return EnumInfo>::Names.at(index); + } +} +template + requires IsValidEnum +constexpr std::string_view getName(const E e) +{ + return getName(toI(e)); +} +template + requires IsValidEnum +constexpr std::vector getDisplayNames() +{ + if constexpr (requires { EnumInfo>::DisplayNames; }) { + return {EnumInfo>::DisplayNames.begin(), EnumInfo>::DisplayNames.end()}; + } else { + return {EnumInfo>::Names.begin(), EnumInfo>::Names.end()}; + } +} +template + requires IsValidEnum +constexpr std::string_view getTitle(const std::int32_t index) +{ + return EnumInfo>::Titles.at(index); +} +template + requires IsValidEnum +constexpr To getValue(const E e) noexcept +{ + if constexpr (std::is_same_v, Detector>) { + return EnumInfo::DetectorValues[toI(e)]; + } else if constexpr (std::is_same_v, PidStrategyAll>) { + return EnumInfo::PidStrategyAllValues[toI(e)]; + } else if constexpr (std::is_same_v, ParticleSpecies>) { + return EnumInfo::ParticleSpeciesValues[toI(e)]; + } else if constexpr (std::is_same_v, ParticleSpeciesAll>) { + return EnumInfo::ParticleSpeciesAllValues[toI(e)]; + } else { + return To::N; + } +} +constexpr std::int32_t getPdgCode(const ParticleSpecies particleSpecies, const ChargeSpecies chargeSpecies) noexcept +{ + return EnumInfo::PdgCodes[toI(particleSpecies)][toI(chargeSpecies)]; +} +constexpr double getMass(const ParticleSpecies particleSpecies) noexcept +{ + return EnumInfo::Masses[toI(particleSpecies)]; +} - struct : ConfigurableGroup { - Configurable cfgFlagPvContributor{"cfgFlagPvContributor", true, "Flag of requiring PV contributor"}; - Configurable cfgCutMinItsNCls{"cfgCutMinItsNCls", 5, "Minimum number of clusters ITS"}; - Configurable cfgCutMaxItsChi2NCls{"cfgCutMaxItsChi2NCls", 30., "Maximum chi2 per cluster ITS"}; - Configurable cfgCutMinTpcNCls{"cfgCutMinTpcNCls", 55, "Minimum number of clusters TPC"}; - Configurable cfgCutMaxTpcChi2NCls{"cfgCutMaxTpcChi2NCls", 3.5, "Maximum chi2 per cluster TPC"}; - Configurable cfgCutMaxTpcNClsSharedRatio{"cfgCutMaxTpcNClsSharedRatio", 0.25, "Maximum ratio of shared clusters over clusters TPC"}; - Configurable cfgCutMinTpcNCrossedRows{"cfgCutMinTpcNCrossedRows", 75, "Minimum number of crossed rows TPC"}; - Configurable cfgCutMinTpcNCrossedRowsRatio{"cfgCutMinTpcNCrossedRowsRatio", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; - Configurable cfgFlagRecalibrationDca{"cfgFlagRecalibrationDca", false, "DCA recalibration flag"}; - Configurable cfgCutMaxAbsNSigmaDcaXy{"cfgCutMaxAbsNSigmaDcaXy", 2.5, "Maximum absolute nSigma of DCAxy (cm)"}; - Configurable cfgCutMaxAbsNSigmaDcaZ{"cfgCutMaxAbsNSigmaDcaZ", 2.5, "Maximum absolute nSigma of DCAz (cm)"}; - Configurable cfgCutMinPt{"cfgCutMinPt", 0.4, "Minimum pT (GeV/c)"}; - Configurable cfgCutMaxPt{"cfgCutMaxPt", 2., "Maximum pT (GeV/c)"}; - Configurable cfgCutMaxAbsEta{"cfgCutMaxAbsEta", 0.8, "Maximum absolute eta"}; - Configurable cfgThresholdPtTofPi{"cfgThresholdPtTofPi", 0.5, "pT (GeV/c) threshold for TOF pions"}; - Configurable cfgThresholdPtTofKa{"cfgThresholdPtTofKa", 0.5, "pT (GeV/c) threshold for TOF kaons"}; - Configurable cfgThresholdPtTofPr{"cfgThresholdPtTofPr", 0.8, "pT (GeV/c) threshold for TOF (anti)protons"}; - Configurable cfgFlagRecalibrationNSigmaPi{"cfgFlagRecalibrationNSigmaPi", false, "nSigmaPi recalibration flag"}; - Configurable cfgFlagRecalibrationNSigmaKa{"cfgFlagRecalibrationNSigmaKa", false, "nSigmaKa recalibration flag"}; - Configurable cfgFlagRecalibrationNSigmaPr{"cfgFlagRecalibrationNSigmaPr", false, "nSigmaPr recalibration flag"}; - Configurable cfgFlagRejectionOthers{"cfgFlagRejectionOthers", false, "Other particle species rejection flag"}; - Configurable cfgCutMaxAbsNSigmaPid{"cfgCutMaxAbsNSigmaPid", 2., "Maximum absolute nSigma for PID"}; - Configurable cfgFlagMcParticlePhysicalPrimary{"cfgFlagMcParticlePhysicalPrimary", true, "Flag of requiring physical primary MC particle"}; - Configurable cfgFlagMcParticleMomentum{"cfgFlagMcParticleMomentum", true, "Flag of using momentum of MC particle"}; - } groupTrack; +namespace mini_collision_codec +{ +constexpr std::int32_t NBinsVz{40}; +constexpr std::array> RangeVz{-10., 10.}; +constexpr std::int32_t NBinsCentrality{550}; +constexpr std::array BinEdgesCentrality{[]() consteval noexcept -> std::array { + constexpr std::array EdgesSelection{0., 0.001, 0.01, 0.1, 1., 10., 100.}; + static_assert(std::ranges::adjacent_find(EdgesSelection, std::greater_equal{}) == EdgesSelection.end()); + constexpr std::array NBinsPerSection{100, 90, 90, 90, 90, 90}; + static_assert(std::cmp_equal(std::accumulate(NBinsPerSection.begin(), NBinsPerSection.end(), 0), NBinsCentrality)); + + std::array edges{}; + std::int32_t index{}; + edges[index++] = EdgesSelection.front(); + for (std::int32_t const& iSection : std::views::iota(0, static_cast(NBinsPerSection.size()))) { + const double width{(EdgesSelection[iSection + 1] - EdgesSelection[iSection]) / NBinsPerSection[iSection]}; + for (std::int32_t const& iBin : std::views::iota(1, NBinsPerSection[iSection] + 1)) { + edges[index++] = EdgesSelection[iSection] + iBin * width; + } + } + return edges; +}()}; +constexpr std::int32_t NStatesGoodNPvContributors{2}; +static_assert(std::cmp_less_equal(static_cast(NBinsVz) * NBinsCentrality * NStatesGoodNPvContributors, static_cast(std::numeric_limits::max()) + 1)); - aod::rctsel::RCTFlagsChecker rctFlagsChecker; +std::optional encode(const double vz, const double centrality, const bool isGoodNPvContributors) noexcept +{ + if (!(RangeVz[toI(RangeEdge::Min)] <= vz) || !(vz < RangeVz[toI(RangeEdge::Max)]) || !(BinEdgesCentrality.front() <= centrality) || !(centrality < BinEdgesCentrality.back())) { + return std::nullopt; + } + + const std::int32_t indexBinVz{static_cast(std::floor((vz - RangeVz[toI(RangeEdge::Min)]) / ((RangeVz[toI(RangeEdge::Max)] - RangeVz[toI(RangeEdge::Min)]) / NBinsVz)))}; + if (std::cmp_less(indexBinVz, 0) || std::cmp_greater_equal(indexBinVz, NBinsVz)) { + return std::nullopt; + } + + aod::mini_collision::Code::type code{static_cast(indexBinVz)}; + code = code * NBinsCentrality + static_cast(std::ranges::upper_bound(BinEdgesCentrality, centrality) - BinEdgesCentrality.begin()) - 1; + code = code * NStatesGoodNPvContributors + static_cast(isGoodNPvContributors); + return code; +} +} // namespace mini_collision_codec + +namespace tiny_collision_codec +{ +constexpr std::int32_t NStatesNTracks{4096}; +constexpr std::array NsBitsCode{std::numeric_limits::digits, std::numeric_limits::digits}; +static_assert(std::cmp_less_equal(std::bit_width(static_cast(mini_collision_codec::NBinsVz) * mini_collision_codec::NBinsCentrality * NStatesNTracks * NStatesNTracks - 1), std::accumulate(NsBitsCode.begin(), NsBitsCode.end(), 0))); - Service pdg; - Service ccdb; +std::optional> encode(const double vz, const double centrality, const std::span>& nsTracks) noexcept +{ + if (!(mini_collision_codec::RangeVz[toI(RangeEdge::Min)] <= vz) || !(vz < mini_collision_codec::RangeVz[toI(RangeEdge::Max)]) || !(mini_collision_codec::BinEdgesCentrality.front() <= centrality) || !(centrality < mini_collision_codec::BinEdgesCentrality.back())) { + return std::nullopt; + } + + const std::int32_t indexBinVz{static_cast(std::floor((vz - mini_collision_codec::RangeVz[toI(RangeEdge::Min)]) / ((mini_collision_codec::RangeVz[toI(RangeEdge::Max)] - mini_collision_codec::RangeVz[toI(RangeEdge::Min)]) / mini_collision_codec::NBinsVz)))}; + if (std::cmp_less(indexBinVz, 0) || std::cmp_greater_equal(indexBinVz, mini_collision_codec::NBinsVz)) { + return std::nullopt; + } + + std::uint64_t code{static_cast(indexBinVz)}; + code = code * mini_collision_codec::NBinsCentrality + static_cast(std::ranges::upper_bound(mini_collision_codec::BinEdgesCentrality, centrality) - mini_collision_codec::BinEdgesCentrality.begin()) - 1; + for (std::uint16_t const& nTracks : nsTracks) { + if (std::cmp_greater_equal(nTracks, NStatesNTracks)) { + LOG(fatal) << "Invalid number of tracks " << nTracks << ": must be less than " << NStatesNTracks << "!"; + } + + code = code * NStatesNTracks + nTracks; + } + return std::tuple{static_cast(code), static_cast(code >> NsBitsCode[0])}; +} +} // namespace tiny_collision_codec + +namespace tiny_mc_collision_codec +{ +constexpr std::array NsBitsCode{std::numeric_limits::digits, std::numeric_limits::digits}; +static_assert(std::cmp_less_equal(std::bit_width(static_cast(tiny_collision_codec::NStatesNTracks) * tiny_collision_codec::NStatesNTracks - 1), std::accumulate(NsBitsCode.begin(), NsBitsCode.end(), 0))); + +std::tuple encode(const std::span>& nsMcParticles) noexcept +{ + std::uint64_t code{}; + for (std::uint16_t const& nMcParticles : nsMcParticles) { + if (std::cmp_greater_equal(nMcParticles, tiny_collision_codec::NStatesNTracks)) { + LOG(fatal) << "Invalid number of MC particles " << nMcParticles << ": must be less than " << tiny_collision_codec::NStatesNTracks << "!"; + } + + code = code * tiny_collision_codec::NStatesNTracks + nMcParticles; + } + return std::tuple{static_cast(code), static_cast(code >> NsBitsCode[0])}; +} +} // namespace tiny_mc_collision_codec + +namespace mini_track_codec +{ +constexpr std::int32_t NStatesPvContributor{2}; +constexpr std::int32_t NBinsPt{18}; +constexpr std::int32_t NBinsEta{16}; +constexpr std::array> RangePt{0.2, 2.}; +constexpr std::array> RangeEta{-0.8, 0.8}; +constexpr std::array NsBitsCode{std::numeric_limits::digits, std::numeric_limits::digits}; +static_assert(std::cmp_less_equal(std::bit_width(static_cast(NStatesPvContributor) * NEs * NEs * NEs * NEs * NEs * NEs * NBinsPt * NBinsEta * NEs * NEs * NEs - 1), std::accumulate(NsBitsCode.begin(), NsBitsCode.end(), 0))); + +struct ConfigSelection { + std::array> cutsMinItsNCls{}; + std::array> cutsMaxItsChi2NCls{}; + std::array> cutsMaxTpcChi2NCls{}; + std::array> cutsMaxTpcNClsSharedRatio{}; + std::array> cutsMinTpcNCrossedRows{}; + std::array>, NEs> cutsMaxAbsNSigmaDca{}; + std::array>, NEs> cutsMaxAbsNSigmaPid{}; +}; + +template + requires IsValidEnumValue +std::optional> encode(const ConfigSelection& configSelection, const bool isPvContributor, const double itsNCls, const double itsChi2NCls, const double tpcChi2NCls, const double tpcNClsSharedRatio, const double tpcNCrossedRows, const std::span>& absNsSigmaDca, const double pt, const double eta, const std::int32_t sign, const double absNSigmaPid) noexcept +{ + if (!(RangePt[toI(RangeEdge::Min)] <= pt) || !(pt < RangePt[toI(RangeEdge::Max)]) || !(RangeEta[toI(RangeEdge::Min)] <= eta) || !(eta < RangeEta[toI(RangeEdge::Max)])) { + return std::nullopt; + } + + const std::int32_t indexBinPt{static_cast(std::floor((pt - RangePt[toI(RangeEdge::Min)]) / ((RangePt[toI(RangeEdge::Max)] - RangePt[toI(RangeEdge::Min)]) / NBinsPt)))}; + if (std::cmp_less(indexBinPt, 0) || std::cmp_greater_equal(indexBinPt, NBinsPt)) { + return std::nullopt; + } + + const std::int32_t indexBinEta{static_cast(std::floor((eta - RangeEta[toI(RangeEdge::Min)]) / ((RangeEta[toI(RangeEdge::Max)] - RangeEta[toI(RangeEdge::Min)]) / NBinsEta)))}; + if (std::cmp_less(indexBinEta, 0) || std::cmp_greater_equal(indexBinEta, NBinsEta)) { + return std::nullopt; + } + + static constexpr auto GetState{ + [] + requires IsValidEnumValue + (const double input, const std::span>& cuts) constexpr noexcept -> std::int32_t { + for (std::int32_t const& iSelection : std::views::iota(0, NEs) | std::views::reverse) { + if constexpr (RangeEdgeValue == RangeEdge::Min) { + if (input > cuts[iSelection]) { + return iSelection; + } + } else { // RangeEdgeValue == RangeEdge::Max + if (input < cuts[iSelection]) { + return iSelection; + } + } + } + return -1; + }}; + std::uint64_t code{static_cast(isPvContributor)}; + for (std::int32_t const& state : std::to_array( + // cppcheck-suppress internalAstError + {GetState.template operator()(itsNCls, configSelection.cutsMinItsNCls), + GetState.template operator()(itsChi2NCls, configSelection.cutsMaxItsChi2NCls), + GetState.template operator()(tpcChi2NCls, configSelection.cutsMaxTpcChi2NCls), + GetState.template operator()(tpcNClsSharedRatio, configSelection.cutsMaxTpcNClsSharedRatio), + GetState.template operator()(tpcNCrossedRows, configSelection.cutsMinTpcNCrossedRows), + std::ranges::min(std::views::iota(0, NEs) | std::views::transform([&absNsSigmaDca, &configSelection](const std::int32_t indexDcaAxis) constexpr noexcept -> std::int32_t { return GetState.template operator()(absNsSigmaDca[indexDcaAxis], configSelection.cutsMaxAbsNSigmaDca[indexDcaAxis]); }))})) { + if (std::cmp_less(state, 0)) { + return std::nullopt; + } + + code = code * NEs + state; + } + code = code * NBinsPt + indexBinPt; + code = code * NBinsEta + indexBinEta; + code = code * NEs + (std::cmp_greater(sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)); + code = code * NEs + toI(ParticleSpeciesAllValue); + if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { + const std::int32_t statePid = GetState.template operator()(absNSigmaPid, configSelection.cutsMaxAbsNSigmaPid[toI(getValue(ParticleSpeciesAllValue))]); + if (std::cmp_less(statePid, 0)) { + return std::nullopt; + } + + code = code * NEs + statePid; + } else { // ParticleSpeciesAllValue == ParticleSpeciesAll::All + code *= NEs; + } + return std::tuple{static_cast(code), static_cast(code >> NsBitsCode[0])}; +} +} // namespace mini_track_codec + +namespace mini_mc_particle_codec +{ +static_assert(std::cmp_less_equal(static_cast(mini_track_codec::NBinsPt) * mini_track_codec::NBinsEta * NEs * NEs, static_cast(std::numeric_limits::max()) + 1)); + +template + requires IsValidEnumValue +std::optional encode(const double pt, const double eta, const std::int32_t sign) noexcept +{ + if (!(mini_track_codec::RangePt[toI(RangeEdge::Min)] <= pt) || !(pt < mini_track_codec::RangePt[toI(RangeEdge::Max)]) || !(mini_track_codec::RangeEta[toI(RangeEdge::Min)] <= eta) || !(eta < mini_track_codec::RangeEta[toI(RangeEdge::Max)])) { + return std::nullopt; + } + + const std::int32_t indexBinPt{static_cast(std::floor((pt - mini_track_codec::RangePt[toI(RangeEdge::Min)]) / ((mini_track_codec::RangePt[toI(RangeEdge::Max)] - mini_track_codec::RangePt[toI(RangeEdge::Min)]) / mini_track_codec::NBinsPt)))}; + if (std::cmp_less(indexBinPt, 0) || std::cmp_greater_equal(indexBinPt, mini_track_codec::NBinsPt)) { + return std::nullopt; + } + + const std::int32_t indexBinEta{static_cast(std::floor((eta - mini_track_codec::RangeEta[toI(RangeEdge::Min)]) / ((mini_track_codec::RangeEta[toI(RangeEdge::Max)] - mini_track_codec::RangeEta[toI(RangeEdge::Min)]) / mini_track_codec::NBinsEta)))}; + if (std::cmp_less(indexBinEta, 0) || std::cmp_greater_equal(indexBinEta, mini_track_codec::NBinsEta)) { + return std::nullopt; + } + + aod::mini_mc_particle::Code::type code{static_cast(indexBinPt)}; + code = code * mini_track_codec::NBinsEta + indexBinEta; + code = code * NEs + (std::cmp_greater(sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)); + code = code * NEs + toI(ParticleSpeciesValue); + return code; +} +} // namespace mini_mc_particle_codec + +template + requires std::is_arithmetic_v +constexpr std::int32_t nEnabled(const Configurable>& cfg) noexcept +{ + const LabeledArray& la{cfg.value}; + return std::ranges::count_if(std::span{la[0], static_cast(la.rows()) * static_cast(la.cols())}, [](const T input) constexpr noexcept -> bool { return input != T{}; }); +} +template + requires std::is_arithmetic_v +constexpr bool isEnabled(const Configurable>& cfg) noexcept +{ + return std::cmp_greater(nEnabled(cfg), 0); +} + +double interpolate(const TH3* const h, const double x, const double y, const double z) +{ + if (h == nullptr) { + return 0.; + } + + static constexpr auto GetBinIndicesWeights{[](const TAxis* const a, const double position) -> std::array, NEs> { + if (!a) { + return {}; + } + + const std::int32_t nBins{a->GetNbins()}; + if (std::cmp_equal(nBins, 1) || position <= a->GetBinCenter(1)) { + return {{{1, 1.}, {1, 0.}}}; + } + if (position >= a->GetBinCenter(nBins)) { + return {{{nBins, 1.}, {nBins, 0.}}}; + } + + const std::int32_t indexBin{a->FindFixBin(position)}; + const std::int32_t indexBinLower{position < a->GetBinCenter(indexBin) ? indexBin - 1 : indexBin}; + const std::int32_t indexBinUpper{indexBinLower + 1}; + const double fraction{(position - a->GetBinCenter(indexBinLower)) / (a->GetBinCenter(indexBinUpper) - a->GetBinCenter(indexBinLower))}; + + return {{{indexBinLower, 1. - fraction}, {indexBinUpper, fraction}}}; + }}; + + const std::array, NEs> indicesAndWeightsBinX{GetBinIndicesWeights(h->GetXaxis(), x)}; + const std::array, NEs> indicesAndWeightsBinY{GetBinIndicesWeights(h->GetYaxis(), y)}; + const std::array, NEs> indicesAndWeightsBinZ{GetBinIndicesWeights(h->GetZaxis(), z)}; + + double result{}; + for (const auto& [iBinX, weightX] : indicesAndWeightsBinX) { + for (const auto& [iBinY, weightY] : indicesAndWeightsBinY) { + for (const auto& [iBinZ, weightZ] : indicesAndWeightsBinZ) { + result += weightX * weightY * weightZ * h->GetBinContent(iBinX, iBinY, iBinZ); + } + } + } + return result; +} +} // namespace + +struct PartNumFluc { struct HolderCcdb { - std::map> runNumbersIndicesGroupIndices; - - std::vector fPtMeanDcaXyP; - std::vector fPtMeanDcaXyM; - std::vector fPtMeanDcaZP; - std::vector fPtMeanDcaZM; - std::vector fPtSigmaDcaXyP; - std::vector fPtSigmaDcaXyM; - std::vector fPtSigmaDcaZP; - std::vector fPtSigmaDcaZM; - - std::vector hCentralityPtEtaShiftTpcNSigmaPiP; - std::vector hCentralityPtEtaShiftTpcNSigmaPiM; - std::vector hCentralityPtEtaShiftTpcNSigmaKaP; - std::vector hCentralityPtEtaShiftTpcNSigmaKaM; - std::vector hCentralityPtEtaShiftTpcNSigmaPrP; - std::vector hCentralityPtEtaShiftTpcNSigmaPrM; - std::vector hCentralityPtEtaShiftTofNSigmaPiP; - std::vector hCentralityPtEtaShiftTofNSigmaPiM; - std::vector hCentralityPtEtaShiftTofNSigmaKaP; - std::vector hCentralityPtEtaShiftTofNSigmaKaM; - std::vector hCentralityPtEtaShiftTofNSigmaPrP; - std::vector hCentralityPtEtaShiftTofNSigmaPrM; - - std::vector hVzCentralityPtEtaEfficiencyTpcPiP; - std::vector hVzCentralityPtEtaEfficiencyTpcPiM; - std::vector hVzCentralityPtEtaEfficiencyTpcKaP; - std::vector hVzCentralityPtEtaEfficiencyTpcKaM; - std::vector hVzCentralityPtEtaEfficiencyTpcPrP; - std::vector hVzCentralityPtEtaEfficiencyTpcPrM; - std::vector hVzCentralityPtEtaEfficiencyTpcTofPiP; - std::vector hVzCentralityPtEtaEfficiencyTpcTofPiM; - std::vector hVzCentralityPtEtaEfficiencyTpcTofKaP; - std::vector hVzCentralityPtEtaEfficiencyTpcTofKaM; - std::vector hVzCentralityPtEtaEfficiencyTpcTofPrP; - std::vector hVzCentralityPtEtaEfficiencyTpcTofPrM; - - void clear() - { - runNumbersIndicesGroupIndices.clear(); - - fPtMeanDcaXyP.clear(); - fPtMeanDcaXyM.clear(); - fPtMeanDcaZP.clear(); - fPtMeanDcaZM.clear(); - fPtSigmaDcaXyP.clear(); - fPtSigmaDcaXyM.clear(); - fPtSigmaDcaZP.clear(); - fPtSigmaDcaZM.clear(); - - hCentralityPtEtaShiftTpcNSigmaPiP.clear(); - hCentralityPtEtaShiftTpcNSigmaPiM.clear(); - hCentralityPtEtaShiftTpcNSigmaKaP.clear(); - hCentralityPtEtaShiftTpcNSigmaKaM.clear(); - hCentralityPtEtaShiftTpcNSigmaPrP.clear(); - hCentralityPtEtaShiftTpcNSigmaPrM.clear(); - hCentralityPtEtaShiftTofNSigmaPiP.clear(); - hCentralityPtEtaShiftTofNSigmaPiM.clear(); - hCentralityPtEtaShiftTofNSigmaKaP.clear(); - hCentralityPtEtaShiftTofNSigmaKaM.clear(); - hCentralityPtEtaShiftTofNSigmaPrP.clear(); - hCentralityPtEtaShiftTofNSigmaPrM.clear(); - - hVzCentralityPtEtaEfficiencyTpcPiP.clear(); - hVzCentralityPtEtaEfficiencyTpcPiM.clear(); - hVzCentralityPtEtaEfficiencyTpcKaP.clear(); - hVzCentralityPtEtaEfficiencyTpcKaM.clear(); - hVzCentralityPtEtaEfficiencyTpcPrP.clear(); - hVzCentralityPtEtaEfficiencyTpcPrM.clear(); - hVzCentralityPtEtaEfficiencyTpcTofPiP.clear(); - hVzCentralityPtEtaEfficiencyTpcTofPiM.clear(); - hVzCentralityPtEtaEfficiencyTpcTofKaP.clear(); - hVzCentralityPtEtaEfficiencyTpcTofKaM.clear(); - hVzCentralityPtEtaEfficiencyTpcTofPrP.clear(); - hVzCentralityPtEtaEfficiencyTpcTofPrM.clear(); - } - } holderCcdb; + static constexpr std::int32_t NDimensionsEfficiency{4}; + + const TList* lCcdb{}; + std::map> runNumbersToIndicesRunAndGroup; + std::int32_t indexRunGroupCurrent{}; + std::array, NEs>, NEs>, NEs> calibrationsPtMeasureDca{}; + std::array>, NEs>, NEs> hsCentralityPtEtaShiftNSigmaPid{}; + std::array>, NEs>, NEs> hsVzCentralityPtEtaEfficiency{}; + } holderCcdb{}; struct HolderMcEvent { - std::int32_t runNumber{}; - std::int32_t runIndex{}; - std::int32_t runGroupIndex{}; double vz{}; - std::int32_t nChP{}; - std::int32_t nChM{}; - std::int32_t nKaP{}; - std::int32_t nKaM{}; - std::int32_t nPrP{}; - std::int32_t nPrM{}; - - void clear() - { - *this = {}; - } - } holderMcEvent; + std::array>, NEs> numbers{}; + std::array>, NEs> numbersEff{}; + + constexpr void clear() noexcept { *this = {}; } + } holderMcEvent{}; struct HolderEvent { - static constexpr std::pair RangeCentrality = {0., 100.}; + static constexpr std::array> RangeCentrality{0., 100.}; + static constexpr bool isValidCentrality(const double centrality) noexcept { return RangeCentrality[toI(RangeEdge::Min)] <= centrality && centrality < RangeCentrality[toI(RangeEdge::Max)]; } std::int32_t runNumber{}; - std::int32_t runIndex{}; - std::int32_t runGroupIndex{}; + std::int32_t indexRun{}; + std::int32_t indexRunGroup{}; double vz{}; - std::int32_t nGlobalTracksP{}; - std::int32_t nGlobalTracksM{}; - std::int32_t nPvContributorsP{}; - std::int32_t nPvContributorsM{}; - double meanDcaXyP{}; - double meanDcaXyM{}; - double meanSquareDcaXyP{}; - double meanSquareDcaXyM{}; - double meanDcaZP{}; - double meanDcaZM{}; - double meanSquareDcaZP{}; - double meanSquareDcaZM{}; - std::int32_t nTofBetaP{}; - std::int32_t nTofBetaM{}; + std::array> nsGlobalTracks{}; + std::array> nsPvContributors{}; + std::array>, NEs>, NEs> measuresDca{}; + std::array> nsTofBeta{}; + double centralityCalibration{}; double centrality{}; - std::int32_t subgroupIndex{}; - std::int32_t nChP{}; - std::int32_t nChM{}; - std::int32_t nKaP{}; - std::int32_t nKaM{}; - std::int32_t nPrP{}; - std::int32_t nPrM{}; - - void clear() + std::int32_t indexSubgroup{}; + std::array>, NEs> numbers{}; + + constexpr void clear() noexcept { *this = {}; } + [[nodiscard]] constexpr std::int32_t getNGlobalTracks() const noexcept { return std::accumulate(nsGlobalTracks.begin(), nsGlobalTracks.end(), 0); } + [[nodiscard]] constexpr std::int32_t getNPvContributors() const noexcept { return std::accumulate(nsPvContributors.begin(), nsPvContributors.end(), 0); } + template + requires IsValidEnumValue + [[nodiscard]] double getMeasureDca() const noexcept { - *this = {}; + const std::int32_t sumNGlobalTracks{getNGlobalTracks()}; + if (std::cmp_equal(sumNGlobalTracks, 0)) { + return 0.; + } + + double sumDca{}; + if constexpr (DcaMeasureValue == DcaMeasure::Sigma) { + const double meanDca{getMeasureDca()}; + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + sumDca += (std::pow(measuresDca[toI(DcaMeasure::Sigma)][toI(DcaAxisValue)][iChargeSpecies], 2.) + std::pow(measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][iChargeSpecies] - meanDca, 2.)) * nsGlobalTracks[iChargeSpecies]; + } + return std::sqrt(sumDca / sumNGlobalTracks); + } else { // DcaMeasureValue == DcaMeasure::Mean + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + sumDca += measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][iChargeSpecies] * nsGlobalTracks[iChargeSpecies]; + } + return sumDca / sumNGlobalTracks; + } } - } holderEvent; + [[nodiscard]] constexpr std::int32_t getNTofBeta() const noexcept { return std::accumulate(nsTofBeta.begin(), nsTofBeta.end(), 0); } + } holderEvent{}; struct HolderMcParticle { std::int32_t pdgCode{}; std::int32_t charge{}; double pt{}; double eta{}; - double phi{}; - void clear() - { - *this = {}; - } - } holderMcParticle; + constexpr void clear() noexcept { *this = {}; } + } holderMcParticle{}; struct HolderTrack { - static constexpr double TruncationAbsNSigmaPid = 999.; - static constexpr double truncateNSigmaPid(const double value) { return (!(std::abs(value) < TruncationAbsNSigmaPid) ? -TruncationAbsNSigmaPid : value); } + static constexpr double TruncationAbsNSigmaPid{999.}; + static constexpr double truncateNSigmaPid(const double nSigmaPid, const double shift = {}) noexcept + { + const double nSigmaPidShifted{nSigmaPid - shift}; + return std::abs(nSigmaPid) < TruncationAbsNSigmaPid && std::abs(nSigmaPidShifted) < TruncationAbsNSigmaPid ? nSigmaPidShifted : -TruncationAbsNSigmaPid; + } - double dcaXY{}; - double dcaZ{}; + std::array> dcas{}; std::int32_t sign{}; - double p{}; double pt{}; double eta{}; double phi{}; - bool hasTpcPid{}; - double tpcNSigmaPi{}; - double tpcNSigmaKa{}; - double tpcNSigmaPr{}; - bool hasTofPid{}; - double tofNSigmaPi{}; - double tofNSigmaKa{}; - double tofNSigmaPr{}; - double tpcTofNSigmaPi{}; - double tpcTofNSigmaKa{}; - double tpcTofNSigmaPr{}; - - void clear() + std::array> havePid{}; + std::array>, NEs> nsSigmaPid{[]() consteval noexcept -> std::array>, NEs> { + std::array>, NEs> result{}; + std::array> source{}; + source.fill(-TruncationAbsNSigmaPid); + result.fill(source); + return result; + }()}; + + [[nodiscard]] double getNSigmaPidCombined(const std::int32_t indexParticleSpecies) const noexcept { - *this = {}; - } - } holderTrack; - - std::unique_ptr fluctuationCalculatorTrackChP; - std::unique_ptr fluctuationCalculatorTrackChM; - std::unique_ptr fluctuationCalculatorTrackChT; - std::unique_ptr fluctuationCalculatorTrackChN; - std::unique_ptr fluctuationCalculatorTrackKaP; - std::unique_ptr fluctuationCalculatorTrackKaM; - std::unique_ptr fluctuationCalculatorTrackKaT; - std::unique_ptr fluctuationCalculatorTrackKaN; - std::unique_ptr fluctuationCalculatorTrackPrP; - std::unique_ptr fluctuationCalculatorTrackPrM; - std::unique_ptr fluctuationCalculatorTrackPrT; - std::unique_ptr fluctuationCalculatorTrackPrN; - - HistogramRegistry hrCalculationFluctuation{"hrCalculationFluctuation", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrCalculationFractionPrimary{"hrCalculationFractionPrimary", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrCalculationPurity{"hrCalculationPurity", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrCalculationYield{"hrCalculationYield", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaMc{"hrQaMc", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaPid{"hrQaPid", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaPhi{"hrQaPhi", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaAcceptance{"hrQaAcceptance", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaDca{"hrQaDca", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaTrack{"hrQaTrack", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaCentrality{"hrQaCentrality", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaEvent{"hrQaEvent", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrQaRun{"hrQaRun", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry hrCounter{"hrCounter", {}, OutputObjHandlingPolicy::AnalysisObject}; - - Filter filterCollision = (aod::evsel::sel8 == true); - Filter filterTrack = requireQualityTracksInFilter(); - Filter filterMcCollision = (aod::mccollisionprop::numRecoCollision > 0); - - Preslice presliceTracksPerCollision = aod::track::collisionId; - - void init(InitContext&) - { - gRandom->SetSeed(0); + return truncateNSigmaPid(std::copysign(std::hypot(nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies], nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies]), nsSigmaPid[toI(Detector::Tpc)][indexParticleSpecies] + nsSigmaPid[toI(Detector::Tof)][indexParticleSpecies])); + } + constexpr void clear() noexcept { *this = {}; } + } holderTrack{}; - if (doprocessRaw.value == doprocessMc.value) { - LOG(fatal) << "Identical doprocessRaw and doprocessMc!"; + struct HolderDerivedData { + template + static constexpr T convert(const double input) noexcept + { + if (std::numeric_limits::lowest() <= input && input <= std::numeric_limits::max()) { + return static_cast(std::rint(input)); + } + if constexpr (std::is_signed_v) { + return std::numeric_limits::lowest(); + } else { + return std::numeric_limits::max(); + } } - if (doprocessRaw.value) { - LOG(info) << "Enabling raw data process."; - } else if (doprocessMc.value) { + + std::array> nsMcParticles{}; + std::array> nsTracks{}; + std::vector signedEfficienciesMcParticle{[]() constexpr -> std::vector { std::vector result{}; result.reserve(256); return result; }()}; + std::vector signedEfficienciesTrack{[]() constexpr -> std::vector { std::vector result{}; result.reserve(256); return result; }()}; + + constexpr void clear() noexcept + { + nsMcParticles = {}; + nsTracks = {}; + signedEfficienciesMcParticle.clear(); + signedEfficienciesTrack.clear(); + } + } holderDerivedData{}; + + struct HolderMember { + bool doQaAcceptance{}; + bool doQaPhi{}; + bool doQaPid{}; + bool doCalculationYield{}; + bool doCalculationPurity{}; + bool doCalculationFractionPrimary{}; + bool doCalculationFluctuation{}; + bool doStorageMiniTable{}; + std::array> rangePtAll{std::numeric_limits::max(), std::numeric_limits::lowest()}; + std::optional configSelection; + std::mt19937_64 engineRandom{std::random_device{}()}; + std::array, NEs>, NEs> fluctuationCalculatorsTrackMcParticle{}; + std::array, NEs>, NEs> fluctuationCalculatorsTrackTrack{}; + } holderMember{}; + + struct : ConfigurableGroup { + std::string prefix{"cgCcdb"}; + Configurable cfgUrl{"cfgUrl", "https://alice-ccdb.cern.ch", "Url of CCDB"}; + Configurable cfgPath{"cfgPath", "Users/f/fasi/test", "Path in CCDB"}; + Configurable cfgTimestampLatest{"cfgTimestampLatest", -1, "Latest timestamp in CCDB"}; + } cgCcdb{}; + + struct : ConfigurableGroup { + std::string prefix{"cgAnalysis"}; + Configurable cfgFlagQaRun{"cfgFlagQaRun", false, "Run QA flag"}; + Configurable cfgFlagQaEvent{"cfgFlagQaEvent", false, "Event QA flag"}; + Configurable cfgFlagQaCentrality{"cfgFlagQaCentrality", false, "Centrality QA flag"}; + Configurable cfgFlagQaTrack{"cfgFlagQaTrack", false, "Track QA flag"}; + Configurable cfgFlagQaDca{"cfgFlagQaDca", false, "DCA QA flag"}; + Configurable> cfgFlagsQaAcceptance{"cfgFlagsQaAcceptance", {std::array>{}.data(), NEs, getDisplayNames()}, "Acceptance QA flags"}; + Configurable> cfgFlagsQaPhi{"cfgFlagsQaPhi", {std::array>{}.data(), NEs, getDisplayNames()}, "Phi QA flags"}; + Configurable> cfgFlagsQaPid{"cfgFlagsQaPid", {std::array>{}.data(), NEs, getDisplayNames()}, "PID QA flags"}; + Configurable cfgFlagQaMc{"cfgFlagQaMc", false, "MC QA flag"}; + Configurable> cfgFlagsCalculationYield{"cfgFlagsCalculationYield", {std::array>{}.data(), NEs, getDisplayNames()}, "Yield calculation flags"}; + Configurable> cfgFlagsCalculationPurity{"cfgFlagsCalculationPurity", {std::array>{}.data(), NEs, getDisplayNames()}, "Purity calculation flags"}; + Configurable> cfgFlagsCalculationFractionPrimary{"cfgFlagsCalculationFractionPrimary", {std::array>{}.data(), NEs, getDisplayNames()}, "Primary fraction calculation flags"}; + Configurable> cfgFlagsCalculationFluctuation{"cfgFlagsCalculationFluctuation", {std::array>{}.data(), NEs, getDisplayNames()}, "Fluctuation calculation flags"}; + Configurable> cfgFlagsStorageMiniTable{"cfgFlagsStorageMiniTable", {std::array>{}.data(), NEs, getDisplayNames()}, "Mini table storage flags"}; + } cgAnalysis{}; + + struct : ConfigurableGroup { + std::string prefix{"cgEvent"}; + Configurable cfgFlagRejectionRunBad{"cfgFlagRejectionRunBad", false, "Bad run rejection flag"}; + Configurable cfgFlagRejectionRunBadMc{"cfgFlagRejectionRunBadMc", false, "MC bad run rejection flag"}; + Configurable cfgLabelFlagsRct{"cfgLabelFlagsRct", "CBT_hadronPID", "RCT flags label"}; + Configurable> cfgFlagsRct{"cfgFlagsRct", {std::array{0, 1, 1}.data(), 3, {"ZDC", "Acceptance", "Table"}}, "RCT flags"}; + Configurable cfgBitsSelection{"cfgBitsSelection", std::uint64_t{0b00000000000001000000000000000000000000000000000000}, "Event selection bits"}; + Configurable cfgFlagInel{"cfgFlagInel", true, "Flag of requiring INEL > 0"}; + Configurable cfgFlagInelMc{"cfgFlagInelMc", true, "Flag of requiring MC INEL > 0"}; + Configurable cfgCutMaxAbsVz{"cfgCutMaxAbsVz", 8., "Maximum absolute z-vertex position (cm)"}; + Configurable cfgCutMaxAbsVzMc{"cfgCutMaxAbsVzMc", 8., "Maximum absolute MC z-vertex position (cm)"}; + Configurable cfgCutMaxOccupancy{"cfgCutMaxOccupancy", -1, "Maximum occupancy"}; + Configurable cfgCutMinDeviationNPvContributors{"cfgCutMinDeviationNPvContributors", -4, "Minimum nPvContributors deviation from nGlobalTracks"}; + Configurable cfgIndexDefinitionCentrality{"cfgIndexDefinitionCentrality", 2, "Centrality definition index"}; + Configurable cfgFlagDefinitionCentralitySameQa{"cfgFlagDefinitionCentralitySameQa", false, "Flag of using the same centrality definition for QA"}; + Configurable cfgNMultiplicityBinsAxis{"cfgNMultiplicityBinsAxis", 200, "Number of multiplicity bins for axis"}; + ConfigurableAxis cfgAxisCentralityCalibration{"cfgAxisCentralityCalibration", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 100.}, "Centrality axis in calibration"}; + ConfigurableAxis cfgAxisCentrality{"cfgAxisCentrality", {20, 0., 100.}, "Centrality axis in fluctuation calculation"}; + Configurable cfgNSubgroups{"cfgNSubgroups", 20, "Number of subgroups in fluctuation calculation"}; + Configurable cfgFactorStorageMiniTable{"cfgFactorStorageMiniTable", 1., "Mini table storage inverse probability factor"}; + Configurable cfgFlagSingleCollisionMc{"cfgFlagSingleCollisionMc", false, "Flag of requiring exactly single collision of MC collision"}; + Configurable cfgFlagMcCollisionVz{"cfgFlagMcCollisionVz", true, "Flag of using z-vertex position of MC collision"}; + } cgEvent{}; + + struct : ConfigurableGroup { + std::string prefix{"cgTrack"}; + Configurable cfgFlagPvContributor{"cfgFlagPvContributor", true, "Flag of requiring PV contributor"}; + Configurable> cfgCutsMinItsNCls{"cfgCutsMinItsNCls", {std::array>{4, 5, 6}.data(), NEs, getDisplayNames()}, "Minimum numbers of clusters ITS"}; + Configurable> cfgCutsMaxItsChi2NCls{"cfgCutsMaxItsChi2NCls", {std::array>{30., 25., 20.}.data(), NEs, getDisplayNames()}, "Maximum chi2 per cluster ITS"}; + Configurable> cfgCutsMinTpcNCls{"cfgCutsMinTpcNCls", {std::array>{45, 55, 65}.data(), NEs, getDisplayNames()}, "Minimum numbers of clusters TPC"}; + Configurable cfgCutMinTpcChi2NCls{"cfgCutMinTpcChi2NCls", 0., "Minimum chi2 per cluster TPC"}; + Configurable> cfgCutsMaxTpcChi2NCls{"cfgCutsMaxTpcChi2NCls", {std::array>{4., 3.5, 3.}.data(), NEs, getDisplayNames()}, "Maximum chi2 per cluster TPC"}; + Configurable> cfgCutsMaxTpcNClsSharedRatio{"cfgCutsMaxTpcNClsSharedRatio", {std::array>{0.5, 0.4, 0.3}.data(), NEs, getDisplayNames()}, "Maximum ratios of shared clusters over clusters TPC"}; + Configurable cfgCutMinTpcNClsRatio{"cfgCutMinTpcNClsRatio", 0., "Minimum ratio of clusters over findable clusters TPC"}; + Configurable> cfgCutsMinTpcNCrossedRows{"cfgCutsMinTpcNCrossedRows", {std::array>{70, 80, 90}.data(), NEs, getDisplayNames()}, "Minimum numbers of crossed rows TPC"}; + Configurable cfgCutMinTpcNCrossedRowsRatio{"cfgCutMinTpcNCrossedRowsRatio", 0.8, "Minimum ratio of crossed rows over findable clusters TPC"}; + Configurable cfgFlagRecalibrationDca{"cfgFlagRecalibrationDca", false, "DCA recalibration flag"}; + Configurable> cfgCutsMaxAbsNSigmaDca{"cfgCutsMaxAbsNSigmaDca", {std::array(NEs) * static_cast(NEs)>{3., 2.5, 2., 3., 2.5, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values of DCA (cm)"}; + Configurable> cfgCutsRangePt{"cfgCutsRangePt", {std::array(NEs) * static_cast(NEs)>{0.2, 2., 0.3, 2., 0.4, 2.}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "pT ranges (GeV/c)"}; + Configurable cfgCutMaxAbsEta{"cfgCutMaxAbsEta", 0.8, "Maximum absolute eta"}; + Configurable> cfgThresholdsPtTofPid{"cfgThresholdsPtTofPid", {std::array>{0.4, 0.4, 0.8}.data(), NEs, getDisplayNames()}, "pT (GeV/c) thresholds for TOF PID"}; + Configurable> cfgFlagsRecalibrationNSigmaPid{"cfgFlagsRecalibrationNSigmaPid", {std::array>{}.data(), NEs, getDisplayNames()}, "nSigma PID recalibration flags"}; + Configurable cfgFlagRejectionOthers{"cfgFlagRejectionOthers", false, "Other particle species rejection flag"}; + Configurable> cfgCutsMaxAbsNSigmaPid{"cfgCutsMaxAbsNSigmaPid", {std::array(NEs) * static_cast(NEs)>{2.5, 2., 1.5, 2.5, 2., 1.5, 2.5, 2., 1.5}.data(), NEs, NEs, getDisplayNames(), getDisplayNames()}, "Maximum absolute nSigma values for PID"}; + Configurable cfgFlagMcParticlePhysicalPrimary{"cfgFlagMcParticlePhysicalPrimary", true, "Flag of requiring physical primary MC particle"}; + Configurable cfgFlagMcParticleMomentum{"cfgFlagMcParticleMomentum", true, "Flag of using momentum of MC particle"}; + } cgTrack{}; + + Service pdg{}; + Service ccdb{}; + + aod::rctsel::RCTFlagsChecker rctFlagsChecker; + + Filter filterCollision{aod::evsel::sel8 == true}; + Filter filterTrack{requireQualityTracksInFilter() && requireTrackCutInFilter(TrackSelectionFlags::kGoldenChi2)}; + Filter filterMcCollision{aod::mccollisionprop::numRecoCollision > 0}; + + struct : PresliceGroup { + Preslice tracksPerCollision{aod::track::collisionId}; + PresliceUnsorted tracksPerMcParticle{aod::mctracklabel::mcParticleId}; + } psg{}; + + struct : ProducesGroup { + Produces miniCollision{}; + Produces miniMcParticle{}; + Produces miniTrack{}; + Produces tinyMcCollision{}; + Produces tinyCollision{}; + Produces tinyMcParticle{}; + Produces tinyTrack{}; + } pg{}; + + HistogramRegistry hrCalculationFluctuation{"hrCalculationFluctuation", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCalculationFractionPrimary{"hrCalculationFractionPrimary", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCalculationPurity{"hrCalculationPurity", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCalculationYield{"hrCalculationYield", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaMc{"hrQaMc", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaPid{"hrQaPid", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaPhi{"hrQaPhi", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaAcceptance{"hrQaAcceptance", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaDca{"hrQaDca", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaTrack{"hrQaTrack", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaCentrality{"hrQaCentrality", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaEvent{"hrQaEvent", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrQaRun{"hrQaRun", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry hrCounter{"hrCounter", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + void init(const InitContext&) + { + holderMember.doQaAcceptance = isEnabled(cgAnalysis.cfgFlagsQaAcceptance); + holderMember.doQaPhi = isEnabled(cgAnalysis.cfgFlagsQaPhi); + holderMember.doQaPid = isEnabled(cgAnalysis.cfgFlagsQaPid); + holderMember.doCalculationYield = isEnabled(cgAnalysis.cfgFlagsCalculationYield); + holderMember.doCalculationPurity = isEnabled(cgAnalysis.cfgFlagsCalculationPurity); + holderMember.doCalculationFractionPrimary = isEnabled(cgAnalysis.cfgFlagsCalculationFractionPrimary); + holderMember.doCalculationFluctuation = isEnabled(cgAnalysis.cfgFlagsCalculationFluctuation); + holderMember.doStorageMiniTable = isEnabled(cgAnalysis.cfgFlagsStorageMiniTable); + + if (doProcessRaw.value == doProcessMc.value) { + LOG(fatal) << "Identical values of doProcessRaw and doProcessMc!"; + } + if (doProcessMc.value) { LOG(info) << "Enabling MC data process."; + } else { + LOG(info) << "Enabling raw data process."; } + if (std::cmp_greater(nEnabled(cgAnalysis.cfgFlagsCalculationFluctuation), 1)) { + LOG(fatal) << "Invalid " << cgAnalysis.cfgFlagsCalculationFluctuation.name << "!"; + } + if (std::cmp_greater(nEnabled(cgAnalysis.cfgFlagsStorageMiniTable), 1)) { + LOG(fatal) << "Invalid " << cgAnalysis.cfgFlagsStorageMiniTable.name << "!"; + } + if ((cgAnalysis.cfgFlagQaEvent.value || cgAnalysis.cfgFlagQaCentrality.value || holderMember.doCalculationFluctuation) && std::cmp_less_equal(cgEvent.cfgNMultiplicityBinsAxis.value, 0)) { + LOG(fatal) << "Invalid " << cgEvent.cfgNMultiplicityBinsAxis.name << "!"; + } + if (holderMember.doCalculationFluctuation && std::cmp_less_equal(cgEvent.cfgNSubgroups.value, 0)) { + LOG(fatal) << "Invalid " << cgEvent.cfgNSubgroups.name << "!"; + } + + if (holderMember.doStorageMiniTable) { + holderMember.configSelection.emplace(); + for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { + holderMember.configSelection->cutsMinItsNCls[iSelection] = cgTrack.cfgCutsMinItsNCls.value.get(iSelection); + holderMember.configSelection->cutsMaxItsChi2NCls[iSelection] = cgTrack.cfgCutsMaxItsChi2NCls.value.get(iSelection); + holderMember.configSelection->cutsMaxTpcChi2NCls[iSelection] = cgTrack.cfgCutsMaxTpcChi2NCls.value.get(iSelection); + holderMember.configSelection->cutsMaxTpcNClsSharedRatio[iSelection] = cgTrack.cfgCutsMaxTpcNClsSharedRatio.value.get(iSelection); + holderMember.configSelection->cutsMinTpcNCrossedRows[iSelection] = cgTrack.cfgCutsMinTpcNCrossedRows.value.get(iSelection); + } + for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { + for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { + holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(std::string{getName(iDcaAxis)}.c_str(), std::string{getName(iSelection)}.c_str()); + } + } + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { + holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies][iSelection] = cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(std::string{getName(iParticleSpecies)}.c_str(), std::string{getName(iSelection)}.c_str()); + } + } - if (!groupEvent.cfgLabelFlagsRct.value.empty()) { - rctFlagsChecker.init(groupEvent.cfgLabelFlagsRct.value, false, true, true); + static constexpr auto ValidateCuts{ + [] + requires IsValidEnumValue + (const std::span>& cuts, const std::string& name) constexpr -> void { + for (std::int32_t const& iSelection : std::views::iota(0, NEs)) { + if (std::cmp_equal(iSelection, 0)) { + continue; + } + + if constexpr (RangeEdgeValue == RangeEdge::Min) { + if (!(cuts[iSelection - 1] < cuts[iSelection])) { + LOG(fatal) << "Values in " << name << " must satisfy " << getName(Selection::Loose) << " < " << getName(Selection::Default) << " < " << getName(Selection::Tight) << "!"; + } + } else { // RangeEdgeValue == RangeEdge::Max + if (!(cuts[iSelection - 1] > cuts[iSelection])) { + LOG(fatal) << "Values in " << name << " must satisfy " << getName(Selection::Loose) << " > " << getName(Selection::Default) << " > " << getName(Selection::Tight) << "!"; + } + } + } + }}; + + ValidateCuts.template operator()(holderMember.configSelection->cutsMinItsNCls, cgTrack.cfgCutsMinItsNCls.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxItsChi2NCls, cgTrack.cfgCutsMaxItsChi2NCls.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxTpcChi2NCls, cgTrack.cfgCutsMaxTpcChi2NCls.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxTpcNClsSharedRatio, cgTrack.cfgCutsMaxTpcNClsSharedRatio.name); + ValidateCuts.template operator()(holderMember.configSelection->cutsMinTpcNCrossedRows, cgTrack.cfgCutsMinTpcNCrossedRows.name); + for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxAbsNSigmaDca[iDcaAxis], cgTrack.cfgCutsMaxAbsNSigmaDca.name); + } + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + ValidateCuts.template operator()(holderMember.configSelection->cutsMaxAbsNSigmaPid[iParticleSpecies], cgTrack.cfgCutsMaxAbsNSigmaPid.name); + } + } + + if (holderMember.doStorageMiniTable || static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Charge)))) { + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + holderMember.rangePtAll[toI(RangeEdge::Min)] = std::min(holderMember.rangePtAll[toI(RangeEdge::Min)], cgTrack.cfgCutsRangePt.value.get(iParticleSpecies, toI(RangeEdge::Min))); + holderMember.rangePtAll[toI(RangeEdge::Max)] = std::max(holderMember.rangePtAll[toI(RangeEdge::Max)], cgTrack.cfgCutsRangePt.value.get(iParticleSpecies, toI(RangeEdge::Max))); + } } - ccdb->setURL(groupCcdb.cfgCcdbUrl.value); + rctFlagsChecker.init(cgEvent.cfgLabelFlagsRct.value, static_cast(cgEvent.cfgFlagsRct.value.get("ZDC")), static_cast(cgEvent.cfgFlagsRct.value.get("Acceptance")), static_cast(cgEvent.cfgFlagsRct.value.get("Table"))); + + ccdb->setURL(cgCcdb.cfgUrl.value); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(true); - if (groupCcdb.cfgCcdbTimestampLatest.value >= 0) { - ccdb->setCreatedNotAfter(groupCcdb.cfgCcdbTimestampLatest.value > 0 ? groupCcdb.cfgCcdbTimestampLatest.value : std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count()); - } - - const TList* const ccdbObject = ccdb->getForTimeStamp(groupCcdb.cfgCcdbPath.value, -1); - if (!ccdbObject || ccdbObject->IsA() != TList::Class()) { - LOG(fatal) << "Invalid ccdb_object!"; + if (std::cmp_greater_equal(cgCcdb.cfgTimestampLatest.value, 0)) { + ccdb->setCreatedNotAfter(cgCcdb.cfgTimestampLatest.value); } - const TGraph* const gRunNumberGroupIndex = dynamic_cast(ccdbObject->FindObject("gRunNumberGroupIndex")); - if (!gRunNumberGroupIndex || gRunNumberGroupIndex->IsA() != TGraph::Class()) { - LOG(fatal) << "Invalid gRunNumberGroupIndex!"; - } - holderCcdb.clear(); - std::int32_t nRunsBad = 0; - std::int32_t nRunGroups = 0; - for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndex->GetN())) { - const std::int32_t runGroupIndex = std::llrint(gRunNumberGroupIndex->GetY()[iRun]); - if (runGroupIndex == 0 || (groupEvent.cfgFlagRejectionRunBad.value && runGroupIndex < 0)) { - nRunsBad++; + readCcdb(); + std::int32_t nRunsBad{}; + for (const auto& [runNumber, indexRunAndGroup] : holderCcdb.runNumbersToIndicesRunAndGroup) { + const std::int32_t indexRunGroup{indexRunAndGroup.second}; + if (std::cmp_equal(indexRunGroup, 0) || (cgEvent.cfgFlagRejectionRunBad.value && std::cmp_less(indexRunGroup, 0))) { + ++nRunsBad; } - nRunGroups = std::max(nRunGroups, std::abs(runGroupIndex)); - holderCcdb.runNumbersIndicesGroupIndices[std::llrint(gRunNumberGroupIndex->GetX()[iRun])] = std::pair(iRun, runGroupIndex); } - if (holderCcdb.runNumbersIndicesGroupIndices.empty()) { + if (holderCcdb.runNumbersToIndicesRunAndGroup.empty()) { LOG(info) << "No run process enabled."; } else { - LOG(info) << "Number of runs: " << holderCcdb.runNumbersIndicesGroupIndices.size(); - for (const auto& [runNumber, runIndexGroupIndex] : holderCcdb.runNumbersIndicesGroupIndices) { - LOG(info) << "Enabling processing run: " << runNumber << " (" << std::abs(runIndexGroupIndex.second) << ")"; + LOG(info) << "Number of runs: " << holderCcdb.runNumbersToIndicesRunAndGroup.size(); + if (std::cmp_less_equal(nRunsBad, 0)) { + LOG(info) << "No run rejection enabled."; + } else { + LOG(info) << "Number of bad runs: " << nRunsBad; } - } - - if (nRunsBad <= 0) { - LOG(info) << "No run rejection enabled."; - } else { - LOG(info) << "Number of bad runs: " << nRunsBad; - for (const auto& [runNumber, runIndexGroupIndex] : holderCcdb.runNumbersIndicesGroupIndices) { - if (runIndexGroupIndex.second == 0 || (groupEvent.cfgFlagRejectionRunBad.value && runIndexGroupIndex.second < 0)) { - LOG(info) << "Enabling rejecting run: " << runNumber; + for (const auto& [runNumber, indexRunAndGroup] : holderCcdb.runNumbersToIndicesRunAndGroup) { + if (std::cmp_equal(indexRunAndGroup.second, 0) || (cgEvent.cfgFlagRejectionRunBad.value && std::cmp_less(indexRunAndGroup.second, 0))) { + LOG(info) << "Enabling rejecting run: " << runNumber << " (" << indexRunAndGroup.second << ")"; + } else { + LOG(info) << "Enabling processing run: " << runNumber << " (" << std::abs(indexRunAndGroup.second) << ")"; } } } - if (groupEvent.cfgLabelFlagsRct.value.empty()) { - LOG(info) << "No RCT flags enabled."; + if (cgEvent.cfgLabelFlagsRct.value.empty()) { + LOG(info) << "No RCT flags label enabled."; } else { - LOG(info) << "Enabling RCT flags label: " << groupEvent.cfgLabelFlagsRct.value; + LOG(info) << "Enabling RCT flags label: " << cgEvent.cfgLabelFlagsRct.value; + } + if (static_cast(cgEvent.cfgFlagsRct.value.get("ZDC"))) { + LOG(info) << "Enabling RCT flag: ZDC"; + } + if (static_cast(cgEvent.cfgFlagsRct.value.get("Acceptance"))) { + LOG(info) << "Enabling RCT flag: acceptance"; + } + if (static_cast(cgEvent.cfgFlagsRct.value.get("Table"))) { + LOG(info) << "Enabling RCT flag: table"; } - if ((groupEvent.cfgBitsSelectionEvent.value & ((1ULL << aod::evsel::EventSelectionFlags::kNsel) - 1)) == 0) { + if (std::cmp_equal(cgEvent.cfgBitsSelection.value & ((std::uint64_t{1} << aod::evsel::EventSelectionFlags::kNsel) - 1), 0)) { LOG(info) << "No event selection bit enabled."; } else { - for (std::int32_t const& iEvSel : std::views::iota(0, aod::evsel::EventSelectionFlags::kNsel)) { - if ((groupEvent.cfgBitsSelectionEvent.value >> iEvSel) & 1) { - LOG(info) << "Enabling event selection bit: " << aod::evsel::selectionLabels[iEvSel]; + for (std::int32_t const& iBit : std::views::iota(0, aod::evsel::EventSelectionFlags::kNsel)) { + if (static_cast((cgEvent.cfgBitsSelection.value >> iBit) & 1)) { + LOG(info) << "Enabling event selection bit: " << aod::evsel::selectionLabels[iBit]; } } } - switch (groupEvent.cfgIndexDefinitionCentrality.value) { - case static_cast(CentralityDefinition::kFt0a): + switch (cgEvent.cfgIndexDefinitionCentrality.value) { + case toI(CentralityDefinition::Ft0a): LOG(info) << "Enabling centrality definition: FT0A"; break; - case static_cast(CentralityDefinition::kFt0c): + case toI(CentralityDefinition::Ft0c): LOG(info) << "Enabling centrality definition: FT0C"; break; default: @@ -861,2429 +1263,1768 @@ struct PartNumFluc { break; } - const auto readListRunGroup = [&](const std::int32_t runGroupIndex) -> const TList* { - const std::string name = Form("lRunGroup_%d", runGroupIndex); - const TList* const lRunGroup = dynamic_cast(ccdbObject->FindObject(name.c_str())); - if (!lRunGroup || lRunGroup->IsA() != TList::Class()) { - LOG(fatal) << "Invalid " << name << "!"; - } - return lRunGroup; - }; - const auto readFormula = [&](const TList* const lRunGroup, const char* const nameBase, const bool doAddingSuffixMc, const std::int32_t runGroupIndex) -> const TFormula* { - const std::string name = Form("%s%s_runGroup%d", nameBase, doAddingSuffixMc ? "_mc" : "", runGroupIndex); - const TFormula* const formula = dynamic_cast(lRunGroup->FindObject(name.c_str())); - if (!formula || formula->IsA() != TFormula::Class()) { - LOG(fatal) << "Invalid " << name << "!"; - } - LOG(info) << "Reading from CCDB: " << formula->GetName() << " \"" << formula->GetExpFormula("clingp") << "\""; - return formula; - }; - const auto readH3 = [&](const TList* const lRunGroup, const char* const nameBase, const bool doAddingSuffixMc, const std::int32_t runGroupIndex) -> const TH3* { - const std::string name = Form("%s%s_runGroup%d", nameBase, doAddingSuffixMc ? "_mc" : "", runGroupIndex); - const TH3* const h3 = dynamic_cast(lRunGroup->FindObject(name.c_str())); - if (!h3 || !h3->InheritsFrom(TH3::Class())) { - LOG(fatal) << "Invalid " << name << "!"; - } - LOG(info) << "Reading from CCDB: " << h3->GetName(); - return h3; - }; - const auto readHn = [&](const TList* const lRunGroup, const char* const nameBase, const bool doAddingSuffixMc, const std::int32_t runGroupIndex, const std::int32_t nDimensions) -> const THnBase* { - const std::string name = Form("%s%s_runGroup%d", nameBase, doAddingSuffixMc ? "_mc" : "", runGroupIndex); - const THnBase* const hn = dynamic_cast(lRunGroup->FindObject(name.c_str())); - if (!hn || !hn->InheritsFrom(THnBase::Class()) || hn->GetNdimensions() != nDimensions) { - LOG(fatal) << "Invalid " << name << "!"; - } - LOG(info) << "Reading from CCDB: " << hn->GetName(); - return hn; - }; - - if (groupTrack.cfgFlagRecalibrationDca.value) { - LOG(info) << "Enabling DCA recalibration."; - - holderCcdb.fPtMeanDcaXyP.resize(nRunGroups); - holderCcdb.fPtMeanDcaXyM.resize(nRunGroups); - holderCcdb.fPtMeanDcaZP.resize(nRunGroups); - holderCcdb.fPtMeanDcaZM.resize(nRunGroups); - holderCcdb.fPtSigmaDcaXyP.resize(nRunGroups); - holderCcdb.fPtSigmaDcaXyM.resize(nRunGroups); - holderCcdb.fPtSigmaDcaZP.resize(nRunGroups); - holderCcdb.fPtSigmaDcaZM.resize(nRunGroups); - for (std::int32_t const& iRunGroup : std::views::iota(0, nRunGroups)) { - const TList* const lRunGroup = readListRunGroup(iRunGroup + 1); - holderCcdb.fPtMeanDcaXyP[iRunGroup] = readFormula(lRunGroup, "fPtMeanDcaXyP", doprocessMc.value, iRunGroup + 1); - holderCcdb.fPtMeanDcaXyM[iRunGroup] = readFormula(lRunGroup, "fPtMeanDcaXyM", doprocessMc.value, iRunGroup + 1); - holderCcdb.fPtMeanDcaZP[iRunGroup] = readFormula(lRunGroup, "fPtMeanDcaZP", doprocessMc.value, iRunGroup + 1); - holderCcdb.fPtMeanDcaZM[iRunGroup] = readFormula(lRunGroup, "fPtMeanDcaZM", doprocessMc.value, iRunGroup + 1); - holderCcdb.fPtSigmaDcaXyP[iRunGroup] = readFormula(lRunGroup, "fPtSigmaDcaXyP", doprocessMc.value, iRunGroup + 1); - holderCcdb.fPtSigmaDcaXyM[iRunGroup] = readFormula(lRunGroup, "fPtSigmaDcaXyM", doprocessMc.value, iRunGroup + 1); - holderCcdb.fPtSigmaDcaZP[iRunGroup] = readFormula(lRunGroup, "fPtSigmaDcaZP", doprocessMc.value, iRunGroup + 1); - holderCcdb.fPtSigmaDcaZM[iRunGroup] = readFormula(lRunGroup, "fPtSigmaDcaZM", doprocessMc.value, iRunGroup + 1); - } - } - - if (groupTrack.cfgFlagRecalibrationNSigmaPi.value || groupTrack.cfgFlagRecalibrationNSigmaKa.value || groupTrack.cfgFlagRecalibrationNSigmaPr.value) { - const auto readH3ShiftNSigmaPid = [&](std::initializer_list*, const char*>> pairsVectorH3NameBase) { - for (const auto& [vectorH3, nameBase] : pairsVectorH3NameBase) { - vectorH3->resize(nRunGroups); - } - for (std::int32_t const& iRunGroup : std::views::iota(0, nRunGroups)) { - const TList* const lRunGroup = readListRunGroup(iRunGroup + 1); - for (const auto& [vectorH3, nameBase] : pairsVectorH3NameBase) { - (*vectorH3)[iRunGroup] = readH3(lRunGroup, nameBase, doprocessMc.value, iRunGroup + 1); - } - } - }; - - if (groupTrack.cfgFlagRecalibrationNSigmaPi.value) { - LOG(info) << "Enabling nSigmaPi recalibration."; - - readH3ShiftNSigmaPid({{&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPiP, "hCentralityPtEtaShiftTpcNSigmaPiP"}, {&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPiM, "hCentralityPtEtaShiftTpcNSigmaPiM"}, {&holderCcdb.hCentralityPtEtaShiftTofNSigmaPiP, "hCentralityPtEtaShiftTofNSigmaPiP"}, {&holderCcdb.hCentralityPtEtaShiftTofNSigmaPiM, "hCentralityPtEtaShiftTofNSigmaPiM"}}); - } - - if (groupTrack.cfgFlagRecalibrationNSigmaKa.value) { - LOG(info) << "Enabling nSigmaKa recalibration."; - - readH3ShiftNSigmaPid({{&holderCcdb.hCentralityPtEtaShiftTpcNSigmaKaP, "hCentralityPtEtaShiftTpcNSigmaKaP"}, {&holderCcdb.hCentralityPtEtaShiftTpcNSigmaKaM, "hCentralityPtEtaShiftTpcNSigmaKaM"}, {&holderCcdb.hCentralityPtEtaShiftTofNSigmaKaP, "hCentralityPtEtaShiftTofNSigmaKaP"}, {&holderCcdb.hCentralityPtEtaShiftTofNSigmaKaM, "hCentralityPtEtaShiftTofNSigmaKaM"}}); - } - - if (groupTrack.cfgFlagRecalibrationNSigmaPr.value) { - LOG(info) << "Enabling nSigmaPr recalibration."; - - readH3ShiftNSigmaPid({{&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPrP, "hCentralityPtEtaShiftTpcNSigmaPrP"}, {&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPrM, "hCentralityPtEtaShiftTpcNSigmaPrM"}, {&holderCcdb.hCentralityPtEtaShiftTofNSigmaPrP, "hCentralityPtEtaShiftTofNSigmaPrP"}, {&holderCcdb.hCentralityPtEtaShiftTofNSigmaPrM, "hCentralityPtEtaShiftTofNSigmaPrM"}}); - } - } - - hrCounter.add("hNEvents", ";;No. of Events", {HistType::kTH1D, {{10 + aod::evsel::EventSelectionFlags::kNsel, -0.5, 9.5 + static_cast(aod::evsel::EventSelectionFlags::kNsel), "Selection"}}}); - if (doprocessMc.value) { - hrCounter.add("hNMcEvents", ";;No. of MC Events", {HistType::kTH1D, {{10, -0.5, 9.5, "Selection"}}}); + hrCounter.add("hNEvents", ";;No. of Events", {HistType::kTH1D, {{NEs + aod::evsel::EventSelectionFlags::kNsel, -0.5, static_cast(NEs + aod::evsel::EventSelectionFlags::kNsel) - 0.5, "Selection"}}}); + if (doProcessMc.value) { + hrCounter.add("hNEventsMc", ";;No. of MC Events", {HistType::kTH1D, {{NEs, -0.5, static_cast(NEs) - 0.5, "Selection"}}}); } - if (groupAnalysis.cfgFlagQaRun.value) { + if (cgAnalysis.cfgFlagQaRun.value) { LOG(info) << "Enabling run QA."; - const HistogramConfigSpec hcsQaRun(HistType::kTProfile, {{static_cast(holderCcdb.runNumbersIndicesGroupIndices.size()), -0.5, holderCcdb.runNumbersIndicesGroupIndices.size() - 0.5, "Run Index"}}); - - hrQaRun.add("QaRun/pRunIndexVx", ";;#LT#it{V}_{#it{x}}#GT (cm)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexVy", ";;#LT#it{V}_{#it{y}}#GT (cm)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexVz", ";;#LT#it{V}_{#it{z}}#GT (cm)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexMultiplicityFt0a", ";;FT0A #LTMultiplicity#GT", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexMultiplicityFt0c", ";;FT0C #LTMultiplicity#GT", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexCentralityFt0a", ";;FT0A #LTCentrality#GT", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexCentralityFt0c", ";;FT0C #LTCentrality#GT", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexCentralityFt0m", ";;FT0M #LTCentrality#GT", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexNGlobalTracks_p", ";;#LTnGlobalTracks#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexNGlobalTracks_m", ";;#LTnGlobalTracks#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexNPvContributors_p", ";;#LTnPvContributors#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexNPvContributors_m", ";;#LTnPvContributors#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexMeanDcaXy_p", ";;#LT#LTDCA_{#it{xy}}#GT_{event}#GT (cm) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexMeanDcaXy_m", ";;#LT#LTDCA_{#it{xy}}#GT_{event}#GT (cm) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexSigmaDcaXy_p", ";;#LT#it{#sigma}(DCA_{#it{xy}})_{event}#GT (cm) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexSigmaDcaXy_m", ";;#LT#it{#sigma}(DCA_{#it{xy}})_{event}#GT (cm) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexMeanDcaZ_p", ";;#LT#LTDCA_{#it{z}}#GT_{event}#GT (cm) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexMeanDcaZ_m", ";;#LT#LTDCA_{#it{z}}#GT_{event}#GT (cm) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexSigmaDcaZ_p", ";;#LT#it{#sigma}(DCA_{#it{z}})_{event}#GT (cm) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexSigmaDcaZ_m", ";;#LT#it{#sigma}(DCA_{#it{z}})_{event}#GT (cm) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexNTofBeta_p", ";;#LTnTofBeta#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexNTofBeta_m", ";;#LTnTofBeta#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexItsNCls_p", ";;ITS #LTnClusters#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexItsNCls_m", ";;ITS #LTnClusters#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexItsChi2NCls_p", ";;ITS #LT#it{#chi}^{2}/nClusters#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexItsChi2NCls_m", ";;ITS #LT#it{#chi}^{2}/nClusters#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNCls_p", ";;TPC #LTnClusters#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNCls_m", ";;TPC #LTnClusters#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcChi2NCls_p", ";;TPC #LT#it{#chi}^{2}/nClusters#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcChi2NCls_m", ";;TPC #LT#it{#chi}^{2}/nClusters#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNClsSharedRatio_p", ";;TPC #LTnSharedClusters/nClusters#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNClsSharedRatio_m", ";;TPC #LTnSharedClusters/nClusters#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNCrossedRows_p", ";;TPC #LTnCrossedRows#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNCrossedRows_m", ";;TPC #LTnCrossedRows#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNCrossedRowsRatio_p", ";;TPC #LTnCrossedRows/nFindableClusters#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNCrossedRowsRatio_m", ";;TPC #LTnCrossedRows/nFindableClusters#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexDcaXy_p", ";;#LTDCA_{#it{xy}}#GT (cm) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexDcaXy_m", ";;#LTDCA_{#it{xy}}#GT (cm) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexDcaZ_p", ";;#LTDCA_{#it{z}}#GT (cm) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexDcaZ_m", ";;#LTDCA_{#it{z}}#GT (cm) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexPt_p", ";;#LT#it{p}_{T}#GT (GeV/#it{c}) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexPt_m", ";;#LT#it{p}_{T}#GT (GeV/#it{c}) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexEta_p", ";;#LT#it{#eta}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexEta_m", ";;#LT#it{#eta}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexPhi_p", ";;#LT#it{#varphi}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexPhi_m", ";;#LT#it{#varphi}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcDeDx_p", ";;TPC #LTd#it{E}/d#it{x}#GT (a.u.) (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcDeDx_m", ";;TPC #LTd#it{E}/d#it{x}#GT (a.u.) (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNSigmaPi_p", ";;TPC #LT#it{n}#it{#sigma}_{#pi}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNSigmaPi_m", ";;TPC #LT#it{n}#it{#sigma}_{#pi}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNSigmaKa_p", ";;TPC #LT#it{n}#it{#sigma}_{K}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNSigmaKa_m", ";;TPC #LT#it{n}#it{#sigma}_{K}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNSigmaPr_p", ";;TPC #LT#it{n}#it{#sigma}_{p}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTpcNSigmaPr_m", ";;TPC #LT#it{n}#it{#sigma}_{p}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofInverseBeta_p", ";;TOF #LT1/#it{#beta}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofInverseBeta_m", ";;TOF #LT1/#it{#beta}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofNSigmaPi_p", ";;TOF #LT#it{n}#it{#sigma}_{#pi}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofNSigmaPi_m", ";;TOF #LT#it{n}#it{#sigma}_{#pi}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofNSigmaKa_p", ";;TOF #LT#it{n}#it{#sigma}_{K}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofNSigmaKa_m", ";;TOF #LT#it{n}#it{#sigma}_{K}#GT (#it{q}<0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofNSigmaPr_p", ";;TOF #LT#it{n}#it{#sigma}_{p}#GT (#it{q}>0)", hcsQaRun); - hrQaRun.add("QaRun/pRunIndexTofNSigmaPr_m", ";;TOF #LT#it{n}#it{#sigma}_{p}#GT (#it{q}<0)", hcsQaRun); - } - - if (groupAnalysis.cfgFlagQaEvent.value) { + const HistogramConfigSpec hcsQaRun{HistType::kTProfile, {{static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()), -0.5, static_cast(holderCcdb.runNumbersToIndicesRunAndGroup.size()) - 0.5, "Run Index"}}}; + + for (const auto& [name, title, isChargeSpeciesSeparated] : std::to_array>( + {{"Vx", "#LT#it{V}_{#it{x}}#GT (cm)", false}, + {"Vy", "#LT#it{V}_{#it{y}}#GT (cm)", false}, + {"Vz", "#LT#it{V}_{#it{z}}#GT (cm)", false}, + {"MultiplicityFt0a", "FT0A #LTMultiplicity#GT", false}, + {"MultiplicityFt0c", "FT0C #LTMultiplicity#GT", false}, + {"CentralityNtpv", "NTPV #LTCentrality#GT", false}, + {"CentralityFt0a", "FT0A #LTCentrality#GT", false}, + {"CentralityFt0c", "FT0C #LTCentrality#GT", false}, + {"CentralityFt0m", "FT0M #LTCentrality#GT", false}, + {"NGlobalTracks", "#LTnGlobalTracks#GT", true}, + {"NPvContributors", "#LTnPvContributors#GT", true}, + {std::format("{}Dca{}", getName(DcaMeasure::Mean), getName(DcaAxis::Xy)), "#LT#LTDCA_{#it{xy}}#GT_{event}#GT (cm)", true}, + {std::format("{}Dca{}", getName(DcaMeasure::Sigma), getName(DcaAxis::Xy)), "#LT#it{#sigma}(DCA_{#it{xy}})_{event}#GT (cm)", true}, + {std::format("{}Dca{}", getName(DcaMeasure::Mean), getName(DcaAxis::Z)), "#LT#LTDCA_{#it{z}}#GT_{event}#GT (cm)", true}, + {std::format("{}Dca{}", getName(DcaMeasure::Sigma), getName(DcaAxis::Z)), "#LT#it{#sigma}(DCA_{#it{z}})_{event}#GT (cm)", true}, + {"NTofBeta", "#LTnTofBeta#GT", true}, + {"ItsNCls", "ITS #LTnClusters#GT", true}, + {"ItsChi2NCls", "ITS #LT#it{#chi}^{2}/nClusters#GT", true}, + {"TpcNCls", "TPC #LTnClusters#GT", true}, + {"TpcChi2NCls", "TPC #LT#it{#chi}^{2}/nClusters#GT", true}, + {"TpcNClsSharedRatio", "TPC #LTnSharedClusters/nClusters#GT", true}, + {"TpcNClsRatio", "TPC #LTnClusters/nFindableClusters#GT", true}, + {"TpcNCrossedRows", "TPC #LTnCrossedRows#GT", true}, + {"TpcNCrossedRowsRatio", "TPC #LTnCrossedRows/nFindableClusters#GT", true}, + {"Pt", "#LT#it{p}_{T}#GT (GeV/#it{c})", true}, + {"Eta", "#LT#it{#eta}#GT", true}, + {"Phi", "#LT#it{#varphi}#GT", true}, + {"TpcDeDx", "TPC #LTd#it{E}/d#it{x}#GT (a.u.)", true}, + {std::format("{}NSigma{}", getName(Detector::Tpc), getName(ParticleSpecies::Pion)), std::format("{} #LT#it{{n}}#it{{#sigma}}_{{#pi}}#GT", getName(Detector::Tpc)), true}, + {std::format("{}NSigma{}", getName(Detector::Tpc), getName(ParticleSpecies::Kaon)), std::format("{} #LT#it{{n}}#it{{#sigma}}_{{K}}#GT", getName(Detector::Tpc)), true}, + {std::format("{}NSigma{}", getName(Detector::Tpc), getName(ParticleSpecies::Proton)), std::format("{} #LT#it{{n}}#it{{#sigma}}_{{p}}#GT", getName(Detector::Tpc)), true}, + {"TofInverseBeta", "TOF #LT1/#it{#beta}#GT", true}, + {std::format("{}NSigma{}", getName(Detector::Tof), getName(ParticleSpecies::Pion)), std::format("{} #LT#it{{n}}#it{{#sigma}}_{{#pi}}#GT", getName(Detector::Tof)), true}, + {std::format("{}NSigma{}", getName(Detector::Tof), getName(ParticleSpecies::Kaon)), std::format("{} #LT#it{{n}}#it{{#sigma}}_{{K}}#GT", getName(Detector::Tof)), true}, + {std::format("{}NSigma{}", getName(Detector::Tof), getName(ParticleSpecies::Proton)), std::format("{} #LT#it{{n}}#it{{#sigma}}_{{p}}#GT", getName(Detector::Tof)), true}})) { + if (!isChargeSpeciesSeparated) { + hrQaRun.add(std::format("pRunIndex{}", name).c_str(), std::format(";;{}", title).c_str(), hcsQaRun); + } else { + hrQaRun.add(std::format("pRunIndex{}_{}", name, getName(ChargeSpecies::Plus)).c_str(), std::format(";;{} (#it{{q}}>0)", title).c_str(), hcsQaRun); + hrQaRun.add(std::format("pRunIndex{}_{}", name, getName(ChargeSpecies::Minus)).c_str(), std::format(";;{} (#it{{q}}<0)", title).c_str(), hcsQaRun); + } + } + } + + if (cgAnalysis.cfgFlagQaEvent.value) { LOG(info) << "Enabling event QA."; - const AxisSpec asNTracks(200, -0.5, 199.5); - const HistogramConfigSpec hcsQaEvent(HistType::kTHnSparseD, {asNTracks, asNTracks}); + const AxisSpec asMultiplicity{cgEvent.cfgNMultiplicityBinsAxis.value, -0.5, cgEvent.cfgNMultiplicityBinsAxis.value - 0.5}; + const HistogramConfigSpec hcsQaEvent{HistType::kTHnSparseD, {asMultiplicity, asMultiplicity}}; - hrQaEvent.add("QaEvent/hVxVy", "", {HistType::kTHnSparseD, {{150, -0.15, 0.15, "#it{V}_{#it{x}} (cm)"}, {150, -0.15, 0.15, "#it{V}_{#it{y}} (cm)"}}}); - hrQaEvent.add("QaEvent/hVz", "", {HistType::kTH1D, {{300, -15., 15., "#it{V}_{#it{z}} (cm)"}}}); - hrQaEvent.add("QaEvent/hNPvContributorsNGlobalTracks", ";nPvContributors;nGlobalTracks;", hcsQaEvent); - hrQaEvent.add("QaEvent/hNGlobalTracksMeanDcaXy", ";nGlobalTracks;", {HistType::kTHnSparseD, {asNTracks, {250, -0.25, 0.25, "#LTDCA_{#it{xy}}#GT_{event} (cm)"}}}); - hrQaEvent.add("QaEvent/hNGlobalTracksMeanDcaXy_nPvContributorsCut", ";nGlobalTracks;", {HistType::kTHnSparseD, {asNTracks, {250, -0.25, 0.25, "#LTDCA_{#it{z}}#GT_{event} (cm)"}}}); - hrQaEvent.add("QaEvent/hNGlobalTracksMeanDcaZ", ";nGlobalTracks;", {HistType::kTHnSparseD, {asNTracks, {200, -2., 2., "#LTDCA_{#it{z}}#GT_{event} (cm)"}}}); - hrQaEvent.add("QaEvent/hNGlobalTracksMeanDcaZ_nPvContributorsCut", ";nGlobalTracks;", {HistType::kTHnSparseD, {asNTracks, {200, -2., 2., "#LTDCA_{#it{z}}#GT_{event} (cm)"}}}); - hrQaEvent.add("QaEvent/hNTofBetaNGlobalTracks", ";nTofBeta;nGlobalTracks;", hcsQaEvent); - hrQaEvent.add("QaEvent/hNTofBetaNGlobalTracks_nPvContributorsCut", ";nTofBeta;nGlobalTracks;", hcsQaEvent); + hrQaEvent.add("hVxVy", "", {HistType::kTHnSparseD, {{150, -0.15, 0.15, "#it{V}_{#it{x}} (cm)"}, {150, -0.15, 0.15, "#it{V}_{#it{y}} (cm)"}}}); + hrQaEvent.add("hVz", "", {HistType::kTH1D, {{300, -15., 15., "#it{V}_{#it{z}} (cm)"}}}); + hrQaEvent.add("hNPvContributorsNGlobalTracks", ";nPvContributors;nGlobalTracks;", hcsQaEvent); + hrQaEvent.add("hNGlobalTracksMeanDcaXy", ";nGlobalTracks;", {HistType::kTHnSparseD, {asMultiplicity, {250, -0.25, 0.25, "#LTDCA_{#it{xy}}#GT_{event} (cm)"}}}); + hrQaEvent.add("hNGlobalTracksMeanDcaXy_nPvContributorsCut", ";nGlobalTracks;", {HistType::kTHnSparseD, {asMultiplicity, {250, -0.25, 0.25, "#LTDCA_{#it{xy}}#GT_{event} (cm)"}}}); + hrQaEvent.add("hNGlobalTracksMeanDcaZ", ";nGlobalTracks;", {HistType::kTHnSparseD, {asMultiplicity, {200, -2., 2., "#LTDCA_{#it{z}}#GT_{event} (cm)"}}}); + hrQaEvent.add("hNGlobalTracksMeanDcaZ_nPvContributorsCut", ";nGlobalTracks;", {HistType::kTHnSparseD, {asMultiplicity, {200, -2., 2., "#LTDCA_{#it{z}}#GT_{event} (cm)"}}}); + hrQaEvent.add("hNTofBetaNGlobalTracks", ";nTofBeta;nGlobalTracks;", hcsQaEvent); + hrQaEvent.add("hNTofBetaNGlobalTracks_nPvContributorsCut", ";nTofBeta;nGlobalTracks;", hcsQaEvent); } - if (groupAnalysis.cfgFlagQaCentrality.value) { + if (cgAnalysis.cfgFlagQaCentrality.value) { LOG(info) << "Enabling centrality QA."; - const AxisSpec asCentrality(20, 0., 100., "Centrality (%)"); - - hrQaCentrality.add("QaCentrality/hCentralityFt0a", "", {HistType::kTHnSparseF, {asCentrality, {12000, 0., 12000., "FT0A Multiplicity"}}}); - hrQaCentrality.add("QaCentrality/hCentralityFt0c", "", {HistType::kTHnSparseF, {asCentrality, {3000, 0., 3000., "FT0C Multiplicity"}}}); - hrQaCentrality.add("QaCentrality/hCentralityFt0m", "", {HistType::kTHnSparseF, {asCentrality, {15000, 0., 15000., "FT0M Multiplicity"}}}); + hrQaCentrality.add("hCentralitySelection", "", {HistType::kTHnSparseD, {{100, 0., 100., "Centrality (%)"}, {NEs + aod::evsel::EventSelectionFlags::kNsel, -0.5, static_cast(NEs + aod::evsel::EventSelectionFlags::kNsel) - 0.5, "Selection"}}}); + hrQaCentrality.add("hCentralityMultiplicity", "", {HistType::kTHnSparseD, {{100, 0., 100., "Centrality (%)"}, {cgEvent.cfgNMultiplicityBinsAxis.value, -0.5, cgEvent.cfgNMultiplicityBinsAxis.value - 0.5, "Multiplicity"}}}); } - if (groupAnalysis.cfgFlagQaTrack.value) { + if (cgAnalysis.cfgFlagQaTrack.value) { LOG(info) << "Enabling track QA."; - hrQaTrack.add("QaTrack/hItsNCls_p", "", {HistType::kTH1D, {{10, -0.5, 9.5, "ITS nClusters"}}}); - hrQaTrack.add("QaTrack/hItsNCls_m", "", {HistType::kTH1D, {{10, -0.5, 9.5, "ITS nClusters"}}}); - hrQaTrack.add("QaTrack/hItsChi2NCls_p", "", {HistType::kTH1D, {{80, 0., 40., "ITS #it{#chi}^{2}/nClusters"}}}); - hrQaTrack.add("QaTrack/hItsChi2NCls_m", "", {HistType::kTH1D, {{80, 0., 40., "ITS #it{#chi}^{2}/nClusters"}}}); - hrQaTrack.add("QaTrack/hTpcNClsNClsShared_p", "", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {180, -0.5, 179.5, "TPC nSharedClusters"}}}); - hrQaTrack.add("QaTrack/hTpcNClsNClsShared_m", "", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {180, -0.5, 179.5, "TPC nSharedClusters"}}}); - hrQaTrack.add("QaTrack/hTpcChi2NCls_p", "", {HistType::kTH1D, {{100, 0., 5., "TPC #it{#chi}^{2}/nClusters"}}}); - hrQaTrack.add("QaTrack/hTpcChi2NCls_m", "", {HistType::kTH1D, {{100, 0., 5., "TPC #it{#chi}^{2}/nClusters"}}}); - hrQaTrack.add("QaTrack/hTpcNClsFindableNCrossedRows_p", "", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nFindableClusters"}, {180, -0.5, 179.5, "TPC nCrossedRows"}}}); - hrQaTrack.add("QaTrack/hTpcNClsFindableNCrossedRows_m", "", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nFindableClusters"}, {180, -0.5, 179.5, "TPC nCrossedRows"}}}); + for (const auto& [name, hcs] : std::to_array>( + {{"ItsNClsChi2NCls", {HistType::kTHnSparseD, {{10, -0.5, 9.5, "ITS nClusters"}, {80, 0., 40., "ITS #it{#chi}^{2}/nClusters"}}}}, + {"TpcNClsChi2NCls", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {100, 0., 5., "TPC #it{#chi}^{2}/nClusters"}}}}, + {"TpcNClsNClsShared", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {180, -0.5, 179.5, "TPC nSharedClusters"}}}}, + {"TpcNClsNClsFindableNCrossedRows", {HistType::kTHnSparseD, {{180, -0.5, 179.5, "TPC nClusters"}, {180, -0.5, 179.5, "TPC nFindableClusters"}, {180, -0.5, 179.5, "TPC nCrossedRows"}}}}})) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaTrack.add(std::format("h{}_{}", name, getName(iChargeSpecies)).c_str(), "", hcs); + } + } } - if (groupAnalysis.cfgFlagQaDca.value) { + if (cgAnalysis.cfgFlagQaDca.value) { LOG(info) << "Enabling DCA QA."; - const AxisSpec asPt(200, 0., 2., "#it{p}_{T} (GeV/#it{c})"); + const AxisSpec asPt{40, 0., 2., "#it{p}_{T} (GeV/#it{c})"}; + const HistogramConfigSpec hcsQaDcaProfile{HistType::kTProfile3D, {{cgEvent.cfgAxisCentralityCalibration, "Centrality (%)"}, asPt, {24, -1.2, 1.2, "#it{#eta}"}}}; - hrQaDca.add("QaDca/hPtDcaXy_p", "", {HistType::kTHnSparseD, {asPt, {500, -0.5, 0.5, "DCA_{#it{xy}} (cm)"}}}); - hrQaDca.add("QaDca/hPtDcaXy_m", "", {HistType::kTHnSparseD, {asPt, {500, -0.5, 0.5, "DCA_{#it{xy}} (cm)"}}}); - hrQaDca.add("QaDca/pPtDcaXy_p", ";;#LTDCA_{#it{xy}}#GT (cm)", {HistType::kTProfile, {asPt}}); - hrQaDca.add("QaDca/pPtDcaXy_m", ";;#LTDCA_{#it{xy}}#GT (cm)", {HistType::kTProfile, {asPt}}); - hrQaDca.add("QaDca/hPtDcaZ_p", "", {HistType::kTHnSparseD, {asPt, {500, -1., 1., "DCA_{#it{z}} (cm)"}}}); - hrQaDca.add("QaDca/hPtDcaZ_m", "", {HistType::kTHnSparseD, {asPt, {500, -1., 1., "DCA_{#it{z}} (cm)"}}}); - hrQaDca.add("QaDca/pPtDcaZ_p", ";;#LTDCA_{#it{z}}#GT (cm)", {HistType::kTProfile, {asPt}}); - hrQaDca.add("QaDca/pPtDcaZ_m", ";;#LTDCA_{#it{z}}#GT (cm)", {HistType::kTProfile, {asPt}}); + for (const auto& [name, title, hcs] : std::to_array>( + {{"hPtDcaXy", "", {HistType::kTHnSparseD, {asPt, {250, -0.25, 0.25, "DCA_{#it{xy}} (cm)"}}}}, + {"pCentralityPtEtaDcaXy", ";;#LTDCA_{#it{xy}}#GT (cm)", hcsQaDcaProfile}, + {"hPtDcaZ", "", {HistType::kTHnSparseD, {asPt, {250, -0.5, 0.5, "DCA_{#it{z}} (cm)"}}}}, + {"pCentralityPtEtaDcaZ", ";;#LTDCA_{#it{z}}#GT (cm)", hcsQaDcaProfile}})) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaDca.add(std::format("{}_{}", name, getName(iChargeSpecies)).c_str(), std::string{title}.c_str(), hcs); + } + } } - if (groupAnalysis.cfgFlagQaAcceptance.value || groupAnalysis.cfgFlagQaAcceptancePi.value || groupAnalysis.cfgFlagQaAcceptanceKa.value || groupAnalysis.cfgFlagQaAcceptancePr.value) { - const AxisSpec asPt(250, 0., 2.5, "#it{p}_{T} (GeV/#it{c})"); - - if (groupAnalysis.cfgFlagQaAcceptance.value) { - LOG(info) << "Enabling acceptance QA."; - - const HistogramConfigSpec hcsQaAcceptance(HistType::kTHnSparseD, {{300, -1.5, 1.5, "#it{#eta}"}, asPt}); - - hrQaAcceptance.add("QaAcceptance/hEtaPt_tpcEdgeP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hEtaPt_tpcEdgeM", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hEtaPt_tpcTofEdgeP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hEtaPt_tpcTofEdgeM", "", hcsQaAcceptance); + for (std::int32_t const& iParticleSpeciesAll : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsQaAcceptance.value.get(iParticleSpeciesAll))) { + continue; } - if (groupAnalysis.cfgFlagQaAcceptancePi.value || groupAnalysis.cfgFlagQaAcceptanceKa.value || groupAnalysis.cfgFlagQaAcceptancePr.value) { - const HistogramConfigSpec hcsQaAcceptance(HistType::kTHnSparseD, {{300, -1.5, 1.5, "#it{y}"}, asPt}); + LOG(info) << "Enabling " << getName(iParticleSpeciesAll) << " acceptance QA."; - if (groupAnalysis.cfgFlagQaAcceptancePi.value) { - LOG(info) << "Enabling pion acceptance QA."; - - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcEdgePiP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcEdgePiM", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcTofEdgePiP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcTofEdgePiM", "", hcsQaAcceptance); + for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaAcceptance.add(std::format("hEta{}Pt{}_{}Edge{}{}", doProcessMc.value ? "Mc" : "", doProcessMc.value ? "Mc" : "", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, doProcessMc.value ? "#it{#eta}_{Gen}" : "#it{#eta}"}, {250, 0., 2.5, doProcessMc.value ? "#it{p}_{T}^{Gen} (GeV/#it{c})" : "#it{p}_{T} (GeV/#it{c})"}}}); + if (!std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All))) { + hrQaAcceptance.add(std::format("hRapidity{}Pt{}_{}Edge{}{}", doProcessMc.value ? "Mc" : "", doProcessMc.value ? "Mc" : "", getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", {HistType::kTHnSparseD, {{300, -1.5, 1.5, doProcessMc.value ? "#it{y}_{Gen}" : "#it{y}"}, {250, 0., 2.5, doProcessMc.value ? "#it{p}_{T}^{Gen} (GeV/#it{c})" : "#it{p}_{T} (GeV/#it{c})"}}}); + } } + } + } - if (groupAnalysis.cfgFlagQaAcceptanceKa.value) { - LOG(info) << "Enabling kaon acceptance QA."; + for (std::int32_t const& iParticleSpeciesAll : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsQaPhi.value.get(iParticleSpeciesAll))) { + continue; + } - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcEdgeKaP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcEdgeKaM", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcTofEdgeKaP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcTofEdgeKaM", "", hcsQaAcceptance); - } + LOG(info) << "Enabling " << getName(iParticleSpeciesAll) << " phi QA."; - if (groupAnalysis.cfgFlagQaAcceptancePr.value) { - LOG(info) << "Enabling (anti)proton acceptance QA."; + const HistogramConfigSpec hcsQaPhi{HistType::kTHnSparseF, {{cgEvent.cfgAxisCentralityCalibration, "Centrality (%)"}, {20, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {24, -1.2, 1.2, "#it{#eta}"}, {360, 0., constants::math::TwoPI, "#it{#varphi} (rad)"}}}; - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcEdgePrP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcEdgePrM", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcTofEdgePrP", "", hcsQaAcceptance); - hrQaAcceptance.add("QaAcceptance/hRapidityPt_tpcTofEdgePrM", "", hcsQaAcceptance); + for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaPhi.add(std::format("hCentralityPtEtaPhi_{}{}{}{}", doProcessMc.value ? "mc" : "", doProcessMc.value ? getName(iPidStrategy) : getName(iPidStrategy), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), "", hcsQaPhi); } } } - if (groupAnalysis.cfgFlagQaPhi.value || groupAnalysis.cfgFlagQaPhiPi.value || groupAnalysis.cfgFlagQaPhiKa.value || groupAnalysis.cfgFlagQaPhiPr.value) { - const HistogramConfigSpec hcsQaPhi(HistType::kTHnSparseF, {{{0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90.}, "Centrality (%)"}, {20, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {16, -0.8, 0.8, "#it{#eta}"}, {360, 0., constants::math::TwoPI, "#it{#varphi} (rad)"}}); - - if (groupAnalysis.cfgFlagQaPhi.value) { - LOG(info) << "Enabling phi QA."; - - if (doprocessMc.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofM", "", hcsQaPhi); - } else if (doprocessRaw.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofM", "", hcsQaPhi); - } + for (std::int32_t const& iParticleSpeciesAll : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsQaPid.value.get(iParticleSpeciesAll))) { + continue; } - if (groupAnalysis.cfgFlagQaPhiPi.value) { - LOG(info) << "Enabling pion phi QA."; - - if (doprocessMc.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcPiP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcPiM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcPiP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcPiM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPiP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPiM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcPiP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcPiM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofPiP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofPiM", "", hcsQaPhi); - } else if (doprocessRaw.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcPiP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcPiM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofPiP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofPiM", "", hcsQaPhi); - } - } + LOG(info) << "Enabling " << getName(iParticleSpeciesAll) << " PID QA."; - if (groupAnalysis.cfgFlagQaPhiKa.value) { - LOG(info) << "Enabling kaon phi QA."; - - if (doprocessMc.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcKaP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcKaM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcKaP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcKaM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofKaP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofKaM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcKaP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcKaM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofKaP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofKaM", "", hcsQaPhi); - } else if (doprocessRaw.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcKaP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcKaM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofKaP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofKaM", "", hcsQaPhi); - } - } + const AxisSpec asCentrality{cgEvent.cfgAxisCentralityCalibration, "Centrality (%)"}; + + if (std::cmp_equal(iParticleSpeciesAll, toI(ParticleSpeciesAll::All))) { + const AxisSpec asPOverQ{350, -3.5, 3.5, "#it{p}/#it{q} (GeV/#it{c})"}; + const AxisSpec asEta{48, -1.2, 1.2, "#it{#eta}"}; - if (groupAnalysis.cfgFlagQaPhiPr.value) { - LOG(info) << "Enabling (anti)proton phi QA."; - - if (doprocessMc.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcPrP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcPrM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcPrP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcPrM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPrP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPrM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcPrP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcPrM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofPrP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_mcTpcTofPrM", "", hcsQaPhi); - } else if (doprocessRaw.value) { - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcPrP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcPrM", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofPrP", "", hcsQaPhi); - hrQaPhi.add("QaPhi/hCentralityPtEtaPhi_tpcTofPrM", "", hcsQaPhi); + hrQaPid.add("hCentralityPOverQEtaTpcLnDeDx", "", {HistType::kTHnSparseF, {asCentrality, asPOverQ, asEta, {240, 3., 9., "TPC ln(d#it{E}/d#it{x} (a.u.))"}}}); + hrQaPid.add("hCentralityPOverQEtaTofInverseBeta", "", {HistType::kTHnSparseF, {asCentrality, asPOverQ, asEta, {120, 0.5, 3.5, "TOF 1/#it{#beta}"}}}); + } else { + const HistogramConfigSpec hcsQaPid{HistType::kTHnSparseF, {asCentrality, {40, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {32, -0.8, 0.8, "#it{#eta}"}, {300, -30., 30.}}}; + + if (doProcessMc.value) { + for (std::int32_t const& iDetector : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaPid.add(std::format("hCentralityPtEta{}NSigma{}_mc{}{}", getName(iDetector), getName(iParticleSpeciesAll), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), std::format(";;;;{} #it{{n}}#it{{#sigma}}_{{{}}};", getName(iDetector), getTitle(iParticleSpeciesAll)).c_str(), hcsQaPid); + } + } + } else { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaPid.add(std::format("hCentralityPtEta{}NSigma{}_{}", getName(Detector::Tpc), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), std::format(";;;;{} #it{{n}}#it{{#sigma}}_{{{}}};", getName(Detector::Tpc), getTitle(iParticleSpeciesAll)).c_str(), hcsQaPid); + } + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaPid.add(std::format("hCentralityPtEta{}NSigma{}_{}{}{}", getName(Detector::Tpc), getName(iParticleSpeciesAll), getName(Detector::Tof), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), std::format(";;;;{} #it{{n}}#it{{#sigma}}_{{{}}};", getName(Detector::Tpc), getTitle(iParticleSpeciesAll)).c_str(), hcsQaPid); + } + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaPid.add(std::format("hCentralityPtEta{}NSigma{}_{}{}{}", getName(Detector::Tof), getName(iParticleSpeciesAll), getName(Detector::Tpc), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), std::format(";;;;{} #it{{n}}#it{{#sigma}}_{{{}}};", getName(Detector::Tof), getTitle(iParticleSpeciesAll)).c_str(), hcsQaPid); + } + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrQaPid.add(std::format("hCentralityPtEta{}NSigma{}_{}", getName(PidStrategy::TpcTof), getName(iParticleSpeciesAll), getName(iChargeSpecies)).c_str(), std::format(";;;;{} #it{{n}}#it{{#sigma}}_{{{}}};", getName(PidStrategy::TpcTof), getTitle(iParticleSpeciesAll)).c_str(), hcsQaPid); + } } } } - if (groupAnalysis.cfgFlagQaPid.value || groupAnalysis.cfgFlagQaPidPi.value || groupAnalysis.cfgFlagQaPidKa.value || groupAnalysis.cfgFlagQaPidPr.value) { - const AxisSpec asCentrality({0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90.}, "Centrality (%)"); + if (doProcessMc.value && cgAnalysis.cfgFlagQaMc.value) { + LOG(info) << "Enabling MC QA."; - if (groupAnalysis.cfgFlagQaPid.value) { - LOG(info) << "Enabling PID QA."; + const double maxAbsVz{std::ceil(cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value)}; + const AxisSpec asCentrality{20, 0., 100., "Centrality (%)"}; - const AxisSpec asPOverQ(350, -3.5, 3.5, "#it{p}/#it{q} (GeV/#it{c})"); - const AxisSpec asEta(48, -1.2, 1.2, "#it{#eta}"); + hrQaMc.add("hNCollisionsPerMcCollision", "", {HistType::kTH1D, {{20, -0.5, 19.5, "No. of collisions per MC collision"}}}); + hrQaMc.add("hCentralityNTracksPerMcParticle", "", {HistType::kTHnSparseF, {asCentrality, {20, -0.5, 19.5, "No. of tracks per MC particle"}}}); + hrQaMc.add("hCentralityVzMcDeltaVz", "", {HistType::kTHnSparseF, {asCentrality, {static_cast(maxAbsVz) * 20, -maxAbsVz, maxAbsVz, "#it{V}_{#it{z}}^{Gen} (cm)"}, {200, -0.2, 0.2, "#it{V}_{#it{z}}^{Rec}#minus#it{V}_{#it{z}}^{Gen} (cm)"}}}); + hrQaMc.add("hCentralityPtMcEtaMcDeltaPt", "", {HistType::kTHnSparseF, {asCentrality, {200, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {24, -1.2, 1.2, "#it{#eta}_{Gen}"}, {320, -0.8, 0.8, "#it{p}_{T}^{Rec}#minus#it{p}_{T}^{Gen} (GeV/#it{c})"}}}); + hrQaMc.add("hCentralityPtMcEtaMcDeltaEta", "", {HistType::kTHnSparseF, {asCentrality, {20, 0., 2., "#it{p}_{T}^{Gen} (GeV/#it{c})"}, {240, -1.2, 1.2, "#it{#eta}_{Gen}"}, {160, -0.4, 0.4, "#it{#eta}_{Rec}#minus#it{#eta}_{Gen}"}}}); + } - hrQaPid.add("QaPid/hCentralityPOverQEtaTpcLnDeDx", "", {HistType::kTHnSparseF, {asCentrality, asPOverQ, asEta, {240, 3., 9., "TPC ln(d#it{E}/d#it{x} (a.u.))"}}}); - hrQaPid.add("QaPid/hCentralityPOverQEtaTofInverseBeta", "", {HistType::kTHnSparseF, {asCentrality, asPOverQ, asEta, {120, 0.5, 3.5, "TOF 1/#it{#beta}"}}}); + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsCalculationYield.value.get(iParticleSpecies))) { + continue; } + LOG(info) << "Enabling " << getName(iParticleSpecies) << " yield calculation."; - if (groupAnalysis.cfgFlagQaPidPi.value || groupAnalysis.cfgFlagQaPidKa.value || groupAnalysis.cfgFlagQaPidPr.value) { - const HistogramConfigSpec hcsQaPid(HistType::kTHnSparseF, {asCentrality, {40, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {32, -0.8, 0.8, "#it{#eta}"}, {300, -30., 30.}}); + const double maxAbsVz{std::ceil(doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value)}; + const HistogramConfigSpec hcsCalculationYield{HistType::kTHnSparseF, {{static_cast(maxAbsVz) * 2, -maxAbsVz, maxAbsVz, "#it{V}_{#it{z}} (cm)"}, {cgEvent.cfgAxisCentralityCalibration, "Centrality (%)"}, {40, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {32, -0.8, 0.8, "#it{#eta}"}}}; - if (groupAnalysis.cfgFlagQaPidPi.value) { - LOG(info) << "Enabling pion PID QA."; - - if (doprocessMc.value) { - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPi_mcPiP", ";;;;TPC #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPi_mcPiM", ";;;;TPC #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPi_mcPiP", ";;;;TOF #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPi_mcPiM", ";;;;TOF #it{n}#it{#sigma}_{#pi};", hcsQaPid); - } else if (doprocessRaw.value) { - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPi_p", ";;;;TPC #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPi_m", ";;;;TPC #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPi_tofPiP", ";;;;TPC #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPi_tofPiM", ";;;;TPC #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPi_tpcPiP", ";;;;TOF #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPi_tpcPiM", ";;;;TOF #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcTofNSigmaPi_p", ";;;;TPC-TOF #it{n}#it{#sigma}_{#pi};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcTofNSigmaPi_m", ";;;;TPC-TOF #it{n}#it{#sigma}_{#pi};", hcsQaPid); - } + if (doProcessMc.value) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrCalculationYield.add(std::format("hVzCentralityPtMcEtaMc_mc{}{}", getName(iParticleSpecies), getName(iChargeSpecies)).c_str(), "", hcsCalculationYield); } - - if (groupAnalysis.cfgFlagQaPidKa.value) { - LOG(info) << "Enabling kaon PID QA."; - - if (doprocessMc.value) { - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaKa_mcKaP", ";;;;TPC #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaKa_mcKaM", ";;;;TPC #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaKa_mcKaP", ";;;;TOF #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaKa_mcKaM", ";;;;TOF #it{n}#it{#sigma}_{K};", hcsQaPid); - } else if (doprocessRaw.value) { - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaKa_p", ";;;;TPC #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaKa_m", ";;;;TPC #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaKa_tofKaP", ";;;;TPC #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaKa_tofKaM", ";;;;TPC #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaKa_tpcKaP", ";;;;TOF #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaKa_tpcKaM", ";;;;TOF #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcTofNSigmaKa_p", ";;;;TPC-TOF #it{n}#it{#sigma}_{K};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcTofNSigmaKa_m", ";;;;TPC-TOF #it{n}#it{#sigma}_{K};", hcsQaPid); + for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + if (cgTrack.cfgFlagMcParticleMomentum.value) { + hrCalculationYield.add(std::format("hVzCentralityPtMcEtaMc_mc{}{}{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies)).c_str(), "", hcsCalculationYield); + } else { + hrCalculationYield.add(std::format("hVzCentralityPtEta_mc{}{}{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies)).c_str(), "", hcsCalculationYield); + } } } - - if (groupAnalysis.cfgFlagQaPidPr.value) { - LOG(info) << "Enabling (anti)proton PID QA."; - - if (doprocessMc.value) { - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPr_mcPrP", ";;;;TPC #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPr_mcPrM", ";;;;TPC #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPr_mcPrP", ";;;;TOF #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPr_mcPrM", ";;;;TOF #it{n}#it{#sigma}_{p};", hcsQaPid); - } else if (doprocessRaw.value) { - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPr_p", ";;;;TPC #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPr_m", ";;;;TPC #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPr_tofPrP", ";;;;TPC #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcNSigmaPr_tofPrM", ";;;;TPC #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPr_tpcPrP", ";;;;TOF #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTofNSigmaPr_tpcPrM", ";;;;TOF #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcTofNSigmaPr_p", ";;;;TPC-TOF #it{n}#it{#sigma}_{p};", hcsQaPid); - hrQaPid.add("QaPid/hCentralityPtEtaTpcTofNSigmaPr_m", ";;;;TPC-TOF #it{n}#it{#sigma}_{p};", hcsQaPid); + } else { + for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrCalculationYield.add(std::format("hVzCentralityPtEta_{}{}{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies)).c_str(), "", hcsCalculationYield); } } } } - if (doprocessMc.value) { - if (groupAnalysis.cfgFlagQaMc.value) { - LOG(info) << "Enabling MC QA."; - - const AxisSpec asCentrality(20, 0., 100., "Centrality (%)"); - - hrQaMc.add("QaMc/hCentralityVzDeltaVz", "", {HistType::kTHnSparseF, {asCentrality, {200, -10., 10., "#it{V}_{#it{z}}^{Rec} (cm)"}, {200, -1., 1., "#it{V}_{#it{z}}^{Rec}#minus#it{V}_{#it{z}}^{Gen} (cm)"}}}); - hrQaMc.add("QaMc/hCentralityPtEtaDeltaPt", "", {HistType::kTHnSparseF, {asCentrality, {100, 0., 2., "#it{p}_{T}^{Rec} (GeV/#it{c})"}, {120, -1.2, 1.2, "#it{#eta}_{Rec}"}, {200, -1., 1., "#it{p}_{T}^{Rec}#minus#it{p}_{T}^{Gen} (GeV/#it{c})"}}}); - hrQaMc.add("QaMc/hCentralityPtEtaDeltaEta", "", {HistType::kTHnSparseF, {asCentrality, {100, 0., 2., "#it{p}_{T}^{Rec} (GeV/#it{c})"}, {120, -1.2, 1.2, "#it{#eta}_{Rec}"}, {200, -1., 1., "#it{#eta}_{Rec}#minus#it{#eta}_{Gen}"}}}); - } - } + if (doProcessMc.value) { + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsCalculationPurity.value.get(iParticleSpecies))) { + continue; + } - if (groupAnalysis.cfgFlagCalculationYieldPi.value || groupAnalysis.cfgFlagCalculationYieldKa.value || groupAnalysis.cfgFlagCalculationYieldPr.value) { - const HistogramConfigSpec hcsCalculationYield(HistType::kTHnSparseF, {{static_cast(std::llrint(std::ceil(groupEvent.cfgCutMaxAbsVertexZ.value))) * 2, -std::ceil(groupEvent.cfgCutMaxAbsVertexZ.value), std::ceil(groupEvent.cfgCutMaxAbsVertexZ.value), "#it{V}_{#it{z}} (cm)"}, {{0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90.}, "Centrality (%)"}, {40, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {32, -0.8, 0.8, "#it{#eta}"}}); + LOG(info) << "Enabling " << getName(iParticleSpecies) << " purity calculation."; - if (groupAnalysis.cfgFlagCalculationYieldPi.value) { - LOG(info) << "Enabling pion yield calculation."; + const HistogramConfigSpec hcsCalculationPurity{HistType::kTProfile3D, {{cgEvent.cfgAxisCentralityCalibration, "Centrality (%)"}, {20, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {16, -0.8, 0.8, "#it{#eta}"}}}; - if (doprocessMc.value) { - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcPiP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcPiM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcPiP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcPiM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPiP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPiM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcPiP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcPiM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcTofPiP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcTofPiM", "", hcsCalculationYield); - } else if (doprocessRaw.value) { - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcPiP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcPiM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcTofPiP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcTofPiM", "", hcsCalculationYield); + for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrCalculationPurity.add(std::format("pCentralityPtEtaPurity{}{}{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies)).c_str(), "", hcsCalculationPurity); + } } } + } - if (groupAnalysis.cfgFlagCalculationYieldKa.value) { - LOG(info) << "Enabling kaon yield calculation."; - - if (doprocessMc.value) { - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcKaP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcKaM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcKaP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcKaM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofKaP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofKaM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcKaP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcKaM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcTofKaP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcTofKaM", "", hcsCalculationYield); - } else if (doprocessRaw.value) { - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcKaP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcKaM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcTofKaP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcTofKaM", "", hcsCalculationYield); + if (doProcessMc.value) { + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsCalculationFractionPrimary.value.get(iParticleSpecies))) { + continue; } - } - if (groupAnalysis.cfgFlagCalculationYieldPr.value) { - LOG(info) << "Enabling (anti)proton yield calculation."; - - if (doprocessMc.value) { - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcPrP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcPrM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcPrP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcPrM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPrP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPrM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcPrP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcPrM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcTofPrP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_mcTpcTofPrM", "", hcsCalculationYield); - } else if (doprocessRaw.value) { - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcPrP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcPrM", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcTofPrP", "", hcsCalculationYield); - hrCalculationYield.add("CalculationYield/hVzCentralityPtEta_tpcTofPrM", "", hcsCalculationYield); + LOG(info) << "Enabling " << getName(iParticleSpecies) << " primary fraction calculation."; + + const HistogramConfigSpec hcsCalculationFractionPrimary{HistType::kTProfile3D, {{cgEvent.cfgAxisCentralityCalibration, "Centrality (%)"}, {20, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {16, -0.8, 0.8, "#it{#eta}"}}}; + + for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + hrCalculationFractionPrimary.add(std::format("pCentralityPtEtaFractionPrimary{}{}{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies)).c_str(), "", hcsCalculationFractionPrimary); + } } } } - if (doprocessMc.value) { - if (groupAnalysis.cfgFlagCalculationPurityPi.value || groupAnalysis.cfgFlagCalculationPurityKa.value || groupAnalysis.cfgFlagCalculationPurityPr.value) { - const HistogramConfigSpec hcsCalculationPurity(HistType::kTProfile3D, {{{0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90.}, "Centrality (%)"}, {20, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {16, -0.8, 0.8, "#it{#eta}"}}); - - if (groupAnalysis.cfgFlagCalculationPurityPi.value) { - LOG(info) << "Enabling pion purity calculation."; + for (std::int32_t const& iParticleNumber : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(iParticleNumber))) { + continue; + } - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcPiP", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcPiM", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcTofPiP", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcTofPiM", "", hcsCalculationPurity); - } + LOG(info) << "Enabling " << getName(iParticleNumber) << " number fluctuation calculation."; - if (groupAnalysis.cfgFlagCalculationPurityKa.value) { - LOG(info) << "Enabling kaon purity calculation."; + const AxisSpec asCentrality{cgEvent.cfgAxisCentrality, "Centrality (%)"}; + const AxisSpec asMultiplicity{cgEvent.cfgNMultiplicityBinsAxis.value, -0.5, cgEvent.cfgNMultiplicityBinsAxis.value - 0.5}; + const HistogramConfigSpec hcsDistribution{HistType::kTHnSparseD, {asCentrality, asMultiplicity, asMultiplicity}}; + const HistogramConfigSpec hcsFluctuationCalculator{HistType::kTH3D, {asCentrality, {cgEvent.cfgNSubgroups.value, -0.5, cgEvent.cfgNSubgroups.value - 0.5, "Subgroup Index"}, {fluctuation_calculator_base::NOrderKeys, -0.5, fluctuation_calculator_base::NOrderKeys - 0.5, "Order Key Index"}}}; - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcKaP", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcKaM", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcTofKaP", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcTofKaM", "", hcsCalculationPurity); + for (std::int32_t const& iChargeNumber : std::views::iota(0, NEs)) { + if (doProcessMc.value) { + holderMember.fluctuationCalculatorsTrackMcParticle[iParticleNumber][iChargeNumber] = std::make_unique(); } + holderMember.fluctuationCalculatorsTrackTrack[iParticleNumber][iChargeNumber] = std::make_unique(); + } - if (groupAnalysis.cfgFlagCalculationPurityPr.value) { - LOG(info) << "Enabling (anti)proton purity calculation."; - - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcPrP", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcPrM", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcTofPrP", "", hcsCalculationPurity); - hrCalculationPurity.add("CalculationPurity/pCentralityPtEtaPurityTpcTofPrM", "", hcsCalculationPurity); + if (doProcessMc.value) { + hrCalculationFluctuation.add(std::format("hCentralityN{}{}N{}{}_mc", getName(iParticleNumber), getName(ChargeSpecies::Plus), getName(iParticleNumber), getName(ChargeSpecies::Minus)).c_str(), std::format(";;#it{{N}}({}^{{{}}});#it{{N}}({}^{{{}}});", getTitle(iParticleNumber), getTitle(toI(ChargeSpecies::Plus)), getTitle(iParticleNumber), getTitle(toI(ChargeSpecies::Minus))).c_str(), hcsDistribution); + hrCalculationFluctuation.add(std::format("hCentralityN{}{}N{}{}_mcEff", getName(iParticleNumber), getName(ChargeSpecies::Plus), getName(iParticleNumber), getName(ChargeSpecies::Minus)).c_str(), std::format(";;#it{{N}}({}^{{{}}});#it{{N}}({}^{{{}}});", getTitle(iParticleNumber), getTitle(toI(ChargeSpecies::Plus)), getTitle(iParticleNumber), getTitle(toI(ChargeSpecies::Minus))).c_str(), hcsDistribution); + for (std::int32_t const& iChargeNumber : std::views::iota(0, NEs)) { + hrCalculationFluctuation.add(std::format("hFluctuationCalculator{}{}_mc", getName(iParticleNumber), getName(iChargeNumber)).c_str(), "", hcsFluctuationCalculator); } } + hrCalculationFluctuation.add(std::format("hCentralityN{}{}N{}{}", getName(iParticleNumber), getName(ChargeSpecies::Plus), getName(iParticleNumber), getName(ChargeSpecies::Minus)).c_str(), std::format(";;#it{{N}}({}^{{{}}});#it{{N}}({}^{{{}}});", getTitle(iParticleNumber), getTitle(toI(ChargeSpecies::Plus)), getTitle(iParticleNumber), getTitle(toI(ChargeSpecies::Minus))).c_str(), hcsDistribution); + for (std::int32_t const& iChargeNumber : std::views::iota(0, NEs)) { + hrCalculationFluctuation.add(std::format("hFluctuationCalculator{}{}", getName(iParticleNumber), getName(iChargeNumber)).c_str(), "", hcsFluctuationCalculator); + } } + } - if (doprocessMc.value) { - if (groupAnalysis.cfgFlagCalculationFractionPrimaryPi.value || groupAnalysis.cfgFlagCalculationFractionPrimaryKa.value || groupAnalysis.cfgFlagCalculationFractionPrimaryPr.value) { - const HistogramConfigSpec hcsCalculationFractionPrimary(HistType::kTProfile3D, {{{0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90.}, "Centrality (%)"}, {20, 0., 2., "#it{p}_{T} (GeV/#it{c})"}, {16, -0.8, 0.8, "#it{#eta}"}}); - - if (groupAnalysis.cfgFlagCalculationFractionPrimaryPi.value) { - LOG(info) << "Enabling pion primary fraction calculation."; - - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPiP", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPiM", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPiP", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPiM", "", hcsCalculationFractionPrimary); - } + template + void readCcdb() + { + if constexpr (DoInit) { + holderCcdb.lCcdb = ccdb->get(cgCcdb.cfgPath.value); + if (!holderCcdb.lCcdb || holderCcdb.lCcdb->IsA() != TList::Class()) { + LOG(fatal) << "Invalid CCDB object!"; + } - if (groupAnalysis.cfgFlagCalculationFractionPrimaryKa.value) { - LOG(info) << "Enabling kaon primary fraction calculation."; + const TGraph* const gRunNumberGroupIndex{dynamic_cast(holderCcdb.lCcdb->FindObject("gRunNumberGroupIndex"))}; + if (!gRunNumberGroupIndex || gRunNumberGroupIndex->IsA() != TGraph::Class()) { + LOG(fatal) << "Invalid gRunNumberGroupIndex!"; + } + for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndex->GetN())) { + holderCcdb.runNumbersToIndicesRunAndGroup[static_cast(std::rint(gRunNumberGroupIndex->GetPointX(iRun)))] = {iRun, static_cast(std::rint(gRunNumberGroupIndex->GetPointY(iRun)))}; + } - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcKaP", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcKaM", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofKaP", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofKaM", "", hcsCalculationFractionPrimary); + if (cgEvent.cfgFlagRejectionRunBadMc.value) { + const TGraph* const gRunNumberGroupIndexMc{dynamic_cast(holderCcdb.lCcdb->FindObject("gRunNumberGroupIndex_mc"))}; + if (!gRunNumberGroupIndexMc || gRunNumberGroupIndexMc->IsA() != TGraph::Class()) { + LOG(fatal) << "Invalid gRunNumberGroupIndex_mc!"; } - - if (groupAnalysis.cfgFlagCalculationFractionPrimaryPr.value) { - LOG(info) << "Enabling (anti)proton primary fraction calculation."; - - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPrP", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPrM", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPrP", "", hcsCalculationFractionPrimary); - hrCalculationFractionPrimary.add("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPrM", "", hcsCalculationFractionPrimary); + for (std::int32_t const& iRun : std::views::iota(0, gRunNumberGroupIndexMc->GetN())) { + if (std::cmp_less_equal(static_cast(std::rint(gRunNumberGroupIndexMc->GetPointY(iRun))), 0)) { + if (const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(static_cast(std::rint(gRunNumberGroupIndexMc->GetPointX(iRun))))}; iter != holderCcdb.runNumbersToIndicesRunAndGroup.end() && std::cmp_greater(iter->second.second, 0)) { + iter->second.second = -iter->second.second; + } + } } } - } + } else { + const std::int32_t indexRunGroup{std::abs(holderEvent.indexRunGroup)}; + if (std::cmp_equal(holderCcdb.indexRunGroupCurrent, indexRunGroup)) { + return; + } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - const AxisSpec asCentrality(groupEvent.cfgAxisCentrality, "Centrality (%)"); - const HistogramConfigSpec hcsDistribution(HistType::kTHnSparseD, {asCentrality, {200, -0.5, 199.5}, {200, -0.5, 199.5}}); - const HistogramConfigSpec hcsFluctuationCalculator(HistType::kTH3D, {asCentrality, {groupEvent.cfgNSubgroups.value, -0.5, groupEvent.cfgNSubgroups.value - 0.5, "Subgroup Index"}, {fluctuation_calculator_base::NOrderVectors, -0.5, fluctuation_calculator_base::NOrderVectors - 0.5, "Order Vector Index"}}); + holderCcdb.indexRunGroupCurrent = indexRunGroup; + holderCcdb.calibrationsPtMeasureDca = {}; + holderCcdb.hsCentralityPtEtaShiftNSigmaPid = {}; + holderCcdb.hsVzCentralityPtEtaEfficiency = {}; + if (!cgTrack.cfgFlagRecalibrationDca.value && !isEnabled(cgTrack.cfgFlagsRecalibrationNSigmaPid) && !holderMember.doCalculationFluctuation) { + return; + } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - LOG(info) << "Enabling charge number fluctuation calculation."; + const std::string nameList{std::format("lRunGroup_{}", indexRunGroup)}; + const TList* const lRunGroup{dynamic_cast(holderCcdb.lCcdb->FindObject(nameList.c_str()))}; + if (!lRunGroup) { + LOG(fatal) << "Invalid " << nameList << "!"; + } - fluctuationCalculatorTrackChP = std::make_unique(); - fluctuationCalculatorTrackChM = std::make_unique(); - fluctuationCalculatorTrackChT = std::make_unique(); - fluctuationCalculatorTrackChN = std::make_unique(); + if (cgTrack.cfgFlagRecalibrationDca.value) { + for (std::int32_t const& iDcaMeasure : std::views::iota(0, NEs)) { + for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + std::pair& calibration{holderCcdb.calibrationsPtMeasureDca[iDcaMeasure][iDcaAxis][iChargeSpecies]}; + const std::string nameFormula{std::format("fPt{}Dca{}{}{}_runGroup{}", getName(iDcaMeasure), getName(iDcaAxis), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", indexRunGroup)}; + calibration.first = dynamic_cast(lRunGroup->FindObject(nameFormula.c_str())); + if (!calibration.first || std::cmp_not_equal(calibration.first->GetNdim(), 1) || std::cmp_less_equal(calibration.first->GetNpar(), 0)) { + LOG(fatal) << "Invalid " << nameFormula << "!"; + } + LOG(info) << "Reading from CCDB: " << nameFormula << " \"" << calibration.first->GetExpFormula() << "\""; + const std::int32_t nParameters{calibration.first->GetNpar()}; - if (doprocessMc.value) { - hrCalculationFluctuation.add("CalculationFluctuation/hCentralityNChPNChM_mc", ";;#it{N}(h^{+});#it{N}(h^{#minus});", hcsDistribution); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChP_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChM_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChT_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChN_mc", "", hcsFluctuationCalculator); - } - hrCalculationFluctuation.add("CalculationFluctuation/hCentralityNChPNChM", ";;#it{N}(h^{+});#it{N}(h^{#minus});", hcsDistribution); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChP", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChM", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChT", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorChN", "", hcsFluctuationCalculator); - } - - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - LOG(info) << "Enabling kaon number fluctuation calculation."; - - fluctuationCalculatorTrackKaP = std::make_unique(); - fluctuationCalculatorTrackKaM = std::make_unique(); - fluctuationCalculatorTrackKaT = std::make_unique(); - fluctuationCalculatorTrackKaN = std::make_unique(); - - if (doprocessMc.value) { - hrCalculationFluctuation.add("CalculationFluctuation/hCentralityNKaPNKaM_mc", ";;#it{N}(K^{+});#it{N}(K^{#minus});", hcsDistribution); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaP_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaM_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaT_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaN_mc", "", hcsFluctuationCalculator); - } - hrCalculationFluctuation.add("CalculationFluctuation/hCentralityNKaPNKaM", ";;#it{N}(K^{+});#it{N}(K^{#minus});", hcsDistribution); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaP", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaM", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaT", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorKaN", "", hcsFluctuationCalculator); - } - - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - LOG(info) << "Enabling (anti)proton number fluctuation calculation."; - - fluctuationCalculatorTrackPrP = std::make_unique(); - fluctuationCalculatorTrackPrM = std::make_unique(); - fluctuationCalculatorTrackPrT = std::make_unique(); - fluctuationCalculatorTrackPrN = std::make_unique(); - - if (doprocessMc.value) { - hrCalculationFluctuation.add("CalculationFluctuation/hCentralityNPrPNPrM_mc", ";;#it{N}(p);#it{N}(#bar{p});", hcsDistribution); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrP_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrM_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrT_mc", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrN_mc", "", hcsFluctuationCalculator); + const std::string nameHistogram{std::format("hCentralityEtaParameterPt{}Dca{}{}{}_runGroup{}", getName(iDcaMeasure), getName(iDcaAxis), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", indexRunGroup)}; + calibration.second = dynamic_cast(lRunGroup->FindObject(nameHistogram.c_str())); + if (calibration.second == nullptr || std::cmp_not_equal(calibration.second->GetNbinsZ(), nParameters) || std::ranges::any_of(std::views::iota(0, nParameters), [aZ = calibration.second->GetZaxis()](const std::int32_t indexBin) -> bool { return aZ->GetBinCenter(indexBin + 1) != indexBin; })) { + LOG(fatal) << "Invalid " << nameHistogram << "!"; + } + LOG(info) << "Reading from CCDB: " << nameHistogram; + } + } } - hrCalculationFluctuation.add("CalculationFluctuation/hCentralityNPrPNPrM", ";;#it{N}(p);#it{N}(#bar{p});", hcsDistribution); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrP", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrM", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrT", "", hcsFluctuationCalculator); - hrCalculationFluctuation.add("CalculationFluctuation/hFluctuationCalculatorPrN", "", hcsFluctuationCalculator); } - const auto readHnEfficiency = [&](std::initializer_list*, const char*>> pairsVectorHnNameBase) { - for (const auto& [vectorHn, nameBase] : pairsVectorHnNameBase) { - vectorHn->resize(nRunGroups); + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + if (!static_cast(cgTrack.cfgFlagsRecalibrationNSigmaPid.value.get(iParticleSpecies))) { + continue; } - for (std::int32_t const& iRunGroup : std::views::iota(0, nRunGroups)) { - const TList* const lRunGroup = readListRunGroup(iRunGroup + 1); - for (const auto& [vectorHn, nameBase] : pairsVectorHnNameBase) { - (*vectorHn)[iRunGroup] = readHn(lRunGroup, nameBase, false, iRunGroup + 1, 4); + + for (std::int32_t const& iDetector : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + const std::string name{std::format("hCentralityPtEtaShift{}NSigma{}{}{}_runGroup{}", getName(iDetector), getName(iParticleSpecies), getName(iChargeSpecies), doProcessMc.value ? "_mc" : "", indexRunGroup)}; + const TH3*& h{holderCcdb.hsCentralityPtEtaShiftNSigmaPid[iDetector][iParticleSpecies][iChargeSpecies]}; + h = dynamic_cast(lRunGroup->FindObject(name.c_str())); + if (!h) { + LOG(fatal) << "Invalid " << name << "!"; + } + LOG(info) << "Reading from CCDB: " << name; } } - }; - - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - readHnEfficiency({{&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPiP, "hVzCentralityPtEtaEfficiencyTpcPiP"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPiM, "hVzCentralityPtEtaEfficiencyTpcPiM"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPiP, "hVzCentralityPtEtaEfficiencyTpcTofPiP"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPiM, "hVzCentralityPtEtaEfficiencyTpcTofPiM"}}); } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - readHnEfficiency({{&holderCcdb.hVzCentralityPtEtaEfficiencyTpcKaP, "hVzCentralityPtEtaEfficiencyTpcKaP"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcKaM, "hVzCentralityPtEtaEfficiencyTpcKaM"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofKaP, "hVzCentralityPtEtaEfficiencyTpcTofKaP"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofKaM, "hVzCentralityPtEtaEfficiencyTpcTofKaM"}}); - } + for (std::int32_t const& iParticleSpecies : std::views::iota(0, NEs)) { + if (!static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Charge))) && (std::cmp_not_equal(iParticleSpecies, toI(ParticleSpecies::Kaon)) || !static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Kaon)))) && (std::cmp_not_equal(iParticleSpecies, toI(ParticleSpecies::Proton)) || !static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumber::Proton))))) { + continue; + } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - readHnEfficiency({{&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPrP, "hVzCentralityPtEtaEfficiencyTpcPrP"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPrM, "hVzCentralityPtEtaEfficiencyTpcPrM"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPrP, "hVzCentralityPtEtaEfficiencyTpcTofPrP"}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPrM, "hVzCentralityPtEtaEfficiencyTpcTofPrM"}}); + for (std::int32_t const& iPidStrategy : std::views::iota(0, NEs)) { + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + const std::string name{std::format("hVzCentralityPtEtaEfficiency{}{}{}_runGroup{}", getName(iPidStrategy), getName(iParticleSpecies), getName(iChargeSpecies), indexRunGroup)}; + const THnBase*& h{holderCcdb.hsVzCentralityPtEtaEfficiency[iPidStrategy][iParticleSpecies][iChargeSpecies]}; + h = dynamic_cast(lRunGroup->FindObject(name.c_str())); + if (!h || std::cmp_not_equal(h->GetNdimensions(), HolderCcdb::NDimensionsEfficiency)) { + LOG(fatal) << "Invalid " << name << "!"; + } + LOG(info) << "Reading from CCDB: " << name; + } + } } } } - template - double getEfficiency(const bool doUsingMcParticleMomentum) + template + requires IsValidEnumValue + double getEfficiency(const bool doUseMcParticleMomentum) const { - static_assert(particleSpecies == ParticleSpecies::kPi || particleSpecies == ParticleSpecies::kKa || particleSpecies == ParticleSpecies::kPr); - static_assert(pidStrategy == PidStrategy::kTpc || pidStrategy == PidStrategy::kTpcTof); - - static const std::array*, 2>, 2>, static_cast(ParticleSpecies::kNSpecies)> pointersVectorHistogramEfficiency = {{{{{&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPiP, &holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPiP}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPiM, &holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPiM}}}, {{{&holderCcdb.hVzCentralityPtEtaEfficiencyTpcKaP, &holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofKaP}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcKaM, &holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofKaM}}}, {{{&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPrP, &holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPrP}, {&holderCcdb.hVzCentralityPtEtaEfficiencyTpcPrM, &holderCcdb.hVzCentralityPtEtaEfficiencyTpcTofPrM}}}}}; + const THnBase* const hVzCentralityPtEtaEfficiency{holderCcdb.hsVzCentralityPtEtaEfficiency[toI(PidStrategyValue)][toI(ParticleSpeciesValue)][toI(ChargeSpeciesValue)]}; + return hVzCentralityPtEtaEfficiency ? hVzCentralityPtEtaEfficiency->GetBinContent(hVzCentralityPtEtaEfficiency->GetBin(std::array{doProcessMc.value && cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, doUseMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt, doUseMcParticleMomentum ? holderMcParticle.eta : holderTrack.eta}.data())) : 0.; + } - if constexpr (doProcessingMc) { - if (holderMcParticle.charge == 0) { - return 0.; + template + requires IsValidEnumValue + double getShiftNSigmaPid() const + { + if constexpr (DoRecalibrate) { + if (cgTrack.cfgFlagsRecalibrationNSigmaPid.value.get(toI(ParticleSpeciesValue))) { + return interpolate(holderCcdb.hsCentralityPtEtaShiftNSigmaPid[toI(DetectorValue)][toI(ParticleSpeciesValue)][std::cmp_greater(holderTrack.sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)], holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta); } - const THnBase* const hVzCentralityPtEtaEfficiency = pointersVectorHistogramEfficiency[static_cast(particleSpecies)][holderMcParticle.charge > 0 ? 0 : 1][pidStrategy == PidStrategy::kTpc ? 0 : 1]->at(std::abs(holderEvent.runGroupIndex) - 1); - return hVzCentralityPtEtaEfficiency ? (doUsingMcParticleMomentum ? hVzCentralityPtEtaEfficiency->GetBinContent(hVzCentralityPtEtaEfficiency->GetBin(std::array{holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta}.data())) : hVzCentralityPtEtaEfficiency->GetBinContent(hVzCentralityPtEtaEfficiency->GetBin(std::array{holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta}.data()))) : 0.; } - - if (holderTrack.sign == 0) { - return 0.; - } - const THnBase* const hVzCentralityPtEtaEfficiency = pointersVectorHistogramEfficiency[static_cast(particleSpecies)][holderTrack.sign > 0 ? 0 : 1][pidStrategy == PidStrategy::kTpc ? 0 : 1]->at(std::abs(holderEvent.runGroupIndex) - 1); - return hVzCentralityPtEtaEfficiency ? hVzCentralityPtEtaEfficiency->GetBinContent(hVzCentralityPtEtaEfficiency->GetBin(std::array{holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta}.data())) : 0.; + return 0.; } - template - double getShiftNSigmaPid() + double getMeasureDca(const std::pair& calibration) const { - static_assert(particleSpecies == ParticleSpecies::kPi || particleSpecies == ParticleSpecies::kKa || particleSpecies == ParticleSpecies::kPr); - static_assert(pidStrategy == PidStrategy::kTpc || pidStrategy == PidStrategy::kTof); + static thread_local std::vector parametersPtMeasureDcaScratch{}; - if constexpr (particleSpecies == ParticleSpecies::kPi) { - if (!groupTrack.cfgFlagRecalibrationNSigmaPi.value) { - return 0.; - } - } else if constexpr (particleSpecies == ParticleSpecies::kKa) { - if (!groupTrack.cfgFlagRecalibrationNSigmaKa.value) { - return 0.; - } - } else if constexpr (particleSpecies == ParticleSpecies::kPr) { - if (!groupTrack.cfgFlagRecalibrationNSigmaPr.value) { - return 0.; - } - } else { - return 0.; + const TFormula* const fPtMeasureDca{calibration.first}; + const TH3* const hCentralityEtaParameterPtMeasureDca{calibration.second}; + const std::int32_t nParameters{fPtMeasureDca->GetNpar()}; + if (std::cmp_less(parametersPtMeasureDcaScratch.size(), nParameters)) { + parametersPtMeasureDcaScratch.resize(nParameters); } - - static const std::array*, 2>, 2>, static_cast(ParticleSpecies::kNSpecies)> pointersVectorHistogramShiftNSigmaPid = {{{{{&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPiP, &holderCcdb.hCentralityPtEtaShiftTofNSigmaPiP}, {&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPiM, &holderCcdb.hCentralityPtEtaShiftTofNSigmaPiM}}}, {{{&holderCcdb.hCentralityPtEtaShiftTpcNSigmaKaP, &holderCcdb.hCentralityPtEtaShiftTofNSigmaKaP}, {&holderCcdb.hCentralityPtEtaShiftTpcNSigmaKaM, &holderCcdb.hCentralityPtEtaShiftTofNSigmaKaM}}}, {{{&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPrP, &holderCcdb.hCentralityPtEtaShiftTofNSigmaPrP}, {&holderCcdb.hCentralityPtEtaShiftTpcNSigmaPrM, &holderCcdb.hCentralityPtEtaShiftTofNSigmaPrM}}}}}; - static const auto clampInAxis = [](const double value, const TAxis* const axis) { - const std::int32_t first = std::clamp(axis->GetFirst(), 1, axis->GetNbins()); - const std::int32_t last = std::clamp(axis->GetLast(), 1, axis->GetNbins()); - return first == last ? axis->GetBinCenter(first) : std::clamp(value, std::nextafter(axis->GetBinCenter(first), std::numeric_limits::infinity()), std::nextafter(axis->GetBinCenter(last), -std::numeric_limits::infinity())); - }; - - if (holderTrack.sign == 0) { - return 0.; + const std::int32_t indexBinCentrality{std::clamp(hCentralityEtaParameterPtMeasureDca->GetXaxis()->FindFixBin(holderEvent.centralityCalibration), 1, hCentralityEtaParameterPtMeasureDca->GetNbinsX())}; + const std::int32_t indexBinEta{std::clamp(hCentralityEtaParameterPtMeasureDca->GetYaxis()->FindFixBin(holderTrack.eta), 1, hCentralityEtaParameterPtMeasureDca->GetNbinsY())}; + for (std::int32_t const& iParameter : std::views::iota(0, nParameters)) { + parametersPtMeasureDcaScratch[iParameter] = hCentralityEtaParameterPtMeasureDca->GetBinContent(indexBinCentrality, indexBinEta, iParameter + 1); } - const TH3* const hCentralityPtEtaShiftNSigmaPid = pointersVectorHistogramShiftNSigmaPid[static_cast(particleSpecies)][holderTrack.sign > 0 ? 0 : 1][pidStrategy == PidStrategy::kTpc ? 0 : 1]->at(std::abs(holderEvent.runGroupIndex) - 1); - return hCentralityPtEtaShiftNSigmaPid ? hCentralityPtEtaShiftNSigmaPid->Interpolate(clampInAxis(holderEvent.centrality, hCentralityPtEtaShiftNSigmaPid->GetXaxis()), clampInAxis(holderTrack.pt, hCentralityPtEtaShiftNSigmaPid->GetYaxis()), clampInAxis(holderTrack.eta, hCentralityPtEtaShiftNSigmaPid->GetZaxis())) : 0.; + return fPtMeasureDca->EvalPar(&holderTrack.pt, parametersPtMeasureDcaScratch.data()); } - template - std::int32_t isPid(const bool doRejectingOthers) + template + requires IsValidEnumValue + double getAbsNSigmaDca() const { - static_assert(particleSpecies == ParticleSpecies::kPi || particleSpecies == ParticleSpecies::kKa || particleSpecies == ParticleSpecies::kPr); - static_assert(pidStrategy == PidStrategy::kTpc || pidStrategy == PidStrategy::kTof || pidStrategy == PidStrategy::kTpcAndTof || pidStrategy == PidStrategy::kTpcTof); + if (!cgTrack.cfgFlagRecalibrationDca.value) { + return std::abs(holderTrack.dcas[toI(DcaAxisValue)]); + } - static const std::array, static_cast(ParticleSpecies::kNSpecies)> pointersNSigmaPid = {{{&holderTrack.tpcNSigmaPi, &holderTrack.tofNSigmaPi, &holderTrack.tpcTofNSigmaPi}, {&holderTrack.tpcNSigmaKa, &holderTrack.tofNSigmaKa, &holderTrack.tpcTofNSigmaKa}, {&holderTrack.tpcNSigmaPr, &holderTrack.tofNSigmaPr, &holderTrack.tpcTofNSigmaPr}}}; + const std::int32_t indexChargeSpecies{std::cmp_greater(holderTrack.sign, 0) ? toI(ChargeSpecies::Plus) : toI(ChargeSpecies::Minus)}; + const double sigma{getMeasureDca(holderCcdb.calibrationsPtMeasureDca[toI(DcaMeasure::Sigma)][toI(DcaAxisValue)][indexChargeSpecies])}; + return sigma > 0. ? std::abs((holderTrack.dcas[toI(DcaAxisValue)] - getMeasureDca(holderCcdb.calibrationsPtMeasureDca[toI(DcaMeasure::Mean)][toI(DcaAxisValue)][indexChargeSpecies])) / sigma) : std::numeric_limits::infinity(); + } - if constexpr (pidStrategy == PidStrategy::kTpc) { - if (doRejectingOthers) { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][0]) < std::min({groupTrack.cfgCutMaxAbsNSigmaPid.value, std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 1) % static_cast(ParticleSpecies::kNSpecies)][0]), std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 2) % static_cast(ParticleSpecies::kNSpecies)][0])}))) { - return 0; + template + requires IsValidEnumValue + bool isPid(const bool doRejectOthers) const + { + if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { + if constexpr (PidStrategyAllValue == PidStrategyAll::Tpc) { + if (!holderTrack.havePid[toI(Detector::Tpc)]) { + return false; + } + } else if constexpr (PidStrategyAllValue == PidStrategyAll::Tof) { + if (!holderTrack.havePid[toI(Detector::Tof)]) { + return false; } } else { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][0]) < groupTrack.cfgCutMaxAbsNSigmaPid.value)) { - return 0; + if (!holderTrack.havePid[toI(Detector::Tpc)] || !holderTrack.havePid[toI(Detector::Tof)]) { + return false; } } - } else if constexpr (pidStrategy == PidStrategy::kTof) { - if (doRejectingOthers) { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][1]) < std::min({groupTrack.cfgCutMaxAbsNSigmaPid.value, std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 1) % static_cast(ParticleSpecies::kNSpecies)][1]), std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 2) % static_cast(ParticleSpecies::kNSpecies)][1])}))) { - return 0; + } else { + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; + if constexpr (PidStrategyAllValue == PidStrategyAll::TpcTofSeparated) { + if (!(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { + return false; } - } else { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][1]) < groupTrack.cfgCutMaxAbsNSigmaPid.value)) { - return 0; + if (!(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { + return false; } - } - } else if constexpr (pidStrategy == PidStrategy::kTpcAndTof) { - if (doRejectingOthers) { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][0]) < groupTrack.cfgCutMaxAbsNSigmaPid.value && std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][1]) < std::min({groupTrack.cfgCutMaxAbsNSigmaPid.value, std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 1) % static_cast(ParticleSpecies::kNSpecies)][1]), std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 2) % static_cast(ParticleSpecies::kNSpecies)][1])}))) { - return 0; + if (doRejectOthers && !(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]) < std::min(std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][(IndexParticleSpecies + 1) % NEs]), std::abs(holderTrack.nsSigmaPid[toI(Detector::Tof)][(IndexParticleSpecies + 2) % NEs])))) { + return false; } - } else { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][0]) < groupTrack.cfgCutMaxAbsNSigmaPid.value && std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][1]) < groupTrack.cfgCutMaxAbsNSigmaPid.value)) { - return 0; + } else if constexpr (PidStrategyAllValue == PidStrategyAll::TpcTofCombined) { + const double absNSigmaPidCombined{std::abs(holderTrack.getNSigmaPidCombined(IndexParticleSpecies))}; + if (!(absNSigmaPidCombined < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { + return false; } - } - } else if constexpr (pidStrategy == PidStrategy::kTpcTof) { - if (doRejectingOthers) { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][2]) < std::min({groupTrack.cfgCutMaxAbsNSigmaPid.value, std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 1) % static_cast(ParticleSpecies::kNSpecies)][2]), std::abs(*pointersNSigmaPid[(static_cast(particleSpecies) + 2) % static_cast(ParticleSpecies::kNSpecies)][2])}))) { - return 0; + if (doRejectOthers && !(absNSigmaPidCombined < std::min(std::abs(holderTrack.getNSigmaPidCombined((IndexParticleSpecies + 1) % NEs)), std::abs(holderTrack.getNSigmaPidCombined((IndexParticleSpecies + 2) % NEs))))) { + return false; } } else { - if (!(std::abs(*pointersNSigmaPid[static_cast(particleSpecies)][2]) < groupTrack.cfgCutMaxAbsNSigmaPid.value)) { - return 0; + constexpr std::int32_t IndexDetector{toI(getValue(PidStrategyAllValue))}; + if (!(std::abs(holderTrack.nsSigmaPid[IndexDetector][IndexParticleSpecies]) < cgTrack.cfgCutsMaxAbsNSigmaPid.value.get(IndexParticleSpecies, toI(SelectionValue)))) { + return false; + } + if (doRejectOthers && !(std::abs(holderTrack.nsSigmaPid[IndexDetector][IndexParticleSpecies]) < std::min(std::abs(holderTrack.nsSigmaPid[IndexDetector][(IndexParticleSpecies + 1) % NEs]), std::abs(holderTrack.nsSigmaPid[IndexDetector][(IndexParticleSpecies + 2) % NEs])))) { + return false; } } - } else { - return 0; } - return holderTrack.sign; + return true; } - bool isGoodMomentum(const bool doUsingMcParticleMomentum) + template + requires IsValidEnumValue + bool isPid() const noexcept { - if (doUsingMcParticleMomentum) { - if (!(groupTrack.cfgCutMinPt.value < holderMcParticle.pt && holderMcParticle.pt < groupTrack.cfgCutMaxPt.value)) { - return false; - } - if (!(std::abs(holderMcParticle.eta) < groupTrack.cfgCutMaxAbsEta.value)) { - return false; - } + if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { + return ChargeSpeciesValue == ChargeSpecies::Plus ? std::cmp_greater(holderMcParticle.charge, 0) : std::cmp_less(holderMcParticle.charge, 0); } else { - if (!(groupTrack.cfgCutMinPt.value < holderTrack.pt && holderTrack.pt < groupTrack.cfgCutMaxPt.value)) { - return false; - } - if (!(std::abs(holderTrack.eta) < groupTrack.cfgCutMaxAbsEta.value)) { - return false; - } + return std::cmp_equal(holderMcParticle.pdgCode, getPdgCode(getValue(ParticleSpeciesAllValue), ChargeSpeciesValue)); } - return true; } - bool isGoodDca() + template + requires IsValidEnumValue + bool isGoodMomentum(const bool doUseMcParticleMomentum) const { - if (!groupTrack.cfgFlagRecalibrationDca.value) { - if (!(std::abs(holderTrack.dcaXY) < groupTrack.cfgCutMaxAbsNSigmaDcaXy.value)) { - return false; - } - if (!(std::abs(holderTrack.dcaZ) < groupTrack.cfgCutMaxAbsNSigmaDcaZ.value)) { + const double pt{doUseMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt}; + if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { + if (!(holderMember.rangePtAll[toI(RangeEdge::Min)] < pt) || !(pt < holderMember.rangePtAll[toI(RangeEdge::Max)])) { return false; } } else { - if (holderTrack.sign == 0) { - return false; - } - const TFormula* const fPtMeanDcaXy = (holderTrack.sign > 0 ? holderCcdb.fPtMeanDcaXyP.at(std::abs(holderEvent.runGroupIndex) - 1) : holderCcdb.fPtMeanDcaXyM.at(std::abs(holderEvent.runGroupIndex) - 1)); - const TFormula* const fPtSigmaDcaXy = (holderTrack.sign > 0 ? holderCcdb.fPtSigmaDcaXyP.at(std::abs(holderEvent.runGroupIndex) - 1) : holderCcdb.fPtSigmaDcaXyM.at(std::abs(holderEvent.runGroupIndex) - 1)); - if (!fPtMeanDcaXy || !fPtSigmaDcaXy || !(std::abs(holderTrack.dcaXY - fPtMeanDcaXy->Eval(holderTrack.pt)) < groupTrack.cfgCutMaxAbsNSigmaDcaXy.value * fPtSigmaDcaXy->Eval(holderTrack.pt))) { - return false; - } - const TFormula* const fPtMeanDcaZ = (holderTrack.sign > 0 ? holderCcdb.fPtMeanDcaZP.at(std::abs(holderEvent.runGroupIndex) - 1) : holderCcdb.fPtMeanDcaZM.at(std::abs(holderEvent.runGroupIndex) - 1)); - const TFormula* const fPtSigmaDcaZ = (holderTrack.sign > 0 ? holderCcdb.fPtSigmaDcaZP.at(std::abs(holderEvent.runGroupIndex) - 1) : holderCcdb.fPtSigmaDcaZM.at(std::abs(holderEvent.runGroupIndex) - 1)); - if (!fPtMeanDcaZ || !fPtSigmaDcaZ || !(std::abs(holderTrack.dcaZ - fPtMeanDcaZ->Eval(holderTrack.pt)) < groupTrack.cfgCutMaxAbsNSigmaDcaZ.value * fPtSigmaDcaZ->Eval(holderTrack.pt))) { + const std::int32_t indexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; + if (!(cgTrack.cfgCutsRangePt.value.get(indexParticleSpecies, toI(RangeEdge::Min)) < pt) || !(pt < cgTrack.cfgCutsRangePt.value.get(indexParticleSpecies, toI(RangeEdge::Max)))) { return false; } } - return true; + return std::abs(doUseMcParticleMomentum ? holderMcParticle.eta : holderTrack.eta) < cgTrack.cfgCutMaxAbsEta.value; + } + + bool isGoodDca() const + { + return getAbsNSigmaDca() < cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(toI(DcaAxis::Xy), toI(Selection::Default)) && getAbsNSigmaDca() < cgTrack.cfgCutsMaxAbsNSigmaDca.value.get(toI(DcaAxis::Z), toI(Selection::Default)); } template - bool isGoodTrack(const T& track) + bool isGoodTrack(const T& track) const { - if ((groupTrack.cfgFlagPvContributor.value && !track.isPVContributor())) { + if (cgTrack.cfgFlagPvContributor.value && !track.isPVContributor()) { return false; } - if (!(track.itsNCls() > groupTrack.cfgCutMinItsNCls.value)) { + if (!std::cmp_greater(track.itsNCls(), cgTrack.cfgCutsMinItsNCls.value.get(toI(Selection::Default)))) { return false; } - if (!(track.itsChi2NCl() < groupTrack.cfgCutMaxItsChi2NCls.value)) { + if (!(track.itsChi2NCl() < cgTrack.cfgCutsMaxItsChi2NCls.value.get(toI(Selection::Default)))) { return false; } - if (!(track.tpcNClsFound() > groupTrack.cfgCutMinTpcNCls.value)) { + if (!std::cmp_greater(track.tpcNClsFound(), cgTrack.cfgCutsMinTpcNCls.value.get(toI(Selection::Default)))) { return false; } - if (!(track.tpcChi2NCl() < groupTrack.cfgCutMaxTpcChi2NCls.value)) { + if (!(cgTrack.cfgCutMinTpcChi2NCls.value < track.tpcChi2NCl()) || !(track.tpcChi2NCl() < cgTrack.cfgCutsMaxTpcChi2NCls.value.get(toI(Selection::Default)))) { return false; } - if (!(track.tpcFractionSharedCls() < groupTrack.cfgCutMaxTpcNClsSharedRatio.value)) { + if (!(track.tpcFractionSharedCls() < cgTrack.cfgCutsMaxTpcNClsSharedRatio.value.get(toI(Selection::Default)))) { return false; } - if (!(track.tpcNClsCrossedRows() > groupTrack.cfgCutMinTpcNCrossedRows.value)) { + if (!(track.tpcFoundOverFindableCls() > cgTrack.cfgCutMinTpcNClsRatio.value)) { return false; } - if (!(track.tpcCrossedRowsOverFindableCls() > groupTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { + if (!std::cmp_greater(track.tpcNClsCrossedRows(), cgTrack.cfgCutsMinTpcNCrossedRows.value.get(toI(Selection::Default)))) { + return false; + } + if (!(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { return false; } return true; } - template - bool isGoodMcParticle(const MP& mcParticle) + template + requires IsValidEnumValue + void fillQaRunByTrackByChargeSpecies(const T& track) { - if constexpr (doProcessingMc) { - if (!mcParticle.isPhysicalPrimary()) { - return false; - } + const auto fill{[this](const auto& name, const auto input) -> void { + hrQaRun.fill(C_CS("pRunIndex") + name + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.indexRun, input); + }}; + const auto fillNSigmaPidByDetectorParticleSpecies{ + [this, &fill] + requires IsValidEnumValue + () -> void { + const double nSigmaPid{holderTrack.nsSigmaPid[toI(DetectorValue)][toI(ParticleSpeciesValue)]}; + if (std::abs(nSigmaPid) < HolderTrack::TruncationAbsNSigmaPid) { + fill(C_SV(getName(DetectorValue)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesValue)), nSigmaPid); + } + }}; + + fill(C_CS("ItsNCls"), track.itsNCls()); + fill(C_CS("ItsChi2NCls"), track.itsChi2NCl()); + fill(C_CS("TpcNCls"), track.tpcNClsFound()); + fill(C_CS("TpcChi2NCls"), track.tpcChi2NCl()); + fill(C_CS("TpcNClsSharedRatio"), track.tpcFractionSharedCls()); + fill(C_CS("TpcNClsRatio"), track.tpcFoundOverFindableCls()); + fill(C_CS("TpcNCrossedRows"), track.tpcNClsCrossedRows()); + fill(C_CS("TpcNCrossedRowsRatio"), track.tpcCrossedRowsOverFindableCls()); + fill(C_CS("Pt"), holderTrack.pt); + fill(C_CS("Eta"), holderTrack.eta); + fill(C_CS("Phi"), holderTrack.phi); + if (holderTrack.havePid[toI(Detector::Tpc)]) { + fill(C_SV(getName(Detector::Tpc)) + C_CS("DeDx"), track.tpcSignal()); + fillNSigmaPidByDetectorParticleSpecies.template operator()(); + fillNSigmaPidByDetectorParticleSpecies.template operator()(); + fillNSigmaPidByDetectorParticleSpecies.template operator()(); + } + if (holderTrack.havePid[toI(Detector::Tof)]) { + fill(C_SV(getName(Detector::Tof)) + C_CS("InverseBeta"), 1. / track.beta()); + fillNSigmaPidByDetectorParticleSpecies.template operator()(); + fillNSigmaPidByDetectorParticleSpecies.template operator()(); + fillNSigmaPidByDetectorParticleSpecies.template operator()(); } - return true; } - template - void calculateFluctuation() + template + requires IsValidEnumValue + void fillQaRunByEventByChargeSpecies() + { + const auto fill{[this](const auto& name, const auto input) -> void { + hrQaRun.fill(C_CS("pRunIndex") + name + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.indexRun, input); + }}; + + fill(C_CS("NGlobalTracks"), holderEvent.nsGlobalTracks[toI(ChargeSpeciesValue)]); + fill(C_CS("NPvContributors"), holderEvent.nsPvContributors[toI(ChargeSpeciesValue)]); + if (std::cmp_greater(holderEvent.nsGlobalTracks[toI(ChargeSpeciesValue)], 0)) { + fill(C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxis::Xy)][toI(ChargeSpeciesValue)]); + fill(C_SV(getName(DcaMeasure::Sigma)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.measuresDca[toI(DcaMeasure::Sigma)][toI(DcaAxis::Xy)][toI(ChargeSpeciesValue)]); + fill(C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.measuresDca[toI(DcaMeasure::Mean)][toI(DcaAxis::Z)][toI(ChargeSpeciesValue)]); + fill(C_SV(getName(DcaMeasure::Sigma)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.measuresDca[toI(DcaMeasure::Sigma)][toI(DcaAxis::Z)][toI(ChargeSpeciesValue)]); + } + fill(C_CS("NTofBeta"), holderEvent.nsTofBeta[toI(ChargeSpeciesValue)]); + } + + template + requires IsValidEnumValue + void fillQaTrackByChargeSpecies(const T& track) { - const bool doUsingMcParticleMomentum = doProcessingMc && groupTrack.cfgFlagMcParticleMomentum.value; - if (!(isGoodMomentum(doUsingMcParticleMomentum) && holderTrack.hasTpcPid)) { + const auto fill{[this](const auto& name, const auto... positionAndWeight) -> void { + hrQaTrack.fill(C_CS("h") + name + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), positionAndWeight...); + }}; + + fill(C_CS("ItsNClsChi2NCls"), track.itsNCls(), track.itsChi2NCl()); + fill(C_CS("TpcNClsChi2NCls"), track.tpcNClsFound(), track.tpcChi2NCl()); + fill(C_CS("TpcNClsNClsShared"), track.tpcNClsFound(), track.tpcNClsShared()); + fill(C_CS("TpcNClsNClsFindableNCrossedRows"), track.tpcNClsFound(), track.tpcNClsFindable(), track.tpcNClsCrossedRows()); + } + + template + requires IsValidEnumValue + void fillQaDcaByChargeSpecies() + { + const auto fillByDcaAxis{ + [this] + requires IsValidEnumValue + () -> void { + hrQaDca.fill(C_CS("hPtDca") + C_SV(getName(DcaAxisValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderTrack.pt, holderTrack.dcas[toI(DcaAxisValue)]); + hrQaDca.fill(C_CS("pCentralityPtEtaDca") + C_SV(getName(DcaAxisValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.dcas[toI(DcaAxisValue)]); + }}; + + fillByDcaAxis.template operator()(); + fillByDcaAxis.template operator()(); + } + + template + requires IsValidEnumValue && (DataModeValue == DataMode::McMcParticle || DataModeValue == DataMode::RawTrack) + void fillQaAcceptanceByParticleSpeciesAll() + { + if (!cgAnalysis.cfgFlagsQaAcceptance.value.get(toI(ParticleSpeciesAllValue))) { return; } - const auto processSign = [&](const std::int32_t sign, const std::int32_t pdgCodeP, const std::int32_t pdgCodeM, std::int32_t& nP, std::int32_t& nM, std::unique_ptr& fluctuationCalculatorTrackP, std::unique_ptr& fluctuationCalculatorTrackM, std::unique_ptr& fluctuationCalculatorTrackT, std::unique_ptr& fluctuationCalculatorTrackN) { - if (std::abs(sign) != 1) { - return; - } - if constexpr (doProcessingMc) { - if (holderMcParticle.pdgCode != (sign > 0 ? pdgCodeP : pdgCodeM)) { - return; - } - } + const auto fillByChargeSpecies{ + [this] + requires IsValidEnumValue + () -> void { + const auto fillByPidStrategy{ + [this] + requires IsValidEnumValue + () -> void { + if constexpr (DataModeValue == DataMode::McMcParticle) { + if (!isPid(PidStrategyValue), ParticleSpeciesAll::All>(false) || !isPid()) { + return; + } - const double efficiency = getEfficiency(doUsingMcParticleMomentum); - if (sign > 0) { - nP++; - fluctuationCalculatorTrackP->fill(1., efficiency); - fluctuationCalculatorTrackT->fill(1., efficiency); - fluctuationCalculatorTrackN->fill(1., efficiency); - } else { - nM++; - fluctuationCalculatorTrackM->fill(1., efficiency); - fluctuationCalculatorTrackT->fill(1., efficiency); - fluctuationCalculatorTrackN->fill(-1., efficiency); - } - }; // NOLINT(readability/braces) + hrQaAcceptance.fill(C_CS("hEtaMcPtMc_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderMcParticle.eta, holderMcParticle.pt); + if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { + hrQaAcceptance.fill(C_CS("hRapidityMcPtMc_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), RecoDecayPtEtaPhi::y(holderMcParticle.pt, holderMcParticle.eta, getMass(getValue(ParticleSpeciesAllValue))), holderMcParticle.pt); + } + } else { // DataModeValue == DataMode::RawTrack + if (!isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { + return; + } - const auto processSpecies = [&](const double thresholdPtTof, const std::int32_t pdgCodeP, const std::int32_t pdgCodeM, std::int32_t& nP, std::int32_t& nM, std::unique_ptr& fluctuationCalculatorTrackP, std::unique_ptr& fluctuationCalculatorTrackM, std::unique_ptr& fluctuationCalculatorTrackT, std::unique_ptr& fluctuationCalculatorTrackN) { - if ((doUsingMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt) < thresholdPtTof) { - processSign.template operator()(isPid(groupTrack.cfgFlagRejectionOthers.value), pdgCodeP, pdgCodeM, nP, nM, fluctuationCalculatorTrackP, fluctuationCalculatorTrackM, fluctuationCalculatorTrackT, fluctuationCalculatorTrackN); - } else if (holderTrack.hasTofPid) { - processSign.template operator()(isPid(groupTrack.cfgFlagRejectionOthers.value), pdgCodeP, pdgCodeM, nP, nM, fluctuationCalculatorTrackP, fluctuationCalculatorTrackM, fluctuationCalculatorTrackT, fluctuationCalculatorTrackN); - } - }; // NOLINT(readability/braces) + hrQaAcceptance.fill(C_CS("hEtaPt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderTrack.eta, holderTrack.pt); + if constexpr (ParticleSpeciesAllValue != ParticleSpeciesAll::All) { + hrQaAcceptance.fill(C_CS("hRapidityPt_") + C_SV(getName(PidStrategyValue)) + C_CS("Edge") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), RecoDecayPtEtaPhi::y(holderTrack.pt, holderTrack.eta, getMass(getValue(ParticleSpeciesAllValue))), holderTrack.pt); + } + } + }}; - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - processSpecies.template operator()(groupTrack.cfgThresholdPtTofPi.value, PDG_t::kPiPlus, PDG_t::kPiMinus, holderEvent.nChP, holderEvent.nChM, fluctuationCalculatorTrackChP, fluctuationCalculatorTrackChM, fluctuationCalculatorTrackChT, fluctuationCalculatorTrackChN); - processSpecies.template operator()(groupTrack.cfgThresholdPtTofKa.value, PDG_t::kKPlus, PDG_t::kKMinus, holderEvent.nChP, holderEvent.nChM, fluctuationCalculatorTrackChP, fluctuationCalculatorTrackChM, fluctuationCalculatorTrackChT, fluctuationCalculatorTrackChN); - processSpecies.template operator()(groupTrack.cfgThresholdPtTofPr.value, PDG_t::kProton, PDG_t::kProtonBar, holderEvent.nChP, holderEvent.nChM, fluctuationCalculatorTrackChP, fluctuationCalculatorTrackChM, fluctuationCalculatorTrackChT, fluctuationCalculatorTrackChN); - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - processSpecies.template operator()(groupTrack.cfgThresholdPtTofKa.value, PDG_t::kKPlus, PDG_t::kKMinus, holderEvent.nKaP, holderEvent.nKaM, fluctuationCalculatorTrackKaP, fluctuationCalculatorTrackKaM, fluctuationCalculatorTrackKaT, fluctuationCalculatorTrackKaN); - } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - processSpecies.template operator()(groupTrack.cfgThresholdPtTofPr.value, PDG_t::kProton, PDG_t::kProtonBar, holderEvent.nPrP, holderEvent.nPrM, fluctuationCalculatorTrackPrP, fluctuationCalculatorTrackPrM, fluctuationCalculatorTrackPrT, fluctuationCalculatorTrackPrN); + fillByPidStrategy.template operator()(); + fillByPidStrategy.template operator()(); + }}; + + if (std::cmp_greater(DataModeValue == DataMode::McMcParticle ? holderMcParticle.charge : holderTrack.sign, 0)) { + fillByChargeSpecies.template operator()(); + } else { + fillByChargeSpecies.template operator()(); } } - template - bool initTrack(const T& track) + template + requires IsValidEnumValue && (DataModeValue != DataMode::McMcParticle) + void fillQaPhiByParticleSpeciesAll() { - holderTrack.clear(); - holderTrack.dcaXY = track.dcaXY(); - holderTrack.dcaZ = track.dcaZ(); - holderTrack.sign = track.sign(); - holderTrack.p = track.p(); - holderTrack.pt = track.pt(); - holderTrack.eta = track.eta(); - holderTrack.phi = track.phi(); - holderTrack.hasTpcPid = (track.hasTPC() && track.tpcSignal() > 0.); - if (holderTrack.hasTpcPid) { - holderTrack.tpcNSigmaPi = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPi() - getShiftNSigmaPid()); - holderTrack.tpcNSigmaKa = HolderTrack::truncateNSigmaPid(track.tpcNSigmaKa() - getShiftNSigmaPid()); - holderTrack.tpcNSigmaPr = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPr() - getShiftNSigmaPid()); - } else { - holderTrack.tpcNSigmaPi = holderTrack.tpcNSigmaKa = holderTrack.tpcNSigmaPr = HolderTrack::truncateNSigmaPid(HolderTrack::TruncationAbsNSigmaPid); - } - holderTrack.hasTofPid = (track.hasTOF() && track.beta() > 0.); - if (holderTrack.hasTofPid) { - holderTrack.tofNSigmaPi = HolderTrack::truncateNSigmaPid(track.tofNSigmaPi() - getShiftNSigmaPid()); - holderTrack.tofNSigmaKa = HolderTrack::truncateNSigmaPid(track.tofNSigmaKa() - getShiftNSigmaPid()); - holderTrack.tofNSigmaPr = HolderTrack::truncateNSigmaPid(track.tofNSigmaPr() - getShiftNSigmaPid()); - } else { - holderTrack.tofNSigmaPi = holderTrack.tofNSigmaKa = holderTrack.tofNSigmaPr = HolderTrack::truncateNSigmaPid(HolderTrack::TruncationAbsNSigmaPid); + if (!cgAnalysis.cfgFlagsQaPhi.value.get(toI(ParticleSpeciesAllValue))) { + return; } - if (holderTrack.hasTpcPid && holderTrack.hasTofPid) { - holderTrack.tpcTofNSigmaPi = HolderTrack::truncateNSigmaPid(std::copysign(std::hypot(holderTrack.tpcNSigmaPi, holderTrack.tofNSigmaPi), holderTrack.tpcNSigmaPi + holderTrack.tofNSigmaPi)); - holderTrack.tpcTofNSigmaKa = HolderTrack::truncateNSigmaPid(std::copysign(std::hypot(holderTrack.tpcNSigmaKa, holderTrack.tofNSigmaKa), holderTrack.tpcNSigmaKa + holderTrack.tofNSigmaKa)); - holderTrack.tpcTofNSigmaPr = HolderTrack::truncateNSigmaPid(std::copysign(std::hypot(holderTrack.tpcNSigmaPr, holderTrack.tofNSigmaPr), holderTrack.tpcNSigmaPr + holderTrack.tofNSigmaPr)); + + const auto fillByChargeSpecies{ + [this] + requires IsValidEnumValue + () -> void { + const auto fillByPidStrategy{ + [this] + requires IsValidEnumValue + () -> void { + if constexpr (DataModeValue == DataMode::McTrack) { + if (isPid() && isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { + hrQaPhi.fill(C_CS("hCentralityPtEtaPhi_mc") + C_SV(getName(PidStrategyValue)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.phi); + } + } else { // DataModeValue == DataMode::RawTrack + if (isPid(PidStrategyValue), ParticleSpeciesAllValue>(false)) { + hrQaPhi.fill(C_CS("hCentralityPtEtaPhi_") + C_SV(getName(PidStrategyValue)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.phi); + } + } + }}; + + fillByPidStrategy.template operator()(); + fillByPidStrategy.template operator()(); + }}; + + if (std::cmp_greater(holderTrack.sign, 0)) { + fillByChargeSpecies.template operator()(); } else { - holderTrack.tpcTofNSigmaPi = holderTrack.tpcTofNSigmaKa = holderTrack.tpcTofNSigmaPr = HolderTrack::truncateNSigmaPid(HolderTrack::TruncationAbsNSigmaPid); + fillByChargeSpecies.template operator()(); } + } - if constexpr (doInitingEvent) { - if (holderTrack.sign > 0) { - if (track.isPrimaryTrack()) { - holderEvent.nGlobalTracksP++; - if (track.isPVContributor()) { - holderEvent.nPvContributorsP++; - } - holderEvent.meanDcaXyP += holderTrack.dcaXY; - holderEvent.meanSquareDcaXyP += std::pow(holderTrack.dcaXY, 2.); - holderEvent.meanDcaZP += holderTrack.dcaZ; - holderEvent.meanSquareDcaZP += std::pow(holderTrack.dcaZ, 2.); - if (holderTrack.hasTofPid) { - holderEvent.nTofBetaP++; - } - } - } else if (holderTrack.sign < 0) { - if (track.isPrimaryTrack()) { - holderEvent.nGlobalTracksM++; - if (track.isPVContributor()) { - holderEvent.nPvContributorsM++; - } - holderEvent.meanDcaXyM += holderTrack.dcaXY; - holderEvent.meanSquareDcaXyM += std::pow(holderTrack.dcaXY, 2.); - holderEvent.meanDcaZM += holderTrack.dcaZ; - holderEvent.meanSquareDcaZM += std::pow(holderTrack.dcaZ, 2.); - if (holderTrack.hasTofPid) { - holderEvent.nTofBetaM++; - } - } - } + template + requires IsValidEnumValue && (DataModeValue != DataMode::McMcParticle) + void fillQaPidByParticleSpeciesAll(const T& track) + { + if (!cgAnalysis.cfgFlagsQaPid.value.get(toI(ParticleSpeciesAllValue))) { + return; } - if constexpr (doInitingEvent) { - if (groupAnalysis.cfgFlagQaRun.value && track.isPrimaryTrack()) { - if (holderTrack.sign > 0.) { - hrQaRun.fill(HIST("QaRun/pRunIndexItsNCls_p"), holderEvent.runIndex, track.itsNCls()); - hrQaRun.fill(HIST("QaRun/pRunIndexItsChi2NCls_p"), holderEvent.runIndex, track.itsChi2NCl()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNCls_p"), holderEvent.runIndex, track.tpcNClsFound()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcChi2NCls_p"), holderEvent.runIndex, track.tpcChi2NCl()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNClsSharedRatio_p"), holderEvent.runIndex, track.tpcFractionSharedCls()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNCrossedRows_p"), holderEvent.runIndex, track.tpcNClsCrossedRows()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNCrossedRowsRatio_p"), holderEvent.runIndex, track.tpcCrossedRowsOverFindableCls()); - hrQaRun.fill(HIST("QaRun/pRunIndexDcaXy_p"), holderEvent.runIndex, holderTrack.dcaXY); - hrQaRun.fill(HIST("QaRun/pRunIndexDcaZ_p"), holderEvent.runIndex, holderTrack.dcaZ); - hrQaRun.fill(HIST("QaRun/pRunIndexPt_p"), holderEvent.runIndex, holderTrack.pt); - hrQaRun.fill(HIST("QaRun/pRunIndexEta_p"), holderEvent.runIndex, holderTrack.eta); - hrQaRun.fill(HIST("QaRun/pRunIndexPhi_p"), holderEvent.runIndex, holderTrack.phi); - if (holderTrack.hasTpcPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcDeDx_p"), holderEvent.runIndex, track.tpcSignal()); - if (std::abs(holderTrack.tpcNSigmaPi) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNSigmaPi_p"), holderEvent.runIndex, holderTrack.tpcNSigmaPi); - } - if (std::abs(holderTrack.tpcNSigmaKa) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNSigmaKa_p"), holderEvent.runIndex, holderTrack.tpcNSigmaKa); - } - if (std::abs(holderTrack.tpcNSigmaPr) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNSigmaPr_p"), holderEvent.runIndex, holderTrack.tpcNSigmaPr); - } - } - if (holderTrack.hasTofPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofInverseBeta_p"), holderEvent.runIndex, 1. / track.beta()); - if (std::abs(holderTrack.tofNSigmaPi) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofNSigmaPi_p"), holderEvent.runIndex, holderTrack.tofNSigmaPi); - } - if (std::abs(holderTrack.tofNSigmaKa) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofNSigmaKa_p"), holderEvent.runIndex, holderTrack.tofNSigmaKa); - } - if (std::abs(holderTrack.tofNSigmaPr) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofNSigmaPr_p"), holderEvent.runIndex, holderTrack.tofNSigmaPr); - } - } - } else if (holderTrack.sign < 0.) { - hrQaRun.fill(HIST("QaRun/pRunIndexItsNCls_m"), holderEvent.runIndex, track.itsNCls()); - hrQaRun.fill(HIST("QaRun/pRunIndexItsChi2NCls_m"), holderEvent.runIndex, track.itsChi2NCl()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNCls_m"), holderEvent.runIndex, track.tpcNClsFound()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcChi2NCls_m"), holderEvent.runIndex, track.tpcChi2NCl()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNClsSharedRatio_m"), holderEvent.runIndex, track.tpcFractionSharedCls()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNCrossedRows_m"), holderEvent.runIndex, track.tpcNClsCrossedRows()); - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNCrossedRowsRatio_m"), holderEvent.runIndex, track.tpcCrossedRowsOverFindableCls()); - hrQaRun.fill(HIST("QaRun/pRunIndexDcaXy_m"), holderEvent.runIndex, holderTrack.dcaXY); - hrQaRun.fill(HIST("QaRun/pRunIndexDcaZ_m"), holderEvent.runIndex, holderTrack.dcaZ); - hrQaRun.fill(HIST("QaRun/pRunIndexPt_m"), holderEvent.runIndex, holderTrack.pt); - hrQaRun.fill(HIST("QaRun/pRunIndexEta_m"), holderEvent.runIndex, holderTrack.eta); - hrQaRun.fill(HIST("QaRun/pRunIndexPhi_m"), holderEvent.runIndex, holderTrack.phi); - if (holderTrack.hasTpcPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcDeDx_m"), holderEvent.runIndex, track.tpcSignal()); - if (std::abs(holderTrack.tpcNSigmaPi) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNSigmaPi_m"), holderEvent.runIndex, holderTrack.tpcNSigmaPi); - } - if (std::abs(holderTrack.tpcNSigmaKa) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNSigmaKa_m"), holderEvent.runIndex, holderTrack.tpcNSigmaKa); - } - if (std::abs(holderTrack.tpcNSigmaPr) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTpcNSigmaPr_m"), holderEvent.runIndex, holderTrack.tpcNSigmaPr); - } - } - if (holderTrack.hasTofPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofInverseBeta_m"), holderEvent.runIndex, 1. / track.beta()); - if (std::abs(holderTrack.tofNSigmaPi) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofNSigmaPi_m"), holderEvent.runIndex, holderTrack.tofNSigmaPi); + if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { + if (isPid(PidStrategy::Tpc), ParticleSpeciesAll::All>(false)) { + hrQaPid.fill(C_CS("hCentralityPOverQEtaTpcLnDeDx"), holderEvent.centralityCalibration, track.p() / holderTrack.sign, holderTrack.eta, std::log(track.tpcSignal())); + } + if (isPid(PidStrategy::TpcTof), ParticleSpeciesAll::All>(false)) { + hrQaPid.fill(C_CS("hCentralityPOverQEtaTofInverseBeta"), holderEvent.centralityCalibration, track.p() / holderTrack.sign, holderTrack.eta, 1. / track.beta()); + } + } else { + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; + const auto fillByChargeSpecies{ + [this] + requires IsValidEnumValue + () -> void { + if constexpr (DataModeValue == DataMode::McTrack) { + if (isPid()) { + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_mc") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]); + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_mc") + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]); } - if (std::abs(holderTrack.tofNSigmaKa) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofNSigmaKa_m"), holderEvent.runIndex, holderTrack.tofNSigmaKa); + } else { // DataModeValue == DataMode::RawTrack + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]); + if (isPid(false)) { + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tpc)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(Detector::Tof)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]); } - if (std::abs(holderTrack.tofNSigmaPr) < HolderTrack::TruncationAbsNSigmaPid) { - hrQaRun.fill(HIST("QaRun/pRunIndexTofNSigmaPr_m"), holderEvent.runIndex, holderTrack.tofNSigmaPr); + if (isPid(false)) { + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(Detector::Tof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(Detector::Tpc)) + C_SV(getName(ParticleSpeciesAllValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.nsSigmaPid[toI(Detector::Tof)][IndexParticleSpecies]); } + hrQaPid.fill(C_CS("hCentralityPtEta") + C_SV(getName(PidStrategy::TpcTof)) + C_CS("NSigma") + C_SV(getName(ParticleSpeciesAllValue)) + C_CS("_") + C_SV(getName(ChargeSpeciesValue)), holderEvent.centralityCalibration, holderTrack.pt, holderTrack.eta, holderTrack.getNSigmaPidCombined(IndexParticleSpecies)); } - } + }}; + + if (std::cmp_greater(holderTrack.sign, 0)) { + fillByChargeSpecies.template operator()(); + } else { + fillByChargeSpecies.template operator()(); } } + } + + template + requires IsValidEnumValue + void fillCalculationYieldByParticleSpecies() + { + if (!cgAnalysis.cfgFlagsCalculationYield.value.get(toI(ParticleSpeciesValue))) { + return; + } + + const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr noexcept -> std::int32_t { + if constexpr (DataModeValue == DataMode::McMcParticle) { + return chargeMcParticle; + } else { + return signTrack; + } + }(holderMcParticle.charge, holderTrack.sign)}; + + const auto fillByChargeSpecies{ + [this] + requires IsValidEnumValue + () -> void { + if constexpr (DataModeValue == DataMode::McMcParticle) { + if (isPid(ParticleSpeciesValue), ChargeSpeciesValue>()) { + hrCalculationYield.fill(C_CS("hVzCentralityPtMcEtaMc_mc") + C_SV(getName(ParticleSpeciesValue)) + C_SV(getName(ChargeSpeciesValue)), cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); + } + } else { + const auto fillByPidStrategy{ + [this] + requires IsValidEnumValue + () -> void { + if constexpr (DataModeValue == DataMode::McTrack) { + if (isPid(ParticleSpeciesValue), ChargeSpeciesValue>() && isPid(PidStrategyValue), getValue(ParticleSpeciesValue)>(cgTrack.cfgFlagRejectionOthers.value)) { + if (cgTrack.cfgFlagMcParticleMomentum.value) { + hrCalculationYield.fill(C_CS("hVzCentralityPtMcEtaMc_mc") + C_SV(getName(PidStrategyValue)) + C_SV(getName(ParticleSpeciesValue)) + C_SV(getName(ChargeSpeciesValue)), cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); + } else { + hrCalculationYield.fill(C_CS("hVzCentralityPtEta_mc") + C_SV(getName(PidStrategyValue)) + C_SV(getName(ParticleSpeciesValue)) + C_SV(getName(ChargeSpeciesValue)), cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); + } + } + } else { // DataModeValue == DataMode::RawTrack + if (isPid(PidStrategyValue), getValue(ParticleSpeciesValue)>(cgTrack.cfgFlagRejectionOthers.value)) { + hrCalculationYield.fill(C_CS("hVzCentralityPtEta_") + C_SV(getName(PidStrategyValue)) + C_SV(getName(ParticleSpeciesValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); + } + } + }}; - if constexpr (!doInitingEvent) { - if (groupAnalysis.cfgFlagQaTrack.value && track.isPrimaryTrack()) { - if (holderTrack.sign > 0) { - hrQaTrack.fill(HIST("QaTrack/hItsNCls_p"), track.itsNCls()); - hrQaTrack.fill(HIST("QaTrack/hItsChi2NCls_p"), track.itsChi2NCl()); - hrQaTrack.fill(HIST("QaTrack/hTpcNClsNClsShared_p"), track.tpcNClsFound(), track.tpcNClsShared()); - hrQaTrack.fill(HIST("QaTrack/hTpcChi2NCls_p"), track.tpcChi2NCl()); - hrQaTrack.fill(HIST("QaTrack/hTpcNClsFindableNCrossedRows_p"), track.tpcNClsFindable(), track.tpcNClsCrossedRows()); - } else if (holderTrack.sign < 0) { - hrQaTrack.fill(HIST("QaTrack/hItsNCls_m"), track.itsNCls()); - hrQaTrack.fill(HIST("QaTrack/hItsChi2NCls_m"), track.itsChi2NCl()); - hrQaTrack.fill(HIST("QaTrack/hTpcNClsNClsShared_m"), track.tpcNClsFound(), track.tpcNClsShared()); - hrQaTrack.fill(HIST("QaTrack/hTpcChi2NCls_m"), track.tpcChi2NCl()); - hrQaTrack.fill(HIST("QaTrack/hTpcNClsFindableNCrossedRows_m"), track.tpcNClsFindable(), track.tpcNClsCrossedRows()); + fillByPidStrategy.template operator()(); + fillByPidStrategy.template operator()(); } - } + }}; + + if (std::cmp_greater(chargeSign, 0)) { + fillByChargeSpecies.template operator()(); + } else { + fillByChargeSpecies.template operator()(); } + } - if (!isGoodTrack(track)) { - return false; + template + requires IsValidEnumValue + void fillCalculationPurityByParticleSpecies() + { + if (!cgAnalysis.cfgFlagsCalculationPurity.value.get(toI(ParticleSpeciesValue))) { + return; } - if constexpr (!doInitingEvent) { - if (groupAnalysis.cfgFlagQaDca.value) { - if (holderTrack.sign > 0) { - hrQaDca.fill(HIST("QaDca/hPtDcaXy_p"), holderTrack.pt, holderTrack.dcaXY); - hrQaDca.fill(HIST("QaDca/pPtDcaXy_p"), holderTrack.pt, holderTrack.dcaXY); - hrQaDca.fill(HIST("QaDca/hPtDcaZ_p"), holderTrack.pt, holderTrack.dcaZ); - hrQaDca.fill(HIST("QaDca/pPtDcaZ_p"), holderTrack.pt, holderTrack.dcaZ); - } else if (holderTrack.sign < 0) { - hrQaDca.fill(HIST("QaDca/hPtDcaXy_m"), holderTrack.pt, holderTrack.dcaXY); - hrQaDca.fill(HIST("QaDca/pPtDcaXy_m"), holderTrack.pt, holderTrack.dcaXY); - hrQaDca.fill(HIST("QaDca/hPtDcaZ_m"), holderTrack.pt, holderTrack.dcaZ); - hrQaDca.fill(HIST("QaDca/pPtDcaZ_m"), holderTrack.pt, holderTrack.dcaZ); - } - } + const auto fillByChargeSpecies{ + [this] + requires IsValidEnumValue + () -> void { + const auto fillByPidStrategy{ + [this] + requires IsValidEnumValue + () -> void { + if (isPid(PidStrategyValue), getValue(ParticleSpeciesValue)>(cgTrack.cfgFlagRejectionOthers.value)) { + hrCalculationPurity.fill(C_CS("pCentralityPtEtaPurity") + C_SV(getName(PidStrategyValue)) + C_SV(getName(ParticleSpeciesValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centrality, holderTrack.pt, holderTrack.eta, isPid(ParticleSpeciesValue), ChargeSpeciesValue>() ? 1. : 0.); + } + }}; + + fillByPidStrategy.template operator()(); + fillByPidStrategy.template operator()(); + }}; + + if (std::cmp_greater(holderTrack.sign, 0)) { + fillByChargeSpecies.template operator()(); + } else { + fillByChargeSpecies.template operator()(); } + } - if (!isGoodDca()) { - return false; + template + requires IsValidEnumValue + void fillCalculationFractionPrimaryByParticleSpecies(const MP& mcParticle) + { + if (!cgAnalysis.cfgFlagsCalculationFractionPrimary.value.get(toI(ParticleSpeciesValue))) { + return; } - if constexpr (!doInitingEvent) { - if ((groupAnalysis.cfgFlagQaAcceptance.value || groupAnalysis.cfgFlagQaAcceptancePi.value || groupAnalysis.cfgFlagQaAcceptanceKa.value || groupAnalysis.cfgFlagQaAcceptancePr.value) && ((holderTrack.eta > 0. && holderEvent.vz > groupEvent.cfgCutMaxAbsVertexZ.value - 1.) || (holderTrack.eta < 0. && holderEvent.vz < -groupEvent.cfgCutMaxAbsVertexZ.value + 1.)) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagQaAcceptance.value) { - if (holderTrack.sign > 0) { - hrQaAcceptance.fill(HIST("QaAcceptance/hEtaPt_tpcEdgeP"), holderTrack.eta, holderTrack.pt); - } else if (holderTrack.sign < 0) { - hrQaAcceptance.fill(HIST("QaAcceptance/hEtaPt_tpcEdgeM"), holderTrack.eta, holderTrack.pt); - } - if (holderTrack.hasTofPid) { - if (holderTrack.sign > 0) { - hrQaAcceptance.fill(HIST("QaAcceptance/hEtaPt_tpcTofEdgeP"), holderTrack.eta, holderTrack.pt); - } else if (holderTrack.sign < 0) { - hrQaAcceptance.fill(HIST("QaAcceptance/hEtaPt_tpcTofEdgeM"), holderTrack.eta, holderTrack.pt); + const auto fillByChargeSpecies{ + [this, &mcParticle] + requires IsValidEnumValue + () -> void { + const auto fillByPidStrategy{ + [this, &mcParticle] + requires IsValidEnumValue + () -> void { + if (isPid(ParticleSpeciesValue), ChargeSpeciesValue>() && isPid(PidStrategyValue), getValue(ParticleSpeciesValue)>(cgTrack.cfgFlagRejectionOthers.value)) { + hrCalculationFractionPrimary.fill(C_CS("pCentralityPtEtaFractionPrimary") + C_SV(getName(PidStrategyValue)) + C_SV(getName(ParticleSpeciesValue)) + C_SV(getName(ChargeSpeciesValue)), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); } + }}; + + fillByPidStrategy.template operator()(); + fillByPidStrategy.template operator()(); + }}; + + if (std::cmp_greater(holderTrack.sign, 0)) { + fillByChargeSpecies.template operator()(); + } else { + fillByChargeSpecies.template operator()(); + } + } + + void initCalculationFluctuation() + { + for (std::int32_t const& iParticleNumber : std::views::iota(0, NEs)) { + if (static_cast(cgAnalysis.cfgFlagsCalculationFluctuation.value.get(iParticleNumber))) { + for (std::int32_t const& iChargeNumber : std::views::iota(0, NEs)) { + if (doProcessMc.value) { + holderMember.fluctuationCalculatorsTrackMcParticle[iParticleNumber][iChargeNumber]->clear(); } + holderMember.fluctuationCalculatorsTrackTrack[iParticleNumber][iChargeNumber]->clear(); } + } + } + } - if (groupAnalysis.cfgFlagQaAcceptancePi.value) { - switch (isPid(false)) { - case 1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcEdgePiP"), track.rapidity(constants::physics::MassPiPlus), holderTrack.pt); - break; - case -1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcEdgePiM"), track.rapidity(constants::physics::MassPiPlus), holderTrack.pt); - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(false)) { - case 1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcTofEdgePiP"), track.rapidity(constants::physics::MassPiPlus), holderTrack.pt); - break; - case -1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcTofEdgePiM"), track.rapidity(constants::physics::MassPiPlus), holderTrack.pt); - break; + template + requires IsValidEnumValue + void calculateFluctuationByParticleNumber() + { + if (!cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumberValue))) { + return; + } + + const std::int32_t chargeSign{[](const std::int32_t chargeMcParticle, const std::int32_t signTrack) constexpr noexcept -> std::int32_t { + if constexpr (DataModeValue == DataMode::McMcParticle) { + return chargeMcParticle; + } else { + return signTrack; + } + }(holderMcParticle.charge, holderTrack.sign)}; + + const bool doUseMcParticleMomentum{[](const bool flagMcParticleMomentum) constexpr noexcept -> bool { + if constexpr (DataModeValue == DataMode::McMcParticle) { + return true; + } else if constexpr (DataModeValue == DataMode::McTrack) { + return flagMcParticleMomentum; + } else { // DataModeValue == DataMode::RawTrack + return false; + } + }(cgTrack.cfgFlagMcParticleMomentum.value)}; + const ChargeSpecies chargeSpecies{std::cmp_greater(chargeSign, 0) ? ChargeSpecies::Plus : ChargeSpecies::Minus}; + if (isGoodMomentum(ParticleNumberValue)>(doUseMcParticleMomentum)) { + if constexpr (DataModeValue == DataMode::McMcParticle) { + ++holderDerivedData.nsMcParticles[toI(chargeSpecies)]; + } else { + ++holderDerivedData.nsTracks[toI(chargeSpecies)]; + } + } + + const auto calculateByParticleSpecies{ + [this, chargeSign, doUseMcParticleMomentum] + requires IsValidEnumValue && (getValue(ParticleNumberValue) == ParticleSpeciesAll::All || getValue(ParticleNumberValue) == getValue(ParticleSpeciesValue)) + () -> bool { + if (!isGoodMomentum(ParticleSpeciesValue)>(doUseMcParticleMomentum) || (DataModeValue != DataMode::RawTrack && (std::cmp_greater(chargeSign, 0) ? !isPid(ParticleSpeciesValue), ChargeSpecies::Plus>() : !isPid(ParticleSpeciesValue), ChargeSpecies::Minus>()))) { + return false; + } + + const bool doUseTofPid{[](const bool doUseMcParticleMomentumValue, const double ptMcParticle, const double ptTrack, const double thresholdPtTofPid) constexpr noexcept -> bool { + if constexpr (DataModeValue == DataMode::McMcParticle) { + return ptMcParticle >= thresholdPtTofPid; + } else if constexpr (DataModeValue == DataMode::McTrack) { + return (doUseMcParticleMomentumValue ? ptMcParticle : ptTrack) >= thresholdPtTofPid; + } else { // DataModeValue == DataMode::RawTrack + return ptTrack >= thresholdPtTofPid; + } + }(doUseMcParticleMomentum, holderMcParticle.pt, holderTrack.pt, cgTrack.cfgThresholdsPtTofPid.value.get(toI(ParticleSpeciesValue)))}; + if constexpr (DataModeValue != DataMode::McMcParticle) { + if (!(doUseTofPid ? isPid(PidStrategy::TpcTof), getValue(ParticleSpeciesValue)>(cgTrack.cfgFlagRejectionOthers.value) : isPid(PidStrategy::Tpc), getValue(ParticleSpeciesValue)>(cgTrack.cfgFlagRejectionOthers.value))) { + return false; + } + } + + const auto calculateByChargeSpecies{ + [this, chargeSign, doUseMcParticleMomentum, doUseTofPid] + requires IsValidEnumValue + () -> void { + const double efficiency{doUseTofPid ? getEfficiency(doUseMcParticleMomentum) : getEfficiency(doUseMcParticleMomentum)}; + const auto fill{ + [this, efficiency]() -> void { + std::array, NEs>, NEs>& fluctuationCalculatorsTrack{DataModeValue == DataMode::McMcParticle ? holderMember.fluctuationCalculatorsTrackMcParticle : holderMember.fluctuationCalculatorsTrackTrack}; + if constexpr (ChargeSpeciesValue == ChargeSpecies::Plus) { + fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Plus)]->fill(1., efficiency); + fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Net)]->fill(1., efficiency); + } else { // ChargeSpeciesValue == ChargeSpecies::Minus + fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Minus)]->fill(1., efficiency); + fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Net)]->fill(-1., efficiency); + } + fluctuationCalculatorsTrack[toI(ParticleNumberValue)][toI(ChargeNumber::Total)]->fill(1., efficiency); + }}; + if constexpr (DataModeValue == DataMode::McMcParticle) { + ++holderMcEvent.numbers[toI(ParticleNumberValue)][toI(ChargeSpeciesValue)]; + if (std::uniform_real_distribution{}(holderMember.engineRandom) < efficiency) { + ++holderMcEvent.numbersEff[toI(ParticleNumberValue)][toI(ChargeSpeciesValue)]; + fill(); + } + holderDerivedData.signedEfficienciesMcParticle.push_back(HolderDerivedData::convert(std::copysign(std::numeric_limits::max(), chargeSign) * efficiency)); + } else { + ++holderEvent.numbers[toI(ParticleNumberValue)][toI(ChargeSpeciesValue)]; + fill(); + holderDerivedData.signedEfficienciesTrack.push_back(HolderDerivedData::convert(std::copysign(std::numeric_limits::max(), chargeSign) * efficiency)); } - } + }}; + + if (std::cmp_greater(chargeSign, 0)) { + calculateByChargeSpecies.template operator()(); + } else { + calculateByChargeSpecies.template operator()(); } + return true; + }}; - if (groupAnalysis.cfgFlagQaAcceptanceKa.value) { - switch (isPid(false)) { - case 1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcEdgeKaP"), track.rapidity(constants::physics::MassKPlus), holderTrack.pt); - break; - case -1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcEdgeKaM"), track.rapidity(constants::physics::MassKPlus), holderTrack.pt); - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(false)) { - case 1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcTofEdgeKaP"), track.rapidity(constants::physics::MassKPlus), holderTrack.pt); - break; - case -1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcTofEdgeKaM"), track.rapidity(constants::physics::MassKPlus), holderTrack.pt); - break; - } + if constexpr (getValue(ParticleNumberValue) == ParticleSpeciesAll::All) { + if (!calculateByParticleSpecies.template operator()() && !calculateByParticleSpecies.template operator()()) { + calculateByParticleSpecies.template operator()(); + } + } else { + calculateByParticleSpecies.template operator()(getValue(ParticleNumberValue))>(); + } + } + + template + requires IsValidEnumValue + void fillCalculationFluctuationByParticleNumber() + { + if (!cgAnalysis.cfgFlagsCalculationFluctuation.value.get(toI(ParticleNumberValue))) { + return; + } + + if (doProcessMc.value) { + hrCalculationFluctuation.fill(C_CS("hCentralityN") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeSpecies::Plus)) + C_CS("N") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeSpecies::Minus)) + C_CS("_mc"), holderEvent.centrality, holderMcEvent.numbers[toI(ParticleNumberValue)][toI(ChargeSpecies::Plus)], holderMcEvent.numbers[toI(ParticleNumberValue)][toI(ChargeSpecies::Minus)]); + hrCalculationFluctuation.fill(C_CS("hCentralityN") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeSpecies::Plus)) + C_CS("N") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeSpecies::Minus)) + C_CS("_mcEff"), holderEvent.centrality, holderMcEvent.numbersEff[toI(ParticleNumberValue)][toI(ChargeSpecies::Plus)], holderMcEvent.numbersEff[toI(ParticleNumberValue)][toI(ChargeSpecies::Minus)]); + } + hrCalculationFluctuation.fill(C_CS("hCentralityN") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeSpecies::Plus)) + C_CS("N") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeSpecies::Minus)), holderEvent.centrality, holderEvent.numbers[toI(ParticleNumberValue)][toI(ChargeSpecies::Plus)], holderEvent.numbers[toI(ParticleNumberValue)][toI(ChargeSpecies::Minus)]); + + const auto fillByChargeNumber{ + [this] + requires IsValidEnumValue + () -> void { + if (doProcessMc.value) { + const std::array products{holderMember.fluctuationCalculatorsTrackMcParticle[toI(ParticleNumberValue)][toI(ChargeNumberValue)]->getProducts()}; + for (std::int32_t const& iOrderKey : std::views::iota(0, fluctuation_calculator_base::NOrderKeys)) { + hrCalculationFluctuation.fill(C_CS("hFluctuationCalculator") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeNumberValue)) + C_CS("_mc"), holderEvent.centrality, holderEvent.indexSubgroup, iOrderKey, products[iOrderKey]); } } - if (groupAnalysis.cfgFlagQaAcceptancePr.value) { - switch (isPid(false)) { - case 1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcEdgePrP"), track.rapidity(constants::physics::MassProton), holderTrack.pt); - break; - case -1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcEdgePrM"), track.rapidity(constants::physics::MassProton), holderTrack.pt); - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(false)) { - case 1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcTofEdgePrP"), track.rapidity(constants::physics::MassProton), holderTrack.pt); - break; - case -1: - hrQaAcceptance.fill(HIST("QaAcceptance/hRapidityPt_tpcTofEdgePrM"), track.rapidity(constants::physics::MassProton), holderTrack.pt); - break; - } - } + const std::array products{holderMember.fluctuationCalculatorsTrackTrack[toI(ParticleNumberValue)][toI(ChargeNumberValue)]->getProducts()}; + for (std::int32_t const& iOrderKey : std::views::iota(0, fluctuation_calculator_base::NOrderKeys)) { + hrCalculationFluctuation.fill(C_CS("hFluctuationCalculator") + C_SV(getName(ParticleNumberValue)) + C_SV(getName(ChargeNumberValue)), holderEvent.centrality, holderEvent.indexSubgroup, iOrderKey, products[iOrderKey]); } - } + }}; + + fillByChargeNumber.template operator()(); + fillByChargeNumber.template operator()(); + fillByChargeNumber.template operator()(); + fillByChargeNumber.template operator()(); + } + + template + bool setTrack(const T& track) + { + if (std::cmp_not_equal(std::abs(track.sign()), 1)) { + return false; } - if constexpr (!doInitingEvent) { - if (groupAnalysis.cfgFlagQaPid.value && holderTrack.hasTpcPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPOverQEtaTpcLnDeDx"), holderEvent.centrality, holderTrack.p / holderTrack.sign, holderTrack.eta, track.tpcSignal()); - if (holderTrack.hasTofPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPOverQEtaTofInverseBeta"), holderEvent.centrality, holderTrack.p / holderTrack.sign, holderTrack.eta, 1. / track.beta()); - } - } + holderTrack.clear(); + holderTrack.dcas[toI(DcaAxis::Xy)] = track.dcaXY(); + holderTrack.dcas[toI(DcaAxis::Z)] = track.dcaZ(); + holderTrack.sign = track.sign(); + holderTrack.pt = track.pt(); + holderTrack.eta = track.eta(); + holderTrack.phi = track.phi(); + holderTrack.havePid[toI(Detector::Tpc)] = (track.hasTPC() && track.tpcSignal() > 0.); + holderTrack.havePid[toI(Detector::Tof)] = (track.hasTOF() && track.beta() > 0.); + if (holderTrack.havePid[toI(Detector::Tpc)]) { + holderTrack.nsSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Pion)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPi(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Kaon)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaKa(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tpc)][toI(ParticleSpecies::Proton)] = HolderTrack::truncateNSigmaPid(track.tpcNSigmaPr(), getShiftNSigmaPid()); + } + if (holderTrack.havePid[toI(Detector::Tof)]) { + holderTrack.nsSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Pion)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaPi(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Kaon)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaKa(), getShiftNSigmaPid()); + holderTrack.nsSigmaPid[toI(Detector::Tof)][toI(ParticleSpecies::Proton)] = HolderTrack::truncateNSigmaPid(track.tofNSigmaPr(), getShiftNSigmaPid()); } return true; } - template + template bool initMcParticle(const MP& mcParticle) { holderMcParticle.clear(); holderMcParticle.pdgCode = mcParticle.pdgCode(); - const TParticlePDG* particlePdg = pdg->GetParticle(mcParticle.pdgCode()); + const TParticlePDG* const particlePdg{pdg->GetParticle(mcParticle.pdgCode())}; if (particlePdg) { - holderMcParticle.charge = std::llrint(particlePdg->Charge()); + holderMcParticle.charge = static_cast(std::rint(particlePdg->Charge())); } else { switch (std::abs(holderMcParticle.pdgCode) / 100000000) { case 10: holderMcParticle.charge = holderMcParticle.pdgCode / 10000 % 1000; break; + default: + break; } } - holderMcParticle.pt = mcParticle.pt(); - holderMcParticle.eta = mcParticle.eta(); - holderMcParticle.phi = mcParticle.phi(); - if (!isGoodMcParticle(mcParticle)) { + if (std::cmp_equal(holderMcParticle.charge, 0)) { return false; } + holderMcParticle.pt = mcParticle.pt(); + holderMcParticle.eta = mcParticle.eta(); + return true; } - template - bool initEvent(const C& collision, const Ts& tracks) + template + bool initTrack(const T& track) { - holderEvent.clear(); - holderEvent.vz = collision.posZ(); - switch (groupEvent.cfgIndexDefinitionCentrality.value) { - case static_cast(CentralityDefinition::kFt0a): - holderEvent.centrality = collision.centFT0A(); - break; - case static_cast(CentralityDefinition::kFt0c): - holderEvent.centrality = collision.centFT0C(); - break; - default: - holderEvent.centrality = collision.centFT0M(); - break; - } - - hrCounter.fill(HIST("hNEvents"), 0.); - - if (!collision.has_foundBC()) { - hrCounter.fill(HIST("hNEvents"), 2.); + if (!setTrack(track)) { return false; } - const auto& bc = collision.template bc_as(); - holderEvent.runNumber = bc.runNumber(); - - if (holderCcdb.runNumbersIndicesGroupIndices.find(holderEvent.runNumber) == holderCcdb.runNumbersIndicesGroupIndices.end()) { - hrCounter.fill(HIST("hNEvents"), 2.); - return false; - } + if constexpr (DoInitEvent) { + if (track.isPrimaryTrack()) { + const std::int32_t indexChargeSpecies{toI(std::cmp_greater(holderTrack.sign, 0) ? ChargeSpecies::Plus : ChargeSpecies::Minus)}; + ++holderEvent.nsGlobalTracks[indexChargeSpecies]; + if (track.isPVContributor()) { + ++holderEvent.nsPvContributors[indexChargeSpecies]; + } + for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { + holderEvent.measuresDca[toI(DcaMeasure::Mean)][iDcaAxis][indexChargeSpecies] += holderTrack.dcas[iDcaAxis]; + holderEvent.measuresDca[toI(DcaMeasure::Sigma)][iDcaAxis][indexChargeSpecies] += std::pow(holderTrack.dcas[iDcaAxis], 2.); + } + if (holderTrack.havePid[toI(Detector::Tof)]) { + ++holderEvent.nsTofBeta[indexChargeSpecies]; + } + } - std::tie(holderEvent.runIndex, holderEvent.runGroupIndex) = holderCcdb.runNumbersIndicesGroupIndices.at(holderEvent.runNumber); + if (cgAnalysis.cfgFlagQaRun.value && track.isPrimaryTrack()) { + if (std::cmp_greater(holderTrack.sign, 0)) { + fillQaRunByTrackByChargeSpecies(track); + } else { + fillQaRunByTrackByChargeSpecies(track); + } + } + } else { + if (cgAnalysis.cfgFlagQaTrack.value && track.isPrimaryTrack()) { + if (std::cmp_greater(holderTrack.sign, 0)) { + fillQaTrackByChargeSpecies(track); + } else { + fillQaTrackByChargeSpecies(track); + } + } - if (holderEvent.runGroupIndex == 0 || (groupEvent.cfgFlagRejectionRunBad.value && holderEvent.runGroupIndex < 0)) { - hrCounter.fill(HIST("hNEvents"), 2.); - return false; - } + if (!isGoodTrack(track)) { + return false; + } - if (!groupEvent.cfgLabelFlagsRct.value.empty() && !rctFlagsChecker.checkTable(collision)) { - hrCounter.fill(HIST("hNEvents"), 3.); - return false; - } + if (cgAnalysis.cfgFlagQaDca.value) { + if (std::cmp_greater(holderTrack.sign, 0)) { + fillQaDcaByChargeSpecies(); + } else { + fillQaDcaByChargeSpecies(); + } + } - for (std::int32_t const& iEvSel : std::views::iota(0, aod::evsel::EventSelectionFlags::kNsel)) { - if (((groupEvent.cfgBitsSelectionEvent.value >> iEvSel) & 1) && !collision.selection_bit(iEvSel)) { - hrCounter.fill(HIST("hNEvents"), 4.); - hrCounter.fill(HIST("hNEvents"), 10. + iEvSel); + if (!isGoodDca()) { return false; } } - if (groupEvent.cfgFlagInelEvent.value && !collision.isInelGt0()) { - hrCounter.fill(HIST("hNEvents"), 5.); - return false; - } + return true; + } - if (groupAnalysis.cfgFlagQaEvent.value) { - hrQaEvent.fill(HIST("QaEvent/hVxVy"), collision.posX(), collision.posY()); - hrQaEvent.fill(HIST("QaEvent/hVz"), holderEvent.vz); + template + requires IsValidEnumValue + void fillMiniTableMc(const MPs& mcParticles, const Ts& tracks, const bool isGoodNPvContributors) + { + if (!static_cast(cgAnalysis.cfgFlagsStorageMiniTable.value.get(toI(ParticleNumberValue)))) { + return; } - if (!(std::abs(holderEvent.vz) < groupEvent.cfgCutMaxAbsVertexZ.value)) { - hrCounter.fill(HIST("hNEvents"), 6.); - return false; + const std::optional codeCollision{mini_collision_codec::encode(cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, isGoodNPvContributors)}; + if (!codeCollision.has_value()) { + return; } - if (groupAnalysis.cfgFlagQaRun.value) { - hrQaRun.fill(HIST("QaRun/pRunIndexVx"), holderEvent.runIndex, collision.posX()); - hrQaRun.fill(HIST("QaRun/pRunIndexVy"), holderEvent.runIndex, collision.posY()); - hrQaRun.fill(HIST("QaRun/pRunIndexVz"), holderEvent.runIndex, holderEvent.vz); - hrQaRun.fill(HIST("QaRun/pRunIndexMultiplicityFt0a"), holderEvent.runIndex, collision.multZeqFT0A()); - hrQaRun.fill(HIST("QaRun/pRunIndexMultiplicityFt0c"), holderEvent.runIndex, collision.multZeqFT0C()); - if (HolderEvent::RangeCentrality.first <= collision.centFT0A() && collision.centFT0A() <= HolderEvent::RangeCentrality.second) { - hrQaRun.fill(HIST("QaRun/pRunIndexCentralityFt0a"), holderEvent.runIndex, collision.centFT0A()); + pg.miniCollision(*codeCollision); + + for (const auto& mcParticle : mcParticles) { + if (!initMcParticle(mcParticle)) { + continue; } - if (HolderEvent::RangeCentrality.first <= collision.centFT0C() && collision.centFT0C() <= HolderEvent::RangeCentrality.second) { - hrQaRun.fill(HIST("QaRun/pRunIndexCentralityFt0c"), holderEvent.runIndex, collision.centFT0C()); + + if (mcParticle.isPhysicalPrimary()) { + const auto fillByParticleSpecies{ + [this] + requires IsValidEnumValue && (getValue(ParticleNumberValue) == ParticleSpeciesAll::All || getValue(ParticleNumberValue) == getValue(ParticleSpeciesValue)) + () -> bool { + if (!isGoodMomentum(ParticleSpeciesValue)>(true) || (std::cmp_greater(holderMcParticle.charge, 0) ? !isPid(ParticleSpeciesValue), ChargeSpecies::Plus>() : !isPid(ParticleSpeciesValue), ChargeSpecies::Minus>())) { + return false; + } + + const std::optional codeMcParticle{mini_mc_particle_codec::encode(holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.charge)}; + if (codeMcParticle.has_value()) { + pg.miniMcParticle(pg.miniCollision.lastIndex(), *codeMcParticle); + } + return true; + }}; + + if constexpr (ParticleNumberValue == ParticleNumber::Charge) { + if (!fillByParticleSpecies.template operator()() && !fillByParticleSpecies.template operator()()) { + fillByParticleSpecies.template operator()(); + } + } else { + fillByParticleSpecies.template operator()(getValue(ParticleNumberValue))>(); + } } - if (HolderEvent::RangeCentrality.first <= collision.centFT0M() && collision.centFT0M() <= HolderEvent::RangeCentrality.second) { - hrQaRun.fill(HIST("QaRun/pRunIndexCentralityFt0m"), holderEvent.runIndex, collision.centFT0M()); + + if (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary()) { + const auto& tracksMatched{tracks.sliceBy(psg.tracksPerMcParticle, mcParticle.globalIndex())}; + for (const auto& track : tracksMatched) { + if (!std::cmp_greater(track.tpcNClsFound(), cgTrack.cfgCutsMinTpcNCls.value.get(toI(Selection::Default))) || !setTrack(track) || !(track.tpcChi2NCl() > cgTrack.cfgCutMinTpcChi2NCls.value) || !(track.tpcFoundOverFindableCls() > cgTrack.cfgCutMinTpcNClsRatio.value) || !(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { + continue; + } + + const auto fillByParticleSpeciesAll{ + [this, &track] + requires IsValidEnumValue && (ParticleSpeciesAllValue != ParticleSpeciesAll::All) + () -> bool { + const bool doUseMcParticleMomentum{cgTrack.cfgFlagMcParticleMomentum.value}; + if (!isGoodMomentum(doUseMcParticleMomentum) || (std::cmp_greater(holderTrack.sign, 0) ? !isPid() : !isPid())) { + return false; + } + + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; + const double pt{doUseMcParticleMomentum ? holderMcParticle.pt : holderTrack.pt}; + const bool doUseTofPid{pt >= cgTrack.cfgThresholdsPtTofPid.value.get(IndexParticleSpecies)}; + if (!(doUseTofPid ? isPid(PidStrategy::TpcTof), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value) : isPid(PidStrategy::Tpc), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value))) { + return false; + } + + const std::optional> codeTrack{mini_track_codec::encode(*holderMember.configSelection, track.isPVContributor(), track.itsNCls(), track.itsChi2NCl(), track.tpcChi2NCl(), track.tpcFractionSharedCls(), track.tpcNClsCrossedRows(), std::array>{getAbsNSigmaDca(), getAbsNSigmaDca()}, pt, doUseMcParticleMomentum ? holderMcParticle.eta : holderTrack.eta, holderTrack.sign, doUseTofPid ? std::abs(holderTrack.getNSigmaPidCombined(IndexParticleSpecies)) : std::abs(holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies]))}; + if (codeTrack.has_value()) { + pg.miniTrack(pg.miniCollision.lastIndex(), std::get<0>(*codeTrack), std::get<1>(*codeTrack)); + } + return true; + }}; + + if constexpr (ParticleNumberValue == ParticleNumber::Charge) { + if (!fillByParticleSpeciesAll.template operator()() && !fillByParticleSpeciesAll.template operator()()) { + fillByParticleSpeciesAll.template operator()(); + } + } else { + fillByParticleSpeciesAll.template operator()(ParticleNumberValue)>(); + } + } } } + } + + template + requires IsValidEnumValue + void fillMiniTableRaw(const T& tracks, const bool isGoodNPvContributors) + { + if (!static_cast(cgAnalysis.cfgFlagsStorageMiniTable.value.get(toI(ParticleNumberValue)))) { + return; + } + + const std::optional codeCollision{mini_collision_codec::encode(holderEvent.vz, holderEvent.centrality, isGoodNPvContributors)}; + if (!codeCollision.has_value()) { + return; + } + + pg.miniCollision(*codeCollision); for (const auto& track : tracks) { - if (!track.has_collision()) { + if (!std::cmp_greater(track.tpcNClsFound(), cgTrack.cfgCutsMinTpcNCls.value.get(toI(Selection::Default))) || !setTrack(track) || !(track.tpcChi2NCl() > cgTrack.cfgCutMinTpcChi2NCls.value) || !(track.tpcFoundOverFindableCls() > cgTrack.cfgCutMinTpcNClsRatio.value) || !(track.tpcCrossedRowsOverFindableCls() > cgTrack.cfgCutMinTpcNCrossedRowsRatio.value)) { continue; } - initTrack(track); - } - if (holderEvent.nGlobalTracksP > 0.) { - holderEvent.meanDcaXyP /= holderEvent.nGlobalTracksP; - holderEvent.meanSquareDcaXyP /= holderEvent.nGlobalTracksP; - holderEvent.meanDcaZP /= holderEvent.nGlobalTracksP; - holderEvent.meanSquareDcaZP /= holderEvent.nGlobalTracksP; - } - if (holderEvent.nGlobalTracksM > 0.) { - holderEvent.meanDcaXyM /= holderEvent.nGlobalTracksM; - holderEvent.meanSquareDcaXyM /= holderEvent.nGlobalTracksM; - holderEvent.meanDcaZM /= holderEvent.nGlobalTracksM; - holderEvent.meanSquareDcaZM /= holderEvent.nGlobalTracksM; - } + const auto fill{ + [this, &track] + requires IsValidEnumValue + (const double absNSigmaPid) -> void { + const std::optional> codeTrack{mini_track_codec::encode(*holderMember.configSelection, track.isPVContributor(), track.itsNCls(), track.itsChi2NCl(), track.tpcChi2NCl(), track.tpcFractionSharedCls(), track.tpcNClsCrossedRows(), std::array>{getAbsNSigmaDca(), getAbsNSigmaDca()}, holderTrack.pt, holderTrack.eta, holderTrack.sign, absNSigmaPid)}; + if (codeTrack.has_value()) { + pg.miniTrack(pg.miniCollision.lastIndex(), std::get<0>(*codeTrack), std::get<1>(*codeTrack)); + } + }}; + const auto fillByParticleSpeciesAll{ + [this, &fill] + requires IsValidEnumValue + () -> bool { + if constexpr (ParticleSpeciesAllValue == ParticleSpeciesAll::All) { + if (!isGoodMomentum(ParticleNumberValue)>(false)) { + return false; + } + + fill.template operator()(0.); + } else { + if (!isGoodMomentum(false)) { + return false; + } + + constexpr std::int32_t IndexParticleSpecies{toI(getValue(ParticleSpeciesAllValue))}; + const bool doUseTofPid{holderTrack.pt >= cgTrack.cfgThresholdsPtTofPid.value.get(IndexParticleSpecies)}; + if (!(doUseTofPid ? isPid(PidStrategy::TpcTof), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value) : isPid(PidStrategy::Tpc), ParticleSpeciesAllValue, Selection::Loose>(cgTrack.cfgFlagRejectionOthers.value))) { + return false; + } + + fill.template operator()(doUseTofPid ? std::abs(holderTrack.getNSigmaPidCombined(IndexParticleSpecies)) : std::abs(holderTrack.nsSigmaPid[toI(Detector::Tpc)][IndexParticleSpecies])); + } + return true; + }}; + + if constexpr (ParticleNumberValue == ParticleNumber::Charge) { + if (!fillByParticleSpeciesAll.template operator()() && !fillByParticleSpeciesAll.template operator()() && !fillByParticleSpeciesAll.template operator()()) { + fillByParticleSpeciesAll.template operator()(); + } + } else { + if (!fillByParticleSpeciesAll.template operator()(ParticleNumberValue)>()) { + fillByParticleSpeciesAll.template operator()(); + } + } + } + } + + template + McEventSelection initMcEvent(const MC& mcCollision) + { + holderMcEvent.clear(); + holderMcEvent.vz = mcCollision.posZ(); + + const auto fillMcEventSelection{ + [this](const McEventSelection selection) -> McEventSelection { + hrCounter.fill(C_CS("hNEventsMc"), toI(selection)); + return selection; + }}; - if (groupAnalysis.cfgFlagQaRun.value) { - hrQaRun.fill(HIST("QaRun/pRunIndexNGlobalTracks_p"), holderEvent.runIndex, holderEvent.nGlobalTracksP); - hrQaRun.fill(HIST("QaRun/pRunIndexNGlobalTracks_m"), holderEvent.runIndex, holderEvent.nGlobalTracksM); - hrQaRun.fill(HIST("QaRun/pRunIndexNPvContributors_p"), holderEvent.runIndex, holderEvent.nPvContributorsP); - hrQaRun.fill(HIST("QaRun/pRunIndexNPvContributors_m"), holderEvent.runIndex, holderEvent.nPvContributorsM); - if (holderEvent.nGlobalTracksP > 0) { - hrQaRun.fill(HIST("QaRun/pRunIndexMeanDcaXy_p"), holderEvent.runIndex, holderEvent.meanDcaXyP); - hrQaRun.fill(HIST("QaRun/pRunIndexSigmaDcaXy_p"), holderEvent.runIndex, std::sqrt(holderEvent.meanSquareDcaXyP - std::pow(holderEvent.meanDcaXyP, 2.))); - hrQaRun.fill(HIST("QaRun/pRunIndexMeanDcaZ_p"), holderEvent.runIndex, holderEvent.meanDcaZP); - hrQaRun.fill(HIST("QaRun/pRunIndexSigmaDcaZ_p"), holderEvent.runIndex, std::sqrt(holderEvent.meanSquareDcaZP - std::pow(holderEvent.meanDcaZP, 2.))); - } - if (holderEvent.nGlobalTracksM > 0) { - hrQaRun.fill(HIST("QaRun/pRunIndexMeanDcaXy_m"), holderEvent.runIndex, holderEvent.meanDcaXyM); - hrQaRun.fill(HIST("QaRun/pRunIndexSigmaDcaXy_m"), holderEvent.runIndex, std::sqrt(holderEvent.meanSquareDcaXyM - std::pow(holderEvent.meanDcaXyM, 2.))); - hrQaRun.fill(HIST("QaRun/pRunIndexMeanDcaZ_m"), holderEvent.runIndex, holderEvent.meanDcaZM); - hrQaRun.fill(HIST("QaRun/pRunIndexSigmaDcaZ_m"), holderEvent.runIndex, std::sqrt(holderEvent.meanSquareDcaZM - std::pow(holderEvent.meanDcaZM, 2.))); - } - hrQaRun.fill(HIST("QaRun/pRunIndexNTofBeta_p"), holderEvent.runIndex, holderEvent.nTofBetaP); - hrQaRun.fill(HIST("QaRun/pRunIndexNTofBeta_m"), holderEvent.runIndex, holderEvent.nTofBetaM); - } + fillMcEventSelection(McEventSelection::All); - if (groupAnalysis.cfgFlagQaEvent.value) { - hrQaEvent.fill(HIST("QaEvent/hNPvContributorsNGlobalTracks"), holderEvent.nPvContributorsP + holderEvent.nPvContributorsM, holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM); - if (holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM > 0) { - hrQaEvent.fill(HIST("QaEvent/hNGlobalTracksMeanDcaXy"), holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM, (holderEvent.meanDcaXyP * holderEvent.nGlobalTracksP + holderEvent.meanDcaXyM * holderEvent.nGlobalTracksM) / (holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM)); - hrQaEvent.fill(HIST("QaEvent/hNGlobalTracksMeanDcaZ"), holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM, (holderEvent.meanDcaZP * holderEvent.nGlobalTracksP + holderEvent.meanDcaZM * holderEvent.nGlobalTracksM) / (holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM)); - } - hrQaEvent.fill(HIST("QaEvent/hNTofBetaNGlobalTracks"), holderEvent.nTofBetaP + holderEvent.nTofBetaM, holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM); + if (cgEvent.cfgFlagInelMc.value && !mcCollision.isInelGt0()) { + return fillMcEventSelection(McEventSelection::Inel); } - if (!(holderEvent.nPvContributorsP + holderEvent.nPvContributorsM - holderEvent.nGlobalTracksP - holderEvent.nGlobalTracksM > groupEvent.cfgCutMinDeviationNPvContributors.value)) { - hrCounter.fill(HIST("hNEvents"), 7.); - return false; + if (!(std::abs(holderMcEvent.vz) < cgEvent.cfgCutMaxAbsVzMc.value)) { + return fillMcEventSelection(McEventSelection::Vz); } - hrCounter.fill(HIST("hNEvents"), 1.); - - if (groupAnalysis.cfgFlagQaEvent.value) { - if (holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM > 0) { - hrQaEvent.fill(HIST("QaEvent/hNGlobalTracksMeanDcaXy_nPvContributorsCut"), holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM, (holderEvent.meanDcaXyP * holderEvent.nGlobalTracksP + holderEvent.meanDcaXyM * holderEvent.nGlobalTracksM) / (holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM)); - hrQaEvent.fill(HIST("QaEvent/hNGlobalTracksMeanDcaZ_nPvContributorsCut"), holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM, (holderEvent.meanDcaZP * holderEvent.nGlobalTracksP + holderEvent.meanDcaZM * holderEvent.nGlobalTracksM) / (holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM)); - } - hrQaEvent.fill(HIST("QaEvent/hNTofBetaNGlobalTracks_nPvContributorsCut"), holderEvent.nTofBetaP + holderEvent.nTofBetaM, holderEvent.nGlobalTracksP + holderEvent.nGlobalTracksM); + if (cgAnalysis.cfgFlagQaMc.value) { + hrQaMc.fill(C_CS("hNCollisionsPerMcCollision"), mcCollision.numRecoCollision()); } - if (groupAnalysis.cfgFlagQaCentrality.value) { - hrQaCentrality.fill(HIST("QaCentrality/hCentralityFt0a"), collision.centFT0A(), collision.multZeqFT0A()); - hrQaCentrality.fill(HIST("QaCentrality/hCentralityFt0c"), collision.centFT0C(), collision.multZeqFT0C()); - hrQaCentrality.fill(HIST("QaCentrality/hCentralityFt0m"), collision.centFT0M(), collision.multZeqFT0A() + collision.multZeqFT0C()); + if (cgEvent.cfgFlagSingleCollisionMc.value && std::cmp_not_equal(mcCollision.numRecoCollision(), 1)) { + return fillMcEventSelection(McEventSelection::NRecoCollisions); } - return true; + return fillMcEventSelection(McEventSelection::Good); } - template - bool initMcEvent(const MC& mcCollision) + template + EventSelection initEvent(const C& collision, const Ts& tracks) { - holderMcEvent.vz = mcCollision.posZ(); + holderEvent.clear(); + holderEvent.vz = collision.posZ(); + switch (cgEvent.cfgIndexDefinitionCentrality.value) { + case toI(CentralityDefinition::Ft0a): + holderEvent.centrality = collision.centFT0A(); + break; + case toI(CentralityDefinition::Ft0c): + holderEvent.centrality = collision.centFT0C(); + break; + default: + holderEvent.centrality = collision.centFT0M(); + break; + } + holderEvent.centralityCalibration = (cgEvent.cfgFlagDefinitionCentralitySameQa.value ? holderEvent.centrality : collision.centNTPV()); + + const auto fillEventSelection{ + [this](const EventSelection selection, const auto... selectionsAdditional) -> EventSelection + requires((std::is_arithmetic_v> && ...)) + { + hrCounter.fill(C_CS("hNEvents"), toI(selection)); + (hrCounter.fill(C_CS("hNEvents"), selectionsAdditional), ...); + if (cgAnalysis.cfgFlagQaCentrality.value) { + hrQaCentrality.fill(C_CS("hCentralitySelection"), holderEvent.centrality, toI(selection)); + (hrQaCentrality.fill(C_CS("hCentralitySelection"), holderEvent.centrality, selectionsAdditional), ...); + } + return selection; + }}; - hrCounter.fill(HIST("hNMcEvents"), 0.); + fillEventSelection(EventSelection::All); - if (!mcCollision.has_bc()) { - hrCounter.fill(HIST("hNMcEvents"), 2.); - return false; + if (!collision.has_foundBC()) { + return fillEventSelection(EventSelection::Run); } - const auto& bc = mcCollision.template bc_as(); - holderMcEvent.runNumber = bc.runNumber(); + const auto& foundBc{collision.template foundBC_as()}; + holderEvent.runNumber = foundBc.runNumber(); - if (holderCcdb.runNumbersIndicesGroupIndices.find(holderMcEvent.runNumber) == holderCcdb.runNumbersIndicesGroupIndices.end()) { - hrCounter.fill(HIST("hNMcEvents"), 2.); - return false; + { + const auto iter{holderCcdb.runNumbersToIndicesRunAndGroup.find(holderEvent.runNumber)}; + if (iter == holderCcdb.runNumbersToIndicesRunAndGroup.end()) { + return fillEventSelection(EventSelection::Run); + } + std::tie(holderEvent.indexRun, holderEvent.indexRunGroup) = iter->second; } - std::tie(holderMcEvent.runIndex, holderMcEvent.runGroupIndex) = holderCcdb.runNumbersIndicesGroupIndices.at(holderMcEvent.runNumber); + if (std::cmp_equal(holderEvent.indexRunGroup, 0) || (cgEvent.cfgFlagRejectionRunBad.value && std::cmp_less(holderEvent.indexRunGroup, 0))) { + return fillEventSelection(EventSelection::Run); + } - if (holderMcEvent.runGroupIndex == 0 || (groupEvent.cfgFlagRejectionRunBadMc.value && holderMcEvent.runGroupIndex < 0)) { - hrCounter.fill(HIST("hNMcEvents"), 2.); - return false; + if (rctFlagsChecker.any() && !rctFlagsChecker.checkTable(collision)) { + return fillEventSelection(EventSelection::Rct); } - if (groupEvent.cfgFlagInelEventMc.value && !mcCollision.isInelGt0()) { - hrCounter.fill(HIST("hNMcEvents"), 3.); - return false; + if (cgEvent.cfgFlagInel.value && !collision.isInelGt0()) { + return fillEventSelection(EventSelection::Inel); } - if (!(std::abs(holderMcEvent.vz) < groupEvent.cfgCutMaxAbsVertexZMc.value)) { - hrCounter.fill(HIST("hNMcEvents"), 4.); - return false; + for (std::int32_t const& iBit : std::views::iota(0, aod::evsel::EventSelectionFlags::kNsel)) { + if (((cgEvent.cfgBitsSelection.value >> iBit) & 1) && !collision.selection_bit(iBit)) { + return fillEventSelection(EventSelection::Bits, NEs + iBit); + } } - hrCounter.fill(HIST("hNMcEvents"), 1.); + if (!HolderEvent::isValidCentrality(holderEvent.centrality) || !HolderEvent::isValidCentrality(holderEvent.centralityCalibration)) { + return fillEventSelection(EventSelection::Centrality); + } - return true; - } + if (cgAnalysis.cfgFlagQaEvent.value) { + hrQaEvent.fill(C_CS("hVxVy"), collision.posX(), collision.posY()); + hrQaEvent.fill(C_CS("hVz"), holderEvent.vz); + } - void processRaw(const soa::Filtered::iterator& collision, const soa::Filtered& tracks, const aod::BCsWithTimestamps&) - { - if (!initEvent(collision, tracks) || (!groupAnalysis.cfgFlagQaTrack.value && !groupAnalysis.cfgFlagQaDca.value && !(groupAnalysis.cfgFlagQaAcceptance.value || groupAnalysis.cfgFlagQaAcceptancePi.value || groupAnalysis.cfgFlagQaAcceptanceKa.value || groupAnalysis.cfgFlagQaAcceptancePr.value) && !(groupAnalysis.cfgFlagQaPhi.value || groupAnalysis.cfgFlagQaPhiPi.value || groupAnalysis.cfgFlagQaPhiKa.value || groupAnalysis.cfgFlagQaPhiPr.value) && !(groupAnalysis.cfgFlagQaPid.value || groupAnalysis.cfgFlagQaPidPi.value || groupAnalysis.cfgFlagQaPidKa.value || groupAnalysis.cfgFlagQaPidPr.value) && !(groupAnalysis.cfgFlagCalculationYieldPi.value || groupAnalysis.cfgFlagCalculationYieldKa.value || groupAnalysis.cfgFlagCalculationYieldPr.value) && !(groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value))) { - return; + if (!(std::abs(holderEvent.vz) < cgEvent.cfgCutMaxAbsVz.value)) { + return fillEventSelection(EventSelection::Vz); + } + + if (std::cmp_greater_equal(cgEvent.cfgCutMaxOccupancy.value, 0)) { + const std::int32_t occupancy{collision.trackOccupancyInTimeRange()}; + if (std::cmp_less(occupancy, 0) || std::cmp_greater_equal(occupancy, cgEvent.cfgCutMaxOccupancy.value)) { + return fillEventSelection(EventSelection::Occupancy); + } } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - holderEvent.subgroupIndex = gRandom->Integer(groupEvent.cfgNSubgroups.value); - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - fluctuationCalculatorTrackChP->init(); - fluctuationCalculatorTrackChM->init(); - fluctuationCalculatorTrackChT->init(); - fluctuationCalculatorTrackChN->init(); + if (cgAnalysis.cfgFlagQaRun.value) { + hrQaRun.fill(C_CS("pRunIndexVx"), holderEvent.indexRun, collision.posX()); + hrQaRun.fill(C_CS("pRunIndexVy"), holderEvent.indexRun, collision.posY()); + hrQaRun.fill(C_CS("pRunIndexVz"), holderEvent.indexRun, holderEvent.vz); + hrQaRun.fill(C_CS("pRunIndexMultiplicityFt0a"), holderEvent.indexRun, collision.multZeqFT0A()); + hrQaRun.fill(C_CS("pRunIndexMultiplicityFt0c"), holderEvent.indexRun, collision.multZeqFT0C()); + if (const double centrality{collision.centNTPV()}; HolderEvent::isValidCentrality(centrality)) { + hrQaRun.fill(C_CS("pRunIndexCentralityNtpv"), holderEvent.indexRun, centrality); + } + if (const double centrality{collision.centFT0A()}; HolderEvent::isValidCentrality(centrality)) { + hrQaRun.fill(C_CS("pRunIndexCentralityFt0a"), holderEvent.indexRun, centrality); } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - fluctuationCalculatorTrackKaP->init(); - fluctuationCalculatorTrackKaM->init(); - fluctuationCalculatorTrackKaT->init(); - fluctuationCalculatorTrackKaN->init(); + if (const double centrality{collision.centFT0C()}; HolderEvent::isValidCentrality(centrality)) { + hrQaRun.fill(C_CS("pRunIndexCentralityFt0c"), holderEvent.indexRun, centrality); } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - fluctuationCalculatorTrackPrP->init(); - fluctuationCalculatorTrackPrM->init(); - fluctuationCalculatorTrackPrT->init(); - fluctuationCalculatorTrackPrN->init(); + if (const double centrality{collision.centFT0M()}; HolderEvent::isValidCentrality(centrality)) { + hrQaRun.fill(C_CS("pRunIndexCentralityFt0m"), holderEvent.indexRun, centrality); } } for (const auto& track : tracks) { - if (!track.has_collision() || !initTrack(track)) { - continue; - } - - if ((groupAnalysis.cfgFlagQaPhi.value || groupAnalysis.cfgFlagQaPhiPi.value || groupAnalysis.cfgFlagQaPhiKa.value || groupAnalysis.cfgFlagQaPhiPr.value) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagQaPhi.value) { - if (holderTrack.sign > 0) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } else if (holderTrack.sign < 0) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } - if (holderTrack.hasTofPid) { - if (holderTrack.sign > 0) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } else if (holderTrack.sign < 0) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } - } - } - - if (groupAnalysis.cfgFlagQaPhiPi.value) { - switch (isPid(false)) { - case 1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case -1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - switch (isPid(false)) { - case 1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case -1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - } - - if (groupAnalysis.cfgFlagQaPhiKa.value) { - switch (isPid(false)) { - case 1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case -1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - switch (isPid(false)) { - case 1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case -1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - } - - if (groupAnalysis.cfgFlagQaPhiPr.value) { - switch (isPid(false)) { - case 1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case -1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - switch (isPid(false)) { - case 1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case -1: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_tpcTofPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } + initTrack(track); + } + for (std::int32_t const& iChargeSpecies : std::views::iota(0, NEs)) { + if (std::cmp_greater(holderEvent.nsGlobalTracks[iChargeSpecies], 0)) { + for (std::int32_t const& iDcaAxis : std::views::iota(0, NEs)) { + holderEvent.measuresDca[toI(DcaMeasure::Mean)][iDcaAxis][iChargeSpecies] /= holderEvent.nsGlobalTracks[iChargeSpecies]; + holderEvent.measuresDca[toI(DcaMeasure::Sigma)][iDcaAxis][iChargeSpecies] = std::sqrt(std::max(0., holderEvent.measuresDca[toI(DcaMeasure::Sigma)][iDcaAxis][iChargeSpecies] / holderEvent.nsGlobalTracks[iChargeSpecies] - std::pow(holderEvent.measuresDca[toI(DcaMeasure::Mean)][iDcaAxis][iChargeSpecies], 2.))); } } + } - if ((groupAnalysis.cfgFlagQaPidPi.value || groupAnalysis.cfgFlagQaPidKa.value || groupAnalysis.cfgFlagQaPidPr.value) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagQaPidPi.value) { - if (holderTrack.sign > 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPi_p"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPi); - } else if (holderTrack.sign < 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPi_m"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPi); - } - if (holderTrack.hasTofPid) { - switch (isPid(false)) { - case 1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPi_tofPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPi); - break; - case -1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPi_tofPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPi); - break; - } - switch (isPid(false)) { - case 1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPi_tpcPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPi); - break; - case -1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPi_tpcPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPi); - break; - } - if (holderTrack.sign > 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcTofNSigmaPi_p"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcTofNSigmaPi); - } else if (holderTrack.sign < 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcTofNSigmaPi_m"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcTofNSigmaPi); - } - } - } - - if (groupAnalysis.cfgFlagQaPidKa.value) { - if (holderTrack.sign > 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaKa_p"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaKa); - } else if (holderTrack.sign < 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaKa_m"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaKa); - } - if (holderTrack.hasTofPid) { - switch (isPid(false)) { - case 1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaKa_tofKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaKa); - break; - case -1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaKa_tofKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaKa); - break; - } - switch (isPid(false)) { - case 1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaKa_tpcKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaKa); - break; - case -1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaKa_tpcKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaKa); - break; - } - if (holderTrack.sign > 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcTofNSigmaKa_p"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcTofNSigmaKa); - } else if (holderTrack.sign < 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcTofNSigmaKa_m"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcTofNSigmaKa); - } - } - } + if (cgAnalysis.cfgFlagQaRun.value) { + fillQaRunByEventByChargeSpecies(); + fillQaRunByEventByChargeSpecies(); + } - if (groupAnalysis.cfgFlagQaPidPr.value) { - if (holderTrack.sign > 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPr_p"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPr); - } else if (holderTrack.sign < 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPr_m"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPr); - } - if (holderTrack.hasTofPid) { - switch (isPid(false)) { - case 1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPr_tofPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPr); - break; - case -1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPr_tofPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPr); - break; - } - switch (isPid(false)) { - case 1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPr_tpcPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPr); - break; - case -1: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPr_tpcPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPr); - break; - } - if (holderTrack.sign > 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcTofNSigmaPr_p"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcTofNSigmaPr); - } else if (holderTrack.sign < 0) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcTofNSigmaPr_m"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcTofNSigmaPr); - } - } - } + if (cgAnalysis.cfgFlagQaEvent.value) { + hrQaEvent.fill(C_CS("hNPvContributorsNGlobalTracks"), holderEvent.getNPvContributors(), holderEvent.getNGlobalTracks()); + if (std::cmp_greater(holderEvent.getNGlobalTracks(), 0)) { + hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); + hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); } + hrQaEvent.fill(C_CS("hNTofBetaNGlobalTracks"), holderEvent.getNTofBeta(), holderEvent.getNGlobalTracks()); + } - if ((groupAnalysis.cfgFlagCalculationYieldPi.value || groupAnalysis.cfgFlagCalculationYieldKa.value || groupAnalysis.cfgFlagCalculationYieldPr.value) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagCalculationYieldPi.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcPiP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - case -1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcPiM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - } - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcTofPiP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - case -1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcTofPiM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - } - } - - if (groupAnalysis.cfgFlagCalculationYieldKa.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcKaP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - case -1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcKaM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - } - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcTofKaP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - case -1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcTofKaM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - } - } + if (!std::cmp_greater(holderEvent.getNPvContributors() - holderEvent.getNGlobalTracks(), cgEvent.cfgCutMinDeviationNPvContributors.value)) { + return fillEventSelection(EventSelection::NPvContributors); + } - if (groupAnalysis.cfgFlagCalculationYieldPr.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcPrP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - case -1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcPrM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - } - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcTofPrP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - case -1: - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_tpcTofPrM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - break; - } - } - } + readCcdb(); - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - calculateFluctuation(); + if (cgAnalysis.cfgFlagQaEvent.value) { + if (std::cmp_greater(holderEvent.getNGlobalTracks(), 0)) { + hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Xy)) + C_CS("_nPvContributorsCut"), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); + hrQaEvent.fill(C_CS("hNGlobalTracks") + C_SV(getName(DcaMeasure::Mean)) + C_CS("Dca") + C_SV(getName(DcaAxis::Z)) + C_CS("_nPvContributorsCut"), holderEvent.getNGlobalTracks(), holderEvent.getMeasureDca()); } + hrQaEvent.fill(C_CS("hNTofBetaNGlobalTracks_nPvContributorsCut"), holderEvent.getNTofBeta(), holderEvent.getNGlobalTracks()); } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNChPNChM"), holderEvent.centrality, holderEvent.nChP, holderEvent.nChM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChP"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChM"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChT"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChN"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChN->getProductFast(iOrderVector)); - } - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNKaPNKaM"), holderEvent.centrality, holderEvent.nKaP, holderEvent.nKaM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaP"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaM"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaT"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaN"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaN->getProductFast(iOrderVector)); - } - } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNPrPNPrM"), holderEvent.centrality, holderEvent.nPrP, holderEvent.nPrM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrP"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrM"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrT"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrN"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrN->getProductFast(iOrderVector)); - } + if (cgAnalysis.cfgFlagQaCentrality.value) { + hrQaCentrality.fill(C_CS("hCentralityMultiplicity"), holderEvent.centrality, collision.multNTracksPVeta1()); } + + return fillEventSelection(EventSelection::Good); } - PROCESS_SWITCH(PartNumFluc, processRaw, "Process raw data", true); void processMc(const soa::Filtered::iterator& mcCollision, const aod::McParticles& mcParticles, const soa::SmallGroups& collisions, const soa::Filtered& tracksUngrouped, const aod::BCsWithTimestamps&) { - if (!initMcEvent(mcCollision)) { + if (initMcEvent(mcCollision) != McEventSelection::Good) { return; } for (const auto& collision : collisions) { - if (collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (std::cmp_not_equal(collision.globalIndex(), mcCollision.bestCollisionIndex())) { continue; } - const auto& tracks = tracksUngrouped.sliceBy(presliceTracksPerCollision, collision.globalIndex()); + const auto& tracks{tracksUngrouped.sliceBy(psg.tracksPerCollision, collision.globalIndex())}; - if (!initEvent(collision, tracks)) { + const EventSelection eventSelection{initEvent(collision, tracks)}; + if ((eventSelection == EventSelection::Good || std::cmp_greater_equal(toI(eventSelection), toI(EventSelection::NPvContributors))) && holderMember.doStorageMiniTable && (std::uniform_real_distribution{}(holderMember.engineRandom)) * cgEvent.cfgFactorStorageMiniTable.value < 1.) { + const bool isGoodNPvContributors{eventSelection != EventSelection::NPvContributors}; + readCcdb(); + fillMiniTableMc(mcParticles, tracks, isGoodNPvContributors); + fillMiniTableMc(mcParticles, tracks, isGoodNPvContributors); + fillMiniTableMc(mcParticles, tracks, isGoodNPvContributors); + } + if (eventSelection != EventSelection::Good) { continue; } - if (groupAnalysis.cfgFlagQaMc.value) { - hrQaMc.fill(HIST("QaMc/hCentralityVzDeltaVz"), holderEvent.centrality, holderEvent.vz, holderEvent.vz - holderMcEvent.vz); + if (cgAnalysis.cfgFlagQaMc.value) { + hrQaMc.fill(C_CS("hCentralityVzMcDeltaVz"), holderEvent.centralityCalibration, holderMcEvent.vz, holderEvent.vz - holderMcEvent.vz); } - if ((groupAnalysis.cfgFlagQaPhi.value || groupAnalysis.cfgFlagQaPhiPi.value || groupAnalysis.cfgFlagQaPhiKa.value || groupAnalysis.cfgFlagQaPhiPr.value) || (groupAnalysis.cfgFlagCalculationYieldPi.value || groupAnalysis.cfgFlagCalculationYieldKa.value || groupAnalysis.cfgFlagCalculationYieldPr.value) || (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value)) { - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - holderEvent.subgroupIndex = gRandom->Integer(groupEvent.cfgNSubgroups.value); - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - fluctuationCalculatorTrackChP->init(); - fluctuationCalculatorTrackChM->init(); - fluctuationCalculatorTrackChT->init(); - fluctuationCalculatorTrackChN->init(); - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - fluctuationCalculatorTrackKaP->init(); - fluctuationCalculatorTrackKaM->init(); - fluctuationCalculatorTrackKaT->init(); - fluctuationCalculatorTrackKaN->init(); - } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - fluctuationCalculatorTrackPrP->init(); - fluctuationCalculatorTrackPrM->init(); - fluctuationCalculatorTrackPrT->init(); - fluctuationCalculatorTrackPrN->init(); - } + if (cgAnalysis.cfgFlagQaTrack.value || cgAnalysis.cfgFlagQaDca.value || holderMember.doQaAcceptance || holderMember.doQaPhi || holderMember.doQaPid || cgAnalysis.cfgFlagQaMc.value || holderMember.doCalculationYield || holderMember.doCalculationPurity || holderMember.doCalculationFractionPrimary || holderMember.doCalculationFluctuation) { + if (holderMember.doCalculationFluctuation) { + holderEvent.indexSubgroup = std::uniform_int_distribution{0, cgEvent.cfgNSubgroups.value - 1}(holderMember.engineRandom); + initCalculationFluctuation(); + holderDerivedData.clear(); } for (const auto& mcParticle : mcParticles) { - if (!initMcParticle(mcParticle)) { + if (!initMcParticle(mcParticle)) { continue; } - if (groupAnalysis.cfgFlagQaPhi.value) { - if (holderMcParticle.charge > 0) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } else if (holderMcParticle.charge < 0) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } - } + const auto& tracksMatched{tracks.sliceBy(psg.tracksPerMcParticle, mcParticle.globalIndex())}; - if ((groupAnalysis.cfgFlagQaPhiPi.value || groupAnalysis.cfgFlagQaPhiKa.value || groupAnalysis.cfgFlagQaPhiPr.value) || (groupAnalysis.cfgFlagCalculationYieldPi.value || groupAnalysis.cfgFlagCalculationYieldKa.value || groupAnalysis.cfgFlagCalculationYieldPr.value) || (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value)) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kPiPlus: - if (groupAnalysis.cfgFlagQaPhiPi.value) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcPiP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } - if (groupAnalysis.cfgFlagCalculationYieldPi.value) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcPiP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value && isGoodMomentum(true)) { - holderMcEvent.nChP++; - fluctuationCalculatorTrackChP->fill(1., 1.); - fluctuationCalculatorTrackChT->fill(1., 1.); - fluctuationCalculatorTrackChN->fill(1., 1.); - } - break; - case PDG_t::kPiMinus: - if (groupAnalysis.cfgFlagQaPhiPi.value) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcPiM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } - if (groupAnalysis.cfgFlagCalculationYieldPi.value) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcPiM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value && isGoodMomentum(true)) { - holderMcEvent.nChM++; - fluctuationCalculatorTrackChM->fill(1., 1.); - fluctuationCalculatorTrackChT->fill(1., 1.); - fluctuationCalculatorTrackChN->fill(-1., 1.); - } - break; - case PDG_t::kKPlus: - if (groupAnalysis.cfgFlagQaPhiKa.value) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcKaP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } - if (groupAnalysis.cfgFlagCalculationYieldKa.value) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcKaP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value && isGoodMomentum(true)) { - holderMcEvent.nChP++; - fluctuationCalculatorTrackChP->fill(1., 1.); - fluctuationCalculatorTrackChT->fill(1., 1.); - fluctuationCalculatorTrackChN->fill(1., 1.); - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value && isGoodMomentum(true)) { - holderMcEvent.nKaP++; - fluctuationCalculatorTrackKaP->fill(1., 1.); - fluctuationCalculatorTrackKaT->fill(1., 1.); - fluctuationCalculatorTrackKaN->fill(1., 1.); - } - break; - case PDG_t::kKMinus: - if (groupAnalysis.cfgFlagQaPhiKa.value) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcKaM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } - if (groupAnalysis.cfgFlagCalculationYieldKa.value) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcKaM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value && isGoodMomentum(true)) { - holderMcEvent.nChM++; - fluctuationCalculatorTrackChM->fill(1., 1.); - fluctuationCalculatorTrackChT->fill(1., 1.); - fluctuationCalculatorTrackChN->fill(-1., 1.); - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value && isGoodMomentum(true)) { - holderMcEvent.nKaM++; - fluctuationCalculatorTrackKaM->fill(1., 1.); - fluctuationCalculatorTrackKaT->fill(1., 1.); - fluctuationCalculatorTrackKaN->fill(-1., 1.); - } - break; - case PDG_t::kProton: - if (groupAnalysis.cfgFlagQaPhiPr.value) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcPrP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } - if (groupAnalysis.cfgFlagCalculationYieldPr.value) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcPrP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value && isGoodMomentum(true)) { - holderMcEvent.nChP++; - fluctuationCalculatorTrackChP->fill(1., 1.); - fluctuationCalculatorTrackChT->fill(1., 1.); - fluctuationCalculatorTrackChN->fill(1., 1.); - } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value && isGoodMomentum(true)) { - holderMcEvent.nPrP++; - fluctuationCalculatorTrackPrP->fill(1., 1.); - fluctuationCalculatorTrackPrT->fill(1., 1.); - fluctuationCalculatorTrackPrN->fill(1., 1.); + if (holderMember.doQaAcceptance && mcParticle.isPhysicalPrimary() && std::cmp_equal(tracksMatched.size(), 1)) { + const double vz{cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz}; + if (holderMcParticle.eta * vz > 0. && std::abs(vz) > (cgEvent.cfgFlagMcCollisionVz.value ? cgEvent.cfgCutMaxAbsVzMc.value : cgEvent.cfgCutMaxAbsVz.value) - 1.) { + for (const auto& track : tracksMatched) { + if (!setTrack(track)) { + continue; } - break; - case PDG_t::kProtonBar: - if (groupAnalysis.cfgFlagQaPhiPr.value) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcPrM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - } - if (groupAnalysis.cfgFlagCalculationYieldPr.value) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcPrM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value && isGoodMomentum(true)) { - holderMcEvent.nChM++; - fluctuationCalculatorTrackChM->fill(1., 1.); - fluctuationCalculatorTrackChT->fill(1., 1.); - fluctuationCalculatorTrackChN->fill(-1., 1.); - } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value && isGoodMomentum(true)) { - holderMcEvent.nPrM++; - fluctuationCalculatorTrackPrM->fill(1., 1.); - fluctuationCalculatorTrackPrT->fill(1., 1.); - fluctuationCalculatorTrackPrN->fill(-1., 1.); - } - break; - } - } - } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNChPNChM_mc"), holderEvent.centrality, holderMcEvent.nChP, holderMcEvent.nChM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChP_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChM_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChT_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChN_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChN->getProductFast(iOrderVector)); - } - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNKaPNKaM_mc"), holderEvent.centrality, holderMcEvent.nKaP, holderMcEvent.nKaM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaP_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaM_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaT_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaN_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaN->getProductFast(iOrderVector)); - } - } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNPrPNPrM_mc"), holderEvent.centrality, holderMcEvent.nPrP, holderMcEvent.nPrM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrP_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrM_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrT_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrN_mc"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrN->getProductFast(iOrderVector)); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + } + } } - } - } - if (groupAnalysis.cfgFlagQaTrack.value || groupAnalysis.cfgFlagQaDca.value || (groupAnalysis.cfgFlagQaAcceptance.value || groupAnalysis.cfgFlagQaAcceptancePi.value || groupAnalysis.cfgFlagQaAcceptanceKa.value || groupAnalysis.cfgFlagQaAcceptancePr.value) || (groupAnalysis.cfgFlagQaPhi.value || groupAnalysis.cfgFlagQaPhiPi.value || groupAnalysis.cfgFlagQaPhiKa.value || groupAnalysis.cfgFlagQaPhiPr.value) || (groupAnalysis.cfgFlagQaPid.value || groupAnalysis.cfgFlagQaPidPi.value || groupAnalysis.cfgFlagQaPidKa.value || groupAnalysis.cfgFlagQaPidPr.value) || (groupAnalysis.cfgFlagCalculationYieldPi.value || groupAnalysis.cfgFlagCalculationYieldKa.value || groupAnalysis.cfgFlagCalculationYieldPr.value) || (groupAnalysis.cfgFlagCalculationPurityPi.value || groupAnalysis.cfgFlagCalculationPurityKa.value || groupAnalysis.cfgFlagCalculationPurityPr.value) || (groupAnalysis.cfgFlagCalculationFractionPrimaryPi.value || groupAnalysis.cfgFlagCalculationFractionPrimaryKa.value || groupAnalysis.cfgFlagCalculationFractionPrimaryPr.value) || (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value)) { - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - fluctuationCalculatorTrackChP->init(); - fluctuationCalculatorTrackChM->init(); - fluctuationCalculatorTrackChT->init(); - fluctuationCalculatorTrackChN->init(); - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - fluctuationCalculatorTrackKaP->init(); - fluctuationCalculatorTrackKaM->init(); - fluctuationCalculatorTrackKaT->init(); - fluctuationCalculatorTrackKaN->init(); + if (cgAnalysis.cfgFlagQaMc.value && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + hrQaMc.fill(C_CS("hCentralityNTracksPerMcParticle"), holderEvent.centralityCalibration, tracksMatched.size()); } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - fluctuationCalculatorTrackPrP->init(); - fluctuationCalculatorTrackPrM->init(); - fluctuationCalculatorTrackPrT->init(); - fluctuationCalculatorTrackPrN->init(); - } - } - for (const auto& track : tracks) { - if (!track.has_collision() || !track.has_mcParticle()) { - continue; - } + if (mcParticle.isPhysicalPrimary()) { + if (holderMember.doCalculationYield) { + fillCalculationYieldByParticleSpecies(); + fillCalculationYieldByParticleSpecies(); + fillCalculationYieldByParticleSpecies(); + } - const auto& mcParticle = track.template mcParticle_as(); - if (!mcParticle.has_mcCollision() || !initTrack(track) || !initMcParticle(mcParticle)) { - continue; + if (holderMember.doCalculationFluctuation) { + calculateFluctuationByParticleNumber(); + calculateFluctuationByParticleNumber(); + calculateFluctuationByParticleNumber(); + } } - if ((groupAnalysis.cfgFlagQaPhi.value || groupAnalysis.cfgFlagQaPhiPi.value || groupAnalysis.cfgFlagQaPhiKa.value || groupAnalysis.cfgFlagQaPhiPr.value) && (!groupTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary()) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagQaPhi.value) { - if (holderTrack.sign > 0.) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } else if (holderTrack.sign < 0.) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } - if (holderTrack.hasTofPid) { - if (holderTrack.sign > 0.) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } else if (holderTrack.sign < 0.) { - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - } - } + for (const auto& track : tracksMatched) { + if (!initTrack(track)) { + continue; } - if (groupAnalysis.cfgFlagQaPhiPi.value) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kPiPlus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcPiP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case PDG_t::kPiMinus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcPiM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - if (holderTrack.hasTofPid) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kPiPlus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPiP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case PDG_t::kPiMinus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPiM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - } + if (holderMember.doQaPhi) { + fillQaPhiByParticleSpeciesAll(); + fillQaPhiByParticleSpeciesAll(); + fillQaPhiByParticleSpeciesAll(); + fillQaPhiByParticleSpeciesAll(); } - if (groupAnalysis.cfgFlagQaPhiKa.value) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kKPlus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcKaP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case PDG_t::kKMinus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcKaM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - if (holderTrack.hasTofPid) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kKPlus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofKaP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case PDG_t::kKMinus: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofKaM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - } + if (holderMember.doQaPid) { + fillQaPidByParticleSpeciesAll(track); + fillQaPidByParticleSpeciesAll(track); + fillQaPidByParticleSpeciesAll(track); + fillQaPidByParticleSpeciesAll(track); } - if (groupAnalysis.cfgFlagQaPhiPr.value) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kProton: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcPrP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case PDG_t::kProtonBar: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcPrM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - if (holderTrack.hasTofPid) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kProton: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPrP"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - case PDG_t::kProtonBar: - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhiMc_mcTpcTofPrM"), holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta, holderMcParticle.phi); - hrQaPhi.fill(HIST("QaPhi/hCentralityPtEtaPhi_mcTpcTofPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.phi); - break; - } - } + if (cgAnalysis.cfgFlagQaMc.value && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + hrQaMc.fill(C_CS("hCentralityPtMcEtaMcDeltaPt"), holderEvent.centralityCalibration, holderMcParticle.pt, holderMcParticle.eta, holderTrack.pt - holderMcParticle.pt); + hrQaMc.fill(C_CS("hCentralityPtMcEtaMcDeltaEta"), holderEvent.centralityCalibration, holderMcParticle.pt, holderMcParticle.eta, holderTrack.eta - holderMcParticle.eta); } - } - if ((groupAnalysis.cfgFlagQaPidPi.value || groupAnalysis.cfgFlagQaPidKa.value || groupAnalysis.cfgFlagQaPidPr.value) && (!groupTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary()) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagQaPidPi.value) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kPiPlus: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPi_mcPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPi); - if (holderTrack.hasTofPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPi_mcPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPi); - } - break; - case PDG_t::kPiMinus: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPi_mcPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPi); - if (holderTrack.hasTofPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPi_mcPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPi); - } - break; - } + if (holderMember.doCalculationYield && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + fillCalculationYieldByParticleSpecies(); + fillCalculationYieldByParticleSpecies(); + fillCalculationYieldByParticleSpecies(); } - if (groupAnalysis.cfgFlagQaPidKa.value) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kKPlus: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaKa_mcKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaKa); - if (holderTrack.hasTofPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaKa_mcKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaKa); - } - break; - case PDG_t::kKMinus: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaKa_mcKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaKa); - if (holderTrack.hasTofPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaKa_mcKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaKa); - } - break; - } + if (holderMember.doCalculationPurity && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + fillCalculationPurityByParticleSpecies(); + fillCalculationPurityByParticleSpecies(); + fillCalculationPurityByParticleSpecies(); } - if (groupAnalysis.cfgFlagQaPidPr.value) { - switch (holderMcParticle.pdgCode) { - case PDG_t::kProton: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPr_mcPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPr); - if (holderTrack.hasTofPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPr_mcPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPr); - } - break; - case PDG_t::kProtonBar: - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTpcNSigmaPr_mcPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tpcNSigmaPr); - if (holderTrack.hasTofPid) { - hrQaPid.fill(HIST("QaPid/hCentralityPtEtaTofNSigmaPr_mcPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.tofNSigmaPr); - } - break; - } + if (holderMember.doCalculationFractionPrimary) { + fillCalculationFractionPrimaryByParticleSpecies(mcParticle); + fillCalculationFractionPrimaryByParticleSpecies(mcParticle); + fillCalculationFractionPrimaryByParticleSpecies(mcParticle); } - } - - if (groupAnalysis.cfgFlagQaMc.value && (!groupTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { - hrQaMc.fill(HIST("QaMc/hCentralityPtEtaDeltaPt"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.pt - holderMcParticle.pt); - hrQaMc.fill(HIST("QaMc/hCentralityPtEtaDeltaEta"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderTrack.eta - holderMcParticle.eta); - } - if ((groupAnalysis.cfgFlagCalculationYieldPi.value || groupAnalysis.cfgFlagCalculationYieldKa.value || groupAnalysis.cfgFlagCalculationYieldPr.value) && (!groupTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary()) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagCalculationYieldPi.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kPiPlus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcPiP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcPiP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kPiMinus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcPiM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcPiM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kPiPlus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPiP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcTofPiP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kPiMinus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPiM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcTofPiM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - } - } + if (holderMember.doCalculationFluctuation && (!cgTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { + calculateFluctuationByParticleNumber(); + calculateFluctuationByParticleNumber(); + calculateFluctuationByParticleNumber(); } + } + } - if (groupAnalysis.cfgFlagCalculationYieldKa.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kKPlus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcKaP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcKaP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kKMinus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcKaM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcKaM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kKPlus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofKaP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcTofKaP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kKMinus) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofKaM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcTofKaM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - } - } + if (holderMember.doCalculationFluctuation) { + fillCalculationFluctuationByParticleNumber(); + fillCalculationFluctuationByParticleNumber(); + fillCalculationFluctuationByParticleNumber(); + if (const std::optional> codeCollision{tiny_collision_codec::encode(cgEvent.cfgFlagMcCollisionVz.value ? holderMcEvent.vz : holderEvent.vz, holderEvent.centrality, holderDerivedData.nsTracks)}; codeCollision.has_value()) { + const std::tuple codeMcCollision{tiny_mc_collision_codec::encode(holderDerivedData.nsMcParticles)}; + pg.tinyMcCollision(std::get<0>(codeMcCollision), std::get<1>(codeMcCollision)); + pg.tinyCollision(std::get<0>(*codeCollision), std::get<1>(*codeCollision)); + for (aod::tiny_mc_particle::SignedEfficiency::type const& signedEfficiency : holderDerivedData.signedEfficienciesMcParticle) { + pg.tinyMcParticle(pg.tinyMcCollision.lastIndex(), signedEfficiency); } - - if (groupAnalysis.cfgFlagCalculationYieldPr.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kProton) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcPrP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcPrP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kProtonBar) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcPrM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcPrM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kProton) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPrP"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcTofPrP"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kProtonBar) { - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEtaMc_mcTpcTofPrM"), holderEvent.vz, holderEvent.centrality, holderMcParticle.pt, holderMcParticle.eta); - hrCalculationYield.fill(HIST("CalculationYield/hVzCentralityPtEta_mcTpcTofPrM"), holderEvent.vz, holderEvent.centrality, holderTrack.pt, holderTrack.eta); - } - break; - } - } + for (aod::tiny_track::SignedEfficiency::type const& signedEfficiency : holderDerivedData.signedEfficienciesTrack) { + pg.tinyTrack(pg.tinyCollision.lastIndex(), signedEfficiency); } } + } + } + } + } - if ((groupAnalysis.cfgFlagCalculationPurityPi.value || groupAnalysis.cfgFlagCalculationPurityKa.value || groupAnalysis.cfgFlagCalculationPurityPr.value) && (!groupTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary()) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagCalculationPurityPi.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kPiPlus ? 1. : 0.); - break; - case -1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kPiMinus ? 1. : 0.); - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcTofPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kPiPlus ? 1. : 0.); - break; - case -1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcTofPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kPiMinus ? 1. : 0.); - break; - } - } - } + void processRaw(const soa::Filtered::iterator& collision, const soa::Filtered& tracks, const aod::BCsWithTimestamps&) + { + const EventSelection eventSelection{initEvent(collision, tracks)}; + if ((eventSelection == EventSelection::Good || std::cmp_greater_equal(toI(eventSelection), toI(EventSelection::NPvContributors))) && holderMember.doStorageMiniTable && (std::uniform_real_distribution{}(holderMember.engineRandom)) * cgEvent.cfgFactorStorageMiniTable.value < 1.) { + const bool isGoodNPvContributors{eventSelection != EventSelection::NPvContributors}; + readCcdb(); + fillMiniTableRaw(tracks, isGoodNPvContributors); + fillMiniTableRaw(tracks, isGoodNPvContributors); + fillMiniTableRaw(tracks, isGoodNPvContributors); + } + if (eventSelection != EventSelection::Good) { + return; + } - if (groupAnalysis.cfgFlagCalculationPurityKa.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kKPlus ? 1. : 0.); - break; - case -1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kKMinus ? 1. : 0.); - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcTofKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kKPlus ? 1. : 0.); - break; - case -1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcTofKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kKMinus ? 1. : 0.); - break; - } - } - } + if (!cgAnalysis.cfgFlagQaTrack.value && !cgAnalysis.cfgFlagQaDca.value && !holderMember.doQaAcceptance && !holderMember.doQaPhi && !holderMember.doQaPid && !holderMember.doCalculationYield && !holderMember.doCalculationFluctuation) { + return; + } - if (groupAnalysis.cfgFlagCalculationPurityPr.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kProton ? 1. : 0.); - break; - case -1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kProtonBar ? 1. : 0.); - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcTofPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kProton ? 1. : 0.); - break; - case -1: - hrCalculationPurity.fill(HIST("CalculationPurity/pCentralityPtEtaPurityTpcTofPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, holderMcParticle.pdgCode == PDG_t::kProtonBar ? 1. : 0.); - break; - } - } - } - } + if (holderMember.doCalculationFluctuation) { + holderEvent.indexSubgroup = std::uniform_int_distribution{0, cgEvent.cfgNSubgroups.value - 1}(holderMember.engineRandom); + initCalculationFluctuation(); + holderDerivedData.clear(); + } - if ((groupAnalysis.cfgFlagCalculationFractionPrimaryPi.value || groupAnalysis.cfgFlagCalculationFractionPrimaryKa.value || groupAnalysis.cfgFlagCalculationFractionPrimaryPr.value) && holderTrack.hasTpcPid) { - if (groupAnalysis.cfgFlagCalculationFractionPrimaryPi.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kPiPlus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kPiMinus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kPiPlus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPiP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kPiMinus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPiM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - } - } - } + for (const auto& track : tracks) { + if (!initTrack(track)) { + continue; + } - if (groupAnalysis.cfgFlagCalculationFractionPrimaryKa.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kKPlus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kKMinus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kKPlus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofKaP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kKMinus) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofKaM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - } - } - } + if (holderMember.doQaAcceptance && holderTrack.eta * holderEvent.vz > 0. && std::abs(holderEvent.vz) > cgEvent.cfgCutMaxAbsVz.value - 1.) { + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + fillQaAcceptanceByParticleSpeciesAll(); + } - if (groupAnalysis.cfgFlagCalculationFractionPrimaryPr.value) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kProton) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kProtonBar) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - } - if (holderTrack.hasTofPid) { - switch (isPid(groupTrack.cfgFlagRejectionOthers.value)) { - case 1: - if (holderMcParticle.pdgCode == PDG_t::kProton) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPrP"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - case -1: - if (holderMcParticle.pdgCode == PDG_t::kProtonBar) { - hrCalculationFractionPrimary.fill(HIST("CalculationFractionPrimary/pCentralityPtEtaFractionPrimaryTpcTofPrM"), holderEvent.centrality, holderTrack.pt, holderTrack.eta, mcParticle.isPhysicalPrimary() ? 1. : 0.); - } - break; - } - } - } - } + if (holderMember.doQaPhi) { + fillQaPhiByParticleSpeciesAll(); + fillQaPhiByParticleSpeciesAll(); + fillQaPhiByParticleSpeciesAll(); + fillQaPhiByParticleSpeciesAll(); + } - if ((groupAnalysis.cfgFlagCalculationFluctuationCh.value || groupAnalysis.cfgFlagCalculationFluctuationKa.value || groupAnalysis.cfgFlagCalculationFluctuationPr.value) && (!groupTrack.cfgFlagMcParticlePhysicalPrimary.value || mcParticle.isPhysicalPrimary())) { - calculateFluctuation(); - } - } + if (holderMember.doQaPid) { + fillQaPidByParticleSpeciesAll(track); + fillQaPidByParticleSpeciesAll(track); + fillQaPidByParticleSpeciesAll(track); + fillQaPidByParticleSpeciesAll(track); + } - if (groupAnalysis.cfgFlagCalculationFluctuationCh.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNChPNChM"), holderEvent.centrality, holderEvent.nChP, holderEvent.nChM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChP"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChM"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChT"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorChN"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackChN->getProductFast(iOrderVector)); - } - } - if (groupAnalysis.cfgFlagCalculationFluctuationKa.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNKaPNKaM"), holderEvent.centrality, holderEvent.nKaP, holderEvent.nKaM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaP"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaM"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaT"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorKaN"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackKaN->getProductFast(iOrderVector)); - } - } - if (groupAnalysis.cfgFlagCalculationFluctuationPr.value) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hCentralityNPrPNPrM"), holderEvent.centrality, holderEvent.nPrP, holderEvent.nPrM); - for (std::int32_t const& iOrderVector : std::views::iota(0, static_cast(fluctuation_calculator_base::NOrderVectors))) { - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrP"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrP->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrM"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrM->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrT"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrT->getProductFast(iOrderVector)); - hrCalculationFluctuation.fill(HIST("CalculationFluctuation/hFluctuationCalculatorPrN"), holderEvent.centrality, holderEvent.subgroupIndex, iOrderVector, fluctuationCalculatorTrackPrN->getProductFast(iOrderVector)); - } + if (holderMember.doCalculationYield) { + fillCalculationYieldByParticleSpecies(); + fillCalculationYieldByParticleSpecies(); + fillCalculationYieldByParticleSpecies(); + } + + if (holderMember.doCalculationFluctuation) { + calculateFluctuationByParticleNumber(); + calculateFluctuationByParticleNumber(); + calculateFluctuationByParticleNumber(); + } + } + + if (holderMember.doCalculationFluctuation) { + fillCalculationFluctuationByParticleNumber(); + fillCalculationFluctuationByParticleNumber(); + fillCalculationFluctuationByParticleNumber(); + if (const std::optional> codeCollision{tiny_collision_codec::encode(holderEvent.vz, holderEvent.centrality, holderDerivedData.nsTracks)}; codeCollision.has_value()) { + pg.tinyCollision(std::get<0>(*codeCollision), std::get<1>(*codeCollision)); + for (aod::tiny_track::SignedEfficiency::type const& signedEfficiency : holderDerivedData.signedEfficienciesTrack) { + pg.tinyTrack(pg.tinyCollision.lastIndex(), signedEfficiency); } } } } - PROCESS_SWITCH(PartNumFluc, processMc, "Process MC data", false); + + PROCESS_SWITCH_FULL(PartNumFluc, processMc, ProcessMc, "Flag of processing MC data", false); + PROCESS_SWITCH_FULL(PartNumFluc, processRaw, ProcessRaw, "Flag of processing raw data", true); }; -WorkflowSpec defineDataProcessing(const ConfigContext& cfgc) +WorkflowSpec defineDataProcessing(const ConfigContext& configContext) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(configContext)}; } diff --git a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx index 9df8a0b2c58..45f4d3b2aa8 100644 --- a/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/radialFlowDecorr.cxx @@ -16,16 +16,14 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/FT0Corrected.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include #include #include #include +#include #include #include #include @@ -38,26 +36,31 @@ #include #include -#include -#include +#include +#include #include #include #include +#include #include +#include #include #include #include +#include #include +#include + #include #include #include #include #include #include +#include #include #include -#include #include #include @@ -68,19 +71,19 @@ using namespace constants::math; struct RadialFlowDecorr { - static constexpr int KPidPionOne = 1; - static constexpr int KPidKaonTwo = 2; - static constexpr int KPidProtonThree = 3; - static constexpr int KConstTen = 10; + // --- fixed constants --------------------------------------------------------- + static constexpr int KIntM = 4; // pT-moment order used in the sums (need m up to 3 for c3) + static constexpr int KIntK = 4; // weight-power order used in the sums (need k up to 3 for c3) + + static constexpr int KNEtaHalfBinsMax = 8; // 0.8 / 0.1: finest (DATA) bins per side + static constexpr int KNEtaMax = 2 * KNEtaHalfBinsMax + 1; // + index-0 full-range reference bin + + // Bootstrap: at most KMaxBoot replicas of 8 profiles each, in one registry of at most + // 512 objects. nBoot = min(cfgNBootstrap, KMaxBoot) replicas are filled. + static constexpr int KMaxBoot = 60; - static constexpr int KnFt0cCell = 96; - static constexpr int KIntM = 3; - static constexpr int KIntK = 3; - static constexpr int KNEta = 17; static constexpr float KFloatEpsilon = 1e-6f; - static constexpr int KPiPlus = 211; - static constexpr int KKPlus = 321; - static constexpr int KProton = 2212; + static constexpr float KEtaEdgeTolerance = 1e-3f; // slack for cfgCutEta being an integer multiple of the bin width static constexpr float KBinOffset = 0.5f; static constexpr float KPhiMin = 0.f; static constexpr int KNbinsZvtx = 240; @@ -94,47 +97,22 @@ struct RadialFlowDecorr { static constexpr float KEtaMin = -1.2f; static constexpr float KEtaMax = 1.2f; static constexpr int KNbinsPhi = 64; - static constexpr float KEtaAxisMin = -0.8f; - static constexpr float KEtaAxisMax = 0.8f; static constexpr int KNbinsPtRes = 50; - static constexpr float KPtResMax = 1.f; static constexpr int KNbinsEtaRes = 100; - static constexpr float KEtaResMax = 0.5f; static constexpr int KNbinsVz = 80; static constexpr float KVzMin = -40.f; static constexpr float KVzMax = 40.f; - static constexpr float KVzResMax = 20.f; static constexpr int KNbinsEtaFine = 20; static constexpr float KEtaFineMax = 1.f; - static constexpr int KNbinsDca = 400; - static constexpr float KDcaMax = 0.2f; - static constexpr float KCentMax = 90; - - enum PIDIdx { - kInclusiveIdx = 0, - kPiMinusIdx, - kPiPlusIdx, - kPiAllIdx, - kKaMinusIdx, - kKaPlusIdx, - kKaAllIdx, - kAntiPrIdx, - kPrIdx, - kPrAllIdx, - KNsp - }; + // occupancy QA axis + static constexpr int KNbinsOccQA = 150; + static constexpr float KOccQAMax = 15000.f; - inline static const std::vector pidSuffix = {"", "_PiMinus", "_PiPlus", "_PiAll", "_KaMinus", "_KaPlus", "_KaAll", "_AntiPr", "_Pr", "_PrAll"}; - - struct PIDMeanSigmaMap { - static constexpr int MaxCentBins = 100; - double meanTOF[KNsp][MaxCentBins] = {{0.0}}; - double sigmaTOF[KNsp][MaxCentBins] = {{1.0}}; // Default sigma = 1 - double meanTPC[KNsp][MaxCentBins] = {{0.0}}; - double sigmaTPC[KNsp][MaxCentBins] = {{1.0}}; // Default sigma = 1 - }; - - PIDMeanSigmaMap* pidMeanSigmaMap = nullptr; + // Forward detectors: FT0 channels 0-95 are A side, 96-207 C side; FV0-A has 48 cells. + static constexpr int KNChFT0A = 96; + static constexpr int KNChFT0 = 208; + static constexpr int KNChFV0 = 48; + static constexpr int KNFwdMax = 32; // detector totals + eta sub-bins enum ECentralityEstimator { kCentFT0C = 1, @@ -148,3443 +126,3113 @@ struct RadialFlowDecorr { kOO = 3, kpp = 4 }; + enum EITSLayerCut { + kITSLayerAuto = -1, // nominal: pp kIsGoodITSLayer0123, other systems kIsGoodITSLayersAll + kITSLayerNone = 0, + kITSLayer3 = 1, + kITSLayer0123 = 2, + kITSLayersAll = 3 + }; + enum EFwdDet { + kFT0A = 0, + kFT0C, + kFV0A, + kNFwdDet + }; + // Histogram level: DATA uses level 0 only; MC fills truth, reco and efficiency-corrected reco. + enum ELevel { + kGen = 0, + kReco, + kRecoEffCorr, + kNLevels + }; + // Last axis of the multi-dimensional objects. + enum EMeanObs { // Prof_Mean_Nch_etabin_obs + kMeanPt = 0, + kMeanN, + kNMeanObs + }; + enum EFlucObs { // Prof_Obs_Nch_etabin_obs + kObsN = 0, // N(eta), efficiency-corrected + kObsMeanPt, // [pT](eta) + kObsC2, // c2 within the bin + kObsC3, // c3 within the bin + kObsC2Sub, // , mirror pair, at the A bin + kObsC3Sub, // c2_A _C at A (AAB) and c2_C _A at C (ABB), mirror pair + kNFlucObs + }; + enum EEffCat { // efficiency maps h3_* (centrality, pT, eta) + kEffAllPrim = 0, // charged primaries of the MC collision + kEffRecoMatchedPrim, // reco tracks of primaries of the same MC collision, at the MC pT and eta + kEffAllReco, + kEffRecoSec, // secondaries of the same MC collision + kEffRecoOtherColl, // particles of another MC collision (pile-up, wrong association) + kEffRecoFake, // reco tracks without MC label + kNEffCat + }; + inline static const std::array effMapName = { + "h3_AllPrimary", "h3_RecoMatchedToPrimary", "h3_AllReco", "h3_RecoUnMatchedToPrimary_Secondary", + "h3_RecoUnMatchedToPrimary_OtherCollision", "h3_RecoUnMatchedToPrimary_Fake"}; + enum ETrackOrigin { // MCQA/Hist3D_Nch_pT_origin + kOriginPrimary = 0, + kOriginSecondary, + kOriginOtherColl, + kOriginNoLabel, + kNOrigin + }; + enum EVtxQA { // MCQA/Hist2D_dVz_type + kVtxSingle = 0, // the only reco collision of the MC collision + kVtxSplitBest, // the one used among several + kVtxSplitOther, // the others (skipped) + kNVtxQA + }; + enum EFlatType { // hFlatMaps + kFlatRaw = 0, + kFlatEffWtd, // (1 - fake) / eff (Base only) + kFlatWtd, // all weights of the pass + kNFlatType + }; + enum EChanQA { // FwdQA/Prof_Channel_qa + kChAmpRaw = 0, + kChAmpCorr, + kChEta, + kNChQA + }; + // Event cut flow (hEvtCount, x axis); the last two bins count selected events only + enum EEvtCut { + kEvAll = 0, + kEvSel8, + kEvVtxZ, + kEvGoodZvtx, + kEvTFBorder, + kEvITSROFBorder, + kEvVertexITSTPC, + kEvSameBunch, + kEvTimeRange, + kEvITSLayer, + kEvAddPileup, + kEvVariationCut, + kEvCent, + kEvInfoPrevRof, // selected events passing kNoHighMultCollInPrevRof + kEvInfoOccUnknown, // selected events with occupancy < 0 (time-frame border) + kNEvCut + }; + // Track cut flow (hTrkCount, x axis) + enum ETrkCut { + kTrkAll = 0, + kTrkSign, + kTrkHasColl, + kTrkPVContrib, + kTrkPtEta, + kTrkGlobal, + kTrkITS, + kTrkTPC, + kTrkTPCChi2, + kTrkDCAz, + kTrkVariationCut, + kTrkWeight, // valid efficiency (and flattening) weight: used + kNTrkCut + }; + + // Cut QA (processCutQA): each quantity the Base selection cuts on, before and after it. + enum ECutQAStage { + kQABefore = 0, // events: all collisions; tracks: all tracks of the selected events + kQAAfter, // Base-selected events / tracks + kNQAStage + }; + enum ECutQAEvt1D { + kQAVtxZ = 0, + kQACent, + kQAOccupancy, + kNQAEvt1D + }; + enum ECutQAEvt2D { + kQANchNPV = 0, + kQANchCent, + kNQAEvt2D + }; + enum ECutQATrk1D { + kQAPt = 0, + kQAEta, + kQAItsNCls, + kQAItsChi2, + kQATpcNClsFound, + kQATpcCrossedRows, + kQATpcCrossedRowsOverFindable, + kQATpcChi2, + kQAPVContributor, + kQAGlobalTrack, + kNQATrk1D + }; + enum ECutQATrk2D { + kQADcaXYPt = 0, + kQADcaZPt, + kNQATrk2D + }; + + // Systematic variations, all evaluated in every job. Base is the nominal measurement. + // The cut values are the K* constants below; only the occupancy limits are configurable. + // Cent : FT0M instead of the nominal estimator (cfgCentralityChoice, FT0C) + // Chi2TPC : chi2 per TPC cluster < 2 instead of cfgCutTpcChi2NCl + // DCAxy : |DCAxy| < 5/7 x (globalTrack pT-dependent DCAxy cut), i.e. 5 instead of 7 sigma + // DCAz : |DCAz| < 1 cm instead of cfgCutTracKDcaMaxZ + // DCAxy and DCAz are null checks: with the nominal isPVContributor requirement even + // 5 sigma DCAxy or |DCAz| < 0.05 cm removes only ~0.2% of the selected tracks + // Eff : efficiency scaled by -10% at all pT (cancels in [pT]: a check) + // EffpTDep : efficiency scaled by -10% at cfgPtMin, linearly to 0 at 10 GeV/c + // Flat : no acceptance (flattening) weights + // ITSLayer : ITS good-layer requirement one step looser (All -> 0123, 0123 -> Layer3) + // NITS : itsNCls >= 4 instead of cfgITScluster + // NTPC : tpcNClsFound >= 80 instead of cfgTPCcluster + // Pileup : additionally kNoCollInRofStandard + // Vz : |z_vtx| < 8 cm instead of cfgVtxZCut + // NchBin2 : Base selection and inputs, observables (and its own mean pass) in N_ch bins + // of 2 (PbPb, OO) or 1 (pp) tracks: cross-check of the N_ch binning of the mean subtraction + // Occupancy : 0 <= trackOccupancyInTimeRange < cfgSystOccMax* of the system + // PileupNSigma : n-sigma pile-up bands narrower by 1 (mean and fluctuation passes) + // Since the observables are stored versus N_ch, Cent differs from Base only in the events + // kept (cent < cfgCentMax) and in its (centrality, N_ch) map, which defines its classes. + enum ESystType { + kSystBase = 0, + kSystCent, + kSystChi2TPC, + kSystDCAxy, + kSystDCAz, + kSystEff, + kSystEffPtDep, + kSystFlat, + kSystITSLayer, + kSystNITS, + kSystNTPC, + kSystPileup, + kSystVz, + kSystNchBin, + kSystOccupancy, + kSystPileupNSigma, + kNSyst + }; + inline static const std::array systName = { + "Base", "systCent", "systChi2TPC", "systDCAxy", "systDCAz", "systEff", "systEffpTDep", + "systFlat", "systITSLayer", "systNITS", "systNTPC", "systPileup", "systVz", "systNchBin2", + "systOccupancy", "systPileupNSigma"}; + static constexpr uint32_t KAllSystMask = (1 << kNSyst) - 1; + static constexpr int KNSystPerRegistry = 4; // variations 1..15 are spread over 4 registries + // Cut of each variation (fixed; the occupancy limits are configurables, cfgSystOccMax*). + // Pileup adds kNoCollInRofStandard; ITSLayer loosens the nominal ITS good-layer requirement by one step. + static constexpr int KSystCentralityChoice = 2; // Cent: FT0M + static constexpr float KSystChi2TPC = 2.f; // Chi2TPC: chi2 per TPC cluster < 2 (nominal 2.5) + static constexpr float KSystDcaXyScale = 5.f / 7.f; // DCAxy: 5 sigma (nominal 7 sigma) + static constexpr float KSystDcaMaxZ = 1.f; // DCAz: |DCAz| < 1 cm (nominal 2 cm) + static constexpr float KEffVarPercent = -10.f; // Eff, EffpTDep: efficiency scaled by -10% + static constexpr float KEffVarPtZero = 10.f; // EffpTDep: variation falls linearly to 0 at 10 GeV/c + static constexpr int KSystITScluster = 4; // NITS: itsNCls >= 4 (nominal 5) + static constexpr int KSystTPCcluster = 80; // NTPC: tpcNClsFound >= 80 (nominal 70) + static constexpr float KSystVtxZCut = 8.f; // Vz: |z_vtx| < 8 cm (nominal 10 cm) + static constexpr int KSystNchBinWidthPbPb = 2; // NchBin2: N_ch bin width, PbPb (nominal 10) + static constexpr int KSystNchBinWidthOO = 2; // NchBin2: OO and NeNe (nominal 5) + static constexpr int KSystNchBinWidthPP = 1; // NchBin2: pp (nominal 2) + static constexpr float KSystPupnSigStep = 1.f; // PileupNSigma: n-sigma bands narrower by 1 + static constexpr double KMinBandEntries = 50.; // n-sigma band bins with fewer flattening-pass events apply no band cut + + // Output folder of a variation (Base keeps the top level). + static std::string systPrefix(int v) { return v == kSystBase ? std::string{} : systName[v] + "/"; } + // Suffix appended to every CCDB object path of a variation. + static std::string systSuffix(int v) { return v == kSystBase ? std::string{} : "_" + systName[v]; } + // Variation whose efficiency map v uses. Cent, Eff, EffpTDep, Flat, NchBin2 and PileupNSigma + // have the Base selection in the efficiency pass and use the Base map; Eff and EffpTDep scale + // it when applied. (The n-sigma bands are applied only in the mean and fluctuation passes.) + static constexpr int effSource(int v) + { + return (v == kSystCent || v == kSystEff || v == kSystEffPtDep || v == kSystFlat || v == kSystNchBin || v == kSystPileupNSigma) ? static_cast(kSystBase) : v; + } + // Variation whose flattening map v uses. Cent, NchBin2 and PileupNSigma use the Base map; + // Flat uses none (-1). + static constexpr int flatSource(int v) + { + if (v == kSystFlat) { + return -1; + } + return (v == kSystCent || v == kSystNchBin || v == kSystPileupNSigma) ? static_cast(kSystBase) : v; + } + static constexpr float KinvalidCentrality = -1.0f; - inline static const std::vector etaLw = { - -0.8, - -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7}; - inline static const std::vector etaUp = { - 0.8, - -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}; - - Configurable cfgVtxZCut{"cfgVtxZCut", 10.f, "z-vertex range"}; - Configurable cfgPtMin{"cfgPtMin", 0.2f, "min pT"}; - Configurable cfgPtMax{"cfgPtMax", 5.0f, "max pT"}; - Configurable cfgEtaCut{"cfgEtaCut", 0.8f, "|η| cut"}; - Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; - Configurable cfgCutTracKDcaMaxZ{"cfgCutTracKDcaMaxZ", 2.0f, "Maximum DcaZ"}; - Configurable cfgCutTracKDcaMaxXY{"cfgCutTracKDcaMaxXY", 0.2f, "Maximum DcaZ"}; - - Configurable cfgPtDepDCAxy{"cfgPtDepDCAxy", false, "Use pt-dependent DCAxy cut"}; - Configurable cfgDcaXyP0{"cfgDcaXyP0", 0.0026f, "p0 for DCAxy"}; - Configurable cfgDcaXyP1{"cfgDcaXyP1", 0.005f, "p1 for DCAxy"}; - Configurable cfgDcaXyP2{"cfgDcaXyP2", 1.01f, "p2 for DCAxy"}; - - Configurable cfgPtDepDCAz{"cfgPtDepDCAz", false, "Use pt-dependent DCAz cut"}; - Configurable cfgDcaZP0{"cfgDcaZP0", 0.0026f, "p0 for DCAz"}; - Configurable cfgDcaZP1{"cfgDcaZP1", 0.005f, "p1 for DCAz"}; - Configurable cfgDcaZP2{"cfgDcaZP2", 1.01f, "p2 for DCAz"}; - - Configurable cfgITScluster{"cfgITScluster", 1, "Minimum Number of ITS cluster"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 80, "Minimum Number of TPC cluster"}; - Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; - Configurable cfgCutPtUpperTPC{"cfgCutPtUpperTPC", 0.6f, "Upper pT cut for PID using TPC only"}; - Configurable cfgnSigmaOtherParticles{"cfgnSigmaOtherParticles", 3.0f, "PID nSigma cut to remove other particles (default:3)"}; - Configurable cfgnSigmaCutTPC{"cfgnSigmaCutTPC", 2.0f, "PID nSigma cut for TPC"}; - Configurable cfgnSigmaCutTOF{"cfgnSigmaCutTOF", 2.0f, "PID nSigma cut for TOF"}; - Configurable cfgnSigmaCutCombTPCTOF{"cfgnSigmaCutCombTPCTOF", 2.0f, "PID nSigma combined cut for TPC and TOF"}; - - Configurable cfgCutPtLower{"cfgCutPtLower", 0.2f, "Lower pT cut"}; - Configurable cfgCutPtUpper{"cfgCutPtUpper", 10.0f, "Higher pT cut for inclusive hadron analysis"}; - Configurable cfgCutPtUpperPID{"cfgCutPtUpperPID", 6.0f, "Higher pT cut for identified particle analysis"}; + + // Observable eta binning, filled at init(): index 0 = full-range reference bin. + std::vector etaLw; + std::vector etaUp; + int nEta = 9; // active eta-bin count (set in init()) + int nBoot = 0; // active bootstrap samples (set in init()) + bool doBoot = false; // bootstrap active for this run (base data fluc only) + + // Variations filled by each pass (set in init()): maskEff in the MC efficiency pass, + // maskFlat in the flattening passes, maskAll in the mean and fluctuation passes. + uint32_t maskAll = 1; + uint32_t maskEff = 1; + uint32_t maskFlat = 1; + int itsLayerNom = kITSLayersAll; + int itsLayerVar = kITSLayer0123; + int occupancyMax = 0; // Occupancy variation cut of this system + float pupNSigma = 5.f; // width of the n-sigma pile-up bands of this system + int nchWidth = 10; // N_ch bin width of the observables of this system + int nchWidthNchBin2 = 2; // N_ch bin width of NchBin2 for this system + + // Forward bins: 0-2 detector totals (FT0A, FT0C, FV0A), then the eta sub-bins of each detector. + int nFwd = kNFwdDet; + std::array, kNFwdDet> fwdEdges; + std::array fwdOffset{}; + std::vector fwdLabels; + + // --- configurables ----------------------------------------------------------- + // Event selection (nominal) + struct : ConfigurableGroup { + Configurable cfgVtxZCut{"cfgVtxZCut", 10.f, "|z_{vtx}| acceptance (cm)"}; + Configurable cfgCentralityChoice{"cfgCentralityChoice", 1, "Which centrality estimator? 1-->FT0C, 2-->FT0M, 3-->FDDM, 4-->FV0A"}; + Configurable cfgCentMax{"cfgCentMax", 99.f, "Events with centrality below this value (%) are kept"}; + Configurable cfgIsGoodZvtxFT0VsPV{"cfgIsGoodZvtxFT0VsPV", true, "Good Vertexing cut"}; + Configurable cfgEvSelNoTimeFrameBorder{"cfgEvSelNoTimeFrameBorder", true, "kNoTimeFrameBorder"}; + Configurable cfgEvSelNoITSROFrameBorder{"cfgEvSelNoITSROFrameBorder", true, "kNoITSROFrameBorder"}; + Configurable cfgEvSelIsVertexITSTPC{"cfgEvSelIsVertexITSTPC", true, "kIsVertexITSTPC"}; + Configurable cfgEvSelNoSameBunchPileup{"cfgEvSelNoSameBunchPileup", true, "Pileup removal: kNoSameBunchPileup"}; + Configurable cfgEvSelNoCollInTimeRangeStd{"cfgEvSelNoCollInTimeRangeStd", true, "Pileup removal: kNoCollInTimeRangeStandard"}; + Configurable cfgITSLayerCut{"cfgITSLayerCut", -1, "ITS good-layer cut: -1 auto (pp 0123, else All), 0 none, 1 Layer3, 2 Layer0123, 3 LayersAll"}; + } cfgEvSel; + + // Pile-up cuts on the correlations of N_ch (global tracks) with N_PV and centrality. + // The linear cut is applied in every pass. The flattening passes (MC and DATA) fill + // Hist2D_globalTracks_cent and Hist2D_globalTracks_PVTracks after it; the mean +- n sigma + // bands of N_ch from these maps are then applied in the mean and fluctuation passes. + struct : ConfigurableGroup { + Configurable cfgPupnSigPbPb{"cfgPupnSigPbPb", 5.f, "Width of the n-sigma bands (number of RMS), PbPb"}; + Configurable cfgPupnSigOO{"cfgPupnSigOO", 5.f, "Width of the n-sigma bands (number of RMS), OO and NeNe"}; + Configurable cfgPupnSigPP{"cfgPupnSigPP", 6.f, "Width of the n-sigma bands (number of RMS), pp"}; + Configurable cfgApplySigPupCut{"cfgApplySigPupCut", 0, "Mean/Fluc: n-sigma band of N_ch versus centrality"}; + Configurable cfgApplySigPupCutPV{"cfgApplySigPupCutPV", 0, "Mean/Fluc: n-sigma band of N_ch versus N_PV"}; + Configurable cfgApplyLinPupCut{"cfgApplyLinPupCut", 0, "Linear cut of N_ch versus N_PV (every pass)"}; + Configurable cfgLinPupParam0{"cfgLinPupParam0", 3.0f, "(Upper) Linear Pileup Cut Const"}; + Configurable cfgLinPupParam1{"cfgLinPupParam1", 3.0f, "(Upper) Linear Pileup Slope"}; + Configurable cfgLinPupParam2{"cfgLinPupParam2", 3.0f, "(Lower) Linear Pileup Cut Const"}; + Configurable cfgLinPupParam3{"cfgLinPupParam3", 3.0f, "(Lower) Linear Pileup Slope"}; + } cfgPileup; + + // Track selection (nominal), on top of isGlobalTrack (Run 3 globalTrack, incl. its 7 sigma pT-dependent DCAxy cut) + Configurable cfgPtMin{"cfgPtMin", 0.2f, "min pT for observables"}; + Configurable cfgPtMax{"cfgPtMax", 5.0f, "max pT for observables"}; + Configurable cfgCutPtLower{"cfgCutPtLower", 0.2f, "Lower pT cut (track selection)"}; + Configurable cfgCutPtUpper{"cfgCutPtUpper", 10.0f, "Higher pT cut (track selection)"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "absolute Eta cut"}; - Configurable cfgNsubsample{"cfgNsubsample", 10, "Number of subsamples"}; - Configurable cfgCentralityChoice{"cfgCentralityChoice", 1, "Which centrality estimator? 1-->FT0C, 2-->FT0M, 3-->FDDM, 4-->FV0A"}; - Configurable cfgEvSelNoSameBunchPileup{"cfgEvSelNoSameBunchPileup", true, "Pileup removal"}; - Configurable cfgUseGoodITSLayerAllCut{"cfgUseGoodITSLayerAllCut", true, "Remove time interval with dead ITS zone"}; - Configurable cfgIsGoodZvtxFT0VsPV{"cfgIsGoodZvtxFT0VsPV", true, "Good Vertexing cut"}; - - Configurable cfgPupnSig{"cfgPupnSig", 6.0f, "Additional Pileup Cut"}; - Configurable cfgApplySigPupCut{"cfgApplySigPupCut", 0, "nSig Pileup Cut"}; - Configurable cfgApplyLinPupCut{"cfgApplyLinPupCut", 0, "Lin Pileup Cut"}; - Configurable cfgLinPupParam0{"cfgLinPupParam0", 3.0f, "(Upper) Linear Pileup Cut Const"}; - Configurable cfgLinPupParam1{"cfgLinPupParam1", 3.0f, "(Upper) Linear Pileup Slope"}; - Configurable cfgLinPupParam2{"cfgLinPupParam2", 3.0f, "(Lower) Linear Pileup Cut Const"}; - Configurable cfgLinPupParam3{"cfgLinPupParam3", 3.0f, "(Lower) Linear Pileup Slope"}; - - Configurable cfgNchPbMax{"cfgNchPbMax", 4000, "Max Nch range for PbPb collisions"}; - Configurable cfgNchOMax{"cfgNchOMax", 800, "Max Nch range for OO collisions"}; - - Configurable cfgSys{"cfgSys", 1, "Efficiency to be used for which system? 1-->PbPb, 2-->NeNe, 3-->OO, 4-->pp"}; + Configurable cfgEtaBinWidth{"cfgEtaBinWidth", 0.1f, "DATA only: width of each narrow observable eta bin (eta units); cfgCutEta must be an integer multiple so eta=0 is a bin edge. MC is pinned to 0.2."}; + Configurable cfgCutTracKDcaMaxZ{"cfgCutTracKDcaMaxZ", 2.0f, "Maximum |DCAz| (cm)"}; + Configurable cfgITScluster{"cfgITScluster", 5, "Nominal: itsNCls >= value"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Nominal: tpcNClsFound >= value"}; + Configurable cfgCutTpcChi2NCl{"cfgCutTpcChi2NCl", 2.5f, "Nominal: tpcChi2NCl < value"}; + Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum Number of TPC crossed-rows"}; + + // Systematic variations (names stay flat in the JSON) + struct : ConfigurableGroup { + Configurable cfgSystMask{"cfgSystMask", static_cast(KAllSystMask), "Bitmask of active variations, bit i = ESystType i: 0 Base (always on), 1 Cent, 2 Chi2TPC, 3 DCAxy, 4 DCAz, 5 Eff, 6 EffpTDep, 7 Flat, 8 ITSLayer, 9 NITS, 10 NTPC, 11 Pileup, 12 Vz, 13 NchBin2, 14 Occupancy, 15 PileupNSigma"}; + Configurable cfgDcaXyP0{"cfgDcaXyP0", 0.0105f, "globalTrack DCAxy cut p0 + p1 / pT^p2 (trackselection dcaSetup=0; dcaSetup=1: 0.004, 0.013, 1)"}; + Configurable cfgDcaXyP1{"cfgDcaXyP1", 0.035f, "globalTrack DCAxy cut p1"}; + Configurable cfgDcaXyP2{"cfgDcaXyP2", 1.1f, "globalTrack DCAxy cut p2"}; + Configurable cfgSystOccMaxPbPb{"cfgSystOccMaxPbPb", 2000, "Occupancy: maximum trackOccupancyInTimeRange, PbPb"}; + Configurable cfgSystOccMaxOO{"cfgSystOccMaxOO", 1000, "Occupancy: maximum trackOccupancyInTimeRange, OO and NeNe"}; + Configurable cfgSystOccMaxPP{"cfgSystOccMaxPP", 500, "Occupancy: maximum trackOccupancyInTimeRange, pp"}; + } cfgSyst; + + // N_ch axes. Largest N_ch (global tracks) seen in the data: 3542 (PbPb), 328 (OO), 72 (pp) + struct : ConfigurableGroup { + Configurable cfgNchPbMax{"cfgNchPbMax", 4000, "Max N_ch (global tracks) and N_PV of the axes, PbPb"}; + Configurable cfgNchOMax{"cfgNchOMax", 400, "Max N_ch (global tracks) and N_PV of the axes, OO and NeNe"}; + Configurable cfgNchPPMax{"cfgNchPPMax", 100, "Max N_ch (global tracks) and N_PV of the axes, pp"}; + Configurable cfgNchBinWidthPbPb{"cfgNchBinWidthPbPb", 10, "N_ch bin width (tracks) of the observables, PbPb; Mean and Fluc passes must use the same value"}; + Configurable cfgNchBinWidthOO{"cfgNchBinWidthOO", 5, "N_ch bin width (tracks) of the observables, OO and NeNe"}; + Configurable cfgNchBinWidthPP{"cfgNchBinWidthPP", 2, "N_ch bin width (tracks) of the observables, pp"}; + } cfgNch; + + Configurable cfgSys{"cfgSys", 1, "Which collision system? 1-->PbPb, 2-->NeNe, 3-->OO, 4-->pp"}; + Configurable cfgNBootstrap{"cfgNBootstrap", 20, "Number of Poisson bootstrap samples (Base, DataFluc only)"}; + Configurable cfgBootstrapSeed{"cfgBootstrapSeed", 0, "TRandom3 seed for bootstrap (0 = machine-random per job)"}; + Configurable cfgFlat{"cfgFlat", false, "Whether to use flattening weights"}; Configurable cfgEff{"cfgEff", false, "Whether to use Efficiency weights"}; - Configurable cfgZDC{"cfgZDC", false, "Whether to use ZDC for pileup histograms"}; + + // Forward multiplicity (FT0A, FT0C, FV0A amplitudes) correlated with mid-rapidity [pT] and N + struct : ConfigurableGroup { + Configurable cfgFwd{"cfgFwd", true, "Mean/Fluc: forward amplitudes and their covariance with [pT](eta) and N(eta)"}; + Configurable cfgFwdGainEq{"cfgFwdGainEq", true, "DATA: apply the FT0/FV0 gain equalisation from cfgGainEqPath"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path of the FT0/FV0 gain equalisation (+ /FT0, /FV0); central objects per run for LHC23zzh Pb-Pb and 2025 O-O/Ne-Ne"}; + Configurable cfgFwdVzCorr{"cfgFwdVzCorr", true, "Channel eta relative to the primary vertex (else to z = 0)"}; + Configurable> cfgFwdEtaEdgesFT0A{"cfgFwdEtaEdgesFT0A", {3.5f, 4.2f, 4.9f}, "FT0A eta sub-bin edges"}; + Configurable> cfgFwdEtaEdgesFT0C{"cfgFwdEtaEdgesFT0C", {-3.3f, -2.7f, -2.1f}, "FT0C eta sub-bin edges"}; + Configurable> cfgFwdEtaEdgesFV0A{"cfgFwdEtaEdgesFV0A", {2.2f, 2.7f, 3.2f, 3.9f, 4.5f, 5.1f}, "FV0A eta sub-bin edges"}; + } cfgFwdOpt; Configurable cfgCCDBurl{"cfgCCDBurl", "https://alice-ccdb.cern.ch", "ccdb url"}; Configurable cfgCCDBUserPath{"cfgCCDBUserPath", "/Users/s/somadutt", "Base CCDB path"}; - ConfigurableAxis cfgAxisCent{"cfgAxisCent", {0.0, 1.0, 5.0, 10, 20, 40, 60, 80, 100}, "centrality axis (percentile)"}; - - const AxisSpec centAxis{cfgAxisCent, "Centrality (%)"}; - const AxisSpec centAxis1Per{100, 0.0, 100.0, "Centrality (%)"}; - AxisSpec nChAxis{1, 0., 1., "Nch", "Nch"}; - AxisSpec nChAxis2{1, 0., 1., "Nch", "Nch"}; - - const AxisSpec vzAxis{5, -12.5, 12.5, "Vz"}; - const AxisSpec chgAxis{3, -1.5, 1.5}; - const AxisSpec pTAxis{{0.0, 0.2, 0.4, 0.6, 0.8, 1, 3, 5, 7, 10}, "pT Axis"}; - const AxisSpec etaAxis{{-0.9, -0.8, -0.7, -0.6, -0.5, -0.4, -0.3, -0.2, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9}, "Eta"}; - const AxisSpec phiAxis{KNbinsPhi, KPhiMin, TwoPI, "#phi"}; - const AxisSpec etaBinAxis{KNEta + 1, -0.5, KNEta + 0.5, "#eta bin Number"}; - const AxisSpec spBinAxis{KNsp + 1, -KBinOffset, static_cast(KNsp) + KBinOffset, "species index Number"}; - - const AxisSpec gapAxis{{-1.55, -1.45, -1.35, -1.25, -1.15, -1.05, -0.95, -0.85, - -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, - 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, - 0.85, 0.95, 1.05, 1.15, 1.25, 1.35, 1.45, 1.55}, - "Gap"}; - - const AxisSpec sumAxis{{-1.55, -1.45, -1.35, -1.25, -1.15, -1.05, -0.95, -0.85, - -0.75, -0.65, -0.55, -0.45, -0.35, -0.25, -0.15, -0.05, - 0.05, 0.15, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, - 0.85, 0.95, 1.05, 1.15, 1.25, 1.35, 1.45, 1.55}, - "Sum"}; - - Configurable cfgRunMCGetNSig{"cfgRunMCGetNSig", false, "Run MC pass to get mean of Nsig Plots"}; + // --- axes -------------------------------------------------------------------- + AxisSpec centAxis1Per{100, 0.0, 100.0, "Centrality (%)"}; + AxisSpec centAxisBoot{20, 0.0, 100.0, "Centrality (%)"}; // bootstrap replicas: final 5% classes + AxisSpec multQAAxis{1, 0., 1., "Nch", "Nch"}; // QA (N_PV correlations) + AxisSpec nchAxisObs{1, 0., 1., "N_{ch}"}; // every observable (bins of nchWidth) + AxisSpec nchAxisNchBin2{1, 0., 1., "N_{ch}"}; // NchBin2 (bins of nchWidthNchBin2) + AxisSpec centAxisEff{{0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "Centrality (%)"}; // efficiency maps + + AxisSpec vzAxis{5, -12.5, 12.5, "Vz"}; + AxisSpec chgAxis{3, -1.5, 1.5}; + AxisSpec pTAxis{{0.0, 0.2, 0.4, 0.6, 0.8, 1, 3, 5, 7, 10}, "pT Axis"}; + AxisSpec phiAxis{KNbinsPhi, KPhiMin, TwoPI, "#phi"}; + + // etaFlatAxis is the *fixed* granularity used only for the flattening map, so + // that a flattening map is reusable across the 0.1/0.2 observable binnings. + AxisSpec etaFlatAxis{{-0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}, "#eta"}; + + // etaAxis (physical) and etaBinAxis (integer index) follow the observable + // binning and are rebuilt in init(). Placeholders here. + AxisSpec etaAxis{9, -0.9, 0.9, "#eta"}; + AxisSpec etaBinAxis{10, -0.5, 9.5, "#eta bin Number"}; + AxisSpec gapAxis{15, -1.5, 1.5, "#Delta#eta"}; + AxisSpec sumAxis{15, -1.5, 1.5, "#Sigma#eta"}; + AxisSpec fwdAxis{kNFwdDet, -0.5, static_cast(kNFwdDet) - 0.5, "forward bin"}; + + // --- process switches -------------------------------------------------------- Configurable cfgRunGetEff{"cfgRunGetEff", false, "Run MC pass to build efficiency/fake maps"}; - Configurable cfgRunGetMCFlat{"cfgRunGetMCFlat", false, "Run MC to Get Flattening Weights"}; - Configurable cfgRunMCMean{"cfgRunMCMean", false, "Run MC mean(pT) & mean(Et)"}; + Configurable cfgRunGetMCFlat{"cfgRunGetMCFlat", false, "Run MC to get flattening weights"}; + Configurable cfgRunMCMean{"cfgRunMCMean", false, "Run MC mean(pT)"}; Configurable cfgRunMCFluc{"cfgRunMCFluc", false, "Run MC fluctuations (C2, subevent)"}; - Configurable cfgRunDataGetNSig{"cfgRunDataGetNSig", false, "Run MC pass to get mean of Nsig Plots"}; - Configurable cfgRunGetDataFlat{"cfgRunGetDataFlat", false, "Run Data Get Flattening Weights"}; - Configurable cfgRunDataMean{"cfgRunDataMean", false, "Run DATA mean(pT) & mean(Et)"}; + Configurable cfgRunGetDataFlat{"cfgRunGetDataFlat", false, "Run data get flattening weights"}; + Configurable cfgRunDataMean{"cfgRunDataMean", false, "Run DATA mean(pT)"}; Configurable cfgRunDataFluc{"cfgRunDataFluc", false, "Run DATA fluctuations (C2, subevent)"}; - Service ccdb; - Service pdg; - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - struct InternalState { - std::array hEff{}; - std::array hFake{}; - std::array hFlatWeight{}; - - std::vector> mLimitsNchCent; - float mMinXNchCent = 0, mMaxXNchCent = 0; - - TProfile3D* pmeanTruNchEtabinSpbinStep2 = nullptr; - TProfile3D* pmeanRecoNchEtabinSpbinStep2 = nullptr; - TProfile3D* pmeanRecoEffcorrNchEtabinSpbinStep2 = nullptr; + Service ccdb{}; + Service pdg{}; - TProfile3D* pmeanMultTruNchEtabinSpbinStep2 = nullptr; - TProfile3D* pmeanMultRecoNchEtabinSpbinStep2 = nullptr; - TProfile3D* pmeanMultRecoEffcorrNchEtabinSpbinStep2 = nullptr; - - TProfile3D* pmeanNchEtabinSpbinStep2 = nullptr; - TProfile3D* pmeanMultNchEtabinSpbinStep2 = nullptr; - - TProfile* pmeanFT0AmultpvStep2 = nullptr; - TProfile* pmeanFT0CmultpvStep2 = nullptr; - } state; - o2::ft0::Geometry ft0Det; + // histos: Base and QA; histosBoot: bootstrap replicas; histosSystA-D: the variations, + // 4 per registry (a registry holds at most 512 objects). + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosBoot{"HistosBoot", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystA{"HistosSystA", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystB{"HistosSystB", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystC{"HistosSystC", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosSystD{"HistosSystD", {}, OutputObjHandlingPolicy::AnalysisObject}; - template - void fillNSigmaBefCut(const T& track, float cent) + HistogramRegistry& reg(int v) { - float pt = track.pt(); - auto sign = track.sign(); - - if (sign > 0) { - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_PiPlus"), cent, pt, track.tpcNSigmaPi()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_PiPlus"), cent, pt, track.tofNSigmaPi()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_PiPlus"), cent, track.tofNSigmaPi(), track.tpcNSigmaPi()); - - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_KaPlus"), cent, pt, track.tpcNSigmaKa()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_KaPlus"), cent, pt, track.tofNSigmaKa()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_KaPlus"), cent, track.tofNSigmaKa(), track.tpcNSigmaKa()); - - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_Pr"), cent, pt, track.tpcNSigmaPr()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_Pr"), cent, pt, track.tofNSigmaPr()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_Pr"), cent, track.tofNSigmaPr(), track.tpcNSigmaPr()); - } else if (sign < 0) { - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_PiMinus"), cent, pt, track.tpcNSigmaPi()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_PiMinus"), cent, pt, track.tofNSigmaPi()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_PiMinus"), cent, track.tofNSigmaPi(), track.tpcNSigmaPi()); - - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_KaMinus"), cent, pt, track.tpcNSigmaKa()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_KaMinus"), cent, pt, track.tofNSigmaKa()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_KaMinus"), cent, track.tofNSigmaKa(), track.tpcNSigmaKa()); - - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_AntiPr"), cent, pt, track.tpcNSigmaPr()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_AntiPr"), cent, pt, track.tofNSigmaPr()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_AntiPr"), cent, track.tofNSigmaPr(), track.tpcNSigmaPr()); - } - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_PiAll"), cent, pt, track.tpcNSigmaPi()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_PiAll"), cent, pt, track.tofNSigmaPi()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_PiAll"), cent, track.tofNSigmaPi(), track.tpcNSigmaPi()); - - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_KaAll"), cent, pt, track.tpcNSigmaKa()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_KaAll"), cent, pt, track.tofNSigmaKa()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_KaAll"), cent, track.tofNSigmaKa(), track.tpcNSigmaKa()); - - histos.fill(HIST("h3DnsigmaTpcVsPtBefCut_Cent_PrAll"), cent, pt, track.tpcNSigmaPr()); - histos.fill(HIST("h3DnsigmaTofVsPtBefCut_Cent_PrAll"), cent, pt, track.tofNSigmaPr()); - histos.fill(HIST("h3DnsigmaTpcVsTofBefCut_Cent_PrAll"), cent, track.tofNSigmaPr(), track.tpcNSigmaPr()); - } - - template - void fillNSigmaAftCut(const T& track, float cent, bool isSpecies[]) - { - float pt = track.pt(); - float tpcPi = track.tpcNSigmaPi(); - float tofPi = track.tofNSigmaPi(); - - float tpcKa = track.tpcNSigmaKa(); - float tofKa = track.tofNSigmaKa(); - - float tpcPr = track.tpcNSigmaPr(); - float tofPr = track.tofNSigmaPr(); - - if (isSpecies[kPiPlusIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_PiPlus"), cent, pt, tpcPi); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_PiPlus"), cent, pt, tofPi); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_PiPlus"), cent, tofPi, tpcPi); - } - if (isSpecies[kPiMinusIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_PiMinus"), cent, pt, tpcPi); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_PiMinus"), cent, pt, tofPi); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_PiMinus"), cent, tofPi, tpcPi); - } - if (isSpecies[kPiAllIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_PiAll"), cent, pt, tpcPi); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_PiAll"), cent, pt, tofPi); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_PiAll"), cent, tofPi, tpcPi); - } - if (isSpecies[kKaPlusIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_KaPlus"), cent, pt, tpcKa); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_KaPlus"), cent, pt, tofKa); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_KaPlus"), cent, tofKa, tpcKa); - } - if (isSpecies[kKaMinusIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_KaMinus"), cent, pt, tpcKa); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_KaMinus"), cent, pt, tofKa); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_KaMinus"), cent, tofKa, tpcKa); - } - if (isSpecies[kKaAllIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_KaAll"), cent, pt, tpcKa); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_KaAll"), cent, pt, tofKa); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_KaAll"), cent, tofKa, tpcKa); - } - if (isSpecies[kPrIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_Pr"), cent, pt, tpcPr); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_Pr"), cent, pt, tofPr); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_Pr"), cent, tofPr, tpcPr); - } - if (isSpecies[kAntiPrIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_AntiPr"), cent, pt, tpcPr); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_AntiPr"), cent, pt, tofPr); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_AntiPr"), cent, tofPr, tpcPr); - } - if (isSpecies[kPrAllIdx]) { - histos.fill(HIST("h3DnsigmaTpcVsPtAftCut_Cent_PrAll"), cent, pt, tpcPr); - histos.fill(HIST("h3DnsigmaTofVsPtAftCut_Cent_PrAll"), cent, pt, tofPr); - histos.fill(HIST("h3DnsigmaTpcVsTofAftCut_Cent_PrAll"), cent, tofPr, tpcPr); - } else { - return; + if (v == kSystBase) { + return histos; + } + switch ((v - 1) / KNSystPerRegistry) { + case 0: + return histosSystA; + case 1: + return histosSystB; + case 2: + return histosSystC; + default: + return histosSystD; } } - // Returns: 0 = Unknown/Reject, 1 = Pion, 2 = Kaon, 3 = Proton - template - int identifyTrack(const T& candidate, int cent) - { - if (!candidate.hasTPC()) - return 0; - - float pt = candidate.pt(); - if (pt <= cfgCutPtLower || pt >= cfgCutPtUpperPID) - return 0; // Out of bounds - - if (!pidMeanSigmaMap) - return 0; - - int centBin = cent + 0.5; - auto charge = candidate.sign(); - int piIdx = (charge > 0) ? kPiPlusIdx : kPiMinusIdx; - int kaIdx = (charge > 0) ? kKaPlusIdx : kKaMinusIdx; - int prIdx = (charge > 0) ? kPrIdx : kAntiPrIdx; - - // TPC - float mPiTpc = pidMeanSigmaMap->meanTPC[piIdx][centBin]; - float sPiTpc = pidMeanSigmaMap->sigmaTPC[piIdx][centBin]; - - float mKaTpc = pidMeanSigmaMap->meanTPC[kaIdx][centBin]; - float sKaTpc = pidMeanSigmaMap->sigmaTPC[kaIdx][centBin]; - - float mPrTpc = pidMeanSigmaMap->meanTPC[prIdx][centBin]; - float sPrTpc = pidMeanSigmaMap->sigmaTPC[prIdx][centBin]; - - // TOF - float mPiTof = pidMeanSigmaMap->meanTOF[piIdx][centBin]; - float sPiTof = pidMeanSigmaMap->sigmaTOF[piIdx][centBin]; - - float mKaTof = pidMeanSigmaMap->meanTOF[kaIdx][centBin]; - float sKaTof = pidMeanSigmaMap->sigmaTOF[kaIdx][centBin]; - - float mPrTof = pidMeanSigmaMap->meanTOF[prIdx][centBin]; - float sPrTof = pidMeanSigmaMap->sigmaTOF[prIdx][centBin]; + const AxisSpec& nchAxisOf(int v) const { return v == kSystNchBin ? nchAxisNchBin2 : nchAxisObs; } + int nchBinWidthOf(int v) const { return v == kSystNchBin ? nchWidthNchBin2 : nchWidth; } + + TRandom3 rng; // bootstrap Poisson weights + + // --- per-variation inputs ---------------------------------------------------- + struct SystInputs { + // efficiency and fake rate versus (centrality, pT, eta), and integrated over centrality (one + // centrality bin: used where the cell is empty or not a valid efficiency) + TH3* hEff = nullptr; + TH3* hFake = nullptr; + TH3* hEffPtEta = nullptr; + TH3* hFakePtEta = nullptr; + THnSparseF* hFlat = nullptr; + // mean pass, per level: <[pT]> and vs (N_ch, eta bin), vs (N_ch, forward bin) + std::array pmean{}; + std::array pmeanFwd{}; + }; + std::array inputs{}; - static int debugLogCounter = 0; - if (debugLogCounter < KConstTen) { - LOGF(info, "[PID DEBUG] CentBin: %d, Charge: %d", centBin, charge); - LOGF(info, " -> TPC USED | Pi (\u03bc=%.3f, \u03c3=%.3f) | Ka (\u03bc=%.3f, \u03c3=%.3f) | Pr (\u03bc=%.3f, \u03c3=%.3f)", - mPiTpc, sPiTpc, mKaTpc, sKaTpc, mPrTpc, sPrTpc); + // mean +- n sigma of N_ch per bin of x (centrality or N_PV), from the flattening pass + struct NSigmaBand { + std::vector> meanRms; // per bin of x; rms < 0 for an empty bin + float xMin = 0.f, xMax = 0.f; + bool applied = false; + }; + enum EPileupBand { + kBandCent = 0, + kBandPV, + kNBands + }; + std::array pileupBands{}; - if (candidate.hasTOF()) { - LOGF(info, " -> TOF USED | Pi (\u03bc=%.3f, \u03c3=%.3f) | Ka (\u03bc=%.3f, \u03c3=%.3f) | Pr (\u03bc=%.3f, \u03c3=%.3f)", - mPiTof, sPiTof, mKaTof, sKaTof, mPrTof, sPrTof); - } else { - LOGF(info, " -> TOF USED | Track has no TOF signal."); + // --- per-variation histograms, filled through pointers (HIST() needs names known at compile time) --- + struct EffHists { + std::array, kNEffCat> maps{}; // (centrality, pT, eta), one per EEffCat + }; + struct FlatHists { + std::shared_ptr maps; // (vz, sign, pT, eta, phi, EFlatType) + }; + struct MeanHists { + std::shared_ptr mean; // (N_ch, eta bin, EMeanObs) + std::shared_ptr fwdMean; // (N_ch, forward bin): + }; + struct FlucHists { + std::shared_ptr obs; // (N_ch, eta bin, EFlucObs) + std::shared_ptr c2Sub2D; // (N_ch, eta_A, eta_C) + std::shared_ptr c3Sub2D; // (N_ch, eta_A pair, eta_C single) + std::shared_ptr gapSum2D; // (N_ch, Delta eta, Sigma eta) of the C2 map + std::shared_ptr c3GapSum2D; // (N_ch, eta_pair - eta_single, eta_pair + eta_single) + std::shared_ptr fwdVar; // (N_ch, forward bin): + std::shared_ptr covFwdPt; // (N_ch, eta bin, forward bin): + std::shared_ptr covFwdN; // (N_ch, eta bin, forward bin): + }; + struct { + std::shared_ptr evtCount; // (EEvtCut, variation) + std::shared_ptr trkCount; // (ETrkCut, variation) + std::array, kNSyst> evtCount1D{}; // EEvtCut, one per variation + std::array, kNSyst> trkCount1D{}; // ETrkCut, one per variation + std::shared_ptr nchOccupancy; // (N_ch, occupancy) of the Base events + std::array, kNQAStage>, kNQAEvt1D> qaEvt1D{}; + std::array, kNQAStage>, kNQAEvt2D> qaEvt2D{}; + std::array, kNQAStage>, kNQATrk1D> qaTrk1D{}; + std::array, kNQAStage>, kNQATrk2D> qaTrk2D{}; + std::array, kNSyst> nchCent{}; // (centrality, N_ch) per variation + std::array eff{}; + std::array flat{}; + std::array, kNSyst> mean{}; + std::array, kNSyst> fluc{}; + std::array boot{}; + std::shared_ptr fwdChannel; // (channel: FT0 0-207, FV0 208-255; EChanQA) + std::shared_ptr fwdEta; // amplitude-weighted eta per forward bin + std::shared_ptr mcTrackOrigin; // (N_ch, pT, ETrackOrigin) of the Base tracks + std::shared_ptr mcVtx; // (z_reco - z_true, EVtxQA) + } hists; + + // --- forward detector calibration (per run) ---------------------------------- + struct { + o2::ft0::Geometry ft0Det; + o2::fv0::Geometry* fv0Det = nullptr; + int run = -1; + std::array, KNChFT0> posFT0{}; + std::array, KNChFV0> posFV0{}; + std::array gainFT0{}; + std::array gainFV0{}; + } fwdCalib; + + // --- per-event work arrays --------------------------------------------------- + // Power sums S_{m,k} = sum w^k pT^m per eta bin. + struct PowerSums { + std::array, KIntM>, KNEtaMax> pmwk{}; + std::array, KNEtaMax> wk{}; + void reset() + { + for (auto& a : pmwk) { + for (auto& b : a) { + b.fill(0.); + } + } + for (auto& a : wk) { + a.fill(0.); } - debugLogCounter++; } - // Fetch Raw nSigma Values - float rawTpcPi = candidate.tpcNSigmaPi(); - float rawTpcKa = candidate.tpcNSigmaKa(); - float rawTpcPr = candidate.tpcNSigmaPr(); - - float rawTofPi = 0.f, rawTofKa = 0.f, rawTofPr = 0.f; - if (candidate.hasTOF()) { - rawTofPi = candidate.tofNSigmaPi(); - rawTofKa = candidate.tofNSigmaKa(); - rawTofPr = candidate.tofNSigmaPr(); + void add(int ieta, double pt, double w) + { + double wPow = 1.; + for (int k = 0; k < KIntK; ++k) { + double ptPow = 1.; + for (int m = 0; m < KIntM; ++m) { + pmwk[ieta][m][k] += wPow * ptPow; + ptPow *= pt; + } + wk[ieta][k] += wPow; + wPow *= w; + } } + }; + std::array, kNSyst> sums{}; + // track cut flow of the current event, added to hTrkCount once per event + std::array, kNSyst> trkCounts{}; - // --- Low PT Regime --- - if (pt <= cfgCutPtUpperTPC) { - // Basic TPC passing check: |Raw - Mean| < (Cut * Sigma) - bool inTpcPi = std::abs(rawTpcPi - mPiTpc) < (cfgnSigmaCutTPC * sPiTpc); - bool inTpcKa = std::abs(rawTpcKa - mKaTpc) < (cfgnSigmaCutTPC * sKaTpc); - bool inTpcPr = std::abs(rawTpcPr - mPrTpc) < (cfgnSigmaCutTPC * sPrTpc); - - // Combined passing check (adds TOF if available) - bool passPi = inTpcPi && (!candidate.hasTOF() || std::abs(rawTofPi - mPiTof) < (cfgnSigmaCutTOF * sPiTof)); - bool passKa = inTpcKa && (!candidate.hasTOF() || std::abs(rawTofKa - mKaTof) < (cfgnSigmaCutTOF * sKaTof)); - bool passPr = inTpcPr && (!candidate.hasTOF() || std::abs(rawTofPr - mPrTof) < (cfgnSigmaCutTOF * sPrTof)); - - // Uniqueness check: Must pass target cut, and NOT fall into the TPC range of the others - if (passPi && !passKa && !passPr) - return 1; - if (passKa && !passPi && !passPr) - return 2; - if (passPr && !passPi && !passKa) - return 3; - - return 0; // Ambiguous or failed all cuts - } + struct FwdSignal { + std::array ampl{}; // amplitude (data, MC reco) or number of particles (MC truth) + std::array valid{}; // detector read out in this event + }; - // --- High PT Regime--- - if (candidate.hasTOF() && pt > cfgCutPtUpperTPC) { - // Calculate 2D Normalized Distance (Elliptical distance normalized by sigma) - float dPi = std::hypot((rawTpcPi - mPiTpc) / sPiTpc, (rawTofPi - mPiTof) / sPiTof); - float dKa = std::hypot((rawTpcKa - mKaTpc) / sKaTpc, (rawTofKa - mKaTof) / sKaTof); - float dPr = std::hypot((rawTpcPr - mPrTpc) / sPrTpc, (rawTofPr - mPrTof) / sPrTof); + struct EventSelection { + uint32_t mask = 0; + std::array cent{}; + int occupancy = -1; + }; - // Count how many particles are within the ambiguity radius - int competitors = (dPi < cfgnSigmaOtherParticles) + - (dKa < cfgnSigmaOtherParticles) + - (dPr < cfgnSigmaOtherParticles); + // Per-event observables of one (variation, level). + struct EventObs { + float nCh = 0.f; + std::array nW{}, mean{}, c2{}, c3{}, p1kBar{}, dN{}; + std::array fwd{}, dFwd{}; + std::array fwdValid{}; + }; - // If 1 or fewer are in the ambiguity region, pick the absolute best match - if (competitors <= 1) { - if (dPi <= dKa && dPi <= dPr && dPi < cfgnSigmaCutCombTPCTOF) - return 1; - if (dKa <= dPi && dKa <= dPr && dKa < cfgnSigmaCutCombTPCTOF) - return 2; - if (dPr <= dPi && dPr <= dKa && dPr < cfgnSigmaCutCombTPCTOF) - return 3; - } + // =========================================================================== + // Selection helpers + // =========================================================================== + template + static bool passItsLayerCut(int cut, const T& col) + { + switch (cut) { + case kITSLayer3: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayer3); + case kITSLayer0123: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123); + case kITSLayersAll: + return col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); + default: + return true; } - return 0; // Unknown/Reject } - template - bool isEventSelected(const T& col) + // N_ch within mean +- nSigma RMS at x (events outside the x range of the band are rejected, + // empty bins of the band accept every event). + static bool insideBand(const NSigmaBand& band, float x, float nCh, float nSigma) { - histos.fill(HIST("hEvtCount"), 0.5); - - if (!col.sel8()) - return false; - histos.fill(HIST("hEvtCount"), 1.5); - - if (std::abs(col.posZ()) > cfgCutVertex) - return false; - histos.fill(HIST("hEvtCount"), 2.5); - - if (cfgEvSelNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return false; - histos.fill(HIST("hEvtCount"), 3.5); - - if (cfgUseGoodITSLayerAllCut && !col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) - return false; - histos.fill(HIST("hEvtCount"), 4.5); - - if (cfgIsGoodZvtxFT0VsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + const auto& mr = band.meanRms; + int bin = 1 + static_cast((x - band.xMin) / (band.xMax - band.xMin) * (mr.size() - 2)); + if (bin < 1 || bin >= static_cast(mr.size() - 1)) { return false; - histos.fill(HIST("hEvtCount"), 5.5); - return true; + } + if (mr[bin].second < 0.f) { + return true; + } + return std::abs(nCh - mr[bin].first) <= nSigma * mr[bin].second; } - bool isPassAddPileup(int multPV, int trksize, float cent) + // Linear cut and n-sigma bands of width nSigma. + bool isPassAddPileup(float multPV, int trksize, float cent, float nSigma) { - auto checkLimits = [](float x, float y, const std::vector>& limits, float xM, float xMx) { - if (limits.empty()) - return true; - int bin = 1 + static_cast((x - xM) / (xMx - xM) * (limits.size() - 2)); - if (bin < 1 || bin >= static_cast(limits.size() - 1)) - return false; - return (y >= limits[bin].first && y <= limits[bin].second); - }; - if (cfgApplySigPupCut) { - if (!checkLimits(cent, trksize, state.mLimitsNchCent, state.mMinXNchCent, state.mMaxXNchCent)) - return false; - histos.fill(HIST("hEvtCount"), 6.5); + if (pileupBands[kBandCent].applied && !insideBand(pileupBands[kBandCent], cent, trksize, nSigma)) { + return false; } - - if (cfgApplyLinPupCut) { - if (trksize > (cfgLinPupParam0 + cfgLinPupParam1 * multPV)) + if (pileupBands[kBandPV].applied && !insideBand(pileupBands[kBandPV], multPV, trksize, nSigma)) { + return false; + } + if (cfgPileup.cfgApplyLinPupCut) { + if (trksize > (cfgPileup.cfgLinPupParam0 + cfgPileup.cfgLinPupParam1 * multPV)) { return false; - histos.fill(HIST("hEvtCount"), 7.5); - if (trksize < (cfgLinPupParam2 + cfgLinPupParam3 * multPV)) + } + if (trksize < (cfgPileup.cfgLinPupParam2 + cfgPileup.cfgLinPupParam3 * multPV)) { return false; - histos.fill(HIST("hEvtCount"), 8.5); + } } return true; } - template - bool isTrackSelected(const T& trk) + // One event at cut-flow stage s of variation v: hEvtCountSyst and that variation's hEvtCount. + void countEvent(int s, int v) { - histos.fill(HIST("hTrkCount"), 0.5); - - if (trk.sign() == 0) - return false; - histos.fill(HIST("hTrkCount"), 1.5); - - if (!trk.has_collision()) - return false; - histos.fill(HIST("hTrkCount"), 2.5); - - if (!trk.isPVContributor()) - return false; - histos.fill(HIST("hTrkCount"), 3.5); - - if (!(trk.itsNCls() > cfgITScluster)) - return false; - histos.fill(HIST("hTrkCount"), 4.5); + hists.evtCount->Fill(s, v); + if (hists.evtCount1D[v]) { + hists.evtCount1D[v]->Fill(s); + } + } - if (!(trk.tpcNClsFound() >= cfgTPCcluster)) - return false; - histos.fill(HIST("hTrkCount"), 5.5); + // Event selection of all active variations: returns the variations the event passes and its + // centrality for each of them. The cuts common to all variations come first. The event cut + // flow of every active variation is filled unless count is false (cut QA). + template + EventSelection isEventSelected(const T& col, int nCh, uint32_t active, bool count = true) + { + EventSelection ev; + auto countAll = [&](int s) { + for (int v = 0; v < kNSyst && count; ++v) { + if (TESTBIT(active, v)) { + countEvent(s, v); + } + } + }; + countAll(kEvAll); + if (!col.sel8()) { + return ev; + } + countAll(kEvSel8); + if (std::abs(col.posZ()) > cfgEvSel.cfgVtxZCut) { + return ev; + } + countAll(kEvVtxZ); + if (cfgEvSel.cfgIsGoodZvtxFT0VsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return ev; + } + countAll(kEvGoodZvtx); + if (cfgEvSel.cfgEvSelNoTimeFrameBorder && !col.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return ev; + } + countAll(kEvTFBorder); + if (cfgEvSel.cfgEvSelNoITSROFrameBorder && !col.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return ev; + } + countAll(kEvITSROFBorder); + if (cfgEvSel.cfgEvSelIsVertexITSTPC && !col.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return ev; + } + countAll(kEvVertexITSTPC); - if (!(trk.tpcNClsCrossedRows() >= cfgTPCnCrossedRows)) - return false; - histos.fill(HIST("hTrkCount"), 6.5); + const float centBase = getCentrality(col, cfgEvSel.cfgCentralityChoice); + const float multPV = col.multNTracksPV(); + ev.occupancy = col.trackOccupancyInTimeRange(); + const bool passAddPileup = isPassAddPileup(multPV, nCh, centBase, pupNSigma); + const bool passAddPileupTight = isPassAddPileup(multPV, nCh, centBase, pupNSigma - KSystPupnSigStep); - if (trk.pt() < cfgCutPtLower || trk.pt() > cfgCutPtUpper || std::abs(trk.eta()) > cfgCutEta) - return false; - histos.fill(HIST("hTrkCount"), 7.5); + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(active, v)) { + continue; + } + auto countOne = [&](int s) { + if (count) { + countEvent(s, v); + } + }; + const float cent = (v == kSystCent) ? getCentrality(col, KSystCentralityChoice) : centBase; + ev.cent[v] = cent; + if (cfgEvSel.cfgEvSelNoSameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } + countOne(kEvSameBunch); + if (cfgEvSel.cfgEvSelNoCollInTimeRangeStd && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + continue; + } + countOne(kEvTimeRange); + if (!passItsLayerCut(v == kSystITSLayer ? itsLayerVar : itsLayerNom, col)) { + continue; + } + countOne(kEvITSLayer); + if (!(v == kSystPileupNSigma ? passAddPileupTight : passAddPileup)) { + continue; + } + countOne(kEvAddPileup); + // the cut of the event-selection variations + if (v == kSystPileup && !col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + continue; + } + if (v == kSystVz && !(std::abs(col.posZ()) < KSystVtxZCut)) { + continue; + } + if (v == kSystOccupancy && (ev.occupancy < 0 || ev.occupancy >= occupancyMax)) { + continue; + } + countOne(kEvVariationCut); + if (!(cent < cfgEvSel.cfgCentMax)) { + continue; + } + countOne(kEvCent); + SETBIT(ev.mask, v); + if (col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + countOne(kEvInfoPrevRof); + } + if (ev.occupancy < 0) { + countOne(kEvInfoOccUnknown); + } + } + return ev; + } - if (!trk.isGlobalTrack()) - return false; - histos.fill(HIST("hTrkCount"), 8.5); + // Track selection of all variations the event passed (evMask): returns the variations the + // track passes. The cuts common to all variations come first. The cut flow is collected in + // trkCounts (unless count is false: cut QA) and added to the track counters by flushTrackCounts(). + template + uint32_t isTrackSelected(const T& trk, uint32_t evMask, bool count = true) + { + const double inc = count ? 1. : 0.; + auto countAll = [&](int s) { + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(evMask, v)) { + trkCounts[v][s] += inc; + } + } + }; + countAll(kTrkAll); + if (trk.sign() == 0) { + return 0; + } + countAll(kTrkSign); + if (!trk.has_collision()) { + return 0; + } + countAll(kTrkHasColl); + if (!trk.isPVContributor()) { + return 0; + } + countAll(kTrkPVContrib); + if (trk.pt() < cfgCutPtLower || trk.pt() > cfgCutPtUpper || std::abs(trk.eta()) > cfgCutEta) { + return 0; + } + countAll(kTrkPtEta); + if (!trk.isGlobalTrack() || trk.tpcNClsCrossedRows() < cfgTPCnCrossedRows) { + return 0; + } + countAll(kTrkGlobal); - if (cfgPtDepDCAxy) { - // Evaluates: P0 + P1 / (pt^P2) - float maxDcaXY = cfgDcaXyP0 + cfgDcaXyP1 / std::pow(trk.pt(), cfgDcaXyP2); - if (std::abs(trk.dcaXY()) > maxDcaXY) { - return false; + const float maxDcaXY = KSystDcaXyScale * (cfgSyst.cfgDcaXyP0 + cfgSyst.cfgDcaXyP1 / std::pow(trk.pt(), cfgSyst.cfgDcaXyP2.value)); + uint32_t mask = 0; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(evMask, v)) { + continue; } - histos.fill(HIST("hTrkCount"), 9.5); - } else { - if (std::abs(trk.dcaXY()) > cfgCutTracKDcaMaxXY) { - return false; + if (!(trk.itsNCls() >= (v == kSystNITS ? KSystITScluster : cfgITScluster.value))) { + continue; } - histos.fill(HIST("hTrkCount"), 9.5); - } - if (cfgPtDepDCAz) { - // Evaluates: P0 + P1 / (pt^P2) - float maxDcaZ = cfgDcaZP0 + cfgDcaZP1 / std::pow(trk.pt(), cfgDcaZP2); - if (std::abs(trk.dcaZ()) > maxDcaZ) { - return false; // Reject track if DCA is too large + trkCounts[v][kTrkITS] += inc; + if (!(trk.tpcNClsFound() >= (v == kSystNTPC ? KSystTPCcluster : cfgTPCcluster.value))) { + continue; } - histos.fill(HIST("hTrkCount"), 10.5); - } else { - if (std::abs(trk.dcaZ()) > cfgCutTracKDcaMaxZ) { - return false; + trkCounts[v][kTrkTPC] += inc; + if (!(trk.tpcChi2NCl() < (v == kSystChi2TPC ? KSystChi2TPC : cfgCutTpcChi2NCl.value))) { + continue; + } + trkCounts[v][kTrkTPCChi2] += inc; + if (!(std::abs(trk.dcaZ()) < (v == kSystDCAz ? KSystDcaMaxZ : cfgCutTracKDcaMaxZ.value))) { + continue; + } + trkCounts[v][kTrkDCAz] += inc; + // the cut of the DCAxy variation + if (v == kSystDCAxy && !(std::abs(trk.dcaXY()) < maxDcaXY)) { + continue; + } + trkCounts[v][kTrkVariationCut] += inc; + SETBIT(mask, v); + } + return mask; + } + + // Adds the track cut flow of the event to hTrkCountSyst and hTrkCount. + void flushTrackCounts() + { + for (int v = 0; v < kNSyst; ++v) { + for (int s = 0; s < kNTrkCut; ++s) { + if (trkCounts[v][s] > 0.) { + hists.trkCount->Fill(s, v, trkCounts[v][s]); + if (hists.trkCount1D[v]) { + hists.trkCount1D[v]->Fill(s, trkCounts[v][s]); + } + trkCounts[v][s] = 0.; + } } - histos.fill(HIST("hTrkCount"), 10.5); } - return true; } + // Charged primary in the observable acceptance (event vertex cuts are applied to the reco collision). template bool isParticleSelected(const T& particle) { auto* pd = pdg->GetParticle(particle.pdgCode()); - if (!pd) - return false; - if (std::abs(pd->Charge()) == 0) + if (!pd) { return false; - if (particle.pt() < cfgCutPtLower || particle.pt() > cfgCutPtUpper || std::abs(particle.eta()) > cfgCutEta) + } + if (std::abs(pd->Charge()) == 0) { return false; - if (std::abs(particle.vz()) > cfgCutVertex) + } + if (particle.pt() < cfgCutPtLower || particle.pt() > cfgCutPtUpper || std::abs(particle.eta()) > cfgCutEta) { return false; + } return true; } - float getCentrality(const auto& col) const + // Origin of a reco MC track (ETrackOrigin) with respect to the MC collision of coll. + template + int trackOrigin(const T& track, const C& coll) const + { + if (!track.has_mcParticle()) { + return kOriginNoLabel; + } + const auto& particle = track.mcParticle(); + if (particle.mcCollisionId() != coll.mcCollisionId()) { + return kOriginOtherColl; + } + return particle.isPhysicalPrimary() ? kOriginPrimary : kOriginSecondary; + } + + float getCentrality(const auto& col, int choice) const { - if (cfgCentralityChoice.value == kCentFT0C) + if (choice == kCentFT0C) { return col.centFT0C(); - if (cfgCentralityChoice.value == kCentFT0M) + } + if (choice == kCentFT0M) { return col.centFT0M(); - if (cfgCentralityChoice.value == kCentFDDM) + } + if (choice == kCentFDDM) { return col.centFDDM(); - if (cfgCentralityChoice.value == kCentFV0A) + } + if (choice == kCentFV0A) { return col.centFV0A(); + } return KinvalidCentrality; } - float getEfficiency(float mult, float pt, float eta, PIDIdx pidType, int effidx, bool cfgEff) const + // Observable eta bins containing eta: the full-range bin 0 and one narrow bin. + int etaBinsOf(float eta, std::array& bins) const { - if (!cfgEff) { - return (effidx == 0) ? 1.0f : 0.0f; + int n = 0; + for (int ieta = 0; ieta < nEta && n < 2; ++ieta) { + if (eta > etaLw[ieta] && eta <= etaUp[ieta]) { + bins[n++] = ieta; + } } - TH3F* h = (effidx == 0) ? state.hEff[pidType] : state.hFake[pidType]; - - if (!h) - return -1; + return n; + } - int ibx = h->GetXaxis()->FindBin(mult); - int iby = h->GetYaxis()->FindBin(pt); - int ibz = h->GetZaxis()->FindBin(eta); - float val = h->GetBinContent(ibx, iby, ibz); + // =========================================================================== + // Corrections + // =========================================================================== + // Inclusive efficiency/fake lookup (no species dependence). + static float getEfficiency(const TH3* h, float cent, float pt, float eta, bool isEff) + { + const float invalid = std::numeric_limits::quiet_NaN(); + if (!h || !std::isfinite(cent) || !std::isfinite(pt) || !std::isfinite(eta)) { + return invalid; + } + int ibx = h->GetXaxis()->FindFixBin(cent); + // Reject centrality underflow, clamp overflow. + if (ibx < 1) { + return invalid; + } + ibx = std::min(ibx, h->GetNbinsX()); + int iby = h->GetYaxis()->FindFixBin(pt); + int ibz = h->GetZaxis()->FindFixBin(eta); + // Do not extrapolate in pT or eta. + if (iby < 1 || iby > h->GetNbinsY() || ibz < 1 || ibz > h->GetNbinsZ()) { + return invalid; + } + const double val = h->GetBinContent(ibx, iby, ibz); + const double err = h->GetBinError(ibx, iby, ibz); + // A bin without a positive error is not a valid measurement, except a fake rate of 0 + // (no fake track in the bin), which has no error. + if (!std::isfinite(val) || !std::isfinite(err) || (err <= 0.0 && (isEff || val != 0.0))) { + return invalid; + } + // Physical-domain checks. + if (val < 0.0 || val > 1.0 || (isEff && val == 0.0)) { + return invalid; + } + return static_cast(val); + } - if (effidx == 0) - return (val > 0.f) ? val : 1.0f; - return val; + // Nominal efficiency and fake rate of the map of variation v (no variation factor). + bool getValidEffFake(int v, float cent, float pt, float eta, float& eff, float& fake) + { + if (!cfgEff) { + eff = 1.f; + fake = 0.f; + return true; + } + const auto& in = inputs[v]; + auto valid = [&]() { return std::isfinite(eff) && std::isfinite(fake) && eff > KFloatEpsilon && eff <= 1.f && fake >= 0.f && fake < 1.f; }; + eff = getEfficiency(in.hEff, cent, pt, eta, true); + fake = getEfficiency(in.hFake, cent, pt, eta, false); + if (valid()) { + return true; + } + // empty or statistically invalid cell, or no valid centrality: the map integrated over centrality + const float centAll = in.hEffPtEta ? in.hEffPtEta->GetXaxis()->GetBinCenter(1) : 0.f; + eff = getEfficiency(in.hEffPtEta, centAll, pt, eta, true); + fake = getEfficiency(in.hFakePtEta, centAll, pt, eta, false); + return valid(); } - float getFlatteningWeight(float vz, float chg, float pt, float eta, float phi, PIDIdx pidType, bool cfgflat) const + float efficiencyVariationFactor(int v, float pt) { - if (!cfgflat) - return 1.0; - THnSparseF* h = state.hFlatWeight[pidType]; + if (v == kSystEff) { + return 1.f + 0.01f * KEffVarPercent; + } + if (v == kSystEffPtDep) { + // Linear in pT: full variation at cfgPtMin, none at KEffVarPtZero. + const float shape = std::clamp((KEffVarPtZero - pt) / (KEffVarPtZero - cfgPtMin), 0.f, 1.f); + return 1.f + 0.01f * KEffVarPercent * shape; + } + return 1.f; + } - if (!h) + static float getFlatteningWeight(const THnSparseF* h, float vz, float chg, float pt, float eta, float phi) + { + if (!h) { return 0.0; - int bins[5]; + } + std::array bins{}; bins[0] = h->GetAxis(0)->FindBin(vz); bins[1] = h->GetAxis(1)->FindBin(chg); bins[2] = h->GetAxis(2)->FindBin(pt); bins[3] = h->GetAxis(3)->FindBin(eta); bins[4] = h->GetAxis(4)->FindBin(phi); - float val = h->GetBinContent(bins); - - return val; + return h->GetBinContent(bins.data()); } - std::vector* offsetFT0 = nullptr; - uint64_t mLastTimestamp = 0; - double getEtaFT0(uint64_t globalChno, int i) + // Weight w = w_flat (1 - fake) / eff of the track for every variation in mask; a map shared + // by several variations is looked up once. Returns the variations with a usable weight and + // the Base efficiency, fake rate and flattening weight for the QA profiles. + uint32_t getTrackWeights(uint32_t mask, float cent, float vz, float sign, float pt, float eta, float phi, bool useFlat, + std::array& weights, float& effBase, float& fakeBase, float& flatBase) { - if (i > 1 || i < 0) { - LOGF(fatal, "kFIT Index %d out of range", i); - } - auto chPos = ft0Det.getChannelCenter(globalChno); - auto x = chPos.X() + (*offsetFT0)[i].getX(); - auto y = chPos.Y() + (*offsetFT0)[i].getY(); - auto z = chPos.Z() + (*offsetFT0)[i].getZ(); - // i == 0 is FT0A (A-side, positive Z) - // i == 1 is FT0C (C-side, negative Z) - if (i == 1) { - z = -std::abs(z); - } else if (i == 0) { - z = std::abs(z); + std::array effDone{}; + std::array effS{}, fakeS{}, flatS{}; + std::array flatDone{}; + uint32_t weightMask = 0; + effBase = std::numeric_limits::quiet_NaN(); + fakeBase = std::numeric_limits::quiet_NaN(); + flatBase = 1.f; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + const int es = effSource(v); + if (effDone[es] == 0) { + effDone[es] = getValidEffFake(es, cent, pt, eta, effS[es], fakeS[es]) ? 1 : -1; + } + if (effDone[es] < 0) { + continue; + } + const float eff = effS[es] * efficiencyVariationFactor(v, pt); + if (!(eff > KFloatEpsilon)) { + continue; + } + float flat = 1.f; + const int fs = flatSource(v); + if (useFlat && cfgFlat && fs >= 0) { + if (!flatDone[fs]) { + flatS[fs] = getFlatteningWeight(inputs[fs].hFlat, vz, sign, pt, eta, phi); + flatDone[fs] = true; + } + flat = flatS[fs]; + } + const float weight = flat * (1.0f - fakeS[es]) / eff; + if (!std::isfinite(weight) || weight <= 0.f) { + continue; + } + weights[v] = weight; + SETBIT(weightMask, v); + trkCounts[v][kTrkWeight] += 1.; + if (v == kSystBase) { + effBase = eff; + fakeBase = fakeS[es]; + flatBase = flat; + } } - auto r = std::sqrt(x * x + y * y); - auto theta = std::atan2(r, z); - return -std::log(std::tan(0.5 * theta)); + return weightMask; } - void loadAlignParam(uint64_t timestamp) + // Per-event two- and three-particle pT correlators (standard method) from the + // power sums, following arXiv:2112.03397, Eqs. 2-3. + struct C2C3Result { + double c2 = std::numeric_limits::quiet_NaN(); + double c3 = std::numeric_limits::quiet_NaN(); + }; + + template + C2C3Result calculateC2C3FromSums(const std::array, M>& sumpmwk, const std::array& sumwk, float referenceMeanPt) const { - if (timestamp == mLastTimestamp && offsetFT0 != nullptr) { - return; - } - offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); - if (!offsetFT0) { - LOGF(fatal, "Failed to load valid FT0 alignment from CCDB!"); - return; + C2C3Result r; + const double s1 = sumwk[1]; + if (s1 <= 0.) { + return r; + } + const double refpt = referenceMeanPt; + const double p11 = sumpmwk[1][1] / s1; + const double p1Bar1 = p11 - refpt; + + // --- c2: needs >=2 particles (D2 = 1 - tau1 > eps) --- + const double tau1 = sumwk[2] / (s1 * s1); + const double denom2 = 1. - tau1; + if (denom2 > KFloatEpsilon) { + const double p12 = sumpmwk[1][2] / sumwk[2]; + const double p22 = sumpmwk[2][2] / sumwk[2]; + const double p2Bar2 = p22 - 2.0 * p12 * refpt + refpt * refpt; + r.c2 = (p1Bar1 * p1Bar1 - tau1 * p2Bar2) / denom2; + } + + // --- c3: needs >=3 particles (D3 = 1 - 3 tau1 + 2 tau2 > eps) --- + const double tau2 = sumwk[3] / (s1 * s1 * s1); + const double denom3 = 1. - 3.0 * tau1 + 2.0 * tau2; + if (denom3 > KFloatEpsilon) { + const double p12 = sumpmwk[1][2] / sumwk[2]; + const double p22 = sumpmwk[2][2] / sumwk[2]; + const double p13 = sumpmwk[1][3] / sumwk[3]; + const double p23 = sumpmwk[2][3] / sumwk[3]; + const double p33 = sumpmwk[3][3] / sumwk[3]; + const double p2Bar2 = p22 - 2.0 * p12 * refpt + refpt * refpt; + const double p3Bar3 = p33 - 3.0 * p23 * refpt + 3.0 * p13 * refpt * refpt - refpt * refpt * refpt; + r.c3 = (p1Bar1 * p1Bar1 * p1Bar1 - 3.0 * tau1 * p2Bar2 * p1Bar1 + 2.0 * tau2 * p3Bar3) / denom3; + } + return r; + } + + // =========================================================================== + // Forward detectors + // =========================================================================== + // Eta sub-bin of detector det containing eta, -1 outside its edges. + int fwdSubBin(int det, float eta) const + { + const auto& e = fwdEdges[det]; + if (e.size() < 2 || eta < e.front() || eta >= e.back()) { + return -1; + } + const int i = static_cast(std::upper_bound(e.begin(), e.end(), eta) - e.begin()) - 1; + return fwdOffset[det] + i; + } + + void addFwd(int det, float eta, float ampl, FwdSignal& fwd) + { + fwd.ampl[det] += ampl; + const int sub = fwdSubBin(det, eta); + if (sub >= 0) { + fwd.ampl[sub] += ampl; + } + if (hists.fwdEta) { + hists.fwdEta->Fill(det, eta, ampl); + if (sub >= 0) { + hists.fwdEta->Fill(sub, eta, ampl); + } + } + } + + void markFwdValid(int det, FwdSignal& fwd) const + { + fwd.valid[det] = true; + for (int i = 0; i + 1 < static_cast(fwdEdges[det].size()); ++i) { + fwd.valid[fwdOffset[det] + i] = true; + } + } + + float channelEta(const std::array& p, float vz) + { + const float r = std::hypot(p[0], p[1]); + const float dz = p[2] - (cfgFwdOpt.cfgFwdVzCorr ? vz : 0.f); + return r > 0.f ? std::asinh(dz / r) : std::numeric_limits::quiet_NaN(); + } + + void fillChannelQA(int ch, float raw, float corr, float eta) + { + if (hists.fwdChannel) { + hists.fwdChannel->Fill(ch, kChAmpRaw, raw); + hists.fwdChannel->Fill(ch, kChAmpCorr, corr); + hists.fwdChannel->Fill(ch, kChEta, eta); } - mLastTimestamp = timestamp; - LOGF(info, "Successfully loaded new alignment parameters for timestamp %llu", timestamp); - LOGF(info, "Offset for FT0A: x = %.3f y = %.3f z = %.3f\n", (*offsetFT0)[0].getX(), (*offsetFT0)[0].getY(), (*offsetFT0)[0].getZ()); - LOGF(info, "Offset for FT0C: x = %.3f y = %.3f z = %.3f\n", (*offsetFT0)[1].getX(), (*offsetFT0)[1].getY(), (*offsetFT0)[1].getZ()); } - template - std::pair calculateMeanAndC2FromSums(const double sumpmwk[KIntM][KIntK], const double sumwk[KIntK], float referenceMeanPt) const + // Channel positions with the FT0/FV0 alignment (as in PWGCF/flowDecorrelation) and the + // gain equalisation, loaded at each new run. + template + void loadFwdCalib(const B& bc, bool applyGain) { - if (sumwk[1] == 0.) { - return {0.f, 0.f}; + if (bc.runNumber() == fwdCalib.run) { + return; + } + fwdCalib.run = bc.runNumber(); + const uint64_t ts = bc.timestamp(); + + auto* alFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", ts); + auto* alFV0 = ccdb->getForTimeStamp>("FV0/Calib/Align", ts); + if (!alFT0 || alFT0->size() < 2 || !alFV0 || alFV0->size() < 2) { + LOGF(fatal, "Could not load FT0/FV0 alignment (FT0/Calib/Align, FV0/Calib/Align) for timestamp %llu", ts); + return; + } + + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + fwdCalib.ft0Det.calculateChannelCenter(); + for (int ch = 0; ch < KNChFT0; ++ch) { + const int side = ch < KNChFT0A ? 0 : 1; + const auto c = fwdCalib.ft0Det.getChannelCenter(ch); + float z = c.Z() + (*alFT0)[side].getZ(); + if (ch >= KNChFT0A) { + z = -z; + } + fwdCalib.posFT0[ch] = {static_cast(c.X() + (*alFT0)[side].getX()), static_cast(c.Y() + (*alFT0)[side].getY()), z}; + } + // offsetFV0[0]: left half, offsetFV0[1]: right half + static constexpr std::array KCellsInLeft = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; + for (int ch = 0; ch < KNChFV0; ++ch) { + const bool left = std::find(KCellsInLeft.begin(), KCellsInLeft.end(), ch) != KCellsInLeft.end(); + const auto& off = (*alFV0)[left ? 0 : 1]; + const auto c = fwdCalib.fv0Det->getReadoutCenter(ch); + fwdCalib.posFV0[ch] = {static_cast(c.x + off.getX()), static_cast(c.y + off.getY()), static_cast(c.z + off.getZ())}; + } + + fwdCalib.gainFT0.fill(1.f); + fwdCalib.gainFV0.fill(1.f); + if (applyGain && cfgFwdOpt.cfgFwdGainEq) { + auto* gFT0 = ccdb->getForTimeStamp>(cfgFwdOpt.cfgGainEqPath.value + "/FT0", ts); + auto* gFV0 = ccdb->getForTimeStamp>(cfgFwdOpt.cfgGainEqPath.value + "/FV0", ts); + if (gFT0 && gFT0->size() >= static_cast(KNChFT0)) { + std::copy_n(gFT0->begin(), KNChFT0, fwdCalib.gainFT0.begin()); + } else { + LOGF(warning, "No FT0 gain equalisation at %s/FT0 for run %d: unit gains", cfgFwdOpt.cfgGainEqPath.value.c_str(), fwdCalib.run); + } + if (gFV0 && gFV0->size() >= static_cast(KNChFV0)) { + std::copy_n(gFV0->begin(), KNChFV0, fwdCalib.gainFV0.begin()); + } else { + LOGF(warning, "No FV0 gain equalisation at %s/FV0 for run %d: unit gains", cfgFwdOpt.cfgGainEqPath.value.c_str(), fwdCalib.run); + } } + LOGF(info, "Forward-detector calibration loaded for run %d", fwdCalib.run); + } - double tau1 = sumwk[2] / (sumwk[1] * sumwk[1]); - double denom2 = 1. - tau1; + // Gain-equalised amplitudes of FT0A, FT0C and FV0A, totals and eta sub-bins. + template + void computeFwdReco(const C& col, float vz, FwdSignal& fwd, bool qa) + { + fwd = {}; + auto corr = [](float a, float g) { return g > 0.f ? a / g : a; }; + if (col.has_foundFT0()) { + const auto& ft0 = col.foundFT0(); + markFwdValid(kFT0A, fwd); + markFwdValid(kFT0C, fwd); + for (std::size_t i = 0; i < ft0.channelA().size(); ++i) { + const int id = ft0.channelA()[i]; + if (id < 0 || id >= KNChFT0A) { + continue; + } + const float a = corr(ft0.amplitudeA()[i], fwdCalib.gainFT0[id]); + const float eta = channelEta(fwdCalib.posFT0[id], vz); + addFwd(kFT0A, eta, a, fwd); + if (qa) { + fillChannelQA(id, ft0.amplitudeA()[i], a, eta); + } + } + for (std::size_t i = 0; i < ft0.channelC().size(); ++i) { + const int id = ft0.channelC()[i] + KNChFT0A; + if (id < KNChFT0A || id >= KNChFT0) { + continue; + } + const float a = corr(ft0.amplitudeC()[i], fwdCalib.gainFT0[id]); + const float eta = channelEta(fwdCalib.posFT0[id], vz); + addFwd(kFT0C, eta, a, fwd); + if (qa) { + fillChannelQA(id, ft0.amplitudeC()[i], a, eta); + } + } + } + if (col.has_foundFV0()) { + const auto& fv0 = col.foundFV0(); + markFwdValid(kFV0A, fwd); + for (std::size_t i = 0; i < fv0.channel().size(); ++i) { + const int id = fv0.channel()[i]; + if (id < 0 || id >= KNChFV0) { + continue; + } + const float a = corr(fv0.amplitude()[i], fwdCalib.gainFV0[id]); + const float eta = channelEta(fwdCalib.posFV0[id], vz); + addFwd(kFV0A, eta, a, fwd); + if (qa) { + fillChannelQA(KNChFT0 + id, fv0.amplitude()[i], a, eta); + } + } + } + } - if (std::abs(denom2) < KFloatEpsilon) { - double pmk11safe = sumpmwk[1][1] / sumwk[1]; - return {static_cast(pmk11safe), 0.f}; + // Number of charged primaries in the acceptance of each forward detector (MC truth). + template + void computeFwdTruth(const P& particles, FwdSignal& fwd) + { + fwd = {}; + for (int det = 0; det < kNFwdDet; ++det) { + markFwdValid(det, fwd); + } + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + const float eta = particle.eta(); + if (std::abs(eta) < 1.5f) { + continue; + } + auto* pd = pdg->GetParticle(particle.pdgCode()); + if (!pd || std::abs(pd->Charge()) == 0) { + continue; + } + for (int det = 0; det < kNFwdDet; ++det) { + const int sub = fwdSubBin(det, eta); + if (sub >= 0) { + fwd.ampl[det] += 1.; + fwd.ampl[sub] += 1.; + } + } } + } - double pmk11 = sumpmwk[1][1] / sumwk[1]; + // =========================================================================== + // Observables + // =========================================================================== + // Bin content of p at (x, ybin[, zbin]), NaN for an empty bin. + static double profileValue(const TProfile2D* p, double x, int ybin) + { + if (!p) { + return std::numeric_limits::quiet_NaN(); + } + const int gb = p->GetBin(p->GetXaxis()->FindBin(x), ybin); + if (p->GetBinEntries(gb) <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return p->GetBinContent(gb); + } + static double profileValue(const TProfile3D* p, double x, int ybin, int zbin) + { + if (!p) { + return std::numeric_limits::quiet_NaN(); + } + const int gb = p->GetBin(p->GetXaxis()->FindBin(x), ybin, zbin); + if (p->GetBinEntries(gb) <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return p->GetBinContent(gb); + } - double pmk12 = 0.f; - if (sumwk[2] != 0.f) { - pmk12 = sumpmwk[1][2] / sumwk[2]; + void computeEventObs(const PowerSums& s, int v, int level, const FwdSignal* fwd, float nCh, EventObs& evObs) + { + const auto& input = inputs[v]; + const double nan = std::numeric_limits::quiet_NaN(); + evObs.nCh = nCh; + for (int ieta = 0; ieta < nEta; ++ieta) { + evObs.nW[ieta] = s.wk[ieta][1]; + evObs.mean[ieta] = (s.wk[ieta][1] > KFloatEpsilon) ? (s.pmwk[ieta][1][1] / s.wk[ieta][1]) : nan; + evObs.c2[ieta] = nan; + evObs.c3[ieta] = nan; + evObs.p1kBar[ieta] = nan; + // c2 (>=2 tracks), c3 (>=3 tracks) and the pT deviation need a usable reference + const double mmpt = profileValue(input.pmean[level], nCh, ieta + 1, kMeanPt + 1); + if (std::isfinite(mmpt) && mmpt != 0) { + const auto corr = calculateC2C3FromSums(s.pmwk[ieta], s.wk[ieta], mmpt); + evObs.c2[ieta] = corr.c2; + evObs.c3[ieta] = corr.c3; + if (std::isfinite(evObs.mean[ieta])) { + evObs.p1kBar[ieta] = evObs.mean[ieta] - mmpt; + } + } + evObs.dN[ieta] = evObs.nW[ieta] - profileValue(input.pmean[level], nCh, ieta + 1, kMeanN + 1); + } + evObs.fwdValid.fill(false); + if (!fwd || !input.pmeanFwd[level]) { + return; + } + for (int k = 0; k < nFwd; ++k) { + if (!fwd->valid[k]) { + continue; + } + const double m = profileValue(input.pmeanFwd[level], nCh, k + 1); + if (!std::isfinite(m)) { + continue; + } + evObs.fwd[k] = fwd->ampl[k]; + evObs.dFwd[k] = fwd->ampl[k] - m; + evObs.fwdValid[k] = true; } + } + + // Fill the fluctuation profiles of one (variation, level) at x = N_ch, or of one bootstrap + // replica with weight w at x = centrality. + void fillFluc(const FlucHists& h, const EventObs& evObs, double w, float x) + { + auto fill = [w](const std::shared_ptr& p, double a, double b, double c, double val) { + if (p) { + p->Fill(a, b, c, val, w); + } + }; - double pmk22 = 0.f; - if (sumwk[2] != 0.f) { - pmk22 = sumpmwk[2][2] / sumwk[2]; + // multiplicity, meanpT, C2, C3 vs eta bin + for (int ieta = 0; ieta < nEta; ++ieta) { + fill(h.obs, x, ieta, kObsN, evObs.nW[ieta]); + if (std::isfinite(evObs.mean[ieta])) { + fill(h.obs, x, ieta, kObsMeanPt, evObs.mean[ieta]); + } + if (std::isfinite(evObs.c2[ieta])) { + fill(h.obs, x, ieta, kObsC2, evObs.c2[ieta]); + } + if (std::isfinite(evObs.c3[ieta])) { + fill(h.obs, x, ieta, kObsC3, evObs.c3[ieta]); + } } - float calculatedMeanPt = pmk11; + // mirror-pair subevent (C2Sub, C3Sub) vs eta bin + for (int ietaA = 1; ietaA <= (nEta - 1) / 2; ++ietaA) { + int ietaC = nEta - ietaA; + float c2Sub = evObs.p1kBar[ietaA] * evObs.p1kBar[ietaC]; + // Mirror pairs (A, C = -A): both orderings are filled, each at the bin of its pair, + // so bins A < 0 hold AAB and bins C > 0 hold ABB of the same mirror pair. For a + // symmetric collision system the two agree in expectation: eta -> -eta maps + // AAB(a, -a) onto AAB(-a, a) = ABB(a, -a). Comparing bin A with bin nEta - A is + // therefore a forward/backward check, not two independent observables. + float c3SubA = evObs.c2[ietaA] * evObs.p1kBar[ietaC]; // AAB: 2 particles from A, 1 from C + float c3SubC = evObs.c2[ietaC] * evObs.p1kBar[ietaA]; // ABB: 2 particles from C, 1 from A + + if (std::isfinite(c2Sub)) { + fill(h.obs, x, ietaA, kObsC2Sub, c2Sub); + } + if (std::isfinite(c3SubA)) { + fill(h.obs, x, ietaA, kObsC3Sub, c3SubA); + } + if (std::isfinite(c3SubC)) { + fill(h.obs, x, ietaC, kObsC3Sub, c3SubC); + } + } - double p1kBar1 = pmk11 - referenceMeanPt; - double p2kBar2 = pmk22 - 2.0f * pmk12 * referenceMeanPt + referenceMeanPt * referenceMeanPt; + // full 2D subevent maps, in (eta_A, eta_C) and in (Delta eta, Sigma eta) + for (int ietaA = 1; ietaA < nEta; ++ietaA) { + for (int ietaC = 1; ietaC < nEta; ++ietaC) { + float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; + float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; + const float bw = etaUp[ietaA] - etaLw[ietaA]; + const float gap = (ietaA - ietaC) * bw; + const float sum = 2.f * etaLw[1] + (ietaA + ietaC - 1) * bw; // = cA + cC on the lattice + + float c2Sub = (ietaA == ietaC) ? static_cast(evObs.c2[ietaA]) : evObs.p1kBar[ietaA] * evObs.p1kBar[ietaC]; + // C3 sub-event map, ORDERED: cell (A,C) = c2[A] p1kBar[C] = _A _C, + // two particles from A and one from C ("AAB"). The same pair of bins read the + // other way round, cell (C,A) = c2[C] p1kBar[A], is "ABB" (one from A, two from C). + // Both are filled, so the map carries AAB and ABB of every bin pair: the row at + // eta_A = eta_ref is AAB with the pair at eta_ref, the column is ABB. Unlike C2, + // this map is NOT symmetric under A <-> C, and the two triangles must not be + // averaged or treated as duplicates. Diagonal: the within-bin c3 ("AAA"). + float c3Sub2D = (ietaA == ietaC) ? static_cast(evObs.c3[ietaA]) : evObs.c2[ietaA] * evObs.p1kBar[ietaC]; - double p1kBar1sq = p1kBar1 * p1kBar1; - double numerator2 = p1kBar1sq - (tau1 * p2kBar2); + if (std::isfinite(c2Sub)) { + fill(h.c2Sub2D, x, etaValA, etaValB, c2Sub); + fill(h.gapSum2D, x, gap, sum, c2Sub); + } + if (std::isfinite(c3Sub2D)) { + fill(h.c3Sub2D, x, etaValA, etaValB, c3Sub2D); + fill(h.c3GapSum2D, x, gap, sum, c3Sub2D); + } + } + } - float twopcorr = numerator2 / denom2; - return {calculatedMeanPt, twopcorr}; + // forward amplitude a_k: , and , with + // da = a - , d[pT] = [pT] - <[pT]>, dN = N - , the means at N_ch from the mean pass + for (int k = 0; k < nFwd; ++k) { + if (!evObs.fwdValid[k]) { + continue; + } + const double da = evObs.dFwd[k]; + if (h.fwdVar) { + h.fwdVar->Fill(x, k, da * da, w); + } + for (int ieta = 0; ieta < nEta; ++ieta) { + if (std::isfinite(evObs.p1kBar[ieta])) { + fill(h.covFwdPt, x, ieta, k, evObs.p1kBar[ieta] * da); + } + if (std::isfinite(evObs.dN[ieta])) { + fill(h.covFwdN, x, ieta, k, evObs.dN[ieta] * da); + } + } + } } + // =========================================================================== + // Table joins + // =========================================================================== using GeneralCollisions = soa::Join; - Filter collisionFilter = nabs(aod::collision::posZ) < cfgVtxZCut; + Filter collisionFilter = nabs(aod::collision::posZ) < cfgEvSel.cfgVtxZCut; using AodCollisionsSel = soa::Filtered; using UnfilteredTracks = soa::Join< - aod::Tracks, - aod::TracksExtra, - aod::TrackSelection, - aod::TracksDCA, - aod::pidTPCFullPi, aod::pidTPCFullKa, aod::pidTPCFullPr, aod::pidTPCFullEl, - aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr, aod::pidTOFFullEl>; - Filter trackFilter = aod::track::pt > KPtMin&& - aod::track::pt < KPtMax&& - requireGlobalTrackInFilter(); + aod::Tracks, aod::TracksExtra, aod::TrackSelection, aod::TracksDCA>; + Filter trackFilter = aod::track::pt > KPtMin&& aod::track::pt < KPtMax&& requireGlobalTrackInFilter(); using AodTracksSel = soa::Filtered; using TCs = soa::Join; using FilteredTCs = soa::Filtered; - using BCsRun3 = soa::Join; using MyRun3MCCollisions = soa::Join< aod::Collisions, aod::EvSels, aod::Mults, aod::MultsExtra, aod::CentFT0Cs, aod::CentFT0Ms, aod::CentFDDMs, aod::CentFV0As, aod::CentNGlobals, aod::McCollisionLabels>; + // reconstructed collisions of one MC collision + using McRecoCollisions = soa::SmallGroups; + + Preslice tracksPerCollision = aod::track::collisionId; + + // =========================================================================== + // Histogram declarations + // =========================================================================== + // Bin labels only on TH1/TH2 and THnSparse axes: ROOT merges labelled axes by label, which for + // profiles and TH3 extends the axis and sums all bins into a new one, or drops the entries of + // bins with zero content. Their bins are named in the axis title instead. + static void labelAxis(TAxis* ax, const std::vector& labels) + { + for (std::size_t i = 0; i < labels.size() && static_cast(i) < ax->GetNbins(); ++i) { + ax->SetBinLabel(i + 1, labels[i]); + } + } - PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + void labelSystAxis(TAxis* ax) const + { + for (int v = 0; v < kNSyst && v < ax->GetNbins(); ++v) { + ax->SetBinLabel(v + 1, systName[v].c_str()); + } + } void declareCommonQA() { histos.add("hVtxZ_after_sel", ";z_{vtx} (cm)", kTH1F, {{KNbinsZvtx, KZvtxMin, KZvtxMax}}); histos.add("hVtxZ", ";z_{vtx} (cm)", kTH1F, {{KNbinsZvtx, KZvtxMin, KZvtxMax}}); histos.add("hCentrality", ";centrality (%)", kTH1F, {{centAxis1Per}}); - histos.add("Hist2D_globalTracks_PVTracks", ";N_{global};N_{PV}", kTH2F, {{nChAxis}, {nChAxis}}); - histos.add("Hist2D_cent_nch", ";N_{PV};cent (%)", kTH2F, {{nChAxis}, {centAxis1Per}}); - - histos.add("Hist2D_globalTracks_cent", "cent (%);N_{global}", kTH2F, {{centAxis1Per}, {nChAxis}}); - histos.add("Hist2D_PVTracks_cent", "cent (%);N_{PV}", kTH2F, {{centAxis1Per}, {nChAxis}}); + histos.add("Hist2D_globalTracks_PVTracks", ";N_{PV};N_{ch} (global tracks)", kTH2F, {{multQAAxis}, {multQAAxis}}); + histos.add("Hist2D_PVTracks_cent", "cent (%);N_{PV}", kTH2F, {{centAxis1Per}, {multQAAxis}}); + hists.nchOccupancy = histos.add("Hist2D_Nch_occupancy", ";N_{ch} (global tracks);trackOccupancyInTimeRange", kTH2F, {{nchAxisObs}, {KNbinsOccQA, 0., KOccQAMax}}); histos.add("hP", ";p (GeV/c)", kTH1F, {{KNbinsPt, KPMin, KPMax}}); histos.add("hPt", ";p_{T} (GeV/c)", kTH1F, {{KNbinsPt, KPtMin, KPtMax}}); histos.add("hEta", ";#eta", kTH1F, {{KNbinsEtaFine, KEtaMin, KEtaMax}}); histos.add("hPhi", ";#phi", kTH1F, {{KNbinsPhi, KPhiMin, TwoPI}}); - histos.add("hEvtCount", "Number of Event;; Count", kTH1F, {{9, 0, 9}}); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(1, "all Events"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(2, "after sel8"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(3, "after VertexZ Cut"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(4, "after kNoSameBunchPileup"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(5, "after kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(6, "after kIsGoodITSLayersAll"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(7, "after PVTracksCent Pileup Cut"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(8, "after Linear Pileup Cut (Up)"); - histos.get(HIST("hEvtCount"))->GetXaxis()->SetBinLabel(9, "after Linear Pileup Cut (Lw)"); - - histos.add("hTrkCount", "Number of Tracks;; Count", kTH1F, {{11, 0, 11}}); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(1, "all Tracks"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(2, "after sign!=0"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(3, "after has_collision"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(4, "after isPVContributor"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(5, "after itsNCls"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(6, "after tpcNClsFound"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(7, "after tpcNClsCrossedRows"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(8, "after pT,#eta selections"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(9, "after isGlobalTrack"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(10, "after dcaXY"); - histos.get(HIST("hTrkCount"))->GetXaxis()->SetBinLabel(11, "after dcaZ"); - } - - void declareMCCommonHists() - { - for (const auto& suf : pidSuffix) { - histos.add("h3_AllPrimary" + suf, ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoMatchedToPrimary" + suf, ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_AllReco" + suf, ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoUnMatchedToPrimary_Secondary" + suf, ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoUnMatchedToPrimary_Fake" + suf, ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - histos.add("h3_RecoMatchedToPrimary_MisID" + suf, ";N_{PV};p_{T};#eta", kTH3F, {{nChAxis2}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - } + // cut flows of every variation (y bin 1 = Base); 1D copies per variation in declareCounts() + hists.evtCount = histos.add("hEvtCountSyst", "Number of events;;variation", kTH2D, {{kNEvCut, -0.5, static_cast(kNEvCut) - 0.5}, {kNSyst, -0.5, static_cast(kNSyst) - 0.5}}); + labelAxis(hists.evtCount->GetXaxis(), evtCutLabels()); + labelSystAxis(hists.evtCount->GetYaxis()); - histos.add("ptResolution", ";p_{T}^{MC};(p_{T}^{reco}-p_{T}^{MC})/p_{T}^{MC}", kTH2F, {{KNbinsPtRes, KPtMin, KPtMax}, {100, -0.2, 0.2}}); - histos.add("etaResolution", ";#eta^{MC};#eta^{reco}-#eta^{MC}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {100, -0.02, 0.02}}); - histos.add("etaTruthReco", ";#eta^{MC};#eta^{reco}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}}); - histos.add("TruthTracKVz", ";Vz^{MC};Vz^{Reco}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {KNbinsVz, KVzMin, KVzMax}}); - histos.add("vzResolution", ";Vz^{MC};(Vz^{reco}-Vz^{MC})/Vz^{MC}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {100, -0.1, 0.1}}); + hists.trkCount = histos.add("hTrkCountSyst", "Number of tracks;;variation", kTH2D, {{kNTrkCut, -0.5, static_cast(kNTrkCut) - 0.5}, {kNSyst, -0.5, static_cast(kNSyst) - 0.5}}); + labelAxis(hists.trkCount->GetXaxis(), trkCutLabels()); + labelSystAxis(hists.trkCount->GetYaxis()); } - void declarenSigHists() + static std::vector evtCutLabels() { - for (const auto& suf : pidSuffix) { - histos.add("h3DnsigmaTpcVsPtBefCut_Cent" + suf, "TPC nSigma vs pT Before Cut;cent [%]; p_{T} (GeV/c);n#sigma_{TPC}", kTH3F, {{centAxis1Per}, {KNbinsPtRes, KPtMin, KPtMax}, {200, -10.f, 10.f}}); - histos.add("h3DnsigmaTofVsPtBefCut_Cent" + suf, "TOF nSigma vs pT Before Cut;cent [%]; p_{T} (GeV/c);n#sigma_{TOF}", kTH3F, {{centAxis1Per}, {KNbinsPtRes, KPtMin, KPtMax}, {200, -10.f, 10.f}}); - histos.add("h3DnsigmaTpcVsTofBefCut_Cent" + suf, "TPC vs TOF nSigma Before Cut;cent [%]; n#sigma_{TOF};n#sigma_{TPC}", kTH3F, {{centAxis1Per}, {200, -10.f, 10.f}, {200, -10.f, 10.f}}); - histos.add("h3DnsigmaTpcVsPtAftCut_Cent" + suf, "TPC nSigma vs pT After Cut;cent [%],; p_{T} (GeV/c);n#sigma_{TPC}", kTH3F, {{centAxis1Per}, {KNbinsPtRes, KPtMin, KPtMax}, {200, -10.f, 10.f}}); - histos.add("h3DnsigmaTofVsPtAftCut_Cent" + suf, "TOF nSigma vs pT After Cut;cent [%],; p_{T} (GeV/c);n#sigma_{TOF}", kTH3F, {{centAxis1Per}, {KNbinsPtRes, KPtMin, KPtMax}, {200, -10.f, 10.f}}); - histos.add("h3DnsigmaTpcVsTofAftCut_Cent" + suf, "TPC vs TOF nSigma After Cut;cent [%],; n#sigma_{TOF};n#sigma_{TPC}", kTH3F, {{centAxis1Per}, {200, -10.f, 10.f}, {200, -10.f, 10.f}}); - } + return {"all", "sel8", "|z_{vtx}|", "kIsGoodZvtxFT0vsPV", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kIsVertexITSTPC", + "kNoSameBunchPileup", "kNoCollInTimeRangeStandard", + "ITS good layers", "n#sigma/linear pile-up", "variation cut", "cent < cfgCentMax (selected)", + "selected: kNoHighMultCollInPrevRof", "selected: occupancy < 0"}; + } + static std::vector trkCutLabels() + { + return {"all", "sign#neq0", "has_collision", "isPVContributor", "p_{T}, #eta", "isGlobalTrack, crossed rows", + "itsNCls", "tpcNClsFound", "tpcChi2NCl", "DCAz", "variation cut (selected)", "valid weight (used)"}; } - void declareMCGetFlatHists() + // hEvtCount and hTrkCount of each variation of this pass (Base at the top level, the others + // in their folder), so that every variation's output carries its own cut flow. + void declareCounts(uint32_t mask) { - for (const auto& suf : pidSuffix) { - histos.add("MCGen/hEtaPhiReco" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoEffWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("MCGen/hEtaPhiRecoWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + auto& r = reg(v); + hists.evtCount1D[v] = r.add((systPrefix(v) + "hEvtCount").c_str(), ("Number of events, " + systName[v]).c_str(), kTH1D, {{kNEvCut, -0.5, static_cast(kNEvCut) - 0.5}}); + labelAxis(hists.evtCount1D[v]->GetXaxis(), evtCutLabels()); + hists.trkCount1D[v] = r.add((systPrefix(v) + "hTrkCount").c_str(), ("Number of tracks, " + systName[v]).c_str(), kTH1D, {{kNTrkCut, -0.5, static_cast(kNTrkCut) - 0.5}}); + labelAxis(hists.trkCount1D[v]->GetXaxis(), trkCutLabels()); } } - void declareMCMeanHists() + void declareCutQA() { - histos.add("Eff_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("Eff_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("Eff_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("Eff_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); - - histos.add("Fake_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("Fake_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("Fake_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("Fake_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); + const AxisSpec ptAxis{100, 0., 10., "p_{T} (GeV/c)"}; + for (int st = 0; st < kNQAStage; ++st) { + const std::string d = st == kQABefore ? "CutQA/before/" : "CutQA/after/"; + auto h1 = [&](const char* name, const char* title, const AxisSpec& ax) { return histos.add((d + name).c_str(), title, kTH1F, {ax}); }; + auto h2 = [&](const char* name, const char* title, const AxisSpec& ax, const AxisSpec& ay) { return histos.add((d + name).c_str(), title, kTH2F, {ax, ay}); }; + hists.qaEvt1D[kQAVtxZ][st] = h1("hVtxZ", ";z_{vtx} (cm)", {200, -20., 20.}); + hists.qaEvt1D[kQACent][st] = h1("hCent", ";centrality (%)", {centAxis1Per}); + hists.qaEvt1D[kQAOccupancy][st] = h1("hOccupancy", ";trackOccupancyInTimeRange", {KNbinsOccQA, 0., KOccQAMax}); + hists.qaEvt2D[kQANchNPV][st] = h2("hNch_NPV", ";N_{PV};N_{ch} (global tracks)", multQAAxis, multQAAxis); + hists.qaEvt2D[kQANchCent][st] = h2("hNch_cent", ";centrality (%);N_{ch} (global tracks)", centAxis1Per, multQAAxis); + hists.qaTrk1D[kQAPt][st] = h1("hTrkPt", "", ptAxis); + hists.qaTrk1D[kQAEta][st] = h1("hTrkEta", ";#eta", {60, -1.5, 1.5}); + hists.qaTrk1D[kQAItsNCls][st] = h1("hTrkItsNCls", ";ITS clusters", {8, -0.5, 7.5}); + hists.qaTrk1D[kQAItsChi2][st] = h1("hTrkItsChi2NCl", ";ITS #chi^{2}/cluster", {80, 0., 40.}); + hists.qaTrk1D[kQATpcNClsFound][st] = h1("hTrkTpcNClsFound", ";TPC found clusters", {161, -0.5, 160.5}); + hists.qaTrk1D[kQATpcCrossedRows][st] = h1("hTrkTpcCrossedRows", ";TPC crossed rows", {161, -0.5, 160.5}); + hists.qaTrk1D[kQATpcCrossedRowsOverFindable][st] = h1("hTrkTpcCrossedRowsOverFindable", ";TPC crossed rows / findable clusters", {75, 0., 1.5}); + hists.qaTrk1D[kQATpcChi2][st] = h1("hTrkTpcChi2NCl", ";TPC #chi^{2}/cluster", {100, 0., 10.}); + hists.qaTrk1D[kQAPVContributor][st] = h1("hTrkPVContributor", ";isPVContributor", {2, -0.5, 1.5}); + hists.qaTrk1D[kQAGlobalTrack][st] = h1("hTrkGlobalTrack", ";isGlobalTrack", {2, -0.5, 1.5}); + hists.qaTrk2D[kQADcaXYPt][st] = h2("hTrkDcaXY_pt", ";p_{T} (GeV/c);DCA_{xy} (cm)", ptAxis, {400, -1., 1.}); + hists.qaTrk2D[kQADcaZPt][st] = h2("hTrkDcaZ_pt", ";p_{T} (GeV/c);DCA_{z} (cm)", ptAxis, {300, -3., 3.}); + } + } - histos.add("wgt_cent", ";cent", kTProfile, {centAxis1Per}); - histos.add("wgt_Ntrk", ";N_{PV}", kTProfile, {nChAxis2}); - histos.add("wgt_pT", ";p_{T}", kTProfile, {{KNbinsPtRes, KPtMin, KPtMax}}); - histos.add("wgt_eta", ";#eta", kTProfile, {{KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); + template + void fillCutQAEvent(int st, const C& col, int nCh, float cent) + { + hists.qaEvt1D[kQAVtxZ][st]->Fill(col.posZ()); + hists.qaEvt1D[kQACent][st]->Fill(cent); + hists.qaEvt1D[kQAOccupancy][st]->Fill(col.trackOccupancyInTimeRange()); + hists.qaEvt2D[kQANchNPV][st]->Fill(col.multNTracksPV(), nCh); + hists.qaEvt2D[kQANchCent][st]->Fill(cent, nCh); + } - histos.add("pmeanFT0Amultpv", ";N_{PV};Ampl", kTProfile, {nChAxis}); - histos.add("pmeanFT0Cmultpv", ";N_{PV};Ampl", kTProfile, {nChAxis}); - histos.add("pmeanFT0A_cent", ";cent;Ampl", kTProfile, {centAxis1Per}); - histos.add("pmeanFT0C_cent", ";cent;Ampl", kTProfile, {centAxis1Per}); - histos.add("pmean_cent_id_eta_FT0", ";cent;id;#eta", kTProfile3D, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - histos.add("h3_cent_id_eta_FT0", ";cent;id;#eta", kTH3F, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); + template + void fillCutQATrack(int st, const T& trk) + { + hists.qaTrk1D[kQAPt][st]->Fill(trk.pt()); + hists.qaTrk1D[kQAEta][st]->Fill(trk.eta()); + hists.qaTrk1D[kQAItsNCls][st]->Fill(trk.itsNCls()); + hists.qaTrk1D[kQAItsChi2][st]->Fill(trk.itsChi2NCl()); + hists.qaTrk1D[kQATpcNClsFound][st]->Fill(trk.tpcNClsFound()); + hists.qaTrk1D[kQATpcCrossedRows][st]->Fill(trk.tpcNClsCrossedRows()); + hists.qaTrk1D[kQATpcCrossedRowsOverFindable][st]->Fill(trk.tpcCrossedRowsOverFindableCls()); + hists.qaTrk1D[kQATpcChi2][st]->Fill(trk.tpcChi2NCl()); + hists.qaTrk1D[kQAPVContributor][st]->Fill(trk.isPVContributor()); + hists.qaTrk1D[kQAGlobalTrack][st]->Fill(trk.isGlobalTrack()); + hists.qaTrk2D[kQADcaXYPt][st]->Fill(trk.pt(), trk.dcaXY()); + hists.qaTrk2D[kQADcaZPt][st]->Fill(trk.pt(), trk.dcaZ()); + } - histos.add("MCGen/Prof_Cent_Nsp_Nchrec", ";cent;isp", kTProfile2D, {{centAxis1Per}, {spBinAxis}}); - histos.add("MCGen/Prof_Mult_Nsp_Nchrec", ";mult;isp", kTProfile2D, {{nChAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_Cent_Nsp_MeanpT", ";cent;isp", kTProfile2D, {{centAxis1Per}, {spBinAxis}}); - histos.add("MCGen/Prof_Mult_Nsp_MeanpT", ";mult;isp", kTProfile2D, {{nChAxis}, {spBinAxis}}); + // (centrality, N_ch) of the selected events of every variation: its event count and the + // centrality of each N_ch bin with the estimator of the variation. + void declareNchCent(uint32_t mask) + { + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v) || hists.nchCent[v]) { + continue; + } + hists.nchCent[v] = reg(v).add((systPrefix(v) + "Hist2D_globalTracks_cent").c_str(), "cent (%);N_{ch} (global tracks)", kTH2F, {{centAxis1Per}, {nchAxisOf(v)}}); + } + } - histos.add("pmeanTru_nch_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("pmeanReco_nch_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("pmeanRecoEffcorr_nch_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); + void declareFwdQA() + { + if (hists.fwdChannel) { + return; + } + hists.fwdChannel = histos.add("FwdQA/Prof_Channel_qa", ";channel (FT0A 0-95, FT0C 96-207, FV0A 208-255);quantity (0 amplitude, 1 gain-equalised amplitude, 2 #eta aligned vs z_{vtx})", kTProfile2D, {{KNChFT0 + KNChFV0, -0.5, KNChFT0 + KNChFV0 - 0.5}, {kNChQA, -0.5, static_cast(kNChQA) - 0.5}}); + hists.fwdEta = histos.add("FwdQA/Prof_Eta_fwdbin", ";forward bin;amplitude-weighted #LT#eta#GT", kTProfile, {fwdAxis}); + } - histos.add("pmeanMultTru_nch_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("pmeanMultReco_nch_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("pmeanMultRecoEffcorr_nch_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); + void declareEff(uint32_t mask) + { + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + for (int c = 0; c < kNEffCat; ++c) { + hists.eff[v].maps[c] = reg(v).add((systPrefix(v) + effMapName[c]).c_str(), ";centrality (%);p_{T} (GeV/c);#eta", kTH3F, + {{centAxisEff}, {KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}}); + } + } + } - for (const auto& suf : pidSuffix) { - histos.add("hEtaPhiReco" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); + void declareMCQA() + { + histos.add("ptResolution", ";p_{T}^{MC};(p_{T}^{reco}-p_{T}^{MC})/p_{T}^{MC}", kTH2F, {{KNbinsPtRes, KPtMin, KPtMax}, {100, -0.2, 0.2}}); + histos.add("etaResolution", ";#eta^{MC};#eta^{reco}-#eta^{MC}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {100, -0.02, 0.02}}); + histos.add("etaTruthReco", ";#eta^{MC};#eta^{reco}", kTH2F, {{KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}, {KNbinsEtaRes, -KEtaFineMax, KEtaFineMax}}); + histos.add("TruthTracKVz", ";Vz^{MC};Vz^{Reco}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {KNbinsVz, KVzMin, KVzMax}}); + histos.add("vzResolution", ";Vz^{MC};(Vz^{reco}-Vz^{MC})/Vz^{MC}", kTH2F, {{KNbinsVz, KVzMin, KVzMax}, {100, -0.1, 0.1}}); + hists.mcTrackOrigin = histos.add("MCQA/Hist3D_Nch_pT_origin", ";N_{ch};p_{T} (GeV/c);origin (0 primary, 1 secondary, 2 other MC collision, 3 no label)", kTH3F, {{nchAxisObs}, {KNbinsPtRes, KPtMin, KPtMax}, {kNOrigin, -0.5, static_cast(kNOrigin) - 0.5}}); + } - histos.add("Prof2D_MeanpTSub_Tru" + suf, ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); - histos.add("Prof2D_MeanpTSub_Reco" + suf, ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); - histos.add("Prof2D_MeanpTSub_RecoEffCorr" + suf, ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {etaBinAxis}}); + // vertex QA of all MC passes + void declareMCVtxQA() + { + if (hists.mcVtx) { + return; } + hists.mcVtx = histos.add("MCQA/Hist2D_dVz_type", ";z_{vtx}^{reco} - z_{vtx}^{MC} (cm);reco collision", kTH2F, {{200, -0.5, 0.5}, {kNVtxQA, -0.5, static_cast(kNVtxQA) - 0.5}}); + labelAxis(hists.mcVtx->GetYaxis(), {"single", "split: used", "split: skipped"}); } - void declareMCFlucHists() + // (vz, sign, pT, eta, phi) maps, unweighted, efficiency-weighted and with all weights; + // dir = "" (DATA) or "MCReco/". + void declareFlat(uint32_t mask, const std::string& dir) { - histos.add("MCGen/Prof_Cent_NEta_Nsp_Nchrec", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_Mult_NEta_Nsp_Nchrec", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_Cent_NEta_Nsp_MeanpT", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_Mult_NEta_Nsp_MeanpT", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - - histos.add("MCGen/Prof_MeanpT_Cent_etabin_spbin", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_C2_Cent_etabin_spbin", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_C2Sub_Cent_etabin_spbin", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_Cov_Cent_etabin_spbin", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_CovFT0A_Cent_etabin_spbin", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_CovFT0C_Cent_etabin_spbin", ";cent;eta;isp", kTProfile3D, {{centAxis1Per}, {etaBinAxis}, {spBinAxis}}); + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v) || hists.flat[v].maps) { + continue; + } + hists.flat[v].maps = reg(v).add((systPrefix(v) + dir + "hFlatMaps").c_str(), ";vz;sign;pt;eta;phi;weight", kTHnSparseF, + {{vzAxis}, {chgAxis}, {pTAxis}, {etaFlatAxis}, {phiAxis}, {kNFlatType, -0.5, static_cast(kNFlatType) - 0.5}}); + labelAxis(hists.flat[v].maps->GetAxis(5), {"raw", "(1-fake)/eff", "all weights"}); + } + } - histos.add("MCGen/Prof_MeanpT_Mult_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_C2_Mult_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_C2Sub_Mult_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_Cov_Mult_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_CovFT0A_Mult_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); - histos.add("MCGen/Prof_CovFT0C_Mult_etabin_spbin", ";mult;eta;isp", kTProfile3D, {{nChAxis}, {etaBinAxis}, {spBinAxis}}); + // Mean-pass profiles; MC: in MCGen/, MCReco/, MCRecoEffCorr/. + void declareMean(uint32_t mask, bool isMC) + { + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + const int nLev = isMC ? kNLevels : 1; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + auto& r = reg(v); + for (int l = 0; l < nLev; ++l) { + const std::string p = systPrefix(v) + (isMC ? mcDir[l] : std::string{}); + auto& h = hists.mean[v][l]; + h.mean = r.add((p + "Prof_Mean_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable (0 #LT[p_{T}]#GT, 1 #LT N#GT)", kTProfile3D, {{nchAxisOf(v)}, {etaBinAxis}, {kNMeanObs, -0.5, static_cast(kNMeanObs) - 0.5}}); + if (cfgFwdOpt.cfgFwd) { + h.fwdMean = r.add((p + "Prof_FwdAmpMean_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT a#GT", kTProfile2D, {{nchAxisOf(v)}, {fwdAxis}}); + } + } + } + } - for (const auto& suf : pidSuffix) { - histos.add("hEtaPhiReco" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); + // Fluctuation-pass profiles named prefix + name + suffix, with x axis ax. + // withGapSum: also the maps in (Delta eta, Sigma eta). + FlucHists declareFluc(HistogramRegistry& r, const std::string& p, const std::string& s, const AxisSpec& ax, bool withGapSum) + { + FlucHists h; + auto n = [&](const char* name) { return p + name + s; }; + h.obs = r.add(n("Prof_Obs_Nch_etabin_obs").c_str(), ";N_{ch};#eta-bin;observable (0 N, 1 [p_{T}], 2 c_{2}, 3 c_{3}, 4 C_{2}^{sub} mirror, 5 C_{3}^{sub} mirror)", kTProfile3D, {{ax}, {etaBinAxis}, {kNFlucObs, -0.5, static_cast(kNFlucObs) - 0.5}}); + h.c2Sub2D = r.add(n("Prof_C2Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A};#eta_{C}", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); + h.c3Sub2D = r.add(n("Prof_C3Sub2D_Nch_etaA_etaC").c_str(), ";N_{ch};#eta_{A} (pair);#eta_{C} (single)", kTProfile3D, {{ax}, {etaAxis}, {etaAxis}}); + if (withGapSum) { + h.gapSum2D = r.add(n("Prof_GapSum2D_Nch_gap_sum").c_str(), ";N_{ch};#eta_{A} #minus #eta_{C} (Gap);#eta_{A} + #eta_{C} (Sum)", kTProfile3D, {{ax}, {gapAxis}, {sumAxis}}); + h.c3GapSum2D = r.add(n("Prof_C3GapSum2D_Nch_gap_sum").c_str(), ";N_{ch};#eta_{pair} #minus #eta_{single};#eta_{pair} + #eta_{single}", kTProfile3D, {{ax}, {gapAxis}, {sumAxis}}); + } + if (cfgFwdOpt.cfgFwd) { + h.fwdVar = r.add(n("Prof_FwdAmpVar_Nch_fwdbin").c_str(), ";N_{ch};forward bin;#LT #delta a^{2}#GT", kTProfile2D, {{ax}, {fwdAxis}}); + h.covFwdPt = r.add(n("Prof_CovFwdAmpMeanpT_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta[p_{T}] #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); + h.covFwdN = r.add(n("Prof_CovFwdAmpNch_Nch_etabin_fwdbin").c_str(), ";N_{ch};#eta-bin;forward bin;#LT #delta N #delta a#GT", kTProfile3D, {{ax}, {etaBinAxis}, {fwdAxis}}); + } + return h; + } - histos.add(Form("MCGen/Prof_C2Sub2D_Cent_etaA_etaC%s", suf.c_str()), ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add(Form("MCGen/Prof_GapSum2D%s", suf.c_str()), ";cent;gap;sum", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add(Form("MCGen/Prof_Cov2D_Cent_etaA_etaC%s", suf.c_str()), ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add(Form("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC%s", suf.c_str()), ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add(Form("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC%s", suf.c_str()), ";cent;etaA;etaC", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + // Gap/sum maps for every DATA variation; in MC only for Base, to limit the memory. + void declareFlucAll(uint32_t mask, bool isMC) + { + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + const int nLev = isMC ? kNLevels : 1; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(mask, v)) { + continue; + } + for (int l = 0; l < nLev; ++l) { + hists.fluc[v][l] = declareFluc(reg(v), systPrefix(v) + (isMC ? mcDir[l] : std::string{}), "", nchAxisOf(v), !isMC || v == kSystBase); + } } } - void declareDataGetFlatHists() + // Poisson-bootstrap replicas of all fluctuation profiles (Base, DataFluc only). Their x axis + // is the Base centrality in 5% classes, the binning in which the errors are evaluated. + void declareBootstrapHists() { - for (const auto& suf : pidSuffix) { - histos.add("hEtaPhiReco" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); + for (int s = 0; s < nBoot; ++s) { + hists.boot[s] = declareFluc(histosBoot, "Bootstrap/", Form("_sample%d", s), centAxisBoot, true); } - histos.add("hnTrkPVZDC", ";N_{PV};ZDC_{A+C}", kTH2F, {{nChAxis2}, {200, 0, 3000}}); - histos.add("hNchZDC", ";N_{trk};ZDC_{A+C}", kTH2F, {{nChAxis2}, {200, 0, 30000}}); } - void declareDataMeanHists() + // Histograms of each pass, booked after the inputs so that switched-off variations get none. + void declareMCGetEffHists() { - histos.add("pmeanFT0Amultpv", "N_{PV}; AmplitudeA", kTProfile, {nChAxis}); - histos.add("pmeanFT0A_cent", "cent; AmplitudeA", kTProfile, {centAxis1Per}); - histos.add("pmeanFT0Cmultpv", "N_{PV}; AmplitudeA", kTProfile, {nChAxis}); - histos.add("pmeanFT0C_cent", "cent; AmplitudeA", kTProfile, {centAxis1Per}); + declareNchCent(maskEff); + declareEff(maskEff); + declareMCQA(); + } - histos.add("pmean_cent_id_eta_FT0", ";cent;channel id; #eta;amplitude", kTProfile3D, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); - histos.add("h3_cent_id_eta_FT0", ";cent;channel id; #eta", kTH3F, {{centAxis1Per}, {200, -0.5, 199.5}, {100, -5.0, 5.0}}); + void declareMCGetFlatHists() + { + declareNchCent(maskFlat); + declareFlat(maskFlat, "MCReco/"); + } - histos.add("Prof_Cent_Nsp_Nchrec", ";cent;Species;#LT N_{PV}#GT", kTProfile2D, {{centAxis1Per}, {spBinAxis}}); - histos.add("Prof_Mult_Nsp_Nchrec", ";N_{PV};Species;#LT N_{PV}#GT", kTProfile2D, {{nChAxis}, {spBinAxis}}); - histos.add("Prof_Cent_Nsp_MeanpT", ";cent;Species;#LT p_{T}#GT", kTProfile2D, {{centAxis1Per}, {spBinAxis}}); - histos.add("Prof_Mult_Nsp_MeanpT", ";N_{PV};Species;#LT p_{T}#GT", kTProfile2D, {{nChAxis}, {spBinAxis}}); + void declareMCMeanHists() + { + declareNchCent(maskAll); + declareFlat(1, "MCReco/"); // Base map with all weights, as QA + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareMean(maskAll, true); + histos.add("Prof_Weights_pT_type", ";p_{T};weight (0 eff, 1 fake, 2 w)", kTProfile2D, {{KNbinsPtRes, KPtMin, KPtMax}, {3, -0.5, 2.5}}); + } - histos.add("pmean_nch_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("pmeanMult_nch_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("pmean_cent_etabin_spbin", ";Centrality (%) ;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("pmeanMult_cent_etabin_spbin", ";Centrality (%) ;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); + void declareMCFlucHists() + { + declareNchCent(maskAll); + declareFlat(1, "MCReco/"); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareFlucAll(maskAll, true); + } - for (const auto& suf : pidSuffix) { - histos.add("hEtaPhiReco" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); + void declareDataGetFlatHists() + { + declareNchCent(maskFlat); + declareFlat(maskFlat, ""); + } - histos.add("Prof2D_MeanpTSub" + suf, ";cent;#eta_{A} bin;#eta_{C} bin", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {{etaBinAxis}}}); + void declareDataMeanHists() + { + declareNchCent(maskAll); + declareFlat(1, ""); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); } + declareMean(maskAll, false); + histos.add("Prof_Weights_pt_eta_type", ";p_{T} (GeV/c);#eta;weight (0 eff, 1 fake, 2 w_{#phi})", kTProfile3D, {{KNbinsPtRes, KPtMin, KPtMax}, {KNbinsEtaFine, -KEtaFineMax, KEtaFineMax}, {3, -0.5, 2.5}}); } void declareDataFlucHists() { - histos.add("Prof_MeanpT_Cent_etabin_spbin", ";cent;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_MeanpT_Mult_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_C2_Cent_etabin_spbin", ";cent;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_C2_Mult_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); - - histos.add("Prof_C2Sub_Cent_etabin_spbin", ";Centrality;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_C2Sub_Mult_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_Cov_Cent_etabin_spbin", ";Centrality;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_Cov_Mult_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); - - histos.add("Prof_CovFT0A_Cent_etabin_spbin", ";Centrality;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_CovFT0A_Mult_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_CovFT0C_Cent_etabin_spbin", ";Centrality;#eta-bin;Species", kTProfile3D, {{centAxis1Per}, {{etaBinAxis}}, {spBinAxis}}); - histos.add("Prof_CovFT0C_Mult_etabin_spbin", ";N_{PV};#eta-bin;Species", kTProfile3D, {{nChAxis}, {{etaBinAxis}}, {spBinAxis}}); + declareNchCent(maskAll); + declareFlat(1, ""); + if (cfgFwdOpt.cfgFwd) { + declareFwdQA(); + } + declareFlucAll(maskAll, false); + if (doBoot) { + declareBootstrapHists(); + } + } - for (const auto& suf : pidSuffix) { - histos.add("hEtaPhiReco" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoEffWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("hEtaPhiRecoWtd" + suf, ";vz;sign;pt;eta;phi", kTHnSparseF, {{vzAxis}, {chgAxis}, {pTAxis}, {etaAxis}, {phiAxis}}); - histos.add("Prof_C2Sub2D_Cent_etaA_etaC" + suf, ";cent;#eta_{A};#eta_{C}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_GapSum2D" + suf, ";cent;#Delta#eta (Gap);#Sigma#eta/2 (Sum)", kTProfile3D, {{centAxis1Per}, {gapAxis}, {sumAxis}}); - histos.add("Prof_Cov2D_Cent_etaA_etaC" + suf, ";cent;#eta_{A} bin;#eta_{C} bin", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_CovFT0A2D_Cent_etaA_etaC" + suf, ";cent;#eta_{A};#eta_{B}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); - histos.add("Prof_CovFT0C2D_Cent_etaA_etaC" + suf, ";cent;#eta_{A};#eta_{B}", kTProfile3D, {{centAxis1Per}, {etaAxis}, {etaAxis}}); + // =========================================================================== + // CCDB helpers + // =========================================================================== + static void copyAxis(TAxis* dst, const TAxis* src) + { + const TArrayD* xb = src->GetXbins(); + if (xb->GetSize() > 0) { + dst->Set(src->GetNbins(), xb->GetArray()); + } else { + dst->Set(src->GetNbins(), src->GetXmin(), src->GetXmax()); } } - THnSparseF* buildWeightMapFromRaw(THnSparseF* hRaw, const char* mapName) + // Flattening weights w_phi = / N_phi from bin `type` of the last axis of hFlatMaps. + THnSparseF* buildWeightMapFromRaw(const THnSparse* hRaw, int type, const char* mapName) { - if (!hRaw) { - LOGF(error, "Raw eta-phi map for '%s' is null; no flattening will be applied.", mapName); + if (!hRaw || hRaw->GetNdimensions() != 6) { + LOGF(error, "Raw eta-phi map for '%s' is null or not 6D; no flattening will be applied.", mapName); return nullptr; } - auto hWMap = reinterpret_cast(hRaw->Clone(mapName)); - hWMap->SetTitle(Form("Flattening Weight Map %s (w_{#phi} = / N_{#phi})", mapName)); - hWMap->Reset(); - auto axV = hRaw->GetAxis(0); // Vz - auto axChg = hRaw->GetAxis(1); // Charge - auto axPt = hRaw->GetAxis(2); // Pt - auto axE = hRaw->GetAxis(3); // Eta - auto axP = hRaw->GetAxis(4); // Phi - - int bins[5]; - for (int iv = 1; iv <= axV->GetNbins(); ++iv) { - bins[0] = iv; - for (int ichg = 1; ichg <= axChg->GetNbins(); ++ichg) { - bins[1] = ichg; - for (int ipt = 1; ipt <= axPt->GetNbins(); ++ipt) { - bins[2] = ipt; - for (int ie = 1; ie <= axE->GetNbins(); ++ie) { - bins[3] = ie; + std::array nb{}; + for (int i = 0; i < 5; ++i) { + nb[i] = hRaw->GetAxis(i)->GetNbins(); + } + auto* hWMap = new THnSparseF(mapName, Form("Flattening Weight Map %s (w_{#phi} = / N_{#phi})", mapName), 5, nb.data()); + for (int i = 0; i < 5; ++i) { + copyAxis(hWMap->GetAxis(i), hRaw->GetAxis(i)); + } + + std::array bins{}; + std::array wbins{}; + bins[5] = type + 1; + for (int iv = 1; iv <= nb[0]; ++iv) { + bins[0] = wbins[0] = iv; + for (int ichg = 1; ichg <= nb[1]; ++ichg) { + bins[1] = wbins[1] = ichg; + for (int ipt = 1; ipt <= nb[2]; ++ipt) { + bins[2] = wbins[2] = ipt; + for (int ie = 1; ie <= nb[3]; ++ie) { + bins[3] = wbins[3] = ie; double sum = 0.0; - int nphi = axP->GetNbins(); + const int nphi = nb[4]; for (int ip = 1; ip <= nphi; ++ip) { bins[4] = ip; - sum += hRaw->GetBinContent(bins); + sum += hRaw->GetBinContent(bins.data()); } const double avg = (nphi > 0 ? sum / nphi : 0.0); for (int ip = 1; ip <= nphi; ++ip) { - bins[4] = ip; - const double raw = hRaw->GetBinContent(bins); + bins[4] = wbins[4] = ip; + const double raw = hRaw->GetBinContent(bins.data()); const double w = (avg > 0.0 && raw > 0.0) ? (avg / raw) : 1.0; - hWMap->SetBinContent(bins, w); + hWMap->SetBinContent(wbins.data(), w); } } } } } - LOGF(info, "Flattening weight map '%s' built.", mapName); return hWMap; } - inline void loadTProfile3D(TDirectory* dir, const char* name, TProfile3D*& target) + template + void loadProfileFromList(TList* src, const char* name, TP*& target) { - if (!dir) { - LOGF(error, "loadTProfile3D: directory is null for object %s", name); + if (!src) { return; } - auto* obj = dir->Get(name); + auto* obj = src->FindObject(name); if (!obj) { - LOGF(error, "loadTProfile3D: object '%s' not found in directory %s", name, dir->GetName()); + LOGF(error, "Profile %s missing in CCDB TList", name); return; } - auto* prof = dynamic_cast(obj); - if (!prof) { - LOGF(error, "loadTProfile3D: object '%s' is not a TProfile3D (it is %s)", name, obj->ClassName()); + auto* tp = dynamic_cast(obj); + if (!tp) { + LOGF(error, "%s is not the expected profile type (it is %s)", name, obj->ClassName()); return; } - target = reinterpret_cast(prof->Clone(Form("%s_clone", name))); + target = dynamic_cast(tp->Clone()); target->SetDirectory(nullptr); - LOGF(info, "Loaded TProfile3D '%s' with entries = %.0f", name, target->GetEntries()); + LOGF(info, "Loaded %s from list", name); } - void init(InitContext&) + // Switch variation v off because an input is missing; a missing Base input is fatal. + void dropSyst(int v, const std::string& why) { - if (cfgSys == kPbPb) { - nChAxis = {cfgNchPbMax / 2, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; - nChAxis2 = {cfgNchPbMax / 4, KBinOffset, cfgNchPbMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; - } else { - nChAxis = {cfgNchOMax, KBinOffset, cfgNchOMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; - nChAxis2 = {cfgNchOMax, KBinOffset, cfgNchOMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + if (v == kSystBase) { + LOGF(fatal, "Base input missing: %s", why.c_str()); + return; } + LOGF(error, "Variation %s disabled: %s", systName[v].c_str(), why.c_str()); + CLRBIT(maskAll, v); + CLRBIT(maskEff, v); + CLRBIT(maskFlat, v); + } - ccdb->setURL(cfgCCDBurl.value); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - ccdb->setCreatedNotAfter(now); - - loadAlignParam(now); - ft0Det.calculateChannelCenter(); - - std::string sysDir = ""; - switch (cfgSys) { - case kPbPb: - sysDir = "PbPbTest"; - break; - case kNeNe: - sysDir = "NeNeTest"; - break; - case kOO: - sysDir = "OOTest"; - break; - case kpp: - sysDir = "ppTest"; - break; - default: - LOGF(fatal, "Invalid cfgSys value: %d", cfgSys.value); + // Efficiency and fake-rate maps of variation s versus (centrality, pT, eta): + // eff = matched primaries / primaries, fake = (secondaries + other collision + no label) / all reco. + bool loadEffMaps(int s, const std::string& path, int64_t now) + { + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + return false; } - std::string pathNsig = cfgCCDBUserPath.value + "/" + sysDir + "/Job0_nSigMaps"; - std::string pathEff = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_EffMaps"; - std::string pathMCFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_MCFlatMaps"; - std::string pathMCMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_MCMean"; - - std::string pathDataNsig = cfgCCDBUserPath.value + "/" + sysDir + "/Job0_DatanSigMaps"; - std::string pathDataFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_DataFlatMaps"; - std::string pathDataMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_DataMean"; - - declareCommonQA(); - if (cfgRunMCGetNSig || cfgRunGetEff || cfgRunDataGetNSig || cfgRunGetDataFlat) { - declarenSigHists(); + std::array h{}; + for (int c = 0; c < kNEffCat; ++c) { + h[c] = dynamic_cast(lst->FindObject(effMapName[c].c_str())); + if (!h[c]) { + LOGF(error, "Missing efficiency map %s in %s", effMapName[c].c_str(), path.c_str()); + return false; + } } + auto* eff = dynamic_cast(h[kEffRecoMatchedPrim]->Clone(Form("hEff_%d", s))); + auto* fake = dynamic_cast(h[kEffRecoSec]->Clone(Form("hFake_%d", s))); + eff->SetDirectory(nullptr); + fake->SetDirectory(nullptr); + fake->Add(h[kEffRecoOtherColl]); + fake->Add(h[kEffRecoFake]); + // integrated over centrality (one centrality bin), for empty or invalid cells + const int nCent = eff->GetNbinsX(); + auto ratio = [&](TH3* num, TH3* den, const char* name) { + auto* r = num->RebinX(nCent, name); + std::unique_ptr d(den->RebinX(nCent, Form("%s_den", name))); + r->SetDirectory(nullptr); + r->Divide(d.get()); + return r; + }; + inputs[s].hEffPtEta = ratio(eff, h[kEffAllPrim], Form("hEff_%d_ptEta", s)); + inputs[s].hFakePtEta = ratio(fake, h[kEffAllReco], Form("hFake_%d_ptEta", s)); + eff->Divide(h[kEffAllPrim]); + fake->Divide(h[kEffAllReco]); + inputs[s].hEff = eff; + inputs[s].hFake = fake; + return true; + } - if (cfgRunMCMean || cfgRunMCFluc || cfgRunGetEff) { - declareMCCommonHists(); - } - if (cfgRunGetMCFlat) { - declareMCGetFlatHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); + THnSparseF* loadFlatMap(int s, const std::string& path, const std::string& dir, int64_t now) + { + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + return nullptr; } - if (cfgRunMCMean) { - declareMCMeanHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); + const std::string hName = dir + "hFlatMaps"; + const auto* hRaw = dynamic_cast(lst->FindObject(hName.c_str())); + if (!hRaw) { + LOGF(error, "Flattening map '%s' not found in %s", hName.c_str(), path.c_str()); + return nullptr; } - if (cfgRunMCFluc) { - declareMCFlucHists(); - histos.addClone("MCGen/", "MCReco/"); - histos.addClone("MCGen/", "MCRecoEffCorr/"); + return buildWeightMapFromRaw(hRaw, cfgEff ? kFlatWtd : kFlatRaw, Form("hFlatWeight_%d", s)); + } + + // Mean-pass objects of variation v for the fluctuation pass. + void loadMeans(int v, const std::string& path, bool isMC, int64_t now) + { + static const std::array mcDir = {"MCGen/", "MCReco/", "MCRecoEffCorr/"}; + auto* lst = ccdb->getForTimeStamp(path, now); + if (!lst) { + dropSyst(v, "no mean-pT list at " + path); + return; } - if (cfgRunDataGetNSig) { - declareDataGetFlatHists(); - } - if (cfgRunGetDataFlat) { - declareDataGetFlatHists(); - } - if (cfgRunDataMean) { - declareDataMeanHists(); - } - if (cfgRunDataFluc) { - declareDataFlucHists(); - } - - if (!cfgRunGetEff && !cfgRunMCGetNSig && (cfgEff)) { - TList* lst = ccdb->getForTimeStamp(pathEff, now); - - if (!lst) { - LOGF(fatal, "Efficiency maps required but CCDB list is null at %s!", pathEff.c_str()); + const int nLev = isMC ? kNLevels : 1; + for (int l = 0; l < nLev; ++l) { + const std::string d = isMC ? mcDir[l] : std::string{}; + loadProfileFromList(lst, (d + "Prof_Mean_Nch_etabin_obs").c_str(), inputs[v].pmean[l]); + if (!inputs[v].pmean[l]) { + dropSyst(v, "mean-pT profile missing in " + path); + return; } - - LOGF(info, "Loading Eff/Fake maps from TList for all species..."); - - auto loadEffFakeForPID = [&](PIDIdx pidType) { - std::string suffix = pidSuffix[pidType]; - std::string hEffNumName = "h3_RecoMatchedToPrimary" + suffix; - std::string hEffDenName = "h3_AllPrimary" + suffix; - std::string hFakeNumSecName = "h3_RecoUnMatchedToPrimary_Secondary" + suffix; - std::string hFakeNumFakName = "h3_RecoUnMatchedToPrimary_Fake" + suffix; - std::string hFakeNumFakName2 = "h3_RecoMatchedToPrimary_MisID" + suffix; - std::string hFakeDenName = "h3_AllReco" + suffix; - - auto* hNum = reinterpret_cast(lst->FindObject(hEffNumName.c_str())); - auto* hDen = reinterpret_cast(lst->FindObject(hEffDenName.c_str())); - - if (hNum && hDen) { - state.hEff[pidType] = reinterpret_cast(hNum->Clone(Form("hEff%s", suffix.c_str()))); - state.hEff[pidType]->SetDirectory(nullptr); - state.hEff[pidType]->Divide(hDen); - } else { - LOGF(error, "Missing CCDB objects for efficiency. Checked: %s, %s", hEffNumName.c_str(), hEffDenName.c_str()); + // The fluctuation pass reads its reference at (N_ch bin, eta bin), so the binning of the + // mean pass has to be the same. + const auto* p = inputs[v].pmean[l]; + if (p->GetYaxis()->GetNbins() != nEta + 1 || + p->GetXaxis()->GetNbins() != nchAxisOf(v).getNbins() || + std::abs(p->GetXaxis()->GetBinWidth(1) - nchBinWidthOf(v)) > KFloatEpsilon) { + LOGF(fatal, + "Mean pass %s has %d N_ch bins of width %.1f and %d eta bins; this job uses %d bins of " + "width %d and %d eta bins. Rerun the mean pass with the same binning.", + path.c_str(), p->GetXaxis()->GetNbins(), p->GetXaxis()->GetBinWidth(1), p->GetYaxis()->GetNbins(), + nchAxisOf(v).getNbins(), nchBinWidthOf(v), nEta + 1); + } + if (cfgFwdOpt.cfgFwd) { + loadProfileFromList(lst, (d + "Prof_FwdAmpMean_Nch_fwdbin").c_str(), inputs[v].pmeanFwd[l]); + if (inputs[v].pmeanFwd[l] && inputs[v].pmeanFwd[l]->GetYaxis()->GetNbins() != nFwd) { + LOGF(error, "%s: mean pass has %d forward bins, this job %d; forward covariances not filled", + path.c_str(), inputs[v].pmeanFwd[l]->GetYaxis()->GetNbins(), nFwd); + inputs[v].pmeanFwd[l] = nullptr; } + } + } + } - auto* hNumS = reinterpret_cast(lst->FindObject(hFakeNumSecName.c_str())); - auto* hNumF = reinterpret_cast(lst->FindObject(hFakeNumFakName.c_str())); - auto* hNumF2 = reinterpret_cast(lst->FindObject(hFakeNumFakName2.c_str())); - auto* hDenF = reinterpret_cast(lst->FindObject(hFakeDenName.c_str())); - - if (hNumS && hNumF && hDenF) { - state.hFake[pidType] = reinterpret_cast(hNumS->Clone(Form("hFake%s", suffix.c_str()))); - state.hFake[pidType]->Add(hNumF); - if (pidType != kInclusiveIdx && hNumF2) { - state.hFake[pidType]->Add(hNumF2); - } - state.hFake[pidType]->SetDirectory(nullptr); - state.hFake[pidType]->Divide(hDenF); - } else { - LOGF(error, "Missing CCDB object(s) for fakes for %s in list.", suffix.c_str()); + // =========================================================================== + // init + // =========================================================================== + void init(InitContext&) + { + TH1::SetDefaultSumw2(true); + // Multiplicity axes: multQAAxis for the QA, nchAxisObs (nchAxisNchBin2) for the observables. + const int nMax = (cfgSys == kPbPb) ? cfgNch.cfgNchPbMax.value : (cfgSys == kpp ? cfgNch.cfgNchPPMax.value : cfgNch.cfgNchOMax.value); + if (cfgSys == kPbPb) { + multQAAxis = {nMax / 2, KBinOffset, nMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + } else { + multQAAxis = {nMax, KBinOffset, nMax + KBinOffset, "Nch", "PV-contributor track multiplicity"}; + } + { + // bin k holds k*width ... (k+1)*width - 1 tracks + auto makeNchAxis = [&](int width) { + if (width < 1) { + LOGF(fatal, "N_ch bin width must be >= 1 (got %d)", width); } + const int nb = (nMax + width - 1) / width; + return AxisSpec{nb, -KBinOffset, nb * width - KBinOffset, "N_{ch}"}; }; - - for (int i = 0; i < KNsp; ++i) { - loadEffFakeForPID(static_cast(i)); + const bool pp = (cfgSys == kpp); + nchWidth = (cfgSys == kPbPb) ? cfgNch.cfgNchBinWidthPbPb.value : (pp ? cfgNch.cfgNchBinWidthPP.value : cfgNch.cfgNchBinWidthOO.value); + nchWidthNchBin2 = (cfgSys == kPbPb) ? KSystNchBinWidthPbPb : (pp ? KSystNchBinWidthPP : KSystNchBinWidthOO); + nchAxisObs = makeNchAxis(nchWidth); + nchAxisNchBin2 = makeNchAxis(nchWidthNchBin2); + } + + // ---- run type: MC is pinned to the reference binning, DATA is configurable ---- + // cfgEtaBinWidth exists only to vary the DATA measurement. MC (efficiency, + // closure) always uses 0.2-wide bins so its mean and fluctuation passes stay + // mutually consistent and reproducible. + const bool isMcRun = (cfgRunGetEff || cfgRunGetMCFlat || cfgRunMCMean || cfgRunMCFluc); + const bool isDataRun = (cfgRunGetDataFlat || cfgRunDataMean || cfgRunDataFluc); + if (isMcRun && isDataRun) { + LOGF(fatal, + "MC and DATA process switches are both enabled in one job: MC binning is " + "pinned to 0.2 while DATA uses cfgEtaBinWidth. Run them as separate jobs."); + } + const float mcEtaBinWidth = 0.2f; + const float effEtaBinWidth = isMcRun ? mcEtaBinWidth : static_cast(cfgEtaBinWidth); + + // ---- observable eta binning (width = effEtaBinWidth) -------------------------- + { + const float halfEta = cfgCutEta; + const float width = effEtaBinWidth; + if (width <= 0.f) { + LOGF(fatal, "eta bin width must be > 0 (got %.3f)", width); + } + const int nHalf = static_cast(std::lround(halfEta / width)); + if (nHalf < 1 || std::abs(nHalf * width - halfEta) > KEtaEdgeTolerance) { + LOGF(fatal, + "cfgCutEta=%.3f is not an integer multiple of the eta bin width=%.3f; " + "eta=0 must be a bin edge for mirror pairing.", + halfEta, width); + } + const int nbins = 2 * nHalf; // even -> symmetric about eta = 0 + const float lo = -halfEta; + const float hi = halfEta; + + etaLw.clear(); + etaUp.clear(); + etaLw.push_back(lo); // index 0: full-range reference bin + etaUp.push_back(hi); + for (int i = 0; i < nbins; ++i) { + etaLw.push_back(lo + i * width); + etaUp.push_back(lo + (i + 1) * width); + } + nEta = nbins + 1; + if (nEta > KNEtaMax) { + LOGF(fatal, "nEta=%d exceeds KNEtaMax=%d (raise the cap or widen the eta bin width)", nEta, KNEtaMax); } - } - - bool requiresMCMap = (cfgRunGetEff || cfgRunGetMCFlat || cfgRunMCMean || cfgRunMCFluc); - bool requiresDataMap = (cfgRunGetDataFlat || cfgRunDataMean || cfgRunDataFluc); - - if (requiresMCMap || requiresDataMap) { - std::string currentPath = requiresMCMap ? pathNsig : pathDataNsig; - TList* pidList = ccdb->getForTimeStamp(currentPath, now); - if (!pidList) { - LOGF(warn, "nSigma maps required but CCDB list is null at %s! Using raw values.", currentPath.c_str()); - } else { - if (!pidMeanSigmaMap) { - pidMeanSigmaMap = new PIDMeanSigmaMap(); - } - LOGF(info, "Performing 2D Gaussian fits on PID maps from CCDB..."); - auto loadPIDMeans = [&](PIDIdx pidType) { - std::string suffix = pidSuffix[pidType]; - std::string hName = "h3DnsigmaTpcVsTofBefCut_Cent" + suffix; - auto* h3 = reinterpret_cast(pidList->FindObject(hName.c_str())); - if (!h3) { - LOGF(warn, " [!] PID Hist %s not found in CCDB list.", hName.c_str()); - return; - } - int nCentBins = std::min(h3->GetXaxis()->GetNbins(), PIDMeanSigmaMap::MaxCentBins - 1); - LOGF(info, " -> Species: %s (Bins: %d)", hName.c_str(), nCentBins); - for (int iCent = 1; iCent <= nCentBins; ++iCent) { - h3->GetXaxis()->SetRange(iCent, iCent); - // Projecting: Z(TPC) vs Y(TOF). Result: X_axis=TOF, Y_axis=TPC - std::unique_ptr h2(reinterpret_cast(h3->Project3D("zy"))); - if (h2) { - int binX, binY, binZ; - h2->GetMaximumBin(binX, binY, binZ); - double guessMeanTOF = h2->GetXaxis()->GetBinCenter(binX); - double guessMeanTPC = h2->GetYaxis()->GetBinCenter(binY); - TF2 f2("f2", "[0]*TMath::Gaus(x,[1],[2])*TMath::Gaus(y,[3],[4])", -3, 3, -3, 3); - f2.SetParameters(h2->GetMaximum(), guessMeanTOF, 1.0, guessMeanTPC, 1.0); - h2->Fit(&f2, "QRN"); // Q=Quiet, R=Range, N=NoDraw - pidMeanSigmaMap->meanTOF[pidType][iCent - 1] = f2.GetParameter(1); - pidMeanSigmaMap->meanTPC[pidType][iCent - 1] = f2.GetParameter(3); - pidMeanSigmaMap->sigmaTOF[pidType][iCent - 1] = std::abs(f2.GetParameter(2)); - pidMeanSigmaMap->sigmaTPC[pidType][iCent - 1] = std::abs(f2.GetParameter(4)); - if (iCent % KConstTen == 0) { - LOGF(info, " Derived: For Species: %s (Bins: %d), Mean TOF = %.3f, Mean TPC = %.3f, Sigma TOF = %.3f, Sigma TPC = %.3f", hName.c_str(), iCent - 1, f2.GetParameter(1), f2.GetParameter(3), f2.GetParameter(2), f2.GetParameter(4)); - } - } - } - }; - for (int i = 1; i < KNsp; ++i) { - loadPIDMeans(static_cast(i)); + std::vector obsEdges; + obsEdges.reserve(nEta); + obsEdges.push_back(etaLw[1]); + for (int i = 1; i < nEta; ++i) { + obsEdges.push_back(etaUp[i]); + } + etaAxis = AxisSpec{obsEdges, "#eta"}; + etaBinAxis = AxisSpec{nEta + 1, -0.5, static_cast(nEta) + 0.5, "#eta bin Number"}; + // (Delta eta, Sigma eta): fills are integer multiples of w, so 2N-1 bins + // of width w centred on them, out to (N-1/2) w. + const int nGS = 2 * nbins - 1; + const double gsMax = (nbins - 0.5) * width; + gapAxis = AxisSpec{nGS, -gsMax, gsMax, "#Delta#eta"}; + sumAxis = AxisSpec{nGS, -gsMax, gsMax, "#Sigma#eta"}; + } + + // ---- forward bins: detector totals, then the eta sub-bins of each detector ---- + { + static const std::array detName = {"FT0A", "FT0C", "FV0A"}; + fwdEdges = {cfgFwdOpt.cfgFwdEtaEdgesFT0A.value, cfgFwdOpt.cfgFwdEtaEdgesFT0C.value, cfgFwdOpt.cfgFwdEtaEdgesFV0A.value}; + fwdLabels.assign(detName.begin(), detName.end()); + nFwd = kNFwdDet; + for (int det = 0; det < kNFwdDet; ++det) { + auto& e = fwdEdges[det]; + if (e.size() < 2 || !std::is_sorted(e.begin(), e.end())) { + LOGF(fatal, "%s eta edges must be >= 2 increasing values", detName[det]); + } + fwdOffset[det] = nFwd; + for (std::size_t i = 0; i + 1 < e.size(); ++i) { + fwdLabels.push_back(Form("%s %.1f<#eta<%.1f", detName[det], e[i], e[i + 1])); + ++nFwd; } + } + if (nFwd > KNFwdMax) { + LOGF(fatal, "%d forward bins exceed KNFwdMax=%d", nFwd, KNFwdMax); + } + fwdAxis = AxisSpec{nFwd, -0.5, nFwd - 0.5, "forward bin"}; + std::string list; + for (int k = 0; k < nFwd; ++k) { + list += Form("%s%d %s", k > 0 ? ", " : "", k, fwdLabels[k].c_str()); + } + LOGF(info, "RadialFlowDecorr forward bins: %s", list.c_str()); + } - auto loadLimits = [&](const char* name, std::vector>& limits, float& xMin, float& xMax) { - auto* h2 = reinterpret_cast(pidList->FindObject(name)); - if (!h2) - return; + // ---- variations ---- + const uint32_t active = (static_cast(cfgSyst.cfgSystMask.value) | 1) & KAllSystMask; + maskAll = maskEff = maskFlat = 0; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(active, v)) { + continue; + } + SETBIT(maskAll, v); + if (effSource(v) == v) { + SETBIT(maskEff, v); + } + if (flatSource(v) == v) { + SETBIT(maskFlat, v); + } + } + const bool isPP = (cfgSys == kpp); + itsLayerNom = cfgEvSel.cfgITSLayerCut >= 0 ? cfgEvSel.cfgITSLayerCut.value : (isPP ? kITSLayer0123 : kITSLayersAll); + if (itsLayerNom == kITSLayersAll) { + itsLayerVar = kITSLayer0123; + } else if (itsLayerNom == kITSLayer0123) { + itsLayerVar = kITSLayer3; + } else { + itsLayerVar = kITSLayersAll; + } + occupancyMax = (cfgSys == kPbPb) ? cfgSyst.cfgSystOccMaxPbPb.value : (isPP ? cfgSyst.cfgSystOccMaxPP.value : cfgSyst.cfgSystOccMaxOO.value); + pupNSigma = (cfgSys == kPbPb) ? cfgPileup.cfgPupnSigPbPb.value : (isPP ? cfgPileup.cfgPupnSigPP.value : cfgPileup.cfgPupnSigOO.value); + if (cfgEvSel.cfgCentralityChoice == KSystCentralityChoice && TESTBIT(maskAll, kSystCent)) { + LOGF(warning, "systCent uses the same centrality estimator as Base (%d).", cfgEvSel.cfgCentralityChoice.value); + } - std::unique_ptr prof(h2->ProfileX("ptmp", 1, -1, "S")); + // bootstrap: Base data fluctuation pass only + doBoot = cfgRunDataFluc && (cfgNBootstrap > 0); + nBoot = std::min(cfgNBootstrap, KMaxBoot); + if (cfgNBootstrap > KMaxBoot) { + LOGF(warning, "cfgNBootstrap=%d capped at %d", cfgNBootstrap.value, KMaxBoot); + } + rng.SetSeed(cfgBootstrapSeed); - int nBins = prof->GetNbinsX(); - xMin = prof->GetXaxis()->GetXmin(); - xMax = prof->GetXaxis()->GetXmax(); + // ---- CCDB ---- + ccdb->setURL(cfgCCDBurl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + // a missing object returns nullptr: every lookup is checked (a missing variation input switches + // the variation off, a missing Base input or alignment is fatal, a missing gain gives unit gains) + ccdb->setFatalWhenNull(false); + fwdCalib.fv0Det = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); - limits.assign(nBins + 2, {-99999.f, 999999.f}); + std::string sysDir; + switch (cfgSys) { + case kPbPb: + sysDir = "PbPbTest"; + break; + case kNeNe: + sysDir = "NeNeTest"; + break; + case kOO: + sysDir = "OOTest"; + break; + case kpp: + sysDir = "ppTest"; + break; + default: + LOGF(fatal, "Invalid cfgSys value: %d", cfgSys.value); + } + const std::string pathEff = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_EffMaps"; + const std::string pathMCFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_MCFlatMaps"; + const std::string pathMCMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_MCMean"; + const std::string pathDataFlat = cfgCCDBUserPath.value + "/" + sysDir + "/Job1_DataFlatMaps"; + const std::string pathDataMean = cfgCCDBUserPath.value + "/" + sysDir + "/Job2_DataMean"; - for (int i = 1; i <= nBins; ++i) { - float mean = prof->GetBinContent(i); - float rms = prof->GetBinError(i); + // ---- inputs of each variation; a variation without its inputs is switched off ---- + // variations used by the enabled passes + uint32_t used = 0; + if (cfgRunGetEff) { + used |= maskEff; + } + if (cfgRunGetMCFlat || cfgRunGetDataFlat) { + used |= maskFlat; + } + if (cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc) { + used |= maskAll; + } - limits[i] = {mean - cfgPupnSig * rms, mean + cfgPupnSig * rms}; - } - }; - loadLimits("Hist2D_globalTracks_cent", state.mLimitsNchCent, state.mMinXNchCent, state.mMaxXNchCent); + // efficiency/fake maps + if (cfgEff && (cfgRunGetMCFlat || cfgRunMCMean || cfgRunMCFluc || isDataRun)) { + std::array loaded{}; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(used, v)) { + continue; + } + const int s = effSource(v); + if (loaded[s] == 0) { + loaded[s] = loadEffMaps(s, pathEff + systSuffix(s), now) ? 1 : -1; + } + if (loaded[s] < 0) { + dropSyst(v, "no efficiency maps at " + pathEff + systSuffix(s)); + continue; + } + inputs[v].hEff = inputs[s].hEff; + inputs[v].hFake = inputs[s].hFake; + inputs[v].hEffPtEta = inputs[s].hEffPtEta; + inputs[v].hFakePtEta = inputs[s].hFakePtEta; } } - if (!cfgRunGetEff && (cfgFlat)) { - if (cfgRunDataMean || cfgRunDataFluc) { - LOGF(info, "Data Run: Loading flattening maps from %s", pathDataFlat.c_str()); - TList* lstDataFlat = ccdb->getForTimeStamp(pathDataFlat, now); - - if (lstDataFlat) { - for (int i = 0; i < KNsp; ++i) { - std::string suffix = pidSuffix[i]; - std::string hName; - - if (cfgEff && cfgFlat) { - hName = "hEtaPhiRecoWtd" + suffix; - } else if (cfgEff) { - hName = "hEtaPhiRecoEffWtd" + suffix; - } else { - hName = "hEtaPhiReco" + suffix; - } - auto* hRaw = reinterpret_cast(lstDataFlat->FindObject(hName.c_str())); - if (hRaw) { - state.hFlatWeight[i] = buildWeightMapFromRaw(hRaw, Form("hFlatWeight%s", suffix.c_str())); - } else { - LOGF(error, "Data flattening map '%s' not found.", hName.c_str()); - } - } - } else { - LOGF(error, "Could not retrieve Data Flattening TList from: %s", pathDataFlat.c_str()); + // flattening maps + if (cfgFlat && (cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc)) { + const bool mc = cfgRunMCMean || cfgRunMCFluc; + const std::string& path = mc ? pathMCFlat : pathDataFlat; + const std::string dir = mc ? "MCReco/" : ""; + std::array loaded{}; + for (int v = 0; v < kNSyst; ++v) { + const int s = flatSource(v); + if (!TESTBIT(maskAll, v) || s < 0) { + continue; } + if (loaded[s] == 0) { + inputs[s].hFlat = loadFlatMap(s, path + systSuffix(s), dir, now); + loaded[s] = inputs[s].hFlat ? 1 : -1; + } + if (loaded[s] < 0) { + dropSyst(v, "no flattening map at " + path + systSuffix(s)); + continue; + } + inputs[v].hFlat = inputs[s].hFlat; } + } - if (cfgRunMCMean || cfgRunMCFluc) { - LOGF(info, "MC Run: Loading flattening maps from %s", pathMCFlat.c_str()); - TList* lstMCFlat = ccdb->getForTimeStamp(pathMCFlat, now); - - if (lstMCFlat) { - auto loadFlatForPID = [&](PIDIdx pidType) { - std::string suffix = pidSuffix[pidType]; - std::string hFlatSrcName; - if (cfgEff && cfgFlat) { - hFlatSrcName = "MCReco/hEtaPhiRecoWtd" + suffix; - } else if (cfgEff) { - hFlatSrcName = "MCReco/hEtaPhiRecoEffWtd" + suffix; - } else { - hFlatSrcName = "MCReco/hEtaPhiReco" + suffix; - } - - auto* hRaw = reinterpret_cast(lstMCFlat->FindObject(hFlatSrcName.c_str())); - - if (hRaw) { - state.hFlatWeight[pidType] = buildWeightMapFromRaw(hRaw, Form("hFlatWeight%s", suffix.c_str())); - } else { - LOGF(warning, "MC flattening source '%s' not found in list.", hFlatSrcName.c_str()); + // ---- n-sigma pile-up bands, from the Base flattening output of the same production ---- + // The flattening and efficiency passes only apply the linear cut: their maps define the bands. + { + const bool meanOrFluc = cfgRunMCMean || cfgRunMCFluc || cfgRunDataMean || cfgRunDataFluc; + const bool wantCent = cfgPileup.cfgApplySigPupCut && meanOrFluc; + const bool wantPV = cfgPileup.cfgApplySigPupCutPV && meanOrFluc; + if ((wantCent || wantPV) && (cfgRunGetEff || cfgRunGetMCFlat || cfgRunGetDataFlat)) { + LOGF(fatal, "The n-sigma pile-up bands come from the flattening pass: run it in a separate job before the mean/fluctuation passes."); + } + if (wantCent || wantPV) { + const std::string& bandPath = isMcRun ? pathMCFlat : pathDataFlat; + auto* bandList = ccdb->getForTimeStamp(bandPath, now); + if (!bandList) { + LOGF(fatal, "n-sigma pile-up bands requested but no flattening output at %s", bandPath.c_str()); + } + auto makeBand = [&](const char* name, NSigmaBand& band) { + auto* h2 = dynamic_cast(bandList->FindObject(name)); + if (!h2) { + LOGF(fatal, "n-sigma pile-up band: %s missing in %s", name, bandPath.c_str()); + return; + } + std::unique_ptr prof(h2->ProfileX(Form("%s_band", name), 1, -1, "S")); + const int nBins = prof->GetNbinsX(); + band.xMin = prof->GetXaxis()->GetXmin(); + band.xMax = prof->GetXaxis()->GetXmax(); + band.meanRms.assign(nBins + 2, {0.f, -1.f}); + for (int i = 1; i <= nBins; ++i) { + // a sparse bin (high-N_PV tail, peripheral edge) gets no band: its RMS is not measured + if (prof->GetBinEntries(i) >= KMinBandEntries) { + band.meanRms[i] = {static_cast(prof->GetBinContent(i)), static_cast(prof->GetBinError(i))}; } - }; - - for (int i = 0; i < KNsp; ++i) { - loadFlatForPID(static_cast(i)); } - } else { - LOGF(error, "Could not retrieve MC Flattening TList from: %s", pathMCFlat.c_str()); + band.applied = true; + LOGF(info, "n-sigma pile-up band (%.1f sigma, PileupNSigma %.1f) from %s in %s", pupNSigma, + pupNSigma - KSystPupnSigStep, name, bandPath.c_str()); + }; + if (wantCent) { + makeBand("Hist2D_globalTracks_cent", pileupBands[kBandCent]); + } + if (wantPV) { + makeBand("Hist2D_globalTracks_PVTracks", pileupBands[kBandPV]); } } } - auto loadTProfile3DFromList = [&](TList* sourceList, const char* objName, TProfile3D*& target) { - if (!sourceList) - return; - auto* tp = reinterpret_cast(sourceList->FindObject(objName)); - if (tp) { - target = reinterpret_cast(tp->Clone()); - target->SetDirectory(nullptr); - LOGF(info, "Loaded %s from list", objName); - } else { - LOGF(error, "Histogram %s missing in CCDB TList", objName); - } - }; - - auto loadTProfileFromList = [&](TList* sourceList, const char* objName, TProfile*& target) { - if (!sourceList) - return; - auto* tp = reinterpret_cast(sourceList->FindObject(objName)); - if (tp) { - target = reinterpret_cast(tp->Clone()); - target->SetDirectory(nullptr); - LOGF(info, "Loaded %s from list", objName); - } else { - LOGF(error, "Histogram %s missing in CCDB TList", objName); + // ---- mean profiles for the fluctuation passes (one per variation) ---- + if (cfgRunMCFluc || cfgRunDataFluc) { + const std::string& path = cfgRunMCFluc ? pathMCMean : pathDataMean; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(maskAll, v)) { + LOGF(info, "Loading mean profiles of %s from %s", systName[v].c_str(), (path + systSuffix(v)).c_str()); + loadMeans(v, path + systSuffix(v), cfgRunMCFluc, now); + } } - }; + } + // ---- declarations ---- + declareCommonQA(); + if (isMcRun) { + declareMCVtxQA(); + } + declareCounts(cfgRunGetEff ? maskEff : ((cfgRunGetMCFlat || cfgRunGetDataFlat) ? maskFlat : maskAll)); + if (doprocessCutQA) { + declareCutQA(); + } + if (cfgRunGetEff) { + declareMCGetEffHists(); + } + if (cfgRunGetMCFlat) { + declareMCGetFlatHists(); + } + if (cfgRunMCMean) { + declareMCMeanHists(); + } if (cfgRunMCFluc) { - LOGF(info, "Loading MC Mean profiles from CCDB path: %s", pathMCMean.c_str()); - TList* lstMCMean = ccdb->getForTimeStamp(pathMCMean, now); - - if (lstMCMean) { - loadTProfileFromList(lstMCMean, "pmeanFT0Amultpv", state.pmeanFT0AmultpvStep2); - loadTProfileFromList(lstMCMean, "pmeanFT0Cmultpv", state.pmeanFT0CmultpvStep2); - - loadTProfile3DFromList(lstMCMean, "pmeanTru_nch_etabin_spbin", state.pmeanTruNchEtabinSpbinStep2); - loadTProfile3DFromList(lstMCMean, "pmeanReco_nch_etabin_spbin", state.pmeanRecoNchEtabinSpbinStep2); - loadTProfile3DFromList(lstMCMean, "pmeanRecoEffcorr_nch_etabin_spbin", state.pmeanRecoEffcorrNchEtabinSpbinStep2); - - loadTProfile3DFromList(lstMCMean, "pmeanMultTru_nch_etabin_spbin", state.pmeanMultTruNchEtabinSpbinStep2); - loadTProfile3DFromList(lstMCMean, "pmeanMultReco_nch_etabin_spbin", state.pmeanMultRecoNchEtabinSpbinStep2); - loadTProfile3DFromList(lstMCMean, "pmeanMultRecoEffcorr_nch_etabin_spbin", state.pmeanMultRecoEffcorrNchEtabinSpbinStep2); - } else { - LOGF(error, "Could not retrieve TList for MC Mean from: %s", pathMCMean.c_str()); - } + declareMCFlucHists(); + } + if (cfgRunGetDataFlat) { + declareDataGetFlatHists(); + } + if (cfgRunDataMean) { + declareDataMeanHists(); } - if (cfgRunDataFluc) { - LOGF(info, "Loading Data Mean profiles from CCDB path: %s", pathDataMean.c_str()); - TList* lstDataMean = ccdb->getForTimeStamp(pathDataMean, now); - - if (lstDataMean) { - loadTProfileFromList(lstDataMean, "pmeanFT0Amultpv", state.pmeanFT0AmultpvStep2); - loadTProfileFromList(lstDataMean, "pmeanFT0Cmultpv", state.pmeanFT0CmultpvStep2); + declareDataFlucHists(); + } - loadTProfile3DFromList(lstDataMean, "pmean_nch_etabin_spbin", state.pmeanNchEtabinSpbinStep2); - loadTProfile3DFromList(lstDataMean, "pmeanMult_nch_etabin_spbin", state.pmeanMultNchEtabinSpbinStep2); - } else { - LOGF(error, "Could not retrieve TList for Data Mean from: %s", pathDataMean.c_str()); + std::string list; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(used & maskAll, v) || (cfgRunGetEff && TESTBIT(maskEff, v)) || ((cfgRunGetMCFlat || cfgRunGetDataFlat) && TESTBIT(maskFlat, v))) { + list += " " + systName[v]; } } + LOGF(info, "RadialFlowDecorr variations in this job:%s (ITS layer cut nominal %d, variation %d; N_ch bins of %d, NchBin2 %d; occupancy cut %d)", + list.c_str(), itsLayerNom, itsLayerVar, nchWidth, nchWidthNchBin2, occupancyMax); LOGF(info, "CCDB initialization complete for RadialFlowDecorr."); } - void processMCGetMeanNsig(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks) + // =========================================================================== + // Helpers of the process functions + // =========================================================================== + // (vz, sign, pT, eta, phi) map of variation v: raw, with all weights w and, for Base, with + // the efficiency weight wEff = (1 - fake) / eff. + void fillFlat(int v, float vz, float sign, float pt, float eta, float phi, float w, float wEff) { - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) - return; - float cent = getCentrality(mcCollision); - if (cent > KCentMax) + const auto& h = hists.flat[v].maps; + if (!h) { return; - float multPV = mcCollision.multNTracksPV(); - - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) - continue; - - if (!isTrackSelected(track)) - continue; - fillNSigmaBefCut(track, cent); + } + std::array x = {vz, sign, pt, eta, phi, static_cast(kFlatRaw)}; + h->Fill(x.data(), 1.); + x[5] = kFlatWtd; + h->Fill(x.data(), w); + if (v == kSystBase) { + x[5] = kFlatEffWtd; + h->Fill(x.data(), wEff); } } - PROCESS_SWITCH(RadialFlowDecorr, processMCGetMeanNsig, "process MC to calculate Mean values of nSig Plots", cfgRunMCGetNSig); - void processGetEffHists(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + // The reco collision with the most PV contributors; with split vertices the others are + // skipped, so the truth of an MC collision enters once. Fills the vertex QA. + template + int64_t bestRecoCollision(const R& recoCollisions, float mcVz) { - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) - return; - float cent = getCentrality(mcCollision); - if (cent > KCentMax) - return; - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) - return; - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) - continue; + int64_t best = -1; + int maxContrib = -1; + int nReco = 0; + for (const auto& rc : recoCollisions) { + ++nReco; + if (rc.numContrib() > maxContrib) { + maxContrib = rc.numContrib(); + best = rc.globalIndex(); + } + } + for (const auto& rc : recoCollisions) { + const int type = (nReco == 1) ? kVtxSingle : (rc.globalIndex() == best ? kVtxSplitBest : kVtxSplitOther); + hists.mcVtx->Fill(rc.posZ() - mcVz, type); + } + return best; + } - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) + // =========================================================================== + // MC: build efficiency / fake maps (inclusive) + // =========================================================================== + void processGetEffHists(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { continue; + } + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskEff); + if (ev.mask == 0) { + return; + } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + const float cent = ev.cent[kSystBase]; + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } + } - const int pdg = particle.pdgCode(); - const int absPdg = std::abs(pdg); - float pt = particle.pt(), eta = particle.eta(); - - bool isSpecies[KNsp] = { - true, // kInclusiveIdx - pdg == -KPiPlus, // kPiMinusIdx - pdg == KPiPlus, // kPiPlusIdx - absPdg == KPiPlus, // kPiAllIdx - pdg == -KKPlus, // kKaMinusIdx - pdg == KKPlus, // kKaPlusIdx - absPdg == KKPlus, // kKaAllIdx - pdg == -KProton, // kAntiPrIdx - pdg == KProton, // kPrIdx - absPdg == KProton // kPrAllIdx + // one entry in the efficiency map of category cat of every variation in mask + auto fillEffMaps = [&](uint32_t mask, float pt, float eta, int cat) { + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(mask, v)) { + hists.eff[v].maps[cat]->Fill(cent, pt, eta); + } + } }; - histos.fill(HIST("h3_AllPrimary"), multPV, pt, eta); - if (isSpecies[kPiMinusIdx]) - histos.fill(HIST("h3_AllPrimary_PiMinus"), multPV, pt, eta); - else if (isSpecies[kPiPlusIdx]) - histos.fill(HIST("h3_AllPrimary_PiPlus"), multPV, pt, eta); - if (isSpecies[kPiAllIdx]) - histos.fill(HIST("h3_AllPrimary_PiAll"), multPV, pt, eta); - - if (isSpecies[kKaMinusIdx]) - histos.fill(HIST("h3_AllPrimary_KaMinus"), multPV, pt, eta); - else if (isSpecies[kKaPlusIdx]) - histos.fill(HIST("h3_AllPrimary_KaPlus"), multPV, pt, eta); - if (isSpecies[kKaAllIdx]) - histos.fill(HIST("h3_AllPrimary_KaAll"), multPV, pt, eta); - - if (isSpecies[kAntiPrIdx]) - histos.fill(HIST("h3_AllPrimary_AntiPr"), multPV, pt, eta); - else if (isSpecies[kPrIdx]) - histos.fill(HIST("h3_AllPrimary_Pr"), multPV, pt, eta); - if (isSpecies[kPrAllIdx]) - histos.fill(HIST("h3_AllPrimary_PrAll"), multPV, pt, eta); - } - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) - continue; - - if (!isTrackSelected(track)) - continue; - - float pt = track.pt(), eta = track.eta(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - auto sign = track.sign(); - fillNSigmaBefCut(track, cent); - - int id = identifyTrack(track, cent); - bool isPi = (id == KPidPionOne); - bool isKa = (id == KPidKaonTwo); - bool isPr = (id == KPidProtonThree); - bool isSpecies[KNsp] = { - true, - isPi && sign < 0, isPi && sign > 0, isPi, - isKa && sign < 0, isKa && sign > 0, isKa, - isPr && sign < 0, isPr && sign > 0, isPr}; - - fillNSigmaAftCut(track, cent, isSpecies); - - for (int isp = 0; isp < KNsp; ++isp) { - if (!isSpecies[isp]) + // --- truth: charged primaries of the MC collision --- + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { continue; + } + fillEffMaps(ev.mask, particle.pt(), particle.eta(), kEffAllPrim); + } - if (isp == kInclusiveIdx) { - histos.fill(HIST("h3_AllReco"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - histos.fill(HIST("ptResolution"), mcP.pt(), (pt - mcP.pt()) / mcP.pt()); - histos.fill(HIST("etaResolution"), mcP.eta(), eta - mcP.eta()); - histos.fill(HIST("etaTruthReco"), mcP.eta(), eta); - histos.fill(HIST("vzResolution"), mcP.vz(), (vz - mcP.vz()) / mcP.vz()); - histos.fill(HIST("TruthTracKVz"), mcP.vz(), vz); - histos.fill(HIST("h3_RecoMatchedToPrimary"), multPV, mcP.pt(), mcP.eta()); - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake"), multPV, pt, eta); - } - } else if (isp == kPiMinusIdx) { - histos.fill(HIST("h3_AllReco_PiMinus"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (mcP.pdgCode() == -KPiPlus) { - histos.fill(HIST("h3_RecoMatchedToPrimary_PiMinus"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_PiMinus"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_PiMinus"), multPV, pt, eta); - } - } else { // No MC - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_PiMinus"), multPV, pt, eta); - } - } else if (isp == kPiPlusIdx) { - histos.fill(HIST("h3_AllReco_PiPlus"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (mcP.pdgCode() == KPiPlus) { - histos.fill(HIST("h3_RecoMatchedToPrimary_PiPlus"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_PiPlus"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_PiPlus"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_PiPlus"), multPV, pt, eta); - } - } else if (isp == kPiAllIdx) { - histos.fill(HIST("h3_AllReco_PiAll"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (std::abs(mcP.pdgCode()) == KPiPlus) { - histos.fill(HIST("h3_RecoMatchedToPrimary_PiAll"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_PiAll"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_PiAll"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_PiAll"), multPV, pt, eta); - } - } else if (isp == kKaMinusIdx) { - histos.fill(HIST("h3_AllReco_KaMinus"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (mcP.pdgCode() == -KKPlus) { - histos.fill(HIST("h3_RecoMatchedToPrimary_KaMinus"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_KaMinus"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_KaMinus"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_KaMinus"), multPV, pt, eta); - } - } else if (isp == kKaPlusIdx) { - histos.fill(HIST("h3_AllReco_KaPlus"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (mcP.pdgCode() == KKPlus) { - histos.fill(HIST("h3_RecoMatchedToPrimary_KaPlus"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_KaPlus"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_KaPlus"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_KaPlus"), multPV, pt, eta); - } - } else if (isp == kKaAllIdx) { - histos.fill(HIST("h3_AllReco_KaAll"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (std::abs(mcP.pdgCode()) == KKPlus) { - histos.fill(HIST("h3_RecoMatchedToPrimary_KaAll"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_KaAll"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_KaAll"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_KaAll"), multPV, pt, eta); - } - } else if (isp == kAntiPrIdx) { - histos.fill(HIST("h3_AllReco_AntiPr"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (mcP.pdgCode() == -KProton) { - histos.fill(HIST("h3_RecoMatchedToPrimary_AntiPr"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_AntiPr"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_AntiPr"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_AntiPr"), multPV, pt, eta); - } - } else if (isp == kPrIdx) { - histos.fill(HIST("h3_AllReco_Pr"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (mcP.pdgCode() == KProton) { - histos.fill(HIST("h3_RecoMatchedToPrimary_Pr"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_Pr"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_Pr"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_Pr"), multPV, pt, eta); - } - } else if (isp == kPrAllIdx) { - histos.fill(HIST("h3_AllReco_PrAll"), multPV, pt, eta); - if (track.has_mcParticle()) { - auto mcP = track.mcParticle(); - if (mcP.isPhysicalPrimary()) { - if (std::abs(mcP.pdgCode()) == KProton) { - histos.fill(HIST("h3_RecoMatchedToPrimary_PrAll"), multPV, mcP.pt(), mcP.eta()); - } else { // Misidentified - histos.fill(HIST("h3_RecoMatchedToPrimary_MisID_PrAll"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Secondary_PrAll"), multPV, pt, eta); - } - } else { - histos.fill(HIST("h3_RecoUnMatchedToPrimary_Fake_PrAll"), multPV, pt, eta); + // --- reco --- + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; + } + float pt = track.pt(), eta = track.eta(); + const bool isBase = TESTBIT(trackMask, kSystBase); + const int origin = trackOrigin(track, coll); + if (isBase) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + hists.mcTrackOrigin->Fill(nCh, pt, origin); + } + fillEffMaps(trackMask, pt, eta, kEffAllReco); + if (origin == kOriginPrimary) { + const auto& mcP = track.mcParticle(); + if (isBase) { + histos.fill(HIST("ptResolution"), mcP.pt(), (pt - mcP.pt()) / mcP.pt()); + histos.fill(HIST("etaResolution"), mcP.eta(), eta - mcP.eta()); + histos.fill(HIST("etaTruthReco"), mcP.eta(), eta); + histos.fill(HIST("vzResolution"), mcP.vz(), (vz - mcP.vz()) / mcP.vz()); + histos.fill(HIST("TruthTracKVz"), mcP.vz(), vz); } + fillEffMaps(trackMask, mcP.pt(), mcP.eta(), kEffRecoMatchedPrim); + } else if (origin == kOriginSecondary) { + fillEffMaps(trackMask, pt, eta, kEffRecoSec); + } else if (origin == kOriginOtherColl) { + fillEffMaps(trackMask, pt, eta, kEffRecoOtherColl); + } else { + fillEffMaps(trackMask, pt, eta, kEffRecoFake); } } + flushTrackCounts(); } } PROCESS_SWITCH(RadialFlowDecorr, processGetEffHists, "process MC to calculate EffWeights", cfgRunGetEff); - void processMCFlat(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks) + // =========================================================================== + // MC: build flattening maps (inclusive) + // =========================================================================== + void processMCFlat(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, FilteredTCs const& mcTracks) { - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) - return; - - float cent = getCentrality(mcCollision); - if (cent > KCentMax) - return; - - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) - return; - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), mcCollision.multNTracksPV(), mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) - continue; - - if (!isTrackSelected(track)) + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { continue; + } + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskFlat); + if (ev.mask == 0) { + return; + } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } + } - float pt = track.pt(), eta = track.eta(), phi = track.phi(); - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - int id = identifyTrack(track, cent); - bool isPi = (id == KPidPionOne); - bool isKa = (id == KPidKaonTwo); - bool isPr = (id == KPidProtonThree); - bool isSpecies[KNsp] = { - true, - isPi && sign < 0, isPi && sign > 0, isPi, - isKa && sign < 0, isKa && sign > 0, isKa, - isPr && sign < 0, isPr && sign > 0, isPr}; - - for (int isp = 0; isp < KNsp; ++isp) { - if (!isSpecies[isp]) + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; + } + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - float eff = getEfficiency(multPV, pt, eta, static_cast(isp), 0, cfgEff); - float fake = getEfficiency(multPV, pt, eta, static_cast(isp), 1, cfgEff); - float w = (eff > KFloatEpsilon) ? (1.0f - fake) / eff : 0.0f; - - if (std::isfinite(w) && w > 0.f) { - if (isp == kInclusiveIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiMinusIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_PiMinus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_PiMinus"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_PiMinus"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiPlusIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_PiPlus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_PiPlus"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_PiPlus"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiAllIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_PiAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_PiAll"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_PiAll"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaMinusIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_KaMinus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_KaMinus"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_KaMinus"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaPlusIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_KaPlus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_KaPlus"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_KaPlus"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaAllIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_KaAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_KaAll"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_KaAll"), vz, sign, pt, eta, phi, w); - } else if (isp == kAntiPrIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_AntiPr"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_AntiPr"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_AntiPr"), vz, sign, pt, eta, phi, w); - } else if (isp == kPrIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_Pr"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_Pr"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_Pr"), vz, sign, pt, eta, phi, w); - } else if (isp == kPrAllIdx) { - histos.fill(HIST("MCReco/hEtaPhiRecoEffWtd_PrAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("MCReco/hEtaPhiReco_PrAll"), vz, sign, pt, eta, phi, 1.0); - histos.fill(HIST("MCReco/hEtaPhiRecoWtd_PrAll"), vz, sign, pt, eta, phi, w); + // efficiency weights only: the flattening map is what this pass measures + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, false, w, eff, fake, flatWeight); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + fillFlat(v, vz, sign, pt, eta, phi, w[v], w[v]); } } } + flushTrackCounts(); } } PROCESS_SWITCH(RadialFlowDecorr, processMCFlat, "process MC to calculate FlatWeights", cfgRunGetMCFlat); - void processMCMean(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::FT0s const&, aod::McParticles const& mcParticles) + // =========================================================================== + // MC: mean pT (truth / reco / reco-eff-corrected) + // =========================================================================== + void processMCMean(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) { - double sumWiTruth[KNsp][KNEta]{}, sumWiptiTruth[KNsp][KNEta]{}; - double sumWiReco[KNsp][KNEta]{}, sumWiptiReco[KNsp][KNEta]{}; - double sumWiRecoEffCorr[KNsp][KNEta]{}, sumWiptiRecoEffCorr[KNsp][KNEta]{}; - histos.fill(HIST("hVtxZ"), mcCollision.posZ()); - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) - return; - float cent = getCentrality(mcCollision); - if (cent > KCentMax) - return; - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) - return; - - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), mcCollision.multNTracksPV(), mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - memset(sumWiTruth, 0, sizeof(sumWiTruth)); - memset(sumWiptiTruth, 0, sizeof(sumWiptiTruth)); - memset(sumWiReco, 0, sizeof(sumWiReco)); - memset(sumWiptiReco, 0, sizeof(sumWiptiReco)); - memset(sumWiRecoEffCorr, 0, sizeof(sumWiRecoEffCorr)); - memset(sumWiptiRecoEffCorr, 0, sizeof(sumWiptiRecoEffCorr)); - - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) - continue; - - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) - continue; - float pt = particle.pt(), eta = particle.eta(); - if (pt <= cfgPtMin || pt > cfgPtMax) + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { continue; - int pdgCode = particle.pdgCode(); - int absPdg = std::abs(pdgCode); - - bool isSpecies[KNsp] = { - true, // kInclusiveIdx - pdgCode == -KPiPlus, // kPiMinusIdx - pdgCode == KPiPlus, // kPiPlusIdx - absPdg == KPiPlus, // kPiAllIdx - pdgCode == -KKPlus, // kKaMinusIdx - pdgCode == KKPlus, // kKaPlusIdx - absPdg == KKPlus, // kKaAllIdx - pdgCode == -KProton, // kAntiPrIdx - pdgCode == KProton, // kPrIdx - absPdg == KProton // kPrAllIdx - }; - - for (int ieta = 0; ieta < KNEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) - continue; + } + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { + return; + } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } + } - for (int isp = 0; isp < KNsp; ++isp) { - if (isSpecies[isp]) { - sumWiTruth[isp][ieta]++; - sumWiptiTruth[isp][ieta] += pt; + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + for (auto& s : sums[v]) { + s.reset(); } } } - } - for (int isp = 0; isp < KNsp; ++isp) { - histos.fill(HIST("MCGen/Prof_Cent_Nsp_Nchrec"), cent, isp, sumWiTruth[isp][0]); - histos.fill(HIST("MCGen/Prof_Mult_Nsp_Nchrec"), multPV, isp, sumWiTruth[isp][0]); - if (sumWiTruth[isp][0] > 1.0f) { - histos.fill(HIST("MCGen/Prof_Cent_Nsp_MeanpT"), cent, isp, sumWiptiTruth[isp][0] / sumWiTruth[isp][0]); - histos.fill(HIST("MCGen/Prof_Mult_Nsp_MeanpT"), multPV, isp, sumWiptiTruth[isp][0] / sumWiTruth[isp][0]); + // --- truth: independent of the track cuts, copied to every selected variation --- + PowerSums& sumsTru = sums[kSystBase][kGen]; + if (!TESTBIT(ev.mask, kSystBase)) { + sumsTru.reset(); } - } - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) - continue; - - if (!isTrackSelected(track)) - continue; - float pt = track.pt(), eta = track.eta(), phi = track.phi(); - if (pt <= cfgPtMin || pt > cfgPtMax) - continue; - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - int id = identifyTrack(track, cent); - bool isPi = (id == KPidPionOne); - bool isKa = (id == KPidKaonTwo); - bool isPr = (id == KPidProtonThree); - bool isSpecies[KNsp] = { - true, - isPi && sign < 0, isPi && sign > 0, isPi, - isKa && sign < 0, isKa && sign > 0, isKa, - isPr && sign < 0, isPr && sign > 0, isPr}; - - for (int isp = 0; isp < KNsp; ++isp) { - if (!isSpecies[isp]) + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { continue; - float eff = getEfficiency(multPV, pt, eta, static_cast(isp), 0, cfgEff); - float fake = getEfficiency(multPV, pt, eta, static_cast(isp), 1, cfgEff); - float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, static_cast(isp), cfgFlat); - float w = flatW * (1.0 - fake) / eff; - if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) + } + float pt = particle.pt(), eta = particle.eta(); + if (pt <= cfgPtMin || pt > cfgPtMax) { continue; - - for (int ieta = 0; ieta < KNEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) + } + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; - sumWiReco[isp][ieta]++; - sumWiptiReco[isp][ieta] += pt; - sumWiRecoEffCorr[isp][ieta] += w; - sumWiptiRecoEffCorr[isp][ieta] += w * pt; - } - - if (isp == kInclusiveIdx) { - histos.fill(HIST("Eff_cent"), cent, eff); - histos.fill(HIST("Fake_cent"), cent, fake); - histos.fill(HIST("wgt_cent"), cent, w); - - histos.fill(HIST("Eff_Ntrk"), multPV, eff); - histos.fill(HIST("Fake_Ntrk"), multPV, fake); - histos.fill(HIST("wgt_Ntrk"), multPV, w); - - histos.fill(HIST("Eff_pT"), pt, eff); - histos.fill(HIST("Fake_pT"), pt, fake); - histos.fill(HIST("wgt_pT"), pt, w); - - histos.fill(HIST("Eff_eta"), eta, eff); - histos.fill(HIST("Fake_eta"), eta, fake); - histos.fill(HIST("wgt_eta"), eta, w); - } - if (isp == kInclusiveIdx) { - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPiMinusIdx) { - histos.fill(HIST("hEtaPhiReco_PiMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PiMinus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiMinus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPiPlusIdx) { - histos.fill(HIST("hEtaPhiReco_PiPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PiPlus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiPlus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPiAllIdx) { - histos.fill(HIST("hEtaPhiReco_PiAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PiAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiAll"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kKaMinusIdx) { - histos.fill(HIST("hEtaPhiReco_KaMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_KaMinus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaMinus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kKaPlusIdx) { - histos.fill(HIST("hEtaPhiReco_KaPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_KaPlus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaPlus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kKaAllIdx) { - histos.fill(HIST("hEtaPhiReco_KaAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_KaAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaAll"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPrIdx) { - histos.fill(HIST("hEtaPhiReco_Pr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_Pr"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_Pr"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kAntiPrIdx) { - histos.fill(HIST("hEtaPhiReco_AntiPr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_AntiPr"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_AntiPr"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPrAllIdx) { - histos.fill(HIST("hEtaPhiReco_PrAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PrAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PrAll"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } - } - } - - for (int isp = 0; isp < KNsp; ++isp) { - histos.fill(HIST("MCReco/Prof_Cent_Nsp_Nchrec"), cent, isp, sumWiReco[isp][0]); - histos.fill(HIST("MCReco/Prof_Mult_Nsp_Nchrec"), multPV, isp, sumWiReco[isp][0]); - - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_Nsp_Nchrec"), cent, isp, sumWiRecoEffCorr[isp][0]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_Nsp_Nchrec"), multPV, isp, sumWiRecoEffCorr[isp][0]); - - if (sumWiReco[isp][0] > 1.0f) { - histos.fill(HIST("MCReco/Prof_Cent_Nsp_MeanpT"), cent, isp, sumWiptiReco[isp][0] / sumWiReco[isp][0]); - histos.fill(HIST("MCReco/Prof_Mult_Nsp_MeanpT"), multPV, isp, sumWiptiReco[isp][0] / sumWiReco[isp][0]); - } - if (sumWiRecoEffCorr[isp][0] > 1.0f) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_Nsp_MeanpT"), cent, isp, sumWiptiRecoEffCorr[isp][0] / sumWiRecoEffCorr[isp][0]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_Nsp_MeanpT"), multPV, isp, sumWiptiRecoEffCorr[isp][0] / sumWiRecoEffCorr[isp][0]); - } - } - - for (int ietaA = 0; ietaA < KNEta; ++ietaA) { - for (int ietaC = 0; ietaC < KNEta; ++ietaC) { - for (int isp = 0; isp < KNsp; ++isp) { - float nTruAB = sumWiTruth[isp][ietaA] + sumWiTruth[isp][ietaC]; - float nRecoAB = sumWiReco[isp][ietaA] + sumWiReco[isp][ietaC]; - float nCorrAB = sumWiRecoEffCorr[isp][ietaA] + sumWiRecoEffCorr[isp][ietaC]; - - float mptsubTru = (sumWiptiTruth[isp][ietaA] + sumWiptiTruth[isp][ietaC]) / nTruAB; - float mptsubReco = (sumWiptiReco[isp][ietaA] + sumWiptiReco[isp][ietaC]) / nRecoAB; - float mptsubRecoEffCorr = (sumWiptiRecoEffCorr[isp][ietaA] + sumWiptiRecoEffCorr[isp][ietaC]) / nCorrAB; - - if (nTruAB > 0) { - if (isp == kInclusiveIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kPiMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_PiMinus"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kPiPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_PiPlus"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kPiAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_PiAll"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kKaMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_KaMinus"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kKaPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_KaPlus"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kKaAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_KaAll"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_Pr"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kAntiPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_AntiPr"), cent, ietaA, ietaC, mptsubTru); - else if (isp == kPrAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Tru_PrAll"), cent, ietaA, ietaC, mptsubTru); - } - - if (nRecoAB > 0) { - if (isp == kInclusiveIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kPiMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_PiMinus"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kPiPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_PiPlus"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kPiAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_PiAll"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kKaMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_KaMinus"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kKaPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_KaPlus"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kKaAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_KaAll"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_Pr"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kAntiPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_AntiPr"), cent, ietaA, ietaC, mptsubReco); - else if (isp == kPrAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Reco_PrAll"), cent, ietaA, ietaC, mptsubReco); - } - - if (nCorrAB > 0) { - if (isp == kInclusiveIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kPiMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_PiMinus"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kPiPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_PiPlus"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kPiAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_PiAll"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kKaMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_KaMinus"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kKaPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_KaPlus"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kKaAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_KaAll"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_Pr"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kAntiPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_AntiPr"), cent, ietaA, ietaC, mptsubRecoEffCorr); - else if (isp == kPrAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_RecoEffCorr_PrAll"), cent, ietaA, ietaC, mptsubRecoEffCorr); } + sumsTru.add(ieta, pt, 1.); } } - - for (int isp = 0; isp < KNsp; ++isp) { - if (sumWiTruth[isp][ietaA] > 0) { - float val = sumWiptiTruth[isp][ietaA] / sumWiTruth[isp][ietaA]; - histos.fill(HIST("pmeanTru_nch_etabin_spbin"), multPV, ietaA, isp, val); - histos.fill(HIST("pmeanMultTru_nch_etabin_spbin"), multPV, ietaA, isp, sumWiTruth[isp][ietaA]); - } - if (sumWiReco[isp][ietaA] > 0) { - float val = sumWiptiReco[isp][ietaA] / sumWiReco[isp][ietaA]; - histos.fill(HIST("pmeanReco_nch_etabin_spbin"), multPV, ietaA, isp, val); - histos.fill(HIST("pmeanMultReco_nch_etabin_spbin"), multPV, ietaA, isp, sumWiReco[isp][ietaA]); - } - if (sumWiRecoEffCorr[isp][ietaA] > 0) { - float val = sumWiptiRecoEffCorr[isp][ietaA] / sumWiRecoEffCorr[isp][ietaA]; - histos.fill(HIST("pmeanRecoEffcorr_nch_etabin_spbin"), multPV, ietaA, isp, val); - histos.fill(HIST("pmeanMultRecoEffcorr_nch_etabin_spbin"), multPV, ietaA, isp, sumWiRecoEffCorr[isp][ietaA]); + for (int v = 1; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][kGen] = sumsTru; } } - } // end ietaA - - double amplFT0A = 0, amplFT0C = 0; - if (mcCollision.has_foundFT0()) { - const auto& ft0 = mcCollision.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - auto chanelid = ft0.channelA()[iCh]; - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - auto eta = getEtaFT0(chanelid, 0); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - auto chanelid = ft0.channelC()[iCh]; - auto globalId = chanelid + KnFt0cCell; - float ampl = ft0.amplitudeC()[iCh]; - auto eta = getEtaFT0(globalId, 1); - amplFT0C += ampl; - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, globalId, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, globalId, eta, ampl); - } - } - histos.fill(HIST("pmeanFT0Amultpv"), multPV, amplFT0A); - histos.fill(HIST("pmeanFT0A_cent"), cent, amplFT0A); - histos.fill(HIST("pmeanFT0Cmultpv"), multPV, amplFT0C); - histos.fill(HIST("pmeanFT0C_cent"), cent, amplFT0C); - } - PROCESS_SWITCH(RadialFlowDecorr, processMCMean, "process MC to calculate mean pt and Eff Hists", cfgRunMCMean); - - void processMCFluc(MyRun3MCCollisions::iterator const& mcCollision, FilteredTCs const& mcTracks, aod::FT0s const&, aod::McParticles const& mcParticles) - { - if (!state.pmeanTruNchEtabinSpbinStep2 || !state.pmeanRecoNchEtabinSpbinStep2 || !state.pmeanRecoEffcorrNchEtabinSpbinStep2 || - !state.pmeanMultTruNchEtabinSpbinStep2 || !state.pmeanMultRecoNchEtabinSpbinStep2 || !state.pmeanMultRecoEffcorrNchEtabinSpbinStep2) { - LOGF(warning, "MC fluc: Unified Mean pT or Mult map missing"); - return; - } - double sumPmwkTru[KNsp][KNEta][KIntM][KIntK]{}; - double sumWkTru[KNsp][KNEta][KIntK]{}; - double sumPmwkReco[KNsp][KNEta][KIntM][KIntK]{}; - double sumWkReco[KNsp][KNEta][KIntK]{}; - double sumPmwkRecoEffCor[KNsp][KNEta][KIntM][KIntK]{}; - double sumWkRecoEffCor[KNsp][KNEta][KIntK]{}; - - double meanTru[KNsp][KNEta]{}, c2Tru[KNsp][KNEta]{}; - double meanReco[KNsp][KNEta]{}, c2Reco[KNsp][KNEta]{}; - double meanRecoEffCor[KNsp][KNEta]{}, c2RecoEffCor[KNsp][KNEta]{}; - - double meanTruMult[KNsp][KNEta]{}; - double meanRecoMult[KNsp][KNEta]{}; - double meanRecoEffCorMult[KNsp][KNEta]{}; - - double p1kBarTru[KNsp][KNEta]{}, p1kBarReco[KNsp][KNEta]{}, p1kBarRecoEffCor[KNsp][KNEta]{}; - double p1kBarTruMult[KNsp][KNEta]{}, p1kBarRecoMult[KNsp][KNEta]{}, p1kBarRecoEffCorMult[KNsp][KNEta]{}; - - if (!mcCollision.has_mcCollision() || !isEventSelected(mcCollision)) - return; - float cent = getCentrality(mcCollision); - if (cent > KCentMax) - return; - float multPV = mcCollision.multNTracksPV(); - float vz = mcCollision.posZ(); - if (!isPassAddPileup(multPV, mcTracks.size(), cent)) - return; - - histos.fill(HIST("hVtxZ_after_sel"), mcCollision.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, mcTracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), mcTracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, mcTracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, multPV); - - memset(sumPmwkTru, 0, sizeof(sumPmwkTru)); - memset(sumWkTru, 0, sizeof(sumWkTru)); - memset(sumPmwkReco, 0, sizeof(sumPmwkReco)); - memset(sumWkReco, 0, sizeof(sumWkReco)); - memset(sumPmwkRecoEffCor, 0, sizeof(sumPmwkRecoEffCor)); - memset(sumWkRecoEffCor, 0, sizeof(sumWkRecoEffCor)); - - memset(meanTru, 0, sizeof(meanTru)); - memset(c2Tru, 0, sizeof(c2Tru)); - memset(meanReco, 0, sizeof(meanReco)); - memset(c2Reco, 0, sizeof(c2Reco)); - memset(meanRecoEffCor, 0, sizeof(meanRecoEffCor)); - memset(c2RecoEffCor, 0, sizeof(c2RecoEffCor)); - - memset(meanTruMult, 0, sizeof(meanTruMult)); - memset(meanRecoMult, 0, sizeof(meanRecoMult)); - memset(meanRecoEffCorMult, 0, sizeof(meanRecoEffCorMult)); - - memset(p1kBarTru, 0, sizeof(p1kBarTru)); - memset(p1kBarReco, 0, sizeof(p1kBarReco)); - memset(p1kBarRecoEffCor, 0, sizeof(p1kBarRecoEffCor)); - - memset(p1kBarTruMult, 0, sizeof(p1kBarTruMult)); - memset(p1kBarRecoMult, 0, sizeof(p1kBarRecoMult)); - memset(p1kBarRecoEffCorMult, 0, sizeof(p1kBarRecoEffCorMult)); - - double p1kBarFt0A = 0.0, p1kBarFt0C = 0.0; - - for (const auto& particle : mcParticles) { - if (particle.mcCollisionId() != mcCollision.mcCollisionId()) - continue; - - if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) - continue; - - float pt = particle.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) - continue; - float eta = particle.eta(); - int pdgCode = particle.pdgCode(); - int absPdg = std::abs(pdgCode); - - bool isSpecies[KNsp] = { - true, // kInclusiveIdx - pdgCode == -KPiPlus, // kPiMinusIdx - pdgCode == KPiPlus, // kPiPlusIdx - absPdg == KPiPlus, // kPiAllIdx - pdgCode == -KKPlus, // kKaMinusIdx - pdgCode == KKPlus, // kKaPlusIdx - absPdg == KKPlus, // kKaAllIdx - pdgCode == -KProton, // kAntiPrIdx - pdgCode == KProton, // kPrIdx - absPdg == KProton // kPrAllIdx - }; - - for (int ieta = 0; ieta < KNEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) + // --- reco --- + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; - for (int isp = 0; isp < KNsp; ++isp) { - if (isSpecies[isp]) { - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumPmwkTru[isp][ieta][m][k] += std::pow(pt, m); - } - sumWkTru[isp][ieta][k]++; - } - } } - } - } // end truth loop - - for (const auto& track : mcTracks) { - if (track.collisionId() != mcCollision.index()) - continue; - - if (!isTrackSelected(track)) - continue; - - float pt = track.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) - continue; - float eta = track.eta(); - float phi = track.phi(); - auto sign = track.sign(); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - - int id = identifyTrack(track, cent); - bool isPi = (id == KPidPionOne); - bool isKa = (id == KPidKaonTwo); - bool isPr = (id == KPidProtonThree); - bool isSpecies[KNsp] = { - true, - isPi && sign < 0, isPi && sign > 0, isPi, - isKa && sign < 0, isKa && sign > 0, isKa, - isPr && sign < 0, isPr && sign > 0, isPr}; - - for (int isp = 0; isp < KNsp; ++isp) { - if (!isSpecies[isp]) - continue; - float eff = getEfficiency(multPV, pt, eta, static_cast(isp), 0, cfgEff); - float fake = getEfficiency(multPV, pt, eta, static_cast(isp), 1, cfgEff); - float flatW = getFlatteningWeight(vz, sign, pt, eta, phi, static_cast(isp), cfgFlat); - float w = flatW * (1.0 - fake) / eff; - - if (!std::isfinite(w) || w <= 0.f || eff <= KFloatEpsilon) + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - for (int ieta = 0; ieta < KNEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) + // reco level: every selected track, independent of the corrections + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumPmwkReco[isp][ieta][m][k] += std::pow(1.0, k) * std::pow(pt, m); - sumPmwkRecoEffCor[isp][ieta][m][k] += std::pow(w, k) * std::pow(pt, m); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(trackMask, v)) { + sums[v][kReco].add(ieta, pt, 1.); } - sumWkReco[isp][ieta][k] += std::pow(1.0, k); - sumWkRecoEffCor[isp][ieta][k] += std::pow(w, k); } } - if (isp == kInclusiveIdx) { - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPiMinusIdx) { - histos.fill(HIST("hEtaPhiReco_PiMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PiMinus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiMinus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPiPlusIdx) { - histos.fill(HIST("hEtaPhiReco_PiPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PiPlus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiPlus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPiAllIdx) { - histos.fill(HIST("hEtaPhiReco_PiAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PiAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiAll"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kKaMinusIdx) { - histos.fill(HIST("hEtaPhiReco_KaMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_KaMinus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaMinus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kKaPlusIdx) { - histos.fill(HIST("hEtaPhiReco_KaPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_KaPlus"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaPlus"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kKaAllIdx) { - histos.fill(HIST("hEtaPhiReco_KaAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_KaAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaAll"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPrIdx) { - histos.fill(HIST("hEtaPhiReco_Pr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_Pr"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_Pr"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kAntiPrIdx) { - histos.fill(HIST("hEtaPhiReco_AntiPr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_AntiPr"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_AntiPr"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } else if (isp == kPrAllIdx) { - histos.fill(HIST("hEtaPhiReco_PrAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoWtd_PrAll"), vz, sign, pt, eta, phi, w); - histos.fill(HIST("hEtaPhiRecoEffWtd_PrAll"), vz, sign, pt, eta, phi, (1.0 - fake) / eff); - } - } - } // trkslice - - for (int ieta = 0; ieta < KNEta; ++ieta) { - const int ibx = state.pmeanTruNchEtabinSpbinStep2->GetXaxis()->FindBin(mcCollision.multNTracksPV()); - const int iby = ieta + 1; - - for (int isp = 0; isp < KNsp; ++isp) { - const int ibz = isp + 1; - - meanTruMult[isp][ieta] = sumWkTru[isp][ieta][1]; - meanRecoMult[isp][ieta] = sumWkReco[isp][ieta][1]; - meanRecoEffCorMult[isp][ieta] = sumWkRecoEffCor[isp][ieta][1]; - - float mmptTru = state.pmeanTruNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - float mmptReco = state.pmeanRecoNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - float mmptRecoEffCor = state.pmeanRecoEffcorrNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - - float mmMultTru = state.pmeanMultTruNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - float mmMultReco = state.pmeanMultRecoNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - float mmMultRecoEffCor = state.pmeanMultRecoEffcorrNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - - if (std::isfinite(mmptTru)) - std::tie(meanTru[isp][ieta], c2Tru[isp][ieta]) = calculateMeanAndC2FromSums(sumPmwkTru[isp][ieta], sumWkTru[isp][ieta], mmptTru); - if (std::isfinite(mmptReco)) - std::tie(meanReco[isp][ieta], c2Reco[isp][ieta]) = calculateMeanAndC2FromSums(sumPmwkReco[isp][ieta], sumWkReco[isp][ieta], mmptReco); - if (std::isfinite(mmptRecoEffCor)) - std::tie(meanRecoEffCor[isp][ieta], c2RecoEffCor[isp][ieta]) = calculateMeanAndC2FromSums(sumPmwkRecoEffCor[isp][ieta], sumWkRecoEffCor[isp][ieta], mmptRecoEffCor); - - if (mmptTru != 0.0f) - p1kBarTru[isp][ieta] = meanTru[isp][ieta] - mmptTru; - if (mmptReco != 0.0f) - p1kBarReco[isp][ieta] = meanReco[isp][ieta] - mmptReco; - if (mmptRecoEffCor != 0.0f) - p1kBarRecoEffCor[isp][ieta] = meanRecoEffCor[isp][ieta] - mmptRecoEffCor; - - if (mmMultTru != 0.0f) - p1kBarTruMult[isp][ieta] = meanTruMult[isp][ieta] - mmMultTru; - if (mmMultReco != 0.0f) - p1kBarRecoMult[isp][ieta] = meanRecoMult[isp][ieta] - mmMultReco; - if (mmMultRecoEffCor != 0.0f) - p1kBarRecoEffCorMult[isp][ieta] = meanRecoEffCorMult[isp][ieta] - mmMultRecoEffCor; - } - } - - double amplFT0A = 0, amplFT0C = 0; - if (mcCollision.has_foundFT0()) { - const auto& ft0 = mcCollision.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - float ampl = ft0.amplitudeC()[iCh]; - amplFT0C += ampl; - } - } - - for (int isp = 0; isp < KNsp; ++isp) { - for (int ieta = 0; ieta < KNEta; ++ieta) { - histos.fill(HIST("MCGen/Prof_Cent_NEta_Nsp_Nchrec"), cent, ieta, isp, sumWkTru[isp][ieta][1]); - histos.fill(HIST("MCGen/Prof_Mult_NEta_Nsp_Nchrec"), multPV, ieta, isp, sumWkTru[isp][ieta][1]); - - histos.fill(HIST("MCReco/Prof_Cent_NEta_Nsp_Nchrec"), cent, ieta, isp, sumWkReco[isp][ieta][1]); - histos.fill(HIST("MCReco/Prof_Mult_NEta_Nsp_Nchrec"), multPV, ieta, isp, sumWkReco[isp][ieta][1]); - - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_Nsp_Nchrec"), cent, ieta, isp, sumWkRecoEffCor[isp][ieta][1]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_Nsp_Nchrec"), multPV, ieta, isp, sumWkRecoEffCor[isp][ieta][1]); - - if (sumWkTru[isp][ieta][1] > 1.0f) { - histos.fill(HIST("MCGen/Prof_Cent_NEta_Nsp_MeanpT"), cent, ieta, isp, meanTru[isp][ieta]); - histos.fill(HIST("MCGen/Prof_Mult_NEta_Nsp_MeanpT"), multPV, ieta, isp, meanTru[isp][ieta]); + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; } - if (sumWkReco[isp][ieta][1] > 1.0f) { - histos.fill(HIST("MCReco/Prof_Cent_NEta_Nsp_MeanpT"), cent, ieta, isp, meanReco[isp][ieta]); - histos.fill(HIST("MCReco/Prof_Mult_NEta_Nsp_MeanpT"), multPV, ieta, isp, meanReco[isp][ieta]); + if (TESTBIT(weightMask, kSystBase)) { + histos.fill(HIST("Prof_Weights_pT_type"), pt, 0, eff); + histos.fill(HIST("Prof_Weights_pT_type"), pt, 1, fake); + histos.fill(HIST("Prof_Weights_pT_type"), pt, 2, w[kSystBase]); + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); } - if (sumWkRecoEffCor[isp][ieta][1] > 1.0f) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cent_NEta_Nsp_MeanpT"), cent, ieta, isp, meanRecoEffCor[isp][ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_Mult_NEta_Nsp_MeanpT"), multPV, ieta, isp, meanRecoEffCor[isp][ieta]); + + // reco, efficiency-corrected + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][kRecoEffCorr].add(ieta, pt, w[v]); + } + } } } - } + flushTrackCounts(); + + // --- forward detectors: charged primaries (truth) and amplitudes (reco) --- + FwdSignal fwdTru, fwdReco; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), false); + computeFwdTruth(mcParticles, fwdTru); + computeFwdReco(coll, vz, fwdReco, TESTBIT(ev.mask, kSystBase)); + } - for (int ieta = 0; ieta < KNEta; ++ieta) { - for (int isp = 0; isp < KNsp; ++isp) { - if (std::isfinite(meanTru[isp][ieta])) { - histos.fill(HIST("MCGen/Prof_MeanpT_Cent_etabin_spbin"), cent, ieta, isp, meanTru[isp][ieta]); - histos.fill(HIST("MCGen/Prof_MeanpT_Mult_etabin_spbin"), multPV, ieta, isp, meanTru[isp][ieta]); - } - if (std::isfinite(c2Tru[isp][ieta])) { - histos.fill(HIST("MCGen/Prof_C2_Cent_etabin_spbin"), cent, ieta, isp, c2Tru[isp][ieta]); - histos.fill(HIST("MCGen/Prof_C2_Mult_etabin_spbin"), multPV, ieta, isp, c2Tru[isp][ieta]); - } - if (std::isfinite(meanReco[isp][ieta])) { - histos.fill(HIST("MCReco/Prof_MeanpT_Cent_etabin_spbin"), cent, ieta, isp, meanReco[isp][ieta]); - histos.fill(HIST("MCReco/Prof_MeanpT_Mult_etabin_spbin"), multPV, ieta, isp, meanReco[isp][ieta]); - } - if (std::isfinite(c2Reco[isp][ieta])) { - histos.fill(HIST("MCReco/Prof_C2_Cent_etabin_spbin"), cent, ieta, isp, c2Reco[isp][ieta]); - histos.fill(HIST("MCReco/Prof_C2_Mult_etabin_spbin"), multPV, ieta, isp, c2Reco[isp][ieta]); - } - if (std::isfinite(meanRecoEffCor[isp][ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_MeanpT_Cent_etabin_spbin"), cent, ieta, isp, meanRecoEffCor[isp][ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_MeanpT_Mult_etabin_spbin"), multPV, ieta, isp, meanRecoEffCor[isp][ieta]); + // --- <[pT]> and per eta bin, per forward bin --- + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; } - if (std::isfinite(c2RecoEffCor[isp][ieta])) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2_Cent_etabin_spbin"), cent, ieta, isp, c2RecoEffCor[isp][ieta]); - histos.fill(HIST("MCRecoEffCorr/Prof_C2_Mult_etabin_spbin"), multPV, ieta, isp, c2RecoEffCor[isp][ieta]); + for (int l = 0; l < kNLevels; ++l) { + const auto& s = sums[v][l]; + const auto& h = hists.mean[v][l]; + for (int ieta = 0; ieta < nEta; ++ieta) { + const double n = s.wk[ieta][1]; + h.mean->Fill(nCh, ieta, kMeanN, n); + if (n <= KFloatEpsilon) { + continue; + } + const double mpt = s.pmwk[ieta][1][1] / n; + if (std::isfinite(mpt)) { + h.mean->Fill(nCh, ieta, kMeanPt, mpt); + } + } + const FwdSignal& fwd = (l == kGen) ? fwdTru : fwdReco; + if (cfgFwdOpt.cfgFwd && h.fwdMean) { + for (int k = 0; k < nFwd; ++k) { + if (fwd.valid[k]) { + h.fwdMean->Fill(nCh, k, fwd.ampl[k]); + } + } + } } } } - - p1kBarFt0A = amplFT0A - state.pmeanFT0AmultpvStep2->GetBinContent(state.pmeanFT0AmultpvStep2->GetXaxis()->FindBin(multPV)); - p1kBarFt0C = amplFT0C - state.pmeanFT0CmultpvStep2->GetBinContent(state.pmeanFT0CmultpvStep2->GetXaxis()->FindBin(multPV)); - - for (int ietaA = 1; ietaA <= (KNEta - 1) / 2; ++ietaA) { - int ietaC = KNEta - ietaA; - for (int isp = 0; isp < KNsp; ++isp) { - float c2SubTru = p1kBarTru[isp][ietaA] * p1kBarTru[isp][ietaC]; - float c2SubReco = p1kBarReco[isp][ietaA] * p1kBarReco[isp][ietaC]; - float c2SubRecoEffCor = p1kBarRecoEffCor[isp][ietaA] * p1kBarRecoEffCor[isp][ietaC]; - - float covTru = p1kBarTruMult[isp][ietaA] * p1kBarTru[isp][ietaC]; - float covReco = p1kBarRecoMult[isp][ietaA] * p1kBarReco[isp][ietaC]; - float covRecoEffCor = p1kBarRecoEffCorMult[isp][ietaA] * p1kBarRecoEffCor[isp][ietaC]; - - float covFT0ATru = p1kBarFt0A * p1kBarTru[isp][ietaC]; - float covFT0AReco = p1kBarFt0A * p1kBarReco[isp][ietaC]; - float covFT0ARecoEffCor = p1kBarFt0A * p1kBarRecoEffCor[isp][ietaC]; - - float covFT0CTru = p1kBarFt0C * p1kBarTru[isp][ietaA]; - float covFT0CReco = p1kBarFt0C * p1kBarReco[isp][ietaA]; - float covFT0CRecoEffCor = p1kBarFt0C * p1kBarRecoEffCor[isp][ietaA]; - - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub_Cent_etabin_spbin"), cent, ietaA, isp, c2SubTru); - histos.fill(HIST("MCGen/Prof_C2Sub_Mult_etabin_spbin"), multPV, ietaA, isp, c2SubTru); + } + PROCESS_SWITCH(RadialFlowDecorr, processMCMean, "process MC to calculate mean pt", cfgRunMCMean); + + // =========================================================================== + // MC: fluctuations (C2, subevent, forward covariances) at three levels + // =========================================================================== + void processMCFluc(aod::McCollisions::iterator const& mcCollision, McRecoCollisions const& recoCollisions, + aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, + FilteredTCs const& mcTracks, aod::McParticles const& mcParticles) + { + const int64_t best = bestRecoCollision(recoCollisions, mcCollision.posZ()); + for (const auto& coll : recoCollisions) { + if (coll.globalIndex() != best) { + continue; + } + const auto tracks = mcTracks.sliceBy(tracksPerCollision, coll.globalIndex()); + histos.fill(HIST("hVtxZ"), coll.posZ()); + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { + return; + } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); + + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub_Cent_etabin_spbin"), cent, ietaA, isp, c2SubReco); - histos.fill(HIST("MCReco/Prof_C2Sub_Mult_etabin_spbin"), multPV, ietaA, isp, c2SubReco); + } + + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + for (auto& s : sums[v]) { + s.reset(); + } } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub_Cent_etabin_spbin"), cent, ietaA, isp, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub_Mult_etabin_spbin"), multPV, ietaA, isp, c2SubRecoEffCor); + } + + // --- truth sums: independent of the track cuts, copied to every selected variation --- + PowerSums& sumsTru = sums[kSystBase][kGen]; + if (!TESTBIT(ev.mask, kSystBase)) { + sumsTru.reset(); + } + for (const auto& particle : mcParticles) { + if (!isParticleSelected(particle) || !particle.isPhysicalPrimary()) { + continue; } - if (std::isfinite(covTru)) { - histos.fill(HIST("MCGen/Prof_Cov_Cent_etabin_spbin"), cent, ietaA, isp, covTru); - histos.fill(HIST("MCGen/Prof_Cov_Mult_etabin_spbin"), multPV, ietaA, isp, covTru); + float pt = particle.pt(), eta = particle.eta(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; } - if (std::isfinite(covReco)) { - histos.fill(HIST("MCReco/Prof_Cov_Cent_etabin_spbin"), cent, ietaA, isp, covReco); - histos.fill(HIST("MCReco/Prof_Cov_Mult_etabin_spbin"), multPV, ietaA, isp, covReco); + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + sumsTru.add(ieta, pt, 1.); } - if (std::isfinite(covRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_Cov_Cent_etabin_spbin"), cent, ietaA, isp, covRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_Cov_Mult_etabin_spbin"), multPV, ietaA, isp, covRecoEffCor); + } + for (int v = 1; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][kGen] = sumsTru; } + } - if (std::isfinite(covFT0ATru)) { - histos.fill(HIST("MCGen/Prof_CovFT0A_Cent_etabin_spbin"), cent, ietaA, isp, covFT0ATru); - histos.fill(HIST("MCGen/Prof_CovFT0A_Mult_etabin_spbin"), multPV, ietaA, isp, covFT0ATru); + // --- reco sums --- + std::array w{}; + for (const auto& track : tracks) { + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { + continue; } - if (std::isfinite(covFT0AReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0A_Cent_etabin_spbin"), cent, ietaA, isp, covFT0AReco); - histos.fill(HIST("MCReco/Prof_CovFT0A_Mult_etabin_spbin"), multPV, ietaA, isp, covFT0AReco); + float pt = track.pt(); + if (pt <= cfgPtMin || pt > cfgPtMax) { + continue; } - if (std::isfinite(covFT0ARecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A_Cent_etabin_spbin"), cent, ietaA, isp, covFT0ARecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A_Mult_etabin_spbin"), multPV, ietaA, isp, covFT0ARecoEffCor); + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); } - if (std::isfinite(covFT0CTru)) { - histos.fill(HIST("MCGen/Prof_CovFT0C_Cent_etabin_spbin"), cent, ietaA, isp, covFT0CTru); - histos.fill(HIST("MCGen/Prof_CovFT0C_Mult_etabin_spbin"), multPV, ietaA, isp, covFT0CTru); + // reco level: every selected track, independent of the corrections + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(trackMask, v)) { + sums[v][kReco].add(ieta, pt, 1.); + } + } } - if (std::isfinite(covFT0CReco)) { - histos.fill(HIST("MCReco/Prof_CovFT0C_Cent_etabin_spbin"), cent, ietaA, isp, covFT0CReco); - histos.fill(HIST("MCReco/Prof_CovFT0C_Mult_etabin_spbin"), multPV, ietaA, isp, covFT0CReco); + + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; } - if (std::isfinite(covFT0CRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C_Cent_etabin_spbin"), cent, ietaA, isp, covFT0CRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C_Mult_etabin_spbin"), multPV, ietaA, isp, covFT0CRecoEffCor); + if (TESTBIT(weightMask, kSystBase)) { + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); } - } - } - - for (int ietaA = 1; ietaA < KNEta; ++ietaA) { - for (int ietaC = 1; ietaC < KNEta; ++ietaC) { - - float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; - float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - float gap = etaValA - etaValB; - float sum = (etaValA + etaValB); - for (int isp = 0; isp < KNsp; ++isp) { - - float c2SubTru = p1kBarTru[isp][ietaA] * p1kBarTru[isp][ietaC]; - float c2SubReco = p1kBarReco[isp][ietaA] * p1kBarReco[isp][ietaC]; - float c2SubRecoEffCor = p1kBarRecoEffCor[isp][ietaA] * p1kBarRecoEffCor[isp][ietaC]; - - float covTru = p1kBarTruMult[isp][ietaA] * p1kBarTru[isp][ietaC]; - float covReco = p1kBarRecoMult[isp][ietaA] * p1kBarReco[isp][ietaC]; - float covRecoEffCor = p1kBarRecoEffCorMult[isp][ietaA] * p1kBarRecoEffCor[isp][ietaC]; - - float covFT0ATru = p1kBarFt0A * p1kBarTru[isp][ietaC]; - float covFT0AReco = p1kBarFt0A * p1kBarReco[isp][ietaC]; - float covFT0ARecoEffCor = p1kBarFt0A * p1kBarRecoEffCor[isp][ietaC]; - - float covFT0CTru = p1kBarFt0C * p1kBarTru[isp][ietaA]; - float covFT0CReco = p1kBarFt0C * p1kBarReco[isp][ietaA]; - float covFT0CRecoEffCor = p1kBarFt0C * p1kBarRecoEffCor[isp][ietaA]; - - if (isp == kInclusiveIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kPiMinusIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_PiMinus"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_PiMinus"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_PiMinus"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kPiPlusIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_PiPlus"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_PiPlus"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_PiPlus"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kPiAllIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_PiAll"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_PiAll"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_PiAll"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kKaMinusIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_KaMinus"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_KaMinus"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_KaMinus"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kKaPlusIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_KaPlus"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_KaPlus"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_KaPlus"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kKaAllIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_KaAll"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_KaAll"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_KaAll"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kPrIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_Pr"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_Pr"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_Pr"), cent, gap, sum, c2SubRecoEffCor); - } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kAntiPrIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_AntiPr"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_AntiPr"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_AntiPr"), cent, gap, sum, c2SubRecoEffCor); - } - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0CRecoEffCor); - - } else if (isp == kPrAllIdx) { - if (std::isfinite(c2SubTru)) { - histos.fill(HIST("MCGen/Prof_C2Sub2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, c2SubTru); - histos.fill(HIST("MCGen/Prof_GapSum2D_PrAll"), cent, gap, sum, c2SubTru); - } - if (std::isfinite(c2SubReco)) { - histos.fill(HIST("MCReco/Prof_C2Sub2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, c2SubReco); - histos.fill(HIST("MCReco/Prof_GapSum2D_PrAll"), cent, gap, sum, c2SubReco); - } - if (std::isfinite(c2SubRecoEffCor)) { - histos.fill(HIST("MCRecoEffCorr/Prof_C2Sub2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, c2SubRecoEffCor); - histos.fill(HIST("MCRecoEffCorr/Prof_GapSum2D_PrAll"), cent, gap, sum, c2SubRecoEffCor); + // reco, efficiency-corrected + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { + continue; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][kRecoEffCorr].add(ieta, pt, w[v]); } - - if (std::isfinite(covTru)) - histos.fill(HIST("MCGen/Prof_Cov2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covTru); - if (std::isfinite(covReco)) - histos.fill(HIST("MCReco/Prof_Cov2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covReco); - if (std::isfinite(covRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_Cov2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covRecoEffCor); - - if (std::isfinite(covFT0ATru)) - histos.fill(HIST("MCGen/Prof_CovFT0A2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0ATru); - if (std::isfinite(covFT0AReco)) - histos.fill(HIST("MCReco/Prof_CovFT0A2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0AReco); - if (std::isfinite(covFT0ARecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0A2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0ARecoEffCor); - - if (std::isfinite(covFT0CTru)) - histos.fill(HIST("MCGen/Prof_CovFT0C2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0CTru); - if (std::isfinite(covFT0CReco)) - histos.fill(HIST("MCReco/Prof_CovFT0C2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0CReco); - if (std::isfinite(covFT0CRecoEffCor)) - histos.fill(HIST("MCRecoEffCorr/Prof_CovFT0C2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0CRecoEffCor); } } } + flushTrackCounts(); + + // --- forward detectors: charged primaries (truth) and amplitudes (reco) --- + FwdSignal fwdTru, fwdReco; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), false); + computeFwdTruth(mcParticles, fwdTru); + computeFwdReco(coll, vz, fwdReco, TESTBIT(ev.mask, kSystBase)); + } + + // --- c2, c3, subevent correlators and forward covariances of every variation and level --- + EventObs evObs; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; + } + for (int l = 0; l < kNLevels; ++l) { + const FwdSignal* fwd = cfgFwdOpt.cfgFwd ? (l == kGen ? &fwdTru : &fwdReco) : nullptr; + computeEventObs(sums[v][l], v, l, fwd, nCh, evObs); + fillFluc(hists.fluc[v][l], evObs, 1., nCh); + } + } } } PROCESS_SWITCH(RadialFlowDecorr, processMCFluc, "process MC to calculate pt fluc", cfgRunMCFluc); - void processDataGetNSig(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) + // =========================================================================== + // DATA: build flattening maps (inclusive) + // =========================================================================== + void processGetDataFlat(AodCollisionsSel::iterator const& coll, AodTracksSel const& tracks) { histos.fill(HIST("hVtxZ"), coll.posZ()); - if (!isEventSelected(coll)) + const auto ev = isEventSelected(coll, tracks.size(), maskFlat); + if (ev.mask == 0) { return; - float cent = getCentrality(coll); - if (cent > KCentMax) - return; - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - int ntrk = 0; - for (const auto& track : tracks) { - if (!isTrackSelected(track)) - continue; - float pt = track.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) - continue; - float eta = track.eta(); - if (eta > etaLw[0] && eta < etaUp[0]) - ntrk++; - fillNSigmaBefCut(track, cent); } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - if (cfgZDC) { - const auto& foundBC = coll.foundBC_as(); - if (!foundBC.has_zdc()) { - return; + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); } - auto zdc = foundBC.zdc(); - auto zdcAmp = zdc.energyCommonZNA() + zdc.energyCommonZNC(); - histos.fill(HIST("hnTrkPVZDC"), coll.multNTracksPV(), zdcAmp); - histos.fill(HIST("hNchZDC"), ntrk, zdcAmp); } - } - PROCESS_SWITCH(RadialFlowDecorr, processDataGetNSig, "process data to Get Nsigma cuts", cfgRunDataGetNSig); - - void processGetDataFlat(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, AodTracksSel const& tracks) - { - histos.fill(HIST("hVtxZ"), coll.posZ()); - if (!isEventSelected(coll)) - return; - float cent = getCentrality(coll); - if (cent > KCentMax) - return; - - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) - return; - - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - int ntrk = 0; - float vz = coll.posZ(); + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; + } float pt = track.pt(); - if (pt <= cfgPtMin || pt > cfgPtMax) + if (pt <= cfgPtMin || pt > cfgPtMax) { continue; - float eta = track.eta(); - float phi = track.phi(); - auto sign = track.sign(); - - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - - if (eta > etaLw[0] && eta < etaUp[0]) - ntrk++; - fillNSigmaBefCut(track, cent); - int id = identifyTrack(track, cent); - bool isPi = (id == KPidPionOne); - bool isKa = (id == KPidKaonTwo); - bool isPr = (id == KPidProtonThree); - bool isSpecies[KNsp] = { - true, - isPi && sign < 0, isPi && sign > 0, isPi, - isKa && sign < 0, isKa && sign > 0, isKa, - isPr && sign < 0, isPr && sign > 0, isPr}; - - fillNSigmaAftCut(track, cent, isSpecies); - for (int isp = 0; isp < KNsp; ++isp) { - if (!isSpecies[isp]) - continue; - float eff = getEfficiency(coll.multNTracksPV(), pt, eta, static_cast(isp), 0, cfgEff); - if (eff <= KFloatEpsilon) - continue; - - float fake = getEfficiency(coll.multNTracksPV(), pt, eta, static_cast(isp), 1, cfgEff); - float w = (1.0f - fake) / eff; - - if (!std::isfinite(w) || w <= 0.f) - continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - if (isp == kInclusiveIdx) { - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiMinusIdx) { - histos.fill(HIST("hEtaPhiReco_PiMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiMinus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PiMinus"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiPlusIdx) { - histos.fill(HIST("hEtaPhiReco_PiPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiPlus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PiPlus"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiAllIdx) { - histos.fill(HIST("hEtaPhiReco_PiAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiAll"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PiAll"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaMinusIdx) { - histos.fill(HIST("hEtaPhiReco_KaMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaMinus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_KaMinus"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaPlusIdx) { - histos.fill(HIST("hEtaPhiReco_KaPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaPlus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_KaPlus"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaAllIdx) { - histos.fill(HIST("hEtaPhiReco_KaAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaAll"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_KaAll"), vz, sign, pt, eta, phi, w); - } else if (isp == kPrIdx) { - histos.fill(HIST("hEtaPhiReco_Pr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_Pr"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_Pr"), vz, sign, pt, eta, phi, w); - } else if (isp == kAntiPrIdx) { - histos.fill(HIST("hEtaPhiReco_AntiPr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_AntiPr"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_AntiPr"), vz, sign, pt, eta, phi, w); - } else if (isp == kPrAllIdx) { - histos.fill(HIST("hEtaPhiReco_PrAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PrAll"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PrAll"), vz, sign, pt, eta, phi, w); - } - } - } - - if (cfgZDC) { - const auto& foundBC = coll.foundBC_as(); - if (!foundBC.has_zdc()) { - return; + // efficiency weights only: the flattening map is what this pass measures + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, false, w, eff, fake, flatWeight); + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + fillFlat(v, vz, sign, pt, eta, phi, w[v], w[v]); + } } - auto zdc = foundBC.zdc(); - auto zdcAmp = zdc.energyCommonZNA() + zdc.energyCommonZNC(); - histos.fill(HIST("hnTrkPVZDC"), coll.multNTracksPV(), zdcAmp); - histos.fill(HIST("hNchZDC"), ntrk, zdcAmp); } + flushTrackCounts(); } PROCESS_SWITCH(RadialFlowDecorr, processGetDataFlat, "process data to calculate Flattening maps", cfgRunGetDataFlat); - void processDataMean(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FT0s const&, AodTracksSel const& tracks) + // =========================================================================== + // DATA: mean pT + // =========================================================================== + void processDataMean(AodCollisionsSel::iterator const& coll, aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, AodTracksSel const& tracks) { - double sumWi[KNsp][KNEta]{}, sumWipti[KNsp][KNEta]{}; - - if (!isEventSelected(coll)) - return; - - float cent = getCentrality(coll); - if (cent > KCentMax) - return; - - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { return; + } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - float vz = coll.posZ(); + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); + } + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][0].reset(); + } + } + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; - + } float p = track.p(); float pt = track.pt(); - float eta = track.eta(); - float phi = track.phi(); - auto sign = track.sign(); - - if (p < KFloatEpsilon) + if (p < KFloatEpsilon) { continue; - - if (pt <= cfgPtMin || pt > cfgPtMax) + } + if (pt <= cfgPtMin || pt > cfgPtMax) { continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hP"), p); + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - histos.fill(HIST("hP"), p); - histos.fill(HIST("hPt"), pt); - histos.fill(HIST("hEta"), eta); - histos.fill(HIST("hPhi"), phi); - int id = identifyTrack(track, cent); - bool isPi = (id == KPidPionOne); - bool isKa = (id == KPidKaonTwo); - bool isPr = (id == KPidProtonThree); - bool isSpecies[KNsp] = { - true, - isPi && sign < 0, isPi && sign > 0, isPi, - isKa && sign < 0, isKa && sign > 0, isKa, - isPr && sign < 0, isPr && sign > 0, isPr}; - - for (int isp = 0; isp < KNsp; ++isp) { - if (!isSpecies[isp]) - continue; - float eff = getEfficiency(coll.multNTracksPV(), pt, eta, static_cast(isp), 0, cfgEff); - if (eff <= KFloatEpsilon) - continue; - - float fake = getEfficiency(coll.multNTracksPV(), pt, eta, static_cast(isp), 1, cfgEff); - float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, static_cast(isp), cfgFlat); - float w = flatWeight * (1.0f - fake) / eff; + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; + } + if (TESTBIT(weightMask, kSystBase)) { + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 0, eff); + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 1, fake); + histos.fill(HIST("Prof_Weights_pt_eta_type"), pt, eta, 2, flatWeight); + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); + } - if (!std::isfinite(w) || w <= 0.f) + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; - - if (isp == kInclusiveIdx) { - histos.fill(HIST("hEtaPhiReco"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiMinusIdx) { - histos.fill(HIST("hEtaPhiReco_PiMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiMinus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PiMinus"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiPlusIdx) { - histos.fill(HIST("hEtaPhiReco_PiPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiPlus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PiPlus"), vz, sign, pt, eta, phi, w); - } else if (isp == kPiAllIdx) { - histos.fill(HIST("hEtaPhiReco_PiAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PiAll"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PiAll"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaMinusIdx) { - histos.fill(HIST("hEtaPhiReco_KaMinus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaMinus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_KaMinus"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaPlusIdx) { - histos.fill(HIST("hEtaPhiReco_KaPlus"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaPlus"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_KaPlus"), vz, sign, pt, eta, phi, w); - } else if (isp == kKaAllIdx) { - histos.fill(HIST("hEtaPhiReco_KaAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_KaAll"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_KaAll"), vz, sign, pt, eta, phi, w); - } else if (isp == kPrIdx) { - histos.fill(HIST("hEtaPhiReco_Pr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_Pr"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_Pr"), vz, sign, pt, eta, phi, w); - } else if (isp == kAntiPrIdx) { - histos.fill(HIST("hEtaPhiReco_AntiPr"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_AntiPr"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_AntiPr"), vz, sign, pt, eta, phi, w); - } else if (isp == kPrAllIdx) { - histos.fill(HIST("hEtaPhiReco_PrAll"), vz, sign, pt, eta, phi); - histos.fill(HIST("hEtaPhiRecoEffWtd_PrAll"), vz, sign, pt, eta, phi, (1.0f - fake) / eff); - histos.fill(HIST("hEtaPhiRecoWtd_PrAll"), vz, sign, pt, eta, phi, w); - } - - for (int ieta = 0; ieta < KNEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) - continue; - sumWi[isp][ieta] += w; - sumWipti[isp][ieta] += w * pt; + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][0].add(ieta, pt, w[v]); + } } } } + flushTrackCounts(); - for (int isp = 0; isp < KNsp; ++isp) { - if (sumWi[isp][0] < 1.0f) - continue; - histos.fill(HIST("Prof_Cent_Nsp_Nchrec"), cent, isp, sumWi[isp][0]); - histos.fill(HIST("Prof_Mult_Nsp_Nchrec"), coll.multNTracksPV(), isp, sumWi[isp][0]); - histos.fill(HIST("Prof_Cent_Nsp_MeanpT"), cent, isp, sumWipti[isp][0] / sumWi[isp][0]); - histos.fill(HIST("Prof_Mult_Nsp_MeanpT"), coll.multNTracksPV(), isp, sumWipti[isp][0] / sumWi[isp][0]); + // --- forward detectors: gain-equalised amplitudes --- + FwdSignal fwd; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), true); + computeFwdReco(coll, vz, fwd, TESTBIT(ev.mask, kSystBase)); } - for (int ietaA = 0; ietaA < KNEta; ++ietaA) { - for (int ietaC = 0; ietaC < KNEta; ++ietaC) { - for (int isp = 0; isp < KNsp; ++isp) { - if ((sumWi[isp][ietaA] < 1.0f) || (sumWi[isp][ietaC] < 1.0f)) - continue; - - double wCorrAB = sumWi[isp][ietaA] + sumWi[isp][ietaC]; - if (wCorrAB > 0) { - float mptsub = (sumWipti[isp][ietaA] + sumWipti[isp][ietaC]) / wCorrAB; - if (isp == kInclusiveIdx) - histos.fill(HIST("Prof2D_MeanpTSub"), cent, ietaA, ietaC, mptsub); - else if (isp == kPiMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_PiMinus"), cent, ietaA, ietaC, mptsub); - else if (isp == kPiPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_PiPlus"), cent, ietaA, ietaC, mptsub); - else if (isp == kPiAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_PiAll"), cent, ietaA, ietaC, mptsub); - else if (isp == kKaMinusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_KaMinus"), cent, ietaA, ietaC, mptsub); - else if (isp == kKaPlusIdx) - histos.fill(HIST("Prof2D_MeanpTSub_KaPlus"), cent, ietaA, ietaC, mptsub); - else if (isp == kKaAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_KaAll"), cent, ietaA, ietaC, mptsub); - else if (isp == kPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_Pr"), cent, ietaA, ietaC, mptsub); - else if (isp == kAntiPrIdx) - histos.fill(HIST("Prof2D_MeanpTSub_AntiPr"), cent, ietaA, ietaC, mptsub); - else if (isp == kPrAllIdx) - histos.fill(HIST("Prof2D_MeanpTSub_PrAll"), cent, ietaA, ietaC, mptsub); - } - if (ietaA == ietaC) { - double mpt = sumWipti[isp][ietaA] / sumWi[isp][ietaA]; - if (sumWi[isp][ietaA] >= 1.0f && std::isfinite(mpt)) { - histos.fill(HIST("pmean_nch_etabin_spbin"), coll.multNTracksPV(), ietaA, isp, mpt); - histos.fill(HIST("pmeanMult_nch_etabin_spbin"), coll.multNTracksPV(), ietaA, isp, sumWi[isp][ietaA]); - histos.fill(HIST("pmean_cent_etabin_spbin"), cent, ietaA, isp, mpt); - histos.fill(HIST("pmeanMult_cent_etabin_spbin"), cent, ietaA, isp, sumWi[isp][ietaA]); - } - } - } + // --- <[pT]> and per eta bin, per forward bin --- + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; } - } - double amplFT0A = 0, amplFT0C = 0; - if (coll.has_foundFT0()) { - const auto& ft0 = coll.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - auto chanelid = ft0.channelA()[iCh]; - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - auto eta = getEtaFT0(chanelid, 0); - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, chanelid, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, chanelid, eta, ampl); + const auto& s = sums[v][0]; + const auto& h = hists.mean[v][0]; + for (int ieta = 0; ieta < nEta; ++ieta) { + const double n = s.wk[ieta][1]; + h.mean->Fill(nCh, ieta, kMeanN, n); + if (n <= KFloatEpsilon) { + continue; + } + const double mpt = s.pmwk[ieta][1][1] / n; + if (std::isfinite(mpt)) { + h.mean->Fill(nCh, ieta, kMeanPt, mpt); + } } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - auto chanelid = ft0.channelC()[iCh]; - auto globalId = chanelid + KnFt0cCell; - float ampl = ft0.amplitudeC()[iCh]; - auto eta = getEtaFT0(globalId, 1); - amplFT0C += ampl; - histos.fill(HIST("pmean_cent_id_eta_FT0"), cent, globalId, eta, ampl); - histos.fill(HIST("h3_cent_id_eta_FT0"), cent, globalId, eta, ampl); + if (cfgFwdOpt.cfgFwd && h.fwdMean) { + for (int k = 0; k < nFwd; ++k) { + if (fwd.valid[k]) { + h.fwdMean->Fill(nCh, k, fwd.ampl[k]); + } + } } } - - histos.fill(HIST("pmeanFT0Amultpv"), coll.multNTracksPV(), amplFT0A); - histos.fill(HIST("pmeanFT0A_cent"), cent, amplFT0A); - histos.fill(HIST("pmeanFT0Cmultpv"), coll.multNTracksPV(), amplFT0C); - histos.fill(HIST("pmeanFT0C_cent"), cent, amplFT0C); } PROCESS_SWITCH(RadialFlowDecorr, processDataMean, "process data to calculate mean pT", cfgRunDataMean); - void processDataFluc(AodCollisionsSel::iterator const& coll, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FT0s const&, AodTracksSel const& tracks) + // =========================================================================== + // DATA: fluctuations (C2, subevent, forward covariances) + Poisson bootstrap for Base + // =========================================================================== + void processDataFluc(AodCollisionsSel::iterator const& coll, aod::BCsWithTimestamps const&, aod::FT0s const&, aod::FV0As const&, AodTracksSel const& tracks) { - if (!isEventSelected(coll)) - return; - float cent = getCentrality(coll); - if (cent > KCentMax) - return; - - if (!isPassAddPileup(coll.multNTracksPV(), tracks.size(), cent)) - return; - - histos.fill(HIST("hVtxZ_after_sel"), coll.posZ()); - histos.fill(HIST("hCentrality"), cent); - - histos.fill(HIST("Hist2D_globalTracks_PVTracks"), coll.multNTracksPV(), tracks.size()); - histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), cent); - histos.fill(HIST("Hist2D_globalTracks_cent"), cent, tracks.size()); - histos.fill(HIST("Hist2D_PVTracks_cent"), cent, coll.multNTracksPV()); - - if (!state.pmeanNchEtabinSpbinStep2 || !state.pmeanMultNchEtabinSpbinStep2) { - LOGF(warning, "Data fluc: Mean pT or Mult map missing"); + const auto ev = isEventSelected(coll, tracks.size(), maskAll); + if (ev.mask == 0) { return; } + const float nCh = tracks.size(); + const float multPV = coll.multNTracksPV(); + const float vz = coll.posZ(); - for (int isp = 0; isp < KNsp; ++isp) { - auto pid = static_cast(isp); - if (!state.hEff[pid] || !state.hFake[pid] || !state.hFlatWeight[pid]) { - LOGF(warning, "Data fluc: Correction maps (Eff, Fake, or Flat) are null for species index %d", isp); - return; + if (TESTBIT(ev.mask, kSystBase)) { + histos.fill(HIST("hVtxZ_after_sel"), vz); + histos.fill(HIST("hCentrality"), ev.cent[kSystBase]); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), multPV, nCh); + histos.fill(HIST("Hist2D_PVTracks_cent"), ev.cent[kSystBase], multPV); + hists.nchOccupancy->Fill(nCh, ev.occupancy); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v) && hists.nchCent[v]) { + hists.nchCent[v]->Fill(ev.cent[v], nCh); } } - double sumpmwk[KNsp][KNEta][KIntM][KIntK]{}; - double sumwk[KNsp][KNEta][KIntK]{}; - - double mean[KNsp][KNEta]{}, c2[KNsp][KNEta]{}; - double p1kBar[KNsp][KNEta]{}; - double meanMult[KNsp][KNEta]{}, p1kBarMult[KNsp][KNEta]{}; - - float vz = coll.posZ(); - + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(ev.mask, v)) { + sums[v][0].reset(); + } + } + std::array w{}; for (const auto& track : tracks) { - if (!isTrackSelected(track)) + const uint32_t trackMask = isTrackSelected(track, ev.mask); + if (trackMask == 0) { continue; - + } float p = track.p(); float pt = track.pt(); - float eta = track.eta(); - float phi = track.phi(); - auto sign = track.sign(); - - if (p < KFloatEpsilon) + if (p < KFloatEpsilon) { continue; - - if (pt <= cfgPtMin || pt > cfgPtMax) + } + if (pt <= cfgPtMin || pt > cfgPtMax) { continue; - int id = identifyTrack(track, cent); - bool isPi = (id == KPidPionOne); - bool isKa = (id == KPidKaonTwo); - bool isPr = (id == KPidProtonThree); - bool isSpecies[KNsp] = { - true, - isPi && sign < 0, isPi && sign > 0, isPi, - isKa && sign < 0, isKa && sign > 0, isKa, - isPr && sign < 0, isPr && sign > 0, isPr}; - - for (int isp = 0; isp < KNsp; ++isp) { - if (!isSpecies[isp]) - continue; - float eff = getEfficiency(coll.multNTracksPV(), pt, eta, static_cast(isp), 0, cfgEff); - if (eff <= KFloatEpsilon) - continue; + } + float eta = track.eta(), phi = track.phi(); + float sign = track.sign(); + if (TESTBIT(trackMask, kSystBase)) { + histos.fill(HIST("hP"), p); + histos.fill(HIST("hPt"), pt); + histos.fill(HIST("hEta"), eta); + histos.fill(HIST("hPhi"), phi); + } - float fake = getEfficiency(coll.multNTracksPV(), pt, eta, static_cast(isp), 1, cfgEff); - float flatWeight = getFlatteningWeight(vz, sign, pt, eta, phi, static_cast(isp), cfgFlat); - float w = flatWeight * (1.0f - fake) / eff; + float eff = 1.f, fake = 0.f, flatWeight = 1.f; + const uint32_t weightMask = getTrackWeights(trackMask, ev.cent[kSystBase], vz, sign, pt, eta, phi, true, w, eff, fake, flatWeight); + if (weightMask == 0) { + continue; + } + if (TESTBIT(weightMask, kSystBase)) { + fillFlat(kSystBase, vz, sign, pt, eta, phi, w[kSystBase], (1.f - fake) / eff); + } - if (!std::isfinite(w) || w <= 0.f) + for (int ieta = 0; ieta < nEta; ++ieta) { + if (eta <= etaLw[ieta] || eta > etaUp[ieta]) { continue; - - for (int ieta = 0; ieta < KNEta; ++ieta) { - if (eta <= etaLw[ieta] || eta > etaUp[ieta]) - continue; - for (int k = 0; k < KIntK; ++k) { - for (int m = 0; m < KIntM; ++m) { - sumpmwk[isp][ieta][m][k] += std::pow(w, k) * std::pow(pt, m); - } - sumwk[isp][ieta][k] += std::pow(w, k); + } + for (int v = 0; v < kNSyst; ++v) { + if (TESTBIT(weightMask, v)) { + sums[v][0].add(ieta, pt, w[v]); } } } } + flushTrackCounts(); - double amplFT0A = 0, amplFT0C = 0; - if (coll.has_foundFT0()) { - const auto& ft0 = coll.foundFT0(); - for (std::size_t iCh = 0; iCh < ft0.channelA().size(); iCh++) { - float ampl = ft0.amplitudeA()[iCh]; - amplFT0A += ampl; - } - for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { - float ampl = ft0.amplitudeC()[iCh]; - amplFT0C += ampl; - } + // --- forward detectors: gain-equalised amplitudes --- + FwdSignal fwd; + if (cfgFwdOpt.cfgFwd) { + loadFwdCalib(coll.template bc_as(), true); + computeFwdReco(coll, vz, fwd, TESTBIT(ev.mask, kSystBase)); } - double p1kBarFt0A = amplFT0A - state.pmeanFT0AmultpvStep2->GetBinContent(state.pmeanFT0AmultpvStep2->GetXaxis()->FindBin(coll.multNTracksPV())); - double p1kBarFt0C = amplFT0C - state.pmeanFT0CmultpvStep2->GetBinContent(state.pmeanFT0CmultpvStep2->GetXaxis()->FindBin(coll.multNTracksPV())); - - for (int ieta = 0; ieta < KNEta; ++ieta) { - const int ibx = state.pmeanNchEtabinSpbinStep2->GetXaxis()->FindBin(coll.multNTracksPV()); - const int iby = ieta + 1; - - for (int isp = 0; isp < KNsp; ++isp) { - const int ibz = isp + 1; - - float mmpt = state.pmeanNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - float mmMult = state.pmeanMultNchEtabinSpbinStep2->GetBinContent(ibx, iby, ibz); - mean[isp][ieta] = sumpmwk[isp][ieta][1][1] / sumwk[isp][ieta][1]; - meanMult[isp][ieta] = sumwk[isp][ieta][1]; - - if (std::isfinite(mmpt)) { - std::tie(mean[isp][ieta], c2[isp][ieta]) = calculateMeanAndC2FromSums(sumpmwk[isp][ieta], sumwk[isp][ieta], mmpt); - p1kBar[isp][ieta] = mean[isp][ieta] - mmpt; - } - p1kBarMult[isp][ieta] = meanMult[isp][ieta] - mmMult; + // --- Poisson bootstrap: one weight per sample per event --- + std::array poisW{}; + if (doBoot) { + for (int s = 0; s < nBoot; ++s) { + poisW[s] = rng.Poisson(1.0); } } - for (int ieta = 0; ieta < KNEta; ++ieta) { - for (int isp = 0; isp < KNsp; ++isp) { - if (std::isfinite(mean[isp][ieta])) { - histos.fill(HIST("Prof_MeanpT_Cent_etabin_spbin"), cent, ieta, isp, mean[isp][ieta]); - histos.fill(HIST("Prof_MeanpT_Mult_etabin_spbin"), coll.multNTracksPV(), ieta, isp, mean[isp][ieta]); - } - if (std::isfinite(c2[isp][ieta])) { - histos.fill(HIST("Prof_C2_Cent_etabin_spbin"), cent, ieta, isp, c2[isp][ieta]); - histos.fill(HIST("Prof_C2_Mult_etabin_spbin"), coll.multNTracksPV(), ieta, isp, c2[isp][ieta]); + + // --- c2, c3, subevent correlators and forward covariances of every variation --- + EventObs evObs; + for (int v = 0; v < kNSyst; ++v) { + if (!TESTBIT(ev.mask, v)) { + continue; + } + computeEventObs(sums[v][0], v, 0, cfgFwdOpt.cfgFwd ? &fwd : nullptr, nCh, evObs); + fillFluc(hists.fluc[v][0], evObs, 1., nCh); + // bootstrap replicas of Base, in the 5% centrality classes + if (v == kSystBase && doBoot) { + for (int s = 0; s < nBoot; ++s) { + if (poisW[s] > 0.) { + fillFluc(hists.boot[s], evObs, poisW[s], ev.cent[kSystBase]); + } } } } + } + PROCESS_SWITCH(RadialFlowDecorr, processDataFluc, "process data to calculate fluc pT", cfgRunDataFluc); - for (int ietaA = 1; ietaA <= (KNEta - 1) / 2; ++ietaA) { - int ietaC = KNEta - ietaA; - for (int isp = 0; isp < KNsp; ++isp) { - float c2Sub = p1kBar[isp][ietaA] * p1kBar[isp][ietaC]; - float covAC = p1kBarMult[isp][ietaA] * p1kBar[isp][ietaC]; - float covCA = p1kBar[isp][ietaA] * p1kBarMult[isp][ietaC]; - - float covFT0A = p1kBarFt0A * p1kBar[isp][ietaC]; - float covFT0C = p1kBarFt0C * p1kBar[isp][ietaA]; - - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub_Cent_etabin_spbin"), cent, ietaA, isp, c2Sub); - histos.fill(HIST("Prof_C2Sub_Mult_etabin_spbin"), coll.multNTracksPV(), ietaA, isp, c2Sub); - } - if (std::isfinite(covAC)) { - histos.fill(HIST("Prof_Cov_Cent_etabin_spbin"), cent, ietaA, isp, covAC); - histos.fill(HIST("Prof_Cov_Mult_etabin_spbin"), coll.multNTracksPV(), ietaA, isp, covAC); - } - if (std::isfinite(covCA)) { - histos.fill(HIST("Prof_Cov_Cent_etabin_spbin"), cent, ietaA, isp, covCA); - histos.fill(HIST("Prof_Cov_Mult_etabin_spbin"), coll.multNTracksPV(), ietaA, isp, covCA); - } - if (std::isfinite(covFT0A)) { - histos.fill(HIST("Prof_CovFT0A_Cent_etabin_spbin"), cent, ietaA, isp, covFT0A); - histos.fill(HIST("Prof_CovFT0A_Mult_etabin_spbin"), coll.multNTracksPV(), ietaA, isp, covFT0A); - } - if (std::isfinite(covFT0C)) { - histos.fill(HIST("Prof_CovFT0C_Cent_etabin_spbin"), cent, ietaA, isp, covFT0C); - histos.fill(HIST("Prof_CovFT0C_Mult_etabin_spbin"), coll.multNTracksPV(), ietaA, isp, covFT0C); - } + // =========================================================================== + // Cut QA + // =========================================================================== + // On the unfiltered collisions and tracks (the analysis passes see only the filtered ones): + // every quantity the Base selection cuts on, before and after it. The counters are not + // filled here. N_ch is the number of tracks of the analysis track filter. + void processCutQA(GeneralCollisions::iterator const& col, UnfilteredTracks const& tracks) + { + int nCh = 0; + for (const auto& trk : tracks) { + if (trk.isGlobalTrack() && trk.pt() > KPtMin && trk.pt() < KPtMax) { + ++nCh; } } - - for (int ietaA = 1; ietaA < KNEta; ++ietaA) { - for (int ietaC = 1; ietaC < KNEta; ++ietaC) { - - float etaValA = (etaLw[ietaA] + etaUp[ietaA]) / 2.0f; - float etaValB = (etaLw[ietaC] + etaUp[ietaC]) / 2.0f; - float gap = etaValA - etaValB; - float sum = (etaValA + etaValB); - - for (int isp = 0; isp < KNsp; ++isp) { - - float c2Sub = p1kBar[isp][ietaA] * p1kBar[isp][ietaC]; - float cov = p1kBarMult[isp][ietaA] * p1kBar[isp][ietaC]; - float covFT0A = p1kBarFt0A * p1kBar[isp][ietaC]; - float covFT0C = p1kBarFt0C * p1kBar[isp][ietaA]; - - if (isp == kInclusiveIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kPiMinusIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_PiMinus"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_PiMinus"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kPiPlusIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_PiPlus"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_PiPlus"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kPiAllIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_PiAll"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_PiAll"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kKaMinusIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_KaMinus"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_KaMinus"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kKaPlusIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_KaPlus"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_KaPlus"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kKaAllIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_KaAll"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_KaAll"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kPrIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_Pr"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_Pr"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kAntiPrIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_AntiPr"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_AntiPr"), cent, etaValA, etaValB, covFT0C); - } else if (isp == kPrAllIdx) { - if (std::isfinite(c2Sub)) { - histos.fill(HIST("Prof_C2Sub2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, c2Sub); - histos.fill(HIST("Prof_GapSum2D_PrAll"), cent, gap, sum, c2Sub); - } - if (std::isfinite(cov)) - histos.fill(HIST("Prof_Cov2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, cov); - if (std::isfinite(covFT0A)) - histos.fill(HIST("Prof_CovFT0A2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0A); - if (std::isfinite(covFT0C)) - histos.fill(HIST("Prof_CovFT0C2D_Cent_etaA_etaC_PrAll"), cent, etaValA, etaValB, covFT0C); - } - } + const float cent = getCentrality(col, cfgEvSel.cfgCentralityChoice); + fillCutQAEvent(kQABefore, col, nCh, cent); + const auto ev = isEventSelected(col, nCh, 1 << kSystBase, false); + if (!TESTBIT(ev.mask, kSystBase)) { + return; + } + fillCutQAEvent(kQAAfter, col, nCh, cent); + for (const auto& trk : tracks) { + fillCutQATrack(kQABefore, trk); + if (TESTBIT(isTrackSelected(trk, 1 << kSystBase, false), kSystBase)) { + fillCutQATrack(kQAAfter, trk); } } } - PROCESS_SWITCH(RadialFlowDecorr, processDataFluc, "process data to calculate fluc pT", cfgRunDataFluc); + PROCESS_SWITCH(RadialFlowDecorr, processCutQA, "QA of the event and track cut quantities before and after the Base selection", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/EbyEFluctuations/Tasks/stronglyIntensiveCorr.cxx b/PWGCF/EbyEFluctuations/Tasks/stronglyIntensiveCorr.cxx new file mode 100644 index 00000000000..6fa2543523b --- /dev/null +++ b/PWGCF/EbyEFluctuations/Tasks/stronglyIntensiveCorr.cxx @@ -0,0 +1,1157 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file stronglyIntensiveCorr.cxx +/// \brief Forward-backward multiplicity correlations for inclusive charged particles. +/// \author Iwona Sputowska + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct StronglyIntensiveCorr { + // ------------------------------------------------------------------ + // Configurables + // ------------------------------------------------------------------ + + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgEtaWidth{"cfgEtaWidth", 0.2f, "FB window width"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "absolute eta cut"}; + Configurable cfgCutPtLower{"cfgCutPtLower", 0.2f, "Lower pT cut"}; + Configurable cfgCutPtUpper{"cfgCutPtUpper", 5.0f, "Upper pT cut"}; + Configurable cfgCutTrackDcaZ{"cfgCutTrackDcaZ", 2.0f, "Maximum DCAz"}; + + Configurable cfgITScluster{"cfgITScluster", 5, "Minimum number of ITS clusters"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 50, "Minimum number of TPC clusters"}; + Configurable cfgTPCnCrossedRows{"cfgTPCnCrossedRows", 70, "Minimum number of TPC crossed rows"}; + Configurable cfgCutTpcChi2NCl{"cfgCutTpcChi2NCl", 2.5f, "Maximum TPC chi2/NCl"}; + Configurable cfgCutItsChi2NCl{"cfgCutItsChi2NCl", 36.0f, "Maximum ITS chi2/NCl"}; + + Configurable cfgRequireGlobalTrack{"cfgRequireGlobalTrack", true, "Require isGlobalTrack OR isGlobalTrackSDD"}; + Configurable cfgUseDCAzCut{"cfgUseDCAzCut", true, "Use DCAz cut"}; + Configurable cfgSel8Trig{"cfgSel8Trig", true, "Require sel8"}; + + Configurable cfgCentMin{"cfgCentMin", 0.0f, "Minimum centrality"}; + Configurable cfgCentMax{"cfgCentMax", 90.0f, "Maximum centrality"}; + Configurable cfgCentralityChoice{"cfgCentralityChoice", 0, "Centrality estimator: 0=FT0C, 1=FT0M"}; + Configurable cfgCentWindowWidth{"cfgCentWindowWidth", 10.0f, "Centrality window width around class centers"}; + + Configurable cfgNSubsamples{"cfgNSubsamples", 20, "Number of subsamples; max is NSubsamplesMax"}; + + Configurable cfgEvSelkNoSameBunchPileup{"cfgEvSelkNoSameBunchPileup", true, "Pileup removal"}; + Configurable cfgUseGoodITSLayerAllCut{"cfgUseGoodITSLayerAllCut", true, "Remove time intervals with bad ITS layers"}; + Configurable cfgEvSelUseGoodZvtxFT0vsPV{"cfgEvSelUseGoodZvtxFT0vsPV", true, "Good z-vertex FT0 vs PV cut"}; + + Configurable cfgUseOnlyPhysicalPrimary{"cfgUseOnlyPhysicalPrimary", true, "MC truth: require physical primary"}; + Configurable cfgUseOnlyChargedMC{"cfgUseOnlyChargedMC", true, "MC truth: require charged particle"}; + + // ------------------------------------------------------------------ + // FB binning + // ------------------------------------------------------------------ + static constexpr int NEtaGaps = 8; + static constexpr int NPtBins = 6; + static constexpr int NPhiBins = 16; + static constexpr int NSubsamplesMax = 20; + static constexpr int NCentClasses = 8; + static constexpr double TwoPi = 6.28318530717958647692; + + std::array etaCenter = {0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0}; + + std::array ptEdges = {0.2, 0.5, 0.8, 1.0, 1.5, 2.0, 5.0}; + std::array centEdges = {0., 10., 20., 30., 40., 50., 60., 70., 80.}; + + using EtaPtPhiArray = std::array, NPtBins>, NEtaGaps>; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service pdg{}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + + // Intentionally loose track filter: manual cuts are done in selTrack(), so QA before/after sees tracks. + Filter trackFilter = (aod::track::pt > -1.0f); + + using AodCollisions = soa::Filtered>; + using AodCollision = AodCollisions::iterator; + + using AodTracks = soa::Filtered>; + + using MyMCRecCollisions = soa::Filtered>; + using MyMCRecCollision = MyMCRecCollisions::iterator; + + using MyMCTracks = soa::Filtered>; + + Preslice perCollision = aod::track::collisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + + // ------------------------------------------------------------------ + // Init + // ------------------------------------------------------------------ + void init(InitContext&) + { + AxisSpec centAxis = {100, 0.0, 100.0, "centrality (%)"}; + std::vector centClassEdges(centEdges.begin(), centEdges.end()); + AxisSpec centClassAxis = {centClassEdges, "centrality class (%)"}; + AxisSpec etaGapAxis = {NEtaGaps, -0.05, 1.55, "#Delta#eta"}; + AxisSpec nAxis = {500, 0.0, 500.0, "N"}; + AxisSpec counterAxis = {20, 0.5, 20.5, "counter"}; + AxisSpec ptAxis = {{0.2, 0.5, 0.8, 1.0, 1.5, 2.0, 5.0}, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec phiAxis = {NPhiBins, 0.0, TwoPi, "#varphi"}; + AxisSpec subsampleAxis = {NSubsamplesMax, -0.5, static_cast(NSubsamplesMax) - 0.5, "subsample"}; + + AxisSpec nchAxis = {500, 0.0, 500.0, "N_{ch}"}; + AxisSpec nchPvAxis = {1000, 0.0, 1000.0, "N_{ch}^{PV}"}; + AxisSpec itsClsAxis = {21, -0.5, 20.5, "N ITS clusters"}; + AxisSpec tpcClsAxis = {201, -0.5, 200.5, "N TPC clusters / crossed rows"}; + AxisSpec chi2ITSAxis = {100, 0.0, 50.0, "#chi^{2}_{ITS}/N_{cls}"}; + AxisSpec chi2TPCAxis = {100, 0.0, 5.0, "#chi^{2}_{TPC}/N_{cls}"}; + AxisSpec dcaXYAxis = {200, -0.5, 0.5, "DCA_{xy} (cm)"}; + AxisSpec dcaZAxis = {200, -2.0, 2.0, "DCA_{z} (cm)"}; + AxisSpec etaAxis = {160, -1.6, 1.6, "#eta"}; + AxisSpec ptFullAxis = {200, 0.0, 10.0, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec phiAxisFull = {64, 0.0, TwoPi, "#varphi"}; + AxisSpec chargeAxis = {5, -2.5, 2.5, "charge"}; + AxisSpec vtxZAxis = {240, -30.0, 30.0, "V_{z} (cm)"}; + + // Event QA + histos.add("QA/eventCounter", "event counter", kTH1D, {counterAxis}); + histos.add("QA/trackCounter", "track counter", kTH1D, {counterAxis}); + histos.add("QA/hTrackSliceSize", "number of tracks in collision-associated slice before manual cuts", kTH1D, {{500, -0.5, 499.5}}); + histos.add("QA/hGlobalTrackFlags", "global-track flags: 1=all, 2=isGlobalTrack, 3=isGlobalTrackSDD, 4=global OR SDD, 5=fail both", kTH1D, {counterAxis}); + histos.add("QA/hCentrality", "centrality", kTH1D, {centAxis}); + histos.add("QA/hNF", "nF", kTH1D, {nAxis}); + histos.add("QA/hNB", "nB", kTH1D, {nAxis}); + histos.add("QA/hNFvsNB", "nF vs nB", kTH2D, {nAxis, nAxis}); + histos.add("QA/fb/nFvsCent", "nF vs centrality;centrality (%);nF", kTH2D, {centAxis, nAxis}); + histos.add("QA/fb/nBvsCent", "nB vs centrality;centrality (%);nB", kTH2D, {centAxis, nAxis}); + histos.add("QA/fb/nFvsNB", "nF vs nB;nF;nB", kTH2D, {nAxis, nAxis}); + histos.add("QA/fb/nFminusNB", "nF-nB;nF-nB;Counts", kTH1D, {{500, -250.0, 250.0}}); + histos.add("QA/fb/nFplusNB", "nF+nB;nF+nB;Counts", kTH1D, {{500, 0.0, 500.0}}); + + histos.add("QA/mcReco/nRecoAll", "MC reco all selected;n;Counts", kTH1D, {nAxis}); + histos.add("QA/mcReco/nRecoPrimary", "MC reco primary selected;n;Counts", kTH1D, {nAxis}); + histos.add("QA/mcReco/nRecoSecondary", "MC reco secondary selected;n;Counts", kTH1D, {nAxis}); + histos.add("QA/mcReco/nRecoFake", "MC reco fake/unmatched selected;n;Counts", kTH1D, {nAxis}); + + histos.add("eventQA/before/nPVeta1TracksVsCent", "before;centrality (%);N_{ch}^{PV, |#eta|<1}", kTH2D, {centAxis, nchAxis}); + histos.add("eventQA/after/nPVeta1TracksVsCent", "after;centrality (%);N_{ch}^{PV, |#eta|<1}", kTH2D, {centAxis, nchAxis}); + histos.add("eventQA/before/nPVTracksVsCent", "before;centrality (%);N_{ch}^{PV}", kTH2D, {centAxis, nchPvAxis}); + histos.add("eventQA/after/nPVTracksVsCent", "after;centrality (%);N_{ch}^{PV}", kTH2D, {centAxis, nchPvAxis}); + histos.add("eventQA/before/vtxZ", "before event selection;V_{z} (cm);Counts", kTH1D, {vtxZAxis}); + histos.add("eventQA/after/vtxZ", "after event selection;V_{z} (cm);Counts", kTH1D, {vtxZAxis}); + histos.add("eventQA/before/centFT0C", "before;FT0C centrality (%);Counts", kTH1D, {centAxis}); + histos.add("eventQA/after/centFT0C", "after;FT0C centrality (%);Counts", kTH1D, {centAxis}); + histos.add("eventQA/before/centFT0M", "before;FT0M centrality (%);Counts", kTH1D, {centAxis}); + histos.add("eventQA/after/centFT0M", "after;FT0M centrality (%);Counts", kTH1D, {centAxis}); + histos.add("eventQA/before/centFT0CvsFT0M", "before;FT0C;FT0M", kTH2D, {centAxis, centAxis}); + histos.add("eventQA/after/centFT0CvsFT0M", "after;FT0C;FT0M", kTH2D, {centAxis, centAxis}); + + // Track QA + histos.add("trackQA/before/nITSClusters", "before;N ITS clusters;Counts", kTH1D, {itsClsAxis}); + histos.add("trackQA/after/nITSClusters", "after;N ITS clusters;Counts", kTH1D, {itsClsAxis}); + histos.add("trackQA/before/chi2prITScls", "before;#chi^{2}_{ITS}/N_{cls};Counts", kTH1D, {chi2ITSAxis}); + histos.add("trackQA/after/chi2prITScls", "after;#chi^{2}_{ITS}/N_{cls};Counts", kTH1D, {chi2ITSAxis}); + histos.add("trackQA/before/nTPCClusters", "before;N TPC clusters;Counts", kTH1D, {tpcClsAxis}); + histos.add("trackQA/after/nTPCClusters", "after;N TPC clusters;Counts", kTH1D, {tpcClsAxis}); + histos.add("trackQA/before/chi2prTPCcls", "before;#chi^{2}_{TPC}/N_{cls};Counts", kTH1D, {chi2TPCAxis}); + histos.add("trackQA/after/chi2prTPCcls", "after;#chi^{2}_{TPC}/N_{cls};Counts", kTH1D, {chi2TPCAxis}); + histos.add("trackQA/before/nTPCCrossedRows", "before;N TPC crossed rows;Counts", kTH1D, {tpcClsAxis}); + histos.add("trackQA/after/nTPCCrossedRows", "after;N TPC crossed rows;Counts", kTH1D, {tpcClsAxis}); + histos.add("trackQA/before/eta", "before;#eta;Counts", kTH1D, {etaAxis}); + histos.add("trackQA/after/eta", "after;#eta;Counts", kTH1D, {etaAxis}); + histos.add("trackQA/before/pt", "before;#it{p}_{T} (GeV/#it{c});Counts", kTH1D, {ptFullAxis}); + histos.add("trackQA/after/pt", "after;#it{p}_{T} (GeV/#it{c});Counts", kTH1D, {ptFullAxis}); + histos.add("trackQA/before/dcaXY", "before;DCA_{xy} (cm);Counts", kTH1D, {dcaXYAxis}); + histos.add("trackQA/after/dcaXY", "after;DCA_{xy} (cm);Counts", kTH1D, {dcaXYAxis}); + histos.add("trackQA/before/dcaZ", "before;DCA_{z} (cm);Counts", kTH1D, {dcaZAxis}); + histos.add("trackQA/after/dcaZ", "after;DCA_{z} (cm);Counts", kTH1D, {dcaZAxis}); + histos.add("trackQA/before/phi", "before;#varphi;Counts", kTH1D, {phiAxisFull}); + histos.add("trackQA/after/phi", "after;#varphi;Counts", kTH1D, {phiAxisFull}); + histos.add("trackQA/before/charge", "before;charge;Counts", kTH1D, {chargeAxis}); + histos.add("trackQA/after/charge", "after;charge;Counts", kTH1D, {chargeAxis}); + histos.add("trackQA/before/ptVsEta", "before;#eta;#it{p}_{T}", kTH2D, {etaAxis, ptFullAxis}); + histos.add("trackQA/after/ptVsEta", "after;#eta;#it{p}_{T}", kTH2D, {etaAxis, ptFullAxis}); + histos.add("trackQA/before/dcaXYvsPt", "before;DCA_{xy};#it{p}_{T}", kTH2D, {dcaXYAxis, ptFullAxis}); + histos.add("trackQA/after/dcaXYvsPt", "after;DCA_{xy};#it{p}_{T}", kTH2D, {dcaXYAxis, ptFullAxis}); + histos.add("trackQA/before/dcaZvsPt", "before;DCA_{z};#it{p}_{T}", kTH2D, {dcaZAxis, ptFullAxis}); + histos.add("trackQA/after/dcaZvsPt", "after;DCA_{z};#it{p}_{T}", kTH2D, {dcaZAxis, ptFullAxis}); + + // Chosen centrality binning, default 0-10 ... 70-80. + + histos.add("SIcentClass/pNF_cent_etaGap", ";centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentClass/pNB_cent_etaGap", ";centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentClass/pNF2_cent_etaGap", ";centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentClass/pNB2_cent_etaGap", ";centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentClass/pNFNB_cent_etaGap", ";centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + + histos.add("SIcentWindow/pNF_cent_etaGap", " in centrality window;centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentWindow/pNB_cent_etaGap", " in centrality window;centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentWindow/pNF2_cent_etaGap", " in centrality window;centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentWindow/pNB2_cent_etaGap", " in centrality window;centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + histos.add("SIcentWindow/pNFNB_cent_etaGap", " in centrality window;centrality class (%);#Delta#eta", kTProfile2D, {centClassAxis, etaGapAxis}); + + // Differential eta-gap x pT x phi moments. + histos.add("SI3D/pNF_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("SI3D/pNB_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("SI3D/pNF2_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("SI3D/pNB2_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("SI3D/pNFNB_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("SI3D/pF20_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("SI3D/pB20_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("SI3D/pF11_etaGapPtPhi", ";#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + + // Subsample centrality-window output. + histos.add("SubsampleCentWindow/pNF_sub_cent_etaGap", " subsamples;subsample;centrality class (%);#Delta#eta", kTProfile3D, {subsampleAxis, centClassAxis, etaGapAxis}); + histos.add("SubsampleCentWindow/pNB_sub_cent_etaGap", " subsamples;subsample;centrality class (%);#Delta#eta", kTProfile3D, {subsampleAxis, centClassAxis, etaGapAxis}); + histos.add("SubsampleCentWindow/pNF2_sub_cent_etaGap", " subsamples;subsample;centrality class (%);#Delta#eta", kTProfile3D, {subsampleAxis, centClassAxis, etaGapAxis}); + histos.add("SubsampleCentWindow/pNB2_sub_cent_etaGap", " subsamples;subsample;centrality class (%);#Delta#eta", kTProfile3D, {subsampleAxis, centClassAxis, etaGapAxis}); + histos.add("SubsampleCentWindow/pNFNB_sub_cent_etaGap", " subsamples;subsample;centrality class (%);#Delta#eta", kTProfile3D, {subsampleAxis, centClassAxis, etaGapAxis}); + + // THnSparse sums per subsample for differential studies. + histos.add("SubsampleSI3D/EventCount_sub_etaGapPtPhi", "event count;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumNF_sub_etaGapPtPhi", "sum nF;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumNB_sub_etaGapPtPhi", "sum nB;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumNF2_sub_etaGapPtPhi", "sum nF^{2};subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumNB2_sub_etaGapPtPhi", "sum nB^{2};subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumNFNB_sub_etaGapPtPhi", "sum nF nB;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumF20_sub_etaGapPtPhi", "sum nF(nF-1);subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumB20_sub_etaGapPtPhi", "sum nB(nB-1);subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleSI3D/SumF11_sub_etaGapPtPhi", "sum nF nB;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + + histos.add("Reco/SI3D/pNF_etaGapPtPhi", " reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Reco/SI3D/pNB_etaGapPtPhi", " reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Reco/SI3D/pNF2_etaGapPtPhi", " reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Reco/SI3D/pNB2_etaGapPtPhi", " reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Reco/SI3D/pNFNB_etaGapPtPhi", " reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + + histos.add("Prim/SI3D/pNF_etaGapPtPhi", " primary reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Prim/SI3D/pNB_etaGapPtPhi", " primary reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Prim/SI3D/pNF2_etaGapPtPhi", " primary reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Prim/SI3D/pNB2_etaGapPtPhi", " primary reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Prim/SI3D/pNFNB_etaGapPtPhi", " primary reco;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + + histos.add("Gen/SI3D/pNF_etaGapPtPhi", " generated;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Gen/SI3D/pNB_etaGapPtPhi", " generated;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Gen/SI3D/pNF2_etaGapPtPhi", " generated;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Gen/SI3D/pNB2_etaGapPtPhi", " generated;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + histos.add("Gen/SI3D/pNFNB_etaGapPtPhi", " generated;#Delta#eta;#it{p}_{T};#varphi", kTProfile3D, {etaGapAxis, ptAxis, phiAxis}); + + // Sample-separated MC subsample sums for SI3D. + // Axes: subsample x eta gap x pT x phi. EventCount is the denominator. + histos.add("SubsampleRecoSI3D/EventCount_sub_etaGapPtPhi", "event count reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleRecoSI3D/SumNF_sub_etaGapPtPhi", "sum nF reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleRecoSI3D/SumNB_sub_etaGapPtPhi", "sum nB reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleRecoSI3D/SumNF2_sub_etaGapPtPhi", "sum nF^{2} reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleRecoSI3D/SumNB2_sub_etaGapPtPhi", "sum nB^{2} reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleRecoSI3D/SumNFNB_sub_etaGapPtPhi", "sum nF nB reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + + histos.add("SubsamplePrimSI3D/EventCount_sub_etaGapPtPhi", "event count primary reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsamplePrimSI3D/SumNF_sub_etaGapPtPhi", "sum nF primary reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsamplePrimSI3D/SumNB_sub_etaGapPtPhi", "sum nB primary reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsamplePrimSI3D/SumNF2_sub_etaGapPtPhi", "sum nF^{2} primary reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsamplePrimSI3D/SumNB2_sub_etaGapPtPhi", "sum nB^{2} primary reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsamplePrimSI3D/SumNFNB_sub_etaGapPtPhi", "sum nF nB primary reco;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + + histos.add("SubsampleGenSI3D/EventCount_sub_etaGapPtPhi", "event count generated;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleGenSI3D/SumNF_sub_etaGapPtPhi", "sum nF generated;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleGenSI3D/SumNB_sub_etaGapPtPhi", "sum nB generated;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleGenSI3D/SumNF2_sub_etaGapPtPhi", "sum nF^{2} generated;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleGenSI3D/SumNB2_sub_etaGapPtPhi", "sum nB^{2} generated;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + histos.add("SubsampleGenSI3D/SumNFNB_sub_etaGapPtPhi", "sum nF nB generated;subsample;#Delta#eta;#it{p}_{T};#varphi", kTHnSparseD, {subsampleAxis, etaGapAxis, ptAxis, phiAxis}); + } + + // ------------------------------------------------------------------ + // Helpers + // ------------------------------------------------------------------ + template + float getCentrality(C const& coll) const + { + switch (cfgCentralityChoice.value) { + case 0: + return coll.centFT0C(); + case 1: + return coll.centFT0M(); + default: + return -999.f; + } + } + + int getCentClass(float cent) const + { + for (int ic = 0; ic < NCentClasses; ++ic) { + if (cent >= centEdges[ic] && cent < centEdges[ic + 1]) { + return ic; + } + } + return -1; + } + + double getCentClassCenter(int ic) const + { + if (ic < 0 || ic >= NCentClasses) { + return -999.0; + } + return 0.5 * (centEdges[ic] + centEdges[ic + 1]); + } + + int getCentWindowClass(float cent) const + { + const double halfWidth = 0.5 * static_cast(cfgCentWindowWidth.value); + if (halfWidth <= 0.0) { + return -1; + } + + for (int ic = 0; ic < NCentClasses; ++ic) { + const double center = getCentClassCenter(ic); + if (cent >= center - halfWidth && cent < center + halfWidth) { + return ic; + } + } + return -1; + } + + template + int getSubsampleIndex(C const& coll) const + { + const int nSubsamples = std::min(std::max(cfgNSubsamples.value, 0), NSubsamplesMax); + if (nSubsamples <= 0) { + return -1; + } + return static_cast(coll.globalIndex() % nSubsamples); + } + + int getPtBin(double pt) const + { + for (int ipt = 0; ipt < NPtBins; ++ipt) { + if (pt >= ptEdges[ipt] && pt < ptEdges[ipt + 1]) { + return ipt; + } + } + return -1; + } + + int getPhiBin(double phi) const + { + while (phi < 0.0) { + phi += TwoPi; + } + while (phi >= TwoPi) { + phi -= TwoPi; + } + + const int iphi = static_cast(phi / TwoPi * NPhiBins); + if (iphi < 0 || iphi >= NPhiBins) { + return -1; + } + return iphi; + } + + bool isChargedMCParticle(int pdgCode) const + { + auto* particle = pdg->GetParticle(pdgCode); + if (!particle) { + return false; + } + + constexpr double MinAbsCharge = 0.0; + return std::abs(particle->Charge()) > MinAbsCharge; + } + + template + bool selCollision(C const& coll, float& cent) + { + histos.fill(HIST("QA/eventCounter"), 1); + + if (std::abs(coll.posZ()) > cfgCutVertex.value) { + return false; + } + histos.fill(HIST("QA/eventCounter"), 2); + + if (cfgSel8Trig.value && !coll.sel8()) { + return false; + } + histos.fill(HIST("QA/eventCounter"), 3); + + if (cfgUseGoodITSLayerAllCut.value && !coll.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + histos.fill(HIST("QA/eventCounter"), 4); + + if (cfgEvSelkNoSameBunchPileup.value && !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("QA/eventCounter"), 5); + + if (cfgEvSelUseGoodZvtxFT0vsPV.value && !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("QA/eventCounter"), 6); + + constexpr float MinCentrality = 0.f; + constexpr float MaxCentrality = 100.f; + + cent = getCentrality(coll); + if (!std::isfinite(cent) || cent < MinCentrality || cent >= MaxCentrality) { + return false; + } + if (cent < cfgCentMin.value || cent >= cfgCentMax.value) { + return false; + } + histos.fill(HIST("QA/eventCounter"), 7); + + return true; + } + + template + void fillTrackQABefore(T const& track) + { + histos.fill(HIST("trackQA/before/nITSClusters"), track.itsNCls()); + histos.fill(HIST("trackQA/before/chi2prITScls"), track.itsChi2NCl()); + histos.fill(HIST("trackQA/before/nTPCClusters"), track.tpcNClsFound()); + histos.fill(HIST("trackQA/before/chi2prTPCcls"), track.tpcChi2NCl()); + histos.fill(HIST("trackQA/before/nTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("trackQA/before/eta"), track.eta()); + histos.fill(HIST("trackQA/before/pt"), track.pt()); + histos.fill(HIST("trackQA/before/dcaXY"), track.dcaXY()); + histos.fill(HIST("trackQA/before/dcaZ"), track.dcaZ()); + histos.fill(HIST("trackQA/before/phi"), track.phi()); + histos.fill(HIST("trackQA/before/charge"), track.sign()); + histos.fill(HIST("trackQA/before/ptVsEta"), track.eta(), track.pt()); + histos.fill(HIST("trackQA/before/dcaXYvsPt"), track.dcaXY(), track.pt()); + histos.fill(HIST("trackQA/before/dcaZvsPt"), track.dcaZ(), track.pt()); + } + + template + void fillTrackQAAfter(T const& track) + { + histos.fill(HIST("trackQA/after/nITSClusters"), track.itsNCls()); + histos.fill(HIST("trackQA/after/chi2prITScls"), track.itsChi2NCl()); + histos.fill(HIST("trackQA/after/nTPCClusters"), track.tpcNClsFound()); + histos.fill(HIST("trackQA/after/chi2prTPCcls"), track.tpcChi2NCl()); + histos.fill(HIST("trackQA/after/nTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("trackQA/after/dcaXYvsPt"), track.dcaXY(), track.pt()); + histos.fill(HIST("trackQA/after/dcaZvsPt"), track.dcaZ(), track.pt()); + histos.fill(HIST("trackQA/after/eta"), track.eta()); + histos.fill(HIST("trackQA/after/pt"), track.pt()); + histos.fill(HIST("trackQA/after/dcaXY"), track.dcaXY()); + histos.fill(HIST("trackQA/after/dcaZ"), track.dcaZ()); + histos.fill(HIST("trackQA/after/phi"), track.phi()); + histos.fill(HIST("trackQA/after/charge"), track.sign()); + histos.fill(HIST("trackQA/after/ptVsEta"), track.eta(), track.pt()); + } + + template + bool selTrack(T const& track) + { + histos.fill(HIST("QA/trackCounter"), 1); + + histos.fill(HIST("QA/hGlobalTrackFlags"), 1); + const bool isGlobal = track.isGlobalTrack(); + const bool isGlobalSDD = track.isGlobalTrackSDD(); + if (isGlobal) { + histos.fill(HIST("QA/hGlobalTrackFlags"), 2); + } + if (isGlobalSDD) { + histos.fill(HIST("QA/hGlobalTrackFlags"), 3); + } + if (isGlobal || isGlobalSDD) { + histos.fill(HIST("QA/hGlobalTrackFlags"), 4); + } else { + histos.fill(HIST("QA/hGlobalTrackFlags"), 5); + } + + if (cfgRequireGlobalTrack.value && !(isGlobal || isGlobalSDD)) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 2); + + if (track.tpcNClsFound() < cfgTPCcluster.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 3); + + if (track.tpcNClsCrossedRows() < cfgTPCnCrossedRows.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 4); + + if (track.itsNCls() < cfgITScluster.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 5); + + if (track.tpcChi2NCl() >= cfgCutTpcChi2NCl.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 6); + + if (track.itsChi2NCl() >= cfgCutItsChi2NCl.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 7); + + if (track.pt() < cfgCutPtLower.value || track.pt() >= cfgCutPtUpper.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 8); + + if (std::abs(track.eta()) >= cfgCutEta.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 9); + + // No DCAxy cut here. It is kept only for QA. + histos.fill(HIST("QA/trackCounter"), 10); + + if (cfgUseDCAzCut.value && std::abs(track.dcaZ()) > cfgCutTrackDcaZ.value) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 11); + + if (track.sign() == 0) { + return false; + } + histos.fill(HIST("QA/trackCounter"), 12); + + return true; + } + + template + bool selMCParticle(P const& particle) + { + if (cfgUseOnlyPhysicalPrimary.value && !particle.isPhysicalPrimary()) { + return false; + } + if (cfgUseOnlyChargedMC.value && !isChargedMCParticle(particle.pdgCode())) { + return false; + } + if (particle.pt() < cfgCutPtLower.value || particle.pt() >= cfgCutPtUpper.value) { + return false; + } + if (std::abs(particle.eta()) >= cfgCutEta.value) { + return false; + } + return true; + } + + void addToFBCounters(double eta, double pt, double phi, + std::array& nF, + std::array& nB, + EtaPtPhiArray& nFPtPhi, + EtaPtPhiArray& nBPtPhi) + { + const int ipt = getPtBin(pt); + const int iphi = getPhiBin(phi); + const bool fillPtPhi = (ipt >= 0 && iphi >= 0); + + for (int i = 0; i < NEtaGaps; ++i) { + const double etaLow = etaCenter[i] - cfgEtaWidth.value / 2.0; + const double etaHigh = etaCenter[i] + cfgEtaWidth.value / 2.0; + + if (eta > etaLow && eta < etaHigh) { + nF[i] += 1.0; + if (fillPtPhi) { + nFPtPhi[i][ipt][iphi] += 1.0; + } + } + + if (eta > -etaHigh && eta < -etaLow) { + nB[i] += 1.0; + if (fillPtPhi) { + nBPtPhi[i][ipt][iphi] += 1.0; + } + } + } + } + + void fillFBQA(float cent, + std::array const& nF, + std::array const& nB) + { + constexpr int DefaultGapBin = 0; + const double nf = nF[DefaultGapBin]; + const double nb = nB[DefaultGapBin]; + + histos.fill(HIST("QA/fb/nFvsCent"), cent, nf); + histos.fill(HIST("QA/fb/nBvsCent"), cent, nb); + histos.fill(HIST("QA/fb/nFvsNB"), nf, nb); + histos.fill(HIST("QA/fb/nFminusNB"), nf - nb); + histos.fill(HIST("QA/fb/nFplusNB"), nf + nb); + } + + void fillInclusiveEtaGapMoments(std::array const& nF, + std::array const& nB) + { + for (int i = 0; i < NEtaGaps; ++i) { + + histos.fill(HIST("QA/hNF"), nF[i]); + histos.fill(HIST("QA/hNB"), nB[i]); + histos.fill(HIST("QA/hNFvsNB"), nF[i], nB[i]); + } + } + + void fillCentClassEtaGapMoments(int centClass, + std::array const& nF, + std::array const& nB) + { + if (centClass < 0 || centClass >= NCentClasses) { + return; + } + + const double centCenter = getCentClassCenter(centClass); + for (int i = 0; i < NEtaGaps; ++i) { + const double gap = 2.0 * (etaCenter[i] - cfgEtaWidth.value / 2.0); + histos.fill(HIST("SIcentClass/pNF_cent_etaGap"), centCenter, gap, nF[i]); + histos.fill(HIST("SIcentClass/pNB_cent_etaGap"), centCenter, gap, nB[i]); + histos.fill(HIST("SIcentClass/pNF2_cent_etaGap"), centCenter, gap, nF[i] * nF[i]); + histos.fill(HIST("SIcentClass/pNB2_cent_etaGap"), centCenter, gap, nB[i] * nB[i]); + histos.fill(HIST("SIcentClass/pNFNB_cent_etaGap"), centCenter, gap, nF[i] * nB[i]); + } + } + + void fillCentWindowEtaGapMoments(int centWindowClass, + std::array const& nF, + std::array const& nB) + { + if (centWindowClass < 0 || centWindowClass >= NCentClasses) { + return; + } + + const double centCenter = getCentClassCenter(centWindowClass); + for (int i = 0; i < NEtaGaps; ++i) { + const double gap = 2.0 * (etaCenter[i] - cfgEtaWidth.value / 2.0); + histos.fill(HIST("SIcentWindow/pNF_cent_etaGap"), centCenter, gap, nF[i]); + histos.fill(HIST("SIcentWindow/pNB_cent_etaGap"), centCenter, gap, nB[i]); + histos.fill(HIST("SIcentWindow/pNF2_cent_etaGap"), centCenter, gap, nF[i] * nF[i]); + histos.fill(HIST("SIcentWindow/pNB2_cent_etaGap"), centCenter, gap, nB[i] * nB[i]); + histos.fill(HIST("SIcentWindow/pNFNB_cent_etaGap"), centCenter, gap, nF[i] * nB[i]); + } + } + + void fillSubsampleCentWindowEtaGapMoments(int isub, + int centWindowClass, + std::array const& nF, + std::array const& nB) + { + if (isub < 0 || isub >= NSubsamplesMax) { + return; + } + if (centWindowClass < 0 || centWindowClass >= NCentClasses) { + return; + } + + const double centCenter = getCentClassCenter(centWindowClass); + for (int i = 0; i < NEtaGaps; ++i) { + const double gap = 2.0 * (etaCenter[i] - cfgEtaWidth.value / 2.0); + histos.fill(HIST("SubsampleCentWindow/pNF_sub_cent_etaGap"), isub, centCenter, gap, nF[i]); + histos.fill(HIST("SubsampleCentWindow/pNB_sub_cent_etaGap"), isub, centCenter, gap, nB[i]); + histos.fill(HIST("SubsampleCentWindow/pNF2_sub_cent_etaGap"), isub, centCenter, gap, nF[i] * nF[i]); + histos.fill(HIST("SubsampleCentWindow/pNB2_sub_cent_etaGap"), isub, centCenter, gap, nB[i] * nB[i]); + histos.fill(HIST("SubsampleCentWindow/pNFNB_sub_cent_etaGap"), isub, centCenter, gap, nF[i] * nB[i]); + } + } + + void fillDifferentialEtaPtPhiMoments(EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("SI3D/pNF_etaGapPtPhi"), gap, ptCenter, phiCenter, nf); + histos.fill(HIST("SI3D/pNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nb); + histos.fill(HIST("SI3D/pNF2_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("SI3D/pNB2_etaGapPtPhi"), gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("SI3D/pNFNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nb); + histos.fill(HIST("SI3D/pF20_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * (nf - 1.0)); + histos.fill(HIST("SI3D/pB20_etaGapPtPhi"), gap, ptCenter, phiCenter, nb * (nb - 1.0)); + histos.fill(HIST("SI3D/pF11_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + + void fillRecoDifferentialEtaPtPhiMoments(EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("Reco/SI3D/pNF_etaGapPtPhi"), gap, ptCenter, phiCenter, nf); + histos.fill(HIST("Reco/SI3D/pNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nb); + histos.fill(HIST("Reco/SI3D/pNF2_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("Reco/SI3D/pNB2_etaGapPtPhi"), gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("Reco/SI3D/pNFNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + void fillPrimDifferentialEtaPtPhiMoments(EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("Prim/SI3D/pNF_etaGapPtPhi"), gap, ptCenter, phiCenter, nf); + histos.fill(HIST("Prim/SI3D/pNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nb); + histos.fill(HIST("Prim/SI3D/pNF2_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("Prim/SI3D/pNB2_etaGapPtPhi"), gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("Prim/SI3D/pNFNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + void fillGenDifferentialEtaPtPhiMoments(EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("Gen/SI3D/pNF_etaGapPtPhi"), gap, ptCenter, phiCenter, nf); + histos.fill(HIST("Gen/SI3D/pNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nb); + histos.fill(HIST("Gen/SI3D/pNF2_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("Gen/SI3D/pNB2_etaGapPtPhi"), gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("Gen/SI3D/pNFNB_etaGapPtPhi"), gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + + void fillRecoSubsampleDifferentialEtaPtPhiMoments(int isub, + EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + if (isub < 0 || isub >= NSubsamplesMax) { + return; + } + + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("SubsampleRecoSI3D/EventCount_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, 1.0); + histos.fill(HIST("SubsampleRecoSI3D/SumNF_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf); + histos.fill(HIST("SubsampleRecoSI3D/SumNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb); + histos.fill(HIST("SubsampleRecoSI3D/SumNF2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("SubsampleRecoSI3D/SumNB2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("SubsampleRecoSI3D/SumNFNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + + void fillPrimSubsampleDifferentialEtaPtPhiMoments(int isub, + EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + if (isub < 0 || isub >= NSubsamplesMax) { + return; + } + + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("SubsamplePrimSI3D/EventCount_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, 1.0); + histos.fill(HIST("SubsamplePrimSI3D/SumNF_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf); + histos.fill(HIST("SubsamplePrimSI3D/SumNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb); + histos.fill(HIST("SubsamplePrimSI3D/SumNF2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("SubsamplePrimSI3D/SumNB2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("SubsamplePrimSI3D/SumNFNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + + void fillGenSubsampleDifferentialEtaPtPhiMoments(int isub, + EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + if (isub < 0 || isub >= NSubsamplesMax) { + return; + } + + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("SubsampleGenSI3D/EventCount_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, 1.0); + histos.fill(HIST("SubsampleGenSI3D/SumNF_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf); + histos.fill(HIST("SubsampleGenSI3D/SumNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb); + histos.fill(HIST("SubsampleGenSI3D/SumNF2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("SubsampleGenSI3D/SumNB2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("SubsampleGenSI3D/SumNFNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + + void fillSubsampleDifferentialEtaPtPhiMoments(int isub, + EtaPtPhiArray const& nF, + EtaPtPhiArray const& nB) + { + if (isub < 0 || isub >= NSubsamplesMax) { + return; + } + + for (int igap = 0; igap < NEtaGaps; ++igap) { + const double gap = 2.0 * (etaCenter[igap] - cfgEtaWidth.value / 2.0); + for (int ipt = 0; ipt < NPtBins; ++ipt) { + const double ptCenter = 0.5 * (ptEdges[ipt] + ptEdges[ipt + 1]); + for (int iphi = 0; iphi < NPhiBins; ++iphi) { + const double phiCenter = (iphi + 0.5) * TwoPi / NPhiBins; + const double nf = nF[igap][ipt][iphi]; + const double nb = nB[igap][ipt][iphi]; + + histos.fill(HIST("SubsampleSI3D/EventCount_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, 1.0); + histos.fill(HIST("SubsampleSI3D/SumNF_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf); + histos.fill(HIST("SubsampleSI3D/SumNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb); + histos.fill(HIST("SubsampleSI3D/SumNF2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nf); + histos.fill(HIST("SubsampleSI3D/SumNB2_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb * nb); + histos.fill(HIST("SubsampleSI3D/SumNFNB_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nb); + histos.fill(HIST("SubsampleSI3D/SumF20_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * (nf - 1.0)); + histos.fill(HIST("SubsampleSI3D/SumB20_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nb * (nb - 1.0)); + histos.fill(HIST("SubsampleSI3D/SumF11_sub_etaGapPtPhi"), isub, gap, ptCenter, phiCenter, nf * nb); + } + } + } + } + + template + void fillEventQABefore(C const& coll, float centBefore) + { + histos.fill(HIST("eventQA/before/vtxZ"), coll.posZ()); + histos.fill(HIST("eventQA/before/nPVeta1TracksVsCent"), centBefore, coll.multNTracksPVeta1()); + histos.fill(HIST("eventQA/before/nPVTracksVsCent"), centBefore, coll.multNTracksPV()); + histos.fill(HIST("eventQA/before/centFT0C"), coll.centFT0C()); + histos.fill(HIST("eventQA/before/centFT0M"), coll.centFT0M()); + histos.fill(HIST("eventQA/before/centFT0CvsFT0M"), coll.centFT0C(), coll.centFT0M()); + } + + template + void fillEventQAAfter(C const& coll, float cent) + { + histos.fill(HIST("eventQA/after/vtxZ"), coll.posZ()); + histos.fill(HIST("eventQA/after/nPVeta1TracksVsCent"), cent, coll.multNTracksPVeta1()); + histos.fill(HIST("eventQA/after/nPVTracksVsCent"), cent, coll.multNTracksPV()); + histos.fill(HIST("eventQA/after/centFT0C"), coll.centFT0C()); + histos.fill(HIST("eventQA/after/centFT0M"), coll.centFT0M()); + histos.fill(HIST("eventQA/after/centFT0CvsFT0M"), coll.centFT0C(), coll.centFT0M()); + } + + template + void fillAllFBOutputs(C const& coll, + float cent, + std::array const& nF, + std::array const& nB, + EtaPtPhiArray const& nFPtPhi, + EtaPtPhiArray const& nBPtPhi) + { + const int centClass = getCentClass(cent); + const int centWindowClass = getCentWindowClass(cent); + const int subsampleIndex = getSubsampleIndex(coll); + + histos.fill(HIST("QA/hCentrality"), cent); + fillFBQA(cent, nF, nB); + fillInclusiveEtaGapMoments(nF, nB); + fillCentClassEtaGapMoments(centClass, nF, nB); + fillCentWindowEtaGapMoments(centWindowClass, nF, nB); + fillSubsampleCentWindowEtaGapMoments(subsampleIndex, centWindowClass, nF, nB); + fillDifferentialEtaPtPhiMoments(nFPtPhi, nBPtPhi); + fillSubsampleDifferentialEtaPtPhiMoments(subsampleIndex, nFPtPhi, nBPtPhi); + } + + // ------------------------------------------------------------------ + // Data + // ------------------------------------------------------------------ + void processData(AodCollision const& coll, AodTracks const& tracks) + { + float cent = -999.0f; + const float centBefore = getCentrality(coll); + fillEventQABefore(coll, centBefore); + + if (!selCollision(coll, cent)) { + return; + } + fillEventQAAfter(coll, cent); + + std::array nF{}; + std::array nB{}; + EtaPtPhiArray nFPtPhi{}; + EtaPtPhiArray nBPtPhi{}; + + histos.fill(HIST("QA/hTrackSliceSize"), static_cast(tracks.size())); + + for (auto const& track : tracks) { + fillTrackQABefore(track); + if (!selTrack(track)) { + continue; + } + fillTrackQAAfter(track); + + addToFBCounters(track.eta(), track.pt(), track.phi(), nF, nB, nFPtPhi, nBPtPhi); + } + + fillAllFBOutputs(coll, cent, nF, nB, nFPtPhi, nBPtPhi); + } + PROCESS_SWITCH(StronglyIntensiveCorr, processData, "process real data", true); + + // ------------------------------------------------------------------ + // MC reconstructed: selected reco tracks with MC label. + // This is still reconstructed-level kinematics/cuts. + // ------------------------------------------------------------------ + void processMCRec(MyMCRecCollision const& coll, + MyMCTracks const& tracks, + aod::McParticles const&) + { + float cent = -999.0f; + const float centBefore = getCentrality(coll); + fillEventQABefore(coll, centBefore); + + if (!selCollision(coll, cent)) { + return; + } + fillEventQAAfter(coll, cent); + + std::array nF{}; + std::array nB{}; + EtaPtPhiArray nFPtPhi{}; + EtaPtPhiArray nBPtPhi{}; + int nRecoAll = 0; + int nRecoPrimary = 0; + int nRecoSecondary = 0; + int nRecoFake = 0; + + histos.fill(HIST("QA/hTrackSliceSize"), static_cast(tracks.size())); + + for (auto const& track : tracks) { + fillTrackQABefore(track); + + if (!selTrack(track)) { + continue; + } + + ++nRecoAll; + + if (!track.has_mcParticle()) { + ++nRecoFake; + continue; + } + + auto mcParticle = track.template mcParticle_as(); + + if (mcParticle.isPhysicalPrimary() && isChargedMCParticle(mcParticle.pdgCode())) { + ++nRecoPrimary; + } else { + ++nRecoSecondary; + } + + fillTrackQAAfter(track); + + addToFBCounters(track.eta(), track.pt(), track.phi(), nF, nB, nFPtPhi, nBPtPhi); + } + histos.fill(HIST("QA/mcReco/nRecoAll"), nRecoAll); + histos.fill(HIST("QA/mcReco/nRecoPrimary"), nRecoPrimary); + histos.fill(HIST("QA/mcReco/nRecoSecondary"), nRecoSecondary); + histos.fill(HIST("QA/mcReco/nRecoFake"), nRecoFake); + const int subsampleIndex = getSubsampleIndex(coll); + + // For MC running with all switches enabled, do not fill common SI histograms here. + // Keep Reco/Prim/Gen separated in multidimensional outputs. + fillRecoDifferentialEtaPtPhiMoments(nFPtPhi, nBPtPhi); + fillRecoSubsampleDifferentialEtaPtPhiMoments(subsampleIndex, nFPtPhi, nBPtPhi); + } + PROCESS_SWITCH(StronglyIntensiveCorr, processMCRec, "process MC reconstructed tracks", false); + + // ------------------------------------------------------------------ + // MC primary reconstructed: selected reco tracks matched to physical-primary charged MC particles. + // ------------------------------------------------------------------ + void processMCPrimaryReco(MyMCRecCollision const& coll, + MyMCTracks const& tracks, + aod::McParticles const&) + { + float cent = -999.0f; + const float centBefore = getCentrality(coll); + fillEventQABefore(coll, centBefore); + + if (!selCollision(coll, cent)) { + return; + } + fillEventQAAfter(coll, cent); + + std::array nF{}; + std::array nB{}; + EtaPtPhiArray nFPtPhi{}; + EtaPtPhiArray nBPtPhi{}; + + histos.fill(HIST("QA/hTrackSliceSize"), static_cast(tracks.size())); + + for (auto const& track : tracks) { + fillTrackQABefore(track); + if (!selTrack(track)) { + continue; + } + if (!track.has_mcParticle()) { + continue; + } + + auto mcParticle = track.template mcParticle_as(); + if (cfgUseOnlyPhysicalPrimary.value && !mcParticle.isPhysicalPrimary()) { + continue; + } + if (cfgUseOnlyChargedMC.value && !isChargedMCParticle(mcParticle.pdgCode())) { + continue; + } + + fillTrackQAAfter(track); + addToFBCounters(track.eta(), track.pt(), track.phi(), nF, nB, nFPtPhi, nBPtPhi); + } + + const int subsampleIndex = getSubsampleIndex(coll); + + // For MC running with all switches enabled, do not fill common SI histograms here. + // Keep Reco/Prim/Gen separated in multidimensional outputs. + fillPrimDifferentialEtaPtPhiMoments(nFPtPhi, nBPtPhi); + fillPrimSubsampleDifferentialEtaPtPhiMoments(subsampleIndex, nFPtPhi, nBPtPhi); + } + PROCESS_SWITCH(StronglyIntensiveCorr, processMCPrimaryReco, "process MC primary reconstructed tracks", false); + + // ------------------------------------------------------------------ + // MC generated: generator-level charged physical primaries from aod::McParticles. + // Uses reconstructed collision only to get event selection + centrality and then maps to mcCollisionId. + // ------------------------------------------------------------------ + void processMCGenerated(MyMCRecCollision const& coll, + aod::McParticles const& mcParticles) + { + if (!coll.has_mcCollision()) { + return; + } + + float cent = -999.0f; + const float centBefore = getCentrality(coll); + fillEventQABefore(coll, centBefore); + + if (!selCollision(coll, cent)) { + return; + } + fillEventQAAfter(coll, cent); + + std::array nF{}; + std::array nB{}; + EtaPtPhiArray nFPtPhi{}; + EtaPtPhiArray nBPtPhi{}; + + auto particlesThisCollision = mcParticles.sliceBy(perMcCollision, coll.mcCollisionId()); + + for (auto const& particle : particlesThisCollision) { + if (!selMCParticle(particle)) { + continue; + } + addToFBCounters(particle.eta(), particle.pt(), particle.phi(), nF, nB, nFPtPhi, nBPtPhi); + } + + const int subsampleIndex = getSubsampleIndex(coll); + + // For MC running with all switches enabled, do not fill common SI histograms here. + // Keep Reco/Prim/Gen separated in multidimensional outputs. + fillGenDifferentialEtaPtPhiMoments(nFPtPhi, nBPtPhi); + fillGenSubsampleDifferentialEtaPtPhiMoments(subsampleIndex, nFPtPhi, nBPtPhi); + } + PROCESS_SWITCH(StronglyIntensiveCorr, processMCGenerated, "process MC generated particles", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/EbyEFluctuations/Tasks/v0ptHadPiKaProt.cxx b/PWGCF/EbyEFluctuations/Tasks/v0ptHadPiKaProt.cxx index cffbe84540c..019fd1855ea 100644 --- a/PWGCF/EbyEFluctuations/Tasks/v0ptHadPiKaProt.cxx +++ b/PWGCF/EbyEFluctuations/Tasks/v0ptHadPiKaProt.cxx @@ -38,15 +38,16 @@ #include #include -#include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -65,14 +66,16 @@ static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3 #define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; struct V0ptHadPiKaProt { - // ITS response o2::aod::ITSResponse itsResponse; // Connect to ccdb Service ccdb; + Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable ccdbUrl{"ccdbUrl", "https://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable ccdbPath{"ccdbPath", "Users/s/swati/PhiWeight", "CCDB path to ccdb object containing phi weight in a 3D histogram"}; + Configurable ccdbNoLaterThanPtEff{"ccdbNoLaterThanPtEff", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable ccdbPathPtEff{"ccdbPathPtEff", "Users/s/swati/EfficiencyWeight", "CCDB path to ccdb object containing pt-dependent efficiency"}; enum Particles { PIONS = 0, @@ -99,6 +102,8 @@ struct V0ptHadPiKaProt { kFV0A }; + Configurable cfgIsMC{"cfgIsMC", false, "Run MC"}; + Configurable cfgMcClosure{"cfgMcClosure", false, "Calculate v0(pT) at Gen and Rec level to check Mc Closure"}; Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgCutTpcChi2NCl{"cfgCutTpcChi2NCl", 2.5f, "Maximum TPCchi2NCl"}; Configurable cfgCutItsChi2NCl{"cfgCutItsChi2NCl", 36.0f, "Maximum ITSchi2NCl"}; @@ -110,19 +115,23 @@ struct V0ptHadPiKaProt { Configurable cfgnSigmaOtherParticles{"cfgnSigmaOtherParticles", 3.0f, "PID nSigma cut to remove other particles (default:3)"}; Configurable cfgnSigmaCutTPC{"cfgnSigmaCutTPC", 2.0f, "PID nSigma cut for TPC"}; Configurable cfgnSigmaCutTOF{"cfgnSigmaCutTOF", 2.0f, "PID nSigma cut for TOF"}; + Configurable cfgUseNewSeperationPid{"cfgUseNewSeperationPid", false, "Use seperation based PID cuts (NEW)"}; + Configurable cfgnSigmaCutTPCorTOFHigherPt{"cfgnSigmaCutTPCorTOFHigherPt", 2.0f, "PID nSigma cut for TPC at higher pt"}; + Configurable cfgnSigmaSeperationCut{"cfgnSigmaSeperationCut", 3.5f, "PID nSigma of other species must be greater than the vale"}; Configurable cfgnSigmaCutCombTPCTOF{"cfgnSigmaCutCombTPCTOF", 2.0f, "PID nSigma combined cut for TPC and TOF"}; ConfigurableAxis nchAxis{"nchAxis", {5000, 0.5, 5000.5}, ""}; ConfigurableAxis centAxis{"centAxis", {90, 0., 90.}, "Centrality/Multiplicity percentile bining"}; ConfigurableAxis nchAxis1{"nchAxis1", {500, 0.5, 500.5}, "Axis for multiplicity of GlobalTracks/PVTracks"}; ConfigurableAxis nchAxis2{"nchAxis2", {1000, 0.5, 30000.5}, "Axis for multiplicity of FT0A/FT0C/FV0A"}; ConfigurableAxis nchAxis3{"nchAxis3", {1000, 0.5, 100000.5}, "Axis for multiplicity of FT0A/FT0C/FV0A"}; + ConfigurableAxis occuAxis{"occuAxis", {1000, 0.5, 50000.5}, "Axis for occupancy of events"}; Configurable cfgCutPtLower{"cfgCutPtLower", 0.2f, "Lower pT cut"}; Configurable cfgCutPtLowerProt{"cfgCutPtLowerProt", 0.2f, "Lower pT cut"}; Configurable cfgCutPtUpper{"cfgCutPtUpper", 10.0f, "Higher pT cut for inclusive hadron analysis"}; Configurable cfgCutPtUpperPID{"cfgCutPtUpperPID", 6.0f, "Higher pT cut for identified particle analysis"}; Configurable cfgCutEta{"cfgCutEta", 0.8f, "absolute Eta cut"}; - Configurable cfgCutEtaLeft{"cfgCutEtaLeft", 0.8f, "Left end of eta gap"}; - Configurable cfgCutEtaRight{"cfgCutEtaRight", 0.8f, "Right end of eta gap"}; + Configurable cfgCutEtaLeft{"cfgCutEtaLeft", -0.4f, "Left end of eta gap"}; + Configurable cfgCutEtaRight{"cfgCutEtaRight", 0.4f, "Right end of eta gap"}; Configurable cfgNSubsample{"cfgNSubsample", 20, "Number of subsamples"}; Configurable cfgCentralityChoice{"cfgCentralityChoice", 0, "Which centrality estimator? 0-->FT0C, 1-->FT0A, 2-->FT0M, 3-->FV0A"}; Configurable cfgEvSelkNoSameBunchPileup{"cfgEvSelkNoSameBunchPileup", true, "Pileup removal"}; @@ -130,6 +139,11 @@ struct V0ptHadPiKaProt { Configurable cfgEvSelkNoITSROFrameBorder{"cfgEvSelkNoITSROFrameBorder", true, "ITSROFrame border event selection cut"}; Configurable cfgEvSelkNoTimeFrameBorder{"cfgEvSelkNoTimeFrameBorder", true, "TimeFrame border event selection cut"}; Configurable cfgEvSelUseGoodZvtxFT0vsPV{"cfgEvSelUseGoodZvtxFT0vsPV", true, "GoodZvertex and FT0 vs PV cut"}; + Configurable cfgEvSelUseOcuppancyTimeCut{"cfgEvSelUseOcuppancyTimeCut", true, "Occupancy Time pattern cut"}; + Configurable cfgEvSelSetOcuppancyRange{"cfgEvSelSetOcuppancyRange", true, "Use cut on occupancy range"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "min. value of occupancy"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 3000, "max. value of occupancy"}; + Configurable cfgUseItsPID{"cfgUseItsPID", false, "Use ITS PID for particle identification"}; Configurable cfgPtCutTOF{"cfgPtCutTOF", 0.3f, "Minimum pt to use TOF N-sigma"}; Configurable> nSigmas{"nSigmas", {LongArrayFloat[0], 3, 6, {"TPC", "TOF", "ITS"}, {"pos_pi", "pos_ka", "pos_pr", "neg_pi", "neg_ka", "neg_pr"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; @@ -138,6 +152,12 @@ struct V0ptHadPiKaProt { Configurable cfgCutPtMaxForV02{"cfgCutPtMaxForV02", 3.0f, "Max. pT for v02(pT)"}; Configurable cfgCutEtaWindowB{"cfgCutEtaWindowB", 0.4f, "value of x in |eta| cfgLoadPhiWeights{"cfgLoadPhiWeights", false, "Load phi weights from CCDB to take care of non-uniform acceptance"}; + Configurable cfgLoadPtEffWeights{"cfgLoadPtEffWeights", false, "Load pt-dependent efficiency weights from CCDB to take care of detector inefficiency"}; + Configurable cfgMinNoOfParticles{"cfgMinNoOfParticles", 4, "Minimum no. of particles for calculating v02(pT)"}; + Configurable cfgV02WeightedFill{"cfgV02WeightedFill", false, "Fill profiles related to v2 with multiplicity-based weights?"}; + Configurable cfgV02WeightedOption3Fill{"cfgV02WeightedOption3Fill", false, "Fill profiles related to v2 with multiplicity-based weights according to Option3, triplet weighting for "}; + Configurable cfgUseDominanceCut{"cfgUseDominanceCut", true, "Require particle selecting species' nSigma to be smallest among other two"}; + Configurable cfgUseSeperationCutLowPt{"cfgUseSeperationCutLowPt", false, "Use separation cut for low pT"}; // pT dep DCAxy and DCAz cuts Configurable cfgUsePtDepDCAxy{"cfgUsePtDepDCAxy", true, "Use pt-dependent DCAxy cut"}; @@ -148,6 +168,7 @@ struct V0ptHadPiKaProt { TF1* fPtDepDCAz = nullptr; O2_DEFINE_CONFIGURABLE(cfgUseSmallIonAdditionalEventCut, bool, true, "Use additional event cut on mult correlations for small ions") + O2_DEFINE_CONFIGURABLE(cfgUseSmallIonAdditionalEventCutInMC, bool, false, "Use additional event cut on mult correlations for small ions while using MC generated") O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") struct : ConfigurableGroup { @@ -187,13 +208,25 @@ struct V0ptHadPiKaProt { // Output HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry histosAnalysis{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosAnalysis{"HistosAnalysis", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosMc{"HistosMc", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::vector>> subSample; std::vector>> subSampleV02; + std::vector>> subSampleV02weighted; + std::vector>> subSampleV02weightedOption3; + std::vector>> subSampleMcGen; + std::vector>> subSampleMcRecRaw; + std::vector>> subSampleMcRecCorr; TRandom3* funRndm = new TRandom3(0); // Phi weight histograms initialization - TH2F* hWeightPhiFunctionVzEtaPhi = nullptr; + TH3D* hWeightPhiFunctionVzEtaPhi = nullptr; + // Efficiency of diff. particle histograms initialization + TH1D* hEffAllCharged = nullptr; + TH1D* hEffPion = nullptr; + TH1D* hEffKaon = nullptr; + TH1D* hEffProton = nullptr; // Filter command*********** Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; @@ -203,6 +236,13 @@ struct V0ptHadPiKaProt { using AodCollisions = soa::Filtered>; using AodTracks = soa::Filtered>; + using MyMCRecCollisions = soa::Filtered>; + using MyMCTracks = soa::Filtered>; + using EventCandidatesMC = soa::Join; + + Preslice perCollision = aod::track::collisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + std::array tofNsigmaCut; std::array itsNsigmaCut; std::array tpcNsigmaCut; @@ -235,7 +275,7 @@ struct V0ptHadPiKaProt { // Loading phi weight histograms from CCDB if (cfgLoadPhiWeights) { - // Accessing eff histograms + // Accessing phi weight histograms ccdb->setURL(ccdbUrl.value); // Enabling object caching, otherwise each call goes to the CCDB server ccdb->setCaching(true); @@ -245,11 +285,28 @@ struct V0ptHadPiKaProt { ccdb->setCreatedNotAfter(ccdbNoLaterThan.value); LOGF(info, "Getting object %s", ccdbPath.value.data()); TList* lst = ccdb->getForTimeStamp(ccdbPath.value, ccdbNoLaterThan.value); - hWeightPhiFunctionVzEtaPhi = reinterpret_cast(lst->FindObject("hWeightPhiFunctionVzEtaPhi")); + hWeightPhiFunctionVzEtaPhi = reinterpret_cast(lst->FindObject("hWeightPhiFunctionVzEtaPhi")); if (!hWeightPhiFunctionVzEtaPhi) LOGF(info, "FATAL!! could not get phi weights---------> check"); } + // Loading pT-dependent efficiency histograms from CCDB + if (cfgLoadPtEffWeights) { + + ccdb->setURL(ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(ccdbNoLaterThanPtEff.value); + LOGF(info, "Getting object %s", ccdbPathPtEff.value.data()); + TList* lst = ccdb->getForTimeStamp(ccdbPathPtEff.value, ccdbNoLaterThanPtEff.value); + hEffAllCharged = reinterpret_cast(lst->FindObject("hEffAllCharged")); + hEffPion = reinterpret_cast(lst->FindObject("hEffPion")); + hEffKaon = reinterpret_cast(lst->FindObject("hEffKaon")); + hEffProton = reinterpret_cast(lst->FindObject("hEffProton")); + if (!hEffAllCharged || !hEffPion || !hEffKaon || !hEffProton) + LOGF(info, "FATAL!! could not get efficiency files---------> !!! check !!!"); + } + // Define axes std::vector ptBin = {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.5, 4.0, 5.0, 6.0, 8.0, 10.0}; AxisSpec ptAxis = {ptBin, "#it{p}_{T} (GeV/#it{c})"}; @@ -262,6 +319,8 @@ struct V0ptHadPiKaProt { histos.add("hEventStatData", "Data Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); histos.add("hZvtx_after_sel", ";Z (cm)", kTH1F, {{240, -12, 12}}); histos.add("hCentrality", ";centrality (%)", kTH1F, {{90, 0, 90}}); + histos.add("hOccupancyVsCentrality_before", "", kTH2F, {{90, 0, 90}, occuAxis}); + histos.add("hOccupancyVsCentrality_after", "", kTH2F, {{90, 0, 90}, occuAxis}); // before selection histos.add("MultCorrelationPlots/BeforeSelection/His2D_globalTracks_PVTracks_beforeSel", "", {HistType::kTH2D, {nchAxis1, nchAxis1}}); histos.add("MultCorrelationPlots/BeforeSelection/His2D_globalTracks_centFT0C_beforeSel", "", {HistType::kTH2D, {centAxis, nchAxis1}}); @@ -303,95 +362,342 @@ struct V0ptHadPiKaProt { histos.add("h2DnsigmaPionTpcVsTofBeforeCut", "2D hist of nSigmaTPC vs. nSigmaTOF (pion)", kTH2F, {nSigmaAxis, nSigmaAxis}); histos.add("h2DnsigmaKaonTpcVsTofBeforeCut", "2D hist of nSigmaTPC vs. nSigmaTOF (kaon)", kTH2F, {nSigmaAxis, nSigmaAxis}); histos.add("h2DnsigmaProtonTpcVsTofBeforeCut", "2D hist of nSigmaTPC vs. nSigmaTOF (proton)", kTH2F, {nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaPionItsVsPtBeforeCut", "2D hist of nSigmaITS vs. pT (pion)", kTH2F, {ptAxis, nSigmaAxis}); + histos.add("h2DnsigmaKaonItsVsPtBeforeCut", "2D hist of nSigmaITS vs. pT (kaon)", kTH2F, {ptAxis, nSigmaAxis}); + histos.add("h2DnsigmaProtonItsVsPtBeforeCut", "2D hist of nSigmaITS vs. pT (proton)", kTH2F, {ptAxis, nSigmaAxis}); + histos.add("h2DnsigmaPionItsVsTofBeforeCut", "2D hist of nSigmaITS vs. nSigmaTOF (pion)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaKaonItsVsTofBeforeCut", "2D hist of nSigmaITS vs. nSigmaTOF (kaon)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaProtonItsVsTofBeforeCut", "2D hist of nSigmaITS vs. nSigmaTOF (proton)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + // after cut + histos.add("h2DnsigmaPionItsVsPtAfterCut", "2D hist of nSigmaITS vs. pT (pion)", kTH2F, {ptAxis, nSigmaAxis}); + histos.add("h2DnsigmaKaonItsVsPtAfterCut", "2D hist of nSigmaITS vs. pT (kaon)", kTH2F, {ptAxis, nSigmaAxis}); + histos.add("h2DnsigmaProtonItsVsPtAfterCut", "2D hist of nSigmaITS vs. pT (proton)", kTH2F, {ptAxis, nSigmaAxis}); histos.add("h2DnsigmaPionTpcVsPtAfterCut", "2D hist of nSigmaTPC vs. pT (pion)", kTH2F, {ptAxis, nSigmaAxis}); histos.add("h2DnsigmaKaonTpcVsPtAfterCut", "2D hist of nSigmaTPC vs. pT (kaon)", kTH2F, {ptAxis, nSigmaAxis}); histos.add("h2DnsigmaProtonTpcVsPtAfterCut", "2D hist of nSigmaTPC vs. pT (proton)", kTH2F, {ptAxis, nSigmaAxis}); histos.add("h2DnsigmaPionTofVsPtAfterCut", "2D hist of nSigmaTOF vs. pT (pion)", kTH2F, {ptAxis, nSigmaAxis}); histos.add("h2DnsigmaKaonTofVsPtAfterCut", "2D hist of nSigmaTOF vs. pT (kaon)", kTH2F, {ptAxis, nSigmaAxis}); histos.add("h2DnsigmaProtonTofVsPtAfterCut", "2D hist of nSigmaTOF vs. pT (proton)", kTH2F, {ptAxis, nSigmaAxis}); - histos.add("h2DnsigmaPionTpcVsTofAfterCut", "2D hist of nSigmaTPC vs. nSigmaTOF (pion)", kTH2F, {nSigmaAxis, nSigmaAxis}); - histos.add("h2DnsigmaKaonTpcVsTofAfterCut", "2D hist of nSigmaTPC vs. nSigmaTOF (kaon)", kTH2F, {nSigmaAxis, nSigmaAxis}); - histos.add("h2DnsigmaProtonTpcVsTofAfterCut", "2D hist of nSigmaTPC vs. nSigmaTOF (proton)", kTH2F, {nSigmaAxis, nSigmaAxis}); + + histos.add("h2DnsigmaPionTpcVsTofInterimCut", "3D hist of nSigmaTPC vs. nSigmaTOF (pion)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaKaonTpcVsTofInterimCut", "3D hist of nSigmaTPC vs. nSigmaTOF (kaon)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaProtonTpcVsTofInterimCut", "3D hist of nSigmaTPC vs. nSigmaTOF (proton)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + + histos.add("h2DnsigmaPionTpcVsTofAfterCut", "3D hist of nSigmaTPC vs. nSigmaTOF (pion)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaKaonTpcVsTofAfterCut", "3D hist of nSigmaTPC vs. nSigmaTOF (kaon)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaProtonTpcVsTofAfterCut", "3D hist of nSigmaTPC vs. nSigmaTOF (proton)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaPionItsVsTofAfterCut", "3D hist of nSigmaITS vs. nSigmaTOF (pion)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaKaonItsVsTofAfterCut", "3D hist of nSigmaITS vs. nSigmaTOF (kaon)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); + histos.add("h2DnsigmaProtonItsVsTofAfterCut", "3D hist of nSigmaITS vs. nSigmaTOF (proton)", kTH3F, {ptAxis, nSigmaAxis, nSigmaAxis}); // Analysis profiles - histos.add("Prof_A_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_C_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_D_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Bone_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Btwo_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - - histos.add("Prof_A_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_C_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_D_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Bone_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Btwo_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - - histos.add("Prof_A_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_C_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_D_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Bone_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Btwo_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - - histos.add("Prof_A_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_C_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_D_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Bone_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_Btwo_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + // v0(pT) analysis for data + if (!cfgIsMC) { + histos.add("Prof_A_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_C_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_D_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Bone_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Btwo_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histos.add("Prof_A_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_C_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_D_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Bone_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Btwo_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histos.add("Prof_A_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_C_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_D_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Bone_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Btwo_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histos.add("Prof_A_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_C_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_D_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Bone_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_Btwo_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + } + if (cfgIsMC && cfgMcClosure) { + + // v0(pT) analysis for MC generated + histos.add("McClosure/Generated/Prof_A_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_C_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_D_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Bone_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Btwo_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histos.add("McClosure/Generated/Prof_A_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_C_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_D_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Bone_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Btwo_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histos.add("McClosure/Generated/Prof_A_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_C_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_D_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Bone_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Btwo_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histos.add("McClosure/Generated/Prof_A_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_C_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("McClosure/Generated/Prof_D_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Bone_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("McClosure/Generated/Prof_Btwo_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + // v0(pT) analysis for MC reconstructed efficiency raw + histosMc.add("McClosure/Reconstructed/Raw/Prof_A_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_C_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_D_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Bone_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Btwo_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histosMc.add("McClosure/Reconstructed/Raw/Prof_A_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_C_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_D_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Bone_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Btwo_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histosMc.add("McClosure/Reconstructed/Raw/Prof_A_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_C_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_D_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Bone_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Btwo_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histosMc.add("McClosure/Reconstructed/Raw/Prof_A_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_C_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_D_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Bone_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Raw/Prof_Btwo_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + // v0(pT) analysis for MC reconstructed efficiency corrected + histosMc.add("McClosure/Reconstructed/Corr/Prof_A_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_C_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_D_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Bone_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Btwo_had", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histosMc.add("McClosure/Reconstructed/Corr/Prof_A_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_C_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_D_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Bone_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Btwo_pi", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histosMc.add("McClosure/Reconstructed/Corr/Prof_A_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_C_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_D_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Bone_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Btwo_ka", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + + histosMc.add("McClosure/Reconstructed/Corr/Prof_A_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_C_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_D_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Bone_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histosMc.add("McClosure/Reconstructed/Corr/Prof_Btwo_prot", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + } // Analysis profile for v02(pT) - histos.add("Prof_XY", "", {HistType::kTProfile2D, {centAxis, noAxis}}); - histos.add("Prof_XYZ_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_Z_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_XYZ_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_Z_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_XYZ_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_Z_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_XYZ_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); - histos.add("Prof_Z_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + if (!cfgIsMC) { + histos.add("Prof_XY", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_XYZ_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + + // check with different normalization for event averaging + if (cfgV02WeightedFill) { + histos.add("Prof_XY_weighted", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_XYZ_weighted_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weighted_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_weighted_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weighted_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_weighted_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weighted_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_weighted_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weighted_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + } + + if (cfgV02WeightedOption3Fill) { + histos.add("Prof_XY_weightedOption3", "", {HistType::kTProfile2D, {centAxis, noAxis}}); + histos.add("Prof_XYZ_weightedOption3_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weightedOption3_had", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_weightedOption3_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weightedOption3_pi", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_weightedOption3_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weightedOption3_ka", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_XYZ_weightedOption3_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + histos.add("Prof_Z_weightedOption3_prot", "", {HistType::kTProfile2D, {centAxis, ptAxis}}); + } + } // initial array subSample.resize(cfgNSubsample); subSampleV02.resize(cfgNSubsample); + subSampleV02weighted.resize(cfgNSubsample); + subSampleV02weightedOption3.resize(cfgNSubsample); + if (cfgIsMC && cfgMcClosure) { + subSampleMcGen.resize(cfgNSubsample); + subSampleMcRecRaw.resize(cfgNSubsample); + subSampleMcRecCorr.resize(cfgNSubsample); + } + for (int i = 0; i < cfgNSubsample; i++) { subSample[i].resize(20); subSampleV02[i].resize(9); + subSampleV02weighted[i].resize(9); + subSampleV02weightedOption3[i].resize(9); + + if (cfgIsMC && cfgMcClosure) { + subSampleMcGen[i].resize(20); + subSampleMcRecRaw[i].resize(20); + subSampleMcRecCorr[i].resize(20); + } } for (int i = 0; i < cfgNSubsample; i++) { - subSample[i][0] = std::get>(histos.add(Form("subSample_%d/Prof_A_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][1] = std::get>(histos.add(Form("subSample_%d/Prof_C_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][2] = std::get>(histos.add(Form("subSample_%d/Prof_D_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][3] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][4] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - - subSample[i][5] = std::get>(histos.add(Form("subSample_%d/Prof_A_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][6] = std::get>(histos.add(Form("subSample_%d/Prof_C_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][7] = std::get>(histos.add(Form("subSample_%d/Prof_D_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][8] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][9] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - - subSample[i][10] = std::get>(histos.add(Form("subSample_%d/Prof_A_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][11] = std::get>(histos.add(Form("subSample_%d/Prof_C_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][12] = std::get>(histos.add(Form("subSample_%d/Prof_D_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][13] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][14] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - - subSample[i][15] = std::get>(histos.add(Form("subSample_%d/Prof_A_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][16] = std::get>(histos.add(Form("subSample_%d/Prof_C_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSample[i][17] = std::get>(histos.add(Form("subSample_%d/Prof_D_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][18] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSample[i][19] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - - subSampleV02[i][0] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XY", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); - subSampleV02[i][1] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSampleV02[i][2] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSampleV02[i][3] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSampleV02[i][4] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSampleV02[i][5] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSampleV02[i][6] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSampleV02[i][7] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); - subSampleV02[i][8] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + + if (!cfgIsMC) { + subSample[i][0] = std::get>(histos.add(Form("subSample_%d/Prof_A_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][1] = std::get>(histos.add(Form("subSample_%d/Prof_C_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][2] = std::get>(histos.add(Form("subSample_%d/Prof_D_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][3] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][4] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSample[i][5] = std::get>(histos.add(Form("subSample_%d/Prof_A_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][6] = std::get>(histos.add(Form("subSample_%d/Prof_C_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][7] = std::get>(histos.add(Form("subSample_%d/Prof_D_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][8] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][9] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSample[i][10] = std::get>(histos.add(Form("subSample_%d/Prof_A_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][11] = std::get>(histos.add(Form("subSample_%d/Prof_C_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][12] = std::get>(histos.add(Form("subSample_%d/Prof_D_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][13] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][14] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSample[i][15] = std::get>(histos.add(Form("subSample_%d/Prof_A_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][16] = std::get>(histos.add(Form("subSample_%d/Prof_C_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSample[i][17] = std::get>(histos.add(Form("subSample_%d/Prof_D_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][18] = std::get>(histos.add(Form("subSample_%d/Prof_Bone_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSample[i][19] = std::get>(histos.add(Form("subSample_%d/Prof_Btwo_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + } + + if (cfgIsMC && cfgMcClosure) { + // generated + subSampleMcGen[i][0] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_A_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][1] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_C_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][2] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_D_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][3] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Bone_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][4] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Btwo_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcGen[i][5] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_A_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][6] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_C_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][7] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_D_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][8] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Bone_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][9] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Btwo_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcGen[i][10] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_A_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][11] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_C_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][12] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_D_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][13] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Bone_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][14] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Btwo_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcGen[i][15] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_A_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][16] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_C_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcGen[i][17] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_D_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][18] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Bone_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcGen[i][19] = std::get>(histos.add(Form("McClosure/Generated/subSample_%d/Prof_Btwo_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + // reconstruced/Raw + subSampleMcRecRaw[i][0] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_A_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][1] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_C_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][2] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_D_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][3] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Bone_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][4] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Btwo_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcRecRaw[i][5] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_A_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][6] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_C_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][7] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_D_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][8] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Bone_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][9] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Btwo_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcRecRaw[i][10] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_A_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][11] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_C_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][12] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_D_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][13] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Bone_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][14] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Btwo_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcRecRaw[i][15] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_A_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][16] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_C_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecRaw[i][17] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_D_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][18] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Bone_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecRaw[i][19] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Raw/subSample_%d/Prof_Btwo_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + // reconstruced/Corr + subSampleMcRecCorr[i][0] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_A_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][1] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_C_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][2] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_D_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][3] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Bone_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][4] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Btwo_had", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcRecCorr[i][5] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_A_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][6] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_C_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][7] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_D_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][8] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Bone_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][9] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Btwo_pi", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcRecCorr[i][10] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_A_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][11] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_C_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][12] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_D_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][13] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Bone_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][14] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Btwo_ka", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + + subSampleMcRecCorr[i][15] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_A_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][16] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_C_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleMcRecCorr[i][17] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_D_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][18] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Bone_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleMcRecCorr[i][19] = std::get>(histosMc.add(Form("McClosure/Reconstructed/Corr/subSample_%d/Prof_Btwo_prot", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + } + + if (!cfgIsMC) { + subSampleV02[i][0] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XY", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleV02[i][1] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02[i][2] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02[i][3] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02[i][4] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02[i][5] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02[i][6] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02[i][7] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_XYZ_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02[i][8] = std::get>(histosAnalysis.add(Form("subSampleV02_%d/Prof_Z_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + + if (cfgV02WeightedFill) { + subSampleV02weighted[i][0] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_XY_weighted", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleV02weighted[i][1] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_XYZ_weighted_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weighted[i][2] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_Z_weighted_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weighted[i][3] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_XYZ_weighted_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weighted[i][4] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_Z_weighted_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weighted[i][5] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_XYZ_weighted_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weighted[i][6] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_Z_weighted_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weighted[i][7] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_XYZ_weighted_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weighted[i][8] = std::get>(histosAnalysis.add(Form("subSampleV02weighted_%d/Prof_Z_weighted_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + } + + if (cfgV02WeightedOption3Fill) { + subSampleV02weightedOption3[i][0] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_XY_weightedOption3", i), "", {HistType::kTProfile2D, {centAxis, noAxis}})); + subSampleV02weightedOption3[i][1] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_XYZ_weightedOption3_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weightedOption3[i][2] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_Z_weightedOption3_had", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weightedOption3[i][3] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_XYZ_weightedOption3_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weightedOption3[i][4] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_Z_weightedOption3_pi", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weightedOption3[i][5] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_XYZ_weightedOption3_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weightedOption3[i][6] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_Z_weightedOption3_ka", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weightedOption3[i][7] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_XYZ_weightedOption3_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + subSampleV02weightedOption3[i][8] = std::get>(histosAnalysis.add(Form("subSampleV02weightedOption3_%d/Prof_Z_weightedOption3_prot", i), "", {HistType::kTProfile2D, {centAxis, ptAxis}})); + } + } } if (cfgEvSelMultCorrelation) { @@ -428,6 +734,35 @@ struct V0ptHadPiKaProt { fPtDepDCAz = new TF1("ptDepDCAz", Form("%s", cfgDCAzFunc->c_str()), 0.001, 1000); } + if (cfgIsMC) { + // MC event counts + histos.add("MCGenerated/hMC", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); + histos.add("MCGenerated/hCentgen", "MC generated centrality", kTH1F, {centAxis}); + histos.add("MCGenerated/hPtEtaPhiCharged", "MC charged particles' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCGenerated/hPtEtaPhiPion", "MC charged pions' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCGenerated/hPtEtaPhiKaon", "MC charged kaons' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCGenerated/hPtEtaPhiProton", "MC charged protons' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCGenerated/hPtEtaPhiCharged_gen", "MC charged particles' pt, eta, phi in the generated process", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCGenerated/hPtEtaPhiPion_gen", "MC charged pions' pt, eta, phi in the generated process", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCGenerated/hPtEtaPhiKaon_gen", "MC charged kaons' pt, eta, phi in the generated process", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCGenerated/hPtEtaPhiProton_gen", "MC charged protons' pt, eta, phi in the generated process", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + + histos.add("MCReconstructed/hPtEtaPhiChargedParticle", "MC reconstructed charged particles' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCReconstructed/hPtEtaPhiChargedTrack", "MC reconstructed charged tracks' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + + histos.add("MCReconstructed/hPtEtaPhiPionParticle", "MC reconstructed pion particles' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCReconstructed/hPtEtaPhiPionTrack", "MC reconstructed pion tracks' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCReconstructed/hPtEtaPhiTruePionTrack", "MC reconstructed pdgcode matched pion tracks' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + + histos.add("MCReconstructed/hPtEtaPhiKaonParticle", "MC reconstructed kaon particles' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCReconstructed/hPtEtaPhiKaonTrack", "MC reconstructed kaon tracks' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCReconstructed/hPtEtaPhiTrueKaonTrack", "MC reconstructed pdgcode matched kaon tracks' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + + histos.add("MCReconstructed/hPtEtaPhiProtonParticle", "MC reconstructed proton particles' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCReconstructed/hPtEtaPhiProtonTrack", "MC reconstructed proton tracks' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + histos.add("MCReconstructed/hPtEtaPhiTrueProtonTrack", "MC reconstructed pdgcode matched proton tracks' pt, eta, phi", kTH3D, {ptAxis, {100, 0., o2::constants::math::TwoPI}, {100, -2.01, 2.01}}); + } + } // end init //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -438,18 +773,37 @@ struct V0ptHadPiKaProt { return false; int flag = 0; //! pid check main flag + float partNsigmaTpcOrItsPi = 0.0; + float partNsigmaTpcOrItsKa = 0.0; + float partNsigmaTpcOrItsPr = 0.0; + if (cfgUseItsPID) { + partNsigmaTpcOrItsPi = itsResponse.nSigmaITS(candidate); + partNsigmaTpcOrItsKa = itsResponse.nSigmaITS(candidate); + partNsigmaTpcOrItsPr = itsResponse.nSigmaITS(candidate); + } else { + partNsigmaTpcOrItsPi = candidate.tpcNSigmaPi(); + partNsigmaTpcOrItsKa = candidate.tpcNSigmaKa(); + partNsigmaTpcOrItsPr = candidate.tpcNSigmaPr(); + } + + bool separatedFromOthers = true; + if (cfgUseSeperationCutLowPt) { + separatedFromOthers = std::abs(partNsigmaTpcOrItsPi) > cfgnSigmaSeperationCut && std::abs(partNsigmaTpcOrItsKa) > cfgnSigmaSeperationCut; + } + if (candidate.pt() > cfgCutPtLower && candidate.pt() <= cfgCutPtUpperTPC) { - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < cfgnSigmaCutTPC) { + + if (!candidate.hasTOF() && std::abs(partNsigmaTpcOrItsPr) < cfgnSigmaCutTPC && separatedFromOthers) { flag = 1; } - if (candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < cfgnSigmaCutTPC && std::abs(candidate.tofNSigmaPr()) < cfgnSigmaCutTOF) { + if (candidate.hasTOF() && std::abs(partNsigmaTpcOrItsPr) < cfgnSigmaCutTPC && std::abs(candidate.tofNSigmaPr()) < cfgnSigmaCutTOF && separatedFromOthers) { flag = 1; } } if (candidate.hasTOF() && candidate.pt() > cfgCutPtUpperTPC && candidate.pt() < cfgCutPtUpperPID) { - const float combNSigmaPr = std::sqrt(std::pow(candidate.tpcNSigmaPr(), 2.0) + std::pow(candidate.tofNSigmaPr(), 2.0)); - const float combNSigmaPi = std::sqrt(std::pow(candidate.tpcNSigmaPi(), 2.0) + std::pow(candidate.tofNSigmaPi(), 2.0)); - const float combNSigmaKa = std::sqrt(std::pow(candidate.tpcNSigmaKa(), 2.0) + std::pow(candidate.tofNSigmaKa(), 2.0)); + const float combNSigmaPr = std::sqrt(std::pow(partNsigmaTpcOrItsPr, 2.0) + std::pow(candidate.tofNSigmaPr(), 2.0)); + const float combNSigmaPi = std::sqrt(std::pow(partNsigmaTpcOrItsPi, 2.0) + std::pow(candidate.tofNSigmaPi(), 2.0)); + const float combNSigmaKa = std::sqrt(std::pow(partNsigmaTpcOrItsKa, 2.0) + std::pow(candidate.tofNSigmaKa(), 2.0)); int flag2 = 0; if (combNSigmaPr < cfgnSigmaOtherParticles) @@ -458,9 +812,26 @@ struct V0ptHadPiKaProt { flag2 += 1; if (combNSigmaKa < cfgnSigmaOtherParticles) flag2 += 1; - if (!(flag2 > 1) && !(combNSigmaPr > combNSigmaPi) && !(combNSigmaPr > combNSigmaKa)) { - if (combNSigmaPr < cfgnSigmaCutCombTPCTOF) { - flag = 1; + + if (cfgUseNewSeperationPid) { + if (std::abs(partNsigmaTpcOrItsPr) < cfgnSigmaCutTPCorTOFHigherPt && std::abs(candidate.tofNSigmaPr()) < cfgnSigmaCutTPCorTOFHigherPt) { + if (!(flag2 > 1) && std::abs(partNsigmaTpcOrItsPi) > cfgnSigmaSeperationCut && std::abs(candidate.tofNSigmaPi()) > cfgnSigmaSeperationCut && std::abs(partNsigmaTpcOrItsKa) > cfgnSigmaSeperationCut && std::abs(candidate.tofNSigmaKa()) > cfgnSigmaSeperationCut) + flag = 1; + } + } else { + + bool passDominance = true; + // Apply condition only if enabled + if (cfgUseDominanceCut) { + passDominance = !(combNSigmaPr > combNSigmaPi) && !(combNSigmaPr > combNSigmaKa); + } + + if (!(flag2 > 1) && passDominance) { + histos.fill(HIST("h2DnsigmaProtonTpcVsTofInterimCut"), candidate.pt(), candidate.tpcNSigmaPr(), candidate.tofNSigmaPr()); + + if (combNSigmaPr < cfgnSigmaCutCombTPCTOF) { + flag = 1; + } } } } @@ -477,18 +848,37 @@ struct V0ptHadPiKaProt { return false; int flag = 0; //! pid check main flag + float partNsigmaTpcOrItsPi = 0.0; + float partNsigmaTpcOrItsKa = 0.0; + float partNsigmaTpcOrItsPr = 0.0; + if (cfgUseItsPID) { + partNsigmaTpcOrItsPi = itsResponse.nSigmaITS(candidate); + partNsigmaTpcOrItsKa = itsResponse.nSigmaITS(candidate); + partNsigmaTpcOrItsPr = itsResponse.nSigmaITS(candidate); + } else { + partNsigmaTpcOrItsPi = candidate.tpcNSigmaPi(); + partNsigmaTpcOrItsKa = candidate.tpcNSigmaKa(); + partNsigmaTpcOrItsPr = candidate.tpcNSigmaPr(); + } + + bool separatedFromOthers = true; + if (cfgUseSeperationCutLowPt) { + separatedFromOthers = std::abs(partNsigmaTpcOrItsKa) > cfgnSigmaSeperationCut && std::abs(partNsigmaTpcOrItsPr) > cfgnSigmaSeperationCut; + } + if (candidate.pt() > cfgCutPtLower && candidate.pt() <= cfgCutPtUpperTPC) { - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < cfgnSigmaCutTPC) { + + if (!candidate.hasTOF() && std::abs(partNsigmaTpcOrItsPi) < cfgnSigmaCutTPC && separatedFromOthers) { flag = 1; } - if (candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < cfgnSigmaCutTPC && std::abs(candidate.tofNSigmaPi()) < cfgnSigmaCutTOF) { + if (candidate.hasTOF() && std::abs(partNsigmaTpcOrItsPi) < cfgnSigmaCutTPC && std::abs(candidate.tofNSigmaPi()) < cfgnSigmaCutTOF && separatedFromOthers) { flag = 1; } } if (candidate.hasTOF() && candidate.pt() > cfgCutPtUpperTPC && candidate.pt() < cfgCutPtUpperPID) { - const float combNSigmaPr = std::sqrt(std::pow(candidate.tpcNSigmaPr(), 2.0) + std::pow(candidate.tofNSigmaPr(), 2.0)); - const float combNSigmaPi = std::sqrt(std::pow(candidate.tpcNSigmaPi(), 2.0) + std::pow(candidate.tofNSigmaPi(), 2.0)); - const float combNSigmaKa = std::sqrt(std::pow(candidate.tpcNSigmaKa(), 2.0) + std::pow(candidate.tofNSigmaKa(), 2.0)); + const float combNSigmaPr = std::sqrt(std::pow(partNsigmaTpcOrItsPr, 2.0) + std::pow(candidate.tofNSigmaPr(), 2.0)); + const float combNSigmaPi = std::sqrt(std::pow(partNsigmaTpcOrItsPi, 2.0) + std::pow(candidate.tofNSigmaPi(), 2.0)); + const float combNSigmaKa = std::sqrt(std::pow(partNsigmaTpcOrItsKa, 2.0) + std::pow(candidate.tofNSigmaKa(), 2.0)); int flag2 = 0; if (combNSigmaPr < cfgnSigmaOtherParticles) @@ -497,9 +887,25 @@ struct V0ptHadPiKaProt { flag2 += 1; if (combNSigmaKa < cfgnSigmaOtherParticles) flag2 += 1; - if (!(flag2 > 1) && !(combNSigmaPi > combNSigmaPr) && !(combNSigmaPi > combNSigmaKa)) { - if (combNSigmaPi < cfgnSigmaCutCombTPCTOF) { - flag = 1; + + if (cfgUseNewSeperationPid) { + if (std::abs(partNsigmaTpcOrItsPi) < cfgnSigmaCutTPCorTOFHigherPt && std::abs(candidate.tofNSigmaPi()) < cfgnSigmaCutTPCorTOFHigherPt) { + if (!(flag2 > 1) && std::abs(partNsigmaTpcOrItsKa) > cfgnSigmaSeperationCut && std::abs(candidate.tofNSigmaKa()) > cfgnSigmaSeperationCut && std::abs(partNsigmaTpcOrItsPr) > cfgnSigmaSeperationCut && std::abs(candidate.tofNSigmaPr()) > cfgnSigmaSeperationCut) + flag = 1; + } + } else { + + bool passDominance = true; + // Apply condition only if enabled + if (cfgUseDominanceCut) { + passDominance = !(combNSigmaPi > combNSigmaPr) && !(combNSigmaPi > combNSigmaKa); + } + + if (!(flag2 > 1) && passDominance) { + histos.fill(HIST("h2DnsigmaPionTpcVsTofInterimCut"), candidate.pt(), candidate.tpcNSigmaPi(), candidate.tofNSigmaPi()); + if (combNSigmaPi < cfgnSigmaCutCombTPCTOF) { + flag = 1; + } } } } @@ -516,18 +922,37 @@ struct V0ptHadPiKaProt { return false; int flag = 0; //! pid check main flag + float partNsigmaTpcOrItsPi = 0.0; + float partNsigmaTpcOrItsKa = 0.0; + float partNsigmaTpcOrItsPr = 0.0; + if (cfgUseItsPID) { + partNsigmaTpcOrItsPi = itsResponse.nSigmaITS(candidate); + partNsigmaTpcOrItsKa = itsResponse.nSigmaITS(candidate); + partNsigmaTpcOrItsPr = itsResponse.nSigmaITS(candidate); + } else { + partNsigmaTpcOrItsPi = candidate.tpcNSigmaPi(); + partNsigmaTpcOrItsKa = candidate.tpcNSigmaKa(); + partNsigmaTpcOrItsPr = candidate.tpcNSigmaPr(); + } + + bool separatedFromOthers = true; + if (cfgUseSeperationCutLowPt) { + separatedFromOthers = std::abs(partNsigmaTpcOrItsPi) > cfgnSigmaSeperationCut && std::abs(partNsigmaTpcOrItsPr) > cfgnSigmaSeperationCut; + } + if (candidate.pt() > cfgCutPtLower && candidate.pt() <= cfgCutPtUpperTPC) { - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < cfgnSigmaCutTPC) { + + if (!candidate.hasTOF() && std::abs(partNsigmaTpcOrItsKa) < cfgnSigmaCutTPC && separatedFromOthers) { flag = 1; } - if (candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < cfgnSigmaCutTPC && std::abs(candidate.tofNSigmaKa()) < cfgnSigmaCutTOF) { + if (candidate.hasTOF() && std::abs(partNsigmaTpcOrItsKa) < cfgnSigmaCutTPC && std::abs(candidate.tofNSigmaKa()) < cfgnSigmaCutTOF && separatedFromOthers) { flag = 1; } } if (candidate.hasTOF() && candidate.pt() > cfgCutPtUpperTPC && candidate.pt() < cfgCutPtUpperPID) { - const float combNSigmaPr = std::sqrt(std::pow(candidate.tpcNSigmaPr(), 2.0) + std::pow(candidate.tofNSigmaPr(), 2.0)); - const float combNSigmaPi = std::sqrt(std::pow(candidate.tpcNSigmaPi(), 2.0) + std::pow(candidate.tofNSigmaPi(), 2.0)); - const float combNSigmaKa = std::sqrt(std::pow(candidate.tpcNSigmaKa(), 2.0) + std::pow(candidate.tofNSigmaKa(), 2.0)); + const float combNSigmaPr = std::sqrt(std::pow(partNsigmaTpcOrItsPr, 2.0) + std::pow(candidate.tofNSigmaPr(), 2.0)); + const float combNSigmaPi = std::sqrt(std::pow(partNsigmaTpcOrItsPi, 2.0) + std::pow(candidate.tofNSigmaPi(), 2.0)); + const float combNSigmaKa = std::sqrt(std::pow(partNsigmaTpcOrItsKa, 2.0) + std::pow(candidate.tofNSigmaKa(), 2.0)); int flag2 = 0; if (combNSigmaPr < cfgnSigmaOtherParticles) @@ -536,9 +961,25 @@ struct V0ptHadPiKaProt { flag2 += 1; if (combNSigmaKa < cfgnSigmaOtherParticles) flag2 += 1; - if (!(flag2 > 1) && !(combNSigmaKa > combNSigmaPi) && !(combNSigmaKa > combNSigmaPr)) { - if (combNSigmaKa < cfgnSigmaCutCombTPCTOF) { - flag = 1; + + if (cfgUseNewSeperationPid) { + if (std::abs(partNsigmaTpcOrItsKa) < cfgnSigmaCutTPCorTOFHigherPt && std::abs(candidate.tofNSigmaKa()) < cfgnSigmaCutTPCorTOFHigherPt) { + if (!(flag2 > 1) && std::abs(partNsigmaTpcOrItsPi) > cfgnSigmaSeperationCut && std::abs(candidate.tofNSigmaPi()) > cfgnSigmaSeperationCut && std::abs(partNsigmaTpcOrItsPr) > cfgnSigmaSeperationCut && std::abs(candidate.tofNSigmaPr()) > cfgnSigmaSeperationCut) + flag = 1; + } + } else { + + bool passDominance = true; + // Apply condition only if enabled + if (cfgUseDominanceCut) { + passDominance = !(combNSigmaKa > combNSigmaPi) && !(combNSigmaKa > combNSigmaPr); + } + + if (!(flag2 > 1) && passDominance) { + histos.fill(HIST("h2DnsigmaKaonTpcVsTofInterimCut"), candidate.pt(), candidate.tpcNSigmaKa(), candidate.tofNSigmaKa()); + if (combNSigmaKa < cfgnSigmaCutCombTPCTOF) { + flag = 1; + } } } } @@ -549,7 +990,7 @@ struct V0ptHadPiKaProt { } template - int getNsigmaPID(TTrack track) + int getNsigmaPID(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -600,7 +1041,7 @@ struct V0ptHadPiKaProt { // additional multiplicity correlation based event selection cuts template - bool eventSelectedSmallion(TCollision collision, const int multTrk, const float centrality) + bool eventSelectedSmallion(const TCollision& collision, const int multTrk, const float centrality) { auto multNTracksPV = collision.multNTracksPV(); @@ -642,7 +1083,7 @@ struct V0ptHadPiKaProt { } template - bool eventSelectionDefaultCuts(TCollision coll) + bool eventSelectionDefaultCuts(const TCollision& coll) { histos.fill(HIST("hEventStatData"), 0.5); if (!coll.sel8()) { @@ -675,6 +1116,22 @@ struct V0ptHadPiKaProt { } histos.fill(HIST("hEventStatData"), 6.5); + // events with selection bits based on occupancy time pattern + if (cfgEvSelUseOcuppancyTimeCut) { + int occupancy = coll.trackOccupancyInTimeRange(); + histos.fill(HIST("hOccupancyVsCentrality_before"), coll.centFT0C(), occupancy); + if (!(coll.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) + return 0; + else + histos.fill(HIST("hOccupancyVsCentrality_after"), coll.centFT0C(), occupancy); + + histos.fill(HIST("hEventStatData"), 7.5); + if (cfgEvSelSetOcuppancyRange && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { + return 0; + } + } + + histos.fill(HIST("hEventStatData"), 8.5); return 1; } @@ -714,8 +1171,765 @@ struct V0ptHadPiKaProt { return weight; } + template + float getEffWeightAllCharged(const T& candidate) + { + if (!cfgLoadPtEffWeights || !hEffAllCharged) { + return 1.0; + } + int bin = hEffAllCharged->FindBin(candidate.pt()); + float eff = hEffAllCharged->GetBinContent(bin); + float ptweight = 1.0 / eff; + if (!std::isfinite(ptweight) || ptweight <= 0) { + return 1.0; + } + return ptweight; + } + + template + float getEffWeightPion(const T& candidate) + { + if (!cfgLoadPtEffWeights || !hEffPion) { + return 1.0; + } + int bin = hEffPion->FindBin(candidate.pt()); + float eff = hEffPion->GetBinContent(bin); + float ptweight = 1.0 / eff; + if (!std::isfinite(ptweight) || ptweight <= 0) { + return 1.0; + } + return ptweight; + } + + template + float getEffWeightKaon(const T& candidate) + { + if (!cfgLoadPtEffWeights || !hEffKaon) { + return 1.0; + } + int bin = hEffKaon->FindBin(candidate.pt()); + float eff = hEffKaon->GetBinContent(bin); + float ptweight = 1.0 / eff; + if (!std::isfinite(ptweight) || ptweight <= 0) { + return 1.0; + } + return ptweight; + } + + template + float getEffWeightProton(const T& candidate) + { + if (!cfgLoadPtEffWeights || !hEffProton) { + return 1.0; + } + int bin = hEffProton->FindBin(candidate.pt()); + float eff = hEffProton->GetBinContent(bin); + float ptweight = 1.0 / eff; + if (!std::isfinite(ptweight) || ptweight <= 0) { + return 1.0; + } + return ptweight; + } + + void processMCGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions, MyMCTracks const& tracks) + { + histos.fill(HIST("MCGenerated/hMC"), 0.5); + if (std::abs(mcCollision.posZ()) < cfgCutVertex) { + histos.fill(HIST("MCGenerated/hMC"), 1.5); + } + auto cent = 0; + + int nchInel = 0; + for (const auto& mcParticle : mcParticles) { + auto pdgcode = std::abs(mcParticle.pdgCode()); + if (mcParticle.isPhysicalPrimary() && (pdgcode == PDG_t::kPiPlus || pdgcode == PDG_t::kKPlus || pdgcode == PDG_t::kProton || pdgcode == PDG_t::kElectron || pdgcode == PDG_t::kMuonMinus)) { + if (std::abs(mcParticle.eta()) < 1.0) { + nchInel = nchInel + 1; + } + } + } + if (nchInel > 0 && std::abs(mcCollision.posZ()) < cfgCutVertex) + histos.fill(HIST("MCGenerated/hMC"), 2.5); + std::vector selectedEvents(collisions.size()); + int nevts = 0; + + for (const auto& collision : collisions) { + if (!collision.sel8() || std::abs(collision.mcCollision().posZ()) > cfgCutVertex) { + continue; + } + if (cfgUseGoodITSLayerAllCut && !(collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))) { + continue; + } + if (cfgEvSelkNoSameBunchPileup && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { + continue; + } + if (cfgEvSelkNoITSROFrameBorder && !(collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder))) { + continue; + } + if (cfgEvSelkNoTimeFrameBorder && !(collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder))) { + continue; + } + if (cfgEvSelUseGoodZvtxFT0vsPV && !(collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV))) { + continue; + } + + // events with selection bits based on occupancy time pattern + if (cfgEvSelUseOcuppancyTimeCut && !(collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) { + continue; + } + + int occupancy = collision.trackOccupancyInTimeRange(); + if (cfgEvSelSetOcuppancyRange && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { + continue; + } + + auto rectrackspart = tracks.sliceBy(perCollision, collision.globalIndex()); + cent = collision.centFT0C(); + if (cfgUseSmallIonAdditionalEventCutInMC && !eventSelectedSmallion(collision, rectrackspart.size(), cent)) { + continue; + } + + selectedEvents[nevts++] = collision.mcCollision_as().globalIndex(); + } + selectedEvents.resize(nevts); + const auto evtReconstructedAndSelected = std::find(selectedEvents.begin(), selectedEvents.end(), mcCollision.globalIndex()) != selectedEvents.end(); + histos.fill(HIST("MCGenerated/hMC"), 3.5); + if (!evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection + return; + } + histos.fill(HIST("MCGenerated/hMC"), 4.5); + histos.fill(HIST("MCGenerated/hCentgen"), cent); + + // initialization of generated level variables for MCclosure + int nbinsHad = 20; + int nbinsPid = 18; + double binsarray[21] = {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.5, 4.0, 5.0, 6.0, 8.0, 10.0}; + TH1D* fPtProfileHadMcGen = new TH1D("fPtProfileHadMcGen", "fPtProfileHadMcGen", 20, binsarray); + TH1D* fPtProfilePiMcGen = new TH1D("fPtProfilePiMcGen", "fPtProfilePiMcGen", 20, binsarray); + TH1D* fPtProfileKaMcGen = new TH1D("fPtProfileKaMcGen", "fPtProfileKaMcGen", 20, binsarray); + TH1D* fPtProfileProtMcGen = new TH1D("fPtProfileProtMcGen", "fPtProfileProtMcGen", 20, binsarray); + double pTsumEtaLeftHadMcGen = 0.0; + double nSumEtaLeftHadMcGen = 0.0; + double pTsumEtaRightHadMcGen = 0.0; + double nSumEtaRightHadMcGen = 0.0; + double nSumEtaLeftPiMcGen = 0.0; + double nSumEtaLeftKaMcGen = 0.0; + double nSumEtaLeftProtMcGen = 0.0; + + // creating phi, pt, eta dstribution of generted MC particles + + // Generated track variables + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.has_mcCollision()) + continue; + + auto pdgcode = std::abs(mcParticle.pdgCode()); + if (!(pdgcode == PDG_t::kPiPlus || + pdgcode == PDG_t::kKPlus || + pdgcode == PDG_t::kProton || + pdgcode == PDG_t::kElectron || + pdgcode == PDG_t::kMuonMinus)) { + continue; // skip this track + } + + if (mcParticle.isPhysicalPrimary()) { + if ((mcParticle.pt() > cfgCutPtLower) && (mcParticle.pt() < cfgCutPtUpper) && (std::abs(mcParticle.eta()) < cfgCutEta)) { + histos.fill(HIST("MCGenerated/hPtEtaPhiCharged_gen"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + + if (pdgcode == PDG_t::kPiPlus) + histos.fill(HIST("MCGenerated/hPtEtaPhiPion_gen"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + + if (pdgcode == PDG_t::kKPlus) + histos.fill(HIST("MCGenerated/hPtEtaPhiKaon_gen"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + + if (pdgcode == PDG_t::kProton) + histos.fill(HIST("MCGenerated/hPtEtaPhiProton_gen"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + + if (cfgMcClosure) { + double trkPt = mcParticle.pt(); + double trkEta = mcParticle.eta(); + + // generated (true) + double effweight = 1.0; + double effweightPion = 1.0; + double effweightKaon = 1.0; + double effweightProton = 1.0; + + if (trkEta < cfgCutEtaLeft) { + fPtProfileHadMcGen->Fill(trkPt); + pTsumEtaLeftHadMcGen += trkPt * effweight; + nSumEtaLeftHadMcGen += effweight; + } + if (trkEta > cfgCutEtaRight) { + pTsumEtaRightHadMcGen += trkPt * effweight; + nSumEtaRightHadMcGen += effweight; + } + + if (trkPt < cfgCutPtUpperPID) { + if (trkEta < cfgCutEtaLeft) { + if (pdgcode == PDG_t::kPiPlus) { + fPtProfilePiMcGen->Fill(trkPt, effweightPion); + nSumEtaLeftPiMcGen += effweightPion; + } + if (pdgcode == PDG_t::kKPlus) { + fPtProfileKaMcGen->Fill(trkPt, effweightKaon); + nSumEtaLeftKaMcGen += effweightKaon; + } + if (pdgcode == PDG_t::kProton && trkPt > cfgCutPtLowerProt) { + fPtProfileProtMcGen->Fill(trkPt, effweightProton); + nSumEtaLeftProtMcGen += effweightProton; + } + } + } + } // end of if(cfgMcClosure) + } + } + } //! end particle loop + + if (cfgMcClosure) { + // selecting subsample and filling profiles + float lRandom = funRndm->Rndm(); + int sampleIndex = static_cast(cfgNSubsample * lRandom); + + // MC generated + if (nSumEtaRightHadMcGen > 0 && nSumEtaLeftHadMcGen > 0) { + for (int i = 0; i < nbinsHad; i++) { + histos.get(HIST("McClosure/Generated/Prof_A_had"))->Fill(cent, fPtProfileHadMcGen->GetBinCenter(i + 1), (fPtProfileHadMcGen->GetBinContent(i + 1) / nSumEtaLeftHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_C_had"))->Fill(cent, fPtProfileHadMcGen->GetBinCenter(i + 1), ((fPtProfileHadMcGen->GetBinContent(i + 1) / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + histos.get(HIST("McClosure/Generated/Prof_Bone_had"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_Btwo_had"))->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_D_had"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + + subSampleMcGen[sampleIndex][0]->Fill(cent, fPtProfileHadMcGen->GetBinCenter(i + 1), (fPtProfileHadMcGen->GetBinContent(i + 1) / nSumEtaLeftHadMcGen)); + subSampleMcGen[sampleIndex][1]->Fill(cent, fPtProfileHadMcGen->GetBinCenter(i + 1), ((fPtProfileHadMcGen->GetBinContent(i + 1) / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][2]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][3]->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + subSampleMcGen[sampleIndex][4]->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + } + } + + if (nSumEtaRightHadMcGen > 0 && nSumEtaLeftHadMcGen > 0 && nSumEtaLeftPiMcGen > 0) { + for (int i = 0; i < nbinsPid; i++) { + histos.get(HIST("McClosure/Generated/Prof_A_pi"))->Fill(cent, fPtProfilePiMcGen->GetBinCenter(i + 1), (fPtProfilePiMcGen->GetBinContent(i + 1) / nSumEtaLeftPiMcGen)); + histos.get(HIST("McClosure/Generated/Prof_C_pi"))->Fill(cent, fPtProfilePiMcGen->GetBinCenter(i + 1), ((fPtProfilePiMcGen->GetBinContent(i + 1) / nSumEtaLeftPiMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + histos.get(HIST("McClosure/Generated/Prof_Bone_pi"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_Btwo_pi"))->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_D_pi"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + + subSampleMcGen[sampleIndex][5]->Fill(cent, fPtProfilePiMcGen->GetBinCenter(i + 1), (fPtProfilePiMcGen->GetBinContent(i + 1) / nSumEtaLeftPiMcGen)); + subSampleMcGen[sampleIndex][6]->Fill(cent, fPtProfilePiMcGen->GetBinCenter(i + 1), ((fPtProfilePiMcGen->GetBinContent(i + 1) / nSumEtaLeftPiMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][7]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][8]->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + subSampleMcGen[sampleIndex][9]->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + } + } + + if (nSumEtaRightHadMcGen > 0 && nSumEtaLeftHadMcGen > 0 && nSumEtaLeftKaMcGen > 0) { + for (int i = 0; i < nbinsPid; i++) { + histos.get(HIST("McClosure/Generated/Prof_A_ka"))->Fill(cent, fPtProfileKaMcGen->GetBinCenter(i + 1), (fPtProfileKaMcGen->GetBinContent(i + 1) / nSumEtaLeftKaMcGen)); + histos.get(HIST("McClosure/Generated/Prof_C_ka"))->Fill(cent, fPtProfileKaMcGen->GetBinCenter(i + 1), ((fPtProfileKaMcGen->GetBinContent(i + 1) / nSumEtaLeftKaMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + histos.get(HIST("McClosure/Generated/Prof_Bone_ka"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_Btwo_ka"))->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_D_ka"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + + subSampleMcGen[sampleIndex][10]->Fill(cent, fPtProfileKaMcGen->GetBinCenter(i + 1), (fPtProfileKaMcGen->GetBinContent(i + 1) / nSumEtaLeftKaMcGen)); + subSampleMcGen[sampleIndex][11]->Fill(cent, fPtProfileKaMcGen->GetBinCenter(i + 1), ((fPtProfileKaMcGen->GetBinContent(i + 1) / nSumEtaLeftKaMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][12]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][13]->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + subSampleMcGen[sampleIndex][14]->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + } + } + if (nSumEtaRightHadMcGen > 0 && nSumEtaLeftHadMcGen > 0 && nSumEtaLeftProtMcGen > 0) { + for (int i = 1; i < nbinsPid; i++) { + histos.get(HIST("McClosure/Generated/Prof_A_prot"))->Fill(cent, fPtProfileProtMcGen->GetBinCenter(i + 1), (fPtProfileProtMcGen->GetBinContent(i + 1) / nSumEtaLeftProtMcGen)); + histos.get(HIST("McClosure/Generated/Prof_C_prot"))->Fill(cent, fPtProfileProtMcGen->GetBinCenter(i + 1), ((fPtProfileProtMcGen->GetBinContent(i + 1) / nSumEtaLeftProtMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + histos.get(HIST("McClosure/Generated/Prof_Bone_prot"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_Btwo_prot"))->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + histos.get(HIST("McClosure/Generated/Prof_D_prot"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + + subSampleMcGen[sampleIndex][15]->Fill(cent, fPtProfileProtMcGen->GetBinCenter(i + 1), (fPtProfileProtMcGen->GetBinContent(i + 1) / nSumEtaLeftProtMcGen)); + subSampleMcGen[sampleIndex][16]->Fill(cent, fPtProfileProtMcGen->GetBinCenter(i + 1), ((fPtProfileProtMcGen->GetBinContent(i + 1) / nSumEtaLeftProtMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][17]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen) * (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen))); + subSampleMcGen[sampleIndex][18]->Fill(cent, 0.5, (pTsumEtaLeftHadMcGen / nSumEtaLeftHadMcGen)); + subSampleMcGen[sampleIndex][19]->Fill(cent, 0.5, (pTsumEtaRightHadMcGen / nSumEtaRightHadMcGen)); + } + } + } + + fPtProfileHadMcGen->Delete(); + fPtProfilePiMcGen->Delete(); + fPtProfileKaMcGen->Delete(); + fPtProfileProtMcGen->Delete(); + } + PROCESS_SWITCH(V0ptHadPiKaProt, processMCGen, "Process Monte-carlo generated data", false); + + // process MC recosnstructed data + void processMCRec(MyMCRecCollisions::iterator const& collision, MyMCTracks const& tracks, aod::McCollisions const&, aod::McParticles const& mcParticles) + { + histos.fill(HIST("MCGenerated/hMC"), 5.5); + + if (!collision.has_mcCollision()) { + return; + } + histos.fill(HIST("MCGenerated/hMC"), 6.5); + + if (!eventSelectionDefaultCuts(collision)) { + return; + } + histos.fill(HIST("MCGenerated/hMC"), 7.5); + + fillMultCorrPlotsBeforeSel(collision, tracks); + + const auto centralityFT0C = collision.centFT0C(); + if (cfgUseSmallIonAdditionalEventCut && !eventSelectedSmallion(collision, tracks.size(), centralityFT0C)) + return; + + if (cfgUseSmallIonAdditionalEventCut) { + fillMultCorrPlotsAfterSel(collision, tracks); + } + + histos.fill(HIST("MCGenerated/hMC"), 8.5); + + // Centrality + double cent = 0.0; + if (cfgCentralityChoice == kFT0C) + cent = collision.centFT0C(); + else if (cfgCentralityChoice == kFT0A) + cent = collision.centFT0A(); + else if (cfgCentralityChoice == kFT0M) + cent = collision.centFT0M(); + else if (cfgCentralityChoice == kFV0A) + cent = collision.centFV0A(); + + histos.fill(HIST("hZvtx_after_sel"), collision.posZ()); + histos.fill(HIST("hCentrality"), cent); + histos.fill(HIST("Hist2D_globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); + histos.fill(HIST("Hist2D_cent_nch"), tracks.size(), centralityFT0C); + + // Calculating generated level pt distributions + auto mcColl = collision.mcCollision(); + // Slice particles belonging only to this MC collision + auto particlesThisEvent = mcParticles.sliceBy(perMcCollision, mcColl.globalIndex()); + + for (const auto& mcParticle : particlesThisEvent) { + if (!mcParticle.has_mcCollision()) + continue; + + auto pdgcode = std::abs(mcParticle.pdgCode()); + if (!(pdgcode == PDG_t::kPiPlus || + pdgcode == PDG_t::kKPlus || + pdgcode == PDG_t::kProton || + pdgcode == PDG_t::kElectron || + pdgcode == PDG_t::kMuonMinus)) { + continue; // skip this track + } + + if (mcParticle.isPhysicalPrimary()) { + if ((mcParticle.pt() > cfgCutPtLower) && (mcParticle.pt() < cfgCutPtUpper) && (std::abs(mcParticle.eta()) < cfgCutEta)) { + histos.fill(HIST("MCGenerated/hPtEtaPhiCharged"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + + if (pdgcode == PDG_t::kPiPlus) + histos.fill(HIST("MCGenerated/hPtEtaPhiPion"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + + if (pdgcode == PDG_t::kKPlus) + histos.fill(HIST("MCGenerated/hPtEtaPhiKaon"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + + if (pdgcode == PDG_t::kProton) + histos.fill(HIST("MCGenerated/hPtEtaPhiProton"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi()); + } + } + } //! end mc particle loop + + // MC reconstructed Analysis variables for v0(pT) + int nbinsHad = 20; + int nbinsPid = 18; + double binsarray[21] = {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.5, 4.0, 5.0, 6.0, 8.0, 10.0}; + + // Raw + TH1D* fPtProfileHadMcRecRaw = new TH1D("fPtProfileHadMcRecRaw", "fPtProfileHadMcRecRaw", 20, binsarray); + TH1D* fPtProfilePiMcRecRaw = new TH1D("fPtProfilePiMcRecRaw", "fPtProfilePiMcRecRaw", 20, binsarray); + TH1D* fPtProfileKaMcRecRaw = new TH1D("fPtProfileKaMcRecRaw", "fPtProfileKaMcRecRaw", 20, binsarray); + TH1D* fPtProfileProtMcRecRaw = new TH1D("fPtProfileProtMcRecRaw", "fPtProfileProtMcRecRaw", 20, binsarray); + double pTsumEtaLeftHadMcRecRaw = 0.0; + double nSumEtaLeftHadMcRecRaw = 0.0; + double pTsumEtaRightHadMcRecRaw = 0.0; + double nSumEtaRightHadMcRecRaw = 0.0; + double nSumEtaLeftPiMcRecRaw = 0.0; + double nSumEtaLeftKaMcRecRaw = 0.0; + double nSumEtaLeftProtMcRecRaw = 0.0; + + // Eff corrected + TH1D* fPtProfileHadMcRecCorr = new TH1D("fPtProfileHadMcRecCorr", "fPtProfileHadMcRecCorr", 20, binsarray); + TH1D* fPtProfilePiMcRecCorr = new TH1D("fPtProfilePiMcRecCorr", "fPtProfilePiMcRecCorr", 20, binsarray); + TH1D* fPtProfileKaMcRecCorr = new TH1D("fPtProfileKaMcRecCorr", "fPtProfileKaMcRecCorr", 20, binsarray); + TH1D* fPtProfileProtMcRecCorr = new TH1D("fPtProfileProtMcRecCorr", "fPtProfileProtMcRecCorr", 20, binsarray); + double pTsumEtaLeftHadMcRecCorr = 0.0; + double nSumEtaLeftHadMcRecCorr = 0.0; + double pTsumEtaRightHadMcRecCorr = 0.0; + double nSumEtaRightHadMcRecCorr = 0.0; + double nSumEtaLeftPiMcRecCorr = 0.0; + double nSumEtaLeftKaMcRecCorr = 0.0; + double nSumEtaLeftProtMcRecCorr = 0.0; + + for (const auto& track : tracks) { // Loop over tracks + + if (!track.has_collision()) { + continue; + } + if (!track.has_mcParticle()) { //! check if track has corresponding MC particle + continue; + } + + auto particle = track.mcParticle(); + if (!particle.has_mcCollision()) { + continue; + } + + if (!track.isPVContributor()) { + continue; + } + + if (!(track.itsNCls() > cfgITScluster) || !(track.tpcNClsFound() >= cfgTPCcluster) || !(track.tpcNClsCrossedRows() >= cfgTPCnCrossedRows)) { + continue; + } + + if (cfgUsePtDepDCAxy && !(std::abs(track.dcaXY()) < fPtDepDCAxy->Eval(track.pt()))) { + continue; + } + if (cfgUsePtDepDCAz && !(std::abs(track.dcaZ()) < fPtDepDCAz->Eval(track.pt()))) { + continue; + } + + if (track.sign() == 0) + continue; + + if (particle.isPhysicalPrimary()) { + if ((particle.pt() > cfgCutPtLower) && (particle.pt() < cfgCutPtUpper) && (std::abs(particle.eta()) < cfgCutEta)) { + + histos.fill(HIST("MCReconstructed/hPtEtaPhiChargedParticle"), particle.pt(), particle.eta(), particle.phi()); + histos.fill(HIST("MCReconstructed/hPtEtaPhiChargedTrack"), track.pt(), track.eta(), track.phi()); + + // PID QAs before selection + double nSigmaTpcPi = track.tpcNSigmaPi(); + double nSigmaTpcKa = track.tpcNSigmaKa(); + double nSigmaTpcProt = track.tpcNSigmaPr(); + double nSigmaTofPi = track.tofNSigmaPi(); + double nSigmaTofKa = track.tofNSigmaKa(); + double nSigmaTofProt = track.tofNSigmaPr(); + double nSigmaItsPi = itsResponse.nSigmaITS(track); + double nSigmaItsKa = itsResponse.nSigmaITS(track); + double nSigmaItsProt = itsResponse.nSigmaITS(track); + + histos.fill(HIST("h2DnsigmaPionTpcVsPtBeforeCut"), track.pt(), nSigmaTpcPi); + histos.fill(HIST("h2DnsigmaKaonTpcVsPtBeforeCut"), track.pt(), nSigmaTpcKa); + histos.fill(HIST("h2DnsigmaProtonTpcVsPtBeforeCut"), track.pt(), nSigmaTpcProt); + histos.fill(HIST("h2DnsigmaPionTofVsPtBeforeCut"), track.pt(), nSigmaTofPi); + histos.fill(HIST("h2DnsigmaKaonTofVsPtBeforeCut"), track.pt(), nSigmaTofKa); + histos.fill(HIST("h2DnsigmaProtonTofVsPtBeforeCut"), track.pt(), nSigmaTofProt); + histos.fill(HIST("h2DnsigmaPionTpcVsTofBeforeCut"), nSigmaTpcPi, nSigmaTofPi); + histos.fill(HIST("h2DnsigmaKaonTpcVsTofBeforeCut"), nSigmaTpcKa, nSigmaTofKa); + histos.fill(HIST("h2DnsigmaProtonTpcVsTofBeforeCut"), nSigmaTpcProt, nSigmaTofProt); + + histos.fill(HIST("h2DnsigmaPionItsVsPtBeforeCut"), track.pt(), nSigmaItsPi); + histos.fill(HIST("h2DnsigmaKaonItsVsPtBeforeCut"), track.pt(), nSigmaItsKa); + histos.fill(HIST("h2DnsigmaProtonItsVsPtBeforeCut"), track.pt(), nSigmaItsProt); + histos.fill(HIST("h2DnsigmaPionItsVsTofBeforeCut"), track.pt(), nSigmaItsPi, nSigmaTofPi); + histos.fill(HIST("h2DnsigmaKaonItsVsTofBeforeCut"), track.pt(), nSigmaItsKa, nSigmaTofKa); + histos.fill(HIST("h2DnsigmaProtonItsVsTofBeforeCut"), track.pt(), nSigmaItsProt, nSigmaTofProt); + + // identified particles selection + bool isPion = false; + bool isKaon = false; + bool isProton = false; + + if (cfgUseRun3V2PID) { + int pidVal = getNsigmaPID(track); + if (pidVal == PIONS + 1) + isPion = true; + if (pidVal == KAONS + 1) + isKaon = true; + if (pidVal == PROTONS + 1) + isProton = true; + } else { + isPion = selectionPion(track); + isKaon = selectionKaon(track); + isProton = selectionProton(track); + } + + // PID QAs after selection + if (isPion) { + histos.fill(HIST("h2DnsigmaPionTpcVsPtAfterCut"), track.pt(), nSigmaTpcPi); + histos.fill(HIST("h2DnsigmaPionTofVsPtAfterCut"), track.pt(), nSigmaTofPi); + histos.fill(HIST("h2DnsigmaPionTpcVsTofAfterCut"), track.pt(), nSigmaTpcPi, nSigmaTofPi); + histos.fill(HIST("h2DnsigmaPionItsVsPtAfterCut"), track.pt(), nSigmaItsPi); + histos.fill(HIST("h2DnsigmaPionItsVsTofAfterCut"), track.pt(), nSigmaItsPi, nSigmaTofPi); + } + if (isKaon) { + histos.fill(HIST("h2DnsigmaKaonTpcVsPtAfterCut"), track.pt(), nSigmaTpcKa); + histos.fill(HIST("h2DnsigmaKaonTofVsPtAfterCut"), track.pt(), nSigmaTofKa); + histos.fill(HIST("h2DnsigmaKaonTpcVsTofAfterCut"), track.pt(), nSigmaTpcKa, nSigmaTofKa); + histos.fill(HIST("h2DnsigmaKaonItsVsPtAfterCut"), track.pt(), nSigmaItsKa); + histos.fill(HIST("h2DnsigmaKaonItsVsTofAfterCut"), track.pt(), nSigmaItsKa, nSigmaTofKa); + } + if (isProton) { + histos.fill(HIST("h2DnsigmaProtonTpcVsPtAfterCut"), track.pt(), nSigmaTpcProt); + histos.fill(HIST("h2DnsigmaProtonTofVsPtAfterCut"), track.pt(), nSigmaTofProt); + histos.fill(HIST("h2DnsigmaProtonTpcVsTofAfterCut"), track.pt(), nSigmaTpcProt, nSigmaTofProt); + histos.fill(HIST("h2DnsigmaProtonItsVsPtAfterCut"), track.pt(), nSigmaItsProt); + histos.fill(HIST("h2DnsigmaProtonItsVsTofAfterCut"), track.pt(), nSigmaItsProt, nSigmaTofProt); + } + + auto pdgcodeRec = std::abs(particle.pdgCode()); + + if (isPion) { + histos.fill(HIST("MCReconstructed/hPtEtaPhiPionParticle"), particle.pt(), particle.eta(), particle.phi()); + histos.fill(HIST("MCReconstructed/hPtEtaPhiPionTrack"), track.pt(), track.eta(), track.phi()); + if (pdgcodeRec == PDG_t::kPiPlus) + histos.fill(HIST("MCReconstructed/hPtEtaPhiTruePionTrack"), track.pt(), track.eta(), track.phi()); + } + + if (isKaon) { + histos.fill(HIST("MCReconstructed/hPtEtaPhiKaonParticle"), particle.pt(), particle.eta(), particle.phi()); + histos.fill(HIST("MCReconstructed/hPtEtaPhiKaonTrack"), track.pt(), track.eta(), track.phi()); + if (pdgcodeRec == PDG_t::kKPlus) + histos.fill(HIST("MCReconstructed/hPtEtaPhiTrueKaonTrack"), track.pt(), track.eta(), track.phi()); + } + + if (isProton) { + histos.fill(HIST("MCReconstructed/hPtEtaPhiProtonParticle"), particle.pt(), particle.eta(), particle.phi()); + histos.fill(HIST("MCReconstructed/hPtEtaPhiProtonTrack"), track.pt(), track.eta(), track.phi()); + if (pdgcodeRec == PDG_t::kProton) + histos.fill(HIST("MCReconstructed/hPtEtaPhiTrueProtonTrack"), track.pt(), track.eta(), track.phi()); + } + + if (cfgMcClosure) { + double trkPt = particle.pt(); + double trkEta = particle.eta(); + + // Raw + double effweight = 1.0; + double effweightPion = 1.0; + double effweightKaon = 1.0; + double effweightProton = 1.0; + + if (track.sign() != 0) { + if (trkEta < cfgCutEtaLeft) { + fPtProfileHadMcRecRaw->Fill(trkPt); + pTsumEtaLeftHadMcRecRaw += trkPt * effweight; + nSumEtaLeftHadMcRecRaw += effweight; + } + if (trkEta > cfgCutEtaRight) { + pTsumEtaRightHadMcRecRaw += trkPt * effweight; + nSumEtaRightHadMcRecRaw += effweight; + } + } + + if (trkPt < cfgCutPtUpperPID) { + if (trkEta < cfgCutEtaLeft) { + if (isPion) { + fPtProfilePiMcRecRaw->Fill(trkPt, effweightPion); + nSumEtaLeftPiMcRecRaw += effweightPion; + } + if (isKaon) { + fPtProfileKaMcRecRaw->Fill(trkPt, effweightKaon); + nSumEtaLeftKaMcRecRaw += effweightKaon; + } + if (isProton && trkPt > cfgCutPtLowerProt) { + fPtProfileProtMcRecRaw->Fill(trkPt, effweightProton); + nSumEtaLeftProtMcRecRaw += effweightProton; + } + } + } + + // Efficiency corrected + if (cfgLoadPtEffWeights) { + effweight = getEffWeightAllCharged(track); // NUE weight + effweightPion = getEffWeightPion(track); // NUE weight for pion + effweightKaon = getEffWeightKaon(track); // NUE weight for kaon + effweightProton = getEffWeightProton(track); // NUE weight for proton + } + if (track.sign() != 0) { + if (trkEta < cfgCutEtaLeft) { + fPtProfileHadMcRecCorr->Fill(trkPt); + pTsumEtaLeftHadMcRecCorr += trkPt * effweight; + nSumEtaLeftHadMcRecCorr += effweight; + } + if (trkEta > cfgCutEtaRight) { + pTsumEtaRightHadMcRecCorr += trkPt * effweight; + nSumEtaRightHadMcRecCorr += effweight; + } + } + + if (trkPt < cfgCutPtUpperPID) { + if (trkEta < cfgCutEtaLeft) { + if (isPion) { + fPtProfilePiMcRecCorr->Fill(trkPt, effweightPion); + nSumEtaLeftPiMcRecCorr += effweightPion; + } + if (isKaon) { + fPtProfileKaMcRecCorr->Fill(trkPt, effweightKaon); + nSumEtaLeftKaMcRecCorr += effweightKaon; + } + if (isProton && trkPt > cfgCutPtLowerProt) { + fPtProfileProtMcRecCorr->Fill(trkPt, effweightProton); + nSumEtaLeftProtMcRecCorr += effweightProton; + } + } + } + } // end of if(cfgMcClosure) + } + } + } // end track loop + + if (cfgMcClosure) { + // selecting subsample and filling profiles + float lRandom = funRndm->Rndm(); + int sampleIndex = static_cast(cfgNSubsample * lRandom); + + // MC reconstructed Raw; not corrected for efficiency + if (nSumEtaRightHadMcRecRaw > 0 && nSumEtaLeftHadMcRecRaw > 0) { + for (int i = 0; i < nbinsHad; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_A_had"))->Fill(cent, fPtProfileHadMcRecRaw->GetBinCenter(i + 1), (fPtProfileHadMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_C_had"))->Fill(cent, fPtProfileHadMcRecRaw->GetBinCenter(i + 1), ((fPtProfileHadMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Bone_had"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Btwo_had"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_D_had"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + + subSampleMcRecRaw[sampleIndex][0]->Fill(cent, fPtProfileHadMcRecRaw->GetBinCenter(i + 1), (fPtProfileHadMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftHadMcRecRaw)); + subSampleMcRecRaw[sampleIndex][1]->Fill(cent, fPtProfileHadMcRecRaw->GetBinCenter(i + 1), ((fPtProfileHadMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][2]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][3]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + subSampleMcRecRaw[sampleIndex][4]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + } + } + + if (nSumEtaRightHadMcRecRaw > 0 && nSumEtaLeftHadMcRecRaw > 0 && nSumEtaLeftPiMcRecRaw > 0) { + for (int i = 0; i < nbinsPid; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_A_pi"))->Fill(cent, fPtProfilePiMcRecRaw->GetBinCenter(i + 1), (fPtProfilePiMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftPiMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_C_pi"))->Fill(cent, fPtProfilePiMcRecRaw->GetBinCenter(i + 1), ((fPtProfilePiMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftPiMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Bone_pi"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Btwo_pi"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_D_pi"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + + subSampleMcRecRaw[sampleIndex][5]->Fill(cent, fPtProfilePiMcRecRaw->GetBinCenter(i + 1), (fPtProfilePiMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftPiMcRecRaw)); + subSampleMcRecRaw[sampleIndex][6]->Fill(cent, fPtProfilePiMcRecRaw->GetBinCenter(i + 1), ((fPtProfilePiMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftPiMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][7]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][8]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + subSampleMcRecRaw[sampleIndex][9]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + } + } + + if (nSumEtaRightHadMcRecRaw > 0 && nSumEtaLeftHadMcRecRaw > 0 && nSumEtaLeftKaMcRecRaw > 0) { + for (int i = 0; i < nbinsPid; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_A_ka"))->Fill(cent, fPtProfileKaMcRecRaw->GetBinCenter(i + 1), (fPtProfileKaMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftKaMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_C_ka"))->Fill(cent, fPtProfileKaMcRecRaw->GetBinCenter(i + 1), ((fPtProfileKaMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftKaMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Bone_ka"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Btwo_ka"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_D_ka"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + + subSampleMcRecRaw[sampleIndex][10]->Fill(cent, fPtProfileKaMcRecRaw->GetBinCenter(i + 1), (fPtProfileKaMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftKaMcRecRaw)); + subSampleMcRecRaw[sampleIndex][11]->Fill(cent, fPtProfileKaMcRecRaw->GetBinCenter(i + 1), ((fPtProfileKaMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftKaMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][12]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][13]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + subSampleMcRecRaw[sampleIndex][14]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + } + } + if (nSumEtaRightHadMcRecRaw > 0 && nSumEtaLeftHadMcRecRaw > 0 && nSumEtaLeftProtMcRecRaw > 0) { + for (int i = 1; i < nbinsPid; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_A_prot"))->Fill(cent, fPtProfileProtMcRecRaw->GetBinCenter(i + 1), (fPtProfileProtMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftProtMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_C_prot"))->Fill(cent, fPtProfileProtMcRecRaw->GetBinCenter(i + 1), ((fPtProfileProtMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftProtMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Bone_prot"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_Btwo_prot"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + histosMc.get(HIST("McClosure/Reconstructed/Raw/Prof_D_prot"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + + subSampleMcRecRaw[sampleIndex][15]->Fill(cent, fPtProfileProtMcRecRaw->GetBinCenter(i + 1), (fPtProfileProtMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftProtMcRecRaw)); + subSampleMcRecRaw[sampleIndex][16]->Fill(cent, fPtProfileProtMcRecRaw->GetBinCenter(i + 1), ((fPtProfileProtMcRecRaw->GetBinContent(i + 1) / nSumEtaLeftProtMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][17]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw) * (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw))); + subSampleMcRecRaw[sampleIndex][18]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecRaw / nSumEtaLeftHadMcRecRaw)); + subSampleMcRecRaw[sampleIndex][19]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecRaw / nSumEtaRightHadMcRecRaw)); + } + } + + // MC Reconstructed Efficiency corrected + + if (nSumEtaRightHadMcRecCorr > 0 && nSumEtaLeftHadMcRecCorr > 0) { + for (int i = 0; i < nbinsHad; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_A_had"))->Fill(cent, fPtProfileHadMcRecCorr->GetBinCenter(i + 1), (fPtProfileHadMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_C_had"))->Fill(cent, fPtProfileHadMcRecCorr->GetBinCenter(i + 1), ((fPtProfileHadMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Bone_had"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Btwo_had"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_D_had"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + + subSampleMcRecCorr[sampleIndex][0]->Fill(cent, fPtProfileHadMcRecCorr->GetBinCenter(i + 1), (fPtProfileHadMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftHadMcRecCorr)); + subSampleMcRecCorr[sampleIndex][1]->Fill(cent, fPtProfileHadMcRecCorr->GetBinCenter(i + 1), ((fPtProfileHadMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][2]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][3]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + subSampleMcRecCorr[sampleIndex][4]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + } + } + + if (nSumEtaRightHadMcRecCorr > 0 && nSumEtaLeftHadMcRecCorr > 0 && nSumEtaLeftPiMcRecCorr > 0) { + for (int i = 0; i < nbinsPid; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_A_pi"))->Fill(cent, fPtProfilePiMcRecCorr->GetBinCenter(i + 1), (fPtProfilePiMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftPiMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_C_pi"))->Fill(cent, fPtProfilePiMcRecCorr->GetBinCenter(i + 1), ((fPtProfilePiMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftPiMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Bone_pi"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Btwo_pi"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_D_pi"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + + subSampleMcRecCorr[sampleIndex][5]->Fill(cent, fPtProfilePiMcRecCorr->GetBinCenter(i + 1), (fPtProfilePiMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftPiMcRecCorr)); + subSampleMcRecCorr[sampleIndex][6]->Fill(cent, fPtProfilePiMcRecCorr->GetBinCenter(i + 1), ((fPtProfilePiMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftPiMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][7]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][8]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + subSampleMcRecCorr[sampleIndex][9]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + } + } + + if (nSumEtaRightHadMcRecCorr > 0 && nSumEtaLeftHadMcRecCorr > 0 && nSumEtaLeftKaMcRecCorr > 0) { + for (int i = 0; i < nbinsPid; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_A_ka"))->Fill(cent, fPtProfileKaMcRecCorr->GetBinCenter(i + 1), (fPtProfileKaMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftKaMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_C_ka"))->Fill(cent, fPtProfileKaMcRecCorr->GetBinCenter(i + 1), ((fPtProfileKaMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftKaMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Bone_ka"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Btwo_ka"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_D_ka"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + + subSampleMcRecCorr[sampleIndex][10]->Fill(cent, fPtProfileKaMcRecCorr->GetBinCenter(i + 1), (fPtProfileKaMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftKaMcRecCorr)); + subSampleMcRecCorr[sampleIndex][11]->Fill(cent, fPtProfileKaMcRecCorr->GetBinCenter(i + 1), ((fPtProfileKaMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftKaMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][12]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][13]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + subSampleMcRecCorr[sampleIndex][14]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + } + } + if (nSumEtaRightHadMcRecCorr > 0 && nSumEtaLeftHadMcRecCorr > 0 && nSumEtaLeftProtMcRecCorr > 0) { + for (int i = 1; i < nbinsPid; i++) { + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_A_prot"))->Fill(cent, fPtProfileProtMcRecCorr->GetBinCenter(i + 1), (fPtProfileProtMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftProtMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_C_prot"))->Fill(cent, fPtProfileProtMcRecCorr->GetBinCenter(i + 1), ((fPtProfileProtMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftProtMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Bone_prot"))->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_Btwo_prot"))->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + histosMc.get(HIST("McClosure/Reconstructed/Corr/Prof_D_prot"))->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + + subSampleMcRecCorr[sampleIndex][15]->Fill(cent, fPtProfileProtMcRecCorr->GetBinCenter(i + 1), (fPtProfileProtMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftProtMcRecCorr)); + subSampleMcRecCorr[sampleIndex][16]->Fill(cent, fPtProfileProtMcRecCorr->GetBinCenter(i + 1), ((fPtProfileProtMcRecCorr->GetBinContent(i + 1) / nSumEtaLeftProtMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][17]->Fill(cent, 0.5, ((pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr) * (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr))); + subSampleMcRecCorr[sampleIndex][18]->Fill(cent, 0.5, (pTsumEtaLeftHadMcRecCorr / nSumEtaLeftHadMcRecCorr)); + subSampleMcRecCorr[sampleIndex][19]->Fill(cent, 0.5, (pTsumEtaRightHadMcRecCorr / nSumEtaRightHadMcRecCorr)); + } + } + } + + fPtProfileHadMcRecRaw->Delete(); + fPtProfilePiMcRecRaw->Delete(); + fPtProfileKaMcRecRaw->Delete(); + fPtProfileProtMcRecRaw->Delete(); + + fPtProfileHadMcRecCorr->Delete(); + fPtProfilePiMcRecCorr->Delete(); + fPtProfileKaMcRecCorr->Delete(); + fPtProfileProtMcRecCorr->Delete(); + } + PROCESS_SWITCH(V0ptHadPiKaProt, processMCRec, "Process Monte-carlo reconstructed data", false); + // process Data - void process(AodCollisions::iterator const& coll, aod::BCsWithTimestamps const&, AodTracks const& inputTracks) + void processData(AodCollisions::iterator const& coll, aod::BCsWithTimestamps const&, AodTracks const& inputTracks) { if (!eventSelectionDefaultCuts(coll)) { return; @@ -751,6 +1965,7 @@ struct V0ptHadPiKaProt { int nbinsHad = 20; int nbinsPid = 18; double binsarray[21] = {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.5, 4.0, 5.0, 6.0, 8.0, 10.0}; + TH1D* fPtProfileHad = new TH1D("fPtProfileHad", "fPtProfileHad", 20, binsarray); TH1D* fPtProfilePi = new TH1D("fPtProfilePi", "fPtProfilePi", 20, binsarray); TH1D* fPtProfileKa = new TH1D("fPtProfileKa", "fPtProfileKa", 20, binsarray); @@ -807,43 +2022,50 @@ struct V0ptHadPiKaProt { double trkEta = track.eta(); double trkPhi = track.phi(); + double effweight = 1.0; + if (cfgLoadPtEffWeights) { + effweight = getEffWeightAllCharged(track); // NUE weight + } + // inclusive charged particles if (track.sign() != 0) { if (trkEta < cfgCutEtaLeft) { fPtProfileHad->Fill(trkPt); - pTsumEtaLeftHad += trkPt; - nSumEtaLeftHad += 1.0; + pTsumEtaLeftHad += trkPt * effweight; + nSumEtaLeftHad += effweight; } if (trkEta > cfgCutEtaRight) { - pTsumEtaRightHad += trkPt; - nSumEtaRightHad += 1.0; + pTsumEtaRightHad += trkPt * effweight; + nSumEtaRightHad += effweight; } } - // fill subevent B for f(pT) in v02(pT) - if (track.sign() != 0 && trkPt < cfgCutPtMaxForV02) { - if (std::abs(trkEta) < cfgCutEtaWindowB) { - fPtProfileHadInWinB->Fill(trkPt); - nSumInWinB += 1.0; - } - } double phiweight = 1.0; if (cfgLoadPhiWeights) { - phiweight = getPhiWeight(track, coll.posZ()); + phiweight = getPhiWeight(track, coll.posZ()); // NUA weight } - // fill subevent C for v2^2 in v02(pT) + + double weight = phiweight * effweight; + if (track.sign() != 0 && trkPt < cfgCutPtMaxForV02) { histos.fill(HIST("h3DVtxZetaPhi"), coll.posZ(), trkEta, trkPhi); - if (cfgCutEtaWindowB < trkEta && trkEta < 0.8) { - vecQInWinC += phiweight * TComplex(TMath::Cos(2. * trkPhi), TMath::Sin(2. * trkPhi)); - nSumInWinC += phiweight; + + // fill subevent C for v2^2 in v02(pT) + if (cfgCutEtaWindowB < trkEta && trkEta < cfgCutEta) { + vecQInWinC += weight * TComplex(TMath::Cos(2. * trkPhi), TMath::Sin(2. * trkPhi)); + nSumInWinC += weight; } - } - // fill subevent A for v2^2 in v02(pT) - if (track.sign() != 0 && trkPt < cfgCutPtMaxForV02) { - if (-0.8 < trkEta && trkEta < -1.0 * cfgCutEtaWindowB) { - vecQInWinA += phiweight * TComplex(TMath::Cos(2. * trkPhi), TMath::Sin(2. * trkPhi)); - nSumInWinA += phiweight; + + // fill subevent A for v2^2 in v02(pT) + if (-1.0 * cfgCutEta < trkEta && trkEta < -1.0 * cfgCutEtaWindowB) { + vecQInWinA += weight * TComplex(TMath::Cos(2. * trkPhi), TMath::Sin(2. * trkPhi)); + nSumInWinA += weight; + } + + // fill subevent B for f(pT) in v02(pT) + if (std::abs(trkEta) < cfgCutEtaWindowB) { + fPtProfileHadInWinB->Fill(trkPt, effweight); + nSumInWinB += effweight; } } @@ -854,6 +2076,9 @@ struct V0ptHadPiKaProt { double nSigmaTofPi = track.tofNSigmaPi(); double nSigmaTofKa = track.tofNSigmaKa(); double nSigmaTofProt = track.tofNSigmaPr(); + double nSigmaItsPi = itsResponse.nSigmaITS(track); + double nSigmaItsKa = itsResponse.nSigmaITS(track); + double nSigmaItsProt = itsResponse.nSigmaITS(track); histos.fill(HIST("h2DnsigmaPionTpcVsPtBeforeCut"), trkPt, nSigmaTpcPi); histos.fill(HIST("h2DnsigmaKaonTpcVsPtBeforeCut"), trkPt, nSigmaTpcKa); histos.fill(HIST("h2DnsigmaProtonTpcVsPtBeforeCut"), trkPt, nSigmaTpcProt); @@ -864,6 +2089,13 @@ struct V0ptHadPiKaProt { histos.fill(HIST("h2DnsigmaKaonTpcVsTofBeforeCut"), nSigmaTpcKa, nSigmaTofKa); histos.fill(HIST("h2DnsigmaProtonTpcVsTofBeforeCut"), nSigmaTpcProt, nSigmaTofProt); + histos.fill(HIST("h2DnsigmaPionItsVsPtBeforeCut"), trkPt, nSigmaItsPi); + histos.fill(HIST("h2DnsigmaKaonItsVsPtBeforeCut"), trkPt, nSigmaItsKa); + histos.fill(HIST("h2DnsigmaProtonItsVsPtBeforeCut"), trkPt, nSigmaItsProt); + histos.fill(HIST("h2DnsigmaPionItsVsTofBeforeCut"), trkPt, nSigmaItsPi, nSigmaTofPi); + histos.fill(HIST("h2DnsigmaKaonItsVsTofBeforeCut"), trkPt, nSigmaItsKa, nSigmaTofKa); + histos.fill(HIST("h2DnsigmaProtonItsVsTofBeforeCut"), trkPt, nSigmaItsProt, nSigmaTofProt); + // identified particles selection bool isPion = false; bool isKaon = false; @@ -887,33 +2119,48 @@ struct V0ptHadPiKaProt { if (isPion) { histos.fill(HIST("h2DnsigmaPionTpcVsPtAfterCut"), trkPt, nSigmaTpcPi); histos.fill(HIST("h2DnsigmaPionTofVsPtAfterCut"), trkPt, nSigmaTofPi); - histos.fill(HIST("h2DnsigmaPionTpcVsTofAfterCut"), nSigmaTpcPi, nSigmaTofPi); + histos.fill(HIST("h2DnsigmaPionTpcVsTofAfterCut"), trkPt, nSigmaTpcPi, nSigmaTofPi); + histos.fill(HIST("h2DnsigmaPionItsVsPtAfterCut"), trkPt, nSigmaItsPi); + histos.fill(HIST("h2DnsigmaPionItsVsTofAfterCut"), trkPt, nSigmaItsPi, nSigmaTofPi); } if (isKaon) { histos.fill(HIST("h2DnsigmaKaonTpcVsPtAfterCut"), trkPt, nSigmaTpcKa); histos.fill(HIST("h2DnsigmaKaonTofVsPtAfterCut"), trkPt, nSigmaTofKa); - histos.fill(HIST("h2DnsigmaKaonTpcVsTofAfterCut"), nSigmaTpcKa, nSigmaTofKa); + histos.fill(HIST("h2DnsigmaKaonTpcVsTofAfterCut"), trkPt, nSigmaTpcKa, nSigmaTofKa); + histos.fill(HIST("h2DnsigmaKaonItsVsPtAfterCut"), trkPt, nSigmaItsKa); + histos.fill(HIST("h2DnsigmaKaonItsVsTofAfterCut"), trkPt, nSigmaItsKa, nSigmaTofKa); } if (isProton) { histos.fill(HIST("h2DnsigmaProtonTpcVsPtAfterCut"), trkPt, nSigmaTpcProt); histos.fill(HIST("h2DnsigmaProtonTofVsPtAfterCut"), trkPt, nSigmaTofProt); - histos.fill(HIST("h2DnsigmaProtonTpcVsTofAfterCut"), nSigmaTpcProt, nSigmaTofProt); + histos.fill(HIST("h2DnsigmaProtonTpcVsTofAfterCut"), trkPt, nSigmaTpcProt, nSigmaTofProt); + histos.fill(HIST("h2DnsigmaProtonItsVsPtAfterCut"), trkPt, nSigmaItsProt); + histos.fill(HIST("h2DnsigmaProtonItsVsTofAfterCut"), trkPt, nSigmaItsProt, nSigmaTofProt); + } + + double effweightPion = 1.0; + double effweightKaon = 1.0; + double effweightProton = 1.0; + if (cfgLoadPtEffWeights) { + effweightPion = getEffWeightPion(track); // NUE weight for pion + effweightKaon = getEffWeightKaon(track); // NUE weight for kaon + effweightProton = getEffWeightProton(track); // NUE weight for proton } if (track.sign() != 0) { if (trkPt < cfgCutPtUpperPID) { if (trkEta < cfgCutEtaLeft) { if (isPion) { - fPtProfilePi->Fill(trkPt); - nSumEtaLeftPi += 1.0; + fPtProfilePi->Fill(trkPt, effweightPion); + nSumEtaLeftPi += effweightPion; } if (isKaon) { - fPtProfileKa->Fill(trkPt); - nSumEtaLeftKa += 1.0; + fPtProfileKa->Fill(trkPt, effweightKaon); + nSumEtaLeftKa += effweightKaon; } if (isProton && trkPt > cfgCutPtLowerProt) { - fPtProfileProt->Fill(trkPt); - nSumEtaLeftProt += 1.0; + fPtProfileProt->Fill(trkPt, effweightProton); + nSumEtaLeftProt += effweightProton; } } } @@ -923,13 +2170,13 @@ struct V0ptHadPiKaProt { if (track.sign() != 0 && trkPt < cfgCutPtMaxForV02) { if (std::abs(trkEta) < cfgCutEtaWindowB) { if (isPion) { - fPtProfilePiInWinB->Fill(trkPt); + fPtProfilePiInWinB->Fill(trkPt, effweightPion); } if (isKaon) { - fPtProfileKaInWinB->Fill(trkPt); + fPtProfileKaInWinB->Fill(trkPt, effweightKaon); } if (isProton && trkPt > cfgCutPtLowerProt) { - fPtProfileProtInWinB->Fill(trkPt); + fPtProfileProtInWinB->Fill(trkPt, effweightProton); } } } @@ -1003,33 +2250,70 @@ struct V0ptHadPiKaProt { } } - if (nSumInWinA > 4 && nSumInWinB > 4 && nSumInWinC > 4) { + if (nSumInWinA > cfgMinNoOfParticles && nSumInWinB > cfgMinNoOfParticles && nSumInWinC > cfgMinNoOfParticles) { double twoParCorr = (vecQInWinA * TComplex::Conjugate(vecQInWinC)).Re(); twoParCorr *= 1.0 / (nSumInWinA * nSumInWinC); - histos.get(HIST("Prof_XY"))->Fill(cent, 0.5, twoParCorr); + histos.get(HIST("Prof_XY"))->Fill(cent, 0.5, twoParCorr); subSampleV02[sampleIndex][0]->Fill(cent, 0.5, twoParCorr); + if (cfgV02WeightedFill) { + histos.get(HIST("Prof_XY_weighted"))->Fill(cent, 0.5, twoParCorr, (nSumInWinA * nSumInWinC)); + subSampleV02weighted[sampleIndex][0]->Fill(cent, 0.5, twoParCorr, (nSumInWinA * nSumInWinC)); + } + + if (cfgV02WeightedOption3Fill) { + histos.get(HIST("Prof_XY_weightedOption3"))->Fill(cent, 0.5, twoParCorr, (nSumInWinA * nSumInWinC)); + subSampleV02weightedOption3[sampleIndex][0]->Fill(cent, 0.5, twoParCorr, (nSumInWinA * nSumInWinC)); + } // hadrons for (int i = 0; i < cfgNbinsV02pt; i++) { double threeParCorrHad = (vecQInWinA * TComplex::Conjugate(vecQInWinC) * fPtProfileHadInWinB->GetBinContent(i + 1)).Re(); threeParCorrHad *= 1.0 / (nSumInWinA * nSumInWinC * nSumInWinB); + histos.get(HIST("Prof_XYZ_had"))->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), threeParCorrHad); histos.get(HIST("Prof_Z_had"))->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), (fPtProfileHadInWinB->GetBinContent(i + 1) / nSumInWinB)); - subSampleV02[sampleIndex][1]->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), threeParCorrHad); subSampleV02[sampleIndex][2]->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), (fPtProfileHadInWinB->GetBinContent(i + 1) / nSumInWinB)); + + if (cfgV02WeightedFill) { + histos.get(HIST("Prof_XYZ_weighted_had"))->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), threeParCorrHad, (nSumInWinA * nSumInWinC)); + histos.get(HIST("Prof_Z_weighted_had"))->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), (fPtProfileHadInWinB->GetBinContent(i + 1) / nSumInWinB)); + subSampleV02weighted[sampleIndex][1]->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), threeParCorrHad, (nSumInWinA * nSumInWinC)); + subSampleV02weighted[sampleIndex][2]->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), (fPtProfileHadInWinB->GetBinContent(i + 1) / nSumInWinB)); + } + + if (cfgV02WeightedOption3Fill) { + histos.get(HIST("Prof_XYZ_weightedOption3_had"))->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), threeParCorrHad, (nSumInWinA * nSumInWinB * nSumInWinC)); + histos.get(HIST("Prof_Z_weightedOption3_had"))->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), (fPtProfileHadInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + subSampleV02weightedOption3[sampleIndex][1]->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), threeParCorrHad, (nSumInWinA * nSumInWinB * nSumInWinC)); + subSampleV02weightedOption3[sampleIndex][2]->Fill(cent, fPtProfileHadInWinB->GetBinCenter(i + 1), (fPtProfileHadInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + } } // pions for (int i = 0; i < cfgNbinsV02pt; i++) { double threeParCorrPi = (vecQInWinA * TComplex::Conjugate(vecQInWinC) * fPtProfilePiInWinB->GetBinContent(i + 1)).Re(); threeParCorrPi *= 1.0 / (nSumInWinA * nSumInWinC * nSumInWinB); + histos.get(HIST("Prof_XYZ_pi"))->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), threeParCorrPi); histos.get(HIST("Prof_Z_pi"))->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), (fPtProfilePiInWinB->GetBinContent(i + 1) / nSumInWinB)); - subSampleV02[sampleIndex][3]->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), threeParCorrPi); subSampleV02[sampleIndex][4]->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), (fPtProfilePiInWinB->GetBinContent(i + 1) / nSumInWinB)); + + if (cfgV02WeightedFill) { + histos.get(HIST("Prof_XYZ_weighted_pi"))->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), threeParCorrPi, (nSumInWinA * nSumInWinC)); + histos.get(HIST("Prof_Z_weighted_pi"))->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), (fPtProfilePiInWinB->GetBinContent(i + 1) / nSumInWinB)); + subSampleV02weighted[sampleIndex][3]->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), threeParCorrPi, (nSumInWinA * nSumInWinC)); + subSampleV02weighted[sampleIndex][4]->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), (fPtProfilePiInWinB->GetBinContent(i + 1) / nSumInWinB)); + } + + if (cfgV02WeightedOption3Fill) { + histos.get(HIST("Prof_XYZ_weightedOption3_pi"))->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), threeParCorrPi, (nSumInWinA * nSumInWinB * nSumInWinC)); + histos.get(HIST("Prof_Z_weightedOption3_pi"))->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), (fPtProfilePiInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + subSampleV02weightedOption3[sampleIndex][3]->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), threeParCorrPi, (nSumInWinA * nSumInWinB * nSumInWinC)); + subSampleV02weightedOption3[sampleIndex][4]->Fill(cent, fPtProfilePiInWinB->GetBinCenter(i + 1), (fPtProfilePiInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + } } // kaons @@ -1041,6 +2325,20 @@ struct V0ptHadPiKaProt { subSampleV02[sampleIndex][5]->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), threeParCorrKa); subSampleV02[sampleIndex][6]->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), (fPtProfileKaInWinB->GetBinContent(i + 1) / nSumInWinB)); + + if (cfgV02WeightedFill) { + histos.get(HIST("Prof_XYZ_weighted_ka"))->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), threeParCorrKa, (nSumInWinA * nSumInWinC)); + histos.get(HIST("Prof_Z_weighted_ka"))->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), (fPtProfileKaInWinB->GetBinContent(i + 1) / nSumInWinB)); + subSampleV02weighted[sampleIndex][5]->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), threeParCorrKa, (nSumInWinA * nSumInWinC)); + subSampleV02weighted[sampleIndex][6]->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), (fPtProfileKaInWinB->GetBinContent(i + 1) / nSumInWinB)); + } + + if (cfgV02WeightedOption3Fill) { + histos.get(HIST("Prof_XYZ_weightedOption3_ka"))->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), threeParCorrKa, (nSumInWinA * nSumInWinB * nSumInWinC)); + histos.get(HIST("Prof_Z_weightedOption3_ka"))->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), (fPtProfileKaInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + subSampleV02weightedOption3[sampleIndex][5]->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), threeParCorrKa, (nSumInWinA * nSumInWinB * nSumInWinC)); + subSampleV02weightedOption3[sampleIndex][6]->Fill(cent, fPtProfileKaInWinB->GetBinCenter(i + 1), (fPtProfileKaInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + } } // protons @@ -1052,6 +2350,20 @@ struct V0ptHadPiKaProt { subSampleV02[sampleIndex][7]->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), threeParCorrProt); subSampleV02[sampleIndex][8]->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), (fPtProfileProtInWinB->GetBinContent(i + 1) / nSumInWinB)); + + if (cfgV02WeightedFill) { + histos.get(HIST("Prof_XYZ_weighted_prot"))->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), threeParCorrProt, (nSumInWinA * nSumInWinC)); + histos.get(HIST("Prof_Z_weighted_prot"))->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), (fPtProfileProtInWinB->GetBinContent(i + 1) / nSumInWinB)); + subSampleV02weighted[sampleIndex][7]->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), threeParCorrProt, (nSumInWinA * nSumInWinC)); + subSampleV02weighted[sampleIndex][8]->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), (fPtProfileProtInWinB->GetBinContent(i + 1) / nSumInWinB)); + } + + if (cfgV02WeightedOption3Fill) { + histos.get(HIST("Prof_XYZ_weightedOption3_prot"))->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), threeParCorrProt, (nSumInWinA * nSumInWinB * nSumInWinC)); + histos.get(HIST("Prof_Z_weightedOption3_prot"))->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), (fPtProfileProtInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + subSampleV02weightedOption3[sampleIndex][7]->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), threeParCorrProt, (nSumInWinA * nSumInWinB * nSumInWinC)); + subSampleV02weightedOption3[sampleIndex][8]->Fill(cent, fPtProfileProtInWinB->GetBinCenter(i + 1), (fPtProfileProtInWinB->GetBinContent(i + 1) / nSumInWinB), nSumInWinB); + } } } @@ -1066,6 +2378,7 @@ struct V0ptHadPiKaProt { fPtProfileProtInWinB->Delete(); } // End process loop + PROCESS_SWITCH(V0ptHadPiKaProt, processData, "Process Real Data", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 9ecf9edfe6d..a477594bd61 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -30,9 +30,10 @@ #include #include #include +#include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::baseselection { /// \class BaseSelection @@ -54,6 +55,11 @@ class BaseSelection /// \brief Destructor virtual ~BaseSelection() = default; + void init(bool passThrough) + { + mPassThrough = passThrough; + } + /// \brief Add a static-value based selection for a specific observable. /// \param observableIndex Index of the observable. /// \param selectionName Name of the selection. @@ -72,12 +78,17 @@ class BaseSelection { // check index if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } // init selection container for selection at given index - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, selectionValues, limitType, skipMostPermissiveBit, isMinimalCut, isOptionalCut); - - init(observableIndex); + mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, + selectionValues, + limitType, + mPassThrough ? false : skipMostPermissiveBit, + mPassThrough ? false : isMinimalCut, + mPassThrough ? false : isOptionalCut); + + addSelection(observableIndex); } /// \brief Add a function-based selection for a specific observable. @@ -101,36 +112,51 @@ class BaseSelection bool isOptionalCut) { if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, lowerLimit, upperLimit, functions, limitType, skipMostPermissiveBit, isMinimalCut, isOptionalCut); - - init(observableIndex); + mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, + lowerLimit, + upperLimit, + functions, + limitType, + mPassThrough ? false : skipMostPermissiveBit, + mPassThrough ? false : isMinimalCut, + mPassThrough ? false : isOptionalCut); + addSelection(observableIndex); } - /// \brief Add a static-value based selection for a specific observable. + /// \brief Add a range-based selection (either inside or outside the configured ranges) for a + /// specific observable. /// \param observableIndex Index of the observable. /// \param selectionName Name of the selection. - /// \param selectionValues Vector of threshold values. - /// \param limitType Type of limit (from limits::LimitType). + /// \param selectionRanges Vector of "lower;upper" range strings. /// \param skipMostPermissiveBit Whether to skip the loosest threshold when assembling the bitmask. /// \param isMinimalCut Whether this cut is mandatory (must be passed for the candidate to be accepted). /// \param isOptionalCut Whether this cut is optional (candidate is accepted if any optional cut passes). + /// \param limitType Either limits::kInsideRange (value must fall inside) or limits::kOutsideRange + /// (value must fall outside, e.g. rejecting a competing mass hypothesis). A + /// fully-open range entry (";") is exempt for either type -- see SelectionContainer. void addSelection(int observableIndex, std::string const& selectionName, std::vector const& selectionRanges, bool skipMostPermissiveBit, bool isMinimalCut, - bool isOptionalCut) + bool isOptionalCut, + limits::LimitType limitType = limits::kInsideRange) { // check index if (static_cast(observableIndex) >= NumObservables) { - LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; + LOG(fatal) << "Selection '" << selectionName << "': observable is not valid. Observable (index) has to be smaller than " << NumObservables; } // init selection container for selection at given index - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, selectionRanges, skipMostPermissiveBit, isMinimalCut, isOptionalCut); - - init(observableIndex); + mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, + selectionRanges, + mPassThrough ? false : skipMostPermissiveBit, + mPassThrough ? false : isMinimalCut, + mPassThrough ? false : isOptionalCut, + limitType); + + addSelection(observableIndex); } /// \brief Add a boolean-based selection for a specific observable. @@ -140,27 +166,34 @@ class BaseSelection /// -1 = optional cut, bit is stored in bitmask; /// 0 = cut is disabled, no bit stored; /// 1 = minimal (mandatory) cut, no extra bit stored since only one threshold exists. + /// In pass-through mode, enabled selections (-1, 1) are stored as bits without being required, + /// disabled selections (0) stay disabled so they do not occupy bits. void addSelection(int observableIndex, std::string const& selectionName, int mode) { - switch (mode) { + int selectionMode = mode; + + if (mPassThrough && mode != 0) { + selectionMode = 2; + } + switch (selectionMode) { case -1: // cut is optional and we store a bit for it - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, false, false, true); + mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, false, false, true); break; case 0: // cut is disabled; initialize with empty vector so evaluation bails out early - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, std::vector{}, limits::LimitType::kEqual, false, false, false); + mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, std::vector{}, limits::LimitType::kEqual, false, false, false); break; case 1: // mandatory cut; only one threshold so the most permissive bit is skipped and no extra bit is stored - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, true, true, false); + mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, true, true, false); break; case 2: // pass through mode; cut is neither minimal nor optional and we store all bits - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, false, false, false); + mSelectionContainers.at(observableIndex) = selectioncontainer::SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, false, false, false); break; default: - LOG(fatal) << "Invalid switch for boolean selection"; + LOG(fatal) << "Selection '" << selectionName << "': invalid switch for boolean selection"; } - init(observableIndex); + addSelection(observableIndex); } /// \brief Update the limits of a function-based selection for a specific observable. @@ -220,10 +253,46 @@ class BaseSelection } } + /// \brief Evaluate the same observable for two independently-computed values of the same + /// candidate and store only the logical AND of the two resulting per-threshold bitmasks + /// \param observableIndex Index of the observable. + /// \param value1 First value to evaluate. + /// \param value2 Second value to evaluate. + void evaluateObservable(int observableIndex, T value1, T value2) + { + // if there are no selections configured, bail out + if (mSelectionContainers.at(observableIndex).isEmpty()) { + return; + } + // if a previous observable already failed a minimal selection, + // there is no point in evaluating further observables + if (!mPassesMinimalSelections) { + return; + } + auto& container = mSelectionContainers.at(observableIndex); + container.evaluate(value1); + auto const bitmask1 = container.getRawBitmask(); + container.evaluate(value2); + auto const bitmask2 = container.getRawBitmask(); + container.setBitmask((bitmask1 & bitmask2).to_ullong()); + // if any minimal selection is not fulfilled, the candidate is rejected + if (mHasMinimalSelection) { + if (!container.passesAsMinimalCut()) { + mPassesMinimalSelections = false; + } + } + // if any optional selection is fulfilled, the candidate is accepted + if (mHasOptionalSelection) { + if (container.passesAsOptionalCut()) { + mPassesOptionalSelections = true; + } + } + } + /// \brief Evaluate a single observable against its configured selections. /// \param observableIndex Index of the observable. /// \param values Vector of values of the observable. - void evaluateObservable(int observableIndex, std::vector values) + void evaluateObservable(int observableIndex, std::vector const& values) { // if there are no selections configured, bail out if (mSelectionContainers.at(observableIndex).isEmpty()) { @@ -255,10 +324,20 @@ class BaseSelection /// \param comments Vector of comment strings, one per selection threshold. void addComments(int observableIndex, std::vector const& comments) { mSelectionContainers.at(observableIndex).addComments(comments); } + /// \brief Attach the same comment to every selection threshold of a specific observable. + /// \param observableIndex Index of the observable. + /// \param comment Comment string applied to all thresholds. + void addComments(int observableIndex, std::string const& comment) { mSelectionContainers.at(observableIndex).addComments(comment); } + + [[nodiscard]] bool isPassThrough() const { return mPassThrough; } + /// \brief Check whether all required and optional cuts are passed. /// \return True if all minimal cuts pass and, if optional cuts are present, at least one of them passes. - bool passesAllRequiredSelections() const + [[nodiscard]] bool passesAllRequiredSelections() const { + if (mPassThrough) { + return true; + } if (mHasMinimalSelection && !mHasOptionalSelection) { return mPassesMinimalSelections; } @@ -275,14 +354,14 @@ class BaseSelection /// \brief Check whether the optional selection for a specific observable is passed. /// \param observableIndex Index of the observable. /// \return True if at least one optional selection for this observable is fulfilled. - bool passesOptionalSelection(int observableIndex) const + [[nodiscard]] bool passesOptionalSelection(int observableIndex) const { return mSelectionContainers.at(observableIndex).passesAsOptionalCut(); } /// \brief Assemble the global selection bitmask from all individual observable selections. /// \tparam HistName Name of the histogram used to track selection statistics. - template + template void assembleBitmask() { mHistRegistry->fill(HIST(HistName), mNSelection); @@ -344,7 +423,7 @@ class BaseSelection /// \brief Print the full configuration of all selections to the log. /// \param objectName Name of the object owning this selection (used as label in the log output). - void printSelections(const std::string& objectName) const + void printSelections(const char* objectName) const { LOG(info) << "Printing Configuration of " << objectName; for (size_t idx = 0; idx < mSelectionContainers.size(); ++idx) { @@ -363,7 +442,6 @@ class BaseSelection LOG(info) << " Selections:"; for (std::size_t j = 0; j < container.getNSelections(); ++j) { - std::stringstream line; std::string sel = container.getValueAsString(j); std::string comment = container.getComment(j); @@ -391,10 +469,13 @@ class BaseSelection /// \brief Initialize histograms and set bitmask offsets for all configured observables. /// \tparam HistName Name of the histogram to create in the registry. /// \param registry Pointer to the histogram registry. - template - void setupContainers(o2::framework::HistogramRegistry* registry) + template + void setupSelectionHistogram(o2::framework::HistogramRegistry* registry) { - mHistRegistry = registry; + if (!mHistRegistry) { + mHistRegistry = registry; + } + // create histogram with one bin per selection, plus two summary bins (all analyzed, all passed) int nBins = mNSelection + 2; mHistRegistry->add(HistName, "; Selection Bits; Entries", o2::framework::HistType::kTH1F, {{nBins, -0.5, nBins - 0.5}}); @@ -418,8 +499,64 @@ class BaseSelection mHistRegistry->get(HIST(HistName))->GetXaxis()->SetBinLabel(mNSelection + 2, "All passed"); } + /// \brief Setup a histogram tracking how many candidates pass each individual filter bound. + /// \tparam FilterHistName Name of the histogram (must be unique in registry). + /// \param registry Pointer to the histogram registry. + /// \param filters Ordered list of (name, value) pairs, one per filter bound. The position in + /// this vector must match the caller's filter enum values (e.g. filters[kPtMin] + /// describes the kPtMin bound), since that's what fillFilter() indexes into. + template + void setupFilterHistogram(o2::framework::HistogramRegistry* registry, + std::vector> const& filters) + { + if (!mHistRegistry) { + mHistRegistry = registry; + } + + mNFilters = filters.size(); + // two extra bins: "All analyzed" and "All passed", mirroring setupContainers() + registry->add(FilterHistName, "; Filter; Entries", o2::framework::HistType::kTH1F, + {{static_cast(mNFilters) + 2, -0.5, static_cast(mNFilters) + 1.5}}); + + auto* axis = registry->get(HIST(FilterHistName))->GetXaxis(); + for (std::size_t i = 0; i < mNFilters; ++i) { + std::ostringstream label; + label << "FilterName" << selectioncontainer::ValueDelimiter << filters[i].first << selectioncontainer::SectionDelimiter + << "FilterValue" << selectioncontainer::ValueDelimiter << filters[i].second; + axis->SetBinLabel(static_cast(i) + 1, label.str().c_str()); + } + axis->SetBinLabel(static_cast(mNFilters) + 1, "All analyzed"); + axis->SetBinLabel(static_cast(mNFilters) + 2, "All passed"); + } + + /// \brief Fill one bin of the filter histogram if the corresponding filter bound was passed. + /// \tparam FilterHistName Name of the histogram (same one passed to setupFilterHistogram). + /// \param filterIndex Index of the filter bound (an enum value from the caller's filter enum). + /// \param passed Whether the candidate passed this specific filter bound. + template + void fillFilter(int filterIndex, bool passed) const + { + if (passed) { + mHistRegistry->fill(HIST(FilterHistName), filterIndex); + } + } + + /// \brief Fill the two summary bins of the filter histogram: called once per candidate, + /// after all individual filter bounds have been checked. + /// \tparam FilterHistName Name of the histogram (same one passed to setupFilterHistogram). + /// \param passedAll Whether the candidate passed every configured filter bound. + template + void fillFilterSummary(bool passedAll) const + + { + mHistRegistry->fill(HIST(FilterHistName), static_cast(mNFilters)); + if (passedAll || mPassThrough) { + mHistRegistry->fill(HIST(FilterHistName), static_cast(mNFilters) + 1); + } + } + protected: - void init(int observableIndex) + void addSelection(int observableIndex) { // check if any selections are configured if (mSelectionContainers.at(observableIndex).isEmpty()) { @@ -440,20 +577,22 @@ class BaseSelection } if (mNSelectionBits > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " number of bits are supported"; + LOG(fatal) << "Selection '" << mSelectionContainers.at(observableIndex).getSelectionName() << "': too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " number of bits are supported"; } } o2::framework::HistogramRegistry* mHistRegistry = nullptr; - std::array, NumObservables> mSelectionContainers = {}; ///< Array of selection containers, one per observable - std::bitset mFinalBitmask = {}; ///< Assembled bitmask combining all observable selections - std::size_t mNSelectionBits = 0; ///< Number of bits occupied in the bitmask (excludes skipped most-permissive bits) - std::size_t mNSelection = 0; ///< Total number of configured selection thresholds across all observables - bool mHasMinimalSelection = false; ///< True if at least one observable has a mandatory (minimal) cut configured - bool mPassesMinimalSelections = true; ///< True if all mandatory (minimal) cuts have been passed so far - bool mHasOptionalSelection = false; ///< True if at least one observable has an optional cut configured - bool mPassesOptionalSelections = false; ///< True if at least one optional cut has been passed + std::array, NumObservables> mSelectionContainers = {}; ///< Array of selection containers, one per observable + std::bitset mFinalBitmask = {}; ///< Assembled bitmask combining all observable selections + std::size_t mNSelectionBits = 0; ///< Number of bits occupied in the bitmask (excludes skipped most-permissive bits) + std::size_t mNSelection = 0; ///< Total number of configured selection thresholds across all observables + std::size_t mNFilters = 0; ///< Total number of configured (pre)filter + bool mHasMinimalSelection = false; ///< True if at least one observable has a mandatory (minimal) cut configured + bool mPassesMinimalSelections = true; ///< True if all mandatory (minimal) cuts have been passed so far + bool mHasOptionalSelection = false; ///< True if at least one observable has an optional cut configured + bool mPassesOptionalSelections = false; ///< True if at least one optional cut has been passed + bool mPassThrough = false; }; -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::baseselection #endif // PWGCF_FEMTO_CORE_BASESELECTION_H_ diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index 2de7099ee6f..3dda17bf3a5 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -33,15 +33,13 @@ #include #include #include +#include #include #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::cascadebuilder { -namespace cascadebuilder -{ - struct ConfCascadeFilters : o2::framework::ConfigurableGroup { std::string prefix = std::string("CascadeFilters"); o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; @@ -52,56 +50,64 @@ struct ConfCascadeFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; o2::framework::Configurable massXiMin{"massXiMin", 1.2f, "Minimum Xi mass"}; o2::framework::Configurable massXiMax{"massXiMax", 1.4f, "Maximum Xi mass"}; - o2::framework::Configurable rejectMassXiMin{"rejectMassXiMin", 1.317f, "Reject Minimum Xi mass for Omega hypothesis"}; - o2::framework::Configurable rejectMassXiMax{"rejectMassXiMax", 1.325f, "Rejection Maximum Xi mass for Omega hypothesis"}; + o2::framework::Configurable massOmegaMin{"massOmegaMin", 1.5f, "Minimum Omega mass"}; o2::framework::Configurable massOmegaMax{"massOmegaMax", 1.9f, "Maximum Omega mass"}; - o2::framework::Configurable rejectMassOmegaMin{"rejectMassOmegaMin", 1.668f, "Reject minimum Omega mass for Xi hypothesis"}; - o2::framework::Configurable rejectMassOmegaMax{"rejectMassOmegaMax", 1.676f, "Reject maximum Omega mass for Xi hypothesis"}; - o2::framework::Configurable massLambdaMin{"massLambdaMin", 1.0f, "Minimum Lambda mass"}; - o2::framework::Configurable massLambdaMax{"massLambdaMax", 1.2f, "Maximum Lambda mass"}; }; -#define CASCADE_DEFAULT_BITS \ - o2::framework::Configurable> cascadeCpaMin{"cascadeCpaMin", {0.95f}, "Minimum cosine of pointing angle"}; \ - o2::framework::Configurable> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ - o2::framework::Configurable> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \ - o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ - o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Minimum DCA of the daughters from primary vertex (cm)"}; \ - o2::framework::Configurable> dauDcaMin{"dauDcaMin", {0.05f}, "Minimum DCA of the daughters from primary vertex (cm)"}; \ - o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ - o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for positive daughter tracks"}; \ - o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsimga_Pion/Proton| TPC for negative daughter tracks"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CASCADE_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all Cascades are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> cascadeCpaMin{"cascadeCpaMin", {"0.95"}, "Minimum CPA as TFormula, x=pt"}; \ + o2::framework::Configurable> cascadePaMax{"cascadePaMax", {}, "Maximum pointing angle (rad) as TFormula, x=pt. Empty to disable"}; \ + o2::framework::Configurable> cascadeTransRadMin{"cascadeTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> cascadeDcaDauMax{"cascadeDcaDauMax", {0.25f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaCpaMin{"lambdaCpaMin", {0.78f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> lambdaTransRadMin{"lambdaTransRadMin", {0.9f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> lambdaDcaDauMax{"lambdaDcaDauMax", {0.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ + o2::framework::Configurable> lambdaDcaToPvMin{"lambdaDcaToPvMin", {0.3f}, "Minimum DCA between the lambda and primary vertex"}; \ + o2::framework::Configurable> lambdaMassRanges{"lambdaMassRanges", {"1.0;1.2"}, "Lambda daughter mass ranges (lower;upper) to accept -- bit is set if the cascade's mLambda falls inside the range"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| of all daughters"}; \ + o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum |DCAxy| of the daughters and bachelor from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ + o2::framework::Configurable> posDauTpc{"posDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTpc{"negDauTpc", {5.f}, "Maximum |nsigma_Pion/Proton| TPC for negative daughter tracks"}; \ + o2::framework::Configurable> posDauTof{"posDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for positive daughter tracks"}; \ + o2::framework::Configurable> negDauTof{"negDauTof", {}, "Maximum |nsigma_Pion/Proton| TOF for negative daughter tracks"}; \ + o2::framework::Configurable requireTof{"requireTof", false, "If true, the TOF PID cut is mandatory. For daughters with a TOF signal, the candidate is rejected on failure. For daughters without a TOF signal, the candidate is rejected unless keepTracksWithoutTof is true"}; \ + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, daughters without a TOF signal pass the TOF PID cut unconditionally, overriding requireTof. If false, daughters without a TOF signal fail the TOF cut (and are rejected if requireTof is true)"}; struct ConfXiBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("XiBits"); CASCADE_DEFAULT_BITS - o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTpcPion{"bachelorTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTofPion{"bachelorTofPion", {}, "Maximum |nsigma_Pion| TOF for bachelor tracks"}; + o2::framework::Configurable> rejectOmegaMassRanges{"rejectOmegaMassRanges", {"1.668;1.676"}, "Omega mass ranges (lower;upper) to reject -- bit is set if the candidate's mOmega falls outside the range"}; }; struct ConfOmegaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("OmegaBits"); CASCADE_DEFAULT_BITS - o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsimga_Kaon| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTpcKaon{"bachelorTpcKaon", {5.f}, "Maximum |nsigma_Kaon| TPC for bachelor tracks"}; + o2::framework::Configurable> bachelorTofKaon{"bachelorTofKaon", {}, "Maximum |nsigma_Kaon| TOF for bachelor tracks"}; + o2::framework::Configurable> rejectXiMassRanges{"rejectXiMassRanges", {"1.317;1.325"}, "Xi mass ranges (lower;upper) to reject -- bit is set if the candidate's mXi falls outside the range"}; }; #undef CASCADE_DEFAULT_BITS -#define CASCADE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCode) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", defaultPdgCode, "Cascade PDG code. Set sign to +1 to select antiparticle"}; \ - o2::framework::Configurable sign{"sign", -1, "Sign of the charge of the Cascade"}; \ - o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ - o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ - o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ - o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ - o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum eta"}; \ - o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", defaultMassMin, "Minimum invariant mass for Cascade"}; \ - o2::framework::Configurable massMax{"massMax", defaultMassMax, "Maximum invariant mass for Cascade"}; \ - o2::framework::Configurable mask{"mask", 0x0, "Bitmask for cascade selection"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CASCADE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCode) \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "Cascade PDG code. Set sign to +1 to select antiparticle"}; \ + o2::framework::Configurable sign{"sign", -1, "Sign of the charge of the Cascade"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Cascade"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Cascade"}; \ + o2::framework::Configurable mask{"mask", 0x0, "Bitmask for cascade selection"}; struct ConfXiSelection : o2::framework::ConfigurableGroup { std::string prefix = std::string("XiSelection"); @@ -113,31 +119,43 @@ struct ConfOmegaSelection : o2::framework::ConfigurableGroup { CASCADE_DEFAULT_SELECTION(1.57, 1.77, 3334) }; +#undef CASCADE_DEFAULT_SELECTION + /// The different selections this task is capable of doing enum CascadeSels { // selections for cascades kCascadeCpaMin, ///< Min. CPA (cosine pointing angle) - kCascadeDcaDaughMax, ///< Max. DCA of the daughers at decay vertex + kCascadePaMax, ///< Max. PA (pointing angle in rad); disabled by default + kCascadeDcaDaughMax, ///< Max. DCA of the daughters at decay vertex kCascadeTransRadMin, ///< max. transverse radius // selection for lambda daughter - kLambdaCpaMin, ///< Min. DCA of the lambda daughers at primary vertex - kLambdaDcaDauMax, ///< TPC PID for daughters (Pion/Proton) - kLambdaTransRadMin, ///< Min. number of TPC clusters of daughter - kLambdaDcaToPvMin, ///< Min. DCA to primary vertex of daughter lambda + kLambdaCpaMin, ///< Min. CPA of the lambda + kLambdaDcaDauMax, ///< Max. DCA between the lambda daughters at lambda decay vertex + kLambdaTransRadMin, ///< Min. transverse radius of the lambda + kLambdaDcaToPvMin, ///< Min. DCA of the lambda to the primary vertex + kLambdaMassRange, ///< Lambda daughter mass must fall inside the configured range(s) - // selection for bachelor/daugthers - kDauAbsEtaMax, ///< Min. DCA of the daughers/bachelor at primary vertex - kDauTpcClsMin, ///< Min. number of TPC clusters of daughters/bachelor - kDauDcaMin, ///< TPC Pion PID for negative daughter + // rejection of competing mass hypotheses (range-based: bit is set if the candidate is outside the range) + kRejectOmegaMass, ///< Xi: reject Omega mass hypothesis + kRejectXiMass, ///< Omega: reject Xi mass hypothesis + + // selection for bachelor/daughters + kDauAbsEtaMax, ///< Max. |eta| of daughter tracks + kDauTpcClsMin, ///< Min. number of TPC clusters of daughters/bachelor + kDauAbsDcaxyMin, ///< Min. |DCAxy| of the daughters and bachelor from primary vertex // PID selection for cascade bachelor kBachelorTpcPion, ///< TPC Pion PID for bachelor kBachelorTpcKaon, ///< TPC Kaon PID for bachelor + kBachelorTofPion, ///< TOF Pion PID for bachelor + kBachelorTofKaon, ///< TOF Kaon PID for bachelor /// - // PID selection for lambda daughers + // PID selection for lambda daughters kPosDauTpc, ///< TPC PID for positive daughter kNegDauTpc, ///< TPC PID for negative daughter + kPosDauTof, ///< TOF PID for positive daughter + kNegDauTof, ///< TOF PID for negative daughter kCascadeSelsMax }; @@ -147,6 +165,7 @@ constexpr char OmegaSelHistName[] = "hOmegaSelection"; constexpr char CascadeSelsName[] = "Cascade Selection Object"; const std::unordered_map cascadeSelectionNames = { {kCascadeCpaMin, "Cascade CPA Min"}, + {kCascadePaMax, "Cascade PA Max"}, {kCascadeDcaDaughMax, "Cascade DCA Daughters Max"}, {kCascadeTransRadMin, "Cascade Transverse Radius Min"}, @@ -154,70 +173,128 @@ const std::unordered_map cascadeSelectionNames = { {kLambdaDcaDauMax, "Lambda DCA Daughter Max"}, {kLambdaTransRadMin, "Lambda Transverse Radius Min"}, {kLambdaDcaToPvMin, "Lambda DCA to PV Min"}, + {kLambdaMassRange, "Lambda Daughter Mass Range"}, + + {kRejectOmegaMass, "Reject Omega mass hypothesis"}, + {kRejectXiMass, "Reject Xi mass hypothesis"}, {kDauAbsEtaMax, "Daughter Abs Eta Max"}, {kDauTpcClsMin, "Daughter TPC Clusters Min"}, - {kDauDcaMin, "Daughter DCA Min"}, + {kDauAbsDcaxyMin, "Daughter |DCAxy| Min"}, {kBachelorTpcPion, "Bachelor TPC Pion PID"}, {kBachelorTpcKaon, "Bachelor TPC Kaon PID"}, - + {kBachelorTofPion, "Bachelor TOF Pion PID"}, + {kBachelorTofKaon, "Bachelor TOF Kaon PID"}, {kPosDauTpc, "Positive Daughter TPC PID"}, {kNegDauTpc, "Negative Daughter TPC PID"}, + {kPosDauTof, "Positive Daughter TOF PID"}, + {kNegDauTof, "Negative Daughter TOF PID"}}; + +/// enum for all cascade pre-filters (evaluated in checkFilters, before the selection bitmask) +enum CascadeFilters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kMassMin, ///< signal mass window lower bound, for this cascadeType's own mass hypothesis + kMassMax, ///< signal mass window upper bound, for this cascadeType's own mass hypothesis + kCascadeFiltersMax +}; - {kCascadeSelsMax, "Cascade Selections Max"}}; +constexpr char XiFilterHistName[] = "hXiFilters"; +constexpr char OmegaFilterHistName[] = "hOmegaFilters"; +const std::unordered_map cascadeFilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}, + {kMassMin, "massMin"}, + {kMassMax, "massMax"}, +}; /// \class FemtoDreamTrackCuts /// \brief Cut class to contain and execute all cuts applied to tracks -template -class CascadeSelection : public BaseSelection +template +class CascadeSelection : public baseselection::BaseSelection { public: CascadeSelection() = default; - ~CascadeSelection() = default; + ~CascadeSelection() override = default; template void configure(o2::framework::HistogramRegistry* registry, T1 const& config, T2 const& filter) { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mRequireTof = config.requireTof.value; + mKeepTracksWithoutTof = config.keepTracksWithoutTof.value; + const std::string tofComment = "requireTof = " + std::to_string(mRequireTof) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof); + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { mXiMassLowerLimit = filter.massXiMin.value; mXiMassUpperLimit = filter.massXiMax.value; - mOmegaMassLowerLimit = filter.rejectMassOmegaMin.value; - mOmegaMassUpperLimit = filter.rejectMassOmegaMax.value; + this->addSelection(kRejectOmegaMass, cascadeSelectionNames.at(kRejectOmegaMass), config.rejectOmegaMassRanges.value, true, true, false, limits::kOutsideRange); this->addSelection(kBachelorTpcPion, cascadeSelectionNames.at(kBachelorTpcPion), config.bachelorTpcPion.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kBachelorTofPion, cascadeSelectionNames.at(kBachelorTofPion), config.bachelorTofPion.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false); + this->addComments(kBachelorTofPion, tofComment); } if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { mOmegaMassLowerLimit = filter.massOmegaMin.value; mOmegaMassUpperLimit = filter.massOmegaMax.value; - mXiMassLowerLimit = filter.rejectMassXiMin.value; - mXiMassUpperLimit = filter.rejectMassXiMax.value; + this->addSelection(kRejectXiMass, cascadeSelectionNames.at(kRejectXiMass), config.rejectXiMassRanges.value, true, true, false, limits::kOutsideRange); this->addSelection(kBachelorTpcKaon, cascadeSelectionNames.at(kBachelorTpcKaon), config.bachelorTpcKaon.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kBachelorTofKaon, cascadeSelectionNames.at(kBachelorTofKaon), config.bachelorTofKaon.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false); + this->addComments(kBachelorTofKaon, tofComment); } - mPtMin = filter.ptMin.value; - mPtMax = filter.ptMax.value; - mEtaMin = filter.etaMin.value; - mEtaMax = filter.etaMax.value; - mPhiMin = filter.phiMin.value; - mPhiMax = filter.phiMax.value; - mLambdaMassMin = filter.massLambdaMin.value; - mLambdaMassMax = filter.massLambdaMax.value; - this->addSelection(kPosDauTpc, cascadeSelectionNames.at(kPosDauTpc), config.posDauTpc.value, limits::kAbsUpperLimit, true, true, false); this->addSelection(kNegDauTpc, cascadeSelectionNames.at(kNegDauTpc), config.negDauTpc.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPosDauTof, cascadeSelectionNames.at(kPosDauTof), config.posDauTof.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false); + this->addSelection(kNegDauTof, cascadeSelectionNames.at(kNegDauTof), config.negDauTof.value, limits::kAbsUpperLimit, mRequireTof, mRequireTof, false); + this->addComments(kPosDauTof, tofComment); + this->addComments(kNegDauTof, tofComment); - this->addSelection(kCascadeCpaMin, cascadeSelectionNames.at(kCascadeCpaMin), config.cascadeCpaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kCascadeCpaMin, cascadeSelectionNames.at(kCascadeCpaMin), filter.ptMin.value, filter.ptMax.value, config.cascadeCpaMin.value, limits::kLowerFunctionLimit, true, true, false); + this->addSelection(kCascadePaMax, cascadeSelectionNames.at(kCascadePaMax), filter.ptMin.value, filter.ptMax.value, config.cascadePaMax.value, limits::kUpperFunctionLimit, true, true, false); this->addSelection(kCascadeTransRadMin, cascadeSelectionNames.at(kCascadeTransRadMin), config.cascadeTransRadMin.value, limits::kLowerLimit, true, true, false); - this->addSelection(kCascadeDcaDaughMax, cascadeSelectionNames.at(kCascadeDcaDaughMax), config.cascadeDcaDauMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kCascadeDcaDaughMax, cascadeSelectionNames.at(kCascadeDcaDaughMax), config.cascadeDcaDauMax.value, limits::kUpperLimit, true, true, false); this->addSelection(kLambdaCpaMin, cascadeSelectionNames.at(kLambdaCpaMin), config.lambdaCpaMin.value, limits::kLowerLimit, true, true, false); this->addSelection(kLambdaTransRadMin, cascadeSelectionNames.at(kLambdaTransRadMin), config.lambdaTransRadMin.value, limits::kLowerLimit, true, true, false); - this->addSelection(kLambdaDcaDauMax, cascadeSelectionNames.at(kLambdaDcaDauMax), config.lambdaDcaDauMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kLambdaDcaDauMax, cascadeSelectionNames.at(kLambdaDcaDauMax), config.lambdaDcaDauMax.value, limits::kUpperLimit, true, true, false); this->addSelection(kLambdaDcaToPvMin, cascadeSelectionNames.at(kLambdaDcaToPvMin), config.lambdaDcaToPvMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kLambdaMassRange, cascadeSelectionNames.at(kLambdaMassRange), config.lambdaMassRanges.value, true, true, false, limits::kInsideRange); this->addSelection(kDauAbsEtaMax, cascadeSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); - this->addSelection(kDauDcaMin, cascadeSelectionNames.at(kDauDcaMin), config.dauDcaMin.value, limits::kAbsLowerLimit, true, true, false); + this->addSelection(kDauAbsDcaxyMin, cascadeSelectionNames.at(kDauAbsDcaxyMin), config.dauAbsDcaxyMin.value, limits::kAbsLowerLimit, true, true, false); this->addSelection(kDauTpcClsMin, cascadeSelectionNames.at(kDauTpcClsMin), config.dauTpcClustersMin.value, limits::kLowerLimit, true, true, false); - this->setupContainers(registry); + this->setupSelectionHistogram(registry); + // signal mass window bins carry this cascadeType's own mass hypothesis only -- otherwise e.g. + // the Xi filter histogram would also show Omega's (irrelevant, default-valued) mass window + float const massLowerLimit = modes::isEqual(cascadeType, modes::Cascade::kOmega) ? mOmegaMassLowerLimit : mXiMassLowerLimit; + float const massUpperLimit = modes::isEqual(cascadeType, modes::Cascade::kOmega) ? mOmegaMassUpperLimit : mXiMassUpperLimit; + this->template setupFilterHistogram( + registry, + { + {cascadeFilterNames.at(kPtMin), mPtMin}, + {cascadeFilterNames.at(kPtMax), mPtMax}, + {cascadeFilterNames.at(kEtaMin), mEtaMin}, + {cascadeFilterNames.at(kEtaMax), mEtaMax}, + {cascadeFilterNames.at(kPhiMin), mPhiMin}, + {cascadeFilterNames.at(kPhiMax), mPhiMax}, + {cascadeFilterNames.at(kMassMin), massLowerLimit}, + {cascadeFilterNames.at(kMassMax), massUpperLimit}, + }); }; template @@ -225,7 +302,11 @@ class CascadeSelection : public BaseSelectionreset(); // cascade selections - this->evaluateObservable(kCascadeCpaMin, cascade.casccosPA(col.posX(), col.posY(), col.posZ())); + const float cpa = cascade.casccosPA(col.posX(), col.posY(), col.posZ()); + this->updateLimits(kCascadeCpaMin, cascade.pt()); + this->evaluateObservable(kCascadeCpaMin, cpa); + this->updateLimits(kCascadePaMax, cascade.pt()); + this->evaluateObservable(kCascadePaMax, std::acos(cpa)); this->evaluateObservable(kCascadeDcaDaughMax, cascade.dcacascdaughters()); this->evaluateObservable(kCascadeTransRadMin, cascade.cascradius()); @@ -234,86 +315,156 @@ class CascadeSelection : public BaseSelectionevaluateObservable(kLambdaDcaDauMax, cascade.dcaV0daughters()); this->evaluateObservable(kLambdaTransRadMin, cascade.v0radius()); this->evaluateObservable(kLambdaDcaToPvMin, cascade.dcav0topv(col.posX(), col.posY(), col.posZ())); + this->evaluateObservable(kLambdaMassRange, cascade.mLambda()); auto bachelor = cascade.template bachelor_as(); auto posDaughter = cascade.template posTrack_as(); auto negDaughter = cascade.template negTrack_as(); // daughter selections - std::array etaDaughters = {std::fabs(bachelor.eta()), std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + std::array etaDaughters = {std::fabs(cascade.bacheloreta()), std::fabs(cascade.positiveeta()), std::fabs(cascade.negativeeta())}; this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaDaughters.begin(), etaDaughters.end())); - std::array dcaDaughters = {std::hypot(bachelor.dcaXY(), bachelor.dcaZ()), - std::hypot(posDaughter.dcaXY(), posDaughter.dcaZ()), - std::hypot(negDaughter.dcaXY(), negDaughter.dcaZ())}; - this->evaluateObservable(kDauDcaMin, *std::min_element(dcaDaughters.begin(), dcaDaughters.end())); + std::array dcaDaughters = {std::fabs(cascade.dcabachtopv()), std::fabs(cascade.dcapostopv()), std::fabs(cascade.dcanegtopv())}; + this->evaluateObservable(kDauAbsDcaxyMin, *std::min_element(dcaDaughters.begin(), dcaDaughters.end())); std::array clustersDaughters = {1.f * bachelor.tpcNClsFound(), 1.f * posDaughter.tpcNClsFound(), 1.f * negDaughter.tpcNClsFound()}; this->evaluateObservable(kDauTpcClsMin, *std::min_element(clustersDaughters.begin(), clustersDaughters.end())); - // bachelor pid selection - // check both pion and kaon PID for xi and omega - this->evaluateObservable(kBachelorTpcPion, bachelor.tpcNSigmaPi()); - this->evaluateObservable(kBachelorTpcKaon, bachelor.tpcNSigmaKa()); - - // depending on the charge, we check lambda or antilambda hypothesis - if (cascade.sign() < 0) { - this->evaluateObservable(kPosDauTpc, posDaughter.tpcNSigmaPr()); - this->evaluateObservable(kNegDauTpc, negDaughter.tpcNSigmaPi()); - } else if (cascade.sign() > 0) { - this->evaluateObservable(kPosDauTpc, posDaughter.tpcNSigmaPi()); - this->evaluateObservable(kNegDauTpc, negDaughter.tpcNSigmaPr()); - } else { + // pid selections + // TPC nSigma comes from the daughter track, TOF nSigma and the has-TOF flags from the cascade candidate + // if a daughter has no TOF signal: feed 0 so the bit passes any limit if keepTracksWithoutTof is set, + // overriding requireTof; otherwise feed a value that fails every limit, so that if requireTof is also + // true, the mandatory TOF cut correctly rejects the candidate instead of silently letting it through + auto evaluatePid = [this](CascadeSels tpcBit, float tpcNSigma, + CascadeSels tofBit, float tofNSigma, bool hasTof) { + this->evaluateObservable(tpcBit, tpcNSigma); + if (hasTof) { + this->evaluateObservable(tofBit, tofNSigma); + } else if (mKeepTracksWithoutTof) { + this->evaluateObservable(tofBit, 0.f); + } else { + this->evaluateObservable(tofBit, std::numeric_limits::max()); + } + }; + + const bool bachHasTof = cascade.bachelorHasTOF(); + const bool posHasTof = cascade.positiveHasTOF(); + const bool negHasTof = cascade.negativeHasTOF(); + + // bachelor: pion for Xi, kaon for Omega + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + evaluatePid(kBachelorTpcPion, bachelor.tpcNSigmaPi(), + kBachelorTofPion, cascade.tofNSigmaXiPi(), bachHasTof); + } else if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + evaluatePid(kBachelorTpcKaon, bachelor.tpcNSigmaKa(), + kBachelorTofKaon, cascade.tofNSigmaOmKa(), bachHasTof); + } + + // v0 daughters: charge of the cascade fixes the Lambda vs. AntiLambda hypothesis + if (cascade.sign() == 0) { LOG(warn) << "Encountered Cascade candidate with 0 charge"; + } else { + // sign < 0: Xi-/Omega- -> Lambda -> p pi- (pos = proton, neg = pion) + // sign > 0: Xi+/Omega+ -> AntiLambda (pos = pion, neg = antiproton) + const bool isMatter = cascade.sign() < 0; + + const float tpcPosDau = isMatter ? posDaughter.tpcNSigmaPr() : posDaughter.tpcNSigmaPi(); + const float tpcNegDau = isMatter ? negDaughter.tpcNSigmaPi() : negDaughter.tpcNSigmaPr(); + + float tofPosDau = 0.f; + float tofNegDau = 0.f; + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + tofPosDau = isMatter ? cascade.tofNSigmaXiLaPr() : cascade.tofNSigmaXiLaPi(); + tofNegDau = isMatter ? cascade.tofNSigmaXiLaPi() : cascade.tofNSigmaXiLaPr(); + } else if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + tofPosDau = isMatter ? cascade.tofNSigmaOmLaPr() : cascade.tofNSigmaOmLaPi(); + tofNegDau = isMatter ? cascade.tofNSigmaOmLaPi() : cascade.tofNSigmaOmLaPr(); + } + + evaluatePid(kPosDauTpc, tpcPosDau, kPosDauTof, tofPosDau, posHasTof); + evaluatePid(kNegDauTpc, tpcNegDau, kNegDauTof, tofNegDau, negHasTof); } - this->assembleBitmask(); + // rejection of competing mass hypotheses: bit is set if the candidate falls outside the + // configured range(s) -- see kOutsideRange in selectionContainer.h + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + this->evaluateObservable(kRejectOmegaMass, cascade.mOmega()); + } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + this->evaluateObservable(kRejectXiMass, cascade.mXi()); + } + + this->assembleBitmask(); }; template bool checkFilters(const T& cascade) const { - // check kinematics - const bool kinematicsOK = - (cascade.pt() > mPtMin && cascade.pt() < mPtMax) && - (cascade.eta() > mEtaMin && cascade.eta() < mEtaMax) && - (cascade.phi() > mPhiMin && cascade.phi() < mPhiMax); + bool pass = true; + bool p = false; + + // kinematics + p = cascade.pt() > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = cascade.pt() < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = cascade.eta() > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = cascade.eta() < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = cascade.phi() > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = cascade.phi() < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + // signal mass window: gating AND-cut (both bounds must pass). The competing hypothesis is no + // longer rejected here -- it's a stored bit instead (see kRejectOmegaMass/kRejectXiMass in + // applySelections()), so it doesn't gate `pass` or fill a filter bin. The Lambda daughter's + // own mass is likewise no longer a hard filter -- see kLambdaMassRange in applySelections(). + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + p = cascade.mXi() > mXiMassLowerLimit; + this->template fillFilter(kMassMin, p); + pass &= p; - if (!kinematicsOK) { - return false; + p = cascade.mXi() < mXiMassUpperLimit; + this->template fillFilter(kMassMax, p); + pass &= p; } - // check mass of daughter lambda - const bool lambdaOK = - (cascade.mLambda() > mLambdaMassMin && cascade.mLambda() < mLambdaMassMax); - - if (!lambdaOK) { - return false; - } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + p = cascade.mOmega() > mOmegaMassLowerLimit; + this->template fillFilter(kMassMin, p); + pass &= p; - // check mass hypothesis - if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { - // Xi candidate must be inside Xi window and outside Omega - return (cascade.mXi() > mXiMassLowerLimit && cascade.mXi() < mXiMassUpperLimit) && - (cascade.mOmega() < mOmegaMassLowerLimit || cascade.mOmega() > mOmegaMassUpperLimit); + p = cascade.mOmega() < mOmegaMassUpperLimit; + this->template fillFilter(kMassMax, p); + pass &= p; } - if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { - // Omega candidate must be inside Omega window and outside Xi - return (cascade.mOmega() > mOmegaMassLowerLimit && cascade.mOmega() < mOmegaMassUpperLimit) && - (cascade.mXi() < mXiMassLowerLimit || cascade.mXi() > mXiMassUpperLimit); - } + this->template fillFilterSummary(pass); - return false; // should never happen + return this->isPassThrough() || pass; } protected: - float mXiMassLowerLimit = 0.f; - float mXiMassUpperLimit = 999.f; - float mOmegaMassLowerLimit = 0.f; float mOmegaMassUpperLimit = 999.f; + float mXiMassLowerLimit = 0.f; + float mXiMassUpperLimit = 999.f; + // kinematic filters float mPtMin = 0.f; float mPtMax = 6.f; @@ -321,53 +472,98 @@ class CascadeSelection : public BaseSelection producedXis; + o2::framework::Produces producedLiteXis; o2::framework::Produces producedXiMasks; o2::framework::Produces producedXiExtras; + o2::framework::Produces producedXiFrac; o2::framework::Produces producedOmegas; + o2::framework::Produces producedLiteOmegas; o2::framework::Produces producedOmegaMasks; o2::framework::Produces producedOmegaExtras; + o2::framework::Produces producedOmegaFrac; }; struct ConfCascadeTables : o2::framework::ConfigurableGroup { std::string prefix = std::string("CascadeTables"); o2::framework::Configurable produceXis{"produceXis", -1, "Produce Xis (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteXis{"produceLiteXis", -1, "Produce LiteXis (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceXiMasks{"produceXiMasks", -1, "Produce XiMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceXiExtras{"produceXiExtras", -1, "Produce XiExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceXiFrac{"produceXiFrac", -1, "Produce XiFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegas{"produceOmegas", -1, "Produce Omegas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteOmegas{"produceLiteOmegas", -1, "Produce LiteOmegas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegaMasks{"produceOmegaMasks", -1, "Produce OmegaMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceOmegaExtras{"produceOmegaExtras", -1, "Produce OmegaExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaFrac{"produceOmegaFrac", -1, "Produce OmegaFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; }; -template +template class CascadeBuilder { public: CascadeBuilder() = default; ~CascadeBuilder() = default; - template - void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext, T5& trackBuilder) { if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { LOG(info) << "Initialize femto Xi builder..."; + mProduceLiteXis = utils::enableTable("FLiteXis_001", table.produceLiteXis.value, initContext); mProduceXis = utils::enableTable("FXis_001", table.produceXis.value, initContext); - mProduceXiMasks = utils::enableTable("FXiMasks_001", table.produceXiMasks.value, initContext); + mProduceXiMasks = utils::enableTable("FXiMasks_002", table.produceXiMasks.value, initContext); mProduceXiExtras = utils::enableTable("FXiExtras_001", table.produceXiExtras.value, initContext); + mProduceXiFrac = utils::enableTable("FXiFrac_001", table.produceXiFrac.value, initContext); + + if (mProduceXis && mProduceLiteXis) { + LOG(fatal) << "FXis and FLiteXis are mutually exclusive -- enable only one. " + << "FLiteXis is meant to replace FXis at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FXis from FLiteXis downstream."; + } + if (mProduceXis && !trackBuilder.producingTracks()) { + LOG(fatal) << "FXis is enabled, but the track builder is not producing FTracks (full precision). " + << "FXis stores daughter indices into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteXis if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteXis && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteXis is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteXis stores daughter indices into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FXis if TrackTables.produceTracks is enabled instead."; + } } if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { LOG(info) << "Initialize femto Omega builder..."; mProduceOmegas = utils::enableTable("FOmegas_001", table.produceOmegas.value, initContext); - mProduceOmegaMasks = utils::enableTable("FOmegaMasks_001", table.produceOmegaMasks.value, initContext); + mProduceLiteOmegas = utils::enableTable("FLiteOmegas_001", table.produceLiteOmegas.value, initContext); + mProduceOmegaMasks = utils::enableTable("FOmegaMasks_002", table.produceOmegaMasks.value, initContext); mProduceOmegaExtras = utils::enableTable("FOmegaExtras_001", table.produceOmegaExtras.value, initContext); + mProduceOmegaFrac = utils::enableTable("FOmegaFrac_001", table.produceOmegaFrac.value, initContext); + + if (mProduceOmegas && mProduceLiteOmegas) { + LOG(fatal) << "FOmegas and FLiteOmegas are mutually exclusive -- enable only one. " + << "FLiteOmegas is meant to replace FOmegas at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FOmegas from FLiteOmegas downstream."; + } + if (mProduceOmegas && !trackBuilder.producingTracks()) { + LOG(fatal) << "FOmegas is enabled, but the track builder is not producing FTracks (full precision). " + << "FOmegas stores daughter indices into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteOmegas if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteOmegas && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteOmegas is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteOmegas stores daughter indices into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FOmegas if TrackTables.produceTracks is enabled instead."; + } } - if (mProduceXis || mProduceXiExtras || mProduceXiMasks || mProduceOmegas || mProduceOmegaMasks || mProduceOmegaExtras) { + if (mProduceXis || mProduceLiteXis || mProduceXiExtras || mProduceXiFrac || mProduceXiMasks || + mProduceOmegas || mProduceLiteOmegas || mProduceOmegaMasks || mProduceOmegaExtras || mProduceOmegaFrac) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -379,8 +575,8 @@ class CascadeBuilder LOG(info) << "Initialization done..."; } - template - void fillCascades(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& cascadeProducts, T6 const& cascades, T7 const& tracks, T8 const& tracksWithItsPid, T9& trackBuilder) + template + void fillCascades(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& cascadeProducts, T6 const& cascades, T7 const& tracks, T8& trackBuilder) { if (!mFillAnyTable) { return; @@ -400,24 +596,21 @@ class CascadeBuilder collisionBuilder.template fillCollision(collisionProducts, col); - // cleaner, but without ITS pid: auto bachelor = cascade.template bachelor_as(); - auto bachelor = tracksWithItsPid.iteratorAt(cascade.bachelorId() - tracksWithItsPid.offset()); - bachelorIndex = trackBuilder.template getDaughterIndex(bachelor, trackProducts, collisionProducts); + auto bachelor = cascade.template bachelor_as(); + bachelorIndex = trackBuilder.template getDaughterIndex(bachelor, trackProducts, collisionBuilder); - // cleaner, but without ITS pid: auto posDaughter = cascade.template posTrack_as(); - auto posDaughter = tracksWithItsPid.iteratorAt(cascade.posTrackId() - tracksWithItsPid.offset()); - posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionProducts); + auto posDaughter = cascade.template posTrack_as(); + posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionBuilder); - // cleaner, but without ITS pid: auto negDaughter = cascade.template negTrack_as(); - auto negDaughter = tracksWithItsPid.iteratorAt(cascade.negTrackId() - tracksWithItsPid.offset()); - negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, collisionProducts); + auto negDaughter = cascade.template negTrack_as(); + negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, collisionBuilder); - fillCascade(collisionProducts, cascadeProducts, cascade, col, bachelorIndex, posDaughterIndex, negDaughterIndex); + fillCascade(collisionBuilder, cascadeProducts, cascade, col, bachelorIndex, posDaughterIndex, negDaughterIndex); } } - template - void fillMcCascades(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& cascadeProducts, T7 const& cascades, T8 const& tracks, T9 const& tracksWithItsPid, T10& trackBuilder, T11 const& mcParticles, T12& mcBuilder, T13& mcProducts) + template + void fillMcCascades(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& cascadeProducts, T7 const& cascades, T8 const& tracks, T9& trackBuilder, T10 const& mcParticles, T11& mcBuilder, T12& mcProducts) { if (!mFillAnyTable) { return; @@ -437,36 +630,47 @@ class CascadeBuilder collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); - auto bachelor = tracks.iteratorAt(cascade.bachelorId() - tracks.offset()); - auto bachelorWithItsPid = tracksWithItsPid.iteratorAt(cascade.bachelorId() - tracksWithItsPid.offset()); - bachelorIndex = trackBuilder.template getDaughterIndex(col, collisionProducts, mcCols, bachelor, bachelorWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); + auto bachelor = cascade.template bachelor_as(); + bachelorIndex = trackBuilder.template getDaughterIndex(bachelor, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); - auto posDaughter = tracks.iteratorAt(cascade.posTrackId() - tracks.offset()); - auto posDaughterWithItsPid = tracksWithItsPid.iteratorAt(cascade.posTrackId() - tracksWithItsPid.offset()); - posDaughterIndex = trackBuilder.template getDaughterIndex(col, collisionProducts, mcCols, posDaughter, posDaughterWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); + auto posDaughter = cascade.template posTrack_as(); + posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); - auto negDaughter = tracks.iteratorAt(cascade.negTrackId() - tracks.offset()); - auto negDaughterWithItsPid = tracksWithItsPid.iteratorAt(cascade.negTrackId() - tracksWithItsPid.offset()); - negDaughterIndex = trackBuilder.template getDaughterIndex(col, collisionProducts, mcCols, negDaughter, negDaughterWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); + auto negDaughter = cascade.template negTrack_as(); + negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); - fillCascade(collisionProducts, cascadeProducts, cascade, col, bachelorIndex, posDaughterIndex, negDaughterIndex); + fillCascade(collisionBuilder, cascadeProducts, cascade, col, bachelorIndex, posDaughterIndex, negDaughterIndex); if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { - mcBuilder.template fillMcXiWithLabel(col, mcCols, cascade, mcParticles, mcProducts); - ; + mcBuilder.template fillMcXiWithLabel(cascade, mcParticles, mcCols, mcProducts); } if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { - mcBuilder.template fillMcOmegaWithLabel(col, mcCols, cascade, mcParticles, mcProducts); - ; + mcBuilder.template fillMcOmegaWithLabel(cascade, mcParticles, mcCols, mcProducts); } } } template - void fillCascade(T1& collisionProducts, T2& cascadeProducts, T3 const& cascade, T4 const& col, int bachelorIndex, int posDaughterIndex, int negDaughterIndex) + void fillCascade(T1& collisionBuilder, T2& cascadeProducts, T3 const& cascade, T4 const& col, int64_t bachelorIndex, int64_t posDaughterIndex, int64_t negDaughterIndex) { + float strangeTofBachelor = 0.f; + float strangeTofPosDau = 0.f; + float strangeTofNegDau = 0.f; + const bool isMatter = cascade.sign() < 0; // Xi-/Omega- -> Lambda -> p pi- (pos=proton, neg=pion) + + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { + strangeTofBachelor = cascade.tofNSigmaXiPi(); + strangeTofPosDau = isMatter ? cascade.tofNSigmaXiLaPr() : cascade.tofNSigmaXiLaPi(); + strangeTofNegDau = isMatter ? cascade.tofNSigmaXiLaPi() : cascade.tofNSigmaXiLaPr(); + } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + strangeTofBachelor = cascade.tofNSigmaOmKa(); + strangeTofPosDau = isMatter ? cascade.tofNSigmaOmLaPr() : cascade.tofNSigmaOmLaPi(); + strangeTofNegDau = isMatter ? cascade.tofNSigmaOmLaPi() : cascade.tofNSigmaOmLaPr(); + } + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { if (mProduceXis) { - cascadeProducts.producedXis(collisionProducts.producedCollision.lastIndex(), + cascadeProducts.producedXis(collisionBuilder.collisionIndex(), cascade.sign() * cascade.pt(), cascade.eta(), cascade.phi(), @@ -475,6 +679,16 @@ class CascadeBuilder posDaughterIndex, negDaughterIndex); } + if (mProduceLiteXis) { + cascadeProducts.producedLiteXis(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(cascade.sign() * cascade.pt()), + o2::aod::femtobase::lite::binEta(cascade.eta()), + o2::aod::femtobase::lite::binPhi(cascade.phi()), + o2::aod::femtocascades::lite::binXiMass(cascade.mXi()), + bachelorIndex, + posDaughterIndex, + negDaughterIndex); + } if (mProduceXiMasks) { cascadeProducts.producedXiMasks(mCascadeSelection.getBitmask()); } @@ -484,15 +698,22 @@ class CascadeBuilder cascade.casccosPA(col.posX(), col.posY(), col.posZ()), cascade.dcacascdaughters(), cascade.cascradius(), + cascade.mLambda(), cascade.v0cosPA(col.posX(), col.posY(), col.posZ()), cascade.dcaV0daughters(), cascade.v0radius(), - cascade.dcav0topv(col.posY(), col.posY(), col.posZ())); + cascade.dcav0topv(col.posX(), col.posY(), col.posZ()), + strangeTofBachelor, + strangeTofPosDau, + strangeTofNegDau); + } + if (mProduceXiFrac) { + cascadeProducts.producedXiFrac(cascade.casccosPA(col.posX(), col.posY(), col.posZ())); } } if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { if (mProduceOmegas) { - cascadeProducts.producedOmegas(collisionProducts.producedCollision.lastIndex(), + cascadeProducts.producedOmegas(collisionBuilder.collisionIndex(), cascade.sign() * cascade.pt(), cascade.eta(), cascade.phi(), @@ -501,6 +722,16 @@ class CascadeBuilder posDaughterIndex, negDaughterIndex); } + if (mProduceLiteOmegas) { + cascadeProducts.producedLiteOmegas(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(cascade.sign() * cascade.pt()), + o2::aod::femtobase::lite::binEta(cascade.eta()), + o2::aod::femtobase::lite::binPhi(cascade.phi()), + o2::aod::femtocascades::lite::binOmegaMass(cascade.mOmega()), + bachelorIndex, + posDaughterIndex, + negDaughterIndex); + } if (mProduceOmegaMasks) { cascadeProducts.producedOmegaMasks(mCascadeSelection.getBitmask()); } @@ -510,27 +741,153 @@ class CascadeBuilder cascade.casccosPA(col.posX(), col.posY(), col.posZ()), cascade.dcacascdaughters(), cascade.cascradius(), + cascade.mLambda(), cascade.v0cosPA(col.posX(), col.posY(), col.posZ()), cascade.dcaV0daughters(), cascade.v0radius(), - cascade.dcav0topv(col.posY(), col.posY(), col.posZ())); + cascade.dcav0topv(col.posX(), col.posY(), col.posZ()), + strangeTofBachelor, + strangeTofPosDau, + strangeTofNegDau); + } + if (mProduceOmegaFrac) { + cascadeProducts.producedOmegaFrac(cascade.casccosPA(col.posX(), col.posY(), col.posZ())); } } } - bool fillAnyTable() { return mFillAnyTable; } + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mCascadeSelection.isPassThrough(); } private: - CascadeSelection mCascadeSelection; + CascadeSelection mCascadeSelection; bool mFillAnyTable = false; bool mProduceXis = false; + bool mProduceLiteXis = false; bool mProduceXiMasks = false; bool mProduceXiExtras = false; + bool mProduceXiFrac = false; bool mProduceOmegas = false; + bool mProduceLiteOmegas = false; bool mProduceOmegaMasks = false; bool mProduceOmegaExtras = false; + bool mProduceOmegaFrac = false; +}; + +struct ConfCascadeTablesDerivedToDerived : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CascadeTables"); + o2::framework::Configurable limitXi{"limitXi", 1, "Require at least this many Xis in the collision. Set to 0 to skip Xis entirely (not written to output)."}; + o2::framework::Configurable limitOmega{"limitOmega", 0, "Require at least this many Omegas in the collision. Set to 0 to skip Omegas entirely (not written to output)."}; +}; + +struct CascadeBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedXis; + o2::framework::Produces producedXiMasks; + o2::framework::Produces producedOmegas; + o2::framework::Produces producedOmegaMasks; +}; + +class CascadeBuilderDerivedToDerived +{ + public: + template + void init(T& config) + { + mLimitXi = config.limitXi.value; + mLimitOmega = config.limitOmega.value; + + if (mLimitXi < 0 || mLimitOmega < 0) { + LOG(fatal) << "Cascade limits must be non-negative (got " << mLimitXi << " and " << mLimitOmega << "). Breaking..."; + } + if (mLimitXi == 0 && mLimitOmega == 0) { + LOG(fatal) << "Both xi limit and omega limit are 0. Breaking..."; + } + } + + template + bool collisionHasTooFewXis(T1 const& col, T2 const& /*xiTable*/, T3& partitionXi, T4& cache) + { + if (mLimitXi == 0) { // xis disabled, cannot reject on them + return false; + } + auto xiSlice = partitionXi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return xiSlice.size() < static_cast(mLimitXi); + } + + template + bool collisionHasTooFewOmegas(T1 const& col, T2 const& /*omegaTable*/, T3& partitionOmega, T4& cache) + { + if (mLimitOmega == 0) { // omegas disabled, cannot reject on them + return false; + } + auto omegaSlice = partitionOmega->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return omegaSlice.size() < static_cast(mLimitOmega); + } + + template + void processXis(T1 const& col, T2 const& /*xiTable*/, T3 const& oldTrackTable, T4& partitionXi, T5& trackBuilder, T6& cache, T7& newXiTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitXi == 0) { // xis disabled + return; + } + + auto xiSlice = partitionXi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& xi : xiSlice) { + auto bachelor = oldTrackTable.rawIteratorAt(utils::daughterRow(xi.bachelorId(), oldTrackTable)); + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(xi.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(xi.negDauId(), oldTrackTable)); + + int64_t bachelorIndex = trackBuilder.getDaughterIndex(bachelor, newTrackTable, newCollisionTable); + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newXiTable.producedXis(newCollisionTable.producedCollision.lastIndex(), + xi.signedPt(), + xi.eta(), + xi.phi(), + xi.mass(), + bachelorIndex, + posDaughterIndex, + negDaughterIndex); + newXiTable.producedXiMasks(xi.mask()); + } + } + + template + void processOmegas(T1 const& col, T2 const& /*omegaTable*/, T3 const& oldTrackTable, T4& partitionOmega, T5& trackBuilder, T6& cache, T7& newOmegaTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitOmega == 0) { // omegas disabled + return; + } + + auto omegaSlice = partitionOmega->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& omega : omegaSlice) { + auto bachelor = oldTrackTable.rawIteratorAt(utils::daughterRow(omega.bachelorId(), oldTrackTable)); + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(omega.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(omega.negDauId(), oldTrackTable)); + + int64_t bachelorIndex = trackBuilder.getDaughterIndex(bachelor, newTrackTable, newCollisionTable); + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newOmegaTable.producedOmegas(newCollisionTable.producedCollision.lastIndex(), + omega.signedPt(), + omega.eta(), + omega.phi(), + omega.mass(), + bachelorIndex, + posDaughterIndex, + negDaughterIndex); + newOmegaTable.producedOmegaMasks(omega.mask()); + } + } + + private: + int mLimitXi = 0; + int mLimitOmega = 0; }; -} // namespace cascadebuilder -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::cascadebuilder #endif // PWGCF_FEMTO_CORE_CASCADEBUILDER_H_ diff --git a/PWGCF/Femto/Core/cascadeHistManager.h b/PWGCF/Femto/Core/cascadeHistManager.h index 9ee370b5c22..73d13b7a6a8 100644 --- a/PWGCF/Femto/Core/cascadeHistManager.h +++ b/PWGCF/Femto/Core/cascadeHistManager.h @@ -36,9 +36,7 @@ #include #include -namespace o2::analysis::femto -{ -namespace cascadehistmanager +namespace o2::analysis::femto::cascadehistmanager { // enum for track histograms enum CascadeHist { @@ -48,24 +46,35 @@ enum CascadeHist { kPhi, kMass, kSign, + kPtVsMass, // nice to have during analysis + kFractionCpa, // fraction estimation: pt vs cosPa, binned independently of the QA cosPa axis // qa variables kMassXi, kMassOmega, kCosPa, + kPa, kDecayDauDca, kTransRadius, + kLambdaMass, kLambdaCosPa, kLambdaDauDca, kLambdaTransRadius, kLambdaDcaToPv, + kStrangeTofBachelor, + kStrangeTofPosDau, + kStrangeTofNegDau, // 2d qa kPtVsEta, kPtVsPhi, kPhiVsEta, - kPtVsCosPa, + kPtVsPa, kPtVsMassXi, kPtVsMassOmega, + kPtVsMassLambda, kMassXiVsMassOmega, + kStrangeTofVsTofBachelor, + kStrangeTofVsTofPosDau, + kStrangeTofVsTofNegDau, // mc kOrigin, kPdg, @@ -74,7 +83,7 @@ enum CascadeHist { kTruePtVsPt, kTrueEtaVsEta, kTruePhiVsPhi, - // histograms for fraction estimation of v0s + // histograms for fraction estimation of cascades kNoMcParticle, kPrimary, kFromWrongCollision, @@ -89,7 +98,7 @@ enum CascadeHist { constexpr std::size_t MaxSecondary = 3; -template +template struct ConfCascadeBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; @@ -98,6 +107,7 @@ struct ConfCascadeBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis mass{"mass", {{1000, 1.f, 2.f}}, "Mass"}; o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed cascades"}; + o2::framework::ConfigurableAxis fractionCpa{"fractionCpa", {{100, 0.95, 1}}, "Cosine of pointing angle (fraction estimation)"}; }; constexpr const char PrefixXiBinning[] = "XiBinning"; @@ -105,26 +115,31 @@ using ConfXiBinning = ConfCascadeBinning; constexpr const char PrefixOmegaBinning[] = "OmegaBinning"; using ConfOmegaBinning = ConfCascadeBinning; -template +template struct ConfCascadeQaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::Configurable plot2d{"plot2d", true, "Enable 2d Qa histograms"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; + o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; + o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; - o2::framework::ConfigurableAxis massXi{"massXi", {{400, 1.2f, 1.6f}}, "mass for antiparticle hypothesis"}; - o2::framework::ConfigurableAxis massOmega{"massOmega", {{400, 1.4f, 1.8f}}, "mass for antiparticle hypothesis"}; - o2::framework::ConfigurableAxis lambdaCosPa{"lambdaCosPa", {{100, 0.9, 1}}, "Cosine of poiting angle of daughter lambda"}; + o2::framework::ConfigurableAxis massXi{"massXi", {{400, 1.2f, 1.6f}}, "Mass under the Xi hypothesis"}; + o2::framework::ConfigurableAxis massOmega{"massOmega", {{400, 1.4f, 1.8f}}, "Mass under the Omega hypothesis"}; + o2::framework::ConfigurableAxis lambdaMass{"lambdaMass", {{200, 1., 1.2}}, "Mass of daughter lambda"}; + o2::framework::ConfigurableAxis lambdaCosPa{"lambdaCosPa", {{100, 0.9, 1}}, "Cosine of pointing angle of daughter lambda"}; o2::framework::ConfigurableAxis lambdaDauDca{"lambdaDauDca", {{150, 0, 1.0}}, "DCA of lambda daughters at lambda decay vertex"}; - o2::framework::ConfigurableAxis lambdaTransRadius{"lambdaTransRadius", {{100, 0, 100}}, "DCA of lambda daughters at lambda decay vertex"}; + o2::framework::ConfigurableAxis lambdaTransRadius{"lambdaTransRadius", {{100, 0, 100}}, "Transverse radius of daughter #Lambda decay vertex"}; o2::framework::ConfigurableAxis lambdaDcaToPv{"lambdaDcaToPv", {{100, 0, 200}}, "DCA of lambda daughter from primary vertex"}; + o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for bachelor/daughters"}; }; constexpr const char PrefixXiQaBinning[] = "XiQaBinning"; using ConfXiQaBinning = ConfCascadeQaBinning; -constexpr const char PrefixOmegatQaBinning[] = "OmegaQaBinning"; -using ConfOmegaQaBinning = ConfCascadeQaBinning; +constexpr const char PrefixOmegaQaBinning[] = "OmegaQaBinning"; +using ConfOmegaQaBinning = ConfCascadeQaBinning; // must be in sync with enum TrackVariables // the enum gives the correct index in the array @@ -133,27 +148,38 @@ constexpr std::array, kCascadeHistLast> HistT {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, - {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> antiparticle, 0 -> self conjugate, +1 -> particle); sign; Entries"}, + {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> particle, 0 -> self conjugate, +1 -> antiparticle); sign; Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "Transverse momentum vs invariant mass; p_{T} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, + {kFractionCpa, o2::framework::HistType::kTH2F, "hFractionCpa", "p_{T} vs Cosine of pointing angle (fraction estimation); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMassXi, o2::framework::HistType::kTH1F, "hMassXi", "Mass #Xi; m_{#Lambda#pi} (GeV/#it{c}^{2}); Entries"}, {kMassOmega, o2::framework::HistType::kTH1F, "hMassOmega", "mass #Omega; m_{#LambdaK} (GeV/#it{c}^{2}); Entries"}, {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, + {kPa, o2::framework::HistType::kTH1F, "hPa", "Pointing angle; #alpha; Entries"}, {kDecayDauDca, o2::framework::HistType::kTH1F, "hDauDca", "Daughter DCA at decay vertex ; DCA_{Decay vertex} (cm); Entries"}, {kTransRadius, o2::framework::HistType::kTH1F, "hTransRadius", "Transverse radius ; r_{xy} (cm); Entries"}, - {kLambdaCosPa, o2::framework::HistType::kTH1F, "hLambdaCosPa", "Cosine of poiting angle of daughter lambda ; cos_{#Lambda}(#alpha); Entries"}, - {kLambdaDauDca, o2::framework::HistType::kTH1F, "hLambdaDauDca", "Daughter DCA at #Lambda decay vertex ; DCA_{#Lambda decay vertex} (cm); Entries"}, - {kLambdaTransRadius, o2::framework::HistType::kTH1F, "hLambdaTransRadius", "Transverse radius of daughter #Lambda ; r_{xy} (cm); Entries"}, - {kLambdaDcaToPv, o2::framework::HistType::kTH1F, "hLambdaDcaToPv", "DCA to primary vertex of daughter #Lambda ; DCA (cm); Entries"}, + {kLambdaMass, o2::framework::HistType::kTH1F, "hLambdaMass", "Invariant mass of daughter lambda ; m_{#Lambda dau} (GeV/#it{c}^{2}); Entries"}, + {kLambdaCosPa, o2::framework::HistType::kTH1F, "hLambdaCosPa", "Cosine of pointing angle of daughter lambda ; cos_{#Lambda dau}(#alpha); Entries"}, + {kLambdaDauDca, o2::framework::HistType::kTH1F, "hLambdaDauDca", "Daughter DCA at #Lambda decay vertex ; DCA_{#Lambda dau decay vertex} (cm); Entries"}, + {kLambdaTransRadius, o2::framework::HistType::kTH1F, "hLambdaTransRadius", "Transverse radius of daughter #Lambda ; r_{xy,#Lambda dau} (cm); Entries"}, + {kLambdaDcaToPv, o2::framework::HistType::kTH1F, "hLambdaDcaToPv", "DCA to primary vertex of daughter #Lambda ; DCA_{#Lambda} (cm); Entries"}, + {kStrangeTofBachelor, o2::framework::HistType::kTH1F, "hStrangeTofBachelor", "Strange TOF of bachelor ; n#sigma_{TOF, strange}; Entries"}, + {kStrangeTofPosDau, o2::framework::HistType::kTH1F, "hStrangeTofPosDau", "Strange TOF of positive Daughter ; n#sigma_{TOF, strange}; Entries"}, + {kStrangeTofNegDau, o2::framework::HistType::kTH1F, "hStrangeTofNegDau", "Strange TOF of negative Daughter ; n#sigma_{TOF, strange}; Entries"}, {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "Cosine of poiting angle vs p_{T}; cos(#alpha); p_{T} (GeV/#it{c})"}, + {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle; p_{T} (GeV/#it{c}); #alpha"}, {kPtVsMassXi, o2::framework::HistType::kTH2F, "hPtVsMassXi", "p_{T} vs mass #Xi; p_{T} (GeV/#it{c}); m_{#Lambda#pi} (GeV/#it{c}^{2})"}, {kPtVsMassOmega, o2::framework::HistType::kTH2F, "hPtVsMassOmega", "p_{T} vs mass #Omega; p_{T} (GeV/#it{c}); m_{#LambdaK} (GeV/#it{c}^{2})"}, + {kPtVsMassLambda, o2::framework::HistType::kTH2F, "hPtVsMassLambda", "p_{T} vs mass daughter #Lambda; p_{T} (GeV/#it{c}); m_{#Lambda dau} (GeV/#it{c}^{2})"}, {kMassXiVsMassOmega, o2::framework::HistType::kTH2F, "hMassXiVsMassOmega", "mass #Xi vs mass #Omega; m_{#Lambda#pi} (GeV/#it{c}^{2}); m_{#LambdaK} (GeV/#it{c}^{2})"}, + {kStrangeTofVsTofBachelor, o2::framework::HistType::kTH2F, "hStrangeTofVsTofBachelor", "TOF_{Strange} vs TOF_{Tracking} of bachelor; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, + {kStrangeTofVsTofPosDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofPosDau", "TOF_{Strange} vs TOF_{Tracking} of positive Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, + {kStrangeTofVsTofNegDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofNegDau", "TOF_{Strange} vs TOF_{Tracking} of negative Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, - {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed v0; PDG Code; Entries"}, - {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed v0; PDG Code; Entries"}, - {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed v0; PDG Code; Entries"}, + {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed cascade; PDG Code; Entries"}, + {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed cascade; PDG Code; Entries"}, + {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed cascade; PDG Code; Entries"}, {kTruePtVsPt, o2::framework::HistType::kTH2F, "hTruePtVsPt", "True transverse momentum vs transverse momentum; p_{T,True} (GeV/#it{c}); p_{T,True} (GeV/#it{c})"}, {kTrueEtaVsEta, o2::framework::HistType::kTH2F, "hTrueEtaVsEta", "True pseudorapdity vs pseudorapdity; #eta_{True}; #eta"}, {kTruePhiVsPhi, o2::framework::HistType::kTH2F, "hTruePhiVsPhi", "True azimuthal angle vs azimuthal angle; #varphi_{True}; #varphi"}, @@ -163,59 +189,72 @@ constexpr std::array, kCascadeHistLast> HistT {kFromMaterial, o2::framework::HistType::kTH2F, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMissidentified, o2::framework::HistType::kTH2F, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kSecondary1, o2::framework::HistType::kTH2F, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}}, + {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}}, }; -#define CASCADE_HIST_ANALYSIS_MAP(conf) \ - {kPt, {conf.pt}}, \ - {kEta, {conf.eta}}, \ - {kPhi, {conf.phi}}, \ - {kMass, {conf.mass}}, \ - {kSign, {conf.sign}}, - -#define CASCADE_HIST_MC_MAP(conf) \ - {kTruePtVsPt, {conf.pt, conf.pt}}, \ - {kTrueEtaVsEta, {conf.eta, conf.eta}}, \ - {kTruePhiVsPhi, {conf.phi, conf.phi}}, \ - {kPdg, {conf.pdgCodes}}, \ - {kPdgMother, {conf.pdgCodes}}, \ - {kPdgPartonicMother, {conf.pdgCodes}}, - -#define CASCADE_HIST_QA_MAP(confAnalysis, confQa) \ - {kPt, {confAnalysis.pt}}, \ - {kEta, {confAnalysis.eta}}, \ - {kPhi, {confAnalysis.phi}}, \ - {kMass, {confAnalysis.mass}}, \ - {kSign, {confAnalysis.sign}}, \ - {kCosPa, {confQa.cosPa}}, \ - {kDecayDauDca, {confQa.dauDcaAtDecay}}, \ - {kTransRadius, {confQa.transRadius}}, \ - {kLambdaCosPa, {confQa.lambdaCosPa}}, \ - {kLambdaDauDca, {confQa.lambdaDauDca}}, \ - {kLambdaTransRadius, {confQa.lambdaTransRadius}}, \ - {kLambdaDcaToPv, {confQa.lambdaDcaToPv}}, \ - {kPtVsEta, {confAnalysis.pt, confAnalysis.eta}}, \ - {kPtVsPhi, {confAnalysis.pt, confAnalysis.phi}}, \ - {kPhiVsEta, {confAnalysis.phi, confAnalysis.eta}}, \ - {kPtVsCosPa, {confAnalysis.pt, confQa.cosPa}}, \ - {kMassXi, {confQa.massXi}}, \ - {kMassOmega, {confQa.massOmega}}, \ - {kPtVsMassXi, {confAnalysis.pt, confQa.massXi}}, \ - {kPtVsMassOmega, {confAnalysis.pt, confQa.massOmega}}, \ - {kMassXiVsMassOmega, {confQa.massXi, confQa.massOmega}}, - -#define CASCADE_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {confAnalysis.pt, confQa.cosPa}}, \ - {kPrimary, {confAnalysis.pt, confQa.cosPa}}, \ - {kFromWrongCollision, {confAnalysis.pt, confQa.cosPa}}, \ - {kFromMaterial, {confAnalysis.pt, confQa.cosPa}}, \ - {kMissidentified, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondary1, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondary2, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondary3, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondaryOther, {confAnalysis.pt, confQa.cosPa}}, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CASCADE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, \ + {kFractionCpa, {(conf).pt, (conf).fractionCpa}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CASCADE_HIST_MC_MAP(conf) \ + {kTruePtVsPt, {(conf).pt, (conf).pt}}, \ + {kTrueEtaVsEta, {(conf).eta, (conf).eta}}, \ + {kTruePhiVsPhi, {(conf).phi, (conf).phi}}, \ + {kPdg, {(conf).pdgCodes}}, \ + {kPdgMother, {(conf).pdgCodes}}, \ + {kPdgPartonicMother, {(conf).pdgCodes}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CASCADE_HIST_QA_MAP(confAnalysis, confQa) \ + {kCosPa, {(confQa).cosPa}}, \ + {kPa, {(confQa).pa}}, \ + {kDecayDauDca, {(confQa).dauDcaAtDecay}}, \ + {kTransRadius, {(confQa).transRadius}}, \ + {kLambdaMass, {(confQa).lambdaMass}}, \ + {kLambdaCosPa, {(confQa).lambdaCosPa}}, \ + {kLambdaDauDca, {(confQa).lambdaDauDca}}, \ + {kLambdaTransRadius, {(confQa).lambdaTransRadius}}, \ + {kLambdaDcaToPv, {(confQa).lambdaDcaToPv}}, \ + {kStrangeTofBachelor, {(confQa).strangeTof}}, \ + {kStrangeTofPosDau, {(confQa).strangeTof}}, \ + {kStrangeTofNegDau, {(confQa).strangeTof}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsPa, {(confAnalysis).pt, (confQa).pa}}, \ + {kMassXi, {(confQa).massXi}}, \ + {kMassOmega, {(confQa).massOmega}}, \ + {kPtVsMassXi, {(confAnalysis).pt, (confQa).massXi}}, \ + {kPtVsMassOmega, {(confAnalysis).pt, (confQa).massOmega}}, \ + {kPtVsMassLambda, {(confAnalysis).pt, (confQa).lambdaMass}}, \ + {kMassXiVsMassOmega, {(confQa).massXi, (confQa).massOmega}}, \ + {kStrangeTofVsTofBachelor, {(confQa).strangeTof, (confQa).strangeTof}}, \ + {kStrangeTofVsTofPosDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ + {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, + +// per-origin truth-split variants of the FitVars fraction-estimation CPA histogram; binned on +// fractionCpa (not the QA-only cosPa) so they are available wherever FitVars is, including the +// pair task where no QA binning config exists +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CASCADE_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionCpa}}, \ + {kPrimary, {(conf).pt, (conf).fractionCpa}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionCpa}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionCpa}}, \ + {kMissidentified, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary1, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary2, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary3, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionCpa}}, template auto makeCascadeHistSpecMap(const T& confBinningAnalysis) @@ -229,7 +268,8 @@ auto makeCascadeMcHistSpecMap(const T& confBinningAnalysis) { return std::map>{ CASCADE_HIST_ANALYSIS_MAP(confBinningAnalysis) - CASCADE_HIST_MC_MAP(confBinningAnalysis)}; + CASCADE_HIST_MC_MAP(confBinningAnalysis) + CASCADE_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } template @@ -247,32 +287,35 @@ std::map> makeCascadeMcQaHistS CASCADE_HIST_ANALYSIS_MAP(confBinningAnalysis) CASCADE_HIST_QA_MAP(confBinningAnalysis, confBinningQa) CASCADE_HIST_MC_MAP(confBinningAnalysis) - CASCADE_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + CASCADE_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } #undef CASCADE_HIST_ANALYSIS_MAP #undef CASCADE_HIST_MC_MAP #undef CASCADE_HIST_QA_MAP -#undef CASCADE_HIST_MC_QA_MAP +#undef CASCADE_HIST_FITVARS_MC_MAP constexpr char PrefixXiQa[] = "XiQA/"; -constexpr char PrefixXi[] = "Xi/"; +constexpr char PrefixXiSe[] = "Xi/SE/"; +constexpr char PrefixXiMe[] = "Xi/ME/"; constexpr char PrefixOmegaQa[] = "OmegaQa/"; -constexpr char PrefixOmega[] = "Omega/"; +constexpr char PrefixOmegaSe[] = "Omega/SE/"; +constexpr char PrefixOmegaMe[] = "Omega/ME/"; constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; /// \class FemtoDreamEventHisto /// \brief Class for histogramming event properties // template -template class CascadeHistManager { @@ -290,7 +333,6 @@ class CascadeHistManager { mHistogramRegistry = registry; mPdgCode = std::abs(ConfCascadeSelection.pdgCodeAbs.value); - int bachelorPdgCodeAbs = 0; int posDauPdgCodeAbs = 0; int negDauPdgCodeAbs = 0; @@ -328,17 +370,19 @@ class CascadeHistManager } else { LOG(fatal) << "PDG code for Cascade has to be either Xi or Omega"; } - mBachelorManager.template init(registry, BachelorSpecs, absCharge, signBachelor, bachelorPdgCodeAbs); mPosDauManager.template init(registry, PosDauSpecs, absCharge, signPlus, posDauPdgCodeAbs); mNegDauManager.template init(registry, NegDauSpecs, absCharge, signMinus, negDauPdgCodeAbs); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { initAnalysis(cascadeSpecs); } if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(cascadeSpecs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(cascadeSpecs); + } } template @@ -349,9 +393,9 @@ class CascadeHistManager std::map> const& BachelorSpecs, T3 const& ConfBachelorQaBinning, std::map> const& PosDauSpecs, - T4& ConfPosDauQaBinning, + T4 const& ConfPosDauQaBinning, std::map> const& NegDauSpecs, - T5& ConfNegDauQaBinning) + T5 const& ConfNegDauQaBinning) { mHistogramRegistry = registry; mPdgCode = std::abs(ConfCascadeSelection.pdgCodeAbs.value); @@ -399,7 +443,7 @@ class CascadeHistManager mPosDauManager.template init(registry, PosDauSpecs, absCharge, signPlus, posDauPdgCodeAbs, ConfPosDauQaBinning); mNegDauManager.template init(registry, NegDauSpecs, absCharge, signMinus, negDauPdgCodeAbs, ConfNegDauQaBinning); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { initAnalysis(cascadeSpecs); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { @@ -408,6 +452,9 @@ class CascadeHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(cascadeSpecs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(cascadeSpecs); + } } template @@ -420,32 +467,38 @@ class CascadeHistManager auto bachelor = tracks.rawIteratorAt(cascadeCandidate.bachelorId() - tracks.offset()); mBachelorManager.template fill(bachelor, tracks); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { fillAnalysis(cascadeCandidate); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { - fillQa(cascadeCandidate); + fillQa(cascadeCandidate, bachelor, posDaughter, negDaughter); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(cascadeCandidate); } } - template - void fill(T1 const& cascadeCandidate, T2 const& tracks, T3 const& mcParticles, T4 const& mcMothers, T5 const& mcPartonicMothers) + template + void fill(T1 const& cascadeCandidate, T2 const& tracks, T3 const& col, T4 const& mcParticles, T5 const& mcMothers, T6 const& mcPartonicMothers) { auto posDaughter = tracks.rawIteratorAt(cascadeCandidate.posDauId() - tracks.offset()); - mPosDauManager.template fill(posDaughter, tracks, mcParticles, mcMothers, mcPartonicMothers); + mPosDauManager.template fill(posDaughter, tracks, col, mcParticles, mcMothers, mcPartonicMothers); auto negDaughter = tracks.rawIteratorAt(cascadeCandidate.negDauId() - tracks.offset()); - mNegDauManager.template fill(negDaughter, tracks, mcParticles, mcMothers, mcPartonicMothers); + mNegDauManager.template fill(negDaughter, tracks, col, mcParticles, mcMothers, mcPartonicMothers); auto bachelor = tracks.rawIteratorAt(cascadeCandidate.bachelorId() - tracks.offset()); - mBachelorManager.template fill(bachelor, tracks, mcParticles, mcMothers, mcPartonicMothers); + mBachelorManager.template fill(bachelor, tracks, col, mcParticles, mcMothers, mcPartonicMothers); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { this->fillAnalysis(cascadeCandidate); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { - this->fillQa(cascadeCandidate); + this->fillQa(cascadeCandidate, bachelor, posDaughter, negDaughter); } if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { - this->template fillMc(cascadeCandidate, mcParticles, mcMothers, mcPartonicMothers); + this->template fillMc(cascadeCandidate, col, mcParticles, mcMothers, mcPartonicMothers); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(cascadeCandidate); } } @@ -454,6 +507,15 @@ class CascadeHistManager void enableOptionalHistograms(T const& CascadeConfBinningQa) { mPlot2d = CascadeConfBinningQa.plot2d.value; + mPlotOrigins = CascadeConfBinningQa.plotOrigins.value; + mPlotNSecondaries = CascadeConfBinningQa.pdgCodesForMothersOfSecondary.value.size(); + for (std::size_t i = 0; i < MaxSecondary; i++) { + if (i < CascadeConfBinningQa.pdgCodesForMothersOfSecondary.value.size()) { + mPdgCodesSecondaryMother.at(i) = std::abs(CascadeConfBinningQa.pdgCodesForMothersOfSecondary.value.at(i)); + } else { + mPdgCodesSecondaryMother.at(i) = 0; + } + } } void initAnalysis(std::map> const& cascadeSpecs) @@ -464,30 +526,40 @@ class CascadeHistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {cascadeSpecs.at(kPhi)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {cascadeSpecs.at(kMass)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kSign, HistTable), getHistDesc(kSign, HistTable), getHistType(kSign, HistTable), {cascadeSpecs.at(kSign)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {cascadeSpecs.at(kPtVsMass)}); } void initQa(std::map> const& cascadeSpecs) { std::string qaDir = std::string(cascadePrefix) + std::string(QaDir); mHistogramRegistry->add(qaDir + getHistNameV2(kCosPa, HistTable), getHistDesc(kCosPa, HistTable), getHistType(kCosPa, HistTable), {cascadeSpecs.at(kCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPa, HistTable), getHistDesc(kPa, HistTable), getHistType(kPa, HistTable), {cascadeSpecs.at(kPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kDecayDauDca, HistTable), getHistDesc(kDecayDauDca, HistTable), getHistType(kDecayDauDca, HistTable), {cascadeSpecs.at(kDecayDauDca)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTransRadius, HistTable), getHistDesc(kTransRadius, HistTable), getHistType(kTransRadius, HistTable), {cascadeSpecs.at(kTransRadius)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kLambdaMass, HistTable), getHistDesc(kLambdaMass, HistTable), getHistType(kLambdaMass, HistTable), {cascadeSpecs.at(kLambdaMass)}); mHistogramRegistry->add(qaDir + getHistNameV2(kLambdaCosPa, HistTable), getHistDesc(kLambdaCosPa, HistTable), getHistType(kLambdaCosPa, HistTable), {cascadeSpecs.at(kLambdaCosPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kLambdaDauDca, HistTable), getHistDesc(kLambdaDauDca, HistTable), getHistType(kLambdaDauDca, HistTable), {cascadeSpecs.at(kLambdaDauDca)}); mHistogramRegistry->add(qaDir + getHistNameV2(kLambdaTransRadius, HistTable), getHistDesc(kLambdaTransRadius, HistTable), getHistType(kLambdaTransRadius, HistTable), {cascadeSpecs.at(kLambdaTransRadius)}); mHistogramRegistry->add(qaDir + getHistNameV2(kLambdaDcaToPv, HistTable), getHistDesc(kLambdaDcaToPv, HistTable), getHistType(kLambdaDcaToPv, HistTable), {cascadeSpecs.at(kLambdaDcaToPv)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMassXi, HistTable), getHistDesc(kMassXi, HistTable), getHistType(kMassXi, HistTable), {cascadeSpecs.at(kMassXi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMassOmega, HistTable), getHistDesc(kMassOmega, HistTable), getHistType(kMassOmega, HistTable), {cascadeSpecs.at(kMassOmega)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofBachelor, HistTable), getHistDesc(kStrangeTofBachelor, HistTable), getHistType(kStrangeTofBachelor, HistTable), {cascadeSpecs.at(kStrangeTofBachelor)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofPosDau, HistTable), getHistDesc(kStrangeTofPosDau, HistTable), getHistType(kStrangeTofPosDau, HistTable), {cascadeSpecs.at(kStrangeTofPosDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofNegDau, HistTable), getHistDesc(kStrangeTofNegDau, HistTable), getHistType(kStrangeTofNegDau, HistTable), {cascadeSpecs.at(kStrangeTofNegDau)}); if (mPlot2d) { mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {cascadeSpecs.at(kPtVsEta)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {cascadeSpecs.at(kPtVsPhi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {cascadeSpecs.at(kPhiVsEta)}); - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {cascadeSpecs.at(kPtVsCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPa, HistTable), getHistDesc(kPtVsPa, HistTable), getHistType(kPtVsPa, HistTable), {cascadeSpecs.at(kPtVsPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsMassXi, HistTable), getHistDesc(kPtVsMassXi, HistTable), getHistType(kPtVsMassXi, HistTable), {cascadeSpecs.at(kPtVsMassXi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsMassOmega, HistTable), getHistDesc(kPtVsMassOmega, HistTable), getHistType(kPtVsMassOmega, HistTable), {cascadeSpecs.at(kPtVsMassOmega)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsMassLambda, HistTable), getHistDesc(kPtVsMassLambda, HistTable), getHistType(kPtVsMassLambda, HistTable), {cascadeSpecs.at(kPtVsMassLambda)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMassXiVsMassOmega, HistTable), getHistDesc(kMassXiVsMassOmega, HistTable), getHistType(kMassXiVsMassOmega, HistTable), {cascadeSpecs.at(kMassXiVsMassOmega)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofBachelor, HistTable), getHistDesc(kStrangeTofVsTofBachelor, HistTable), getHistType(kStrangeTofVsTofBachelor, HistTable), {cascadeSpecs.at(kStrangeTofVsTofBachelor)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofPosDau, HistTable), getHistDesc(kStrangeTofVsTofPosDau, HistTable), getHistType(kStrangeTofVsTofPosDau, HistTable), {cascadeSpecs.at(kStrangeTofVsTofPosDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofNegDau, HistTable), getHistDesc(kStrangeTofVsTofNegDau, HistTable), getHistType(kStrangeTofVsTofNegDau, HistTable), {cascadeSpecs.at(kStrangeTofVsTofNegDau)}); } } @@ -511,24 +583,36 @@ class CascadeHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {V0Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {V0Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {V0Specs.at(kPdgPartonicMother)}); + } - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + // fraction estimation (CPA); only called when mode carries kFitVars + template + void initFitVars(std::map> const& cascadeSpecs) + { + std::string fitVarsDir = std::string(cascadePrefix) + std::string(FitVarsDir); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionCpa, HistTable), getHistDesc(kFractionCpa, HistTable), getHistType(kFractionCpa, HistTable), {cascadeSpecs.at(kFractionCpa)}); - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + // per-origin truth-split fraction-CPA templates; need MC truth, so only booked when the mode + // also carries kMc + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {cascadeSpecs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {cascadeSpecs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {cascadeSpecs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {cascadeSpecs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {cascadeSpecs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {cascadeSpecs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {cascadeSpecs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {cascadeSpecs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {cascadeSpecs.at(kSecondaryOther)}); } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } } @@ -540,126 +624,158 @@ class CascadeHistManager mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), cascadeCandidate.phi()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), cascadeCandidate.mass()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(getHistName(kSign, HistTable)), cascadeCandidate.sign()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), cascadeCandidate.pt(), cascadeCandidate.mass()); } template - void fillQa(T const& cascadeCandidate) + void fillFitVars(T const& cascadeCandidate) + { + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionCpa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + } + + template + void fillQa(T1 const& cascadeCandidate, T2 const& bachelor, T3 const& posDau, T4 const& negDau) { float massXi = 0.f; float massOmega = 0.f; + float tofBachelor = 0.f; + float tofPosDau = 0.f; + float tofNegDau = 0.f; if constexpr (modes::isEqual(cascade, modes::Cascade::kXi)) { massXi = cascadeCandidate.mass(); massOmega = cascadeCandidate.massOmega(); + tofBachelor = bachelor.tofNSigmaPi(); } if constexpr (modes::isEqual(cascade, modes::Cascade::kOmega)) { massXi = cascadeCandidate.massXi(); massOmega = cascadeCandidate.mass(); + tofBachelor = bachelor.tofNSigmaKa(); } + + if (cascadeCandidate.sign() > 0) { + tofPosDau = posDau.tofNSigmaPr(); + tofNegDau = negDau.tofNSigmaPi(); + } else { + tofPosDau = posDau.tofNSigmaPi(); + tofNegDau = negDau.tofNSigmaPr(); + } + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kCosPa, HistTable)), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPa, HistTable)), cascadeCandidate.cascadePa()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kDecayDauDca, HistTable)), cascadeCandidate.cascadeDauDca()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kTransRadius, HistTable)), cascadeCandidate.cascadeTransRadius()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kLambdaMass, HistTable)), cascadeCandidate.lambdaMass()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kLambdaCosPa, HistTable)), cascadeCandidate.lambdaCosPa()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kLambdaDauDca, HistTable)), cascadeCandidate.lambdaDauDca()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kLambdaTransRadius, HistTable)), cascadeCandidate.lambdaTransRadius()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kLambdaDcaToPv, HistTable)), cascadeCandidate.lambdaDcaToPv()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kMassXi, HistTable)), massXi); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kMassOmega, HistTable)), massOmega); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofBachelor, HistTable)), cascadeCandidate.strangeTofBachelor()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofPosDau, HistTable)), cascadeCandidate.strangeTofPosDau()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofNegDau, HistTable)), cascadeCandidate.strangeTofNegDau()); if (mPlot2d) { mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), cascadeCandidate.pt(), cascadeCandidate.eta()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), cascadeCandidate.pt(), cascadeCandidate.phi()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), cascadeCandidate.phi(), cascadeCandidate.eta()); - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPa, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadePa()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMassXi, HistTable)), cascadeCandidate.pt(), massXi); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMassOmega, HistTable)), cascadeCandidate.pt(), massOmega); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMassLambda, HistTable)), cascadeCandidate.pt(), cascadeCandidate.lambdaMass()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kMassXiVsMassOmega, HistTable)), massXi, massOmega); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofBachelor, HistTable)), cascadeCandidate.strangeTofBachelor(), tofBachelor); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofPosDau, HistTable)), cascadeCandidate.strangeTofPosDau(), tofPosDau); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofNegDau, HistTable)), cascadeCandidate.strangeTofNegDau(), tofNegDau); } } - template - void fillMc(T1 const& cascadeCandidate, T2 const& /*mcParticles*/, T3 const& /*mcMothers*/, T4 const& /*mcPartonicMothers*/) + template + void fillMc(T1 const& cascadeCandidate, T2 const& col, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, T5 const& /*mcPartonicMothers*/) { // No MC Particle if (!cascadeCandidate.has_fMcParticle()) { mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } } return; } // Retrieve MC particle - auto mcParticle = cascadeCandidate.template fMcParticle_as(); + auto mcParticle = cascadeCandidate.template fMcParticle_as(); + + // whether a particle is associated to a wrong collision or not cannot be known by the producer so we check it here + bool fromWrongCollision = mcParticle.fMcColId() != col.fMcColId(); - // missidentifed particles are special case // whether a particle is missidentfied or not cannot be known by the producer so we check it here bool isMissidentified = mcParticle.pdgCode() != mPdgCode; mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kTruePtVsPt, HistTable)), mcParticle.pt(), cascadeCandidate.pt()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kTrueEtaVsEta, HistTable)), mcParticle.eta(), cascadeCandidate.eta()); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kTruePhiVsPhi, HistTable)), mcParticle.phi(), cascadeCandidate.phi()); - if (isMissidentified) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kMissidentified)); + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kFromWrongCollision)); + } else if (isMissidentified) { + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kMissidentified)); } else { mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); } mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), mcParticle.pdgCode()); // get mother - if (cascadeCandidate.has_fMcMother()) { - auto mother = cascadeCandidate.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), mother.pdgCode()); } else { mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), 0); } // get partonic mother - if (cascadeCandidate.has_fMcPartMoth()) { - auto partonicMother = cascadeCandidate.template fMcPartMoth_as(); + if (mcParticle.has_fMcPartMoth()) { + auto partonicMother = mcParticle.template fMcPartMoth_as(); mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), partonicMother.pdgCode()); } else { mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - // check first if particle is missidentified - if (isMissidentified) { + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); - break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); break; case modes::McOrigin::kFromSecondaryDecay: - if (cascadeCandidate.has_fMcMother()) { - auto mother = cascadeCandidate.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } else { - mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); + mHistogramRegistry->fill(HIST(cascadePrefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), cascadeCandidate.pt(), cascadeCandidate.cascadeCosPa()); } } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } @@ -669,7 +785,10 @@ class CascadeHistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mPdgCode = 0; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; bool mPlot2d = false; @@ -678,6 +797,5 @@ class CascadeHistManager trackhistmanager::TrackHistManager mPosDauManager; trackhistmanager::TrackHistManager mNegDauManager; }; -}; // namespace cascadehistmanager -}; // namespace o2::analysis::femto +} // namespace o2::analysis::femto::cascadehistmanager #endif // PWGCF_FEMTO_CORE_CASCADEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/charmHadronBuilder.h b/PWGCF/Femto/Core/charmHadronBuilder.h new file mode 100644 index 00000000000..feda7ce2d0e --- /dev/null +++ b/PWGCF/Femto/Core/charmHadronBuilder.h @@ -0,0 +1,724 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file charmHadronBuilder.h +/// \brief charm hadron builder +/// \author Igor Ptak, WUT, igor.tomasz.ptak@cern.ch + +#ifndef PWGCF_FEMTO_CORE_CHARMHADRONBUILDER_H_ +#define PWGCF_FEMTO_CORE_CHARMHADRONBUILDER_H_ + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::charmhadronbuilder +{ +// number of ML probability classes (background, prompt, non-prompt) provided by the HF ML selector +constexpr std::size_t NSizeMLScore{3u}; + +// filter applied in the producer task +struct ConfCharmHadronFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CharmHadronFilters"); + // kinematic windows + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 24.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.8f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.8f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // rapidity acceptance (HF convention: y, not eta) + o2::framework::Configurable useYCut{"useYCut", true, "cut on y (true) or eta (false)"}; + o2::framework::Configurable yMin{"yMin", -0.8f, "Minimum rapidity"}; + o2::framework::Configurable yMax{"yMax", 0.8f, "Maximum rapidity"}; + // invariant-mass window + o2::framework::Configurable massMin{"massMin", 1.7f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 2.0f, "Maximum invariant mass"}; + o2::framework::Configurable rejectAmbiguousHypothesis{"rejectAmbiguousHypothesis", false, "Reject candidates selected under both particle and antiparticle hypotheses"}; +}; + +// derived selection bits for D0s +struct ConfD0Bits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("D0Bits"); + o2::framework::Configurable passThrough{"passThrough", false, "If true, all D0s are passed through. Bits for all selections are stored."}; + o2::framework::Configurable> cpaMin{"cpaMin", {0.9f}, "Minimum cosine of pointing angle"}; + o2::framework::Configurable> decayLengthMin{"decayLengthMin", {0.02f}, "Minimum decay length (cm)"}; + o2::framework::Configurable> impactParameterProductMax{"impactParameterProductMax", {0.f}, "Maximum product of prong impact parameters d0*d0 (cm^2)"}; + o2::framework::Configurable> cosThetaStarMax{"cosThetaStarMax", {1.f}, "Maximum |cos(theta*)| of the decay"}; +}; + +// derived selection bits for Lc +struct ConfLcBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("LcBits"); + o2::framework::Configurable passThrough{"passThrough", false, "If true, all Lcs are passed through. Bits for all selections are stored."}; + o2::framework::Configurable> cpaMin{"cpaMin", {0.9f}, "Minimum cosine of pointing angle"}; + o2::framework::Configurable> decayLengthMin{"decayLengthMin", {0.02f}, "Minimum decay length (cm)"}; + o2::framework::Configurable> chi2PcaMax{"chi2PcaMax", {1.f}, "Maximum sum of distances of the secondary vertex to its prongs (cm)"}; + o2::framework::Configurable> impactParameterProngSqSumMin{"impactParameterProngSqSumMin", {0.f}, "Minimum sum of squared prong impact parameters (cm^2)"}; +}; + +// base selection for analysis task for D0s +template +struct ConfD0Selection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kD0, "PDG code (D0)"}; + o2::framework::Configurable sign{"sign", 0, "Particle sign (+1: D0; -1: D0bar; 0: both)"}; + o2::framework::Configurable ptMin{"ptMin", 1.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 3.f, "Maximum pT"}; + // acceptance is applied as a rapidity cut in the builder; the eta/phi windows + // are kept open and exist only to satisfy the partition macro + o2::framework::Configurable etaMin{"etaMin", -0.8f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.8f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // signal region; side-bands remain available in the derived data + o2::framework::Configurable massMin{"massMin", 1.81f, "Minimum invariant mass for D0"}; + o2::framework::Configurable massMax{"massMax", 1.922f, "Maximum invariant mass for D0"}; + o2::framework::Configurable mask{"mask", 0, "Bitmask for D0 selection"}; +}; + +// base selection for analysis task for Lcs +template +struct ConfLcSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kLambdaCPlus, "PDG code (Lc)"}; + o2::framework::Configurable sign{"sign", 0, "Particle sign (+1: Lc; -1: Lcbar; 0: both)"}; + o2::framework::Configurable ptMin{"ptMin", 1.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 3.f, "Maximum pT"}; + // acceptance is applied as a rapidity cut in the builder; the eta/phi windows + // are kept open and exist only to satisfy the partition macro + o2::framework::Configurable etaMin{"etaMin", -0.8f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.8f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // signal region; side-bands remain available in the derived data + o2::framework::Configurable massMin{"massMin", 2.24f, "Minimum invariant mass for Lc"}; + o2::framework::Configurable massMax{"massMax", 2.33f, "Maximum invariant mass for Lc"}; + o2::framework::Configurable mask{"mask", 0, "Bitmask for Lc selection"}; +}; + +constexpr const char PrefixD0Selection1[] = "D0Selection1"; +constexpr const char PrefixD0Selection2[] = "D0Selection2"; +using ConfD0Selection1 = ConfD0Selection; +using ConfD0Selection2 = ConfD0Selection; + +constexpr const char PrefixLcSelection1[] = "LcSelection1"; +constexpr const char PrefixLcSelection2[] = "LcSelection2"; +using ConfLcSelection1 = ConfLcSelection; +using ConfLcSelection2 = ConfLcSelection; + +/// The different selections for charm hadrons. +/// The enum is shared by all species in the family; which bits are actually +/// registered is decided per species in CharmHadronSelection::configure. +enum CharmHadronSels { + // topological selections + kCpaMin, ///< Min. CPA (cosine pointing angle) + kDecayLengthMin, ///< Min. decay length + + // topological selections, 2-prong only (D0) + kImpactParameterProductMax, ///< Max. product of prong impact parameters (d0*d0) + kCosThetaStarMax, ///< Max. |cos(theta*)| of the decay + + // topological selections, 3-prong only (Lc) + kChi2PcaMax, ///< Max. sum of distances of the secondary vertex to its prongs + kImpactParameterProngSqSumMin, ///< Min. sum of squared prong impact parameters + + kCharmHadronSelsMax +}; + +constexpr char D0SelHistName[] = "hD0Selection"; +constexpr char D0barSelHistName[] = "hD0barSelection"; +constexpr char CharmHadronSelsName[] = "Charm hadron selection object"; +constexpr char LcSelHistName[] = "hLcSelection"; +constexpr char LcBarSelHistName[] = "hLcBarSelection"; +const std::unordered_map charmHadronSelectionNames = { + {kCpaMin, "Min. CPA (cosine pointing angle)"}, + {kDecayLengthMin, "Min. decay length"}, + + {kImpactParameterProductMax, "Max. product of prong impact parameters (d0*d0)"}, + {kCosThetaStarMax, "Max. |cos(theta*)| of the decay"}, + + {kChi2PcaMax, "Max. sum of distances of the secondary vertex to its prongs (cm)"}, + {kImpactParameterProngSqSumMin, "Min. sum of squared prong impact parameters (cm^2)"}}; + +/// enum for all charm hadron filters (loose kinematic pre-selection, applied before the bit selections). +enum CharmHadronFilters { + kDecayChannel, + kHypothesis, + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kUseYCut, + kYMin, + kYMax, + kMassMin, + kMassMax, + kRejectAmbiguous, + kCharmHadronFiltersMax +}; + +constexpr char D0FilterHistName[] = "hD0Filters"; +constexpr char D0barFilterHistName[] = "hD0barFilters"; +constexpr char LcFilterHistName[] = "hLcFilters"; +constexpr char LcBarFilterHistName[] = "hLcBarFilters"; +const std::unordered_map charmHadronFilterNames = { + {kDecayChannel, "Decay channel"}, + {kHypothesis, "Mass hypothesis"}, + {kPtMin, "Minimum pT"}, + {kPtMax, "Maximum pT"}, + {kEtaMin, "Minimum eta"}, + {kEtaMax, "Maximum eta"}, + {kPhiMin, "Minimum phi"}, + {kPhiMax, "Maximum phi"}, + {kUseYCut, "Use rapidity cut instead of eta cut"}, + {kYMin, "Minimum rapidity"}, + {kYMax, "Maximum rapidity"}, + {kMassMin, "Minimum invariant mass"}, + {kMassMax, "Maximum invariant mass"}, + {kRejectAmbiguous, "Reject ambiguous mass hypothesis"}}; + +template +constexpr bool isThreeProng() +{ + return modes::isEqual(hadronType, modes::CharmHadron::kLc) || + modes::isEqual(hadronType, modes::CharmHadron::kLcBar); +} + +template +constexpr bool isParticle() +{ + return modes::isEqual(hadronType, modes::CharmHadron::kD0) || + modes::isEqual(hadronType, modes::CharmHadron::kLc); +} + +template +class CharmHadronSelection : public baseselection::BaseSelection +{ + public: + CharmHadronSelection() = default; + ~CharmHadronSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mMassMin = filter.massMin.value; + mMassMax = filter.massMax.value; + mUseYCut = filter.useYCut.value; + mYMin = filter.yMin.value; + mYMax = filter.yMax.value; + mRejectAmbiguousHypothesis = filter.rejectAmbiguousHypothesis.value; + + this->addSelection(kCpaMin, charmHadronSelectionNames.at(kCpaMin), config.cpaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDecayLengthMin, charmHadronSelectionNames.at(kDecayLengthMin), config.decayLengthMin.value, limits::kLowerLimit, true, true, false); + + if constexpr (isThreeProng()) { + this->addSelection(kChi2PcaMax, charmHadronSelectionNames.at(kChi2PcaMax), config.chi2PcaMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kImpactParameterProngSqSumMin, charmHadronSelectionNames.at(kImpactParameterProngSqSumMin), config.impactParameterProngSqSumMin.value, limits::kLowerLimit, true, true, false); + } else { + this->addSelection(kImpactParameterProductMax, charmHadronSelectionNames.at(kImpactParameterProductMax), config.impactParameterProductMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kCosThetaStarMax, charmHadronSelectionNames.at(kCosThetaStarMax), config.cosThetaStarMax.value, limits::kAbsUpperLimit, true, true, false); + } + + this->template setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {charmHadronFilterNames.at(kDecayChannel), 1}, + {charmHadronFilterNames.at(kHypothesis), 1}, + {charmHadronFilterNames.at(kPtMin), mPtMin}, + {charmHadronFilterNames.at(kPtMax), mPtMax}, + {charmHadronFilterNames.at(kEtaMin), mEtaMin}, + {charmHadronFilterNames.at(kEtaMax), mEtaMax}, + {charmHadronFilterNames.at(kPhiMin), mPhiMin}, + {charmHadronFilterNames.at(kPhiMax), mPhiMax}, + {charmHadronFilterNames.at(kUseYCut), static_cast(mUseYCut)}, + {charmHadronFilterNames.at(kYMin), mYMin}, + {charmHadronFilterNames.at(kYMax), mYMax}, + {charmHadronFilterNames.at(kMassMin), mMassMin}, + {charmHadronFilterNames.at(kMassMax), mMassMax}, + {charmHadronFilterNames.at(kRejectAmbiguous), static_cast(mRejectAmbiguousHypothesis)}, + }); + } + + template + void applySelections(T1 const& candidate) + { + this->reset(); + this->evaluateObservable(kCpaMin, candidate.cpa()); + this->evaluateObservable(kDecayLengthMin, candidate.decayLength()); + + if constexpr (isThreeProng()) { + this->evaluateObservable(kChi2PcaMax, candidate.chi2PCA()); + this->evaluateObservable(kImpactParameterProngSqSumMin, candidate.impactParameterProngSqSum()); + } else { + this->evaluateObservable(kImpactParameterProductMax, candidate.impactParameter0() * candidate.impactParameter1()); + this->evaluateObservable(kCosThetaStarMax, HfHelper::cosThetaStarD0(candidate)); + } + this->template assembleBitmask(); + } + + template + bool checkFilters(const T& candidate) const + { + bool pass = true; + bool p = false; + + // decay channel + if constexpr (isThreeProng()) { + p = (candidate.hfflag() & (1 << o2::aod::hf_cand_3prong::DecayType::LcToPKPi)) != 0; + } else { + p = (candidate.hfflag() & (1 << o2::aod::hf_cand_2prong::DecayType::D0ToPiK)) != 0; + } + this->template fillFilter(kDecayChannel, p); + pass &= p; + + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { + p = candidate.isSelD0(); + } else if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) { + p = candidate.isSelD0bar(); + } else { // kLc / kLcBar + p = candidate.isSelLcToPKPi() || candidate.isSelLcToPiKP(); + } + this->template fillFilter(kHypothesis, p); + pass &= p; + + bool competing = false; + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { + competing = candidate.isSelD0bar(); + } else if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) { + competing = candidate.isSelD0(); + } else { // Lc / Lcbar: ambiguous means both mass hypotheses passed + competing = candidate.isSelLcToPKPi() && candidate.isSelLcToPiKP(); + } + p = !mRejectAmbiguousHypothesis || !competing; + this->template fillFilter(kRejectAmbiguous, p); + pass &= p; + + p = candidate.pt() > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = candidate.pt() < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = candidate.eta() > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = candidate.eta() < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = candidate.phi() > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = candidate.phi() < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + // rapidity + if (mUseYCut) { + float y = 0.f; + if constexpr (isThreeProng()) { + y = HfHelper::yLc(candidate); + } else { + y = HfHelper::yD0(candidate); + } + p = y > mYMin; + this->template fillFilter(kYMin, p); + pass &= p; + + p = y < mYMax; + this->template fillFilter(kYMax, p); + pass &= p; + } + + this->template fillFilterSummary(pass); + return this->isPassThrough() || pass; + } + + [[nodiscard]] float getMassMin() const + { + return mMassMin; + } + + [[nodiscard]] float getMassMax() const + { + return mMassMax; + } + + private: + float mPtMin = 0.f; + float mPtMax = 24.f; + float mEtaMin = -0.8f; + float mEtaMax = 0.8f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + float mMassMin = 1.7f; + float mMassMax = 2.0f; + bool mUseYCut = true; + float mYMin = -0.8f; + float mYMax = 0.8f; + bool mRejectAmbiguousHypothesis = false; +}; + +// tables produced by the charm hadron builder: kinematics, bitmask and QA +struct CharmHadronBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedD0s; + o2::framework::Produces producedD0Masks; + o2::framework::Produces producedD0Extras; + o2::framework::Produces producedLcs; + o2::framework::Produces producedLcMasks; + o2::framework::Produces producedLcExtras; +}; + +// per-table produce switches (-1: auto = produce only if a downstream device subscribes; 0 off; 1 on) +struct ConfCharmHadronTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("CharmHadronTables"); + o2::framework::Configurable produceD0s{"produceD0s", -1, "Produce D0s (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceD0Masks{"produceD0Masks", -1, "Produce D0Masks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceD0Extras{"produceD0Extras", -1, "Produce D0Extras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLcs{"produceLcs", -1, "Produce Lcs (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLcMasks{"produceLcMasks", -1, "Produce LcMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLcExtras{"produceLcExtras", -1, "Produce LcExtras (-1: auto; 0 off; 1 on)"}; +}; + +template +class CharmHadronBuilder +{ + public: + CharmHadronBuilder() = default; + ~CharmHadronBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + { + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0) || modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) { + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { + LOG(info) << "Initialize femto D0 builder..."; + } + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) { + LOG(info) << "Initialize femto D0bar builder..."; + } + mProduceD0s = utils::enableTable("FD0s_001", table.produceD0s.value, initContext); + mProduceD0Masks = utils::enableTable("FD0Masks_001", table.produceD0Masks.value, initContext); + mProduceD0Extras = utils::enableTable("FD0Extras_001", table.produceD0Extras.value, initContext); + } + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kLc) || modes::isEqual(hadronType, modes::CharmHadron::kLcBar)) { + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kLc)) { + LOG(info) << "Initialize femto Lc builder..."; + } + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kLcBar)) { + LOG(info) << "Initialize femto Lcbar builder..."; + } + mProduceLcs = utils::enableTable("FLcs_001", table.produceLcs.value, initContext); + mProduceLcMasks = utils::enableTable("FLcMasks_001", table.produceLcMasks.value, initContext); + mProduceLcExtras = utils::enableTable("FLcExtras_001", table.produceLcExtras.value, initContext); + } + if (mProduceD0s || mProduceD0Masks || mProduceD0Extras || + mProduceLcs || mProduceLcMasks || mProduceLcExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mCharmHadronSelection.configure(registry, config, filter); + mCharmHadronSelection.printSelections(CharmHadronSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillD0Tables(T1& collisionProducts, T2& charmHadronProducts, T3 const& candidate, + float signedPt, float mass, int64_t posDauIndex, int64_t negDauIndex) + { + if (mProduceD0s) { + charmHadronProducts.producedD0s(collisionProducts.producedCollision.lastIndex(), + signedPt, + candidate.eta(), + candidate.phi(), + mass, + posDauIndex, + negDauIndex); + } + if (mProduceD0Masks) { + charmHadronProducts.producedD0Masks(mCharmHadronSelection.getBitmask()); + } + if (mProduceD0Extras) { + charmHadronProducts.producedD0Extras( + candidate.cpa(), + candidate.cpaXY(), + candidate.decayLength(), + candidate.decayLengthXY(), + candidate.impactParameter0() * candidate.impactParameter1(), + HfHelper::cosThetaStarD0(candidate), + candidate.mlProbD0().size() < NSizeMLScore ? -1.f : candidate.mlProbD0()[0], + candidate.mlProbD0().size() < NSizeMLScore ? -1.f : candidate.mlProbD0()[1], + candidate.mlProbD0().size() < NSizeMLScore ? -1.f : candidate.mlProbD0()[2], + candidate.mlProbD0bar().size() < NSizeMLScore ? -1.f : candidate.mlProbD0bar()[0], + candidate.mlProbD0bar().size() < NSizeMLScore ? -1.f : candidate.mlProbD0bar()[1], + candidate.mlProbD0bar().size() < NSizeMLScore ? -1.f : candidate.mlProbD0bar()[2], + static_cast(candidate.isSelD0()), + static_cast(candidate.isSelD0bar())); + } + } + + template + void fillLcTables(T1& collisionProducts, T2& charmHadronProducts, T3 const& candidate, + float signedPt, float mass, float massCompeting, + int64_t protonDauIndex, int64_t kaonDauIndex, int64_t pionDauIndex) + { + if (mProduceLcs) { + charmHadronProducts.producedLcs(collisionProducts.producedCollision.lastIndex(), + signedPt, + candidate.eta(), + candidate.phi(), + mass, + protonDauIndex, + kaonDauIndex, + pionDauIndex); + } + if (mProduceLcMasks) { + charmHadronProducts.producedLcMasks(mCharmHadronSelection.getBitmask()); + } + if (mProduceLcExtras) { + charmHadronProducts.producedLcExtras( + candidate.cpa(), + candidate.cpaXY(), + candidate.decayLength(), + candidate.decayLengthXY(), + candidate.chi2PCA(), + candidate.impactParameterProngSqSum(), + candidate.impactParameter0(), + candidate.impactParameter1(), + candidate.impactParameter2(), + massCompeting, + candidate.mlProbLcToPKPi().size() < NSizeMLScore ? -1.f : candidate.mlProbLcToPKPi()[0], + candidate.mlProbLcToPKPi().size() < NSizeMLScore ? -1.f : candidate.mlProbLcToPKPi()[1], + candidate.mlProbLcToPKPi().size() < NSizeMLScore ? -1.f : candidate.mlProbLcToPKPi()[2], + candidate.mlProbLcToPiKP().size() < NSizeMLScore ? -1.f : candidate.mlProbLcToPiKP()[0], + candidate.mlProbLcToPiKP().size() < NSizeMLScore ? -1.f : candidate.mlProbLcToPiKP()[1], + candidate.mlProbLcToPiKP().size() < NSizeMLScore ? -1.f : candidate.mlProbLcToPiKP()[2], + static_cast(candidate.isSelLcToPKPi()), + static_cast(candidate.isSelLcToPiKP())); + } + } + + /// pass-through accepts every candidate once, under the pKPi prong mapping + template + bool acceptsHypothesis(T const& candidate) const + { + if (mCharmHadronSelection.isPassThrough()) { + return isPKPi; + } + return isPKPi ? candidate.isSelLcToPKPi() : candidate.isSelLcToPiKP(); + } + + /// a candidate passing both hypotheses is stored twice, once per hypothesis + template + void fillLcHypothesis(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, + T5& charmHadronProducts, T6 const& candidate, T7& trackBuilder, + T8 const& prong0, T8 const& prong1, T8 const& prong2) + { + if (!this->acceptsHypothesis(candidate)) { + return; + } + + // remap the prongs onto the accepted hypothesis so that the proton is always first + auto const& protonProng = isPKPi ? prong0 : prong2; + auto const& pionProng = isPKPi ? prong2 : prong0; + float const mass = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + float const massCompeting = isPKPi ? HfHelper::invMassLcToPiKP(candidate) : HfHelper::invMassLcToPKPi(candidate); + + collisionBuilder.template fillCollision(collisionProducts, col); + int64_t const protonDauIndex = trackBuilder.template getDaughterIndex(protonProng, trackProducts, collisionBuilder); + int64_t const kaonDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, collisionBuilder); + int64_t const pionDauIndex = trackBuilder.template getDaughterIndex(pionProng, trackProducts, collisionBuilder); + + float const signedPt = isParticle() ? candidate.pt() : -candidate.pt(); + this->fillLcTables(collisionProducts, charmHadronProducts, candidate, signedPt, mass, massCompeting, protonDauIndex, kaonDauIndex, pionDauIndex); + } + + /// Monte Carlo counterpart of fillLcHypothesis. The label is written inside this method, + /// so FLcs and FLcLabels stay in lockstep also for candidates passing both hypotheses. + template + void fillMcLcHypothesis(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, + T6& charmHadronProducts, T7 const& candidate, T8 const& tracks, T9& trackBuilder, + T10 const& mcParticles, T11& mcBuilder, T12& mcProducts, + T13 const& prong0, T13 const& prong1, T13 const& prong2) + { + if (!this->acceptsHypothesis(candidate)) { + return; + } + + // remap the prongs onto the accepted hypothesis so that the proton is always first + auto const& protonProng = isPKPi ? prong0 : prong2; + auto const& pionProng = isPKPi ? prong2 : prong0; + float const mass = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + float const massCompeting = isPKPi ? HfHelper::invMassLcToPiKP(candidate) : HfHelper::invMassLcToPKPi(candidate); + + collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); + + int64_t const protonDauIndex = trackBuilder.template getDaughterIndex(protonProng, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + int64_t const kaonDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + int64_t const pionDauIndex = trackBuilder.template getDaughterIndex(pionProng, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + + float const signedPt = isParticle() ? candidate.pt() : -candidate.pt(); + this->fillLcTables(collisionProducts, charmHadronProducts, candidate, signedPt, mass, massCompeting, protonDauIndex, kaonDauIndex, pionDauIndex); + mcBuilder.template fillMcLcWithLabel(candidate, tracks, mcParticles, mcCols, mcProducts); + } + + template + void fillCharmHadrons(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, + T5& charmHadronProducts, T6 const& candidates, T7 const& /*tracks*/, T8& trackBuilder) + { + if (!mFillAnyTable) { + return; + } + for (const auto& candidate : candidates) { + if (!mCharmHadronSelection.checkFilters(candidate)) { + continue; + } + mCharmHadronSelection.applySelections(candidate); + if (!mCharmHadronSelection.passesAllRequiredSelections()) { + continue; + } + + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0) || modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) { + auto prong0 = candidate.template prong0_as(); + auto prong1 = candidate.template prong1_as(); + + collisionBuilder.template fillCollision(collisionProducts, col); + + int64_t posDauIndex = trackBuilder.template getDaughterIndex(prong0, trackProducts, collisionBuilder); + int64_t negDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, collisionBuilder); + + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), HfHelper::invMassD0ToPiK(candidate), posDauIndex, negDauIndex); + } else { + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), HfHelper::invMassD0barToKPi(candidate), posDauIndex, negDauIndex); + } + } + + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kLc) || modes::isEqual(hadronType, modes::CharmHadron::kLcBar)) { + auto prong0 = candidate.template prong0_as(); + auto prong1 = candidate.template prong1_as(); + auto prong2 = candidate.template prong2_as(); + + // the charge of prong 0 is the charge of the Lc, so the species is decided here + if ((prong0.sign() > 0) != isParticle()) { + continue; + } + + this->fillLcHypothesis(col, collisionBuilder, collisionProducts, trackProducts, charmHadronProducts, candidate, trackBuilder, prong0, prong1, prong2); + this->fillLcHypothesis(col, collisionBuilder, collisionProducts, trackProducts, charmHadronProducts, candidate, trackBuilder, prong0, prong1, prong2); + } + } + } + + template + void fillMcCharmHadrons(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, + T6& charmHadronProducts, T7 const& candidates, T8 const& tracks, T9& trackBuilder, T10 const& mcParticles, T11& mcBuilder, T12& mcProducts) + { + if (!mFillAnyTable) { + return; + } + for (const auto& candidate : candidates) { + if (!mCharmHadronSelection.checkFilters(candidate)) { + continue; + } + + mCharmHadronSelection.applySelections(candidate); + if (!mCharmHadronSelection.passesAllRequiredSelections()) { + continue; + } + + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0) || modes::isEqual(hadronType, modes::CharmHadron::kD0Bar)) { + auto prong0 = candidate.template prong0_as(); + auto prong1 = candidate.template prong1_as(); + + collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); + + int64_t posDauIndex = trackBuilder.template getDaughterIndex(prong0, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + int64_t negDauIndex = trackBuilder.template getDaughterIndex(prong1, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kD0)) { + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, candidate.pt(), HfHelper::invMassD0ToPiK(candidate), posDauIndex, negDauIndex); + } else { + this->fillD0Tables(collisionProducts, charmHadronProducts, candidate, -candidate.pt(), HfHelper::invMassD0barToKPi(candidate), posDauIndex, negDauIndex); + } + mcBuilder.template fillMcD0WithLabel(candidate, tracks, mcParticles, mcCols, mcProducts); + } + + if constexpr (modes::isEqual(hadronType, modes::CharmHadron::kLc) || modes::isEqual(hadronType, modes::CharmHadron::kLcBar)) { + auto prong0 = candidate.template prong0_as(); + auto prong1 = candidate.template prong1_as(); + auto prong2 = candidate.template prong2_as(); + + // the charge of prong 0 is the charge of the Lc, so the species is decided here + if ((prong0.sign() > 0) != isParticle()) { + continue; + } + + this->fillMcLcHypothesis(col, collisionBuilder, collisionProducts, mcCols, trackProducts, charmHadronProducts, candidate, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts, prong0, prong1, prong2); + this->fillMcLcHypothesis(col, collisionBuilder, collisionProducts, mcCols, trackProducts, charmHadronProducts, candidate, tracks, trackBuilder, mcParticles, mcBuilder, mcProducts, prong0, prong1, prong2); + } + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mCharmHadronSelection.isPassThrough(); } + + private: + CharmHadronSelection mCharmHadronSelection; + + bool mProduceD0s = false; + bool mProduceD0Masks = false; + bool mProduceD0Extras = false; + bool mProduceLcs = false; + bool mProduceLcMasks = false; + bool mProduceLcExtras = false; + bool mFillAnyTable = false; +}; +} // namespace o2::analysis::femto::charmhadronbuilder + +#endif // PWGCF_FEMTO_CORE_CHARMHADRONBUILDER_H_ diff --git a/PWGCF/Femto/Core/charmHadronHistManager.h b/PWGCF/Femto/Core/charmHadronHistManager.h new file mode 100644 index 00000000000..a7f78047ac2 --- /dev/null +++ b/PWGCF/Femto/Core/charmHadronHistManager.h @@ -0,0 +1,709 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file charmHadronHistManager.h +/// \brief histogram manager for charm hadron histograms +/// \author Igor Ptak, WUT, igor.tomasz.ptak@cern.ch + +#ifndef PWGCF_FEMTO_CORE_CHARMHADRONHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_CHARMHADRONHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/charmHadronBuilder.h" +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/trackHistManager.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::charmhadronhistmanager +{ +enum CharmHadronHist { + kPt, + kEta, + kPhi, + kMass, + kSign, + kPtVsMass, + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + + // mass under each hypothesis + kMassD0, + kMassD0bar, + kMassLcToPKPi, + kMassLcToPiKP, + + // ml scores + kMlBkg, + kMlPrompt, + kMlNonPrompt, + + // topology, common + kCpa, + kCpaXY, + kDecayLength, + kDecayLengthXY, + + // topology, 2-prong only (D0) + kImpactParameterProduct, + kCosThetaStar, + + // topology, 3-prong only (Lc) + kChi2Pca, + kImpactParameterProngSqSum, + + // mc + kOrigin, + kPdg, + kPdgMother, + kTruePtVsPt, + kTrueEtaVsEta, + kTruePhiVsPhi, + kPtVsOrigin, + + kCharmHadronHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHARMHADRON_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ + o2::framework::ConfigurableAxis charmHadrons{"charmHadrons", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed charm hadrons"}; \ + o2::framework::ConfigurableAxis pt2d{"pt2d", {{240, 0, 6}}, "Pt for 2D QA"}; \ + o2::framework::ConfigurableAxis eta2d{"eta2d", {{200, -1.5, 1.5}}, "Eta for 2D QA"}; \ + o2::framework::ConfigurableAxis phi2d{"phi2d", {{200, 0, 1.f * o2::constants::math::TwoPI}}, "Phi for 2D QA"}; + +template +struct ConfCharmHadronBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + CHARMHADRON_DEFAULT_BINNING(1.7, 2.0) +}; + +template +struct ConfLcBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + CHARMHADRON_DEFAULT_BINNING(2.15, 2.42) +}; + +#undef CHARMHADRON_DEFAULT_BINNING + +constexpr const char PrefixD0Binning1[] = "D0Binning1"; +constexpr const char PrefixD0Binning2[] = "D0Binning2"; +using ConfD0Binning1 = ConfCharmHadronBinning; +using ConfD0Binning2 = ConfCharmHadronBinning; + +constexpr const char PrefixLcBinning1[] = "LcBinning1"; +using ConfLcBinning1 = ConfLcBinning; + +template +struct ConfCharmHadronQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable plotTopology{"plotTopology", true, "Generate topological QA plots (cpa, decayLength, impactParameterProduct, cosThetaStar)"}; + o2::framework::ConfigurableAxis massD0{"massD0", {{200, 1.7, 2.0}}, "Mass of candidates selected as D0"}; + o2::framework::ConfigurableAxis massD0bar{"massD0bar", {{200, 1.7, 2.0}}, "Mass of candidates selected as D0bar"}; + o2::framework::ConfigurableAxis mlScore{"mlScore", {{100, 0.f, 1.f}}, "BDT ML score (bkg/prompt/non-prompt)"}; + o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.95f, 1.f}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis decayLength{"decayLength", {{200, 0.f, 0.2f}}, "Decay length (cm)"}; + o2::framework::ConfigurableAxis impactParameterProduct{"impactParameterProduct", {{200, -0.001f, 0.001f}}, "Product of daughter impact parameters (cm^2)"}; + o2::framework::ConfigurableAxis cosThetaStar{"cosThetaStar", {{100, -1.f, 1.f}}, "Cosine of decay angle in D0 rest frame"}; +}; + +constexpr const char PrefixD0QaBinning1[] = "D0QaBinning1"; +using ConfD0QaBinning1 = ConfCharmHadronQaBinning; + +template +struct ConfLcQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable plotTopology{"plotTopology", true, "Generate topological QA plots (cpa, decayLength, chi2Pca, impactParameterProngSqSum)"}; + o2::framework::ConfigurableAxis massLcToPKPi{"massLcToPKPi", {{200, 2.15, 2.42}}, "Mass under the p K pi hypothesis"}; + o2::framework::ConfigurableAxis massLcToPiKP{"massLcToPiKP", {{200, 2.15, 2.42}}, "Mass under the pi K p hypothesis"}; + o2::framework::ConfigurableAxis mlScore{"mlScore", {{100, 0.f, 1.f}}, "BDT ML score (bkg/prompt/non-prompt)"}; + o2::framework::ConfigurableAxis cpa{"cpa", {{100, 0.95f, 1.f}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis decayLength{"decayLength", {{200, 0.f, 0.2f}}, "Decay length (cm)"}; + o2::framework::ConfigurableAxis chi2Pca{"chi2Pca", {{200, 0.f, 0.1f}}, "Sum of distances of the secondary vertex to its prongs (cm)"}; + o2::framework::ConfigurableAxis impactParameterProngSqSum{"impactParameterProngSqSum", {{200, 0.f, 0.01f}}, "Sum of squared prong impact parameters (cm^2)"}; +}; + +constexpr const char PrefixLcQaBinning1[] = "LcQaBinning1"; +using ConfLcQaBinning1 = ConfLcQaBinning; + +// must be in sync with enum CharmHadronHist +// the enum gives the correct index in the array +constexpr std::array, kCharmHadronHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, + {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> D0bar, +1 -> D0); sign; Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "Transverse momentum vs invariant mass; p_{T} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}); #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}); #varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi; #eta"}, + {kMassD0, o2::framework::HistType::kTH1F, "hMassD0", "Invariant mass of D^{0} candidates; m_{Inv} (GeV/#it{c}^{2}); Entries"}, + {kMassD0bar, o2::framework::HistType::kTH1F, "hMassD0bar", "Invariant mass of #bar{D}^{0} candidates; m_{Inv} (GeV/#it{c}^{2}); Entries"}, + {kMassLcToPKPi, o2::framework::HistType::kTH1F, "hMassLcToPKPi", "Invariant mass under the p K #pi hypothesis; m_{Inv} (GeV/#it{c}^{2}); Entries"}, + {kMassLcToPiKP, o2::framework::HistType::kTH1F, "hMassLcToPiKP", "Invariant mass under the #pi K p hypothesis; m_{Inv} (GeV/#it{c}^{2}); Entries"}, + {kMlBkg, o2::framework::HistType::kTH1F, "hMlBkg", "BDT background score; ML score (bkg); Entries"}, + {kMlPrompt, o2::framework::HistType::kTH1F, "hMlPrompt", "BDT prompt score; ML score (prompt); Entries"}, + {kMlNonPrompt, o2::framework::HistType::kTH1F, "hMlNonPrompt", "BDT non-prompt score; ML score (non-prompt); Entries"}, + {kCpa, o2::framework::HistType::kTH1F, "hCpa", "Cosine of pointing angle; cos(#alpha); Entries"}, + {kCpaXY, o2::framework::HistType::kTH1F, "hCpaXY", "Cosine of pointing angle (XY); cos(#alpha)_{XY}; Entries"}, + {kDecayLength, o2::framework::HistType::kTH1F, "hDecayLength", "Decay length; L (cm); Entries"}, + {kDecayLengthXY, o2::framework::HistType::kTH1F, "hDecayLengthXY", "Decay length (XY); L_{XY} (cm); Entries"}, + {kImpactParameterProduct, o2::framework::HistType::kTH1F, "hImpactParameterProduct", "Product of daughter impact parameters; d_{0}^{K} #times d_{0}^{#pi} (cm^{2}); Entries"}, + {kCosThetaStar, o2::framework::HistType::kTH1F, "hCosThetaStar", "Cosine of decay angle in D0 rest frame; cos(#theta*); Entries"}, + {kChi2Pca, o2::framework::HistType::kTH1F, "hChi2Pca", "Sum of distances of the secondary vertex to its prongs; #Sigma d (cm); Entries"}, + {kImpactParameterProngSqSum, o2::framework::HistType::kTH1F, "hImpactParameterProngSqSum", "Sum of squared prong impact parameters; #Sigma d_{0}^{2} (cm^{2}); Entries"}, + {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "MC origin (prompt / non-prompt); origin; Entries"}, + {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG code of matched generated particle; PDG code; Entries"}, + {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG code of the mother; PDG code; Entries"}, + {kTruePtVsPt, o2::framework::HistType::kTH2F, "hTruePtVsPt", "True vs reconstructed p_{T}; p_{T,true} (GeV/#it{c}); p_{T} (GeV/#it{c})"}, + {kTrueEtaVsEta, o2::framework::HistType::kTH2F, "hTrueEtaVsEta", "True vs reconstructed #eta; #eta_{true}; #eta"}, + {kTruePhiVsPhi, o2::framework::HistType::kTH2F, "hTruePhiVsPhi", "True vs reconstructed #varphi; #varphi_{true}; #varphi"}, + {kPtVsOrigin, o2::framework::HistType::kTH2F, "hPtVsOrigin", "p_{T} vs MC origin; p_{T} (GeV/#it{c}); origin"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHARMHADRON_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, \ + {kPtVsEta, {(conf).pt2d, (conf).eta2d}}, \ + {kPtVsPhi, {(conf).pt2d, (conf).phi2d}}, \ + {kPhiVsEta, {(conf).phi2d, (conf).eta2d}}, + +template +auto makeCharmHadronHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + CHARMHADRON_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHARMHADRON_HIST_MC_MAP(conf) \ + {kPdg, {(conf).charmHadrons}}, \ + {kPdgMother, {(conf).charmHadrons}}, \ + {kTruePtVsPt, {(conf).pt, (conf).pt}}, \ + {kTrueEtaVsEta, {(conf).eta, (conf).eta}}, \ + {kTruePhiVsPhi, {(conf).phi, (conf).phi}}, \ + {kPtVsOrigin, {(conf).pt2d}}, + +template +auto makeCharmHadronMcHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + CHARMHADRON_HIST_ANALYSIS_MAP(confBinningAnalysis) + CHARMHADRON_HIST_MC_MAP(confBinningAnalysis)}; +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHARMHADRON_HIST_QA_MAP(conf) \ + {kMassD0, {(conf).massD0}}, \ + {kMassD0bar, {(conf).massD0bar}}, \ + {kMlBkg, {(conf).mlScore}}, \ + {kMlPrompt, {(conf).mlScore}}, \ + {kMlNonPrompt, {(conf).mlScore}}, \ + {kCpa, {(conf).cpa}}, \ + {kCpaXY, {(conf).cpa}}, \ + {kDecayLength, {(conf).decayLength}}, \ + {kDecayLengthXY, {(conf).decayLength}}, \ + {kImpactParameterProduct, {(conf).impactParameterProduct}}, \ + {kCosThetaStar, {(conf).cosThetaStar}}, + +template +auto makeD0QaHistSpecMap(const T& confBinningQa) +{ + return std::map>{ + CHARMHADRON_HIST_QA_MAP(confBinningQa)}; +} + +template +auto makeD0McQaHistSpecMap(const T1& confBinningAnalysis, const T2& confBinningQa) +{ + return std::map>{ + CHARMHADRON_HIST_ANALYSIS_MAP(confBinningAnalysis) + CHARMHADRON_HIST_QA_MAP(confBinningQa) + CHARMHADRON_HIST_MC_MAP(confBinningAnalysis)}; +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LC_HIST_QA_MAP(conf) \ + {kMassLcToPKPi, {(conf).massLcToPKPi}}, \ + {kMassLcToPiKP, {(conf).massLcToPiKP}}, \ + {kMlBkg, {(conf).mlScore}}, \ + {kMlPrompt, {(conf).mlScore}}, \ + {kMlNonPrompt, {(conf).mlScore}}, \ + {kCpa, {(conf).cpa}}, \ + {kCpaXY, {(conf).cpa}}, \ + {kDecayLength, {(conf).decayLength}}, \ + {kDecayLengthXY, {(conf).decayLength}}, \ + {kChi2Pca, {(conf).chi2Pca}}, \ + {kImpactParameterProngSqSum, {(conf).impactParameterProngSqSum}}, + +template +auto makeLcQaHistSpecMap(const T& confBinningQa) +{ + return std::map>{ + LC_HIST_QA_MAP(confBinningQa)}; +} + +template +auto makeLcMcQaHistSpecMap(const T1& confBinningAnalysis, const T2& confBinningQa) +{ + return std::map>{ + CHARMHADRON_HIST_ANALYSIS_MAP(confBinningAnalysis) + LC_HIST_QA_MAP(confBinningQa) + CHARMHADRON_HIST_MC_MAP(confBinningAnalysis)}; +} + +#undef CHARMHADRON_HIST_ANALYSIS_MAP +#undef CHARMHADRON_HIST_MC_MAP +#undef CHARMHADRON_HIST_QA_MAP +#undef LC_HIST_QA_MAP + +// prefixes for the output directories in the histogram registry +// same-event / mixed-event siblings: single-candidate histograms for a given slot, split by +// whether the candidate came from a same-event or mixed-event pair +constexpr char PrefixD01Se[] = "D01/SE/"; +constexpr char PrefixD02Se[] = "D02/SE/"; +constexpr char PrefixD01Me[] = "D01/ME/"; +constexpr char PrefixD02Me[] = "D02/ME/"; +constexpr char PrefixD0Qa[] = "D0QA/"; +constexpr char PrefixLc1Se[] = "Lc1/SE/"; +constexpr char PrefixLc1Me[] = "Lc1/ME/"; +constexpr char PrefixLcQa[] = "LcQA/"; +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view McDir = "MC/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class CharmHadronHistManager +/// \brief Class for histogramming charm hadron properties +template +class CharmHadronHistManager +{ + public: + CharmHadronHistManager() = default; + ~CharmHadronHistManager() = default; + + // init for analysis, two-prong candidates + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& CharmHadronSpecs, + T const& ConfCharmHadronSelection, + std::map> const& PosDauSpecs, + std::map> const& NegDauSpecs) + { + static_assert(!charmhadronbuilder::isThreeProng(), "Use the three-prong init for 3-prong candidates"); + mHistogramRegistry = registry; + mPdgCode = std::abs(ConfCharmHadronSelection.pdgCodeAbs.value); + + auto prongPdgCodes = this->resolveProngPdgCodes(ConfCharmHadronSelection.sign.value); + + mProng0Manager.template init(registry, PosDauSpecs, AbsCharge, SignPlus, prongPdgCodes[0]); + mProng1Manager.template init(registry, NegDauSpecs, AbsCharge, SignMinus, prongPdgCodes[1]); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(CharmHadronSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + this->initMc(CharmHadronSpecs); + } + } + + // init for analysis, three-prong candidates + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& CharmHadronSpecs, + T const& ConfCharmHadronSelection, + std::map> const& ProtonDauSpecs, + std::map> const& KaonDauSpecs, + std::map> const& PionDauSpecs) + { + static_assert(charmhadronbuilder::isThreeProng(), "Use the two-prong init for 2-prong candidates"); + mHistogramRegistry = registry; + mPdgCode = std::abs(ConfCharmHadronSelection.pdgCodeAbs.value); + + auto prongPdgCodes = this->resolveProngPdgCodes(ConfCharmHadronSelection.sign.value); + + constexpr int ProtonSign = charmhadronbuilder::isParticle() ? SignPlus : SignMinus; + constexpr int KaonSign = -ProtonSign; + mProng0Manager.template init(registry, ProtonDauSpecs, AbsCharge, ProtonSign, prongPdgCodes[0]); + mProng1Manager.template init(registry, KaonDauSpecs, AbsCharge, KaonSign, prongPdgCodes[1]); + mProng2Manager.template init(registry, PionDauSpecs, AbsCharge, ProtonSign, prongPdgCodes[2]); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(CharmHadronSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + this->initMc(CharmHadronSpecs); + } + } + + // init for analysis and qa, two-prong candidates + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& CharmHadronSpecs, + std::map> const& CharmHadronQaSpecs, + T1 const& ConfCharmHadronSelection, + T2 const& ConfCharmHadronQaBinning, + std::map> const& PosDauSpecs, + T3 const& ConfPosDauBinningQa, + std::map> const& NegDauSpecs, + T4 const& ConfNegDauBinningQa) + { + static_assert(!charmhadronbuilder::isThreeProng(), "Use the three-prong init for 3-prong candidates"); + mHistogramRegistry = registry; + mPdgCode = std::abs(ConfCharmHadronSelection.pdgCodeAbs.value); + this->enableOptionalHistograms(ConfCharmHadronQaBinning); + + auto prongPdgCodes = this->resolveProngPdgCodes(ConfCharmHadronSelection.sign.value); + + mProng0Manager.template init(registry, PosDauSpecs, AbsCharge, SignPlus, prongPdgCodes[0], ConfPosDauBinningQa); + mProng1Manager.template init(registry, NegDauSpecs, AbsCharge, SignMinus, prongPdgCodes[1], ConfNegDauBinningQa); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(CharmHadronSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(CharmHadronQaSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + this->initMc(CharmHadronSpecs); + } + } + + // init for analysis and qa, three-prong candidates + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& CharmHadronSpecs, + std::map> const& CharmHadronQaSpecs, + T1 const& ConfCharmHadronSelection, + T2 const& ConfCharmHadronQaBinning, + std::map> const& ProtonDauSpecs, + T3 const& ConfProtonDauBinningQa, + std::map> const& KaonDauSpecs, + T4 const& ConfKaonDauBinningQa, + std::map> const& PionDauSpecs, + T5 const& ConfPionDauBinningQa) + { + static_assert(charmhadronbuilder::isThreeProng(), "Use the two-prong init for 2-prong candidates"); + mHistogramRegistry = registry; + mPdgCode = std::abs(ConfCharmHadronSelection.pdgCodeAbs.value); + this->enableOptionalHistograms(ConfCharmHadronQaBinning); + + auto prongPdgCodes = this->resolveProngPdgCodes(ConfCharmHadronSelection.sign.value); + + constexpr int ProtonSign = charmhadronbuilder::isParticle() ? SignPlus : SignMinus; + constexpr int KaonSign = -ProtonSign; + mProng0Manager.template init(registry, ProtonDauSpecs, AbsCharge, ProtonSign, prongPdgCodes[0], ConfProtonDauBinningQa); + mProng1Manager.template init(registry, KaonDauSpecs, AbsCharge, KaonSign, prongPdgCodes[1], ConfKaonDauBinningQa); + mProng2Manager.template init(registry, PionDauSpecs, AbsCharge, ProtonSign, prongPdgCodes[2], ConfPionDauBinningQa); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(CharmHadronSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(CharmHadronQaSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + this->initMc(CharmHadronSpecs); + } + } + + template + void fill(T1 const& charmHadronCandidate, T2 const& tracks) + { + if constexpr (charmhadronbuilder::isThreeProng()) { + auto prong0 = tracks.rawIteratorAt(charmHadronCandidate.prong0DauId() - tracks.offset()); + mProng0Manager.template fill(prong0, tracks); + auto prong1 = tracks.rawIteratorAt(charmHadronCandidate.prong1DauId() - tracks.offset()); + mProng1Manager.template fill(prong1, tracks); + auto prong2 = tracks.rawIteratorAt(charmHadronCandidate.prong2DauId() - tracks.offset()); + mProng2Manager.template fill(prong2, tracks); + } else { + auto prong0 = tracks.rawIteratorAt(charmHadronCandidate.posDauId() - tracks.offset()); + mProng0Manager.template fill(prong0, tracks); + auto prong1 = tracks.rawIteratorAt(charmHadronCandidate.negDauId() - tracks.offset()); + mProng1Manager.template fill(prong1, tracks); + } + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->fillAnalysis(charmHadronCandidate); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->fillQa(charmHadronCandidate); + } + } + + template + void fill(T1 const& charmHadronCandidate, T2 const& tracks, T3 const& col, T4 const& mcParticles, T5 const& mcMothers, T6 const& mcPartonicMothers) + { + if constexpr (charmhadronbuilder::isThreeProng()) { + auto prong0 = tracks.rawIteratorAt(charmHadronCandidate.prong0DauId() - tracks.offset()); + mProng0Manager.template fill(prong0, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + auto prong1 = tracks.rawIteratorAt(charmHadronCandidate.prong1DauId() - tracks.offset()); + mProng1Manager.template fill(prong1, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + auto prong2 = tracks.rawIteratorAt(charmHadronCandidate.prong2DauId() - tracks.offset()); + mProng2Manager.template fill(prong2, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + } else { + auto prong0 = tracks.rawIteratorAt(charmHadronCandidate.posDauId() - tracks.offset()); + mProng0Manager.template fill(prong0, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + auto prong1 = tracks.rawIteratorAt(charmHadronCandidate.negDauId() - tracks.offset()); + mProng1Manager.template fill(prong1, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + } + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->fillAnalysis(charmHadronCandidate); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->fillQa(charmHadronCandidate); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + this->fillMc(charmHadronCandidate, mcParticles, mcMothers, mcPartonicMothers); + } + } + + private: + static constexpr int AbsCharge = 1; + static constexpr int SignPlus = 1; + static constexpr int SignMinus = -1; + + // charge of the prongs is fixed by the PWGHF reconstruction: prong0 is the positive + // daughter, prong1 the negative one. The D0/D0bar hypothesis only swaps which prong is + // the pion and which is the kaon, not their charge. + std::array resolveProngPdgCodes(int sign) + { + if constexpr (charmhadronbuilder::isThreeProng()) { + if (mPdgCode != o2::constants::physics::Pdg::kLambdaCPlus) { + LOG(fatal) << "PDG code for a 3-prong charm hadron has to be Lc"; + } + if (sign < 0) { + mPdgCode = -1 * mPdgCode; // switch sign for Lcbar + } + // Lc -> p K pi, prongs stored remapped so that prong 0 is always the proton + return {std::abs(PDG_t::kProton), std::abs(PDG_t::kKPlus), std::abs(PDG_t::kPiPlus)}; + } else { + if (mPdgCode != o2::constants::physics::Pdg::kD0) { + LOG(fatal) << "PDG code for charm hadron has to be D0"; + } + if (sign > 0) { + // D0 -> pi+ K- + return {std::abs(PDG_t::kPiPlus), std::abs(PDG_t::kKMinus), 0}; + } + // D0bar -> K+ pi- + mPdgCode = -1 * mPdgCode; // switch sign for D0bar + return {std::abs(PDG_t::kKPlus), std::abs(PDG_t::kPiMinus), 0}; + } + } + + template + void enableOptionalHistograms(T const& ConfCharmHadronQaBinning) + { + mPlotTopology = ConfCharmHadronQaBinning.plotTopology.value; + } + + void initAnalysis(std::map> const& CharmHadronSpecs) + { + std::string analysisDir = std::string(charmHadronPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {CharmHadronSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {CharmHadronSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {CharmHadronSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {CharmHadronSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kSign, HistTable), getHistDesc(kSign, HistTable), getHistType(kSign, HistTable), {CharmHadronSpecs.at(kSign)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {CharmHadronSpecs.at(kPtVsMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {CharmHadronSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {CharmHadronSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {CharmHadronSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& charmHadronCandidate) + { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), charmHadronCandidate.pt()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), charmHadronCandidate.eta()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), charmHadronCandidate.phi()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), charmHadronCandidate.mass()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kSign, HistTable)), charmHadronCandidate.sign()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), charmHadronCandidate.pt(), charmHadronCandidate.mass()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsEta, HistTable)), charmHadronCandidate.pt(), charmHadronCandidate.eta()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsPhi, HistTable)), charmHadronCandidate.pt(), charmHadronCandidate.phi()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhiVsEta, HistTable)), charmHadronCandidate.phi(), charmHadronCandidate.eta()); + } + + void initQa(std::map> const& CharmHadronQaSpecs) + { + std::string qaDir = std::string(charmHadronPrefix) + std::string(QaDir); + if constexpr (charmhadronbuilder::isThreeProng()) { + mHistogramRegistry->add(qaDir + getHistNameV2(kMassLcToPKPi, HistTable), getHistDesc(kMassLcToPKPi, HistTable), getHistType(kMassLcToPKPi, HistTable), {CharmHadronQaSpecs.at(kMassLcToPKPi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kMassLcToPiKP, HistTable), getHistDesc(kMassLcToPiKP, HistTable), getHistType(kMassLcToPiKP, HistTable), {CharmHadronQaSpecs.at(kMassLcToPiKP)}); + } else { + mHistogramRegistry->add(qaDir + getHistNameV2(kMassD0, HistTable), getHistDesc(kMassD0, HistTable), getHistType(kMassD0, HistTable), {CharmHadronQaSpecs.at(kMassD0)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kMassD0bar, HistTable), getHistDesc(kMassD0bar, HistTable), getHistType(kMassD0bar, HistTable), {CharmHadronQaSpecs.at(kMassD0bar)}); + } + mHistogramRegistry->add(qaDir + getHistNameV2(kMlBkg, HistTable), getHistDesc(kMlBkg, HistTable), getHistType(kMlBkg, HistTable), {CharmHadronQaSpecs.at(kMlBkg)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kMlPrompt, HistTable), getHistDesc(kMlPrompt, HistTable), getHistType(kMlPrompt, HistTable), {CharmHadronQaSpecs.at(kMlPrompt)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kMlNonPrompt, HistTable), getHistDesc(kMlNonPrompt, HistTable), getHistType(kMlNonPrompt, HistTable), {CharmHadronQaSpecs.at(kMlNonPrompt)}); + + if (mPlotTopology) { + mHistogramRegistry->add(qaDir + getHistNameV2(kCpa, HistTable), getHistDesc(kCpa, HistTable), getHistType(kCpa, HistTable), {CharmHadronQaSpecs.at(kCpa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kCpaXY, HistTable), getHistDesc(kCpaXY, HistTable), getHistType(kCpaXY, HistTable), {CharmHadronQaSpecs.at(kCpaXY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayLength, HistTable), getHistDesc(kDecayLength, HistTable), getHistType(kDecayLength, HistTable), {CharmHadronQaSpecs.at(kDecayLength)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayLengthXY, HistTable), getHistDesc(kDecayLengthXY, HistTable), getHistType(kDecayLengthXY, HistTable), {CharmHadronQaSpecs.at(kDecayLengthXY)}); + if constexpr (charmhadronbuilder::isThreeProng()) { + mHistogramRegistry->add(qaDir + getHistNameV2(kChi2Pca, HistTable), getHistDesc(kChi2Pca, HistTable), getHistType(kChi2Pca, HistTable), {CharmHadronQaSpecs.at(kChi2Pca)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kImpactParameterProngSqSum, HistTable), getHistDesc(kImpactParameterProngSqSum, HistTable), getHistType(kImpactParameterProngSqSum, HistTable), {CharmHadronQaSpecs.at(kImpactParameterProngSqSum)}); + } else { + mHistogramRegistry->add(qaDir + getHistNameV2(kImpactParameterProduct, HistTable), getHistDesc(kImpactParameterProduct, HistTable), getHistType(kImpactParameterProduct, HistTable), {CharmHadronQaSpecs.at(kImpactParameterProduct)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kCosThetaStar, HistTable), getHistDesc(kCosThetaStar, HistTable), getHistType(kCosThetaStar, HistTable), {CharmHadronQaSpecs.at(kCosThetaStar)}); + } + } + } + + void initMc(std::map> const& CharmHadronSpecs) + { + std::string mcDir = std::string(charmHadronPrefix) + std::string(McDir); + + const o2::framework::AxisSpec axisOrigin = {static_cast(modes::McOrigin::kMcOriginLast), -0.5, static_cast(modes::McOrigin::kMcOriginLast) - 0.5}; + mHistogramRegistry->add(mcDir + getHistNameV2(kOrigin, HistTable), getHistDesc(kOrigin, HistTable), getHistType(kOrigin, HistTable), {axisOrigin}); + mHistogramRegistry->get(HIST(charmHadronPrefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kPrompt), modes::mcOriginToString(modes::McOrigin::kPrompt)); + mHistogramRegistry->get(HIST(charmHadronPrefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kNonPrompt), modes::mcOriginToString(modes::McOrigin::kNonPrompt)); + + mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {CharmHadronSpecs.at(kPdg)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {CharmHadronSpecs.at(kPdgMother)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePtVsPt, HistTable), getHistDesc(kTruePtVsPt, HistTable), getHistType(kTruePtVsPt, HistTable), {CharmHadronSpecs.at(kTruePtVsPt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueEtaVsEta, HistTable), getHistDesc(kTrueEtaVsEta, HistTable), getHistType(kTrueEtaVsEta, HistTable), {CharmHadronSpecs.at(kTrueEtaVsEta)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePhiVsPhi, HistTable), getHistDesc(kTruePhiVsPhi, HistTable), getHistType(kTruePhiVsPhi, HistTable), {CharmHadronSpecs.at(kTruePhiVsPhi)}); + std::vector ptVsOriginAxes = CharmHadronSpecs.at(kPtVsOrigin); + ptVsOriginAxes.push_back(axisOrigin); + mHistogramRegistry->add(mcDir + getHistNameV2(kPtVsOrigin, HistTable), getHistDesc(kPtVsOrigin, HistTable), getHistType(kPtVsOrigin, HistTable), ptVsOriginAxes); + } + + template + void fillQa(T const& charmHadronCandidate) + { + float mlBkg = 0.f; + float mlPrompt = 0.f; + float mlNonPrompt = 0.f; + + if constexpr (charmhadronbuilder::isThreeProng()) { + // both hypotheses are histogrammed, so ambiguous candidates are visible + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kMassLcToPKPi, HistTable)), charmHadronCandidate.isSelLcToPKPi() ? charmHadronCandidate.mass() : charmHadronCandidate.massCompetingHypothesis()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kMassLcToPiKP, HistTable)), charmHadronCandidate.isSelLcToPKPi() ? charmHadronCandidate.massCompetingHypothesis() : charmHadronCandidate.mass()); + + if (charmHadronCandidate.isSelLcToPKPi()) { + mlBkg = charmHadronCandidate.mlProbLcToPKPiBkg(); + mlPrompt = charmHadronCandidate.mlProbLcToPKPiPrompt(); + mlNonPrompt = charmHadronCandidate.mlProbLcToPKPiNonPrompt(); + } else { + mlBkg = charmHadronCandidate.mlProbLcToPiKPBkg(); + mlPrompt = charmHadronCandidate.mlProbLcToPiKPPrompt(); + mlNonPrompt = charmHadronCandidate.mlProbLcToPiKPNonPrompt(); + } + } else { + // invariant mass split by hypothesis: D0 (sign > 0) or D0bar (sign < 0) + if (charmHadronCandidate.sign() > 0) { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kMassD0, HistTable)), charmHadronCandidate.mass()); + mlBkg = charmHadronCandidate.mlProbD0Bkg(); + mlPrompt = charmHadronCandidate.mlProbD0Prompt(); + mlNonPrompt = charmHadronCandidate.mlProbD0NonPrompt(); + } else { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kMassD0bar, HistTable)), charmHadronCandidate.mass()); + mlBkg = charmHadronCandidate.mlProbD0barBkg(); + mlPrompt = charmHadronCandidate.mlProbD0barPrompt(); + mlNonPrompt = charmHadronCandidate.mlProbD0barNonPrompt(); + } + } + + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kMlBkg, HistTable)), mlBkg); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kMlPrompt, HistTable)), mlPrompt); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kMlNonPrompt, HistTable)), mlNonPrompt); + + if (mPlotTopology) { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kCpa, HistTable)), charmHadronCandidate.cpa()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kCpaXY, HistTable)), charmHadronCandidate.cpaXY()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kDecayLength, HistTable)), charmHadronCandidate.decayLength()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kDecayLengthXY, HistTable)), charmHadronCandidate.decayLengthXY()); + if constexpr (charmhadronbuilder::isThreeProng()) { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kChi2Pca, HistTable)), charmHadronCandidate.chi2PCA()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kImpactParameterProngSqSum, HistTable)), charmHadronCandidate.impactParameterProngSqSum()); + } else { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kImpactParameterProduct, HistTable)), charmHadronCandidate.impactParameterProduct()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(QaDir) + HIST(getHistName(kCosThetaStar, HistTable)), charmHadronCandidate.cosThetaStar()); + } + } + } + + template + void fillMc(T1 const& charmHadronCandidate, T2 const& /*mcParticles*/, T3 const& /*mcMothers*/, T4 const& /*mcPartonicMothers*/) + { + // no matched generated particle: reconstructed, but not a true D0 + if (!charmHadronCandidate.has_fMcParticle()) { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); + return; + } + + auto mcParticle = charmHadronCandidate.template fMcParticle_as(); + + // resolution: generated vs reconstructed kinematics + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kTruePtVsPt, HistTable)), mcParticle.pt(), charmHadronCandidate.pt()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kTrueEtaVsEta, HistTable)), mcParticle.eta(), charmHadronCandidate.eta()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kTruePhiVsPhi, HistTable)), mcParticle.phi(), charmHadronCandidate.phi()); + + // origin is resolved to prompt / non-prompt in the mc builder + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kPtVsOrigin, HistTable)), charmHadronCandidate.pt(), mcParticle.origin()); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), mcParticle.pdgCode()); + + // pdg of the mother, i.e. the beauty hadron for non-prompt D0s + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), mother.pdgCode()); + } else { + mHistogramRegistry->fill(HIST(charmHadronPrefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), 0); + } + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + int mPdgCode = 0; + bool mPlotTopology = true; + + trackhistmanager::TrackHistManager mProng0Manager; + trackhistmanager::TrackHistManager mProng1Manager; + trackhistmanager::TrackHistManager mProng2Manager; +}; +}; // namespace o2::analysis::femto::charmhadronhistmanager +#endif // PWGCF_FEMTO_CORE_CHARMHADRONHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/closePairRejection.h b/PWGCF/Femto/Core/closePairRejection.h index 2c311b750e6..d7d8b320171 100644 --- a/PWGCF/Femto/Core/closePairRejection.h +++ b/PWGCF/Femto/Core/closePairRejection.h @@ -1,4 +1,4 @@ -// Copyright 2019-2022 CERN and copyright holders of ALICE O2. +// 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. // @@ -17,6 +17,7 @@ #define PWGCF_FEMTO_CORE_CLOSEPAIRREJECTION_H_ #include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" #include "Common/Core/RecoDecay.h" @@ -38,9 +39,7 @@ #include #include -namespace o2::analysis::femto -{ -namespace closepairrejection +namespace o2::analysis::femto::closepairrejection { // enum for track histograms enum CprHist { @@ -55,49 +54,78 @@ enum CprHist { kRadius6, kRadius7, kRadius8, + kKinematic, // kinematic variable of the pair/triplet which are blocked kPhi1VsPhi2, kEta1VsEta2, kCprHistogramLast }; // template configurable group for Cpr -template +// DefaultCutAverage: default of cutAverage (e.g. false for pairs of generated particles, where no detector effects are present) +// DefaultKinematicVariable: default of kinematicVariable (e.g. Q3 for triplets, kstar for pairs) +template struct ConfCpr : o2::framework::ConfigurableGroup { std::string prefix = std::string(Prefix); - o2::framework::Configurable cutAverage{"cutAverage", true, "Apply CPR if the average deta-dphistar is below the configured values"}; + o2::framework::Configurable cutAverage{"cutAverage", DefaultCutAverage, "Apply CPR if the average deta-dphistar is below the configured values"}; o2::framework::Configurable cutAnyRadius{"cutAnyRadius", false, "Apply CPR if the deta-dphistar is below the configured values at any radius"}; + o2::framework::Configurable cutElipsoidal{"cutElipsoidal", true, "If true, apply CPR as ellipsoidal cut. If false use rectangular cut."}; o2::framework::Configurable plotAllRadii{"plotAllRadii", true, "Plot deta-dphi distribution at all radii"}; o2::framework::Configurable plotAverage{"plotAverage", true, "Plot average deta dphi distribution"}; - o2::framework::Configurable plotAngularCorrelation{"plotAngularCorrelation", false, "Plot angular correlation of particles (eta1 vs eta2 & phi1 vs phi2"}; + o2::framework::Configurable plotAngularCorrelation{"plotAngularCorrelation", false, "Plot angular correlation of particles (eta1 vs eta2 & phi1 vs phi2)"}; + o2::framework::Configurable plotKinematic{"plotKinematic", true, "Plot kinematic (kstar/Q3) distribution of blocked pairs/triplets"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(DefaultKinematicVariable), "Type of kinematic variable (0->kstar, 1->kT, 2->mT, 3->Q3) reported by the kinematic plot"}; 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 kinematicMin{"kinematicMin", -1.f, "Minimum kstar/Q3 of pair/triplet for plotting (Set to negative value to turn off the cut)"}; o2::framework::Configurable kinematicMax{"kinematicMax", -1.f, "Maximum kstar/Q3 of pair/triplet for plotting (Set to negative value to turn off the cut)"}; + o2::framework::ConfigurableAxis binningKinematic{"binningKinematic", {100, 0, 1}, "Binning of kinematic variable of pair/triplet which are cut"}; o2::framework::ConfigurableAxis binningDeta{"binningDeta", {{250, -0.5, 0.5}}, "deta"}; o2::framework::ConfigurableAxis binningDphistar{"binningDphistar", {{250, -0.5, 0.5}}, "dphi"}; o2::framework::ConfigurableAxis binningCorrelationPhi{"binningCorrelationPhi", {{720, 0, o2::constants::math::TwoPI}}, "Phi binning for correlation plot"}; o2::framework::ConfigurableAxis binningCorrelationEta{"binningCorrelationEta", {{160, -0.8, 0.8}}, "Eta binning for correlation plot"}; o2::framework::Configurable seed{"seed", -1, "Seed to randomize particle 1 and particle 2. Set to negative value to deactivate. Set to 0 to generate unique seed in time."}; + o2::framework::Configurable magField{"magField", 5, "MC ONLY: In case of pure MC processing (no reconstruction), set magnetic field in kG"}; }; constexpr const char PrefixCprTrackTrack[] = "CprTrackTrack"; constexpr const char PrefixCprTrackV0Daughter[] = "CprTrackV0Daughter"; +constexpr const char PrefixCprTrackD0Daughter[] = "CprTrackD0Daughter"; +constexpr const char PrefixCprTrackLcProton[] = "CprTrackLcProton"; +constexpr const char PrefixCprTrackLcKaon[] = "CprTrackLcKaon"; +constexpr const char PrefixCprTrackLcPion[] = "CprTrackLcPion"; +constexpr const char PrefixCprD0DaughterD0DaughterPos[] = "CprD0DaughterD0DaughterPos"; +constexpr const char PrefixCprD0DaughterD0DaughterNeg[] = "CprD0DaughterD0DaughterNeg"; constexpr const char PrefixCprTrackResonanceDaughter[] = "CprTrackResonanceDaughter"; +constexpr const char PrefixCprTrackTwoPhotonResonanceDaughter[] = "CprTrackTwoPhotonResonanceDaughter"; constexpr const char PrefixCprTrackKinkDaughter[] = "CprTrackKinkDaughter"; constexpr const char PrefixCprV0DaughterV0DaughterPos[] = "CprV0DaughterV0DaughterPos"; constexpr const char PrefixCprV0DaughterV0DaughterNeg[] = "CprV0DaughterV0DaughterNeg"; +constexpr const char PrefixCprV0DaughterResoDaughterPos[] = "CprV0DaughterResoDaughterPos"; +constexpr const char PrefixCprV0DaughterResoDaughterNeg[] = "CprV0DaughterResoDaughterNeg"; constexpr const char PrefixCprTrackCascadeBachelor[] = "CprTrackCascadeBachelor"; +constexpr const char PrefixCprMcParticleMcParticle[] = "CprMcParticleMcParticle"; // pairs using ConfCprTrackTrack = ConfCpr; using ConfCprTrackV0Daughter = ConfCpr; +using ConfCprTrackD0Daughter = ConfCpr; +using ConfCprTrackLcProton = ConfCpr; +using ConfCprTrackLcKaon = ConfCpr; +using ConfCprTrackLcPion = ConfCpr; +using ConfCprD0DaugherD0DaughterPos = ConfCpr; +using ConfCprD0DaugherD0DaughterNeg = ConfCpr; using ConfCprTrackResonanceDaughter = ConfCpr; +// no cut by default: CPR is a no-op for two-photon resonances (see ClosePairRejectionTrackTwoPhotonResonance) +using ConfCprTrackTwoPhotonResonanceDaughter = ConfCpr; using ConfCprTrackKinkDaughter = ConfCpr; using ConfCprV0DaugherV0DaughterPos = ConfCpr; using ConfCprV0DaugherV0DaughterNeg = ConfCpr; +using ConfCprV0DaughterResoDaughterPos = ConfCpr; +using ConfCprV0DaughterResoDaughterNeg = ConfCpr; using ConfCprTrackCascadeBachelor = ConfCpr; +using ConfCprMcParticleMcParticle = ConfCpr; // generated particles, no cut by default // tpc radii for computing phistar constexpr int Nradii = 9; @@ -108,16 +136,36 @@ constexpr char PrefixTrackTrackSe[] = "CPR_TrackTrack/SE/"; constexpr char PrefixTrackTrackMe[] = "CPR_TrackTrack/ME/"; constexpr char PrefixTrackV0DaughterSe[] = "CPR_TrackV0Dau/SE/"; constexpr char PrefixTrackV0DaughterMe[] = "CPR_TrackV0Dau/ME/"; +constexpr char PrefixTrackD0DaughterSe[] = "CPR_TrackD0Dau/SE/"; +constexpr char PrefixTrackD0DaughterMe[] = "CPR_TrackD0Dau/ME/"; +constexpr char PrefixD0D0PosSe[] = "CPR_D0D0_PosDau/SE/"; +constexpr char PrefixD0D0NegSe[] = "CPR_D0D0_NegDau/SE/"; +constexpr char PrefixD0D0PosMe[] = "CPR_D0D0_PosDau/ME/"; +constexpr char PrefixD0D0NegMe[] = "CPR_D0D0_NegDau/ME/"; +constexpr char PrefixTrackLcProtonSe[] = "CPR_TrackLc_Proton/SE/"; +constexpr char PrefixTrackLcKaonSe[] = "CPR_TrackLc_Kaon/SE/"; +constexpr char PrefixTrackLcPionSe[] = "CPR_TrackLc_Pion/SE/"; +constexpr char PrefixTrackLcProtonMe[] = "CPR_TrackLc_Proton/ME/"; +constexpr char PrefixTrackLcKaonMe[] = "CPR_TrackLc_Kaon/ME/"; +constexpr char PrefixTrackLcPionMe[] = "CPR_TrackLc_Pion/ME/"; constexpr char PrefixV0V0PosSe[] = "CPR_V0V0_PosDau/SE/"; constexpr char PrefixV0V0NegSe[] = "CPR_V0V0_NegDau/SE/"; constexpr char PrefixV0V0PosMe[] = "CPR_V0V0_PosDau/ME/"; constexpr char PrefixV0V0NegMe[] = "CPR_V0V0_NegDau/ME/"; +constexpr char PrefixV0TwoTrackResonancePosSe[] = "CPR_V0Resonance_PosDau/SE/"; +constexpr char PrefixV0TwoTrackResonanceNegSe[] = "CPR_V0Resonance_NegDau/SE/"; +constexpr char PrefixV0TwoTrackResonancePosMe[] = "CPR_V0Resonance_PosDau/ME/"; +constexpr char PrefixV0TwoTrackResonanceNegMe[] = "CPR_V0Resonance_NegDau/ME/"; constexpr char PrefixTrackTwoTrackResonanceSe[] = "CPR_TrackResonanceDau/SE/"; constexpr char PrefixTrackTwoTrackResonanceMe[] = "CPR_TrackResonanceDau/ME/"; +constexpr char PrefixTrackTwoPhotonResonanceSe[] = "CPR_TrackTwoPhotonResonanceDau/SE/"; +constexpr char PrefixTrackTwoPhotonResonanceMe[] = "CPR_TrackTwoPhotonResonanceDau/ME/"; constexpr char PrefixTrackCascadeBachelorSe[] = "CPR_TrackCascadeBachelor/SE/"; constexpr char PrefixTrackCascadeBachelorMe[] = "CPR_TrackCascadeBachelor/ME/"; constexpr char PrefixTrackKinkSe[] = "CPR_TrackKink/SE/"; constexpr char PrefixTrackKinkMe[] = "CPR_TrackKink/ME/"; +constexpr char PrefixMcParticleMcParticleSe[] = "CPR_McParticleMcParticle/SE/"; +constexpr char PrefixMcParticleMcParticleMe[] = "CPR_McParticleMcParticle/ME/"; // must be in sync with enum TrackVariables // the enum gives the correct index in the array @@ -132,6 +180,7 @@ constexpr std::array, kCprHistogramLast> HistTabl {kRadius6, o2::framework::HistType::kTH2F, "hRadius6", "Radius 6: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, {kRadius7, o2::framework::HistType::kTH2F, "hRadius7", "Radius 7: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, {kRadius8, o2::framework::HistType::kTH2F, "hRadius8", "Radius 8: #Delta #eta vs #Delta #phi*; #Delta #eta; #Delta #phi*"}, + {kKinematic, o2::framework::HistType::kTH1F, "hKinematic", "Kinematic distribution of blocked pairs; kinematic Variable (GeV/#it{c}); Entries"}, {kPhi1VsPhi2, o2::framework::HistType::kTH2F, "hPhi1vsPhi2", "#phi_{1} vs #phi_{2}; #phi_{1}; #phi_{2}"}, {kEta1VsEta2, o2::framework::HistType::kTH2F, "hEta1VsEta2", "#eta_{1} vs #eta_{2}; #eta_{1}; #eta_{2}"}}}; @@ -149,12 +198,13 @@ auto makeCprHistSpecMap(const T& confCpr) {kRadius6, {confCpr.binningDeta, confCpr.binningDphistar}}, {kRadius7, {confCpr.binningDeta, confCpr.binningDphistar}}, {kRadius8, {confCpr.binningDeta, confCpr.binningDphistar}}, + {kKinematic, {confCpr.binningKinematic}}, {kPhi1VsPhi2, {confCpr.binningCorrelationPhi, confCpr.binningCorrelationPhi}}, {kEta1VsEta2, {confCpr.binningCorrelationEta, confCpr.binningCorrelationEta}}, }; }; -template +template class CloseTrackRejection { public: @@ -184,6 +234,7 @@ class CloseTrackRejection mCutAverage = confCpr.cutAverage.value; mCutAnyRadius = confCpr.cutAnyRadius.value; + mUseEpisoidalCut = confCpr.cutElipsoidal.value; mKinematicMin = confCpr.kinematicMin.value; mKinematicMax = confCpr.kinematicMax.value; @@ -192,9 +243,11 @@ class CloseTrackRejection mPlotAllRadii = confCpr.plotAllRadii.value; mPlotAngularCorrelation = confCpr.plotAngularCorrelation.value; + mPlotKinematic = confCpr.plotKinematic.value; + mKinematicVariable = static_cast(confCpr.kinematicVariable.value); if (confCpr.seed.value >= 0) { - uint64_t randomSeed; + uint64_t randomSeed = 0; mRandomizeTracks = true; if (confCpr.seed.value == 0) { randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); @@ -204,11 +257,16 @@ class CloseTrackRejection mRng = std::mt19937(randomSeed); } - // check if we need to apply any cut a plot is requested + // check if we need to apply any cut or if a plot is requested mIsActivated = mCutAverage || mCutAnyRadius || mPlotAverage || mPlotAllRadii; mHistogramRegistry = registry; + if (mPlotKinematic) { + std::string kinematicDesc = histmanager::replaceFirstAxisTitle(getHistDesc(kKinematic, HistTable), modes::kinematicVariableToAxisTitle(mKinematicVariable)); + mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), kinematicDesc.c_str(), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); + } + if (mPlotAverage) { mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kAverage, HistTable), getHistDesc(kAverage, HistTable), getHistType(kAverage, HistTable), {specs.at(kAverage)}); } @@ -232,12 +290,34 @@ class CloseTrackRejection void setMagField(float magField) { mMagField = magField; } - template - void compute(T1 const& track1, T2 const& track2) + // checks if the cut is activated; if so, computes deta/dphistar for the pair, + // fills the "blocked" or "passed" histograms accordingly, and returns whether + // the pair was rejected. If the cut is not activated, does nothing and returns false. + // pairHistManager must expose getKinematic() (same interface PcManager::isCleanPair expects). + template + [[nodiscard]] bool isClosePair(T1 const& track1, T2 const& track2, T3 const& pairHistManager) { if (!mIsActivated) { - return; + return false; + } + + compute(track1, track2); + + bool isClose = mUseEpisoidalCut ? isClosePairEpisoidalCut() : isClosePairRectengularCut(); + + if (isClose) { + fillBlocked(pairHistManager.getKinematic(mKinematicVariable)); + } else { + fillPassed(pairHistManager.getKinematic(mKinematicVariable)); } + + return isClose; + } + + private: + template + void compute(T1 const& track1, T2 const& track2) + { // reset values mAverageDphistar = 0.f; int count = 0; @@ -250,41 +330,102 @@ class CloseTrackRejection swapTracks = (mSwapDist(mRng) == 1); } - auto const& t1 = swapTracks ? track2 : track1; - auto const& t2 = swapTracks ? track1 : track2; + // get kinematics of the particles + // if configured, randomly swap particle 1 and particle 2 + float eta1 = swapTracks ? track2.eta() : track1.eta(); + float eta2 = swapTracks ? track1.eta() : track2.eta(); + float phi1 = swapTracks ? track2.phi() : track1.phi(); + float phi2 = swapTracks ? track1.phi() : track2.phi(); + float signedPt1 = swapTracks ? track2.signedPt() : track1.signedPt(); + float signedPt2 = swapTracks ? track1.signedPt() : track2.signedPt(); + int chargeAbs1 = swapTracks ? mChargeAbsTrack2 : mChargeAbsTrack1; + int chargeAbs2 = swapTracks ? mChargeAbsTrack1 : mChargeAbsTrack2; - mDeta = t1.eta() - t2.eta(); + mDeta = eta1 - eta2; for (size_t i = 0; i < TpcRadii.size(); i++) { - auto phistar1 = phistar(mMagField, TpcRadii[i], mChargeAbsTrack1 * t1.signedPt(), t1.phi()); - auto phistar2 = phistar(mMagField, TpcRadii[i], mChargeAbsTrack2 * t2.signedPt(), t2.phi()); + auto phistar1 = phistar(mMagField, TpcRadii.at(i), chargeAbs1 * signedPt1, phi1); + auto phistar2 = phistar(mMagField, TpcRadii.at(i), chargeAbs2 * signedPt2, phi2); if (phistar1 && phistar2) { mDphistar.at(i) = RecoDecay::constrainAngle(phistar1.value() - phistar2.value(), -o2::constants::math::PI); // constrain angular difference between -pi and pi mDphistarMask.at(i) = true; count++; } } - // for small momemeta the calculation of phistar might fail, if the particle did not reach one or more of the outer radii + // for small momenta the calculation of phistar might fail, if the particle did not reach one or more of the outer radii if (count > 0) { mAverageDphistar = std::accumulate(mDphistar.begin(), mDphistar.end(), 0.f) / count; // only average values if phistar could be computed } else { - mAverageDphistar = 0.f; // if computation at all radii fail, set it 0 + mAverageDphistar = 0.f; // if computation at all radii failed, set it 0 } if (mPlotAngularCorrelation) { - mPhi1 = t1.phi(); - mPhi2 = t2.phi(); - mEta1 = t1.eta(); - mEta2 = t2.eta(); + mPhi1 = phi1; + mPhi2 = phi2; + mEta1 = eta1; + mEta2 = eta2; } } - void fill(float kinematic) + std::optional phistar(float magfield, float radius, float signedPt, float phi) { - if (!mIsActivated) { - return; + double arg = 0.3 * (0.1 * magfield) * (0.01 * radius) / (2. * signedPt); + if (std::fabs(arg) <= 1.) { + double angle = phi - std::asin(arg); + return static_cast(RecoDecay::constrainAngle(angle)); } + return std::nullopt; + } + + [[nodiscard]] bool isClosePairEpisoidalCut() const + { + bool isCloseAverage = false; + bool isCloseAnyRadius = false; + if (mCutAverage) { + isCloseAverage = std::hypot((mAverageDphistar - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f; + } + + if (mCutAnyRadius) { + for (size_t i = 0; i < TpcRadii.size(); i++) { + if (isCloseAnyRadius) { + break; + } + if (mDphistarMask.at(i)) { + isCloseAnyRadius = std::hypot((mDphistar.at(i) - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f; + } + } + } + return isCloseAverage || isCloseAnyRadius; + } + + [[nodiscard]] bool isClosePairRectengularCut() const + { + bool isCloseAverage = false; + bool isCloseAnyRadius = false; + + if (mCutAverage) { + isCloseAverage = std::fabs(mAverageDphistar - mDphistarCenter) < mDphistarMax && + std::fabs(mDeta - mDetaCenter) < mDetaMax; + } + + if (mCutAnyRadius) { + for (size_t i = 0; i < TpcRadii.size(); i++) { + if (isCloseAnyRadius) { + break; + } + if (mDphistarMask.at(i)) { + isCloseAnyRadius = std::fabs(mDphistar.at(i) - mDphistarCenter) < mDphistarMax && + std::fabs(mDeta - mDetaCenter) < mDetaMax; + } + } + } + return isCloseAverage || isCloseAnyRadius; + } + + // fill the "normal" distributions (deta-dphistar, angular correlation) for pairs that passed the cut + void fillPassed(float kinematic) const + { if (mKinematicMin > 0.f && kinematic < mKinematicMin) { return; } @@ -307,82 +448,64 @@ class CloseTrackRejection if (mPlotAllRadii) { if (mDphistarMask.at(0)) { mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius0, HistTable)), mDeta, mDphistar.at(0)); - } - if (mDphistarMask.at(1)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius1, HistTable)), mDeta, mDphistar.at(1)); - } - if (mDphistarMask.at(2)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius2, HistTable)), mDeta, mDphistar.at(2)); - } - if (mDphistarMask.at(3)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius3, HistTable)), mDeta, mDphistar.at(3)); - } - if (mDphistarMask.at(4)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius4, HistTable)), mDeta, mDphistar.at(4)); - } - if (mDphistarMask.at(5)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius5, HistTable)), mDeta, mDphistar.at(5)); - } - if (mDphistarMask.at(6)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius6, HistTable)), mDeta, mDphistar.at(6)); - } - if (mDphistarMask.at(7)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius7, HistTable)), mDeta, mDphistar.at(7)); - } - if (mDphistarMask.at(8)) { - mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius8, HistTable)), mDeta, mDphistar.at(8)); + if (mDphistarMask.at(1)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius1, HistTable)), mDeta, mDphistar.at(1)); + } + if (mDphistarMask.at(2)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius2, HistTable)), mDeta, mDphistar.at(2)); + } + if (mDphistarMask.at(3)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius3, HistTable)), mDeta, mDphistar.at(3)); + } + if (mDphistarMask.at(4)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius4, HistTable)), mDeta, mDphistar.at(4)); + } + if (mDphistarMask.at(5)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius5, HistTable)), mDeta, mDphistar.at(5)); + } + if (mDphistarMask.at(6)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius6, HistTable)), mDeta, mDphistar.at(6)); + } + if (mDphistarMask.at(7)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius7, HistTable)), mDeta, mDphistar.at(7)); + } + if (mDphistarMask.at(8)) { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kRadius8, HistTable)), mDeta, mDphistar.at(8)); + } } } } - bool isClosePair() const + // fill the kinematic (kstar/Q3) distribution of pairs that were blocked by the CPR cut + void fillBlocked(float kinematic) const { - if (!mIsActivated) { - return false; - } - bool isCloseAverage = false; - bool isCloseAnyRadius = false; - - if (mCutAverage) { - isCloseAverage = std::hypot((mAverageDphistar - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f; + if (!mPlotKinematic) { + return; } - if (mCutAnyRadius) { - for (size_t i = 0; i < TpcRadii.size(); i++) { - if (isCloseAnyRadius) { - break; - } - if (mDphistarMask.at(i)) { - isCloseAnyRadius = std::hypot((mDphistar.at(i) - mDphistarCenter) / mDphistarMax, (mDeta - mDetaCenter) / mDetaMax) < 1.f; - } - } + if (mKinematicMin > 0.f && kinematic < mKinematicMin) { + return; } - return isCloseAverage || isCloseAnyRadius; - } - bool isActivated() const { return mIsActivated; } - - private: - std::optional phistar(float magfield, float radius, float signedPt, float phi) - { - double arg = 0.3 * (0.1 * magfield) * (0.01 * radius) / (2. * signedPt); - if (std::fabs(arg) <= 1.) { - double angle = phi - std::asin(arg); - return static_cast(RecoDecay::constrainAngle(angle)); + if (mKinematicMax > 0.f && kinematic > mKinematicMax) { + return; } - return std::nullopt; + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kKinematic, HistTable)), kinematic); } o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; bool mPlotAllRadii = false; bool mPlotAverage = false; bool mPlotAngularCorrelation = false; + bool mPlotKinematic = false; + modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; float mKinematicMin = -1.f; float mKinematicMax = -1.f; bool mCutAverage = false; bool mCutAnyRadius = false; + bool mUseEpisoidalCut = true; bool mIsActivated = false; @@ -410,14 +533,14 @@ class CloseTrackRejection std::uniform_int_distribution mSwapDist{0, 1}; }; -template +template class ClosePairRejectionTrackTrack { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2) { @@ -425,19 +548,18 @@ class ClosePairRejectionTrackTrack } void setMagField(float magField) { mCtr.setMagField(magField); } - template - void setPair(T1 const& track1, T2 const& track2, T3 const& /*tracks*/) + + template + [[nodiscard]] bool isClosePair(T1 const& track1, T2 const& track2, T3 const& /*tracks*/, T4 const& pairHistManager) { - mCtr.compute(track1, track2); + return mCtr.isClosePair(track1, track2, pairHistManager); } - bool isClosePair() const { return mCtr.isClosePair(); } - void fill(float kstar) { mCtr.fill(kstar); } private: CloseTrackRejection mCtr; }; -template +template class ClosePairRejectionV0V0 { public: @@ -458,24 +580,18 @@ class ClosePairRejectionV0V0 mCtrNeg.setMagField(magField); } - template - void setPair(T1 const& v01, T2 const& v02, T3 const& tracks) + template + [[nodiscard]] bool isClosePair(T1 const& v01, T2 const& v02, T3 const& tracks, T4 const& pairHistManager) { auto posDau1 = tracks.rawIteratorAt(v01.posDauId() - tracks.offset()); auto posDau2 = tracks.rawIteratorAt(v02.posDauId() - tracks.offset()); - mCtrPos.compute(posDau1, posDau2); + bool isClosePos = mCtrPos.isClosePair(posDau1, posDau2, pairHistManager); auto negDau1 = tracks.rawIteratorAt(v01.negDauId() - tracks.offset()); auto negDau2 = tracks.rawIteratorAt(v02.negDauId() - tracks.offset()); - mCtrNeg.compute(negDau1, negDau2); - } + bool isCloseNeg = mCtrNeg.isClosePair(negDau1, negDau2, pairHistManager); - bool isClosePair() const { return mCtrPos.isClosePair() || mCtrNeg.isClosePair(); } - - void fill(float kstar) - { - mCtrPos.fill(kstar); - mCtrNeg.fill(kstar); + return isClosePos || isCloseNeg; } private: @@ -483,14 +599,14 @@ class ClosePairRejectionV0V0 CloseTrackRejection mCtrNeg; }; -template +template class ClosePairRejectionTrackV0 // can also be used for any particle type that has pos/neg daughters, like resonances { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack) { mCtr.init(registry, specs, confCpr, absChargeTrack, 1); @@ -498,27 +614,69 @@ class ClosePairRejectionTrackV0 // can also be used for any particle type that h void setMagField(float magField) { mCtr.setMagField(magField); } - template - void setPair(T1 const& track, T2 const& v0, T3 const& trackTable) + template + [[nodiscard]] bool isClosePair(T1 const& track, T2 const& v0, T3 const& trackTable, T4 const& pairHistManager) { if (track.sign() > 0) { auto posDau = trackTable.rawIteratorAt(v0.posDauId() - trackTable.offset()); - mCtr.compute(track, posDau); - } else { - auto negDau = trackTable.rawIteratorAt(v0.negDauId() - trackTable.offset()); - mCtr.compute(track, negDau); + return mCtr.isClosePair(track, posDau, pairHistManager); } + auto negDau = trackTable.rawIteratorAt(v0.negDauId() - trackTable.offset()); + return mCtr.isClosePair(track, negDau, pairHistManager); + } + + private: + CloseTrackRejection mCtr; +}; + +template +class ClosePairRejectionTrackLc +{ + public: + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& specsProton, + std::map> const& specsKaon, + std::map> const& specsPion, + T1 const& confCprProton, + T2 const& confCprKaon, + T3 const& confCprPion, + int absChargeTrack) + { + mCtrProton.init(registry, specsProton, confCprProton, absChargeTrack, 1); + mCtrKaon.init(registry, specsKaon, confCprKaon, absChargeTrack, 1); + mCtrPion.init(registry, specsPion, confCprPion, absChargeTrack, 1); + } + + void setMagField(float magField) + { + mCtrProton.setMagField(magField); + mCtrKaon.setMagField(magField); + mCtrPion.setMagField(magField); } - bool isClosePair() const { return mCtr.isClosePair(); } + template + [[nodiscard]] bool isClosePair(T1 const& track, T2 const& lc, T3 const& trackTable, T4 const& pairHistManager) + { + auto prong0 = trackTable.rawIteratorAt(lc.prong0DauId() - trackTable.offset()); + bool isCloseProton = mCtrProton.isClosePair(track, prong0, pairHistManager); + + auto prong1 = trackTable.rawIteratorAt(lc.prong1DauId() - trackTable.offset()); + bool isCloseKaon = mCtrKaon.isClosePair(track, prong1, pairHistManager); + + auto prong2 = trackTable.rawIteratorAt(lc.prong2DauId() - trackTable.offset()); + bool isClosePion = mCtrPion.isClosePair(track, prong2, pairHistManager); - void fill(float kstar) { mCtr.fill(kstar); } + return isCloseProton || isCloseKaon || isClosePion; + } private: - CloseTrackRejection mCtr; + CloseTrackRejection mCtrProton; + CloseTrackRejection mCtrKaon; + CloseTrackRejection mCtrPion; }; -template +template class ClosePairRejectionTrackCascade { public: @@ -540,31 +698,22 @@ class ClosePairRejectionTrackCascade mCtrV0Daughter.setMagField(magField); } - template - void setPair(T1 const& track, T2 const& cascade, T3 const& trackTable) + template + [[nodiscard]] bool isClosePair(T1 const& track, T2 const& cascade, T3 const& trackTable, T4 const& pairHistManager) { auto bachelor = trackTable.rawIteratorAt(cascade.bachelorId() - trackTable.offset()); - mCtrBachelor.compute(track, bachelor); + bool isCloseBachelor = mCtrBachelor.isClosePair(track, bachelor, pairHistManager); + bool isCloseV0Daughter = false; if (track.sign() > 0) { auto posDau = trackTable.rawIteratorAt(cascade.posDauId() - trackTable.offset()); - mCtrV0Daughter.compute(track, posDau); + isCloseV0Daughter = mCtrV0Daughter.isClosePair(track, posDau, pairHistManager); } else { auto negDau = trackTable.rawIteratorAt(cascade.negDauId() - trackTable.offset()); - mCtrV0Daughter.compute(track, negDau); + isCloseV0Daughter = mCtrV0Daughter.isClosePair(track, negDau, pairHistManager); } - } - bool - isClosePair() const - { - return mCtrBachelor.isClosePair(); - } - - void fill(float kstar) - { - mCtrBachelor.fill(kstar); - mCtrV0Daughter.fill(kstar); + return isCloseBachelor || isCloseV0Daughter; } private: @@ -572,14 +721,14 @@ class ClosePairRejectionTrackCascade CloseTrackRejection mCtrV0Daughter; }; -template +template class ClosePairRejectionTrackKink { public: - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack) { mCtr.init(registry, specs, confCpr, absChargeTrack, 1); @@ -590,20 +739,65 @@ class ClosePairRejectionTrackKink mCtr.setMagField(magField); } - template - void setPair(T1 const& track, T2 const& kink, T3 const& trackTable) + template + [[nodiscard]] bool isClosePair(T1 const& track, T2 const& kink, T3 const& trackTable, T4 const& pairHistManager) { auto daughter = trackTable.rawIteratorAt(kink.chaDauId() - trackTable.offset()); - mCtr.compute(track, daughter); + return mCtr.isClosePair(track, daughter, pairHistManager); + } + + private: + CloseTrackRejection mCtr; +}; + +// PCM photon daughters are, for now, not indexed into the track table (see photonBuilder.h): +// they are TPC-only tracks, so they cannot be confused for a global track the way two V0 +// daughters or a track and a V0 daughter can -- there is no detector-overlap effect to correct +// for. This is a no-op placeholder so Track-TwoPhotonResonance pairs can use the same generic +// pair-building code path. +template +class ClosePairRejectionTrackTwoPhotonResonance +{ + public: + template + void init(o2::framework::HistogramRegistry* /*registry*/, + std::map> const& /*specs*/, + T1 const& /*confCpr*/, + int /*absChargeTrack*/) + { } - bool isClosePair() const { return mCtr.isClosePair(); } - void fill(float kstar) { mCtr.fill(kstar); } + void setMagField(float /*magField*/) {} + + template + [[nodiscard]] bool isClosePair(T1 const& /*track*/, T2 const& /*resonance*/, T3 const& /*photonTable*/, T4 const& /*pairHistManager*/) + { + return false; + } +}; + +template +class ClosePairRejectionMcParticleMcParticle +{ + public: + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& specs, + T1 const& confCpr) + { + mCtr.init(registry, specs, confCpr, 1, 1); + mCtr.setMagField(confCpr.magField.value); + } + + template + [[nodiscard]] bool isClosePair(T1 const& mcParticle1, T2 const& mcParticle2, T3 const& pairHistManager) + { + return mCtr.isClosePair(mcParticle1, mcParticle2, pairHistManager); + } private: CloseTrackRejection mCtr; }; -}; // namespace closepairrejection -}; // namespace o2::analysis::femto +}; // namespace o2::analysis::femto::closepairrejection #endif // PWGCF_FEMTO_CORE_CLOSEPAIRREJECTION_H_ diff --git a/PWGCF/Femto/Core/closeTripletRejection.h b/PWGCF/Femto/Core/closeTripletRejection.h index dd1bd0182c0..a3e2f1b8cc7 100644 --- a/PWGCF/Femto/Core/closeTripletRejection.h +++ b/PWGCF/Femto/Core/closeTripletRejection.h @@ -17,21 +17,27 @@ #define PWGCF_FEMTO_CORE_CLOSETRIPLETREJECTION_H_ #include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/modes.h" #include #include -#include #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::closetripletrejection { -namespace closetripletrejection -{ - constexpr const char PrefixCtrTrackTrackTrack[] = "CtrTrackTrackTrack"; -using ConfCtrTrackTrackTrack = closepairrejection::ConfCpr; +constexpr const char PrefixCtrTrackTrackV0[] = "CtrTrackTrackV0"; +constexpr const char PrefixCtrTrackTrackCascade[] = "CtrTrackTrackCascade"; +constexpr const char PrefixCtrMcParticleMcParticleMcParticle[] = "CtrMcParticleMcParticleMcParticle"; + +// Q3 (not kstar) is the natural kinematic variable for triplets, so it is the default here +using ConfCtrTrackTrackTrack = closepairrejection::ConfCpr; +using ConfCtrTrackTrackV0 = closepairrejection::ConfCpr; +using ConfCtrTrackTrackCascade = closepairrejection::ConfCpr; +// generated particles, no cut by default (close triplet rejection corrects a detector effect) +using ConfCtrMcParticleMcParticleMcParticle = closepairrejection::ConfCpr; // directory names constexpr char PrefixTrack1Track2Se[] = "CPR_Track1Track2/SE/"; @@ -41,24 +47,46 @@ constexpr char PrefixTrack1Track2Me[] = "CPR_Track1Track2/ME/"; constexpr char PrefixTrack2Track3Me[] = "CPR_Track2Track3/ME/"; constexpr char PrefixTrack1Track3Me[] = "CPR_Track1Track3/ME/"; +constexpr char PrefixMcParticle1McParticle2Se[] = "CPR_McParticle1McParticle2/SE/"; +constexpr char PrefixMcParticle2McParticle3Se[] = "CPR_McParticle2McParticle3/SE/"; +constexpr char PrefixMcParticle1McParticle3Se[] = "CPR_McParticle1McParticle3/SE/"; +constexpr char PrefixMcParticle1McParticle2Me[] = "CPR_McParticle1McParticle2/ME/"; +constexpr char PrefixMcParticle2McParticle3Me[] = "CPR_McParticle2McParticle3/ME/"; +constexpr char PrefixMcParticle1McParticle3Me[] = "CPR_McParticle1McParticle3/ME/"; + constexpr char PrefixTrack1V0Se[] = "CPR_Track1V0/SE/"; constexpr char PrefixTrack2V0Se[] = "CPR_Track2V0/SE/"; constexpr char PrefixTrack1V0Me[] = "CPR_Track1V0/ME/"; constexpr char PrefixTrack2V0Me[] = "CPR_Track2V0/ME/"; -template +constexpr char PrefixTrack1CascadeSe[] = "CPR_Track1Cascade/SE/"; +constexpr char PrefixTrack2CascadeSe[] = "CPR_Track2Cascade/SE/"; +constexpr char PrefixTrack1CascadeMe[] = "CPR_Track1Cascade/ME/"; +constexpr char PrefixTrack2CascadeMe[] = "CPR_Track2Cascade/ME/"; + +constexpr char PrefixTrack1V0DaughterSe[] = "CPR_Track1V0Dau/SE/"; +constexpr char PrefixTrack2V0DaughterSe[] = "CPR_Track2V0Dau/SE/"; +constexpr char PrefixTrack1V0DaughterMe[] = "CPR_Track1V0Dau/ME/"; +constexpr char PrefixTrack2V0DaughterMe[] = "CPR_Track2V0Dau/ME/"; + +constexpr char PrefixTrack1CascadeBachelorSe[] = "CPR_Track1CascadeBachelor/SE/"; +constexpr char PrefixTrack2CascadeBachelorSe[] = "CPR_Track2CascadeBachelor/SE/"; +constexpr char PrefixTrack1CascadeBachelorMe[] = "CPR_Track1CascadeBachelor/ME/"; +constexpr char PrefixTrack2CascadeBachelorMe[] = "CPR_Track2CascadeBachelor/ME/"; + +template class CloseTripletRejectionTrackTrackTrack { public: CloseTripletRejectionTrackTrackTrack() = default; ~CloseTripletRejectionTrackTrackTrack() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2, int absChargeTrack3) @@ -74,23 +102,17 @@ class CloseTripletRejectionTrackTrackTrack mCtrTrack23.setMagField(magField); mCtrTrack13.setMagField(magField); } - template - void setTriplet(T1 const& track1, T2 const& track2, T3 const& track3, T4 const& trackTable) - { - mCtrTrack12.setPair(track1, track2, trackTable); - mCtrTrack23.setPair(track2, track3, trackTable); - mCtrTrack13.setPair(track1, track3, trackTable); - } - bool isCloseTriplet() const - { - return mCtrTrack12.isClosePair() || mCtrTrack23.isClosePair() || mCtrTrack13.isClosePair(); - } - void fill(float q3) + // checks all three constituent pairs of the triplet; fills the deta-dphi/kinematic + // histograms of each pair internally and returns whether the triplet is rejected. + // tripletHistManager must expose getKinematic() (same interface CloseTrackRejection expects). + template + [[nodiscard]] bool isCloseTriplet(T1 const& track1, T2 const& track2, T3 const& track3, T4 const& trackTable, T5 const& tripletHistManager) { - mCtrTrack12.fill(q3); - mCtrTrack23.fill(q3); - mCtrTrack13.fill(q3); + bool isClose12 = mCtrTrack12.isClosePair(track1, track2, trackTable, tripletHistManager); + bool isClose23 = mCtrTrack23.isClosePair(track2, track3, trackTable, tripletHistManager); + bool isClose13 = mCtrTrack13.isClosePair(track1, track3, trackTable, tripletHistManager); + return isClose12 || isClose23 || isClose13; } private: @@ -99,19 +121,19 @@ class CloseTripletRejectionTrackTrackTrack closepairrejection::ClosePairRejectionTrackTrack mCtrTrack13; }; -template +template class CloseTripletRejectionTrackTrackV0 { public: CloseTripletRejectionTrackTrackV0() = default; ~CloseTripletRejectionTrackTrackV0() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, - T const& confCpr, + T1 const& confCpr, int absChargeTrack1, int absChargeTrack2) { @@ -126,31 +148,109 @@ class CloseTripletRejectionTrackTrackV0 mCtrTrack1V0.setMagField(magField); mCtrTrack2V0.setMagField(magField); } - template - void setTriplet(T1 const& track1, T2 const& track2, T3 const& v0, T4 const& trackTable) + + // checks track1-track2, track1-v0 and track2-v0; fills the deta-dphi/kinematic + // histograms of each pair internally and returns whether the triplet is rejected. + template + [[nodiscard]] bool isCloseTriplet(T1 const& track1, T2 const& track2, T3 const& v0, T4 const& trackTable, T5 const& tripletHistManager) { - mCtrTrack12.setPair(track1, track2, trackTable); - mCtrTrack1V0.setPair(track1, v0, trackTable); - mCtrTrack2V0.setPair(track2, v0, trackTable); + bool isClose12 = mCtrTrack12.isClosePair(track1, track2, trackTable, tripletHistManager); + bool isClose1V0 = mCtrTrack1V0.isClosePair(track1, v0, trackTable, tripletHistManager); + bool isClose2V0 = mCtrTrack2V0.isClosePair(track2, v0, trackTable, tripletHistManager); + return isClose12 || isClose1V0 || isClose2V0; } - bool isCloseTriplet() const + + private: + closepairrejection::ClosePairRejectionTrackTrack mCtrTrack12; + closepairrejection::ClosePairRejectionTrackV0 mCtrTrack1V0; + closepairrejection::ClosePairRejectionTrackV0 mCtrTrack2V0; +}; + +template +class CloseTripletRejectionTrackTrackCascade +{ + public: + CloseTripletRejectionTrackTrackCascade() = default; + ~CloseTripletRejectionTrackTrackCascade() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& specs, + std::map> const& specsBachelor, + std::map> const& specsV0Daughter, + T1 const& confCpr, + T2 const& confCprBachelor, + T3 const& confCprV0Daughter, + int absChargeTrack1, + int absChargeTrack2) { - return mCtrTrack12.isClosePair() || mCtrTrack1V0.isClosePair() || mCtrTrack2V0.isClosePair(); + mCtrTrack12.init(registry, specs, confCpr, absChargeTrack1, absChargeTrack2); + mCtrTrack1Cascade.init(registry, specsBachelor, specsV0Daughter, confCprBachelor, confCprV0Daughter, absChargeTrack1); + mCtrTrack2Cascade.init(registry, specsBachelor, specsV0Daughter, confCprBachelor, confCprV0Daughter, absChargeTrack2); } - void fill(float q3) + void setMagField(float magField) { - mCtrTrack12.fill(q3); - mCtrTrack1V0.fill(q3); - mCtrTrack2V0.fill(q3); + mCtrTrack12.setMagField(magField); + mCtrTrack1Cascade.setMagField(magField); + mCtrTrack2Cascade.setMagField(magField); + } + + // checks track1-track2, track1-cascade and track2-cascade; fills the deta-dphi/kinematic + // histograms of each pair internally and returns whether the triplet is rejected. + template + [[nodiscard]] bool isCloseTriplet(T1 const& track1, T2 const& track2, T3 const& cascade, T4 const& trackTable, T5 const& tripletHistManager) + { + bool isClose12 = mCtrTrack12.isClosePair(track1, track2, trackTable, tripletHistManager); + bool isClose1Cascade = mCtrTrack1Cascade.isClosePair(track1, cascade, trackTable, tripletHistManager); + bool isClose2Cascade = mCtrTrack2Cascade.isClosePair(track2, cascade, trackTable, tripletHistManager); + return isClose12 || isClose1Cascade || isClose2Cascade; } private: closepairrejection::ClosePairRejectionTrackTrack mCtrTrack12; - closepairrejection::ClosePairRejectionTrackV0 mCtrTrack1V0; - closepairrejection::ClosePairRejectionTrackV0 mCtrTrack2V0; + closepairrejection::ClosePairRejectionTrackCascade mCtrTrack1Cascade; + closepairrejection::ClosePairRejectionTrackCascade mCtrTrack2Cascade; +}; + +// close triplet rejection for triplets of generated particles; checks all three constituent pairs +template +class CloseTripletRejectionMcParticleMcParticleMcParticle +{ + public: + CloseTripletRejectionMcParticleMcParticleMcParticle() = default; + ~CloseTripletRejectionMcParticleMcParticleMcParticle() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& specs, + T1 const& confCpr) + { + mCtr12.init(registry, specs, confCpr); + mCtr23.init(registry, specs, confCpr); + mCtr13.init(registry, specs, confCpr); + } + + template + [[nodiscard]] bool isCloseTriplet(T1 const& particle1, T2 const& particle2, T3 const& particle3, T4 const& tripletHistManager) + { + bool isClose12 = mCtr12.isClosePair(particle1, particle2, tripletHistManager); + bool isClose23 = mCtr23.isClosePair(particle2, particle3, tripletHistManager); + bool isClose13 = mCtr13.isClosePair(particle1, particle3, tripletHistManager); + return isClose12 || isClose23 || isClose13; + } + + private: + closepairrejection::ClosePairRejectionMcParticleMcParticle mCtr12; + closepairrejection::ClosePairRejectionMcParticleMcParticle mCtr23; + closepairrejection::ClosePairRejectionMcParticleMcParticle mCtr13; }; -}; // namespace closetripletrejection -}; // namespace o2::analysis::femto +} // namespace o2::analysis::femto::closetripletrejection #endif // PWGCF_FEMTO_CORE_CLOSETRIPLETREJECTION_H_ diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index 5093ed99269..03bda34b671 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -25,6 +25,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/Zorro.h" #include @@ -41,11 +42,8 @@ #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::collisionbuilder { -namespace collisionbuilder -{ - // configurables for collision selection struct ConfCollisionFilters : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionFilter"); @@ -59,43 +57,46 @@ struct ConfCollisionFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable sphericityMax{"sphericityMax", 1.f, "Maximum sphericity"}; o2::framework::Configurable magFieldMin{"magFieldMin", -5, "Minimum magnetic field strength (kG)"}; o2::framework::Configurable magFieldMax{"magFieldMax", 5, "Maximum magnetic field strength (kG)"}; - o2::framework::Configurable subGeneratorId{"subGeneratorId", 0, "MC ONLY: If positive, keep 0 = MB, <0 triggered on something"}; + o2::framework::Configurable subGeneratorId{"subGeneratorId", -1, "MC ONLY: Sub-generator ID to keep (0 = MB). Only applied if >= 0; set to -1 (default) to disable and keep all sub-generators"}; }; struct ConfCollisionBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionBits"); - o2::framework::Configurable passThrough{"passThrough", false, "If true, all tracks are passed through. Bits for all selections are stored."}; - o2::framework::Configurable sel8{"sel8", 1, "Use sel8 (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", 0, "Reject collisions in case of pileup with another collision in the same foundBC (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", 0, "At least one ITS-TPC track found for the vertex (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", 0, "small difference between z-vertex from PV and from FT0 (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeNarrow{"noCollInTimeRangeNarrow", 0, "no other collisions in specified time range (narrower than Strict)(-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeStrict{"noCollInTimeRangeStrict", 0, "no other collisions in specified time range strict (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", 0, "no other collisions in specified time range with per-collision multiplicity above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInRofStrict{"noCollInRofStrict", 0, "no other collisions in this Readout Frame strict (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noCollInRofStandard{"noCollInRofStandard", 0, "no other collisions in this Readout Frame with per-collision multiplicity above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", 0, "veto an event if FT0C amplitude in previous ITS ROF is above threshold (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayer3{"isGoodItsLayer3", 0, "number of inactive chips on ITS layer 3 is below maximum allowed value (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayer0123{"isGoodItsLayer0123", 0, "numbers of inactive chips on ITS layers 0-3 are below maximum allowed values (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable isGoodItsLayersAll{"isGoodItsLayersAll", 0, "numbers of inactive chips on all ITS layers are below maximum allowed values (-1: stored in bitmaks; 0 off; 1 on)"}; - o2::framework::Configurable> occupancyMin{"occupancyMin", {}, "Minimum occpancy"}; - o2::framework::Configurable> occupancyMax{"occupancyMax", {}, "Maximum occpancy"}; + o2::framework::Configurable passThrough{"passThrough", false, "If true, all collisions are passed through. Bits for all enabled (non-zero) selection flags are stored, disabled flags (0) are not."}; + o2::framework::Configurable sel8{"sel8", 1, "Use sel8 (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable rctFlags{"rctFlags", 1, "RCT flags ok, checker configured via CollisionRctFlags (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noSameBunchPileup{"noSameBunchPileup", 0, "Reject collisions in case of pileup with another collision in the same foundBC (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isVertexItsTpc{"isVertexItsTpc", 0, "At least one ITS-TPC track found for the vertex (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodZvtxFt0VsPv{"isGoodZvtxFt0VsPv", 0, "small difference between z-vertex from PV and from FT0 (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeNarrow{"noCollInTimeRangeNarrow", 0, "no other collisions in specified time range (narrower than Strict)(-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeStrict{"noCollInTimeRangeStrict", 0, "no other collisions in specified time range strict (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", 0, "no other collisions in specified time range with per-collision multiplicity above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInRofStrict{"noCollInRofStrict", 0, "no other collisions in this Readout Frame strict (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noCollInRofStandard{"noCollInRofStandard", 0, "no other collisions in this Readout Frame with per-collision multiplicity above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", 0, "veto an event if FT0C amplitude in previous ITS ROF is above threshold (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayer3{"isGoodItsLayer3", 0, "number of inactive chips on ITS layer 3 is below maximum allowed value (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayer0123{"isGoodItsLayer0123", 0, "numbers of inactive chips on ITS layers 0-3 are below maximum allowed values (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable isGoodItsLayerAll{"isGoodItsLayerAll", 0, "numbers of inactive chips on all ITS layers are below maximum allowed values (-1: stored in bitmask; 0 off; 1 on)"}; + o2::framework::Configurable> occupancyMin{"occupancyMin", {}, "Minimum occupancy"}; + o2::framework::Configurable> occupancyMax{"occupancyMax", {}, "Maximum occupancy"}; o2::framework::Configurable> sphericityMin{"sphericityMin", {}, "Minimum sphericity"}; o2::framework::Configurable> sphericityMax{"sphericityMax", {}, "Maximum sphericity"}; o2::framework::Configurable> triggers{"triggers", {}, "List of all triggers to be used"}; + o2::framework::Configurable eventPlaneAngleDetector{"eventPlaneAngleDetector", 0, "Detector used to estimate the event plane angle: 0 -> FT0C, 1 -> FT0A"}; + o2::framework::Configurable qvecDetector{"qvecDetector", 0, "Detector used to estimate the Q-vector: 0 -> FT0C, 1 -> FT0A"}; + o2::framework::Configurable qvecHarmonic{"qvecHarmonic", 2, "Harmonic n of the Q-vector and event plane angle Psi_n: 2 -> elliptic, 3 -> triangular"}; }; struct ConfCcdb : o2::framework::ConfigurableGroup { std::string prefix = std::string("ConfCcdb"); o2::framework::Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "URL to ccdb"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField, Run2 -> GLO/GRP/GRP"}; + o2::framework::Configurable grpPath{"grpPath", "GLO/Config/GRPMagField", "Path to GRP object (Run3 -> GLO/Config/GRPMagField, Run2 -> GLO/GRP/GRP)"}; o2::framework::Configurable triggerPath{"triggerPath", "EventFiltering/Zorro/", "CCDB path for trigger information"}; o2::framework::Configurable magFieldForced{"magFieldForced", 0, "Force value for magnetic field (kG). This will skip calls to the ccdb. Deactivate by setting value to 0"}; }; struct ConfCollisionRctFlags : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionRctFlags"); - o2::framework::Configurable useRctFlags{"useRctFlags", true, "Set to true to use RCT flags"}; o2::framework::Configurable label{"label", std::string("CBT_hadronPID"), "Which RCT flag to check"}; o2::framework::Configurable useZdc{"useZdc", false, "Whether to use ZDC (only use for PbPb)"}; o2::framework::Configurable treatLimitedAcceptanceAsBad{"treatLimitedAcceptanceAsBad", false, "Whether to treat limited acceptance as bad or not"}; @@ -112,13 +113,14 @@ struct ConfCollisionSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable centMax{"centMax", 100.f, "Maximum centrality (multiplicity percentile)"}; o2::framework::Configurable magFieldMin{"magFieldMin", -5, "Minimum magnetic field strength (kG)"}; o2::framework::Configurable magFieldMax{"magFieldMax", 5, "Maximum magnetic field strength (kG)"}; - o2::framework::Configurable collisionMask{"collisionMask", 0x0, "Bitmask for collision"}; + o2::framework::Configurable collisionMask{"collisionMask", 0x0, "Bitmask for collision"}; }; /// enum for all collision selections enum CollisionSels { - // collsion selection flags + // collision selection flags kSel8, ///< Sel8 + kRctFlags, ///< RCT flags ok kNoSameBunchPileUp, ///< Reject collisions in case of pileup with another collision in the same foundBC kIsVertexItsTpc, ///< At least one ITS-TPC track found for the vertex kIsGoodZvtxFt0VsPv, ///< small difference between z-vertex from PV and from FT0 @@ -130,7 +132,7 @@ enum CollisionSels { kNoHighMultCollInPrevRof, ///< veto an event if FT0C amplitude in previous ITS ROF is above threshold kIsGoodItsLayer3, ///< number of inactive chips on ITS layer 3 is below maximum allowed value kIsGoodItsLayer0123, ///< numbers of inactive chips on ITS layers 0-3 are below maximum allowed values - kIsGoodItsLayersAll, ///< numbers of inactive chips on all ITS layers are below maximum allowed values + kIsGoodItsLayerAll, ///< numbers of inactive chips on all ITS layers are below maximum allowed values kOccupancyMin, ///< Min. occupancy kOccupancyMax, ///< Max. occupancy kSphericityMin, ///< Min. sphericity @@ -145,37 +147,72 @@ constexpr char ColSelHistName[] = "hCollisionSelection"; const char colSelsName[] = "Collision Selection Object"; const std::unordered_map collisionSelectionNames = { {kSel8, "Sel8"}, + {kRctFlags, "RCT flags ok"}, {kNoSameBunchPileUp, "No same bunch pileup"}, {kIsVertexItsTpc, "Is vertex ITS TPC"}, {kIsGoodZvtxFt0VsPv, "Is good zvtx FT0 vs PV"}, {kNoCollInTimeRangeNarrow, "No collision in time range narrow"}, {kNoCollInTimeRangeStrict, "No collision in time range strict"}, - {kNoCollInTimeRangeStandard, "No collission in time range standard"}, - {kNoCollInRofStrict, "No collsion in ROF strict"}, + {kNoCollInTimeRangeStandard, "No collision in time range standard"}, + {kNoCollInRofStrict, "No collision in ROF strict"}, {kNoCollInRofStandard, "No collision in ROF standard"}, - {kNoHighMultCollInPrevRof, "No high mult collsions in previous ROF"}, + {kNoHighMultCollInPrevRof, "No high mult collisions in previous ROF"}, {kIsGoodItsLayer3, "Is good ITS layer 3"}, {kIsGoodItsLayer0123, "Is good ITS layer 0-3"}, - {kIsGoodItsLayersAll, "Is good ITS layer all"}, + {kIsGoodItsLayerAll, "Is good ITS layer all"}, {kOccupancyMin, "Minimum Occupancy"}, {kOccupancyMax, "Maximum Occupancy"}, {kSphericityMin, "Minimum Sphericity"}, {kSphericityMax, "Maximum Sphericity"}, - - {kTriggers, "Triggers"} - + {kTriggers, "Triggers"}}; + +/// enum for all collision pre-filters (evaluated in checkFilters, before the selection bitmask) +enum CollisionFilters { + kFilterVtxZMin, + kFilterVtxZMax, + kFilterMultMin, + kFilterMultMax, + kFilterCentMin, + kFilterCentMax, + kFilterMagFieldMin, + kFilterMagFieldMax, + kFilterSphericityMin, + kFilterSphericityMax, + kFilterCollisionFiltersMax }; -template -class CollisionSelection : public BaseSelection +constexpr char CollisionFilterHistName[] = "hCollisionFilters"; +const std::unordered_map collisionFilterNames = { + {kFilterVtxZMin, "vtxZMin"}, + {kFilterVtxZMax, "vtxZMax"}, + {kFilterMultMin, "multMin"}, + {kFilterMultMax, "multMax"}, + {kFilterCentMin, "centMin"}, + {kFilterCentMax, "centMax"}, + {kFilterMagFieldMin, "magFieldMin"}, + {kFilterMagFieldMax, "magFieldMax"}, + {kFilterSphericityMin, "sphericityMin"}, + {kFilterSphericityMax, "sphericityMax"}}; + +template +class CollisionSelection : public baseselection::BaseSelection { public: CollisionSelection() = default; - ~CollisionSelection() = default; - - template - void configure(o2::framework::HistogramRegistry* registry, T1 const& filter, T2 const& config) + ~CollisionSelection() override = default; + + /// \brief Configure the collision selection: kinematic/quality filters, RCT flag checker, + /// trigger (Zorro) setup, and the selection bitmask. + /// \param registry Histogram registry. + /// \param filter ConfCollisionFilters (kinematic/quality pre-filter bounds). + /// \param config ConfCollisionBits (selection bits + trigger list + event shape settings). + /// \param confRct ConfCollisionRctFlags (RCT flag checker configuration, used if CollisionBits.rctFlags != 0). + /// \param confCcdb ConfCcdb (needed for the trigger CCDB path). + template + void configure(o2::framework::HistogramRegistry* registry, T1 const& filter, T2 const& config, T3 const& confRct, T4 const& confCcdb) { + this->init(config.passThrough.value); + // filters mVtxZMin = filter.vtxZMin.value; mVtxZMax = filter.vtxZMax.value; @@ -188,70 +225,121 @@ class CollisionSelection : public BaseSelectionaddSelection(kSel8, collisionSelectionNames.at(kSel8), 2); - this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), 2); - this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), 2); - this->addSelection(kNoCollInTimeRangeNarrow, collisionSelectionNames.at(kNoCollInTimeRangeNarrow), 2); - this->addSelection(kNoCollInTimeRangeStrict, collisionSelectionNames.at(kNoCollInTimeRangeStrict), 2); - this->addSelection(kNoCollInTimeRangeStandard, collisionSelectionNames.at(kNoCollInTimeRangeStandard), 2); - this->addSelection(kNoCollInRofStrict, collisionSelectionNames.at(kNoCollInRofStrict), 2); - this->addSelection(kNoCollInRofStandard, collisionSelectionNames.at(kNoCollInRofStandard), 2); - this->addSelection(kNoHighMultCollInPrevRof, collisionSelectionNames.at(kNoHighMultCollInPrevRof), 2); - this->addSelection(kIsGoodItsLayer3, collisionSelectionNames.at(kIsGoodItsLayer3), 2); - this->addSelection(kIsGoodItsLayer0123, collisionSelectionNames.at(kIsGoodItsLayer0123), 2); - this->addSelection(kIsGoodItsLayersAll, collisionSelectionNames.at(kIsGoodItsLayersAll), 2); - } else { - this->addSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value); - this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value); - this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value); - this->addSelection(kNoCollInTimeRangeNarrow, collisionSelectionNames.at(kNoCollInTimeRangeNarrow), config.noCollInTimeRangeNarrow.value); - this->addSelection(kNoCollInTimeRangeStrict, collisionSelectionNames.at(kNoCollInTimeRangeStrict), config.noCollInTimeRangeStrict.value); - this->addSelection(kNoCollInTimeRangeStandard, collisionSelectionNames.at(kNoCollInTimeRangeStandard), config.noCollInTimeRangeStandard.value); - this->addSelection(kNoCollInRofStrict, collisionSelectionNames.at(kNoCollInRofStrict), config.noCollInRofStrict.value); - this->addSelection(kNoCollInRofStandard, collisionSelectionNames.at(kNoCollInRofStandard), config.noCollInRofStandard.value); - this->addSelection(kNoHighMultCollInPrevRof, collisionSelectionNames.at(kNoHighMultCollInPrevRof), config.noHighMultCollInPrevRof.value); - this->addSelection(kIsGoodItsLayer3, collisionSelectionNames.at(kIsGoodItsLayer3), config.isGoodItsLayer3.value); - this->addSelection(kIsGoodItsLayer0123, collisionSelectionNames.at(kIsGoodItsLayer0123), config.isGoodItsLayer0123.value); - this->addSelection(kIsGoodItsLayersAll, collisionSelectionNames.at(kIsGoodItsLayersAll), config.isGoodItsLayersAll.value); - } - - const bool isMinimalCut = mPassThrough ? false : true; - const bool isOptionalCut = mPassThrough ? false : true; - const bool skipMostPermissiveBit = mPassThrough ? false : true; - - this->addSelection(kOccupancyMin, collisionSelectionNames.at(kOccupancyMin), config.occupancyMin.value, limits::kLowerLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kOccupancyMax, collisionSelectionNames.at(kOccupancyMax), config.occupancyMax.value, limits::kUpperLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kSphericityMin, collisionSelectionNames.at(kSphericityMin), config.sphericityMin.value, limits::kLowerLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kSphericityMax, collisionSelectionNames.at(kSphericityMax), config.sphericityMax.value, limits::kUpperLimit, skipMostPermissiveBit, isMinimalCut, false); + // RCT flag checker, only needed if the rct selection is enabled + mUseRctFlags = config.rctFlags.value != 0; + if (mUseRctFlags) { + LOG(info) << "Init RCT flag checker with label: " << confRct.label.value << "; use ZDC: " << confRct.useZdc.value << "; Limited acceptance is bad: " << confRct.treatLimitedAcceptanceAsBad.value; + mRctFlagsChecker.init(confRct.label.value, confRct.useZdc.value, confRct.treatLimitedAcceptanceAsBad.value); + } + + // trigger (Zorro) setup + if (!config.triggers.value.empty()) { + mUseTrigger = true; + for (size_t i = 0; i < config.triggers.value.size(); ++i) { + mTriggerNames += config.triggers.value[i]; + if (i != config.triggers.value.size() - 1) { + mTriggerNames += ","; + } + } + mZorro.setBaseCCDBPath(confCcdb.triggerPath.value); + } + + // event shape + mEventPlaneAngleDetector = static_cast(config.eventPlaneAngleDetector.value); + if (mEventPlaneAngleDetector >= modes::EventShapeDetector::kEventShapeDetectorLast) { + LOG(fatal) << "Detector " << static_cast(mEventPlaneAngleDetector) << " for event plane angle is not supported"; + } + + mQvecDetector = static_cast(config.qvecDetector.value); + if (mQvecDetector >= modes::EventShapeDetector::kEventShapeDetectorLast) { + LOG(fatal) << "Detector " << static_cast(mQvecDetector) << " for q-vector is not supported"; + } + + mQvecHarmonic = static_cast(config.qvecHarmonic.value); + if (mQvecHarmonic < modes::QvecHarmonic::kN2 || mQvecHarmonic >= modes::QvecHarmonic::kQvecHarmonicLast) { + LOG(fatal) << "Harmonic " << static_cast(mQvecHarmonic) << " is not supported"; + } + + this->addSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value); + this->addSelection(kRctFlags, collisionSelectionNames.at(kRctFlags), config.rctFlags.value); + this->addComments(kRctFlags, "label = " + confRct.label.value + "; useZdc = " + std::to_string(confRct.useZdc.value) + "; treatLimitedAcceptanceAsBad = " + std::to_string(confRct.treatLimitedAcceptanceAsBad.value)); + this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value); + this->addSelection(kIsVertexItsTpc, collisionSelectionNames.at(kIsVertexItsTpc), config.isVertexItsTpc.value); + this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value); + this->addSelection(kNoCollInTimeRangeNarrow, collisionSelectionNames.at(kNoCollInTimeRangeNarrow), config.noCollInTimeRangeNarrow.value); + this->addSelection(kNoCollInTimeRangeStrict, collisionSelectionNames.at(kNoCollInTimeRangeStrict), config.noCollInTimeRangeStrict.value); + this->addSelection(kNoCollInTimeRangeStandard, collisionSelectionNames.at(kNoCollInTimeRangeStandard), config.noCollInTimeRangeStandard.value); + this->addSelection(kNoCollInRofStrict, collisionSelectionNames.at(kNoCollInRofStrict), config.noCollInRofStrict.value); + this->addSelection(kNoCollInRofStandard, collisionSelectionNames.at(kNoCollInRofStandard), config.noCollInRofStandard.value); + this->addSelection(kNoHighMultCollInPrevRof, collisionSelectionNames.at(kNoHighMultCollInPrevRof), config.noHighMultCollInPrevRof.value); + this->addSelection(kIsGoodItsLayer3, collisionSelectionNames.at(kIsGoodItsLayer3), config.isGoodItsLayer3.value); + this->addSelection(kIsGoodItsLayer0123, collisionSelectionNames.at(kIsGoodItsLayer0123), config.isGoodItsLayer0123.value); + this->addSelection(kIsGoodItsLayerAll, collisionSelectionNames.at(kIsGoodItsLayerAll), config.isGoodItsLayerAll.value); + this->addSelection(kOccupancyMin, collisionSelectionNames.at(kOccupancyMin), config.occupancyMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kOccupancyMax, collisionSelectionNames.at(kOccupancyMax), config.occupancyMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kSphericityMin, collisionSelectionNames.at(kSphericityMin), config.sphericityMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kSphericityMax, collisionSelectionNames.at(kSphericityMax), config.sphericityMax.value, limits::kUpperLimit, true, true, false); std::vector triggerValues(config.triggers.value.size(), 1.f); - this->addSelection(kTriggers, collisionSelectionNames.at(kTriggers), triggerValues, limits::kEqualArray, false, false, isOptionalCut); + this->addSelection(kTriggers, collisionSelectionNames.at(kTriggers), triggerValues, limits::kEqualArray, false, false, true); this->addComments(kTriggers, config.triggers.value); - this->setupContainers(registry); - }; + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {collisionFilterNames.at(kFilterVtxZMin), mVtxZMin}, + {collisionFilterNames.at(kFilterVtxZMax), mVtxZMax}, + {collisionFilterNames.at(kFilterMultMin), mMultMin}, + {collisionFilterNames.at(kFilterMultMax), mMultMax}, + {collisionFilterNames.at(kFilterCentMin), mCentMin}, + {collisionFilterNames.at(kFilterCentMax), mCentMax}, + {collisionFilterNames.at(kFilterMagFieldMin), mMagFieldMin}, + {collisionFilterNames.at(kFilterMagFieldMax), mMagFieldMax}, + {collisionFilterNames.at(kFilterSphericityMin), mSphericityMin}, + {collisionFilterNames.at(kFilterSphericityMax), mSphericityMax}, + }); + } + + /// \brief Initialize the Zorro trigger machinery for a new run. No-op if no triggers configured. + template + void initTrigger(int runNumber, uint64_t timestamp, T1& ccdb, T2& histRegistry) + { + if (!mUseTrigger) { + return; + } + LOG(info) << "Init Zorro with Run Number: " << runNumber << "; timestamp: " << timestamp << "; Trigger Names: " << mTriggerNames; + mZorro.initCCDB(ccdb.service, runNumber, timestamp, mTriggerNames); + mZorro.populateHistRegistry(histRegistry, runNumber); + } + + /// \brief Fetch trigger-of-interest decisions for the given global BC. Empty if no triggers configured. + template + std::vector getTriggerDecisions(T globalBC) + { + if (!mUseTrigger) { + return {}; + } + return mZorro.getTriggerOfInterestResults(globalBC); + } void setMagneticField(int MagField) { mMagField = MagField; } - float getMagneticField() + [[nodiscard]] int getMagneticField() { return mMagField; } template - void setSphericity(T tracks) + void setSphericity(T const& tracks) { mSphericity = computeSphericity(tracks); } - float getSphericity() const { return mSphericity; } + [[nodiscard]] float getSphericity() const { return mSphericity; } template void setCentrality(const T& col) @@ -263,7 +351,7 @@ class CollisionSelection : public BaseSelection void setMultiplicity(const T& col) @@ -276,27 +364,98 @@ class CollisionSelection : public BaseSelection - bool checkFilters(T const& col) const + /// \brief Reduced flow vector |q_n| = |Q_n| * sqrt(M) for the configured detector and harmonic + template + void setQvector(T const& col) { - if (col.posZ() < mVtxZMin || col.posZ() > mVtxZMax) { - return false; + const auto index = static_cast(static_cast(mQvecHarmonic) - 2); // n=2 -> 0, n=3 -> 1 + switch (mQvecDetector) { + case modes::EventShapeDetector::kFT0C: + mQvec = computeReducedQvec(col.qvecFT0CReVec(), col.qvecFT0CImVec(), col.sumAmplFT0C(), index); + break; + case modes::EventShapeDetector::kFT0A: + mQvec = computeReducedQvec(col.qvecFT0AReVec(), col.qvecFT0AImVec(), col.sumAmplFT0A(), index); + break; + default: + LOG(fatal) << "Invalid detector for q-vector"; + break; } - if (mMultiplicity < mMultMin || mMultiplicity > mMultMax) { - return false; - } - if (mCentrality < mCentMin || mCentrality > mCentMax) { - return false; - } - if (mMagField < mMagFieldMin || mMagField > mMagFieldMax) { - return false; - } - if (mSphericity < mSphericityMin || mSphericity > mSphericityMax) { - return false; + } + [[nodiscard]] float getQvector() const { return mQvec; } + + /// \brief Event plane angle Psi_n in [0, 2pi/n) for the configured detector and harmonic + template + void setEventPlane(T const& col) + { + const int harmonic = static_cast(mQvecHarmonic); + const auto index = static_cast(harmonic - 2); // n=2 -> 0, n=3 -> 1 + switch (mEventPlaneAngleDetector) { + case modes::EventShapeDetector::kFT0C: + mEventPlane = computeEventPlane(col.qvecFT0CReVec(), col.qvecFT0CImVec(), index, harmonic); + break; + case modes::EventShapeDetector::kFT0A: + mEventPlane = computeEventPlane(col.qvecFT0AReVec(), col.qvecFT0AImVec(), index, harmonic); + break; + default: + LOG(fatal) << "Invalid detector for event plane angle"; + break; } - return true; + } + [[nodiscard]] float getEventPlane() const { return mEventPlane; } + + /// \brief Evaluate all pre-filters (kinematics, quality, RCT flags) for a collision candidate, + /// filling one filter-histogram bin per bound plus the "All analyzed"/"All passed" summary bins. + template + bool checkFilters(T const& col) const + { + bool pass = true; + bool p = false; + + p = col.posZ() >= mVtxZMin; + this->template fillFilter(kFilterVtxZMin, p); + pass &= p; + + p = col.posZ() <= mVtxZMax; + this->template fillFilter(kFilterVtxZMax, p); + pass &= p; + + p = mMultiplicity >= mMultMin; + this->template fillFilter(kFilterMultMin, p); + pass &= p; + + p = mMultiplicity <= mMultMax; + this->template fillFilter(kFilterMultMax, p); + pass &= p; + + p = mCentrality >= mCentMin; + this->template fillFilter(kFilterCentMin, p); + pass &= p; + + p = mCentrality <= mCentMax; + this->template fillFilter(kFilterCentMax, p); + pass &= p; + + p = mMagField >= mMagFieldMin; + this->template fillFilter(kFilterMagFieldMin, p); + pass &= p; + + p = mMagField <= mMagFieldMax; + this->template fillFilter(kFilterMagFieldMax, p); + pass &= p; + + p = mSphericity >= mSphericityMin; + this->template fillFilter(kFilterSphericityMin, p); + pass &= p; + + p = mSphericity <= mSphericityMax; + this->template fillFilter(kFilterSphericityMax, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; } template @@ -304,7 +463,7 @@ class CollisionSelection : public BaseSelectionreset(); - // casting bool to float gurantees false -> 0 and true -> 1 + // casting bool to float guarantees false -> 0 and true -> 1 // and we check for equality to 1, so evaluation succeeds if the selection bit is true this->evaluateObservable(kSel8, static_cast(col.sel8())); this->evaluateObservable(kNoSameBunchPileUp, static_cast(col.selection_bit(o2::aod::evsel::kNoSameBunchPileup))); @@ -312,12 +471,17 @@ class CollisionSelection : public BaseSelectionevaluateObservable(kIsGoodZvtxFt0VsPv, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV))); this->evaluateObservable(kNoCollInTimeRangeNarrow, static_cast(col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow))); this->evaluateObservable(kNoCollInTimeRangeStrict, static_cast(col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict))); + this->evaluateObservable(kNoCollInTimeRangeStandard, static_cast(col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))); this->evaluateObservable(kNoCollInRofStrict, static_cast(col.selection_bit(o2::aod::evsel::kNoCollInRofStrict))); this->evaluateObservable(kNoCollInRofStandard, static_cast(col.selection_bit(o2::aod::evsel::kNoCollInRofStandard))); this->evaluateObservable(kNoHighMultCollInPrevRof, static_cast(col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof))); this->evaluateObservable(kIsGoodItsLayer3, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodITSLayer3))); this->evaluateObservable(kIsGoodItsLayer0123, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123))); - this->evaluateObservable(kIsGoodItsLayersAll, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))); + this->evaluateObservable(kIsGoodItsLayerAll, static_cast(col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll))); + // checker is only initialized if the rct selection is enabled + if (mUseRctFlags) { + this->evaluateObservable(kRctFlags, static_cast(mRctFlagsChecker(col))); + } this->evaluateObservable(kOccupancyMin, col.trackOccupancyInTimeRange()); this->evaluateObservable(kOccupancyMax, col.trackOccupancyInTimeRange()); @@ -331,17 +495,15 @@ class CollisionSelection : public BaseSelectionevaluateObservable(kTriggers, trigger); } - this->assembleBitmask(); - }; - - bool passThroughAllCollisions() const { return mPassThrough; } + this->assembleBitmask(); + } protected: template float computeSphericity(T const& tracks) { - int minNumberTracks = 2; - double maxSphericity = 2.f; + const int64_t minNumberTracks = 2; + const double maxSphericity = 2.f; if (tracks.size() <= minNumberTracks) { return maxSphericity; } @@ -370,9 +532,33 @@ class CollisionSelection : public BaseSelection(2. * lambda2 / (lambda1 + lambda2)); } + /// \brief |q_n| = |Q_n| * sqrt(M); returns 0 for non-positive amplitude sum to avoid NaN + template + static float computeReducedQvec(TRe const& re, TIm const& im, float sumAmpl, std::size_t index) + { + if (index >= re.size() || index >= im.size()) { + LOG(fatal) << "Requested Q-vector harmonic index " << index << " but only " << re.size() << " harmonics are stored"; + } + if (!(sumAmpl > 0.f)) { + return 0.f; + } + return static_cast(std::hypot(re[index], im[index]) * std::sqrt(sumAmpl)); + } + + /// \brief Psi_n = atan2(Im Q_n, Re Q_n) / n, constrained to [0, 2pi/n) + template + static float computeEventPlane(TRe const& re, TIm const& im, std::size_t index, int harmonic) + { + if (index >= re.size() || index >= im.size()) { + LOG(fatal) << "Requested Q-vector harmonic index " << index << " but only " << re.size() << " harmonics are stored"; + } + const double psi = std::atan2(static_cast(im[index]), static_cast(re[index])) / static_cast(harmonic); + return static_cast(RecoDecay::constrainAngle(psi, 0., static_cast(harmonic))); + } + // filter cuts float mVtxZMin = -12.f; - float mVtxZMax = -12.f; + float mVtxZMax = 12.f; float mSphericityMin = 0.f; float mSphericityMax = 1.f; float mMagFieldMin = -5.f; @@ -382,36 +568,54 @@ class CollisionSelection : public BaseSelection producedCollision; + o2::framework::Produces producedLiteCollision; o2::framework::Produces producedCollisionMask; o2::framework::Produces producedPositions; o2::framework::Produces producedSphericities; o2::framework::Produces producedMultiplicityEstimators; o2::framework::Produces producedCentralityEstimators; - o2::framework::Produces producedQns; + o2::framework::Produces producedShapes; + o2::framework::Produces producedLiteShapes; }; struct ConfCollisionTables : o2::framework::ConfigurableGroup { std::string prefix = std::string("CollisionTables"); o2::framework::Configurable produceCollisions{"produceCollisions", -1, "Produce Collisions (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteCollisions{"produceLiteCollisions", -1, "Produce Lite Collisions (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceCollisionMasks{"produceCollisionMasks", -1, "Produce Collision Masks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producePositions{"producePositions", -1, "Produce Positions (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSphericities{"produceSphericities", -1, "Produce Sphericity (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceMults{"produceMults", -1, "Produce Multiplicities (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceCents{"produceCents", -1, "Produce Centralities (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable produceQns{"produceQns", -1, "Produce Qn (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceShapes{"produceShapes", -1, "Produce Event shape variables (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteShapes{"produceLiteShapes", -1, "Produce Lite Event shape variables (-1: auto; 0 off; 1 on)"}; }; -template +template class CollisionBuilder { public: @@ -427,30 +631,30 @@ class CollisionBuilder mGrpPath = confCcdb.grpPath.value; mSubGeneratorId = confFilter.subGeneratorId.value; - if (!confBits.triggers.value.empty()) { - mUseTrigger = true; - for (size_t i = 0; i < confBits.triggers.value.size(); ++i) { - mTriggerNames += confBits.triggers.value[i]; - if (i != confBits.triggers.value.size() - 1) { - mTriggerNames += ","; - } - } - mZorro.setBaseCCDBPath(confCcdb.triggerPath.value); - } - if (confRct.useRctFlags.value) { - mUseRctFlags = true; - LOG(info) << "Init RCT flag checker with label: " << confRct.label.value << "; use ZDC: " << confRct.useZdc.value << "; Limimted acceptance is bad: " << confRct.treatLimitedAcceptanceAsBad.value; - mRctFlagsChecker.init(confRct.label.value, confRct.useZdc.value, confRct.treatLimitedAcceptanceAsBad.value); - } - mProducedCollisions = utils::enableTable("FCols_001", confTable.produceCollisions.value, initContext); + mProducedLiteCollisions = utils::enableTable("FLiteCols_001", confTable.produceLiteCollisions.value, initContext); mProducedCollisionMasks = utils::enableTable("FColMasks_001", confTable.produceCollisionMasks.value, initContext); mProducedPositions = utils::enableTable("FColPos_001", confTable.producePositions.value, initContext); mProducedSphericities = utils::enableTable("FColSphericities_001", confTable.produceSphericities.value, initContext); mProducedMultiplicities = utils::enableTable("FColMults_001", confTable.produceMults.value, initContext); mProducedCentralities = utils::enableTable("FColCents_001", confTable.produceCents.value, initContext); - mProduceQns = utils::enableTable("FColQnBins_001", confTable.produceQns.value, initContext); - if (mProducedCollisions || mProducedCollisionMasks || mProducedPositions || mProducedSphericities || mProducedMultiplicities || mProducedCentralities) { + mProducedShapes = utils::enableTable("FColShapes_001", confTable.produceShapes.value, initContext); + mProducedLiteShapes = utils::enableTable("FLiteColShapes_001", confTable.produceLiteShapes.value, initContext); + + if (mProducedCollisions && mProducedLiteCollisions) { + LOG(fatal) << "FCols and FLiteCols are mutually exclusive -- enable only one. " + << "FLiteCols is meant to only replace FCols at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FCols from FLiteCols downstream."; + } + if (mProducedShapes && mProducedLiteShapes) { + LOG(fatal) << "FColShapes and FLiteColShapes are mutually exclusive -- enable only one. " + << "FLiteColShapes is meant to only replace FColShapes at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FColShapes from FLiteColShapes downstream."; + } + + if (mProducedCollisions || mProducedLiteCollisions || mProducedCollisionMasks || + mProducedPositions || mProducedSphericities || mProducedMultiplicities || + mProducedCentralities || mProducedShapes || mProducedLiteShapes) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -458,34 +662,32 @@ class CollisionBuilder return; } - mCollisionSelection.configure(registry, confFilter, confBits); + mCollisionSelection.configure(registry, confFilter, confBits, confRct, confCcdb); mCollisionSelection.printSelections(colSelsName); LOG(info) << "Initialization done..."; } template - void initCollision(T1& bc, T2& col, T3& tracks, T4& ccdb, T5& histRegistry) + void initCollision(T1 const& bc, T2 const& col, T3 const& tracks, T4& ccdb, T5& histRegistry) { + if (!mFillAnyTable) { + return; // selection object was never configured (no collision table requested), applying selections would use an unset registry + } if (mRunNumber != bc.runNumber()) { mRunNumber = bc.runNumber(); if (mMagFieldForced == 0) { - static o2::parameters::GRPMagField* grpo = nullptr; - LOG(info) << "Get magentic field with Path: " << mGrpPath << "; Run number: " << mRunNumber; - grpo = ccdb->template getForRun(mGrpPath, mRunNumber); + o2::parameters::GRPMagField* grpo = ccdb->template getForRun(mGrpPath, mRunNumber); + LOG(info) << "Get magnetic field with Path: " << mGrpPath << "; Run number: " << mRunNumber; if (grpo == nullptr) { LOG(fatal) << "GRP object not found for Run " << mRunNumber; } mMagField = static_cast(grpo->getNominalL3Field()); // get magnetic field in kG } else { - LOG(info) << "Force magentic field to " << mMagFieldForced << "kG"; + LOG(info) << "Force magnetic field to " << mMagFieldForced << "kG"; mMagField = mMagFieldForced; } - if (mUseTrigger) { - LOG(info) << "Init Zorro with Run Number: " << mRunNumber << "; timestamp: " << bc.timestamp() << "; Trigger Names: " << mTriggerNames; - mZorro.initCCDB(ccdb.service, mRunNumber, bc.timestamp(), mTriggerNames); - mZorro.populateHistRegistry(histRegistry, mRunNumber); - } + mCollisionSelection.initTrigger(mRunNumber, bc.timestamp(), ccdb, histRegistry); } mCollisionSelection.setMagneticField(mMagField); @@ -493,29 +695,22 @@ class CollisionBuilder mCollisionSelection.template setMultiplicity(col); mCollisionSelection.template setCentrality(col); - std::vector triggerDecisions = {}; - if (mUseTrigger) { - triggerDecisions = mZorro.getTriggerOfInterestResults(bc.globalBC()); + if constexpr (utils::HasQvectors) { + mCollisionSelection.template setQvector(col); + mCollisionSelection.template setEventPlane(col); } + std::vector triggerDecisions = mCollisionSelection.getTriggerDecisions(bc.globalBC()); + mCollisionSelection.applySelections(col, triggerDecisions); } template bool checkCollision(T1 const& col) { - - if (mCollisionSelection.passThroughAllCollisions()) { - return true; - } - - // check RCT flags first - if (mUseRctFlags) { - if (!mRctFlagsChecker(col)) { - return false; - } + if (!mFillAnyTable) { + return false; // selection object was never configured, nothing to check or fill } - // make other checks return mCollisionSelection.checkFilters(col) && mCollisionSelection.passesAllRequiredSelections(); } @@ -523,6 +718,9 @@ class CollisionBuilder template bool checkCollision(T1 const& col, T2 const& /*mcCols*/) { + if (!mFillAnyTable) { + return false; // selection object was never configured, nothing to check or fill + } // check sub generator id of associated generated collision if (mSubGeneratorId >= 0) { if (col.has_mcCollision()) { @@ -533,11 +731,6 @@ class CollisionBuilder } } - // check RCT flags first - if (mUseRctFlags && !mRctFlagsChecker(col)) { - return false; - } - // make other checks return mCollisionSelection.checkFilters(col) && mCollisionSelection.passesAllRequiredSelections(); } @@ -548,17 +741,25 @@ class CollisionBuilder if (!mFillAnyTable) { return; } - if (mCollisionAlreadyFilled) { return; } - if (mProducedCollisions) { collisionProducts.producedCollision(col.posZ(), col.multNTracksPV(), mCollisionSelection.getCentrality(), static_cast(mCollisionSelection.getMagneticField())); + + mCurrentCollisionIndex = collisionProducts.producedCollision.lastIndex(); } + if (mProducedLiteCollisions) { + collisionProducts.producedLiteCollision(o2::aod::femtocollisions::lite::binPosZ(col.posZ()), + o2::aod::femtocollisions::lite::binMult(col.multNTracksPV()), + o2::aod::femtocollisions::lite::binCent(mCollisionSelection.getCentrality()), + static_cast(mCollisionSelection.getMagneticField())); + mCurrentCollisionIndex = collisionProducts.producedLiteCollision.lastIndex(); + } + if (mProducedCollisionMasks) { collisionProducts.producedCollisionMask(mCollisionSelection.getBitmask()); } @@ -573,22 +774,30 @@ class CollisionBuilder collisionProducts.producedMultiplicityEstimators( col.multFT0A(), col.multFT0C(), + col.numContrib(), col.multNTracksPVeta1(), col.multNTracksPVetaHalf(), col.trackOccupancyInTimeRange(), col.ft0cOccupancyInTimeRange()); } + if (mProducedCentralities) { collisionProducts.producedCentralityEstimators( col.centFT0A(), - col.centFT0C()); + col.centFT0C(), + col.centFT0M()); } - // PbPb specific columns - if constexpr (modes::isFlagSet(system, modes::System::kPbPb)) { - if (mProduceQns) { - collisionProducts.producedQns(utils::qn(col)); - } + if (mProducedShapes) { + collisionProducts.producedShapes( + mCollisionSelection.getQvector(), + mCollisionSelection.getEventPlane()); + } + + if (mProducedLiteShapes) { + collisionProducts.producedLiteShapes( + o2::aod::femtocollisions::lite::binQvec(mCollisionSelection.getQvector()), + o2::aod::femtocollisions::lite::binEventPlaneAngle(mCollisionSelection.getEventPlane())); } mCollisionAlreadyFilled = true; @@ -601,32 +810,41 @@ class CollisionBuilder return; } this->template fillCollision(collisionProducts, col); - mcBuilder.template fillMcCollisionWithLabel(mcProducts, col, mcCols); + mcBuilder.template fillMcCollisionWithLabel(col, mcCols, mcProducts); + } + + [[nodiscard]] int64_t collisionIndex() const { return mCurrentCollisionIndex; } + void reset() + { + mCollisionAlreadyFilled = false; + mCurrentCollisionIndex = -1; } - void reset() { mCollisionAlreadyFilled = false; } + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mCollisionSelection.isPassThrough(); } + [[nodiscard]] int subGeneratorId() const { return mSubGeneratorId; } + [[nodiscard]] bool producingCollisions() const { return mProducedCollisions; } + [[nodiscard]] bool producingLiteCollisions() const { return mProducedLiteCollisions; } private: - CollisionSelection mCollisionSelection; + CollisionSelection mCollisionSelection; bool mCollisionAlreadyFilled = false; - Zorro mZorro; - bool mUseTrigger = false; + int64_t mCurrentCollisionIndex = -1; int mRunNumber = -1; std::string mGrpPath = std::string(""); int mMagFieldForced = 0; int mMagField = 0; - aod::rctsel::RCTFlagsChecker mRctFlagsChecker; - bool mUseRctFlags = false; - std::string mTriggerNames = std::string(""); int mSubGeneratorId = -1; bool mFillAnyTable = false; bool mProducedCollisions = false; + bool mProducedLiteCollisions = false; bool mProducedCollisionMasks = false; bool mProducedPositions = false; bool mProducedSphericities = false; bool mProducedMultiplicities = false; bool mProducedCentralities = false; - bool mProduceQns = false; + bool mProducedShapes = false; + bool mProducedLiteShapes = false; }; struct CollisionBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { @@ -651,7 +869,5 @@ class CollisionBuilderDerivedToDerived } }; -} // namespace collisionbuilder -} // namespace o2::analysis::femto -; +} // namespace o2::analysis::femto::collisionbuilder #endif // PWGCF_FEMTO_CORE_COLLISIONBUILDER_H_ diff --git a/PWGCF/Femto/Core/collisionHistManager.h b/PWGCF/Femto/Core/collisionHistManager.h index 39d78a0e6ad..2c77591e0c4 100644 --- a/PWGCF/Femto/Core/collisionHistManager.h +++ b/PWGCF/Femto/Core/collisionHistManager.h @@ -16,24 +16,24 @@ #ifndef PWGCF_FEMTO_CORE_COLLISIONHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_COLLISIONHISTMANAGER_H_ +#include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/histManager.h" #include "PWGCF/Femto/Core/modes.h" +#include #include #include #include #include +#include #include #include #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::colhistmanager { -namespace colhistmanager -{ - enum ColHist { kPosZ, kMult, @@ -51,9 +51,17 @@ enum ColHist { kCentVsMult, kCentVsSphericity, kMultVsSphericity, + kFT0AvsFT0C, + // event shape + kQvector, + kEventPlaneAngle, // mc - kTrueCentVsCent, - kTrueMultVsMult, + kTruePosZ, // pure mc-truth, no reco collision (kMc without kReco) + kTrueCent, // pure mc-truth, no reco collision (kMc without kReco) + kTrueMult, // pure mc-truth, no reco collision (kMc without kReco) + kTruePosZVsPosZ, // reco-vs-truth correlation (kReco and kMc both set) + kTrueCentVsCent, // reco-vs-truth correlation (kReco and kMc both set) + kTrueMultVsMult, // reco-vs-truth correlation (kReco and kMc both set) kColHistLast }; @@ -79,32 +87,50 @@ constexpr std::array, kColHistLast> HistTable = { {kCentVsMult, o2::framework::HistType::kTH2F, "hCentVsMult", "Centrality vs Multiplicity; Centrality (%); Multiplicity"}, {kMultVsSphericity, o2::framework::HistType::kTH2F, "hMultVsSphericity", "Multiplicity vs Sphericity; Multiplicity; Sphericity"}, {kCentVsSphericity, o2::framework::HistType::kTH2F, "hCentVsSphericity", "Centrality vs Sphericity; Centrality (%); Sphericity"}, + {kFT0AvsFT0C, o2::framework::HistType::kTH2F, "hFT0AvsFT0C", "FT0A centrality vs FT0C centrality; Centrality_{FT0A} (%); Centrality_{FT0C} (%)"}, + // event shape + {kQvector, o2::framework::HistType::kTH1F, "hQvector", "q-vector; q-vector; Entries"}, + {kEventPlaneAngle, o2::framework::HistType::kTH1F, "hEventPlaneAngle", "Event Plane angle; #Psi_{EP}; Entries"}, // mc + {kTruePosZ, o2::framework::HistType::kTH1F, "hTruePosZ", "True vertex Z (mc-truth collision); V_{Z,True} (cm); Entries"}, + {kTrueCent, o2::framework::HistType::kTH1F, "hTrueCent", "True centrality (mc-truth collision); Centrality_{True} (%); Entries"}, + {kTrueMult, o2::framework::HistType::kTH1F, "hTrueMult", "True multiplicity (mc-truth collision); Multiplicity_{True}; Entries"}, + {kTruePosZVsPosZ, o2::framework::HistType::kTH2F, "hTruePosZVsPosZ", "True Vertex Z vs Vertex Z; V_{Z,True} (cm); V_{Z} (cm)"}, {kTrueCentVsCent, o2::framework::HistType::kTH2F, "hTrueCentVsCent", "True centrality vs centrality; Centrality_{True} (%); Centrality (%)"}, {kTrueMultVsMult, o2::framework::HistType::kTH2F, "hTrueMultVsMult", "True multiplicity vs multiplicity; Multiplicity_{True}; Multiplicity"}, }}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define COL_HIST_ANALYSIS_MAP(conf) \ - {kPosZ, {conf.vtxZ}}, \ - {kMult, {conf.mult}}, \ - {kCent, {conf.cent}}, \ - {kMagField, {conf.magField}}, - -#define COL_HIST_QA_MAP(confAnalysis, confQa) \ - {kPosX, {confQa.vtxXY}}, \ - {kPosY, {confQa.vtxXY}}, \ - {kPos, {confQa.vtx}}, \ - {kSphericity, {confQa.sphericity}}, \ - {kOccupancy, {confQa.occupancy}}, \ - {kPoszVsMult, {confAnalysis.vtxZ, confAnalysis.mult}}, \ - {kPoszVsCent, {confAnalysis.vtxZ, confAnalysis.cent}}, \ - {kCentVsMult, {confAnalysis.cent, confAnalysis.mult}}, \ - {kMultVsSphericity, {confAnalysis.mult, confQa.sphericity}}, \ - {kCentVsSphericity, {confBinningAnalysis.cent, confQa.sphericity}}, - -#define COL_HIST_MC_MAP(conf) \ - {kTrueMultVsMult, {conf.mult, conf.mult}}, \ - {kTrueCentVsCent, {conf.cent, conf.cent}}, + {kPosZ, {(conf).vtxZ}}, \ + {kMult, {(conf).mult}}, \ + {kCent, {(conf).cent}}, \ + {kMagField, {(conf).magField}}, \ + {kQvector, {(conf).qvector}}, \ + {kEventPlaneAngle, {(conf).eventPlaneAngle}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define COL_HIST_QA_MAP(confAnalysis, confQa) \ + {kPosX, {(confQa).vtxXY}}, \ + {kPosY, {(confQa).vtxXY}}, \ + {kPos, {(confQa).vtx}}, \ + {kSphericity, {(confQa).sphericity}}, \ + {kOccupancy, {(confQa).occupancy}}, \ + {kPoszVsMult, {(confAnalysis).vtxZ, (confAnalysis).mult}}, \ + {kPoszVsCent, {(confAnalysis).vtxZ, (confAnalysis).cent}}, \ + {kCentVsMult, {(confAnalysis).cent, (confAnalysis).mult}}, \ + {kMultVsSphericity, {(confAnalysis).mult, (confQa).sphericity}}, \ + {kCentVsSphericity, {(confAnalysis).cent, (confQa).sphericity}}, \ + {kFT0AvsFT0C, {(confAnalysis).cent, (confAnalysis).cent}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define COL_HIST_MC_MAP(conf) \ + {kTruePosZ, {(conf).vtxZ}}, \ + {kTrueCent, {(conf).cent}}, \ + {kTrueMult, {(conf).mult}}, \ + {kTruePosZVsPosZ, {(conf).vtxZ, (conf).vtxZ}}, \ + {kTrueCentVsCent, {(conf).cent, (conf).cent}}, \ + {kTrueMultVsMult, {(conf).mult, (conf).mult}}, template auto makeColHistSpecMap(const T& confBinningAnalysis) @@ -138,6 +164,10 @@ auto makeColMcQaHistSpecMap(const T1& confBinningAnalysis, const T2& confBinning COL_HIST_MC_MAP(confBinningAnalysis)}; } +// pure mc-truth collision (no reco counterpart) uses kTruePosZ/kTrueCent/kTrueMult, +// which are already included in makeColMcHistSpecMap()/makeColMcQaHistSpecMap() above — +// no separate spec-map builder needed; just don't pass a reco collision to fill(). + #undef COL_HIST_ANALYSIS_MAP #undef COL_HIST_QA_MAP #undef COL_HIST_MC_MAP @@ -148,6 +178,9 @@ struct ConfCollisionBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis mult{"mult", {200, 0, 200}, "Multiplicity binning"}; o2::framework::ConfigurableAxis cent{"cent", {100, 0.0f, 100.0f}, "Centrality (multiplicity percentile) binning"}; o2::framework::ConfigurableAxis magField{"magField", {11, -5.5, 5.5}, "Magnetic field binning"}; + o2::framework::Configurable plotEventShape{"plotEventShape", false, "Activate histograms for event shape (qvector, event plane angle)"}; + o2::framework::ConfigurableAxis qvector{"qvector", {100, 0.0f, 100.0f}, "Q-vector binning"}; + o2::framework::ConfigurableAxis eventPlaneAngle{"eventPlaneAngle", {720, 0, 1.f * o2::constants::math::PI}, "Event plane angle binning"}; }; struct ConfCollisionQaBinning : o2::framework::ConfigurableGroup { @@ -155,8 +188,8 @@ struct ConfCollisionQaBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plot2d{"plot2d", true, "Enable 2d QA histograms"}; o2::framework::ConfigurableAxis vtx{"vtx", {120, 0.f, 12.f}, "Vertex position binning"}; o2::framework::ConfigurableAxis vtxXY{"vtxXY", {100, -1.f, 1.f}, "Vertex X/Y binning"}; - o2::framework::ConfigurableAxis sphericity{"sphericity", {100, 0.f, 1.f}, "Spericity Binning"}; - o2::framework::ConfigurableAxis occupancy{"occupancy", {500, 0.f, 5000.f}, "Spericity Binning"}; + o2::framework::ConfigurableAxis sphericity{"sphericity", {100, 0.f, 1.f}, "Sphericity Binning"}; + o2::framework::ConfigurableAxis occupancy{"occupancy", {500, 0.f, 5000.f}, "Occupancy Binning"}; }; class CollisionHistManager @@ -168,18 +201,24 @@ class CollisionHistManager template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, - T const& /*ConfCollisionBinning*/) + T const& ConfCollisionBinning) { mHistogramRegistry = registry; - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + mPlotEventShape = ConfCollisionBinning.plotEventShape.value; + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { initAnalysis(Specs); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { initQa(Specs); } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + // reco-vs-truth correlation: requires BOTH a reco collision and mc info + if constexpr (isFlagSet(mode, modes::Mode::kReco) && isFlagSet(mode, modes::Mode::kMc)) { initMc(Specs); } + // pure mc-truth collision: requires mc info WITHOUT a reco collision + if constexpr (isFlagSet(mode, modes::Mode::kMc) && !isFlagSet(mode, modes::Mode::kReco)) { + initMcTruth(Specs); + } } template @@ -198,27 +237,32 @@ class CollisionHistManager init(registry, Specs, ConfCollisionBinning); } + // single-collision fill: reco-only, qa-only, or pure mc-truth (kMc without kReco) template void fill(T const& col) { - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { fillAnalysis(col); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { fillQa(col); } + if constexpr (isFlagSet(mode, modes::Mode::kMc) && !isFlagSet(mode, modes::Mode::kReco)) { + fillMc(col); + } } + // two-argument fill: reco collision + its matched mc collision, for True-vs-Reco correlation template void fill(T1 const& col, T2 const& mcCols) { - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { fillAnalysis(col); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { fillQa(col); } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco) && isFlagSet(mode, modes::Mode::kMc)) { fillMc(col, mcCols); } } @@ -231,6 +275,11 @@ class CollisionHistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kMult, HistTable), getHistDesc(kMult, HistTable), getHistType(kMult, HistTable), {Specs.at(kMult)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kCent, HistTable), getHistDesc(kCent, HistTable), getHistType(kCent, HistTable), {Specs.at(kCent)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kMagField, HistTable), getHistDesc(kMagField, HistTable), getHistType(kMagField, HistTable), {Specs.at(kMagField)}); + + if (mPlotEventShape) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kQvector, HistTable), getHistDesc(kQvector, HistTable), getHistType(kQvector, HistTable), {Specs.at(kQvector)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEventPlaneAngle, HistTable), getHistDesc(kEventPlaneAngle, HistTable), getHistType(kEventPlaneAngle, HistTable), {Specs.at(kEventPlaneAngle)}); + } } void initQa(std::map> const& Specs) @@ -247,14 +296,26 @@ class CollisionHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kCentVsMult, HistTable), getHistDesc(kCentVsMult, HistTable), getHistType(kCentVsMult, HistTable), {Specs.at(kCentVsMult)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMultVsSphericity, HistTable), getHistDesc(kMultVsSphericity, HistTable), getHistType(kMultVsSphericity, HistTable), {Specs.at(kMultVsSphericity)}); mHistogramRegistry->add(qaDir + getHistNameV2(kCentVsSphericity, HistTable), getHistDesc(kCentVsSphericity, HistTable), getHistType(kCentVsSphericity, HistTable), {Specs.at(kCentVsSphericity)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kFT0AvsFT0C, HistTable), getHistDesc(kFT0AvsFT0C, HistTable), getHistType(kFT0AvsFT0C, HistTable), {Specs.at(kFT0AvsFT0C)}); } } + // reco-vs-truth correlation histograms (kReco and kMc both set) void initMc(std::map> const& Specs) { std::string mcDir = std::string(McDir); - mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMultVsMult, HistTable), getHistDesc(kTrueMultVsMult, HistTable), getHistType(kTrueMultVsMult, HistTable), {Specs.at(kTrueMultVsMult)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePosZVsPosZ, HistTable), getHistDesc(kTruePosZVsPosZ, HistTable), getHistType(kTruePosZVsPosZ, HistTable), {Specs.at(kTruePosZVsPosZ)}); mHistogramRegistry->add(mcDir + getHistNameV2(kTrueCentVsCent, HistTable), getHistDesc(kTrueCentVsCent, HistTable), getHistType(kTrueCentVsCent, HistTable), {Specs.at(kTrueCentVsCent)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMultVsMult, HistTable), getHistDesc(kTrueMultVsMult, HistTable), getHistType(kTrueMultVsMult, HistTable), {Specs.at(kTrueMultVsMult)}); + } + + // pure mc-truth collision: 1D only, no reco collision exists (kMc without kReco) + void initMcTruth(std::map> const& Specs) + { + std::string mcDir = std::string(McDir); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePosZ, HistTable), getHistDesc(kTruePosZ, HistTable), getHistType(kTruePosZ, HistTable), {Specs.at(kTruePosZ)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueCent, HistTable), getHistDesc(kTrueCent, HistTable), getHistType(kTrueCent, HistTable), {Specs.at(kTrueCent)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMult, HistTable), getHistDesc(kTrueMult, HistTable), getHistType(kTrueMult, HistTable), {Specs.at(kTrueMult)}); } template @@ -264,6 +325,13 @@ class CollisionHistManager mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kMult, HistTable)), col.mult()); mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kCent, HistTable)), col.cent()); mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kMagField, HistTable)), col.magField()); + + if (mPlotEventShape) { + if constexpr (utils::HasEventShape) { + mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kQvector, HistTable)), col.qvec()); + mHistogramRegistry->fill(HIST(AnalysisDir) + HIST(getHistName(kEventPlaneAngle, HistTable)), col.eventPlaneAngle()); + } + } } template @@ -280,9 +348,11 @@ class CollisionHistManager mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kCentVsMult, HistTable)), col.cent(), col.mult()); mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kMultVsSphericity, HistTable)), col.mult(), col.sphericity()); mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kCentVsSphericity, HistTable)), col.cent(), col.sphericity()); + mHistogramRegistry->fill(HIST(QaDir) + HIST(getHistName(kFT0AvsFT0C, HistTable)), col.centFT0A(), col.centFT0C()); } } + // reco collision + matched mc collision: fill reco-vs-truth 2D correlations template void fillMc(T1 const& col, T2 const& /*mcCols*/) { @@ -290,13 +360,23 @@ class CollisionHistManager return; } auto mcCol = col.template fMcCol_as(); - mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueMultVsMult, HistTable)), mcCol.mult(), col.mult()); + mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTruePosZVsPosZ, HistTable)), mcCol.posZ(), col.posZ()); mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueCentVsCent, HistTable)), mcCol.cent(), col.cent()); + mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueMultVsMult, HistTable)), mcCol.mult(), col.mult()); + } + + // pure mc-truth collision: 'col' here IS the truth collision, no reco object exists + template + void fillMc(T const& col) + { + mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTruePosZ, HistTable)), col.posZ()); + mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueCent, HistTable)), col.cent()); + mHistogramRegistry->fill(HIST(McDir) + HIST(getHistName(kTrueMult, HistTable)), col.mult()); } o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + bool mPlotEventShape = false; bool mPlot2d = false; -}; // namespace femtounitedcolhistmanager -}; // namespace colhistmanager -}; // namespace o2::analysis::femto +}; +} // namespace o2::analysis::femto::colhistmanager #endif // PWGCF_FEMTO_CORE_COLLISIONHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/dataTypes.h b/PWGCF/Femto/Core/dataTypes.h index c873bf14020..d96d1cf69bf 100644 --- a/PWGCF/Femto/Core/dataTypes.h +++ b/PWGCF/Femto/Core/dataTypes.h @@ -18,9 +18,7 @@ #include -namespace o2::aod -{ -namespace femtodatatypes +namespace o2::analysis::femto::datatypes { // Note: Length of the bitmask is the limit of how many selections can be configured @@ -33,9 +31,13 @@ using TrackMaskType = uint64_t; using TrackType = uint16_t; // datatypes for v0s -using V0MaskType = uint16_t; +using V0MaskType = uint32_t; +using V0MaskType001 = uint16_t; // old data type, was too narrow using V0Type = uint16_t; +// datatypes for photons +using PhotonMaskType = uint16_t; + // datatypes for kinks using KinkMaskType = uint32_t; using KinkType = uint8_t; @@ -45,8 +47,15 @@ using TwoTrackResonanceMaskType = uint32_t; // two track resonance types using TwoTrackResonanceType = uint16_t; +// datatype for resonances built from two photons (pi0, eta, ...) +using TwoPhotonResonanceType = uint16_t; + +// datatype for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +using TrackTrackTwoPhotonResonanceType = uint8_t; + // datatypes for cascades -using CascadeMaskType = uint16_t; +using CascadeMaskType = uint32_t; +using CascadeMaskType001 = uint16_t; // old data type, was too narrow using CascadeType = uint16_t; // datatype for origin of mc particle @@ -59,8 +68,17 @@ using ParticleType = uint16_t; using MomentumType = uint16_t; using TransverseMassType = uint16_t; -} // namespace femtodatatypes +// datatype for charm hadrons +using CharmHadronMaskType = uint32_t; +using CharmHadronType = uint16_t; + +// datatypes for event shape enums +using EventShapeDetectorType = uint8_t; +using QvecHarmonicType = uint8_t; + +// datatype for kinematic variable +using KinematicVariableType = uint8_t; -} // namespace o2::aod +} // namespace o2::analysis::femto::datatypes #endif // PWGCF_FEMTO_CORE_DATATYPES_H_ diff --git a/PWGCF/Femto/Core/dndetaBuilder.h b/PWGCF/Femto/Core/dndetaBuilder.h new file mode 100644 index 00000000000..2404c573ec3 --- /dev/null +++ b/PWGCF/Femto/Core/dndetaBuilder.h @@ -0,0 +1,584 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file dndetaBuilder.h +/// \brief configuration, selections and processing for charged-particle pseudorapidity densities on femto derived data +/// \author Anton Riedel, TU München, anton.riedel@cern.ch +/// +/// Needs a femto producer run in pass-through mode (collisions, tracks and, for mc, mc particles). +/// The event trigger (e.g. a femto pair or triplet) is passed in as a callable returning bool, +/// so the same builder is used by all dN/deta tasks independent of the trigger type. +/// +/// Tracks are selected with the femto track bitmask via two partitions: +/// - global tracks (with TPC): ITS + TPC quality bits (trackbuilder::ConfTrackSelectionDndetaGlobal) +/// - ITS-only tracks (no TPC): ITS quality bits only (trackbuilder::ConfTrackSelectionDndetaItsOnly) +/// The QA of the selected tracks is done with one track histogram manager per track class. +/// This reproduces the global track selection, where the TPC cuts are only applied if the track has TPC. +/// A track is taken from the first partition only if it has TPC and from the second only if it has no TPC, +/// so no track is counted twice. + +#ifndef PWGCF_FEMTO_CORE_DNDETABUILDER_H_ +#define PWGCF_FEMTO_CORE_DNDETABUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/dndetaHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::dndetabuilder +{ + +/// general configuration +struct ConfDndeta : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Dndeta"); + o2::framework::Configurable requireInelGt0{"requireInelGt0", false, "Require INEL>0 (reco: at least one PV contributor in |eta|<1; gen: at least one charged primary in |eta|<1)"}; + o2::framework::Configurable doCorrelation{"doCorrelation", false, "Data only: fill multiplicity estimator correlations"}; + o2::framework::Configurable doMcEfficiency{"doMcEfficiency", true, "MC only: fill generated/reconstructed dN/deta for the efficiency"}; + o2::framework::Configurable doMcLoss{"doMcLoss", true, "MC only: fill event and signal loss histograms"}; + o2::framework::Configurable useGenTrigger{"useGenTrigger", false, "MC only: require the trigger also at generator level for the event/signal loss (only if the task supports it)"}; + o2::framework::Configurable multEtaMax{"multEtaMax", 0.5f, "MC only: maximum |eta| of the generated multiplicity estimator used in the event/signal loss histograms"}; + // pT variation of the generated spectrum below the threshold, to estimate the uncertainty of the extrapolation to pT = 0 + // weight = p0 + p1 * pT for pT < ptVariationThreshold, 1 otherwise + o2::framework::Configurable ptVariationThreshold{"ptVariationThreshold", 0.1f, "MC only: generated particles below this pT (GeV/c) are reweighted for the pT variations"}; + o2::framework::Configurable> ptVariationUp{"ptVariationUp", {2., -10.}, "MC only: weight of the upward pT variation, p0 + p1 * pT"}; + o2::framework::Configurable> ptVariationDown{"ptVariationDown", {0.5, 5.}, "MC only: weight of the downward pT variation, p0 + p1 * pT"}; +}; + +/// acceptance of the measurement, applied to reconstructed tracks and generated particles +/// the track quality is selected with the track partitions (bitmask) +struct ConfDndetaAcceptance : o2::framework::ConfigurableGroup { + std::string prefix = std::string("DndetaAcceptance"); + o2::framework::Configurable etaMax{"etaMax", 1.0f, "Maximum |eta| (reconstructed tracks and generated particles). The eta range of the track partitions should be at least as large"}; + // regions with possible ITS acceptance problems, off by default + o2::framework::Configurable applyExtraPhiCut{"applyExtraPhiCut", false, "Reject the phi regions below (reconstructed tracks and generated particles)"}; + o2::framework::Configurable phiGapLow{"phiGapLow", 3.07666f, "Reject phiGapLow < phi < phiGapHigh"}; + o2::framework::Configurable phiGapHigh{"phiGapHigh", 3.12661f, "Reject phiGapLow < phi < phiGapHigh"}; + o2::framework::Configurable phiEdgeLow{"phiEdgeLow", 0.03f, "Reject phi <= phiEdgeLow"}; + o2::framework::Configurable phiEdgeHigh{"phiEdgeHigh", 6.253f, "Reject phi >= phiEdgeHigh"}; +}; + +constexpr double MinAbsChargeInUnitsOfThird = 3.; // TParticlePDG::Charge() is given in units of |e|/3 +constexpr float InelGt0EtaMax = 1.f; // INEL>0 is defined for |eta| < 1 (reco side: multNTracksPVeta1) +constexpr std::size_t NPtVariationParameters = 2; + +class DndetaBuilder +{ + public: + DndetaBuilder() = default; + ~DndetaBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confDndeta, + T2 const& confAcceptance, + T3 const& confCollisionSelection, + T4 const& confTracksGlobal, + T5 const& confTrackBinningGlobal, + T6 const& confTrackQaBinningGlobal, + T7 const& confTracksItsOnly, + T8 const& confTrackBinningItsOnly, + T9 const& confTrackQaBinningItsOnly, + T10 const& confBinning, + bool isMc, + bool doStrangeness) + { + // general + mRequireInelGt0 = confDndeta.requireInelGt0.value; + mDoCorrelation = confDndeta.doCorrelation.value; + mDoMcEfficiency = confDndeta.doMcEfficiency.value; + mDoMcLoss = confDndeta.doMcLoss.value; + mUseGenTrigger = confDndeta.useGenTrigger.value; + mMultEtaMax = confDndeta.multEtaMax.value; + mPtVariationThreshold = confDndeta.ptVariationThreshold.value; + mPtVariationUp = confDndeta.ptVariationUp.value; + mPtVariationDown = confDndeta.ptVariationDown.value; + if (mPtVariationUp.size() != NPtVariationParameters || mPtVariationDown.size() != NPtVariationParameters) { + LOG(fatal) << "Dndeta.ptVariationUp and Dndeta.ptVariationDown need exactly " << NPtVariationParameters << " parameters (p0 + p1 * pT). Breaking..."; + } + + // collision selection + mVtxZMin = confCollisionSelection.vtxZMin.value; + mVtxZMax = confCollisionSelection.vtxZMax.value; + mMultMin = confCollisionSelection.multMin.value; + mMultMax = confCollisionSelection.multMax.value; + mCentMin = confCollisionSelection.centMin.value; + mCentMax = confCollisionSelection.centMax.value; + mMagFieldMin = confCollisionSelection.magFieldMin.value; + mMagFieldMax = confCollisionSelection.magFieldMax.value; + mCollisionMask = confCollisionSelection.collisionMask.value; + + // acceptance + mEtaMax = confAcceptance.etaMax.value; + mApplyExtraPhiCut = confAcceptance.applyExtraPhiCut.value; + mPhiGapLow = confAcceptance.phiGapLow.value; + mPhiGapHigh = confAcceptance.phiGapHigh.value; + mPhiEdgeLow = confAcceptance.phiEdgeLow.value; + mPhiEdgeHigh = confAcceptance.phiEdgeHigh.value; + + // the dndeta track selections reuse the standard track selection object, check that it is configured for charged particle counting + checkTrackSelection(confTracksGlobal); + checkTrackSelection(confTracksItsOnly); + + // track QA (analysis + QA histograms, also in mc; the mc track classes are part of the dndeta histograms, + // the mc histograms of the track histogram manager assume a single particle species) + mTrackHistManagerGlobal.template init(registry, trackhistmanager::makeTrackQaHistSpecMap(confTrackBinningGlobal, confTrackQaBinningGlobal), confTracksGlobal, confTrackQaBinningGlobal); + mTrackHistManagerItsOnly.template init(registry, trackhistmanager::makeTrackQaHistSpecMap(confTrackBinningItsOnly, confTrackQaBinningItsOnly), confTracksItsOnly, confTrackQaBinningItsOnly); + + // histograms + uint32_t blocks = dndetahistmanager::kBlockEvent; + if (isMc) { + if (mDoMcEfficiency) { + blocks |= dndetahistmanager::kBlockMcEfficiency; + } + if (mDoMcLoss) { + blocks |= dndetahistmanager::kBlockLoss; + } + } else { + blocks |= dndetahistmanager::kBlockData; + if (mDoCorrelation) { + blocks |= dndetahistmanager::kBlockCorrelation; + } + if (doStrangeness) { + blocks |= dndetahistmanager::kBlockStrangeness; + } + } + mHistManager.init(registry, dndetahistmanager::makeDndetaHistSpecMap(confBinning), blocks); + } + + [[nodiscard]] bool useGenTrigger() const { return mUseGenTrigger; } + + // --------------------------------------------------------------------------- + // data + // --------------------------------------------------------------------------- + + /// \return true if the collision was selected (including the trigger) + template + bool processData(T1 const& col, T2& partitionGlobal, T3& partitionItsOnly, T4& cache, T5 const& trigger) + { + if (!selectCollision(col, trigger)) { + return false; + } + const float posZ = col.posZ(); + const float cent = col.cent(); + mHistManager.fillEvent(posZ, cent); + + auto tracksGlobal = partitionGlobal->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto tracksItsOnly = partitionItsOnly->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + int nch = 0; + for (auto const& track : tracksGlobal) { + if (!isTrackAccepted(track, true)) { + continue; + } + nch++; + mTrackHistManagerGlobal.template fill(track, tracksGlobal); + mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), true); + } + for (auto const& track : tracksItsOnly) { + if (!isTrackAccepted(track, false)) { + continue; + } + nch++; + mTrackHistManagerItsOnly.template fill(track, tracksItsOnly); + mHistManager.fillDataTrack(posZ, cent, track.eta(), track.phi(), false); + } + if (mDoCorrelation) { + mHistManager.fillCorrelation(static_cast(nch), col.mult(), col.multFT0A(), col.multFT0C()); + } + return true; + } + + /// fill strangeness yields of a selected collision; lambdas and k0shorts are the already selected slices of this collision + template + void processStrangeness(T1 const& col, T2 const& lambdas, T3 const& k0shorts) + { + const float cent = col.cent(); + mHistManager.fillStrangenessCollision(col.posZ(), cent); + for (auto const& lambda : lambdas) { + mHistManager.fillLambda(cent, lambda.eta(), lambda.mass(), lambda.sign() < 0); + } + for (auto const& k0short : k0shorts) { + mHistManager.fillK0short(cent, k0short.eta(), k0short.mass()); + } + } + + // --------------------------------------------------------------------------- + // mc + // --------------------------------------------------------------------------- + + /// timeframe-level mc processing + /// - reco: for every mc collision only the reconstructed collision with the most PV contributors is used + /// (emulates bestCollisionIndex); it has to pass the full event selection including the reco trigger + /// - gen: efficiency histograms for all generated collisions, loss histograms for the generated event class + /// (optional INEL>0, optional generator-level trigger); the loss block's reco requirement is Sel8 + /// only (no vtxZ, see isSelectedByMaskOnly), the vtxZ cut only gates the efficiency block above + template + void processMc(T1 const& cols, + T2 const& mcCols, + T3& partitionGlobal, + T4& partitionItsOnly, + T5& cache, + T6 const& mcParticles, + T7 const& mcMothers, + T8 const& perMcColParticles, + T9& pdg, + T10 const& recoTrigger, + T11 const& genTrigger) + { + const auto nMcCols = static_cast(mcCols.size()); + + // best reconstructed collision per mc collision (largest number of PV contributors, as in mcCollisionExtra) + std::vector bestReco(nMcCols, -1); + std::vector bestNumContrib(nMcCols, -1); + for (auto const& col : cols) { + if (!col.has_fMcCol()) { + continue; + } + const auto mcColIndex = static_cast(col.fMcColId()); + if (col.numContrib() > bestNumContrib[mcColIndex]) { + bestNumContrib[mcColIndex] = col.numContrib(); + bestReco[mcColIndex] = col.globalIndex(); + } + } + + // reconstructed side + // selectedReco: full "selvtx" selection (Sel8 + |vtxZ| < cut + trigger), feeds the Gen-efficiency block (Ctrk) + // hasSel8Reco: "sel" selection (Sel8 only, no vtxZ, no trigger), feeds the event/signal loss block (Csel); + // per the analysis note, Csel's Gsel/Esel correspond to the Sel8 class only, Cvtx (the vtxZ-related bias) is + // not implemented (every Run3 event with a vertex is already within the stored range), so the loss-block + // reco requirement must not carry the vtxZ cut like the Ctrk-side "selvtx" one does + std::vector selectedReco(nMcCols, -1); + std::vector hasSel8Reco(nMcCols, false); + for (auto const& col : cols) { + if (!col.has_fMcCol() || bestReco[static_cast(col.fMcColId())] != col.globalIndex()) { + continue; + } + if (isSelectedByMaskOnly(col)) { + hasSel8Reco[static_cast(col.fMcColId())] = true; + } + if (!selectCollision(col, [&]() { + return recoTrigger(col); + })) { + continue; + } + selectedReco[static_cast(col.fMcColId())] = col.globalIndex(); + if (mDoMcEfficiency) { + auto tracksGlobal = partitionGlobal->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto tracksItsOnly = partitionItsOnly->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + fillRecoMc(col, tracksGlobal, tracksItsOnly, mcParticles, mcMothers); + } + } + + // generated side + for (auto const& mcCol : mcCols) { + const auto mcColIndex = static_cast(mcCol.globalIndex()); + const bool hasSelectedReco = selectedReco[mcColIndex] >= 0; + auto particles = mcParticles.sliceBy(perMcColParticles, mcCol.globalIndex()); + + // efficiency: centrality of the selected reconstructed collision (underflow if there is none) + if (mDoMcEfficiency) { + const float recoCent = hasSelectedReco ? cols.rawIteratorAt(selectedReco[mcColIndex]).cent() : -1.f; + mHistManager.fillGenCollision(mcCol.posZ(), recoCent, hasSelectedReco); + for (auto const& particle : particles) { + if (!isGenParticleSelected(particle, pdg)) { + continue; + } + mHistManager.fillGenParticle(mcCol.posZ(), recoCent, particle.eta(), particle.phi(), genType(particle.pdgCode()), hasSelectedReco, + ptVariationWeight(particle.pt(), mPtVariationUp), ptVariationWeight(particle.pt(), mPtVariationDown)); + } + } + + // event and signal loss: generated event class (INEL or INEL>0), true centrality + // Gall/Gsel (and Eall/Esel) are defined by the analysis note without any vtxZ restriction - the vtxZ + // cut belongs only to Ctrk's "selvtx" quantities above, so it is intentionally not applied here + if (!mDoMcLoss) { + continue; + } + int nChMult = 0; + int nChEta1 = 0; + for (auto const& particle : particles) { + if (!isPhysicalPrimary(particle) || std::abs(particle.eta()) >= InelGt0EtaMax || !isCharged(particle.pdgCode(), pdg)) { + continue; + } + nChEta1++; + if (std::abs(particle.eta()) < mMultEtaMax) { + nChMult++; + } + } + if (mRequireInelGt0 && nChEta1 == 0) { + continue; + } + if (mUseGenTrigger && !genTrigger(mcCol)) { + continue; + } + const bool hasSel8RecoForLoss = hasSel8Reco[mcColIndex]; + const auto mult = static_cast(nChMult); + mHistManager.fillLossEvent(mcCol.cent(), mult, hasSel8RecoForLoss); + for (auto const& particle : particles) { + if (!isGenParticleSelected(particle, pdg)) { + continue; + } + mHistManager.fillLossParticle(particle.eta(), mcCol.cent(), mult, hasSel8RecoForLoss); + } + } + } + + private: + /// warn about settings of the standard track selection object that do not make sense for charged particle counting + template + static void checkTrackSelection(T const& conf) + { + const std::string name = conf.prefix; + if (conf.chargeSign.value != 0) { + LOG(warn) << name << ".chargeSign is " << conf.chargeSign.value << ", only one charge sign is counted. Set it to 0 for dN/deta."; + } + if (conf.maskLowMomentum.value != conf.maskHighMomentum.value) { + LOG(warn) << name << ": maskLowMomentum and maskHighMomentum differ, the track selection depends on pidThres. Set both to the same track quality mask for dN/deta."; + } + if (conf.rejectionMaskLowMomentum.value != 0 || conf.rejectionMaskHighMomentum.value != 0) { + LOG(warn) << name << ": rejection masks are set, tracks are rejected based on PID. Set them to 0 for dN/deta."; + } + LOG(info) << name << ": pT range " << conf.ptMin.value << " - " << conf.ptMax.value << " GeV/c, eta range " << conf.etaMin.value << " - " << conf.etaMax.value; + } + + /// selected by maks only class (no vtxZ, no INEL>0, no trigger; pure predicate, no cutflow side effects), used to + /// build Gsel/Esel of the event/signal loss block - kept separate from selectCollision() below, whose + /// vtxZ-gated "selvtx" result must not be reused for the loss block (see processMc) + template + bool isSelectedByMaskOnly(T const& col) const + { + return col.mult() >= mMultMin && col.mult() <= mMultMax && + col.cent() >= mCentMin && col.cent() <= mCentMax && + col.magField() >= mMagFieldMin && col.magField() <= mMagFieldMax && + (col.mask() & mCollisionMask) == mCollisionMask; + } + + /// event selection with cutflow; the trigger is evaluated last, so the trigger QA + /// only contains events that passed all other event selections + template + bool selectCollision(T1 const& col, T2 const& trigger) + { + mHistManager.fillEventCutflow(dndetahistmanager::kEventAll); + if (col.posZ() < mVtxZMin || col.posZ() > mVtxZMax) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventVtxZ); + if (col.mult() < mMultMin || col.mult() > mMultMax || + col.cent() < mCentMin || col.cent() > mCentMax || + col.magField() < mMagFieldMin || col.magField() > mMagFieldMax || + (col.mask() & mCollisionMask) != mCollisionMask) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventCollisionSelection); + if (mRequireInelGt0 && col.multNTracksPVeta1() <= 0) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventInelGt0); + if (!trigger()) { + return false; + } + mHistManager.fillEventCutflow(dndetahistmanager::kEventTrigger); + return true; + } + + /// weight of a pT variation, p[0] + p[1] * pT below the threshold, 1 above + [[nodiscard]] double ptVariationWeight(float pt, std::vector const& p) const + { + if (pt >= mPtVariationThreshold) { + return 1.; + } + return p[0] + p[1] * pt; + } + + [[nodiscard]] bool isInPhiGap(float phi) const + { + if (!mApplyExtraPhiCut) { + return false; + } + return (phi > mPhiGapLow && phi < mPhiGapHigh) || phi <= mPhiEdgeLow || phi >= mPhiEdgeHigh; + } + + /// track quality is already selected by the partition + /// \param fromTpcPartition true if the track comes from the partition with TPC quality bits + template + bool isTrackAccepted(T const& track, bool fromTpcPartition) const + { + // rows only stored to resolve a daughter index (e.g. V0 daughter from another collision) are no tracks of this collision + if (track.isDaughterOnly()) { + return false; + } + // tracks with TPC are only taken from the TPC partition and tracks without TPC only from the other one, + // so the TPC cuts apply to every track with TPC and no track is counted twice + if (track.hasTpc() != fromTpcPartition) { + return false; + } + return std::abs(track.eta()) < mEtaMax && !isInPhiGap(track.phi()); + } + + template + static bool isPhysicalPrimary(T const& particle) + { + return particle.origin() == static_cast(modes::McOrigin::kPhysicalPrimary); + } + + template + static bool isCharged(int pdgCode, T& pdg) + { + const auto* particle = pdg->GetParticle(pdgCode); + return particle != nullptr && std::abs(particle->Charge()) >= MinAbsChargeInUnitsOfThird; + } + + /// generated charged physical primary inside the acceptance + template + bool isGenParticleSelected(T1 const& particle, T2& pdg) const + { + if (!isPhysicalPrimary(particle)) { + return false; + } + if (std::abs(particle.eta()) >= mEtaMax || isInPhiGap(particle.phi())) { + return false; + } + return isCharged(particle.pdgCode(), pdg); + } + + static dndetahistmanager::GenType genType(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kPiPlus: + return dndetahistmanager::kGenPion; + case PDG_t::kKPlus: + return dndetahistmanager::kGenKaon; + case PDG_t::kProton: + return dndetahistmanager::kGenProton; + default: + return dndetahistmanager::kGenOther; + } + } + + static dndetahistmanager::RecoType recoTypeFromSpecies(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kPiPlus: + return dndetahistmanager::kRecoPion; + case PDG_t::kKPlus: + return dndetahistmanager::kRecoKaon; + case PDG_t::kProton: + return dndetahistmanager::kRecoProton; + default: + return dndetahistmanager::kRecoOther; + } + } + + template + void fillRecoMcTrack(T1 const& col, T2 const& track, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, std::vector& usedLabels) + { + const float posZ = col.posZ(); + const float cent = col.cent(); + if (!track.has_fMcParticle()) { + mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoBkg); + return; + } + auto mcParticle = track.template fMcParticle_as(); + // tracks matched to a particle from another mc collision are skipped (as in the legacy task) + if (mcParticle.fMcColId() != col.fMcColId()) { + return; + } + mHistManager.fillRecoTrack(posZ, cent, track.eta(), track.phi(), dndetahistmanager::kRecoAll); + + dndetahistmanager::RecoType type = dndetahistmanager::kRecoSecondary; + if (isPhysicalPrimary(mcParticle)) { + type = recoTypeFromSpecies(mcParticle.pdgCode()); + } + if (mcParticle.has_fMcMother()) { + const int motherPdg = mcParticle.template fMcMother_as().pdgCode(); + if (motherPdg == PDG_t::kK0Short || std::abs(motherPdg) == PDG_t::kLambda0) { + type = dndetahistmanager::kRecoWeakDecay; + } + } + if (std::find(usedLabels.begin(), usedLabels.end(), track.fMcParticleId()) != usedLabels.end()) { + type = dndetahistmanager::kRecoFake; + } + usedLabels.push_back(track.fMcParticleId()); + mHistManager.fillRecoTrack(posZ, cent, mcParticle.eta(), mcParticle.phi(), type); + } + + template + void fillRecoMc(T1 const& col, T2 const& tracksGlobal, T3 const& tracksItsOnly, T4 const& mcParticles, T5 const& mcMothers) + { + mHistManager.fillRecoCollision(col.posZ(), col.cent()); + // labels are tracked over both partitions, so a fake is also found if the other track comes from the other partition + std::vector usedLabels; + for (auto const& track : tracksGlobal) { + if (isTrackAccepted(track, true)) { + mTrackHistManagerGlobal.template fill(track, tracksGlobal); + fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels); + } + } + for (auto const& track : tracksItsOnly) { + if (isTrackAccepted(track, false)) { + mTrackHistManagerItsOnly.template fill(track, tracksItsOnly); + fillRecoMcTrack(col, track, mcParticles, mcMothers, usedLabels); + } + } + } + + dndetahistmanager::DndetaHistManager mHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerGlobal; + trackhistmanager::TrackHistManager mTrackHistManagerItsOnly; + + // general + bool mRequireInelGt0 = false; + bool mDoCorrelation = false; + bool mDoMcEfficiency = true; + bool mDoMcLoss = true; + bool mUseGenTrigger = false; + float mMultEtaMax = 0.f; + float mPtVariationThreshold = 0.f; + std::vector mPtVariationUp; + std::vector mPtVariationDown; + + // collision selection + float mVtxZMin = 0.f; + float mVtxZMax = 0.f; + float mMultMin = 0.f; + float mMultMax = 0.f; + float mCentMin = 0.f; + float mCentMax = 0.f; + int mMagFieldMin = 0; + int mMagFieldMax = 0; + datatypes::CollisionMaskType mCollisionMask = 0; + + // acceptance + float mEtaMax = 0.f; + bool mApplyExtraPhiCut = false; + float mPhiGapLow = 0.f; + float mPhiGapHigh = 0.f; + float mPhiEdgeLow = 0.f; + float mPhiEdgeHigh = 0.f; +}; + +} // namespace o2::analysis::femto::dndetabuilder + +#endif // PWGCF_FEMTO_CORE_DNDETABUILDER_H_ diff --git a/PWGCF/Femto/Core/dndetaHistManager.h b/PWGCF/Femto/Core/dndetaHistManager.h new file mode 100644 index 00000000000..69b2107d575 --- /dev/null +++ b/PWGCF/Femto/Core/dndetaHistManager.h @@ -0,0 +1,567 @@ +// Copyright 2019-2022 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file dndetaHistManager.h +/// \brief histogram manager for charged-particle pseudorapidity density measurements on femto derived data +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::dndetahistmanager +{ + +// --------------------------------------------------------------------------- +// categories used as histogram axes, with labels +// --------------------------------------------------------------------------- + +/// event selection steps, used for the cutflow histogram +enum EventCut : int { + kEventAll = 0, + kEventVtxZ, + kEventCollisionSelection, ///< multiplicity/centrality/magnetic field ranges and collision bitmask + kEventInelGt0, + kEventTrigger, ///< pair/triplet trigger (always passed for minimum bias) + kEventCutLast +}; +constexpr std::array EventCutNames = {"All", "Vertex z", "Collision selection", "INEL>0", "Trigger"}; + +/// reconstructed track types in data +enum DataTrackType : int { + kGlobalPlusIts = 0, ///< all selected tracks + kGlobalOnly, ///< tracks with TPC + kItsOnly, ///< tracks without TPC + kDataTrackTypeLast +}; +constexpr std::array DataTrackTypeNames = {"Global+ITS", "Global", "ITS only"}; + +/// generated particle species +enum GenType : int { + kGenAll = 0, + kGenPion, + kGenKaon, + kGenProton, + kGenOther, + kGenTypeLast +}; +constexpr std::array GenTypeNames = {"All", "Pion", "Kaon", "Proton", "Other"}; + +/// variations of the generated pT spectrum below the extrapolation threshold +enum GenPtVariation : int { + kPtNominal = 0, + kPtUp, + kPtDown, + kGenPtVariationLast +}; +constexpr std::array GenPtVariationNames = {"Nominal", "p_{T} up", "p_{T} down"}; + +/// classification of reconstructed tracks in MC +enum RecoType : int { + kRecoAll = 0, + kRecoPion, + kRecoKaon, + kRecoProton, + kRecoOther, + kRecoSecondary, + kRecoWeakDecay, + kRecoFake, + kRecoBkg, + kRecoTypeLast +}; +constexpr std::array RecoTypeNames = {"All", "Pion", "Kaon", "Proton", "Other", "Secondary", "Weak decay", "Fake", "No MC particle"}; + +/// generated event classes for event/signal loss +enum LossEvent : int { + kLossGenAll = 0, + kLossGenWithReco, + kLossEventLast +}; +constexpr std::array LossEventNames = {"Generated", "Generated with selected reco"}; + +// --------------------------------------------------------------------------- +// histograms +// --------------------------------------------------------------------------- + +enum DndetaHist { + // event + kEventCutflow, + kPosZ, + kCent, + kPosZVsCent, + // data + kDataDndeta, + kDataDndetaMb, + // mc: generated + kGenPosZ, + kGenPosZVsCent, + kGenAssocRecoPosZ, + kGenAssocRecoPosZVsCent, + kGenDndeta, + kGenAssocRecoDndeta, + kGenAssocRecoDndetaMb, + // mc: reconstructed + kRecoPosZ, + kRecoCent, + kRecoPosZVsCent, + kRecoDndeta, + kRecoDndetaMb, + // mc: event and signal loss + kLossEvents, + kLossCentGen, + kLossCentGenAssocReco, + kLossMultGen, + kLossMultGenAssocReco, + kLossEtaGen, + kLossEtaGenAssocReco, + kLossEtaVsCentGen, + kLossEtaVsCentGenAssocReco, + kLossEtaVsMultGen, + kLossEtaVsMultGenAssocReco, + // multiplicity correlations + kCorrNchVsFt0a, + kCorrNchVsFt0c, + kCorrNchVsNpv, + kCorrNpvVsFt0c, + // strangeness + kStrangePosZVsCent, + kK0shortCentEtaMass, + kLambdaCentEtaMass, + kAntiLambdaCentEtaMass, + kDndetaHistLast +}; + +constexpr std::string_view EventDir = "Dndeta/Event/"; +constexpr std::string_view DataDir = "Dndeta/Data/"; +constexpr std::string_view GenDir = "Dndeta/Gen/"; +constexpr std::string_view RecoDir = "Dndeta/Reco/"; +constexpr std::string_view LossDir = "Dndeta/Loss/"; +constexpr std::string_view CorrelationDir = "Dndeta/Correlation/"; +constexpr std::string_view StrangenessDir = "Dndeta/Strangeness/"; + +constexpr std::array, kDndetaHistLast> HistTable = { + { + // event + {kEventCutflow, o2::framework::HistType::kTH1F, "hEventCutflow", "Event selection; ; Entries"}, + {kPosZ, o2::framework::HistType::kTH1F, "hPosZ", "Vertex z; V_{Z} (cm); Entries"}, + {kCent, o2::framework::HistType::kTH1F, "hCent", "Centrality; Centrality (%); Entries"}, + {kPosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Vertex z vs centrality; V_{Z} (cm); Centrality (%)"}, + // data + {kDataDndeta, o2::framework::HistType::kTHnSparseF, "hDndeta", "Tracks; V_{Z} (cm); Centrality (%); #eta; #varphi; Track type"}, + {kDataDndetaMb, o2::framework::HistType::kTHnSparseF, "hDndetaMb", "Tracks (MB); V_{Z} (cm); #eta; #varphi"}, + // mc: generated + {kGenPosZ, o2::framework::HistType::kTH1F, "hPosZ", "Generated vertex z; V_{Z,gen} (cm); Entries"}, + {kGenPosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Generated vertex z vs centrality; V_{Z,gen} (cm); Centrality_{gen} (%)"}, + {kGenAssocRecoPosZ, o2::framework::HistType::kTH1F, "hAssocRecoPosZ", "Generated vertex z (with selected reco); V_{Z,gen} (cm); Entries"}, + {kGenAssocRecoPosZVsCent, o2::framework::HistType::kTH2F, "hAssocRecoPosZVsCent", "Generated vertex z vs centrality (with selected reco); V_{Z,gen} (cm); Centrality_{gen} (%)"}, + {kGenDndeta, o2::framework::HistType::kTHnSparseF, "hDndeta", "Generated primaries; V_{Z,gen} (cm); Centrality_{gen} (%); #eta; #varphi"}, + {kGenAssocRecoDndeta, o2::framework::HistType::kTHnSparseF, "hAssocRecoDndeta", "Generated primaries (with selected reco); V_{Z,gen} (cm); Centrality_{gen} (%); #eta; #varphi; Species; p_{T} variation"}, + {kGenAssocRecoDndetaMb, o2::framework::HistType::kTHnSparseF, "hAssocRecoDndetaMb", "Generated primaries (with selected reco, MB); V_{Z,gen} (cm); #eta; #varphi; Species"}, + // mc: reconstructed + {kRecoPosZ, o2::framework::HistType::kTH1F, "hPosZ", "Reconstructed vertex z; V_{Z} (cm); Entries"}, + {kRecoCent, o2::framework::HistType::kTH1F, "hCent", "Reconstructed centrality; Centrality (%); Entries"}, + {kRecoPosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Reconstructed vertex z vs centrality; V_{Z} (cm); Centrality (%)"}, + {kRecoDndeta, o2::framework::HistType::kTHnSparseF, "hDndeta", "Reconstructed tracks; V_{Z} (cm); Centrality (%); #eta; #varphi; Track class"}, + {kRecoDndetaMb, o2::framework::HistType::kTHnSparseF, "hDndetaMb", "Reconstructed tracks (MB); V_{Z} (cm); #eta; #varphi; Track class"}, + // mc: event and signal loss + {kLossEvents, o2::framework::HistType::kTH1F, "hEvents", "Generated events; ; Entries"}, + {kLossCentGen, o2::framework::HistType::kTH1F, "hCentGen", "Generated events; Centrality_{gen} (%); Entries"}, + {kLossCentGenAssocReco, o2::framework::HistType::kTH1F, "hCentGenAssocReco", "Generated events with selected reco; Centrality_{gen} (%); Entries"}, + {kLossMultGen, o2::framework::HistType::kTH1F, "hMultGen", "Generated events; N_{ch,gen}; Entries"}, + {kLossMultGenAssocReco, o2::framework::HistType::kTH1F, "hMultGenAssocReco", "Generated events with selected reco; N_{ch,gen}; Entries"}, + {kLossEtaGen, o2::framework::HistType::kTH1F, "hEtaGen", "Generated primaries; #eta; Entries"}, + {kLossEtaGenAssocReco, o2::framework::HistType::kTH1F, "hEtaGenAssocReco", "Generated primaries in events with selected reco; #eta; Entries"}, + {kLossEtaVsCentGen, o2::framework::HistType::kTH2F, "hEtaVsCentGen", "Generated primaries; #eta; Centrality_{gen} (%)"}, + {kLossEtaVsCentGenAssocReco, o2::framework::HistType::kTH2F, "hEtaVsCentGenAssocReco", "Generated primaries in events with selected reco; #eta; Centrality_{gen} (%)"}, + {kLossEtaVsMultGen, o2::framework::HistType::kTH2F, "hEtaVsMultGen", "Generated primaries; #eta; N_{ch,gen}"}, + {kLossEtaVsMultGenAssocReco, o2::framework::HistType::kTH2F, "hEtaVsMultGenAssocReco", "Generated primaries in events with selected reco; #eta; N_{ch,gen}"}, + // multiplicity correlations + {kCorrNchVsFt0a, o2::framework::HistType::kTH2F, "hNchVsFt0a", "N_{ch} vs FT0A; N_{ch}; FT0A amplitude"}, + {kCorrNchVsFt0c, o2::framework::HistType::kTH2F, "hNchVsFt0c", "N_{ch} vs FT0C; N_{ch}; FT0C amplitude"}, + {kCorrNchVsNpv, o2::framework::HistType::kTH2F, "hNchVsNpv", "N_{ch} vs N_{PV}; N_{ch}; N_{PV contributors}"}, + {kCorrNpvVsFt0c, o2::framework::HistType::kTH2F, "hNpvVsFt0c", "N_{PV} vs FT0C; N_{PV contributors}; FT0C amplitude"}, + // strangeness + {kStrangePosZVsCent, o2::framework::HistType::kTH2F, "hPosZVsCent", "Vertex z vs centrality; V_{Z} (cm); Centrality (%)"}, + {kK0shortCentEtaMass, o2::framework::HistType::kTH3F, "hK0shortCentEtaMass", "K^{0}_{S}; Centrality (%); #eta; m_{#pi#pi} (GeV/#it{c}^{2})"}, + {kLambdaCentEtaMass, o2::framework::HistType::kTH3F, "hLambdaCentEtaMass", "#Lambda; Centrality (%); #eta; m_{p#pi} (GeV/#it{c}^{2})"}, + {kAntiLambdaCentEtaMass, o2::framework::HistType::kTH3F, "hAntiLambdaCentEtaMass", "#bar{#Lambda}; Centrality (%); #eta; m_{p#pi} (GeV/#it{c}^{2})"}, + }}; + +// --------------------------------------------------------------------------- +// binning +// --------------------------------------------------------------------------- + +struct ConfDndetaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("DndetaBinning"); + o2::framework::ConfigurableAxis vtxZ{"vtxZ", {40, -20, 20}, "Vertex z binning"}; + o2::framework::ConfigurableAxis cent{"cent", {o2::framework::VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Centrality binning for the dN/deta histograms"}; + o2::framework::ConfigurableAxis centFine{"centFine", {100, 0, 100}, "Centrality binning for event and loss histograms"}; + o2::framework::ConfigurableAxis eta{"eta", {40, -2, 2}, "Pseudorapidity binning"}; + o2::framework::ConfigurableAxis phi{"phi", {o2::framework::VARIABLE_WIDTH, 0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf, o2::constants::math::PIQuarter * 3., o2::constants::math::PI, o2::constants::math::PIQuarter * 5., o2::constants::math::PIHalf * 3., o2::constants::math::PIQuarter * 7., o2::constants::math::TwoPI}, "Azimuth binning for the dN/deta histograms"}; + o2::framework::ConfigurableAxis multGen{"multGen", {500, 0, 500}, "Binning of the generated multiplicity estimator of the loss histograms (eta range: Dndeta.multEtaMax)"}; + o2::framework::ConfigurableAxis nch{"nch", {501, -0.5, 500.5}, "Number of selected tracks binning (correlations)"}; + o2::framework::ConfigurableAxis npv{"npv", {501, -0.5, 500.5}, "Number of PV contributors binning (correlations)"}; + o2::framework::ConfigurableAxis ft0a{"ft0a", {501, -0.5, 500.5}, "FT0A amplitude binning (correlations)"}; + o2::framework::ConfigurableAxis ft0c{"ft0c", {501, -0.5, 500.5}, "FT0C amplitude binning (correlations)"}; + o2::framework::ConfigurableAxis massK0short{"massK0short", {200, 0.4, 0.6}, "K0short mass binning"}; + o2::framework::ConfigurableAxis massLambda{"massLambda", {200, 1.07, 1.17}, "Lambda mass binning"}; +}; + +namespace detail +{ +/// one bin per category, centered on the integer value of the enum; labels are set after booking +template +o2::framework::AxisSpec makeCategoryAxis(std::array const& /*names*/, const char* title) +{ + return {static_cast(N), -0.5, static_cast(N) - 0.5, title}; +} +} // namespace detail + +template +auto makeDndetaHistSpecMap(T const& conf) +{ + const o2::framework::AxisSpec axisTrackType = detail::makeCategoryAxis(DataTrackTypeNames, "Track type"); + const o2::framework::AxisSpec axisGenType = detail::makeCategoryAxis(GenTypeNames, "Species"); + const o2::framework::AxisSpec axisPtVariation = detail::makeCategoryAxis(GenPtVariationNames, "p_{T} variation"); + const o2::framework::AxisSpec axisRecoType = detail::makeCategoryAxis(RecoTypeNames, "Track class"); + const o2::framework::AxisSpec axisEventCut = detail::makeCategoryAxis(EventCutNames, ""); + const o2::framework::AxisSpec axisLossEvent = detail::makeCategoryAxis(LossEventNames, ""); + + return std::map>{ + // event + {kEventCutflow, {axisEventCut}}, + {kPosZ, {conf.vtxZ}}, + {kCent, {conf.centFine}}, + {kPosZVsCent, {conf.vtxZ, conf.cent}}, + // data + {kDataDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi, axisTrackType}}, + {kDataDndetaMb, {conf.vtxZ, conf.eta, conf.phi}}, + // mc: generated + {kGenPosZ, {conf.vtxZ}}, + {kGenPosZVsCent, {conf.vtxZ, conf.cent}}, + {kGenAssocRecoPosZ, {conf.vtxZ}}, + {kGenAssocRecoPosZVsCent, {conf.vtxZ, conf.cent}}, + {kGenDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi}}, + {kGenAssocRecoDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi, axisGenType, axisPtVariation}}, + {kGenAssocRecoDndetaMb, {conf.vtxZ, conf.eta, conf.phi, axisGenType}}, + // mc: reconstructed + {kRecoPosZ, {conf.vtxZ}}, + {kRecoCent, {conf.centFine}}, + {kRecoPosZVsCent, {conf.vtxZ, conf.cent}}, + {kRecoDndeta, {conf.vtxZ, conf.cent, conf.eta, conf.phi, axisRecoType}}, + {kRecoDndetaMb, {conf.vtxZ, conf.eta, conf.phi, axisRecoType}}, + // mc: event and signal loss + {kLossEvents, {axisLossEvent}}, + {kLossCentGen, {conf.centFine}}, + {kLossCentGenAssocReco, {conf.centFine}}, + {kLossMultGen, {conf.multGen}}, + {kLossMultGenAssocReco, {conf.multGen}}, + {kLossEtaGen, {conf.eta}}, + {kLossEtaGenAssocReco, {conf.eta}}, + {kLossEtaVsCentGen, {conf.eta, conf.centFine}}, + {kLossEtaVsCentGenAssocReco, {conf.eta, conf.centFine}}, + {kLossEtaVsMultGen, {conf.eta, conf.multGen}}, + {kLossEtaVsMultGenAssocReco, {conf.eta, conf.multGen}}, + // multiplicity correlations + {kCorrNchVsFt0a, {conf.nch, conf.ft0a}}, + {kCorrNchVsFt0c, {conf.nch, conf.ft0c}}, + {kCorrNchVsNpv, {conf.nch, conf.npv}}, + {kCorrNpvVsFt0c, {conf.npv, conf.ft0c}}, + // strangeness + {kStrangePosZVsCent, {conf.vtxZ, conf.cent}}, + {kK0shortCentEtaMass, {conf.centFine, conf.eta, conf.massK0short}}, + {kLambdaCentEtaMass, {conf.centFine, conf.eta, conf.massLambda}}, + {kAntiLambdaCentEtaMass, {conf.centFine, conf.eta, conf.massLambda}}, + }; +} + +/// blocks of histograms, enabled at runtime depending on the active process functions +enum Block : uint32_t { + kBlockEvent = 1u << 0, + kBlockData = 1u << 1, + kBlockMcEfficiency = 1u << 2, + kBlockLoss = 1u << 3, + kBlockCorrelation = 1u << 4, + kBlockStrangeness = 1u << 5, +}; + +class DndetaHistManager +{ + public: + DndetaHistManager() = default; + ~DndetaHistManager() = default; + + /// \param blocks bitwise OR of Block values + void init(o2::framework::HistogramRegistry* registry, + std::map> const& Specs, + uint32_t blocks) + { + mHistogramRegistry = registry; + mBlocks = blocks; + if ((mBlocks & kBlockEvent) != 0u) { + initEvent(Specs); + } + if ((mBlocks & kBlockData) != 0u) { + initData(Specs); + } + if ((mBlocks & kBlockMcEfficiency) != 0u) { + initMcEfficiency(Specs); + } + if ((mBlocks & kBlockLoss) != 0u) { + initLoss(Specs); + } + if ((mBlocks & kBlockCorrelation) != 0u) { + initCorrelation(Specs); + } + if ((mBlocks & kBlockStrangeness) != 0u) { + initStrangeness(Specs); + } + } + + // ---- event -------------------------------------------------------------- + + void fillEventCutflow(EventCut cut) + { + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kEventCutflow, HistTable)), static_cast(cut)); + } + + void fillEvent(float posZ, float cent) + { + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kCent, HistTable)), cent); + mHistogramRegistry->fill(HIST(EventDir) + HIST(getHistName(kPosZVsCent, HistTable)), posZ, cent); + } + + // ---- data --------------------------------------------------------------- + + /// fill a selected data track (track QA is done with the track histogram managers) + void fillDataTrack(float posZ, float cent, float eta, float phi, bool hasTpc) + { + mHistogramRegistry->fill(HIST(DataDir) + HIST(getHistName(kDataDndetaMb, HistTable)), posZ, eta, phi); + mHistogramRegistry->fill(HIST(DataDir) + HIST(getHistName(kDataDndeta, HistTable)), posZ, cent, eta, phi, static_cast(kGlobalPlusIts)); + const DataTrackType type = hasTpc ? kGlobalOnly : kItsOnly; + mHistogramRegistry->fill(HIST(DataDir) + HIST(getHistName(kDataDndeta, HistTable)), posZ, cent, eta, phi, static_cast(type)); + } + + // ---- mc: generated ------------------------------------------------------ + + void fillGenCollision(float posZ, float cent, bool hasSelectedReco) + { + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenPosZVsCent, HistTable)), posZ, cent); + if (hasSelectedReco) { + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoPosZVsCent, HistTable)), posZ, cent); + } + } + + /// fill a selected generated primary + /// weightUp/weightDown are the weights of the pT variations (uncertainty of the extrapolation to pT = 0), + /// they are computed by the dndeta builder + void fillGenParticle(float posZ, float cent, float eta, float phi, GenType species, bool hasSelectedReco, double weightUp, double weightDown) + { + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenDndeta, HistTable)), posZ, cent, eta, phi); + if (!hasSelectedReco) { + return; + } + const auto all = static_cast(kGenAll); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, all, static_cast(kPtNominal)); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndetaMb, HistTable)), posZ, eta, phi, all); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, all, static_cast(kPtUp), weightUp); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, all, static_cast(kPtDown), weightDown); + + const auto spec = static_cast(species); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)), posZ, cent, eta, phi, spec, static_cast(kPtNominal)); + mHistogramRegistry->fill(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndetaMb, HistTable)), posZ, eta, phi, spec); + } + + // ---- mc: reconstructed -------------------------------------------------- + + void fillRecoCollision(float posZ, float cent) + { + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoPosZ, HistTable)), posZ); + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoCent, HistTable)), cent); + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoPosZVsCent, HistTable)), posZ, cent); + } + + /// eta/phi are the reconstructed ones for kRecoAll and kRecoBkg, the generated ones otherwise (as in the legacy task) + void fillRecoTrack(float posZ, float cent, float eta, float phi, RecoType type) + { + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoDndeta, HistTable)), posZ, cent, eta, phi, static_cast(type)); + mHistogramRegistry->fill(HIST(RecoDir) + HIST(getHistName(kRecoDndetaMb, HistTable)), posZ, eta, phi, static_cast(type)); + } + + // ---- mc: event and signal loss ------------------------------------------ + + void fillLossEvent(float cent, float multGen, bool hasSelectedReco) + { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEvents, HistTable)), static_cast(kLossGenAll)); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossCentGen, HistTable)), cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossMultGen, HistTable)), multGen); + if (hasSelectedReco) { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEvents, HistTable)), static_cast(kLossGenWithReco)); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossCentGenAssocReco, HistTable)), cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossMultGenAssocReco, HistTable)), multGen); + } + } + + void fillLossParticle(float eta, float cent, float multGen, bool hasSelectedReco) + { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaGen, HistTable)), eta); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsCentGen, HistTable)), eta, cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsMultGen, HistTable)), eta, multGen); + if (hasSelectedReco) { + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaGenAssocReco, HistTable)), eta); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsCentGenAssocReco, HistTable)), eta, cent); + mHistogramRegistry->fill(HIST(LossDir) + HIST(getHistName(kLossEtaVsMultGenAssocReco, HistTable)), eta, multGen); + } + } + + // ---- multiplicity correlations ------------------------------------------ + + void fillCorrelation(float nch, float npv, float ft0a, float ft0c) + { + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNchVsFt0a, HistTable)), nch, ft0a); + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNchVsFt0c, HistTable)), nch, ft0c); + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNchVsNpv, HistTable)), nch, npv); + mHistogramRegistry->fill(HIST(CorrelationDir) + HIST(getHistName(kCorrNpvVsFt0c, HistTable)), npv, ft0c); + } + + // ---- strangeness -------------------------------------------------------- + + void fillStrangenessCollision(float posZ, float cent) + { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kStrangePosZVsCent, HistTable)), posZ, cent); + } + + void fillK0short(float cent, float eta, float mass) + { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kK0shortCentEtaMass, HistTable)), cent, eta, mass); + } + + void fillLambda(float cent, float eta, float mass, bool isAntiLambda) + { + if (isAntiLambda) { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kAntiLambdaCentEtaMass, HistTable)), cent, eta, mass); + } else { + mHistogramRegistry->fill(HIST(StrangenessDir) + HIST(getHistName(kLambdaCentEtaMass, HistTable)), cent, eta, mass); + } + } + + private: + void add(std::string_view dir, DndetaHist hist, std::map> const& Specs) + { + mHistogramRegistry->add(std::string(dir) + getHistNameV2(hist, HistTable), getHistDesc(hist, HistTable), getHistType(hist, HistTable), {Specs.at(hist)}); + } + + template + static void setLabels(TAxis* axis, std::array const& names) + { + for (std::size_t i = 0; i < N; i++) { + axis->SetBinLabel(static_cast(i) + 1, names[i]); + } + } + + void initEvent(std::map> const& Specs) + { + add(EventDir, kEventCutflow, Specs); + setLabels(mHistogramRegistry->get(HIST(EventDir) + HIST(getHistName(kEventCutflow, HistTable)))->GetXaxis(), EventCutNames); + add(EventDir, kPosZ, Specs); + add(EventDir, kCent, Specs); + add(EventDir, kPosZVsCent, Specs); + } + + void initData(std::map> const& Specs) + { + add(DataDir, kDataDndeta, Specs); + setLabels(mHistogramRegistry->get(HIST(DataDir) + HIST(getHistName(kDataDndeta, HistTable)))->GetAxis(4), DataTrackTypeNames); + add(DataDir, kDataDndetaMb, Specs); + } + + void initMcEfficiency(std::map> const& Specs) + { + add(GenDir, kGenPosZ, Specs); + add(GenDir, kGenPosZVsCent, Specs); + add(GenDir, kGenAssocRecoPosZ, Specs); + add(GenDir, kGenAssocRecoPosZVsCent, Specs); + add(GenDir, kGenDndeta, Specs); + add(GenDir, kGenAssocRecoDndeta, Specs); + setLabels(mHistogramRegistry->get(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)))->GetAxis(4), GenTypeNames); + setLabels(mHistogramRegistry->get(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndeta, HistTable)))->GetAxis(5), GenPtVariationNames); + add(GenDir, kGenAssocRecoDndetaMb, Specs); + setLabels(mHistogramRegistry->get(HIST(GenDir) + HIST(getHistName(kGenAssocRecoDndetaMb, HistTable)))->GetAxis(3), GenTypeNames); + + add(RecoDir, kRecoPosZ, Specs); + add(RecoDir, kRecoCent, Specs); + add(RecoDir, kRecoPosZVsCent, Specs); + add(RecoDir, kRecoDndeta, Specs); + setLabels(mHistogramRegistry->get(HIST(RecoDir) + HIST(getHistName(kRecoDndeta, HistTable)))->GetAxis(4), RecoTypeNames); + add(RecoDir, kRecoDndetaMb, Specs); + setLabels(mHistogramRegistry->get(HIST(RecoDir) + HIST(getHistName(kRecoDndetaMb, HistTable)))->GetAxis(3), RecoTypeNames); + } + + void initLoss(std::map> const& Specs) + { + add(LossDir, kLossEvents, Specs); + setLabels(mHistogramRegistry->get(HIST(LossDir) + HIST(getHistName(kLossEvents, HistTable)))->GetXaxis(), LossEventNames); + add(LossDir, kLossCentGen, Specs); + add(LossDir, kLossCentGenAssocReco, Specs); + add(LossDir, kLossMultGen, Specs); + add(LossDir, kLossMultGenAssocReco, Specs); + add(LossDir, kLossEtaGen, Specs); + add(LossDir, kLossEtaGenAssocReco, Specs); + add(LossDir, kLossEtaVsCentGen, Specs); + add(LossDir, kLossEtaVsCentGenAssocReco, Specs); + add(LossDir, kLossEtaVsMultGen, Specs); + add(LossDir, kLossEtaVsMultGenAssocReco, Specs); + } + + void initCorrelation(std::map> const& Specs) + { + add(CorrelationDir, kCorrNchVsFt0a, Specs); + add(CorrelationDir, kCorrNchVsFt0c, Specs); + add(CorrelationDir, kCorrNchVsNpv, Specs); + add(CorrelationDir, kCorrNpvVsFt0c, Specs); + } + + void initStrangeness(std::map> const& Specs) + { + add(StrangenessDir, kStrangePosZVsCent, Specs); + add(StrangenessDir, kK0shortCentEtaMass, Specs); + add(StrangenessDir, kLambdaCentEtaMass, Specs); + add(StrangenessDir, kAntiLambdaCentEtaMass, Specs); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + uint32_t mBlocks = 0; +}; + +} // namespace o2::analysis::femto::dndetahistmanager + +#endif // PWGCF_FEMTO_CORE_DNDETAHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/femtoMath.h b/PWGCF/Femto/Core/femtoMath.h new file mode 100644 index 00000000000..f686b85d9b9 --- /dev/null +++ b/PWGCF/Femto/Core/femtoMath.h @@ -0,0 +1,284 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoMath.h +/// \brief shared kinematic math (kstar, Q3, mt, kt, spherical-harmonics kinematics, ...) +/// used by the pair and triplet histogram managers +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_FEMTOMATH_H_ +#define PWGCF_FEMTO_CORE_FEMTOMATH_H_ + +#include "PWGCF/Femto/Core/modes.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::femtomath +{ + +// SH reference frame selectors for computeShKinematics(), mirrors ConfPairBinning.shFrame +constexpr int ShFrameLcmsNonIdentical = 0; +constexpr int ShFrameLcmsIdentical = 1; +constexpr int ShFramePrf = 2; + +inline float getKt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + auto sum = (part1 + part2); + double kt = 0.5 * sum.Pt(); + return static_cast(kt); +} + +inline float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, modes::TransverseMassType mtType = modes::TransverseMassType::kAveragePdgMass) +{ + auto sum = part1 + part2; + double mt = 0; + double mass = 0; + switch (mtType) { + case modes::TransverseMassType::kAveragePdgMass: + mass = 0.5 * (part1.M() + part2.M()); + mt = std::hypot(0.5 * sum.Pt(), mass); + break; + case modes::TransverseMassType::kReducedPdgMass: + mass = 2. * (part1.M() * part2.M()) / (part1.M() + part2.M()); + mt = std::hypot(0.5 * sum.Pt(), mass); + break; + case modes::TransverseMassType::kMt4Vector: + mt = 0.5 * sum.Mt(); + break; + default: + LOG(fatal) << "Invalid transverse mass type, breaking..."; + } + return static_cast(mt); +} + +inline float getMinv(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + return static_cast((part1 + part2).M()); +} + +inline float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + // Use Cartesian 4-vectors: addition/M2() become pure arithmetic + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + + // Mandelstam s = (p1 + p2)^2 + const double s = (p1 + p2).M2(); + const double m1sq = p1.M2(); + const double m2sq = p2.M2(); + + // Källen function λ(s, m1^2, m2^2) = (s - m1^2 - m2^2)² - 4*m1^2*m2^2 + const double kallen = (s - m1sq - m2sq) * (s - m1sq - m2sq) - 4.0 * m1sq * m2sq; + + // k* = 0.5 * sqrt(λ/s) + return static_cast(0.5 * std::sqrt(std::max(0.0, kallen) / s)); +} + +// Alpha: azimuthal opening angle between the two particles' transverse momenta, +// i.e. phi2 - phi1 wrapped to [-pi, pi). Purely a lab-frame angular variable (no +// boosting involved) -- small |alpha| means the pair sits close together in phi, +// which is where detector effects like track splitting/merging and two-track +// resolution losses tend to show up. +inline float getAlpha(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + return static_cast(RecoDecay::constrainAngle(part2.phi() - part1.phi(), -o2::constants::math::PI)); +} + +// BetaLab: lab-frame angle between the pair's relative 3-momentum (p1 - p2) and its +// total 3-momentum (p1 + p2, i.e. the kT direction). Note the (p1 - p2) here is the +// raw 3-vector difference, not the Lorentz-invariant k* -- this is a lab-frame proxy +// for how the pair's separation axis is oriented relative to its direction of motion, +// without needing to boost to the pair rest frame. +inline float getBetaLab(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + ROOT::Math::XYZVector p1(part1.Px(), part1.Py(), part1.Pz()); + ROOT::Math::XYZVector p2(part2.Px(), part2.Py(), part2.Pz()); + + ROOT::Math::XYZVector kstar = p1 - p2; + ROOT::Math::XYZVector kt = p1 + p2; + + return static_cast(std::acos(kstar.Dot(kt) / std::sqrt(kstar.Mag2() * kt.Mag2()))); +} + +inline std::tuple computeBertschPrattLCMS(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) +{ + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector pSum = p1 + p2; + const ROOT::Math::PxPyPzEVector pDiff = p1 - p2; + + const double tPx = pSum.Px(); + const double tPy = pSum.Py(); + const double tPz = pSum.Pz(); + const double tE = pSum.E(); + + const double tPt = std::sqrt(tPx * tPx + tPy * tPy); + const double tMt = std::sqrt(tE * tE - tPz * tPz); + + static constexpr double MinTransverseMomentum = 1e-9; + if (tPt < MinTransverseMomentum || tMt < MinTransverseMomentum) { + return {0.0f, 0.0f, 0.0f}; + } + + const double betaL = tPz / tE; + const double gammaL = tE / tMt; + + // out/side/long are linear projections of the momentum, so projecting the + // difference p1-p2 directly is equivalent to (and half the divisions of) + // computing each particle's out/side/long component and subtracting. + const double qOut = (pDiff.Px() * tPx + pDiff.Py() * tPy) / tPt; + const double qSide = (pDiff.Py() * tPx - pDiff.Px() * tPy) / tPt; + const double qLong = gammaL * (pDiff.Pz() - betaL * pDiff.E()); + + return {static_cast(qOut), static_cast(qSide), static_cast(qLong)}; +} + +// Return the bin index for value given ascending bin edges, or -1 if out of range. +// edges = {e0, e1, ..., eN} defines N bins [e0,e1), [e1,e2), ..., [e_{N-1},eN). +inline int findShBin(double value, std::vector const& edges) +{ + static constexpr std::size_t MinEdgesForOneBin = 2; + if (edges.size() < MinEdgesForOneBin || value < edges.front() || value >= edges.back()) { + return -1; + } + for (std::size_t i = 0; i < edges.size() - 1; ++i) { + if (value >= edges[i] && value < edges[i + 1]) { + return static_cast(i); + } + } + return -1; +} + +// Kinematics feeding the spherical-harmonics decomposition, ported 1:1 from +// FemtoUniverseMath::newpairfunc so results are numerically comparable. +// Returns {kv, out, side, long}, selected by shFrame: +// ShFrameLcmsNonIdentical: {kstar, fDKOut, fDKSide, fDKLong} (LCMS components of particle 1) +// ShFrameLcmsIdentical: {qinv, outLCMS, sideLCMS, longLCMS} (LCMS q-differences) +// ShFramePrf: {|qPRF|, outPRF, sidePRF, longPRF} (matches FemtoUniverse isIdenPRF=true) +inline std::tuple computeShKinematics(ROOT::Math::PtEtaPhiMVector const& part1, + ROOT::Math::PtEtaPhiMVector const& part2, + int shFrame) +{ + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector sum = p1 + p2; + + const double tPx = sum.Px(); + const double tPy = sum.Py(); + const double tPz = sum.Pz(); + const double tE = sum.E(); + const double tPtSq = tPx * tPx + tPy * tPy; + const double tMtSq = tE * tE - tPz * tPz; + + static constexpr double MinTransverseMomentum = 1e-9; + if (tPtSq < MinTransverseMomentum || tMtSq < MinTransverseMomentum) { + return {0.f, 0.f, 0.f, 0.f}; + } + const double tPt = std::sqrt(tPtSq); + const double tMt = std::sqrt(tMtSq); + const double tM = std::sqrt(std::max(0.0, tMtSq - tPtSq)); + + const double beta = tPz / tE; + const double gamma = tE / tMt; + + // LCMS components of particle 1 (fDKOut/Side/Long in newpairfunc) + const double e1 = p1.E(); + const double fDKOut = (p1.Px() * tPx + p1.Py() * tPy) / tPt; + const double fDKSide = (-p1.Px() * tPy + p1.Py() * tPx) / tPt; + const double fDKLong = gamma * (p1.Pz() - beta * e1); + const double fDE = gamma * (e1 - beta * p1.Pz()); + + // LCMS components of particle 2 + const double e2 = p2.E(); + const double px2 = (p2.Px() * tPx + p2.Py() * tPy) / tPt; + const double py2 = (p2.Py() * tPx - p2.Px() * tPy) / tPt; + const double pz2 = gamma * (p2.Pz() - beta * e2); + + const double outLCMS = fDKOut - px2; + const double sideLCMS = fDKSide - py2; + const double longLCMS = fDKLong - pz2; + + if (shFrame == ShFrameLcmsIdentical) { + // LCMS identical: Ylm <- LCMS q-differences, axis <- qinv + const double qinv = std::sqrt(outLCMS * outLCMS + sideLCMS * sideLCMS + longLCMS * longLCMS); + return {static_cast(qinv), + static_cast(outLCMS), static_cast(sideLCMS), static_cast(longLCMS)}; + } + if (shFrame == ShFramePrf) { + // PRF: Ylm <- PRF q-differences, axis <- |q_PRF| (matches FemtoUniverse isIdenPRF=true) + const double pE2LCMS = gamma * (e2 - beta * p2.Pz()); + const double betaOut = tPt / tMt; + const double gammaOut = tMt / tM; + const double outPRF = gammaOut * (outLCMS - betaOut * (fDE - pE2LCMS)); + const double sidePRF = sideLCMS; + const double longPRF = longLCMS; + const double qPRF = std::sqrt(outPRF * outPRF + sidePRF * sidePRF + longPRF * longPRF); + return {static_cast(qPRF), + static_cast(outPRF), static_cast(sidePRF), static_cast(longPRF)}; + } + // LCMS non-identical (default): Ylm <- LCMS components of particle 1, axis <- k* + const double betaOut = tPt / tMt; + const double gammaOut = tMt / tM; + const double fKOut = gammaOut * (fDKOut - betaOut * fDE); + const double kstar = std::sqrt(fKOut * fKOut + fDKSide * fDKSide + fDKLong * fDKLong); + return {static_cast(kstar), + static_cast(fDKOut), static_cast(fDKSide), static_cast(fDKLong)}; +} + +template +inline float getPairPhiFromEventPlaneAngle(const ROOT::Math::PtEtaPhiMVector& part1, const ROOT::Math::PtEtaPhiMVector& part2, const T1& col) +{ + const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; + float phi = RecoDecay::constrainAngle(trackSum.Phi() - col.eventPlaneAngle()); + return phi; +} + +// triplet-only helpers + +inline ROOT::Math::PxPyPzEVector getqij(ROOT::Math::PxPyPzEVector const& vi, ROOT::Math::PxPyPzEVector const& vj) +{ + auto trackSum = vi + vj; + auto trackDifference = vi - vj; + const double s = trackSum.M2(); + const double scaling = (s != 0.0) ? (vi.M2() - vj.M2()) / s : 0.0; + return trackDifference - scaling * trackSum; +} + +inline float getQ3(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) +{ + // upfront conversion to PxPyPzEVector + const ROOT::Math::PxPyPzEVector p1(part1); + const ROOT::Math::PxPyPzEVector p2(part2); + const ROOT::Math::PxPyPzEVector p3(part3); + auto q12 = getqij(p1, p2); + auto q23 = getqij(p2, p3); + auto q31 = getqij(p3, p1); + double q = q12.M2() + q23.M2() + q31.M2(); + return static_cast(std::sqrt(std::max(0.0, -q))); +} + +} // namespace o2::analysis::femto::femtomath + +#endif // PWGCF_FEMTO_CORE_FEMTOMATH_H_ diff --git a/PWGCF/Femto/Core/femtoSpherHarMath.h b/PWGCF/Femto/Core/femtoSpherHarMath.h new file mode 100644 index 00000000000..65cd7e98dd5 --- /dev/null +++ b/PWGCF/Femto/Core/femtoSpherHarMath.h @@ -0,0 +1,230 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoSpherHarMath.h +/// \brief Container for the calculation of spherical harmonics components +/// \author Pritam Chakraborty, WUT Warsaw, pritam.chakraborty@pw.edu.pl + +#ifndef PWGCF_FEMTO_CORE_FEMTOSPHERHARMATH_H_ +#define PWGCF_FEMTO_CORE_FEMTOSPHERHARMATH_H_ + +#include + +#include +#include +#include + +namespace o2::analysis::femto +{ + +/// \class SpherHarMath +/// \brief Container for math calculations of quantities related to pairs +class SpherHarMath +{ + public: + static constexpr int MaxSupportedL = 5; // analytic Ylm implemented up to l=5 + static constexpr int TrigCacheSize = MaxSupportedL + 1; // sin/cos powers cache size (indices 0..5) + static constexpr double SmallLength = 1e-10; // numerical guard for |r|, |z| + + // thresholds for legendreUpToYlm(): the l=N block is only filled once lmax reaches N + static constexpr int MinLmaxForL3Block = 3; + static constexpr int MinLmaxForL4Block = 4; + static constexpr int MinLmaxForL5Block = 5; + + /// Values of various coefficients + void initializeYlms() + { + double oneoversqrtpi = 1.0 / std::sqrt(o2::constants::math::PI); + + // l=0 prefactors + fgPrefactors[0] = 0.5 * oneoversqrtpi; + + // l=1 prefactors + fgPrefactors[1] = 0.5 * std::sqrt(3.0 / 2.0) * oneoversqrtpi; + fgPrefactors[2] = 0.5 * std::sqrt(3.0) * oneoversqrtpi; + fgPrefactors[3] = -fgPrefactors[1]; + + // l=2 prefactors + fgPrefactors[4] = 0.25 * std::sqrt(15.0 / 2.0) * oneoversqrtpi; + fgPrefactors[5] = 0.5 * std::sqrt(15.0 / 2.0) * oneoversqrtpi; + fgPrefactors[6] = 0.25 * std::sqrt(5.0) * oneoversqrtpi; + fgPrefactors[7] = -fgPrefactors[5]; + fgPrefactors[8] = fgPrefactors[4]; + + // l=3 prefactors + fgPrefactors[9] = 0.125 * std::sqrt(35.0) * oneoversqrtpi; + fgPrefactors[10] = 0.25 * std::sqrt(105.0 / 2.0) * oneoversqrtpi; + fgPrefactors[11] = 0.125 * std::sqrt(21.0) * oneoversqrtpi; + fgPrefactors[12] = 0.25 * std::sqrt(7.0) * oneoversqrtpi; + fgPrefactors[13] = -fgPrefactors[11]; + fgPrefactors[14] = fgPrefactors[10]; + fgPrefactors[15] = -fgPrefactors[9]; + + // l=4 prefactors + fgPrefactors[16] = 3.0 / 16.0 * std::sqrt(35.0 / 2.0) * oneoversqrtpi; + fgPrefactors[17] = 3.0 / 8.0 * std::sqrt(35.0) * oneoversqrtpi; + fgPrefactors[18] = 3.0 / 8.0 * std::sqrt(5.0 / 2.0) * oneoversqrtpi; + fgPrefactors[19] = 3.0 / 8.0 * std::sqrt(5.0) * oneoversqrtpi; + fgPrefactors[20] = 3.0 / 16.0 * oneoversqrtpi; + fgPrefactors[21] = -fgPrefactors[19]; + fgPrefactors[22] = fgPrefactors[18]; + fgPrefactors[23] = -fgPrefactors[17]; + fgPrefactors[24] = fgPrefactors[16]; + + // l=5 prefactors + fgPrefactors[25] = 3.0 / 32.0 * std::sqrt(77.0) * oneoversqrtpi; + fgPrefactors[26] = 3.0 / 16.0 * std::sqrt(385.0 / 2.0) * oneoversqrtpi; + fgPrefactors[27] = 1.0 / 32.0 * std::sqrt(385.0) * oneoversqrtpi; + fgPrefactors[28] = 1.0 / 8.0 * std::sqrt(1155.0 / 2.0) * oneoversqrtpi; + fgPrefactors[29] = 1.0 / 16.0 * std::sqrt(165.0 / 2.0) * oneoversqrtpi; + fgPrefactors[30] = 1.0 / 16.0 * std::sqrt(11.0) * oneoversqrtpi; + fgPrefactors[31] = -fgPrefactors[29]; + fgPrefactors[32] = fgPrefactors[28]; + fgPrefactors[33] = -fgPrefactors[27]; + fgPrefactors[34] = fgPrefactors[26]; + fgPrefactors[35] = -fgPrefactors[25]; + + fgPrefshift[0] = 0; + fgPrefshift[1] = 2; + fgPrefshift[2] = 6; + fgPrefshift[3] = 12; + fgPrefshift[4] = 20; + fgPrefshift[5] = 30; + + fgPlmshift[0] = 0; + fgPlmshift[1] = 2; + fgPlmshift[2] = 5; + fgPlmshift[3] = 9; + fgPlmshift[4] = 14; + fgPlmshift[5] = 20; + } + + /// Function to calculate Legendre Polynomials + /// \param lmax Maximum value of L component + /// \param ctheta Value of theta + /// \param lbuf values of coefficients + void legendreUpToYlm(int lmax, double ctheta, std::array& lbuf) + { + // Calculate a set of legendre polynomials up to a given l + // with spherical input + std::array sins{}; + std::array coss{}; + sins[0] = 0.0; + coss[0] = 1.0; + sins[1] = std::sqrt(1 - ctheta * ctheta); + coss[1] = ctheta; + for (int iter = 2; iter < TrigCacheSize; iter++) { + sins[iter] = sins[iter - 1] * sins[1]; + coss[iter] = coss[iter - 1] * coss[1]; + } + + // Legendre polynomials l=0 + lbuf[0] = 1.0; + + // Legendre polynomials l=1 + if (lmax > 0) { + lbuf[1] = sins[1]; + lbuf[2] = coss[1]; + } + + // Legendre polynomials l=2 + if (lmax > 1) { + lbuf[3] = sins[2]; + lbuf[4] = sins[1] * coss[1]; + lbuf[5] = 3 * coss[2] - 1; + } + + // Legendre polynomials l=3 + if (lmax >= MinLmaxForL3Block) { + lbuf[6] = sins[3]; + lbuf[7] = sins[2] * coss[1]; + lbuf[8] = (5 * coss[2] - 1) * sins[1]; + lbuf[9] = 5 * coss[3] - 3 * coss[1]; + } + + // Legendre polynomials l=4 + if (lmax >= MinLmaxForL4Block) { + lbuf[10] = sins[4]; + lbuf[11] = sins[3] * coss[1]; + lbuf[12] = (7 * coss[2] - 1) * sins[2]; + lbuf[13] = (7 * coss[3] - 3 * coss[1]) * sins[1]; + lbuf[14] = 35 * coss[4] - 30 * coss[2] + 3; + } + + // Legendre polynomials l=5 + if (lmax >= MinLmaxForL5Block) { + lbuf[15] = sins[5]; + lbuf[16] = sins[4] * coss[1]; + lbuf[17] = (9 * coss[2] - 1) * sins[3]; + lbuf[18] = (3 * coss[3] - 1 * coss[1]) * sins[2]; + lbuf[19] = (21 * coss[4] - 14 * coss[2] + 1) * sins[1]; + lbuf[20] = 63 * coss[5] - 70 * coss[3] + 15 * coss[1]; + } + } + + /// Function to calculate a set of Ylms up to a given l with cartesian input + void doYlmUpToL(int lmax, double x, double y, double z, std::complex* ylms) + { + double ctheta = 0.0; + double phi = 0.0; + + double r = std::sqrt(x * x + y * y + z * z); + if (r < SmallLength || std::fabs(z) < SmallLength) { + ctheta = 0.0; + } else { + ctheta = z / r; + } + phi = std::atan2(y, x); + doYlmUpToL(lmax, ctheta, phi, ylms); + } + + /// Function to calculate a set of Ylms up to a given l with spherical input + void doYlmUpToL(int lmax, double ctheta, double phi, std::complex* ylms) + { + int lcur = 0; + double lpol = 0.0; + + std::array coss{}; + std::array sins{}; + + std::array lbuf{}; + legendreUpToYlm(lmax, ctheta, lbuf); + initializeYlms(); + + for (int iter = 1; iter <= lmax; iter++) { + coss[iter - 1] = std::cos(iter * phi); + sins[iter - 1] = std::sin(iter * phi); + } + + ylms[lcur++] = fgPrefactors[0] * lbuf[0] * std::complex(1, 0); + + for (int il = 1; il <= lmax; il++) { + // First im = 0 + ylms[lcur + il] = fgPrefactors[fgPrefshift[il]] * lbuf[static_cast(fgPlmshift[il])] * std::complex(1.0, 0.0); + // Im != 0 + for (int im = 1; im <= il; im++) { + lpol = lbuf[static_cast(fgPlmshift[il]) - im]; + ylms[lcur + il - im] = fgPrefactors[fgPrefshift[il] - im] * lpol * std::complex(coss[im - 1], -sins[im - 1]); + ylms[lcur + il + im] = fgPrefactors[fgPrefshift[il] + im] * lpol * std::complex(coss[im - 1], sins[im - 1]); + } + lcur += 2 * il + 1; + } + } + + private: + std::array fgPrefactors{}; + std::array fgPrefshift{}; + std::array fgPlmshift{}; +}; + +} // namespace o2::analysis::femto + +#endif // PWGCF_FEMTO_CORE_FEMTOSPHERHARMATH_H_ diff --git a/PWGCF/Femto/Core/femtoUtils.h b/PWGCF/Femto/Core/femtoUtils.h index f0a72abf4bd..22dc4fa37e9 100644 --- a/PWGCF/Femto/Core/femtoUtils.h +++ b/PWGCF/Femto/Core/femtoUtils.h @@ -24,10 +24,13 @@ #include +#include #include #include #include +#include #include +#include #include namespace o2::analysis::femto @@ -45,6 +48,20 @@ inline std::optional getIndex(const T1& index, const std::unordered_map +int64_t daughterRow(int64_t daughterId, T const& trackTable) +{ + const int64_t row = daughterId - trackTable.offset(); + if (daughterId < 0 || row < 0 || row >= static_cast(trackTable.size())) { + LOG(fatal) << "Daughter index " << daughterId << " out of range for track table (offset " + << trackTable.offset() << ", size " << trackTable.size() << "). Breaking..."; + } + return row; +} + template float itsSignal(T const& track) { @@ -67,7 +84,7 @@ inline double getPdgMass(int pdgCode) { // use this function instead of TDatabasePDG to return masses defined in the PhysicsConstants.h header // this approach saves a lot of memory and important partilces like deuteron are missing in TDatabasePDG anyway - double mass = 0.f; + double mass = 0.; // add new particles if necessary here switch (std::abs(pdgCode)) { case kPiPlus: @@ -76,6 +93,9 @@ inline double getPdgMass(int pdgCode) case kKPlus: mass = o2::constants::physics::MassKPlus; break; + case kK0Short: + mass = o2::constants::physics::MassK0Short; + break; case kProton: mass = o2::constants::physics::MassProton; break; @@ -86,10 +106,10 @@ inline double getPdgMass(int pdgCode) mass = o2::constants::physics::MassPhi; break; case kRho770_0: - mass = 775.26; // not defined in O2? + mass = 0.77526; // not defined in O2? break; case kRho770Plus: - mass = 775.11; // not defined in O2? + mass = 0.77511; // not defined in O2? break; case o2::constants::physics::Pdg::kK0Star892: mass = o2::constants::physics::MassK0Star892; @@ -97,6 +117,9 @@ inline double getPdgMass(int pdgCode) case o2::constants::physics::Pdg::kLambdaCPlus: mass = o2::constants::physics::MassLambdaCPlus; break; + case o2::constants::physics::Pdg::kD0: + mass = o2::constants::physics::MassD0; + break; case o2::constants::physics::Pdg::kDeuteron: mass = o2::constants::physics::MassDeuteron; break; @@ -118,32 +141,52 @@ inline double getPdgMass(int pdgCode) case kOmegaMinus: mass = o2::constants::physics::MassOmegaMinus; break; + case kPi0: + mass = o2::constants::physics::MassPi0; + break; + case o2::constants::physics::kEta: + mass = o2::constants::physics::MassEta; + break; default: - LOG(warn) << "PDG code is not suppored. Return 0..."; + LOG(warn) << "PDG code " << pdgCode << " is not suppored. Return 0... "; } return mass; } template -float qn(T const& col) -{ - float qn = std::sqrt(col.qvecFT0CReVec()[0] * col.qvecFT0CReVec()[0] + col.qvecFT0CImVec()[0] * col.qvecFT0CImVec()[0]) * std::sqrt(col.sumAmplFT0C()); - return qn; -} +concept HasQvectors = requires(T col) { + col.qvecFT0CReVec(); + col.qvecFT0CImVec(); + col.sumAmplFT0C(); + col.qvecFT0AReVec(); + col.qvecFT0AImVec(); + col.sumAmplFT0A(); +}; + +template +concept HasEventShapeRow = requires(T row) { + row.qvec(); + row.eventPlaneAngle(); +}; + +/// accepts either a row/iterator or a table (Filtered> etc.) +template +concept HasEventShape = HasEventShapeRow> || (requires { typename std::decay_t::iterator; } && HasEventShapeRow::iterator>); -/// Recalculate pT for Kinks (Sigmas) using kinematic constraints -inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxDaughter, float pyDaughter, float pzDaughter) +/// Recalculate the pT of a kink mother from its direction and the charged daughter momentum +inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxDaughter, float pyDaughter, float pzDaughter, + float massMother, float massChargedDaughter, float massNeutralDaughter) { float almost0 = 1e-6f; - // Particle masses in GeV/c^2 - auto massPion = o2::constants::physics::MassPionCharged; - auto massNeutron = o2::constants::physics::MassNeutron; - auto massSigmaMinus = o2::constants::physics::MassSigmaMinus; + const float massPion = massChargedDaughter; + const float massNeutron = massNeutralDaughter; + const float massSigmaMinus = massMother; // Calculate mother momentum and direction versor float pMother = std::sqrt(pxMother * pxMother + pyMother * pyMother + pzMother * pzMother); - if (pMother < almost0) + if (pMother < almost0) { return -999.f; + } float versorX = pxMother / pMother; float versorY = pyMother / pMother; @@ -161,22 +204,26 @@ inline float calcPtnew(float pxMother, float pyMother, float pzMother, float pxD float b = -4.f * scalarProduct * k; float c = 4.f * ePi * ePi * massSigmaMinus * massSigmaMinus - k * k; - if (std::abs(a) < almost0) + if (std::abs(a) < almost0) { return -999.f; + } float d = b * b - 4.f * a * c; - if (d < 0.f) + if (d < 0.f) { return -999.f; + } float sqrtD = std::sqrt(d); float p1 = (-b + sqrtD) / (2.f * a); float p2 = (-b - sqrtD) / (2.f * a); // Pick physical solution: prefer P2 if positive, otherwise P1 - if (p2 < 0.f && p1 < 0.f) + if (p2 < 0.f && p1 < 0.f) { return -999.f; - if (p2 < 0.f) + } + if (p2 < 0.f) { return p1; + } // Choose solution closest to original momentum float p1Diff = std::abs(p1 - pMother); @@ -223,6 +270,146 @@ inline int signum(T x) return (T(0) < x) - (x < T(0)); } +template +T binLinear(float value, float lo, float hi, float step) +{ + float v = std::clamp(value, lo, hi); + auto idx = static_cast(std::round((v - lo) / step)); + auto maxIdx = static_cast(std::numeric_limits::max()) - static_cast(std::numeric_limits::min()); + idx = std::clamp(idx, static_cast(0), maxIdx); + return static_cast(idx + std::numeric_limits::min()); +} + +template +float unBinLinear(T binned, float lo, float step) +{ + auto idx = static_cast(binned) - static_cast(std::numeric_limits::min()); + return lo + static_cast(idx) * step; +} + +template +T binLogSigned(float signedValue, float magMin, float magMax) +{ + static_assert(std::is_unsigned_v, "binLogSigned requires an unsigned storage type"); + constexpr uint64_t TotalBits = sizeof(T) * 8; + constexpr uint64_t HalfLevels = uint64_t{1} << (TotalBits - 1); + const uint64_t sign = (signedValue < 0.f) ? uint64_t{1} : uint64_t{0}; + const float mag = std::clamp(std::fabs(signedValue), magMin, magMax); + const float logLo = std::log(magMin); + const float logHi = std::log(magMax); + const float step = (logHi - logLo) / static_cast(HalfLevels - 1); + auto idx = static_cast(std::round((std::log(mag) - logLo) / step)); + idx = std::clamp(idx, int64_t{0}, static_cast(HalfLevels - 1)); + return static_cast((sign << (TotalBits - 1)) | static_cast(idx)); +} + +template +float unBinLogSigned(T binned, float magMin, float magMax) +{ + static_assert(std::is_unsigned_v, "unBinLogSigned requires an unsigned storage type"); + constexpr uint64_t TotalBits = sizeof(T) * 8; + constexpr uint64_t HalfLevels = uint64_t{1} << (TotalBits - 1); + constexpr T SignMask = static_cast(uint64_t{1} << (TotalBits - 1)); + constexpr T MagMask = static_cast(SignMask - 1); + const float sign = (binned & SignMask) ? -1.f : 1.f; + const auto idx = static_cast(binned & MagMask); + const float logLo = std::log(magMin); + const float logHi = std::log(magMax); + const float step = (logHi - logLo) / static_cast(HalfLevels - 1); + return sign * std::exp(logLo + static_cast(idx) * step); +} + +template +int unBinSign(T binned) +{ + static_assert(std::is_unsigned_v, "unBinSign requires an unsigned storage type"); + constexpr uint64_t TotalBits = sizeof(T) * 8; + constexpr T SignMask = static_cast(uint64_t{1} << (TotalBits - 1)); + return (binned & SignMask) ? -1 : 1; +} + +template +T binLogUnsigned(float value, float magMin, float magMax) +{ + static_assert(std::is_unsigned_v, "binLogUnsigned requires an unsigned storage type"); + constexpr uint64_t TotalBits = sizeof(T) * 8; + constexpr uint64_t Levels = uint64_t{1} << TotalBits; // number of representable values, e.g. 65536 for uint16_t + float mag = std::clamp(value, magMin, magMax); + float logLo = std::log(magMin); + float logHi = std::log(magMax); + float step = (logHi - logLo) / static_cast(Levels - 1); + auto idx = static_cast(std::round((std::log(mag) - logLo) / step)); + idx = std::clamp(idx, uint64_t{0}, Levels - 1); + return static_cast(idx); +} + +template +float unBinLogUnsigned(T binned, float magMin, float magMax) +{ + constexpr uint64_t TotalBits = sizeof(T) * 8; + constexpr uint64_t Levels = uint64_t{1} << TotalBits; + uint64_t idx = binned; + float logLo = std::log(magMin); + float logHi = std::log(magMax); + float step = (logHi - logLo) / static_cast(Levels - 1); + float mag = std::exp(logLo + static_cast(idx) * step); + return mag; +} + +// --------------------------------------------------------------------------- +// purity/fraction packing: up to 4 species' log-binned nSigma values (see +// binLogSigned) packed bitwise into a single int64_t column. Slot +// assignment (which species occupies which of the 4 slots) is decided at +// producer init time from which requirePidXxx flags are active, and exposed +// via a self-documenting histogram -- these functions only do the fixed-slot +// bit shifting, nothing species-specific. +// --------------------------------------------------------------------------- + +constexpr int PurityNSlots = 4; + +/// pack 4 already-binned uint16_t codes (one per slot) into a single int64_t +inline int64_t packPurity4(uint16_t slot0, uint16_t slot1, uint16_t slot2, uint16_t slot3) +{ + return (static_cast(slot0) << 48) | + (static_cast(slot1) << 32) | + (static_cast(slot2) << 16) | + static_cast(slot3); +} + +/// extract the raw uint16_t code of one of the 4 slots (0..3) from a packed int64_t +inline uint16_t unpackPurity4(int64_t packed, int slot) +{ + switch (slot) { + case 0: + return static_cast((packed >> 48) & 0xFFFF); + case 1: + return static_cast((packed >> 32) & 0xFFFF); + case 2: + return static_cast((packed >> 16) & 0xFFFF); + case 3: + return static_cast(packed & 0xFFFF); + default: + LOG(fatal) << "unpackPurity4: invalid slot " << slot << " (must be 0-3). Breaking..."; + return 0; + } +} + +/// pack 4 raw signed nSigma values (one per slot, unused slots pass 0) directly into a packed int64_t +inline int64_t packPurityNSigma4(float nSigma0, float nSigma1, float nSigma2, float nSigma3, float magMin, float magMax) +{ + return packPurity4(binLogSigned(nSigma0, magMin, magMax), + binLogSigned(nSigma1, magMin, magMax), + binLogSigned(nSigma2, magMin, magMax), + binLogSigned(nSigma3, magMin, magMax)); +} + +/// unpack the nSigma value of one of the 4 slots (0..3) from a packed int64_t +inline float unpackPurityNSigma4(int64_t packed, int slot, float magMin, float magMax) +{ + return unBinLogSigned(unpackPurity4(packed, slot), magMin, magMax); +} + }; // namespace utils }; // namespace o2::analysis::femto +// #endif // PWGCF_FEMTO_CORE_FEMTOUTILS_H_ diff --git a/PWGCF/Femto/Core/histManager.h b/PWGCF/Femto/Core/histManager.h index ceadf18f38a..a5f2aeb6175 100644 --- a/PWGCF/Femto/Core/histManager.h +++ b/PWGCF/Femto/Core/histManager.h @@ -21,11 +21,8 @@ #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::histmanager { -namespace histmanager -{ - // plot level for secondaries during mc processing enum SecondaryPlotLevel { kSecondaryPlotLevel1 = 1, @@ -35,8 +32,16 @@ enum SecondaryPlotLevel { template struct HistInfo { - Hist hist; - o2::framework::HistType histtype; + constexpr HistInfo(Hist h, + o2::framework::HistType t, + std::string_view n, + std::string_view d) + : hist(h), histtype(t), histname(n), histdesc(d) + { + } + + Hist hist{}; + o2::framework::HistType histtype{o2::framework::kUndefinedHist}; std::string_view histname; std::string_view histdesc; }; @@ -80,6 +85,19 @@ constexpr const char* getHistDesc(EnumType variable, const ArrayType& array) return it != array.end() ? it->histdesc.data() : ""; } -} // namespace histmanager -} // namespace o2::analysis::femto + +// Replace the x-axis segment of a "title; x axis; y axis[; z axis...]" histogram description +// with newTitle. Used to patch a static HistTable description at runtime once the actual axis +// quantity (e.g. which momentum type, which kinematic variable) is resolved from a Configurable. +inline std::string replaceFirstAxisTitle(std::string_view desc, std::string_view newTitle) +{ + std::string result(desc); + const auto first = result.find(';'); + const auto second = (first == std::string::npos) ? std::string::npos : result.find(';', first + 1); + if (second != std::string::npos) { + result.replace(first + 1, second - first - 1, " " + std::string(newTitle) + " "); + } + return result; +} +} // namespace o2::analysis::femto::histmanager #endif // PWGCF_FEMTO_CORE_HISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index 214260ee245..34728fbdcec 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -27,6 +27,7 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include @@ -41,11 +42,8 @@ #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::kinkbuilder { -namespace kinkbuilder -{ - // filters applied in the producer task struct ConfKinkFilters : o2::framework::ConfigurableGroup { std::string prefix = std::string("KinkFilters"); @@ -59,26 +57,33 @@ struct ConfKinkFilters : o2::framework::ConfigurableGroup { o2::framework::Configurable massMaxSigma{"massMaxSigma", 1.3f, "Maximum mass for Sigma hypothesis"}; o2::framework::Configurable massMinSigmaPlus{"massMinSigmaPlus", 1.1f, "Minimum mass for SigmaPlus hypothesis"}; o2::framework::Configurable massMaxSigmaPlus{"massMaxSigmaPlus", 1.3f, "Maximum mass for SigmaPlus hypothesis"}; + o2::framework::Configurable massMinSigmaPlusPcm{"massMinSigmaPlusPcm", 1.1f, "Minimum mass for SigmaPlus (PCM) hypothesis"}; + o2::framework::Configurable massMaxSigmaPlusPcm{"massMaxSigmaPlusPcm", 1.3f, "Maximum mass for SigmaPlus (PCM) hypothesis"}; + o2::framework::Configurable mlScoreMinSigmaPlusPcm{"mlScoreMinSigmaPlusPcm", -1.f, "Minimum BDT score for SigmaPlus (PCM) (-1: no cut)"}; }; // selections bits for all kinks -#define KINK_DEFAULT_BITS \ - o2::framework::Configurable> kinkTopoDcaMax{"kinkTopoDcaMax", {2.0f}, "Maximum kink topological DCA"}; \ - o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum absolute pseudorapidity for daughter track"}; \ - o2::framework::Configurable> dauDcaPvMin{"dauDcaPvMin", {0.0f}, "Minimum DCA of daughter from primary vertex (cm)"}; \ - o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; \ - o2::framework::Configurable> alphaAPMin{"alphaAPMin", {-1.0f}, "Minimum Alpha_AP for Sigma candidates"}; \ - o2::framework::Configurable> alphaAPMax{"alphaAPMax", {0.0f}, "Maximum Alpha_AP for Sigma candidates"}; \ - o2::framework::Configurable> qtAPMin{"qtAPMin", {0.15f}, "Minimum qT_AP for Sigma candidates"}; \ - o2::framework::Configurable> qtAPMax{"qtAPMax", {0.2f}, "Maximum qT_AP for Sigma candidates"}; \ - o2::framework::Configurable> cosPointingAngleMin{"cosPointingAngleMin", {0.0f}, "Minimum cosine of pointing angle"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define KINK_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all Kinks are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> kinkTopoDcaMax{"kinkTopoDcaMax", {2.0f}, "Maximum kink topological DCA"}; \ + o2::framework::Configurable> transRadMin{"transRadMin", {19.6f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {1.0f}, "Maximum absolute pseudorapidity for daughter track"}; \ + o2::framework::Configurable> dauDcaPvMin{"dauDcaPvMin", {0.1f}, "Minimum DCA of daughter from primary vertex (cm)"}; \ + o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; \ + o2::framework::Configurable> qtAPMin{"qtAPMin", {0.15f}, "Minimum qT_AP for Sigma candidates"}; \ + o2::framework::Configurable> qtAPMax{"qtAPMax", {0.2f}, "Maximum qT_AP for Sigma candidates"}; \ + o2::framework::Configurable> cosPointingAngleMin{"cosPointingAngleMin", {0.0f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> ptOriginalMin{"ptOriginalMin", {1.2f}, "Minimum original (not recalculated) pT of the mother (GeV/c)"}; \ + o2::framework::Configurable> ptOriginalMax{"ptOriginalMax", {10.f}, "Maximum original (not recalculated) pT of the mother (GeV/c)"}; // derived selection bits for sigma struct ConfSigmaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("SigmaBits"); KINK_DEFAULT_BITS + o2::framework::Configurable> alphaAPMin{"alphaAPMin", {-1.0f}, "Minimum Alpha_AP for Sigma candidates"}; + o2::framework::Configurable> alphaAPMax{"alphaAPMax", {0.0f}, "Maximum Alpha_AP for Sigma candidates"}; o2::framework::Configurable> chaDauTpcPion{"chaDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for charged daughter tracks"}; }; @@ -86,28 +91,46 @@ struct ConfSigmaBits : o2::framework::ConfigurableGroup { struct ConfSigmaPlusBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("SigmaPlusBits"); KINK_DEFAULT_BITS + o2::framework::Configurable> alphaAPMin{"alphaAPMin", {0.0f}, "Minimum Alpha_AP for SigmaPlus candidates"}; + o2::framework::Configurable> alphaAPMax{"alphaAPMax", {1.0f}, "Maximum Alpha_AP for SigmaPlus candidates"}; o2::framework::Configurable> chaDauTpcProton{"chaDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for charged daughter tracks"}; - o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {5.f}, "Maximum combined |nsigma_Proton| (TPC+TOF) for charged daughter tracks"}; - o2::framework::Configurable pidThres{"pidThres", 0.75f, "Momentum threshold for using TOF/combined pid for daughter tracks (GeV/c)"}; + o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {}, "Maximum |nsigma_Proton| TOF for charged daughter tracks"}; + o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is optional"}; + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; }; #undef KINK_DEFAULT_BITS +// selection bits for sigma plus reconstructed with a PCM photon +struct ConfSigmaPlusPcmBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("SigmaPlusPcmBits"); + o2::framework::Configurable passThrough{"passThrough", false, "If true, all SigmaPlusPcm are passed through. Bits for all selections are stored."}; + o2::framework::Configurable> flightDirTiltMax{"flightDirTiltMax", {0.05f}, "Maximum tilt of the flight direction (rad)"}; + o2::framework::Configurable> mothDcaPvMax{"mothDcaPvMax", {1.0f}, "Maximum DCA of mother from primary vertex (cm)"}; + o2::framework::Configurable> chaDauDcaZMax{"chaDauDcaZMax", {0.3f}, "Maximum |DCAz| of charged daughter from primary vertex (cm)"}; + o2::framework::Configurable> photonDcaZMax{"photonDcaZMax", {2.0f}, "Maximum |DCAz| of the photon from primary vertex (cm)"}; + o2::framework::Configurable> chaDauTpcProton{"chaDauTpcProton", {3.f}, "Maximum |nsigma_Proton| TPC for charged daughter tracks"}; + o2::framework::Configurable> chaDauTofProton{"chaDauTofProton", {}, "Maximum |nsigma_Proton| TOF for charged daughter tracks"}; + o2::framework::Configurable requireTof{"requireTof", false, "If true, TOF PID is a minimal selection. If false, TOF PID is optional"}; + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, the bit mask for the TOF selection will be true for all limits if the daughter track has no TOF"}; +}; + // base selection for analysis task for kinks -#define KINK_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", defaultPdgCode, "PDG code. Select antipartilce via sign"}; \ - o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ - o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ - o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ - o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ - o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ - o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", defaultMassMin, "Minimum invariant mass for Sigma"}; \ - o2::framework::Configurable massMax{"massMax", defaultMassMax, "Maximum invariant mass for Sigma"}; \ - o2::framework::Configurable mask{"mask", 0x0, "Bitmask for kink selection"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define KINK_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "PDG code. Select antiparticle via sign"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass"}; \ + o2::framework::Configurable mask{"mask", 0x0, "Bitmask for kink selection"}; // base selection for analysis task for sigmas -template +template struct ConfSigmaSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; KINK_DEFAULT_SELECTIONS(1.1, 1.3, 3112) @@ -115,13 +138,22 @@ struct ConfSigmaSelection : o2::framework::ConfigurableGroup { }; // base selection for analysis task for sigma plus -template +template struct ConfSigmaPlusSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; KINK_DEFAULT_SELECTIONS(1.1, 1.3, 3222) o2::framework::Configurable sign{"sign", 1, "Sign of the Sigma mother track (e.g. +1 for Sigma+ or -1 for AntiSigma+)"}; }; +// base selection for analysis task for sigma plus reconstructed with a PCM photon +template +struct ConfSigmaPlusPcmSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + KINK_DEFAULT_SELECTIONS(1.1, 1.3, 3222) + o2::framework::Configurable sign{"sign", 1, "Sign of the Sigma candidate (e.g. +1 for Sigma+ or -1 for AntiSigma+)"}; + o2::framework::Configurable mlScoreMin{"mlScoreMin", -1.f, "Minimum BDT score (-1: no cut)"}; +}; + #undef KINK_DEFAULT_SELECTIONS constexpr const char PrefixSigmaSelection1[] = "SigmaSelection1"; @@ -132,6 +164,10 @@ constexpr const char PrefixSigmaPlusSelection1[] = "SigmaPlusSelection1"; constexpr const char PrefixSigmaPlusSelection2[] = "SigmaPlusSelection2"; using ConfSigmaPlusSelection1 = ConfSigmaPlusSelection; using ConfSigmaPlusSelection2 = ConfSigmaPlusSelection; +constexpr const char PrefixSigmaPlusPcmSelection1[] = "SigmaPlusPcmSelection1"; +constexpr const char PrefixSigmaPlusPcmSelection2[] = "SigmaPlusPcmSelection2"; +using ConfSigmaPlusPcmSelection1 = ConfSigmaPlusPcmSelection; +using ConfSigmaPlusPcmSelection2 = ConfSigmaPlusPcmSelection; /// The different selections for kinks enum KinkSeles { @@ -153,11 +189,19 @@ enum KinkSeles { kQtAPMax, kCosPointingAngleMin, + kPtOriginalMin, + kPtOriginalMax, + + kFlightDirTiltMax, + kChaDauDcaZMax, + kPhotonDcaZMax, + kKinkSelsMax }; constexpr char SigmaSelHistName[] = "hSigmaSelection"; constexpr char SigmaPlusSelHistName[] = "hSigmaPlusSelection"; +constexpr char SigmaPlusPcmSelHistName[] = "hSigmaPlusPcmSelection"; const char kinkSelsName[] = "Kink selection object"; const std::unordered_map kinkSelectionNames = { {kKinkTopoDcaMax, "kinkTopoDcaMax"}, @@ -173,20 +217,55 @@ const std::unordered_map kinkSelectionNames = { {kAlphaAPMax, "alphaAPMax"}, {kQtAPMin, "qtAPMin"}, {kQtAPMax, "qtAPMax"}, - {kCosPointingAngleMin, "cosPointingAngleMin"}}; + {kCosPointingAngleMin, "cosPointingAngleMin"}, + {kPtOriginalMin, "ptOriginalMin"}, + {kPtOriginalMax, "ptOriginalMax"}, + {kFlightDirTiltMax, "flightDirTiltMax"}, + {kChaDauDcaZMax, "chaDauDcaZMax"}, + {kPhotonDcaZMax, "photonDcaZMax"}}; + +/// enum for all kink pre-filters (evaluated in checkFilters, before the selection bitmask) +enum KinkFilters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kMassMin, + kMassMax, + kMlScoreMin, + kKinkFiltersMax +}; + +constexpr char SigmaFilterHistName[] = "hSigmaFilters"; +constexpr char SigmaPlusFilterHistName[] = "hSigmaPlusFilters"; +constexpr char SigmaPlusPcmFilterHistName[] = "hSigmaPlusPcmFilters"; +const std::unordered_map kinkFilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}, + {kMassMin, "massMin"}, + {kMassMax, "massMax"}, + {kMlScoreMin, "mlScoreMin"}}; /// \class KinkCuts /// \brief Cut class to contain and execute all cuts applied to kinks -template -class KinkSelection : public BaseSelection +template +class KinkSelection : public baseselection::BaseSelection { public: KinkSelection() = default; - ~KinkSelection() = default; + ~KinkSelection() override = default; template void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) { + this->init(config.passThrough.value); + mPtMin = filter.ptMin.value; mPtMax = filter.ptMax.value; mEtaMin = filter.etaMin.value; @@ -204,9 +283,10 @@ class KinkSelection : public BaseSelectionaddSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, false, false, true); - this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kUpperLimit, false, false, true); + mKeepTracksWithoutTof = config.keepTracksWithoutTof.value; + this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false); + this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof)); } this->addSelection(kKinkTopoDcaMax, kinkSelectionNames.at(kKinkTopoDcaMax), config.kinkTopoDcaMax.value, limits::kUpperLimit, true, true, false); @@ -220,12 +300,43 @@ class KinkSelection : public BaseSelectionaddSelection(kQtAPMin, kinkSelectionNames.at(kQtAPMin), config.qtAPMin.value, limits::kLowerLimit, true, true, false); this->addSelection(kQtAPMax, kinkSelectionNames.at(kQtAPMax), config.qtAPMax.value, limits::kUpperLimit, true, true, false); this->addSelection(kCosPointingAngleMin, kinkSelectionNames.at(kCosPointingAngleMin), config.cosPointingAngleMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kPtOriginalMin, kinkSelectionNames.at(kPtOriginalMin), config.ptOriginalMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kPtOriginalMax, kinkSelectionNames.at(kPtOriginalMax), config.ptOriginalMax.value, limits::kUpperLimit, true, true, false); - this->setupContainers(registry); + this->setupSelectionHistogram(registry); + + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { + this->template setupFilterHistogram( + registry, + { + {kinkFilterNames.at(kPtMin), mPtMin}, + {kinkFilterNames.at(kPtMax), mPtMax}, + {kinkFilterNames.at(kEtaMin), mEtaMin}, + {kinkFilterNames.at(kEtaMax), mEtaMax}, + {kinkFilterNames.at(kPhiMin), mPhiMin}, + {kinkFilterNames.at(kPhiMax), mPhiMax}, + {kinkFilterNames.at(kMassMin), mMassSigmaLowerLimit}, + {kinkFilterNames.at(kMassMax), mMassSigmaUpperLimit}, + }); + } + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { + this->template setupFilterHistogram( + registry, + { + {kinkFilterNames.at(kPtMin), mPtMin}, + {kinkFilterNames.at(kPtMax), mPtMax}, + {kinkFilterNames.at(kEtaMin), mEtaMin}, + {kinkFilterNames.at(kEtaMax), mEtaMax}, + {kinkFilterNames.at(kPhiMin), mPhiMin}, + {kinkFilterNames.at(kPhiMax), mPhiMax}, + {kinkFilterNames.at(kMassMin), mMassSigmaPlusLowerLimit}, + {kinkFilterNames.at(kMassMax), mMassSigmaPlusUpperLimit}, + }); + } }; - template - void computeQaVariables(T1 const& kinkCand, T2 const& /*tracks*/) + template + void computeKinkKinematics(T1 const& kinkCand, T2 const& /*tracks*/, T3 const& /*col*/) { std::array momMother = {kinkCand.pxMoth(), kinkCand.pyMoth(), kinkCand.pzMoth()}; float kinkMomP = RecoDecay::p(momMother); @@ -242,14 +353,13 @@ class KinkSelection : public BaseSelection 0.f) ? std::sqrt(std::max(0.f, p2A - dp * dp / p2V0)) : 0.f; std::array vMother = {kinkCand.xDecVtx(), kinkCand.yDecVtx(), kinkCand.zDecVtx()}; float vMotherNorm = std::sqrt(std::inner_product(vMother.begin(), vMother.end(), vMother.begin(), 0.f)); mCosPointingAngle = (vMotherNorm > 0.f && kinkMomP > 0.f) ? (std::inner_product(momMother.begin(), momMother.end(), vMother.begin(), 0.f)) / (kinkMomP * vMotherNorm) : 0.f; mTransRadius = std::hypot(kinkCand.xDecVtx(), kinkCand.yDecVtx()); - mKinkDauP = kinkDauP; mKinkDauEta = RecoDecay::eta(momDaughter); mKinkAngle = 0.f; @@ -271,6 +381,9 @@ class KinkSelection : public BaseSelectionevaluateObservable(kQtAPMin, mQtAp); this->evaluateObservable(kQtAPMax, mQtAp); this->evaluateObservable(kCosPointingAngleMin, mCosPointingAngle); + // the stored pT is the recalculated one, the original pT is only available as selection bits + this->evaluateObservable(kPtOriginalMin, mKinkMotherPtOriginal); + this->evaluateObservable(kPtOriginalMax, mKinkMotherPtOriginal); this->evaluateObservable(kKinkTopoDcaMax, kinkCand.dcaKinkTopo()); // Compute transRadius @@ -289,18 +402,15 @@ class KinkSelection : public BaseSelectionevaluateObservable(kChaDaughTpcPion, chaDaughter.tpcNSigmaPi()); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - if (mKinkDauP < mPidThreshold) { - this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + if (chaDaughter.hasTOF()) { + this->evaluateObservable(kChaDaughTofProton, chaDaughter.tofNSigmaPr()); } else { - if (chaDaughter.hasTOF()) { - this->evaluateObservable(kChaDaughTofProton, std::abs(chaDaughter.tofNSigmaPr())); - } else { - this->evaluateObservable(kChaDaughTofProton, 999.f); - } + this->evaluateObservable(kChaDaughTofProton, mKeepTracksWithoutTof ? 0.f : 999.f); } } - this->assembleBitmask(); + this->assembleBitmask(); }; template @@ -312,50 +422,93 @@ class KinkSelection : public BaseSelection 0.f) { - mKinkMotherPt = ptRecalc; - } else { - mKinkMotherPt = -1.f; + // Recalculate pT using kinematic constraints of the decay channel + float ptRecalc = -999.f; + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { + // Sigma- -> pi- n + ptRecalc = utils::calcPtnew(momMother[0], momMother[1], momMother[2], momDaughter[0], momDaughter[1], momDaughter[2], + o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassNeutron); } + if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { + // Sigma+ -> p pi0 + ptRecalc = utils::calcPtnew(momMother[0], momMother[1], momMother[2], momDaughter[0], momDaughter[1], momDaughter[2], + o2::constants::physics::MassSigmaPlus, o2::constants::physics::MassProton, o2::constants::physics::MassPionNeutral); + } + mKinkMotherPtOriginal = std::hypot(momMother[0], momMother[1]); + mKinkMotherPt = (ptRecalc > 0.f) ? ptRecalc : mKinkMotherPtOriginal; } template bool checkFilters(const T& kinkCand) const { - const bool kinematicOk = ((mKinkMotherPt > mPtMin && mKinkMotherPt < mPtMax) && - (mKinkMotherEta > mEtaMin && mKinkMotherEta < mEtaMax) && - (mKinkMotherPhi > mPhiMin && mKinkMotherPhi < mPhiMax)); - if (!kinematicOk) { - return false; - } + bool pass = true; + bool p = false; + + p = mKinkMotherPt > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = mKinkMotherPt < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = mKinkMotherEta > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = mKinkMotherEta < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = mKinkMotherPhi > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = mKinkMotherPhi < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { float sigmaMass = kinkCand.mSigmaMinus(); - return (sigmaMass > mMassSigmaLowerLimit && sigmaMass < mMassSigmaUpperLimit); + + p = sigmaMass > mMassSigmaLowerLimit; + this->template fillFilter(kMassMin, p); + pass &= p; + + p = sigmaMass < mMassSigmaUpperLimit; + this->template fillFilter(kMassMax, p); + pass &= p; } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { float sigmaMass = kinkCand.mSigmaPlus(); - return (sigmaMass > mMassSigmaPlusLowerLimit && sigmaMass < mMassSigmaPlusUpperLimit); + + p = sigmaMass > mMassSigmaPlusLowerLimit; + this->template fillFilter(kMassMin, p); + pass &= p; + + p = sigmaMass < mMassSigmaPlusUpperLimit; + this->template fillFilter(kMassMax, p); + pass &= p; } - return false; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; } - float getKinkMotherPt() const { return mKinkMotherPt; } - float getKinkMotherEta() const { return mKinkMotherEta; } - float getKinkMotherPhi() const { return mKinkMotherPhi; } - float getKinkTransRadius() const { return mTransRadius; } - float getKinkAngle() const { return mKinkAngle; } + [[nodiscard]] float getKinkMotherPt() const { return mKinkMotherPt; } + [[nodiscard]] float getKinkMotherEta() const { return mKinkMotherEta; } + [[nodiscard]] float getKinkMotherPhi() const { return mKinkMotherPhi; } + [[nodiscard]] float getKinkTransRadius() const { return mTransRadius; } + [[nodiscard]] float getKinkAngle() const { return mKinkAngle; } - public: + private: float mMassSigmaLowerLimit = 1.15f; float mMassSigmaUpperLimit = 1.25f; float mMassSigmaPlusLowerLimit = 1.15f; float mMassSigmaPlusUpperLimit = 1.25f; - float mPidThreshold = 0.75f; + bool mKeepTracksWithoutTof = true; // kinematic filters float mPtMin = 0.f; @@ -366,7 +519,8 @@ class KinkSelection : public BaseSelection producedSigmas; + o2::framework::Produces producedLiteSigmas; o2::framework::Produces producedSigmaMasks; o2::framework::Produces producedSigmaExtras; o2::framework::Produces producedSigmaPlus; + o2::framework::Produces producedLiteSigmaPlus; o2::framework::Produces producedSigmaPlusMasks; o2::framework::Produces producedSigmaPlusExtras; + o2::framework::Produces producedSigmaPlusPcm; + o2::framework::Produces producedLiteSigmaPlusPcm; + o2::framework::Produces producedSigmaPlusPcmMasks; + o2::framework::Produces producedSigmaPlusPcmMlScores; + o2::framework::Produces producedSigmaPlusPcmExtras; }; struct ConfKinkTables : o2::framework::ConfigurableGroup { std::string prefix = std::string("KinkTables"); o2::framework::Configurable produceSigmas{"produceSigmas", -1, "Produce Sigmas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteSigmas{"produceLiteSigmas", -1, "Produce LiteSigmas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaMasks{"produceSigmaMasks", -1, "Produce SigmaMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaExtras{"produceSigmaExtras", -1, "Produce SigmaExtras (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaPlus{"produceSigmaPlus", -1, "Produce SigmaPlus (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteSigmaPlus{"produceLiteSigmaPlus", -1, "Produce LiteSigmaPlus (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaPlusMasks{"produceSigmaPlusMasks", -1, "Produce SigmaPlusMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceSigmaPlusExtras{"produceSigmaPlusExtras", -1, "Produce SigmaPlusExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcm{"produceSigmaPlusPcm", -1, "Produce SigmaPlusPcm (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteSigmaPlusPcm{"produceLiteSigmaPlusPcm", -1, "Produce LiteSigmaPlusPcm (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmMasks{"produceSigmaPlusPcmMasks", -1, "Produce SigmaPlusPcmMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmMlScores{"produceSigmaPlusPcmMlScores", -1, "Produce SigmaPlusPcmMlScores (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceSigmaPlusPcmExtras{"produceSigmaPlusPcmExtras", -1, "Produce SigmaPlusPcmExtras (-1: auto; 0 off; 1 on)"}; }; -template +template class KinkBuilder { public: KinkBuilder() = default; ~KinkBuilder() = default; - template - void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext, T5& trackBuilder) { if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { LOG(info) << "Initialize femto Sigma builder..."; mProduceSigmas = utils::enableTable("FSigmas_002", table.produceSigmas.value, initContext); + mProduceLiteSigmas = utils::enableTable("FLiteSigmas_001", table.produceLiteSigmas.value, initContext); mProduceSigmaMasks = utils::enableTable("FSigmaMasks_001", table.produceSigmaMasks.value, initContext); mProduceSigmaExtras = utils::enableTable("FSigmaExtras_001", table.produceSigmaExtras.value, initContext); + + if (mProduceSigmas && mProduceLiteSigmas) { + LOG(fatal) << "FSigmas and FLiteSigmas are mutually exclusive -- enable only one. " + << "FLiteSigmas is meant to replace FSigmas at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FSigmas from FLiteSigmas downstream."; + } + if (mProduceSigmas && !trackBuilder.producingTracks()) { + LOG(fatal) << "FSigmas is enabled, but the track builder is not producing FTracks (full precision). " + << "FSigmas stores the daughter index into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteSigmas if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteSigmas && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteSigmas is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteSigmas stores the daughter index into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FSigmas if TrackTables.produceTracks is enabled instead."; + } } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { LOG(info) << "Initialize femto SigmaPlus builder..."; mProduceSigmaPlus = utils::enableTable("FSigmaPlus_001", table.produceSigmaPlus.value, initContext); + mProduceLiteSigmaPlus = utils::enableTable("FLiteSigmaPlus_001", table.produceLiteSigmaPlus.value, initContext); mProduceSigmaPlusMasks = utils::enableTable("FSigmaPlusMasks_001", table.produceSigmaPlusMasks.value, initContext); - mProduceSigmaPlusExtras = utils::enableTable("FSigmaPlusExtras_001", table.produceSigmaPlusExtras.value, initContext); + mProduceSigmaPlusExtras = utils::enableTable("FSigmaPlusExtras_002", table.produceSigmaPlusExtras.value, initContext); + + if (mProduceSigmaPlus && mProduceLiteSigmaPlus) { + LOG(fatal) << "FSigmaPlus and FLiteSigmaPlus are mutually exclusive -- enable only one. " + << "FLiteSigmaPlus is meant to replace FSigmaPlus at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FSigmaPlus from FLiteSigmaPlus downstream."; + } + if (mProduceSigmaPlus && !trackBuilder.producingTracks()) { + LOG(fatal) << "FSigmaPlus is enabled, but the track builder is not producing FTracks (full precision). " + << "FSigmaPlus stores the daughter index into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteSigmaPlus if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteSigmaPlus && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteSigmaPlus is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteSigmaPlus stores the daughter index into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FSigmaPlus if TrackTables.produceTracks is enabled instead."; + } } - if (mProduceSigmas || mProduceSigmaMasks || mProduceSigmaExtras || mProduceSigmaPlus || mProduceSigmaPlusMasks || mProduceSigmaPlusExtras) { + if (mProduceSigmas || mProduceLiteSigmas || mProduceSigmaMasks || mProduceSigmaExtras || + mProduceSigmaPlus || mProduceLiteSigmaPlus || mProduceSigmaPlusMasks || mProduceSigmaPlusExtras) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -435,8 +637,8 @@ class KinkBuilder LOG(info) << "Initialization done..."; } - template - void fillKinks(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& kinkProducts, T6 const& kinks, T7 const& tracks, T8 const& tracksWithItsPid, T9& trackBuilder) + template + void fillKinks(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& kinkProducts, T6 const& kinks, T7 const& tracks, T8& trackBuilder) { if (!mFillAnyTable) { return; @@ -450,32 +652,28 @@ class KinkBuilder continue; } // compute qa variables before applying selections - mKinkSelection.computeQaVariables(kink, tracks); + mKinkSelection.computeKinkKinematics(kink, tracks, col); mKinkSelection.applySelections(kink, tracks); if (!mKinkSelection.passesAllRequiredSelections()) { continue; } collisionBuilder.template fillCollision(collisionProducts, col); - // cleaner, but without ITS pid: auto daughter = kink.template trackDaug_as(); - int64_t idx = kink.trackDaugId() - tracksWithItsPid.offset(); - // check for valid index - if (idx < 0 || idx >= static_cast(tracksWithItsPid.size())) { - return; - } - auto daughter = tracksWithItsPid.iteratorAt(idx); - daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, collisionProducts); + + auto daughter = kink.template trackDaug_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, collisionBuilder); if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { - fillSigma(collisionProducts, kinkProducts, kink, daughterIndex); + fillSigma(collisionBuilder, kinkProducts, kink, daughterIndex); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - fillSigmaPlus(collisionProducts, kinkProducts, kink, daughterIndex); + auto mother = kink.template trackMoth_as(); + fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughter, mother, daughterIndex); } } } - template - void fillMcKinks(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& kinkProducts, T7 const& kinks, T8 const& tracks, T9 const& tracksWithItsPid, T10& trackBuilder, T11 const& mcParticles, T12& mcBuilder, T13& mcProducts) + template + void fillMcKinks(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& kinkProducts, T7 const& kinks, T8 const& tracks, T9& trackBuilder, T10 const& mcParticles, T11& mcBuilder, T12& mcProducts) { if (!mFillAnyTable) { @@ -489,7 +687,7 @@ class KinkBuilder continue; } // compute qa variables before applying selections - mKinkSelection.computeQaVariables(kink, tracks); + mKinkSelection.computeKinkKinematics(kink, tracks, col); mKinkSelection.applySelections(kink, tracks); if (!mKinkSelection.passesAllRequiredSelections()) { continue; @@ -497,40 +695,44 @@ class KinkBuilder collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); - int64_t idx = kink.trackDaugId() - tracks.offset(); - // check for valid index - if (idx < 0 || idx >= static_cast(tracks.size())) { - return; - } - auto daughter = tracks.iteratorAt(idx); - auto daughterWithItsPid = tracksWithItsPid.iteratorAt(idx); - daughterIndex = trackBuilder.template getDaughterIndex(col, collisionProducts, mcCols, daughter, daughterWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); + auto daughter = kink.template trackDaug_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { - fillSigma(collisionProducts, kinkProducts, kink, daughterIndex); - mcBuilder.template fillMcSigmaWithLabel(col, mcCols, daughter, mcParticles, mcProducts); + fillSigma(collisionBuilder, kinkProducts, kink, daughterIndex); + mcBuilder.template fillMcSigmaWithLabel(daughter, mcParticles, mcCols, mcProducts); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - fillSigmaPlus(collisionProducts, kinkProducts, kink, daughterIndex); - mcBuilder.template fillMcSigmaPlusWithLabel(col, mcCols, daughter, mcParticles, mcProducts); + auto mother = kink.template trackMoth_as(); + fillSigmaPlus(collisionBuilder, kinkProducts, kink, daughter, mother, daughterIndex); + mcBuilder.template fillMcSigmaPlusWithLabel(daughter, mcParticles, mcCols, mcProducts); } } } template - void fillSigma(T1& collisionProducts, T2& kinkProducts, T3 const& kink, int64_t daughterIndex) + void fillSigma(T1& collisionBuilder, T2& kinkProducts, T3 const& kink, int64_t daughterIndex) { // Mass is calculated from original momentum components stored in kink table float mass = kink.mSigmaMinus(); + float signedPt = kink.mothSign() * mKinkSelection.getKinkMotherPt(); // Recalculated pT if (mProduceSigmas) { - kinkProducts.producedSigmas(collisionProducts.producedCollision.lastIndex(), - kink.mothSign() * mKinkSelection.getKinkMotherPt(), // Recalculated pT + kinkProducts.producedSigmas(collisionBuilder.collisionIndex(), + signedPt, mKinkSelection.getKinkMotherEta(), mKinkSelection.getKinkMotherPhi(), mass, daughterIndex); } + if (mProduceLiteSigmas) { + kinkProducts.producedLiteSigmas(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(signedPt), + o2::aod::femtobase::lite::binEta(mKinkSelection.getKinkMotherEta()), + o2::aod::femtobase::lite::binPhi(mKinkSelection.getKinkMotherPhi()), + o2::aod::femtokinks::lite::binSigmaMass(mass), + daughterIndex); + } if (mProduceSigmaMasks) { kinkProducts.producedSigmaMasks(mKinkSelection.getBitmask()); } @@ -546,20 +748,28 @@ class KinkBuilder } } - template - void fillSigmaPlus(T1& collisionProducts, T2& kinkProducts, T3 const& kink, int64_t daughterIndex) + template + void fillSigmaPlus(T1& collisionBuilder, T2& kinkProducts, T3 const& kink, T4 const& daughter, T5 const& mother, int64_t daughterIndex) { // Mass is calculated from original momentum components stored in kink table float mass = kink.mSigmaPlus(); - + float signedPt = kink.mothSign() * mKinkSelection.getKinkMotherPt(); // Recalculated pT if (mProduceSigmaPlus) { - kinkProducts.producedSigmaPlus(collisionProducts.producedCollision.lastIndex(), - kink.mothSign() * mKinkSelection.getKinkMotherPt(), // Recalculated pT + kinkProducts.producedSigmaPlus(collisionBuilder.collisionIndex(), + signedPt, mKinkSelection.getKinkMotherEta(), mKinkSelection.getKinkMotherPhi(), mass, daughterIndex); } + if (mProduceLiteSigmaPlus) { + kinkProducts.producedLiteSigmaPlus(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(signedPt), + o2::aod::femtobase::lite::binEta(mKinkSelection.getKinkMotherEta()), + o2::aod::femtobase::lite::binPhi(mKinkSelection.getKinkMotherPhi()), + o2::aod::femtokinks::lite::binSigmaMass(mass), + daughterIndex); + } if (mProduceSigmaPlusMasks) { kinkProducts.producedSigmaPlusMasks(mKinkSelection.getBitmask()); } @@ -571,83 +781,421 @@ class KinkBuilder kink.xDecVtx(), kink.yDecVtx(), kink.zDecVtx(), - mKinkSelection.getKinkTransRadius()); + mKinkSelection.getKinkTransRadius(), + kink.dcaKinkTopo(), + daughter.tpcNSigmaPr(), + daughter.tpcNSigmaPi(), + daughter.tofNSigmaPr(), + kink.ptMoth(), + mother.itsClusterSizes()); } } - bool fillAnyTable() { return mFillAnyTable; } + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mKinkSelection.isPassThrough(); } private: - KinkSelection mKinkSelection; + KinkSelection mKinkSelection; bool mFillAnyTable = false; bool mProduceSigmas = false; + bool mProduceLiteSigmas = false; bool mProduceSigmaMasks = false; bool mProduceSigmaExtras = false; bool mProduceSigmaPlus = false; + bool mProduceLiteSigmaPlus = false; bool mProduceSigmaPlusMasks = false; bool mProduceSigmaPlusExtras = false; }; +/// \class SigmaPlusPcmSelection +/// \brief Cut class to contain and execute all cuts applied to sigma plus reconstructed with a PCM photon +template +class SigmaPlusPcmSelection : public baseselection::BaseSelection +{ + public: + SigmaPlusPcmSelection() = default; + ~SigmaPlusPcmSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mMassLowerLimit = filter.massMinSigmaPlusPcm.value; + mMassUpperLimit = filter.massMaxSigmaPlusPcm.value; + mMlScoreMin = filter.mlScoreMinSigmaPlusPcm.value; + mKeepTracksWithoutTof = config.keepTracksWithoutTof.value; + + this->addSelection(kFlightDirTiltMax, kinkSelectionNames.at(kFlightDirTiltMax), config.flightDirTiltMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kMothDcaPvMax, kinkSelectionNames.at(kMothDcaPvMax), config.mothDcaPvMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kChaDauDcaZMax, kinkSelectionNames.at(kChaDauDcaZMax), config.chaDauDcaZMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPhotonDcaZMax, kinkSelectionNames.at(kPhotonDcaZMax), config.photonDcaZMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kAbsUpperLimit, true, config.requireTof.value, false); + this->addComments(kChaDaughTofProton, "requireTof = " + std::to_string(config.requireTof.value) + "; keepTracksWithoutTof = " + std::to_string(mKeepTracksWithoutTof)); + + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {kinkFilterNames.at(kPtMin), mPtMin}, + {kinkFilterNames.at(kPtMax), mPtMax}, + {kinkFilterNames.at(kEtaMin), mEtaMin}, + {kinkFilterNames.at(kEtaMax), mEtaMax}, + {kinkFilterNames.at(kPhiMin), mPhiMin}, + {kinkFilterNames.at(kPhiMax), mPhiMax}, + {kinkFilterNames.at(kMassMin), mMassLowerLimit}, + {kinkFilterNames.at(kMassMax), mMassUpperLimit}, + {kinkFilterNames.at(kMlScoreMin), mMlScoreMin}, + }); + }; + + template + void computeKinematics(T const& sigmaPlusPcmCand) + { + std::array momMother = {sigmaPlusPcmCand.pxSigmaPlus(), sigmaPlusPcmCand.pySigmaPlus(), sigmaPlusPcmCand.pzSigmaPlus()}; + mPt = RecoDecay::pt(momMother); + mEta = RecoDecay::eta(momMother); + mPhi = RecoDecay::phi(momMother); + mMass = sigmaPlusPcmCand.massSigmaPlus(); + mMlScore = sigmaPlusPcmCand.mlScore(); + } + + template + bool checkFilters(T const& /*sigmaPlusPcmCand*/) const + { + bool pass = true; + bool p = false; + + p = mPt > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = mPt < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = mEta > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = mEta < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = mPhi > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = mPhi < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + p = mMass > mMassLowerLimit; + this->template fillFilter(kMassMin, p); + pass &= p; + + p = mMass < mMassUpperLimit; + this->template fillFilter(kMassMax, p); + pass &= p; + + p = mMlScore >= mMlScoreMin; + this->template fillFilter(kMlScoreMin, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; + } + + template + void applySelections(T1 const& sigmaPlusPcmCand, T2 const& /*tracks*/) + { + this->reset(); + this->evaluateObservable(kFlightDirTiltMax, sigmaPlusPcmCand.flightDirTilt()); + this->evaluateObservable(kMothDcaPvMax, sigmaPlusPcmCand.candDcaToPV()); + this->evaluateObservable(kChaDauDcaZMax, sigmaPlusPcmCand.protonDcaZ()); + this->evaluateObservable(kPhotonDcaZMax, sigmaPlusPcmCand.photonDcaZToPV()); + + auto chaDaughter = sigmaPlusPcmCand.template protonTrack_as(); + this->evaluateObservable(kChaDaughTpcProton, chaDaughter.tpcNSigmaPr()); + if (chaDaughter.hasTOF()) { + this->evaluateObservable(kChaDaughTofProton, chaDaughter.tofNSigmaPr()); + } else { + this->evaluateObservable(kChaDaughTofProton, mKeepTracksWithoutTof ? 0.f : 999.f); + } + + this->assembleBitmask(); + }; + + [[nodiscard]] float getPt() const { return mPt; } + [[nodiscard]] float getEta() const { return mEta; } + [[nodiscard]] float getPhi() const { return mPhi; } + [[nodiscard]] float getMass() const { return mMass; } + + private: + float mMassLowerLimit = 1.1f; + float mMassUpperLimit = 1.3f; + float mMlScoreMin = -1.f; + bool mKeepTracksWithoutTof = true; + + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 6.f; + float mEtaMin = -1.f; + float mEtaMax = 1.f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + + // mother kinematic + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + float mMlScore = 0.f; +}; + +template +class SigmaPlusPcmBuilder +{ + public: + SigmaPlusPcmBuilder() = default; + ~SigmaPlusPcmBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext, T5& trackBuilder) + { + LOG(info) << "Initialize femto SigmaPlusPcm builder..."; + mProduceSigmaPlusPcm = utils::enableTable("FSigmaPlusPcm_001", table.produceSigmaPlusPcm.value, initContext); + mProduceLiteSigmaPlusPcm = utils::enableTable("FLiteSigmaPlusPcm_001", table.produceLiteSigmaPlusPcm.value, initContext); + mProduceSigmaPlusPcmMasks = utils::enableTable("FSigmaPlusPcmMasks_001", table.produceSigmaPlusPcmMasks.value, initContext); + mProduceSigmaPlusPcmMlScores = utils::enableTable("FSigmaPlusPcmMlScores_001", table.produceSigmaPlusPcmMlScores.value, initContext); + mProduceSigmaPlusPcmExtras = utils::enableTable("FSigmaPlusPcmExtras_001", table.produceSigmaPlusPcmExtras.value, initContext); + + if (mProduceSigmaPlusPcm && mProduceLiteSigmaPlusPcm) { + LOG(fatal) << "FSigmaPlusPcm and FLiteSigmaPlusPcm are mutually exclusive -- enable only one. " + << "FLiteSigmaPlusPcm is meant to replace FSigmaPlusPcm at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FSigmaPlusPcm from FLiteSigmaPlusPcm downstream."; + } + if (mProduceSigmaPlusPcm && !trackBuilder.producingTracks()) { + LOG(fatal) << "FSigmaPlusPcm is enabled, but the track builder is not producing FTracks (full precision). " + << "FSigmaPlusPcm stores the daughter index into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteSigmaPlusPcm if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteSigmaPlusPcm && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteSigmaPlusPcm is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteSigmaPlusPcm stores the daughter index into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FSigmaPlusPcm if TrackTables.produceTracks is enabled instead."; + } + + if (mProduceSigmaPlusPcm || mProduceLiteSigmaPlusPcm || mProduceSigmaPlusPcmMasks || mProduceSigmaPlusPcmMlScores || mProduceSigmaPlusPcmExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mSigmaPlusPcmSelection.configure(registry, config, filter); + mSigmaPlusPcmSelection.printSelections(kinkSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillSigmaPlusPcms(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& kinkProducts, T6 const& sigmaPlusPcms, T7 const& tracks, T8& trackBuilder) + { + if (!mFillAnyTable) { + return; + } + int64_t daughterIndex = 0; + + for (const auto& sigmaPlusPcm : sigmaPlusPcms) { + mSigmaPlusPcmSelection.computeKinematics(sigmaPlusPcm); + if (!mSigmaPlusPcmSelection.checkFilters(sigmaPlusPcm)) { + continue; + } + mSigmaPlusPcmSelection.applySelections(sigmaPlusPcm, tracks); + if (!mSigmaPlusPcmSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillCollision(collisionProducts, col); + + auto daughter = sigmaPlusPcm.template protonTrack_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, collisionBuilder); + fillSigmaPlusPcm(collisionBuilder, kinkProducts, sigmaPlusPcm, daughterIndex); + } + } + + template + void fillMcSigmaPlusPcms(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& kinkProducts, T7 const& sigmaPlusPcms, T8 const& tracks, T9& trackBuilder, T10 const& mcParticles, T11& mcBuilder, T12& mcProducts) + { + if (!mFillAnyTable) { + return; + } + int64_t daughterIndex = 0; + + for (const auto& sigmaPlusPcm : sigmaPlusPcms) { + mSigmaPlusPcmSelection.computeKinematics(sigmaPlusPcm); + if (!mSigmaPlusPcmSelection.checkFilters(sigmaPlusPcm)) { + continue; + } + mSigmaPlusPcmSelection.applySelections(sigmaPlusPcm, tracks); + if (!mSigmaPlusPcmSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); + + auto daughter = sigmaPlusPcm.template protonTrack_as(); + daughterIndex = trackBuilder.template getDaughterIndex(daughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); + fillSigmaPlusPcm(collisionBuilder, kinkProducts, sigmaPlusPcm, daughterIndex); + mcBuilder.template fillMcSigmaPlusPcmWithLabel(sigmaPlusPcm, daughter, mcParticles, mcCols, mcProducts); + } + } + + template + void fillSigmaPlusPcm(T1& collisionBuilder, T2& kinkProducts, T3 const& sigmaPlusPcm, int64_t daughterIndex) + { + float signedPt = sigmaPlusPcm.protonSign() * mSigmaPlusPcmSelection.getPt(); + if (mProduceSigmaPlusPcm) { + kinkProducts.producedSigmaPlusPcm(collisionBuilder.collisionIndex(), + signedPt, + mSigmaPlusPcmSelection.getEta(), + mSigmaPlusPcmSelection.getPhi(), + mSigmaPlusPcmSelection.getMass(), + daughterIndex); + } + if (mProduceLiteSigmaPlusPcm) { + kinkProducts.producedLiteSigmaPlusPcm(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(signedPt), + o2::aod::femtobase::lite::binEta(mSigmaPlusPcmSelection.getEta()), + o2::aod::femtobase::lite::binPhi(mSigmaPlusPcmSelection.getPhi()), + o2::aod::femtokinks::lite::binSigmaMass(mSigmaPlusPcmSelection.getMass()), + daughterIndex); + } + if (mProduceSigmaPlusPcmMasks) { + kinkProducts.producedSigmaPlusPcmMasks(mSigmaPlusPcmSelection.getBitmask()); + } + if (mProduceSigmaPlusPcmMlScores) { + kinkProducts.producedSigmaPlusPcmMlScores(sigmaPlusPcm.mlScore()); + } + if (mProduceSigmaPlusPcmExtras) { + kinkProducts.producedSigmaPlusPcmExtras( + sigmaPlusPcm.flightDirTilt(), + sigmaPlusPcm.candDcaToPV(), + sigmaPlusPcm.protonDcaXY(), + sigmaPlusPcm.protonDcaZ(), + sigmaPlusPcm.nSigmaTPCProton(), + sigmaPlusPcm.nSigmaTOFProton(), + sigmaPlusPcm.photonDcaXYToPV(), + sigmaPlusPcm.photonDcaZToPV(), + sigmaPlusPcm.photonCosPAToPV(), + sigmaPlusPcm.photonConvRadius(), + sigmaPlusPcm.antiSigmaPointingAngle(), + sigmaPlusPcm.dcaProtonGamma(), + sigmaPlusPcm.radius()); + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mSigmaPlusPcmSelection.isPassThrough(); } + + private: + SigmaPlusPcmSelection mSigmaPlusPcmSelection; + bool mFillAnyTable = false; + bool mProduceSigmaPlusPcm = false; + bool mProduceLiteSigmaPlusPcm = false; + bool mProduceSigmaPlusPcmMasks = false; + bool mProduceSigmaPlusPcmMlScores = false; + bool mProduceSigmaPlusPcmExtras = false; +}; + struct ConfKinkTablesDerivedToDerived : o2::framework::ConfigurableGroup { std::string prefix = std::string("KinkTables"); - o2::framework::Configurable limitSigma{"limitSigma", 1, "At least this many sigmas need to be in the collision"}; - o2::framework::Configurable limitSigmaPlus{"limitSigmaPlus", 0, "At least this many sigma pluses need to be in the collision"}; + o2::framework::Configurable limitSigma{"limitSigma", 1, "Require at least this many Sigmas in the collision. Set to 0 to skip Sigmas entirely (not written to output)."}; + o2::framework::Configurable limitSigmaPlus{"limitSigmaPlus", 0, "Require at least this many SigmaPlus in the collision. Set to 0 to skip SigmaPlus entirely (not written to output)."}; + o2::framework::Configurable limitSigmaPlusPcm{"limitSigmaPlusPcm", 0, "Require at least this many SigmaPlusPcm in the collision. Set to 0 to skip SigmaPlusPcm entirely (not written to output)."}; }; struct KinkBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { - o2::framework::Produces producedSigmas; - o2::framework::Produces producedSigmaMasks; - o2::framework::Produces producedSigmaPluses; - o2::framework::Produces producedSigmaPlusMasks; + o2::framework::Produces producedSigmas; + o2::framework::Produces producedSigmaMasks; + o2::framework::Produces producedSigmaPluses; + o2::framework::Produces producedSigmaPlusMasks; + o2::framework::Produces producedSigmaPlusExtras; + o2::framework::Produces producedSigmaPlusPcms; + o2::framework::Produces producedSigmaPlusPcmMasks; + o2::framework::Produces producedSigmaPlusPcmMlScores; + o2::framework::Produces producedSigmaPlusPcmExtras; }; class KinkBuilderDerivedToDerived { public: - KinkBuilderDerivedToDerived() = default; - ~KinkBuilderDerivedToDerived() = default; - template void init(T& config) { mLimitSigma = config.limitSigma.value; mLimitSigmaPlus = config.limitSigmaPlus.value; + mLimitSigmaPlusPcm = config.limitSigmaPlusPcm.value; - if (mLimitSigma == 0 && mLimitSigmaPlus == 0) { - LOG(fatal) << "Both sigma limit and sigmaplus limit are 0. Breaking..."; + if (mLimitSigma < 0 || mLimitSigmaPlus < 0 || mLimitSigmaPlusPcm < 0) { + LOG(fatal) << "Kink limits must be non-negative (got " << mLimitSigma << ", " << mLimitSigmaPlus << " and " << mLimitSigmaPlusPcm << "). Breaking..."; + } + if (mLimitSigma == 0 && mLimitSigmaPlus == 0 && mLimitSigmaPlusPcm == 0) { + LOG(fatal) << "Sigma, sigmaplus and sigmapluspcm limits are all 0. Breaking..."; } } template bool collisionHasTooFewSigma(T1 const& col, T2 const& /*sigmaTable*/, T3& partitionSigma, T4& cache) { - auto sigmaSlice = partitionSigma->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (sigmaSlice.size() >= mLimitSigma) { + if (mLimitSigma == 0) { // sigmas disabled, cannot reject on them return false; } - return true; + auto sigmaSlice = partitionSigma->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return sigmaSlice.size() < static_cast(mLimitSigma); } template bool collisionHasTooFewSigmaPlus(T1 const& col, T2 const& /*sigmaPlusTable*/, T3& partitionSigmaPlus, T4& cache) { + if (mLimitSigmaPlus == 0) { // sigma pluses disabled, cannot reject on them + return false; + } auto sigmaPlusSlice = partitionSigmaPlus->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (sigmaPlusSlice.size() >= mLimitSigmaPlus) { + return sigmaPlusSlice.size() < static_cast(mLimitSigmaPlus); + } + + template + bool collisionHasTooFewSigmaPlusPcm(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3& partitionSigmaPlusPcm, T4& cache) + { + if (mLimitSigmaPlusPcm == 0) { // sigma pluses (PCM) disabled, cannot reject on them return false; } - return true; + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return sigmaPlusPcmSlice.size() < static_cast(mLimitSigmaPlusPcm); } template void processSigma(T1 const& col, T2 const& /*sigmaTable*/, T3 const& oldTrackTable, T4& partitionSigma, T5& trackBuilder, T6& cache, T7& newSigmaTable, T8& newTrackTable, T9& newCollisionTable) { + if (mLimitSigma == 0) { // sigmas disabled + return; + } + auto sigmaSlice = partitionSigma->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); for (auto const& sigma : sigmaSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigma.chaDauId(), oldTrackTable)); - auto chaDaughter = oldTrackTable.rawIteratorAt(sigma.chaDauId() - oldTrackTable.offset()); - - int chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable); + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable); newSigmaTable.producedSigmas(newCollisionTable.producedCollision.lastIndex(), sigma.signedPt(), @@ -662,13 +1210,68 @@ class KinkBuilderDerivedToDerived template void processSigmaPlus(T1 const& col, T2 const& /*sigmaPlusTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlus, T5& trackBuilder, T6& cache, T7& newSigmaPlusTable, T8& newTrackTable, T9& newCollisionTable) { + if (mLimitSigmaPlus == 0) { // sigma pluses disabled + return; + } + auto sigmaPlusSlice = partitionSigmaPlus->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); for (auto const& sigmaPlus : sigmaPlusSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlus.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable); + + newSigmaPlusTable.producedSigmaPluses(newCollisionTable.producedCollision.lastIndex(), + sigmaPlus.signedPt(), + sigmaPlus.eta(), + sigmaPlus.phi(), + sigmaPlus.mass(), + chaDaughterIndex); + newSigmaPlusTable.producedSigmaPlusMasks(sigmaPlus.mask()); + fillSigmaPlusExtensions(sigmaPlus, newSigmaPlusTable); + } + } - auto chaDaughter = oldTrackTable.rawIteratorAt(sigmaPlus.chaDauId() - oldTrackTable.offset()); + template + void processSigmaWithMc(T1 const& col, T2 const& /*sigmaTable*/, T3 const& oldTrackTable, T4& partitionSigma, T5& trackBuilder, T6& cache, T7& newSigmaTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigma == 0) { + return; + } + + auto sigmaSlice = partitionSigma->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigma : sigmaSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigma.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaTable.producedSigmas(newCollisionTable.producedCollision.lastIndex(), + sigma.signedPt(), + sigma.eta(), + sigma.phi(), + sigma.mass(), + chaDaughterIndex); + newSigmaTable.producedSigmaMasks(sigma.mask()); + mcBuilder.fillSigmaWithLabel(sigma, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + + template + void processSigmaPlusWithMc(T1 const& col, T2 const& /*sigmaPlusTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlus, T5& trackBuilder, T6& cache, T7& newSigmaPlusTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigmaPlus == 0) { + return; + } + + auto sigmaPlusSlice = partitionSigmaPlus->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlus : sigmaPlusSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlus.chaDauId(), oldTrackTable)); - int chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable); + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); newSigmaPlusTable.producedSigmaPluses(newCollisionTable.producedCollision.lastIndex(), sigmaPlus.signedPt(), @@ -677,14 +1280,110 @@ class KinkBuilderDerivedToDerived sigmaPlus.mass(), chaDaughterIndex); newSigmaPlusTable.producedSigmaPlusMasks(sigmaPlus.mask()); + fillSigmaPlusExtensions(sigmaPlus, newSigmaPlusTable); + mcBuilder.fillSigmaPlusWithLabel(sigmaPlus, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + + template + void processSigmaPlusPcm(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlusPcm, T5& trackBuilder, T6& cache, T7& newSigmaPlusPcmTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitSigmaPlusPcm == 0) { // sigma pluses (PCM) disabled + return; + } + + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlusPcm : sigmaPlusPcmSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlusPcm.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable); + + newSigmaPlusPcmTable.producedSigmaPlusPcms(newCollisionTable.producedCollision.lastIndex(), + sigmaPlusPcm.signedPt(), + sigmaPlusPcm.eta(), + sigmaPlusPcm.phi(), + sigmaPlusPcm.mass(), + chaDaughterIndex); + newSigmaPlusPcmTable.producedSigmaPlusPcmMasks(sigmaPlusPcm.mask()); + fillSigmaPlusPcmExtensions(sigmaPlusPcm, newSigmaPlusPcmTable); + } + } + + template + void processSigmaPlusPcmWithMc(T1 const& col, T2 const& /*sigmaPlusPcmTable*/, T3 const& oldTrackTable, T4& partitionSigmaPlusPcm, T5& trackBuilder, T6& cache, T7& newSigmaPlusPcmTable, T8& newTrackTable, T9& newCollisionTable, + T10& mcBuilder, T11 const& mcCols, T12 const& mcParticles, T13 const& mcMothers, T14 const& mcPartonicMothers, T15& mcProducts) + { + if (mLimitSigmaPlusPcm == 0) { + return; + } + + auto sigmaPlusPcmSlice = partitionSigmaPlusPcm->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& sigmaPlusPcm : sigmaPlusPcmSlice) { + auto chaDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(sigmaPlusPcm.chaDauId(), oldTrackTable)); + + int64_t chaDaughterIndex = trackBuilder.getDaughterIndex(chaDaughter, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + + newSigmaPlusPcmTable.producedSigmaPlusPcms(newCollisionTable.producedCollision.lastIndex(), + sigmaPlusPcm.signedPt(), + sigmaPlusPcm.eta(), + sigmaPlusPcm.phi(), + sigmaPlusPcm.mass(), + chaDaughterIndex); + newSigmaPlusPcmTable.producedSigmaPlusPcmMasks(sigmaPlusPcm.mask()); + fillSigmaPlusPcmExtensions(sigmaPlusPcm, newSigmaPlusPcmTable); + mcBuilder.fillSigmaPlusPcmWithLabel(sigmaPlusPcm, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); } } private: + template + void fillSigmaPlusExtensions(T1 const& sigmaPlus, T2& newSigmaPlusTable) + { + if constexpr (requires(T1 t) { t.transRadius(); }) { + newSigmaPlusTable.producedSigmaPlusExtras(sigmaPlus.kinkAngle(), + sigmaPlus.dcaDaugToPV(), + sigmaPlus.dcaMothToPV(), + sigmaPlus.decayVtxX(), + sigmaPlus.decayVtxY(), + sigmaPlus.decayVtxZ(), + sigmaPlus.transRadius(), + sigmaPlus.dcaKinkTopo(), + sigmaPlus.dauTpcNSigmaPr(), + sigmaPlus.dauTpcNSigmaPi(), + sigmaPlus.dauTofNSigmaPr(), + sigmaPlus.ptOriginal(), + sigmaPlus.itsClusterSizes()); + } + } + + template + void fillSigmaPlusPcmExtensions(T1 const& sigmaPlusPcm, T2& newSigmaPlusPcmTable) + { + if constexpr (requires(T1 t) { t.mlScore(); }) { + newSigmaPlusPcmTable.producedSigmaPlusPcmMlScores(sigmaPlusPcm.mlScore()); + } + if constexpr (requires(T1 t) { t.photonConvRadius(); }) { + newSigmaPlusPcmTable.producedSigmaPlusPcmExtras(sigmaPlusPcm.flightDirTilt(), + sigmaPlusPcm.dcaMothToPV(), + sigmaPlusPcm.dauDcaXY(), + sigmaPlusPcm.dauDcaZ(), + sigmaPlusPcm.dauTpcNSigmaPr(), + sigmaPlusPcm.dauTofNSigmaPr(), + sigmaPlusPcm.photonDcaXYToPV(), + sigmaPlusPcm.photonDcaZToPV(), + sigmaPlusPcm.photonCosPAToPV(), + sigmaPlusPcm.photonConvRadius(), + sigmaPlusPcm.antiSigmaPointingAngle(), + sigmaPlusPcm.dcaDauPhoton(), + sigmaPlusPcm.transRadius()); + } + } + int mLimitSigma = 0; int mLimitSigmaPlus = 0; + int mLimitSigmaPlusPcm = 0; }; - -} // namespace kinkbuilder -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::kinkbuilder #endif // PWGCF_FEMTO_CORE_KINKBUILDER_H_ diff --git a/PWGCF/Femto/Core/kinkHistManager.h b/PWGCF/Femto/Core/kinkHistManager.h index f0a5c5dae3f..2d9eed98b87 100644 --- a/PWGCF/Femto/Core/kinkHistManager.h +++ b/PWGCF/Femto/Core/kinkHistManager.h @@ -37,9 +37,7 @@ #include #include -namespace o2::analysis::femto -{ -namespace kinkhistmanager +namespace o2::analysis::femto::kinkhistmanager { // enum for kink histograms enum KinkHist { @@ -88,24 +86,31 @@ enum KinkHist { constexpr std::size_t MaxSecondary = 3; -#define KINK_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ - o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ - o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ - o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ - o2::framework::ConfigurableAxis mass{"mass", {{200, defaultMassMin, defaultMassMax}}, "Mass"}; \ - o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "PDG codes of selected V0s"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define KINK_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ + o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "PDG codes of selected kinks"}; -template +template struct ConfSigmaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; KINK_DEFAULT_BINNING(1.1, 1.3) }; -template +template struct ConfSigmaPlusBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; KINK_DEFAULT_BINNING(1.1, 1.3) }; +template +struct ConfSigmaPlusPcmBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + KINK_DEFAULT_BINNING(1.1, 1.3) +}; + #undef KINK_DEFAULT_BINNING constexpr const char PrefixSigmaBinning1[] = "SigmaBinning1"; @@ -114,7 +119,10 @@ using ConfSigmaBinning1 = ConfSigmaBinning; constexpr const char PrefixSigmaPlusBinning1[] = "SigmaPlusBinning1"; using ConfSigmaPlusBinning1 = ConfSigmaPlusBinning; -template +constexpr const char PrefixSigmaPlusPcmBinning1[] = "SigmaPlusPcmBinning1"; +using ConfSigmaPlusPcmBinning1 = ConfSigmaPlusPcmBinning; + +template struct ConfKinkQaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::Configurable plot2d{"plot2d", true, "Enable 2d QA h histograms"}; @@ -157,7 +165,7 @@ constexpr std::array, kKinkHistLast> HistTable = {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed kinks; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed kink; PDG Code; Entries"}, - {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother reconstructed knik; PDG Code; Entries"}, + {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother of reconstructed kink; PDG Code; Entries"}, {kTruePt, o2::framework::HistType::kTH1F, "hTruePt", "True transverse momentum; p_{T} (GeV/#it{c}); Entries"}, {kTrueEta, o2::framework::HistType::kTH1F, "hTrueEta", "True pseudorapdity; #eta; Entries"}, {kTruePhi, o2::framework::HistType::kTH1F, "hTruePhi", "True azimuthal angle; #varphi; Entries"}, @@ -167,50 +175,54 @@ constexpr std::array, kKinkHistLast> HistTable = {kFromMaterial, o2::framework::HistType::kTH2F, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); kink angle"}, {kMissidentified, o2::framework::HistType::kTH2F, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); kink angle"}, {kSecondary1, o2::framework::HistType::kTH2F, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); kink angle"}, - {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); kink angle"}}}; + {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, + {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); kink angle"}, + {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); kink angle"}}}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define KINK_HIST_ANALYSIS_MAP(conf) \ - {kPt, {conf.pt}}, \ - {kEta, {conf.eta}}, \ - {kPhi, {conf.phi}}, \ - {kMass, {conf.mass}}, \ - {kSign, {conf.sign}}, - -#define KINK_HIST_MC_MAP(conf) \ - {kTruePt, {conf.pt}}, \ - {kTrueEta, {conf.eta}}, \ - {kTruePhi, {conf.phi}}, \ - {kPdg, {conf.pdgCodes}}, \ - {kPdgMother, {conf.pdgCodes}}, \ - {kPdgPartonicMother, {conf.pdgCodes}}, - -#define KINK_HIST_QA_MAP(confAnalysis, confQa) \ - {kKinkAngle, {confQa.kinkAngle}}, \ - {kDcaMothToPV, {confQa.dcaMothToPV}}, \ - {kDcaDaugToPV, {confQa.dcaDaugToPV}}, \ - {kDecayVtxX, {confQa.decayVertex}}, \ - {kDecayVtxY, {confQa.decayVertex}}, \ - {kDecayVtxZ, {confQa.decayVertex}}, \ - {kDecayVtx, {confQa.decayVertex}}, \ - {kTransRadius, {confQa.transRadius}}, \ - {kPtVsEta, {confAnalysis.pt, confAnalysis.eta}}, \ - {kPtVsPhi, {confAnalysis.pt, confAnalysis.phi}}, \ - {kPhiVsEta, {confAnalysis.phi, confAnalysis.eta}}, \ - {kPtVsKinkAngle, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kPtVsDecayRadius, {confAnalysis.pt, confQa.transRadius}}, - -#define KINK_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kPrimary, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kFromWrongCollision, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kFromMaterial, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kMissidentified, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kSecondary1, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kSecondary2, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kSecondary3, {confAnalysis.pt, confQa.kinkAngle}}, \ - {kSecondaryOther, {confAnalysis.pt, confQa.kinkAngle}}, + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define KINK_HIST_MC_MAP(conf) \ + {kTruePt, {(conf).pt}}, \ + {kTrueEta, {(conf).eta}}, \ + {kTruePhi, {(conf).phi}}, \ + {kPdg, {(conf).pdgCodes}}, \ + {kPdgMother, {(conf).pdgCodes}}, \ + {kPdgPartonicMother, {(conf).pdgCodes}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define KINK_HIST_QA_MAP(confAnalysis, confQa) \ + {kKinkAngle, {(confQa).kinkAngle}}, \ + {kDcaMothToPV, {(confQa).dcaMothToPV}}, \ + {kDcaDaugToPV, {(confQa).dcaDaugToPV}}, \ + {kDecayVtxX, {(confQa).decayVertex}}, \ + {kDecayVtxY, {(confQa).decayVertex}}, \ + {kDecayVtxZ, {(confQa).decayVertex}}, \ + {kDecayVtx, {(confQa).decayVertex}}, \ + {kTransRadius, {(confQa).transRadius}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsKinkAngle, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kPtVsDecayRadius, {(confAnalysis).pt, (confQa).transRadius}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define KINK_HIST_MC_QA_MAP(confAnalysis, confQa) \ + {kNoMcParticle, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kPrimary, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kFromWrongCollision, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kFromMaterial, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kMissidentified, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kSecondary1, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kSecondary2, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kSecondary3, {(confAnalysis).pt, (confQa).kinkAngle}}, \ + {kSecondaryOther, {(confAnalysis).pt, (confQa).kinkAngle}}, template auto makeKinkHistSpecMap(const T& confBinningAnalysis) @@ -251,11 +263,15 @@ std::map> makeKinkMcQaHistSpecMap #undef KINK_HIST_MC_QA_MAP constexpr char PrefixSigmaQa[] = "SigmaQA/"; -constexpr char PrefixSigma1[] = "Sigma1/"; +constexpr char PrefixSigma1Se[] = "Sigma1/SE/"; +constexpr char PrefixSigma1Me[] = "Sigma1/ME/"; constexpr char PrefixSigma2[] = "Sigma2/"; constexpr char PrefixSigmaPlusQa[] = "SigmaPlusQA/"; -constexpr char PrefixSigmaPlus1[] = "SigmaPlus1/"; +constexpr char PrefixSigmaPlus1Se[] = "SigmaPlus1/SE/"; +constexpr char PrefixSigmaPlus1Me[] = "SigmaPlus1/ME/"; constexpr char PrefixSigmaPlus2[] = "SigmaPlus2/"; +constexpr char PrefixSigmaPlusPcm1Se[] = "SigmaPlusPcm1/SE/"; +constexpr char PrefixSigmaPlusPcm1Me[] = "SigmaPlusPcm1/ME/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; @@ -263,8 +279,8 @@ constexpr std::string_view McDir = "MC/"; constexpr int AbsChargeDaughters = 1; -template class KinkHistManager { @@ -280,25 +296,27 @@ class KinkHistManager std::map> const& ChaDauSpecs) { mHistogramRegistry = registry; - mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value) * ConfKinkSelection.sign.value; + mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value); int chaDauPdgCodeAbs = 0; int chaDauCharge = 0; const int absCharge = 1; - if (std::abs(mPdgCode) == PDG_t::kSigmaMinus) { + if (mPdgCode == PDG_t::kSigmaMinus) { if (ConfKinkSelection.sign.value < 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); chaDauCharge = -1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma- is positively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); chaDauCharge = 1; } - } else if (std::abs(mPdgCode) == PDG_t::kSigmaPlus) { + } else if (mPdgCode == PDG_t::kSigmaPlus) { if (ConfKinkSelection.sign.value > 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kProton); chaDauCharge = 1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma+ is negatively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kProtonBar); chaDauCharge = -1; } @@ -307,7 +325,7 @@ class KinkHistManager } mChaDauManager.template init(registry, ChaDauSpecs, absCharge, chaDauCharge, chaDauPdgCodeAbs); - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { this->initAnalysis(KinkSpecs); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { @@ -328,26 +346,28 @@ class KinkHistManager T3 const& ConfChaDauBinningQa) { mHistogramRegistry = registry; - mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value) * ConfKinkSelection.sign.value; + mPdgCode = std::abs(ConfKinkSelection.pdgCodeAbs.value); this->enableOptionalHistograms(ConfKinkBinningQa); int chaDauPdgCodeAbs = 0; int chaDauCharge = 0; const int absCharge = 1; - if (std::abs(mPdgCode) == PDG_t::kSigmaMinus) { + if (mPdgCode == PDG_t::kSigmaMinus) { if (ConfKinkSelection.sign.value < 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); chaDauCharge = -1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma- is positively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); chaDauCharge = 1; } - } else if (std::abs(mPdgCode) == PDG_t::kSigmaPlus) { + } else if (mPdgCode == PDG_t::kSigmaPlus) { if (ConfKinkSelection.sign.value > 0) { chaDauPdgCodeAbs = std::abs(PDG_t::kProton); chaDauCharge = 1; } else { + mPdgCode = -1 * mPdgCode; // anti-Sigma+ is negatively charged and has negative pdg code chaDauPdgCodeAbs = std::abs(PDG_t::kProtonBar); chaDauCharge = -1; } @@ -356,7 +376,7 @@ class KinkHistManager } mChaDauManager.template init(registry, ChaDauSpecs, absCharge, chaDauCharge, chaDauPdgCodeAbs, ConfChaDauBinningQa); - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { this->initAnalysis(KinkSpecs); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { @@ -374,7 +394,7 @@ class KinkHistManager // auto chaDaughter = kinkcandidate.template chaDau_as(); auto chaDaughter = tracks.rawIteratorAt(kinkCandidate.chaDauId() - tracks.offset()); mChaDauManager.template fill(chaDaughter, tracks); - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { this->fillAnalysis(kinkCandidate); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { @@ -382,19 +402,19 @@ class KinkHistManager } } - template - void fill(T1 const& kinkCandidate, T2 const& tracks, T3 const& mcParticles, T4 const& mcMothers, T5 const& mcPartonicMothers) + template + void fill(T1 const& kinkCandidate, T2 const& tracks, T3 const& col, T4 const& mcParticles, T5 const& mcMothers, T6 const& mcPartonicMothers) { auto chaDaughter = tracks.rawIteratorAt(kinkCandidate.chaDauId() - tracks.offset()); - mChaDauManager.template fill(chaDaughter, tracks, mcParticles, mcMothers, mcPartonicMothers); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + mChaDauManager.template fill(chaDaughter, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { this->fillAnalysis(kinkCandidate); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { this->fillQa(kinkCandidate); } if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { - this->template fillMc(kinkCandidate, mcParticles, mcMothers, mcPartonicMothers); + this->template fillMc(kinkCandidate, col, mcParticles, mcMothers, mcPartonicMothers); } } @@ -498,7 +518,7 @@ class KinkHistManager mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), kinkcandidate.phi()); mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), kinkcandidate.mass()); - if constexpr (isEqual(kink, modes::Kink::kSigma) || isEqual(kink, modes::Kink::kSigmaPlus)) { + if constexpr (isEqual(kink, modes::Kink::kSigma) || isEqual(kink, modes::Kink::kSigmaPlus) || isEqual(kink, modes::Kink::kSigmaPlusPcm)) { mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(AnalysisDir) + HIST(getHistName(kSign, HistTable)), kinkcandidate.sign()); } } @@ -526,8 +546,8 @@ class KinkHistManager } } - template - void fillMc(T1 const& kinkCandidate, T2 const& /*mcParticles*/, T3 const& /*mcMothers*/, T4 const& /*mcPartonicMothers*/) + template + void fillMc(T1 const& kinkCandidate, T2 const& col, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, T5 const& /*mcPartonicMothers*/) { // No MC Particle if (!kinkCandidate.has_fMcParticle()) { @@ -542,33 +562,37 @@ class KinkHistManager } // Retrieve MC particle - auto mcParticle = kinkCandidate.template fMcParticle_as(); + auto mcParticle = kinkCandidate.template fMcParticle_as(); + + // whether a particle is associated to a wrong collision or not cannot be known by the producer so we check it here + bool fromWrongCollision = mcParticle.fMcColId() != col.fMcColId(); - // missidentifed particles are special case // whether a particle is missidentfied or not cannot be known by the producer so we check it here bool isMissidentified = mcParticle.pdgCode() != mPdgCode; mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kTruePt, HistTable)), mcParticle.pt()); mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kTrueEta, HistTable)), mcParticle.eta()); mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kTruePhi, HistTable)), mcParticle.phi()); - if (isMissidentified) { - mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kMissidentified)); + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kFromWrongCollision)); + } else if (isMissidentified) { + mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kMissidentified)); } else { mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); } mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), mcParticle.pdgCode()); // get mother - if (kinkCandidate.has_fMcMother()) { - auto mother = kinkCandidate.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), mother.pdgCode()); } else { mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), 0); } // get partonic mother - if (kinkCandidate.has_fMcPartMoth()) { - auto partonicMother = kinkCandidate.template fMcPartMoth_as(); + if (mcParticle.has_fMcPartMoth()) { + auto partonicMother = mcParticle.template fMcPartMoth_as(); mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), partonicMother.pdgCode()); } else { mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); @@ -576,8 +600,9 @@ class KinkHistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { if (mPlotOrigins) { - // check first if particle is missidentified - if (isMissidentified) { + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); + } else if (isMissidentified) { // if it is, we fill it as such mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); } else { @@ -586,15 +611,12 @@ class KinkHistManager case modes::McOrigin::kPhysicalPrimary: mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); - break; case modes::McOrigin::kFromMaterial: mHistogramRegistry->fill(HIST(kinkPrefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), kinkCandidate.pt(), kinkCandidate.kinkAngle()); break; case modes::McOrigin::kFromSecondaryDecay: - if (kinkCandidate.has_fMcMother()) { - auto mother = kinkCandidate.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { @@ -609,7 +631,7 @@ class KinkHistManager } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } @@ -625,6 +647,5 @@ class KinkHistManager int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; }; -}; // namespace kinkhistmanager -}; // namespace o2::analysis::femto +}; // namespace o2::analysis::femto::kinkhistmanager #endif // PWGCF_FEMTO_CORE_KINKHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/mcBuilder.h b/PWGCF/Femto/Core/mcBuilder.h index d799265dd88..8cde761ad01 100644 --- a/PWGCF/Femto/Core/mcBuilder.h +++ b/PWGCF/Femto/Core/mcBuilder.h @@ -21,27 +21,34 @@ #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" +#include "Common/Core/RecoDecay.h" + +#include +#include #include #include #include +#include #include +#include #include #include #include #include #include -namespace o2::analysis::femto -{ -namespace mcbuilder +namespace o2::analysis::femto::mcbuilder { struct ConfMc : o2::framework::ConfigurableGroup { std::string prefix = std::string("MonteCarlo"); o2::framework::Configurable passThrough{"passThrough", false, "Passthrough all MC collisions and particles"}; o2::framework::Configurable findLastPartonicMother{"findLastPartonicMother", true, "If true, the partonic mother will be the first parton directly after the initial collision. If false, the partonic mother will be the last parton before hadronization"}; + o2::framework::Configurable etaAcceptanceMcOnly{"etaAcceptanceMcOnly", 0.8, "For MC ONLY processing. |eta| acceptance for estimating primary track multiplicity"}; + o2::framework::Configurable etaAcceptanceMcReco{"etaAcceptanceMcReco", 1, "For MC/RECO processing. |eta| acceptance for generated particles"}; + o2::framework::Configurable charmYGenMax{"charmYGenMax", 0.8f, "Max |y| (rapidity) for generated charm hadrons (mc-only truth acceptance)"}; }; struct McBuilderProducts : o2::framework::ProducesGroup { @@ -49,13 +56,17 @@ struct McBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedMcParticles; o2::framework::Produces producedMothers; o2::framework::Produces producedPartonicMothers; + o2::framework::Produces producedMcMotherLabels; o2::framework::Produces producedCollisionLabels; o2::framework::Produces producedTrackLabels; o2::framework::Produces producedLambdaLabels; o2::framework::Produces producedK0shortLabels; + o2::framework::Produces producedD0Labels; + o2::framework::Produces producedLcLabels; o2::framework::Produces producedSigmaLabels; o2::framework::Produces producedSigmaPlusLabels; + o2::framework::Produces producedSigmaPlusPcmLabels; o2::framework::Produces producedXiLabels; o2::framework::Produces producedOmegaLabels; }; @@ -66,17 +77,59 @@ struct ConfMcTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceMcParticles{"produceMcParticles", -1, "Produce MC particles (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceMcMothers{"produceMcMothers", -1, "Produce MC mother particles (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceMcPartonicMothers{"produceMcPartonicMothers", -1, "Produce MC partonic mother particles (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedMcMotherLabels{"producedMcMotherLabels", -1, "Produce mother/partonic-mother labels (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable producedCollisionLabels{"producedCollisionLabels", -1, "Produce MC partonic mother particles (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedCollisionLabels{"producedCollisionLabels", -1, "Produce MC collision labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedTrackLabels{"producedTrackLabels", -1, "Produce track labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedLambdaLabels{"producedLambdaLabels", -1, "Produce lambda labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedK0shortLabels{"producedK0shortLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable producedSigmaLabels{"producedSigmaLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable producedSigmaPlusLabels{"producedSigmaPlusLabels", -1, "Produce k0short labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedD0Labels{"producedD0Labels", -1, "Produce D0 labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedLcLabels{"producedLcLabels", -1, "Produce Lc labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaLabels{"producedSigmaLabels", -1, "Produce sigma labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaPlusLabels{"producedSigmaPlusLabels", -1, "Produce sigma plus labels (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producedSigmaPlusPcmLabels{"producedSigmaPlusPcmLabels", -1, "Produce sigma plus (PCM) labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedXiLabels{"producedXiLabels", -1, "Produce xi labels (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producedOmegaLabels{"producedOmegaLabels", -1, "Produce omega labels (-1: auto; 0 off; 1 on)"}; }; +// filter/selections for mc collision and mc particles + +struct ConfMcCollisionFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("McCollisionFilter"); + o2::framework::Configurable vtxZMin{"vtxZMin", -10.f, "Minimum vertex Z position (cm)"}; + o2::framework::Configurable vtxZMax{"vtxZMax", 10.f, "Maximum vertex Z position (cm)"}; + o2::framework::Configurable multMin{"multMin", 0.f, "Minimum multiplicity"}; + o2::framework::Configurable multMax{"multMax", 5000.f, "Maximum multiplicity"}; + o2::framework::Configurable centMin{"centMin", 0.f, "Minimum centrality (multiplicity percentile)"}; + o2::framework::Configurable centMax{"centMax", 100.f, "Maximum centrality (multiplicity percentile)"}; +}; + +template +struct ConfMcParticleSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string(Prefix); + // kinematic cuts + o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // pdg code and charge + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", 2212, "Absolute value of PDG code. Set sign of charge to -1 for antiparticle."}; + o2::framework::Configurable chargeSign{"chargeSign", 1, "Particle charge sign: +1 for positive, -1 for negative, 0 for both"}; + // origin + o2::framework::Configurable requireOrigin{"requireOrigin", false, "If true, only particles with the origin given in 'origin' are selected"}; + o2::framework::Configurable origin{"origin", static_cast(modes::McOrigin::kPhysicalPrimary), "Required mc origin, only used if requireOrigin is true (see modes::McOrigin; 2: physical primary)"}; +}; + +constexpr const char PrefixMcParticleSelection1[] = "McParticleSelection1"; +constexpr const char PrefixMcParticleSelection2[] = "McParticleSelection2"; +constexpr const char PrefixMcParticleSelection3[] = "McParticleSelection3"; + +using ConfMcParticleSelection1 = ConfMcParticleSelection; +using ConfMcParticleSelection2 = ConfMcParticleSelection; +using ConfMcParticleSelection3 = ConfMcParticleSelection; + class McBuilder { public: @@ -88,41 +141,52 @@ class McBuilder { LOG(info) << "Initialize monte carlo builder..."; + mPassThrough = config.passThrough.value; + mEtaAcceptanceMcOnly = config.etaAcceptanceMcOnly.value; + mEtaAcceptanceMcReco = config.etaAcceptanceMcReco.value; + mFindLastPartonicMother = config.findLastPartonicMother.value; + mCharmYGenMax = config.charmYGenMax.value; + mProduceMcCollisions = utils::enableTable("FMcCols_001", table.produceMcCollisions.value, initContext); mProduceMcParticles = utils::enableTable("FMcParticles_001", table.produceMcParticles.value, initContext); mProduceMcMothers = utils::enableTable("FMcMothers_001", table.produceMcMothers.value, initContext); mProduceMcPartonicMothers = utils::enableTable("FMcPartMoths_001", table.produceMcPartonicMothers.value, initContext); - - mProduceCollisionLabels = utils::enableTable("FColLabels", table.producedCollisionLabels.value, initContext); - mProduceTrackLabels = utils::enableTable("FTrackLabels", table.producedTrackLabels.value, initContext); - mProduceLambdaLabels = utils::enableTable("FLambdaLabels", table.producedLambdaLabels.value, initContext); - mProduceK0shortLabels = utils::enableTable("FK0shortLabels", table.producedK0shortLabels.value, initContext); - mProduceSigmaLabels = utils::enableTable("FSigmaLabels", table.producedSigmaLabels.value, initContext); - mProduceSigmaPlusLabels = utils::enableTable("FSigmaPlusLabels", table.producedSigmaPlusLabels.value, initContext); - mProduceXiLabels = utils::enableTable("FXiLabels", table.producedXiLabels.value, initContext); - mProduceOmegaLabels = utils::enableTable("FOmegaLabels", table.producedOmegaLabels.value, initContext); + mProduceMcMotherLabels = utils::enableTable("FMcMotherLabels_001", table.producedMcMotherLabels.value, initContext); + + mProduceCollisionLabels = utils::enableTable("FColLabels_001", table.producedCollisionLabels.value, initContext); + mProduceTrackLabels = utils::enableTable("FTrackLabels_001", table.producedTrackLabels.value, initContext); + mProduceLambdaLabels = utils::enableTable("FLambdaLabels_001", table.producedLambdaLabels.value, initContext); + mProduceK0shortLabels = utils::enableTable("FK0shortLabels_001", table.producedK0shortLabels.value, initContext); + mProduceD0Labels = utils::enableTable("FD0Labels_001", table.producedD0Labels.value, initContext); + mProduceLcLabels = utils::enableTable("FLcLabels_001", table.producedLcLabels.value, initContext); + mProduceSigmaLabels = utils::enableTable("FSigmaLabels_001", table.producedSigmaLabels.value, initContext); + mProduceSigmaPlusLabels = utils::enableTable("FSigmaPlusLabels_001", table.producedSigmaPlusLabels.value, initContext); + mProduceSigmaPlusPcmLabels = utils::enableTable("FSigmaPlusPcmLabels_001", table.producedSigmaPlusPcmLabels.value, initContext); + mProduceXiLabels = utils::enableTable("FXiLabels_001", table.producedXiLabels.value, initContext); + mProduceOmegaLabels = utils::enableTable("FOmegaLabels_001", table.producedOmegaLabels.value, initContext); if (mProduceMcCollisions || mProduceCollisionLabels || - mProduceMcParticles || mProduceMcMothers || mProduceMcPartonicMothers || + mProduceMcParticles || mProduceMcMotherLabels || + mProduceMcMothers || mProduceMcPartonicMothers || mProduceTrackLabels || mProduceLambdaLabels || mProduceK0shortLabels || - mProduceSigmaLabels || mProduceSigmaPlusLabels || - mProduceXiLabels || mProduceOmegaLabels) { + mProduceSigmaLabels || mProduceSigmaPlusLabels || mProduceSigmaPlusPcmLabels || + mProduceXiLabels || mProduceOmegaLabels || + mProduceD0Labels || mProduceLcLabels) { mFillAnyTable = true; } else { LOG(info) << "No tables configured..."; LOG(info) << "Initialization done..."; return; } - mPassThrough = config.passThrough.value; - mFindLastPartonicMother = config.findLastPartonicMother.value; LOG(info) << "Initialization done..."; } template - void fillMcCollisionWithLabel(T1& mcProducts, T2 const& col, T3 const& /*mcCols*/) + void fillMcCollisionWithLabel(T1 const& col, T2 const& /*mcCols*/, T3& mcProducts) { if (!mProduceCollisionLabels) { + mcProducts.producedCollisionLabels(-1); return; } // Case: This reconstructed collision has an MC collision @@ -132,86 +196,276 @@ class McBuilder auto it = mCollisionMap.find(originalIndex); if (it == mCollisionMap.end()) { // Not yet created → create it - auto mcCol = col.template mcCollision_as(); + auto mcCol = col.template mcCollision_as(); this->fillMcCollision(mcCol, mcProducts); } // Add label - mcProducts.producedCollisionLabels(mCollisionMap.at(originalIndex)); // mc collsions has been added so we can now safely retrieve the index + mcProducts.producedCollisionLabels(mCollisionMap.at(originalIndex)); // mc collision has been added so we can now safely retrieve the index } else { // If no MC collision associated, fill empty label mcProducts.producedCollisionLabels(-1); } } - template - void fillMcTrackWithLabel(T1 const& col, T2 const& mcCols, T3 const& track, T4 const& mcParticles, T5& mcProducts) + template + void fillMcCollision(T1 const& mcCol, T2& mcProducts) + { + // check if collision already exists + if (mCollisionMap.find(mcCol.globalIndex()) != mCollisionMap.end()) { + return; + } + float centrality = -1; + float multiplicity = -1; + if constexpr (modes::isFlagSet(system, modes::System::kPP)) { + centrality = mcCol.centFT0M(); + multiplicity = mcCol.multMCNParticlesEta08(); + } + if constexpr (modes::isFlagSet(system, modes::System::kPbPb)) { + centrality = mcCol.centFT0C(); + multiplicity = mcCol.multMCNParticlesEta08(); + } + + mcProducts.producedMcCollisions( + mcCol.posZ(), + multiplicity, + centrality); + mCollisionMap.emplace(mcCol.globalIndex(), mcProducts.producedMcCollisions.lastIndex()); + } + + // for mc only + template + void fillMcCollision(T1 const& mcCol, T2 const& mcParticles, T3& mcProducts, T4& pdgDb) + { + // check if collision already exists + if (mCollisionMap.find(mcCol.globalIndex()) != mCollisionMap.end()) { + return; + } + + float centrality = 0; // no centrality estimator for mc only, so set to 0 + float multiplicity = 0; // no multiplicity estimator for mc only + + // define multiplicity ourselves by counting primary particles for |eta|< some config threshold + // this is similar to how define it in data + for (auto const& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary() || std::fabs(mcParticle.eta()) > mEtaAcceptanceMcOnly) { + continue; + } + const auto* pdgParticle = pdgDb->GetParticle(mcParticle.pdgCode()); + if (pdgParticle == nullptr || std::fabs(pdgParticle->Charge()) < o2::constants::math::Almost0) { + continue; + } + multiplicity += 1; + } + + mcProducts.producedMcCollisions( + mcCol.posZ(), + multiplicity, + centrality); + mCollisionMap.emplace(mcCol.globalIndex(), mcProducts.producedMcCollisions.lastIndex()); + } + + template + void fillMcParticle(T1 const& mcParticle, T2 const& mcParticles, T3 const& mcCol, T4& mcProducts) + { + // charm hadrons get a prompt/non-prompt origin, consistent with the reco-matched path; + // all other particles use the generic getOrigin inside getOrCreateMcParticleRow + const int pdgAbs = std::abs(mcParticle.pdgCode()); + if (pdgAbs == o2::constants::physics::Pdg::kD0) { + // truth-level acceptance for the efficiency denominator: keep only + // generated D0 -> K pi decays inside the rapidity acceptance + int8_t sign = 0; + if (!RecoDecay::isMatchedMCGen(mcParticles, mcParticle, o2::constants::physics::Pdg::kD0, std::array{+kPiPlus, -kKPlus}, true, &sign)) { + return; + } + if (std::abs(mcParticle.y()) > mCharmYGenMax) { + return; + } + } + if (pdgAbs == o2::constants::physics::Pdg::kLambdaCPlus) { + int8_t sign = 0; + if (!RecoDecay::isMatchedMCGen(mcParticles, mcParticle, o2::constants::physics::Pdg::kLambdaCPlus, std::array{+kProton, -kKPlus, +kPiPlus}, true, &sign, 2)) { + return; + } + if (std::abs(mcParticle.y()) > mCharmYGenMax) { + return; + } + } + this->getOrCreateMcParticleRow(mcParticle, mcParticles, mcCol, mcProducts); + } + + /// Write all generated physical primaries within the eta acceptance. + /// No charge requirement is applied here: neutral primaries (e.g. Lambdas) are needed for generator-level + /// pair triggers. Charge (and any other) selection has to be done downstream. + /// NOTE: FMcParticles also contains rows created through reco labels (secondaries, particles outside the + /// acceptance), so a dNch/deta loop must still require origin == kPhysicalPrimary, a charged pdg code + /// and the eta acceptance + template + void fillMcPassThrough(T1 const& mcCols, T2 const& mcParticles, T3& perMcCollision, T4& mcProducts) + { + if (!mPassThrough) { + return; + } + for (const auto& mcCol : mcCols) { + // every MC collision unconditionally: E_all is counted over all of them + this->fillMcCollision(mcCol, mcProducts); + + auto particlesThisCollision = mcParticles.sliceBy(perMcCollision, mcCol.globalIndex()); + for (const auto& mcParticle : particlesThisCollision) { + if (!mcParticle.isPhysicalPrimary() || std::fabs(mcParticle.eta()) > mEtaAcceptanceMcReco) { + continue; + } + // NOTE: full mcParticles table, never the slice - the ancestry walk resolves global indices + this->fillMcParticle(mcParticle, mcParticles, mcCol, mcProducts); + } + } + } + + template + void fillMcTrackWithLabel(T1 const& track, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { if (!mProduceTrackLabels) { - mcProducts.producedTrackLabels(-1, -1, -1); + mcProducts.producedTrackLabels(-1); return; } - fillMcLabelGeneric(col, mcCols, track, mcParticles, mcProducts, [](auto& prod, int64_t p, int64_t m, int64_t pm) { prod.producedTrackLabels(p, m, pm); }); + fillMcLabelGeneric(track, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedTrackLabels(p); }); } - template - void fillMcLambdaWithLabel(T1 const& col, T2 const& mcCols, T3 const& lambda, T4 const& mcParticles, T5& mcProducts) + template + void fillMcLambdaWithLabel(T1 const& lambda, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { if (!mProduceLambdaLabels) { - mcProducts.producedLambdaLabels(-1, -1, -1); + mcProducts.producedLambdaLabels(-1); return; } - fillMcLabelGeneric(col, mcCols, lambda, mcParticles, mcProducts, [](auto& prod, int64_t p, int64_t m, int64_t pm) { prod.producedLambdaLabels(p, m, pm); }); + fillMcLabelGeneric(lambda, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedLambdaLabels(p); }); } - template - void fillMcK0shortWithLabel(T1 const& col, T2 const& mcCols, T3 const& k0short, T4 const& mcParticles, T5& mcProducts) + template + void fillMcK0shortWithLabel(T1 const& k0short, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { if (!mProduceK0shortLabels) { - mcProducts.producedK0shortLabels(-1, -1, -1); + mcProducts.producedK0shortLabels(-1); return; } - fillMcLabelGeneric(col, mcCols, k0short, mcParticles, mcProducts, [](auto& prod, int64_t p, int64_t m, int64_t pm) { prod.producedK0shortLabels(p, m, pm); }); + fillMcLabelGeneric(k0short, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedK0shortLabels(p); }); } + // D0 has no direct MC label (2-prong hypothesis built by PWGHF), so fillMcLabelGeneric + // cannot be reused. Both prongs are matched to a generated D0 -> K pi decay with + // RecoDecay::getMatchedMCRec, which returns the index of the generated mother; + // unmatched candidates get -1. template - void fillMcSigmaWithLabel(T1 const& col, T2 const& mcCols, T3 const& sigmaDaughter, T4 const& mcParticles, T5& mcProducts) + void fillMcD0WithLabel(T1 const& d0candidate, T2 const& /*tracks*/, T3 const& mcParticles, T4 const& /*mcCols*/, T5& mcProducts) { - if (!mProduceSigmaLabels) { + if (!mProduceD0Labels) { + mcProducts.producedD0Labels(-1); return; } - fillMcLabelGeneric(col, mcCols, sigmaDaughter, mcParticles, mcProducts, [](auto& prod, int64_t p, int64_t m, int64_t pm) { prod.producedSigmaLabels(p, m, pm); }, true); + + auto prong0 = d0candidate.template prong0_as(); + auto prong1 = d0candidate.template prong1_as(); + auto arrayDaughters = std::array{prong0, prong1}; + int8_t sign = 0; + const int indexMcRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughters, o2::constants::physics::Pdg::kD0, std::array{+kPiPlus, -kKPlus}, true, &sign); + + if (indexMcRec < 0) { + mcProducts.producedD0Labels(-1); + return; + } + + auto mcParticle = mcParticles.rawIteratorAt(indexMcRec); + auto mcCol = mcParticle.template mcCollision_as(); + int64_t mcParticleRow = this->getOrCreateMcParticleRow(mcParticle, mcParticles, mcCol, mcProducts); + + mcProducts.producedD0Labels(mcParticleRow); } + // Lc has no direct MC label. The three prongs are matched to a generated + // Lc -> p K pi decay;depthMax = 2 because the decay also proceeds through intermediate resonances. template - void fillMcSigmaPlusWithLabel(T1 const& col, T2 const& mcCols, T3 const& sigmaPlusDaughter, T4 const& mcParticles, T5& mcProducts) + void fillMcLcWithLabel(T1 const& lcCandidate, T2 const& /*tracks*/, T3 const& mcParticles, T4 const& /*mcCols*/, T5& mcProducts) + { + if (!mProduceLcLabels) { + mcProducts.producedLcLabels(-1); + return; + } + + auto prong0 = lcCandidate.template prong0_as(); + auto prong1 = lcCandidate.template prong1_as(); + auto prong2 = lcCandidate.template prong2_as(); + auto arrayDaughters = std::array{prong0, prong1, prong2}; + int8_t sign = 0; + const int indexMcRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughters, o2::constants::physics::Pdg::kLambdaCPlus, std::array{+kProton, -kKPlus, +kPiPlus}, true, &sign, 2); + + if (indexMcRec < 0) { + mcProducts.producedLcLabels(-1); + return; + } + + auto mcParticle = mcParticles.rawIteratorAt(indexMcRec); + auto mcCol = mcParticle.template mcCollision_as(); + int64_t mcParticleRow = this->getOrCreateMcParticleRow(mcParticle, mcParticles, mcCol, mcProducts); + + mcProducts.producedLcLabels(mcParticleRow); + } + + template + void fillMcSigmaWithLabel(T1 const& sigmaDaughter, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) + { + if (!mProduceSigmaLabels) { + mcProducts.producedSigmaLabels(-1); + return; + } + fillMcLabelGeneric(sigmaDaughter, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaLabels(p); }, true); + } + + template + void fillMcSigmaPlusWithLabel(T1 const& sigmaPlusDaughter, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { if (!mProduceSigmaPlusLabels) { + mcProducts.producedSigmaPlusLabels(-1); return; } - fillMcLabelGeneric(col, mcCols, sigmaPlusDaughter, mcParticles, mcProducts, [](auto& prod, int64_t p, int64_t m, int64_t pm) { prod.producedSigmaPlusLabels(p, m, pm); }, true); + fillMcLabelGeneric(sigmaPlusDaughter, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusLabels(p); }, true); } template - void fillMcXiWithLabel(T1 const& col, T2 const& mcCols, T3 const& xi, T4 const& mcParticles, T5& mcProducts) + void fillMcSigmaPlusPcmWithLabel(T1 const& sigmaPlusPcm, T2 const& chaDaughter, T3 const& mcParticles, T4 const& mcCols, T5& mcProducts) + { + if (!mProduceSigmaPlusPcmLabels) { + mcProducts.producedSigmaPlusPcmLabels(-1); + return; + } + // true sigma plus (proton and photon from it): label of the sigma plus, otherwise label of the proton, its mother gives the origin of the background + if (sigmaPlusPcm.has_mcParticle()) { + fillMcLabelGeneric(sigmaPlusPcm, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusPcmLabels(p); }); + } else { + fillMcLabelGeneric(chaDaughter, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedSigmaPlusPcmLabels(p); }); + } + } + + template + void fillMcXiWithLabel(T1 const& xi, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { if (!mProduceXiLabels) { - mcProducts.producedXiLabels(-1, -1, -1); + mcProducts.producedXiLabels(-1); return; } - fillMcLabelGeneric(col, mcCols, xi, mcParticles, mcProducts, [](auto& prod, int64_t p, int64_t m, int64_t pm) { prod.producedXiLabels(p, m, pm); }); + fillMcLabelGeneric(xi, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedXiLabels(p); }); } - template - void fillMcOmegaWithLabel(T1 const& col, T2 const& mcCols, T3 const& omega, T4 const& mcParticles, T5& mcProducts) + template + void fillMcOmegaWithLabel(T1 const& omega, T2 const& mcParticles, T3 const& mcCols, T4& mcProducts) { if (!mProduceOmegaLabels) { - mcProducts.producedOmegaLabels(-1, -1, -1); + mcProducts.producedOmegaLabels(-1); return; } - fillMcLabelGeneric(col, mcCols, omega, mcParticles, mcProducts, [](auto& prod, int64_t p, int64_t m, int64_t pm) { prod.producedOmegaLabels(p, m, pm); }); + fillMcLabelGeneric(omega, mcParticles, mcCols, mcProducts, [](auto& prod, int64_t p) { prod.producedOmegaLabels(p); }); } bool fillAnyTable() const { return mFillAnyTable; } + bool isPassThrough() const { return mPassThrough; } template void reset(T1 const& mcCollisions, T2 const& mcParticles) @@ -227,36 +481,40 @@ class McBuilder } private: - template - void fillMcCollision(T1 const& mcCol, T2& mcProducts) + // classify a charm hadron as prompt (charm from a c quark) or non-prompt (charm from a + // beauty decay) from the mc decay tree; shared by the reco-matched and generator-level paths + template + modes::McOrigin getHeavyFlavourOrigin(T1 const& mcParticle, T2 const& mcParticles) { - mcProducts.producedMcCollisions( - mcCol.multMCNParticlesEta08(), - mcCol.centFT0M()); - mCollisionMap.emplace(mcCol.globalIndex(), mcProducts.producedMcCollisions.lastIndex()); + const int charmOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, mcParticle); + return (charmOrigin == RecoDecay::OriginType::NonPrompt) ? modes::McOrigin::kNonPrompt : modes::McOrigin::kPrompt; } - template - modes::McOrigin getOrigin(T1 const& col, T2 const& /*mcCols*/, T3 const& mcParticle) + template + modes::McOrigin getOrigin(T1 const& mcParticle) { - // constants - const int producedByDecay = 4; - // check if reconstructed collision has a generated collision - if (col.has_mcCollision()) { - // now check collision ids, if they do not match, then the track belongs to another collision - if (col.mcCollisionId() != mcParticle.mcCollisionId()) { - return modes::McOrigin::kFromWrongCollision; - } - if (mcParticle.isPhysicalPrimary()) { - return modes::McOrigin::kPhysicalPrimary; - } else if (mcParticle.has_mothers() && mcParticle.getProcess() == producedByDecay) { - return modes::McOrigin::kFromSecondaryDecay; - } else { - // not a primary and not from a decay, we label as material - return modes::McOrigin::kFromMaterial; - } + // whether a particle is misidentified or not can only be checked by qa/pair task later so it is not set here + // whether a particle is associated to the wrong collision or not can only be checked by qa/pair task later so it is not set here + if (mcParticle.isPhysicalPrimary()) { + return modes::McOrigin::kPhysicalPrimary; + } + + // A non-primary the generator itself produced can only come from a decay the generator + // performed, and strong/EM decay products are primaries by definition - so this is a + // weak decay. NOTE: getProcess() returns kPrimary (0) for every generator particle, + // so the kPDecay check below never fires for them. + if (mcParticle.producedByGenerator()) { + return modes::McOrigin::kFromSecondaryDecay; } - return modes::McOrigin::kFromWrongCollision; + // produced by transport: kPDecay means GEANT decayed it, the usual path for + // Lambda / K0s / Xi / Omega since ALICE hands them to transport undecayed + const int process = mcParticle.getProcess(); + if (process == TMCProcess::kPDecay || process == TMCProcess::kPRadDecay) { + return modes::McOrigin::kFromSecondaryDecay; + } + + // not a primary and not from a decay we label as material + return modes::McOrigin::kFromMaterial; } template @@ -273,80 +531,75 @@ class McBuilder return it->second; } - template - void fillMcLabelGeneric(T1 const& col, - T2 const& mcCols, - T3 const& particle, - T4 const& mcParticles, - T5& mcProducts, - T6 writeLabels, - bool startFromMotherParticle = false) + template + int64_t getOrCreateMcParticleRow(T1 const& mcParticle, T2 const& mcParticles, T3 const& mcCol, T4& mcProducts) { - if (!particle.has_mcParticle()) { - writeLabels(mcProducts, -1, -1, -1); - return; - } - - auto mcParticle = particle.template mcParticle_as(); - auto mcCol = mcParticle.template mcCollision_as(); - - if (startFromMotherParticle) { - // in case of e.g. sigmas we do not reconstruct the mother but the daughter, so here we want to start from the mother particle - auto mcDaughterParticle = particle.template mcParticle_as(); - if (!mcDaughterParticle.has_mothers()) { - writeLabels(mcProducts, -1, -1, -1); - return; - } - auto mothersOfDaughter = mcDaughterParticle.template mothers_as(); - mcParticle = mothersOfDaughter.front(); - } + const int pdgAbs = std::abs(mcParticle.pdgCode()); + const bool isCharmHadron = (pdgAbs == o2::constants::physics::Pdg::kD0 || + pdgAbs == o2::constants::physics::Pdg::kLambdaCPlus); + auto origin = isCharmHadron + ? this->getHeavyFlavourOrigin(mcParticle, mcParticles) + : this->getOrigin(mcParticle); + return this->buildMcParticleRow(mcParticle, mcParticles, mcCol, origin, mcProducts); + } + /// Find-or-create the FMcParticles row for mcParticle. On first creation, also + /// resolves the mother / partonic mother (with kinematics) and writes the single + /// corresponding FMcMotherLabels row, so it always stays in lockstep, one row + /// per FMcParticles row. + template + int64_t buildMcParticleRow(T1 const& mcParticle, T2 const& mcParticles, T3 const& mcCol, modes::McOrigin origin, T4& mcProducts) + { int64_t mcParticleIndex = mcParticle.globalIndex(); - int64_t mcParticleRow = -1; - // MC particle auto itP = mMcParticleMap.find(mcParticleIndex); if (itP != mMcParticleMap.end()) { - mcParticleRow = itP->second; - } else { - auto origin = this->getOrigin(col, mcCols, mcParticle); - int64_t mcColId = this->getMcColId(mcCol, mcProducts); + return itP->second; + } - mcProducts.producedMcParticles( - mcColId, - static_cast(origin), - mcParticle.pdgCode(), - mcParticle.pt() * utils::signum(mcParticle.pdgCode()), - mcParticle.eta(), - mcParticle.phi()); + int64_t mcColId = this->getMcColId(mcCol, mcProducts); - mcParticleRow = mcProducts.producedMcParticles.lastIndex(); - mMcParticleMap[mcParticleIndex] = mcParticleRow; - } + mcProducts.producedMcParticles( + mcColId, + static_cast(origin), + mcParticle.pdgCode(), + mcParticle.pt() * utils::signum(mcParticle.pdgCode()), + mcParticle.eta(), + mcParticle.phi()); - // MC mother + int64_t mcParticleRow = mcProducts.producedMcParticles.lastIndex(); + mMcParticleMap[mcParticleIndex] = mcParticleRow; + + // --- mother --- int64_t mcMotherRow = -1; - if (mcParticle.has_mothers()) { - auto mothers = mcParticle.template mothers_as(); - auto mcMotherIndex = mothers.front().globalIndex(); + if (mProduceMcMothers && mcParticle.has_mothers()) { + auto mothers = mcParticle.template mothers_as(); + auto const& motherParticle = mothers.front(); + auto mcMotherIndex = motherParticle.globalIndex(); auto itM = mMcMotherMap.find(mcMotherIndex); if (itM != mMcMotherMap.end()) { mcMotherRow = itM->second; } else { - mcProducts.producedMothers(mothers.front().pdgCode()); + auto motherOrigin = this->getOrigin(motherParticle); + mcProducts.producedMothers( + static_cast(motherOrigin), + motherParticle.pdgCode(), + motherParticle.pt() * utils::signum(motherParticle.pdgCode()), + motherParticle.eta(), + motherParticle.phi()); mcMotherRow = mcProducts.producedMothers.lastIndex(); mMcMotherMap[mcMotherIndex] = mcMotherRow; } } - // Partonic mother + // --- partonic mother --- int64_t mcPartonicMotherRow = -1; int64_t mcPartonicMotherIndex = -1; - if (mFindLastPartonicMother) { - mcPartonicMotherIndex = this->findLastPartonicMother(mcParticle, mcParticles); - } else { - mcPartonicMotherIndex = this->findFirstPartonicMother(mcParticle, mcParticles); + if (mProduceMcPartonicMothers) { + mcPartonicMotherIndex = mFindLastPartonicMother + ? this->findLastPartonicMother(mcParticle, mcParticles) + : this->findFirstPartonicMother(mcParticle, mcParticles); } if (mcPartonicMotherIndex >= 0) { auto itPM = mMcPartonicMotherMap.find(mcPartonicMotherIndex); @@ -360,35 +613,75 @@ class McBuilder } } - writeLabels(mcProducts, mcParticleRow, mcMotherRow, mcPartonicMotherRow); + // exactly one FMcMotherLabels row per FMcParticles row, written here and only here + if (mProduceMcMotherLabels) { + mcProducts.producedMcMotherLabels(mcMotherRow, mcPartonicMotherRow); + } + + return mcParticleRow; + } + + template + void fillMcLabelGeneric(T1 const& particle, + T2 const& mcParticles, + T3 const& /*mcCols*/, + T4& mcProducts, + T5 writeLabels, + bool startFromMotherParticle = false) + { + if (!particle.has_mcParticle()) { + writeLabels(mcProducts, -1); + return; + } + + auto mcParticle = particle.template mcParticle_as(); + auto mcCol = mcParticle.template mcCollision_as(); + + if (startFromMotherParticle) { + // in case of e.g. sigmas we do not reconstruct the mother but the daughter, so here we want to start from the mother particle + auto mcDaughterParticle = particle.template mcParticle_as(); + if (!mcDaughterParticle.has_mothers()) { + writeLabels(mcProducts, -1); + return; + } + auto mothersOfDaughter = mcDaughterParticle.template mothers_as(); + mcParticle = mothersOfDaughter.front(); + } + + int64_t mcParticleRow = this->getOrCreateMcParticleRow(mcParticle, mcParticles, mcCol, mcProducts); + + writeLabels(mcProducts, mcParticleRow); } template - int64_t findFirstPartonicMother(const T1& mcParticle, const T2& mcParticles) + int64_t findFirstPartonicMother(const T1& mcParticle, const T2& mcParticles, int depth = 0) { - if (!mcParticle.has_mothers()) { + // guards against cyclic/malformed mother chains in some MC productions, which would + // otherwise recurse unboundedly and crash with a stack overflow; no physically sensible + // generator history is anywhere close to this deep + constexpr int MaxPartonicMotherDepth = 100; + if (depth >= MaxPartonicMotherDepth) { + // some productions hit this on a large fraction of particles (e.g. a mother-range + // heuristic below misfiring on this production's indexing convention), so warn once per + // device lifetime rather than once per particle + if (!mWarnedPartonicMotherDepth) { + LOG(warn) << "findFirstPartonicMother exceeded max recursion depth (" << MaxPartonicMotherDepth << ") at least once; mother chain may be cyclic or malformed for this MC production. Consider using findLastPartonicMother instead. Further occurrences will be suppressed."; + mWarnedPartonicMotherDepth = true; + } return -1; } - auto motherIds = mcParticle.mothersIds(); - // adapted these checks from MCUtils in PWGEM - const int defaultMotherSize = 2; - std::vector allMotherIds; - if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { - // keep for now, might be needed later - // && ((80 < std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) && std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) < 90) || (100 < std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) && std::abs(o2::mcgenstatus::getGenStatusCode(p.statusCode())) < 110))) { - // Pythia: mother range - for (int i = motherIds[0]; i <= motherIds[1]; i++) - allMotherIds.push_back(i); - } else { - // Otherwise just use them as given - for (const int& id : motherIds) - allMotherIds.push_back(id); + if (!mcParticle.has_mothers()) { + return -1; } + // RecoDecay::getOrderedMotherIds() implements the Pythia mother-id-range expansion (gated on the particle's gen status code) + std::vector allMotherIds; + RecoDecay::getOrderedMotherIds(mcParticle, allMotherIds, /*searchUpToQuark=*/true); // Loop over all mothers - for (const int& i : allMotherIds) { + for (const int64_t& i : allMotherIds) { - if (i < 0 || i >= mcParticles.size()) + if (i < 0 || i >= mcParticles.size()) { continue; + } const auto& mother = mcParticles.iteratorAt(i); int pdgAbs = std::abs(mother.pdgCode()); // Is it a parton? (quark or gluon) @@ -396,9 +689,10 @@ class McBuilder return i; // Found a parton → return index } // Recurse upward - int64_t found = this->findFirstPartonicMother(mother, mcParticles); - if (found != -1) + int64_t found = this->findFirstPartonicMother(mother, mcParticles, depth + 1); + if (found != -1) { return found; + } } // No partonic ancestor found return -1; @@ -411,23 +705,19 @@ class McBuilder int64_t currentIndex = mcParticle.globalIndex(); while (currentIndex >= 0 && currentIndex < mcParticles.size()) { const auto& current = mcParticles.iteratorAt(currentIndex); - if (!current.has_mothers()) + if (!current.has_mothers()) { break; - auto motherIds = current.mothersIds(); - int nextIndex = -1; - const int defaultMotherSize = 2; - if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { - nextIndex = motherIds[0]; - } else { - for (const int& id : motherIds) { - if (id >= 0 && id < mcParticles.size()) { - nextIndex = id; - break; - } - } } - if (nextIndex < 0 || nextIndex >= mcParticles.size()) + // RecoDecay::getOrderedMotherIds() implements the Pythia mother-id-range expansion (gated on + // the particle's gen status code). This function only ever follows a single branch upward, + // so just the first ordered id is used. + std::vector motherIdsOrdered; + RecoDecay::getOrderedMotherIds(current, motherIdsOrdered, /*searchUpToQuark=*/true); + int64_t nextIndex = motherIdsOrdered.empty() ? -1 : motherIdsOrdered.front(); + + if (nextIndex < 0 || nextIndex >= mcParticles.size()) { break; + } const auto& mother = mcParticles.iteratorAt(nextIndex); int pdgAbs = std::abs(mother.pdgCode()); int status = std::abs(o2::mcgenstatus::getGenStatusCode(mother.statusCode())); @@ -435,18 +725,20 @@ class McBuilder const int isBeamParticleLowerLimit = 11; const int isBeamParticleUpperLimit = 19; bool isBeam = (status >= isBeamParticleLowerLimit && status <= isBeamParticleUpperLimit); - if (isBeam) + if (isBeam) { return lastPartonIndex; - if (isParton) + } + if (isParton) { lastPartonIndex = nextIndex; - + } currentIndex = nextIndex; } - return -1; + return lastPartonIndex; } bool mPassThrough = false; bool mFindLastPartonicMother = false; + bool mWarnedPartonicMotherDepth = false; bool mFillAnyTable = false; bool mProduceMcCollisions = false; bool mProduceMcParticles = false; @@ -457,10 +749,18 @@ class McBuilder bool mProduceTrackLabels = false; bool mProduceLambdaLabels = false; bool mProduceK0shortLabels = false; + bool mProduceD0Labels = false; + bool mProduceLcLabels = false; bool mProduceSigmaLabels = false; bool mProduceSigmaPlusLabels = false; + bool mProduceSigmaPlusPcmLabels = false; bool mProduceXiLabels = false; bool mProduceOmegaLabels = false; + bool mProduceMcMotherLabels = false; + float mCharmYGenMax = 0.8f; + + float mEtaAcceptanceMcOnly = 0.8f; + float mEtaAcceptanceMcReco = 1.f; std::unordered_map mCollisionMap; @@ -469,8 +769,220 @@ class McBuilder std::unordered_map mMcPartonicMotherMap; }; -} // namespace mcbuilder -// -} // namespace o2::analysis::femto +struct McBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedMcCollisions; + o2::framework::Produces producedMcParticles; + o2::framework::Produces producedMothers; + o2::framework::Produces producedPartonicMothers; + o2::framework::Produces producedMcMotherLabels; + + o2::framework::Produces producedCollisionLabels; + o2::framework::Produces producedTrackLabels; + o2::framework::Produces producedSigmaLabels; + o2::framework::Produces producedSigmaPlusLabels; + o2::framework::Produces producedSigmaPlusPcmLabels; +}; + +struct ConfMcTablesDerivedToDerived : o2::framework::ConfigurableGroup { + std::string prefix = std::string("McTables"); + o2::framework::Configurable requireMcLabel{"requireMcLabel", false, "Reject particles without an associated MC particle instead of writing a -1 label"}; // dummy configuration for now +}; + +/// Copies MC information from one femto derived file into another. +/// The ancestry is already resolved upstream (FMcMothers / FMcPartMoths / +/// FMcMotherLabels), so this only remaps indices: each source row is copied on +/// first use and cached for the rest of the dataframe. +class McBuilderDerivedToDerived +{ + public: + McBuilderDerivedToDerived() = default; + ~McBuilderDerivedToDerived() = default; + + template + void init(T& config) + { + LOG(info) << "Initialize derived-to-derived monte carlo builder..."; + mRequireMcLabel = config.requireMcLabel.value; + LOG(info) << "Initialization done..."; + } + + /// Write one FColLabels row. Call exactly once per produced collision, + /// directly after collisionBuilder.processCollision(). + template + void fillCollisionWithLabel(T1 const& col, T2 const& /*mcCols*/, T3& mcProducts) + { + if (!col.has_fMcCol()) { + mcProducts.producedCollisionLabels(-1); + return; + } + auto mcCol = col.template fMcCol_as(); + mcProducts.producedCollisionLabels(this->getOrCreateMcCollisionRow(mcCol, mcProducts)); + } + + /// True if this track has no MC particle and we are configured to drop such tracks. + template + bool rejectParticle(T const& particle) const + { + return mRequireMcLabel && !particle.has_fMcParticle(); + } + + /// Write one FTrackLabels row. Call exactly once per produced track row. + /// mcParticles must be joined with FMcMotherLabels so the mother indices resolve. + template + void fillTrackWithLabel(T1 const& track, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!track.has_fMcParticle()) { + mcProducts.producedTrackLabels(-1); + return; + } + auto mcParticle = track.template fMcParticle_as(); + mcProducts.producedTrackLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + + template + void fillSigmaWithLabel(T1 const& sigma, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigma.has_fMcParticle()) { + mcProducts.producedSigmaLabels(-1); + return; + } + auto mcParticle = sigma.template fMcParticle_as(); + mcProducts.producedSigmaLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + + template + void fillSigmaPlusWithLabel(T1 const& sigmaPlus, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigmaPlus.has_fMcParticle()) { + mcProducts.producedSigmaPlusLabels(-1); + return; + } + auto mcParticle = sigmaPlus.template fMcParticle_as(); + mcProducts.producedSigmaPlusLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + + template + void fillSigmaPlusPcmWithLabel(T1 const& sigmaPlusPcm, T2 const& mcCols, T3 const& /*mcParticles*/, T4 const& mcMothers, T5 const& mcPartonicMothers, T6& mcProducts) + { + if (!sigmaPlusPcm.has_fMcParticle()) { + mcProducts.producedSigmaPlusPcmLabels(-1); + return; + } + auto mcParticle = sigmaPlusPcm.template fMcParticle_as(); + mcProducts.producedSigmaPlusPcmLabels( + this->getOrCreateMcParticleRow(mcParticle, mcCols, mcMothers, mcPartonicMothers, mcProducts)); + } + + template + void reset(T1 const& mcCols, T2 const& mcParticles) + { + mCollisionMap.clear(); + mCollisionMap.reserve(mcCols.size()); + mMcParticleMap.clear(); + mMcParticleMap.reserve(mcParticles.size()); + mMcMotherMap.clear(); + mMcMotherMap.reserve(mcParticles.size()); + mMcPartonicMotherMap.clear(); + mMcPartonicMotherMap.reserve(mcParticles.size()); + } + + private: + template + int64_t getOrCreateMcCollisionRow(T1 const& mcCol, T2& mcProducts) + { + const int64_t gid = mcCol.globalIndex(); + auto it = mCollisionMap.find(gid); + if (it != mCollisionMap.end()) { + return it->second; + } + mcProducts.producedMcCollisions(mcCol.posZ(), + mcCol.mult(), + mcCol.cent()); + const int64_t row = mcProducts.producedMcCollisions.lastIndex(); + mCollisionMap.emplace(gid, row); + return row; + } + + /// Find-or-create the FMcParticles row. On first creation the mother and + /// partonic mother rows are copied and the single matching FMcMotherLabels row + /// is written, so the two tables stay in lockstep exactly as in McBuilder. + template + int64_t getOrCreateMcParticleRow(T1 const& mcParticle, T2 const& /*mcCols*/, T3 const& /*mcMothers*/, T4 const& /*mcPartonicMothers*/, T5& mcProducts) + { + const int64_t gid = mcParticle.globalIndex(); + auto it = mMcParticleMap.find(gid); + if (it != mMcParticleMap.end()) { + return it->second; + } + + // NOTE: the MC collision of the particle, not of the reconstructed collision. + // These differ for wrongly associated particles, which is the point of keeping it. + int64_t mcColId = -1; + if (mcParticle.has_fMcCol()) { + auto mcCol = mcParticle.template fMcCol_as(); + mcColId = this->getOrCreateMcCollisionRow(mcCol, mcProducts); + } + + mcProducts.producedMcParticles(mcColId, + mcParticle.origin(), + mcParticle.pdgCode(), + mcParticle.signedPt(), + mcParticle.eta(), + mcParticle.phi()); + const int64_t row = mcProducts.producedMcParticles.lastIndex(); + mMcParticleMap.emplace(gid, row); + + // --- mother --- + int64_t mcMotherRow = -1; + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); + const int64_t motherGid = mother.globalIndex(); + auto itM = mMcMotherMap.find(motherGid); + if (itM != mMcMotherMap.end()) { + mcMotherRow = itM->second; + } else { + mcProducts.producedMothers(mother.origin(), + mother.pdgCode(), + mother.signedPt(), + mother.eta(), + mother.phi()); + mcMotherRow = mcProducts.producedMothers.lastIndex(); + mMcMotherMap.emplace(motherGid, mcMotherRow); + } + } + + // --- partonic mother --- + int64_t mcPartonicMotherRow = -1; + if (mcParticle.has_fMcPartMoth()) { + auto partonicMother = mcParticle.template fMcPartMoth_as(); + const int64_t partonicGid = partonicMother.globalIndex(); + auto itPM = mMcPartonicMotherMap.find(partonicGid); + if (itPM != mMcPartonicMotherMap.end()) { + mcPartonicMotherRow = itPM->second; + } else { + mcProducts.producedPartonicMothers(partonicMother.pdgCode()); + mcPartonicMotherRow = mcProducts.producedPartonicMothers.lastIndex(); + mMcPartonicMotherMap.emplace(partonicGid, mcPartonicMotherRow); + } + } + + // exactly one FMcMotherLabels row per FMcParticles row, written here and only here + mcProducts.producedMcMotherLabels(mcMotherRow, mcPartonicMotherRow); + + return row; + } + + bool mRequireMcLabel = false; + + std::unordered_map mCollisionMap; + std::unordered_map mMcParticleMap; + std::unordered_map mMcMotherMap; + std::unordered_map mMcPartonicMotherMap; +}; + +} // namespace o2::analysis::femto::mcbuilder #endif // PWGCF_FEMTO_CORE_MCBUILDER_H_ diff --git a/PWGCF/Femto/Core/mcParticleHistManager.h b/PWGCF/Femto/Core/mcParticleHistManager.h new file mode 100644 index 00000000000..ce9c8bd4bed --- /dev/null +++ b/PWGCF/Femto/Core/mcParticleHistManager.h @@ -0,0 +1,318 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file mcParticleHistManager.h +/// \brief histogram manager for mc particle histograms +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#ifndef PWGCF_FEMTO_CORE_MCPARTICLEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_MCPARTICLEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::mcparticlehistmanager +{ +// enum for mc particle histograms +enum McParticleHist { + // kinemtics + kPt, + kEta, + kPhi, + kSign, + // pdg codes + kPdg, + kPdgMother, + kPdgPartonicMother, + // mother kinematics + kMotherPt, + kMotherEta, + kMotherPhi, + kMotherSign, + // origin (kept in sync with TrackHist, even though some categories + // are not expected to be populated for pure MC truth particles) + kOrigin, + kNoMcParticle, + kPrimary, + kFromWrongCollision, + kFromMaterial, + kMissidentified, + kSecondary1, + kSecondary2, + kSecondary3, + kSecondaryOther, + + kMcParticleHistLast +}; + +constexpr std::size_t MaxSecondary = 3; + +template +struct ConfMcParticleBinning : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; + o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "PDG codes of selected particles"}; + o2::framework::Configurable plotOrigins{"plotOrigins", true, "Plot pt distributions for different particle origins"}; + o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3122}, "PDG codes of mothers of secondaries (Max 3 will be considered)"}; +}; + +constexpr const char PrefixMcParticleBinning1[] = "McParticleBinning1"; +constexpr const char PrefixMcParticleBinning2[] = "McParticleBinning2"; +constexpr const char PrefixMcParticleBinning3[] = "McParticleBinning3"; + +using ConfMcParticleBinning1 = ConfMcParticleBinning; +using ConfMcParticleBinning2 = ConfMcParticleBinning; +using ConfMcParticleBinning3 = ConfMcParticleBinning; + +// the enum gives the correct index in the array +constexpr std::array, kMcParticleHistLast> + HistTable = { + { + {kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapidity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign of charge; Sign; Entries"}, + {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG code of the particle; PDG Code; Entries"}, + {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG code of the mother; PDG Code; Entries"}, + {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG code of the partonic mother; PDG Code; Entries"}, + {kMotherPt, o2::framework::HistType::kTH1F, "hMotherPt", "Mother transverse momentum; p_{T} (GeV/#it{c}); Entries"}, + {kMotherEta, o2::framework::HistType::kTH1F, "hMotherEta", "Mother pseudorapidity; #eta; Entries"}, + {kMotherPhi, o2::framework::HistType::kTH1F, "hMotherPhi", "Mother azimuthal angle; #varphi; Entries"}, + {kMotherSign, o2::framework::HistType::kTH1F, "hMotherSign", "Sign of mother charge; Sign; Entries"}, + {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, + {kNoMcParticle, o2::framework::HistType::kTH1F, "hNoMcParticle", "Particles with no associated MC information; p_{T} (GeV/#it{c}); Entries"}, + {kPrimary, o2::framework::HistType::kTH1F, "hPrimary", "Primary particles; p_{T} (GeV/#it{c}); Entries"}, + {kFromWrongCollision, o2::framework::HistType::kTH1F, "hFromWrongCollision", "Particles associated to wrong collision; p_{T} (GeV/#it{c}); Entries"}, + {kFromMaterial, o2::framework::HistType::kTH1F, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); Entries"}, + {kMissidentified, o2::framework::HistType::kTH1F, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); Entries"}, + {kSecondary1, o2::framework::HistType::kTH1F, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); Entries"}, + {kSecondary2, o2::framework::HistType::kTH1F, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); Entries"}, + {kSecondary3, o2::framework::HistType::kTH1F, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); Entries"}, + {kSecondaryOther, o2::framework::HistType::kTH1F, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); Entries"}, + }}; + +template +auto makeMcParticleHistSpecMap(const T& confBinning) +{ + return std::map>{ + {kPt, {confBinning.pt}}, + {kEta, {confBinning.eta}}, + {kPhi, {confBinning.phi}}, + {kSign, {confBinning.sign}}, + {kPdg, {confBinning.pdgCodes}}, + {kPdgMother, {confBinning.pdgCodes}}, + {kPdgPartonicMother, {confBinning.pdgCodes}}, + {kMotherPt, {confBinning.pt}}, + {kMotherEta, {confBinning.eta}}, + {kMotherPhi, {confBinning.phi}}, + {kMotherSign, {confBinning.sign}}, + {kNoMcParticle, {confBinning.pt}}, + {kPrimary, {confBinning.pt}}, + {kFromWrongCollision, {confBinning.pt}}, + {kFromMaterial, {confBinning.pt}}, + {kMissidentified, {confBinning.pt}}, + {kSecondary1, {confBinning.pt}}, + {kSecondary2, {confBinning.pt}}, + {kSecondary3, {confBinning.pt}}, + {kSecondaryOther, {confBinning.pt}}, + }; +} + +inline constexpr char PrefixMcParticle1[] = "McParticle1/"; +inline constexpr char PrefixMcParticle2[] = "McParticle2/"; +inline constexpr char PrefixMcParticle1Se[] = "McParticle1Se/"; +inline constexpr char PrefixMcParticle2Se[] = "McParticle2Se/"; +inline constexpr char PrefixMcParticle3Se[] = "McParticle3Se/"; +inline constexpr char PrefixMcParticle1Me[] = "McParticle1Me/"; +inline constexpr char PrefixMcParticle2Me[] = "McParticle2Me/"; +inline constexpr char PrefixMcParticle3Me[] = "McParticle3Me/"; + +constexpr std::string_view McDir = "MC/"; + +template +class McParticleHistManager +{ + public: + McParticleHistManager() = default; + ~McParticleHistManager() = default; + + // init with origin histograms enabled, controlled via confBinning.plotOrigins + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& Specs, + T const& confBinning) + { + mHistogramRegistry = registry; + + std::string mcDir = std::string(prefix) + std::string(McDir); + mHistogramRegistry->add(mcDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {Specs.at(kPt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {Specs.at(kEta)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {Specs.at(kPhi)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kSign, HistTable), getHistDesc(kSign, HistTable), getHistType(kSign, HistTable), {Specs.at(kSign)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {Specs.at(kPdg)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {Specs.at(kPdgMother)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {Specs.at(kPdgPartonicMother)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kMotherPt, HistTable), getHistDesc(kMotherPt, HistTable), getHistType(kMotherPt, HistTable), {Specs.at(kMotherPt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kMotherEta, HistTable), getHistDesc(kMotherEta, HistTable), getHistType(kMotherEta, HistTable), {Specs.at(kMotherEta)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kMotherPhi, HistTable), getHistDesc(kMotherPhi, HistTable), getHistType(kMotherPhi, HistTable), {Specs.at(kMotherPhi)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kMotherSign, HistTable), getHistDesc(kMotherSign, HistTable), getHistType(kMotherSign, HistTable), {Specs.at(kMotherSign)}); + + this->enableOptionalHistograms(confBinning); + this->initOrigin(Specs); + } + + template + void fill(T1 const& mcParticle, T2 const& /*mcMothers*/, T3 const& /*mcPartonicMothers*/) + { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPt, HistTable)), mcParticle.pt()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kEta, HistTable)), mcParticle.eta()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPhi, HistTable)), mcParticle.phi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSign, HistTable)), mcParticle.sign()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), mcParticle.pdgCode()); + + bool hasMother = mcParticle.has_fMcMother(); + if (hasMother) { + auto mother = mcParticle.template fMcMother_as(); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), mother.pdgCode()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMotherPt, HistTable)), mother.pt()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMotherEta, HistTable)), mother.eta()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMotherPhi, HistTable)), mother.phi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMotherSign, HistTable)), mother.sign()); + } else { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), 0); + } + + if (mcParticle.has_fMcPartMoth()) { + auto partonicMother = mcParticle.template fMcPartMoth_as(); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), partonicMother.pdgCode()); + } else { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); + } + + if (mPlotOrigins) { + // NOTE: kNoMcParticle and kMissidentified are kept here only for enum/histogram + // symmetry with TrackHist — they describe reco-vs-truth mismatches that cannot + // occur for a pure MC-truth particle, so these bins are expected to stay empty. + // kFromWrongCollision is not handled here at all: mcBuilder.h's getOrigin() never + // assigns it (only kPhysicalPrimary/kFromSecondaryDecay/kFromMaterial), so it would + // be dead code the same way it was in the per-candidate hist managers. + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); + switch (static_cast(mcParticle.origin())) { + case modes::McOrigin::kNoMcParticle: + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), mcParticle.pt()); + break; + case modes::McOrigin::kPhysicalPrimary: + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), mcParticle.pt()); + break; + case modes::McOrigin::kFromMaterial: + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), mcParticle.pt()); + break; + case modes::McOrigin::kMissidentified: + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), mcParticle.pt()); + break; + case modes::McOrigin::kFromSecondaryDecay: + if (hasMother) { + auto mother = mcParticle.template fMcMother_as(); + int motherPdgCode = std::abs(mother.pdgCode()); + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), mcParticle.pt()); + } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), mcParticle.pt()); + } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), mcParticle.pt()); + } else { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), mcParticle.pt()); + } + } + break; + default: + LOG(warn) << "Encountered mc particle with unknown origin!"; + break; + } + } + } + + private: + template + void enableOptionalHistograms(T const& confBinning) + { + mPlotOrigins = confBinning.plotOrigins.value; + mPlotNSecondaries = confBinning.pdgCodesForMothersOfSecondary.value.size(); + + for (std::size_t i = 0; i < MaxSecondary; i++) { + if (i < confBinning.pdgCodesForMothersOfSecondary.value.size()) { + mPdgCodesSecondaryMother.at(i) = std::abs(confBinning.pdgCodesForMothersOfSecondary.value.at(i)); + } else { + mPdgCodesSecondaryMother.at(i) = 0; + } + } + } + + void initOrigin(std::map> const& Specs) + { + if (!mPlotOrigins) { + return; + } + std::string mcDir = std::string(prefix) + std::string(McDir); + + // mc origin axis can be configured here + const o2::framework::AxisSpec axisOrigin = {static_cast(modes::McOrigin::kMcOriginLast), -0.5, static_cast(modes::McOrigin::kMcOriginLast) - 0.5}; + mHistogramRegistry->add(mcDir + getHistNameV2(kOrigin, HistTable), getHistDesc(kOrigin, HistTable), getHistType(kOrigin, HistTable), {axisOrigin}); + mHistogramRegistry->get(HIST(prefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kNoMcParticle), modes::mcOriginToString(modes::McOrigin::kNoMcParticle)); + mHistogramRegistry->get(HIST(prefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kFromWrongCollision), modes::mcOriginToString(modes::McOrigin::kFromWrongCollision)); + mHistogramRegistry->get(HIST(prefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kPhysicalPrimary), modes::mcOriginToString(modes::McOrigin::kPhysicalPrimary)); + mHistogramRegistry->get(HIST(prefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kFromSecondaryDecay), modes::mcOriginToString(modes::McOrigin::kFromSecondaryDecay)); + mHistogramRegistry->get(HIST(prefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kFromMaterial), modes::mcOriginToString(modes::McOrigin::kFromMaterial)); + mHistogramRegistry->get(HIST(prefix) + HIST(McDir) + HIST(histmanager::getHistName(kOrigin, HistTable)))->GetXaxis()->SetBinLabel(1 + static_cast(modes::McOrigin::kMissidentified), modes::mcOriginToString(modes::McOrigin::kMissidentified)); + + mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {Specs.at(kNoMcParticle)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {Specs.at(kPrimary)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {Specs.at(kFromWrongCollision)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {Specs.at(kFromMaterial)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {Specs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {Specs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {Specs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {Specs.at(kSecondary3)}); + } + mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {Specs.at(kSecondaryOther)}); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + bool mPlotOrigins = true; + int mPlotNSecondaries = 0; + std::array mPdgCodesSecondaryMother = {0}; +}; +} // namespace o2::analysis::femto::mcparticlehistmanager +#endif // PWGCF_FEMTO_CORE_MCPARTICLEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/modes.h b/PWGCF/Femto/Core/modes.h index c694e1916a6..1920096a835 100644 --- a/PWGCF/Femto/Core/modes.h +++ b/PWGCF/Femto/Core/modes.h @@ -18,15 +18,18 @@ #include "PWGCF/Femto/Core/dataTypes.h" -#include - #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::modes { -namespace modes + +template +constexpr std::underlying_type_t bit(uint64_t n) { + using U = std::underlying_type_t; + return static_cast(U{1} << n); +} // check if flag is set template @@ -45,61 +48,91 @@ constexpr bool isEqual(T lhs, T rhs) } enum class Mode : uint32_t { - kAnalysis = BIT(0), - kQa = BIT(1), - kMc = BIT(2), - kSe = BIT(3), - kMe = BIT(4), - kAnalysis_Qa = kAnalysis | kQa, - kAnalysis_Mc = kAnalysis | kMc, - kAnalysis_Qa_Mc = kAnalysis | kQa | kMc, - kSe_Analysis = kAnalysis | kSe, - kMe_Analysis = kAnalysis | kMe, - kSe_Analysis_Mc = kAnalysis | kSe | kMc, - kMe_Analysis_Mc = kAnalysis | kMe | kMc, + kReco = bit(0), + kQa = bit(1), + kMc = bit(2), + kSe = bit(3), + kMe = bit(4), + kFitVars = bit(5), // purity/fraction histograms (tracks: packed nSigma tables; V0s: cpa) -- gates booking at init time + kReco_Qa = kReco | kQa, + kReco_Mc = kReco | kMc, + kReco_Qa_Mc = kReco | kQa | kMc, + kReco_Qa_FitVars = kReco | kQa | kFitVars, + kReco_Qa_Mc_FitVars = kReco | kQa | kMc | kFitVars, + kSe_Reco = kSe | kReco, + kMe_Reco = kMe | kReco, + kSe_Reco_Mc = kSe | kReco | kMc, + kMe_Reco_Mc = kMe | kReco | kMc, + kSe_Mc = kSe | kMc, + kMe_Mc = kMe | kMc, + kSe_Reco_FitVars = kSe | kReco | kFitVars, + kMe_Reco_FitVars = kMe | kReco | kFitVars, + kSe_Reco_Mc_FitVars = kSe | kReco | kMc | kFitVars, + kMe_Reco_Mc_FitVars = kMe | kReco | kMc | kFitVars, }; enum class System : uint32_t { - kPP = BIT(0), - kPbPb = BIT(1), - kMC = BIT(2), - kRun3 = BIT(3), - kRun2 = BIT(4), + kPP = bit(0), + kPbPb = bit(1), + kMC = bit(2), + kRun3 = bit(3), + kRun2 = bit(4), kPP_Run3 = kPP | kRun3, kPP_Run3_MC = kPP | kRun3 | kMC, kPP_Run2 = kPP | kRun2, kPbPb_Run3 = kPbPb | kRun3, + kPbPb_Run3_MC = kPbPb | kRun3 | kMC, kPbPb_Run2 = kPbPb | kRun2, }; -enum class MomentumType : o2::aod::femtodatatypes::MomentumType { +enum class MomentumType : o2::analysis::femto::datatypes::MomentumType { kPt = 0, // transverse momentum kPAtPv = 1, // momentum at primary vertex kPTpc = 2, // momentum at inner wall of tpc }; -enum class TransverseMassType : o2::aod::femtodatatypes::TransverseMassType { +/// axis title for the momentum used on the x axis of PID histograms +constexpr const char* momentumTypeToAxisTitle(MomentumType type) +{ + switch (type) { + case MomentumType::kPt: + return "p_{T} (GeV/#it{c})"; + case MomentumType::kPAtPv: + return "p (GeV/#it{c})"; + case MomentumType::kPTpc: + return "p_{TPC} (GeV/#it{c})"; + default: + return "p (GeV/#it{c})"; + } +} + +enum class TransverseMassType : o2::analysis::femto::datatypes::TransverseMassType { kAveragePdgMass = 0, kReducedPdgMass = 1, kMt4Vector = 2 }; -enum class Particle : o2::aod::femtodatatypes::ParticleType { - kTrack = 0, - kTwoTrackResonance = 1, - kV0 = 2, - kKink = 3, - kCascade = 4, +enum class Particle : o2::analysis::femto::datatypes::ParticleType { + mcParticle = 0, + kTrack = 1, + kTwoTrackResonance = 2, + kV0 = 3, + kKink = 4, + kCascade = 5, + kCharmHadron = 6, + kTwoPhotonResonance = 7, }; -enum class McOrigin : o2::aod::femtodatatypes::McOriginType { - kNoMcParticle = 0, // no associated mc particle normally indicated a wrongly reconstruced partilce - kFromWrongCollision = 1, // partilce originates from the wrong collision or a collision which was wrongly reconstructed (like a split vertex) +enum class McOrigin : o2::analysis::femto::datatypes::McOriginType { + kNoMcParticle = 0, // no associated mc particle, normally indicated by wrongly reconstructed particle + kFromWrongCollision = 1, // particle originates from the wrong collision or a collision which was wrongly reconstructed (like a split vertex) kPhysicalPrimary = 2, // primary particle kFromSecondaryDecay = 3, // particle from secondary decay - kFromMaterial = 4, // partilce orginates from material - kMissidentified = 5, // partilce was kMissidentified (also know as fake) - kMcOriginLast = 6 + kFromMaterial = 4, // particle originates from material + kMissidentified = 5, // particle was kMissidentified (also know as fake) + kPrompt = 6, // HF only: charm hadron produced promptly (from c quark) + kNonPrompt = 7, // HF only: charm hadron from beauty decay + kMcOriginLast = 8 // kFromFakeRecoCollision, // kFromUnkown }; @@ -119,43 +152,152 @@ constexpr const char* mcOriginToString(McOrigin origin) return "FromMaterial"; case McOrigin::kMissidentified: return "Missidentified"; + case McOrigin::kPrompt: + return "Prompt"; + case McOrigin::kNonPrompt: + return "NonPrompt"; default: return "UnknownMcOrigin"; } } -enum class Track : o2::aod::femtodatatypes::TrackType { +enum class Track : o2::analysis::femto::datatypes::TrackType { kTrack, kV0Daughter, kCascadeBachelor, kResonanceDaughter, - kKinkDaughter + kKinkDaughter, + kCharmDaughter, + kTrackTypeLast }; -enum class V0 : o2::aod::femtodatatypes::V0Type { +constexpr const char* trackTypeToString(Track type) +{ + switch (type) { + case Track::kTrack: + return "Track"; + case Track::kV0Daughter: + return "V0Daughter"; + case Track::kCascadeBachelor: + return "CascadeBachelor"; + case Track::kResonanceDaughter: + return "ResonanceDaughter"; + case Track::kKinkDaughter: + return "KinkDaughter"; + case Track::kCharmDaughter: + return "CharmDaughter"; + default: + return "UnknownTrackType"; + } +} + +enum class Trigger : uint8_t { + kMinimumBias, + kTrackTrack, + kTrackV0, + kTrackTrackTrack, + kTrackTrackV0, + kTriggerLast +}; + +constexpr const char* triggerToString(Trigger trigger) +{ + switch (trigger) { + case Trigger::kMinimumBias: + return "MinimumBias"; + case Trigger::kTrackTrack: + return "TrackTrack"; + case Trigger::kTrackV0: + return "TrackV0"; + case Trigger::kTrackTrackTrack: + return "TrackTrackTrack"; + case Trigger::kTrackTrackV0: + return "TrackTrackV0"; + default: + return "UnknownTrigger"; + } +} + +enum class V0 : o2::analysis::femto::datatypes::V0Type { kLambda, kAntiLambda, kK0short }; -enum class Kink : o2::aod::femtodatatypes::KinkType { +enum class Kink : o2::analysis::femto::datatypes::KinkType { kSigma, - kSigmaPlus + kSigmaPlus, + kSigmaPlusPcm }; -enum class Cascade : o2::aod::femtodatatypes::CascadeType { +enum class Cascade : o2::analysis::femto::datatypes::CascadeType { kXi, kOmega }; // enum of supported resonances -enum class TwoTrackResonance : o2::aod::femtodatatypes::TwoTrackResonanceType { +enum class TwoTrackResonance : o2::analysis::femto::datatypes::TwoTrackResonanceType { kRho0, kPhi, kKstar0, kKstar0Bar }; -}; // namespace modes -}; // namespace o2::analysis::femto +// resonances reconstructed from two photons (PCM), e.g. pi0 -> gamma gamma, eta -> gamma gamma +enum class TwoPhotonResonance : o2::analysis::femto::datatypes::TwoPhotonResonanceType { + kPi0, + kEta +}; + +// resonances reconstructed from two tracks and a two-photon resonance, e.g. omega(782) -> pi+ pi- pi0. +// Named kOmegaMeson (not kOmega) to not collide with Cascade::kOmega, the Omega- baryon. +enum class TrackTrackTwoPhotonResonance : o2::analysis::femto::datatypes::TrackTrackTwoPhotonResonanceType { + kOmegaMeson +}; + +enum class CharmHadron : o2::analysis::femto::datatypes::CharmHadronType { + kD0, + kD0Bar, + kLc, + kLcBar +}; + +enum class EventShapeDetector : o2::analysis::femto::datatypes::EventShapeDetectorType { + kFT0C = 0, + kFT0A = 1, + kEventShapeDetectorLast = 2 +}; + +enum class QvecHarmonic : o2::analysis::femto::datatypes::QvecHarmonicType { + kN2 = 2, + kN3 = 3, + kQvecHarmonicLast = 4 +}; + +enum class KinematicVariable : o2::analysis::femto::datatypes::KinematicVariableType { + kKstar = 0, + kKt = 1, + kMt = 2, + kQ3 = 3, + kKinematicVariableLast = 4 +}; + +/// axis title for the kinematic variable plotted by CPR / pair- and triplet-cleaner QA histograms +constexpr const char* kinematicVariableToAxisTitle(KinematicVariable type) +{ + switch (type) { + case KinematicVariable::kKstar: + return "k* (GeV/#it{c})"; + case KinematicVariable::kKt: + return "k_{T} (GeV/#it{c})"; + case KinematicVariable::kMt: + return "m_{T} (GeV/#it{c}^{2})"; + case KinematicVariable::kQ3: + return "Q_{3} (GeV/#it{c})"; + default: + return "kinematic variable (GeV/#it{c})"; + } +} + +}; // namespace o2::analysis::femto::modes #endif // PWGCF_FEMTO_CORE_MODES_H_ diff --git a/PWGCF/Femto/Core/pairBuilder.h b/PWGCF/Femto/Core/pairBuilder.h index 31fb57c6ff7..dd157bbb0ae 100644 --- a/PWGCF/Femto/Core/pairBuilder.h +++ b/PWGCF/Femto/Core/pairBuilder.h @@ -17,15 +17,19 @@ #define PWGCF_FEMTO_CORE_PAIRBUILDER_H_ #include "PWGCF/Femto/Core/cascadeHistManager.h" +#include "PWGCF/Femto/Core/charmHadronHistManager.h" #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/kinkHistManager.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/pairProcessHelpers.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" #include "PWGCF/Femto/Core/twoTrackResonanceHistManager.h" #include "PWGCF/Femto/Core/v0HistManager.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" @@ -40,17 +44,15 @@ #include #include #include +#include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::pairbuilder { -namespace pairbuilder -{ - const int64_t nLimitPartitionIdenticalParticles = 2; const int64_t nLimitPartitionParticles = 1; -template +template class PairTrackTrackBuilder { public: @@ -72,7 +74,8 @@ class PairTrackTrackBuilder typename T11, typename T12, typename T13, - typename T14> + typename T14, + typename T15> void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, T2 const& confTrackSelection1, @@ -87,7 +90,8 @@ class PairTrackTrackBuilder std::map> const& trackHistSpec1, std::map> const& trackHistSpec2, std::map> const& pairHistSpec, - std::map> const& cprHistSpec) + std::map> const& cprHistSpec, + std::map> const& pairCleanerHistSpec) { // check if correlate the same tracks or not @@ -96,12 +100,14 @@ class PairTrackTrackBuilder mColHistManager.template init(registry, colHistSpec, confCollisionBinning); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); - mPc.template init(confPairCuts); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); if (mSameSpecies) { mTrackCleaner1.init(confTrackCleaner1); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -113,8 +119,10 @@ class PairTrackTrackBuilder } else { mTrackCleaner1.init(confTrackCleaner1); mTrackCleaner2.init(confTrackCleaner2); - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); mPairHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value); @@ -128,6 +136,7 @@ class PairTrackTrackBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -144,13 +153,14 @@ class PairTrackTrackBuilder } // data + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& cache) { if (mSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); @@ -158,27 +168,28 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mPairHistManagerSe, mCprSe, mPc, pairOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache) { if (mSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); @@ -186,33 +197,40 @@ class PairTrackTrackBuilder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPc, pairOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mPairHistManagerSe, mTrackCleaner1, mCprSe, mPcSe, pairOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mPairHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCprSe, mPcSe); } - template - void processMixedEvent(T1 const& cols, T2& trackTable, T3& partition1, T4& partition2, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + template + void processMixedEvent(T1 const& cols, T2& trackTable, T3& partition1, T4& partition2, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent, T9& binsVtxCentEventPlaneAngle) { if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): + if constexpr (utils::HasEventShape) { + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); + } else { + LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; + } break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -220,13 +238,20 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): + if constexpr (utils::HasEventShape) { + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); + } else { + LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; + } break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -234,19 +259,26 @@ class PairTrackTrackBuilder } } - template - void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache, T10& binsVtxMult, T11& binsVtxCent, T12& binsVtxMultCent) + template + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache, T10& binsVtxMult, T11& binsVtxCent, T12& binsVtxMultCent, T13& binsVtxCentEventPlaneAngle) { if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): + if constexpr (utils::HasEventShape) { + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1); + } else { + LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; + } break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -254,13 +286,20 @@ class PairTrackTrackBuilder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxCentEventPlaneAngle): + if constexpr (utils::HasEventShape) { + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCentEventPlaneAngle, mMixingDepth, mPairHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2); + } else { + LOG(fatal) << "Mixing policy kVtxCentEventPlaneAngle requires a collision table with event-shape columns. Breaking..."; + } break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -270,35 +309,47 @@ class PairTrackTrackBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + // single-particle histograms for particles that end up in mixed-event pairs -- same config + // objects as the same-event managers above, just a different histogram directory + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackTrack mCprSe; closepairrejection::ClosePairRejectionTrackTrack mCprMe; - paircleaner::TrackTrackPairCleaner mPc; + paircleaner::TrackTrackPairCleaner mPcSe; + paircleaner::TrackTrackPairCleaner mPcMe; particlecleaner::ParticleCleaner mTrackCleaner1; particlecleaner::ParticleCleaner mTrackCleaner2; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; bool mSameSpecies = false; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; }; -template class PairV0V0Builder @@ -325,8 +376,11 @@ class PairV0V0Builder typename T14, typename T15, typename T16, + typename T17, typename T18, - typename T17> + typename T19, + typename T20, + typename T21> void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, T2 const& confV0Selection1, @@ -341,37 +395,51 @@ class PairV0V0Builder std::map> const& colHistSpec, std::map> const& V0HistSpec1, std::map> const& V0HistSpec2, - std::map> const& PosDauHistSpec, - std::map> const& NegDauHistSpec, - std::map> const& pairHistSpec, - std::map> const& cprHistSpecPos, - std::map> const& cprHistSpecNeg) + std::map> const& PosDauHistSpec1, + std::map> const& NegDauHistSpec1, + std::map> const& PosDauHistSpec2, + std::map> const& NegDauHistSpec2, + std::map> const& pairHistSpec, + std::map> const& cprHistSpecPos, + std::map> const& cprHistSpecNeg, + std::map> const& pairCleanerHistSpec) { - - // check if correlate the same tracks or not mSameSpecies = confMixing.sameSpecies.value; + if constexpr (v0Type1 != v0Type2) { + // two different v0 species can never be identical particles + if (mSameSpecies) { + LOG(warn) << "sameSpecies=true is impossible for different v0 species (v0Type1=" + << static_cast(v0Type1) << ", v0Type2=" << static_cast(v0Type2) + << "). Overriding sameSpecies to false."; + mSameSpecies = false; + } + } + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); - mPc.template init(confPairCuts); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); - if (mSameSpecies) { - mV0Cleaner1.init(confV0Cleaner1); - mV0HistManager1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec, NegDauHistSpec); + mV0Cleaner1.init(confV0Cleaner1); + mV0Cleaner2.init(confV0Cleaner2); + // the first v0 (and its daughters) is always present + mV0HistManagerSe1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mV0HistManagerMe1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec1, NegDauHistSpec1); + + if (mSameSpecies) { mPairHistManagerSe.setMass(confV0Selection1.pdgCodeAbs.value, confV0Selection1.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); - mCprSe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprPos); + mCprSe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); mPairHistManagerMe.setMass(confV0Selection1.pdgCodeAbs.value, confV0Selection1.pdgCodeAbs.value); mPairHistManagerMe.setCharge(1, 1); mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); } else { - mV0Cleaner1.init(confV0Cleaner1); - mV0Cleaner2.init(confV0Cleaner2); - mV0HistManager1.template init(registry, V0HistSpec1, confV0Selection1, PosDauHistSpec, NegDauHistSpec); - mV0HistManager2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec, NegDauHistSpec); + mV0HistManagerSe2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mV0HistManagerMe2.template init(registry, V0HistSpec2, confV0Selection2, PosDauHistSpec2, NegDauHistSpec2); mPairHistManagerSe.setMass(confV0Selection1.pdgCodeAbs.value, confV0Selection2.pdgCodeAbs.value); mPairHistManagerSe.setCharge(1, 1); @@ -385,8 +453,10 @@ class PairV0V0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary + // mDist is bounded at declaration, so it stays valid even if this branch is not taken if (confMixing.seed.value >= 0) { uint64_t randomSeed = 0; mMixIdenticalParticles = true; @@ -399,13 +469,14 @@ class PairV0V0Builder } } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& trackTable, T3& /*v0table*/, T4& partition1, T5& partition2, T6& cache) + bool processSameEvent(T1 const& col, T2 const& trackTable, T3& /*v0table*/, T4& partition1, T5& partition2, T6& cache) { if (mSameSpecies) { auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (v0Slice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); @@ -413,27 +484,28 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManager1, mPairHistManagerSe, mCprSe, mPc, pairOrder); - } else { - auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, col, mV0HistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); } + + auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, col, mV0HistManagerSe1, mV0HistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } // mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4 const& /*v0table*/, T5& partition1, T6& partition2, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4 const& /*v0table*/, T5& partition1, T6& partition2, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { if (mSameSpecies) { auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (v0Slice1.size() < nLimitPartitionIdenticalParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); @@ -441,33 +513,33 @@ class PairV0V0Builder if (mMixIdenticalParticles) { pairOrder = static_cast(mDist(mRng)); } - pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPc, pairOrder); - } else { - auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManager1, mV0HistManager2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(v0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManagerSe1, mPairHistManagerSe, mV0Cleaner1, mCprSe, mPcSe, pairOrder); + } + + auto v0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (v0Slice1.size() < nLimitPartitionParticles || v0Slice2.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(v0Slice1, v0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mV0HistManagerSe1, mV0HistManagerSe2, mPairHistManagerSe, mV0Cleaner1, mV0Cleaner2, mCprSe, mPcSe); } - template - void processMixedEvent(T1 const& cols, T2 const& trackTable, T3 const& /*v0table*/, T4& partition1, T5& partition2, T6& cache, T7& binsVtxMult, T8& binsVtxCent, T9& binsVtxMultCent) + template + void processMixedEvent(T1 const& cols, T2 const& trackTable, T3& partition1, T4& partition2, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) { if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -475,13 +547,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -495,13 +567,13 @@ class PairV0V0Builder if (mSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner1, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe1); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -509,13 +581,13 @@ class PairV0V0Builder } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mV0Cleaner1, mV0Cleaner2, mCprMe, mPcMe, mEventStride, mV0HistManagerMe1, mV0HistManagerMe2); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -525,37 +597,53 @@ class PairV0V0Builder private: colhistmanager::CollisionHistManager mColHistManager; - v0histmanager::V0HistManager mV0HistManager1; - v0histmanager::V0HistManager mV0HistManager2; + v0histmanager::V0HistManager mV0HistManagerSe1; + v0histmanager::V0HistManager mV0HistManagerSe2; + v0histmanager::V0HistManager mV0HistManagerMe1; + v0histmanager::V0HistManager mV0HistManagerMe2; particlecleaner::ParticleCleaner mV0Cleaner1; particlecleaner::ParticleCleaner mV0Cleaner2; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionV0V0 mCprSe; closepairrejection::ClosePairRejectionV0V0 mCprMe; - paircleaner::V0V0PairCleaner mPc; + paircleaner::V0V0PairCleaner mPcSe; + paircleaner::V0V0PairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; bool mSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; - std::uniform_int_distribution<> mDist; + std::uniform_int_distribution<> mDist{static_cast(pairprocesshelpers::kOrder12), + static_cast(pairprocesshelpers::kOrder21)}; }; -template -class PairTrackV0Builder +template +class PairD0D0Builder { public: - PairTrackV0Builder() = default; - ~PairTrackV0Builder() = default; + PairD0D0Builder() = default; + ~PairD0D0Builder() = default; template + typename T16, + typename T17, + typename T18, + typename T19, + typename T20, + typename T21> void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, - T2 const& confTrackSelection, - T3 const& confTrackCleaner, - T4 const& confV0Selection, - T5 const& confV0Cleaner, - T6 const& confCpr, - T7 const& confMixing, - T8 const& confPairBinning, - T9 const& confPairCuts, - std::map>& colHistSpec, - std::map>& trackHistSpec, - std::map>& v0HistSpec, - std::map>& posDauHistSpec, - std::map>& negDauHistSpec, - std::map>& pairHistSpec, - std::map>& cprHistSpec) + T2 const& confD0Selection1, + T3 const& confD0Selection2, + T4 const& confD0Cleaner1, + T5 const& confD0Cleaner2, + T6 const& confCprPos, + T7 const& confCprNeg, + T8 const& confMixing, + T9 const& confPairBinning, + T10 const& confPairCuts, + std::map> const& colHistSpec, + std::map> const& D0HistSpec1, + std::map> const& D0HistSpec2, + std::map> const& PosDauHistSpec1, + std::map> const& NegDauHistSpec1, + std::map> const& PosDauHistSpec2, + std::map> const& NegDauHistSpec2, + std::map> const& pairHistSpec, + std::map> const& cprHistSpecPos, + std::map> const& cprHistSpecNeg, + std::map> const& pairCleanerHistSpec) { + mSameSpecies = confMixing.sameSpecies.value; + + if (mSameSpecies && confD0Selection1.sign.value != confD0Selection2.sign.value) { + LOG(warn) << "sameSpecies=true, but the two selections have different signs (" + << confD0Selection1.sign.value << " vs " << confD0Selection2.sign.value + << "). D0 and D0bar are not identical particles. Overriding sameSpecies to false."; + mSameSpecies = false; + } + if (!mSameSpecies && confD0Selection1.sign.value == confD0Selection2.sign.value) { + LOG(warn) << "sameSpecies=false for identical selections with identical sign (" + << confD0Selection1.sign.value << "). If both selections match the same " + << "candidates, this produces self-pairs and double counting."; + } + if (confD0Selection1.sign.value == 0 || confD0Selection2.sign.value == 0) { + LOG(warn) << "sign=0 accepts both the D0 and the D0bar hypothesis of the same candidate. " + << "Set an explicit sign (+1: D0, -1: D0bar) for both slots in a D0-D0 analysis."; + } mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mV0HistManager.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); + mD0Cleaner1.init(confD0Cleaner1); + mD0Cleaner2.init(confD0Cleaner2); - mTrackCleaner.init(confTrackCleaner); - mV0Cleaner.init(confV0Cleaner); + mD0HistManagerSe1.template init(registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); + mD0HistManagerMe1.template init(registry, D0HistSpec1, confD0Selection1, PosDauHistSpec1, NegDauHistSpec1); - int pdgCodePosDau = 0; - int pdgCodeNegDau = 0; - if (modes::isEqual(v0Type, modes::V0::kK0short)) { - pdgCodePosDau = kPiPlus; - pdgCodeNegDau = kPiMinus; - } else if (modes::isEqual(v0Type, modes::V0::kLambda) || modes::isEqual(v0Type, modes::V0::kAntiLambda)) { - if (confV0Selection.sign.value > 0) { - pdgCodePosDau = kProton; - pdgCodeNegDau = kPiMinus; - } else { - pdgCodePosDau = kPiPlus; - pdgCodeNegDau = kProtonBar; + auto prongPdgCodes = [](int sign) -> std::pair { + if (sign > 0) { + return {PDG_t::kPiPlus, PDG_t::kKMinus}; } - } + return {PDG_t::kKPlus, PDG_t::kPiMinus}; + }; + auto [posDauPdg1, negDauPdg1] = prongPdgCodes(confD0Selection1.sign.value); - mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); - mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confV0Selection.pdgCodeAbs.value, pdgCodePosDau, pdgCodeNegDau); - mPairHistManagerSe.setCharge(confTrackSelection.chargeAbs.value, 1); - mCprSe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + if (mSameSpecies) { + mPairHistManagerSe.setMass(confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1, confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1); + mPairHistManagerSe.setCharge(1, 1); + mCprSe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); - mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); - mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confV0Selection.pdgCodeAbs.value, pdgCodePosDau, pdgCodeNegDau); - mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); - mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); - mPc.template init(confPairCuts); + mPairHistManagerMe.setMass(confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1, confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1); + mPairHistManagerMe.setCharge(1, 1); + mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); + } else { + mD0HistManagerSe2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); + mD0HistManagerMe2.template init(registry, D0HistSpec2, confD0Selection2, PosDauHistSpec2, NegDauHistSpec2); + auto [posDauPdg2, negDauPdg2] = prongPdgCodes(confD0Selection2.sign.value); + + mPairHistManagerSe.setMass(confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1, confD0Selection2.pdgCodeAbs.value, posDauPdg2, negDauPdg2); + mPairHistManagerSe.setCharge(1, 1); + mCprSe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); + + mPairHistManagerMe.setMass(confD0Selection1.pdgCodeAbs.value, posDauPdg1, negDauPdg1, confD0Selection2.pdgCodeAbs.value, posDauPdg2, negDauPdg2); + mPairHistManagerMe.setCharge(1, 1); + mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); + } // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + // setup rng if necessary + // mDist is bounded at declaration, so it stays valid even if this branch is not taken + if (confMixing.seed.value >= 0) { + uint64_t randomSeed = 0; + mMixIdenticalParticles = true; + if (confMixing.seed.value == 0) { + randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + } else { + randomSeed = static_cast(confMixing.seed.value); + } + mRng = std::mt19937(randomSeed); + } } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*v0table*/, T5& v0Partition, T6& cache) + bool processSameEvent(T1 const& col, T2 const& trackTable, T3& /*d0table*/, T4& partition1, T5& partition2, T6& cache) { - auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; + if (mSameSpecies) { + auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice1.size() < nLimitPartitionIdenticalParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + pairprocesshelpers::PairOrder pairOrder = pairprocesshelpers::kOrder12; + if (mMixIdenticalParticles) { + pairOrder = static_cast(mDist(mRng)); + } + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, col, mD0HistManagerSe1, mPairHistManagerSe, mCprSe, mPcSe, pairOrder); + } + + auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice1.size() < nLimitPartitionParticles || d0Slice2.size() < nLimitPartitionParticles) { + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, col, mD0HistManagerSe1, mD0HistManagerSe2, mPairHistManagerSe, mCprSe, mPcSe); } + // mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*v0table*/, T6& v0Partition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4 const& /*d0table*/, T5& partition1, T6& partition2, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { - auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; + if (mSameSpecies) { + auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice1.size() < nLimitPartitionIdenticalParticles) { + return false; + } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + pairprocesshelpers::PairOrder pairOrder = pairprocesshelpers::kOrder12; + if (mMixIdenticalParticles) { + pairOrder = static_cast(mDist(mRng)); + } + return pairprocesshelpers::processSameEvent(d0Slice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManagerSe1, mPairHistManagerSe, mD0Cleaner1, mCprSe, mPcSe, pairOrder); + } + auto d0Slice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto d0Slice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice1.size() < nLimitPartitionParticles || d0Slice2.size() < nLimitPartitionParticles) { + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mV0HistManager, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(d0Slice1, d0Slice2, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mD0HistManagerSe1, mD0HistManagerSe2, mPairHistManagerSe, mD0Cleaner1, mD0Cleaner2, mCprSe, mPcSe); } template - void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& v0Partition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + void processMixedEvent(T1 const& cols, T2 const& trackTable, T3& partition1, T4& partition2, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) { - switch (mMixingPolicy) { - case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); - break; - case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); - break; - case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + if (mSameSpecies) { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } template - void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& v0Partition, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache, T10& binsVtxMult, T11& binsVtxCent, T12& binsVtxMultCent) + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache, T10& binsVtxMult, T11& binsVtxCent, T12& binsVtxMultCent) { - switch (mMixingPolicy) { - case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPc); - break; - case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPc); - break; - case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPc); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + if (mSameSpecies) { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner1, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe1); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mD0Cleaner1, mD0Cleaner2, mCprMe, mPcMe, mEventStride, mD0HistManagerMe1, mD0HistManagerMe2); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - v0histmanager::V0HistManager mV0HistManager; - particlecleaner::ParticleCleaner mTrackCleaner; - particlecleaner::ParticleCleaner mV0Cleaner; - pairhistmanager::PairHistManager mPairHistManagerSe; - pairhistmanager::PairHistManager mPairHistManagerMe; - closepairrejection::ClosePairRejectionTrackV0 mCprSe; - closepairrejection::ClosePairRejectionTrackV0 mCprMe; - paircleaner::TrackV0PairCleaner mPc; + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe1; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe2; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe1; // the third prong prefix is unused for 2-prong candidates + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe2; // the third prong prefix is unused for 2-prong candidates + particlecleaner::ParticleCleaner mD0Cleaner1; + particlecleaner::ParticleCleaner mD0Cleaner2; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionV0V0 mCprSe; + closepairrejection::ClosePairRejectionV0V0 mCprMe; + paircleaner::V0V0PairCleaner mPcSe; + paircleaner::V0V0PairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + bool mSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; + bool mMixIdenticalParticles = false; + std::mt19937 mRng; + std::uniform_int_distribution<> mDist{static_cast(pairprocesshelpers::kOrder12), + static_cast(pairprocesshelpers::kOrder21)}; }; -template -class PairTrackTwoTrackResonanceBuilder +template +class PairTrackD0Builder { public: - PairTrackTwoTrackResonanceBuilder() = default; - ~PairTrackTwoTrackResonanceBuilder() = default; + PairTrackD0Builder() = default; + ~PairTrackD0Builder() = default; template + typename T14, + typename T15, + typename T16, + typename T17> + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confTrackSelection, + T3 const& confTrackCleaner, + T4 const& confD0Selection, + T5 const& confD0Cleaner, + T6 const& confCpr, + T7 const& confMixing, + T8 const& confPairBinning, + T9 const& confPairCuts, + std::map>& colHistSpec, + std::map>& trackHistSpec, + std::map>& d0HistSpec, + std::map>& posDauHistSpec, + std::map>& negDauHistSpec, + std::map>& pairHistSpec, + std::map>& cprHistSpec, + std::map>& pairCleanerHistSpec) + { + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mD0HistManagerSe.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mD0HistManagerMe.template init(registry, d0HistSpec, confD0Selection, posDauHistSpec, negDauHistSpec); + + mTrackCleaner.init(confTrackCleaner); + mD0Cleaner.init(confD0Cleaner); + + // pdg codes of the D0 prongs depend on the hypothesis: + // D0 -> pi+ K- => PosDau = pion, NegDau = kaon + // D0bar -> K+ pi- => PosDau = kaon, NegDau = pion + int posDauPdg = 0; + int negDauPdg = 0; + if (confD0Selection.sign.value > 0) { + posDauPdg = PDG_t::kPiPlus; + negDauPdg = PDG_t::kKMinus; + } else { + posDauPdg = PDG_t::kKPlus; + negDauPdg = PDG_t::kPiMinus; + } + + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confD0Selection.pdgCodeAbs.value, posDauPdg, negDauPdg); + mPairHistManagerSe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprSe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confD0Selection.pdgCodeAbs.value, posDauPdg, negDauPdg); + mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*d0table*/, T5& d0Partition, T6& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto d0Slice = d0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || d0Slice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, col, mTrackHistManagerSe, mD0HistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& d0Partition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, d0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*d0table*/, T6& d0Partition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto d0Slice = d0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || d0Slice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, d0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mD0HistManagerSe, mPairHistManagerSe, mTrackCleaner, mD0Cleaner, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& d0Partition, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache, T10& binsVtxMult, T11& binsVtxCent, T12& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, d0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mD0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mD0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerSe; // the third prong prefix is unused for 2-prong candidates + trackhistmanager::TrackHistManager mTrackHistManagerMe; + charmhadronhistmanager::CharmHadronHistManager mD0HistManagerMe; // the third prong prefix is unused for 2-prong candidates + particlecleaner::ParticleCleaner mTrackCleaner; + particlecleaner::ParticleCleaner mD0Cleaner; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionTrackV0 mCprSe; + closepairrejection::ClosePairRejectionTrackV0 mCprMe; + paircleaner::TrackV0PairCleaner mPcSe; + paircleaner::TrackV0PairCleaner mPcMe; + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; +}; + +template +class PairTrackLcBuilder +{ + public: + PairTrackLcBuilder() = default; + ~PairTrackLcBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confTrackSelection, + T3 const& confTrackCleaner, + T4 const& confLcSelection, + T5 const& confLcCleaner, + T6 const& confCprProton, + T7 const& confCprKaon, + T8 const& confCprPion, + T9 const& confMixing, + T10 const& confPairBinning, + T11 const& confPairCuts, + std::map>& colHistSpec, + std::map>& trackHistSpec, + std::map>& lcHistSpec, + std::map>& protonDauHistSpec, + std::map>& kaonDauHistSpec, + std::map>& pionDauHistSpec, + std::map>& pairHistSpec, + std::map>& cprHistSpec, + std::map>& pairCleanerHistSpec) + { + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mLcHistManagerSe.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mLcHistManagerMe.template init(registry, lcHistSpec, confLcSelection, protonDauHistSpec, kaonDauHistSpec, pionDauHistSpec); + + mTrackCleaner.init(confTrackCleaner); + mLcCleaner.init(confLcCleaner); + + // prongs are stored remapped onto the accepted hypothesis, so the roles are fixed: + // Lc -> p+ K- pi+ + // Lcbar -> p- K+ pi- + const int protonDauPdg = (confLcSelection.sign.value > 0) ? PDG_t::kProton : -PDG_t::kProton; + const int kaonDauPdg = (confLcSelection.sign.value > 0) ? PDG_t::kKMinus : PDG_t::kKPlus; + + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confLcSelection.pdgCodeAbs.value, protonDauPdg, kaonDauPdg); + mPairHistManagerSe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprSe.init(registry, cprHistSpec, cprHistSpec, cprHistSpec, confCprProton, confCprKaon, confCprPion, confTrackSelection.chargeAbs.value); + + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confLcSelection.pdgCodeAbs.value, protonDauPdg, kaonDauPdg); + mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprMe.init(registry, cprHistSpec, cprHistSpec, cprHistSpec, confCprProton, confCprKaon, confCprPion, confTrackSelection.chargeAbs.value); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*lcTable*/, T5& lcPartition, T6& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto lcSlice = lcPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || lcSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, col, mTrackHistManagerSe, mLcHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& lcPartition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, lcPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*lcTable*/, T6& lcPartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto lcSlice = lcPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || lcSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, lcSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mLcHistManagerSe, mPairHistManagerSe, mTrackCleaner, mLcCleaner, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& lcPartition, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache, T10& binsVtxMult, T11& binsVtxCent, T12& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, lcPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mLcCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mLcHistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + charmhadronhistmanager::CharmHadronHistManager mLcHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + charmhadronhistmanager::CharmHadronHistManager mLcHistManagerMe; + particlecleaner::ParticleCleaner mTrackCleaner; + particlecleaner::ParticleCleaner mLcCleaner; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionTrackLc mCprSe; + closepairrejection::ClosePairRejectionTrackLc mCprMe; + paircleaner::TrackLcPairCleaner mPcSe; + paircleaner::TrackLcPairCleaner mPcMe; + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; +}; + +template +class PairTrackV0Builder +{ + public: + PairTrackV0Builder() = default; + ~PairTrackV0Builder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confTrackSelection, + T3 const& confTrackCleaner, + T4 const& confV0Selection, + T5 const& confV0Cleaner, + T6 const& confCpr, + T7 const& confMixing, + T8 const& confPairBinning, + T9 const& confPairCuts, + std::map>& colHistSpec, + std::map>& trackHistSpec, + std::map>& v0HistSpec, + std::map>& posDauHistSpec, + std::map>& negDauHistSpec, + std::map>& pairHistSpec, + std::map>& cprHistSpec, + std::map>& pairCleanerHistSpec) + { + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mV0HistManagerSe.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mV0HistManagerMe.template init(registry, v0HistSpec, confV0Selection, posDauHistSpec, negDauHistSpec); + + mTrackCleaner.init(confTrackCleaner); + mV0Cleaner.init(confV0Cleaner); + + int pdgCodePosDau = 0; + int pdgCodeNegDau = 0; + if (modes::isEqual(v0Type, modes::V0::kK0short)) { + pdgCodePosDau = kPiPlus; + pdgCodeNegDau = kPiMinus; + } else if (modes::isEqual(v0Type, modes::V0::kLambda) || modes::isEqual(v0Type, modes::V0::kAntiLambda)) { + if (confV0Selection.sign.value > 0) { + pdgCodePosDau = kProton; + pdgCodeNegDau = kPiMinus; + } else { + pdgCodePosDau = kPiPlus; + pdgCodeNegDau = kProtonBar; + } + } + + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confV0Selection.pdgCodeAbs.value, pdgCodePosDau, pdgCodeNegDau); + mPairHistManagerSe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprSe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, 0, 0, confV0Selection.pdgCodeAbs.value, pdgCodePosDau, pdgCodeNegDau); + mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*v0table*/, T5& v0Partition, T6& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, col, mTrackHistManagerSe, mV0HistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*v0table*/, T6& v0Partition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col, mcCols); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mV0HistManagerSe, mPairHistManagerSe, mTrackCleaner, mV0Cleaner, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& v0Partition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + template + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& v0Partition, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& cache, T10& binsVtxMult, T11& binsVtxCent, T12& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, v0Partition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mV0Cleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mV0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + v0histmanager::V0HistManager mV0HistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + v0histmanager::V0HistManager mV0HistManagerMe; + particlecleaner::ParticleCleaner mTrackCleaner; + particlecleaner::ParticleCleaner mV0Cleaner; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionTrackV0 mCprSe; + closepairrejection::ClosePairRejectionTrackV0 mCprMe; + paircleaner::TrackV0PairCleaner mPcSe; + paircleaner::TrackV0PairCleaner mPcMe; + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; +}; + +// Track-TwoPhotonResonance pair builder. +// Note: close pair rejection is a no-op (ClosePairRejectionTrackTwoPhotonResonance) and pair cleaning +// reuses the plain TrackTrackPairCleaner (globalIndex-only), since photon daughters are, for +// now, not indexed into FTracks: they are TPC-only tracks, so they cannot be confused for a +// global track the way two V0 daughters or a track and a V0 daughter can. +template +class PairTrackTwoPhotonResonanceBuilder +{ + public: + PairTrackTwoPhotonResonanceBuilder() = default; + ~PairTrackTwoPhotonResonanceBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confTrackSelection, + T3 const& confResonanceSelection, + T4 const& confCpr, + T5 const& confMixing, + T6 const& confPairBinning, + T7 const& confPairCuts, + T8 const& confDauQaBinning, + std::map> const& colHistSpec, + std::map> const& trackHistSpec, + std::map> const& resonanceHistSpec, + std::map> const& dauHistSpec, + std::map> const& pairHistSpec, + std::map> const& cprHistSpec, + std::map> const& pairCleanerHistSpec) + { + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, dauHistSpec, confDauQaBinning); + + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerSe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprSe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); + mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2& trackPartition, T3& resonancePartition, T4& photonTable, T5& cache) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto resonanceSlice = resonancePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() < nLimitPartitionParticles || resonanceSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(trackSlice, resonanceSlice, photonTable, col, mTrackHistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2& trackPartition, T3& resonancePartition, T4& photonTable, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, photonTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mResonanceHistManagerMe; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionTrackTwoPhotonResonance mCprSe; + closepairrejection::ClosePairRejectionTrackTwoPhotonResonance mCprMe; + paircleaner::TrackTrackPairCleaner mPcSe; + paircleaner::TrackTrackPairCleaner mPcMe; + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; +}; + +template +class PairTrackTwoTrackResonanceBuilder +{ + public: + PairTrackTwoTrackResonanceBuilder() = default; + ~PairTrackTwoTrackResonanceBuilder() = default; + + template void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, T2 const& confTrackSelection, @@ -754,12 +1632,15 @@ class PairTrackTwoTrackResonanceBuilder std::map> const& posDauHistSpec, std::map> const& negDauHistSpec, std::map> const& pairHistSpec, - std::map> const& cprHistSpec) + std::map> const& cprHistSpec, + std::map> const& pairCleanerHistSpec) { mColHistManager.template init(registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mResonanceHistManager.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, confResonanceSelection, posDauHistSpec, negDauHistSpec); mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); mPairHistManagerSe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); @@ -770,23 +1651,27 @@ class PairTrackTwoTrackResonanceBuilder mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*resonanceTable*/, T5& resonancePartition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*resonanceTable*/, T5& resonancePartition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto resonanaceSlice = resonancePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || resonanaceSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManager, mResonanceHistManager, mPairHistManagerSe, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(trackSlice, resonanaceSlice, trackTable, col, mTrackHistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } template @@ -794,13 +1679,13 @@ class PairTrackTwoTrackResonanceBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mResonanceHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -809,24 +1694,173 @@ class PairTrackTwoTrackResonanceBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerMe; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; - closepairrejection::ClosePairRejectionTrackV0 mCprSe; // cpr for twotrackresonances and v0 work the same way - closepairrejection::ClosePairRejectionTrackV0 mCprMe; // cpr for twotrackresonances and v0 work the same way - paircleaner::TrackV0PairCleaner mPc; // pc for twotrackresonances and v0 work the same way + closepairrejection::ClosePairRejectionTrackV0 mCprSe; // cpr for twotrackresonances and v0 work the same way + closepairrejection::ClosePairRejectionTrackV0 mCprMe; // cpr for twotrackresonances and v0 work the same way + paircleaner::TrackV0PairCleaner mPcSe; // pc for twotrackresonances and v0 work the same way + paircleaner::TrackV0PairCleaner mPcMe; // pc for twotrackresonances and v0 work the same way + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; +}; + +template +class PairV0TwoTrackResonanceBuilder +{ + public: + PairV0TwoTrackResonanceBuilder() = default; + ~PairV0TwoTrackResonanceBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confV0Selection, + T3 const& confResonanceSelection, + T4 const& confCprPos, + T5 const& confCprNeg, + T6 const& confMixing, + T7 const& confPairBinning, + T8 const& confPairCuts, + std::map> const& colHistSpec, + std::map> const& v0HistSpec, + std::map> const& V0posDauHistSpec, + std::map> const& V0negDauHistSpec, + std::map> const& resonanceHistSpec, + std::map> const& ResonancePosDauHistSpec, + std::map> const& ResonanceNegDauHistSpec, + std::map> const& pairHistSpec, + std::map> const& cprHistSpecPos, + std::map> const& cprHistSpecNeg, + std::map> const& pairCleanerHistSpec) + { + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + + mV0HistManagerSe.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); + mResonanceHistManagerSe.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); + mV0HistManagerMe.template init(registry, v0HistSpec, confV0Selection, V0posDauHistSpec, V0negDauHistSpec); + mResonanceHistManagerMe.template init(registry, resonanceHistSpec, confResonanceSelection, ResonancePosDauHistSpec, ResonanceNegDauHistSpec); + + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerSe.setMass(confV0Selection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerSe.setCharge(1, 1); // set charge to 1 + mCprSe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); + + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.setMass(confV0Selection.pdgCodeAbs.value, confResonanceSelection.pdgCodeAbs.value); + mPairHistManagerMe.setCharge(1, 1); // set charge to 1 + mCprMe.init(registry, cprHistSpecPos, cprHistSpecNeg, confCprPos, confCprNeg); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + } + + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2& trackTable, T3& v0Partition, T4& resonancePartition, T5& cache) + { + auto v0Slice = v0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto resonanaceSlice = resonancePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (v0Slice.size() < nLimitPartitionParticles || resonanaceSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCprSe.setMagField(col.magField()); + return pairprocesshelpers::processSameEvent(v0Slice, resonanaceSlice, trackTable, col, mV0HistManagerSe, mResonanceHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2& trackTable, T3& v0Partition, T4& resonancePartition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, v0Partition, resonancePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mV0HistManagerMe, mResonanceHistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + v0histmanager::V0HistManager mV0HistManagerSe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerSe; + v0histmanager::V0HistManager mV0HistManagerMe; + twotrackresonancehistmanager::TwoTrackResonanceHistManager mResonanceHistManagerMe; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionV0V0 mCprSe; // cpr for twotrackresonances and v0 work the same way + closepairrejection::ClosePairRejectionV0V0 mCprMe; // cpr for twotrackresonances and v0 work the same way + paircleaner::V0V0PairCleaner mPcSe; // pc for twotrackresonances and v0 work the same way + paircleaner::V0V0PairCleaner mPcMe; // pc for twotrackresonances and v0 work the same way pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; -template class PairTrackKinkBuilder { @@ -850,7 +1884,8 @@ class PairTrackKinkBuilder typename T12, typename T13, typename T14, - typename T15> + typename T15, + typename T16> void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, T2 const& confTrackSelection, @@ -866,12 +1901,15 @@ class PairTrackKinkBuilder std::map> const& kinkHistSpec, std::map> const& chaDauHistSpec, std::map> const& pairHistSpec, - std::map> const& cprHistSpec) + std::map> const& cprHistSpec, + std::map> const& pairCleanerHistSpec) { mColHistManager.template init(registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mKinkHistManager.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mKinkHistManagerSe.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mKinkHistManagerMe.template init(registry, kinkHistSpec, confKinkSelection, chaDauHistSpec); mTrackCleaner.init(confTrackCleaner); mKinkCleaner.init(confKinkCleaner); @@ -885,37 +1923,41 @@ class PairTrackKinkBuilder mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, confKinkSelection.pdgCodeAbs.value); mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); // abs charge of kink daughter is always 1 mCprMe.init(registry, cprHistSpec, confCpr, confTrackSelection.chargeAbs.value); - mPc.template init(confPairCuts); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*kinktable*/, T5& kinkPartition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*kinktable*/, T5& kinkPartition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto kinkSlice = kinkPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || kinkSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, col, mTrackHistManagerSe, mKinkHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& /*kinktable*/, T6& kinkPartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& trackPartition, T5& /*kinktable*/, T6& kinkPartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto kinkSlice = kinkPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || kinkSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mKinkHistManager, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(trackSlice, kinkSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mKinkHistManagerSe, mPairHistManagerSe, mTrackCleaner, mKinkCleaner, mCprSe, mPcSe); } template @@ -923,13 +1965,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, kinkPartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -941,13 +1983,13 @@ class PairTrackKinkBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, kinkPartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mKinkCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mKinkHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -956,30 +1998,39 @@ class PairTrackKinkBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - kinkhistmanager::KinkHistManager mKinkHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + kinkhistmanager::KinkHistManager mKinkHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + kinkhistmanager::KinkHistManager mKinkHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mKinkCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackKink mCprSe; closepairrejection::ClosePairRejectionTrackKink mCprMe; - paircleaner::TrackKinkPairCleaner mPc; + paircleaner::TrackKinkPairCleaner mPcSe; + paircleaner::TrackKinkPairCleaner mPcMe; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; }; -template class PairTrackCascadeBuilder { @@ -1007,7 +2058,8 @@ class PairTrackCascadeBuilder typename T16, typename T17, typename T18, - typename T19> + typename T19, + typename T20> void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, T2 const& confTrackSelection, @@ -1027,12 +2079,15 @@ class PairTrackCascadeBuilder std::map> const& negDauHistSpec, std::map> const& pairHistSpec, std::map> const& cprHistSpecBachelor, - std::map> const& cprHistSpecV0Daughter) + std::map> const& cprHistSpecV0Daughter, + std::map> const& pairCleanerHistSpec) { mColHistManager.template init(registry, colHistSpec, confCollisionBinning); - mTrackHistManager.template init(registry, trackHistSpec, confTrackSelection); - mCascadeHistManager.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerSe.template init(registry, trackHistSpec, confTrackSelection); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe.template init(registry, trackHistSpec, confTrackSelection); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); mTrackCleaner.init(confTrackCleaner); mCascadeCleaner.init(confCascadeCleaner); @@ -1046,50 +2101,55 @@ class PairTrackCascadeBuilder mPairHistManagerMe.setMass(confTrackSelection.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); mPairHistManagerMe.setCharge(confTrackSelection.chargeAbs.value, 1); mCprMe.init(registry, cprHistSpecBachelor, cprHistSpecV0Daughter, confCprBachelor, confCprV0Daughter, confTrackSelection.chargeAbs.value); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*cascadeTable*/, T5& cascadePartition, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& trackPartition, T4& /*cascadeTable*/, T5& cascadePartition, T6& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto cascadeSlice = cascadePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, col, mTrackHistManagerSe, mCascadeHistManagerSe, mPairHistManagerSe, mCprSe, mPcSe); } + /// \return true if at least one pair passed all pair selections (usable as pair trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*cascadeTabel*/, T6& cascadePartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3 const& trackTable, T4& trackPartition, T5 const& /*cascadeTabel*/, T6& cascadePartition, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto cascadeSlice = cascadePartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCprSe.setMagField(col.magField()); - pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager, mCascadeHistManager, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPc); + return pairprocesshelpers::processSameEvent(trackSlice, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe, mCascadeHistManagerSe, mPairHistManagerSe, mTrackCleaner, mCascadeCleaner, mCprSe, mPcSe); } template - void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& v0Partition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) + void processMixedEvent(T1 const& cols, T2& trackTable, T3& trackPartition, T4& cascadePartition, T5& cache, T6& binsVtxMult, T7& binsVtxCent, T8& binsVtxMultCent) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, trackPartition, v0Partition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, trackPartition, cascadePartition, trackTable, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1101,13 +2161,13 @@ class PairTrackCascadeBuilder { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPc); + pairprocesshelpers::processMixedEvent(cols, mcCols, trackPartition, cascadePartition, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mTrackCleaner, mCascadeCleaner, mCprMe, mPcMe, mEventStride, mTrackHistManagerMe, mCascadeHistManagerMe); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -1116,20 +2176,212 @@ class PairTrackCascadeBuilder private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager; - cascadehistmanager::CascadeHistManager mCascadeHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe; + cascadehistmanager::CascadeHistManager mCascadeHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe; + cascadehistmanager::CascadeHistManager mCascadeHistManagerMe; particlecleaner::ParticleCleaner mTrackCleaner; particlecleaner::ParticleCleaner mCascadeCleaner; pairhistmanager::PairHistManager mPairHistManagerSe; pairhistmanager::PairHistManager mPairHistManagerMe; closepairrejection::ClosePairRejectionTrackCascade mCprSe; closepairrejection::ClosePairRejectionTrackCascade mCprMe; - paircleaner::TrackCascadePairCleaner mPc; + paircleaner::TrackCascadePairCleaner mPcSe; + paircleaner::TrackCascadePairCleaner mPcMe; + pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; +}; + +template +class PairMcParticleMcParticleBuilder +{ + public: + PairMcParticleMcParticleBuilder() = default; + ~PairMcParticleMcParticleBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confMcParticleSelection1, + T3 const& confMcParticleSelection2, + T4 const& confMcParticleBinning1, + T5 const& confMcParticleBinning2, + T6 const& confMcParticleCleaner1, + T7 const& confMcParticleCleaner2, + T8 const& confCpr, + T9 const& confMixing, + T10 const& confPairBinning, + T11 const& confPairCuts, + std::map> const& colHistSpec, + std::map> const& mcParticleHistSpec1, + std::map> const& mcParticleHistSpec2, + std::map> const& pairHistSpec, + std::map> const& cprHistSpec, + std::map> const& pairCleanerHistSpec) + { + + // check if correlate the same tracks or not + mSameSpecies = confMixing.sameSpecies.value; + + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + mPairHistManagerSe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPairHistManagerMe.template init(registry, pairHistSpec, confPairBinning, confPairCuts, confMixing); + mPcSe.template init(registry, pairCleanerHistSpec, confPairCuts); + mPcMe.template init(registry, pairCleanerHistSpec, confPairCuts); + + if (mSameSpecies) { + mMcParticleCleaner1.init(confMcParticleCleaner1); + + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + + mPairHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value); + mPairHistManagerSe.setCharge(1, 1); + mCprSe.init(registry, cprHistSpec, confCpr); + + mPairHistManagerMe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value); + mPairHistManagerMe.setCharge(1, 1); + mCprMe.init(registry, cprHistSpec, confCpr); + } else { + mMcParticleCleaner1.init(confMcParticleCleaner1); + mMcParticleCleaner2.init(confMcParticleCleaner2); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerSe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + + mPairHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection2.pdgCodeAbs.value); + mPairHistManagerSe.setCharge(1, 1); + mCprSe.init(registry, cprHistSpec, confCpr); + + mPairHistManagerMe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection2.pdgCodeAbs.value); + mPairHistManagerMe.setCharge(1, 1); + mCprMe.init(registry, cprHistSpec, confCpr); + } + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + + // setup rng if necessary + if (confMixing.seed.value >= 0) { + uint64_t randomSeed = 0; + mMixIdenticalParticles = true; + if (confMixing.seed.value == 0) { + randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + } else { + randomSeed = static_cast(confMixing.seed.value); + } + mRng = std::mt19937(randomSeed); + mDist = std::uniform_int_distribution(static_cast(pairprocesshelpers::kOrder12), static_cast(pairprocesshelpers::kOrder21)); + } + } + + // only mc + /// \return true if at least one pair passed all pair selections (usable as pair trigger) + template + bool processSameEvent(T1 const& col, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& cache) + { + if (mSameSpecies) { + auto mcParticleSlice = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + + if (mcParticleSlice.size() < nLimitPartitionIdenticalParticles) { + return false; + } + mColHistManager.template fill(col); + pairprocesshelpers::PairOrder pairOrder = pairprocesshelpers::kOrder12; + if (mMixIdenticalParticles) { + pairOrder = static_cast(mDist(mRng)); + } + return pairprocesshelpers::processSameEvent(mcParticleSlice, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManagerSe1, mPairHistManagerSe, mMcParticleCleaner1, mCprSe, mPcSe, pairOrder); + } + + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + auto mcParticleSlice2 = partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + if (mcParticleSlice1.size() < nLimitPartitionParticles || mcParticleSlice2.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + return pairprocesshelpers::processSameEvent(mcParticleSlice1, mcParticleSlice2, mcParticles, mcMothers, mcPartonicMothers, col, mMcParticleHistManagerSe1, mMcParticleHistManagerSe2, mPairHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mCprSe, mPcSe); + } + + template + void processMixedEvent(T1 const& cols, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& cache, T8& binsVtxMult, T9& binsVtxCent, T10& binsVtxMultCent) + { + if (mSameSpecies) { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); + break; + case static_cast(pairhistmanager::kVtxMultCent): + pairprocesshelpers::processMixedEvent(cols, partition1, partition2, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mPairHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mCprMe, mPcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe2; + pairhistmanager::PairHistManager mPairHistManagerSe; + pairhistmanager::PairHistManager mPairHistManagerMe; + closepairrejection::ClosePairRejectionMcParticleMcParticle mCprSe; + closepairrejection::ClosePairRejectionMcParticleMcParticle mCprMe; + paircleaner::McParticleMcParticlePairCleaner mPcSe; + paircleaner::McParticleMcParticlePairCleaner mPcMe; + particlecleaner::ParticleCleaner mMcParticleCleaner1; + particlecleaner::ParticleCleaner mMcParticleCleaner2; pairhistmanager::MixingPolicy mMixingPolicy = pairhistmanager::MixingPolicy::kVtxMult; int mMixingDepth = 5; + int mEventStride = 1; + bool mSameSpecies = false; + bool mMixIdenticalParticles = false; + std::mt19937 mRng; + std::uniform_int_distribution<> mDist; }; -} // namespace pairbuilder -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::pairbuilder #endif // PWGCF_FEMTO_CORE_PAIRBUILDER_H_ diff --git a/PWGCF/Femto/Core/pairCleaner.h b/PWGCF/Femto/Core/pairCleaner.h index 0d66be6721f..358ba59962f 100644 --- a/PWGCF/Femto/Core/pairCleaner.h +++ b/PWGCF/Femto/Core/pairCleaner.h @@ -16,223 +16,497 @@ #ifndef PWGCF_FEMTO_CORE_PAIRCLEANER_H_ #define PWGCF_FEMTO_CORE_PAIRCLEANER_H_ +#include "PWGCF/Femto/Core/histManager.h" #include "PWGCF/Femto/Core/modes.h" +#include +#include +#include #include -namespace o2::analysis::femto +#include + +#include +#include +#include +#include + +namespace o2::analysis::femto::paircleaner { -namespace paircleaner + +// enum for pair cleaner histograms +enum PairCleanerHist { + // standard 1D + kStats, + kKinematic, // kinematic variable of the pair/triplet + kPairCleanerHistLast +}; + +// bin indices (0-based fill values) for the kStats histogram +enum PairCleanerStatsBin { + kAllAnalyzed = 0, + kAllBlocked = 1, + kPairCleanerStatsBinLast = 2, +}; + +struct ConfPairCleanerBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PairCleanerBinning"); + o2::framework::ConfigurableAxis kinematic{"kinematic", {{100, 0.f, 1.f}}, "kinematic"}; +}; + +constexpr std::array, kPairCleanerHistLast> + HistTable = { + { + // 1D + {kStats, o2::framework::HistType::kTH1F, "hStats", "Stats; ; Entries"}, + {kKinematic, o2::framework::HistType::kTH1F, "hKinematic", "Kinematic of blocked pairs; Kinematic variable (GeV/#it{c}); Entries"}, + }}; + +template +auto makePairCleanerHistSpecMap(const T& confPairCleaner) { + return std::map>{ + {kKinematic, {confPairCleaner.kinematic}}, + }; +}; +constexpr char PrefixPairCleanerTrackTrackSe[] = "TrackTrackCleaner/SE/"; +constexpr char PrefixPairCleanerTrackTrackMe[] = "TrackTrackCleaner/ME/"; +constexpr char PrefixPairCleanerV0V0Se[] = "V0V0Cleaner/SE/"; +constexpr char PrefixPairCleanerV0V0Me[] = "V0V0Cleaner/ME/"; +constexpr char PrefixPairCleanerD0D0Se[] = "D0D0Cleaner/SE/"; +constexpr char PrefixPairCleanerD0D0Me[] = "D0D0Cleaner/ME/"; +constexpr char PrefixPairCleanerTrackV0Se[] = "TrackV0Cleaner/SE/"; +constexpr char PrefixPairCleanerTrackV0Me[] = "TrackV0Cleaner/ME/"; +constexpr char PrefixPairCleanerTrackD0Se[] = "TrackD0Cleaner/SE/"; +constexpr char PrefixPairCleanerTrackD0Me[] = "TrackD0Cleaner/ME/"; +constexpr char PrefixPairCleanerTrackLcSe[] = "TrackLcCleaner/SE/"; +constexpr char PrefixPairCleanerTrackLcMe[] = "TrackLcCleaner/ME/"; +constexpr char PrefixPairCleanerTrackResonanceSe[] = "TrackResonanceCleaner/SE/"; +constexpr char PrefixPairCleanerTrackResonanceMe[] = "TrackResonanceCleaner/ME/"; +constexpr char PrefixPairCleanerTrackTwoPhotonResonanceSe[] = "TrackTwoPhotonResonanceCleaner/SE/"; +constexpr char PrefixPairCleanerTrackTwoPhotonResonanceMe[] = "TrackTwoPhotonResonanceCleaner/ME/"; +constexpr char PrefixPairCleanerV0ResonanceSe[] = "V0ResonanceCleaner/SE/"; +constexpr char PrefixPairCleanerV0ResonanceMe[] = "V0ResonanceCleaner/ME/"; +constexpr char PrefixPairCleanerTrackKinkSe[] = "TrackKinkCleaner/SE/"; +constexpr char PrefixPairCleanerTrackKinkMe[] = "TrackKinkCleaner/ME/"; +constexpr char PrefixPairCleanerTrackCascadeSe[] = "TrackCascadeCleaner/SE/"; +constexpr char PrefixPairCleanerTrackCascadeMe[] = "TrackCascadeCleaner/ME/"; +constexpr char PrefixPairCleanerMcParticleMcParticleSe[] = "McParticleMcParticleCleaner/SE/"; +constexpr char PrefixPairCleanerMcParticleMcParticleMe[] = "McParticleMcParticleCleaner/ME/"; + +template class BasePairCleaner { public: BasePairCleaner() = default; virtual ~BasePairCleaner() = default; - template - void init(T const& PairCuts) + // pairCuts is the pair/triplet histogram manager's own ConfPairCuts/ConfTripletCuts group, which + // carries this cleaner's own kinematicVariable Configurable (independent from CPR's and from + // whatever the pair/triplet histogram manager itself may be plotting elsewhere). + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& specs, + T2 const& pairCuts) // pair cuts configurable is defined in pairHistManager.h { if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { - mMixPairsWithCommonAncestor = PairCuts.mixOnlyCommonAncestor.value; - mMixPairsWithNonCommonAncestor = PairCuts.mixOnlyNonCommonAncestor.value; + mMixPairsWithCommonAncestor = pairCuts.mixOnlyCommonAncestor.value; + mMixPairsWithNonCommonAncestor = pairCuts.mixOnlyNonCommonAncestor.value; + mUseMotherAsAncestor = pairCuts.useMotherAsAncestor.value; if (mMixPairsWithCommonAncestor && mMixPairsWithNonCommonAncestor) { LOG(fatal) << "Both mixing with common and non-common ancestor is activated. Breaking..."; } } + + mHistogramRegistry = registry; + int nStatBins = static_cast(kPairCleanerStatsBinLast); + const o2::framework::AxisSpec axisStats = {nStatBins, -0.5, nStatBins - 0.5}; + mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kStats, HistTable), getHistDesc(kStats, HistTable), getHistType(kStats, HistTable), {axisStats}); + mHistogramRegistry->get(HIST(prefix) + HIST(histmanager::getHistName(kStats, HistTable)))->GetXaxis()->SetBinLabel(1, "All analyzed reco pairs"); + mHistogramRegistry->get(HIST(prefix) + HIST(histmanager::getHistName(kStats, HistTable)))->GetXaxis()->SetBinLabel(2, "All blocked reco pairs"); + mKinematicVariable = static_cast(pairCuts.kinematicVariable.value); + std::string kinematicDesc = histmanager::replaceFirstAxisTitle(getHistDesc(kKinematic, HistTable), modes::kinematicVariableToAxisTitle(mKinematicVariable)); + mHistogramRegistry->add(std::string(prefix) + getHistNameV2(kKinematic, HistTable), kinematicDesc.c_str(), getHistType(kKinematic, HistTable), {specs.at(kKinematic)}); } protected: template - bool isCleanTrackPair(T1 const& track1, T2 const& track2) const + bool isCleanParticlePair(T1 const& particle1, T2 const& particle2) const + { + return particle1.globalIndex() != particle2.globalIndex(); + }; + + // const: only mutates *mHistogramRegistry through the stored pointer, not the pointer itself, + // so it can be called from the const isCleanPair() overloads below. + void fillAll() const + { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kStats, HistTable)), kAllAnalyzed); + } + + void fillBlocked(float kinematic) const + { + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kStats, HistTable)), kAllBlocked); + mHistogramRegistry->fill(HIST(prefix) + HIST(getHistName(kKinematic, HistTable)), kinematic); + } + + // mc only + // ancestry is checked either with the partonic mother or with the first ancestor (i.e. the direct mother), depending on mUseMotherAsAncestor + template + bool mcPairHasCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& partonicMothers) const { - return track1.globalIndex() != track2.globalIndex(); + if (mUseMotherAsAncestor) { + return this->mcPairHasCommonMother(particle1, particle2); + } + return this->mcPairHasCommonPartonicMother(particle1, particle2, partonicMothers); }; template - bool pairHasCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*partonicMothers*/) const + bool mcPairHasNonCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& partonicMothers) const { - // if one of the two particles has no associated partonic mother, we cannot know if they have a common anchestor, so we break out with false + if (mUseMotherAsAncestor) { + // if one of the two particles has no associated mother, we cannot know if they have a common ancestor, so we break out with false + if (!particle1.has_fMcMother() || !particle2.has_fMcMother()) { + return false; + } + return !this->mcPairHasCommonMother(particle1, particle2); + } + // if one of the two particles has no associated partonic mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcPartMoth() || !particle2.has_fMcPartMoth()) { return false; } + return !this->mcPairHasCommonPartonicMother(particle1, particle2, partonicMothers); + }; + + // reco + mc + template + bool pairHasCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*mcparticles*/, T4 const& partonicMothers) const + { + // if one of the two particles has no associated mc particle, we cannot know if they have a common ancestor, so we break out with false + if (!particle1.has_fMcParticle() || !particle2.has_fMcParticle()) { + return false; + } + // get mc particles - auto partonicMother1 = particle1.template fMcPartMoth_as(); - auto partonicMother2 = particle2.template fMcPartMoth_as(); - // get partonic mothers - return partonicMother1.globalIndex() == partonicMother2.globalIndex(); + auto mcParticle1 = particle1.template fMcParticle_as(); + auto mcParticle2 = particle2.template fMcParticle_as(); + + return this->mcPairHasCommonAncestor(mcParticle1, mcParticle2, partonicMothers); + }; + + template + bool pairHasNonCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*mcparticles*/, T4 const& partonicMothers) const + { + // if one of the two particles has no associated mc particle, we cannot know if they have a common ancestor, so we break out with false + if (!particle1.has_fMcParticle() || !particle2.has_fMcParticle()) { + return false; + } + + // get mc particles + auto mcParticle1 = particle1.template fMcParticle_as(); + auto mcParticle2 = particle2.template fMcParticle_as(); + + return this->mcPairHasNonCommonAncestor(mcParticle1, mcParticle2, partonicMothers); }; + bool mMixPairsWithCommonAncestor = false; + bool mMixPairsWithNonCommonAncestor = false; + modes::KinematicVariable mKinematicVariable = modes::KinematicVariable::kKstar; + + private: + // require both particles to originate from the same partonic mother template - bool pairHasNonCommonAncestor(T1 const& particle1, T2 const& particle2, T3 const& /*partonicMothers*/) const + bool mcPairHasCommonPartonicMother(T1 const& particle1, T2 const& particle2, T3 const& /*partonicMothers*/) const { - // if one of the two particles has no associated partonic mother, we cannot now if they have a non-common anchestor, so we break out with false + // if one of the two particles has no associated partonic mother, we cannot know if they have a common ancestor, so we break out with false if (!particle1.has_fMcPartMoth() || !particle2.has_fMcPartMoth()) { return false; } - // get mc particles + + // get partonic mothers auto partonicMother1 = particle1.template fMcPartMoth_as(); auto partonicMother2 = particle2.template fMcPartMoth_as(); - // get partonic mothers - return partonicMother1.globalIndex() != partonicMother2.globalIndex(); + + return partonicMother1.globalIndex() == partonicMother2.globalIndex(); }; - bool mMixPairsWithCommonAncestor = false; - bool mMixPairsWithNonCommonAncestor = false; + // require both particles to have the same first ancestor, i.e. the same direct mother + // there is exactly one row in the mother table per generated mother, so comparing the indices is sufficient + template + bool mcPairHasCommonMother(T1 const& particle1, T2 const& particle2) const + { + // if one of the two particles has no associated mother, we cannot know if they have a common ancestor, so we break out with false + if (!particle1.has_fMcMother() || !particle2.has_fMcMother()) { + return false; + } + + return particle1.fMcMotherId() == particle2.fMcMotherId(); + }; + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + bool mUseMotherAsAncestor = false; }; -class TrackTrackPairCleaner : public BasePairCleaner +template +class TrackTrackPairCleaner : public BasePairCleaner { public: TrackTrackPairCleaner() = default; - template - bool isCleanPair(T1 const& track1, T2 const& track2, T3 const& /*trackTable*/) const + template + bool isCleanPair(T1 const& track1, T2 const& track2, T3 const& /*trackTable*/, T4 const& pairHistManager) const { - return this->isCleanTrackPair(track1, track2); + this->fillAll(); + bool isClean = this->isCleanParticlePair(track1, track2); + if (!isClean) { + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; } - template - bool isCleanPair(T1 const& track1, T2 const& track2, T3 const& trackTable, T4 const& partonicMothers) const + template + bool isCleanPair(T1 const& track1, T2 const& track2, T3 const& trackTable, T4 const& mcParticles, T5 const& partonicMothers, T6 const& pairHistManager) const { - if (!this->isCleanPair(track1, track2, trackTable)) { + if (!this->isCleanPair(track1, track2, trackTable, pairHistManager)) { return false; } // pair is clean // no check if we require common or non-common ancestry - if (mMixPairsWithCommonAncestor) { - return this->pairHasCommonAncestor(track1, track2, partonicMothers); + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, track2, mcParticles, partonicMothers); } - if (mMixPairsWithNonCommonAncestor) { - return this->pairHasNonCommonAncestor(track1, track2, partonicMothers); + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, track2, mcParticles, partonicMothers); } return true; } }; -class V0V0PairCleaner : public BasePairCleaner +template +class V0V0PairCleaner : public BasePairCleaner // also works for particles decaying into a positive and negative daughter, like resonances { public: V0V0PairCleaner() = default; - template - bool isCleanPair(const T1& v01, const T2& v02, const T3& trackTable) const + + template + bool isCleanPair(T1 const& v01, T2 const& v02, T3 const& trackTable, T4 const& pairHistManager) const { + this->fillAll(); auto posDaughter1 = trackTable.rawIteratorAt(v01.posDauId() - trackTable.offset()); auto negDaughter1 = trackTable.rawIteratorAt(v01.negDauId() - trackTable.offset()); auto posDaughter2 = trackTable.rawIteratorAt(v02.posDauId() - trackTable.offset()); auto negDaughter2 = trackTable.rawIteratorAt(v02.negDauId() - trackTable.offset()); - return this->isCleanTrackPair(posDaughter1, posDaughter2) && this->isCleanTrackPair(negDaughter1, negDaughter2); + bool isClean = this->isCleanParticlePair(v01, v02) && + this->isCleanParticlePair(posDaughter1, posDaughter2) && this->isCleanParticlePair(negDaughter1, negDaughter2) && + this->isCleanParticlePair(posDaughter1, negDaughter2) && this->isCleanParticlePair(negDaughter1, posDaughter2); + if (!isClean) { + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; } - template - bool isCleanPair(T1 const& v01, T2 const& v02, T3 const& trackTable, T4 const& partonicMothers) const + template + bool isCleanPair(T1 const& v01, T2 const& v02, T3 const& trackTable, T4 const& mcParticles, T5 const& partonicMothers, T6 const& pairHistManager) const { - if (!this->isCleanPair(v01, v02, trackTable)) { + if (!this->isCleanPair(v01, v02, trackTable, pairHistManager)) { return false; } // pair is clean // no check if we require common or non-common ancestry - if (mMixPairsWithCommonAncestor) { - return this->pairHasCommonAncestor(v01, v02, partonicMothers); + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(v01, v02, mcParticles, partonicMothers); } - if (mMixPairsWithNonCommonAncestor) { - return this->pairHasNonCommonAncestor(v01, v02, partonicMothers); + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(v01, v02, mcParticles, partonicMothers); } return true; } }; -class TrackV0PairCleaner : public BasePairCleaner // also works for particles decaying into a positive and negative daughter, like resonances +template +class TrackV0PairCleaner : public BasePairCleaner // also works for particles decaying into a positive and negative daughter, like resonances { public: TrackV0PairCleaner() = default; - template - bool isCleanPair(const T1& track, const T2& v0, const T3& trackTable) const + + template + bool isCleanPair(T1 const& track, T2 const& v0, T3 const& trackTable, T4 const& pairHistManager) const { + this->fillAll(); auto posDaughter = trackTable.rawIteratorAt(v0.posDauId() - trackTable.offset()); auto negDaughter = trackTable.rawIteratorAt(v0.negDauId() - trackTable.offset()); - return (this->isCleanTrackPair(posDaughter, track) && this->isCleanTrackPair(negDaughter, track)); + bool isClean = this->isCleanParticlePair(posDaughter, track) && this->isCleanParticlePair(negDaughter, track); + if (!isClean) { + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; } - template - bool isCleanPair(T1 const& track1, T2 const& v0, T3 const& trackTable, T4 const& partonicMothers) const + template + bool isCleanPair(T1 const& track1, T2 const& v0, T3 const& trackTable, T4 const& mcParticles, T5 const& partonicMothers, T6 const& pairHistManager) const { - if (!this->isCleanPair(track1, v0, trackTable)) { + if (!this->isCleanPair(track1, v0, trackTable, pairHistManager)) { return false; } // pair is clean // now check if we require common or non-common ancestry - if (mMixPairsWithCommonAncestor) { - return this->pairHasCommonAncestor(track1, v0, partonicMothers); + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, v0, mcParticles, partonicMothers); } - if (mMixPairsWithNonCommonAncestor) { - return this->pairHasNonCommonAncestor(track1, v0, partonicMothers); + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, v0, mcParticles, partonicMothers); } return true; } }; -class TrackKinkPairCleaner : public BasePairCleaner +template +class TrackKinkPairCleaner : public BasePairCleaner { public: TrackKinkPairCleaner() = default; - template - bool isCleanPair(const T1& track, const T2& kink, const T3& trackTable) const + + template + bool isCleanPair(T1 const& track, T2 const& kink, T3 const& trackTable, T4 const& pairHistManager) const { + this->fillAll(); auto chaDaughter = trackTable.rawIteratorAt(kink.chaDauId() - trackTable.offset()); - return this->isCleanTrackPair(chaDaughter, track); + bool isClean = this->isCleanParticlePair(chaDaughter, track); + if (!isClean) { + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; } - template - bool isCleanPair(T1 const& track1, T2 const& kink, T3 const& trackTable, T4 const& partonicMothers) const + template + bool isCleanPair(T1 const& track1, T2 const& kink, T3 const& trackTable, T4 const& mcParticles, T5 const& partonicMothers, T6 const& pairHistManager) const { - if (!this->isCleanPair(track1, kink, trackTable)) { + if (!this->isCleanPair(track1, kink, trackTable, pairHistManager)) { return false; } // pair is clean // now check if we require common or non-common ancestry - if (mMixPairsWithCommonAncestor) { - return this->pairHasCommonAncestor(track1, kink, partonicMothers); + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, kink, mcParticles, partonicMothers); } - if (mMixPairsWithNonCommonAncestor) { - return this->pairHasNonCommonAncestor(track1, kink, partonicMothers); + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, kink, mcParticles, partonicMothers); } return true; } }; -class TrackCascadePairCleaner : public BasePairCleaner +template +class TrackCascadePairCleaner : public BasePairCleaner { public: TrackCascadePairCleaner() = default; - template - bool isCleanPair(const T1& track, const T2& cascade, const T3& trackTable) const + + template + bool isCleanPair(T1 const& track, T2 const& cascade, T3 const& trackTable, T4 const& pairHistManager) const { + this->fillAll(); auto bachelor = trackTable.rawIteratorAt(cascade.bachelorId() - trackTable.offset()); auto posDaughter = trackTable.rawIteratorAt(cascade.posDauId() - trackTable.offset()); auto negDaughter = trackTable.rawIteratorAt(cascade.negDauId() - trackTable.offset()); - return (this->isCleanTrackPair(bachelor, track) && this->isCleanTrackPair(posDaughter, track) && this->isCleanTrackPair(negDaughter, track)); + bool isClean = this->isCleanParticlePair(bachelor, track) && this->isCleanParticlePair(posDaughter, track) && this->isCleanParticlePair(negDaughter, track); + if (!isClean) { + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; + } + + template + bool isCleanPair(T1 const& track1, T2 const& cascade, T3 const& trackTable, T4 const& mcParticles, T5 const& partonicMothers, T6 const& pairHistManager) const + { + if (!this->isCleanPair(track1, cascade, trackTable, pairHistManager)) { + return false; + } + // pair is clean + // now check if we require common or non-common ancestry + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, cascade, mcParticles, partonicMothers); + } + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, cascade, mcParticles, partonicMothers); + } + return true; } +}; + +template +class TrackLcPairCleaner : public BasePairCleaner +{ + public: + TrackLcPairCleaner() = default; template - bool isCleanPair(T1 const& track1, T2 const& cascade, T3 const& trackTable, T4 const& partonicMothers) const + bool isCleanPair(T1 const& track, T2 const& lc, T3 const& trackTable, T4 const& pairHistManager) const + { + this->fillAll(); + auto prong0 = trackTable.rawIteratorAt(lc.prong0DauId() - trackTable.offset()); + auto prong1 = trackTable.rawIteratorAt(lc.prong1DauId() - trackTable.offset()); + auto prong2 = trackTable.rawIteratorAt(lc.prong2DauId() - trackTable.offset()); + bool isClean = this->isCleanParticlePair(prong0, track) && this->isCleanParticlePair(prong1, track) && this->isCleanParticlePair(prong2, track); + if (!isClean) { + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; + } + + template + bool isCleanPair(T1 const& track1, T2 const& lc, T3 const& trackTable, T4 const& mcParticles, T5 const& partonicMothers, T6 const& pairHistManager) const { - if (!this->isCleanPair(track1, cascade, trackTable)) { + if (!this->isCleanPair(track1, lc, trackTable, pairHistManager)) { return false; } // pair is clean // now check if we require common or non-common ancestry - if (mMixPairsWithCommonAncestor) { - return this->pairHasCommonAncestor(track1, cascade, partonicMothers); + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, lc, mcParticles, partonicMothers); + } + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, lc, mcParticles, partonicMothers); + } + return true; + } +}; + +template +class McParticleMcParticlePairCleaner : public BasePairCleaner +{ + public: + McParticleMcParticlePairCleaner() = default; + + template + bool isCleanPair(T1 const& particle1, T2 const& particle2, T3 const& pairHistManager) const + { + this->fillAll(); + bool isClean = this->isCleanParticlePair(particle1, particle2); + if (!isClean) { + this->fillBlocked(pairHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; + } + + template + bool isCleanPair(T1 const& particle1, T2 const& particle2, T3 const& partonicMothers, T4 const& pairHistManager) const + { + if (!this->isCleanPair(particle1, particle2, pairHistManager)) { + return false; } - if (mMixPairsWithNonCommonAncestor) { - return this->pairHasNonCommonAncestor(track1, cascade, partonicMothers); + // pair is clean + // no check if we require common or non-common ancestry + if (this->mMixPairsWithCommonAncestor) { + return this->mcPairHasCommonAncestor(particle1, particle2, partonicMothers); + } + if (this->mMixPairsWithNonCommonAncestor) { + return this->mcPairHasNonCommonAncestor(particle1, particle2, partonicMothers); } return true; } }; -} // namespace paircleaner -} // namespace o2::analysis::femto + +} // namespace o2::analysis::femto::paircleaner #endif // PWGCF_FEMTO_CORE_PAIRCLEANER_H_ diff --git a/PWGCF/Femto/Core/pairHistManager.h b/PWGCF/Femto/Core/pairHistManager.h index aebd65579ea..b6bb3ca1942 100644 --- a/PWGCF/Femto/Core/pairHistManager.h +++ b/PWGCF/Femto/Core/pairHistManager.h @@ -11,36 +11,44 @@ /// \file pairHistManager.h /// \brief histogram manager for pair tasks -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ +#include "PWGCF/Femto/Core/femtoMath.h" +#include "PWGCF/Femto/Core/femtoSpherHarMath.h" #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/histManager.h" #include "PWGCF/Femto/Core/modes.h" +#include "Common/Core/RecoDecay.h" + +#include #include #include #include #include -#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include #include #include +#include +#include #include #include +#include #include #include +#include #include #include -namespace o2::analysis::femto -{ -namespace pairhistmanager +namespace o2::analysis::femto::pairhistmanager { // enum for pair histograms enum PairHist { @@ -61,6 +69,8 @@ enum PairHist { kKstarVsMt, kKstarVsMult, kKstarVsCent, + // 3D: k* vs kT vs centrality + kKstarVsKtVsCent, // 2D with mass kKstarVsMass1, kKstarVsMass2, @@ -88,22 +98,81 @@ enum PairHist { kKstarVsMtVsMinvVsPt1VsPt2, kKstarVsMtVsMinvVsPt1VsPt2VsMult, kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, + // higher dimensions with kt, invariant mass and pt + kKstarVsMinvVsKtVsMult, + kKstarVsMtVsMinvVsKtVsMult, + kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult, + kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, // dalitz plots kDalitz, // between a track and pos/neg daughter of another particle - // mc + // angles + kAlpha, // angle between pT1 and pT2 of the paritcles in the lab frame + kAlphaVsKstar, + kAlphaVsKt, + kAlphaVsMt, + kBetaLab, // angle between kT and kstar in lab frame + kBetaLabVsKstar, + kBetaLabVsKt, + kBetaLabVsMt, + kKstarVsAlphaVsMtVsMult, + kKstarVsAlphaVsMtVsPt1VsPt2VsMult, + kKstarVsBetaLabVsMtVsMult, + kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, + // reco-vs-mc-truth correlation (requires BOTH a reco pair and matched mc info) kTrueKstarVsKstar, kTrueKtVsKt, kTrueMtVsMt, kTrueMinvVsMinv, kTrueMultVsMult, kTrueCentVsCent, + // pure mc-truth pair (no reco counterpart, kMc without kReco) + kTrueKstar, + kTrueKt, + kTrueMt, + kTrueMinv1D, + kTruePt1VsPt2, + kTruePt1VsKstar, + kTruePt2VsKstar, + kTruePt1VsKt, + kTruePt2VsKt, + kTruePt1VsMt, + kTruePt2VsMt, + kTrueKstarVsKt, + kTrueKstarVsMt, + kTrueKstarVsMult, + kTrueKstarVsCent, + kTrueDeltaEtaDeltaPhi, + kTrueQout, + kTrueQside, + kTrueQlong, + kTrueQoutQsideQlong, + kTrueKstarVsMtVsMult, + kTrueKstarVsMtVsMultVsCent, + kTrueKstarVsMtVsPt1VsPt2, + kTrueKstarVsMtVsPt1VsPt2VsMult, + kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, + kTrueQoutVsQout, + kTrueQsideVsQside, + kTrueQlongVsQlong, // mixing qa kSeNpart1VsNpart2, // number of unique particles 1 vs unique number of particles 2 in each same event - kMeMixingWindow, // mixing window size + kMeMixingWindowRaw, // mixing window size + kMeMixingWindowEffective, // mixing window size, counting event pairs with particle pairs kMeNpart1VsNpart2, // number of unique particles 1 vs number of unique particles 2 in each mixed event kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, // correlation of event properties in each mixing bin + // angular + kDeltaEtaDeltaPhi, + // Bertsch-Pratt 3D decomposition in LCMS + kQout, + kQside, + kQlong, + kQoutVsQsideVsQlong, + + // event shape enginerring + kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector, + kPairHistogramLast }; @@ -111,6 +180,7 @@ enum MixingPolicy { kVtxMult, kVtxCent, kVtxMultCent, + kVtxCentEventPlaneAngle, kMixingPolicyLast }; @@ -120,8 +190,10 @@ struct ConfMixing : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis multBins{"multBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f, 64.0f, 68.0f, 72.0f, 76.0f, 80.0f, 84.0f, 88.0f, 92.0f, 96.0f, 100.0f, 200.0f}, "Mixing bins - multiplicity"}; o2::framework::ConfigurableAxis centBins{"centBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "Mixing bins - centrality"}; o2::framework::ConfigurableAxis vtxBins{"vtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + o2::framework::ConfigurableAxis eventPlaneAngle{"eventPlaneAngle", {10, 0.f, 1.f * o2::constants::math::TwoPI}, "Mixing bins - event plane angle"}; o2::framework::Configurable depth{"depth", 5, "Number of events for mixing"}; - o2::framework::Configurable policy{"policy", 0, "Binning policy for mixing (alywas in combination with z-vertex) -> 0: multiplicity, -> 1: centrality, -> 2: both"}; + o2::framework::Configurable policy{"policy", 0, "Binning policy for mixing (alywas in combination with z-vertex) -> 0: multiplicity, -> 1: centrality, -> 2: multiplicity and centrality, -> 3: centrality and event plane angle"}; + o2::framework::Configurable eventStride{"eventStride", 1, "Only use every Nth collision as a mixing anchor (1 = use every collision). Reduces mixed-event combinatorics roughly 1/eventStride."}; o2::framework::Configurable sameSpecies{"sameSpecies", false, "Enable if particle 1 and particle 2 are the same"}; o2::framework::Configurable seed{"seed", -1, "Seed to randomize particle 1 and particle 2 (if they are identical). Set to negative value to deactivate. Set to 0 to generate unique seed in time."}; o2::framework::Configurable enablePairCorrelationQa{"enablePairCorrelationQa", true, "Enable pair-level correlation QA (same-event + mixed-event)"}; @@ -131,24 +203,38 @@ struct ConfMixing : o2::framework::ConfigurableGroup { struct ConfPairBinning : o2::framework::ConfigurableGroup { std::string prefix = std::string("PairBinning"); - o2::framework::Configurable usePdgMass{"usePdgMass", true, "Use PDF masses for 4-vectors. If false, use reconstructed mass (if available)"}; - o2::framework::Configurable plot1D{"plot1D", true, "Enable 1D histograms"}; - o2::framework::Configurable plot2D{"plot2D", true, "Enable 2D histograms"}; - o2::framework::Configurable plotKstarVsMtVsMult{"plotKstarVsMtVsMult", false, "Enable 3D histogram (Kstar Vs Mt Vs Mult)"}; - o2::framework::Configurable plotKstarVsMtVsMultVsCent{"plotKstarVsMtVsMultVsCent", false, "Enable 4D histogram (Kstar Vs Mt Vs Mult Vs Cent)"}; - o2::framework::Configurable plotKstarVsMtVsPt1VsPt2{"plotKstarVsMtVsPt1VsPt2", false, "Enable 4D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; - o2::framework::Configurable plotKstarVsMtVsPt1VsPt2VsMult{"plotKstarVsMtVsPt1VsPt2VsMult", false, "Enable 5D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; - o2::framework::Configurable plotKstarVsMtVsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsPt1VsPt2VsMultVsCent", false, "Enable 6D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mult Vs Cent)"}; - o2::framework::Configurable plotKstarVsMtVsMass1VsMass2{"plotKstarVsMtVsMass1VsMass2", false, "Enable 4D histogram (Kstar Vs Mt Vs Mass1 Vs Mass2)"}; - o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsMult{"plotKstarVsMtVsMass1VsMass2VsMult", false, "Enable 5D histogram (Kstar Vs Mt Vs Mass1 Vs Mass2 Vs Mult)"}; - o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsMultVsCent{"plotKstarVsMtVsMass1VsMass2VsMultVsCent", false, "Enable 6D histogram (Kstar Vs Mt Vs Mass1 Vs Mass2 Vs Mult Vs Cent)"}; - o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsPt1VsPt2{"plotKstarVsMtVsMass1VsMass2VsPt1VsPt2", false, "Enable 6D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mass1 Vs Mass2)"}; - o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult{"plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult", false, "Enable 7D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mass1 Vs Mass2 Vs Mult)"}; - o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent", false, "Enable 8D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mass1 Vs Mass2 Vs Mult Vs Cent)"}; - o2::framework::Configurable plotKstarVsMtVsMinv1VsPt1VsPt2{"plotKstarVsMtVsMinv1VsPt1VsPt2", false, "Enable 5D histogram (Kstar Vs Mt Vs Minv Vs Pt1 Vs Pt2)"}; - o2::framework::Configurable plotKstarVsMtVsMinv1VsPt1VsPt2VsMult{"plotKstarVsMtVsMinv1VsPt1VsPt2VsMult", false, "Enable 6D histogram (Kstar Vs Mt Vs Minv Vs Pt1 Vs Pt2 Vs Mult)"}; - o2::framework::Configurable plotKstarVsMtVsMinv1VsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsMinv1VsPt1VsPt2VsMultVsCent", false, "Enable 7D histogram (Kstar Vs Mt Vs Minv Vs Pt1 Vs Pt2 Vs Mult Vs Cent)"}; - o2::framework::Configurable plotDalitz{"plotDalitz", false, "Enable dalitz plot"}; + o2::framework::Configurable usePdgMass{"usePdgMass", true, "(Reco) Use PDF masses for 4-vectors. If false, use reconstructed mass (if available). Not consulted for pure mc-truth pairs, which always use PDG mass"}; + o2::framework::Configurable plot1D{"plot1D", true, "(Reco/Mc) Enable 1D histograms"}; + o2::framework::Configurable plot2D{"plot2D", true, "(Reco/Mc) Enable 2D histograms"}; + o2::framework::Configurable plotKstarVsKtVsCent{"plotKstarVsKtVsCent", false, "(Reco/Mc) Enable 3D histogram (Kstar Vs Kt Vs Cent)"}; + o2::framework::Configurable plotKstarVsMtVsMult{"plotKstarVsMtVsMult", false, "(Reco/Mc) Enable 3D histogram (Kstar Vs Mt Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsMultVsCent{"plotKstarVsMtVsMultVsCent", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs Mt Vs Mult Vs Cent)"}; + o2::framework::Configurable plotKstarVsMtVsPt1VsPt2{"plotKstarVsMtVsPt1VsPt2", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2)"}; + o2::framework::Configurable plotKstarVsMtVsPt1VsPt2VsMult{"plotKstarVsMtVsPt1VsPt2VsMult", false, "(Reco/Mc) Enable 5D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsPt1VsPt2VsMultVsCent", false, "(Reco/Mc) Enable 6D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mult Vs Cent)"}; + o2::framework::Configurable plotKstarVsMtVsMass1VsMass2{"plotKstarVsMtVsMass1VsMass2", false, "(Reco) Enable 4D histogram (Kstar Vs Mt Vs Mass1 Vs Mass2)"}; + o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsMult{"plotKstarVsMtVsMass1VsMass2VsMult", false, "(Reco) Enable 5D histogram (Kstar Vs Mt Vs Mass1 Vs Mass2 Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsMultVsCent{"plotKstarVsMtVsMass1VsMass2VsMultVsCent", false, "(Reco) Enable 6D histogram (Kstar Vs Mt Vs Mass1 Vs Mass2 Vs Mult Vs Cent)"}; + o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsPt1VsPt2{"plotKstarVsMtVsMass1VsMass2VsPt1VsPt2", false, "(Reco) Enable 6D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mass1 Vs Mass2)"}; + o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult{"plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult", false, "(Reco) Enable 7D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mass1 Vs Mass2 Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent", false, "(Reco) Enable 8D histogram (Kstar Vs Mt Vs Pt1 Vs Pt2 Vs Mass1 Vs Mass2 Vs Mult Vs Cent)"}; + o2::framework::Configurable plotKstarVsMtVsMinv1VsPt1VsPt2{"plotKstarVsMtVsMinv1VsPt1VsPt2", false, "(Reco) Enable 5D histogram (Kstar Vs Mt Vs Minv Vs Pt1 Vs Pt2)"}; + o2::framework::Configurable plotKstarVsMtVsMinv1VsPt1VsPt2VsMult{"plotKstarVsMtVsMinv1VsPt1VsPt2VsMult", false, "(Reco) Enable 6D histogram (Kstar Vs Mt Vs Minv Vs Pt1 Vs Pt2 Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsMinv1VsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsMinv1VsPt1VsPt2VsMultVsCent", false, "(Reco) Enable 7D histogram (Kstar Vs Mt Vs Minv Vs Pt1 Vs Pt2 Vs Mult Vs Cent)"}; + o2::framework::Configurable plotKstarVsMinvVsKtVsMult{"plotKstarVsMinvVsKtVsMult", false, "(Reco) Enable 4D histogram (Kstar Vs Minv Vs Kt Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsMinvVsKtVsMult{"plotKstarVsMtVsMinvVsKtVsMult", false, "(Reco) Enable 5D histogram (Kstar Vs Mt Vs Minv Vs Kt Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult{"plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult", false, "(Reco) Enable 7D histogram (Kstar Vs Mt Vs Minv Vs Kt Vs Pt1 Vs Pt2 Vs Mult)"}; + o2::framework::Configurable plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent{"plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent", false, "(Reco) Enable 8D histogram (Kstar Vs Mt Vs Minv Vs Kt Vs Pt1 Vs Pt2 Vs Mult Vs Cent)"}; + o2::framework::Configurable plotDalitz{"plotDalitz", false, "(Reco) Enable dalitz plot. Not supported for pure mc-truth pairs (no trackTable/daughter structure)"}; + o2::framework::Configurable plotDeltaEtaDeltaPhi{"plotDeltaEtaDeltaPhi", false, "(Reco/Mc) Plot #Delta#phi vs #Delta#eta"}; + o2::framework::Configurable plotAlpha{"plotAlpha", false, "(Reco/Mc) Plot alpha (angle between pT1 and pT2) 1D distribution, and (if plot2D) vs kstar/kT/mT"}; + o2::framework::ConfigurableAxis alpha{"alpha", {{180, -o2::constants::math::PI, o2::constants::math::PI}}, "alpha (angle between pT1 and pT2)"}; + o2::framework::Configurable plotKstarVsAlphaVsMtVsMult{"plotKstarVsAlphaVsMtVsMult", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs Alpha Vs Mt Vs Mult)"}; + o2::framework::Configurable plotKstarVsAlphaVsMtVsPt1VsPt2VsMult{"plotKstarVsAlphaVsMtVsPt1VsPt2VsMult", false, "(Reco/Mc) Enable 6D histogram (Kstar Vs Alpha Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; + o2::framework::Configurable plotBetaLab{"plotBetaLab", false, "(Reco/Mc) Plot betaLab (angle between kT and k* in lab frame) 1D distribution, and (if plot2D) vs kstar/kT/mT"}; + o2::framework::ConfigurableAxis betaLab{"betaLab", {{90, 0.f, o2::constants::math::PI}}, "betaLab (angle between kT and k* in lab frame)"}; + o2::framework::Configurable plotKstarVsBetaLabVsMtVsMult{"plotKstarVsBetaLabVsMtVsMult", false, "(Reco/Mc) Enable 4D histogram (Kstar Vs BetaLab Vs Mt Vs Mult)"}; + o2::framework::Configurable plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult{"plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult", false, "(Reco/Mc) Enable 6D histogram (Kstar Vs BetaLab Vs Mt Vs Pt1 Vs Pt2 Vs Mult)"}; o2::framework::ConfigurableAxis kstar{"kstar", {{600, 0, 6}}, "kstar"}; o2::framework::ConfigurableAxis kt{"kt", {{600, 0, 6}}, "kt"}; o2::framework::ConfigurableAxis mt{"mt", {{500, 0.8, 5.8}}, "mt"}; @@ -158,11 +244,28 @@ struct ConfPairBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis pt2{"pt2", {{100, 0, 6}}, "Pt binning for particle 2"}; o2::framework::ConfigurableAxis mass1{"mass1", {{100, 0, 2}}, "Mass binning for particle 1 (if particle has mass getter, otherwise PDG mass)"}; o2::framework::ConfigurableAxis mass2{"mass2", {{100, 0, 2}}, "Mass binning for particle 2 (if particle has mass getter, otherwise PDG mass)"}; - o2::framework::ConfigurableAxis massInv{"massInv", {{100, 0, 2}}, "Invariant Mass binning"}; + o2::framework::ConfigurableAxis massInv{"massInv", {{100, 0, 5}}, "Invariant Mass binning"}; o2::framework::ConfigurableAxis dalitzMtot{"dalitzMtot", {{100, 0, 10}}, "Total invariant mass squared binning in darlitz plot"}; o2::framework::ConfigurableAxis dalitzM12{"dalitzM12", {{100, 0, 10}}, "Mass12 binning of darlitz plot"}; o2::framework::ConfigurableAxis dalitzM13{"dalitzM13", {{100, 0, 10}}, "Mass13 binning of darlitz plot"}; o2::framework::Configurable transverseMassType{"transverseMassType", static_cast(modes::TransverseMassType::kAveragePdgMass), "Type of transverse mass (0-> Average Pdg Mass, 1-> Reduced Pdg Mass, 2-> Mt from combined 4 vector)"}; + o2::framework::ConfigurableAxis binningDeltaEta{"binningDeltaEta", {{35, -1.6, 1.6}}, "Delta eta"}; + o2::framework::ConfigurableAxis binningDeltaPhi{"binningDeltaPhi", {{35, -o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}}, "Delta phi"}; + o2::framework::Configurable plotBertschPratt{"plotBertschPratt", false, "(Reco/Mc) Enable 1D projections and 3D (q_out, q_side, q_long) Bertsch-Pratt histograms in LCMS"}; + o2::framework::ConfigurableAxis qout{"qout", {{300, -1.5f, 1.5f}}, "q_{out} (GeV/c) in LCMS"}; + o2::framework::ConfigurableAxis qside{"qside", {{300, -1.5f, 1.5f}}, "q_{side} (GeV/c) in LCMS"}; + o2::framework::ConfigurableAxis qlong{"qlong", {{300, -1.5f, 1.5f}}, "q_{long} (GeV/c) in LCMS"}; + o2::framework::Configurable plotEventShape{"plotEventShape", false, "(Reco/Mc) Enable 7D (q_out, q_side, q_long, mt, centrality event plane angle, qvector) histogram"}; + o2::framework::ConfigurableAxis eventPlaneAngle{"eventPlaneAngle", {{10, 0.f, 1.f * o2::constants::math::TwoPI}}, "event plane angle"}; + o2::framework::ConfigurableAxis qvector{"qvector", {{o2::framework::VARIABLE_WIDTH, 0.50f, 68.50f, 100.50f, 126.50f, 151.50f, 176.50f, 203.50f, 232.50f, 269.50f, 322.50f, 833.50f}}, "qvector"}; + o2::framework::Configurable plotSH{"plotSH", false, "(Reco) Enable spherical-harmonics decomposition of the pair momentum-difference vector"}; + o2::framework::Configurable shLMax{"shLMax", 2, "Maximum l for SH decomposition (0..5). FemtoUniverse hard-codes 1."}; + o2::framework::Configurable shFrame{"shFrame", 1, "SH reference frame/variable: 0=LCMS non-identical (k*), 1=LCMS identical (qinv, FemtoUniverse default), 2=PRF (q_PRF, matches FemtoUniverse isIdenPRF=true)"}; + o2::framework::ConfigurableAxis shKstar{"shKstar", {{60, 0.0f, 0.3f}}, "k*/qinv binning for SH histograms"}; + o2::framework::Configurable shUseCent{"shUseCent", false, "SH: bin by centrality instead of multiplicity"}; + o2::framework::ConfigurableAxis shCentBins{"shCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 200.0f}, "SH: multiplicity/centrality bin edges (like FemtoUniverse confMultKstarBins)"}; + o2::framework::ConfigurableAxis shKtBins{"shKtBins", {o2::framework::VARIABLE_WIDTH, 0.1f, 0.2f, 0.3f, 0.4f}, "SH: kT bin edges (like FemtoUniverse confKtKstarBins)"}; + o2::framework::Configurable shPlot1D{"shPlot1D", false, "(SH) Also fill the 1D qinv/k* distribution (h1D) and the bin occupancy (BinCount) per (mult,kT) bin"}; }; struct ConfPairCuts : o2::framework::ConfigurableGroup { @@ -175,8 +278,10 @@ struct ConfPairCuts : o2::framework::ConfigurableGroup { o2::framework::Configurable mtMin{"mtMin", -1, "Minimal mt (set to -1 to deactivate)"}; o2::framework::Configurable massInvMin{"massInvMin", -1, "Minimal invariant mass (set to -1 to deactivate)"}; o2::framework::Configurable massInvMax{"massInvMax", -1, "Maximal invariant mass (set to -1 to deactivate)"}; - o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common anchestor (in the same event)"}; - o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common anchestor (in the same event)"}; + o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; + o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; + o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kKstar), "Type of kinematic variable (0->kstar, 1->kT, 2->mT) reported by the pair-cleaner kinematic plot"}; }; // the enum gives the correct index in the array @@ -190,7 +295,7 @@ constexpr std::array, kPairHistogramLast> {kMinv, o2::framework::HistType::kTH1F, "hMinv", "invariant mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, // 2D {kPt1VsPt2, o2::framework::HistType::kTH2F, "hPt1VsPt2", " p_{T,1} vs p_{T,2}; p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c})"}, - {kPt1VsKstar, o2::framework::HistType::kTH2F, "hPt1VsKstar", "p_{T,1} vs k*; p_{T,2} (GeV/#it{c}); k* (GeV/#it{c})"}, + {kPt1VsKstar, o2::framework::HistType::kTH2F, "hPt1VsKstar", "p_{T,1} vs k*; p_{T,1} (GeV/#it{c}); k* (GeV/#it{c})"}, {kPt2VsKstar, o2::framework::HistType::kTH2F, "hPt2VsKstar", "p_{T,2} vs k*; p_{T,2} (GeV/#it{c}); k* (GeV/#it{c})"}, {kPt1VsKt, o2::framework::HistType::kTH2F, "hPt1VsKt", "p_{T,1} vs k_{T}; p_{T,1} (GeV/#it{c}); k_{T} (GeV/#it{c})"}, {kPt2VsKt, o2::framework::HistType::kTH2F, "hPt2VsKt", "p_{T,2} vs k_{T}; p_{T,2} (GeV/#it{c}); k_{T} (GeV/#it{c})"}, @@ -198,8 +303,8 @@ constexpr std::array, kPairHistogramLast> {kPt2VsMt, o2::framework::HistType::kTH2F, "hPt2VsMt", "p_{T,2} vs m_{T}; p_{T,2} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2})"}, {kKstarVsKt, o2::framework::HistType::kTH2F, "hKstarVsKt", "k* vs k_{T}; k* (GeV/#it{c}); k_{T} (GeV/#it{c})"}, {kKstarVsMt, o2::framework::HistType::kTH2F, "hKstarVsMt", "k* vs m_{T}; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2})"}, - {kKstarVsCent, o2::framework::HistType::kTH2F, "hKstarVsCent", "k* vs Centrality (Mult. Percentile); k* (GeV/#it{c}); Centrality (%)"}, {kKstarVsMult, o2::framework::HistType::kTH2F, "hKstarVsMult", "k* vs Multiplicity; k* (GeV/#it{c}); Multiplicity"}, + {kKstarVsCent, o2::framework::HistType::kTH2F, "hKstarVsCent", "k* vs Centrality (Mult. Percentile); k* (GeV/#it{c}); Centrality (%)"}, // 2D with mass {kKstarVsMass1, o2::framework::HistType::kTH2F, "hKstarVsMass1", "k* vs m_{1}; k* (GeV/#it{c}); m_{1} (GeV/#it{c}^{2})"}, {kKstarVsMass2, o2::framework::HistType::kTH2F, "hKstarVsMass2", "k* vs m_{2}; k* (GeV/#it{c}); m_{2} (GeV/#it{c}^{2})"}, @@ -208,6 +313,7 @@ constexpr std::array, kPairHistogramLast> {kPt1VsMinv, o2::framework::HistType::kTH2F, "hPt1VsMinv", "p_{T,1} vs m_{Inv}; p_{T,1} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, {kPt2VsMinv, o2::framework::HistType::kTH2F, "hPt2VsMinv", "p_{T,2} vs m_{Inv}; p_{T,2} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, // n-D + {kKstarVsKtVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsKtVsCent", "k* vs k_{T} vs centrality; k* (GeV/#it{c}); k_{T} (GeV/#it{c}); Centrality (%);"}, {kKstarVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMult", "k* vs m_{T} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); Multiplicity;"}, {kKstarVsMtVsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMultVsCent", "k* vs m_{T} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); Multiplicity; Centrality (%);"}, // n-D with pt @@ -215,96 +321,234 @@ constexpr std::array, kPairHistogramLast> {kKstarVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsPt1VsPt2VsMult", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, {kKstarVsMtVsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity; Centrality;"}, // n-D with mass - {kKstarVsMtVsMass1VsMass2, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2", "k* vs m_{T} vs m_{1} vs m_{2}; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2});"}, - {kKstarVsMtVsMass1VsMass2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsMult", "k* vs m_{T} vs m_{1} vs m_{2} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); Multiplicity;"}, - {kKstarVsMtVsMass1VsMass2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsMultVsCent", "k* vs m_{T} vs m_{1} vs m_{2} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); Multiplicity; Centrality (%);"}, + {kKstarVsMtVsMass1VsMass2, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2", "k* vs m_{T} vs m_{1} vs m_{2}; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{2} (GeV/#it{c}^{2});"}, + {kKstarVsMtVsMass1VsMass2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsMult", "k* vs m_{T} vs m_{1} vs m_{2} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{2} (GeV/#it{c}^{2}); Multiplicity;"}, + {kKstarVsMtVsMass1VsMass2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsMultVsCent", "k* vs m_{T} vs m_{1} vs m_{2} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{2} (GeV/#it{c}^{2}); Multiplicity; Centrality (%);"}, // n-D with pt and mass - {kKstarVsMtVsMass1VsMass2VsPt1VsPt2, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsPt1VsPt2", "k* vs m_{T} vs m_{1} vs m_{2} vs p_{T,1} vs p_{T,2}; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c});"}, - {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult", "k* vs m_{T} vs m_{1} vs m_{2} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, - {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs m_{1} vs m_{2} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity; Centrality (%);"}, + {kKstarVsMtVsMass1VsMass2VsPt1VsPt2, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsPt1VsPt2", "k* vs m_{T} vs m_{1} vs m_{2} vs p_{T,1} vs p_{T,2}; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{2} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c});"}, + {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult", "k* vs m_{T} vs m_{1} vs m_{2} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{2} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, + {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs m_{1} vs m_{2} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{1} (GeV/#it{c}^{2}); m_{2} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity; Centrality (%);"}, {kKstarVsMtVsMinvVsPt1VsPt2, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMinvVsPt1VsPt2", "k* vs m_{T} vs m_{Inv} vs p_{T,1} vs p_{T,2}; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{Inv} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c})"}, {kKstarVsMtVsMinvVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMinvVsPt1VsPt2VsMult", "k* vs m_{T} vs m_{Inv} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{Inv} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity"}, {kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs m_{Inv} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{Inv} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity; Centrality (%)"}, + {kKstarVsMinvVsKtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMinvVsKtVsMult", "k* vs m_{Inv} vs k_{T} vs multiplicity; k* (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2}); k_{T} (GeV/#it{c}); Multiplicity"}, + {kKstarVsMtVsMinvVsKtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMinvVsKtVsMult", "k* vs m_{T} vs m_{Inv} vs k_{T} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{Inv} (GeV/#it{c}^{2}); k_{T} (GeV/#it{c}); Multiplicity"}, + {kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult", "k* vs m_{T} vs m_{Inv} vs k_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{Inv} (GeV/#it{c}^{2}); k_{T} (GeV/#it{c}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity"}, + {kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs m_{Inv} vs k_{T} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); m_{Inv} (GeV/#it{c}^{2}); k_{T} (GeV/#it{c}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity; Centrality (%)"}, {kDalitz, o2::framework::HistType::kTHnSparseF, "hDalitz", "Dalitz plot; k* (GeV/#it{c}); m^{2}_{123} (GeV/#it{c}^{2})^{2}; m^{2}_{12} (GeV/#it{c}^{2})^{2}; m^{2}_{13} (GeV/#it{c}^{2})^{2};"}, + // reco-vs-mc-truth correlation {kTrueKstarVsKstar, o2::framework::HistType::kTH2F, "hTrueKstarVsKstar", "k*_{True} vs k*; k*_{True} (GeV/#it{c}); k* (GeV/#it{c})"}, {kTrueKtVsKt, o2::framework::HistType::kTH2F, "hTrueKtVsKt", "k_{T,True} vs k_{T}; k_{T,True} (GeV/#it{c}); k_{T} (GeV/#it{c})"}, {kTrueMtVsMt, o2::framework::HistType::kTH2F, "hTrueMtVsMt", "m_{T,True} vs m_{T}; m_{T,True} (GeV/#it{c}^{2}); m_{T} (GeV/#it{c}^{2})"}, {kTrueMinvVsMinv, o2::framework::HistType::kTH2F, "hTrueMinvVsMinv", "m_{Inv,True} vs m_{Inv}; m_{Inv,True} (GeV/#it{c}^{2}); m_{Inv} (GeV/#it{c}^{2})"}, {kTrueMultVsMult, o2::framework::HistType::kTH2F, "hTrueMultVsMult", "Multiplicity_{True} vs Multiplicity; Multiplicity_{True} ; Multiplicity"}, {kTrueCentVsCent, o2::framework::HistType::kTH2F, "hTrueCentVsCent", "Centrality_{True} vs Centrality; Centrality_{True} (%); Centrality (%)"}, - {kSeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hSeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each same event; # partilce 1; # particle 2"}, - {kMeMixingWindow, o2::framework::HistType::kTH1F, "hMeMixingWindow", "Mixing Window; Mixing Windown ; Entries"}, - {kMeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hMeNpart1VsNpart2", "# unique particle 1 vs # unique partilce 2 in each mixing bin; # partilce 1; # particle 2"}, + // pure mc-truth pair (no reco counterpart) + {kTrueKstar, o2::framework::HistType::kTH1F, "hTrueKstar", "k* (mc-truth pair); k*_{True} (GeV/#it{c}); Entries"}, + {kTrueKt, o2::framework::HistType::kTH1F, "hTrueKt", "transverse momentum (mc-truth pair); k_{T,True} (GeV/#it{c}); Entries"}, + {kTrueMt, o2::framework::HistType::kTH1F, "hTrueMt", "transverse mass (mc-truth pair); m_{T,True} (GeV/#it{c}^{2}); Entries"}, + {kTrueMinv1D, o2::framework::HistType::kTH1F, "hTrueMinv", "invariant mass (mc-truth pair); m_{Inv,True} (GeV/#it{c}^{2}); Entries"}, + {kTruePt1VsPt2, o2::framework::HistType::kTH2F, "hTruePt1VsPt2", "p_{T,1} vs p_{T,2} (mc-truth pair); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c})"}, + {kTruePt1VsKstar, o2::framework::HistType::kTH2F, "hTruePt1VsKstar", "p_{T,1} vs k* (mc-truth pair); p_{T,1,True} (GeV/#it{c}); k*_{True} (GeV/#it{c})"}, + {kTruePt2VsKstar, o2::framework::HistType::kTH2F, "hTruePt2VsKstar", "p_{T,2} vs k* (mc-truth pair); p_{T,2,True} (GeV/#it{c}); k*_{True} (GeV/#it{c})"}, + {kTruePt1VsKt, o2::framework::HistType::kTH2F, "hTruePt1VsKt", "p_{T,1} vs k_{T} (mc-truth pair); p_{T,1,True} (GeV/#it{c}); k_{T,True} (GeV/#it{c})"}, + {kTruePt2VsKt, o2::framework::HistType::kTH2F, "hTruePt2VsKt", "p_{T,2} vs k_{T} (mc-truth pair); p_{T,2,True} (GeV/#it{c}); k_{T,True} (GeV/#it{c})"}, + {kTruePt1VsMt, o2::framework::HistType::kTH2F, "hTruePt1VsMt", "p_{T,1} vs m_{T} (mc-truth pair); p_{T,1,True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2})"}, + {kTruePt2VsMt, o2::framework::HistType::kTH2F, "hTruePt2VsMt", "p_{T,2} vs m_{T} (mc-truth pair); p_{T,2,True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2})"}, + {kTrueKstarVsKt, o2::framework::HistType::kTH2F, "hTrueKstarVsKt", "k* vs k_{T} (mc-truth pair); k*_{True} (GeV/#it{c}); k_{T,True} (GeV/#it{c})"}, + {kTrueKstarVsMt, o2::framework::HistType::kTH2F, "hTrueKstarVsMt", "k* vs m_{T} (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2})"}, + {kTrueKstarVsMult, o2::framework::HistType::kTH2F, "hTrueKstarVsMult", "k* vs Multiplicity (mc-truth pair); k*_{True} (GeV/#it{c}); Multiplicity_{True}"}, + {kTrueKstarVsCent, o2::framework::HistType::kTH2F, "hTrueKstarVsCent", "k* vs Centrality (mc-truth pair); k*_{True} (GeV/#it{c}); Centrality_{True} (%)"}, + {kTrueDeltaEtaDeltaPhi, o2::framework::HistType::kTH2F, "hTrueDeltaEtaDeltaPhi", "#Delta#phi vs #Delta#eta (mc-truth pair); #Delta#phi_{True}; #Delta#eta_{True}"}, + {kTrueQout, o2::framework::HistType::kTH1F, "hTrueQout", "q_{out} in LCMS (mc-truth pair); q_{out,True} (GeV/#it{c}); Entries"}, + {kTrueQside, o2::framework::HistType::kTH1F, "hTrueQside", "q_{side} in LCMS (mc-truth pair); q_{side,True} (GeV/#it{c}); Entries"}, + {kTrueQlong, o2::framework::HistType::kTH1F, "hTrueQlong", "q_{long} in LCMS (mc-truth pair); q_{long,True} (GeV/#it{c}); Entries"}, + {kTrueQoutQsideQlong, o2::framework::HistType::kTH3F, "hTrueQoutQsideQlong", "Bertsch-Pratt 3D (mc-truth pair); q_{out,True} (GeV/#it{c}); q_{side,True} (GeV/#it{c}); q_{long,True} (GeV/#it{c})"}, + {kTrueKstarVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsMult", "k* vs m_{T} vs multiplicity (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); Multiplicity_{True};"}, + {kTrueKstarVsMtVsMultVsCent, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsMultVsCent", "k* vs m_{T} vs multiplicity vs centrality (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); Multiplicity_{True}; Centrality_{True} (%);"}, + {kTrueKstarVsMtVsPt1VsPt2, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsPt1VsPt2", "k* vs m_{T} vs p_{T,1} vs p_{T,2} (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c});"}, + {kTrueKstarVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsPt1VsPt2VsMult", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c}); Multiplicity_{True};"}, + {kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hTrueKstarVsMtVsPt1VsPt2VsMultVsCent", "k* vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity vs centrality (mc-truth pair); k*_{True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2}); p_{T,1,True} (GeV/#it{c}); p_{T,2,True} (GeV/#it{c}); Multiplicity_{True}; Centrality_{True} (%);"}, + // mixing qa + {kSeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hSeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each same event; # particle 1; # particle 2"}, + {kMeMixingWindowRaw, o2::framework::HistType::kTH1F, "hMeMixingWindowRaw", "Raw Mixing Window; Raw Mixing Window; Entries"}, + {kMeMixingWindowEffective, o2::framework::HistType::kTH1F, "hMeMixingWindowEffective", "Effective Mixing Window; Effective Mixing Window; Entries"}, + {kMeNpart1VsNpart2, o2::framework::HistType::kTH2F, "hMeNpart1VsNpart2", "# unique particle 1 vs # unique particle 2 in each mixing bin; # particle 1; # particle 2"}, {kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, o2::framework::HistType::kTHnSparseF, "hVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2", "Mixing bins; V_{z,1} (cm); multiplicity_{1}; centrality_{1} (%); V_{z,2} (cm); multiplicity_{2}; centrality_{2} (%)"}, + // angular + {kDeltaEtaDeltaPhi, o2::framework::HistType::kTH2F, "hDeltaEtaDeltaPhi", "#Delta#phi vs #Delta#eta; #Delta#phi; #Delta#eta"}, + // angles + {kAlpha, o2::framework::HistType::kTH1F, "hAlpha", "angle between p_{T,1} and p_{T,2}; #alpha; Entries"}, + {kAlphaVsKstar, o2::framework::HistType::kTH2F, "hAlphaVsKstar", "#alpha vs k*; #alpha; k* (GeV/#it{c})"}, + {kAlphaVsKt, o2::framework::HistType::kTH2F, "hAlphaVsKt", "#alpha vs k_{T}; #alpha; k_{T} (GeV/#it{c})"}, + {kAlphaVsMt, o2::framework::HistType::kTH2F, "hAlphaVsMt", "#alpha vs m_{T}; #alpha; m_{T} (GeV/#it{c}^{2})"}, + {kBetaLab, o2::framework::HistType::kTH1F, "hBetaLab", "angle between k_{T} and k* (lab frame); #beta_{lab}; Entries"}, + {kBetaLabVsKstar, o2::framework::HistType::kTH2F, "hBetaLabVsKstar", "#beta_{lab} vs k*; #beta_{lab}; k* (GeV/#it{c})"}, + {kBetaLabVsKt, o2::framework::HistType::kTH2F, "hBetaLabVsKt", "#beta_{lab} vs k_{T}; #beta_{lab}; k_{T} (GeV/#it{c})"}, + {kBetaLabVsMt, o2::framework::HistType::kTH2F, "hBetaLabVsMt", "#beta_{lab} vs m_{T}; #beta_{lab}; m_{T} (GeV/#it{c}^{2})"}, + {kKstarVsAlphaVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsAlphaVsMtVsMult", "k* vs #alpha vs m_{T} vs multiplicity; k* (GeV/#it{c}); #alpha; m_{T} (GeV/#it{c}^{2}); Multiplicity;"}, + {kKstarVsAlphaVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsAlphaVsMtVsPt1VsPt2VsMult", "k* vs #alpha vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); #alpha; m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, + {kKstarVsBetaLabVsMtVsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsBetaLabVsMtVsMult", "k* vs #beta_{lab} vs m_{T} vs multiplicity; k* (GeV/#it{c}); #beta_{lab}; m_{T} (GeV/#it{c}^{2}); Multiplicity;"}, + {kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, o2::framework::HistType::kTHnSparseF, "hKstarVsBetaLabVsMtVsPt1VsPt2VsMult", "k* vs #beta_{lab} vs m_{T} vs p_{T,1} vs p_{T,2} vs multiplicity; k* (GeV/#it{c}); #beta_{lab}; m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); Multiplicity;"}, + // Bertsch-Pratt 3D decomposition in LCMS + {kQout, o2::framework::HistType::kTH1F, "hQout", "q_{out} in LCMS; q_{out} (GeV/#it{c}); Entries"}, + {kQside, o2::framework::HistType::kTH1F, "hQside", "q_{side} in LCMS; q_{side} (GeV/#it{c}); Entries"}, + {kQlong, o2::framework::HistType::kTH1F, "hQlong", "q_{long} in LCMS; q_{long} (GeV/#it{c}); Entries"}, + {kQoutVsQsideVsQlong, o2::framework::HistType::kTH3F, "hQoutQsideQlong", "Bertsch-Pratt 3D; q_{out} (GeV/#it{c}); q_{side} (GeV/#it{c}); q_{long} (GeV/#it{c})"}, + {kTrueQoutVsQout, o2::framework::HistType::kTH2F, "hTrueQoutVsQout", "q_{out,True} vs q_{out}; q_{out,True} (GeV/#it{c}); q_{out} (GeV/#it{c})"}, + {kTrueQsideVsQside, o2::framework::HistType::kTH2F, "hTrueQsideVsQside", "q_{side,True} vs q_{side}; q_{side,True} (GeV/#it{c}); q_{side} (GeV/#it{c})"}, + {kTrueQlongVsQlong, o2::framework::HistType::kTH2F, "hTrueQlongVsQlong", "q_{long,True} vs q_{long}; q_{long,True} (GeV/#it{c}); q_{long} (GeV/#it{c})"}, + {kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector, o2::framework::HistType::kTHnSparseF, "hQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector", "Event shape enginering; q_{out} (GeV/#it{c}); q_{side} (GeV/#it{c}); q_{long} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); centrality (%); #varphi_{pair} - #Psi_{EP}; q-vector;"}, }}; -#define PAIR_HIST_ANALYSIS_MAP(confAnalysis, confMixing) \ - {kKstar, {confAnalysis.kstar}}, \ - {kKt, {confAnalysis.kt}}, \ - {kMt, {confAnalysis.mt}}, \ - {kMinv, {confAnalysis.massInv}}, \ - {kPt1VsPt2, {confAnalysis.pt1, confAnalysis.pt2}}, \ - {kPt1VsKstar, {confAnalysis.pt1, confAnalysis.kstar}}, \ - {kPt2VsKstar, {confAnalysis.pt2, confAnalysis.kstar}}, \ - {kPt1VsKt, {confAnalysis.pt1, confAnalysis.kt}}, \ - {kPt2VsKt, {confAnalysis.pt2, confAnalysis.kt}}, \ - {kPt1VsMt, {confAnalysis.pt1, confAnalysis.mt}}, \ - {kPt2VsMt, {confAnalysis.pt2, confAnalysis.mt}}, \ - {kKstarVsKt, {confAnalysis.kstar, confAnalysis.kt}}, \ - {kKstarVsMt, {confAnalysis.kstar, confAnalysis.mt}}, \ - {kKstarVsMult, {confAnalysis.kstar, confAnalysis.multiplicity}}, \ - {kKstarVsCent, {confAnalysis.kstar, confAnalysis.centrality}}, \ - {kKstarVsMass1, {confAnalysis.kstar, confAnalysis.mass1}}, \ - {kKstarVsMass2, {confAnalysis.kstar, confAnalysis.mass2}}, \ - {kMass1VsMass2, {confAnalysis.mass1, confAnalysis.mass2}}, \ - {kKstarVsMinv, {confAnalysis.kstar, confAnalysis.massInv}}, \ - {kPt1VsMinv, {confAnalysis.pt1, confAnalysis.massInv}}, \ - {kPt2VsMinv, {confAnalysis.pt2, confAnalysis.massInv}}, \ - {kKstarVsMtVsMult, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.multiplicity}}, \ - {kKstarVsMtVsMultVsCent, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.multiplicity, confAnalysis.centrality}}, \ - {kKstarVsMtVsPt1VsPt2, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.pt1, confAnalysis.pt2}}, \ - {kKstarVsMtVsPt1VsPt2VsMult, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.pt1, confAnalysis.pt2, confAnalysis.multiplicity}}, \ - {kKstarVsMtVsPt1VsPt2VsMultVsCent, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.pt1, confAnalysis.pt2, confAnalysis.multiplicity, confAnalysis.centrality}}, \ - {kKstarVsMtVsMass1VsMass2, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.mass1, confAnalysis.mass2}}, \ - {kKstarVsMtVsMass1VsMass2VsMult, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.mass1, confAnalysis.mass2, confAnalysis.multiplicity}}, \ - {kKstarVsMtVsMass1VsMass2VsMultVsCent, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.mass1, confAnalysis.mass2, confAnalysis.multiplicity, confAnalysis.centrality}}, \ - {kKstarVsMtVsMass1VsMass2VsPt1VsPt2, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.mass1, confAnalysis.mass2, confAnalysis.pt1, confAnalysis.pt2}}, \ - {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.mass1, confAnalysis.mass2, confAnalysis.pt1, confAnalysis.pt2, confAnalysis.multiplicity}}, \ - {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.mass1, confAnalysis.mass2, confAnalysis.pt1, confAnalysis.pt2, confAnalysis.multiplicity, confAnalysis.centrality}}, \ - {kKstarVsMtVsMinvVsPt1VsPt2, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.massInv, confAnalysis.pt1, confAnalysis.pt2}}, \ - {kKstarVsMtVsMinvVsPt1VsPt2VsMult, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.massInv, confAnalysis.pt1, confAnalysis.pt2, confAnalysis.multiplicity}}, \ - {kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, {confAnalysis.kstar, confAnalysis.mt, confAnalysis.massInv, confAnalysis.pt1, confAnalysis.pt2, confAnalysis.multiplicity, confAnalysis.centrality}}, \ - {kDalitz, {confAnalysis.kstar, confAnalysis.dalitzMtot, confAnalysis.dalitzM12, confAnalysis.dalitzM13}}, \ - {kSeNpart1VsNpart2, {confMixing.particleBinning, confMixing.particleBinning}}, \ - {kMeMixingWindow, {confMixing.particleBinning}}, \ - {kMeNpart1VsNpart2, {confMixing.particleBinning, confMixing.particleBinning}}, \ - {kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, {confMixing.vtxBins, confMixing.multBins, confMixing.centBins, confMixing.vtxBins, confMixing.multBins, confMixing.centBins}}, - -#define PAIR_HIST_MC_MAP(conf) \ - {kTrueKstarVsKstar, {conf.kstar, conf.kstar}}, \ - {kTrueKtVsKt, {conf.kt, conf.kt}}, \ - {kTrueMtVsMt, {conf.mt, conf.mt}}, \ - {kTrueMinvVsMinv, {conf.massInv, conf.massInv}}, \ - {kTrueMultVsMult, {conf.multiplicity, conf.multiplicity}}, \ - {kTrueCentVsCent, {conf.centrality, conf.centrality}}, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PAIR_HIST_ANALYSIS_MAP(confAnalysis) \ + {kKstar, {(confAnalysis).kstar}}, \ + {kKt, {(confAnalysis).kt}}, \ + {kMt, {(confAnalysis).mt}}, \ + {kMinv, {(confAnalysis).massInv}}, \ + {kPt1VsPt2, {(confAnalysis).pt1, (confAnalysis).pt2}}, \ + {kPt1VsKstar, {(confAnalysis).pt1, (confAnalysis).kstar}}, \ + {kPt2VsKstar, {(confAnalysis).pt2, (confAnalysis).kstar}}, \ + {kPt1VsKt, {(confAnalysis).pt1, (confAnalysis).kt}}, \ + {kPt2VsKt, {(confAnalysis).pt2, (confAnalysis).kt}}, \ + {kPt1VsMt, {(confAnalysis).pt1, (confAnalysis).mt}}, \ + {kPt2VsMt, {(confAnalysis).pt2, (confAnalysis).mt}}, \ + {kKstarVsKt, {(confAnalysis).kstar, (confAnalysis).kt}}, \ + {kKstarVsMt, {(confAnalysis).kstar, (confAnalysis).mt}}, \ + {kKstarVsMult, {(confAnalysis).kstar, (confAnalysis).multiplicity}}, \ + {kKstarVsCent, {(confAnalysis).kstar, (confAnalysis).centrality}}, \ + {kKstarVsKtVsCent, {(confAnalysis).kstar, (confAnalysis).kt, (confAnalysis).centrality}}, \ + {kKstarVsMass1, {(confAnalysis).kstar, (confAnalysis).mass1}}, \ + {kKstarVsMass2, {(confAnalysis).kstar, (confAnalysis).mass2}}, \ + {kMass1VsMass2, {(confAnalysis).mass1, (confAnalysis).mass2}}, \ + {kKstarVsMinv, {(confAnalysis).kstar, (confAnalysis).massInv}}, \ + {kPt1VsMinv, {(confAnalysis).pt1, (confAnalysis).massInv}}, \ + {kPt2VsMinv, {(confAnalysis).pt2, (confAnalysis).massInv}}, \ + {kKstarVsMtVsMult, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ + {kKstarVsMtVsPt1VsPt2, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2}}, \ + {kKstarVsMtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsPt1VsPt2VsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ + {kKstarVsMtVsMass1VsMass2, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).mass1, (confAnalysis).mass2}}, \ + {kKstarVsMtVsMass1VsMass2VsMult, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).mass1, (confAnalysis).mass2, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsMass1VsMass2VsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).mass1, (confAnalysis).mass2, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ + {kKstarVsMtVsMass1VsMass2VsPt1VsPt2, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).mass1, (confAnalysis).mass2, (confAnalysis).pt1, (confAnalysis).pt2}}, \ + {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).mass1, (confAnalysis).mass2, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsMass1VsMass2VsPt1VsPt2VsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).mass1, (confAnalysis).mass2, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ + {kKstarVsMtVsMinvVsPt1VsPt2, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).pt1, (confAnalysis).pt2}}, \ + {kKstarVsMtVsMinvVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ + {kKstarVsMinvVsKtVsMult, {(confAnalysis).kstar, (confAnalysis).massInv, (confAnalysis).kt, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsMinvVsKtVsMult, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).kt, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).kt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, {(confAnalysis).kstar, (confAnalysis).mt, (confAnalysis).massInv, (confAnalysis).kt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity, (confAnalysis).centrality}}, \ + {kDalitz, {(confAnalysis).kstar, (confAnalysis).dalitzMtot, (confAnalysis).dalitzM12, (confAnalysis).dalitzM13}}, \ + {kDeltaEtaDeltaPhi, {(confAnalysis).binningDeltaPhi, (confAnalysis).binningDeltaEta}}, \ + {kAlpha, {(confAnalysis).alpha}}, \ + {kAlphaVsKstar, {(confAnalysis).alpha, (confAnalysis).kstar}}, \ + {kAlphaVsKt, {(confAnalysis).alpha, (confAnalysis).kt}}, \ + {kAlphaVsMt, {(confAnalysis).alpha, (confAnalysis).mt}}, \ + {kBetaLab, {(confAnalysis).betaLab}}, \ + {kBetaLabVsKstar, {(confAnalysis).betaLab, (confAnalysis).kstar}}, \ + {kBetaLabVsKt, {(confAnalysis).betaLab, (confAnalysis).kt}}, \ + {kBetaLabVsMt, {(confAnalysis).betaLab, (confAnalysis).mt}}, \ + {kKstarVsAlphaVsMtVsMult, {(confAnalysis).kstar, (confAnalysis).alpha, (confAnalysis).mt, (confAnalysis).multiplicity}}, \ + {kKstarVsAlphaVsMtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).alpha, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kKstarVsBetaLabVsMtVsMult, {(confAnalysis).kstar, (confAnalysis).betaLab, (confAnalysis).mt, (confAnalysis).multiplicity}}, \ + {kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, {(confAnalysis).kstar, (confAnalysis).betaLab, (confAnalysis).mt, (confAnalysis).pt1, (confAnalysis).pt2, (confAnalysis).multiplicity}}, \ + {kQout, {(confAnalysis).qout}}, \ + {kQside, {(confAnalysis).qside}}, \ + {kQlong, {(confAnalysis).qlong}}, \ + {kQoutVsQsideVsQlong, {(confAnalysis).qout, (confAnalysis).qside, (confAnalysis).qlong}}, \ + {kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector, {(confAnalysis).qout, (confAnalysis).qside, (confAnalysis).qlong, (confAnalysis).mt, (confAnalysis).centrality, (confAnalysis).eventPlaneAngle, (confAnalysis).qvector}}, + +// mixing-qa entries are independent of reco vs mc-truth status — both the reco +// analysis path and the pure mc-truth path need them whenever kSe/kMe is set +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PAIR_HIST_MIXING_QA_MAP(confMixing) \ + {kSeNpart1VsNpart2, {(confMixing).particleBinning, (confMixing).particleBinning}}, \ + {kMeMixingWindowRaw, {(confMixing).particleBinning}}, \ + {kMeMixingWindowEffective, {(confMixing).particleBinning}}, \ + {kMeNpart1VsNpart2, {(confMixing).particleBinning, (confMixing).particleBinning}}, \ + {kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, {(confMixing).vtxBins, (confMixing).multBins, (confMixing).centBins, (confMixing).vtxBins, (confMixing).multBins, (confMixing).centBins}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PAIR_HIST_MC_MAP(conf) \ + {kTrueKstarVsKstar, {(conf).kstar, (conf).kstar}}, \ + {kTrueKtVsKt, {(conf).kt, (conf).kt}}, \ + {kTrueMtVsMt, {(conf).mt, (conf).mt}}, \ + {kTrueMinvVsMinv, {(conf).massInv, (conf).massInv}}, \ + {kTrueMultVsMult, {(conf).multiplicity, (conf).multiplicity}}, \ + {kTrueCentVsCent, {(conf).centrality, (conf).centrality}}, \ + {kTrueQoutVsQout, {(conf).qout, (conf).qout}}, \ + {kTrueQsideVsQside, {(conf).qside, (conf).qside}}, \ + {kTrueQlongVsQlong, {(conf).qlong, (conf).qlong}}, + +// pure mc-truth pair (no reco counterpart) — reuses the same analysis binning, +// since there is no separate "true" axis (conf)iguration: the truth value IS the +// analysis-level value for this path. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PAIR_HIST_MC_TRUTH_MAP(conf) \ + {kTrueKstar, {(conf).kstar}}, \ + {kTrueKt, {(conf).kt}}, \ + {kTrueMt, {(conf).mt}}, \ + {kTrueMinv1D, {(conf).massInv}}, \ + {kTruePt1VsPt2, {(conf).pt1, (conf).pt2}}, \ + {kTruePt1VsKstar, {(conf).pt1, (conf).kstar}}, \ + {kTruePt2VsKstar, {(conf).pt2, (conf).kstar}}, \ + {kTruePt1VsKt, {(conf).pt1, (conf).kt}}, \ + {kTruePt2VsKt, {(conf).pt2, (conf).kt}}, \ + {kTruePt1VsMt, {(conf).pt1, (conf).mt}}, \ + {kTruePt2VsMt, {(conf).pt2, (conf).mt}}, \ + {kTrueKstarVsKt, {(conf).kstar, (conf).kt}}, \ + {kTrueKstarVsMt, {(conf).kstar, (conf).mt}}, \ + {kTrueKstarVsMult, {(conf).kstar, (conf).multiplicity}}, \ + {kTrueKstarVsCent, {(conf).kstar, (conf).centrality}}, \ + {kTrueDeltaEtaDeltaPhi, {(conf).binningDeltaPhi, (conf).binningDeltaEta}}, \ + {kTrueQout, {(conf).qout}}, \ + {kTrueQside, {(conf).qside}}, \ + {kTrueQlong, {(conf).qlong}}, \ + {kTrueQoutQsideQlong, {(conf).qout, (conf).qside, (conf).qlong}}, \ + {kTrueKstarVsMtVsMult, {(conf).kstar, (conf).mt, (conf).multiplicity}}, \ + {kTrueKstarVsMtVsMultVsCent, {(conf).kstar, (conf).mt, (conf).multiplicity, (conf).centrality}}, \ + {kTrueKstarVsMtVsPt1VsPt2, {(conf).kstar, (conf).mt, (conf).pt1, (conf).pt2}}, \ + {kTrueKstarVsMtVsPt1VsPt2VsMult, {(conf).kstar, (conf).mt, (conf).pt1, (conf).pt2, (conf).multiplicity}}, \ + {kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, {(conf).kstar, (conf).mt, (conf).pt1, (conf).pt2, (conf).multiplicity, (conf).centrality}}, template auto makePairHistSpecMap(T1 const& confPairBinning, T2 const& confMixing) { return std::map>{ - PAIR_HIST_ANALYSIS_MAP(confPairBinning, confMixing)}; + PAIR_HIST_ANALYSIS_MAP(confPairBinning) + PAIR_HIST_MIXING_QA_MAP(confMixing)}; }; template auto makePairMcHistSpecMap(T1 const& confPairBinning, T2 const& confMixing) { return std::map>{ - PAIR_HIST_ANALYSIS_MAP(confPairBinning, confMixing) - PAIR_HIST_MC_MAP(confPairBinning)}; + PAIR_HIST_ANALYSIS_MAP(confPairBinning) + PAIR_HIST_MIXING_QA_MAP(confMixing) + PAIR_HIST_MC_MAP(confPairBinning)}; +}; + +// for the pure mc-truth-only pair builder (kMc without kReco): needs both the +// mc-truth binning AND the mixing-qa entries (kSe/kMe histograms are filled +// for this path too, via initSeMixingQa/initMeMixingQa being gated on kSe/kMe +// alone, independent of kReco/kMc). +template +auto makePairMcTruthHistSpecMap(T1 const& confPairBinning, T2 const& confMixing) +{ + return std::map>{ + PAIR_HIST_MC_TRUTH_MAP(confPairBinning) + PAIR_HIST_MIXING_QA_MAP(confMixing)}; }; #undef PAIR_HIST_ANALYSIS_MAP +#undef PAIR_HIST_MIXING_QA_MAP #undef PAIR_HIST_MC_MAP +#undef PAIR_HIST_MC_TRUTH_MAP constexpr char PrefixTrackTrackSe[] = "TrackTrack/SE/"; constexpr char PrefixTrackTrackMe[] = "TrackTrack/ME/"; @@ -312,23 +556,41 @@ constexpr char PrefixTrackTrackMe[] = "TrackTrack/ME/"; constexpr char PrefixTrackV0Se[] = "TrackV0/SE/"; constexpr char PrefixTrackV0Me[] = "TrackV0/ME/"; +constexpr char PrefixTrackD0Se[] = "TrackD0/SE/"; +constexpr char PrefixTrackD0Me[] = "TrackD0/ME/"; +constexpr char PrefixTrackLcSe[] = "TrackLc/SE/"; +constexpr char PrefixTrackLcMe[] = "TrackLc/ME/"; +constexpr char PrefixD0D0Se[] = "D0D0/SE/"; +constexpr char PrefixD0D0Me[] = "D0D0/ME/"; + constexpr char PrefixV0V0Se[] = "V0V0/SE/"; constexpr char PrefixV0V0Me[] = "V0V0/ME/"; constexpr char PrefixTrackResonanceSe[] = "TrackResonance/SE/"; constexpr char PrefixTrackResonanceMe[] = "TrackResonance/ME/"; +constexpr char PrefixTrackPi0Se[] = "TrackPi0/SE/"; +constexpr char PrefixTrackPi0Me[] = "TrackPi0/ME/"; +constexpr char PrefixTrackEtaSe[] = "TrackEta/SE/"; +constexpr char PrefixTrackEtaMe[] = "TrackEta/ME/"; + +constexpr char PrefixV0ResonanceSe[] = "V0Resonance/SE/"; +constexpr char PrefixV0ResonanceMe[] = "V0Resonance/ME/"; + constexpr char PrefixTrackCascadeSe[] = "TrackCascade/SE/"; constexpr char PrefixTrackCascadeMe[] = "TrackCascade/ME/"; constexpr char PrefixTrackKinkSe[] = "TrackKink/SE/"; constexpr char PrefixTrackKinkMe[] = "TrackKink/ME/"; +constexpr char PrefixMcParticleMcParticleSe[] = "McParticleMcParticle/SE/"; +constexpr char PrefixMcParticleMcParticleMe[] = "McParticleMcParticle/ME/"; + constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view MixingQaDir = "MixingQA/"; constexpr std::string_view McDir = "MC/"; -template class PairHistManager @@ -351,6 +613,7 @@ class PairHistManager // flags for histograms mPlot1d = ConfPairBinning.plot1D.value; mPlot2d = ConfPairBinning.plot2D.value; + mPlotKstarVsKtVsCent = ConfPairBinning.plotKstarVsKtVsCent.value; mPlotKstarVsMtVsMult = ConfPairBinning.plotKstarVsMtVsMult.value; mPlotKstarVsMtVsMultVsCent = ConfPairBinning.plotKstarVsMtVsMultVsCent.value; @@ -370,7 +633,36 @@ class PairHistManager mPlotKstarVsMtVsMinvVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsMtVsMinv1VsPt1VsPt2VsMult.value; mPlotKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent = ConfPairBinning.plotKstarVsMtVsMinv1VsPt1VsPt2VsMultVsCent.value; + mPlotKstarVsMinvVsKtVsMult = ConfPairBinning.plotKstarVsMinvVsKtVsMult.value; + mPlotKstarVsMtVsMinvVsKtVsMult = ConfPairBinning.plotKstarVsMtVsMinvVsKtVsMult.value; + mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult.value; + mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent = ConfPairBinning.plotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent.value; + mPlotDalitz = ConfPairBinning.plotDalitz.value; + mPlotDeltaEtaDeltaPhi = ConfPairBinning.plotDeltaEtaDeltaPhi.value; + mPlotAlpha = ConfPairBinning.plotAlpha.value; + mPlotKstarVsAlphaVsMtVsMult = ConfPairBinning.plotKstarVsAlphaVsMtVsMult.value; + mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsAlphaVsMtVsPt1VsPt2VsMult.value; + mPlotBetaLab = ConfPairBinning.plotBetaLab.value; + mPlotKstarVsBetaLabVsMtVsMult = ConfPairBinning.plotKstarVsBetaLabVsMtVsMult.value; + mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult = ConfPairBinning.plotKstarVsBetaLabVsMtVsPt1VsPt2VsMult.value; + mPlotBertschPratt = ConfPairBinning.plotBertschPratt.value; + mPlotEventShape = ConfPairBinning.plotEventShape.value; + + mPlotSH = ConfPairBinning.plotSH.value; + mShUseCent = ConfPairBinning.shUseCent.value; + mShLMax = ConfPairBinning.shLMax.value; + mShFrame = ConfPairBinning.shFrame.value; + mShPlot1D = ConfPairBinning.shPlot1D.value; + if (mPlotSH) { + mShKstarSpec = {ConfPairBinning.shKstar, "k* (GeV/#it{c})"}; + mYlm.initializeYlms(); + // copy bin edges, stripping the leading VARIABLE_WIDTH (0) marker + mShCentEdges.assign(ConfPairBinning.shCentBins.value.begin() + 1, ConfPairBinning.shCentBins.value.end()); + mShKtEdges.assign(ConfPairBinning.shKtBins.value.begin() + 1, ConfPairBinning.shKtBins.value.end()); + mShCentSpec = {ConfPairBinning.shCentBins, mShUseCent ? "centrality (%)" : "multiplicity"}; + mShKtSpec = {ConfPairBinning.shKtBins, "k_{T} (GeV/#it{c})"}; + } // transverse mass type mMtType = static_cast(ConfPairBinning.transverseMassType.value); @@ -388,14 +680,20 @@ class PairHistManager mPairCorrelationQa = ConfMixing.enablePairCorrelationQa.value; mEventMixingQa = ConfMixing.enableEventMixingQa.value; - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { initAnalysis(Specs); } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + // reco-vs-truth correlation: requires BOTH a reco pair and matched mc info + if constexpr (isFlagSet(mode, modes::Mode::kReco) && isFlagSet(mode, modes::Mode::kMc)) { initMc(Specs); } + // pure mc-truth pair: requires mc info WITHOUT a reco pair + if constexpr (isFlagSet(mode, modes::Mode::kMc) && !isFlagSet(mode, modes::Mode::kReco)) { + initMcTruth(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kSe)) { initSeMixingQa(Specs); } @@ -427,67 +725,10 @@ class PairHistManager mAbsCharge2 = std::abs(chargeAbsParticle2); } - template - void setPair(T1 const& particle1, T2 const& particle2, T3 const& trackTable) - { - // if one of the particles has a mass getter (like lambda), we cache the value for the filling later - // otherwise set it to the pdg mass - if constexpr (utils::HasMass) { - mRecoMass1 = particle1.mass(); - } else { - mRecoMass1 = mPdgMass1; - } - if constexpr (utils::HasMass) { - mRecoMass2 = particle2.mass(); - } else { - mRecoMass2 = mPdgMass2; - } - - // get mass for 4-vectors - double mass1 = 0.f; - double mass2 = 0.f; - if (mUsePdgMass) { - mass1 = mPdgMass1; - mass2 = mPdgMass2; - } else { - mass1 = mRecoMass1; - mass2 = mRecoMass2; - } - - // pt in track table is calculated from 1/signedPt from the original track table - // in case of He with Z=2, we have to rescale the pt with the absolute charge - mParticle1 = ROOT::Math::PtEtaPhiMVector(mAbsCharge1 * particle1.pt(), particle1.eta(), particle1.phi(), mass1); - mParticle2 = ROOT::Math::PtEtaPhiMVector(mAbsCharge2 * particle2.pt(), particle2.eta(), particle2.phi(), mass2); - - // set kT - mKt = getKt(mParticle1, mParticle2); - - // set mT - mMt = getMt(mParticle1, mParticle2); - - // set Minv - mMassInv = getMinv(mParticle1, mParticle2); - - // set kstar - mKstar = getKstar(mParticle1, mParticle2); - - if (mPlotDalitz) { - if constexpr (modes::isEqual(particleType1, modes::Particle::kTrack) && modes::isEqual(particleType2, modes::Particle::kV0)) { - auto posDaughter = trackTable.rawIteratorAt(particle2.posDauId() - trackTable.offset()); - auto negDaughter = trackTable.rawIteratorAt(particle2.negDauId() - trackTable.offset()); - ROOT::Math::PtEtaPhiMVector posDau4v = ROOT::Math::PtEtaPhiMVector(posDaughter.pt(), posDaughter.eta(), posDaughter.phi(), mPdgMassPosDau2); - ROOT::Math::PtEtaPhiMVector negDau4v = ROOT::Math::PtEtaPhiMVector(negDaughter.pt(), negDaughter.eta(), negDaughter.phi(), mPdgMassNegDau2); - mMassTot2 = (mParticle1 + posDau4v + negDau4v).M2(); - mMass12 = (mParticle1 + posDau4v).M2(); - mMass13 = (mParticle1 + negDau4v).M2(); - } - } - } - template void setPair(T1 const& particle1, T2 const& particle2, T3 const& trackTable, T4 const& col) { - setPair(particle1, particle2, trackTable); + setPairKinematics(particle1, particle2, trackTable, col); mMult = col.mult(); mCent = col.cent(); } @@ -495,7 +736,7 @@ class PairHistManager template void setPair(T1 const& particle1, T2 const& particle2, T3 const& trackTable, T4 const& col1, T5 const& col2) { - setPair(particle1, particle2, trackTable); + setPairKinematics(particle1, particle2, trackTable, col1); mMult = 0.5f * (col1.mult() + col2.mult()); // if mixing with multiplicity, should be in the same mixing bin mCent = 0.5f * (col1.cent() + col2.cent()); // if mixing with centrality, should be in the same mixing bin } @@ -512,14 +753,19 @@ class PairHistManager auto mcParticle1 = particle1.template fMcParticle_as(); auto mcParticle2 = particle2.template fMcParticle_as(); - mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(mAbsCharge1 * mcParticle1.pt(), mcParticle1.eta(), mcParticle1.phi(), mPdgMass1); - mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(mAbsCharge2 * mcParticle2.pt(), mcParticle2.eta(), mcParticle2.phi(), mPdgMass2); + mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(mcParticle1.pt(), mcParticle1.eta(), mcParticle1.phi(), mPdgMass1); + mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(mcParticle2.pt(), mcParticle2.eta(), mcParticle2.phi(), mPdgMass2); // compute true kinematics - mTrueKt = getKt(mTrueParticle1, mTrueParticle2); - mTrueMt = getMt(mTrueParticle1, mTrueParticle2); - mTrueMt = getMinv(mTrueParticle1, mTrueParticle2); - mTrueKstar = getKstar(mTrueParticle1, mTrueParticle2); + mTrueKt = femtomath::getKt(mTrueParticle1, mTrueParticle2); + mTrueMt = femtomath::getMt(mTrueParticle1, mTrueParticle2, mMtType); + mTrueMinv = femtomath::getMinv(mTrueParticle1, mTrueParticle2); + mTrueKstar = femtomath::getKstar(mTrueParticle1, mTrueParticle2); + + if (mPlotBertschPratt) { + std::tie(mTrueQout, mTrueQside, mTrueQlong) = + femtomath::computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); + } } template @@ -553,6 +799,71 @@ class PairHistManager mTrueCent = 0.5f * (mcCol1.cent() + mcCol2.cent()); } + // pure mc-truth pair: particles ARE the truth, no reco track/trackTable exists. + // Mass is always the PDG mass here — there is no "reconstructed mass" concept + // for a truth particle, so mUsePdgMass is not consulted in this path. + // NOTE: Dalitz plots are not supported here — there is no trackTable/daughter + // structure for a pure mc-truth particle-particle pair. + template + void setPairMcTruth(T1 const& particle1, T2 const& particle2) + { + mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(particle1.pt(), particle1.eta(), particle1.phi(), mPdgMass1); + mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(particle2.pt(), particle2.eta(), particle2.phi(), mPdgMass2); + + mTrueKt = femtomath::getKt(mTrueParticle1, mTrueParticle2); + mTrueMt = femtomath::getMt(mTrueParticle1, mTrueParticle2, mMtType); + mTrueMinv = femtomath::getMinv(mTrueParticle1, mTrueParticle2); + mTrueKstar = femtomath::getKstar(mTrueParticle1, mTrueParticle2); + + // in the pure mc-truth path there is no reco counterpart, so the generated values are also + // stored in the reco members; they are used by getKinematic(), i.e. by the kinematic histograms + // of the pair cleaner and of the close pair rejection + mKt = mTrueKt; + mMt = mTrueMt; + mMassInv = mTrueMinv; + mKstar = mTrueKstar; + + if (mPlotBertschPratt) { + std::tie(mTrueQout, mTrueQside, mTrueQlong) = femtomath::computeBertschPrattLCMS(mTrueParticle1, mTrueParticle2); + } + if (mPlotDeltaEtaDeltaPhi) { + mTrueDeltaEta = particle1.eta() - particle2.eta(); + mTrueDeltaPhi = RecoDecay::constrainAngle(particle1.phi() - particle2.phi(), -o2::constants::math::PIHalf); + } + } + + // same-event: single (truth) collision for true mult/cent + template + void setPairMcTruth(T1 const& particle1, T2 const& particle2, T3 const& col) + { + setPairMcTruth(particle1, particle2); + mTrueMult = col.mult(); + mTrueCent = col.cent(); + } + + // mixed-event: two (truth) collisions, averaged mult/cent — same convention as setPair + template + void setPairMcTruth(T1 const& particle1, T2 const& particle2, T3 const& col1, T4 const& col2) + { + setPairMcTruth(particle1, particle2); + mTrueMult = 0.5f * (col1.mult() + col2.mult()); + mTrueCent = 0.5f * (col1.cent() + col2.cent()); + } + + /// pair cuts on the mc-truth values, for the pure mc-truth path (kMc without kReco), + /// where the reco values are never set + bool checkPairCutsMcTruth() const + { + return (!(mKstarMin > 0.f) || mTrueKstar > mKstarMin) && + (!(mKstarMax > 0.f) || mTrueKstar < mKstarMax) && + (!(mKtMin > 0.f) || mTrueKt > mKtMin) && + (!(mKtMax > 0.f) || mTrueKt < mKtMax) && + (!(mMtMin > 0.f) || mTrueMt > mMtMin) && + (!(mMtMax > 0.f) || mTrueMt < mMtMax) && + (!(mMassInvMin > 0.f) || mTrueMinv > mMassInvMin) && + (!(mMassInvMax > 0.f) || mTrueMinv < mMassInvMax); + } + bool checkPairCuts() const { return (!(mKstarMin > 0.f) || mKstar > mKstarMin) && @@ -570,12 +881,15 @@ class PairHistManager template void fill() { - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { fillAnalysis(); } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco) && isFlagSet(mode, modes::Mode::kMc)) { fillMc(); } + if constexpr (isFlagSet(mode, modes::Mode::kMc) && !isFlagSet(mode, modes::Mode::kReco)) { + fillMcTruth(); + } } template @@ -599,7 +913,9 @@ class PairHistManager void resetTrackedParticlesPerEvent() { mParticles1PerEvent.clear(); + mParticles1PerEvent.reserve(100); mParticles2PerEvent.clear(); + mParticles2PerEvent.reserve(100); } void fillMixingQaSe() @@ -616,14 +932,33 @@ class PairHistManager } } - void fillMixingQaMePerMixingBin(int windowSize) + void fillMixingQaMePerMixingBin(int windowSizeRaw, int windowSizeEffective) { - if (mEventMixingQa) { - mHistogramRegistry->fill(HIST(prefix) + HIST(MixingQaDir) + HIST(getHistName(kMeMixingWindow, HistTable)), windowSize); + if (mPairCorrelationQa) { + mHistogramRegistry->fill(HIST(prefix) + HIST(MixingQaDir) + HIST(getHistName(kMeMixingWindowRaw, HistTable)), windowSizeRaw); + mHistogramRegistry->fill(HIST(prefix) + HIST(MixingQaDir) + HIST(getHistName(kMeMixingWindowEffective, HistTable)), windowSizeEffective); } } - float getKstar() const { return mKstar; } + // kinematicVariable is supplied by the caller (CPR/pair cleaner), each of which carries its own + // Configurable for it -- this manager has no opinion of its own on which variable to report + float getKinematic(modes::KinematicVariable kinematicVariable) const + { + switch (kinematicVariable) { + case modes::KinematicVariable::kKstar: + return mKstar; + break; + case modes::KinematicVariable::kKt: + return mKt; + break; + case modes::KinematicVariable::kMt: + return mMt; + break; + default: + LOG(fatal) << "Invalid kinematic Variable, breaking..."; + } + return mKstar; + } private: void initAnalysis(std::map> const& Specs) @@ -658,6 +993,9 @@ class PairHistManager } // higher dimensional histograms + if (mPlotKstarVsKtVsCent) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsKtVsCent, HistTable), getHistDesc(kKstarVsKtVsCent, HistTable), getHistType(kKstarVsKtVsCent, HistTable), {Specs.at(kKstarVsKtVsCent)}); + } if (mPlotKstarVsMtVsMult) { mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsMtVsMult, HistTable), getHistDesc(kKstarVsMtVsMult, HistTable), getHistType(kKstarVsMtVsMult, HistTable), {Specs.at(kKstarVsMtVsMult)}); } @@ -704,11 +1042,153 @@ class PairHistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, HistTable), getHistDesc(kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, HistTable), getHistType(kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, HistTable), {Specs.at(kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent)}); } + if (mPlotKstarVsMinvVsKtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsMinvVsKtVsMult, HistTable), getHistDesc(kKstarVsMinvVsKtVsMult, HistTable), getHistType(kKstarVsMinvVsKtVsMult, HistTable), {Specs.at(kKstarVsMinvVsKtVsMult)}); + } + if (mPlotKstarVsMtVsMinvVsKtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsMtVsMinvVsKtVsMult, HistTable), getHistDesc(kKstarVsMtVsMinvVsKtVsMult, HistTable), getHistType(kKstarVsMtVsMinvVsKtVsMult, HistTable), {Specs.at(kKstarVsMtVsMinvVsKtVsMult)}); + } + if (mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult, HistTable), getHistDesc(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult, HistTable), getHistType(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult, HistTable), {Specs.at(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult)}); + } + if (mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, HistTable), getHistDesc(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, HistTable), getHistType(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, HistTable), {Specs.at(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent)}); + } + if (mPlotDalitz) { mHistogramRegistry->add(analysisDir + getHistNameV2(kDalitz, HistTable), getHistDesc(kDalitz, HistTable), getHistType(kDalitz, HistTable), {Specs.at(kDalitz)}); } + if (mPlotDeltaEtaDeltaPhi) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kDeltaEtaDeltaPhi, HistTable), getHistDesc(kDeltaEtaDeltaPhi, HistTable), getHistType(kDeltaEtaDeltaPhi, HistTable), {Specs.at(kDeltaEtaDeltaPhi)}); + } + if (mPlotAlpha) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlpha, HistTable), getHistDesc(kAlpha, HistTable), getHistType(kAlpha, HistTable), {Specs.at(kAlpha)}); + if (mPlot2d) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsKstar, HistTable), getHistDesc(kAlphaVsKstar, HistTable), getHistType(kAlphaVsKstar, HistTable), {Specs.at(kAlphaVsKstar)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsKt, HistTable), getHistDesc(kAlphaVsKt, HistTable), getHistType(kAlphaVsKt, HistTable), {Specs.at(kAlphaVsKt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kAlphaVsMt, HistTable), getHistDesc(kAlphaVsMt, HistTable), getHistType(kAlphaVsMt, HistTable), {Specs.at(kAlphaVsMt)}); + } + } + if (mPlotKstarVsAlphaVsMtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsAlphaVsMtVsMult, HistTable), getHistDesc(kKstarVsAlphaVsMtVsMult, HistTable), getHistType(kKstarVsAlphaVsMtVsMult, HistTable), {Specs.at(kKstarVsAlphaVsMtVsMult)}); + } + if (mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), getHistDesc(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), getHistType(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable), {Specs.at(kKstarVsAlphaVsMtVsPt1VsPt2VsMult)}); + } + if (mPlotBetaLab) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLab, HistTable), getHistDesc(kBetaLab, HistTable), getHistType(kBetaLab, HistTable), {Specs.at(kBetaLab)}); + if (mPlot2d) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsKstar, HistTable), getHistDesc(kBetaLabVsKstar, HistTable), getHistType(kBetaLabVsKstar, HistTable), {Specs.at(kBetaLabVsKstar)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsKt, HistTable), getHistDesc(kBetaLabVsKt, HistTable), getHistType(kBetaLabVsKt, HistTable), {Specs.at(kBetaLabVsKt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kBetaLabVsMt, HistTable), getHistDesc(kBetaLabVsMt, HistTable), getHistType(kBetaLabVsMt, HistTable), {Specs.at(kBetaLabVsMt)}); + } + } + if (mPlotKstarVsBetaLabVsMtVsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsBetaLabVsMtVsMult, HistTable), getHistDesc(kKstarVsBetaLabVsMtVsMult, HistTable), getHistType(kKstarVsBetaLabVsMtVsMult, HistTable), {Specs.at(kKstarVsBetaLabVsMtVsMult)}); + } + if (mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), getHistDesc(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), getHistType(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable), {Specs.at(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult)}); + } + if (mPlotBertschPratt) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kQout, HistTable), getHistDesc(kQout, HistTable), getHistType(kQout, HistTable), {Specs.at(kQout)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kQside, HistTable), getHistDesc(kQside, HistTable), getHistType(kQside, HistTable), {Specs.at(kQside)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kQlong, HistTable), getHistDesc(kQlong, HistTable), getHistType(kQlong, HistTable), {Specs.at(kQlong)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kQoutVsQsideVsQlong, HistTable), getHistDesc(kQoutVsQsideVsQlong, HistTable), getHistType(kQoutVsQsideVsQlong, HistTable), {Specs.at(kQoutVsQsideVsQlong)}); + } + + if (mPlotEventShape) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector, HistTable), getHistDesc(kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector, HistTable), getHistType(kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector, HistTable), {Specs.at(kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector)}); + } + + if (mPlotSH) { + const int nJM = (mShLMax + 1) * (mShLMax + 1); + const int nCent = static_cast(mShCentEdges.size()) - 1; // n edges -> n - 1 bins + const int nKt = static_cast(mShKtEdges.size()) - 1; + mShYlmBuffer.assign(nJM, {}); + + mShReal.resize(nJM); + mShImag.resize(nJM); + mShCov.resize(nCent); + mSh1D.resize(nCent); + mShBinCount.resize(nCent); + + const std::string dir = std::string(prefix) + std::string(AnalysisDir) + "SH/"; + int ihist = 0; + for (int l = 0; l <= mShLMax; ++l) { + for (int m = -l; m <= l; ++m) { + std::string lm = std::to_string(l); + lm += (m < 0) ? std::to_string(l - m) : std::to_string(m); + std::string nameRe = dir; + nameRe += "ReYlm"; + nameRe += lm; + std::string nameIm = dir; + nameIm += "ImYlm"; + nameIm += lm; + // shared "Y_{l}^{m}" suffix for both titles + std::string ylmLabel = "Y_{"; + ylmLabel += std::to_string(l); + ylmLabel += "}^{"; + ylmLabel += std::to_string(m); + ylmLabel += "}"; + std::string titleRe = "Re "; + titleRe += ylmLabel; + titleRe += "; k* (GeV/#it{c}); mult/cent; k_{T} (GeV/#it{c})"; + std::string titleIm = "Im "; + titleIm += ylmLabel; + titleIm += "; k* (GeV/#it{c}); mult/cent; k_{T} (GeV/#it{c})"; + mShReal[ihist] = mHistogramRegistry->add(nameRe.c_str(), titleRe.c_str(), o2::framework::kTH3D, {mShKstarSpec, mShCentSpec, mShKtSpec}); + mShImag[ihist] = mHistogramRegistry->add(nameIm.c_str(), titleIm.c_str(), o2::framework::kTH3D, {mShKstarSpec, mShCentSpec, mShKtSpec}); + mShReal[ihist]->Sumw2(); + mShImag[ihist]->Sumw2(); + ++ihist; + } + } + + const int nAxisLM = 2 * nJM; + const o2::framework::AxisSpec covLmAxis{nAxisLM, -0.5, static_cast(nAxisLM) - 0.5, "l,m #times (re,im)"}; + + for (int iCent = 0; iCent < nCent; ++iCent) { + mShCov[iCent].resize(nKt); + mSh1D[iCent].resize(nKt); + mShBinCount[iCent].resize(nKt); + // name suffix: mult_{low}_{high} + std::string centSuffix = "_mult_"; + centSuffix += std::to_string(static_cast(mShCentEdges[iCent])); + centSuffix += "_"; + centSuffix += std::to_string(static_cast(mShCentEdges[iCent + 1])); + for (int iKt = 0; iKt < nKt; ++iKt) { + // name suffix: _mult_{low}_{high}_kT_{low*100}_{high*100} + std::string cellSuffix = centSuffix; + cellSuffix += "_kT_"; + cellSuffix += std::to_string(static_cast(mShKtEdges[iKt] * 100.0)); + cellSuffix += "_"; + cellSuffix += std::to_string(static_cast(mShKtEdges[iKt + 1] * 100.0)); + + // SH covariance TH3D + std::string nameCov = dir; + nameCov += "Cov"; + nameCov += cellSuffix; + mShCov[iCent][iKt] = mHistogramRegistry->add(nameCov.c_str(), "SH covariance; k* (GeV/#it{c}); l,m; l,m", o2::framework::kTH3D, {mShKstarSpec, covLmAxis, covLmAxis}); + mShCov[iCent][iKt]->Sumw2(); + + if (mShPlot1D) { + std::string nameBinCount = dir; + nameBinCount += "BinCount"; + nameBinCount += cellSuffix; + mShBinCount[iCent][iKt] = mHistogramRegistry->add(nameBinCount.c_str(), "SH bin occupancy; k* (GeV/#it{c}); Entries", o2::framework::kTH1D, {mShKstarSpec}); + + std::string name1D = dir; + name1D += "h1D"; + name1D += cellSuffix; + mSh1D[iCent][iKt] = mHistogramRegistry->add(name1D.c_str(), "1D distribution; k* (GeV/#it{c}); Entries", o2::framework::kTH1D, {mShKstarSpec}); + mSh1D[iCent][iKt]->Sumw2(); + } + } + } + } } + // reco-vs-truth correlation histograms (kReco and kMc both set) void initMc(std::map> const& Specs) { std::string mcDir = std::string(prefix) + std::string(McDir); @@ -718,25 +1198,173 @@ class PairHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMinvVsMinv, HistTable), getHistDesc(kTrueMinvVsMinv, HistTable), getHistType(kTrueMinvVsMinv, HistTable), {Specs.at(kTrueMinvVsMinv)}); mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMultVsMult, HistTable), getHistDesc(kTrueMultVsMult, HistTable), getHistType(kTrueMultVsMult, HistTable), {Specs.at(kTrueMultVsMult)}); mHistogramRegistry->add(mcDir + getHistNameV2(kTrueCentVsCent, HistTable), getHistDesc(kTrueCentVsCent, HistTable), getHistType(kTrueCentVsCent, HistTable), {Specs.at(kTrueCentVsCent)}); + + if (mPlotBertschPratt) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQoutVsQout, HistTable), getHistDesc(kTrueQoutVsQout, HistTable), getHistType(kTrueQoutVsQout, HistTable), {Specs.at(kTrueQoutVsQout)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQsideVsQside, HistTable), getHistDesc(kTrueQsideVsQside, HistTable), getHistType(kTrueQsideVsQside, HistTable), {Specs.at(kTrueQsideVsQside)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQlongVsQlong, HistTable), getHistDesc(kTrueQlongVsQlong, HistTable), getHistType(kTrueQlongVsQlong, HistTable), {Specs.at(kTrueQlongVsQlong)}); + } + } + + // pure mc-truth pair (no reco counterpart, kMc without kReco) — reuses the + // same mPlot1d/mPlot2d/mPlotDeltaEtaDeltaPhi/mPlotBertschPratt flags as the + // reco analysis path, since these are histogram-content flags, not mode flags + void initMcTruth(std::map> const& Specs) + { + std::string mcDir = std::string(prefix) + std::string(McDir); + if (mPlot1d) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar, HistTable), getHistDesc(kTrueKstar, HistTable), getHistType(kTrueKstar, HistTable), {Specs.at(kTrueKstar)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKt, HistTable), getHistDesc(kTrueKt, HistTable), getHistType(kTrueKt, HistTable), {Specs.at(kTrueKt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMt, HistTable), getHistDesc(kTrueMt, HistTable), getHistType(kTrueMt, HistTable), {Specs.at(kTrueMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMinv1D, HistTable), getHistDesc(kTrueMinv1D, HistTable), getHistType(kTrueMinv1D, HistTable), {Specs.at(kTrueMinv1D)}); + } + if (mPlot2d) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt1VsPt2, HistTable), getHistDesc(kTruePt1VsPt2, HistTable), getHistType(kTruePt1VsPt2, HistTable), {Specs.at(kTruePt1VsPt2)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt1VsKstar, HistTable), getHistDesc(kTruePt1VsKstar, HistTable), getHistType(kTruePt1VsKstar, HistTable), {Specs.at(kTruePt1VsKstar)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt2VsKstar, HistTable), getHistDesc(kTruePt2VsKstar, HistTable), getHistType(kTruePt2VsKstar, HistTable), {Specs.at(kTruePt2VsKstar)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt1VsKt, HistTable), getHistDesc(kTruePt1VsKt, HistTable), getHistType(kTruePt1VsKt, HistTable), {Specs.at(kTruePt1VsKt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt2VsKt, HistTable), getHistDesc(kTruePt2VsKt, HistTable), getHistType(kTruePt2VsKt, HistTable), {Specs.at(kTruePt2VsKt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt1VsMt, HistTable), getHistDesc(kTruePt1VsMt, HistTable), getHistType(kTruePt1VsMt, HistTable), {Specs.at(kTruePt1VsMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt2VsMt, HistTable), getHistDesc(kTruePt2VsMt, HistTable), getHistType(kTruePt2VsMt, HistTable), {Specs.at(kTruePt2VsMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsKt, HistTable), getHistDesc(kTrueKstarVsKt, HistTable), getHistType(kTrueKstarVsKt, HistTable), {Specs.at(kTrueKstarVsKt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsMt, HistTable), getHistDesc(kTrueKstarVsMt, HistTable), getHistType(kTrueKstarVsMt, HistTable), {Specs.at(kTrueKstarVsMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsMult, HistTable), getHistDesc(kTrueKstarVsMult, HistTable), getHistType(kTrueKstarVsMult, HistTable), {Specs.at(kTrueKstarVsMult)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsCent, HistTable), getHistDesc(kTrueKstarVsCent, HistTable), getHistType(kTrueKstarVsCent, HistTable), {Specs.at(kTrueKstarVsCent)}); + } + if (mPlotDeltaEtaDeltaPhi) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueDeltaEtaDeltaPhi, HistTable), getHistDesc(kTrueDeltaEtaDeltaPhi, HistTable), getHistType(kTrueDeltaEtaDeltaPhi, HistTable), {Specs.at(kTrueDeltaEtaDeltaPhi)}); + } + if (mPlotBertschPratt) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQout, HistTable), getHistDesc(kTrueQout, HistTable), getHistType(kTrueQout, HistTable), {Specs.at(kTrueQout)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQside, HistTable), getHistDesc(kTrueQside, HistTable), getHistType(kTrueQside, HistTable), {Specs.at(kTrueQside)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQlong, HistTable), getHistDesc(kTrueQlong, HistTable), getHistType(kTrueQlong, HistTable), {Specs.at(kTrueQlong)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQoutQsideQlong, HistTable), getHistDesc(kTrueQoutQsideQlong, HistTable), getHistType(kTrueQoutQsideQlong, HistTable), {Specs.at(kTrueQoutQsideQlong)}); + } + if (mPlotKstarVsMtVsMult) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsMtVsMult, HistTable), getHistDesc(kTrueKstarVsMtVsMult, HistTable), getHistType(kTrueKstarVsMtVsMult, HistTable), {Specs.at(kTrueKstarVsMtVsMult)}); + } + if (mPlotKstarVsMtVsMultVsCent) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsMtVsMultVsCent, HistTable), getHistDesc(kTrueKstarVsMtVsMultVsCent, HistTable), getHistType(kTrueKstarVsMtVsMultVsCent, HistTable), {Specs.at(kTrueKstarVsMtVsMultVsCent)}); + } + if (mPlotKstarVsMtVsPt1VsPt2) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsMtVsPt1VsPt2, HistTable), getHistDesc(kTrueKstarVsMtVsPt1VsPt2, HistTable), getHistType(kTrueKstarVsMtVsPt1VsPt2, HistTable), {Specs.at(kTrueKstarVsMtVsPt1VsPt2)}); + } + if (mPlotKstarVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsMtVsPt1VsPt2VsMult, HistTable), getHistDesc(kTrueKstarVsMtVsPt1VsPt2VsMult, HistTable), getHistType(kTrueKstarVsMtVsPt1VsPt2VsMult, HistTable), {Specs.at(kTrueKstarVsMtVsPt1VsPt2VsMult)}); + } + if (mPlotKstarVsMtVsPt1VsPt2VsMultVsCent) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, HistTable), getHistDesc(kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, HistTable), getHistType(kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, HistTable), {Specs.at(kTrueKstarVsMtVsPt1VsPt2VsMultVsCent)}); + } } void initSeMixingQa(std::map> const& Specs) { - std::string mcDir = std::string(prefix) + std::string(MixingQaDir); + std::string dir = std::string(prefix) + std::string(MixingQaDir); if (mPairCorrelationQa) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSeNpart1VsNpart2, HistTable), getHistDesc(kSeNpart1VsNpart2, HistTable), getHistType(kSeNpart1VsNpart2, HistTable), {Specs.at(kSeNpart1VsNpart2)}); + mHistogramRegistry->add(dir + getHistNameV2(kSeNpart1VsNpart2, HistTable), getHistDesc(kSeNpart1VsNpart2, HistTable), getHistType(kSeNpart1VsNpart2, HistTable), {Specs.at(kSeNpart1VsNpart2)}); } } void initMeMixingQa(std::map> const& Specs) { - std::string mcDir = std::string(prefix) + std::string(MixingQaDir); + std::string dir = std::string(prefix) + std::string(MixingQaDir); if (mPairCorrelationQa) { - mHistogramRegistry->add(mcDir + getHistNameV2(kMeMixingWindow, HistTable), getHistDesc(kMeMixingWindow, HistTable), getHistType(kMeMixingWindow, HistTable), {Specs.at(kMeMixingWindow)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMeNpart1VsNpart2, HistTable), getHistDesc(kMeNpart1VsNpart2, HistTable), getHistType(kMeNpart1VsNpart2, HistTable), {Specs.at(kMeNpart1VsNpart2)}); + mHistogramRegistry->add(dir + getHistNameV2(kMeMixingWindowRaw, HistTable), getHistDesc(kMeMixingWindowRaw, HistTable), getHistType(kMeMixingWindowRaw, HistTable), {Specs.at(kMeMixingWindowRaw)}); + mHistogramRegistry->add(dir + getHistNameV2(kMeMixingWindowEffective, HistTable), getHistDesc(kMeMixingWindowEffective, HistTable), getHistType(kMeMixingWindowEffective, HistTable), {Specs.at(kMeMixingWindowEffective)}); + mHistogramRegistry->add(dir + getHistNameV2(kMeNpart1VsNpart2, HistTable), getHistDesc(kMeNpart1VsNpart2, HistTable), getHistType(kMeNpart1VsNpart2, HistTable), {Specs.at(kMeNpart1VsNpart2)}); } if (mEventMixingQa) { - mHistogramRegistry->add(mcDir + getHistNameV2(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, HistTable), getHistDesc(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, HistTable), getHistType(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, HistTable), {Specs.at(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2)}); + mHistogramRegistry->add(dir + getHistNameV2(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, HistTable), getHistDesc(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, HistTable), getHistType(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2, HistTable), {Specs.at(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2)}); + } + } + + template + void setPairKinematics(T1 const& particle1, T2 const& particle2, T3 const& trackTable, T4 const& col) + { + // if one of the particles has a mass getter (like lambda), we cache the value for the filling later + // otherwise set it to the pdg mass + if constexpr (utils::HasMass) { + mRecoMass1 = particle1.mass(); + } else { + mRecoMass1 = mPdgMass1; + } + if constexpr (utils::HasMass) { + mRecoMass2 = particle2.mass(); + } else { + mRecoMass2 = mPdgMass2; + } + + // get mass for 4-vectors + double mass1 = 0.f; + double mass2 = 0.f; + if (mUsePdgMass) { + mass1 = mPdgMass1; + mass2 = mPdgMass2; + } else { + mass1 = mRecoMass1; + mass2 = mRecoMass2; + } + + // pt in track table is calculated from 1/signedPt from the original track table + // in case of He with Z=2, we have to rescale the pt with the absolute charge + mParticle1 = ROOT::Math::PtEtaPhiMVector(mAbsCharge1 * particle1.pt(), particle1.eta(), particle1.phi(), mass1); + mParticle2 = ROOT::Math::PtEtaPhiMVector(mAbsCharge2 * particle2.pt(), particle2.eta(), particle2.phi(), mass2); + + // set kT + mKt = femtomath::getKt(mParticle1, mParticle2); + + // set mT + mMt = femtomath::getMt(mParticle1, mParticle2, mMtType); + + // set Minv + mMassInv = femtomath::getMinv(mParticle1, mParticle2); + + // set kstar + mKstar = femtomath::getKstar(mParticle1, mParticle2); + + if (mPlotBertschPratt) { + std::tie(mQout, mQside, mQlong) = femtomath::computeBertschPrattLCMS(mParticle1, mParticle2); + } + + if (mPlotEventShape) { + if (!mPlotBertschPratt) { + std::tie(mQout, mQside, mQlong) = femtomath::computeBertschPrattLCMS(mParticle1, mParticle2); + } + if constexpr (utils::HasEventShape) { + mPairPhiFromEventPlaneAngle = femtomath::getPairPhiFromEventPlaneAngle(mParticle1, mParticle2, col); + mQvector = col.qvec(); + } else { + LOG(fatal) << "plotEventShape is enabled but the collision table has no event-shape columns. Breaking..."; + } + } + + if (mPlotSH) { + std::tie(mShKv, mShOut, mShSide, mShLong) = femtomath::computeShKinematics(mParticle1, mParticle2, mShFrame); + } + + if (mPlotDeltaEtaDeltaPhi) { + mDeltaEta = particle1.eta() - particle2.eta(); + mDeltaPhi = RecoDecay::constrainAngle(particle1.phi() - particle2.phi(), -o2::constants::math::PIHalf); + } + + if (mPlotAlpha || mPlotKstarVsAlphaVsMtVsMult || mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mAlpha = femtomath::getAlpha(mParticle1, mParticle2); + } + if (mPlotBetaLab || mPlotKstarVsBetaLabVsMtVsMult || mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mBetaLab = femtomath::getBetaLab(mParticle1, mParticle2); + } + + if (mPlotDalitz) { + if constexpr (modes::isEqual(particleType1, modes::Particle::kTrack) && (modes::isEqual(particleType2, modes::Particle::kV0) || modes::isEqual(particleType2, modes::Particle::kTwoTrackResonance) || modes::isEqual(particleType2, modes::Particle::kCharmHadron)) && + requires(T2 p) { p.posDauId(); p.negDauId(); }) { + auto posDaughter = trackTable.rawIteratorAt(particle2.posDauId() - trackTable.offset()); + auto negDaughter = trackTable.rawIteratorAt(particle2.negDauId() - trackTable.offset()); + ROOT::Math::PtEtaPhiMVector posDau4v = ROOT::Math::PtEtaPhiMVector(posDaughter.pt(), posDaughter.eta(), posDaughter.phi(), mPdgMassPosDau2); + ROOT::Math::PtEtaPhiMVector negDau4v = ROOT::Math::PtEtaPhiMVector(negDaughter.pt(), negDaughter.eta(), negDaughter.phi(), mPdgMassNegDau2); + mMassTot2 = (mParticle1 + posDau4v + negDau4v).M2(); + mMass12 = (mParticle1 + posDau4v).M2(); + mMass13 = (mParticle1 + negDau4v).M2(); + } } } @@ -773,6 +1401,9 @@ class PairHistManager // n-D histograms are only filled if enabled // if "mass" getter does not exist for particle, it will be just set to 0 // the user has to make sure that in this case the bin number of this dimension is set to 1 + if (mPlotKstarVsKtVsCent) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsKtVsCent, HistTable)), mKstar, mKt, mCent); + } if (mPlotKstarVsMtVsMult) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsMtVsMult, HistTable)), mKstar, mMt, mMult); } @@ -815,11 +1446,94 @@ class PairHistManager if (mPlotKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent, HistTable)), mKstar, mMt, mMassInv, mParticle1.Pt(), mParticle2.Pt(), mMult, mCent); } + if (mPlotKstarVsMinvVsKtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsMinvVsKtVsMult, HistTable)), mKstar, mMassInv, mKt, mMult); + } + if (mPlotKstarVsMtVsMinvVsKtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsMtVsMinvVsKtVsMult, HistTable)), mKstar, mMt, mMassInv, mKt, mMult); + } + if (mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult, HistTable)), mKstar, mMt, mMassInv, mKt, mParticle1.Pt(), mParticle2.Pt(), mMult); + } + if (mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent, HistTable)), mKstar, mMt, mMassInv, mKt, mParticle1.Pt(), mParticle2.Pt(), mMult, mCent); + } if (mPlotDalitz) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kDalitz, HistTable)), mKstar, mMassTot2, mMass12, mMass13); } + if (mPlotDeltaEtaDeltaPhi) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kDeltaEtaDeltaPhi, HistTable)), mDeltaPhi, mDeltaEta); + } + if (mPlotAlpha) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlpha, HistTable)), mAlpha); + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsKstar, HistTable)), mAlpha, mKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsKt, HistTable)), mAlpha, mKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kAlphaVsMt, HistTable)), mAlpha, mMt); + } + } + if (mPlotKstarVsAlphaVsMtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsAlphaVsMtVsMult, HistTable)), mKstar, mAlpha, mMt, mMult); + } + if (mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsAlphaVsMtVsPt1VsPt2VsMult, HistTable)), mKstar, mAlpha, mMt, mParticle1.Pt(), mParticle2.Pt(), mMult); + } + if (mPlotBetaLab) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLab, HistTable)), mBetaLab); + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsKstar, HistTable)), mBetaLab, mKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsKt, HistTable)), mBetaLab, mKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kBetaLabVsMt, HistTable)), mBetaLab, mMt); + } + } + if (mPlotKstarVsBetaLabVsMtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsBetaLabVsMtVsMult, HistTable)), mKstar, mBetaLab, mMt, mMult); + } + if (mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstarVsBetaLabVsMtVsPt1VsPt2VsMult, HistTable)), mKstar, mBetaLab, mMt, mParticle1.Pt(), mParticle2.Pt(), mMult); + } + if (mPlotBertschPratt) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQout, HistTable)), mQout); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQside, HistTable)), mQside); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQlong, HistTable)), mQlong); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQoutVsQsideVsQlong, HistTable)), mQout, mQside, mQlong); + } + if (mPlotEventShape) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQoutVsQsideVsQlongVsMtVsCentVsEventPlaneAngleVsQvector, HistTable)), mQout, mQside, mQlong, mMt, mCent, mPairPhiFromEventPlaneAngle, mQvector); + } + if (mPlotSH) { + const float shCentValue = mShUseCent ? mCent : mMult; + const int iCent = femtomath::findShBin(shCentValue, mShCentEdges); + const int iKt = femtomath::findShBin(mKt, mShKtEdges); + if (iCent >= 0 && iKt >= 0) { + mYlm.doYlmUpToL(mShLMax, mShOut, mShSide, mShLong, mShYlmBuffer.data()); + for (std::size_t i = 0; i < mShYlmBuffer.size(); ++i) { + mShReal[i]->Fill(mShKv, shCentValue, mKt, std::real(mShYlmBuffer[i])); + mShImag[i]->Fill(mShKv, shCentValue, mKt, -std::imag(mShYlmBuffer[i])); + } + // covariance: outer product of the (re, -im) Ylm vector packed on 2*nJM axes + // (each Ylm contributes two consecutive axis bins: even = real, odd = -imag) + static constexpr int ComponentsPerLM = 2; + const int nAxisLM = ComponentsPerLM * static_cast(mShYlmBuffer.size()); + for (int iz = 0; iz < nAxisLM; ++iz) { + const double vz = (iz % ComponentsPerLM == 0) ? std::real(mShYlmBuffer[iz / ComponentsPerLM]) : -std::imag(mShYlmBuffer[iz / ComponentsPerLM]); + for (int ip = 0; ip < nAxisLM; ++ip) { + const double vp = (ip % ComponentsPerLM == 0) ? std::real(mShYlmBuffer[ip / ComponentsPerLM]) : -std::imag(mShYlmBuffer[ip / ComponentsPerLM]); + mShCov[iCent][iKt]->Fill(mShKv, static_cast(iz), static_cast(ip), vz * vp); + } + } + + if (mShPlot1D) { + mShBinCount[iCent][iKt]->Fill(mShKv, 1.0); + // FemtoUniverse h1D = f3d[0]: qinv (=2k*) for identical-LCMS, else k*. + const float sh1DValue = (mShFrame == femtomath::ShFrameLcmsIdentical) ? (2.0f * mKstar) : mKstar; + mSh1D[iCent][iKt]->Fill(sh1DValue); + } + } + } } + // reco-vs-truth correlation fill (kReco and kMc both set) void fillMc() { if (mHasMcPair) { @@ -832,55 +1546,61 @@ class PairHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMultVsMult, HistTable)), mTrueMult, mMult); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueCentVsCent, HistTable)), mTrueCent, mCent); } - } - - float getKt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = (part1 + part2); - double kt = 0.5 * sum.Pt(); - return static_cast(kt); - } - - float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = part1 + part2; - double mt = 0; - double averageMass = 0; - double reducedMass = 0; - switch (mMtType) { - case modes::TransverseMassType::kAveragePdgMass: - averageMass = 0.5 * (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), averageMass); - break; - case modes::TransverseMassType::kReducedPdgMass: - reducedMass = 2. * (part1.M() * part2.M()) / (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), reducedMass); - break; - case modes::TransverseMassType::kMt4Vector: - mt = 0.5 * sum.Mt(); - break; - default: - LOG(fatal) << "Invalid transverse mass type, breaking..."; + if (mHasMcPair && mPlotBertschPratt) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQoutVsQout, HistTable)), mTrueQout, mQout); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQsideVsQside, HistTable)), mTrueQside, mQside); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQlongVsQlong, HistTable)), mTrueQlong, mQlong); } - return static_cast(mt); - } - - float getMinv(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - return static_cast((part1 + part2).M()); } - float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) + // pure mc-truth pair fill (kMc without kReco) — uses the same mTrue* members + // that setPairMcTruth() populated; no mHasMcPair/mHasMcCol gating needed since + // there is no missing-link case here (the particles ARE the truth particles) + void fillMcTruth() { - // compute pair momentum - auto sum = part1 + part2; - // Boost particle 1 to the pair rest frame (Prf) and calculate k* (would be equivalent using particle 2) - // make a copy of particle 1 - auto particle1Prf = ROOT::Math::PtEtaPhiMVector(part1); - // get lorentz boost into pair rest frame - ROOT::Math::Boost boostPrf(sum.BoostToCM()); - // boost particle 1 into pair rest frame and calculate its momentum, which has the same value as k* - return static_cast(boostPrf(particle1Prf).P()); + if (mPlot1d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar, HistTable)), mTrueKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKt, HistTable)), mTrueKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMt, HistTable)), mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMinv1D, HistTable)), mTrueMinv); + } + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt1VsPt2, HistTable)), mTrueParticle1.Pt(), mTrueParticle2.Pt()); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt1VsKstar, HistTable)), mTrueParticle1.Pt(), mTrueKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt2VsKstar, HistTable)), mTrueParticle2.Pt(), mTrueKstar); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt1VsKt, HistTable)), mTrueParticle1.Pt(), mTrueKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt2VsKt, HistTable)), mTrueParticle2.Pt(), mTrueKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt1VsMt, HistTable)), mTrueParticle1.Pt(), mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt2VsMt, HistTable)), mTrueParticle2.Pt(), mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsKt, HistTable)), mTrueKstar, mTrueKt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsMt, HistTable)), mTrueKstar, mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsMult, HistTable)), mTrueKstar, mTrueMult); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsCent, HistTable)), mTrueKstar, mTrueCent); + } + if (mPlotDeltaEtaDeltaPhi) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueDeltaEtaDeltaPhi, HistTable)), mTrueDeltaPhi, mTrueDeltaEta); + } + if (mPlotBertschPratt) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQout, HistTable)), mTrueQout); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQside, HistTable)), mTrueQside); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQlong, HistTable)), mTrueQlong); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQoutQsideQlong, HistTable)), mTrueQout, mTrueQside, mTrueQlong); + } + if (mPlotKstarVsMtVsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsMtVsMult, HistTable)), mTrueKstar, mTrueMt, mTrueMult); + } + if (mPlotKstarVsMtVsMultVsCent) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsMtVsMultVsCent, HistTable)), mTrueKstar, mTrueMt, mTrueMult, mTrueCent); + } + if (mPlotKstarVsMtVsPt1VsPt2) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsMtVsPt1VsPt2, HistTable)), mTrueKstar, mTrueMt, mTrueParticle1.Pt(), mTrueParticle2.Pt()); + } + if (mPlotKstarVsMtVsPt1VsPt2VsMult) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsMtVsPt1VsPt2VsMult, HistTable)), mTrueKstar, mTrueMt, mTrueParticle1.Pt(), mTrueParticle2.Pt(), mTrueMult); + } + if (mPlotKstarVsMtVsPt1VsPt2VsMultVsCent) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstarVsMtVsPt1VsPt2VsMultVsCent, HistTable)), mTrueKstar, mTrueMt, mTrueParticle1.Pt(), mTrueParticle2.Pt(), mTrueMult, mTrueCent); + } } o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; @@ -896,8 +1616,8 @@ class PairHistManager int mAbsCharge1 = 1; int mAbsCharge2 = 1; - ROOT::Math::PtEtaPhiMVector mParticle1{}; - ROOT::Math::PtEtaPhiMVector mParticle2{}; + ROOT::Math::PtEtaPhiMVector mParticle1; + ROOT::Math::PtEtaPhiMVector mParticle2; float mRecoMass1 = 0.f; float mRecoMass2 = 0.f; float mKstar = 0.f; @@ -909,16 +1629,25 @@ class PairHistManager double mMass12 = 0.; double mMass13 = 0.; double mMassTot2 = 0.; - - // mc - ROOT::Math::PtEtaPhiMVector mTrueParticle1{}; - ROOT::Math::PtEtaPhiMVector mTrueParticle2{}; + float mAlpha = 0.f; + float mBetaLab = 0.f; + + // mc (used for both reco-vs-truth correlation AND pure mc-truth-only pairs — + // for the latter, these are simply the primary/only kinematic values, not a + // "true" comparison against anything) + ROOT::Math::PtEtaPhiMVector mTrueParticle1; + ROOT::Math::PtEtaPhiMVector mTrueParticle2; float mTrueKstar = 0.f; float mTrueKt = 0.f; float mTrueMt = 0.f; float mTrueMinv = 0.f; float mTrueMult = 0.f; float mTrueCent = 0.f; + float mTrueQout = 0.f; + float mTrueQside = 0.f; + float mTrueQlong = 0.f; + float mTrueDeltaEta = 0.f; + float mTrueDeltaPhi = 0.f; // cuts bool mHasMcPair = false; @@ -936,6 +1665,7 @@ class PairHistManager bool mPlot1d = true; bool mPlot2d = true; + bool mPlotKstarVsKtVsCent = false; bool mPlotKstarVsMtVsMult = false; bool mPlotKstarVsMtVsMultVsCent = false; @@ -955,16 +1685,71 @@ class PairHistManager bool mPlotKstarVsMtVsMinvVsPt1VsPt2VsMult = false; bool mPlotKstarVsMtVsMinvVsPt1VsPt2VsMultVsCent = false; + bool mPlotKstarVsMinvVsKtVsMult = false; + bool mPlotKstarVsMtVsMinvVsKtVsMult = false; + bool mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMult = false; + bool mPlotKstarVsMtVsMinvVsKtVsPt1VsPt2VsMultVsCent = false; + bool mPlotDalitz = false; + bool mPlotDeltaEtaDeltaPhi = false; + + bool mPlotAlpha = false; + bool mPlotKstarVsAlphaVsMtVsMult = false; + bool mPlotKstarVsAlphaVsMtVsPt1VsPt2VsMult = false; + bool mPlotBetaLab = false; + bool mPlotKstarVsBetaLabVsMtVsMult = false; + bool mPlotKstarVsBetaLabVsMtVsPt1VsPt2VsMult = false; + + bool mPlotBertschPratt = false; + + bool mPlotEventShape = false; + + float mQout = 0.f; + float mQside = 0.f; + float mQlong = 0.f; + + float mDeltaEta = 0.f; + float mDeltaPhi = 0.f; + + float mQvector = 0.f; + float mPairPhiFromEventPlaneAngle = 0.f; + + // Spherical harmonics + bool mPlotSH = false; + bool mShUseCent = false; + int mShLMax = 1; + int mShFrame = 0; + + o2::framework::AxisSpec mShKstarSpec{{60, 0.0f, 0.3f}, "k* (GeV/#it{c})"}; // set in init() + o2::framework::AxisSpec mShCentSpec{{1, 0.0f, 200.0f}, "mult/cent"}; + o2::framework::AxisSpec mShKtSpec{{3, 0.1f, 0.4f}, "k_{T} (GeV/#it{c})"}; + + // kinematics computed in setPair(): axis value + 3 components feeding Ylm + float mShKv = 0.f; // kstar (non-identical) or qinv (identical) + float mShOut = 0.f; + float mShSide = 0.f; + float mShLong = 0.f; + + // SH histograms per [ihist] (ihist = l*(l+1)+m); TH3: k* on X, mult/cent on Y, kT on Z + std::vector> mShReal; + std::vector> mShImag; + // SH covariance matrix per [iCent][iKt]; TH3d: k* on X, 2*nJM (l,m x re/im) + std::vector>> mShCov; + bool mShPlot1D = false; + std::vector>> mSh1D; + std::vector>> mShBinCount; + std::vector> mShYlmBuffer; // reused, allocated once + std::vector mShCentEdges; + std::vector mShKtEdges; + + o2::analysis::femto::SpherHarMath mYlm{}; // qa bool mPairCorrelationQa = false; bool mEventMixingQa = false; - std::unordered_set mParticles1PerEvent = {}; - std::unordered_set mParticles2PerEvent = {}; + std::unordered_set mParticles1PerEvent; + std::unordered_set mParticles2PerEvent; }; - -}; // namespace pairhistmanager -}; // namespace o2::analysis::femto +}; // namespace o2::analysis::femto::pairhistmanager #endif // PWGCF_FEMTO_CORE_PAIRHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/pairProcessHelpers.h b/PWGCF/Femto/Core/pairProcessHelpers.h index 48b330b1be9..6a41c2095c8 100644 --- a/PWGCF/Femto/Core/pairProcessHelpers.h +++ b/PWGCF/Femto/Core/pairProcessHelpers.h @@ -23,11 +23,13 @@ #include #include +#include -namespace o2::analysis::femto -{ -namespace pairprocesshelpers +namespace o2::analysis::femto::pairprocesshelpers { +// NOTE: all processSameEvent helpers return true if at least one pair in the event passed +// the pair cleaner, the close pair rejection and the pair cuts (e.g. kstarMax). +// This allows to use them as a pair trigger. enum PairOrder : uint8_t { kOrder12, @@ -43,7 +45,7 @@ template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& Collision, T4& ParticleHistManager, @@ -53,20 +55,12 @@ void processSameEvent(T1 const& SliceParticle, PairOrder pairOrder) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; for (auto const& part : SliceParticle) { ParticleHistManager.template fill(part, TrackTable); } for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle, SliceParticle))) { - // check if pair is clean - if (!PcManager.isCleanPair(p1, p2, TrackTable)) { - continue; - } - // check if pair is close - CprManager.setPair(p1, p2, TrackTable); - if (CprManager.isClosePair()) { - continue; - } - // Randomize pair order if enabled + // Randomize pair order if enabled, then compute the kinematic (kstar) for this pair switch (pairOrder) { case kOrder12: PairHistManager.setPair(p1, p2, TrackTable, Collision); @@ -77,15 +71,23 @@ void processSameEvent(T1 const& SliceParticle, default: PairHistManager.setPair(p1, p2, TrackTable, Collision); } - // fill deta-dphi histograms with kstar cutoff - CprManager.fill(PairHistManager.getKstar()); + // check if pair is clean + if (!PcManager.isCleanPair(p1, p2, TrackTable, PairHistManager)) { + continue; + } + // check if pair is close; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, TrackTable, PairHistManager)) { + continue; + } // if pair cuts are configured check them before filling if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for identical particles with mc information @@ -102,7 +104,7 @@ template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& mcParticles, T4 const& mcMothers, @@ -117,11 +119,12 @@ void processSameEvent(T1 const& SliceParticle, PairOrder pairOrder) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; for (auto const& part : SliceParticle) { if (!ParticleCleaner.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - ParticleHistManager.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); + ParticleHistManager.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); } for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle, SliceParticle))) { // check if particles are clean @@ -129,16 +132,7 @@ void processSameEvent(T1 const& SliceParticle, !ParticleCleaner.isClean(p2, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - // check if pair is clean - if (!PcManager.isCleanPair(p1, p2, TrackTable, mcPartonicMothers)) { - continue; - } - // check if pair is close - CprManager.setPair(p1, p2, TrackTable); - if (CprManager.isClosePair()) { - continue; - } - // Randomize pair order if enabled + // Randomize pair order if enabled, then compute the kinematic (kstar) for this pair switch (pairOrder) { case kOrder12: PairHistManager.setPairMc(p1, p2, TrackTable, mcParticles, Collision, mcCollisions); @@ -149,15 +143,23 @@ void processSameEvent(T1 const& SliceParticle, default: PairHistManager.setPairMc(p1, p2, TrackTable, mcParticles, Collision, mcCollisions); } - // fill deta-dphi histograms with kstar cutoff - CprManager.fill(PairHistManager.getKstar()); + // check if pair is clean + if (!PcManager.isCleanPair(p1, p2, TrackTable, mcParticles, mcPartonicMothers, PairHistManager)) { + continue; + } + // check if pair is close; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, TrackTable, PairHistManager)) { + continue; + } // if pair cuts are configured check them before filling if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for non-identical particles @@ -171,7 +173,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& TrackTable, T4 const& Collision, @@ -182,6 +184,7 @@ void processSameEvent(T1 const& SliceParticle1, T9& PcManager) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; // Fill single particle histograms for (auto const& part : SliceParticle1) { ParticleHistManager1.template fill(part, TrackTable); @@ -190,23 +193,24 @@ void processSameEvent(T1 const& SliceParticle1, ParticleHistManager2.template fill(part, TrackTable); } for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(SliceParticle1, SliceParticle2))) { + // compute the kinematic (kstar) for this pair + PairHistManager.setPair(p1, p2, TrackTable, Collision); // pair cleaning - if (!PcManager.isCleanPair(p1, p2, TrackTable)) { + if (!PcManager.isCleanPair(p1, p2, TrackTable, PairHistManager)) { continue; } - // Close pair rejection - CprManager.setPair(p1, p2, TrackTable); - if (CprManager.isClosePair()) { + // Close pair rejection; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, TrackTable, PairHistManager)) { continue; } - PairHistManager.setPair(p1, p2, TrackTable, Collision); - CprManager.fill(PairHistManager.getKstar()); if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } // process same event for non-identical particles with mc information @@ -226,7 +230,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& TrackTable, T4 const& mcParticles, @@ -243,18 +247,19 @@ void processSameEvent(T1 const& SliceParticle1, T15& PcManager) { PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; // Fill single particle histograms for (auto const& part : SliceParticle1) { if (!ParticleCleaner1.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - ParticleHistManager1.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); + ParticleHistManager1.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); } for (auto const& part : SliceParticle2) { if (!ParticleCleaner2.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - ParticleHistManager2.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); + ParticleHistManager2.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); } for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(SliceParticle1, SliceParticle2))) { // check if particles are clean @@ -262,26 +267,26 @@ void processSameEvent(T1 const& SliceParticle1, !ParticleCleaner2.isClean(p2, mcParticles, mcMothers, mcPartonicMothers)) { continue; } + // compute the kinematic (kstar) for this pair + PairHistManager.setPairMc(p1, p2, TrackTable, mcParticles, Collision, mcCollisions); // pair cleaning - if (!PcManager.isCleanPair(p1, p2, TrackTable, mcPartonicMothers)) { + if (!PcManager.isCleanPair(p1, p2, TrackTable, mcParticles, mcPartonicMothers, PairHistManager)) { continue; } - // Close pair rejection - CprManager.setPair(p1, p2, TrackTable); - if (CprManager.isClosePair()) { + // Close pair rejection; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, TrackTable, PairHistManager)) { continue; } - PairHistManager.setPairMc(p1, p2, TrackTable, mcParticles, Collision, mcCollisions); - CprManager.fill(PairHistManager.getKstar()); if (PairHistManager.checkPairCuts()) { PairHistManager.template fill(); PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; } } PairHistManager.fillMixingQaSe(); + return foundPair; } - -// process mixed event +// process same event for identical particles, mc truth only (no track table, no reco collisions) template +bool processSameEvent(T1 const& SliceParticle, + T2 const& /*mcParticles*/, + T3 const& mcMothers, + T4 const& mcPartonicMothers, + T5 const& Collision, + T6& ParticleHistManager, + T7& PairHistManager, + T8& ParticleCleaner, + T9& CprManager, + T10& PcManager, + PairOrder pairOrder) +{ + PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; + for (auto const& part : SliceParticle) { + if (!ParticleCleaner.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager.fill(part, mcMothers, mcPartonicMothers); + } + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle, SliceParticle))) { + if (!ParticleCleaner.isClean(p1, mcMothers, mcPartonicMothers) || + !ParticleCleaner.isClean(p2, mcMothers, mcPartonicMothers)) { + continue; + } + // Randomize pair order if enabled, then compute the kinematic (kstar) for this pair + switch (pairOrder) { + case kOrder12: + PairHistManager.setPairMcTruth(p1, p2, Collision); + break; + case kOrder21: + PairHistManager.setPairMcTruth(p2, p1, Collision); + break; + default: + PairHistManager.setPairMcTruth(p1, p2, Collision); + } + if (!PcManager.isCleanPair(p1, p2, mcPartonicMothers, PairHistManager)) { + continue; + } + // Close pair rejection; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, PairHistManager)) { + continue; + } + if (PairHistManager.checkPairCutsMcTruth()) { + PairHistManager.template fill(); + PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; + } + } + PairHistManager.fillMixingQaSe(); + return foundPair; +} + +// process same event for non-identical particles, mc truth only +template +bool processSameEvent(T1 const& SliceParticle1, + T2 const& SliceParticle2, + T3 const& /*mcParticles*/, + T4 const& mcMothers, + T5 const& mcPartonicMothers, + T6 const& Collision, + T7& ParticleHistManager1, + T8& ParticleHistManager2, + T9& PairHistManager, + T10& ParticleCleaner1, + T11& ParticleCleaner2, + T12& CprManager, + T13& PcManager) +{ + PairHistManager.resetTrackedParticlesPerEvent(); + bool foundPair = false; + for (auto const& part : SliceParticle1) { + if (!ParticleCleaner1.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager1.fill(part, mcMothers, mcPartonicMothers); + } + for (auto const& part : SliceParticle2) { + if (!ParticleCleaner2.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager2.fill(part, mcMothers, mcPartonicMothers); + } + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(SliceParticle1, SliceParticle2))) { + if (!ParticleCleaner1.isClean(p1, mcMothers, mcPartonicMothers) || + !ParticleCleaner2.isClean(p2, mcMothers, mcPartonicMothers)) { + continue; + } + // compute the kinematic (kstar) for this pair + PairHistManager.setPairMcTruth(p1, p2, Collision); + if (!PcManager.isCleanPair(p1, p2, mcPartonicMothers, PairHistManager)) { + continue; + } + // Close pair rejection; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, PairHistManager)) { + continue; + } + if (PairHistManager.checkPairCutsMcTruth()) { + PairHistManager.template fill(); + PairHistManager.trackParticlesPerEvent(p1, p2); + foundPair = true; + } + } + PairHistManager.fillMixingQaSe(); + return foundPair; +} + +// mixed event in data +template void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -302,56 +441,109 @@ void processMixedEvent(T1 const& Collisions, T7 const& depth, T8& PairHistManager, T9& CprManager, - T10& PcManager) + T10& PcManager, + int eventStride, + T11& ParticleHistManagerMe1, + T12& ParticleHistManagerMe2) { - bool firstBin = true; - int windowSize = 0; - PairHistManager.resetTrackedParticlesPerEvent(); - auto pairGenerator = o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions); - for (auto pairIterator = pairGenerator.begin(); pairIterator != pairGenerator.end(); pairIterator++) { - auto const& [collision1, collision2] = *pairIterator; - if (pairIterator.isNewWindow()) { // is true for the first bin, so we skip - if (!firstBin) { - // we fill histograms after we finish processing one mixing bin - // the very first bin is considered a new window, so we skp it - PairHistManager.fillMixingQaMePerMixingBin(windowSize); + int64_t lastCollisionIndex = -1; + int windowSizeRaw = 0; + int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; + + // collision1 is fixed across each mixing window, so its track slice is + // materialized once per window and reused for every mixing partner, instead + // of being re-sliced (a fresh arrow Slice + selection copy, the dominant cost + // on the heaviest femto trains) on every (collision1, collision2) pair. + std::optionalsliceByCached(o2::aod::femtobase::stored::fColId, 0, cache))> sliceParticle1; + + for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { + + // --- new window --- + if (collision1.globalIndex() != lastCollisionIndex) { + if (lastCollisionIndex != -1 && !skipThisWindow) { + PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } + windowSizeRaw = 0; + windowSizeEffective = 0; + lastCollisionIndex = collision1.globalIndex(); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); } - windowSize = pairIterator.currentWindowNeighbours(); - firstBin = false; } + + if (skipThisWindow) { + continue; + } + + ++windowSizeRaw; + if (collision1.magField() != collision2.magField()) { - LOG(warn) << "Tried to mixng to events with different magntic field. This should not happen..."; + LOG(warn) << "Tried mixing events with different magnetic field."; continue; } + CprManager.setMagField(collision1.magField()); - auto sliceParticle1 = Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache); + auto sliceParticle2 = Partition2->sliceByCached(o2::aod::femtobase::stored::fColId, collision2.globalIndex(), cache); PairHistManager.resetTrackedParticlesPerEvent(); + + if (sliceParticle1->size() == 0 || sliceParticle2.size() == 0) { + PairHistManager.fillMixingQaMePerEvent(); + continue; + } + + bool hasValidPair = false; PairHistManager.fillMixingQaMe(collision1, collision2); - for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(sliceParticle1, sliceParticle2))) { - // pair cleaning - if (!PcManager.isCleanPair(p1, p2, TrackTable)) { + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(*sliceParticle1, sliceParticle2))) { + + // compute the kinematic (kstar) for this pair + PairHistManager.setPair(p1, p2, TrackTable, collision1, collision2); + + if (!PcManager.isCleanPair(p1, p2, TrackTable, PairHistManager)) { continue; } - // Close pair rejection - CprManager.setPair(p1, p2, TrackTable); - if (CprManager.isClosePair()) { + + // Close pair rejection; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, TrackTable, PairHistManager)) { continue; } - PairHistManager.setPair(p1, p2, TrackTable, collision1, collision2); - CprManager.fill(PairHistManager.getKstar()); + if (PairHistManager.checkPairCuts()) { - PairHistManager.template fill(); + hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); + PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event + // pair -- filled once per accepted pair (not deduplicated), so a particle contributes + // here exactly as often as it actually contributes to the mixed-event pair sample. + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); } } + + if (hasValidPair) { + ++windowSizeEffective; + } + PairHistManager.fillMixingQaMePerEvent(); } - PairHistManager.fillMixingQaMePerMixingBin(windowSize); // fill last window + + // --- final window --- + if (windowSizeRaw > 0) { + PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } } -// process mixed event with mc information +// process mixed event in mc template + typename T16, + typename T17, + typename T18> void processMixedEvent(T1 const& Collisions, T2 const& mcCollisions, T3& Partition1, @@ -384,69 +578,232 @@ void processMixedEvent(T1 const& Collisions, T13& ParticleCleaner1, T14& ParticleCleaner2, T15& CprManager, - T16& PcManager) + T16& PcManager, + int eventStride, + T17& ParticleHistManagerMe1, + T18& ParticleHistManagerMe2) { - bool firstBin = true; - int windowSize = 0; - PairHistManager.resetTrackedParticlesPerEvent(); - auto pairGenerator = o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions); - for (auto pairIterator = pairGenerator.begin(); pairIterator != pairGenerator.end(); ++pairIterator) { - auto const& [collision1, collision2] = *pairIterator; - // handle mixing windows (same as data) - if (pairIterator.isNewWindow()) { - if (!firstBin) { - PairHistManager.fillMixingQaMePerMixingBin(windowSize); + int64_t lastCollisionIndex = -1; + int windowSizeRaw = 0; + int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; + + // collision1 is fixed across each mixing window, so its track slice is + // materialized once per window and reused for every mixing partner, instead + // of being re-sliced (a fresh arrow Slice + selection copy, the dominant cost + // on the heaviest femto trains) on every (collision1, collision2) pair. + std::optionalsliceByCached(o2::aod::femtobase::stored::fColId, 0, cache))> sliceParticle1; + + for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { + if (collision1.globalIndex() != lastCollisionIndex) { + if (lastCollisionIndex != -1 && !skipThisWindow) { + PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } + windowSizeRaw = 0; + windowSizeEffective = 0; + lastCollisionIndex = collision1.globalIndex(); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); } - windowSize = pairIterator.currentWindowNeighbours(); - firstBin = false; } + + if (skipThisWindow) { + continue; + } + + ++windowSizeRaw; + if (collision1.magField() != collision2.magField()) { - LOG(warn) << "Tried to mixng to events with different magntic field. This should not happen..."; + LOG(warn) << "Tried mixing events with different magnetic field."; continue; } + CprManager.setMagField(collision1.magField()); - auto sliceParticle1 = Partition1->sliceByCached( - o2::aod::femtobase::stored::fColId, - collision1.globalIndex(), - cache); - auto sliceParticle2 = Partition2->sliceByCached( - o2::aod::femtobase::stored::fColId, - collision2.globalIndex(), - cache); + auto sliceParticle2 = Partition2->sliceByCached(o2::aod::femtobase::stored::fColId, collision2.globalIndex(), cache); PairHistManager.resetTrackedParticlesPerEvent(); + + if (sliceParticle1->size() == 0 || sliceParticle2.size() == 0) { + PairHistManager.fillMixingQaMePerEvent(); + continue; + } + + bool hasValidPair = false; PairHistManager.fillMixingQaMe(collision1, collision2); - for (auto const& [p1, p2] : - o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(sliceParticle1, sliceParticle2))) { - // particle cleaning (MC-specific) + + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(*sliceParticle1, sliceParticle2))) { + if (!ParticleCleaner1.isClean(p1, mcParticles, mcMothers, mcPartonicMothers) || !ParticleCleaner2.isClean(p2, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - // pair cleaning - if (!PcManager.isCleanPair(p1, p2, TrackTable)) { + + // compute the kinematic (kstar) for this pair + PairHistManager.setPairMc(p1, p2, TrackTable, mcParticles, collision1, collision2, mcCollisions); + + if (!PcManager.isCleanPair(p1, p2, TrackTable, PairHistManager)) { continue; } - // close pair rejection - CprManager.setPair(p1, p2, TrackTable); - if (CprManager.isClosePair()) { + + // Close pair rejection; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, TrackTable, PairHistManager)) { continue; } - PairHistManager.setPairMc(p1, p2, TrackTable, mcParticles, collision1, collision2, mcCollisions); - CprManager.fill(PairHistManager.getKstar()); + if (PairHistManager.checkPairCuts()) { + hasValidPair = true; + PairHistManager.trackParticlesPerEvent(p1, p2); PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event pair + ParticleHistManagerMe1.template fill(p1, TrackTable, collision1, mcParticles, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.template fill(p2, TrackTable, collision2, mcParticles, mcMothers, mcPartonicMothers); + } + } + + if (hasValidPair) { + ++windowSizeEffective; + } + + PairHistManager.fillMixingQaMePerEvent(); + } + + if (windowSizeRaw > 0) { + PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } +} +// process mixed event, mc truth only (no track table, collisions already mc truth so no separate mcCollisions) +template +void processMixedEvent(T1 const& Collisions, + T2& Partition1, + T3& Partition2, + T4 const& /*mcParticles*/, + T5 const& mcMothers, + T6 const& mcPartonicMothers, + T7& cache, + T8 const& policy, + T9 const& depth, + T10& PairHistManager, + T11& ParticleCleaner1, + T12& ParticleCleaner2, + T13& CprManager, + T14& PcManager, + int eventStride, + T15& ParticleHistManagerMe1, + T16& ParticleHistManagerMe2) +{ + int64_t lastCollisionIndex = -1; + int windowSizeRaw = 0; + int windowSizeEffective = 0; + // counts actual anchor windows encountered (not raw collision IDs, which may have gaps from + // upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; + + std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; + + for (auto const& [collision1, collision2] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions)) { + + if (collision1.globalIndex() != lastCollisionIndex) { + if (lastCollisionIndex != -1 && !skipThisWindow) { + PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } + windowSizeRaw = 0; + windowSizeEffective = 0; + lastCollisionIndex = collision1.globalIndex(); + ++windowCounter; + // skip entire anchor windows to reduce mixed-event combinatorics without truncating any + // single event's particle content -- every anchor that IS processed still gets its full + // `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; + } + + ++windowSizeRaw; + + auto sliceParticle2 = Partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision2.globalIndex(), cache); + + PairHistManager.resetTrackedParticlesPerEvent(); + + if (sliceParticle1->size() == 0 || sliceParticle2.size() == 0) { + PairHistManager.fillMixingQaMePerEvent(); + continue; + } + + bool hasValidPair = false; + PairHistManager.fillMixingQaMe(collision1, collision2); + + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(*sliceParticle1, sliceParticle2))) { + + if (!ParticleCleaner1.isClean(p1, mcMothers, mcPartonicMothers) || + !ParticleCleaner2.isClean(p2, mcMothers, mcPartonicMothers)) { + continue; + } + + // compute the kinematic (kstar) for this pair + PairHistManager.setPairMcTruth(p1, p2, collision1, collision2); + + if (!PcManager.isCleanPair(p1, p2, mcPartonicMothers, PairHistManager)) { + continue; + } + + // Close pair rejection; fills deta-dphi/kinematic histograms internally + if (CprManager.isClosePair(p1, p2, PairHistManager)) { + continue; + } + + if (PairHistManager.checkPairCutsMcTruth()) { + hasValidPair = true; PairHistManager.trackParticlesPerEvent(p1, p2); + PairHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event pair + ParticleHistManagerMe1.fill(p1, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.fill(p2, mcMothers, mcPartonicMothers); } } + + if (hasValidPair) { + ++windowSizeEffective; + } + PairHistManager.fillMixingQaMePerEvent(); } - // final window - PairHistManager.fillMixingQaMePerMixingBin(windowSize); + + if (windowSizeRaw > 0) { + PairHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } } -} // namespace pairprocesshelpers -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::pairprocesshelpers #endif // PWGCF_FEMTO_CORE_PAIRPROCESSHELPERS_H_ diff --git a/PWGCF/Femto/Core/particleCleaner.h b/PWGCF/Femto/Core/particleCleaner.h index 045f3dea785..f284385cce9 100644 --- a/PWGCF/Femto/Core/particleCleaner.h +++ b/PWGCF/Femto/Core/particleCleaner.h @@ -21,12 +21,9 @@ #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::particlecleaner { -namespace particlecleaner -{ - -template +template struct ConfParticleCleaner : o2::framework::ConfigurableGroup { std::string prefix = std::string(Prefix); o2::framework::Configurable activate{"activate", false, "Activate particle cleaner"}; @@ -34,11 +31,11 @@ struct ConfParticleCleaner : o2::framework::ConfigurableGroup { o2::framework::Configurable> rejectedPdgCodes{"rejectedPdgCodes", {}, "Reject particles with this exact pdg code (including the sign!)"}; o2::framework::Configurable rejectParticleWithoutMcParticle{"rejectParticleWithoutMcParticle", false, "If true, particles which have no associated MC information, are rejected"}; o2::framework::Configurable rejectParticleWithoutMcMother{"rejectParticleWithoutMcMother", false, "If true, particles which have no associated mother are rejected"}; - o2::framework::Configurable rejectParticleWithoutMcPartonicMother{"rejectParticleWithoutMcPartonicMother", false, "If true, all particles which have no associated partonic mother"}; - o2::framework::Configurable> requiredMotherPdgCodes{"requiredMotherPdgCodes", {}, "Only consider particles whose mothers have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> rejectMotherPdgCodes{"rejectMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> requiredPartonicMotherPdgCodes{"requiredPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers have one of the supplied pdg codes (inclduing the sign!)"}; - o2::framework::Configurable> rejectPartonicMotherPdgCodes{"rejectPartonicMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (inclduing the sign!)"}; + o2::framework::Configurable rejectParticleWithoutMcPartonicMother{"rejectParticleWithoutMcPartonicMother", false, "If true, particles which have no associated partonic mother are rejected"}; + o2::framework::Configurable> requiredMotherPdgCodes{"requiredMotherPdgCodes", {}, "Only consider particles whose mothers have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> rejectMotherPdgCodes{"rejectMotherPdgCodes", {}, "Only consider particles whose mothers do not have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> requiredPartonicMotherPdgCodes{"requiredPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers have one of the supplied pdg codes (including the sign!)"}; + o2::framework::Configurable> rejectPartonicMotherPdgCodes{"rejectPartonicMotherPdgCodes", {}, "Only consider particles whose partonic mothers do not have one of the supplied pdg codes (including the sign!)"}; }; constexpr const char PrefixTrackCleaner1[] = "TrackCleaner1"; @@ -53,6 +50,14 @@ constexpr const char PrefixLambdaCleaner2[] = "LambdaCleaner2"; using ConfLambdaCleaner1 = ConfParticleCleaner; using ConfLambdaCleaner2 = ConfParticleCleaner; +constexpr const char PrefixD0Cleaner1[] = "D0Cleaner1"; +constexpr const char PrefixD0Cleaner2[] = "D0Cleaner2"; +using ConfD0Cleaner1 = ConfParticleCleaner; +using ConfD0Cleaner2 = ConfParticleCleaner; + +constexpr const char PrefixLcCleaner1[] = "LcCleaner1"; +using ConfLcCleaner1 = ConfParticleCleaner; + constexpr const char PrefixK0shortCleaner1[] = "K0shortCleaner1"; constexpr const char PrefixK0shortCleaner2[] = "K0shortCleaner2"; using ConfK0shortCleaner1 = ConfParticleCleaner; @@ -68,6 +73,11 @@ constexpr const char PrefixSigmaPlusCleaner2[] = "SigmaPlusCleaner2"; using ConfSigmaPlusCleaner1 = ConfParticleCleaner; using ConfSigmaPlusCleaner2 = ConfParticleCleaner; +constexpr const char PrefixSigmaPlusPcmCleaner1[] = "SigmaPlusPcmCleaner1"; +constexpr const char PrefixSigmaPlusPcmCleaner2[] = "SigmaPlusPcmCleaner2"; +using ConfSigmaPlusPcmCleaner1 = ConfParticleCleaner; +using ConfSigmaPlusPcmCleaner2 = ConfParticleCleaner; + constexpr const char PrefixXiCleaner1[] = "XiCleaner1"; constexpr const char PrefixXiCleaner2[] = "XiCleaner2"; using ConfXiCleaner1 = ConfParticleCleaner; @@ -78,6 +88,13 @@ constexpr const char PrefixOmegaCleaner2[] = "OmegaCleaner2"; using ConfOmegaCleaner1 = ConfParticleCleaner; using ConfOmegaCleaner2 = ConfParticleCleaner; +constexpr const char PrefixMcParticleCleaner1[] = "McParticleCleaner1"; +constexpr const char PrefixMcParticleCleaner2[] = "McParticleCleaner2"; +constexpr const char PrefixMcParticleCleaner3[] = "McParticleCleaner3"; +using ConfMcParticleCleaner1 = ConfParticleCleaner; +using ConfMcParticleCleaner2 = ConfParticleCleaner; +using ConfMcParticleCleaner3 = ConfParticleCleaner; + class ParticleCleaner { public: @@ -103,16 +120,40 @@ class ParticleCleaner mRejectedPartonicMotherPdgCodes = confMpc.rejectPartonicMotherPdgCodes.value; } + /// mc + reco template bool isClean(T1 const& particle, T2 const& /*mcParticles*/, - T3 const& /*mcMothers*/, - T4 const& /*mcPartonicMothers*/) + T3 const& mcMothers, + T4 const& mcPartonicMothers) { if (!mActivate) { return true; } + // No MC particle at all → no mother/partonic-mother info is reachable either, + // since that lookup now goes through the mc particle row. + if (!particle.has_fMcParticle()) { + return !mRejectParticleWithoutMcParticle && mRequiredPdgCodes.empty() && + !mRejectParticleWithoutMcMother && mRequiredMotherPdgCodes.empty() && + !mRejectParticleWithoutMcPartonicMother && mRequiredPartonicMotherPdgCodes.empty(); + } + + auto mcParticle = particle.template fMcParticle_as(); + return this->isClean(mcParticle, mcMothers, mcPartonicMothers); + } + + // mc only + template + bool isClean(T1 const& mcParticle, + T2 const& /*mcMothers*/, + T3 const& /*mcPartonicMothers*/) + { + + if (!mActivate) { + return true; + } + bool hasRequiredPdgCode = true; bool hasRejectedPdgCode = false; @@ -123,40 +164,32 @@ class ParticleCleaner bool hasPartonicMotherWithRejectedPdgCode = false; // MC particle - if (!particle.has_fMcParticle()) { - if (mRejectParticleWithoutMcParticle || !mRequiredPdgCodes.empty()) { - return false; - } - } else { - auto mcParticle = particle.template fMcParticle_as(); - - if (!mRequiredPdgCodes.empty()) { - hasRequiredPdgCode = false; - for (int const& pdgCode : mRequiredPdgCodes) { - if (pdgCode == mcParticle.pdgCode()) { - hasRequiredPdgCode = true; - break; - } + if (!mRequiredPdgCodes.empty()) { + hasRequiredPdgCode = false; + for (int const& pdgCode : mRequiredPdgCodes) { + if (pdgCode == mcParticle.pdgCode()) { + hasRequiredPdgCode = true; + break; } } + } - if (!mRejectedPdgCodes.empty()) { - for (int const& pdgCode : mRejectedPdgCodes) { - if (pdgCode == mcParticle.pdgCode()) { - hasRejectedPdgCode = true; - break; - } + if (!mRejectedPdgCodes.empty()) { + for (int const& pdgCode : mRejectedPdgCodes) { + if (pdgCode == mcParticle.pdgCode()) { + hasRejectedPdgCode = true; + break; } } } - // MC mother - if (!particle.has_fMcMother()) { + // MC mother — looked up via mcParticle (T2 must be Join) + if (!mcParticle.has_fMcMother()) { if (mRejectParticleWithoutMcMother || !mRequiredMotherPdgCodes.empty()) { return false; } } else { - auto mother = particle.template fMcMother_as(); + auto mother = mcParticle.template fMcMother_as(); if (!mRequiredMotherPdgCodes.empty()) { hasMotherWithRequiredPdgCode = false; @@ -178,14 +211,14 @@ class ParticleCleaner } } - // MC partonic mother - if (!particle.has_fMcPartMoth()) { + // MC partonic mother — same idea, via mcParticle + if (!mcParticle.has_fMcPartMoth()) { if (mRejectParticleWithoutMcPartonicMother || !mRequiredPartonicMotherPdgCodes.empty()) { return false; } } else { - auto partonicMother = particle.template fMcPartMoth_as(); + auto partonicMother = mcParticle.template fMcPartMoth_as(); if (!mRequiredPartonicMotherPdgCodes.empty()) { hasPartonicMotherWithRequiredPdgCode = false; @@ -209,24 +242,22 @@ class ParticleCleaner return hasRequiredPdgCode && !hasRejectedPdgCode && hasMotherWithRequiredPdgCode && !hasMotherWithRejectedPdgCode && - hasPartonicMotherWithRequiredPdgCode && - !hasPartonicMotherWithRejectedPdgCode; + hasPartonicMotherWithRequiredPdgCode && !hasPartonicMotherWithRejectedPdgCode; } private: bool mActivate = false; - bool mRejectParticleWithoutMcParticle = true; - bool mRejectParticleWithoutMcMother = true; - bool mRejectParticleWithoutMcPartonicMother = true; - std::vector mRequiredPdgCodes = {}; - std::vector mRejectedPdgCodes = {}; - std::vector mRequiredMotherPdgCodes{}; - std::vector mRejectedMotherPdgCodes{}; - std::vector mRequiredPartonicMotherPdgCodes{}; - std::vector mRejectedPartonicMotherPdgCodes{}; + bool mRejectParticleWithoutMcParticle = false; + bool mRejectParticleWithoutMcMother = false; + bool mRejectParticleWithoutMcPartonicMother = false; + std::vector mRequiredPdgCodes; + std::vector mRejectedPdgCodes; + std::vector mRequiredMotherPdgCodes; + std::vector mRejectedMotherPdgCodes; + std::vector mRequiredPartonicMotherPdgCodes; + std::vector mRejectedPartonicMotherPdgCodes; }; -} // namespace particlecleaner -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::particlecleaner #endif // PWGCF_FEMTO_CORE_PARTICLECLEANER_H_ diff --git a/PWGCF/Femto/Core/partitions.h b/PWGCF/Femto/Core/partitions.h index aac05f3ea91..6a3a473ecf9 100644 --- a/PWGCF/Femto/Core/partitions.h +++ b/PWGCF/Femto/Core/partitions.h @@ -16,140 +16,299 @@ #ifndef PWGCF_FEMTO_CORE_PARTITIONS_H_ #define PWGCF_FEMTO_CORE_PARTITIONS_H_ +#include + // collsion selection -#define MAKE_COLLISION_FILTER(selection) \ - (o2::aod::femtocollisions::posZ >= selection.vtxZMin && o2::aod::femtocollisions::posZ <= selection.vtxZMax) && \ - (o2::aod::femtocollisions::mult >= selection.multMin && o2::aod::femtocollisions::mult <= selection.multMax) && \ - (o2::aod::femtocollisions::cent >= selection.centMin && o2::aod::femtocollisions::cent <= selection.centMax) && \ - (o2::aod::femtocollisions::magField >= o2::framework::expressions::as(selection.magFieldMin) && \ - o2::aod::femtocollisions::magField <= o2::framework::expressions::as(selection.magFieldMax)) && \ - ncheckbit(o2::aod::femtocollisions::mask, selection.collisionMask) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_COLLISION_FILTER(selection) \ + (o2::aod::femtocollisions::posZ >= (selection).vtxZMin && o2::aod::femtocollisions::posZ <= (selection).vtxZMax) && \ + (o2::aod::femtocollisions::mult >= (selection).multMin && o2::aod::femtocollisions::mult <= (selection).multMax) && \ + (o2::aod::femtocollisions::cent >= (selection).centMin && o2::aod::femtocollisions::cent <= (selection).centMax) && \ + (o2::aod::femtocollisions::magField >= o2::framework::expressions::as((selection).magFieldMin) && \ + o2::aod::femtocollisions::magField <= o2::framework::expressions::as((selection).magFieldMax)) && \ + ncheckbit(o2::aod::femtocollisions::mask, (selection).collisionMask) // macro for track momentum, i.e. ||q|*pT/q| * cosh(eta) // there is no ncosh function, so we have to make our own, i.e. cosh(x) = (exp(x)+exp(-x))/2 +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define TRACK_MOMENTUM(chargeAbs, signedPt, eta) (nabs((chargeAbs) * (signedPt)) * (nexp(eta) + nexp(-1.f * (eta))) / 2.f) // standard track partition -#define MAKE_TRACK_PARTITION(selection) \ - ifnode(selection.chargeSign.node() != 0, ifnode(selection.chargeSign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ - (nabs(selection.chargeAbs * o2::aod::femtobase::stored::signedPt) > selection.ptMin) && \ - (nabs(selection.chargeAbs * o2::aod::femtobase::stored::signedPt) < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - ifnode(TRACK_MOMENTUM(selection.chargeAbs, o2::aod::femtobase::stored::signedPt, o2::aod::femtobase::stored::eta) <= selection.pidThres, \ - ncheckbit(o2::aod::femtotracks::mask, selection.maskLowMomentum) && \ - (o2::aod::femtotracks::mask & selection.rejectionMaskLowMomentum) == static_cast(0), \ - ncheckbit(o2::aod::femtotracks::mask, selection.maskHighMomentum) && \ - (o2::aod::femtotracks::mask & selection.rejectionMaskHighMomentum) == static_cast(0)) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TRACK_PARTITION(selection) \ + ifnode((selection).chargeSign.node() != 0, ifnode((selection).chargeSign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ifnode(TRACK_MOMENTUM((selection).chargeAbs, o2::aod::femtobase::stored::signedPt, o2::aod::femtobase::stored::eta) <= (selection).pidThres, \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskLowMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskLowMomentum) == static_cast(0), \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskHighMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskHighMomentum) == static_cast(0)) // track partition with optional mass cut -#define MAKE_TRACK_PARTITION_WITH_MASS(selection) \ - MAKE_TRACK_PARTITION(selection) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) - -// partition for phis and rhos, i.e. resonance that are their own antiparticle -#define MAKE_RESONANCE_0_PARTITON(selection) \ - (o2::aod::femtobase::stored::pt > selection.ptMin) && \ - (o2::aod::femtobase::stored::pt < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) && \ - ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, selection.posDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, selection.posDauMaskBelowThres)) && \ - ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, selection.negDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, selection.negDauMaskBelowThres)) - -// partition for kstars, they have distinct antiparticle -#define MAKE_RESONANCE_1_PARTITON(selection) \ - ifnode(selection.sign.node() != 0, \ - ifnode(selection.sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ - (nabs(o2::aod::femtobase::stored::signedPt) > selection.ptMin) && \ - (nabs(o2::aod::femtobase::stored::signedPt) < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) && \ - ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, selection.posDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, selection.posDauMaskBelowThres)) && \ - ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, selection.negDauMaskAboveThres), \ - ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, selection.negDauMaskBelowThres)) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TRACK_PARTITION_WITH_MASS(selection) \ + MAKE_TRACK_PARTITION((selection)) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) + +// partition for resonance daughters (pion+/-, kaon+/- legs): unlike MAKE_TRACK_PARTITION, chargeSign +// and chargeAbs are compile-time constants on the selection struct (ConfResonanceDaughterSelection in +// trackBuilder.h), not runtime Configurables, so the sign comparison is just a comparison to a constant +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_RESONANCE_DAUGHTER_PARTITION(selection) \ + ((selection).chargeSign > 0 ? o2::aod::femtobase::stored::signedPt > 0.f : o2::aod::femtobase::stored::signedPt < 0.f) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs((selection).chargeAbs * o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ifnode(TRACK_MOMENTUM((selection).chargeAbs, o2::aod::femtobase::stored::signedPt, o2::aod::femtobase::stored::eta) <= (selection).pidThres, \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskLowMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskLowMomentum) == static_cast(0), \ + ncheckbit(o2::aod::femtotracks::mask, (selection).maskHighMomentum) && \ + (o2::aod::femtotracks::mask & (selection).rejectionMaskHighMomentum) == static_cast(0)) + +// partition for phis and rhos, i.e. resonance that are their own antiparticle -- both daughters are +// the same species, so the selection carries a single dauMask pair applied to both stored columns +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_RESONANCE_0_PARTITON(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).dauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).dauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).dauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).dauMaskBelowThres)) + +// partition for kstars, they have distinct antiparticle -- K*0 (K+ pi-) and K*0bar (K- pi+) swap +// which physical daughter (pos/neg) is the kaon vs the pion, so the selection carries masks named by +// species (kaon/pion) and this macro resolves pos/neg <-> kaon/pion from the candidate's own +// signedPt, the same way it's reconstructed (no need for the user to reason about this downstream) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_RESONANCE_1_PARTITON(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtobase::stored::signedPt > 0.f, \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).kaonDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).kaonDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).pionDauMaskBelowThres)), \ + ifnode(o2::aod::femtotwotrackresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskPosDau, (selection).pionDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotwotrackresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).kaonDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotwotrackresonances::maskNegDau, (selection).kaonDauMaskBelowThres))) + +// partition for resonances built from two photons (pi0, eta, ...): unlike MAKE_RESONANCE_0/1_PARTITON +// there is no sign and no momentum-threshold PID switch, and just a single mask check -- both +// daughters are plain photons with the same selection requirement, so the candidate only stores the +// AND of both legs' own masks (see femtotwophotonresonances::MaskDau) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtotwophotonresonances::maskDau, (selection).dauMask) + +// partition for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> +// pi+ pi- pi0): same pattern as MAKE_RESONANCE_0_PARTITON (both charged daughters are the same +// species -- pions, just opposite charge, no kstar-style species swap -- so a single dauMask pair +// covers both), plus a 3rd mask for the neutral daughter (no momentum-threshold split there) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::posDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskPosDau, (selection).pionDauMaskBelowThres)) && \ + ifnode(o2::aod::femtotrackstwophotonresonances::negDauHasHighMomentum, \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).pionDauMaskAboveThres), \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNegDau, (selection).pionDauMaskBelowThres)) && \ + ncheckbit(o2::aod::femtotrackstwophotonresonances::maskNeutralDau, (selection).neutralDauMask) // partition for lambdas -#define MAKE_LAMBDA_PARTITION(selection) \ - ifnode(selection.sign.node() != 0, \ - ifnode(selection.sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ - (nabs(o2::aod::femtobase::stored::signedPt) > selection.ptMin) && \ - (nabs(o2::aod::femtobase::stored::signedPt) < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) && \ - ncheckbit(o2::aod::femtov0s::mask, selection.mask) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_LAMBDA_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtov0s::mask, (selection).mask) // partition for k0shorts // need special partition since k0shorts have no antiparticle -#define MAKE_K0SHORT_PARTITION(selection) \ - (o2::aod::femtobase::stored::pt > selection.ptMin) && \ - (o2::aod::femtobase::stored::pt < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) && \ - ncheckbit(o2::aod::femtov0s::mask, selection.mask) - -#define MAKE_CASCADE_PARTITION(selection) \ - ifnode(selection.sign.node() != 0, \ - ifnode(selection.sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ - (nabs(o2::aod::femtobase::stored::signedPt) > selection.ptMin) && \ - (nabs(o2::aod::femtobase::stored::signedPt) < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) && \ - ncheckbit(o2::aod::femtocascades::mask, selection.mask) - -#define MAKE_SIGMA_PARTITION(selection) \ - ifnode(selection.sign.node() != 0, \ - ifnode(selection.sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ - (nabs(o2::aod::femtobase::stored::signedPt) > selection.ptMin) && \ - (nabs(o2::aod::femtobase::stored::signedPt) < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) && \ - ncheckbit(o2::aod::femtokinks::mask, selection.mask) - -#define MAKE_SIGMAPLUS_PARTITION(selection) \ - ifnode(selection.sign.node() != 0, \ - ifnode(selection.sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ - (nabs(o2::aod::femtobase::stored::signedPt) > selection.ptMin) && \ - (nabs(o2::aod::femtobase::stored::signedPt) < selection.ptMax) && \ - (o2::aod::femtobase::stored::eta > selection.etaMin) && \ - (o2::aod::femtobase::stored::eta < selection.etaMax) && \ - (o2::aod::femtobase::stored::phi > selection.phiMin) && \ - (o2::aod::femtobase::stored::phi < selection.phiMax) && \ - (o2::aod::femtobase::stored::mass > selection.massMin) && \ - (o2::aod::femtobase::stored::mass < selection.massMax) && \ - ncheckbit(o2::aod::femtokinks::mask, selection.mask) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_K0SHORT_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtov0s::mask, (selection).mask) + +// partition for photons (PCM) +// no sign/mass handling: photons are their own antiparticle and have no mass window +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_PHOTON_PARTITION(selection) \ + (o2::aod::femtobase::stored::pt > (selection).ptMin) && \ + (o2::aod::femtobase::stored::pt < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + ncheckbit(o2::aod::femtophotons::mask, (selection).mask) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_CASCADE_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtocascades::mask, (selection).mask) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_SIGMA_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtokinks::mask, (selection).mask) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_SIGMAPLUS_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtokinks::mask, (selection).mask) + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_SIGMAPLUSPCM_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + (o2::aod::femtokinks::mlScore >= (selection).mlScoreMin) && \ + ncheckbit(o2::aod::femtokinks::mask, (selection).mask) + +// partition for D0 +// sign of signedPt encodes the hypothesis: D0 (+) / D0bar (-) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_D0_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtocharmhadrons::mask, (selection).mask) + +// partition for 3-prong charm hadrons +// sign of signedPt encodes particle (+) / antiparticle (-) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_CHARM3PRONG_PARTITION(selection) \ + ifnode((selection).sign.node() != 0, \ + ifnode((selection).sign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) && \ + (o2::aod::femtobase::stored::mass > (selection).massMin) && \ + (o2::aod::femtobase::stored::mass < (selection).massMax) && \ + ncheckbit(o2::aod::femtocharmhadrons::mask, (selection).mask) + +// macros for mc collisions (mc only) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_MC_COLLISION_FILTER(selection) \ + (o2::aod::femtocollisions::posZ >= (selection).vtxZMin && o2::aod::femtocollisions::posZ <= (selection).vtxZMax) && \ + (o2::aod::femtocollisions::mult >= (selection).multMin && o2::aod::femtocollisions::mult <= (selection).multMax) && \ + (o2::aod::femtocollisions::cent >= (selection).centMin && o2::aod::femtocollisions::cent <= (selection).centMax) + +// macros for mc particle (mc only) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define MAKE_MC_PARTICLE_PARTITION(selection) \ + ifnode((selection).requireOrigin.node(), o2::aod::femtomcparticle::origin == o2::framework::expressions::as((selection).origin), true) && \ + ifnode((selection).pdgCodeAbs.node() == 0, true, (selection).pdgCodeAbs == nabs(o2::aod::femtomcparticle::pdgCode)) && \ + ifnode((selection).chargeSign.node() != 0, ifnode((selection).chargeSign.node() > 0, o2::aod::femtobase::stored::signedPt > 0.f, o2::aod::femtobase::stored::signedPt < 0.f), true) && \ + (nabs(o2::aod::femtobase::stored::signedPt) > (selection).ptMin) && \ + (nabs(o2::aod::femtobase::stored::signedPt) < (selection).ptMax) && \ + (o2::aod::femtobase::stored::eta > (selection).etaMin) && \ + (o2::aod::femtobase::stored::eta < (selection).etaMax) && \ + (o2::aod::femtobase::stored::phi > (selection).phiMin) && \ + (o2::aod::femtobase::stored::phi < (selection).phiMax) #endif // PWGCF_FEMTO_CORE_PARTITIONS_H_ diff --git a/PWGCF/Femto/Core/photonBuilder.h b/PWGCF/Femto/Core/photonBuilder.h new file mode 100644 index 00000000000..d820c6dca72 --- /dev/null +++ b/PWGCF/Femto/Core/photonBuilder.h @@ -0,0 +1,413 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonBuilder.h +/// \brief photon (PCM) builder +/// +/// Default selection values for the mandatory cuts (dauTpcNSigmaElAbsMax, keepDaughtersWithoutTpc) +/// are taken from the PCM branch of EventFiltering/PWGEM/EMPhotonFilter.cxx (isSelectedSecondary() +/// and the min_pt_pcm_photon cut in runFilter()): both V0 daughters must have |TPC nSigma_e| < 3.5 +/// whenever a TPC signal is available, and a daughter without TPC is not rejected. Note that +/// EMPhotonFilter.cxx also declares minpt_v0, maxeta_v0, max_dcatopv_xy_v0 and max_dcatopv_z_v0, +/// but never applies them -- they are dead configurables in that task. The additional geometric +/// cuts here (cosPA, DCA to PV, chi2/NDF, radius) are standard PCM conversion-quality cuts, not +/// used by that filter task, so they default to permissive (no-op) values. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ +#define PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ + +#include "PWGCF/Femto/Core/baseSelection.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/selectionContainer.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::photonbuilder +{ + +// filters applied in the producer task (loose pre-selection, applied before quality/PID cuts) +struct ConfPhotonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT (matches EMPhotonFilter min_pt_pcm_photon default)"}; + o2::framework::Configurable ptMax{"ptMax", 99.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable mGammaAbsMax{"mGammaAbsMax", 1e10f, "Maximum |m_ee| of the conversion (KF) at the secondary vertex"}; +}; + +// selection bits for photons (PCM) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all photons are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> cosPaMin{"cosPaMin", {-1.f}, "Minimum cosine of pointing angle (not used by EMPhotonFilter, default is permissive)"}; \ + o2::framework::Configurable> dcaToPvXYAbsMax{"dcaToPvXYAbsMax", {1e10f}, "Maximum |DCAxy| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> dcaToPvZAbsMax{"dcaToPvZAbsMax", {1e10f}, "Maximum |DCAz| of the photon (V0) to the PV (cm)"}; \ + o2::framework::Configurable> chi2NdfMax{"chi2NdfMax", {1e10f}, "Maximum chi2/NDF of the KF conversion vertex"}; \ + o2::framework::Configurable> v0RadiusMin{"v0RadiusMin", {0.f}, "Minimum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> v0RadiusMax{"v0RadiusMax", {1e10f}, "Maximum transverse radius of the conversion point (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.9f}, "Maximum |eta| for daughter tracks (matches EMPhotonFilter maxeta_v0, though unused there)"}; \ + o2::framework::Configurable> dauTpcNSigmaElAbsMax{"dauTpcNSigmaElAbsMax", {3.5f}, "Maximum |TPC nSigma_e| for V0 daughters (from EMPhotonFilter isSelectedSecondary)"}; \ + o2::framework::Configurable keepDaughtersWithoutTpc{"keepDaughtersWithoutTpc", true, "If true, a daughter without a TPC signal is not rejected by the TPC electron PID cut (matches EMPhotonFilter behaviour)"}; + +struct ConfPhotonBits : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBits"); + PHOTON_DEFAULT_BITS +}; + +#undef PHOTON_DEFAULT_BITS + +// base selection for QA/analysis tasks, used to re-select already produced photons by mask +// (analogous to ConfLambdaSelection/ConfK0shortSelection in spirit, but photons have neither +// a PDG hypothesis nor a mass window) +template +struct ConfPhotonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable mask{"mask", 0, "Bitmask for photon selection"}; +}; + +constexpr const char PrefixPhotonSelection1[] = "PhotonSelection1"; +using ConfPhotonSelection1 = ConfPhotonSelection; + +/// The different selections for photons (PCM) +enum PhotonSels { + kCosPaMin, ///< Min. CPA (cosine pointing angle) of the conversion to the PV + kDcaToPvXYAbsMax, ///< Max. |DCAxy| of the photon to the PV + kDcaToPvZAbsMax, ///< Max. |DCAz| of the photon to the PV + kChi2NdfMax, ///< Max. chi2/NDF of the KF conversion vertex + kV0RadiusMin, ///< Min. transverse radius of the conversion point + kV0RadiusMax, ///< Max. transverse radius of the conversion point + kDauAbsEtaMax, ///< Max. absolute pseudorapidity of the daughters + kPosDauTpcNSigmaEl, ///< TPC electron PID for positive daughter + kNegDauTpcNSigmaEl, ///< TPC electron PID for negative daughter + kPhotonSelsMax +}; + +constexpr char PhotonSelHistName[] = "hPhotonSelection"; +constexpr char PhotonSelsName[] = "Photon selection object"; +const std::unordered_map photonSelectionNames = { + {kCosPaMin, "Min. CPA (cosine pointing angle)"}, + {kDcaToPvXYAbsMax, "Max. |DCAxy| to PV"}, + {kDcaToPvZAbsMax, "Max. |DCAz| to PV"}, + {kChi2NdfMax, "Max. chi2/NDF of conversion vertex"}, + {kV0RadiusMin, "Min. transverse radius"}, + {kV0RadiusMax, "Max. transverse radius"}, + {kDauAbsEtaMax, "Max. absolute pseudorapidity of daughters"}, + {kPosDauTpcNSigmaEl, "TPC electron PID for positive daughter"}, + {kNegDauTpcNSigmaEl, "TPC electron PID for negative daughter"}, +}; + +// enum for all photon (pre)filters (loose pre-selection, applied before quality/PID cuts) +enum PhotonFilters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kMGammaAbsMax, + kPhotonFiltersMax +}; + +constexpr char PhotonFilterHistName[] = "hPhotonFilters"; +const std::unordered_map photonFilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}, + {kMGammaAbsMax, "mGammaAbsMax"}, +}; + +/// \brief Cut class to contain and execute all cuts applied to PCM photons (V0PhotonsKF) +template +class PhotonSelection : public baseselection::BaseSelection +{ + public: + PhotonSelection() = default; + ~PhotonSelection() override = default; + + template + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) + { + this->init(config.passThrough.value); + + mPtMin = filter.ptMin.value; + mPtMax = filter.ptMax.value; + mEtaMin = filter.etaMin.value; + mEtaMax = filter.etaMax.value; + mPhiMin = filter.phiMin.value; + mPhiMax = filter.phiMax.value; + mMGammaAbsMax = filter.mGammaAbsMax.value; + mKeepDaughtersWithoutTpc = config.keepDaughtersWithoutTpc.value; + + this->addSelection(kCosPaMin, photonSelectionNames.at(kCosPaMin), config.cosPaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDcaToPvXYAbsMax, photonSelectionNames.at(kDcaToPvXYAbsMax), config.dcaToPvXYAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDcaToPvZAbsMax, photonSelectionNames.at(kDcaToPvZAbsMax), config.dcaToPvZAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kChi2NdfMax, photonSelectionNames.at(kChi2NdfMax), config.chi2NdfMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kV0RadiusMin, photonSelectionNames.at(kV0RadiusMin), config.v0RadiusMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kV0RadiusMax, photonSelectionNames.at(kV0RadiusMax), config.v0RadiusMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kDauAbsEtaMax, photonSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kPosDauTpcNSigmaEl, photonSelectionNames.at(kPosDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addComments(kPosDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc)); + this->addSelection(kNegDauTpcNSigmaEl, photonSelectionNames.at(kNegDauTpcNSigmaEl), config.dauTpcNSigmaElAbsMax.value, limits::kAbsUpperLimit, true, true, false); + this->addComments(kNegDauTpcNSigmaEl, "keepDaughtersWithoutTpc = " + std::to_string(mKeepDaughtersWithoutTpc)); + + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + { + {photonFilterNames.at(kPtMin), mPtMin}, + {photonFilterNames.at(kPtMax), mPtMax}, + {photonFilterNames.at(kEtaMin), mEtaMin}, + {photonFilterNames.at(kEtaMax), mEtaMax}, + {photonFilterNames.at(kPhiMin), mPhiMin}, + {photonFilterNames.at(kPhiMax), mPhiMax}, + {photonFilterNames.at(kMGammaAbsMax), mMGammaAbsMax}, + }); + } + + template + void applySelections(T1 const& photon, T2 const& /*v0legs*/) + { + this->reset(); + this->evaluateObservable(kCosPaMin, photon.cospa()); + this->evaluateObservable(kDcaToPvXYAbsMax, photon.dcaXYtopv()); + this->evaluateObservable(kDcaToPvZAbsMax, photon.dcaZtopv()); + this->evaluateObservable(kChi2NdfMax, photon.chiSquareNDF()); + this->evaluateObservable(kV0RadiusMin, photon.v0radius()); + this->evaluateObservable(kV0RadiusMax, photon.v0radius()); + + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + + std::array etaAbsDaughters = {std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; + this->evaluateObservable(kDauAbsEtaMax, *std::max_element(etaAbsDaughters.begin(), etaAbsDaughters.end())); + + // TPC electron PID: evaluate only when a TPC signal is available, matching + // EMPhotonFilter::isSelectedSecondary(), which never rejects a daughter without TPC. + // If keepDaughtersWithoutTpc is false, feed a value that fails every limit instead of + // skipping evaluation entirely, so this (always mandatory) cut correctly rejects the + // candidate instead of silently letting it through + auto evaluateDaughterTpcEl = [this](PhotonSels bit, bool hasTpc, float tpcNSigmaEl) { + if (hasTpc) { + this->evaluateObservable(bit, tpcNSigmaEl); + } else if (mKeepDaughtersWithoutTpc) { + this->evaluateObservable(bit, 0.f); + } else { + this->evaluateObservable(bit, std::numeric_limits::max()); + } + }; + evaluateDaughterTpcEl(kPosDauTpcNSigmaEl, posDaughter.hasTPC(), posDaughter.tpcNSigmaEl()); + evaluateDaughterTpcEl(kNegDauTpcNSigmaEl, negDaughter.hasTPC(), negDaughter.tpcNSigmaEl()); + + this->assembleBitmask(); + } + + template + bool checkFilters(const T& photon) const + { + bool pass = true; + bool p = false; + + p = photon.pt() > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = photon.pt() < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = photon.eta() > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = photon.eta() < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = photon.phi() > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = photon.phi() < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + p = std::fabs(photon.mGamma()) < mMGammaAbsMax; + this->template fillFilter(kMGammaAbsMax, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; + } + + protected: + // kinematic filters + float mPtMin = 0.f; + float mPtMax = 99.f; + float mEtaMin = -10.f; + float mEtaMax = 10.f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + float mMGammaAbsMax = 1e10f; + + bool mKeepDaughtersWithoutTpc = true; +}; + +struct PhotonBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPhotons; + o2::framework::Produces producedLitePhotons; + o2::framework::Produces producedPhotonMasks; + o2::framework::Produces producedPhotonExtras; +}; + +struct ConfPhotonTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonTables"); + o2::framework::Configurable producePhotons{"producePhotons", -1, "Produce Photons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLitePhotons{"produceLitePhotons", -1, "Produce LitePhotons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonMasks{"producePhotonMasks", -1, "Produce PhotonMasks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePhotonExtras{"producePhotonExtras", -1, "Produce PhotonExtras (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for PCM photons (V0PhotonsKF + V0Legs, from PWGEM/PhotonMeson) +/// +/// Unlike the strangeness V0 builder, daughter (conversion electron) tracks are, for now, not +/// indexed into FTracks: the legs (V0Legs) are TPC-only tracks, so they cannot be confused for +/// (i.e. share hits with/be the same reconstructed object as) a global track the way two V0 +/// daughters or a track and a V0 daughter can -- there is no detector-overlap effect for close +/// pair rejection to correct for. Only photon-level kinematics plus daughter PID/quality +/// diagnostics (FPhotonExtras) are stored. +class PhotonBuilder +{ + public: + PhotonBuilder() = default; + ~PhotonBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + { + LOG(info) << "Initialize femto photon (PCM) builder..."; + mProducePhotons = utils::enableTable("FPhotons_001", table.producePhotons.value, initContext); + mProduceLitePhotons = utils::enableTable("FLitePhotons_001", table.produceLitePhotons.value, initContext); + mProducePhotonMasks = utils::enableTable("FPhotonMasks_001", table.producePhotonMasks.value, initContext); + mProducePhotonExtras = utils::enableTable("FPhotonExtras_001", table.producePhotonExtras.value, initContext); + + if (mProducePhotons && mProduceLitePhotons) { + LOG(fatal) << "FPhotons and FLitePhotons are mutually exclusive -- enable only one. " + << "FLitePhotons is meant to replace FPhotons at the producer stage (for better compression in derived data)."; + } + if (mProducePhotons || mProduceLitePhotons || mProducePhotonMasks || mProducePhotonExtras) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + mPhotonSelection.configure(registry, config, filter); + mPhotonSelection.printSelections(PhotonSelsName); + LOG(info) << "Initialization done..."; + } + + template + void fillPhotons(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& photonProducts, T5 const& photons, T6 const& v0legs) + { + if (!mFillAnyTable) { + return; + } + for (const auto& photon : photons) { + if (!mPhotonSelection.checkFilters(photon)) { + continue; + } + mPhotonSelection.applySelections(photon, v0legs); + if (!mPhotonSelection.passesAllRequiredSelections()) { + continue; + } + + collisionBuilder.template fillCollision(collisionProducts, col); + + if (mProducePhotons) { + photonProducts.producedPhotons(collisionBuilder.collisionIndex(), + photon.pt(), + photon.eta(), + photon.phi()); + } + if (mProduceLitePhotons) { + photonProducts.producedLitePhotons(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binUnsignedPt(photon.pt()), + o2::aod::femtobase::lite::binEta(photon.eta()), + o2::aod::femtobase::lite::binPhi(photon.phi())); + } + if (mProducePhotonMasks) { + photonProducts.producedPhotonMasks(mPhotonSelection.getBitmask()); + } + if (mProducePhotonExtras) { + auto posDaughter = photon.template posTrack_as(); + auto negDaughter = photon.template negTrack_as(); + photonProducts.producedPhotonExtras( + photon.cospa(), + photon.dcaXYtopv(), + photon.dcaZtopv(), + photon.chiSquareNDF(), + photon.v0radius(), + posDaughter.pt(), + negDaughter.pt(), + posDaughter.tpcInnerParam(), + negDaughter.tpcInnerParam(), + posDaughter.tpcSignal(), + negDaughter.tpcSignal(), + posDaughter.tpcNSigmaEl(), + negDaughter.tpcNSigmaEl(), + photon.vx(), + photon.vy(), + photon.vz()); + } + } + } + + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mPhotonSelection.isPassThrough(); } + + private: + PhotonSelection mPhotonSelection; + bool mFillAnyTable = false; + bool mProducePhotons = false; + bool mProduceLitePhotons = false; + bool mProducePhotonMasks = false; + bool mProducePhotonExtras = false; +}; + +} // namespace o2::analysis::femto::photonbuilder +#endif // PWGCF_FEMTO_CORE_PHOTONBUILDER_H_ diff --git a/PWGCF/Femto/Core/photonHistManager.h b/PWGCF/Femto/Core/photonHistManager.h new file mode 100644 index 00000000000..96ab98cc711 --- /dev/null +++ b/PWGCF/Femto/Core/photonHistManager.h @@ -0,0 +1,303 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file photonHistManager.h +/// \brief histogram manager for photon (PCM) histograms +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::photonhistmanager +{ +// enum for photon (PCM) histograms +enum PhotonHist { + // analysis + kPt, + kEta, + kPhi, + // qa variables + kCosPa, + kPa, + kDcaToPvXY, + kDcaToPvZ, + kChi2Ndf, + kV0Radius, + kDecayVtxX, + kDecayVtxY, + kDecayVtxZ, + kDecayVtx, + kPosDauTpcNSigmaEl, + kNegDauTpcNSigmaEl, + kPosDauPt, + kNegDauPt, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kPtVsCosPa, + kPosDauVsNegDauTpcNSigmaEl, + kPosDauTpcSignalVsP, + kNegDauTpcSignalVsP, + + kPhotonHistLast +}; + +struct ConfPhotonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonBinning"); + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; +}; + +struct ConfPhotonQaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("PhotonQaBinning"); + o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; + o2::framework::ConfigurableAxis dcaToPv{"dcaToPv", {{200, -1, 1}}, "DCA of the photon to the PV (cm)"}; + o2::framework::ConfigurableAxis chi2Ndf{"chi2Ndf", {{100, 0, 50}}, "Chi2/NDF of the KF conversion vertex"}; + o2::framework::ConfigurableAxis v0Radius{"v0Radius", {{200, 0, 200}}, "Transverse radius of the conversion point (cm)"}; + o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{200, 0, 200}}, "Distance of the conversion point from the nominal interaction point (cm)"}; + o2::framework::ConfigurableAxis tpcNSigmaEl{"tpcNSigmaEl", {{200, -10, 10}}, "TPC n sigma electron for daughters"}; + o2::framework::ConfigurableAxis dauPt{"dauPt", {{300, 0, 3}}, "Pt of the daughters"}; + o2::framework::ConfigurableAxis dauTpcInnerParam{"dauTpcInnerParam", {{200, 0, 4}}, "Momentum of the daughters at the inner wall of the TPC (GeV/#it{c})"}; + o2::framework::ConfigurableAxis dauTpcSignal{"dauTpcSignal", {{200, 0, 200}}, "TPC dE/dx of the daughters"}; +}; + +// must be in sync with enum PhotonHist +constexpr std::array, kPhotonHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, + {kPa, o2::framework::HistType::kTH1F, "hPa", "Pointing angle; #alpha; Entries"}, + {kDcaToPvXY, o2::framework::HistType::kTH1F, "hDcaToPvXY", "DCA_{xy} to PV; DCA_{xy} (cm); Entries"}, + {kDcaToPvZ, o2::framework::HistType::kTH1F, "hDcaToPvZ", "DCA_{z} to PV; DCA_{z} (cm); Entries"}, + {kChi2Ndf, o2::framework::HistType::kTH1F, "hChi2Ndf", "Chi2/NDF of KF conversion vertex; #chi^{2}/NDF; Entries"}, + {kV0Radius, o2::framework::HistType::kTH1F, "hV0Radius", "Transverse radius; r_{xy} (cm); Entries"}, + {kDecayVtxX, o2::framework::HistType::kTH1F, "hDecayVtxX", "X coordinate of conversion point; DV_{X} (cm); Entries"}, + {kDecayVtxY, o2::framework::HistType::kTH1F, "hDecayVtxY", "Y coordinate of conversion point; DV_{Y} (cm); Entries"}, + {kDecayVtxZ, o2::framework::HistType::kTH1F, "hDecayVtxZ", "Z coordinate of conversion point; DV_{Z} (cm); Entries"}, + {kDecayVtx, o2::framework::HistType::kTH1F, "hDecayVtx", "Distance of conversion point from primary vertex; DV (cm); Entries"}, + {kPosDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hPosDauTpcNSigmaEl", "TPC n#sigma_{e} of positive daughter; n#sigma_{TPC,e}; Entries"}, + {kNegDauTpcNSigmaEl, o2::framework::HistType::kTH1F, "hNegDauTpcNSigmaEl", "TPC n#sigma_{e} of negative daughter; n#sigma_{TPC,e}; Entries"}, + {kPosDauPt, o2::framework::HistType::kTH1F, "hPosDauPt", "p_{T} of positive daughter; p_{T} (GeV/#it{c}); Entries"}, + {kNegDauPt, o2::framework::HistType::kTH1F, "hNegDauPt", "p_{T} of negative daughter; p_{T} (GeV/#it{c}); Entries"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, + {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "p_{T} vs cosine of pointing angle; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kPosDauVsNegDauTpcNSigmaEl, o2::framework::HistType::kTH2F, "hPosDauVsNegDauTpcNSigmaEl", "TPC n#sigma_{e} of positive vs negative daughter; n#sigma_{TPC,e} (pos.); n#sigma_{TPC,e} (neg.)"}, + {kPosDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hPosDauTpcSignalVsP", "TPC dE/dx vs p of positive daughter; p_{TPC} (GeV/#it{c}); dE/dx"}, + {kNegDauTpcSignalVsP, o2::framework::HistType::kTH2F, "hNegDauTpcSignalVsP", "TPC dE/dx vs p of negative daughter; p_{TPC} (GeV/#it{c}); dE/dx"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define PHOTON_HIST_QA_MAP(confAnalysis, confQa) \ + {kCosPa, {(confQa).cosPa}}, \ + {kPa, {(confQa).pa}}, \ + {kDcaToPvXY, {(confQa).dcaToPv}}, \ + {kDcaToPvZ, {(confQa).dcaToPv}}, \ + {kChi2Ndf, {(confQa).chi2Ndf}}, \ + {kV0Radius, {(confQa).v0Radius}}, \ + {kDecayVtxX, {(confQa).decayVertex}}, \ + {kDecayVtxY, {(confQa).decayVertex}}, \ + {kDecayVtxZ, {(confQa).decayVertex}}, \ + {kDecayVtx, {(confQa).decayVertex}}, \ + {kPosDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl}}, \ + {kPosDauPt, {(confQa).dauPt}}, \ + {kNegDauPt, {(confQa).dauPt}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsCosPa, {(confAnalysis).pt, (confQa).cosPa}}, \ + {kPosDauVsNegDauTpcNSigmaEl, {(confQa).tpcNSigmaEl, (confQa).tpcNSigmaEl}}, \ + {kPosDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, \ + {kNegDauTpcSignalVsP, {(confQa).dauTpcInnerParam, (confQa).dauTpcSignal}}, + +template +auto makePhotonHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +std::map> makePhotonQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinningQa) +{ + return std::map>{ + PHOTON_HIST_ANALYSIS_MAP(confBinningAnalysis) + PHOTON_HIST_QA_MAP(confBinningAnalysis, confBinningQa)}; +} + +#undef PHOTON_HIST_ANALYSIS_MAP +#undef PHOTON_HIST_QA_MAP + +constexpr char PrefixPhotonQa[] = "PhotonQA/"; +// used only by the standalone QA task +constexpr char PrefixTwoPhotonResonanceDau1Qa[] = "TwoPhotonResonanceDau1Qa/"; +constexpr char PrefixTwoPhotonResonanceDau2Qa[] = "TwoPhotonResonanceDau2Qa/"; +// same-event siblings, used only by a pair builder's same-event resonance manager +constexpr char PrefixTwoPhotonResonanceDau1Se[] = "TwoPhotonResonanceDau1Se/"; +constexpr char PrefixTwoPhotonResonanceDau2Se[] = "TwoPhotonResonanceDau2Se/"; +// mixed-event siblings, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixTwoPhotonResonanceDau1Me[] = "TwoPhotonResonanceDau1Me/"; +constexpr char PrefixTwoPhotonResonanceDau2Me[] = "TwoPhotonResonanceDau2Me/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class PhotonHistManager +/// \brief Histogram manager for PCM photon candidates (reco + QA only, no MC) +template +class PhotonHistManager +{ + public: + PhotonHistManager() = default; + ~PhotonHistManager() = default; + + // init for analysis only + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs) + { + mHistogramRegistry = registry; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + } + + // init for analysis + qa + template + void init(o2::framework::HistogramRegistry* registry, std::map> const& specs, T const& confQaBinning) + { + mHistogramRegistry = registry; + mPlot2d = confQaBinning.plot2d.value; + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(specs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(specs); + } + } + + template + void fill(T const& photon) + { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + this->fillAnalysis(photon); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + this->fillQa(photon); + } + } + + private: + void initAnalysis(std::map> const& specs) + { + std::string analysisDir = std::string(Prefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {specs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {specs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {specs.at(kPhi)}); + } + + void initQa(std::map> const& specs) + { + std::string qaDir = std::string(Prefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kCosPa, HistTable), getHistDesc(kCosPa, HistTable), getHistType(kCosPa, HistTable), {specs.at(kCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPa, HistTable), getHistDesc(kPa, HistTable), getHistType(kPa, HistTable), {specs.at(kPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvXY, HistTable), getHistDesc(kDcaToPvXY, HistTable), getHistType(kDcaToPvXY, HistTable), {specs.at(kDcaToPvXY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDcaToPvZ, HistTable), getHistDesc(kDcaToPvZ, HistTable), getHistType(kDcaToPvZ, HistTable), {specs.at(kDcaToPvZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kChi2Ndf, HistTable), getHistDesc(kChi2Ndf, HistTable), getHistType(kChi2Ndf, HistTable), {specs.at(kChi2Ndf)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kV0Radius, HistTable), getHistDesc(kV0Radius, HistTable), getHistType(kV0Radius, HistTable), {specs.at(kV0Radius)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxX, HistTable), getHistDesc(kDecayVtxX, HistTable), getHistType(kDecayVtxX, HistTable), {specs.at(kDecayVtxX)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxY, HistTable), getHistDesc(kDecayVtxY, HistTable), getHistType(kDecayVtxY, HistTable), {specs.at(kDecayVtxY)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxZ, HistTable), getHistDesc(kDecayVtxZ, HistTable), getHistType(kDecayVtxZ, HistTable), {specs.at(kDecayVtxZ)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtx, HistTable), getHistDesc(kDecayVtx, HistTable), getHistType(kDecayVtx, HistTable), {specs.at(kDecayVtx)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauTpcNSigmaEl, HistTable), getHistType(kPosDauTpcNSigmaEl, HistTable), {specs.at(kPosDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcNSigmaEl, HistTable), getHistDesc(kNegDauTpcNSigmaEl, HistTable), getHistType(kNegDauTpcNSigmaEl, HistTable), {specs.at(kNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauPt, HistTable), getHistDesc(kPosDauPt, HistTable), getHistType(kPosDauPt, HistTable), {specs.at(kPosDauPt)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauPt, HistTable), getHistDesc(kNegDauPt, HistTable), getHistType(kNegDauPt, HistTable), {specs.at(kNegDauPt)}); + + if (mPlot2d) { + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {specs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {specs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {specs.at(kPhiVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {specs.at(kPtVsCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistDesc(kPosDauVsNegDauTpcNSigmaEl, HistTable), getHistType(kPosDauVsNegDauTpcNSigmaEl, HistTable), {specs.at(kPosDauVsNegDauTpcNSigmaEl)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPosDauTpcSignalVsP, HistTable), getHistDesc(kPosDauTpcSignalVsP, HistTable), getHistType(kPosDauTpcSignalVsP, HistTable), {specs.at(kPosDauTpcSignalVsP)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kNegDauTpcSignalVsP, HistTable), getHistDesc(kNegDauTpcSignalVsP, HistTable), getHistType(kNegDauTpcSignalVsP, HistTable), {specs.at(kNegDauTpcSignalVsP)}); + } + } + + template + void fillAnalysis(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), photon.pt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), photon.phi()); + } + + template + void fillQa(T const& photon) + { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kCosPa, HistTable)), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPa, HistTable)), photon.pa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvXY, HistTable)), photon.dcaToPvXY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDcaToPvZ, HistTable)), photon.dcaToPvZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kChi2Ndf, HistTable)), photon.chi2Ndf()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kV0Radius, HistTable)), photon.v0Radius()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxX, HistTable)), photon.decayVtxX()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxY, HistTable)), photon.decayVtxY()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxZ, HistTable)), photon.decayVtxZ()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtx, HistTable)), photon.decayVtx()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcNSigmaEl, HistTable)), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauPt, HistTable)), photon.posDauPt()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauPt, HistTable)), photon.negDauPt()); + + if (mPlot2d) { + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), photon.pt(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), photon.pt(), photon.phi()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), photon.phi(), photon.eta()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), photon.pt(), photon.cosPa()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauVsNegDauTpcNSigmaEl, HistTable)), photon.posDauTpcNSigmaEl(), photon.negDauTpcNSigmaEl()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kPosDauTpcSignalVsP, HistTable)), photon.posDauTpcInnerParam(), photon.posDauTpcSignal()); + mHistogramRegistry->fill(HIST(Prefix) + HIST(QaDir) + HIST(getHistName(kNegDauTpcSignalVsP, HistTable)), photon.negDauTpcInnerParam(), photon.negDauTpcSignal()); + } + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + bool mPlot2d = false; +}; +} // namespace o2::analysis::femto::photonhistmanager +#endif // PWGCF_FEMTO_CORE_PHOTONHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/selectionContainer.h b/PWGCF/Femto/Core/selectionContainer.h index fe5616f8302..0017215b718 100644 --- a/PWGCF/Femto/Core/selectionContainer.h +++ b/PWGCF/Femto/Core/selectionContainer.h @@ -26,9 +26,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -49,7 +51,8 @@ enum LimitType { kUpperLimit, ///< simple upper limit, e.g. p_T < 1 G kAbsLowerFunctionLimit, ///< lower limit on the absolute value given by a function, e.g. |DCA_xy| > f(pt) kEqual, ///< value must equal a fixed threshold, e.g. sign == 1 kEqualArray, ///< each element of a value array must equal the corresponding threshold - kRange, ///< value must fall within [lower, upper]; either bound can be omitted (open interval) + kInsideRange, ///< value must fall within [lower, upper]; either bound can be omitted (open interval) + kOutsideRange, ///< value must fall outside [lower, upper]; fully-open range (both bounds omitted) disables the cut kLimitTypeLast }; @@ -64,12 +67,22 @@ inline const std::unordered_map limitTypeAsStrings = { {kAbsLowerFunctionLimit, "Absolute Lower Function Limit"}, {kEqual, "Equal"}, {kEqualArray, "EqualArray"}, - {kRange, "Range"}, + {kInsideRange, "Inside Range"}, + {kOutsideRange, "Outside Range"}, {kLimitTypeLast, "Last Limit Type"}, }; }; // namespace limits +namespace selectioncontainer +{ + +// strings for selection/filter histogram +constexpr std::string_view SectionDelimiter = ":::"; +constexpr std::string_view RangeDelimiter = ";"; +constexpr std::string_view ValueDelimiter = "___"; +constexpr std::string_view NoValue = "X"; + /// \class SelectionContainer /// \brief Stores and evaluates multiple selection thresholds for a single observable. /// @@ -110,10 +123,10 @@ class SelectionContainer mIsOptionalCut(isOptionalCut) { if (mSelectionValues.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } // values for selection are not necessarily ordered correctly sortSelections(); @@ -121,34 +134,46 @@ class SelectionContainer /// \brief Constructor for range-based selections defined as "lower;upper" strings. /// Either bound may be omitted to represent an open interval, e.g. ";1.0" means value < 1.0. + /// A fully-open range (both bounds omitted, i.e. ";") is exempt: for kInsideRange it means + /// "no constraint" (every value is inside); for kOutsideRange it means "no rejection" (every + /// value counts as outside) -- the natural reading breaks down for kOutsideRange otherwise, + /// since an infinitely wide excluded region would reject everything instead of nothing. /// \param selectionName Name of the observable this container manages. /// \param rangeStrings Vector of range strings, each of the form "lower;upper". /// \param skipMostPermissiveBit Whether to skip the most permissive threshold when assembling the bitmask. /// \param isMinimalCut Whether this selection is mandatory (candidate is rejected if it fails). /// \param isOptionalCut Whether this selection is optional (candidate is accepted if any optional cut passes). + /// \param limitType Either kInsideRange (value must fall inside the interval) or kOutsideRange + /// (value must fall outside it, e.g. rejecting a competing mass hypothesis). SelectionContainer(std::string const& selectionName, std::vector const& rangeStrings, bool skipMostPermissiveBit, bool isMinimalCut, - bool isOptionalCut) + bool isOptionalCut, + limits::LimitType limitType = limits::kInsideRange) : mSelectionName(selectionName), - mLimitType(limits::kRange), + mLimitType(limitType), mSkipMostPermissiveBit(skipMostPermissiveBit), mIsMinimalCut(isMinimalCut), mIsOptionalCut(isOptionalCut) { + if (mLimitType != limits::kInsideRange && mLimitType != limits::kOutsideRange) { + LOG(fatal) << "Selection '" << mSelectionName << "': range-string selections only support kInsideRange or kOutsideRange"; + } if (rangeStrings.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } for (auto const& rangeStr : rangeStrings) { T lower, upper; parseRangeString(rangeStr, lower, upper); mSelectionRanges.emplace_back(lower, upper); } - // ranges are sorted by their lenghts, i.e. from widest range to tightest range + // for kInsideRange, ranges are sorted from widest (most permissive) to tightest; for + // kOutsideRange it's the opposite -- the narrowest excluded region is the most permissive + // (fewer values get rejected) // in principle the ranges do not have to include each other, exepct this is configured as minimal cut, check is added at the end // to cover both cases, this also means we always check all ranges, when assembling the bit mask sortSelections(); @@ -157,15 +182,25 @@ class SelectionContainer mSelectionValues.push_back(mSelectionRanges[i].second - mSelectionRanges[i].first); } - // for minimal range cut, ranges must be strictly nested (each range contains all narrower ones) - // this is required for the early-exit logic in passesAsMinimalCut() to be correct + // for a minimal range cut, ranges must be strictly nested (each range contains all ranges that + // sort after it) -- this is required for the single-bit shortcut in passesAsMinimalCut() to be + // correct. For kInsideRange (sorted widest-first) that means range[i] must contain range[i+1]; + // for kOutsideRange (sorted narrowest-first) it's the reverse: range[i] must be contained in + // range[i+1]. A fully-open ("reject nothing") entry is the empty interval [max, -max], which is + // trivially contained in (or containing, for kInsideRange) anything, so this check already holds + // for it without any special-casing. if (isMinimalCut) { for (std::size_t i = 0; i + 1 < mSelectionRanges.size(); i++) { - if (mSelectionRanges[i].first > mSelectionRanges[i + 1].first || - mSelectionRanges[i].second < mSelectionRanges[i + 1].second) { - LOG(fatal) << "Ranges for minimal cut " << selectionName + bool const nested = (mLimitType == limits::kOutsideRange) + ? (mSelectionRanges[i].first >= mSelectionRanges[i + 1].first && + mSelectionRanges[i].second <= mSelectionRanges[i + 1].second) + : (mSelectionRanges[i].first <= mSelectionRanges[i + 1].first && + mSelectionRanges[i].second >= mSelectionRanges[i + 1].second); + if (!nested) { + LOG(fatal) << "Selection '" << selectionName << "': ranges for minimal cut" << " are not nested. Range [" << mSelectionRanges[i].first << ";" << mSelectionRanges[i].second - << "] does not contain [" << mSelectionRanges[i + 1].first << ";" << mSelectionRanges[i + 1].second << "]"; + << "] is not " << (mLimitType == limits::kOutsideRange ? "contained in" : "containing") + << " [" << mSelectionRanges[i + 1].first << ";" << mSelectionRanges[i + 1].second << "]"; } } } @@ -195,10 +230,10 @@ class SelectionContainer mIsOptionalCut(isOptionalCut) { if (functions.size() > sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; + LOG(fatal) << "Selection '" << mSelectionName << "': too many selections for a single observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } if (isMinimalCut && isOptionalCut) { - LOG(fatal) << "A selection cannot be both minimal and optional"; + LOG(fatal) << "Selection '" << mSelectionName << "': a selection cannot be both minimal and optional"; } for (std::size_t i = 0; i < functions.size(); i++) { mSelectionFunctions.emplace_back((mSelectionName + std::to_string(i)).c_str(), functions.at(i).c_str(), lowerLimit, upperLimit); @@ -221,24 +256,31 @@ class SelectionContainer // note: threshold values may be reordered by sortSelections() or sortFunctions(), // so comments must be provided in the already-sorted order if (comments.size() != getNSelections()) { - LOG(fatal) << "Number of comments and number of selections are inconsistent"; + LOG(fatal) << "Selection '" << mSelectionName << "': number of comments and number of selections are inconsistent"; } mComments = comments; } + /// \brief Attach the same comment to every threshold of this selection. + /// \param comment Comment string applied to all thresholds. + void addComments(std::string const& comment) + { + mComments = std::vector(getNSelections(), comment); + } + /// \brief Get comments attached to the selection thresholds. /// \return Vector of comment strings. - std::string getComment(int selectionIndex) const + [[nodiscard]] std::string getComment(int selectionIndex) const { if (mComments.empty()) { - return std::string(""); + return std::string{""}; } return mComments.at(selectionIndex); } /// \brief Get the name of this selection. /// \return Selection name string. - std::string const& getSelectionName() const { return mSelectionName; } + [[nodiscard]] std::string const& getSelectionName() const { return mSelectionName; } /// \brief Update threshold values by re-evaluating the internal TF1 functions at a given point. /// \param value Input value at which to evaluate the functions. @@ -301,7 +343,7 @@ class SelectionContainer } } break; - case (limits::kRange): + case (limits::kInsideRange): // ranges are sorted widest-first but a narrower range passing does not imply a wider one fails (no check on boundries), // so all ranges must be checked explicitly — no early exit for (std::size_t i = 0; i < mSelectionRanges.size(); i++) { @@ -310,6 +352,18 @@ class SelectionContainer } } break; + case (limits::kOutsideRange): + // ranges are sorted narrowest-first (most permissive -- smallest excluded region); same + // no-early-exit reasoning as kInsideRange applies, just with the pass condition inverted. + // A fully-open range string (";") is parsed as the empty interval [max, -max] (see + // parseRangeString()), so this plain check already evaluates true for every value -- no + // special-casing needed here to express "reject nothing". + for (std::size_t i = 0; i < mSelectionRanges.size(); i++) { + if (value < mSelectionRanges.at(i).first || value > mSelectionRanges.at(i).second) { + mBitmask.set(i); + } + } + break; case (limits::kEqual): // kEqual has no natural ordering, so all thresholds must be checked and we cannot bail early for (std::size_t i = 0; i < mSelectionValues.size(); i++) { @@ -329,7 +383,7 @@ class SelectionContainer void evaluate(std::vector const& values) { if (values.size() != mSelectionValues.size()) { - LOG(fatal) << "Wrong number of values have been passed"; + LOG(fatal) << "Selection '" << mSelectionName << "': wrong number of values have been passed"; } // reset the bitmask before evaluating mBitmask.reset(); @@ -354,10 +408,9 @@ class SelectionContainer { if (!mSkipMostPermissiveBit) { return mBitmask; - } else { - // remove the first (most permissive) bit since it corresponds to the minimal selection and is always true - return mBitmask >> 1; } + // remove the first (most permissive) bit since it corresponds to the minimal selection and is always true + return mBitmask >> 1; } /// \brief Manually set the internal bitmask. @@ -369,12 +422,20 @@ class SelectionContainer mBitmask = std::bitset(bitmask); } + /// \brief Retrieve the raw (unshifted) internal bitmask, including the most-permissive-threshold + /// bit even if mSkipMostPermissiveBit is set. Used when two separate evaluate() calls for + /// the same observable need to be combined externally (e.g. logical AND) before the + /// result is written back via setBitmask() -- unlike getBitmask(), this never shifts, so + /// it stays the correct counterpart to setBitmask() (which also never shifts). + /// \return Bitset exactly as currently stored. + [[nodiscard]] std::bitset const& getRawBitmask() const { return mBitmask; } + /// \brief Reset the internal bitmask to zero. void reset() { mBitmask.reset(); } /// \brief Check whether the mandatory (minimal) cut condition is fulfilled. /// \return True if the minimal selection passes or if this container is not marked as a minimal cut. - bool passesAsMinimalCut() const + [[nodiscard]] bool passesAsMinimalCut() const { if (mIsMinimalCut) { // if any bit is set the loosest threshold passed; since thresholds are ordered, @@ -387,7 +448,7 @@ class SelectionContainer /// \brief Check whether any optional cut is fulfilled. /// \return True if at least one optional threshold is passed, false if this container is not marked as optional. - bool passesAsOptionalCut() const + [[nodiscard]] bool passesAsOptionalCut() const { if (mIsOptionalCut) { // if any bit is set the loosest threshold passed @@ -402,7 +463,7 @@ class SelectionContainer T getLoosestSelection() const { if (mSelectionValues.empty()) { - LOG(fatal) << "No selections configured"; + LOG(fatal) << "Selection '" << mSelectionName << "': no selections configured"; } return mSelectionValues.at(0); } @@ -411,12 +472,12 @@ class SelectionContainer /// For function-based selections, mSelectionValues is always populated (initialised at the midpoint), /// so this check is safe for both static and function-based containers. /// \return True if no thresholds are configured. - bool isEmpty() const { return mSelectionValues.empty(); } + [[nodiscard]] bool isEmpty() const { return mSelectionValues.empty(); } /// \brief Get the number of bits this container contributes to the global bitmask. /// If the most permissive bit is skipped, the contribution is reduced by one. /// \return Number of bits to add to the global bitmask offset. - int getShift() const + [[nodiscard]] int getShift() const { if (mSelectionValues.empty()) { return 0; @@ -433,61 +494,106 @@ class SelectionContainer /// \brief Get the bit offset of this container within the global bitmask. /// \return Bit offset. - int getOffset() const { return mOffset; } + [[nodiscard]] int getOffset() const { return mOffset; } /// \brief Get the total number of configured selection thresholds. /// \return Number of thresholds. - std::size_t getNSelections() const { return mSelectionValues.size(); } + [[nodiscard]] std::size_t getNSelections() const { return mSelectionValues.size(); } /// \brief Build a histogram bin label string encoding the full configuration of a single threshold. /// \param selectionIndex Index of the threshold within this container. /// \return Encoded label string. - std::string getBinLabel(int selectionIndex) const + /// \brief Describes an open (sentinel, i.e. omitted-by-the-user) side of a range, if any. + /// For kOutsideRange, a single omitted side flips which half of the "value < lower || + /// value > upper" OR-condition stays active, so the resulting satisfied/reject direction + /// is spelled out explicitly rather than named generically -- omitting the upper bound + /// there means the lower-bound inequality alone remains, i.e. "reject value < lower". + /// \param range Lower/upper bound pair for a kInsideRange/kOutsideRange threshold. + /// \return A human-readable description of the effective cut, or "" if both sides are bounded. + [[nodiscard]] std::string getOpenSideComment(std::pair const& range) const + { + bool const lowerOpen = std::abs(range.first) == std::numeric_limits::max(); + bool const upperOpen = std::abs(range.second) == std::numeric_limits::max(); + if (!lowerOpen && !upperOpen) { + return ""; + } + if (lowerOpen && upperOpen) { + return "no cut"; + } + std::ostringstream oss; + if (mLimitType == limits::kInsideRange) { + oss << "accept value " << (upperOpen ? ">= " : "<= ") << (upperOpen ? range.first : range.second); + } else { + oss << "reject value " << (upperOpen ? "< " : "> ") << (upperOpen ? range.first : range.second); + } + return oss.str(); + } + + [[nodiscard]] std::string getBinLabel(int selectionIndex) const { std::ostringstream oss; - std::string sectionDelimiter = ":::"; - std::string valueDelimiter = "___"; - std::string noValue = "X"; // Determine value string std::string valueStr; + std::string autoComment; - if (mLimitType == limits::kRange) { - // Print actual lower;upper interval + if (mLimitType == limits::kInsideRange || mLimitType == limits::kOutsideRange) { + // Print the actual lower;upper interval -- kept as real, parseable numbers (not a word like + // "no cut") since this Value field is read by cutculator.py; an open side (lower and/or + // upper bound left at the sentinel, i.e. omitted by the user) is instead flagged via the + // Comment field below. const auto& range = mSelectionRanges.at(selectionIndex); std::ostringstream rangeStream; - rangeStream << range.first << ";" << range.second; + rangeStream << range.first << RangeDelimiter << range.second; valueStr = rangeStream.str(); + autoComment = getOpenSideComment(range); } else if (mSelectionFunctions.empty()) { valueStr = std::to_string(mSelectionValues.at(selectionIndex)); } else { valueStr = mSelectionFunctions.at(selectionIndex).GetExpFormula().Data(); } - oss << "SelectionName" << valueDelimiter << mSelectionName << sectionDelimiter - << "LimitType" << valueDelimiter << getLimitTypeAsString() << sectionDelimiter - << "MinimalCut" << valueDelimiter << (mIsMinimalCut ? "1" : "0") << sectionDelimiter - << "SkipMostPermissiveBit" << valueDelimiter << (mSkipMostPermissiveBit ? "1" : "0") << sectionDelimiter - << "OptionalCut" << valueDelimiter << (mIsOptionalCut ? "1" : "0") << sectionDelimiter - << "Shift" << valueDelimiter << getShift() << sectionDelimiter - << "Offset" << valueDelimiter << mOffset << sectionDelimiter - << "Value" << valueDelimiter << valueStr << sectionDelimiter - << "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)); + // combine rather than override -- e.g. TOF PID selections attach a "minMomTofX = ..." comment + // to every threshold via addComments(), independent of whether a given threshold's range + // also happens to be open-sided + std::string const userComment = mComments.empty() ? "" : mComments.at(selectionIndex); + std::string comment = userComment; + if (!autoComment.empty()) { + comment = userComment.empty() ? autoComment : autoComment + "; " + userComment; + } + + oss << "SelectionName" << ValueDelimiter << mSelectionName << SectionDelimiter + << "LimitType" << ValueDelimiter << getLimitTypeAsString() << SectionDelimiter + << "MinimalCut" << ValueDelimiter << (mIsMinimalCut ? "1" : "0") << SectionDelimiter + << "SkipMostPermissiveBit" << ValueDelimiter << (mSkipMostPermissiveBit ? "1" : "0") << SectionDelimiter + << "OptionalCut" << ValueDelimiter << (mIsOptionalCut ? "1" : "0") << SectionDelimiter + << "Shift" << ValueDelimiter << getShift() << SectionDelimiter + << "Offset" << ValueDelimiter << mOffset << SectionDelimiter + << "Value" << ValueDelimiter << valueStr << SectionDelimiter + << "BitPosition" << ValueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? NoValue : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)) << SectionDelimiter + << "Comment" << ValueDelimiter << (comment.empty() ? NoValue : comment); return oss.str(); } - std::string getValueAsString(int selectionIndex) const + [[nodiscard]] std::string getValueAsString(int selectionIndex) const { if (this->isEmpty()) { - return std::string("No value configured"); + return std::string{"No value configured"}; } if (!mSelectionFunctions.empty()) { return std::string(mSelectionFunctions.at(selectionIndex).GetExpFormula().Data()); } if (!mSelectionRanges.empty()) { + const auto& range = mSelectionRanges.at(selectionIndex); + bool const lowerOpen = std::abs(range.first) == std::numeric_limits::max(); + bool const upperOpen = std::abs(range.second) == std::numeric_limits::max(); + if (lowerOpen && upperOpen) { + return std::string{"no cut"}; + } std::ostringstream oss; - oss << mSelectionRanges.at(selectionIndex).first << ";" << mSelectionRanges.at(selectionIndex).second; + oss << (lowerOpen ? "-inf" : std::to_string(range.first).c_str()) + << ";" + << (upperOpen ? "+inf" : std::to_string(range.second).c_str()); return oss.str(); } return std::to_string(mSelectionValues.at(selectionIndex)); @@ -497,22 +603,21 @@ class SelectionContainer /// Calling this for the skipped most-permissive threshold is a fatal error. /// \param selectionIndex Index of the threshold within this container. /// \return Global bit position. - int getBitPosition(int selectionIndex) const + [[nodiscard]] int getBitPosition(int selectionIndex) const { if (selectionIndex == 0 && mSkipMostPermissiveBit) { - LOG(fatal) << "Trying to accessed the bit position of a skipped selection. Breaking..."; + LOG(fatal) << "Selection '" << mSelectionName << "': trying to access the bit position of a skipped selection. Breaking..."; return -1; } if (mSkipMostPermissiveBit) { return mOffset + selectionIndex - 1; - } else { - return mOffset + selectionIndex; } + return mOffset + selectionIndex; } /// \brief Get the string representation of the configured limit type. /// \return Human-readable limit type name. - std::string getLimitTypeAsString() const { return limits::limitTypeAsStrings.at(mLimitType); } + [[nodiscard]] std::string getLimitTypeAsString() const { return limits::limitTypeAsStrings.at(mLimitType); } /// \brief Get the configured static threshold values. /// \return Const reference to the vector of threshold values. @@ -520,19 +625,19 @@ class SelectionContainer /// \brief Get the configured TF1 threshold functions. /// \return Const reference to the vector of TF1 functions. - std::vector const& getSelectionFunction() const { return mSelectionFunctions; } + [[nodiscard]] std::vector const& getSelectionFunction() const { return mSelectionFunctions; } /// \brief Check whether this container is marked as a mandatory (minimal) cut. /// \return True if this is a minimal cut. - bool isMinimalCut() const { return mIsMinimalCut; } + [[nodiscard]] bool isMinimalCut() const { return mIsMinimalCut; } /// \brief Check whether this container is marked as an optional cut. /// \return True if this is an optional cut. - bool isOptionalCut() const { return mIsOptionalCut; } + [[nodiscard]] bool isOptionalCut() const { return mIsOptionalCut; } /// \brief Check whether the most permissive threshold bit is skipped when assembling the bitmask. /// \return True if the most permissive bit is skipped. - bool skipMostPermissiveBit() const { return mSkipMostPermissiveBit; } + [[nodiscard]] [[nodiscard]] bool skipMostPermissiveBit() const { return mSkipMostPermissiveBit; } private: /// \brief Sort static threshold values from most permissive to most restrictive based on the limit type. @@ -547,13 +652,23 @@ class SelectionContainer case (limits::kAbsLowerLimit): std::sort(mSelectionValues.begin(), mSelectionValues.end(), [](T a, T b) { return a < b; }); break; - case (limits::kRange): + case (limits::kInsideRange): // sort by range width descending so the most permissive (widest) range comes first std::sort(mSelectionRanges.begin(), mSelectionRanges.end(), [](std::pair const& a, std::pair const& b) { return (a.second - a.first) > (b.second - b.first); }); break; + case (limits::kOutsideRange): + // opposite of kInsideRange: the most permissive (most likely to be satisfied) exclusion is + // the narrowest one, since it rejects the fewest values. A fully-open ("reject nothing") + // range parses to the empty interval [max, -max] (see parseRangeString()), whose "width" is + // the most negative possible value, so it naturally sorts first without any special-casing. + std::sort(mSelectionRanges.begin(), mSelectionRanges.end(), + [](std::pair const& a, std::pair const& b) { + return (a.second - a.first) < (b.second - b.first); + }); + break; default: break; } @@ -579,7 +694,12 @@ class SelectionContainer /// \brief Parse a range string of the form "lower;upper" into a lower and upper bound. /// Either bound may be omitted (e.g. ";1.0" or "0.5;"), in which case - /// -/+ numeric_limits::max() is used respectively. + /// -/+ numeric_limits::max() is used respectively. For kOutsideRange, a fully-open string + /// (";", both bounds omitted) is the one exception: it is parsed as the empty interval + /// [max, -max] instead of [-max, max] -- every value then trivially fails the normal + /// "value >= lower && value <= upper" check, i.e. every value counts as outside, which is + /// what "reject nothing" needs. [-max, max] would instead mean every value is inside, + /// i.e. nothing is ever outside, which would reject everything -- the opposite intent. /// \param rangeStr Input string to parse. /// \param lower Output lower bound. /// \param upper Output upper bound. @@ -587,31 +707,69 @@ class SelectionContainer { auto pos = rangeStr.find(';'); if (pos == std::string::npos) { - LOG(fatal) << "Range string '" << rangeStr << "' is missing ';' separator. Expected format: 'lower;upper'"; + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' is missing ';' separator. Expected format: 'lower;upper'"; + } + if (rangeStr.find(';', pos + 1) != std::string::npos) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' has more than one ';' separator. Expected format: 'lower;upper'"; } std::string lowerStr = rangeStr.substr(0, pos); std::string upperStr = rangeStr.substr(pos + 1); - lower = lowerStr.empty() ? -std::numeric_limits::max() : static_cast(std::stod(lowerStr)); - upper = upperStr.empty() ? std::numeric_limits::max() : static_cast(std::stod(upperStr)); + if (lowerStr.empty() && upperStr.empty() && mLimitType == limits::kOutsideRange) { + // kOutsideRange's "value < lower || value > upper" is the negation of kInsideRange's + // "value >= lower && value <= upper" -- substituting the same [-max, +max] that makes the + // latter trivially true for every value would make the former trivially FALSE for every + // value instead (nothing is < -max or > +max), i.e. "reject everything" rather than the + // intended "reject nothing". The empty interval [max, -max] is the encoding that keeps the + // disjunction trivially true for every value instead, giving the correct "exempt" meaning. + lower = std::numeric_limits::max(); + upper = -std::numeric_limits::max(); + return; + } + + lower = lowerStr.empty() ? -std::numeric_limits::max() : parseBound(rangeStr, lowerStr); + upper = upperStr.empty() ? std::numeric_limits::max() : parseBound(rangeStr, upperStr); if (lower >= upper) { - LOG(fatal) << "Range string '" << rangeStr << "' has lower bound >= upper bound"; + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' has lower bound >= upper bound"; } } - std::string mSelectionName = ""; + /// \brief Parse a single range bound and make sure the whole string was consumed, so that + /// malformed input such as "3;5" (extra ';') or "-3,5" (comma used as decimal + /// separator) is rejected instead of silently truncated by std::stod (which happily + /// parses just the numeric prefix "3" or "-3" and ignores the rest). + /// \param rangeStr Full "lower;upper" string, only used for the error message. + /// \param boundStr The individual bound substring (lower or upper) to parse. + T parseBound(std::string const& rangeStr, std::string const& boundStr) const + { + std::size_t idx = 0; + double value = 0.; + try { + value = std::stod(boundStr, &idx); + } catch (std::exception const& e) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' contains bound '" << boundStr << "' that is not a valid number (" << e.what() << ")"; + } + if (idx != boundStr.size()) { + LOG(fatal) << "Selection '" << mSelectionName << "': range string '" << rangeStr << "' contains bound '" << boundStr + << "' with trailing characters ('" << boundStr.substr(idx) << "') that could not be parsed"; + } + return static_cast(value); + } + + std::string mSelectionName; std::vector mSelectionValues = {}; ///< Threshold values, sorted from most permissive to most restrictive - std::vector mSelectionFunctions = {}; ///< TF1 threshold functions (empty for static selections) - std::vector> mSelectionRanges = {}; ///< Lower and upper bounds for kRange selections, one pair per threshold + std::vector mSelectionFunctions; ///< TF1 threshold functions (empty for static selections) + std::vector> mSelectionRanges = {}; ///< Lower and upper bounds for kInsideRange/kOutsideRange selections, one pair per threshold limits::LimitType mLimitType = limits::kLimitTypeLast; ///< Comparison type applied during evaluation bool mSkipMostPermissiveBit = false; ///< If true, the most permissive threshold does not occupy a bit in the global bitmask bool mIsMinimalCut = false; ///< If true, this selection is mandatory; failing it rejects the candidate bool mIsOptionalCut = false; ///< If true, this selection is optional; passing it accepts the candidate - std::vector mComments = {}; ///< Optional comments per threshold, in the same order as mSelectionValues + std::vector mComments; ///< Optional comments per threshold, in the same order as mSelectionValues std::bitset mBitmask = {}; ///< Bitmask indicating which thresholds were passed during the last evaluation int mOffset = 0; ///< Bit offset of this container within the global bitmask }; +}; // namespace selectioncontainer } // namespace o2::analysis::femto diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index a9ac74542a2..3e703ddd94e 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -27,19 +27,20 @@ #include #include #include +#include #include +#include + +#include #include #include #include #include #include -namespace o2::analysis::femto -{ -namespace trackbuilder +namespace o2::analysis::femto::trackbuilder { - struct ConfTrackFilters : o2::framework::ConfigurableGroup { std::string prefix = std::string("TrackFilters"); // kinematic cuts for filtering tracks @@ -57,103 +58,107 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable passThrough{"passThrough", false, "If true, all tracks are passed through. Bits for all selections are stored."}; o2::framework::Configurable> tpcClustersMin{"tpcClustersMin", {90.f}, "Minimum number of clusters in TPC"}; o2::framework::Configurable> tpcCrossedRowsMin{"tpcCrossedRowsMin", {80.f}, "Minimum number of crossed rows in TPC"}; - o2::framework::Configurable> tpcClustersOverCrossedRows{"tpcClustersOverCrossedRows", {0.83f}, "Minimum fraction of clusters over crossed rows in TPC"}; + o2::framework::Configurable> tpcClustersOverCrossedRows{"tpcClustersOverCrossedRows", {0.0f}, "Minimum fraction of clusters over crossed rows in TPC"}; o2::framework::Configurable> tpcSharedClustersMax{"tpcSharedClustersMax", {160.f}, "Maximum number of shared clusters in TPC"}; o2::framework::Configurable> tpcSharedClusterFractionMax{"tpcSharedClusterFractionMax", {1.f}, "Maximum fraction of shared clusters in TPC"}; o2::framework::Configurable> itsClustersMin{"itsClustersMin", {5.f}, "Minimum number of clusters in ITS"}; o2::framework::Configurable> itsIbClustersMin{"itsIbClustersMin", {3.f}, "Minimum number of clusters in inner barrel (max 3) of ITS"}; - o2::framework::Configurable> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TForumal, where x=pt"}; - o2::framework::Configurable> dcazMax{"dcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT. Has to be a valid TForumal, where x=pt"}; + o2::framework::Configurable> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TFormula, where x=pt"}; + o2::framework::Configurable> dcazMax{"dcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT. Has to be a valid TFormula, where x=pt"}; + o2::framework::Configurable> itsChi2Max{"itsChi2Max", {}, "Maximum ITS chi2 per cluster"}; + o2::framework::Configurable> tpcChi2Max{"tpcChi2Max", {}, "Maximum TPC chi2 per cluster"}; // Electron PID cuts - o2::framework::Configurable requirePidElectron{"requirePidElectron", false, "Make election PID optional"}; + o2::framework::Configurable requirePidElectron{"requirePidElectron", false, "Make electron PID optional"}; o2::framework::Configurable minMomTofElectron{"minMomTofElectron", 0.3, "Minimum momentum to required TOF PID for Electron"}; o2::framework::Configurable> itsElectron{"itsElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Electron PID"}; o2::framework::Configurable> tpcElectron{"tpcElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Electron PID"}; - o2::framework::Configurable> tofElectron{"tofElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Electron PID"}; + o2::framework::Configurable> tofElectron{"tofElectron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Electron PID. Momentum threshold above which TOF is required: minMomTofElectron"}; o2::framework::Configurable> tpcitsElectron{"tpcitsElectron", {}, "Maximum nsigma_TPCITS for Electron PID"}; - o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum nsigma_TPCTOF for Electron PID"}; + o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum nsigma_TPCTOF for Electron PID. Momentum threshold above which TOF is required: minMomTofElectron"}; // Pion PID cuts - o2::framework::Configurable requirePidPion{"requirePidPion", false, "Make election PID optional"}; + o2::framework::Configurable requirePidPion{"requirePidPion", false, "Make pion PID optional"}; o2::framework::Configurable minMomTofPion{"minMomTofPion", 0.5, "Minimum momentum to required TOF PID for Pion"}; o2::framework::Configurable> itsPion{"itsPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Pion PID"}; o2::framework::Configurable> tpcPion{"tpcPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Pion PID"}; - o2::framework::Configurable> tofPion{"tofPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Pion PID"}; + o2::framework::Configurable> tofPion{"tofPion", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Pion PID. Momentum threshold above which TOF is required: minMomTofPion"}; o2::framework::Configurable> tpcitsPion{"tpcitsPion", {}, "Maximum nsigma_TPCITS for Pion PID"}; - o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum nsigma_TPCTOF for Pion PID"}; + o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum nsigma_TPCTOF for Pion PID. Momentum threshold above which TOF is required: minMomTofPion"}; // Kaon PID cuts - o2::framework::Configurable requirePidKaon{"requirePidKaon", false, "Make election PID optional"}; + o2::framework::Configurable requirePidKaon{"requirePidKaon", false, "Make kaon PID optional"}; o2::framework::Configurable minMomTofKaon{"minMomTofKaon", 0.4, "Minimum momentum to required TOF PID for Kaon"}; o2::framework::Configurable> itsKaon{"itsKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Kaon PID"}; o2::framework::Configurable> tpcKaon{"tpcKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Kaon PID"}; - o2::framework::Configurable> tofKaon{"tofKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Kaon PID"}; + o2::framework::Configurable> tofKaon{"tofKaon", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Kaon PID. Momentum threshold above which TOF is required: minMomTofKaon"}; o2::framework::Configurable> tpcitsKaon{"tpcitsKaon", {}, "Maximum nsigma_TPCITS for Kaon PID"}; - o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum nsigma_TPCTOF for Kaon PID"}; + o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum nsigma_TPCTOF for Kaon PID. Momentum threshold above which TOF is required: minMomTofKaon"}; // Proton PID cuts - o2::framework::Configurable requirePidProton{"requirePidProton", true, "Make election PID optional"}; + o2::framework::Configurable requirePidProton{"requirePidProton", true, "Make proton PID optional"}; o2::framework::Configurable minMomTofProton{"minMomTofProton", 0.75, "Minimum momentum to required TOF PID for Proton"}; o2::framework::Configurable> itsProton{"itsProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Proton PID"}; o2::framework::Configurable> tpcProton{"tpcProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Proton PID"}; - o2::framework::Configurable> tofProton{"tofProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Proton PID"}; + o2::framework::Configurable> tofProton{"tofProton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Proton PID. Momentum threshold above which TOF is required: minMomTofProton"}; o2::framework::Configurable> tpcitsProton{"tpcitsProton", {}, "Maximum nsigma_TPCITS for Proton PID"}; - o2::framework::Configurable> tpctofProton{"tpctofProton", {}, "Maximum nsigma_TPCTOF for Proton PID"}; + o2::framework::Configurable> tpctofProton{"tpctofProton", {}, "Maximum nsigma_TPCTOF for Proton PID. Momentum threshold above which TOF is required: minMomTofProton"}; // Deuteron PID cuts - o2::framework::Configurable requirePidDeuteron{"requirePidDeuteron", false, "Make election PID optional"}; + o2::framework::Configurable requirePidDeuteron{"requirePidDeuteron", false, "Make deuteron PID optional"}; o2::framework::Configurable minMomTofDeuteron{"minMomTofDeuteron", 1.2, "Minimum momentum to required TOF PID for Deuteron"}; o2::framework::Configurable> itsDeuteron{"itsDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Deuteron PID"}; o2::framework::Configurable> tpcDeuteron{"tpcDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Deuteron PID"}; - o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Deuteron PID"}; + o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Deuteron PID. Momentum threshold above which TOF is required: minMomTofDeuteron"}; o2::framework::Configurable> tpcitsDeuteron{"tpcitsDeuteron", {}, "Maximum nsigma_TPCITS for Deuteron PID"}; - o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum nsigma_TPCTOF for Deuteron PID"}; + o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum nsigma_TPCTOF for Deuteron PID. Momentum threshold above which TOF is required: minMomTofDeuteron"}; // Triton PID cuts - o2::framework::Configurable requirePidTriton{"requirePidTriton", false, "Make election PID optional"}; + o2::framework::Configurable requirePidTriton{"requirePidTriton", false, "Make triton PID optional"}; o2::framework::Configurable minMomTofTriton{"minMomTofTriton", 1.4, "Minimum momentum to required TOF PID for Triton"}; o2::framework::Configurable> itsTriton{"itsTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Triton PID"}; o2::framework::Configurable> tpcTriton{"tpcTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Triton PID"}; - o2::framework::Configurable> tofTriton{"tofTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Triton PID"}; + o2::framework::Configurable> tofTriton{"tofTriton", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Triton PID. Momentum threshold above which TOF is required: minMomTofTriton"}; o2::framework::Configurable> tpcitsTriton{"tpcitsTriton", {}, "Maximum nsigma_TPCITS for Triton PID"}; - o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum nsigma_TPCTOF for Triton PID"}; + o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum nsigma_TPCTOF for Triton PID. Momentum threshold above which TOF is required: minMomTofTriton"}; // Helium PID cuts - o2::framework::Configurable requirePidHelium{"requirePidHelium", false, "Make election PID optional"}; + o2::framework::Configurable requirePidHelium{"requirePidHelium", false, "Make helium PID optional"}; o2::framework::Configurable minMomTofHelium{"minMomTofHelium", 1.6, "Minimum momentum to required TOF PID for Helium"}; o2::framework::Configurable> itsHelium{"itsHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_ITS for Helium PID"}; o2::framework::Configurable> tpcHelium{"tpcHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TPC for Helium PID"}; - o2::framework::Configurable> tofHelium{"tofHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Helium PID"}; + o2::framework::Configurable> tofHelium{"tofHelium", {}, "Ranges LowerLimit;UpperLimit for nsigma_TOF for Helium PID. Momentum threshold above which TOF is required: minMomTofHelium"}; o2::framework::Configurable> tpcitsHelium{"tpcitsHelium", {}, "Maximum nsigma_TPCITS for Helium PID"}; - o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum nsigma_TPCTOF for Helium PID"}; + o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum nsigma_TPCTOF for Helium PID. Momentum threshold above which TOF is required: minMomTofHelium"}; }; // define the template structure for TrackSelection -template +template struct ConfTrackSelection : public o2::framework::ConfigurableGroup { std::string prefix = Prefix; // Unique prefix based on the template argument // configuration parameters o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", 2212, "Absolute value of PDG code. Set sign of charge to -1 for antiparticle."}; - o2::framework::Configurable chargeAbs{"chargeAbs", 1, "Absolute value of charge (e.g. 1 for most tracks, 2 for He3). Set sign of charge to -1 for antiparticle"}; - o2::framework::Configurable chargeSign{"chargeSign", 1, "Track charge sign: +1 for positive, -1 for negative, 0 for both"}; + o2::framework::Configurable chargeAbs{"chargeAbs", 1, "Absolute value of charge (e.g. 1 for most tracks, 2 for He3)"}; + o2::framework::Configurable chargeSign{"chargeSign", DefaultChargeSign, "Track charge sign: +1 for positive, -1 for negative, 0 for both"}; // filters for kinematics - o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT (GeV/c)"}; + o2::framework::Configurable ptMin{"ptMin", 0.0f, "Minimum pT (GeV/c)"}; o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT (GeV/c)"}; o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; o2::framework::Configurable massMin{"massMin", 0.f, "Minimum TOF mass (only used if enabled)"}; - o2::framework::Configurable massMax{"massMax", 2.f, "Maximum TOF mass (only used if enabled)"}; + o2::framework::Configurable massMax{"massMax", 99.f, "Maximum TOF mass (only used if enabled)"}; // track selection masks - o2::framework::Configurable maskLowMomentum{"maskLowMomentum", 1ul, "Bitmask for selections below momentum threshold"}; - o2::framework::Configurable maskHighMomentum{"maskHighMomentum", 2ul, "Bitmask for selections above momentum threshold"}; + o2::framework::Configurable maskLowMomentum{"maskLowMomentum", 1ul, "Bitmask for selections below momentum threshold"}; + o2::framework::Configurable maskHighMomentum{"maskHighMomentum", 2ul, "Bitmask for selections above momentum threshold"}; // track rejection masks - o2::framework::Configurable rejectionMaskLowMomentum{"rejectionMaskLowMomentum", 0ul, "Bitmask for rejections below momentum threshold"}; - o2::framework::Configurable rejectionMaskHighMomentum{"rejectionMaskHighMomentum", 0ul, "Bitmask for rejections above momentum threshold"}; + o2::framework::Configurable rejectionMaskLowMomentum{"rejectionMaskLowMomentum", 0ul, "Bitmask for rejections below momentum threshold"}; + o2::framework::Configurable rejectionMaskHighMomentum{"rejectionMaskHighMomentum", 0ul, "Bitmask for rejections above momentum threshold"}; // momentum threshold for PID usage o2::framework::Configurable pidThres{"pidThres", 1.2f, "Momentum threshold for using TPCTOF/TOF pid for tracks with large momentum (GeV/c)"}; + // which species slot this selection corresponds to in the packed purity tables (FitVars only) + o2::framework::Configurable puritySlotMask{"puritySlotMask", 1, "Bitmask (bit N = slot N) selecting which purity slot to plot, read off hPuritySlotAssignment in the producer output."}; }; // Define unique prefixes as constexpr string literals @@ -161,21 +166,55 @@ constexpr const char PrefixTrackSelection1[] = "TrackSelection1"; constexpr const char PrefixTrackSelection2[] = "TrackSelection2"; constexpr const char PrefixTrackSelection3[] = "TrackSelection3"; -// for resonances builder -constexpr const char PrefixPionPlus[] = "PionPlusSelection"; -constexpr const char PrefixPionMinus[] = "PionMinusSelection"; -constexpr const char PrefixKaonPlus[] = "KaonPlusSelection"; -constexpr const char PrefixKaonMinus[] = "KaonMinusSelection"; +// for dN/deta: tracks with TPC (ITS + TPC quality bits) and tracks without TPC (ITS quality bits only) +// set maskLowMomentum = maskHighMomentum (quality bits only), chargeSign = 0 and the eta/pT range of the measurement +constexpr const char PrefixTrackSelectionDndetaGlobal[] = "TrackSelectionDndetaGlobal"; // Global = ITS + TPC +constexpr const char PrefixTrackSelectionDndetaItsOnly[] = "TrackSelectionDndetaItsOnly"; // ITS only // Instantiate different instances with unique prefixes using ConfTrackSelection1 = ConfTrackSelection; using ConfTrackSelection2 = ConfTrackSelection; using ConfTrackSelection3 = ConfTrackSelection; -using ConfPionPlusSelection = ConfTrackSelection; -using ConfPionMinusSelection = ConfTrackSelection; -using ConfKaonPlusSelection = ConfTrackSelection; -using ConfKaonMinusSelection = ConfTrackSelection; +using ConfTrackSelectionDndetaGlobal = ConfTrackSelection; +using ConfTrackSelectionDndetaItsOnly = ConfTrackSelection; + +// Track selection for resonance daughters (pion+/-, kaon+/-). Charge sign and abs charge are fixed +// by the species itself, so they are compile-time constants rather than configurables. No pdg code +// here at all: the daughter mass used to reconstruct the resonance is set directly in the resonance +// builder (e.g. MassPiPlus/MassKMinus), never read from this selection. Used exclusively by the +// resonance builders via MAKE_RESONANCE_DAUGHTER_PARTITION. +template +struct ConfResonanceDaughterSelection : public o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + static const int chargeAbs = 1; + static const int chargeSign = ChargeSign; + // filters for kinematics + o2::framework::Configurable ptMin{"ptMin", 0.0f, "Minimum pT (GeV/c)"}; + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT (GeV/c)"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + // track selection masks + o2::framework::Configurable maskLowMomentum{"maskLowMomentum", 1ul, "Bitmask for selections below momentum threshold"}; + o2::framework::Configurable maskHighMomentum{"maskHighMomentum", 2ul, "Bitmask for selections above momentum threshold"}; + // track rejection masks + o2::framework::Configurable rejectionMaskLowMomentum{"rejectionMaskLowMomentum", 0ul, "Bitmask for rejections below momentum threshold"}; + o2::framework::Configurable rejectionMaskHighMomentum{"rejectionMaskHighMomentum", 0ul, "Bitmask for rejections above momentum threshold"}; + // momentum threshold for PID usage + o2::framework::Configurable pidThres{"pidThres", PidThresDefault, "Momentum threshold for using TPCTOF/TOF pid for tracks with large momentum (GeV/c)"}; +}; + +constexpr const char PrefixPionPlus[] = "PionPlusSelection"; +constexpr const char PrefixPionMinus[] = "PionMinusSelection"; +constexpr const char PrefixKaonPlus[] = "KaonPlusSelection"; +constexpr const char PrefixKaonMinus[] = "KaonMinusSelection"; + +using ConfPionPlusSelection = ConfResonanceDaughterSelection; +using ConfPionMinusSelection = ConfResonanceDaughterSelection; +using ConfKaonPlusSelection = ConfResonanceDaughterSelection; +using ConfKaonMinusSelection = ConfResonanceDaughterSelection; /// enum for all track selections enum TrackSels { @@ -189,9 +228,11 @@ enum TrackSels { kITSnClsIbMin, ///< Min. number of ITS clusters in the inner barrel kDCAxyMax, ///< Max. |DCA_xy| (cm) as a function of pT kDCAzMax, ///< Max. |DCA_z| (cm) as a function of pT + kITSchi2Max, ///< Max. ITS chi2 per cluster + kTPCchi2Max, ///< Max. TPC chi2 per cluster /// track pid cuts - kItsElectron, ///< ITS Electon PID + kItsElectron, ///< ITS Electron PID kItsPion, ///< ITS Pion PID kItsKaon, ///< ITS Kaon PID kItsProton, ///< ITS Proton PID @@ -199,7 +240,7 @@ enum TrackSels { kItsTriton, ///< ITS Triton PID kItsHelium, ///< ITS He3 PID - kTpcElectron, ///< TPC Electon PID + kTpcElectron, ///< TPC Electron PID kTpcPion, ///< TPC Pion PID kTpcKaon, ///< TPC Kaon PID kTpcProton, ///< TPC Proton PID @@ -207,7 +248,7 @@ enum TrackSels { kTpcTriton, ///< TPC Triton PID kTpcHelium, ///< TPC He3 PID - kTofElectron, ///< TOF Electon PID + kTofElectron, ///< TOF Electron PID kTofPion, ///< TOF Pion PID kTofKaon, ///< TOF Kaon PID kTofProton, ///< TOF Proton PID @@ -215,7 +256,7 @@ enum TrackSels { kTofTriton, ///< TOF Triton PID kTofHelium, ///< TOF He3 PID - kTpcitsElectron, ///< TPC+ITS Electon PID + kTpcitsElectron, ///< TPC+ITS Electron PID kTpcitsPion, ///< TPC+ITS Pion PID kTpcitsKaon, ///< TPC+ITS Kaon PID kTpcitsProton, ///< TPC+ITS Proton PID @@ -223,7 +264,7 @@ enum TrackSels { kTpcitsTriton, ///< TPC+ITS Triton PID kTpcitsHelium, ///< TPC+ITS He3 PID - kTpctofElectron, ///< TPC+TOF Electon PID + kTpctofElectron, ///< TPC+TOF Electron PID kTpctofPion, ///< TPC+TOF Pion PID kTpctofKaon, ///< TPC+TOF Kaon PID kTpctofProton, ///< TPC+TOF Proton PID @@ -241,12 +282,14 @@ const std::unordered_map trackSelectionNames = { {kTPCcRowsMin, "Min. number of crossed TPC rows"}, {kTPCnClsOvercRowsMin, "Min. fraction of TPC clusters over TPC crossed rows"}, {kTPCsClsMax, "Max. number of shared TPC clusters"}, - {kTPCsClsMax, "Max. number of shared TPC clusters"}, + // NOTE: removed duplicate {kTPCsClsMax, ...} entry that was here (harmless but dead duplicate key). {kTPCsClsFracMax, "Max. fractions of shared TPC clusters"}, {kITSnClsMin, "Min. number of ITS clusters"}, {kITSnClsIbMin, "Min. number of ITS clusters in the inner barrel"}, {kDCAxyMax, "Max. |DCA_xy| (cm) as a function of pT"}, {kDCAzMax, "Max. |DCA_z| (cm) as a function of pT"}, + {kITSchi2Max, "Max. ITS chi2 per cluster"}, + {kTPCchi2Max, "Max. TPC chi2 per cluster"}, {kItsElectron, "ITS Electron PID"}, {kItsPion, "ITS Pion PID"}, @@ -278,7 +321,7 @@ const std::unordered_map trackSelectionNames = { {kTpcitsProton, "TPC+ITS Proton PID"}, {kTpcitsDeuteron, "TPC+ITS Deuteron PID"}, {kTpcitsTriton, "TPC+ITS Triton PID"}, - {kTpcitsHelium, "TPC+ITS He PID"}, + {kTpcitsHelium, "TPC+ITS He3 PID"}, {kTpctofElectron, "TPC+TOF Electron PID"}, {kTpctofPion, "TPC+TOF Pion PID"}, @@ -288,18 +331,40 @@ const std::unordered_map trackSelectionNames = { {kTpctofTriton, "TPC+TOF Triton PID"}, {kTpctofHelium, "TPC+TOF He3 PID"}}; -/// \class FemtoDreamTrackCuts +// enum for all track filters (loose pre-selection, applied before quality/PID cuts) +enum TrackFilters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kTrackFiltersMax +}; + +constexpr char TrackFilterHistName[] = "hTrackFilters"; +const std::unordered_map trackFilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}}; + +/// \class TrackSelecion /// \brief Cut class to contain and execute all cuts applied to tracks -template -class TrackSelection : public BaseSelection +template +class TrackSelection : public baseselection::BaseSelection { public: TrackSelection() = default; - ~TrackSelection() = default; + ~TrackSelection() override = default; template void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) { + this->init(config.passThrough.value); + // filters mPtMin = filter.ptMin.value; mPtMax = filter.ptMax.value; @@ -308,90 +373,133 @@ class TrackSelection : public BaseSelectionaddSelection(kTPCnClsMin, trackSelectionNames.at(kTPCnClsMin), config.tpcClustersMin.value, limits::kLowerLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kTPCcRowsMin, trackSelectionNames.at(kTPCcRowsMin), config.tpcCrossedRowsMin.value, limits::kLowerLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kTPCnClsOvercRowsMin, trackSelectionNames.at(kTPCnClsOvercRowsMin), config.tpcClustersOverCrossedRows.value, limits::kLowerLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kTPCsClsMax, trackSelectionNames.at(kTPCsClsMax), config.tpcSharedClustersMax.value, limits::kUpperLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kTPCsClsFracMax, trackSelectionNames.at(kTPCsClsFracMax), config.tpcSharedClusterFractionMax.value, limits::kUpperLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kITSnClsMin, trackSelectionNames.at(kITSnClsMin), config.itsClustersMin.value, limits::kLowerLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kITSnClsIbMin, trackSelectionNames.at(kITSnClsIbMin), config.itsIbClustersMin.value, limits::kLowerLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kDCAxyMax, trackSelectionNames.at(kDCAxyMax), filter.ptMin.value, filter.ptMax.value, config.dcaxyMax.value, limits::kAbsUpperFunctionLimit, skipMostPermissiveBit, isMinimalCut, false); - this->addSelection(kDCAzMax, trackSelectionNames.at(kDCAzMax), filter.ptMin.value, filter.ptMax.value, config.dcazMax.value, limits::kAbsUpperFunctionLimit, skipMostPermissiveBit, isMinimalCut, false); + this->addSelection(kTPCnClsMin, trackSelectionNames.at(kTPCnClsMin), config.tpcClustersMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTPCcRowsMin, trackSelectionNames.at(kTPCcRowsMin), config.tpcCrossedRowsMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTPCnClsOvercRowsMin, trackSelectionNames.at(kTPCnClsOvercRowsMin), config.tpcClustersOverCrossedRows.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTPCsClsMax, trackSelectionNames.at(kTPCsClsMax), config.tpcSharedClustersMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kTPCsClsFracMax, trackSelectionNames.at(kTPCsClsFracMax), config.tpcSharedClusterFractionMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kITSnClsMin, trackSelectionNames.at(kITSnClsMin), config.itsClustersMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kITSnClsIbMin, trackSelectionNames.at(kITSnClsIbMin), config.itsIbClustersMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDCAxyMax, trackSelectionNames.at(kDCAxyMax), filter.ptMin.value, filter.ptMax.value, config.dcaxyMax.value, limits::kAbsUpperFunctionLimit, true, true, false); + this->addSelection(kDCAzMax, trackSelectionNames.at(kDCAzMax), filter.ptMin.value, filter.ptMax.value, config.dcazMax.value, limits::kAbsUpperFunctionLimit, true, true, false); + this->addSelection(kITSchi2Max, trackSelectionNames.at(kITSchi2Max), config.itsChi2Max.value, limits::kUpperLimit, true, true, false); + this->addSelection(kTPCchi2Max, trackSelectionNames.at(kTPCchi2Max), config.tpcChi2Max.value, limits::kUpperLimit, true, true, false); // add selections for Electron pid - this->addSelection(kItsElectron, trackSelectionNames.at(kItsElectron), config.itsElectron.value, false, false, isOptionalCut && config.requirePidElectron); - this->addSelection(kTpcElectron, trackSelectionNames.at(kTpcElectron), config.tpcElectron.value, false, false, isOptionalCut && config.requirePidElectron); - this->addSelection(kTofElectron, trackSelectionNames.at(kTofElectron), config.tofElectron.value, false, false, isOptionalCut && config.requirePidElectron); - this->addSelection(kTpcitsElectron, trackSelectionNames.at(kTpcitsElectron), config.tpcitsElectron.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidElectron); - this->addSelection(kTpctofElectron, trackSelectionNames.at(kTpctofElectron), config.tpctofElectron.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidElectron); + this->addSelection(kItsElectron, trackSelectionNames.at(kItsElectron), config.itsElectron.value, false, false, config.requirePidElectron); + this->addSelection(kTpcElectron, trackSelectionNames.at(kTpcElectron), config.tpcElectron.value, false, false, config.requirePidElectron); + this->addSelection(kTofElectron, trackSelectionNames.at(kTofElectron), config.tofElectron.value, false, false, config.requirePidElectron); + this->addComments(kTofElectron, "minMomTofElectron = " + std::to_string(config.minMomTofElectron.value)); + this->addSelection(kTpcitsElectron, trackSelectionNames.at(kTpcitsElectron), config.tpcitsElectron.value, limits::kUpperLimit, false, false, config.requirePidElectron); + this->addSelection(kTpctofElectron, trackSelectionNames.at(kTpctofElectron), config.tpctofElectron.value, limits::kUpperLimit, false, false, config.requirePidElectron); + this->addComments(kTpctofElectron, "minMomTofElectron = " + std::to_string(config.minMomTofElectron.value)); mElectronTofThres = config.minMomTofElectron.value; // add selections for Pion pid - this->addSelection(kItsPion, trackSelectionNames.at(kItsPion), config.itsPion.value, false, false, isOptionalCut && config.requirePidPion); - this->addSelection(kTpcPion, trackSelectionNames.at(kTpcPion), config.tpcPion.value, false, false, isOptionalCut && config.requirePidPion); - this->addSelection(kTofPion, trackSelectionNames.at(kTofPion), config.tofPion.value, false, false, isOptionalCut && config.requirePidPion); - this->addSelection(kTpcitsPion, trackSelectionNames.at(kTpcitsPion), config.tpcitsPion.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidPion); - this->addSelection(kTpctofPion, trackSelectionNames.at(kTpctofPion), config.tpctofPion.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidPion); + this->addSelection(kItsPion, trackSelectionNames.at(kItsPion), config.itsPion.value, false, false, config.requirePidPion); + this->addSelection(kTpcPion, trackSelectionNames.at(kTpcPion), config.tpcPion.value, false, false, config.requirePidPion); + this->addSelection(kTofPion, trackSelectionNames.at(kTofPion), config.tofPion.value, false, false, config.requirePidPion); + this->addComments(kTofPion, "minMomTofPion = " + std::to_string(config.minMomTofPion.value)); + this->addSelection(kTpcitsPion, trackSelectionNames.at(kTpcitsPion), config.tpcitsPion.value, limits::kUpperLimit, false, false, config.requirePidPion); + this->addSelection(kTpctofPion, trackSelectionNames.at(kTpctofPion), config.tpctofPion.value, limits::kUpperLimit, false, false, config.requirePidPion); + this->addComments(kTpctofPion, "minMomTofPion = " + std::to_string(config.minMomTofPion.value)); mPionTofThres = config.minMomTofPion.value; // add selections for Kaon pid - this->addSelection(kItsKaon, trackSelectionNames.at(kItsKaon), config.itsKaon.value, false, false, isOptionalCut && config.requirePidKaon); - this->addSelection(kTpcKaon, trackSelectionNames.at(kTpcKaon), config.tpcKaon.value, false, false, isOptionalCut && config.requirePidKaon); - this->addSelection(kTofKaon, trackSelectionNames.at(kTofKaon), config.tofKaon.value, false, false, isOptionalCut && config.requirePidKaon); - this->addSelection(kTpcitsKaon, trackSelectionNames.at(kTpcitsKaon), config.tpcitsKaon.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidKaon); - this->addSelection(kTpctofKaon, trackSelectionNames.at(kTpctofKaon), config.tpctofKaon.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidKaon); + this->addSelection(kItsKaon, trackSelectionNames.at(kItsKaon), config.itsKaon.value, false, false, config.requirePidKaon); + this->addSelection(kTpcKaon, trackSelectionNames.at(kTpcKaon), config.tpcKaon.value, false, false, config.requirePidKaon); + this->addSelection(kTofKaon, trackSelectionNames.at(kTofKaon), config.tofKaon.value, false, false, config.requirePidKaon); + this->addComments(kTofKaon, "minMomTofKaon = " + std::to_string(config.minMomTofKaon.value)); + this->addSelection(kTpcitsKaon, trackSelectionNames.at(kTpcitsKaon), config.tpcitsKaon.value, limits::kUpperLimit, false, false, config.requirePidKaon); + this->addSelection(kTpctofKaon, trackSelectionNames.at(kTpctofKaon), config.tpctofKaon.value, limits::kUpperLimit, false, false, config.requirePidKaon); + this->addComments(kTpctofKaon, "minMomTofKaon = " + std::to_string(config.minMomTofKaon.value)); mKaonTofThres = config.minMomTofKaon.value; // add selections for Proton pid - this->addSelection(kItsProton, trackSelectionNames.at(kItsProton), config.itsProton.value, false, false, isOptionalCut && config.requirePidProton); - this->addSelection(kTpcProton, trackSelectionNames.at(kTpcProton), config.tpcProton.value, false, false, isOptionalCut && config.requirePidProton); - this->addSelection(kTofProton, trackSelectionNames.at(kTofProton), config.tofProton.value, false, false, isOptionalCut && config.requirePidProton); - this->addSelection(kTpcitsProton, trackSelectionNames.at(kTpcitsProton), config.tpcitsProton.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidProton); - this->addSelection(kTpctofProton, trackSelectionNames.at(kTpctofProton), config.tpctofProton.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidProton); + this->addSelection(kItsProton, trackSelectionNames.at(kItsProton), config.itsProton.value, false, false, config.requirePidProton); + this->addSelection(kTpcProton, trackSelectionNames.at(kTpcProton), config.tpcProton.value, false, false, config.requirePidProton); + this->addSelection(kTofProton, trackSelectionNames.at(kTofProton), config.tofProton.value, false, false, config.requirePidProton); + this->addComments(kTofProton, "minMomTofProton = " + std::to_string(config.minMomTofProton.value)); + this->addSelection(kTpcitsProton, trackSelectionNames.at(kTpcitsProton), config.tpcitsProton.value, limits::kUpperLimit, false, false, config.requirePidProton); + this->addSelection(kTpctofProton, trackSelectionNames.at(kTpctofProton), config.tpctofProton.value, limits::kUpperLimit, false, false, config.requirePidProton); + this->addComments(kTpctofProton, "minMomTofProton = " + std::to_string(config.minMomTofProton.value)); mProtonTofThres = config.minMomTofProton.value; // add selections for Deuteron pid - this->addSelection(kItsDeuteron, trackSelectionNames.at(kItsDeuteron), config.itsDeuteron.value, false, false, isOptionalCut && config.requirePidDeuteron); - this->addSelection(kTpcDeuteron, trackSelectionNames.at(kTpcDeuteron), config.tpcDeuteron.value, false, false, isOptionalCut && config.requirePidDeuteron); - this->addSelection(kTofDeuteron, trackSelectionNames.at(kTofDeuteron), config.tofDeuteron.value, false, false, isOptionalCut && config.requirePidDeuteron); - this->addSelection(kTpcitsDeuteron, trackSelectionNames.at(kTpcitsDeuteron), config.tpcitsDeuteron.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidDeuteron); - this->addSelection(kTpctofDeuteron, trackSelectionNames.at(kTpctofDeuteron), config.tpctofDeuteron.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidDeuteron); + this->addSelection(kItsDeuteron, trackSelectionNames.at(kItsDeuteron), config.itsDeuteron.value, false, false, config.requirePidDeuteron); + this->addSelection(kTpcDeuteron, trackSelectionNames.at(kTpcDeuteron), config.tpcDeuteron.value, false, false, config.requirePidDeuteron); + this->addSelection(kTofDeuteron, trackSelectionNames.at(kTofDeuteron), config.tofDeuteron.value, false, false, config.requirePidDeuteron); + this->addComments(kTofDeuteron, "minMomTofDeuteron = " + std::to_string(config.minMomTofDeuteron.value)); + this->addSelection(kTpcitsDeuteron, trackSelectionNames.at(kTpcitsDeuteron), config.tpcitsDeuteron.value, limits::kUpperLimit, false, false, config.requirePidDeuteron); + this->addSelection(kTpctofDeuteron, trackSelectionNames.at(kTpctofDeuteron), config.tpctofDeuteron.value, limits::kUpperLimit, false, false, config.requirePidDeuteron); + this->addComments(kTpctofDeuteron, "minMomTofDeuteron = " + std::to_string(config.minMomTofDeuteron.value)); mDeuteronTofThres = config.minMomTofDeuteron.value; // add selections for Triton pid - this->addSelection(kItsTriton, trackSelectionNames.at(kItsTriton), config.itsTriton.value, false, false, isOptionalCut && config.requirePidTriton); - this->addSelection(kTpcTriton, trackSelectionNames.at(kTpcTriton), config.tpcTriton.value, false, false, isOptionalCut && config.requirePidTriton); - this->addSelection(kTofTriton, trackSelectionNames.at(kTofTriton), config.tofTriton.value, false, false, isOptionalCut && config.requirePidTriton); - this->addSelection(kTpcitsTriton, trackSelectionNames.at(kTpcitsTriton), config.tpcitsTriton.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidTriton); - this->addSelection(kTpctofTriton, trackSelectionNames.at(kTpctofTriton), config.tpctofTriton.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidTriton); + this->addSelection(kItsTriton, trackSelectionNames.at(kItsTriton), config.itsTriton.value, false, false, config.requirePidTriton); + this->addSelection(kTpcTriton, trackSelectionNames.at(kTpcTriton), config.tpcTriton.value, false, false, config.requirePidTriton); + this->addSelection(kTofTriton, trackSelectionNames.at(kTofTriton), config.tofTriton.value, false, false, config.requirePidTriton); + this->addComments(kTofTriton, "minMomTofTriton = " + std::to_string(config.minMomTofTriton.value)); + this->addSelection(kTpcitsTriton, trackSelectionNames.at(kTpcitsTriton), config.tpcitsTriton.value, limits::kUpperLimit, false, false, config.requirePidTriton); + this->addSelection(kTpctofTriton, trackSelectionNames.at(kTpctofTriton), config.tpctofTriton.value, limits::kUpperLimit, false, false, config.requirePidTriton); + this->addComments(kTpctofTriton, "minMomTofTriton = " + std::to_string(config.minMomTofTriton.value)); mTritonTofThres = config.minMomTofTriton.value; // add selections for Helium pid - this->addSelection(kItsHelium, trackSelectionNames.at(kItsHelium), config.itsHelium.value, false, false, isOptionalCut && config.requirePidHelium); - this->addSelection(kTpcHelium, trackSelectionNames.at(kTpcHelium), config.tpcHelium.value, false, false, isOptionalCut && config.requirePidHelium); - this->addSelection(kTofHelium, trackSelectionNames.at(kTofHelium), config.tofHelium.value, false, false, isOptionalCut && config.requirePidHelium); - this->addSelection(kTpcitsHelium, trackSelectionNames.at(kTpcitsHelium), config.tpcitsHelium.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidHelium); - this->addSelection(kTpctofHelium, trackSelectionNames.at(kTpctofHelium), config.tpctofHelium.value, limits::kUpperLimit, false, false, isOptionalCut && config.requirePidHelium); + this->addSelection(kItsHelium, trackSelectionNames.at(kItsHelium), config.itsHelium.value, false, false, config.requirePidHelium); + this->addSelection(kTpcHelium, trackSelectionNames.at(kTpcHelium), config.tpcHelium.value, false, false, config.requirePidHelium); + this->addSelection(kTofHelium, trackSelectionNames.at(kTofHelium), config.tofHelium.value, false, false, config.requirePidHelium); + this->addComments(kTofHelium, "minMomTofHelium = " + std::to_string(config.minMomTofHelium.value)); + this->addSelection(kTpcitsHelium, trackSelectionNames.at(kTpcitsHelium), config.tpcitsHelium.value, limits::kUpperLimit, false, false, config.requirePidHelium); + this->addSelection(kTpctofHelium, trackSelectionNames.at(kTpctofHelium), config.tpctofHelium.value, limits::kUpperLimit, false, false, config.requirePidHelium); + this->addComments(kTpctofHelium, "minMomTofHelium = " + std::to_string(config.minMomTofHelium.value)); mHeliumTofThres = config.minMomTofHelium.value; - this->setupContainers(registry); + this->setupSelectionHistogram(registry); + this->template setupFilterHistogram( + registry, + {{trackFilterNames.at(kPtMin), mPtMin}, + {trackFilterNames.at(kPtMax), mPtMax}, + {trackFilterNames.at(kEtaMin), mEtaMin}, + {trackFilterNames.at(kEtaMax), mEtaMax}, + {trackFilterNames.at(kPhiMin), mPhiMin}, + {trackFilterNames.at(kPhiMax), mPhiMax}}); } template bool checkFilters(T const& track) const { - return ((track.pt() > mPtMin && track.pt() < mPtMax) && - (track.eta() > mEtaMin && track.eta() < mEtaMax) && - (track.phi() > mPhiMin && track.phi() < mPhiMax)); + bool pass = true; + bool p = false; + + p = track.pt() > mPtMin; + this->template fillFilter(kPtMin, p); + pass &= p; + + p = track.pt() < mPtMax; + this->template fillFilter(kPtMax, p); + pass &= p; + + p = track.eta() > mEtaMin; + this->template fillFilter(kEtaMin, p); + pass &= p; + + p = track.eta() < mEtaMax; + this->template fillFilter(kEtaMax, p); + pass &= p; + + p = track.phi() > mPhiMin; + this->template fillFilter(kPhiMin, p); + pass &= p; + + p = track.phi() < mPhiMax; + this->template fillFilter(kPhiMax, p); + pass &= p; + + this->template fillFilterSummary(pass); + + return this->isPassThrough() || pass; } template @@ -436,9 +544,23 @@ class TrackSelection : public BaseSelectionevaluateObservable(kDCAxyMax, Track.dcaXY()); this->updateLimits(kDCAzMax, Track.pt()); this->evaluateObservable(kDCAzMax, Track.dcaZ()); - - if (mPassThrough) { - // if pass through is activated, we evaluate each particle hypothesis regradless of threshold + this->evaluateObservable(kITSchi2Max, Track.itsChi2NCl()); + this->evaluateObservable(kTPCchi2Max, Track.tpcChi2NCl()); + + // first pass: threshold-aware PID evaluation + // determines if the track passes any optional selection and if should be stored in the first place + this->evaluatePid(Track, mElectronTofThres, Track.itsNSigmaEl(), Track.tpcNSigmaEl(), Track.tofNSigmaEl(), kItsElectron, kTpcElectron, kTofElectron, kTpcitsElectron, kTpctofElectron); + this->evaluatePid(Track, mPionTofThres, Track.itsNSigmaPi(), Track.tpcNSigmaPi(), Track.tofNSigmaPi(), kItsPion, kTpcPion, kTofPion, kTpcitsPion, kTpctofPion); + this->evaluatePid(Track, mKaonTofThres, Track.itsNSigmaKa(), Track.tpcNSigmaKa(), Track.tofNSigmaKa(), kItsKaon, kTpcKaon, kTofKaon, kTpcitsKaon, kTpctofKaon); + this->evaluatePid(Track, mProtonTofThres, Track.itsNSigmaPr(), Track.tpcNSigmaPr(), Track.tofNSigmaPr(), kItsProton, kTpcProton, kTofProton, kTpcitsProton, kTpctofProton); + this->evaluatePid(Track, mDeuteronTofThres, Track.itsNSigmaDe(), Track.tpcNSigmaDe(), Track.tofNSigmaDe(), kItsDeuteron, kTpcDeuteron, kTofDeuteron, kTpcitsDeuteron, kTpctofDeuteron); + this->evaluatePid(Track, mTritonTofThres, Track.itsNSigmaTr(), Track.tpcNSigmaTr(), Track.tofNSigmaTr(), kItsTriton, kTpcTriton, kTofTriton, kTpcitsTriton, kTpctofTriton); + this->evaluatePid(Track, mHeliumTofThres, Track.itsNSigmaHe(), Track.tpcNSigmaHe(), Track.tofNSigmaHe(), kItsHelium, kTpcHelium, kTofHelium, kTpcitsHelium, kTpctofHelium); + + // second pass: if the track passes minimal and any optional selection, + // re-evaluate all species ignoring thresholds so rejection bits are fully + // populated for all competing hypotheses + if (this->passesAllRequiredSelections()) { this->evaluatePid(Track, mElectronTofThres, Track.itsNSigmaEl(), Track.tpcNSigmaEl(), Track.tofNSigmaEl(), kItsElectron, kTpcElectron, kTofElectron, kTpcitsElectron, kTpctofElectron, true); this->evaluatePid(Track, mPionTofThres, Track.itsNSigmaPi(), Track.tpcNSigmaPi(), Track.tofNSigmaPi(), kItsPion, kTpcPion, kTofPion, kTpcitsPion, kTpctofPion, true); this->evaluatePid(Track, mKaonTofThres, Track.itsNSigmaKa(), Track.tpcNSigmaKa(), Track.tofNSigmaKa(), kItsKaon, kTpcKaon, kTofKaon, kTpcitsKaon, kTpctofKaon, true); @@ -446,40 +568,11 @@ class TrackSelection : public BaseSelectionevaluatePid(Track, mDeuteronTofThres, Track.itsNSigmaDe(), Track.tpcNSigmaDe(), Track.tofNSigmaDe(), kItsDeuteron, kTpcDeuteron, kTofDeuteron, kTpcitsDeuteron, kTpctofDeuteron, true); this->evaluatePid(Track, mTritonTofThres, Track.itsNSigmaTr(), Track.tpcNSigmaTr(), Track.tofNSigmaTr(), kItsTriton, kTpcTriton, kTofTriton, kTpcitsTriton, kTpctofTriton, true); this->evaluatePid(Track, mHeliumTofThres, Track.itsNSigmaHe(), Track.tpcNSigmaHe(), Track.tofNSigmaHe(), kItsHelium, kTpcHelium, kTofHelium, kTpcitsHelium, kTpctofHelium, true); - } else { - // first pass: threshold-aware PID evaluation - // determines if the track passes any optional selection and if should be stored in the first place - this->evaluatePid(Track, mElectronTofThres, Track.itsNSigmaEl(), Track.tpcNSigmaEl(), Track.tofNSigmaEl(), kItsElectron, kTpcElectron, kTofElectron, kTpcitsElectron, kTpctofElectron); - this->evaluatePid(Track, mPionTofThres, Track.itsNSigmaPi(), Track.tpcNSigmaPi(), Track.tofNSigmaPi(), kItsPion, kTpcPion, kTofPion, kTpcitsPion, kTpctofPion); - this->evaluatePid(Track, mKaonTofThres, Track.itsNSigmaKa(), Track.tpcNSigmaKa(), Track.tofNSigmaKa(), kItsKaon, kTpcKaon, kTofKaon, kTpcitsKaon, kTpctofKaon); - this->evaluatePid(Track, mProtonTofThres, Track.itsNSigmaPr(), Track.tpcNSigmaPr(), Track.tofNSigmaPr(), kItsProton, kTpcProton, kTofProton, kTpcitsProton, kTpctofProton); - this->evaluatePid(Track, mDeuteronTofThres, Track.itsNSigmaDe(), Track.tpcNSigmaDe(), Track.tofNSigmaDe(), kItsDeuteron, kTpcDeuteron, kTofDeuteron, kTpcitsDeuteron, kTpctofDeuteron); - this->evaluatePid(Track, mTritonTofThres, Track.itsNSigmaTr(), Track.tpcNSigmaTr(), Track.tofNSigmaTr(), kItsTriton, kTpcTriton, kTofTriton, kTpcitsTriton, kTpctofTriton); - this->evaluatePid(Track, mHeliumTofThres, Track.itsNSigmaHe(), Track.tpcNSigmaHe(), Track.tofNSigmaHe(), kItsHelium, kTpcHelium, kTofHelium, kTpcitsHelium, kTpctofHelium); - - // second pass: if the track passes minimal and any optional selection, - // re-evaluate all species ignoring thresholds so rejection bits are fully - // populated for all competing hypotheses - if (this->passesAllRequiredSelections()) { - this->evaluatePid(Track, mElectronTofThres, Track.itsNSigmaEl(), Track.tpcNSigmaEl(), Track.tofNSigmaEl(), kItsElectron, kTpcElectron, kTofElectron, kTpcitsElectron, kTpctofElectron, true); - this->evaluatePid(Track, mPionTofThres, Track.itsNSigmaPi(), Track.tpcNSigmaPi(), Track.tofNSigmaPi(), kItsPion, kTpcPion, kTofPion, kTpcitsPion, kTpctofPion, true); - this->evaluatePid(Track, mKaonTofThres, Track.itsNSigmaKa(), Track.tpcNSigmaKa(), Track.tofNSigmaKa(), kItsKaon, kTpcKaon, kTofKaon, kTpcitsKaon, kTpctofKaon, true); - this->evaluatePid(Track, mProtonTofThres, Track.itsNSigmaPr(), Track.tpcNSigmaPr(), Track.tofNSigmaPr(), kItsProton, kTpcProton, kTofProton, kTpcitsProton, kTpctofProton, true); - this->evaluatePid(Track, mDeuteronTofThres, Track.itsNSigmaDe(), Track.tpcNSigmaDe(), Track.tofNSigmaDe(), kItsDeuteron, kTpcDeuteron, kTofDeuteron, kTpcitsDeuteron, kTpctofDeuteron, true); - this->evaluatePid(Track, mTritonTofThres, Track.itsNSigmaTr(), Track.tpcNSigmaTr(), Track.tofNSigmaTr(), kItsTriton, kTpcTriton, kTofTriton, kTpcitsTriton, kTpctofTriton, true); - this->evaluatePid(Track, mHeliumTofThres, Track.itsNSigmaHe(), Track.tpcNSigmaHe(), Track.tofNSigmaHe(), kItsHelium, kTpcHelium, kTofHelium, kTpcitsHelium, kTpctofHelium, true); - } } this->assembleBitmask(); } - bool - passThroughAllTracks() const - { - return mPassThrough; - } - protected: float mElectronTofThres = 99.f; float mPionTofThres = 99.f; @@ -495,12 +588,11 @@ class TrackSelection : public BaseSelection producedTracks; + o2::framework::Produces producedLiteTracks; o2::framework::Produces producedTrackMass; o2::framework::Produces producedTrackMasks; o2::framework::Produces producedTrackDcas; @@ -512,11 +604,15 @@ struct TrackBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedDeuteronPids; o2::framework::Produces producedTritonPids; o2::framework::Produces producedHeliumPids; + o2::framework::Produces producedItsPurity; + o2::framework::Produces producedTpcPurity; + o2::framework::Produces producedTofPurity; }; struct ConfTrackTables : o2::framework::ConfigurableGroup { std::string prefix = std::string("TrackTables"); o2::framework::Configurable produceTracks{"produceTracks", -1, "Produce Tracks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteTracks{"produceLiteTracks", -1, "Produce LiteTracks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceTrackMasks{"produceTrackMasks", -1, "Produce TrackMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceTrackMass{"produceTrackMass", -1, "Produce TrackMass (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceTrackDcas{"produceTrackDcas", -1, "Produce TrackDcas (-1: auto; 0 off; 1 on)"}; @@ -528,21 +624,25 @@ struct ConfTrackTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceDeuteronPids{"produceDeuteronPids", -1, "Produce DeuteronPids (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceTritonPids{"produceTritonPids", -1, "Produce TritonPids (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceHeliumPids{"produceHeliumPids", -1, "Produce HeliumPids (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceItsPurity{"produceItsPurity", -1, "Produce ItsPurity (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTpcPurity{"produceTpcPurity", -1, "Produce TpcPurity (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceTofPurity{"produceTofPurity", -1, "Produce TofPurity (-1: auto; 0 off; 1 on)"}; }; -template +template class TrackBuilder { public: TrackBuilder() = default; ~TrackBuilder() = default; - template - void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext, T5& collisionBuilder) { LOG(info) << "Initialize femto track builder..."; mProduceTracks = utils::enableTable("FTracks_001", table.produceTracks.value, initContext); + mProduceLiteTracks = utils::enableTable("FLiteTracks_001", table.produceLiteTracks.value, initContext); // new mProduceTrackMasks = utils::enableTable("FTrackMasks_001", table.produceTrackMasks.value, initContext); mProduceTrackMass = utils::enableTable("FTrackMass_001", table.produceTrackMass.value, initContext); mProduceTrackDcas = utils::enableTable("FTrackDcas_001", table.produceTrackDcas.value, initContext); @@ -554,8 +654,78 @@ class TrackBuilder mProduceDeuteronPids = utils::enableTable("FDeuteronPids_001", table.produceDeuteronPids.value, initContext); mProduceTritonPids = utils::enableTable("FTritonPids_001", table.produceTritonPids.value, initContext); mProduceHeliumPids = utils::enableTable("FHeliumPids_001", table.produceHeliumPids.value, initContext); + mProduceItsPurity = utils::enableTable("FItsPurity_001", table.produceItsPurity.value, initContext); + mProduceTpcPurity = utils::enableTable("FTpcPurity_001", table.produceTpcPurity.value, initContext); + mProduceTofPurity = utils::enableTable("FTofPurity_001", table.produceTofPurity.value, initContext); + + if (mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { + // assign each active species (requirePidXxx == true) to the next free slot (0-3), in + // canonical order (PuritySpeciesName); species with requirePidXxx == false get no slot. + const std::array requirePid = { + config.requirePidElectron.value, config.requirePidPion.value, config.requirePidKaon.value, + config.requirePidProton.value, config.requirePidDeuteron.value, config.requirePidTriton.value, config.requirePidHelium.value}; + + int nextSlot = 0; + for (int i = 0; i < PuritySpeciesLast; i++) { + if (!requirePid[i]) { + continue; + } + if (nextSlot >= utils::PurityNSlots) { + LOG(fatal) << "More than " << utils::PurityNSlots << " species have requirePidXxx enabled, " + << "but the purity tables only have " << utils::PurityNSlots << " slots. " + << "Disable requirePidXxx for species you don't need purity info for. Breaking..."; + } + mPuritySlot[i] = nextSlot; + nextSlot++; + } + + // one bin per species (own SelectionName -> own cutculator group), plus the usual + // trailing "All analyzed"/"All passed" pair that cutculator always skips by position. + // BitPosition___X (unassigned species) is deliberately excluded from cutculator's + // "neutral" category, so nothing is selectable for a species that isn't configured here. + // Picking the one assigned bit for a species gives exactly 1<add("hPuritySlotAssignment", "Purity slot assignment", o2::framework::HistType::kTH1F, {{PuritySpeciesLast + 2, -0.5, PuritySpeciesLast + 2 - 0.5}}); + auto hSlotAssignment = registry->get(HIST("hPuritySlotAssignment")); + for (int i = 0; i < PuritySpeciesLast; i++) { + std::string bitPosition = mPuritySlot[i] >= 0 ? std::to_string(mPuritySlot[i]) : "X"; + std::string value = "Not configured"; + if (mPuritySlot[i] >= 0) { + value = "Slot "; + value += std::to_string(mPuritySlot[i]); + } + std::string label = "SelectionName___"; + label += PuritySpeciesName[i]; + label += ":::MinimalCut___0:::OptionalCut___0:::BitPosition___"; + label += bitPosition; + label += ":::Value___"; + label += value; + hSlotAssignment->GetXaxis()->SetBinLabel(i + 1, label.c_str()); + hSlotAssignment->Fill(i, 1); + } + hSlotAssignment->GetXaxis()->SetBinLabel(PuritySpeciesLast + 1, "All analyzed"); + hSlotAssignment->GetXaxis()->SetBinLabel(PuritySpeciesLast + 2, "All passed"); + } + + if (mProduceTracks && mProduceLiteTracks) { + LOG(fatal) << "FTracks and FLiteTracks are mutually exclusive -- enable only one. " + << "FLiteTracks is meant to replace FTracks at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FTracks from FLiteTracks downstream."; + } - if (mProduceTracks || mProduceTrackMasks || mProduceTrackMass || mProduceTrackDcas || mProduceTrackExtras || mProduceElectronPids || mProducePionPids || mProduceKaonPids || mProduceProtonPids || mProduceDeuteronPids || mProduceTritonPids || mProduceHeliumPids) { + if (mProduceTracks && !collisionBuilder.producingCollisions()) { + LOG(fatal) << "FTracks is enabled, but the collision builder is not producing FCols (full precision). " + << "FTracks stores the collision index into FCols -- enable CollisionTables.produceCollisions, " + << "or switch to FLiteTracks if CollisionTables.produceLiteCollisions is enabled instead."; + } + if (mProduceLiteTracks && !collisionBuilder.producingLiteCollisions()) { + LOG(fatal) << "FLiteTracks is enabled, but the collision builder is not producing FLiteCols. " + << "FLiteTracks stores the collision index into FLiteCols -- enable CollisionTables.produceLiteCollisions, " + << "or switch to FTracks if CollisionTables.produceCollisions is enabled instead."; + } + + if (mProduceTracks || mProduceLiteTracks || mProduceTrackMasks || mProduceTrackMass || mProduceTrackDcas || mProduceTrackExtras || mProduceElectronPids || mProducePionPids || mProduceKaonPids || mProduceProtonPids || mProduceDeuteronPids || mProduceTritonPids || mProduceHeliumPids || mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -574,38 +744,65 @@ class TrackBuilder return; } for (const auto& track : tracks) { - if (!mTrackSelection.passThroughAllTracks() && !mTrackSelection.checkFilters(track)) { + if (!mTrackSelection.checkFilters(track)) { continue; } mTrackSelection.applySelections(track); - if (!mTrackSelection.passThroughAllTracks() && !mTrackSelection.passesAllRequiredSelections()) { + if (!mTrackSelection.passesAllRequiredSelections()) { continue; } collisionBuilder.template fillCollision(collisionProducts, col); - this->fillTrack(track, trackProducts, collisionProducts); + this->fillTrack(track, trackProducts, collisionBuilder); } } template - void fillTrack(T1 const& track, T2& trackProducts, T3& collisionProducts) + bool fillTrack(T1 const& track, T2& trackProducts, T3& collisionBuilder) { + if (!mProduceTracks && !mProduceLiteTracks) { + return false; + } + // getDaughterIndex() already checks indexMap and skips calling fillTrack() when the track is + // already present, so reaching this point with an existing entry only happens via the direct + // standalone-track path (fillTracks()/fillMcTracks()). Two distinct causes land here: the track + // was already registered as someone's daughter before its own collision was processed, or the + // track is itself associated with more than one collision and was already filled for an earlier + // one. Either way, keep the earlier row rather than writing a duplicate; its mask/fillType + // reflects whichever collision/role filled it first, not necessarily this one. + if (utils::getIndex(track.globalIndex(), indexMap)) { + LOG(warn) << "Track " << track.globalIndex() << " was already filled (as a daughter, or under another " + << "collision it is associated with); skipping the duplicate standalone fill. Its mask/fillType " + << "reflects whichever fill happened first, not necessarily this collision's selection."; + return true; + } + int64_t lastIndex = 0; if (mProduceTracks) { - trackProducts.producedTracks(collisionProducts.producedCollision.lastIndex(), + trackProducts.producedTracks(collisionBuilder.collisionIndex(), track.pt() * track.sign(), track.eta(), track.phi()); - indexMap.emplace(track.globalIndex(), trackProducts.producedTracks.lastIndex()); + lastIndex = trackProducts.producedTracks.lastIndex(); + } + + if (mProduceLiteTracks) { + trackProducts.producedLiteTracks(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(track.pt() * track.sign()), + o2::aod::femtobase::lite::binEta(track.eta()), + o2::aod::femtobase::lite::binPhi(track.phi())); + lastIndex = trackProducts.producedLiteTracks.lastIndex(); } + indexMap.emplace(track.globalIndex(), lastIndex); + if (mProduceTrackMasks) { if constexpr (type == modes::Track::kTrack) { trackProducts.producedTrackMasks(mTrackSelection.getBitmask()); } else { - trackProducts.producedTrackMasks(static_cast(0u)); + trackProducts.producedTrackMasks(static_cast(0u)); } } if (mProduceTrackMass) { - trackProducts.producedTrackMass(track.mass()); + trackProducts.producedTrackMass(track.mass()); // default value if track has no TOF signal is -999 } if (mProduceTrackDcas) { trackProducts.producedTrackDcas(track.dcaXY(), track.dcaZ()); @@ -621,29 +818,99 @@ class TrackBuilder track.tpcNClsFound(), track.tpcNClsCrossedRows(), track.tpcNClsShared(), - track.beta()); + track.beta(), + track.tpcChi2NCl(), + track.detectorMap(), + // fillType distinguishes selected tracks (kTrack) from rows that only exist to resolve a daughter index. + // A daughter whose collisionId differs from the one of its mother candidate is written a second time + // under the mother's collision, so downstream counting must skip isDaughterOnly() rows. + static_cast(type)); } if (mProduceElectronPids) { - trackProducts.producedElectronPids(track.itsNSigmaEl(), track.tpcNSigmaEl(), track.tofNSigmaEl()); + float itsEl = 0.f; + if constexpr (requires(T1 t) { t.itsNSigmaEl(); }) { + itsEl = track.itsNSigmaEl(); + } + trackProducts.producedElectronPids(itsEl, track.tpcNSigmaEl(), track.tofNSigmaEl()); } if (mProducePionPids) { - trackProducts.producedPionPids(track.itsNSigmaPi(), track.tpcNSigmaPi(), track.tofNSigmaPi()); + float itsPi = 0.f; + if constexpr (requires(T1 t) { t.itsNSigmaPi(); }) { + itsPi = track.itsNSigmaPi(); + } + trackProducts.producedPionPids(itsPi, track.tpcNSigmaPi(), track.tofNSigmaPi()); } if (mProduceKaonPids) { - trackProducts.producedKaonPids(track.itsNSigmaKa(), track.tpcNSigmaKa(), track.tofNSigmaKa()); + float itsKa = 0.f; + if constexpr (requires(T1 t) { t.itsNSigmaKa(); }) { + itsKa = track.itsNSigmaKa(); + } + trackProducts.producedKaonPids(itsKa, track.tpcNSigmaKa(), track.tofNSigmaKa()); } if (mProduceProtonPids) { - trackProducts.producedProtonPids(track.itsNSigmaPr(), track.tpcNSigmaPr(), track.tofNSigmaPr()); + float itsPr = 0.f; + if constexpr (requires(T1 t) { t.itsNSigmaPr(); }) { + itsPr = track.itsNSigmaPr(); + } + trackProducts.producedProtonPids(itsPr, track.tpcNSigmaPr(), track.tofNSigmaPr()); } if (mProduceDeuteronPids) { - trackProducts.producedDeuteronPids(track.itsNSigmaDe(), track.tpcNSigmaDe(), track.tofNSigmaDe()); + float itsDe = 0.f; + if constexpr (requires(T1 t) { t.itsNSigmaDe(); }) { + itsDe = track.itsNSigmaDe(); + } + trackProducts.producedDeuteronPids(itsDe, track.tpcNSigmaDe(), track.tofNSigmaDe()); } if (mProduceTritonPids) { - trackProducts.producedTritonPids(track.itsNSigmaTr(), track.tpcNSigmaTr(), track.tofNSigmaTr()); + float itsTr = 0.f; + if constexpr (requires(T1 t) { t.itsNSigmaTr(); }) { + itsTr = track.itsNSigmaTr(); + } + trackProducts.producedTritonPids(itsTr, track.tpcNSigmaTr(), track.tofNSigmaTr()); } if (mProduceHeliumPids) { - trackProducts.producedHeliumPids(track.itsNSigmaHe(), track.tpcNSigmaHe(), track.tofNSigmaHe()); + float itsHe = 0.f; + if constexpr (requires(T1 t) { t.itsNSigmaHe(); }) { + itsHe = track.itsNSigmaHe(); + } + trackProducts.producedHeliumPids(itsHe, track.tpcNSigmaHe(), track.tofNSigmaHe()); + } + if (mProduceItsPurity || mProduceTpcPurity || mProduceTofPurity) { + // raw nSigma per species, same values already used for the FXxxPids blocks above -- + // unused slots (species without a requirePidXxx flag, or not covered by this track + // type) stay at the 0 placeholder, same contract as the missing-Tof case + std::array itsNSigma = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + std::array tpcNSigma = {track.tpcNSigmaEl(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe()}; + std::array tofNSigma = {track.tofNSigmaEl(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), track.tofNSigmaDe(), track.tofNSigmaTr(), track.tofNSigmaHe()}; + + if constexpr (requires(T1 t) { t.itsNSigmaEl(); }) { + itsNSigma = {track.itsNSigmaEl(), track.itsNSigmaPi(), track.itsNSigmaKa(), track.itsNSigmaPr(), track.itsNSigmaDe(), track.itsNSigmaTr(), track.itsNSigmaHe()}; + } + + std::array itsSlots = {0.f, 0.f, 0.f, 0.f}; + std::array tpcSlots = {0.f, 0.f, 0.f, 0.f}; + std::array tofSlots = {0.f, 0.f, 0.f, 0.f}; + bool hasTof = track.hasTOF(); + for (int i = 0; i < PuritySpeciesLast; i++) { + if (mPuritySlot[i] < 0) { + continue; + } + itsSlots[mPuritySlot[i]] = itsNSigma[i]; + tpcSlots[mPuritySlot[i]] = tpcNSigma[i]; + tofSlots[mPuritySlot[i]] = hasTof ? tofNSigma[i] : 0.f; // PurityHasTof is the gate; 0 here is just a placeholder + } + + if (mProduceItsPurity) { + trackProducts.producedItsPurity(utils::packPurityNSigma4(itsSlots[0], itsSlots[1], itsSlots[2], itsSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax)); + } + if (mProduceTpcPurity) { + trackProducts.producedTpcPurity(utils::packPurityNSigma4(tpcSlots[0], tpcSlots[1], tpcSlots[2], tpcSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax)); + } + if (mProduceTofPurity) { + trackProducts.producedTofPurity(utils::packPurityNSigma4(tofSlots[0], tofSlots[1], tofSlots[2], tofSlots[3], o2::aod::femtotracks::NSigmaPurityMagMin, o2::aod::femtotracks::NSigmaPurityMagMax), hasTof); + } } + return true; } template @@ -663,47 +930,58 @@ class TrackBuilder collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); // get track from the track table so we can dereference mc particle properly auto track = tracks.iteratorAt(trackWithItsPid.index()); - this->template fillMcTrack(col, collisionProducts, mcCols, track, trackWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); + this->template fillMcTrack(track, trackWithItsPid, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); } } - template - void fillMcTrack(T1 const& col, T2& collisionProducts, T3 const& mcCols, T4 const& track, T5 const& trackWithItsPid, T6& trackProducts, T7 const& mcParticles, T8& mcBuilder, T9& mcProducts) + template + bool fillMcTrack(T1 const& track, T2 const& trackWithItsPid, T3& trackProducts, T4 const& mcCols, T5& collisionBuilder, T6 const& mcParticles, T7& mcBuilder, T8& mcProducts) { - if (!mProduceTracks) { - return; + if (!mProduceTracks && !mProduceLiteTracks) { + return false; } - this->template fillTrack(trackWithItsPid, trackProducts, collisionProducts); - mcBuilder.template fillMcTrackWithLabel(col, mcCols, track, mcParticles, mcProducts); + this->template fillTrack(trackWithItsPid, trackProducts, collisionBuilder); + mcBuilder.template fillMcTrackWithLabel(track, mcParticles, mcCols, mcProducts); + return true; } template - int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3& collisionProducts) + int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3& collisionBuilder) { auto result = utils::getIndex(daughter.globalIndex(), indexMap); if (result) { return result.value(); - } else { - this->fillTrack(daughter, trackProducts, collisionProducts); - int64_t idx = trackProducts.producedTracks.lastIndex(); - return idx; } + if (!this->template fillTrack(daughter, trackProducts, collisionBuilder)) { + LOG(fatal) << "Trying to register a daughter track, but FTracks or FLiteTrack table is disabled. " + << "Enable TrackTables.produceTracks/produceLiteTracks when V0/Cascade/Kink tables that need daughter indices are enabled."; + } + // fillTrack already inserted the correct index (FTracks or FLiteTracks) into indexMap + return indexMap.at(daughter.globalIndex()); } - template - int64_t getDaughterIndex(const T1& col, T2& collisionProducts, T3 const& mcCols, const T4& daughter, T5& daughterWithItsPid, T6& trackProducts, T7 const& mcParticles, T8& mcBuilder, T9& mcProducts) + template + int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3 const& mcCols, T4& collisionBuilder, T5 const& mcParticles, T6& mcBuilder, T7& mcProducts) { auto result = utils::getIndex(daughter.globalIndex(), indexMap); if (result) { - // daugher already in track table + // daughter already in track table return result.value(); - } else { - this->fillMcTrack(col, collisionProducts, mcCols, daughter, daughterWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); - // daughter is last track which was added added - return trackProducts.producedTracks.lastIndex(); } + if (!this->template fillMcTrack(daughter, daughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts)) { + LOG(fatal) << "Trying to register a daughter track, but FTracks or FLiteTrack table is disabled. " + << "Enable TrackTables.produceTracks/produceLiteTracks when V0/Cascade/Kink tables that need daughter indices are enabled."; + } + // fillTrack already inserted the correct index (FTracks or FLiteTracks) into indexMap + return indexMap.at(daughter.globalIndex()); } + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mTrackSelection.isPassThrough(); } + [[nodiscard]] bool producingTracks() const { return mProduceTracks; } + [[nodiscard]] bool producingLiteTracks() const { return mProduceLiteTracks; } + [[nodiscard]] bool producingTrackExtras() const { return mProduceTrackExtras; } + template void reset(T const& tracks) { @@ -712,9 +990,10 @@ class TrackBuilder }; private: - TrackSelection mTrackSelection; + TrackSelection mTrackSelection; bool mFillAnyTable = false; bool mProduceTracks = false; + bool mProduceLiteTracks = false; bool mProduceTrackMasks = false; bool mProduceTrackMass = false; bool mProduceTrackDcas = false; @@ -726,51 +1005,74 @@ class TrackBuilder bool mProduceDeuteronPids = false; bool mProduceTritonPids = false; bool mProduceHeliumPids = false; - - std::unordered_map indexMap; // for mapping tracks to daughers of lambdas, cascades and resonances ... + bool mProduceItsPurity = false; + bool mProduceTpcPurity = false; + bool mProduceTofPurity = false; + + // canonical species order for purity slot assignment -- fixed, not configurable (see + // init() for how requirePidXxx flags map onto slots 0-3) + static constexpr int PuritySpeciesLast = 7; + static constexpr std::array PuritySpeciesName = {"Electron", "Pion", "Kaon", "Proton", "Deuteron", "Triton", "Helium"}; + // mPuritySlot[i] = assigned slot (0-3) for species i (PuritySpeciesName order), or -1 if not assigned + std::array mPuritySlot = {-1, -1, -1, -1, -1, -1, -1}; + + std::unordered_map indexMap; // for mapping tracks to daughters of lambdas, cascades and resonances ... }; struct TrackBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { o2::framework::Produces producedTracks; o2::framework::Produces producedTrackMasks; + o2::framework::Produces producedTrackMass; + // purity/fraction, copied through if the input track type has them joined (same + // compile-time-detected, opt-in-by-type pattern as producedTrackMass above) + o2::framework::Produces producedItsPurity; + o2::framework::Produces producedTpcPurity; + o2::framework::Produces producedTofPurity; + o2::framework::Produces producedTrackDcas; }; struct ConfTrackTablesDerivedToDerived : o2::framework::ConfigurableGroup { std::string prefix = std::string("TrackTables"); - o2::framework::Configurable limitTrack1{"limitTrack1", 1, "At least this many tracks of type 1 need to be in the collision. Ignored if set to 0."}; - o2::framework::Configurable limitTrack2{"limitTrack2", 0, "At least this many tracks of type 2 need to be in the collision. Ignored if set to 0."}; + o2::framework::Configurable limitTrack1{"limitTrack1", 1, "Require at least this many tracks of type 1 in the collision. Set to 0 to skip this track species entirely (not written to output)."}; + o2::framework::Configurable limitTrack2{"limitTrack2", 1, "Require at least this many tracks of type 2 in the collision. Set to 0 to skip this track species entirely (not written to output)."}; }; class TrackBuilderDerivedToDerived { public: - TrackBuilderDerivedToDerived() = default; - ~TrackBuilderDerivedToDerived() = default; - template void init(T& config) { mLimitTrack1 = config.limitTrack1.value; mLimitTrack2 = config.limitTrack2.value; + if (mLimitTrack1 < 0 || mLimitTrack2 < 0) { + LOG(fatal) << "Track limits must be non-negative (got " << mLimitTrack1 << " and " << mLimitTrack2 << "). Breaking..."; + } if (mLimitTrack1 == 0 && mLimitTrack2 == 0) { LOG(fatal) << "Both track limits are 0. Breaking..."; } } template - bool collisionHasTooFewTracks(T1& col, T2& /*trackTable*/, T3& partitionTrack1, T4& partitionTrack2, T5& cache) + bool collisionHasTooFewTracks(T1 const& col, T2 const& /*tracks*/, T3& partitionTrack1, T4& partitionTrack2, T5& cache) const { - auto trackSlice1 = partitionTrack1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partitionTrack2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - return trackSlice1.size() < mLimitTrack1 || trackSlice2.size() < mLimitTrack2; + bool tooFew1 = false; + if (mLimitTrack1 > 0) { + auto slice1 = partitionTrack1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + tooFew1 = slice1.size() < static_cast(mLimitTrack1); + } + bool tooFew2 = false; + if (mLimitTrack2 > 0) { + auto slice2 = partitionTrack2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + tooFew2 = slice2.size() < static_cast(mLimitTrack2); + } + return tooFew1 || tooFew2; } template void processTracks(T1& col, T2& /*trackTable*/, T3& partitionTrack1, T4& partitionTrack2, T5& cache, T6& newTrackTable, T7& newCollisionTable) { - indexMap.clear(); - if (mLimitTrack1 > 0) { auto trackSlice1 = partitionTrack1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); for (auto const& track : trackSlice1) { @@ -786,41 +1088,99 @@ class TrackBuilderDerivedToDerived } } + /// Fill a track into the output table, or return its index if it was already filled. + /// \return index of the track in the produced track table template - void fillTrack(T1 const& track, T2& trackProducts, T3& collisionProducts) + int64_t fillTrack(T1 const& track, T2& trackProducts, T3& collisionProducts) { - if (indexMap.find(track.globalIndex()) == indexMap.end()) { // protect against double filling - trackProducts.producedTracks(collisionProducts.producedCollision.lastIndex(), - track.signedPt(), - track.eta(), - track.phi()); - trackProducts.producedTrackMasks(track.mask()); - indexMap.emplace(track.globalIndex(), trackProducts.producedTracks.lastIndex()); + auto index = utils::getIndex(track.globalIndex(), indexMap); + if (index) { // protect against double filling + return index.value(); + } + + trackProducts.producedTracks(collisionProducts.producedCollision.lastIndex(), + track.signedPt(), + track.eta(), + track.phi()); + trackProducts.producedTrackMasks(track.mask()); + if constexpr (utils::HasMass) { + trackProducts.producedTrackMass(track.mass()); + } + if constexpr (requires(T1 t) { t.itsPackedNSigma(); }) { + trackProducts.producedItsPurity(track.itsPackedNSigma()); + trackProducts.producedTpcPurity(track.tpcPackedNSigma()); + trackProducts.producedTofPurity(track.tofPackedNSigma(), track.purityHasTof()); + } + if constexpr (requires(T1 t) { t.dcaXY(); }) { + trackProducts.producedTrackDcas(track.dcaXY(), track.dcaZ()); + } + + const int64_t idx = trackProducts.producedTracks.lastIndex(); + indexMap.emplace(track.globalIndex(), idx); + return idx; + } + + template + void processTracksWithMc(T1& col, T2& /*trackTable*/, T3& partitionTrack1, T4& partitionTrack2, T5& cache, + T6& newTrackTable, T7& newCollisionTable, + T8& mcBuilder, T9 const& mcCols, T10 const& mcParticles, T11 const& mcMothers, T12 const& mcPartonicMothers, auto& mcProducts) + { + if (mLimitTrack1 > 0) { + auto trackSlice1 = partitionTrack1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& track : trackSlice1) { + this->fillTrackWithMcLabel(track, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + if (mLimitTrack2 > 0) { + auto trackSlice2 = partitionTrack2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& track : trackSlice2) { + this->fillTrackWithMcLabel(track, newTrackTable, newCollisionTable, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + } + } + + /// Same as fillTrack, but writes the matching FTrackLabels row. The indexMap + /// lookup happens first, so a track selected by both partitions produces + /// exactly one track row and exactly one label row. + template + int64_t fillTrackWithMcLabel(T1 const& track, T2& trackProducts, T3& collisionProducts, + T4& mcBuilder, T5 const& mcCols, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& mcProducts) + { + auto index = utils::getIndex(track.globalIndex(), indexMap); + if (index) { + return index.value(); } + const int64_t idx = this->fillTrack(track, trackProducts, collisionProducts); + mcBuilder.fillTrackWithLabel(track, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + return idx; } template int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3& collisionProducts) { - auto result = utils::getIndex(daughter.globalIndex(), indexMap); - if (result) { - return result.value(); - } else { - this->fillTrack(daughter, trackProducts, collisionProducts); - int64_t idx = trackProducts.producedTracks.lastIndex(); - return idx; - } + return this->fillTrack(daughter, trackProducts, collisionProducts); + } + + template + int64_t getDaughterIndex(const T1& daughter, T2& trackProducts, T3& collisionProducts, + T4& mcBuilder, T5 const& mcCols, T6 const& mcParticles, T7 const& mcMothers, T8 const& mcPartonicMothers, T9& mcProducts) + { + return this->fillTrackWithMcLabel(daughter, trackProducts, collisionProducts, mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcProducts); + } + + template + void reset(T const& tracks) + { + indexMap.clear(); + indexMap.reserve(tracks.size()); } private: int mLimitTrack1 = 0; int mLimitTrack2 = 0; - std::unordered_map indexMap; // for mapping tracks to daughers of lambdas, cascades and resonances ... + std::unordered_map indexMap; // for mapping tracks to daughters of lambdas, cascades and resonances ... }; - -} // namespace trackbuilder -// -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::trackbuilder #endif // PWGCF_FEMTO_CORE_TRACKBUILDER_H_ diff --git a/PWGCF/Femto/Core/trackHistManager.h b/PWGCF/Femto/Core/trackHistManager.h index 9ce323841c1..66e4fe3fab3 100644 --- a/PWGCF/Femto/Core/trackHistManager.h +++ b/PWGCF/Femto/Core/trackHistManager.h @@ -29,17 +29,15 @@ #include #include +#include #include #include #include #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::trackhistmanager { -namespace trackhistmanager -{ - // enum for track histograms enum TrackHist { // kinemtics @@ -58,6 +56,9 @@ enum TrackHist { kTpcClusterOverCrossedRows, kTpcClusterShared, kTpcClusterFractionShared, + kItsChi2, + kTpcChi2, + kTrackType, // 2d qa kPtVsEta, kPtVsPhi, @@ -68,11 +69,14 @@ enum TrackHist { kPtVsTpcClusterOverCrossedRows, kPtVsTpcClusterShared, kPtVsTpcClusterFractionShared, + kPtVsItsChi2, + kPtVsTpcChi2, kTpcClusterVsTpcCrossedRows, kTpcClusterVsTpcClusterShared, kPtVsDcaxy, kPtVsDcaz, kPtVsDca, + kPVsPTpc, // its pid kItsSignal, kItsElectron, @@ -117,6 +121,29 @@ enum TrackHist { kTpctofDeuteron, kTpctofTriton, kTpctofHelium, + // momentum vs tpc vs its pid (uncombined, orthogonal nSigma axes -- lets one cut on both at once) + kTpcVsItsElectron, + kTpcVsItsPion, + kTpcVsItsKaon, + kTpcVsItsProton, + kTpcVsItsDeuteron, + kTpcVsItsTriton, + kTpcVsItsHelium, + // momentum vs tpc vs tof pid (uncombined, orthogonal nSigma axes -- lets one cut on both at once) + kTpcVsTofElectron, + kTpcVsTofPion, + kTpcVsTofKaon, + kTpcVsTofProton, + kTpcVsTofDeuteron, + kTpcVsTofTriton, + kTpcVsTofHelium, + // purity (species picked at runtime via puritySlotMask, see hPuritySlotAssignment) + kItsPurity, + kTpcPurity, + kTofPurity, + kTpcitsPurity, + kTpctofPurity, + kFractionDca, // mc kOrigin, kPdg, @@ -141,7 +168,7 @@ enum TrackHist { constexpr std::size_t MaxSecondary = 3; -template +template struct ConfTrackBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; @@ -150,13 +177,26 @@ struct ConfTrackBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; o2::framework::ConfigurableAxis mass{"mass", {{200, 0.f, 2.f}}, "Mass (if enabled, plot TOF mass, otherwise PDG mass)"}; o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of selected tracks"}; + o2::framework::ConfigurableAxis purityNSigma{"purityNSigma", {{3000, -15, 15}}, "nSigma decoded from the packed purity tables (species picked via puritySlotMask); signed, for the raw Its/Tpc/Tof values"}; + o2::framework::ConfigurableAxis purityNSigmaComb{"purityNSigmaComb", {{2000, 0, 20}}, "Combined nSigma (hypot of two raw values); always non-negative, for Tpc+Its/Tpc+Tof"}; + o2::framework::ConfigurableAxis fractionDcaXy{"fractionDcaXy", {{300, -0.3, 0.3}}, "DCA_xy (fraction estimation, needs FTrackDcas joined)"}; + o2::framework::ConfigurableAxis fractionDcaZ{"fractionDcaZ", {{300, -0.3, 0.3}}, "DCA_z (fraction estimation, needs FTrackDcas joined)"}; }; constexpr const char PrefixTrackBinning1[] = "TrackBinning1"; constexpr const char PrefixTrackBinning2[] = "TrackBinning2"; constexpr const char PrefixTrackBinning3[] = "TrackBinning3"; -constexpr const char PrefixV0PosDauBinning[] = "V0PosDauBinning"; -constexpr const char PrefixV0NegDauBinning[] = "V0NegDauBinning"; +constexpr const char PrefixV01PosDauBinning[] = "V01PosDauBinning"; +constexpr const char PrefixV01NegDauBinning[] = "V01NegDauBinning"; +constexpr const char PrefixV02PosDauBinning[] = "V02PosDauBinning"; +constexpr const char PrefixV02NegDauBinning[] = "V02NegDauBinning"; +constexpr const char PrefixD01PosDauBinning[] = "D01PosDauBinning"; +constexpr const char PrefixD01NegDauBinning[] = "D01NegDauBinning"; +constexpr const char PrefixD02PosDauBinning[] = "D02PosDauBinning"; +constexpr const char PrefixD02NegDauBinning[] = "D02NegDauBinning"; +constexpr const char PrefixLc1ProtonDauBinning[] = "Lc1ProtonDauBinning"; +constexpr const char PrefixLc1KaonDauBinning[] = "Lc1KaonDauBinning"; +constexpr const char PrefixLc1PionDauBinning[] = "Lc1PionDauBinning"; constexpr const char PrefixCascadePosDauBinning[] = "CascadePosDauBinning"; constexpr const char PrefixCascadeNegDauBinning[] = "CascadeNegDauBinning"; constexpr const char PrefixCascadeBachelorBinning[] = "CascadeBachelorBinning"; @@ -171,32 +211,46 @@ constexpr const char PrefixKaonMinusBinning[] = "KaonMinusBinning"; using ConfTrackBinning1 = ConfTrackBinning; using ConfTrackBinning2 = ConfTrackBinning; using ConfTrackBinning3 = ConfTrackBinning; -using ConfV0PosDauBinning = ConfTrackBinning; -using ConfV0NegDauBinning = ConfTrackBinning; +using ConfV01PosDauBinning = ConfTrackBinning; +using ConfV01NegDauBinning = ConfTrackBinning; +using ConfV02PosDauBinning = ConfTrackBinning; +using ConfV02NegDauBinning = ConfTrackBinning; +using ConfD01PosDauBinning = ConfTrackBinning; +using ConfD01NegDauBinning = ConfTrackBinning; +using ConfD02PosDauBinning = ConfTrackBinning; +using ConfD02NegDauBinning = ConfTrackBinning; +using ConfLc1ProtonDauBinning = ConfTrackBinning; +using ConfLc1KaonDauBinning = ConfTrackBinning; +using ConfLc1PionDauBinning = ConfTrackBinning; using ConfCascadePosDauBinning = ConfTrackBinning; using ConfCascadeNegDauBinning = ConfTrackBinning; using ConfCascadeBachelorBinning = ConfTrackBinning; +// for dN/deta tracks +constexpr const char PrefixDndetaTrackGlobalBinning[] = "DndetaTrackGlobalBinning"; +constexpr const char PrefixDndetaTrackItsOnlyBinning[] = "DndetaTrackItsOnlyBinning"; +using ConfDndetaTrackGlobalBinning = ConfTrackBinning; +using ConfDndetaTrackItsOnlyBinning = ConfTrackBinning; using ConfKinkChaDauBinning = ConfTrackBinning; // for resonance daughters using ConfResonancePosDauBinning = ConfTrackBinning; using ConfResonanceNegDauBinning = ConfTrackBinning; using ConfPionPlusBinning = ConfTrackBinning; -using ConfPionMinusBinning = ConfTrackBinning; +using ConfPionMinusBinning = ConfTrackBinning; using ConfKaonPlusBinning = ConfTrackBinning; -using ConfKaonMinusBinning = ConfTrackBinning; +using ConfKaonMinusBinning = ConfTrackBinning; -template +template struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::Configurable momentumType{"momentumType", static_cast(modes::MomentumType::kPAtPv), "Momentum on x-axis (0->Pt, 1->P at PV, 2->P at TPC inner wall)"}; o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; - o2::framework::Configurable plotElectronPid{"plotElectronPid", true, "Generate plots for Electron PID"}; - o2::framework::Configurable plotPionPid{"plotPionPid", true, "Generate plots for Pion PID"}; - o2::framework::Configurable plotKaonPid{"plotKaonPid", true, "Generate plots for Kaon PID"}; + o2::framework::Configurable plotElectronPid{"plotElectronPid", false, "Generate plots for Electron PID"}; + o2::framework::Configurable plotPionPid{"plotPionPid", false, "Generate plots for Pion PID"}; + o2::framework::Configurable plotKaonPid{"plotKaonPid", false, "Generate plots for Kaon PID"}; o2::framework::Configurable plotProtonPid{"plotProtonPid", true, "Generate plots for Proton PID"}; - o2::framework::Configurable plotDeuteronPid{"plotDeuteronPid", true, "Generate plots for Deuteron PID"}; - o2::framework::Configurable plotTritonPid{"plotTritonPid", true, "Generate plots for Triton PID"}; - o2::framework::Configurable plotHeliumPid{"plotHeliumPid", true, "Generate plots for Helium PID"}; + o2::framework::Configurable plotDeuteronPid{"plotDeuteronPid", false, "Generate plots for Deuteron PID"}; + o2::framework::Configurable plotTritonPid{"plotTritonPid", false, "Generate plots for Triton PID"}; + o2::framework::Configurable plotHeliumPid{"plotHeliumPid", false, "Generate plots for Helium PID"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs DCAxy vs DCAz for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3122}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; o2::framework::ConfigurableAxis itsCluster{"itsCluster", {{8, -0.5, 7.5}}, "ITS cluster"}; @@ -206,10 +260,12 @@ struct ConfTrackQaBinning : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis tpcClusterOverCrossedRows{"tpcClusterOverCrossedRows", {{75, 0, 1.5}}, "TPC cluster over TPC crossed rows"}; o2::framework::ConfigurableAxis tpcClusterShared{"tpcClusterShared", {{161, -0.5, 160.5}}, "TPC cluster shared"}; o2::framework::ConfigurableAxis tpcClusterFractionShared{"tpcClusterFractionShared", {{60, 0, 1.2}}, "TPC cluster fraction shared"}; + o2::framework::ConfigurableAxis itsChi2{"itsChi2", {{100, 0, 50}}, "ITS chi2 / cluster"}; + o2::framework::ConfigurableAxis tpcChi2{"tpcChi2", {{100, 0, 10}}, "TPC chi2 / cluster"}; o2::framework::ConfigurableAxis dcaXy{"dcaXy", {{300, -0.3, 0.3}}, "DCA_xy"}; o2::framework::ConfigurableAxis dcaZ{"dcaZ", {{300, -0.3, 0.3}}, "DCA_Z"}; o2::framework::ConfigurableAxis dca{"dca", {{300, 0, 0.3}}, "DCA"}; - o2::framework::ConfigurableAxis p{"p", {{300, 0, 6}}, "Momentum axis"}; + o2::framework::ConfigurableAxis p{"p", {{300, 0, 6}}, "Momentum axis. Used for all PID histograms and momentum correlation histogram"}; o2::framework::ConfigurableAxis itsSignal{"itsSignal", {{150, 0, 15}}, "ITS Signal"}; o2::framework::ConfigurableAxis itsElectron{"itsElectron", {{300, -3, 3}}, "ITS PID for electron"}; o2::framework::ConfigurableAxis itsPion{"itsPion", {{300, -3, 3}}, "ITS PID for pion"}; @@ -255,8 +311,15 @@ constexpr const char PrefixTrackQaBinning1[] = "TrackQaBinning1"; constexpr const char PrefixTrackQaBinning2[] = "TrackQaBinning2"; constexpr const char PrefixResonancePosDauQaBinning[] = "ResonancePosDauQaBinning"; constexpr const char PrefixResonanceNegDauQaBinning[] = "ResonanceNegDauQaBinning"; -constexpr const char PrefixV0PosDauQaBinning[] = "V0PosDauQaBinning"; -constexpr const char PrefixV0NegDauQaBinning[] = "V0NegDauQaBinning"; +constexpr const char PrefixV01PosDauQaBinning[] = "V01PosDauQaBinning"; +constexpr const char PrefixV01NegDauQaBinning[] = "V01NegDauQaBinning"; +constexpr const char PrefixV02PosDauQaBinning[] = "V02PosDauQaBinning"; +constexpr const char PrefixV02NegDauQaBinning[] = "V02NegDauQaBinning"; +constexpr const char PrefixD01PosDauQaBinning[] = "D01PosDauQaBinning"; +constexpr const char PrefixD01NegDauQaBinning[] = "D01NegDauQaBinning"; +constexpr const char PrefixLc1ProtonDauQaBinning[] = "Lc1ProtonDauQaBinning"; +constexpr const char PrefixLc1KaonDauQaBinning[] = "Lc1KaonDauQaBinning"; +constexpr const char PrefixLc1PionDauQaBinning[] = "Lc1PionDauQaBinning"; constexpr const char PrefixCascadePosDauQaBinning[] = "CascadePosDauQaBinning"; constexpr const char PrefixCascadeNegDauQaBinning[] = "CascadeNegDauQaBinning"; constexpr const char PrefixCascadeBachelorQaBinning[] = "CascadeBachelorQaBinning"; @@ -264,10 +327,22 @@ constexpr const char PrefixKinkChaDauQaBinning[] = "KinkChaDauQaBinning"; using ConfTrackQaBinning1 = ConfTrackQaBinning; using ConfTrackQaBinning2 = ConfTrackQaBinning; +// for dN/deta tracks +constexpr const char PrefixDndetaTrackGlobalQaBinning[] = "DndetaTrackGlobalQaBinning"; +constexpr const char PrefixDndetaTrackItsOnlyQaBinning[] = "DndetaTrackItsOnlyQaBinning"; +using ConfDndetaTrackGlobalQaBinning = ConfTrackQaBinning; +using ConfDndetaTrackItsOnlyQaBinning = ConfTrackQaBinning; using ConfResonancePosDauQaBinning = ConfTrackQaBinning; using ConfResonanceNegDauQaBinning = ConfTrackQaBinning; -using ConfV0PosDauQaBinning = ConfTrackQaBinning; -using ConfV0NegDauQaBinning = ConfTrackQaBinning; +using ConfV01PosDauQaBinning = ConfTrackQaBinning; +using ConfV01NegDauQaBinning = ConfTrackQaBinning; +using ConfV02PosDauQaBinning = ConfTrackQaBinning; +using ConfV02NegDauQaBinning = ConfTrackQaBinning; +using ConfD01PosDauQaBinning = ConfTrackQaBinning; +using ConfD01NegDauQaBinning = ConfTrackQaBinning; +using ConfLc1ProtonDauQaBinning = ConfTrackQaBinning; +using ConfLc1KaonDauQaBinning = ConfTrackQaBinning; +using ConfLc1PionDauQaBinning = ConfTrackQaBinning; using ConfCascadePosDauQaBinning = ConfTrackQaBinning; using ConfCascadeNegDauQaBinning = ConfTrackQaBinning; using ConfCascadeBachelorQaBinning = ConfTrackQaBinning; @@ -289,9 +364,12 @@ constexpr std::array, kTrackHistLast> {kItsClusterIb, o2::framework::HistType::kTH1F, "hItsClusterIb", "ITS cluster in inner barrel; ITS IB cluster; Entries"}, {kTpcCrossedRows, o2::framework::HistType::kTH1F, "hTpcCrossedRows", "TPC crossed rows; TPC crossed rows; Entries"}, {kTpcCluster, o2::framework::HistType::kTH1F, "hTpcCluster", "TPC cluster found; TPC cluster found; Entries"}, - {kTpcClusterOverCrossedRows, o2::framework::HistType::kTH1F, "hTpcClusterOverCrossedRows", "TPC cluster found over TPC crossed rows; TPC cluster found / Tpc crossed rows; Entries"}, - {kTpcClusterShared, o2::framework::HistType::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared ; Entries"}, - {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster found / TPC cluster shared ; Entries"}, + {kTpcClusterOverCrossedRows, o2::framework::HistType::kTH1F, "hTpcClusterOverCrossedRows", "TPC cluster found over TPC crossed rows; TPC cluster found / TPC crossed rows; Entries"}, + {kTpcClusterShared, o2::framework::HistType::kTH1F, "hTpcClusterShared", "TPC cluster shared; TPC cluster shared; Entries"}, + {kTpcClusterFractionShared, o2::framework::HistType::kTH1F, "hTpcClusterFractionShared", "TPC cluster fraction shared; TPC cluster shared / TPC cluster found; Entries"}, + {kItsChi2, o2::framework::HistType::kTH1F, "hItsChi2", "ITS #chi^{2} / cluster; #chi^{2}_{ITS} / N_{cls}; Entries"}, + {kTpcChi2, o2::framework::HistType::kTH1F, "hTpcChi2", "TPC #chi^{2} / cluster; #chi^{2}_{TPC} / N_{cls}; Entries"}, + {kTrackType, o2::framework::HistType::kTH1F, "hTrackType", "Type the track row was stored as; Track type; Entries"}, {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, @@ -301,13 +379,16 @@ constexpr std::array, kTrackHistLast> {kPtVsTpcClusterOverCrossedRows, o2::framework::HistType::kTH2F, "hPtVsTpcClusterOverCrossedRows", "p_{T} vs TPC cluster found over crossed rows; p_{T} (GeV/#it{c}) ; TPC cluster found / TPC crossed rows"}, {kPtVsTpcClusterShared, o2::framework::HistType::kTH2F, "hPtVsTpcClusterShared", "p_{T} vs TPC cluster shared; p_{T} (GeV/#it{c}) ; TPC cluster shared"}, {kPtVsTpcClusterFractionShared, o2::framework::HistType::kTH2F, "hPtVsTpcClusterSharedFraction", "p_{T} vs TPC cluster shared over TPC cluster found; p_{T} (GeV/#it{c}) ; TPC cluster shared / TPC cluster found"}, + {kPtVsItsChi2, o2::framework::HistType::kTH2F, "hPtVsItsChi2", "p_{T} vs ITS #chi^{2} / cluster; p_{T} (GeV/#it{c}) ; #chi^{2}_{ITS} / N_{cls}"}, + {kPtVsTpcChi2, o2::framework::HistType::kTH2F, "hPtVsTpcChi2", "p_{T} vs TPC #chi^{2} / cluster; p_{T} (GeV/#it{c}) ; #chi^{2}_{TPC} / N_{cls}"}, {kTpcClusterVsTpcCrossedRows, o2::framework::HistType::kTH2F, "hTpcClusterVsTpcCrossedRows", "TPC cluster found vs TPC crossed rows; TPC cluster found; TPC crossed rows"}, {kTpcClusterVsTpcClusterShared, o2::framework::HistType::kTH2F, "hTpcClusterVsTpcClusterShared", "TPC cluster found vs TPC cluster shared; TPC cluster found; TPC cluster shared"}, {kPtVsDcaxy, o2::framework::HistType::kTH2F, "hPtVsDcaxy", "p_{T} vs DCA_{XY}; p_{T} (GeV/#it{c}); DCA_{XY} (cm)"}, {kPtVsDcaz, o2::framework::HistType::kTH2F, "hPtVsDcaz", "p_{T} vs DCA_{Z}; p_{T} (GeV/#it{c}); DCA_{Z} (cm)"}, {kPtVsDca, o2::framework::HistType::kTH2F, "hPtVsDca", "p_{T} vs DCA; p_{T} (GeV/#it{c}); DCA (cm)"}, + {kPVsPTpc, o2::framework::HistType::kTH2F, "hPVsPTpc", "Correlation p_{global} and p_{TPC}; p_{global} (GeV/#it{c}); p_{tpc} (GeV/#it{c})"}, {kItsSignal, o2::framework::HistType::kTH2F, "hItsSignal", "ITS Signal; p (GeV/#it{c}) ; x "}, - {kItsElectron, o2::framework::HistType::kTH2F, "hItsPidElectron", "ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el}"}, + {kItsElectron, o2::framework::HistType::kTH2F, "hItsPidElectron", "ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{ITS,el}"}, {kItsPion, o2::framework::HistType::kTH2F, "hItsPidPion", "ITS PID Pion; p (GeV/#it{c}) ; n#sigma_{ITS,pi}"}, {kItsKaon, o2::framework::HistType::kTH2F, "hItsPidKaon", "ITS PID Kaon; p (GeV/#it{c}) ; n#sigma_{ITS,ka}"}, {kItsProton, o2::framework::HistType::kTH2F, "hItsPidProton", "ITS PID Proton; p (GeV/#it{c}) ; n#sigma_{ITS,pr}"}, @@ -331,127 +412,179 @@ constexpr std::array, kTrackHistLast> {kTofDeuteron, o2::framework::HistType::kTH2F, "hTofPidDeuteron", "TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TOF,de}"}, {kTofTriton, o2::framework::HistType::kTH2F, "hTofPidTriton", "TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TOF,tr}"}, {kTofHelium, o2::framework::HistType::kTH2F, "hTofPidHelium", "TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TOF,he}"}, - {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "its PID Electron; p (GeV/#it{c}) ; n#sigma_{its,el}"}, + {kTpcitsElectron, o2::framework::HistType::kTH2F, "hTpcitsPidElectron", "TPC+ITS PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{its,el}^{2}}"}, {kTpcitsPion, o2::framework::HistType::kTH2F, "hTpcitsPidPion", "TPC+ITS PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{its,pi}^{2}}"}, {kTpcitsKaon, o2::framework::HistType::kTH2F, "hTpcitsPidKaon", "TPC+ITS PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{its,ka}^{2}}"}, {kTpcitsProton, o2::framework::HistType::kTH2F, "hTpcitsPidProton", "TPC+ITS PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{its,pr}^{2}}"}, {kTpcitsDeuteron, o2::framework::HistType::kTH2F, "hTpcitsPidDeuteron", "TPC+ITS PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{its,de}^{2}}"}, {kTpcitsTriton, o2::framework::HistType::kTH2F, "hTpcitsPidTriton", "TPC+ITS PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{its,tr}^{2}}"}, {kTpcitsHelium, o2::framework::HistType::kTH2F, "hTpcitsPidHelium", "TPC+ITS PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{its,he}^{2}}"}, - {kTpctofElectron, o2::framework::HistType::kTH2F, "hTpctofPidElectron", "TOF PID Electron; p (GeV/#it{c}) ; n#sigma_{TOF,el}"}, + {kTpctofElectron, o2::framework::HistType::kTH2F, "hTpctofPidElectron", "TPC+TOF PID Electron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,el}^{2}+n#sigma_{TOF,el}^{2}}"}, {kTpctofPion, o2::framework::HistType::kTH2F, "hTpctofPidPion", "TPC+TOF PID Pion; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pi}^{2}+n#sigma_{TOF,pi}^{2}}"}, {kTpctofKaon, o2::framework::HistType::kTH2F, "hTpctofPidKaon", "TPC+TOF PID Kaon; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,ka}^{2}+n#sigma_{TOF,ka}^{2}}"}, {kTpctofProton, o2::framework::HistType::kTH2F, "hTpctofPidProton", "TPC+TOF PID Proton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,pr}^{2}+n#sigma_{TOF,pr}^{2}}"}, {kTpctofDeuteron, o2::framework::HistType::kTH2F, "hTpctofPidDeuteron", "TPC+TOF PID Deuteron; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,de}^{2}+n#sigma_{TOF,de}^{2}}"}, {kTpctofTriton, o2::framework::HistType::kTH2F, "hTpctofPidTriton", "TPC+TOF PID Triton; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,tr}^{2}+n#sigma_{TOF,tr}^{2}}"}, {kTpctofHelium, o2::framework::HistType::kTH2F, "hTpctofPidHelium", "TPC+TOF PID Helium; p (GeV/#it{c}) ; #sqrt{n#sigma_{TPC,he}^{2}+n#sigma_{TOF,he}^{2}}"}, + {kTpcVsItsElectron, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidElectron", "TPC vs ITS PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el} ; n#sigma_{ITS,el}"}, + {kTpcVsItsPion, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidPion", "TPC vs ITS PID Pion; p (GeV/#it{c}) ; n#sigma_{TPC,pi} ; n#sigma_{ITS,pi}"}, + {kTpcVsItsKaon, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidKaon", "TPC vs ITS PID Kaon; p (GeV/#it{c}) ; n#sigma_{TPC,ka} ; n#sigma_{ITS,ka}"}, + {kTpcVsItsProton, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidProton", "TPC vs ITS PID Proton; p (GeV/#it{c}) ; n#sigma_{TPC,pr} ; n#sigma_{ITS,pr}"}, + {kTpcVsItsDeuteron, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidDeuteron", "TPC vs ITS PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TPC,de} ; n#sigma_{ITS,de}"}, + {kTpcVsItsTriton, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidTriton", "TPC vs ITS PID Triton; p (GeV/#it{c}) ; n#sigma_{TPC,tr} ; n#sigma_{ITS,tr}"}, + {kTpcVsItsHelium, o2::framework::HistType::kTHnSparseF, "hTpcVsItsPidHelium", "TPC vs ITS PID Helium; p (GeV/#it{c}) ; n#sigma_{TPC,he} ; n#sigma_{ITS,he}"}, + {kTpcVsTofElectron, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidElectron", "TPC vs TOF PID Electron; p (GeV/#it{c}) ; n#sigma_{TPC,el} ; n#sigma_{TOF,el}"}, + {kTpcVsTofPion, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidPion", "TPC vs TOF PID Pion; p (GeV/#it{c}) ; n#sigma_{TPC,pi} ; n#sigma_{TOF,pi}"}, + {kTpcVsTofKaon, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidKaon", "TPC vs TOF PID Kaon; p (GeV/#it{c}) ; n#sigma_{TPC,ka} ; n#sigma_{TOF,ka}"}, + {kTpcVsTofProton, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidProton", "TPC vs TOF PID Proton; p (GeV/#it{c}) ; n#sigma_{TPC,pr} ; n#sigma_{TOF,pr}"}, + {kTpcVsTofDeuteron, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidDeuteron", "TPC vs TOF PID Deuteron; p (GeV/#it{c}) ; n#sigma_{TPC,de} ; n#sigma_{TOF,de}"}, + {kTpcVsTofTriton, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidTriton", "TPC vs TOF PID Triton; p (GeV/#it{c}) ; n#sigma_{TPC,tr} ; n#sigma_{TOF,tr}"}, + {kTpcVsTofHelium, o2::framework::HistType::kTHnSparseF, "hTpcVsTofPidHelium", "TPC vs TOF PID Helium; p (GeV/#it{c}) ; n#sigma_{TPC,he} ; n#sigma_{TOF,he}"}, + {kItsPurity, o2::framework::HistType::kTH2F, "hItsPurity", "ITS purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{ITS}"}, + {kTpcPurity, o2::framework::HistType::kTH2F, "hTpcPurity", "TPC purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TPC}"}, + {kTofPurity, o2::framework::HistType::kTH2F, "hTofPurity", "TOF purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; n#sigma_{TOF}"}, + {kTpcitsPurity, o2::framework::HistType::kTH2F, "hTpcitsPurity", "TPC+ITS combined purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; #sqrt{n#sigma_{TPC}^{2}+n#sigma_{ITS}^{2}}"}, + {kTpctofPurity, o2::framework::HistType::kTH2F, "hTpctofPurity", "TPC+TOF combined purity nSigma (species picked via puritySlotMask); p_{T} (GeV/#it{c}) ; #sqrt{n#sigma_{TPC}^{2}+n#sigma_{TOF}^{2}}"}, + {kFractionDca, o2::framework::HistType::kTHnSparseF, "hFractionDca", "DCA for fraction extraction; p_{T} (GeV/#it{c}) ; DCA_{xy} (cm) ; DCA_{z} (cm)"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of selected tracks; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of selected tracks; PDG Code; Entries"}, {kPdgPartonicMother, o2::framework::HistType::kTH1F, "hPdgPartonicMother", "PDG Codes of partonic mother selected tracks; PDG Code; Entries"}, - {kTruePtVsPt, o2::framework::HistType::kTH2F, "hTruePtVsPt", "True transverse momentum vs transverse momentum; p_{T,True} (GeV/#it{c}); p_{T,True} (GeV/#it{c})"}, + {kTruePtVsPt, o2::framework::HistType::kTH2F, "hTruePtVsPt", "True transverse momentum vs transverse momentum; p_{T,True} (GeV/#it{c}); p_{T} (GeV/#it{c})"}, {kTrueEtaVsEta, o2::framework::HistType::kTH2F, "hTrueEtaVsEta", "True pseudorapdity vs pseudorapdity; #eta_{True}; #eta"}, {kTruePhiVsPhi, o2::framework::HistType::kTH2F, "hTruePhiVsPhi", "True azimuthal angle vs azimuthal angle; #varphi_{True}; #varphi"}, - {kNoMcParticle, o2::framework::HistType::kTHnSparseF, "hNoMcParticle", "Wrongly reconstructed particles; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, + {kNoMcParticle, o2::framework::HistType::kTHnSparseF, "hNoMcParticle", "Wrongly reconstructed particles; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, {kPrimary, o2::framework::HistType::kTHnSparseF, "hPrimary", "Primary particles; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, - {kFromWrongCollision, o2::framework::HistType::kTHnSparseF, "hFromWrongCollision", "Particles associated to wrong collision; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, - {kFromMaterial, o2::framework::HistType::kTHnSparseF, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, - {kMissidentified, o2::framework::HistType::kTHnSparseF, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, - {kSecondary1, o2::framework::HistType::kTHnSparseF, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, - {kSecondary2, o2::framework::HistType::kTHnSparseF, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, - {kSecondary3, o2::framework::HistType::kTHnSparseF, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, - {kSecondaryOther, o2::framework::HistType::kTHnSparseF, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm)"}, + {kFromWrongCollision, o2::framework::HistType::kTHnSparseF, "hFromWrongCollision", "Particles associated to wrong collision; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kFromMaterial, o2::framework::HistType::kTHnSparseF, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kMissidentified, o2::framework::HistType::kTHnSparseF, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondary1, o2::framework::HistType::kTHnSparseF, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondary2, o2::framework::HistType::kTHnSparseF, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondary3, o2::framework::HistType::kTHnSparseF, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, + {kSecondaryOther, o2::framework::HistType::kTHnSparseF, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); DCA_{xy} (cm); DCA_{z} (cm);"}, }}; -#define TRACK_HIST_ANALYSIS_MAP(conf) \ - {kPt, {conf.pt}}, \ - {kEta, {conf.eta}}, \ - {kPhi, {conf.phi}}, \ - {kSign, {conf.sign}}, \ - {kMass, {conf.mass}}, - -#define TRACK_HIST_QA_MAP(confAnalysis, confQa) \ - {kPAtPv, {confQa.p}}, \ - {kPTpc, {confQa.p}}, \ - {kItsCluster, {confQa.itsCluster}}, \ - {kItsClusterIb, {confQa.itsClusterIb}}, \ - {kPtVsEta, {confAnalysis.pt, confAnalysis.eta}}, \ - {kPtVsPhi, {confAnalysis.pt, confAnalysis.phi}}, \ - {kPhiVsEta, {confAnalysis.phi, confAnalysis.eta}}, \ - {kPtVsItsCluster, {confAnalysis.pt, confQa.itsCluster}}, \ - {kPtVsTpcCluster, {confAnalysis.pt, confQa.tpcCluster}}, \ - {kPtVsTpcCrossedRows, {confAnalysis.pt, confQa.tpcCrossedRows}}, \ - {kPtVsTpcClusterOverCrossedRows, {confAnalysis.pt, confQa.tpcClusterOverCrossedRows}}, \ - {kPtVsTpcClusterShared, {confAnalysis.pt, confQa.tpcClusterShared}}, \ - {kPtVsTpcClusterFractionShared, {confAnalysis.pt, confQa.tpcClusterFractionShared}}, \ - {kTpcClusterVsTpcCrossedRows, {confQa.tpcCluster, confQa.tpcCrossedRows}}, \ - {kTpcClusterVsTpcClusterShared, {confQa.tpcCluster, confQa.tpcClusterShared}}, \ - {kTpcCrossedRows, {confQa.tpcCrossedRows}}, \ - {kTpcCluster, {confQa.tpcCluster}}, \ - {kTpcClusterOverCrossedRows, {confQa.tpcClusterOverCrossedRows}}, \ - {kTpcClusterShared, {confQa.tpcClusterShared}}, \ - {kTpcClusterFractionShared, {confQa.tpcClusterFractionShared}}, \ - {kPtVsDcaxy, {confAnalysis.pt, confQa.dcaXy}}, \ - {kPtVsDcaz, {confAnalysis.pt, confQa.dcaZ}}, \ - {kPtVsDca, {confAnalysis.pt, confQa.dca}}, \ - {kItsSignal, {confQa.p, confQa.itsSignal}}, \ - {kItsElectron, {confQa.p, confQa.itsElectron}}, \ - {kItsPion, {confQa.p, confQa.itsPion}}, \ - {kItsKaon, {confQa.p, confQa.itsKaon}}, \ - {kItsProton, {confQa.p, confQa.itsProton}}, \ - {kItsDeuteron, {confQa.p, confQa.itsDeuteron}}, \ - {kItsTriton, {confQa.p, confQa.itsTriton}}, \ - {kItsHelium, {confQa.p, confQa.itsHelium}}, \ - {kTpcSignal, {confQa.p, confQa.tpcSignal}}, \ - {kTpcElectron, {confQa.p, confQa.tpcElectron}}, \ - {kTpcPion, {confQa.p, confQa.tpcPion}}, \ - {kTpcKaon, {confQa.p, confQa.tpcKaon}}, \ - {kTpcProton, {confQa.p, confQa.tpcProton}}, \ - {kTpcDeuteron, {confQa.p, confQa.tpcDeuteron}}, \ - {kTpcTriton, {confQa.p, confQa.tpcTriton}}, \ - {kTpcHelium, {confQa.p, confQa.tpcHelium}}, \ - {kTofBeta, {confQa.p, confQa.tofBeta}}, \ - {kTofMass, {confQa.p, confQa.tofMass}}, \ - {kTofElectron, {confQa.p, confQa.tofElectron}}, \ - {kTofPion, {confQa.p, confQa.tofPion}}, \ - {kTofKaon, {confQa.p, confQa.tofKaon}}, \ - {kTofProton, {confQa.p, confQa.tofProton}}, \ - {kTofDeuteron, {confQa.p, confQa.tofDeuteron}}, \ - {kTofTriton, {confQa.p, confQa.tofTriton}}, \ - {kTofHelium, {confQa.p, confQa.tofHelium}}, \ - {kTpcitsElectron, {confQa.p, confQa.tpcitsElectron}}, \ - {kTpcitsPion, {confQa.p, confQa.tpcitsPion}}, \ - {kTpcitsKaon, {confQa.p, confQa.tpcitsKaon}}, \ - {kTpcitsProton, {confQa.p, confQa.tpcitsProton}}, \ - {kTpcitsDeuteron, {confQa.p, confQa.tpcitsDeuteron}}, \ - {kTpcitsTriton, {confQa.p, confQa.tpcitsTriton}}, \ - {kTpcitsHelium, {confQa.p, confQa.tpcitsHelium}}, \ - {kTpctofElectron, {confQa.p, confQa.tpctofElectron}}, \ - {kTpctofPion, {confQa.p, confQa.tpctofPion}}, \ - {kTpctofKaon, {confQa.p, confQa.tpctofKaon}}, \ - {kTpctofProton, {confQa.p, confQa.tpctofProton}}, \ - {kTpctofDeuteron, {confQa.p, confQa.tpctofDeuteron}}, \ - {kTpctofTriton, {confQa.p, confQa.tpctofTriton}}, \ - {kTpctofHelium, {confQa.p, confQa.tpctofHelium}}, - -#define TRACK_HIST_MC_MAP(conf) \ - {kTruePtVsPt, {conf.pt, conf.pt}}, \ - {kTrueEtaVsEta, {conf.eta, conf.eta}}, \ - {kTruePhiVsPhi, {conf.phi, conf.phi}}, \ - {kPdg, {conf.pdgCodes}}, \ - {kPdgMother, {conf.pdgCodes}}, \ - {kPdgPartonicMother, {conf.pdgCodes}}, - -#define TRACK_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kPrimary, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kFromWrongCollision, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kFromMaterial, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kMissidentified, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kSecondary1, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kSecondary2, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kSecondary3, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, \ - {kSecondaryOther, {confAnalysis.pt, confQa.dcaXy, confQa.dcaZ}}, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACK_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kSign, {(conf).sign}}, \ + {kMass, {(conf).mass}}, \ + {kItsPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTpcPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTofPurity, {(conf).pt, (conf).purityNSigma}}, \ + {kTpcitsPurity, {(conf).pt, (conf).purityNSigmaComb}}, \ + {kTpctofPurity, {(conf).pt, (conf).purityNSigmaComb}}, \ + {kFractionDca, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACK_HIST_QA_MAP(confAnalysis, confQa) \ + {kPAtPv, {(confQa).p}}, \ + {kPTpc, {(confQa).p}}, \ + {kItsCluster, {(confQa).itsCluster}}, \ + {kItsClusterIb, {(confQa).itsClusterIb}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsItsCluster, {(confAnalysis).pt, (confQa).itsCluster}}, \ + {kPtVsTpcCluster, {(confAnalysis).pt, (confQa).tpcCluster}}, \ + {kPtVsTpcCrossedRows, {(confAnalysis).pt, (confQa).tpcCrossedRows}}, \ + {kPtVsTpcClusterOverCrossedRows, {(confAnalysis).pt, (confQa).tpcClusterOverCrossedRows}}, \ + {kPtVsTpcClusterShared, {(confAnalysis).pt, (confQa).tpcClusterShared}}, \ + {kPtVsTpcClusterFractionShared, {(confAnalysis).pt, (confQa).tpcClusterFractionShared}}, \ + {kPtVsItsChi2, {(confAnalysis).pt, (confQa).itsChi2}}, \ + {kPtVsTpcChi2, {(confAnalysis).pt, (confQa).tpcChi2}}, \ + {kTpcClusterVsTpcCrossedRows, {(confQa).tpcCluster, (confQa).tpcCrossedRows}}, \ + {kTpcClusterVsTpcClusterShared, {(confQa).tpcCluster, (confQa).tpcClusterShared}}, \ + {kTpcCrossedRows, {(confQa).tpcCrossedRows}}, \ + {kTpcCluster, {(confQa).tpcCluster}}, \ + {kTpcClusterOverCrossedRows, {(confQa).tpcClusterOverCrossedRows}}, \ + {kTpcClusterShared, {(confQa).tpcClusterShared}}, \ + {kTpcClusterFractionShared, {(confQa).tpcClusterFractionShared}}, \ + {kItsChi2, {(confQa).itsChi2}}, \ + {kTpcChi2, {(confQa).tpcChi2}}, \ + {kPtVsDcaxy, {(confAnalysis).pt, (confQa).dcaXy}}, \ + {kPtVsDcaz, {(confAnalysis).pt, (confQa).dcaZ}}, \ + {kPtVsDca, {(confAnalysis).pt, (confQa).dca}}, \ + {kPVsPTpc, {(confQa).p, (confQa).p}}, \ + {kItsSignal, {(confQa).p, (confQa).itsSignal}}, \ + {kItsElectron, {(confQa).p, (confQa).itsElectron}}, \ + {kItsPion, {(confQa).p, (confQa).itsPion}}, \ + {kItsKaon, {(confQa).p, (confQa).itsKaon}}, \ + {kItsProton, {(confQa).p, (confQa).itsProton}}, \ + {kItsDeuteron, {(confQa).p, (confQa).itsDeuteron}}, \ + {kItsTriton, {(confQa).p, (confQa).itsTriton}}, \ + {kItsHelium, {(confQa).p, (confQa).itsHelium}}, \ + {kTpcSignal, {(confQa).p, (confQa).tpcSignal}}, \ + {kTpcElectron, {(confQa).p, (confQa).tpcElectron}}, \ + {kTpcPion, {(confQa).p, (confQa).tpcPion}}, \ + {kTpcKaon, {(confQa).p, (confQa).tpcKaon}}, \ + {kTpcProton, {(confQa).p, (confQa).tpcProton}}, \ + {kTpcDeuteron, {(confQa).p, (confQa).tpcDeuteron}}, \ + {kTpcTriton, {(confQa).p, (confQa).tpcTriton}}, \ + {kTpcHelium, {(confQa).p, (confQa).tpcHelium}}, \ + {kTofBeta, {(confQa).p, (confQa).tofBeta}}, \ + {kTofMass, {(confQa).p, (confQa).tofMass}}, \ + {kTofElectron, {(confQa).p, (confQa).tofElectron}}, \ + {kTofPion, {(confQa).p, (confQa).tofPion}}, \ + {kTofKaon, {(confQa).p, (confQa).tofKaon}}, \ + {kTofProton, {(confQa).p, (confQa).tofProton}}, \ + {kTofDeuteron, {(confQa).p, (confQa).tofDeuteron}}, \ + {kTofTriton, {(confQa).p, (confQa).tofTriton}}, \ + {kTofHelium, {(confQa).p, (confQa).tofHelium}}, \ + {kTpcitsElectron, {(confQa).p, (confQa).tpcitsElectron}}, \ + {kTpcitsPion, {(confQa).p, (confQa).tpcitsPion}}, \ + {kTpcitsKaon, {(confQa).p, (confQa).tpcitsKaon}}, \ + {kTpcitsProton, {(confQa).p, (confQa).tpcitsProton}}, \ + {kTpcitsDeuteron, {(confQa).p, (confQa).tpcitsDeuteron}}, \ + {kTpcitsTriton, {(confQa).p, (confQa).tpcitsTriton}}, \ + {kTpcitsHelium, {(confQa).p, (confQa).tpcitsHelium}}, \ + {kTpctofElectron, {(confQa).p, (confQa).tpctofElectron}}, \ + {kTpctofPion, {(confQa).p, (confQa).tpctofPion}}, \ + {kTpctofKaon, {(confQa).p, (confQa).tpctofKaon}}, \ + {kTpctofProton, {(confQa).p, (confQa).tpctofProton}}, \ + {kTpctofDeuteron, {(confQa).p, (confQa).tpctofDeuteron}}, \ + {kTpctofTriton, {(confQa).p, (confQa).tpctofTriton}}, \ + {kTpctofHelium, {(confQa).p, (confQa).tpctofHelium}}, \ + {kTpcVsItsElectron, {(confQa).p, (confQa).tpcElectron, (confQa).itsElectron}}, \ + {kTpcVsItsPion, {(confQa).p, (confQa).tpcPion, (confQa).itsPion}}, \ + {kTpcVsItsKaon, {(confQa).p, (confQa).tpcKaon, (confQa).itsKaon}}, \ + {kTpcVsItsProton, {(confQa).p, (confQa).tpcProton, (confQa).itsProton}}, \ + {kTpcVsItsDeuteron, {(confQa).p, (confQa).tpcDeuteron, (confQa).itsDeuteron}}, \ + {kTpcVsItsTriton, {(confQa).p, (confQa).tpcTriton, (confQa).itsTriton}}, \ + {kTpcVsItsHelium, {(confQa).p, (confQa).tpcHelium, (confQa).itsHelium}}, \ + {kTpcVsTofElectron, {(confQa).p, (confQa).tpcElectron, (confQa).tofElectron}}, \ + {kTpcVsTofPion, {(confQa).p, (confQa).tpcPion, (confQa).tofPion}}, \ + {kTpcVsTofKaon, {(confQa).p, (confQa).tpcKaon, (confQa).tofKaon}}, \ + {kTpcVsTofProton, {(confQa).p, (confQa).tpcProton, (confQa).tofProton}}, \ + {kTpcVsTofDeuteron, {(confQa).p, (confQa).tpcDeuteron, (confQa).tofDeuteron}}, \ + {kTpcVsTofTriton, {(confQa).p, (confQa).tpcTriton, (confQa).tofTriton}}, \ + {kTpcVsTofHelium, {(confQa).p, (confQa).tpcHelium, (confQa).tofHelium}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACK_HIST_MC_MAP(conf) \ + {kTruePtVsPt, {(conf).pt, (conf).pt}}, \ + {kTrueEtaVsEta, {(conf).eta, (conf).eta}}, \ + {kTruePhiVsPhi, {(conf).phi, (conf).phi}}, \ + {kPdg, {(conf).pdgCodes}}, \ + {kPdgMother, {(conf).pdgCodes}}, \ + {kPdgPartonicMother, {(conf).pdgCodes}}, + +// per-origin truth-split variants of the FitVars fraction-estimation DCA histogram; binned on +// fractionDcaXy/fractionDcaZ (not the QA-only dcaXy/dcaZ) so they are available wherever FitVars +// is, including the pair task where no QA binning config exists +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACK_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kPrimary, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kMissidentified, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary1, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary2, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondary3, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionDcaXy, (conf).fractionDcaZ}}, template auto makeTrackHistSpecMap(const T& confBinningAnalysis) @@ -465,7 +598,8 @@ auto makeTrackMcHistSpecMap(T const& confBinningAnalysis) { return std::map>{ TRACK_HIST_ANALYSIS_MAP(confBinningAnalysis) - TRACK_HIST_MC_MAP(confBinningAnalysis)}; + TRACK_HIST_MC_MAP(confBinningAnalysis) + TRACK_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; }; template @@ -483,73 +617,114 @@ auto makeTrackMcQaHistSpecMap(T1 const& confBinningAnalysis, T2 const& confBinni TRACK_HIST_ANALYSIS_MAP(confBinningAnalysis) TRACK_HIST_QA_MAP(confBinningAnalysis, confBinningQa) TRACK_HIST_MC_MAP(confBinningAnalysis) - TRACK_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + TRACK_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; }; #undef TRACK_HIST_ANALYSIS_MAP #undef TRACK_HIST_QA_MAP #undef TRACK_HIST_MC_MAP -#undef TRACK_HIST_MC_QA_MAP +#undef TRACK_HIST_FITVARS_MC_MAP constexpr char PrefixTrackQa[] = "TrackQA/"; -constexpr char PrefixTrack1[] = "Track1/"; -constexpr char PrefixTrack2[] = "Track2/"; -constexpr char PrefixTrack3[] = "Track3/"; - -constexpr char PrefixResonancePosDaughter[] = "ResonancePosDau/"; -constexpr char PrefixResonanceNegDaughter[] = "ResonanceNegDau/"; +// same-event / mixed-event siblings: single-particle histograms for a given track slot, split by +// whether the particle came from a same-event or mixed-event pair +constexpr char PrefixTrack1Se[] = "Track1/SE/"; +constexpr char PrefixTrack2Se[] = "Track2/SE/"; +constexpr char PrefixTrack3Se[] = "Track3/SE/"; +constexpr char PrefixTrack1Me[] = "Track1/ME/"; +constexpr char PrefixTrack2Me[] = "Track2/ME/"; +constexpr char PrefixTrack3Me[] = "Track3/ME/"; +// for dN/deta tracks +constexpr char PrefixDndetaTrackGlobal[] = "DndetaTrackGlobal/"; +constexpr char PrefixDndetaTrackItsOnly[] = "DndetaTrackItsOnly/"; + +// daughter-track prefixes below: Se/Me siblings for pair-builder single-particle histograms +// (split by whether the daughter came from a same-event or mixed-event candidate), plus the +// original, unsplit Qa variants used by standalone QA tasks (left untouched). +constexpr char PrefixResonancePosDaughterSe[] = "ResonancePosDau/SE/"; +constexpr char PrefixResonanceNegDaughterSe[] = "ResonanceNegDau/SE/"; +constexpr char PrefixResonancePosDaughterMe[] = "ResonancePosDau/ME/"; +constexpr char PrefixResonanceNegDaughterMe[] = "ResonanceNegDau/ME/"; constexpr char PrefixResonancePosDaughterQa[] = "ResonancePosDauQa/"; constexpr char PrefixResonanceNegDaughterQa[] = "ResonanceNegDauQa/"; -constexpr char PrefixV01PosDaughter[] = "V01PosDau/"; -constexpr char PrefixV01NegDaughter[] = "V01NegDau/"; -constexpr char PrefixV02PosDaughter[] = "V02PosDau/"; -constexpr char PrefixV02NegDaughter[] = "V02NegDau/"; +constexpr char PrefixD01PosDaughterSe[] = "D01PosDau/SE/"; +constexpr char PrefixD01NegDaughterSe[] = "D01NegDau/SE/"; +constexpr char PrefixD02PosDaughterSe[] = "D02PosDau/SE/"; +constexpr char PrefixD02NegDaughterSe[] = "D02NegDau/SE/"; +constexpr char PrefixD01PosDaughterMe[] = "D01PosDau/ME/"; +constexpr char PrefixD01NegDaughterMe[] = "D01NegDau/ME/"; +constexpr char PrefixD02PosDaughterMe[] = "D02PosDau/ME/"; +constexpr char PrefixD02NegDaughterMe[] = "D02NegDau/ME/"; +constexpr char PrefixD01PosDaughterQa[] = "D01PosDauQa/"; +constexpr char PrefixD01NegDaughterQa[] = "D01NegDauQa/"; + +constexpr char PrefixLc1ProtonDaughterSe[] = "Lc1ProtonDau/SE/"; +constexpr char PrefixLc1KaonDaughterSe[] = "Lc1KaonDau/SE/"; +constexpr char PrefixLc1PionDaughterSe[] = "Lc1PionDau/SE/"; +constexpr char PrefixLc1ProtonDaughterMe[] = "Lc1ProtonDau/ME/"; +constexpr char PrefixLc1KaonDaughterMe[] = "Lc1KaonDau/ME/"; +constexpr char PrefixLc1PionDaughterMe[] = "Lc1PionDau/ME/"; +constexpr char PrefixLc1ProtonDaughterQa[] = "Lc1ProtonDauQa/"; +constexpr char PrefixLc1KaonDaughterQa[] = "Lc1KaonDauQa/"; +constexpr char PrefixLc1PionDaughterQa[] = "Lc1PionDauQa/"; + +constexpr char PrefixV01PosDaughterSe[] = "V01PosDau/SE/"; +constexpr char PrefixV01NegDaughterSe[] = "V01NegDau/SE/"; +constexpr char PrefixV02PosDaughterSe[] = "V02PosDau/SE/"; +constexpr char PrefixV02NegDaughterSe[] = "V02NegDau/SE/"; +constexpr char PrefixV01PosDaughterMe[] = "V01PosDau/ME/"; +constexpr char PrefixV01NegDaughterMe[] = "V01NegDau/ME/"; +constexpr char PrefixV02PosDaughterMe[] = "V02PosDau/ME/"; +constexpr char PrefixV02NegDaughterMe[] = "V02NegDau/ME/"; constexpr char PrefixV0PosDaughterQa[] = "V0PosDauQa/"; constexpr char PrefixV0NegDaughterQa[] = "V0NegDauQa/"; -constexpr char PrefixCascadePosDaughter[] = "CascadePosDau/"; -constexpr char PrefixCascadeNegDaughter[] = "CascadeNegDau/"; -constexpr char PrefixCascadeBachelor[] = "CascadeBachelor/"; +constexpr char PrefixCascadePosDaughterSe[] = "CascadePosDau/SE/"; +constexpr char PrefixCascadeNegDaughterSe[] = "CascadeNegDau/SE/"; +constexpr char PrefixCascadeBachelorSe[] = "CascadeBachelor/SE/"; +constexpr char PrefixCascadePosDaughterMe[] = "CascadePosDau/ME/"; +constexpr char PrefixCascadeNegDaughterMe[] = "CascadeNegDau/ME/"; +constexpr char PrefixCascadeBachelorMe[] = "CascadeBachelor/ME/"; constexpr char PrefixCascadePosDaughterQa[] = "CascadePosDauQa/"; constexpr char PrefixCascadeNegDaughterQa[] = "CascadeNegDauQa/"; constexpr char PrefixCascadeBachelorQa[] = "CascadeBachelorQa/"; -constexpr char PrefixKinkChaDaughter[] = "KinkChaDau/"; +constexpr char PrefixKinkChaDaughterSe[] = "KinkChaDau/SE/"; +constexpr char PrefixKinkChaDaughterMe[] = "KinkChaDau/ME/"; constexpr char PrefixKinkChaDaughterQa[] = "KinkChaDauQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view PidDir = "PID/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; -template +template class TrackHistManager { public: TrackHistManager() = default; ~TrackHistManager() = default; - // init for analysis and mc + // init for analysis, qa, mc and fitvars -- from a selection config object (track-track pairs, + // triplets, standalone QA task's own species). puritySlotMask lives on ConfTrackSelection + // itself (it picks which species slot in the packed purity tables this selection corresponds + // to), so it travels along with the selection object instead of needing its own parameter at + // every layer between the task and here. template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, T const& ConfTrackSelection) { - mHistogramRegistry = registry; - mAbsCharge = std::abs(ConfTrackSelection.chargeAbs.value); - mPdgCode = std::abs(ConfTrackSelection.pdgCodeAbs.value) * ConfTrackSelection.chargeSign.value; - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { - this->initAnalysis(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kQa)) { - this->initQa(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { - this->initMc(Specs); - } + this->template initCommon(registry, Specs, + std::abs(ConfTrackSelection.chargeAbs.value), + std::abs(ConfTrackSelection.pdgCodeAbs.value) * ConfTrackSelection.chargeSign.value, + ConfTrackSelection.puritySlotMask.value); } + // same, from raw charge/pdg ints (V0/cascade/resonance daughter sub-managers, which have no + // selection config object of their own -- the parent builder already applied the selection) template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, @@ -557,21 +732,10 @@ class TrackHistManager int ChargeSign, int PdgCodeAbs) { - mHistogramRegistry = registry; - mAbsCharge = std::abs(ChargeAbs); - mPdgCode = std::abs(PdgCodeAbs) * ChargeSign; - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { - this->initAnalysis(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kQa)) { - this->initQa(Specs); - } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { - this->initMc(Specs); - } + this->template initCommon(registry, Specs, std::abs(ChargeAbs), std::abs(PdgCodeAbs) * ChargeSign, 1); } - // init for analysis and qa and mc + // same as above, but also configure the QA-only optional histograms (species PID toggles, plot2d, ...) template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, @@ -597,29 +761,60 @@ class TrackHistManager template void fill(T1 const& track, T2 const& /*trackTable*/) { - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { this->fillAnalysis(track); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { this->fillQa(track); } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template fillFitVars(track); + } } - template - void fill(T1 const& track, T2 const& /*trackTable*/, T3 const& mcParticles, T4 const& mcMothers, T5 const& mcPartonicMothers) + template + void fill(T1 const& track, T2 const& /*trackTable*/, T3 const& col, T4 const& mcParticles, T5 const& mcMothers, T6 const& mcPartonicMothers) { - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { this->fillAnalysis(track); } if constexpr (isFlagSet(mode, modes::Mode::kQa)) { this->fillQa(track); } if constexpr (isFlagSet(mode, modes::Mode::kMc)) { - this->template fillMc(track, mcParticles, mcMothers, mcPartonicMothers); + this->template fillMc(track, col, mcParticles, mcMothers, mcPartonicMothers); + } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template fillFitVars(track); } } private: + // shared dispatch for all init() overloads above -- the only thing that differs between them + // is how mAbsCharge/mPdgCode/mPuritySlotMask get computed + template + void initCommon(o2::framework::HistogramRegistry* registry, + std::map> const& Specs, + int absCharge, int pdgCode, int puritySlotMask) + { + mHistogramRegistry = registry; + mAbsCharge = absCharge; + mPdgCode = pdgCode; + mPuritySlotMask = puritySlotMask; + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { + this->initAnalysis(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kQa)) { + this->initQa(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + this->initMc(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(Specs); + } + } + template void enableOptionalHistograms(T const& ConfBinningQa) { @@ -655,6 +850,50 @@ class TrackHistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {Specs.at(kMass)}); } + // purity (packed nSigma tables) and fraction (DCA) -- only called when mode carries kFitVars, + // which also implies the track type actually has the relevant tables joined. + // Purity itself is skipped in QA mode: the QA task already has full per-species PID + // (Its/Tpc/Tof/Tpcits/Tpctof/TpcVsIts/TpcVsTof in PID/), so the puritySlotMask-selected, + // single-species Purity histograms would be pure redundancy there -- they only earn their + // keep in the pair task, where there is no per-species breakdown to fall back on. + template + void initFitVars(std::map> const& Specs) + { + std::string fitVarsDir = std::string(prefix) + std::string(FitVarsDir); + if constexpr (!isFlagSet(mode, modes::Mode::kQa)) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kItsPurity, HistTable), getHistDesc(kItsPurity, HistTable), getHistType(kItsPurity, HistTable), {Specs.at(kItsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcPurity, HistTable), getHistDesc(kTpcPurity, HistTable), getHistType(kTpcPurity, HistTable), {Specs.at(kTpcPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTofPurity, HistTable), getHistDesc(kTofPurity, HistTable), getHistType(kTofPurity, HistTable), {Specs.at(kTofPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpcitsPurity, HistTable), getHistDesc(kTpcitsPurity, HistTable), getHistType(kTpcitsPurity, HistTable), {Specs.at(kTpcitsPurity)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kTpctofPurity, HistTable), getHistDesc(kTpctofPurity, HistTable), getHistType(kTpctofPurity, HistTable), {Specs.at(kTpctofPurity)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionDca, HistTable), getHistDesc(kFractionDca, HistTable), getHistType(kFractionDca, HistTable), {Specs.at(kFractionDca)}); + + // per-origin truth-split fraction-DCA templates; need MC truth, so only booked when the mode + // also carries kMc (available in the QA task and in the pair task's SE/ME, consistently, since + // they use the same fractionDcaXy/fractionDcaZ binning as hFractionDca above) + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {Specs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {Specs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {Specs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {Specs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {Specs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {Specs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {Specs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {Specs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {Specs.at(kSecondaryOther)}); + } + } + } + void initQa(std::map> const& Specs) { std::string qaDir = std::string(prefix) + std::string(QaDir); @@ -668,6 +907,16 @@ class TrackHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterOverCrossedRows, HistTable), getHistDesc(kTpcClusterOverCrossedRows, HistTable), getHistType(kTpcClusterOverCrossedRows, HistTable), {Specs.at(kTpcClusterOverCrossedRows)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterShared, HistTable), getHistDesc(kTpcClusterShared, HistTable), getHistType(kTpcClusterShared, HistTable), {Specs.at(kTpcClusterShared)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterFractionShared, HistTable), getHistDesc(kTpcClusterFractionShared, HistTable), getHistType(kTpcClusterFractionShared, HistTable), {Specs.at(kTpcClusterFractionShared)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kItsChi2, HistTable), getHistDesc(kItsChi2, HistTable), getHistType(kItsChi2, HistTable), {Specs.at(kItsChi2)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kTpcChi2, HistTable), getHistDesc(kTpcChi2, HistTable), getHistType(kTpcChi2, HistTable), {Specs.at(kTpcChi2)}); + + // track type: shows e.g. whether rows were only stored as daughters of a candidate + const int nTrackTypes = static_cast(modes::Track::kTrackTypeLast); + const o2::framework::AxisSpec axisTrackType = {nTrackTypes, -0.5, static_cast(nTrackTypes) - 0.5}; + mHistogramRegistry->add(qaDir + getHistNameV2(kTrackType, HistTable), getHistDesc(kTrackType, HistTable), getHistType(kTrackType, HistTable), {axisTrackType}); + for (int i = 0; i < nTrackTypes; i++) { + mHistogramRegistry->get(HIST(prefix) + HIST(QaDir) + HIST(histmanager::getHistName(kTrackType, HistTable)))->GetXaxis()->SetBinLabel(1 + i, modes::trackTypeToString(static_cast(i))); + } // qa 2d if (mPlot2d) { @@ -680,78 +929,103 @@ class TrackHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcClusterOverCrossedRows, HistTable), getHistDesc(kPtVsTpcClusterOverCrossedRows, HistTable), getHistType(kPtVsTpcClusterOverCrossedRows, HistTable), {Specs.at(kPtVsTpcClusterOverCrossedRows)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcClusterShared, HistTable), getHistDesc(kPtVsTpcClusterShared, HistTable), getHistType(kPtVsTpcClusterShared, HistTable), {Specs.at(kPtVsTpcClusterShared)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcClusterFractionShared, HistTable), getHistDesc(kPtVsTpcClusterFractionShared, HistTable), getHistType(kPtVsTpcClusterFractionShared, HistTable), {Specs.at(kPtVsTpcClusterFractionShared)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsItsChi2, HistTable), getHistDesc(kPtVsItsChi2, HistTable), getHistType(kPtVsItsChi2, HistTable), {Specs.at(kPtVsItsChi2)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsTpcChi2, HistTable), getHistDesc(kPtVsTpcChi2, HistTable), getHistType(kPtVsTpcChi2, HistTable), {Specs.at(kPtVsTpcChi2)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterVsTpcCrossedRows, HistTable), getHistDesc(kTpcClusterVsTpcCrossedRows, HistTable), getHistType(kTpcClusterVsTpcCrossedRows, HistTable), {Specs.at(kTpcClusterVsTpcCrossedRows)}); mHistogramRegistry->add(qaDir + getHistNameV2(kTpcClusterVsTpcClusterShared, HistTable), getHistDesc(kTpcClusterVsTpcClusterShared, HistTable), getHistType(kTpcClusterVsTpcClusterShared, HistTable), {Specs.at(kTpcClusterVsTpcClusterShared)}); // dca mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsDcaxy, HistTable), getHistDesc(kPtVsDcaxy, HistTable), getHistType(kPtVsDcaxy, HistTable), {Specs.at(kPtVsDcaxy)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsDcaz, HistTable), getHistDesc(kPtVsDcaz, HistTable), getHistType(kPtVsDcaz, HistTable), {Specs.at(kPtVsDcaz)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsDca, HistTable), getHistDesc(kPtVsDca, HistTable), getHistType(kPtVsDca, HistTable), {Specs.at(kPtVsDca)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPVsPTpc, HistTable), getHistDesc(kPVsPTpc, HistTable), getHistType(kPVsPTpc, HistTable), {Specs.at(kPVsPTpc)}); } std::string pidDir = std::string(prefix) + std::string(PidDir); - mHistogramRegistry->add(pidDir + getHistNameV2(kItsSignal, HistTable), getHistDesc(kItsSignal, HistTable), getHistType(kItsSignal, HistTable), {Specs.at(kItsSignal)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcSignal, HistTable), getHistDesc(kTpcSignal, HistTable), getHistType(kTpcSignal, HistTable), {Specs.at(kTpcSignal)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofBeta, HistTable), getHistDesc(kTofBeta, HistTable), getHistType(kTofBeta, HistTable), {Specs.at(kTofBeta)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofMass, HistTable), getHistDesc(kTofMass, HistTable), getHistType(kTofMass, HistTable), {Specs.at(kTofMass)}); + this->addPidHistogram(pidDir, kItsSignal, Specs); + this->addPidHistogram(pidDir, kTpcSignal, Specs); + this->addPidHistogram(pidDir, kTofBeta, Specs); + this->addPidHistogram(pidDir, kTofMass, Specs); if (mPlotElectronPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsElectron, HistTable), getHistDesc(kItsElectron, HistTable), getHistType(kItsElectron, HistTable), {Specs.at(kItsElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcElectron, HistTable), getHistDesc(kTpcElectron, HistTable), getHistType(kTpcElectron, HistTable), {Specs.at(kTpcElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofElectron, HistTable), getHistDesc(kTofElectron, HistTable), getHistType(kTofElectron, HistTable), {Specs.at(kTofElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsElectron, HistTable), getHistDesc(kTpcitsElectron, HistTable), getHistType(kTpcitsElectron, HistTable), {Specs.at(kTpcitsElectron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofElectron, HistTable), getHistDesc(kTpctofElectron, HistTable), getHistType(kTpctofElectron, HistTable), {Specs.at(kTpctofElectron)}); + this->addPidHistogram(pidDir, kItsElectron, Specs); + this->addPidHistogram(pidDir, kTpcElectron, Specs); + this->addPidHistogram(pidDir, kTofElectron, Specs); + this->addPidHistogram(pidDir, kTpcitsElectron, Specs); + this->addPidHistogram(pidDir, kTpctofElectron, Specs); + this->addPidHistogram(pidDir, kTpcVsItsElectron, Specs); + this->addPidHistogram(pidDir, kTpcVsTofElectron, Specs); } if (mPlotPionPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsPion, HistTable), getHistDesc(kItsPion, HistTable), getHistType(kItsPion, HistTable), {Specs.at(kItsPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcPion, HistTable), getHistDesc(kTpcPion, HistTable), getHistType(kTpcPion, HistTable), {Specs.at(kTpcPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofPion, HistTable), getHistDesc(kTofPion, HistTable), getHistType(kTofPion, HistTable), {Specs.at(kTofPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsPion, HistTable), getHistDesc(kTpcitsPion, HistTable), getHistType(kTpcitsPion, HistTable), {Specs.at(kTpcitsPion)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofPion, HistTable), getHistDesc(kTpctofPion, HistTable), getHistType(kTpctofPion, HistTable), {Specs.at(kTpctofPion)}); + this->addPidHistogram(pidDir, kItsPion, Specs); + this->addPidHistogram(pidDir, kTpcPion, Specs); + this->addPidHistogram(pidDir, kTofPion, Specs); + this->addPidHistogram(pidDir, kTpcitsPion, Specs); + this->addPidHistogram(pidDir, kTpctofPion, Specs); + this->addPidHistogram(pidDir, kTpcVsItsPion, Specs); + this->addPidHistogram(pidDir, kTpcVsTofPion, Specs); } if (mPlotKaonPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsKaon, HistTable), getHistDesc(kItsKaon, HistTable), getHistType(kItsKaon, HistTable), {Specs.at(kItsKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcKaon, HistTable), getHistDesc(kTpcKaon, HistTable), getHistType(kTpcKaon, HistTable), {Specs.at(kTpcKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofKaon, HistTable), getHistDesc(kTofKaon, HistTable), getHistType(kTofKaon, HistTable), {Specs.at(kTofKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsKaon, HistTable), getHistDesc(kTpcitsKaon, HistTable), getHistType(kTpcitsKaon, HistTable), {Specs.at(kTpcitsKaon)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofKaon, HistTable), getHistDesc(kTpctofKaon, HistTable), getHistType(kTpctofKaon, HistTable), {Specs.at(kTpctofKaon)}); + this->addPidHistogram(pidDir, kItsKaon, Specs); + this->addPidHistogram(pidDir, kTpcKaon, Specs); + this->addPidHistogram(pidDir, kTofKaon, Specs); + this->addPidHistogram(pidDir, kTpcitsKaon, Specs); + this->addPidHistogram(pidDir, kTpctofKaon, Specs); + this->addPidHistogram(pidDir, kTpcVsItsKaon, Specs); + this->addPidHistogram(pidDir, kTpcVsTofKaon, Specs); } if (mPlotProtonPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsProton, HistTable), getHistDesc(kItsProton, HistTable), getHistType(kItsProton, HistTable), {Specs.at(kItsProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcProton, HistTable), getHistDesc(kTpcProton, HistTable), getHistType(kTpcProton, HistTable), {Specs.at(kTpcProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofProton, HistTable), getHistDesc(kTofProton, HistTable), getHistType(kTofProton, HistTable), {Specs.at(kTofProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsProton, HistTable), getHistDesc(kTpcitsProton, HistTable), getHistType(kTpcitsProton, HistTable), {Specs.at(kTpcitsProton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofProton, HistTable), getHistDesc(kTpctofProton, HistTable), getHistType(kTpctofProton, HistTable), {Specs.at(kTpctofProton)}); + this->addPidHistogram(pidDir, kItsProton, Specs); + this->addPidHistogram(pidDir, kTpcProton, Specs); + this->addPidHistogram(pidDir, kTofProton, Specs); + this->addPidHistogram(pidDir, kTpcitsProton, Specs); + this->addPidHistogram(pidDir, kTpctofProton, Specs); + this->addPidHistogram(pidDir, kTpcVsItsProton, Specs); + this->addPidHistogram(pidDir, kTpcVsTofProton, Specs); } if (mPlotDeuteronPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsDeuteron, HistTable), getHistDesc(kItsDeuteron, HistTable), getHistType(kItsDeuteron, HistTable), {Specs.at(kItsDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcDeuteron, HistTable), getHistDesc(kTpcDeuteron, HistTable), getHistType(kTpcDeuteron, HistTable), {Specs.at(kTpcDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofDeuteron, HistTable), getHistDesc(kTofDeuteron, HistTable), getHistType(kTofDeuteron, HistTable), {Specs.at(kTofDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsDeuteron, HistTable), getHistDesc(kTpcitsDeuteron, HistTable), getHistType(kTpcitsDeuteron, HistTable), {Specs.at(kTpcitsDeuteron)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofDeuteron, HistTable), getHistDesc(kTpctofDeuteron, HistTable), getHistType(kTpctofDeuteron, HistTable), {Specs.at(kTpctofDeuteron)}); + this->addPidHistogram(pidDir, kItsDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcDeuteron, Specs); + this->addPidHistogram(pidDir, kTofDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcitsDeuteron, Specs); + this->addPidHistogram(pidDir, kTpctofDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcVsItsDeuteron, Specs); + this->addPidHistogram(pidDir, kTpcVsTofDeuteron, Specs); } if (mPlotTritonPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsTriton, HistTable), getHistDesc(kItsTriton, HistTable), getHistType(kItsTriton, HistTable), {Specs.at(kItsTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcTriton, HistTable), getHistDesc(kTpcTriton, HistTable), getHistType(kTpcTriton, HistTable), {Specs.at(kTpcTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofTriton, HistTable), getHistDesc(kTofTriton, HistTable), getHistType(kTofTriton, HistTable), {Specs.at(kTofTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsTriton, HistTable), getHistDesc(kTpcitsTriton, HistTable), getHistType(kTpcitsTriton, HistTable), {Specs.at(kTpcitsTriton)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofTriton, HistTable), getHistDesc(kTpctofTriton, HistTable), getHistType(kTpctofTriton, HistTable), {Specs.at(kTpctofTriton)}); + this->addPidHistogram(pidDir, kItsTriton, Specs); + this->addPidHistogram(pidDir, kTpcTriton, Specs); + this->addPidHistogram(pidDir, kTofTriton, Specs); + this->addPidHistogram(pidDir, kTpcitsTriton, Specs); + this->addPidHistogram(pidDir, kTpctofTriton, Specs); + this->addPidHistogram(pidDir, kTpcVsItsTriton, Specs); + this->addPidHistogram(pidDir, kTpcVsTofTriton, Specs); } if (mPlotHeliumPid) { - mHistogramRegistry->add(pidDir + getHistNameV2(kItsHelium, HistTable), getHistDesc(kItsHelium, HistTable), getHistType(kItsHelium, HistTable), {Specs.at(kItsHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcHelium, HistTable), getHistDesc(kTpcHelium, HistTable), getHistType(kTpcHelium, HistTable), {Specs.at(kTpcHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTofHelium, HistTable), getHistDesc(kTofHelium, HistTable), getHistType(kTofHelium, HistTable), {Specs.at(kTofHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpcitsHelium, HistTable), getHistDesc(kTpcitsHelium, HistTable), getHistType(kTpcitsHelium, HistTable), {Specs.at(kTpcitsHelium)}); - mHistogramRegistry->add(pidDir + getHistNameV2(kTpctofHelium, HistTable), getHistDesc(kTpctofHelium, HistTable), getHistType(kTpctofHelium, HistTable), {Specs.at(kTpctofHelium)}); + this->addPidHistogram(pidDir, kItsHelium, Specs); + this->addPidHistogram(pidDir, kTpcHelium, Specs); + this->addPidHistogram(pidDir, kTofHelium, Specs); + this->addPidHistogram(pidDir, kTpcitsHelium, Specs); + this->addPidHistogram(pidDir, kTpctofHelium, Specs); + this->addPidHistogram(pidDir, kTpcVsItsHelium, Specs); + this->addPidHistogram(pidDir, kTpcVsTofHelium, Specs); } } + /// add a PID histogram, replacing the x-axis title of the table description + /// with the one matching the configured momentum type + void addPidHistogram(std::string const& pidDir, TrackHist hist, std::map> const& Specs) + { + std::string desc = histmanager::replaceFirstAxisTitle(getHistDesc(hist, HistTable), modes::momentumTypeToAxisTitle(mMomentumType)); + mHistogramRegistry->add(pidDir + getHistNameV2(hist, HistTable), desc.c_str(), getHistType(hist, HistTable), {Specs.at(hist)}); + } + void initMc(std::map> const& Specs) { std::string mcDir = std::string(prefix) + std::string(McDir); @@ -772,25 +1046,6 @@ class TrackHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {Specs.at(kPdgPartonicMother)}); - - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {Specs.at(kMissidentified)}); - - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {Specs.at(kSecondary3)}); - } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {Specs.at(kSecondaryOther)}); - } } template @@ -807,6 +1062,55 @@ class TrackHistManager } } + // purity (packed nSigma tables) and fraction (DCA) -- fills whichever of the two is actually + // joined on T, independently of the other; mPuritySlotMask picks the species slot (one bit set, + // e.g. from cutculator reading hPuritySlotAssignment), everything is filled unconditionally + // once that slot is decoded -- no momentum-regime selection, the analyst picks the right + // combination/range downstream. Purity itself is skipped in QA mode, mirroring initFitVars. + template + void fillFitVars(T const& track) + { + if constexpr (!isFlagSet(mode, modes::Mode::kQa) && requires(T t) { t.itsNSigmaSlot0(); }) { + float itsNSigma = 0.f; + float tpcNSigma = 0.f; + float tofNSigma = 0.f; + switch (mPuritySlotMask) { + case 1: + itsNSigma = track.itsNSigmaSlot0(); + tpcNSigma = track.tpcNSigmaSlot0(); + tofNSigma = track.tofNSigmaSlot0(); + break; + case 2: + itsNSigma = track.itsNSigmaSlot1(); + tpcNSigma = track.tpcNSigmaSlot1(); + tofNSigma = track.tofNSigmaSlot1(); + break; + case 4: + itsNSigma = track.itsNSigmaSlot2(); + tpcNSigma = track.tpcNSigmaSlot2(); + tofNSigma = track.tofNSigmaSlot2(); + break; + case 8: + itsNSigma = track.itsNSigmaSlot3(); + tpcNSigma = track.tpcNSigmaSlot3(); + tofNSigma = track.tofNSigmaSlot3(); + break; + default: + LOG(fatal) << "puritySlotMask must have exactly one bit set (1, 2, 4 or 8), got " << mPuritySlotMask << ". Breaking..."; + } + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kItsPurity, HistTable)), mAbsCharge * track.pt(), itsNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpcPurity, HistTable)), mAbsCharge * track.pt(), tpcNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpcitsPurity, HistTable)), mAbsCharge * track.pt(), std::hypot(tpcNSigma, itsNSigma)); + if (track.purityHasTof()) { + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTofPurity, HistTable)), mAbsCharge * track.pt(), tofNSigma); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kTpctofPurity, HistTable)), mAbsCharge * track.pt(), std::hypot(tpcNSigma, tofNSigma)); + } + } + if constexpr (requires(T t) { t.dcaXY(); }) { + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionDca, HistTable)), mAbsCharge * track.pt(), track.dcaXY(), track.dcaZ()); + } + } + template void fillQa(T const& track) { @@ -819,6 +1123,9 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterOverCrossedRows, HistTable)), static_cast(track.tpcNClsFound()) / static_cast(track.tpcNClsCrossedRows())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterShared, HistTable)), static_cast(track.tpcNClsShared())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterFractionShared, HistTable)), track.tpcSharedOverFound()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kItsChi2, HistTable)), track.itsChi2NCl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcChi2, HistTable)), track.tpcChi2NCl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTrackType, HistTable)), static_cast(track.fillType())); if (mPlot2d) { mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), mAbsCharge * track.pt(), track.eta()); @@ -830,11 +1137,14 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcClusterOverCrossedRows, HistTable)), mAbsCharge * track.pt(), static_cast(track.tpcNClsFound()) / static_cast(track.tpcNClsCrossedRows())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcClusterShared, HistTable)), mAbsCharge * track.pt(), static_cast(track.tpcNClsShared())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcClusterFractionShared, HistTable)), mAbsCharge * track.pt(), static_cast(track.tpcNClsShared()) / static_cast(track.tpcNClsFound())); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsItsChi2, HistTable)), mAbsCharge * track.pt(), track.itsChi2NCl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsTpcChi2, HistTable)), mAbsCharge * track.pt(), track.tpcChi2NCl()); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterVsTpcCrossedRows, HistTable)), static_cast(track.tpcNClsFound()), static_cast(track.tpcNClsCrossedRows())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kTpcClusterVsTpcClusterShared, HistTable)), static_cast(track.tpcNClsFound()), static_cast(track.tpcNClsShared())); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsDcaxy, HistTable)), mAbsCharge * track.pt(), track.dcaXY()); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsDcaz, HistTable)), mAbsCharge * track.pt(), track.dcaZ()); mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPtVsDca, HistTable)), mAbsCharge * track.pt(), track.dca()); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kPVsPTpc, HistTable)), track.p(), track.tpcInnerParam()); } float momentum = 0.f; @@ -860,6 +1170,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofElectron, HistTable)), momentum, track.tofNSigmaEl()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsElectron, HistTable)), momentum, track.tpcitsNSigmaEl()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofElectron, HistTable)), momentum, track.tpctofNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsElectron, HistTable)), momentum, track.tpcNSigmaEl(), track.itsNSigmaEl()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofElectron, HistTable)), momentum, track.tpcNSigmaEl(), track.tofNSigmaEl()); } if (mPlotPionPid) { @@ -868,6 +1180,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofPion, HistTable)), momentum, track.tofNSigmaPi()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsPion, HistTable)), momentum, track.tpcitsNSigmaPi()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofPion, HistTable)), momentum, track.tpctofNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsPion, HistTable)), momentum, track.tpcNSigmaPi(), track.itsNSigmaPi()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofPion, HistTable)), momentum, track.tpcNSigmaPi(), track.tofNSigmaPi()); } if (mPlotKaonPid) { @@ -876,6 +1190,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofKaon, HistTable)), momentum, track.tofNSigmaKa()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsKaon, HistTable)), momentum, track.tpcitsNSigmaKa()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofKaon, HistTable)), momentum, track.tpctofNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsKaon, HistTable)), momentum, track.tpcNSigmaKa(), track.itsNSigmaKa()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofKaon, HistTable)), momentum, track.tpcNSigmaKa(), track.tofNSigmaKa()); } if (mPlotProtonPid) { @@ -884,6 +1200,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofProton, HistTable)), momentum, track.tofNSigmaPr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsProton, HistTable)), momentum, track.tpcitsNSigmaPr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofProton, HistTable)), momentum, track.tpctofNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsProton, HistTable)), momentum, track.tpcNSigmaPr(), track.itsNSigmaPr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofProton, HistTable)), momentum, track.tpcNSigmaPr(), track.tofNSigmaPr()); } if (mPlotDeuteronPid) { @@ -892,6 +1210,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofDeuteron, HistTable)), momentum, track.tofNSigmaDe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsDeuteron, HistTable)), momentum, track.tpcitsNSigmaDe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofDeuteron, HistTable)), momentum, track.tpctofNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsDeuteron, HistTable)), momentum, track.tpcNSigmaDe(), track.itsNSigmaDe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofDeuteron, HistTable)), momentum, track.tpcNSigmaDe(), track.tofNSigmaDe()); } if (mPlotTritonPid) { @@ -900,6 +1220,8 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofTriton, HistTable)), momentum, track.tofNSigmaTr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsTriton, HistTable)), momentum, track.tpcitsNSigmaTr()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofTriton, HistTable)), momentum, track.tpctofNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsTriton, HistTable)), momentum, track.tpcNSigmaTr(), track.itsNSigmaTr()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofTriton, HistTable)), momentum, track.tpcNSigmaTr(), track.tofNSigmaTr()); } if (mPlotHeliumPid) { @@ -908,26 +1230,32 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTofHelium, HistTable)), momentum, track.tofNSigmaHe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcitsHelium, HistTable)), momentum, track.tpcitsNSigmaHe()); mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpctofHelium, HistTable)), momentum, track.tpctofNSigmaHe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsItsHelium, HistTable)), momentum, track.tpcNSigmaHe(), track.itsNSigmaHe()); + mHistogramRegistry->fill(HIST(prefix) + HIST(PidDir) + HIST(getHistName(kTpcVsTofHelium, HistTable)), momentum, track.tpcNSigmaHe(), track.tofNSigmaHe()); } } - template - void fillMc(T1 const& track, T2 const& /*mcParticles*/, T3 const& /*mcMothers*/, T4 const& /*mcPartonicMothers*/) + template + void fillMc(T1 const& track, T2 const& col, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, T5 const& /*mcPartonicMothers*/) { // No MC Particle if (!track.has_fMcParticle()) { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } } return; } // Retrieve MC particle - auto mcParticle = track.template fMcParticle_as(); + auto mcParticle = track.template fMcParticle_as(); + + // particles associcated to wrong collision + // whether a particle is associated to a wrong collision or not cannot be known by the producer so we check it here + bool fromWrongCollision = mcParticle.fMcColId() != col.fMcColId(); // missidentifed particles are special case // whether a particle is missidentfied or not cannot be known by the producer so we check it here @@ -936,65 +1264,66 @@ class TrackHistManager mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePtVsPt, HistTable)), mcParticle.pt(), track.pt()); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueEtaVsEta, HistTable)), mcParticle.eta(), track.eta()); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePhiVsPhi, HistTable)), mcParticle.phi(), track.phi()); - if (isMissidentified) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kMissidentified)); + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kFromWrongCollision)); + } else if (isMissidentified) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kMissidentified)); } else { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); } mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), mcParticle.pdgCode()); // get mother - if (track.has_fMcMother()) { - auto mother = track.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), mother.pdgCode()); } else { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), 0); } // get partonic mother - if (track.has_fMcPartMoth()) { - auto partonicMother = track.template fMcPartMoth_as(); + if (mcParticle.has_fMcPartMoth()) { + auto partonicMother = mcParticle.template fMcPartMoth_as(); mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), partonicMother.pdgCode()); } else { mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - // check first if particle is missidentified - if (isMissidentified) { + // check first if particle is from a wrong collision + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); - break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); break; case modes::McOrigin::kFromSecondaryDecay: - if (track.has_fMcMother()) { - auto mother = track.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } else { - mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); + mHistogramRegistry->fill(HIST(prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), track.pt(), track.dcaXY(), track.dcaZ()); } } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } @@ -1005,6 +1334,7 @@ class TrackHistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mAbsCharge = 1; int mPdgCode = 0; + int mPuritySlotMask = 1; bool mPlot2d = false; bool mPlotElectronPid = false; bool mPlotPionPid = false; @@ -1013,12 +1343,13 @@ class TrackHistManager bool mPlotDeuteronPid = false; bool mPlotTritonPid = false; bool mPlotHeliumPid = false; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; modes::MomentumType mMomentumType = modes::MomentumType::kPAtPv; }; -}; // namespace trackhistmanager -// aespace trackhistmanager -}; // namespace o2::analysis::femto +} // namespace o2::analysis::femto::trackhistmanager #endif // PWGCF_FEMTO_CORE_TRACKHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..dacdde4f272 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h @@ -0,0 +1,235 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trackTrackTwoPhotonResonanceBuilder.h +/// \brief builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include + +namespace o2::analysis::femto::trackstwophotonresonancebuilder +{ +// filters applied in the producer task (loose pre-selection, applied before the mass window itself) +struct ConfOmegaMesonFilters : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonFilters"); + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; +}; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window +// and daughter mask. both charged daughters are pions (just opposite charge, no species swap the +// way kstar's kaon/pion does), so -- same as TWOTRACKRESONANCE_SAME_SPECIES_MASK for phi/rho0 -- +// a single pair of mask thresholds is applied to both. plus a 3rd mask for the neutral daughter (no +// momentum-threshold split there, same as femtotwophotonresonances' single dauMask) +template +struct ConfOmegaMesonSelection : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", o2::constants::physics::Pdg::kOmega, "Absolute PDG code of the resonance (pdg[omega(782)]=223)"}; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.6f, "Minimum invariant mass"}; + o2::framework::Configurable massMax{"massMax", 0.95f, "Maximum invariant mass"}; + o2::framework::Configurable pionDauMaskBelowThres{"pionDauMaskBelowThres", 0x10u, "Bitmask for both pion daughters below threshold"}; + o2::framework::Configurable pionDauMaskAboveThres{"pionDauMaskAboveThres", 0x8u, "Bitmask for both pion daughters above threshold"}; + o2::framework::Configurable neutralDauMask{"neutralDauMask", 0, "Bitmask required for the neutral (two-photon-resonance) daughter"}; +}; + +constexpr const char PrefixOmegaMesonSelection1[] = "OmegaMesonSelection1"; +using ConfOmegaMesonSelection1 = ConfOmegaMesonSelection; + +struct TrackTrackTwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedOmegaMesons; + o2::framework::Produces producedOmegaMesonMasks; +}; + +struct ConfTrackTrackTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TrackTrackTwoPhotonResonanceTables"); + o2::framework::Configurable produceOmegaMesons{"produceOmegaMesons", -1, "Produce OmegaMesons (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceOmegaMesonMasks{"produceOmegaMesonMasks", -1, "Produce OmegaMesonMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0). +/// +/// posDau/negDau are drawn from already-selected FTracks partitions (sign and PID handled by the +/// normal track selection, same as any other track daughter use case), and neutralDau from an +/// already-built FPi0s/FEtas partition. This builder does the 3-body combinatorics, the +/// resonance's own mass window, and -- like TwoTrackResonanceBuilder -- copies the daughters' +/// bitmasks onto the candidate so systematic variations can be reselected downstream from the +/// candidate table alone, without rejoining the daughter tables. +template +class TrackTrackTwoPhotonResonanceBuilder +{ + public: + TrackTrackTwoPhotonResonanceBuilder() = default; + ~TrackTrackTwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confPosDauSelection, T3& confNegDauSelection, T4& confTable, T5& initContext) + { + mMassMin = confFilter.massMin.value; + mMassMax = confFilter.massMax.value; + mPtMin = confFilter.ptMin.value; + mPtMax = confFilter.ptMax.value; + mEtaMin = confFilter.etaMin.value; + mEtaMax = confFilter.etaMax.value; + mPhiMin = confFilter.phiMin.value; + mPhiMax = confFilter.phiMax.value; + + mPosDauThreshold = confPosDauSelection.pidThres.value; + mNegDauThreshold = confNegDauSelection.pidThres.value; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + LOG(info) << "Initialize femto OmegaMeson builder..."; + mPosDauMass = o2::constants::physics::MassPiPlus; + mNegDauMass = o2::constants::physics::MassPiMinus; + mProduceOmegaMesons = utils::enableTable("FOmegaMesons_001", confTable.produceOmegaMesons.value, initContext); + mProduceOmegaMesonMasks = utils::enableTable("FOmegaMesonMasks_001", confTable.produceOmegaMesonMasks.value, initContext); + } + + if (mProduceOmegaMesons || mProduceOmegaMesonMasks) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1 const& col, T2& resonanceProducts, T3& posDauPartition, T4& negDauPartition, T5& neutralDauPartition, T6& cache) + { + if (!mFillAnyTable) { + return; + } + auto posDauSlice = posDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto negDauSlice = negDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto neutralDauSlice = neutralDauPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [posDau, negDau, neutralDau] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDauSlice, negDauSlice, neutralDauSlice))) { + this->fillResonance(col, posDau, negDau, neutralDau, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& posDau, T2 const& negDau, T3 const& neutralDau) + { + ROOT::Math::PtEtaPhiMVector vecPosDau{posDau.pt(), posDau.eta(), posDau.phi(), mPosDauMass}; + ROOT::Math::PtEtaPhiMVector vecNegDau{negDau.pt(), negDau.eta(), negDau.phi(), mNegDauMass}; + ROOT::Math::PtEtaPhiMVector vecNeutralDau{neutralDau.pt(), neutralDau.eta(), neutralDau.phi(), neutralDau.mass()}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecPosDau + vecNegDau + vecNeutralDau; + + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + mMass = vecResonance.M(); + } + + [[nodiscard]] bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi >= mPhiMin && mPhi < mPhiMax)); + } + + template + void fillResonance(T1 const& col, T2 const& posDau, T3 const& negDau, T4 const& neutralDau, T5& resonanceProducts) + { + reconstructResonance(posDau, negDau, neutralDau); + if (!checkFilters()) { + return; + } + + bool posDauHasHighMomentum = posDau.p() > mPosDauThreshold; + bool negDauHasHighMomentum = negDau.p() > mNegDauThreshold; + + if constexpr (modes::isEqual(resoType, modes::TrackTrackTwoPhotonResonance::kOmegaMeson)) { + if (mProduceOmegaMesons) { + resonanceProducts.producedOmegaMesons(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + posDau.globalIndex(), + negDau.globalIndex(), + neutralDau.globalIndex()); + } + if (mProduceOmegaMesonMasks) { + resonanceProducts.producedOmegaMesonMasks(posDau.mask(), + posDauHasHighMomentum, + negDau.mask(), + negDauHasHighMomentum, + neutralDau.maskDau()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0.f; + float mEta = 0.f; + float mPhi = 0.f; + float mMass = 0.f; + + float mMassMin = 0.f; + float mMassMax = 1.f; + float mPtMin = 0.f; + float mPtMax = 10.f; + float mEtaMin = -0.9f; + float mEtaMax = 0.9f; + float mPhiMin = 0.f; + float mPhiMax = o2::constants::math::TwoPI; + + float mPosDauMass = 0.f; + float mNegDauMass = 0.f; + + float mPosDauThreshold = 0.f; + float mNegDauThreshold = 0.f; + + bool mFillAnyTable = false; + bool mProduceOmegaMesons = false; + bool mProduceOmegaMesonMasks = false; +}; + +} // namespace o2::analysis::femto::trackstwophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..e50ade02d91 --- /dev/null +++ b/PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h @@ -0,0 +1,228 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trackTrackTwoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two tracks and a two-photon resonance +/// (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::trackstwophotonresonancehistmanager +{ +enum TrackTrackTwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTrackTrackTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; + +struct ConfOmegaMesonBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("OmegaMesonBinning"); + TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING(0.6f, 0.95f) +}; +#undef TRACKTRACKTWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTrackTrackTwoPhotonResonanceHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant mass; m (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}}}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, + +template +auto makeTrackTrackTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTrackTrackTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TRACKTRACKTWOPHOTONRESONANCE_HIST_QA_MAP + +constexpr char PrefixOmegaMeson[] = "OmegaMeson/"; +constexpr char PrefixOmegaMesonNeutralDau[] = "OmegaMesonNeutralDau/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau1[] = "OmegaMesonNeutralDauPhotonDau1Qa/"; +constexpr char PrefixOmegaMesonNeutralDauPhotonDau2[] = "OmegaMesonNeutralDauPhotonDau2Qa/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TrackTrackTwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two tracks and a two-photon resonance (e.g. +/// omega(782) -> pi+ pi- pi0). The charged daughters reuse the regular TrackHistManager; +/// the embedded two-photon-resonance daughter reuses TwoPhotonResonanceHistManager as-is +/// (same reco+QA histograms, including its own photon-leg QA, that the dedicated Pi0/Eta +/// QA task gets) by also threading the FPhotons table through to it. +template +class TrackTrackTwoPhotonResonanceHistManager +{ + public: + TrackTrackTwoPhotonResonanceHistManager() = default; + ~TrackTrackTwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& PosDauSpecs, + std::map> const& NegDauSpecs, + std::map> const& NeutralDauSpecs, + std::map> const& NeutralDauPhotonSpecs, + T1 const& ConfPosDauQaBinning, + T2 const& ConfNegDauQaBinning, + T3 const& ConfNeutralDauPhotonQaBinning) + { + mHistogramRegistry = registry; + + // omega(782) -> pi+ pi- pi0: both charged daughters are pions + mPosDauManager.template init(registry, PosDauSpecs, 1, 1, kPiPlus, ConfPosDauQaBinning); + mNegDauManager.template init(registry, NegDauSpecs, 1, -1, kPiPlus, ConfNegDauQaBinning); + + // the embedded pi0/eta candidate is a fully-fledged femto table with its own mask/extras + // columns, so unlike the plain-track daughters above, it can just follow the outer mode. + mNeutralDauManager.template init(registry, NeutralDauSpecs, NeutralDauPhotonSpecs, ConfNeutralDauPhotonQaBinning); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + initAnalysis(ResoSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + initQa(ResoSpecs); + } + } + + template + void fill(T1 const& resonance, T2 const& tracks, T3 const& neutralDaus, T4 const& photons) + { + auto posDau = tracks.rawIteratorAt(resonance.posDauId() - tracks.offset()); + mPosDauManager.template fill(posDau, tracks); + auto negDau = tracks.rawIteratorAt(resonance.negDauId() - tracks.offset()); + mNegDauManager.template fill(negDau, tracks); + auto neutralDau = neutralDaus.rawIteratorAt(resonance.neutralDauId() - neutralDaus.offset()); + mNeutralDauManager.template fill(neutralDau, photons); + + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + fillAnalysis(resonance); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + fillQa(resonance); + } + } + + private: + void initAnalysis(std::map> const& ResoSpecs) + { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {ResoSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {ResoSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); + } + + void initQa(std::map> const& ResoSpecs) + { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), resonance.pt()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), resonance.mass()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + + template + void fillQa(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + trackhistmanager::TrackHistManager mPosDauManager; + trackhistmanager::TrackHistManager mNegDauManager; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager mNeutralDauManager; +}; +} // namespace o2::analysis::femto::trackstwophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TRACKTRACKTWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/tripletBuilder.h b/PWGCF/Femto/Core/tripletBuilder.h index 909934a1b69..8a39f818c8b 100644 --- a/PWGCF/Femto/Core/tripletBuilder.h +++ b/PWGCF/Femto/Core/tripletBuilder.h @@ -10,16 +10,19 @@ // or submit itself to any jurisdiction. /// \file tripletBuilder.h -/// \brief histogram manager for pair tasks +/// \brief histogram manager for triplet tasks /// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_TRIPLETBUILDER_H_ #define PWGCF_FEMTO_CORE_TRIPLETBUILDER_H_ +#include "PWGCF/Femto/Core/cascadeHistManager.h" #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/trackHistManager.h" #include "PWGCF/Femto/Core/tripletCleaner.h" #include "PWGCF/Femto/Core/tripletHistManager.h" @@ -37,33 +40,34 @@ #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::tripletbuilder { -namespace tripletbuilder -{ - const int64_t nLimitPartitionIdenticalParticles123 = 3; const int64_t nLimitPartitionIdenticalParticles12 = 2; const int64_t nLimitPartitionParticles = 1; -template +template class TripletTrackTrackTrackBuilder { public: TripletTrackTrackTrackBuilder() = default; ~TripletTrackTrackTrackBuilder() = default; - template + typename T14, + typename T15, + typename T16, + typename T17, + typename T18> void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, T2 const& confTrackSelection1, T3 const& confTrackSelection2, T4 const& confTrackSelection3, - T5 const& confCtr, - T6 const& confMixing, - T7 const& confTripletBinning, - T8 const& confTripletCuts, - std::map> const& colHistSpec, - std::map> const& trackHistSpec1, - std::map> const& trackHistSpec2, - std::map> const& trackHistSpec3, - std::map> const& pairHistSpec, - std::map> const& cprHistSpec) + T5 const& confCleaner1, + T6 const& confCleaner2, + T7 const& confCleaner3, + T8 const& confCtr, + T9 const& confMixing, + T10 const& confTripletBinning, + T11 const& confTripletCuts, + std::map> const& colHistSpec, + std::map> const& trackHistSpec1, + std::map> const& trackHistSpec2, + std::map> const& trackHistSpec3, + std::map> const& pairHistSpec, + std::map> const& cprHistSpec, + std::map> const& tripletCleanerHistSpec) { // check if correlate the same tracks or not mTrack1Track2Track3AreSameSpecies = confMixing.particle123AreSameSpecies.value; mTrack1Track2AreSameSpecies = confMixing.particle12AreSameSpecies.value; if (mTrack1Track2Track3AreSameSpecies && mTrack1Track2AreSameSpecies) { - LOG(fatal) << "Option Track 1&2 are identical and Option Track 1&2&3 are identical is activated. Breaking..."; + LOG(fatal) << "Option Track 1&2 are identical and Option Track 1&2&3 are identical are activated. Breaking..."; } - mColHistManager.template init(registry, colHistSpec, confCollisionBinning); - mTripletHistManagerSe.template init(registry, pairHistSpec, confTripletBinning, confTripletCuts); - mTripletHistManagerMe.template init(registry, pairHistSpec, confTripletBinning, confTripletCuts); + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + mTripletHistManagerSe.template init(registry, pairHistSpec, confTripletBinning, confTripletCuts, confMixing); + mTripletHistManagerMe.template init(registry, pairHistSpec, confTripletBinning, confTripletCuts, confMixing); - mTc.template init(confTripletCuts); + mTcSe.template init(registry, tripletCleanerHistSpec, confTripletCuts); + mTcMe.template init(registry, tripletCleanerHistSpec, confTripletCuts); if (mTrack1Track2Track3AreSameSpecies) { // Track1 & Track2 & Track3 are the same particle species - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); + mCleaner1.init(confCleaner1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); @@ -121,8 +136,12 @@ class TripletTrackTrackTrackBuilder mCtrMe.init(registry, cprHistSpec, confCtr, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); } else if (mTrack1Track2AreSameSpecies) { // Track1 & Track2 & are the same particle species and track 3 is something else - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager3.template init(registry, trackHistSpec3, confTrackSelection2); + mCleaner1.init(confCleaner1); + mCleaner3.init(confCleaner3); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe3.template init(registry, trackHistSpec3, confTrackSelection3); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confTrackSelection3.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection3.chargeAbs.value); @@ -133,9 +152,15 @@ class TripletTrackTrackTrackBuilder mCtrMe.init(registry, cprHistSpec, confCtr, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, confTrackSelection3.chargeAbs.value); } else { // all three tracks are different - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mTrackHistManager3.template init(registry, trackHistSpec3, confTrackSelection3); + mCleaner1.init(confCleaner1); + mCleaner2.init(confCleaner2); + mCleaner3.init(confCleaner3); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerSe3.template init(registry, trackHistSpec3, confTrackSelection3); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mTrackHistManagerMe3.template init(registry, trackHistSpec3, confTrackSelection3); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confTrackSelection3.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, confTrackSelection3.chargeAbs.value); @@ -149,6 +174,7 @@ class TripletTrackTrackTrackBuilder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -170,224 +196,245 @@ class TripletTrackTrackTrackBuilder } // data + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2Track3AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles123) { - return; + return false; } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManager1, mTripletHistManagerSe, mCtrSe, mTc, tripletOrder); - } else if (mTrack1Track2AreSameSpecies) { + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, col, mTrackHistManagerSe1, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + } + + if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || trackSlice3.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTc, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTc); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe3, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mTrackHistManagerSe3, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2Track3AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles123) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTripletHistManagerSe, mCtrSe, mTc, tripletOrder); - } else if (mTrack1Track2AreSameSpecies) { + return tripletprocesshelpers::processSameEvent(trackSlice1, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTripletHistManagerSe, mCleaner1, mCtrSe, mTcSe, tripletOrder); + } + if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || trackSlice3.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTc, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col, mcCols); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mTrackHistManager3, mTripletHistManagerSe, mCtrSe, mTc); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe3, mTripletHistManagerSe, mCleaner1, mCleaner3, mCtrSe, mTcSe, tripletOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice3 = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || trackSlice3.size() < nLimitPartitionParticles) { + return false; } + mColHistManager.template fill(col, mcCols); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, trackSlice3, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mTrackHistManagerSe3, mTripletHistManagerSe, mCleaner1, mCleaner2, mCleaner3, mCtrSe, mTcSe); } + // data template void processMixedEvent(T1 const& cols, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache, T7& binsVtxMult, T8& binsVtxCent, T9& binsVtxMultCent) { if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition1, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } } - template - void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8& cache, T9& binsVtxMult, T10& binsVtxCent, T11& binsVtxMultCent) + // mc — NOTE: now takes mcMothers, mcPartonicMothers (required by the fixed tripletprocesshelpers mc overload) + template + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache, T11& binsVtxMult, T12& binsVtxCent, T13& binsVtxMultCent) { if (mTrack1Track2Track3AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition1, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner1, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe1); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } else if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mTrackHistManagerMe3); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mCleaner3, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mTrackHistManagerMe3); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } } private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - trackhistmanager::TrackHistManager mTrackHistManager3; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + trackhistmanager::TrackHistManager mTrackHistManagerSe3; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + trackhistmanager::TrackHistManager mTrackHistManagerMe3; + particlecleaner::ParticleCleaner mCleaner1; + particlecleaner::ParticleCleaner mCleaner2; + particlecleaner::ParticleCleaner mCleaner3; triplethistmanager::TripletHistManager mTripletHistManagerSe; triplethistmanager::TripletHistManager mTripletHistManagerMe; closetripletrejection::CloseTripletRejectionTrackTrackTrack mCtrSe; closetripletrejection::CloseTripletRejectionTrackTrackTrack mCtrMe; - tripletcleaner::TrackTrackTrackTripletCleaner mTc; + tripletcleaner::TrackTrackTrackTripletCleaner mTcSe; + tripletcleaner::TrackTrackTrackTripletCleaner mTcMe; triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; bool mTrack1Track2Track3AreSameSpecies = false; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; }; template + auto& prefixTrack1, + auto& prefixTrack2, + auto& prefixV0, + auto& prefixPosDau, + auto& prefixNegDau, + auto& prefixTrack1Me, + auto& prefixTrack2Me, + auto& prefixV0Me, + auto& prefixPosDauMe, + auto& prefixNegDauMe, + auto& prefixSe, + auto& prefixMe, + auto& prefixCtrSeTrack1Track2, + auto& prefixCtrSeTrack1V0, + auto& prefixCtrSeTrack2V0, + auto& prefixCtrMeTrack1Track2, + auto& prefixCtrMeTrack1V0, + auto& prefixCtrMeTrack2V0> class TripletTrackTrackV0Builder { public: TripletTrackTrackV0Builder() = default; ~TripletTrackTrackV0Builder() = default; - template + typename T16, + typename T17, + typename T18, + typename T19, + typename T20> void init(o2::framework::HistogramRegistry* registry, T1 const& confCollisionBinning, T2 const& confTrackSelection1, T3 const& confTrackSelection2, T4 const& confV0Selection, - T5 const& confCtr, - T6 const& confMixing, - T7 const& confTripletBinning, - T8 const& confTripletCuts, - std::map> const& colHistSpec, - std::map> const& trackHistSpec1, - std::map> const& trackHistSpec2, - std::map> const& v0histSpec, - std::map> const& posDauhistSpec, - std::map> const& negDauhistSpec, - std::map> const& tripletHistSpec, - std::map> const& ctrHistSpec) + T5 const& confCleaner1, + T6 const& confCleaner2, + T7 const& confV0Cleaner, + T8 const& confCtr, + T9 const& confMixing, + T10 const& confTripletBinning, + T11 const& confTripletCuts, + std::map> const& colHistSpec, + std::map> const& trackHistSpec1, + std::map> const& trackHistSpec2, + std::map> const& v0histSpec, + std::map> const& posDauhistSpec, + std::map> const& negDauhistSpec, + std::map> const& tripletHistSpec, + std::map> const& ctrHistSpec, + std::map> const& tripletCleanerHistSpec) { // check if correlate the same tracks or not mTrack1Track2AreSameSpecies = confMixing.particle12AreSameSpecies.value; - mColHistManager.template init(registry, colHistSpec, confCollisionBinning); - mTripletHistManagerSe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts); - mTripletHistManagerMe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts); + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + mTripletHistManagerSe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts, confMixing); + mTripletHistManagerMe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts, confMixing); - mTc.template init(confTripletCuts); + mTcSe.template init(registry, tripletCleanerHistSpec, confTripletCuts); + mTcMe.template init(registry, tripletCleanerHistSpec, confTripletCuts); + + mV0Cleaner.init(confV0Cleaner); if (mTrack1Track2AreSameSpecies) { // Track1 & Track2 & are the same particle species and track 3 is something else - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mV0HistManager.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mCleaner1.init(confCleaner1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mV0HistManagerSe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mV0HistManagerMe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confV0Selection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, 1); @@ -445,9 +506,14 @@ class TripletTrackTrackV0Builder mCtrMe.init(registry, ctrHistSpec, confCtr, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); } else { // all three tracks are different - mTrackHistManager1.template init(registry, trackHistSpec1, confTrackSelection1); - mTrackHistManager2.template init(registry, trackHistSpec2, confTrackSelection2); - mV0HistManager.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mCleaner1.init(confCleaner1); + mCleaner2.init(confCleaner2); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mV0HistManagerSe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mV0HistManagerMe.template init(registry, v0histSpec, confV0Selection, posDauhistSpec, negDauhistSpec); mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confV0Selection.pdgCodeAbs.value); mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, 1); @@ -461,6 +527,7 @@ class TripletTrackTrackV0Builder // setup mixing mMixingPolicy = static_cast(confMixing.policy.value); mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; // setup rng if necessary if (confMixing.seed.value >= 0) { @@ -477,153 +544,705 @@ class TripletTrackTrackV0Builder } // data + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || v0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTc, tripletOrder); - } else { - auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; - } - mColHistManager.template fill(col); - mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTc); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, col, mTrackHistManagerSe1, mV0HistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mV0HistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe); } // mc + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) template - void processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) { tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || v0Slice.size() < nLimitPartitionParticles) { - return; + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); if (mMixIdenticalParticles) { tripletOrder = static_cast(mDist(mRng)); } - tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTc, tripletOrder); + return tripletprocesshelpers::processSameEvent(trackSlice1, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mV0HistManagerSe, mTripletHistManagerSe, mCleaner1, mV0Cleaner, mCtrSe, mTcSe, tripletOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col, mcCols); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mV0HistManagerSe, mTripletHistManagerSe, mCleaner1, mCleaner2, mV0Cleaner, mCtrSe, mTcSe); + } + + // data + template + void processMixedEvent(T1 const& cols, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache, T7& binsVtxMult, T8& binsVtxCent, T9& binsVtxMultCent) + { + if (mTrack1Track2AreSameSpecies) { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; + } + } + } + + // mc — NOTE: now takes mcMothers, mcPartonicMothers (required by the fixed tripletprocesshelpers mc overload) + template + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache, T11& binsVtxMult, T12& binsVtxCent, T13& binsVtxMultCent) + { + if (mTrack1Track2AreSameSpecies) { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner1, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mV0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case static_cast(pairhistmanager::kVtxMult): + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxCent): + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); + break; + case static_cast(pairhistmanager::kVtxMultCent): + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCleaner1, mCleaner2, mV0Cleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mV0HistManagerMe); + break; + default: + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; + } + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + v0histmanager::V0HistManager mV0HistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + v0histmanager::V0HistManager mV0HistManagerMe; + particlecleaner::ParticleCleaner mCleaner1; + particlecleaner::ParticleCleaner mCleaner2; + particlecleaner::ParticleCleaner mV0Cleaner; + triplethistmanager::TripletHistManager mTripletHistManagerSe; + triplethistmanager::TripletHistManager mTripletHistManagerMe; + + closetripletrejection::CloseTripletRejectionTrackTrackV0 mCtrSe; + closetripletrejection::CloseTripletRejectionTrackTrackV0 mCtrMe; + tripletcleaner::TrackTrackV0TripletCleaner mTcSe; + tripletcleaner::TrackTrackV0TripletCleaner mTcMe; + triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; + bool mTrack1Track2AreSameSpecies = false; + int mMixingDepth = 5; + int mEventStride = 1; + bool mMixIdenticalParticles = false; + std::mt19937 mRng; + std::uniform_int_distribution<> mDist; +}; + +template +class TripletTrackTrackCascadeBuilder +{ + public: + TripletTrackTrackCascadeBuilder() = default; + ~TripletTrackTrackCascadeBuilder() = default; + + template + + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confTrackSelection1, + T3 const& confTrackSelection2, + T4 const& confTrackCleaner1, + T5 const& confTrackCleaner2, + T6 const& confCtr, + T7 const& confCascadeSelection, + T8 const& confCascadeCleaner, + T9 const& confCprBachelor, + T10 const& confCprV0Daughter, + T11 const& confMixing, + T12 const& confTripletBinning, + T13 const& confTripletCuts, + std::map> const& colHistSpec, + std::map> const& trackHistSpec1, + std::map> const& trackHistSpec2, + std::map> const& cascadeHistSpec, + std::map> const& bachelorHistSpec, + std::map> const& posDauHistSpec, + std::map> const& negDauHistSpec, + std::map> const& tripletHistSpec, + std::map> const& cprHistSpecBachelor, + std::map> const& cprHistSpecV0Daughter, + std::map> const& ctrHistSpec, + std::map> const& tripletCleanerHistSpec) + { + // check if correlate the same tracks or not + mTrack1Track2AreSameSpecies = confMixing.particle12AreSameSpecies.value; + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + mTripletHistManagerSe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts, confMixing); + mTripletHistManagerMe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts, confMixing); + + mTcSe.template init(registry, tripletCleanerHistSpec, confTripletCuts); + mTcMe.template init(registry, tripletCleanerHistSpec, confTripletCuts); + + mCascadeCleaner.init(confCascadeCleaner); + if (mTrack1Track2AreSameSpecies) { + // Track1 & Track2 & are the same particle species and track 3 is something else + mTrackCleaner1.init(confTrackCleaner1); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); + mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, 1); + mCtrSe.init(registry, ctrHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, confCtr, confCprBachelor, confCprV0Daughter, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); + mTripletHistManagerMe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection1.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); + mTripletHistManagerMe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value, 1); + mCtrMe.init(registry, ctrHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, confCtr, confCprBachelor, confCprV0Daughter, confTrackSelection1.chargeAbs.value, confTrackSelection1.chargeAbs.value); } else { + // all three tracks are different + mTrackCleaner1.init(confTrackCleaner1); + mTrackCleaner2.init(confTrackCleaner2); + mTrackHistManagerSe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerSe2.template init(registry, trackHistSpec2, confTrackSelection2); + mCascadeHistManagerSe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + mTrackHistManagerMe1.template init(registry, trackHistSpec1, confTrackSelection1); + mTrackHistManagerMe2.template init(registry, trackHistSpec2, confTrackSelection2); + mCascadeHistManagerMe.template init(registry, cascadeHistSpec, confCascadeSelection, bachelorHistSpec, posDauHistSpec, negDauHistSpec); + + mTripletHistManagerSe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); + mTripletHistManagerSe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, 1); + mCtrSe.init(registry, ctrHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, confCtr, confCprBachelor, confCprV0Daughter, confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value); + + mTripletHistManagerMe.setMass(confTrackSelection1.pdgCodeAbs.value, confTrackSelection2.pdgCodeAbs.value, confCascadeSelection.pdgCodeAbs.value); + mTripletHistManagerMe.setCharge(confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value, 1); + mCtrMe.init(registry, ctrHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, confCtr, confCprBachelor, confCprV0Daughter, confTrackSelection1.chargeAbs.value, confTrackSelection2.chargeAbs.value); + } + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + // setup rng if necessary + if (confMixing.seed.value >= 0) { + uint64_t randomSeed = 0; + mMixIdenticalParticles = true; + if (confMixing.seed.value == 0) { + randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + } else { + randomSeed = static_cast(confMixing.seed.value); + } + mRng = std::mt19937(randomSeed); + mDist = std::uniform_int_distribution<>(tripletprocesshelpers::kOrder123, tripletprocesshelpers::kOrder213); + } + } + + // data + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) + template + bool processSameEvent(T1 const& col, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache) + { + tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; + if (mTrack1Track2AreSameSpecies) { auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - auto v0Slice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || v0Slice.size() < nLimitPartitionParticles) { - return; + auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || cascadeSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCtrSe.setMagField(col.magField()); + if (mMixIdenticalParticles) { + tripletOrder = static_cast(mDist(mRng)); + } + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, col, mTrackHistManagerSe1, mCascadeHistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe, tripletOrder); + } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, col, mTrackHistManagerSe1, mTrackHistManagerSe2, mCascadeHistManagerSe, mTripletHistManagerSe, mCtrSe, mTcSe); + } + + // mc + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) + template + bool processSameEvent(T1 const& col, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache) + { + tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; + if (mTrack1Track2AreSameSpecies) { + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionIdenticalParticles12 || cascadeSlice.size() < nLimitPartitionParticles) { + return false; } mColHistManager.template fill(col, mcCols); mCtrSe.setMagField(col.magField()); - tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, v0Slice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManager1, mTrackHistManager2, mV0HistManager, mTripletHistManagerSe, mCtrSe, mTc); + if (mMixIdenticalParticles) { + tripletOrder = static_cast(mDist(mRng)); + } + return tripletprocesshelpers::processSameEvent(trackSlice1, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mCascadeHistManagerSe, mTripletHistManagerSe, mTrackCleaner1, mCascadeCleaner, mCtrSe, mTcSe, tripletOrder); } + + auto trackSlice1 = partition1->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto trackSlice2 = partition2->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto cascadeSlice = partition3->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice1.size() < nLimitPartitionParticles || trackSlice2.size() < nLimitPartitionParticles || cascadeSlice.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(col, mcCols); + mCtrSe.setMagField(col.magField()); + return tripletprocesshelpers::processSameEvent(trackSlice1, trackSlice2, cascadeSlice, trackTable, mcParticles, mcMothers, mcPartonicMothers, col, mcCols, mTrackHistManagerSe1, mTrackHistManagerSe2, mCascadeHistManagerSe, mTripletHistManagerSe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrSe, mTcSe); } + // data template void processMixedEvent(T1 const& cols, T2& trackTable, T3& partition1, T4& partition2, T5& partition3, T6& cache, T7& binsVtxMult, T8& binsVtxCent, T9& binsVtxMultCent) { if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition1, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, partition1, partition2, partition3, trackTable, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } } - template - void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8& cache, T9& binsVtxMult, T10& binsVtxCent, T11& binsVtxMultCent) + // mc — NOTE: now takes mcMothers, mcPartonicMothers (required by the fixed tripletprocesshelpers mc overload) + template + void processMixedEvent(T1 const& cols, T2 const& mcCols, T3& trackTable, T4& partition1, T5& partition2, T6& partition3, T7 const& mcParticles, T8 const& mcMothers, T9 const& mcPartonicMothers, T10& cache, T11& binsVtxMult, T12& binsVtxCent, T13& binsVtxMultCent) { if (mTrack1Track2AreSameSpecies) { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition1, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner1, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe1, mCascadeHistManagerMe); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } else { switch (mMixingPolicy) { case static_cast(pairhistmanager::kVtxMult): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; case static_cast(pairhistmanager::kVtxMultCent): - tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mCtrMe, mTc); + tripletprocesshelpers::processMixedEvent(cols, mcCols, partition1, partition2, partition3, trackTable, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mTrackCleaner1, mTrackCleaner2, mCascadeCleaner, mCtrMe, mTcMe, mEventStride, mTrackHistManagerMe1, mTrackHistManagerMe2, mCascadeHistManagerMe); break; default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + LOG(fatal) << "Invalid binning policy specifed. Breaking..."; } } } private: colhistmanager::CollisionHistManager mColHistManager; - trackhistmanager::TrackHistManager mTrackHistManager1; - trackhistmanager::TrackHistManager mTrackHistManager2; - v0histmanager::V0HistManager mV0HistManager; - triplethistmanager::TripletHistManager mTripletHistManagerSe; - triplethistmanager::TripletHistManager mTripletHistManagerMe; + trackhistmanager::TrackHistManager mTrackHistManagerSe1; + trackhistmanager::TrackHistManager mTrackHistManagerSe2; + cascadehistmanager::CascadeHistManager mCascadeHistManagerSe; + trackhistmanager::TrackHistManager mTrackHistManagerMe1; + trackhistmanager::TrackHistManager mTrackHistManagerMe2; + cascadehistmanager::CascadeHistManager mCascadeHistManagerMe; + particlecleaner::ParticleCleaner mTrackCleaner1; + particlecleaner::ParticleCleaner mTrackCleaner2; + particlecleaner::ParticleCleaner mCascadeCleaner; + triplethistmanager::TripletHistManager mTripletHistManagerSe; + triplethistmanager::TripletHistManager mTripletHistManagerMe; - closetripletrejection::CloseTripletRejectionTrackTrackV0 mCtrSe; - closetripletrejection::CloseTripletRejectionTrackTrackV0 mCtrMe; - tripletcleaner::TrackTrackV0TripletCleaner mTc; + closetripletrejection::CloseTripletRejectionTrackTrackCascade mCtrSe; + closetripletrejection::CloseTripletRejectionTrackTrackCascade mCtrMe; + tripletcleaner::TrackTrackCascadeTripletCleaner mTcSe; + tripletcleaner::TrackTrackCascadeTripletCleaner mTcMe; triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; bool mTrack1Track2AreSameSpecies = false; int mMixingDepth = 5; + int mEventStride = 1; + bool mMixIdenticalParticles = false; + std::mt19937 mRng; + std::uniform_int_distribution<> mDist; +}; + +// builder for triplets of generated particles (mc truth only, kMc without kReco) +// covers all triplet types, since e.g. a lambda is just another mc particle +template +class TripletMcParticleMcParticleMcParticleBuilder +{ + public: + TripletMcParticleMcParticleMcParticleBuilder() = default; + ~TripletMcParticleMcParticleMcParticleBuilder() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + T1 const& confCollisionBinning, + T2 const& confMcParticleSelection1, + T3 const& confMcParticleSelection2, + T4 const& confMcParticleSelection3, + T5 const& confMcParticleBinning1, + T6 const& confMcParticleBinning2, + T7 const& confMcParticleBinning3, + T8 const& confMcParticleCleaner1, + T9 const& confMcParticleCleaner2, + T10 const& confMcParticleCleaner3, + T11 const& confCtr, + T12 const& confMixing, + T13 const& confTripletBinning, + T14 const& confTripletCuts, + std::map> const& colHistSpec, + std::map> const& mcParticleHistSpec1, + std::map> const& mcParticleHistSpec2, + std::map> const& mcParticleHistSpec3, + std::map> const& tripletHistSpec, + std::map> const& ctrHistSpec, + std::map> const& tripletCleanerHistSpec) + { + mParticle1Particle2Particle3AreSameSpecies = confMixing.particle123AreSameSpecies.value; + mParticle1Particle2AreSameSpecies = confMixing.particle12AreSameSpecies.value; + if (mParticle1Particle2Particle3AreSameSpecies && mParticle1Particle2AreSameSpecies) { + LOG(fatal) << "Option Particle 1&2 are identical and Option Particle 1&2&3 are identical are activated. Breaking..."; + } + + mColHistManager.template init(registry, colHistSpec, confCollisionBinning); + mTripletHistManagerSe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts, confMixing); + mTripletHistManagerMe.template init(registry, tripletHistSpec, confTripletBinning, confTripletCuts, confMixing); + + mTcSe.template init(registry, tripletCleanerHistSpec, confTripletCuts); + mTcMe.template init(registry, tripletCleanerHistSpec, confTripletCuts); + + mCtrSe.init(registry, ctrHistSpec, confCtr); + mCtrMe.init(registry, ctrHistSpec, confCtr); + + // generated particles carry the true pt, so the charge is always 1 here + if (mParticle1Particle2Particle3AreSameSpecies) { + mMcParticleCleaner1.init(confMcParticleCleaner1); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mTripletHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value); + mTripletHistManagerMe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value); + } else if (mParticle1Particle2AreSameSpecies) { + mMcParticleCleaner1.init(confMcParticleCleaner1); + mMcParticleCleaner3.init(confMcParticleCleaner3); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerSe3.init(registry, mcParticleHistSpec3, confMcParticleBinning3); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe3.init(registry, mcParticleHistSpec3, confMcParticleBinning3); + mTripletHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection3.pdgCodeAbs.value); + mTripletHistManagerMe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection3.pdgCodeAbs.value); + } else { + mMcParticleCleaner1.init(confMcParticleCleaner1); + mMcParticleCleaner2.init(confMcParticleCleaner2); + mMcParticleCleaner3.init(confMcParticleCleaner3); + mMcParticleHistManagerSe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerSe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + mMcParticleHistManagerSe3.init(registry, mcParticleHistSpec3, confMcParticleBinning3); + mMcParticleHistManagerMe1.init(registry, mcParticleHistSpec1, confMcParticleBinning1); + mMcParticleHistManagerMe2.init(registry, mcParticleHistSpec2, confMcParticleBinning2); + mMcParticleHistManagerMe3.init(registry, mcParticleHistSpec3, confMcParticleBinning3); + mTripletHistManagerSe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection2.pdgCodeAbs.value, confMcParticleSelection3.pdgCodeAbs.value); + mTripletHistManagerMe.setMass(confMcParticleSelection1.pdgCodeAbs.value, confMcParticleSelection2.pdgCodeAbs.value, confMcParticleSelection3.pdgCodeAbs.value); + } + mTripletHistManagerSe.setCharge(1, 1, 1); + mTripletHistManagerMe.setCharge(1, 1, 1); + + // setup mixing + mMixingPolicy = static_cast(confMixing.policy.value); + mMixingDepth = confMixing.depth.value; + mEventStride = confMixing.eventStride.value; + + // setup rng if necessary + if (confMixing.seed.value >= 0) { + uint64_t randomSeed = 0; + mMixIdenticalParticles = true; + if (confMixing.seed.value == 0) { + randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + } else { + randomSeed = static_cast(confMixing.seed.value); + } + mRng = std::mt19937(randomSeed); + mDist = std::uniform_int_distribution(static_cast(tripletprocesshelpers::kOrder123), static_cast(tripletprocesshelpers::kOrder321)); + } + } + + /// \return true if at least one triplet passed all triplet selections (usable as triplet trigger) + template + bool processSameEvent(T1 const& mcCol, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& partition3, T8& cache) + { + tripletprocesshelpers::TripletOrder tripletOrder = tripletprocesshelpers::kOrder123; + + if (mParticle1Particle2Particle3AreSameSpecies) { + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + if (mcParticleSlice1.size() < nLimitPartitionIdenticalParticles123) { + return false; + } + mColHistManager.template fill(mcCol); + if (mMixIdenticalParticles) { + tripletOrder = static_cast(mDist(mRng)); + } + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mTripletHistManagerSe, mMcParticleCleaner1, mCtrSe, mTcSe, tripletOrder); + } + + if (mParticle1Particle2AreSameSpecies) { + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice3 = partition3->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + if (mcParticleSlice1.size() < nLimitPartitionIdenticalParticles12 || mcParticleSlice3.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(mcCol); + if (mMixIdenticalParticles) { + tripletOrder = static_cast(mDist(mRng)); + } + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mMcParticleHistManagerSe3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner3, mCtrSe, mTcSe, tripletOrder); + } + + auto mcParticleSlice1 = partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice2 = partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + auto mcParticleSlice3 = partition3->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, mcCol.globalIndex(), cache); + if (mcParticleSlice1.size() < nLimitPartitionParticles || mcParticleSlice2.size() < nLimitPartitionParticles || mcParticleSlice3.size() < nLimitPartitionParticles) { + return false; + } + mColHistManager.template fill(mcCol); + return tripletprocesshelpers::processSameEventMcTruth(mcParticleSlice1, mcParticleSlice2, mcParticleSlice3, mcParticles, mcMothers, mcPartonicMothers, mcCol, mMcParticleHistManagerSe1, mMcParticleHistManagerSe2, mMcParticleHistManagerSe3, mTripletHistManagerSe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrSe, mTcSe); + } + + template + void processMixedEvent(T1 const& mcCols, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers, T5& partition1, T6& partition2, T7& partition3, T8& cache, T9& binsVtxMult, T10& binsVtxCent, T11& binsVtxMultCent) + { + // mMcParticleHistManagerMeN are distinct types (templated on a per-slot prefix), so -- unlike + // the partitions/cleaners above, which share a single concrete type and can be selected via a + // ternary reference -- the histogram managers must be selected via explicit branching instead + if (mParticle1Particle2Particle3AreSameSpecies) { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition1, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner1, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else if (mParticle1Particle2AreSameSpecies) { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition1, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner1, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe1, mMcParticleHistManagerMe3); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } else { + switch (mMixingPolicy) { + case triplethistmanager::MixingPolicy::kVtxMult: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMult, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + case triplethistmanager::MixingPolicy::kVtxMultCent: + tripletprocesshelpers::processMixedEventMcTruth(mcCols, partition1, partition2, partition3, mcParticles, mcMothers, mcPartonicMothers, cache, binsVtxMultCent, mMixingDepth, mTripletHistManagerMe, mMcParticleCleaner1, mMcParticleCleaner2, mMcParticleCleaner3, mCtrMe, mTcMe, mEventStride, mMcParticleHistManagerMe1, mMcParticleHistManagerMe2, mMcParticleHistManagerMe3); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + } + + private: + colhistmanager::CollisionHistManager mColHistManager; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerSe3; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe1; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe2; + mcparticlehistmanager::McParticleHistManager mMcParticleHistManagerMe3; + triplethistmanager::TripletHistManager mTripletHistManagerSe; + triplethistmanager::TripletHistManager mTripletHistManagerMe; + closetripletrejection::CloseTripletRejectionMcParticleMcParticleMcParticle mCtrSe; + closetripletrejection::CloseTripletRejectionMcParticleMcParticleMcParticle mCtrMe; + tripletcleaner::McParticleMcParticleMcParticleTripletCleaner mTcSe; + tripletcleaner::McParticleMcParticleMcParticleTripletCleaner mTcMe; + particlecleaner::ParticleCleaner mMcParticleCleaner1; + particlecleaner::ParticleCleaner mMcParticleCleaner2; + particlecleaner::ParticleCleaner mMcParticleCleaner3; + triplethistmanager::MixingPolicy mMixingPolicy = triplethistmanager::MixingPolicy::kVtxMult; + int mMixingDepth = 5; + int mEventStride = 1; + bool mParticle1Particle2Particle3AreSameSpecies = false; + bool mParticle1Particle2AreSameSpecies = false; bool mMixIdenticalParticles = false; std::mt19937 mRng; std::uniform_int_distribution<> mDist; }; -} // namespace tripletbuilder -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::tripletbuilder #endif // PWGCF_FEMTO_CORE_TRIPLETBUILDER_H_ diff --git a/PWGCF/Femto/Core/tripletCleaner.h b/PWGCF/Femto/Core/tripletCleaner.h index d6f38c78709..1c7f42f4cce 100644 --- a/PWGCF/Femto/Core/tripletCleaner.h +++ b/PWGCF/Femto/Core/tripletCleaner.h @@ -17,89 +17,198 @@ #define PWGCF_FEMTO_CORE_TRIPLETCLEANER_H_ #include "PWGCF/Femto/Core/pairCleaner.h" - -namespace o2::analysis::femto -{ -namespace tripletcleaner +namespace o2::analysis::femto::tripletcleaner { -class TrackTrackTrackTripletCleaner : public paircleaner::BasePairCleaner +constexpr char PrefixTripletCleanerTrackTrackTrackSe[] = "TrackTrackTrackCleaner/SE/"; +constexpr char PrefixTripletCleanerTrackTrackTrackMe[] = "TrackTrackTrackCleaner/ME/"; +constexpr char PrefixTripletCleanerTrackTrackV0Se[] = "TrackTrackV0Cleaner/SE/"; +constexpr char PrefixTripletCleanerTrackTrackV0Me[] = "TrackTrackV0Cleaner/ME/"; +constexpr char PrefixTripletCleanerTrackTrackCascadeSe[] = "TrackTrackCascadeCleaner/SE/"; +constexpr char PrefixTripletCleanerTrackTrackCascadeMe[] = "TrackTrackCascadeCleaner/ME/"; +constexpr char PrefixTripletCleanerMcParticleMcParticleMcParticleSe[] = "McParticleMcParticleMcParticleCleaner/SE/"; +constexpr char PrefixTripletCleanerMcParticleMcParticleMcParticleMe[] = "McParticleMcParticleMcParticleCleaner/ME/"; + +template +class TrackTrackTrackTripletCleaner : public paircleaner::BasePairCleaner { public: TrackTrackTrackTripletCleaner() = default; - ~TrackTrackTrackTripletCleaner() = default; + ~TrackTrackTrackTripletCleaner() override = default; - template - bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& track3, T4 const& /*trackTable*/) const + template + bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& track3, T4 const& /*trackTable*/, T5 const& tripletHistManager) const { - return this->isCleanTrackPair(track1, track2) && - this->isCleanTrackPair(track2, track3) && - this->isCleanTrackPair(track1, track3); + this->fillAll(); + bool isClean = this->isCleanParticlePair(track1, track2) && + this->isCleanParticlePair(track2, track3) && + this->isCleanParticlePair(track1, track3); + if (!isClean) { + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; } - template - bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& track3, T4 const& trackTable, T5 const& partonicMothers) const + template + bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& track3, T4 const& trackTable, T5 const& mcParticles, T6 const& partonicMothers, T7 const& tripletHistManager) const { - if (!this->isCleanTriplet(track1, track2, track3, trackTable)) { + if (!this->isCleanTriplet(track1, track2, track3, trackTable, tripletHistManager)) { return false; } // pair is clean // no check if we require common or non-common ancestry - if (mMixPairsWithCommonAncestor) { - return this->pairHasCommonAncestor(track1, track2, partonicMothers) && - this->pairHasCommonAncestor(track2, track3, partonicMothers) && - this->pairHasCommonAncestor(track1, track3, partonicMothers); + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, track2, mcParticles, partonicMothers) && + this->pairHasCommonAncestor(track2, track3, mcParticles, partonicMothers) && + this->pairHasCommonAncestor(track1, track3, mcParticles, partonicMothers); } - if (mMixPairsWithNonCommonAncestor) { - return this->pairHasNonCommonAncestor(track1, track2, partonicMothers) && - this->pairHasNonCommonAncestor(track2, track3, partonicMothers) && - this->pairHasNonCommonAncestor(track1, track3, partonicMothers); + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, track2, mcParticles, partonicMothers) && + this->pairHasNonCommonAncestor(track2, track3, mcParticles, partonicMothers) && + this->pairHasNonCommonAncestor(track1, track3, mcParticles, partonicMothers); } return true; } }; -class TrackTrackV0TripletCleaner : public paircleaner::BasePairCleaner +template +class TrackTrackV0TripletCleaner : public paircleaner::BasePairCleaner { public: TrackTrackV0TripletCleaner() = default; - ~TrackTrackV0TripletCleaner() = default; + ~TrackTrackV0TripletCleaner() override = default; - template - bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& v0, T4 const& trackTable) const + template + bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& v0, T4 const& trackTable, T5 const& tripletHistManager) const { + this->fillAll(); auto posDaughter = trackTable.rawIteratorAt(v0.posDauId() - trackTable.offset()); auto negDaughter = trackTable.rawIteratorAt(v0.negDauId() - trackTable.offset()); - return this->isCleanTrackPair(track1, track2) && - this->isCleanTrackPair(track1, posDaughter) && - this->isCleanTrackPair(track1, negDaughter) && - this->isCleanTrackPair(track2, posDaughter) && - this->isCleanTrackPair(track2, negDaughter); + bool isClean = this->isCleanParticlePair(track1, track2) && + this->isCleanParticlePair(track1, posDaughter) && + this->isCleanParticlePair(track1, negDaughter) && + this->isCleanParticlePair(track2, posDaughter) && + this->isCleanParticlePair(track2, negDaughter); + if (!isClean) { + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; } + template + bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& v0, T4 const& trackTable, T5 const& mcParticles, T6 const& partonicMothers, T7 const& tripletHistManager) const + { + if (!this->isCleanTriplet(track1, track2, v0, trackTable, tripletHistManager)) { + return false; + } + // pair is clean + // no check if we require common or non-common ancestry + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, track2, mcParticles, partonicMothers) && + this->pairHasCommonAncestor(track1, v0, mcParticles, partonicMothers) && + this->pairHasCommonAncestor(track2, v0, mcParticles, partonicMothers); + } + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, track2, mcParticles, partonicMothers) && + this->pairHasNonCommonAncestor(track1, v0, mcParticles, partonicMothers) && + this->pairHasNonCommonAncestor(track2, v0, mcParticles, partonicMothers); + } + return true; + } +}; + +template +class TrackTrackCascadeTripletCleaner : public paircleaner::BasePairCleaner +{ + public: + TrackTrackCascadeTripletCleaner() = default; + ~TrackTrackCascadeTripletCleaner() override = default; + template - bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& v0, T4 const& trackTable, T5 const& partonicMothers) const + bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& cascade, T4 const& trackTable, T5 const& tripletHistManager) const + { + this->fillAll(); + auto bachelor = trackTable.rawIteratorAt(cascade.bachelorId() - trackTable.offset()); + auto posDaughter = trackTable.rawIteratorAt(cascade.posDauId() - trackTable.offset()); + auto negDaughter = trackTable.rawIteratorAt(cascade.negDauId() - trackTable.offset()); + bool isClean = this->isCleanParticlePair(track1, track2) && + this->isCleanParticlePair(track1, posDaughter) && + this->isCleanParticlePair(track1, negDaughter) && + this->isCleanParticlePair(track1, bachelor) && + this->isCleanParticlePair(track2, posDaughter) && + this->isCleanParticlePair(track2, negDaughter) && + this->isCleanParticlePair(track2, bachelor); + if (!isClean) { + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; + } + + template + bool isCleanTriplet(T1 const& track1, T2 const& track2, T3 const& cascade, T4 const& trackTable, T5 const& mcParticles, T6 const& partonicMothers, T7 const& tripletHistManager) const { - if (!this->isCleanTriplet(track1, track2, v0, trackTable)) { + if (!this->isCleanTriplet(track1, track2, cascade, trackTable, tripletHistManager)) { return false; } // pair is clean // no check if we require common or non-common ancestry - if (mMixPairsWithCommonAncestor) { - return this->pairHasCommonAncestor(track1, track2, partonicMothers) && - this->pairHasCommonAncestor(track1, v0, partonicMothers) && - this->pairHasCommonAncestor(track2, v0, partonicMothers); + if (this->mMixPairsWithCommonAncestor) { + return this->pairHasCommonAncestor(track1, track2, mcParticles, partonicMothers) && + this->pairHasCommonAncestor(track1, cascade, mcParticles, partonicMothers) && + this->pairHasCommonAncestor(track2, cascade, mcParticles, partonicMothers); + } + if (this->mMixPairsWithNonCommonAncestor) { + return this->pairHasNonCommonAncestor(track1, track2, mcParticles, partonicMothers) && + this->pairHasNonCommonAncestor(track1, cascade, mcParticles, partonicMothers) && + this->pairHasNonCommonAncestor(track2, cascade, mcParticles, partonicMothers); + } + return true; + } +}; +// triplet of generated particles; there are no daughters to share, so only the +// (non-)common ancestry requirement can reject a triplet +template +class McParticleMcParticleMcParticleTripletCleaner : public paircleaner::BasePairCleaner +{ + public: + McParticleMcParticleMcParticleTripletCleaner() = default; + ~McParticleMcParticleMcParticleTripletCleaner() override = default; + + template + bool isCleanTriplet(T1 const& particle1, T2 const& particle2, T3 const& particle3, T4 const& tripletHistManager) const + { + this->fillAll(); + bool isClean = this->isCleanParticlePair(particle1, particle2) && + this->isCleanParticlePair(particle2, particle3) && + this->isCleanParticlePair(particle1, particle3); + if (!isClean) { + this->fillBlocked(tripletHistManager.getKinematic(this->mKinematicVariable)); + } + return isClean; + } + + template + bool isCleanTriplet(T1 const& particle1, T2 const& particle2, T3 const& particle3, T4 const& partonicMothers, T5 const& tripletHistManager) const + { + if (!this->isCleanTriplet(particle1, particle2, particle3, tripletHistManager)) { + return false; + } + // triplet is clean + // no check if we require common or non-common ancestry + if (this->mMixPairsWithCommonAncestor) { + return this->mcPairHasCommonAncestor(particle1, particle2, partonicMothers) && + this->mcPairHasCommonAncestor(particle2, particle3, partonicMothers) && + this->mcPairHasCommonAncestor(particle1, particle3, partonicMothers); } - if (mMixPairsWithNonCommonAncestor) { - return this->pairHasNonCommonAncestor(track1, track2, partonicMothers) && - this->pairHasNonCommonAncestor(track1, v0, partonicMothers) && - this->pairHasNonCommonAncestor(track2, v0, partonicMothers); + if (this->mMixPairsWithNonCommonAncestor) { + return this->mcPairHasNonCommonAncestor(particle1, particle2, partonicMothers) && + this->mcPairHasNonCommonAncestor(particle2, particle3, partonicMothers) && + this->mcPairHasNonCommonAncestor(particle1, particle3, partonicMothers); } return true; } }; -} // namespace tripletcleaner -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::tripletcleaner #endif // PWGCF_FEMTO_CORE_TRIPLETCLEANER_H_ diff --git a/PWGCF/Femto/Core/tripletHistManager.h b/PWGCF/Femto/Core/tripletHistManager.h index 6f9f2323f31..b7eadec7642 100644 --- a/PWGCF/Femto/Core/tripletHistManager.h +++ b/PWGCF/Femto/Core/tripletHistManager.h @@ -11,11 +11,12 @@ /// \file tripletHistManager.h /// \brief histogram manager for triplet tasks -/// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de +/// \author Anton Riedel, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ #define PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ +#include "PWGCF/Femto/Core/femtoMath.h" #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/Core/histManager.h" #include "PWGCF/Femto/Core/modes.h" @@ -30,20 +31,23 @@ #include #include +#include #include #include #include +#include #include -namespace o2::analysis::femto -{ -namespace triplethistmanager +namespace o2::analysis::femto::triplethistmanager { // enum for pair histograms enum TripletHist { // standard 1D kQ3, - kMt, // averate mt of all pairs in the triplet + kMt, // average mt of all pairs in the triplet + kKstar12, // kstar between single particles, always computed + kKstar13, + kKstar23, // standard 2D kPt1VsQ3, kPt2VsQ3, @@ -62,12 +66,32 @@ enum TripletHist { kQ3VsMtVsMultVsCent, kQ3VsMtVsPt1VsPt2VsPt3VsMult, kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, + kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, // higher dimensions with mass // mc kTrueQ3VsQ3, kTrueMtVsMt, kTrueMultVsMult, kTrueCentVsCent, + // mc truth (kMc without kReco), i.e. triplets of generated particles + kTrueQ3, + kTrueMt, + kTrueKstar12, + kTrueKstar13, + kTrueKstar23, + kTruePt1VsTrueQ3, + kTruePt2VsTrueQ3, + kTruePt3VsTrueQ3, + kTrueQ3VsTrueMt, + kTrueQ3VsTrueMult, + kTrueQ3VsTrueCent, + + // mixing qa + kSeNpart1VsNpart2VsNpart3, // unique particles 1,2,3 in each same event + kMeMixingWindowRaw, // mixing window size + kMeMixingWindowEffective, // mixing window size, counting event triplets with particle triplets + kMeNpart1VsNpart2VsNpart3, // unique particles 1,2,3 in each mixed event + kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3, // correlation of event properties in each mixing bin (super heavy! use with caution) kTripletHistogramLast }; @@ -87,9 +111,13 @@ struct ConfMixing : o2::framework::ConfigurableGroup { o2::framework::ConfigurableAxis vtxBins{"vtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; o2::framework::Configurable depth{"depth", 5, "Number of events for mixing"}; o2::framework::Configurable policy{"policy", 0, "Binning policy for mixing (alywas in combination with z-vertex) -> 0: multiplicity, -> 1: centrality, -> 2: both"}; + o2::framework::Configurable eventStride{"eventStride", 1, "Only use every Nth collision as a mixing anchor (1 = use every collision). Reduces mixed-event combinatorics roughly 1/eventStride."}; o2::framework::Configurable seed{"seed", -1, "Seed to randomize particle 1/2/3 (if they are identical). Set to negative value to deactivate. Set to 0 to generate unique seed in time."}; o2::framework::Configurable particle123AreSameSpecies{"particle123AreSameSpecies", false, "Particle 1,2 and 3 are of the same species"}; o2::framework::Configurable particle12AreSameSpecies{"particle12AreSameSpecies", false, "Particle 1 and 2 are of the same species"}; + o2::framework::Configurable enablePairCorrelationQa{"enablePairCorrelationQa", true, "Enable triplet-level correlation QA (same-event + mixed-event)"}; + o2::framework::Configurable enableEventMixingQa{"enableEventMixingQa", false, "Enable QA of event properties used in event mixing (vtx, multiplicity, centrality)"}; + o2::framework::ConfigurableAxis particleBinning{"particleBinning", {20, -0.5f, 19.5f}, "Binning for particle number correlation in triplets"}; }; struct ConfTripletBinning : o2::framework::ConfigurableGroup { @@ -101,8 +129,10 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { o2::framework::Configurable plotQ3VsMtVsMult{"plotQ3VsMtVsMult", false, "Enable 3D histogram (Q3 vs Mt vs Mult)"}; o2::framework::Configurable plotQ3VsMtVsMultVsCent{"plotQ3VsMtVsMultVsCent", false, "Enable 4D histogram (Q3 vs Mt vs Mult Vs Cent)"}; o2::framework::Configurable plotQ3VsMtVsPt1VsPt2VsPt3VsMult{"plotQ3VsMtVsPt1VsPt2VsPt3VsMult", false, "Enable 6D histogram (Q3 vs Mt Vs Pt1 vs Pt2 vs Pt3 vs Mult)"}; - o2::framework::Configurable plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent{"plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", false, "Enable 3D histogram (Q3 vs Mt Vs Pt1 vs Pt2 vs Pt3 vs Mult vs Cent)"}; + o2::framework::Configurable plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent{"plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", false, "Enable 7D histogram (Q3 vs Mt Vs Pt1 vs Pt2 vs Pt3 vs Mult vs Cent)"}; + o2::framework::Configurable plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3{"plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3", false, "Enable 7D histogram (Q3 vs kstar12 vs kstar13 vs kstar23 vs Pt1 vs Pt2 vs Pt3)"}; o2::framework::ConfigurableAxis q3{"q3", {{600, 0, 6}}, "q3"}; + o2::framework::ConfigurableAxis kstar{"kstar", {{600, 0, 6}}, "kstar (between pairs of particles)"}; o2::framework::ConfigurableAxis mt{"mt", {{500, 0.8, 5.8}}, "mt"}; o2::framework::ConfigurableAxis multiplicity{"multiplicity", {{50, 0, 200}}, "multiplicity"}; o2::framework::ConfigurableAxis centrality{"centrality", {{10, 0, 100}}, "centrality (mult. percentile)"}; @@ -117,12 +147,20 @@ struct ConfTripletBinning : o2::framework::ConfigurableGroup { struct ConfTripletCuts : o2::framework::ConfigurableGroup { std::string prefix = std::string("TripletCuts"); - o2::framework::Configurable q3Max{"q3Max", -1, "Maximal kstar (set to -1 to deactivate)"}; - o2::framework::Configurable q3Min{"q3Min", -1, "Minimal kstar (set to -1 to deactivate)"}; + o2::framework::Configurable q3Max{"q3Max", -1, "Maximal Q3 (set to -1 to deactivate)"}; + o2::framework::Configurable q3Min{"q3Min", -1, "Minimal Q3 (set to -1 to deactivate)"}; o2::framework::Configurable mtMax{"mtMax", -1, "Maximal mt (set to -1 to deactivate)"}; o2::framework::Configurable mtMin{"mtMin", -1, "Minimal mt (set to -1 to deactivate)"}; - o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common anchestor (in the same event)"}; - o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common anchestor (in the same event)"}; + o2::framework::Configurable kstar12Min{"kstar12Min", -1, "Minimal k* between particle 1 and particle 2 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar12Max{"kstar12Max", -1, "Maximal k* between particle 1 and particle 2 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar13Min{"kstar13Min", -1, "Minimal k* between particle 1 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar13Max{"kstar13Max", -1, "Maximal k* between particle 1 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar23Min{"kstar23Min", -1, "Minimal k* between particle 2 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable kstar23Max{"kstar23Max", -1, "Maximal k* between particle 2 and particle 3 (set to -1 to deactivate)"}; + o2::framework::Configurable mixOnlyCommonAncestor{"mixOnlyCommonAncestor", false, "Require pair to have common ancestor (in the same event)"}; + o2::framework::Configurable mixOnlyNonCommonAncestor{"mixOnlyNonCommonAncestor", false, "Require pair to have non-common ancestor (in the same event)"}; + o2::framework::Configurable useMotherAsAncestor{"useMotherAsAncestor", false, "Use the first ancestor (i.e. the direct mother) instead of the partonic mother when requiring (non-)common ancestry"}; + o2::framework::Configurable kinematicVariable{"kinematicVariable", static_cast(modes::KinematicVariable::kQ3), "Type of kinematic variable (2->mT, 3->Q3) reported by the triplet-cleaner kinematic plot"}; }; // the enum gives the correct index in the array @@ -132,66 +170,129 @@ constexpr std::array, kTripletHistogramLast> // 1D {kQ3, o2::framework::HistType::kTH1F, "hQ3", "Q_{3}; Q_{3} (GeV/#it{c}); Entries"}, {kMt, o2::framework::HistType::kTH1F, "hMt", "transverse mass; m_{T} (GeV/#it{c}^{2}); Entries"}, + {kKstar12, o2::framework::HistType::kTH1F, "hKstar12", "k* between particle 1 and particle 2; k* (GeV/#it{c}); Entries"}, + {kKstar13, o2::framework::HistType::kTH1F, "hKstar13", "k* between particle 1 and particle 3; k* (GeV/#it{c}); Entries"}, + {kKstar23, o2::framework::HistType::kTH1F, "hKstar23", "k* between particle 2 and particle 3; k* (GeV/#it{c}); Entries"}, // 2D - {kPt1VsQ3, o2::framework::HistType::kTH2F, "hPt1VsQ3", " p_{T,1} vs Q_{3}; p_{T,1} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, + {kPt1VsQ3, o2::framework::HistType::kTH2F, "hPt1VsQ3", "p_{T,1} vs Q_{3}; p_{T,1} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, {kPt2VsQ3, o2::framework::HistType::kTH2F, "hPt2VsQ3", "p_{T,2} vs Q_{3}; p_{T,2} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, - {kPt3VsQ3, o2::framework::HistType::kTH2F, "hPt3VsQ3", "p_{T,3} vs Q_{3}; p_{T,2} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, + {kPt3VsQ3, o2::framework::HistType::kTH2F, "hPt3VsQ3", "p_{T,3} vs Q_{3}; p_{T,3} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, {kQ3VsMt, o2::framework::HistType::kTH2F, "hQ3VsMt", "Q_{3} vs m_{T}; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2})"}, {kQ3VsMult, o2::framework::HistType::kTH2F, "hQ3VsMult", "Q_{3} vs Multiplicity; Q_{3} (GeV/#it{c}); Multiplicity"}, {kQ3VsCent, o2::framework::HistType::kTH2F, "hQ3VsCent", "Q_{3} vs Centrality; Q_{3} (GeV/#it{c}); Centrality"}, // n-D - {kPt1VsPt2VsPt3, o2::framework::HistType::kTHnSparseF, "hPt1VsPt2VsPt3", "k* vs m_{T} vs multiplicity; k* (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); Multiplicity"}, - {kQ3VsPt1VsPt2VsPt3, o2::framework::HistType::kTHnSparseF, "hQ3VsPt1VsPt2VsPt3", "Q_{3} vs p_{T,1} vs p_{T,2} vs p_{T,3}; Q_{3} (GeV/#it{c}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c})"}, + {kPt1VsPt2VsPt3, o2::framework::HistType::kTHnSparseF, "hPt1VsPt2VsPt3", "p_{T,1} vs p_{T,2} vs p_{T,3}; p_{T,1} (GeV/#it{c});p_{T,2} (GeV/#it{c});p_{T,3} (GeV/#it{c});"}, + {kQ3VsPt1VsPt2VsPt3, o2::framework::HistType::kTHnSparseF, "hQ3VsPt1VsPt2VsPt3", "Q_{3} vs p_{T,1} vs p_{T,2} vs p_{T,3}; Q_{3} (GeV/#it{c}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c});"}, {kQ3VsMtVsMult, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsMult", "Q_{3} vs m_{T} vs multiplicity; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); Multiplicity"}, - {kQ3VsMtVsMultVsCent, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsMultVsCent", "Q_{3} vs m_{T} vs multiplicity vs centrality; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); Multiplicity; Centrality"}, + {kQ3VsMtVsMultVsCent, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsMultVsCent", "Q_{3} vs m_{T} vs multiplicity vs centrality; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); Multiplicity; Centrality (%);"}, // n-D with mass - {kQ3VsMtVsPt1VsPt2VsPt3VsMult, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMult", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}) ; Multiplicity"}, - {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity vs centrality; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}) vs multiplicity vs centrality"}, + {kQ3VsMtVsPt1VsPt2VsPt3VsMult, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMult", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}); Multiplicity;"}, + {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, o2::framework::HistType::kTHnSparseF, "hQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent", "Q_{3} vs m_{T} vs p_{T,1} vs p_{T,2} vs p_{T,3} vs multiplicity vs centrality; Q_{3} (GeV/#it{c}); m_{T} (GeV/#it{c}^{2}); p_{T,1} (GeV/#it{c}) ; p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c}); Multiplicity; Centrality (%);"}, + {kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, o2::framework::HistType::kTHnSparseF, "hQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3", "Q_{3} vs k*_{12} vs k*_{13} vs k*_{23} vs p_{T,1} vs p_{T,2} vs p_{T,3}; Q_{3} (GeV/#it{c}); k*_{12} (GeV/#it{c}); k*_{13} (GeV/#it{c}); k*_{23} (GeV/#it{c}); p_{T,1} (GeV/#it{c}); p_{T,2} (GeV/#it{c}); p_{T,3} (GeV/#it{c});"}, {kTrueQ3VsQ3, o2::framework::HistType::kTH2F, "hTrueQ3VsQ3", "Q_{3,True} vs Q_{3}; Q_{3,true} (GeV/#it{c}); Q_{3} (GeV/#it{c})"}, {kTrueMtVsMt, o2::framework::HistType::kTH2F, "hTrueMtVsMt", "m_{T,True} vs m_{T}; m_{T,True} (GeV/#it{c}^{2}); m_{T} (GeV/#it{c}^{2})"}, {kTrueMultVsMult, o2::framework::HistType::kTH2F, "hTrueMultVsMult", "Multiplicity_{True} vs Multiplicity; Multiplicity_{True} ; Multiplicity"}, {kTrueCentVsCent, o2::framework::HistType::kTH2F, "hTrueCentVsCent", "Centrality_{True} vs Centrality; Centrality_{True} (%); Centrality (%)"}, + // mc truth + {kTrueQ3, o2::framework::HistType::kTH1F, "hTrueQ3", "Q_{3,True}; Q_{3,True} (GeV/#it{c}); Entries"}, + {kTrueMt, o2::framework::HistType::kTH1F, "hTrueMt", "true transverse mass; m_{T,True} (GeV/#it{c}^{2}); Entries"}, + {kTrueKstar12, o2::framework::HistType::kTH1F, "hTrueKstar12", "true k* between particle 1 and particle 2; k*_{True} (GeV/#it{c}); Entries"}, + {kTrueKstar13, o2::framework::HistType::kTH1F, "hTrueKstar13", "true k* between particle 1 and particle 3; k*_{True} (GeV/#it{c}); Entries"}, + {kTrueKstar23, o2::framework::HistType::kTH1F, "hTrueKstar23", "true k* between particle 2 and particle 3; k*_{True} (GeV/#it{c}); Entries"}, + {kTruePt1VsTrueQ3, o2::framework::HistType::kTH2F, "hTruePt1VsTrueQ3", "p_{T,1,True} vs Q_{3,True}; p_{T,1,True} (GeV/#it{c}); Q_{3,True} (GeV/#it{c})"}, + {kTruePt2VsTrueQ3, o2::framework::HistType::kTH2F, "hTruePt2VsTrueQ3", "p_{T,2,True} vs Q_{3,True}; p_{T,2,True} (GeV/#it{c}); Q_{3,True} (GeV/#it{c})"}, + {kTruePt3VsTrueQ3, o2::framework::HistType::kTH2F, "hTruePt3VsTrueQ3", "p_{T,3,True} vs Q_{3,True}; p_{T,3,True} (GeV/#it{c}); Q_{3,True} (GeV/#it{c})"}, + {kTrueQ3VsTrueMt, o2::framework::HistType::kTH2F, "hTrueQ3VsTrueMt", "Q_{3,True} vs m_{T,True}; Q_{3,True} (GeV/#it{c}); m_{T,True} (GeV/#it{c}^{2})"}, + {kTrueQ3VsTrueMult, o2::framework::HistType::kTH2F, "hTrueQ3VsTrueMult", "Q_{3,True} vs Multiplicity_{True}; Q_{3,True} (GeV/#it{c}); Multiplicity_{True}"}, + {kTrueQ3VsTrueCent, o2::framework::HistType::kTH2F, "hTrueQ3VsTrueCent", "Q_{3,True} vs Centrality_{True}; Q_{3,True} (GeV/#it{c}); Centrality_{True} (%)"}, + // mixing qa + {kSeNpart1VsNpart2VsNpart3, o2::framework::HistType::kTHnSparseF, "hSeNpart1VsNpart2VsNpart3", "# unique particle 1 vs # unique particle 2 vs # unique particle 3 in each same event; # particle 1; # particle 2; # particle 3;"}, + {kMeMixingWindowRaw, o2::framework::HistType::kTH1F, "hMeMixingWindowRaw", "Raw Mixing Window; Raw Mixing Window; Entries"}, + {kMeMixingWindowEffective, o2::framework::HistType::kTH1F, "hMeMixingWindowEffective", "Effective Mixing Window; Effective Mixing Window; Entries"}, + {kMeNpart1VsNpart2VsNpart3, o2::framework::HistType::kTHnSparseF, "hMeNpart1VsNpart2VsNpart3", "# unique particle 1 vs # unique particle 2 vs # unique particle 3 in each mixing bin; # particle 1; # particle 2; # particle 3;"}, + {kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3, o2::framework::HistType::kTHnSparseF, "hVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3", "Mixing bins; V_{z,1} (cm); mult_{1}; cent_{1} (%); V_{z,2} (cm); mult_{2}; cent_{2} (%); V_{z,3} (cm); mult_{3}; cent_{3} (%);"}, }}; -#define TRIPLET_HIST_ANALYSIS_MAP(conf) \ - {kQ3, {conf.q3}}, \ - {kMt, {conf.mt}}, \ - {kPt1VsQ3, {conf.pt1, conf.q3}}, \ - {kPt2VsQ3, {conf.pt2, conf.q3}}, \ - {kPt3VsQ3, {conf.pt3, conf.q3}}, \ - {kQ3VsMt, {conf.q3, conf.mt}}, \ - {kQ3VsMult, {conf.q3, conf.multiplicity}}, \ - {kQ3VsCent, {conf.q3, conf.centrality}}, \ - {kPt1VsPt2VsPt3, {conf.pt1, conf.pt2, conf.pt3}}, \ - {kQ3VsPt1VsPt2VsPt3, {conf.q3, conf.pt1, conf.pt2, conf.pt3}}, \ - {kQ3VsMtVsMult, {conf.q3, conf.mt, conf.multiplicity}}, \ - {kQ3VsMtVsMultVsCent, {conf.q3, conf.mt, conf.multiplicity, conf.centrality}}, \ - {kQ3VsMtVsPt1VsPt2VsPt3VsMult, {conf.q3, conf.mt, conf.pt1, conf.pt2, conf.pt3, conf.multiplicity}}, \ - {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, {conf.q3, conf.mt, conf.pt1, conf.pt2, conf.pt3, conf.multiplicity, conf.centrality}}, - -#define TRIPLET_HIST_MC_MAP(conf) \ - {kTrueQ3VsQ3, {conf.q3, conf.q3}}, \ - {kTrueMtVsMt, {conf.mt, conf.mt}}, \ - {kTrueMultVsMult, {conf.multiplicity, conf.multiplicity}}, \ - {kTrueCentVsCent, {conf.centrality, conf.centrality}}, - -template -auto makeTripletHistSpecMap(const T& confPairBinning) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRIPLET_HIST_ANALYSIS_MAP(conf, confMixing) \ + {kQ3, {(conf).q3}}, \ + {kMt, {(conf).mt}}, \ + {kKstar12, {(conf).kstar}}, \ + {kKstar13, {(conf).kstar}}, \ + {kKstar23, {(conf).kstar}}, \ + {kPt1VsQ3, {(conf).pt1, (conf).q3}}, \ + {kPt2VsQ3, {(conf).pt2, (conf).q3}}, \ + {kPt3VsQ3, {(conf).pt3, (conf).q3}}, \ + {kQ3VsMt, {(conf).q3, (conf).mt}}, \ + {kQ3VsMult, {(conf).q3, (conf).multiplicity}}, \ + {kQ3VsCent, {(conf).q3, (conf).centrality}}, \ + {kPt1VsPt2VsPt3, {(conf).pt1, (conf).pt2, (conf).pt3}}, \ + {kQ3VsPt1VsPt2VsPt3, {(conf).q3, (conf).pt1, (conf).pt2, (conf).pt3}}, \ + {kQ3VsMtVsMult, {(conf).q3, (conf).mt, (conf).multiplicity}}, \ + {kQ3VsMtVsMultVsCent, {(conf).q3, (conf).mt, (conf).multiplicity, (conf).centrality}}, \ + {kQ3VsMtVsPt1VsPt2VsPt3VsMult, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity}}, \ + {kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, {(conf).q3, (conf).mt, (conf).pt1, (conf).pt2, (conf).pt3, (conf).multiplicity, (conf).centrality}}, \ + {kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, {(conf).q3, (conf).kstar, (conf).kstar, (conf).kstar, (conf).pt1, (conf).pt2, (conf).pt3}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRIPLET_HIST_MIXING_QA_MAP(confMixing) \ + {kSeNpart1VsNpart2VsNpart3, {(confMixing).particleBinning, (confMixing).particleBinning, (confMixing).particleBinning}}, \ + {kMeMixingWindowRaw, {(confMixing).particleBinning}}, \ + {kMeMixingWindowEffective, {(confMixing).particleBinning}}, \ + {kMeNpart1VsNpart2VsNpart3, {(confMixing).particleBinning, (confMixing).particleBinning, (confMixing).particleBinning}}, \ + {kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3, {(confMixing).vtxBins, (confMixing).multBins, (confMixing).centBins, (confMixing).vtxBins, (confMixing).multBins, (confMixing).centBins, (confMixing).vtxBins, (confMixing).multBins, (confMixing).centBins}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRIPLET_HIST_MC_TRUTH_MAP(conf) \ + {kTrueQ3, {(conf).q3}}, \ + {kTrueMt, {(conf).mt}}, \ + {kTrueKstar12, {(conf).kstar}}, \ + {kTrueKstar13, {(conf).kstar}}, \ + {kTrueKstar23, {(conf).kstar}}, \ + {kTruePt1VsTrueQ3, {(conf).pt1, (conf).q3}}, \ + {kTruePt2VsTrueQ3, {(conf).pt2, (conf).q3}}, \ + {kTruePt3VsTrueQ3, {(conf).pt3, (conf).q3}}, \ + {kTrueQ3VsTrueMt, {(conf).q3, (conf).mt}}, \ + {kTrueQ3VsTrueMult, {(conf).q3, (conf).multiplicity}}, \ + {kTrueQ3VsTrueCent, {(conf).q3, (conf).centrality}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TRIPLET_HIST_MC_MAP(conf) \ + {kTrueQ3VsQ3, {(conf).q3, (conf).q3}}, \ + {kTrueMtVsMt, {(conf).mt, (conf).mt}}, \ + {kTrueMultVsMult, {(conf).multiplicity, (conf).multiplicity}}, \ + {kTrueCentVsCent, {(conf).centrality, (conf).centrality}}, + +template +auto makeTripletHistSpecMap(T1 const& confPairBinning, T2 const& confMixing) +{ + return std::map>{ + TRIPLET_HIST_ANALYSIS_MAP(confPairBinning, confMixing) + TRIPLET_HIST_MIXING_QA_MAP(confMixing)}; +}; + +template +auto makeTripletMcHistSpecMap(T1 const& confPairBinning, T2 const& confMixing) { return std::map>{ - TRIPLET_HIST_ANALYSIS_MAP(confPairBinning)}; + TRIPLET_HIST_ANALYSIS_MAP(confPairBinning, confMixing) + TRIPLET_HIST_MIXING_QA_MAP(confMixing) + TRIPLET_HIST_MC_MAP(confPairBinning)}; }; -template -auto makeTripletMcHistSpecMap(const T& confPairBinning) +/// triplets of generated particles only (kMc without kReco) +template +auto makeTripletMcTruthHistSpecMap(T1 const& confPairBinning, T2 const& confMixing) { return std::map>{ - TRIPLET_HIST_ANALYSIS_MAP(confPairBinning) - TRIPLET_HIST_MC_MAP(confPairBinning)}; + TRIPLET_HIST_MC_TRUTH_MAP(confPairBinning) + TRIPLET_HIST_MIXING_QA_MAP(confMixing)}; }; #undef TRIPLET_HIST_ANALYSIS_MAP +#undef TRIPLET_HIST_MIXING_QA_MAP #undef TRIPLET_HIST_MC_MAP +#undef TRIPLET_HIST_MC_TRUTH_MAP constexpr char PrefixTrackTrackTrackSe[] = "TrackTrackTrack/SE/"; constexpr char PrefixTrackTrackTrackMe[] = "TrackTrackTrack/ME/"; @@ -199,11 +300,17 @@ constexpr char PrefixTrackTrackTrackMe[] = "TrackTrackTrack/ME/"; constexpr char PrefixTrackTrackLambdaSe[] = "TrackTrackLambda/SE/"; constexpr char PrefixTrackTrackLambdaMe[] = "TrackTrackLambda/ME/"; +constexpr char PrefixMcParticleMcParticleMcParticleSe[] = "McParticleMcParticleMcParticle/SE/"; +constexpr char PrefixMcParticleMcParticleMcParticleMe[] = "McParticleMcParticleMcParticle/ME/"; + +constexpr char PrefixTrackTrackCascadeSe[] = "TrackTrackCascade/SE/"; +constexpr char PrefixTrackTrackCascadeMe[] = "TrackTrackCascade/ME/"; + constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view McDir = "MC/"; -template @@ -213,11 +320,12 @@ class TripletHistManager TripletHistManager() = default; ~TripletHistManager() = default; - template + template void init(o2::framework::HistogramRegistry* registry, std::map> const& Specs, T1 const& ConfTripletBinning, - T2 const& ConfTripletCuts) + T2 const& ConfTripletCuts, + T3 const& ConfMixing) { mHistogramRegistry = registry; @@ -233,6 +341,12 @@ class TripletHistManager mQ3Max = ConfTripletCuts.q3Max.value; mMtMin = ConfTripletCuts.mtMin.value; mMtMax = ConfTripletCuts.mtMax.value; + mKstar12Min = ConfTripletCuts.kstar12Min.value; + mKstar12Max = ConfTripletCuts.kstar12Max.value; + mKstar13Min = ConfTripletCuts.kstar13Min.value; + mKstar13Max = ConfTripletCuts.kstar13Max.value; + mKstar23Min = ConfTripletCuts.kstar23Min.value; + mKstar23Max = ConfTripletCuts.kstar23Max.value; mPlotPt1VsPt2VsPt3 = ConfTripletBinning.plotPt1VsPt2VsPt3.value; mPlotQ3VsPt1VsPt2VsPt3 = ConfTripletBinning.plotQ3VsPt1VsPt2VsPt3.value; @@ -240,14 +354,27 @@ class TripletHistManager mPlotQ3VsMtVsMultVsCent = ConfTripletBinning.plotQ3VsMtVsMultVsCent.value; mPlotQ3VsMtVsPt1VsPt2VsPt3VsMult = ConfTripletBinning.plotQ3VsMtVsPt1VsPt2VsPt3VsMult.value; mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent = ConfTripletBinning.plotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent.value; + mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 = ConfTripletBinning.plotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3.value; + + mPairCorrelationQa = ConfMixing.enablePairCorrelationQa.value; + mEventMixingQa = ConfMixing.enableEventMixingQa.value; - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { initAnalysis(Specs); } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco) && isFlagSet(mode, modes::Mode::kMc)) { initMc(Specs); } + if constexpr (isFlagSet(mode, modes::Mode::kMc) && !isFlagSet(mode, modes::Mode::kReco)) { + initMcTruth(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kSe)) { + initSeMixingQa(Specs); + } + if constexpr (isFlagSet(mode, modes::Mode::kMe)) { + initMeMixingQa(Specs); + } } void setMass(int PdgParticle1, int PdgParticle2, int PdgParticle3) @@ -276,7 +403,11 @@ class TripletHistManager // set mT mMt = getMt(mParticle1, mParticle2, mParticle3); // set Q3 - mQ3 = getQ3(mParticle1, mParticle2, mParticle3); + mQ3 = femtomath::getQ3(mParticle1, mParticle2, mParticle3); + + mKstar12 = femtomath::getKstar(mParticle1, mParticle2); + mKstar13 = femtomath::getKstar(mParticle1, mParticle3); + mKstar23 = femtomath::getKstar(mParticle2, mParticle3); // if one of the particles has a mass getter, we cache the value for the filling later if constexpr (utils::HasMass) { @@ -306,6 +437,51 @@ class TripletHistManager mCent = (col1.cent() + col2.cent() + col3.cent()) / 3.f; // if mixing with centrality, should be in the same mixing bin } + // pure mc-truth triplet: the particles ARE the truth, there is no reco counterpart. + // The mass is always the PDG mass here, and the pt needs no charge rescaling, + // since generated particles store the true pt. + template + void setTripletMcTruth(T1 const& particle1, T2 const& particle2, T3 const& particle3) + { + mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(particle1.pt(), particle1.eta(), particle1.phi(), mPdgMass1); + mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(particle2.pt(), particle2.eta(), particle2.phi(), mPdgMass2); + mTrueParticle3 = ROOT::Math::PtEtaPhiMVector(particle3.pt(), particle3.eta(), particle3.phi(), mPdgMass3); + + mTrueMt = getMt(mTrueParticle1, mTrueParticle2, mTrueParticle3); + mTrueQ3 = femtomath::getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); + + // in the pure mc-truth path there is no reco counterpart, so the generated values are also + // stored in the reco members; they are used by getKinematic(), i.e. by the kinematic histograms + // of the triplet cleaner and of the close triplet rejection + mMt = mTrueMt; + mQ3 = mTrueQ3; + + mTrueKstar12 = femtomath::getKstar(mTrueParticle1, mTrueParticle2); + mTrueKstar13 = femtomath::getKstar(mTrueParticle1, mTrueParticle3); + mTrueKstar23 = femtomath::getKstar(mTrueParticle2, mTrueParticle3); + mKstar12 = mTrueKstar12; + mKstar13 = mTrueKstar13; + mKstar23 = mTrueKstar23; + } + + // same event: one (truth) collision for true mult/cent + template + void setTripletMcTruth(T1 const& particle1, T2 const& particle2, T3 const& particle3, T4 const& col) + { + setTripletMcTruth(particle1, particle2, particle3); + mTrueMult = col.mult(); + mTrueCent = col.cent(); + } + + // mixed event: three (truth) collisions, averaged mult/cent (same convention as setTriplet) + template + void setTripletMcTruth(T1 const& particle1, T2 const& particle2, T3 const& particle3, T4 const& col1, T4 const& col2, T4 const& col3) + { + setTripletMcTruth(particle1, particle2, particle3); + mTrueMult = (col1.mult() + col2.mult() + col3.mult()) / 3.f; + mTrueCent = (col1.cent() + col2.cent() + col3.cent()) / 3.f; + } + template void setTripletMc(T1 const& particle1, T2 const& particle2, T3 const& particle3, const T4& /*mcParticles*/) { @@ -319,14 +495,14 @@ class TripletHistManager auto mcParticle2 = particle2.template fMcParticle_as(); auto mcParticle3 = particle3.template fMcParticle_as(); - mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(mAbsCharge1 * mcParticle1.pt(), mcParticle1.eta(), mcParticle1.phi(), mPdgMass1); - mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(mAbsCharge2 * mcParticle2.pt(), mcParticle2.eta(), mcParticle2.phi(), mPdgMass2); - mTrueParticle3 = ROOT::Math::PtEtaPhiMVector(mAbsCharge3 * mcParticle3.pt(), mcParticle3.eta(), mcParticle3.phi(), mPdgMass3); + mTrueParticle1 = ROOT::Math::PtEtaPhiMVector(mcParticle1.pt(), mcParticle1.eta(), mcParticle1.phi(), mPdgMass1); + mTrueParticle2 = ROOT::Math::PtEtaPhiMVector(mcParticle2.pt(), mcParticle2.eta(), mcParticle2.phi(), mPdgMass2); + mTrueParticle3 = ROOT::Math::PtEtaPhiMVector(mcParticle3.pt(), mcParticle3.eta(), mcParticle3.phi(), mPdgMass3); // set true mT mTrueMt = getMt(mTrueParticle1, mTrueParticle2, mTrueParticle3); // set true Q3 - mTrueQ3 = getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); + mTrueQ3 = femtomath::getQ3(mTrueParticle1, mTrueParticle2, mTrueParticle3); } template @@ -356,60 +532,140 @@ class TripletHistManager mHasMcCol = true; auto mcCol1 = col1.template fMcCol_as(); auto mcCol2 = col2.template fMcCol_as(); - auto mcCol3 = col2.template fMcCol_as(); + auto mcCol3 = col3.template fMcCol_as(); mTrueMult = (mcCol1.mult() + mcCol2.mult() + mcCol3.mult()) / 3.f; mTrueCent = (mcCol1.cent() + mcCol2.cent() + mcCol3.cent()) / 3.f; } + /// triplet cuts on the mc-truth values, for the pure mc-truth path (kMc without kReco), + /// where the reco values are never set + bool checkTripletCutsMcTruth() const + { + return (!(mQ3Min > 0.f) || mTrueQ3 > mQ3Min) && + (!(mQ3Max > 0.f) || mTrueQ3 < mQ3Max) && + (!(mMtMin > 0.f) || mTrueMt > mMtMin) && + (!(mMtMax > 0.f) || mTrueMt < mMtMax) && + (!(mKstar12Min > 0.f) || mTrueKstar12 > mKstar12Min) && + (!(mKstar12Max > 0.f) || mTrueKstar12 < mKstar12Max) && + (!(mKstar13Min > 0.f) || mTrueKstar13 > mKstar13Min) && + (!(mKstar13Max > 0.f) || mTrueKstar13 < mKstar13Max) && + (!(mKstar23Min > 0.f) || mTrueKstar23 > mKstar23Min) && + (!(mKstar23Max > 0.f) || mTrueKstar23 < mKstar23Max); + } + bool checkTripletCuts() const { return (!(mQ3Min > 0.f) || mQ3 > mQ3Min) && (!(mQ3Max > 0.f) || mQ3 < mQ3Max) && (!(mMtMin > 0.f) || mMt > mMtMin) && - (!(mMtMax > 0.f) || mMt < mMtMax); + (!(mMtMax > 0.f) || mMt < mMtMax) && + (!(mKstar12Min > 0.f) || mKstar12 > mKstar12Min) && + (!(mKstar12Max > 0.f) || mKstar12 < mKstar12Max) && + (!(mKstar13Min > 0.f) || mKstar13 > mKstar13Min) && + (!(mKstar13Max > 0.f) || mKstar13 < mKstar13Max) && + (!(mKstar23Min > 0.f) || mKstar23 > mKstar23Min) && + (!(mKstar23Max > 0.f) || mKstar23 < mKstar23Max); } template void fill() { - if constexpr (isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco)) { fillAnalysis(); } - if constexpr (isFlagSet(mode, modes::Mode::kMc)) { + if constexpr (isFlagSet(mode, modes::Mode::kReco) && isFlagSet(mode, modes::Mode::kMc)) { fillMc(); } + if constexpr (isFlagSet(mode, modes::Mode::kMc) && !isFlagSet(mode, modes::Mode::kReco)) { + fillMcTruth(); + } + } + + // kinematicVariable is supplied by the caller (CPR/triplet cleaner), each of which carries its + // own Configurable for it -- this manager has no opinion of its own on which variable to report. + // kstar has no single value for a triplet, so kKstar reports the average of the three pair kstars. + float getKinematic(modes::KinematicVariable kinematicVariable) const + { + switch (kinematicVariable) { + case modes::KinematicVariable::kKstar: + return (mKstar12 + mKstar13 + mKstar23) / 3.f; + break; + case modes::KinematicVariable::kMt: + return mMt; + break; + case modes::KinematicVariable::kQ3: + return mQ3; + break; + default: + LOG(fatal) << "Invalid kinematic Variable, breaking..."; + } + return mQ3; } - float getQ3() const { return mQ3; } + template + void trackParticlesPerEvent(T1 const& particle1, T2 const& particle2, T3 const& particle3) + { + if (!mPairCorrelationQa) { + return; + } + mParticles1PerEvent.insert(particle1.globalIndex()); + mParticles2PerEvent.insert(particle2.globalIndex()); + mParticles3PerEvent.insert(particle3.globalIndex()); + } - private: - ROOT::Math::PxPyPzEVector getqij(ROOT::Math::PtEtaPhiMVector const& pi, ROOT::Math::PtEtaPhiMVector const& pj) + template + void fillMixingQaMe(T1 const& col1, T2 const& col2, T3 const& col3) + { + if (mEventMixingQa) { + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3, HistTable)), + col1.posZ(), col1.mult(), col1.cent(), + col2.posZ(), col2.mult(), col2.cent(), + col3.posZ(), col3.mult(), col3.cent()); + } + } + + void resetTrackedParticlesPerEvent() + { + mParticles1PerEvent.clear(); + mParticles1PerEvent.reserve(100); + mParticles2PerEvent.clear(); + mParticles2PerEvent.reserve(100); + mParticles3PerEvent.clear(); + mParticles3PerEvent.reserve(100); + } + + void fillMixingQaSe() + { + if (mPairCorrelationQa) { + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kSeNpart1VsNpart2VsNpart3, HistTable)), mParticles1PerEvent.size(), mParticles2PerEvent.size(), mParticles3PerEvent.size()); + } + } + + void fillMixingQaMePerEvent() { - // Convert to PxPyPzEVector to get proper Lorentz dot product - ROOT::Math::PxPyPzEVector vi(pi); - ROOT::Math::PxPyPzEVector vj(pj); - - auto trackSum = vi + vj; - auto trackDifference = vi - vj; - double scaling = trackDifference.Dot(trackSum) / trackSum.Dot(trackSum); - return trackDifference - scaling * trackSum; + if (mPairCorrelationQa) { + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kMeNpart1VsNpart2VsNpart3, HistTable)), mParticles1PerEvent.size(), mParticles2PerEvent.size(), mParticles3PerEvent.size()); + } } - float getQ3(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) + void fillMixingQaMePerMixingBin(int windowSizeRaw, int windowSizeEffective) { - auto q12 = getqij(part1, part2); - auto q23 = getqij(part2, part3); - auto q31 = getqij(part3, part1); - double q = q12.M2() + q23.M2() + q31.M2(); - return static_cast(std::sqrt(-q)); + if (mPairCorrelationQa) { + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kMeMixingWindowRaw, HistTable)), windowSizeRaw); + mHistogramRegistry->fill(HIST(prefix) + HIST(QaDir) + HIST(getHistName(kMeMixingWindowEffective, HistTable)), windowSizeEffective); + } } + private: void initAnalysis(std::map> const& Specs) { std::string analysisDir = std::string(prefix) + std::string(AnalysisDir); if (mPlot1d) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3, HistTable), getHistDesc(kQ3, HistTable), getHistType(kQ3, HistTable), {Specs.at(kQ3)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kMt, HistTable), getHistDesc(kMt, HistTable), getHistType(kMt, HistTable), {Specs.at(kMt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar12, HistTable), getHistDesc(kKstar12, HistTable), getHistType(kKstar12, HistTable), {Specs.at(kKstar12)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar13, HistTable), getHistDesc(kKstar13, HistTable), getHistType(kKstar13, HistTable), {Specs.at(kKstar13)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kKstar23, HistTable), getHistDesc(kKstar23, HistTable), getHistType(kKstar23, HistTable), {Specs.at(kKstar23)}); } if (mPlot2d) { mHistogramRegistry->add(analysisDir + getHistNameV2(kPt1VsQ3, HistTable), getHistDesc(kPt1VsQ3, HistTable), getHistType(kPt1VsQ3, HistTable), {Specs.at(kPt1VsQ3)}); @@ -439,6 +695,9 @@ class TripletHistManager if (mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent) { mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), getHistDesc(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), getHistType(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable), {Specs.at(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent)}); } + if (mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3) { + mHistogramRegistry->add(analysisDir + getHistNameV2(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), getHistDesc(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), getHistType(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable), {Specs.at(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3)}); + } } void initMc(std::map> const& Specs) @@ -450,11 +709,76 @@ class TripletHistManager mHistogramRegistry->add(mcDir + getHistNameV2(kTrueCentVsCent, HistTable), getHistDesc(kTrueCentVsCent, HistTable), getHistType(kTrueCentVsCent, HistTable), {Specs.at(kTrueCentVsCent)}); } + void initSeMixingQa(std::map> const& Specs) + { + std::string dir = std::string(prefix) + std::string(QaDir); + if (mPairCorrelationQa) { + mHistogramRegistry->add(dir + getHistNameV2(kSeNpart1VsNpart2VsNpart3, HistTable), getHistDesc(kSeNpart1VsNpart2VsNpart3, HistTable), getHistType(kSeNpart1VsNpart2VsNpart3, HistTable), {Specs.at(kSeNpart1VsNpart2VsNpart3)}); + } + } + + void initMeMixingQa(std::map> const& Specs) + { + std::string dir = std::string(prefix) + std::string(QaDir); + if (mPairCorrelationQa) { + mHistogramRegistry->add(dir + getHistNameV2(kMeMixingWindowRaw, HistTable), getHistDesc(kMeMixingWindowRaw, HistTable), getHistType(kMeMixingWindowRaw, HistTable), {Specs.at(kMeMixingWindowRaw)}); + mHistogramRegistry->add(dir + getHistNameV2(kMeMixingWindowEffective, HistTable), getHistDesc(kMeMixingWindowEffective, HistTable), getHistType(kMeMixingWindowEffective, HistTable), {Specs.at(kMeMixingWindowEffective)}); + mHistogramRegistry->add(dir + getHistNameV2(kMeNpart1VsNpart2VsNpart3, HistTable), getHistDesc(kMeNpart1VsNpart2VsNpart3, HistTable), getHistType(kMeNpart1VsNpart2VsNpart3, HistTable), {Specs.at(kMeNpart1VsNpart2VsNpart3)}); + } + if (mEventMixingQa) { + mHistogramRegistry->add(dir + getHistNameV2(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3, HistTable), getHistDesc(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3, HistTable), getHistType(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3, HistTable), {Specs.at(kMeVtz1VsMult1VsCent1VsVtz2VsMult2VsCent2VsVtz3VsMult3VsCent3)}); + } + } + + // pure mc-truth triplet (kMc without kReco) — reuses the mPlot1d/mPlot2d flags, + // since these are histogram-content flags, not mode flags + void initMcTruth(std::map> const& Specs) + { + std::string mcDir = std::string(prefix) + std::string(McDir); + if (mPlot1d) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3, HistTable), getHistDesc(kTrueQ3, HistTable), getHistType(kTrueQ3, HistTable), {Specs.at(kTrueQ3)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueMt, HistTable), getHistDesc(kTrueMt, HistTable), getHistType(kTrueMt, HistTable), {Specs.at(kTrueMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar12, HistTable), getHistDesc(kTrueKstar12, HistTable), getHistType(kTrueKstar12, HistTable), {Specs.at(kTrueKstar12)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar13, HistTable), getHistDesc(kTrueKstar13, HistTable), getHistType(kTrueKstar13, HistTable), {Specs.at(kTrueKstar13)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueKstar23, HistTable), getHistDesc(kTrueKstar23, HistTable), getHistType(kTrueKstar23, HistTable), {Specs.at(kTrueKstar23)}); + } + if (mPlot2d) { + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt1VsTrueQ3, HistTable), getHistDesc(kTruePt1VsTrueQ3, HistTable), getHistType(kTruePt1VsTrueQ3, HistTable), {Specs.at(kTruePt1VsTrueQ3)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt2VsTrueQ3, HistTable), getHistDesc(kTruePt2VsTrueQ3, HistTable), getHistType(kTruePt2VsTrueQ3, HistTable), {Specs.at(kTruePt2VsTrueQ3)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTruePt3VsTrueQ3, HistTable), getHistDesc(kTruePt3VsTrueQ3, HistTable), getHistType(kTruePt3VsTrueQ3, HistTable), {Specs.at(kTruePt3VsTrueQ3)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3VsTrueMt, HistTable), getHistDesc(kTrueQ3VsTrueMt, HistTable), getHistType(kTrueQ3VsTrueMt, HistTable), {Specs.at(kTrueQ3VsTrueMt)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3VsTrueMult, HistTable), getHistDesc(kTrueQ3VsTrueMult, HistTable), getHistType(kTrueQ3VsTrueMult, HistTable), {Specs.at(kTrueQ3VsTrueMult)}); + mHistogramRegistry->add(mcDir + getHistNameV2(kTrueQ3VsTrueCent, HistTable), getHistDesc(kTrueQ3VsTrueCent, HistTable), getHistType(kTrueQ3VsTrueCent, HistTable), {Specs.at(kTrueQ3VsTrueCent)}); + } + } + + void fillMcTruth() + { + if (mPlot1d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3, HistTable)), mTrueQ3); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueMt, HistTable)), mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar12, HistTable)), mTrueKstar12); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar13, HistTable)), mTrueKstar13); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueKstar23, HistTable)), mTrueKstar23); + } + if (mPlot2d) { + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt1VsTrueQ3, HistTable)), mTrueParticle1.Pt(), mTrueQ3); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt2VsTrueQ3, HistTable)), mTrueParticle2.Pt(), mTrueQ3); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTruePt3VsTrueQ3, HistTable)), mTrueParticle3.Pt(), mTrueQ3); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3VsTrueMt, HistTable)), mTrueQ3, mTrueMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3VsTrueMult, HistTable)), mTrueQ3, mTrueMult); + mHistogramRegistry->fill(HIST(prefix) + HIST(McDir) + HIST(getHistName(kTrueQ3VsTrueCent, HistTable)), mTrueQ3, mTrueCent); + } + } + void fillAnalysis() { if (mPlot1d) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3, HistTable)), mQ3); mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kMt, HistTable)), mMt); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar12, HistTable)), mKstar12); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar13, HistTable)), mKstar13); + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kKstar23, HistTable)), mKstar23); } if (mPlot2d) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kPt1VsQ3, HistTable)), mParticle1.Pt(), mQ3); @@ -483,6 +807,9 @@ class TripletHistManager if (mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent) { mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent, HistTable)), mQ3, mMt, mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt(), mMult, mCent); } + if (mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3) { + mHistogramRegistry->fill(HIST(prefix) + HIST(AnalysisDir) + HIST(getHistName(kQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3, HistTable)), mQ3, mKstar12, mKstar13, mKstar23, mParticle1.Pt(), mParticle2.Pt(), mParticle3.Pt()); + } } void fillMc() @@ -497,33 +824,11 @@ class TripletHistManager } } - double getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) - { - auto sum = part1 + part2; - double mt = 0.; - double averageMass = 0.; - double reducedMass = 0.; - switch (mMtType) { - case modes::TransverseMassType::kAveragePdgMass: - averageMass = 0.5 * (part1.M() + part2.M()); - mt = std::hypot(0.5 * sum.Pt(), averageMass); - break; - case modes::TransverseMassType::kReducedPdgMass: - reducedMass = 2. * (part1.M() * part2.M() / (part1.M() + part2.M())); - mt = std::hypot(0.5 * sum.Pt(), reducedMass); - break; - case modes::TransverseMassType::kMt4Vector: - mt = 0.5 * sum.Mt(); - break; - default: - LOG(fatal) << "Invalid transverse mass type, breaking..."; - } - return mt; - } - float getMt(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2, ROOT::Math::PtEtaPhiMVector const& part3) { - return static_cast((getMt(part1, part2) + getMt(part2, part3) + getMt(part1, part3)) / 3.); + // all three pairwise combinations are intentional, not a mixed-up argument order + // NOLINTNEXTLINE(readability-suspicious-call-argument) + return (femtomath::getMt(part1, part2, mMtType) + femtomath::getMt(part2, part3, mMtType) + femtomath::getMt(part1, part3, mMtType)) / 3.f; } o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; @@ -536,9 +841,9 @@ class TripletHistManager int mAbsCharge1 = 1; int mAbsCharge2 = 1; int mAbsCharge3 = 1; - ROOT::Math::PtEtaPhiMVector mParticle1{}; - ROOT::Math::PtEtaPhiMVector mParticle2{}; - ROOT::Math::PtEtaPhiMVector mParticle3{}; + ROOT::Math::PtEtaPhiMVector mParticle1; + ROOT::Math::PtEtaPhiMVector mParticle2; + ROOT::Math::PtEtaPhiMVector mParticle3; float mMass1 = 0.f; float mMass2 = 0.f; float mMass3 = 0.f; @@ -546,13 +851,19 @@ class TripletHistManager float mMt = 0.f; float mMult = 0.f; float mCent = 0.f; + float mKstar12 = 0.f; + float mKstar13 = 0.f; + float mKstar23 = 0.f; // mc - ROOT::Math::PtEtaPhiMVector mTrueParticle1{}; - ROOT::Math::PtEtaPhiMVector mTrueParticle2{}; - ROOT::Math::PtEtaPhiMVector mTrueParticle3{}; + ROOT::Math::PtEtaPhiMVector mTrueParticle1; + ROOT::Math::PtEtaPhiMVector mTrueParticle2; + ROOT::Math::PtEtaPhiMVector mTrueParticle3; float mTrueQ3 = 0.f; float mTrueMt = 0.f; + float mTrueKstar12 = 0.f; + float mTrueKstar13 = 0.f; + float mTrueKstar23 = 0.f; float mTrueMult = 0.f; float mTrueCent = 0.f; @@ -561,10 +872,14 @@ class TripletHistManager bool mHasMcCol = false; float mQ3Min = -1.f; float mQ3Max = -1.f; - float mKtMin = -1.f; - float mKtMax = -1.f; float mMtMin = -1.f; float mMtMax = -1.f; + float mKstar12Min = -1.f; + float mKstar12Max = -1.f; + float mKstar13Min = -1.f; + float mKstar13Max = -1.f; + float mKstar23Min = -1.f; + float mKstar23Max = -1.f; // flags bool mPlot1d = true; @@ -576,8 +891,15 @@ class TripletHistManager bool mPlotQ3VsMtVsMultVsCent = false; bool mPlotQ3VsMtVsPt1VsPt2VsPt3VsMult = false; bool mPlotQ3VsMtVsPt1VsPt2VsPt3VsMultVsCent = false; -}; + bool mPlotQ3VsKstar12VsKstar13VsKstar23VsPt1VsPt2VsPt3 = false; + + // mixing qa + bool mPairCorrelationQa = false; + bool mEventMixingQa = false; -}; // namespace triplethistmanager -}; // namespace o2::analysis::femto + std::unordered_set mParticles1PerEvent; + std::unordered_set mParticles2PerEvent; + std::unordered_set mParticles3PerEvent; +}; +} // namespace o2::analysis::femto::triplethistmanager #endif // PWGCF_FEMTO_CORE_TRIPLETHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/tripletProcessHelpers.h b/PWGCF/Femto/Core/tripletProcessHelpers.h index 75538ae024d..df75ace9ef9 100644 --- a/PWGCF/Femto/Core/tripletProcessHelpers.h +++ b/PWGCF/Femto/Core/tripletProcessHelpers.h @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file tripletProcessHelpers.h -/// \brief process functions used in pair tasks +/// \brief process functions used in triplet tasks /// \author anton.riedel@tum.de, TU München, anton.riedel@tum.de #ifndef PWGCF_FEMTO_CORE_TRIPLETPROCESSHELPERS_H_ @@ -20,19 +20,24 @@ #include "PWGCF/Femto/DataModel/FemtoTables.h" #include +#include #include +#include -namespace o2::analysis::femto -{ -namespace tripletprocesshelpers +namespace o2::analysis::femto::tripletprocesshelpers { +// NOTE: all processSameEvent helpers return true if at least one triplet in the event passed +// the triplet cleaner, the close triplet rejection and the triplet cuts. +// This allows to use them as a triplet trigger. enum TripletOrder : uint8_t { kOrder123, // no swap - kOrder213, // first two swap 1&2 so we can use them for the case that particle 1 & 2 are the same species, particle 3 is something else + kOrder213, // swap 1&2: for the case that particle 1 & 2 are the same species, particle 3 is something else kOrder132, // swap 2&3 - kOrder321, // swap 1&2&3 + kOrder231, // swap 1&2 & 1&3 + kOrder312, // swap 2&3 & 1&3 + kOrder321, // swap 1&3 }; // process same event for identical 3 particles @@ -44,7 +49,7 @@ template -void processSameEvent(T1 const& SliceParticle, +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& Collision, T4& ParticleHistManager, @@ -53,24 +58,16 @@ void processSameEvent(T1 const& SliceParticle, T7& TcManager, TripletOrder tripletOrder) { + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + for (auto const& part : SliceParticle) { ParticleHistManager.template fill(part, TrackTable); } for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle, SliceParticle, SliceParticle))) { - // check if triplet is clean - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable)) { - continue; - } - - // check if triplet is close - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { - continue; - } - - // Randomize pair order if enabled + // Randomize triplet order if enabled, then compute the kinematic (Q3) for this triplet switch (tripletOrder) { case kOrder123: TripletHistManager.setTriplet(p1, p2, p3, Collision); @@ -81,6 +78,12 @@ void processSameEvent(T1 const& SliceParticle, case kOrder132: TripletHistManager.setTriplet(p1, p3, p2, Collision); break; + case kOrder231: + TripletHistManager.setTriplet(p2, p3, p1, Collision); + break; + case kOrder312: + TripletHistManager.setTriplet(p3, p1, p2, Collision); + break; case kOrder321: TripletHistManager.setTriplet(p3, p2, p1, Collision); break; @@ -89,28 +92,33 @@ void processSameEvent(T1 const& SliceParticle, break; } - // fill deta-dphi histograms with !3 cutoff - CtrManager.fill(TripletHistManager.getQ3()); + // check if triplet is clean + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { + continue; + } + + // check if triplet is close; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { + continue; + } // if triplet cuts are configured check them before filling if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for identical 2 particles and 1 other particle template -void processSameEvent(T1 const& SliceParticle1, // 1&2 have same species + typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7, typename T8, typename T9> +bool processSameEvent(T1 const& SliceParticle1, // 1&2 have same species T2 const& SliceParticle3, T3 const& TrackTable, T4 const& Collision, @@ -119,8 +127,11 @@ void processSameEvent(T1 const& SliceParticle1, // 1&2 have same species T7& TripletHistManager, T8& CtrManager, T9& TcManager, - TripletOrder triplerOrder) + TripletOrder tripletOrder) { + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + for (auto const& part : SliceParticle1) { ParticleHistManager1.template fill(part, TrackTable); } @@ -128,40 +139,42 @@ void processSameEvent(T1 const& SliceParticle1, // 1&2 have same species ParticleHistManager3.template fill(part, TrackTable); } - for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle1, SliceParticle1, SliceParticle3))) { - - // check if triplet is clean - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable)) { - continue; - } + for (auto const& p3 : SliceParticle3) { + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle1, SliceParticle1))) { - // check if triplet is close - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { - continue; - } + // Randomize triplet order if enabled, then compute the kinematic (Q3) for this triplet + // only kOrder123 and kOrder213 are meaningful here since particle 1 & 2 are the same species + switch (tripletOrder) { + case kOrder213: + TripletHistManager.setTriplet(p2, p1, p3, Collision); + break; + case kOrder123: + default: + TripletHistManager.setTriplet(p1, p2, p3, Collision); + break; + } - // Randomize triplet order if enabled - switch (triplerOrder) { - case kOrder123: - TripletHistManager.setTriplet(p1, p2, p3, Collision); - break; - case kOrder213: - TripletHistManager.setTriplet(p1, p2, p3, Collision); - break; - default: - TripletHistManager.setTriplet(p1, p2, p3, Collision); - break; - } + // check if triplet is clean + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { + continue; + } - // fill deta-dphi histograms with !3 cutoff - CtrManager.fill(TripletHistManager.getQ3()); + // check if triplet is close; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { + continue; + } - // if triplet cuts are configured check them before filling - if (TripletHistManager.checkTripletCuts()) { - TripletHistManager.template fill(); + // if triplet cuts are configured check them before filling + if (TripletHistManager.checkTripletCuts()) { + TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; + } } } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for 3 different particles @@ -177,7 +190,7 @@ template -void processSameEvent(T1 const& SliceParticle1, +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& SliceParticle3, T4 const& TrackTable, @@ -189,6 +202,9 @@ void processSameEvent(T1 const& SliceParticle1, T10& CtrManager, T11& TcManager) { + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + for (auto const& part : SliceParticle1) { ParticleHistManager1.template fill(part, TrackTable); } @@ -199,32 +215,34 @@ void processSameEvent(T1 const& SliceParticle1, ParticleHistManager3.template fill(part, TrackTable); } - for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle1, SliceParticle2, SliceParticle3))) { + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(SliceParticle1, SliceParticle2, SliceParticle3))) { + + // compute the kinematic (Q3) for this triplet + TripletHistManager.setTriplet(p1, p2, p3, Collision); // check if triplet is clean - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable)) { + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { continue; } - // check if triplet is close - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { + // check if triplet is close; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { continue; } - TripletHistManager.setTriplet(p1, p2, p3, Collision); - - // fill deta-dphi histograms with !3 cutoff - CtrManager.fill(TripletHistManager.getQ3()); - // if triplet cuts are configured check them before filling if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; } -// // process same event for 3 identical particles with mc information +// process same event for 3 identical particles with mc information template -void processSameEvent(T1 const& SliceParticle, + typename T11, + typename T12> +bool processSameEvent(T1 const& SliceParticle, T2 const& TrackTable, T3 const& mcParticles, T4 const& mcMothers, @@ -246,65 +265,81 @@ void processSameEvent(T1 const& SliceParticle, T7 const& mcCollisions, T8& ParticleHistManager, T9& TripletHistManager, - T10& CtrManager, - T11& TcManager, - int swapTriplet) + T10& Cleaner, + T11& CtrManager, + T12& TcManager, + TripletOrder tripletOrder) { + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + for (auto const& part : SliceParticle) { - ParticleHistManager.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); - } - for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle, SliceParticle, SliceParticle))) { - // check if pair is clean - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, mcPartonicMothers)) { + if (!Cleaner.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - // check if pair is close - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { + ParticleHistManager.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); + } + + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle, SliceParticle, SliceParticle))) { + // check if all three particles are clean + if (!Cleaner.isClean(p1, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner.isClean(p2, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner.isClean(p3, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - // Randomize pair order if enabled - // - switch (swapTriplet) { - case 3: - TripletHistManager.setTripletMc(p1, p3, p2, mcParticles, Collision, mcCollisions); + + // Randomize triplet order if enabled, then compute the kinematic (Q3) for this triplet + switch (tripletOrder) { + case kOrder123: + TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, Collision, mcCollisions); break; - case 2: + case kOrder213: TripletHistManager.setTripletMc(p2, p1, p3, mcParticles, Collision, mcCollisions); break; - case 1: - TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, Collision, mcCollisions); + case kOrder132: + TripletHistManager.setTripletMc(p1, p3, p2, mcParticles, Collision, mcCollisions); + break; + case kOrder231: + TripletHistManager.setTripletMc(p2, p3, p1, mcParticles, Collision, mcCollisions); + break; + case kOrder312: + TripletHistManager.setTripletMc(p3, p1, p2, mcParticles, Collision, mcCollisions); + break; + case kOrder321: + TripletHistManager.setTripletMc(p3, p2, p1, mcParticles, Collision, mcCollisions); break; default: TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, Collision, mcCollisions); break; } - // float threshold = 0.5f; - // fill deta-dphi histograms with q3 cutoff - CtrManager.fill(TripletHistManager.getQ3()); - // if pair cuts are configured check them before filling + + // check if triplet is clean + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, mcParticles, mcPartonicMothers, TripletHistManager)) { + continue; + } + + // check if triplet is close; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { + continue; + } + + // if triplet cuts are configured check them before filling if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process same event for 2 identical particles and one other with mc information template -void processSameEvent(T1 const& SliceParticle1, + typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, + typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15> +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle3, T3 const& TrackTable, T4 const& mcParticles, @@ -315,49 +350,73 @@ void processSameEvent(T1 const& SliceParticle1, T9& ParticleHistManager1, T10& ParticleHistManager3, T11& TripletHistManager, - T12& CtrManager, - T13& TcManager, - int swapTriplet) + T12& Cleaner1, + T13& Cleaner3, + T14& CtrManager, + T15& TcManager, + TripletOrder tripletOrder) { + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + for (auto const& part : SliceParticle1) { - ParticleHistManager1.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); - } - for (auto const& part : SliceParticle3) { - ParticleHistManager3.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); - } - for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle1, SliceParticle1, SliceParticle3))) { - // check if pair is clean - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, mcPartonicMothers)) { + if (!Cleaner1.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - // check if pair is close - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { + ParticleHistManager1.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); + } + for (auto const& part : SliceParticle3) { + if (!Cleaner3.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - // Randomize pair order if enabled - // - switch (swapTriplet) { - case 2: - TripletHistManager.setTripletMc(p2, p1, p3, mcParticles, Collision, mcCollisions); - break; - case 1: - TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, Collision, mcCollisions); - break; - default: - TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, Collision, mcCollisions); - break; - } - // fill deta-dphi histograms with q3 cutoff - CtrManager.fill(TripletHistManager.getQ3()); - // if pair cuts are configured check them before filling - if (TripletHistManager.checkTripletCuts()) { - TripletHistManager.template fill(); + ParticleHistManager3.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); + } + + for (auto const& p3 : SliceParticle3) { + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle1, SliceParticle1))) { + // check if all three particles are clean + if (!Cleaner1.isClean(p1, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner1.isClean(p2, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner3.isClean(p3, mcParticles, mcMothers, mcPartonicMothers)) { + continue; + } + + // Randomize triplet order if enabled, then compute the kinematic (Q3) for this triplet + // only kOrder123 and kOrder213 are meaningful here since particle 1 & 2 are the same species + switch (tripletOrder) { + case kOrder213: + TripletHistManager.setTripletMc(p2, p1, p3, mcParticles, Collision, mcCollisions); + break; + case kOrder123: + default: + TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, Collision, mcCollisions); + break; + } + + // check if triplet is clean + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, mcParticles, mcPartonicMothers, TripletHistManager)) { + continue; + } + + // check if triplet is close; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { + continue; + } + + // if triplet cuts are configured check them before filling + if (TripletHistManager.checkTripletCuts()) { + TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; + } } } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; } -// process same event for 3 different particles +// process same event for 3 different particles with mc information template -void processSameEvent(T1 const& SliceParticle1, + typename T15, + typename T16, + typename T17, + typename T18> +bool processSameEvent(T1 const& SliceParticle1, T2 const& SliceParticle2, T3 const& SliceParticle3, T4 const& TrackTable, @@ -387,36 +449,297 @@ void processSameEvent(T1 const& SliceParticle1, T11& ParticleHistManager2, T12& ParticleHistManager3, T13& TripletHistManager, - T14& CtrManager, - T15& TcManager) + T14& Cleaner1, + T15& Cleaner2, + T16& Cleaner3, + T17& CtrManager, + T18& TcManager) { + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + for (auto const& part : SliceParticle1) { - ParticleHistManager1.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); + if (!Cleaner1.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager1.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); } for (auto const& part : SliceParticle2) { - ParticleHistManager2.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); + if (!Cleaner2.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager2.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); } for (auto const& part : SliceParticle3) { - ParticleHistManager3.template fill(part, TrackTable, mcParticles, mcMothers, mcPartonicMothers); - } - for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle1, SliceParticle2, SliceParticle3))) { - // check if pair is clean - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, mcPartonicMothers)) { + if (!Cleaner3.isClean(part, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - // check if pair is close - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { + ParticleHistManager3.template fill(part, TrackTable, Collision, mcParticles, mcMothers, mcPartonicMothers); + } + + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(SliceParticle1, SliceParticle2, SliceParticle3))) { + // check if all three particles are clean + if (!Cleaner1.isClean(p1, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner2.isClean(p2, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner3.isClean(p3, mcParticles, mcMothers, mcPartonicMothers)) { continue; } + + // compute the kinematic (Q3) for this triplet TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, Collision, mcCollisions); - // fill deta-dphi histograms with q3 cutoff - CtrManager.fill(TripletHistManager.getQ3()); - // if pair cuts are configured check them before filling + + // check if triplet is clean + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, mcParticles, mcPartonicMothers, TripletHistManager)) { + continue; + } + + // check if triplet is close; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { + continue; + } + + // if triplet cuts are configured check them before filling if (TripletHistManager.checkTripletCuts()) { TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; + } + } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; +} + +// --- mc truth only (kMc without kReco): triplets of generated particles --------------------------- +// process same event for identical 3 mc particles +template +bool processSameEventMcTruth(T1 const& SliceParticle, + T2 const& /*mcParticles*/, + T3 const& mcMothers, + T4 const& mcPartonicMothers, + T5 const& Collision, + T6& ParticleHistManager, + T7& TripletHistManager, + T8& ParticleCleaner, + T9& CtrManager, + T10& TcManager, + TripletOrder tripletOrder) +{ + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + + for (auto const& part : SliceParticle) { + if (!ParticleCleaner.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager.fill(part, mcMothers, mcPartonicMothers); + } + + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle, SliceParticle, SliceParticle))) { + if (!ParticleCleaner.isClean(p1, mcMothers, mcPartonicMothers) || + !ParticleCleaner.isClean(p2, mcMothers, mcPartonicMothers) || + !ParticleCleaner.isClean(p3, mcMothers, mcPartonicMothers)) { + continue; + } + + // Randomize triplet order if enabled, then compute the kinematic (Q3) for this triplet + switch (tripletOrder) { + case kOrder123: + TripletHistManager.setTripletMcTruth(p1, p2, p3, Collision); + break; + case kOrder213: + TripletHistManager.setTripletMcTruth(p2, p1, p3, Collision); + break; + case kOrder132: + TripletHistManager.setTripletMcTruth(p1, p3, p2, Collision); + break; + case kOrder231: + TripletHistManager.setTripletMcTruth(p2, p3, p1, Collision); + break; + case kOrder312: + TripletHistManager.setTripletMcTruth(p3, p1, p2, Collision); + break; + case kOrder321: + TripletHistManager.setTripletMcTruth(p3, p2, p1, Collision); + break; + default: + TripletHistManager.setTripletMcTruth(p1, p2, p3, Collision); + break; + } + + if (!TcManager.isCleanTriplet(p1, p2, p3, mcPartonicMothers, TripletHistManager)) { + continue; + } + + if (CtrManager.isCloseTriplet(p1, p2, p3, TripletHistManager)) { + continue; + } + + if (TripletHistManager.checkTripletCutsMcTruth()) { + TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; } } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; +} + +// process same event for 2 identical mc particles and 1 other mc particle +template +bool processSameEventMcTruth(T1 const& SliceParticle1, // 1&2 have same species + T2 const& SliceParticle3, + T3 const& /*mcParticles*/, + T4 const& mcMothers, + T5 const& mcPartonicMothers, + T6 const& Collision, + T7& ParticleHistManager1, + T8& ParticleHistManager3, + T9& TripletHistManager, + T10& ParticleCleaner1, + T11& ParticleCleaner3, + T12& CtrManager, + T13& TcManager, + TripletOrder tripletOrder) +{ + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + + for (auto const& part : SliceParticle1) { + if (!ParticleCleaner1.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager1.fill(part, mcMothers, mcPartonicMothers); + } + for (auto const& part : SliceParticle3) { + if (!ParticleCleaner3.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager3.fill(part, mcMothers, mcPartonicMothers); + } + + for (auto const& p3 : SliceParticle3) { + if (!ParticleCleaner3.isClean(p3, mcMothers, mcPartonicMothers)) { + continue; + } + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(SliceParticle1, SliceParticle1))) { + if (!ParticleCleaner1.isClean(p1, mcMothers, mcPartonicMothers) || + !ParticleCleaner1.isClean(p2, mcMothers, mcPartonicMothers)) { + continue; + } + + switch (tripletOrder) { + case kOrder213: + TripletHistManager.setTripletMcTruth(p2, p1, p3, Collision); + break; + case kOrder123: + default: + TripletHistManager.setTripletMcTruth(p1, p2, p3, Collision); + break; + } + + if (!TcManager.isCleanTriplet(p1, p2, p3, mcPartonicMothers, TripletHistManager)) { + continue; + } + + if (CtrManager.isCloseTriplet(p1, p2, p3, TripletHistManager)) { + continue; + } + + if (TripletHistManager.checkTripletCutsMcTruth()) { + TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; + } + } + } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; +} + +// process same event for 3 different mc particles +template +bool processSameEventMcTruth(T1 const& SliceParticle1, + T2 const& SliceParticle2, + T3 const& SliceParticle3, + T4 const& /*mcParticles*/, + T5 const& mcMothers, + T6 const& mcPartonicMothers, + T7 const& Collision, + T8& ParticleHistManager1, + T9& ParticleHistManager2, + T10& ParticleHistManager3, + T11& TripletHistManager, + T12& ParticleCleaner1, + T13& ParticleCleaner2, + T14& ParticleCleaner3, + T15& CtrManager, + T16& TcManager) +{ + TripletHistManager.resetTrackedParticlesPerEvent(); + bool foundTriplet = false; + + for (auto const& part : SliceParticle1) { + if (!ParticleCleaner1.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager1.fill(part, mcMothers, mcPartonicMothers); + } + for (auto const& part : SliceParticle2) { + if (!ParticleCleaner2.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager2.fill(part, mcMothers, mcPartonicMothers); + } + for (auto const& part : SliceParticle3) { + if (!ParticleCleaner3.isClean(part, mcMothers, mcPartonicMothers)) { + continue; + } + ParticleHistManager3.fill(part, mcMothers, mcPartonicMothers); + } + + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(SliceParticle1, SliceParticle2, SliceParticle3))) { + if (!ParticleCleaner1.isClean(p1, mcMothers, mcPartonicMothers) || + !ParticleCleaner2.isClean(p2, mcMothers, mcPartonicMothers) || + !ParticleCleaner3.isClean(p3, mcMothers, mcPartonicMothers)) { + continue; + } + + TripletHistManager.setTripletMcTruth(p1, p2, p3, Collision); + + if (!TcManager.isCleanTriplet(p1, p2, p3, mcPartonicMothers, TripletHistManager)) { + continue; + } + + if (CtrManager.isCloseTriplet(p1, p2, p3, TripletHistManager)) { + continue; + } + + if (TripletHistManager.checkTripletCutsMcTruth()) { + TripletHistManager.template fill(); + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + foundTriplet = true; + } + } + + TripletHistManager.fillMixingQaSe(); + return foundTriplet; } // process mixed event @@ -431,7 +754,10 @@ template + typename T11, + typename T12, + typename T13, + typename T14> void processMixedEvent(T1 const& Collisions, T2& Partition1, T3& Partition2, @@ -442,37 +768,116 @@ void processMixedEvent(T1 const& Collisions, T8 const& depth, T9& TripletHistManager, T10& CtrManager, - T11& TcManager) + T11& TcManager, + int eventStride, + T12& ParticleHistManagerMe1, + T13& ParticleHistManagerMe2, + T14& ParticleHistManagerMe3) { + int64_t lastCollisionIndex1 = -1; + int64_t lastCollisionIndex2 = -1; + int windowSizeRaw = 0; + int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; + + // collision1 stays fixed across the outer mixing window, and collision2 stays fixed across each inner sub-window (it only advances once collision3 wraps + // back to its start Both slices are therefore materialized once per window/sub-window and reused, instead of being re-sliced, i.e. a fresh arrow Slice + selection copy + std::optionalsliceByCached(o2::aod::femtobase::stored::fColId, 0, cache))> sliceParticle1; + std::optionalsliceByCached(o2::aod::femtobase::stored::fColId, 0, cache))> sliceParticle2; + for (auto const& [collision1, collision2, collision3] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions, Collisions)) { + + // outer window + if (collision1.globalIndex() != lastCollisionIndex1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { + TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } + windowSizeRaw = 0; + windowSizeEffective = 0; + lastCollisionIndex1 = collision1.globalIndex(); + lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; + } + + // inner sub-window + if (collision2.globalIndex() != lastCollisionIndex2) { + lastCollisionIndex2 = collision2.globalIndex(); + sliceParticle2.emplace(Partition2->sliceByCached(o2::aod::femtobase::stored::fColId, collision2.globalIndex(), cache)); + } + + ++windowSizeRaw; + if (collision1.magField() != collision2.magField() || collision2.magField() != collision3.magField() || collision1.magField() != collision3.magField()) { + LOG(warn) << "Tried mixing events with different magnetic field."; continue; } + CtrManager.setMagField(collision1.magField()); - auto sliceParticle1 = Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache); - auto sliceParticle2 = Partition2->sliceByCached(o2::aod::femtobase::stored::fColId, collision2.globalIndex(), cache); + auto sliceParticle3 = Partition3->sliceByCached(o2::aod::femtobase::stored::fColId, collision3.globalIndex(), cache); - if (sliceParticle1.size() == 0 || sliceParticle2.size() == 0 || sliceParticle3.size() == 0) { + + TripletHistManager.resetTrackedParticlesPerEvent(); + + if (sliceParticle1->size() == 0 || sliceParticle2->size() == 0 || sliceParticle3.size() == 0) { + TripletHistManager.fillMixingQaMePerEvent(); continue; } - for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(sliceParticle1, sliceParticle2, sliceParticle3))) { - // pair cleaning - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable)) { + + bool hasValidTriplet = false; + TripletHistManager.fillMixingQaMe(collision1, collision2, collision3); + + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(*sliceParticle1, *sliceParticle2, sliceParticle3))) { + + // compute the kinematic (Q3) for this triplet + TripletHistManager.setTriplet(p1, p2, p3, collision1, collision2, collision3); + + // triplet cleaning + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { continue; } - // Close pair rejection - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { + + // Close triplet rejection; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { continue; } - TripletHistManager.setTriplet(p1, p2, p3, collision1, collision2, collision3); - CtrManager.fill(TripletHistManager.getQ3()); + if (TripletHistManager.checkTripletCuts()) { + hasValidTriplet = true; + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); + ParticleHistManagerMe3.template fill(p3, TrackTable); } } + + if (hasValidTriplet) { + ++windowSizeEffective; + } + + TripletHistManager.fillMixingQaMePerEvent(); + } + + // final window + if (windowSizeRaw > 0) { + TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } } @@ -490,7 +895,15 @@ template + typename T13, + typename T14, + typename T15, + typename T16, + typename T17, + typename T18, + typename T19, + typename T20, + typename T21> void processMixedEvent(T1 const& Collisions, T2 const& mcCollisions, T3& Partition1, @@ -498,46 +911,264 @@ void processMixedEvent(T1 const& Collisions, T5& Partition3, T6 const& TrackTable, T7 const& mcParticles, - T8& cache, - T9 const& policy, - T10 const& depth, - T11& TripletHistManager, - T12& CtrManager, - T13& TcManager) + T8 const& mcMothers, + T9 const& mcPartonicMothers, + T10& cache, + T11 const& policy, + T12 const& depth, + T13& TripletHistManager, + T14& Cleaner1, + T15& Cleaner2, + T16& Cleaner3, + T17& CtrManager, + T18& TcManager, + int eventStride, + T19& ParticleHistManagerMe1, + T20& ParticleHistManagerMe2, + T21& ParticleHistManagerMe3) { + int64_t lastCollisionIndex1 = -1; + int64_t lastCollisionIndex2 = -1; + int windowSizeRaw = 0; + int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; + + // collision1 stays fixed across the outer mixing window, and collision2 stays fixed across each inner sub-window (it only advances once collision3 wraps + // back to its start Both slices are therefore materialized once per window/sub-window and reused, instead of being re-sliced, i.e. a fresh arrow Slice + selection copy + std::optionalsliceByCached(o2::aod::femtobase::stored::fColId, 0, cache))> sliceParticle1; + std::optionalsliceByCached(o2::aod::femtobase::stored::fColId, 0, cache))> sliceParticle2; + for (auto const& [collision1, collision2, collision3] : o2::soa::selfCombinations(policy, depth, -1, Collisions, Collisions, Collisions)) { + + // outer window + if (collision1.globalIndex() != lastCollisionIndex1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { + TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } + windowSizeRaw = 0; + windowSizeEffective = 0; + lastCollisionIndex1 = collision1.globalIndex(); + lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; + } + + // inner sub-window + if (collision2.globalIndex() != lastCollisionIndex2) { + lastCollisionIndex2 = collision2.globalIndex(); + sliceParticle2.emplace(Partition2->sliceByCached(o2::aod::femtobase::stored::fColId, collision2.globalIndex(), cache)); + } + + ++windowSizeRaw; + if (collision1.magField() != collision2.magField() || collision2.magField() != collision3.magField() || collision1.magField() != collision3.magField()) { + LOG(warn) << "Tried mixing events with different magnetic field."; continue; } + CtrManager.setMagField(collision1.magField()); - auto sliceParticle1 = Partition1->sliceByCached(o2::aod::femtobase::stored::fColId, collision1.globalIndex(), cache); - auto sliceParticle2 = Partition2->sliceByCached(o2::aod::femtobase::stored::fColId, collision2.globalIndex(), cache); + auto sliceParticle3 = Partition3->sliceByCached(o2::aod::femtobase::stored::fColId, collision3.globalIndex(), cache); - if (sliceParticle1.size() == 0 || sliceParticle2.size() == 0 || sliceParticle3.size() == 0) { + + TripletHistManager.resetTrackedParticlesPerEvent(); + + if (sliceParticle1->size() == 0 || sliceParticle2->size() == 0 || sliceParticle3.size() == 0) { + TripletHistManager.fillMixingQaMePerEvent(); continue; } - for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(sliceParticle1, sliceParticle2, sliceParticle3))) { - // pair cleaning - if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable)) { + + bool hasValidTriplet = false; + TripletHistManager.fillMixingQaMe(collision1, collision2, collision3); + + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(*sliceParticle1, *sliceParticle2, sliceParticle3))) { + + // particle cleaning + if (!Cleaner1.isClean(p1, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner2.isClean(p2, mcParticles, mcMothers, mcPartonicMothers) || + !Cleaner3.isClean(p3, mcParticles, mcMothers, mcPartonicMothers)) { + continue; + } + + // compute the kinematic (Q3) for this triplet + TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, collision1, collision2, collision3, mcCollisions); + + // triplet cleaning + if (!TcManager.isCleanTriplet(p1, p2, p3, TrackTable, mcParticles, mcPartonicMothers, TripletHistManager)) { continue; } - // Close pair rejection - CtrManager.setTriplet(p1, p2, p3, TrackTable); - if (CtrManager.isCloseTriplet()) { + + // Close triplet rejection; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TrackTable, TripletHistManager)) { continue; } - TripletHistManager.setTripletMc(p1, p2, p3, mcParticles, collision1, collision2, collision3, mcCollisions); - CtrManager.fill(TripletHistManager.getQ3()); + if (TripletHistManager.checkTripletCuts()) { + hasValidTriplet = true; + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.template fill(p1, TrackTable); + ParticleHistManagerMe2.template fill(p2, TrackTable); + ParticleHistManagerMe3.template fill(p3, TrackTable); } } + + if (hasValidTriplet) { + ++windowSizeEffective; + } + + TripletHistManager.fillMixingQaMePerEvent(); + } + + // --- final window --- + if (windowSizeRaw > 0) { + TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } +} + +// process mixed event for triplets of generated particles (mc truth only) +// mc collisions carry no magnetic field, so there is no magnetic field check here +template +void processMixedEventMcTruth(T1 const& McCollisions, + T2& Partition1, + T3& Partition2, + T4& Partition3, + T5 const& /*mcParticles*/, + T6 const& mcMothers, + T7 const& mcPartonicMothers, + T8& cache, + T9 const& policy, + T10 const& depth, + T11& TripletHistManager, + T12& ParticleCleaner1, + T13& ParticleCleaner2, + T14& ParticleCleaner3, + T15& CtrManager, + T16& TcManager, + int eventStride, + T17& ParticleHistManagerMe1, + T18& ParticleHistManagerMe2, + T19& ParticleHistManagerMe3) +{ + int64_t lastCollisionIndex1 = -1; + int64_t lastCollisionIndex2 = -1; + int windowSizeRaw = 0; + int windowSizeEffective = 0; + // counts actual outer-anchor windows encountered (not raw collision IDs, which may have gaps + // from upstream event selection) so "every Nth anchor" is evenly spaced regardless of such gaps + int64_t windowCounter = -1; + bool skipThisWindow = false; + + std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle1; + std::optionalsliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, 0, cache))> sliceParticle2; + + for (auto const& [collision1, collision2, collision3] : o2::soa::selfCombinations(policy, depth, -1, McCollisions, McCollisions, McCollisions)) { + + // outer window + if (collision1.globalIndex() != lastCollisionIndex1) { + if (lastCollisionIndex1 != -1 && !skipThisWindow) { + TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); + } + windowSizeRaw = 0; + windowSizeEffective = 0; + lastCollisionIndex1 = collision1.globalIndex(); + lastCollisionIndex2 = -1; // force sliceParticle2 to refresh below + ++windowCounter; + // skip entire outer-anchor windows to reduce mixed-event combinatorics without truncating + // any single event's particle content -- every anchor that IS processed still gets its + // full `depth` worth of mixing partners + skipThisWindow = eventStride > 1 && windowCounter % eventStride != 0; + if (!skipThisWindow) { + sliceParticle1.emplace(Partition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision1.globalIndex(), cache)); + } + } + + if (skipThisWindow) { + continue; + } + + // inner sub-window + if (collision2.globalIndex() != lastCollisionIndex2) { + lastCollisionIndex2 = collision2.globalIndex(); + sliceParticle2.emplace(Partition2->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision2.globalIndex(), cache)); + } + + ++windowSizeRaw; + + auto sliceParticle3 = Partition3->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, collision3.globalIndex(), cache); + + TripletHistManager.resetTrackedParticlesPerEvent(); + + if (sliceParticle1->size() == 0 || sliceParticle2->size() == 0 || sliceParticle3.size() == 0) { + TripletHistManager.fillMixingQaMePerEvent(); + continue; + } + + bool hasValidTriplet = false; + TripletHistManager.fillMixingQaMe(collision1, collision2, collision3); + + for (auto const& [p1, p2, p3] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(*sliceParticle1, *sliceParticle2, sliceParticle3))) { + + if (!ParticleCleaner1.isClean(p1, mcMothers, mcPartonicMothers) || + !ParticleCleaner2.isClean(p2, mcMothers, mcPartonicMothers) || + !ParticleCleaner3.isClean(p3, mcMothers, mcPartonicMothers)) { + continue; + } + + // compute the kinematic (Q3) for this triplet + TripletHistManager.setTripletMcTruth(p1, p2, p3, collision1, collision2, collision3); + + if (!TcManager.isCleanTriplet(p1, p2, p3, mcPartonicMothers, TripletHistManager)) { + continue; + } + + // Close triplet rejection; fills deta-dphi/kinematic histograms internally + if (CtrManager.isCloseTriplet(p1, p2, p3, TripletHistManager)) { + continue; + } + + if (TripletHistManager.checkTripletCutsMcTruth()) { + hasValidTriplet = true; + TripletHistManager.trackParticlesPerEvent(p1, p2, p3); + TripletHistManager.template fill(); + // single-particle histograms for particles that make it into an accepted mixed-event triplet + ParticleHistManagerMe1.fill(p1, mcMothers, mcPartonicMothers); + ParticleHistManagerMe2.fill(p2, mcMothers, mcPartonicMothers); + ParticleHistManagerMe3.fill(p3, mcMothers, mcPartonicMothers); + } + } + + if (hasValidTriplet) { + ++windowSizeEffective; + } + + TripletHistManager.fillMixingQaMePerEvent(); + } + + // final window + if (windowSizeRaw > 0) { + TripletHistManager.fillMixingQaMePerMixingBin(windowSizeRaw, windowSizeEffective); } } -} // namespace tripletprocesshelpers -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::tripletprocesshelpers #endif // PWGCF_FEMTO_CORE_TRIPLETPROCESSHELPERS_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h new file mode 100644 index 00000000000..5883ca23411 --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceBuilder.h @@ -0,0 +1,246 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonResonanceBuilder.h +/// \brief two photon resonance builder (pi0, eta, ... -> gamma gamma) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include + +namespace o2::analysis::femto::twophotonresonancebuilder +{ +template +struct ConfTwoPhotonResonanceFilters : o2::framework::ConfigurableGroup { + std::string prefix = Prefix; + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; + o2::framework::Configurable massMin{"massMin", 0.f, "Minimum invariant mass for the resonance"}; + o2::framework::Configurable massMax{"massMax", 1.f, "Maximum invariant mass for the resonance"}; +}; +constexpr const char PrefixPi0Filters[] = "Pi0Filters1"; +constexpr const char PrefixEtaFilters[] = "EtaFilters1"; + +using ConfPi0Filters = ConfTwoPhotonResonanceFilters; +using ConfEtaFilters = ConfTwoPhotonResonanceFilters; + +// selection used downstream (QA/pairing) to re-select already produced resonances by mass window + +// daughter mask. unlike TWOTRACKRESONANCE_DEFAULT_SELECTION there is no pos/neg split and no +// momentum-threshold PID switch: photon daughters are unordered and have no momentum-dependent PID +// regime. there is also just a single dauMask (not one per leg): both legs are plain photons with +// the same selection requirement, so the candidate only ever stores/needs the AND of both legs' +// masks (see femtotwophotonresonances::MaskDau in FemtoTables.h) -- no per-leg distinction to select on. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_DEFAULT_SELECTION(defaultMassMin, defaultMassMax, defaultPdgCodeAbs) \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 10.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for the resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for the resonance"}; \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCodeAbs), "Absolute PDG code of the resonance, used to set its mass for kstar/kt/mt (not the reconstructed two-photon mass)"}; \ + o2::framework::Configurable dauMask{"dauMask", 0, "Bitmask required for both photon daughters"}; + +struct ConfPi0Selection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Selection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.1f, 0.17f, kPi0) +}; + +struct ConfEtaSelection : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaSelection1"); + TWOPHOTONRESONANCE_DEFAULT_SELECTION(0.4f, 0.7f, o2::constants::physics::kEta) +}; + +#undef TWOPHOTONRESONANCE_DEFAULT_SELECTION + +struct TwoPhotonResonanceBuilderProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedPi0s; + o2::framework::Produces producedPi0Masks; + o2::framework::Produces producedEtas; + o2::framework::Produces producedEtaMasks; +}; + +struct ConfTwoPhotonResonanceTables : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoPhotonResonanceTables"); + o2::framework::Configurable producePi0s{"producePi0s", -1, "Produce Pi0s (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable producePi0Masks{"producePi0Masks", -1, "Produce Pi0Masks (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtas{"produceEtas", -1, "Produce Etas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceEtaMasks{"produceEtaMasks", -1, "Produce EtaMasks (-1: auto; 0 off; 1 on)"}; +}; + +/// \brief Builder for resonances reconstructed from two PCM photons (pi0, eta, ... -> gamma gamma) +/// +/// Unlike TwoTrackResonanceBuilder, there is no pos/neg daughter distinction (photons are their own +/// antiparticle) and no momentum-threshold PID switch (photon selection has no momentum-dependent PID +/// regime the way track PID does), so daughter combinatorics run over a single photon partition using +/// CombinationsStrictlyUpperIndexPolicy to avoid self-pairing/double-counting. +template +class TwoPhotonResonanceBuilder +{ + public: + TwoPhotonResonanceBuilder() = default; + ~TwoPhotonResonanceBuilder() = default; + + template + void init(T1& confFilter, T2& confTable, T3& initContext) + { + mMassMin = confFilter.massMin.value; + mMassMax = confFilter.massMax.value; + mPtMin = confFilter.ptMin.value; + mPtMax = confFilter.ptMax.value; + mEtaMin = confFilter.etaMin.value; + mEtaMax = confFilter.etaMax.value; + mPhiMin = confFilter.phiMin.value; + mPhiMax = confFilter.phiMax.value; + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + LOG(info) << "Initialize femto Pi0 builder..."; + mProducePi0s = utils::enableTable("FPi0s_001", confTable.producePi0s.value, initContext); + mProducePi0Masks = utils::enableTable("FPi0Masks_001", confTable.producePi0Masks.value, initContext); + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + LOG(info) << "Initialize femto Eta builder..."; + mProduceEtas = utils::enableTable("FEtas_001", confTable.produceEtas.value, initContext); + mProduceEtaMasks = utils::enableTable("FEtaMasks_001", confTable.produceEtaMasks.value, initContext); + } + + if (mProducePi0s || mProducePi0Masks || mProduceEtas || mProduceEtaMasks) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + LOG(info) << "Initialization done..."; + } + + template + void fillResonances(T1 const& col, T2& resonanceProducts, T3& photonPartition, T4& cache) + { + if (!mFillAnyTable) { + return; + } + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + for (auto const& [dau1, dau2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(photonSlice, photonSlice))) { + this->fillResonance(col, dau1, dau2, resonanceProducts); + } + } + + private: + template + void reconstructResonance(T1 const& dau1, T2 const& dau2) + { + ROOT::Math::PtEtaPhiMVector vecDau1{dau1.pt(), dau1.eta(), dau1.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecDau2{dau2.pt(), dau2.eta(), dau2.phi(), 0.f}; + ROOT::Math::PtEtaPhiMVector vecResonance = vecDau1 + vecDau2; + + mPt = vecResonance.Pt(); + mEta = vecResonance.Eta(); + mPhi = RecoDecay::constrainAngle(vecResonance.Phi()); + mMass = vecResonance.M(); + } + + [[nodiscard]] bool checkFilters() const + { + return ((mMass > mMassMin && mMass < mMassMax) && + (mPt > mPtMin && mPt < mPtMax) && + (mEta > mEtaMin && mEta < mEtaMax) && + (mPhi >= mPhiMin && mPhi < mPhiMax)); + } + + template + void fillResonance(T1 const& col, T2 const& dau1, T3 const& dau2, T4& resonanceProducts) + { + reconstructResonance(dau1, dau2); + if (!checkFilters()) { + return; + } + + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kPi0)) { + if (mProducePi0s) { + resonanceProducts.producedPi0s(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProducePi0Masks) { + resonanceProducts.producedPi0Masks(dau1.mask() & dau2.mask()); + } + } + if constexpr (modes::isEqual(resoType, modes::TwoPhotonResonance::kEta)) { + if (mProduceEtas) { + resonanceProducts.producedEtas(col.globalIndex(), + mPt, + mEta, + mPhi, + mMass, + dau1.globalIndex(), + dau2.globalIndex()); + } + if (mProduceEtaMasks) { + resonanceProducts.producedEtaMasks(dau1.mask() & dau2.mask()); + } + } + } + + // cached kinematics of the resonance + float mPt = 0; + float mEta = 0; + float mPhi = 0; + float mMass = 0; + + float mMassMin = 0.f; + float mMassMax = 0.f; + float mPtMin = 0.f; + float mPtMax = 0.f; + float mEtaMin = 0.f; + float mEtaMax = 0.f; + float mPhiMin = 0.f; + float mPhiMax = 0.f; + + bool mFillAnyTable = false; + bool mProducePi0s = false; + bool mProducePi0Masks = false; + bool mProduceEtas = false; + bool mProduceEtaMasks = false; +}; + +} // namespace o2::analysis::femto::twophotonresonancebuilder +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h new file mode 100644 index 00000000000..52e19c78bba --- /dev/null +++ b/PWGCF/Femto/Core/twoPhotonResonanceHistManager.h @@ -0,0 +1,214 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonResonanceHistManager.h +/// \brief histogram manager for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ +#define PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ + +#include "PWGCF/Femto/Core/histManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonHistManager.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::analysis::femto::twophotonresonancehistmanager +{ +enum TwoPhotonResonanceHist { + // analysis + kPt, + kEta, + kPhi, + kMass, + kPtVsMass, + // 2d qa + kPtVsEta, + kPtVsPhi, + kPhiVsEta, + kTwoPhotonResonanceHistLast +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; + +struct ConfPi0Binning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Pi0Binning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.f, 0.3f) +}; + +struct ConfEtaBinning : o2::framework::ConfigurableGroup { + std::string prefix = std::string("EtaBinning"); + TWOPHOTONRESONANCE_DEFAULT_BINNING(0.2f, 0.9f) +}; +#undef TWOPHOTONRESONANCE_DEFAULT_BINNING + +constexpr std::array, kTwoPhotonResonanceHistLast> HistTable = { + {{kPt, o2::framework::HistType::kTH1F, "hPt", "Transverse Momentum; p_{T} (GeV/#it{c}); Entries"}, + {kEta, o2::framework::HistType::kTH1F, "hEta", "Pseudorapdity; #eta; Entries"}, + {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, + {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant mass; m_{#gamma#gamma} (GeV/#it{c}^{2}); Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m_{#gamma#gamma} (GeV/#it{c}^{2})"}, + {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, + {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi;p_{T} (GeV/#it{c});#varphi"}, + {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}}, +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOPHOTONRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +template +std::map> makeTwoPhotonResonanceHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; +} + +template +auto makeTwoPhotonResonanceQaHistSpecMap(const T& confBinningAnalysis) +{ + return std::map>{ + TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TWOPHOTONRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; +} + +#undef TWOPHOTONRESONANCE_HIST_ANALYSIS_MAP +#undef TWOPHOTONRESONANCE_HIST_QA_MAP + +// used only by the standalone QA task +constexpr char PrefixPi0[] = "Pi0/"; +constexpr char PrefixEta[] = "Eta/"; +// same-event sibling, used only by a pair builder's same-event resonance manager +constexpr char PrefixPi0Se[] = "Pi0/SE/"; +constexpr char PrefixEtaSe[] = "Eta/SE/"; +// mixed-event sibling, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixPi0Me[] = "Pi0/ME/"; +constexpr char PrefixEtaMe[] = "Eta/ME/"; + +constexpr std::string_view AnalysisDir = "Analysis/"; +constexpr std::string_view QaDir = "QA/"; + +/// \class TwoPhotonResonanceHistManager +/// \brief Histogram manager for resonances built from two PCM photons (reco + QA only, no MC). +/// Both daughters share the same PhotonHistManager-based sub-manager treatment: there is no +/// pos/neg distinction, nor a PDG-dependent daughter lookup the way TwoTrackResonanceHistManager +/// needs (photon daughters are always massless, regardless of the mother species). +template +class TwoPhotonResonanceHistManager +{ + public: + TwoPhotonResonanceHistManager() = default; + ~TwoPhotonResonanceHistManager() = default; + + template + void init(o2::framework::HistogramRegistry* registry, + std::map> const& ResoSpecs, + std::map> const& DauSpecs, + T1 const& ConfDauQaBinning) + { + mHistogramRegistry = registry; + mDau1Manager.template init(registry, DauSpecs, ConfDauQaBinning); + mDau2Manager.template init(registry, DauSpecs, ConfDauQaBinning); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + initAnalysis(ResoSpecs); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + initQa(ResoSpecs); + } + } + + template + void fill(T1 const& resonance, T2 const& photons) + { + auto dau1 = photons.rawIteratorAt(resonance.dau1Id() - photons.offset()); + mDau1Manager.template fill(dau1); + auto dau2 = photons.rawIteratorAt(resonance.dau2Id() - photons.offset()); + mDau2Manager.template fill(dau2); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { + fillAnalysis(resonance); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + fillQa(resonance); + } + } + + private: + void initAnalysis(std::map> const& ResoSpecs) + { + std::string analysisDir = std::string(resoPrefix) + std::string(AnalysisDir); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPt, HistTable), getHistDesc(kPt, HistTable), getHistType(kPt, HistTable), {ResoSpecs.at(kPt)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kEta, HistTable), getHistDesc(kEta, HistTable), getHistType(kEta, HistTable), {ResoSpecs.at(kEta)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); + } + + void initQa(std::map> const& ResoSpecs) + { + std::string qaDir = std::string(resoPrefix) + std::string(QaDir); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); + } + + template + void fillAnalysis(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPt, HistTable)), resonance.pt()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), resonance.mass()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); + } + + template + void fillQa(T const& resonance) + { + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); + } + + o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; + photonhistmanager::PhotonHistManager mDau1Manager; + photonhistmanager::PhotonHistManager mDau2Manager; +}; +} // namespace o2::analysis::femto::twophotonresonancehistmanager +#endif // PWGCF_FEMTO_CORE_TWOPHOTONRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h index 21ca5ff343c..26228021eea 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h @@ -33,15 +33,12 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include +#include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::twotrackresonancebuilder { -namespace twotrackresonancebuilder -{ - -template +template struct ConfTwoTrackResonanceFilters : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::Configurable ptMin{"ptMin", 0.2f, "Minimum pT"}; @@ -61,40 +58,61 @@ using ConfRhoFilters = ConfTwoTrackResonanceFilters; using ConfPhiFilters = ConfTwoTrackResonanceFilters; using ConfKstarFilters = ConfTwoTrackResonanceFilters; -#define TWOTRACKRESONANCE_DEFAULT_SELECTION(defaultPdgCode, defaultMassMin, defaultMassMax) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", defaultPdgCode, "Resonance PDG code. Set sign to minus 1 for antiparticle"}; \ - o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ - o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; \ - o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ - o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ - o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ - o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", defaultMassMin, "Minimum invariant mass for Resonance"}; \ - o2::framework::Configurable massMax{"massMax", defaultMassMax, "Maximum invariant mass for Resonance"}; \ - o2::framework::Configurable posDauMaskBelowThres{"posDauMaskBelowThres", 0x10u, "Bitmask for positive daughter below threshold"}; \ - o2::framework::Configurable posDauMaskAboveThres{"posDauMaskAboveThres", 0x8u, "Bitmask for positive daughter above threshold"}; \ - o2::framework::Configurable negDauMaskBelowThres{"negDauMaskBelowThres", 0x2u, "Bitmask for negative daughter below threshold"}; \ - o2::framework::Configurable negDauMaskAboveThres{"negDauMaskAboveThres", 0x1u, "Bitmask for negative daughter above threshold"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_DEFAULT_SELECTION(defaultPdgCode, defaultMassMin, defaultMassMax) \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "Resonance PDG code. Set sign to minus 1 for antiparticle"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 6.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -0.9f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 0.9f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass for Resonance"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass for Resonance"}; + +// phi -> K+ K-, rho0 -> pi+ pi-: both daughters are the same species, so a single pair of mask +// thresholds is enough, applied to both the positive and negative daughter alike +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_SAME_SPECIES_MASK \ + o2::framework::Configurable dauMaskBelowThres{"dauMaskBelowThres", 0x10u, "Bitmask for both daughters below threshold"}; \ + o2::framework::Configurable dauMaskAboveThres{"dauMaskAboveThres", 0x8u, "Bitmask for both daughters above threshold"}; struct ConfPhiSelection : o2::framework::ConfigurableGroup { std::string prefix = std::string("PhiSelection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(333, 0.95f, 1.05f) - o2::framework::Configurable sign{"sign", 1, "Dummy value for compatability"}; + TWOTRACKRESONANCE_SAME_SPECIES_MASK + o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; struct ConfRho0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Rho0Selection"); TWOTRACKRESONANCE_DEFAULT_SELECTION(113, 0.7f, 0.84f) - o2::framework::Configurable sign{"sign", 1, "Dummy value for compatability"}; + TWOTRACKRESONANCE_SAME_SPECIES_MASK + o2::framework::Configurable sign{"sign", 1, "Dummy value for compatibility"}; }; +#undef TWOTRACKRESONANCE_SAME_SPECIES_MASK + +// K*0 -> K+ pi-, K*0bar -> K- pi+: the physical pos/neg daughter's species flips with the +// candidate's charge sign, so the masks are named by species (kaon/pion) rather than by charge -- +// MAKE_RESONANCE_1_PARTITON resolves which stored column (maskPosDau/maskNegDau) each applies to +// from the candidate's own signedPt, so the user never has to reason about pos/neg <-> kaon/pion +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_KSTAR_MASK \ + o2::framework::Configurable kaonDauMaskBelowThres{"kaonDauMaskBelowThres", 0x10u, "Bitmask for the kaon daughter below threshold"}; \ + o2::framework::Configurable kaonDauMaskAboveThres{"kaonDauMaskAboveThres", 0x8u, "Bitmask for the kaon daughter above threshold"}; \ + o2::framework::Configurable pionDauMaskBelowThres{"pionDauMaskBelowThres", 0x2u, "Bitmask for the pion daughter below threshold"}; \ + o2::framework::Configurable pionDauMaskAboveThres{"pionDauMaskAboveThres", 0x1u, "Bitmask for the pion daughter above threshold"}; + struct ConfKstar0Selection : o2::framework::ConfigurableGroup { std::string prefix = std::string("Kstar0Selection"); o2::framework::Configurable sign{"sign", 1, "Sign (+1 for Kstar0 and -1 for Kstar0Bar) "}; TWOTRACKRESONANCE_DEFAULT_SELECTION(313, 0.8f, 1.0f) + TWOTRACKRESONANCE_KSTAR_MASK }; #undef TWOTRACKRESONANCE_DEFAULT_SELECTION +#undef TWOTRACKRESONANCE_KSTAR_MASK struct TwoTrackResonanceBuilderProducts : o2::framework::ProducesGroup { o2::framework::Produces producedPhis; @@ -109,7 +127,7 @@ struct ConfTwoTrackResonanceTables : o2::framework::ConfigurableGroup { std::string prefix = std::string("TwoTrackResonanceTables"); o2::framework::Configurable producePhis{"producePhis", -1, "Produce Phis (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable producePhiMasks{"producePhiMasks", -1, "Produce PhiMasks (-1: auto; 0 off; 1 on)"}; - o2::framework::Configurable produceKstar0s{"produceKstar0s", -1, "Produce K0stars (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceKstar0s{"produceKstar0s", -1, "Produce Kstar0s (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceKstar0Masks{"produceKstar0Masks", -1, "Produce Kstar0Masks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceRho0s{"produceRho0s", -1, "Produce Rho0s (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceRho0Masks{"produceRho0Masks", -1, "Produce Rho0Masks (-1: auto; 0 off; 1 on)"}; @@ -185,7 +203,6 @@ class TwoTrackResonanceBuilder } auto posDaughterSlice = posDaughterPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); auto negDaughterSlice = negDaughterPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - // LOG(warn) << "Size: " << posDaughterSlice.size() << " " << negDaughterSlice.size(); for (auto const& [posDaughter, negDaughter] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posDaughterSlice, negDaughterSlice))) { this->fillResonance(col, posDaughter, negDaughter, resonanceProducts); } @@ -205,12 +222,12 @@ class TwoTrackResonanceBuilder mMass = vecResonance.M(); } - bool checkFilters() const + [[nodiscard]] bool checkFilters() const { return ((mMass > mMassMin && mMass < mMassMax) && (mPt > mPtMin && mPt < mPtMax) && (mEta > mEtaMin && mEta < mEtaMax) && - (mPhi > mPhiMin && mPhi < mPhiMax)); + (mPhi >= mPhiMin && mPhi < mPhiMax)); } template @@ -278,7 +295,7 @@ class TwoTrackResonanceBuilder if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { if (mProduceKstar0s) { resonanceProducts.producedKstars(col.globalIndex(), - mPt, + -1.f * mPt, mEta, mPhi, mMass, @@ -325,6 +342,120 @@ class TwoTrackResonanceBuilder }; // namespace twotrackresonancebuilder -} // namespace twotrackresonancebuilder -} // namespace o2::analysis::femto +struct ConfTwoTrackResonanceTablesDerivedToDerived : o2::framework::ConfigurableGroup { + std::string prefix = std::string("TwoTrackResonanceTables"); + o2::framework::Configurable limitRho0{"limitRho0", 1, "Require at least this many Rho0s in the collision. Set to 0 to skip Rho0s entirely (not written to output)."}; + o2::framework::Configurable limitPhi{"limitPhi", 0, "Require at least this many Phis in the collision. Set to 0 to skip Phis entirely (not written to output)."}; +}; + +struct TwoTrackResonanceBuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { + o2::framework::Produces producedRho0s; + o2::framework::Produces producedRho0Masks; + o2::framework::Produces producedPhis; + o2::framework::Produces producedPhiMasks; +}; + +class TwoTrackResonanceBuilderDerivedToDerived +{ + public: + template + void init(T& config) + { + mLimitRho0 = config.limitRho0.value; + mLimitPhi = config.limitPhi.value; + + if (mLimitRho0 < 0 || mLimitPhi < 0) { + LOG(fatal) << "Two-track resonance limits must be non-negative (got " << mLimitRho0 << " and " << mLimitPhi << "). Breaking..."; + } + if (mLimitRho0 == 0 && mLimitPhi == 0) { + LOG(fatal) << "Both Rho0 limit and Phi limit are 0. Breaking..."; + } + } + + template + bool collisionHasTooFewRho0s(T1 const& col, T2 const& /*rho0Table*/, T3& partitionRho0, T4& cache) + { + if (mLimitRho0 == 0) { // Rho0s disabled, cannot reject on them + return false; + } + auto rho0Slice = partitionRho0->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return rho0Slice.size() < static_cast(mLimitRho0); + } + + template + bool collisionHasTooFewPhis(T1 const& col, T2 const& /*phiTable*/, T3& partitionPhi, T4& cache) + { + if (mLimitPhi == 0) { // Phis disabled, cannot reject on them + return false; + } + auto phiSlice = partitionPhi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return phiSlice.size() < static_cast(mLimitPhi); + } + + template + void processRho0s(T1 const& col, T2 const& /*rho0Table*/, T3 const& oldTrackTable, T4& partitionRho0, T5& trackBuilder, T6& cache, T7& newRho0Table, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitRho0 == 0) { // Rho0s disabled + return; + } + + auto rho0Slice = partitionRho0->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& rho0 : rho0Slice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(rho0.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(rho0.negDauId(), oldTrackTable)); + + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newRho0Table.producedRho0s(newCollisionTable.producedCollision.lastIndex(), + rho0.pt(), + rho0.eta(), + rho0.phi(), + rho0.mass(), + posDaughterIndex, + negDaughterIndex); + newRho0Table.producedRho0Masks(rho0.maskPosDau(), + rho0.posDauHasHighMomentum(), + rho0.maskNegDau(), + rho0.negDauHasHighMomentum()); + } + } + + template + void processPhis(T1 const& col, T2 const& /*phiTable*/, T3 const& oldTrackTable, T4& partitionPhi, T5& trackBuilder, T6& cache, T7& newPhiTable, T8& newTrackTable, T9& newCollisionTable) + { + if (mLimitPhi == 0) { // Phis disabled + return; + } + + auto phiSlice = partitionPhi->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + + for (auto const& phi : phiSlice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(phi.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(phi.negDauId(), oldTrackTable)); + + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + + newPhiTable.producedPhis(newCollisionTable.producedCollision.lastIndex(), + phi.pt(), + phi.eta(), + phi.phi(), + phi.mass(), + posDaughterIndex, + negDaughterIndex); + newPhiTable.producedPhiMasks(phi.maskPosDau(), + phi.posDauHasHighMomentum(), + phi.maskNegDau(), + phi.negDauHasHighMomentum()); + } + } + + private: + int mLimitRho0 = 0; + int mLimitPhi = 0; +}; + +} // namespace o2::analysis::femto::twotrackresonancebuilder #endif // PWGCF_FEMTO_CORE_TWOTRACKRESONANCEBUILDER_H_ diff --git a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h index a8171a1b5fd..95cdd37c01d 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceHistManager.h +++ b/PWGCF/Femto/Core/twoTrackResonanceHistManager.h @@ -35,9 +35,7 @@ #include #include -namespace o2::analysis::femto -{ -namespace twotrackresonancehistmanager +namespace o2::analysis::femto::twotrackresonancehistmanager { // enum for track histograms enum TwoTrackResonanceHist { @@ -46,20 +44,21 @@ enum TwoTrackResonanceHist { kEta, kPhi, kMass, + kPtVsMass, kSign, // 2d qa kPtVsEta, kPtVsPhi, kPhiVsEta, - kPtVsMass, kTwoTrackResonanceHistLast }; -#define TWOTRACKRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ - o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ - o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ - o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ - o2::framework::ConfigurableAxis mass{"mass", {{200, defaultMassMin, defaultMassMax}}, "Mass"}; \ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; struct ConfPhiBinning : o2::framework::ConfigurableGroup { @@ -89,42 +88,59 @@ constexpr std::array, kTwoTrackReso {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "p_{T} vs invariant mass; p_{T} (GeV/#it{c}); m (GeV/#it{c}^{2})"}}}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define TWOTRACKRESONANCE_HIST_QA_MAP(conf) \ + {kPtVsEta, {(conf).pt, (conf).eta}}, \ + {kPtVsPhi, {(conf).pt, (conf).phi}}, \ + {kPhiVsEta, {(conf).phi, (conf).eta}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, + template std::map> makeTwoTrackResonanceHistSpecMap(const T& confBinningAnalysis) { return std::map>{ - {kPt, {confBinningAnalysis.pt}}, - {kEta, {confBinningAnalysis.eta}}, - {kPhi, {confBinningAnalysis.phi}}, - {kMass, {confBinningAnalysis.mass}}, - {kSign, {confBinningAnalysis.sign}}}; + TWOTRACKRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis)}; }; template auto makeTwoTrackResonanceQaHistSpecMap(const T& confBinningAnalysis) { return std::map>{ - {kPt, {confBinningAnalysis.pt}}, - {kEta, {confBinningAnalysis.eta}}, - {kPhi, {confBinningAnalysis.phi}}, - {kMass, {confBinningAnalysis.mass}}, - {kSign, {confBinningAnalysis.sign}}, - {kPtVsEta, {confBinningAnalysis.pt, confBinningAnalysis.eta}}, - {kPtVsPhi, {confBinningAnalysis.pt, confBinningAnalysis.phi}}, - {kPhiVsEta, {confBinningAnalysis.phi, confBinningAnalysis.eta}}, - {kPtVsMass, {confBinningAnalysis.pt, confBinningAnalysis.mass}}}; + TWOTRACKRESONANCE_HIST_ANALYSIS_MAP(confBinningAnalysis) + TWOTRACKRESONANCE_HIST_QA_MAP(confBinningAnalysis)}; }; +#undef TWOTRACKRESONANCE_HIST_ANALYSIS_MAP +#undef TWOTRACKRESONANCE_HIST_QA_MAP + +// used only by the standalone QA task constexpr char PrefixRho[] = "Rho0/"; constexpr char PrefixPhi[] = "Phi/"; constexpr char PrefixKstar[] = "Kstar0/"; +// same-event siblings, used only by a pair builder's same-event resonance manager +constexpr char PrefixRhoSe[] = "Rho0/SE/"; +constexpr char PrefixPhiSe[] = "Phi/SE/"; +constexpr char PrefixKstarSe[] = "Kstar0/SE/"; +// mixed-event siblings, used only by a pair builder's mixed-event resonance manager +constexpr char PrefixRhoMe[] = "Rho0/ME/"; +constexpr char PrefixPhiMe[] = "Phi/ME/"; +constexpr char PrefixKstarMe[] = "Kstar0/ME/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; -template class TwoTrackResonanceHistManager { @@ -161,16 +177,16 @@ class TwoTrackResonanceHistManager negDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); } else { mPdgCode = -1 * mPdgCode; - posDauPdgCodeAbs = std::abs(PDG_t::kKMinus); - negDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); + posDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); + negDauPdgCodeAbs = std::abs(PDG_t::kKMinus); } } else { - LOG(fatal) << "PDG code for V0 has to be either Lambda or K0short"; + LOG(fatal) << "PDG code for Resonance has to be either Phi, Rho or K*0"; } mPosDauManager.template init(registry, PosDauSpecs, absCharge, signPlus, posDauPdgCodeAbs); mNegDauManager.template init(registry, NegDauSpecs, absCharge, signMinus, negDauPdgCodeAbs); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { initAnalysis(ResoSpecs); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { @@ -209,8 +225,8 @@ class TwoTrackResonanceHistManager negDauPdgCodeAbs = std::abs(PDG_t::kPiMinus); } else { mPdgCode = -1 * mPdgCode; - posDauPdgCodeAbs = std::abs(PDG_t::kKMinus); - negDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); + posDauPdgCodeAbs = std::abs(PDG_t::kPiPlus); + negDauPdgCodeAbs = std::abs(PDG_t::kKMinus); } } else { LOG(fatal) << "PDG code for TwoTrackResonance has to be either Rho, Phi or K^0*(892)"; @@ -218,7 +234,7 @@ class TwoTrackResonanceHistManager mPosDauManager.template init(registry, PosDauSpecs, absCharge, signPlus, posDauPdgCodeAbs, ConfPosDauBinningQa); mNegDauManager.template init(registry, NegDauSpecs, absCharge, signMinus, negDauPdgCodeAbs, ConfNegDauBinningQa); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { initAnalysis(ResoSpecs); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { @@ -236,7 +252,7 @@ class TwoTrackResonanceHistManager mPosDauManager.template fill(posDaughter, tracks); auto negDaughter = tracks.rawIteratorAt(resonance.negDauId() - tracks.offset()); mNegDauManager.template fill(negDaughter, tracks); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { fillAnalysis(resonance); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { @@ -253,6 +269,7 @@ class TwoTrackResonanceHistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {ResoSpecs.at(kPhi)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {ResoSpecs.at(kMass)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kSign, HistTable), getHistDesc(kSign, HistTable), getHistType(kSign, HistTable), {ResoSpecs.at(kSign)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); } void initQa(std::map> const& ResoSpecs) { @@ -260,7 +277,6 @@ class TwoTrackResonanceHistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {ResoSpecs.at(kPtVsEta)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {ResoSpecs.at(kPtVsPhi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {ResoSpecs.at(kPhiVsEta)}); - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {ResoSpecs.at(kPtVsMass)}); } template @@ -270,6 +286,7 @@ class TwoTrackResonanceHistManager mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), resonance.eta()); mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), resonance.phi()); mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), resonance.mass()); + mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); if constexpr (modes::isEqual(reso, modes::TwoTrackResonance::kPhi) || modes::isEqual(reso, modes::TwoTrackResonance::kRho0)) { mHistogramRegistry->fill(HIST(resoPrefix) + HIST(AnalysisDir) + HIST(getHistName(kSign, HistTable)), 0); } @@ -284,7 +301,6 @@ class TwoTrackResonanceHistManager mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), resonance.pt(), resonance.eta()); mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), resonance.pt(), resonance.phi()); mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), resonance.phi(), resonance.eta()); - mHistogramRegistry->fill(HIST(resoPrefix) + HIST(QaDir) + HIST(getHistName(kPtVsMass, HistTable)), resonance.pt(), resonance.mass()); } o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; @@ -292,6 +308,5 @@ class TwoTrackResonanceHistManager trackhistmanager::TrackHistManager mNegDauManager; int mPdgCode = 0; }; -}; // namespace twotrackresonancehistmanager -}; // namespace o2::analysis::femto +}; // namespace o2::analysis::femto::twotrackresonancehistmanager #endif // PWGCF_FEMTO_CORE_TWOTRACKRESONANCEHISTMANAGER_H_ diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index 28b2b9c37af..154233ebfa4 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -33,15 +33,13 @@ #include #include #include +#include #include #include #include -namespace o2::analysis::femto +namespace o2::analysis::femto::v0builder { -namespace v0builder -{ - // filters applied in the producer task struct ConfV0Filters : o2::framework::ConfigurableGroup { std::string prefix = std::string("V0Filters"); @@ -53,73 +51,81 @@ struct ConfV0Filters : o2::framework::ConfigurableGroup { o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; o2::framework::Configurable massMinLambda{"massMinLambda", 1.f, "Minimum mass for Lambda hypothesis"}; o2::framework::Configurable massMaxLambda{"massMaxLambda", 1.2f, "Maximum mass for Lambda hypothesis"}; - o2::framework::Configurable massMinK0short{"massMinK0short", 0.45f, "Minimum mass for K0Short hypothesis"}; - o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.53f, "Maximum mass for K0Short hypothesis"}; - o2::framework::Configurable rejectMassMinLambda{"rejectMassMinLambda", 1.11f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMaxLambda{"rejectMassMaxLambda", 1.12f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMinK0short{"rejectMassMinK0short", 0.48f, "Minimum mass to rejection K0short hypothesis for Lambda candidates"}; - o2::framework::Configurable rejectMassMaxK0short{"rejectMassMaxK0short", 0.5f, "Maximum mass to rejection K0short hypothesis for Lambda candidates"}; + o2::framework::Configurable massMinK0short{"massMinK0short", 0.485f, "Minimum mass for K0Short hypothesis"}; + o2::framework::Configurable massMaxK0short{"massMaxK0short", 0.51f, "Maximum mass for K0Short hypothesis"}; }; // selections bits for all v0s -#define V0_DEFAULT_BITS \ - o2::framework::Configurable passThrough{"passThrough", false, "If true, all V0s are passed through. Bits for all selections are stored."}; \ - o2::framework::Configurable> dcaDauMax{"dcaDauMax", {1.5f}, "Maximum DCA between the daughters at decay vertex (cm)"}; \ - o2::framework::Configurable> cpaMin{"cpaMin", {0.99f}, "Minimum cosine of pointing angle"}; \ - o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ - o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ - o2::framework::Configurable> decayVtxMax{"decayVtxMax", {100.f}, "Maximum distance in x,y,z of the decay vertex from primary vertex (cm)"}; \ - o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ - o2::framework::Configurable> dauDcaMin{"dauDcaMin", {0.05f}, "Minimum DCA of the daughters from primary vertex (cm)"}; \ - o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define V0_DEFAULT_BITS \ + o2::framework::Configurable passThrough{"passThrough", false, "If true, all V0s are passed through. Bits for all selections are stored."}; \ + o2::framework::Configurable> dcaDauMax{"dcaDauMax", {1.5f}, "Maximum DCA between the daughters at V0 decay vertex (cm)"}; \ + o2::framework::Configurable> cpaMin{"cpaMin", {0.99f}, "Minimum cosine of pointing angle"}; \ + o2::framework::Configurable> transRadMin{"transRadMin", {0.2f}, "Minimum transverse radius (cm)"}; \ + o2::framework::Configurable> transRadMax{"transRadMax", {100.f}, "Maximum transverse radius (cm)"}; \ + o2::framework::Configurable> decayVtxMax{"decayVtxMax", {100.f}, "Maximum distance in x,y,z of the decay vertex from primary vertex (cm)"}; \ + o2::framework::Configurable> dauAbsEtaMax{"dauAbsEtaMax", {0.8f}, "Maximum |eta| for daughter tracks"}; \ + o2::framework::Configurable> dauAbsDcaxyMin{"dauAbsDcaxyMin", {0.05f}, "Minimum DCAxy of the daughters from primary vertex (cm)"}; \ + o2::framework::Configurable> dauTpcClustersMin{"dauTpcClustersMin", {80.f}, "Minimum number of TPC clusters for daughter tracks"}; \ + o2::framework::Configurable requireTof{"requireTof", false, "If true, the TOF PID cut is mandatory. For daughters with a TOF signal, the candidate is rejected on failure. For daughters without a TOF signal, the candidate is rejected unless keepTracksWithoutTof is true"}; \ + o2::framework::Configurable keepTracksWithoutTof{"keepTracksWithoutTof", true, "If true, daughters without a TOF signal pass the TOF PID cut unconditionally, overriding requireTof. If false, daughters without a TOF signal fail the TOF cut (and are rejected if requireTof is true)"}; // derived selection bits for lambda struct ConfLambdaBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("LambdaBits"); V0_DEFAULT_BITS - o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; - o2::framework::Configurable> posDauTpcProton{"posDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC for positive daughter tracks"}; - o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; - o2::framework::Configurable> negDauTpcProton{"negDauTpcProton", {5.f}, "Maximum |nsimga_Proton| TPC negative for daughter tracks"}; + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> posDauTpcProton{"posDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> negDauTpcProton{"negDauTpcProton", {5.f}, "Maximum |nsigma_Proton| TPC for negative daughter tracks"}; + o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsigma_Pion| TOF for positive daughter tracks"}; + o2::framework::Configurable> posDauTofProton{"posDauTofProton", {}, "Maximum |nsigma_Proton| TOF for positive daughter tracks"}; + o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; + o2::framework::Configurable> negDauTofProton{"negDauTofProton", {}, "Maximum |nsigma_Proton| TOF for negative daughter tracks"}; + o2::framework::Configurable> rejectK0shortMassRanges{"rejectK0shortMassRanges", {"0.475;0.515"}, "K0short mass ranges (lower;upper) to reject -- bit is set if the candidate's mK0Short falls outside the range"}; }; // derived selection bits for K0Short struct ConfK0shortBits : o2::framework::ConfigurableGroup { std::string prefix = std::string("K0shortBits"); V0_DEFAULT_BITS - o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for positive daughter tracks"}; - o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsimga_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> posDauTpcPion{"posDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for positive daughter tracks"}; + o2::framework::Configurable> negDauTpcPion{"negDauTpcPion", {5.f}, "Maximum |nsigma_Pion| TPC for negative daughter tracks"}; + o2::framework::Configurable> posDauTofPion{"posDauTofPion", {}, "Maximum |nsigma_Pion| TOF for positive daughter tracks"}; + o2::framework::Configurable> negDauTofPion{"negDauTofPion", {}, "Maximum |nsigma_Pion| TOF for negative daughter tracks"}; + o2::framework::Configurable> rejectLambdaMassRanges{"rejectLambdaMassRanges", {"1.11;1.12"}, "Lambda/AntiLambda mass ranges (lower;upper) to reject -- bit is set if the candidate's mLambda (resp. mAntiLambda) falls outside the range; same range for both since they share the same PDG mass"}; }; #undef V0_DEFAULT_BITS // base selection for analysis task for v0s -#define V0_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ - o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", defaultPdgCode, "PDG code. Set sign to -1 for antiparticle"}; \ - o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ - o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ - o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ - o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ - o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum eta"}; \ - o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ - o2::framework::Configurable massMin{"massMin", defaultMassMin, "Minimum invariant mass for Lambda"}; \ - o2::framework::Configurable massMax{"massMax", defaultMassMax, "Maximum invariant mass for Lambda"}; \ - o2::framework::Configurable mask{"mask", 0, "Bitmask for v0 selection"}; +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define V0_DEFAULT_SELECTIONS(defaultMassMin, defaultMassMax, defaultPdgCode) \ + o2::framework::Configurable pdgCodeAbs{"pdgCodeAbs", (defaultPdgCode), "PDG code. Set sign to -1 for antiparticle"}; \ + o2::framework::Configurable ptMin{"ptMin", 0.f, "Minimum pT"}; \ + o2::framework::Configurable ptMax{"ptMax", 999.f, "Maximum pT"}; \ + o2::framework::Configurable etaMin{"etaMin", -10.f, "Minimum eta"}; \ + o2::framework::Configurable etaMax{"etaMax", 10.f, "Maximum eta"}; \ + o2::framework::Configurable phiMin{"phiMin", 0.f, "Minimum phi"}; \ + o2::framework::Configurable phiMax{"phiMax", 1.f * o2::constants::math::TwoPI, "Maximum phi"}; \ + o2::framework::Configurable massMin{"massMin", (defaultMassMin), "Minimum invariant mass"}; \ + o2::framework::Configurable massMax{"massMax", (defaultMassMax), "Maximum invariant mass"}; \ + o2::framework::Configurable mask{"mask", 0, "Bitmask for v0 selection"}; // base selection for analysis task for lambdas -template +template struct ConfLambdaSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; V0_DEFAULT_SELECTIONS(1.0, 1.2, 3122) - o2::framework::Configurable sign{"sign", 1, "Sign of the Lambda (+1: Lambda; -1: Antilambda; 0: both)"}; + o2::framework::Configurable sign{"sign", DefaultSign, "Sign of the Lambda (+1: Lambda; -1: Antilambda; 0: both)"}; }; // base selection for analysis task for k0short -template +template struct ConfK0shortSelection : o2::framework::ConfigurableGroup { std::string prefix = Prefix; - V0_DEFAULT_SELECTIONS(0.47, 0.51, 310) - o2::framework::Configurable sign{"sign", 0, "Dummy value. For compatability with Lambda selection"}; + V0_DEFAULT_SELECTIONS(0.485, 0.51, 310) + o2::framework::Configurable sign{"sign", 0, "Dummy value. For compatibility with Lambda selection"}; }; #undef V0_DEFAULT_SELECTIONS @@ -133,6 +139,12 @@ constexpr const char PrefixK0shortSelection2[] = "K0shortSelection2"; using ConfK0shortSelection1 = ConfK0shortSelection; using ConfK0shortSelection2 = ConfK0shortSelection; +// selections for strangeness yields (e.g. strangeness enhancement in dN/deta tasks), independent of any pair/triplet selection +constexpr const char PrefixLambdaSelectionStrangeness[] = "LambdaSelectionStrangeness"; +using ConfLambdaSelectionStrangeness = ConfLambdaSelection; // lambdas and antilambdas by default +constexpr const char PrefixK0shortSelectionStrangeness[] = "K0shortSelectionStrangeness"; +using ConfK0shortSelectionStrangeness = ConfK0shortSelection; + /// The different selections for v0s enum V0Sels { // selections for lambdas @@ -143,9 +155,9 @@ enum V0Sels { kTransRadMax, ///< max. transverse radius // selection for daughter - kDauAbsEtaMax, ///< Max. absolute pseudo rapidity - kDauDcaMin, ///< Min. DCA of the daughters at primary vertex - kDauTpcClsMin, ///< Min. number of TPC clusters of daughter + kDauAbsEtaMax, ///< Max. absolute pseudo rapidity + kDauAbsDcaxyMin, ///< Min. |DCAxy| of the daughters from primary vertex + kDauTpcClsMin, ///< Min. number of TPC clusters of daughter // pid selection for daughters kPosDaughTpcPion, ///< TPC Pion PID for positive daughter @@ -153,6 +165,15 @@ enum V0Sels { kNegDaughTpcPion, ///< TPC Pion PID for negative daughter kNegDaughTpcProton, ///< TPC Proton PID for negative daughter + kPosDaughTofPion, ///< TOF Pion PID for positive daughter + kPosDaughTofProton, ///< TOF Proton PID for positive daughter + kNegDaughTofPion, ///< TOF Pion PID for negative daughter + kNegDaughTofProton, ///< TOF Proton PID for negative daughter + + // rejection of competing mass hypotheses (range-based: bit is set if the candidate is outside the range) + kRejectK0shortMass, ///< Lambda/AntiLambda: reject K0short mass hypothesis + kRejectLambdaMass, ///< K0short: reject Lambda/AntiLambda mass hypothesis (single bit, see applySelections()) + kV0SelsMax }; @@ -168,26 +189,59 @@ const std::unordered_map v0SelectionNames = { {kTransRadMax, "Max. transverse radius"}, {kDauAbsEtaMax, "Max. absolute pseudo rapidity of daughters"}, - {kDauDcaMin, "Min. DCA of the daughters at primary vertex"}, + {kDauAbsDcaxyMin, "Min. |DCAxy| of the daughters from primary vertex"}, {kDauTpcClsMin, "Min. number of TPC clusters of daughters"}, {kPosDaughTpcPion, "TPC Pion PID for positive daughter"}, {kPosDaughTpcProton, "TPC Proton PID for positive daughter"}, {kNegDaughTpcPion, "TPC Pion PID for negative daughter"}, - {kNegDaughTpcProton, "TPC Proton PID for negative daughter"}}; + {kNegDaughTpcProton, "TPC Proton PID for negative daughter"}, + {kPosDaughTofPion, "TOF Pion PID for positive daughter"}, + {kPosDaughTofProton, "TOF Proton PID for positive daughter"}, + {kNegDaughTofPion, "TOF Pion PID for negative daughter"}, + {kNegDaughTofProton, "TOF Proton PID for negative daughter"}, + {kRejectK0shortMass, "Reject K0short mass hypothesis"}, + {kRejectLambdaMass, "Reject Lambda/AntiLambda mass hypothesis"}}; + +// enum for all track filters (loose pre-selection, applied before quality/PID cuts) +enum V0Filters { + kPtMin, + kPtMax, + kEtaMin, + kEtaMax, + kPhiMin, + kPhiMax, + kMassMin, ///< signal mass window lower bound, for this v0Type's own mass hypothesis + kMassMax, ///< signal mass window upper bound, for this v0Type's own mass hypothesis + kV0FiltersMax +}; + +constexpr char LambdaFilterHistName[] = "hLambdaFilters"; +constexpr char AntiLambdaFilterHistName[] = "hAntiLambdaFilters"; +constexpr char K0shortFilterHistName[] = "hK0shortFilters"; +const std::unordered_map v0FilterNames = { + {kPtMin, "ptMin"}, + {kPtMax, "ptMax"}, + {kEtaMin, "etaMin"}, + {kEtaMax, "etaMax"}, + {kPhiMin, "phiMin"}, + {kPhiMax, "phiMax"}, + {kMassMin, "massMin"}, + {kMassMax, "massMax"}, +}; -/// \class FemtoDreamTrackCuts -/// \brief Cut class to contain and execute all cuts applied to tracks -template -class V0Selection : public BaseSelection +/// \brief Cut class to contain and execute all cuts applied to v0s +template +class V0Selection : public baseselection::BaseSelection { public: V0Selection() = default; - ~V0Selection() = default; + ~V0Selection() override = default; template void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) { + this->init(config.passThrough.value); mPtMin = filter.ptMin.value; mPtMax = filter.ptMax.value; @@ -195,48 +249,74 @@ class V0Selection : public BaseSelection produceLambdas{"produceLambdas", -1, "Produce Lambdas (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteLambdas{"produceLiteLambdas", -1, "Produce LiteLambdas (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLambdaMasks{"produceLambdaMasks", -1, "Produce LambdaMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceLambdaExtras{"produceLambdaExtras", -1, "Produce LambdaExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLambdaFrac{"produceLambdaFrac", -1, "Produce LambdaFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shorts{"produceK0shorts", -1, "Produce K0shorts (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceLiteK0shorts{"produceLiteK0shorts", -1, "Produce LiteK0shorts (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shortMasks{"produceK0shortMasks", -1, "Produce K0shortMasks (-1: auto; 0 off; 1 on)"}; o2::framework::Configurable produceK0shortExtras{"produceK0shortExtras", -1, "Produce K0shortExtras (-1: auto; 0 off; 1 on)"}; + o2::framework::Configurable produceK0shortFrac{"produceK0shortFrac", -1, "Produce K0shortFrac (cpa for fraction extraction) (-1: auto; 0 off; 1 on)"}; }; -template +template class V0Builder { public: V0Builder() = default; ~V0Builder() = default; - template - void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) + template + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext, T5& trackBuilder) { if constexpr (modes::isEqual(v0Type, modes::V0::kLambda) || modes::isEqual(v0Type, modes::V0::kAntiLambda)) { if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { @@ -364,16 +529,53 @@ class V0Builder LOG(info) << "Initialize femto AntiLambda builder..."; } mProduceLambdas = utils::enableTable("FLambdas_001", table.produceLambdas.value, initContext); - mProduceLambdaMasks = utils::enableTable("FLambdaMasks_001", table.produceLambdaMasks.value, initContext); + mProduceLiteLambdas = utils::enableTable("FLiteLambdas_001", table.produceLiteLambdas.value, initContext); + mProduceLambdaMasks = utils::enableTable("FLambdaMasks_002", table.produceLambdaMasks.value, initContext); mProduceLambdaExtras = utils::enableTable("FLambdaExtras_001", table.produceLambdaExtras.value, initContext); + mProduceLambdaFrac = utils::enableTable("FLambdaFrac_001", table.produceLambdaFrac.value, initContext); + + if (mProduceLambdas && mProduceLiteLambdas) { + LOG(fatal) << "FLambdas and FLiteLambdas are mutually exclusive -- enable only one. " + << "FLiteLambdas is meant to replace FLambdas at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FLambdas from FLiteLambdas downstream."; + } + if (mProduceLambdas && !trackBuilder.producingTracks()) { + LOG(fatal) << "FLambdas is enabled, but the track builder is not producing FTracks (full precision). " + << "FLambdas stores daughter indices into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteLambdas if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteLambdas && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteLambdas is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteLambdas stores daughter indices into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FLambdas if TrackTables.produceTracks is enabled instead."; + } } if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { LOG(info) << "Initialize femto K0short builder..."; mProduceK0shorts = utils::enableTable("FK0shorts_001", table.produceK0shorts.value, initContext); - mProduceK0shortMasks = utils::enableTable("FK0shortMasks_001", table.produceK0shortMasks.value, initContext); + mProduceLiteK0shorts = utils::enableTable("FLiteK0shorts_001", table.produceLiteK0shorts.value, initContext); + mProduceK0shortMasks = utils::enableTable("FK0shortMasks_002", table.produceK0shortMasks.value, initContext); mProduceK0shortExtras = utils::enableTable("FK0shortExtras_001", table.produceK0shortExtras.value, initContext); + mProduceK0shortFrac = utils::enableTable("FK0shortFrac_001", table.produceK0shortFrac.value, initContext); + + if (mProduceK0shorts && mProduceLiteK0shorts) { + LOG(fatal) << "FK0shorts and FLiteK0shorts are mutually exclusive -- enable only one. " + << "FLiteK0shorts is meant to replace FK0shorts at the producer stage (for better compression in derived data); " + << "use the dedicated converter task to reconstruct FK0shorts from FLiteK0shorts downstream."; + } + if (mProduceK0shorts && !trackBuilder.producingTracks()) { + LOG(fatal) << "FK0shorts is enabled, but the track builder is not producing FTracks (full precision). " + << "FK0shorts stores daughter indices into FTracks -- enable TrackTables.produceTracks, " + << "or switch to FLiteK0shorts if TrackTables.produceLiteTracks is enabled instead."; + } + if (mProduceLiteK0shorts && !trackBuilder.producingLiteTracks()) { + LOG(fatal) << "FLiteK0shorts is enabled, but the track builder is not producing FLiteTracks. " + << "FLiteK0shorts stores daughter indices into FLiteTracks -- enable TrackTables.produceLiteTracks, " + << "or switch to FK0shorts if TrackTables.produceTracks is enabled instead."; + } } - if (mProduceLambdas || mProduceLambdaMasks || mProduceLambdaExtras || mProduceK0shorts || mProduceK0shortMasks || mProduceK0shortExtras) { + if (mProduceLambdas || mProduceLiteLambdas || mProduceLambdaMasks || mProduceLambdaExtras || mProduceLambdaFrac || + mProduceK0shorts || mProduceLiteK0shorts || mProduceK0shortMasks || mProduceK0shortExtras || mProduceK0shortFrac) { mFillAnyTable = true; } else { LOG(info) << "No tables configured, Selection object will not be configured..."; @@ -385,8 +587,8 @@ class V0Builder LOG(info) << "Initialization done..."; } - template - void fillV0s(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& v0Products, T6 const& v0s, T7 const& tracks, T8 const& tracksWithItsPid, T9& trackBuilder) + template + void fillV0s(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4& trackProducts, T5& v0Products, T6 const& v0s, T7 const& tracks, T8& trackBuilder) { if (!mFillAnyTable) { return; @@ -394,38 +596,36 @@ class V0Builder int64_t posDaughterIndex = 0; int64_t negDaughterIndex = 0; for (const auto& v0 : v0s) { - if (!mV0Selection.passThroughAllV0s() && !mV0Selection.checkFilters(v0)) { + if (!mV0Selection.checkFilters(v0)) { continue; } mV0Selection.applySelections(v0, tracks); - if (!mV0Selection.passThroughAllV0s() && !mV0Selection.passesAllRequiredSelections()) { + if (!mV0Selection.passesAllRequiredSelections()) { continue; } collisionBuilder.template fillCollision(collisionProducts, col); - // cleaner, but without ITS pid: auto posDaughter = v0.template posTrack_as(); - auto posDaughter = tracksWithItsPid.iteratorAt(v0.posTrackId() - tracksWithItsPid.offset()); - // cleaner, but without ITS pid: auto negDaughter = v0.template negTrack_as(); - auto negDaughter = tracksWithItsPid.iteratorAt(v0.negTrackId() - tracksWithItsPid.offset()); + auto posDaughter = v0.template posTrack_as(); + auto negDaughter = v0.template negTrack_as(); - posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionProducts); - negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, collisionProducts); + posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionBuilder); + negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, collisionBuilder); if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { - fillLambda(collisionProducts, v0Products, v0, 1.f, posDaughterIndex, negDaughterIndex); + fillLambda(collisionBuilder, v0Products, v0, 1.f, posDaughterIndex, negDaughterIndex); } if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { - fillLambda(collisionProducts, v0Products, v0, -1.f, posDaughterIndex, negDaughterIndex); + fillLambda(collisionBuilder, v0Products, v0, -1.f, posDaughterIndex, negDaughterIndex); } if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { - fillK0short(collisionProducts, v0Products, v0, posDaughterIndex, negDaughterIndex); + fillK0short(collisionBuilder, v0Products, v0, posDaughterIndex, negDaughterIndex); } } } - template - void fillMcV0s(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& v0Products, T7 const& v0s, T8 const& tracks, T9 const& tracksWithItsPid, T10& trackBuilder, T11 const& mcParticles, T12& mcBuilder, T13& mcProducts) + template + void fillMcV0s(T1 const& col, T2& collisionBuilder, T3& collisionProducts, T4 const& mcCols, T5& trackProducts, T6& v0Products, T7 const& v0s, T8 const& tracks, T9& trackBuilder, T10 const& mcParticles, T11& mcBuilder, T12& mcProducts) { if (!mFillAnyTable) { @@ -444,42 +644,47 @@ class V0Builder collisionBuilder.template fillMcCollision(collisionProducts, col, mcCols, mcProducts, mcBuilder); - auto posDaughter = tracks.iteratorAt(v0.posTrackId() - tracksWithItsPid.offset()); - auto posDaughterWithItsPid = tracksWithItsPid.iteratorAt(v0.posTrackId() - tracksWithItsPid.offset()); - posDaughterIndex = trackBuilder.template getDaughterIndex(col, collisionProducts, mcCols, posDaughter, posDaughterWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); + auto posDaughter = v0.template posTrack_as(); + posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); - auto negDaughter = tracks.iteratorAt(v0.negTrackId() - tracksWithItsPid.offset()); - auto negDaughterWithItsPid = tracksWithItsPid.iteratorAt(v0.negTrackId() - tracksWithItsPid.offset()); - negDaughterIndex = trackBuilder.template getDaughterIndex(col, collisionProducts, mcCols, negDaughter, negDaughterWithItsPid, trackProducts, mcParticles, mcBuilder, mcProducts); + auto negDaughter = v0.template negTrack_as(); + negDaughterIndex = trackBuilder.template getDaughterIndex(negDaughter, trackProducts, mcCols, collisionBuilder, mcParticles, mcBuilder, mcProducts); if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { - fillLambda(collisionProducts, v0Products, v0, 1.f, posDaughterIndex, negDaughterIndex); - mcBuilder.template fillMcLambdaWithLabel(col, mcCols, v0, mcParticles, mcProducts); + fillLambda(collisionBuilder, v0Products, v0, 1.f, posDaughterIndex, negDaughterIndex); + mcBuilder.template fillMcLambdaWithLabel(v0, mcParticles, mcCols, mcProducts); } if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { - fillLambda(collisionProducts, v0Products, v0, -1.f, posDaughterIndex, negDaughterIndex); - mcBuilder.template fillMcLambdaWithLabel(col, mcCols, v0, mcParticles, mcProducts); + fillLambda(collisionBuilder, v0Products, v0, -1.f, posDaughterIndex, negDaughterIndex); + mcBuilder.template fillMcLambdaWithLabel(v0, mcParticles, mcCols, mcProducts); } if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { - fillK0short(collisionProducts, v0Products, v0, posDaughterIndex, negDaughterIndex); - mcBuilder.template fillMcK0shortWithLabel(col, mcCols, v0, mcParticles, mcProducts); + fillK0short(collisionBuilder, v0Products, v0, posDaughterIndex, negDaughterIndex); + mcBuilder.template fillMcK0shortWithLabel(v0, mcParticles, mcCols, mcProducts); } } } template - void fillLambda(T1& collisionProducts, T2& v0Products, T3 const& v0, float sign, int64_t posDaughterIndex, int64_t negDaughterIndex) + void fillLambda(T1& collisionBuilder, T2& v0Products, T3 const& v0, float sign, int64_t posDaughterIndex, int64_t negDaughterIndex) { - float mass, massAnti; + float mass = 0; + float massAnti = 0; + float strangeTofPosDau = 0; + float strangeTofNegDau = 0; if (sign > 0.f) { mass = v0.mLambda(); massAnti = v0.mAntiLambda(); + strangeTofPosDau = v0.tofNSigmaLaPr(); + strangeTofNegDau = v0.tofNSigmaLaPi(); } else { mass = v0.mAntiLambda(); massAnti = v0.mLambda(); + strangeTofPosDau = v0.tofNSigmaALaPi(); + strangeTofNegDau = v0.tofNSigmaALaPr(); } if (mProduceLambdas) { - v0Products.producedLambdas(collisionProducts.producedCollision.lastIndex(), + v0Products.producedLambdas(collisionBuilder.collisionIndex(), sign * v0.pt(), v0.eta(), v0.phi(), @@ -487,6 +692,15 @@ class V0Builder posDaughterIndex, negDaughterIndex); } + if (mProduceLiteLambdas) { + v0Products.producedLiteLambdas(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binSignedPt(sign * v0.pt()), + o2::aod::femtobase::lite::binEta(v0.eta()), + o2::aod::femtobase::lite::binPhi(v0.phi()), + o2::aod::femtov0s::lite::binLambdaMass(mass), + posDaughterIndex, + negDaughterIndex); + } if (mProduceLambdaMasks) { v0Products.producedLambdaMasks(mV0Selection.getBitmask()); } @@ -496,18 +710,25 @@ class V0Builder v0.mK0Short(), v0.v0cosPA(), v0.dcaV0daughters(), + strangeTofPosDau, + strangeTofNegDau, v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); + } + if (mProduceLambdaFrac) { + v0Products.producedLambdaFrac(v0.v0cosPA()); } } template - void fillK0short(T1& collisionProducts, T2& v0Products, T3 const& v0, int64_t posDaughterIndex, int64_t negDaughterIndex) + void fillK0short(T1& collisionBuilder, T2& v0Products, T3 const& v0, int64_t posDaughterIndex, int64_t negDaughterIndex) { if (mProduceK0shorts) { - v0Products.producedK0shorts(collisionProducts.producedCollision.lastIndex(), + v0Products.producedK0shorts(collisionBuilder.collisionIndex(), v0.pt(), v0.eta(), v0.phi(), @@ -515,6 +736,15 @@ class V0Builder posDaughterIndex, negDaughterIndex); } + if (mProduceLiteK0shorts) { + v0Products.producedLiteK0shorts(collisionBuilder.collisionIndex(), + o2::aod::femtobase::lite::binUnsignedPt(v0.pt()), + o2::aod::femtobase::lite::binEta(v0.eta()), + o2::aod::femtobase::lite::binPhi(v0.phi()), + o2::aod::femtov0s::lite::binK0shortMass(v0.mK0Short()), + posDaughterIndex, + negDaughterIndex); + } if (mProduceK0shortMasks) { v0Products.producedK0shortMasks(mV0Selection.getBitmask()); } @@ -524,30 +754,43 @@ class V0Builder v0.mAntiLambda(), v0.v0cosPA(), v0.dcaV0daughters(), + v0.tofNSigmaK0PiPlus(), + v0.tofNSigmaK0PiMinus(), v0.v0radius(), v0.x(), v0.y(), - v0.z()); + v0.z(), + v0.alpha(), + v0.qtarm()); + } + if (mProduceK0shortFrac) { + v0Products.producedK0shortFrac(v0.v0cosPA()); } } - bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool fillAnyTable() const { return mFillAnyTable; } + [[nodiscard]] bool isPassThrough() const { return mV0Selection.isPassThrough(); } private: - V0Selection mV0Selection; + V0Selection mV0Selection; bool mFillAnyTable = false; bool mProduceLambdas = false; + bool mProduceLiteLambdas = false; bool mProduceLambdaMasks = false; bool mProduceLambdaExtras = false; + bool mProduceLambdaFrac = false; bool mProduceK0shorts = false; + bool mProduceLiteK0shorts = false; bool mProduceK0shortMasks = false; bool mProduceK0shortExtras = false; + bool mProduceK0shortFrac = false; }; struct ConfV0TablesDerivedToDerived : o2::framework::ConfigurableGroup { std::string prefix = std::string("V0Tables"); - o2::framework::Configurable limitLambda{"limitLambda", 1, "At least this many lambdas need to be in the collision"}; - o2::framework::Configurable limitK0short{"limitK0short", 0, "At least this many k0short need to be in the collision"}; + o2::framework::Configurable limitLambda{"limitLambda", 1, "Require at least this many Lambdas in the collision. Set to 0 to skip Lambdas entirely (not written to output)."}; + + o2::framework::Configurable limitK0short{"limitK0short", 0, "Require at least this many K0shorts in the collision. Set to 0 to skip K0shorts entirely (not written to output)."}; }; struct V0BuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { @@ -560,15 +803,15 @@ struct V0BuilderDerivedToDerivedProducts : o2::framework::ProducesGroup { class V0BuilderDerivedToDerived { public: - V0BuilderDerivedToDerived() = default; - ~V0BuilderDerivedToDerived() = default; - template void init(T& config) { mLimitLambda = config.limitLambda.value; mLimitK0short = config.limitK0short.value; + if (mLimitLambda < 0 || mLimitK0short < 0) { + LOG(fatal) << "V0 limits must be non-negative (got " << mLimitLambda << " and " << mLimitK0short << "). Breaking..."; + } if (mLimitLambda == 0 && mLimitK0short == 0) { LOG(fatal) << "Both lambda limit and k0short limit are 0. Breaking..."; } @@ -577,37 +820,38 @@ class V0BuilderDerivedToDerived template bool collisionHasTooFewLambdas(T1 const& col, T2 const& /*lambdaTable*/, T3& partitionLambda, T4& cache) { - auto lambdaSlice = partitionLambda->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (lambdaSlice.size() >= mLimitLambda) { + if (mLimitLambda == 0) { // lambdas disabled, cannot reject on them return false; } - return true; + auto lambdaSlice = partitionLambda->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return lambdaSlice.size() < static_cast(mLimitLambda); } template bool collisionHasTooFewK0shorts(T1 const& col, T2 const& /*k0shortTable*/, T3& partitionK0short, T4& cache) { - auto k0shortSlice = partitionK0short->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); - if (k0shortSlice.size() >= mLimitK0short) { + if (mLimitK0short == 0) { // k0shorts disabled, cannot reject on them return false; } - return true; + auto k0shortSlice = partitionK0short->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + return k0shortSlice.size() < static_cast(mLimitK0short); } template void processLambdas(T1 const& col, T2 const& /*lambdaTable*/, T3 const& oldTrackTable, T4& partitionLambda, T5& trackBuilder, T6& cache, T7& newLambdaTable, T8& newTrackTable, T9& newCollisionTable) { + if (mLimitLambda == 0) { // lambdas disabled + return; + } + auto lambdaSlice = partitionLambda->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); for (auto const& lambda : lambdaSlice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(lambda.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(lambda.negDauId(), oldTrackTable)); - // auto posDaughter = lambda.template posDau_as(); - // auto negDaughter = lambda.template negDau_as(); - auto posDaughter = oldTrackTable.rawIteratorAt(lambda.posDauId() - oldTrackTable.offset()); - auto negDaughter = oldTrackTable.rawIteratorAt(lambda.negDauId() - oldTrackTable.offset()); - - int posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); - int negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); newLambdaTable.producedLambdas(newCollisionTable.producedCollision.lastIndex(), lambda.signedPt(), @@ -623,17 +867,18 @@ class V0BuilderDerivedToDerived template void processK0shorts(T1 const& col, T2 const& /*k0shortTable*/, T3 const& oldTrackTable, T4& partitionK0short, T5& trackBuilder, T6& cache, T7& newK0shortTable, T8& newTrackTable, T9& newCollisionTable) { + if (mLimitK0short == 0) { // k0shorts disabled + return; + } + auto k0shortSlice = partitionK0short->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); for (auto const& k0short : k0shortSlice) { + auto posDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(k0short.posDauId(), oldTrackTable)); + auto negDaughter = oldTrackTable.rawIteratorAt(utils::daughterRow(k0short.negDauId(), oldTrackTable)); - // auto posDaughter = k0short.template posDau_as(); - // auto negDaughter = k0short.template negDau_as(); - auto posDaughter = oldTrackTable.rawIteratorAt(k0short.posDauId() - oldTrackTable.offset()); - auto negDaughter = oldTrackTable.rawIteratorAt(k0short.negDauId() - oldTrackTable.offset()); - - int posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); - int negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); + int64_t posDaughterIndex = trackBuilder.getDaughterIndex(posDaughter, newTrackTable, newCollisionTable); + int64_t negDaughterIndex = trackBuilder.getDaughterIndex(negDaughter, newTrackTable, newCollisionTable); newK0shortTable.producedK0shorts(newCollisionTable.producedCollision.lastIndex(), k0short.pt(), @@ -651,6 +896,5 @@ class V0BuilderDerivedToDerived int mLimitK0short = 0; }; -} // namespace v0builder -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::v0builder #endif // PWGCF_FEMTO_CORE_V0BUILDER_H_ diff --git a/PWGCF/Femto/Core/v0HistManager.h b/PWGCF/Femto/Core/v0HistManager.h index 3b9faba0a70..058edf2e6c8 100644 --- a/PWGCF/Femto/Core/v0HistManager.h +++ b/PWGCF/Femto/Core/v0HistManager.h @@ -36,9 +36,7 @@ #include #include -namespace o2::analysis::femto -{ -namespace v0histmanager +namespace o2::analysis::femto::v0histmanager { // enum for track histograms enum V0Hist { @@ -48,12 +46,17 @@ enum V0Hist { kPhi, kMass, kSign, + kPtVsMass, // nice to have during analysis + kFractionCpa, // fraction estimation: pt vs cosPa, binned independently of the QA cosPa axis // qa variables kMassLambda, kMassAntiLambda, kMassK0short, kCosPa, + kPa, kDecayDauDca, + kStrangeTofPosDau, + kStrangeTofNegDau, kDecayVtxX, kDecayVtxY, kDecayVtxZ, @@ -63,13 +66,16 @@ enum V0Hist { kPtVsEta, kPtVsPhi, kPhiVsEta, - kPtVsCosPa, + kPtVsPa, kPtVsLambdaMass, kPtVsAntiLambdaMass, kPtVsK0shortMass, kLambdaMassVsAntiLambdaMass, kK0shortMassVsLambdaMass, kK0shortMassVsAntiLambdaMass, + kStrangeTofVsTofPosDau, + kStrangeTofVsTofNegDau, + kArmenterosQtVsAlpha, // mc kOrigin, kPdg, @@ -94,44 +100,55 @@ enum V0Hist { constexpr std::size_t MaxSecondary = 3; -#define V0_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ - o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ - o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ - o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ - o2::framework::ConfigurableAxis mass{"mass", {{200, defaultMassMin, defaultMassMax}}, "Mass"}; \ - o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ - o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed V0s"}; - -template +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define V0_DEFAULT_BINNING(defaultMassMin, defaultMassMax) \ + o2::framework::ConfigurableAxis pt{"pt", {{600, 0, 6}}, "Pt"}; \ + o2::framework::ConfigurableAxis eta{"eta", {{300, -1.5, 1.5}}, "Eta"}; \ + o2::framework::ConfigurableAxis phi{"phi", {{720, 0, 1.f * o2::constants::math::TwoPI}}, "Phi"}; \ + o2::framework::ConfigurableAxis mass{"mass", {{200, (defaultMassMin), (defaultMassMax)}}, "Mass"}; \ + o2::framework::ConfigurableAxis sign{"sign", {{3, -1.5, 1.5}}, "Sign"}; \ + o2::framework::ConfigurableAxis pdgCodes{"pdgCodes", {{8001, -4000.5, 4000.5}}, "MC ONLY: PDG codes of reconstructed V0s"}; \ + o2::framework::ConfigurableAxis fractionCpa{"fractionCpa", {{100, 0.95, 1}}, "Cosine of pointing angle (fraction estimation)"}; + +template struct ConfLambdaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; V0_DEFAULT_BINNING(1.0, 1.2) }; -template +template struct ConfK0shortBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; V0_DEFAULT_BINNING(0.475, 0.515) }; + #undef V0_DEFAULT_BINNING constexpr const char PrefixLambdaBinning1[] = "LambdaBinning1"; using ConfLambdaBinning1 = ConfLambdaBinning; +constexpr const char PrefixLambdaBinning2[] = "LambdaBinning2"; +using ConfLambdaBinning2 = ConfLambdaBinning; constexpr const char PrefixK0shortBinning1[] = "K0shortBinning1"; using ConfK0shortBinning1 = ConfK0shortBinning; +constexpr const char PrefixK0shortBinning2[] = "K0shortBinning2"; +using ConfK0shortBinning2 = ConfK0shortBinning; -template +template struct ConfV0QaBinning : o2::framework::ConfigurableGroup { std::string prefix = Prefix; o2::framework::Configurable plot2d{"plot2d", true, "Generate various 2D QA plots"}; o2::framework::Configurable plotOrigins{"plotOrigins", true, "MC ONLY: Plot pt vs cosPa for different particle origins"}; o2::framework::Configurable> pdgCodesForMothersOfSecondary{"pdgCodesForMothersOfSecondary", {3312, 3334}, "MC ONLY: PDG codes of mothers of secondaries (Max 3 will be considered)"}; - o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.9, 1}}, "Cosine of poiting angle"}; + o2::framework::ConfigurableAxis cosPa{"cosPa", {{100, 0.95, 1}}, "Cosine of pointing angle"}; + o2::framework::ConfigurableAxis pa{"pa", {{180, 0, 1.f * o2::constants::math::PI}}, "Pointing angle"}; o2::framework::ConfigurableAxis dauDcaAtDecay{"dauDcaAtDecay", {{150, 0, 1.5}}, "Daughter DCA at decay vertex"}; o2::framework::ConfigurableAxis decayVertex{"decayVertex", {{100, 0, 100}}, "Decay vertex"}; o2::framework::ConfigurableAxis transRadius{"transRadius", {{100, 0, 100}}, "Transverse radius"}; - o2::framework::ConfigurableAxis massLambda{"massLambda", {{200, 1, 1.2}}, "mass for antiparticle hypothesis"}; + o2::framework::ConfigurableAxis massLambda{"massLambda", {{200, 1, 1.2}}, "mass for particle hypothesis"}; o2::framework::ConfigurableAxis massAntiLambda{"massAntiLambda", {{100, 1, 1.2}}, "mass for antiparticle hypothesis"}; o2::framework::ConfigurableAxis massK0short{"massK0short", {{200, 0.45, 0.55}}, "Mass for k0short hypothesis"}; + o2::framework::ConfigurableAxis strangeTof{"strangeTof", {{500, -5, 5}}, "Strangeness TOF vs TOF Nsigma for daughters"}; + o2::framework::ConfigurableAxis armenterosAlpha{"armenterosAlpha", {{200, -1, 1}}, "Armenteros-Podolanski alpha"}; + o2::framework::ConfigurableAxis armenterosQt{"armenterosQt", {{150, 0, 0.3}}, "Armenteros-Podolanski qt (GeV/#it{c})"}; }; constexpr const char PrefixLambdaQaBinning1[] = "LambdaQaBinning1"; @@ -148,11 +165,16 @@ constexpr std::array, kV0HistLast> HistTable = { {kPhi, o2::framework::HistType::kTH1F, "hPhi", "Azimuthal angle; #varphi; Entries"}, {kMass, o2::framework::HistType::kTH1F, "hMass", "Invariant Mass; m_{Inv} (GeV/#it{c}^{2}); Entries"}, {kSign, o2::framework::HistType::kTH1F, "hSign", "Sign (-1 -> antiparticle, 0 -> self conjugate, +1 -> particle); sign; Entries"}, + {kPtVsMass, o2::framework::HistType::kTH2F, "hPtVsMass", "Transverse momentum vs invariant mass; p_{T} (GeV/#it{c}); m_{Inv} (GeV/#it{c}^{2})"}, + {kFractionCpa, o2::framework::HistType::kTH2F, "hFractionCpa", "p_{T} vs Cosine of pointing angle (fraction estimation); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMassLambda, o2::framework::HistType::kTH1F, "hMassLambda", "#Lambda mass; m_{p#pi^{-}} (GeV/#it{c}^{2}); Entries"}, {kMassAntiLambda, o2::framework::HistType::kTH1F, "hMassAntiLambda", "#bar{#Lambda} mass; m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2}); Entries"}, {kMassK0short, o2::framework::HistType::kTH1F, "hMassK0short", "K^{0}_{s} mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); Entries"}, {kCosPa, o2::framework::HistType::kTH1F, "hCosPa", "Cosine of pointing angle; cos(#alpha); Entries"}, + {kPa, o2::framework::HistType::kTH1F, "hPa", "Pointing angle; #alpha; Entries"}, {kDecayDauDca, o2::framework::HistType::kTH1F, "hDauDca", "Daughter DCA at decay vertex ; DCA_{Decay vertex} (cm); Entries"}, + {kStrangeTofPosDau, o2::framework::HistType::kTH1F, "hStrangeTofPosDau", "Strange TOF of positive Daughter ; n#sigma_{TOF, strange}; Entries"}, + {kStrangeTofNegDau, o2::framework::HistType::kTH1F, "hStrangeTofNegDau", "Strange TOF of negative Daughter ; n#sigma_{TOF, strange}; Entries"}, {kDecayVtxX, o2::framework::HistType::kTH1F, "hDecayVtxX", "X coordinate of decay vertex ; DV_{X} (cm); Entries"}, {kDecayVtxY, o2::framework::HistType::kTH1F, "hDecayVtxY", "Y coordinate of decay vertex ; DV_{Y} (cm); Entries"}, {kDecayVtxZ, o2::framework::HistType::kTH1F, "hDecayVtxZ", "Z coordinate of decay vertex ; DV_{Z} (cm); Entries"}, @@ -161,13 +183,16 @@ constexpr std::array, kV0HistLast> HistTable = { {kPtVsEta, o2::framework::HistType::kTH2F, "hPtVsEta", "p_{T} vs #eta; p_{T} (GeV/#it{c}) ; #eta"}, {kPtVsPhi, o2::framework::HistType::kTH2F, "hPtVsPhi", "p_{T} vs #varphi; p_{T} (GeV/#it{c}) ; #varphi"}, {kPhiVsEta, o2::framework::HistType::kTH2F, "hPhiVsEta", "#varphi vs #eta; #varphi ; #eta"}, - {kPtVsCosPa, o2::framework::HistType::kTH2F, "hPtVsCosPa", "Cosine of poiting angle vs p_{T}; cos(#alpha); p_{T} (GeV/#it{c})"}, + {kPtVsPa, o2::framework::HistType::kTH2F, "hPtVsPa", "p_{T} vs Pointing angle;p_{T} (GeV/#it{c}); #alpha"}, {kPtVsLambdaMass, o2::framework::HistType::kTH2F, "hPtVsLambdaMass", "p_{T} vs #Lambda mass; p_{T} (GeV/#it{c}); m_{p#pi^{-}} (GeV/#it{c}^{2})"}, {kPtVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hPtVsAntiLambdaMass", "p_{T} vs #bar{#Lambda} mass; p_{T} (GeV/#it{c}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, {kPtVsK0shortMass, o2::framework::HistType::kTH2F, "hPtVsK0shortMass", "p_{T} vs K^{0}_{S} mass; p_{T} (GeV/#it{c}); m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2})"}, {kK0shortMassVsLambdaMass, o2::framework::HistType::kTH2F, "hK0shortMassVsLambdaMass", " K^{0}_{S} mass vs #Lambda mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); m_{p#pi^{-}} (GeV/#it{c}^{2})"}, {kK0shortMassVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hK0shortMassVsAntiLambdaMass", "K^{0}_{S} mass vs #bar{#Lambda} mass; m_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, {kLambdaMassVsAntiLambdaMass, o2::framework::HistType::kTH2F, "hLambdaMassVsAntiLambdaMass", "#Lambda mass vs #bar{#Lambda}; m_{p#pi^{-}} (GeV/#it{c}^{2}); m_{#bar{p}#pi^{+}} (GeV/#it{c}^{2})"}, + {kStrangeTofVsTofPosDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofPosDau", "TOF_{Strange} vs TOF_{Tracking} of positive Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, + {kStrangeTofVsTofNegDau, o2::framework::HistType::kTH2F, "hStrangeTofVsTofNegDau", "TOF_{Strange} vs TOF_{Tracking} of negative Daughter; n#sigma_{TOF, strange}; n#sigma_{TOF, tracking}"}, + {kArmenterosQtVsAlpha, o2::framework::HistType::kTH2F, "hArmenterosPodolanski", "Armenteros-Podolanski; #alpha; q_{T} (GeV/#it{c})"}, {kOrigin, o2::framework::HistType::kTH1F, "hOrigin", "Status Codes (=Origin); Status Code; Entries"}, {kPdg, o2::framework::HistType::kTH1F, "hPdg", "PDG Codes of reconstructed v0; PDG Code; Entries"}, {kPdgMother, o2::framework::HistType::kTH1F, "hPdgMother", "PDG Codes of mother of reconstructed v0; PDG Code; Entries"}, @@ -181,58 +206,73 @@ constexpr std::array, kV0HistLast> HistTable = { {kFromMaterial, o2::framework::HistType::kTH2F, "hFromMaterial", "Particles from material; p_{T} (GeV/#it{c}); cos(#alpha)"}, {kMissidentified, o2::framework::HistType::kTH2F, "hMissidentified", "Missidentified particles (fake/wrong PDG code); p_{T} (GeV/#it{c}); cos(#alpha)"}, {kSecondary1, o2::framework::HistType::kTH2F, "hFromSecondary1", "Particles from secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from seconary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from seconary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, - {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other seconary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}}, + {kSecondary2, o2::framework::HistType::kTH2F, "hFromSecondary2", "Particles from secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kSecondary3, o2::framework::HistType::kTH2F, "hFromSecondary3", "Particles from secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}, + {kSecondaryOther, o2::framework::HistType::kTH2F, "hFromSecondaryOther", "Particles from every other secondary decay; p_{T} (GeV/#it{c}); cos(#alpha)"}}, }; -#define V0_HIST_ANALYSIS_MAP(conf) \ - {kPt, {conf.pt}}, \ - {kEta, {conf.eta}}, \ - {kPhi, {conf.phi}}, \ - {kMass, {conf.mass}}, \ - {kSign, {conf.sign}}, - -#define V0_HIST_MC_MAP(conf) \ - {kTruePtVsPt, {conf.pt, conf.pt}}, \ - {kTrueEtaVsEta, {conf.eta, conf.eta}}, \ - {kTruePhiVsPhi, {conf.phi, conf.phi}}, \ - {kPdg, {conf.pdgCodes}}, \ - {kPdgMother, {conf.pdgCodes}}, \ - {kPdgPartonicMother, {conf.pdgCodes}}, - -#define V0_HIST_QA_MAP(confAnalysis, confQa) \ - {kCosPa, {confQa.cosPa}}, \ - {kDecayDauDca, {confQa.dauDcaAtDecay}}, \ - {kDecayVtxX, {confQa.decayVertex}}, \ - {kDecayVtxY, {confQa.decayVertex}}, \ - {kDecayVtxZ, {confQa.decayVertex}}, \ - {kDecayVtx, {confQa.decayVertex}}, \ - {kTransRadius, {confQa.transRadius}}, \ - {kPtVsEta, {confAnalysis.pt, confAnalysis.eta}}, \ - {kPtVsPhi, {confAnalysis.pt, confAnalysis.phi}}, \ - {kPhiVsEta, {confAnalysis.phi, confAnalysis.eta}}, \ - {kPtVsCosPa, {confAnalysis.pt, confQa.cosPa}}, \ - {kMassLambda, {confQa.massLambda}}, \ - {kMassAntiLambda, {confQa.massAntiLambda}}, \ - {kMassK0short, {confQa.massK0short}}, \ - {kPtVsLambdaMass, {confAnalysis.pt, confQa.massLambda}}, \ - {kPtVsAntiLambdaMass, {confAnalysis.pt, confQa.massAntiLambda}}, \ - {kPtVsK0shortMass, {confAnalysis.pt, confQa.massK0short}}, \ - {kLambdaMassVsAntiLambdaMass, {confQa.massLambda, confQa.massAntiLambda}}, \ - {kK0shortMassVsLambdaMass, {confQa.massK0short, confQa.massLambda}}, \ - {kK0shortMassVsAntiLambdaMass, {confQa.massK0short, confQa.massAntiLambda}}, - -#define V0_HIST_MC_QA_MAP(confAnalysis, confQa) \ - {kNoMcParticle, {confAnalysis.pt, confQa.cosPa}}, \ - {kPrimary, {confAnalysis.pt, confQa.cosPa}}, \ - {kFromWrongCollision, {confAnalysis.pt, confQa.cosPa}}, \ - {kFromMaterial, {confAnalysis.pt, confQa.cosPa}}, \ - {kMissidentified, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondary1, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondary2, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondary3, {confAnalysis.pt, confQa.cosPa}}, \ - {kSecondaryOther, {confAnalysis.pt, confQa.cosPa}}, +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define V0_HIST_ANALYSIS_MAP(conf) \ + {kPt, {(conf).pt}}, \ + {kEta, {(conf).eta}}, \ + {kPhi, {(conf).phi}}, \ + {kMass, {(conf).mass}}, \ + {kSign, {(conf).sign}}, \ + {kPtVsMass, {(conf).pt, (conf).mass}}, \ + {kFractionCpa, {(conf).pt, (conf).fractionCpa}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define V0_HIST_MC_MAP(conf) \ + {kTruePtVsPt, {(conf).pt, (conf).pt}}, \ + {kTrueEtaVsEta, {(conf).eta, (conf).eta}}, \ + {kTruePhiVsPhi, {(conf).phi, (conf).phi}}, \ + {kPdg, {(conf).pdgCodes}}, \ + {kPdgMother, {(conf).pdgCodes}}, \ + {kPdgPartonicMother, {(conf).pdgCodes}}, + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define V0_HIST_QA_MAP(confAnalysis, confQa) \ + {kCosPa, {(confQa).cosPa}}, \ + {kPa, {(confQa).pa}}, \ + {kDecayDauDca, {(confQa).dauDcaAtDecay}}, \ + {kStrangeTofPosDau, {(confQa).strangeTof}}, \ + {kStrangeTofNegDau, {(confQa).strangeTof}}, \ + {kDecayVtxX, {(confQa).decayVertex}}, \ + {kDecayVtxY, {(confQa).decayVertex}}, \ + {kDecayVtxZ, {(confQa).decayVertex}}, \ + {kDecayVtx, {(confQa).decayVertex}}, \ + {kTransRadius, {(confQa).transRadius}}, \ + {kPtVsEta, {(confAnalysis).pt, (confAnalysis).eta}}, \ + {kPtVsPhi, {(confAnalysis).pt, (confAnalysis).phi}}, \ + {kPhiVsEta, {(confAnalysis).phi, (confAnalysis).eta}}, \ + {kPtVsPa, {(confAnalysis).pt, (confQa).pa}}, \ + {kMassLambda, {(confQa).massLambda}}, \ + {kMassAntiLambda, {(confQa).massAntiLambda}}, \ + {kMassK0short, {(confQa).massK0short}}, \ + {kPtVsLambdaMass, {(confAnalysis).pt, (confQa).massLambda}}, \ + {kPtVsAntiLambdaMass, {(confAnalysis).pt, (confQa).massAntiLambda}}, \ + {kPtVsK0shortMass, {(confAnalysis).pt, (confQa).massK0short}}, \ + {kLambdaMassVsAntiLambdaMass, {(confQa).massLambda, (confQa).massAntiLambda}}, \ + {kK0shortMassVsLambdaMass, {(confQa).massK0short, (confQa).massLambda}}, \ + {kK0shortMassVsAntiLambdaMass, {(confQa).massK0short, (confQa).massAntiLambda}}, \ + {kStrangeTofVsTofPosDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ + {kStrangeTofVsTofNegDau, {(confQa).strangeTof, (confQa).strangeTof}}, \ + {kArmenterosQtVsAlpha, {(confQa).armenterosAlpha, (confQa).armenterosQt}}, + +// per-origin truth-split variants of the FitVars fraction-estimation CPA histogram; binned on +// fractionCpa (not the QA-only cosPa) so they are available wherever FitVars is, including the +// pair task where no QA binning config exists +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define V0_HIST_FITVARS_MC_MAP(conf) \ + {kNoMcParticle, {(conf).pt, (conf).fractionCpa}}, \ + {kPrimary, {(conf).pt, (conf).fractionCpa}}, \ + {kFromWrongCollision, {(conf).pt, (conf).fractionCpa}}, \ + {kFromMaterial, {(conf).pt, (conf).fractionCpa}}, \ + {kMissidentified, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary1, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary2, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondary3, {(conf).pt, (conf).fractionCpa}}, \ + {kSecondaryOther, {(conf).pt, (conf).fractionCpa}}, template auto makeV0HistSpecMap(const T& confBinningAnalysis) @@ -246,7 +286,8 @@ auto makeV0McHistSpecMap(const T& confBinningAnalysis) { return std::map>{ V0_HIST_ANALYSIS_MAP(confBinningAnalysis) - V0_HIST_MC_MAP(confBinningAnalysis)}; + V0_HIST_MC_MAP(confBinningAnalysis) + V0_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } template @@ -264,33 +305,40 @@ std::map> makeV0McQaHistSpecMap(T1 V0_HIST_ANALYSIS_MAP(confBinningAnalysis) V0_HIST_QA_MAP(confBinningAnalysis, confBinningQa) V0_HIST_MC_MAP(confBinningAnalysis) - V0_HIST_MC_QA_MAP(confBinningAnalysis, confBinningQa)}; + V0_HIST_FITVARS_MC_MAP(confBinningAnalysis)}; } #undef V0_HIST_ANALYSIS_MAP #undef V0_HIST_QA_MAP #undef V0_HIST_MC_MAP -#undef V0_HIST_MC_QA_MAP +#undef V0_HIST_FITVARS_MC_MAP constexpr char PrefixLambdaQa[] = "LambdaQA/"; -constexpr char PrefixLambda1[] = "Lambda1/"; -constexpr char PrefixLambda2[] = "Lambda2/"; +// same-event / mixed-event siblings: single-candidate histograms for a given slot, split by +// whether the candidate came from a same-event or mixed-event pair +constexpr char PrefixLambda1Se[] = "Lambda1/SE/"; +constexpr char PrefixLambda2Se[] = "Lambda2/SE/"; +constexpr char PrefixLambda1Me[] = "Lambda1/ME/"; +constexpr char PrefixLambda2Me[] = "Lambda2/ME/"; constexpr char PrefixK0shortQa[] = "K0shortQa/"; -constexpr char PrefixK0short1[] = "K0short1/"; -constexpr char PrefixK0short2[] = "K0short2/"; +constexpr char PrefixK0short1Se[] = "K0short1/SE/"; +constexpr char PrefixK0short2Se[] = "K0short2/SE/"; +constexpr char PrefixK0short1Me[] = "K0short1/ME/"; +constexpr char PrefixK0short2Me[] = "K0short2/ME/"; constexpr char PrefixLambdaCascade[] = "LambdaCascadeQa/"; constexpr std::string_view AnalysisDir = "Analysis/"; constexpr std::string_view QaDir = "QA/"; constexpr std::string_view McDir = "MC/"; +constexpr std::string_view FitVarsDir = "FitVars/"; /// \class FemtoDreamEventHisto /// \brief Class for histogramming event properties // template -template class V0HistManager { @@ -334,12 +382,15 @@ class V0HistManager mPosDauManager.template init(registry, PosDauSpecs, absCharge, signPlus, posDauPdgCodeAbs); mNegDauManager.template init(registry, NegDauSpecs, absCharge, signMinus, negDauPdgCodeAbs); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { this->initAnalysis(V0Specs); } if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(V0Specs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(V0Specs); + } } // init for analysis and qa and and mc @@ -382,7 +433,7 @@ class V0HistManager mPosDauManager.template init(registry, PosDauSpecs, absCharge, signPlus, posDauPdgCode, ConfPosDauBinningQa); mNegDauManager.template init(registry, NegDauSpecs, absCharge, signMinus, negDauPdgCode, ConfNegDauBinningQa); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { this->initAnalysis(V0Specs); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { @@ -391,6 +442,9 @@ class V0HistManager if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { this->initMc(V0Specs); } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->template initFitVars(V0Specs); + } } template @@ -401,29 +455,35 @@ class V0HistManager auto negDaughter = tracks.rawIteratorAt(v0candidate.negDauId() - tracks.offset()); mNegDauManager.template fill(negDaughter, tracks); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { this->fillAnalysis(v0candidate); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { - this->fillQa(v0candidate); + this->fillQa(v0candidate, posDaughter, negDaughter); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(v0candidate); } } - template - void fill(T1 const& v0candidate, T2 const& tracks, T3 const& mcParticles, T4 const& mcMothers, T5 const& mcPartonicMothers) + template + void fill(T1 const& v0candidate, T2 const& tracks, T3 const& col, T4 const& mcParticles, T5 const& mcMothers, T6 const& mcPartonicMothers) { auto posDaughter = tracks.rawIteratorAt(v0candidate.posDauId() - tracks.offset()); - mPosDauManager.template fill(posDaughter, tracks, mcParticles, mcMothers, mcPartonicMothers); + mPosDauManager.template fill(posDaughter, tracks, col, mcParticles, mcMothers, mcPartonicMothers); auto negDaughter = tracks.rawIteratorAt(v0candidate.negDauId() - tracks.offset()); - mNegDauManager.template fill(negDaughter, tracks, mcParticles, mcMothers, mcPartonicMothers); - if constexpr (modes::isFlagSet(mode, modes::Mode::kAnalysis)) { + mNegDauManager.template fill(negDaughter, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + if constexpr (modes::isFlagSet(mode, modes::Mode::kReco)) { this->fillAnalysis(v0candidate); } if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { - this->fillQa(v0candidate); + this->fillQa(v0candidate, posDaughter, negDaughter); } if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { - this->template fillMc(v0candidate, mcParticles, mcMothers, mcPartonicMothers); + this->template fillMc(v0candidate, col, mcParticles, mcMothers, mcPartonicMothers); + } + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { + this->fillFitVars(v0candidate); } } @@ -451,6 +511,7 @@ class V0HistManager mHistogramRegistry->add(analysisDir + getHistNameV2(kPhi, HistTable), getHistDesc(kPhi, HistTable), getHistType(kPhi, HistTable), {V0Specs.at(kPhi)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kMass, HistTable), getHistDesc(kMass, HistTable), getHistType(kMass, HistTable), {V0Specs.at(kMass)}); mHistogramRegistry->add(analysisDir + getHistNameV2(kSign, HistTable), getHistDesc(kSign, HistTable), getHistType(kSign, HistTable), {V0Specs.at(kSign)}); + mHistogramRegistry->add(analysisDir + getHistNameV2(kPtVsMass, HistTable), getHistDesc(kPtVsMass, HistTable), getHistType(kPtVsMass, HistTable), {V0Specs.at(kPtVsMass)}); } void initQa(std::map> const& V0Specs) @@ -458,7 +519,10 @@ class V0HistManager std::string qaDir = std::string(v0Prefix) + std::string(QaDir); mHistogramRegistry->add(qaDir + getHistNameV2(kCosPa, HistTable), getHistDesc(kCosPa, HistTable), getHistType(kCosPa, HistTable), {V0Specs.at(kCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPa, HistTable), getHistDesc(kPa, HistTable), getHistType(kPa, HistTable), {V0Specs.at(kPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kDecayDauDca, HistTable), getHistDesc(kDecayDauDca, HistTable), getHistType(kDecayDauDca, HistTable), {V0Specs.at(kDecayDauDca)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofPosDau, HistTable), getHistDesc(kStrangeTofPosDau, HistTable), getHistType(kStrangeTofPosDau, HistTable), {V0Specs.at(kStrangeTofPosDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofNegDau, HistTable), getHistDesc(kStrangeTofNegDau, HistTable), getHistType(kStrangeTofNegDau, HistTable), {V0Specs.at(kStrangeTofNegDau)}); mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxX, HistTable), getHistDesc(kDecayVtxX, HistTable), getHistType(kDecayVtxX, HistTable), {V0Specs.at(kDecayVtxX)}); mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxY, HistTable), getHistDesc(kDecayVtxY, HistTable), getHistType(kDecayVtxY, HistTable), {V0Specs.at(kDecayVtxY)}); mHistogramRegistry->add(qaDir + getHistNameV2(kDecayVtxZ, HistTable), getHistDesc(kDecayVtxZ, HistTable), getHistType(kDecayVtxZ, HistTable), {V0Specs.at(kDecayVtxZ)}); @@ -469,7 +533,7 @@ class V0HistManager mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsEta, HistTable), getHistDesc(kPtVsEta, HistTable), getHistType(kPtVsEta, HistTable), {V0Specs.at(kPtVsEta)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPhi, HistTable), getHistDesc(kPtVsPhi, HistTable), getHistType(kPtVsPhi, HistTable), {V0Specs.at(kPtVsPhi)}); mHistogramRegistry->add(qaDir + getHistNameV2(kPhiVsEta, HistTable), getHistDesc(kPhiVsEta, HistTable), getHistType(kPhiVsEta, HistTable), {V0Specs.at(kPhiVsEta)}); - mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsCosPa, HistTable), getHistDesc(kPtVsCosPa, HistTable), getHistType(kPtVsCosPa, HistTable), {V0Specs.at(kPtVsCosPa)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kPtVsPa, HistTable), getHistDesc(kPtVsPa, HistTable), getHistType(kPtVsPa, HistTable), {V0Specs.at(kPtVsPa)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMassLambda, HistTable), getHistDesc(kMassLambda, HistTable), getHistType(kMassLambda, HistTable), {V0Specs.at(kMassLambda)}); mHistogramRegistry->add(qaDir + getHistNameV2(kMassAntiLambda, HistTable), getHistDesc(kMassAntiLambda, HistTable), getHistType(kMassAntiLambda, HistTable), {V0Specs.at(kMassAntiLambda)}); @@ -480,6 +544,9 @@ class V0HistManager mHistogramRegistry->add(qaDir + getHistNameV2(kLambdaMassVsAntiLambdaMass, HistTable), getHistDesc(kLambdaMassVsAntiLambdaMass, HistTable), getHistType(kLambdaMassVsAntiLambdaMass, HistTable), {V0Specs.at(kLambdaMassVsAntiLambdaMass)}); mHistogramRegistry->add(qaDir + getHistNameV2(kK0shortMassVsLambdaMass, HistTable), getHistDesc(kK0shortMassVsLambdaMass, HistTable), getHistType(kK0shortMassVsLambdaMass, HistTable), {V0Specs.at(kK0shortMassVsLambdaMass)}); mHistogramRegistry->add(qaDir + getHistNameV2(kK0shortMassVsAntiLambdaMass, HistTable), getHistDesc(kK0shortMassVsAntiLambdaMass, HistTable), getHistType(kK0shortMassVsAntiLambdaMass, HistTable), {V0Specs.at(kK0shortMassVsAntiLambdaMass)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofPosDau, HistTable), getHistDesc(kStrangeTofVsTofPosDau, HistTable), getHistType(kStrangeTofVsTofPosDau, HistTable), {V0Specs.at(kStrangeTofVsTofPosDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kStrangeTofVsTofNegDau, HistTable), getHistDesc(kStrangeTofVsTofNegDau, HistTable), getHistType(kStrangeTofVsTofNegDau, HistTable), {V0Specs.at(kStrangeTofVsTofNegDau)}); + mHistogramRegistry->add(qaDir + getHistNameV2(kArmenterosQtVsAlpha, HistTable), getHistDesc(kArmenterosQtVsAlpha, HistTable), getHistType(kArmenterosQtVsAlpha, HistTable), {V0Specs.at(kArmenterosQtVsAlpha)}); } } @@ -503,24 +570,36 @@ class V0HistManager mHistogramRegistry->add(mcDir + getHistNameV2(kPdg, HistTable), getHistDesc(kPdg, HistTable), getHistType(kPdg, HistTable), {V0Specs.at(kPdg)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgMother, HistTable), getHistDesc(kPdgMother, HistTable), getHistType(kPdgMother, HistTable), {V0Specs.at(kPdgMother)}); mHistogramRegistry->add(mcDir + getHistNameV2(kPdgPartonicMother, HistTable), getHistDesc(kPdgPartonicMother, HistTable), getHistType(kPdgPartonicMother, HistTable), {V0Specs.at(kPdgPartonicMother)}); + } - if (mPlotOrigins) { - mHistogramRegistry->add(mcDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); - mHistogramRegistry->add(mcDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + // fraction estimation (CPA); only called when mode carries kFitVars + template + void initFitVars(std::map> const& V0Specs) + { + std::string fitVarsDir = std::string(v0Prefix) + std::string(FitVarsDir); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFractionCpa, HistTable), getHistDesc(kFractionCpa, HistTable), getHistType(kFractionCpa, HistTable), {V0Specs.at(kFractionCpa)}); - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); - } - if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + // per-origin truth-split fraction-CPA templates; need MC truth, so only booked when the mode + // also carries kMc + if constexpr (modes::isFlagSet(mode, modes::Mode::kMc)) { + if (mPlotOrigins) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kNoMcParticle, HistTable), getHistDesc(kNoMcParticle, HistTable), getHistType(kNoMcParticle, HistTable), {V0Specs.at(kNoMcParticle)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kPrimary, HistTable), getHistDesc(kPrimary, HistTable), getHistType(kPrimary, HistTable), {V0Specs.at(kPrimary)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromWrongCollision, HistTable), getHistDesc(kFromWrongCollision, HistTable), getHistType(kFromWrongCollision, HistTable), {V0Specs.at(kFromWrongCollision)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kFromMaterial, HistTable), getHistDesc(kFromMaterial, HistTable), getHistType(kFromMaterial, HistTable), {V0Specs.at(kFromMaterial)}); + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kMissidentified, HistTable), getHistDesc(kMissidentified, HistTable), getHistType(kMissidentified, HistTable), {V0Specs.at(kMissidentified)}); + + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary1, HistTable), getHistDesc(kSecondary1, HistTable), getHistType(kSecondary1, HistTable), {V0Specs.at(kSecondary1)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary2, HistTable), getHistDesc(kSecondary2, HistTable), getHistType(kSecondary2, HistTable), {V0Specs.at(kSecondary2)}); + } + if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3) { + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondary3, HistTable), getHistDesc(kSecondary3, HistTable), getHistType(kSecondary3, HistTable), {V0Specs.at(kSecondary3)}); + } + mHistogramRegistry->add(fitVarsDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } - mHistogramRegistry->add(mcDir + getHistNameV2(kSecondaryOther, HistTable), getHistDesc(kSecondaryOther, HistTable), getHistType(kSecondaryOther, HistTable), {V0Specs.at(kSecondaryOther)}); } } @@ -531,6 +610,7 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(getHistName(kEta, HistTable)), v0candidate.eta()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPhi, HistTable)), v0candidate.phi()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(getHistName(kMass, HistTable)), v0candidate.mass()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(getHistName(kPtVsMass, HistTable)), v0candidate.pt(), v0candidate.mass()); if constexpr (modes::isEqual(v0, modes::V0::kLambda) || modes::isEqual(v0, modes::V0::kAntiLambda)) { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(AnalysisDir) + HIST(getHistName(kSign, HistTable)), v0candidate.sign()); @@ -541,82 +621,105 @@ class V0HistManager } template - void fillQa(T const& v0candidate) + void fillFitVars(T const& v0candidate) + { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFractionCpa, HistTable)), v0candidate.pt(), v0candidate.cosPa()); + } + + template + void fillQa(T1 const& v0candidate, T2 const& posDau, T3 const& negDau) { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kCosPa, HistTable)), v0candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPa, HistTable)), v0candidate.pa()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kDecayDauDca, HistTable)), v0candidate.dauDca()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofPosDau, HistTable)), v0candidate.strangeTofPosDau()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofNegDau, HistTable)), v0candidate.strangeTofNegDau()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxX, HistTable)), v0candidate.decayVtxX()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxY, HistTable)), v0candidate.decayVtxY()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtxZ, HistTable)), v0candidate.decayVtxZ()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kDecayVtx, HistTable)), v0candidate.decayVtx()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kTransRadius, HistTable)), v0candidate.transRadius()); + float massLambda = 0; + float massAntiLambda = 0; + float massK0short = 0; + float tofPosDau = 0; + float tofNegDau = 0; + + if constexpr (modes::isEqual(v0, modes::V0::kLambda) || modes::isEqual(v0, modes::V0::kAntiLambda)) { + massK0short = v0candidate.massK0short(); + if (v0candidate.sign() > 0) { + massLambda = v0candidate.mass(); + massAntiLambda = v0candidate.massAnti(); + tofPosDau = posDau.tofNSigmaPr(); + tofNegDau = negDau.tofNSigmaPi(); + } else { + massLambda = v0candidate.massAnti(); + massAntiLambda = v0candidate.mass(); + tofPosDau = posDau.tofNSigmaPi(); + tofNegDau = negDau.tofNSigmaPr(); + } + } + if constexpr (modes::isEqual(v0, modes::V0::kK0short)) { + massK0short = v0candidate.mass(); + massLambda = v0candidate.massLambda(); + massAntiLambda = v0candidate.massAntiLambda(); + tofPosDau = posDau.tofNSigmaPi(); + tofNegDau = negDau.tofNSigmaPi(); + } + + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassLambda, HistTable)), massLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassAntiLambda, HistTable)), massAntiLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassK0short, HistTable)), massK0short); + if (mPlot2d) { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsEta, HistTable)), v0candidate.pt(), v0candidate.eta()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPhi, HistTable)), v0candidate.pt(), v0candidate.phi()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPhiVsEta, HistTable)), v0candidate.phi(), v0candidate.eta()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsCosPa, HistTable)), v0candidate.pt(), v0candidate.cosPa()); - - if constexpr (modes::isEqual(v0, modes::V0::kLambda) || modes::isEqual(v0, modes::V0::kAntiLambda)) { - float massLambda, massAntiLambda; - if (v0candidate.sign() > 0) { - massLambda = v0candidate.mass(); - massAntiLambda = v0candidate.massAnti(); - } else { - massLambda = v0candidate.massAnti(); - massAntiLambda = v0candidate.mass(); - } - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassLambda, HistTable)), massLambda); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassAntiLambda, HistTable)), massAntiLambda); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassK0short, HistTable)), v0candidate.massK0short()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsLambdaMass, HistTable)), v0candidate.pt(), massLambda); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsAntiLambdaMass, HistTable)), v0candidate.pt(), massAntiLambda); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsK0shortMass, HistTable)), v0candidate.pt(), v0candidate.massK0short()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kLambdaMassVsAntiLambdaMass, HistTable)), massLambda, massAntiLambda); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsLambdaMass, HistTable)), v0candidate.massK0short(), massLambda); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsAntiLambdaMass, HistTable)), v0candidate.massK0short(), massAntiLambda); - } - - if constexpr (modes::isEqual(v0, modes::V0::kK0short)) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassLambda, HistTable)), v0candidate.massLambda()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassAntiLambda, HistTable)), v0candidate.massAntiLambda()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kMassK0short, HistTable)), v0candidate.mass()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsLambdaMass, HistTable)), v0candidate.pt(), v0candidate.massLambda()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsAntiLambdaMass, HistTable)), v0candidate.pt(), v0candidate.massAntiLambda()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsK0shortMass, HistTable)), v0candidate.pt(), v0candidate.mass()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kLambdaMassVsAntiLambdaMass, HistTable)), v0candidate.massLambda(), v0candidate.massAntiLambda()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsLambdaMass, HistTable)), v0candidate.mass(), v0candidate.massLambda()); - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsAntiLambdaMass, HistTable)), v0candidate.mass(), v0candidate.massAntiLambda()); - } + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsPa, HistTable)), v0candidate.pt(), v0candidate.pa()); + + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsLambdaMass, HistTable)), v0candidate.pt(), massLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsAntiLambdaMass, HistTable)), v0candidate.pt(), massAntiLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kPtVsK0shortMass, HistTable)), v0candidate.pt(), massK0short); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kLambdaMassVsAntiLambdaMass, HistTable)), massLambda, massAntiLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsLambdaMass, HistTable)), massK0short, massLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kK0shortMassVsAntiLambdaMass, HistTable)), massK0short, massAntiLambda); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofPosDau, HistTable)), v0candidate.strangeTofPosDau(), tofPosDau); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kStrangeTofVsTofNegDau, HistTable)), v0candidate.strangeTofNegDau(), tofNegDau); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(QaDir) + HIST(getHistName(kArmenterosQtVsAlpha, HistTable)), v0candidate.alpha(), v0candidate.qtArm()); } } - template - void fillMc(T1 const& v0Candidate, T2 const& /*mcParticles*/, T3 const& /*mcMothers*/, T4 const& /*mcPartonicMothers*/) + template + void fillMc(T1 const& v0Candidate, T2 const& col, T3 const& /*mcParticles*/, T4 const& /*mcMothers*/, T5 const& /*mcPartonicMothers*/) { // No MC Particle if (!v0Candidate.has_fMcParticle()) { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), 0); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kNoMcParticle)); - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kNoMcParticle, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kNoMcParticle, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } } return; } // Retrieve MC particle - auto mcParticle = v0Candidate.template fMcParticle_as(); + auto mcParticle = v0Candidate.template fMcParticle_as(); + + // whether a particle is associated to a wrong collision or not cannot be known by the producer so we check it here + bool fromWrongCollision = mcParticle.fMcColId() != col.fMcColId(); - // missidentifed particles are special case // whether a particle is missidentfied or not cannot be known by the producer so we check it here bool isMissidentified = mcParticle.pdgCode() != mPdgCode; mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kTruePtVsPt, HistTable)), mcParticle.pt(), v0Candidate.pt()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kTrueEtaVsEta, HistTable)), mcParticle.eta(), v0Candidate.eta()); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kTruePhiVsPhi, HistTable)), mcParticle.phi(), v0Candidate.phi()); - if (isMissidentified) { + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kFromWrongCollision)); + } else if (isMissidentified) { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), static_cast(modes::McOrigin::kMissidentified)); } else { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kOrigin, HistTable)), mcParticle.origin()); @@ -624,57 +727,56 @@ class V0HistManager mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdg, HistTable)), mcParticle.pdgCode()); // get mother - if (v0Candidate.has_fMcMother()) { - auto mother = v0Candidate.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), mother.pdgCode()); } else { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdgMother, HistTable)), 0); } // get partonic mother - if (v0Candidate.has_fMcPartMoth()) { - auto partonicMother = v0Candidate.template fMcPartMoth_as(); + if (mcParticle.has_fMcPartMoth()) { + auto partonicMother = mcParticle.template fMcPartMoth_as(); mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), partonicMother.pdgCode()); } else { mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPdgPartonicMother, HistTable)), 0); } - if constexpr (modes::isFlagSet(mode, modes::Mode::kQa)) { + if constexpr (modes::isFlagSet(mode, modes::Mode::kFitVars)) { if (mPlotOrigins) { // check first if particle is missidentified - if (isMissidentified) { + if (fromWrongCollision) { + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + } else if (isMissidentified) { // if it is, we fill it as such - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kMissidentified, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kMissidentified, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else { // if not, we fill it acccoridng to its origin switch (static_cast(mcParticle.origin())) { case modes::McOrigin::kPhysicalPrimary: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kPrimary, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); - break; - case modes::McOrigin::kFromWrongCollision: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromWrongCollision, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kPrimary, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; case modes::McOrigin::kFromMaterial: - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kFromMaterial, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kFromMaterial, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); break; case modes::McOrigin::kFromSecondaryDecay: - if (v0Candidate.has_fMcMother()) { - auto mother = v0Candidate.template fMcMother_as(); + if (mcParticle.has_fMcMother()) { + auto mother = mcParticle.template fMcMother_as(); int motherPdgCode = std::abs(mother.pdgCode()); // Switch on PDG of the mother if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel1 && motherPdgCode == mPdgCodesSecondaryMother[0]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary1, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary1, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel2 && motherPdgCode == mPdgCodesSecondaryMother[1]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary2, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary2, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else if (mPlotNSecondaries >= histmanager::kSecondaryPlotLevel3 && motherPdgCode == mPdgCodesSecondaryMother[2]) { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondary3, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondary3, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } else { - mHistogramRegistry->fill(HIST(v0Prefix) + HIST(McDir) + HIST(getHistName(kSecondaryOther, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); + mHistogramRegistry->fill(HIST(v0Prefix) + HIST(FitVarsDir) + HIST(getHistName(kSecondaryOther, HistTable)), v0Candidate.pt(), v0Candidate.cosPa()); } } break; default: - LOG(warn) << "Encounted partilce with unknown origin!"; + LOG(warn) << "Encountered particle with unknown origin!"; break; } } @@ -684,7 +786,10 @@ class V0HistManager o2::framework::HistogramRegistry* mHistogramRegistry = nullptr; int mPdgCode = 0; - bool mPlotOrigins = false; + // true by default so the per-origin FitVars templates are booked/filled even where + // enableOptionalHistograms() is never called (e.g. the pair task, which has no QA-binning + // config); the QA task can still opt out via its plotOrigins Configurable + bool mPlotOrigins = true; int mPlotNSecondaries = 0; std::array mPdgCodesSecondaryMother = {0}; bool mPlot2d = false; @@ -692,6 +797,5 @@ class V0HistManager trackhistmanager::TrackHistManager mPosDauManager; trackhistmanager::TrackHistManager mNegDauManager; }; -}; // namespace v0histmanager -}; // namespace o2::analysis::femto +}; // namespace o2::analysis::femto::v0histmanager #endif // PWGCF_FEMTO_CORE_V0HISTMANAGER_H_ diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 5792f152278..4816b1527b7 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -17,14 +17,17 @@ #define PWGCF_FEMTO_DATAMODEL_FEMTOTABLES_H_ #include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGCF/Femto/Core/modes.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include #include #include -#include +#include #include #include @@ -33,17 +36,80 @@ namespace o2::aod { namespace femtocollisions { -DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::CollisionMaskType); //! Bitmask for collision selections -DECLARE_SOA_COLUMN(CollisionTag, collisionTag, femtodatatypes::CollisionTagType); //! Bitmask for collision selections - -DECLARE_SOA_COLUMN(PosX, posX, float); //! x coordinate of vertex -DECLARE_SOA_COLUMN(PosY, posY, float); //! y coordinate of vertex -DECLARE_SOA_COLUMN(PosZ, posZ, float); //! z coordinate of vertex -DECLARE_SOA_COLUMN(Mult, mult, float); //! Multiplicity estimator set by producer -DECLARE_SOA_COLUMN(Cent, cent, float); //! Centrality (~= multiplicity percentile) estimator set by producer -DECLARE_SOA_COLUMN(MagField, magField, int8_t); //! Magnetic field in kG (5 kG at normal configuration and 2kG in low B field configuration) -DECLARE_SOA_COLUMN(Sphericity, sphericity, float); //! Sphericity of the event -DECLARE_SOA_COLUMN(Qn, qn, float); //! qn bins for dividing eventsfemtab +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::CollisionMaskType); //! Bitmask for collision selections +DECLARE_SOA_COLUMN(CollisionTag, collisionTag, o2::analysis::femto::datatypes::CollisionTagType); //! Bitmask for collision selections + +DECLARE_SOA_COLUMN(PosX, posX, float); //! x coordinate of vertex +DECLARE_SOA_COLUMN(PosY, posY, float); //! y coordinate of vertex +DECLARE_SOA_COLUMN(PosZ, posZ, float); //! z coordinate of vertex +DECLARE_SOA_COLUMN(Mult, mult, float); //! Multiplicity estimator set by producer +DECLARE_SOA_COLUMN(Cent, cent, float); //! Centrality (~= multiplicity percentile) estimator set by producer +DECLARE_SOA_COLUMN(MagField, magField, int8_t); //! Magnetic field in kG (5 kG at normal configuration and 2kG in low B field configuration) +DECLARE_SOA_COLUMN(Sphericity, sphericity, float); //! Sphericity of the event +DECLARE_SOA_COLUMN(Qvec, qvec, float); //! qvector +DECLARE_SOA_COLUMN(EventPlaneAngle, eventPlaneAngle, float); //! event plane angle (for corresponding q vector) + +namespace lite +{ +constexpr float PosZMin = -20.f; +constexpr float PosZMax = 20.f; +constexpr float PosZStep = 0.5f; // bin vtz in 0.5cm steps + +constexpr float CentMin = 0.f; +constexpr float CentMax = 100.f; +constexpr float CentStep = 0.5f; // bin centrality in 0.5% steps + +constexpr float MultStep = 1.f; // round multiplicity to nearest integer + +constexpr float QvecMin = 1e-3f; // close to 0, but not 0 due to log +constexpr float QvecMax = 1e3f; // usual range for qvector + +constexpr float EventPlaneAngleMin = 0.f; +constexpr float EventPlaneAngleMax = o2::constants::math::TwoPI; // angle bound in [0,2pi) +constexpr float EventPlaneAngleStep = (EventPlaneAngleMax - EventPlaneAngleMin) / 65536.f; + +inline uint8_t binPosZ(float posZ) { return o2::analysis::femto::utils::binLinear(posZ, PosZMin, PosZMax, PosZStep); } +inline float unBinPosZ(uint8_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, PosZMin, PosZStep); } + +inline uint8_t binCent(float cent) { return o2::analysis::femto::utils::binLinear(cent, CentMin, CentMax, CentStep); } +inline float unBinCent(uint8_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, CentMin, CentStep); } + +inline uint16_t binMult(float mult) { return o2::analysis::femto::utils::binLinear(mult, 0.f, 65535.f, MultStep); } // use full range of uint16_t +inline float unBinMult(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, 0.f, MultStep); } + +inline uint16_t binQvec(float qvec) { return o2::analysis::femto::utils::binLogUnsigned(qvec, QvecMin, QvecMax); } +inline float unBinQvec(uint16_t binned) { return o2::analysis::femto::utils::unBinLogUnsigned(binned, QvecMin, QvecMax); } + +inline uint16_t binEventPlaneAngle(float eventPlaneAngle) { return o2::analysis::femto::utils::binLinear(eventPlaneAngle, EventPlaneAngleMin, EventPlaneAngleMax, EventPlaneAngleStep); } +inline float unBinEventPlaneAngle(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, EventPlaneAngleMin, EventPlaneAngleStep); } + +DECLARE_SOA_COLUMN(BinnedPosZ, binnedPosZ, uint8_t); +DECLARE_SOA_COLUMN(BinnedMult, binnedMult, uint16_t); +DECLARE_SOA_COLUMN(BinnedCent, binnedCent, uint8_t); +DECLARE_SOA_COLUMN(BinnedQvec, binnedQvec, uint16_t); +DECLARE_SOA_COLUMN(BinnedEventPlaneAngle, binnedEventPlaneAngle, uint16_t); + +DECLARE_SOA_DYNAMIC_COLUMN(PosZ, posZ, + [](uint8_t binnedPosZ) -> float { + return unBinPosZ(binnedPosZ); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Mult, mult, + [](uint16_t binnedMult) -> float { + return unBinMult(binnedMult); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Cent, cent, + [](uint8_t binnedCent) -> float { + return unBinCent(binnedCent); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Qvec, qvec, + [](uint16_t binnedQvec) -> float { + return unBinQvec(binnedQvec); + }); +DECLARE_SOA_DYNAMIC_COLUMN(EventPlaneAngle, eventPlaneAngle, + [](uint16_t binnedEventPlaneAngle) -> float { + return unBinEventPlaneAngle(binnedEventPlaneAngle); + }); +} // namespace lite } // namespace femtocollisions // table for basic collision information @@ -57,6 +123,20 @@ using FCols = FCols_001; using FCol = FCols::iterator; using StoredFCols = StoredFCols_001; +// table for basic collision information, compressed/binned information +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteCols_001, "FLITECOL", 1, //! femto collisions, binned information + o2::soa::Index<>, + femtocollisions::lite::BinnedPosZ, + femtocollisions::lite::BinnedMult, + femtocollisions::lite::BinnedCent, + femtocollisions::MagField, + femtocollisions::lite::PosZ, + femtocollisions::lite::Mult, + femtocollisions::lite::Cent); +using FLiteCols = FLiteCols_001; +using FLiteCol = FLiteCols::iterator; +using StoredFLiteCols = StoredFLiteCols_001; + // table for collisions selections DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMasks_001, "FCOLMASK", 1, //! collision masks femtocollisions::Mask); @@ -67,10 +147,21 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FColSphericities_001, "FCOLSPHERICITY", 1, // femtocollisions::Sphericity); using FColSphericities = FColSphericities_001; -// table for qn values -DECLARE_SOA_TABLE_STAGED_VERSIONED(FColQns_001, "FCOLQN", 1, //! qn vector - femtocollisions::Qn); -using FColQns = FColQns_001; +// table for event shape analysis +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColShapes_001, "FCOLSHAPE", 1, //! event shape + femtocollisions::Qvec, + femtocollisions::EventPlaneAngle); +using FColShapes = FColShapes_001; +using StoredFColShapes = StoredFColShapes_001; + +// lite table for event shape analysis +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteColShapes_001, "FLITECOLSHAPE", 1, //! event shape + femtocollisions::lite::BinnedQvec, + femtocollisions::lite::BinnedEventPlaneAngle, + femtocollisions::lite::Qvec, + femtocollisions::lite::EventPlaneAngle); +using FLiteColShapes = FLiteColShapes_001; +using StoredFLiteColShapes = StoredFLiteColShapes_001; // table for primary vertex location DECLARE_SOA_TABLE_STAGED_VERSIONED(FColPos_001, "FCOLPOS", 1, //! full vertex position @@ -79,28 +170,32 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FColPos_001, "FCOLPOS", 1, //! full vertex po using FColPos = FColPos_001; // table for different multiplicity estimators -DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMults_001, "FCOLMULT", 1, //! multiplicities - mult::MultFT0A, mult::MultFT0C, //! FIT detectors - mult::MultNTracksPVeta1, //! number of PV contribs total - mult::MultNTracksPVetaHalf, //! global track multiplicities - evsel::NumTracksInTimeRange, //! occupancy (number of track in time range) - evsel::SumAmpFT0CInTimeRange); //! occupancy (FT0C amplitude in time range) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMults_001, "FCOLMULT", 1, //! multiplicities + mult::MultFT0A, //! FT0A detectors + mult::MultFT0C, //! FT0C detectors + collision::NumContrib, //! number of tracks used tto find PV + mult::MultNTracksPVeta1, //! number of PV contribs total + mult::MultNTracksPVetaHalf, //! global track multiplicities + evsel::NumTracksInTimeRange, //! occupancy (number of tracks from different collisions in time range) + evsel::SumAmpFT0CInTimeRange); //! occupancy (FT0C amplitude in time range) using FColMults = FColMults_001; // table for different centrality (multiplicity percentile) estimators DECLARE_SOA_TABLE_STAGED_VERSIONED(FColCents_001, "FCOLCENT", 1, //! centralities cent::CentFT0A, //! centrality from FT0A - cent::CentFT0C); //! centrality from FT0C + cent::CentFT0C, //! centrality from FT0C + cent::CentFT0M); //! centrality from FT0M using FColCents = FColCents_001; namespace femtobase // all "basic" information to perform femto analysis, i.e. collision index and kinematics -// split kinematics in stored, i.e. stored in derived data, and dynmaic, i.e. can be computed on the fly +// split kinematics in stored, i.e. stored in derived data, and dynamic, i.e. can be computed on the fly { namespace stored { // static columns DECLARE_SOA_INDEX_COLUMN(FCol, fCol); //! collision index of femto collision table +DECLARE_SOA_INDEX_COLUMN(FLiteCol, fLiteCol); //! collision index of femto collision table DECLARE_SOA_COLUMN(SignedPt, signedPt, float); //! signed pt DECLARE_SOA_COLUMN(Pt, pt, float); //! pt DECLARE_SOA_COLUMN(Eta, eta, float); //! eta @@ -119,14 +214,13 @@ DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, //! transverse momentum [](float signedPt) -> float { return std::fabs(signedPt); }); -// use fabs for pt so it can also be used with signed pt DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! momentum in x [](float pt, float phi) -> float { - return std::fabs(pt) * std::sin(phi); + return std::fabs(pt) * std::cos(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! momentum in y [](float pt, float phi) -> float { - return std::fabs(pt) * std::cos(phi); + return std::fabs(pt) * std::sin(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! momentum in z [](float pt, float eta) -> float { @@ -141,12 +235,78 @@ DECLARE_SOA_DYNAMIC_COLUMN(Theta, theta, //! theta return 2.f * std::atan(std::exp(-eta)); }); } // namespace dynamic + +namespace lite +{ + +constexpr float EtaMin = -1.5f; +constexpr float EtaMax = 1.5f; +constexpr float EtaStep = (EtaMax - EtaMin) / 65536.f; + +constexpr float PhiMin = 0.f; +constexpr float PhiMax = constants::math::TwoPI; // phi is bound on [0,2pi) +constexpr float PhiStep = (PhiMax - PhiMin) / 65536.f; + +constexpr float PtMagMin = 0.1f; // lowest pt cut for pions is usually 0.12-0.14 GeV/c +constexpr float PtMagMax = 6.f; // sensible upper limit for pt + +inline uint16_t binEta(float eta) { return o2::analysis::femto::utils::binLinear(eta, EtaMin, EtaMax, EtaStep); } +inline float unBinEta(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, EtaMin, EtaStep); } + +inline uint16_t binPhi(float phi) { return o2::analysis::femto::utils::binLinear(phi, PhiMin, PhiMax, PhiStep); } +inline float unBinPhi(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, PhiMin, PhiStep); } + +inline uint16_t binSignedPt(float signedPt) { return o2::analysis::femto::utils::binLogSigned(signedPt, PtMagMin, PtMagMax); } +inline float unBinSignedPt(uint16_t binned) { return o2::analysis::femto::utils::unBinLogSigned(binned, PtMagMin, PtMagMax); } + +inline uint16_t binUnsignedPt(float unsignedPt) { return o2::analysis::femto::utils::binLogUnsigned(unsignedPt, PtMagMin, PtMagMax); } +inline float unBinUnsignedPt(uint16_t binned) { return o2::analysis::femto::utils::unBinLogUnsigned(binned, PtMagMin, PtMagMax); } + +DECLARE_SOA_COLUMN(SignedBinnedPt, signedBinnedPt, uint16_t); +DECLARE_SOA_COLUMN(UnsignedBinnedPt, unsignedBinnedPt, uint16_t); +DECLARE_SOA_COLUMN(BinnedEta, binnedEta, uint16_t); +DECLARE_SOA_COLUMN(BinnedPhi, binnedPhi, uint16_t); + +DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, + [](uint16_t signedBinnedPt) -> int { + return o2::analysis::femto::utils::unBinSign(signedBinnedPt); + }); +DECLARE_SOA_DYNAMIC_COLUMN(SignedPt, signedPt, + [](uint16_t signedBinnedPt) -> float { + return unBinSignedPt(signedBinnedPt); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, + [](uint16_t binnedEta) -> float { + return unBinEta(binnedEta); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, + [](uint16_t binnedPhi) -> float { + return unBinPhi(binnedPhi); + }); + +namespace signedpt +{ +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, + [](uint16_t signedBinnedPt) -> float { + return std::fabs(unBinSignedPt(signedBinnedPt)); + }); +} // namespace signedpt + +namespace unsignedpt +{ +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, + [](uint16_t unsignedBinnedPt) -> float { + return unBinUnsignedPt(unsignedBinnedPt); + }); +} // namespace unsignedpt + +} // namespace lite } // namespace femtobase namespace femtotracks { // columns for track selections -DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::TrackMaskType); //! Bitmask for track selections +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for track selections // columns for DCA DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); //! Dca in XY plane @@ -172,6 +332,17 @@ DECLARE_SOA_DYNAMIC_COLUMN(TpcSharedOverFound, tpcSharedOverFound, //! Number of [](uint8_t tpcNclsFound, uint8_t tpcNClsShared) -> float { return static_cast(tpcNClsShared) / static_cast(tpcNclsFound); }); DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); //! Tpc chi2 +// detector map and row type +DECLARE_SOA_COLUMN(DetectorMap, detectorMap, uint8_t); //! Detector map of the track (same bit layout as o2::aod::track::DetectorMap) +DECLARE_SOA_DYNAMIC_COLUMN(HasIts, hasIts, [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::ITS; }); //! Track has ITS +DECLARE_SOA_DYNAMIC_COLUMN(HasTpc, hasTpc, [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::TPC; }); //! Track has TPC +DECLARE_SOA_DYNAMIC_COLUMN(HasTof, hasTof, [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::TOF; }); //! Track has TOF +DECLARE_SOA_COLUMN(FillType, fillType, o2::analysis::femto::datatypes::TrackType); //! modes::Track this row was written as (kTrack = selected track, otherwise daughter-only row) +DECLARE_SOA_DYNAMIC_COLUMN(IsDaughterOnly, isDaughterOnly, //! True if the row was only written to resolve a daughter index (not a selected track) + [](o2::analysis::femto::datatypes::TrackType fillType) -> bool { + return fillType != static_cast(o2::analysis::femto::modes::Track::kTrack); + }); + // tof related information DECLARE_SOA_COLUMN(TofBeta, tofBeta, float); //! Tof beta // tof mass will be stored in mass column @@ -220,6 +391,38 @@ DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaDe, tpctofNSigmaDe, [](float tpc, float t DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaTr, tpctofNSigmaTr, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for triton DECLARE_SOA_DYNAMIC_COLUMN(TpctofNSigmaHe, tpctofNSigmaHe, [](float tpc, float tof) -> float { return std::hypot(tpc, tof); }); //! Combined Nsigma separation with Tpc and Tof for helium3 +// --------------------------------------------------------------------------- +// purity: up to 4 species' nSigma (per detector: Its, Tpc, Tof) packed into a +// single int64_t column each, see utils::packPurity4/unpackPurity4. Which +// species occupies which of the 4 slots is decided at producer init time +// (from the active requirePidXxx flags, in canonical species order) and +// exposed via a self-documenting histogram -- it is not stored per-row. +// --------------------------------------------------------------------------- +constexpr float NSigmaPurityMagMin = 1e-3f; // resolution floor near the signal peak +constexpr float NSigmaPurityMagMax = 50.f; // clamp for the tails + +DECLARE_SOA_COLUMN(ItsPackedNSigma, itsPackedNSigma, int64_t); //! 4 species' Its nSigma, log-binned and packed +DECLARE_SOA_COLUMN(TpcPackedNSigma, tpcPackedNSigma, int64_t); //! 4 species' Tpc nSigma, log-binned and packed +DECLARE_SOA_COLUMN(TofPackedNSigma, tofPackedNSigma, int64_t); //! 4 species' Tof nSigma, log-binned and packed +// if false, all 4 Tof slots are meaningless placeholders (producer writes 0 to all of them) -- +// check this flag before reading any TofNSigmaSlotN; never infer missingness from the slot value +DECLARE_SOA_COLUMN(PurityHasTof, purityHasTof, bool); //! whether the Tof slots are meaningful for this track (species-independent) + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +// each dynamic column below is bound to its PackedNSigma column at the table-declaration +// site, e.g. femtotracks::ItsNSigmaSlot0 +#define FEMTO_PURITY_SLOT_COLUMN(Detector, detector) \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot0, detector##NSigmaSlot0, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 0, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot1, detector##NSigmaSlot1, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 1, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot2, detector##NSigmaSlot2, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 2, NSigmaPurityMagMin, NSigmaPurityMagMax); }); \ + DECLARE_SOA_DYNAMIC_COLUMN(Detector##NSigmaSlot3, detector##NSigmaSlot3, [](int64_t packed) -> float { return o2::analysis::femto::utils::unpackPurityNSigma4(packed, 3, NSigmaPurityMagMin, NSigmaPurityMagMax); }); + +FEMTO_PURITY_SLOT_COLUMN(Its, its) +FEMTO_PURITY_SLOT_COLUMN(Tpc, tpc) +FEMTO_PURITY_SLOT_COLUMN(Tof, tof) +#undef FEMTO_PURITY_SLOT_COLUMN +// NOLINTEND(cppcoreguidelines-macro-usage) + } // namespace femtotracks // table for basic track information @@ -240,6 +443,22 @@ using FTracks = FTracks_001; using FTrack = FTracks::iterator; using StoredFTracks = StoredFTracks_001; +// table for basic track information, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteTracks_001, "FLITETRACK", 1, //! femto tracks, binned kinematics + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi); +using FLiteTracks = FLiteTracks_001; +using FLiteTrack = FLiteTracks::iterator; +using StoredFLiteTracks = StoredFLiteTracks_001; + // table for track selections and PID selections DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackMasks_001, "FTRACKMASK", 1, //! track masks femtotracks::Mask); @@ -250,6 +469,7 @@ using StoredFTrackMasks = StoredFTrackMasks_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackMass_001, "FTRACKMASS", 1, //! track mass femtobase::stored::Mass); using FTrackMass = FTrackMass_001; +using StoredFTrackMass = StoredFTrackMass_001; // table for track DCA DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackDcas_001, "FTRACKDCAS", 1, //! track dcas @@ -257,6 +477,7 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackDcas_001, "FTRACKDCAS", 1, //! track dc femtotracks::DcaZ, femtotracks::Dca); using FTrackDcas = FTrackDcas_001; +using StoredFTrackDcas = StoredFTrackDcas_001; // table for extra track information DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackExtras_001, "FTRACKEXTRA", 1, //! track extra information @@ -271,8 +492,15 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackExtras_001, "FTRACKEXTRA", 1, //! track femtotracks::TpcNClsCrossedRows, femtotracks::TpcNClsShared, femtotracks::TofBeta, + femtotracks::TpcChi2NCl, + femtotracks::DetectorMap, + femtotracks::FillType, femtotracks::TpcCrossedRowsOverFound, - femtotracks::TpcSharedOverFound); + femtotracks::TpcSharedOverFound, + femtotracks::HasIts, + femtotracks::HasTpc, + femtotracks::HasTof, + femtotracks::IsDaughterOnly); using FTrackExtras = FTrackExtras_001; // table for extra PID information @@ -328,14 +556,47 @@ using FHeliumPids = FHeliumPids_001; using FTrackPids = soa::Join; +// tables for track purity extraction: up to 4 species' nSigma packed per detector (see +// femtotracks::PurityNSlots / utils::packPurity4). Slot assignment is not stored here -- +// it is exposed separately via a self-documenting histogram at producer init time. +DECLARE_SOA_TABLE_STAGED_VERSIONED(FItsPurity_001, "FITSPURITY", 1, //! up to 4 species' Its nSigma, packed + femtotracks::ItsPackedNSigma, + femtotracks::ItsNSigmaSlot0, + femtotracks::ItsNSigmaSlot1, + femtotracks::ItsNSigmaSlot2, + femtotracks::ItsNSigmaSlot3); +using FItsPurity = FItsPurity_001; +using StoredFItsPurity = StoredFItsPurity_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTpcPurity_001, "FTPCPURITY", 1, //! up to 4 species' Tpc nSigma, packed + femtotracks::TpcPackedNSigma, + femtotracks::TpcNSigmaSlot0, + femtotracks::TpcNSigmaSlot1, + femtotracks::TpcNSigmaSlot2, + femtotracks::TpcNSigmaSlot3); +using FTpcPurity = FTpcPurity_001; +using StoredFTpcPurity = StoredFTpcPurity_001; + +// also carries PurityHasTof (species-independent: whether this track reached Tof at all) -- +// the most natural home for it, since it only matters for interpreting the Tof slots +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTofPurity_001, "FTOFPURITY", 1, //! up to 4 species' Tof nSigma, packed + femtotracks::TofPackedNSigma, + femtotracks::PurityHasTof, + femtotracks::TofNSigmaSlot0, + femtotracks::TofNSigmaSlot1, + femtotracks::TofNSigmaSlot2, + femtotracks::TofNSigmaSlot3); +using FTofPurity = FTofPurity_001; +using StoredFTofPurity = StoredFTofPurity_001; + namespace femtotwotrackresonances { // columns for resonance bit masks -// DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::TwoTrackResonanceMaskType); //! Bitmask for resonance selections -DECLARE_SOA_COLUMN(MaskPosDau, maskPosDau, femtodatatypes::TrackMaskType); //! Bitmask for positive daughter -DECLARE_SOA_COLUMN(PosDauHasHighMomentum, posDauHasHighMomentum, bool); //! switch for pid threshold -DECLARE_SOA_COLUMN(MaskNegDau, maskNegDau, femtodatatypes::TrackMaskType); //! Bitmask for negative daughter -DECLARE_SOA_COLUMN(NegDauHasHighMomentum, negDauHasHighMomentum, bool); //! switch for pid threshold +// DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::TwoTrackResonanceMaskType); //! Bitmask for resonance selections +DECLARE_SOA_COLUMN(MaskPosDau, maskPosDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for positive daughter +DECLARE_SOA_COLUMN(PosDauHasHighMomentum, posDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNegDau, maskNegDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for negative daughter +DECLARE_SOA_COLUMN(NegDauHasHighMomentum, negDauHasHighMomentum, bool); //! switch for pid threshold // id columns for resonance daughter tracks DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for positive daughter track @@ -357,12 +618,14 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhis_001, "FPHI", 1, //! femto phis femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FPhis = FPhis_001; +using StoredFPhis = StoredFPhis_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhiMasks_001, "FPHIMASK", 1, //! mask for phis femtotwotrackresonances::MaskPosDau, femtotwotrackresonances::PosDauHasHighMomentum, femtotwotrackresonances::MaskNegDau, femtotwotrackresonances::NegDauHasHighMomentum); using FPhiMasks = FPhiMasks_001; +using StoredFPhiMasks = StoredFPhiMasks_001; // table for kstars DECLARE_SOA_TABLE_STAGED_VERSIONED(FKstar0s_001, "FKSTAR0", 1, //! femto k0star @@ -404,38 +667,89 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0s_001, "FRHO0", 1, //! femto rho0s femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FRho0s = FRho0s_001; +using StoredFRho0s = StoredFRho0s_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FRho0Masks_001, "FRHO0MASK", 1, //! rho0s masks femtotwotrackresonances::MaskPosDau, femtotwotrackresonances::PosDauHasHighMomentum, femtotwotrackresonances::MaskNegDau, femtotwotrackresonances::NegDauHasHighMomentum); using FRho0Masks = FRho0Masks_001; +using StoredFRho0Masks = StoredFRho0Masks_001; namespace femtov0s { // columns for bit masks -DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::V0MaskType); //! Bitmask for v0 selections +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::V0MaskType); //! Bitmask for v0 selections +// +namespace legacy001 +{ +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::V0MaskType001); //! Bitmask for v0 selections +} // columns for debug information DECLARE_SOA_COLUMN(MassAnti, massAnti, float); //! mass of particle using antiparticle hypothesis (for Lambda/AntiLambda extra table) DECLARE_SOA_COLUMN(MassLambda, massLambda, float); //! Mass of Lambda (for k0short table) DECLARE_SOA_COLUMN(MassAntiLambda, massAntiLambda, float); //! Mass of AntiLambda (for k0short table) DECLARE_SOA_COLUMN(MassK0short, massK0short, float); //! Mass of K0short (for lambda/antitlambda table) -DECLARE_SOA_COLUMN(CosPa, cosPa, float); //! Lambda daughter DCA at decay vertex -DECLARE_SOA_COLUMN(DauDca, dauDca, float); //! Lambda daughter DCA at decay vertex -DECLARE_SOA_COLUMN(TransRadius, transRadius, float); //! Lambda transvers radius -DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of Lambda decay vertex -DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of Lambda decay vertex -DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of Lambda decay vertex -DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of decay vertex from nominal interaction point +DECLARE_SOA_COLUMN(CosPa, cosPa, float); //! Cosine of poiting angle +DECLARE_SOA_DYNAMIC_COLUMN(Pa, pa, //! pointing angle + [](float cosPa) -> float { + return std::acos(cosPa); + }); +DECLARE_SOA_COLUMN(DauDca, dauDca, float); //! Lambda daughter DCA at decay vertex +DECLARE_SOA_COLUMN(StrangeTofPosDau, strangeTofPosDau, float); //! TOF Strangeness for positive daughter +DECLARE_SOA_COLUMN(StrangeTofNegDau, strangeTofNegDau, float); //! TOF Strangeness for negative daughter +DECLARE_SOA_COLUMN(TransRadius, transRadius, float); //! Lambda transvers radius +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of Lambda decay vertex +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of Lambda decay vertex +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of Lambda decay vertex +DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of decay vertex from nominal interaction point [](float vtxX, float vtxY, float vtxZ) -> float { return std::hypot(vtxX, vtxY, vtxZ); }); +DECLARE_SOA_COLUMN(Alpha, alpha, float); //! Armenteros-Podolanski alpha +DECLARE_SOA_COLUMN(QtArm, qtArm, float); //! Armenteros-Podolanski qt // id columns for Lambda daughter tracks DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for positive daughter track DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! index column for negative daughter track +namespace lite +{ + +constexpr float LambdaMassMin = 1.05f; // Kinematic lower limit +constexpr float LambdaMassMax = 1.30f; +constexpr float LambdaMassStep = (LambdaMassMax - LambdaMassMin) / 65536.f; + +// K0short: PDG mass 0.497611 GeV roughly ±100 MeV window +constexpr float K0shortMassMin = 0.4f; +constexpr float K0shortMassMax = 0.6f; +constexpr float K0shortMassStep = (K0shortMassMax - K0shortMassMin) / 65536.f; + +inline uint16_t binLambdaMass(float lambdaMass) { return o2::analysis::femto::utils::binLinear(lambdaMass, LambdaMassMin, LambdaMassMax, LambdaMassStep); } +inline float unBinLambdaMass(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, LambdaMassMin, LambdaMassStep); } + +inline uint16_t binK0shortMass(float lambdaMass) { return o2::analysis::femto::utils::binLinear(lambdaMass, K0shortMassMin, K0shortMassMax, K0shortMassStep); } +inline float unBinK0shortMass(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, K0shortMassMin, K0shortMassStep); } + +DECLARE_SOA_COLUMN(BinnedLambdaMass, binnedLambdaMass, uint16_t); +DECLARE_SOA_COLUMN(BinnedK0shortMass, binnedK0shortMass, uint16_t); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMass, lambdaMass, + [](uint16_t binnedLambdaMass) -> float { + return unBinLambdaMass(binnedLambdaMass); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(K0shortMass, k0shortMass, + [](uint16_t binnedK0shortMass) -> float { + return unBinK0shortMass(binnedK0shortMass); + }); + +// index for lite track table +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FLiteTracks, "_PosDau"); //! index column for positive daughter track +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FLiteTracks, "_NegDau"); //! index column for negative daughter track + +} // namespace lite } // namespace femtov0s // table for basic lambda information @@ -458,24 +772,58 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdas_001, "FLAMBDA", 1, //! femto lambdas using FLambdas = FLambdas_001; using StoredFLambdas = StoredFLambdas_001; -DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaMasks_001, "FLAMBDAMASK", 1, //! lambda masks +// table for basic lambda information with compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteLambdas_001, "FLITELAMBDA", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtov0s::lite::BinnedLambdaMass, + femtov0s::lite::PosDauId, + femtov0s::lite::NegDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtov0s::lite::LambdaMass); +using FLiteLambdas = FLiteLambdas_001; +using FLiteLambda = FLiteLambdas::iterator; +using StoredFLiteLambdas = StoredFLiteLambdas_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaMasks_001, "FLAMBDAMASK", 1, //! legacy lambda mask + femtov0s::legacy001::Mask); + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaMasks_002, "FLAMBDAMASK", 2, //! lambda mask femtov0s::Mask); -using FLambdaMasks = FLambdaMasks_001; -using StoredFLambdaMasks = StoredFLambdaMasks_001; + +using FLambdaMasks = FLambdaMasks_002; +using StoredFLambdaMasks = StoredFLambdaMasks_002; DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaExtras_001, "FLAMBDAEXTRA", 1, //! lambda extra information femtov0s::MassAnti, // put mass of antiparticle, i.e. antilambda mass for lambdas and vice versa femtov0s::MassK0short, femtov0s::CosPa, femtov0s::DauDca, + femtov0s::StrangeTofPosDau, + femtov0s::StrangeTofNegDau, femtov0s::TransRadius, femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, - femtov0s::DecayVtx); + femtov0s::Alpha, + femtov0s::QtArm, + femtov0s::DecayVtx, + femtov0s::Pa); using FLambdaExtras = FLambdaExtras_001; +// table for lambda fraction extraction (cpa only -- mass is already in FLambdas, no need to duplicate it) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaFrac_001, "FLAMBDAFRAC", 1, //! lambda cpa for fraction extraction + femtov0s::CosPa); +using FLambdaFrac = FLambdaFrac_001; + // table for basic k0short information DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shorts_001, "FK0SHORT", 1, //! femto k0shorts o2::soa::Index<>, @@ -494,28 +842,245 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shorts_001, "FK0SHORT", 1, //! femto k0sho using FK0shorts = FK0shorts_001; using StoredFK0shorts = StoredFK0shorts_001; -DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortMasks_001, "FK0SHORTMASK", 1, //! k0short masks +// table for basic lambda information with compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteK0shorts_001, "FLITEK0SHORT", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::UnsignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtov0s::lite::BinnedK0shortMass, + femtov0s::lite::PosDauId, + femtov0s::lite::NegDauId, + femtobase::lite::unsignedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtov0s::lite::K0shortMass); +using FLiteK0shorts = FLiteK0shorts_001; +using FLiteK0short = FLiteK0shorts::iterator; +using StoredFLiteK0shorts = StoredFLiteK0shorts_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortMasks_001, "FK0SHORTMASK", 1, //! legacy k0short masks + femtov0s::legacy001::Mask); + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortMasks_002, "FK0SHORTMASK", 2, //! k0short masks femtov0s::Mask); -using FK0shortMasks = FK0shortMasks_001; -using StoredFK0shortMasks = StoredFK0shortMasks_001; +using FK0shortMasks = FK0shortMasks_002; +using StoredFK0shortMasks = StoredFK0shortMasks_002; DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortExtras_001, "FK0SHORTEXTRA", 1, //! k0short extra information femtov0s::MassLambda, femtov0s::MassAntiLambda, femtov0s::CosPa, femtov0s::DauDca, + femtov0s::StrangeTofPosDau, + femtov0s::StrangeTofNegDau, femtov0s::TransRadius, femtov0s::DecayVtxX, femtov0s::DecayVtxY, femtov0s::DecayVtxZ, - femtov0s::DecayVtx); + femtov0s::Alpha, + femtov0s::QtArm, + femtov0s::DecayVtx, + femtov0s::Pa); using FK0shortExtras = FK0shortExtras_001; +// table for k0short fraction extraction (cpa only -- mass is already in FK0shorts, no need to duplicate it) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortFrac_001, "FK0SHORTFRAC", 1, //! k0short cpa for fraction extraction + femtov0s::CosPa); +using FK0shortFrac = FK0shortFrac_001; + +namespace femtophotons +{ +// columns for bit masks +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for photon (PCM) selections + +// columns for debug information +DECLARE_SOA_COLUMN(CosPa, cosPa, float); //! Cosine of pointing angle of the V0 (conversion) to the primary vertex +DECLARE_SOA_DYNAMIC_COLUMN(Pa, pa, //! pointing angle + [](float cosPa) -> float { + return std::acos(cosPa); + }); +DECLARE_SOA_COLUMN(DcaToPvXY, dcaToPvXY, float); //! DCAxy of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(DcaToPvZ, dcaToPvZ, float); //! DCAz of the photon (V0) to the primary vertex +DECLARE_SOA_COLUMN(Chi2Ndf, chi2Ndf, float); //! Chi2 / NDF of the reconstructed conversion vertex (KF) +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); //! Transverse radius of the conversion point +DECLARE_SOA_COLUMN(PosDauTpcNSigmaEl, posDauTpcNSigmaEl, float); //! TPC electron nSigma of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcNSigmaEl, negDauTpcNSigmaEl, float); //! TPC electron nSigma of the negative daughter +DECLARE_SOA_COLUMN(PosDauPt, posDauPt, float); //! pT of the positive daughter (at the conversion point) +DECLARE_SOA_COLUMN(NegDauPt, negDauPt, float); //! pT of the negative daughter (at the conversion point) +DECLARE_SOA_COLUMN(PosDauTpcInnerParam, posDauTpcInnerParam, float); //! momentum of the positive daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(NegDauTpcInnerParam, negDauTpcInnerParam, float); //! momentum of the negative daughter at the inner wall of the TPC +DECLARE_SOA_COLUMN(PosDauTpcSignal, posDauTpcSignal, float); //! TPC dE/dx of the positive daughter +DECLARE_SOA_COLUMN(NegDauTpcSignal, negDauTpcSignal, float); //! TPC dE/dx of the negative daughter +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of conversion point +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of conversion point +DECLARE_SOA_DYNAMIC_COLUMN(DecayVtx, decayVtx, //! distance of conversion point from nominal interaction point + [](float vtxX, float vtxY, float vtxZ) -> float { + return std::hypot(vtxX, vtxY, vtxZ); + }); +} // namespace femtophotons + +// table for basic PCM photon information (mass is fixed to 0 by construction, not stored) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotons_001, "FPHOTON", 1, //! femto PCM photons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPhotons = FPhotons_001; +using StoredFPhotons = StoredFPhotons_001; + +// table for basic PCM photon information with compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLitePhotons_001, "FLITEPHOTON", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::UnsignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtobase::lite::unsignedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi); +using FLitePhotons = FLitePhotons_001; +using FLitePhoton = FLitePhotons::iterator; +using StoredFLitePhotons = StoredFLitePhotons_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonMasks_001, "FPHOTONMASK", 1, //! photon (PCM) masks + femtophotons::Mask); +using FPhotonMasks = FPhotonMasks_001; +using StoredFPhotonMasks = StoredFPhotonMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPhotonExtras_001, "FPHOTONEXTRA", 1, //! photon (PCM) extra information + femtophotons::CosPa, + femtophotons::DcaToPvXY, + femtophotons::DcaToPvZ, + femtophotons::Chi2Ndf, + femtophotons::V0Radius, + femtophotons::PosDauPt, + femtophotons::NegDauPt, + femtophotons::PosDauTpcInnerParam, + femtophotons::NegDauTpcInnerParam, + femtophotons::PosDauTpcSignal, + femtophotons::NegDauTpcSignal, + femtophotons::PosDauTpcNSigmaEl, + femtophotons::NegDauTpcNSigmaEl, + femtophotons::DecayVtxX, + femtophotons::DecayVtxY, + femtophotons::DecayVtxZ, + femtophotons::DecayVtx, + femtophotons::Pa); +using FPhotonExtras = FPhotonExtras_001; +using StoredFPhotonExtras = StoredFPhotonExtras_001; + +namespace femtotwophotonresonances +{ +// column for the daughter (photon) bit mask -- no momentum-threshold bit: photon daughters have no +// momentum-dependent PID switch the way charged-track daughters do in femtotwotrackresonances. Both +// legs are plain photons with the same selection requirement, so only the AND of both legs' own +// masks is stored (not one column per leg): requiring a set of bits in the AND is equivalent to +// requiring that same set of bits in both legs individually. +DECLARE_SOA_COLUMN(MaskDau, maskDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the photon daughters (AND of both legs' own masks) + +// id columns for the two photon daughters (unordered, no pos/neg distinction) +DECLARE_SOA_INDEX_COLUMN_FULL(Dau1, dau1, int32_t, FPhotons, "_Dau1"); //! index column for first photon daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Dau2, dau2, int32_t, FPhotons, "_Dau2"); //! index column for second photon daughter +} // namespace femtotwophotonresonances + +// table for pi0s (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0s_001, "FPI0", 1, //! femto pi0s + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FPi0s = FPi0s_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FPi0Masks_001, "FPI0MASK", 1, //! mask for pi0s + femtotwophotonresonances::MaskDau); +using FPi0Masks = FPi0Masks_001; + +// table for etas (-> gamma gamma) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtas_001, "FETA", 1, //! femto etas + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotwophotonresonances::Dau1Id, + femtotwophotonresonances::Dau2Id, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FEtas = FEtas_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FEtaMasks_001, "FETAMASK", 1, //! mask for etas + femtotwophotonresonances::MaskDau); +using FEtaMasks = FEtaMasks_001; + +namespace femtotrackstwophotonresonances +{ +// columns for daughter bit masks, copied onto the candidate at build time -- same pattern as +// femtotwotrackresonances: systematic reselection happens on the candidate itself, not by rejoining +// the daughter tables. The two charged daughters keep their own momentum-threshold PID switch; the +// neutral (pi0/eta) daughter has no such switch and no dedicated mask of its own -- it is a direct +// copy of its FPi0Masks/FEtaMasks maskDau (itself already the AND of its two photon legs). +DECLARE_SOA_COLUMN(MaskPosDau, maskPosDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for positive charged-track daughter +DECLARE_SOA_COLUMN(PosDauHasHighMomentum, posDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNegDau, maskNegDau, o2::analysis::femto::datatypes::TrackMaskType); //! Bitmask for negative charged-track daughter +DECLARE_SOA_COLUMN(NegDauHasHighMomentum, negDauHasHighMomentum, bool); //! switch for pid threshold +DECLARE_SOA_COLUMN(MaskNeutralDau, maskNeutralDau, o2::analysis::femto::datatypes::PhotonMaskType); //! Bitmask for the two-photon-resonance daughter (copy of its own maskDau) + +// id columns for the two charged-track daughters and the two-photon-resonance daughter +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! index column for the positive charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! index column for the negative charged-track daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NeutralDau, neutralDau, int32_t, FPi0s, "_NeutralDau"); //! index column for the two-photon-resonance daughter +} // namespace femtotrackstwophotonresonances + +// table for resonances built from two tracks and a two-photon resonance (e.g. omega(782) -> pi+ pi- pi0) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesons_001, "FOMEMES", 1, //! femto omega mesons + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::Pt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtotrackstwophotonresonances::PosDauId, + femtotrackstwophotonresonances::NegDauId, + femtotrackstwophotonresonances::NeutralDauId, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FOmegaMesons = FOmegaMesons_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMesonMasks_001, "FOMEMESMASK", 1, //! mask for omega mesons + femtotrackstwophotonresonances::MaskPosDau, + femtotrackstwophotonresonances::PosDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNegDau, + femtotrackstwophotonresonances::NegDauHasHighMomentum, + femtotrackstwophotonresonances::MaskNeutralDau); +using FOmegaMesonMasks = FOmegaMesonMasks_001; + namespace femtokinks { // columns for bit masks -DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::KinkMaskType); //! Bitmask for kink selections +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::KinkMaskType); //! Bitmask for kink selections // columns for debug information DECLARE_SOA_COLUMN(KinkAngle, kinkAngle, float); //! Kink angle between mother and charged daughter at decay vertex @@ -525,16 +1090,55 @@ DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); //! x coordinate of decay v DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! y coordinate of decay vertex (relative to PV) DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! z coordinate of decay vertex (relative to PV) DECLARE_SOA_COLUMN(TransRadius, transRadius, float); //! Transverse decay radius from PV +DECLARE_SOA_COLUMN(DcaKinkTopo, dcaKinkTopo, float); +DECLARE_SOA_COLUMN(DauTpcNSigmaPr, dauTpcNSigmaPr, float); +DECLARE_SOA_COLUMN(DauTpcNSigmaPi, dauTpcNSigmaPi, float); +DECLARE_SOA_COLUMN(DauTofNSigmaPr, dauTofNSigmaPr, float); +DECLARE_SOA_COLUMN(PtOriginal, ptOriginal, float); + +// columns for sigma plus reconstructed with a PCM photon +DECLARE_SOA_COLUMN(MlScore, mlScore, float); //! BDT score of the candidate +DECLARE_SOA_COLUMN(FlightDirTilt, flightDirTilt, float); //! Tilt of the flight direction needed for a real solution of the missing photon +DECLARE_SOA_COLUMN(DauDcaXY, dauDcaXY, float); //! DCAxy of the charged daughter to the primary vertex +DECLARE_SOA_COLUMN(DauDcaZ, dauDcaZ, float); //! DCAz of the charged daughter to the primary vertex +DECLARE_SOA_COLUMN(PhotonDcaXYToPV, photonDcaXYToPV, float); //! Transverse DCA of the photon flight line to the primary vertex +DECLARE_SOA_COLUMN(PhotonDcaZToPV, photonDcaZToPV, float); //! Longitudinal DCA of the photon flight line to the primary vertex +DECLARE_SOA_COLUMN(PhotonCosPAToPV, photonCosPAToPV, float); //! Cosine of the photon pointing angle to the primary vertex +DECLARE_SOA_COLUMN(PhotonConvRadius, photonConvRadius, float); //! Conversion radius of the photon +DECLARE_SOA_COLUMN(AntiSigmaPointingAngle, antiSigmaPointingAngle, float); //! Angle between the unbent proton momentum and the flight direction +DECLARE_SOA_COLUMN(DcaDauPhoton, dcaDauPhoton, float); //! DCA between charged daughter and photon at the decay vertex // id column for charged daughter track DECLARE_SOA_INDEX_COLUMN_FULL(ChaDau, chaDau, int32_t, FTracks, "_ChaDau"); //! + +namespace lite +{ +// Sigma (using a shared window covering both Sigma- 1.19745 and Sigma+ 1.18937) with roughly +-100 MeV +constexpr float SigmaMassMin = 1.1f; +constexpr float SigmaMassMax = 1.3f; +constexpr float SigmaMassStep = (SigmaMassMax - SigmaMassMin) / 65536.f; + +inline uint16_t binSigmaMass(float mass) { return o2::analysis::femto::utils::binLinear(mass, SigmaMassMin, SigmaMassMax, SigmaMassStep); } +inline float unBinSigmaMass(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, SigmaMassMin, SigmaMassStep); } + +DECLARE_SOA_COLUMN(BinnedSigmaMass, binnedSigmaMass, uint16_t); + +DECLARE_SOA_DYNAMIC_COLUMN(SigmaMass, sigmaMass, + [](uint16_t binnedSigmaMass) -> float { + return unBinSigmaMass(binnedSigmaMass); + }); + +// index for lite track table +DECLARE_SOA_INDEX_COLUMN_FULL(ChaDau, chaDau, int32_t, FLiteTracks, "_ChaDau"); //! charged daughter index, into FLiteTracks +} // namespace lite + } // namespace femtokinks // table for basic sigma information DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmas_001, "FSIGMA", 1, o2::soa::Index<>, - femtobase::stored::FColId, // use sign to differentiate between sigma minus (-1) and anti sigma minus (+1) - femtobase::stored::SignedPt, + femtobase::stored::FColId, + femtobase::stored::SignedPt, // use sign to differentiate between sigma minus (-1) and anti sigma minus (+1) femtobase::stored::Eta, femtobase::stored::Phi, femtobase::stored::Mass, @@ -550,8 +1154,8 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmas_001, "FSIGMA", 1, // table for basic sigma information DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmas_002, "FSIGMA", 2, o2::soa::Index<>, - femtobase::stored::FColId, // use sign to differentiate between sigma minus (-1) and anti sigma minus (+1) - femtobase::stored::SignedPt, + femtobase::stored::FColId, + femtobase::stored::SignedPt, // use sign to differentiate between sigma minus (-1) and anti sigma minus (+1) femtobase::stored::Eta, femtobase::stored::Phi, femtobase::stored::Mass, @@ -564,10 +1168,33 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmas_002, "FSIGMA", 2, femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FSigmas = FSigmas_002; +using FSigma = FSigmas::iterator; +using StoredFSigmas = StoredFSigmas_002; + +// table for basic sigma information, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteSigmas_001, "FLITESIGMA", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtokinks::lite::BinnedSigmaMass, + femtokinks::lite::ChaDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtokinks::lite::SigmaMass); +using FLiteSigmas = FLiteSigmas_001; +using FLiteSigma = FLiteSigmas::iterator; +using StoredFLiteSigmas = StoredFLiteSigmas_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaMasks_001, "FSIGMAMASKS", 1, femtokinks::Mask); using FSigmaMasks = FSigmaMasks_001; +using FSigmamask = FSigmaMasks::iterator; +using StoredFSigmaMasks = StoredFSigmaMasks_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaExtras_001, "FSIGMAEXTRAS", 1, femtokinks::KinkAngle, @@ -583,8 +1210,8 @@ using FSigmaExtras = FSigmaExtras_001; // table for basic sigma plus information DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlus_001, "FSIGMAPLUS", 1, o2::soa::Index<>, - femtobase::stored::FColId, // use sign to differentiate between sigma minus (-1) and anti sigma minus (+1) - femtobase::stored::SignedPt, + femtobase::stored::FColId, + femtobase::stored::SignedPt, // use sign to differentiate between sigma minus (-1) and anti sigma minus (+1) femtobase::stored::Eta, femtobase::stored::Phi, femtobase::stored::Mass, @@ -597,10 +1224,32 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlus_001, "FSIGMAPLUS", 1, femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FSigmaPlus = FSigmaPlus_001; +using FSigmaPlusRow = FSigmaPlus::iterator; +using StoredFSigmaPlus = StoredFSigmaPlus_001; + +// table for basic sigma plus information, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteSigmaPlus_001, "FLITESIGMAPLUS", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtokinks::lite::BinnedSigmaMass, + femtokinks::lite::ChaDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtokinks::lite::SigmaMass); +using FLiteSigmaPlus = FLiteSigmaPlus_001; +using FLiteSigmaPlusRow = FLiteSigmaPlus::iterator; +using StoredFLiteSigmaPlus = StoredFLiteSigmaPlus_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusMasks_001, "FSIGMAPLUSMASKS", 1, femtokinks::Mask); using FSigmaPlusMasks = FSigmaPlusMasks_001; +using StoredFSigmaPlusMasks = StoredFSigmaPlusMasks_001; DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_001, "FSIGMAPLUSEXTRAS", 1, femtokinks::KinkAngle, @@ -611,28 +1260,155 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_001, "FSIGMAPLUSEXTRAS", 1, femtokinks::DecayVtxZ, femtokinks::TransRadius); -using FSigmaPlusExtras = FSigmaPlusExtras_001; +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusExtras_002, "FSIGMAPLUSEXTRAS", 2, + femtokinks::KinkAngle, + femtokinks::DcaDaugToPV, + femtokinks::DcaMothToPV, + femtokinks::DecayVtxX, + femtokinks::DecayVtxY, + femtokinks::DecayVtxZ, + femtokinks::TransRadius, + femtokinks::DcaKinkTopo, + femtokinks::DauTpcNSigmaPr, + femtokinks::DauTpcNSigmaPi, + femtokinks::DauTofNSigmaPr, + femtokinks::PtOriginal, + femtotracks::ItsClusterSizes); + +using FSigmaPlusExtras = FSigmaPlusExtras_002; +using StoredFSigmaPlusExtras = StoredFSigmaPlusExtras_002; + +// table for basic sigma plus information, sigma plus reconstructed with a PCM photon +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcm_001, "FSIGMAPLUSPCM", 1, + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::SignedPt, // use sign to differentiate between sigma plus (+1) and anti sigma plus (-1) + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, + femtokinks::ChaDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FSigmaPlusPcm = FSigmaPlusPcm_001; +using FSigmaPlusPcmRow = FSigmaPlusPcm::iterator; +using StoredFSigmaPlusPcm = StoredFSigmaPlusPcm_001; + +// table for basic sigma plus information, sigma plus reconstructed with a PCM photon, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteSigmaPlusPcm_001, "FLITESIGMAPCM", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtokinks::lite::BinnedSigmaMass, + femtokinks::lite::ChaDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtokinks::lite::SigmaMass); +using FLiteSigmaPlusPcm = FLiteSigmaPlusPcm_001; +using FLiteSigmaPlusPcmRow = FLiteSigmaPlusPcm::iterator; +using StoredFLiteSigmaPlusPcm = StoredFLiteSigmaPlusPcm_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmMasks_001, "FSIGMAPCMMASKS", 1, + femtokinks::Mask); +using FSigmaPlusPcmMasks = FSigmaPlusPcmMasks_001; +using StoredFSigmaPlusPcmMasks = StoredFSigmaPlusPcmMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmMlScores_001, "FSIGMAPCMML", 1, + femtokinks::MlScore); +using FSigmaPlusPcmMlScores = FSigmaPlusPcmMlScores_001; +using StoredFSigmaPlusPcmMlScores = StoredFSigmaPlusPcmMlScores_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmExtras_001, "FSIGMAPCMEXTRA", 1, + femtokinks::FlightDirTilt, + femtokinks::DcaMothToPV, + femtokinks::DauDcaXY, + femtokinks::DauDcaZ, + femtokinks::DauTpcNSigmaPr, + femtokinks::DauTofNSigmaPr, + femtokinks::PhotonDcaXYToPV, + femtokinks::PhotonDcaZToPV, + femtokinks::PhotonCosPAToPV, + femtokinks::PhotonConvRadius, + femtokinks::AntiSigmaPointingAngle, + femtokinks::DcaDauPhoton, + femtokinks::TransRadius); +using FSigmaPlusPcmExtras = FSigmaPlusPcmExtras_001; +using StoredFSigmaPlusPcmExtras = StoredFSigmaPlusPcmExtras_001; namespace femtocascades { // columns for cascade bit masks -DECLARE_SOA_COLUMN(Mask, mask, femtodatatypes::CascadeMaskType); //! Bitmask for cascade selections +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::CascadeMaskType); //! Bitmask for cascade selections + +namespace legacy001 +{ +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::CascadeMaskType001); //! Bitmask for cascade selections +} // columns for cascad debug information -DECLARE_SOA_COLUMN(MassXi, massXi, float); //! Mass of xi -DECLARE_SOA_COLUMN(MassOmega, massOmega, float); //! Mass of omega -DECLARE_SOA_COLUMN(CascadeCosPa, cascadeCosPa, float); //! cosine of the poiting angle at decay vertex +DECLARE_SOA_COLUMN(MassXi, massXi, float); //! Mass of xi +DECLARE_SOA_COLUMN(MassOmega, massOmega, float); //! Mass of omega +DECLARE_SOA_COLUMN(CascadeCosPa, cascadeCosPa, float); //! cosine of the poiting angle at decay vertex +DECLARE_SOA_DYNAMIC_COLUMN(CascadePa, cascadePa, //! pointing angle + [](float cosPa) -> float { + return std::acos(cosPa); + }); DECLARE_SOA_COLUMN(CascadeDauDca, cascadeDauDca, float); //! Lambda daughter DCA at decay vertex DECLARE_SOA_COLUMN(CascadeTransRadius, cascadeTransRadius, float); //! Lambda transvers radius +DECLARE_SOA_COLUMN(LambdaMass, lambdaMass, float); //! Lambda daughter mass DECLARE_SOA_COLUMN(LambdaCosPa, lambdaCosPa, float); //! cosine of the poiting angle at decay vertex DECLARE_SOA_COLUMN(LambdaDauDca, lambdaDauDca, float); //! Lambda daughter DCA at decay vertex DECLARE_SOA_COLUMN(LambdaTransRadius, lambdaTransRadius, float); //! Lambda transvers radius DECLARE_SOA_COLUMN(LambdaDcaToPv, lambdaDcaToPv, float); //! Lambda transvers radius +DECLARE_SOA_COLUMN(StrangeTofBachelor, strangeTofBachelor, float); //! Lambda transvers radius // id columns for bachelor // following same style as strangeness tables were we do not store the id of the lambda, but its daughters DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int32_t, FTracks, "_Bachelor"); //! bachelor id +namespace lite +{ + +// Xi-: PDG mass 1.32171 GeV, roughly ±100 MeV window +constexpr float XiMassMin = 1.22f; +constexpr float XiMassMax = 1.42f; +constexpr float XiMassStep = (XiMassMax - XiMassMin) / 65536.f; + +// Omega-: PDG mass 1.67245 GeV, roughly ±100 MeV window +constexpr float OmegaMassMin = 1.57f; +constexpr float OmegaMassMax = 1.77f; +constexpr float OmegaMassStep = (OmegaMassMax - OmegaMassMin) / 65536.f; + +inline uint16_t binXiMass(float mass) { return o2::analysis::femto::utils::binLinear(mass, XiMassMin, XiMassMax, XiMassStep); } +inline float unBinXiMass(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, XiMassMin, XiMassStep); } + +inline uint16_t binOmegaMass(float mass) { return o2::analysis::femto::utils::binLinear(mass, OmegaMassMin, OmegaMassMax, OmegaMassStep); } +inline float unBinOmegaMass(uint16_t binned) { return o2::analysis::femto::utils::unBinLinear(binned, OmegaMassMin, OmegaMassStep); } + +DECLARE_SOA_COLUMN(BinnedXiMass, binnedXiMass, uint16_t); +DECLARE_SOA_COLUMN(BinnedOmegaMass, binnedOmegaMass, uint16_t); + +DECLARE_SOA_DYNAMIC_COLUMN(XiMass, xiMass, + [](uint16_t binnedXiMass) -> float { + return unBinXiMass(binnedXiMass); + }); +DECLARE_SOA_DYNAMIC_COLUMN(OmegaMass, omegaMass, + [](uint16_t binnedOmegaMass) -> float { + return unBinOmegaMass(binnedOmegaMass); + }); + +// index for lite track table +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int32_t, FLiteTracks, "_Bachelor"); //! bachelor index, into FLiteTracks +} // namespace lite } // namespace femtocascades DECLARE_SOA_TABLE_STAGED_VERSIONED(FXis_001, "FXI", 1, //! femto xis @@ -653,22 +1429,60 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FXis_001, "FXI", 1, //! femto xis femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FXis = FXis_001; +using FXi = FXis::iterator; +using StoredFXis = StoredFXis_001; -DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiMasks_001, "FXIMASK", 1, //! xi masks +// table for basic xi information, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteXis_001, "FLITEXI", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtocascades::lite::BinnedXiMass, + femtocascades::lite::BachelorId, + femtov0s::lite::PosDauId, + femtov0s::lite::NegDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtocascades::lite::XiMass); +using FLiteXis = FLiteXis_001; +using FLiteXi = FLiteXis::iterator; +using StoredFLiteXis = StoredFLiteXis_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiMasks_001, "FXIMASK", 1, //! legacy xi masks + femtocascades::legacy001::Mask); + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiMasks_002, "FXIMASK", 2, //! xi masks femtocascades::Mask); -using FXiMasks = FXiMasks_001; +using FXiMasks = FXiMasks_002; +using StoredFXiMasks = StoredFXiMasks_002; DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiExtras_001, "FXIEXTRA", 1, //! xi extra information femtocascades::MassOmega, femtocascades::CascadeCosPa, femtocascades::CascadeDauDca, femtocascades::CascadeTransRadius, + femtocascades::LambdaMass, femtocascades::LambdaCosPa, femtocascades::LambdaDauDca, femtocascades::LambdaTransRadius, - femtocascades::LambdaDcaToPv); + femtocascades::LambdaDcaToPv, + femtocascades::StrangeTofBachelor, + femtov0s::StrangeTofPosDau, + femtov0s::StrangeTofNegDau, + femtocascades::CascadePa); using FXiExtras = FXiExtras_001; +// table for xi fraction extraction (cascade cpa only -- mass is already in FXis, and the daughter +// lambda's own cpa is not needed here) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiFrac_001, "FXIFRAC", 1, //! xi cpa for fraction extraction + femtocascades::CascadeCosPa); +using FXiFrac = FXiFrac_001; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegas_001, "FOMEGA", 1, //! femto omegas o2::soa::Index<>, femtobase::stored::FColId, @@ -687,43 +1501,218 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegas_001, "FOMEGA", 1, //! femto omegas femtobase::dynamic::Pz, femtobase::dynamic::Theta); using FOmegas = FOmegas_001; +using FOmega = FOmegas::iterator; +using StoredFOmegas = StoredFOmegas_001; -DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMasks_001, "FOMEGAMASK", 1, //! omega masks +// table for basic omega information, compressed/binned kinematics +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLiteOmegas_001, "FLITEOMEGA", 1, + o2::soa::Index<>, + femtobase::stored::FLiteColId, + femtobase::lite::SignedBinnedPt, + femtobase::lite::BinnedEta, + femtobase::lite::BinnedPhi, + femtocascades::lite::BinnedOmegaMass, + femtocascades::lite::BachelorId, + femtov0s::lite::PosDauId, + femtov0s::lite::NegDauId, + femtobase::lite::Sign, + femtobase::lite::SignedPt, + femtobase::lite::signedpt::Pt, + femtobase::lite::Eta, + femtobase::lite::Phi, + femtocascades::lite::OmegaMass); +using FLiteOmegas = FLiteOmegas_001; +using FLiteOmega = FLiteOmegas::iterator; +using StoredFLiteOmegas = StoredFLiteOmegas_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMasks_001, "FOMEGAMASK", 1, //! legacy omega masks + femtocascades::legacy001::Mask); + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaMasks_002, "FOMEGAMASK", 2, //! omega masks femtocascades::Mask); -using FOmegaMasks = FOmegaMasks_001; +using FOmegaMasks = FOmegaMasks_002; +using StoredFOmegaMasks = StoredFOmegaMasks_002; DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaExtras_001, "FOMEGAEXTRA", 1, //! omega extra information femtocascades::MassXi, femtocascades::CascadeCosPa, femtocascades::CascadeDauDca, femtocascades::CascadeTransRadius, + femtocascades::LambdaMass, femtocascades::LambdaCosPa, femtocascades::LambdaDauDca, femtocascades::LambdaTransRadius, - femtocascades::LambdaDcaToPv); + femtocascades::LambdaDcaToPv, + femtocascades::StrangeTofBachelor, + femtov0s::StrangeTofPosDau, + femtov0s::StrangeTofNegDau, + femtocascades::CascadePa); using FOmegaExtras = FOmegaExtras_001; -// tables for monte carlo +// table for omega fraction extraction (cascade cpa only -- mass is already in FOmegas, and the daughter +// lambda's own cpa is not needed here) +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaFrac_001, "FOMEGAFRAC", 1, //! omega cpa for fraction extraction + femtocascades::CascadeCosPa); +using FOmegaFrac = FOmegaFrac_001; + +namespace femtocharmhadrons +{ +// bitmask column +DECLARE_SOA_COLUMN(Mask, mask, o2::analysis::femto::datatypes::CharmHadronMaskType); //! selection bitmask + +// daughter links: row indices into the femto track table +DECLARE_SOA_INDEX_COLUMN_FULL(PosDau, posDau, int32_t, FTracks, "_PosDau"); //! + prong (pion in D0) +DECLARE_SOA_INDEX_COLUMN_FULL(NegDau, negDau, int32_t, FTracks, "_NegDau"); //! - prong (kaon in D0) +// daughter links, 3-prong: row indices into the femto track table. +DECLARE_SOA_INDEX_COLUMN_FULL(Prong0Dau, prong0Dau, int32_t, FTracks, "_Prong0Dau"); //! prong 0 (proton in Lc -> p K pi) +DECLARE_SOA_INDEX_COLUMN_FULL(Prong1Dau, prong1Dau, int32_t, FTracks, "_Prong1Dau"); //! prong 1 (kaon, opposite sign to the mother) +DECLARE_SOA_INDEX_COLUMN_FULL(Prong2Dau, prong2Dau, int32_t, FTracks, "_Prong2Dau"); //! prong 2 (pion in Lc -> p K pi) + +// QA/debug columns +DECLARE_SOA_COLUMN(Cpa, cpa, float); +DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); +DECLARE_SOA_COLUMN(DecayLength, decayLength, float); +DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); +DECLARE_SOA_COLUMN(ImpactParameterProduct, impactParameterProduct, float); //! d0*d0 of the two prongs +DECLARE_SOA_COLUMN(CosThetaStar, cosThetaStar, float); +// QA/debug columns, 3-prong only +DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); //! sum of distances of the secondary vertex to its prongs +DECLARE_SOA_COLUMN(ImpactParameterProngSqSum, impactParameterProngSqSum, float); //! sum of squared prong impact parameters, 3-prong analogue of d0*d0 +DECLARE_SOA_COLUMN(ImpactParameter0, impactParameter0, float); //! impact parameter of prong 0 +DECLARE_SOA_COLUMN(ImpactParameter1, impactParameter1, float); //! impact parameter of prong 1 +DECLARE_SOA_COLUMN(ImpactParameter2, impactParameter2, float); //! impact parameter of prong 2 +// ML BDT scores: [0] background, [1] prompt (D0 from c), [2] non-prompt (D0 from b decay) +DECLARE_SOA_COLUMN(MlProbD0Bkg, mlProbD0Bkg, float); //! D0 hypothesis: background score +DECLARE_SOA_COLUMN(MlProbD0Prompt, mlProbD0Prompt, float); //! D0 hypothesis: prompt score +DECLARE_SOA_COLUMN(MlProbD0NonPrompt, mlProbD0NonPrompt, float); //! D0 hypothesis: non-prompt score +DECLARE_SOA_COLUMN(MlProbD0barBkg, mlProbD0barBkg, float); //! D0bar hypothesis: background +DECLARE_SOA_COLUMN(MlProbD0barPrompt, mlProbD0barPrompt, float); //! D0bar hypothesis: prompt +DECLARE_SOA_COLUMN(MlProbD0barNonPrompt, mlProbD0barNonPrompt, float); //! D0bar hypothesis: non-prompt +DECLARE_SOA_COLUMN(IsSelD0, isSelD0, int8_t); //! PWGHF selection flag +DECLARE_SOA_COLUMN(IsSelD0bar, isSelD0bar, int8_t); //! PWGHF selection flag +// ML BDT scores for Lc, both mass hypotheses: [0] background, [1] prompt, [2] non-prompt +DECLARE_SOA_COLUMN(MlProbLcToPKPiBkg, mlProbLcToPKPiBkg, float); //! Lc -> p K pi hypothesis: background score +DECLARE_SOA_COLUMN(MlProbLcToPKPiPrompt, mlProbLcToPKPiPrompt, float); //! Lc -> p K pi hypothesis: prompt score +DECLARE_SOA_COLUMN(MlProbLcToPKPiNonPrompt, mlProbLcToPKPiNonPrompt, float); //! Lc -> p K pi hypothesis: non-prompt score +DECLARE_SOA_COLUMN(MlProbLcToPiKPBkg, mlProbLcToPiKPBkg, float); //! Lc -> pi K p hypothesis: background score +DECLARE_SOA_COLUMN(MlProbLcToPiKPPrompt, mlProbLcToPiKPPrompt, float); //! Lc -> pi K p hypothesis: prompt score +DECLARE_SOA_COLUMN(MlProbLcToPiKPNonPrompt, mlProbLcToPiKPNonPrompt, float); //! Lc -> pi K p hypothesis: non-prompt score +DECLARE_SOA_COLUMN(IsSelLcToPKPi, isSelLcToPKPi, int8_t); //! PWGHF selection flag +DECLARE_SOA_COLUMN(IsSelLcToPiKP, isSelLcToPiKP, int8_t); //! PWGHF selection flag +// mass under the competing hypothesis, following the MassK0short-in-lambda-table pattern +DECLARE_SOA_COLUMN(MassCompetingHypothesis, massCompetingHypothesis, float); //! Lc mass under the hypothesis that was not accepted +} // namespace femtocharmhadrons + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FD0s_001, "FD0", 1, //! femto D0/D0bar (kinematics only) + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::SignedPt, //! sign encodes D0(+)/D0bar(-) + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, //! mass of the accepted hypothesis + femtocharmhadrons::PosDauId, + femtocharmhadrons::NegDauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FD0s = FD0s_001; +using StoredFD0s = StoredFD0s_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FD0Masks_001, "FD0MASK", 1, //! femto D0 selection bitmask + femtocharmhadrons::Mask); +using FD0Masks = FD0Masks_001; +using StoredFD0Masks = StoredFD0Masks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FD0Extras_001, "FD0EXTRA", 1, //! femto D0 QA / debug + femtocharmhadrons::Cpa, + femtocharmhadrons::CpaXY, + femtocharmhadrons::DecayLength, + femtocharmhadrons::DecayLengthXY, + femtocharmhadrons::ImpactParameterProduct, + femtocharmhadrons::CosThetaStar, + femtocharmhadrons::MlProbD0Bkg, + femtocharmhadrons::MlProbD0Prompt, + femtocharmhadrons::MlProbD0NonPrompt, + femtocharmhadrons::MlProbD0barBkg, + femtocharmhadrons::MlProbD0barPrompt, + femtocharmhadrons::MlProbD0barNonPrompt, + femtocharmhadrons::IsSelD0, // raw PWGHF selection flags + femtocharmhadrons::IsSelD0bar); +using FD0Extras = FD0Extras_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLcs_001, "FLC", 1, //! femto Lc/Lcbar (kinematics only) + o2::soa::Index<>, + femtobase::stored::FColId, + femtobase::stored::SignedPt, //! sign encodes Lc(+)/Lcbar(-) + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::stored::Mass, //! mass of the accepted hypothesis + femtocharmhadrons::Prong0DauId, + femtocharmhadrons::Prong1DauId, + femtocharmhadrons::Prong2DauId, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); +using FLcs = FLcs_001; +using StoredFLcs = StoredFLcs_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLcMasks_001, "FLCMASK", 1, //! femto Lc selection bitmask + femtocharmhadrons::Mask); +using FLcMasks = FLcMasks_001; +using StoredFLcMasks = StoredFLcMasks_001; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLcExtras_001, "FLCEXTRA", 1, //! femto Lc QA / debug + femtocharmhadrons::Cpa, + femtocharmhadrons::CpaXY, + femtocharmhadrons::DecayLength, + femtocharmhadrons::DecayLengthXY, + femtocharmhadrons::Chi2PCA, + femtocharmhadrons::ImpactParameterProngSqSum, + femtocharmhadrons::ImpactParameter0, + femtocharmhadrons::ImpactParameter1, + femtocharmhadrons::ImpactParameter2, + femtocharmhadrons::MassCompetingHypothesis, + femtocharmhadrons::MlProbLcToPKPiBkg, + femtocharmhadrons::MlProbLcToPKPiPrompt, + femtocharmhadrons::MlProbLcToPKPiNonPrompt, + femtocharmhadrons::MlProbLcToPiKPBkg, + femtocharmhadrons::MlProbLcToPiKPPrompt, + femtocharmhadrons::MlProbLcToPiKPNonPrompt, + femtocharmhadrons::IsSelLcToPKPi, // raw PWGHF selection flags + femtocharmhadrons::IsSelLcToPiKP); +using FLcExtras = FLcExtras_001; +// tables for monte carlo namespace femtomccollisions { -DECLARE_SOA_COLUMN(Mult, mult, int); //! Multiplicity of the event as given by the generator in |eta|<0.8 -DECLARE_SOA_COLUMN(Cent, cent, float); //! Multiplicity of the event as given by the generator in |eta|<0.8 - // +// DECLARE_SOA_COLUMN(Mult, mult, int); //! Multiplicity of the event as given by the generator in |eta|<0.8 +// DECLARE_SOA_COLUMN(Cent, cent, float); +// } // namespace femtomccollisions DECLARE_SOA_TABLE_STAGED_VERSIONED(FMcCols_001, "FMCCOL", 1, //! femto mc collisions o2::soa::Index<>, - femtomccollisions::Mult, - femtomccollisions::Cent); + femtocollisions::PosZ, + femtocollisions::Mult, + femtocollisions::Cent); using FMcCols = FMcCols_001; using FMcCol = FMcCols_001::iterator; +using StoredFMcCols = StoredFMcCols_001; +using StoredFMcCol = StoredFMcCols_001::iterator; namespace femtomcparticle { -DECLARE_SOA_COLUMN(Origin, origin, femtodatatypes::McOriginType); //! Multiplicity of the event as given by the generator in |eta|<0.8 -DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); //! Multiplicity of the event as given by the generator in |eta|<0.8 -DECLARE_SOA_INDEX_COLUMN(FMcCol, fMcCol); //! +DECLARE_SOA_COLUMN(Origin, origin, o2::analysis::femto::datatypes::McOriginType); //! Multiplicity of the event as given by the generator in |eta|<0.8 +DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); //! Multiplicity of the event as given by the generator in |eta|<0.8 +DECLARE_SOA_INDEX_COLUMN(FMcCol, fMcCol); //! } // namespace femtomcparticle // table for basic track information @@ -744,18 +1733,36 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FMcParticles_001, "FMCPARTICLE", 1, //! femto femtobase::dynamic::Theta); using FMcParticles = FMcParticles_001; using FMcParticle = FMcParticles::iterator; +using StoredFMcParticles = StoredFMcParticles_001; +using StoredFMcParticle = StoredFMcParticles::iterator; DECLARE_SOA_TABLE_STAGED_VERSIONED(FMcMothers_001, "FMCMOTHER", 1, //! first direct mother of the monte carlo particle - o2::soa::Index<>, - femtomcparticle::PdgCode); + o2::soa::Index<>, // no collision index needed since the mother is retrieved from the daughter mc particle + femtomcparticle::Origin, + femtomcparticle::PdgCode, + femtobase::stored::SignedPt, + femtobase::stored::Eta, + femtobase::stored::Phi, + femtobase::dynamic::Sign, + femtobase::dynamic::Pt, + femtobase::dynamic::P, + femtobase::dynamic::Px, + femtobase::dynamic::Py, + femtobase::dynamic::Pz, + femtobase::dynamic::Theta); + using FMcMothers = FMcMothers_001; using FMcMother = FMcMothers::iterator; +using StoredFMcMothers = StoredFMcMothers_001; +using StoredFMcMother = StoredFMcMothers::iterator; DECLARE_SOA_TABLE_STAGED_VERSIONED(FMcPartMoths_001, "FMCPARTMOTH", 1, //! first partonic mother of the monte carlo particle after hadronization o2::soa::Index<>, femtomcparticle::PdgCode); using FMcPartMoths = FMcPartMoths_001; using FMcPartMoth = FMcPartMoths::iterator; +using StoredFMcPartMoths = StoredFMcPartMoths_001; +using StoredFMcPartMoth = StoredFMcPartMoths::iterator; namespace femtolabels { @@ -766,43 +1773,94 @@ DECLARE_SOA_INDEX_COLUMN(FMcMother, fMcMother); //! DECLARE_SOA_INDEX_COLUMN(FMcPartMoth, fMcPartMoth); //! } // namespace femtolabels -DECLARE_SOA_TABLE(FColLabels, "AOD", "FCOLMCLABEL", - femtolabels::FMcColId); - -DECLARE_SOA_TABLE(FTrackLabels, "AOD", "FTRACKLABEL", - femtolabels::FMcParticleId, - femtolabels::FMcMotherId, - femtolabels::FMcPartMothId); - -DECLARE_SOA_TABLE(FLambdaLabels, "AOD", "FLAMBDALABEL", - femtolabels::FMcParticleId, - femtolabels::FMcMotherId, - femtolabels::FMcPartMothId); - -DECLARE_SOA_TABLE(FK0shortLabels, "AOD", "FK0SHORTLABEL", - femtolabels::FMcParticleId, - femtolabels::FMcMotherId, - femtolabels::FMcPartMothId); - -DECLARE_SOA_TABLE(FSigmaLabels, "AOD", "FSIGMALABEL", - femtolabels::FMcParticleId, - femtolabels::FMcMotherId, - femtolabels::FMcPartMothId); - -DECLARE_SOA_TABLE(FSigmaPlusLabels, "AOD", "FSIGMAPLUSLABEL", - femtolabels::FMcParticleId, - femtolabels::FMcMotherId, - femtolabels::FMcPartMothId); - -DECLARE_SOA_TABLE(FXiLabels, "AOD", "FXILABEL", - femtolabels::FMcParticleId, - femtolabels::FMcMotherId, - femtolabels::FMcPartMothId); - -DECLARE_SOA_TABLE(FOmegaLabels, "AOD", "FOMEGALABEL", - femtolabels::FMcParticleId, - femtolabels::FMcMotherId, - femtolabels::FMcPartMothId); +// Labels are staged like the tables they point into: the derived-to-derived +// producer writes the Stored* variant, so MC information survives a second +// derived-data pass. +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColLabels_001, "FCOLMCLABEL", 1, //! label from femto collision to femto mc collision + femtolabels::FMcColId); +using FColLabels = FColLabels_001; +using FColLabel = FColLabels::iterator; +using StoredFColLabels = StoredFColLabels_001; +using StoredFColLabel = StoredFColLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FTrackLabels_001, "FTRACKLABEL", 1, //! label from femto track to femto mc particle + femtolabels::FMcParticleId); +using FTrackLabels = FTrackLabels_001; +using FTrackLabel = FTrackLabels::iterator; +using StoredFTrackLabels = StoredFTrackLabels_001; +using StoredFTrackLabel = StoredFTrackLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLambdaLabels_001, "FLAMBDALABEL", 1, //! label from femto lambda to femto mc particle + femtolabels::FMcParticleId); +using FLambdaLabels = FLambdaLabels_001; +using FLambdaLabel = FLambdaLabels::iterator; +using StoredFLambdaLabels = StoredFLambdaLabels_001; +using StoredFLambdaLabel = StoredFLambdaLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FK0shortLabels_001, "FK0SHORTLABEL", 1, //! label from femto k0short to femto mc particle + femtolabels::FMcParticleId); +using FK0shortLabels = FK0shortLabels_001; +using FK0shortLabel = FK0shortLabels::iterator; +using StoredFK0shortLabels = StoredFK0shortLabels_001; +using StoredFK0shortLabel = StoredFK0shortLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FD0Labels_001, "FD0LABEL", 1, //! label from femto d0 to femto mc particle + femtolabels::FMcParticleId); +using FD0Labels = FD0Labels_001; +using FD0Label = FD0Labels::iterator; +using StoredFD0Labels = StoredFD0Labels_001; +using StoredFD0Label = StoredFD0Labels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FLcLabels_001, "FLCLABEL", 1, //! label from femto lc to femto mc particle + femtolabels::FMcParticleId); +using FLcLabels = FLcLabels_001; +using FLcLabel = FLcLabels::iterator; +using StoredFLcLabels = StoredFLcLabels_001; +using StoredFLcLabel = StoredFLcLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaLabels_001, "FSIGMALABEL", 1, //! label from femto sigma to femto mc particle + femtolabels::FMcParticleId); +using FSigmaLabels = FSigmaLabels_001; +using FSigmaLabel = FSigmaLabels::iterator; +using StoredFSigmaLabels = StoredFSigmaLabels_001; +using StoredFSigmaLabel = StoredFSigmaLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusLabels_001, "FSIGMAPLUSLABEL", 1, //! label from femto sigma plus to femto mc particle + femtolabels::FMcParticleId); +using FSigmaPlusLabels = FSigmaPlusLabels_001; +using FSigmaPlusLabel = FSigmaPlusLabels::iterator; +using StoredFSigmaPlusLabels = StoredFSigmaPlusLabels_001; +using StoredFSigmaPlusLabel = StoredFSigmaPlusLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FSigmaPlusPcmLabels_001, "FSIGMAPCMLABEL", 1, //! label from femto sigma plus (PCM) to femto mc particle + femtolabels::FMcParticleId); +using FSigmaPlusPcmLabels = FSigmaPlusPcmLabels_001; +using FSigmaPlusPcmLabel = FSigmaPlusPcmLabels::iterator; +using StoredFSigmaPlusPcmLabels = StoredFSigmaPlusPcmLabels_001; +using StoredFSigmaPlusPcmLabel = StoredFSigmaPlusPcmLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FXiLabels_001, "FXILABEL", 1, //! label from femto xi to femto mc particle + femtolabels::FMcParticleId); +using FXiLabels = FXiLabels_001; +using FXiLabel = FXiLabels::iterator; +using StoredFXiLabels = StoredFXiLabels_001; +using StoredFXiLabel = StoredFXiLabels::iterator; + +DECLARE_SOA_TABLE_STAGED_VERSIONED(FOmegaLabels_001, "FOMEGALABEL", 1, //! label from femto omega to femto mc particle + femtolabels::FMcParticleId); +using FOmegaLabels = FOmegaLabels_001; +using FOmegaLabel = FOmegaLabels::iterator; +using StoredFOmegaLabels = StoredFOmegaLabels_001; +using StoredFOmegaLabel = StoredFOmegaLabels::iterator; + +// for mc only processing, we also need Labels pointing from mc particles to mothers and partonic mothers +DECLARE_SOA_TABLE_STAGED_VERSIONED(FMcMotherLabels_001, "FMCMOTHERLABEL", 1, //! labels from femto mc particle to its mother and partonic mother + femtolabels::FMcMotherId, + femtolabels::FMcPartMothId); +using FMcMotherLabels = FMcMotherLabels_001; +using FMcMotherLabel = FMcMotherLabels::iterator; +using StoredFMcMotherLabels = StoredFMcMotherLabels_001; +using StoredFMcMotherLabel = StoredFMcMotherLabels::iterator; } // namespace o2::aod #endif // PWGCF_FEMTO_DATAMODEL_FEMTOTABLES_H_ diff --git a/PWGCF/Femto/FemtoNuclei/CMakeLists.txt b/PWGCF/Femto/FemtoNuclei/CMakeLists.txt index 27462d99baf..03068dcd4aa 100644 --- a/PWGCF/Femto/FemtoNuclei/CMakeLists.txt +++ b/PWGCF/Femto/FemtoNuclei/CMakeLists.txt @@ -10,3 +10,5 @@ # or submit itself to any jurisdiction. add_subdirectory(TableProducer) +add_subdirectory(Tasks) + diff --git a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h index c26513b6e30..98b3e7e14dc 100644 --- a/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h +++ b/PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h @@ -24,6 +24,64 @@ namespace o2::aod { +// Independent mixed-event audit output. IDs are unique only together with the +// two UUID words identifying the task instance. Not an extension of pair rows. +namespace hnmediag +{ +DECLARE_SOA_COLUMN(InstanceHi, instanceHi, uint64_t); +DECLARE_SOA_COLUMN(InstanceLo, instanceLo, uint64_t); +DECLARE_SOA_COLUMN(CallId, callId, uint64_t); +DECLARE_SOA_COLUMN(Timeslice, timeslice, uint64_t); +DECLARE_SOA_COLUMN(TfCounter, tfCounter, uint32_t); +DECLARE_SOA_COLUMN(TfRun, tfRun, uint32_t); +DECLARE_SOA_COLUMN(Kind, kind, uint8_t); +DECLARE_SOA_COLUMN(Status, status, uint8_t); +DECLARE_SOA_COLUMN(PoolEpoch, poolEpoch, uint64_t); +DECLARE_SOA_COLUMN(Run, run, int32_t); +DECLARE_SOA_COLUMN(PreviousRun, previousRun, int32_t); +DECLARE_SOA_COLUMN(EventId, eventId, int64_t); +DECLARE_SOA_COLUMN(PartnerId, partnerId, int64_t); +DECLARE_SOA_COLUMN(InputCollisionId, inputCollisionId, int64_t); +DECLARE_SOA_COLUMN(GlobalBc, globalBc, uint64_t); +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); +DECLARE_SOA_COLUMN(ZVertex, zVertex, float); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(PvMultiplicity, pvMultiplicity, float); +DECLARE_SOA_COLUMN(NumContrib, numContrib, uint32_t); +DECLARE_SOA_COLUMN(NNuclei, nNuclei, uint32_t); +DECLARE_SOA_COLUMN(NHadrons, nHadrons, uint32_t); +DECLARE_SOA_COLUMN(PoolBin, poolBin, int32_t); +DECLARE_SOA_COLUMN(PoolBefore, poolBefore, uint32_t); +DECLARE_SOA_COLUMN(PoolAfter, poolAfter, uint32_t); +DECLARE_SOA_COLUMN(BufferedEvents, bufferedEvents, uint64_t); +DECLARE_SOA_COLUMN(OccupiedBins, occupiedBins, uint32_t); +DECLARE_SOA_COLUMN(ExpectedPairs, expectedPairs, uint64_t); +DECLARE_SOA_COLUMN(AcceptedPairs, acceptedPairs, uint64_t); +DECLARE_SOA_COLUMN(RejectedPairs, rejectedPairs, uint64_t); +DECLARE_SOA_COLUMN(Combinations, combinations, uint64_t); +DECLARE_SOA_COLUMN(InputCollisions, inputCollisions, uint64_t); +DECLARE_SOA_COLUMN(PassedEventSelection, passedEventSelection, uint64_t); +DECLARE_SOA_COLUMN(PassedZorro, passedZorro, uint64_t); +} // namespace hnmediag +DECLARE_SOA_TABLE(HadNucleiMEDiag, "AOD", "HNMEDIAG", + hnmediag::InstanceHi, hnmediag::InstanceLo, hnmediag::CallId, + hnmediag::Timeslice, hnmediag::TfCounter, hnmediag::TfRun, + hnmediag::Kind, hnmediag::Status, hnmediag::PoolEpoch, + hnmediag::Run, hnmediag::PreviousRun, hnmediag::EventId, + hnmediag::InputCollisionId, hnmediag::GlobalBc, hnmediag::Timestamp, + hnmediag::ZVertex, hnmediag::Centrality, hnmediag::PvMultiplicity, + hnmediag::NumContrib, hnmediag::NNuclei, hnmediag::NHadrons, + hnmediag::PoolBin, hnmediag::PoolBefore, hnmediag::PoolAfter, + hnmediag::BufferedEvents, hnmediag::OccupiedBins, + hnmediag::ExpectedPairs, hnmediag::AcceptedPairs, hnmediag::RejectedPairs, + hnmediag::Combinations, hnmediag::InputCollisions, + hnmediag::PassedEventSelection, hnmediag::PassedZorro); +DECLARE_SOA_TABLE(HadNucleiMEDiagLinks, "AOD", "HNMEDIAGLINK", + hnmediag::InstanceHi, hnmediag::InstanceLo, hnmediag::CallId, + hnmediag::PoolEpoch, hnmediag::Run, hnmediag::EventId, + hnmediag::PartnerId, hnmediag::PoolBin, + hnmediag::ExpectedPairs, hnmediag::AcceptedPairs); + namespace hadron_nuclei_tables { @@ -43,22 +101,36 @@ DECLARE_SOA_COLUMN(DcaxyNu, dcaxyNu, float); DECLARE_SOA_COLUMN(DcazNu, dcazNu, float); DECLARE_SOA_COLUMN(DcaxyHad, dcaxyHad, float); DECLARE_SOA_COLUMN(DcazHad, dcazHad, float); +DECLARE_SOA_COLUMN(DcaPair, dcaPair, float); DECLARE_SOA_COLUMN(SignalTPCNu, signalTPCNu, float); DECLARE_SOA_COLUMN(InnerParamTPCNu, innerParamTPCNu, float); DECLARE_SOA_COLUMN(SignalTPCHad, signalTPCHad, float); DECLARE_SOA_COLUMN(InnerParamTPCHad, innerParamTPCHad, float); DECLARE_SOA_COLUMN(NClsTPCNu, nClsTPCNu, uint8_t); +DECLARE_SOA_COLUMN(NClsTPCHad, nClsTPCHad, uint8_t); +DECLARE_SOA_COLUMN(NCrossedRowsTPCNu, nCrossedRowsTPCNu, uint8_t); +DECLARE_SOA_COLUMN(NCrossedRowsTPCHad, nCrossedRowsTPCHad, uint8_t); DECLARE_SOA_COLUMN(NSigmaTPCNu, nSigmaTPCNu, float); DECLARE_SOA_COLUMN(NSigmaTPCHad, nSigmaTPCHad, float); +DECLARE_SOA_COLUMN(NSigmaTOFNu, nSigmaTOFNu, float); +DECLARE_SOA_COLUMN(NSigmaITSNu, nSigmaITSNu, float); +DECLARE_SOA_COLUMN(NSigmaTOFHad, nSigmaTOFHad, float); +DECLARE_SOA_COLUMN(NSigmaITSHad, nSigmaITSHad, float); +DECLARE_SOA_COLUMN(NSigmaTPCHadPi, nSigmaTPCHadPi, float); +DECLARE_SOA_COLUMN(NSigmaTPCHadKa, nSigmaTPCHadKa, float); +DECLARE_SOA_COLUMN(NSigmaTPCHadPr, nSigmaTPCHadPr, float); +DECLARE_SOA_COLUMN(NSigmaTOFHadPi, nSigmaTOFHadPi, float); +DECLARE_SOA_COLUMN(NSigmaTOFHadKa, nSigmaTOFHadKa, float); +DECLARE_SOA_COLUMN(NSigmaTOFHadPr, nSigmaTOFHadPr, float); DECLARE_SOA_COLUMN(Chi2TPCNu, chi2TPCNu, float); DECLARE_SOA_COLUMN(Chi2TPCHad, chi2TPCHad, float); DECLARE_SOA_COLUMN(MassTOFNu, massTOFNu, float); DECLARE_SOA_COLUMN(MassTOFHad, massTOFHad, float); -DECLARE_SOA_COLUMN(HaddTrkNu, pidTrkNu, uint32_t); -DECLARE_SOA_COLUMN(HaddTrkHad, pidTrkHad, uint32_t); -DECLARE_SOA_COLUMN(TrackIDHad, trackIDHad, int); -DECLARE_SOA_COLUMN(TrackIDNu, trackIDNu, int); +DECLARE_SOA_COLUMN(PidTrkNu, pidTrkNu, uint32_t); +DECLARE_SOA_COLUMN(PidTrkHad, pidTrkHad, uint32_t); +DECLARE_SOA_COLUMN(TrackIDHad, trackIDHad, int64_t); +DECLARE_SOA_COLUMN(TrackIDNu, trackIDNu, int64_t); DECLARE_SOA_COLUMN(ItsClusterSizeNu, itsClusterSizeNu, uint32_t); DECLARE_SOA_COLUMN(ItsClusterSizeHad, itsClusterSizeHad, uint32_t); @@ -66,6 +138,43 @@ DECLARE_SOA_COLUMN(ItsClusterSizeHad, itsClusterSizeHad, uint32_t); DECLARE_SOA_COLUMN(SharedClustersNu, sharedClustersNu, uint8_t); DECLARE_SOA_COLUMN(SharedClustersHad, sharedClustersHad, uint8_t); +DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(MagneticField, magneticField, float); +DECLARE_SOA_COLUMN(CollisionIdNu, collisionIdNu, int64_t); +DECLARE_SOA_COLUMN(CollisionIdHad, collisionIdHad, int64_t); +DECLARE_SOA_COLUMN(NClsFindableTPCNu, nClsFindableTPCNu, uint8_t); +DECLARE_SOA_COLUMN(NClsFindableTPCHad, nClsFindableTPCHad, uint8_t); +DECLARE_SOA_COLUMN(FractionSharedTPCNu, fractionSharedTPCNu, float); +DECLARE_SOA_COLUMN(FractionSharedTPCHad, fractionSharedTPCHad, float); +DECLARE_SOA_COLUMN(NClsITSNu, nClsITSNu, uint8_t); +DECLARE_SOA_COLUMN(NClsITSHad, nClsITSHad, uint8_t); +DECLARE_SOA_COLUMN(NClsITSInnerBarrelNu, nClsITSInnerBarrelNu, uint8_t); +DECLARE_SOA_COLUMN(NClsITSInnerBarrelHad, nClsITSInnerBarrelHad, uint8_t); +DECLARE_SOA_COLUMN(Chi2ITSNu, chi2ITSNu, float); +DECLARE_SOA_COLUMN(Chi2ITSHad, chi2ITSHad, float); +DECLARE_SOA_COLUMN(NSigmaTPCNuDe, nSigmaTPCNuDe, float); +DECLARE_SOA_COLUMN(NSigmaTPCNuPr, nSigmaTPCNuPr, float); +DECLARE_SOA_COLUMN(NSigmaTPCNuPi, nSigmaTPCNuPi, float); + +// Reconstructed-MC pair information. The signed generated pT follows the +// convention used by PtNu/PtHad: particles are positive and antiparticles +// are negative. +DECLARE_SOA_COLUMN(PtNuMC, ptNuMC, float); +DECLARE_SOA_COLUMN(EtaNuMC, etaNuMC, float); +DECLARE_SOA_COLUMN(PhiNuMC, phiNuMC, float); +DECLARE_SOA_COLUMN(PtHadMC, ptHadMC, float); +DECLARE_SOA_COLUMN(EtaHadMC, etaHadMC, float); +DECLARE_SOA_COLUMN(PhiHadMC, phiHadMC, float); +DECLARE_SOA_COLUMN(KstarMC, kstarMC, float); +DECLARE_SOA_COLUMN(PdgCodeNuMC, pdgCodeNuMC, int32_t); +DECLARE_SOA_COLUMN(PdgCodeHadMC, pdgCodeHadMC, int32_t); +DECLARE_SOA_COLUMN(IsPhysicalPrimaryNuMC, isPhysicalPrimaryNuMC, bool); +DECLARE_SOA_COLUMN(IsPhysicalPrimaryHadMC, isPhysicalPrimaryHadMC, bool); +DECLARE_SOA_COLUMN(SameMCCollision, sameMCCollision, bool); +DECLARE_SOA_COLUMN(MatchesRecoMCCollision, matchesRecoMCCollision, bool); + DECLARE_SOA_COLUMN(IsBkgUS, isBkgUS, bool); DECLARE_SOA_COLUMN(IsBkgEM, isBkgEM, bool); @@ -74,48 +183,331 @@ DECLARE_SOA_COLUMN(ZVertex, zVertex, float); DECLARE_SOA_COLUMN(Multiplicity, multiplicity, uint16_t); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0C, float); +DECLARE_SOA_COLUMN(MultNTracksPV, multNTracksPV, int32_t); } // namespace hadron_nuclei_tables DECLARE_SOA_TABLE(HadronNucleiTable, "AOD", "HADNUCLEITABLE", - hadron_nuclei_tables::PtHad, hadron_nuclei_tables::PtNu, - hadron_nuclei_tables::InnerParamTPCHad, - hadron_nuclei_tables::InnerParamTPCNu, - hadron_nuclei_tables::TrackIDHad, - hadron_nuclei_tables::TrackIDNu) -DECLARE_SOA_TABLE(HadronHyperTable, "AOD", "HADHYPERTABLE", - hadron_nuclei_tables::PtHyp, - hadron_nuclei_tables::EtaHyp, - hadron_nuclei_tables::PtHe3, - hadron_nuclei_tables::EtaHe3, - hadron_nuclei_tables::PhiHyp, + hadron_nuclei_tables::EtaNu, + hadron_nuclei_tables::PhiNu, hadron_nuclei_tables::PtHad, hadron_nuclei_tables::EtaHad, hadron_nuclei_tables::PhiHad, + hadron_nuclei_tables::DcaxyNu, + hadron_nuclei_tables::DcazNu, hadron_nuclei_tables::DcaxyHad, hadron_nuclei_tables::DcazHad, - hadron_nuclei_tables::SignalTPCHad, + hadron_nuclei_tables::DcaPair, hadron_nuclei_tables::SignalTPCNu, + hadron_nuclei_tables::InnerParamTPCNu, + hadron_nuclei_tables::SignalTPCHad, hadron_nuclei_tables::InnerParamTPCHad, - hadron_nuclei_tables::NSigmaTPCHad, + hadron_nuclei_tables::NClsTPCNu, + hadron_nuclei_tables::NClsTPCHad, + hadron_nuclei_tables::NCrossedRowsTPCNu, + hadron_nuclei_tables::NCrossedRowsTPCHad, hadron_nuclei_tables::NSigmaTPCNu, - hadron_nuclei_tables::Chi2TPCHad, + hadron_nuclei_tables::NSigmaTPCHadPi, + hadron_nuclei_tables::NSigmaTPCHadKa, + hadron_nuclei_tables::NSigmaTPCHadPr, + hadron_nuclei_tables::NSigmaTOFHadPi, + hadron_nuclei_tables::NSigmaTOFHadKa, + hadron_nuclei_tables::NSigmaTOFHadPr, hadron_nuclei_tables::Chi2TPCNu, + hadron_nuclei_tables::Chi2TPCHad, + hadron_nuclei_tables::MassTOFNu, hadron_nuclei_tables::MassTOFHad, - hadron_nuclei_tables::HaddTrkHad, - hadron_nuclei_tables::ItsClusterSizeHad, + hadron_nuclei_tables::PidTrkNu, + hadron_nuclei_tables::PidTrkHad, hadron_nuclei_tables::ItsClusterSizeNu, + hadron_nuclei_tables::ItsClusterSizeHad, + hadron_nuclei_tables::SharedClustersNu, hadron_nuclei_tables::SharedClustersHad, + hadron_nuclei_tables::DeltaEta, + hadron_nuclei_tables::DeltaPhi, + hadron_nuclei_tables::NSigmaTPCHad, + hadron_nuclei_tables::NSigmaTOFNu, + hadron_nuclei_tables::NSigmaITSNu, + hadron_nuclei_tables::NSigmaTOFHad, + hadron_nuclei_tables::NSigmaITSHad, + hadron_nuclei_tables::RunNumber, + hadron_nuclei_tables::MagneticField, + hadron_nuclei_tables::TrackIDNu, hadron_nuclei_tables::TrackIDHad, - hadron_nuclei_tables::IsBkgUS, - hadron_nuclei_tables::IsBkgEM) + hadron_nuclei_tables::CollisionIdNu, + hadron_nuclei_tables::CollisionIdHad, + hadron_nuclei_tables::NClsFindableTPCNu, + hadron_nuclei_tables::NClsFindableTPCHad, + hadron_nuclei_tables::FractionSharedTPCNu, + hadron_nuclei_tables::FractionSharedTPCHad, + hadron_nuclei_tables::NClsITSNu, + hadron_nuclei_tables::NClsITSHad, + hadron_nuclei_tables::NClsITSInnerBarrelNu, + hadron_nuclei_tables::NClsITSInnerBarrelHad, + hadron_nuclei_tables::Chi2ITSNu, + hadron_nuclei_tables::Chi2ITSHad, + hadron_nuclei_tables::NSigmaTPCNuDe, + hadron_nuclei_tables::NSigmaTPCNuPr, + hadron_nuclei_tables::NSigmaTPCNuPi) +DECLARE_SOA_TABLE(HadronNucleiTableMC, "AOD", "HADNUCLEIMC", + hadron_nuclei_tables::PtNuMC, + hadron_nuclei_tables::EtaNuMC, + hadron_nuclei_tables::PhiNuMC, + hadron_nuclei_tables::PtHadMC, + hadron_nuclei_tables::EtaHadMC, + hadron_nuclei_tables::PhiHadMC, + hadron_nuclei_tables::KstarMC, + hadron_nuclei_tables::PdgCodeNuMC, + hadron_nuclei_tables::PdgCodeHadMC, + hadron_nuclei_tables::IsPhysicalPrimaryNuMC, + hadron_nuclei_tables::IsPhysicalPrimaryHadMC, + hadron_nuclei_tables::SameMCCollision, + hadron_nuclei_tables::MatchesRecoMCCollision) DECLARE_SOA_TABLE(HadronNucleiMult, "AOD", "HADNUCLEIMULT", hadron_nuclei_tables::CollisionId, hadron_nuclei_tables::ZVertex, hadron_nuclei_tables::Multiplicity, hadron_nuclei_tables::CentFT0C, - hadron_nuclei_tables::MultiplicityFT0C) + hadron_nuclei_tables::MultiplicityFT0C, + hadron_nuclei_tables::MultNTracksPV) + +// Reduced hadron-hypertriton pair output. Reconstructable quantities such as +// k*, invariant mass and pointing variables are intentionally derived offline. +namespace hadronhyperpair +{ +DECLARE_SOA_COLUMN(IsMixed, isMixed, bool); +DECLARE_SOA_COLUMN(MixingDepth, mixingDepth, int); +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(CentFT0A, centFT0A, float); +DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); +DECLARE_SOA_COLUMN(TrackOccupancy, trackOccupancy, int); +DECLARE_SOA_COLUMN(Ft0cOccupancy, ft0cOccupancy, float); +DECLARE_SOA_COLUMN(NContributors, nContributors, uint16_t); +DECLARE_SOA_COLUMN(MultFT0C, multFT0C, float); +DECLARE_SOA_COLUMN(XPrimVtx, xPrimVtx, float); +DECLARE_SOA_COLUMN(YPrimVtx, yPrimVtx, float); +DECLARE_SOA_COLUMN(ZPrimVtx, zPrimVtx, float); +DECLARE_SOA_COLUMN(HypIsMatter, hypIsMatter, bool); +DECLARE_SOA_COLUMN(HypPtHe3, hypPtHe3, float); +DECLARE_SOA_COLUMN(HypEtaHe3, hypEtaHe3, float); +DECLARE_SOA_COLUMN(HypPhiHe3, hypPhiHe3, float); +DECLARE_SOA_COLUMN(HypPtPi, hypPtPi, float); +DECLARE_SOA_COLUMN(HypEtaPi, hypEtaPi, float); +DECLARE_SOA_COLUMN(HypPhiPi, hypPhiPi, float); +DECLARE_SOA_COLUMN(HypDcaV0Daug, hypDcaV0Daug, float); +DECLARE_SOA_COLUMN(HypDcaHe, hypDcaHe, float); +DECLARE_SOA_COLUMN(HypDcaPi, hypDcaPi, float); +DECLARE_SOA_COLUMN(HypNSigmaHe, hypNSigmaHe, float); +DECLARE_SOA_COLUMN(HypTofMass, hypTofMass, float); +DECLARE_SOA_COLUMN(HypNTPCCrossedRowsHe, hypNTPCCrossedRowsHe, uint8_t); +DECLARE_SOA_COLUMN(HypNTPCCrossedRowsPi, hypNTPCCrossedRowsPi, uint8_t); +DECLARE_SOA_COLUMN(HypTpcMomHe, hypTpcMomHe, float); +DECLARE_SOA_COLUMN(HypTpcMomPi, hypTpcMomPi, float); +DECLARE_SOA_COLUMN(HypTpcSignalHe, hypTpcSignalHe, uint16_t); +DECLARE_SOA_COLUMN(HypTpcSignalPi, hypTpcSignalPi, uint16_t); +DECLARE_SOA_COLUMN(HypTpcChi2He, hypTpcChi2He, float); +DECLARE_SOA_COLUMN(HypItsChi2He, hypItsChi2He, float); +DECLARE_SOA_COLUMN(HypItsChi2Pi, hypItsChi2Pi, float); +DECLARE_SOA_COLUMN(HypItsClusterSizesHe, hypItsClusterSizesHe, uint32_t); +DECLARE_SOA_COLUMN(HypItsClusterSizesPi, hypItsClusterSizesPi, uint32_t); +DECLARE_SOA_COLUMN(HypXDecVtx, hypXDecVtx, float); +DECLARE_SOA_COLUMN(HypYDecVtx, hypYDecVtx, float); +DECLARE_SOA_COLUMN(HypZDecVtx, hypZDecVtx, float); +DECLARE_SOA_COLUMN(HadPt, hadPt, float); +DECLARE_SOA_COLUMN(HadEta, hadEta, float); +DECLARE_SOA_COLUMN(HadPhi, hadPhi, float); +DECLARE_SOA_COLUMN(HadSign, hadSign, int8_t); +DECLARE_SOA_COLUMN(HadDcaXY, hadDcaXY, float); +DECLARE_SOA_COLUMN(HadTpcNClsCrossedRows, hadTpcNClsCrossedRows, uint8_t); +DECLARE_SOA_COLUMN(HadTpcNClsPID, hadTpcNClsPID, uint8_t); +DECLARE_SOA_COLUMN(HadTpcChi2NCl, hadTpcChi2NCl, float); +DECLARE_SOA_COLUMN(HadItsClusterSizes, hadItsClusterSizes, uint32_t); +DECLARE_SOA_COLUMN(HadItsChi2NCl, hadItsChi2NCl, float); +DECLARE_SOA_COLUMN(HadHasTOF, hadHasTOF, bool); +DECLARE_SOA_COLUMN(HadTpcNSigmaPi, hadTpcNSigmaPi, float); +DECLARE_SOA_COLUMN(HadTpcNSigmaKa, hadTpcNSigmaKa, float); +DECLARE_SOA_COLUMN(HadTpcNSigmaPr, hadTpcNSigmaPr, float); +DECLARE_SOA_COLUMN(HadTofNSigmaPi, hadTofNSigmaPi, float); +DECLARE_SOA_COLUMN(HadTofNSigmaKa, hadTofNSigmaKa, float); +DECLARE_SOA_COLUMN(HadTofNSigmaPr, hadTofNSigmaPr, float); +DECLARE_SOA_COLUMN(HypGenPt, hypGenPt, float); +DECLARE_SOA_COLUMN(HypGenEta, hypGenEta, float); +DECLARE_SOA_COLUMN(HypGenPhi, hypGenPhi, float); +DECLARE_SOA_COLUMN(HypGenXDecVtx, hypGenXDecVtx, float); +DECLARE_SOA_COLUMN(HypGenYDecVtx, hypGenYDecVtx, float); +DECLARE_SOA_COLUMN(HypGenZDecVtx, hypGenZDecVtx, float); +DECLARE_SOA_COLUMN(HypIsReco, hypIsReco, bool); +DECLARE_SOA_COLUMN(HypIsSignal, hypIsSignal, bool); +DECLARE_SOA_COLUMN(HypIsRecoMCCollision, hypIsRecoMCCollision, bool); +DECLARE_SOA_COLUMN(HypIsSurvEvSel, hypIsSurvEvSel, bool); +DECLARE_SOA_COLUMN(HypIsTwoBodyDecay, hypIsTwoBodyDecay, bool); +DECLARE_SOA_COLUMN(HypStatusCode, hypStatusCode, int16_t); +DECLARE_SOA_COLUMN(HeGenPt, heGenPt, float); +DECLARE_SOA_COLUMN(HeIsPhysicalPrimary, heIsPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(DecayPiIsPhysicalPrimary, decayPiIsPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(HadRecoPt, hadRecoPt, float); +DECLARE_SOA_COLUMN(HadRecoEta, hadRecoEta, float); +DECLARE_SOA_COLUMN(HadRecoPhi, hadRecoPhi, float); +DECLARE_SOA_COLUMN(HadGenPt, hadGenPt, float); +DECLARE_SOA_COLUMN(HadGenEta, hadGenEta, float); +DECLARE_SOA_COLUMN(HadGenPhi, hadGenPhi, float); +DECLARE_SOA_COLUMN(HadIsPhysicalPrimary, hadIsPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(HadProcess, hadProcess, int16_t); +DECLARE_SOA_COLUMN(SameMCCollision, sameMCCollision, bool); +DECLARE_SOA_COLUMN(MatchesHypRecoMCCollision, matchesHypRecoMCCollision, bool); +DECLARE_SOA_COLUMN(MatchesPairRecoMCCollision, matchesPairRecoMCCollision, bool); +DECLARE_SOA_COLUMN(IsTruthSelfCorrelation, isTruthSelfCorrelation, bool); +DECLARE_SOA_COLUMN(IsTruePrimaryHadHyperPair, isTruePrimaryHadHyperPair, bool); +} // namespace hadronhyperpair + +DECLARE_SOA_TABLE(HadronHyperTable, "AOD", "HADHYPERTABLE", + hadronhyperpair::IsMixed, + hadronhyperpair::MixingDepth, + hadronhyperpair::RunNumber, + hadronhyperpair::PosZ, + hadronhyperpair::CentFT0A, + hadronhyperpair::CentFT0C, + hadronhyperpair::CentFT0M, + hadronhyperpair::TrackOccupancy, + hadronhyperpair::Ft0cOccupancy, + hadronhyperpair::NContributors, + hadronhyperpair::MultFT0C, + hadronhyperpair::XPrimVtx, + hadronhyperpair::YPrimVtx, + hadronhyperpair::ZPrimVtx, + hadronhyperpair::HypIsMatter, + hadronhyperpair::HypPtHe3, + hadronhyperpair::HypEtaHe3, + hadronhyperpair::HypPhiHe3, + hadronhyperpair::HypPtPi, + hadronhyperpair::HypEtaPi, + hadronhyperpair::HypPhiPi, + hadronhyperpair::HypDcaV0Daug, + hadronhyperpair::HypDcaHe, + hadronhyperpair::HypDcaPi, + hadronhyperpair::HypNSigmaHe, + hadronhyperpair::HypTofMass, + hadronhyperpair::HypNTPCCrossedRowsHe, + hadronhyperpair::HypNTPCCrossedRowsPi, + hadronhyperpair::HypTpcMomHe, + hadronhyperpair::HypTpcMomPi, + hadronhyperpair::HypTpcSignalHe, + hadronhyperpair::HypTpcSignalPi, + hadronhyperpair::HypTpcChi2He, + hadronhyperpair::HypItsChi2He, + hadronhyperpair::HypItsChi2Pi, + hadronhyperpair::HypItsClusterSizesHe, + hadronhyperpair::HypItsClusterSizesPi, + hadronhyperpair::HypXDecVtx, + hadronhyperpair::HypYDecVtx, + hadronhyperpair::HypZDecVtx, + hadronhyperpair::HadPt, + hadronhyperpair::HadEta, + hadronhyperpair::HadPhi, + hadronhyperpair::HadSign, + hadronhyperpair::HadDcaXY, + hadronhyperpair::HadTpcNClsCrossedRows, + hadronhyperpair::HadTpcNClsPID, + hadronhyperpair::HadTpcChi2NCl, + hadronhyperpair::HadItsClusterSizes, + hadronhyperpair::HadItsChi2NCl, + hadronhyperpair::HadHasTOF, + hadronhyperpair::HadTpcNSigmaPi, + hadronhyperpair::HadTpcNSigmaKa, + hadronhyperpair::HadTpcNSigmaPr, + hadronhyperpair::HadTofNSigmaPi, + hadronhyperpair::HadTofNSigmaKa, + hadronhyperpair::HadTofNSigmaPr); + +DECLARE_SOA_TABLE(HadronHyperTableMC, "AOD", "HADHYPERMC", + hadronhyperpair::IsMixed, + hadronhyperpair::MixingDepth, + hadronhyperpair::RunNumber, + hadronhyperpair::PosZ, + hadronhyperpair::CentFT0A, + hadronhyperpair::CentFT0C, + hadronhyperpair::CentFT0M, + hadronhyperpair::TrackOccupancy, + hadronhyperpair::Ft0cOccupancy, + hadronhyperpair::NContributors, + hadronhyperpair::MultFT0C, + hadronhyperpair::XPrimVtx, + hadronhyperpair::YPrimVtx, + hadronhyperpair::ZPrimVtx, + hadronhyperpair::HypIsMatter, + hadronhyperpair::HypPtHe3, + hadronhyperpair::HypEtaHe3, + hadronhyperpair::HypPhiHe3, + hadronhyperpair::HypPtPi, + hadronhyperpair::HypEtaPi, + hadronhyperpair::HypPhiPi, + hadronhyperpair::HypDcaV0Daug, + hadronhyperpair::HypDcaHe, + hadronhyperpair::HypDcaPi, + hadronhyperpair::HypNSigmaHe, + hadronhyperpair::HypTofMass, + hadronhyperpair::HypNTPCCrossedRowsHe, + hadronhyperpair::HypNTPCCrossedRowsPi, + hadronhyperpair::HypTpcMomHe, + hadronhyperpair::HypTpcMomPi, + hadronhyperpair::HypTpcSignalHe, + hadronhyperpair::HypTpcSignalPi, + hadronhyperpair::HypTpcChi2He, + hadronhyperpair::HypItsChi2He, + hadronhyperpair::HypItsChi2Pi, + hadronhyperpair::HypItsClusterSizesHe, + hadronhyperpair::HypItsClusterSizesPi, + hadronhyperpair::HypXDecVtx, + hadronhyperpair::HypYDecVtx, + hadronhyperpair::HypZDecVtx, + hadronhyperpair::HadPt, + hadronhyperpair::HadEta, + hadronhyperpair::HadPhi, + hadronhyperpair::HadSign, + hadronhyperpair::HadDcaXY, + hadronhyperpair::HadTpcNClsCrossedRows, + hadronhyperpair::HadTpcNClsPID, + hadronhyperpair::HadTpcChi2NCl, + hadronhyperpair::HadItsClusterSizes, + hadronhyperpair::HadItsChi2NCl, + hadronhyperpair::HadHasTOF, + hadronhyperpair::HadTpcNSigmaPi, + hadronhyperpair::HadTpcNSigmaKa, + hadronhyperpair::HadTpcNSigmaPr, + hadronhyperpair::HadTofNSigmaPi, + hadronhyperpair::HadTofNSigmaKa, + hadronhyperpair::HadTofNSigmaPr, + hadronhyperpair::HypGenPt, + hadronhyperpair::HypGenEta, + hadronhyperpair::HypGenPhi, + hadronhyperpair::HypGenXDecVtx, + hadronhyperpair::HypGenYDecVtx, + hadronhyperpair::HypGenZDecVtx, + hadronhyperpair::HypIsReco, + hadronhyperpair::HypIsSignal, + hadronhyperpair::HypIsRecoMCCollision, + hadronhyperpair::HypIsSurvEvSel, + hadronhyperpair::HypIsTwoBodyDecay, + hadronhyperpair::HypStatusCode, + hadronhyperpair::HeGenPt, + hadronhyperpair::HeIsPhysicalPrimary, + hadronhyperpair::DecayPiIsPhysicalPrimary, + hadronhyperpair::HadRecoPt, + hadronhyperpair::HadRecoEta, + hadronhyperpair::HadRecoPhi, + hadronhyperpair::HadGenPt, + hadronhyperpair::HadGenEta, + hadronhyperpair::HadGenPhi, + hadronhyperpair::HadIsPhysicalPrimary, + hadronhyperpair::HadProcess, + hadronhyperpair::SameMCCollision, + hadronhyperpair::MatchesHypRecoMCCollision, + hadronhyperpair::MatchesPairRecoMCCollision, + hadronhyperpair::IsTruthSelfCorrelation, + hadronhyperpair::IsTruePrimaryHadHyperPair); } // namespace o2::aod diff --git a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx index e424be7f54d..6a77e7bd814 100644 --- a/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx +++ b/PWGCF/Femto/FemtoNuclei/TableProducer/HadNucleiFemto.cxx @@ -10,13 +10,12 @@ // or submit itself to any jurisdiction. // -/// \file HadNucleiFemto.cxx -/// \brief Analysis task for Nuclei-Hadron femto analysis +/// \file HadNucleiFemtoDcaPurity.cxx +/// \brief Nuclei-hadron femtoscopy task with DCA-fraction and purity inputs /// \author CMY /// \date 2025-04-10 #include "PWGCF/Femto/FemtoNuclei/DataModel/HadronNucleiTables.h" -#include "PWGCF/FemtoWorld/Core/FemtoWorldMath.h" #include "PWGLF/DataModel/LFHypernucleiTables.h" #include "PWGLF/Utils/svPoolCreator.h" @@ -44,21 +43,31 @@ #include #include #include +#include #include -#include +#include +#include #include #include #include #include +#include +#include #include #include #include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include @@ -67,6 +76,9 @@ #include #include #include +#include +#include +#include #include using namespace o2; @@ -75,14 +87,46 @@ using namespace o2::framework::expressions; using std::array; using CollBracket = o2::math_utils::Bracket; -using CollisionsFull = soa::Join; -using CollisionsFullMC = soa::Join; -using TrackCandidates = soa::Join; +using HyperCandidates = aod::DataHypCandsWColl; +using HyperCandidatesMC = aod::MCHypCandsWColl; +// Save the PV-track multiplicity alongside numContrib in the event-info output +// for both same-event and mixed-event pairs. +using CollisionsFull = soa::Join; +using CollisionsFullWithPVMult = CollisionsFull; +using CollisionsFullMC = soa::Join; +using HadHyperCollisionsFull = soa::Join; +using HadHyperCollisionsFullMC = soa::Join; +using TrackCandidates = soa::Join; +using TrackCandidatesMC = soa::Join; +// The DCA-template process explicitly subscribes to the propagated covariance +// table. This makes the propagation service enter the branch where TrackTuner +// corrections can be applied when enabled, without adding that dependency to +// unrelated MC process switches. +using TrackCandidatesMCDca = soa::Join; namespace { -constexpr double betheBlochDefault[1][6]{{-136.71, 0.441, 0.2269, 1.347, 0.8035, 0.09}}; -static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; +constexpr std::array BetheBlochDefault{-136.71, 0.441, 0.2269, 1.347, 0.8035, 0.09, + -321.34, 0.6539, 1.591, 0.8225, 2.363, 0.09}; +const std::vector betheBlochParticleNames{"De", "He3"}; +const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; +constexpr std::array tmpRadiiTPC{{85.f, 105.f, 125.f, 145.f, 165.f, 185.f, 205.f, 225.f, 245.f}}; +constexpr int DeuteronPDG = o2::constants::physics::Pdg::kDeuteron; +constexpr int TritonPDG = o2::constants::physics::Pdg::kTriton; +constexpr int He3PDG = o2::constants::physics::Pdg::kHelium3; +constexpr int HyperTritonPDG = o2::constants::physics::Pdg::kHyperTriton; +constexpr int Lithium4PDG = o2::constants::physics::Pdg::kLithium4; +// Store an exact integer PDG code in sparse histograms as +// sign * (high * kMotherPdgChunkBase + low). Splitting the code avoids the +// loss of integer precision that a single float coordinate has near 10^9. +constexpr int MotherPdgChunkBase = 10000; // o2-linter: disable=pdg/explicit-code (encoding base, not a PDG code) +constexpr int MotherPdgHighMax = 120000; // o2-linter: disable=pdg/explicit-code (encoding-axis limit, not a PDG code) +constexpr int HadronDcaFitBins = 2400; // 0.002 cm/bin in [-2.4, 2.4] cm +constexpr float HadronDcaFitAxisMax = 2.4f; +constexpr int NucleusDcaFitBins = 2000; // 0.001 cm/bin in [-1, 1] cm +constexpr float NucleusDcaFitAxisMax = 1.f; +constexpr float He3TPCChi2NClMin = 0.5f; +using PairLorentzVector = ROOT::Math::LorentzVector>; enum Selections { kNoCuts = 0, @@ -91,18 +135,58 @@ enum Selections { kAll }; -float MassHad = 0; +enum DcaOrigin { + Primary = 0, + WeakDecay, + Material, + NDcaOrigins +}; + +enum CollisionAssociation { + CorrectCollision = 0, + WrongCollision, + NCollisionAssociations +}; + +// The base DCA origin stays primary/weak/material. This independent parent +// axis permits optional feed-down splits without changing the base templates. +enum ParentCategory { + NoSpecialParent = 0, + LambdaParent, + SigmaParent, + K0ShortParent, + K0LongParent, + ChargedKaonParent, + HypertritonParent, + Lithium4Parent, + OtherDecayParent, + MissingDecayParent, + NParentCategories +}; + +enum PurityCategory { + AllSelected = 0, + CorrectSpecies, + MisidentifiedSpecies, + NoMCLabel, + CorrectCollisionSpecies, + WrongCollisionSpecies, + CorrectPrimary, + CorrectWeakDecay, + CorrectMaterial, + NPurityCategories +}; } // namespace struct HadNucandidate { - float recoPtNu() const { return signNu * std::hypot(momNu[0], momNu[1]); } - float recoPhiNu() const { return std::atan2(momNu[1], momNu[0]); } - float recoEtaNu() const { return std::asinh(momNu[2] / std::abs(recoPtNu())); } - float recoPtHad() const { return signHad * std::hypot(momHad[0], momHad[1]); } - float recoPhiHad() const { return std::atan2(momHad[1], momHad[0]); } - float recoEtaHad() const { return std::asinh(momHad[2] / std::abs(recoPtHad())); } + [[nodiscard]] float recoPtNu() const { return signNu * std::hypot(momNu[0], momNu[1]); } + [[nodiscard]] float recoPhiNu() const { return std::atan2(momNu[1], momNu[0]); } + [[nodiscard]] float recoEtaNu() const { return std::asinh(momNu[2] / std::abs(recoPtNu())); } + [[nodiscard]] float recoPtHad() const { return signHad * std::hypot(momHad[0], momHad[1]); } + [[nodiscard]] float recoPhiHad() const { return std::atan2(momHad[1], momHad[0]); } + [[nodiscard]] float recoEtaHad() const { return std::asinh(momHad[2] / std::abs(recoPtHad())); } std::array momNu = {99.f, 99.f, 99.f}; std::array momHad = {99.f, 99.f, 99.f}; @@ -116,18 +200,41 @@ struct HadNucandidate { float dcazNu = -10.f; float dcaxyHad = -10.f; float dcazHad = -10.f; + float dcaPair = -10.f; uint16_t tpcSignalNu = 0u; uint16_t tpcSignalHad = 0u; float momNuTPC = -99.f; float momHadTPC = -99.f; uint8_t nTPCClustersNu = 0u; + uint8_t nTPCClustersHad = 0u; + uint8_t nTPCCrossedRowsNu = 0u; + uint8_t nTPCCrossedRowsHad = 0u; + uint8_t nTPCFindableNu = 0u; + uint8_t nTPCFindableHad = 0u; uint8_t sharedClustersNu = 0u; uint8_t sharedClustersHad = 0u; + float fractionSharedTPCNu = 0.f; + float fractionSharedTPCHad = 0.f; float chi2TPCNu = -10.f; float chi2TPCHad = -10.f; + float chi2ITSNu = -10.f; + float chi2ITSHad = -10.f; float nSigmaNu = -10.f; + float nSigmaTPCNuDe = -10.f; + float nSigmaTPCNuPr = -10.f; + float nSigmaTPCNuPi = -10.f; float nSigmaHad = -10.f; + float nSigmaTOFNu = -10.f; + float nSigmaITSNu = -10.f; + float nSigmaTOFHad = -10.f; + float nSigmaITSHad = -10.f; + float nSigmaTPCHadPi = -10.f; + float nSigmaTPCHadKa = -10.f; + float nSigmaTPCHadPr = -10.f; + float nSigmaTOFHadPi = -10.f; + float nSigmaTOFHadKa = -10.f; + float nSigmaTOFHadPr = -10.f; float tpcPrnsigma = -10.f; float tofPrnsigma = -10.f; uint32_t pidTrkNu = 0xFFFFF; // PID in tracking @@ -139,13 +246,19 @@ struct HadNucandidate { uint8_t nClsItsNu = 0u; uint8_t nClsItsHad = 0u; + uint8_t nClsItsInnerBarrelNu = 0u; + uint8_t nClsItsInnerBarrelHad = 0u; bool isBkgUS = false; // unlike sign bool isBkgEM = false; // event mixing - int trackIDNu = -1; - int trackIDHad = -1; + int64_t trackIDNu = -1; + int64_t trackIDHad = -1; + int64_t collisionIDNu = -1; + int64_t collisionIDHad = -1; + float deltaEta = -99.f; + float deltaPhi = -99.f; float kstar = 1.f; float mT = 1.f; @@ -154,93 +267,438 @@ struct HadNucandidate { float cent = 1.f; }; +struct BufferedTrack { + [[nodiscard]] float pt() const { return ptValue; } + [[nodiscard]] float eta() const { return etaValue; } + [[nodiscard]] float phi() const { return phiValue; } + [[nodiscard]] int8_t sign() const { return signValue; } + + std::array momentum{}; + float ptValue{0.f}; + float etaValue{0.f}; + float phiValue{0.f}; + int8_t signValue{0}; + float dcaXY{-10.f}; + float dcaZ{-10.f}; + uint16_t tpcSignal{0u}; + float tpcInnerParam{-99.f}; + uint8_t tpcNClsFound{0u}; + uint8_t tpcNClsCrossedRows{0u}; + uint8_t tpcNClsFindable{0u}; + uint8_t tpcNClsShared{0u}; + float tpcFractionSharedCls{0.f}; + uint8_t itsNCls{0u}; + uint8_t itsNClsInnerBarrel{0u}; + float tpcChi2NCl{-10.f}; + float itsChi2NCl{-10.f}; + float nSigmaTPC{-10.f}; + float nSigmaTPCDe{-10.f}; + float nSigmaTPCPr{-10.f}; + float nSigmaTPCPi{-10.f}; + float nSigmaTOF{-10.f}; + float nSigmaITS{-10.f}; + float nSigmaTPCHadPi{-10.f}; + float nSigmaTPCHadKa{-10.f}; + float nSigmaTPCHadPr{-10.f}; + float nSigmaTOFHadPi{-10.f}; + float nSigmaTOFHadKa{-10.f}; + float nSigmaTOFHadPr{-10.f}; + float massTOF{-10.f}; + uint32_t pidForTracking{0xFFFFFu}; + uint32_t itsClusterSizes{0u}; + int64_t trackId{-1}; + int64_t collisionId{-1}; +}; + +struct BufferedCollision { + int64_t eventId{-1}; + float posZ{0.f}; + uint16_t numContrib{0u}; + float centFT0C{0.f}; + float multFT0C{0.f}; + int32_t multNTracksPV{0}; + std::vector nuclei; + std::vector hadrons; +}; + struct HadNucleiFemto { Produces mOutputDataTable; - Produces mOutputHyperDataTable; + Produces mOutputMCTable; + Produces mOutputHadHyperDataTable; + Produces mOutputHadHyperMCTable; Produces mOutputMultiplicityTable; + Produces mOutputMEDiagnostic; + Produces mOutputMEDiagnosticLinks; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"meDiagnostic"}; + Configurable saveRejectedEvents{"saveRejectedEvents", false, "Save rejected collision rows too (potentially very large); rejection counters are always recorded"}; + Configurable savePartnerLinks{"savePartnerLinks", true, "Save one diagnostic row per undirected mixed-event combination"}; + Configurable productionTag{"productionTag", "unspecified", "Production label recorded with diagnostic instance UUID"}; + Configurable codeTag{"codeTag", "unspecified", "Exact source/build revision label for the diagnostic run"}; + } meDiagnostic; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"species"}; + // Particle species configuration + Configurable settingNuPDGCode{"settingNuPDGCode", static_cast(DeuteronPDG), "Nucleus - PDG code"}; + Configurable settingHadPDGCode{"settingHadPDGCode", 211, "Hadron - PDG code"}; + } species; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"eventMixing"}; + // Event selection and mixing configuration + Configurable settingCutVertex{"settingCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable settingNoMixedEvents{"settingNoMixedEvents", 5, "Number of mixed events per event"}; + Configurable settingUseMultiplicityBinning{"settingUseMultiplicityBinning", false, "Use vertex and multNTracksPV for event mixing instead of vertex and centrality"}; + Configurable settingRequireBothSpeciesForMixing{"settingRequireBothSpeciesForMixing", false, "Use only events containing at least one selected nucleus and one selected hadron for mixed-event pairing"}; + Configurable settingEnableBkgUS{"settingEnableBkgUS", false, "Enable US background"}; + Configurable settingSaveUSandLS{"settingSaveUSandLS", true, "Save All Pairs"}; + } eventMixing; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"trackCut"}; + // Common track-quality cuts + Configurable settingCutEta{"settingCutEta", 0.8f, "Eta cut on daughter track"}; + Configurable settingCutNCls{"settingCutNCls", 5.0f, "Minimum ITS Ncluster for tracks"}; + Configurable settingCutChi2tpcLow{"settingCutChi2tpcLow", 0.5f, "Low cut on TPC chi2"}; + Configurable settingCutChi2tpcHigh{"settingCutChi2tpcHigh", 4.f, "High cut on TPC chi2"}; + Configurable settingCutChi2NClITS{"settingCutChi2NClITS", 36.f, "Maximum ITS Chi2 for tracks"}; + } trackCut; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"hadronPid"}; + // Hadron purity and PID cuts + Configurable settingCutPinMinTOFHad{"settingCutPinMinTOFHad", 0.5f, "Minimum Pin to apply the TOF cut on hadrons"}; + Configurable settingCutNsigmaTPCHad{"settingCutNsigmaTPCHad", 3.0f, "Value of the TPC Nsigma cut on Had"}; + Configurable settingCutNsigmaTOFHad{"settingCutNsigmaTOFHad", 3.0f, "Value of the hsdron TOF Nsigma cut"}; + Configurable settingCutNsigTPCPrMin{"settingCutNsigTPCPrMin", 3.0f, "Minimum TPC Pr Nsigma cut for rejection"}; + Configurable settingCutNsigTPCPiMin{"settingCutNsigTPCPiMin", 3.0f, "Minimum TPC Pi Nsigma cut for rejection"}; + Configurable settingCutNsigTOFPrMin{"settingCutNsigTOFPrMin", 3.0f, "Minimum TOF Pr Nsigma cut for rejection"}; + Configurable settingCutNsigTOFPiMin{"settingCutNsigTOFPiMin", 3.0f, "Minimum TOF Pi Nsigma cut for rejection"}; + Configurable settingHadptMin{"settingHadptMin", 0.14f, "Minimum pT for the reference pion track cuts"}; + Configurable settingHadptMax{"settingHadptMax", 4.0f, "Maximum pT for the reference pion track cuts"}; + Configurable settingProtonPtMin{"settingProtonPtMin", 0.5f, "Minimum pT for proton candidates"}; + Configurable settingProtonPtMax{"settingProtonPtMax", 3.0f, "Maximum pT for proton candidates"}; + Configurable settingPionITSInnerBarrelMin{"settingPionITSInnerBarrelMin", 3, "Minimum ITS inner barrel clusters for the reference pion track cuts"}; + Configurable settingPionITSNClsMin{"settingPionITSNClsMin", 7, "Minimum ITS clusters for the reference pion track cuts"}; + Configurable settingPionTPCNClsFoundMin{"settingPionTPCNClsFoundMin", 80, "Minimum found TPC clusters for the reference pion track cuts"}; + Configurable settingPionTPCCrossedRowsMin{"settingPionTPCCrossedRowsMin", 90, "Minimum crossed TPC rows for the reference pion track cuts"}; + Configurable settingPionDCAxyOffset{"settingPionDCAxyOffset", 0.004f, "DCAxy offset for the reference pion track cuts"}; + Configurable settingPionDCAxyPtCoeff{"settingPionDCAxyPtCoeff", 0.013f, "DCAxy 1/pT coefficient for the reference pion track cuts"}; + Configurable settingPionDCAzOffset{"settingPionDCAzOffset", 0.004f, "DCAz offset for the reference pion track cuts"}; + Configurable settingPionDCAzPtCoeff{"settingPionDCAzPtCoeff", 0.013f, "DCAz 1/pT coefficient for the reference pion track cuts"}; + Configurable settingPionMomCombMin{"settingPionMomCombMin", 0.5f, "Minimum momentum to use combined TPC+TOF PID for reference pions"}; + Configurable settingPionTPCNsigMax{"settingPionTPCNsigMax", 3.0f, "Maximum TPC n-sigma for reference pions below the TOF threshold"}; + Configurable settingPionCombNsigMax{"settingPionCombNsigMax", 3.0f, "Maximum combined TPC+TOF n-sigma for reference pions"}; + } hadronPid; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"deuteronPid"}; + // Deuteron purity and PID cuts + Configurable settingTPCNClsFoundMinDe{"settingTPCNClsFoundMinDe", 110, "Minimum found TPC clusters for deuterons"}; + Configurable settingTPCCrossedRowsMinDe{"settingTPCCrossedRowsMinDe", 100, "Minimum crossed TPC rows for deuterons"}; + Configurable settingDeDCAxyOffset{"settingDeDCAxyOffset", 0.004f, "Deuteron DCAxy cut offset in cm"}; + Configurable settingDeDCAxyPtCoeff{"settingDeDCAxyPtCoeff", 0.013f, "Deuteron DCAxy inverse-pT cut coefficient"}; + Configurable settingDeDCAzOffset{"settingDeDCAzOffset", 0.004f, "Deuteron DCAz cut offset in cm"}; + Configurable settingDeDCAzPtCoeff{"settingDeDCAzPtCoeff", 0.013f, "Deuteron DCAz inverse-pT cut coefficient"}; + Configurable settingCutPinMinDe{"settingCutPinMinDe", 0.0f, "Minimum Pin for De"}; + Configurable settingCutDeptMin{"settingCutDeptMin", 0.6f, "Minimum PT cut on De"}; + Configurable settingCutDeptMax{"settingCutDeptMax", 1.6f, "Maximum PT cut on De"}; + Configurable settingCutPinMinTOFITSDe{"settingCutPinMinTOFITSDe", 1.2f, "Minimum p to apply the TOF ITS cut on De"}; + Configurable settingCutNsigmaTPCDe{"settingCutNsigmaTPCDe", 2.5f, "Value of the TPC Nsigma cut on De"}; + Configurable settingCutNsigmaITSDe{"settingCutNsigmaITSDe", 2.5f, "Value of the ITD Nsigma cut on De"}; + Configurable settingCutNsigmaTOFTPCDe{"settingCutNsigmaTOFTPCDe", 2.5f, "Value of the De TOF TPC combNsigma cut"}; + Configurable settingReqSingleNsig{"settingReqSingleNsig", false, "If true, also require individual TPC and TOF n-sigma cuts in branches using combined TPC+TOF PID"}; + Configurable settingUseProtonMassForKstarMt{"settingUseProtonMassForKstarMt", false, "If true, use proton mass instead of deuteron mass for kstar and mT"}; + } deuteronPid; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"helium3Pid"}; + Configurable settingRigidityMinHe3{"settingRigidityMinHe3", 0.0f, "Minimum He3 TPC rigidity"}; + Configurable settingTPCNClsFoundMinHe3{"settingTPCNClsFoundMinHe3", 110, "Minimum found TPC clusters for He3"}; + Configurable settingTPCCrossedRowsMinHe3{"settingTPCCrossedRowsMinHe3", 70, "Minimum crossed TPC rows for He3"}; + Configurable settingTPCNSigmaMaxHe3{"settingTPCNSigmaMaxHe3", 3.f, "Maximum absolute TPC n-sigma for He3"}; + Configurable settingITSNSigmaMinHe3{"settingITSNSigmaMinHe3", -1.5f, "Minimum ITS n-sigma for He3"}; + } helium3Pid; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"tritonPid"}; + // Triton track-quality, purity and PID cuts + Configurable settingITSNClsMinTr{"settingITSNClsMinTr", 5, "Minimum number of ITS clusters for triton candidates"}; + Configurable settingPIDMomentumSplitTr{"settingPIDMomentumSplitTr", 2.f, "Momentum separating low- and high-momentum triton TPC PID"}; + Configurable settingCutTPCNsigmaLowPTr{"settingCutTPCNsigmaLowPTr", 3.f, "Maximum absolute TPC n-sigma for tritons below the momentum split"}; + Configurable settingCutITSNsigmaLowPTr{"settingCutITSNsigmaLowPTr", 3.f, "Maximum absolute ITS n-sigma for tritons below the momentum split"}; + Configurable settingCutTPCNsigmaHighPMinTr{"settingCutTPCNsigmaHighPMinTr", -2.f, "Minimum TPC n-sigma for tritons above the momentum split"}; + Configurable settingCutTPCNsigmaHighPMaxTr{"settingCutTPCNsigmaHighPMaxTr", 3.f, "Maximum TPC n-sigma for tritons above the momentum split"}; + Configurable settingTOFMassMomentumMinTr{"settingTOFMassMomentumMinTr", 1.2f, "Minimum momentum to apply the triton TOF mass cut"}; + Configurable settingTOFMassMinTr{"settingTOFMassMinTr", 2.5f, "Minimum triton TOF mass"}; + Configurable settingTOFMassMaxTr{"settingTOFMassMaxTr", 3.4f, "Maximum triton TOF mass"}; + Configurable settingTPCRejectNsig{"settingTPCRejectNsig", 3.f, "Minimum absolute TPC n-sigma from deuteron, proton and pion hypotheses"}; + } tritonPid; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"CPR"}; + // Close pair rejection controls + Configurable settingEnableClosePairRejection{"settingEnableClosePairRejection", false, "Enable close pair rejection for nucleus-hadron track pairs"}; + Configurable settingApplyClosePairRejection{"settingApplyClosePairRejection", true, "Reject close pairs; disable to calculate and store CPR variables without rejecting pairs"}; + Configurable settingClosePairDeltaPhiMax{"settingClosePairDeltaPhiMax", 0.01f, "Maximum delta phi star for close pair rejection"}; + Configurable settingClosePairDeltaEtaMax{"settingClosePairDeltaEtaMax", 0.01f, "Maximum delta eta for close pair rejection"}; + Configurable settingClosePairRadiusMode{"settingClosePairRadiusMode", 1, "Close pair rejection mode: 0 = PV, 1 = average phi star, 2 = specific TPC radius"}; + Configurable settingClosePairSpecificRadius{"settingClosePairSpecificRadius", 85.f, "TPC radius in cm used when close pair rejection mode is 2"}; + } CPR; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"mc"}; + Configurable settingRequireSel8{"settingRequireSel8", true, "Apply the same sel8 event selection to reconstructed MC as to data"}; + Configurable settingRequireTruthSpecies{"settingRequireTruthSpecies", true, "Store only truth-matched pion-nucleus pairs"}; + Configurable settingRequireSameMCCollision{"settingRequireSameMCCollision", true, "Require the two truth particles to come from the same MC collision"}; + Configurable settingRequireRecoMCCollisionMatch{"settingRequireRecoMCCollisionMatch", true, "Require both truth particles to match the reconstructed collision MC label"}; + Configurable settingRequirePhysicalPrimaries{"settingRequirePhysicalPrimaries", false, "Store only pairs in which both truth particles are physical primaries"}; + } mc; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"fractionPurity"}; + Configurable settingHadronDcaFitAbsMax{"settingHadronDcaFitAbsMax", 2.4f, "Maximum absolute hadron DCAxy and DCAz stored for the fraction fit"}; + Configurable settingNucleusDcaFitAbsMax{"settingNucleusDcaFitAbsMax", 1.0f, "Maximum absolute nucleus DCAxy and DCAz stored for the fraction fit"}; + Configurable settingRequireRecoMCCollisionMatch{"settingRequireRecoMCCollisionMatch", true, "For the base DCA templates, require the truth particle to belong to the reconstructed collision MC label; detailed templates always store both association classes"}; + } fractionPurity; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"hypertriton"}; + // Hypertriton-specific cuts + Configurable settingHypMassMin{"settingHypMassMin", 2.94f, "Minimum hypertriton invariant mass"}; + Configurable settingHypMassMax{"settingHypMassMax", 3.10f, "Maximum hypertriton invariant mass"}; + } hypertriton; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"hadHyper"}; + Configurable enableMixing{"enableMixing", true, "Build mixed-event pion-hypertriton pairs"}; + Configurable maxOutputKstar{"maxOutputKstar", -1.f, "Maximum pair k* (GeV/c); negative saves all selected pairs"}; + } hadHyper; + + HistogramRegistry hadHyperRegistry{ + "hadHyperRegistry", + {{"hSE", "Raw same-event pairs;k*;Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"hME", "Raw mixed-event pairs;k*;Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"hMass", "Unique selected candidates;mass He3-pi;Entries", {HistType::kTH1D, {{160, 2.94, 3.10}}}}, + {"hSelfPairs", "Rejected daughter reuse;reason (0=He3,1=pion,2=truth);k*", {HistType::kTH2F, {{3, -0.5, 2.5}, {300, 0., 3.}}}}, + {"hSameEventSelfPairs", "Same-event rejected daughter reuse;reason (0=He3,1=pion,2=truth);k*", {HistType::kTH2F, {{3, -0.5, 2.5}, {300, 0., 3.}}}}, + {"hCandidatePairMultiplicitySE", "Accepted same-event hadron pairs per hypertriton candidate;pairs;centrality", {HistType::kTH2F, {{200, -0.5, 199.5}, {10, 0., 100.}}}}, + {"hCandidatePairMultiplicityME", "Accepted mixed-event hadron pairs per hypertriton candidate;pairs;centrality", {HistType::kTH2F, {{200, -0.5, 199.5}, {10, 0., 100.}}}}, + {"hMixEventDeltaPosZVsCent", "Mixed-event #Delta z_{vtx} vs hypertriton centrality;hypertriton CentFT0C;#Delta z_{vtx}", {HistType::kTH2F, {{100, 0., 100.}, {120, -30., 30.}}}}, + {"hMixEventDeltaCentFT0CVsCent", "Mixed-event #Delta CentFT0C vs hypertriton centrality;hypertriton CentFT0C;#Delta CentFT0C", {HistType::kTH2F, {{100, 0., 100.}, {200, -100., 100.}}}}, + {"hMixingDepth", "Available partner events;events;anchor events", {HistType::kTH1F, {{101, -0.5, 100.5}}}}, + {"hPoolFlow", "Mixing pool selection;0=selected,1=in range;events", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, + {"hClosePairBeforePV", "Hadron-hypertriton PV close-pair QA before rejection;#Delta#eta;#Delta#varphi", {HistType::kTH2F, {{200, -0.1, 0.1}, {200, -0.1, 0.1}}}}, + {"hClosePairAfterPV", "Hadron-hypertriton PV close-pair QA after rejection;#Delta#eta;#Delta#varphi", {HistType::kTH2F, {{200, -0.1, 0.1}, {200, -0.1, 0.1}}}}, + {"hDaughterHeTPC", "He3 daughter QA per candidate;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, + {"hDaughterPiTPC", "Pion daughter QA per candidate;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, + {"hHadronTPC", "Pair hadron QA per selected track;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, + {"MC/hKstarRecVsGenHyperReco", "Hadron-hypertriton k* response for reconstructed true hypertritons;generated k* (GeV/#it{c});reconstructed k* (GeV/#it{c})", {HistType::kTH2F, {{300, 0., 3.}, {300, 0., 3.}}}}, + {"MC/hKstarResolutionHyperReco", "Hadron-hypertriton k* resolution for reconstructed true hypertritons;reconstructed k* (GeV/#it{c});k*_{reco}-k*_{gen} (GeV/#it{c})", {HistType::kTH2F, {{300, 0., 3.}, {200, -0.2, 0.2}}}}, + {"MC/hPrimaryHadronVsKstarDen", "Truth-matched selected hadron denominator;k* (GeV/#it{c});Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"MC/hPrimaryHadronVsKstarNum", "Physical-primary selected hadron numerator;k* (GeV/#it{c});Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"MC/hPrimaryHadronVsCentDen", "Truth-matched selected hadron denominator;hypertriton CentFT0C;Entries", {HistType::kTH1D, {{100, 0., 100.}}}}, + {"MC/hPrimaryHadronVsCentNum", "Physical-primary selected hadron numerator;hypertriton CentFT0C;Entries", {HistType::kTH1D, {{100, 0., 100.}}}}}}; + + // Tuple order follows the column blocks in HadronNucleiTables.h. + using HadHyperTrackInfo = std::tuple; + using HadHyperEventInfo = std::tuple; + using HadHyperCandidateInfo = std::tuple; + using HadHyperDataInfo = decltype(std::tuple_cat(std::declval>(), + std::declval(), + std::declval(), + std::declval())); + using HadHyperMCInfo = std::tuple; + + struct HadHyperParticleTruth { + int64_t particleId{-1}; + int64_t collisionId{-1}; + int32_t pdgCode{0}; + float pt{-1.f}; + float eta{-999.f}; + float phi{-999.f}; + bool isPhysicalPrimary{false}; + int16_t statusCode{0}; + int16_t process{0}; + std::array momentum{0.f, 0.f, 0.f}; + }; + + struct HadHyperCandidate { + HadHyperCandidateInfo info{}; + HadHyperParticleTruth hyperTruth{}; + HadHyperParticleTruth heTruth{}; + HadHyperParticleTruth decayPionTruth{}; + int64_t sourceCandidateId{-1}; + int64_t heTrackId{-1}; + int64_t pionTrackId{-1}; + int16_t statusCode{0}; + bool isReco{true}; + bool isSignal{false}; + bool isRecoMCCollision{false}; + bool isSurvEvSel{false}; + bool isTwoBodyDecay{false}; + uint8_t isFakeHeOnITSLayer{0u}; + float genPt{-1.f}; + float genEta{-1.f}; + float genPhi{-1.f}; + float genPtHe3{-1.f}; + std::array genDecVtx{-1.f, -1.f, -1.f}; + std::array momentum{}; + std::array genMomentum{}; + float mass{0.f}; + float etaHe{0.f}; + float phiHe{0.f}; + float etaPi{0.f}; + float phiPi{0.f}; + bool isMatter{false}; + + [[nodiscard]] float pt() const { return std::hypot(momentum[0], momentum[1]); } + [[nodiscard]] float eta() const + { + const float transverseMomentum = pt(); + return transverseMomentum > 0.f ? std::asinh(momentum[2] / transverseMomentum) : 999.f; + } + [[nodiscard]] float phi() const { return std::atan2(momentum[1], momentum[0]); } + [[nodiscard]] int8_t sign() const { return isMatter ? 1 : -1; } + }; + + struct HadHyperHadron { + HadHyperTrackInfo info{}; + HadHyperParticleTruth truth{}; + std::vector motherIds; + int64_t sourceId{-1}; + std::array momentum{}; + float tpcInnerParam{0.f}; + float tpcSignal{0.f}; + float etaValue{0.f}; + float phiValue{0.f}; + int8_t signValue{0}; + + [[nodiscard]] float pt() const { return std::hypot(momentum[0], momentum[1]); } + [[nodiscard]] float eta() const { return etaValue; } + [[nodiscard]] float phi() const { return phiValue; } + [[nodiscard]] int8_t sign() const { return signValue; } + }; + + struct HadHyperEvent { + HadHyperEventInfo info{}; + int64_t mcCollisionId{-1}; + bool hasMCCollision{false}; + float centrality{0.f}; + std::vector candidates; + std::vector hadrons; + }; - // Particle species configuration - Configurable settingHadPDGCode{"settingHadPDGCode", 211, "Hadron - PDG code"}; - // Event selection and mixing configuration - Configurable settingCutVertex{"settingCutVertex", 10.0f, "Accepted z-vertex range"}; - Configurable settingNoMixedEvents{"settingNoMixedEvents", 5, "Number of mixed events per event"}; - Configurable settingEnableBkgUS{"settingEnableBkgUS", false, "Enable US background"}; - Configurable settingSaferME{"settingSaferME", false, "For Safer ME"}; - Configurable settingSaveUSandLS{"settingSaveUSandLS", true, "Save All Pairs"}; - // Common track-quality cuts - Configurable settingCutEta{"settingCutEta", 0.8f, "Eta cut on daughter track"}; - Configurable settingCutNCls{"settingCutNCls", 5.0f, "Minimum ITS Ncluster for tracks"}; - Configurable settingCutChi2tpcLow{"settingCutChi2tpcLow", 0.5f, "Low cut on TPC chi2"}; - Configurable settingCutChi2tpcHigh{"settingCutChi2tpcHigh", 4.f, "High cut on TPC chi2"}; - Configurable settingCutChi2NClITS{"settingCutChi2NClITS", 36.f, "Maximum ITS Chi2 for tracks"}; - // Hadron purity and PID cuts - Configurable settingCutHadptMin{"settingCutHadptMin", 0.14f, "Minimum PT cut on Had"}; - Configurable settingCutHadptMax{"settingCutHadptMax", 4.0f, "Maximum PT cut on Had"}; - Configurable settingCutHadDCAxyMin{"settingCutHadDCAxyMin", 0.3f, "DCAxy Min for Had"}; - Configurable settingCutHadDCAzMin{"settingCutHadDCAzMin", 0.3f, "DCAz Min for Had"}; - Configurable settingCutPinMinTOFHad{"settingCutPinMinTOFHad", 0.5f, "Minimum Pin to apply the TOF cut on hadrons"}; - Configurable settingCutNsigmaTPCHad{"settingCutNsigmaTPCHad", 3.0f, "Value of the TPC Nsigma cut on Had"}; - Configurable settingCutNsigmaTOFHad{"settingCutNsigmaTOFHad", 3.0f, "Value of the hsdron TOF Nsigma cut"}; - Configurable settingCutNsigmaTOFTPCHad{"settingCutNsigmaTOFTPCHad", 3.0f, "Value of the hsdron TOF TPC combNsigma cut"}; - Configurable settingCutNsigTPCPrMin{"settingCutNsigTPCPrMin", 3.0f, "Minimum TPC Pr Nsigma cut for rejection"}; - Configurable settingCutNsigTPCPiMin{"settingCutNsigTPCPiMin", 3.0f, "Minimum TPC Pi Nsigma cut for rejection"}; - Configurable settingCutNsigTOFPrMin{"settingCutNsigTOFPrMin", 3.0f, "Minimum TOF Pr Nsigma cut for rejection"}; - Configurable settingCutNsigTOFPiMin{"settingCutNsigTOFPiMin", 3.0f, "Minimum TOF Pi Nsigma cut for rejection"}; - // Deuteron purity and PID cuts - Configurable settingCutPinMinDe{"settingCutPinMinDe", 0.0f, "Minimum Pin for De"}; - Configurable settingCutClSizeItsDe{"settingCutClSizeItsDe", 4.0f, "Minimum ITS cluster size for De"}; - Configurable settingCutDeptMin{"settingCutDeptMin", 0.6f, "Minimum PT cut on De"}; - Configurable settingCutDeptMax{"settingCutDeptMax", 1.6f, "Maximum PT cut on De"}; - Configurable settingCutDeDCAzMin{"settingCutDeDCAzMin", 0.2f, "DCAxy Min for De"}; - Configurable settingCutPinMinTOFITSDe{"settingCutPinMinTOFITSDe", 1.2f, "Minimum p to apply the TOF ITS cut on De"}; - Configurable settingCutNsigmaTPCDe{"settingCutNsigmaTPCDe", 2.5f, "Value of the TPC Nsigma cut on De"}; - Configurable settingCutNsigmaITSDe{"settingCutNsigmaITSDe", 2.5f, "Value of the ITD Nsigma cut on De"}; - Configurable settingCutNsigmaTOFTPCDe{"settingCutNsigmaTOFTPCDe", 2.5f, "Value of the De TOF TPC combNsigma cut"}; - // Hypertriton-specific cuts - Configurable settingCutTPCChi2He{"settingCutTPCChi2He", 0.0f, "Minimum tpcChi2He for Hyper He3"}; - Configurable settingCutAverClsSizeHe{"settingCutAverClsSizeHe", 0.0f, "Minimum averClusSizeHe for Hyper He3"}; - // Output and QA controls - Configurable settingFillTable{"settingFillTable", false, "Enable table filling"}; - Configurable settingFillMultiplicity{"settingFillMultiplicity", false, "Fill multiplicity table"}; - Configurable settingUseBBcomputeDeNsigma{"settingUseBBcomputeDeNsigma", false, "Use BB params to compute De TPC Nsigma"}; - // Zorro - Configurable settingSkimmedProcessing{"settingSkimmedProcessing", false, "Skimmed dataset processing"}; - // CCDB options - Configurable settingDbz{"settingDbz", -999, "bz field, -999 is automatic"}; - Configurable settingCcdburl{"settingCcdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable settingGrpPath{"settingGrpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable settingGrpmagPath{"settingGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable settingLutPath{"settingLutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable settingGeoPath{"settingGeoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable settingPidPath{"settingPidPath", "", "Path to the PID response object"}; - - Configurable> settingBetheBlochParams{"settingBetheBlochParams", {betheBlochDefault[0], 1, 6, {"De"}, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for De"}; - Configurable settingCompensatePIDinTracking{"settingCompensatePIDinTracking", false, "If true, divide tpcInnerParam by the electric charge"}; - Configurable settingMaterialCorrection{"settingMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Material correction type"}; + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"output"}; + // Output and QA controls + Configurable settingFillTable{"settingFillTable", false, "Enable table filling"}; + Configurable settingFillTableLowKstarOnly{"settingFillTableLowKstarOnly", false, "If true, apply the configured low-kstar threshold to pion-deuteron and pion-triton pairs"}; + Configurable settingFillTablePiDeKstarMax{"settingFillTablePiDeKstarMax", 0.1f, "Maximum kstar for pion-deuteron pairs written to the output table"}; + Configurable settingFillTablePiTrKstarMax{"settingFillTablePiTrKstarMax", 0.1f, "Maximum kstar for pion-triton pairs written to the output table"}; + Configurable settingFillMultiplicity{"settingFillMultiplicity", false, "Fill multiplicity table"}; + Configurable settingUseBBcomputeDeNsigma{"settingUseBBcomputeDeNsigma", false, "Use BB params to compute De TPC Nsigma"}; + } output; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"zorro"}; + // Zorro + Configurable settingSkimmedProcessing{"settingSkimmedProcessing", false, "Skimmed dataset processing"}; + } zorro; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"ccdb"}; + // CCDB options + Configurable settingDbz{"settingDbz", -999, "bz field, -999 is automatic"}; + Configurable settingCcdburl{"settingCcdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable settingGrpPath{"settingGrpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable settingGrpmagPath{"settingGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable settingLutPath{"settingLutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable settingGeoPath{"settingGeoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable settingPidPath{"settingPidPath", "", "Path to the PID response object"}; + } ccdb; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"pidCalibration"}; + // PID calibration and material correction controls + Configurable> settingBetheBlochParams{"settingBetheBlochParams", {BetheBlochDefault.data(), 2, 6, betheBlochParticleNames, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for the selected nucleus"}; + Configurable settingCompensatePIDinTracking{"settingCompensatePIDinTracking", false, "If true, divide tpcInnerParam by the electric charge"}; + Configurable settingMaterialCorrection{"settingMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Material correction type"}; + } pidCalibration; Preslice mPerCol = aod::track::collisionId; - PresliceUnsorted hypPerCol = o2::aod::hyperrec::collisionId; + Preslice mPerColMC = aod::track::collisionId; + PresliceUnsorted hypPerCol = o2::aod::hyperrec::collisionId; + PresliceUnsorted hypPerColMC = o2::aod::hyperrec::collisionId; // binning for EM background ConfigurableAxis axisVertex{"axisVertex", {30, -10, 10}, "Binning for vtxz"}; ConfigurableAxis axisCentrality{"axisCentrality", {40, 0, 100}, "Binning for centrality"}; - using BinningType = ColumnBinningPolicy; - BinningType binningPolicy{{axisVertex, axisCentrality}, true}; - SliceCache cache; - SameKindPair mPair{binningPolicy, settingNoMixedEvents, -1, &cache}; - // Pair hyperPair{binningPolicy, settingNoMixedEvents, -1, &cache}; - - std::array mBBparamsDe; - std::vector mRecoCollisionIDs; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0., 4., 8., 12., 16., 20., 24., 28., 32., 36., 40., 44., 48., 52., 56., 60., 64., 68., 72., 76., 80., 84., 88., 92., 96., 100., 200.}, "Variable-width multNTracksPV bins for event mixing"}; + using CentralityBinningType = ColumnBinningPolicy; + using MultiplicityBinningType = ColumnBinningPolicy; + + std::array mBBparamsNucleus{}; + float mMassHad{0.f}; std::vector mGoodCollisions; std::vector mTrackPairs; - std::vector mTrackHypPairs; + std::unordered_map> mMixingPools; + std::unordered_map> mHyperMixingPools; + std::unordered_map> mHyperMCMixingPools; + int64_t mNextMixedEventId{0}; + int mMixingRunNumber{-1}; + int mHyperMixingRunNumber{-1}; + int mHyperMCMixingRunNumber{-1}; + // A separate state machine: the ordinary ME pools and counters are untouched. + std::unordered_map> mDiagnosticPools; + TimingInfo mDiagnosticTiming; + std::string mDiagnosticUUID; + uint64_t mDiagnosticInstanceHi{0}, mDiagnosticInstanceLo{0}; + uint64_t mDiagnosticCall{0}, mDiagnosticEpoch{0}, mDiagnosticBuffered{0}; + uint64_t mDiagnosticExpected{0}, mDiagnosticAccepted{0}, mDiagnosticCombinations{0}; + int64_t mNextDiagnosticEventId{0}; + int mDiagnosticRun{-1}; + static constexpr uint8_t DiagnosticEventKind = 3; + struct MEDiagnosticRow { + uint8_t kind{DiagnosticEventKind}, status{0}; + int run{-1}, previousRun{-1}; + int64_t eventId{-1}, inputCollisionId{-1}; + uint64_t globalBC{0}, timestamp{0}; + float z{0.f}, centrality{0.f}, multiplicity{0.f}; + uint32_t numContrib{0}, nNuclei{0}, nHadrons{0}; + int poolBin{-1}; + uint32_t before{0}, after{0}; + uint64_t expected{0}, accepted{0}, combinations{0}; + uint64_t inputCollisions{0}, passedEventSelection{0}, passedZorro{0}; + }; + HistogramRegistry mDiagnosticRegistry{"MEDiagnostic", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; o2::vertexing::DCAFitterN<2> mFitter; - int mRunNumber; - float mDbz; - Service mCcdb; + int mRunNumber{0}; + float mDbz{0.f}; + Service mCcdb{}; Zorro mZorro; OutputObj mZorroSummary{"zorroSummary"}; @@ -252,17 +710,21 @@ struct HadNucleiFemto { {"hCentrality", "Centrality", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}}, {"hSkipReasons", "Why storedEvent skipped;Reason;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}}, {"hEvents", "; Events;", {HistType::kTH1F, {{3, -0.5, 2.5}}}}, - {"hEmptyPool", "svPoolCreator did not find track pairs false/true", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, + {"hMixedEventSelections", "Mixed-event collision selection;Step;Counts", {HistType::kTH1F, {{4, -0.5, 3.5}}}}, + {"hMixingPoolOccupancy", "Events already available in the mixing pool;Events;Counts", {HistType::kTH1F, {{101, -0.5, 100.5}}}}, + {"hMixedNucleiPerEvent", "Selected nuclei in mixed-event input;Candidates;Events", {HistType::kTH1F, {{201, -0.5, 200.5}}}}, + {"hMixedHadronsPerEvent", "Selected hadrons in mixed-event input;Candidates;Events", {HistType::kTH1F, {{501, -0.5, 500.5}}}}, // Candidate topology and kinematics - {"hTrackSel", "Accepted tracks", {HistType::kTH1F, {{Selections::kAll, -0.5, static_cast(Selections::kAll) - 0.5}}}}, + {"hTrackSel", "Accepted hadron tracks", {HistType::kTH1F, {{Selections::kAll, -0.5, static_cast(Selections::kAll) - 0.5}}}}, + {"hTrackSelNu", "Accepted nucleus tracks", {HistType::kTH1F, {{Selections::kAll, -0.5, static_cast(Selections::kAll) - 0.5}}}}, + {"hNuPairFlow", "Nucleus pair-building flow;step;counts", {HistType::kTH1F, {{3, -0.5, 2.5}}}}, {"hdcaxyNu", ";DCA_{xy} (cm)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, {"hdcazNu", ";DCA_{z} (cm)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, - {"hdcazNu_min", ";DCA_{z}-min (cm)", {HistType::kTH1F, {{20, -1.0f, 1.0f}}}}, {"hNClsNuITS", ";N_{ITS} Cluster", {HistType::kTH1F, {{20, -10.0f, 10.0f}}}}, - {"hNuPt", "#it{p}_{T} distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{240, -6.0f, 6.0f}}}}, - {"hSingleNuPt", "#it{p}_{T} distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{240, -6.0f, 6.0f}}}}, + {"hNuPt", "#it{p}_{T} distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{280, -7.0f, 7.0f}}}}, + {"hSingleNuPt", "#it{p}_{T} distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{280, -7.0f, 7.0f}}}}, {"hNuPin", "#it{p} distribution; #it{p} (GeV/#it{c})", {HistType::kTH1F, {{240, -6.0f, 6.0f}}}}, {"hSingleNuPin", "#it{p} distribution; #it{p} (GeV/#it{c})", {HistType::kTH1F, {{240, -6.0f, 6.0f}}}}, {"hNuEta", "eta distribution; #eta(Nu)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, @@ -271,51 +733,121 @@ struct HadNucleiFemto { {"hdcaxyHad", ";DCA_{xy} (cm)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, {"hdcazHad", ";DCA_{z} (cm)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, {"hNClsHadITS", ";N_{ITS} Cluster", {HistType::kTH1F, {{20, -10.0f, 10.0f}}}}, - {"hHadPt", "Pt distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{120, -3.0f, 3.0f}}}}, - {"hSingleHadPt", "#it{p}_{T} distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{120, -3.0f, 3.0f}}}}, + {"hHadPt", "Pt distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{280, -7.0f, 7.0f}}}}, + {"hSingleHadPt", "#it{p}_{T} distribution; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{280, -7.0f, 7.0f}}}}, {"hHadPin", "P distribution; #it{p} (GeV/#it{c})", {HistType::kTH1F, {{120, -4.0f, 4.0f}}}}, {"hHadEta", "eta distribution; #eta(had)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, {"hHadPhi", "phi distribution; phi(had)", {HistType::kTH1F, {{600, -4.0f, 4.0f}}}}, + {"h2CPRBefore", "Close pair rejection before cut; #Delta#eta; #Delta#phi^{*}", {HistType::kTH2F, {{300, -0.15f, 0.15f}, {400, -0.2f, 0.2f}}}}, + {"h2CPRAfter", "Close pair rejection after cut; #Delta#eta; #Delta#phi^{*}", {HistType::kTH2F, {{300, -0.15f, 0.15f}, {400, -0.2f, 0.2f}}}}, + + // Reconstructed MC pair QA + {"MC/hPairFlow", "MC pair flow;step;counts", {HistType::kTH1F, {{7, -0.5f, 6.5f}}}}, + {"MC/hKstarRecVsGen", "Reconstructed versus generated k*;generated k* (GeV/c);reconstructed k* (GeV/c)", {HistType::kTH2F, {{300, 0.f, 3.f}, {300, 0.f, 3.f}}}}, + {"MC/hPtNuRecVsGen", "Reconstructed versus generated signed nucleus pT;generated pT (GeV/c);reconstructed pT (GeV/c)", {HistType::kTH2F, {{280, -7.f, 7.f}, {280, -7.f, 7.f}}}}, + {"MC/hPtHadRecVsGen", "Reconstructed versus generated signed pion pT;generated pT (GeV/c);reconstructed pT (GeV/c)", {HistType::kTH2F, {{280, -7.f, 7.f}, {280, -7.f, 7.f}}}}, + + // Base inputs for Giorgio-style offline DCA fits. The origin axis is: + // 0 primary, 1 weak decay, 2 material (every non-primary, non-decay process). + {"fraction/hDcaDataHad", "Hadron DCA-fit input;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}}}}, + {"fraction/hDcaDataNu", "Nucleus DCA-fit input;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}}}}, + {"fraction/hDcaTemplateHad", "Truth hadron DCA templates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}}}}, + {"fraction/hDcaTemplateNu", "Truth nucleus DCA templates;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}}}}, + + // DCA shapes of selected candidates that are not the requested truth + // species. They are kept separate from primary/weak/material so that + // their normalisation can be fixed or constrained by the PID purity. + {"fraction/hDcaMisidentifiedHad", "Misidentified hadron candidates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;collision association", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}}}}, + {"fraction/hDcaMisidentifiedNu", "Misidentified nucleus candidates;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;collision association", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}}}}, + {"fraction/hDcaNoMCLabelHad", "Selected hadron candidates without an MC label;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}}}}, + {"fraction/hDcaNoMCLabelNu", "Selected nucleus candidates without an MC label;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}}}}, + + // Superset templates for optional offline refinements. They retain the + // same base origin plus collision association, direct-parent category, + // and the particle production radius. Projecting away the extra axes + // recovers the base shapes; selecting them enables species-specific fits. + {"fraction/hDcaTemplateDetailHad", "Detailed truth hadron DCA templates;signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;parent category;production R_{xy} (cm)", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {NParentCategories, -0.5f, static_cast(NParentCategories) - 0.5f}, {200, 0.f, 100.f}}}}, + {"fraction/hDcaTemplateDetailNu", "Detailed truth nucleus DCA templates;signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;parent category;production R_{xy} (cm)", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {NParentCategories, -0.5f, static_cast(NParentCategories) - 0.5f}, {200, 0.f, 100.f}}}}, + + // Mother-resolved templates. There is one entry per direct mother (or a + // single zero-code entry when no mother is stored). Reconstruct the exact + // PDG code offline as sign * (PDG high * 10000 + PDG low). + {"fraction/hDcaMotherPdgHad", "Hadron DCA by exact direct-mother PDG; signed reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {HadronDcaFitBins, -HadronDcaFitAxisMax, HadronDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, + {"fraction/hDcaMotherPdgNu", "Nucleus DCA by exact direct-mother PDG; signed physical reconstructed p_{T} (GeV/c);DCA_{xy} (cm);DCA_{z} (cm);centrality;origin;collision association;mother PDG sign;mother PDG high;mother PDG low", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {NucleusDcaFitBins, -NucleusDcaFitAxisMax, NucleusDcaFitAxisMax}, {40, 0.f, 100.f}, {NDcaOrigins, -0.5f, static_cast(NDcaOrigins) - 0.5f}, {NCollisionAssociations, -0.5f, static_cast(NCollisionAssociations) - 0.5f}, {3, -1.5f, 1.5f}, {MotherPdgHighMax + 1, -0.5f, static_cast(MotherPdgHighMax) + 0.5f}, {MotherPdgChunkBase, -0.5f, static_cast(MotherPdgChunkBase) - 0.5f}}}}, + + // Generator denominators and source-resolved response matrices for the + // hypertriton-to-primary He3 and triton constraints. Generated momenta + // are physical particle momenta and must never receive the reconstructed + // He3-track x2 rigidity conversion. The two-body histograms are + // conditional on their generated decay channel, so the physical + // branching ratios remain independent offline inputs. + {"fractionMCGen/hPrimaryHe3", "Physical-primary generated He3 in selected MC collisions;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertriton", "Physical-primary generated hypertritons in selected MC collisions;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonToHe3Pi", "Physical-primary generated hypertritons in the He3-pion channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonDaughterHe3", "Generated direct He3 daughters in the hypertriton-He3-pion channel;signed generated p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonPtVsDaughterHe3Pt", "Generated hypertriton-to-He3 decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hPrimaryTriton", "Physical-primary generated tritons in selected MC collisions;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonToTritonPi0", "Physical-primary generated hypertritons in the triton-pi0 channel;signed generated p_{T}^{hypertriton} (GeV/c);generated y^{hypertriton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonDaughterTriton", "Generated direct triton daughters in the hypertriton-triton-pi0 channel;signed generated p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCGen/hHypertritonPtVsDaughterTritonPt", "Generated hypertriton-to-triton decay mapping;signed generated p_{T}^{hypertriton} (GeV/c);signed generated p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hPrimaryHe3GenPtVsRecoPt", "DCA-selected physical-primary He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt", "DCA-selected hypertriton-daughter He3 response;signed generated p_{T}^{He3} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{He3};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonParentPtVsDaughterRecoPt", "DCA-selected hypertriton parent-to-He3 response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{He3} (GeV/c);generated y^{hypertriton};generated y^{He3};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hPrimaryTritonGenPtVsRecoPt", "DCA-selected physical-primary triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt", "DCA-selected hypertriton-daughter triton response;signed generated p_{T}^{triton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{triton};centrality", {HistType::kTHnSparseF, {{280, -7.f, 7.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + {"fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt", "DCA-selected hypertriton parent-to-triton response;signed generated p_{T}^{hypertriton} (GeV/c);signed physical reconstructed p_{T}^{triton} (GeV/c);generated y^{hypertriton};generated y^{triton};centrality", {HistType::kTHnSparseF, {{400, -10.f, 10.f}, {280, -7.f, 7.f}, {120, -1.5f, 1.5f}, {120, -1.5f, 1.5f}, {40, 0.f, 100.f}}}}, + + // Hierarchical MC truth purity counters: all = correct species + mis-ID + // + no label; correct species = correct collision + wrong collision; + // correct collision = primary + weak + material. + {"purityMC/hHadron", "Selected hadron truth composition;signed reconstructed p_{T} (GeV/c);category;centrality", {HistType::kTH3F, {{280, -7.f, 7.f}, {NPurityCategories, -0.5f, static_cast(NPurityCategories) - 0.5f}, {40, 0.f, 100.f}}}}, + {"purityMC/hNucleus", "Selected nucleus truth composition;signed physical reconstructed p_{T} (GeV/c);category;centrality", {HistType::kTH3F, {{280, -7.f, 7.f}, {NPurityCategories, -0.5f, static_cast(NPurityCategories) - 0.5f}, {40, 0.f, 100.f}}}}, // dE/dx {"h2dEdxNucandidates", "dEdx distribution; #it{p} (GeV/#it{c}); dE/dx (a.u.)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {100, 0.0f, 2000.0f}}}}, {"h2dEdxHadcandidates", "dEdx distribution; #it{p} (GeV/#it{c}); dE/dx (a.u.)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {100, 0.0f, 2000.0f}}}}, {"h2dEdx", "dEdx distribution; #it{p} (GeV/#it{c}); dE/dx (a.u.)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {100, 0.0f, 2000.0f}}}}, - // Deuteron PID - {"h2NsigmaNuTPC", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{100, -2.0f, 2.0f}, {200, -5.0f, 5.0f}}}}, - {"h2NsigmaNuComb", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{100, -2.0f, 2.0f}, {100, 0.0f, 5.0f}}}}, - {"h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{50, -5.0f, 5.0f}, {120, -3.0f, 3.0f}}}}, - {"h2NSigmaNuITS", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{100, -2.0f, 2.0f}, {120, -3.0f, 3.0f}}}}, - {"h2NsigmaNuTOF", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {200, -5.0f, 5.0f}}}}, - {"h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, + // Nucleus PID + {"h2NsigmaNuTPC", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, + {"h2NsigmaNuComb", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, + {"h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -3.0f, 3.0f}}}}, + {"h2NSigmaNuITS", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -3.0f, 3.0f}}}}, + {"h2NsigmaNuTOF", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, + {"h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"h2MassTOFTr", "Triton TOF mass; signed #it{p} (GeV/#it{c}); m_{TOF} (GeV/#it{c}^{2})", {HistType::kTH2F, {{240, -6.0f, 6.0f}, {200, 0.0f, 5.0f}}}}, // Hadron PID - {"h2NsigmaHadComb", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{100, -2.0f, 2.0f}, {100, 0.0f, 5.0f}}}}, - {"h2NsigmaHadTPC", "NsigmaHad TPC distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(p)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {200, -5.0f, 5.0f}}}}, - {"h2NsigmaHadTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"h2NsigmaHadTOF", "NsigmaHad TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {200, -5.0f, 5.0f}}}}, - {"h2NsigmaHadTOF_preselection", "NsigmaHad TOF distribution; #iit{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, + {"h2NsigmaHadComb", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, + {"h2NsigmaHadTPC", "NsigmaHad TPC distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(p)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, + {"h2NsigmaHadTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"h2NsigmaHadTOF", "NsigmaHad TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, + {"h2NsigmaHadTOF_preselection", "NsigmaHad TOF distribution; #iit{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, {"h2NsigmaHadPrTPC", "NsigmaHad TPC distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(p)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, {"h2NsigmaHadPiTPC", "NsigmaHad TPC distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(pi)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, + {"h2NsigmaHadKaTPC", "NsigmaHad TPC distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(K)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, {"h2NsigmaHadPrTOF", "NsigmaHad TOF distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(p)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, {"h2NsigmaHadPiTOF", "NsigmaHad TOF distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(pi)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, + {"h2NsigmaHadKaTOF", "NsigmaHad TOF distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(K)", {HistType::kTH1F, {{200, -5.0f, 5.0f}}}}, // Purity - {"purity/h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{50, -5.0f, 5.0f}, {120, -3.0f, 3.0f}}}}, - {"purity/h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaHadTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"purity/h2NsigmaHadTOF_preselection", "NsigmaHad TOF distribution; #iit{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, + {"purity/h2MassTOFTr_preselection", "Triton TOF mass before mass cut; signed #it{p}_{T} (GeV/#it{c}); m_{TOF} (GeV/#it{c}^{2})", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {280, 0.0f, 7.0f}}}}, + {"purity/h2NsigmaNuTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"purity/h2NsigmaNuTPC_preselecComp", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"purity/h2NSigmaNuITS_preselection", "NsigmaNu ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} Nu", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {120, -5.0f, 5.0f}}}}, + {"purity/h2NsigmaNuTOF_preselection", "NsigmaNu TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"purity/h2NsigmaNuComb_preselection", "NsigmaNu TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 10.0f}}}}, + {"purity/h2NsigmaHadTPC_preselection", "NsigmaNu TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(Nu)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"purity/h2NsigmaHadTOF_preselection", "NsigmaHad TOF distribution; #iit{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(p)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"purity/h2NsigmaHadComb_preselection", "NsigmaHad TPCTOF comb distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{comb}(had)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 10.0f}}}}, // Hypertriton - {"hHe3TPCnsigma", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(He3)", {HistType::kTH2F, {{100, -2.0f, 2.0f}, {200, -5.0f, 5.0f}}}}, + {"hHe3TPCnsigma", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(He3)", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, {"hHe3P", "Pin distribution; p (GeV/#it{c})", {HistType::kTH1F, {{120, -3.0f, 3.0f}}}}, {"hHe3P_preselected", "Pin distribution_preselected; p (GeV/#it{c})", {HistType::kTH1F, {{120, -3.0f, 3.0f}}}}, - {"hNHypsPerPrevColl", "Number of V0Hypers in previous collision used for mixing;N_{V0Hypers};Entries", {HistType::kTH2F, {{4000, 0.0f, 4000.0f}, {50, -0.5, 49.5}}}}, // Correlation observables {"hkStar_LS_M", ";kStar (GeV/c)", {HistType::kTH1F, {{300, 0.0f, 3.0f}}}}, @@ -326,7 +858,11 @@ struct HadNucleiFemto { {"hkStaVsmT_LS_A", ";kStar (GeV/c);mT (GeV/#it{c}^{2})", {HistType::kTH2F, {{300, 0.0f, 3.0f}, {2000, 0.8, 2.0}}}}, {"hkStaVsmT_US_M", ";kStar (GeV/c);mT (GeV/#it{c}^{2})", {HistType::kTH2F, {{300, 0.0f, 3.0f}, {2000, 0.8, 2.0}}}}, {"hkStaVsmT_US_A", ";kStar (GeV/c);mT (GeV/#it{c}^{2})", {HistType::kTH2F, {{300, 0.0f, 3.0f}, {2000, 0.8, 2.0}}}}, - {"hNuHadtInvMass", "; M(Nu + p) (GeV/#it{c}^{2})", {HistType::kTH1F, {{300, 3.74f, 4.34f}}}}, + {"hkStaVsCent_LS_M", ";kStar (GeV/c);Centrality", {HistType::kTH2F, {{300, 0.0f, 3.0f}, {100, 0.0f, 100.0f}}}}, + {"hkStaVsCent_LS_A", ";kStar (GeV/c);Centrality", {HistType::kTH2F, {{300, 0.0f, 3.0f}, {100, 0.0f, 100.0f}}}}, + {"hkStaVsCent_US_M", ";kStar (GeV/c);Centrality", {HistType::kTH2F, {{300, 0.0f, 3.0f}, {100, 0.0f, 100.0f}}}}, + {"hkStaVsCent_US_A", ";kStar (GeV/c);Centrality", {HistType::kTH2F, {{300, 0.0f, 3.0f}, {100, 0.0f, 100.0f}}}}, + {"hNuHadtInvMass", "; M(Nu + had) (GeV/#it{c}^{2})", {HistType::kTH1F, {{500, 2.5f, 4.5f}}}}, // Mixed-event {"hisBkgEM", "; isBkgEM;", {HistType::kTH1F, {{3, -1, 2}}}}}, @@ -334,58 +870,72 @@ struct HadNucleiFemto { false, true}; - int numOfCentBins = 40; - int numOfVertexZBins = 30; - float Vz_low = -10.0f; - float Vz_high = 10.0f; - float Vz_step = (Vz_high - Vz_low) / numOfVertexZBins; - - struct EventRef { - uint64_t collisionId; - }; - - struct PoolBin { - std::deque events; - }; - - std::vector All_Event_pool; - bool isInitialized = false; - - int nPoolBins() const { return numOfVertexZBins * numOfCentBins; } - - void initializePools() + void init(o2::framework::InitContext& initContext) { - All_Event_pool.clear(); - All_Event_pool.resize(nPoolBins()); - isInitialized = true; - } - - int where_pool(float vz, float v0Centr) const - { - float CentBinWidth = 100.0 / numOfCentBins; // = 2.5 - - int iy = static_cast(std::floor(v0Centr / CentBinWidth)); - if (iy < 0) - iy = 0; - if (iy >= numOfCentBins) - iy = numOfCentBins - 1; - - int ix = static_cast(std::floor((vz - Vz_low) / Vz_step)); - if (ix < 0) - ix = 0; - if (ix >= numOfVertexZBins) - ix = numOfVertexZBins - 1; - - int bin = ix + numOfVertexZBins * iy; - return bin; - } + if (doprocessMixedEventDiagnostic) { + // AnalysisTask does not automatically invoke a task endOfStream method. + // CallbackService appends this callback without replacing output flushing. + initContext.services().get().set( + [this](EndOfStreamContext& context) { endOfStream(context); }); + if (doprocessMixedEvent || doprocessSameEvent || doprocessMC || doprocessHyper || doprocessMCHyper || doprocessPurity || doprocessDcaFractionData || doprocessDcaFractionPurityMC) { + LOG(fatal) << "processMixedEventDiagnostic must run alone; disable the other HadNucleiFemto processes to avoid shared selection-QA contamination"; + } + if (eventMixing.settingNoMixedEvents.value <= 0) { + LOG(fatal) << "ME diagnostic requires a positive mixing depth"; + } + mDiagnosticUUID = TUUID().AsString(); + std::string uuidWords = mDiagnosticUUID; + uuidWords.erase(std::remove(uuidWords.begin(), uuidWords.end(), '-'), uuidWords.end()); + mDiagnosticInstanceHi = std::stoull(uuidWords.substr(0, 16), nullptr, 16); + mDiagnosticInstanceLo = std::stoull(uuidWords.substr(16, 16), nullptr, 16); + const auto& device = initContext.services().get(); + const char* alienJob = std::getenv("ALIEN_PROC_ID"); + LOG(info) << "ME_DIAG INIT instance=" << mDiagnosticUUID << " device=" << device.id + << " rank=" << device.rank << " inputTimesliceId=" << device.inputTimesliceId + << " job=" << (alienJob ? alienJob : "unknown") + << " production=" << meDiagnostic.productionTag.value << " code=" << meDiagnostic.codeTag.value + << " depth=" << eventMixing.settingNoMixedEvents.value + << " requireBoth=" << eventMixing.settingRequireBothSpeciesForMixing.value + << " multiplicityBinning=" << eventMixing.settingUseMultiplicityBinning.value + << " applyCPR=" << CPR.settingApplyClosePairRejection.value; + for (const auto& name : {"hLifecycle", "hSelection"}) { + mDiagnosticRegistry.add(name, name, HistType::kTH1D, {{6, -0.5, 5.5}}); + } + mDiagnosticRegistry.add("hPoolOccupancy", "Prior partners;partners;events", HistType::kTH1D, {{101, -0.5, 100.5}}); + mDiagnosticRegistry.add("hPairs", "0=expected,1=accepted,2=CPR rejected;counter;pairs", HistType::kTH1D, {{3, -0.5, 2.5}}); + // The global sink keys by task + histogram name, not registry directory. + for (const auto& name : {"hMEDiagKStar_LS_M", "hMEDiagKStar_LS_A", "hMEDiagKStar_US_M", "hMEDiagKStar_US_A"}) { + mDiagnosticRegistry.add(name, name, HistType::kTH1D, {{300, 0., 3.}}); + } + } + const bool processHyperPairs = doprocessHyper || doprocessMCHyper; + if (processHyperPairs && hadHyper.maxOutputKstar.value == 0.f) { + LOG(fatal) << "Hadron-hypertriton mode requires a nonzero output k* range"; + } + if (processHyperPairs && hadHyper.enableMixing.value && eventMixing.settingNoMixedEvents.value <= 0) { + LOG(fatal) << "Hadron-hypertriton mixed-event mode requires enabled mixing and positive mixing depth"; + } + if (processHyperPairs && hypertriton.settingHypMassMin.value >= hypertriton.settingHypMassMax.value) { + LOG(fatal) << "Hadron-hypertriton mode requires settingHypMassMin < settingHypMassMax"; + } + constexpr int closePairRadiusModePv = 0; + constexpr int closePairRadiusModeSpecificTpc = 2; + if (CPR.settingEnableClosePairRejection.value) { + if (CPR.settingClosePairDeltaEtaMax.value <= 0.f || CPR.settingClosePairDeltaPhiMax.value <= 0.f) { + LOG(fatal) << "Close-pair rejection requires positive delta-eta and delta-phi-star limits"; + } + if (CPR.settingClosePairRadiusMode.value < closePairRadiusModePv || CPR.settingClosePairRadiusMode.value > closePairRadiusModeSpecificTpc) { + LOG(fatal) << "Invalid close-pair radius mode " << CPR.settingClosePairRadiusMode.value << "; expected 0, 1, or 2"; + } + if (CPR.settingClosePairRadiusMode.value == closePairRadiusModeSpecificTpc && CPR.settingClosePairSpecificRadius.value <= 0.f) { + LOG(fatal) << "Close-pair rejection at a specific TPC radius requires a positive radius"; + } + } - void init(o2::framework::InitContext&) - { mZorroSummary.setObject(mZorro.getZorroSummary()); mRunNumber = 0; - mCcdb->setURL(settingCcdburl); + mCcdb->setURL(ccdb.settingCcdburl); mCcdb->setCaching(true); mCcdb->setLocalObjectValidityChecking(); mCcdb->setFatalWhenNull(false); @@ -397,95 +947,199 @@ struct HadNucleiFemto { mFitter.setMaxDZIni(1e9); mFitter.setMaxChi2(1e9); mFitter.setUseAbsDCA(true); - int mat{static_cast(settingMaterialCorrection)}; + int mat{static_cast(pidCalibration.settingMaterialCorrection)}; mFitter.setMatCorrType(static_cast(mat)); - const int numParticles = 5; - for (int i = 0; i < numParticles; i++) { - mBBparamsDe[i] = settingBetheBlochParams->get("De", Form("p%i", i)); + if (!useTritonNucleus()) { + const int numParticles = 5; + const char* betheBlochLabel = nucleusBetheBlochLabel(); + for (int i = 0; i < numParticles; i++) { + mBBparamsNucleus[i] = pidCalibration.settingBetheBlochParams->get(betheBlochLabel, Form("p%i", i)); + } + mBBparamsNucleus[5] = pidCalibration.settingBetheBlochParams->get(betheBlochLabel, "resolution"); } - mBBparamsDe[5] = settingBetheBlochParams->get("De", "resolution"); std::vector selectionLabels = {"All", "Track selection", "PID"}; for (int i = 0; i < Selections::kAll; i++) { mQaRegistry.get(HIST("hTrackSel"))->GetXaxis()->SetBinLabel(i + 1, selectionLabels[i].c_str()); + mQaRegistry.get(HIST("hTrackSelNu"))->GetXaxis()->SetBinLabel(i + 1, selectionLabels[i].c_str()); } - std::vector eventsLabels = {"All", "Selected", "Zorro De events"}; + std::vector eventsLabels = {"All", "Selected", "Zorro selected events"}; for (int i = 0; i < Selections::kAll; i++) { mQaRegistry.get(HIST("hEvents"))->GetXaxis()->SetBinLabel(i + 1, eventsLabels[i].c_str()); } - mQaRegistry.get(HIST("hEmptyPool"))->GetXaxis()->SetBinLabel(1, "False"); - mQaRegistry.get(HIST("hEmptyPool"))->GetXaxis()->SetBinLabel(2, "True"); + const std::array mixedEventLabels = {"All collisions", "Event selection", "Mixing pool", "Mixed combinations"}; + for (size_t i = 0; i < mixedEventLabels.size(); i++) { + mQaRegistry.get(HIST("hMixedEventSelections"))->GetXaxis()->SetBinLabel(i + 1, mixedEventLabels[i].c_str()); + } + + const std::array originLabels = {"Primary", "WeakDecay", "Material"}; + const std::array collisionLabels = {"CorrectCollision", "WrongCollision"}; + const std::array parentLabels = {"NoSpecialParent", "Lambda", "Sigma", "K0Short", "K0Long", "ChargedKaon", "Hypertriton", "Lithium4", "OtherDecay", "MissingDecayMother"}; + const std::array purityLabels = {"AllSelected", "CorrectSpecies", "MisidentifiedSpecies", "NoMCLabel", "CorrectCollisionSpecies", "WrongCollisionSpecies", "CorrectPrimary", "CorrectWeakDecay", "CorrectMaterial"}; + + const auto baseDcaHad = mQaRegistry.get(HIST("fraction/hDcaTemplateHad")); + const auto baseDcaNu = mQaRegistry.get(HIST("fraction/hDcaTemplateNu")); + const std::array baseDcaHistograms = {baseDcaHad.get(), baseDcaNu.get()}; + for (const auto& histogram : baseDcaHistograms) { + for (int i = 0; i < NDcaOrigins; ++i) { + histogram->GetAxis(4)->SetBinLabel(i + 1, originLabels[i]); + } + } + const auto misidentifiedDcaHad = mQaRegistry.get(HIST("fraction/hDcaMisidentifiedHad")); + const auto misidentifiedDcaNu = mQaRegistry.get(HIST("fraction/hDcaMisidentifiedNu")); + const std::array misidentifiedDcaHistograms = {misidentifiedDcaHad.get(), misidentifiedDcaNu.get()}; + for (const auto& histogram : misidentifiedDcaHistograms) { + for (int i = 0; i < NCollisionAssociations; ++i) { + histogram->GetAxis(4)->SetBinLabel(i + 1, collisionLabels[i]); + } + } + const auto detailedDcaHad = mQaRegistry.get(HIST("fraction/hDcaTemplateDetailHad")); + const auto detailedDcaNu = mQaRegistry.get(HIST("fraction/hDcaTemplateDetailNu")); + const std::array detailedDcaHistograms = {detailedDcaHad.get(), detailedDcaNu.get()}; + for (const auto& histogram : detailedDcaHistograms) { + for (int i = 0; i < NDcaOrigins; ++i) { + histogram->GetAxis(4)->SetBinLabel(i + 1, originLabels[i]); + } + for (int i = 0; i < NCollisionAssociations; ++i) { + histogram->GetAxis(5)->SetBinLabel(i + 1, collisionLabels[i]); + } + for (int i = 0; i < NParentCategories; ++i) { + histogram->GetAxis(6)->SetBinLabel(i + 1, parentLabels[i]); + } + } + const auto motherPdgHad = mQaRegistry.get(HIST("fraction/hDcaMotherPdgHad")); + const auto motherPdgNu = mQaRegistry.get(HIST("fraction/hDcaMotherPdgNu")); + const std::array motherPdgHistograms = {motherPdgHad.get(), motherPdgNu.get()}; + for (const auto& histogram : motherPdgHistograms) { + for (int i = 0; i < NDcaOrigins; ++i) { + histogram->GetAxis(4)->SetBinLabel(i + 1, originLabels[i]); + } + for (int i = 0; i < NCollisionAssociations; ++i) { + histogram->GetAxis(5)->SetBinLabel(i + 1, collisionLabels[i]); + } + histogram->GetAxis(6)->SetBinLabel(1, "Negative"); + histogram->GetAxis(6)->SetBinLabel(2, "NoMother"); + histogram->GetAxis(6)->SetBinLabel(3, "Positive"); + } + const auto purityHad = mQaRegistry.get(HIST("purityMC/hHadron")); + const auto purityNu = mQaRegistry.get(HIST("purityMC/hNucleus")); + const std::array purityHistograms = {purityHad.get(), purityNu.get()}; + for (const auto& histogram : purityHistograms) { + for (int i = 0; i < NPurityCategories; ++i) { + histogram->GetYaxis()->SetBinLabel(i + 1, purityLabels[i]); + } + } } + template void initCCDB(const aod::BCsWithTimestamps::iterator& bc) { if (mRunNumber == bc.runNumber()) { return; } - if (settingSkimmedProcessing) { - mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), "fDe"); - mZorro.populateHistRegistry(mQaRegistry, bc.runNumber()); + if constexpr (!isMC) { + if (zorro.settingSkimmedProcessing) { + mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), zorroTriggerMask()); + mZorro.populateHistRegistry(mQaRegistry, bc.runNumber()); + } } mRunNumber = bc.runNumber(); const float defaultBzValue = -999.0f; + + // A fixed field is sufficient for CPR and DCAFitter when material + // corrections are disabled. This also makes local MC tests independent + // of an AliEn token when the CCDB payload is stored on Grid. + if (ccdb.settingDbz > defaultBzValue && pidCalibration.settingMaterialCorrection == 0) { + mDbz = ccdb.settingDbz; + mFitter.setBz(mDbz); + LOG(info) << "Using configured magnetic field of " << mDbz << " kZG"; + return; + } + auto run3GrpTimestamp = bc.timestamp(); - o2::parameters::GRPObject* grpo = mCcdb->getForTimeStamp(settingGrpPath, run3GrpTimestamp); - o2::parameters::GRPMagField* grpmag = 0x0; + auto* grpo = mCcdb->getForTimeStamp(ccdb.settingGrpPath, run3GrpTimestamp); + o2::parameters::GRPMagField* grpmag = nullptr; if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); - if (settingDbz < defaultBzValue) { + if (ccdb.settingDbz <= defaultBzValue) { // Fetch magnetic field from ccdb for current collision mDbz = grpo->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << run3GrpTimestamp << " with magnetic field of " << mDbz << " kZG"; } else { - mDbz = settingDbz; + mDbz = ccdb.settingDbz; } } else { - grpmag = mCcdb->getForTimeStamp(settingGrpmagPath, run3GrpTimestamp); + grpmag = mCcdb->getForTimeStamp(ccdb.settingGrpmagPath, run3GrpTimestamp); if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << settingGrpmagPath << " of object GRPMagField and " << settingGrpPath << " of object GRPObject for timestamp " << run3GrpTimestamp; + LOG(fatal) << "Got nullptr from CCDB for path " << ccdb.settingGrpmagPath << " of object GRPMagField and " << ccdb.settingGrpPath << " of object GRPObject for timestamp " << run3GrpTimestamp; } o2::base::Propagator::initFieldFromGRP(grpmag); - if (settingDbz < defaultBzValue) { + if (ccdb.settingDbz <= defaultBzValue) { // Fetch magnetic field from ccdb for current collision mDbz = std::lround(5.f * grpmag->getL3Current() / 30000.f); LOG(info) << "Retrieved GRP for timestamp " << run3GrpTimestamp << " with magnetic field of " << mDbz << " kZG"; } else { - mDbz = settingDbz; + mDbz = ccdb.settingDbz; } } + mFitter.setBz(mDbz); } // ================================================================================================================== template - bool selectCollision(const Tcollision& collision, const aod::BCsWithTimestamps&) + bool passesEventSelection(const Tcollision& collision) { - mQaRegistry.fill(HIST("hEvents"), 0); + // CPR uses phi* and therefore needs the magnetic field for MC as well. + auto bc = collision.template bc_as(); + initCCDB(bc); if constexpr (isMC) { - if (/*!collision.sel8() ||*/ std::abs(collision.posZ()) > settingCutVertex) { + if ((mc.settingRequireSel8.value && !collision.sel8()) || std::abs(collision.posZ()) > eventMixing.settingCutVertex) { return false; } } else { - auto bc = collision.template bc_as(); - initCCDB(bc); - - if (!collision.sel8() || std::abs(collision.posZ()) > settingCutVertex) { + if (!collision.sel8() || std::abs(collision.posZ()) > eventMixing.settingCutVertex) { return false; } - if (settingSkimmedProcessing) { - bool zorroSelected = mZorro.isSelected(collision.template bc_as().globalBC()); - if (zorroSelected) { - mQaRegistry.fill(HIST("hEvents"), 2); + } + + return true; + } + + template + bool passesZorroSelection(const Tcollision& collision) + { + if (!zorro.settingSkimmedProcessing) { + return true; + } + auto bc = collision.template bc_as(); + return mZorro.isSelected(bc.globalBC()); + } + + template + bool selectCollision(const Tcollision& collision, const aod::BCsWithTimestamps&) + { + mQaRegistry.fill(HIST("hEvents"), 0); + + if (!passesEventSelection(collision)) { + return false; + } + + mQaRegistry.fill(HIST("hEvents"), 1); + + if constexpr (!isMC) { + if (zorro.settingSkimmedProcessing) { + if (!passesZorroSelection(collision)) { + return false; } + mQaRegistry.fill(HIST("hEvents"), 2); } } - mQaRegistry.fill(HIST("hEvents"), 1); mQaRegistry.fill(HIST("hNcontributor"), collision.numContrib()); mQaRegistry.fill(HIST("hVtxZ"), collision.posZ()); return true; @@ -494,240 +1148,1075 @@ struct HadNucleiFemto { template bool selectTrack(const Ttrack& candidate) { - if (std::abs(candidate.eta()) > settingCutEta) { + if (std::abs(candidate.eta()) > trackCut.settingCutEta) { return false; } const int minTPCNClsFound = 90; - const int minTPCNClsCrossedRows = 100; + const int minTPCNClsCrossedRows = 70; const float crossedRowsToFindableRatio = 0.83f; - if (candidate.itsNCls() < settingCutNCls || - candidate.tpcNClsFound() < minTPCNClsFound || - candidate.tpcNClsCrossedRows() < minTPCNClsCrossedRows || - candidate.tpcNClsCrossedRows() < crossedRowsToFindableRatio * candidate.tpcNClsFindable() || - candidate.tpcChi2NCl() > settingCutChi2tpcHigh || - candidate.tpcChi2NCl() < settingCutChi2tpcLow || - candidate.itsChi2NCl() > settingCutChi2NClITS) { - return false; - } - - return true; + return !(candidate.itsNCls() < trackCut.settingCutNCls || + candidate.tpcNClsFound() < minTPCNClsFound || + candidate.tpcNClsCrossedRows() < minTPCNClsCrossedRows || + candidate.tpcNClsCrossedRows() < crossedRowsToFindableRatio * candidate.tpcNClsFindable() || + candidate.tpcChi2NCl() > trackCut.settingCutChi2tpcHigh || + candidate.tpcChi2NCl() < trackCut.settingCutChi2tpcLow || + candidate.itsChi2NCl() > trackCut.settingCutChi2NClITS); } template - bool selectionPIDKaon(const Ttrack& candidate) + bool selectTrackPion(const Ttrack& candidate) { - auto tpcNSigmaKa = candidate.tpcNSigmaKa(); - float DeDCAxyMin = 0.004 + (0.013 / candidate.pt()); - float DeDCAzMin = 0.004 + (0.013 / candidate.pt()); - if (std::abs(candidate.dcaXY()) > DeDCAxyMin || std::abs(candidate.dcaZ()) > DeDCAzMin) + if (std::abs(candidate.eta()) > trackCut.settingCutEta) { return false; + } - mQaRegistry.fill(HIST("h2NsigmaHadTPC_preselection"), candidate.tpcInnerParam(), tpcNSigmaKa); - if (std::abs(candidate.pt()) < settingCutHadptMin || std::abs(candidate.pt()) > settingCutHadptMax) + const float absPt = std::abs(candidate.pt()); + if (absPt < hadronPid.settingHadptMin || absPt > hadronPid.settingHadptMax) { return false; + } - // reject protons and pions - if (std::abs(candidate.tpcNSigmaPr()) < settingCutNsigTPCPrMin || std::abs(candidate.tpcNSigmaPi()) < settingCutNsigTPCPiMin) - return false; - mQaRegistry.fill(HIST("h2NsigmaHadPrTPC"), candidate.tpcNSigmaPr()); - mQaRegistry.fill(HIST("h2NsigmaHadPiTPC"), candidate.tpcNSigmaPi()); - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < settingCutNsigTOFPrMin) + if (candidate.itsNClsInnerBarrel() < hadronPid.settingPionITSInnerBarrelMin || + candidate.itsNCls() < hadronPid.settingPionITSNClsMin || + candidate.tpcNClsFound() < hadronPid.settingPionTPCNClsFoundMin || + candidate.tpcNClsCrossedRows() < hadronPid.settingPionTPCCrossedRowsMin) { return false; - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < settingCutNsigTOFPiMin) + } + + if (absPt <= 0.f) { return false; - mQaRegistry.fill(HIST("h2NsigmaHadPrTOF"), candidate.tofNSigmaPr()); - mQaRegistry.fill(HIST("h2NsigmaHadPiTOF"), candidate.tofNSigmaPi()); - // rejection end + } - if (candidate.hasTOF() && candidate.tpcInnerParam() >= settingCutPinMinTOFHad) { - auto tofNSigmaKa = candidate.tofNSigmaKa(); + const float pionDCAxyMax = hadronPid.settingPionDCAxyOffset + hadronPid.settingPionDCAxyPtCoeff / absPt; + const float pionDCAzMax = hadronPid.settingPionDCAzOffset + hadronPid.settingPionDCAzPtCoeff / absPt; + return !(std::abs(candidate.dcaXY()) > pionDCAxyMax || std::abs(candidate.dcaZ()) > pionDCAzMax); + } - mQaRegistry.fill(HIST("h2NsigmaHadTOF_preselection"), candidate.pt(), tofNSigmaKa); - if (std::abs(tofNSigmaKa) > settingCutNsigmaTOFHad) { - return false; - } - if (std::abs(tpcNSigmaKa) > settingCutNsigmaTPCHad) { - return false; - } - mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaKa); - mQaRegistry.fill(HIST("h2NsigmaHadTOF"), candidate.sign() * candidate.pt(), tofNSigmaKa); - mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); - return true; - } else if (candidate.tpcInnerParam() < settingCutPinMinTOFHad) { - if (std::abs(tpcNSigmaKa) > settingCutNsigmaTPCHad) { - return false; - } - mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaKa); - mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); - return true; + template + bool selectTrackHadron(const Ttrack& candidate) + { + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return selectTrackProton(candidate); } - return false; + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return selectTrackPion(candidate); + } + return selectTrack(candidate); } template - bool selectionPIDPion(const Ttrack& candidate) + bool selectTrackProton(const Ttrack& candidate) { - if (std::abs(candidate.dcaXY()) > settingCutHadDCAxyMin || std::abs(candidate.dcaZ()) > settingCutHadDCAzMin) - return false; + constexpr float protonEtaMax = 0.8f; + constexpr int protonTPCNClsFoundMin = 90; + constexpr int protonTPCCrossedRowsMin = 80; + constexpr float protonDCAzMax = 0.2f; - auto tpcNSigmaPi = candidate.tpcNSigmaPi(); - mQaRegistry.fill(HIST("h2NsigmaHadTPC_preselection"), candidate.sign() * candidate.tpcInnerParam(), tpcNSigmaPi); - if (std::abs(candidate.pt()) < settingCutHadptMin || std::abs(candidate.pt()) > settingCutHadptMax) - return false; - // reject protons - if (std::abs(candidate.tpcNSigmaPr()) < settingCutNsigTPCPrMin) - return false; - mQaRegistry.fill(HIST("h2NsigmaHadPrTPC"), candidate.tpcNSigmaPr()); - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < settingCutNsigTOFPrMin) + if (std::abs(candidate.eta()) >= protonEtaMax) { return false; - mQaRegistry.fill(HIST("h2NsigmaHadPrTOF"), candidate.tofNSigmaPr()); + } - if (candidate.hasTOF() && candidate.tpcInnerParam() >= settingCutPinMinTOFHad) { - auto tofNSigmaPi = candidate.tofNSigmaPi(); - auto combNsigma = std::sqrt(tofNSigmaPi * tofNSigmaPi + tpcNSigmaPi * tpcNSigmaPi); + const float absPt = std::abs(candidate.pt()); + if (absPt <= 0.f) { + return false; + } - mQaRegistry.fill(HIST("h2NsigmaHadTOF_preselection"), candidate.pt(), tofNSigmaPi); - if (std::abs(tofNSigmaPi) > settingCutNsigmaTOFHad) { - return false; - } - if (std::abs(tpcNSigmaPi) > settingCutNsigmaTPCHad) { - return false; - } - mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaPi); - mQaRegistry.fill(HIST("h2NsigmaHadTOF"), candidate.sign() * candidate.pt(), tofNSigmaPi); - mQaRegistry.fill(HIST("h2NsigmaHadComb"), candidate.sign() * candidate.pt(), combNsigma); - mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); - return true; - } else if (candidate.tpcInnerParam() < settingCutPinMinTOFHad) { - if (std::abs(tpcNSigmaPi) > settingCutNsigmaTPCHad) { - return false; - } - mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaPi); - mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); - return true; + if (candidate.tpcNClsFound() <= protonTPCNClsFoundMin || + candidate.tpcNClsCrossedRows() <= protonTPCCrossedRowsMin) { + return false; } - return false; + + const float prDCAxyMax = 105.e-3f + 30.5e-3f / std::pow(absPt, 1.1f); + return !(std::abs(candidate.dcaXY()) >= prDCAxyMax || std::abs(candidate.dcaZ()) >= protonDCAzMax); } template - bool selectionPIDHadron(const Ttrack& candidate) + bool selectTrackDe(const Ttrack& candidate) { - bool PID = false; - if (settingHadPDGCode == PDG_t::kPiPlus) { - PID = selectionPIDPion(candidate); - MassHad = o2::constants::physics::MassPiPlus; - } else if (settingHadPDGCode == PDG_t::kKPlus) { - PID = selectionPIDKaon(candidate); - MassHad = o2::constants::physics::MassKPlus; - } else { - LOG(info) << "invalid PDG code"; + if (std::abs(candidate.eta()) > trackCut.settingCutEta) { + return false; } - return PID; + + constexpr float minTPCCrossedRowsOverFound = 0.f; + constexpr int maxTPCNClsShared = 160; + constexpr float maxSharedTPCFraction = 1.f; + constexpr int minITSNClsInnerBarrel = 1; + const float tpcCrossedRowsOverFound = candidate.tpcNClsFound() > 0 ? static_cast(candidate.tpcNClsCrossedRows()) / candidate.tpcNClsFound() : 0.f; + + return !(candidate.tpcNClsFound() < deuteronPid.settingTPCNClsFoundMinDe || + candidate.tpcNClsCrossedRows() < deuteronPid.settingTPCCrossedRowsMinDe || + tpcCrossedRowsOverFound < minTPCCrossedRowsOverFound || + candidate.tpcNClsShared() > maxTPCNClsShared || + candidate.tpcFractionSharedCls() > maxSharedTPCFraction || + candidate.itsNCls() < trackCut.settingCutNCls || + candidate.itsNClsInnerBarrel() < minITSNClsInnerBarrel); } template - float computeNSigmaDe(const Ttrack& candidate) + bool selectTrackTr(const Ttrack& candidate) { - float expTPCSignal = o2::common::BetheBlochAleph(static_cast(candidate.tpcInnerParam() / constants::physics::MassDeuteron), mBBparamsDe[0], mBBparamsDe[1], mBBparamsDe[2], mBBparamsDe[3], mBBparamsDe[4]); - double resoTPC{expTPCSignal * mBBparamsDe[5]}; - return static_cast((candidate.tpcSignal() - expTPCSignal) / resoTPC); + constexpr float maxAbsEta = 0.8f; + constexpr float maxAbsDcaXY = 0.2f; + constexpr float maxAbsDcaZ = 0.2f; + constexpr int minTPCCrossedRows = 70; + constexpr float maxTPCChi2NCl = 5.f; + constexpr float maxTPCFractionSharedCls = 0.3f; + constexpr float maxITSChi2NCl = 10.f; + + return !(std::abs(candidate.eta()) >= maxAbsEta || + std::abs(candidate.dcaXY()) >= maxAbsDcaXY || + std::abs(candidate.dcaZ()) >= maxAbsDcaZ || + candidate.tpcNClsCrossedRows() < minTPCCrossedRows || + candidate.tpcChi2NCl() >= maxTPCChi2NCl || + candidate.tpcFractionSharedCls() >= maxTPCFractionSharedCls || + candidate.itsNCls() < tritonPid.settingITSNClsMinTr || + candidate.itsChi2NCl() >= maxITSChi2NCl); } - template - bool selectionPIDDe(const Ttrack& candidate) + bool useDeuteronNucleus() const { - float tpcInnerParam = candidate.tpcInnerParam(); - mQaRegistry.fill(HIST("h2dEdx"), candidate.sign() * tpcInnerParam, candidate.tpcSignal()); + return species.settingNuPDGCode.value == DeuteronPDG; + } - float DeDCAxyMin = 0.015 + 0.0305 / TMath::Power(candidate.pt(), 1.1); - if (std::abs(candidate.dcaXY()) > DeDCAxyMin || std::abs(candidate.dcaZ()) > settingCutDeDCAzMin) - return false; + bool useTritonNucleus() const + { + return species.settingNuPDGCode.value == TritonPDG; + } - if (std::abs(tpcInnerParam) < settingCutPinMinDe) { - return false; + bool useHelium3Nucleus() const + { + return species.settingNuPDGCode.value == He3PDG; + } + + const char* zorroTriggerMask() const + { + if (useTritonNucleus()) { + return "fTritonFemto"; } - float tpcNSigmaDe; - if (settingUseBBcomputeDeNsigma) { - tpcNSigmaDe = computeNSigmaDe(candidate); - } else { - tpcNSigmaDe = candidate.tpcNSigmaDe(); + return useHelium3Nucleus() ? "fHe" : "fDe"; + } + + const char* nucleusBetheBlochLabel() const + { + return useHelium3Nucleus() ? "He3" : "De"; + } + + float nucleusChargeFactor() const + { + return useHelium3Nucleus() ? 2.f : 1.f; + } + + float nucleusMass() const + { + if (useHelium3Nucleus()) { + return static_cast(o2::constants::physics::MassHelium3); + } + if (useTritonNucleus()) { + return static_cast(o2::constants::physics::MassTriton); } + return static_cast(o2::constants::physics::MassDeuteron); + } - mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselection"), candidate.sign() * candidate.pt(), tpcNSigmaDe); - mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselecComp"), candidate.sign() * candidate.pt(), candidate.tpcNSigmaDe()); - if (std::abs(candidate.pt()) < settingCutDeptMin || std::abs(candidate.pt()) > settingCutDeptMax) + template + bool selectTrackHe3(const Ttrack& candidate) + { + if (std::abs(candidate.eta()) > trackCut.settingCutEta) { return false; - if (candidate.hasTOF() && candidate.tpcInnerParam() > settingCutPinMinTOFITSDe) { - auto tofNSigmaDe = candidate.tofNSigmaDe(); - auto combNsigma = std::sqrt(tofNSigmaDe * tofNSigmaDe + tpcNSigmaDe * tpcNSigmaDe); - mQaRegistry.fill(HIST("h2NsigmaNuTOF_preselection"), candidate.sign() * candidate.pt(), tofNSigmaDe); - if (combNsigma > settingCutNsigmaTOFTPCDe) { - return false; - } - mQaRegistry.fill(HIST("h2dEdxNucandidates"), candidate.sign() * tpcInnerParam, candidate.tpcSignal()); - mQaRegistry.fill(HIST("h2NsigmaNuComb"), candidate.sign() * candidate.pt(), combNsigma); - mQaRegistry.fill(HIST("h2NsigmaNuTPC"), candidate.sign() * candidate.pt(), tpcNSigmaDe); - mQaRegistry.fill(HIST("h2NsigmaNuTOF"), candidate.sign() * candidate.pt(), tofNSigmaDe); - return true; - } else if (candidate.tpcInnerParam() <= settingCutPinMinTOFITSDe) { - if (std::abs(tpcNSigmaDe) > settingCutNsigmaTPCDe) { - return false; - } - o2::aod::ITSResponse mResponseITS; - auto itsnSigmaDe = mResponseITS.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); - mQaRegistry.fill(HIST("h2NSigmaNuITS_preselection"), candidate.sign() * candidate.pt(), itsnSigmaDe); - if (std::abs(itsnSigmaDe) > settingCutNsigmaITSDe) { - return false; - } - mQaRegistry.fill(HIST("h2NsigmaNuTPC"), candidate.sign() * candidate.pt(), tpcNSigmaDe); - mQaRegistry.fill(HIST("h2NSigmaNuITS"), candidate.sign() * candidate.pt(), itsnSigmaDe); - mQaRegistry.fill(HIST("h2dEdxNucandidates"), candidate.sign() * tpcInnerParam, candidate.tpcSignal()); - return true; } + + constexpr float crossedRowsToFindableRatio = 0.8f; + return !(candidate.itsNCls() < trackCut.settingCutNCls || + candidate.tpcNClsFound() < helium3Pid.settingTPCNClsFoundMinHe3 || + candidate.tpcNClsCrossedRows() < helium3Pid.settingTPCCrossedRowsMinHe3 || + candidate.tpcNClsCrossedRows() < crossedRowsToFindableRatio * candidate.tpcNClsFindable() || + candidate.tpcChi2NCl() > trackCut.settingCutChi2tpcHigh || + candidate.tpcChi2NCl() < He3TPCChi2NClMin || + candidate.itsChi2NCl() > trackCut.settingCutChi2NClITS); + } + + template + bool selectTrackNu(const Ttrack& candidate) + { + if (useHelium3Nucleus()) { + return selectTrackHe3(candidate); + } + if (useTritonNucleus()) { + return selectTrackTr(candidate); + } + if (useDeuteronNucleus()) { + return selectTrackDe(candidate); + } + LOG(info) << "invalid nucleus PDG code"; return false; } - float averageClusterSizeCosl(uint32_t itsClusterSizes, float eta) + // DCA-template inputs must retain the tails. These predicates reproduce the + // nominal track-quality selections while deliberately omitting only DCA. + template + bool selectPionTrackForDcaFit(const Ttrack& candidate) const { - float average = 0; - int nclusters = 0; - const float cosl = 1. / std::cosh(eta); - const int nlayerITS = 7; + const float absPt = std::abs(candidate.pt()); + return std::abs(candidate.eta()) <= trackCut.settingCutEta.value && + absPt >= hadronPid.settingHadptMin.value && + absPt <= hadronPid.settingHadptMax.value && + candidate.itsNClsInnerBarrel() >= hadronPid.settingPionITSInnerBarrelMin.value && + candidate.itsNCls() >= hadronPid.settingPionITSNClsMin.value && + candidate.tpcNClsFound() >= hadronPid.settingPionTPCNClsFoundMin.value && + candidate.tpcNClsCrossedRows() >= hadronPid.settingPionTPCCrossedRowsMin.value; + } - for (int layer = 0; layer < nlayerITS; layer++) { - if ((itsClusterSizes >> (layer * 4)) & 0xf) { - nclusters++; - average += (itsClusterSizes >> (layer * 4)) & 0xf; - } + template + bool selectProtonTrackForDcaFit(const Ttrack& candidate) const + { + constexpr float protonEtaMax = 0.8f; + constexpr int protonTPCNClsFoundMin = 90; + constexpr int protonTPCCrossedRowsMin = 80; + return std::abs(candidate.eta()) < protonEtaMax && + std::abs(candidate.pt()) > 0.f && + candidate.tpcNClsFound() > protonTPCNClsFoundMin && + candidate.tpcNClsCrossedRows() > protonTPCCrossedRowsMin; + } + + template + bool selectTritonTrackForDcaFit(const Ttrack& candidate) const + { + constexpr float maxAbsEta = 0.8f; + constexpr int minTPCCrossedRows = 70; + constexpr float maxTPCChi2NCl = 5.f; + constexpr float maxTPCFractionSharedCls = 0.3f; + constexpr int minITSNCls = 5; + constexpr float maxITSChi2NCl = 10.f; + return std::abs(candidate.eta()) < maxAbsEta && + candidate.tpcNClsCrossedRows() >= minTPCCrossedRows && + candidate.tpcChi2NCl() < maxTPCChi2NCl && + candidate.tpcFractionSharedCls() < maxTPCFractionSharedCls && + candidate.itsNCls() >= minITSNCls && + candidate.itsChi2NCl() < maxITSChi2NCl; + } + + template + bool selectHadronTrackForDcaFit(const Ttrack& candidate) + { + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return selectPionTrackForDcaFit(candidate); } - if (nclusters == 0) { - return 0; + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return selectProtonTrackForDcaFit(candidate); } - return average * cosl / nclusters; - }; + // Keep the nominal selection for other supported hadrons instead of + // silently changing their established cuts. + return selectTrackHadron(candidate); + } - bool selectionPIDHyper(const aod::DataHypCandsWColl::iterator& V0Hyper) + template + bool selectNucleusTrackForDcaFit(const Ttrack& candidate) { - mQaRegistry.fill(HIST("hHe3P_preselected"), V0Hyper.tpcMomHe()); - float averClusSizeHe = averageClusterSizeCosl(V0Hyper.itsClusterSizesHe(), V0Hyper.etaHe3()); - if (averClusSizeHe <= settingCutAverClsSizeHe) { - return false; - } - if (V0Hyper.tpcChi2He() <= settingCutTPCChi2He) { - return false; + if (useTritonNucleus()) { + return selectTritonTrackForDcaFit(candidate); } - mQaRegistry.fill(HIST("hHe3P"), V0Hyper.tpcMomHe()); - mQaRegistry.fill(HIST("hHe3TPCnsigma"), V0Hyper.ptHe3(), V0Hyper.nSigmaHe()); + // The deuteron and helium-3 track-quality predicates contain no DCA cut. + return selectTrackNu(candidate); + } - return true; + void fillNucleusTrackSelection(const Selections selection) + { + mQaRegistry.fill(HIST("hTrackSelNu"), selection); } - // ================================================================================================================== + void fillNucleusPairFlow(const int step) + { + mQaRegistry.fill(HIST("hNuPairFlow"), step); + } - template - bool fillCandidateInfo(const Ttrack& trackDe, const Ttrack& trackHad, const CollBracket& collBracket, const Tcollisions& collisions, HadNucandidate& hadNucand, const Ttracks& /*trackTable*/, bool isMixedEvent) + bool isPionDeuteronPair() const { - const int numCoordinates = 3; + return species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus) && useDeuteronNucleus(); + } + + bool isPionTritonPair() const + { + return species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus) && useTritonNucleus(); + } + + bool shouldFillOutputTable(const HadNucandidate& hadNucand) const + { + if (!output.settingFillTableLowKstarOnly.value) { + return true; + } + if (isPionDeuteronPair()) { + return hadNucand.kstar < output.settingFillTablePiDeKstarMax.value; + } + if (isPionTritonPair()) { + return hadNucand.kstar < output.settingFillTablePiTrKstarMax.value; + } + return true; + } + + template + float phiAtSpecificRadiiTPC(const Ttrack& track, float radius) const + { + const float absPt = std::abs(track.pt()); + if (absPt <= 0.f) { + return 999.f; + } + const float arg = 0.3f * static_cast(track.sign()) * 0.1f * mDbz * radius * 0.01f / (2.f * absPt); + if (std::fabs(arg) >= 1.f) { + return 999.f; + } + return track.phi() - std::asin(arg); + } + + float wrapDeltaPhi(float dphi) const + { + return std::atan2(std::sin(dphi), std::cos(dphi)); + } + + template + float averagePhiStar(const Ttrack1& firstTrack, const Ttrack2& secondTrack) const + { + constexpr float invalidPhiStar = 999.f; + float dPhiAvg = 0.f; + int meaningfulEntries = 0; + for (const auto& radius : tmpRadiiTPC) { + const float phi1 = phiAtSpecificRadiiTPC(firstTrack, radius); + const float phi2 = phiAtSpecificRadiiTPC(secondTrack, radius); + if (phi1 == invalidPhiStar || phi2 == invalidPhiStar) { + continue; + } + dPhiAvg += wrapDeltaPhi(phi1 - phi2); + meaningfulEntries++; + } + if (meaningfulEntries == 0) { + return invalidPhiStar; + } + return dPhiAvg / static_cast(meaningfulEntries); + } + + template + bool computeClosePairDeltas(const Ttrack1& firstTrack, const Ttrack2& secondTrack, float& deltaEta, float& deltaPhi) const + { + constexpr int closePairRadiusModePv = 0; + constexpr int closePairRadiusModeSpecificTpc = 2; + constexpr float invalidPhiStar = 999.f; + constexpr float invalidOutputDelta = -99.f; + + deltaEta = invalidOutputDelta; + deltaPhi = invalidOutputDelta; + if (!CPR.settingEnableClosePairRejection.value) { + return false; + } + + float selectedDeltaPhi = averagePhiStar(firstTrack, secondTrack); + if (CPR.settingClosePairRadiusMode.value == closePairRadiusModePv) { + selectedDeltaPhi = wrapDeltaPhi(firstTrack.phi() - secondTrack.phi()); + } else if (CPR.settingClosePairRadiusMode.value == closePairRadiusModeSpecificTpc) { + const float firstPhi = phiAtSpecificRadiiTPC(firstTrack, CPR.settingClosePairSpecificRadius.value); + const float secondPhi = phiAtSpecificRadiiTPC(secondTrack, CPR.settingClosePairSpecificRadius.value); + if (firstPhi == invalidPhiStar || secondPhi == invalidPhiStar) { + return false; + } + selectedDeltaPhi = wrapDeltaPhi(firstPhi - secondPhi); + } + + if (selectedDeltaPhi == invalidPhiStar) { + return false; + } + + deltaEta = firstTrack.eta() - secondTrack.eta(); + deltaPhi = selectedDeltaPhi; + return true; + } + + template + bool isClosePair(const Ttrack1& firstTrack, const Ttrack2& secondTrack, bool fillQA) + { + if (!CPR.settingEnableClosePairRejection.value) { + return false; + } + if (firstTrack.sign() != secondTrack.sign()) { + return false; + } + + float deltaEta = -99.f; + float deltaPhi = -99.f; + if (!computeClosePairDeltas(firstTrack, secondTrack, deltaEta, deltaPhi)) { + return false; + } + + if (fillQA) { + mQaRegistry.fill(HIST("h2CPRBefore"), deltaEta, deltaPhi); + } + if (!CPR.settingApplyClosePairRejection.value) { + if (fillQA) { + mQaRegistry.fill(HIST("h2CPRAfter"), deltaEta, deltaPhi); + } + return false; + } + const bool isRejected = std::pow(deltaPhi, 2.f) / std::pow(CPR.settingClosePairDeltaPhiMax.value, 2.f) + + std::pow(deltaEta, 2.f) / std::pow(CPR.settingClosePairDeltaEtaMax.value, 2.f) < + 1.f; + if (fillQA && !isRejected) { + mQaRegistry.fill(HIST("h2CPRAfter"), deltaEta, deltaPhi); + } + return isRejected; + } + + bool isCloseHadHyperPairAtPV(const HadHyperCandidate& candidate, const HadHyperHadron& hadron, bool fillQA) + { + if (!CPR.settingEnableClosePairRejection.value) { + return false; + } + constexpr int closePairRadiusModePv = 0; + if (CPR.settingClosePairRadiusMode.value != closePairRadiusModePv) { + return false; + } + if (candidate.sign() != hadron.sign()) { + return false; + } + + const float deltaEta = candidate.eta() - hadron.eta(); + const float deltaPhi = wrapDeltaPhi(candidate.phi() - hadron.phi()); + if (fillQA) { + hadHyperRegistry.fill(HIST("hClosePairBeforePV"), deltaEta, deltaPhi); + } + const bool isRejected = std::pow(deltaPhi, 2.f) / std::pow(CPR.settingClosePairDeltaPhiMax.value, 2.f) + + std::pow(deltaEta, 2.f) / std::pow(CPR.settingClosePairDeltaEtaMax.value, 2.f) < + 1.f; + if (fillQA && !isRejected) { + hadHyperRegistry.fill(HIST("hClosePairAfterPV"), deltaEta, deltaPhi); + } + return isRejected; + } + + template + bool selectionPIDProton(const Ttrack& candidate) + { + constexpr float protonPCombMin = 0.75f; + constexpr float protonTPCNsigmaMax = 3.0f; + constexpr float protonCombNsigmaMax = 3.0f; + + const float tpcNSigmaPr = candidate.tpcNSigmaPr(); + mQaRegistry.fill(HIST("h2NsigmaHadTPC_preselection"), candidate.sign() * candidate.tpcInnerParam(), tpcNSigmaPr); + + if (std::abs(candidate.pt()) <= hadronPid.settingProtonPtMin || std::abs(candidate.pt()) >= hadronPid.settingProtonPtMax) { + return false; + } + + const float absPin = std::abs(candidate.tpcInnerParam()); + if (absPin < protonPCombMin) { + if (std::abs(tpcNSigmaPr) > protonTPCNsigmaMax) { + return false; + } + mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaPr); + mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; + } + + if (!candidate.hasTOF()) { + return false; + } + + const float tofNSigmaPr = candidate.tofNSigmaPr(); + const float combNsigma = std::sqrt(tpcNSigmaPr * tpcNSigmaPr + tofNSigmaPr * tofNSigmaPr); + mQaRegistry.fill(HIST("h2NsigmaHadTOF_preselection"), candidate.sign() * candidate.pt(), tofNSigmaPr); + mQaRegistry.fill(HIST("h2NsigmaHadComb_preselection"), candidate.sign() * candidate.pt(), combNsigma); + if (combNsigma > protonCombNsigmaMax) { + return false; + } + if (deuteronPid.settingReqSingleNsig.value && + (std::abs(tpcNSigmaPr) > protonCombNsigmaMax || std::abs(tofNSigmaPr) > protonCombNsigmaMax)) { + return false; + } + + mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaPr); + mQaRegistry.fill(HIST("h2NsigmaHadTOF"), candidate.sign() * candidate.pt(), tofNSigmaPr); + mQaRegistry.fill(HIST("h2NsigmaHadComb"), candidate.sign() * candidate.pt(), combNsigma); + mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; + } + + template + bool selectionPIDKaon(const Ttrack& candidate) + { + auto tpcNSigmaKa = candidate.tpcNSigmaKa(); + float DeDCAxyMin = 0.004 + (0.013 / candidate.pt()); + float DeDCAzMin = 0.004 + (0.013 / candidate.pt()); + if (std::abs(candidate.dcaXY()) > DeDCAxyMin || std::abs(candidate.dcaZ()) > DeDCAzMin) { + return false; + } + + mQaRegistry.fill(HIST("h2NsigmaHadTPC_preselection"), candidate.tpcInnerParam(), tpcNSigmaKa); + if (std::abs(candidate.pt()) < hadronPid.settingHadptMin || std::abs(candidate.pt()) > hadronPid.settingHadptMax) { + return false; + } + + // reject protons and pions + if (std::abs(candidate.tpcNSigmaPr()) < hadronPid.settingCutNsigTPCPrMin || std::abs(candidate.tpcNSigmaPi()) < hadronPid.settingCutNsigTPCPiMin) { + return false; + } + mQaRegistry.fill(HIST("h2NsigmaHadPrTPC"), candidate.tpcNSigmaPr()); + mQaRegistry.fill(HIST("h2NsigmaHadPiTPC"), candidate.tpcNSigmaPi()); + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < hadronPid.settingCutNsigTOFPrMin) { + return false; + } + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < hadronPid.settingCutNsigTOFPiMin) { + return false; + } + mQaRegistry.fill(HIST("h2NsigmaHadPrTOF"), candidate.tofNSigmaPr()); + mQaRegistry.fill(HIST("h2NsigmaHadPiTOF"), candidate.tofNSigmaPi()); + // rejection end + + if (candidate.hasTOF() && candidate.tpcInnerParam() >= hadronPid.settingCutPinMinTOFHad) { + auto tofNSigmaKa = candidate.tofNSigmaKa(); + + mQaRegistry.fill(HIST("h2NsigmaHadTOF_preselection"), candidate.pt(), tofNSigmaKa); + if (std::abs(tofNSigmaKa) > hadronPid.settingCutNsigmaTOFHad) { + return false; + } + if (std::abs(tpcNSigmaKa) > hadronPid.settingCutNsigmaTPCHad) { + return false; + } + mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaKa); + mQaRegistry.fill(HIST("h2NsigmaHadTOF"), candidate.sign() * candidate.pt(), tofNSigmaKa); + mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; + } + if (candidate.tpcInnerParam() < hadronPid.settingCutPinMinTOFHad) { + if (std::abs(tpcNSigmaKa) > hadronPid.settingCutNsigmaTPCHad) { + return false; + } + mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaKa); + mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; + } + return false; + } + + template + bool selectionPIDPion(const Ttrack& candidate) + { + const float tpcNSigmaPi = candidate.tpcNSigmaPi(); + const float absP = std::abs(candidate.p()); + mQaRegistry.fill(HIST("h2NsigmaHadTPC_preselection"), candidate.sign() * candidate.tpcInnerParam(), tpcNSigmaPi); + + if (absP <= hadronPid.settingPionMomCombMin) { + if (std::abs(tpcNSigmaPi) > hadronPid.settingPionTPCNsigMax) { + return false; + } + mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaPi); + mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; + } + + if (!candidate.hasTOF()) { + return false; + } + + const float tofNSigmaPi = candidate.tofNSigmaPi(); + const float combNsigma = std::sqrt(tofNSigmaPi * tofNSigmaPi + tpcNSigmaPi * tpcNSigmaPi); + mQaRegistry.fill(HIST("h2NsigmaHadTOF_preselection"), candidate.sign() * candidate.pt(), tofNSigmaPi); + mQaRegistry.fill(HIST("h2NsigmaHadComb_preselection"), candidate.sign() * candidate.pt(), combNsigma); + if (combNsigma > hadronPid.settingPionCombNsigMax) { + return false; + } + if (deuteronPid.settingReqSingleNsig.value && + (std::abs(tpcNSigmaPi) > hadronPid.settingPionCombNsigMax || std::abs(tofNSigmaPi) > hadronPid.settingPionCombNsigMax)) { + return false; + } + + mQaRegistry.fill(HIST("h2NsigmaHadTPC"), candidate.sign() * candidate.pt(), tpcNSigmaPi); + mQaRegistry.fill(HIST("h2NsigmaHadTOF"), candidate.sign() * candidate.pt(), tofNSigmaPi); + mQaRegistry.fill(HIST("h2NsigmaHadComb"), candidate.sign() * candidate.pt(), combNsigma); + mQaRegistry.fill(HIST("h2dEdxHadcandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; + } + + template + bool selectionPIDHadron(const Ttrack& candidate) + { + bool PID = false; + if (species.settingHadPDGCode == PDG_t::kPiPlus) { + PID = selectionPIDPion(candidate); + mMassHad = o2::constants::physics::MassPiPlus; + } else if (species.settingHadPDGCode == PDG_t::kKPlus) { + PID = selectionPIDKaon(candidate); + mMassHad = o2::constants::physics::MassKPlus; + } else if (species.settingHadPDGCode == PDG_t::kProton) { + PID = selectionPIDProton(candidate); + mMassHad = o2::constants::physics::MassProton; + } else { + LOG(info) << "invalid PDG code"; + } + return PID; + } + + template + float getHadronTPCNSigma(const Ttrack& candidate) const + { + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return candidate.tpcNSigmaPi(); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kKPlus)) { + return candidate.tpcNSigmaKa(); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return candidate.tpcNSigmaPr(); + } + return -10.f; + } + + template + float getHadronTOFNSigma(const Ttrack& candidate) const + { + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return candidate.tofNSigmaPi(); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kKPlus)) { + return candidate.tofNSigmaKa(); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return candidate.tofNSigmaPr(); + } + return -10.f; + } + + template + float getHadronITSNSigma(const Ttrack& candidate) const + { + o2::aod::ITSResponse itsResponse; + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kKPlus)) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + return -10.f; + } + + template + float computeNSigmaDe(const Ttrack& candidate) + { + float expTPCSignal = o2::common::BetheBlochAleph(static_cast(candidate.tpcInnerParam() / constants::physics::MassDeuteron), mBBparamsNucleus[0], mBBparamsNucleus[1], mBBparamsNucleus[2], mBBparamsNucleus[3], mBBparamsNucleus[4]); + double resoTPC{expTPCSignal * mBBparamsNucleus[5]}; + return static_cast((candidate.tpcSignal() - expTPCSignal) / resoTPC); + } + + template + float correctedTPCInnerParamHe3(const Ttrack& candidate) const + { + const bool heliumPID = candidate.pidForTracking() == o2::track::PID::Helium3 || candidate.pidForTracking() == o2::track::PID::Alpha; + return (heliumPID && pidCalibration.settingCompensatePIDinTracking.value) ? candidate.tpcInnerParam() / 2.f : candidate.tpcInnerParam(); + } + + template + float computeNSigmaHe3(const Ttrack& candidate) + { + const float correctedTPCinnerParam = correctedTPCInnerParamHe3(candidate); + float expTPCSignal = o2::common::BetheBlochAleph(static_cast(correctedTPCinnerParam * 2.f / constants::physics::MassHelium3), mBBparamsNucleus[0], mBBparamsNucleus[1], mBBparamsNucleus[2], mBBparamsNucleus[3], mBBparamsNucleus[4]); + double resoTPC{expTPCSignal * mBBparamsNucleus[5]}; + return static_cast((candidate.tpcSignal() - expTPCSignal) / resoTPC); + } + + template + bool selectionPIDDe(const Ttrack& candidate) + { + float tpcInnerParam = candidate.tpcInnerParam(); + mQaRegistry.fill(HIST("h2dEdx"), candidate.sign() * tpcInnerParam, candidate.tpcSignal()); + + if (std::abs(tpcInnerParam) < deuteronPid.settingCutPinMinDe) { + return false; + } + float tpcNSigmaDe = 0.f; + if (output.settingUseBBcomputeDeNsigma) { + tpcNSigmaDe = computeNSigmaDe(candidate); + } else { + tpcNSigmaDe = candidate.tpcNSigmaDe(); + } + + mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselection"), candidate.sign() * candidate.pt(), tpcNSigmaDe); + mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselecComp"), candidate.sign() * candidate.pt(), candidate.tpcNSigmaDe()); + if (std::abs(candidate.pt()) < deuteronPid.settingCutDeptMin || std::abs(candidate.pt()) > deuteronPid.settingCutDeptMax) { + return false; + } + const float absPt = std::abs(candidate.pt()); + if (absPt <= 0.f) { + return false; + } + const float deDCAxyMax = deuteronPid.settingDeDCAxyOffset + deuteronPid.settingDeDCAxyPtCoeff / absPt; + const float deDCAzMax = deuteronPid.settingDeDCAzOffset + deuteronPid.settingDeDCAzPtCoeff / absPt; + if (std::abs(candidate.dcaXY()) > deDCAxyMax || std::abs(candidate.dcaZ()) > deDCAzMax) { + return false; + } + + if (candidate.hasTOF() && candidate.tpcInnerParam() > deuteronPid.settingCutPinMinTOFITSDe) { + auto tofNSigmaDe = candidate.tofNSigmaDe(); + auto combNsigma = std::sqrt(tofNSigmaDe * tofNSigmaDe + tpcNSigmaDe * tpcNSigmaDe); + mQaRegistry.fill(HIST("h2NsigmaNuTOF_preselection"), candidate.sign() * candidate.pt(), tofNSigmaDe); + if (combNsigma > deuteronPid.settingCutNsigmaTOFTPCDe) { + return false; + } + if (deuteronPid.settingReqSingleNsig.value && + (std::abs(tpcNSigmaDe) > deuteronPid.settingCutNsigmaTOFTPCDe || std::abs(tofNSigmaDe) > deuteronPid.settingCutNsigmaTOFTPCDe)) { + return false; + } + mQaRegistry.fill(HIST("h2dEdxNucandidates"), candidate.sign() * tpcInnerParam, candidate.tpcSignal()); + mQaRegistry.fill(HIST("h2NsigmaNuComb"), candidate.sign() * candidate.pt(), combNsigma); + mQaRegistry.fill(HIST("h2NsigmaNuTPC"), candidate.sign() * candidate.pt(), tpcNSigmaDe); + mQaRegistry.fill(HIST("h2NsigmaNuTOF"), candidate.sign() * candidate.pt(), tofNSigmaDe); + return true; + } + if (candidate.tpcInnerParam() <= deuteronPid.settingCutPinMinTOFITSDe) { + if (std::abs(tpcNSigmaDe) > deuteronPid.settingCutNsigmaTPCDe) { + return false; + } + o2::aod::ITSResponse itsResponse; + auto itsnSigmaDe = itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + mQaRegistry.fill(HIST("h2NSigmaNuITS_preselection"), candidate.sign() * candidate.pt(), itsnSigmaDe); + if (std::abs(itsnSigmaDe) > deuteronPid.settingCutNsigmaITSDe) { + return false; + } + mQaRegistry.fill(HIST("h2NsigmaNuTPC"), candidate.sign() * candidate.pt(), tpcNSigmaDe); + mQaRegistry.fill(HIST("h2NSigmaNuITS"), candidate.sign() * candidate.pt(), itsnSigmaDe); + mQaRegistry.fill(HIST("h2dEdxNucandidates"), candidate.sign() * tpcInnerParam, candidate.tpcSignal()); + return true; + } + return false; + } + + template + bool selectionPIDHe3(const Ttrack& candidate) + { + const float correctedTPCinnerParam = correctedTPCInnerParamHe3(candidate); + mQaRegistry.fill(HIST("h2dEdx"), candidate.sign() * correctedTPCinnerParam, candidate.tpcSignal()); + + if (correctedTPCinnerParam < helium3Pid.settingRigidityMinHe3) { + return false; + } + + const float nSigmaHe3 = computeNSigmaHe3(candidate); + mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselection"), candidate.sign() * 2.f * candidate.pt(), nSigmaHe3); + if (std::abs(nSigmaHe3) > helium3Pid.settingTPCNSigmaMaxHe3) { + return false; + } + + o2::aod::ITSResponse itsResponse; + const float itsNsigmaHe3 = itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + mQaRegistry.fill(HIST("h2NSigmaNuITS_preselection"), candidate.sign() * 2.f * candidate.pt(), itsNsigmaHe3); + if (itsNsigmaHe3 < helium3Pid.settingITSNSigmaMinHe3) { + return false; + } + + mQaRegistry.fill(HIST("h2dEdxNucandidates"), candidate.sign() * correctedTPCinnerParam, candidate.tpcSignal()); + mQaRegistry.fill(HIST("h2NsigmaNuTPC"), candidate.sign() * 2.f * candidate.pt(), nSigmaHe3); + mQaRegistry.fill(HIST("h2NSigmaNuITS"), candidate.sign() * 2.f * candidate.pt(), itsNsigmaHe3); + return true; + } + + template + float computeTOFMass(const Ttrack& candidate) const + { + if (!candidate.hasTOF()) { + return -1.f; + } + float beta = o2::pid::tof::Beta::GetBeta(candidate); + beta = std::clamp(beta, 1.e-4f, 1.f - 1.e-6f); + return std::abs(candidate.tpcInnerParam()) * std::sqrt(1.f / (beta * beta) - 1.f); + } + + template + bool selectionPIDTr(const Ttrack& candidate) + { + const float tpcNSigmaTr = candidate.tpcNSigmaTr(); + const float absP = std::abs(candidate.p()); + o2::aod::ITSResponse itsResponse; + const float itsNSigmaTr = itsResponse.nSigmaITS(candidate.itsClusterSizes(), absP, candidate.eta()); + + mQaRegistry.fill(HIST("h2dEdx"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + mQaRegistry.fill(HIST("h2NsigmaNuTPC_preselection"), candidate.sign() * candidate.pt(), tpcNSigmaTr); + mQaRegistry.fill(HIST("h2NSigmaNuITS_preselection"), candidate.sign() * candidate.pt(), itsNSigmaTr); + + if (std::abs(candidate.tpcNSigmaDe()) < tritonPid.settingTPCRejectNsig || + std::abs(candidate.tpcNSigmaPr()) < tritonPid.settingTPCRejectNsig || + std::abs(candidate.tpcNSigmaPi()) < tritonPid.settingTPCRejectNsig) { + return false; + } + + if (absP < tritonPid.settingPIDMomentumSplitTr) { + if (std::abs(tpcNSigmaTr) >= tritonPid.settingCutTPCNsigmaLowPTr || + std::abs(itsNSigmaTr) >= tritonPid.settingCutITSNsigmaLowPTr) { + return false; + } + } else if (tpcNSigmaTr <= tritonPid.settingCutTPCNsigmaHighPMinTr || + tpcNSigmaTr >= tritonPid.settingCutTPCNsigmaHighPMaxTr) { + return false; + } + + if (absP > tritonPid.settingTOFMassMomentumMinTr) { + if (!candidate.hasTOF()) { + return false; + } + const float massTOFTr = computeTOFMass(candidate); + mQaRegistry.fill(HIST("h2MassTOFTr"), candidate.sign() * absP, massTOFTr); + if (massTOFTr <= tritonPid.settingTOFMassMinTr || massTOFTr >= tritonPid.settingTOFMassMaxTr) { + return false; + } + } + + mQaRegistry.fill(HIST("h2dEdxNucandidates"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + mQaRegistry.fill(HIST("h2NsigmaNuTPC"), candidate.sign() * candidate.pt(), tpcNSigmaTr); + mQaRegistry.fill(HIST("h2NSigmaNuITS"), candidate.sign() * candidate.pt(), itsNSigmaTr); + return true; + } + + template + bool selectionPIDNu(const Ttrack& candidate) + { + if (useHelium3Nucleus()) { + return selectionPIDHe3(candidate); + } + if (useTritonNucleus()) { + return selectionPIDTr(candidate); + } + if (useDeuteronNucleus()) { + return selectionPIDDe(candidate); + } + return false; + } + + template + bool selectionPIDDeForDcaFit(const Ttrack& candidate) + { + const float absPt = std::abs(candidate.pt()); + const float absTPCInnerParam = std::abs(candidate.tpcInnerParam()); + if (absTPCInnerParam < deuteronPid.settingCutPinMinDe.value || + absPt < deuteronPid.settingCutDeptMin.value || + absPt > deuteronPid.settingCutDeptMax.value) { + return false; + } + + const float tpcNSigmaDe = output.settingUseBBcomputeDeNsigma.value ? computeNSigmaDe(candidate) : candidate.tpcNSigmaDe(); + if (absTPCInnerParam > deuteronPid.settingCutPinMinTOFITSDe.value) { + if (!candidate.hasTOF()) { + return false; + } + const float tofNSigmaDe = candidate.tofNSigmaDe(); + const float combinedNSigma = std::hypot(tpcNSigmaDe, tofNSigmaDe); + if (combinedNSigma > deuteronPid.settingCutNsigmaTOFTPCDe.value) { + return false; + } + return !deuteronPid.settingReqSingleNsig.value || + (std::abs(tpcNSigmaDe) <= deuteronPid.settingCutNsigmaTOFTPCDe.value && + std::abs(tofNSigmaDe) <= deuteronPid.settingCutNsigmaTOFTPCDe.value); + } + + if (std::abs(tpcNSigmaDe) > deuteronPid.settingCutNsigmaTPCDe.value) { + return false; + } + o2::aod::ITSResponse itsResponse; + const float itsNSigmaDe = itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + return std::abs(itsNSigmaDe) <= deuteronPid.settingCutNsigmaITSDe.value; + } + + template + bool selectionPIDNuForDcaFit(const Ttrack& candidate) + { + if (useDeuteronNucleus()) { + return selectionPIDDeForDcaFit(candidate); + } + // The helium-3 and triton PID predicates do not contain DCA selections. + return selectionPIDNu(candidate); + } + + template + float signedPhysicalPt(const Ttrack& track, bool isNucleus) const + { + const float chargeFactor = isNucleus ? nucleusChargeFactor() : 1.f; + return track.sign() * chargeFactor * std::abs(track.pt()); + } + + template + int classifyDcaOrigin(const Tparticle& particle) const + { + if (particle.isPhysicalPrimary()) { + return Primary; + } + if (particle.getProcess() == TMCProcess::kPDecay) { + return WeakDecay; + } + // Following the DCA-template convention used by Giorgio: after excluding + // physical primaries and decay daughters, every remaining transport + // process is treated as a secondary produced in detector material. + return Material; + } + + int purityOriginCategory(int origin) const + { + switch (origin) { + case Primary: + return CorrectPrimary; + case WeakDecay: + return CorrectWeakDecay; + case Material: + return CorrectMaterial; + default: + // classifyDcaOrigin deliberately has only the three Giorgio classes. + return CorrectMaterial; + } + } + + template + int classifyParentCategory(const Tparticle& particle, int origin) const + { + if (!particle.has_mothers()) { + return origin == WeakDecay ? MissingDecayParent : NoSpecialParent; + } + + const int fallbackCategory = origin == WeakDecay ? OtherDecayParent : NoSpecialParent; + for (const auto& mother : particle.template mothers_as()) { + const int motherPdg = std::abs(mother.pdgCode()); + + // Keep the nuclear feed-down parents identifiable for all origin labels. + if (motherPdg == HyperTritonPDG) { + return HypertritonParent; + } + if (motherPdg == Lithium4PDG) { + return Lithium4Parent; + } + + if (origin != WeakDecay) { + continue; + } + if (motherPdg == PDG_t::kLambda0) { + return LambdaParent; + } + if (motherPdg == PDG_t::kSigmaMinus || motherPdg == PDG_t::kSigma0 || motherPdg == PDG_t::kSigmaPlus) { + return SigmaParent; + } + if (motherPdg == PDG_t::kK0Short) { + return K0ShortParent; + } + if (motherPdg == PDG_t::kK0Long) { + return K0LongParent; + } + if (motherPdg == PDG_t::kKPlus) { + return ChargedKaonParent; + } + } + return fallbackCategory; + } + + template + float getNucleusTPCNSigma(const Ttrack& candidate) + { + if (useHelium3Nucleus()) { + return computeNSigmaHe3(candidate); + } + if (useTritonNucleus()) { + return candidate.tpcNSigmaTr(); + } + return computeNSigmaDe(candidate); + } + + template + float getNucleusTOFNSigma(const Ttrack& candidate) const + { + if (useHelium3Nucleus()) { + return candidate.tofNSigmaHe(); + } + if (useTritonNucleus()) { + return candidate.tofNSigmaTr(); + } + if (useDeuteronNucleus()) { + return candidate.tofNSigmaDe(); + } + return -10.f; + } + + template + float getNucleusITSNSigma(const Ttrack& candidate) const + { + o2::aod::ITSResponse itsResponse; + if (useHelium3Nucleus()) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (useTritonNucleus()) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + if (useDeuteronNucleus()) { + return itsResponse.nSigmaITS(candidate.itsClusterSizes(), candidate.p(), candidate.eta()); + } + return -10.f; + } + + template + float computeHyperCandidateMass(const Tcandidate& candidate) const + { + const std::array heMomentum{ + candidate.ptHe3() * std::cos(candidate.phiHe3()), + candidate.ptHe3() * std::sin(candidate.phiHe3()), + candidate.ptHe3() * std::sinh(candidate.etaHe3())}; + const std::array pionMomentum{ + candidate.ptPi() * std::cos(candidate.phiPi()), + candidate.ptPi() * std::sin(candidate.phiPi()), + candidate.ptPi() * std::sinh(candidate.etaPi())}; + return RecoDecay::m(std::array{heMomentum, pionMomentum}, + std::array{static_cast(o2::constants::physics::MassHelium3), + static_cast(o2::constants::physics::MassPiPlus)}); + } + + template + bool selectHyperCandidate(const Tcandidate& candidate) + { + mQaRegistry.fill(HIST("hHe3P_preselected"), candidate.tpcMomHe()); + const float mass = computeHyperCandidateMass(candidate); + if (!std::isfinite(mass) || mass < hypertriton.settingHypMassMin || mass > hypertriton.settingHypMassMax) { + return false; + } + mQaRegistry.fill(HIST("hHe3P"), candidate.tpcMomHe()); + mQaRegistry.fill(HIST("hHe3TPCnsigma"), candidate.ptHe3(), candidate.nSigmaHe()); + + return true; + } + + float computePairKstar(const std::array& momHad, const float massHad, const std::array& momNu, const float massNu) const + { + const PairLorentzVector vecHad(momHad[0], momHad[1], momHad[2], massHad); + const PairLorentzVector vecNu(momNu[0], momNu[1], momNu[2], massNu); + const PairLorentzVector trackSum = vecHad + vecNu; + + const float beta = trackSum.Beta(); + const float betax = beta * std::cos(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betay = beta * std::sin(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betaz = beta * std::cos(trackSum.Theta()); + + PairLorentzVector partHadCMS(vecHad); + PairLorentzVector partNuCMS(vecNu); + + const ROOT::Math::Boost boostPRF = ROOT::Math::Boost(-betax, -betay, -betaz); + partHadCMS = boostPRF(partHadCMS); + partNuCMS = boostPRF(partNuCMS); + + const PairLorentzVector trackRelK = partHadCMS - partNuCMS; + return 0.5f * trackRelK.P(); + } + + float computePairMT(const std::array& momHad, const float massHad, const std::array& momNu, const float massNu) const + { + const PairLorentzVector vecHad(momHad[0], momHad[1], momHad[2], massHad); + const PairLorentzVector vecNu(momNu[0], momNu[1], momNu[2], massNu); + const PairLorentzVector trackSum = vecHad + vecNu; + const float kT = 0.5f * trackSum.Pt(); + return std::sqrt(kT * kT + std::pow(0.5f * (massHad + massNu), 2.f)); + } + + // ================================================================================================================== + + template + bool fillCandidateInfo(const Ttrack& trackDe, const Ttrack& trackHad, const CollBracket& collBracket, const Tcollisions& collisions, HadNucandidate& hadNucand, const Ttracks& /*trackTable*/, bool isMixedEvent) + { + const int numCoordinates = 3; if (!isMixedEvent) { auto trackCovDe = getTrackParCov(trackDe); auto trackCovHad = getTrackParCov(trackHad); @@ -743,6 +2232,7 @@ struct HadNucleiFemto { mQaRegistry.fill(HIST("hSkipReasons"), 1); return false; } + hadNucand.dcaPair = std::sqrt(std::abs(mFitter.getChi2AtPCACandidate())); // associate collision id as the one that minimises the distance between the vertex and the PCAs of the daughters double distanceMin = -1; @@ -771,421 +2261,1063 @@ struct HadNucleiFemto { hadNucand.collisionID = collBracket.getMin(); } + const float nuChargeFactor = nucleusChargeFactor(); hadNucand.momNu = std::array{trackDe.px(), trackDe.py(), trackDe.pz()}; + for (auto i = 0u; i < hadNucand.momNu.size(); ++i) { + hadNucand.momNu[i] *= nuChargeFactor; + } hadNucand.momHad = std::array{trackHad.px(), trackHad.py(), trackHad.pz()}; float invMass = 0; - invMass = RecoDecay::m(std::array, 2>{hadNucand.momNu, hadNucand.momHad}, std::array{static_cast(o2::constants::physics::MassDeuteron), MassHad}); + invMass = RecoDecay::m(std::array, 2>{hadNucand.momNu, hadNucand.momHad}, std::array{nucleusMass(), mMassHad}); + + hadNucand.signNu = trackDe.sign(); + hadNucand.signHad = trackHad.sign(); + computeClosePairDeltas(trackDe, trackHad, hadNucand.deltaEta, hadNucand.deltaPhi); + + hadNucand.dcaxyNu = trackDe.dcaXY(); + hadNucand.dcaxyHad = trackHad.dcaXY(); + + hadNucand.dcazNu = trackDe.dcaZ(); + hadNucand.dcazHad = trackHad.dcaZ(); + + hadNucand.tpcSignalNu = trackDe.tpcSignal(); + hadNucand.momNuTPC = useHelium3Nucleus() ? correctedTPCInnerParamHe3(trackDe) : trackDe.tpcInnerParam(); + hadNucand.tpcSignalHad = trackHad.tpcSignal(); + hadNucand.momHadTPC = trackHad.tpcInnerParam(); + + hadNucand.nTPCClustersNu = trackDe.tpcNClsFound(); + hadNucand.nTPCClustersHad = trackHad.tpcNClsFound(); + hadNucand.nTPCCrossedRowsNu = trackDe.tpcNClsCrossedRows(); + hadNucand.nTPCCrossedRowsHad = trackHad.tpcNClsCrossedRows(); + hadNucand.nTPCFindableNu = trackDe.tpcNClsFindable(); + hadNucand.nTPCFindableHad = trackHad.tpcNClsFindable(); + hadNucand.nSigmaNu = getNucleusTPCNSigma(trackDe); + hadNucand.nSigmaTPCNuDe = trackDe.tpcNSigmaDe(); + hadNucand.nSigmaTPCNuPr = trackDe.tpcNSigmaPr(); + hadNucand.nSigmaTPCNuPi = trackDe.tpcNSigmaPi(); + hadNucand.nSigmaHad = getHadronTPCNSigma(trackHad); + hadNucand.nSigmaTOFNu = getNucleusTOFNSigma(trackDe); + hadNucand.nSigmaITSNu = getNucleusITSNSigma(trackDe); + hadNucand.nSigmaTOFHad = getHadronTOFNSigma(trackHad); + hadNucand.nSigmaITSHad = getHadronITSNSigma(trackHad); + hadNucand.nSigmaTPCHadPi = trackHad.tpcNSigmaPi(); + hadNucand.nSigmaTPCHadKa = trackHad.tpcNSigmaKa(); + hadNucand.nSigmaTPCHadPr = trackHad.tpcNSigmaPr(); + hadNucand.nSigmaTOFHadPi = trackHad.tofNSigmaPi(); + hadNucand.nSigmaTOFHadKa = trackHad.tofNSigmaKa(); + hadNucand.nSigmaTOFHadPr = trackHad.tofNSigmaPr(); + + hadNucand.chi2TPCNu = trackDe.tpcChi2NCl(); + hadNucand.chi2TPCHad = trackHad.tpcChi2NCl(); + hadNucand.chi2ITSNu = trackDe.itsChi2NCl(); + hadNucand.chi2ITSHad = trackHad.itsChi2NCl(); + + hadNucand.pidTrkNu = trackDe.pidForTracking(); + hadNucand.pidTrkHad = trackHad.pidForTracking(); + + hadNucand.itsClSizeNu = trackDe.itsClusterSizes(); + hadNucand.itsClSizeHad = trackHad.itsClusterSizes(); + + hadNucand.nClsItsNu = trackDe.itsNCls(); + hadNucand.nClsItsHad = trackHad.itsNCls(); + hadNucand.nClsItsInnerBarrelNu = trackDe.itsNClsInnerBarrel(); + hadNucand.nClsItsInnerBarrelHad = trackHad.itsNClsInnerBarrel(); + + hadNucand.sharedClustersNu = trackDe.tpcNClsShared(); + hadNucand.sharedClustersHad = trackHad.tpcNClsShared(); + hadNucand.fractionSharedTPCNu = trackDe.tpcFractionSharedCls(); + hadNucand.fractionSharedTPCHad = trackHad.tpcFractionSharedCls(); + + hadNucand.isBkgUS = trackDe.sign() * trackHad.sign() < 0; + hadNucand.isBkgEM = isMixedEvent; + + hadNucand.invMass = invMass; + + hadNucand.trackIDNu = trackDe.globalIndex(); + hadNucand.trackIDHad = trackHad.globalIndex(); + hadNucand.collisionIDNu = trackDe.collisionId(); + hadNucand.collisionIDHad = trackHad.collisionId(); + + if (trackDe.hasTOF()) { + float beta = o2::pid::tof::Beta::GetBeta(trackDe); + beta = std::min(1.f - 1.e-6f, std::max(1.e-4f, beta)); /// sometimes beta > 1 or < 0, to be checked + hadNucand.massTOFNu = hadNucand.momNuTPC * nuChargeFactor * std::sqrt(1.f / (beta * beta) - 1.f); + } + if (trackHad.hasTOF()) { + float beta = o2::pid::tof::Beta::GetBeta(trackHad); + beta = std::min(1.f - 1.e-6f, std::max(1.e-4f, beta)); /// sometimes beta > 1 or < 0, to be checked + hadNucand.massTOFHad = trackHad.tpcInnerParam() * std::sqrt(1.f / (beta * beta) - 1.f); + } + + float massLightNucleusForKstarMt = nucleusMass(); + if (useDeuteronNucleus() && deuteronPid.settingUseProtonMassForKstarMt) { + massLightNucleusForKstarMt = static_cast(o2::constants::physics::MassProton); + } + hadNucand.kstar = computePairKstar(hadNucand.momHad, mMassHad, hadNucand.momNu, massLightNucleusForKstarMt); + hadNucand.mT = computePairMT(hadNucand.momHad, mMassHad, hadNucand.momNu, massLightNucleusForKstarMt); + + return true; + } + + template + void pairTracksSameEvent(const Ttrack& tracks, float /*cent*/) + { + // LOG(info) << "Number of tracks: " << tracks.size(); + for (const auto& track0 : tracks) { + + mQaRegistry.fill(HIST("hTrackSel"), Selections::kNoCuts); + fillNucleusTrackSelection(Selections::kNoCuts); + + if (!selectTrackNu(track0)) { + continue; + } + mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); + fillNucleusTrackSelection(Selections::kTrackCuts); + + if (!selectionPIDNu(track0)) { + continue; + } + mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); + fillNucleusTrackSelection(Selections::kPID); + mQaRegistry.fill(HIST("hSingleNuPt"), track0.pt() * track0.sign() * nucleusChargeFactor()); + mQaRegistry.fill(HIST("hSingleNuPin"), (useHelium3Nucleus() ? correctedTPCInnerParamHe3(track0) : track0.tpcInnerParam()) * track0.sign()); + fillNucleusPairFlow(0); + + bool hasHadronSelected = false; + bool hasStoredPair = false; + + for (const auto& track1 : tracks) { + if (track0 == track1) { + continue; + } + + if (!eventMixing.settingSaveUSandLS) { + if (!eventMixing.settingEnableBkgUS && (track0.sign() * track1.sign() < 0)) { + continue; + } + if (eventMixing.settingEnableBkgUS && (track0.sign() * track1.sign() > 0)) { + continue; + } + } + + if (!selectTrackHadron(track1) || !selectionPIDHadron(track1)) { + continue; + } + hasHadronSelected = true; + if (isClosePair(track0, track1, /*fillQA*/ true)) { + continue; + } + + SVCand trackPair; + trackPair.tr0Idx = track0.globalIndex(); + trackPair.tr1Idx = track1.globalIndex(); + const int collIdx = track0.collisionId(); + CollBracket collBracket{collIdx, collIdx}; + trackPair.collBracket = collBracket; + mTrackPairs.push_back(trackPair); + hasStoredPair = true; + } + + if (hasHadronSelected) { + fillNucleusPairFlow(1); + } + if (hasStoredPair) { + fillNucleusPairFlow(2); + } + } + } + + template + BufferedTrack makeBufferedTrack(const Ttrack& track, bool isNucleus, int64_t collisionId) + { + BufferedTrack bufferedTrack; + bufferedTrack.momentum = {track.px(), track.py(), track.pz()}; + bufferedTrack.ptValue = track.pt(); + bufferedTrack.etaValue = track.eta(); + bufferedTrack.phiValue = track.phi(); + bufferedTrack.signValue = track.sign(); + bufferedTrack.dcaXY = track.dcaXY(); + bufferedTrack.dcaZ = track.dcaZ(); + bufferedTrack.tpcSignal = track.tpcSignal(); + bufferedTrack.tpcInnerParam = isNucleus && useHelium3Nucleus() ? correctedTPCInnerParamHe3(track) : track.tpcInnerParam(); + bufferedTrack.tpcNClsFound = track.tpcNClsFound(); + bufferedTrack.tpcNClsCrossedRows = track.tpcNClsCrossedRows(); + bufferedTrack.tpcNClsFindable = track.tpcNClsFindable(); + bufferedTrack.tpcNClsShared = track.tpcNClsShared(); + bufferedTrack.tpcFractionSharedCls = track.tpcFractionSharedCls(); + bufferedTrack.itsNCls = track.itsNCls(); + bufferedTrack.itsNClsInnerBarrel = track.itsNClsInnerBarrel(); + bufferedTrack.tpcChi2NCl = track.tpcChi2NCl(); + bufferedTrack.itsChi2NCl = track.itsChi2NCl(); + bufferedTrack.pidForTracking = track.pidForTracking(); + bufferedTrack.itsClusterSizes = track.itsClusterSizes(); + bufferedTrack.trackId = track.globalIndex(); + bufferedTrack.collisionId = collisionId; + + if (isNucleus) { + bufferedTrack.nSigmaTPC = getNucleusTPCNSigma(track); + bufferedTrack.nSigmaTPCDe = track.tpcNSigmaDe(); + bufferedTrack.nSigmaTPCPr = track.tpcNSigmaPr(); + bufferedTrack.nSigmaTPCPi = track.tpcNSigmaPi(); + bufferedTrack.nSigmaTOF = getNucleusTOFNSigma(track); + bufferedTrack.nSigmaITS = getNucleusITSNSigma(track); + } else { + bufferedTrack.nSigmaTPC = getHadronTPCNSigma(track); + bufferedTrack.nSigmaTOF = getHadronTOFNSigma(track); + bufferedTrack.nSigmaITS = getHadronITSNSigma(track); + bufferedTrack.nSigmaTPCHadPi = track.tpcNSigmaPi(); + bufferedTrack.nSigmaTPCHadKa = track.tpcNSigmaKa(); + bufferedTrack.nSigmaTPCHadPr = track.tpcNSigmaPr(); + bufferedTrack.nSigmaTOFHadPi = track.tofNSigmaPi(); + bufferedTrack.nSigmaTOFHadKa = track.tofNSigmaKa(); + bufferedTrack.nSigmaTOFHadPr = track.tofNSigmaPr(); + } + + if (track.hasTOF()) { + float beta = o2::pid::tof::Beta::GetBeta(track); + beta = std::clamp(beta, 1.e-4f, 1.f - 1.e-6f); + const float momentumForMass = isNucleus ? bufferedTrack.tpcInnerParam * nucleusChargeFactor() : bufferedTrack.tpcInnerParam; + bufferedTrack.massTOF = momentumForMass * std::sqrt(1.f / (beta * beta) - 1.f); + } + return bufferedTrack; + } + + float configuredHadronMass() const + { + if (species.settingHadPDGCode.value == static_cast(PDG_t::kPiPlus)) { + return static_cast(o2::constants::physics::MassPiPlus); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kKPlus)) { + return static_cast(o2::constants::physics::MassKPlus); + } + if (species.settingHadPDGCode.value == static_cast(PDG_t::kProton)) { + return static_cast(o2::constants::physics::MassProton); + } + return 0.f; + } + + void fillBufferedCandidateInfo(const BufferedTrack& nucleus, const BufferedTrack& hadron, HadNucandidate& hadNucand) + { + const float nuChargeFactor = nucleusChargeFactor(); + hadNucand.momNu = nucleus.momentum; + for (size_t iComponent = 0; iComponent < hadNucand.momNu.size(); ++iComponent) { + hadNucand.momNu[iComponent] *= nuChargeFactor; + } + hadNucand.momHad = hadron.momentum; + + const float massHadron = configuredHadronMass(); + hadNucand.invMass = RecoDecay::m(std::array, 2>{hadNucand.momNu, hadNucand.momHad}, std::array{nucleusMass(), massHadron}); + hadNucand.signNu = nucleus.sign(); + hadNucand.signHad = hadron.sign(); + computeClosePairDeltas(nucleus, hadron, hadNucand.deltaEta, hadNucand.deltaPhi); + hadNucand.dcaxyNu = nucleus.dcaXY; + hadNucand.dcazNu = nucleus.dcaZ; + hadNucand.dcaxyHad = hadron.dcaXY; + hadNucand.dcazHad = hadron.dcaZ; + hadNucand.tpcSignalNu = nucleus.tpcSignal; + hadNucand.tpcSignalHad = hadron.tpcSignal; + hadNucand.momNuTPC = nucleus.tpcInnerParam; + hadNucand.momHadTPC = hadron.tpcInnerParam; + hadNucand.nTPCClustersNu = nucleus.tpcNClsFound; + hadNucand.nTPCClustersHad = hadron.tpcNClsFound; + hadNucand.nTPCCrossedRowsNu = nucleus.tpcNClsCrossedRows; + hadNucand.nTPCCrossedRowsHad = hadron.tpcNClsCrossedRows; + hadNucand.nTPCFindableNu = nucleus.tpcNClsFindable; + hadNucand.nTPCFindableHad = hadron.tpcNClsFindable; + hadNucand.sharedClustersNu = nucleus.tpcNClsShared; + hadNucand.sharedClustersHad = hadron.tpcNClsShared; + hadNucand.fractionSharedTPCNu = nucleus.tpcFractionSharedCls; + hadNucand.fractionSharedTPCHad = hadron.tpcFractionSharedCls; + hadNucand.nClsItsNu = nucleus.itsNCls; + hadNucand.nClsItsHad = hadron.itsNCls; + hadNucand.nClsItsInnerBarrelNu = nucleus.itsNClsInnerBarrel; + hadNucand.nClsItsInnerBarrelHad = hadron.itsNClsInnerBarrel; + hadNucand.chi2TPCNu = nucleus.tpcChi2NCl; + hadNucand.chi2TPCHad = hadron.tpcChi2NCl; + hadNucand.chi2ITSNu = nucleus.itsChi2NCl; + hadNucand.chi2ITSHad = hadron.itsChi2NCl; + hadNucand.nSigmaNu = nucleus.nSigmaTPC; + hadNucand.nSigmaTPCNuDe = nucleus.nSigmaTPCDe; + hadNucand.nSigmaTPCNuPr = nucleus.nSigmaTPCPr; + hadNucand.nSigmaTPCNuPi = nucleus.nSigmaTPCPi; + hadNucand.nSigmaHad = hadron.nSigmaTPC; + hadNucand.nSigmaTOFNu = nucleus.nSigmaTOF; + hadNucand.nSigmaITSNu = nucleus.nSigmaITS; + hadNucand.nSigmaTOFHad = hadron.nSigmaTOF; + hadNucand.nSigmaITSHad = hadron.nSigmaITS; + hadNucand.nSigmaTPCHadPi = hadron.nSigmaTPCHadPi; + hadNucand.nSigmaTPCHadKa = hadron.nSigmaTPCHadKa; + hadNucand.nSigmaTPCHadPr = hadron.nSigmaTPCHadPr; + hadNucand.nSigmaTOFHadPi = hadron.nSigmaTOFHadPi; + hadNucand.nSigmaTOFHadKa = hadron.nSigmaTOFHadKa; + hadNucand.nSigmaTOFHadPr = hadron.nSigmaTOFHadPr; + hadNucand.massTOFNu = nucleus.massTOF; + hadNucand.massTOFHad = hadron.massTOF; + hadNucand.pidTrkNu = nucleus.pidForTracking; + hadNucand.pidTrkHad = hadron.pidForTracking; + hadNucand.itsClSizeNu = nucleus.itsClusterSizes; + hadNucand.itsClSizeHad = hadron.itsClusterSizes; + hadNucand.trackIDNu = nucleus.trackId; + hadNucand.trackIDHad = hadron.trackId; + hadNucand.collisionIDNu = nucleus.collisionId; + hadNucand.collisionIDHad = hadron.collisionId; + hadNucand.isBkgUS = nucleus.sign() * hadron.sign() < 0; + hadNucand.isBkgEM = true; + + float massLightNucleusForKstarMt = nucleusMass(); + if (useDeuteronNucleus() && deuteronPid.settingUseProtonMassForKstarMt) { + massLightNucleusForKstarMt = static_cast(o2::constants::physics::MassProton); + } + hadNucand.kstar = computePairKstar(hadNucand.momHad, massHadron, hadNucand.momNu, massLightNucleusForKstarMt); + hadNucand.mT = computePairMT(hadNucand.momHad, massHadron, hadNucand.momNu, massLightNucleusForKstarMt); + } + + void fillDataTable(const HadNucandidate& hadNucand) + { + mOutputDataTable( + hadNucand.recoPtNu(), + hadNucand.recoEtaNu(), + hadNucand.recoPhiNu(), + hadNucand.recoPtHad(), + hadNucand.recoEtaHad(), + hadNucand.recoPhiHad(), + hadNucand.dcaxyNu, + hadNucand.dcazNu, + hadNucand.dcaxyHad, + hadNucand.dcazHad, + hadNucand.dcaPair, + hadNucand.tpcSignalNu, + hadNucand.momNuTPC, + hadNucand.tpcSignalHad, + hadNucand.momHadTPC, + hadNucand.nTPCClustersNu, + hadNucand.nTPCClustersHad, + hadNucand.nTPCCrossedRowsNu, + hadNucand.nTPCCrossedRowsHad, + hadNucand.nSigmaNu, + hadNucand.nSigmaTPCHadPi, + hadNucand.nSigmaTPCHadKa, + hadNucand.nSigmaTPCHadPr, + hadNucand.nSigmaTOFHadPi, + hadNucand.nSigmaTOFHadKa, + hadNucand.nSigmaTOFHadPr, + hadNucand.chi2TPCNu, + hadNucand.chi2TPCHad, + hadNucand.massTOFNu, + hadNucand.massTOFHad, + hadNucand.pidTrkNu, + hadNucand.pidTrkHad, + hadNucand.itsClSizeNu, + hadNucand.itsClSizeHad, + hadNucand.sharedClustersNu, + hadNucand.sharedClustersHad, + hadNucand.deltaEta, + hadNucand.deltaPhi, + hadNucand.nSigmaHad, + hadNucand.nSigmaTOFNu, + hadNucand.nSigmaITSNu, + hadNucand.nSigmaTOFHad, + hadNucand.nSigmaITSHad, + mRunNumber, + mDbz, + hadNucand.trackIDNu, + hadNucand.trackIDHad, + hadNucand.collisionIDNu, + hadNucand.collisionIDHad, + hadNucand.nTPCFindableNu, + hadNucand.nTPCFindableHad, + hadNucand.fractionSharedTPCNu, + hadNucand.fractionSharedTPCHad, + hadNucand.nClsItsNu, + hadNucand.nClsItsHad, + hadNucand.nClsItsInnerBarrelNu, + hadNucand.nClsItsInnerBarrelHad, + hadNucand.chi2ITSNu, + hadNucand.chi2ITSHad, + hadNucand.nSigmaTPCNuDe, + hadNucand.nSigmaTPCNuPr, + hadNucand.nSigmaTPCNuPi); + } + + template + void fillTable(const HadNucandidate& hadNucand, const Tcoll& collision) + { + fillDataTable(hadNucand); + if (output.settingFillMultiplicity) { + mOutputMultiplicityTable( + collision.globalIndex(), + collision.posZ(), + collision.numContrib(), + collision.centFT0C(), + collision.multFT0C(), + collision.multNTracksPV()); + } + } + + void fillTable(const HadNucandidate& hadNucand, const BufferedCollision& collision) + { + fillDataTable(hadNucand); + if (output.settingFillMultiplicity) { + mOutputMultiplicityTable( + collision.eventId, + collision.posZ, + collision.numContrib, + collision.centFT0C, + collision.multFT0C, + collision.multNTracksPV); + } + } + + template + void fillMCTable(const HadNucandidate& hadNucand, const TparticleNu& particleNu, const TparticleHad& particleHad, bool sameMCCollision, bool matchesRecoMCCollision) + { + const float signedPtNuMC = particleNu.pdgCode() >= 0 ? particleNu.pt() : -particleNu.pt(); + const float signedPtHadMC = particleHad.pdgCode() >= 0 ? particleHad.pt() : -particleHad.pt(); + const std::array momentumNuMC{particleNu.px(), particleNu.py(), particleNu.pz()}; + const std::array momentumHadMC{particleHad.px(), particleHad.py(), particleHad.pz()}; + const float kstarMC = computePairKstar(momentumHadMC, mMassHad, momentumNuMC, nucleusMass()); + + mOutputMCTable( + signedPtNuMC, + particleNu.eta(), + particleNu.phi(), + signedPtHadMC, + particleHad.eta(), + particleHad.phi(), + kstarMC, + particleNu.pdgCode(), + particleHad.pdgCode(), + particleNu.isPhysicalPrimary(), + particleHad.isPhysicalPrimary(), + sameMCCollision, + matchesRecoMCCollision); + + mQaRegistry.fill(HIST("MC/hKstarRecVsGen"), kstarMC, hadNucand.kstar); + mQaRegistry.fill(HIST("MC/hPtNuRecVsGen"), signedPtNuMC, hadNucand.recoPtNu()); + mQaRegistry.fill(HIST("MC/hPtHadRecVsGen"), signedPtHadMC, hadNucand.recoPtHad()); + } + + void fillHistograms(const HadNucandidate& hadNucand) + { + mQaRegistry.fill(HIST("hNuPt"), hadNucand.recoPtNu()); + mQaRegistry.fill(HIST("hHadPt"), hadNucand.recoPtHad()); + mQaRegistry.fill(HIST("hNuPin"), hadNucand.momNuTPC * hadNucand.signNu); + mQaRegistry.fill(HIST("hHadPin"), hadNucand.momHadTPC * hadNucand.signHad); + mQaRegistry.fill(HIST("hNuEta"), hadNucand.recoEtaNu()); + mQaRegistry.fill(HIST("hHadEta"), hadNucand.recoEtaHad()); + mQaRegistry.fill(HIST("hNuPhi"), hadNucand.recoPhiNu()); + mQaRegistry.fill(HIST("hHadPhi"), hadNucand.recoPhiHad()); + mQaRegistry.fill(HIST("hNuHadtInvMass"), hadNucand.invMass); + mQaRegistry.fill(HIST("hdcaxyNu"), hadNucand.dcaxyNu); + mQaRegistry.fill(HIST("hdcazNu"), hadNucand.dcazNu); + mQaRegistry.fill(HIST("hdcaxyHad"), hadNucand.dcaxyHad); + mQaRegistry.fill(HIST("hdcazHad"), hadNucand.dcazHad); + mQaRegistry.fill(HIST("hNClsNuITS"), hadNucand.nClsItsNu); + mQaRegistry.fill(HIST("hNClsHadITS"), hadNucand.nClsItsHad); + mQaRegistry.fill(HIST("hisBkgEM"), hadNucand.isBkgEM); + } + + void fillKstarAtCentrality(const HadNucandidate& hadNucand, float centrality) + { + if (!hadNucand.isBkgUS) { + if (hadNucand.recoPtNu() > 0) { + mQaRegistry.fill(HIST("hkStar_LS_M"), hadNucand.kstar); + mQaRegistry.fill(HIST("hkStaVsmT_LS_M"), hadNucand.kstar, hadNucand.mT); + mQaRegistry.fill(HIST("hkStaVsCent_LS_M"), hadNucand.kstar, centrality); + } else { + mQaRegistry.fill(HIST("hkStar_LS_A"), hadNucand.kstar); + mQaRegistry.fill(HIST("hkStaVsmT_LS_A"), hadNucand.kstar, hadNucand.mT); + mQaRegistry.fill(HIST("hkStaVsCent_LS_A"), hadNucand.kstar, centrality); + } + } else { + if (hadNucand.recoPtNu() > 0) { + mQaRegistry.fill(HIST("hkStar_US_M"), hadNucand.kstar); + mQaRegistry.fill(HIST("hkStaVsmT_US_M"), hadNucand.kstar, hadNucand.mT); + mQaRegistry.fill(HIST("hkStaVsCent_US_M"), hadNucand.kstar, centrality); + } else { + mQaRegistry.fill(HIST("hkStar_US_A"), hadNucand.kstar); + mQaRegistry.fill(HIST("hkStaVsmT_US_A"), hadNucand.kstar, hadNucand.mT); + mQaRegistry.fill(HIST("hkStaVsCent_US_A"), hadNucand.kstar, centrality); + } + } + } + + template + void fillKstar(const HadNucandidate& hadNucand, const Tcoll& collision) + { + fillKstarAtCentrality(hadNucand, collision.centFT0C()); + } + + void fillMixedPair(const BufferedTrack& nucleus, const BufferedTrack& hadron, const BufferedCollision& nucleusCollision) + { + if (isClosePair(nucleus, hadron, /*fillQA*/ true)) { + return; + } + + HadNucandidate hadNucand; + fillBufferedCandidateInfo(nucleus, hadron, hadNucand); + fillKstarAtCentrality(hadNucand, nucleusCollision.centFT0C); + fillHistograms(hadNucand); + + if (output.settingFillTable && shouldFillOutputTable(hadNucand)) { + fillTable(hadNucand, nucleusCollision); + } + } + + // ================================================================================================================== + + template + void fillPairs(const Tcollisions& collisions, const Ttracks& tracks, const bool isMixedEvent) + { + for (const auto& trackPair : mTrackPairs) { - hadNucand.signNu = trackDe.sign(); - hadNucand.signHad = trackHad.sign(); + auto deTrack = tracks.rawIteratorAt(trackPair.tr0Idx); + auto hadTrack = tracks.rawIteratorAt(trackPair.tr1Idx); + auto collBracket = trackPair.collBracket; - hadNucand.dcaxyNu = trackDe.dcaXY(); - hadNucand.dcaxyHad = trackHad.dcaXY(); + HadNucandidate hadNucand; + if (!fillCandidateInfo(deTrack, hadTrack, collBracket, collisions, hadNucand, tracks, isMixedEvent)) { + continue; + } - hadNucand.dcazNu = trackDe.dcaZ(); - hadNucand.dcazHad = trackHad.dcaZ(); + auto collision = collisions.rawIteratorAt(hadNucand.collisionID); + fillKstar(hadNucand, collision); + fillHistograms(hadNucand); - hadNucand.tpcSignalNu = trackDe.tpcSignal(); - hadNucand.momNuTPC = trackDe.tpcInnerParam(); - hadNucand.tpcSignalHad = trackHad.tpcSignal(); - hadNucand.momHadTPC = trackHad.tpcInnerParam(); + if (output.settingFillTable && shouldFillOutputTable(hadNucand)) { + fillTable(hadNucand, collision); + } + } + } - hadNucand.nTPCClustersNu = trackDe.tpcNClsFound(); - hadNucand.nSigmaNu = computeNSigmaDe(trackDe); - // hadNucand.nSigmaHad = trackHad.tpcNSigmaPi(); + template + HadHyperTrackInfo makeHadHyperTrackInfo(const Ttrack& track) + { + constexpr float InvalidPID = -999.f; + return {track.pt(), track.eta(), track.phi(), static_cast(track.sign()), + track.dcaXY(), track.tpcNClsCrossedRows(), track.tpcNClsPID(), track.tpcChi2NCl(), + track.itsClusterSizes(), track.itsChi2NCl(), track.hasTOF(), + track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), + track.hasTOF() ? track.tofNSigmaPi() : InvalidPID, + track.hasTOF() ? track.tofNSigmaKa() : InvalidPID, + track.hasTOF() ? track.tofNSigmaPr() : InvalidPID}; + } - hadNucand.chi2TPCNu = trackDe.tpcChi2NCl(); - hadNucand.chi2TPCHad = trackHad.tpcChi2NCl(); + template + HadHyperEventInfo makeHadHyperEventInfo(const Tcollision& collision) + { + const auto bc = collision.template bc_as(); + return {bc.runNumber(), collision.posZ(), collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), + static_cast(collision.numContrib()), collision.multFT0C(), + collision.posX(), collision.posY(), collision.posZ()}; + } - hadNucand.pidTrkNu = trackDe.pidForTracking(); - hadNucand.pidTrkHad = trackHad.pidForTracking(); + template + HadHyperParticleTruth makeHadHyperParticleTruth(const Tparticle& particle) + { + return {particle.globalIndex(), particle.mcCollisionId(), particle.pdgCode(), particle.pt(), particle.eta(), particle.phi(), particle.isPhysicalPrimary(), static_cast(particle.statusCode()), static_cast(particle.getProcess()), {particle.px(), particle.py(), particle.pz()}}; + } - hadNucand.itsClSizeNu = trackDe.itsClusterSizes(); - hadNucand.itsClSizeHad = trackHad.itsClusterSizes(); + template + HadHyperHadron makeHadHyperHadron(const Ttrack& track) + { + HadHyperHadron hadron{makeHadHyperTrackInfo(track), {}, {}, track.globalIndex(), {track.px(), track.py(), track.pz()}, track.tpcInnerParam(), track.tpcSignal(), track.eta(), track.phi(), static_cast(track.sign())}; + if constexpr (isMC) { + if (track.has_mcParticle()) { + const auto particle = track.template mcParticle_as(); + hadron.truth = makeHadHyperParticleTruth(particle); + if (particle.has_mothers()) { + for (const auto& mother : particle.template mothers_as()) { + hadron.motherIds.push_back(mother.globalIndex()); + } + } + } + } + return hadron; + } - hadNucand.nClsItsNu = trackDe.itsNCls(); - hadNucand.nClsItsHad = trackHad.itsNCls(); + template + HadHyperCandidate makeHadHyperCandidate(const Tcandidate& candidate, const Ttracks& tracks) + { + const auto he = tracks.rawIteratorAt(candidate.heTrackId() - tracks.offset()); + const auto pion = tracks.rawIteratorAt(candidate.piTrackId() - tracks.offset()); + const std::array heMomentum{ + candidate.ptHe3() * std::cos(candidate.phiHe3()), + candidate.ptHe3() * std::sin(candidate.phiHe3()), + candidate.ptHe3() * std::sinh(candidate.etaHe3())}; + const std::array pionMomentum{ + candidate.ptPi() * std::cos(candidate.phiPi()), + candidate.ptPi() * std::sin(candidate.phiPi()), + candidate.ptPi() * std::sinh(candidate.etaPi())}; + HadHyperCandidate result; + for (size_t i = 0; i < result.momentum.size(); ++i) { + result.momentum[i] = heMomentum[i] + pionMomentum[i]; + } + result.mass = computeHyperCandidateMass(candidate); + result.heTrackId = candidate.heTrackId(); + result.pionTrackId = candidate.piTrackId(); + result.sourceCandidateId = candidate.globalIndex(); + result.etaHe = candidate.etaHe3(); + result.phiHe = candidate.phiHe3(); + result.etaPi = candidate.etaPi(); + result.phiPi = candidate.phiPi(); + result.isMatter = candidate.isMatter(); + if constexpr (isMC) { + if (he.has_mcParticle()) { + result.heTruth = makeHadHyperParticleTruth(he.template mcParticle_as()); + } + if (pion.has_mcParticle()) { + result.decayPionTruth = makeHadHyperParticleTruth(pion.template mcParticle_as()); + } + if (he.has_mcParticle() && pion.has_mcParticle()) { + const auto heParticle = he.template mcParticle_as(); + const auto pionParticle = pion.template mcParticle_as(); + if (heParticle.has_mothers() && pionParticle.has_mothers()) { + for (const auto& heMother : heParticle.template mothers_as()) { + for (const auto& pionMother : pionParticle.template mothers_as()) { + if (heMother.globalIndex() == pionMother.globalIndex() && + std::abs(heMother.pdgCode()) == HyperTritonPDG) { + result.hyperTruth = makeHadHyperParticleTruth(heMother); + } + } + } + } + } + } + result.info = {candidate.isMatter(), + candidate.ptHe3(), candidate.etaHe3(), candidate.phiHe3(), + candidate.ptPi(), candidate.etaPi(), candidate.phiPi(), + candidate.dcaV0Daug(), candidate.dcaHe(), candidate.dcaPi(), + candidate.nSigmaHe(), candidate.tofMass(), + candidate.nTPCCrossedRowsHe(), candidate.nTPCCrossedRowsPi(), + candidate.tpcMomHe(), candidate.tpcMomPi(), + candidate.tpcSignalHe(), candidate.tpcSignalPi(), + candidate.tpcChi2He(), candidate.itsChi2He(), candidate.itsChi2Pi(), + candidate.itsClusterSizesHe(), candidate.itsClusterSizesPi(), + candidate.xDecVtx(), candidate.yDecVtx(), candidate.zDecVtx()}; + if constexpr (isMC) { + result.statusCode = static_cast(candidate.statusCode()); + result.isReco = candidate.isReco(); + result.isSignal = candidate.isSignal(); + result.isRecoMCCollision = candidate.isRecoMCCollision(); + result.isSurvEvSel = candidate.isSurvEvSel(); + result.isTwoBodyDecay = candidate.isTwoBodyDecay(); + result.isFakeHeOnITSLayer = candidate.isFakeHeOnITSLayer(); + result.genPt = candidate.genPt(); + result.genEta = candidate.genEta(); + result.genPhi = candidate.genPhi(); + result.genPtHe3 = candidate.genPtHe3(); + result.genDecVtx = {candidate.genXDecVtx(), candidate.genYDecVtx(), candidate.genZDecVtx()}; + const float absGenPt = std::abs(candidate.genPt()); + result.genMomentum = {absGenPt * std::cos(candidate.genPhi()), + absGenPt * std::sin(candidate.genPhi()), + absGenPt * std::sinh(candidate.genEta())}; + } + hadHyperRegistry.fill(HIST("hMass"), result.mass); + hadHyperRegistry.fill(HIST("hDaughterHeTPC"), he.tpcInnerParam(), he.tpcSignal()); + hadHyperRegistry.fill(HIST("hDaughterPiTPC"), pion.tpcInnerParam(), pion.tpcSignal()); + return result; + } - hadNucand.sharedClustersNu = trackDe.tpcNClsShared(); - hadNucand.sharedClustersHad = trackHad.tpcNClsShared(); + HadHyperDataInfo makeHadHyperDataInfo(const HadHyperCandidate& candidate, const HadHyperHadron& hadron, + const HadHyperEvent& hyperEvent, const HadHyperEvent& hadronEvent, + bool mixed, int mixingDepth) + { + (void)hadronEvent; + return std::tuple_cat(std::make_tuple(mixed, mixingDepth), + hyperEvent.info, candidate.info, + hadron.info); + } - hadNucand.isBkgUS = trackDe.sign() * trackHad.sign() < 0; - hadNucand.isBkgEM = isMixedEvent; + bool hasTruthMother(const HadHyperHadron& hadron, int64_t motherId) const + { + return motherId >= 0 && std::find(hadron.motherIds.begin(), hadron.motherIds.end(), motherId) != hadron.motherIds.end(); + } - hadNucand.invMass = invMass; + bool isHadHyperTruthSelfCorrelation(const HadHyperCandidate& candidate, const HadHyperHadron& hadron) const + { + return hadron.truth.particleId >= 0 && + (hadron.truth.particleId == candidate.heTruth.particleId || + hadron.truth.particleId == candidate.decayPionTruth.particleId || + hasTruthMother(hadron, candidate.hyperTruth.particleId)); + } - hadNucand.trackIDNu = trackDe.globalIndex(); - hadNucand.trackIDHad = trackHad.globalIndex(); + HadHyperMCInfo makeHadHyperMCInfo(const HadHyperCandidate& candidate, const HadHyperHadron& hadron, + const HadHyperEvent& hyperEvent, const HadHyperEvent& hadronEvent) const + { + const bool sameMCCollision = candidate.hyperTruth.collisionId >= 0 && + hadron.truth.collisionId == candidate.hyperTruth.collisionId; + const bool matchesHypRecoMCCollision = hyperEvent.hasMCCollision && + candidate.hyperTruth.collisionId == hyperEvent.mcCollisionId; + const bool matchesPairRecoMCCollision = hadronEvent.hasMCCollision && + hadron.truth.collisionId == hadronEvent.mcCollisionId; + const bool isTruthSelfCorrelation = isHadHyperTruthSelfCorrelation(candidate, hadron); + const bool isTruePrimaryHadHyperPair = candidate.isSignal && + std::abs(candidate.hyperTruth.pdgCode) == HyperTritonPDG && + std::abs(hadron.truth.pdgCode) == std::abs(species.settingHadPDGCode.value) && + hadron.truth.isPhysicalPrimary && sameMCCollision && !isTruthSelfCorrelation; + return {candidate.genPt, candidate.genEta, candidate.genPhi, + candidate.genDecVtx[0], candidate.genDecVtx[1], candidate.genDecVtx[2], + candidate.isReco, candidate.isSignal, + candidate.isRecoMCCollision, candidate.isSurvEvSel, candidate.isTwoBodyDecay, candidate.statusCode, + candidate.heTruth.pt, candidate.heTruth.isPhysicalPrimary, + candidate.decayPionTruth.isPhysicalPrimary, + std::get<0>(hadron.info), std::get<1>(hadron.info), std::get<2>(hadron.info), + hadron.truth.pt, hadron.truth.eta, hadron.truth.phi, hadron.truth.isPhysicalPrimary, + hadron.truth.process, + sameMCCollision, matchesHypRecoMCCollision, matchesPairRecoMCCollision, + isTruthSelfCorrelation, isTruePrimaryHadHyperPair}; + } - if (trackDe.hasTOF()) { - float beta = o2::pid::tof::Beta::GetBeta(trackDe); - beta = std::min(1.f - 1.e-6f, std::max(1.e-4f, beta)); /// sometimes beta > 1 or < 0, to be checked - float tpcInnerParamDe = trackDe.tpcInnerParam(); - hadNucand.massTOFNu = tpcInnerParamDe * std::sqrt(1.f / (beta * beta) - 1.f); + void fillHadHyperMCQA(const HadHyperCandidate& candidate, const HadHyperHadron& hadron, + const HadHyperEvent& hyperEvent, float kstar) + { + if (!candidate.isReco || !candidate.isSignal || + std::abs(candidate.hyperTruth.pdgCode) != HyperTritonPDG) { + return; } - if (trackHad.hasTOF()) { - float beta = o2::pid::tof::Beta::GetBeta(trackHad); - beta = std::min(1.f - 1.e-6f, std::max(1.e-4f, beta)); /// sometimes beta > 1 or < 0, to be checked - hadNucand.massTOFHad = trackHad.tpcInnerParam() * std::sqrt(1.f / (beta * beta) - 1.f); + + const bool isSelectedHadronSpecies = std::abs(hadron.truth.pdgCode) == std::abs(species.settingHadPDGCode.value); + if (!isSelectedHadronSpecies) { + return; } - hadNucand.kstar = o2::analysis::femtoWorld::FemtoWorldMath::getkstar(trackHad, MassHad, trackDe, o2::constants::physics::MassDeuteron); - hadNucand.mT = o2::analysis::femtoWorld::FemtoWorldMath::getmT(trackHad, MassHad, trackDe, o2::constants::physics::MassDeuteron); + const float kstarMC = computePairKstar(hadron.truth.momentum, configuredHadronMass(), + candidate.genMomentum, o2::constants::physics::MassHyperTriton); + if (std::isfinite(kstarMC)) { + hadHyperRegistry.fill(HIST("MC/hKstarRecVsGenHyperReco"), kstarMC, kstar); + hadHyperRegistry.fill(HIST("MC/hKstarResolutionHyperReco"), kstar, kstar - kstarMC); + } - return true; + hadHyperRegistry.fill(HIST("MC/hPrimaryHadronVsKstarDen"), kstar); + hadHyperRegistry.fill(HIST("MC/hPrimaryHadronVsCentDen"), hyperEvent.centrality); + if (hadron.truth.isPhysicalPrimary) { + hadHyperRegistry.fill(HIST("MC/hPrimaryHadronVsKstarNum"), kstar); + hadHyperRegistry.fill(HIST("MC/hPrimaryHadronVsCentNum"), hyperEvent.centrality); + } } - template - void fillCandidateInfoHyper(const aod::DataHypCandsWColl::iterator& V0Hyper, const Ttrack& trackHad, HadNucandidate& hadHypercand, bool isMixedEvent) - { - hadHypercand.collisionID = V0Hyper.collisionId(); - // get hypertriton information - // constexpr double mHe3 = o2::constants::physics::MassHelium3; - // constexpr double mPi = o2::constants::physics::MassPiPlus; - // --- He3 - float pxHe3 = V0Hyper.ptHe3() * std::cos(V0Hyper.phiHe3()); - float pyHe3 = V0Hyper.ptHe3() * std::sin(V0Hyper.phiHe3()); - float pzHe3 = V0Hyper.ptHe3() * std::sinh(V0Hyper.etaHe3()); - // float pHe3 = V0Hyper.ptHe3() * std::cosh(V0Hyper.etaHe3()); - // float enHe3 = std::sqrt(pHe3 * pHe3 + mHe3 * mHe3); - // --- pi - float pxPi = V0Hyper.ptPi() * std::cos(V0Hyper.phiPi()); - float pyPi = V0Hyper.ptPi() * std::sin(V0Hyper.phiPi()); - float pzPi = V0Hyper.ptPi() * std::sinh(V0Hyper.etaPi()); - // float pPi = V0Hyper.ptPi() * std::cosh(V0Hyper.etaPi()); - // float enPi = std::sqrt(pPi * pPi + mPi * mPi); - // --- hypertriton - float px = pxHe3 + pxPi; - float py = pyHe3 + pyPi; - float pz = pzHe3 + pzPi; - hadHypercand.momNu = std::array{px, py, pz}; - hadHypercand.momHad = std::array{trackHad.px(), trackHad.py(), trackHad.pz()}; + template + bool fillHadHyperPair(const HadHyperCandidate& candidate, const HadHyperHadron& hadron, + const HadHyperEvent& hyperEvent, const HadHyperEvent& hadronEvent, + bool mixed, int mixingDepth) + { + const float kstar = computePairKstar(hadron.momentum, configuredHadronMass(), + candidate.momentum, o2::constants::physics::MassHyperTriton); + // All source indices here belong to the same timeframe. Also clean mixed + // pairs in case LF reassigned a daughter to a different collision. + if (hadron.sourceId == candidate.heTrackId || hadron.sourceId == candidate.pionTrackId) { + const int reason = hadron.sourceId == candidate.heTrackId ? 0 : 1; + hadHyperRegistry.fill(HIST("hSelfPairs"), reason, kstar); + if (!mixed) { + hadHyperRegistry.fill(HIST("hSameEventSelfPairs"), reason, kstar); + } + return false; + } + if constexpr (isMC) { + if (isHadHyperTruthSelfCorrelation(candidate, hadron)) { + hadHyperRegistry.fill(HIST("hSelfPairs"), 2, kstar); + if (!mixed) { + hadHyperRegistry.fill(HIST("hSameEventSelfPairs"), 2, kstar); + } + return false; + } + } + const bool unlikeSign = candidate.sign() * hadron.sign() < 0; + if (!eventMixing.settingSaveUSandLS && unlikeSign != eventMixing.settingEnableBkgUS.value) { + return false; + } + if (!std::isfinite(kstar)) { + return false; + } + if (isCloseHadHyperPairAtPV(candidate, hadron, /*fillQA*/ true)) { + return false; + } + if (mixed) { + hadHyperRegistry.fill(HIST("hME"), kstar); + } else { + hadHyperRegistry.fill(HIST("hSE"), kstar); + } - float invMass = 0; - invMass = RecoDecay::m(std::array, 2>{hadHypercand.momNu, hadHypercand.momHad}, std::array{static_cast(o2::constants::physics::MassHelium3), MassHad}); + if constexpr (isMC) { + fillHadHyperMCQA(candidate, hadron, hyperEvent, kstar); + } - hadHypercand.signHad = trackHad.sign(); - if (V0Hyper.isMatter()) { - hadHypercand.signNu = 1; + if (!output.settingFillTable || (hadHyper.maxOutputKstar.value > 0.f && kstar >= hadHyper.maxOutputKstar.value)) { + return true; + } + const auto dataInfo = makeHadHyperDataInfo(candidate, hadron, hyperEvent, hadronEvent, mixed, mixingDepth); + if constexpr (isMC) { + std::apply([this](const auto&... columns) { mOutputHadHyperMCTable(columns...); }, + std::tuple_cat(dataInfo, makeHadHyperMCInfo(candidate, hadron, hyperEvent, hadronEvent))); } else { - hadHypercand.signNu = -1; - } - hadHypercand.etaHe3 = V0Hyper.etaHe3(); - hadHypercand.ptHe3 = V0Hyper.ptHe3(); - hadHypercand.dcaxyHad = trackHad.dcaXY(); - hadHypercand.dcazHad = trackHad.dcaZ(); - hadHypercand.tpcSignalHad = trackHad.tpcSignal(); - hadHypercand.tpcSignalNu = V0Hyper.tpcSignalHe(); - hadHypercand.momHadTPC = trackHad.tpcInnerParam(); - hadHypercand.nSigmaHad = trackHad.tpcNSigmaPi(); - hadHypercand.nSigmaNu = V0Hyper.nSigmaHe(); - hadHypercand.chi2TPCHad = trackHad.tpcChi2NCl(); - hadHypercand.chi2TPCNu = V0Hyper.tpcChi2He(); - hadHypercand.pidTrkHad = trackHad.pidForTracking(); - hadHypercand.itsClSizeHad = trackHad.itsClusterSizes(); - hadHypercand.itsClSizeNu = V0Hyper.itsClusterSizesHe(); - hadHypercand.nClsItsHad = trackHad.itsNCls(); - hadHypercand.sharedClustersHad = trackHad.tpcNClsShared(); - - hadHypercand.isBkgUS = hadHypercand.signNu * trackHad.sign() < 0; - hadHypercand.isBkgEM = isMixedEvent; - hadHypercand.invMass = invMass; - - hadHypercand.trackIDHad = trackHad.globalIndex(); - - if (trackHad.hasTOF()) { - float beta = o2::pid::tof::Beta::GetBeta(trackHad); - beta = std::min(1.f - 1.e-6f, std::max(1.e-4f, beta)); /// sometimes beta > 1 or < 0, to be checked - hadHypercand.massTOFHad = trackHad.tpcInnerParam() * std::sqrt(1.f / (beta * beta) - 1.f); + std::apply([this](const auto&... columns) { mOutputHadHyperDataTable(columns...); }, dataInfo); } + return true; } - template - void pairTracksSameEvent(const Ttrack& tracks, float /*cent*/) + template + bool hasValidHyperDaughterIndices(const Ttracks& tracks, int64_t heTrackId, int64_t pionTrackId) const { - // LOG(info) << "Number of tracks: " << tracks.size(); - for (const auto& track0 : tracks) { - - mQaRegistry.fill(HIST("hTrackSel"), Selections::kNoCuts); - - if (!selectTrack(track0)) { - continue; - } - mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); + const auto first = static_cast(tracks.offset()); + const auto last = first + static_cast(tracks.size()); + return heTrackId >= first && heTrackId < last && pionTrackId >= first && pionTrackId < last; + } - if (!selectionPIDDe(track0)) { + template + void collectHyperHadronTracks(HadHyperEvent& event, const Ttracks& eventTracks) + { + for (const auto& track : eventTracks) { + if (!selectTrackHadron(track) || !selectionPIDHadron(track)) { continue; } - mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); - mQaRegistry.fill(HIST("hSingleNuPt"), track0.pt() * track0.sign()); - mQaRegistry.fill(HIST("hSingleNuPin"), track0.tpcInnerParam() * track0.sign()); + event.hadrons.push_back(makeHadHyperHadron(track)); + hadHyperRegistry.fill(HIST("hHadronTPC"), track.tpcInnerParam(), track.tpcSignal()); + } + } - for (const auto& track1 : tracks) { - if (track0 == track1) { + template + void collectHyperCandidates(HadHyperEvent& event, const Tcandidates& eventCandidates, const Ttracks& tracks) + { + for (const auto& candidate : eventCandidates) { + if constexpr (isMC) { + if (!candidate.isReco()) { continue; } - - if (!settingSaveUSandLS) { - if (!settingEnableBkgUS && (track0.sign() * track1.sign() < 0)) { - continue; - } - if (settingEnableBkgUS && (track0.sign() * track1.sign() > 0)) { - continue; - } - } - - if (!selectTrack(track1) || !selectionPIDHadron(track1)) { + } + if (!hasValidHyperDaughterIndices(tracks, candidate.heTrackId(), candidate.piTrackId())) { + if constexpr (isMC) { continue; + } else { + LOG(fatal) << "Hypertriton daughter indices must reference the input Tracks table"; } - - SVCand trackPair; - trackPair.tr0Idx = track0.globalIndex(); - trackPair.tr1Idx = track1.globalIndex(); - const int collIdx = track0.collisionId(); - CollBracket collBracket{collIdx, collIdx}; - trackPair.collBracket = collBracket; - mTrackPairs.push_back(trackPair); } + if (!selectHyperCandidate(candidate)) { + continue; + } + event.candidates.push_back(makeHadHyperCandidate(candidate, tracks)); } } - template - void pairTracksSameEventHyper(const Ttrack& hadTracks, const Thypers& V0Hypers) + template + HadHyperEvent buildHyperEvent(const Tcollision& collision, const Ttracks& tracks, const Tcandidates& candidates) { - for (const auto& V0Hyper : V0Hypers) { - if (!selectionPIDHyper(V0Hyper)) { - continue; - } - for (const auto& hadTrack : hadTracks) { - - mQaRegistry.fill(HIST("hTrackSel"), Selections::kNoCuts); + HadHyperEvent event; + event.info = makeHadHyperEventInfo(collision); + event.centrality = collision.centFT0C(); + if constexpr (isMC) { + event.hasMCCollision = collision.has_mcCollision(); + event.mcCollisionId = collision.has_mcCollision() ? collision.mcCollisionId() : -1; + } - if (!selectTrack(hadTrack)) { - continue; - } - mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); + if constexpr (isMC) { + const auto eventTracks = tracks.sliceBy(mPerColMC, collision.globalIndex()); + collectHyperHadronTracks(event, eventTracks); - if (!selectionPIDHadron(hadTrack)) { - continue; - } - mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); + const auto eventCandidates = candidates.sliceBy(hypPerColMC, collision.globalIndex()); + collectHyperCandidates(event, eventCandidates, tracks); + } else { + const auto eventTracks = tracks.sliceBy(mPerCol, collision.globalIndex()); + collectHyperHadronTracks(event, eventTracks); - SVCand pair; - pair.tr0Idx = V0Hyper.globalIndex(); - pair.tr1Idx = hadTrack.globalIndex(); - const int collIdx = V0Hyper.collisionId(); - CollBracket collBracket{collIdx, collIdx}; - pair.collBracket = collBracket; - mTrackHypPairs.push_back(pair); - } + const auto eventCandidates = candidates.sliceBy(hypPerCol, collision.globalIndex()); + collectHyperCandidates(event, eventCandidates, tracks); } + + return event; } - template - void pairTracksEventMixing(T& DeCands, T& hadCands) + template + void fillSameEventHyperPairs(const HadHyperEvent& event) { - for (const auto& DeCand : DeCands) { - if (!selectTrack(DeCand) || !selectionPIDDe(DeCand)) { - continue; - } - for (const auto& hadCand : hadCands) { - if (!selectTrack(hadCand) || !selectionPIDHadron(hadCand)) { - continue; + for (const auto& candidate : event.candidates) { + int acceptedPairs = 0; + for (const auto& hadron : event.hadrons) { + if (fillHadHyperPair(candidate, hadron, event, event, false, 0)) { + ++acceptedPairs; } - - SVCand trackPair; - trackPair.tr0Idx = DeCand.globalIndex(); - trackPair.tr1Idx = hadCand.globalIndex(); - const int collIdx = DeCand.collisionId(); - CollBracket collBracket{collIdx, collIdx}; - trackPair.collBracket = collBracket; - mTrackPairs.push_back(trackPair); } + hadHyperRegistry.fill(HIST("hCandidatePairMultiplicitySE"), acceptedPairs, event.centrality); } } - template - void pairHyperEventMixing(T1& hadCands, T2& hypCands) + template + void fillMixedEventHyperPairs(const HadHyperEvent& currentEvent, const std::deque& pool) { - for (const auto& hypCand : hypCands) { - if (!selectionPIDHyper(hypCand)) { - continue; + const int depth = static_cast(pool.size()); + hadHyperRegistry.fill(HIST("hMixingDepth"), depth); + std::vector currentCandidatePairCounts(currentEvent.candidates.size(), 0); + for (const auto& partner : pool) { + const float currentPosZ = std::get<1>(currentEvent.info); + const float partnerPosZ = std::get<1>(partner.info); + hadHyperRegistry.fill(HIST("hMixEventDeltaPosZVsCent"), currentEvent.centrality, currentPosZ - partnerPosZ); + hadHyperRegistry.fill(HIST("hMixEventDeltaCentFT0CVsCent"), currentEvent.centrality, currentEvent.centrality - partner.centrality); + size_t candidateIndex = 0; + for (const auto& candidate : currentEvent.candidates) { + for (const auto& hadron : partner.hadrons) { + if (fillHadHyperPair(candidate, hadron, currentEvent, partner, true, depth)) { + ++currentCandidatePairCounts[candidateIndex]; + } + } + ++candidateIndex; } - for (const auto& hadCand : hadCands) { - if (!selectTrack(hadCand) || !selectionPIDHadron(hadCand)) { - continue; + for (const auto& candidate : partner.candidates) { + int acceptedPairs = 0; + for (const auto& hadron : currentEvent.hadrons) { + if (fillHadHyperPair(candidate, hadron, partner, currentEvent, true, depth)) { + ++acceptedPairs; + } } - - SVCand pair; - pair.tr0Idx = hypCand.globalIndex(); - pair.tr1Idx = hadCand.globalIndex(); - const int collIdx = hypCand.collisionId(); - CollBracket collBracket{collIdx, collIdx}; - pair.collBracket = collBracket; - mTrackHypPairs.push_back(pair); + hadHyperRegistry.fill(HIST("hCandidatePairMultiplicityME"), acceptedPairs, partner.centrality); } } - } - - template - void fillTable(const HadNucandidate& hadNucand, const Tcoll& collision) - { - mOutputDataTable( - hadNucand.recoPtHad(), - hadNucand.recoPtNu(), - hadNucand.momHadTPC, - hadNucand.momNuTPC, - hadNucand.trackIDHad, - hadNucand.trackIDNu); - if (settingFillMultiplicity) { - mOutputMultiplicityTable( - collision.globalIndex(), - collision.posZ(), - collision.numContrib(), - collision.centFT0C(), - collision.multFT0C()); + for (const auto& acceptedPairs : currentCandidatePairCounts) { + hadHyperRegistry.fill(HIST("hCandidatePairMultiplicityME"), acceptedPairs, currentEvent.centrality); } } - template - void fillTableHyper(const HadNucandidate& hadNucand, const Tcoll& collision) + void storeHyperEventInPool(std::deque& pool, HadHyperEvent&& event) { - mOutputHyperDataTable( - hadNucand.recoPtNu(), - hadNucand.recoEtaNu(), - hadNucand.ptHe3, - hadNucand.etaHe3, - hadNucand.recoPhiNu(), - hadNucand.recoPtHad(), - hadNucand.recoEtaHad(), - hadNucand.recoPhiHad(), - hadNucand.dcaxyHad, - hadNucand.dcazHad, - hadNucand.tpcSignalHad, - hadNucand.tpcSignalNu, - hadNucand.momHadTPC, - hadNucand.nSigmaHad, - hadNucand.nSigmaNu, - hadNucand.chi2TPCHad, - hadNucand.chi2TPCNu, - hadNucand.massTOFHad, - hadNucand.pidTrkHad, - hadNucand.itsClSizeHad, - hadNucand.itsClSizeNu, - hadNucand.sharedClustersHad, - hadNucand.trackIDHad, - hadNucand.isBkgUS, - hadNucand.isBkgEM); - if (settingFillMultiplicity) { - mOutputMultiplicityTable( - collision.globalIndex(), - collision.posZ(), - collision.numContrib(), - collision.centFT0C(), - collision.multFT0C()); + const int requestedMixingDepth = eventMixing.settingNoMixedEvents.value; + if (requestedMixingDepth <= 0) { + return; + } + const auto mixingDepth = static_cast(requestedMixingDepth); + if (pool.size() >= mixingDepth) { + pool.pop_front(); } + pool.push_back(std::move(event)); } - void fillHistograms(const HadNucandidate& hadNucand) + template + void processHyperPairs(const Tcollisions& collisions, const Ttracks& tracks, const Tcandidates& candidates, + const aod::BCsWithTimestamps& bcs, + std::unordered_map>& mixingPools, + int& mixingRunNumber) { - mQaRegistry.fill(HIST("hNuPt"), hadNucand.recoPtNu()); - mQaRegistry.fill(HIST("hHadPt"), hadNucand.recoPtHad()); - mQaRegistry.fill(HIST("hNuPin"), hadNucand.momNuTPC * hadNucand.signNu); - mQaRegistry.fill(HIST("hHadPin"), hadNucand.momHadTPC * hadNucand.signHad); - mQaRegistry.fill(HIST("hNuEta"), hadNucand.recoEtaNu()); - mQaRegistry.fill(HIST("hHadEta"), hadNucand.recoEtaHad()); - mQaRegistry.fill(HIST("hNuPhi"), hadNucand.recoPhiNu()); - mQaRegistry.fill(HIST("hHadPhi"), hadNucand.recoPhiHad()); - mQaRegistry.fill(HIST("hNuHadtInvMass"), hadNucand.invMass); - mQaRegistry.fill(HIST("hdcaxyNu"), hadNucand.dcaxyNu); - mQaRegistry.fill(HIST("hdcazNu"), hadNucand.dcazNu); - mQaRegistry.fill(HIST("hdcaxyHad"), hadNucand.dcaxyHad); - mQaRegistry.fill(HIST("hdcazHad"), hadNucand.dcazHad); - mQaRegistry.fill(HIST("hdcazNu_min"), (std::abs(hadNucand.dcazNu) - settingCutDeDCAzMin)); - mQaRegistry.fill(HIST("hNClsNuITS"), hadNucand.nClsItsNu); - mQaRegistry.fill(HIST("hNClsHadITS"), hadNucand.nClsItsHad); - mQaRegistry.fill(HIST("hisBkgEM"), hadNucand.isBkgEM); - } + const CentralityBinningType configuredBinningPolicy{{axisVertex, axisCentrality}, true}; + for (const auto& collision : collisions) { + if (!selectCollision(collision, bcs)) { + continue; + } + if constexpr (isMC) { + if (mc.settingRequireRecoMCCollisionMatch.value && !collision.has_mcCollision()) { + continue; + } + } + + hadHyperRegistry.fill(HIST("hPoolFlow"), 0); + int poolBin = -1; + if (hadHyper.enableMixing.value) { + poolBin = configuredBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + if (poolBin < 0) { + continue; + } + hadHyperRegistry.fill(HIST("hPoolFlow"), 1); + } - template - void fillKstar(const HadNucandidate& hadNucand, const Tcoll& /*collision*/) - { - if (hadNucand.isBkgUS == 0) { - if (hadNucand.recoPtNu() > 0) { - mQaRegistry.fill(HIST("hkStar_LS_M"), hadNucand.kstar); - mQaRegistry.fill(HIST("hkStaVsmT_LS_M"), hadNucand.kstar, hadNucand.mT); - } else { - mQaRegistry.fill(HIST("hkStar_LS_A"), hadNucand.kstar); - mQaRegistry.fill(HIST("hkStaVsmT_LS_A"), hadNucand.kstar, hadNucand.mT); + auto event = buildHyperEvent(collision, tracks, candidates); + fillSameEventHyperPairs(event); + + if (!hadHyper.enableMixing.value) { + continue; } - } else { - if (hadNucand.recoPtNu() > 0) { - mQaRegistry.fill(HIST("hkStar_US_M"), hadNucand.kstar); - mQaRegistry.fill(HIST("hkStaVsmT_US_M"), hadNucand.kstar, hadNucand.mT); - } else { - mQaRegistry.fill(HIST("hkStar_US_A"), hadNucand.kstar); - mQaRegistry.fill(HIST("hkStaVsmT_US_A"), hadNucand.kstar, hadNucand.mT); + + const auto bc = collision.template bc_as(); + if (mixingRunNumber != bc.runNumber()) { + mixingPools.clear(); + mixingRunNumber = bc.runNumber(); } + + auto& pool = mixingPools[poolBin]; + fillMixedEventHyperPairs(event, pool); + storeHyperEventInPool(pool, std::move(event)); } } // ================================================================================================================== - template - void fillPairs(const Tcollisions& collisions, const Ttracks& tracks, const bool isMixedEvent) + void processMC(const CollisionsFullMC& collisions, const TrackCandidatesMC& tracks, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) { - for (const auto& trackPair : mTrackPairs) { + mGoodCollisions.clear(); + mGoodCollisions.resize(collisions.size(), false); - auto deTrack = tracks.rawIteratorAt(trackPair.tr0Idx); - auto hadTrack = tracks.rawIteratorAt(trackPair.tr1Idx); - auto collBracket = trackPair.collBracket; + for (const auto& collision : collisions) { + mTrackPairs.clear(); - HadNucandidate hadNucand; - if (!fillCandidateInfo(deTrack, hadTrack, collBracket, collisions, hadNucand, tracks, isMixedEvent)) { + if (!selectCollision(collision, bcs)) { + continue; + } + if (mc.settingRequireRecoMCCollisionMatch.value && !collision.has_mcCollision()) { continue; } - auto collision = collisions.rawIteratorAt(hadNucand.collisionID); - fillKstar(hadNucand, collision); - fillHistograms(hadNucand); + const uint64_t collIdx = collision.globalIndex(); + mGoodCollisions[collIdx] = true; + auto tracksThisCollision = tracks.sliceBy(mPerColMC, collIdx); + tracksThisCollision.bindExternalIndices(&tracks); - if (settingFillTable) { - fillTable(hadNucand, collision); - } - } - } + // This is deliberately the same pair builder as for data. It applies + // nucleus/pion selections and CPR before truth matching. + pairTracksSameEvent(tracksThisCollision, collision.centFT0C()); - template - void fillPairsHyper(const Tcollisions& collisions, const Ttracks& hadTracks, const o2::aod::DataHypCandsWColl& V0Hypers, const bool isMixedEvent) - { - for (const auto& trackPair : mTrackHypPairs) { + for (const auto& trackPair : mTrackPairs) { + mQaRegistry.fill(HIST("MC/hPairFlow"), 0); + auto trackNu = tracks.rawIteratorAt(trackPair.tr0Idx); + auto trackHad = tracks.rawIteratorAt(trackPair.tr1Idx); - auto v0hyper = V0Hypers.rawIteratorAt(trackPair.tr0Idx); - auto hadTrack = hadTracks.rawIteratorAt(trackPair.tr1Idx); - // auto collBracket = trackPair.collBracket; + if (!trackNu.has_mcParticle() || !trackHad.has_mcParticle()) { + continue; + } + mQaRegistry.fill(HIST("MC/hPairFlow"), 1); - HadNucandidate hadNucand; - fillCandidateInfoHyper(v0hyper, hadTrack, hadNucand, isMixedEvent); - - mQaRegistry.fill(HIST("hNuPt"), hadNucand.recoPtNu()); - mQaRegistry.fill(HIST("hHadPt"), hadNucand.recoPtHad()); - mQaRegistry.fill(HIST("hNuEta"), hadNucand.recoEtaNu()); - mQaRegistry.fill(HIST("hHadEta"), hadNucand.recoEtaHad()); - mQaRegistry.fill(HIST("hNuPhi"), hadNucand.recoPhiNu()); - mQaRegistry.fill(HIST("hHadPhi"), hadNucand.recoPhiHad()); - mQaRegistry.fill(HIST("hNuHadtInvMass"), hadNucand.invMass); - mQaRegistry.fill(HIST("hNClsHadITS"), hadNucand.nClsItsHad); - mQaRegistry.fill(HIST("hisBkgEM"), hadNucand.isBkgEM); + const auto particleNu = trackNu.template mcParticle_as(); + const auto particleHad = trackHad.template mcParticle_as(); + const bool truthSpeciesMatch = std::abs(particleNu.pdgCode()) == std::abs(species.settingNuPDGCode.value) && + std::abs(particleHad.pdgCode()) == std::abs(species.settingHadPDGCode.value); + if (mc.settingRequireTruthSpecies.value && !truthSpeciesMatch) { + continue; + } + mQaRegistry.fill(HIST("MC/hPairFlow"), 2); - auto collision = collisions.rawIteratorAt(hadNucand.collisionID); + const bool sameMCCollision = particleNu.mcCollisionId() == particleHad.mcCollisionId(); + if (mc.settingRequireSameMCCollision.value && !sameMCCollision) { + continue; + } + mQaRegistry.fill(HIST("MC/hPairFlow"), 3); + + const bool matchesRecoMCCollision = collision.has_mcCollision() && + particleNu.mcCollisionId() == collision.mcCollisionId() && + particleHad.mcCollisionId() == collision.mcCollisionId(); + if (mc.settingRequireRecoMCCollisionMatch.value && !matchesRecoMCCollision) { + continue; + } + mQaRegistry.fill(HIST("MC/hPairFlow"), 4); - if (settingFillTable) { - fillTableHyper(hadNucand, collision); + if (mc.settingRequirePhysicalPrimaries.value && + (!particleNu.isPhysicalPrimary() || !particleHad.isPhysicalPrimary())) { + continue; + } + mQaRegistry.fill(HIST("MC/hPairFlow"), 5); + + HadNucandidate hadNucand; + if (!fillCandidateInfo(trackNu, trackHad, trackPair.collBracket, collisions, hadNucand, tracks, /*isMixedEvent*/ false)) { + continue; + } + mQaRegistry.fill(HIST("MC/hPairFlow"), 6); + auto selectedCollision = collisions.rawIteratorAt(hadNucand.collisionID); + fillKstar(hadNucand, selectedCollision); + fillHistograms(hadNucand); + + if (output.settingFillTable && shouldFillOutputTable(hadNucand)) { + fillTable(hadNucand, selectedCollision); + fillMCTable(hadNucand, particleNu, particleHad, sameMCCollision, matchesRecoMCCollision); + } } } } - - // ================================================================================================================== + PROCESS_SWITCH(HadNucleiFemto, processMC, "Process reconstructed MC same-event pairs", false); void processSameEvent(const CollisionsFull& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps& bcs) { @@ -1207,7 +3339,7 @@ struct HadNucleiFemto { pairTracksSameEvent(trackTableThisCollision, collision.centFT0C()); - if (mTrackPairs.size() == 0) { + if (mTrackPairs.empty()) { continue; } @@ -1216,144 +3348,661 @@ struct HadNucleiFemto { } PROCESS_SWITCH(HadNucleiFemto, processSameEvent, "Process Same event", false); - void processSameEventHyper(const CollisionsFull& collisions, const TrackCandidates& hadtracks, o2::aod::DataHypCandsWColl const& V0Hypers, const aod::BCsWithTimestamps& bcs) + void processMixedEvent(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) { - mGoodCollisions.clear(); - mGoodCollisions.resize(collisions.size(), false); - // LOG(info) << "Number of hyperCandidates read = " << V0Hypers.size(); + LOG(debug) << "Processing mixed event"; + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; for (const auto& collision : collisions) { + mQaRegistry.fill(HIST("hMixedEventSelections"), 0); + if (!passesEventSelection(collision)) { + continue; + } + mQaRegistry.fill(HIST("hMixedEventSelections"), 1); + if (!passesZorroSelection(collision)) { + continue; + } + mQaRegistry.fill(HIST("hMixedEventSelections"), 2); + + const auto bc = collision.template bc_as(); + if (mMixingRunNumber != bc.runNumber()) { + mMixingPools.clear(); + mMixingRunNumber = bc.runNumber(); + } - mTrackHypPairs.clear(); + BufferedCollision currentCollision; + currentCollision.eventId = mNextMixedEventId++; + currentCollision.posZ = collision.posZ(); + currentCollision.numContrib = collision.numContrib(); + currentCollision.centFT0C = collision.centFT0C(); + currentCollision.multFT0C = collision.multFT0C(); + currentCollision.multNTracksPV = collision.multNTracksPV(); - if (!selectCollision(collision, bcs)) { + const uint64_t collIdx = collision.globalIndex(); + auto tracksThisCollision = tracks.sliceBy(mPerCol, collIdx); + tracksThisCollision.bindExternalIndices(&tracks); + for (const auto& track : tracksThisCollision) { + if (selectTrackNu(track) && selectionPIDNu(track)) { + currentCollision.nuclei.push_back(makeBufferedTrack(track, /*isNucleus*/ true, currentCollision.eventId)); + } + if (selectTrackHadron(track) && selectionPIDHadron(track)) { + currentCollision.hadrons.push_back(makeBufferedTrack(track, /*isNucleus*/ false, currentCollision.eventId)); + } + } + + mQaRegistry.fill(HIST("hMixedNucleiPerEvent"), currentCollision.nuclei.size()); + mQaRegistry.fill(HIST("hMixedHadronsPerEvent"), currentCollision.hadrons.size()); + + if (eventMixing.settingRequireBothSpeciesForMixing.value && + (currentCollision.nuclei.empty() || currentCollision.hadrons.empty())) { continue; } - mGoodCollisions[collision.globalIndex()] = true; - const uint64_t collIdx = collision.globalIndex(); - auto trackTableThisCollision = hadtracks.sliceBy(mPerCol, collIdx); - auto hypdTableThisCollision = V0Hypers.sliceBy(hypPerCol, collIdx); - trackTableThisCollision.bindExternalIndices(&hadtracks); - hypdTableThisCollision.bindExternalIndices(&V0Hypers); + const int poolBin = eventMixing.settingUseMultiplicityBinning.value + ? multiplicityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.multNTracksPV())) + : centralityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + if (poolBin < 0) { + continue; + } + auto& pool = mMixingPools[poolBin]; + mQaRegistry.fill(HIST("hMixingPoolOccupancy"), pool.size()); - pairTracksSameEventHyper(trackTableThisCollision, hypdTableThisCollision); + for (const auto& bufferedCollision : pool) { + mQaRegistry.fill(HIST("hMixedEventSelections"), 3); + mQaRegistry.fill(HIST("hNcontributor"), collision.numContrib()); + mQaRegistry.fill(HIST("hVtxZ"), collision.posZ()); + + for (const auto& nucleus : currentCollision.nuclei) { + for (const auto& hadron : bufferedCollision.hadrons) { + fillMixedPair(nucleus, hadron, currentCollision); + } + } + for (const auto& nucleus : bufferedCollision.nuclei) { + for (const auto& hadron : currentCollision.hadrons) { + fillMixedPair(nucleus, hadron, bufferedCollision); + } + } + } - if (mTrackHypPairs.size() == 0) { + const int requestedMixingDepth = eventMixing.settingNoMixedEvents.value; + if (requestedMixingDepth <= 0) { continue; } + const auto mixingDepth = static_cast(requestedMixingDepth); + if (pool.size() >= mixingDepth) { + pool.pop_front(); + } + pool.push_back(std::move(currentCollision)); + } + } + PROCESS_SWITCH(HadNucleiFemto, processMixedEvent, "Process Mixed event", false); - fillPairsHyper(collisions, hadtracks, V0Hypers, /*isMixedEvent*/ false); + void run(ProcessingContext& context) + { + if (doprocessMixedEventDiagnostic) { + // TimingInfo is stream-scoped; access it through the active context. + mDiagnosticTiming = context.services().get(); } } - PROCESS_SWITCH(HadNucleiFemto, processSameEventHyper, "Process Same event", false); - void processMixedEvent(const CollisionsFull& collisions, const TrackCandidates& tracks) + void writeMEDiagnostic(const MEDiagnosticRow& row) { - LOG(debug) << "Processing mixed event"; - mTrackPairs.clear(); + mOutputMEDiagnostic(mDiagnosticInstanceHi, mDiagnosticInstanceLo, mDiagnosticCall, + static_cast(mDiagnosticTiming.timeslice), mDiagnosticTiming.tfCounter, mDiagnosticTiming.runNumber, + row.kind, row.status, mDiagnosticEpoch, row.run, row.previousRun, row.eventId, + row.inputCollisionId, row.globalBC, row.timestamp, row.z, row.centrality, row.multiplicity, + row.numContrib, row.nNuclei, row.nHadrons, row.poolBin, row.before, row.after, + mDiagnosticBuffered, static_cast(mDiagnosticPools.size()), + row.expected, row.accepted, row.expected - row.accepted, row.combinations, + row.inputCollisions, row.passedEventSelection, row.passedZorro); + if (row.kind != DiagnosticEventKind) { + mDiagnosticRegistry.fill(HIST("hLifecycle"), row.kind); + } + } + + bool fillDiagnosticMixedPair(const BufferedTrack& nucleus, const BufferedTrack& hadron) + { + // Same CPR predicate and kinematics as fillMixedPair; separate final QA, + // and never write the ordinary pair/multiplicity tables from this process. + if (isClosePair(nucleus, hadron, /*fillQA*/ true)) { + return false; + } + HadNucandidate candidate; + fillBufferedCandidateInfo(nucleus, hadron, candidate); + if (candidate.recoPtNu() > 0) { + if (candidate.isBkgUS) { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_US_M"), candidate.kstar); + } else { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_LS_M"), candidate.kstar); + } + } else if (candidate.isBkgUS) { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_US_A"), candidate.kstar); + } else { + mDiagnosticRegistry.fill(HIST("hMEDiagKStar_LS_A"), candidate.kstar); + } + return true; + } + + void processMixedEventDiagnostic(const CollisionsFullWithPVMult& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps&) + { + const CentralityBinningType centralityBinningPolicy{{axisVertex, axisCentrality}, true}; + const MultiplicityBinningType multiplicityBinningPolicy{{axisVertex, axisMultiplicity}, true}; + ++mDiagnosticCall; + MEDiagnosticRow call; + call.run = mDiagnosticRun; + call.inputCollisions = collisions.size(); + if (mDiagnosticCall == 1) { + call.kind = 0; // Instance initialization, emitted in a valid table context. + writeMEDiagnostic(call); + } + call.kind = 1; + writeMEDiagnostic(call); + LOG(info) << "ME_DIAG BEGIN instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " timeslice=" << mDiagnosticTiming.timeslice << " tfCounter=" << mDiagnosticTiming.tfCounter + << " run=" << mDiagnosticRun << " buffered=" << mDiagnosticBuffered << " bins=" << mDiagnosticPools.size(); + + for (const auto& collision : collisions) { + const auto bc = collision.template bc_as(); + MEDiagnosticRow row; + row.run = bc.runNumber(); + row.inputCollisionId = collision.globalIndex(); + row.globalBC = bc.globalBC(); + row.timestamp = bc.timestamp(); + row.z = collision.posZ(); + row.centrality = collision.centFT0C(); + row.multiplicity = collision.multNTracksPV(); + row.numContrib = collision.numContrib(); + mDiagnosticRegistry.fill(HIST("hSelection"), 0); + if (!passesEventSelection(collision)) { + row.status = 1; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + ++call.passedEventSelection; + mDiagnosticRegistry.fill(HIST("hSelection"), 1); + if (!passesZorroSelection(collision)) { + row.status = 2; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + ++call.passedZorro; + mDiagnosticRegistry.fill(HIST("hSelection"), 2); + // Match the ordinary ME ordering: observe the run before species/bin cuts. + if (mDiagnosticRun != bc.runNumber()) { + MEDiagnosticRow reset = row; + reset.kind = 2; + reset.status = mDiagnosticRun < 0 ? 0 : 1; + reset.previousRun = mDiagnosticRun; + ++mDiagnosticEpoch; + writeMEDiagnostic(reset); // Contains the pre-clear buffered totals. + LOG(info) << "ME_DIAG CLEAR instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " reason=" << (mDiagnosticRun < 0 ? "first-run" : "run-change") + << " oldRun=" << mDiagnosticRun << " newRun=" << bc.runNumber() + << " epoch=" << mDiagnosticEpoch << " buffered=" << mDiagnosticBuffered + << " bins=" << mDiagnosticPools.size(); + mDiagnosticPools.clear(); + mDiagnosticBuffered = 0; + mDiagnosticRun = bc.runNumber(); + } + BufferedCollision current; + current.eventId = mNextDiagnosticEventId++; + current.posZ = collision.posZ(); + current.numContrib = collision.numContrib(); + current.centFT0C = collision.centFT0C(); + current.multFT0C = collision.multFT0C(); + current.multNTracksPV = collision.multNTracksPV(); + row.eventId = current.eventId; + auto tracksThisCollision = tracks.sliceBy(mPerCol, collision.globalIndex()); + tracksThisCollision.bindExternalIndices(&tracks); + for (const auto& track : tracksThisCollision) { + if (selectTrackNu(track) && selectionPIDNu(track)) { + current.nuclei.push_back(makeBufferedTrack(track, true, current.eventId)); + } + if (selectTrackHadron(track) && selectionPIDHadron(track)) { + current.hadrons.push_back(makeBufferedTrack(track, false, current.eventId)); + } + } + row.nNuclei = current.nuclei.size(); + row.nHadrons = current.hadrons.size(); + if (eventMixing.settingRequireBothSpeciesForMixing.value && (current.nuclei.empty() || current.hadrons.empty())) { + row.status = 3; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + mDiagnosticRegistry.fill(HIST("hSelection"), 3); + row.poolBin = eventMixing.settingUseMultiplicityBinning.value + ? multiplicityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.multNTracksPV())) + : centralityBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + if (row.poolBin < 0) { + row.status = 4; + if (meDiagnostic.saveRejectedEvents) { + writeMEDiagnostic(row); + } + continue; + } + mDiagnosticRegistry.fill(HIST("hSelection"), 4); + auto& pool = mDiagnosticPools[row.poolBin]; + row.before = pool.size(); + row.combinations = pool.size(); + mDiagnosticRegistry.fill(HIST("hPoolOccupancy"), row.before); + for (const auto& partner : pool) { + const uint64_t expected = static_cast(current.nuclei.size()) * partner.hadrons.size() + + static_cast(partner.nuclei.size()) * current.hadrons.size(); + uint64_t accepted = 0; + for (const auto& nucleus : current.nuclei) { + for (const auto& hadron : partner.hadrons) { + accepted += fillDiagnosticMixedPair(nucleus, hadron); + } + } + for (const auto& nucleus : partner.nuclei) { + for (const auto& hadron : current.hadrons) { + accepted += fillDiagnosticMixedPair(nucleus, hadron); + } + } + row.expected += expected; + row.accepted += accepted; + if (meDiagnostic.savePartnerLinks) { + mOutputMEDiagnosticLinks(mDiagnosticInstanceHi, mDiagnosticInstanceLo, mDiagnosticCall, + mDiagnosticEpoch, mDiagnosticRun, current.eventId, partner.eventId, row.poolBin, expected, accepted); + } + } + if (pool.size() >= static_cast(eventMixing.settingNoMixedEvents.value)) { + pool.pop_front(); + } else { + ++mDiagnosticBuffered; + } + pool.push_back(std::move(current)); + row.after = pool.size(); + writeMEDiagnostic(row); + call.expected += row.expected; + call.accepted += row.accepted; + call.combinations += row.combinations; + mDiagnosticRegistry.fill(HIST("hPairs"), 0., static_cast(row.expected)); + mDiagnosticRegistry.fill(HIST("hPairs"), 1., static_cast(row.accepted)); + mDiagnosticRegistry.fill(HIST("hPairs"), 2., static_cast(row.expected - row.accepted)); + } + call.kind = 4; + call.run = mDiagnosticRun; + writeMEDiagnostic(call); + mDiagnosticExpected += call.expected; + mDiagnosticAccepted += call.accepted; + mDiagnosticCombinations += call.combinations; + LOG(info) << "ME_DIAG END instance=" << mDiagnosticUUID << " call=" << mDiagnosticCall + << " buffered=" << mDiagnosticBuffered << " bins=" << mDiagnosticPools.size() + << " combinations=" << call.combinations << " expected=" << call.expected << " accepted=" << call.accepted; + } + PROCESS_SWITCH(HadNucleiFemto, processMixedEventDiagnostic, "Independent ME pool lifecycle diagnostic (run alone)", false); + + void endOfStream(EndOfStreamContext&) + { + if (doprocessMixedEventDiagnostic) { + // EOS table writes are not guaranteed to be flushed; final snapshot in log. + LOG(info) << "ME_DIAG EOS instance=" << mDiagnosticUUID << " calls=" << mDiagnosticCall + << " epochs=" << mDiagnosticEpoch << " buffered=" << mDiagnosticBuffered + << " bins=" << mDiagnosticPools.size() << " combinations=" << mDiagnosticCombinations + << " expected=" << mDiagnosticExpected << " accepted=" << mDiagnosticAccepted; + } + } - for (const auto& [c1, tracks1, c2, tracks2] : mPair) { - if (!c1.sel8() || !c2.sel8()) { + // Produce the data distributions fitted offline with the MC templates. All + // nominal quality and PID selections are applied; only DCA is relaxed. + void processDcaFractionData(const CollisionsFull& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps& bcs) + { + for (const auto& collision : collisions) { + if (!selectCollision(collision, bcs)) { continue; } - mQaRegistry.fill(HIST("hNcontributor"), c1.numContrib()); - mQaRegistry.fill(HIST("hVtxZ"), c1.posZ()); + const uint64_t collIdx = collision.globalIndex(); + auto tracksThisCollision = tracks.sliceBy(mPerCol, collIdx); + tracksThisCollision.bindExternalIndices(&tracks); + for (const auto& track : tracksThisCollision) { + if (selectHadronTrackForDcaFit(track) && selectionPIDHadron(track) && + std::abs(track.dcaXY()) <= fractionPurity.settingHadronDcaFitAbsMax.value && + std::abs(track.dcaZ()) <= fractionPurity.settingHadronDcaFitAbsMax.value) { + mQaRegistry.fill(HIST("fraction/hDcaDataHad"), signedPhysicalPt(track, false), track.dcaXY(), track.dcaZ(), collision.centFT0C()); + } - pairTracksEventMixing(tracks1, tracks2); - pairTracksEventMixing(tracks2, tracks1); + if (selectNucleusTrackForDcaFit(track) && selectionPIDNuForDcaFit(track) && + std::abs(track.dcaXY()) <= fractionPurity.settingNucleusDcaFitAbsMax.value && + std::abs(track.dcaZ()) <= fractionPurity.settingNucleusDcaFitAbsMax.value) { + mQaRegistry.fill(HIST("fraction/hDcaDataNu"), signedPhysicalPt(track, true), track.dcaXY(), track.dcaZ(), collision.centFT0C()); + } + } } + } + PROCESS_SWITCH(HadNucleiFemto, processDcaFractionData, "Produce data DCA-fraction fit inputs", false); - fillPairs(collisions, tracks, /*isMixedEvent*/ true); + template + bool truthBelongsToRecoCollision(const Ttrack&, const Tparticle& particle, const Tcollision& collision) const + { + return collision.has_mcCollision() && particle.mcCollisionId() == collision.mcCollisionId(); } - PROCESS_SWITCH(HadNucleiFemto, processMixedEvent, "Process Mixed event", false); - /*void processMixedEventHyper(const CollisionsFull& collisions, o2::aod::DataHypCandsWColl const& V0Hypers, const TrackCandidates& hadtracks) + template + void fillMCDcaMisidentified(const Ttrack& track, float centrality, bool isNucleus, bool matchesRecoCollision) { - LOG(debug) << "Processing mixed event for hypertriton"; - mTrackHypPairs.clear(); + const float signedPt = signedPhysicalPt(track, isNucleus); + const float collisionAssociation = matchesRecoCollision ? static_cast(CorrectCollision) : static_cast(WrongCollision); + if (isNucleus) { + mQaRegistry.fill(HIST("fraction/hDcaMisidentifiedNu"), signedPt, track.dcaXY(), track.dcaZ(), centrality, collisionAssociation); + } else { + mQaRegistry.fill(HIST("fraction/hDcaMisidentifiedHad"), signedPt, track.dcaXY(), track.dcaZ(), centrality, collisionAssociation); + } + } - for (const auto& [c1, tracks1, c2, V0Hypers2] : hyperPair) { - if (!c1.sel8() || !c2.sel8()) { - continue; + template + void fillMCDcaNoLabel(const Ttrack& track, float centrality, bool isNucleus) + { + const float signedPt = signedPhysicalPt(track, isNucleus); + if (isNucleus) { + mQaRegistry.fill(HIST("fraction/hDcaNoMCLabelNu"), signedPt, track.dcaXY(), track.dcaZ(), centrality); + } else { + mQaRegistry.fill(HIST("fraction/hDcaNoMCLabelHad"), signedPt, track.dcaXY(), track.dcaZ(), centrality); + } + } + + template + void fillMCDcaTemplate(const Ttrack& track, const Tparticle& particle, float centrality, bool isNucleus, bool matchesRecoCollision) + { + const int origin = classifyDcaOrigin(particle); + const int collisionAssociation = matchesRecoCollision ? CorrectCollision : WrongCollision; + const int parentCategory = classifyParentCategory(particle, origin); + const float productionRadius = std::hypot(particle.vx(), particle.vy()); + const float signedPt = signedPhysicalPt(track, isNucleus); + + // These response matrices use the relaxed DCA-fit selection under which + // this function is called. Keep only correct-collision He3 or triton so + // the reconstructed numerators match the selected-MC-collision generator + // denominators filled below. Hypertriton responses are conditional on the + // explicit He3-pion or triton-pi0 channel; physical branching ratios are + // deliberately not folded into these histograms. + const int absoluteParticlePdg = std::abs(particle.pdgCode()); + if (isNucleus && matchesRecoCollision && (absoluteParticlePdg == He3PDG || absoluteParticlePdg == TritonPDG)) { + const float signedGeneratedPt = particle.pdgCode() > 0 ? particle.pt() : -particle.pt(); + const float generatedRapidity = particle.y(); + if (origin == Primary) { + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hPrimaryTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + } + } else if (origin == WeakDecay && particle.has_mothers()) { + for (const auto& mother : particle.template mothers_as()) { + if (std::abs(mother.pdgCode()) != HyperTritonPDG || !mother.isPhysicalPrimary() || + ((mother.pdgCode() > 0) != (particle.pdgCode() > 0))) { + continue; + } + const int expectedPionPdg = absoluteParticlePdg == He3PDG + ? (mother.pdgCode() > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus) + : PDG_t::kPi0; + bool hasExpectedPion = false; + for (const auto& daughter : mother.template daughters_as()) { + if (daughter.pdgCode() == expectedPionPdg) { + hasExpectedPion = true; + break; + } + } + if (!hasExpectedPion) { + continue; + } + const float signedParentPt = mother.pdgCode() > 0 ? mother.pt() : -mother.pt(); + if (absoluteParticlePdg == He3PDG) { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterHe3GenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } else { + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonDaughterTritonGenPtVsRecoPt"), signedGeneratedPt, signedPt, generatedRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCReco/hHypertritonParentPtVsDaughterTritonRecoPt"), signedParentPt, signedPt, mother.y(), generatedRapidity, centrality); + } + break; + } + } + } + + // The detailed histogram is always filled for a truth-PDG-matched track, + // including wrong-collision associations, so tighter choices can be made + // offline without rerunning the table producer. + if (isNucleus) { + mQaRegistry.fill(HIST("fraction/hDcaTemplateDetailNu"), signedPt, track.dcaXY(), track.dcaZ(), centrality, static_cast(origin), static_cast(collisionAssociation), static_cast(parentCategory), productionRadius); + } else { + mQaRegistry.fill(HIST("fraction/hDcaTemplateDetailHad"), signedPt, track.dcaXY(), track.dcaZ(), centrality, static_cast(origin), static_cast(collisionAssociation), static_cast(parentCategory), productionRadius); + } + + const auto fillMotherPdg = [&](int motherPdg) { + const int motherSign = (motherPdg > 0) - (motherPdg < 0); + const int absoluteMotherPdg = std::abs(motherPdg); + const int motherPdgHigh = absoluteMotherPdg / MotherPdgChunkBase; + const int motherPdgLow = absoluteMotherPdg % MotherPdgChunkBase; + if (motherPdgHigh > MotherPdgHighMax) { + LOG(warning) << "Direct-mother PDG " << motherPdg << " exceeds the configured exact-PDG histogram range"; + return; + } + if (isNucleus) { + mQaRegistry.fill(HIST("fraction/hDcaMotherPdgNu"), signedPt, track.dcaXY(), track.dcaZ(), centrality, static_cast(origin), static_cast(collisionAssociation), static_cast(motherSign), static_cast(motherPdgHigh), static_cast(motherPdgLow)); + } else { + mQaRegistry.fill(HIST("fraction/hDcaMotherPdgHad"), signedPt, track.dcaXY(), track.dcaZ(), centrality, static_cast(origin), static_cast(collisionAssociation), static_cast(motherSign), static_cast(motherPdgHigh), static_cast(motherPdgLow)); } + }; - mQaRegistry.fill(HIST("hNcontributor"), c2.numContrib()); - //mQaRegistry.fill(HIST("hCentrality"), c2.centFT0C()); - mQaRegistry.fill(HIST("hVtxZ"), c2.posZ()); + if (particle.has_mothers()) { + for (const auto& mother : particle.template mothers_as()) { + fillMotherPdg(mother.pdgCode()); + } + } else { + fillMotherPdg(0); + } - pairHyperEventMixing(tracks1, V0Hypers2); + // This is the stable three-component Giorgio template. The collision-match + // configurable preserves the previous strict/relaxed behavior. + if (fractionPurity.settingRequireRecoMCCollisionMatch.value && !matchesRecoCollision) { + return; } -} -PROCESS_SWITCH(HadNucleiFemto, processMixedEventHyper, "Process Mixed event", false);*/ + if (isNucleus) { + mQaRegistry.fill(HIST("fraction/hDcaTemplateNu"), signedPt, track.dcaXY(), track.dcaZ(), centrality, static_cast(origin)); + } else { + mQaRegistry.fill(HIST("fraction/hDcaTemplateHad"), signedPt, track.dcaXY(), track.dcaZ(), centrality, static_cast(origin)); + } + } + + template + void fillMCPurityComposition(const Ttrack& track, const Tparticle& particle, float centrality, bool isNucleus, bool correctSpecies, bool matchesRecoCollision) + { + const float signedPt = signedPhysicalPt(track, isNucleus); + const auto fillCategory = [&](int category) { + if (isNucleus) { + mQaRegistry.fill(HIST("purityMC/hNucleus"), signedPt, static_cast(category), centrality); + } else { + mQaRegistry.fill(HIST("purityMC/hHadron"), signedPt, static_cast(category), centrality); + } + }; + fillCategory(AllSelected); + if (!correctSpecies) { + fillCategory(MisidentifiedSpecies); + return; + } + fillCategory(CorrectSpecies); + if (!matchesRecoCollision) { + fillCategory(WrongCollisionSpecies); + return; + } + fillCategory(CorrectCollisionSpecies); + const int origin = classifyDcaOrigin(particle); + fillCategory(purityOriginCategory(origin)); + } - void processMixedEventHyperPool(const CollisionsFull& collisions, o2::aod::DataHypCandsWColl const& V0Hypers, const TrackCandidates& hadtracks) + template + void fillMCNoLabelPurity(const Ttrack& track, float centrality, bool isNucleus) { - mTrackHypPairs.clear(); - if (!isInitialized) { - initializePools(); - LOG(info) << "Initialized event pool with size = " << All_Event_pool.size(); + const float signedPt = signedPhysicalPt(track, isNucleus); + if (isNucleus) { + mQaRegistry.fill(HIST("purityMC/hNucleus"), signedPt, static_cast(AllSelected), centrality); + mQaRegistry.fill(HIST("purityMC/hNucleus"), signedPt, static_cast(NoMCLabel), centrality); + } else { + mQaRegistry.fill(HIST("purityMC/hHadron"), signedPt, static_cast(AllSelected), centrality); + mQaRegistry.fill(HIST("purityMC/hHadron"), signedPt, static_cast(NoMCLabel), centrality); } - for (auto const& collision : collisions) { - if (!collision.sel8()) { - mQaRegistry.fill(HIST("hSkipReasons"), 0); + } + + using SelectedMcCollisionInfo = std::unordered_map>; + + void fillMCGeneratorNucleiFeedDown(const aod::McParticles& mcParticles, const SelectedMcCollisionInfo& selectedMcCollisions) + { + for (const auto& particle : mcParticles) { + const auto collision = selectedMcCollisions.find(particle.mcCollisionId()); + if (collision == selectedMcCollisions.end()) { continue; } - mQaRegistry.fill(HIST("hNcontributor"), collision.numContrib()); - mQaRegistry.fill(HIST("hCentrality"), collision.centFT0C()); - mQaRegistry.fill(HIST("hVtxZ"), collision.posZ()); + const float centrality = collision->second.second; + const int particlePdg = particle.pdgCode(); + const int absolutePdg = std::abs(particlePdg); + const float signedPt = particlePdg > 0 ? particle.pt() : -particle.pt(); - int poolIndexHad = where_pool(collision.posZ(), collision.centFT0C()); - if (poolIndexHad < 0 || static_cast(poolIndexHad) >= All_Event_pool.size()) { + if (absolutePdg == He3PDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryHe3"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg == TritonPDG && particle.isPhysicalPrimary()) { + mQaRegistry.fill(HIST("fractionMCGen/hPrimaryTriton"), signedPt, particle.y(), centrality); + continue; + } + if (absolutePdg != HyperTritonPDG || !particle.isPhysicalPrimary()) { continue; } - auto& pool = All_Event_pool[poolIndexHad]; - - const uint64_t collIdxHad = collision.globalIndex(); - auto trackTableThisCollision = hadtracks.sliceBy(mPerCol, collIdxHad); - trackTableThisCollision.bindExternalIndices(&hadtracks); - for (auto const& storedEvent : pool.events) { - const uint64_t collIdxHyp = storedEvent.collisionId; - if (settingSaferME) { - if (static_cast(collIdxHyp) > collisions.size()) { - mQaRegistry.fill(HIST("hSkipReasons"), 4); - continue; - } + mQaRegistry.fill(HIST("fractionMCGen/hHypertriton"), signedPt, particle.y(), centrality); + + const int expectedHe3Pdg = particlePdg > 0 ? He3PDG : -He3PDG; + const int expectedTritonPdg = particlePdg > 0 ? TritonPDG : -TritonPDG; + const int expectedChargedPionPdg = particlePdg > 0 ? -PDG_t::kPiPlus : PDG_t::kPiPlus; + bool hasExpectedHe3 = false; + bool hasExpectedTriton = false; + bool hasExpectedChargedPion = false; + bool hasPi0 = false; + float signedHe3Pt = 0.f; + float he3Rapidity = 0.f; + float signedTritonPt = 0.f; + float tritonRapidity = 0.f; + for (const auto& daughter : particle.template daughters_as()) { + if (daughter.pdgCode() == expectedHe3Pdg) { + hasExpectedHe3 = true; + signedHe3Pt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + he3Rapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedTritonPdg) { + hasExpectedTriton = true; + signedTritonPt = daughter.pdgCode() > 0 ? daughter.pt() : -daughter.pt(); + tritonRapidity = daughter.y(); + } else if (daughter.pdgCode() == expectedChargedPionPdg) { + hasExpectedChargedPion = true; + } else if (daughter.pdgCode() == PDG_t::kPi0) { + hasPi0 = true; } + } - auto hypdTablepreviousCollision = V0Hypers.sliceBy(hypPerCol, collIdxHyp); - hypdTablepreviousCollision.bindExternalIndices(&V0Hypers); - if (hypdTablepreviousCollision.size() == 0) { - mQaRegistry.fill(HIST("hSkipReasons"), 1); - continue; + if (hasExpectedHe3 && hasExpectedChargedPion) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToHe3Pi"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterHe3"), signedHe3Pt, he3Rapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterHe3Pt"), signedPt, signedHe3Pt, particle.y(), he3Rapidity, centrality); + } + if (hasExpectedTriton && hasPi0) { + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonToTritonPi0"), signedPt, particle.y(), centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonDaughterTriton"), signedTritonPt, tritonRapidity, centrality); + mQaRegistry.fill(HIST("fractionMCGen/hHypertritonPtVsDaughterTritonPt"), signedPt, signedTritonPt, particle.y(), tritonRapidity, centrality); + } + } + } + + // MC DCA templates use the relaxed-DCA selection. MC purity uses the full + // nominal candidate selection, including its DCA requirement. + void processDcaFractionPurityMC(const CollisionsFullMC& collisions, const TrackCandidatesMCDca& tracks, const aod::McParticles& mcParticles, const aod::BCsWithTimestamps& bcs) + { + SelectedMcCollisionInfo selectedMcCollisions; + for (const auto& collision : collisions) { + if (!selectCollision(collision, bcs)) { + continue; + } + + if (collision.has_mcCollision()) { + const int64_t mcCollisionId = collision.mcCollisionId(); + const int numberOfContributors = collision.numContrib(); + const auto storedCollision = selectedMcCollisions.find(mcCollisionId); + if (storedCollision == selectedMcCollisions.end() || numberOfContributors > storedCollision->second.first) { + selectedMcCollisions[mcCollisionId] = {numberOfContributors, collision.centFT0C()}; } + } - auto firstHyp = hypdTablepreviousCollision.iteratorAt(0); - int poolIndexHyp = where_pool(firstHyp.zPrimVtx(), firstHyp.centralityFT0C()); - if (poolIndexHyp != poolIndexHad) { - mQaRegistry.fill(HIST("hSkipReasons"), 2); + const uint64_t collIdx = collision.globalIndex(); + auto tracksThisCollision = tracks.sliceBy(mPerColMC, collIdx); + tracksThisCollision.bindExternalIndices(&tracks); + for (const auto& track : tracksThisCollision) { + const bool selectedHadronForFraction = selectHadronTrackForDcaFit(track) && selectionPIDHadron(track) && + std::abs(track.dcaXY()) <= fractionPurity.settingHadronDcaFitAbsMax.value && + std::abs(track.dcaZ()) <= fractionPurity.settingHadronDcaFitAbsMax.value; + const bool selectedNucleusForFraction = selectNucleusTrackForDcaFit(track) && selectionPIDNuForDcaFit(track) && + std::abs(track.dcaXY()) <= fractionPurity.settingNucleusDcaFitAbsMax.value && + std::abs(track.dcaZ()) <= fractionPurity.settingNucleusDcaFitAbsMax.value; + + const bool selectedHadronForPurity = selectTrackHadron(track) && selectionPIDHadron(track); + const bool selectedNucleusForPurity = selectTrackNu(track) && selectionPIDNu(track); + + if (!track.has_mcParticle()) { + if (selectedHadronForFraction) { + fillMCDcaNoLabel(track, collision.centFT0C(), false); + } + if (selectedNucleusForFraction) { + fillMCDcaNoLabel(track, collision.centFT0C(), true); + } + if (selectedHadronForPurity) { + fillMCNoLabelPurity(track, collision.centFT0C(), false); + } + if (selectedNucleusForPurity) { + fillMCNoLabelPurity(track, collision.centFT0C(), true); + } continue; } - mQaRegistry.fill(HIST("hNHypsPerPrevColl"), collIdxHyp, hypdTablepreviousCollision.size()); - pairHyperEventMixing(trackTableThisCollision, hypdTablepreviousCollision); - } + const auto particle = track.template mcParticle_as(); + const bool matchesRecoCollision = truthBelongsToRecoCollision(track, particle, collision); + const int expectedHadronPdg = track.sign() >= 0 ? std::abs(species.settingHadPDGCode.value) : -std::abs(species.settingHadPDGCode.value); + const int expectedNucleusPdg = track.sign() >= 0 ? std::abs(species.settingNuPDGCode.value) : -std::abs(species.settingNuPDGCode.value); + const bool correctHadronSpecies = particle.pdgCode() == expectedHadronPdg; + const bool correctNucleusSpecies = particle.pdgCode() == expectedNucleusPdg; + + if (selectedHadronForPurity) { + fillMCPurityComposition(track, particle, collision.centFT0C(), false, correctHadronSpecies, matchesRecoCollision); + } + if (selectedNucleusForPurity) { + fillMCPurityComposition(track, particle, collision.centFT0C(), true, correctNucleusSpecies, matchesRecoCollision); + } - if (static_cast(pool.events.size()) >= settingNoMixedEvents) { - pool.events.pop_front(); + if (selectedHadronForFraction) { + if (correctHadronSpecies) { + fillMCDcaTemplate(track, particle, collision.centFT0C(), false, matchesRecoCollision); + } else { + fillMCDcaMisidentified(track, collision.centFT0C(), false, matchesRecoCollision); + } + } + if (selectedNucleusForFraction) { + if (correctNucleusSpecies) { + fillMCDcaTemplate(track, particle, collision.centFT0C(), true, matchesRecoCollision); + } else { + fillMCDcaMisidentified(track, collision.centFT0C(), true, matchesRecoCollision); + } + } } - pool.events.push_back({collIdxHad}); } - fillPairsHyper(collisions, hadtracks, V0Hypers, /*isMixedEvent*/ true); + fillMCGeneratorNucleiFeedDown(mcParticles, selectedMcCollisions); } - PROCESS_SWITCH(HadNucleiFemto, processMixedEventHyperPool, "Process Mixed event", false); + PROCESS_SWITCH(HadNucleiFemto, processDcaFractionPurityMC, "Produce MC DCA templates and truth-purity counters", false); void processPurity(const CollisionsFull& collisions, const TrackCandidates& tracks, const aod::BCsWithTimestamps& bcs) { - o2::aod::ITSResponse itsResponse; - for (const auto& collision : collisions) { if (!selectCollision(collision, bcs)) { continue; @@ -1364,59 +4013,144 @@ PROCESS_SWITCH(HadNucleiFemto, processMixedEventHyper, "Process Mixed event", fa trackTableThisCollision.bindExternalIndices(&tracks); for (const auto& track : trackTableThisCollision) { + const bool passTrackHad = selectTrackHadron(track); + const bool passTrackNu = selectTrackNu(track); + mQaRegistry.fill(HIST("hTrackSel"), Selections::kNoCuts); + if (passTrackHad) { + mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); + } - if (!selectTrack(track)) { - continue; + fillNucleusTrackSelection(Selections::kNoCuts); + if (passTrackNu) { + fillNucleusTrackSelection(Selections::kTrackCuts); } - mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); - if (settingHadPDGCode == PDG_t::kPiPlus) { + if (passTrackHad && species.settingHadPDGCode == PDG_t::kPiPlus) { const float tpcNSigmaHad = track.tpcNSigmaPi(); mQaRegistry.fill(HIST("purity/h2NsigmaHadTPC_preselection"), track.sign() * track.pt(), tpcNSigmaHad); - if (track.hasTOF() && track.tpcInnerParam() >= settingCutPinMinTOFHad) { + if (track.hasTOF() && std::abs(track.p()) > hadronPid.settingPionMomCombMin) { const float tofNSigmaHad = track.tofNSigmaPi(); + const float combNsigmaHad = std::sqrt(tofNSigmaHad * tofNSigmaHad + tpcNSigmaHad * tpcNSigmaHad); mQaRegistry.fill(HIST("purity/h2NsigmaHadTOF_preselection"), track.sign() * track.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("purity/h2NsigmaHadComb_preselection"), track.sign() * track.pt(), combNsigmaHad); } - } else if (settingHadPDGCode == PDG_t::kKPlus) { + } else if (passTrackHad && species.settingHadPDGCode == PDG_t::kKPlus) { const float tpcNSigmaHad = track.tpcNSigmaKa(); mQaRegistry.fill(HIST("purity/h2NsigmaHadTPC_preselection"), track.sign() * track.pt(), tpcNSigmaHad); - if (track.hasTOF() && track.tpcInnerParam() >= settingCutPinMinTOFHad) { + if (track.hasTOF() && track.tpcInnerParam() >= hadronPid.settingCutPinMinTOFHad) { const float tofNSigmaHad = track.tofNSigmaKa(); + const float combNsigmaHad = std::sqrt(tofNSigmaHad * tofNSigmaHad + tpcNSigmaHad * tpcNSigmaHad); + mQaRegistry.fill(HIST("purity/h2NsigmaHadTOF_preselection"), track.sign() * track.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("purity/h2NsigmaHadComb_preselection"), track.sign() * track.pt(), combNsigmaHad); + } + } else if (passTrackHad && species.settingHadPDGCode == PDG_t::kProton) { + constexpr float protonPCombMin = 0.75f; + const float tpcNSigmaHad = track.tpcNSigmaPr(); + mQaRegistry.fill(HIST("purity/h2NsigmaHadTPC_preselection"), track.sign() * track.pt(), tpcNSigmaHad); + if (track.hasTOF() && std::abs(track.tpcInnerParam()) >= protonPCombMin) { + const float tofNSigmaHad = track.tofNSigmaPr(); + const float combNsigmaHad = std::sqrt(tofNSigmaHad * tofNSigmaHad + tpcNSigmaHad * tpcNSigmaHad); mQaRegistry.fill(HIST("purity/h2NsigmaHadTOF_preselection"), track.sign() * track.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("purity/h2NsigmaHadComb_preselection"), track.sign() * track.pt(), combNsigmaHad); } } - const float tpcNSigmaDe = settingUseBBcomputeDeNsigma ? computeNSigmaDe(track) : track.tpcNSigmaDe(); - mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), track.sign() * track.pt(), tpcNSigmaDe); - mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselecComp"), track.sign() * track.pt(), track.tpcNSigmaDe()); - if (track.hasTOF() && track.tpcInnerParam() > settingCutPinMinTOFITSDe) { - const float tofNSigmaDe = track.tofNSigmaDe(); - mQaRegistry.fill(HIST("purity/h2NsigmaNuTOF_preselection"), track.sign() * track.pt(), tofNSigmaDe); - } else if (track.tpcInnerParam() <= settingCutPinMinTOFITSDe) { - const float itsNSigmaDe = itsResponse.nSigmaITS(track.itsClusterSizes(), track.p(), track.eta()); - mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), track.sign() * track.pt(), itsNSigmaDe); + if (passTrackNu && useDeuteronNucleus()) { + const float tpcNSigmaDe = output.settingUseBBcomputeDeNsigma ? computeNSigmaDe(track) : track.tpcNSigmaDe(); + const float absTPCInnerParam = std::abs(track.tpcInnerParam()); + if (absTPCInnerParam > deuteronPid.settingCutPinMinTOFITSDe) { + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), track.sign() * track.pt(), tpcNSigmaDe); + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselecComp"), track.sign() * track.pt(), track.tpcNSigmaDe()); + if (track.hasTOF()) { + const float tofNSigmaDe = track.tofNSigmaDe(); + const float combNsigmaDe = std::sqrt(tofNSigmaDe * tofNSigmaDe + tpcNSigmaDe * tpcNSigmaDe); + mQaRegistry.fill(HIST("purity/h2NsigmaNuTOF_preselection"), track.sign() * track.pt(), tofNSigmaDe); + mQaRegistry.fill(HIST("purity/h2NsigmaNuComb_preselection"), track.sign() * track.pt(), combNsigmaDe); + } + } else { + o2::aod::ITSResponse itsResponse; + const float itsNSigmaDe = itsResponse.nSigmaITS(track.itsClusterSizes(), track.p(), track.eta()); + mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), track.sign() * track.pt(), itsNSigmaDe); + if (std::abs(itsNSigmaDe) <= deuteronPid.settingCutNsigmaITSDe) { + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), track.sign() * track.pt(), tpcNSigmaDe); + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselecComp"), track.sign() * track.pt(), track.tpcNSigmaDe()); + } + } + } else if (passTrackNu && useHelium3Nucleus()) { + const float tpcNSigmaHe3 = computeNSigmaHe3(track); + const float signedPtHe3 = track.sign() * 2.f * track.pt(); + o2::aod::ITSResponse itsResponse; + const float itsNSigmaHe3 = itsResponse.nSigmaITS(track.itsClusterSizes(), track.p(), track.eta()); + mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), signedPtHe3, itsNSigmaHe3); + if (itsNSigmaHe3 >= helium3Pid.settingITSNSigmaMinHe3) { + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtHe3, tpcNSigmaHe3); + } + } else if (passTrackNu && useTritonNucleus()) { + const float tpcNSigmaTr = track.tpcNSigmaTr(); + const float absPTr = std::abs(track.p()); + const float signedPtTr = track.sign() * track.pt(); + const bool passTPCRejectTr = !(std::abs(track.tpcNSigmaDe()) < tritonPid.settingTPCRejectNsig || + std::abs(track.tpcNSigmaPr()) < tritonPid.settingTPCRejectNsig || + std::abs(track.tpcNSigmaPi()) < tritonPid.settingTPCRejectNsig); + o2::aod::ITSResponse itsResponse; + const float itsNSigmaTr = itsResponse.nSigmaITS(track.itsClusterSizes(), absPTr, track.eta()); + if (passTPCRejectTr) { + mQaRegistry.fill(HIST("purity/h2NSigmaNuITS_preselection"), signedPtTr, itsNSigmaTr); + } + const bool passITSTr = absPTr >= tritonPid.settingPIDMomentumSplitTr || + std::abs(itsNSigmaTr) < tritonPid.settingCutITSNsigmaLowPTr; + const bool passTPCTr = absPTr < tritonPid.settingPIDMomentumSplitTr + ? std::abs(tpcNSigmaTr) < tritonPid.settingCutTPCNsigmaLowPTr + : tpcNSigmaTr > tritonPid.settingCutTPCNsigmaHighPMinTr && + tpcNSigmaTr < tritonPid.settingCutTPCNsigmaHighPMaxTr; + // Keep triton TPC PID open, while matching the species-rejection and ITS cuts. + if (passTPCRejectTr && passITSTr) { + mQaRegistry.fill(HIST("purity/h2NsigmaNuTPC_preselection"), signedPtTr, tpcNSigmaTr); + // Apply the triton TPC PID cut but keep TOF mass uncut to retain its sidebands. + if (passTPCTr && absPTr > tritonPid.settingTOFMassMomentumMinTr && track.hasTOF()) { + const float massTOFTr = computeTOFMass(track); + if (std::isfinite(massTOFTr)) { + mQaRegistry.fill(HIST("purity/h2MassTOFTr_preselection"), signedPtTr, massTOFTr); + } + } + } } - const bool isHadronSelected = selectionPIDHadron(track); - const bool isDeuteronSelected = selectionPIDDe(track); - if (!isHadronSelected && !isDeuteronSelected) { + const bool isHadronSelected = passTrackHad && selectionPIDHadron(track); + const bool isNucleusSelected = passTrackNu && selectionPIDNu(track); + if (!isHadronSelected && !isNucleusSelected) { continue; } - mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); if (isHadronSelected) { + mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); mQaRegistry.fill(HIST("hSingleHadPt"), track.pt() * track.sign()); } - if (isDeuteronSelected) { - mQaRegistry.fill(HIST("hSingleNuPt"), track.pt() * track.sign()); - mQaRegistry.fill(HIST("hSingleNuPin"), track.tpcInnerParam() * track.sign()); + if (isNucleusSelected) { + fillNucleusTrackSelection(Selections::kPID); + mQaRegistry.fill(HIST("hSingleNuPt"), track.pt() * track.sign() * nucleusChargeFactor()); + mQaRegistry.fill(HIST("hSingleNuPin"), (useHelium3Nucleus() ? correctedTPCInnerParamHe3(track) : track.tpcInnerParam()) * track.sign()); } } } } - PROCESS_SWITCH(HadNucleiFemto, processPurity, "Process for pion and deuteron purity QA", false); + PROCESS_SWITCH(HadNucleiFemto, processPurity, "Process for hadron and nucleus purity QA", false); + + void processHyper(const HadHyperCollisionsFull& collisions, const TrackCandidates& tracks, + const HyperCandidates& candidates, const aod::BCsWithTimestamps& bcs) + { + processHyperPairs(collisions, tracks, candidates, bcs, mHyperMixingPools, mHyperMixingRunNumber); + } + PROCESS_SWITCH(HadNucleiFemto, processHyper, "Process same-event and mixed-event hadron-hypertriton pairs", false); + + void processMCHyper(const HadHyperCollisionsFullMC& collisions, const TrackCandidatesMC& tracks, + const HyperCandidatesMC& candidates, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) + { + processHyperPairs(collisions, tracks, candidates, bcs, mHyperMCMixingPools, mHyperMCMixingRunNumber); + } + PROCESS_SWITCH(HadNucleiFemto, processMCHyper, "Process MC same-event and mixed-event hadron-hypertriton pairs", false); }; WorkflowSpec defineDataProcessing(const ConfigContext& cfgc) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt b/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt new file mode 100644 index 00000000000..9709ae21578 --- /dev/null +++ b/PWGCF/Femto/FemtoNuclei/Tasks/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2026 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(pi-de-femto-systematics + SOURCES piDeFemtoSystematics.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx new file mode 100644 index 00000000000..08328b97ca9 --- /dev/null +++ b/PWGCF/Femto/FemtoNuclei/Tasks/piDeFemtoSystematics.cxx @@ -0,0 +1,858 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file piDeFemtoSystematics.cxx +/// \brief Histogram-only pion-deuteron femtoscopy task for train subwagon cut variations +/// +/// The task intentionally produces no derived AOD tables. A train subwagon +/// supplies one complete set of cuts. The default wagon and every systematic +/// subwagon therefore run the same event and pair workflow, while a systematic +/// subwagon changes exactly one configurable cut. + +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using CollisionsFull = soa::Join; +using TrackCandidates = soa::Join; + +namespace +{ +constexpr std::array BetheBlochDeDefault{-136.71, 0.441, 0.2269, 1.347, 0.8035, 0.09}; +const std::vector betheBlochParticleNames{"De"}; +const std::vector betheBlochParameterNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; +constexpr std::array tpcRadii{{85.f, 105.f, 125.f, 145.f, 165.f, 185.f, 205.f, 225.f, 245.f}}; +constexpr int NumberOfBetheBlochShapeParameters = 5; +constexpr int BetheBlochResolutionIndex = 5; +constexpr double AutomaticMagneticFieldThreshold = -998.; +constexpr float NominalL3Current = 30000.f; +constexpr float InvalidPhiStar = 999.f; +constexpr int ClosePairRadiusModeAtPV = 0; +constexpr int ClosePairRadiusModeAtSpecificRadius = 2; +using PairLorentzVector = ROOT::Math::LorentzVector>; + +enum SelectionStep { + kAllTracks = 0, + kTrackCuts, + kPidCuts, + kNSelectionSteps +}; + +enum PairChannel { + kLikeSignMatter = 0, + kLikeSignAntimatter, + kUnlikeSignMatter, + kUnlikeSignAntimatter, + kNPairChannels +}; +} // namespace + +struct PiDeFemtoSystematics { + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"event"}; + Configurable vertexZMax{"vertexZMax", 10.f, "Maximum absolute collision z vertex"}; + Configurable numberOfMixedEvents{"numberOfMixedEvents", 5, "Number of mixed events per event"}; + } eventCuts; + + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"pion"}; + Configurable ptMin{"ptMin", 0.14f, "Minimum pion pT"}; + Configurable ptMax{"ptMax", 4.0f, "Maximum pion pT"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute pion eta"}; + Configurable itsInnerBarrelClustersMin{"itsInnerBarrelClustersMin", 3, "Minimum pion ITS inner-barrel clusters"}; + Configurable itsClustersMin{"itsClustersMin", 7, "Minimum pion ITS clusters"}; + Configurable tpcClustersMin{"tpcClustersMin", 80, "Minimum pion found TPC clusters"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 90, "Minimum pion crossed TPC rows"}; + Configurable dcaXYOffset{"dcaXYOffset", 0.004f, "Pion DCAxy cut offset"}; + Configurable dcaXYPtCoefficient{"dcaXYPtCoefficient", 0.013f, "Pion DCAxy inverse-pT coefficient"}; + Configurable dcaZOffset{"dcaZOffset", 0.004f, "Pion DCAz cut offset"}; + Configurable dcaZPtCoefficient{"dcaZPtCoefficient", 0.013f, "Pion DCAz inverse-pT coefficient"}; + Configurable combinedPidMomentumMin{"combinedPidMomentumMin", 0.5f, "Momentum above which pion TPC+TOF PID is required"}; + Configurable tpcNsigmaMax{"tpcNsigmaMax", 3.f, "Maximum absolute pion TPC n-sigma below the TOF threshold"}; + Configurable combinedNsigmaMax{"combinedNsigmaMax", 3.f, "Maximum pion combined TPC+TOF n-sigma"}; + } pionCuts; + + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"deuteron"}; + Configurable ptMin{"ptMin", 0.6f, "Minimum deuteron pT"}; + Configurable ptMax{"ptMax", 2.0f, "Maximum deuteron pT"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute deuteron eta"}; + Configurable tpcClustersMin{"tpcClustersMin", 110, "Minimum deuteron found TPC clusters"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 100, "Minimum deuteron crossed TPC rows"}; + Configurable itsClustersMin{"itsClustersMin", 5, "Minimum deuteron ITS clusters"}; + Configurable itsInnerBarrelClustersMin{"itsInnerBarrelClustersMin", 1, "Minimum deuteron ITS inner-barrel clusters"}; + Configurable sharedTPCClustersMax{"sharedTPCClustersMax", 160, "Maximum deuteron shared TPC clusters"}; + Configurable sharedTPCFractionMax{"sharedTPCFractionMax", 1.f, "Maximum deuteron shared TPC-cluster fraction"}; + Configurable tpcInnerParamMin{"tpcInnerParamMin", 0.f, "Minimum deuteron TPC inner parameter"}; + Configurable tofMomentumMin{"tofMomentumMin", 1.2f, "TPC inner parameter above which combined TPC+TOF PID is required"}; + Configurable combinedNsigmaMax{"combinedNsigmaMax", 2.5f, "Maximum deuteron combined TPC+TOF n-sigma"}; + Configurable tpcNsigmaMax{"tpcNsigmaMax", 2.5f, "Maximum absolute deuteron TPC n-sigma"}; + Configurable itsNsigmaMax{"itsNsigmaMax", 2.5f, "Maximum absolute deuteron ITS n-sigma"}; + Configurable requireIndividualNsigma{"requireIndividualNsigma", false, "Also apply individual TPC and TOF cuts in the combined PID branch"}; + Configurable dcaXYOffset{"dcaXYOffset", 0.004f, "Deuteron DCAxy cut offset"}; + Configurable dcaXYPtCoefficient{"dcaXYPtCoefficient", 0.013f, "Deuteron DCAxy inverse-pT coefficient"}; + Configurable dcaZOffset{"dcaZOffset", 0.004f, "Deuteron DCAz cut offset"}; + Configurable dcaZPtCoefficient{"dcaZPtCoefficient", 0.013f, "Deuteron DCAz inverse-pT coefficient"}; + } deuteronCuts; + + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"pair"}; + Configurable enableClosePairRejection{"enableClosePairRejection", true, "Enable pion-deuteron close-pair rejection"}; + Configurable closePairDeltaEtaMax{"closePairDeltaEtaMax", 0.01f, "CPR ellipse delta-eta radius"}; + Configurable closePairDeltaPhiMax{"closePairDeltaPhiMax", 0.01f, "CPR ellipse delta-phi-star radius"}; + Configurable closePairRadiusMode{"closePairRadiusMode", 1, "CPR mode: 0 PV, 1 average TPC phi-star, 2 one TPC radius"}; + Configurable closePairSpecificRadius{"closePairSpecificRadius", 85.f, "TPC radius in cm for CPR mode 2"}; + } pairCuts; + + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"analysis"}; + Configurable useBetheBlochDeuteronNsigma{"useBetheBlochDeuteronNsigma", false, "Compute deuteron TPC n-sigma from the configured Bethe-Bloch parameters"}; + Configurable lowKstarYieldMax{"lowKstarYieldMax", 0.3f, "Upper kstar used for the SE-yield stability check"}; + } analysisSettings; + + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"zorro"}; + Configurable skimmedProcessing{"skimmedProcessing", false, "Process a Zorro-selected deuteron skim"}; + } zorroSettings; + + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"ccdb"}; + Configurable magneticField{"magneticField", -999., "Magnetic field; -999 reads it from CCDB"}; + Configurable url{"url", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "GRP object path"}; + Configurable grpMagPath{"grpMagPath", "GLO/Config/GRPMagField", "GRP magnetic-field object path"}; + Configurable materialCorrection{"materialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Material correction used by the optional pair DCA fitter"}; + } ccdbSettings; + + struct : ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"pidCalibration"}; + Configurable> betheBlochParameters{"betheBlochParameters", + {BetheBlochDeDefault.data(), 1, 6, betheBlochParticleNames, betheBlochParameterNames}, + "Deuteron TPC Bethe-Bloch parameters"}; + } pidCalibration; + + ConfigurableAxis mixingVertexAxis{"mixingVertexAxis", {30, -10., 10.}, "Event-mixing z-vertex bins"}; + ConfigurableAxis mixingCentralityAxis{"mixingCentralityAxis", {40, 0., 100.}, "Event-mixing centrality bins"}; + ConfigurableAxis kstarAxis{"kstarAxis", {300, 0., 3.}, "kstar axis"}; + ConfigurableAxis mtAxis{"mtAxis", {240, 0.8, 3.2}, "pair mT axis"}; + ConfigurableAxis centralityAxis{"centralityAxis", {100, 0., 100.}, "centrality axis"}; + + using MixingBinning = ColumnBinningPolicy; + MixingBinning mixingBinning{{mixingVertexAxis, mixingCentralityAxis}}; + SliceCache cache; + SameKindPair mixedEventPairs{ + mixingBinning, eventCuts.numberOfMixedEvents, -1, &cache}; + Preslice tracksPerCollision = aod::track::collisionId; + + HistogramRegistry registry{"PiDeFemtoSystematics", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + + Service ccdb{}; + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + o2::vertexing::DCAFitterN<2> pairFitter; + + std::array betheBlochDe{}; + int currentRunNumber{0}; + float magneticField{0.f}; + + void init(InitContext const&) + { + const AxisSpec channelAxis{kNPairChannels, -0.5, static_cast(kNPairChannels) - 0.5, "pair channel"}; + registry.add("Pairs/hSE", "Same-event pion-deuteron pairs", HistType::kTHnSparseF, + {kstarAxis, mtAxis, centralityAxis, channelAxis}); + registry.add("Pairs/hME", "Mixed-event pion-deuteron pairs", HistType::kTHnSparseF, + {kstarAxis, mtAxis, centralityAxis, channelAxis}); + registry.add("Pairs/hSELowKstarYield", "SE low-kstar yield;pair channel;Entries", + HistType::kTH1F, {channelAxis}); + registry.add("Pairs/hMELowKstarYield", "ME low-kstar yield;pair channel;Entries", + HistType::kTH1F, {channelAxis}); + registry.add("Pairs/hCPRBefore", "CPR before selection;#Delta#eta;#Delta#varphi^{*}", + HistType::kTH2F, {{300, -0.15, 0.15}, {400, -0.2, 0.2}}); + registry.add("Pairs/hCPRAfter", "CPR after selection;#Delta#eta;#Delta#varphi^{*}", + HistType::kTH2F, {{300, -0.15, 0.15}, {400, -0.2, 0.2}}); + registry.add("Event/hCounter", "Event selection;step;Entries", + HistType::kTH1F, {{3, -0.5, 2.5}}); + registry.add("Event/hVertexZ", "Selected collision vertex;z (cm);Entries", + HistType::kTH1F, {{200, -20., 20.}}); + registry.add("Event/hCentrality", "Selected collision centrality;FT0C percentile;Entries", + HistType::kTH1F, {{100, 0., 100.}}); + registry.add("Pion/hSelection", "Pion selection;step;Entries", + HistType::kTH1F, {{kNSelectionSteps, -0.5, static_cast(kNSelectionSteps) - 0.5}}); + registry.add("Pion/hPt", "Selected pions;signed p_{T} (GeV/#it{c});Entries", + HistType::kTH1F, {{400, -5., 5.}}); + registry.add("Pion/hEta", "Selected pions;#eta;Entries", + HistType::kTH1F, {{200, -1., 1.}}); + registry.add("Pion/hTPCNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{TPC}", + HistType::kTH2F, {{400, -5., 5.}, {200, -5., 5.}}); + registry.add("Pion/hTOFNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{TOF}", + HistType::kTH2F, {{280, -7., 7.}, {200, -5., 5.}}); + registry.add("Pion/hITSNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{ITS}", + HistType::kTH2F, {{280, -7., 7.}, {120, -3., 3.}}); + registry.add("Pion/hTPCTOFNsigma", "Selected pions;signed p_{T} (GeV/#it{c});n#sigma_{TPC+TOF}", + HistType::kTH2F, {{280, -7., 7.}, {100, 0., 5.}}); + registry.add("Deuteron/hSelection", "Deuteron selection;step;Entries", + HistType::kTH1F, {{kNSelectionSteps, -0.5, static_cast(kNSelectionSteps) - 0.5}}); + registry.add("Deuteron/hPt", "Selected deuterons;signed p_{T} (GeV/#it{c});Entries", + HistType::kTH1F, {{400, -5., 5.}}); + registry.add("Deuteron/hEta", "Selected deuterons;#eta;Entries", + HistType::kTH1F, {{200, -1., 1.}}); + registry.add("Deuteron/hTPCNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{TPC}", + HistType::kTH2F, {{400, -5., 5.}, {200, -5., 5.}}); + registry.add("Deuteron/hTOFNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{TOF}", + HistType::kTH2F, {{280, -7., 7.}, {200, -5., 5.}}); + registry.add("Deuteron/hITSNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{ITS}", + HistType::kTH2F, {{280, -7., 7.}, {120, -3., 3.}}); + registry.add("Deuteron/hTPCTOFNsigma", "Selected deuterons;signed p_{T} (GeV/#it{c});n#sigma_{TPC+TOF}", + HistType::kTH2F, {{280, -7., 7.}, {100, 0., 5.}}); + registry.add("Deuteron/hTPCClusters", "Selected deuterons;TPC clusters;Entries", + HistType::kTH1F, {{161, -0.5, 160.5}}); + registry.add("Deuteron/hTPCCrossedRows", "Selected deuterons;TPC crossed rows;Entries", + HistType::kTH1F, {{161, -0.5, 160.5}}); + + addCutConfigurationHistogram(); + setAxisLabels(); + + zorroSummary.setObject(zorro.getZorroSummary()); + ccdb->setURL(ccdbSettings.url.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + pairFitter.setPropagateToPCA(true); + pairFitter.setMaxR(200.); + pairFitter.setMinParamChange(1.e-3); + pairFitter.setMinRelChi2Change(0.9); + pairFitter.setMaxDZIni(1.e9); + pairFitter.setMaxChi2(1.e9); + pairFitter.setUseAbsDCA(true); + pairFitter.setMatCorrType( + static_cast(ccdbSettings.materialCorrection.value)); + + for (int i = 0; i < NumberOfBetheBlochShapeParameters; ++i) { + betheBlochDe[i] = pidCalibration.betheBlochParameters->get("De", Form("p%i", i)); + } + betheBlochDe[BetheBlochResolutionIndex] = pidCalibration.betheBlochParameters->get("De", "resolution"); + } + + void setAxisLabels() + { + const std::array channelLabels{ + "LS matter", "LS antimatter", "US matter", "US antimatter"}; + const auto setSparseChannelLabels = [&channelLabels](THnSparse* histogram) { + for (int channel = 0; channel < kNPairChannels; ++channel) { + histogram->GetAxis(3)->SetBinLabel(channel + 1, channelLabels[channel]); + } + }; + setSparseChannelLabels(registry.get(HIST("Pairs/hSE")).get()); + setSparseChannelLabels(registry.get(HIST("Pairs/hME")).get()); + + const auto setYieldChannelLabels = [&channelLabels](TH1* histogram) { + for (int channel = 0; channel < kNPairChannels; ++channel) { + histogram->GetXaxis()->SetBinLabel(channel + 1, channelLabels[channel]); + } + }; + setYieldChannelLabels(registry.get(HIST("Pairs/hSELowKstarYield")).get()); + setYieldChannelLabels(registry.get(HIST("Pairs/hMELowKstarYield")).get()); + + const std::array selectionLabels{"all", "track cuts", "PID"}; + const auto setSelectionLabels = [&selectionLabels](TH1* histogram) { + for (int step = 0; step < kNSelectionSteps; ++step) { + histogram->GetXaxis()->SetBinLabel(step + 1, selectionLabels[step]); + } + }; + setSelectionLabels(registry.get(HIST("Pion/hSelection")).get()); + setSelectionLabels(registry.get(HIST("Deuteron/hSelection")).get()); + auto eventCounter = registry.get(HIST("Event/hCounter")); + eventCounter->GetXaxis()->SetBinLabel(1, "all"); + eventCounter->GetXaxis()->SetBinLabel(2, "sel8 and vertex"); + eventCounter->GetXaxis()->SetBinLabel(3, "Zorro"); + } + + void addCutConfigurationHistogram() + { + const std::vector> cuts{ + {"pionPtMin", pionCuts.ptMin.value}, + {"pionPtMax", pionCuts.ptMax.value}, + {"pionEtaMax", pionCuts.etaMax.value}, + {"pionITSInnerBarrelClustersMin", pionCuts.itsInnerBarrelClustersMin.value}, + {"pionITSClustersMin", pionCuts.itsClustersMin.value}, + {"pionTPCClustersMin", pionCuts.tpcClustersMin.value}, + {"pionTPCCrossedRowsMin", pionCuts.tpcCrossedRowsMin.value}, + {"pionDCAxyOffset", pionCuts.dcaXYOffset.value}, + {"pionDCAxyPtCoefficient", pionCuts.dcaXYPtCoefficient.value}, + {"pionDCAzOffset", pionCuts.dcaZOffset.value}, + {"pionDCAzPtCoefficient", pionCuts.dcaZPtCoefficient.value}, + {"pionCombinedPidMomentumMin", pionCuts.combinedPidMomentumMin.value}, + {"pionTPCNsigmaMax", pionCuts.tpcNsigmaMax.value}, + {"pionCombinedNsigmaMax", pionCuts.combinedNsigmaMax.value}, + {"deuteronPtMin", deuteronCuts.ptMin.value}, + {"deuteronPtMax", deuteronCuts.ptMax.value}, + {"deuteronEtaMax", deuteronCuts.etaMax.value}, + {"deuteronTPCClustersMin", deuteronCuts.tpcClustersMin.value}, + {"deuteronTPCCrossedRowsMin", deuteronCuts.tpcCrossedRowsMin.value}, + {"deuteronITSClustersMin", deuteronCuts.itsClustersMin.value}, + {"deuteronITSInnerBarrelClustersMin", deuteronCuts.itsInnerBarrelClustersMin.value}, + {"deuteronSharedTPCClustersMax", deuteronCuts.sharedTPCClustersMax.value}, + {"deuteronSharedTPCFractionMax", deuteronCuts.sharedTPCFractionMax.value}, + {"deuteronTPCInnerParamMin", deuteronCuts.tpcInnerParamMin.value}, + {"deuteronTOFMomentumMin", deuteronCuts.tofMomentumMin.value}, + {"deuteronCombNsigmaMax", deuteronCuts.combinedNsigmaMax.value}, + {"deuteronTPCNsigmaMax", deuteronCuts.tpcNsigmaMax.value}, + {"deuteronITSNsigmaMax", deuteronCuts.itsNsigmaMax.value}, + {"requireIndividualNsigma", deuteronCuts.requireIndividualNsigma.value}, + {"deuteronDCAxyOffset", deuteronCuts.dcaXYOffset.value}, + {"deuteronDCAxyPtCoefficient", deuteronCuts.dcaXYPtCoefficient.value}, + {"deuteronDCAzOffset", deuteronCuts.dcaZOffset.value}, + {"deuteronDCAzPtCoefficient", deuteronCuts.dcaZPtCoefficient.value}, + {"enableCPR", pairCuts.enableClosePairRejection.value}, + {"cprDeltaEtaMax", pairCuts.closePairDeltaEtaMax.value}, + {"cprDeltaPhiMax", pairCuts.closePairDeltaPhiMax.value}, + {"cprRadiusMode", pairCuts.closePairRadiusMode.value}, + {"cprSpecificRadius", pairCuts.closePairSpecificRadius.value}, + {"useBetheBlochDeuteronNsigma", analysisSettings.useBetheBlochDeuteronNsigma.value}, + {"skimmedProcessing", zorroSettings.skimmedProcessing.value}, + {"vertexZMax", eventCuts.vertexZMax.value}, + {"numberOfMixedEvents", eventCuts.numberOfMixedEvents.value}}; + + const AxisSpec cutAxis{static_cast(cuts.size()), -0.5, + static_cast(cuts.size()) - 0.5, "cut"}; + registry.add("Config/hCutValues", "Configured cut values;cut;value", + HistType::kTH1D, {cutAxis}); + auto histogram = registry.get(HIST("Config/hCutValues")); + for (std::size_t index = 0; index < cuts.size(); ++index) { + histogram->GetXaxis()->SetBinLabel(static_cast(index) + 1, cuts[index].first.c_str()); + histogram->SetBinContent(static_cast(index) + 1, cuts[index].second); + } + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (currentRunNumber == bc.runNumber()) { + return; + } + if (zorroSettings.skimmedProcessing.value) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fDe"); + zorro.populateHistRegistry(registry, bc.runNumber()); + } + + auto* grp = ccdb->getForTimeStamp( + ccdbSettings.grpPath.value, bc.timestamp()); + if (grp) { + o2::base::Propagator::initFieldFromGRP(grp); + magneticField = ccdbSettings.magneticField.value < AutomaticMagneticFieldThreshold ? grp->getNominalL3Field() + : ccdbSettings.magneticField.value; + } else { + auto* grpMag = ccdb->getForTimeStamp( + ccdbSettings.grpMagPath.value, bc.timestamp()); + if (!grpMag) { + LOG(fatal) << "Could not load magnetic field from " << ccdbSettings.grpPath.value + << " or " << ccdbSettings.grpMagPath.value; + } + o2::base::Propagator::initFieldFromGRP(grpMag); + magneticField = ccdbSettings.magneticField.value < AutomaticMagneticFieldThreshold + ? std::lround(5.f * grpMag->getL3Current() / NominalL3Current) + : ccdbSettings.magneticField.value; + } + currentRunNumber = bc.runNumber(); + } + + template + bool isCollisionSelected(Collision const& collision, bool fillQA) + { + if (fillQA) { + registry.fill(HIST("Event/hCounter"), 0.); + } + auto bc = collision.template bc_as(); + initCCDB(bc); + if (!collision.sel8() || std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return false; + } + if (zorroSettings.skimmedProcessing.value) { + if (!zorro.isSelected(bc.globalBC())) { + return false; + } + if (fillQA) { + registry.fill(HIST("Event/hCounter"), 2.); + } + } + if (fillQA) { + registry.fill(HIST("Event/hCounter"), 1.); + registry.fill(HIST("Event/hVertexZ"), collision.posZ()); + registry.fill(HIST("Event/hCentrality"), collision.centFT0C()); + } + return true; + } + + template + bool passPionTrackCuts(Track const& track) const + { + const float absPt = std::abs(track.pt()); + if (absPt <= 0.f || absPt < pionCuts.ptMin.value || absPt > pionCuts.ptMax.value || + std::abs(track.eta()) > pionCuts.etaMax.value) { + return false; + } + if (track.itsNClsInnerBarrel() < pionCuts.itsInnerBarrelClustersMin.value || + track.itsNCls() < pionCuts.itsClustersMin.value || + track.tpcNClsFound() < pionCuts.tpcClustersMin.value || + track.tpcNClsCrossedRows() < pionCuts.tpcCrossedRowsMin.value) { + return false; + } + const float dcaXYMax = pionCuts.dcaXYOffset.value + + pionCuts.dcaXYPtCoefficient.value / absPt; + const float dcaZMax = pionCuts.dcaZOffset.value + + pionCuts.dcaZPtCoefficient.value / absPt; + return std::abs(track.dcaXY()) <= dcaXYMax && + std::abs(track.dcaZ()) <= dcaZMax; + } + + template + bool passPionPID(Track const& track) const + { + const float tpcNsigma = track.tpcNSigmaPi(); + if (std::abs(track.p()) <= pionCuts.combinedPidMomentumMin.value) { + return std::abs(tpcNsigma) <= pionCuts.tpcNsigmaMax.value; + } + if (!track.hasTOF()) { + return false; + } + const float tofNsigma = track.tofNSigmaPi(); + if (std::hypot(tpcNsigma, tofNsigma) > + pionCuts.combinedNsigmaMax.value) { + return false; + } + return !deuteronCuts.requireIndividualNsigma.value || + (std::abs(tpcNsigma) <= pionCuts.combinedNsigmaMax.value && + std::abs(tofNsigma) <= pionCuts.combinedNsigmaMax.value); + } + + template + bool passDeuteronTrackCuts(Track const& track) const + { + const float absPt = std::abs(track.pt()); + if (absPt <= 0.f || absPt < deuteronCuts.ptMin.value || + absPt > deuteronCuts.ptMax.value || + std::abs(track.eta()) > deuteronCuts.etaMax.value) { + return false; + } + if (track.tpcNClsFound() < deuteronCuts.tpcClustersMin.value || + track.tpcNClsCrossedRows() < deuteronCuts.tpcCrossedRowsMin.value || + track.tpcNClsShared() > deuteronCuts.sharedTPCClustersMax.value || + track.tpcFractionSharedCls() > deuteronCuts.sharedTPCFractionMax.value || + track.itsNCls() < deuteronCuts.itsClustersMin.value || + track.itsNClsInnerBarrel() < deuteronCuts.itsInnerBarrelClustersMin.value) { + return false; + } + const float dcaXYMax = deuteronCuts.dcaXYOffset.value + + deuteronCuts.dcaXYPtCoefficient.value / absPt; + const float dcaZMax = deuteronCuts.dcaZOffset.value + + deuteronCuts.dcaZPtCoefficient.value / absPt; + return std::abs(track.dcaXY()) <= dcaXYMax && + std::abs(track.dcaZ()) <= dcaZMax; + } + + template + float deuteronTPCNsigma(Track const& track) const + { + if (!analysisSettings.useBetheBlochDeuteronNsigma.value) { + return track.tpcNSigmaDe(); + } + const float expected = o2::common::BetheBlochAleph( + static_cast(track.tpcInnerParam() / + o2::constants::physics::MassDeuteron), + betheBlochDe[0], betheBlochDe[1], betheBlochDe[2], + betheBlochDe[3], betheBlochDe[4]); + return static_cast((track.tpcSignal() - expected) / + (expected * betheBlochDe[BetheBlochResolutionIndex])); + } + + template + bool passDeuteronPID(Track const& track) const + { + const float tpcInnerParam = track.tpcInnerParam(); + if (std::abs(tpcInnerParam) < deuteronCuts.tpcInnerParamMin.value) { + return false; + } + const float tpcNsigma = deuteronTPCNsigma(track); + if (track.hasTOF() && + tpcInnerParam > deuteronCuts.tofMomentumMin.value) { + const float tofNsigma = track.tofNSigmaDe(); + if (std::hypot(tpcNsigma, tofNsigma) > deuteronCuts.combinedNsigmaMax.value) { + return false; + } + return !deuteronCuts.requireIndividualNsigma.value || + (std::abs(tpcNsigma) <= deuteronCuts.combinedNsigmaMax.value && + std::abs(tofNsigma) <= deuteronCuts.combinedNsigmaMax.value); + } + if (tpcInnerParam <= deuteronCuts.tofMomentumMin.value) { + if (std::abs(tpcNsigma) > deuteronCuts.tpcNsigmaMax.value) { + return false; + } + o2::aod::ITSResponse itsResponse; + const float itsNsigma = + itsResponse.nSigmaITS( + track.itsClusterSizes(), track.p(), track.eta()); + return std::abs(itsNsigma) <= deuteronCuts.itsNsigmaMax.value; + } + return false; + } + + template + void fillSingleParticleQA(Tracks const& tracks) + { + for (auto const& track : tracks) { + registry.fill(HIST("Pion/hSelection"), static_cast(kAllTracks)); + if (passPionTrackCuts(track)) { + registry.fill(HIST("Pion/hSelection"), static_cast(kTrackCuts)); + if (passPionPID(track)) { + registry.fill(HIST("Pion/hSelection"), static_cast(kPidCuts)); + const float signedPt = track.sign() * track.pt(); + const float tpcNsigma = track.tpcNSigmaPi(); + registry.fill(HIST("Pion/hPt"), signedPt); + registry.fill(HIST("Pion/hEta"), track.eta()); + registry.fill(HIST("Pion/hTPCNsigma"), signedPt, tpcNsigma); + o2::aod::ITSResponse itsResponse; + const float itsNsigma = itsResponse.nSigmaITS( + track.itsClusterSizes(), track.p(), track.eta()); + registry.fill(HIST("Pion/hITSNsigma"), signedPt, itsNsigma); + if (track.hasTOF() && + std::abs(track.p()) > pionCuts.combinedPidMomentumMin.value) { + const float tofNsigma = track.tofNSigmaPi(); + registry.fill(HIST("Pion/hTOFNsigma"), signedPt, tofNsigma); + registry.fill(HIST("Pion/hTPCTOFNsigma"), signedPt, + std::hypot(tpcNsigma, tofNsigma)); + } + } + } + + registry.fill(HIST("Deuteron/hSelection"), + static_cast(kAllTracks)); + if (passDeuteronTrackCuts(track)) { + registry.fill(HIST("Deuteron/hSelection"), + static_cast(kTrackCuts)); + if (passDeuteronPID(track)) { + registry.fill(HIST("Deuteron/hSelection"), + static_cast(kPidCuts)); + const float signedPt = track.sign() * track.pt(); + const float tpcNsigma = deuteronTPCNsigma(track); + registry.fill(HIST("Deuteron/hPt"), signedPt); + registry.fill(HIST("Deuteron/hEta"), track.eta()); + registry.fill(HIST("Deuteron/hTPCNsigma"), signedPt, tpcNsigma); + if (track.tpcInnerParam() <= deuteronCuts.tofMomentumMin.value) { + o2::aod::ITSResponse itsResponse; + const float itsNsigma = + itsResponse.nSigmaITS( + track.itsClusterSizes(), track.p(), track.eta()); + registry.fill(HIST("Deuteron/hITSNsigma"), signedPt, itsNsigma); + } else if (track.hasTOF()) { + const float tofNsigma = track.tofNSigmaDe(); + registry.fill(HIST("Deuteron/hTOFNsigma"), signedPt, tofNsigma); + registry.fill(HIST("Deuteron/hTPCTOFNsigma"), signedPt, + std::hypot(tpcNsigma, tofNsigma)); + } + registry.fill(HIST("Deuteron/hTPCClusters"), + static_cast(track.tpcNClsFound())); + registry.fill(HIST("Deuteron/hTPCCrossedRows"), + static_cast(track.tpcNClsCrossedRows())); + } + } + } + } + + template + float phiAtRadius(Track const& track, float radius) const + { + const float absPt = std::abs(track.pt()); + if (absPt <= 0.f) { + return InvalidPhiStar; + } + const float argument = + 0.3f * static_cast(track.sign()) * 0.1f * magneticField * + radius * 0.01f / (2.f * absPt); + if (std::abs(argument) >= 1.f) { + return InvalidPhiStar; + } + return track.phi() - std::asin(argument); + } + + static float wrapDeltaPhi(float deltaPhi) + { + return std::atan2(std::sin(deltaPhi), std::cos(deltaPhi)); + } + + template + float averagePhiStar(FirstTrack const& first, + SecondTrack const& second) const + { + float sum = 0.f; + int entries = 0; + for (const auto& radius : tpcRadii) { + const float firstPhi = phiAtRadius(first, radius); + const float secondPhi = phiAtRadius(second, radius); + if (firstPhi == InvalidPhiStar || secondPhi == InvalidPhiStar) { + continue; + } + sum += wrapDeltaPhi(firstPhi - secondPhi); + ++entries; + } + return entries > 0 ? sum / static_cast(entries) : InvalidPhiStar; + } + + template + bool isClosePair(FirstTrack const& first, SecondTrack const& second, + bool fillQA) + { + if (!pairCuts.enableClosePairRejection.value || + first.sign() != second.sign()) { + return false; + } + const float deltaEta = first.eta() - second.eta(); + float deltaPhi = averagePhiStar(first, second); + if (pairCuts.closePairRadiusMode.value == ClosePairRadiusModeAtPV) { + deltaPhi = wrapDeltaPhi(first.phi() - second.phi()); + } else if (pairCuts.closePairRadiusMode.value == ClosePairRadiusModeAtSpecificRadius) { + const float firstPhi = + phiAtRadius(first, pairCuts.closePairSpecificRadius.value); + const float secondPhi = + phiAtRadius(second, pairCuts.closePairSpecificRadius.value); + if (firstPhi == InvalidPhiStar || secondPhi == InvalidPhiStar) { + return false; + } + deltaPhi = wrapDeltaPhi(firstPhi - secondPhi); + } + if (deltaPhi == InvalidPhiStar || + pairCuts.closePairDeltaEtaMax.value <= 0.f || + pairCuts.closePairDeltaPhiMax.value <= 0.f) { + return false; + } + if (fillQA) { + registry.fill(HIST("Pairs/hCPRBefore"), deltaEta, deltaPhi); + } + const bool rejected = + deltaEta * deltaEta / + (pairCuts.closePairDeltaEtaMax.value * pairCuts.closePairDeltaEtaMax.value) + + deltaPhi * deltaPhi / + (pairCuts.closePairDeltaPhiMax.value * pairCuts.closePairDeltaPhiMax.value) < + 1.f; + if (fillQA && !rejected) { + registry.fill(HIST("Pairs/hCPRAfter"), deltaEta, deltaPhi); + } + return rejected; + } + + template + bool hasValidPairFit(FirstTrack const& first, SecondTrack const& second) + { + try { + return pairFitter.process(getTrackParCov(first), + getTrackParCov(second)) > 0; + } catch (...) { + return false; + } + } + + float pairKstar(std::array const& pionMomentum, + std::array const& deuteronMomentum) const + { + const PairLorentzVector pion( + pionMomentum[0], pionMomentum[1], pionMomentum[2], + o2::constants::physics::MassPiPlus); + const PairLorentzVector deuteron( + deuteronMomentum[0], deuteronMomentum[1], deuteronMomentum[2], + o2::constants::physics::MassDeuteron); + const PairLorentzVector pair = pion + deuteron; + const float beta = pair.Beta(); + const ROOT::Math::Boost boost( + -beta * std::cos(pair.Phi()) * std::sin(pair.Theta()), + -beta * std::sin(pair.Phi()) * std::sin(pair.Theta()), + -beta * std::cos(pair.Theta())); + const PairLorentzVector relativeMomentum = + boost(pion) - boost(deuteron); + return 0.5f * relativeMomentum.P(); + } + + float pairMT(std::array const& pionMomentum, + std::array const& deuteronMomentum) const + { + const PairLorentzVector pion( + pionMomentum[0], pionMomentum[1], pionMomentum[2], + o2::constants::physics::MassPiPlus); + const PairLorentzVector deuteron( + deuteronMomentum[0], deuteronMomentum[1], deuteronMomentum[2], + o2::constants::physics::MassDeuteron); + const float kT = 0.5f * (pion + deuteron).Pt(); + const float averageMass = + 0.5f * (o2::constants::physics::MassPiPlus + + o2::constants::physics::MassDeuteron); + return std::hypot(kT, averageMass); + } + + static int pairChannel(int deuteronSign, int pionSign) + { + const bool unlikeSign = deuteronSign * pionSign < 0; + const bool antimatter = deuteronSign < 0; + return 2 * static_cast(unlikeSign) + + static_cast(antimatter); + } + + template + void fillPair(DeuteronTrack const& deuteron, PionTrack const& pion, + float centrality, bool mixedEvent) + { + const std::array deuteronMomentum{ + deuteron.px(), deuteron.py(), deuteron.pz()}; + const std::array pionMomentum{ + pion.px(), pion.py(), pion.pz()}; + const float kstar = pairKstar(pionMomentum, deuteronMomentum); + const float mt = pairMT(pionMomentum, deuteronMomentum); + const auto channel = static_cast( + pairChannel(deuteron.sign(), pion.sign())); + if (mixedEvent) { + registry.fill(HIST("Pairs/hME"), kstar, mt, centrality, channel); + if (kstar < analysisSettings.lowKstarYieldMax.value) { + registry.fill(HIST("Pairs/hMELowKstarYield"), channel); + } + } else { + registry.fill(HIST("Pairs/hSE"), kstar, mt, centrality, channel); + if (kstar < analysisSettings.lowKstarYieldMax.value) { + registry.fill(HIST("Pairs/hSELowKstarYield"), channel); + } + } + } + + template + void buildPairs(DeuteronTracks const& deuteronTracks, + PionTracks const& pionTracks, float centrality, + bool mixedEvent) + { + for (auto const& deuteron : deuteronTracks) { + if (!passDeuteronTrackCuts(deuteron) || + !passDeuteronPID(deuteron)) { + continue; + } + for (auto const& pion : pionTracks) { + if (!mixedEvent && deuteron.globalIndex() == pion.globalIndex()) { + continue; + } + if (!passPionTrackCuts(pion) || !passPionPID(pion)) { + continue; + } + if (isClosePair(deuteron, pion, !mixedEvent)) { + continue; + } + if (!mixedEvent && !hasValidPairFit(deuteron, pion)) { + continue; + } + fillPair(deuteron, pion, centrality, mixedEvent); + } + } + } + + void processSameEvent(CollisionsFull const& collisions, + TrackCandidates const& tracks, + aod::BCsWithTimestamps const&) + { + for (auto const& collision : collisions) { + if (!isCollisionSelected(collision, true)) { + continue; + } + auto collisionTracks = + tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + collisionTracks.bindExternalIndices(&tracks); + fillSingleParticleQA(collisionTracks); + buildPairs(collisionTracks, collisionTracks, + collision.centFT0C(), false); + } + } + PROCESS_SWITCH(PiDeFemtoSystematics, processSameEvent, + "Fill same-event pion-deuteron pairs", true); + + void processMixedEvent(CollisionsFull const& /*collisions*/, + TrackCandidates const& /*tracks*/, + aod::BCsWithTimestamps const&) + { + for (auto const& [collision1, tracks1, collision2, tracks2] : + mixedEventPairs) { + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + auto bc1 = collision1.template bc_as(); + auto bc2 = collision2.template bc_as(); + initCCDB(bc1); + buildPairs(tracks1, tracks2, collision1.centFT0C(), true); + initCCDB(bc2); + buildPairs(tracks2, tracks1, collision2.centFT0C(), true); + } + } + PROCESS_SWITCH(PiDeFemtoSystematics, processMixedEvent, + "Fill mixed-event pion-deuteron pairs", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/Femto/Macros/cutculator.py b/PWGCF/Femto/Macros/cutculator.py old mode 100755 new mode 100644 index add45265ec6..e2d534b64e2 --- a/PWGCF/Femto/Macros/cutculator.py +++ b/PWGCF/Femto/Macros/cutculator.py @@ -15,8 +15,7 @@ @brief CutCulator (Compute bitmask for selecting particles in the Femto Framework) @author Anton Riedel , Technical University of Munich """ - -import ROOT +import ROOT # pylint: disable=import-error import argparse VALUE_DELIM = "___" @@ -35,6 +34,21 @@ def parse_bin_label(label): return result +def is_filter_histogram(bins): + """ + Determine whether the parsed bins belong to a filter histogram (fixed, + non-selectable pre-filter values) rather than a selection/bitmask histogram. + Filter bins carry a "FilterName" key; selection bins carry "SelectionName". + """ + for b in bins: + if "FilterName" in b: + return True + if "SelectionName" in b: + return False + # no parseable bins at all; treat as selection histogram (existing behavior) + return False + + def format_value_with_comment(b): """Return Value plus optional (comment=...) suffix.""" val = b.get("Value", "") @@ -44,6 +58,20 @@ def format_value_with_comment(b): return val +def print_filter_values(bins): + """ + Filters are fixed values already applied when the producer ran — there is + nothing to select. Just print each filter's name and value. + """ + print("\n=======================================") + print("Filter values used in this histogram:") + print("=======================================\n") + for b in bins: + name = b.get("FilterName", "unknown") + value = b.get("FilterValue", "") + print(f" {name}: {value}") + + def ask_user_selection(group): """ Prompt user to select bin(s) for this selection group. @@ -153,7 +181,77 @@ def ask_user_selection(group): return selected_bins -def main(rootfile_path, tdir_path="femto-producer"): +def parse_bitmask(text): + """Parse a bitmask given as decimal, hex (0x...) or binary (0b...).""" + text = text.strip() + try: + value = int(text, 0) + except ValueError: + # int(x, 0) rejects decimals with leading zeros, e.g. "010" + value = int(text, 10) + if value < 0: + raise ValueError("bitmask must be non-negative") + return value + + +def is_minimal_bin(b): + return b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0" + + +def print_decoded_bitmask(groups, bitmask): + """ + Print all cuts selected by the given bitmask, grouped by selection. Minimal + cuts with BitPosition X (always applied) are listed as well for every + selection where no stricter minimal bit is set. + """ + print("\n=======================================") + print(f"Cuts selected by bitmask {bitmask} ({hex(bitmask)}):") + print("=======================================\n") + + known_bits = set() + for sel_name, group in groups.items(): + selected = [] + has_minimal_bit = False + for b in group: + pos = b.get("BitPosition", "X") + if pos.upper() == "X": + continue + known_bits.add(int(pos)) + if bitmask & (1 << int(pos)): + selected.append(b) + if is_minimal_bin(b): + has_minimal_bit = True + + entries = [] + if not has_minimal_bit: + for b in group: + if is_minimal_bin(b) and b.get("BitPosition", "X").upper() == "X": + entries.append(f"{format_value_with_comment(b)} [minimal, no bit]") + for b in selected: + if is_minimal_bin(b): + kind = "minimal" + elif b.get("OptionalCut", "0") == "1": + kind = "optional" + else: + kind = "neutral" + entries.append(f"{format_value_with_comment(b)} [{kind}, bit {b.get('BitPosition')}]") + + if entries: + print(f" {sel_name}:") + for e in entries: + print(f" {e}") + + unknown_bits = [i for i in range(bitmask.bit_length()) if bitmask & (1 << i) and i not in known_bits] + if unknown_bits: + print(f"\nWarning: bit(s) {', '.join(map(str, unknown_bits))} are set but not defined in this histogram!") + + print("\nBitmask:") + print(f" Decimal: {bitmask}") + print(f" Binary: {bin(bitmask)}") + print(f" Hex: {hex(bitmask)}") + + +def main(rootfile_path, tdir_path="femto-producer", decode=False): print(f"Opening ROOT file: {rootfile_path}") f = ROOT.TFile.Open(rootfile_path) if not f: @@ -188,7 +286,8 @@ def main(rootfile_path, tdir_path="femto-producer"): print(f"\nUsing histogram: {hname}") print(f"Histogram contains {nbins} bins.\n") - # parse all bins, ignoring the last 2 special bins + # parse all bins, ignoring the last 2 special bins ("All analyzed"/"All passed", + # present on both selection and filter histograms) bins = [] for i in range(1, nbins - 2 + 1): label = hist.GetXaxis().GetBinLabel(i) @@ -198,12 +297,33 @@ def main(rootfile_path, tdir_path="femto-producer"): bdict["_bin_index"] = i bins.append(bdict) + if not bins: + print("No parseable bins found in this histogram.") + return + + # filter histograms carry fixed, already-applied values — nothing to select, + # so just print them and skip the interactive/bitmask machinery entirely + if is_filter_histogram(bins): + print_filter_values(bins) + return + # group by SelectionName groups = {} for b in bins: sel_name = b.get("SelectionName", f"unknown_{b['_bin_index']}") groups.setdefault(sel_name, []).append(b) + # decode an existing bitmask instead of building a new one + if decode: + while True: + try: + bitmask = parse_bitmask(input("\nEnter bitmask to decode (dec, 0x hex or 0b bin): ")) + break + except ValueError: + print("Invalid bitmask.") + print_decoded_bitmask(groups, bitmask) + return + selected_bins = [] for group in groups.values(): @@ -241,5 +361,10 @@ def main(rootfile_path, tdir_path="femto-producer"): parser = argparse.ArgumentParser() parser.add_argument("rootfile", help="Path to ROOT file") parser.add_argument("--dir", default="femto-producer", help="TDirectory path in ROOT file") + parser.add_argument( + "--bitmask", + action="store_true", + help="Ask for a bitmask after selecting the histogram and print the cuts it selects", + ) args = parser.parse_args() - main(args.rootfile, args.dir) + main(args.rootfile, args.dir, args.bitmask) diff --git a/PWGCF/Femto/Macros/cutculatorGui.py b/PWGCF/Femto/Macros/cutculatorGui.py deleted file mode 100755 index db776251e77..00000000000 --- a/PWGCF/Femto/Macros/cutculatorGui.py +++ /dev/null @@ -1,437 +0,0 @@ -#!/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. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -"""! -@brief CutCulator GUI (Compute bitmask for selecting particles in the Femto Framework) -@author Anton Riedel , Technical University of Munich -""" - -import tkinter as tk -from tkinter import ttk, filedialog, messagebox -import argparse -import sys - -try: - import ROOT - - ROOT.gROOT.SetBatch(True) -except ImportError: - ROOT = None - -VALUE_DELIM = "___" -SECTION_DELIM = ":::" - -# ── Colours ────────────────────────────────────────────────────────────────── -BG = "#1e1e2e" -BG_CARD = "#2a2a3e" -BG_HOVER = "#3a3a50" -ACCENT = "#89b4fa" # blue – minimal -ACCENT_OPT = "#a6e3a1" # green – optional -ACCENT_REJ = "#f38ba8" # red – rejection / neutral -FG = "#cdd6f4" -FG_DIM = "#6c7086" -BORDER = "#45475a" -SEL_BG = "#313244" - -FONT_TITLE = ("Inter", 15, "bold") -FONT_HEAD = ("Inter", 11, "bold") -FONT_BODY = ("Inter", 10) -FONT_MONO = ("JetBrains Mono", 11, "bold") -FONT_SMALL = ("Inter", 9) - - -# ── Helpers ─────────────────────────────────────────────────────────────────── -def parse_bin_label(label): - result = {} - for sec in label.split(SECTION_DELIM): - if VALUE_DELIM in sec: - k, v = sec.split(VALUE_DELIM, 1) - result[k.strip()] = v.strip() - return result - - -def format_value_with_comment(b): - val = b.get("Value", "") - comment = b.get("Comment", "") - if comment and comment.upper() != "X": - return f"{val} ({comment})" - return val - - -def bin_type(b): - """Return 'minimal', 'optional', 'neutral', or 'skip'.""" - is_min = b.get("MinimalCut", "0") == "1" - is_opt = b.get("OptionalCut", "0") == "1" - pos = b.get("BitPosition", "X") - if pos.upper() == "X": - return "skip" - if is_min and not is_opt: - return "minimal" - if is_opt: - return "optional" - return "neutral" - - -def load_bins_from_hist(hist): - nbins = hist.GetNbinsX() - bins = [] - for i in range(1, nbins - 2 + 1): - label = hist.GetXaxis().GetBinLabel(i) - if not label: - continue - b = parse_bin_label(label) - b["_bin_index"] = i - bins.append(b) - # group by SelectionName preserving order - groups = {} - for b in bins: - name = b.get("SelectionName", f"unknown_{b['_bin_index']}") - groups.setdefault(name, []).append(b) - return groups - - -# ── Main Application ────────────────────────────────────────────────────────── -class CutCulatorApp(tk.Tk): - def __init__(self, rootfile=None, tdir="femto-producer"): - super().__init__() - self.title("CutCulator") - self.configure(bg=BG) - self.geometry("920x720") - self.minsize(780, 560) - self.resizable(True, True) - - self._rootfile_path = rootfile - self._tdir_path = tdir - self._root_file = None - self._hist = None - self._groups = {} # SelectionName → list[bin_dict] - self._vars = {} # (SelectionName, idx) → BooleanVar - - self._build_ui() - - if rootfile: - self._load_file(rootfile) - - # ── UI skeleton ────────────────────────────────────────────────────────── - def _build_ui(self): - # ── top bar ── - top = tk.Frame(self, bg=BG, pady=10, padx=18) - top.pack(fill="x") - - tk.Label(top, text="✂ CutCulator", font=FONT_TITLE, bg=BG, fg=ACCENT).pack(side="left") - - btn_frame = tk.Frame(top, bg=BG) - btn_frame.pack(side="right") - self._btn_open = self._make_button(btn_frame, "📂 Open ROOT file", self._open_file_dialog, ACCENT) - self._btn_open.pack(side="left", padx=4) - - # ── file + histogram selector bar ── - bar = tk.Frame(self, bg=BG_CARD, pady=8, padx=18) - bar.pack(fill="x") - - tk.Label(bar, text="File:", font=FONT_BODY, bg=BG_CARD, fg=FG_DIM).pack(side="left") - self._lbl_file = tk.Label(bar, text="(none)", font=FONT_BODY, bg=BG_CARD, fg=FG, wraplength=400, anchor="w") - self._lbl_file.pack(side="left", padx=6) - - tk.Label(bar, text="Histogram:", font=FONT_BODY, bg=BG_CARD, fg=FG_DIM).pack(side="left", padx=(20, 0)) - self._hist_var = tk.StringVar() - self._hist_combo = ttk.Combobox(bar, textvariable=self._hist_var, state="disabled", width=30, font=FONT_BODY) - self._hist_combo.pack(side="left", padx=6) - self._hist_combo.bind("<>", self._on_hist_selected) - - self._style_combobox() - - # ── legend ── - legend = tk.Frame(self, bg=BG, pady=4, padx=18) - legend.pack(fill="x") - for color, label in [(ACCENT, "Minimal"), (ACCENT_OPT, "Optional"), (ACCENT_REJ, "Neutral")]: - dot = tk.Label(legend, text="●", font=FONT_BODY, bg=BG, fg=color) - dot.pack(side="left") - tk.Label(legend, text=label, font=FONT_SMALL, bg=BG, fg=FG_DIM).pack(side="left", padx=(2, 12)) - - # ── scrollable selection area ── - outer = tk.Frame(self, bg=BG) - outer.pack(fill="both", expand=True, padx=12, pady=4) - - self._canvas = tk.Canvas(outer, bg=BG, highlightthickness=0, bd=0) - vsb = ttk.Scrollbar(outer, orient="vertical", command=self._canvas.yview) - self._canvas.configure(yscrollcommand=vsb.set) - vsb.pack(side="right", fill="y") - self._canvas.pack(side="left", fill="both", expand=True) - - self._inner = tk.Frame(self._canvas, bg=BG) - self._canvas_win = self._canvas.create_window((0, 0), window=self._inner, anchor="nw") - self._inner.bind("", self._on_inner_configure) - self._canvas.bind("", self._on_canvas_configure) - self._canvas.bind_all("", self._on_mousewheel) - self._canvas.bind_all("", self._on_mousewheel) - self._canvas.bind_all("", self._on_mousewheel) - - # ── bottom result bar ── - bottom = tk.Frame(self, bg=BG_CARD, pady=10, padx=18) - bottom.pack(fill="x", side="bottom") - - tk.Label(bottom, text="Bitmask:", font=FONT_HEAD, bg=BG_CARD, fg=FG).pack(side="left") - - self._lbl_dec = tk.Label(bottom, text="—", font=FONT_MONO, bg=BG_CARD, fg=ACCENT, width=14, anchor="w") - self._lbl_dec.pack(side="left", padx=8) - - self._lbl_hex = tk.Label(bottom, text="—", font=FONT_MONO, bg=BG_CARD, fg=ACCENT_OPT, width=14, anchor="w") - self._lbl_hex.pack(side="left", padx=8) - - self._lbl_bin = tk.Label(bottom, text="—", font=FONT_MONO, bg=BG_CARD, fg=FG_DIM, anchor="w") - self._lbl_bin.pack(side="left", padx=8) - - copy_frame = tk.Frame(bottom, bg=BG_CARD) - copy_frame.pack(side="right") - self._make_button(copy_frame, "Copy Dec", lambda: self._copy(self._lbl_dec["text"]), ACCENT).pack( - side="left", padx=3 - ) - self._make_button(copy_frame, "Copy Hex", lambda: self._copy(self._lbl_hex["text"]), ACCENT_OPT).pack( - side="left", padx=3 - ) - - # ── Combobox styling ────────────────────────────────────────────────────── - def _style_combobox(self): - s = ttk.Style(self) - s.theme_use("clam") - s.configure( - "TCombobox", - fieldbackground=BG_CARD, - background=BG_CARD, - foreground=FG, - selectbackground=SEL_BG, - selectforeground=FG, - bordercolor=BORDER, - arrowcolor=ACCENT, - ) - s.map("TCombobox", fieldbackground=[("readonly", BG_CARD)]) - s.configure("TScrollbar", troughcolor=BG, background=BORDER) - - # ── Canvas resize helpers ───────────────────────────────────────────────── - def _on_inner_configure(self, _e=None): - self._canvas.configure(scrollregion=self._canvas.bbox("all")) - - def _on_canvas_configure(self, e): - self._canvas.itemconfig(self._canvas_win, width=e.width) - - def _on_mousewheel(self, e): - if e.num == 4: - self._canvas.yview_scroll(-1, "units") - elif e.num == 5: - self._canvas.yview_scroll(1, "units") - else: - self._canvas.yview_scroll(int(-1 * (e.delta / 120)), "units") - - # ── File / histogram loading ────────────────────────────────────────────── - def _open_file_dialog(self): - path = filedialog.askopenfilename( - title="Open ROOT file", filetypes=[("ROOT files", "*.root"), ("All files", "*.*")] - ) - if path: - self._load_file(path) - - def _load_file(self, path): - if ROOT is None: - messagebox.showerror("Missing dependency", "PyROOT is not available. Please install ROOT.") - return - f = ROOT.TFile.Open(path) - if not f or f.IsZombie(): - messagebox.showerror("Error", f"Cannot open ROOT file:\n{path}") - return - self._root_file = f - self._lbl_file.config(text=path) - - d = f.Get(self._tdir_path) - if not d: - messagebox.showerror("Error", f"Directory '{self._tdir_path}' not found in file.") - return - - histograms = [k.GetName() for k in d.GetListOfKeys() if k.ReadObj().InheritsFrom("TH1")] - if not histograms: - messagebox.showwarning("Warning", "No TH1 histograms found in directory.") - return - - self._hist_combo.config(values=histograms, state="readonly") - self._hist_combo.current(0) - self._on_hist_selected() - - def _on_hist_selected(self, _e=None): - if self._root_file is None: - return - d = self._root_file.Get(self._tdir_path) - if not d: - return - hname = self._hist_var.get() - hist = d.Get(hname) - if not hist: - return - self._hist = hist - self._groups = load_bins_from_hist(hist) - self._vars = {} - self._build_selections() - self._update_bitmask() - - # ── Selection cards ─────────────────────────────────────────────────────── - def _build_selections(self): - for w in self._inner.winfo_children(): - w.destroy() - - for sel_name, group in self._groups.items(): - self._build_group_card(sel_name, group) - - self._on_inner_configure() - - def _build_group_card(self, sel_name, group): - # categorise - minimal = [(i, b) for i, b in enumerate(group) if bin_type(b) == "minimal"] - optional = [(i, b) for i, b in enumerate(group) if bin_type(b) == "optional"] - neutral = [(i, b) for i, b in enumerate(group) if bin_type(b) == "neutral"] - - if not (minimal or optional or neutral): - return # nothing to show (all "skip") - - card = tk.Frame(self._inner, bg=BG_CARD, bd=0, highlightthickness=1, highlightbackground=BORDER) - card.pack(fill="x", padx=10, pady=5, ipadx=8, ipady=6) - - # header row - hdr = tk.Frame(card, bg=BG_CARD) - hdr.pack(fill="x", padx=6, pady=(4, 2)) - tk.Label(hdr, text=sel_name, font=FONT_HEAD, bg=BG_CARD, fg=FG).pack(side="left") - - # show the loosest (most permissive) minimal threshold as a hint. - # the truly loosest threshold has mSkipMostPermissiveBit=true so its - # BitPosition is "X" — it lands in the "skip" category. check there first, - # then fall back to the loosest bit-carrying minimal bin. - skipped_minimal = [ - b for b in group if b.get("MinimalCut", "0") == "1" and b.get("BitPosition", "X").upper() == "X" - ] - if skipped_minimal: - loosest_val = format_value_with_comment(skipped_minimal[0]) - tk.Label( - hdr, text=f"minimal cut → loosest selection: {loosest_val}", font=FONT_SMALL, bg=BG_CARD, fg=FG_DIM - ).pack(side="left", padx=10) - elif minimal: - loosest_val = format_value_with_comment(minimal[0][1]) - tk.Label( - hdr, text=f"minimal cut → loosest selection: {loosest_val}", font=FONT_SMALL, bg=BG_CARD, fg=FG_DIM - ).pack(side="left", padx=10) - elif optional: - tk.Label(hdr, text="optional", font=FONT_SMALL, bg=BG_CARD, fg=ACCENT_OPT).pack(side="left", padx=10) - elif neutral: - tk.Label(hdr, text="neutral", font=FONT_SMALL, bg=BG_CARD, fg=ACCENT_REJ).pack(side="left", padx=10) - - # separator - tk.Frame(card, bg=BORDER, height=1).pack(fill="x", padx=6, pady=2) - - # bins - bins_frame = tk.Frame(card, bg=BG_CARD) - bins_frame.pack(fill="x", padx=6, pady=4) - - for i, b in minimal: - self._build_bin_row(bins_frame, sel_name, i, b, "minimal") - for i, b in optional: - self._build_bin_row(bins_frame, sel_name, i, b, "optional") - for i, b in neutral: - self._build_bin_row(bins_frame, sel_name, i, b, "neutral") - - def _build_bin_row(self, parent, sel_name, idx, b, kind): - color = {"minimal": ACCENT, "optional": ACCENT_OPT, "neutral": ACCENT_REJ}[kind] - label_text = format_value_with_comment(b) - - var = tk.BooleanVar(value=False) - - self._vars[(sel_name, idx)] = var - - row = tk.Frame(parent, bg=BG_CARD) - row.pack(fill="x", pady=1) - - # coloured dot - tk.Label(row, text="●", font=FONT_BODY, bg=BG_CARD, fg=color).pack(side="left", padx=(0, 4)) - - # checkbox styled as a toggle button - cb = tk.Checkbutton( - row, - text=label_text, - variable=var, - font=FONT_BODY, - bg=BG_CARD, - fg=FG, - activebackground=BG_HOVER, - activeforeground=FG, - selectcolor=SEL_BG, - relief="flat", - bd=0, - highlightthickness=0, - cursor="hand2", - command=self._update_bitmask, - ) - cb.pack(side="left", fill="x", expand=True) - - # bit-position badge - pos = b.get("BitPosition", "X") - if pos.upper() != "X": - tk.Label(row, text=f"bit {pos}", font=FONT_SMALL, bg=BG_CARD, fg=FG_DIM, width=8).pack(side="right", padx=4) - - # ── Bitmask computation ─────────────────────────────────────────────────── - def _update_bitmask(self): - bitmask = 0 - for (sel_name, idx), var in self._vars.items(): - if not var.get(): - continue - b = self._groups[sel_name][idx] - pos = b.get("BitPosition", "X") - if pos.upper() == "X": - continue - bitmask |= 1 << int(pos) - - self._lbl_dec.config(text=str(bitmask)) - self._lbl_hex.config(text=hex(bitmask)) - self._lbl_bin.config(text=bin(bitmask)) - - # ── Utilities ───────────────────────────────────────────────────────────── - def _copy(self, text): - self.clipboard_clear() - self.clipboard_append(text) - self.update() - - @staticmethod - def _make_button(parent, text, cmd, color): - return tk.Button( - parent, - text=text, - command=cmd, - font=FONT_BODY, - bg=BG_CARD, - fg=color, - activebackground=BG_HOVER, - activeforeground=color, - relief="flat", - bd=0, - padx=10, - pady=4, - highlightthickness=1, - highlightbackground=color, - cursor="hand2", - ) - - -# ── Entry point ─────────────────────────────────────────────────────────────── -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="CutCulator GUI") - parser.add_argument("rootfile", nargs="?", default=None, help="Path to ROOT file (optional, can be opened via GUI)") - parser.add_argument("--dir", default="femto-producer", help="TDirectory path in ROOT file") - args = parser.parse_args() - - app = CutCulatorApp(rootfile=args.rootfile, tdir=args.dir) - app.mainloop() diff --git a/PWGCF/Femto/Macros/cutculator_gui.py b/PWGCF/Femto/Macros/cutculator_gui.py new file mode 100644 index 00000000000..9f0393895c6 --- /dev/null +++ b/PWGCF/Femto/Macros/cutculator_gui.py @@ -0,0 +1,1167 @@ +#!/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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +"""! +@brief CutCulator GUI (Compute bitmask for selecting particles in the Femto Framework) +@author Anton Riedel , Technical University of Munich +""" + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox +import argparse +import itertools +import json +import os +from typing import Any, Dict, List + +try: + import ROOT # pylint: disable=import-error + + ROOT.gROOT.SetBatch(True) +except ImportError: + ROOT = None + +VALUE_DELIM = "___" +SECTION_DELIM = ":::" + +# Above this number of enumerated variations the export asks for confirmation +# before building the list (the product over cuts grows multiplicatively). +VARIATION_WARN_LIMIT = 5000 + +# ── Colours ────────────────────────────────────────────────────────────────── +BG = "#1e1e2e" +BG_CARD = "#2a2a3e" +BG_HOVER = "#3a3a50" +ACCENT = "#89b4fa" # blue – minimal +ACCENT_OPT = "#a6e3a1" # green – optional +ACCENT_REJ = "#f38ba8" # red – rejection / neutral +ACCENT_ALWAYS = "#cba6f7" # purple – loosest/always-true (no bit set) +ACCENT_FILTER = "#f9e2af" # yellow – filter values (read-only) +FG = "#cdd6f4" +FG_DIM = "#6c7086" +BORDER = "#45475a" +SEL_BG = "#313244" + +FONT_TITLE = ("Inter", 15, "bold") +FONT_HEAD = ("Inter", 11, "bold") +FONT_BODY = ("Inter", 10) +FONT_MONO = ("JetBrains Mono", 11, "bold") +FONT_SMALL = ("Inter", 9) + +# Leading column width shared by every row type (checkbox glyph, loosest-bin +# spacer) so all rows' text starts at exactly the same x position. +CHECK_COL_WIDTH = 3 + + +# ── Helpers ─────────────────────────────────────────────────────────────────── +def parse_bin_label(label): + result = {} + for sec in label.split(SECTION_DELIM): + if VALUE_DELIM in sec: + k, v = sec.split(VALUE_DELIM, 1) + result[k.strip()] = v.strip() + return result + + +def format_value_with_comment(b): + val = b.get("Value", "") + comment = b.get("Comment", "") + if comment and comment.upper() != "X": + return f"{val} ({comment})" + return val + + +def bit_position_int(b): + """BitPosition as an int, or -1 for the always-applied (X) bins.""" + pos = b.get("BitPosition", "X") + return int(pos) if pos.upper() != "X" else -1 + + +def bin_type(b): + """Return 'minimal', 'optional', 'neutral', or 'skip'.""" + is_min = b.get("MinimalCut", "0") == "1" + is_opt = b.get("OptionalCut", "0") == "1" + pos = b.get("BitPosition", "X") + if pos.upper() == "X": + return "skip" + if is_min and not is_opt: + return "minimal" + if is_opt: + return "optional" + return "neutral" + + +def variation_kind(b): + """Axis kind used for grouping systematic variations. + + Identical to bin_type() except that an always-applied minimal floor + (BitPosition=="X", which bin_type() calls 'skip') is folded onto the + 'minimal' axis of its selection: it is the loosest rung of the same + threshold ladder, so it is a legitimate variation point alongside the + stricter, bit-carrying rungs — it just contributes no bit. + """ + kind = bin_type(b) + if kind == "skip" and b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0": + return "minimal" + return kind + + +def has_only_skipped_minimal_bins(group): + """ + Returns True when a group has at least one minimal bin but ALL of them have + BitPosition=="X" — meaning the cut is the loosest (always-true) selection + and no bit needs to be set. The group should still be displayed so the + user knows the cut exists. + """ + minimal = [b for b in group if b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0"] + return bool(minimal) and all(b.get("BitPosition", "X").upper() == "X" for b in minimal) + + +def get_skipped_minimal_bin_items(group): + """ + Returns (index_in_group, bin) for the minimal bins whose BitPosition=="X" — + i.e. the loosest threshold(s), always in effect unless a stricter minimal + bit is set. They carry no bit, but they are still a selectable variation + point (see variation_kind), so the index is needed to key self._vars. + """ + return [ + (i, b) + for i, b in enumerate(group) + if b.get("MinimalCut", "0") == "1" + and b.get("OptionalCut", "0") == "0" + and b.get("BitPosition", "X").upper() == "X" + ] + + +def get_skipped_minimal_bins(group): + """The bins from get_skipped_minimal_bin_items(), without their indices.""" + return [b for _i, b in get_skipped_minimal_bin_items(group)] + + +def is_filter_group(bins): + """ + Determine whether the parsed bins belong to a filter histogram (fixed, + non-selectable pre-filter values) rather than a selection/bitmask histogram. + Filter bins carry a "FilterName" key; selection bins carry "SelectionName". + """ + for b in bins: + if "FilterName" in b: + return True + if "SelectionName" in b: + return False + return False + + +def load_bins_from_hist(hist): + nbins = hist.GetNbinsX() + bins = [] + for i in range(1, nbins - 2 + 1): + label = hist.GetXaxis().GetBinLabel(i) + if not label: + continue + b = parse_bin_label(label) + b["_bin_index"] = i + bins.append(b) + + is_filter = is_filter_group(bins) + if is_filter: + # filter histograms are a flat list of fixed name/value pairs — no grouping needed + return bins, True + + # group by SelectionName preserving order + groups = {} + for b in bins: + name = b.get("SelectionName", f"unknown_{b['_bin_index']}") + groups.setdefault(name, []).append(b) + return groups, False + + +def resolve_file_path(path): + """Absolute, ~-expanded path for a local file. + + Remote specifications (root://, alien://, http://, …) are returned + untouched — os.path.abspath() would prepend the cwd and corrupt them. + """ + if "://" in path: + return path + return os.path.abspath(os.path.expanduser(path)) + + +def unique_key(mapping, base): + """Return `base`, or `base #2`, `base #3`, … if it is already taken. + + Needed because two different axes (or an axis and an always-applied floor) + can carry the same SelectionName; JSON objects cannot hold duplicate keys, + so the collision has to be resolved rather than silently overwritten. + """ + if base not in mapping: + return base + n = 2 + while f"{base} #{n}" in mapping: + n += 1 + return f"{base} #{n}" + + +# ── Main Application ────────────────────────────────────────────────────────── +class CutCulatorApp(tk.Tk): + def __init__(self, rootfile=None, tdir="femto-producer"): + super().__init__() + self.title("CutCulator") + self.configure(bg=BG) + self.geometry("1200x820") + self.minsize(900, 600) + self.resizable(True, True) + + self._rootfile_path = rootfile + self._tdir_path = tdir + self._root_file = None + self._hist = None + self._groups: Dict[str, List[Dict[str, Any]]] = {} # SelectionName → list[bin_dict] + self._filter_bins: List[Dict[str, Any]] = [] # list[bin_dict], used when the histogram is a filter histogram + self._is_filter_hist = False + self._vars = {} # (SelectionName, idx) → BooleanVar + self._check_labels = {} # (SelectionName, idx) → Label (custom checkbox glyph) + self._setters = {} # (SelectionName, idx) → callable(bool) setting the checkbox state + glyph + + self._build_ui() + + if rootfile: + self._load_file(rootfile) + + # ── UI skeleton ────────────────────────────────────────────────────────── + def _build_ui(self): + # ── top bar ── + top = tk.Frame(self, bg=BG, pady=10, padx=18) + top.pack(fill="x") + + tk.Label(top, text="CutCulator", font=FONT_TITLE, bg=BG, fg=ACCENT).pack(side="left") + + btn_frame = tk.Frame(top, bg=BG) + btn_frame.pack(side="right") + self._btn_open = self._make_button(btn_frame, "Open ROOT file", self._open_file_dialog, ACCENT) + self._btn_open.pack(side="left", padx=4) + + # ── file + histogram selector bar ── + bar = tk.Frame(self, bg=BG_CARD, pady=8, padx=18) + bar.pack(fill="x") + + tk.Label(bar, text="File:", font=FONT_BODY, bg=BG_CARD, fg=FG_DIM).pack(side="left") + self._lbl_file = tk.Label(bar, text="(none)", font=FONT_BODY, bg=BG_CARD, fg=FG, wraplength=400, anchor="w") + self._lbl_file.pack(side="left", padx=6) + + tk.Label(bar, text="Histogram:", font=FONT_BODY, bg=BG_CARD, fg=FG_DIM).pack(side="left", padx=(20, 0)) + self._hist_var = tk.StringVar() + self._hist_combo = ttk.Combobox(bar, textvariable=self._hist_var, state="disabled", width=30, font=FONT_BODY) + self._hist_combo.pack(side="left", padx=6) + self._hist_combo.bind("<>", self._on_hist_selected) + + # ── bitmask input: check all cuts corresponding to a given bitmask ── + tk.Label(bar, text="Bitmask:", font=FONT_BODY, bg=BG_CARD, fg=FG_DIM).pack(side="left", padx=(20, 0)) + self._mask_var = tk.StringVar() + mask_entry = tk.Entry( + bar, + textvariable=self._mask_var, + width=16, + font=FONT_BODY, + bg=BG, + fg=FG, + insertbackground=FG, + relief="flat", + highlightthickness=1, + highlightbackground=BORDER, + highlightcolor=ACCENT, + ) + mask_entry.pack(side="left", padx=6) + mask_entry.bind("", lambda _e: self._apply_bitmask()) + self._make_button(bar, "Set Bitmask", self._apply_bitmask, ACCENT).pack(side="left", padx=2) + + self._style_combobox() + + # ── legend ── + self._legend = tk.Frame(self, bg=BG, pady=4, padx=18) + self._legend.pack(fill="x") + self._build_legend_selection() + + # ── bottom result bar (packed to bottom BEFORE the main area so the + # main area fills remaining space correctly) ── + self._bottom = tk.Frame(self, bg=BG_CARD, pady=10, padx=18) + self._bottom.pack(fill="x", side="bottom") + + tk.Label(self._bottom, text="Bitmask:", font=FONT_HEAD, bg=BG_CARD, fg=FG).pack(side="left") + + self._lbl_dec = tk.Label(self._bottom, text="—", font=FONT_MONO, bg=BG_CARD, fg=ACCENT, width=14, anchor="w") + self._lbl_dec.pack(side="left", padx=8) + + self._lbl_hex = tk.Label( + self._bottom, text="—", font=FONT_MONO, bg=BG_CARD, fg=ACCENT_OPT, width=14, anchor="w" + ) + self._lbl_hex.pack(side="left", padx=8) + + self._lbl_bin = tk.Label(self._bottom, text="—", font=FONT_MONO, bg=BG_CARD, fg=FG_DIM, anchor="w") + self._lbl_bin.pack(side="left", padx=8) + + # live count of enumerated systematic variations for the current checkboxes + self._lbl_var_count = tk.Label(self._bottom, text="", font=FONT_SMALL, bg=BG_CARD, fg=ACCENT_ALWAYS, anchor="w") + self._lbl_var_count.pack(side="left", padx=12) + + copy_frame = tk.Frame(self._bottom, bg=BG_CARD) + copy_frame.pack(side="right") + self._make_button(copy_frame, "Copy Dec", lambda: self._copy(self._lbl_dec["text"]), ACCENT).pack( + side="left", padx=3 + ) + self._make_button(copy_frame, "Copy Hex", lambda: self._copy(self._lbl_hex["text"]), ACCENT_OPT).pack( + side="left", padx=3 + ) + self._make_button(copy_frame, "Export variations", self._export_variations, ACCENT_ALWAYS).pack( + side="left", padx=3 + ) + + # ── main paned area: selection cards (left) + summary sidebar (right) ── + self._paned = tk.PanedWindow(self, orient="horizontal", bg=BORDER, sashwidth=4, sashrelief="flat") + self._paned.pack(fill="both", expand=True, padx=12, pady=4) + + # ── scrollable selection area (left) ── + outer = tk.Frame(self._paned, bg=BG) + self._paned.add(outer, stretch="always", minsize=360) + + self._canvas = tk.Canvas(outer, bg=BG, highlightthickness=0, bd=0) + vsb = ttk.Scrollbar(outer, orient="vertical", command=self._canvas.yview) + self._canvas.configure(yscrollcommand=vsb.set) + vsb.pack(side="right", fill="y") + self._canvas.pack(side="left", fill="both", expand=True) + + self._inner = tk.Frame(self._canvas, bg=BG) + self._canvas_win = self._canvas.create_window((0, 0), window=self._inner, anchor="nw") + self._inner.bind("", self._on_inner_configure) + self._canvas.bind("", self._on_canvas_configure) + self._canvas.bind_all("", self._on_mousewheel) + self._canvas.bind_all("", self._on_mousewheel) + self._canvas.bind_all("", self._on_mousewheel) + + # ── summary sidebar (right) ── + self._summary_outer = tk.Frame(self._paned, bg=BG_CARD) + self._paned.add(self._summary_outer, stretch="never", minsize=280, width=320) + + summary_hdr = tk.Frame(self._summary_outer, bg=BG_CARD, pady=8, padx=10) + summary_hdr.pack(fill="x") + self._lbl_summary_title = tk.Label(summary_hdr, text="Selected cuts", font=FONT_HEAD, bg=BG_CARD, fg=FG) + self._lbl_summary_title.pack(anchor="w") + self._lbl_summary_empty = tk.Label(summary_hdr, text="(none)", font=FONT_SMALL, bg=BG_CARD, fg=FG_DIM) + self._lbl_summary_empty.pack(anchor="w", pady=(2, 0)) + + tk.Frame(self._summary_outer, bg=BORDER, height=1).pack(fill="x") + + summary_scroll_outer = tk.Frame(self._summary_outer, bg=BG_CARD) + summary_scroll_outer.pack(fill="both", expand=True) + + self._summary_canvas = tk.Canvas(summary_scroll_outer, bg=BG_CARD, highlightthickness=0, bd=0) + summary_vsb = ttk.Scrollbar(summary_scroll_outer, orient="vertical", command=self._summary_canvas.yview) + self._summary_canvas.configure(yscrollcommand=summary_vsb.set) + summary_vsb.pack(side="right", fill="y") + self._summary_canvas.pack(side="left", fill="both", expand=True) + + self._summary_inner = tk.Frame(self._summary_canvas, bg=BG_CARD) + self._summary_canvas_win = self._summary_canvas.create_window((0, 0), window=self._summary_inner, anchor="nw") + self._summary_inner.bind("", self._on_summary_inner_configure) + self._summary_canvas.bind("", self._on_summary_canvas_configure) + + def _build_legend_selection(self): + for w in self._legend.winfo_children(): + w.destroy() + for color, label in [ + (ACCENT, "Minimal"), + (ACCENT_OPT, "Optional"), + (ACCENT_REJ, "Neutral"), + (ACCENT_ALWAYS, "Loosest (no bit set)"), + ]: + dot = tk.Label(self._legend, text="●", font=FONT_BODY, bg=BG, fg=color) + dot.pack(side="left") + tk.Label(self._legend, text=label, font=FONT_SMALL, bg=BG, fg=FG_DIM).pack(side="left", padx=(2, 12)) + + def _build_legend_filter(self): + for w in self._legend.winfo_children(): + w.destroy() + dot = tk.Label(self._legend, text="●", font=FONT_BODY, bg=BG, fg=ACCENT_FILTER) + dot.pack(side="left") + tk.Label( + self._legend, + text="Filter value (fixed, already applied — not selectable)", + font=FONT_SMALL, + bg=BG, + fg=FG_DIM, + ).pack(side="left", padx=(2, 12)) + + # ── Combobox styling ────────────────────────────────────────────────────── + def _style_combobox(self): + s = ttk.Style(self) + s.theme_use("clam") + s.configure( + "TCombobox", + fieldbackground=BG_CARD, + background=BG_CARD, + foreground=FG, + selectbackground=SEL_BG, + selectforeground=FG, + bordercolor=BORDER, + arrowcolor=ACCENT, + ) + s.map("TCombobox", fieldbackground=[("readonly", BG_CARD)]) + s.configure("TScrollbar", troughcolor=BG, background=BORDER) + + # ── Canvas resize helpers ───────────────────────────────────────────────── + def _on_inner_configure(self, _e=None): + self._canvas.configure(scrollregion=self._canvas.bbox("all")) + + def _on_canvas_configure(self, e): + self._canvas.itemconfig(self._canvas_win, width=e.width) + + def _on_summary_inner_configure(self, _e=None): + self._summary_canvas.configure(scrollregion=self._summary_canvas.bbox("all")) + + def _on_summary_canvas_configure(self, e): + self._summary_canvas.itemconfig(self._summary_canvas_win, width=e.width) + + def _on_mousewheel(self, e): + if e.num == 4: + self._canvas.yview_scroll(-1, "units") + elif e.num == 5: + self._canvas.yview_scroll(1, "units") + else: + self._canvas.yview_scroll(int(-1 * (e.delta / 120)), "units") + + # ── File / histogram loading ────────────────────────────────────────────── + def _open_file_dialog(self): + path = filedialog.askopenfilename( + title="Open ROOT file", filetypes=[("ROOT files", "*.root"), ("All files", "*.*")] + ) + if path: + self._load_file(path) + + def _load_file(self, path): + if ROOT is None: + messagebox.showerror("Missing dependency", "PyROOT is not available. Please install ROOT.") + return + f = ROOT.TFile.Open(path) + if not f or f.IsZombie(): + messagebox.showerror("Error", f"Cannot open ROOT file:\n{path}") + return + self._root_file = f + # Keep the attribute in sync with what is actually loaded — it was + # previously only ever set from the CLI argument, so a file opened via + # the dialog left it pointing at the old (or None) path. Resolved to an + # absolute path so a relative CLI argument (./AnalysisResults.root) still + # gives a reproducible provenance record in the exported JSON. + self._rootfile_path = resolve_file_path(path) + self._lbl_file.config(text=self._rootfile_path) + + d = f.Get(self._tdir_path) + if not d: + messagebox.showerror("Error", f"Directory '{self._tdir_path}' not found in file.") + return + + histograms = [k.GetName() for k in d.GetListOfKeys() if k.ReadObj().InheritsFrom("TH1")] + if not histograms: + messagebox.showwarning("Warning", "No TH1 histograms found in directory.") + return + + self._hist_combo.config(values=histograms, state="readonly") + self._hist_combo.current(0) + self._on_hist_selected() + + def _on_hist_selected(self, _e=None): + if self._root_file is None: + return + d = self._root_file.Get(self._tdir_path) + if not d: + return + hname = self._hist_var.get() + hist = d.Get(hname) + if not hist: + return + self._hist = hist + self._vars = {} + self._check_labels = {} + self._setters = {} + + # Always start from a clean summary panel — forget()-ing the pane only + # hides it, it does not destroy previously built rows. + self._clear_summary() + self._lbl_var_count.config(text="") + + parsed, is_filter = load_bins_from_hist(hist) + self._is_filter_hist = is_filter + + if is_filter: + # load_bins_from_hist returns a flat list of bin dicts when is_filter + # is True. This check narrows `parsed` from the dict|list union the + # type checker infers from the function's two return shapes (same + # purpose an assert would serve, but unlike assert this is never + # stripped out under python -O, so it stays a real runtime guard). + if not isinstance(parsed, list): + raise TypeError("load_bins_from_hist() must return a list of bin dicts when is_filter is True") + self._filter_bins = parsed + self._groups = {} + self._build_legend_filter() + self._build_filter_view() + self._set_bitmask_bar_visible(False) + self._set_summary_visible(False) + else: + # load_bins_from_hist returns a dict of SelectionName -> bins when + # is_filter is False; same narrowing purpose as above. + if not isinstance(parsed, dict): + raise TypeError("load_bins_from_hist() must return a dict of bins when is_filter is False") + self._groups = parsed + self._filter_bins = [] + self._build_legend_selection() + self._build_selections() + self._set_bitmask_bar_visible(True) + self._set_summary_visible(True) + self._update_bitmask() + + def _clear_summary(self): + """Destroy all summary rows and reset to the empty state. Called on every + histogram switch so stale rows from a previous selection histogram never + linger while a filter histogram (which never calls _rebuild_summary) is + being shown.""" + for w in self._summary_inner.winfo_children(): + w.destroy() + self._lbl_summary_empty.config(text="(none)") + self._on_summary_inner_configure() + + def _set_bitmask_bar_visible(self, visible): + # self._bottom is a plain pack()ed frame (never added to the PanedWindow), + # so toggling it with pack()/pack_forget() is fine here. + if visible: + self._bottom.pack(fill="x", side="bottom") + else: + self._bottom.pack_forget() + + def _set_summary_visible(self, visible): + # self._summary_outer IS managed by the PanedWindow, so it must be + # shown/hidden via the PanedWindow's own add()/forget() API — calling + # pack() on it directly conflicts with the PanedWindow geometry manager + # and produces broken layout (this was the bug: the summary pane would + # swallow the whole window and the selection cards would disappear). + panes = self._paned.panes() + summary_path = str(self._summary_outer) + if visible: + if summary_path not in panes: + self._paned.add(self._summary_outer, stretch="never", minsize=280, width=320) + else: + if summary_path in panes: + self._paned.forget(self._summary_outer) + + # ── Filter (read-only) view ─────────────────────────────────────────────── + def _build_filter_view(self): + for w in self._inner.winfo_children(): + w.destroy() + + card = tk.Frame(self._inner, bg=BG_CARD, bd=0, highlightthickness=1, highlightbackground=BORDER) + card.pack(fill="x", padx=10, pady=5, ipadx=8, ipady=6) + + hdr = tk.Frame(card, bg=BG_CARD) + hdr.pack(fill="x", padx=6, pady=(4, 2)) + tk.Label(hdr, text="Filter values", font=FONT_HEAD, bg=BG_CARD, fg=FG).pack(side="left") + tk.Label( + hdr, + text="fixed values already applied when the producer ran — nothing to select", + font=FONT_SMALL, + bg=BG_CARD, + fg=FG_DIM, + ).pack(side="left", padx=10) + + tk.Frame(card, bg=BORDER, height=1).pack(fill="x", padx=6, pady=2) + + rows_frame = tk.Frame(card, bg=BG_CARD) + rows_frame.pack(fill="x", padx=6, pady=4) + + for b in self._filter_bins: + name = b.get("FilterName", "unknown") + value = b.get("FilterValue", "") + + row = tk.Frame(rows_frame, bg=BG_CARD) + row.pack(fill="x", pady=1) + + tk.Label(row, text="●", font=FONT_BODY, bg=BG_CARD, fg=ACCENT_FILTER).pack(side="left", padx=(0, 4)) + tk.Label(row, text=name, font=FONT_BODY, bg=BG_CARD, fg=FG, anchor="w").pack(side="left") + tk.Label(row, text=value, font=FONT_BODY, bg=BG_CARD, fg=FG_DIM, anchor="e").pack(side="right", padx=4) + + self._on_inner_configure() + + # ── Selection cards ─────────────────────────────────────────────────────── + def _build_selections(self): + for w in self._inner.winfo_children(): + w.destroy() + + for sel_name, group in self._groups.items(): + self._build_group_card(sel_name, group) + + self._on_inner_configure() + + def _build_group_card(self, sel_name, group): + # categorise into selectable bins + minimal = [(i, b) for i, b in enumerate(group) if bin_type(b) == "minimal"] + optional = [(i, b) for i, b in enumerate(group) if bin_type(b) == "optional"] + neutral = [(i, b) for i, b in enumerate(group) if bin_type(b) == "neutral"] + + # detect loosest/always-true: minimal bins exist but all have BitPosition==X + loosest_only = has_only_skipped_minimal_bins(group) + + # skip entirely if there is truly nothing to show + if not (minimal or optional or neutral or loosest_only): + return + + card = tk.Frame(self._inner, bg=BG_CARD, bd=0, highlightthickness=1, highlightbackground=BORDER) + card.pack(fill="x", padx=10, pady=5, ipadx=8, ipady=6) + + # header row + hdr = tk.Frame(card, bg=BG_CARD) + hdr.pack(fill="x", padx=6, pady=(4, 2)) + tk.Label(hdr, text=sel_name, font=FONT_HEAD, bg=BG_CARD, fg=FG).pack(side="left") + + if loosest_only: + # All minimal bins are BitPosition==X → loosest cut, no bit set + tk.Label( + hdr, + text="loosest selection — no bit set", + font=FONT_SMALL, + bg=BG_CARD, + fg=ACCENT_ALWAYS, + ).pack(side="left", padx=10) + else: + # show the loosest (most permissive) minimal threshold as a hint + skipped_minimal = get_skipped_minimal_bins(group) + if skipped_minimal: + loosest_val = format_value_with_comment(skipped_minimal[0]) + tk.Label( + hdr, + text=f"minimal cut → always applied floor: {loosest_val}", + font=FONT_SMALL, + bg=BG_CARD, + fg=FG_DIM, + ).pack(side="left", padx=10) + elif minimal: + loosest_val = format_value_with_comment(minimal[0][1]) + tk.Label( + hdr, + text=f"minimal cut → loosest selection: {loosest_val}", + font=FONT_SMALL, + bg=BG_CARD, + fg=FG_DIM, + ).pack(side="left", padx=10) + elif optional: + tk.Label(hdr, text="optional", font=FONT_SMALL, bg=BG_CARD, fg=ACCENT_OPT).pack(side="left", padx=10) + elif neutral: + tk.Label(hdr, text="neutral", font=FONT_SMALL, bg=BG_CARD, fg=ACCENT_REJ).pack(side="left", padx=10) + + # separator + tk.Frame(card, bg=BORDER, height=1).pack(fill="x", padx=6, pady=2) + + # bins + bins_frame = tk.Frame(card, bg=BG_CARD) + bins_frame.pack(fill="x", padx=6, pady=4) + + if loosest_only: + # The whole cut is a single always-true rung — there is no stricter + # alternative to vary against, so nothing to check: informational only. + for b in group: + if b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0": + self._build_loosest_row(bins_frame, None, None, b) + else: + # The loosest bin is always in effect when no stricter minimal bit is + # set — which makes it the loosest rung of the ladder and therefore a + # variation point in its own right. It gets a checkbox like any other + # rung; it just contributes no bit when picked. + for i, b in get_skipped_minimal_bin_items(group): + self._build_loosest_row(bins_frame, sel_name, i, b) + for i, b in minimal: + self._build_bin_row(bins_frame, sel_name, i, b, "minimal") + for i, b in optional: + self._build_bin_row(bins_frame, sel_name, i, b, "optional") + for i, b in neutral: + self._build_bin_row(bins_frame, sel_name, i, b, "neutral") + + def _build_loosest_row(self, parent, sel_name, idx, b): + """Row for a loosest/always-true cut — never sets a bit. + + With sel_name/idx given the row is checkable, so the loosest rung can be + included as a systematic variation; with both None it is purely + informational (used when the cut has no stricter rung to vary against). + + Either way the leading column is the same widget type/width (a Label with + width=CHECK_COL_WIDTH) as _build_bin_row's checkbox glyph, so all row + types line up pixel-for-pixel. A native tk.Checkbutton's indicator box has + no queryable pixel width, so matching it with a plain spacer Label never + aligns reliably — using an identical Label everywhere is the only way to + guarantee it. + """ + label_text = format_value_with_comment(b) + checkable = sel_name is not None and idx is not None + + row = tk.Frame(parent, bg=BG_CARD, cursor="hand2" if checkable else "") + row.pack(fill="x", pady=1) + + tk.Label(row, text="●", font=FONT_BODY, bg=BG_CARD, fg=ACCENT_ALWAYS).pack(side="left", padx=(0, 4)) + + check_lbl = tk.Label( + row, + text="[ ]" if checkable else " ", + font=FONT_BODY, + bg=BG_CARD, + fg=FG_DIM, + width=CHECK_COL_WIDTH, + anchor="w", + ) + check_lbl.pack(side="left") + + text_lbl = tk.Label(row, text=label_text, font=FONT_BODY, bg=BG_CARD, fg=FG_DIM, anchor="w") + text_lbl.pack(side="left", fill="x", expand=True) + + tk.Label(row, text="no bit", font=FONT_SMALL, bg=BG_CARD, fg=ACCENT_ALWAYS, width=8).pack(side="right", padx=4) + + if not checkable: + return + + var = tk.BooleanVar(value=False) + self._vars[(sel_name, idx)] = var + self._check_labels[(sel_name, idx)] = check_lbl + + def set_state(state): + var.set(state) + check_lbl.config(text="[x]" if state else "[ ]", fg=ACCENT_ALWAYS if state else FG_DIM) + text_lbl.config(fg=FG if state else FG_DIM) + + self._setters[(sel_name, idx)] = set_state + + def toggle(_e=None): + set_state(not var.get()) + self._update_bitmask() + + for w in (row, check_lbl, text_lbl): + w.bind("", toggle) + + def _build_bin_row(self, parent, sel_name, idx, b, kind): + color = {"minimal": ACCENT, "optional": ACCENT_OPT, "neutral": ACCENT_REJ}[kind] + label_text = format_value_with_comment(b) + + var = tk.BooleanVar(value=False) + self._vars[(sel_name, idx)] = var + + row = tk.Frame(parent, bg=BG_CARD, cursor="hand2") + row.pack(fill="x", pady=1) + + tk.Label(row, text="●", font=FONT_BODY, bg=BG_CARD, fg=color).pack(side="left", padx=(0, 4)) + + # Custom checkbox glyph, built from the SAME Label(width=CHECK_COL_WIDTH) + # pattern as the loosest-row spacer above — this is what actually fixes + # the alignment: both row types share one widget recipe for their + # leading column instead of trying to match a native Checkbutton's + # unmeasurable indicator box. + check_lbl = tk.Label(row, text="[ ]", font=FONT_BODY, bg=BG_CARD, fg=FG_DIM, width=CHECK_COL_WIDTH, anchor="w") + check_lbl.pack(side="left") + self._check_labels[(sel_name, idx)] = check_lbl + + text_lbl = tk.Label(row, text=label_text, font=FONT_BODY, bg=BG_CARD, fg=FG, anchor="w") + text_lbl.pack(side="left", fill="x", expand=True) + + # bit-position badge + pos = b.get("BitPosition", "X") + if pos.upper() != "X": + tk.Label(row, text=f"bit {pos}", font=FONT_SMALL, bg=BG_CARD, fg=FG_DIM, width=8).pack(side="right", padx=4) + + def set_state(state): + var.set(state) + check_lbl.config(text="[x]" if state else "[ ]", fg=color if state else FG_DIM) + + self._setters[(sel_name, idx)] = set_state + + def toggle(_e=None): + set_state(not var.get()) + self._update_bitmask() + + for w in (row, check_lbl, text_lbl): + w.bind("", toggle) + + # ── Bitmask input ───────────────────────────────────────────────────────── + def _apply_bitmask(self): + """Check exactly the cuts whose bit is set in the entered bitmask. The + always-applied minimal floors are added to the summary automatically.""" + if self._is_filter_hist or not self._groups: + messagebox.showinfo("Not applicable", "Load a selection histogram first.") + return + + text = self._mask_var.get().strip() + try: + bitmask = int(text, 0) + except ValueError: + try: + # int(x, 0) rejects decimals with leading zeros, e.g. "010" + bitmask = int(text, 10) + except ValueError: + bitmask = -1 + if bitmask < 0: + messagebox.showerror("Invalid bitmask", f"Cannot parse '{text}'.\nUse decimal, 0x hex or 0b binary.") + return + + known_bits = set() + for (sel_name, idx), set_state in self._setters.items(): + pos = bit_position_int(self._groups[sel_name][idx]) + if pos >= 0: + known_bits.add(pos) + set_state(pos >= 0 and bool(bitmask & (1 << pos))) + self._update_bitmask() + + unknown_bits = [i for i in range(bitmask.bit_length()) if bitmask & (1 << i) and i not in known_bits] + if unknown_bits: + messagebox.showwarning( + "Unknown bits", + f"Bit(s) {', '.join(map(str, unknown_bits))} are set but not defined in this histogram.", + ) + + # ── Bitmask computation + summary update ────────────────────────────────── + def _update_bitmask(self): + if self._is_filter_hist: + return + + bitmask = 0 + checked = [] # (sel_name, kind, pos_int, label_text, pos_str, color, is_loosest) + + for (sel_name, idx), var in self._vars.items(): + if not var.get(): + continue + b = self._groups[sel_name][idx] + pos = b.get("BitPosition", "X") + # a checked loosest rung sets no bit; variation_kind still files it + # under its selection's minimal axis + is_loosest = pos.upper() == "X" + kind = variation_kind(b) + color = ( + ACCENT_ALWAYS + if is_loosest + else {"minimal": ACCENT, "optional": ACCENT_OPT, "neutral": ACCENT_REJ}.get(kind, FG_DIM) + ) + + if not is_loosest: + bitmask |= 1 << int(pos) + + label_text = format_value_with_comment(b) + checked.append((sel_name, kind, bit_position_int(b), label_text, pos, color, is_loosest)) + + # Within the same (sel_name, kind) group, multiple checked bits represent a + # threshold ladder for one observable, not independent alternatives — only + # the strictest one (highest BitPosition) is the meaningful cut, so collapse + # to that single entry for display. The loosest rung sorts to -1 and so + # loses to any bit-carrying rung of the same ladder, which is correct: the + # bitmask bar shows one working point, not the scan. + tightest_by_group = {} + for sel_name, kind, pos_int, label_text, pos, color, is_loosest in checked: + key = (sel_name, kind) + if key not in tightest_by_group or pos_int > tightest_by_group[key][0]: + tightest_by_group[key] = (pos_int, label_text, pos, color, is_loosest) + + selected_entries = [ + (sel_name, label_text, pos, color, is_loosest) + for (sel_name, kind), (pos_int, label_text, pos, color, is_loosest) in tightest_by_group.items() + ] + + # Selection names that already have a checked "minimal" entry — for those, + # the checked threshold is strictly stricter than the always-applied floor + # (that's what makes it a selectable, higher-bit-position option in the + # first place), so the floor is logically subsumed and showing it alongside + # the checked value is redundant. Only surface the floor when nothing + # stricter has been chosen for that selection. + sel_names_with_checked_minimal = { + sel_name for (sel_name, kind) in tightest_by_group.keys() if kind == "minimal" + } + + # Loosest/always-applied bins: shown only when no stricter minimal cut for + # the same selection is currently checked — otherwise this entry is fully + # dominated by the checked one and would just duplicate the same cut. + for sel_name, group in self._groups.items(): + if sel_name in sel_names_with_checked_minimal: + continue + for b in get_skipped_minimal_bins(group): + label_text = format_value_with_comment(b) + selected_entries.append((sel_name, label_text, "X", ACCENT_ALWAYS, True)) + + self._lbl_dec.config(text=str(bitmask)) + self._lbl_hex.config(text=hex(bitmask)) + self._lbl_bin.config(text=bin(bitmask)) + + self._rebuild_summary(selected_entries) + self._update_variation_count() + + def _rebuild_summary(self, entries): + """Rebuild the selected-cuts summary sidebar. Narrow column → each value + gets its own row, stacked under the selection name, rather than packed + side-by-side in a single line.""" + self._clear_summary() + + if not entries: + return + + self._lbl_summary_empty.config(text="") + + # group by selection name, preserving is_loosest flag + by_name = {} + for sel_name, label_text, pos, color, is_loosest in entries: + by_name.setdefault(sel_name, []).append((label_text, pos, color, is_loosest)) + + for sel_name, items in by_name.items(): + block = tk.Frame(self._summary_inner, bg=BG_CARD) + block.pack(fill="x", padx=10, pady=(4, 2)) + + tk.Label( + block, + text=sel_name, + font=FONT_BODY, + bg=BG_CARD, + fg=FG, + anchor="w", + wraplength=280, + justify="left", + ).pack(fill="x") + + for label_text, pos, color, is_loosest in items: + entry_row = tk.Frame(block, bg=BG_CARD) + entry_row.pack(fill="x", padx=(14, 0), pady=1) + + tk.Label(entry_row, text="●", font=FONT_SMALL, bg=BG_CARD, fg=color).pack(side="left") + tk.Label( + entry_row, + text=label_text, + font=FONT_SMALL, + bg=BG_CARD, + fg=FG_DIM if is_loosest else FG, + anchor="w", + wraplength=220, + justify="left", + ).pack(side="left", padx=(4, 0), fill="x", expand=True) + + if is_loosest: + tk.Label( + entry_row, + text="no bit", + font=FONT_SMALL, + bg=BG_CARD, + fg=ACCENT_ALWAYS, + ).pack(side="right") + elif pos.upper() != "X": + tk.Label( + entry_row, + text=f"bit {pos}", + font=FONT_SMALL, + bg=BG_CARD, + fg=FG_DIM, + ).pack(side="right") + + tk.Frame(self._summary_inner, bg=BORDER, height=1).pack(fill="x", padx=10, pady=(4, 0)) + + self._on_summary_inner_configure() + + # ── Systematic variations ───────────────────────────────────────────────── + def _variation_axes(self): + """Group the currently checked bins into one variation axis per + (SelectionName, kind). + + Semantics: within one axis the checked bins are treated as MUTUALLY + EXCLUSIVE alternatives — each one is a separate systematic variation of + the same observable, not an additional cut applied on top. This holds for + minimal and optional cuts alike, and includes a checked loosest rung + (BitPosition=="X"), which is just the variation that sets no bit. + + Returns an ordered dict {(sel_name, kind): [bin_dict, ...]}, each list + sorted by ascending BitPosition (loosest → strictest). Insertion order + follows the card order on screen, so the enumeration is reproducible. + """ + axes: Dict[Any, List[Dict[str, Any]]] = {} + for (sel_name, idx), var in self._vars.items(): + if not var.get(): + continue + b = self._groups[sel_name][idx] + axes.setdefault((sel_name, variation_kind(b)), []).append(b) + + for bins in axes.values(): + bins.sort(key=bit_position_int) + return axes + + @staticmethod + def _axis_key_labels(axes): + """JSON key for each axis: the plain SelectionName, disambiguated with the + cut kind only when the same SelectionName carries more than one axis.""" + counts: Dict[str, int] = {} + for sel_name, _kind in axes: + counts[sel_name] = counts.get(sel_name, 0) + 1 + return { + (sel_name, kind): (sel_name if counts[sel_name] == 1 else f"{sel_name} [{kind}]") for sel_name, kind in axes + } + + def _always_applied_bins(self, axes): + """The BitPosition==X minimal floors that are in effect for every variation. + + Skipped for any SelectionName that has a checked minimal axis: either the + picked rung sits at a higher bit position and is therefore strictly + tighter (floor subsumed), or the floor itself is the picked rung and is + already carried by the axis. Emitting it here too would duplicate the key. + """ + sel_with_minimal_axis = {sel_name for sel_name, kind in axes if kind == "minimal"} + out = [] + for sel_name, group in self._groups.items(): + if sel_name in sel_with_minimal_axis: + continue + for b in get_skipped_minimal_bins(group): + out.append((sel_name, b)) + return out + + def _variation_count(self, axes=None): + axes = self._variation_axes() if axes is None else axes + if not axes: + return 0 + n = 1 + for bins in axes.values(): + n *= len(bins) + return n + + def _update_variation_count(self): + n = self._variation_count() + if n <= 1: + # 0 = nothing checked, 1 = a single fixed working point (no variation) + self._lbl_var_count.config(text="") + else: + self._lbl_var_count.config(text=f"→ {n} variations") + + def _compute_variations(self): + """Cartesian product over all variation axes → one dict per combination.""" + axes = self._variation_axes() + if not axes: + return [] + + labels = self._axis_key_labels(axes) + axis_items = list(axes.items()) + floors = self._always_applied_bins(axes) + + variations = [] + for combo in itertools.product(*[bins for _key, bins in axis_items]): + bitmask = 0 + entry: Dict[str, Any] = {} + for (key, _bins), b in zip(axis_items, combo): + pos = b.get("BitPosition", "X") + if pos.upper() != "X": + bitmask |= 1 << int(pos) + entry[unique_key(entry, labels[key])] = b.get("Value", "") + + # floors carry no bit but define the cut actually in effect, so they + # belong in the record of each variation + for sel_name, b in floors: + base = sel_name if sel_name not in entry else f"{sel_name} [floor]" + entry[unique_key(entry, base)] = b.get("Value", "") + + variations.append( + { + "index": len(variations), + "bitmask": bitmask, + "bitmask_hex": hex(bitmask), + **entry, + } + ) + return variations + + def _export_variations(self): + if self._is_filter_hist: + messagebox.showinfo( + "Not applicable", + "This is a filter histogram — its values are fixed and carry no bits, so there is nothing to vary.", + ) + return + + axes = self._variation_axes() + if not axes: + messagebox.showwarning( + "Nothing selected", + "Check the options you want to scan first.\n\n" + "Options checked within the same cut are enumerated as alternative " + "variations; the export is the product across all cuts.", + ) + return + + n = self._variation_count(axes) + if n > VARIATION_WARN_LIMIT and not messagebox.askyesno( + "Many variations", f"This will enumerate {n} variations. Continue?" + ): + return + + variations = self._compute_variations() + + path = filedialog.asksaveasfilename( + title="Save systematic variations", + defaultextension=".json", + initialfile="cut_variations.json", + filetypes=[("JSON files", "*.json"), ("All files", "*.*")], + ) + if not path: + return + + payload = { + "file": self._rootfile_path, + "directory": self._tdir_path, + "histogram": self._hist_var.get(), + "n_variations": len(variations), + "variations": variations, + } + + try: + with open(path, "w", encoding="utf-8") as fh: + json.dump(payload, fh, indent=2) + fh.write("\n") + except OSError as exc: + messagebox.showerror("Error", f"Could not write file:\n{exc}") + return + + messagebox.showinfo("Exported", f"Wrote {len(variations)} variations to\n{path}") + + # ── Utilities ───────────────────────────────────────────────────────────── + def _copy(self, text): + self.clipboard_clear() + self.clipboard_append(text) + self.update() + + @staticmethod + def _make_button(parent, text, cmd, color): + return tk.Button( + parent, + text=text, + command=cmd, + font=FONT_BODY, + bg=BG_CARD, + fg=color, + activebackground=BG_HOVER, + activeforeground=color, + relief="flat", + bd=0, + padx=10, + pady=4, + highlightthickness=1, + highlightbackground=color, + cursor="hand2", + ) + + +# ── Entry point ─────────────────────────────────────────────────────────────── +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="CutCulator GUI") + parser.add_argument("rootfile", nargs="?", default=None, help="Path to ROOT file (optional, can be opened via GUI)") + parser.add_argument("--dir", default="femto-producer", help="TDirectory path in ROOT file") + args = parser.parse_args() + + app = CutCulatorApp(rootfile=args.rootfile, tdir=args.dir) + app.mainloop() diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 8fb76cfc5fb..9658d1b354f 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -10,8 +10,8 @@ # or submit itself to any jurisdiction. o2physics_add_dpl_workflow(femto-producer - SOURCES femtoProducer.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + SOURCES ./femtoProducer.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(femto-producer-derived-to-derived @@ -19,12 +19,32 @@ o2physics_add_dpl_workflow(femto-producer-derived-to-derived PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-producer-lite-converter + SOURCES ./femtoProducerLiteConverter.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-producer-kink-pt-converter SOURCES ./femtoProducerKinkPtConverter.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-producer-mask-converter-001-to-002 + SOURCES ./femtoProducerMaskConverter001To002.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-producer-resonances SOURCES ./femtoProducerResonances.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-two-photon-resonances + SOURCES ./femtoProducerTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-producer-track-track-two-photon-resonances + SOURCES ./femtoProducerTrackTrackTwoPhotonResonances.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index 6d655cb02e2..711183d7327 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -14,13 +14,19 @@ /// \author Anton Riedel, TU München, anton.riedel@tum.de #include "PWGCF/Femto/Core/cascadeBuilder.h" +#include "PWGCF/Femto/Core/charmHadronBuilder.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/mcBuilder.h" #include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/photonBuilder.h" #include "PWGCF/Femto/Core/trackBuilder.h" #include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGLF/DataModel/LFKinkDecayTables.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/mcCentrality.h" @@ -30,9 +36,11 @@ #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include @@ -40,22 +48,31 @@ #include #include #include +#include #include #include +#include #include #include +#include +#include +#include using namespace o2::analysis::femto; -namespace o2::analysis::femto +namespace o2::analysis::femto::rawinputs { -namespace consumeddata -{ -using Run3PpCollisions = o2::soa::Join; +using Run3PpCollisions = o2::soa::Join; using Run3PpMcRecoCollisions = o2::soa::Join; using Run3PpMcGenCollisions = o2::soa::Join; +using Run3PbPbCollisions = o2::soa::Join; +using Run3PbPbCollisionsWithEventShape = o2::soa::Join; + +using Run3PbPbMcRecoCollisions = o2::soa::Join; +using Run3PbPbMcGenCollisions = o2::soa::Join; + using Run3FullPidTracks = soa::Join; using Run3McRecoTracks = soa::Join; -using Run3Vzeros = o2::aod::V0Datas; -using Run3RecoVzeros = o2::soa::Join; +using Run3Vzeros = o2::soa::Join; +using Run3RecoVzeros = o2::soa::Join; + +using Run3D0s = soa::Join; +using Run3RecoD0s = soa::Join; -using Run3Cascades = o2::aod::CascDatas; +using Run3Lcs = soa::Join; +using Run3RecoLcs = soa::Join; + +using Run3Cascades = o2::soa::Join; using Run3RecoCascades = o2::soa::Join; using Run3Kinks = o2::aod::KinkCands; +using Run3SigmaPlusPcms = o2::aod::SigmaPlusCands; +using Run3RecoSigmaPlusPcms = o2::aod::SigmaPlusCandsMC; + using Run3McGenParticles = o2::aod::McParticles; -} // namespace consumeddata -} // namespace o2::analysis::femto +} // namespace o2::analysis::femto::rawinputs struct FemtoProducer { - // ccdb + // all Preslice declarations, grouped into a single data member + struct : public o2::framework::PresliceGroup { + o2::framework::Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; + o2::framework::Preslice perColTracks = o2::aod::track::collisionId; + o2::framework::Preslice perColRecoTracks = o2::aod::track::collisionId; + o2::framework::Preslice perColV0s = o2::aod::v0data::collisionId; + o2::framework::Preslice perColRecoV0s = o2::aod::v0data::collisionId; + o2::framework::Preslice perColRecoPCMPhotons = o2::aod::v0photonkf::collisionId; + o2::framework::Preslice perColCascades = o2::aod::cascdata::collisionId; + o2::framework::Preslice perColRecoCascades = o2::aod::cascdata::collisionId; + o2::framework::Preslice perColRecoKinks = o2::aod::kinkcand::collisionId; + o2::framework::Preslice perColSigmaPlusPcms = o2::aod::sigmapluscand::collisionId; + o2::framework::Preslice perColRecoSigmaPlusPcms = o2::aod::sigmapluscand::collisionId; + o2::framework::Preslice perColD0s = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColRecoD0s = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColLcs = o2::aod::hf_cand::collisionId; + o2::framework::Preslice perColRecoLcs = o2::aod::hf_cand::collisionId; + } preslices; + + // plain builder helper objects, grouped into a single data member + struct { + collisionbuilder::CollisionBuilder collisionBuilder; + trackbuilder::TrackBuilder trackBuilder; + v0builder::V0Builder k0shortBuilder; + v0builder::V0Builder lambdaBuilder; + v0builder::V0Builder antilambdaBuilder; + photonbuilder::PhotonBuilder photonBuilder; + charmhadronbuilder::CharmHadronBuilder d0Builder; + charmhadronbuilder::CharmHadronBuilder d0barBuilder; + charmhadronbuilder::CharmHadronBuilder lcBuilder; + charmhadronbuilder::CharmHadronBuilder lcBarBuilder; + cascadebuilder::CascadeBuilder xiBuilder; + cascadebuilder::CascadeBuilder omegaBuilder; + kinkbuilder::KinkBuilder sigmaBuilder; + kinkbuilder::KinkBuilder sigmaPlusBuilder; + kinkbuilder::SigmaPlusPcmBuilder sigmaPlusPcmBuilder; + mcbuilder::McBuilder mcBuilder; + } builders; + + // ccdb config collisionbuilder::ConfCcdb confCcdb; // collision builder @@ -87,12 +151,10 @@ struct FemtoProducer { collisionbuilder::ConfCollisionFilters confCollisionFilters; collisionbuilder::ConfCollisionBits confCollisionBits; collisionbuilder::ConfCollisionRctFlags confCollisionRctFlags; - collisionbuilder::CollisionBuilder collisionBuilder; // track builder trackbuilder::TrackBuilderProducts trackBuilderProducts; trackbuilder::ConfTrackTables confTrackTables; - trackbuilder::TrackBuilder trackBuilder; trackbuilder::ConfTrackBits confTrackBits; trackbuilder::ConfTrackFilters confTrackFilters; @@ -101,86 +163,244 @@ struct FemtoProducer { v0builder::ConfV0Tables confV0Tables; v0builder::ConfV0Filters confV0Filters; v0builder::ConfK0shortBits confK0shortBits; - v0builder::V0Builder k0shortBuilder; v0builder::ConfLambdaBits confLambdaBits; - v0builder::V0Builder lambdaBuilder; - v0builder::V0Builder antilambdaBuilder; + + // photon (PCM) builder + photonbuilder::PhotonBuilderProducts photonBuilderProducts; + photonbuilder::ConfPhotonTables confPhotonTables; + photonbuilder::ConfPhotonFilters confPhotonFilters; + photonbuilder::ConfPhotonBits confPhotonBits; + + // charm hadron builder + charmhadronbuilder::CharmHadronBuilderProducts charmHadronBuilderProducts; + charmhadronbuilder::ConfCharmHadronFilters confCharmHadronFilters; + charmhadronbuilder::ConfCharmHadronTables confCharmHadronTables; + charmhadronbuilder::ConfD0Bits confD0Bits; + charmhadronbuilder::ConfLcBits confLcBits; // cascade builder cascadebuilder::CascadeBuilderProducts cascadeBuilderProducts; cascadebuilder::ConfCascadeTables confCascadeTables; cascadebuilder::ConfCascadeFilters confCascadeFilters; cascadebuilder::ConfXiBits confXiBits; - cascadebuilder::CascadeBuilder xiBuilder; cascadebuilder::ConfOmegaBits confOmegaBits; - cascadebuilder::CascadeBuilder omegaBuilder; // kink builder kinkbuilder::KinkBuilderProducts kinkBuilderProducts; kinkbuilder::ConfKinkTables confKinkTables; kinkbuilder::ConfKinkFilters confKinkFilters; kinkbuilder::ConfSigmaBits confSigmaBits; - kinkbuilder::KinkBuilder sigmaBuilder; kinkbuilder::ConfSigmaPlusBits confSigmaPlusBits; - kinkbuilder::KinkBuilder sigmaPlusBuilder; + kinkbuilder::ConfSigmaPlusPcmBits confSigmaPlusPcmBits; // mc builder mcbuilder::ConfMc confMc; mcbuilder::ConfMcTables confMcTables; mcbuilder::McBuilderProducts mcProducts; - mcbuilder::McBuilder mcBuilder; // histogramming - // add histograms in next iteration o2::framework::HistogramRegistry hRegistry{"FemtoProducer", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; // data members - o2::framework::Service ccdb; /// Accessing the CCDB + o2::framework::Service ccdb = {}; /// Accessing the CCDB + o2::framework::Service pdgDb = {}; void init(o2::framework::InitContext& context) { - if ((xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesKinksRun3pp)) { + // ---- ccdb --------------------------------------------------------------- + LOG(info) << "Setting up connection to CCDB with URL: " << confCcdb.ccdbUrl.value; + ccdb->setURL(confCcdb.ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + // ---- builders ----------------------------------------------------------- + // Order matters here: CollisionBuilder must init before TrackBuilder (which checks + // builders.collisionBuilder.producingCollisions()/producingLiteCollisions()), and TrackBuilder + // must init before the V0/Cascade/Kink builders (which check + // builders.trackBuilder.producingTracks()/producingLiteTracks()). + builders.collisionBuilder.init(&hRegistry, confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); + builders.trackBuilder.init(&hRegistry, confTrackBits, confTrackFilters, confTrackTables, context, builders.collisionBuilder); + + builders.k0shortBuilder.init(&hRegistry, confK0shortBits, confV0Filters, confV0Tables, context, builders.trackBuilder); + builders.lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, builders.trackBuilder); + builders.antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context, builders.trackBuilder); + + builders.photonBuilder.init(&hRegistry, confPhotonBits, confPhotonFilters, confPhotonTables, context); + + builders.d0Builder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); + builders.d0barBuilder.init(&hRegistry, confD0Bits, confCharmHadronFilters, confCharmHadronTables, context); + + builders.lcBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); + builders.lcBarBuilder.init(&hRegistry, confLcBits, confCharmHadronFilters, confCharmHadronTables, context); + + builders.sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); + builders.sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); + builders.sigmaPlusPcmBuilder.init(&hRegistry, confSigmaPlusPcmBits, confKinkFilters, confKinkTables, context, builders.trackBuilder); + + builders.xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); + builders.omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context, builders.trackBuilder); + + builders.mcBuilder.init(confMc, confMcTables, context); + + // ---- guard: enabled tables vs. enabled process function ------------------ + if ((builders.xiBuilder.fillAnyTable() || builders.omegaBuilder.fillAnyTable()) && + (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && + !doprocessTracksV0sCascadesRun3PbPbWithEventShape && + !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && + !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && + !doprocessTracksV0sCascadesRun3ppMc && + !doprocessTracksV0sCascadesRun3PbPbMc)) { LOG(fatal) << "At least one cascade table is enabled, but wrong process function is enabled. Breaking..."; } - if ((lambdaBuilder.fillAnyTable() || antilambdaBuilder.fillAnyTable() || k0shortBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sRun3ppMc)) { + if ((builders.lambdaBuilder.fillAnyTable() || builders.antilambdaBuilder.fillAnyTable() || builders.k0shortBuilder.fillAnyTable()) && + (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesRun3PbPb && + !doprocessTracksV0sRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && + !doprocessTracksV0sCascadesRun3PbPbWithEventShape && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape && + !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPb && !doprocessTracksV0sRun3PbPbMc && + !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && + !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one v0 table is enabled, but wrong process function is enabled. Breaking..."; } - if ((sigmaBuilder.fillAnyTable() || sigmaPlusBuilder.fillAnyTable()) && (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksKinksRun3ppMc)) { + if (builders.photonBuilder.fillAnyTable() && + (!doprocessTracksPhotonsRun3pp && !doprocessTracksPhotonsRun3PbPb && + !doprocessTracksV0sCascadesKinksPhotonsRun3pp && !doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape)) { + LOG(fatal) << "At least one photon table is enabled, but wrong process function is enabled. Breaking..."; + } + if ((builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable()) && + (!doprocessTracksKinksRun3pp && !doprocessTracksV0sCascadesKinksRun3pp && !doprocessTracksV0sCascadesKinksPhotonsRun3pp && + !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc)) { LOG(fatal) << "At least one kink table is enabled, but wrong process function is enabled. Breaking..."; } - if (mcBuilder.fillAnyTable() && (!doprocessTracksV0sRun3ppMc && !doprocessTracksKinksRun3ppMc)) { + if (builders.sigmaPlusPcmBuilder.fillAnyTable() && + (!doprocessTracksSigmaPlusPcmsRun3pp && !doprocessTracksSigmaPlusPcmsRun3ppMc)) { + LOG(fatal) << "At least one sigma plus (PCM) table is enabled, but wrong process function is enabled. Breaking..."; + } + if ((builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable()) && + (!doprocessTracksD0sRun3pp && !doprocessTracksD0sRun3PbPb && + !doprocessTracksD0sRun3ppMc && !doprocessTracksD0sRun3PbPbMc)) { + LOG(fatal) << "At least one d0 table is enabled, but wrong process function is enabled. Breaking..."; + } + if ((builders.lcBuilder.fillAnyTable() || builders.lcBarBuilder.fillAnyTable()) && + (!doprocessTracksLcsRun3pp && !doprocessTracksLcsRun3PbPb && + !doprocessTracksLcsRun3ppMc && !doprocessTracksLcsRun3PbPbMc)) { + LOG(fatal) << "At least one lc table is enabled, but wrong process function is enabled. Breaking..."; + } + if (builders.mcBuilder.fillAnyTable() && + (!doprocessTracksRun3ppMc && !doprocessTracksRun3PbPbMc && + !doprocessTracksV0sRun3ppMc && !doprocessTracksV0sRun3PbPbMc && + !doprocessTracksV0sCascadesRun3ppMc && !doprocessTracksV0sCascadesRun3PbPbMc && + !doprocessTracksKinksRun3ppMc && !doprocessTracksV0sKinksRun3ppMc && + !doprocessTracksSigmaPlusPcmsRun3ppMc && + !doprocessTracksD0sRun3ppMc && !doprocessTracksD0sRun3PbPbMc && + !doprocessTracksLcsRun3ppMc && !doprocessTracksLcsRun3PbPbMc && + !doprocessMcOnly)) { LOG(fatal) << "At least one mc table is enabled, but wrong process function is enabled. Breaking..."; } - // init ccdb - LOG(info) << "Setting up connection to CCDB with URL: " << confCcdb.ccdbUrl.value; - ccdb->setURL(confCcdb.ccdbUrl.value); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - ccdb->setCreatedNotAfter(now); + // ---- guard: pass-through consistency across builders --------------------- + { + std::vector> passThroughFlags; + auto add = [&passThroughFlags](const std::string& name, bool enabled, bool passThrough) { + if (enabled) { + passThroughFlags.emplace_back(name, passThrough); + } + }; + // substitute the real accessors if they live on the builders rather than the conf groups + add("collision", builders.collisionBuilder.fillAnyTable(), builders.collisionBuilder.isPassThrough()); + add("track", builders.trackBuilder.fillAnyTable(), builders.trackBuilder.isPassThrough()); + add("k0short", builders.k0shortBuilder.fillAnyTable(), builders.k0shortBuilder.isPassThrough()); + add("lambda", builders.lambdaBuilder.fillAnyTable(), builders.lambdaBuilder.isPassThrough()); + add("antilambda", builders.antilambdaBuilder.fillAnyTable(), builders.antilambdaBuilder.isPassThrough()); + add("photon", builders.photonBuilder.fillAnyTable(), builders.photonBuilder.isPassThrough()); + add("d0", builders.d0Builder.fillAnyTable(), builders.d0Builder.isPassThrough()); + add("d0bar", builders.d0barBuilder.fillAnyTable(), builders.d0barBuilder.isPassThrough()); + add("lc", builders.lcBuilder.fillAnyTable(), builders.lcBuilder.isPassThrough()); + add("lcbar", builders.lcBarBuilder.fillAnyTable(), builders.lcBarBuilder.isPassThrough()); + add("sigma", builders.sigmaBuilder.fillAnyTable(), builders.sigmaBuilder.isPassThrough()); + add("sigmaplus", builders.sigmaPlusBuilder.fillAnyTable(), builders.sigmaPlusBuilder.isPassThrough()); + add("sigmapluspcm", builders.sigmaPlusPcmBuilder.fillAnyTable(), builders.sigmaPlusPcmBuilder.isPassThrough()); + add("xi", builders.xiBuilder.fillAnyTable(), builders.xiBuilder.isPassThrough()); + add("omega", builders.omegaBuilder.fillAnyTable(), builders.omegaBuilder.isPassThrough()); + add("mc", builders.mcBuilder.fillAnyTable(), builders.mcBuilder.isPassThrough()); - // collision selection - collisionBuilder.init(&hRegistry, confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); + const bool anyPassThrough = std::any_of(passThroughFlags.begin(), passThroughFlags.end(), + [](const auto& f) { return f.second; }); + const bool allPassThrough = std::all_of(passThroughFlags.begin(), passThroughFlags.end(), + [](const auto& f) { return f.second; }); - // configure track builder - trackBuilder.init(&hRegistry, confTrackBits, confTrackFilters, confTrackTables, context); + if (anyPassThrough && !allPassThrough) { + LOG(warn) << "Builders disagree about pass-through mode:"; + for (const auto& [name, passThrough] : passThroughFlags) { + LOG(warn) << " " << name << " builder: pass-through = " << (passThrough ? "true" : "false"); + } + LOG(warn) << "This is legal, but check it is intended:"; + LOG(warn) << " - collisions in pass-through, particles not: every collision is written, " + << "but the particle tables only cover collisions with a candidate."; + LOG(warn) << " - particles in pass-through, collisions not: the extra particle rows hang off " + << "a candidate-biased event sample, which will bias event-normalised observables."; + } - // configure v0 builder - k0shortBuilder.init(&hRegistry, confK0shortBits, confV0Filters, confV0Tables, context); - lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context); - antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context); + // in mc pass-through every generated collision is written, but reco collisions of other + // sub-generators are still rejected in checkCollision -> biased event-loss denominator + if (builders.mcBuilder.isPassThrough() && builders.collisionBuilder.isPassThrough() && builders.collisionBuilder.subGeneratorId() >= 0) { + LOG(warn) << "MC and collision pass-through are enabled, but CollisionFilter.subGeneratorId = " + << builders.collisionBuilder.subGeneratorId() << " still rejects reconstructed collisions of other sub-generators, " + << "while all generated collisions are written. Set it to -1 for unbiased event/signal-loss corrections."; + } - // configure kink builder - sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context); - sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context); + // daughter tracks can be written a second time under the collision of their mother candidate; + // without FTrackExtras (fillType) these rows cannot be told apart from selected tracks downstream + const bool anyDaughterBuilder = builders.k0shortBuilder.fillAnyTable() || builders.lambdaBuilder.fillAnyTable() || builders.antilambdaBuilder.fillAnyTable() || + builders.xiBuilder.fillAnyTable() || builders.omegaBuilder.fillAnyTable() || + builders.sigmaBuilder.fillAnyTable() || builders.sigmaPlusBuilder.fillAnyTable() || builders.sigmaPlusPcmBuilder.fillAnyTable() || + builders.d0Builder.fillAnyTable() || builders.d0barBuilder.fillAnyTable() || builders.lcBuilder.fillAnyTable() || builders.lcBarBuilder.fillAnyTable(); + if (builders.trackBuilder.isPassThrough() && anyDaughterBuilder && !builders.trackBuilder.producingTrackExtras()) { + LOG(warn) << "Track pass-through is enabled together with candidates that register daughter tracks, but FTrackExtras is not produced. " + << "Daughter-only track rows cannot be identified downstream (enable TrackTables.produceTrackExtras)."; + } + } - // cascade selections - xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context); - omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context); + // ---- guard: exactly one process function --------------------------------- + const int nProcesses = + static_cast(doprocessTracksRun3pp) + + static_cast(doprocessTracksRun3PbPb) + + static_cast(doprocessTracksRun3PbPbWithEventShape) + + static_cast(doprocessTracksV0sRun3pp) + + static_cast(doprocessTracksPhotonsRun3pp) + + static_cast(doprocessTracksPhotonsRun3PbPb) + + static_cast(doprocessTracksV0sRun3PbPb) + + static_cast(doprocessTracksV0sCascadesRun3pp) + + static_cast(doprocessTracksV0sCascadesRun3PbPb) + + static_cast(doprocessTracksV0sCascadesRun3PbPbWithEventShape) + + static_cast(doprocessTracksV0sCascadesPhotonsRun3PbPbWithEventShape) + + static_cast(doprocessTracksKinksRun3pp) + + static_cast(doprocessTracksV0sCascadesKinksRun3pp) + + static_cast(doprocessTracksV0sCascadesKinksPhotonsRun3pp) + + static_cast(doprocessTracksSigmaPlusPcmsRun3pp) + + static_cast(doprocessTracksD0sRun3pp) + + static_cast(doprocessTracksD0sRun3PbPb) + + static_cast(doprocessTracksRun3ppMc) + + static_cast(doprocessTracksRun3PbPbMc) + + static_cast(doprocessTracksV0sRun3ppMc) + + static_cast(doprocessTracksV0sRun3PbPbMc) + + static_cast(doprocessTracksV0sCascadesRun3ppMc) + + static_cast(doprocessTracksV0sCascadesRun3PbPbMc) + + static_cast(doprocessTracksKinksRun3ppMc) + + static_cast(doprocessTracksV0sKinksRun3ppMc) + + static_cast(doprocessTracksSigmaPlusPcmsRun3ppMc) + + static_cast(doprocessTracksD0sRun3ppMc) + + static_cast(doprocessTracksD0sRun3PbPbMc) + + static_cast(doprocessTracksLcsRun3pp) + + static_cast(doprocessTracksLcsRun3PbPb) + + static_cast(doprocessTracksLcsRun3ppMc) + + static_cast(doprocessTracksLcsRun3PbPbMc) + + static_cast(doprocessMcOnly); - // configure mcBuilder - mcBuilder.init(confMc, confMcTables, context); + if (nProcesses != 1) { + LOG(fatal) << "Exactly one process function must be enabled, found " << nProcesses << ". Breaking..."; + } hRegistry.print(); } @@ -189,270 +409,970 @@ struct FemtoProducer { template bool processCollisions(T1 const& col, T2 const& /* bcs*/, T3 const& tracks) { - collisionBuilder.reset(); + builders.collisionBuilder.reset(); auto bc = col.template bc_as(); - collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); - if (!collisionBuilder.checkCollision(col)) { + builders.collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); + if (!builders.collisionBuilder.checkCollision(col)) { return false; } - // do not fill collsions into the table here - // collisions are filled if at least one partilce is found in the collisions + // Normally the collision row is written lazily, once a candidate is found. In + // pass-through we need every accepted collision, including those with no candidate, + // otherwise event-normalised observables lose exactly those events. + if (builders.collisionBuilder.isPassThrough()) { + builders.collisionBuilder.fillCollision(collisionBuilderProducts, col); + } return true; } - template - bool processMcCollisions(T1 const& col, T2 const& mcCols, T3 const& /* bcs*/, T4 const& tracks, T5 const& mcParticles) + + template + bool processMcCollisions(T1 const& col, T2 const& mcCols, T3 const& /* bcs*/, T4 const& tracks) { - collisionBuilder.reset(); - mcBuilder.reset(mcCols, mcParticles); // we call this function always first so we reset the mcBuilder here + builders.collisionBuilder.reset(); + // builders.mcBuilder.reset() runs once per timeframe in the calling processTracksXXXMc function + // (both pass-through and lazy-fill mode); resetting here on every reco collision would + // wipe the dedup maps and duplicate every mc collision/particle referenced by more than + // one reco collision (e.g. split-vertex reconstruction under pileup) auto bc = col.template bc_as(); - collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); - if (!collisionBuilder.checkCollision(col, mcCols)) { + builders.collisionBuilder.initCollision(bc, col, tracks, ccdb, hRegistry); + if (!builders.collisionBuilder.checkCollision(col, mcCols)) { return false; } - // do not fill collsions into the table here - // collisions are filled if at least one partilce is found in the collisions + // NOTE: must be fillMcCollision, not fillCollision - fillMcCollision returns early on + // mCollisionAlreadyFilled, so filling the collision first would permanently skip + // the collision label + if (builders.collisionBuilder.isPassThrough()) { + builders.collisionBuilder.fillMcCollision(collisionBuilderProducts, col, mcCols, mcProducts, builders.mcBuilder); + } return true; } // processing tracks + // NOTE: trackBuilder.reset() is NOT called here. It must happen exactly once per dataframe + // (in the calling processTracksXXX/processTracksXXXMc function, before the reco-collision + // loop), not once per collision, so that fillTrack()'s "protect against double filling" dedup + // (indexMap) can actually catch a track referenced from more than one collision in the same + // dataframe instead of being wiped before it gets the chance. template void processTracks(T1 const& col, T2 const& tracksWithItsPid) { - trackBuilder.reset(tracksWithItsPid); - trackBuilder.fillTracks(col, collisionBuilder, collisionBuilderProducts, tracksWithItsPid, trackBuilderProducts); + builders.trackBuilder.fillTracks(col, builders.collisionBuilder, collisionBuilderProducts, tracksWithItsPid, trackBuilderProducts); } template void processMcTracks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& mcParticles) { - trackBuilder.reset(tracksWithItsPid); - trackBuilder.fillMcTracks(col, collisionBuilder, collisionBuilderProducts, mcCols, tracks, tracksWithItsPid, trackBuilderProducts, mcParticles, mcBuilder, mcProducts); + builders.trackBuilder.fillMcTracks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, tracks, tracksWithItsPid, trackBuilderProducts, mcParticles, builders.mcBuilder, mcProducts); } // processing v0s - template - void processV0s(T1 const& col, T2 const& tracks, T3 const& tracksWithItsPid, T4 const& v0s) + template + void processV0s(T1 const& col, T2 const& tracks, T3 const& v0s) { - lambdaBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, tracksWithItsPid, trackBuilder); - antilambdaBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, tracksWithItsPid, trackBuilder); - k0shortBuilder.fillV0s(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, tracksWithItsPid, trackBuilder); + builders.lambdaBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); + builders.antilambdaBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); + builders.k0shortBuilder.fillV0s(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder); } - template - void processMcV0s(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& v0s, T6 const& mcParticles) + template + void processMcV0s(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& v0s, T5 const& mcParticles) { - lambdaBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, tracksWithItsPid, trackBuilder, mcParticles, mcBuilder, mcProducts); - antilambdaBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, tracksWithItsPid, trackBuilder, mcParticles, mcBuilder, mcProducts); - k0shortBuilder.fillMcV0s(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, tracksWithItsPid, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.lambdaBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.antilambdaBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.k0shortBuilder.fillMcV0s(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, v0builderProducts, v0s, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + } + + // processing photons (PCM) + template + void processPhotons(T1 const& col, T2 const& photons, T3 const& v0legs) + { + builders.photonBuilder.fillPhotons(col, builders.collisionBuilder, collisionBuilderProducts, photonBuilderProducts, photons, v0legs); } // processing kinks - template - void processKinks(T1 const& col, T2 const& tracks, T3 const& tracksWithItsPid, T4 const& kinks) + template + void processKinks(T1 const& col, T2 const& tracks, T3 const& kinks) + { + builders.sigmaBuilder.fillKinks(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder); + builders.sigmaPlusBuilder.fillKinks(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder); + } + template + void processMcKinks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& kinks, T5 const& mcParticles) { - sigmaBuilder.fillKinks(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, tracksWithItsPid, trackBuilder); - sigmaPlusBuilder.fillKinks(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, tracksWithItsPid, trackBuilder); + builders.sigmaBuilder.fillMcKinks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.sigmaPlusBuilder.fillMcKinks(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } - template - void processMcKinks(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& kinks, T6 const& mcParticles) + + // processing sigma plus reconstructed with a PCM photon + template + void processSigmaPlusPcms(T1 const& col, T2 const& tracks, T3 const& sigmaPlusPcms) + { + builders.sigmaPlusPcmBuilder.fillSigmaPlusPcms(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, kinkBuilderProducts, sigmaPlusPcms, tracks, builders.trackBuilder); + } + template + void processMcSigmaPlusPcms(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& sigmaPlusPcms, T5 const& mcParticles) { - sigmaBuilder.fillMcKinks(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, tracksWithItsPid, trackBuilder, mcParticles, mcBuilder, mcProducts); - sigmaPlusBuilder.fillMcKinks(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, kinks, tracks, tracksWithItsPid, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.sigmaPlusPcmBuilder.fillMcSigmaPlusPcms(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, kinkBuilderProducts, sigmaPlusPcms, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } // processing cascades - template - void processCascades(T1 const& col, T2 const& tracks, T3 const& tracksWithItsPid, T4 const& cascades) + template + void processCascades(T1 const& col, T2 const& tracks, T3 const& cascades) { - xiBuilder.fillCascades(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, - cascades, tracks, tracksWithItsPid, trackBuilder); - omegaBuilder.fillCascades(col, collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, - cascades, tracks, tracksWithItsPid, trackBuilder); + builders.xiBuilder.fillCascades(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, builders.trackBuilder); + builders.omegaBuilder.fillCascades(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, cascadeBuilderProducts, + cascades, tracks, builders.trackBuilder); } - template - void processMcCascades(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& tracksWithItsPid, T5 const& cascades, T6 const& mcParticles) + template + void processMcCascades(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& cascades, T5 const& mcParticles) { - xiBuilder.fillMcCascades(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, tracksWithItsPid, trackBuilder, mcParticles, mcBuilder, mcProducts); - omegaBuilder.fillMcCascades(col, collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, tracksWithItsPid, trackBuilder, mcParticles, mcBuilder, mcProducts); + builders.xiBuilder.fillMcCascades(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.omegaBuilder.fillMcCascades(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, cascadeBuilderProducts, cascades, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); } - // proccess functions - void processTracksRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + // processing D0s + template + void processD0s(T1 const& col, T2 const& tracks, T3 const& candidates) + { + builders.d0Builder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + builders.d0barBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + } + template + void processMcD0s(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& candidates, T5 const& mcParticles) + { + builders.d0Builder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.d0barBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + } + + // processing Lcs + template + void processLcs(T1 const& col, T2 const& tracks, T3 const& candidates) + { + builders.lcBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + builders.lcBarBuilder.fillCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder); + } + template + void processMcLcs(T1 const& col, T2 const& mcCols, T3 const& tracks, T4 const& candidates, T5 const& mcParticles) + { + builders.lcBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + builders.lcBarBuilder.fillMcCharmHadrons(col, builders.collisionBuilder, collisionBuilderProducts, mcCols, trackBuilderProducts, charmHadronBuilderProducts, candidates, tracks, builders.trackBuilder, mcParticles, builders.mcBuilder, mcProducts); + } + + // ========================================================================== + // process functions - data + // ========================================================================== + + void processTracksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3FullPidTracks const& tracks) + rawinputs::Run3FullPidTracks const& tracks) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksRun3pp, "Provide tracks", true); + + void processTracksRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); } - PROCESS_SWITCH(FemtoProducer, processTracksRun3pp, "Process tracks", true); + PROCESS_SWITCH(FemtoProducer, processTracksRun3PbPb, "Provide tracks in PbPb collisions", false); - // process tracks and v0s - void processTracksV0sRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + void processTracksRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksRun3PbPbWithEventShape, "Provide tracks in PbPb collisions with event shape information", false); + + void processTracksV0sRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3FullPidTracks const& tracks, - consumeddata::Run3Vzeros const& v0s) + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3pp, "Provide tracks and v0s", false); + + void processTracksPhotonsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, tracksWithItsPid, v0s); - }; - PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3pp, "Process tracks and v0s", false); + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3pp, "Provide tracks and PCM photons", false); - // process tracks and kinks - void processTracksKinksRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + void processTracksPhotonsRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksPhotonsRun3PbPb, "Provide tracks and PCM photons in PbPb collisions", false); + + void processTracksV0sRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3FullPidTracks const& tracks, - consumeddata::Run3Kinks const& kinks) + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processKinks(col, tracks, tracksWithItsPid, kinks); } - PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3pp, "Process tracks and kinks", false); + PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3PbPb, "Provide tracks and v0s in PbPb collisions", false); - // process tracks, v0s and cascades - void processTracksV0sCascadesRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3FullPidTracks const& tracks, - consumeddata::Run3Vzeros const& v0s, - consumeddata::Run3Cascades const& cascades) + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades) { - if (!processCollisions(col, bcs, tracks)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, tracksWithItsPid, v0s); - processCascades(col, tracks, tracksWithItsPid, cascades); } - PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3pp, "Provide Tracks, V0s and Cascades for Run3", false); + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3pp, "Provide tracks, v0s and cascades", false); - // process tracks, v0s, cascades and kinks - void processTracksV0sCascadesKinksRun3pp(consumeddata::Run3PpCollisions::iterator const& col, + void processTracksV0sCascadesRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPb, "Provide tracks, v0s and cascades in PbPb collisions", false); + + void processTracksV0sCascadesRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPbWithEventShape, "Provide tracks, v0s and cascades in PbPb collisions with event shape information", false); + + void processTracksV0sCascadesPhotonsRun3PbPbWithEventShape(rawinputs::Run3PbPbCollisionsWithEventShape const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesPhotonsRun3PbPbWithEventShape, "Provide tracks, v0s, cascades and PCM photons in PbPb collisions with event shape information", false); + + void processTracksKinksRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Kinks const& kinks) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3pp, "Provide tracks and kinks", false); + + void processTracksSigmaPlusPcmsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3SigmaPlusPcms const& sigmaPlusPcms) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColSigmaPlusPcms, col.globalIndex()); + processSigmaPlusPcms(col, tracksThisCol, sigmaPlusPcmsThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3pp, "Provide tracks and sigma plus reconstructed with a PCM photon", false); + + void processTracksV0sCascadesKinksRun3pp(rawinputs::Run3PpCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3FullPidTracks const& tracks, - consumeddata::Run3Vzeros const& v0s, - consumeddata::Run3Cascades const& cascades, - consumeddata::Run3Kinks const& kinks) - { - if (!processCollisions(col, bcs, tracks)) { - return; - } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processTracks(col, tracksWithItsPid); - processV0s(col, tracks, tracksWithItsPid, v0s); - processKinks(col, tracks, tracksWithItsPid, kinks); - processCascades(col, tracks, tracksWithItsPid, cascades); - } - PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksRun3pp, "Provide Tracks, V0s and Cascades for Run3", false); - - // process monte carlo tracks - void processTracksRun3ppMc(consumeddata::Run3PpMcRecoCollisions::iterator const& col, - consumeddata::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades, + rawinputs::Run3Kinks const& kinks) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksRun3pp, "Provide tracks, v0s, cascades and kinks", false); + + void processTracksV0sCascadesKinksPhotonsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Vzeros const& v0s, + rawinputs::Run3Cascades const& cascades, + rawinputs::Run3Kinks const& kinks, + o2::aod::V0PhotonsKF const& photons, + o2::aod::V0Legs const& v0legs) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto v0sThisCol = v0s.sliceBy(preslices.perColV0s, col.globalIndex()); + processV0s(col, tracksThisCol, v0sThisCol); + auto cascadesThisCol = cascades.sliceBy(preslices.perColCascades, col.globalIndex()); + processCascades(col, tracksThisCol, cascadesThisCol); + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processKinks(col, tracksThisCol, kinksThisCol); + // V0PhotonsKF/V0Legs carry a plain CollisionId column (not a framework index column), so they + // are not auto-grouped by the DPL binder and must be sliced manually, same as EMPhotonFilter.cxx + auto photonsThisCol = photons.sliceBy(preslices.perColRecoPCMPhotons, col.globalIndex()); + processPhotons(col, photonsThisCol, v0legs); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesKinksPhotonsRun3pp, "Provide tracks, v0s, cascades, kinks and PCM photons", false); + + void processTracksD0sRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3D0s const& candidates) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColD0s, col.globalIndex()); + processD0s(col, tracksThisCol, candidatesThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3pp, "Provide tracks and D0s", false); + + void processTracksD0sRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3D0s const& candidates) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColD0s, col.globalIndex()); + processD0s(col, tracksThisCol, candidatesThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3PbPb, "Provide tracks and D0s in PbPb collisions", false); + + // ========================================================================== + // process functions - monte carlo (ungrouped) + // + // No argument is an ::iterator, so DPL hands over the full timeframe tables and + // does no auto-grouping. Tables that are iterated over (tracks, v0s, cascades, + // kinks) are sliced per collision; the full track table is passed alongside so + // the v0/cascade/kink builders can resolve their daughter indices. + // These functions own the per-timeframe reset of the mc builder maps AND of trackBuilder + // (once, before the reco-collision loop, in both pass-through and lazy-fill mode); neither + // processMcCollisions nor processMcTracks resets them, since they run once per reco collision + // and the dedup maps have to survive across all reco collisions in the timeframe. + // ========================================================================== + + void processTracksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3McRecoTracks const& tracks, - consumeddata::Run3McGenParticles const& mcParticles) + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3McGenParticles const& mcParticles) { - if (!processMcCollisions(col, mcCols, bcs, tracks, mcParticles)) { - return; + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksRun3ppMc, "Provide reconstructed and generated tracks", false); + + void processTracksRun3PbPbMc(rawinputs::Run3PbPbMcGenCollisions const& mcCols, + rawinputs::Run3PbPbMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processMcTracks(col, mcCols, tracks, tracksWithItsPid, mcParticles); } - PROCESS_SWITCH(FemtoProducer, processTracksRun3ppMc, "Provide reconstructed and generated Tracks", false); + PROCESS_SWITCH(FemtoProducer, processTracksRun3PbPbMc, "Provide reconstructed and generated tracks in PbPb collisions", false); - // process monte carlo tracks and v0s - void processTracksV0sRun3ppMc(consumeddata::Run3PpMcRecoCollisions::iterator const& col, - consumeddata::Run3PpMcGenCollisions const& mcCols, + void processTracksV0sRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3McRecoTracks const& tracks, - consumeddata::Run3RecoVzeros const& v0s, - consumeddata::Run3McGenParticles const& mcParticles) + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoVzeros const& v0s, + rawinputs::Run3McGenParticles const& mcParticles) { - if (!processMcCollisions(col, mcCols, bcs, tracks, mcParticles)) { - return; + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); + processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processMcTracks(col, mcCols, tracks, tracksWithItsPid, mcParticles); - processMcV0s(col, mcCols, tracks, tracksWithItsPid, v0s, mcParticles); } PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3ppMc, "Provide reconstructed and generated tracks and v0s", false); - // process monte carlo tracks and kinks - void processTracksKinksRun3ppMc(consumeddata::Run3PpMcRecoCollisions::iterator const& col, - consumeddata::Run3PpMcGenCollisions const& mcCols, + void processTracksV0sRun3PbPbMc(rawinputs::Run3PbPbMcGenCollisions const& mcCols, + rawinputs::Run3PbPbMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoVzeros const& v0s, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); + processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sRun3PbPbMc, "Provide reconstructed and generated tracks and v0s in PbPb collisions", false); + + void processTracksV0sCascadesRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoVzeros const& v0s, + rawinputs::Run3RecoCascades const& cascades, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); + processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); + + auto cascadesThisCol = cascades.sliceBy(preslices.perColRecoCascades, col.globalIndex()); + processMcCascades(col, mcCols, tracks, cascadesThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3ppMc, "Provide reconstructed and generated tracks, v0s and cascades", false); + + void processTracksV0sCascadesRun3PbPbMc(rawinputs::Run3PbPbMcGenCollisions const& mcCols, + rawinputs::Run3PbPbMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoVzeros const& v0s, + rawinputs::Run3RecoCascades const& cascades, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); + processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); + + auto cascadesThisCol = cascades.sliceBy(preslices.perColRecoCascades, col.globalIndex()); + processMcCascades(col, mcCols, tracks, cascadesThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3PbPbMc, "Provide reconstructed and generated tracks, v0s and cascades in PbPb collisions", false); + + void processTracksKinksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3McRecoTracks const& tracks, - consumeddata::Run3Kinks const& kinks, - consumeddata::Run3McGenParticles const& mcParticles) + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3Kinks const& kinks, + rawinputs::Run3McGenParticles const& mcParticles) { - if (!processMcCollisions(col, mcCols, bcs, tracks, mcParticles)) { - return; + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processMcTracks(col, mcCols, tracks, tracksWithItsPid, mcParticles); - processMcKinks(col, mcCols, tracks, tracksWithItsPid, kinks, mcParticles); } PROCESS_SWITCH(FemtoProducer, processTracksKinksRun3ppMc, "Provide reconstructed and generated tracks and kinks", false); - // process monte carlo tracks and v0s and kinks (adding cascades later here) - void processTracksV0sKinksRun3ppMc(consumeddata::Run3PpMcRecoCollisions::iterator const& col, - consumeddata::Run3PpMcGenCollisions const& mcCols, + void processTracksSigmaPlusPcmsRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoSigmaPlusPcms const& sigmaPlusPcms, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto sigmaPlusPcmsThisCol = sigmaPlusPcms.sliceBy(preslices.perColRecoSigmaPlusPcms, col.globalIndex()); + processMcSigmaPlusPcms(col, mcCols, tracks, sigmaPlusPcmsThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksSigmaPlusPcmsRun3ppMc, "Provide reconstructed and generated tracks and sigma plus reconstructed with a PCM photon", false); + + void processTracksV0sKinksRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3McRecoTracks const& tracks, - consumeddata::Run3RecoVzeros const& v0s, - consumeddata::Run3Kinks const& kinks, - consumeddata::Run3McGenParticles const& mcParticles) + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoVzeros const& v0s, + rawinputs::Run3Kinks const& kinks, + rawinputs::Run3McGenParticles const& mcParticles) { - if (!processMcCollisions(col, mcCols, bcs, tracks, mcParticles)) { - return; + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto v0sThisCol = v0s.sliceBy(preslices.perColRecoV0s, col.globalIndex()); + processMcV0s(col, mcCols, tracks, v0sThisCol, mcParticles); + + auto kinksThisCol = kinks.sliceBy(preslices.perColRecoKinks, col.globalIndex()); + processMcKinks(col, mcCols, tracks, kinksThisCol, mcParticles); } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processMcTracks(col, mcCols, tracks, tracksWithItsPid, mcParticles); - processMcV0s(col, mcCols, tracks, tracksWithItsPid, v0s, mcParticles); - processMcKinks(col, mcCols, tracks, tracksWithItsPid, kinks, mcParticles); } - PROCESS_SWITCH(FemtoProducer, processTracksV0sKinksRun3ppMc, "Provide reconstructed and generated tracks and v0s and kinks", false); + PROCESS_SWITCH(FemtoProducer, processTracksV0sKinksRun3ppMc, "Provide reconstructed and generated tracks, v0s and kinks", false); - // process monte carlo tracks and v0s - void processTracksV0sCascadesRun3ppMc(consumeddata::Run3PpMcRecoCollisions::iterator const& col, - consumeddata::Run3PpMcGenCollisions const& mcCols, - o2::aod::BCsWithTimestamps const& bcs, - consumeddata::Run3McRecoTracks const& tracks, - consumeddata::Run3RecoVzeros const& v0s, - consumeddata::Run3RecoCascades const& cascades, - consumeddata::Run3McGenParticles const& mcParticles) + void processTracksD0sRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoD0s const& d0s, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto d0sThisCol = d0s.sliceBy(preslices.perColRecoD0s, col.globalIndex()); + processMcD0s(col, mcCols, tracks, d0sThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3ppMc, "Provide reconstructed and generated tracks and D0s", false); + + void processTracksD0sRun3PbPbMc(rawinputs::Run3PbPbMcGenCollisions const& mcCols, + rawinputs::Run3PbPbMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoD0s const& d0s, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto d0sThisCol = d0s.sliceBy(preslices.perColRecoD0s, col.globalIndex()); + processMcD0s(col, mcCols, tracks, d0sThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksD0sRun3PbPbMc, "Provide reconstructed and generated tracks and D0s in PbPb collisions", false); + + void processTracksLcsRun3pp(rawinputs::Run3PpCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Lcs const& candidates) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColLcs, col.globalIndex()); + processLcs(col, tracksThisCol, candidatesThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3pp, "Provide tracks and Lcs", false); + + void processTracksLcsRun3PbPb(rawinputs::Run3PbPbCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3FullPidTracks const& tracks, + rawinputs::Run3Lcs const& candidates) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColTracks, col.globalIndex()); + if (!processCollisions(col, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processTracks(col, tracksWithItsPid); + auto candidatesThisCol = candidates.sliceBy(preslices.perColLcs, col.globalIndex()); + processLcs(col, tracksThisCol, candidatesThisCol); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3PbPb, "Provide tracks and Lcs in PbPb collisions", false); + + void processTracksLcsRun3ppMc(rawinputs::Run3PpMcGenCollisions const& mcCols, + rawinputs::Run3PpMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoLcs const& lcs, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto lcsThisCol = lcs.sliceBy(preslices.perColRecoLcs, col.globalIndex()); + processMcLcs(col, mcCols, tracks, lcsThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3ppMc, "Provide reconstructed and generated tracks and Lcs", false); + + void processTracksLcsRun3PbPbMc(rawinputs::Run3PbPbMcGenCollisions const& mcCols, + rawinputs::Run3PbPbMcRecoCollisions const& cols, + o2::aod::BCsWithTimestamps const& bcs, + rawinputs::Run3McRecoTracks const& tracks, + rawinputs::Run3RecoLcs const& lcs, + rawinputs::Run3McGenParticles const& mcParticles) + { + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + if (builders.mcBuilder.isPassThrough()) { + builders.mcBuilder.fillMcPassThrough(mcCols, mcParticles, preslices.perMcCollision, mcProducts); + } + + for (const auto& col : cols) { + auto tracksThisCol = tracks.sliceBy(preslices.perColRecoTracks, col.globalIndex()); + if (!processMcCollisions(col, mcCols, bcs, tracksThisCol)) { + continue; + } + auto tracksWithItsPid = o2::soa::Attach(tracksThisCol); + processMcTracks(col, mcCols, tracksThisCol, tracksWithItsPid, mcParticles); + + auto lcsThisCol = lcs.sliceBy(preslices.perColRecoLcs, col.globalIndex()); + processMcLcs(col, mcCols, tracks, lcsThisCol, mcParticles); + } + } + PROCESS_SWITCH(FemtoProducer, processTracksLcsRun3PbPbMc, "Provide reconstructed and generated tracks and Lcs in PbPb collisions", false); + + // ========================================================================== + // process functions - generator level only (for MCGEN datasets) + // ========================================================================== + + // do not preslice the mcParticles table passed to fillMcParticle, otherwise the finding of the partonic mother can fail + void processMcOnly(o2::aod::McCollisions const& mcCols, o2::aod::McParticles const& mcParticles) { - if (!processMcCollisions(col, mcCols, bcs, tracks, mcParticles)) { - return; + builders.mcBuilder.reset(mcCols, mcParticles); + for (const auto& mcCol : mcCols) { + auto particlesThisCollision = mcParticles.sliceBy(preslices.perMcCollision, mcCol.globalIndex()); + builders.mcBuilder.fillMcCollision(mcCol, particlesThisCollision, mcProducts, pdgDb); + for (const auto& mcParticle : particlesThisCollision) { + builders.mcBuilder.fillMcParticle(mcParticle, mcParticles, mcCol, mcProducts); + } } - auto tracksWithItsPid = o2::soa::Attach(tracks); - processMcTracks(col, mcCols, tracks, tracksWithItsPid, mcParticles); - processMcV0s(col, mcCols, tracks, tracksWithItsPid, v0s, mcParticles); - processMcCascades(col, mcCols, tracks, tracksWithItsPid, cascades, mcParticles); } - PROCESS_SWITCH(FemtoProducer, processTracksV0sCascadesRun3ppMc, "Provide reconstructed and generated tracks and v0s", false); + PROCESS_SWITCH(FemtoProducer, processMcOnly, "Provide generated particles", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - o2::framework::WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx index 64a3ede4696..c7c560e0f8e 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerDerivedToDerived.cxx @@ -13,10 +13,13 @@ /// \brief Tasks that produces the femto tables from derived data /// \author Anton Riedel, TU München, anton.riedel@tum.de +#include "PWGCF/Femto/Core/cascadeBuilder.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/kinkBuilder.h" +#include "PWGCF/Femto/Core/mcBuilder.h" #include "PWGCF/Femto/Core/partitions.h" #include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/twoTrackResonanceBuilder.h" #include "PWGCF/Femto/Core/v0Builder.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" @@ -30,135 +33,703 @@ using namespace o2::analysis::femto; -struct FemtoProducerDerivedToDerived { +// kept outside the task struct on purpose: the struct is reflected via a brace-decomposition +// mechanism capped at 100 direct data members (see o2::framework::PresliceGroup for the same +// concern applied to Preslice members below); type aliases don't need to live inside the struct +// at all, so moving them here keeps that budget free for actual data members. +namespace femtotables +{ +using FemtoCollisions = o2::soa::Join; + +using FilteredFemtoCollisions = o2::soa::Filtered; +using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + +using FemtoMcCollisions = o2::soa::Join; +using FilteredFemtoMcCollisions = o2::soa::Filtered; - // setup tables - using Collisions = o2::soa::Join; - using Collision = Collisions::iterator; +using FemtoTracks = o2::soa::Join; +using FemtoTracksWithMass = o2::soa::Join; +// opt-in: carries the packed purity tables and FTrackDcas through to the re-written output -- +// needs the upstream producer's TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled +using FemtoTracksWithFitVars = o2::soa::Join; +using FemtoLambdas = o2::soa::Join; +using FemtoK0shorts = o2::soa::Join; +using FemtoRho0s = o2::soa::Join; +using FemtoPhis = o2::soa::Join; +using FemtoXis = o2::soa::Join; +using FemtoOmegas = o2::soa::Join; +using FemtoSigma = o2::soa::Join; +using FemtoSigmaPlus = o2::soa::Join; +using FemtoSigmaPlusExtended = o2::soa::Join; +using FemtoSigmaPlusPcm = o2::soa::Join; +using FemtoSigmaPlusPcmExtended = o2::soa::Join; - using FilteredCollisions = o2::soa::Filtered; - using FilteredCollision = FilteredCollisions::iterator; +using FemtoMcTracks = o2::soa::Join; +using FemtoMcSigma = o2::soa::Join; +using FemtoMcSigmaPlus = o2::soa::Join; +using FemtoMcSigmaPlusExtended = o2::soa::Join; +using FemtoMcSigmaPlusPcm = o2::soa::Join; +using FemtoMcSigmaPlusPcmExtended = o2::soa::Join; +using FemtoMcParticles = o2::soa::Join; +} // namespace femtotables - using Tracks = o2::soa::Join; - using Lambdas = o2::soa::Join; - using K0shorts = o2::soa::Join; - using Sigma = o2::soa::Join; - using SigmaPlus = o2::soa::Join; +struct FemtoProducerDerivedToDerived { o2::framework::SliceCache cache; + // all fColId Preslice declarations, grouped into a single data member + struct : public o2::framework::PresliceGroup { + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColTracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColSigmaPlusPcmExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColXis = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColOmegas = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusExtended = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + o2::framework::Preslice perColMcSigmaPlusPcmExtended = o2::aod::femtobase::stored::fColId; + } preslices; + + // plain builder helper objects, grouped into a single data member (not Configurable/Partition/ + // Preslice/Produces, so the framework never needs to discover them individually -- free to nest) + struct { + collisionbuilder::CollisionBuilderDerivedToDerived collisionBuilder; + trackbuilder::TrackBuilderDerivedToDerived trackBuilder; + v0builder::V0BuilderDerivedToDerived v0Builder; + twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerived resonanceBuilder; + kinkbuilder::KinkBuilderDerivedToDerived kinkBuilder; + cascadebuilder::CascadeBuilderDerivedToDerived cascadeBuilder; + mcbuilder::McBuilderDerivedToDerived mcBuilder; + } builders; + // collision builder collisionbuilder::ConfCollisionSelection collisionSelection; o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); collisionbuilder::CollisionBuilderDerivedToDerivedProducts collisionBuilderProducts; - collisionbuilder::CollisionBuilderDerivedToDerived collisionBuilder; // track builder - trackbuilder::TrackBuilderDerivedToDerived trackBuilder; trackbuilder::TrackBuilderDerivedToDerivedProducts trackBuilderProducts; trackbuilder::ConfTrackTablesDerivedToDerived confTrackBuilder; trackbuilder::ConfTrackSelection1 trackSelections1; trackbuilder::ConfTrackSelection2 trackSelections2; - o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); - o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); - o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); + + o2::framework::Partition trackWithMassPartition1 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections1); + o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(trackSelections2); + + o2::framework::Partition trackWithFitVarsPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition trackWithFitVarsPartition2 = MAKE_TRACK_PARTITION(trackSelections2); // v0 builder - v0builder::V0BuilderDerivedToDerived v0Builder; v0builder::V0BuilderDerivedToDerivedProducts v0BuilderProducts; v0builder::ConfV0TablesDerivedToDerived confV0Builder; v0builder::ConfLambdaSelection1 lambdaSelection1; - o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(lambdaSelection1); - o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(lambdaSelection1); v0builder::ConfK0shortSelection1 k0shortSelection1; - o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection1); - o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection1); + + // two-track resonance builder + twotrackresonancebuilder::TwoTrackResonanceBuilderDerivedToDerivedProducts resonanceBuilderProducts; + twotrackresonancebuilder::ConfTwoTrackResonanceTablesDerivedToDerived confResonanceBuilder; + + twotrackresonancebuilder::ConfRho0Selection rho0Selection; + o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); + + twotrackresonancebuilder::ConfPhiSelection phiSelection; + o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); // kink builder - kinkbuilder::KinkBuilderDerivedToDerived kinkBuilder; kinkbuilder::KinkBuilderDerivedToDerivedProducts kinkBuilderProducts; kinkbuilder::ConfKinkTablesDerivedToDerived confKinkBuilder; kinkbuilder::ConfSigmaSelection1 sigmaSelection1; - o2::framework::Partition sigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); - o2::framework::Preslice perColSigma = o2::aod::femtobase::stored::fColId; + o2::framework::Partition sigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); kinkbuilder::ConfSigmaPlusSelection1 sigmaPlusSelection1; - o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); - o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; + o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Partition sigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + + kinkbuilder::ConfSigmaPlusPcmSelection1 sigmaPlusPcmSelection1; + o2::framework::Partition sigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + o2::framework::Partition sigmaPlusPcmExtendedPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + + // cascade builder + cascadebuilder::CascadeBuilderDerivedToDerivedProducts cascadeBuilderProducts; + cascadebuilder::ConfCascadeTablesDerivedToDerived confCascadeBuilder; + + cascadebuilder::ConfXiSelection xiSelection; + o2::framework::Partition xiPartition = MAKE_CASCADE_PARTITION(xiSelection); + + cascadebuilder::ConfOmegaSelection omegaSelection; + o2::framework::Partition omegaPartition = MAKE_CASCADE_PARTITION(omegaSelection); + + // mc builder + mcbuilder::McBuilderDerivedToDerivedProducts mcBuilderProducts; + mcbuilder::ConfMcTablesDerivedToDerived confMcBuilder; + + o2::framework::Partition mcTrackPartition1 = MAKE_TRACK_PARTITION(trackSelections1); + o2::framework::Partition mcTrackPartition2 = MAKE_TRACK_PARTITION(trackSelections2); + + o2::framework::Partition mcSigmaPartition = MAKE_SIGMA_PARTITION(sigmaSelection1); + + o2::framework::Partition mcSigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + o2::framework::Partition mcSigmaPlusExtendedPartition = MAKE_SIGMAPLUS_PARTITION(sigmaPlusSelection1); + + o2::framework::Partition mcSigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); + o2::framework::Partition mcSigmaPlusPcmExtendedPartition = MAKE_SIGMAPLUSPCM_PARTITION(sigmaPlusPcmSelection1); void init(o2::framework::InitContext& /*context*/) { - trackBuilder.init(confTrackBuilder); - v0Builder.init(confV0Builder); - kinkBuilder.init(confKinkBuilder); + const int activeProcesses = + static_cast(doprocessTracks) + static_cast(doprocessTracksWithMass) + static_cast(doprocessTracksWithFitVars) + + static_cast(doprocessTracksLambdas) + static_cast(doprocessTracksWithMassLambdas) + static_cast(doprocessTracksWithFitVarsLambdas) + + static_cast(doprocessLambdas) + + static_cast(doprocessTracksXis) + static_cast(doprocessTracksWithFitVarsXis) + + static_cast(doprocessTracksOmegas) + static_cast(doprocessTracksWithFitVarsOmegas) + + static_cast(doprocessTracksK0shorts) + static_cast(doprocessTracksWithFitVarsK0shorts) + static_cast(doprocessTracksSigma) + + static_cast(doprocessTracksSigmaPlus) + static_cast(doprocessTracksSigmaPlusExtended) + + static_cast(doprocessTracksSigmaPlusPcm) + static_cast(doprocessTracksSigmaPlusPcmExtended) + + static_cast(doprocessTracksRho0) + static_cast(doprocessTracksPhi) + + static_cast(doprocessLambdasXis) + + static_cast(doprocessTracksMc) + static_cast(doprocessTracksSigmaMc) + + static_cast(doprocessTracksSigmaPlusMc) + static_cast(doprocessTracksSigmaPlusExtendedMc) + + static_cast(doprocessTracksSigmaPlusPcmMc) + static_cast(doprocessTracksSigmaPlusPcmExtendedMc); + if (activeProcesses != 1) { + LOG(fatal) << "Exactly one process function must be activated (got " << activeProcesses << ")."; + } + + // which builders the currently active process function actually exercises -- + // only those need their limit configuration validated (see e.g. TrackBuilderDerivedToDerived::init()); + // an unused builder is free to keep its (possibly all-zero) default limits + const bool needsTrackBuilder = + doprocessTracks || doprocessTracksWithMass || doprocessTracksWithFitVars || + doprocessTracksLambdas || doprocessTracksWithMassLambdas || doprocessTracksWithFitVarsLambdas || + doprocessTracksXis || doprocessTracksWithFitVarsXis || + doprocessTracksOmegas || doprocessTracksWithFitVarsOmegas || + doprocessTracksK0shorts || doprocessTracksWithFitVarsK0shorts || + doprocessTracksSigma || doprocessTracksSigmaPlus || doprocessTracksSigmaPlusExtended || + doprocessTracksSigmaPlusPcm || doprocessTracksSigmaPlusPcmExtended || + doprocessTracksRho0 || doprocessTracksPhi || + doprocessTracksMc || doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || + doprocessTracksSigmaPlusExtendedMc || doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + const bool needsV0Builder = + doprocessTracksLambdas || doprocessTracksWithMassLambdas || doprocessTracksWithFitVarsLambdas || + doprocessLambdas || + doprocessTracksK0shorts || doprocessTracksWithFitVarsK0shorts || + doprocessLambdasXis; - if ((doprocessTracks + doprocessTracksLambdas + doprocessTracksK0shorts + doprocessTracksSigma + doprocessTracksSigmaPlus) > 1) { - LOG(fatal) << "Only one proccess function can be activated"; + const bool needsCascadeBuilder = + doprocessTracksXis || doprocessTracksWithFitVarsXis || + doprocessTracksOmegas || doprocessTracksWithFitVarsOmegas || + doprocessLambdasXis; + + const bool needsKinkBuilder = + doprocessTracksSigma || doprocessTracksSigmaPlus || doprocessTracksSigmaPlusExtended || + doprocessTracksSigmaPlusPcm || doprocessTracksSigmaPlusPcmExtended || + doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || doprocessTracksSigmaPlusExtendedMc || + doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + const bool needsResonanceBuilder = doprocessTracksRho0 || doprocessTracksPhi; + + const bool needsMcBuilder = + doprocessTracksMc || doprocessTracksSigmaMc || doprocessTracksSigmaPlusMc || + doprocessTracksSigmaPlusExtendedMc || doprocessTracksSigmaPlusPcmMc || doprocessTracksSigmaPlusPcmExtendedMc; + + // skip init() entirely for builders the active process doesn't touch: their limit + // members default to 0 and are only ever read by the collisionHasTooFew*/process* + // methods that the active process also doesn't call, so leaving them uninitialized + // (i.e. at their 0 default) is harmless -- this also sidesteps each builder's "all + // limits are 0" fatal check, which would otherwise fire for an unrelated species + if (needsTrackBuilder) { + builders.trackBuilder.init(confTrackBuilder); + } + if (needsV0Builder) { + builders.v0Builder.init(confV0Builder); + } + if (needsCascadeBuilder) { + builders.cascadeBuilder.init(confCascadeBuilder); + } + if (needsKinkBuilder) { + builders.kinkBuilder.init(confKinkBuilder); + } + if (needsResonanceBuilder) { + builders.resonanceBuilder.init(confResonanceBuilder); + } + if (needsMcBuilder) { + builders.mcBuilder.init(confMcBuilder); } } - // proccess functions - void processTracks(FilteredCollision const& col, Tracks const& tracks) + // process functions + void processTracks(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracks, "Process tracks", true); - void processTracksLambdas(FilteredCollision const& col, Tracks const& tracks, Lambdas const& lambdas) + void processTracksWithMass(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithMass const& tracks) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMass, "Process tracks with mass", false); + + void processTracksWithFitVars(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVars, "Process tracks, carrying through FitVars tables", false); + + void processTracksLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksLambdas, "Process lambdas and tracks", false); - void processTracksK0shorts(FilteredCollision const& col, Tracks const& tracks, K0shorts const& k0shorts) + void processTracksWithMassLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithMass const& tracks, femtotables::FemtoLambdas const& lambdas) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithMassLambdas, "Process lambdas and tracks with mass", false); + + void processTracksWithFitVarsLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoLambdas const& lambdas) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsLambdas, "Process lambdas and tracks, carrying through FitVars tables", false); + + void processLambdas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdas, "Process lambdas only (produced track table contains daughers only)", false); + + void processTracksXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoXis const& xis) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksXis, "Process tracks and xis", false); + + void processTracksWithFitVarsXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoXis const& xis) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsXis, "Process tracks and xis, carrying through FitVars tables", false); + + void processTracksOmegas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoOmegas const& omegas) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksOmegas, "Process tracks and omegas", false); + + void processTracksWithFitVarsOmegas(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoOmegas const& omegas) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.cascadeBuilder.collisionHasTooFewOmegas(col, omegas, omegaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processOmegas(col, omegas, tracks, omegaPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsOmegas, "Process tracks and omegas, carrying through FitVars tables", false); + + void processTracksK0shorts(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoK0shorts const& k0shorts) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksK0shorts, "Process k0short and tracks", false); - void processTracksSigma(FilteredCollision const& col, Tracks const& tracks, Sigma const& sigma) + void processTracksWithFitVarsK0shorts(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracksWithFitVars const& tracks, femtotables::FemtoK0shorts const& k0shorts) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache) || builders.v0Builder.collisionHasTooFewK0shorts(col, k0shorts, k0shortPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.v0Builder.processK0shorts(col, k0shorts, tracks, k0shortPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksWithFitVarsK0shorts, "Process k0short and tracks, carrying through FitVars tables", false); + + void processTracksSigma(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigma const& sigma) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, sigmaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - kinkBuilder.processSigma(col, sigma, tracks, sigmaPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigma, "Process sigma and tracks", false); - void processTracksSigmaPlus(FilteredCollision const& col, Tracks const& tracks, SigmaPlus const& sigmaplus) + void processTracksSigmaPlus(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlus const& sigmaplus) { - if (trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { - return; + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } - collisionBuilder.processCollision(col, collisionBuilderProducts); - trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); - kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusPartition, trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); } PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlus, "Process sigmaPlus and tracks", false); + + void processTracksSigmaPlusExtended(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusExtended const& sigmaplus) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, sigmaPlusExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlus(col, sigmaplus, tracks, sigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtended, "Process sigmaPlus (with SigmaPlusExtras) and tracks", false); + + void processTracksSigmaPlusPcm(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcm const& sigmapluspcm) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcm, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores, selected on the BDT score) and tracks", false); + + void processTracksSigmaPlusPcmExtended(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoSigmaPlusPcmExtended const& sigmapluspcm) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, sigmaPlusPcmExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcm(col, sigmapluspcm, tracks, sigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmExtended, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores and SigmaPlusPcmExtras, selected on the BDT score) and tracks", false); + + void processTracksRho0(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoRho0s const& rho0s) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewRho0s(col, rho0s, rho0Partition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processRho0s(col, rho0s, tracks, rho0Partition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksRho0, "Process rho0 and tracks", false); + + void processTracksPhi(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoPhis const& phis) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, trackPartition1, trackPartition2, cache) || builders.resonanceBuilder.collisionHasTooFewPhis(col, phis, phiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.trackBuilder.processTracks(col, tracks, trackPartition1, trackPartition2, cache, trackBuilderProducts, collisionBuilderProducts); + builders.resonanceBuilder.processPhis(col, phis, tracks, phiPartition, builders.trackBuilder, cache, resonanceBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksPhi, "Process phi and tracks", false); + + void processLambdasXis(femtotables::FilteredFemtoCollisions const& cols, femtotables::FemtoTracks const& tracks, femtotables::FemtoLambdas const& lambdas, femtotables::FemtoXis const& xis) + { + builders.trackBuilder.reset(tracks); + for (auto const& col : cols) { + if (builders.v0Builder.collisionHasTooFewLambdas(col, lambdas, lambdaPartition, cache) || builders.cascadeBuilder.collisionHasTooFewXis(col, xis, xiPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.v0Builder.processLambdas(col, lambdas, tracks, lambdaPartition, builders.trackBuilder, cache, v0BuilderProducts, trackBuilderProducts, collisionBuilderProducts); + builders.cascadeBuilder.processXis(col, xis, tracks, xiPartition, builders.trackBuilder, cache, cascadeBuilderProducts, trackBuilderProducts, collisionBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processLambdasXis, "Process lambdas and xis", false); + + // mc processing + void processTracksMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksMc, "Process tracks with MC information", false); + + void processTracksSigmaMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigma const& sigma, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigma(col, sigma, mcSigmaPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaWithMc(col, sigma, tracks, mcSigmaPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaMc, "Process sigma and tracks with MC information", false); + + void processTracksSigmaPlusMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlus const& sigmaplus, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusMc, "Process sigmaPlus and tracks with MC information", false); + + void processTracksSigmaPlusExtendedMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusExtended const& sigmaplus, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlus(col, sigmaplus, mcSigmaPlusExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusWithMc(col, sigmaplus, tracks, mcSigmaPlusExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusExtendedMc, "Process sigmaPlus (with SigmaPlusExtras) and tracks with MC information", false); + + void processTracksSigmaPlusPcmMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusPcm const& sigmapluspcm, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcmWithMc(col, sigmapluspcm, tracks, mcSigmaPlusPcmPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmMc, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores, selected on the BDT score) and tracks with MC information", false); + + void processTracksSigmaPlusPcmExtendedMc(femtotables::FilteredFemtoMcCollisions const& cols, + femtotables::FemtoMcTracks const& tracks, + femtotables::FemtoMcSigmaPlusPcmExtended const& sigmapluspcm, + o2::aod::FMcCols const& mcCols, + femtotables::FemtoMcParticles const& mcParticles, + o2::aod::FMcMothers const& mcMothers, + o2::aod::FMcPartMoths const& mcPartonicMothers) + { + // once per dataframe + builders.mcBuilder.reset(mcCols, mcParticles); + builders.trackBuilder.reset(tracks); + + for (auto const& col : cols) { + if (builders.trackBuilder.collisionHasTooFewTracks(col, tracks, mcTrackPartition1, mcTrackPartition2, cache) || builders.kinkBuilder.collisionHasTooFewSigmaPlusPcm(col, sigmapluspcm, mcSigmaPlusPcmExtendedPartition, cache)) { + continue; + } + builders.collisionBuilder.processCollision(col, collisionBuilderProducts); + builders.mcBuilder.fillCollisionWithLabel(col, mcCols, mcBuilderProducts); + builders.trackBuilder.processTracksWithMc(col, tracks, mcTrackPartition1, mcTrackPartition2, cache, + trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + builders.kinkBuilder.processSigmaPlusPcmWithMc(col, sigmapluspcm, tracks, mcSigmaPlusPcmExtendedPartition, builders.trackBuilder, cache, kinkBuilderProducts, trackBuilderProducts, collisionBuilderProducts, + builders.mcBuilder, mcCols, mcParticles, mcMothers, mcPartonicMothers, mcBuilderProducts); + } + } + PROCESS_SWITCH(FemtoProducerDerivedToDerived, processTracksSigmaPlusPcmExtendedMc, "Process sigmaPlusPcm (with SigmaPlusPcmMlScores and SigmaPlusPcmExtras, selected on the BDT score) and tracks with MC information", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - o2::framework::WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx b/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx index 46dfd292186..b97853e6829 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerKinkPtConverter.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/femtoUtils.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" +#include #include #include #include @@ -73,7 +74,9 @@ struct FemtoProducerKinkPtConverter { float pyMoth = sigma.pt() * std::sin(sigma.phi()); float pzMoth = sigma.pt() * std::sinh(sigma.eta()); - float ptRecalc = utils::calcPtnew(pxMoth, pyMoth, pzMoth, pxDaug, pyDaug, pzDaug); + // Sigma- -> pi- n + float ptRecalc = utils::calcPtnew(pxMoth, pyMoth, pzMoth, pxDaug, pyDaug, pzDaug, + o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPionCharged, o2::constants::physics::MassNeutron); ROOT::Math::PtEtaPhiMVector recalcVec(ptRecalc, sigma.eta(), sigma.phi(), sigma.mass()); float ptFrom4Vec = recalcVec.Pt(); @@ -106,8 +109,8 @@ struct FemtoProducerKinkPtConverter { } }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - o2::framework::WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx b/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx new file mode 100644 index 00000000000..fe68e9c13ad --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerLiteConverter.cxx @@ -0,0 +1,159 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerLiteConverter.cxx +/// \brief Task that converts FLite* tables (binned kinematics) back to their full-precision counterparts +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerLiteConverter { + o2::framework::Produces producedCols; + o2::framework::Produces producedColShapes; + o2::framework::Produces producedTracks; + o2::framework::Produces producedLambdas; + o2::framework::Produces producedK0shorts; + o2::framework::Produces producedXis; + o2::framework::Produces producedOmegas; + o2::framework::Produces producedSigmas; + o2::framework::Produces producedSigmaPlus; + o2::framework::Produces producedSigmaPlusPcm; + + void init(o2::framework::InitContext&) + { + if (!doprocessLiteCols) { + LOG(fatal) << "At least processLiteCols needs to be enabled, otherwise the collision index points no where!"; + } + } + + void processLiteCols(o2::aod::FLiteCols::iterator const& liteCol) + { + producedCols(liteCol.posZ(), + liteCol.mult(), + liteCol.cent(), + liteCol.magField()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteCols, "Convert FLiteCols to FCols", true); + + void processLiteColShapes(o2::aod::FLiteColShapes::iterator const& liteColShape) + { + producedColShapes(liteColShape.qvec(), + liteColShape.eventPlaneAngle()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteColShapes, "Convert FLiteColShapes to FColShapes", false); + + void processLiteTracks(o2::aod::FLiteTracks::iterator const& liteTrack) + { + producedTracks(liteTrack.fLiteColId(), + liteTrack.signedPt(), + liteTrack.eta(), + liteTrack.phi()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteTracks, "Convert FLiteTracks to FTracks", true); + + void processLiteLambdas(o2::aod::FLiteLambdas::iterator const& liteLambda) + { + producedLambdas(liteLambda.fLiteColId(), + liteLambda.signedPt(), + liteLambda.eta(), + liteLambda.phi(), + liteLambda.lambdaMass(), + liteLambda.posDauId(), + liteLambda.negDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteLambdas, "Convert FLiteLambdas to FLambdas", false); + + void processLiteK0shorts(o2::aod::FLiteK0shorts::iterator const& liteK0short) + { + producedK0shorts(liteK0short.fLiteColId(), + liteK0short.pt(), + liteK0short.eta(), + liteK0short.phi(), + liteK0short.k0shortMass(), + liteK0short.posDauId(), + liteK0short.negDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteK0shorts, "Convert FLiteK0shorts to FK0shorts", false); + + void processLiteXis(o2::aod::FLiteXis::iterator const& liteXi) + { + producedXis(liteXi.fLiteColId(), + liteXi.signedPt(), + liteXi.eta(), + liteXi.phi(), + liteXi.xiMass(), + liteXi.bachelorId(), + liteXi.posDauId(), + liteXi.negDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteXis, "Convert FLiteXis to FXis", false); + + void processLiteOmegas(o2::aod::FLiteOmegas::iterator const& liteOmega) + { + producedOmegas(liteOmega.fLiteColId(), + liteOmega.signedPt(), + liteOmega.eta(), + liteOmega.phi(), + liteOmega.omegaMass(), + liteOmega.bachelorId(), + liteOmega.posDauId(), + liteOmega.negDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteOmegas, "Convert FLiteOmegas to FOmegas", false); + + void processLiteSigmas(o2::aod::FLiteSigmas::iterator const& liteSigma) + { + producedSigmas(liteSigma.fLiteColId(), + liteSigma.signedPt(), + liteSigma.eta(), + liteSigma.phi(), + liteSigma.sigmaMass(), + liteSigma.chaDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteSigmas, "Convert FLiteSigmas to FSigmas", false); + + void processLiteSigmaPlus(o2::aod::FLiteSigmaPlus::iterator const& liteSigmaPlus) + { + producedSigmaPlus(liteSigmaPlus.fLiteColId(), + liteSigmaPlus.signedPt(), + liteSigmaPlus.eta(), + liteSigmaPlus.phi(), + liteSigmaPlus.sigmaMass(), + liteSigmaPlus.chaDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteSigmaPlus, "Convert FLiteSigmaPlus to FSigmaPlus", false); + + void processLiteSigmaPlusPcm(o2::aod::FLiteSigmaPlusPcm::iterator const& liteSigmaPlusPcm) + { + producedSigmaPlusPcm(liteSigmaPlusPcm.fLiteColId(), + liteSigmaPlusPcm.signedPt(), + liteSigmaPlusPcm.eta(), + liteSigmaPlusPcm.phi(), + liteSigmaPlusPcm.sigmaMass(), + liteSigmaPlusPcm.chaDauId()); + } + PROCESS_SWITCH(FemtoProducerLiteConverter, processLiteSigmaPlusPcm, "Convert FLiteSigmaPlusPcm to FSigmaPlusPcm", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{o2::framework::adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/TableProducer/femtoProducerMaskConverter001To002.cxx b/PWGCF/Femto/TableProducer/femtoProducerMaskConverter001To002.cxx new file mode 100644 index 00000000000..b1a57b7bfed --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerMaskConverter001To002.cxx @@ -0,0 +1,67 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerMaskConverter001To002.cxx +/// \brief Task that converts *Masks_001 tables to *Masks_002 +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerMaskConverter001To002 { + o2::framework::Produces producedLambdaMasks; + o2::framework::Produces producedK0shortMasks; + o2::framework::Produces producedXiMasks; + o2::framework::Produces producedOmegaMasks; + + void init(o2::framework::InitContext&) + { + } + + void processLambdaMasks001(o2::aod::FLambdaMasks_001::iterator const& lambdaMaskOld) + { + producedLambdaMasks(static_cast(lambdaMaskOld.mask())); + } + PROCESS_SWITCH(FemtoProducerMaskConverter001To002, processLambdaMasks001, "Convert LambdaMasks_001 to LambdaMasks_002", false); + + void processK0shortMasks001(o2::aod::FK0shortMasks_001::iterator const& k0shortOld) + { + producedK0shortMasks(static_cast(k0shortOld.mask())); + } + PROCESS_SWITCH(FemtoProducerMaskConverter001To002, processK0shortMasks001, "Convert K0shortMasks_001 to K0shortMasks_002", false); + + void processXiMasks001(o2::aod::FXiMasks_001::iterator const& xiMaskOld) + { + producedXiMasks(static_cast(xiMaskOld.mask())); + } + PROCESS_SWITCH(FemtoProducerMaskConverter001To002, processXiMasks001, "Convert XiMasks_001 to XiMasks_002", false); + + void processOmegaMasks001(o2::aod::FOmegaMasks_001::iterator const& omegaMaskOld) + { + producedOmegaMasks(static_cast(omegaMaskOld.mask())); + } + PROCESS_SWITCH(FemtoProducerMaskConverter001To002, processOmegaMasks001, "Convert OmegaMasks_001 to OmegaMasks_002", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{o2::framework::adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx index 1eaba0fa6bd..179023fc657 100644 --- a/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducerResonances.cxx @@ -47,16 +47,16 @@ struct FemtoProducerResonances { // setup for resonance daughter tracks trackbuilder::ConfPionPlusSelection confPionPlusSelection; - o2::framework::Partition pionPlusPartition = MAKE_TRACK_PARTITION(confPionPlusSelection); + o2::framework::Partition pionPlusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confPionPlusSelection); trackbuilder::ConfPionMinusSelection confPionMinusSelection; - o2::framework::Partition pionMinusPartition = MAKE_TRACK_PARTITION(confPionMinusSelection); + o2::framework::Partition pionMinusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confPionMinusSelection); trackbuilder::ConfKaonPlusSelection confKaonPlusSelection; - o2::framework::Partition kaonPlusPartition = MAKE_TRACK_PARTITION(confKaonPlusSelection); + o2::framework::Partition kaonPlusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confKaonPlusSelection); trackbuilder::ConfKaonMinusSelection confKaonMinusSelection; - o2::framework::Partition kaonMinusPartition = MAKE_TRACK_PARTITION(confKaonMinusSelection); + o2::framework::Partition kaonMinusPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(confKaonMinusSelection); o2::framework::Preslice perColtracks = o2::aod::femtobase::stored::fColId; @@ -103,8 +103,8 @@ struct FemtoProducerResonances { PROCESS_SWITCH(FemtoProducerResonances, processKstar0, "Build Kstar0/Kstar0bar candidates", true); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - o2::framework::WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx new file mode 100644 index 00000000000..20308a135a4 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTrackTrackTwoPhotonResonances.cxx @@ -0,0 +1,92 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerTrackTrackTwoPhotonResonances.cxx +/// \brief Task that produces femto tables for resonances built from two tracks and a two-photon +/// resonance (e.g. omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +// reads the already-built FTracks (for the charged pion daughters) and FPi0s (for the neutral +// daughter) and combines them into omega(782) -> pi+ pi- pi0 candidates. A 3rd-stage producer, +// chained after femtoProducer.cxx (FTracks) and femtoProducerTwoPhotonResonances.cxx (FPi0s), same +// pattern as femtoProducerTwoPhotonResonances.cxx itself reading femtoProducer.cxx's FPhotons. +struct FemtoProducerTrackTrackTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for the two charged (pion) daughters. Dedicated pion-species selections, same ones + // femtoProducerResonances.cxx uses for other two track resonances + trackbuilder::ConfPionPlusSelection posDauSelection; + o2::framework::Partition posDauPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(posDauSelection); + o2::framework::Preslice perColPosDau = o2::aod::femtobase::stored::fColId; + + trackbuilder::ConfPionMinusSelection negDauSelection; + o2::framework::Partition negDauPartition = MAKE_RESONANCE_DAUGHTER_PARTITION(negDauSelection); + o2::framework::Preslice perColNegDau = o2::aod::femtobase::stored::fColId; + + // setup for the neutral (pi0) daughter + twophotonresonancebuilder::ConfPi0Selection neutralDauSelection; + o2::framework::Partition neutralDauPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(neutralDauSelection); + o2::framework::Preslice perColNeutralDau = o2::aod::femtobase::stored::fColId; + + // resonance filter and builder + trackstwophotonresonancebuilder::ConfOmegaMesonFilters confOmegaMesonFilter; + trackstwophotonresonancebuilder::ConfTrackTrackTwoPhotonResonanceTables confOmegaMesonTables; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilderProducts omegaMesonBuilderProducts; + trackstwophotonresonancebuilder::TrackTrackTwoPhotonResonanceBuilder omegaMesonBuilder; + + void init(o2::framework::InitContext& context) + { + omegaMesonBuilder.init(confOmegaMesonFilter, posDauSelection, negDauSelection, confOmegaMesonTables, context); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/) + { + omegaMesonBuilder.fillResonances(col, omegaMesonBuilderProducts, posDauPartition, negDauPartition, neutralDauPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTrackTrackTwoPhotonResonances, processOmegaMesons, "Build OmegaMeson candidates", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx new file mode 100644 index 00000000000..4752d5f3a54 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerTwoPhotonResonances.cxx @@ -0,0 +1,88 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoProducerTwoPhotonResonances.cxx +/// \brief Tasks that produces femto tables for resonances built from two PCM photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerTwoPhotonResonances { + + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for resonance daughter photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + // resonance filters + twophotonresonancebuilder::ConfPi0Filters confPi0Filter; + twophotonresonancebuilder::ConfEtaFilters confEtaFilter; + + // resonance builders + twophotonresonancebuilder::ConfTwoPhotonResonanceTables confTwoPhotonResonanceTables; + twophotonresonancebuilder::TwoPhotonResonanceBuilderProducts twoPhotonResonanceBuilderProducts; + twophotonresonancebuilder::TwoPhotonResonanceBuilder pi0Builder; + twophotonresonancebuilder::TwoPhotonResonanceBuilder etaBuilder; + + void init(o2::framework::InitContext& context) + { + // init builders + pi0Builder.init(confPi0Filter, confTwoPhotonResonanceTables, context); + etaBuilder.init(confEtaFilter, confTwoPhotonResonanceTables, context); + } + + // process functions + void processPi0(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + pi0Builder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processPi0, "Build Pi0 candidates", true); + + void processEta(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + etaBuilder.fillResonances(col, twoPhotonResonanceBuilderProducts, photonPartition, cache); + } + PROCESS_SWITCH(FemtoProducerTwoPhotonResonances, processEta, "Build Eta candidates", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{adaptAnalysisTask(context)}; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/CMakeLists.txt b/PWGCF/Femto/Tasks/CMakeLists.txt index baad9c28a8a..1f9647bd7b2 100644 --- a/PWGCF/Femto/Tasks/CMakeLists.txt +++ b/PWGCF/Femto/Tasks/CMakeLists.txt @@ -19,11 +19,26 @@ o2physics_add_dpl_workflow(femto-twotrackresonance-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-two-photon-resonance-qa + SOURCES femtoTwoPhotonResonanceQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-track-track-two-photon-resonance-qa + SOURCES femtoTrackTrackTwoPhotonResonanceQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-v0-qa SOURCES femtoV0Qa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-photon-qa + SOURCES femtoPhotonQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-kink-qa SOURCES femtoKinkQa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -34,6 +49,11 @@ o2physics_add_dpl_workflow(femto-cascade-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-mc-particle-qa + SOURCES femtoMcParticleQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-pair-track-track SOURCES femtoPairTrackTrack.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -44,11 +64,36 @@ o2physics_add_dpl_workflow(femto-pair-track-v0 PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-pair-track-charm-hadron + SOURCES femtoPairTrackCharmHadron.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-pair-d0-d0 + SOURCES femtoPairD0D0.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-charm-hadron-qa + SOURCES femtoCharmHadronQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-pair-track-two-track-resonance SOURCES femtoPairTrackTwoTrackResonance.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-pair-track-two-photon-resonance + SOURCES femtoPairTrackTwoPhotonResonance.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-pair-v0-two-track-resonance + SOURCES femtoPairV0TwoTrackResonance.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-pair-track-cascade SOURCES femtoPairTrackCascade.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -64,6 +109,11 @@ o2physics_add_dpl_workflow(femto-pair-v0-v0 PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-pair-mc-particle-mc-particle + SOURCES femtoPairMcParticleMcParticle.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-triplet-track-track-track SOURCES femtoTripletTrackTrackTrack.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -73,3 +123,28 @@ o2physics_add_dpl_workflow(femto-triplet-track-track-v0 SOURCES femtoTripletTrackTrackV0.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-triplet-track-track-cascade + SOURCES femtoTripletTrackTrackCascade.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-triplet-mc-particle-mc-particle-mc-particle + SOURCES femtoTripletMcParticleMcParticleMcParticle.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-dndeta-pair + SOURCES femtoDndetaPair.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-dndeta-triplet + SOURCES femtoDndetaTriplet.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto-pair-efficiency + SOURCES femtoPairEfficiency.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx index 80a19160b94..78b8ae337dc 100644 --- a/PWGCF/Femto/Tasks/femtoCascadeQa.cxx +++ b/PWGCF/Femto/Tasks/femtoCascadeQa.cxx @@ -41,7 +41,7 @@ using namespace o2::analysis::femto; struct FemtoCascadeQa { - using FemtoCollisions = o2::soa::Join; + using FemtoCollisions = o2::soa::Join; using FilteredFemtoCollisions = o2::soa::Filtered; using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; @@ -57,6 +57,8 @@ struct FemtoCascadeQa { using FemtoOmegasWithLabel = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup collisions @@ -122,7 +124,7 @@ struct FemtoCascadeQa { void init(o2::framework::InitContext&) { - if ((doprocessXi + doprocessXiMc + doprocessOmega + doprocessOmegaMc) > 1) { + if ((static_cast(doprocessXi) + static_cast(doprocessXiMc) + static_cast(doprocessOmega) + static_cast(doprocessOmegaMc)) > 1) { LOG(fatal) << "Only one process can be activated"; } bool processData = doprocessXi || doprocessOmega; @@ -139,31 +141,31 @@ struct FemtoCascadeQa { if (processData) { colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); bachelorHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confBachelorBinning, confBachelorQaBinning); posDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDaughterBinning, confPosDaughterQaBinning); negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); if (doprocessXi) { xiHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confXiBinning, confXiQaBinning); - xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessOmega) { omegaHistSpec = cascadehistmanager::makeCascadeQaHistSpecMap(confOmegaBinning, confOmegaQaBinning); - omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); bachelorHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confBachelorBinning, confBachelorQaBinning); posDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confPosDaughterBinning, confPosDaughterQaBinning); negDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); if (doprocessXiMc) { xiHistSpec = cascadehistmanager::makeCascadeMcQaHistSpecMap(confXiBinning, confXiQaBinning); - xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + xiHistManager.init(&hRegistry, xiHistSpec, confXiSelection, confXiQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessOmegaMc) { omegaHistSpec = cascadehistmanager::makeCascadeMcQaHistSpecMap(confOmegaBinning, confOmegaQaBinning); - omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + omegaHistManager.init(&hRegistry, omegaHistSpec, confOmegaSelection, confOmegaQaBinning, bachelorHistSpec, confBachelorQaBinning, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } } hRegistry.print(); @@ -175,25 +177,25 @@ struct FemtoCascadeQa { if (xiSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& xi : xiSlice) { - xiHistManager.fill(xi, tracks); + xiHistManager.fill(xi, tracks); } } PROCESS_SWITCH(FemtoCascadeQa, processXi, "Process Xis", true); - void processXiMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processXiMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { auto xiSlice = xiWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (xiSlice.size() == 0) { return; } - colHistManager.fill(col, mcCols); + colHistManager.fill(col, mcCols); for (auto const& xi : xiSlice) { if (!xiCleaner.isClean(xi, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - xiHistManager.fill(xi, tracks, mcParticles, mcMothers, mcPartonicMothers); + xiHistManager.fill(xi, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoCascadeQa, processXiMc, "Process Xis with MC information", false); @@ -204,34 +206,34 @@ struct FemtoCascadeQa { if (omegaSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& omega : omegaSlice) { - omegaHistManager.fill(omega, tracks); + omegaHistManager.fill(omega, tracks); } } PROCESS_SWITCH(FemtoCascadeQa, processOmega, "Process Omegas", false); - void processOmegaMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& /*omegas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processOmegaMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& /*omegas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { auto omegaSlice = omegaWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (omegaSlice.size() == 0) { return; } - colHistManager.fill(col, mcCols); + colHistManager.fill(col, mcCols); for (auto const& omega : omegaSlice) { if (!omegaCleaner.isClean(omega, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - omegaHistManager.fill(omega, tracks, mcParticles, mcMothers, mcPartonicMothers); + omegaHistManager.fill(omega, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoCascadeQa, processOmegaMc, "Process Omegas with MC information", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoCharmHadronQa.cxx b/PWGCF/Femto/Tasks/femtoCharmHadronQa.cxx new file mode 100644 index 00000000000..76bf7ad3f31 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoCharmHadronQa.cxx @@ -0,0 +1,249 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoCharmHadronQa.cxx +/// \brief QA task for charm hadrons +/// \author Igor Ptak, WUT, igor.tomasz.ptak@cern.ch + +#include "PWGCF/Femto/Core/charmHadronBuilder.h" +#include "PWGCF/Femto/Core/charmHadronHistManager.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoCharmHadronQa { + // setup collisions + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + // setup D0s, joined with the mask for the partition and the QA columns + using FemtoD0s = o2::soa::Join; + // setup Lcs, joined with the mask for the partition and the QA columns + using FemtoLcs = o2::soa::Join; + // setup tracks with full pid information for the daughter QA + using FemtoTracks = o2::soa::Join; + + // setup monte carlo, joining the labels that link reco to generated particles + using FemtoCollisionsWithLabel = o2::soa::Join; + using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; + using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + + using FemtoD0sWithLabel = o2::soa::Join; + using FemtoLcsWithLabel = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for D0s + charmhadronbuilder::ConfD0Selection1 confD0Selection; + + o2::framework::Partition d0Partition = MAKE_D0_PARTITION(confD0Selection); + o2::framework::Preslice perColD0s = o2::aod::femtobase::stored::fColId; + o2::framework::Partition d0WithLabelPartition = MAKE_D0_PARTITION(confD0Selection); + o2::framework::Preslice perColD0sWithLabel = o2::aod::femtobase::stored::fColId; + + charmhadronhistmanager::ConfD0Binning1 confD0Binning; + charmhadronhistmanager::ConfD0QaBinning1 confD0QaBinning; + + // setup for Lcs + charmhadronbuilder::ConfLcSelection1 confLcSelection; + + o2::framework::Partition lcPartition = MAKE_CHARM3PRONG_PARTITION(confLcSelection); + o2::framework::Preslice perColLcs = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lcWithLabelPartition = MAKE_CHARM3PRONG_PARTITION(confLcSelection); + o2::framework::Preslice perColLcsWithLabel = o2::aod::femtobase::stored::fColId; + + charmhadronhistmanager::ConfLcBinning1 confLcBinning; + charmhadronhistmanager::ConfLcQaBinning1 confLcQaBinning; + + // D0 + charmhadronhistmanager::CharmHadronHistManager< + charmhadronhistmanager::PrefixD0Qa, + trackhistmanager::PrefixD01PosDaughterQa, + trackhistmanager::PrefixD01NegDaughterQa, + trackhistmanager::PrefixD01NegDaughterQa, // unused for 2-prong candidates + modes::CharmHadron::kD0> + d0HistManager; + // Lc + charmhadronhistmanager::CharmHadronHistManager< + charmhadronhistmanager::PrefixLcQa, + trackhistmanager::PrefixLc1ProtonDaughterQa, + trackhistmanager::PrefixLc1KaonDaughterQa, + trackhistmanager::PrefixLc1PionDaughterQa, + modes::CharmHadron::kLc> + lcHistManager; + + // setup for daughters + trackhistmanager::ConfD01PosDauBinning confD01PosDaughterBinning; + trackhistmanager::ConfD01PosDauQaBinning confD01PosDaughterQaBinning; + + trackhistmanager::ConfD01NegDauBinning confD01NegDaughterBinning; + trackhistmanager::ConfD01NegDauQaBinning confD01NegDaughterQaBinning; + + trackhistmanager::ConfLc1ProtonDauBinning confLc1ProtonDaughterBinning; + trackhistmanager::ConfLc1ProtonDauQaBinning confLc1ProtonDaughterQaBinning; + + trackhistmanager::ConfLc1KaonDauBinning confLc1KaonDaughterBinning; + trackhistmanager::ConfLc1KaonDauQaBinning confLc1KaonDaughterQaBinning; + + trackhistmanager::ConfLc1PionDauBinning confLc1PionDaughterBinning; + trackhistmanager::ConfLc1PionDauQaBinning confLc1PionDaughterQaBinning; + + o2::framework::HistogramRegistry hRegistry{"FemtoCharmHadronQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + if ((static_cast(doprocessD0) + static_cast(doprocessD0Mc) + static_cast(doprocessLc) + static_cast(doprocessLcMc)) != 1) { + LOG(fatal) << "Only one process can be activated"; + } + bool processData = doprocessD0 || doprocessLc; + + std::map> colHistSpec; + std::map> posDaughterHistSpec; + std::map> negDaughterHistSpec; + std::map> protonHistSpec; + std::map> kaonHistSpec; + std::map> pionHistSpec; + std::map> d0HistSpec; + std::map> d0QaHistSpec; + std::map> lcHistSpec; + std::map> lcQaHistSpec; + + if (processData) { + colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + if (doprocessD0) { + posDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confD01PosDaughterBinning, confD01PosDaughterQaBinning); + negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confD01NegDaughterBinning, confD01NegDaughterQaBinning); + d0HistSpec = charmhadronhistmanager::makeCharmHadronHistSpecMap(confD0Binning); + d0QaHistSpec = charmhadronhistmanager::makeD0QaHistSpecMap(confD0QaBinning); + d0HistManager.init(&hRegistry, d0HistSpec, d0QaHistSpec, confD0Selection, confD0QaBinning, posDaughterHistSpec, confD01PosDaughterQaBinning, negDaughterHistSpec, confD01NegDaughterQaBinning); + } + if (doprocessLc) { + protonHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confLc1ProtonDaughterBinning, confLc1ProtonDaughterQaBinning); + kaonHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confLc1KaonDaughterBinning, confLc1KaonDaughterQaBinning); + pionHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confLc1PionDaughterBinning, confLc1PionDaughterQaBinning); + lcHistSpec = charmhadronhistmanager::makeCharmHadronHistSpecMap(confLcBinning); + lcQaHistSpec = charmhadronhistmanager::makeLcQaHistSpecMap(confLcQaBinning); + lcHistManager.init(&hRegistry, lcHistSpec, lcQaHistSpec, confLcSelection, confLcQaBinning, protonHistSpec, confLc1ProtonDaughterQaBinning, kaonHistSpec, confLc1KaonDaughterQaBinning, pionHistSpec, confLc1PionDaughterQaBinning); + } + } else { + colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + if (doprocessD0Mc) { + posDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confD01PosDaughterBinning, confD01PosDaughterQaBinning); + negDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confD01NegDaughterBinning, confD01NegDaughterQaBinning); + d0HistSpec = charmhadronhistmanager::makeD0McQaHistSpecMap(confD0Binning, confD0QaBinning); + d0QaHistSpec = charmhadronhistmanager::makeD0QaHistSpecMap(confD0QaBinning); + d0HistManager.init(&hRegistry, d0HistSpec, d0QaHistSpec, confD0Selection, confD0QaBinning, posDaughterHistSpec, confD01PosDaughterQaBinning, negDaughterHistSpec, confD01NegDaughterQaBinning); + } + if (doprocessLcMc) { + protonHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confLc1ProtonDaughterBinning, confLc1ProtonDaughterQaBinning); + kaonHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confLc1KaonDaughterBinning, confLc1KaonDaughterQaBinning); + pionHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confLc1PionDaughterBinning, confLc1PionDaughterQaBinning); + lcHistSpec = charmhadronhistmanager::makeLcMcQaHistSpecMap(confLcBinning, confLcQaBinning); + lcQaHistSpec = charmhadronhistmanager::makeLcQaHistSpecMap(confLcQaBinning); + lcHistManager.init(&hRegistry, lcHistSpec, lcQaHistSpec, confLcSelection, confLcQaBinning, protonHistSpec, confLc1ProtonDaughterQaBinning, kaonHistSpec, confLc1KaonDaughterQaBinning, pionHistSpec, confLc1PionDaughterQaBinning); + } + } + hRegistry.print(); + }; + + void processD0(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + auto d0Slice = d0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& d0 : d0Slice) { + d0HistManager.fill(d0, tracks); + } + } + PROCESS_SWITCH(FemtoCharmHadronQa, processD0, "Process D0s", true); + + void processD0Mc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoD0sWithLabel const& /*d0s*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + auto d0Slice = d0WithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (d0Slice.size() == 0) { + return; + } + colHistManager.fill(col, mcCols); + for (auto const& d0 : d0Slice) { + d0HistManager.fill(d0, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + } + } + PROCESS_SWITCH(FemtoCharmHadronQa, processD0Mc, "Process D0s with MC information", false); + + void processLc(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) + { + auto lcSlice = lcPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (lcSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& lc : lcSlice) { + lcHistManager.fill(lc, tracks); + } + } + PROCESS_SWITCH(FemtoCharmHadronQa, processLc, "Process Lcs", false); + + void processLcMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLcsWithLabel const& /*lcs*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + auto lcSlice = lcWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (lcSlice.size() == 0) { + return; + } + colHistManager.fill(col, mcCols); + for (auto const& lc : lcSlice) { + lcHistManager.fill(lc, tracks, col, mcParticles, mcMothers, mcPartonicMothers); + } + } + PROCESS_SWITCH(FemtoCharmHadronQa, processLcMc, "Process Lcs with MC information", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoDndetaPair.cxx b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx new file mode 100644 index 00000000000..df35e04f725 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoDndetaPair.cxx @@ -0,0 +1,398 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoDndetaPair.cxx +/// \brief charged-particle pseudorapidity density in events triggered on femto pairs (or minimum bias) +/// \author Anton Riedel, TU München, anton.riedel@cern.ch +/// +/// An event is triggered if at least one pair passes the particle selections, the pair cleaner, +/// the close pair rejection and the pair cuts (e.g. PairCuts.kstarMax). +/// Needs a femto producer run in pass-through mode. + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/dndetaBuilder.h" +#include "PWGCF/Femto/Core/dndetaHistManager.h" +#include "PWGCF/Femto/Core/mcBuilder.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGCF/Femto/Core/v0HistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoDndetaPair { + + // setup tables + // FCols provides the default centrality (FT0M in pp, FT0C in PbPb), FColMults the INEL>0 and correlation estimators + using FemtoCollisions = o2::soa::Join; + using FemtoCollision = FemtoCollisions::iterator; + using FemtoCollisionsWithLabel = o2::soa::Join; + + // same join as femtoTrackQa, the track histogram managers (QA) need mass, DCAs, extras and PID + using FemtoTracks = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + + using FemtoLambdas = o2::soa::Join; + using FemtoLambdasWithLabel = o2::soa::Join; + using FemtoK0shorts = o2::soa::Join; + + using FemtoMcParticles = o2::soa::Join; + + o2::framework::SliceCache cache; + o2::framework::Service pdg = {}; + + // setup dndeta + dndetabuilder::ConfDndeta confDndeta; + dndetabuilder::ConfDndetaAcceptance confDndetaAcceptance; + dndetahistmanager::ConfDndetaBinning confDndetaBinning; + dndetabuilder::DndetaBuilder dndetaBuilder; + + // pass-through mode does not guarantee fMcColId is written in sorted order (reco-driven label + // rows can be created out of mc-collision order), so this must be an unsorted grouping + o2::framework::PresliceUnsorted perMcColParticles = o2::aod::femtomcparticle::fMcColId; + + // setup collisions (applied in the dndeta builder, no filter, so the cutflow sees every collision) + collisionbuilder::ConfCollisionSelection collisionSelection; + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup tracks for dndeta (bitmask with track quality bits only, see dndetaBuilder.h) + trackbuilder::ConfTrackSelectionDndetaGlobal confDndetaTracksGlobal; + trackhistmanager::ConfDndetaTrackGlobalBinning confDndetaTrackGlobalBinning; + trackhistmanager::ConfDndetaTrackGlobalQaBinning confDndetaTrackGlobalQaBinning; + + trackbuilder::ConfTrackSelectionDndetaItsOnly confDndetaTracksItsOnly; + trackhistmanager::ConfDndetaTrackItsOnlyBinning confDndetaTrackItsOnlyBinning; + trackhistmanager::ConfDndetaTrackItsOnlyQaBinning confDndetaTrackItsOnlyQaBinning; + + o2::framework::Partition dndetaTrackPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); + o2::framework::Partition dndetaTrackPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + + o2::framework::Partition dndetaTrackWithLabelPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); + o2::framework::Partition dndetaTrackWithLabelPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + + // the *Mc process functions take the ungrouped tables (no ::iterator first argument), so DPL never + // auto-registers the fColId slicing cache for FemtoTracksWithLabel; this unused Preslice is the only + // thing that requests/enables it, otherwise sliceByCached() throws "Disabled cache ... is requested" + // once the first accepted reco collision actually reaches it + o2::framework::Preslice perColTracksWithLabel = o2::aod::femtobase::stored::fColId; + + // setup tracks (trigger) + trackbuilder::ConfTrackSelection1 confTrackSelection1; + trackhistmanager::ConfTrackBinning1 confTrackBinning1; + particlecleaner::ConfTrackCleaner1 confTrackCleaner1; + + trackbuilder::ConfTrackSelection2 confTrackSelection2; + trackhistmanager::ConfTrackBinning2 confTrackBinning2; + particlecleaner::ConfTrackCleaner2 confTrackCleaner2; + + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(confTrackSelection1); + o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(confTrackSelection2); + + o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelection1); + o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelection2); + + // setup for daughters + trackhistmanager::ConfV01PosDauBinning confPosDauBinning; + trackhistmanager::ConfV01NegDauBinning confNegDauBinning; + + // setup lambdas (trigger) + v0builder::ConfLambdaSelection1 confLambdaSelection; + v0histmanager::ConfLambdaBinning1 confLambdaBinning; + particlecleaner::ConfLambdaCleaner1 confLambdaCleaner; + + o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + // same reason as perColTracksWithLabel above, needed by processTrackV0Mc + o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; + + // setup strangeness yields (dedicated selections, independent of the trigger) + v0builder::ConfLambdaSelectionStrangeness confStrangeLambdaSelection; + v0builder::ConfK0shortSelectionStrangeness confStrangeK0shortSelection; + + o2::framework::Partition strangeLambdaPartition = MAKE_LAMBDA_PARTITION(confStrangeLambdaSelection); + o2::framework::Partition strangeK0shortPartition = MAKE_K0SHORT_PARTITION(confStrangeK0shortSelection); + + // setup mc particles (generator-level trigger) + // FMcParticles also contains rows of reconstructed secondaries (created via labels), so set + // McParticleSelection1/2.requireOrigin = true (origin = 2, physical primary) for an unbiased generator-level trigger + mcbuilder::ConfMcParticleSelection1 confMcParticleSelection1; + mcparticlehistmanager::ConfMcParticleBinning1 confMcParticleBinning1; + particlecleaner::ConfMcParticleCleaner1 confMcParticleCleaner1; + + mcbuilder::ConfMcParticleSelection2 confMcParticleSelection2; + mcparticlehistmanager::ConfMcParticleBinning2 confMcParticleBinning2; + particlecleaner::ConfMcParticleCleaner2 confMcParticleCleaner2; + + o2::framework::Partition mcParticlePartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); + o2::framework::Partition mcParticlePartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + pairhistmanager::ConfMixing confMixing; // only same event is processed, used for sameSpecies/seed + + closepairrejection::ConfCprTrackTrack confCprTrackTrack; + closepairrejection::ConfCprMcParticleMcParticle confCprMcParticleMcParticle; // generator-level trigger, no cut by default + closepairrejection::ConfCprTrackV0Daughter confCprTrackV0; + + pairbuilder::PairTrackTrackBuilder< + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + pairhistmanager::PrefixTrackTrackSe, + pairhistmanager::PrefixTrackTrackMe, + closepairrejection::PrefixTrackTrackSe, + closepairrejection::PrefixTrackTrackMe> + pairTrackTrackBuilder; + + pairbuilder::PairTrackV0Builder< + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + pairhistmanager::PrefixTrackV0Se, + pairhistmanager::PrefixTrackV0Me, + closepairrejection::PrefixTrackV0DaughterSe, + closepairrejection::PrefixTrackV0DaughterMe, + modes::V0::kLambda> + pairTrackLambdaBuilder; + + pairbuilder::PairMcParticleMcParticleBuilder< + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + pairhistmanager::PrefixMcParticleMcParticleSe, + pairhistmanager::PrefixMcParticleMcParticleMe, + closepairrejection::PrefixMcParticleMcParticleSe, + closepairrejection::PrefixMcParticleMcParticleMe> + pairMcParticleMcParticleBuilder; + + // dndeta histograms and trigger QA are kept in separate registries, so the fixed histogram names of the builders cannot clash + o2::framework::HistogramRegistry hRegistry{"FemtoDndetaPair", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistryTrigger{"FemtoDndetaPairTrigger", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistryGenTrigger{"FemtoDndetaPairGenTrigger", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + const bool processDataWithStrangeness = doprocessMinimumBiasWithStrangeness || doprocessTrackTrackWithStrangeness || doprocessTrackV0WithStrangeness; + const bool processMc = doprocessMinimumBiasMc || doprocessTrackTrackMc || doprocessTrackV0Mc; + const int nProcess = static_cast(doprocessMinimumBias) + static_cast(doprocessTrackTrack) + static_cast(doprocessTrackV0) + + static_cast(doprocessMinimumBiasWithStrangeness) + static_cast(doprocessTrackTrackWithStrangeness) + static_cast(doprocessTrackV0WithStrangeness) + + static_cast(doprocessMinimumBiasMc) + static_cast(doprocessTrackTrackMc) + static_cast(doprocessTrackV0Mc); + if (nProcess != 1) { + LOG(fatal) << "Exactly one process function has to be activated (found " << nProcess << "). Breaking..."; + } + + modes::Trigger trigger = modes::Trigger::kMinimumBias; + if (doprocessTrackTrack || doprocessTrackTrackWithStrangeness || doprocessTrackTrackMc) { + trigger = modes::Trigger::kTrackTrack; + } else if (doprocessTrackV0 || doprocessTrackV0WithStrangeness || doprocessTrackV0Mc) { + trigger = modes::Trigger::kTrackV0; + } + LOG(info) << "Trigger: " << modes::triggerToString(trigger); + + dndetaBuilder.init(&hRegistry, confDndeta, confDndetaAcceptance, collisionSelection, + confDndetaTracksGlobal, confDndetaTrackGlobalBinning, confDndetaTrackGlobalQaBinning, + confDndetaTracksItsOnly, confDndetaTrackItsOnlyBinning, confDndetaTrackItsOnlyQaBinning, + confDndetaBinning, processMc, processDataWithStrangeness); + if (dndetaBuilder.useGenTrigger() && (!processMc || trigger == modes::Trigger::kMinimumBias)) { + LOG(warn) << "Dndeta.useGenTrigger is only used for mc processing with a pair trigger. Ignoring it."; + } + + // setup histogram specs + std::map> colHistSpec; + std::map> trackHistSpec1; + std::map> trackHistSpec2; + std::map> posDauSpec; + std::map> negDauSpec; + std::map> lambdaHistSpec; + std::map> pairHistSpec; + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + if (!processMc) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + pairHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + } else { + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); + posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); + lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); + pairHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); + } + + // only the builder of the active trigger is initialized + // only same event processing is used; the mixed event histograms are booked but stay empty + if (trigger == modes::Trigger::kTrackTrack) { + std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCprTrackTrack); + if (!processMc) { + pairTrackTrackBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confTrackCleaner1, confTrackCleaner2, confCprTrackTrack, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + pairTrackTrackBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confTrackCleaner1, confTrackCleaner2, confCprTrackTrack, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } + } else if (trigger == modes::Trigger::kTrackV0) { + std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCprTrackV0); + if (!processMc) { + pairTrackLambdaBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackCleaner1, confLambdaSelection, confLambdaCleaner, confCprTrackV0, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, lambdaHistSpec, posDauSpec, negDauSpec, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + pairTrackLambdaBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackCleaner1, confLambdaSelection, confLambdaCleaner, confCprTrackV0, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, lambdaHistSpec, posDauSpec, negDauSpec, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } + } + + // generator-level trigger + if (processMc && trigger != modes::Trigger::kMinimumBias && dndetaBuilder.useGenTrigger()) { + std::map> genColHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + std::map> mcParticleHistSpec1 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning1); + std::map> mcParticleHistSpec2 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning2); + std::map> genPairHistSpec = pairhistmanager::makePairMcTruthHistSpecMap(confPairBinning, confMixing); + std::map> genCprHistSpec = closepairrejection::makeCprHistSpecMap(confCprMcParticleMcParticle); + pairMcParticleMcParticleBuilder.init(&hRegistryGenTrigger, confCollisionBinning, confMcParticleSelection1, confMcParticleSelection2, confMcParticleBinning1, confMcParticleBinning2, confMcParticleCleaner1, confMcParticleCleaner2, confCprMcParticleMcParticle, confMixing, confPairBinning, confPairCuts, genColHistSpec, mcParticleHistSpec1, mcParticleHistSpec2, genPairHistSpec, genCprHistSpec, pairCleanerHistSpec); + } + + hRegistry.print(); + hRegistryTrigger.print(); + hRegistryGenTrigger.print(); + }; + + // data + void processMinimumBias(FemtoCollision const& col, FemtoTracks const& /*tracks*/) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, []() { return true; }); + } + PROCESS_SWITCH(FemtoDndetaPair, processMinimumBias, "Data, no trigger", true); + + void processTrackTrack(FemtoCollision const& col, FemtoTracks const& tracks) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { return pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackTrack, "Data, track-track trigger", false); + + void processTrackV0(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { return pairTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, lambdas, lambdaPartition, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackV0, "Data, track-lambda trigger", false); + + // data with strangeness yields + template + void fillStrangeness(T const& col) + { + auto lambdas = strangeLambdaPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto k0shorts = strangeK0shortPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + dndetaBuilder.processStrangeness(col, lambdas, k0shorts); + } + + void processMinimumBiasWithStrangeness(FemtoCollision const& col, FemtoTracks const& /*tracks*/, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, []() { + return true; + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaPair, processMinimumBiasWithStrangeness, "Data with strangeness yields, no trigger", false); + + void processTrackTrackWithStrangeness(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackTrackWithStrangeness, "Data with strangeness yields, track-track trigger", false); + + void processTrackV0WithStrangeness(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return pairTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, lambdas, lambdaPartition, cache); + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackV0WithStrangeness, "Data with strangeness yields, track-lambda trigger", false); + + // mc + void processMinimumBiasMc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& /*tracks*/, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [](auto const& /*col*/) { + return true; + }, + [](auto const& /*mcCol*/) { return true; }); + } + PROCESS_SWITCH(FemtoDndetaPair, processMinimumBiasMc, "MC, no trigger", false); + + void processTrackTrackMc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [&](auto const& col) { return pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); }, + [&](auto const& mcCol) { return pairMcParticleMcParticleBuilder.processSameEvent(mcCol, mcParticles, mcMothers, mcPartonicMothers, mcParticlePartition1, mcParticlePartition2, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackTrackMc, "MC, track-track trigger", false); + + void processTrackV0Mc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [&](auto const& col) { return pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); }, + [&](auto const& mcCol) { return pairMcParticleMcParticleBuilder.processSameEvent(mcCol, mcParticles, mcMothers, mcPartonicMothers, mcParticlePartition1, mcParticlePartition2, cache); }); + } + PROCESS_SWITCH(FemtoDndetaPair, processTrackV0Mc, "MC, track-lambda trigger", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx new file mode 100644 index 00000000000..44e830e1aed --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoDndetaTriplet.cxx @@ -0,0 +1,420 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoDndetaTriplet.cxx +/// \brief charged-particle pseudorapidity density in events triggered on femto triplets +/// \author Anton Riedel, TU München, anton.riedel@cern.ch +/// +/// An event is triggered if at least one triplet passes the particle selections, the triplet cleaner, +/// the close triplet rejection and the triplet cuts (e.g. TripletCuts.q3Max). +/// There is no generator-level triplet builder yet, so the event/signal loss is not conditioned on a generator-level trigger. +/// Needs a femto producer run in pass-through mode. + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/closeTripletRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/dndetaBuilder.h" +#include "PWGCF/Femto/Core/dndetaHistManager.h" +#include "PWGCF/Femto/Core/mcBuilder.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/tripletBuilder.h" +#include "PWGCF/Femto/Core/tripletHistManager.h" +#include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGCF/Femto/Core/v0HistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoDndetaTriplet { + + // setup tables + // FCols provides the default centrality (FT0M in pp, FT0C in PbPb), FColMults the INEL>0 and correlation estimators + using FemtoCollisions = o2::soa::Join; + using FemtoCollision = FemtoCollisions::iterator; + using FemtoCollisionsWithLabel = o2::soa::Join; + + // same join as femtoTrackQa, the track histogram managers (QA) need mass, DCAs, extras and PID + using FemtoTracks = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + + using FemtoLambdas = o2::soa::Join; + using FemtoLambdasWithLabel = o2::soa::Join; + using FemtoK0shorts = o2::soa::Join; + + using FemtoMcParticles = o2::soa::Join; + + o2::framework::SliceCache cache; + o2::framework::Service pdg = {}; + + // setup dndeta + dndetabuilder::ConfDndeta confDndeta; + dndetabuilder::ConfDndetaAcceptance confDndetaAcceptance; + dndetahistmanager::ConfDndetaBinning confDndetaBinning; + dndetabuilder::DndetaBuilder dndetaBuilder; + + // pass-through mode does not guarantee fMcColId is written in sorted order (reco-driven label + // rows can be created out of mc-collision order), so this must be an unsorted grouping + o2::framework::PresliceUnsorted perMcColParticles = o2::aod::femtomcparticle::fMcColId; + + // setup collisions (applied in the dndeta builder, no filter, so the cutflow sees every collision) + collisionbuilder::ConfCollisionSelection collisionSelection; + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup tracks for dndeta (bitmask with track quality bits only, see dndetaBuilder.h) + trackbuilder::ConfTrackSelectionDndetaGlobal confDndetaTracksGlobal; + trackhistmanager::ConfDndetaTrackGlobalBinning confDndetaTrackGlobalBinning; + trackhistmanager::ConfDndetaTrackGlobalQaBinning confDndetaTrackGlobalQaBinning; + + trackbuilder::ConfTrackSelectionDndetaItsOnly confDndetaTracksItsOnly; + trackhistmanager::ConfDndetaTrackItsOnlyBinning confDndetaTrackItsOnlyBinning; + trackhistmanager::ConfDndetaTrackItsOnlyQaBinning confDndetaTrackItsOnlyQaBinning; + + o2::framework::Partition dndetaTrackPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); + o2::framework::Partition dndetaTrackPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + + o2::framework::Partition dndetaTrackWithLabelPartitionGlobal = MAKE_TRACK_PARTITION(confDndetaTracksGlobal); + o2::framework::Partition dndetaTrackWithLabelPartitionItsOnly = MAKE_TRACK_PARTITION(confDndetaTracksItsOnly); + + // the *Mc process functions take the ungrouped tables (no ::iterator first argument), so DPL never + // auto-registers the fColId slicing cache for FemtoTracksWithLabel; this unused Preslice is the only + // thing that requests/enables it, otherwise sliceByCached() throws "Disabled cache ... is requested" + // once the first accepted reco collision actually reaches it + o2::framework::Preslice perColTracksWithLabel = o2::aod::femtobase::stored::fColId; + + // setup tracks (trigger) + trackbuilder::ConfTrackSelection1 confTrackSelection1; + trackhistmanager::ConfTrackBinning1 confTrackBinning1; + particlecleaner::ConfTrackCleaner1 confTrackCleaner1; + + trackbuilder::ConfTrackSelection2 confTrackSelection2; + trackhistmanager::ConfTrackBinning2 confTrackBinning2; + particlecleaner::ConfTrackCleaner2 confTrackCleaner2; + + trackbuilder::ConfTrackSelection3 confTrackSelection3; + trackhistmanager::ConfTrackBinning3 confTrackBinning3; + particlecleaner::ConfTrackCleaner3 confTrackCleaner3; + + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(confTrackSelection1); + o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(confTrackSelection2); + o2::framework::Partition trackPartition3 = MAKE_TRACK_PARTITION(confTrackSelection3); + + o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelection1); + o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelection2); + o2::framework::Partition trackWithLabelPartition3 = MAKE_TRACK_PARTITION(confTrackSelection3); + + // setup for daughters + trackhistmanager::ConfV01PosDauBinning confPosDauBinning; + trackhistmanager::ConfV01NegDauBinning confNegDauBinning; + + // setup lambdas (trigger) + v0builder::ConfLambdaSelection1 confLambdaSelection; + v0histmanager::ConfLambdaBinning1 confLambdaBinning; + particlecleaner::ConfLambdaCleaner1 confLambdaCleaner; + + o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + // same reason as perColTracksWithLabel above, needed by processTrackTrackV0Mc + o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; + + // setup strangeness yields (dedicated selections, independent of the trigger) + v0builder::ConfLambdaSelectionStrangeness confStrangeLambdaSelection; + v0builder::ConfK0shortSelectionStrangeness confStrangeK0shortSelection; + + o2::framework::Partition strangeLambdaPartition = MAKE_LAMBDA_PARTITION(confStrangeLambdaSelection); + o2::framework::Partition strangeK0shortPartition = MAKE_K0SHORT_PARTITION(confStrangeK0shortSelection); + + // setup mc particles (generator-level trigger) + // FMcParticles also contains rows of reconstructed secondaries (created via labels), so set + // McParticleSelection1/2/3.requireOrigin = true (origin = 2, physical primary) for an unbiased generator-level trigger + mcbuilder::ConfMcParticleSelection1 confMcParticleSelection1; + mcparticlehistmanager::ConfMcParticleBinning1 confMcParticleBinning1; + particlecleaner::ConfMcParticleCleaner1 confMcParticleCleaner1; + + mcbuilder::ConfMcParticleSelection2 confMcParticleSelection2; + mcparticlehistmanager::ConfMcParticleBinning2 confMcParticleBinning2; + particlecleaner::ConfMcParticleCleaner2 confMcParticleCleaner2; + + mcbuilder::ConfMcParticleSelection3 confMcParticleSelection3; + mcparticlehistmanager::ConfMcParticleBinning3 confMcParticleBinning3; + particlecleaner::ConfMcParticleCleaner3 confMcParticleCleaner3; + + o2::framework::Partition mcParticlePartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); + o2::framework::Partition mcParticlePartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); + o2::framework::Partition mcParticlePartition3 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection3); + + // setup triplets + triplethistmanager::ConfTripletBinning confTripletBinning; + triplethistmanager::ConfTripletCuts confTripletCuts; + paircleaner::ConfPairCleanerBinning confTripletCleanerBinning; + triplethistmanager::ConfMixing confMixing; // only same event is processed, used for particle12(3)AreSameSpecies/seed + + closetripletrejection::ConfCtrTrackTrackTrack confCtrTrackTrackTrack; + closetripletrejection::ConfCtrTrackTrackV0 confCtrTrackTrackV0; + closetripletrejection::ConfCtrMcParticleMcParticleMcParticle confCtrMcParticleMcParticleMcParticle; // generator-level trigger, no cut by default + + tripletbuilder::TripletTrackTrackTrackBuilder< + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack3Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + trackhistmanager::PrefixTrack3Me, + triplethistmanager::PrefixTrackTrackTrackSe, + triplethistmanager::PrefixTrackTrackTrackMe, + closetripletrejection::PrefixTrack1Track2Se, + closetripletrejection::PrefixTrack2Track3Se, + closetripletrejection::PrefixTrack1Track3Se, + closetripletrejection::PrefixTrack1Track2Me, + closetripletrejection::PrefixTrack2Track3Me, + closetripletrejection::PrefixTrack1Track3Me> + tripletTrackTrackTrackBuilder; + + tripletbuilder::TripletTrackTrackV0Builder< + modes::V0::kLambda, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + triplethistmanager::PrefixTrackTrackLambdaSe, + triplethistmanager::PrefixTrackTrackLambdaMe, + closetripletrejection::PrefixTrack1Track2Se, + closetripletrejection::PrefixTrack1V0Se, + closetripletrejection::PrefixTrack2V0Se, + closetripletrejection::PrefixTrack1Track2Me, + closetripletrejection::PrefixTrack1V0Me, + closetripletrejection::PrefixTrack2V0Me> + tripletTrackTrackLambdaBuilder; + + tripletbuilder::TripletMcParticleMcParticleMcParticleBuilder< + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle3Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + mcparticlehistmanager::PrefixMcParticle3Me, + triplethistmanager::PrefixMcParticleMcParticleMcParticleSe, + triplethistmanager::PrefixMcParticleMcParticleMcParticleMe, + closetripletrejection::PrefixMcParticle1McParticle2Se, + closetripletrejection::PrefixMcParticle2McParticle3Se, + closetripletrejection::PrefixMcParticle1McParticle3Se, + closetripletrejection::PrefixMcParticle1McParticle2Me, + closetripletrejection::PrefixMcParticle2McParticle3Me, + closetripletrejection::PrefixMcParticle1McParticle3Me> + tripletMcParticleMcParticleMcParticleBuilder; + + // dndeta histograms and trigger QA are kept in separate registries, so the fixed histogram names of the builders cannot clash + o2::framework::HistogramRegistry hRegistry{"FemtoDndetaTriplet", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistryTrigger{"FemtoDndetaTripletTrigger", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistryGenTrigger{"FemtoDndetaTripletGenTrigger", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + const bool processDataWithStrangeness = doprocessTrackTrackTrackWithStrangeness || doprocessTrackTrackV0WithStrangeness; + const bool processMc = doprocessTrackTrackTrackMc || doprocessTrackTrackV0Mc; + const int nProcess = static_cast(doprocessTrackTrackTrack) + static_cast(doprocessTrackTrackV0) + + static_cast(doprocessTrackTrackTrackWithStrangeness) + static_cast(doprocessTrackTrackV0WithStrangeness) + + static_cast(doprocessTrackTrackTrackMc) + static_cast(doprocessTrackTrackV0Mc); + if (nProcess != 1) { + LOG(fatal) << "Exactly one process function has to be activated (found " << nProcess << "). Breaking..."; + } + + const modes::Trigger trigger = (doprocessTrackTrackTrack || doprocessTrackTrackTrackWithStrangeness || doprocessTrackTrackTrackMc) ? modes::Trigger::kTrackTrackTrack : modes::Trigger::kTrackTrackV0; + LOG(info) << "Trigger: " << modes::triggerToString(trigger); + + dndetaBuilder.init(&hRegistry, confDndeta, confDndetaAcceptance, collisionSelection, + confDndetaTracksGlobal, confDndetaTrackGlobalBinning, confDndetaTrackGlobalQaBinning, + confDndetaTracksItsOnly, confDndetaTrackItsOnlyBinning, confDndetaTrackItsOnlyQaBinning, + confDndetaBinning, processMc, processDataWithStrangeness); + if (dndetaBuilder.useGenTrigger() && !processMc) { + LOG(warn) << "Dndeta.useGenTrigger is only used for mc processing. Ignoring it."; + } + + // setup histogram specs + std::map> colHistSpec; + std::map> trackHistSpec1; + std::map> trackHistSpec2; + std::map> trackHistSpec3; + std::map> posDauSpec; + std::map> negDauSpec; + std::map> lambdaHistSpec; + std::map> tripletHistSpec; + std::map> tripletCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confTripletCleanerBinning); + + if (!processMc) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); + trackHistSpec3 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning3); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + tripletHistSpec = triplethistmanager::makeTripletHistSpecMap(confTripletBinning, confMixing); + } else { + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); + trackHistSpec3 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning3); + posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); + lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); + tripletHistSpec = triplethistmanager::makeTripletMcHistSpecMap(confTripletBinning, confMixing); + } + + // only the builder of the active trigger is initialized + // only same event processing is used; the mixed event histograms are booked but stay empty + if (trigger == modes::Trigger::kTrackTrackTrack) { + std::map> ctrHistSpec = closepairrejection::makeCprHistSpecMap(confCtrTrackTrackTrack); + if (!processMc) { + tripletTrackTrackTrackBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confTrackSelection3, confTrackCleaner1, confTrackCleaner2, confTrackCleaner3, confCtrTrackTrackTrack, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, trackHistSpec3, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); + } else { + tripletTrackTrackTrackBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confTrackSelection3, confTrackCleaner1, confTrackCleaner2, confTrackCleaner3, confCtrTrackTrackTrack, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, trackHistSpec3, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); + } + } else { + std::map> ctrHistSpec = closepairrejection::makeCprHistSpecMap(confCtrTrackTrackV0); + if (!processMc) { + tripletTrackTrackLambdaBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confLambdaSelection, confTrackCleaner1, confTrackCleaner2, confLambdaCleaner, confCtrTrackTrackV0, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, lambdaHistSpec, posDauSpec, negDauSpec, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); + } else { + tripletTrackTrackLambdaBuilder.init(&hRegistryTrigger, confCollisionBinning, confTrackSelection1, confTrackSelection2, confLambdaSelection, confTrackCleaner1, confTrackCleaner2, confLambdaCleaner, confCtrTrackTrackV0, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, lambdaHistSpec, posDauSpec, negDauSpec, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); + } + } + + // generator-level trigger; the species come from McParticleSelection1/2/3, so it covers + // both triplet triggers (a lambda is just another mc particle) + if (processMc && dndetaBuilder.useGenTrigger()) { + std::map> genColHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + std::map> mcParticleHistSpec1 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning1); + std::map> mcParticleHistSpec2 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning2); + std::map> mcParticleHistSpec3 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning3); + std::map> genTripletHistSpec = triplethistmanager::makeTripletMcTruthHistSpecMap(confTripletBinning, confMixing); + std::map> genCtrHistSpec = closepairrejection::makeCprHistSpecMap(confCtrMcParticleMcParticleMcParticle); + tripletMcParticleMcParticleMcParticleBuilder.init(&hRegistryGenTrigger, confCollisionBinning, confMcParticleSelection1, confMcParticleSelection2, confMcParticleSelection3, confMcParticleBinning1, confMcParticleBinning2, confMcParticleBinning3, confMcParticleCleaner1, confMcParticleCleaner2, confMcParticleCleaner3, confCtrMcParticleMcParticleMcParticle, confMixing, confTripletBinning, confTripletCuts, genColHistSpec, mcParticleHistSpec1, mcParticleHistSpec2, mcParticleHistSpec3, genTripletHistSpec, genCtrHistSpec, tripletCleanerHistSpec); + } + + hRegistry.print(); + hRegistryTrigger.print(); + hRegistryGenTrigger.print(); + }; + + /// generator-level triplet trigger, or always true if not configured + template + bool evaluateGenTrigger(T1 const& mcCol, T2 const& mcParticles, T3 const& mcMothers, T4 const& mcPartonicMothers) + { + if (!dndetaBuilder.useGenTrigger()) { + return true; + } + return tripletMcParticleMcParticleMcParticleBuilder.processSameEvent(mcCol, mcParticles, mcMothers, mcPartonicMothers, mcParticlePartition1, mcParticlePartition2, mcParticlePartition3, cache); + } + + template + void fillStrangeness(T const& col) + { + auto lambdas = strangeLambdaPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + auto k0shorts = strangeK0shortPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + dndetaBuilder.processStrangeness(col, lambdas, k0shorts); + } + + // data + void processTrackTrackTrack(FemtoCollision const& col, FemtoTracks const& tracks) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return tripletTrackTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, trackPartition3, cache); + }); + } + PROCESS_SWITCH(FemtoDndetaTriplet, processTrackTrackTrack, "Data, track-track-track trigger", true); + + void processTrackTrackV0(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) + { + dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return tripletTrackTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, lambdaPartition, cache); + }); + } + PROCESS_SWITCH(FemtoDndetaTriplet, processTrackTrackV0, "Data, track-track-lambda trigger", false); + + // data with strangeness yields + void processTrackTrackTrackWithStrangeness(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return tripletTrackTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, trackPartition3, cache); + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaTriplet, processTrackTrackTrackWithStrangeness, "Data with strangeness yields, track-track-track trigger", false); + + void processTrackTrackV0WithStrangeness(FemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + if (dndetaBuilder.processData(col, dndetaTrackPartitionGlobal, dndetaTrackPartitionItsOnly, cache, [&]() { + return tripletTrackTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, lambdaPartition, cache); + })) { + fillStrangeness(col); + } + } + PROCESS_SWITCH(FemtoDndetaTriplet, processTrackTrackV0WithStrangeness, "Data with strangeness yields, track-track-lambda trigger", false); + + // mc + void processTrackTrackTrackMc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [&](auto const& col) { + return tripletTrackTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, trackWithLabelPartition3, mcParticles, mcMothers, mcPartonicMothers, cache); + }, + [&](auto const& mcCol) { return evaluateGenTrigger(mcCol, mcParticles, mcMothers, mcPartonicMothers); }); + } + PROCESS_SWITCH(FemtoDndetaTriplet, processTrackTrackTrackMc, "MC, track-track-track trigger", false); + + void processTrackTrackV0Mc(FemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + dndetaBuilder.processMc( + cols, mcCols, dndetaTrackWithLabelPartitionGlobal, dndetaTrackWithLabelPartitionItsOnly, cache, mcParticles, mcMothers, perMcColParticles, pdg, + [&](auto const& col) { + return tripletTrackTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + }, + [&](auto const& mcCol) { return evaluateGenTrigger(mcCol, mcParticles, mcMothers, mcPartonicMothers); }); + } + PROCESS_SWITCH(FemtoDndetaTriplet, processTrackTrackV0Mc, "MC, track-track-lambda trigger", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoKinkQa.cxx b/PWGCF/Femto/Tasks/femtoKinkQa.cxx index 7b0c17d210a..e89ed4926dc 100644 --- a/PWGCF/Femto/Tasks/femtoKinkQa.cxx +++ b/PWGCF/Femto/Tasks/femtoKinkQa.cxx @@ -43,7 +43,7 @@ using namespace o2::analysis::femto; struct FemtoKinkQa { // setup tables - using FemtoCollisions = o2::soa::Join; + using FemtoCollisions = o2::soa::Join; using FilteredFemtoCollisions = o2::soa::Filtered; using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; @@ -60,6 +60,8 @@ struct FemtoKinkQa { using FemtoSigmaPlusWithLabel = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup for collisions @@ -99,7 +101,7 @@ struct FemtoKinkQa { o2::framework::Partition sigmaPlusPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaPlusSelection); o2::framework::Preslice perColSigmaPlus = o2::aod::femtobase::stored::fColId; - o2::framework::Partition sigmaPlusWithLabelPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaSelection); + o2::framework::Partition sigmaPlusWithLabelPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaPlusSelection); o2::framework::Preslice perColSigmaPlussWithLabel = o2::aod::femtobase::stored::fColId; kinkhistmanager::ConfSigmaPlusBinning1 confSigmaPlusBinning; @@ -118,7 +120,7 @@ struct FemtoKinkQa { void init(o2::framework::InitContext&) { - if ((doprocessSigma + doprocessSigmaMc + doprocessSigmaPlus + doprocessSigmaPlusMc) > 1) { + if ((static_cast(doprocessSigma) + static_cast(doprocessSigmaMc) + static_cast(doprocessSigmaPlus) + static_cast(doprocessSigmaPlusMc)) > 1) { LOG(fatal) << "Only one process can be activated"; } @@ -134,27 +136,27 @@ struct FemtoKinkQa { if (processData) { colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); chaDauHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confKinkChaDaughterBinning, confKinkChaDaughterQaBinning); if (doprocessSigma) { sigmaHistSpec = kinkhistmanager::makeKinkQaHistSpecMap(confSigmaBinning, confSigmaQaBinning); - sigmaHistManager.init(&hRegistry, sigmaHistSpec, confSigmaSelection, confSigmaQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); + sigmaHistManager.init(&hRegistry, sigmaHistSpec, confSigmaSelection, confSigmaQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); } if (doprocessSigmaPlus) { sigmaPlusHistSpec = kinkhistmanager::makeKinkQaHistSpecMap(confSigmaPlusBinning, confSigmaPlusQaBinning); - sigmaPlusHistManager.init(&hRegistry, sigmaPlusHistSpec, confSigmaPlusSelection, confSigmaPlusQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); + sigmaPlusHistManager.init(&hRegistry, sigmaPlusHistSpec, confSigmaPlusSelection, confSigmaPlusQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); } } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); chaDauHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confKinkChaDaughterBinning, confKinkChaDaughterQaBinning); if (doprocessSigmaMc) { sigmaHistSpec = kinkhistmanager::makeKinkMcQaHistSpecMap(confSigmaBinning, confSigmaQaBinning); - sigmaHistManager.init(&hRegistry, sigmaHistSpec, confSigmaSelection, confSigmaQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); + sigmaHistManager.init(&hRegistry, sigmaHistSpec, confSigmaSelection, confSigmaQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); } if (doprocessSigmaPlusMc) { sigmaPlusHistSpec = kinkhistmanager::makeKinkMcQaHistSpecMap(confSigmaPlusBinning, confSigmaPlusQaBinning); - sigmaPlusHistManager.init(&hRegistry, sigmaPlusHistSpec, confSigmaPlusSelection, confSigmaPlusQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); + sigmaPlusHistManager.init(&hRegistry, sigmaPlusHistSpec, confSigmaPlusSelection, confSigmaPlusQaBinning, chaDauHistSpec, confKinkChaDaughterQaBinning); } } hRegistry.print(); @@ -166,25 +168,25 @@ struct FemtoKinkQa { if (sigmaSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& sigma : sigmaSlice) { - sigmaHistManager.fill(sigma, tracks); + sigmaHistManager.fill(sigma, tracks); } } PROCESS_SWITCH(FemtoKinkQa, processSigma, "Process sigmas", true); - void processSigmaMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmasWithLabel const& /*sigmas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSigmaMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmasWithLabel const& /*sigmas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { auto sigmaSlice = sigmaWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (sigmaSlice.size() == 0) { return; } - colHistManager.fill(col, mcCols); + colHistManager.fill(col, mcCols); for (auto const& sigma : sigmaSlice) { if (!sigmaCleaner.isClean(sigma, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - sigmaHistManager.fill(sigma, tracks, mcParticles, mcMothers, mcPartonicMothers); + sigmaHistManager.fill(sigma, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoKinkQa, processSigmaMc, "Process sigmas", false); @@ -195,34 +197,34 @@ struct FemtoKinkQa { if (sigmaPlusSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& sp : sigmaPlusSlice) { - sigmaPlusHistManager.fill(sp, tracks); + sigmaPlusHistManager.fill(sp, tracks); } } PROCESS_SWITCH(FemtoKinkQa, processSigmaPlus, "Process sigma plus", false); - void processSigmaPlusMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusWithLabel const& /*sigmaPlus*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSigmaPlusMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusWithLabel const& /*sigmaPlus*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { auto sigmaPlusSlice = sigmaPlusWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (sigmaPlusSlice.size() == 0) { return; } - colHistManager.fill(col, mcCols); + colHistManager.fill(col, mcCols); for (auto const& sigmaPlus : sigmaPlusSlice) { if (!sigmaPlusCleaner.isClean(sigmaPlus, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - sigmaPlusHistManager.fill(sigmaPlus, tracks, mcParticles, mcMothers, mcPartonicMothers); + sigmaPlusHistManager.fill(sigmaPlus, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoKinkQa, processSigmaPlusMc, "Process sigmas", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx new file mode 100644 index 00000000000..ca7560853da --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoMcParticleQa.cxx @@ -0,0 +1,116 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoMcParticleQa.cxx +/// \brief QA task for tracks +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/mcBuilder.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoMcParticleQa { + + // setup tables + using FemtoMcCollisions = o2::aod::FMcCols; + using FilteredFemtoMcCollisions = o2::soa::Filtered; + using FilteredFemtoMcCollision = FilteredFemtoMcCollisions::iterator; + + using FemtoMcParticles = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + mcbuilder::ConfMcCollisionFilters collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_MC_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::CollisionHistManager colHistManager; + + // setup mc particles + mcbuilder::ConfMcParticleSelection1 confMcParticleSelection1; + mcparticlehistmanager::ConfMcParticleBinning1 confMcParticleBinning1; + mcparticlehistmanager::McParticleHistManager mcParticleHistManager1; + + o2::framework::Partition mcParticlesPartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); + o2::framework::PresliceUnsorted perColReco = o2::aod::femtomcparticle::fMcColId; + + particlecleaner::ConfMcParticleCleaner1 confMcParticleCleaner1; + particlecleaner::ParticleCleaner mcParticleCleaner; + + o2::framework::HistogramRegistry hRegistry{"FemtoMcParticleQA", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + mcParticleCleaner.init(confMcParticleCleaner1); + + std::map> colHistSpec; + std::map> mcParticleHistSpec; + + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning); + mcParticleHistSpec = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning1); + mcParticleHistManager1.init(&hRegistry, mcParticleHistSpec, confMcParticleBinning1); + + hRegistry.print(); + }; + + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() below (see the same workaround in femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); + // FemtoMcParticles must still appear as an argument (even though it's only accessed via the partition) + // so DPL actually subscribes to it -- dropping it entirely leaves mcParticlesPartition1 unbound and + // segfaults on the first sliceByCachedUnsorted() call + void process(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& /*mcParticles*/, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + for (auto const& col : cols) { + auto mcParticleSlice = mcParticlesPartition1->sliceByCachedUnsorted(o2::aod::femtomcparticle::fMcColId, col.globalIndex(), cache); + if (mcParticleSlice.size() == 0) { + continue; + } + colHistManager.fill(col); + for (auto const& mcParticle : mcParticleSlice) { + if (!mcParticleCleaner.isClean(mcParticle, mcMothers, mcPartonicMothers)) { + continue; + } + mcParticleHistManager1.fill(mcParticle, mcMothers, mcPartonicMothers); + } + } + } +}; + +o2::framework::WorkflowSpec + defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx new file mode 100644 index 00000000000..6b433496642 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -0,0 +1,230 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairD0D0.cxx +/// \brief Tasks that computes correlation between two D0 mesons +/// \author Igor Ptak, WUT, igor.tomasz.ptak@cern.ch + +#include "PWGCF/Femto/Core/charmHadronBuilder.h" +#include "PWGCF/Femto/Core/charmHadronHistManager.h" +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +// Each pair slot (1 and 2) has its own selection, cleaner, binning and partition. +// The sign of the selection picks the decay hypothesis: +// d0-d0 : D0Selection1.sign = +1, D0Selection2.sign = +1, Mixing.sameSpecies = true +// d0bar-d0bar : D0Selection1.sign = -1, D0Selection2.sign = -1, Mixing.sameSpecies = true +// d0-d0bar : D0Selection1.sign = +1, D0Selection2.sign = -1, Mixing.sameSpecies = false +struct FemtoPairD0D0 { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoCollisionsWithLabel = o2::soa::Join; + using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; + using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + + using FemtoTracks = o2::aod::FTracks; + using FemtoD0s = o2::soa::Join; + + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoD0sWithLabel = o2::soa::Join; + + using FemtoMcParticlesWithLabel = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for daughters + // separate binning per d0 slot, so that both hypotheses can be binned independently + trackhistmanager::ConfD01PosDauBinning confD01PosDauBinning; + trackhistmanager::ConfD01NegDauBinning confD01NegDauBinning; + trackhistmanager::ConfD02PosDauBinning confD02PosDauBinning; + trackhistmanager::ConfD02NegDauBinning confD02NegDauBinning; + + // setup d0s + charmhadronbuilder::ConfD0Selection1 confD0Selection1; + charmhadronbuilder::ConfD0Selection2 confD0Selection2; + particlecleaner::ConfD0Cleaner1 confD0Cleaner1; + particlecleaner::ConfD0Cleaner2 confD0Cleaner2; + charmhadronhistmanager::ConfD0Binning1 confD0Binning1; + charmhadronhistmanager::ConfD0Binning2 confD0Binning2; + + o2::framework::Partition d0Partition1 = MAKE_D0_PARTITION(confD0Selection1); + o2::framework::Partition d0Partition2 = MAKE_D0_PARTITION(confD0Selection2); + o2::framework::Preslice perColD0s = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition d0WithLabelPartition1 = MAKE_D0_PARTITION(confD0Selection1); + o2::framework::Partition d0WithLabelPartition2 = MAKE_D0_PARTITION(confD0Selection2); + o2::framework::Preslice perColD0sWithLabel = o2::aod::femtobase::stored::fColId; + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + + pairbuilder::PairD0D0Builder< + charmhadronhistmanager::PrefixD01Se, + trackhistmanager::PrefixD01PosDaughterSe, + trackhistmanager::PrefixD01NegDaughterSe, + charmhadronhistmanager::PrefixD02Se, + trackhistmanager::PrefixD02PosDaughterSe, + trackhistmanager::PrefixD02NegDaughterSe, + charmhadronhistmanager::PrefixD01Me, + trackhistmanager::PrefixD01PosDaughterMe, + trackhistmanager::PrefixD01NegDaughterMe, + charmhadronhistmanager::PrefixD02Me, + trackhistmanager::PrefixD02PosDaughterMe, + trackhistmanager::PrefixD02NegDaughterMe, + pairhistmanager::PrefixD0D0Se, + pairhistmanager::PrefixD0D0Me, + closepairrejection::PrefixD0D0PosSe, + closepairrejection::PrefixD0D0NegSe, + closepairrejection::PrefixD0D0PosMe, + closepairrejection::PrefixD0D0NegMe, + modes::CharmHadron::kD0, + modes::CharmHadron::kD0> + pairD0D0Builder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + pairhistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoD0D0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + // setup cpr + closepairrejection::ConfCprD0DaugherD0DaughterPos confCprPos; + closepairrejection::ConfCprD0DaugherD0DaughterNeg confCprNeg; + + void init(o2::framework::InitContext&) + { + bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processMc = doprocessSameEventMc || doprocessMixedEventMc; + + if (processData && processMc) { + LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; + } + if (!processData && !processMc) { + LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; + } + + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histograms + std::map> colHistSpec; + std::map> posDauSpec1; + std::map> negDauSpec1; + std::map> posDauSpec2; + std::map> negDauSpec2; + std::map> d0HistSpec1; + std::map> d0HistSpec2; + std::map> pairD0D0HistSpec; + std::map> cprHistSpecPos = closepairrejection::makeCprHistSpecMap(confCprPos); + std::map> cprHistSpecNeg = closepairrejection::makeCprHistSpecMap(confCprNeg); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + if (processData) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + posDauSpec1 = trackhistmanager::makeTrackHistSpecMap(confD01PosDauBinning); + negDauSpec1 = trackhistmanager::makeTrackHistSpecMap(confD01NegDauBinning); + posDauSpec2 = trackhistmanager::makeTrackHistSpecMap(confD02PosDauBinning); + negDauSpec2 = trackhistmanager::makeTrackHistSpecMap(confD02NegDauBinning); + d0HistSpec1 = charmhadronhistmanager::makeCharmHadronHistSpecMap(confD0Binning1); + d0HistSpec2 = charmhadronhistmanager::makeCharmHadronHistSpecMap(confD0Binning2); + pairD0D0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairD0D0Builder.init(&hRegistry, confCollisionBinning, confD0Selection1, confD0Selection2, confD0Cleaner1, confD0Cleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, d0HistSpec1, d0HistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairD0D0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } else { + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + posDauSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confD01PosDauBinning); + negDauSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confD01NegDauBinning); + posDauSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confD02PosDauBinning); + negDauSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confD02NegDauBinning); + d0HistSpec1 = charmhadronhistmanager::makeCharmHadronMcHistSpecMap(confD0Binning1); + d0HistSpec2 = charmhadronhistmanager::makeCharmHadronMcHistSpecMap(confD0Binning2); + pairD0D0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); + pairD0D0Builder.init(&hRegistry, confCollisionBinning, confD0Selection1, confD0Selection2, confD0Cleaner1, confD0Cleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, d0HistSpec1, d0HistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairD0D0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + hRegistry.print(); + } + + void processSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoD0s const& d0s) + { + pairD0D0Builder.processSameEvent(col, tracks, d0s, d0Partition1, d0Partition2, cache); + } + PROCESS_SWITCH(FemtoPairD0D0, processSameEvent, "Enable processing same event processing for d0-d0", true); + + void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoD0sWithLabel const& d0s, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairD0D0Builder.processSameEvent(col, mcCols, tracks, d0s, d0WithLabelPartition1, d0WithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoPairD0D0, processSameEventMc, "Enable processing same event processing for d0-d0 with mc information", false); + + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + pairD0D0Builder.processMixedEvent(cols, tracks, d0Partition1, d0Partition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairD0D0, processMixedEvent, "Enable processing mixed event processing for d0-d0", true); + + void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoD0sWithLabel const& /*d0s*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairD0D0Builder.processMixedEvent(cols, mcCols, tracks, d0WithLabelPartition1, d0WithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairD0D0, processMixedEventMc, "Enable processing mixed event processing for d0-d0 with mc information", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairEfficiency.cxx b/PWGCF/Femto/Tasks/femtoPairEfficiency.cxx new file mode 100644 index 00000000000..d3cffb80f0b --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairEfficiency.cxx @@ -0,0 +1,875 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairEfficiency.cxx +/// \brief task for analysis of charged-particle pseudorapidity density at midrapidity in pp collisions triggered with a paritcle pair +/// \author Anton Riedel (anton.riedel@tum.ch) + +#include "PWGCF/Femto/Core/femtoUtils.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using o2::framework::Configurable; +using std::string; + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::track; +using namespace o2::aod::evsel; + +using CollisionDataTable = soa::Join; +using TrackDataTable = soa::Join; +using FilTrackDataTable = soa::Filtered; +using CollisionMCTrueTable = aod::McCollisions; +using TrackMCTrueTable = aod::McParticles; +using CollisionMCRecTable = soa::SmallGroups>; +using TrackMCRecTable = soa::Join; +using FilTrackMCRecTable = soa::Filtered; +using V0TrackCandidates = soa::Join; + +enum { + kTrackTypebegin = 0, + kGlobalplusITS = 1, + kGlobalonly, + kITSonly, + kTrackTypeend +}; + +enum { + kGenpTbegin = 0, + kNoGenpTVar = 1, + kGenpTup, + kGenpTdown, + kGenpTend +}; + +enum { + kGenTrkTypebegin = 0, + kGenAll = 1, + kGenPion, + kGenKaon, + kGenProton, + kGenOther, + kGenTrkTypeend +}; + +enum { + kRecTrkTypebegin = 0, + kRecoAll = 1, + kRecoPion, + kRecoKaon, + kRecoProton, + kRecoOther, + kRecoSecondary, + kRecoWeakDecay, + kRecoFake, + kRecoBkg, + kRecTrkTypeend +}; + +static constexpr TrackSelectionFlags::flagtype TrackSelectionIts = + TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | + TrackSelectionFlags::kITSHits; +static constexpr TrackSelectionFlags::flagtype TrackSelectionTpc = + TrackSelectionFlags::kTPCNCls | + TrackSelectionFlags::kTPCCrossedRowsOverNCls | + TrackSelectionFlags::kTPCChi2NDF; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = + TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = + TrackSelectionFlags::kDCAxy; + +AxisSpec axisEvent{15, 0.5, 15.5, "#Event", "EventAxis"}; +AxisSpec axisVtxZ{40, -20, 20, "Vertex Z", "VzAxis"}; +AxisSpec axisEta{40, -2, 2, "#eta", "EtaAxis"}; +AxisSpec axisPhi{{0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf, o2::constants::math::PIQuarter * 3., o2::constants::math::PI, o2::constants::math::PIQuarter * 5., o2::constants::math::PIHalf * 3., o2::constants::math::PIQuarter * 7., o2::constants::math::TwoPI}, "#phi", "PhiAxis"}; +AxisSpec axisPhi2{629, 0, o2::constants::math::TwoPI, "#phi"}; +AxisSpec axisCent{100, 0, 100, "#Cent"}; +AxisSpec axisTrackType = {kTrackTypeend - 1, +kTrackTypebegin + 0.5, +kTrackTypeend - 0.5, "", "TrackTypeAxis"}; +AxisSpec axisGenPtVary = {kGenpTend - 1, +kGenpTbegin + 0.5, +kGenpTend - 0.5, "", "GenpTVaryAxis"}; +AxisSpec axisGenTrkType = {kGenTrkTypeend - 1, +kGenTrkTypebegin + 0.5, +kGenTrkTypeend - 0.5, "", "GenTrackTypeAxis"}; +AxisSpec axisRecTrkType = {kRecTrkTypeend - 1, +kRecTrkTypebegin + 0.5, +kRecTrkTypeend - 0.5, "", "RecTrackTypeAxis"}; +AxisSpec axisMassK0s = {200, 0.4, 0.6, "K0sMass", "K0sMass"}; +AxisSpec axisMassLambda = {200, 1.07, 1.17, "Lambda/AntiLamda Mass", "Lambda/AntiLamda Mass"}; +auto static constexpr KminCharge = 3.f; +auto static constexpr KminPtCut = 0.1f; + +struct FemtoPairEfficiency { + + o2::framework::SliceCache cache; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service pdg; + Preslice perCollision = aod::track::collisionId; + + Configurable etaRange{"etaRange", 1.0f, "Eta range to consider"}; + Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; + Configurable dcaZ{"dcaZ", 0.2f, "Custom DCA Z cut (ignored if negative)"}; + Configurable v0radiusK0SCut{"v0radiusK0SCut", 1.2f, "K0S RadiusCut"}; + Configurable dcapostopvK0SCut{"dcapostopvK0SCut", 0.05f, "K0S dcapostopvCut"}; + Configurable dcanegtopvK0SCut{"dcanegtopvK0SCut", 0.05f, "K0S dcanegtopvCut"}; + Configurable v0cospaK0SCut{"v0cospaK0SCut", 0.995f, "K0S v0cospaCut"}; + Configurable dcav0daughterK0Scut{"dcav0daughterK0Scut", 1.0f, "K0S dcav0daughtercut"}; + Configurable minTPCnClsK0SCut{"minTPCnClsK0SCut", 50.0f, "K0S minTPCnClsCut"}; + Configurable nSigmaTpcK0SCut{"nSigmaTpcK0SCut", 5.0f, "K0S nSigmaTpcCut"}; + Configurable v0etaK0SCut{"v0etaK0SCut", 0.9f, "K0S v0etaCut"}; + Configurable v0radiusLambdaCut{"v0radiusLambdaCut", 1.2f, "Lambda RadiusCut"}; + Configurable dcapostopvLambdaCut{"dcapostopvLambdaCut", 0.05f, "Lambda dcapostopvCut"}; + Configurable dcanegtopvLambdaCut{"dcanegtopvLambdaCut", 0.05f, "Lambda dcanegtopvCut"}; + Configurable v0cospaLambdaCut{"v0cospaLambdaCut", 0.995f, "Lambda v0cospaCut"}; + Configurable dcav0daughterLambdacut{"dcav0daughterLambdacut", 1.0f, "Lambda dcav0daughtercut"}; + Configurable minTPCnClsLambdaCut{"minTPCnClsLambdaCut", 50.0f, "Lambda minTPCnClsCut"}; + Configurable nSigmaTpcLambdaCut{"nSigmaTpcLambdaCut", 5.0f, "Lambda nSigmaTpcCut"}; + Configurable v0etaLambdaCut{"v0etaLambdaCut", 0.9f, "Lambda v0etaCut"}; + Configurable extraphicut1{"extraphicut1", 3.07666f, "Extra Phi cut 1"}; + Configurable extraphicut2{"extraphicut2", 3.12661f, "Extra Phi cut 2"}; + Configurable extraphicut3{"extraphicut3", 0.03f, "Extra Phi cut 3"}; + Configurable extraphicut4{"extraphicut4", 6.253f, "Extra Phi cut 4"}; + ConfigurableAxis multHistBin{"multHistBin", {501, -0.5, 500.5}, ""}; + ConfigurableAxis pvHistBin{"pvHistBin", {501, -0.5, 500.5}, ""}; + ConfigurableAxis fv0aMultHistBin{"fv0aMultHistBin", {501, -0.5, 500.5}, ""}; + ConfigurableAxis ft0aMultHistBin{"ft0aMultHistBin", {501, -0.5, 500.5}, ""}; + ConfigurableAxis ft0cMultHistBin{"ft0cMultHistBin", {501, -0.5, 500.5}, ""}; + ConfigurableAxis ptHistBin{"ptHistBin", {200, 0., 20.}, ""}; + ConfigurableAxis centralityBinning{"centralityBinning", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, ""}; + ConfigurableAxis binsMult{"binsMult", {500, 0.0f, +500.0f}, ""}; + ConfigurableAxis binsDCA{"binsDCA", {500, -10.0f, 10.0f}, ""}; + + Configurable isApplyTFcut{"isApplyTFcut", true, "Enable TimeFrameBorder cut"}; + Configurable isApplyITSROcut{"isApplyITSROcut", true, "Enable ITS ReadOutFrameBorder cut"}; + Configurable isApplySameBunchPileup{"isApplySameBunchPileup", true, "Enable SameBunchPileup cut"}; + Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", true, "Enable GoodZvtxFT0vsPV cut"}; + Configurable isApplyInelgt0{"isApplyInelgt0", false, "Enable INEL > 0 condition"}; + Configurable isApplyExtraPhiCut{"isApplyExtraPhiCut", false, "Enable extra phi cut"}; + Configurable isApplyPairCut{"isApplyPairCut", false, "Apply pair cut"}; + Configurable kStarMax{"kStarMax", 0.2, "k* threshold for pair cut"}; + + struct : ConfigurableGroup { + string prefix = string("TrackSel1"); + Configurable ptMin{"ptMin", 0.f, "Minimum pT of track"}; + Configurable ptMax{"ptMax", 999.f, "Maximum pT of track"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum |eta| of track"}; + Configurable tpcClustersMin{"tpcClustersMin", {90.f}, "Minimum number of clusters in TPC"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", {80.f}, "Minimum number of crossed rows in TPC"}; + Configurable tpcClustersOverCrossedRows{"tpcClustersOverCrossedRows", {0.83f}, "Minimum fraction of clusters over crossed rows in TPC"}; + Configurable tpcSharedClustersMax{"tpcSharedClustersMax", {160.f}, "Maximum number of shared clusters in TPC"}; + Configurable tpcSharedClusterFractionMax{"tpcSharedClusterFractionMax", {1.f}, "Maximum fraction of shared clusters in TPC"}; + Configurable itsClustersMin{"itsClustersMin", {5.f}, "Minimum number of clusters in ITS"}; + Configurable itsIbClustersMin{"itsIbClustersMin", {3.f}, "Minimum number of clusters in inner barrel (max 3) of ITS"}; + Configurable> dcaxyMax{"dcaxyMax", {0.004, 0.013, -1}, "Maximum |dca_xy| as a function of pT (v[0] + v[1]*pow(x, v[2])"}; + Configurable> dcazMax{"dcazMax", {0.004, 0.013, -1}, "Maximum |dca_z| as a function of pT (v[0] + v[1]*pow(x, v[2])"}; + Configurable pidThres{"pidThres", 0.8, "Minimum momentum to required TOF PID"}; + Configurable tpcNsigma{"tpcNsigma", 3, "Maximum NSigma"}; + Configurable tpctofNsigma{"tpctofNsigma", 3, "Maximum NSigma TPCTOF"}; + Configurable pdgCode{"pdgCode", 2212, "Pdg Code"}; + } TrackSel1; + + struct : ConfigurableGroup { + string prefix = string("TrackSel2"); + Configurable ptMin{"ptMin", 0.f, "Minimum pT of track"}; + Configurable ptMax{"ptMax", 999.f, "Maximum pT of track"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum |eta| of track"}; + Configurable tpcClustersMin{"tpcClustersMin", {90.f}, "Minimum number of clusters in TPC"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", {80.f}, "Minimum number of crossed rows in TPC"}; + Configurable tpcClustersOverCrossedRows{"tpcClustersOverCrossedRows", {0.83f}, "Minimum fraction of clusters over crossed rows in TPC"}; + Configurable tpcSharedClustersMax{"tpcSharedClustersMax", {160.f}, "Maximum number of shared clusters in TPC"}; + Configurable tpcSharedClusterFractionMax{"tpcSharedClusterFractionMax", {1.f}, "Maximum fraction of shared clusters in TPC"}; + Configurable itsClustersMin{"itsClustersMin", {5.f}, "Minimum number of clusters in ITS"}; + Configurable itsIbClustersMin{"itsIbClustersMin", {3.f}, "Minimum number of clusters in inner barrel (max 3) of ITS"}; + Configurable> dcaxyMax{"dcaxyMax", {0.004, 0.013, -1}, "Maximum |dca_xy| as a function of pT (v[0] + v[1]*pow(x, v[2])"}; + Configurable> dcazMax{"dcazMax", {0.004, 0.013, -1}, "Maximum |dca_z| as a function of pT (v[0] + v[1]*pow(x, v[2])"}; + Configurable pidThres{"pidThres", 0.8, "Minimum momentum to required TOF PID"}; + Configurable tpcNsigma{"tpcNsigma", 3, "Maximum NSigma"}; + Configurable tpctofNsigma{"tpctofNsigma", 3, "Maximum NSigma TPCTOF"}; + Configurable pdgCode{"pdgCode", 2212, "Pdg Code"}; + } TrackSel2; + + void init(InitContext const&) + { + AxisSpec axisMult = {multHistBin, "Mult", "MultAxis"}; + AxisSpec axisPV = {pvHistBin, "PV", "PVAxis"}; + AxisSpec axisFv0aMult = {fv0aMultHistBin, "fv0a", "FV0AMultAxis"}; + AxisSpec axisFt0aMult = {ft0aMultHistBin, "ft0a", "FT0AMultAxis"}; + AxisSpec axisFt0cMult = {ft0cMultHistBin, "ft0c", "FT0CMultAxis"}; + AxisSpec centAxis = {centralityBinning, "Centrality", "CentralityAxis"}; + AxisSpec axisPt = {ptHistBin, "pT", "pTAxis"}; + AxisSpec dcaAxis = {binsDCA, "DCA vs PV"}; + AxisSpec multAxis = {binsMult, "Multiplicity #eta<0.5"}; + AxisSpec axisKstar = {200, 0., 1., "k* (GeV/c)", "KstarAxis"}; + + histos.add("EventHist", "EventHist", kTH1D, {axisEvent}, false); + histos.add("VtxZHist", "VtxZHist", kTH1D, {axisVtxZ}, false); + + auto hstat = histos.get(HIST("EventHist")); + auto* x = hstat->GetXaxis(); + x->SetBinLabel(1, "All events"); + x->SetBinLabel(2, "kIsTriggerTVX"); + x->SetBinLabel(3, "kNoTimeFrameBorder"); + x->SetBinLabel(4, "kNoITSROFrameBorder"); + x->SetBinLabel(5, "kNoSameBunchPileup"); // reject collisions in case of pileup with another collision in the same foundBC + x->SetBinLabel(6, "kIsGoodZvtxFT0vsPV"); // small difference between z-vertex from PV and from FT0 + x->SetBinLabel(7, "INEL > 0"); + x->SetBinLabel(8, "hasPair"); + + histos.add("hTrack1Pt", "Track1 pT", kTH1D, {AxisSpec{ptHistBin, "pT", "pTAxis"}}, false); + histos.add("hTrack1Eta", "Track1 eta", kTH1D, {axisEta}, false); + histos.add("hTrack1Phi", "Track1 phi", kTH1D, {axisPhi2}, false); + histos.add("hTrack2Pt", "Track2 pT", kTH1D, {AxisSpec{ptHistBin, "pT", "pTAxis"}}, false); + histos.add("hTrack2Eta", "Track2 eta", kTH1D, {axisEta}, false); + histos.add("hTrack2Phi", "Track2 phi", kTH1D, {axisPhi2}, false); + histos.add("hPairKstar", "Pair k*", kTH1D, {axisKstar}, false); + + histos.add("hTrack1PtMC", "Track1 pT MC", kTH1D, {AxisSpec{ptHistBin, "pT", "pTAxis"}}, false); + histos.add("hTrack1EtaMC", "Track1 eta MC", kTH1D, {axisEta}, false); + histos.add("hTrack1PhiMC", "Track1 phi MC", kTH1D, {axisPhi2}, false); + histos.add("hTrack2PtMC", "Track2 pT MC", kTH1D, {AxisSpec{ptHistBin, "pT", "pTAxis"}}, false); + histos.add("hTrack2EtaMC", "Track2 eta MC", kTH1D, {axisEta}, false); + histos.add("hTrack2PhiMC", "Track2 phi MC", kTH1D, {axisPhi2}, false); + histos.add("hPairKstarMC", "Pair k* MC", kTH1D, {axisKstar}, false); + + if (doprocessData) { + histos.add("hdcaxy", "dca to pv in the xy plane", kTH1D, {dcaAxis}, false); + histos.add("hdcaz", "dca to pv in the z axis", kTH1D, {dcaAxis}, false); + histos.add("CentPercentileHist", "CentPercentileHist", kTH1D, {axisCent}, false); + histos.add("hdatazvtxcent", "hdatazvtxcent", kTH2D, {axisVtxZ, centAxis}, false); + histos.add("PhiVsEtaHist", "PhiVsEtaHist", kTH2D, {axisPhi2, axisEta}, false); + histos.add("hdatadndeta", "hdatadndeta", kTHnSparseD, {axisVtxZ, centAxis, axisEta, axisPhi, axisTrackType}, false); + histos.add("hdatadndetaMB", "hdatadndetaMB", kTHnSparseD, {axisVtxZ, axisEta, axisPhi}, false); + } + + if (doprocessCorrelation) { + histos.add("GlobalMult_vs_FT0A", "GlobalMult_vs_FT0A", kTH2F, {axisMult, axisFt0aMult}, true); + histos.add("GlobalMult_vs_FT0C", "GlobalMult_vs_FT0C", kTH2F, {axisMult, axisFt0cMult}, true); + histos.add("GlobalMult_vs_FV0A", "GlobalMult_vs_FV0A", kTH2F, {axisMult, axisFv0aMult}, true); + histos.add("GlobalMult_vs_NPVtracks", "GlobalMult_vs_NPVtracks", kTH2F, {axisMult, axisPV}, true); + histos.add("NPVtracks_vs_FT0C", "NPVtracks_vs_FT0C", kTH2F, {axisPV, axisFt0cMult}, true); + } + + if (doprocessStrangeYield) { + histos.add("hzvtxcent", "hzvtxcent", kTH2D, {axisVtxZ, centAxis}, false); + histos.add("K0sCentEtaMass", "K0sCentEtaMass", kTH3D, {centAxis, axisEta, axisMassK0s}, false); + histos.add("LambdaCentEtaMass", "LambdaCentEtaMass", kTH3D, {centAxis, axisEta, axisMassLambda}, false); + histos.add("AntiLambdaCentEtaMass", "AntiLambdaCentEtaMass", kTH3D, {centAxis, axisEta, axisMassLambda}, false); + } + + if (doprocessMCeff) { + histos.add("hmcdcaxy", "dca to pv in the xy plane", kTH1D, {dcaAxis}, false); + histos.add("hmcdcaz", "dca to pv in the z axis", kTH1D, {dcaAxis}, false); + histos.add("hGenMCvertexZ", "hGenMCvertexZ", kTH1D, {axisVtxZ}, false); + histos.add("hGenMCvtxzcent", "hGenMCvtxzcent", kTH2D, {axisVtxZ, centAxis}, false); + histos.add("hGenMCAssoRecvertexZ", "hGenMCAssoRecvertexZ", kTH1D, {axisVtxZ}, false); + histos.add("hGenMCAssoRecvtxzcent", "hGenMCAssoRecvtxzcent", kTH2D, {axisVtxZ, centAxis}, false); + histos.add("hGenMCdndeta", "hGenMCdndeta", kTHnSparseD, {axisVtxZ, centAxis, axisEta, axisPhi}, false); + histos.add("hGenMCAssoRecdndeta", "hGenMCAssoRecdndeta", kTHnSparseD, {axisVtxZ, centAxis, axisEta, axisPhi, axisGenTrkType, axisGenPtVary}, false); + + histos.add("hRecMCvertexZ", "hRecMCvertexZ", kTH1D, {axisVtxZ}, false); + histos.add("hRecMCvtxzcent", "hRecMCvtxzcent", kTH2D, {axisVtxZ, centAxis}, false); + histos.add("hRecMCcentrality", "hRecMCcentrality", kTH1D, {axisCent}, false); + histos.add("hRecMCphivseta", "hRecMCphivseta", kTH2D, {axisPhi2, axisEta}, false); + histos.add("hRecMCdndeta", "hRecMCdndeta", kTHnSparseD, {axisVtxZ, centAxis, axisEta, axisPhi, axisRecTrkType}, false); + histos.add("hRecMCdndetaMB", "hRecMCdndetaMB", kTHnSparseD, {axisVtxZ, axisEta, axisPhi, axisRecTrkType}, false); + histos.add("hGenMCAssoRecdndetaMB", "hGenMCAssoRecdndetaMB", kTHnSparseD, {axisVtxZ, axisEta, axisPhi, axisGenTrkType}, false); + } + + if (doprocessEvtLossSigLossMC) { + histos.add("MCEventHist", "MCEventHist", kTH1F, {axisEvent}, false); + auto hstat = histos.get(HIST("MCEventHist")); + auto* x = hstat->GetXaxis(); + x->SetBinLabel(1, "All MC events"); + x->SetBinLabel(2, "MC events with reco event after event selection"); + histos.add("hMultEta05Gen", "multiplicity in eta<0.5 of generated MC events", kTH1F, {multAxis}); + histos.add("hMultEta05GenAssoRec", "multiplicity in eta<0.5 of selected MC events", kTH1F, {multAxis}); + histos.add("hgendndetaVsMultEta05BeforeEvtSel", "hgendndetaBeforeEvtSel vs multiplicity in eta<0.5", kTH2F, {axisEta, multAxis}); + histos.add("hgendndetaVsMultEta05AfterEvtSel", "hgendndetaAfterEvtSel vs multiplicity in eta<0.5", kTH2F, {axisEta, multAxis}); + histos.add("hGenCent", "Centrality of generated MC events", kTH1F, {axisCent}); + histos.add("hGenAssoRecCent", "Centrality of selected MC events", kTH1F, {axisCent}); + histos.add("hgendndetaBeforeEvtSel", "Eta of all generated particles", kTH1F, {axisEta}); + histos.add("hgendndetaAfterEvtSel", "Eta of generated particles after EvtSel", kTH1F, {axisEta}); + histos.add("hgendndetaVscentBeforeEvtSel", "hgendndetaBeforeEvtSel vs centrality", kTH2F, {axisEta, centAxis}); + histos.add("hgendndetaVscentAfterEvtSel", "hgendndetaAfterEvtSel vs centrality", kTH2F, {axisEta, centAxis}); + } + } + + template + bool checkTrack(T const& track, S const& sel) + { + if (track.pt() < sel.ptMin.value || track.pt() > sel.ptMax.value) + return false; + if (std::fabs(track.eta()) > sel.etaMax.value) + return false; + if (track.tpcNClsFound() < sel.tpcClustersMin.value) + return false; + if (track.tpcNClsCrossedRows() < sel.tpcCrossedRowsMin.value) + return false; + if (track.tpcCrossedRowsOverFindableCls() < sel.tpcClustersOverCrossedRows.value) + return false; + if (track.tpcNClsShared() > sel.tpcSharedClustersMax.value) + return false; + if (track.tpcFractionSharedCls() > sel.tpcSharedClusterFractionMax.value) + return false; + if (track.itsNCls() < sel.itsClustersMin.value) + return false; + if (track.itsNClsInnerBarrel() < sel.itsIbClustersMin.value) + return false; + + const auto& dxy = sel.dcaxyMax.value; + const auto& dz = sel.dcazMax.value; + float dcaXyMax = dxy.at(0) + dxy.at(1) * std::pow(track.pt(), dxy.at(2)); + float dcaZMax = dz.at(0) + dz.at(1) * std::pow(track.pt(), dz.at(2)); + + if (std::abs(track.dcaXY()) > dcaXyMax) + return false; + if (std::abs(track.dcaZ()) > dcaZMax) + return false; + + return true; + } + + template + bool checkTrackPid(T const& track, S const& sel) + { + float nsigmaTPC = -99; + float nsigmaTPCTOF = -99; + switch (std::abs(sel.pdgCode.value)) { + case 211: + nsigmaTPC = track.tpcNSigmaPi(); + nsigmaTPCTOF = std::hypot(track.tpcNSigmaPi(), track.tofNSigmaPi()); + break; + case 321: + nsigmaTPC = track.tpcNSigmaKa(); + nsigmaTPCTOF = std::hypot(track.tpcNSigmaKa(), track.tofNSigmaKa()); + break; + case 2212: + nsigmaTPC = track.tpcNSigmaPr(); + nsigmaTPCTOF = std::hypot(track.tpcNSigmaPr(), track.tofNSigmaPr()); + break; + case 1000010020: + nsigmaTPC = track.tpcNSigmaDe(); + nsigmaTPCTOF = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()); + break; + default: + LOG(fatal) << "Unsupported PDG code: " << std::abs(sel.pdgCode.value); + return false; + } + if (track.p() < sel.pidThres.value) { + if (std::abs(nsigmaTPC) > sel.tpcNsigma.value) + return false; + } else { + if (nsigmaTPCTOF > sel.tpctofNsigma.value) + return false; + } + return true; + } + + float getKstar(ROOT::Math::PtEtaPhiMVector const& part1, ROOT::Math::PtEtaPhiMVector const& part2) + { + ROOT::Math::PxPyPzEVector p1(part1); + ROOT::Math::PxPyPzEVector p2(part2); + auto sum = p1 + p2; + ROOT::Math::Boost boostPrf(sum.BoostToCM()); + return static_cast(boostPrf(p1).P()); + } + + template + bool hasPair(const T& tracks) + { + bool foundPair = false; + float mass1 = o2::analysis::femto::utils::getPdgMass(TrackSel1.pdgCode.value); + float mass2 = o2::analysis::femto::utils::getPdgMass(TrackSel2.pdgCode.value); + float kstar; + + ROOT::Math::PtEtaPhiMVector particle1; + ROOT::Math::PtEtaPhiMVector particle2; + + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { + + bool order1 = checkTrack(p1, TrackSel1) && checkTrackPid(p1, TrackSel1) && + checkTrack(p2, TrackSel2) && checkTrackPid(p2, TrackSel2); + bool order2 = checkTrack(p1, TrackSel2) && checkTrackPid(p1, TrackSel2) && + checkTrack(p2, TrackSel1) && checkTrackPid(p2, TrackSel1); + + if (!order1 && !order2) { + continue; + } + + if (order1) { + particle1 = ROOT::Math::PtEtaPhiMVector(p1.pt(), p1.eta(), p1.phi(), mass1); + particle2 = ROOT::Math::PtEtaPhiMVector(p2.pt(), p2.eta(), p2.phi(), mass2); + } else { + particle1 = ROOT::Math::PtEtaPhiMVector(p2.pt(), p2.eta(), p2.phi(), mass1); + particle2 = ROOT::Math::PtEtaPhiMVector(p1.pt(), p1.eta(), p1.phi(), mass2); + } + + kstar = getKstar(particle1, particle2); + + if (kstar > kStarMax.value) { + continue; + } + // fill histograms + histos.fill(HIST("hTrack1Pt"), particle1.Pt()); + histos.fill(HIST("hTrack1Eta"), particle1.Eta()); + histos.fill(HIST("hTrack1Phi"), RecoDecay::constrainAngle(particle1.Phi())); + histos.fill(HIST("hTrack2Pt"), particle2.Pt()); + histos.fill(HIST("hTrack2Eta"), particle2.Eta()); + histos.fill(HIST("hTrack2Phi"), RecoDecay::constrainAngle(particle2.Phi())); + histos.fill(HIST("hPairKstar"), kstar); + + foundPair = true; + } + return foundPair; + } + + template + bool checkTrackMC(T const& particle, S const& sel) + { + if (!particle.isPhysicalPrimary()) // add this + return false; + if (particle.pt() < sel.ptMin.value || particle.pt() > sel.ptMax.value) + return false; + if (std::abs(particle.eta()) > sel.etaMax.value) + return false; + return true; + } + + template + bool hasPairMC(const T& tracks) + { + float mass1 = o2::analysis::femto::utils::getPdgMass(std::abs(TrackSel1.pdgCode.value)); + float mass2 = o2::analysis::femto::utils::getPdgMass(std::abs(TrackSel2.pdgCode.value)); + bool foundPair = false; + + for (auto const& [p1, p2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { + + bool order1 = checkTrackMC(p1, TrackSel1) && checkTrackMC(p2, TrackSel2) && + std::abs(p1.pdgCode()) == std::abs(TrackSel1.pdgCode.value) && + std::abs(p2.pdgCode()) == std::abs(TrackSel2.pdgCode.value); + bool order2 = checkTrackMC(p2, TrackSel1) && checkTrackMC(p1, TrackSel2) && + std::abs(p1.pdgCode()) == std::abs(TrackSel2.pdgCode.value) && + std::abs(p2.pdgCode()) == std::abs(TrackSel1.pdgCode.value); + + if (!order1 && !order2) { + continue; + } + + ROOT::Math::PtEtaPhiMVector particle1; + ROOT::Math::PtEtaPhiMVector particle2; + + if (order1) { + particle1 = ROOT::Math::PtEtaPhiMVector(p1.pt(), p1.eta(), p1.phi(), mass1); + particle2 = ROOT::Math::PtEtaPhiMVector(p2.pt(), p2.eta(), p2.phi(), mass2); + } else { + particle1 = ROOT::Math::PtEtaPhiMVector(p2.pt(), p2.eta(), p2.phi(), mass1); + particle2 = ROOT::Math::PtEtaPhiMVector(p1.pt(), p1.eta(), p1.phi(), mass2); + } + + float kstar = getKstar(particle1, particle2); + + // fill for all pairs regardless of kstar cut + histos.fill(HIST("hTrack1PtMC"), particle1.Pt()); + histos.fill(HIST("hTrack1EtaMC"), particle1.Eta()); + histos.fill(HIST("hTrack1PhiMC"), RecoDecay::constrainAngle(particle1.Phi())); + histos.fill(HIST("hTrack2PtMC"), particle2.Pt()); + histos.fill(HIST("hTrack2EtaMC"), particle2.Eta()); + histos.fill(HIST("hTrack2PhiMC"), RecoDecay::constrainAngle(particle2.Phi())); + histos.fill(HIST("hPairKstarMC"), kstar); + + if (kstar > kStarMax.value) { + continue; + } + foundPair = true; + } + return foundPair; + } + + template + bool isEventSelected(CheckCol const& col, CheckTracks const& tracks) + { + histos.fill(HIST("EventHist"), 1); + if (!col.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + histos.fill(HIST("EventHist"), 2); + if (isApplyTFcut && !col.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + histos.fill(HIST("EventHist"), 3); + if (isApplyITSROcut && !col.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + histos.fill(HIST("EventHist"), 4); + if (isApplySameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("EventHist"), 5); + if (isApplyGoodZvtxFT0vsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("EventHist"), 6); + if (isApplyInelgt0 && !col.isInelGt0()) { + return false; + } + histos.fill(HIST("EventHist"), 7); + if (isApplyPairCut && !hasPair(tracks)) { + return false; + } + histos.fill(HIST("EventHist"), 8); + return true; + } + + template + bool isTrackSelected(CheckTrack const& track) + { + if (std::abs(track.eta()) >= etaRange) { + return false; + } + if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + return false; + } + return true; + } + + template + bool isGenTrackSelected(CheckGenTrack const& track) + { + if (!track.isPhysicalPrimary()) { + return false; + } + if (!track.producedByGenerator()) { + return false; + } + auto pdgTrack = pdg->GetParticle(track.pdgCode()); + if (pdgTrack == nullptr) { + return false; + } + if (std::abs(pdgTrack->Charge()) < KminCharge) { + return false; + } + if (std::abs(track.eta()) >= etaRange) { + return false; + } + if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + return false; + } + return true; + } + + Filter fTrackSelectionITS = ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && + ncheckbit(aod::track::trackCutFlag, TrackSelectionIts); + Filter fTrackSelectionTPC = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), + ncheckbit(aod::track::trackCutFlag, TrackSelectionTpc), true); + Filter fTrackSelectionDCA = ifnode(dcaZ.node() > 0.f, nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), + ncheckbit(aod::track::trackCutFlag, TrackSelectionDca)); + + void processData(CollisionDataTable::iterator const& collision, FilTrackDataTable const& tracks) + { + if (!isEventSelected(collision, tracks)) { + return; + } + histos.fill(HIST("VtxZHist"), collision.posZ()); + histos.fill(HIST("CentPercentileHist"), collision.centFT0M()); + histos.fill(HIST("hdatazvtxcent"), collision.posZ(), collision.centFT0M()); + + for (const auto& track : tracks) { + if (!isTrackSelected(track)) { + continue; + } + histos.fill(HIST("hdcaxy"), track.dcaXY()); + histos.fill(HIST("hdcaz"), track.dcaZ()); + histos.fill(HIST("PhiVsEtaHist"), track.phi(), track.eta()); + histos.fill(HIST("hdatadndeta"), collision.posZ(), collision.centFT0M(), track.eta(), track.phi(), kGlobalplusITS); + histos.fill(HIST("hdatadndetaMB"), collision.posZ(), track.eta(), track.phi()); + if (track.hasTPC()) { + histos.fill(HIST("hdatadndeta"), collision.posZ(), collision.centFT0M(), track.eta(), track.phi(), kGlobalonly); + } else { + histos.fill(HIST("hdatadndeta"), collision.posZ(), collision.centFT0M(), track.eta(), track.phi(), kITSonly); + } + } + } + + void processCorrelation(CollisionDataTable::iterator const& collision, FilTrackDataTable const& tracks) + { + if (!isEventSelected(collision, tracks)) { + return; + } + if (std::abs(collision.posZ()) >= vtxRange) { + return; + } + histos.fill(HIST("VtxZHist"), collision.posZ()); + + auto nchTracks = 0; + for (const auto& track : tracks) { + if (std::abs(track.eta()) >= etaRange) { + continue; + } + nchTracks++; + } + + histos.fill(HIST("GlobalMult_vs_FT0A"), nchTracks, collision.multFT0A()); + histos.fill(HIST("GlobalMult_vs_FT0C"), nchTracks, collision.multFT0C()); + histos.fill(HIST("GlobalMult_vs_FV0A"), nchTracks, collision.multFV0A()); + histos.fill(HIST("GlobalMult_vs_NPVtracks"), nchTracks, collision.multNTracksPV()); + histos.fill(HIST("NPVtracks_vs_FT0C"), collision.multNTracksPV(), collision.multFT0C()); + } + + void processStrangeYield(CollisionDataTable::iterator const& collision, FilTrackDataTable const& tracks, V0TrackCandidates const&, aod::V0Datas const& v0data) + { + if (!isEventSelected(collision, tracks)) { + return; + } + if (std::abs(collision.posZ()) >= vtxRange) { + return; + } + histos.fill(HIST("hzvtxcent"), collision.posZ(), collision.centFT0M()); + for (const auto& v0track : v0data) { + auto v0pTrack = v0track.template posTrack_as(); + auto v0nTrack = v0track.template negTrack_as(); + if (std::abs(v0pTrack.eta()) <= v0etaK0SCut && std::abs(v0nTrack.eta()) <= v0etaK0SCut && v0pTrack.tpcNClsFound() >= minTPCnClsK0SCut && v0nTrack.tpcNClsFound() >= minTPCnClsK0SCut && std::abs(v0track.dcapostopv()) >= dcapostopvK0SCut && std::abs(v0track.dcanegtopv()) >= dcanegtopvK0SCut && v0track.v0radius() >= v0radiusK0SCut && v0track.v0cosPA() >= v0cospaK0SCut && std::abs(v0track.dcaV0daughters()) <= dcav0daughterK0Scut && std::abs(v0pTrack.tpcNSigmaPi()) <= nSigmaTpcK0SCut && std::abs(v0nTrack.tpcNSigmaPi()) <= nSigmaTpcK0SCut) { + + histos.fill(HIST("K0sCentEtaMass"), collision.centFT0M(), v0track.eta(), v0track.mK0Short()); + } + if (std::abs(v0pTrack.eta()) <= v0etaLambdaCut && std::abs(v0nTrack.eta()) <= v0etaLambdaCut && v0pTrack.tpcNClsFound() >= minTPCnClsLambdaCut && v0nTrack.tpcNClsFound() >= minTPCnClsLambdaCut && std::abs(v0track.dcapostopv()) >= dcapostopvLambdaCut && std::abs(v0track.dcanegtopv()) >= dcanegtopvLambdaCut && v0track.v0radius() >= v0radiusLambdaCut && v0track.v0cosPA() >= v0cospaLambdaCut && std::abs(v0track.dcaV0daughters()) <= dcav0daughterLambdacut) { + + if (std::abs(v0pTrack.tpcNSigmaPr()) <= nSigmaTpcLambdaCut && std::abs(v0nTrack.tpcNSigmaPi()) <= nSigmaTpcLambdaCut) { + histos.fill(HIST("LambdaCentEtaMass"), collision.centFT0M(), v0track.eta(), v0track.mLambda()); + } + if (std::abs(v0pTrack.tpcNSigmaPi()) <= nSigmaTpcLambdaCut && std::abs(v0nTrack.tpcNSigmaPr()) <= nSigmaTpcLambdaCut) { + histos.fill(HIST("AntiLambdaCentEtaMass"), collision.centFT0M(), v0track.eta(), v0track.mAntiLambda()); + } + } + } + } + + void processMCeff(soa::Join::iterator const& mcCollision, CollisionMCRecTable const& RecCols, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + { + auto gencent = -999; + bool atLeastOne = false; + + for (const auto& RecCol : RecCols) { + auto recTracksPart = RecTracks.sliceByCached(aod::track::collisionId, RecCol.globalIndex(), cache); + if (!isEventSelected(RecCol, recTracksPart)) { + continue; + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + atLeastOne = true; + gencent = RecCol.centFT0M(); + } + + histos.fill(HIST("hGenMCvertexZ"), mcCollision.posZ()); + histos.fill(HIST("hGenMCvtxzcent"), mcCollision.posZ(), gencent); + + if (atLeastOne) { + histos.fill(HIST("hGenMCAssoRecvertexZ"), mcCollision.posZ()); + histos.fill(HIST("hGenMCAssoRecvtxzcent"), mcCollision.posZ(), gencent); + } + + for (const auto& particle : GenParticles) { + if (!isGenTrackSelected(particle)) { + continue; + } + histos.fill(HIST("hGenMCdndeta"), mcCollision.posZ(), gencent, particle.eta(), particle.phi()); + if (atLeastOne) { + histos.fill(HIST("hGenMCAssoRecdndeta"), mcCollision.posZ(), gencent, particle.eta(), particle.phi(), static_cast(kGenAll), kNoGenpTVar); + histos.fill(HIST("hGenMCAssoRecdndetaMB"), mcCollision.posZ(), particle.eta(), particle.phi(), static_cast(kGenAll)); + if (particle.pt() < KminPtCut) { + histos.fill(HIST("hGenMCAssoRecdndeta"), mcCollision.posZ(), gencent, particle.eta(), particle.phi(), static_cast(kGenAll), kGenpTup, -10.0 * particle.pt() + 2); + histos.fill(HIST("hGenMCAssoRecdndeta"), mcCollision.posZ(), gencent, particle.eta(), particle.phi(), static_cast(kGenAll), kGenpTdown, 5.0 * particle.pt() + 0.5); + } else { + histos.fill(HIST("hGenMCAssoRecdndeta"), mcCollision.posZ(), gencent, particle.eta(), particle.phi(), static_cast(kGenAll), kGenpTup); + histos.fill(HIST("hGenMCAssoRecdndeta"), mcCollision.posZ(), gencent, particle.eta(), particle.phi(), static_cast(kGenAll), kGenpTdown); + } + int pid = 0; + switch (std::abs(particle.pdgCode())) { + case PDG_t::kPiPlus: + pid = kGenPion; + break; + case PDG_t::kKPlus: + pid = kGenKaon; + break; + case PDG_t::kProton: + pid = kGenProton; + break; + default: + pid = kGenOther; + break; + } + histos.fill(HIST("hGenMCAssoRecdndeta"), mcCollision.posZ(), gencent, particle.eta(), particle.phi(), static_cast(pid), kNoGenpTVar); + histos.fill(HIST("hGenMCAssoRecdndetaMB"), mcCollision.posZ(), particle.eta(), particle.phi(), static_cast(pid)); + } // Associated with reco col + } // track (mcgen) loop + + for (const auto& RecCol : RecCols) { + auto recTracksPart = RecTracks.sliceByCached(aod::track::collisionId, RecCol.globalIndex(), cache); + if (!isEventSelected(RecCol, recTracksPart)) { + continue; + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + histos.fill(HIST("hRecMCvertexZ"), RecCol.posZ()); + histos.fill(HIST("hRecMCcentrality"), RecCol.centFT0M()); + histos.fill(HIST("hRecMCvtxzcent"), RecCol.posZ(), RecCol.centFT0M()); + + std::vector mclabels; + for (const auto& Rectrack : recTracksPart) { + if (!isTrackSelected(Rectrack)) { + continue; + } + if (!Rectrack.has_mcParticle()) { + histos.fill(HIST("hRecMCdndeta"), RecCol.posZ(), RecCol.centFT0M(), Rectrack.eta(), Rectrack.phi(), static_cast(kRecoBkg)); + histos.fill(HIST("hRecMCdndetaMB"), RecCol.posZ(), Rectrack.eta(), Rectrack.phi(), static_cast(kRecoBkg)); + continue; + } + auto mcpart = Rectrack.mcParticle(); + if (RecCol.mcCollisionId() != mcpart.mcCollisionId()) { + continue; + } + histos.fill(HIST("hmcdcaxy"), Rectrack.dcaXY()); + histos.fill(HIST("hmcdcaz"), Rectrack.dcaZ()); + histos.fill(HIST("hRecMCphivseta"), Rectrack.phi(), Rectrack.eta()); + histos.fill(HIST("hRecMCdndeta"), RecCol.posZ(), RecCol.centFT0M(), Rectrack.eta(), Rectrack.phi(), static_cast(kRecoAll)); + histos.fill(HIST("hRecMCdndetaMB"), RecCol.posZ(), Rectrack.eta(), Rectrack.phi(), static_cast(kRecoAll)); + + int pid = 0; + if (mcpart.isPhysicalPrimary()) { + switch (std::abs(mcpart.pdgCode())) { + case PDG_t::kPiPlus: + pid = kRecoPion; + break; + case PDG_t::kKPlus: + pid = kRecoKaon; + break; + case PDG_t::kProton: + pid = kRecoProton; + break; + default: + pid = kRecoOther; + break; + } + } else { + pid = kRecoSecondary; + } + if (mcpart.has_mothers()) { + auto mcpartMother = mcpart.template mothers_as().front(); + if (mcpartMother.pdgCode() == PDG_t::kK0Short || std::abs(mcpartMother.pdgCode()) == PDG_t::kLambda0) { + pid = kRecoWeakDecay; + } + } + if (find(mclabels.begin(), mclabels.end(), Rectrack.mcParticleId()) != mclabels.end()) { + pid = kRecoFake; + } + mclabels.push_back(Rectrack.mcParticleId()); + histos.fill(HIST("hRecMCdndeta"), RecCol.posZ(), RecCol.centFT0M(), mcpart.eta(), mcpart.phi(), static_cast(pid)); + histos.fill(HIST("hRecMCdndetaMB"), RecCol.posZ(), mcpart.eta(), mcpart.phi(), static_cast(pid)); + } // track (mcrec) loop + } // collision loop + } + + void processEvtLossSigLossMC(soa::Join::iterator const& mcCollision, CollisionMCRecTable const& RecCols, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + { + + if ((isApplyInelgt0 && !mcCollision.isInelGt0()) || (isApplyPairCut && !hasPairMC(GenParticles))) { + return; + } + if (std::abs(mcCollision.posZ()) >= vtxRange) { + return; + } + // All generated events + histos.fill(HIST("MCEventHist"), 1); + histos.fill(HIST("hGenCent"), mcCollision.centFT0M()); + histos.fill(HIST("hMultEta05Gen"), mcCollision.multMCNParticlesEta05()); + bool atLeastOne = false; + for (const auto& RecCol : RecCols) { + auto recTracksPart = RecTracks.sliceByCached(aod::track::collisionId, RecCol.globalIndex(), cache); + if (!isEventSelected(RecCol, recTracksPart)) { + continue; + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + atLeastOne = true; + } + // Generated events with at least one reconstructed collision (event loss estimation) + if (atLeastOne) { + histos.fill(HIST("MCEventHist"), 2); + histos.fill(HIST("hGenAssoRecCent"), mcCollision.centFT0M()); + histos.fill(HIST("hMultEta05GenAssoRec"), mcCollision.multMCNParticlesEta05()); + } + for (const auto& particle : GenParticles) { + if (!isGenTrackSelected(particle)) { + continue; + } + // All generated particles + histos.fill(HIST("hgendndetaBeforeEvtSel"), particle.eta()); + histos.fill(HIST("hgendndetaVscentBeforeEvtSel"), particle.eta(), mcCollision.centFT0M()); + histos.fill(HIST("hgendndetaVsMultEta05BeforeEvtSel"), particle.eta(), mcCollision.multMCNParticlesEta05()); + if (atLeastOne) { + // All generated particles with at least one reconstructed collision (signal loss estimation) + histos.fill(HIST("hgendndetaAfterEvtSel"), particle.eta()); + histos.fill(HIST("hgendndetaVscentAfterEvtSel"), particle.eta(), mcCollision.centFT0M()); + histos.fill(HIST("hgendndetaVsMultEta05AfterEvtSel"), particle.eta(), mcCollision.multMCNParticlesEta05()); + } + } + } + PROCESS_SWITCH(FemtoPairEfficiency, processData, "process data CentFT0C", false); + PROCESS_SWITCH(FemtoPairEfficiency, processCorrelation, "do correlation study in data/MC", false); + PROCESS_SWITCH(FemtoPairEfficiency, processStrangeYield, "Strange particle yield", false); + PROCESS_SWITCH(FemtoPairEfficiency, processMCeff, "process MC efficiency function", false); + PROCESS_SWITCH(FemtoPairEfficiency, processEvtLossSigLossMC, "process Signal Loss, Event Loss", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx new file mode 100644 index 00000000000..f4eb09769b3 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairMcParticleMcParticle.cxx @@ -0,0 +1,161 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairMcParticleMcParticle.cxx +/// \brief Tasks that computes correlation between two tracks +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/mcBuilder.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPairMcParticleMcParticle { + + // setup tables + using FemtoMcCollisions = o2::aod::FMcCols; + using FilteredFemtoMcCollisions = o2::soa::Filtered; + using FilteredFemtoMcCollision = FilteredFemtoMcCollisions::iterator; + + using FemtoMcParticles = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + mcbuilder::ConfMcCollisionFilters collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_MC_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::CollisionHistManager colHistManager; + + // setup mc particles + mcbuilder::ConfMcParticleSelection1 confMcParticleSelection1; + mcparticlehistmanager::ConfMcParticleBinning1 confMcParticleBinning1; + mcparticlehistmanager::McParticleHistManager mcParticleHistManager1; + particlecleaner::ConfMcParticleCleaner1 confMcParticleCleaner1; + particlecleaner::ParticleCleaner mcParticleCleaner1; + + o2::framework::Partition mcParticlesPartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); + + mcbuilder::ConfMcParticleSelection2 confMcParticleSelection2; + mcparticlehistmanager::ConfMcParticleBinning2 confMcParticleBinning2; + mcparticlehistmanager::McParticleHistManager mcParticleHistManager2; + particlecleaner::ConfMcParticleCleaner2 confMcParticleCleaner2; + particlecleaner::ParticleCleaner mcParticleCleaner2; + + o2::framework::Partition mcParticlesPartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); + + o2::framework::PresliceUnsorted perColParticles = o2::aod::femtomcparticle::fMcColId; + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + + closepairrejection::ConfCprTrackTrack confCpr; + + pairbuilder::PairMcParticleMcParticleBuilder< + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + pairhistmanager::PrefixMcParticleMcParticleSe, + pairhistmanager::PrefixMcParticleMcParticleMe, + closepairrejection::PrefixMcParticleMcParticleSe, + closepairrejection::PrefixMcParticleMcParticleMe> + pairMcParticleMcParticleBuilder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + pairhistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoMcParticleMcParticle", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histogram specs + std::map> colHistSpec; + std::map> mcParticleHistSpec1; + std::map> mcParticleHistSpec2; + std::map> pairHistSpec; + std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + mcParticleHistSpec1 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning1); + mcParticleHistSpec2 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning2); + pairHistSpec = pairhistmanager::makePairMcTruthHistSpecMap(confPairBinning, confMixing); + pairMcParticleMcParticleBuilder.init(&hRegistry, confCollisionBinning, confMcParticleSelection1, confMcParticleSelection2, confMcParticleBinning1, confMcParticleBinning2, confMcParticleCleaner1, confMcParticleCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, mcParticleHistSpec1, mcParticleHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + + hRegistry.print(); + }; + + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() used inside the builder (see the same workaround in + // femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); we run the per-collision loop ourselves instead + void processSameEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + for (auto const& col : cols) { + pairMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, cache); + } + } + PROCESS_SWITCH(FemtoPairMcParticleMcParticle, processSameEvent, "Enable processing same event processing", true); + + void processMixedEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairMcParticleMcParticleBuilder.processMixedEvent(cols, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairMcParticleMcParticle, processMixedEvent, "Enable processing mixed event processing", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 59cbf706fb1..0f190a841cf 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -20,6 +20,7 @@ #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" @@ -59,9 +60,16 @@ struct FemtoPairTrackCascade { using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; - using FemtoXisWithLabel = o2::soa::Join; - using FemtoOmegasWithLabel = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoXisWithLabel = o2::soa::Join; + using FemtoOmegasWithLabel = o2::soa::Join; + + // reco-only + FitVars (no MC labels, but carries the opt-in purity/fraction tables) + using FemtoTracksWithFitVars = o2::soa::Join; + using FemtoXisWithFitVars = o2::soa::Join; + using FemtoOmegasWithFitVars = o2::soa::Join; + + using FemtoMcParticlesWithLabel = o2::soa::Join; o2::framework::SliceCache cache; @@ -81,6 +89,9 @@ struct FemtoPairTrackCascade { o2::framework::Partition trackWithLabelPartition = MAKE_TRACK_PARTITION(confTrackSelection); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + // setup for daughters/bachelor trackhistmanager::ConfCascadePosDauBinning confPosDauBinning; trackhistmanager::ConfCascadeNegDauBinning confNegDauBinning; @@ -97,6 +108,9 @@ struct FemtoPairTrackCascade { o2::framework::Partition xiWithLabelPartition = MAKE_CASCADE_PARTITION(confXiSelection); o2::framework::Preslice perColXisWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition xiWithFitVarsPartition = MAKE_CASCADE_PARTITION(confXiSelection); + o2::framework::Preslice perColXisWithFitVars = o2::aod::femtobase::stored::fColId; + // setup omegas cascadebuilder::ConfOmegaSelection confOmegaSelection; cascadehistmanager::ConfOmegaBinning confOmegaBinning; @@ -108,16 +122,25 @@ struct FemtoPairTrackCascade { o2::framework::Partition omegaWithLabelPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); o2::framework::Preslice perColOmegasWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition omegaWithFitVarsPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); + o2::framework::Preslice perColOmegasWithFitVars = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackCascadeBuilder< - trackhistmanager::PrefixTrack1, - cascadehistmanager::PrefixXi, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + cascadehistmanager::PrefixXiSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + cascadehistmanager::PrefixXiMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, pairhistmanager::PrefixTrackCascadeSe, pairhistmanager::PrefixTrackCascadeMe, closepairrejection::PrefixTrackCascadeBachelorSe, @@ -128,11 +151,16 @@ struct FemtoPairTrackCascade { pairTrackXiBuilder; pairbuilder::PairTrackCascadeBuilder< - trackhistmanager::PrefixTrack1, - cascadehistmanager::PrefixOmega, - trackhistmanager::PrefixCascadeBachelor, - trackhistmanager::PrefixCascadePosDaughter, - trackhistmanager::PrefixCascadeNegDaughter, + trackhistmanager::PrefixTrack1Se, + cascadehistmanager::PrefixOmegaSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + cascadehistmanager::PrefixOmegaMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, pairhistmanager::PrefixTrackCascadeSe, pairhistmanager::PrefixTrackCascadeMe, closepairrejection::PrefixTrackCascadeBachelorSe, @@ -146,9 +174,9 @@ struct FemtoPairTrackCascade { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackCascade", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -160,14 +188,15 @@ struct FemtoPairTrackCascade { void init(o2::framework::InitContext&) { bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processFitVars = doprocessXiSameEventWithFitVars || doprocessXiMixedEventWithFitVars || doprocessOmegaSameEventWithFitVars || doprocessOmegaMixedEventWithFitVars; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventWithFitVars || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithFitVars || doprocessXiMixedEventMc; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventWithFitVars || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithFitVars || doprocessOmegaMixedEventMc; if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; @@ -175,9 +204,9 @@ struct FemtoPairTrackCascade { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; @@ -190,8 +219,23 @@ struct FemtoPairTrackCascade { std::map> cprHistSpecBachelor = closepairrejection::makeCprHistSpecMap(confCprBachelor); std::map> cprHistSpecV0Daughter = closepairrejection::makeCprHistSpecMap(confCprV0Daughter); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + bachelorHistSpec = trackhistmanager::makeTrackHistSpecMap(confBachelorBinning); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + pairTrackCascadeHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processXi) { + xiHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confXiBinning); + pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + } else { + omegaHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confOmegaBinning); + pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); + } + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); bachelorHistSpec = trackhistmanager::makeTrackHistSpecMap(confBachelorBinning); @@ -200,11 +244,11 @@ struct FemtoPairTrackCascade { pairTrackCascadeHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); if (processXi) { xiHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confXiBinning); - pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter); + pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } else { omegaHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confOmegaBinning); pairTrackCascadeHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter); + pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); @@ -215,67 +259,92 @@ struct FemtoPairTrackCascade { pairTrackCascadeHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); if (processXi) { xiHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confXiBinning); - pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter); + pairTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } else { omegaHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confOmegaBinning); - pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter); + pairTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, pairTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, pairCleanerHistSpec); } } + hRegistry.print(); }; void processXiSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoXis const& xis) { - pairTrackXiBuilder.processSameEvent(col, tracks, trackPartition, xis, xiPartition, cache); + pairTrackXiBuilder.processSameEvent(col, tracks, trackPartition, xis, xiPartition, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEvent, "Enable processing same event processing for tracks and xis", true); - void processXiSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& xis, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processXiSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& xis, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackXiBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, xis, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackXiBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, xis, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) { - pairTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEvent, "Enable processing mixed event processing for tracks and xis", true); - void processXiMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processXiMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackXiBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackXiBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventMc, "Enable processing mixed event processing for tracks and xis with mc information", false); + void processXiSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoXisWithFitVars const& xis) + { + pairTrackXiBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, xis, xiWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventWithFitVars, "Enable processing same event processing for tracks and xis, carrying through FitVars tables", false); + + void processXiMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoXisWithFitVars const& /*xis*/) + { + pairTrackXiBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, xiWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventWithFitVars, "Enable processing mixed event processing for tracks and xis, carrying through FitVars tables", false); + void processOmegaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoOmegas const& omegas) { - pairTrackOmegaBuilder.processSameEvent(col, tracks, trackPartition, omegas, omegaPartition, cache); + pairTrackOmegaBuilder.processSameEvent(col, tracks, trackPartition, omegas, omegaPartition, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEvent, "Enable processing same event processing for tracks and omegas", false); - void processOmegaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& omegas, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processOmegaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& omegas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackOmegaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, omegas, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackOmegaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, omegas, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) { - pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEvent, "Enable processing mixed event processing for tracks and omegas", false); - void processOmegaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processOmegaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& /*omegas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackOmegaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackOmegaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventMc, "Enable processing mixed event processing for tracks and omegas with mc information", false); + + void processOmegaSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoOmegasWithFitVars const& omegas) + { + pairTrackOmegaBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, omegas, omegaWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventWithFitVars, "Enable processing same event processing for tracks and omegas, carrying through FitVars tables", false); + + void processOmegaMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoOmegasWithFitVars const& /*omegas*/) + { + pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, omegaWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventWithFitVars, "Enable processing mixed event processing for tracks and omegas, carrying through FitVars tables", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx new file mode 100644 index 00000000000..172c3c2ffc8 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -0,0 +1,307 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairTrackCharmHadron.cxx +/// \brief Tasks that computes correlation between tracks and charm hadrons +/// \author Igor Ptak, WUT, igor.tomasz.ptak@cern.ch + +#include "PWGCF/Femto/Core/charmHadronBuilder.h" +#include "PWGCF/Femto/Core/charmHadronHistManager.h" +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPairTrackCharmHadron { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoD0s = o2::soa::Join; + using FemtoLcs = o2::soa::Join; + + using FemtoCollisionsWithLabel = o2::soa::Join; + using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; + using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoD0sWithLabel = o2::soa::Join; + using FemtoLcsWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup tracks + trackbuilder::ConfTrackSelection1 confTrackSelection; + trackhistmanager::ConfTrackBinning1 confTrackBinning; + particlecleaner::ConfTrackCleaner1 confTrackCleaner; + + o2::framework::Partition trackPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition trackWithLabelPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracksWithLabel = o2::aod::femtobase::stored::fColId; + + // setup for D0 daughters + trackhistmanager::ConfD01PosDauBinning confPosDauBinning; + trackhistmanager::ConfD01NegDauBinning confNegDauBinning; + + // setup D0s + charmhadronbuilder::ConfD0Selection1 d0Selection; + charmhadronhistmanager::ConfD0Binning1 confD0Binning; + particlecleaner::ConfD0Cleaner1 confD0Cleaner; + + o2::framework::Partition d0Partition = MAKE_D0_PARTITION(d0Selection); + o2::framework::Preslice perColD0s = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition d0WithLabelPartition = MAKE_D0_PARTITION(d0Selection); + o2::framework::Preslice perColD0sWithLabel = o2::aod::femtobase::stored::fColId; + + // setup for Lc daughters + trackhistmanager::ConfLc1ProtonDauBinning confProtonDauBinning; + trackhistmanager::ConfLc1KaonDauBinning confKaonDauBinning; + trackhistmanager::ConfLc1PionDauBinning confPionDauBinning; + + // setup Lcs + charmhadronbuilder::ConfLcSelection1 lcSelection; + charmhadronhistmanager::ConfLcBinning1 confLcBinning; + particlecleaner::ConfLcCleaner1 confLcCleaner; + + o2::framework::Partition lcPartition = MAKE_CHARM3PRONG_PARTITION(lcSelection); + o2::framework::Preslice perColLcs = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition lcWithLabelPartition = MAKE_CHARM3PRONG_PARTITION(lcSelection); + o2::framework::Preslice perColLcsWithLabel = o2::aod::femtobase::stored::fColId; + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + + pairbuilder::PairTrackD0Builder< + trackhistmanager::PrefixTrack1Se, + charmhadronhistmanager::PrefixD01Se, + trackhistmanager::PrefixD01PosDaughterSe, + trackhistmanager::PrefixD01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + charmhadronhistmanager::PrefixD01Me, + trackhistmanager::PrefixD01PosDaughterMe, + trackhistmanager::PrefixD01NegDaughterMe, + pairhistmanager::PrefixTrackD0Se, + pairhistmanager::PrefixTrackD0Me, + closepairrejection::PrefixTrackD0DaughterSe, + closepairrejection::PrefixTrackD0DaughterMe, + modes::CharmHadron::kD0> + pairTrackD0Builder; + + pairbuilder::PairTrackLcBuilder< + trackhistmanager::PrefixTrack1Se, + charmhadronhistmanager::PrefixLc1Se, + trackhistmanager::PrefixLc1ProtonDaughterSe, + trackhistmanager::PrefixLc1KaonDaughterSe, + trackhistmanager::PrefixLc1PionDaughterSe, + trackhistmanager::PrefixTrack1Me, + charmhadronhistmanager::PrefixLc1Me, + trackhistmanager::PrefixLc1ProtonDaughterMe, + trackhistmanager::PrefixLc1KaonDaughterMe, + trackhistmanager::PrefixLc1PionDaughterMe, + pairhistmanager::PrefixTrackLcSe, + pairhistmanager::PrefixTrackLcMe, + closepairrejection::PrefixTrackLcProtonSe, + closepairrejection::PrefixTrackLcKaonSe, + closepairrejection::PrefixTrackLcPionSe, + closepairrejection::PrefixTrackLcProtonMe, + closepairrejection::PrefixTrackLcKaonMe, + closepairrejection::PrefixTrackLcPionMe, + modes::CharmHadron::kLc> + pairTrackLcBuilder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + pairhistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackD0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + // setup cpr + closepairrejection::ConfCprTrackD0Daughter confCpr; + closepairrejection::ConfCprTrackLcProton confCprLcProton; + closepairrejection::ConfCprTrackLcKaon confCprLcKaon; + closepairrejection::ConfCprTrackLcPion confCprLcPion; + + void init(o2::framework::InitContext&) + { + bool processD0 = doprocessD0SameEvent || doprocessD0MixedEvent; + bool processD0Mc = doprocessD0SameEventMc || doprocessD0MixedEventMc; + bool processLc = doprocessLcSameEvent || doprocessLcMixedEvent; + bool processLcMc = doprocessLcSameEventMc || doprocessLcMixedEventMc; + + if ((static_cast(processD0) + static_cast(processD0Mc) + static_cast(processLc) + static_cast(processLcMc)) > 1) { + LOG(fatal) << "Only one charm hadron species and data/mc mode can be processed at a time. Breaking..."; + } + + bool processData = processD0 || processLc; + + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + std::map> colHistSpec; + std::map> trackHistSpec; + std::map> posDauSpec; + std::map> negDauSpec; + std::map> protonDauSpec; + std::map> kaonDauSpec; + std::map> pionDauSpec; + std::map> d0HistSpec; + std::map> lcHistSpec; + std::map> pairTrackD0HistSpec; + std::map> pairTrackLcHistSpec; + std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + std::map> cprHistSpecLc = closepairrejection::makeCprHistSpecMap(confCprLcProton); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + if (processData) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + if (processD0) { + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + d0HistSpec = charmhadronhistmanager::makeCharmHadronHistSpecMap(confD0Binning); + pairTrackD0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackD0Builder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, d0Selection, confD0Cleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, d0HistSpec, posDauSpec, negDauSpec, pairTrackD0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + if (processLc) { + protonDauSpec = trackhistmanager::makeTrackHistSpecMap(confProtonDauBinning); + kaonDauSpec = trackhistmanager::makeTrackHistSpecMap(confKaonDauBinning); + pionDauSpec = trackhistmanager::makeTrackHistSpecMap(confPionDauBinning); + lcHistSpec = charmhadronhistmanager::makeCharmHadronHistSpecMap(confLcBinning); + pairTrackLcHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackLcBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lcSelection, confLcCleaner, confCprLcProton, confCprLcKaon, confCprLcPion, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lcHistSpec, protonDauSpec, kaonDauSpec, pionDauSpec, pairTrackLcHistSpec, cprHistSpecLc, pairCleanerHistSpec); + } + } else { + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning); + if (processD0Mc) { + posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); + d0HistSpec = charmhadronhistmanager::makeCharmHadronMcHistSpecMap(confD0Binning); + pairTrackD0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); + pairTrackD0Builder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, d0Selection, confD0Cleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, d0HistSpec, posDauSpec, negDauSpec, pairTrackD0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + if (processLcMc) { + protonDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confProtonDauBinning); + kaonDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confKaonDauBinning); + pionDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPionDauBinning); + lcHistSpec = charmhadronhistmanager::makeCharmHadronMcHistSpecMap(confLcBinning); + pairTrackLcHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); + pairTrackLcBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lcSelection, confLcCleaner, confCprLcProton, confCprLcKaon, confCprLcPion, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lcHistSpec, protonDauSpec, kaonDauSpec, pionDauSpec, pairTrackLcHistSpec, cprHistSpecLc, pairCleanerHistSpec); + } + } + + hRegistry.print(); + }; + + void processD0SameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoD0s const& d0s) + { + pairTrackD0Builder.processSameEvent(col, tracks, trackPartition, d0s, d0Partition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0SameEvent, "Enable processing same event processing for tracks and D0s", true); + + void processD0MixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + pairTrackD0Builder.processMixedEvent(cols, tracks, trackPartition, d0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0MixedEvent, "Enable processing mixed event processing for tracks and D0s", true); + + void processD0SameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoD0sWithLabel const& d0s, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackD0Builder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, d0s, d0WithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0SameEventMc, "Enable processing same event processing for tracks and D0s with MC information", false); + + void processD0MixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoD0sWithLabel const& /*d0s*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackD0Builder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, d0WithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0MixedEventMc, "Enable processing mixed event processing for tracks and D0s with MC information", false); + + void processLcSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLcs const& lcs) + { + pairTrackLcBuilder.processSameEvent(col, tracks, trackPartition, lcs, lcPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcSameEvent, "Enable processing same event processing for tracks and Lcs", false); + + void processLcMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) + { + pairTrackLcBuilder.processMixedEvent(cols, tracks, trackPartition, lcPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcMixedEvent, "Enable processing mixed event processing for tracks and Lcs", false); + + void processLcSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLcsWithLabel const& lcs, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackLcBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, lcs, lcWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcSameEventMc, "Enable processing same event processing for tracks and Lcs with MC information", false); + + void processLcMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLcsWithLabel const& /*lcs*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackLcBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, lcWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcMixedEventMc, "Enable processing mixed event processing for tracks and Lcs with MC information", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index 2b0730109f3..affa4a1fcff 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -21,6 +21,7 @@ #include "PWGCF/Femto/Core/kinkHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" @@ -59,10 +60,14 @@ struct FemtoPairTrackKink { using FemtoTracks = o2::soa::Join; using FemtoSigmas = o2::soa::Join; using FemtoSigmaPlus = o2::soa::Join; + using FemtoSigmaPlusPcm = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; using FemtoSigmasWithLabel = o2::soa::Join; using FemtoSigmaPlusWithLabel = o2::soa::Join; + using FemtoSigmaPlusPcmWithLabel = o2::soa::Join; + + using FemtoMcParticlesWithLabel = o2::soa::Join; o2::framework::SliceCache cache; @@ -107,14 +112,29 @@ struct FemtoPairTrackKink { o2::framework::Partition sigmaPlusWithLabelPartition = MAKE_SIGMAPLUS_PARTITION(confSigmaPlusSelection); o2::framework::Preslice perColSigmaPlusWithLabel = o2::aod::femtobase::stored::fColId; + // setup for sigma plus reconstructed with a PCM photon + kinkbuilder::ConfSigmaPlusPcmSelection1 confSigmaPlusPcmSelection; + kinkhistmanager::ConfSigmaPlusPcmBinning1 confSigmaPlusPcmBinning; + particlecleaner::ConfSigmaPlusPcmCleaner1 confSigmaPlusPcmCleaner; + + o2::framework::Partition sigmaPlusPcmPartition = MAKE_SIGMAPLUSPCM_PARTITION(confSigmaPlusPcmSelection); + o2::framework::Preslice perColSigmaPlusPcm = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition sigmaPlusPcmWithLabelPartition = MAKE_SIGMAPLUSPCM_PARTITION(confSigmaPlusPcmSelection); + o2::framework::Preslice perColSigmaPlusPcmWithLabel = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackKinkBuilder< - trackhistmanager::PrefixTrack1, - kinkhistmanager::PrefixSigma1, - trackhistmanager::PrefixKinkChaDaughter, + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigma1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigma1Me, + trackhistmanager::PrefixKinkChaDaughterMe, pairhistmanager::PrefixTrackKinkSe, pairhistmanager::PrefixTrackKinkMe, closepairrejection::PrefixTrackKinkSe, @@ -123,9 +143,12 @@ struct FemtoPairTrackKink { pairTrackSigmaBuilder; pairbuilder::PairTrackKinkBuilder< - trackhistmanager::PrefixTrack1, - kinkhistmanager::PrefixSigmaPlus1, - trackhistmanager::PrefixKinkChaDaughter, + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigmaPlus1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigmaPlus1Me, + trackhistmanager::PrefixKinkChaDaughterMe, pairhistmanager::PrefixTrackKinkSe, pairhistmanager::PrefixTrackKinkMe, closepairrejection::PrefixTrackKinkSe, @@ -133,13 +156,27 @@ struct FemtoPairTrackKink { modes::Kink::kSigmaPlus> pairTrackSigmaPlusBuilder; + pairbuilder::PairTrackKinkBuilder< + trackhistmanager::PrefixTrack1Se, + kinkhistmanager::PrefixSigmaPlusPcm1Se, + trackhistmanager::PrefixKinkChaDaughterSe, + trackhistmanager::PrefixTrack1Me, + kinkhistmanager::PrefixSigmaPlusPcm1Me, + trackhistmanager::PrefixKinkChaDaughterMe, + pairhistmanager::PrefixTrackKinkSe, + pairhistmanager::PrefixTrackKinkMe, + closepairrejection::PrefixTrackKinkSe, + closepairrejection::PrefixTrackKinkMe, + modes::Kink::kSigmaPlusPcm> + pairTrackSigmaPlusPcmBuilder; + // setup mixing std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackKink", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -149,8 +186,8 @@ struct FemtoPairTrackKink { void init(o2::framework::InitContext&) { - bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent; - bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc; + bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmMixedEvent; + bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; @@ -159,23 +196,30 @@ struct FemtoPairTrackKink { bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc; bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc; + bool processSigmaPlusPcm = doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaPlusPcmMixedEventMc; + if (processSigma && processSigmaPlus) { LOG(fatal) << "Both Sigma-track and SigmaPlus-track processing is enabled. Breaking..."; } + if ((processSigma || processSigmaPlus) && processSigmaPlusPcm) { + LOG(fatal) << "Both Sigma/SigmaPlus-track and SigmaPlusPcm-track processing is enabled. Breaking..."; + } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; std::map> chaDauSpec; std::map> sigmaHistSpec; std::map> sigmaPlusHistSpec; + std::map> sigmaPlusPcmHistSpec; std::map> pairTrackKinkHistSpec; std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); @@ -184,10 +228,13 @@ struct FemtoPairTrackKink { pairTrackKinkHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); if (processSigma) { sigmaHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaBinning); - pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec); - } else { + pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else if (processSigmaPlus) { sigmaPlusHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaPlusBinning); - pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec); + pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + sigmaPlusPcmHistSpec = kinkhistmanager::makeKinkHistSpecMap(confSigmaPlusPcmBinning); + pairTrackSigmaPlusPcmBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusPcmSelection, confSigmaPlusPcmCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusPcmHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); } } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); @@ -196,10 +243,13 @@ struct FemtoPairTrackKink { pairTrackKinkHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); if (processSigma) { sigmaHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaBinning); - pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec); - } else { + pairTrackSigmaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaSelection, confSigmaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else if (processSigmaPlus) { sigmaPlusHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaPlusBinning); - pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec); + pairTrackSigmaPlusBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusSelection, confSigmaPlusCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + sigmaPlusPcmHistSpec = kinkhistmanager::makeKinkMcHistSpecMap(confSigmaPlusPcmBinning); + pairTrackSigmaPlusPcmBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, confSigmaPlusPcmSelection, confSigmaPlusPcmCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, sigmaPlusPcmHistSpec, chaDauSpec, pairTrackKinkHistSpec, cprHistSpec, pairCleanerHistSpec); } } hRegistry.print(); @@ -207,57 +257,81 @@ struct FemtoPairTrackKink { void processSigmaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmas const& sigmas) { - pairTrackSigmaBuilder.processSameEvent(col, tracks, trackPartition, sigmas, sigmaPartition, cache); + pairTrackSigmaBuilder.processSameEvent(col, tracks, trackPartition, sigmas, sigmaPartition, cache); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaSameEvent, "Enable processing same event processing for tracks and sigmas", true); - void processSigmaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmasWithLabel const& sigmas, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSigmaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmasWithLabel const& sigmas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackSigmaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, sigmas, sigmaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackSigmaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, sigmas, sigmaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaSameEventMc, "Enable processing same event processing for tracks and sigmas with MC information", false); void processSigmaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmas const& /*sigmas*/) { - pairTrackSigmaBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackSigmaBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaMixedEvent, "Enable processing mixed event processing for tracks and sigmas", true); - void processSigmaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmasWithLabel const& /*sigmas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSigmaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmasWithLabel const& /*sigmas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackSigmaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackSigmaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaMixedEventMc, "Enable processing mixed event processing for tracks and sigmas with MC information", false); void processSigmaPlusSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlus const& sigmaplus) { - pairTrackSigmaPlusBuilder.processSameEvent(col, tracks, trackPartition, sigmaplus, sigmaPlusPartition, cache); + pairTrackSigmaPlusBuilder.processSameEvent(col, tracks, trackPartition, sigmaplus, sigmaPlusPartition, cache); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusSameEvent, "Enable processing same event processing for tracks and sigma plus", false); - void processSigmaPlusSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusWithLabel const& sigmaplus, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSigmaPlusSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusWithLabel const& sigmaplus, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackSigmaPlusBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, sigmaplus, sigmaPlusWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackSigmaPlusBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, sigmaplus, sigmaPlusWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusSameEventMc, "Enable processing same event processing for tracks and sigma plus with MC information", false); void processSigmaPlusMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlus const& /*sigmaplus*/) { - pairTrackSigmaPlusBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackSigmaPlusBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusMixedEvent, "Enable processing mixed event processing for tracks and sigma plus", false); - void processSigmaPlusMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusWithLabel const& /*sigmaplus*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSigmaPlusMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusWithLabel const& /*sigmaplus*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackSigmaPlusBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaPlusWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackSigmaPlusBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaPlusWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusMixedEventMc, "Enable processing mixed event processing for tracks and sigma plus with MC information", false); + + void processSigmaPlusPcmSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& sigmapluspcm) + { + pairTrackSigmaPlusPcmBuilder.processSameEvent(col, tracks, trackPartition, sigmapluspcm, sigmaPlusPcmPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEvent, "Enable processing same event processing for tracks and sigma plus (PCM), selected on the mask and the BDT score", false); + + void processSigmaPlusPcmSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusPcmWithLabel const& sigmapluspcm, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackSigmaPlusPcmBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, sigmapluspcm, sigmaPlusPcmWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEventMc, "Enable processing same event processing for tracks and sigma plus (PCM) with MC information", false); + + void processSigmaPlusPcmMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) + { + pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEvent, "Enable processing mixed event processing for tracks and sigma plus (PCM), selected on the mask and the BDT score", false); + + void processSigmaPlusPcmMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoSigmaPlusPcmWithLabel const& /*sigmapluspcm*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, sigmaPlusPcmWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEventMc, "Enable processing mixed event processing for tracks and sigma plus (PCM) with MC information", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 688d95a4d0a..660f368891a 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" @@ -25,6 +26,7 @@ #include "PWGCF/Femto/Core/trackHistManager.h" #include "PWGCF/Femto/DataModel/FemtoTables.h" +#include #include #include #include @@ -53,12 +55,27 @@ struct FemtoPairTrackTrack { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // collision table with event shape, same filter as standard collisions + using FemtoCollisionsWithEventShape = o2::soa::Join; + using FilteredFemtoCollisionsWithEventShape = o2::soa::Filtered; + using FilteredFemtoCollisionWithEventShape = FilteredFemtoCollisionsWithEventShape::iterator; + using FemtoTracks = o2::soa::Join; // for analysis which require particles at high pt, add tof mass so sidebands can be used using FemtoTracksWithMass = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; + // opt-in: adds the packed purity tables (nSigma, see TrackSelection1/2.puritySlotMask) and FTrackDcas + // (fraction estimation) for the FitVars histograms. Needs the producer's + // TrackTables.produceItsPurity/produceTpcPurity/produceTofPurity/produceTrackDcas enabled. + using FemtoTracksWithFitVars = o2::soa::Join; + + // the Mc process functions always carry the purity/fraction tables (not opt-in like the data + // path's WithFitVars variant) -- requires the producer's TrackTables.produceItsPurity/ + // produceTpcPurity/produceTofPurity/produceTrackDcas enabled whenever MC processing is used + using FemtoTracksWithLabel = o2::soa::Join; + + using FemtoMcParticlesWithLabel = o2::soa::Join; o2::framework::SliceCache cache; @@ -84,6 +101,10 @@ struct FemtoPairTrackTrack { o2::framework::Partition trackWithMassPartition2 = MAKE_TRACK_PARTITION_WITH_MASS(confTrackSelections2); o2::framework::Preslice perColtracksWithMass = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); + o2::framework::Partition trackWithFitVarsPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); + o2::framework::Preslice perColtracksWithFitVars = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; @@ -91,12 +112,15 @@ struct FemtoPairTrackTrack { // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; closepairrejection::ConfCprTrackTrack confCpr; pairbuilder::PairTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, pairhistmanager::PrefixTrackTrackSe, pairhistmanager::PrefixTrackTrackMe, closepairrejection::PrefixTrackTrackSe, @@ -107,29 +131,34 @@ struct FemtoPairTrackTrack { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + std::vector defaultEventPlaneAngleBins{10, 0, o2::constants::math::TwoPI}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCentEventPlaneAngle{{defaultVtxBins, defaultCentBins, defaultEventPlaneAngleBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; void init(o2::framework::InitContext&) { - if ((doprocessSameEvent + doprocessSameEventWithMass + doprocessSameEventMc) > 1 || (doprocessMixedEvent + doprocessMixedEventWithMass + doprocessMixedEventMc) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventWithFitVars) + static_cast(doprocessSameEventMc)) > 1 || + (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithFitVars) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape; + bool processFitVars = doprocessSameEventWithFitVars || doprocessMixedEventWithFitVars; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is activated. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, purity or mc processing is activated. Breaking..."; } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + mixBinsVtxCentEventPlaneAngle = {{confMixing.vtxBins.value, confMixing.centBins.value, confMixing.eventPlaneAngle.value}}; // setup histogram specs std::map> colHistSpec; @@ -137,64 +166,95 @@ struct FemtoPairTrackTrack { std::map> trackHistSpec2; std::map> pairHistSpec; std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); + pairHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); + } else if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); pairHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); pairHistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec); + pairTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec1, trackHistSpec2, pairHistSpec, cprHistSpec, pairCleanerHistSpec); } hRegistry.print(); }; void processSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks) { - pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); + pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEvent, "Enable processing same event processing", true); void processSameEventWithMass(FilteredFemtoCollision const& col, FemtoTracksWithMass const& tracks) { - pairTrackTrackBuilder.processSameEvent(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache); + pairTrackTrackBuilder.processSameEvent(col, tracks, trackWithMassPartition1, trackWithMassPartition2, cache); } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithMass, "Enable processing same event processing (with track masses)", false); - void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks) + { + pairTrackTrackBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithFitVars, "Enable processing same event processing (with FitVars tables)", false); + + void processSameEventWithEventShape(FilteredFemtoCollisionWithEventShape const& col, FemtoTracks const& tracks) { - pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, cache); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventWithEventShape, "Enable processing same event processing with event shape information", false); + + void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventMc, "Enable processing same event processing", false); void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { - pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEvent, "Enable processing mixed event processing", true); void processMixedEventWithMass(FilteredFemtoCollisions const& cols, FemtoTracksWithMass const& tracks) { - pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithMass, "Enable processing mixed event processing (with track masses)", false); - void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks) + { + pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithFitVars, "Enable processing mixed event processing (with FitVars tables)", false); + + void processMixedEventWithEventShape(FilteredFemtoCollisionsWithEventShape const& cols, FemtoTracks const& tracks) + { + pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithEventShape, "Enable processing mixed event processing with event shape information", false); + + void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); } PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventMc, "Enable processing mixed event processing", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx new file mode 100644 index 00000000000..66a4cea7412 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx @@ -0,0 +1,212 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairTrackTwoPhotonResonance.cxx +/// \brief Task that computes correlation between tracks and resonances decaying into two photons (pi0, eta) +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPairTrackTwoPhotonResonance { + + // setup tables + using Collisions = o2::soa::Join; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup tracks + trackbuilder::ConfTrackSelection1 trackSelection; + trackhistmanager::ConfTrackBinning1 confTrackBinning; + o2::framework::Partition trackPartition = MAKE_TRACK_PARTITION(trackSelection); + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; + + // setup for photon daughters (shared between pi0 and eta -- unordered, no pos/neg or species asymmetry) + photonhistmanager::ConfPhotonBinning confPhotonDauBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonDauQaBinning; + + // setup pi0s + twophotonresonancebuilder::ConfPi0Selection pi0Selection; + twophotonresonancehistmanager::ConfPi0Binning confPi0Binning; + o2::framework::Partition pi0Partition = MAKE_TWOPHOTONRESONANCE_PARTITION(pi0Selection); + o2::framework::Preslice perColPi0s = o2::aod::femtobase::stored::fColId; + + // setup etas + twophotonresonancebuilder::ConfEtaSelection etaSelection; + twophotonresonancehistmanager::ConfEtaBinning confEtaBinning; + o2::framework::Partition etaPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(etaSelection); + o2::framework::Preslice perColEtas = o2::aod::femtobase::stored::fColId; + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + + // setup for track-pi0 pairs + pairbuilder::PairTrackTwoPhotonResonanceBuilder< + trackhistmanager::PrefixTrack1Se, + twophotonresonancehistmanager::PrefixPi0Se, + photonhistmanager::PrefixTwoPhotonResonanceDau1Se, + photonhistmanager::PrefixTwoPhotonResonanceDau2Se, + trackhistmanager::PrefixTrack1Me, + twophotonresonancehistmanager::PrefixPi0Me, + photonhistmanager::PrefixTwoPhotonResonanceDau1Me, + photonhistmanager::PrefixTwoPhotonResonanceDau2Me, + pairhistmanager::PrefixTrackPi0Se, + pairhistmanager::PrefixTrackPi0Me, + closepairrejection::PrefixTrackTwoPhotonResonanceSe, + closepairrejection::PrefixTrackTwoPhotonResonanceMe, + modes::TwoPhotonResonance::kPi0> + pairTrackPi0Builder; + + // setup for track-eta pairs + pairbuilder::PairTrackTwoPhotonResonanceBuilder< + trackhistmanager::PrefixTrack1Se, + twophotonresonancehistmanager::PrefixEtaSe, + photonhistmanager::PrefixTwoPhotonResonanceDau1Se, + photonhistmanager::PrefixTwoPhotonResonanceDau2Se, + trackhistmanager::PrefixTrack1Me, + twophotonresonancehistmanager::PrefixEtaMe, + photonhistmanager::PrefixTwoPhotonResonanceDau1Me, + photonhistmanager::PrefixTwoPhotonResonanceDau2Me, + pairhistmanager::PrefixTrackEtaSe, + pairhistmanager::PrefixTrackEtaMe, + closepairrejection::PrefixTrackTwoPhotonResonanceSe, + closepairrejection::PrefixTrackTwoPhotonResonanceMe, + modes::TwoPhotonResonance::kEta> + pairTrackEtaBuilder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + pairhistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTwoPhotonResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + // setup cpr (no-op for two-photon resonances: PCM photon daughters are not indexed into the track + // table, so there is no trajectory to compare against a track's -- see closePairRejection.h) + closepairrejection::ConfCprTrackTwoPhotonResonanceDaughter confCpr; + + void init(o2::framework::InitContext&) + { + + if (static_cast(doprocessPi0SameEvent || doprocessPi0MixedEvent) + static_cast(doprocessEtaSameEvent || doprocessEtaMixedEvent) > 1) { + LOG(fatal) << "Can only process SE/ME for pi0-tracks or eta-tracks"; + } + + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histograms + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + auto trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + auto dauHistSpec = photonhistmanager::makePhotonHistSpecMap(confPhotonDauBinning); + auto cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + // setup for pi0 + if (doprocessPi0SameEvent || doprocessPi0MixedEvent) { + auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confPi0Binning); + auto pairTrackPi0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackPi0Builder.init(&hRegistry, confCollisionBinning, trackSelection, pi0Selection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, pi0HistSpec, dauHistSpec, pairTrackPi0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + + // setup for eta + if (doprocessEtaSameEvent || doprocessEtaMixedEvent) { + auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confEtaBinning); + auto pairTrackEtaHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + pairTrackEtaBuilder.init(&hRegistry, confCollisionBinning, trackSelection, etaSelection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, etaHistSpec, dauHistSpec, pairTrackEtaHistSpec, cprHistSpec, pairCleanerHistSpec); + } + + hRegistry.print(); + }; + + void processPi0SameEvent(FilteredCollision const& col, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + pairTrackPi0Builder.processSameEvent(col, trackPartition, pi0Partition, photons, cache); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0SameEvent, "Enable same event processing for tracks and pi0s", true); + + void processPi0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + pairTrackPi0Builder.processMixedEvent(cols, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0MixedEvent, "Enable mixed event processing for tracks and pi0s", true); + + void processEtaSameEvent(FilteredCollision const& col, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + pairTrackEtaBuilder.processSameEvent(col, trackPartition, etaPartition, photons, cache); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaSameEvent, "Enable same event processing for tracks and etas", false); + + void processEtaMixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + pairTrackEtaBuilder.processMixedEvent(cols, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaMixedEvent, "Enable mixed event processing for tracks and etas", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index 371e2fa0aaf..25602952698 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/partitions.h" #include "PWGCF/Femto/Core/trackBuilder.h" @@ -40,39 +41,34 @@ #include #include -using namespace o2; -using namespace o2::aod; -using namespace o2::soa; -using namespace o2::framework; -using namespace o2::framework::expressions; using namespace o2::analysis::femto; struct FemtoPairTrackTwoTrackResonance { // setup tables - using Collisions = Join; + using Collisions = o2::soa::Join; using Collision = Collisions::iterator; using FilteredCollisions = o2::soa::Filtered; using FilteredCollision = FilteredCollisions::iterator; - using Tracks = o2::soa::Join; - using Phis = o2::soa::Join; - using Kstar0s = o2::soa::Join; - using Rho0s = o2::soa::Join; + using FemtoTracks = o2::soa::Join; + using FemtoPhis = o2::soa::Join; + using FemtoKstar0s = o2::soa::Join; + using FemtoRho0s = o2::soa::Join; - SliceCache cache; + o2::framework::SliceCache cache; // setup collisions collisionbuilder::ConfCollisionSelection collisionSelection; - Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; // setup tracks trackbuilder::ConfTrackSelection1 trackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; - Partition trackPartition = MAKE_TRACK_PARTITION(trackSelection); - Preslice perColTracks = aod::femtobase::stored::fColId; + o2::framework::Partition trackPartition = MAKE_TRACK_PARTITION(trackSelection); + o2::framework::Preslice perColTracks = o2::aod::femtobase::stored::fColId; // setup for daughters trackhistmanager::ConfResonancePosDauBinning confPosDauBinning; @@ -81,31 +77,36 @@ struct FemtoPairTrackTwoTrackResonance { // setup phis twotrackresonancebuilder::ConfPhiSelection phiSelection; twotrackresonancehistmanager::ConfPhiBinning confPhiBinning; - Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); - Preslice perColPhis = aod::femtobase::stored::fColId; + o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); + o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; // setup kstar0 twotrackresonancebuilder::ConfKstar0Selection kstar0Selection; twotrackresonancehistmanager::ConfKstar0Binning confKstar0Binning; - Partition kstar0Partition = MAKE_RESONANCE_1_PARTITON(kstar0Selection); - Preslice perColKstar0s = aod::femtobase::stored::fColId; + o2::framework::Partition kstar0Partition = MAKE_RESONANCE_1_PARTITON(kstar0Selection); + o2::framework::Preslice perColKstar0s = o2::aod::femtobase::stored::fColId; // rho0s twotrackresonancebuilder::ConfRho0Selection rho0Selection; twotrackresonancehistmanager::ConfRho0Binning confRho0Binning; - Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); - Preslice perColRho0s = aod::femtobase::stored::fColId; + o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); + o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; // setup for track-phi pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixPhi, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -115,10 +116,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-kstar0 pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixKstar, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -128,10 +133,14 @@ struct FemtoPairTrackTwoTrackResonance { // setup for track-rho0 pairs pairbuilder::PairTrackTwoTrackResonanceBuilder< - trackhistmanager::PrefixTrack1, - twotrackresonancehistmanager::PrefixRho, - trackhistmanager::PrefixResonancePosDaughter, - trackhistmanager::PrefixResonanceNegDaughter, + trackhistmanager::PrefixTrack1Se, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, pairhistmanager::PrefixTrackResonanceSe, pairhistmanager::PrefixTrackResonanceMe, closepairrejection::PrefixTrackTwoTrackResonanceSe, @@ -143,28 +152,28 @@ struct FemtoPairTrackTwoTrackResonance { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; - HistogramRegistry hRegistry{"FemtoTrackTwoTrackResonance", {}, OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTwoTrackResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; // setup cpr closepairrejection::ConfCprTrackResonanceDaughter confCpr; - void init(InitContext&) + void init(o2::framework::InitContext&) { - if (((doprocessPhiSameEvent || doprocessPhiMixedEvent) + (doprocessKstar0SameEvent || doprocessKstar0MixedEvent)) + (doprocessRho0SameEvent || doprocessRho0MixedEvent) > 1) { - LOG(fatal) << "Can only process phi-tracks, rho-tracks or k0*-tracks"; + if ((static_cast(doprocessPhiSameEvent || doprocessPhiMixedEvent) + static_cast(doprocessKstar0SameEvent || doprocessKstar0MixedEvent)) + static_cast(doprocessRho0SameEvent || doprocessRho0MixedEvent) > 1) { + LOG(fatal) << "Can only process SE/ME for phi-tracks, rho-tracks or k0*-tracks"; } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); @@ -172,70 +181,73 @@ struct FemtoPairTrackTwoTrackResonance { auto posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); auto negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); auto cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for phi if (doprocessPhiSameEvent || doprocessPhiMixedEvent) { auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); auto pairTrackPhiHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairTrackPhiBuilder.init(&hRegistry, confCollisionBinning, trackSelection, phiSelection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, phiHistSpec, posDauSpec, negDauSpec, pairTrackPhiHistSpec, cprHistSpec); + pairTrackPhiBuilder.init(&hRegistry, confCollisionBinning, trackSelection, phiSelection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, phiHistSpec, posDauSpec, negDauSpec, pairTrackPhiHistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 if (doprocessKstar0SameEvent || doprocessKstar0MixedEvent) { auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); auto pairTrackKstar0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairTrackKstar0Builder.init(&hRegistry, confCollisionBinning, trackSelection, kstar0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, kstar0HistSpec, posDauSpec, negDauSpec, pairTrackKstar0HistSpec, cprHistSpec); + pairTrackKstar0Builder.init(&hRegistry, confCollisionBinning, trackSelection, kstar0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, kstar0HistSpec, posDauSpec, negDauSpec, pairTrackKstar0HistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 if (doprocessRho0SameEvent || doprocessRho0MixedEvent) { auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); auto pairTrackRho0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairTrackRho0Builder.init(&hRegistry, confCollisionBinning, trackSelection, rho0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, rho0HistSpec, posDauSpec, negDauSpec, pairTrackRho0HistSpec, cprHistSpec); + pairTrackRho0Builder.init(&hRegistry, confCollisionBinning, trackSelection, rho0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, rho0HistSpec, posDauSpec, negDauSpec, pairTrackRho0HistSpec, cprHistSpec, pairCleanerHistSpec); } + + hRegistry.print(); }; - void processPhiSameEvent(FilteredCollision const& col, Tracks const& tracks, Phis const& phis) + void processPhiSameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoPhis const& phis) { - pairTrackPhiBuilder.processSameEvent(col, tracks, trackPartition, phis, phiPartition, cache); + pairTrackPhiBuilder.processSameEvent(col, tracks, trackPartition, phis, phiPartition, cache); } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiSameEvent, "Enable processing same event processing for tracks and phis", true); - void processPhiMixedEvent(FilteredCollisions const& cols, Tracks const& tracks, Phis const& /*phis*/) + void processPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoPhis const& /*phis*/) { - pairTrackPhiBuilder.processMixedEvent(cols, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackPhiBuilder.processMixedEvent(cols, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiMixedEvent, "Enable processing mixed event processing for tracks and phis", true); - void processKstar0SameEvent(FilteredCollision const& col, Tracks const& tracks, Kstar0s const& kstar0s) + void processKstar0SameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoKstar0s const& kstar0s) { - pairTrackKstar0Builder.processSameEvent(col, tracks, trackPartition, kstar0s, kstar0Partition, cache); + pairTrackKstar0Builder.processSameEvent(col, tracks, trackPartition, kstar0s, kstar0Partition, cache); } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0SameEvent, "Enable processing same event processing for tracks and kstar0s", false); - void processKstar0MixedEvent(FilteredCollisions const& cols, Tracks const& tracks, Kstar0s const& /*kstar0s*/) + void processKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoKstar0s const& /*kstar0s*/) { - pairTrackKstar0Builder.processMixedEvent(cols, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackKstar0Builder.processMixedEvent(cols, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0MixedEvent, "Enable processing mixed event processing for tracks and kstar0s", false); - void processRho0SameEvent(FilteredCollision const& col, Tracks const& tracks, Rho0s const& rho0s) + void processRho0SameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoRho0s const& rho0s) { - pairTrackRho0Builder.processSameEvent(col, tracks, trackPartition, rho0s, rho0Partition, cache); + pairTrackRho0Builder.processSameEvent(col, tracks, trackPartition, rho0s, rho0Partition, cache); } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0SameEvent, "Enable processing same event processing for tracks and rho0s", false); - void processRho0MixedEvent(FilteredCollisions const& cols, Tracks const& tracks, Rho0s const& /*rho0s*/) + void processRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoRho0s const& /*rho0s*/) { - pairTrackRho0Builder.processMixedEvent(cols, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackRho0Builder.processMixedEvent(cols, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0MixedEvent, "Enable processing mixed event processing for tracks and rho0s", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 6747aabb561..054e8bc40cf 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" @@ -59,9 +60,16 @@ struct FemtoPairTrackV0 { using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; - using FemtoTracksWithLabel = o2::soa::Join; - using FemtoLambdasWithLabel = o2::soa::Join; - using FemtoK0shortsWithLabel = o2::soa::Join; + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoLambdasWithLabel = o2::soa::Join; + using FemtoK0shortsWithLabel = o2::soa::Join; + + // reco-only + FitVars (no MC labels, but carries the opt-in purity/fraction tables) + using FemtoTracksWithFitVars = o2::soa::Join; + using FemtoLambdasWithFitVars = o2::soa::Join; + using FemtoK0shortsWithFitVars = o2::soa::Join; + + using FemtoMcParticlesWithLabel = o2::soa::Join; o2::framework::SliceCache cache; @@ -81,9 +89,12 @@ struct FemtoPairTrackV0 { o2::framework::Partition trackWithLabelPartition = MAKE_TRACK_PARTITION(confTrackSelection); o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition trackWithFitVarsPartition = MAKE_TRACK_PARTITION(confTrackSelection); + o2::framework::Preslice perColTracksWithFitVars = o2::aod::femtobase::stored::fColId; + // setup for daughters - trackhistmanager::ConfV0PosDauBinning confPosDauBinning; - trackhistmanager::ConfV0NegDauBinning confNegDauBinning; + trackhistmanager::ConfV01PosDauBinning confPosDauBinning; + trackhistmanager::ConfV01NegDauBinning confNegDauBinning; // setup lambdas v0builder::ConfLambdaSelection1 lambdaSelection; @@ -96,6 +107,9 @@ struct FemtoPairTrackV0 { o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(lambdaSelection); o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lambdaWithFitVarsPartition = MAKE_LAMBDA_PARTITION(lambdaSelection); + o2::framework::Preslice perColLambdasWithFitVars = o2::aod::femtobase::stored::fColId; + // setup k0shorts v0builder::ConfK0shortSelection1 k0shortSelection; v0histmanager::ConfK0shortBinning1 confK0shortBinning; @@ -107,15 +121,23 @@ struct FemtoPairTrackV0 { o2::framework::Partition k0shortWithLabelPartition = MAKE_K0SHORT_PARTITION(k0shortSelection); o2::framework::Preslice perColk0shortsWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortWithFitVarsPartition = MAKE_K0SHORT_PARTITION(k0shortSelection); + o2::framework::Preslice perColk0shortsWithFitVars = o2::aod::femtobase::stored::fColId; + // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, @@ -124,10 +146,14 @@ struct FemtoPairTrackV0 { pairTrackLambdaBuilder; pairbuilder::PairTrackV0Builder< - trackhistmanager::PrefixTrack1, - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, + trackhistmanager::PrefixTrack1Se, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, pairhistmanager::PrefixTrackV0Se, pairhistmanager::PrefixTrackV0Me, closepairrejection::PrefixTrackV0DaughterSe, @@ -139,9 +165,9 @@ struct FemtoPairTrackV0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -152,14 +178,15 @@ struct FemtoPairTrackV0 { void init(o2::framework::InitContext&) { bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent; + bool processFitVars = doprocessLambdaSameEventWithFitVars || doprocessLambdaMixedEventWithFitVars || doprocessK0shortSameEventWithFitVars || doprocessK0shortMixedEventWithFitVars; bool processMc = doprocessLambdaSameEventMc || doprocessLambdaMixedEventMc || doprocessK0shortSameEventMc || doprocessK0shortMixedEventMc; - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; + if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { + LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventMc; - bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventMc; + bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventWithFitVars || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventWithFitVars || doprocessLambdaMixedEventMc; + bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventWithFitVars || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventWithFitVars || doprocessK0shortMixedEventMc; if (processLambda && processK0short) { LOG(fatal) << "Both lambda-track and k0short-track processing is enabled. Breaking..."; @@ -167,9 +194,9 @@ struct FemtoPairTrackV0 { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; std::map> colHistSpec; std::map> trackHistSpec; @@ -179,8 +206,9 @@ struct FemtoPairTrackV0 { std::map> k0shortHistSpec; std::map> pairTrackV0HistSpec; std::map> cprHistSpec = closepairrejection::makeCprHistSpecMap(confCpr); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); - if (processData) { + if (processFitVars) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); @@ -188,10 +216,23 @@ struct FemtoPairTrackV0 { pairTrackV0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); if (processLambda) { lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); - pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec); + pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } else { k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); - pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confTrackCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec); + pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + } + } else if (processData) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec = trackhistmanager::makeTrackHistSpecMap(confTrackBinning); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + pairTrackV0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processLambda) { + lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); + } else { + k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); + pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); @@ -201,10 +242,10 @@ struct FemtoPairTrackV0 { pairTrackV0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); if (processLambda) { lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); - pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec); + pairTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } else { k0shortHistSpec = v0histmanager::makeV0McHistSpecMap(confK0shortBinning); - pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, lambdaSelection, confLambdaCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec); + pairTrackK0shortBuilder.init(&hRegistry, confCollisionBinning, confTrackSelection, confTrackCleaner, k0shortSelection, confK0shortCleaner, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairTrackV0HistSpec, cprHistSpec, pairCleanerHistSpec); } } hRegistry.print(); @@ -212,57 +253,81 @@ struct FemtoPairTrackV0 { void processLambdaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) { - pairTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition, lambdas, lambdaPartition, cache); + pairTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition, lambdas, lambdaPartition, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEvent, "Enable processing same event processing for tracks and lambdas", true); - void processLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, lambdas, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventMc, "Enable processing same event processing for tracks and lambdas with MC information", false); void processLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambas*/) { - pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEvent, "Enable processing mixed event processing for tracks and lambdas", true); - void processLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventMc, "Enable processing mixed event processing for tracks and lambdas with MC information", false); + void processLambdaSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoLambdasWithFitVars const& lambdas) + { + pairTrackLambdaBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, lambdas, lambdaWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventWithFitVars, "Enable processing same event processing for tracks and lambdas, carrying through FitVars tables", false); + + void processLambdaMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoLambdasWithFitVars const& /*lambas*/) + { + pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, lambdaWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventWithFitVars, "Enable processing mixed event processing for tracks and lambdas, carrying through FitVars tables", false); + void processK0shortSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) { - pairTrackK0shortBuilder.processSameEvent(col, tracks, trackPartition, k0shorts, k0shortPartition, cache); + pairTrackK0shortBuilder.processSameEvent(col, tracks, trackPartition, k0shorts, k0shortPartition, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEvent, "Enable processing same event processing for tracks and k0shorts", false); - void processK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackK0shortBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, k0shorts, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairTrackK0shortBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition, k0shorts, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventMc, "Enable processing same event processing for tracks and k0shorts with MC information", false); void processK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { - pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEvent, "Enable processing mixed event processing for tracks and k0shorts", false); - void processK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairTrackK0shortBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairTrackK0shortBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventMc, "Enable processing mixed event processing for tracks and k0shorts with mc information", false); + + void processK0shortSameEventWithFitVars(FilteredFemtoCollision const& col, FemtoTracksWithFitVars const& tracks, FemtoK0shortsWithFitVars const& k0shorts) + { + pairTrackK0shortBuilder.processSameEvent(col, tracks, trackWithFitVarsPartition, k0shorts, k0shortWithFitVarsPartition, cache); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventWithFitVars, "Enable processing same event processing for tracks and k0shorts, carrying through FitVars tables", false); + + void processK0shortMixedEventWithFitVars(FilteredFemtoCollisions const& cols, FemtoTracksWithFitVars const& tracks, FemtoK0shortsWithFitVars const& /*k0shorts*/) + { + pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackWithFitVarsPartition, k0shortWithFitVarsPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventWithFitVars, "Enable processing mixed event processing for tracks and k0shorts, carrying through FitVars tables", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx new file mode 100644 index 00000000000..bfb115b7d87 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -0,0 +1,446 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPairV0TwoTrackResonance.cxx +/// \brief Tasks that computes correlation between v0s and resonances decaying into two tracks +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/pairHistManager.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/twoTrackResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoTrackResonanceHistManager.h" +#include "PWGCF/Femto/Core/v0Builder.h" +#include "PWGCF/Femto/Core/v0HistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoPairV0TwoTrackResonance { + + // setup tables + using Collisions = o2::soa::Join; + using Collision = Collisions::iterator; + + using FilteredCollisions = o2::soa::Filtered; + using FilteredCollision = FilteredCollisions::iterator; + + using FemtoTracks = o2::soa::Join; + + using FemtoLambdas = o2::soa::Join; + using FemtoK0shorts = o2::soa::Join; + + using FemtoPhis = o2::soa::Join; + using FemtoKstar0s = o2::soa::Join; + using FemtoRho0s = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup for daughters for v0s + trackhistmanager::ConfV01PosDauBinning confV0PosDauBinning; + trackhistmanager::ConfV01NegDauBinning confV0NegDauBinning; + + // setup lambdas + v0builder::ConfLambdaSelection1 lambdaSelection; + v0histmanager::ConfLambdaBinning1 confLambdaBinning; + particlecleaner::ConfLambdaCleaner1 confLambdaCleaner; + + o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(lambdaSelection); + o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; + + // setup k0shorts + v0builder::ConfK0shortSelection1 k0shortSelection; + v0histmanager::ConfK0shortBinning1 confK0shortBinning; + particlecleaner::ConfK0shortCleaner1 confK0shortCleaner; + + o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(k0shortSelection); + o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; + + // setup for daughters for resonances + trackhistmanager::ConfResonancePosDauBinning confResoPosDauBinning; + trackhistmanager::ConfResonanceNegDauBinning confResoNegDauBinning; + + // setup phis + twotrackresonancebuilder::ConfPhiSelection phiSelection; + twotrackresonancehistmanager::ConfPhiBinning confPhiBinning; + o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(phiSelection); + o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; + + // setup kstar0 + twotrackresonancebuilder::ConfKstar0Selection kstar0Selection; + twotrackresonancehistmanager::ConfKstar0Binning confKstar0Binning; + o2::framework::Partition kstar0Partition = MAKE_RESONANCE_1_PARTITON(kstar0Selection); + o2::framework::Preslice perColKstar0s = o2::aod::femtobase::stored::fColId; + + // setup rho0s + twotrackresonancebuilder::ConfRho0Selection rho0Selection; + twotrackresonancehistmanager::ConfRho0Binning confRho0Binning; + o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(rho0Selection); + o2::framework::Preslice perColRho0s = o2::aod::femtobase::stored::fColId; + + // setup pairs + pairhistmanager::ConfPairBinning confPairBinning; + pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; + + // setup for lambda-phi pairs + pairbuilder::PairV0TwoTrackResonanceBuilder< + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, + pairhistmanager::PrefixV0ResonanceSe, + pairhistmanager::PrefixV0ResonanceMe, + closepairrejection::PrefixV0TwoTrackResonancePosSe, + closepairrejection::PrefixV0TwoTrackResonanceNegSe, + closepairrejection::PrefixV0TwoTrackResonancePosMe, + closepairrejection::PrefixV0TwoTrackResonanceNegMe, + modes::V0::kLambda, + modes::TwoTrackResonance::kPhi> + pairLambdaPhiBuilder; + + // setup for lambda-kstar0 pairs + pairbuilder::PairV0TwoTrackResonanceBuilder< + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, + pairhistmanager::PrefixV0ResonanceSe, + pairhistmanager::PrefixV0ResonanceMe, + closepairrejection::PrefixV0TwoTrackResonancePosSe, + closepairrejection::PrefixV0TwoTrackResonanceNegSe, + closepairrejection::PrefixV0TwoTrackResonancePosMe, + closepairrejection::PrefixV0TwoTrackResonanceNegMe, + modes::V0::kLambda, + modes::TwoTrackResonance::kKstar0> + pairLambdaKstar0Builder; + + // setup for lambda-rho0 pairs + pairbuilder::PairV0TwoTrackResonanceBuilder< + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, + pairhistmanager::PrefixV0ResonanceSe, + pairhistmanager::PrefixV0ResonanceMe, + closepairrejection::PrefixV0TwoTrackResonancePosSe, + closepairrejection::PrefixV0TwoTrackResonanceNegSe, + closepairrejection::PrefixV0TwoTrackResonancePosMe, + closepairrejection::PrefixV0TwoTrackResonanceNegMe, + modes::V0::kLambda, + modes::TwoTrackResonance::kRho0> + pairLambdaRho0Builder; + + // setup for k0short-phi pairs + pairbuilder::PairV0TwoTrackResonanceBuilder< + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixPhiSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixPhiMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, + pairhistmanager::PrefixV0ResonanceSe, + pairhistmanager::PrefixV0ResonanceMe, + closepairrejection::PrefixV0TwoTrackResonancePosSe, + closepairrejection::PrefixV0TwoTrackResonanceNegSe, + closepairrejection::PrefixV0TwoTrackResonancePosMe, + closepairrejection::PrefixV0TwoTrackResonanceNegMe, + modes::V0::kK0short, + modes::TwoTrackResonance::kPhi> + pairK0shortPhiBuilder; + + // setup for k0short-kstar0 pairs + pairbuilder::PairV0TwoTrackResonanceBuilder< + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixKstarSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixKstarMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, + pairhistmanager::PrefixV0ResonanceSe, + pairhistmanager::PrefixV0ResonanceMe, + closepairrejection::PrefixV0TwoTrackResonancePosSe, + closepairrejection::PrefixV0TwoTrackResonanceNegSe, + closepairrejection::PrefixV0TwoTrackResonancePosMe, + closepairrejection::PrefixV0TwoTrackResonanceNegMe, + modes::V0::kK0short, + modes::TwoTrackResonance::kKstar0> + pairK0shortKstar0Builder; + + // setup for k0short-rho0 pairs + pairbuilder::PairV0TwoTrackResonanceBuilder< + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + twotrackresonancehistmanager::PrefixRhoSe, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + twotrackresonancehistmanager::PrefixRhoMe, + trackhistmanager::PrefixResonancePosDaughterMe, + trackhistmanager::PrefixResonanceNegDaughterMe, + pairhistmanager::PrefixV0ResonanceSe, + pairhistmanager::PrefixV0ResonanceMe, + closepairrejection::PrefixV0TwoTrackResonancePosSe, + closepairrejection::PrefixV0TwoTrackResonanceNegSe, + closepairrejection::PrefixV0TwoTrackResonancePosMe, + closepairrejection::PrefixV0TwoTrackResonanceNegMe, + modes::V0::kK0short, + modes::TwoTrackResonance::kRho0> + pairK0shortRho0Builder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + + pairhistmanager::ConfMixing confMixing; + + // setup cpr + closepairrejection::ConfCprV0DaughterResoDaughterPos confCprPos; + closepairrejection::ConfCprV0DaughterResoDaughterNeg confCprNeg; + + o2::framework::HistogramRegistry hRegistry{"FemtoV0TwoTrackResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + + // all pair combinations share the same histogram prefixes, so only one combination can be processed per workflow instance + if (static_cast(doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) + + static_cast(doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) + + static_cast(doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) + + static_cast(doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) + + static_cast(doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) + + static_cast(doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) > + 1) { + LOG(fatal) << "Can only process one v0-resonance combination (lambda/k0short x phi/kstar0/rho0)"; + } + + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histograms + auto colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + + auto v0PosDauSpec = trackhistmanager::makeTrackHistSpecMap(confV0PosDauBinning); + auto v0NegDauSpec = trackhistmanager::makeTrackHistSpecMap(confV0NegDauBinning); + + auto resoPosDauSpec = trackhistmanager::makeTrackHistSpecMap(confResoPosDauBinning); + auto resoNegDauSpec = trackhistmanager::makeTrackHistSpecMap(confResoNegDauBinning); + + auto cprHistSpecPos = closepairrejection::makeCprHistSpecMap(confCprPos); + auto cprHistSpecNeg = closepairrejection::makeCprHistSpecMap(confCprNeg); + + auto pairV0TwoTrackResonanceHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + + auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); + + // setup for lambda-phi + if (doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) { + auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); + pairLambdaPhiBuilder.init(&hRegistry, confCollisionBinning, lambdaSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + // setup for lambda-kstar0 + if (doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) { + auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); + pairLambdaKstar0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + // setup for lambda-rho0 + if (doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) { + auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); + auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); + pairLambdaRho0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + // setup for k0short-phi + if (doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) { + auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); + auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); + pairK0shortPhiBuilder.init(&hRegistry, confCollisionBinning, k0shortSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + // setup for k0short-kstar0 + if (doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) { + auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); + auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); + pairK0shortKstar0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + // setup for k0short-rho0 + if (doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) { + auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); + auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); + pairK0shortRho0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + hRegistry.print(); + } + + // lambda-phi + void processLambdaPhiSameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) + { + pairLambdaPhiBuilder.processSameEvent(col, tracks, lambdaPartition, phiPartition, cache); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiSameEvent, "Enable processing same event processing for lambdas and phis", true); + + void processLambdaPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) + { + pairLambdaPhiBuilder.processMixedEvent(cols, tracks, lambdaPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiMixedEvent, "Enable processing mixed event processing for lambdas and phis", true); + + // lambda-kstar0 + void processLambdaKstar0SameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) + { + pairLambdaKstar0Builder.processSameEvent(col, tracks, lambdaPartition, kstar0Partition, cache); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0SameEvent, "Enable processing same event processing for lambdas and kstar0s", false); + + void processLambdaKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) + { + pairLambdaKstar0Builder.processMixedEvent(cols, tracks, lambdaPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0MixedEvent, "Enable processing mixed event processing for lambdas and kstar0s", false); + + // lambda-rho0 + void processLambdaRho0SameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) + { + pairLambdaRho0Builder.processSameEvent(col, tracks, lambdaPartition, rho0Partition, cache); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0SameEvent, "Enable processing same event processing for lambdas and rho0s", false); + + void processLambdaRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) + { + pairLambdaRho0Builder.processMixedEvent(cols, tracks, lambdaPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0MixedEvent, "Enable processing mixed event processing for lambdas and rho0s", false); + + // k0short-phi + void processK0shortPhiSameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) + { + pairK0shortPhiBuilder.processSameEvent(col, tracks, k0shortPartition, phiPartition, cache); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiSameEvent, "Enable processing same event processing for k0shorts and phis", false); + + void processK0shortPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) + { + pairK0shortPhiBuilder.processMixedEvent(cols, tracks, k0shortPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiMixedEvent, "Enable processing mixed event processing for k0shorts and phis", false); + + // k0short-kstar0 + void processK0shortKstar0SameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) + { + pairK0shortKstar0Builder.processSameEvent(col, tracks, k0shortPartition, kstar0Partition, cache); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0SameEvent, "Enable processing same event processing for k0shorts and kstar0s", false); + + void processK0shortKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) + { + pairK0shortKstar0Builder.processMixedEvent(cols, tracks, k0shortPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0MixedEvent, "Enable processing mixed event processing for k0shorts and kstar0s", false); + + // k0short-rho0 + void processK0shortRho0SameEvent(FilteredCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) + { + pairK0shortRho0Builder.processSameEvent(col, tracks, k0shortPartition, rho0Partition, cache); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0SameEvent, "Enable processing same event processing for k0shorts and rho0s", false); + + void processK0shortRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) + { + pairK0shortRho0Builder.processMixedEvent(cols, tracks, k0shortPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0MixedEvent, "Enable processing mixed event processing for k0shorts and rho0s", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index 02cedd702f5..371102a368a 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" @@ -43,6 +44,13 @@ using namespace o2::analysis::femto; +// Each pair slot (1 and 2) has its own selection, cleaner, binning and partition, so that +// any combination of v0s can be correlated: +// lambda-lambda : LambdaSelection1 == LambdaSelection2, Mixing.sameSpecies = true +// lambda-antilambda : LambdaSelection1/2 differ only in sign, Mixing.sameSpecies = false +// k0short-k0short : K0shortSelection1 == K0shortSelection2, Mixing.sameSpecies = true +// lambda-k0short : LambdaSelection1 and K0shortSelection2, Mixing.sameSpecies = false +// Slot N always uses selection N, cleaner N, binning N and partition N. struct FemtoPairV0V0 { // setup tables @@ -54,14 +62,16 @@ struct FemtoPairV0V0 { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; - using FemtoTracks = o2::soa::Join; + using FemtoTracks = o2::aod::FTracks; using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; using FemtoLambdasWithLabel = o2::soa::Join; using FemtoK0shortsWithLabel = o2::soa::Join; - // + + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup collisions @@ -70,42 +80,63 @@ struct FemtoPairV0V0 { colhistmanager::ConfCollisionBinning confCollisionBinning; // setup for daughters - trackhistmanager::ConfV0PosDauBinning confPosDauBinning; - trackhistmanager::ConfV0NegDauBinning confNegDauBinning; + // separate binning per v0 slot, so that e.g. lambda (proton/pion daughters) and + // k0short (pion/pion daughters) can be binned independently + trackhistmanager::ConfV01PosDauBinning confV01PosDauBinning; + trackhistmanager::ConfV01NegDauBinning confV01NegDauBinning; + trackhistmanager::ConfV02PosDauBinning confV02PosDauBinning; + trackhistmanager::ConfV02NegDauBinning confV02NegDauBinning; // setup lambdas - v0builder::ConfLambdaSelection1 confLambdaSelection; - particlecleaner::ConfLambdaCleaner1 confLambdaCleaner; - v0histmanager::ConfLambdaBinning1 confLambdaBinning; - - o2::framework::Partition lambdaPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); + v0builder::ConfLambdaSelection1 confLambdaSelection1; + v0builder::ConfLambdaSelection2 confLambdaSelection2; + particlecleaner::ConfLambdaCleaner1 confLambdaCleaner1; + particlecleaner::ConfLambdaCleaner2 confLambdaCleaner2; + v0histmanager::ConfLambdaBinning1 confLambdaBinning1; + v0histmanager::ConfLambdaBinning2 confLambdaBinning2; + + o2::framework::Partition lambdaPartition1 = MAKE_LAMBDA_PARTITION(confLambdaSelection1); + o2::framework::Partition lambdaPartition2 = MAKE_LAMBDA_PARTITION(confLambdaSelection2); o2::framework::Preslice perColLambdas = o2::aod::femtobase::stored::fColId; - o2::framework::Partition lambdaWithLabelPartition = MAKE_LAMBDA_PARTITION(confLambdaSelection); - o2::framework::Preslice perCollambdasWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition lambdaWithLabelPartition1 = MAKE_LAMBDA_PARTITION(confLambdaSelection1); + o2::framework::Partition lambdaWithLabelPartition2 = MAKE_LAMBDA_PARTITION(confLambdaSelection2); + o2::framework::Preslice perColLambdasWithLabel = o2::aod::femtobase::stored::fColId; // setup k0shorts - v0builder::ConfK0shortSelection1 confK0shortSelection; - particlecleaner::ConfK0shortCleaner1 confK0shortCleaner; - v0histmanager::ConfK0shortBinning1 confK0shortBinning; + v0builder::ConfK0shortSelection1 confK0shortSelection1; + v0builder::ConfK0shortSelection2 confK0shortSelection2; + particlecleaner::ConfK0shortCleaner1 confK0shortCleaner1; + particlecleaner::ConfK0shortCleaner2 confK0shortCleaner2; + v0histmanager::ConfK0shortBinning1 confK0shortBinning1; + v0histmanager::ConfK0shortBinning2 confK0shortBinning2; - o2::framework::Partition k0shortPartition = MAKE_K0SHORT_PARTITION(confK0shortSelection); - o2::framework::Preslice perColk0shorts = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortPartition1 = MAKE_K0SHORT_PARTITION(confK0shortSelection1); + o2::framework::Partition k0shortPartition2 = MAKE_K0SHORT_PARTITION(confK0shortSelection2); + o2::framework::Preslice perColK0shorts = o2::aod::femtobase::stored::fColId; - o2::framework::Partition k0shortWithLabelPartition = MAKE_K0SHORT_PARTITION(confK0shortSelection); - o2::framework::Preslice perColk0shortsWithLabel = o2::aod::femtobase::stored::fColId; + o2::framework::Partition k0shortWithLabelPartition1 = MAKE_K0SHORT_PARTITION(confK0shortSelection1); + o2::framework::Partition k0shortWithLabelPartition2 = MAKE_K0SHORT_PARTITION(confK0shortSelection2); + o2::framework::Preslice perColK0shortsWithLabel = o2::aod::femtobase::stored::fColId; // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixLambda2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixLambda2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixLambda2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -117,12 +148,18 @@ struct FemtoPairV0V0 { pairLambdaLambdaBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -133,13 +170,36 @@ struct FemtoPairV0V0 { modes::V0::kK0short> pairK0shortK0shortBuilder; + pairbuilder::PairV0V0Builder< + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, + pairhistmanager::PrefixV0V0Se, + pairhistmanager::PrefixV0V0Me, + closepairrejection::PrefixV0V0PosSe, + closepairrejection::PrefixV0V0NegSe, + closepairrejection::PrefixV0V0PosMe, + closepairrejection::PrefixV0V0NegMe, + modes::V0::kLambda, + modes::V0::kK0short> + pairLambdaK0shortBuilder; + // setup mixing std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoV0V0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -150,125 +210,183 @@ struct FemtoPairV0V0 { void init(o2::framework::InitContext&) { - // TODO: implement lambda-k0short - bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent; - bool processMc = doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; + bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || + doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || + doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent; + bool processMc = doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc || + doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc || + doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; } + if (!processData && !processMc) { + LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; + } - bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc; - bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; + bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || + doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc; + bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || + doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; + bool processLambdaK0short = doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || + doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; - if (processLambdaLambda && processK0shortK0short) { - LOG(fatal) << "Both lambda-lambda and k0short-k0short processing is enabled. Breaking..."; + if (static_cast(processLambdaLambda) + static_cast(processK0shortK0short) + static_cast(processLambdaK0short) > 1) { + LOG(fatal) << "Only one of lambda-lambda, k0short-k0short or lambda-k0short processing can be enabled. Breaking..."; } // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; - std::map> posDauSpec; - std::map> negDauSpec; - std::map> lambdaHistSpec; - std::map> k0shortHistSpec; + std::map> posDauSpec1; + std::map> negDauSpec1; + std::map> posDauSpec2; + std::map> negDauSpec2; + std::map> lambdaHistSpec1; + std::map> lambdaHistSpec2; + std::map> k0shortHistSpec1; + std::map> k0shortHistSpec2; std::map> pairV0V0HistSpec; std::map> cprHistSpecPos = closepairrejection::makeCprHistSpecMap(confCprPos); std::map> cprHistSpecNeg = closepairrejection::makeCprHistSpecMap(confCprNeg); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); - posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); - negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + posDauSpec1 = trackhistmanager::makeTrackHistSpecMap(confV01PosDauBinning); + negDauSpec1 = trackhistmanager::makeTrackHistSpecMap(confV01NegDauBinning); + posDauSpec2 = trackhistmanager::makeTrackHistSpecMap(confV02PosDauBinning); + negDauSpec2 = trackhistmanager::makeTrackHistSpecMap(confV02NegDauBinning); + pairV0V0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processLambdaLambda) { - lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); - pairV0V0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection, confLambdaSelection, confLambdaCleaner, confLambdaCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + lambdaHistSpec1 = v0histmanager::makeV0HistSpecMap(confLambdaBinning1); + lambdaHistSpec2 = v0histmanager::makeV0HistSpecMap(confLambdaBinning2); + pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection1, confLambdaSelection2, confLambdaCleaner1, confLambdaCleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec1, lambdaHistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + if (processK0shortK0short) { + k0shortHistSpec1 = v0histmanager::makeV0HistSpecMap(confK0shortBinning1); + k0shortHistSpec2 = v0histmanager::makeV0HistSpecMap(confK0shortBinning2); + pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection1, confK0shortSelection2, confK0shortCleaner1, confK0shortCleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec1, k0shortHistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } - // setup for k0short - if (doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent) { - k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); - pairV0V0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection, confK0shortSelection, confK0shortCleaner, confK0shortCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + if (processLambdaK0short) { + lambdaHistSpec1 = v0histmanager::makeV0HistSpecMap(confLambdaBinning1); + k0shortHistSpec2 = v0histmanager::makeV0HistSpecMap(confK0shortBinning2); + pairLambdaK0shortBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection1, confK0shortSelection2, confLambdaCleaner1, confK0shortCleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec1, k0shortHistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); - posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); - negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); + posDauSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confV01PosDauBinning); + negDauSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confV01NegDauBinning); + posDauSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confV02PosDauBinning); + negDauSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confV02NegDauBinning); + pairV0V0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); + if (processLambdaLambda) { - lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); - pairV0V0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection, confLambdaSelection, confLambdaCleaner, confLambdaCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + lambdaHistSpec1 = v0histmanager::makeV0McHistSpecMap(confLambdaBinning1); + lambdaHistSpec2 = v0histmanager::makeV0McHistSpecMap(confLambdaBinning2); + pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection1, confLambdaSelection2, confLambdaCleaner1, confLambdaCleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec1, lambdaHistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); + } + + if (processK0shortK0short) { + k0shortHistSpec1 = v0histmanager::makeV0McHistSpecMap(confK0shortBinning1); + k0shortHistSpec2 = v0histmanager::makeV0McHistSpecMap(confK0shortBinning2); + pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection1, confK0shortSelection2, confK0shortCleaner1, confK0shortCleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec1, k0shortHistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } - // setup for k0short - if (doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent) { - k0shortHistSpec = v0histmanager::makeV0McHistSpecMap(confK0shortBinning); - pairV0V0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection, confK0shortSelection, confK0shortCleaner, confK0shortCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + if (processLambdaK0short) { + lambdaHistSpec1 = v0histmanager::makeV0McHistSpecMap(confLambdaBinning1); + k0shortHistSpec2 = v0histmanager::makeV0McHistSpecMap(confK0shortBinning2); + pairLambdaK0shortBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection1, confK0shortSelection2, confLambdaCleaner1, confK0shortCleaner2, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec1, k0shortHistSpec2, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } } - }; + hRegistry.print(); + } void processLambdaLambdaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) { - pairLambdaLambdaBuilder.processSameEvent(col, tracks, lambdas, lambdaPartition, lambdaPartition, cache); + pairLambdaLambdaBuilder.processSameEvent(col, tracks, lambdas, lambdaPartition1, lambdaPartition2, cache); } PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaSameEvent, "Enable processing same event processing for lambda-lambda", true); - void processLambdaLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processLambdaLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairLambdaLambdaBuilder.processSameEvent(col, mcCols, tracks, lambdas, lambdaWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairLambdaLambdaBuilder.processSameEvent(col, mcCols, tracks, lambdas, lambdaWithLabelPartition1, lambdaWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaSameEventMc, "Enable processing same event processing for lambda-lambda with mc information", false); - void processLambdaLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + void processLambdaLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { - pairLambdaLambdaBuilder.processMixedEvent(cols, tracks, lambdas, lambdaPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairLambdaLambdaBuilder.processMixedEvent(cols, tracks, lambdaPartition1, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaMixedEvent, "Enable processing mixed event processing for lambda-lambda", true); - void processLambdaLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processLambdaLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairLambdaLambdaBuilder.processMixedEvent(cols, mcCols, tracks, lambdaWithLabelPartition, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairLambdaLambdaBuilder.processMixedEvent(cols, mcCols, tracks, lambdaWithLabelPartition1, lambdaWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaMixedEventMc, "Enable processing mixed event processing for lambda-lambda with mc information", false); void processK0shortK0shortSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) { - pairK0shortK0shortBuilder.processSameEvent(col, tracks, k0shorts, k0shortPartition, k0shortPartition, cache); + pairK0shortK0shortBuilder.processSameEvent(col, tracks, k0shorts, k0shortPartition1, k0shortPartition2, cache); } PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortSameEvent, "Enable processing same event processing for k0short-k0short", false); - void processK0shortK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processK0shortK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairK0shortK0shortBuilder.processSameEvent(col, mcCols, tracks, k0shorts, k0shortWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairK0shortK0shortBuilder.processSameEvent(col, mcCols, tracks, k0shorts, k0shortWithLabelPartition1, k0shortWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortSameEventMc, "Enable processing same event processing for k0short-k0short with mc information", false); - void processK0shortK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) + void processK0shortK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { - pairK0shortK0shortBuilder.processMixedEvent(cols, tracks, k0shorts, k0shortPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairK0shortK0shortBuilder.processMixedEvent(cols, tracks, k0shortPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortMixedEvent, "Enable processing mixed event processing for k0short-k0short", false); - void processK0shortK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processK0shortK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairK0shortK0shortBuilder.processMixedEvent(cols, mcCols, tracks, k0shortWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairK0shortK0shortBuilder.processMixedEvent(cols, mcCols, tracks, k0shortWithLabelPartition1, k0shortWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortMixedEventMc, "Enable processing mixed event processing for k0short-k0short with mc information", false); + + void processLambdaK0shortSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas, FemtoK0shorts const& /*k0shorts*/) + { + pairLambdaK0shortBuilder.processSameEvent(col, tracks, lambdas, lambdaPartition1, k0shortPartition2, cache); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortSameEvent, "Enable processing same event processing for lambda-k0short", false); + + void processLambdaK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairLambdaK0shortBuilder.processSameEvent(col, mcCols, tracks, lambdas, lambdaWithLabelPartition1, k0shortWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortSameEventMc, "Enable processing same event processing for lambda-k0short with mc information", false); + + void processLambdaK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + pairLambdaK0shortBuilder.processMixedEvent(cols, tracks, lambdaPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortMixedEvent, "Enable processing mixed event processing for lambda-k0short", false); + + void processLambdaK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + pairLambdaK0shortBuilder.processMixedEvent(cols, mcCols, tracks, lambdaWithLabelPartition1, k0shortWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortMixedEventMc, "Enable processing mixed event processing for lambda-k0short with mc information", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoPhotonQa.cxx b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx new file mode 100644 index 00000000000..2e074fb1ff3 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoPhotonQa.cxx @@ -0,0 +1,99 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoPhotonQa.cxx +/// \brief QA task for photons (PCM) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonBuilder.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2::analysis::femto; + +struct FemtoPhotonQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for photons + photonbuilder::ConfPhotonSelection1 confPhotonSelection; + + o2::framework::Partition photonPartition = MAKE_PHOTON_PARTITION(confPhotonSelection); + o2::framework::Preslice perColPhotons = o2::aod::femtobase::stored::fColId; + + photonhistmanager::ConfPhotonBinning confPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonQaBinning; + photonhistmanager::PhotonHistManager photonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoPhotonQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto photonHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonBinning, confPhotonQaBinning); + photonHistManager.init(&hRegistry, photonHistSpec, confPhotonQaBinning); + + hRegistry.print(); + } + + void processPhotons(FilteredFemtoCollision const& col, FemtoPhotons const& /*photons*/) + { + auto photonSlice = photonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (photonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& photon : photonSlice) { + photonHistManager.fill(photon); + } + } + PROCESS_SWITCH(FemtoPhotonQa, processPhotons, "Process photons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTrackQa.cxx b/PWGCF/Femto/Tasks/femtoTrackQa.cxx index 27ef50fa3a7..c3dc0898145 100644 --- a/PWGCF/Femto/Tasks/femtoTrackQa.cxx +++ b/PWGCF/Femto/Tasks/femtoTrackQa.cxx @@ -41,10 +41,14 @@ using namespace o2::analysis::femto; struct FemtoTrackQa { // setup tables - using FemtoCollisions = o2::soa::Join; + using FemtoCollisions = o2::soa::Join; using FilteredFemtoCollisions = o2::soa::Filtered; using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + using FemtoCollisionsWithEventShape = o2::soa::Join; + using FilteredFemtoCollisionsWithEventShape = o2::soa::Filtered; + using FilteredFemtoCollisionWithEventShape = FilteredFemtoCollisionsWithEventShape::iterator; + using FemtoCollisionsWithLabel = o2::soa::Join; using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; @@ -53,6 +57,8 @@ struct FemtoTrackQa { using FemtoTracksWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup collisions @@ -81,25 +87,25 @@ struct FemtoTrackQa { void init(o2::framework::InitContext&) { - if ((doprocessData + doprocessMc) > 1) { + if ((static_cast(doprocessData) + static_cast(doprocessMc) + static_cast(doprocessDataWithEventShape)) > 1) { LOG(fatal) << "More than 1 process function is activated. Breaking..."; } - bool processData = doprocessData; + bool processDataFlag = doprocessData || doprocessDataWithEventShape; trackCleaner.init(confTrackCleaner); std::map> colHistSpec; std::map> trackHistSpec; - if (processData) { + if (processDataFlag) { colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); trackHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confTrackBinning, confTrackQaBinning); - trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); + trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); trackHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confTrackBinning, confTrackQaBinning); - trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); + trackHistManager.init(&hRegistry, trackHistSpec, confTrackSelection, confTrackQaBinning); } hRegistry.print(); }; @@ -110,35 +116,48 @@ struct FemtoTrackQa { if (trackSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& track : trackSlice) { - trackHistManager.fill(track, tracks); + trackHistManager.fill(track, tracks); } }; PROCESS_SWITCH(FemtoTrackQa, processData, "Track QA in Data", true); - void processMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { auto trackSlice = trackWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (trackSlice.size() == 0) { return; } - colHistManager.fill(col, mcCols); + colHistManager.fill(col, mcCols); for (auto const& track : trackSlice) { if (!trackCleaner.isClean(track, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - trackHistManager.fill(track, tracks, mcParticles, mcMothers, mcPartonicMothers); + trackHistManager.fill(track, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoTrackQa, processMc, "Track QA in Monte Carlo", false); + + void processDataWithEventShape(FilteredFemtoCollisionWithEventShape const& col, FemtoTracks const& tracks) + { + auto trackSlice = trackPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (trackSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& track : trackSlice) { + trackHistManager.fill(track, tracks); + } + }; + PROCESS_SWITCH(FemtoTrackQa, processDataWithEventShape, "Track QA in Data with event shape information", false); }; o2::framework::WorkflowSpec - defineDataProcessing(o2::framework::ConfigContext const& cfgc) + defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..59c98e936c2 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTrackTrackTwoPhotonResonanceQa.cxx @@ -0,0 +1,127 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTrackTrackTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two tracks and a two-photon resonance (omega(782) -> pi+ pi- pi0) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/trackTrackTwoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTrackTrackTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoOmegaMesons = o2::soa::Join; + using FemtoTracks = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for omega mesons + trackstwophotonresonancebuilder::ConfOmegaMesonSelection1 confOmegaMesonSelection; + o2::framework::Partition omegaMesonPartition = MAKE_TRACKTRACKTWOPHOTONRESONANCE_PARTITION(confOmegaMesonSelection); + o2::framework::Preslice perColOmegaMesons = o2::aod::femtobase::stored::fColId; + + trackstwophotonresonancehistmanager::ConfOmegaMesonBinning confOmegaMesonBinning; + + // setup for the two charged (pion) daughters + trackhistmanager::ConfResonancePosDauBinning confPosDauBinning; + trackhistmanager::ConfResonancePosDauQaBinning confPosDauQaBinning; + trackhistmanager::ConfResonanceNegDauBinning confNegDauBinning; + trackhistmanager::ConfResonanceNegDauQaBinning confNegDauQaBinning; + + // setup for the neutral (pi0) daughter -- reuses the regular Pi0 binning/QA config, since the + // embedded candidate gets the same TwoPhotonResonanceHistManager treatment (including its own + // photon-leg QA) as the dedicated Pi0/Eta QA task + twophotonresonancehistmanager::ConfPi0Binning confNeutralDauBinning; + photonhistmanager::ConfPhotonBinning confNeutralDauPhotonBinning; + photonhistmanager::ConfPhotonQaBinning confNeutralDauPhotonQaBinning; + + trackstwophotonresonancehistmanager::TrackTrackTwoPhotonResonanceHistManager< + trackstwophotonresonancehistmanager::PrefixOmegaMeson, + trackhistmanager::PrefixResonancePosDaughterSe, + trackhistmanager::PrefixResonanceNegDaughterSe, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDau, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau1, + trackstwophotonresonancehistmanager::PrefixOmegaMesonNeutralDauPhotonDau2, + modes::TrackTrackTwoPhotonResonance::kOmegaMeson, + modes::TwoPhotonResonance::kPi0> + omegaMesonHistManager; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto omegaMesonHistSpec = trackstwophotonresonancehistmanager::makeTrackTrackTwoPhotonResonanceQaHistSpecMap(confOmegaMesonBinning); + auto posDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDauBinning, confPosDauQaBinning); + auto negDauSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDauBinning, confNegDauQaBinning); + auto neutralDauSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confNeutralDauBinning); + auto neutralDauPhotonSpec = photonhistmanager::makePhotonQaHistSpecMap(confNeutralDauPhotonBinning, confNeutralDauPhotonQaBinning); + + omegaMesonHistManager.init(&hRegistry, omegaMesonHistSpec, posDauSpec, negDauSpec, neutralDauSpec, neutralDauPhotonSpec, confPosDauQaBinning, confNegDauQaBinning, confNeutralDauPhotonQaBinning); + } + + void processOmegaMesons(FilteredFemtoCollision const& col, FemtoOmegaMesons const& /*omegaMesons*/, FemtoTracks const& tracks, FemtoPi0s const& pi0s, FemtoPhotons const& photons) + { + auto omegaMesonSlice = omegaMesonPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (omegaMesonSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& omegaMeson : omegaMesonSlice) { + omegaMesonHistManager.fill(omegaMeson, tracks, pi0s, photons); + } + } + PROCESS_SWITCH(FemtoTrackTrackTwoPhotonResonanceQa, processOmegaMesons, "Process OmegaMesons", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx new file mode 100644 index 00000000000..c2cee7d7321 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTripletMcParticleMcParticleMcParticle.cxx @@ -0,0 +1,163 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTripletMcParticleMcParticleMcParticle.cxx +/// \brief Task for triplets of generated particles (mc truth only) +/// \author Anton Riedel, TU München, anton.riedel@cern.ch + +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/closeTripletRejection.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/mcBuilder.h" +#include "PWGCF/Femto/Core/mcParticleHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/tripletBuilder.h" +#include "PWGCF/Femto/Core/tripletHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTripletMcParticleMcParticleMcParticle { + + // setup tables + using FemtoMcCollisions = o2::aod::FMcCols; + using FilteredFemtoMcCollisions = o2::soa::Filtered; + using FilteredFemtoMcCollision = FilteredFemtoMcCollisions::iterator; + + using FemtoMcParticles = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup collisions + mcbuilder::ConfMcCollisionFilters collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_MC_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup mc particles + mcbuilder::ConfMcParticleSelection1 confMcParticleSelection1; + mcparticlehistmanager::ConfMcParticleBinning1 confMcParticleBinning1; + particlecleaner::ConfMcParticleCleaner1 confMcParticleCleaner1; + + mcbuilder::ConfMcParticleSelection2 confMcParticleSelection2; + mcparticlehistmanager::ConfMcParticleBinning2 confMcParticleBinning2; + particlecleaner::ConfMcParticleCleaner2 confMcParticleCleaner2; + + mcbuilder::ConfMcParticleSelection3 confMcParticleSelection3; + mcparticlehistmanager::ConfMcParticleBinning3 confMcParticleBinning3; + particlecleaner::ConfMcParticleCleaner3 confMcParticleCleaner3; + + o2::framework::Partition mcParticlesPartition1 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection1); + o2::framework::Partition mcParticlesPartition2 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection2); + o2::framework::Partition mcParticlesPartition3 = MAKE_MC_PARTICLE_PARTITION(confMcParticleSelection3); + o2::framework::PresliceUnsorted perColParticles = o2::aod::femtomcparticle::fMcColId; + + // setup triplets + triplethistmanager::ConfTripletBinning confTripletBinning; + triplethistmanager::ConfTripletCuts confTripletCuts; + paircleaner::ConfPairCleanerBinning confTripletCleanerBinning; + + closetripletrejection::ConfCtrMcParticleMcParticleMcParticle confCtr; + + tripletbuilder::TripletMcParticleMcParticleMcParticleBuilder< + mcparticlehistmanager::PrefixMcParticle1Se, + mcparticlehistmanager::PrefixMcParticle2Se, + mcparticlehistmanager::PrefixMcParticle3Se, + mcparticlehistmanager::PrefixMcParticle1Me, + mcparticlehistmanager::PrefixMcParticle2Me, + mcparticlehistmanager::PrefixMcParticle3Me, + triplethistmanager::PrefixMcParticleMcParticleMcParticleSe, + triplethistmanager::PrefixMcParticleMcParticleMcParticleMe, + closetripletrejection::PrefixMcParticle1McParticle2Se, + closetripletrejection::PrefixMcParticle2McParticle3Se, + closetripletrejection::PrefixMcParticle1McParticle3Se, + closetripletrejection::PrefixMcParticle1McParticle2Me, + closetripletrejection::PrefixMcParticle2McParticle3Me, + closetripletrejection::PrefixMcParticle1McParticle3Me> + tripletMcParticleMcParticleMcParticleBuilder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + triplethistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoMcParticleMcParticleMcParticle", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histogram specs + std::map> colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + std::map> mcParticleHistSpec1 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning1); + std::map> mcParticleHistSpec2 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning2); + std::map> mcParticleHistSpec3 = mcparticlehistmanager::makeMcParticleHistSpecMap(confMcParticleBinning3); + std::map> tripletHistSpec = triplethistmanager::makeTripletMcTruthHistSpecMap(confTripletBinning, confMixing); + std::map> ctrHistSpec = closepairrejection::makeCprHistSpecMap(confCtr); + std::map> tripletCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confTripletCleanerBinning); + + tripletMcParticleMcParticleMcParticleBuilder.init(&hRegistry, confCollisionBinning, confMcParticleSelection1, confMcParticleSelection2, confMcParticleSelection3, confMcParticleBinning1, confMcParticleBinning2, confMcParticleBinning3, confMcParticleCleaner1, confMcParticleCleaner2, confMcParticleCleaner3, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, mcParticleHistSpec1, mcParticleHistSpec2, mcParticleHistSpec3, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); + + hRegistry.print(); + }; + + // deliberately takes the full (ungrouped) FilteredFemtoMcCollisions table rather than its ::iterator: + // pairing an ::iterator first argument with FemtoMcParticles as a second table argument would make + // DPL auto-register a sorted per-collision slicing cache for fMcColId, which then collides with the + // manual sliceByCachedUnsorted() used inside the builder (see the same workaround in + // femtoDndetaPair.cxx/femtoDndetaTriplet.cxx); we run the per-collision loop ourselves instead + void processSameEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + for (auto const& col : cols) { + tripletMcParticleMcParticleMcParticleBuilder.processSameEvent(col, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, mcParticlesPartition3, cache); + } + } + PROCESS_SWITCH(FemtoTripletMcParticleMcParticleMcParticle, processSameEvent, "Enable processing same event processing", true); + + void processMixedEvent(FilteredFemtoMcCollisions const& cols, FemtoMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + tripletMcParticleMcParticleMcParticleBuilder.processMixedEvent(cols, mcParticles, mcMothers, mcPartonicMothers, mcParticlesPartition1, mcParticlesPartition2, mcParticlesPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletMcParticleMcParticleMcParticle, processMixedEvent, "Enable processing mixed event processing", true); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx new file mode 100644 index 00000000000..319fc73ea14 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -0,0 +1,326 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTripletTrackTrackCascade.cxx +/// \brief Tasks that computes correlation between two tracks and a cascade +/// \author Roberta Ferioli roberta.ferioli@cern.ch, Raffaele Del Grande raffaele.del.grande@cern.ch, CTU Prague + +#include "PWGCF/Femto/Core/cascadeBuilder.h" +#include "PWGCF/Femto/Core/cascadeHistManager.h" +#include "PWGCF/Femto/Core/closePairRejection.h" +#include "PWGCF/Femto/Core/closeTripletRejection.h" +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/particleCleaner.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/trackBuilder.h" +#include "PWGCF/Femto/Core/trackHistManager.h" +#include "PWGCF/Femto/Core/tripletBuilder.h" +#include "PWGCF/Femto/Core/tripletHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTripletTrackTrackCascade { + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoCollisionsWithLabel = o2::soa::Join; + using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; + using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + + using FemtoTracks = o2::soa::Join; + using FemtoXis = o2::soa::Join; + using FemtoOmegas = o2::soa::Join; + + using FemtoTracksWithLabel = o2::soa::Join; + using FemtoXisWithLabel = o2::soa::Join; + using FemtoOmegasWithLabel = o2::soa::Join; + + using FemtoMcParticlesWithLabel = o2::soa::Join; + + o2::framework::SliceCache cache; + // setup collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::ConfCollisionBinning confCollisionBinning; + + // setup tracks + trackbuilder::ConfTrackSelection1 confTrackSelections1; + trackhistmanager::ConfTrackBinning1 confTrackBinning1; + trackbuilder::ConfTrackSelection2 confTrackSelections2; + trackhistmanager::ConfTrackBinning2 confTrackBinning2; + + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); + o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); + o2::framework::Preslice perColtracks = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition trackWithLabelPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); + o2::framework::Partition trackWithLabelPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); + o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; + + // setup for daughters + trackhistmanager::ConfCascadePosDauBinning confPosDauBinning; + trackhistmanager::ConfCascadeNegDauBinning confNegDauBinning; + trackhistmanager::ConfCascadeBachelorBinning confBachelorBinning; + + // setup xis + cascadebuilder::ConfXiSelection confXiSelection; + cascadehistmanager::ConfXiBinning confXiBinning; + particlecleaner::ConfXiCleaner1 confXiCleaner; + particlecleaner::ConfTrackCleaner1 confTrackCleaner1; + particlecleaner::ConfTrackCleaner2 confTrackCleaner2; + + o2::framework::Partition xiPartition = MAKE_CASCADE_PARTITION(confXiSelection); + o2::framework::Preslice perColXis = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition xiWithLabelPartition = MAKE_CASCADE_PARTITION(confXiSelection); + o2::framework::Preslice perColXisWithLabel = o2::aod::femtobase::stored::fColId; + + // setup omegas + cascadebuilder::ConfOmegaSelection confOmegaSelection; + cascadehistmanager::ConfOmegaBinning confOmegaBinning; + particlecleaner::ConfOmegaCleaner1 confOmegaCleaner; + + o2::framework::Partition omegaPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); + o2::framework::Preslice perColOmegas = o2::aod::femtobase::stored::fColId; + + o2::framework::Partition omegaWithLabelPartition = MAKE_CASCADE_PARTITION(confOmegaSelection); + o2::framework::Preslice perColOmegasWithLabel = o2::aod::femtobase::stored::fColId; + + // setup triplets + triplethistmanager::ConfTripletBinning confTripletBinning; + triplethistmanager::ConfTripletCuts confTripletCuts; + paircleaner::ConfPairCleanerBinning confTripletCleanerBinning; + + closetripletrejection::ConfCtrTrackTrackTrack confCtr; + + tripletbuilder::TripletTrackTrackCascadeBuilder< + modes::Cascade::kXi, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + cascadehistmanager::PrefixXiSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + cascadehistmanager::PrefixXiMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, + triplethistmanager::PrefixTrackTrackCascadeSe, + triplethistmanager::PrefixTrackTrackCascadeMe, + closetripletrejection::PrefixTrack1Track2Se, + closetripletrejection::PrefixTrack1CascadeBachelorSe, + closetripletrejection::PrefixTrack2CascadeBachelorSe, + closetripletrejection::PrefixTrack1V0DaughterSe, + closetripletrejection::PrefixTrack2V0DaughterSe, + closetripletrejection::PrefixTrack1Track2Me, + closetripletrejection::PrefixTrack1CascadeBachelorMe, + closetripletrejection::PrefixTrack2CascadeBachelorMe, + closetripletrejection::PrefixTrack1V0DaughterMe, + closetripletrejection::PrefixTrack2V0DaughterMe> + tripletTrackTrackXiBuilder; + + tripletbuilder::TripletTrackTrackCascadeBuilder< + modes::Cascade::kOmega, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + cascadehistmanager::PrefixOmegaSe, + trackhistmanager::PrefixCascadeBachelorSe, + trackhistmanager::PrefixCascadePosDaughterSe, + trackhistmanager::PrefixCascadeNegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + cascadehistmanager::PrefixOmegaMe, + trackhistmanager::PrefixCascadeBachelorMe, + trackhistmanager::PrefixCascadePosDaughterMe, + trackhistmanager::PrefixCascadeNegDaughterMe, + triplethistmanager::PrefixTrackTrackCascadeSe, + triplethistmanager::PrefixTrackTrackCascadeMe, + closetripletrejection::PrefixTrack1Track2Se, + closetripletrejection::PrefixTrack1CascadeBachelorSe, + closetripletrejection::PrefixTrack2CascadeBachelorSe, + closetripletrejection::PrefixTrack1V0DaughterSe, + closetripletrejection::PrefixTrack2V0DaughterSe, + closetripletrejection::PrefixTrack1Track2Me, + closetripletrejection::PrefixTrack1CascadeBachelorMe, + closetripletrejection::PrefixTrack2CascadeBachelorMe, + closetripletrejection::PrefixTrack1V0DaughterMe, + closetripletrejection::PrefixTrack2V0DaughterMe> + tripletTrackTrackOmegaBuilder; + + // setup mixing + std::vector defaultVtxBins{10, -10, 10}; + std::vector defaultMultBins{50, 0, 200}; + std::vector defaultCentBins{10, 0, 100}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; + triplethistmanager::ConfMixing confMixing; + + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackCascade", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + // setup cpr + closepairrejection::ConfCprTrackCascadeBachelor confCprBachelor; + closepairrejection::ConfCprTrackV0Daughter confCprV0Daughter; + + void init(o2::framework::InitContext&) + { + if ((static_cast(doprocessXiSameEvent) + static_cast(doprocessXiSameEventMc)) > 1 || (static_cast(doprocessXiMixedEvent) + static_cast(doprocessXiMixedEventMc)) > 1 || (static_cast(doprocessOmegaSameEvent) + static_cast(doprocessOmegaSameEventMc)) > 1 || (static_cast(doprocessOmegaMixedEvent) + static_cast(doprocessOmegaMixedEventMc)) > 1) { + LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; + } + bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; + if (processData && processMc) { + LOG(fatal) << "Both data and mc processing is activated. Breaking..."; + } + + bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventMc; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventMc; + + if (processXi && processOmega) { + LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; + } + // setup columnpolicy for binning + // default values are used during instantiation, so we need to explicity update them here + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; + + // setup histogram specs + std::map> colHistSpec; + std::map> trackHistSpec1; + std::map> trackHistSpec2; + std::map> bachelorHistSpec; + + std::map> posDauSpec; + std::map> negDauSpec; + std::map> xiHistSpec; + std::map> omegaHistSpec; + std::map> tripletTrackTrackCascadeHistSpec; + + std::map> cprHistSpecBachelor = closepairrejection::makeCprHistSpecMap(confCprBachelor); + std::map> cprHistSpecV0Daughter = closepairrejection::makeCprHistSpecMap(confCprV0Daughter); + std::map> ctrHistSpec = closepairrejection::makeCprHistSpecMap(confCtr); + std::map> tripletCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confTripletCleanerBinning); + + if (processData) { + colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); + bachelorHistSpec = trackhistmanager::makeTrackHistSpecMap(confBachelorBinning); + posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + tripletTrackTrackCascadeHistSpec = triplethistmanager::makeTripletHistSpecMap(confTripletBinning, confMixing); + + if (processXi) { + xiHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confXiBinning); + tripletTrackTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCtr, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, tripletTrackTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, ctrHistSpec, tripletCleanerHistSpec); + } else { + omegaHistSpec = cascadehistmanager::makeCascadeHistSpecMap(confOmegaBinning); + tripletTrackTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCtr, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, tripletTrackTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, ctrHistSpec, tripletCleanerHistSpec); + } + } else { + colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); + trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); + trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); + bachelorHistSpec = trackhistmanager::makeTrackMcHistSpecMap(confBachelorBinning); + posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); + negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); + tripletTrackTrackCascadeHistSpec = triplethistmanager::makeTripletMcHistSpecMap(confTripletBinning, confMixing); + if (processXi) { + xiHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confXiBinning); + tripletTrackTrackXiBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCtr, confXiSelection, confXiCleaner, confCprBachelor, confCprV0Daughter, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, xiHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, tripletTrackTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, ctrHistSpec, tripletCleanerHistSpec); + } else { + omegaHistSpec = cascadehistmanager::makeCascadeMcHistSpecMap(confOmegaBinning); + tripletTrackTrackOmegaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackCleaner1, confTrackCleaner2, confCtr, confOmegaSelection, confOmegaCleaner, confCprBachelor, confCprV0Daughter, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, omegaHistSpec, bachelorHistSpec, posDauSpec, negDauSpec, tripletTrackTrackCascadeHistSpec, cprHistSpecBachelor, cprHistSpecV0Daughter, ctrHistSpec, tripletCleanerHistSpec); + } + } + hRegistry.print(); + }; + + void processXiSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + tripletTrackTrackXiBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, xiPartition, cache); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiSameEvent, "Enable processing same event processing for tracks and xis", true); + + void processXiSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + tripletTrackTrackXiBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); + + void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + tripletTrackTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiMixedEvent, "Enable processing mixed event processing for tracks and xis", true); + + void processXiMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoXisWithLabel const& /*xis*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + tripletTrackTrackXiBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, xiWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiMixedEventMc, "Enable processing mixed event processing for tracks and xis with mc information", false); + + void processOmegaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoOmegas const& /*omega*/) + { + tripletTrackTrackOmegaBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, omegaPartition, cache); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaSameEvent, "Enable processing same event processing for tracks and omegas", false); + + void processOmegaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& /*omegas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + tripletTrackTrackOmegaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); + + void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) + { + tripletTrackTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaMixedEvent, "Enable processing mixed event processing for tracks and omegas", false); + + void processOmegaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoOmegasWithLabel const& /*omegas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + { + tripletTrackTrackOmegaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, omegaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaMixedEventMc, "Enable processing mixed event processing for tracks and omegas with mc information", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 05dcdd5b153..6c80679fc17 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -18,6 +18,8 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" #include "PWGCF/Femto/Core/trackBuilder.h" #include "PWGCF/Femto/Core/trackHistManager.h" @@ -57,6 +59,8 @@ struct FemtoTripletTrackTrackTrack { using FemtoTracksWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup collisions @@ -72,6 +76,11 @@ struct FemtoTripletTrackTrackTrack { trackbuilder::ConfTrackSelection3 confTrackSelections3; trackhistmanager::ConfTrackBinning3 confTrackBinning3; + // particle cleaners (mc only; one per track species) + particlecleaner::ConfTrackCleaner1 confTrackCleaner1; + particlecleaner::ConfTrackCleaner2 confTrackCleaner2; + particlecleaner::ConfTrackCleaner3 confTrackCleaner3; + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); o2::framework::Partition trackPartition3 = MAKE_TRACK_PARTITION(confTrackSelections3); @@ -85,13 +94,17 @@ struct FemtoTripletTrackTrackTrack { // setup triplets triplethistmanager::ConfTripletBinning confTripletBinning; triplethistmanager::ConfTripletCuts confTripletCuts; + paircleaner::ConfPairCleanerBinning confTripletCleanerBinning; closetripletrejection::ConfCtrTrackTrackTrack confCtr; tripletbuilder::TripletTrackTrackTrackBuilder< - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - trackhistmanager::PrefixTrack3, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + trackhistmanager::PrefixTrack3Se, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + trackhistmanager::PrefixTrack3Me, triplethistmanager::PrefixTrackTrackTrackSe, triplethistmanager::PrefixTrackTrackTrackMe, closetripletrejection::PrefixTrack1Track2Se, @@ -106,16 +119,16 @@ struct FemtoTripletTrackTrackTrack { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; void init(o2::framework::InitContext&) { - if ((doprocessSameEvent + doprocessSameEventMc) > 1 || (doprocessMixedEvent + doprocessMixedEventMc) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } bool processData = doprocessSameEvent || doprocessMixedEvent; @@ -126,9 +139,9 @@ struct FemtoTripletTrackTrackTrack { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs @@ -138,54 +151,55 @@ struct FemtoTripletTrackTrackTrack { std::map> trackHistSpec3; std::map> tripletHistSpec; std::map> ctrHistSpec = closepairrejection::makeCprHistSpecMap(confCtr); + std::map> tripletCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confTripletCleanerBinning); if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning2); trackHistSpec3 = trackhistmanager::makeTrackHistSpecMap(confTrackBinning3); - tripletHistSpec = triplethistmanager::makeTripletHistSpecMap(confTripletBinning); - tripletTrackTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackSelections3, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, trackHistSpec3, tripletHistSpec, ctrHistSpec); + tripletHistSpec = triplethistmanager::makeTripletHistSpecMap(confTripletBinning, confMixing); + tripletTrackTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackSelections3, confTrackCleaner1, confTrackCleaner2, confTrackCleaner3, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, trackHistSpec3, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); trackHistSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning2); trackHistSpec3 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning3); - tripletHistSpec = triplethistmanager::makeTripletMcHistSpecMap(confTripletBinning); - tripletTrackTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackSelections3, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, trackHistSpec3, tripletHistSpec, ctrHistSpec); + tripletHistSpec = triplethistmanager::makeTripletMcHistSpecMap(confTripletBinning, confMixing); + tripletTrackTrackTrackBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confTrackSelections3, confTrackCleaner1, confTrackCleaner2, confTrackCleaner3, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, trackHistSpec3, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); } hRegistry.print(); }; void processSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks) { - tripletTrackTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, trackPartition3, cache); + tripletTrackTrackTrackBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, trackPartition3, cache); } PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processSameEvent, "Enable processing same event processing", true); - void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - tripletTrackTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, trackWithLabelPartition3, mcParticles, mcMothers, mcPartonicMothers, cache); + tripletTrackTrackTrackBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, trackWithLabelPartition3, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processSameEventMc, "Enable processing same event processing", false); void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { - tripletTrackTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + tripletTrackTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processMixedEvent, "Enable processing mixed event processing", true); - void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, o2::aod::FMcParticles const& mcParticles) + void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - tripletTrackTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, trackWithLabelPartition3, mcParticles, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + tripletTrackTrackTrackBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, trackWithLabelPartition3, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processMixedEventMc, "Enable processing mixed event processing", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index 370cee244a3..22dd856be44 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -18,6 +18,8 @@ #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/pairCleaner.h" +#include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" #include "PWGCF/Femto/Core/trackBuilder.h" #include "PWGCF/Femto/Core/trackHistManager.h" @@ -64,6 +66,8 @@ struct FemtoTripletTrackTrackV0 { using FemtoLambdasWithLabel = o2::soa::Join; // using FemtoK0shortsWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup collisions @@ -77,6 +81,11 @@ struct FemtoTripletTrackTrackV0 { trackbuilder::ConfTrackSelection2 confTrackSelections2; trackhistmanager::ConfTrackBinning2 confTrackBinning2; + // particle cleaners (mc only) + particlecleaner::ConfTrackCleaner1 confTrackCleaner1; + particlecleaner::ConfTrackCleaner2 confTrackCleaner2; + particlecleaner::ConfLambdaCleaner1 confLambdaCleaner; + o2::framework::Partition trackPartition1 = MAKE_TRACK_PARTITION(confTrackSelections1); o2::framework::Partition trackPartition2 = MAKE_TRACK_PARTITION(confTrackSelections2); o2::framework::Preslice perColtracks = o2::aod::femtobase::stored::fColId; @@ -86,8 +95,8 @@ struct FemtoTripletTrackTrackV0 { o2::framework::Preslice perColtracksWithLabel = o2::aod::femtobase::stored::fColId; // setup for daughters - trackhistmanager::ConfV0PosDauBinning confPosDauBinning; - trackhistmanager::ConfV0NegDauBinning confNegDauBinning; + trackhistmanager::ConfV01PosDauBinning confPosDauBinning; + trackhistmanager::ConfV01NegDauBinning confNegDauBinning; // setup lambdas v0builder::ConfLambdaSelection1 confLambdaSelection; @@ -102,16 +111,22 @@ struct FemtoTripletTrackTrackV0 { // setup triplets triplethistmanager::ConfTripletBinning confTripletBinning; triplethistmanager::ConfTripletCuts confTripletCuts; + paircleaner::ConfPairCleanerBinning confTripletCleanerBinning; - closetripletrejection::ConfCtrTrackTrackTrack confCtr; + closetripletrejection::ConfCtrTrackTrackV0 confCtr; tripletbuilder::TripletTrackTrackV0Builder< modes::V0::kLambda, - trackhistmanager::PrefixTrack1, - trackhistmanager::PrefixTrack2, - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + trackhistmanager::PrefixTrack1Se, + trackhistmanager::PrefixTrack2Se, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + trackhistmanager::PrefixTrack1Me, + trackhistmanager::PrefixTrack2Me, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, triplethistmanager::PrefixTrackTrackLambdaSe, triplethistmanager::PrefixTrackTrackLambdaMe, closetripletrejection::PrefixTrack1Track2Se, @@ -126,16 +141,16 @@ struct FemtoTripletTrackTrackV0 { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; triplethistmanager::ConfMixing confMixing; - o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; void init(o2::framework::InitContext&) { - if ((doprocessSameEvent + doprocessSameEventMc) > 1 || (doprocessMixedEvent + doprocessMixedEventMc) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } bool processData = doprocessSameEvent || doprocessMixedEvent; @@ -146,9 +161,9 @@ struct FemtoTripletTrackTrackV0 { // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histogram specs std::map> colHistSpec; @@ -160,6 +175,7 @@ struct FemtoTripletTrackTrackV0 { std::map> lambdaHistSpec; std::map> tripletHistSpec; std::map> ctrHistSpec = closepairrejection::makeCprHistSpecMap(confCtr); + std::map> tripletCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confTripletCleanerBinning); if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); @@ -168,8 +184,8 @@ struct FemtoTripletTrackTrackV0 { lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); - tripletHistSpec = triplethistmanager::makeTripletHistSpecMap(confTripletBinning); - tripletTrackTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confLambdaSelection, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, lambdaHistSpec, posDauSpec, negDauSpec, tripletHistSpec, ctrHistSpec); + tripletHistSpec = triplethistmanager::makeTripletHistSpecMap(confTripletBinning, confMixing); + tripletTrackTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confLambdaSelection, confTrackCleaner1, confTrackCleaner2, confLambdaCleaner, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, lambdaHistSpec, posDauSpec, negDauSpec, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); trackHistSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confTrackBinning1); @@ -177,41 +193,41 @@ struct FemtoTripletTrackTrackV0 { lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); - tripletHistSpec = triplethistmanager::makeTripletMcHistSpecMap(confTripletBinning); - tripletTrackTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confLambdaSelection, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, lambdaHistSpec, posDauSpec, negDauSpec, tripletHistSpec, ctrHistSpec); + tripletHistSpec = triplethistmanager::makeTripletMcHistSpecMap(confTripletBinning, confMixing); + tripletTrackTrackLambdaBuilder.init(&hRegistry, confCollisionBinning, confTrackSelections1, confTrackSelections2, confLambdaSelection, confTrackCleaner1, confTrackCleaner2, confLambdaCleaner, confCtr, confMixing, confTripletBinning, confTripletCuts, colHistSpec, trackHistSpec1, trackHistSpec2, lambdaHistSpec, posDauSpec, negDauSpec, tripletHistSpec, ctrHistSpec, tripletCleanerHistSpec); } hRegistry.print(); }; void processSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { - tripletTrackTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, lambdaPartition, cache); + tripletTrackTrackLambdaBuilder.processSameEvent(col, tracks, trackPartition1, trackPartition2, lambdaPartition, cache); } PROCESS_SWITCH(FemtoTripletTrackTrackV0, processSameEvent, "Enable processing same event processing", true); - void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdas const& /*lambdas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - tripletTrackTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + tripletTrackTrackLambdaBuilder.processSameEvent(col, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoTripletTrackTrackV0, processSameEventMc, "Enable processing same event processing", false); void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { - tripletTrackTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + tripletTrackTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoTripletTrackTrackV0, processMixedEvent, "Enable processing mixed event processing", true); - void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdas const& /*lambdas*/, o2::aod::FMcParticles const& mcParticles) + void processMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - tripletTrackTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, lambdaWithLabelPartition, mcParticles, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + tripletTrackTrackLambdaBuilder.processMixedEvent(cols, mcCols, tracks, trackWithLabelPartition1, trackWithLabelPartition2, lambdaWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoTripletTrackTrackV0, processMixedEventMc, "Enable processing mixed event processing", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx new file mode 100644 index 00000000000..afcf432c8e4 --- /dev/null +++ b/PWGCF/Femto/Tasks/femtoTwoPhotonResonanceQa.cxx @@ -0,0 +1,143 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoTwoPhotonResonanceQa.cxx +/// \brief QA task for resonances built from two photons (pi0, eta, ...) +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionBuilder.h" +#include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/modes.h" +#include "PWGCF/Femto/Core/partitions.h" +#include "PWGCF/Femto/Core/photonHistManager.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceBuilder.h" +#include "PWGCF/Femto/Core/twoPhotonResonanceHistManager.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoTwoPhotonResonanceQa { + + // setup tables + using FemtoCollisions = o2::soa::Join; + using FilteredFemtoCollisions = o2::soa::Filtered; + using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; + + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + using FemtoPhotons = o2::soa::Join; + + o2::framework::SliceCache cache; + + // setup for collisions + collisionbuilder::ConfCollisionSelection collisionSelection; + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + colhistmanager::CollisionHistManager colHistManager; + colhistmanager::ConfCollisionBinning confCollisionBinning; + colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; + + // setup for pi0s + twophotonresonancebuilder::ConfPi0Selection confPi0Selection; + o2::framework::Partition pi0Partition = MAKE_TWOPHOTONRESONANCE_PARTITION(confPi0Selection); + o2::framework::Preslice perColPi0s = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfPi0Binning confPi0Binning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixPi0, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kPi0> + pi0HistManager; + + // setup for etas + twophotonresonancebuilder::ConfEtaSelection confEtaSelection; + o2::framework::Partition etaPartition = MAKE_TWOPHOTONRESONANCE_PARTITION(confEtaSelection); + o2::framework::Preslice perColEtas = o2::aod::femtobase::stored::fColId; + + twophotonresonancehistmanager::ConfEtaBinning confEtaBinning; + twophotonresonancehistmanager::TwoPhotonResonanceHistManager< + twophotonresonancehistmanager::PrefixEta, + photonhistmanager::PrefixTwoPhotonResonanceDau1Qa, + photonhistmanager::PrefixTwoPhotonResonanceDau2Qa, + modes::TwoPhotonResonance::kEta> + etaHistManager; + + // setup for daughters (shared between pi0 and eta -- unordered, no pos/neg or species asymmetry) + photonhistmanager::ConfPhotonBinning confPhotonDauBinning; + photonhistmanager::ConfPhotonQaBinning confPhotonDauQaBinning; + + o2::framework::HistogramRegistry hRegistry{"FemtoTwoPhotonResonanceQa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext&) + { + auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + + auto dauHistSpec = photonhistmanager::makePhotonQaHistSpecMap(confPhotonDauBinning, confPhotonDauQaBinning); + + if ((static_cast(doprocessPi0s) + static_cast(doprocessEtas)) > 1) { + LOG(fatal) << "Only one process can be activated"; + } + + if (doprocessPi0s) { + auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confPi0Binning); + pi0HistManager.init(&hRegistry, pi0HistSpec, dauHistSpec, confPhotonDauQaBinning); + } + if (doprocessEtas) { + auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceQaHistSpecMap(confEtaBinning); + etaHistManager.init(&hRegistry, etaHistSpec, dauHistSpec, confPhotonDauQaBinning); + } + } + + void processPi0s(FilteredFemtoCollision const& col, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + auto pi0Slice = pi0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (pi0Slice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& pi0 : pi0Slice) { + pi0HistManager.fill(pi0, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processPi0s, "Process Pi0s", true); + + void processEtas(FilteredFemtoCollision const& col, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + auto etaSlice = etaPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); + if (etaSlice.size() == 0) { + return; + } + colHistManager.fill(col); + for (auto const& eta : etaSlice) { + etaHistManager.fill(eta, photons); + } + } + PROCESS_SWITCH(FemtoTwoPhotonResonanceQa, processEtas, "Process Etas", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoTwotrackresonanceQa.cxx b/PWGCF/Femto/Tasks/femtoTwotrackresonanceQa.cxx index 36e663bc6d7..fbb62a1003f 100644 --- a/PWGCF/Femto/Tasks/femtoTwotrackresonanceQa.cxx +++ b/PWGCF/Femto/Tasks/femtoTwotrackresonanceQa.cxx @@ -35,38 +35,35 @@ #include #include -using namespace o2::aod; -using namespace o2::framework; -using namespace o2::framework::expressions; using namespace o2::analysis::femto; struct FemtoTwotrackresonanceQa { // setup tables - using FemtoCollisions = o2::soa::Join; + using FemtoCollisions = o2::soa::Join; using FemtoCollision = FemtoCollisions::iterator; using FilteredFemtoCollisions = o2::soa::Filtered; using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; - using FemtoPhis = o2::soa::Join; - using FemtoRho0s = o2::soa::Join; - using FemtoKstar0s = o2::soa::Join; - using FemtoTracks = o2::soa::Join; + using FemtoPhis = o2::soa::Join; + using FemtoRho0s = o2::soa::Join; + using FemtoKstar0s = o2::soa::Join; + using FemtoTracks = o2::soa::Join; - SliceCache cache; + o2::framework::SliceCache cache; // setup for collisions collisionbuilder::ConfCollisionSelection collisionSelection; - Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); + o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::CollisionHistManager colHistManager; colhistmanager::ConfCollisionBinning confCollisionBinning; colhistmanager::ConfCollisionQaBinning confCollisionQaBinning; // setup for phis twotrackresonancebuilder::ConfPhiSelection confPhiSelection; - Partition phiPartition = MAKE_RESONANCE_0_PARTITON(confPhiSelection); - Preslice perColPhis = femtobase::stored::fColId; + o2::framework::Partition phiPartition = MAKE_RESONANCE_0_PARTITON(confPhiSelection); + o2::framework::Preslice perColPhis = o2::aod::femtobase::stored::fColId; twotrackresonancehistmanager::ConfPhiBinning confPhiBinning; twotrackresonancehistmanager::TwoTrackResonanceHistManager< @@ -78,8 +75,8 @@ struct FemtoTwotrackresonanceQa { // setup for rho0s twotrackresonancebuilder::ConfRho0Selection confRho0Selection; - Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(confRho0Selection); - Preslice perColRhos = femtobase::stored::fColId; + o2::framework::Partition rho0Partition = MAKE_RESONANCE_0_PARTITON(confRho0Selection); + o2::framework::Preslice perColRhos = o2::aod::femtobase::stored::fColId; twotrackresonancehistmanager::ConfRho0Binning confRho0Binning; twotrackresonancehistmanager::TwoTrackResonanceHistManager< @@ -91,8 +88,8 @@ struct FemtoTwotrackresonanceQa { // setup for kstar0s twotrackresonancebuilder::ConfKstar0Selection confKstar0Selection; - Partition kstar0Partition = MAKE_RESONANCE_1_PARTITON(confKstar0Selection); - Preslice perColKstars = femtobase::stored::fColId; + o2::framework::Partition kstar0Partition = MAKE_RESONANCE_1_PARTITON(confKstar0Selection); + o2::framework::Preslice perColKstars = o2::aod::femtobase::stored::fColId; twotrackresonancehistmanager::ConfKstar0Binning confKstar0Binning; twotrackresonancehistmanager::TwoTrackResonanceHistManager< @@ -108,80 +105,80 @@ struct FemtoTwotrackresonanceQa { trackhistmanager::ConfResonanceNegDauBinning confNegDaughterBinning; trackhistmanager::ConfResonanceNegDauQaBinning confNegDaughterQaBinning; - HistogramRegistry hRegistry{"ResonanceQA", {}, OutputObjHandlingPolicy::AnalysisObject}; + o2::framework::HistogramRegistry hRegistry{"ResonanceQA", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(InitContext&) + void init(o2::framework::InitContext&) { // create a map for histogram specs auto colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); auto posDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confPosDaughterBinning, confPosDaughterQaBinning); auto negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confNegDaughterBinning, confNegDaughterQaBinning); - if ((doprocessPhis + doprocessRho0s + doprocessKstar0s) > 1) { + if ((static_cast(doprocessPhis) + static_cast(doprocessRho0s) + static_cast(doprocessKstar0s)) > 1) { LOG(fatal) << "Only one process can be activated"; } if (doprocessPhis) { auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceQaHistSpecMap(confPhiBinning); - phiHistManager.init(&hRegistry, phiHistSpec, confPhiSelection, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + phiHistManager.init(&hRegistry, phiHistSpec, confPhiSelection, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessRho0s) { auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceQaHistSpecMap(confRho0Binning); - rho0HistManager.init(&hRegistry, rho0HistSpec, confRho0Selection, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + rho0HistManager.init(&hRegistry, rho0HistSpec, confRho0Selection, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } if (doprocessKstar0s) { auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceQaHistSpecMap(confKstar0Binning); - kstar0HistManager.init(&hRegistry, kstar0HistSpec, confKstar0Selection, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); + kstar0HistManager.init(&hRegistry, kstar0HistSpec, confKstar0Selection, posDaughterHistSpec, confPosDaughterQaBinning, negDaughterHistSpec, confNegDaughterQaBinning); } }; void processPhis(FilteredFemtoCollision const& col, FemtoPhis const& /*phis*/, FemtoTracks const& tracks) { - auto phiSlice = phiPartition->sliceByCached(femtobase::stored::fColId, col.globalIndex(), cache); + auto phiSlice = phiPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (phiSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& phi : phiSlice) { - phiHistManager.fill(phi, tracks); + phiHistManager.fill(phi, tracks); } }; PROCESS_SWITCH(FemtoTwotrackresonanceQa, processPhis, "Process Phis", true); void processRho0s(FilteredFemtoCollision const& col, FemtoRho0s const& /*rho0s*/, FemtoTracks const& tracks) { - auto rho0Slice = rho0Partition->sliceByCached(femtobase::stored::fColId, col.globalIndex(), cache); + auto rho0Slice = rho0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (rho0Slice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& rho0 : rho0Slice) { - rho0HistManager.fill(rho0, tracks); + rho0HistManager.fill(rho0, tracks); } }; PROCESS_SWITCH(FemtoTwotrackresonanceQa, processRho0s, "Process Rho0s", false); void processKstar0s(FilteredFemtoCollision const& col, FemtoKstar0s const& /*kstar0s*/, FemtoTracks const& tracks) { - auto kstar0Slice = kstar0Partition->sliceByCached(femtobase::stored::fColId, col.globalIndex(), cache); + auto kstar0Slice = kstar0Partition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (kstar0Slice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& kstar0 : kstar0Slice) { - kstar0HistManager.fill(kstar0, tracks); + kstar0HistManager.fill(kstar0, tracks); } }; PROCESS_SWITCH(FemtoTwotrackresonanceQa, processKstar0s, "Process Kstar0s", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + o2::framework::WorkflowSpec workflow{ + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto/Tasks/femtoV0Qa.cxx b/PWGCF/Femto/Tasks/femtoV0Qa.cxx index a8bf0fa91e5..4ae00099140 100644 --- a/PWGCF/Femto/Tasks/femtoV0Qa.cxx +++ b/PWGCF/Femto/Tasks/femtoV0Qa.cxx @@ -42,7 +42,7 @@ using namespace o2::analysis::femto; struct FemtoV0Qa { // setup tables - using FemtoCollisions = o2::soa::Join; + using FemtoCollisions = o2::soa::Join; using FilteredFemtoCollisions = o2::soa::Filtered; using FilteredFemtoCollision = FilteredFemtoCollisions::iterator; @@ -58,6 +58,8 @@ struct FemtoV0Qa { using FemtoK0shortsWithLabel = o2::soa::Join; using FemtoTracksWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup for collisions @@ -110,17 +112,17 @@ struct FemtoV0Qa { k0shortHistManager; // setup for daughters - trackhistmanager::ConfV0PosDauBinning confV0PosDaughterBinning; - trackhistmanager::ConfV0PosDauQaBinning confV0PosDaughterQaBinning; + trackhistmanager::ConfV01PosDauBinning confV0PosDaughterBinning; + trackhistmanager::ConfV01PosDauQaBinning confV0PosDaughterQaBinning; - trackhistmanager::ConfV0NegDauBinning confV0NegDaughterBinning; - trackhistmanager::ConfV0NegDauQaBinning confV0NegDaughterQaBinning; + trackhistmanager::ConfV01NegDauBinning confV0NegDaughterBinning; + trackhistmanager::ConfV01NegDauQaBinning confV0NegDaughterQaBinning; o2::framework::HistogramRegistry hRegistry{"FemtoV0Qa", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; void init(o2::framework::InitContext&) { - if ((doprocessLambda + doprocessLambdaMc + doprocessK0short + doprocessK0shortMc) > 1) { + if ((static_cast(doprocessLambda) + static_cast(doprocessLambdaMc) + static_cast(doprocessK0short) + static_cast(doprocessK0shortMc)) != 1) { LOG(fatal) << "Only one process can be activated"; } bool processData = doprocessLambda || doprocessK0short; @@ -136,29 +138,29 @@ struct FemtoV0Qa { if (processData) { colHistSpec = colhistmanager::makeColQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); posDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confV0PosDaughterBinning, confV0PosDaughterQaBinning); negDaughterHistSpec = trackhistmanager::makeTrackQaHistSpecMap(confV0NegDaughterBinning, confV0NegDaughterQaBinning); if (doprocessLambda) { lambdaHistSpec = v0histmanager::makeV0QaHistSpecMap(confLambdaBinning, confLambdaQaBinning); - lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } if (doprocessK0short) { k0shortHistSpec = v0histmanager::makeV0QaHistSpecMap(confK0shortBinning, confK0shortQaBinning); - k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } } else { colHistSpec = colhistmanager::makeColMcQaHistSpecMap(confCollisionBinning, confCollisionQaBinning); - colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); + colHistManager.init(&hRegistry, colHistSpec, confCollisionBinning, confCollisionQaBinning); posDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confV0PosDaughterBinning, confV0PosDaughterQaBinning); negDaughterHistSpec = trackhistmanager::makeTrackMcQaHistSpecMap(confV0NegDaughterBinning, confV0NegDaughterQaBinning); if (doprocessLambdaMc) { lambdaHistSpec = v0histmanager::makeV0McQaHistSpecMap(confLambdaBinning, confLambdaQaBinning); - lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + lambdaHistManager.init(&hRegistry, lambdaHistSpec, confLambdaSelection, confLambdaQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } if (doprocessK0shortMc) { k0shortHistSpec = v0histmanager::makeV0McQaHistSpecMap(confK0shortBinning, confK0shortQaBinning); - k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); + k0shortHistManager.init(&hRegistry, k0shortHistSpec, confK0shortSelection, confK0shortQaBinning, posDaughterHistSpec, confV0PosDaughterQaBinning, negDaughterHistSpec, confV0NegDaughterQaBinning); } } hRegistry.print(); @@ -170,25 +172,25 @@ struct FemtoV0Qa { if (k0shortSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& k0short : k0shortSlice) { - k0shortHistManager.fill(k0short, tracks); + k0shortHistManager.fill(k0short, tracks); } } PROCESS_SWITCH(FemtoV0Qa, processK0short, "Process k0shorts", false); - void processK0shortMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processK0shortMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { auto k0shortSlice = k0shortWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (k0shortSlice.size() == 0) { return; } - colHistManager.fill(col, mcCols); + colHistManager.fill(col, mcCols); for (auto const& k0short : k0shortSlice) { if (!k0shortCleaner.isClean(k0short, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - k0shortHistManager.fill(k0short, tracks, mcParticles, mcMothers, mcPartonicMothers); + k0shortHistManager.fill(k0short, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } PROCESS_SWITCH(FemtoV0Qa, processK0shortMc, "Process k0shorts with MC information", false); @@ -199,34 +201,34 @@ struct FemtoV0Qa { if (lambdaSlice.size() == 0) { return; } - colHistManager.fill(col); + colHistManager.fill(col); for (auto const& lambda : lambdaSlice) { - lambdaHistManager.fill(lambda, tracks); + lambdaHistManager.fill(lambda, tracks); } } PROCESS_SWITCH(FemtoV0Qa, processLambda, "Process lambdas", true); - void processLambdaMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processLambdaMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { auto lambdaSlice = lambdaWithLabelPartition->sliceByCached(o2::aod::femtobase::stored::fColId, col.globalIndex(), cache); if (lambdaSlice.size() == 0) { return; } - colHistManager.fill(col, mcCols); + colHistManager.fill(col, mcCols); for (auto const& lambda : lambdaSlice) { if (!lambdaCleaner.isClean(lambda, mcParticles, mcMothers, mcPartonicMothers)) { continue; } - lambdaHistManager.fill(lambda, tracks, mcParticles, mcMothers, mcPartonicMothers); + lambdaHistManager.fill(lambda, tracks, col, mcParticles, mcMothers, mcPartonicMothers); } } - PROCESS_SWITCH(FemtoV0Qa, processLambdaMc, "Process lambdas", false); + PROCESS_SWITCH(FemtoV0Qa, processLambdaMc, "Process lambdas with MC informaton", false); }; -o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { o2::framework::WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(context), }; return workflow; } diff --git a/PWGCF/Femto3D/DataModel/PIDutils.h b/PWGCF/Femto3D/DataModel/PIDutils.h index 8baed073633..f35737dfc6d 100644 --- a/PWGCF/Femto3D/DataModel/PIDutils.h +++ b/PWGCF/Femto3D/DataModel/PIDutils.h @@ -33,7 +33,7 @@ namespace o2::aod::singletrackselector namespace pidutils { -//========================================== SFINAE checks ========================================== +//========================================== SFINAE checks for TPC ========================================== template struct hasTPCPi : std::false_type { @@ -77,6 +77,8 @@ template struct hasTPCHe>> : std::true_type { }; +//========================================== SFINAE checks for TOF ========================================== + template struct hasTOFPi : std::false_type { }; @@ -119,6 +121,50 @@ template struct hasTOFHe>> : std::true_type { }; +//========================================== SFINAE checks for ITS ========================================== + +template +struct hasITSPi : std::false_type { +}; +template +struct hasITSPi().itsNSigmaPi())>> : std::true_type { +}; + +template +struct hasITSKa : std::false_type { +}; +template +struct hasITSKa().itsNSigmaKa())>> : std::true_type { +}; + +template +struct hasITSPr : std::false_type { +}; +template +struct hasITSPr().itsNSigmaPr())>> : std::true_type { +}; + +template +struct hasITSDe : std::false_type { +}; +template +struct hasITSDe().itsNSigmaDe())>> : std::true_type { +}; + +template +struct hasITSTr : std::false_type { +}; +template +struct hasITSTr().itsNSigmaTr())>> : std::true_type { +}; + +template +struct hasITSHe : std::false_type { +}; +template +struct hasITSHe().itsNSigmaHe())>> : std::true_type { +}; + } // namespace pidutils //========================================== ITS PID ========================================== @@ -128,17 +174,23 @@ inline float getITSNsigma(TrackType const& track, int const& PDG) { switch (PDG) { case 211: - return track.itsNSigmaPi(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSPi::value) + return track.itsNSigmaPi(); case 321: - return track.itsNSigmaKa(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSKa::value) + return track.itsNSigmaKa(); case 2212: - return track.itsNSigmaPr(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSPr::value) + return track.itsNSigmaPr(); case 1000010020: - return track.itsNSigmaDe(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSDe::value) + return track.itsNSigmaDe(); case 1000020030: - return track.itsNSigmaHe(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSHe::value) + return track.itsNSigmaHe(); case 1000010030: - return track.itsNSigmaTr(); + if constexpr (o2::aod::singletrackselector::pidutils::hasITSTr::value) + return track.itsNSigmaTr(); case 0: return -1000.0; default: diff --git a/PWGCF/Femto3D/DataModel/singletrackselector.h b/PWGCF/Femto3D/DataModel/singletrackselector.h index 9241f54fe0f..9eb7d9458ec 100644 --- a/PWGCF/Femto3D/DataModel/singletrackselector.h +++ b/PWGCF/Femto3D/DataModel/singletrackselector.h @@ -16,8 +16,6 @@ #ifndef PWGCF_FEMTO3D_DATAMODEL_SINGLETRACKSELECTOR_H_ #define PWGCF_FEMTO3D_DATAMODEL_SINGLETRACKSELECTOR_H_ -#include "PWGCF/Femto3D/DataModel/PIDutils.h" // IWYU pragma: keep (until PWGLF gets fixed) - #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseITS.h" diff --git a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx index b1704a160b5..7f20deb9c56 100644 --- a/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx +++ b/PWGCF/Femto3D/TableProducer/singleTrackSelector.cxx @@ -85,6 +85,7 @@ struct singleTrackSelector { Configurable> keepWithinNsigmaTPC{"keepWithinNsigmaTPC", std::vector{-4.0f, 4.0f}, "TPC range for preselection of particles specified with PDG"}; Configurable> _particlesToReject{"particlesToRejectPDGs", std::vector{211, 321, 1000020030}, "PDG codes of particles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; Configurable> rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-5.0f, 5.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + Configurable> rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", std::vector{-5.0f, 5.0f}, "TPC Nsigma range required together with rejectWithinNsigmaTOF for particles specified with PDG to be rejected"}; Configurable _pRemoveTofOutOfRange{"pRemoveTofOutOfRange", 100.f, "momentum starting from which request TOF nSigma to be within the stored range (-10 < Nsigma < 10)"}; Configurable> _ptRemoveTofOutOfRange{"ptRemoveTofOutOfRange", {100.f, -10.f, 10.f}, "transverse momentum starting from which request TOF nSigma to be within the stored range (-10 < Nsigma < 10)"}; @@ -182,10 +183,16 @@ struct singleTrackSelector { registry.add("hNEvents_MCGen", "hNEvents_MCGen", {HistType::kTH1F, {{1, 0.f, 1.f}}}); registry.add("hGen_EtaPhiPt_Proton", "Gen (anti)protons in true collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); registry.add("hGen_EtaPhiPt_Deuteron", "Gen (anti)deuteron in true collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); + registry.add("hGen_EtaYPt_Deuteron", "Gen (anti)deuteron in true collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {100, -1., 1., "y"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); registry.add("hGen_EtaPhiPt_Helium3", "Gen (anti)Helium3 in true collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); + registry.add("hGen_EtaPhiPt_Triton", "Gen (anti)triton in true collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); + registry.add("hGen_EtaYPt_Triton", "Gen (anti)triton in true collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {100, -1., 1., "y"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); registry.add("hReco_EtaPhiPt_Proton", "Gen (anti)protons in reco collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); registry.add("hReco_EtaPhiPt_Deuteron", "Gen (anti)deuteron in reco collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); + registry.add("hReco_EtaYPt_Deuteron", "Gen (anti)deuteron in reco collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {100, -1., 1., "y"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); registry.add("hReco_EtaPhiPt_Helium3", "Gen (anti)Helium3 in reco collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); + registry.add("hReco_EtaPhiPt_Triton", "Gen (anti)triton in reco collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {157, 0., o2::constants::math::TwoPI, "#phi"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); + registry.add("hReco_EtaYPt_Triton", "Gen (anti)triton in reco collisions", {HistType::kTH3F, {{100, -1., 1., "#eta"}, {100, -1., 1., "y"}, {100, -5.f, 5.f, "p_{T} GeV/c"}}}); } } @@ -241,7 +248,7 @@ struct singleTrackSelector { for (auto i : particlesToReject) { // if satisfied, want to continue in the upper loop (over tracks) -- skip the current track // cannot use simple 'continue' since it will be applied to the current loop, so have to use a flag - if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(i, rejectWithinNsigmaTOF))) { + if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(i, rejectWithinNsigmaTOF), rejectWithinNsigmaTPC.value)) { skip_track = true; break; } @@ -253,7 +260,7 @@ struct singleTrackSelector { for (auto ii : particlesToKeep) if (o2::aod::singletrackselector::TPCselection(track, std::make_pair(ii, keepWithinNsigmaTPC))) { - if (track.p() > _pRemoveTofOutOfRange && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{-10.0, 10.0}), std::vector{-10.0, 10.0})) + if (track.p() > _pRemoveTofOutOfRange && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{-6.0, 6.0}), std::vector{-10.0, 10.0})) continue; if (track.pt() > _ptRemoveTofOutOfRange.value[0] && !o2::aod::singletrackselector::TOFselection(track, std::make_pair(ii, std::vector{_ptRemoveTofOutOfRange.value[1], _ptRemoveTofOutOfRange.value[2]}), std::vector{-10.f, +10.f})) continue; @@ -567,8 +574,10 @@ struct singleTrackSelector { if (mcParticle.pdgCode() == 1000010020) { registry.fill(HIST("hReco_EtaPhiPt_Deuteron"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt()); + registry.fill(HIST("hReco_EtaYPt_Deuteron"), mcParticle.eta(), mcParticle.y(), mcParticle.pt()); } else if (mcParticle.pdgCode() == -1000010020) { registry.fill(HIST("hReco_EtaPhiPt_Deuteron"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt() * -1); + registry.fill(HIST("hReco_EtaYPt_Deuteron"), mcParticle.eta(), mcParticle.y(), mcParticle.pt() * -1); } if (mcParticle.pdgCode() == 2212) { @@ -582,6 +591,15 @@ struct singleTrackSelector { } else if (mcParticle.pdgCode() == -1000020030) { registry.fill(HIST("hReco_EtaPhiPt_Helium3"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt() * -1); } + + if (mcParticle.pdgCode() == 1000010030) { + registry.fill(HIST("hReco_EtaPhiPt_Triton"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt()); + registry.fill(HIST("hReco_EtaYPt_Triton"), mcParticle.eta(), mcParticle.y(), mcParticle.pt()); + + } else if (mcParticle.pdgCode() == -1000010030) { + registry.fill(HIST("hReco_EtaPhiPt_Triton"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt() * -1); + registry.fill(HIST("hReco_EtaYPt_Triton"), mcParticle.eta(), mcParticle.y(), mcParticle.pt() * -1); + } } } } @@ -607,8 +625,10 @@ struct singleTrackSelector { } if (mcParticle.pdgCode() == 1000010020) { registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt()); + registry.fill(HIST("hGen_EtaYPt_Deuteron"), mcParticle.eta(), mcParticle.y(), mcParticle.pt()); } else if (mcParticle.pdgCode() == -1000010020) { registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt() * -1); + registry.fill(HIST("hGen_EtaYPt_Deuteron"), mcParticle.eta(), mcParticle.y(), mcParticle.pt() * -1); } if (mcParticle.pdgCode() == 2212) { @@ -622,6 +642,14 @@ struct singleTrackSelector { } else if (mcParticle.pdgCode() == -1000020030) { registry.fill(HIST("hGen_EtaPhiPt_Helium3"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt() * -1); } + + if (mcParticle.pdgCode() == 1000010030) { + registry.fill(HIST("hGen_EtaPhiPt_Triton"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt()); + registry.fill(HIST("hGen_EtaYPt_Triton"), mcParticle.eta(), mcParticle.y(), mcParticle.pt()); + } else if (mcParticle.pdgCode() == -1000010030) { + registry.fill(HIST("hGen_EtaPhiPt_Triton"), mcParticle.eta(), mcParticle.phi(), mcParticle.pt() * -1); + registry.fill(HIST("hGen_EtaYPt_Triton"), mcParticle.eta(), mcParticle.y(), mcParticle.pt() * -1); + } } } PROCESS_SWITCH(singleTrackSelector, processGenRun3, "process MC Gen collisions Run3", false); diff --git a/PWGCF/Femto3D/Tasks/CMakeLists.txt b/PWGCF/Femto3D/Tasks/CMakeLists.txt index 4090fea6795..b7b8ad7c31f 100644 --- a/PWGCF/Femto3D/Tasks/CMakeLists.txt +++ b/PWGCF/Femto3D/Tasks/CMakeLists.txt @@ -28,3 +28,8 @@ o2physics_add_dpl_workflow(femto3d-pid-optimization SOURCES PIDoptimization.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femto3d-mult-estimator + SOURCES multEstimator.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx b/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx index 183c2e24d05..0d29ebf334f 100644 --- a/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx +++ b/PWGCF/Femto3D/Tasks/femto3dPairTask.cxx @@ -94,8 +94,9 @@ struct FemtoCorrelations { Configurable> _tofNSigma_2{"tofNSigma_2", std::vector{-3.0f, 3.0f}, "second particle PID: Nsigma range in TOF"}; Configurable> _tpcNSigmaResidual_2{"tpcNSigmaResidual_2", std::vector{-5.0f, 5.0f}, "second particle PID: residual TPC Nsigma cut to use with the TOF"}; - Configurable _particlePDGtoReject{"particlePDGtoRejectFromSecond", 0, "applied only if the particles are non-identical and only to the second particle in the pair!!!"}; - Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for the particle specified with PDG to be rejected"}; + Configurable _particlePDGtoReject{"particlePDGtoRejectFromFirst", 211, "if the particles are non-identical then applied only to the first"}; + Configurable _rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", 0.0, "TPC rejection Nsigma abs range for the particle specified with PDG to be rejected"}; + Configurable _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", 0.0, "TOF rejection Nsigma abs range for the particle specified with PDG to be rejected"}; Configurable _dPhiMode{"dPhiMode", 0, "Flag to choose how to calc. dphi*: 0 - at a fixed TPC radius; 1 - average over different TPC radii;"}; Configurable _radiusTPC{"radiusTPC", 1.2, "TPC radius to calculate phi_star for"}; @@ -122,7 +123,7 @@ struct FemtoCorrelations { // P.P.P.P.S this way is still being testing i might be reconsidered; might change in the future, keep looking at the source code Configurable _MEreductionFactor{"MEreductionFactor", 1, "only one (pseudo)randomly choosen event out per pair $value events will be processed and contribute to the final mixing (if < 2 -> all the possible event pairs (per vertex¢ bin) will be processed); implemented for the sake of efficiency; look at the source code;"}; - bool IsIdentical; + bool IsIdentical = true; std::pair> TPCcuts_1; std::pair> TOFcuts_1; @@ -258,7 +259,7 @@ struct FemtoCorrelations { } if (_fillDetaDphi > -1) { - TString suffix[] = {"_nocut", "_cut"}; + const TString suffix[2] = {"_nocut", "_cut"}; for (int f = 0; f < 2; f++) { std::vector> DoubleTrack_SE_histos_perMult; @@ -356,8 +357,8 @@ struct FemtoCorrelations { if (_fill3dCF && multBin > SEhistos_3D.size()) LOGF(fatal, "multBin value passed to the mixTracks function exceeds the configured number of Cent. bins (3D)"); - for (auto ii : tracks1) { - for (auto iii : tracks2) { + for (const auto& ii : tracks1) { + for (const auto& iii : tracks2) { Pair->SetPair(ii, iii); float pair_kT = Pair->GetKt(); @@ -450,6 +451,9 @@ struct FemtoCorrelations { continue; if (track.sign() == _sign_1 && (track.p() < _PIDtrshld_1 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_1, _itsNSigma_1.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_1, _tpcNSigmaResidual_1.value))) { // filling the map: eventID <-> selected particles1 + if ((_rejectWithinNsigmaTPC != 0.0 && std::fabs(o2::aod::singletrackselector::getTPCNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTPC) || (_rejectWithinNsigmaTOF != 0.0 && std::fabs(o2::aod::singletrackselector::getTOFNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTOF)) + continue; + selectedtracks_1[track.singleCollSelId()].push_back(std::make_shared::iterator>(track)); pHisto_first->Fill(track.p()); @@ -460,7 +464,7 @@ struct FemtoCorrelations { if (IsIdentical) { continue; - } else if (track.sign() != _sign_2 && !TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF)) && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2, _itsNSigma_2.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2, _tpcNSigmaResidual_2.value))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) + } else if (track.sign() != _sign_2 && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2, _itsNSigma_2.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2, _tpcNSigmaResidual_2.value))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) selectedtracks_2[track.singleCollSelId()].push_back(std::make_shared::iterator>(track)); pHisto_second->Fill(track.p()); diff --git a/PWGCF/Femto3D/Tasks/femto3dPairTaskMC.cxx b/PWGCF/Femto3D/Tasks/femto3dPairTaskMC.cxx index 827494f94f3..f1c96a319cd 100644 --- a/PWGCF/Femto3D/Tasks/femto3dPairTaskMC.cxx +++ b/PWGCF/Femto3D/Tasks/femto3dPairTaskMC.cxx @@ -118,14 +118,16 @@ struct FemtoCorrelationsMC { using FilteredTracks = soa::Join; // using FilteredTracks = soa::Join; - typedef std::shared_ptr::iterator> trkType; - typedef std::shared_ptr::iterator> colType; + using TrackType = const soa::Filtered::iterator; + using TrackTypePtr = std::shared_ptr; + using ColType = const soa::Filtered::iterator; + using ColTypePtr = std::shared_ptr; - std::map> selectedtracks_1; - std::map> selectedtracks_2; - std::map, std::vector> mixbins; + std::map> selectedtracks_1; + std::map> selectedtracks_2; + std::map, std::vector> mixbins; - std::unique_ptr> Pair = std::make_unique>(); + std::unique_ptr> Pair = std::make_unique>(); Filter pFilter = o2::aod::singletrackselector::p > _min_P&& o2::aod::singletrackselector::p < _max_P; Filter etaFilter = nabs(o2::aod::singletrackselector::eta) < _eta; @@ -283,8 +285,8 @@ struct FemtoCorrelationsMC { template void fillEtaPhi(Type const& tracks1, Type const& tracks2, unsigned int centBin) { // template for particles from the same collision non-identical - for (auto ii : tracks1) { - for (auto iii : tracks2) { + for (const auto& ii : tracks1) { + for (const auto& iii : tracks2) { Pair->SetPair(ii, iii); float pair_kT = Pair->GetKt(); @@ -307,8 +309,8 @@ struct FemtoCorrelationsMC { template void fillResMatrix(Type const& tracks1, Type const& tracks2, unsigned int centBin) { // template for ME - for (auto ii : tracks1) { - for (auto iii : tracks2) { + for (const auto& ii : tracks1) { + for (const auto& iii : tracks2) { Pair->SetPair(ii, iii); float pair_kT = Pair->GetKt(); @@ -344,7 +346,7 @@ struct FemtoCorrelationsMC { int trackPDG, trackOrigin; - for (auto track : tracks) { + for (const auto& track : tracks) { if (std::fabs(track.template singleCollSel_as>().posZ()) > _vertexZ) continue; if (track.tpcFractionSharedCls() > _tpcFractionSharedCls || track.itsNCls() < _itsNCls) @@ -385,7 +387,7 @@ struct FemtoCorrelationsMC { if (trackPDG == 11 || trackPDG == 13 || trackPDG == 211 || trackPDG == 321 || trackPDG == 2212 || trackPDG == 1000010020) Purity_histos_1[centBin][trackPDG]->Fill(track.p()); - selectedtracks_1[track.singleCollSelId()].push_back(std::make_shared(track)); // filling the map: eventID <-> selected particles1 + selectedtracks_1[track.singleCollSelId()].push_back(std::make_shared(track)); // filling the map: eventID <-> selected particles1 } if (IsIdentical) { @@ -406,11 +408,11 @@ struct FemtoCorrelationsMC { if (trackPDG == 11 || trackPDG == 13 || trackPDG == 211 || trackPDG == 321 || trackPDG == 2212 || trackPDG == 1000010020) Purity_histos_2[centBin][trackPDG]->Fill(track.p()); - selectedtracks_2[track.singleCollSelId()].push_back(std::make_shared(track)); // filling the map: eventID <-> selected particles2 + selectedtracks_2[track.singleCollSelId()].push_back(std::make_shared(track)); // filling the map: eventID <-> selected particles2 } } - for (auto collision : collisions) { + for (const auto& collision : collisions) { if (collision.multPerc() < *_centBins.value.begin() || collision.multPerc() >= *(_centBins.value.end() - 1)) continue; if (collision.hadronicRate() < _IRcut.value.first || collision.hadronicRate() >= _IRcut.value.second) @@ -440,7 +442,7 @@ struct FemtoCorrelationsMC { int vertexBinToMix = std::floor((collision.posZ() + _vertexZ) / (2 * _vertexZ / _vertexNbinsToMix)); float centBinToMix = o2::aod::singletrackselector::getBinIndex(collision.multPerc(), _centBins, _multNsubBins); - mixbins[std::pair{vertexBinToMix, centBinToMix}].push_back(std::make_shared(collision)); + mixbins[std::pair{vertexBinToMix, centBinToMix}].push_back(std::make_shared(collision)); } //====================================== filling deta(dphi*) & res. matrix starts here ====================================== diff --git a/PWGCF/Femto3D/Tasks/femto3dQA.cxx b/PWGCF/Femto3D/Tasks/femto3dQA.cxx index e55cd3c73ed..5e218e1a938 100644 --- a/PWGCF/Femto3D/Tasks/femto3dQA.cxx +++ b/PWGCF/Femto3D/Tasks/femto3dQA.cxx @@ -75,15 +75,16 @@ struct QAHistograms { Configurable _tpcFractionSharedCls{"maxTpcFractionSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; Configurable _itsNCls{"minItsNCls", 0, "minimum allowed number of ITS clasters for a track"}; Configurable _itsChi2NCl{"itsChi2NCl", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track"}; - Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for (only pion, kaon, proton and deurton are supported now)"}; + Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for"}; Configurable> _tpcNSigma{"tpcNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TPC before the TOF is used"}; Configurable> _itsNSigma{"itsNSigma", std::vector{-10.0f, 10.0f}, "Nsigma range in ITS to use along with TPC"}; Configurable _PIDtrshld{"PIDtrshld", 10.0, "value of momentum from which the PID is done with TOF (before that only TPC is used)"}; Configurable> _tofNSigma{"tofNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TOF"}; Configurable> _tpcNSigmaResidual{"tpcNSigmaResidual", std::vector{-5.0f, 5.0f}, "residual TPC Nsigma cut to use with the TOF"}; - Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of perticles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; - Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of particles that will be rejected with TPC and/or TOF"}; + Configurable _rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", 0.0, "TPC rejection Nsigma abs for particles specified with PDG to be rejected"}; + Configurable _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", 0.0, "TOF rejection Nsigma abs for particles specified with PDG to be rejected"}; Configurable> _centCut{"centCut", std::pair{0.f, 100.f}, "[min., max.] centrality range to keep tracks within"}; @@ -235,7 +236,10 @@ struct QAHistograms { registry.fill(HIST("TOFSignal_nocuts"), track.p(), track.beta()); } - if (!TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF)) && (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value))) { + if (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value)) { + if ((_rejectWithinNsigmaTPC != 0.0 && std::fabs(o2::aod::singletrackselector::getTPCNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTPC) || (_rejectWithinNsigmaTOF != 0.0 && std::fabs(o2::aod::singletrackselector::getTOFNsigma(track, _particlePDGtoReject)) < _rejectWithinNsigmaTOF)) + continue; + registry.fill(HIST("eta"), track.eta()); registry.fill(HIST("phi"), track.phi()); registry.fill(HIST("px"), track.px()); diff --git a/PWGCF/Femto3D/Tasks/multEstimator.cxx b/PWGCF/Femto3D/Tasks/multEstimator.cxx new file mode 100644 index 00000000000..1d6b9e845eb --- /dev/null +++ b/PWGCF/Femto3D/Tasks/multEstimator.cxx @@ -0,0 +1,332 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \brief create a table applying some basic cuts on the ITS and DCA. +/// \author Sofia Tomassini, Gleb Romanenko, Nicolò Jacazio +/// \since 31 May 2023 + +#include "PWGCF/Femto3D/DataModel/PIDutils.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::track; +using namespace o2::aod; +//::singletrackselector; // the namespace defined in .h + +struct multEstimator { + Service ccdb; + + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable CBThadronPID{"CBThadronPID", false, "Apply ev. sel. based on RCT flag `hadronPID`"}; // more in Common/CCDB/RCTSelectionFlags.h + Configurable centTableToUse{"centTableToUse", 1, "Flag to choose cent./mult.perc. estimator (Run3 only [FTOC for PbPb; FTOM for pp], for Run2 the V0M is used): 0 -> CentFV0As, 1 -> CentFT0Ms, 2 -> CentFT0As, 3 -> CentFT0Cs, 4 -> CentFDDMs, 5 -> CentNTPVs"}; + + Configurable applyEvSel{"applyEvSel", 2, "Flag to apply rapidity cut: 0 -> no event selection, 1 -> Run 2 event selection, 2 -> Run 3 event selection"}; + Configurable _eta{"eta", 0.5, "abs eta value limit"}; + Configurable _vertexZ{"VertexZ", 10.0, "abs vertexZ value limit"}; + + // the cuts below are used to count and for the conditional mult ... + + Configurable _maxTofChi2{"maxTofChi2", 10.f, "Maximum TOF Chi2 value -> to remove mismatched tracks"}; + + Configurable rejectNotPropagatedTrks{"rejectNotPropagatedTrks", true, "rejects tracks that are not propagated to the primary vertex"}; + Configurable _removeSameBunchPileup{"removeSameBunchPileup", false, ""}; + Configurable _requestGoodZvtxFT0vsPV{"requestGoodZvtxFT0vsPV", false, ""}; + Configurable _requestVertexITSTPC{"requestVertexITSTPC", false, ""}; + Configurable _requestVertexTOForTRDmatched{"requestVertexTOFmatched", 0, "0 -> no selectio; 1 -> vertex is matched to TOF or TRD; 2 -> matched to both;"}; + Configurable _requestNoCollInTimeRangeStandard{"requestNoCollInTimeRangeStandard", false, ""}; + Configurable _requestIsGoodITSLayersAll{"requestIsGoodITSLayersAll", false, "cut time intervals with dead ITS staves"}; + // Configurable fetchRate{"fetchRate", true, "Fetch the hadronic rate from the CCDB"}; + Configurable> _IRcut{"IRcut", std::pair{0.f, 100.f}, "[min., max.] IR range to keep events within"}; + Configurable> _OccupancyCut{"OccupancyCut", std::pair{0, 10000}, "[min., max.] occupancy range to keep events within"}; + + Configurable _sign{"sign", 1, "sign of a track"}; + Configurable _min_P{"min_P", 0.0, "lower mometum limit"}; + Configurable _max_P{"max_P", 100.0, "upper mometum limit"}; + Configurable> _dcaXY{"dcaXY", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaXY value limit; formula: [0] + [1]*pT^[2]"}; + Configurable> _dcaZ{"dcaZ", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaZ value limit; formula: [0] + [1]*pT^[2]"}; + Configurable _tpcNClsFound{"minTpcNClsFound", 0, "minimum allowed number of TPC clasters"}; + Configurable _tpcChi2NCl{"tpcChi2NCl", 100.0, "upper limit for chi2 value of a fit over TPC clasters"}; + Configurable _tpcCrossedRowsOverFindableCls{"tpcCrossedRowsOverFindableCls", 0, "lower limit of TPC CrossedRows/FindableCls value"}; + Configurable _tpcFractionSharedCls{"maxTpcFractionSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; + Configurable _itsNCls{"minItsNCls", 0, "minimum allowed number of ITS clasters for a track"}; + Configurable _itsChi2NCl{"itsChi2NCl", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track"}; + + Configurable> _dcaXYCond{"dcaXYCond", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaXY value limit for conditioned multiplicity; formula: [0] + [1]*pT^[2]"}; + Configurable> _dcaZCond{"dcaZCond", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaZ value limit for conditioned multiplicity; formula: [0] + [1]*pT^[2]"}; + Configurable _tpcNClsFoundCond{"minTpcNClsFoundCond", 0, "minimum allowed number of TPC clasters for conditioned multiplicity"}; + Configurable _tpcChi2NClCond{"tpcChi2NClCond", 100.0, "upper limit for chi2 value of a fit over TPC clasters for conditioned multiplicity"}; + Configurable _tpcCrossedRowsOverFindableClsCond{"tpcCrossedRowsOverFindableClsCond", 0, "lower limit of TPC CrossedRows/FindableCls value for conditioned multiplicity"}; + Configurable _itsNClsCond{"minItsNClsCond", 0, "minimum allowed number of ITS clasters for a track for conditioned multiplicity"}; + Configurable _itsChi2NClCond{"itsChi2NClCond", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track for conditioned multiplicity"}; + + Configurable _particlePDG{"particlePDG", 2212, "PDG code of a particle to perform PID for (only pion, kaon, proton and deurton are supported now)"}; + Configurable> _tpcNSigma{"tpcNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TPC before the TOF is used"}; + Configurable> _itsNSigma{"itsNSigma", std::vector{-10.0f, 10.0f}, "Nsigma range in ITS to use along with TPC"}; + Configurable _PIDtrshld{"PIDtrshld", 10.0, "value of momentum from which the PID is done with TOF (before that only TPC is used)"}; + Configurable> _tofNSigma{"tofNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TOF"}; + Configurable> _tpcNSigmaResidual{"tpcNSigmaResidual", std::vector{-5.0f, 5.0f}, "residual TPC Nsigma cut to use with the TOF"}; + + Configurable _particlePDGtoReject{"particlePDGtoReject", 211, "PDG codes of perticles that will be rejected with TOF (only pion, kaon, proton and deurton are supported now)"}; + Configurable> _rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + + Configurable _minNcandidates{"minNcandidates", 1, "minimum namber of candidates for the conditional multiplicity"}; + + std::pair> TPCcuts; + std::pair> TOFcuts; + + std::shared_ptr ITShisto; + std::shared_ptr TPChisto; + std::shared_ptr TOFhisto; + + Configurable> _centCut{"centCut", std::pair{0.f, 100.f}, "[min., max.] centrality range to keep events within"}; + + using Trks = soa::Join; + + using CollRun3 = soa::Join; + + Filter eventFilter = (applyEvSel.node() == 0) || + ((applyEvSel.node() == 1) && (aod::evsel::sel7 == true)) || + ((applyEvSel.node() == 2) && (aod::evsel::sel8 == true)); + Filter vertexFilter = nabs(o2::aod::collision::posZ) < _vertexZ; + + Filter etaFilter = nabs(o2::aod::track::eta) < _eta; + + ctpRateFetcher mRateFetcher; // inspired by zdcSP.cxx in PWGLF + int mRunNumber = 0; + + int tot_counter = 0; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + SliceCache cache; + + rctsel::RCTFlagsChecker myChecker{"CBT_hadronPID"}; + + std::shared_ptr Nch_vs_cent_vs_eta; + std::shared_ptr Nch_vs_cent_vs_eta_conditional; + std::shared_ptr tmp_histo_per_event; + std::shared_ptr Events_vs_cent; + std::shared_ptr Events_vs_cent_conditional; + + void init(InitContext&) + { + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + myChecker.init("CBT_hadronPID", true); + + TPCcuts = std::make_pair(_particlePDG.value, _tpcNSigma.value); + TOFcuts = std::make_pair(_particlePDG.value, _tofNSigma.value); + + Nch_vs_cent_vs_eta = registry.add("Nch_vs_cent_vs_eta", "Nch_vs_cent_vs_eta", kTH2F, {{100, 0.0, 100.0, "cent"}, {200, -1.0, 1.0, "deta"}}); + Nch_vs_cent_vs_eta_conditional = registry.add("Nch_vs_cent_vs_eta_conditional", "Nch_vs_cent_vs_eta_conditional", kTH2F, {{100, 0.0, 100.0, "cent"}, {200, -1.0, 1.0, "deta"}}); + tmp_histo_per_event = std::make_shared(TH2F("tmp_histo_per_event", "tmp_histo_per_event", 100, 0.0, 100.0, 200, -1.0, 1.0)); + + Events_vs_cent = registry.add("Events_vs_cent", "Events_vs_cent", kTH1F, {{100, 0.0, 100., "cent"}}); + Events_vs_cent_conditional = registry.add("Events_vs_cent_conditional", "Events_vs_cent_conditional", kTH1F, {{100, 0.0, 100.0, "cent"}}); + + ITShisto = registry.add(Form("nsigmaITS_PDG%i", _particlePDG.value), Form("nsigmaITS_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + TPChisto = registry.add(Form("nsigmaTPC_PDG%i", _particlePDG.value), Form("nsigmaTPC_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + TOFhisto = registry.add(Form("nsigmaTOF_PDG%i", _particlePDG.value), Form("nsigmaTOF_PDG%i", _particlePDG.value), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) // inspired by PWGLF/TableProducer/lambdakzerobuilder.cxx + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + } + + void process(soa::Filtered::iterator const& collision, + soa::Filtered const& tracks, + aod::BCsWithTimestamps const&) + + { + + const auto& bc = collision.bc_as(); + initCCDB(bc); + + if (!myChecker(*collision) && CBThadronPID) + return; + + if (_removeSameBunchPileup && !collision.selection_bit(evsel::kNoSameBunchPileup)) + return; + if (_requestGoodZvtxFT0vsPV && !collision.selection_bit(evsel::kIsGoodZvtxFT0vsPV)) + return; + if (std::fabs(collision.posZ()) > _vertexZ) + return; + + int occupancy = collision.trackOccupancyInTimeRange(); + + float centValue = -100.0f; + + switch (centTableToUse) { + case 0: + centValue = collision.centFV0A(); + break; + case 1: + centValue = collision.centFT0M(); + break; + case 2: + centValue = collision.centFT0A(); + break; + case 3: + centValue = collision.centFT0C(); + break; + case 4: + centValue = collision.centFDDM(); + break; + case 5: + centValue = collision.centNTPV(); + break; + default: + LOGF(fatal, "Invalid flag for cent./mult.perc. estimator has been choosen. Please check."); + break; + } + + if (centValue < _centCut.value.first || centValue >= _centCut.value.second) + return; + + Events_vs_cent->Fill(centValue); + // ============================ dNch/deta no cuts ================================ + + int cadidates_counter = 0; + + // auto tracksWithPid = soa::Attach, aod::pidits::ITSNSigmaPi, aod::pidits::ITSNSigmaKa, aod::pidits::ITSNSigmaPr, aod::pidits::ITSNSigmaDe, aod::pidits::ITSNSigmaTr, aod::pidits::ITSNSigmaHe>(tracks); + auto tracksWithPid = soa::Attach, aod::pidits::ITSNSigmaPr>(tracks); + + for (const auto& track : tracksWithPid) { + + // ============================ dNch/deta normal ================================ + + if (rejectNotPropagatedTrks && track.trackType() != aod::track::Track) { + continue; + } + + if ((track.tpcNClsFound()) < _tpcNClsFound || (track.itsNCls()) < _itsNCls) + continue; + if ((track.itsChi2NCl()) > _itsChi2NCl || (track.tpcChi2NCl()) > _tpcChi2NCl) + continue; + if ((track.tpcCrossedRowsOverFindableCls()) < _tpcCrossedRowsOverFindableCls) + continue; + if (std::fabs(track.dcaXY()) > _dcaXY.value[0] + _dcaXY.value[1] * std::pow(track.pt(), _dcaXY.value[2]) || std::fabs(track.dcaZ()) > _dcaZ.value[0] + _dcaZ.value[1] * std::pow(track.pt(), _dcaZ.value[2])) + continue; + + if (track.sign() != 0) { + tmp_histo_per_event->Fill(centValue, track.eta()); + tot_counter++; + } + + // ============================ dNch/deta conditioned (additional cuts) ================================ + + // analisiys track cuts + if (std::fabs(track.eta()) > _eta) // because soa::Attach operation breaks the filtering + continue; + if (track.sign() != _sign) + continue; + if (track.p() < _min_P || track.p() > _max_P) + continue; + if (_requestVertexITSTPC && !collision.selection_bit(evsel::kIsVertexITSTPC)) + continue; + if (_requestVertexTOForTRDmatched > static_cast(collision.selection_bit(evsel::kIsVertexTOFmatched)) + static_cast(collision.selection_bit(evsel::kIsVertexTRDmatched))) + continue; + if (_requestNoCollInTimeRangeStandard && !collision.selection_bit(evsel::kNoCollInTimeRangeStandard)) + continue; + if (_requestIsGoodITSLayersAll && !collision.selection_bit(evsel::kIsGoodITSLayersAll)) + continue; + + if (occupancy < _OccupancyCut.value.first || occupancy >= _OccupancyCut.value.second) + continue; + if (track.tofChi2() > _maxTofChi2) + continue; + if ((track.tpcNClsFound()) < _tpcNClsFoundCond || (track.itsNCls()) < _itsNClsCond) + continue; + if ((track.itsChi2NCl()) > _itsChi2NClCond || (track.tpcChi2NCl()) > _tpcChi2NClCond) + continue; + if ((track.tpcCrossedRowsOverFindableCls()) < _tpcCrossedRowsOverFindableClsCond) + continue; + if (std::fabs(track.dcaXY()) > _dcaXYCond.value[0] + _dcaXYCond.value[1] * std::pow(track.pt(), _dcaXYCond.value[2]) || std::fabs(track.dcaZ()) > _dcaZCond.value[0] + _dcaZCond.value[1] * std::pow(track.pt(), _dcaZCond.value[2])) + continue; + // ========================== PID cuts ========================== + + if (o2::aod::singletrackselector::TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF))) + continue; + + if (track.p() < _PIDtrshld ? o2::aod::singletrackselector::TPCselection(track, TPCcuts, _itsNSigma.value) : o2::aod::singletrackselector::TOFselection(track, TOFcuts, _tpcNSigmaResidual.value)) { + ITShisto->Fill(track.p(), o2::aod::singletrackselector::getITSNsigma(track, _particlePDG)); + TPChisto->Fill(track.p(), o2::aod::singletrackselector::getTPCNsigma(track, _particlePDG)); + TOFhisto->Fill(track.p(), o2::aod::singletrackselector::getTOFNsigma(track, _particlePDG)); + cadidates_counter++; + } + } + Nch_vs_cent_vs_eta->Add(tmp_histo_per_event.get()); + if (Nch_vs_cent_vs_eta->GetEntries() != tot_counter) + LOGF(fatal, "tot counter != entries"); + if (cadidates_counter >= _minNcandidates) { + Nch_vs_cent_vs_eta_conditional->Add(tmp_histo_per_event.get()); + Events_vs_cent_conditional->Fill(centValue); + } + + tmp_histo_per_event->Reset(); + if (tmp_histo_per_event->GetEntries()) + LOGF(fatal, "not cleared"); + cadidates_counter = 0; + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/Femto3D/Tools/checkPacking.cxx b/PWGCF/Femto3D/Tools/checkPacking.cxx index 50a1c4cd60a..085f9825d31 100644 --- a/PWGCF/Femto3D/Tools/checkPacking.cxx +++ b/PWGCF/Femto3D/Tools/checkPacking.cxx @@ -30,7 +30,7 @@ using namespace o2; template -bool process(const TString outputName, const int nevents = 100000) +bool process(const TString& outputName, const int nevents = 100000) { class Container { diff --git a/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h b/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h index 542de338734..c958a4d9af0 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h +++ b/PWGCF/FemtoDream/Core/femtoDreamCollisionSelection.h @@ -31,6 +31,7 @@ #include #include #include +#include #include namespace o2::analysis::femtoDream @@ -420,7 +421,7 @@ class FemtoDreamCollisionSelection // add a param : bool doFillHisto ? int myqnBin(float centrality, float centMax, bool doFillCent, std::vector qnBinSeparator, float qn, const int numQnBins, float centBinWidth = 1.f) { - auto twoDSeparator = getQnBinSeparator2D(qnBinSeparator, numQnBins); + auto twoDSeparator = getQnBinSeparator2D(std::move(qnBinSeparator), numQnBins); if (twoDSeparator.empty() || twoDSeparator[0][0] == -999.) { LOGP(warning, "ConfQnBinSeparator not set, using default fallback!"); return -999; // safe fallback diff --git a/PWGCF/FemtoDream/Core/femtoDreamContainer.h b/PWGCF/FemtoDream/Core/femtoDreamContainer.h index 8a8f05b6879..ec18b4a1d1a 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamContainer.h +++ b/PWGCF/FemtoDream/Core/femtoDreamContainer.h @@ -30,8 +30,10 @@ #include +#include #include #include +#include #include namespace o2::analysis::femtoDream @@ -46,6 +48,9 @@ enum Observable { kstar ///< kstar enum EventType { same, ///< Pair from same event mixed ///< Pair from mixed event }; + +static constexpr int nMinMomComponents{4}; ///< Minimum number of momentum components to compute the femto observable + }; // namespace femtoDreamContainer /// \class FemtoDreamContainer @@ -72,7 +77,7 @@ class FemtoDreamContainer /// \param mTAxis axis object for the mT axis template - void init_base(std::string folderName, std::string femtoObs, + void init_base(const std::string& folderName, const std::string& femtoObs, T& femtoObsAxis, T& pTAxis, T& kTAxis, T& mTAxis, T& multAxis, T& multPercentileAxis, T& /*kstarAxis4D*/, T& mTAxis4D, T& multAxis4D, T& multPercentileAxis4D, bool use4dplots, bool extendedplots, T& mP2Axis) @@ -105,7 +110,7 @@ class FemtoDreamContainer /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) /// \param femtoObsAxis axis object for the femto observable axis template - void init_MC(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis, T mTAxis, bool smearingByOrigin) + void init_MC(const std::string& folderName, const std::string& femtoObs, T femtoObsAxis, T multAxis, T mTAxis, bool smearingByOrigin) { mHistogramRegistry->add((folderName + "/relPairDist_ReconNoFake").c_str(), ("; " + femtoObs + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxis}); mHistogramRegistry->add((folderName + "/relPairkstarmT_ReconNoFake").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}^{2})").c_str(), o2::framework::HistType::kTH2F, {femtoObsAxis, mTAxis}); @@ -158,7 +163,7 @@ class FemtoDreamContainer framework::AxisSpec multAxis4D = {multBins4D, "Multiplicity"}; framework::AxisSpec multPercentileAxis4D = {multPercentileBins4D, "Multiplicity Percentile (%)"}; - framework::AxisSpec mP2Axis = {invMassBin, "Mass (GeV)"}; + framework::AxisSpec mP2Axis = {std::move(invMassBin), "Mass (GeV)"}; std::string folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kRecon]); @@ -179,8 +184,8 @@ class FemtoDreamContainer /// Initialize the histograms for pairs in divided qn or phi-psi bins template - void init_base_EP(std::string folderName, std::string femtoObs, - T& femtoObsAxis, T& mTAxi4D, T& multPercentileAxis4D, T& epObsAxis, std::string epObs) + void init_base_EP(const std::string& folderName, const std::string& femtoObs, + T& femtoObsAxis, T& mTAxi4D, T& multPercentileAxis4D, T& epObsAxis, const std::string& epObs) { mHistogramRegistry->add((folderName + "/relPairkstarmTMultMultPercentileQn").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}); Centrality;" + epObs).c_str(), o2::framework::HistType::kTHnSparseF, {femtoObsAxis, mTAxi4D, multPercentileAxis4D, epObsAxis}); } @@ -216,14 +221,19 @@ class FemtoDreamContainer /// Initialize the histograms for pairs with 3D component in divided qn bins template - void init_base_3Dqn(std::string folderName, std::string femtoDKout, std::string femtoDKside, std::string femtoDKlong, + void init_base_3Dqn(const std::string& folderName, const std::string& femtoDKout, const std::string& femtoDKside, const std::string& femtoDKlong, T& femtoDKoutAxis, T& femtoDKsideAxis, T& femtoDKlongAxis, T& mTAxi4D, T& multPercentileAxis4D, T& qnAxis, T& pairPhiAxis) { - mHistogramRegistry->add((folderName + "/relPair3dRmTMultPercentileQnPairphi").c_str(), ("; " + femtoDKout + femtoDKside + femtoDKlong + "; #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}").c_str(), o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + if (mStoreEProt) { + // DK_x, DK_y, DK_z are the EP-rotated (B-frame) axes; DK_y is out-of-plane (||B). + mHistogramRegistry->add((folderName + "/relPair3dEProtRmTMultPercentileQnPairphi").c_str(), "; DK_{x} (GeV/#it{c}); DK_{y} (GeV/#it{c}); DK_{z} (GeV/#it{c}); #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}", o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + } else { + mHistogramRegistry->add((folderName + "/relPair3dRmTMultPercentileQnPairphi").c_str(), ("; " + femtoDKout + femtoDKside + femtoDKlong + "; #it{m}_{T} (GeV/#it{c}); Centrality; qn; #varphi_{pair} - #Psi_{EP}").c_str(), o2::framework::HistType::kTHnSparseF, {femtoDKoutAxis, femtoDKsideAxis, femtoDKlongAxis, mTAxi4D, multPercentileAxis4D, qnAxis, pairPhiAxis}); + } } template - void init_3Dqn_MC(std::string folderName, std::string femtoDKout, std::string femtoDKside, std::string femtoDKlong, + void init_3Dqn_MC(const std::string& folderName, const std::string& femtoDKout, const std::string& femtoDKside, const std::string& femtoDKlong, T& femtoDKoutAxis, T& femtoDKsideAxis, T& femtoDKlongAxis, bool smearingByOrigin = false) { mHistogramRegistry->add((folderName + "/hNoMCtruthPairsCounter").c_str(), "; Counter; Entries", o2::framework::HistType::kTH1I, {{1, 0, 1}}); @@ -254,18 +264,26 @@ class FemtoDreamContainer framework::AxisSpec DKlongAxis = {DKlongBins, femtoObsDKlong}; framework::AxisSpec mTAxis4D = {mTBins4D, "#it{m}_{T} (GeV/#it{c})"}; framework::AxisSpec multPercentileAxis4D = {multPercentileBins4D, "Centralty(%)"}; - framework::AxisSpec qnAxis = {qnBins, "qn"}; - framework::AxisSpec pairPhiAxis = {pairPhiBins, "#varphi_{pair} - #Psi_{EP} (rad)"}; + framework::AxisSpec qnAxis = {std::move(qnBins), "qn"}; + framework::AxisSpec pairPhiAxis = {std::move(pairPhiBins), "#varphi_{pair} - #Psi_{EP} (rad)"}; + + // EP-rotated (B-frame) axis labels, same binning as out/side/long. + framework::AxisSpec DKxAxis = {DKoutBins, "DK_{x} (GeV/#it{c})"}; + framework::AxisSpec DKyAxis = {DKsideBins, "DK_{y} (GeV/#it{c})"}; + framework::AxisSpec DKzAxis = {DKlongBins, "DK_{z} (GeV/#it{c})"}; + framework::AxisSpec& mom1Axis = mStoreEProt ? DKxAxis : DKoutAxis; + framework::AxisSpec& mom2Axis = mStoreEProt ? DKyAxis : DKsideAxis; + framework::AxisSpec& mom3Axis = mStoreEProt ? DKzAxis : DKlongAxis; std::string folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kRecon]) + static_cast("_3Dqn"); init_base_3Dqn(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, - DKoutAxis, DKsideAxis, DKlongAxis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); + mom1Axis, mom2Axis, mom3Axis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); if (isMC) { folderName = static_cast(mFolderSuffix[mEventType]) + static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + static_cast("_3Dqn"); init_base_3Dqn(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, - DKoutAxis, DKsideAxis, DKlongAxis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); + mom1Axis, mom2Axis, mom3Axis, mTAxis4D, multPercentileAxis4D, qnAxis, pairPhiAxis); init_3Dqn_MC(folderName, femtoObsDKout, femtoObsDKside, femtoObsDKlong, DKoutAxis, DKsideAxis, DKlongAxis, smearingByOrigin); } @@ -282,6 +300,9 @@ class FemtoDreamContainer mPDGTwo = pdg2; } + /// Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long. Call before init_3Dqn(). + void setStoreEProt(bool doStore) { mStoreEProt = doStore; } + /// Pass a pair to the container and compute all the relevant observables /// Called by setPair both in case of data/ and Monte Carlo reconstructed and for Monte Carlo truth /// \tparam T type of the femtodreamparticle @@ -367,18 +388,21 @@ class FemtoDreamContainer if constexpr (isMC) { if constexpr (isHF) { // calculate the femto observable and the mT with MC truth information - if constexpr (mFemtoObs == femtoDreamContainer::Observable::kstar) { - femtoObsMC = FemtoDreamMath::getkstar(part1.fdMCParticle(), mMassOne, part2, mMassTwo); - } - const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2, mMassTwo); - - if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPair_base(femtoObsMC, mTMC, part1.fdMCParticle(), part2, mult, multPercentile, use4dplots, extendedplots); - setPair_MC(femtoObsMC, femtoObs, mT, mult, part1.fdMCParticle().partOriginMCTruth(), part2.flagMc(), smearingByOrigin); + if (part1.has_fdMCParticle()) { + if constexpr (mFemtoObs == femtoDreamContainer::Observable::kstar) { + femtoObsMC = FemtoDreamMath::getkstar(part1.fdMCParticle(), mMassOne, part2, mMassTwo); + } + const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2, mMassTwo); + + if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates + setPair_base(femtoObsMC, mTMC, part1.fdMCParticle(), part2, mult, multPercentile, use4dplots, extendedplots); + setPair_MC(femtoObsMC, femtoObs, mT, mult, part1.fdMCParticle().partOriginMCTruth(), part2.flagMc(), smearingByOrigin); + } else { + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); + } } else { - mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hNoMCtruthPairsCounter"), 0); } - } else if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { // calculate the femto observable and the mT with MC truth information if constexpr (mFemtoObs == femtoDreamContainer::Observable::kstar) { @@ -501,6 +525,16 @@ class FemtoDreamContainer mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[mc]) + HIST("_3Dqn") + HIST("/relPair3dRmTMultPercentileQnPairphi"), femtoDKout, femtoDKside, femtoDKlong, mT, multPercentile, myQnBin, pairPhiEP); } + /// EP-rotated (B-frame) version: rotate (out, side) by the signed φ_pair − Ψ_EP so DK_y is out-of-plane (||B); DK_z unchanged. + template + void setPair_3Dqn_base(const float femtoDKout, const float femtoDKside, const float femtoDKlong, const float mT, const float multPercentile, const float myQnBin, const float pairPhiEP, const float pairPhiEPforRot) + { + const float DKx = femtoDKout * std::cos(pairPhiEPforRot) - femtoDKside * std::sin(pairPhiEPforRot); + const float DKy = femtoDKout * std::sin(pairPhiEPforRot) + femtoDKside * std::cos(pairPhiEPforRot); + const float DKz = femtoDKlong; + mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[mc]) + HIST("_3Dqn") + HIST("/relPair3dEProtRmTMultPercentileQnPairphi"), DKx, DKy, DKz, mT, multPercentile, myQnBin, pairPhiEP); + } + /// Called by setPair_3Dqn only in case of Monte Carlo truth /// Fills resolution of DKout, DKside, DKlong void setPair_3Dqn_MC(std::vector k3dMC, std::vector k3d, const int originPart1, const int originPart2, bool smearingByOrigin) @@ -517,7 +551,7 @@ class FemtoDreamContainer { std::vector k3d = FemtoDreamMath::newpairfunc(part1, mMassOne, part2, mMassTwo, IsSameSpecies); - if (k3d.size() < 4) { + if (k3d.size() < femtoDreamContainer::nMinMomComponents) { LOG(error) << "newpairfunc returned size=" << k3d.size(); return; } @@ -528,23 +562,33 @@ class FemtoDreamContainer const float mT = FemtoDreamMath::getmT(part1, mMassOne, part2, mMassTwo); const float pairPhiEP = FemtoDreamMath::getPairPhiEP(part1, mMassOne, part2, mMassTwo, eventPlane); + const float pairPhiEPforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1, mMassOne, part2, mMassTwo, eventPlane) : 0.f; if (mHistogramRegistry) { - setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + if (mStoreEProt) { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP, pairPhiEPforRot); + } else { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + } if constexpr (isMC) { if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { std::vector k3dMC = FemtoDreamMath::newpairfuncMC(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, IsSameSpecies); - if (k3dMC.size() < 4) { + if (k3dMC.size() < femtoDreamContainer::nMinMomComponents) { LOG(error) << "newpairfunc returned size=" << k3d.size(); return; } const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); const float pairPhiEPMC = FemtoDreamMath::getPairPhiEP(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, eventPlane); + const float pairPhiEPMCforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, eventPlane) : 0.f; if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne && std::abs(part2.fdMCParticle().pdgMCTruth()) == mPDGTwo) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + if (mStoreEProt) { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC, pairPhiEPMCforRot); + } else { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + } setPair_3Dqn_MC(k3dMC, k3d, part1.fdMCParticle().partOriginMCTruth(), part2.fdMCParticle().partOriginMCTruth(), smearingByOrigin); } else { mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); @@ -562,7 +606,7 @@ class FemtoDreamContainer { std::vector k3d = FemtoDreamMath::newpairfunc(part1, mMassOne, part2, mMassTwo, IsSameSpecies); - if (k3d.size() < 4) { + if (k3d.size() < femtoDreamContainer::nMinMomComponents) { LOG(error) << "newpairfunc returned size=" << k3d.size(); return; } @@ -573,23 +617,33 @@ class FemtoDreamContainer const float mT = FemtoDreamMath::getmT(part1, mMassOne, part2, mMassTwo); const float pairPhiEP = FemtoDreamMath::getPairPhiEP(part1, mMassOne, part2, mMassTwo, EP1, EP2); + const float pairPhiEPforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1, mMassOne, part2, mMassTwo, EP1, EP2) : 0.f; if (mHistogramRegistry) { - setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + if (mStoreEProt) { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP, pairPhiEPforRot); + } else { + setPair_3Dqn_base(DKout, DKside, DKlong, mT, multPercentile, myQnBin, pairPhiEP); + } if constexpr (isMC) { if (part1.has_fdMCParticle() && part2.has_fdMCParticle()) { std::vector k3dMC = FemtoDreamMath::newpairfuncMC(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, IsSameSpecies); - if (k3dMC.size() < 4) { + if (k3dMC.size() < femtoDreamContainer::nMinMomComponents) { LOG(error) << "newpairfunc returned size=" << k3d.size(); return; } const float mTMC = FemtoDreamMath::getmT(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo); const float pairPhiEPMC = FemtoDreamMath::getPairPhiEP(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, EP1, EP2); + const float pairPhiEPMCforRot = mStoreEProt ? FemtoDreamMath::getPairPhiEPSigned(part1.fdMCParticle(), mMassOne, part2.fdMCParticle(), mMassTwo, EP1, EP2) : 0.f; if (std::abs(part1.fdMCParticle().pdgMCTruth()) == mPDGOne && std::abs(part2.fdMCParticle().pdgMCTruth()) == mPDGTwo) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + if (mStoreEProt) { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC, pairPhiEPMCforRot); + } else { + setPair_3Dqn_base(k3dMC[1], k3dMC[2], k3dMC[3], mTMC, multPercentile, myQnBin, pairPhiEPMC); + } setPair_3Dqn_MC(k3dMC, k3d, part1.fdMCParticle().partOriginMCTruth(), part2.fdMCParticle().partOriginMCTruth(), smearingByOrigin); } else { mHistogramRegistry->fill(HIST(mFolderSuffix[mEventType]) + HIST(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); @@ -612,6 +666,7 @@ class FemtoDreamContainer int mPDGOne = 0; ///< PDG code of particle 1 int mPDGTwo = 0; ///< PDG code of particle 2 float mHighkstarCut = 6.; + bool mStoreEProt = false; ///< Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long }; } // namespace o2::analysis::femtoDream diff --git a/PWGCF/FemtoDream/Core/femtoDreamContainerThreeBody.h b/PWGCF/FemtoDream/Core/femtoDreamContainerThreeBody.h index b82ff3f1b59..b291cbe82d7 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamContainerThreeBody.h +++ b/PWGCF/FemtoDream/Core/femtoDreamContainerThreeBody.h @@ -68,7 +68,7 @@ class FemtoDreamContainerThreeBody /// \param femtoObsAxis axis object for the femto observable axis /// \param multAxis axis object for the multiplicity axis template - void init_base(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis) + void init_base(const std::string& folderName, const std::string& femtoObs, T femtoObsAxis, T multAxis) { mHistogramRegistry->add((folderName + "/relTripletDist").c_str(), ("; " + femtoObs + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxis}); @@ -85,7 +85,7 @@ class FemtoDreamContainerThreeBody /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) /// \param femtoObsAxis axis object for the femto observable axis template - void init_MC(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis) + void init_MC(const std::string& folderName, const std::string& femtoObs, T femtoObsAxis, T multAxis) { mHistogramRegistry->add((folderName + "/relTripletDist_ReconNoFake").c_str(), ("; " + femtoObs + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxis}); mHistogramRegistry->add((folderName + "/relTripletQ3Mult_ReconNoFake").c_str(), ("; " + femtoObs + "; Multiplicity").c_str(), o2::framework::HistType::kTH2F, {femtoObsAxis, multAxis}); diff --git a/PWGCF/FemtoDream/Core/femtoDreamMath.h b/PWGCF/FemtoDream/Core/femtoDreamMath.h index 2ddc7291818..90a83a9ed72 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamMath.h +++ b/PWGCF/FemtoDream/Core/femtoDreamMath.h @@ -373,6 +373,29 @@ class FemtoDreamMath phi_pair_onPsi = TMath::Abs(phi_pair_onPsi); return phi_pair_onPsi; } + + /// Signed φ_pair − Ψ_EP (same as getPairPhiEP without the final |·|); used as the B-frame rotation angle. + template + static float getPairPhiEPSigned(const T1& part1, const float mass1, const T2& part2, const float mass2, const float Psi_ep) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2; + return TVector2::Phi_mpi_pi(trackSum.Phi() - Psi_ep); + } + + /// Signed counterpart of the plane-calibrated (two-event-plane) getPairPhiEP. + template + static float getPairPhiEPSigned(const T1& part1, const float mass1, const T2& part2, const float mass2, const float Psi_ep1, const float Psi_ep2) + { + const ROOT::Math::PtEtaPhiMVector vecpart1(part1.pt(), part1.eta(), part1.phi(), mass1); + const ROOT::Math::PtEtaPhiMVector vecpart2(part2.pt(), part2.eta(), part2.phi(), mass2); + const float psidiff = Psi_ep2 - Psi_ep1; + const float newPhi2 = TVector2::Phi_mpi_pi(vecpart2.Phi() - psidiff); + const ROOT::Math::PtEtaPhiMVector vecpart2_calibd(vecpart2.Pt(), vecpart2.Eta(), newPhi2, vecpart2.M()); + const ROOT::Math::PtEtaPhiMVector trackSum = vecpart1 + vecpart2_calibd; + return TVector2::Phi_mpi_pi(trackSum.Phi() - Psi_ep1); + } }; } // namespace o2::analysis::femtoDream diff --git a/PWGCF/FemtoDream/Core/femtoDreamObjectSelection.h b/PWGCF/FemtoDream/Core/femtoDreamObjectSelection.h index fb3b134e606..f1ce8019547 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamObjectSelection.h +++ b/PWGCF/FemtoDream/Core/femtoDreamObjectSelection.h @@ -24,6 +24,8 @@ #include +#include + #include #include #include @@ -52,7 +54,11 @@ class FemtoDreamObjectSelection void fillSelectionHistogram() { int nBins = mSelections.size(); - mQAHistogramRegistry->add((static_cast(o2::aod::femtodreamparticle::ParticleTypeName[part]) + "/cuthist").c_str(), "; Cut; Value", o2::framework::HistType::kTH1F, {{nBins, 0, static_cast(nBins)}}); + // fmt::format, not std::string + const char*: the longest names here are + // exactly 32 characters, so the concatenation crosses std::string's SSO + // boundary and GCC 14 reports the constant-folded copy as + // -Werror=array-bounds= on a buffer that is never actually used. + mQAHistogramRegistry->add(fmt::format("{}/cuthist", o2::aod::femtodreamparticle::ParticleTypeName[part]).c_str(), "; Cut; Value", o2::framework::HistType::kTH1F, {{nBins, 0, static_cast(nBins)}}); auto hist = mQAHistogramRegistry->get(HIST(o2::aod::femtodreamparticle::ParticleTypeName[part]) + HIST("/cuthist")); for (size_t i = 0; i < mSelections.size(); ++i) { hist->GetXaxis()->SetBinLabel(i + 1, Form("%u", mSelections.at(i).getSelectionVariable())); diff --git a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h index 8371f683515..410e79774a5 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h +++ b/PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h @@ -28,6 +28,8 @@ #include #include +#include + #include #include #include @@ -55,7 +57,7 @@ class FemtoDreamParticleHisto /// \param tempFitVarpTAxis axis object for the pT axis in the pT vs. tempFitVar plots /// \param tempFitVarAxis axis object for the tempFitVar axis template - void init_base(std::string folderName, std::string tempFitVarAxisTitle, T& pTAxis, T& tempFitVarAxis, T& InvMassAxis, T& /*multAxis*/) + void init_base(const std::string& folderName, const std::string& tempFitVarAxisTitle, T& pTAxis, T& tempFitVarAxis, T& InvMassAxis, T& /*multAxis*/) { std::string folderSuffix = static_cast(o2::aod::femtodreamMCparticle::MCTypeName[mc]).c_str(); /// Histograms of the kinematic properties @@ -97,7 +99,7 @@ class FemtoDreamParticleHisto // comment template - void init_debug(std::string folderName, T& multAxis, T& multPercentileAxis, T& pTAxis, T& etaAxis, T& phiAxis, T& tempFitVarAxis, T& dcazAxis, T& NsigmaTPCAxis, T& NsigmaTOFAxis, T& NsigmaTPCTOFAxis, T& NsigmaITSAxis, T& InvMassCompetingAxis, bool correlatedPlots) + void init_debug(const std::string& folderName, T& multAxis, T& multPercentileAxis, T& pTAxis, T& etaAxis, T& phiAxis, T& tempFitVarAxis, T& dcazAxis, T& NsigmaTPCAxis, T& NsigmaTOFAxis, T& NsigmaTPCTOFAxis, T& NsigmaITSAxis, T& InvMassCompetingAxis, bool correlatedPlots) { std::string folderSuffix = static_cast(o2::aod::femtodreamMCparticle::MCTypeName[mc]).c_str(); @@ -205,14 +207,15 @@ class FemtoDreamParticleHisto /// \param tempFitVarpTAxis axis object for the pT axis in the pT vs. tempFitVar plots /// \param tempFitVarAxis axis object for the tempFitVar axis template - void init_MC(std::string folderName, std::string /*tempFitVarAxisTitle*/, T& tempFitVarpTAxis, T& tempFitVarAxis, T& dcazAxis, T& multAxis, bool isDebug) + void init_MC(const std::string& folderName, const std::string& /*tempFitVarAxisTitle*/, T& tempFitVarpTAxis, T& tempFitVarAxis, T& dcazAxis, T& multAxis, bool isDebug) { /// Particle-type specific histograms std::string folderSuffix = static_cast(o2::aod::femtodreamMCparticle::MCTypeName[o2::aod::femtodreamMCparticle::MCType::kTruth]).c_str(); + const auto nMcOriginTypes = static_cast(o2::aod::femtodreamMCparticle::ParticleOriginMCTruth::kNOriginMCTruthTypes); mHistogramRegistry->add((folderName + folderSuffix + "/hPt_ReconNoFake").c_str(), "; #it{p}_{T} (GeV/#it{c}); Entries", o2::framework::HistType::kTH1F, {{240, 0, 6}}); mHistogramRegistry->add((folderName + folderSuffix + "/hPDG").c_str(), "; PDG; Entries", o2::framework::HistType::kTH1I, {{6001, -3000.5, 3000.5}}); - mHistogramRegistry->add((folderName + folderSuffix + "/hOrigin_MC").c_str(), "; Origin; Entries", o2::framework::HistType::kTH1I, {{7, -0.5, 6.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hOrigin_MC").c_str(), "; Origin; Entries", o2::framework::HistType::kTH1I, {{nMcOriginTypes, -0.5, static_cast(nMcOriginTypes) - 0.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/hNoMCtruthCounter").c_str(), "; Counter; Entries", o2::framework::HistType::kTH1I, {{1, -0.5, 0.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/hPt_DiffTruthReco").c_str(), "; p^{truth}_{T}; (p^{reco}_{T} - p^{truth}_{T}) / p^{truth}_{T}", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, {200, -1, 1}}); mHistogramRegistry->add((folderName + folderSuffix + "/hEta_DiffTruthReco").c_str(), "; #eta^{truth}; #eta^{reco} - #eta^{truth}", o2::framework::HistType::kTH2F, {{200, -1, 1}, {200, -1, 1}}); @@ -229,6 +232,13 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_Else").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{6001, -3000.5, 3000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterLambda").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterSigmaplus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryDaughterOmegaMinus").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromHF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/Debug/hPDGmother_SecondaryFromLF").c_str(), "; PDG mother; Entries", o2::framework::HistType::kTH1I, {{12001, -6000.5, 6000.5}}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); @@ -237,15 +247,21 @@ class FemtoDreamParticleHisto mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Fake").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryDaughterLambda").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryDaughterSigmaplus").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryDaughterOmegaMinus").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Else").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTHnSparseF, {tempFitVarpTAxis, tempFitVarAxis, dcazAxis, multAxis}); } else { mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Primary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + if (std::abs(mPDG) == kKPlus) { + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromHF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryFromLF").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + } mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Secondary").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Material").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_WrongCollision").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Fake").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryDaughterLambda").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryDaughterSigmaplus").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); + mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_SecondaryDaughterOmegaMinus").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); mHistogramRegistry->add((folderName + folderSuffix + "/hDCAxy_Else").c_str(), "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", o2::framework::HistType::kTH2F, {tempFitVarpTAxis, tempFitVarAxis}); } @@ -336,7 +352,7 @@ class FemtoDreamParticleHisto framework::AxisSpec InvMassAxis = {InvMassBins, "M_{inv} (GeV/#it{c}^{2})"}; framework::AxisSpec InvMassCompetingAxis = {InvMassCompetingBins, "M_{inv} (GeV/#it{c}^{2})"}; - std::string folderName = (static_cast(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]).c_str() + static_cast(mFolderSuffix[mFolderSuffixType])).c_str(); + std::string folderName = fmt::format("{}{}", o2::aod::femtodreamparticle::ParticleTypeName[mParticleType], mFolderSuffix[mFolderSuffixType]); // Fill here the actual histogramms by calling init_base and init_MC init_base(folderName, tempFitVarAxisTitle, pTAxis, tempFitVarAxis, InvMassAxis, multAxis); @@ -591,6 +607,16 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar(), part.dcaZ(), mult); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromHF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryFromLF"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_Material"), part.fdExtMCParticle().motherPDG()); mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), @@ -616,6 +642,11 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryDaughterSigmaplus"), part.pt(), part.tempFitVar(), part.dcaZ(), mult); break; + case (o2::aod::femtodreamMCparticle::kSecondaryDaughterOmegaMinus): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_SecondaryDaughterOmegaMinus"), part.fdExtMCParticle().motherPDG()); + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryDaughterOmegaMinus"), + part.pt(), part.tempFitVar(), part.dcaZ(), mult); + break; case (o2::aod::femtodreamMCparticle::kElse): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/Debug/hPDGmother_Else"), part.fdExtMCParticle().motherPDG()); mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Else"), @@ -634,6 +665,14 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Secondary"), part.pt(), part.tempFitVar()); break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromHF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromHF"), + part.pt(), part.tempFitVar()); + break; + case (o2::aod::femtodreamMCparticle::kSecondaryFromLF): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryFromLF"), + part.pt(), part.tempFitVar()); + break; case (o2::aod::femtodreamMCparticle::kMaterial): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Material"), part.pt(), part.tempFitVar()); @@ -654,6 +693,10 @@ class FemtoDreamParticleHisto mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryDaughterSigmaplus"), part.pt(), part.tempFitVar()); break; + case (o2::aod::femtodreamMCparticle::kSecondaryDaughterOmegaMinus): + mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_SecondaryDaughterOmegaMinus"), + part.pt(), part.tempFitVar()); + break; case (o2::aod::femtodreamMCparticle::kElse): mHistogramRegistry->fill(HIST(o2::aod::femtodreamparticle::ParticleTypeName[mParticleType]) + HIST(mFolderSuffix[mFolderSuffixType]) + HIST("_MC/hDCAxy_Else"), part.pt(), part.tempFitVar()); diff --git a/PWGCF/FemtoDream/Core/femtoDreamTrackSelection.h b/PWGCF/FemtoDream/Core/femtoDreamTrackSelection.h index 2e4f29a70f4..71d78c0d54d 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamTrackSelection.h +++ b/PWGCF/FemtoDream/Core/femtoDreamTrackSelection.h @@ -29,6 +29,8 @@ #include #include +#include + #include #include #include @@ -313,7 +315,7 @@ void FemtoDreamTrackSelection::init(o2::framework::HistogramRegistry* QAregistry if (QAregistry && Registry) { mHistogramRegistry = Registry; mQAHistogramRegistry = QAregistry; - std::string folderName = static_cast(o2::aod::femtodreamparticle::ParticleTypeName[part]) + "/" + static_cast(o2::aod::femtodreamparticle::TrackTypeName[tracktype]); + std::string folderName = fmt::format("{}/{}", o2::aod::femtodreamparticle::ParticleTypeName[part], o2::aod::femtodreamparticle::TrackTypeName[tracktype]); /// check whether the number of selection exceeds the bitmap size unsigned int nSelections = getNSelections() - getNSelections(femtoDreamTrackSelection::kPIDnSigmaMax); diff --git a/PWGCF/FemtoDream/Core/femtoDreamUtils.h b/PWGCF/FemtoDream/Core/femtoDreamUtils.h index efc075671c1..fde124f5db4 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamUtils.h +++ b/PWGCF/FemtoDream/Core/femtoDreamUtils.h @@ -74,6 +74,9 @@ inline float getMass(int pdgCode) case o2::constants::physics::Pdg::kLambdaCPlus: mass = o2::constants::physics::MassLambdaCPlus; break; + case o2::constants::physics::Pdg::kXiCPlus: + mass = o2::constants::physics::MassXiCPlus; + break; case o2::constants::physics::Pdg::kDeuteron: mass = o2::constants::physics::MassDeuteron; break; @@ -99,11 +102,29 @@ inline float getMass(int pdgCode) return mass; } -inline int checkDaughterType(o2::aod::femtodreamparticle::ParticleType partType, int motherPDG) +inline int checkDaughterType(o2::aod::femtodreamparticle::ParticleType partType, int motherPDG, int daughterPDG = 0) { int partOrigin = 0; + const auto absMotherPDG = std::abs(motherPDG); + const auto absDaughterPDG = std::abs(daughterPDG); + const bool isTrackLike = partType == o2::aod::femtodreamparticle::ParticleType::kTrack || + partType == o2::aod::femtodreamparticle::ParticleType::kV0Child || + partType == o2::aod::femtodreamparticle::ParticleType::kCascadeV0Child || + partType == o2::aod::femtodreamparticle::ParticleType::kCascadeBachelor; + + if (absDaughterPDG == kKPlus && isTrackLike) { + switch (absMotherPDG) { + case kOmegaMinus: + partOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kSecondaryDaughterOmegaMinus; + break; + default: + partOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kSecondary; + } + return partOrigin; + } + if (partType == o2::aod::femtodreamparticle::ParticleType::kTrack) { - switch (std::abs(motherPDG)) { + switch (absMotherPDG) { case kLambda0: partOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kSecondaryDaughterLambda; break; @@ -115,7 +136,7 @@ inline int checkDaughterType(o2::aod::femtodreamparticle::ParticleType partType, } // switch } else if (partType == o2::aod::femtodreamparticle::ParticleType::kV0) { - switch (std::abs(motherPDG)) { + switch (absMotherPDG) { case kSigma0: partOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kSecondaryDaughterSigma0; break; @@ -130,7 +151,7 @@ inline int checkDaughterType(o2::aod::femtodreamparticle::ParticleType partType, } } else if (partType == o2::aod::femtodreamparticle::ParticleType::kV0Child || partType == o2::aod::femtodreamparticle::ParticleType::kCascadeV0Child || partType == o2::aod::femtodreamparticle::ParticleType::kCascadeBachelor) { - switch (abs(motherPDG)) { + switch (absMotherPDG) { case kLambda0: partOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kSecondaryDaughterLambda; break; @@ -142,7 +163,7 @@ inline int checkDaughterType(o2::aod::femtodreamparticle::ParticleType partType, } // switch } else if (partType == o2::aod::femtodreamparticle::ParticleType::kCascade) { - switch (std::abs(motherPDG)) { + switch (absMotherPDG) { case kOmegaMinus: partOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kSecondaryDaughterOmegaMinus; break; diff --git a/PWGCF/FemtoDream/Core/femtoDreamV0Selection.h b/PWGCF/FemtoDream/Core/femtoDreamV0Selection.h index 3c7bed428ec..041a1278f0b 100644 --- a/PWGCF/FemtoDream/Core/femtoDreamV0Selection.h +++ b/PWGCF/FemtoDream/Core/femtoDreamV0Selection.h @@ -28,6 +28,8 @@ #include #include +#include + #include #include #include @@ -304,8 +306,8 @@ void FemtoDreamV0Selection::init(o2::framework::HistogramRegistry* QAregistry, o } for (int istage = 0; istage < femtoDreamSelection::kNcutStages; istage++) { std::string folderName = - static_cast(o2::aod::femtodreamparticle::ParticleTypeName[part]) + "/" + - static_cast(femtoDreamSelection::mCutStage[istage]); + fmt::format("{}/{}", o2::aod::femtodreamparticle::ParticleTypeName[part], + femtoDreamSelection::mCutStage[istage]); /// \todo initialize histograms for children tracks of v0s mQAHistogramRegistry->add((folderName + "/hPt").c_str(), "; #it{p}_{T} (GeV/#it{c}); Entries", o2::framework::HistType::kTH1F, diff --git a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx index 8d2a80b00c4..8d401c41226 100644 --- a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx +++ b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerReducedTask.cxx @@ -161,7 +161,7 @@ struct femtoDreamProducerReducedTask { } /// Function to retrieve the nominal magnetic field in kG (0.1T) and convert it directly to T - void getMagneticFieldTesla(aod::BCsWithTimestamps::iterator bc) + void getMagneticFieldTesla(const aod::BCsWithTimestamps::iterator& bc) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable // get magnetic field for run @@ -214,7 +214,7 @@ struct femtoDreamProducerReducedTask { } else if (particleMC.isPhysicalPrimary()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kPrimary; } else if (motherparticleMC.isPhysicalPrimary() && particleMC.getProcess() == 4) { - particleOrigin = checkDaughterType(fdparttype, motherparticleMC.pdgCode()); + particleOrigin = checkDaughterType(fdparttype, motherparticleMC.pdgCode(), pdgCode); } else if (particleMC.getGenStatusCode() == -1) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kMaterial; } else { diff --git a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTask.cxx b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTask.cxx index 9f47d915620..675419459da 100644 --- a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTask.cxx +++ b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTask.cxx @@ -487,7 +487,7 @@ struct femtoDreamProducerTask { } /// Function to retrieve the nominal magnetic field in kG (0.1T) and convert it directly to T - void initCCDB_Mag_Trig(aod::BCsWithTimestamps::iterator bc) + void initCCDB_Mag_Trig(const aod::BCsWithTimestamps::iterator& bc) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable // get magnetic field for run @@ -669,10 +669,10 @@ struct femtoDreamProducerTask { // list of mothers is not empty } else if (particleMC.getProcess() == 4 && particleMC.getGenStatusCode() == -1 && !motherparticlesMC.empty()) { // get direct mother - auto motherparticleMC = motherparticlesMC.front(); + const auto& motherparticleMC = motherparticlesMC.front(); pdgCodeMother = motherparticleMC.pdgCode(); TrackRegistry.fill(HIST("AnalysisQA/Mother"), pdgCodeMother); - particleOrigin = checkDaughterType(fdparttype, motherparticleMC.pdgCode()); + particleOrigin = checkDaughterType(fdparttype, motherparticleMC.pdgCode(), pdgCode); // check if particle is material // particle is from inelastic hadronic interaction -> getProcess() == 23 // particle is generated during transport -> getGenStatusCode() == -1 diff --git a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskForSpecificAnalysis.cxx b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskForSpecificAnalysis.cxx index d808767c9db..5e8b6290672 100644 --- a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskForSpecificAnalysis.cxx +++ b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskForSpecificAnalysis.cxx @@ -371,7 +371,7 @@ struct FemtoDreamProducerTaskForSpecificAnalysis { /// @param groupSelectedV0s partition for the second particle passed by the process function /// @param parts femtoDreamParticles table template - void createSpecifiedDerivedData(const o2::aod::FDCollision& col, PartitionType groupSelectedTracks, PartitionType groupSelectedV0s, PartType parts) + void createSpecifiedDerivedData(const o2::aod::FDCollision& col, const PartitionType& groupSelectedTracks, const PartitionType& groupSelectedV0s, const PartType& parts) { /// check tracks int tracksCount = 0; @@ -512,7 +512,7 @@ struct FemtoDreamProducerTaskForSpecificAnalysis { /// @param groupSelectedV0s partition for the second particle passed by the process function /// @param parts femtoDreamParticles table template - void createSpecifiedDerivedDataTrkCascade(const o2::aod::FDCollision& col, PartitionType groupSelectedTracks, PartitionType groupSelectedCascades, PartType parts) + void createSpecifiedDerivedDataTrkCascade(const o2::aod::FDCollision& col, const PartitionType& groupSelectedTracks, const PartitionType& groupSelectedCascades, const PartType& parts) { /// check tracks @@ -592,7 +592,7 @@ struct FemtoDreamProducerTaskForSpecificAnalysis { /// @param groupSelectedV0s partition for the second particle passed by the process function /// @param parts femtoDreamParticles table template - void createSpecifiedDerivedDataV0Cascade(const o2::aod::FDCollision& col, PartitionType groupSelectedV0s, PartitionType groupSelectedCascades, PartType parts) + void createSpecifiedDerivedDataV0Cascade(const o2::aod::FDCollision& col, const PartitionType& groupSelectedV0s, const PartitionType& groupSelectedCascades, const PartType& parts) { /// check V0s @@ -634,7 +634,7 @@ struct FemtoDreamProducerTaskForSpecificAnalysis { PROCESS_SWITCH(FemtoDreamProducerTaskForSpecificAnalysis, processCollisionsWithNV0sAndNCascades, "Enable producing data with ppp collisions for data", false); template - void createSpecifiedDerivedDataV0Phi(const o2::aod::FDCollision& col, PartitionType groupSelectedV0s, PartitionType groupSelectedResos, PartType parts) + void createSpecifiedDerivedDataV0Phi(const o2::aod::FDCollision& col, const PartitionType& groupSelectedV0s, const PartitionType& groupSelectedResos, const PartType& parts) { // check v0's int v0Count = 0; @@ -792,7 +792,7 @@ struct FemtoDreamProducerTaskForSpecificAnalysis { PROCESS_SWITCH(FemtoDreamProducerTaskForSpecificAnalysis, processCollisionsWithNV0AndNPhi, "Enable producing data with pp collisions for data v0-reso", true); template - void createSpecifiedDerivedDataK0ShortKStar(const o2::aod::FDCollision& col, PartitionType groupSelectedV0s, PartitionType groupSelectedResos, PartType parts) + void createSpecifiedDerivedDataK0ShortKStar(const o2::aod::FDCollision& col, const PartitionType& groupSelectedV0s, const PartitionType& groupSelectedResos, const PartType& parts) { // check v0's int v0Count = 0; @@ -954,7 +954,7 @@ struct FemtoDreamProducerTaskForSpecificAnalysis { /// For that, this function is created. /// For eventual future changes of femtoDreamProducerTaskReso.cxx, createSpecifiedDerivedDataK0ShortKStar() is kept as it is template - void createSpecifiedDerivedDataK0ShortKStarLegacy(const o2::aod::FDCollision& col, PartitionType groupSelectedV0s, PartitionType groupSelectedResos, PartType parts) + void createSpecifiedDerivedDataK0ShortKStarLegacy(const o2::aod::FDCollision& col, const PartitionType& groupSelectedV0s, const PartitionType& groupSelectedResos, const PartType& parts) { // check v0's int v0Count = 0; @@ -1119,7 +1119,7 @@ struct FemtoDreamProducerTaskForSpecificAnalysis { PROCESS_SWITCH(FemtoDreamProducerTaskForSpecificAnalysis, createSpecifiedDerivedDataNK0ShortNKStarLegacy, "Enable producing data with pp collisions for data K0Short-KStar with kV0Child/KResoChild daughter types", false); template - void createSpecifiedDerivedDataK0ShortKStarFromMothers(const o2::aod::FDCollision& col, PartitionType groupSelectedV0s, PartitionType groupSelectedResos, PartType parts) + void createSpecifiedDerivedDataK0ShortKStarFromMothers(const o2::aod::FDCollision& col, const PartitionType& groupSelectedV0s, const PartitionType& groupSelectedResos, const PartType& parts) { // check v0's int v0Count = 0; diff --git a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskReso.cxx b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskReso.cxx index 9b0f753ef9c..3bab4b2fb72 100644 --- a/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskReso.cxx +++ b/PWGCF/FemtoDream/TableProducer/femtoDreamProducerTaskReso.cxx @@ -733,7 +733,7 @@ struct FemtoDreamProducerTaskReso { } /// Function to retrieve the nominal magnetic field in kG (0.1T) and convert it directly to T - void initCcdbMagTrig(aod::BCsWithTimestamps::iterator bc) + void initCcdbMagTrig(const aod::BCsWithTimestamps::iterator& bc) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable // get magnetic field for run @@ -925,10 +925,10 @@ struct FemtoDreamProducerTaskReso { // list of mothers is not empty } else if (particleMC.getProcess() == ProcessDirectMother && particleMC.getGenStatusCode() == GenStatusTransport && !motherparticlesMC.empty()) { // get direct mother - auto motherparticleMC = motherparticlesMC.front(); + const auto& motherparticleMC = motherparticlesMC.front(); pdgCodeMother = motherparticleMC.pdgCode(); trackRegistry.fill(HIST("AnalysisQA/Mother"), pdgCodeMother); - particleOrigin = checkDaughterType(fdparttype, motherparticleMC.pdgCode()); + particleOrigin = checkDaughterType(fdparttype, motherparticleMC.pdgCode(), pdgCode); // check if particle is material // particle is from inelastic hadronic interaction -> getProcess() == 23 // particle is generated during transport -> getGenStatusCode() == -1 diff --git a/PWGCF/FemtoDream/Tasks/CMakeLists.txt b/PWGCF/FemtoDream/Tasks/CMakeLists.txt index 4e1485f4133..ec9a91513fa 100644 --- a/PWGCF/FemtoDream/Tasks/CMakeLists.txt +++ b/PWGCF/FemtoDream/Tasks/CMakeLists.txt @@ -93,3 +93,8 @@ o2physics_add_dpl_workflow(femto-dream-pair-efficiency SOURCES femtoDreamPairEfficiency.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(femtodream-triplet-track-track-cascade + SOURCES femtoDreamTripletTaskTrackTrackCascade.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx index 35c5b4a64a1..3b8164861ee 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackCascade.cxx @@ -211,9 +211,9 @@ struct femtoDreamPairTaskTrackCascade { Configurable Depth{"Depth", 5, "Number of events for mixing"}; Configurable Policy{"BinPolicy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; FemtoDreamPairCleaner pairCleaner; @@ -227,9 +227,9 @@ struct femtoDreamPairTaskTrackCascade { void init(InitContext&) { // setup binnnig policy for mixing - colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}, true}; - colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}}; + colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; eventHisto.init(&Registry, Option.IsMC); trackHistoPartOne.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTTrack, Option.Dummy, Option.Dummy, Binning.TempFitVarTrack, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.IsMC, Track1.PDGCode); trackHistoPartTwo.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTCascade, Option.Dummy, Option.Dummy, Binning.TempFitVarCascade, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Binning.InvMass, Option.Dummy, Option.IsMC, Cascade2.PDGCode); @@ -262,7 +262,7 @@ struct femtoDreamPairTaskTrackCascade { } template - bool checkChildCuts(PartType posChild, PartType negChild, PartType bachChild) + bool checkChildCuts(const PartType& posChild, const PartType& negChild, const PartType& bachChild) { bool passCuts = true; @@ -375,7 +375,7 @@ struct femtoDreamPairTaskTrackCascade { PROCESS_SWITCH(femtoDreamPairTaskTrackCascade, processSameEventMC, "Enable processing same event with Monte Carlo", false); template - void doMixedEvent(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, BinningType policy) + void doMixedEvent(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, const BinningType& policy) { // Partition PartitionMaskedCol = ncheckbit(aod::femtodreamcollision::bitmaskTrackOne, BitMask) && ncheckbit(aod::femtodreamcollision::bitmaskTrackTwo, BitMask);// && aod::femtodreamcollision::downsample == true; // PartitionMaskedCol.bindTable(cols); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx index 5c087e14033..cb482f58715 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackTrack.cxx @@ -115,6 +115,7 @@ struct femtoDreamPairTaskTrackTrack { ConfigurableAxis DKlong{"DKlong", {500, -2., 2.}, "binning DKlong for the 3-D femtoscopy plot: R_long(LCMS) vs mT vs multiplicity percentile vs qnBin vs pait phi wrt EP (set <> to true)"}; ConfigurableAxis qnBins{"qnBins", {10, 0, 10}, "binning of qn interval"}; ConfigurableAxis pairPhiBins{"pairPhiBins", {12, 0., TMath::Pi()}, "binning of pair phi"}; + Configurable storeEProt{"storeEProt", false, "Store EP-rotated (B-frame) DK_x,DK_y,DK_z instead of out,side,long in the 3D qn histogram"}; } EPCal; using FilteredCollisions = soa::Filtered; @@ -258,11 +259,11 @@ struct femtoDreamPairTaskTrackTrack { Configurable Policy{"Policy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both, 3: multipliciy percentile and qn value, 4: multipliciy percentile and event plane"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentileqn{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentileEP{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultPercentileqn{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}}; + ColumnBinningPolicy colBinningMultPercentileEP{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -283,11 +284,11 @@ struct femtoDreamPairTaskTrackTrack { { // setup columnpolicy for binning - colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultPercentileqn = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}, true}; - colBinningMultPercentileEP = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}, true}; + colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}}; + colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultPercentileqn = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.QnMixBins}}; + colBinningMultPercentileEP = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins, Mixing.EPMixBins}}; if (Option.RandomizePair.value) { random = new TRandom3(0); @@ -333,6 +334,8 @@ struct femtoDreamPairTaskTrackTrack { } if (EPCal.do3DFemto) { + sameEventQnCont.setStoreEProt(EPCal.storeEProt); + mixedEventQnCont.setStoreEProt(EPCal.storeEProt); sameEventQnCont.init_3Dqn(&Registry, EPCal.DKout, EPCal.DKside, EPCal.DKlong, Binning4D.mT, Binning4D.multPercentile, Option.IsMC, EPCal.qnBins, EPCal.pairPhiBins, Option.SmearingByOrigin); mixedEventQnCont.init_3Dqn(&Registry, EPCal.DKout, EPCal.DKside, EPCal.DKlong, @@ -398,7 +401,7 @@ struct femtoDreamPairTaskTrackTrack { }; template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { eventHisto.fillQA(col); } @@ -414,7 +417,7 @@ struct femtoDreamPairTaskTrackTrack { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType SliceTrk1, PartitionType SliceTrk2, PartType parts, Collision col) + void doSameEvent(const PartitionType& SliceTrk1, const PartitionType& SliceTrk2, const PartType& parts, const Collision& col) { for (auto& part : SliceTrk1) { trackHistoPartOne.fillQA(part, aod::femtodreamparticle::kPt, col.multNtr(), col.multV0M()); @@ -536,7 +539,7 @@ struct femtoDreamPairTaskTrackTrack { PROCESS_SWITCH(femtoDreamPairTaskTrackTrack, processSameEventMCMasked, "Enable processing same event for Monte Carlo with masked collisions", false); template - void doMixedEvent_NotMasked(CollisionType& cols, PartType& parts, PartitionType& part1, PartitionType& part2, BinningType policy) + void doMixedEvent_NotMasked(CollisionType& cols, PartType& parts, PartitionType& part1, PartitionType& part2, const BinningType& policy) { for (auto const& [collision1, collision2] : soa::selfCombinations(policy, Mixing.Depth.value, -1, cols, cols)) { auto SliceTrk1 = part1->sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -705,7 +708,7 @@ struct femtoDreamPairTaskTrackTrack { /// This function processes the same event in divided qn bins /// col.multV0M() get the event centrality from ft0c for PbPb data template - void doSameEventEP(PartitionType SliceTrk1, PartitionType SliceTrk2, PartType parts, Collision col) + void doSameEventEP(const PartitionType& SliceTrk1, const PartitionType& SliceTrk2, const PartType& parts, const Collision& col) { if (EPCal.storeEvtTrkInfo) { for (auto& part : SliceTrk1) { @@ -830,7 +833,7 @@ struct femtoDreamPairTaskTrackTrack { PROCESS_SWITCH(femtoDreamPairTaskTrackTrack, processSameEventEPMC, "Enable processing same event of 3D for Monte Carlo", false); template - void doMixedEvent_NotMaskedEP(CollisionType& cols, PartType& parts, PartitionType& part1, PartitionType& part2, BinningType policy) + void doMixedEvent_NotMaskedEP(CollisionType& cols, PartType& parts, PartitionType& part1, PartitionType& part2, const BinningType& policy) { for (auto const& [collision1, collision2] : soa::selfCombinations(policy, Mixing.Depth.value, -1, cols, cols)) { auto SliceTrk1 = part1->sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx index 9fec3723f24..ccf630b78a8 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskTrackV0.cxx @@ -224,9 +224,9 @@ struct femtoDreamPairTaskTrackV0 { Configurable Policy{"BinPolicy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.BinVztx, Mixing.BinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.BinVztx, Mixing.BinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -240,9 +240,9 @@ struct femtoDreamPairTaskTrackV0 { void init(InitContext& context) { // setup binnnig policy for mixing - colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}, true}; - colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}, true}; + colBinningMult = {{Mixing.BinVztx, Mixing.BinMult}}; + colBinningMultPercentile = {{Mixing.BinVztx, Mixing.BinMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.BinVztx, Mixing.BinMult, Mixing.BinMultPercentile}}; eventHisto.init(&Registry, Option.IsMC); trackHistoPartOne.init(&Registry, Binning.multTempFit, Option.Dummy, Binning.pTTrack, Option.Dummy, Option.Dummy, Binning.TempFitVarTrack, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.Dummy, Option.IsMC, Track1.PDGCode); @@ -388,7 +388,7 @@ struct femtoDreamPairTaskTrackV0 { PROCESS_SWITCH(femtoDreamPairTaskTrackV0, processSameEventMCMasked, "Enable processing same event MC with masks", false); template - void doMixedEvent_Masked(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, BinningType policy) + void doMixedEvent_Masked(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, const BinningType& policy) { if (Option.MixEventWithPairs.value) { diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx index 905d692bee0..5bd1511f013 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0Reso.cxx @@ -223,9 +223,9 @@ struct FemtoDreamPairTaskV0Reso { (aod::femtodreamparticle::mLambda > Reso2.invMassMin) && (aod::femtodreamparticle::mLambda < Reso2.invMassMax); - ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= EventSel.multMin && aod::femtodreamcollision::multNtr <= EventSel.multMax; Filter eventMultiplicityPercentile = aod::femtodreamcollision::multV0M >= EventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= EventSel.multPercentileMax; @@ -251,9 +251,9 @@ struct FemtoDreamPairTaskV0Reso { { // setup binnnig policy for mixing - colBinningMult = {{Mixing.binVztx, Mixing.binMult}, true}; - colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + colBinningMult = {{Mixing.binVztx, Mixing.binMult}}; + colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; eventHisto.init(®istry, Option.isMC); @@ -367,7 +367,7 @@ struct FemtoDreamPairTaskV0Reso { } template - void doMixedEvent(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, BinningType policy) + void doMixedEvent(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, const BinningType& policy) { if (Option.mixEventWithPairs.value) { diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx index 234b44a8a38..bc5191724c6 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamPairTaskV0V0.cxx @@ -208,9 +208,9 @@ struct femtoDreamPairTaskV0V0 { Configurable Policy{"Policy", 0, "Binning policy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.VztxMixBins, Mixing.MultMixBins}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -225,9 +225,9 @@ struct femtoDreamPairTaskV0V0 { void init(InitContext&) { // setup columnpolicy for binning - colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}, true}; - colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}, true}; - colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}, true}; + colBinningMult = {{Mixing.VztxMixBins, Mixing.MultMixBins}}; + colBinningMultPercentile = {{Mixing.VztxMixBins, Mixing.MultPercentileMixBins}}; + colBinningMultMultPercentile = {{Mixing.VztxMixBins, Mixing.MultMixBins, Mixing.MultPercentileMixBins}}; if (Option.RandomizePair.value) { random = new TRandom3(0); @@ -261,7 +261,7 @@ struct femtoDreamPairTaskV0V0 { }; template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { eventHisto.fillQA(col); } @@ -277,7 +277,7 @@ struct femtoDreamPairTaskV0V0 { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType SliceV01, PartitionType SliceV02, PartType parts, Collision col) + void doSameEvent(const PartitionType& SliceV01, const PartitionType& SliceV02, const PartType& parts, const Collision& col) { for (auto& v0 : SliceV01) { const auto& posChild = parts.iteratorAt(v0.index() - 2); @@ -365,7 +365,7 @@ struct femtoDreamPairTaskV0V0 { PROCESS_SWITCH(femtoDreamPairTaskV0V0, processSameEvent, "Enable processing same event", true); template - void doMixedEvent_NotMasked(CollisionType& cols, PartType& parts, PartitionType& part1, PartitionType& part2, BinningType policy) + void doMixedEvent_NotMasked(CollisionType& cols, PartType& parts, PartitionType& part1, PartitionType& part2, const BinningType& policy) { for (auto const& [collision1, collision2] : soa::selfCombinations(policy, Mixing.Depth.value, -1, cols, cols)) { auto SliceV01 = part1->sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx new file mode 100644 index 00000000000..1599bedb0bb --- /dev/null +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackCascade.cxx @@ -0,0 +1,423 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file femtoDreamTripletTaskTrackTrackCascade.cxx +/// \brief Tasks that reads the track and Cascade tables and creates triplets; only two identical tracks and a Cascade can be used +/// \author Raffaele Del Grande, Roberta Ferioli CTU Prague + +#include "PWGCF/DataModel/FemtoDerived.h" +#include "PWGCF/FemtoDream/Core/femtoDreamContainerThreeBody.h" +#include "PWGCF/FemtoDream/Core/femtoDreamDetaDphiStar.h" +#include "PWGCF/FemtoDream/Core/femtoDreamEventHisto.h" +#include "PWGCF/FemtoDream/Core/femtoDreamMath.h" +#include "PWGCF/FemtoDream/Core/femtoDreamPairCleaner.h" +#include "PWGCF/FemtoDream/Core/femtoDreamParticleHisto.h" +#include "PWGCF/FemtoDream/Core/femtoDreamUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +using namespace o2; +using namespace o2::analysis::femtoDream; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct femtoDreamTripletTaskTrackTrackCascade { + SliceCache cache; + Preslice perCol = aod::femtodreamparticle::fdCollisionId; + using MaskedCollisions = soa::Join; + using MaskedCollision = MaskedCollisions::iterator; + aod::femtodreamcollision::BitMaskType MaskBit = -1; + float mMassOne = -999, mMassTwo = -999, mMassThree = -999; + Configurable ConfMixIfTripletPresent{"ConfMixIfTripletPresent", true, "Use for mixing only events which have a TTV0 triplet"}; + Configurable ConfMixIfTVOPairPresent{"ConfMixIfTVOPairPresent", false, "Use for mixing only events which have a TV0 pair (at least one track and one V0)"}; + Configurable ConfMixIfTOrVOPartsPresent{"ConfMixIfTOrVOPartsPresent", false, "Use for mixing only events which have at least one particle of interest"}; + // which CPR to use, old is with a possible bug and new is fixed + Configurable ConfUseOLD_possiblyWrong_CPR{"ConfUseOLD_possiblyWrong_CPR", true, "Use for old CPR, which possibly has a bug. This is implemented only for debugging reasons to compare old and new code on hyperloop datasets."}; + Configurable ConfAtWhichRadiiToCut{"ConfAtWhichRadiiToCut", 1, "At which radii perform deta dphi selection: 0 - at PV, 1 - averaged phi, 2 - at given radii"}; + Configurable ConfAtWhichTPCRadii{"ConfAtWhichTPCRadii", 85., "If ConfAtWhichRadiiToCut = 2; this allows to select at which TPC radii to cut"}; + /// Track selection + Configurable ConfPDGCodePart{"ConfPDGCodePart", 2212, "Particle PDG code"}; + Configurable ConfCutPart{"ConfCutPart", 5542474, "Track - Selection bit from cutCulator"}; + Configurable ConfTPCPIDBit{"ConfTPCPIDBit", 16, "PID TPC bit from cutCulator "}; + Configurable ConfTPCTOFPIDBit{"ConfTPCTOFPIDBit", 8, "PID TPCTOF bit from cutCulator"}; + Configurable ConfMaxpT{"ConfMaxpT", 4.05f, "Maximum transverse momentum of the particles"}; + Configurable ConfMinpT{"ConfMinpT", 0.3f, "Minimum transverse momentum of the particles"}; + Configurable ConfMaxDCAxy{"ConfMaxDCAxy", -0.1f, "Maximum DCAxy of the particles"}; + Configurable ConfMinDCAxy{"ConfMinDCAxy", 0.1f, "Minimum DCAxy of the particles"}; + Configurable ConfPIDthrMom{"ConfPIDthrMom", 1.f, "Momentum threshold from which TPC and TOF are required for PID"}; + Configurable ConfIsMC{"ConfIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; + Configurable ConfUse3D{"ConfUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; + Configurable ConfDCACutPtDep{"ConfDCACutPtDep", false, "Use pt dependent dca cut for tracks"}; + /// Partition for selected particles + Partition SelectedParts = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && + ifnode(aod::femtodreamparticle::pt * (nexp(aod::femtodreamparticle::eta) + nexp(-1.f * aod::femtodreamparticle::eta)) / 2.f <= ConfPIDthrMom, ncheckbit(aod::femtodreamparticle::pidcut, ConfTPCPIDBit), ncheckbit(aod::femtodreamparticle::pidcut, ConfTPCTOFPIDBit)) && + (ncheckbit(aod::femtodreamparticle::cut, ConfCutPart)) && + (aod::femtodreamparticle::pt < ConfMaxpT) && + (aod::femtodreamparticle::pt > ConfMinpT) && + ifnode(ConfDCACutPtDep, (nabs(aod::femtodreamparticle::tempFitVar) <= 0.0105f + (0.035f / npow(aod::femtodreamparticle::pt, 1.1f))), + ((aod::femtodreamparticle::tempFitVar >= ConfMinDCAxy) && + (aod::femtodreamparticle::tempFitVar <= ConfMaxDCAxy))); + ; + /// Histogramming of selected tracks + FemtoDreamParticleHisto trackHistoSelectedParts; + /// Cascade selection + Configurable ConfPDGCodeCascade{"ConfPDGCodeCascade", 3312, "Cascade PDG code"}; + Configurable ConfCutCascade{"ConfCutCascade", 32221874, "Selection bit from cutCulator"}; + Configurable ChildPos_CutBit{"ChildPos_CutBit", 278, "Selection bit for positive child of Cascade"}; + Configurable ChildPos_TPCBit{"ChildPos_TPCBit", 1024, "PID TPC bit for positive child of Cascade"}; + Configurable ChildNeg_CutBit{"ChildNeg_CutBit", 277, "Selection bit for negative child of Cascade"}; + Configurable ChildNeg_TPCBit{"ChildNeg_TPCBit", 4096, "PID TPC bit for negative child of Cascade"}; + Configurable ChildBach_CutBit{"ChildBach_CutBit", 277, "Selection bit for negative child of Cascade"}; + Configurable ChildBach_TPCBit{"ChildBach_TPCBit", 64, "PID TPC bit for bachelor child of Cascade"}; // RAFFA: Why this pion has a different TPC bit? // BACHELOR means pion from the Xi + Configurable Conf_minInvMass_Cascade{"Conf_minInvMass_Cascade", 1.2, "Minimum invariant mass of Cascade (particle)"}; + Configurable Conf_maxInvMass_Cascade{"Conf_maxInvMass_Cascade", 1.4, "Maximum invariant mass of Cascade (particle)"}; + Configurable InvMassV0DaughMin{"InvMassV0DaughMin", 0., "Minimum invariant mass of the V0 Daughter"}; + Configurable InvMassV0DaughMax{"InvMassV0DaughMax", 999., "Maximum invariant mass of the V0 Daughter"}; + Configurable Conf_minPt_Cascade{"Conf_minPt_Cascade", 0., "Minimum pT of Cascade"}; + Configurable Conf_maxPt_Cascade{"Conf_maxPt_Cascade", 999., "Maximum pT of Cascade"}; + // Partition for selected particles + Partition SelectedCascades = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kCascade)) && + (ncheckbit(aod::femtodreamparticle::cut, ConfCutCascade)) && + (aod::femtodreamparticle::mLambda > Conf_minInvMass_Cascade) && + (aod::femtodreamparticle::mLambda < Conf_maxInvMass_Cascade) && + (aod::femtodreamparticle::mAntiLambda > InvMassV0DaughMin) && + (aod::femtodreamparticle::mAntiLambda < InvMassV0DaughMax) && + (aod::femtodreamparticle::pt > Conf_minPt_Cascade) && + (aod::femtodreamparticle::pt < Conf_maxPt_Cascade); + /// Histogramming of selected Cascades + FemtoDreamParticleHisto particleHistoSelectedCascades; + FemtoDreamParticleHisto particleHistoPosChild; + FemtoDreamParticleHisto particleHistoNegChild; + FemtoDreamParticleHisto particleHistoBachChild; + /// Histogramming for Event + FemtoDreamEventHisto eventHisto; + /// Binning + ConfigurableAxis ConfTempFitVarBinsTrack{"ConfTempFitVarBinsTrack", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis ConfTempFitVarpTBins{"ConfTempFitVarpTBins", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot (track)"}; + ConfigurableAxis ConfTempFitVarBinsCascade{"ConfTempFitVarBinsCascade", {300, 0.9, 1}, "binning of the TempFitVar in the pT vs. TempFitVar plot (V0)"}; + ConfigurableAxis ConfTempFitVarBinsCascadeChild{"ConfTempFitVarBinsCascadeChild", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot (V0 child)"}; + ConfigurableAxis ConfTempFitVarpTV0Bins{"ConfTempFitVarpTV0Bins", {35, 0, 6}, "pT binning of the pT vs. TempFitVar plot (V0)"}; + ConfigurableAxis ConfTempFitVarpTV0Child{"ConfTempFitVarpTV0Child", {35, 0, 6}, "pT binning of the pT vs. TempFitVar plot (V0 child)"}; + ConfigurableAxis ConfInvMassBins{"ConfInvMassBins", {200, 1, 1.6}, "InvMass binning"}; + ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; + ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; + ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; + Configurable ConfNEventsMix{"ConfNEventsMix", 5, "Number of events for mixing"}; + Configurable ConfIsCPR{"ConfIsCPR", true, "Close Pair Rejection"}; + Configurable ConfFillCPRQA{"ConfFillCPRQA", false, "Fill Close Pair Rejection plots as a function of eta and phi"}; + Configurable ConfCPRPlotPerRadii{"ConfCPRPlotPerRadii", false, "Plot CPR per radii"}; + Configurable ConfCPRdeltaPhiMax_pp{"ConfCPRdeltaPhiMax_pp", 0.01, "Max. Delta Phi for Close Pair Rejection of pp"}; + Configurable ConfCPRdeltaEtaMax_pp{"ConfCPRdeltaEtaMax_pp", 0.01, "Max. Delta Eta for Close Pair Rejection of pp"}; + Configurable ConfCPRdeltaPhiMax_pL{"ConfCPRdeltaPhiMax_pL", 0.1, "Max. Delta Phi for Close Pair Rejection of p and Lambda daughter"}; + Configurable ConfCPRdeltaEtaMax_pL{"ConfCPRdeltaEtaMax_pL", 0.1, "Max. Delta Eta for Close Pair Rejection of p and Lambda daughter"}; + Configurable ConfMaxQ3IncludedInCPRPlots{"ConfMaxQ3IncludedInCPRPlots", 8., "Maximum Q3, for which the pair CPR is included in plots"}; + ConfigurableAxis ConfDummy{"ConfDummy", {1, 0, 1}, "Dummy axis"}; + FemtoDreamContainerThreeBody sameEventCont; + FemtoDreamContainerThreeBody mixedEventCont; + FemtoDreamPairCleaner pairCleanerTrackTrack; + FemtoDreamPairCleaner pairCleanerTrackCascade; + FemtoDreamDetaDphiStar pairCloseRejectionTrackTrackSE; + FemtoDreamDetaDphiStar pairCloseRejectionTrackCascadeSE; + FemtoDreamDetaDphiStar pairCloseRejectionTrackTrackME; + FemtoDreamDetaDphiStar pairCloseRejectionTrackCascadeME; + /// Histogram output + HistogramRegistry qaRegistry{"TrackQA", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry resultRegistry{"Correlations", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry ThreeBodyQARegistry{"ThreeBodyQARegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; + void init(InitContext& context) + { + eventHisto.init(&qaRegistry, false); + colBinning = {{ConfVtxBins, ConfMultBins}}; + trackHistoSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); + particleHistoSelectedCascades.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Bins, ConfDummy, ConfDummy, ConfTempFitVarBinsCascade, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfInvMassBins, ConfDummy, ConfIsMC, ConfPDGCodeCascade); + particleHistoPosChild.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Child, ConfDummy, ConfDummy, ConfTempFitVarBinsCascadeChild, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, 0); + particleHistoNegChild.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Child, ConfDummy, ConfDummy, ConfTempFitVarBinsCascadeChild, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, 0); + particleHistoBachChild.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTV0Child, ConfDummy, ConfDummy, ConfTempFitVarBinsCascadeChild, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, 0); + ThreeBodyQARegistry.add("TripletTaskQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + ThreeBodyQARegistry.add("TripletTaskQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + ThreeBodyQARegistry.add("TripletTaskQA/hMinvSE_Cascade", ";Q_{3};M_{inv}", kTH2F, {ConfQ3Bins, ConfInvMassBins}); + ThreeBodyQARegistry.add("TripletTaskQA/hMinvME_Cascade", ";Q_{3};M_{inv}", kTH2F, {ConfQ3Bins, ConfInvMassBins}); + ThreeBodyQARegistry.add("TripletTaskQA/hMinvSE_LambdaChild", ";Q_{3};M_{inv}", kTH2F, {ConfQ3Bins, ConfInvMassBins}); + ThreeBodyQARegistry.add("TripletTaskQA/hMinvME_LambdaChild", ";Q_{3};M_{inv}", kTH2F, {ConfQ3Bins, ConfInvMassBins}); + ThreeBodyQARegistry.add("TripletTaskQA/particle_pT_in_Triplet_SE", "; p_{T1} ; p_{T2} ; p_{T3} ; Q_{3}", kTHnSparseF, {ConfTempFitVarpTBins, ConfTempFitVarpTBins, ConfTempFitVarpTV0Bins, ConfQ3BinsFor4D}); + ThreeBodyQARegistry.add("TripletTaskQA/particle_pT_in_Triplet_ME", "; p_{T1} ; p_{T2} ; p_{T3} ; Q_{3}", kTHnSparseF, {ConfTempFitVarpTBins, ConfTempFitVarpTBins, ConfTempFitVarpTV0Bins, ConfQ3BinsFor4D}); + ThreeBodyQARegistry.add("TripletTaskQA/hCentrality", ";Centrality; Q3", kTH2F, {{100, 0, 100}, ConfQ3Bins}); + std::vector tmpVecMult = ConfMultBins; + framework::AxisSpec multAxis = {tmpVecMult, "Multiplicity"}; + ThreeBodyQARegistry.add("TripletTaskQA/hSEMultVSGoodTracks", ";Mult;GoodT", kTH2F, {multAxis, {100, 0, 100}}); + ThreeBodyQARegistry.add("TripletTaskQA/hTestPairCleaner", ";posDaughtID; negDaughID", kTH2F, {{40, -20, 20}, {40, -20, 20}}); + ThreeBodyQARegistry.add("TripletTaskQA/hTestPairCleanerPos", ";primaryTrack; posDaughtID", kTH2F, {{40, -20, 20}, {40, -20, 20}}); + ThreeBodyQARegistry.add("TripletTaskQA/hTestPairCleanerNeg", ";primaryTrack; negDaughtID", kTH2F, {{40, -20, 20}, {40, -20, 20}}); + ThreeBodyQARegistry.add("TripletTaskQA/hTestPairCleanerPosGlobal", ";primaryTrackGlobal; posDaughtID", kTH2F, {{40, -20, 20}, {40, -20, 20}}); + ThreeBodyQARegistry.add("TripletTaskQA/hTestPairCleanerNegGlobal", ";primaryTrackGlobal; negDaughtID", kTH2F, {{40, -20, 20}, {40, -20, 20}}); + ThreeBodyQARegistry.add("TripletTaskQA/hTestPairCleanerPosAfter", ";primaryTrack; posDaughtID", kTH2F, {{40, -20, 20}, {40, -20, 20}}); + ThreeBodyQARegistry.add("TripletTaskQA/hTestPairCleanerNegAfter", ";primaryTrack; negDaughtID", kTH2F, {{40, -20, 20}, {40, -20, 20}}); + sameEventCont.init(&resultRegistry, ConfQ3Bins, ConfMultBins, ConfIsMC); + mixedEventCont.init(&resultRegistry, ConfQ3Bins, ConfMultBins, ConfIsMC); + sameEventCont.setPDGCodes(ConfPDGCodePart, ConfPDGCodePart, ConfPDGCodeCascade); + mixedEventCont.setPDGCodes(ConfPDGCodePart, ConfPDGCodePart, ConfPDGCodeCascade); + pairCleanerTrackTrack.init(&qaRegistry); + pairCleanerTrackCascade.init(&qaRegistry); + if (ConfIsCPR.value) { + pairCloseRejectionTrackTrackSE.init(&resultRegistry, &qaRegistry, ConfCPRdeltaPhiMax_pp.value, ConfCPRdeltaEtaMax_pp.value, ConfCPRPlotPerRadii.value, 1, ConfUseOLD_possiblyWrong_CPR, ConfMaxQ3IncludedInCPRPlots, false, ConfAtWhichRadiiToCut, ConfAtWhichTPCRadii, ConfFillCPRQA); + pairCloseRejectionTrackCascadeSE.init(&resultRegistry, &qaRegistry, ConfCPRdeltaPhiMax_pL.value, ConfCPRdeltaEtaMax_pL.value, ConfCPRPlotPerRadii.value, 1, ConfUseOLD_possiblyWrong_CPR, ConfMaxQ3IncludedInCPRPlots, false, ConfAtWhichRadiiToCut, ConfAtWhichTPCRadii, ConfFillCPRQA); + pairCloseRejectionTrackTrackME.init(&resultRegistry, &qaRegistry, ConfCPRdeltaPhiMax_pp.value, ConfCPRdeltaEtaMax_pp.value, ConfCPRPlotPerRadii.value, 2, ConfUseOLD_possiblyWrong_CPR, ConfMaxQ3IncludedInCPRPlots, false, ConfAtWhichRadiiToCut, ConfAtWhichTPCRadii, ConfFillCPRQA); + pairCloseRejectionTrackCascadeME.init(&resultRegistry, &qaRegistry, ConfCPRdeltaPhiMax_pL.value, ConfCPRdeltaEtaMax_pL.value, ConfCPRPlotPerRadii.value, 2, ConfUseOLD_possiblyWrong_CPR, ConfMaxQ3IncludedInCPRPlots, true, ConfAtWhichRadiiToCut, ConfAtWhichTPCRadii, ConfFillCPRQA); + } + // get masses + mMassOne = TDatabasePDG::Instance()->GetParticle(ConfPDGCodePart)->Mass(); + mMassTwo = TDatabasePDG::Instance()->GetParticle(ConfPDGCodePart)->Mass(); + mMassThree = TDatabasePDG::Instance()->GetParticle(ConfPDGCodeCascade)->Mass(); + // get bit for the collision mask + std::bitset<8 * sizeof(aod::femtodreamcollision::BitMaskType)> mask; + int index = 0; + const auto& workflows = context.services().get(); + for (DeviceSpec const& device : workflows.devices) { + if (device.name.find("femto-dream-triplet-task-track-track-cascade") != std::string::npos) { + if (containsNameValuePair(device.options, "ConfCutPart", ConfCutPart.value) && + containsNameValuePair(device.options, "ConfTPCPIDBit", ConfTPCPIDBit.value) && + containsNameValuePair(device.options, "ConfTPCTOFPIDBit", ConfTPCTOFPIDBit.value) && + containsNameValuePair(device.options, "ConfPIDthrMom", ConfPIDthrMom.value) && + containsNameValuePair(device.options, "ConfMaxpT", ConfMaxpT.value) && + containsNameValuePair(device.options, "ConfMinpT", ConfMinpT.value) && + containsNameValuePair(device.options, "ConfCutCascade", ConfCutCascade.value) && + containsNameValuePair(device.options, "ChildPos_CutBit", ChildPos_CutBit.value) && + containsNameValuePair(device.options, "ChildPos_TPCBit", ChildPos_TPCBit.value) && + containsNameValuePair(device.options, "ChildNeg_CutBit", ChildNeg_CutBit.value) && + containsNameValuePair(device.options, "ChildNeg_TPCBit", ChildNeg_TPCBit.value) && + containsNameValuePair(device.options, "ChildBach_CutBit", ChildBach_CutBit.value) && + containsNameValuePair(device.options, "ChildBach_TPCBit", ChildBach_TPCBit.value) && + containsNameValuePair(device.options, "Conf_minInvMass_Cascade", Conf_minInvMass_Cascade.value) && + containsNameValuePair(device.options, "Conf_maxInvMass_Cascade", Conf_maxInvMass_Cascade.value) && + containsNameValuePair(device.options, "InvMassV0DaughMin", InvMassV0DaughMin.value) && + containsNameValuePair(device.options, "InvMassV0DaughMax", InvMassV0DaughMax.value) && + containsNameValuePair(device.options, "Conf_minPt_Cascade", Conf_minPt_Cascade.value) && + containsNameValuePair(device.options, "Conf_maxPt_Cascade", Conf_maxPt_Cascade.value)) { + mask.set(index); + MaskBit = static_cast(mask.to_ulong()); + LOG(info) << "Device name matched: " << device.name; + LOG(info) << "Bitmask for collisions: " << mask.to_string(); + break; + } else { + index++; + } + } + } + if ((ConfMixIfTripletPresent && ConfMixIfTVOPairPresent) || + (ConfMixIfTripletPresent && ConfMixIfTOrVOPartsPresent) || + (ConfMixIfTVOPairPresent && ConfMixIfTOrVOPartsPresent)) { + LOG(fatal) << "Only one method of mixing can be chosen!"; + } + } + /// This function processes the same event and takes care of all the histogramming + /// @tparam PartitionType + /// @tparam PartType + /// @tparam isMC: enables Monte Carlo truth specific histograms + /// @param groupSelectedTracks partition for the first particle passed by the process function + /// @param parts femtoDreamParticles table (in case of Monte Carlo joined with FemtoDreamMCLabels) + /// @param magFieldTesla magnetic field of the collision + /// @param multCol multiplicity of the collision + template + void doSameEvent(const PartitionType& groupSelectedTracks, const PartitionType& groupSelectedCascades, const PartType& parts, float magFieldTesla, int multCol, float centCol) + { + /// Histograming tracks + for (auto& part : groupSelectedTracks) { + trackHistoSelectedParts.fillQA(part, aod::femtodreamparticle::kPt, multCol, centCol); + } + /// Histograming V0s + for (auto& casc : groupSelectedCascades) { + const auto& posChild = parts.iteratorAt(casc.index() - 3); + const auto& negChild = parts.iteratorAt(casc.index() - 2); + const auto& bachChild = parts.iteratorAt(casc.index() - 1); + if (((posChild.cut() & ChildPos_CutBit) == ChildPos_CutBit && + (posChild.pidcut() & ChildPos_TPCBit) == ChildPos_TPCBit && + (negChild.cut() & ChildNeg_CutBit) == ChildNeg_CutBit && + (negChild.pidcut() & ChildNeg_TPCBit) == ChildNeg_TPCBit && + (bachChild.cut() & ChildBach_CutBit) == ChildBach_CutBit && + (bachChild.pidcut() & ChildBach_TPCBit) == ChildBach_TPCBit)) { + particleHistoSelectedCascades.fillQA(casc, aod::femtodreamparticle::kPt, multCol, centCol); + particleHistoPosChild.fillQA(posChild, aod::femtodreamparticle::kPt, multCol, centCol); + particleHistoNegChild.fillQA(negChild, aod::femtodreamparticle::kPt, multCol, centCol); + particleHistoBachChild.fillQA(bachChild, aod::femtodreamparticle::kPt, multCol, centCol); + } + } + /// Now build the combinations + for (auto& casc : groupSelectedCascades) { + const auto& posChild = parts.iteratorAt(casc.index() - 3); + const auto& negChild = parts.iteratorAt(casc.index() - 2); + const auto& childrenPos = posChild.childrenIds(); + const auto& childrenNeg = negChild.childrenIds(); + auto posID = childrenPos[0]; + auto negID = childrenNeg[1]; + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleaner"), posID, negID); + if (!((posChild.cut() & ChildPos_CutBit) == ChildPos_CutBit && + (posChild.pidcut() & ChildPos_TPCBit) == ChildPos_TPCBit && + (negChild.cut() & ChildNeg_CutBit) == ChildNeg_CutBit && + (negChild.pidcut() & ChildNeg_TPCBit) == ChildNeg_TPCBit)) { + continue; + } + for (auto& [T1, T2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupSelectedTracks, groupSelectedTracks))) { + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerPos"), T1.index(), posID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerNeg"), T1.index(), negID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerPos"), T2.index(), posID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerNeg"), T2.index(), negID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerPosGlobal"), T1.globalIndex(), posID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerNegGlobal"), T1.globalIndex(), negID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerPosGlobal"), T2.globalIndex(), posID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerNegGlobal"), T2.globalIndex(), negID); + auto Q3 = FemtoDreamMath::getQ3(T1, mMassOne, T2, mMassTwo, casc, mMassThree); + // Close pair rejection + if (ConfIsCPR.value) { + if (pairCloseRejectionTrackTrackSE.isClosePair(T1, T2, parts, magFieldTesla, Q3)) { + continue; + } + if (pairCloseRejectionTrackCascadeSE.isClosePair(T1, casc, parts, magFieldTesla, Q3)) { + continue; + } + if (pairCloseRejectionTrackCascadeSE.isClosePair(T2, casc, parts, magFieldTesla, Q3)) { + continue; + } + } + // track cleaning + if (!pairCleanerTrackTrack.isCleanPair(T1, T2, parts)) { + continue; + } + if (!pairCleanerTrackCascade.isCleanPair(T2, casc, parts)) { + continue; + } + if (!pairCleanerTrackCascade.isCleanPair(T1, casc, parts)) { + continue; + } + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerPosAfter"), T1.index(), posID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerNegAfter"), T1.index(), negID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerPosAfter"), T2.index(), posID); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hTestPairCleanerNegAfter"), T2.index(), negID); + // fill inv Mass as a function of Q3 for purity fits + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hMinvSE_Cascade"), Q3, casc.mLambda()); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hMinvSE_LambdaChild"), Q3, casc.mAntiLambda()); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/particle_pT_in_Triplet_SE"), T1.pt(), T2.pt(), casc.pt(), Q3); + sameEventCont.setTriplet(T1, T2, casc, multCol, Q3); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hCentrality"), centCol, Q3); + } + } + } + /// process function to call doSameEvent with Data + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoDreamParticleTable + void processSameEvent(o2::aod::FDCollision& col, + o2::aod::FDParticles& parts) + { + eventHisto.fillQA(col); + auto thegroupSelectedTracks = SelectedParts->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + auto thegroupSelectedCascades = SelectedCascades->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); /// Histograming V0s + if (thegroupSelectedTracks.size() < 2 || thegroupSelectedCascades.size() < 1) { + return; + } + doSameEvent(thegroupSelectedTracks, thegroupSelectedCascades, parts, col.magField(), col.multNtr(), col.multV0M()); + } + PROCESS_SWITCH(femtoDreamTripletTaskTrackTrackCascade, processSameEvent, "Enable processing same event", true); + /// This function processes the mixed event + /// \tparam PartitionType + /// \tparam PartType + /// \tparam isMC: enables Monte Carlo truth specific histograms + /// \param groupPartsOne partition for the first particle passed by the process function + /// \param groupPartsTwo partition for the second particle passed by the process function + /// \param groupPartsThree partition for the third particle passed by the process function + /// \param parts femtoDreamParticles table (in case of Monte Carlo joined with FemtoDreamMCLabels) + /// \param magFieldTesla magnetic field of the collision + /// \param multCol multiplicity of the collision + template + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartitionType& groupPartsThree, const PartType& parts, float magFieldTesla, int multCol) + { + for (auto& [T1, T2, casc] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo, groupPartsThree))) { + const auto& posChild = parts.iteratorAt(casc.globalIndex() - 3); + const auto& negChild = parts.iteratorAt(casc.globalIndex() - 2); + const auto& bachChild = parts.iteratorAt(casc.globalIndex() - 1); + if (((posChild.cut() & ChildPos_CutBit) == ChildPos_CutBit && + (posChild.pidcut() & ChildPos_TPCBit) == ChildPos_TPCBit && + (negChild.cut() & ChildNeg_CutBit) == ChildNeg_CutBit && + (negChild.pidcut() & ChildNeg_TPCBit) == ChildNeg_TPCBit && + (bachChild.cut() & ChildBach_CutBit) == ChildBach_CutBit && + (bachChild.pidcut() & ChildBach_TPCBit) == ChildBach_TPCBit)) { + continue; + } + auto Q3 = FemtoDreamMath::getQ3(T1, mMassOne, T2, mMassTwo, casc, mMassThree); + // Close pair rejection + if (ConfIsCPR.value) { + if (pairCloseRejectionTrackTrackME.isClosePair(T1, T2, parts, magFieldTesla, Q3)) { + continue; + } + if (pairCloseRejectionTrackCascadeME.isClosePair(T1, casc, parts, magFieldTesla, Q3)) { + continue; + } + if (pairCloseRejectionTrackCascadeME.isClosePair(T2, casc, parts, magFieldTesla, Q3)) { + continue; + } + } + // fill inv Mass as a function of Q3 for purity fits + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hMinvME_Cascade"), Q3, casc.mLambda()); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hMinvME_LambdaChild"), Q3, casc.mAntiLambda()); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/particle_pT_in_Triplet_ME"), T1.pt(), T2.pt(), casc.pt(), Q3); + mixedEventCont.setTriplet(T1, T2, casc, multCol, Q3); + } + } + /// process function for to call doMixedEvent with Data + /// @param cols subscribe to the collisions table (Data) + /// @param parts subscribe to the femtoDreamParticleTable + void processMixedEvent(o2::aod::FDCollisions& cols, + o2::aod::FDParticles& parts) + { + for (auto& [collision1, collision2, collision3] : soa::selfCombinations(colBinning, ConfNEventsMix, -1, cols, cols, cols)) { + const int multiplicityCol = collision1.multNtr(); + ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); + auto groupPartsOne = SelectedParts->sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = SelectedParts->sliceByCached(aod::femtodreamparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupPartsThree = SelectedCascades->sliceByCached(aod::femtodreamparticle::fdCollisionId, collision3.globalIndex(), cache); + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + const auto& magFieldTesla3 = collision3.magField(); + if ((magFieldTesla1 != magFieldTesla2) || (magFieldTesla2 != magFieldTesla3)) { + continue; + } + doMixedEvent(groupPartsOne, groupPartsTwo, groupPartsThree, parts, magFieldTesla1, multiplicityCol); + } + } + PROCESS_SWITCH(femtoDreamTripletTaskTrackTrackCascade, processMixedEvent, "Enable processing mixed events", true); +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask(cfgc), + }; + return workflow; +} diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx index 7af5db93467..7456409d1bc 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrack.cxx @@ -122,7 +122,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -150,7 +150,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); @@ -218,7 +218,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -233,7 +233,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupSelectedParts, PartType parts, float magFieldTesla, int multCol, float centCol) + void doSameEvent(const PartitionType& groupSelectedParts, const PartType& parts, float magFieldTesla, int multCol, float centCol) { /// Histogramming same event int numberOfTracksPassingSelection = 0; @@ -371,7 +371,7 @@ struct femtoDreamTripletTaskTrackTrackTrack { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartitionType groupPartsThree, PartType parts, float magFieldTesla, int multCol) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartitionType& groupPartsThree, const PartType& parts, float magFieldTesla, int multCol) { for (auto& [p1, p2, p3] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo, groupPartsThree))) { diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx index a92d54332f8..9a5087d9f6b 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackTrackPbPb.cxx @@ -129,7 +129,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -157,7 +157,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfBinmultTempFit, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBins, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); @@ -226,7 +226,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -241,7 +241,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupSelectedParts, PartType parts, float magFieldTesla, int multCol, float centCol) + void doSameEvent(const PartitionType& groupSelectedParts, const PartType& parts, float magFieldTesla, int multCol, float centCol) { /// Histogramming same event int numberOfTracksPassingSelection = 0; @@ -379,7 +379,7 @@ struct femtoDreamTripletTaskTrackTrackTrackPbPb { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartitionType groupPartsThree, PartType parts, float magFieldTesla, int multCol) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartitionType& groupPartsThree, const PartType& parts, float magFieldTesla, int multCol) { for (auto& [p1, p2, p3] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo, groupPartsThree))) { diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx index ce37fa91140..8702d8d5c77 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0.cxx @@ -167,7 +167,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { ConfigurableAxis ConfMultBins{"ConfMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis ConfVtxBins{"ConfVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfQ3Bins{"ConfQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis ConfQ3BinsFor4D{"ConfQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -200,7 +200,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { eventHisto.init(&qaRegistry, false); - colBinning = {{ConfVtxBins, ConfMultBins}, true}; + colBinning = {{ConfVtxBins, ConfMultBins}}; trackHistoSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); trackHistoALLSelectedParts.init(&qaRegistry, ConfDummy, ConfDummy, ConfTempFitVarpTBins, ConfDummy, ConfDummy, ConfTempFitVarBinsTrack, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfDummy, ConfIsMC, ConfPDGCodePart); @@ -300,7 +300,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { ThreeBodyQARegistry.fill(HIST("TripletTaskQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -315,7 +315,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupSelectedTracks, PartitionType groupSelectedV0s, PartType parts, float magFieldTesla, int multCol, float centCol) + void doSameEvent(const PartitionType& groupSelectedTracks, const PartitionType& groupSelectedV0s, const PartType& parts, float magFieldTesla, int multCol, float centCol) { /// Histograming tracks for (auto& part : groupSelectedTracks) { @@ -543,7 +543,7 @@ struct femtoDreamTripletTaskTrackTrackV0 { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartitionType groupPartsThree, PartType parts, float magFieldTesla, int multCol) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartitionType& groupPartsThree, const PartType& parts, float magFieldTesla, int multCol) { for (auto& [T1, T2, V0] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo, groupPartsThree))) { const auto& posChild = parts.iteratorAt(V0.globalIndex() - 2); diff --git a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx index f58c1deaf6e..1eecc066d27 100644 --- a/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx +++ b/PWGCF/FemtoDream/Tasks/femtoDreamTripletTaskTrackTrackV0PbPb.cxx @@ -179,7 +179,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 23.0f, 38.0f, 53.0f, 81.0f, 110.0f, 157.0f, 205.0f, 278.0f, 351.0f, 455.0f, 559.0f, 703.0f, 848.0f, 1050.0f, 1253.0f, 1530.0f, 1668.0f, 1857.0f, 2047.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confQ3Bins{"confQ3Bins", {2000, 0., 8.}, "binning Q3"}; ConfigurableAxis confQ3BinsFor4D{"confQ3BinsFor4D", {500, 0., 2.}, "binning Q3 for 4D hist"}; @@ -212,7 +212,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { eventHisto.init(&qaRegistry, false); - colBinning = {{confVtxBins, confMultBins}, true}; + colBinning = {{confVtxBins, confMultBins}}; trackHistoselectedParts.init(&qaRegistry, confDummy, confDummy, confTempFitVarpTBins, confDummy, confDummy, confTempFitVarBinsTrack, confDummy, confDummy, confDummy, confDummy, confDummy, confDummy, confIsMC, confPDGCodePart); trackHistoALLselectedParts.init(&qaRegistry, confDummy, confDummy, confTempFitVarpTBins, confDummy, confDummy, confTempFitVarBinsTrack, confDummy, confDummy, confDummy, confDummy, confDummy, confDummy, confIsMC, confPDGCodePart); @@ -316,7 +316,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { threeBodyQARegistry.fill(HIST("TripletTaskQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -331,7 +331,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupSelectedTracks, PartitionType groupselectedV0s, PartType parts, float magFieldTesla, int multCol, float centCol) + void doSameEvent(const PartitionType& groupSelectedTracks, const PartitionType& groupselectedV0s, const PartType& parts, float magFieldTesla, int multCol, float centCol) { /// Histograming tracks for (const auto& part : groupSelectedTracks) { @@ -570,7 +570,7 @@ struct FemtoDreamTripletTaskTrackTrackV0PbPb { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartitionType groupPartsThree, PartType parts, float magFieldTesla, int multCol) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartitionType& groupPartsThree, const PartType& parts, float magFieldTesla, int multCol) { for (const auto& [T1, T2, V0] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo, groupPartsThree))) { const auto& posChild = parts.iteratorAt(V0.globalIndex() - 2); diff --git a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx index ca4e27c7327..3c4ebd17713 100644 --- a/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx +++ b/PWGCF/FemtoDream/Tasks/femtodreamPairCascadeCascade.cxx @@ -200,9 +200,9 @@ struct FemtoDreamPairCascadeCascade { Configurable depth{"depth", 5, "Number of events for mixing"}; Configurable binPolicy{"binPolicy", 0, "Binning binPolicy for mixing - 0: multiplicity, 1: multipliciy percentile, 2: both"}; } Mixing; - ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{Mixing.binVztx, Mixing.binMult}}; + ColumnBinningPolicy colBinningMultPercentile{{Mixing.binVztx, Mixing.binMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; FemtoDreamPairCleaner pairCleaner; @@ -216,9 +216,9 @@ struct FemtoDreamPairCascadeCascade { void init(InitContext&) { // setup binnnig binPolicy for mixing - colBinningMult = {{Mixing.binVztx, Mixing.binMult}, true}; - colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}, true}; - colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}, true}; + colBinningMult = {{Mixing.binVztx, Mixing.binMult}}; + colBinningMultPercentile = {{Mixing.binVztx, Mixing.binMultPercentile}}; + colBinningMultMultPercentile = {{Mixing.binVztx, Mixing.binMult, Mixing.binMultPercentile}}; eventHisto.init(®istry, Option.isMC); cascHistoPartOne.init(®istry, Binning.multTempFit, Option.dummy, Binning.pTCascade, Option.dummy, Option.dummy, Binning.tempFitVarCascade, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.dummy, Option.isMC, Cascade1.pdgCode); @@ -373,7 +373,7 @@ struct FemtoDreamPairCascadeCascade { // Mixed events template - void doMixedEvent(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, BinningType binPolicy) + void doMixedEvent(CollisionType const& cols, PartType const& parts, PartitionType& part1, PartitionType& part2, const BinningType& binPolicy) { for (auto const& [collision1, collision2] : soa::selfCombinations(binPolicy, Mixing.depth.value, -1, cols, cols)) { // make sure that tracks in same events are not mixed diff --git a/PWGCF/FemtoDream/Utils/femtoDreamCutCulator.h b/PWGCF/FemtoDream/Utils/femtoDreamCutCulator.h index 5e41fb5187f..a57164aafe6 100644 --- a/PWGCF/FemtoDream/Utils/femtoDreamCutCulator.h +++ b/PWGCF/FemtoDream/Utils/femtoDreamCutCulator.h @@ -80,7 +80,7 @@ class FemtoDreamCutculator /// returns an std::vector in the proper format \param name Name of the /// selection in the dpl-config.json \return std::vector that can be directly /// passed to the FemtoDreamTrack/V0/../Selection - std::vector setSelection(std::string name) + std::vector setSelection(const std::string& name) { try { boost::property_tree::ptree& selections = mConfigTree.get_child(name); @@ -357,7 +357,7 @@ class FemtoDreamCutculator /// This is the function called by the executable that then outputs the full /// selection bit-wise container incorporating the user choice of selections - void analyseCuts(std::string choice, bool SysChecks = false, float sign = 1) + void analyseCuts(const std::string& choice, bool SysChecks = false, float sign = 1) { aod::femtodreamparticle::cutContainerType output = -1; if (choice == std::string("T")) { diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverse3DContainer.h b/PWGCF/FemtoUniverse/Core/FemtoUniverse3DContainer.h index 5ebc096a908..1348d2f383c 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverse3DContainer.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverse3DContainer.h @@ -75,7 +75,7 @@ class FemtoUniverse3DContainer /// \param use3dplots Flag to fill 3D plots /// \param isiden Identical or non-identical particle pair template - void initBase(std::string folderName, std::string femtoObs1D, std::string femtoObsKout, std::string femtoObsKside, std::string femtoObsKlong, T femtoObsAxis1D, T femtoObsAxisOut, T femtoObsAxisSide, T femtoObsAxisLong, T multAxis, T kTAxis, T mTAxis, T multAxis3D, T mTAxis3D, bool use3dplots, bool isiden) + void initBase(const std::string& folderName, const std::string& femtoObs1D, const std::string& femtoObsKout, const std::string& femtoObsKside, const std::string& femtoObsKlong, T femtoObsAxis1D, T femtoObsAxisOut, T femtoObsAxisSide, T femtoObsAxisLong, T multAxis, T kTAxis, T mTAxis, T multAxis3D, T mTAxis3D, bool use3dplots, bool isiden) { mHistogramRegistry->add((folderName + "/relPairMom3D").c_str(), ("; " + femtoObsKout + "; " + femtoObsKside + "; " + femtoObsKlong).c_str(), o2::framework::HistType::kTH3F, {femtoObsAxisOut, femtoObsAxisSide, femtoObsAxisLong}); mHistogramRegistry->add((folderName + "/relPairMomOut").c_str(), ("; " + femtoObsKout + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxisOut}); diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseAngularContainer.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseAngularContainer.h index 096446333e6..d6c9ae7c43c 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseAngularContainer.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseAngularContainer.h @@ -71,7 +71,7 @@ class FemtoUniverseAngularContainer /// \param kTAxis axis object for the kT axis /// \param mTAxis axis object for the mT axis template - void initBase(std::string folderName, std::string /*femtoObs*/, T /*femtoObsAxis*/, T /*multAxis*/, T /*kTAxis*/, T /*mTAxis*/, T /*multAxis3D*/, T /*mTAxis3D*/, T etaAxis, T phiAxis, bool use3dplots) + void initBase(const std::string& folderName, const std::string& /*femtoObs*/, T /*femtoObsAxis*/, T /*multAxis*/, T /*kTAxis*/, T /*mTAxis*/, T /*multAxis3D*/, T /*mTAxis3D*/, T etaAxis, T phiAxis, bool use3dplots) { mHistogramRegistry->add((folderName + "/DeltaEtaDeltaPhi").c_str(), "; #Delta#varphi (rad); #Delta#eta", o2::framework::HistType::kTH2F, {phiAxis, etaAxis}); if (use3dplots) { @@ -85,7 +85,7 @@ class FemtoUniverseAngularContainer /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) /// \param femtoObsAxis axis object for the femto observable axis template - void initMC(std::string /*folderName*/, std::string /*femtoObs*/, T /*femtoObsAxis*/, T /*multAxis*/, T /*mTAxis*/) + void initMC(const std::string& /*folderName*/, const std::string& /*femtoObs*/, T /*femtoObsAxis*/, T /*multAxis*/, T /*mTAxis*/) { } diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h index 43d3e2cf54b..920cfbdd2b2 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseCascadeSelection.h @@ -86,7 +86,7 @@ class FemtoUniverseCascadeSelection public: FemtoUniverseCascadeSelection() - : nPtCascadeMinSel(0), nPtCascadeMaxSel(0), nEtaCascadeMaxSel(0), nDCAV0DaughMax(0), nCPAV0Min(0), nTranRadV0Min(0), nTranRadV0Max(0), nV0DecVtxMax(0), nDCACascadeDaughMax(0), nCPACascadeMin(0), nTranRadCascadeMin(0), nTranRadCascadeMax(0), nDecVtxMax(0), nDCAPosToPV(0), nDCANegToPV(0), nDCABachToPV(0), nDCAV0ToPV(0), pTCascadeMin(9999999.), pTCascadeMax(-9999999.), etaCascadeMax(-9999999.), fDCAV0DaughMax(-9999999.), fCPAV0Min(9999999.), fTranRadV0Min(9999999.), fTranRadV0Max(-9999999.), fV0DecVtxMax(-9999999.), fDCACascadeDaughMax(-9999999.), fCPACascadeMin(9999999.), fTranRadCascadeMin(9999999.), fTranRadCascadeMax(-9999999.), fDecVtxMax(-9999999.), fDCAPosToPV(9999999.), fDCANegToPV(9999999.), fDCABachToPV(9999999.), fDCAV0ToPV(9999999.), fV0InvMassLowLimit(1.05), fV0InvMassUpLimit(1.3), fInvMassLowLimitXi(1.25), fInvMassUpLimitXi(1.4), fInvMassLowLimitOmega(1.6), fInvMassUpLimitOmega(1.8) /*, fRejectCompetingMass(false), fInvMassCompetingLowLimit(1.5), fInvMassCompetingUpLimit(2.0), nSigmaPIDOffsetTPC(0.)*/ + : nPtCascadeMinSel(0), nPtCascadeMaxSel(0), nEtaCascadeMaxSel(0), nDCAV0DaughMax(0), nCPAV0Min(0), nTranRadV0Min(0), nTranRadV0Max(0), nV0DecVtxMax(0), nDCACascadeDaughMax(0), nCPACascadeMin(0), nTranRadCascadeMin(0), nTranRadCascadeMax(0), nDecVtxMax(0), nDCAPosToPV(0), nDCANegToPV(0), nDCABachToPV(0), nDCAV0ToPV(0), pTCascadeMin(9999999.), pTCascadeMax(-9999999.), etaCascadeMax(-9999999.), fDCAV0DaughMax(-9999999.), fCPAV0Min(9999999.), fTranRadV0Min(9999999.), fTranRadV0Max(-9999999.), fV0DecVtxMax(-9999999.), fDCACascadeDaughMax(-9999999.), fCPACascadeMin(9999999.), fTranRadCascadeMin(9999999.), fTranRadCascadeMax(-9999999.), fDecVtxMax(-9999999.), fDCAPosToPV(9999999.), fDCANegToPV(9999999.), fDCABachToPV(9999999.), fDCAV0ToPV(9999999.), fV0InvMassLowLimit(1.05), fV0InvMassUpLimit(1.3), fInvMassLowLimitXi(1.25), fInvMassUpLimitXi(1.4), fInvMassLowLimitOmega(1.6), fInvMassUpLimitOmega(1.8), fRejectCompetingMass(false) /*, fInvMassCompetingLowLimit(1.5), fInvMassCompetingUpLimit(2.0), nSigmaPIDOffsetTPC(0.)*/ { } @@ -176,6 +176,13 @@ class FemtoUniverseCascadeSelection fInvMassCompetingUpLimit = upLimit; }*/ + /// Set whether to reject competing mass for Omega + /// \param reject Boolean flag + void setRejectCompetingMassOmega(bool reject) + { + fRejectCompetingMass = reject; + } + private: int nPtCascadeMinSel; int nPtCascadeMaxSel; @@ -227,6 +234,8 @@ class FemtoUniverseCascadeSelection // float nSigmaPIDOffsetTPC; + bool fRejectCompetingMass; + FemtoUniverseTrackSelection posDaughTrack; FemtoUniverseTrackSelection negDaughTrack; FemtoUniverseTrackSelection bachTrackSel; @@ -437,11 +446,15 @@ bool FemtoUniverseCascadeSelection::isSelectedMinimal(Col const& col, Casc const const float invMassXi = cascade.mXi(); const float invMassOmega = cascade.mOmega(); + // Check individual mass window hypotheses + const bool isXi = (invMassXi >= fInvMassLowLimitXi && invMassXi <= fInvMassUpLimitXi); + const bool isOmega = (invMassOmega >= fInvMassLowLimitOmega && invMassOmega <= fInvMassUpLimitOmega); + const bool passOmega = isOmega && (!fRejectCompetingMass || !isXi); + if (invMassLambda < fV0InvMassLowLimit || invMassLambda > fV0InvMassUpLimit) { return false; } - // Accepts the cascade candidates as either Xi or Omega but not both - if ((invMassXi < fInvMassLowLimitXi || invMassXi > fInvMassUpLimitXi) == (invMassOmega < fInvMassLowLimitOmega || invMassOmega > fInvMassUpLimitOmega)) { + if (!isXi && !passOmega) { return false; } /*if (fRejectCompetingMass) { diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseContainer.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseContainer.h index 6de1e014233..e471117d25f 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseContainer.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseContainer.h @@ -71,7 +71,7 @@ class FemtoUniverseContainer /// \param kTAxis axis object for the kT axis /// \param mTAxis axis object for the mT axis template - void initBase(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis, T kTAxis, T mTAxis, T multAxis3D, T mTAxis3D, T etaAxis, T phiAxis, bool use3dplots) + void initBase(const std::string& folderName, const std::string& femtoObs, T femtoObsAxis, T multAxis, T kTAxis, T mTAxis, T multAxis3D, T mTAxis3D, T etaAxis, T phiAxis, bool use3dplots) { mHistogramRegistry->add((folderName + "/relPairDist").c_str(), ("; " + femtoObs + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxis}); mHistogramRegistry->add((folderName + "/relPairkT").c_str(), "; #it{k}_{T} (GeV/#it{c}); Entries", o2::framework::HistType::kTH1F, {kTAxis}); @@ -97,7 +97,7 @@ class FemtoUniverseContainer /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) /// \param femtoObsAxis axis object for the femto observable axis template - void initMC(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis, T mTAxis) + void initMC(const std::string& folderName, const std::string& femtoObs, T femtoObsAxis, T multAxis, T mTAxis) { mHistogramRegistry->add((folderName + "/relPairDist_ReconNoFake").c_str(), ("; " + femtoObs + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxis}); mHistogramRegistry->add((folderName + "/relPairkstarmT_ReconNoFake").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}^{2})").c_str(), o2::framework::HistType::kTH2F, {femtoObsAxis, mTAxis}); diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseCutculator.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseCutculator.h index aa59d49f1fc..8d0865e0887 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseCutculator.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseCutculator.h @@ -78,7 +78,7 @@ class FemtoUniverseCutculator /// returns an std::vector in the proper format \param name Name of the /// selection in the dpl-config.json \return std::vector that can be directly /// passed to the FemtoUniverseTrack/V0/../Selection - std::vector setSelection(std::string name) + std::vector setSelection(const std::string& name) { try { boost::property_tree::ptree& selections = mConfigTree.get_child(name); @@ -313,7 +313,7 @@ class FemtoUniverseCutculator /// This is the function called by the executable that then outputs the full /// selection bit-wise container incorporating the user choice of selections - void analyseCuts(std::string choice, bool SysChecks = false, float sign = 1) + void analyseCuts(const std::string& choice, bool SysChecks = false, float sign = 1) { aod::femtouniverseparticle::CutContainerType output = -1; if (choice == std::string("T")) { diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseEfficiencyCorrection.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseEfficiencyCorrection.h index 23506574426..29ea922550e 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseEfficiencyCorrection.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseEfficiencyCorrection.h @@ -70,7 +70,7 @@ class EfficiencyCorrection { } - auto init(framework::HistogramRegistry* registry, std::vector axisSpecs) -> void + auto init(framework::HistogramRegistry* registry, const std::vector& axisSpecs) -> void { shouldFillHistograms = config->confEffCorFillHist; shouldSetMultToConst = config->confEffCorSetMultToConst; diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseFemtoContainer.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseFemtoContainer.h index 6fbd4d004e3..d862f8f2056 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseFemtoContainer.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseFemtoContainer.h @@ -70,7 +70,7 @@ class FemtoUniverseFemtoContainer /// \param kTAxis axis object for the kT axis /// \param mTAxis axis object for the mT axis template - void initBase(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis, T kTAxis, T mTAxis, T multAxis3D, T mTAxis3D, bool use3dplots) + void initBase(const std::string& folderName, const std::string& femtoObs, T femtoObsAxis, T multAxis, T kTAxis, T mTAxis, T multAxis3D, T mTAxis3D, bool use3dplots) { kHistogramRegistry->add((folderName + "/relPairDist").c_str(), ("; " + femtoObs + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxis}); kHistogramRegistry->add((folderName + "/relPairkT").c_str(), "; #it{k}_{T} (GeV/#it{c}); Entries", o2::framework::HistType::kTH1F, {kTAxis}); @@ -93,7 +93,7 @@ class FemtoUniverseFemtoContainer /// \param folderName Name of the directory in the output file (no suffix for reconstructed data/ Monte Carlo; "_MC" for Monte Carlo Truth) /// \param femtoObsAxis axis object for the femto observable axis template - void initMC(std::string folderName, std::string femtoObs, T femtoObsAxis, T multAxis, T mTAxis) + void initMC(const std::string& folderName, const std::string& femtoObs, T femtoObsAxis, T multAxis, T mTAxis) { kHistogramRegistry->add((folderName + "/relPairDist_ReconNoFake").c_str(), ("; " + femtoObs + "; Entries").c_str(), o2::framework::HistType::kTH1F, {femtoObsAxis}); kHistogramRegistry->add((folderName + "/relPairkstarmT_ReconNoFake").c_str(), ("; " + femtoObs + "; #it{m}_{T} (GeV/#it{c}^{2})").c_str(), o2::framework::HistType::kTH2F, {femtoObsAxis, mTAxis}); @@ -205,9 +205,10 @@ class FemtoUniverseFemtoContainer /// \param part2 Particle two /// \param mult Multiplicity of the event template - void setPair(T const& part1, T const& part2, const int mult, bool use3dplots) + void setPair(T const& part1, T const& part2, const int mult, bool use3dplots, bool onlyPrimaryMC = false) { float femtoObs, femtoObsMC; + // Calculate femto observable and the mT with reconstructed information if constexpr (kFemtoObs == femto_universe_femto_container::Observable::kstar) { femtoObs = FemtoUniverseMath::getkstar(part1, kMassOne, part2, kMassTwo); @@ -226,8 +227,10 @@ class FemtoUniverseFemtoContainer const float mTMC = FemtoUniverseMath::getmT(part1.fdMCParticle(), kMassOne, part2.fdMCParticle(), kMassTwo); if (std::abs(part1.fdMCParticle().pdgMCTruth()) == std::abs(kPDGOne) && std::abs(part2.fdMCParticle().pdgMCTruth()) == std::abs(kPDGTwo)) { // Note: all pair-histogramms are filled with MC truth information ONLY in case of non-fake candidates - setPairBase(femtoObsMC, mTMC, part1.fdMCParticle(), part2.fdMCParticle(), mult, use3dplots); - setPairMC(femtoObsMC, femtoObs, mT, mult); + if (!onlyPrimaryMC || (part1.fdMCParticle().partOriginMCTruth() == o2::aod::femtouniverse_mc_particle::kPrimary && part2.fdMCParticle().partOriginMCTruth() == o2::aod::femtouniverse_mc_particle::kPrimary)) { + setPairBase(femtoObsMC, mTMC, part1.fdMCParticle(), part1.fdMCParticle(), mult, use3dplots); + setPairMC(femtoObsMC, femtoObs, mT, mult); + } } else { kHistogramRegistry->fill(HIST(kFolderSuffix[kEventType]) + HIST(o2::aod::femtouniverse_mc_particle::MCTypeName[o2::aod::femtouniverse_mc_particle::MCType::kTruth]) + HIST("/hFakePairsCounter"), 0); } diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniversePairSHCentMultKt.h b/PWGCF/FemtoUniverse/Core/FemtoUniversePairSHCentMultKt.h index a1d65c44fc6..abcfc8efada 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniversePairSHCentMultKt.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniversePairSHCentMultKt.h @@ -20,7 +20,6 @@ #include "PWGCF/FemtoUniverse/Core/FemtoUniverseSHContainer.h" #include "PWGCF/FemtoUniverse/Core/FemtoUniverseSpherHarMath.h" -#include #include #include @@ -84,7 +83,6 @@ class PairSHCentMultKt int em = 0; int il = 0; do { - fels[il] = el; fems[il] = em; felsi[il] = static_cast(el); @@ -245,10 +243,10 @@ class PairSHCentMultKt /// Set the PDG codes of the two particles involved /// \param pdg1 PDG code of particle one /// \param pdg2 PDG code of particle two - void setPionPairMass() + void setPairMass(const float mass1, const float mass2) { - mMassOne = o2::constants::physics::MassPiPlus; // FIXME: Get from the PDG service of the common header - mMassTwo = o2::constants::physics::MassPiPlus; // FIXME: Get from the PDG service of the common header + mMassOne = mass1; + mMassTwo = mass2; } /// To compute the bin value for cavariance matrix @@ -278,10 +276,8 @@ class PairSHCentMultKt int fKtBin = ktval; std::vector> fYlmBuffer(kMaxJM); std::vector f3d; - setPionPairMass(); - f3d = FemtoUniverseMath::newpairfunc(part1, mMassOne, part2, mMassTwo, - isIdenLCMS, isWeight, isIdenPRF); + f3d = FemtoUniverseMath::newpairfunc(part1, mMassOne, part2, mMassTwo, isIdenLCMS, isWeight, isIdenPRF); double varout = 0.0; double varside = 0.0; double varlong = 0.0; diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseParticleHisto.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseParticleHisto.h index 02f5a330340..0426ccef44f 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseParticleHisto.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseParticleHisto.h @@ -55,7 +55,7 @@ class FemtoUniverseParticleHisto /// \param tempFitVarpTAxis axis object for the pT axis in the pT vs. tempFitVar plots /// \param tempFitVarAxis axis object for the tempFitVar axis template - void init_base(std::string folderName, std::string tempFitVarAxisTitle, T& tempFitVarpTAxis, T& tempFitVarAxis) // o2-linter: disable=name/function-variable + void init_base(const std::string& folderName, const std::string& tempFitVarAxisTitle, T& tempFitVarpTAxis, T& tempFitVarAxis) // o2-linter: disable=name/function-variable { std::string folderSuffix = static_cast(o2::aod::femtouniverse_mc_particle::MCTypeName[mc]).c_str(); /// Histograms of the kinematic properties @@ -72,7 +72,7 @@ class FemtoUniverseParticleHisto // comment template - void init_debug(std::string folderName, T& tempFitVarMomAxis, bool isFillITSNsigma) // o2-linter: disable=name/function-variable + void init_debug(const std::string& folderName, T& tempFitVarMomAxis, bool isFillITSNsigma) // o2-linter: disable=name/function-variable { std::string folderSuffix = static_cast(o2::aod::femtouniverse_mc_particle::MCTypeName[mc]).c_str(); if constexpr (mParticleType == o2::aod::femtouniverseparticle::ParticleType::kTrack || mParticleType == o2::aod::femtouniverseparticle::ParticleType::kV0Child || mParticleType == o2::aod::femtouniverseparticle::ParticleType::kCascadeBachelor || mParticleType == o2::aod::femtouniverseparticle::ParticleType::kMCTruthTrack) { @@ -146,7 +146,7 @@ class FemtoUniverseParticleHisto /// \param tempFitVarpTAxis axis object for the pT axis in the pT vs. tempFitVar plots /// \param tempFitVarAxis axis object for the tempFitVar axis template - void init_MC(std::string folderName, std::string /*tempFitVarAxisTitle*/, T& tempFitVarpTAxis, T& tempFitVarAxis, bool isDebug) // o2-linter: disable=name/function-variable + void init_MC(const std::string& folderName, const std::string& /*tempFitVarAxisTitle*/, T& tempFitVarpTAxis, T& tempFitVarAxis, bool isDebug) // o2-linter: disable=name/function-variable { /// Particle-type specific histograms std::string folderSuffix = static_cast(o2::aod::femtouniverse_mc_particle::MCTypeName[o2::aod::femtouniverse_mc_particle::MCType::kTruth]).c_str(); @@ -222,7 +222,7 @@ class FemtoUniverseParticleHisto /// \param tempFitVarBins binning of the tempFitVar (DCA_xy in case of tracks, CPA in case of V0s, etc.) /// \param isMC add Monte Carlo truth histograms to the output file template - void init(o2::framework::HistogramRegistry* registry, T& tempFitVarpTBins, T& tempFitVarBins, bool isMC, int pdgCode, bool isDebug = false, std::optional flexibleFolder = std::nullopt, bool isFillITSNsigma = false) + void init(o2::framework::HistogramRegistry* registry, T& tempFitVarpTBins, T& tempFitVarBins, bool isMC, int pdgCode, bool isDebug = false, const std::optional& flexibleFolder = std::nullopt, bool isFillITSNsigma = false) { mPDG = pdgCode; if (registry) { diff --git a/PWGCF/FemtoUniverse/Core/FemtoUniverseTrackSelection.h b/PWGCF/FemtoUniverse/Core/FemtoUniverseTrackSelection.h index ed83b9e6e19..82a7d075840 100644 --- a/PWGCF/FemtoUniverse/Core/FemtoUniverseTrackSelection.h +++ b/PWGCF/FemtoUniverse/Core/FemtoUniverseTrackSelection.h @@ -22,7 +22,6 @@ #include "PWGCF/FemtoUniverse/Core/FemtoUniverseSelection.h" #include "PWGCF/FemtoUniverse/DataModel/FemtoDerived.h" -#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" diff --git a/PWGCF/FemtoUniverse/Core/femtoUtils.h b/PWGCF/FemtoUniverse/Core/femtoUtils.h index daeef089442..e451cf06c35 100644 --- a/PWGCF/FemtoUniverse/Core/femtoUtils.h +++ b/PWGCF/FemtoUniverse/Core/femtoUtils.h @@ -61,7 +61,7 @@ bool isPIDSelected(aod::femtouniverseparticle::CutContainerType pidcut, int vSpecies, int nSpecies, float nSigma, - std::vector vNsigma, + const std::vector& vNsigma, KDetector iDet) { int iNsigma = getPIDselection(nSigma, vNsigma); @@ -85,7 +85,7 @@ bool isFullPIDSelected(aod::femtouniverseparticle::CutContainerType const& pidCu float pidThresh, int vSpecies, int nSpecies, - std::vector vNsigma, + const std::vector& vNsigma, float nSigmaTPC, float nSigmaTPCTOF) { diff --git a/PWGCF/FemtoUniverse/DataModel/FemtoDerived.h b/PWGCF/FemtoUniverse/DataModel/FemtoDerived.h index 71a3a3acdeb..339f477249a 100644 --- a/PWGCF/FemtoUniverse/DataModel/FemtoDerived.h +++ b/PWGCF/FemtoUniverse/DataModel/FemtoDerived.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -75,8 +76,8 @@ enum ParticleType { kNParticleTypes //! Number of particle types }; -static constexpr std::string_view ParticleTypeName[kNParticleTypes] = {"Tracks", "MCTruthTracks", "V0", "V0Child", "Cascade", "CascadeV0Child", "CascadeBachelor", "Phi", "PhiChild", "D0", "D0Child"}; //! Naming of the different particle types -static constexpr std::string_view TempFitVarName[kNParticleTypes] = {"/hDCAxy", "/hPDGvspT", "/hCPA", "/hDCAxy", "/hCPA", "/hDCAxy", "/hInvMass", "/hDCAxy", "/hInvMass", "/hDCAxy"}; +static constexpr std::array ParticleTypeName = {"Tracks", "MCTruthTracks", "V0", "V0Child", "Cascade", "CascadeV0Child", "CascadeBachelor", "Phi", "PhiChild", "D0", "D0Child"}; //! Naming of the different particle types +static constexpr std::array TempFitVarName = {"/hDCAxy", "/hPDGvspT", "/hCPA", "/hDCAxy", "/hCPA", "/hDCAxy", "/hInvMass", "/hDCAxy", "/hInvMass", "/hDCAxy"}; using CutContainerType = uint32_t; //! Definition of the data type for the bit-wise container for the different selection criteria @@ -88,7 +89,7 @@ enum TrackType { kNTrackTypes //! Number of child types }; -static constexpr std::string_view TrackTypeName[kNTrackTypes] = {"Trk", "Pos", "Neg", "Bach"}; //! Naming of the different particle types +static constexpr std::array TrackTypeName = {"Trk", "Pos", "Neg", "Bach"}; //! Naming of the different particle types DECLARE_SOA_INDEX_COLUMN(FdCollision, fdCollision); DECLARE_SOA_COLUMN(Pt, pt, float); //! p_T (GeV/c) @@ -123,13 +124,25 @@ DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! Compute the overall momentum in GeV/c return pt * std::cosh(eta); }); -DECLARE_SOA_COLUMN(ITSNSigmaEl, itsNSigmaEl, float); //! Nsigma separation with the Its detector for electron -DECLARE_SOA_COLUMN(ITSNSigmaPi, itsNSigmaPi, float); //! Nsigma separation with the Its detector for pion -DECLARE_SOA_COLUMN(ITSNSigmaKa, itsNSigmaKa, float); //! Nsigma separation with the Its detector for kaon -DECLARE_SOA_COLUMN(ITSNSigmaPr, itsNSigmaPr, float); //! Nsigma separation with the Its detector for proton -DECLARE_SOA_COLUMN(ITSNSigmaDe, itsNSigmaDe, float); //! Nsigma separation with the Its detector for deuteron -DECLARE_SOA_COLUMN(ITSNSigmaTr, itsNSigmaTr, float); //! Nsigma separation with the Its detector for triton -DECLARE_SOA_COLUMN(ITSNSigmaHe, itsNSigmaHe, float); //! Nsigma separation with the Its detector for helium3 +DECLARE_SOA_COLUMN(TofFullNSigmaEl, tofFullNSigmaEl, float); //! Nsigma separation with the TOF detector for electron +DECLARE_SOA_COLUMN(TofFullNSigmaPi, tofFullNSigmaPi, float); //! Nsigma separation with the TOF detector for pion +DECLARE_SOA_COLUMN(TofFullNSigmaKa, tofFullNSigmaKa, float); //! Nsigma separation with the TOF detector for kaon +DECLARE_SOA_COLUMN(TofFullNSigmaPr, tofFullNSigmaPr, float); //! Nsigma separation with the TOF detector for proton +DECLARE_SOA_COLUMN(TofFullNSigmaDe, tofFullNSigmaDe, float); //! Nsigma separation with the TOF detector for deuteron + +DECLARE_SOA_COLUMN(TpcFullNSigmaEl, tpcFullNSigmaEl, float); //! Nsigma separation with the TPC detector for electron +DECLARE_SOA_COLUMN(TpcFullNSigmaPi, tpcFullNSigmaPi, float); //! Nsigma separation with the TPC detector for pion +DECLARE_SOA_COLUMN(TpcFullNSigmaKa, tpcFullNSigmaKa, float); //! Nsigma separation with the TPC detector for kaon +DECLARE_SOA_COLUMN(TpcFullNSigmaPr, tpcFullNSigmaPr, float); //! Nsigma separation with the TPC detector for proton +DECLARE_SOA_COLUMN(TpcFullNSigmaDe, tpcFullNSigmaDe, float); //! Nsigma separation with the TPC detector for deuteron + +DECLARE_SOA_COLUMN(ItsNSigmaEl, itsNSigmaEl, float); //! Nsigma separation with the Its detector for electron +DECLARE_SOA_COLUMN(ItsNSigmaPi, itsNSigmaPi, float); //! Nsigma separation with the Its detector for pion +DECLARE_SOA_COLUMN(ItsNSigmaKa, itsNSigmaKa, float); //! Nsigma separation with the Its detector for kaon +DECLARE_SOA_COLUMN(ItsNSigmaPr, itsNSigmaPr, float); //! Nsigma separation with the Its detector for proton +DECLARE_SOA_COLUMN(ItsNSigmaDe, itsNSigmaDe, float); //! Nsigma separation with the Its detector for deuteron +DECLARE_SOA_COLUMN(ItsNSigmaTr, itsNSigmaTr, float); //! Nsigma separation with the Its detector for triton +DECLARE_SOA_COLUMN(ItsNSigmaHe, itsNSigmaHe, float); //! Nsigma separation with the Its detector for helium3 // debug variables DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Sign of the track charge @@ -174,7 +187,7 @@ using FDParticle = FDParticles::iterator; /// FemtoUniverseCascadeTrack namespace femtouniversecascparticle { -DECLARE_SOA_INDEX_COLUMN(FDParticle, fdParticle); +DECLARE_SOA_INDEX_COLUMN(FDParticle, fdParticle); // o2-linter: disable=name/o2-column DECLARE_SOA_COLUMN(DcaV0daughters, dcaV0daughters, float); //! DCA between V0 daughters DECLARE_SOA_COLUMN(Cpav0, cpav0, float); //! V0 cos of pointing angle DECLARE_SOA_COLUMN(V0radius, v0radius, float); //! V0 transverse radius @@ -230,12 +243,25 @@ DECLARE_SOA_TABLE(FDExtParticles, "AOD", "FDEXTPARTICLE", pidtof_tiny::TOFNSigmaDe); using FDFullParticle = FDExtParticles::iterator; +DECLARE_SOA_TABLE(FDSigmaParticles, "AOD", "FDSIGMAPARTICLE", + femtouniverseparticle::TofFullNSigmaEl, + femtouniverseparticle::TofFullNSigmaPi, + femtouniverseparticle::TofFullNSigmaKa, + femtouniverseparticle::TofFullNSigmaPr, + femtouniverseparticle::TofFullNSigmaDe, + femtouniverseparticle::TpcFullNSigmaEl, + femtouniverseparticle::TpcFullNSigmaPi, + femtouniverseparticle::TpcFullNSigmaKa, + femtouniverseparticle::TpcFullNSigmaPr, + femtouniverseparticle::TpcFullNSigmaDe); +using FDSigmaParticle = FDSigmaParticles::iterator; + DECLARE_SOA_TABLE(FDItsParticles, "AOD", "FDITSPARTICLE", - femtouniverseparticle::ITSNSigmaEl, - femtouniverseparticle::ITSNSigmaPi, - femtouniverseparticle::ITSNSigmaKa, - femtouniverseparticle::ITSNSigmaPr, - femtouniverseparticle::ITSNSigmaDe); + femtouniverseparticle::ItsNSigmaEl, + femtouniverseparticle::ItsNSigmaPi, + femtouniverseparticle::ItsNSigmaKa, + femtouniverseparticle::ItsNSigmaPr, + femtouniverseparticle::ItsNSigmaDe); using FDItsParticle = FDItsParticles::iterator; DECLARE_SOA_TABLE(FDCascParticles, "AOD", "FDCASCPARTICLE", @@ -279,7 +305,7 @@ enum ParticleOriginMCTruth { }; //! Naming of the different OriginMCTruth types -static constexpr std::string_view ParticleOriginMCTruthName[kNOriginMCTruthTypes] = { +static constexpr std::array ParticleOriginMCTruthName = { "_Primary", "_Daughter", "_Material", @@ -297,7 +323,7 @@ enum MCType { kNMCTypes }; -static constexpr std::string_view MCTypeName[kNMCTypes] = {"", "_MC"}; +static constexpr std::array MCTypeName = {"", "_MC"}; DECLARE_SOA_COLUMN(PartOriginMCTruth, partOriginMCTruth, uint8_t); //! Origin of the particle, according to femtouniverseparticle::ParticleOriginMCTruth DECLARE_SOA_COLUMN(PdgMCTruth, pdgMCTruth, int); //! Particle PDG diff --git a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerMCTruthTask.cxx b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerMCTruthTask.cxx index 3f94d573ee9..dda1fa86320 100644 --- a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerMCTruthTask.cxx +++ b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerMCTruthTask.cxx @@ -18,6 +18,8 @@ #include "PWGCF/FemtoUniverse/Core/FemtoUniverseTrackSelection.h" #include "PWGCF/FemtoUniverse/DataModel/FemtoDerived.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/EventSelection.h" @@ -40,6 +42,7 @@ #include #include #include +#include #include using namespace o2; @@ -47,6 +50,7 @@ using namespace o2::analysis::femto_universe; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::physics; +using namespace o2::aod::rctsel; namespace o2::aod { @@ -76,9 +80,9 @@ int getRowDaughters(int daughID, T const& vecID) } struct FemtoUniverseProducerMCTruthTask { - int mRunNumber; - float mMagField; - Service ccdb; /// Accessing the CCDB + + float mMagField = 0.; + Service ccdb{}; /// Accessing the CCDB // Tables being produced Produces outputCollision; @@ -86,12 +90,16 @@ struct FemtoUniverseProducerMCTruthTask { // Produces outputPartsMCLabels; // Produces outputPartsMC; + RCTFlagsChecker rctChecker; + + static constexpr int kHadElasticScatt = 20; // kPHElastic - hadronic elastic scattering + static constexpr int kHadInelasticScatt = 23; // kPHInhelastic - hadronic inelastic scattering + // Analysis configs Configurable confIsRun3{"confIsRun3", false, "Running on Run3 or pilot"}; - Configurable confIsMC{"confIsMC", false, "Running on MC; implemented only for Run3"}; - Configurable confIsForceGRP{"confIsForceGRP", false, "Set true if the magnetic field configuration is not available in the usual CCDB directory (e.g. for Run 2 converted data or unanchorad Monte Carlo)"}; Configurable> confPDGCodes{"confPDGCodes", std::vector{211, -211, 2212, -2212, 333}, "PDG of particles to be stored"}; Configurable confAnalysisWithPID{"confAnalysisWithPID", true, "1: take only particles with specified PDG, 0: all particles"}; + Configurable confStoreMotherPDG{"confStoreMotherPDG", false, "Store mother's PDG in tempFitVar."}; /// Event cuts Configurable confEvtZvtx{"confEvtZvtx", 10.f, "Evt sel: Max. z-Vertex (cm)"}; @@ -101,6 +109,7 @@ struct FemtoUniverseProducerMCTruthTask { Configurable confCentFT0Min{"confCentFT0Min", 0.f, "Min CentFT0 value for centrality selection"}; Configurable confCentFT0Max{"confCentFT0Max", 200.f, "Max CentFT0 value for centrality selection"}; Configurable confDoSpher{"confDoSpher", false, "Calculate sphericity. If false sphericity will take value of 2."}; + Configurable confIsCheckRCTFlags{"confIsCheckRCTFlags", true, "Use RCTFlags"}; // Track cuts struct : o2::framework::ConfigurableGroup { @@ -113,21 +122,26 @@ struct FemtoUniverseProducerMCTruthTask { Configurable yD0CandGenMax{"yD0CandGenMax", 0.5, "Rapidity cut for the D0/D0bar mesons"}; FemtoUniverseCollisionSelection colCuts; + FemtoUniverseCollisionSelection recoCollCuts; FemtoUniverseTrackSelection trackCuts; HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::QAObject}; + HistogramRegistry qaRecoRegistry{"QARecoHistos", {}, OutputObjHandlingPolicy::QAObject}; void init(InitContext&) { - if ((doprocessTrackMC) == false) { - LOGF(fatal, "Neither processFullData nor processFullMC enabled. Please choose one."); + if (!doprocessTrackMC && !doprocessTrackMcOnlyRecoColl) { + LOGF(fatal, "Neither processTrackMC nor processTrackMcOnlyRecoColl enabled. Please choose one."); } - + rctChecker.init("CBT_hadronPID", false, true); + // MC Truth collisions colCuts.setCuts(confEvtZvtx, confEvtTriggerCheck, confEvtTriggerSel, confEvtOfflineCheck, confIsRun3, confCentFT0Min, confCentFT0Max); - colCuts.init(&qaRegistry); + // MC Reco collisions + recoCollCuts.setCuts(confEvtZvtx, confEvtTriggerCheck, confEvtTriggerSel, confEvtOfflineCheck, confIsRun3, confCentFT0Min, confCentFT0Max); + recoCollCuts.init(&qaRecoRegistry); + trackCuts.init(&qaRegistry); - mRunNumber = 0; mMagField = 0.0; /// Initializing CCDB @@ -159,6 +173,43 @@ struct FemtoUniverseProducerMCTruthTask { } } + template + bool checkMcRecoCollisions(CollisionType const& col) + { + if (confIsCheckRCTFlags && !rctChecker(col)) { + return false; + } + + // check whether the basic event selection criteria are fulfilled + // if the basic selection is NOT fulfilled MC Truth collision is rejected + if (!recoCollCuts.isSelected(col)) { + return false; + } + // additional checks on the reconstructed collisions + if (col.selection_bit(aod::evsel::kNoSameBunchPileup) && col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + recoCollCuts.fillQA(col); + return true; + } + return false; + } + + template + int getMotherPDG(ParticleType const& particle) + { + if (particle.isPhysicalPrimary()) { + return 0; + } + if (particle.has_mothers()) { + if (particle.getProcess() == kHadElasticScatt || particle.getProcess() == kHadInelasticScatt) { // treat particles from hadronic scattering (20, 23) as primary + return 0; + } + auto motherparticlesMC = particle.template mothers_as(); + const auto& motherparticleMC = motherparticlesMC.front(); + return motherparticleMC.pdgCode(); + } + return 999; + } + template void fillParticles(TrackType const& tracks) { @@ -168,8 +219,9 @@ struct FemtoUniverseProducerMCTruthTask { /// if the most open selection criteria are not fulfilled there is no /// point looking further at the track - if (particle.pt() < ConfFilteringTracks.confPtLowFilterCut || particle.pt() > ConfFilteringTracks.confPtHighFilterCut) + if (particle.pt() < ConfFilteringTracks.confPtLowFilterCut || particle.pt() > ConfFilteringTracks.confPtHighFilterCut) { continue; + } int pdgCode = particle.pdgCode(); @@ -177,19 +229,17 @@ struct FemtoUniverseProducerMCTruthTask { bool pass = false; std::vector tmpPDGCodes = confPDGCodes; // necessary due to some features of the Configurable for (auto const& pdg : tmpPDGCodes) { - if (pdgCode == Pdg::kPhi) { // phi meson - pass = true; - } else if (std::abs(pdgCode) == Pdg::kD0) { // D0(bar) meson - pass = true; - } else if (pdgCode == Pdg::kDPlus) { // D+ meson + if (pdgCode == Pdg::kPhi || // phi meson + std::abs(pdgCode) == Pdg::kD0 || // D0(bar) meson + pdgCode == Pdg::kDPlus || // D+ meson + (pdg == pdgCode && particle.isPhysicalPrimary())) { pass = true; - } else if (static_cast(pdg) == static_cast(pdgCode)) { - if (particle.isPhysicalPrimary()) - pass = true; + break; // Exit early once a match is found } } - if (!pass) + if (!pass) { continue; + } } // check if D0/D0bar mesons pass the rapidity cut @@ -199,15 +249,10 @@ struct FemtoUniverseProducerMCTruthTask { if (std::abs(particle.pdgCode()) == Pdg::kD0) { if (std::abs(particle.y()) > yD0CandGenMax) { continue; - } else { - origin = RecoDecay::getCharmHadronOrigin(tracks, particle); - } - } else { - if (std::abs(particle.eta()) > ConfFilteringTracks.confEtaFilterCut) { - continue; - } else { - origin = -99; } + origin = RecoDecay::getCharmHadronOrigin(tracks, particle); + } else if (std::abs(particle.eta()) > ConfFilteringTracks.confEtaFilterCut) { + continue; } /// check if we end-up with the correct final state using MC info @@ -216,7 +261,8 @@ struct FemtoUniverseProducerMCTruthTask { /// check if we have D0(bar) → π± K∓ continue; } - + // Getting the PDG code of the mother particle + int motherPDG = confStoreMotherPDG ? getMotherPDG(particle) : particle.pdgCode(); // we cannot use isSelectedMinimal since it takes Ncls // if (!trackCuts.isSelectedMinimal(track)) { // continue; @@ -239,15 +285,14 @@ struct FemtoUniverseProducerMCTruthTask { pdgCode, childIDs, origin, - -999.); + motherPDG); } } - void - processTrackMC(aod::McCollision const&, - soa::SmallGroups> const& collisions, - aod::McParticles const& mcParticles, - aod::BCsWithTimestamps const&) + void processTrackMC(aod::McCollision const&, + soa::SmallGroups> const& collisions, + aod::McParticles const& mcParticles, + aod::BCsWithTimestamps const&) { // magnetic field for run not needed for mc truth // fill the tables @@ -255,6 +300,39 @@ struct FemtoUniverseProducerMCTruthTask { fillParticles(mcParticles); } PROCESS_SWITCH(FemtoUniverseProducerMCTruthTask, processTrackMC, "Provide MC data for track analysis", true); + + Preslice perMCCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted> recoCollsPerMCColl = aod::mcparticle::mcCollisionId; + + void processTrackMcOnlyRecoColl(aod::McCollisions const& mccols, + soa::Join const& collisions, + aod::McParticles const& mcParticles, + aod::BCsWithTimestamps const&) + { + // check on the reco collisions + static std::set mcColIds; + mcColIds.clear(); + + for (const auto& col : collisions) { + const auto colcheck = checkMcRecoCollisions(col); + if (colcheck) { + mcColIds.insert(col.mcCollisionId()); + } + } + // filling truth collisions and particles + for (const auto& mccol : mccols) { + if (!mcColIds.contains(mccol.globalIndex())) { + continue; + } + auto groupedMCParticles = mcParticles.sliceBy(perMCCollision, mccol.globalIndex()); + auto groupedCollisions = collisions.sliceBy(recoCollsPerMCColl, mccol.globalIndex()); + // magnetic field for run not needed for mc truth + // fill the tables + fillCollisions(groupedCollisions, groupedMCParticles); + fillParticles(groupedMCParticles); + } + } + PROCESS_SWITCH(FemtoUniverseProducerMCTruthTask, processTrackMcOnlyRecoColl, "Provide MC data for track analysis from truth coll which were recontructed ", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerReducedTask.cxx b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerReducedTask.cxx index a0eb1fe69d1..627000c53d0 100644 --- a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerReducedTask.cxx +++ b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerReducedTask.cxx @@ -162,7 +162,7 @@ struct femtoUniverseProducerReducedTask { } /// Function to retrieve the nominal magnetic field in kG (0.1T) and convert it directly to T - void getMagneticFieldTesla(aod::BCsWithTimestamps::iterator bc) + void getMagneticFieldTesla(const aod::BCsWithTimestamps::iterator& bc) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable // get magnetic field for run diff --git a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx index ab9ed0fce9d..880003d8e81 100644 --- a/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx +++ b/PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx @@ -34,6 +34,7 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" @@ -62,11 +63,10 @@ #include #include -#include +#include +#include #include -#include - #include #include #include @@ -86,6 +86,7 @@ using namespace o2::analysis::femto_universe; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::physics; +using namespace o2::aod::rctsel; namespace o2::aod { @@ -100,6 +101,7 @@ using FemtoFullCollisionCentRun3MC = soa::Join; +using FemtoFullNSigmaTracks = soa::Join; // using FilteredFullV0s = soa::Filtered; /// predefined Join // table for o2::aod::V0s = soa::Join @@ -133,10 +135,13 @@ struct FemtoUniverseProducerTask { Produces outputPartsMC; Produces outputDebugParts; Produces outputDebugITSParts; + Produces outputDebugSigmaParts; Produces outputPartsMCLabels; Produces outputDebugPartsMC; Produces outputCascParts; + RCTFlagsChecker rctChecker; + Configurable confIsDebug{"confIsDebug", true, "Enable Debug tables"}; Configurable confFillITSPid{"confFillITSPid", false, "Fill ITSPid information"}; Configurable confIsUseCutculator{"confIsUseCutculator", true, "Enable cutculator for track cuts"}; @@ -188,6 +193,7 @@ struct FemtoUniverseProducerTask { Configurable confTPCOccupancyMin{"confTPCOccupancyMin", 0, "Minimum value for TPC Occupancy selection"}; Configurable confTPCOccupancyMax{"confTPCOccupancyMax", 5000, "Maximum value for TPC Occupancy selection"}; Configurable confIsCent{"confIsCent", true, "Centrality or multiplicity selection"}; + Configurable confIsCheckRCTFlags{"confIsCheckRCTFlags", true, "Use RCTFlags"}; } ConfGeneral; Filter customCollCentFilter = (aod::cent::centFT0C > ConfGeneral.confCentFT0Min) && (aod::cent::centFT0C < ConfGeneral.confCentFT0Max); @@ -318,6 +324,7 @@ struct FemtoUniverseProducerTask { Configurable confXiInvMassUpLimit{"confXiInvMassUpLimit", 1.40, "Upper limit of the Xi invariant mass"}; Configurable confOmegaInvMassLowLimit{"confOmegaInvMassLowLimit", 1.60, "Lower limit of the Omega invariant mass"}; Configurable confOmegaInvMassUpLimit{"confOmegaInvMassUpLimit", 1.80, "Upper limit of the Omega invariant mass"}; + Configurable confRejectCompetingMassOmega{"confRejectCompetingMassOmega", false, "Switch to reject competing mass for Omega"}; } ConfCascadeSelection; // PHI @@ -349,6 +356,7 @@ struct FemtoUniverseProducerTask { Configurable confPhiKaonNsigmaTOFfrom055to15{"confPhiKaonNsigmaTOFfrom055to15", 3.0, "Reject if Kaons in 0.55-1.5 are have TOF n sigma above this value."}; Configurable confPhiKaonNsigmaTPCfrom15{"confPhiKaonNsigmaTPCfrom15", 3.0, "Reject if Kaons above 1.5 are have TPC n sigma above this value."}; Configurable confPhiKaonNsigmaTOFfrom15{"confPhiKaonNsigmaTOFfrom15", 3.0, "Reject if Kaons above 1.5 are have TOF n sigma above this value."}; + Configurable confDoLikeSign{"confDoLikeSign", false, "Enable like-sign (fake Phi) background calculation"}; } ConfPhiSelection; // PDG codes for fillMCParticle function @@ -376,68 +384,36 @@ struct FemtoUniverseProducerTask { Configurable confNsigmaCombinedParticle{"confNsigmaCombinedParticle", 3.0, "TPC and TOF Sigma (combined) for particle (track) momentum > Confmom"}; } ConfPIDBitmask; - HfHelper hfHelper; bool isKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) { - if (mom < ConfPhiSelection.confMomKaon03) { // 0.0-0.3 - if (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom00to03) { - return true; - } else { - return false; - } - } else if (mom < ConfPhiSelection.confMomKaon045) { // 0.30 - 0.45 - if (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom03to045) { - return true; - } else { - return false; - } - } else if (mom < ConfPhiSelection.confMomKaon055) { // 0.45-0.55 - if (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom045to055) { - return true; - } else { - return false; - } - } else if (mom < ConfPhiSelection.confMomKaon15) { // 0.55-1.5 (now we use TPC and TOF) - if ((std::abs(nsigmaTOFK) < ConfPhiSelection.confPhiKaonNsigmaTOFfrom055to15) && (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom055to15)) { - { - return true; - } - } else { - return false; - } - } else if (mom > ConfPhiSelection.confMomKaon15) { // 1.5 - - if ((std::abs(nsigmaTOFK) < ConfPhiSelection.confPhiKaonNsigmaTOFfrom15) && (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom15)) { - return true; - } else { - return false; - } - } else { - return false; + return (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom00to03); } + if (mom < ConfPhiSelection.confMomKaon045) { // 0.30 - 0.45 + return (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom03to045); + } + if (mom < ConfPhiSelection.confMomKaon055) { // 0.45-0.55 + return (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom045to055); + } + if (mom < ConfPhiSelection.confMomKaon15) { // 0.55-1.5 (now we use TPC and TOF) + return ((std::abs(nsigmaTOFK) < ConfPhiSelection.confPhiKaonNsigmaTOFfrom055to15) && (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom055to15)); + } + if (mom > ConfPhiSelection.confMomKaon15) { // 1.5 - + return ((std::abs(nsigmaTOFK) < ConfPhiSelection.confPhiKaonNsigmaTOFfrom15) && (std::abs(nsigmaTPCK) < ConfPhiSelection.confPhiKaonNsigmaTPCfrom15)); + } + return false; } bool isKaonNSigmaLF(float mom, float nsigmaTPCK, float nsigmaTOFK, bool hasTOF) { if (mom < ConfPhiSelection.confMomKaonLF) { - if (std::abs(nsigmaTPCK) < ConfPhiSelection.confNSigmaTPCKaonLF) { - return true; - } else { - return false; - } - } else if (mom >= ConfPhiSelection.confMomKaonLF) { // 0.5-1.5 (now we use TPC and TOF) - if (!hasTOF) { - return false; - } else { - if (std::sqrt(nsigmaTPCK * nsigmaTPCK + nsigmaTOFK * nsigmaTOFK) < ConfPhiSelection.confNSigmaCombKaonLF) { - return true; - } else { - return false; - } - } - } else { + return (std::abs(nsigmaTPCK) < ConfPhiSelection.confNSigmaTPCKaonLF); + } + // mom >= ConfPhiSelection.confMomKaonLF 0.5-1.5 (now we use TPC and TOF) + if (!hasTOF) { return false; } + return (std::sqrt(nsigmaTPCK * nsigmaTPCK + nsigmaTOFK * nsigmaTOFK) < ConfPhiSelection.confNSigmaCombKaonLF); } bool isKaonRejected(float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi) @@ -445,21 +421,20 @@ struct FemtoUniverseProducerTask { if (mom < ConfPhiSelection.confMomKaonRejected) { if (std::abs(nsigmaTPCPi) < ConfPhiSelection.confPhiKaonRejectPionNsigma.value) { return true; - } else if (std::abs(nsigmaTPCPr) < ConfPhiSelection.confPhiKaonRejectProtonNsigma.value) { + } + if (std::abs(nsigmaTPCPr) < ConfPhiSelection.confPhiKaonRejectProtonNsigma.value) { return true; } } if (mom > ConfPhiSelection.confMomKaonRejected) { if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfPhiSelection.confPhiKaonRejectPionNsigma.value) { return true; - } else if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfPhiSelection.confPhiKaonRejectProtonNsigma.value) { + } + if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfPhiSelection.confPhiKaonRejectProtonNsigma.value) { return true; - } else { - return false; } - } else { - return false; } + return false; } bool isNSigmaTPC(float nsigmaTPCParticle) @@ -472,84 +447,99 @@ struct FemtoUniverseProducerTask { // Cut only on daughter and bachelor tracks, that have TOF signal if (mom > ConfPIDBitmask.confMinMomTOF && hasTOF) { return (std::abs(nsigmaTOFParticle) < ConfPIDBitmask.confNsigmaTOFParticleChild); - } else { - return true; } + return true; } bool isNSigmaCombined(float mom, float nsigmaTPCParticle, float nsigmaTOFParticle) { if (mom <= ConfPIDBitmask.confMinMomTOF) { return (std::abs(nsigmaTPCParticle) < ConfPIDBitmask.confNsigmaTPCParticle); - } else { - return (std::hypot(nsigmaTOFParticle, nsigmaTPCParticle) < ConfPIDBitmask.confNsigmaCombinedParticle); } + return (std::hypot(nsigmaTOFParticle, nsigmaTPCParticle) < ConfPIDBitmask.confNsigmaCombinedParticle); } template - aod::femtouniverseparticle::CutContainerType PIDBitmask(const TrackType& track) + aod::femtouniverseparticle::CutContainerType pidBitmask(const TrackType& track) { - static const o2::track::PID pids[] = {o2::track::PID::Proton, o2::track::PID::Pion, o2::track::PID::Kaon}; + static const std::array pids = {o2::track::PID::Proton, o2::track::PID::Pion, o2::track::PID::Kaon}; aod::femtouniverseparticle::CutContainerType mask = 0u; - for (UInt_t i = 0; i < 3; ++i) { - if (isNSigmaTPC(trackCuts.getNsigmaTPC(track, pids[i]))) + int size = 3; + for (int i = 0; i < size; ++i) { + if (isNSigmaTPC(trackCuts.getNsigmaTPC(track, pids[i]))) { mask |= (1u << i); - if (isNSigmaTOF(track.p(), trackCuts.getNsigmaTOF(track, pids[i]), track.hasTOF())) + } + if (isNSigmaTOF(track.p(), trackCuts.getNsigmaTOF(track, pids[i]), track.hasTOF())) { mask |= (8u << i); - if (isNSigmaCombined(track.p(), trackCuts.getNsigmaTPC(track, pids[i]), trackCuts.getNsigmaTOF(track, pids[i]))) + } + if (isNSigmaCombined(track.p(), trackCuts.getNsigmaTPC(track, pids[i]), trackCuts.getNsigmaTOF(track, pids[i]))) { mask |= (64u << i); + } } - if (track.hasTOF()) + if (track.hasTOF()) { mask |= (512u); + } return mask; } template - using hasStrangeTOFinV0 = decltype(std::declval().tofNSigmaLaPr()); + using HasStrangeTOFinV0 = decltype(std::declval().tofNSigmaLaPr()); /// bitmask to save strangeness TOF for V0 analysis template - aod::femtouniverseparticle::CutContainerType PIDStrangeTOFBitmaskV0(const V0Type& v0) + aod::femtouniverseparticle::CutContainerType pidStrangeTOFBitmaskV0(const V0Type& v0) { aod::femtouniverseparticle::CutContainerType mask = 0u; - if constexpr (std::experimental::is_detected::value) { - if (v0.tofNSigmaLaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) + if constexpr (std::experimental::is_detected::value) { + if (v0.tofNSigmaLaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (1u); - if (v0.tofNSigmaLaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (v0.tofNSigmaLaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (2u); - if (v0.tofNSigmaALaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (v0.tofNSigmaALaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (4u); - if (v0.tofNSigmaALaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (v0.tofNSigmaALaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (8u); - if (v0.tofNSigmaK0PiPlus() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (v0.tofNSigmaK0PiPlus() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (16u); - if (v0.tofNSigmaK0PiMinus() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (v0.tofNSigmaK0PiMinus() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (32u); + } } return mask; } template - using hasStrangeTOFinCasc = decltype(std::declval().tofNSigmaXiLaPi()); + using HasStrangeTOFinCasc = decltype(std::declval().tofNSigmaXiLaPi()); /// bitmask to save strangeness TOF for cascade analysis template - aod::femtouniverseparticle::CutContainerType PIDStrangeTOFBitmaskCasc(const CascType& casc) + aod::femtouniverseparticle::CutContainerType pidStrangeTOFBitmaskCasc(const CascType& casc) { aod::femtouniverseparticle::CutContainerType mask = 0u; - if constexpr (std::experimental::is_detected::value) { - if (casc.tofNSigmaXiLaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) + if constexpr (std::experimental::is_detected::value) { + if (casc.tofNSigmaXiLaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (1u); - if (casc.tofNSigmaXiLaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (casc.tofNSigmaXiLaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (2u); - if (casc.tofNSigmaXiPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (casc.tofNSigmaXiPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (4u); - if (casc.tofNSigmaOmLaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (casc.tofNSigmaOmLaPi() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (8u); - if (casc.tofNSigmaOmLaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (casc.tofNSigmaOmLaPr() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (16u); - if (casc.tofNSigmaOmKa() < ConfPIDBitmask.confNsigmaTOFParticleChild) + } + if (casc.tofNSigmaOmKa() < ConfPIDBitmask.confNsigmaTOFParticleChild) { mask |= (32u); + } } return mask; } @@ -563,9 +553,9 @@ struct FemtoUniverseProducerTask { void initZorro(aod::BCsWithTimestamps::iterator const& bc) { - if (mRunNumberZorro == bc.runNumber()) + if (mRunNumberZorro == bc.runNumber()) { return; - + } zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); zorro.populateHistRegistry(qaRegistry, bc.runNumber()); mRunNumberZorro = bc.runNumber(); @@ -579,22 +569,23 @@ struct FemtoUniverseProducerTask { // for now do not know why int mRunNumber = 0; - float mMagField; - Service ccdb; /// Accessing the CCDB - ctpRateFetcher mRateFetcher; // inspired by zdcSP.cxx in PWGLF + float mMagField = 0; + Service ccdb{}; /// Accessing the CCDB + ctpRateFetcher mRateFetcher; // inspired by zdcSP.cxx in PWGLF void init(InitContext&) { - if ((doprocessFullData || doprocessTrackPhiData || doprocessTrackData || doprocessTrackV0 || doprocessTrackCascadeData || doprocessTrackV0Cascade || doprocessTrackD0mesonData || doprocessTrackD0DataML || doprocessTrackCentRun2Data || doprocessTrackV0CentRun2Data || doprocessTrackCentRun3Data || doprocessV0CentRun3Data || doprocessCascadeCentRun3Data || doprocessTrackDataCentPP) == false && (doprocessFullMC || doprocessTrackMC || doprocessTrackMCTruth || doprocessTrackMCGen || doprocessTruthAndFullMCV0 || doprocessTrackD0MC || doprocessTruthAndFullMCCasc || doprocessFullMCCent || doprocessTrackCentRun3DataMC || doprocessTruthAndFullMCCentRun3 || doprocessTruthAndFullMCCentRun3V0) == false) { + if (!(doprocessFullData || doprocessTrackPhiData || doprocessTrackData || doprocessTrackV0 || doprocessTrackCascadeData || doprocessTrackV0Cascade || doprocessTrackD0mesonData || doprocessTrackD0DataML || doprocessTrackCentRun2Data || doprocessTrackV0CentRun2Data || doprocessTrackCentRun3Data || doprocessTrackCentRun3DataFullNSigma || doprocessV0CentRun3Data || doprocessCascadeCentRun3Data || doprocessTrackDataCentPP) && !(doprocessFullMC || doprocessTrackMC || doprocessTrackMCTruth || doprocessTrackMCGen || doprocessTruthAndFullMCV0 || doprocessTrackD0MC || doprocessTruthAndFullMCCasc || doprocessFullMCCent || doprocessTrackCentRun3DataMC || doprocessTruthAndFullMCCentRun3 || doprocessTruthAndFullMCCentRun3V0 || doprocessTruthAndFullMCCentRun3Casc)) { LOGF(fatal, "Neither processFullData nor processFullMC enabled. Please choose one."); } - if ((doprocessFullData || doprocessTrackPhiData || doprocessTrackData || doprocessTrackV0 || doprocessTrackCascadeData || doprocessTrackV0Cascade || doprocessTrackD0mesonData || doprocessTrackD0DataML || doprocessTrackCentRun2Data || doprocessTrackV0CentRun2Data || doprocessTrackCentRun3Data || doprocessV0CentRun3Data || doprocessCascadeCentRun3Data || doprocessTrackDataCentPP) == true && (doprocessFullMC || doprocessTrackMC || doprocessTrackMCTruth || doprocessTrackMCGen || doprocessTruthAndFullMCV0 || doprocessTrackD0MC || doprocessTruthAndFullMCCasc || doprocessFullMCCent || doprocessTrackCentRun3DataMC || doprocessTruthAndFullMCCentRun3 || doprocessTruthAndFullMCCentRun3V0) == true) { + if ((doprocessFullData || doprocessTrackPhiData || doprocessTrackData || doprocessTrackV0 || doprocessTrackCascadeData || doprocessTrackV0Cascade || doprocessTrackD0mesonData || doprocessTrackD0DataML || doprocessTrackCentRun2Data || doprocessTrackV0CentRun2Data || doprocessTrackCentRun3Data || doprocessTrackCentRun3DataFullNSigma || doprocessV0CentRun3Data || doprocessCascadeCentRun3Data || doprocessTrackDataCentPP) && (doprocessFullMC || doprocessTrackMC || doprocessTrackMCTruth || doprocessTrackMCGen || doprocessTruthAndFullMCV0 || doprocessTrackD0MC || doprocessTruthAndFullMCCasc || doprocessFullMCCent || doprocessTrackCentRun3DataMC || doprocessTruthAndFullMCCentRun3 || doprocessTruthAndFullMCCentRun3V0 || doprocessTruthAndFullMCCentRun3Casc)) { LOGF(fatal, "Cannot enable process Data and process MC at the same time. " "Please choose one."); } zorroSummary.setObject(zorro.getZorroSummary()); + rctChecker.init("CBT_hadronPID", false, true); colCuts.setCuts(ConfGeneral.confEvtZvtx, ConfGeneral.confEvtTriggerCheck, ConfGeneral.confEvtTriggerSel, ConfGeneral.confEvtOfflineCheck, confIsRun3, ConfGeneral.confCentFT0Min, ConfGeneral.confCentFT0Max); colCuts.init(&qaRegistry); @@ -702,15 +693,15 @@ struct FemtoUniverseProducerTask { cascadeCuts.setChildCuts(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildTPCnClsMin, femto_universe_track_selection::kTPCnClsMin, femto_universe_selection::kLowerLimit); cascadeCuts.setChildCuts(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildPIDnSigmaMax, femto_universe_track_selection::kPIDnSigmaMax, femto_universe_selection::kAbsUpperLimit); - // TODO cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kPosTrack, ConfV0Selection.confChildPIDspecies); cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kNegTrack, ConfV0Selection.confChildPIDspecies); cascadeCuts.setChildPIDSpecies(femto_universe_cascade_selection::kBachTrack, ConfCascadeSelection.confCascChildPIDspecies); // check if works correctly for bachelor track cascadeCuts.init(&cascadeQaRegistry); - // invmass cuts + // invmass cuts & competing mass rejection cascadeCuts.setInvMassLimits(ConfCascadeSelection.confXiInvMassLowLimit, ConfCascadeSelection.confOmegaInvMassLowLimit, ConfCascadeSelection.confXiInvMassUpLimit, ConfCascadeSelection.confOmegaInvMassUpLimit); + cascadeCuts.setRejectCompetingMassOmega(ConfCascadeSelection.confRejectCompetingMassOmega); } if (ConfGeneral.confIsActivatePhi) { @@ -730,12 +721,13 @@ struct FemtoUniverseProducerTask { } /// Function to retrieve the nominal magnetic field in kG (0.1T) and convert it directly to T - void getMagneticFieldTesla(aod::BCsWithTimestamps::iterator bc) + void getMagneticFieldTesla(aod::BCsWithTimestamps::iterator const& bc) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable // get magnetic field for run - if (mRunNumber == bc.runNumber()) + if (mRunNumber == bc.runNumber()) { return; + } auto timestamp = bc.timestamp(); float output = -999; @@ -769,7 +761,7 @@ struct FemtoUniverseProducerTask { void fillDebugParticle(ParticleType const& particle) { if constexpr (isTrackOrV0) { - if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { outputDebugParts(particle.sign(), (uint8_t)particle.tpcNClsFound(), particle.tpcNClsFindable(), (uint8_t)particle.tpcNClsCrossedRows(), @@ -805,7 +797,7 @@ struct FemtoUniverseProducerTask { -999., -999., -999., -999.); // QA for phi or D0/D0bar children } else if constexpr (isCasc) { - if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { outputDebugParts(-999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., particle.tofNSigmaXiLaPi(), particle.tofNSigmaXiLaPr(), particle.tofNSigmaXiPi(), particle.tofNSigmaOmLaPi(), @@ -838,17 +830,17 @@ struct FemtoUniverseProducerTask { -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius - particle.mlProbD0()[0], // getter decayVtxX - particle.mlProbD0()[1], // getter decayVtxY - particle.mlProbD0()[2], // getter decayVtxZ - -999.); // Additional info for D0/D0bar + HfHelper::yD0(particle), // getter transRadius + particle.mlProbD0()[0], // getter decayVtxX + particle.mlProbD0()[1], // getter decayVtxY + particle.mlProbD0()[2], // getter decayVtxZ + -999.); // Additional info for D0/D0bar } else if constexpr (isD0barML) { outputDebugParts(-999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius + HfHelper::yD0(particle), // getter transRadius particle.mlProbD0bar()[0], // getter decayVtxX particle.mlProbD0bar()[1], // getter decayVtxY particle.mlProbD0bar()[2], // getter decayVtxZ @@ -878,17 +870,17 @@ struct FemtoUniverseProducerTask { -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius - particle.mlProbD0()[0], // getter decayVtxX - particle.mlProbD0()[1], // getter decayVtxY - particle.mlProbD0()[2], // getter decayVtxZ - -999.); // Additional info for D0/D0bar + HfHelper::yD0(particle), // getter transRadius + particle.mlProbD0()[0], // getter decayVtxX + particle.mlProbD0()[1], // getter decayVtxY + particle.mlProbD0()[2], // getter decayVtxZ + -999.); // Additional info for D0/D0bar } else if constexpr (isD0barML) { outputDebugParts(particle.flagMcMatchRec(), -999., -999., -999., -999., -999., -999., -999., -999., originMcReco, -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., -999., - hfHelper.yD0(particle), // getter transRadius + HfHelper::yD0(particle), // getter transRadius particle.mlProbD0bar()[0], // getter decayVtxX particle.mlProbD0bar()[1], // getter decayVtxY particle.mlProbD0bar()[2], // getter decayVtxZ @@ -902,20 +894,22 @@ struct FemtoUniverseProducerTask { } template - int32_t getMotherPDG(ParticleType particle) + int32_t getMotherPDG(ParticleType const& particle) { if (particle.isPhysicalPrimary()) { return 0; - } else if (particle.has_mothers()) { - if (particle.getProcess() == 20 || particle.getProcess() == 23) { // treat particles from hadronic scattering (20, 23) as primary + } + if (particle.has_mothers()) { + int hadronElasticStatter = 20; + int hadronInelasticStatter = 23; + if (particle.getProcess() == hadronElasticStatter || particle.getProcess() == hadronInelasticStatter) { // treat particles from hadronic scattering (20, 23) as primary return 0; } auto motherparticlesMC = particle.template mothers_as(); - auto motherparticleMC = motherparticlesMC.front(); + auto const& motherparticleMC = motherparticlesMC.front(); return motherparticleMC.pdgCode(); - } else { - return 999; } + return 999; } template @@ -938,7 +932,7 @@ struct FemtoUniverseProducerTask { if (particleMC.isPhysicalPrimary()) { particleOrigin = aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary; } else if (!motherparticlesMC.empty()) { - auto motherparticleMC = motherparticlesMC.front(); + auto const& motherparticleMC = motherparticlesMC.front(); if (motherparticleMC.producedByGenerator()) { particleOrigin = checkDaughterType(fdparttype, motherparticleMC.pdgCode()); } else { @@ -971,7 +965,7 @@ struct FemtoUniverseProducerTask { } else { auto mothers = mcparticle.template mothers_as(); if (!mothers.empty()) { - auto mother = mothers.front(); + auto const& mother = mothers.front(); if (mother.producedByGenerator()) { particleOrigin = checkDaughterType(fdparttype, mother.pdgCode()); } else { @@ -1027,16 +1021,13 @@ struct FemtoUniverseProducerTask { phiOrigin = aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kFake; } - TLorentzVector part1Vec; - TLorentzVector part2Vec; - const auto mMassOne = o2::constants::physics::MassKPlus; // FIXME: Get from the PDG service of the common header const auto mMassTwo = o2::constants::physics::MassKMinus; // FIXME: Get from the PDG service of the common header - part1Vec.SetPtEtaPhiM(kaon1MC.pt(), kaon1MC.eta(), kaon1MC.phi(), mMassOne); - part2Vec.SetPtEtaPhiM(kaon2MC.pt(), kaon2MC.eta(), kaon2MC.phi(), mMassTwo); + ROOT::Math::LorentzVector> part1Vec(kaon1MC.pt(), kaon1MC.eta(), kaon1MC.phi(), mMassOne); + ROOT::Math::LorentzVector> part2Vec(kaon2MC.pt(), kaon2MC.eta(), kaon2MC.phi(), mMassTwo); - TLorentzVector sumVec(part1Vec); + ROOT::Math::LorentzVector> sumVec(part1Vec); sumVec += part2Vec; float phiEta = sumVec.Eta(); @@ -1053,6 +1044,9 @@ struct FemtoUniverseProducerTask { template bool fillCollisions(CollisionType const& col, TrackType const& tracks) { + if (ConfGeneral.confIsCheckRCTFlags && !rctChecker(col)) { + return false; + } const auto vtxZ = col.posZ(); float mult = 0; int multNtr = 0; @@ -1077,24 +1071,20 @@ struct FemtoUniverseProducerTask { return false; } - if (zorroMask.value != "") { + if (!zorroMask.value.empty()) { auto bc = col.template bc_as(); initZorro(bc); - if (!zorro.isSelected(col.template bc_as().globalBC())) + if (!zorro.isSelected(col.template bc_as().globalBC())) { return false; + } } - if (!ConfGeneral.confIsUsePileUp) { + if (!ConfGeneral.confIsUsePileUp || ((!ConfGeneral.confEvNoSameBunchPileup || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && (!ConfGeneral.confEvIsGoodZvtxFT0vsPV || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && (!ConfGeneral.confEvIsVertexITSTPC || col.selection_bit(aod::evsel::kIsVertexITSTPC)))) { outputCollision(vtxZ, mult, multNtr, confDoSpher ? colCuts.computeSphericity(col, tracks) : 2, mMagField); colCuts.fillQA(col); return true; - } else if ((!ConfGeneral.confEvNoSameBunchPileup || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && (!ConfGeneral.confEvIsGoodZvtxFT0vsPV || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && (!ConfGeneral.confEvIsVertexITSTPC || col.selection_bit(aod::evsel::kIsVertexITSTPC))) { - outputCollision(vtxZ, mult, multNtr, confDoSpher ? colCuts.computeSphericity(col, tracks) : 2, mMagField); - colCuts.fillQA(col); - return true; - } else { - return false; } + return false; } template @@ -1123,17 +1113,12 @@ struct FemtoUniverseProducerTask { if (!colCuts.isSelected(col)) { return false; } - if (!ConfGeneral.confIsUsePileUp) { + if (!ConfGeneral.confIsUsePileUp || ((!ConfGeneral.confEvNoSameBunchPileup || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && (!ConfGeneral.confEvIsGoodZvtxFT0vsPV || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && (!ConfGeneral.confEvIsVertexITSTPC || col.selection_bit(aod::evsel::kIsVertexITSTPC)))) { outputCollision(vtxZ, mult, multNtr, confDoSpher ? colCuts.computeSphericity(col, tracks) : 2, mMagField); colCuts.fillQA(col); return true; - } else if ((!ConfGeneral.confEvNoSameBunchPileup || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && (!ConfGeneral.confEvIsGoodZvtxFT0vsPV || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && (!ConfGeneral.confEvIsVertexITSTPC || col.selection_bit(aod::evsel::kIsVertexITSTPC))) { - outputCollision(vtxZ, mult, multNtr, confDoSpher ? colCuts.computeSphericity(col, tracks) : 2, mMagField); - colCuts.fillQA(col); - return true; - } else { - return false; } + return false; } template @@ -1170,10 +1155,9 @@ struct FemtoUniverseProducerTask { // particle candidates for such collisions if (!colCuts.isSelected(col)) { return false; - } else { - outputCollision(vtxZ, cent, multNtr, 2, mMagField); - return true; } + outputCollision(vtxZ, cent, multNtr, 2, mMagField); + return true; } template @@ -1192,22 +1176,20 @@ struct FemtoUniverseProducerTask { if (!colCuts.isSelectedRun3(col) || (occupancy < ConfGeneral.confTPCOccupancyMin || occupancy > ConfGeneral.confTPCOccupancyMax)) { return false; - } else { - if ((!ConfGeneral.confEvNoSameBunchPileup || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && - (!ConfGeneral.confEvIsGoodZvtxFT0vsPV || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && - (!ConfGeneral.confIsGoodITSLayersAll || col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) && - (!ConfGeneral.confNoCollInRofStandard || col.selection_bit(aod::evsel::kNoCollInRofStandard)) && - (!ConfGeneral.confNoHighMultCollInPrevRof || col.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) && - (!ConfGeneral.confEvIsVertexITSTPC || col.selection_bit(aod::evsel::kIsVertexITSTPC)) && - (!ConfGeneral.confNoCollInTimeRangeStandard || col.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) && - (!ConfGeneral.confNoITSROFrameBorder || col.selection_bit(aod::evsel::kNoITSROFrameBorder)) && - (!ConfGeneral.confNoTimeFrameBorder || col.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - outputCollision(vtxZ, cent, multNtr, 2, mMagField); - return true; - } else { - return false; - } } + if ((!ConfGeneral.confEvNoSameBunchPileup || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && + (!ConfGeneral.confEvIsGoodZvtxFT0vsPV || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && + (!ConfGeneral.confIsGoodITSLayersAll || col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) && + (!ConfGeneral.confNoCollInRofStandard || col.selection_bit(aod::evsel::kNoCollInRofStandard)) && + (!ConfGeneral.confNoHighMultCollInPrevRof || col.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) && + (!ConfGeneral.confEvIsVertexITSTPC || col.selection_bit(aod::evsel::kIsVertexITSTPC)) && + (!ConfGeneral.confNoCollInTimeRangeStandard || col.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) && + (!ConfGeneral.confNoITSROFrameBorder || col.selection_bit(aod::evsel::kNoITSROFrameBorder)) && + (!ConfGeneral.confNoTimeFrameBorder || col.selection_bit(aod::evsel::kNoTimeFrameBorder))) { + outputCollision(vtxZ, cent, multNtr, 2, mMagField); + return true; + } + return false; } template @@ -1217,10 +1199,9 @@ struct FemtoUniverseProducerTask { if (std::abs(vtxZ) > ConfGeneral.confEvtZvtx) { return false; - } else { - outputCollision(vtxZ, 0, 0, 2, mMagField); - return true; } + outputCollision(vtxZ, 0, 0, 2, mMagField); + return true; } template @@ -1268,7 +1249,7 @@ struct FemtoUniverseProducerTask { femto_universe_track_selection::TrackContainerPosition::kCuts), confIsUseCutculator ? cutContainer.at( femto_universe_track_selection::TrackContainerPosition::kPID) - : PIDBitmask(track), + : pidBitmask(track), track.dcaXY(), childIDs, track.hasTOF(), // hasTOF getter is mLambda() track.sign()); // sign getter is mAntiLambda() @@ -1277,13 +1258,76 @@ struct FemtoUniverseProducerTask { if (confIsDebug) { fillDebugParticle(track); } - if constexpr (isMC) { fillMCParticle(track, o2::aod::femtouniverseparticle::ParticleType::kTrack); } } } + template + void fillTracksFullNsigma(TrackType const& tracks) + { + std::vector childIDs = {0, 0}; + std::vector tmpIDtrack; + + for (const auto& track : tracks) { + if (!trackCuts.isSelectedMinimal(track)) { + continue; + } + if (track.tpcChi2NCl() < ConfTrkSelection.confTrkMinChi2PerClusterTPC || track.tpcChi2NCl() > ConfTrkSelection.confTrkMaxChi2PerClusterTPC) { + continue; + } + if (ConfTrkSelection.confTrkTPCRefit && !track.hasTPC()) { + continue; + } + if (track.pt() > confTOFpTmin) { + if (!track.hasTOF()) { + continue; + } + } + + trackCuts.fillQA(track); + // the bit-wise container of the systematic variations is obtained + auto cutContainer = trackCuts.getCutContainer(track); + + // now the table is filled + outputParts(outputCollision.lastIndex(), track.pt(), track.eta(), + track.phi(), aod::femtouniverseparticle::ParticleType::kTrack, + cutContainer.at( + femto_universe_track_selection::TrackContainerPosition::kCuts), + confIsUseCutculator ? cutContainer.at( + femto_universe_track_selection::TrackContainerPosition::kPID) + : pidBitmask(track), + track.dcaXY(), childIDs, + track.hasTOF(), // hasTOF getter is mLambda() + track.sign()); // sign getter is mAntiLambda() + + tmpIDtrack.push_back(track.globalIndex()); + + outputDebugParts(track.sign(), (uint8_t)track.tpcNClsFound(), + track.tpcNClsFindable(), + (uint8_t)track.tpcNClsCrossedRows(), + track.tpcNClsShared(), track.tpcFractionSharedCls(), track.tpcInnerParam(), + track.itsNCls(), track.itsNClsInnerBarrel(), + track.dcaXY(), track.dcaZ(), track.tpcSignal(), + -999., -999., -999., -999., -999., -999., -999., -999., + -999., -999., -999., -999., -999., -999., -999., -999.); + + outputDebugSigmaParts(track.tofNSigmaEl(), track.tofNSigmaPi(), + track.tofNSigmaKa(), track.tofNSigmaPr(), + track.tofNSigmaDe(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.tpcNSigmaKa(), track.tpcNSigmaPr(), + track.tpcNSigmaDe()); + + if constexpr (fillITS) { + outputDebugITSParts(track.itsNSigmaEl(), track.itsNSigmaPi(), + track.itsNSigmaKa(), track.itsNSigmaPr(), + track.itsNSigmaDe()); + } + } + } + template void fillTracksITSTPCTOF(TrackType const& tracks) { @@ -1304,7 +1348,10 @@ struct FemtoUniverseProducerTask { if (track.itsChi2NCl() > ConfTrkSelection.confTrkMaxChi2PerClusterITS) { continue; } - if ((ConfTrkSelection.confTrkTPCRefit && !track.hasTPC()) || (ConfTrkSelection.confTrkITSRefit && !track.hasITS())) { + if (ConfTrkSelection.confTrkTPCRefit && !track.hasTPC()) { + continue; + } + if (ConfTrkSelection.confTrkITSRefit && track.pt() < confTOFpTmin && !track.hasITS()) { continue; } @@ -1378,8 +1425,8 @@ struct FemtoUniverseProducerTask { v0.positiveeta(), v0.positivephi(), aod::femtouniverseparticle::ParticleType::kV0Child, cutContainerV0.at(femto_universe_v0_selection::V0ContainerPosition::kPosCuts), - confIsUseCutculator ? cutContainerV0.at(femto_universe_v0_selection::V0ContainerPosition::kPosPID) : PIDBitmask(postrack), - 0., + confIsUseCutculator ? cutContainerV0.at(femto_universe_v0_selection::V0ContainerPosition::kPosPID) : pidBitmask(postrack), + postrack.dcaXY(), childIDs, 0, postrack.sign()); @@ -1398,8 +1445,8 @@ struct FemtoUniverseProducerTask { v0.negativephi(), aod::femtouniverseparticle::ParticleType::kV0Child, cutContainerV0.at(femto_universe_v0_selection::V0ContainerPosition::kNegCuts), - confIsUseCutculator ? cutContainerV0.at(femto_universe_v0_selection::V0ContainerPosition::kNegPID) : PIDBitmask(negtrack), - 0., + confIsUseCutculator ? cutContainerV0.at(femto_universe_v0_selection::V0ContainerPosition::kNegPID) : pidBitmask(negtrack), + negtrack.dcaXY(), childIDs, 0, negtrack.sign()); @@ -1414,7 +1461,7 @@ struct FemtoUniverseProducerTask { v0.phi(), aod::femtouniverseparticle::ParticleType::kV0, cutContainerV0.at(femto_universe_v0_selection::V0ContainerPosition::kV0), - PIDStrangeTOFBitmaskV0(v0), + pidStrangeTOFBitmaskV0(v0), v0.v0cosPA(), indexChildID, v0.mLambda(), @@ -1434,8 +1481,9 @@ struct FemtoUniverseProducerTask { void fillV0MCTruth(MCParticlesType const& mcParticles) { for (const auto& mc : mcParticles) { // Loop over all MC Truth particles - if (mc.pdgCode() != ConfV0Selection.confV0PDGMCTruth.value[2]) + if (mc.pdgCode() != ConfV0Selection.confV0PDGMCTruth.value[2]) { continue; // Artificially single out V0s + } auto daughters = mc.template daughters_as(); // Access daughters (no differentiation of signs, it needs to be checked separately) @@ -1481,8 +1529,9 @@ struct FemtoUniverseProducerTask { } } - if (!foundPos || !foundNeg) + if (!foundPos || !foundNeg) { continue; + } childIDs[0] = rowPos; childIDs[1] = rowNeg; @@ -1564,7 +1613,7 @@ struct FemtoUniverseProducerTask { casc.positivephi(), aod::femtouniverseparticle::ParticleType::kV0Child, 0, - PIDBitmask(posTrackCasc), + pidBitmask(posTrackCasc), hasTOF, childIDs, 0, @@ -1586,7 +1635,7 @@ struct FemtoUniverseProducerTask { casc.negativephi(), aod::femtouniverseparticle::ParticleType::kV0Child, 0, - PIDBitmask(negTrackCasc), + pidBitmask(negTrackCasc), hasTOF, childIDs, 0, @@ -1609,7 +1658,7 @@ struct FemtoUniverseProducerTask { casc.bachelorphi(), aod::femtouniverseparticle::ParticleType::kCascadeBachelor, 0, - PIDBitmask(bachTrackCasc), + pidBitmask(bachTrackCasc), hasTOF, childIDs, 0, @@ -1626,7 +1675,7 @@ struct FemtoUniverseProducerTask { casc.phi(), aod::femtouniverseparticle::ParticleType::kCascade, 0, - PIDStrangeTOFBitmaskCasc(casc), + pidStrangeTOFBitmaskCasc(casc), 0, indexCascChildID, casc.mXi(), @@ -1671,7 +1720,7 @@ struct FemtoUniverseProducerTask { continue; } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -1689,18 +1738,18 @@ struct FemtoUniverseProducerTask { auto negtrack = hfCand.template prong1_as(); if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -1794,7 +1843,7 @@ struct FemtoUniverseProducerTask { continue; } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -1811,18 +1860,18 @@ struct FemtoUniverseProducerTask { auto negtrack = hfCand.template prong1_as(); if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -1926,7 +1975,7 @@ struct FemtoUniverseProducerTask { } } - if (ConfD0Selection.useYCutD0Cand && std::abs(hfHelper.yD0(hfCand)) > ConfD0Selection.yD0CandMax) { + if (ConfD0Selection.useYCutD0Cand && std::abs(HfHelper::yD0(hfCand)) > ConfD0Selection.yD0CandMax) { continue; } @@ -1952,18 +2001,18 @@ struct FemtoUniverseProducerTask { childIDs[1] = 0; if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 0) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = -hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = -HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = 1; } else if (hfCand.isSelD0() == 0 && hfCand.isSelD0bar() == 1) { - invMassD0 = -hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = -HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); isD0D0bar = true; daughFlag = -1; } else if (hfCand.isSelD0() == 1 && hfCand.isSelD0bar() == 1) { - invMassD0 = hfHelper.invMassD0ToPiK(hfCand); - invMassD0bar = hfHelper.invMassD0barToKPi(hfCand); + invMassD0 = HfHelper::invMassD0ToPiK(hfCand); + invMassD0bar = HfHelper::invMassD0barToKPi(hfCand); if (ConfD0Selection.storeD0D0barDoubleMassHypo) { isD0D0bar = true; daughFlag = 0; @@ -2066,7 +2115,7 @@ struct FemtoUniverseProducerTask { } // implementing PID cuts for phi children if (ConfPhiSelection.confPhiDoLFPID4Kaons) { - if ((p1.isGlobalTrackWoDCA() == false) || (p2.isGlobalTrackWoDCA() == false) || (p1.isPVContributor() == false) || (p2.isPVContributor() == false)) { + if (!p1.isGlobalTrackWoDCA() || !p2.isGlobalTrackWoDCA() || !p1.isPVContributor() || p2.isPVContributor()) { continue; } if (!(isKaonNSigmaLF(p1.pt(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), p1.hasTOF()))) { @@ -2090,16 +2139,13 @@ struct FemtoUniverseProducerTask { continue; } - TLorentzVector part1Vec; - TLorentzVector part2Vec; - const auto mMassOne = o2::constants::physics::MassKPlus; // FIXME: Get from the PDG service of the common header const auto mMassTwo = o2::constants::physics::MassKMinus; // FIXME: Get from the PDG service of the common header - part1Vec.SetPtEtaPhiM(p1.pt(), p1.eta(), p1.phi(), mMassOne); - part2Vec.SetPtEtaPhiM(p2.pt(), p2.eta(), p2.phi(), mMassTwo); + ROOT::Math::LorentzVector> part1Vec(p1.pt(), p1.eta(), p1.phi(), mMassOne); + ROOT::Math::LorentzVector> part2Vec(p2.pt(), p2.eta(), p2.phi(), mMassTwo); - TLorentzVector sumVec(part1Vec); + ROOT::Math::LorentzVector> sumVec(part1Vec); sumVec += part2Vec; float phiEta = sumVec.Eta(); @@ -2115,8 +2161,9 @@ struct FemtoUniverseProducerTask { float phiPhi = RecoDecay::constrainAngle(sumVec.Phi(), 0); float phiM = sumVec.M(); - if ((phiM < ConfPhiSelection.confPhiInvMassLowLimit) || (phiM > ConfPhiSelection.confPhiInvMassUpLimit)) + if ((phiM < ConfPhiSelection.confPhiInvMassLowLimit) || (phiM > ConfPhiSelection.confPhiInvMassUpLimit)) { continue; + } phiCuts.fillQA(col, p1, p1, p2, 321, -321); ///\todo fill QA also for daughters @@ -2184,6 +2231,156 @@ struct FemtoUniverseProducerTask { } } + template + void fillLikeSignPhi(CollisionType const& col, TrackType const& tracks) + { + std::vector childIDs = {0, 0}; + std::vector tmpIDtrack; + + // Loop over all track combinations using FullIndexPolicy + for (const auto& [p1, p2] : combinations(soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + // 1. Check minimal track quality cuts for both tracks + if (!trackCuts.isSelectedMinimal(p1) || !trackCuts.isSelectedMinimal(p2)) { + continue; + } + + if (p1.sign() != p2.sign()) { // K+K- pairs are already handled in fillPhi, so we skip them here, only K+K+ and K-K- + continue; + } + + // avoid double counting and self-pairing when using FullIndexPolicy + if (p1.globalIndex() >= p2.globalIndex()) { + continue; + } + + // cuts for daughters + if (ConfPhiSelection.confPhiDoLFPID4Kaons) { + if (!p1.isGlobalTrackWoDCA() || !p2.isGlobalTrackWoDCA() || !p1.isPVContributor() || !p2.isPVContributor()) { + continue; + } + if (!(isKaonNSigmaLF(p1.pt(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), p1.hasTOF()))) { + continue; + } + if (!(isKaonNSigmaLF(p2.pt(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), p2.hasTOF()))) { + continue; + } + } else { + if (!(isKaonNSigma(p1.pt(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon)))) { + continue; + } + if (!(isKaonNSigma(p2.pt(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon)))) { + continue; + } + } + + // Reject protons and pions contamination + if (isKaonRejected(p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion))) { + continue; + } + if (isKaonRejected(p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion))) { + continue; + } + + const auto mMassOne = o2::constants::physics::MassKPlus; + const auto mMassTwo = o2::constants::physics::MassKPlus; + + ROOT::Math::LorentzVector> part1Vec(p1.pt(), p1.eta(), p1.phi(), mMassOne); + ROOT::Math::LorentzVector> part2Vec(p2.pt(), p2.eta(), p2.phi(), mMassTwo); + + ROOT::Math::LorentzVector> sumVec(part1Vec); + sumVec += part2Vec; + + // kinematic cuts for phi candidate + float phiEta = sumVec.Eta(); + if (std::abs(phiEta) > ConfPhiSelection.confPhiEtaHighLimit.value) { + continue; + } + + float phiPt = sumVec.Pt(); + if ((phiPt < ConfPhiSelection.confPhiPtLowLimit.value) || (phiPt > ConfPhiSelection.confPhiPtHighLimit.value)) { + continue; + } + + float phiPhi = RecoDecay::constrainAngle(sumVec.Phi(), 0); + float phiM = sumVec.M(); + + if ((phiM < ConfPhiSelection.confPhiInvMassLowLimit) || (phiM > ConfPhiSelection.confPhiInvMassUpLimit)) { + continue; + } + + phiCuts.fillQA(col, p1, p1, p2, 321, 321); + + int postrackID = p1.globalIndex(); + int rowInPrimaryTrackTablePos = -1; + rowInPrimaryTrackTablePos = getRowDaughters(postrackID, tmpIDtrack); + childIDs[0] = rowInPrimaryTrackTablePos; + childIDs[1] = 0; + + // first daughter track + outputParts(outputCollision.lastIndex(), p1.pt(), + p1.eta(), p1.phi(), + aod::femtouniverseparticle::ParticleType::kPhiChild, + -999, + -999, + p1.dcaXY(), + childIDs, + 0, + p1.sign()); + const int rowOfPosTrack = outputParts.lastIndex(); + if constexpr (isMC) { + fillMCParticle(p1, o2::aod::femtouniverseparticle::ParticleType::kPhiChild); + } + + int negtrackID = p2.globalIndex(); + int rowInPrimaryTrackTableNeg = -1; + rowInPrimaryTrackTableNeg = getRowDaughters(negtrackID, tmpIDtrack); + childIDs[0] = 0; + childIDs[1] = rowInPrimaryTrackTableNeg; + + // second daughter track + outputParts(outputCollision.lastIndex(), + p2.pt(), + p2.eta(), + p2.phi(), + aod::femtouniverseparticle::ParticleType::kPhiChild, + -999, + -999, + p2.dcaXY(), + childIDs, + 0, + p2.sign()); + const int rowOfNegTrack = outputParts.lastIndex(); + if constexpr (isMC) { + fillMCParticle(p2, o2::aod::femtouniverseparticle::ParticleType::kPhiChild); + } + + std::vector indexChildID = {rowOfPosTrack, rowOfNegTrack}; + + // Define like-sign flag: +1 for (K+K+), -1 for (K-K-) + int likeSignFlag = p1.sign(); + + // Save the Fake Phi candidate from K+K+ or K-K- combination + outputParts(outputCollision.lastIndex(), + phiPt, + phiEta, + phiPhi, + aod::femtouniverseparticle::ParticleType::kPhi, + likeSignFlag, // Flag: +1 for K+K+, -1 for K-K-, before it was empty slot for cutcontainer + 0, + phiM, // Invariant mass of the fake Phi candidate + indexChildID, + phiM, + 0); + + if (confIsDebug) { + fillDebugParticle(p1); // QA for first daughter + fillDebugParticle(p2); // QA for second daughter + fillDebugParticle(p1); // QA for fake Phi mother + } + } + } + template void fillParticles(TrackType const& tracks, std::optional>> recoMcIds = std::nullopt) { @@ -2194,30 +2391,32 @@ struct FemtoUniverseProducerTask { /// if the most open selection criteria are not fulfilled there is no /// point looking further at the track - if (particle.eta() < -ConfFilterCuts.confEtaFilterCut || particle.eta() > ConfFilterCuts.confEtaFilterCut) + if (particle.eta() < -ConfFilterCuts.confEtaFilterCut || particle.eta() > ConfFilterCuts.confEtaFilterCut) { continue; - if (particle.pt() < ConfFilterCuts.confPtLowFilterCut || particle.pt() > ConfFilterCuts.confPtHighFilterCut) + } + if (particle.pt() < ConfFilterCuts.confPtLowFilterCut || particle.pt() > ConfFilterCuts.confPtHighFilterCut) { continue; + } - uint32_t pdgCode = static_cast(particle.pdgCode()); + auto pdgCode = static_cast(particle.pdgCode()); if (ConfGeneral.confMCTruthAnalysisWithPID) { bool pass = false; std::vector tmpPDGCodes = ConfGeneral.confMCTruthPDGCodes; // necessary due to some features of the Configurable for (auto const& pdg : tmpPDGCodes) { if (static_cast(pdg) == static_cast(pdgCode)) { - if (pdgCode == Pdg::kPhi) { // && (recoMcIds && recoMcIds->get().contains(particle.globalIndex()))) { // ATTENTION: all Phi mesons are NOT primary particles - pass = true; - } else if (pdgCode == Pdg::kD0) { + if (pdgCode == Pdg::kPhi || pdgCode == Pdg::kD0) { // && (recoMcIds && recoMcIds->get().contains(particle.globalIndex()))) { // ATTENTION: all Phi mesons are NOT primary particles pass = true; } else { - if (confStoreMCmothers || particle.isPhysicalPrimary() || (ConfGeneral.confActivateSecondaries && recoMcIds && recoMcIds->get().contains(particle.globalIndex()))) + if (confStoreMCmothers || particle.isPhysicalPrimary() || (ConfGeneral.confActivateSecondaries && recoMcIds && recoMcIds->get().contains(particle.globalIndex()))) { pass = true; + } } } } - if (!pass) + if (!pass) { continue; + } } // we cannot use isSelectedMinimal since it takes Ncls @@ -2240,8 +2439,9 @@ struct FemtoUniverseProducerTask { continue; } - if (ConfGeneral.confIsActivateCascade) + if (ConfGeneral.confIsActivateCascade) { childIDs.push_back(0); + } outputParts(outputCollision.lastIndex(), particle.pt(), particle.eta(), @@ -2318,12 +2518,12 @@ struct FemtoUniverseProducerTask { void fillMCTruthParticlesD0(TrackType const& mcParts) { std::vector childIDs = {0, 0}; // these IDs are necessary to keep track of the children - std::vector tmpIDtrack; for (const auto& particle : mcParts) { - if (particle.pt() < ConfD0Selection.trackD0pTGenMin || particle.pt() > ConfD0Selection.trackD0pTGenMax) + if (particle.pt() < ConfD0Selection.trackD0pTGenMin || particle.pt() > ConfD0Selection.trackD0pTGenMax) { continue; + } int pdgCode = particle.pdgCode(); @@ -2331,19 +2531,19 @@ struct FemtoUniverseProducerTask { bool pass = false; std::vector tmpPDGCodes = ConfGeneral.confMCTruthPDGCodes; // necessary due to some features of the Configurable for (auto const& pdg : tmpPDGCodes) { - if (static_cast(pdg) == static_cast(pdgCode)) { - if (pdgCode == Pdg::kPhi) { - pass = true; - } else if (std::abs(pdgCode) == Pdg::kD0) { + if (static_cast(pdg) == pdgCode) { + if (pdgCode == Pdg::kPhi || std::abs(pdgCode) == Pdg::kD0) { pass = true; } else { - if (particle.isPhysicalPrimary()) + if (particle.isPhysicalPrimary()) { pass = true; + } } } } - if (!pass) + if (!pass) { continue; + } } /// check if we end-up with the correct final state using MC info @@ -2358,10 +2558,9 @@ struct FemtoUniverseProducerTask { if (std::abs(particle.pdgCode()) == Pdg::kD0) { if (std::abs(particle.y()) > ConfD0Selection.yD0CandGenMax) { continue; - } else { - origin = RecoDecay::getCharmHadronOrigin(mcParts, particle); - mcGenFlag = particle.flagMcMatchGen(); } + origin = RecoDecay::getCharmHadronOrigin(mcParts, particle); + mcGenFlag = particle.flagMcMatchGen(); } else { if (std::abs(particle.eta()) > ConfD0Selection.trackD0CandEtaMax) { continue; @@ -2405,6 +2604,9 @@ struct FemtoUniverseProducerTask { } if (ConfGeneral.confIsActivatePhi) { fillPhi(col, tracks); + if (ConfPhiSelection.confDoLikeSign) { + fillLikeSignPhi(col, tracks); + } } } return colcheck; @@ -2581,6 +2783,9 @@ struct FemtoUniverseProducerTask { if (colcheck) { fillTracks(tracks); fillPhi(col, tracks); + if (ConfPhiSelection.confDoLikeSign) { + fillLikeSignPhi(col, tracks); + } } } PROCESS_SWITCH(FemtoUniverseProducerTask, processTrackPhiData, @@ -2667,32 +2872,33 @@ struct FemtoUniverseProducerTask { for (const auto& col : collisions) { auto groupedTracks = tracks.sliceBy(perCollisionTracks, col.globalIndex()); - auto groupedStrageParts = strangeParts.sliceBy(ps, col.globalIndex()); + auto groupedStrangeParts = strangeParts.sliceBy(ps, col.globalIndex()); getMagneticFieldTesla(col.bc_as()); if constexpr (std::experimental::is_detected::value) { const auto colcheck = fillCollisions(col, groupedTracks); if (colcheck) { mcColIds.insert(col.mcCollisionId()); fillTracks(groupedTracks); - fillCascade(col, groupedStrageParts, groupedTracks); + fillCascade(col, groupedStrangeParts, groupedTracks); } } else { - const auto colcheck = fillCollisionsAndTracksAndV0AndPhi(col, groupedTracks, groupedStrageParts); + const auto colcheck = fillCollisionsAndTracksAndV0AndPhi(col, groupedTracks, groupedStrangeParts); if (colcheck) { mcColIds.insert(col.mcCollisionId()); } } for (const auto& track : groupedTracks) { - if (trackCuts.isSelectedMinimal(track)) + if (trackCuts.isSelectedMinimal(track)) { recoMcIds.insert(track.mcParticleId()); + } } } // truth for (const auto& mccol : mccols) { auto groupedMCParticles = mcParticles.sliceBy(perMCCollision, mccol.globalIndex()); - if (confCollMCTruthOnlyReco && !mcColIds.contains(mccol.globalIndex())) { - continue; + if (confCollMCTruthOnlyReco && !mcColIds.contains(mccol.globalIndex())) { /// For strange particle correlation, one can consider all MC truth collisions i.e. + continue; /// confCollMCTruthOnlyReco is false. } auto groupedCollisions = collisions.sliceBy(recoCollsPerMCColl, mccol.globalIndex()); fillMCTruthCollisions(groupedCollisions, groupedMCParticles); // fills the reco collisions for mc collision @@ -2729,8 +2935,9 @@ struct FemtoUniverseProducerTask { fillTracks(groupedTracks); } for (const auto& track : groupedTracks) { - if (trackCuts.isSelectedMinimal(track)) + if (trackCuts.isSelectedMinimal(track)) { recoMcIds.insert(track.mcParticleId()); + } } } @@ -2793,28 +3000,37 @@ struct FemtoUniverseProducerTask { // recos std::set recoMcIds; + std::set mcColIds; + recoMcIds.clear(); + mcColIds.clear(); + for (const auto& col : collisions) { auto groupedTracks = tracks.sliceBy(perCollisionTracks, col.globalIndex()); auto groupedCascParts = fullCascades.sliceBy(perCollisionCascs, col.globalIndex()); getMagneticFieldTesla(col.bc_as()); const auto colcheck = fillCollisionsCentRun3(col); if (colcheck) { + mcColIds.insert(col.mcCollisionId()); fillTracks(groupedTracks); fillCascade(col, groupedCascParts, groupedTracks); } for (const auto& track : groupedTracks) { - if (trackCuts.isSelectedMinimal(track)) + if (trackCuts.isSelectedMinimal(track)) { recoMcIds.insert(track.mcParticleId()); + } } } // truth for (const auto& mccol : mccols) { - auto groupedCollisions = collisions.sliceBy(recoCollsPerMCCollCentPbPb, mccol.globalIndex()); + if (confCollMCTruthOnlyReco && !mcColIds.contains(mccol.globalIndex())) { + continue; + } + auto groupedCollisions = collisions.sliceBy(recoCollsPerMCCollCentPbPb, mccol.globalIndex()); // slicing for MC collisions + auto groupedMCParticles = mcParticles.sliceBy(perMCCollision, mccol.globalIndex()); // slicing for MC particles for (const auto& col : groupedCollisions) { const auto colcheck = fillMCTruthCollisionsCentRun3(col); // fills the reco collisions for mc collision if (colcheck) { - auto groupedMCParticles = mcParticles.sliceBy(perMCCollision, mccol.globalIndex()); outputCollExtra(1.0, 1.0); fillParticles(groupedMCParticles, recoMcIds); // fills mc particles } @@ -2833,6 +3049,8 @@ struct FemtoUniverseProducerTask { aod::BCsWithTimestamps const&, aod::McParticles const& mcParts) { + std::set mcColIds; + mcColIds.clear(); // MC Reco for (const auto& col : collisions) { auto groupedTracks = tracks.sliceBy(perCollisionTracks, col.globalIndex()); @@ -2842,12 +3060,16 @@ struct FemtoUniverseProducerTask { // fill the tables const auto colcheck = fillCollisions(col, tracks); if (colcheck) { + mcColIds.insert(col.mcCollisionId()); fillTracks(groupedTracks); fillD0D0barMcMl(col, groupedTracks, groupedD0s, mcParts); } } // MC Truth for (const auto& mccol : mccols) { + if (confCollMCTruthOnlyReco && !mcColIds.contains(mccol.globalIndex())) { + continue; + } auto groupedMCParticles = hfMcGenCands.sliceBy(mcPartPerMcColl, mccol.globalIndex()); auto groupedCollisions = collisions.sliceBy(recoCollsPerMCColl, mccol.globalIndex()); fillMCTruthCollisions(groupedCollisions, groupedMCParticles); // fills the reco collisions for mc collision @@ -2938,6 +3160,27 @@ struct FemtoUniverseProducerTask { } PROCESS_SWITCH(FemtoUniverseProducerTask, processTrackCentRun3Data, "Provide experimental data for Run 3 with centrality for track track", false); + void processTrackCentRun3DataFullNSigma(aod::FemtoFullCollisionCentRun3 const& col, + soa::Filtered> const& tracksNsigma) + { + + auto tracksWithItsPid = soa::Attach>, + aod::pidits::ITSNSigmaEl, aod::pidits::ITSNSigmaPi, aod::pidits::ITSNSigmaKa, aod::pidits::ITSNSigmaPr, aod::pidits::ITSNSigmaDe, aod::pidits::ITSNSigmaTr, aod::pidits::ITSNSigmaHe>(tracksNsigma); + + // fill the tables + const auto colcheck = fillCollisionsCentRun3(col); + if (colcheck) { + if (!confFillITSPid) { + fillTracksFullNsigma(tracksNsigma); + } else { + fillTracksFullNsigma(tracksWithItsPid); + } + } + } + PROCESS_SWITCH(FemtoUniverseProducerTask, processTrackCentRun3DataFullNSigma, "Provide experimental data for Run 3 with centrality for track track with full NSigma information", false); + void processTrackCentRun3DataMC(aod::FemtoFullCollisionCentRun3MC const& col, aod::BCsWithTimestamps const&, soa::Join const& tracks, @@ -3001,8 +3244,9 @@ struct FemtoUniverseProducerTask { fillV0(col, groupedV0Parts, groupedTracks); } for (const auto& track : groupedTracks) { - if (trackCuts.isSelectedMinimal(track)) + if (trackCuts.isSelectedMinimal(track)) { recoMcIds.insert(track.mcParticleId()); + } } } diff --git a/PWGCF/FemtoUniverse/Tasks/CMakeLists.txt b/PWGCF/FemtoUniverse/Tasks/CMakeLists.txt index 346188d08fd..607386c1d4b 100644 --- a/PWGCF/FemtoUniverse/Tasks/CMakeLists.txt +++ b/PWGCF/FemtoUniverse/Tasks/CMakeLists.txt @@ -44,11 +44,16 @@ o2physics_add_dpl_workflow(femtouniverse-pair-track-track-threedrelmom-mult-kt-e PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(o2-analysis-cf-femtouniverse-pair-track-track-spherhar-mult-kt-extended +o2physics_add_dpl_workflow(femto-universe-pair-task-track-track-spher-har-mult-kt-extended SOURCES femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-universe-pair-task-track-track-spher-har-mult-kt-extended-its-pid + SOURCES femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femtouniverse-debug-track SOURCES femtoUniverseDebugTrack.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -107,4 +112,4 @@ o2physics_add_dpl_workflow(femtouniverse-efficiency-base o2physics_add_executable(femtouniverse-cutculator SOURCES femtoUniverseCutCulator.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore - COMPONENT_NAME Analysis) + COMPONENT_NAME Analysis) \ No newline at end of file diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniverseEfficiencyBase.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniverseEfficiencyBase.cxx index 96a1a63e56a..abc3036d1b0 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniverseEfficiencyBase.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniverseEfficiencyBase.cxx @@ -47,12 +47,13 @@ using namespace o2::soa; struct FemtoUniverseEfficiencyBase { SliceCache cache; - using FemtoFullParticles = soa::Join; + using FemtoFullParticles = soa::Join; Preslice perCol = aod::femtouniverseparticle::fdCollisionId; Configurable confIsDebug{"confIsDebug", true, "Enable debug histograms"}; Configurable confIsMCGen{"confIsMCGen", false, "Enable QA histograms for MC Gen"}; Configurable confIsMCReco{"confIsMCReco", false, "Enable QA histograms for MC Reco"}; + Configurable fillSecTrkContHistos{"fillSecTrkContHistos", false, "Enable histograms for secondary track contamination"}; Configurable confDoPartNsigmaRejection{"confDoPartNsigmaRejection", false, "Enable particle nSigma rejection"}; Configurable forceTof{"forceTof", false, "Enable to reject tracks without TOF for PID, set to false for processes with V0"}; @@ -67,6 +68,7 @@ struct FemtoUniverseEfficiencyBase { /// Particle selection part /// Configurables for both particles + ConfigurableAxis confHistCutsVsPtBins{"confHistCutsVsPtBins", {500, 0., 5.}, "Binning of the pT in the Cuts Vs pT plot"}; ConfigurableAxis confTempFitVarpTBins{"confTempFitVarpTBins", {20, 0.5, 4.05}, "Binning of the pT in the pT vs. TempFitVar plot"}; ConfigurableAxis confTempFitVarPDGBins{"confTempFitVarPDGBins", {6000, -2300, 2300}, "Binning of the PDG code in the pT vs. TempFitVar plot"}; ConfigurableAxis confTempFitVarCPABins{"confTempFitVarCPABins", {1000, 0.9, 1}, "Binning of the pointing angle cosinus in the pT vs. TempFitVar plot"}; @@ -78,13 +80,19 @@ struct FemtoUniverseEfficiencyBase { Configurable confMomPion{"confMomPion", 0.75, "Momentum threshold for pion identification using TOF"}; Configurable confNsigmaCombinedProton{"confNsigmaCombinedProton", 3.0, "TPC and TOF Proton Sigma (combined) for momentum > confMomProton"}; Configurable confNsigmaTPCProton{"confNsigmaTPCProton", 3.0, "TPC Proton Sigma for momentum < confMomProton"}; - Configurable confNsigmaPrRejectPiNsigma{"confNsigmaPrRejectPiNsigma", 2.0, "Reject if a proton could be a pion within a givien nSigma value"}; - Configurable confNsigmaPrRejectKaNsigma{"confNsigmaPrRejectKaNsigma", 2.0, "Reject if a proton could be a kaon within a givien nSigma value"}; + Configurable confNsigmaTPCPrRejectPiNsigma{"confNsigmaTPCPrRejectPiNsigma", 3.0, "Reject if a proton could be a pion within a givien TPC nSigma value"}; + Configurable confNsigmaTPCPrRejectKaNsigma{"confNsigmaTPCPrRejectKaNsigma", 2.0, "Reject if a proton could be a kaon within a givien TPC nSigma value"}; + Configurable confNsigmaCombPrRejectPiNsigma{"confNsigmaCombPrRejectPiNsigma", 4.2, "Reject if a proton could be a pion within a givien nSigma comb. value"}; + Configurable confNsigmaCombPrRejectKaNsigma{"confNsigmaCombPrRejectKaNsigma", 2.8, "Reject if a proton could be a kaon within a givien nSigma comb. value"}; Configurable confNsigmaCombinedPion{"confNsigmaCombinedPion", 3.0, "TPC and TOF Pion Sigma (combined) for momentum > confMomPion"}; Configurable confNsigmaTPCPion{"confNsigmaTPCPion", 3.0, "TPC Pion Sigma for momentum < confMomPion"}; - Configurable confNsigmaPiRejectKaNsigma{"confNsigmaPiRejectKaNsigma", 2.0, "Reject if a pion could be a kaon within a givien nSigma value"}; - Configurable confNsigmaPiRejectPrNsigma{"confNsigmaPiRejectPrNsigma", 2.0, "Reject if a pion could be a proton within a givien nSigma value"}; + Configurable confNsigmaTPCPiRejectKaNsigma{"confNsigmaTPCPiRejectKaNsigma", 2.0, "Reject if a pion could be a kaon within a givien TPC nSigma value"}; + Configurable confNsigmaTPCPiRejectPrNsigma{"confNsigmaTPCPiRejectPrNsigma", 2.0, "Reject if a pion could be a proton within a givien TPC nSigma value"}; + Configurable confNsigmaCombPiRejectKaNsigma{"confNsigmaCombPiRejectKaNsigma", 2.8, "Reject if a pion could be a kaon within a givien comb. nSigma value"}; + Configurable confNsigmaCombPiRejectPrNsigma{"confNsigmaCombPiRejectPrNsigma", 2.8, "Reject if a pion could be a proton within a givien comb. nSigma value"}; Configurable confPDGCheckMCReco{"confPDGCheckMCReco", true, "Check PDG code of MC reco paricles"}; + Configurable confCheckMotherPDG{"confCheckMotherPDG", false, "Check PDG code of the mother particle"}; + Configurable confMotherPDGCode{"confMotherPDGCode", 421, "Mother PDG code"}; } ConfBothTracks; struct : o2::framework::ConfigurableGroup { @@ -129,8 +137,10 @@ struct FemtoUniverseEfficiencyBase { Configurable confMomKaonLF{"confMomKaonLF", 0.5, "Momentum threshold for kaon identification using TOF (LF selection)"}; Configurable confNSigmaTPCKaonLF{"confNSigmaTPCKaonLF", 3.0, "TPC Kaon Sigma as in LF"}; Configurable confNSigmaCombKaonLF{"confNSigmaCombKaonLF", 3.0, "TPC and TOF Kaon Sigma (combined) as in LF"}; - Configurable confNsigmaKaRejectPiNsigma{"confNsigmaKaRejectPiNsigma", 3.0, "Reject if a kaon could be a pion within a given nSigma value"}; - Configurable confNsigmaKaRejectPrNsigma{"confNsigmaKaRejectPrNsigma", 3.0, "Reject if a kaon could be a proton within a given nSigma value"}; + Configurable confNsigmaTPCKaRejectPiNsigma{"confNsigmaTPCKaRejectPiNsigma", 1.0, "Reject if a kaon could be a pion within a given TPC nSigma value"}; + Configurable confNsigmaTPCKaRejectPrNsigma{"confNsigmaTPCKaRejectPrNsigma", 2.0, "Reject if a kaon could be a proton within a given TPC nSigma value"}; + Configurable confNsigmaCombKaRejectPiNsigma{"confNsigmaCombKaRejectPiNsigma", 1.4, "Reject if a kaon could be a pion within a given comb. nSigma value"}; + Configurable confNsigmaCombKaRejectPrNsigma{"confNsigmaCombKaRejectPrNsigma", 2.8, "Reject if a kaon could be a proton within a given comb. nSigma value"}; } ConfKaonSelection; /// Deuteron configurables @@ -193,43 +203,75 @@ struct FemtoUniverseEfficiencyBase { HistogramRegistry registryPDG{"PDGHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registryCuts{"CutsPtHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registryMCOrigin{"MCOriginHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry registryTOFMatch{"TOFMatchHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry registrySecTrkCont{"registrySecTrkCont", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; void init(InitContext&) { eventHisto.init(&qaRegistry); - registryCuts.add("part1/cutsVspT", ";#it{p}_{T} (GeV/c) ;Cut no.", {HistType::kTH2F, {{500, 0, 5}, {7, 0, 7}}}); + registryCuts.add("part1/cutsVspT", ";#it{p}_{T} (GeV/c) ;Cut no.", {HistType::kTH2F, {{confHistCutsVsPtBins}, {7, 0, 7}}}); trackHistoPartOneGen.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarPDGBins, confIsMCGen, confPDGCodePartOne, false); trackHistoPartOneRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, confIsMCReco, confPDGCodePartOne, confIsDebug); - registryMCOrigin.add("part1/hPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{240, 0, 6}}}); - registryMCOrigin.add("part1/hTofMatchingPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{240, 0, 6}}}); - registryMCOrigin.add("part1/hTpcPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{240, 0, 6}}}); - registryPDG.add("part1/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); - registryPDG.add("part1/PDGvspTall", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); + registryMCOrigin.add("part1/hRecoPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryMCOrigin.add("part1/hRecoPtBeforePDGCheck", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryMCOrigin.add("part1/hTruthPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part1/hTofMatchPtBeforePIDAllPart", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part1/hTofMatchPtBeforePIDPartWithTof", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part1/hTofMatchPtAfterPIDAllPart", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part1/hTofMatchPtAfterPIDPartWithTof", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part1/hTofMatchPtAfterPIDPrimaryWithTof", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part1/hTpcPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryPDG.add("part1/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{confTempFitVarpTBins}, {16001, -8000.5, 8000.5}}}); + registryPDG.add("part1/PDGvspTall", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{confTempFitVarpTBins}, {16001, -8000.5, 8000.5}}}); if (confParticleTypePartOne == uint8_t(aod::femtouniverseparticle::ParticleType::kV0)) { - trackHistoV0OneRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarCPABins, 0, confPDGCodePartOne, confIsDebug); - trackHistoV0OneChildPosRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, 0, 0, confIsDebug, "posChildV0_1"); - trackHistoV0OneChildNegRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, 0, 0, confIsDebug, "negChildV0_1"); + trackHistoV0OneRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarCPABins, false, confPDGCodePartOne, confIsDebug); + trackHistoV0OneChildPosRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, false, 0, confIsDebug, "posChildV0_1"); + trackHistoV0OneChildNegRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, false, 0, confIsDebug, "negChildV0_1"); registryPDG.add("part1/dpositive/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); registryPDG.add("part1/dnegative/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); } + if (fillSecTrkContHistos) { + registrySecTrkCont.add("part1/hDCAxy_Primary", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part1/hDCAxy_Daughter", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part1/hDCAxy_Material", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part1/hDCAxy_WrongCollision", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part1/hDCAxy_Fake", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part1/hDCAxy_Else", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part1/hDCAxy_NoMCTruthOrgin", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + } - registryPDG.add("part2/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); - registryPDG.add("part2/PDGvspTall", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); if (!confIsSame) { - registryCuts.add("part2/cutsVspT", ";#it{p}_{T} (GeV/c) ;Cut no.", {HistType::kTH2F, {{500, 0, 5}, {7, 0, 7}}}); + registryCuts.add("part2/cutsVspT", ";#it{p}_{T} (GeV/c) ;Cut no.", {HistType::kTH2F, {{confHistCutsVsPtBins}, {7, 0, 7}}}); trackHistoPartTwoGen.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarPDGBins, confIsMCGen, confPDGCodePartTwo, false); trackHistoPartTwoRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, confIsMCReco, confPDGCodePartTwo, confIsDebug); - registryMCOrigin.add("part2/hPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{240, 0, 6}}}); - registryMCOrigin.add("part2/hTofMatchingPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{240, 0, 6}}}); - registryMCOrigin.add("part2/hTpcPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{240, 0, 6}}}); + registryMCOrigin.add("part2/hRecoPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryMCOrigin.add("part2/hRecoPtBeforePDGCheck", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryMCOrigin.add("part2/hTruthPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part2/hTofMatchPtBeforePIDAllPart", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part2/hTofMatchPtBeforePIDPartWithTof", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part2/hTofMatchPtAfterPIDAllPart", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part2/hTofMatchPtAfterPIDPartWithTof", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part2/hTofMatchPtAfterPIDPrimaryWithTof", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryTOFMatch.add("part2/hTpcPt", " ;#it{p}_{T} (GeV/c); Entries", {HistType::kTH1F, {{confTempFitVarpTBins}}}); + registryPDG.add("part2/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{confTempFitVarpTBins}, {16001, -8000.5, 8000.5}}}); + registryPDG.add("part2/PDGvspTall", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{confTempFitVarpTBins}, {16001, -8000.5, 8000.5}}}); if (confParticleTypePartTwo == uint8_t(aod::femtouniverseparticle::ParticleType::kV0)) { - trackHistoV0TwoRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarCPABins, 0, confPDGCodePartTwo, confIsDebug); - trackHistoV0TwoChildPosRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, 0, 0, confIsDebug, "posChildV0_2"); - trackHistoV0TwoChildNegRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, 0, 0, confIsDebug, "negChildV0_2"); + trackHistoV0TwoRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarCPABins, false, confPDGCodePartTwo, confIsDebug); + trackHistoV0TwoChildPosRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, false, 0, confIsDebug, "posChildV0_2"); + trackHistoV0TwoChildNegRec.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarDCABins, false, 0, confIsDebug, "negChildV0_2"); registryPDG.add("part2/dpositive/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); registryPDG.add("part2/dnegative/PDGvspT", "PDG;#it{p}_{T} (GeV/c); PDG", {HistType::kTH2F, {{500, 0, 5}, {16001, -8000.5, 8000.5}}}); } + if (fillSecTrkContHistos) { + registrySecTrkCont.add("part2/hDCAxy_Primary", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part2/hDCAxy_Daughter", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part2/hDCAxy_Material", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part2/hDCAxy_WrongCollision", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part2/hDCAxy_Fake", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part2/hDCAxy_Else", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + registrySecTrkCont.add("part2/hDCAxy_NoMCTruthOrgin", ";#it{p}_{T} (GeV/c); DCA_{xy} (cm)", {HistType::kTH2F, {{confTempFitVarpTBins}, {confTempFitVarDCABins}}}); + } } } @@ -238,24 +280,14 @@ struct FemtoUniverseEfficiencyBase { if (forceTof && !partHasTof) { return false; } - if (mom <= ConfBothTracks.confMomProton) { - if (std::abs(nsigmaTPCPr) < ConfBothTracks.confNsigmaTPCProton) { - return true; - } else { - return false; - } + return std::abs(nsigmaTPCPr) < ConfBothTracks.confNsigmaTPCProton; } - if (mom > ConfBothTracks.confMomProton) { - if (checkIfTofAvailable && !partHasTof) { - return std::abs(nsigmaTPCPr) < ConfBothTracks.confNsigmaTPCProton; - } else if (checkIfTofAvailable && partHasTof) { - return std::sqrt(std::pow(nsigmaTPCPr, 2) + std::pow(nsigmaTOFPr, 2)) < ConfBothTracks.confNsigmaCombinedProton; - } else { - return std::sqrt(std::pow(nsigmaTPCPr, 2) + std::pow(nsigmaTOFPr, 2)) < ConfBothTracks.confNsigmaCombinedProton; - } + // mom > ConfBothTracks.confMomProton + if (checkIfTofAvailable && !partHasTof) { + return std::abs(nsigmaTPCPr) < ConfBothTracks.confNsigmaTPCProton; } - return false; + return std::sqrt(std::pow(nsigmaTPCPr, 2) + std::pow(nsigmaTOFPr, 2)) < ConfBothTracks.confNsigmaCombinedProton; } bool isProtonNSigmaRejected(bool partHasTof, float mom, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCKa, float nsigmaTOFKa) @@ -264,80 +296,56 @@ struct FemtoUniverseEfficiencyBase { return true; } - // if (mom <= ConfBothTracks.confMomProton || !partHasTof) { if (mom <= ConfBothTracks.confMomProton || !partHasTof) { - if (std::abs(nsigmaTPCPi) < ConfBothTracks.confNsigmaPrRejectPiNsigma) { - return true; - } else if (std::abs(nsigmaTPCKa) < ConfBothTracks.confNsigmaPrRejectKaNsigma) { + if (std::abs(nsigmaTPCPi) < ConfBothTracks.confNsigmaTPCPrRejectPiNsigma) { return true; - } else { - return false; } - } - if (mom > ConfBothTracks.confMomProton && partHasTof) { - if (std::sqrt(std::pow(nsigmaTPCPi, 2) + std::pow(nsigmaTOFPi, 2)) < ConfBothTracks.confNsigmaPrRejectPiNsigma) { + if (std::abs(nsigmaTPCKa) < ConfBothTracks.confNsigmaTPCPrRejectKaNsigma) { return true; - } else if (std::sqrt(std::pow(nsigmaTPCKa, 2) + std::pow(nsigmaTOFKa, 2)) < ConfBothTracks.confNsigmaPrRejectKaNsigma) { - return true; - } else { - return false; } - } else { return false; } + // mom > ConfBothTracks.confMomProton && partHasTof + if (std::sqrt(std::pow(nsigmaTPCPi, 2) + std::pow(nsigmaTOFPi, 2)) < ConfBothTracks.confNsigmaCombPrRejectPiNsigma) { + return true; + } + if (std::sqrt(std::pow(nsigmaTPCKa, 2) + std::pow(nsigmaTOFKa, 2)) < ConfBothTracks.confNsigmaCombPrRejectKaNsigma) { + return true; + } + return false; } bool isKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) { - if (isKaonRun2 == true) { + if (isKaonRun2) { if (mom < ConfKaonSelection.confMomKaonRun2) { // < 0.4 GeV/c return std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCbelow04Run2; - } else if (mom > ConfKaonSelection.confMomKaonRun2 && mom < ConfKaonSelection.confMomKaon045) { // 0.4 - 0.45 + } + if (mom > ConfKaonSelection.confMomKaonRun2 && mom < ConfKaonSelection.confMomKaon045) { // 0.4 - 0.45 GeV/c return std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom04to045Run2; - } else if (mom > ConfKaonSelection.confMomKaon045 && mom < ConfKaonSelection.confMomKaon08) { // 0.45 - 0.8 + } + if (mom > ConfKaonSelection.confMomKaon045 && mom < ConfKaonSelection.confMomKaon08) { // 0.45 - 0.8 GeV/c return (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom045to08Run2 && std::abs(nsigmaTOFK) < ConfKaonSelection.confKaonNsigmaTOFfrom045to08Run2); - } else if (mom > ConfKaonSelection.confMomKaon08 && mom < ConfKaonSelection.confMomKaon15) { // 0.8 - 1.5 - return (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom08to15Run2 && std::abs(nsigmaTOFK) < ConfKaonSelection.confKaonNsigmaTOFfrom08to15Run2); - } else { - return false; } - } else { - if (mom < ConfKaonSelection.confMomKaonRun3) { // 0.0-0.3 - if (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom0to03) { - return true; - } else { - return false; - } - } else if (mom < ConfKaonSelection.confMomKaon045) { // 0.30 - 0.45 - if (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom03to045) { - return true; - } else { - return false; - } - } else if (mom < ConfKaonSelection.confMomKaon055) { // 0.45-0.55 - if (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom045to055) { - return true; - } else { - return false; - } - } else if (mom < ConfKaonSelection.confMomKaon15) { // 0.55-1.5 (now we use TPC and TOF) - if ((std::abs(nsigmaTOFK) < ConfKaonSelection.confKaonNsigmaTOFfrom055to15) && (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom055to15)) { - { - return true; - } - } else { - return false; - } - } else if (mom > ConfKaonSelection.confMomKaon15) { // > 1.5 GeV/c - if ((std::abs(nsigmaTOFK) < ConfKaonSelection.confKaonNsigmaTOFfrom15) && (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom15)) { - return true; - } else { - return false; - } - } else { - return false; + if (mom > ConfKaonSelection.confMomKaon08 && mom < ConfKaonSelection.confMomKaon15) { // 0.8 - 1.5 GeV/c + return (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom08to15Run2 && std::abs(nsigmaTOFK) < ConfKaonSelection.confKaonNsigmaTOFfrom08to15Run2); } + return false; + } + if (mom < ConfKaonSelection.confMomKaonRun3) { // 0.0-0.3 GeV/c + return std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom0to03; + } + if (mom < ConfKaonSelection.confMomKaon045) { // 0.30 - 0.45 GeV/c + return std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom03to045; } + if (mom < ConfKaonSelection.confMomKaon055) { // 0.45-0.55 GeV/c + return std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom045to055; + } + if (mom < ConfKaonSelection.confMomKaon15) { // 0.55-1.5 GeV/c (now we use TPC and TOF) + return ((std::abs(nsigmaTOFK) < ConfKaonSelection.confKaonNsigmaTOFfrom055to15) && (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom055to15)); + } + // mom > ConfKaonSelection.confMomKaon15) | momentum > 1.5 GeV/c + return ((std::abs(nsigmaTOFK) < ConfKaonSelection.confKaonNsigmaTOFfrom15) && (std::abs(nsigmaTPCK) < ConfKaonSelection.confKaonNsigmaTPCfrom15)); } bool isKaonNSigmaLF(bool partHasTof, float mom, float nsigmaTPCK, float nsigmaTOFK) @@ -345,24 +353,14 @@ struct FemtoUniverseEfficiencyBase { if (forceTof && !partHasTof) { return false; } - if (mom <= ConfKaonSelection.confMomKaonLF) { - if (std::abs(nsigmaTPCK) < ConfKaonSelection.confNSigmaTPCKaonLF) { - return true; - } else { - return false; - } - } else if (mom > ConfKaonSelection.confMomKaonLF) { - if (checkIfTofAvailable && !partHasTof) { - return std::abs(nsigmaTPCK) < ConfKaonSelection.confNSigmaTPCKaonLF; - } else if (checkIfTofAvailable && partHasTof) { - return std::sqrt(std::pow(nsigmaTPCK, 2) + std::pow(nsigmaTOFK, 2)) < ConfKaonSelection.confNSigmaCombKaonLF; - } else { - return std::sqrt(std::pow(nsigmaTPCK, 2) + std::pow(nsigmaTOFK, 2)) < ConfKaonSelection.confNSigmaCombKaonLF; - } - } else { - return false; + return std::abs(nsigmaTPCK) < ConfKaonSelection.confNSigmaTPCKaonLF; + } + // mom > ConfKaonSelection.confMomKaonLF + if (checkIfTofAvailable && !partHasTof) { + return std::abs(nsigmaTPCK) < ConfKaonSelection.confNSigmaTPCKaonLF; } + return std::sqrt(std::pow(nsigmaTPCK, 2) + std::pow(nsigmaTOFK, 2)) < ConfKaonSelection.confNSigmaCombKaonLF; } bool isKaonNSigmaLFRejected(bool partHasTof, float mom, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCPr, float nsigmaTOFPr) @@ -372,23 +370,22 @@ struct FemtoUniverseEfficiencyBase { } if (mom <= ConfKaonSelection.confMomKaonLF || !partHasTof) { - if (std::abs(nsigmaTPCPi) < ConfKaonSelection.confNsigmaKaRejectPiNsigma) { - return true; - } else if (std::abs(nsigmaTPCPr) < ConfKaonSelection.confNsigmaKaRejectPrNsigma) { + if (std::abs(nsigmaTPCPi) < ConfKaonSelection.confNsigmaTPCKaRejectPiNsigma) { return true; } - } - if (mom > ConfKaonSelection.confMomKaonLF && partHasTof) { - if (std::sqrt(std::pow(nsigmaTPCPi, 2) + std::pow(nsigmaTOFPi, 2)) < ConfKaonSelection.confNsigmaKaRejectPiNsigma) { - return true; - } else if (std::sqrt(std::pow(nsigmaTPCPr, 2) + std::pow(nsigmaTOFPr, 2)) < ConfKaonSelection.confNsigmaKaRejectPrNsigma) { + if (std::abs(nsigmaTPCPr) < ConfKaonSelection.confNsigmaTPCKaRejectPrNsigma) { return true; - } else { - return false; } - } else { return false; } + // mom > ConfKaonSelection.confMomKaonLF && partHasTof + if (std::sqrt(std::pow(nsigmaTPCPi, 2) + std::pow(nsigmaTOFPi, 2)) < ConfKaonSelection.confNsigmaCombKaRejectPiNsigma) { + return true; + } + if (std::sqrt(std::pow(nsigmaTPCPr, 2) + std::pow(nsigmaTOFPr, 2)) < ConfKaonSelection.confNsigmaCombKaRejectPrNsigma) { + return true; + } + return false; } bool isPionNSigma(bool partHasTof, float mom, float nsigmaTPCPi, float nsigmaTOFPi) @@ -396,24 +393,14 @@ struct FemtoUniverseEfficiencyBase { if (forceTof && !partHasTof) { return false; } - if (mom <= ConfBothTracks.confMomPion) { - if (std::abs(nsigmaTPCPi) < ConfBothTracks.confNsigmaTPCPion) { - return true; - } else { - return false; - } - } else if (mom > ConfBothTracks.confMomPion) { - if (checkIfTofAvailable && !partHasTof) { - return std::abs(nsigmaTPCPi) < ConfBothTracks.confNsigmaTPCPion; - } else if (checkIfTofAvailable && partHasTof) { - return std::sqrt(std::pow(nsigmaTPCPi, 2) + std::pow(nsigmaTOFPi, 2)) < ConfBothTracks.confNsigmaCombinedPion; - } else { - return std::sqrt(std::pow(nsigmaTPCPi, 2) + std::pow(nsigmaTOFPi, 2)) < ConfBothTracks.confNsigmaCombinedPion; - } - } else { - return false; + return std::abs(nsigmaTPCPi) < ConfBothTracks.confNsigmaTPCPion; + } + // mom > ConfBothTracks.confMomPion + if (checkIfTofAvailable && !partHasTof) { + return std::abs(nsigmaTPCPi) < ConfBothTracks.confNsigmaTPCPion; } + return std::sqrt(std::pow(nsigmaTPCPi, 2) + std::pow(nsigmaTOFPi, 2)) < ConfBothTracks.confNsigmaCombinedPion; } bool isPionNSigmaRejected(bool partHasTof, float mom, float nsigmaTPCKa, float nsigmaTOFKa, float nsigmaTPCPr, float nsigmaTOFPr) @@ -423,36 +410,35 @@ struct FemtoUniverseEfficiencyBase { } if (mom <= ConfBothTracks.confMomPion || !partHasTof) { - if (std::abs(nsigmaTPCKa) < ConfBothTracks.confNsigmaPiRejectKaNsigma) { - return true; - } else if (std::abs(nsigmaTPCPr) < ConfBothTracks.confNsigmaPiRejectPrNsigma) { + if (std::abs(nsigmaTPCKa) < ConfBothTracks.confNsigmaTPCPiRejectKaNsigma) { return true; } - } - if (mom > ConfBothTracks.confMomPion && partHasTof) { - if (std::sqrt(std::pow(nsigmaTPCKa, 2) + std::pow(nsigmaTOFKa, 2)) < ConfBothTracks.confNsigmaPiRejectKaNsigma) { + if (std::abs(nsigmaTPCPr) < ConfBothTracks.confNsigmaTPCPiRejectPrNsigma) { return true; - } else if (std::sqrt(std::pow(nsigmaTPCPr, 2) + std::pow(nsigmaTOFPr, 2)) < ConfBothTracks.confNsigmaPiRejectPrNsigma) { - return true; - } else { - return false; } - } else { return false; } + // mom > ConfBothTracks.confMomPion && partHasTof + if (std::sqrt(std::pow(nsigmaTPCKa, 2) + std::pow(nsigmaTOFKa, 2)) < ConfBothTracks.confNsigmaCombPiRejectKaNsigma) { + return true; + } + if (std::sqrt(std::pow(nsigmaTPCPr, 2) + std::pow(nsigmaTOFPr, 2)) < ConfBothTracks.confNsigmaCombPiRejectPrNsigma) { + return true; + } + return false; } bool isDeuteronNSigma(float mom, float nsigmaTPCDe, float nsigmaTOFDe) { - if (mom > deuteronconfigs.confPLowDe && mom < deuteronconfigs.confPHighDe) { - if (mom < deuteronconfigs.confTOFpMinDe) { - return (std::abs(nsigmaTPCDe) < deuteronconfigs.confNsigmaTPCDe); - } else { - return (std::abs(nsigmaTOFDe) < deuteronconfigs.confNsigmaTOFDe && (std::abs(nsigmaTPCDe) < deuteronconfigs.confNsigmaTPCDe)); - } - } else { + if (mom <= deuteronconfigs.confPLowDe || mom >= deuteronconfigs.confPHighDe) { return false; } + // Momentum is within valid range | PID on TPC only + if (mom < deuteronconfigs.confTOFpMinDe) { + return (std::abs(nsigmaTPCDe) < deuteronconfigs.confNsigmaTPCDe); + } + // mom >= deuteronconfigs.confTOFpMinDe + return (std::abs(nsigmaTOFDe) < deuteronconfigs.confNsigmaTOFDe && std::abs(nsigmaTPCDe) < deuteronconfigs.confNsigmaTPCDe); } bool isParticleNSigma(int pdgCode, bool partHasTof, float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK, float nsigmaTPCDe, float nsigmaTOFDe) @@ -508,8 +494,8 @@ struct FemtoUniverseEfficiencyBase { } template - bool passPIDSelection(Atrack const& track, SpeciesContainer const mPIDspecies, - T1 const maxTPC, T2 const maxTOF, double ptThreshold = 0.75, bool tofForced = false, bool partHasTof = false) + bool passPIDSelection(Atrack const& track, SpeciesContainer const& mPIDspecies, + T1 const& maxTPC, T2 const& maxTOF, double ptThreshold = 0.75, bool tofForced = false, bool partHasTof = false) { // Ensure size consistency if (mPIDspecies.value.size() != maxTPC.value.size() || mPIDspecies.value.size() != maxTOF.value.size()) { @@ -553,14 +539,12 @@ struct FemtoUniverseEfficiencyBase { bool invMLambda(float invMassLambda, float invMassAntiLambda) { - if ((invMassLambda < confV0InvMassLowLimit || invMassLambda > confV0InvMassUpLimit) && (invMassAntiLambda < confV0InvMassLowLimit || invMassAntiLambda > confV0InvMassUpLimit)) { - return false; - } - return true; + return (invMassLambda >= confV0InvMassLowLimit && invMassLambda <= confV0InvMassUpLimit) || + (invMassAntiLambda >= confV0InvMassLowLimit && invMassAntiLambda <= confV0InvMassUpLimit); } template - void fillCollision(CollisionType col) + void fillCollision(CollisionType const& col) { eventHisto.fillQA(col); } @@ -571,11 +555,14 @@ struct FemtoUniverseEfficiencyBase { /// @param grouppartsOneMCGen partition for the first particle passed by the process function /// @param grouppartsTwoMCGen partition for the second particle passed by the process function template - void doMCGen(PartitionType grouppartsOneMCGen, PartitionType grouppartsTwoMCGen) + void doMCGen(PartitionType const& grouppartsOneMCGen, PartitionType const& grouppartsTwoMCGen) { /// Histogramming same event for (const auto& part : grouppartsOneMCGen) { - if (!confNoPDGPartOne && part.tempFitVar() != confPDGCodePartOne) { + if (!confNoPDGPartOne && static_cast(part.pidCut()) != confPDGCodePartOne) { + continue; + } + if (ConfBothTracks.confCheckMotherPDG && std::abs(static_cast(part.tempFitVar())) == std::abs(ConfBothTracks.confMotherPDGCode)) { continue; } trackHistoPartOneGen.fillQA(part); @@ -583,7 +570,10 @@ struct FemtoUniverseEfficiencyBase { if (!confIsSame) { for (const auto& part : grouppartsTwoMCGen) { - if (!confNoPDGPartTwo && part.tempFitVar() != confPDGCodePartTwo) { + if (!confNoPDGPartTwo && static_cast(part.pidCut()) != confPDGCodePartTwo) { + continue; + } + if (ConfBothTracks.confCheckMotherPDG && std::abs(static_cast(part.tempFitVar())) == std::abs(ConfBothTracks.confMotherPDGCode)) { continue; } trackHistoPartTwoGen.fillQA(part); @@ -598,7 +588,7 @@ struct FemtoUniverseEfficiencyBase { /// @param grouppartsOneMCRec partition for the first particle passed by the process function /// @param grouppartsTwoMCRec partition for the second particle passed by the process function template - void doMCRecTrackTrack(PartitionType grouppartsOneMCRec, PartitionType grouppartsTwoMCRec) + void doMCRecTrackTrack(PartitionType const& grouppartsOneMCRec, PartitionType const& grouppartsTwoMCRec) { /// Histogramming same event for (const auto& part : grouppartsOneMCRec) { @@ -616,6 +606,16 @@ struct FemtoUniverseEfficiencyBase { } registryCuts.fill(HIST("part1/cutsVspT"), part.pt(), 2); + if (!part.has_fdMCParticle()) { + continue; + } + registryCuts.fill(HIST("part1/cutsVspT"), part.pt(), 3); + // TOF Matching efficiency before PID + registryTOFMatch.fill(HIST("part1/hTofMatchPtBeforePIDAllPart"), part.pt()); + if (part.mLambda() == 1) { + registryTOFMatch.fill(HIST("part1/hTofMatchPtBeforePIDPartWithTof"), part.pt()); + } + if (!ConfTracksPid.trkUsePassPIDSelection) { if (!isParticleNSigma(confPDGCodePartOne, static_cast(part.mLambda()), part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Deuteron), trackCuts.getNsigmaTOF(part, o2::track::PID::Deuteron))) { continue; @@ -628,37 +628,61 @@ struct FemtoUniverseEfficiencyBase { continue; } } + registryCuts.fill(HIST("part1/cutsVspT"), part.pt(), 4); - registryCuts.fill(HIST("part1/cutsVspT"), part.pt(), 3); - - if (!part.has_fdMCParticle()) { - continue; + // TOF Matching efficiency after PID + registryTOFMatch.fill(HIST("part1/hTofMatchPtAfterPIDAllPart"), part.pt()); + if (part.mLambda() == 1) { + registryTOFMatch.fill(HIST("part1/hTofMatchPtAfterPIDPartWithTof"), part.pt()); + } else { + registryTOFMatch.fill(HIST("part1/hTpcPt"), part.pt()); } - registryCuts.fill(HIST("part1/cutsVspT"), part.pt(), 4); + // Get the coresponding MC particle const auto mcParticle = part.fdMCParticle(); registryPDG.fill(HIST("part1/PDGvspTall"), part.pt(), mcParticle.pdgMCTruth()); - trackHistoPartOneRec.fillQA(part); + + // Primary particles which have TOF after PID + if (part.mLambda() == 1 && mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { + registryTOFMatch.fill(HIST("part1/hTofMatchPtAfterPIDPrimaryWithTof"), part.pt()); + } + + if (fillSecTrkContHistos) { + if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { + registrySecTrkCont.fill(HIST("part1/hDCAxy_Primary"), part.pt(), part.tempFitVar()); + } else if ((mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kDaughter) || (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kDaughterLambda) || (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kDaughterSigmaplus)) { + registrySecTrkCont.fill(HIST("part1/hDCAxy_Daughter"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kMaterial) { + registrySecTrkCont.fill(HIST("part1/hDCAxy_Material"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kWrongCollision) { + registrySecTrkCont.fill(HIST("part1/hDCAxy_WrongCollision"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kFake) { + registrySecTrkCont.fill(HIST("part1/hDCAxy_Fake"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kElse) { + registrySecTrkCont.fill(HIST("part1/hDCAxy_Else"), part.pt(), part.tempFitVar()); + } else { + registrySecTrkCont.fill(HIST("part1/hDCAxy_NoMCTruthOrgin"), part.pt(), part.tempFitVar()); + } + } if (!(mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary)) { continue; } + if (ConfBothTracks.confCheckMotherPDG && std::abs(static_cast(part.motherPDG())) == std::abs(ConfBothTracks.confMotherPDGCode)) { + continue; + } registryCuts.fill(HIST("part1/cutsVspT"), part.pt(), 5); + registryMCOrigin.fill(HIST("part1/hRecoPtBeforePDGCheck"), part.pt()); if (ConfBothTracks.confPDGCheckMCReco && !(std::abs(mcParticle.pdgMCTruth()) == std::abs(confPDGCodePartOne))) { continue; } registryCuts.fill(HIST("part1/cutsVspT"), part.pt(), 6); - + trackHistoPartOneRec.fillQA(part); registryPDG.fill(HIST("part1/PDGvspT"), part.pt(), mcParticle.pdgMCTruth()); - registryMCOrigin.fill(HIST("part1/hPt"), mcParticle.pt()); - // TOF Matching efficiency - if (part.mLambda() == 1) { - registryMCOrigin.fill(HIST("part1/hTofMatchingPt"), mcParticle.pt()); - } else { - registryMCOrigin.fill(HIST("part1/hTpcPt"), mcParticle.pt()); - } + registryMCOrigin.fill(HIST("part1/hRecoPt"), part.pt()); + registryMCOrigin.fill(HIST("part1/hTruthPt"), mcParticle.pt()); } if (!confIsSame) { @@ -675,6 +699,16 @@ struct FemtoUniverseEfficiencyBase { } registryCuts.fill(HIST("part2/cutsVspT"), part.pt(), 2); + if (!part.has_fdMCParticle()) { + continue; + } + registryCuts.fill(HIST("part2/cutsVspT"), part.pt(), 3); + // TOF Matching efficiency before PID + registryTOFMatch.fill(HIST("part2/hTofMatchPtBeforePIDAllPart"), part.pt()); + if (part.mLambda() == 1) { + registryTOFMatch.fill(HIST("part2/hTofMatchPtBeforePIDPartWithTof"), part.pt()); + } + if (!ConfTracksPid.trkUsePassPIDSelection) { if (!isParticleNSigma(confPDGCodePartTwo, static_cast(part.mLambda()), part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Deuteron), trackCuts.getNsigmaTOF(part, o2::track::PID::Deuteron))) { continue; @@ -687,36 +721,58 @@ struct FemtoUniverseEfficiencyBase { continue; } } + registryCuts.fill(HIST("part2/cutsVspT"), part.pt(), 4); - registryCuts.fill(HIST("part2/cutsVspT"), part.pt(), 3); - - if (!part.has_fdMCParticle()) { - continue; + // TOF Matching efficiency after PID + registryTOFMatch.fill(HIST("part2/hTofMatchPtAfterPIDAllPart"), part.pt()); + if (part.mLambda() == 1) { + registryTOFMatch.fill(HIST("part2/hTofMatchPtAfterPIDPartWithTof"), part.pt()); + } else { + registryTOFMatch.fill(HIST("part2/hTpcPt"), part.pt()); } - registryCuts.fill(HIST("part2/cutsVspT"), part.pt(), 4); + + // Get the coresponding MC particle const auto mcParticle = part.fdMCParticle(); registryPDG.fill(HIST("part2/PDGvspTall"), part.pt(), mcParticle.pdgMCTruth()); - trackHistoPartTwoRec.fillQA(part); + + // Primary particles which have TOF after PID + if (part.mLambda() == 1 && mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { + registryTOFMatch.fill(HIST("part2/hTofMatchPtAfterPIDPrimaryWithTof"), part.pt()); + } + + if (fillSecTrkContHistos) { + if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { + registrySecTrkCont.fill(HIST("part2/hDCAxy_Primary"), part.pt(), part.tempFitVar()); + } else if ((mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kDaughter) || (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kDaughterLambda) || (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kDaughterSigmaplus)) { + registrySecTrkCont.fill(HIST("part2/hDCAxy_Daughter"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kMaterial) { + registrySecTrkCont.fill(HIST("part2/hDCAxy_Material"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kWrongCollision) { + registrySecTrkCont.fill(HIST("part2/hDCAxy_WrongCollision"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kFake) { + registrySecTrkCont.fill(HIST("part2/hDCAxy_Fake"), part.pt(), part.tempFitVar()); + } else if (mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kElse) { + registrySecTrkCont.fill(HIST("part2/hDCAxy_Else"), part.pt(), part.tempFitVar()); + } else { + registrySecTrkCont.fill(HIST("part2/hDCAxy_NoMCTruthOrgin"), part.pt(), part.tempFitVar()); + } + } if (!(mcParticle.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary)) { continue; } registryCuts.fill(HIST("part2/cutsVspT"), part.pt(), 5); + registryMCOrigin.fill(HIST("part2/hRecoPtBeforePDGCheck"), part.pt()); if (ConfBothTracks.confPDGCheckMCReco && !(std::abs(mcParticle.pdgMCTruth()) == std::abs(confPDGCodePartTwo))) { continue; } registryCuts.fill(HIST("part2/cutsVspT"), part.pt(), 6); - + trackHistoPartTwoRec.fillQA(part); registryPDG.fill(HIST("part2/PDGvspT"), part.pt(), mcParticle.pdgMCTruth()); - registryMCOrigin.fill(HIST("part2/hPt"), mcParticle.pt()); - // TOF Matching efficiency - if (part.mLambda() == 1) { - registryMCOrigin.fill(HIST("part2/hTofMatchingPt"), mcParticle.pt()); - } else { - registryMCOrigin.fill(HIST("part2/hTpcPt"), mcParticle.pt()); - } + registryMCOrigin.fill(HIST("part2/hRecoPt"), part.pt()); + registryMCOrigin.fill(HIST("part2/hTruthPt"), mcParticle.pt()); } } } @@ -728,7 +784,7 @@ struct FemtoUniverseEfficiencyBase { /// @param grouppartsOneMCRec partition for the first particle passed by the process function /// @param grouppartsTwoMCRec partition for the second particle passed by the process function template - void doMCRecTrackPhi(PartitionType grouppartsOneMCRec, PartitionType grouppartsTwoMCRec) + void doMCRecTrackPhi(PartitionType const& grouppartsOneMCRec, PartitionType const& grouppartsTwoMCRec) { // part1 is track and part2 is Phi for (const auto& part : grouppartsOneMCRec) { @@ -774,7 +830,7 @@ struct FemtoUniverseEfficiencyBase { /// @param grouppartsTwoMCRec partition for the second particle passed by the process function /// @param parts all tracks template - void doMCRecV0V0(PartitionType grouppartsOneMCRec, PartitionType grouppartsTwoMCRec, ParticlesType parts) + void doMCRecV0V0(PartitionType const& grouppartsOneMCRec, PartitionType const& grouppartsTwoMCRec, ParticlesType const& parts) { /// Histogramming same event for (const auto& part : grouppartsOneMCRec) { @@ -855,7 +911,7 @@ struct FemtoUniverseEfficiencyBase { /// @param grouppartsTwoMCRec partition for the second particle passed by the process function /// @param parts all tracks template - void doMCRecTrackV0(PartitionType grouppartsOneMCRec, PartitionType grouppartsTwoMCRec, ParticlesType const& parts) + void doMCRecTrackV0(PartitionType const& grouppartsOneMCRec, PartitionType const& grouppartsTwoMCRec, ParticlesType const& parts) { // part1 is track and part2 is V0 /// Histogramming same event @@ -865,8 +921,9 @@ struct FemtoUniverseEfficiencyBase { } trackHistoPartOneRec.fillQA(part); - if (!part.has_fdMCParticle()) + if (!part.has_fdMCParticle()) { continue; + } const auto mcParticle = part.fdMCParticle(); registryPDG.fill(HIST("part1/PDGvspT"), part.pt(), mcParticle.pdgMCTruth()); @@ -926,7 +983,7 @@ struct FemtoUniverseEfficiencyBase { auto thegroupPartsTrackOneRec = partsTrackOneMCReco->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); auto thegroupPartsTrackTwoRec = partsTrackTwoMCReco->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); if (confIsDebug) { - if (confIsMCGen) { + if (confIsMCReco) { doMCRecTrackTrack(thegroupPartsTrackOneRec, thegroupPartsTrackTwoRec); } else { doMCRecTrackTrack(thegroupPartsTrackOneRec, thegroupPartsTrackTwoRec); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx index 838f5cbbc3d..b9a740f4874 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackCascadeExtended.cxx @@ -10,9 +10,10 @@ // or submit itself to any jurisdiction. /// \file femtoUniversePairTaskTrackCascadeExtended.cxx -/// \brief Task for cascade correlations and QA +/// \brief Task for track and cascade correlations and QA /// \author Barbara Chytla, WUT Warsaw, barbara.chytla@cern.ch /// \author Shirajum Monira, WUT Warsaw, shirajum.monira@cern.ch +/// \author Tomasz Pawlaczyk, WUT Warsaw, tpawlacz@cern.ch #include "PWGCF/FemtoUniverse/Core/FemtoUniverseContainer.h" #include "PWGCF/FemtoUniverse/Core/FemtoUniverseDetaDphiStar.h" @@ -46,13 +47,17 @@ #include #include +#include +#include #include +#include #include #include #include #include #include #include +#include #include using namespace o2; @@ -62,7 +67,7 @@ using namespace o2::framework::expressions; using namespace o2::analysis::femto_universe; using namespace o2::aod::pidutils; -struct femtoUniversePairTaskTrackCascadeExtended { +struct femtoUniversePairTaskTrackCascadeExtended { // NOLINT(cppcoreguidelines-pro-type-member-init) Service pdgMC; SliceCache cache; @@ -76,35 +81,52 @@ struct femtoUniversePairTaskTrackCascadeExtended { Configurable confCascInvMassUpLimit{"confCascInvMassUpLimit", 1.325, "Upper limit of the Casc invariant mass"}; /// applying narrow cut - Configurable confZVertexCut{"confZVertexCut", 10.f, "Event sel: Maximum z-Vertex (cm)"}; - Configurable confEta{"confEta", 0.8, "Eta cut for the global track"}; - - // configurations for correlation part - Configurable confTrackChoicePartOne{"confTrackChoicePartOne", 0, "0:Proton, 1:Pion, 2:Kaon"}; - Configurable confTrkPDGCodePartOne{"confTrkPDGCodePartOne", 2212, "Particle 1 (Track) - PDG code"}; - Configurable confCascPDGCodePartTwo{"confCascPDGCodePartTwo", 3312, "Particle 2 (Cascade) - PDG code"}; - Configurable confCascType1{"confCascType1", 0, "select one of the Cascades (Omega = 0, Xi = 1, anti-Omega = 2, anti-Xi = 3) for track-cascade combination"}; - Configurable confCascType2{"confCascType2", 0, "select one of the Cascades (Omega = 0, Xi = 1, anti-Omega = 2, anti-Xi = 3) for cascade-cascade combination"}; - Configurable confIsCPR{"confIsCPR", false, "Close Pair Rejection"}; - Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; - Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; - Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; - Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; - Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; - Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.0, "Delta Eta cut for Close Pair Rejection"}; - Configurable confIsSameSignCPR{"confIsSameSignCPR", false, "Close Pair Rejection for same sign children of cascades"}; - Configurable confChargePart1{"confChargePart1", 1, "sign of particle 1"}; - Configurable confHPtPart1{"confHPtPart1", 4.0f, "higher limit for pt of particle 1"}; - Configurable confLPtPart1{"confLPtPart1", 0.5f, "lower limit for pt of particle 1"}; - Configurable confHPtPart2{"confHPtPart2", 4.0f, "higher limit for pt of particle 2"}; - Configurable confLPtPart2{"confLPtPart2", 0.3f, "lower limit for pt of particle 2"}; - Configurable confmom{"confmom", 0.75, "momentum threshold for particle identification using TOF"}; - Configurable confNsigmaTPCParticle{"confNsigmaTPCParticle", 3.0, "TPC Sigma for particle (track) momentum < Confmom"}; - Configurable confNsigmaCombinedParticle{"confNsigmaCombinedParticle", 3.0, "TPC and TOF Sigma (combined) for particle (track) momentum > Confmom"}; - Configurable confNsigmaTPCParticleChild{"confNsigmaTPCParticleChild", 3.0, "TPC Sigma for particle (daugh & bach) momentum < Confmom"}; - Configurable confNsigmaTOFParticleChild{"confNsigmaTOFParticleChild", 3.0, "TOF Sigma for particle (daugh & bach) momentum > Confmom"}; - Configurable confUseStrangenessTOF{"confUseStrangenessTOF", true, "Use strangeness TOF for cascade PID"}; + struct : o2::framework::ConfigurableGroup { + Configurable confZVertexCut{"confZVertexCut", 10.f, "Event sel: Maximum z-Vertex (cm)"}; + Configurable confEta{"confEta", 0.8, "Eta cut for the global track"}; + } narrowcuts; + // configurations for track particles + struct : o2::framework::ConfigurableGroup { + Configurable confTrackChoicePartOne{"confTrackChoicePartOne", 0, "0:Proton, 1:Pion, 2:Kaon"}; + Configurable confTrackChoicePartTwo{"confTrackChoicePartTwo", 0, "0:Proton, 1:Pion, 2:Kaon"}; + Configurable confTrkPDGCodePartOne{"confTrkPDGCodePartOne", 2212, "Particle 1 (Track) - PDG code"}; + Configurable confTrkPDGCodePartTwo{"confTrkPDGCodePartTwo", 2212, "Particle 1 (Track) - PDG code"}; + Configurable confChargePart1{"confChargePart1", 1, "sign of track particle 1"}; + Configurable confChargePart2{"confChargePart2", 1, "sign of track particle 2"}; + Configurable confHPtPart1{"confHPtPart1", 4.0f, "higher limit for pt of track particle"}; + Configurable confLPtPart1{"confLPtPart1", 0.5f, "lower limit for pt of track particle"}; + Configurable confNsigmaCombinedParticle{"confNsigmaCombinedParticle", 3.0, "combined TPC and TOF Sigma for track particle for momentum > cascparticleconfigs.confmom"}; + } trackparticleconfigs; + + /// configurations for cascades + struct : o2::framework::ConfigurableGroup { + Configurable confCascPDGCode{"confCascPDGCode", 3312, "Particle 2 (Cascade) - PDG code"}; + Configurable confCascType1{"confCascType1", 0, "select one of the Cascades (Omega = 0, Xi = 1, anti-Omega = 2, anti-Xi = 3) for track-cascade and cascade-cascade combination"}; + Configurable confCascType2{"confCascType2", 0, "select one of the Cascades (Omega = 0, Xi = 1, anti-Omega = 2, anti-Xi = 3) for cascade-cascade combination"}; + Configurable confHPtPart2{"confHPtPart2", 4.0f, "higher limit for pt of cascade"}; + Configurable confLPtPart2{"confLPtPart2", 0.3f, "lower limit for pt of cascade"}; + Configurable confmom{"confmom", 0.75, "momentum threshold for particle identification using TOF"}; + Configurable confNsigmaTPCParticle{"confNsigmaTPCParticle", 3.0, "TPC Sigma for particle (track) momentum < cascparticleconfigs.confmom"}; + Configurable confNsigmaTPCParticleChild{"confNsigmaTPCParticleChild", 3.0, "TPC Sigma for particle (daugh & bach) momentum < cascparticleconfigs.confmom"}; + Configurable confNsigmaTOFParticleChild{"confNsigmaTOFParticleChild", 3.0, "TOF Sigma for particle (daugh & bach) momentum > cascparticleconfigs.confmom"}; + Configurable confUseStrangenessTOF{"confUseStrangenessTOF", true, "Enable strangeness TOF for cascade PID"}; + Configurable confQARejectByPDG{"confQARejectByPDG", false, "Reject particle 2 by PDG code (only for QA)"}; + } cascparticleconfigs; + + /// configurations for correlation part - CPR + struct : o2::framework::ConfigurableGroup { + Configurable confIsCPR{"confIsCPR", false, "Close Pair Rejection"}; + Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; + Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; + Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; + Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.0, "Delta Eta cut for Close Pair Rejection"}; + Configurable confIsSameSignCPR{"confIsSameSignCPR", false, "Close Pair Rejection for same sign children of cascades"}; + } cprconfigs; + + /// configurations for correlation part ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -127,33 +149,48 @@ struct femtoUniversePairTaskTrackCascadeExtended { } twotracksconfigs; // Efficiency - Configurable confLocalEfficiency{"confLocalEfficiency", "", "Local path to efficiency .root file"}; - Configurable confCCDBEfficiency{"confCCDBEfficiency", "", "CCDB path to efficiency object"}; + struct : o2::framework::ConfigurableGroup { + Configurable confLocalEfficiency{"confLocalEfficiency", "", "Local path to efficiency .root file"}; + Configurable confCCDBEfficiency{"confCCDBEfficiency", "", "CCDB path to efficiency object"}; + Configurable confCCDBNoLaterThanTrack{"confCCDBNoLaterThanTrack", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable confCCDBNoLaterThanCasc{"confCCDBNoLaterThanCasc", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + } ccdbEffLoader; - Filter collisionFilter = (nabs(aod::collision::posZ) < confZVertexCut); + Filter collisionFilter = (nabs(aod::collision::posZ) < narrowcuts.confZVertexCut); using FilteredFDCollisions = soa::Filtered; using FilteredFDCollision = FilteredFDCollisions::iterator; - /// Partition for particle 1 using extended table (track) - Partition partsOneFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == confChargePart1) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < confHPtPart1) && (aod::femtouniverseparticle::pt > confLPtPart1); + /// Partition for the first track particle using extended table + Partition partsTrackOneFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == trackparticleconfigs.confChargePart1) && (nabs(aod::femtouniverseparticle::eta) < narrowcuts.confEta) && (aod::femtouniverseparticle::pt < trackparticleconfigs.confHPtPart1) && (aod::femtouniverseparticle::pt > trackparticleconfigs.confLPtPart1); // used for track - cascade and track - track correlations + /// Partition for MC QA of track using partone configs + Partition partsTrackOneFullMc = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == trackparticleconfigs.confChargePart1) && (nabs(aod::femtouniverseparticle::eta) < narrowcuts.confEta) && (aod::femtouniverseparticle::pt < trackparticleconfigs.confHPtPart1) && (aod::femtouniverseparticle::pt > trackparticleconfigs.confLPtPart1); + /// Partition for the first track particle using bitmask (without extended table) + Partition partsTrackOneBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == trackparticleconfigs.confChargePart1) && (nabs(aod::femtouniverseparticle::eta) < narrowcuts.confEta) && (aod::femtouniverseparticle::pt < trackparticleconfigs.confHPtPart1) && (aod::femtouniverseparticle::pt > trackparticleconfigs.confLPtPart1); + Partition partsTrackOneMCgenBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (nabs(aod::femtouniverseparticle::eta) < narrowcuts.confEta) && (aod::femtouniverseparticle::pt < trackparticleconfigs.confHPtPart1) && (aod::femtouniverseparticle::pt > trackparticleconfigs.confLPtPart1); - /// Partition for particle 1 without extended table (track) - Partition partsOneBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == confChargePart1) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < confHPtPart1) && (aod::femtouniverseparticle::pt > confLPtPart1); - Partition partsOneMCgenBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < confHPtPart1) && (aod::femtouniverseparticle::pt > confLPtPart1); - Partition partsOneMCrecoBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == confChargePart1) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < confHPtPart1) && (aod::femtouniverseparticle::pt > confLPtPart1); + /// Partition for the second track particle using extended table + Partition partsTrackTwoFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == trackparticleconfigs.confChargePart2) && (nabs(aod::femtouniverseparticle::eta) < narrowcuts.confEta) && (aod::femtouniverseparticle::pt < trackparticleconfigs.confHPtPart1) && (aod::femtouniverseparticle::pt > trackparticleconfigs.confLPtPart1); // used for track - track correlations - /// Partition for particle 2 using extended table (cascade) - Partition partsTwoFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kCascade)) && (aod::femtouniverseparticle::pt < confHPtPart2) && (aod::femtouniverseparticle::pt > confLPtPart2); + /// Partition for the second track particle using bitmask (without extended table) + Partition partsTrackTwoBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::mAntiLambda == trackparticleconfigs.confChargePart2) && (nabs(aod::femtouniverseparticle::eta) < narrowcuts.confEta) && (aod::femtouniverseparticle::pt < trackparticleconfigs.confHPtPart1) && (aod::femtouniverseparticle::pt > trackparticleconfigs.confLPtPart1); - /// Partition for particle 2 without extended table (cascade) - Partition partsTwoBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kCascade)) && (aod::femtouniverseparticle::pt < confHPtPart2) && (aod::femtouniverseparticle::pt > confLPtPart2); - Partition partsTwoMCgenBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pt < confHPtPart2) && (aod::femtouniverseparticle::pt > confLPtPart2); - Partition partsTwoMCrecoBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kCascade)) && (aod::femtouniverseparticle::pt < confHPtPart2) && (aod::femtouniverseparticle::pt > confLPtPart2); + /// Partition for cascades using extended table + Partition partsTwoFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kCascade)) && (aod::femtouniverseparticle::pt < cascparticleconfigs.confHPtPart2) && (aod::femtouniverseparticle::pt > cascparticleconfigs.confLPtPart2); + + /// Partition for cascades using extended MC reco table + Partition partsTwoMCrecoFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kCascade)) && (aod::femtouniverseparticle::pt < cascparticleconfigs.confHPtPart2) && (aod::femtouniverseparticle::pt > cascparticleconfigs.confLPtPart2); + + /// Partition for cascades using bitmask (without extended table) + Partition partsTwoBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kCascade)) && (aod::femtouniverseparticle::pt < cascparticleconfigs.confHPtPart2) && (aod::femtouniverseparticle::pt > cascparticleconfigs.confLPtPart2); + Partition partsTwoMCgenBasic = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pt < cascparticleconfigs.confHPtPart2) && (aod::femtouniverseparticle::pt > cascparticleconfigs.confLPtPart2); /// Histogramming for track particle FemtoUniverseParticleHisto trackHistoPartOnePos; FemtoUniverseParticleHisto trackHistoPartOneNeg; + /// Histogramming for MC track QA + FemtoUniverseParticleHisto trackQAHistos; + /// Histogramming for cascade FemtoUniverseParticleHisto posChildHistos; FemtoUniverseParticleHisto negChildHistos; @@ -166,8 +203,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { FemtoUniverseContainer sameEventCont; FemtoUniverseContainer mixedEventCont; FemtoUniversePairCleaner pairCleaner; + FemtoUniversePairCleaner pairCleanerTrack; FemtoUniversePairCleaner pairCleanerCasc; FemtoUniverseDetaDphiStar pairCloseRejection; + FemtoUniverseDetaDphiStar pairCloseRejectionTrack; FemtoUniverseDetaDphiStar pairCloseRejectionCasc; HistogramRegistry CascQAExtra{"CascQAExtra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -186,7 +225,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { // Table to select cascade daughters // Charges: = +--, +--, +-+, +-+ - static constexpr unsigned int CascChildTable[][3] = {{0, 1, 2}, {0, 1, 1}, {1, 0, 2}, {1, 0, 1}}; + static constexpr std::array, 4> CascChildTable = {{{0, 1, 2}, {0, 1, 1}, {1, 0, 2}, {1, 0, 1}}}; bool invMCascade(float invMassXi, float invMassOmega, int cascType) { @@ -195,73 +234,66 @@ struct femtoUniversePairTaskTrackCascadeExtended { bool isNSigmaTPC(float nsigmaTPCParticle) { - if (std::abs(nsigmaTPCParticle) < confNsigmaTPCParticleChild) { - return true; - } else { - return false; - } + return std::abs(nsigmaTPCParticle) < cascparticleconfigs.confNsigmaTPCParticleChild; } bool isNSigmaTOF(float mom, float nsigmaTOFParticle, float hasTOF) { // Cut only on daughter and bachelor tracks, that have TOF signal - if (mom > confmom && hasTOF == 1) { - if (std::abs(nsigmaTOFParticle) < confNsigmaTOFParticleChild) { - return true; - } else { - return false; - } - } else { - return true; + if (mom > cascparticleconfigs.confmom && hasTOF == 1) { + return std::abs(nsigmaTOFParticle) < cascparticleconfigs.confNsigmaTOFParticleChild; } + return true; } bool isNSigmaCombined(float mom, float nsigmaTPCParticle, float nsigmaTOFParticle, bool hasTOF) { - if (mom <= confmom) { - return (std::abs(nsigmaTPCParticle) < confNsigmaTPCParticle); - } else if (hasTOF == 1) { - return (TMath::Hypot(nsigmaTOFParticle, nsigmaTPCParticle) < confNsigmaCombinedParticle); - } else { - return false; + if (mom <= cascparticleconfigs.confmom) { + return std::abs(nsigmaTPCParticle) < cascparticleconfigs.confNsigmaTPCParticle; } + if (hasTOF) { + return TMath::Hypot(nsigmaTOFParticle, nsigmaTPCParticle) < trackparticleconfigs.confNsigmaCombinedParticle; + } + return false; } template bool isNSigmaCombinedBitmask(float mom, const T& part) { - if (mom <= confmom) { - return ((part.pidCut() & (1u << confTrackChoicePartOne)) != 0); - } else if ((part.pidCut() & 512u) != 0) { - return ((part.pidCut() & (64u << confTrackChoicePartOne)) != 0); - } else { - return false; + if (mom <= cascparticleconfigs.confmom) { + return (part.pidCut() & (1u << trackparticleconfigs.confTrackChoicePartOne)) != 0; } + if ((part.pidCut() & 512u) != 0) { + return (part.pidCut() & (64u << trackparticleconfigs.confTrackChoicePartOne)) != 0; + } + return false; } template - bool isParticleTPC(const T& part, int id, float* partSigma = 0) + bool isParticleTPC(const T& part, int id, float* partSigma = nullptr) { - const float tpcNSigmas[3] = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; - if (partSigma) + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + if (partSigma) { *partSigma = tpcNSigmas[id]; + } return isNSigmaTPC(tpcNSigmas[id]); } template - bool isParticleTOF(const T& part, int id, float* partSigma = 0) + bool isParticleTOF(const T& part, int id, float* partSigma = nullptr) { - const float tofNSigmas[3] = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; - if (partSigma) + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + if (partSigma) { *partSigma = tofNSigmas[id]; + } return isNSigmaTOF(part.p(), tofNSigmas[id], part.tempFitVar()); } template bool isParticleCombined(const T& part, int id) { - const float tpcNSigmas[3] = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; - const float tofNSigmas[3] = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; return isNSigmaCombined(part.p(), tpcNSigmas[id], tofNSigmas[id], (part.pidCut() & 512u) != 0); } @@ -318,8 +350,8 @@ struct femtoUniversePairTaskTrackCascadeExtended { registryMCgen.add("plus/MCgenCasc", "MC gen cascades;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCgen.add("minus/MCgenCasc", "MC gen cascades;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); - registryMCgen.add("plus/MCgenAllPt", "MC gen all;#it{p}_{T} (GeV/c); #eta", {HistType::kTH1F, {{500, 0, 5}}}); - registryMCgen.add("minus/MCgenAllPt", "MC gen all;#it{p}_{T} (GeV/c); #eta", {HistType::kTH1F, {{500, 0, 5}}}); + registryMCgen.add("plus/MCgenAllPt", "MC gen all;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); + registryMCgen.add("minus/MCgenAllPt", "MC gen all;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); registryMCgen.add("plus/MCgenPr", "MC gen protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCgen.add("minus/MCgenPr", "MC gen protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); @@ -331,8 +363,8 @@ struct femtoUniversePairTaskTrackCascadeExtended { registryMCreco.add("plus/MCrecoCascade", "MC reco Cascades;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("minus/MCrecoCascade", "MC reco Cascades;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); - registryMCreco.add("plus/MCrecoAllPt", "MC reco all;#it{p}_{T} (GeV/c); #eta", {HistType::kTH1F, {{500, 0, 5}}}); - registryMCreco.add("minus/MCrecoAllPt", "MC reco all;#it{p}_{T} (GeV/c); #eta", {HistType::kTH1F, {{500, 0, 5}}}); + registryMCreco.add("plus/MCrecoAllPt", "MC reco all;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); + registryMCreco.add("minus/MCrecoAllPt", "MC reco all;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); registryMCreco.add("plus/MCrecoPr", "MC reco protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("minus/MCrecoPr", "MC reco protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); @@ -340,8 +372,9 @@ struct femtoUniversePairTaskTrackCascadeExtended { registryMCreco.add("plus/MCrecoPrPt", "MC reco protons;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); registryMCreco.add("minus/MCrecoPrPt", "MC reco protons;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); - trackHistoPartOnePos.init(&qaRegistry, confTrkTempFitVarpTBins, confTrkTempFitVarBins, confIsMC, confTrkPDGCodePartOne); - trackHistoPartOneNeg.init(&qaRegistry, confTrkTempFitVarpTBins, confTrkTempFitVarBins, confIsMC, confTrkPDGCodePartOne); + trackHistoPartOnePos.init(&qaRegistry, confTrkTempFitVarpTBins, confTrkTempFitVarBins, confIsMC, trackparticleconfigs.confTrkPDGCodePartOne); + trackHistoPartOneNeg.init(&qaRegistry, confTrkTempFitVarpTBins, confTrkTempFitVarBins, confIsMC, trackparticleconfigs.confTrkPDGCodePartOne); + trackQAHistos.init(&qaRegistry, confTrkTempFitVarpTBins, confTrkTempFitVarBins, true, trackparticleconfigs.confTrkPDGCodePartOne.value, true); posChildHistos.init(&qaRegistry, confChildTempFitVarpTBins, confChildTempFitVarBins, false, 0, true); negChildHistos.init(&qaRegistry, confChildTempFitVarpTBins, confChildTempFitVarBins, false, 0, true); bachHistos.init(&qaRegistry, confChildTempFitVarpTBins, confChildTempFitVarBins, false, 0, true, "hBachelor"); @@ -349,46 +382,55 @@ struct femtoUniversePairTaskTrackCascadeExtended { sameEventCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); mixedEventCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); - sameEventCont.setPDGCodes(confTrkPDGCodePartOne, confCascPDGCodePartTwo); - mixedEventCont.setPDGCodes(confTrkPDGCodePartOne, confCascPDGCodePartTwo); + sameEventCont.setPDGCodes(trackparticleconfigs.confTrkPDGCodePartOne, cascparticleconfigs.confCascPDGCode); + mixedEventCont.setPDGCodes(trackparticleconfigs.confTrkPDGCodePartOne, cascparticleconfigs.confCascPDGCode); pairCleaner.init(&qaRegistry); + pairCleanerTrack.init(&qaRegistry); pairCleanerCasc.init(&qaRegistry); - if (confIsCPR.value) { + + if (cprconfigs.confIsCPR.value) { if (doprocessSameEvent || doprocessSameEventBitmask || doprocessMixedEvent || doprocessMixedEventBitmask) - pairCloseRejection.init(&resultRegistry, &qaRegistry, twotracksconfigs.confDeltaEtaAxis, twotracksconfigs.confDeltaPhiStarAxis, confCPRdeltaPhiCutMin.value, confCPRdeltaPhiCutMax.value, confCPRdeltaEtaCutMin.value, confCPRdeltaEtaCutMax.value, confCPRChosenRadii.value, confCPRPlotPerRadii.value, 0, 0, confIsSameSignCPR.value); + pairCloseRejection.init(&resultRegistry, &qaRegistry, twotracksconfigs.confDeltaEtaAxis, twotracksconfigs.confDeltaPhiStarAxis, cprconfigs.confCPRdeltaPhiCutMin.value, cprconfigs.confCPRdeltaPhiCutMax.value, cprconfigs.confCPRdeltaEtaCutMin.value, cprconfigs.confCPRdeltaEtaCutMax.value, cprconfigs.confCPRChosenRadii.value, cprconfigs.confCPRPlotPerRadii.value, 0, 0, cprconfigs.confIsSameSignCPR.value); + if (doprocessSameEventTrack || doprocessSameEventTrackBitmask || doprocessMixedEventTrack || doprocessMixedEventTrackBitmask) + pairCloseRejectionTrack.init(&resultRegistry, &qaRegistry, twotracksconfigs.confDeltaEtaAxis, twotracksconfigs.confDeltaPhiStarAxis, cprconfigs.confCPRdeltaPhiCutMin.value, cprconfigs.confCPRdeltaPhiCutMax.value, cprconfigs.confCPRdeltaEtaCutMin.value, cprconfigs.confCPRdeltaEtaCutMax.value, cprconfigs.confCPRChosenRadii.value, cprconfigs.confCPRPlotPerRadii.value, 0, 0, cprconfigs.confIsSameSignCPR.value); if (doprocessSameEventCasc || doprocessSameEventCascBitmask || doprocessMixedEventCasc || doprocessMixedEventCascBitmask) - pairCloseRejectionCasc.init(&resultRegistry, &qaRegistry, twotracksconfigs.confDeltaEtaAxis, twotracksconfigs.confDeltaPhiStarAxis, confCPRdeltaPhiCutMin.value, confCPRdeltaPhiCutMax.value, confCPRdeltaEtaCutMin.value, confCPRdeltaEtaCutMax.value, confCPRChosenRadii.value, confCPRPlotPerRadii.value, 0, 0, confIsSameSignCPR.value); + pairCloseRejectionCasc.init(&resultRegistry, &qaRegistry, twotracksconfigs.confDeltaEtaAxis, twotracksconfigs.confDeltaPhiStarAxis, cprconfigs.confCPRdeltaPhiCutMin.value, cprconfigs.confCPRdeltaPhiCutMax.value, cprconfigs.confCPRdeltaEtaCutMin.value, cprconfigs.confCPRdeltaEtaCutMax.value, cprconfigs.confCPRChosenRadii.value, cprconfigs.confCPRPlotPerRadii.value, 0, 0, cprconfigs.confIsSameSignCPR.value); } - if (!confLocalEfficiency.value.empty()) { - plocalEffFile = std::unique_ptr(TFile::Open(confLocalEfficiency.value.c_str(), "read")); + if (!ccdbEffLoader.confLocalEfficiency.value.empty()) { + plocalEffFile = std::unique_ptr(TFile::Open(ccdbEffLoader.confLocalEfficiency.value.c_str(), "read")); if (!plocalEffFile || plocalEffFile.get()->IsZombie()) - LOGF(fatal, "Could not load efficiency histogram from %s", confLocalEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram from %s", ccdbEffLoader.confLocalEfficiency.value.c_str()); if (doprocessSameEvent || doprocessSameEventBitmask || doprocessMixedEvent || doprocessMixedEventBitmask) { - pEffHistp1 = (confChargePart1 > 0) ? std::unique_ptr(plocalEffFile.get()->Get("PrPlus")) : std::unique_ptr(plocalEffFile.get()->Get("PrMinus")); // note: works only for protons for now - pEffHistp2 = (confCascType1 == 0 || confCascType1 == 1) ? std::unique_ptr(plocalEffFile.get()->Get("Cascade")) : std::unique_ptr(plocalEffFile.get()->Get("AntiCascade")); + pEffHistp1 = (trackparticleconfigs.confChargePart1 > 0) ? std::unique_ptr(plocalEffFile.get()->Get("PrPlus")) : std::unique_ptr(plocalEffFile.get()->Get("PrMinus")); // note: works only for protons for now + pEffHistp2 = (cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 1) ? std::unique_ptr(plocalEffFile.get()->Get("Cascade")) : std::unique_ptr(plocalEffFile.get()->Get("AntiCascade")); LOGF(info, "Loaded efficiency histograms for track-Cascade."); } else if (doprocessSameEventCasc || doprocessSameEventCascBitmask || doprocessMixedEventCasc || doprocessMixedEventCascBitmask) { - pEffHistp1 = (confCascType1 == 0 || confCascType1 == 1) ? std::unique_ptr(plocalEffFile.get()->Get("Cascade")) : std::unique_ptr(plocalEffFile.get()->Get("AntiCascade")); - pEffHistp2 = (confCascType2 == 0 || confCascType2 == 1) ? std::unique_ptr(plocalEffFile.get()->Get("Cascade")) : std::unique_ptr(plocalEffFile.get()->Get("AntiCascade")); + pEffHistp1 = (cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 1) ? std::unique_ptr(plocalEffFile.get()->Get("Cascade")) : std::unique_ptr(plocalEffFile.get()->Get("AntiCascade")); + pEffHistp2 = (cascparticleconfigs.confCascType2 == 0 || cascparticleconfigs.confCascType2 == 1) ? std::unique_ptr(plocalEffFile.get()->Get("Cascade")) : std::unique_ptr(plocalEffFile.get()->Get("AntiCascade")); LOGF(info, "Loaded efficiency histograms for Cascade-Cascade."); + } else if (doprocessSameEventTrack || doprocessSameEventTrackBitmask || doprocessMixedEventTrack || doprocessMixedEventTrackBitmask) { + pEffHistp1 = (trackparticleconfigs.confChargePart1 > 0) ? std::unique_ptr(plocalEffFile.get()->Get("PrPlus")) : std::unique_ptr(plocalEffFile.get()->Get("PrMinus")); // note: works only for protons for now + pEffHistp2 = (trackparticleconfigs.confChargePart2 > 0) ? std::unique_ptr(plocalEffFile.get()->Get("PrPlus")) : std::unique_ptr(plocalEffFile.get()->Get("PrMinus")); // note: works only for protons for now } - } else if (!confCCDBEfficiency.value.empty()) { + } else if (!ccdbEffLoader.confCCDBEfficiency.value.empty()) { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - ccdb->setCreatedNotAfter(now); + ccdb->setCreatedNotAfter(std::max(ccdbEffLoader.confCCDBNoLaterThanTrack.value, ccdbEffLoader.confCCDBNoLaterThanCasc.value)); if (doprocessSameEvent || doprocessSameEventBitmask || doprocessMixedEvent || doprocessMixedEventBitmask) { - pEffHistp1 = (confChargePart1 > 0) ? std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/PrPlus", now)) : std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/PrMinus", now)); // note: works only for protons for now - pEffHistp2 = (confCascType1 == 0 || confCascType1 == 1) ? std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/Cascade", now)) : std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/AntiCascade", now)); + pEffHistp1 = (trackparticleconfigs.confChargePart1 > 0) ? std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/PrPlus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)) : std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/PrMinus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)); /// works only for protons + pEffHistp2 = (cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 1) ? std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/Cascade", ccdbEffLoader.confCCDBNoLaterThanCasc.value)) : std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/AntiCascade", ccdbEffLoader.confCCDBNoLaterThanCasc.value)); LOGF(info, "Loaded efficiency histograms for track-Cascade from CCDB"); } else if (doprocessSameEventCasc || doprocessSameEventCascBitmask || doprocessMixedEventCasc || doprocessMixedEventCascBitmask) { - pEffHistp1 = (confCascType1 == 0 || confCascType1 == 1) ? std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/Cascade", now)) : std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/AntiCascade", now)); - pEffHistp2 = (confCascType2 == 0 || confCascType2 == 1) ? std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/Cascade", now)) : std::unique_ptr(ccdb->getForTimeStamp(confCCDBEfficiency.value + "/AntiCascade", now)); + pEffHistp1 = (cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 1) ? std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/Cascade", ccdbEffLoader.confCCDBNoLaterThanCasc.value)) : std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/AntiCascade", ccdbEffLoader.confCCDBNoLaterThanCasc.value)); + pEffHistp2 = (cascparticleconfigs.confCascType2 == 0 || cascparticleconfigs.confCascType2 == 1) ? std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/Cascade", ccdbEffLoader.confCCDBNoLaterThanCasc.value)) : std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/AntiCascade", ccdbEffLoader.confCCDBNoLaterThanCasc.value)); LOGF(info, "Loaded efficiency histograms for Cascade-Cascade from CCDB."); + } else if (doprocessSameEventTrack || doprocessSameEventTrackBitmask || doprocessMixedEventTrack || doprocessMixedEventTrackBitmask) { + pEffHistp1 = (trackparticleconfigs.confChargePart1 > 0) ? std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/PrPlus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)) : std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/PrMinus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)); /// works only for protons + pEffHistp2 = (trackparticleconfigs.confChargePart2 > 0) ? std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/PrPlus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)) : std::unique_ptr(ccdb->getForTimeStamp(ccdbEffLoader.confCCDBEfficiency.value + "/PrMinus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)); /// works only for protons } } } @@ -411,18 +453,22 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelor = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); if constexpr (std::experimental::is_detected::value) { - float posChildTPC, negChildTPC, bachelorTPC, posChildTOF, negChildTOF, bachelorTOF; - if (!isParticleTPC(posChild, CascChildTable[confCascType1][0], &posChildTPC) || !isParticleTPC(negChild, CascChildTable[confCascType1][1], &negChildTPC) || !isParticleTPC(bachelor, CascChildTable[confCascType1][2], &bachelorTPC)) + float posChildTPC = 0., negChildTPC = 0., bachelorTPC = 0., posChildTOF = 0., negChildTOF = 0., bachelorTOF = 0.; + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTPC) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTPC) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTPC)) + continue; + + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTOF) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTOF) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTOF)) continue; - if (!isParticleTOF(posChild, CascChildTable[confCascType1][0], &posChildTOF) || !isParticleTOF(negChild, CascChildTable[confCascType1][1], &negChildTOF) || !isParticleTOF(bachelor, CascChildTable[confCascType1][2], &bachelorTOF)) + CascQAExtra.fill(HIST("hMassXiSelected"), part.mLambda()); + CascQAExtra.fill(HIST("hInvMpT"), part.pt(), part.mLambda()); + + if (!invMCascade(part.mLambda(), part.mAntiLambda(), cascparticleconfigs.confCascType1)) continue; CascQAExtra.fill(HIST("hPtXi"), part.pt()); CascQAExtra.fill(HIST("hEtaXi"), part.eta()); CascQAExtra.fill(HIST("hPhiXi"), part.phi()); - CascQAExtra.fill(HIST("hMassXiSelected"), part.mLambda()); - CascQAExtra.fill(HIST("hInvMpT"), part.pt(), part.mLambda()); } } @@ -435,11 +481,14 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelorExt = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); if constexpr (std::experimental::is_detected::value) { - float posChildTPCExt, negChildTPCExt, bachelorTPCExt, posChildTOFExt, negChildTOFExt, bachelorTOFExt; - if (!isParticleTPC(posChildExt, CascChildTable[confCascType1][0], &posChildTPCExt) || !isParticleTPC(negChildExt, CascChildTable[confCascType1][1], &negChildTPCExt) || !isParticleTPC(bachelorExt, CascChildTable[confCascType1][2], &bachelorTPCExt)) + float posChildTPCExt = 0., negChildTPCExt = 0., bachelorTPCExt = 0., posChildTOFExt = 0., negChildTOFExt = 0., bachelorTOFExt = 0.; + if (!isParticleTPC(posChildExt, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTPCExt) || !isParticleTPC(negChildExt, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTPCExt) || !isParticleTPC(bachelorExt, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTPCExt)) + continue; + + if (!isParticleTOF(posChildExt, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTOFExt) || !isParticleTOF(negChildExt, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTOFExt) || !isParticleTOF(bachelorExt, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTOFExt)) continue; - if (!isParticleTOF(posChildExt, CascChildTable[confCascType1][0], &posChildTOFExt) || !isParticleTOF(negChildExt, CascChildTable[confCascType1][1], &negChildTOFExt) || !isParticleTOF(bachelorExt, CascChildTable[confCascType1][2], &bachelorTOFExt)) + if (!invMCascade(part.mLambda(), part.mAntiLambda(), cascparticleconfigs.confCascType1)) continue; CascQAExtra.fill(HIST("hDCAV0Daughters"), casc.dcaV0daughters()); @@ -456,9 +505,52 @@ struct femtoUniversePairTaskTrackCascadeExtended { } } - /// track - cascade correlations + // Additional cascade QA plots for MC template - void doSameEvent(const FilteredFDCollision& col, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) + void doCascadeMCQA([[maybe_unused]] const FilteredFDCollision& col, const TableType& parts, PartitionType& partsTwo, aod::FdMCParticles const& mcparts) + { + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + // Basic particle loop + for (const auto& part : groupPartsTwo) { + auto mcPartId = part.fdMCParticleId(); + if (mcPartId == -1) + continue; // no MC particle + const auto& mcpart = mcparts.iteratorAt(mcPartId); + + if (cascparticleconfigs.confQARejectByPDG && ((cascparticleconfigs.confCascType1 == 0 && mcpart.pdgMCTruth() == kOmegaMinus) || (cascparticleconfigs.confCascType1 == 1 && mcpart.pdgMCTruth() == kXiMinus) || (cascparticleconfigs.confCascType1 == 2 && mcpart.pdgMCTruth() == kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 3 && mcpart.pdgMCTruth() == kXiPlusBar))) + continue; + + CascQAExtra.fill(HIST("hMassXi"), part.mLambda()); + + const auto& posChild = parts.iteratorAt(part.globalIndex() - 3 - parts.begin().globalIndex()); + const auto& negChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); + const auto& bachelor = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); + + if constexpr (std::experimental::is_detected::value) { + float posChildTPC = 0., negChildTPC = 0., bachelorTPC = 0., posChildTOF = 0., negChildTOF = 0., bachelorTOF = 0.; + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTPC) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTPC) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTPC)) + continue; + + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTOF) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTOF) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTOF)) + continue; + + CascQAExtra.fill(HIST("hMassXiSelected"), part.mLambda()); + CascQAExtra.fill(HIST("hInvMpT"), part.pt(), part.mLambda()); + + if (!invMCascade(part.mLambda(), part.mAntiLambda(), cascparticleconfigs.confCascType1)) + continue; + + CascQAExtra.fill(HIST("hPtXi"), part.pt()); + CascQAExtra.fill(HIST("hEtaXi"), part.eta()); + CascQAExtra.fill(HIST("hPhiXi"), part.phi()); + } + } + } + + /// track - cascade correlations + template + void doSameEvent(const FilteredFDCollision& col, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { const auto& magFieldTesla = col.magField(); @@ -467,10 +559,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { eventHisto.fillQA(col); - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : static_cast(col.multNtr()); for (const auto& part : groupPartsTwo) { - if (!invMCascade(part.mLambda(), part.mAntiLambda(), confCascType1)) /// mLambda stores Xi mass, mAntiLambda stores Omega mass + if (!invMCascade(part.mLambda(), part.mAntiLambda(), cascparticleconfigs.confCascType1)) /// mLambda stores Xi mass, mAntiLambda stores Omega mass continue; const auto& posChild = parts.iteratorAt(part.globalIndex() - 3 - parts.begin().globalIndex()); @@ -478,11 +570,11 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelor = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); /// Child particles must pass this condition to be selected if constexpr (std::experimental::is_detected::value) { - float posChildTPC, negChildTPC, bachelorTPC, posChildTOF, negChildTOF, bachelorTOF; - if (!isParticleTPC(posChild, CascChildTable[confCascType1][0], &posChildTPC) || !isParticleTPC(negChild, CascChildTable[confCascType1][1], &negChildTPC) || !isParticleTPC(bachelor, CascChildTable[confCascType1][2], &bachelorTPC)) + float posChildTPC = 0., negChildTPC = 0., bachelorTPC = 0., posChildTOF = 0., negChildTOF = 0., bachelorTOF = 0.; + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTPC) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTPC) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTPC)) continue; - if (!isParticleTOF(posChild, CascChildTable[confCascType1][0], &posChildTOF) || !isParticleTOF(negChild, CascChildTable[confCascType1][1], &negChildTOF) || !isParticleTOF(bachelor, CascChildTable[confCascType1][2], &bachelorTOF)) + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1][0], &posChildTOF) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1][1], &negChildTOF) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2], &bachelorTOF)) continue; posChildHistos.fillQA(posChild); @@ -499,14 +591,14 @@ struct femtoUniversePairTaskTrackCascadeExtended { cascQAHistos.fillQA(part); } else { - if ((posChild.pidCut() & (1u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1 || confCascType1 == 3) && (part.pidCut() & 7) != 7) || ((confCascType1 == 0 || confCascType1 == 2) && (part.pidCut() & 56) != 56)) + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1 || cascparticleconfigs.confCascType1 == 3) && (part.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 2) && (part.pidCut() & 56) != 56)) continue; } else { - if ((posChild.pidCut() & (8u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; } @@ -521,19 +613,19 @@ struct femtoUniversePairTaskTrackCascadeExtended { for (const auto& part : groupPartsOne) { if constexpr (std::experimental::is_detected::value) { /// PID plot for track particle - const float tpcNSigmas[3] = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; - const float tofNSigmas[3] = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; - if (!isNSigmaCombined(part.p(), tpcNSigmas[confTrackChoicePartOne], tofNSigmas[confTrackChoicePartOne], (part.pidCut() & 512u) != 0)) + if (!isNSigmaCombined(part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartOne], tofNSigmas[trackparticleconfigs.confTrackChoicePartOne], (part.pidCut() & 512u) != 0)) continue; if (part.mAntiLambda() > 0) { - qaRegistry.fill(HIST("Tracks_pos/nSigmaTPC"), part.p(), tpcNSigmas[confTrackChoicePartOne]); - qaRegistry.fill(HIST("Tracks_pos/nSigmaTOF"), part.p(), tofNSigmas[confTrackChoicePartOne]); + qaRegistry.fill(HIST("Tracks_pos/nSigmaTPC"), part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartOne]); + qaRegistry.fill(HIST("Tracks_pos/nSigmaTOF"), part.p(), tofNSigmas[trackparticleconfigs.confTrackChoicePartOne]); trackHistoPartOnePos.fillQA(part); } else if (part.mAntiLambda() < 0) { - qaRegistry.fill(HIST("Tracks_neg/nSigmaTPC"), part.p(), tpcNSigmas[confTrackChoicePartOne]); - qaRegistry.fill(HIST("Tracks_neg/nSigmaTOF"), part.p(), tofNSigmas[confTrackChoicePartOne]); + qaRegistry.fill(HIST("Tracks_neg/nSigmaTPC"), part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartOne]); + qaRegistry.fill(HIST("Tracks_neg/nSigmaTOF"), part.p(), tofNSigmas[trackparticleconfigs.confTrackChoicePartOne]); trackHistoPartOneNeg.fillQA(part); } } else { @@ -550,11 +642,11 @@ struct femtoUniversePairTaskTrackCascadeExtended { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { // Cascade inv mass cut (mLambda stores Xi mass, mAntiLambda stores Omega mass) - if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), confCascType1)) + if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), cascparticleconfigs.confCascType1)) continue; // PID if constexpr (std::experimental::is_detected::value) { - if (!isParticleCombined(p1, confTrackChoicePartOne)) + if (!isParticleCombined(p1, trackparticleconfigs.confTrackChoicePartOne)) continue; } else { if (!isNSigmaCombinedBitmask(p1.p(), p1)) { @@ -565,7 +657,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { if (!pairCleaner.isCleanPair(p1, p2, parts)) { continue; } - if (confIsCPR.value) { + if (cprconfigs.confIsCPR.value) { if (pairCloseRejection.isClosePair(p1, p2, parts, magFieldTesla, femto_universe_container::EventType::same)) { continue; } @@ -576,25 +668,28 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelor = parts.iteratorAt(p2.globalIndex() - 1 - parts.begin().globalIndex()); /// Child particles must pass this condition to be selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, CascChildTable[confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[confCascType1][2])) + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; - if (!isParticleTOF(posChild, CascChildTable[confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[confCascType1][2])) + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; } else { - if ((posChild.pidCut() & (1u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1 || confCascType1 == 3) && (p2.pidCut() & 7) != 7) || ((confCascType1 == 0 || confCascType1 == 2) && (p2.pidCut() & 56) != 56)) + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1 || cascparticleconfigs.confCascType1 == 3) && (p2.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 2) && (p2.pidCut() & 56) != 56)) continue; } else { - if ((posChild.pidCut() & (8u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; } } float weight = 1.0f; if (pEffHistp1) weight = pEffHistp1.get()->GetBinContent(pEffHistp1->FindBin(p1.pt(), p1.eta())) * pEffHistp2.get()->GetBinContent(pEffHistp2->FindBin(p2.pt(), p2.eta())); - sameEventCont.setPair(p1, p2, multCol, confUse3D, weight); + if constexpr (std::is_same::value) + sameEventCont.setPair(p1, p2, multCol, confUse3D, weight); + else + sameEventCont.setPair(p1, p2, multCol, confUse3D, weight); } } @@ -604,15 +699,21 @@ struct femtoUniversePairTaskTrackCascadeExtended { } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processCascadeQA, "Enable additional QA for cascades", false); + void processCascadeMCQA([[maybe_unused]] const FilteredFDCollision& col, const FemtoRecoFullParticles& parts, aod::FdMCParticles const& mcparts) + { + doCascadeMCQA(col, parts, partsTwoMCrecoFull, mcparts); + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processCascadeMCQA, "Enable additional QA for cascades in MC (when processCascadeQA is off)", false); + void processSameEvent(const FilteredFDCollision& col, const FemtoFullParticles& parts) { - doSameEvent(col, parts, partsOneFull, partsTwoFull); + doSameEvent(col, parts, partsTrackOneFull, partsTwoFull); } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEvent, "Enable processing same event for track - cascade", false); void processSameEventBitmask(const FilteredFDCollision& col, const aod::FDParticles& parts) { - doSameEvent(col, parts, partsOneBasic, partsTwoBasic); + doSameEvent(col, parts, partsTrackOneBasic, partsTwoBasic); } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEventBitmask, "Enable processing same event for track - cascade using bitmask for PID", false); @@ -628,54 +729,53 @@ struct femtoUniversePairTaskTrackCascadeExtended { eventHisto.fillQA(col); - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : static_cast(col.multNtr()); for (const auto& part : groupPartsTwo) { - if (!invMCascade(part.mLambda(), part.mAntiLambda(), confCascType1)) /// mLambda stores Xi mass, mAntiLambda stores Omega mass + if (!invMCascade(part.mLambda(), part.mAntiLambda(), cascparticleconfigs.confCascType1)) /// mLambda stores Xi mass, mAntiLambda stores Omega mass continue; - if constexpr (std::experimental::is_detected::value) - cascQAHistos.fillQA(part); - else - cascQAHistos.fillQA(part); - const auto& posChild = parts.iteratorAt(part.globalIndex() - 3 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& bachelor = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); /// Check daughters of first cascade if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, CascChildTable[confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[confCascType1][2])) + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; - if (!isParticleTOF(posChild, CascChildTable[confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[confCascType1][2])) + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; posChildHistos.fillQA(posChild); negChildHistos.fillQA(negChild); bachHistos.fillQABase(bachelor, HIST("hBachelor")); + cascQAHistos.fillQA(part); } else { - if ((posChild.pidCut() & (1u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1 || confCascType1 == 3) && (part.pidCut() & 7) != 7) || ((confCascType1 == 0 || confCascType1 == 2) && (part.pidCut() & 56) != 56)) + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1 || cascparticleconfigs.confCascType1 == 3) && (part.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 2) && (part.pidCut() & 56) != 56)) continue; } else { - if ((posChild.pidCut() & (8u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; } posChildHistos.fillQA(posChild); negChildHistos.fillQA(negChild); bachHistos.fillQABase(bachelor, HIST("hBachelor")); + cascQAHistos.fillQA(part); } } auto pairDuplicateCheckFunc = [&](auto& p1, auto& p2) -> void { // Cascade inv mass cut for p1 (mLambda stores Xi mass, mAntiLambda stores Omega mass) - if (!invMCascade(p1.mLambda(), p1.mAntiLambda(), confCascType1)) + if (!invMCascade(p1.mLambda(), p1.mAntiLambda(), cascparticleconfigs.confCascType1)) { return; + } // Cascade inv mass cut for p2 - if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), confCascType2)) + if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), cascparticleconfigs.confCascType2)) { return; + } // track cleaning & checking for duplicate pairs if (!pairCleanerCasc.isCleanPair(p1, p2, parts)) { // mark for rejection the cascade that share a daughter with the other cascade and has a better cosPA value @@ -694,31 +794,39 @@ struct femtoUniversePairTaskTrackCascadeExtended { }; auto pairProcessFunc = [&](auto& p1, auto& p2) -> bool { - if (cascDuplicates.contains(p1.globalIndex()) || cascDuplicates.contains(p2.globalIndex())) + if (cascDuplicates.contains(p1.globalIndex()) || cascDuplicates.contains(p2.globalIndex())) { return false; - if (!invMCascade(p1.mLambda(), p1.mAntiLambda(), confCascType1)) + } + if (!invMCascade(p1.mLambda(), p1.mAntiLambda(), cascparticleconfigs.confCascType1)) { return false; - if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), confCascType2)) + } + if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), cascparticleconfigs.confCascType2)) { return false; + } const auto& posChild1 = parts.iteratorAt(p1.globalIndex() - 3 - parts.begin().globalIndex()); const auto& negChild1 = parts.iteratorAt(p1.globalIndex() - 2 - parts.begin().globalIndex()); const auto& bachelor1 = parts.iteratorAt(p1.globalIndex() - 1 - parts.begin().globalIndex()); /// Child particles must pass this condition to be selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild1, CascChildTable[confCascType1][0]) || !isParticleTPC(negChild1, CascChildTable[confCascType1][1]) || !isParticleTPC(bachelor1, CascChildTable[confCascType1][2])) + if (!isParticleTPC(posChild1, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTPC(negChild1, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTPC(bachelor1, CascChildTable[cascparticleconfigs.confCascType1][2])) { return false; - if (!isParticleTOF(posChild1, CascChildTable[confCascType1][0]) || !isParticleTOF(negChild1, CascChildTable[confCascType1][1]) || !isParticleTOF(bachelor1, CascChildTable[confCascType1][2])) + } + if (!isParticleTOF(posChild1, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTOF(negChild1, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTOF(bachelor1, CascChildTable[cascparticleconfigs.confCascType1][2])) { return false; + } } else { - if ((posChild1.pidCut() & (1u << CascChildTable[confCascType1][0])) == 0 || (negChild1.pidCut() & (1u << CascChildTable[confCascType1][1])) == 0 || (bachelor1.pidCut() & (1u << CascChildTable[confCascType1][2])) == 0) + if ((posChild1.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild1.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor1.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) { return false; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1 || confCascType1 == 3) && (p1.pidCut() & 7) != 7) || ((confCascType1 == 0 || confCascType1 == 2) && (p1.pidCut() & 56) != 56)) + } + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1 || cascparticleconfigs.confCascType1 == 3) && (p1.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 2) && (p1.pidCut() & 56) != 56)) { return false; + } } else { - if ((posChild1.pidCut() & (8u << CascChildTable[confCascType1][0])) == 0 || (negChild1.pidCut() & (8u << CascChildTable[confCascType1][1])) == 0 || (bachelor1.pidCut() & (8u << CascChildTable[confCascType1][2])) == 0) + if ((posChild1.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild1.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor1.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) { return false; + } } } @@ -727,23 +835,28 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelor2 = parts.iteratorAt(p2.globalIndex() - 1 - parts.begin().globalIndex()); /// Child particles must pass this condition to be selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild2, CascChildTable[confCascType2][0]) || !isParticleTPC(negChild2, CascChildTable[confCascType2][1]) || !isParticleTPC(bachelor2, CascChildTable[confCascType2][2])) + if (!isParticleTPC(posChild2, CascChildTable[cascparticleconfigs.confCascType2][0]) || !isParticleTPC(negChild2, CascChildTable[cascparticleconfigs.confCascType2][1]) || !isParticleTPC(bachelor2, CascChildTable[cascparticleconfigs.confCascType2][2])) { return false; - if (!isParticleTOF(posChild2, CascChildTable[confCascType2][0]) || !isParticleTOF(negChild2, CascChildTable[confCascType2][1]) || !isParticleTOF(bachelor2, CascChildTable[confCascType2][2])) + } + if (!isParticleTOF(posChild2, CascChildTable[cascparticleconfigs.confCascType2][0]) || !isParticleTOF(negChild2, CascChildTable[cascparticleconfigs.confCascType2][1]) || !isParticleTOF(bachelor2, CascChildTable[cascparticleconfigs.confCascType2][2])) { return false; + } } else { - if ((posChild2.pidCut() & (1u << CascChildTable[confCascType2][0])) == 0 || (negChild2.pidCut() & (1u << CascChildTable[confCascType2][1])) == 0 || (bachelor2.pidCut() & (1u << CascChildTable[confCascType2][2])) == 0) + if ((posChild2.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType2][0])) == 0 || (negChild2.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType2][1])) == 0 || (bachelor2.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType2][2])) == 0) { return false; - if (confUseStrangenessTOF) { - if (((confCascType2 == 1 || confCascType2 == 3) && (p2.pidCut() & 7) != 7) || ((confCascType2 == 0 || confCascType2 == 2) && (p2.pidCut() & 56) != 56)) + } + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType2 == 1 || cascparticleconfigs.confCascType2 == 3) && (p2.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType2 == 0 || cascparticleconfigs.confCascType2 == 2) && (p2.pidCut() & 56) != 56)) { return false; + } } else { - if ((posChild2.pidCut() & (8u << CascChildTable[confCascType2][0])) == 0 || (negChild2.pidCut() & (8u << CascChildTable[confCascType2][1])) == 0 || (bachelor2.pidCut() & (8u << CascChildTable[confCascType2][2])) == 0) + if ((posChild2.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType2][0])) == 0 || (negChild2.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType2][1])) == 0 || (bachelor2.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType2][2])) == 0) { return false; + } } } - if (confIsCPR.value) { + if (cprconfigs.confIsCPR.value) { if (pairCloseRejectionCasc.isClosePair(p1, p2, parts, magFieldTesla, femto_universe_container::EventType::same)) { return false; } @@ -779,15 +892,152 @@ struct femtoUniversePairTaskTrackCascadeExtended { } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEventCascBitmask, "Enable processing same event for cascade - cascade using bitmask for PID", false); - /// track - cascade correlations + /// track-track correlations template - void doMixedEvent(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) + void doSameEventTrack(const FilteredFDCollision& col, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + const auto& magFieldTesla = col.magField(); + + auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + eventHisto.fillQA(col); + + const int multCol = confUseCent ? static_cast(col.multV0M()) : static_cast(col.multNtr()); + + for (const auto& part : groupPartsOne) { + if constexpr (std::experimental::is_detected::value) { + /// PID plot for the first track particle + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + + if (!isNSigmaCombined(part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartOne], tofNSigmas[trackparticleconfigs.confTrackChoicePartOne], (part.pidCut() & 512u) != 0)) + continue; + + if (part.mAntiLambda() > 0) { + qaRegistry.fill(HIST("Tracks_pos/nSigmaTPC"), part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartOne]); + qaRegistry.fill(HIST("Tracks_pos/nSigmaTOF"), part.p(), tofNSigmas[trackparticleconfigs.confTrackChoicePartOne]); + trackHistoPartOnePos.fillQA(part); + } else if (part.mAntiLambda() < 0) { + qaRegistry.fill(HIST("Tracks_neg/nSigmaTPC"), part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartOne]); + qaRegistry.fill(HIST("Tracks_neg/nSigmaTOF"), part.p(), tofNSigmas[trackparticleconfigs.confTrackChoicePartOne]); + trackHistoPartOneNeg.fillQA(part); + } + } else { + if (!isNSigmaCombinedBitmask(part.p(), part)) { + continue; + } + if (part.mAntiLambda() > 0) { + trackHistoPartOnePos.fillQA(part); + } else if (part.mAntiLambda() < 0) { + trackHistoPartOneNeg.fillQA(part); + } + } + } + + if (trackparticleconfigs.confChargePart1 != trackparticleconfigs.confChargePart2) { + for (const auto& part : groupPartsTwo) { + if constexpr (std::experimental::is_detected::value) { + /// PID plot for the first track particle + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + + if (!isNSigmaCombined(part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartTwo], tofNSigmas[trackparticleconfigs.confTrackChoicePartTwo], (part.pidCut() & 512u) != 0)) + continue; + + if (part.mAntiLambda() > 0) { + qaRegistry.fill(HIST("Tracks_pos/nSigmaTPC"), part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartTwo]); + qaRegistry.fill(HIST("Tracks_pos/nSigmaTOF"), part.p(), tofNSigmas[trackparticleconfigs.confTrackChoicePartTwo]); + trackHistoPartOnePos.fillQA(part); + } else if (part.mAntiLambda() < 0) { + qaRegistry.fill(HIST("Tracks_neg/nSigmaTPC"), part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartTwo]); + qaRegistry.fill(HIST("Tracks_neg/nSigmaTOF"), part.p(), tofNSigmas[trackparticleconfigs.confTrackChoicePartTwo]); + trackHistoPartOneNeg.fillQA(part); + } + } else { + if (!isNSigmaCombinedBitmask(part.p(), part)) { + continue; + } + if (part.mAntiLambda() > 0) { + trackHistoPartOnePos.fillQA(part); + } else if (part.mAntiLambda() < 0) { + trackHistoPartOneNeg.fillQA(part); + } + } + } + } + + auto pairProcessFunc = [&](auto& p1, auto& p2) -> void { + // PID for the first track particle + if constexpr (std::experimental::is_detected::value) { + if (!isParticleCombined(p1, trackparticleconfigs.confTrackChoicePartOne)) { + return; + } + } else { + if (!isNSigmaCombinedBitmask(p1.p(), p1)) { + return; + } + } + // PID for the second track particle + if constexpr (std::experimental::is_detected::value) { + if (!isParticleCombined(p2, trackparticleconfigs.confTrackChoicePartTwo)) { + return; + } + } else { + if (!isNSigmaCombinedBitmask(p2.p(), p2)) { + return; + } + } + // track cleaning + if (!pairCleanerTrack.isCleanPair(p1, p2, parts)) { + return; + } + + if (cprconfigs.confIsCPR.value) { + if (pairCloseRejectionTrack.isClosePair(p1, p2, parts, magFieldTesla, femto_universe_container::EventType::same)) { + return; + } + } + + float weight = 1.0f; + if (pEffHistp1) + weight = pEffHistp1.get()->GetBinContent(pEffHistp1->FindBin(p1.pt(), p1.eta())) * pEffHistp2.get()->GetBinContent(pEffHistp2->FindBin(p2.pt(), p2.eta())); + sameEventCont.setPair(p1, p2, multCol, confUse3D, weight); + }; + + /// Now build the combinations for track-track particles + if (trackparticleconfigs.confTrackChoicePartOne == trackparticleconfigs.confTrackChoicePartTwo && trackparticleconfigs.confChargePart1 == trackparticleconfigs.confChargePart2) { + for (const auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsOne, groupPartsTwo))) { + pairProcessFunc(p1, p2); + } + } else { + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + pairProcessFunc(p1, p2); + } + } + } + + void processSameEventTrack(const FilteredFDCollision& col, const FemtoFullParticles& parts) + { + doSameEventTrack(col, parts, partsTrackOneFull, partsTrackTwoFull); + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEventTrack, "Enable processing same event for track - track", false); + + void processSameEventTrackBitmask(const FilteredFDCollision& col, const aod::FDParticles& parts) + { + doSameEventTrack(col, parts, partsTrackOneBasic, partsTrackTwoBasic); + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEventTrackBitmask, "Enable processing same event for track - track using bitmask for PID", false); + + /// track - cascade correlations + template + void doMixedEvent(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) + { + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); @@ -800,11 +1050,11 @@ struct femtoUniversePairTaskTrackCascadeExtended { } for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { // Cascade inv mass cut (mLambda stores Xi mass, mAntiLambda stores Omega mass) - if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), confCascType1)) + if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), cascparticleconfigs.confCascType1)) continue; // PID if constexpr (std::experimental::is_detected::value) { - if (!isParticleCombined(p1, confTrackChoicePartOne)) + if (!isParticleCombined(p1, trackparticleconfigs.confTrackChoicePartOne)) continue; } else { if (!isNSigmaCombinedBitmask(p1.p(), p1)) { @@ -817,18 +1067,18 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelor = parts.iteratorAt(p2.globalIndex() - 1 - parts.begin().globalIndex()); /// Child particles must pass this condition to be selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, CascChildTable[confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[confCascType1][2])) + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; - if (!isParticleTOF(posChild, CascChildTable[confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[confCascType1][2])) + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; } else { - if ((posChild.pidCut() & (1u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1 || confCascType1 == 3) && (p2.pidCut() & 7) != 7) || ((confCascType1 == 0 || confCascType1 == 2) && (p2.pidCut() & 56) != 56)) + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1 || cascparticleconfigs.confCascType1 == 3) && (p2.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 2) && (p2.pidCut() & 56) != 56)) continue; } else { - if ((posChild.pidCut() & (8u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; } } @@ -837,7 +1087,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { if (!pairCleaner.isCleanPair(p1, p2, parts)) { continue; } - if (confIsCPR.value) { + if (cprconfigs.confIsCPR.value) { if (pairCloseRejection.isClosePair(p1, p2, parts, magFieldTesla1, femto_universe_container::EventType::mixed)) { continue; } @@ -846,7 +1096,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { float weight = 1.0f; if (pEffHistp1) weight = pEffHistp1.get()->GetBinContent(pEffHistp1->FindBin(p1.pt(), p1.eta())) * pEffHistp2.get()->GetBinContent(pEffHistp2->FindBin(p2.pt(), p2.eta())); - mixedEventCont.setPair(p1, p2, multCol, confUse3D, weight); + if constexpr (std::is_same::value) + mixedEventCont.setPair(p1, p2, multCol, confUse3D, weight); + else + mixedEventCont.setPair(p1, p2, multCol, confUse3D, weight); } }; @@ -865,13 +1118,13 @@ struct femtoUniversePairTaskTrackCascadeExtended { void processMixedEvent(const FilteredFDCollisions& cols, const FemtoFullParticles& parts) { - doMixedEvent(cols, parts, partsOneFull, partsTwoFull); + doMixedEvent(cols, parts, partsTrackOneFull, partsTwoFull); } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMixedEvent, "Enable processing mixed event for track - cascade", false); void processMixedEventBitmask(const FilteredFDCollisions& cols, const aod::FDParticles& parts) { - doMixedEvent(cols, parts, partsOneBasic, partsTwoBasic); + doMixedEvent(cols, parts, partsTrackOneBasic, partsTwoBasic); } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMixedEventBitmask, "Enable processing mixed event for track - cascade using bitmask for PID", false); @@ -879,10 +1132,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { template void doMixedEventCasc(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); auto groupPartsOne = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); @@ -895,10 +1148,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { } for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { // Cascade inv mass cut for p1 (mLambda stores Xi mass, mAntiLambda stores Omega mass) - if (!invMCascade(p1.mLambda(), p1.mAntiLambda(), confCascType1)) + if (!invMCascade(p1.mLambda(), p1.mAntiLambda(), cascparticleconfigs.confCascType1)) continue; // Cascade inv mass cut for p2 - if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), confCascType2)) + if (!invMCascade(p2.mLambda(), p2.mAntiLambda(), cascparticleconfigs.confCascType2)) continue; const auto& posChild1 = parts.iteratorAt(p1.globalIndex() - 3 - parts.begin().globalIndex()); @@ -906,18 +1159,18 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelor1 = parts.iteratorAt(p1.globalIndex() - 1 - parts.begin().globalIndex()); /// Child particles must pass this condition to be selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild1, CascChildTable[confCascType1][0]) || !isParticleTPC(negChild1, CascChildTable[confCascType1][1]) || !isParticleTPC(bachelor1, CascChildTable[confCascType1][2])) + if (!isParticleTPC(posChild1, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTPC(negChild1, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTPC(bachelor1, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; - if (!isParticleTOF(posChild1, CascChildTable[confCascType1][0]) || !isParticleTOF(negChild1, CascChildTable[confCascType1][1]) || !isParticleTOF(bachelor1, CascChildTable[confCascType1][2])) + if (!isParticleTOF(posChild1, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTOF(negChild1, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTOF(bachelor1, CascChildTable[cascparticleconfigs.confCascType1][2])) continue; } else { - if ((posChild1.pidCut() & (1u << CascChildTable[confCascType1][0])) == 0 || (negChild1.pidCut() & (1u << CascChildTable[confCascType1][1])) == 0 || (bachelor1.pidCut() & (1u << CascChildTable[confCascType1][2])) == 0) + if ((posChild1.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild1.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor1.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1 || confCascType1 == 3) && (p1.pidCut() & 7) != 7) || ((confCascType1 == 0 || confCascType1 == 2) && (p1.pidCut() & 56) != 56)) + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1 || cascparticleconfigs.confCascType1 == 3) && (p1.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0 || cascparticleconfigs.confCascType1 == 2) && (p1.pidCut() & 56) != 56)) continue; } else { - if ((posChild1.pidCut() & (8u << CascChildTable[confCascType1][0])) == 0 || (negChild1.pidCut() & (8u << CascChildTable[confCascType1][1])) == 0 || (bachelor1.pidCut() & (8u << CascChildTable[confCascType1][2])) == 0) + if ((posChild1.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild1.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor1.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) continue; } } @@ -927,18 +1180,18 @@ struct femtoUniversePairTaskTrackCascadeExtended { const auto& bachelor2 = parts.iteratorAt(p2.globalIndex() - 1 - parts.begin().globalIndex()); /// Child particles must pass this condition to be selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild2, CascChildTable[confCascType2][0]) || !isParticleTPC(negChild2, CascChildTable[confCascType2][1]) || !isParticleTPC(bachelor2, CascChildTable[confCascType2][2])) + if (!isParticleTPC(posChild2, CascChildTable[cascparticleconfigs.confCascType2][0]) || !isParticleTPC(negChild2, CascChildTable[cascparticleconfigs.confCascType2][1]) || !isParticleTPC(bachelor2, CascChildTable[cascparticleconfigs.confCascType2][2])) continue; - if (!isParticleTOF(posChild2, CascChildTable[confCascType2][0]) || !isParticleTOF(negChild2, CascChildTable[confCascType2][1]) || !isParticleTOF(bachelor2, CascChildTable[confCascType2][2])) + if (!isParticleTOF(posChild2, CascChildTable[cascparticleconfigs.confCascType2][0]) || !isParticleTOF(negChild2, CascChildTable[cascparticleconfigs.confCascType2][1]) || !isParticleTOF(bachelor2, CascChildTable[cascparticleconfigs.confCascType2][2])) continue; } else { - if ((posChild2.pidCut() & (1u << CascChildTable[confCascType2][0])) == 0 || (negChild2.pidCut() & (1u << CascChildTable[confCascType2][1])) == 0 || (bachelor2.pidCut() & (1u << CascChildTable[confCascType2][2])) == 0) + if ((posChild2.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType2][0])) == 0 || (negChild2.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType2][1])) == 0 || (bachelor2.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType2][2])) == 0) continue; - if (confUseStrangenessTOF) { - if (((confCascType2 == 1 || confCascType2 == 3) && (p2.pidCut() & 7) != 7) || ((confCascType2 == 0 || confCascType2 == 2) && (p2.pidCut() & 56) != 56)) + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType2 == 1 || cascparticleconfigs.confCascType2 == 3) && (p2.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType2 == 0 || cascparticleconfigs.confCascType2 == 2) && (p2.pidCut() & 56) != 56)) continue; } else { - if ((posChild2.pidCut() & (8u << CascChildTable[confCascType2][0])) == 0 || (negChild2.pidCut() & (8u << CascChildTable[confCascType2][1])) == 0 || (bachelor2.pidCut() & (8u << CascChildTable[confCascType2][2])) == 0) + if ((posChild2.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType2][0])) == 0 || (negChild2.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType2][1])) == 0 || (bachelor2.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType2][2])) == 0) continue; } } @@ -946,7 +1199,7 @@ struct femtoUniversePairTaskTrackCascadeExtended { if (!pairCleanerCasc.isCleanPair(p1, p2, parts)) { continue; } - if (confIsCPR.value) { + if (cprconfigs.confIsCPR.value) { if (pairCloseRejectionCasc.isClosePair(p1, p2, parts, magFieldTesla1, femto_universe_container::EventType::mixed)) { continue; } @@ -972,55 +1225,123 @@ struct femtoUniversePairTaskTrackCascadeExtended { } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMixedEventCascBitmask, "Enable processing mixed event for cascade - cascade using bitmask for PID", false); + /// track-track correlations + template + void doMixedEventTrack(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) + { + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; + + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); + + auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; + } + + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + // PID for the first track particle + if constexpr (std::experimental::is_detected::value) { + if (!isParticleCombined(p1, trackparticleconfigs.confTrackChoicePartOne)) + continue; + } else { + if (!isNSigmaCombinedBitmask(p1.p(), p1)) { + continue; + } + } + // PID for the second track particle + if constexpr (std::experimental::is_detected::value) { + if (!isParticleCombined(p2, trackparticleconfigs.confTrackChoicePartTwo)) + continue; + } else { + if (!isNSigmaCombinedBitmask(p2.p(), p2)) { + continue; + } + } + // track cleaning + if (!pairCleanerTrack.isCleanPair(p1, p2, parts)) { + continue; + } + + if (cprconfigs.confIsCPR.value) { + if (pairCloseRejectionTrack.isClosePair(p1, p2, parts, magFieldTesla1, femto_universe_container::EventType::mixed)) { + continue; + } + } + + float weight = 1.0f; + if (pEffHistp1) + weight = pEffHistp1.get()->GetBinContent(pEffHistp1->FindBin(p1.pt(), p1.eta())) * pEffHistp2.get()->GetBinContent(pEffHistp2->FindBin(p2.pt(), p2.eta())); + mixedEventCont.setPair(p1, p2, multCol, confUse3D, weight); + } + } + } + + void processMixedEventTrack(const FilteredFDCollisions& cols, const FemtoFullParticles& parts) + { + doMixedEventTrack(cols, parts, partsTrackOneFull, partsTrackTwoFull); + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMixedEventTrack, "Enable processing mixed event for track - track", false); + + void processMixedEventTrackBitmask(const FilteredFDCollisions& cols, const aod::FDParticles& parts) + { + doMixedEventTrack(cols, parts, partsTrackOneBasic, partsTrackTwoBasic); + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMixedEventTrackBitmask, "Enable processing mixed event for track - track using bitmask for PID", false); + // MC truth for track - cascade void processSameEventMCgen(const FilteredFDCollision& col, [[maybe_unused]] const aod::FDParticles& parts) { - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : static_cast(col.multNtr()); - auto groupPartsOne = partsOneMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsOne = partsTrackOneMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); auto groupPartsTwo = partsTwoMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); eventHisto.fillQA(col); for (const auto& part : groupPartsTwo) { int pdgCode = static_cast(part.pidCut()); - if ((confCascType1 == 0 && pdgCode != kOmegaMinus) || (confCascType1 == 2 && pdgCode != kOmegaPlusBar) || (confCascType1 == 1 && pdgCode != kXiMinus) || (confCascType1 == 3 && pdgCode != kXiPlusBar)) + if ((cascparticleconfigs.confCascType1 == 0 && pdgCode != kOmegaMinus) || (cascparticleconfigs.confCascType1 == 2 && pdgCode != kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && pdgCode != kXiMinus) || (cascparticleconfigs.confCascType1 == 3 && pdgCode != kXiPlusBar)) continue; - cascQAHistos.fillQA(part); + } - for (const auto& part : groupPartsOne) { - int pdgCode = static_cast(part.pidCut()); - if (pdgCode != confTrkPDGCodePartOne) - continue; - const auto& pdgTrackParticle = pdgMC->GetParticle(pdgCode); - if (!pdgTrackParticle) { - continue; - } - - if (pdgTrackParticle->Charge() > 0) { - trackHistoPartOnePos.fillQA(part); - } else if (pdgTrackParticle->Charge() < 0) { - trackHistoPartOneNeg.fillQA(part); - } + for (const auto& part : groupPartsOne) { + int pdgCode = static_cast(part.pidCut()); + if (pdgCode != trackparticleconfigs.confTrkPDGCodePartOne) + continue; + const auto& pdgTrackParticle = pdgMC->GetParticle(pdgCode); + if (!pdgTrackParticle) { + continue; } - for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (static_cast(p1.pidCut()) != confTrkPDGCodePartOne) - continue; - int pdgCodeCasc = static_cast(p2.pidCut()); - if ((confCascType1 == 0 && pdgCodeCasc != kOmegaMinus) || (confCascType1 == 2 && pdgCodeCasc != kOmegaPlusBar) || (confCascType1 == 1 && pdgCodeCasc != kXiMinus) || (confCascType1 == 3 && pdgCodeCasc != kXiPlusBar)) - continue; - sameEventCont.setPair(p1, p2, multCol, confUse3D, 1.0f); + if (pdgTrackParticle->Charge() > 0) { + trackHistoPartOnePos.fillQA(part); + } else if (pdgTrackParticle->Charge() < 0) { + trackHistoPartOneNeg.fillQA(part); } } + + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + if (static_cast(p1.pidCut()) != trackparticleconfigs.confTrkPDGCodePartOne) + continue; + int pdgCodeCasc = static_cast(p2.pidCut()); + if ((cascparticleconfigs.confCascType1 == 0 && pdgCodeCasc != kOmegaMinus) || (cascparticleconfigs.confCascType1 == 2 && pdgCodeCasc != kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && pdgCodeCasc != kXiMinus) || (cascparticleconfigs.confCascType1 == 3 && pdgCodeCasc != kXiPlusBar)) + continue; + sameEventCont.setPair(p1, p2, multCol, confUse3D, 1.0f); + } } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEventMCgen, "Enable processing same event MC truth for track - cascade", false); // MC truth for cascade - cascade void processSameEventCascMCgen(const FilteredFDCollision& col, [[maybe_unused]] const aod::FDParticles& parts) { - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : static_cast(col.multNtr()); auto groupPartsTwo = partsTwoMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); @@ -1028,28 +1349,29 @@ struct femtoUniversePairTaskTrackCascadeExtended { for (const auto& part : groupPartsTwo) { int pdgCode = static_cast(part.pidCut()); - if ((confCascType1 == 0 && pdgCode != kOmegaMinus) || (confCascType1 == 2 && pdgCode != kOmegaPlusBar) || (confCascType1 == 1 && pdgCode != kXiMinus) || (confCascType1 == 3 && pdgCode != kXiPlusBar)) + if ((cascparticleconfigs.confCascType1 == 0 && pdgCode != kOmegaMinus) || (cascparticleconfigs.confCascType1 == 2 && pdgCode != kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && pdgCode != kXiMinus) || (cascparticleconfigs.confCascType1 == 3 && pdgCode != kXiPlusBar)) continue; cascQAHistos.fillQA(part); - - auto pairProcessFunc = [&](auto& p1, auto& p2) -> void { - int pdgCodeCasc1 = static_cast(p1.pidCut()); - if ((confCascType1 == 0 && pdgCodeCasc1 != kOmegaMinus) || (confCascType1 == 2 && pdgCodeCasc1 != kOmegaPlusBar) || (confCascType1 == 1 && pdgCodeCasc1 != kXiMinus) || (confCascType1 == 3 && pdgCodeCasc1 != kXiPlusBar)) - return; - int pdgCodeCasc2 = static_cast(p2.pidCut()); - if ((confCascType2 == 0 && pdgCodeCasc2 != kOmegaMinus) || (confCascType2 == 2 && pdgCodeCasc2 != kOmegaPlusBar) || (confCascType2 == 1 && pdgCodeCasc2 != kXiMinus) || (confCascType2 == 3 && pdgCodeCasc2 != kXiPlusBar)) - return; - sameEventCont.setPair(p1, p2, multCol, confUse3D, 1.0f); - }; - - if (confCascType1 == confCascType2) { - for (const auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsTwo, groupPartsTwo))) - pairProcessFunc(p1, p2); - } else { - for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsTwo, groupPartsTwo))) - pairProcessFunc(p1, p2); + } + auto pairProcessFunc = [&](auto& p1, auto& p2) -> void { + int pdgCodeCasc1 = static_cast(p1.pidCut()); + if ((cascparticleconfigs.confCascType1 == 0 && pdgCodeCasc1 != kOmegaMinus) || (cascparticleconfigs.confCascType1 == 2 && pdgCodeCasc1 != kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && pdgCodeCasc1 != kXiMinus) || (cascparticleconfigs.confCascType1 == 3 && pdgCodeCasc1 != kXiPlusBar)) { + return; + } + int pdgCodeCasc2 = static_cast(p2.pidCut()); + if ((cascparticleconfigs.confCascType2 == 0 && pdgCodeCasc2 != kOmegaMinus) || (cascparticleconfigs.confCascType2 == 2 && pdgCodeCasc2 != kOmegaPlusBar) || (cascparticleconfigs.confCascType2 == 1 && pdgCodeCasc2 != kXiMinus) || (cascparticleconfigs.confCascType2 == 3 && pdgCodeCasc2 != kXiPlusBar)) { + return; } + sameEventCont.setPair(p1, p2, multCol, confUse3D, 1.0f); + }; + + if (cascparticleconfigs.confCascType1 == cascparticleconfigs.confCascType2) { + for (const auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsTwo, groupPartsTwo))) + pairProcessFunc(p1, p2); + } else { + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsTwo, groupPartsTwo))) + pairProcessFunc(p1, p2); } } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEventCascMCgen, "Enable processing same event MC truth for cascade - cascade", false); @@ -1057,12 +1379,12 @@ struct femtoUniversePairTaskTrackCascadeExtended { // MC truth for track - cascade void processMixedEventMCgen(const FilteredFDCollisions& cols, [[maybe_unused]] const aod::FDParticles& parts) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); - auto groupPartsOne = partsOneMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsOne = partsTrackOneMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwoMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); @@ -1072,10 +1394,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { continue; } for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (static_cast(p1.pidCut()) != confTrkPDGCodePartOne) + if (static_cast(p1.pidCut()) != trackparticleconfigs.confTrkPDGCodePartOne) continue; int pdgCodeCasc = static_cast(p2.pidCut()); - if ((confCascType1 == 0 && pdgCodeCasc != kOmegaMinus) || (confCascType1 == 2 && pdgCodeCasc != kOmegaPlusBar) || (confCascType1 == 1 && pdgCodeCasc != kXiMinus) || (confCascType1 == 3 && pdgCodeCasc != kXiPlusBar)) + if ((cascparticleconfigs.confCascType1 == 0 && pdgCodeCasc != kOmegaMinus) || (cascparticleconfigs.confCascType1 == 2 && pdgCodeCasc != kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && pdgCodeCasc != kXiMinus) || (cascparticleconfigs.confCascType1 == 3 && pdgCodeCasc != kXiPlusBar)) continue; mixedEventCont.setPair(p1, p2, multCol, confUse3D, 1.0f); } @@ -1086,10 +1408,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { // MC truth for cascade - cascade void processMixedEventCascMCgen(const FilteredFDCollisions& cols, [[maybe_unused]] const aod::FDParticles& parts) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : static_cast(collision1.multNtr()); auto groupPartsOne = partsTwoMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwoMCgenBasic->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); @@ -1102,10 +1424,10 @@ struct femtoUniversePairTaskTrackCascadeExtended { } for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { int pdgCodeCasc1 = static_cast(p1.pidCut()); - if ((confCascType1 == 0 && pdgCodeCasc1 != kOmegaMinus) || (confCascType1 == 2 && pdgCodeCasc1 != kOmegaPlusBar) || (confCascType1 == 1 && pdgCodeCasc1 != kXiMinus) || (confCascType1 == 3 && pdgCodeCasc1 != kXiPlusBar)) + if ((cascparticleconfigs.confCascType1 == 0 && pdgCodeCasc1 != kOmegaMinus) || (cascparticleconfigs.confCascType1 == 2 && pdgCodeCasc1 != kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && pdgCodeCasc1 != kXiMinus) || (cascparticleconfigs.confCascType1 == 3 && pdgCodeCasc1 != kXiPlusBar)) continue; int pdgCodeCasc2 = static_cast(p2.pidCut()); - if ((confCascType2 == 0 && pdgCodeCasc2 != kOmegaMinus) || (confCascType2 == 2 && pdgCodeCasc2 != kOmegaPlusBar) || (confCascType2 == 1 && pdgCodeCasc2 != kXiMinus) || (confCascType2 == 3 && pdgCodeCasc2 != kXiPlusBar)) + if ((cascparticleconfigs.confCascType2 == 0 && pdgCodeCasc2 != kOmegaMinus) || (cascparticleconfigs.confCascType2 == 2 && pdgCodeCasc2 != kOmegaPlusBar) || (cascparticleconfigs.confCascType2 == 1 && pdgCodeCasc2 != kXiMinus) || (cascparticleconfigs.confCascType2 == 3 && pdgCodeCasc2 != kXiPlusBar)) continue; mixedEventCont.setPair(p1, p2, multCol, confUse3D, 1.0f); } @@ -1113,6 +1435,20 @@ struct femtoUniversePairTaskTrackCascadeExtended { } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMixedEventCascMCgen, "Enable processing mixed event MC truth for cascade - cascade", false); + // WIP: process same event mc truth and reco + void processSameEventMCReco(FilteredFDCollision const& col, FemtoRecoFullParticles const& parts, aod::FdMCParticles const& mcparts) + { + doSameEvent(col, parts, partsTrackOneFullMc, partsTwoMCrecoFull, mcparts); + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processSameEventMCReco, "Enable processing same event for track - V0 MC Reco", false); + + // WIP: process mixed event mc truth and reco + void processMixedEventMCReco(FilteredFDCollisions const& cols, FemtoRecoFullParticles const& parts, aod::FdMCParticles const& mcparts) + { + doMixedEvent(cols, parts, partsTrackOneFullMc, partsTwoMCrecoFull, mcparts); + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMixedEventMCReco, "Enable processing mixed event for track - V0 for MC Reco", false); + /// This function fills MC truth particles from derived MC table void processMCgen(aod::FDParticles const& parts) { @@ -1126,10 +1462,11 @@ struct femtoUniversePairTaskTrackCascadeExtended { continue; } - if ((confCascType1 == 0 && pdgCode == kOmegaMinus) || (confCascType1 == 1 && pdgCode == kXiMinus)) { + if ((cascparticleconfigs.confCascType1 == 0 && pdgCode == kOmegaMinus) || (cascparticleconfigs.confCascType1 == 1 && pdgCode == kXiMinus)) { registryMCgen.fill(HIST("plus/MCgenCasc"), part.pt(), part.eta()); continue; - } else if ((confCascType1 == 0 && pdgCode == kOmegaPlusBar) || (confCascType1 == 1 && pdgCode == kXiPlusBar)) { + } + if ((cascparticleconfigs.confCascType1 == 0 && pdgCode == kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && pdgCode == kXiPlusBar)) { registryMCgen.fill(HIST("minus/MCgenCasc"), part.pt(), part.eta()); continue; } @@ -1159,54 +1496,67 @@ struct femtoUniversePairTaskTrackCascadeExtended { { for (const auto& part : parts) { auto mcPartId = part.fdMCParticleId(); - if (mcPartId == -1) + if (mcPartId == -1) { continue; // no MC particle + } const auto& mcpart = mcparts.iteratorAt(mcPartId); // if (part.partType() == aod::femtouniverseparticle::ParticleType::kCascade) { - if (!invMCascade(part.mLambda(), part.mAntiLambda(), confCascType1)) /// mLambda stores Xi mass, mAntiLambda stores Omega mass + if (!invMCascade(part.mLambda(), part.mAntiLambda(), cascparticleconfigs.confCascType1)) { /// mLambda stores Xi mass, mAntiLambda stores Omega mass continue; - if ((confCascType1 == 0 && mcpart.pdgMCTruth() == kOmegaMinus) || (confCascType1 == 1 && mcpart.pdgMCTruth() == kXiMinus)) { + } + if ((cascparticleconfigs.confCascType1 == 0 && mcpart.pdgMCTruth() == kOmegaMinus) || (cascparticleconfigs.confCascType1 == 1 && mcpart.pdgMCTruth() == kXiMinus)) { const auto& posChild = parts.iteratorAt(part.globalIndex() - 3 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& bachelor = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); /// Daughters that do not pass this condition are not selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, CascChildTable[confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[confCascType1][2])) + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) { continue; - if (!isParticleTOF(posChild, CascChildTable[confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[confCascType1][2])) + } + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1][0]) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1][1]) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1][2])) { continue; + } } else { - if ((posChild.pidCut() & (1u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) { continue; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1) && (part.pidCut() & 7) != 7) || ((confCascType1 == 0) && (part.pidCut() & 56) != 56)) + } + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1) && (part.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0) && (part.pidCut() & 56) != 56)) { continue; + } } else { - if ((posChild.pidCut() & (8u << CascChildTable[confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[confCascType1][2])) == 0) + if ((posChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1][2])) == 0) { continue; + } } } registryMCreco.fill(HIST("plus/MCrecoCascade"), mcpart.pt(), mcpart.eta()); - } else if ((confCascType1 == 0 && mcpart.pdgMCTruth() == kOmegaPlusBar) || (confCascType1 == 1 && mcpart.pdgMCTruth() == kXiPlusBar)) { + } else if ((cascparticleconfigs.confCascType1 == 0 && mcpart.pdgMCTruth() == kOmegaPlusBar) || (cascparticleconfigs.confCascType1 == 1 && mcpart.pdgMCTruth() == kXiPlusBar)) { const auto& posChild = parts.iteratorAt(part.globalIndex() - 3 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& bachelor = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); + /// Daughters that do not pass this condition are not selected if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, CascChildTable[confCascType1 + 2][0]) && !isParticleTPC(negChild, CascChildTable[confCascType1 + 2][1]) && !isParticleTPC(bachelor, CascChildTable[confCascType1 + 2][2])) + if (!isParticleTPC(posChild, CascChildTable[cascparticleconfigs.confCascType1 + 2][0]) || !isParticleTPC(negChild, CascChildTable[cascparticleconfigs.confCascType1 + 2][1]) || !isParticleTPC(bachelor, CascChildTable[cascparticleconfigs.confCascType1 + 2][2])) { continue; - if (!isParticleTOF(posChild, CascChildTable[confCascType1 + 2][0]) || !isParticleTOF(negChild, CascChildTable[confCascType1 + 2][1]) || !isParticleTOF(bachelor, CascChildTable[confCascType1 + 2][2])) + } + if (!isParticleTOF(posChild, CascChildTable[cascparticleconfigs.confCascType1 + 2][0]) || !isParticleTOF(negChild, CascChildTable[cascparticleconfigs.confCascType1 + 2][1]) || !isParticleTOF(bachelor, CascChildTable[cascparticleconfigs.confCascType1 + 2][2])) { continue; + } } else { - if ((posChild.pidCut() & (1u << CascChildTable[confCascType1 + 2][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[confCascType1 + 2][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[confCascType1 + 2][2])) == 0) + if ((posChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1 + 2][0])) == 0 || (negChild.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1 + 2][1])) == 0 || (bachelor.pidCut() & (1u << CascChildTable[cascparticleconfigs.confCascType1 + 2][2])) == 0) { continue; - if (confUseStrangenessTOF) { - if (((confCascType1 == 1) && (part.pidCut() & 7) != 7) || ((confCascType1 == 0) && (part.pidCut() & 56) != 56)) + } + if (cascparticleconfigs.confUseStrangenessTOF) { + if (((cascparticleconfigs.confCascType1 == 1) && (part.pidCut() & 7) != 7) || ((cascparticleconfigs.confCascType1 == 0) && (part.pidCut() & 56) != 56)) { continue; + } } else { - if ((posChild.pidCut() & (8u << CascChildTable[confCascType1 + 2][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[confCascType1 + 2][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[confCascType1 + 2][2])) == 0) + if ((posChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1 + 2][0])) == 0 || (negChild.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1 + 2][1])) == 0 || (bachelor.pidCut() & (8u << CascChildTable[cascparticleconfigs.confCascType1 + 2][2])) == 0) { continue; + } } } registryMCreco.fill(HIST("minus/MCrecoCascade"), mcpart.pt(), mcpart.eta()); @@ -1215,11 +1565,13 @@ struct femtoUniversePairTaskTrackCascadeExtended { } else if (part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) { if (part.mAntiLambda() > 0) { registryMCreco.fill(HIST("plus/MCrecoAllPt"), mcpart.pt()); - if (mcpart.pdgMCTruth() != kProton) + if (mcpart.pdgMCTruth() != kProton) { continue; + } if constexpr (std::experimental::is_detected::value) { - if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) + if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) { continue; + } } else { if (!isNSigmaCombinedBitmask(part.p(), part)) { continue; @@ -1229,11 +1581,13 @@ struct femtoUniversePairTaskTrackCascadeExtended { registryMCreco.fill(HIST("plus/MCrecoPrPt"), mcpart.pt()); } else if (part.mAntiLambda() < 0) { registryMCreco.fill(HIST("minus/MCrecoAllPt"), mcpart.pt()); - if (mcpart.pdgMCTruth() != kProtonBar) + if (mcpart.pdgMCTruth() != kProtonBar) { continue; + } if constexpr (std::experimental::is_detected::value) { - if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) + if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) { continue; + } } else { if (!isNSigmaCombinedBitmask(part.p(), part)) { continue; @@ -1257,6 +1611,20 @@ struct femtoUniversePairTaskTrackCascadeExtended { doMCReco(parts, mcparts); } PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMCRecoBitmask, "Process MC reco data for cascades using Bitmask for PID", false); + + /// Function used to get QA for MC tracks, mainly for DCAxy Primary, daughter and material + void processMCTrackQA(const FilteredFDCollision& col, const FemtoRecoFullParticles&, const o2::aod::FdMCParticles&) + { + auto groupPartsOneMC = partsTrackOneFullMc->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + for (const auto& part : groupPartsOneMC) { + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + if (!isNSigmaCombined(part.p(), tpcNSigmas[trackparticleconfigs.confTrackChoicePartOne], tofNSigmas[trackparticleconfigs.confTrackChoicePartOne], (part.pidCut() & 512u) != 0)) + continue; + trackQAHistos.fillQA(part); + } + } + PROCESS_SWITCH(femtoUniversePairTaskTrackCascadeExtended, processMCTrackQA, "Process MC track QA", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx index 3166d98990d..a1739dbe1e7 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackD0.cxx @@ -25,8 +25,6 @@ #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/SelectorCuts.h" -#include "Common/Core/RecoDecay.h" - #include #include #include @@ -47,6 +45,7 @@ #include #include +#include #include #include @@ -59,62 +58,48 @@ using namespace o2; using namespace o2::analysis; using namespace o2::analysis::femto_universe; using namespace o2::analysis::femto_universe::efficiency; +using namespace o2::constants::physics; +using namespace o2::constants::math; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; namespace { -static const int8_t genPromptD0 = 1; -static const int8_t genNonPromptD0 = 2; +const int8_t genPromptD0 = 1; +const int8_t genNonPromptD0 = 2; } // namespace -/// Returns deltaPhi value within the range [-pi/2, 3/2*pi] -/// -double getDeltaPhi(double phiD, double phiDbar) -{ - return RecoDecay::constrainAngle(phiDbar - phiD, -o2::constants::math::PIHalf); -} - -/// Returns deltaPhi value within the range [0, pi] -/// -double wrapDeltaPhi0PI(double phiD, double phiDbar) -{ - double deltaPhi = 0.0; - deltaPhi = RecoDecay::constrainAngle(phiDbar - phiD, 0.0); - if (deltaPhi > o2::constants::math::TwoPI) { - deltaPhi = o2::constants::math::TwoPI - deltaPhi; - } - return deltaPhi; -} - struct FemtoUniversePairTaskTrackD0 { - Service pdgMC; + Service pdgMC{}; using FemtoFullParticles = soa::Join; + using FemtoFullSigmaParticles = soa::Join; SliceCache cache; Preslice perCol = aod::femtouniverseparticle::fdCollisionId; + Preslice perColFullSigma = aod::femtouniverseparticle::fdCollisionId; - using FemtoMcRecoParticles = soa::Join; - Preslice perColMc = aod::femtouniverseparticle::fdCollisionId; + using FemtoMcParticles = soa::Join; + Preslice perColMc = aod::femtouniverseparticle::fdCollisionId; /// Table for both particles struct : o2::framework::ConfigurableGroup { Configurable confIsMC{"confIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; - Configurable> confTrkPIDnSigmaMax{"confTrkPIDnSigmaMax", std::vector{4.f, 3.f, 2.f}, "This configurable needs to be the same as the one used in the producer task"}; + Configurable confIsDebug{"confIsDebug", true, "Enable filling additional histograms for track and D0"}; Configurable confUse3D{"confUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; Configurable confPhiBins{"confPhiBins", 29, "Number of phi bins in deta dphi"}; Configurable confEtaBins{"confEtaBins", 29, "Number of eta bins in deta dphi"}; ConfigurableAxis confDeltaEtaAxis{"confDeltaEtaAxis", {100, -0.15, 0.15}, "DeltaEta"}; ConfigurableAxis confDeltaPhiStarAxis{"confDeltaPhiStarAxis", {100, -0.15, 0.15}, "DeltaPhiStar"}; + ConfigurableAxis confNSigmaTPCTOFAxis{"confNSigmaTPCTOFAxis", {200, -4.975, 5.025}, "Binning and range for nSigma axis"}; + ConfigurableAxis confNSigmaCombAxis{"confNSigmaCombAxis", {200, 0.0, 5.025}, "Binning and range for nSigma axis"}; } ConfBothTracks; /// Particle 1 --- IDENTIFIED TRACK struct : o2::framework::ConfigurableGroup { Configurable confIsSame{"confIsSame", false, "Pairs of the same particle"}; Configurable confPDGCodeTrack{"confPDGCodeTrack", 2212, "Particle 2 - PDG code"}; - Configurable confPIDTrack{"confPIDTrack", 2, "Particle 2 - Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector> Configurable confTrackSign{"confTrackSign", 1, "Track sign"}; Configurable confIsTrackIdentified{"confIsTrackIdentified", true, "Enable PID for the track"}; Configurable confTrackLowPtCut{"confTrackLowPtCut", 0.5, "Low pT cut of the track"}; @@ -131,13 +116,21 @@ struct FemtoUniversePairTaskTrackD0 { Configurable confNsigmaTPCPion{"confNsigmaTPCPion", 3.0, "TPC Pion Sigma"}; Configurable confNsigmaCombinedKaon{"confNsigmaCombinedKaon", 3.0, "TPC and TOF Kaon Sigma (combined)"}; Configurable confNsigmaTPCKaon{"confNsigmaTPCKaon", 3.0, "TPC Kaon Sigma"}; - // Configurable with nSigma values for particle rejection - Configurable confNsigmaPrRejectPiNsigma{"confNsigmaPrRejectPiNsigma", 2.0, "Reject if proton could be a pion within a givien nSigma value"}; - Configurable confNsigmaPrRejectKaNsigma{"confNsigmaPrRejectKaNsigma", 2.0, "Reject if proton could be a kaon within a givien nSigma value"}; - Configurable confNsigmaKaRejectPiNsigma{"confNsigmaKaRejectPiNsigma", 2.0, "Reject if kaon could be a pion within a givien nSigma value"}; - Configurable confNsigmaKaRejectPrNsigma{"confNsigmaKaRejectPrNsigma", 2.0, "Reject if kaon could be a proton within a givien nSigma value"}; - Configurable confNsigmaPiRejectKaNsigma{"confNsigmaPiRejectKaNsigma", 2.0, "Reject if pion could be a kaon within a givien nSigma value"}; - Configurable confNsigmaPiRejectPrNsigma{"confNsigmaPiRejectPrNsigma", 2.0, "Reject if pion could be a proton within a givien nSigma value"}; + // Configurable with nSigma values for protons rejection + Configurable confNsigmaTPCPrRejectPiNsigma{"confNsigmaTPCPrRejectPiNsigma", 3.0, "Reject if a proton could be a pion within a givien TPC nSigma value"}; + Configurable confNsigmaTPCPrRejectKaNsigma{"confNsigmaTPCPrRejectKaNsigma", 2.0, "Reject if a proton could be a kaon within a givien TPC nSigma value"}; + Configurable confNsigmaCombPrRejectPiNsigma{"confNsigmaCombPrRejectPiNsigma", 4.2, "Reject if a proton could be a pion within a givien nSigma comb. value"}; + Configurable confNsigmaCombPrRejectKaNsigma{"confNsigmaCombPrRejectKaNsigma", 2.8, "Reject if a proton could be a kaon within a givien nSigma comb. value"}; + // Configurable with nSigma values for kaons rejection + Configurable confNsigmaTPCKaRejectPiNsigma{"confNsigmaTPCKaRejectPiNsigma", 1.0, "Reject if a kaon could be a pion within a given TPC nSigma value"}; + Configurable confNsigmaTPCKaRejectPrNsigma{"confNsigmaTPCKaRejectPrNsigma", 2.0, "Reject if a kaon could be a proton within a given TPC nSigma value"}; + Configurable confNsigmaCombKaRejectPiNsigma{"confNsigmaCombKaRejectPiNsigma", 1.4, "Reject if a kaon could be a pion within a given comb. nSigma value"}; + Configurable confNsigmaCombKaRejectPrNsigma{"confNsigmaCombKaRejectPrNsigma", 2.8, "Reject if a kaon could be a proton within a given comb. nSigma value"}; + // Configurables with nSigma values for pions rejection + Configurable confNsigmaTPCPiRejectKaNsigma{"confNsigmaTPCPiRejectKaNsigma", 2.0, "Reject if a pion could be a kaon within a givien TPC nSigma value"}; + Configurable confNsigmaTPCPiRejectPrNsigma{"confNsigmaTPCPiRejectPrNsigma", 2.0, "Reject if a pion could be a proton within a givien TPC nSigma value"}; + Configurable confNsigmaCombPiRejectKaNsigma{"confNsigmaCombPiRejectKaNsigma", 2.8, "Reject if a pion could be a kaon within a givien comb. nSigma value"}; + Configurable confNsigmaCombPiRejectPrNsigma{"confNsigmaCombPiRejectPrNsigma", 2.8, "Reject if a pion could be a proton within a givien comb. nSigma value"}; // Configurable to enable particle rejection Configurable confDoPartNsigmaRejection{"confDoPartNsigmaRejection", false, "Enable particle nSigma rejection"}; } ConfTrack; @@ -160,56 +153,51 @@ struct FemtoUniversePairTaskTrackD0 { } ConfDmesons; struct : o2::framework::ConfigurableGroup { - Configurable confMaxProbMlClass1Bg{"confMaxProbMlClass1Bg", 0.4, "ML: max prob. that D0/D0bar cand. is from the backgound"}; - Configurable confMinProbMlClass2Prompt{"confMinProbMlClass2Prompt", 0.05, "ML: min prob. that D0/D0bar cand. is prompt"}; - Configurable confMaxProbMlClass3NonPrompt{"confMaxProbMlClass3NonPrompt", 1.0, "ML: max prob. that D0/D0bar cand. is non-prompt"}; - Configurable confClass1BgProbStep{"confClass1BgProbStep", 0.05, "ML: prob. step for score class 1"}; - Configurable confClass1BgProbStart{"confClass1BgProbStart", 0.05, "ML: starting prob. value in optimization for score class 1"}; - Configurable confClass2PromptProbStep{"confClass2PromptProbStep", 0.05, "ML: prob. step for score class 2 - prompt"}; - Configurable confClass2PromptProbStart{"confClass2PromptProbStart", 0.1, "ML: starting prob. value in optimization for score class 2"}; - Configurable confClass3NonPromptProbStep{"confClass3NonPromptProbStep", 0.05, "ML: prob. step for score class 2 - non-prompt"}; - Configurable confClass3NonPromptProbStart{"confClass3NonPromptProbStart", 0.05, "ML: starting prob. value in optimization for score class 3"}; - } ConfMlOpt; + Configurable confMlProbPromptMin{"confMlProbPromptMin", 0.0, "ML: minimum prob. that D0/D0bar cand. is prompt"}; + Configurable confMlProbNonPromptMax{"confMlProbNonPromptMax", 1.0, "ML: maximum prob. that D0/D0bar cand. is non-prompt"}; + } ConfMlProb; Configurable> binsPt{"binsPt", std::vector{hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits"}; + Configurable> binsPtFive{"binsPtFive", std::vector{hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits"}; Configurable confChooseD0trackCorr{"confChooseD0trackCorr", 0, "If 0 correlations with D0s, if 1 with D0bars"}; - // Correlated background for D0/D0bar candidates - Configurable fillCorrBkgs{"fillCorrBkgs", false, "Fill histograms with correlated background candidates"}; - + struct : o2::framework::ConfigurableGroup { + Configurable fillTrackQA{"fillTrackQA", false, "Fill additional histograms for track in PurityQA process"}; + // Correlated background for D0/D0bar candidates + Configurable fillCorrBkgs{"fillCorrBkgs", false, "Fill histograms with correlated background candidates"}; + // Configurable to enable filling histograms with BDT scores values + Configurable fillBDTvsPtVsMass{"fillBDTvsPtVsMass", false, "Fill BDT vs pT vs mass histograms for D0/D0bar candidates"}; + } ConfFill; // Efficiency Configurable doEfficiencyCorr{"doEfficiencyCorr", false, "Apply efficiency corrections"}; /// Partitions for particle 1 - Partition partsTrack = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == int8_t(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); - Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrack = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackFullSigma = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == static_cast(ConfTrack.confTrackSign)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); + Partition partsTrackMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfTrack.confPDGCodeTrack)) && (aod::femtouniverseparticle::pt > ConfTrack.confTrackLowPtCut) && (aod::femtouniverseparticle::pt < ConfTrack.confTrackHighPtCut) && (nabs(aod::femtouniverseparticle::eta) < ConfTrack.confTrackEtaMax); /// Partitions for particle 2 /// Partition with all D0/D0bar mesons (which pass one and double mass hypothesis) - Partition partsAllDmesons = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > 0.0f) || (aod::femtouniverseparticle::mAntiLambda > 0.0f)) && (aod::femtouniverseparticle::decayVtxX < ConfMlOpt.confMaxProbMlClass1Bg) && (aod::femtouniverseparticle::decayVtxY > ConfMlOpt.confMinProbMlClass2Prompt); + Partition partsAllDmesons = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > 0.0f) || (aod::femtouniverseparticle::mAntiLambda > 0.0f)) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0 mesons only (one and double mass hypothesis) - Partition partsAllD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); - /// Partition with D0 mesons only (one mass hypothesis) - Partition partsD0s = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < 0.0f) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxX < ConfMlOpt.confMaxProbMlClass1Bg) && (aod::femtouniverseparticle::decayVtxY > ConfMlOpt.confMinProbMlClass2Prompt); + Partition partSigD0s = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0s selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partsD0sFromSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partSidebandD0s = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bar mesons only (one and double mass hypothesis) - Partition partsAllD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); - /// Partition with D0bar mesons only (one mass hypothesis) - Partition partsD0bars = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda < 0.0f) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxX < ConfMlOpt.confMaxProbMlClass1Bg) && (aod::femtouniverseparticle::decayVtxY > ConfMlOpt.confMinProbMlClass2Prompt); + Partition partSigD0bars = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition with D0bars selected from the side-band (SB) regions (candidates with double mass hypothesis included) - Partition partsD0barsFromSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partSidebandD0bars = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxZ < ConfMlProb.confMlProbNonPromptMax); /// Partition for MC Reco prompt D0/D0bar mesons - Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxX < ConfMlOpt.confMaxProbMlClass1Bg) && (aod::femtouniverseparticle::decayVtxY > ConfMlOpt.confMinProbMlClass2Prompt) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxX < ConfMlOpt.confMaxProbMlClass1Bg) && (aod::femtouniverseparticle::decayVtxY > ConfMlOpt.confMinProbMlClass2Prompt) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barSignal) && (aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barSignal) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); /// Partition for MC Reco prompt D0/D0bar mesons (sideband candidates) - Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxX < ConfMlOpt.confMaxProbMlClass1Bg) && (aod::femtouniverseparticle::decayVtxY > ConfMlOpt.confMinProbMlClass2Prompt) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); - Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::decayVtxX < ConfMlOpt.confMaxProbMlClass1Bg) && (aod::femtouniverseparticle::decayVtxY > ConfMlOpt.confMinProbMlClass2Prompt) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0MCRecoSB = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); + Partition partsPromptD0barMCRecoSB = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0)) && ((aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barLeftSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barLeftSB) || (aod::femtouniverseparticle::mAntiLambda > ConfDmesons.minInvMassD0D0barRightSB && aod::femtouniverseparticle::mAntiLambda < ConfDmesons.maxInvMassD0D0barRightSB)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar) && (aod::femtouniverseparticle::tpcNClsFound == ConfDmesons.confD0OriginFlag); // MC Truth D0/D0bar candidates - Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); - Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pidCut == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0MCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); + Partition partsD0barMCTruth = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::tempFitVar == static_cast(ConfDmesons.confPDGCodeD0bar)) && (aod::femtouniverseparticle::pt > ConfDmesons.confMinPtD0D0bar) && (aod::femtouniverseparticle::pt < ConfDmesons.confMaxPtD0D0bar); /// Partition for D0/D0bar daughters - Partition partsDmesonsChildren = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kD0Child); + Partition partsDmesonsChildren = aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kD0Child); /// Histogramming for particle 1 FemtoUniverseParticleHisto trackHistoPartTrack; @@ -220,10 +208,6 @@ struct FemtoUniversePairTaskTrackD0 { /// Histogramming for Event FemtoUniverseEventHisto eventHisto; - /// The configurables need to be passed to an std::vector - int vPIDTrack; - std::vector kNsigma; - /// particle part ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; ConfigurableAxis confTempFitVarInvMassBins{"confTempFitVarInvMassBins", {6000, 0.9, 4.0}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; @@ -232,12 +216,11 @@ struct FemtoUniversePairTaskTrackD0 { /// Correlation part ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f, 64.0f, 68.0f, 72.0f, 76.0f, 80.0f, 84.0f, 88.0f, 92.0f, 96.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; // \todo to be obtained from the hash task - // ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -245,15 +228,24 @@ struct FemtoUniversePairTaskTrackD0 { ConfigurableAxis confPtBins{"confPtBins", {360, 0., 36.}, "binning pT"}; ConfigurableAxis confInvMassBins{"confInvMassBins", {500, 0., 5.0}, "binning inv. mass"}; - Configurable confIsCPR{"confIsCPR", true, "Close Pair Rejection"}; - Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; - Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; - Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; - Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; - Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; - Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.80, "Delta Eta cut for Close Pair Rejection"}; + struct : o2::framework::ConfigurableGroup { + ConfigurableAxis thnConfigAxisNonPromptScore{"thnConfigAxisNonPromptScore", {100, 0., 1.}, "Non-prompt score bins"}; + ConfigurableAxis thnConfigAxisPromptScore{"thnConfigAxisPromptScore", {100, 0., 1.}, "Prompt score bins"}; + ConfigurableAxis thnConfigAxisBkgScore{"thnConfigAxisBkgScore", {100, 0., 1.}, "Background score bins"}; + ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {500, 0., 5.0}, "Cand. inv-mass bins"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {360, 0., 36.}, "Cand. pT bins"}; + } ConfThnAxes; + + struct : o2::framework::ConfigurableGroup { + Configurable confIsCPR{"confIsCPR", true, "Close Pair Rejection"}; + Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; + Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; + Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; + Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.80, "Delta Eta cut for Close Pair Rejection"}; + } ConfCPR; - Configurable applyMLOpt{"applyMLOpt", false, "Enable for ML selection optimization"}; Configurable confRemoveSoftPions{"confRemoveSoftPions", false, "Enable to remove soft pions from D* decays"}; Configurable confSoftPionD0Flag{"confSoftPionD0Flag", false, "Enable soft pion check for D0s"}; Configurable confSoftPionD0barFlag{"confSoftPionD0barFlag", false, "Enable soft pion check for D0bars"}; @@ -267,9 +259,8 @@ struct FemtoUniversePairTaskTrackD0 { FemtoUniverseDetaDphiStar pairCloseRejection; FemtoUniverseSoftPionRemoval softPionRemoval; FemtoUniverseTrackSelection trackCuts; - // Axes for BDT score classes' histograms + // Axis for BDT score classes' histograms AxisSpec axisBdtScore{100, 0.f, 1.f}; - AxisSpec axisSelStatus{2, -0.5f, 1.5f}; /// Histogram output HistogramRegistry qaRegistry{"TrackQA", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -288,135 +279,63 @@ struct FemtoUniversePairTaskTrackD0 { {{"hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, {"hPtD0bar", "#bar{D^{0}};#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, {"hPtD0D0bar", "#bar{D^{0}};#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {confPtBins}}}, - {"hPhiD0D0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, - {"hPhiD0", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, - {"hPhiD0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}}, + {"hPhiD0D0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, + {"hPhiD0", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, + {"hPhiD0bar", ";#varphi (rad);counts", {HistType::kTH1F, {{80, 0., TwoPI}}}}, {"hEtaD0D0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hEtaD0bar", ";#eta ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, + {"hYD0D0bar", ";y ;counts", {HistType::kTH1F, {{200, -1., 1.}}}}, {"hPtDaughters", ";#it{p}_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{300, 0., 12.}}}}, {"hSignDaughters", ";sign ;counts", {HistType::kTH1F, {{10, -2.5, 2.5}}}}}}; // PID for protons bool isProtonNSigma(float mom, float nsigmaTPCPr, float nsigmaTOFPr) // previous version from: https://github.com/alisw/AliPhysics/blob/master/PWGCF/FEMTOSCOPY/AliFemtoUser/AliFemtoMJTrackCut.cxx { - if (mom < ConfTrack.minMomPidTpcTofProton) { - if (std::abs(nsigmaTPCPr) < ConfTrack.confNsigmaTPCProton) { - return true; - } else { - return false; - } - } else if (mom > ConfTrack.minMomPidTpcTofProton) { - if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfTrack.confNsigmaCombinedProton) { - return true; - } else { - return false; - } + if (mom <= ConfTrack.minMomPidTpcTofProton) { + return std::abs(nsigmaTPCPr) < ConfTrack.confNsigmaTPCProton; } - return false; + return std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfTrack.confNsigmaCombinedProton; } bool isProtonNSigmaRejected(float mom, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCKa, float nsigmaTOFKa) { - if (mom < ConfTrack.minMomPidTpcTofProton) { - if (std::abs(nsigmaTPCPi) < ConfTrack.confNsigmaPrRejectPiNsigma) { - return true; - } else if (std::abs(nsigmaTPCKa) < ConfTrack.confNsigmaPrRejectKaNsigma) { - return true; - } - } - if (mom > ConfTrack.minMomPidTpcTofProton) { - if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfTrack.confNsigmaPrRejectPiNsigma) { - return true; - } else if (std::hypot(nsigmaTOFKa, nsigmaTPCKa) < ConfTrack.confNsigmaPrRejectKaNsigma) { - return true; - } else { - return false; - } - } else { - return false; + if (mom <= ConfTrack.minMomPidTpcTofProton) { + return std::abs(nsigmaTPCPi) < ConfTrack.confNsigmaTPCPrRejectPiNsigma || std::abs(nsigmaTPCKa) < ConfTrack.confNsigmaTPCPrRejectKaNsigma; } + return std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfTrack.confNsigmaCombPrRejectPiNsigma || std::hypot(nsigmaTOFKa, nsigmaTPCKa) < ConfTrack.confNsigmaCombPrRejectKaNsigma; } - bool isKaonNSigmaLF(float mom, float nsigmaTPCKa, float nsigmaTOFKa) + bool isKaonNSigma(float mom, float nsigmaTPCKa, float nsigmaTOFKa) { - if (mom < ConfTrack.minMomPidTpcTofKaon) { - if (std::abs(nsigmaTPCKa) < ConfTrack.confNsigmaTPCKaon) { - return true; - } else { - return false; - } - } else if (mom > ConfTrack.minMomPidTpcTofKaon) { - if (std::hypot(nsigmaTOFKa, nsigmaTPCKa) < ConfTrack.confNsigmaCombinedKaon) { - return true; - } else { - return false; - } - } else { - return false; + if (mom <= ConfTrack.minMomPidTpcTofKaon) { + return std::abs(nsigmaTPCKa) < ConfTrack.confNsigmaTPCKaon; } + return std::hypot(nsigmaTOFKa, nsigmaTPCKa) < ConfTrack.confNsigmaCombinedKaon; } - bool isKaonNSigmaLFRejected(float mom, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCPr, float nsigmaTOFPr) + bool isKaonNSigmaRejected(float mom, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCPr, float nsigmaTOFPr) { - if (mom < ConfTrack.minMomPidTpcTofKaon) { - if (std::abs(nsigmaTPCPi) < ConfTrack.confNsigmaKaRejectPiNsigma) { - return true; - } else if (std::abs(nsigmaTPCPr) < ConfTrack.confNsigmaKaRejectPrNsigma) { - return true; - } - } - if (mom > ConfTrack.minMomPidTpcTofKaon) { - if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfTrack.confNsigmaKaRejectPiNsigma) { - return true; - } else if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfTrack.confNsigmaKaRejectPrNsigma) { - return true; - } else { - return false; - } - } else { - return false; + if (mom <= ConfTrack.minMomPidTpcTofKaon) { + return std::abs(nsigmaTPCPi) < ConfTrack.confNsigmaTPCKaRejectPiNsigma || std::abs(nsigmaTPCPr) < ConfTrack.confNsigmaTPCKaRejectPrNsigma; } + return std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfTrack.confNsigmaCombKaRejectPiNsigma || std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfTrack.confNsigmaCombKaRejectPrNsigma; } bool isPionNSigma(float mom, float nsigmaTPCPi, float nsigmaTOFPi) { - if (mom < ConfTrack.minMomPidTpcTofPion) { - if (std::abs(nsigmaTPCPi) < ConfTrack.confNsigmaTPCPion) { - return true; - } else { - return false; - } - } else if (mom > ConfTrack.minMomPidTpcTofPion) { - if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfTrack.confNsigmaCombinedPion) { - return true; - } else { - return false; - } + if (mom <= ConfTrack.minMomPidTpcTofPion) { + return std::abs(nsigmaTPCPi) < ConfTrack.confNsigmaTPCPion; } - return false; + return std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfTrack.confNsigmaCombinedPion; } bool isPionNSigmaRejected(float mom, float nsigmaTPCKa, float nsigmaTOFKa, float nsigmaTPCPr, float nsigmaTOFPr) { - if (mom < ConfTrack.minMomPidTpcTofPion) { - if (std::abs(nsigmaTPCKa) < ConfTrack.confNsigmaPiRejectKaNsigma) { - return true; - } else if (std::abs(nsigmaTPCPr) < ConfTrack.confNsigmaPiRejectPrNsigma) { - return true; - } - } - if (mom > ConfTrack.minMomPidTpcTofPion) { - if (std::hypot(nsigmaTOFKa, nsigmaTPCKa) < ConfTrack.confNsigmaPiRejectKaNsigma) { - return true; - } else if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfTrack.confNsigmaPiRejectPrNsigma) { - return true; - } else { - return false; - } - } else { - return false; + if (mom <= ConfTrack.minMomPidTpcTofPion) { + return std::abs(nsigmaTPCKa) < ConfTrack.confNsigmaTPCPiRejectKaNsigma || std::abs(nsigmaTPCPr) < ConfTrack.confNsigmaTPCPiRejectPrNsigma; } + return std::hypot(nsigmaTOFKa, nsigmaTPCKa) < ConfTrack.confNsigmaCombPiRejectKaNsigma || std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfTrack.confNsigmaCombPiRejectPrNsigma; } bool isParticleNSigma(float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK) @@ -432,7 +351,7 @@ struct FemtoUniversePairTaskTrackD0 { break; case 321: // Kaon+ case -321: // Kaon- - return isKaonNSigmaLF(mom, nsigmaTPCK, nsigmaTOFK); + return isKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); break; default: return false; @@ -452,7 +371,7 @@ struct FemtoUniversePairTaskTrackD0 { break; case 321: // Kaon+ case -321: // Kaon- - return isKaonNSigmaLFRejected(mom, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCPr, nsigmaTOFPr); + return isKaonNSigmaRejected(mom, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCPr, nsigmaTOFPr); break; default: return false; @@ -464,53 +383,86 @@ struct FemtoUniversePairTaskTrackD0 { efficiencyCalculator.init(); eventHisto.init(&qaRegistry); - qaRegistry.add("QA_D0D0barSelection/hInvMassD0", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection/hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("QA_D0D0barSelection/hInvMassD0bar", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection/hPtD0bar", "#bar{D^{0}} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("QA_D0D0barSelection_SB/hInvMassD0", ";#it{M}(K^{-}#pi^{+}) (GeV/#it{c}^{2});counts", kTH1F, {confInvMassBins}); - qaRegistry.add("QA_D0D0barSelection_SB/hPtD0", "D^{0} cand.;#it{p}_{T} (GeV/#it{c});counts", kTH1F, {confPtBins}); - qaRegistry.add("D0_pos_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_pos_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_pos_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0_pos_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0_pos_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0_neg_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_neg_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0_neg_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0_neg_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0_neg_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0bar_pos_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_pos_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_pos_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0bar_pos_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0bar_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0bar_pos_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0bar_pos_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); qaRegistry.add("D0bar_neg_daugh/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TPC}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_neg_daugh/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("D0bar_neg_daugh/pt", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); qaRegistry.add("D0bar_neg_daugh/eta", "; #it{eta}; Counts", kTH1F, {{200, -1.5, 1.5}}); - qaRegistry.add("D0bar_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, o2::constants::math::TwoPI}}); + qaRegistry.add("D0bar_neg_daugh/phi", "; #it{varphi}; Counts", kTH1F, {{200, 0, TwoPI}}); qaRegistry.add("D0bar_neg_daugh/hDCAxy", "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); - qaRegistry.add("Hadron/nSigmaTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaCombPr", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Pr}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaTPCPi", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPi}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaTOFPi", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPi}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaCombPi", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Pi}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaTPCKa", "; #it{p} (GeV/#it{c}); n#sigma_{TPCKa}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaTOFKa", "; #it{p} (GeV/#it{c}); n#sigma_{TOFKa}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); - qaRegistry.add("Hadron/nSigmaCombKa", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Ka}", kTH2F, {{100, 0, 10}, {200, -5.025, 5.025}}); + qaRegistry.add("Hadron/nSigmaTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("Hadron/nSigmaTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("Hadron/nSigmaCombPr", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Pr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + qaRegistry.add("Hadron/nSigmaTPCPi", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPi}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("Hadron/nSigmaTOFPi", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPi}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("Hadron/nSigmaCombPi", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Pi}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + qaRegistry.add("Hadron/nSigmaTPCKa", "; #it{p} (GeV/#it{c}); n#sigma_{TPCKa}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("Hadron/nSigmaTOFKa", "; #it{p} (GeV/#it{c}); n#sigma_{TOFKa}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("Hadron/nSigmaCombKa", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Ka}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + + if (doprocessPurityQA && ConfFill.fillTrackQA) { + qaRegistry.add("TrackQA/hCharge", "; Charge; Entries", kTH1F, {{5, -2.5, 2.5}}); + qaRegistry.add("TrackQA/hTPCfindable", "; TPC findable clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfound", "; TPC found clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCcrossedOverFindable", "; TPC ratio findable; Entries", kTH1F, {{150, 0., 1.5}}); + qaRegistry.add("TrackQA/hTPCcrossedRows", "; TPC crossed rows; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfindableVsCrossed", ";TPC findable clusters ; TPC crossed rows;", kTH2F, {{163, -0.5, 162.5}, {163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCshared", "; TPC shared clusters; Entries", kTH1F, {{163, -0.5, 162.5}}); + qaRegistry.add("TrackQA/hTPCfractionSharedCls", "; TPC fraction of shared clusters; Entries", kTH1F, {{100, 0.0, 1.0}}); + qaRegistry.add("TrackQA/hITSclusters", "; ITS clusters; Entries", kTH1F, {{10, -0.5, 9.5}}); + qaRegistry.add("TrackQA/hITSclustersIB", "; ITS clusters in IB; Entries", kTH1F, {{10, -0.5, 9.5}}); + qaRegistry.add("TrackQA/hDCAz", "; #it{p}_{T} (GeV/#it{c}); DCA_{z} (cm)", kTH2F, {{100, 0, 10}, {500, -5, 5}}); + qaRegistry.add("TrackQA/hTPCdEdX", "; #it{p} (GeV/#it{c}); TPC Signal", kTH2F, {{100, 0, 10}, {1000, 0, 1000}}); + } + if (doprocessPurityQA && std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kProton) { + qaRegistry.add("PurityQA/nSigmaFullTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("PurityQA/nSigmaFullTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("PurityQA/nSigmaFullCombPr", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Pr}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + } else if (doprocessPurityQA && std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kPiPlus) { + qaRegistry.add("PurityQA/nSigmaFullTPCPi", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPi}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("PurityQA/nSigmaFullTOFPi", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPi}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("PurityQA/nSigmaFullCombPi", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Pi}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + } else if (doprocessPurityQA && std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kKPlus) { + qaRegistry.add("PurityQA/nSigmaFullTPCKa", "; #it{p} (GeV/#it{c}); n#sigma_{TPCKa}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("PurityQA/nSigmaFullTOFKa", "; #it{p} (GeV/#it{c}); n#sigma_{TOFKa}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaTPCTOFAxis}); + qaRegistry.add("PurityQA/nSigmaFullCombKa", "; #it{p} (GeV/#it{c}); n#sigma^{combined}_{Ka}", kTH2F, {{100, 0, 10}, ConfBothTracks.confNSigmaCombAxis}); + } // D0/D0bar histograms - auto vbins = (std::vector)binsPt; + auto vbins = binsPt.value; + auto vFivePtBins = binsPtFive.value; + // Axes for THnSparse + const AxisSpec thnAxisNonPromptScore{ConfThnAxes.thnConfigAxisNonPromptScore, "BDT score non-prompt"}; + const AxisSpec thnAxisPromptScore{ConfThnAxes.thnConfigAxisPromptScore, "BDT score prompt"}; + const AxisSpec thnAxisBkgScore{ConfThnAxes.thnConfigAxisBkgScore, "BDT score background"}; + const AxisSpec thnAxisMass{ConfThnAxes.thnConfigAxisMass, "inv. mass (#pi K) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{vbins, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPtWiderBins{vFivePtBins, "#it{p}_{T} (GeV/#it{c})"}; + std::vector axesTHn = {thnAxisPromptScore, thnAxisNonPromptScore, thnAxisMass, thnAxisPt}; + std::vector axesTHnMc = {thnAxisBkgScore, thnAxisPromptScore, thnAxisNonPromptScore, thnAxisPt}; + std::vector axesTHnFD = {thnAxisBkgScore, thnAxisPromptScore, thnAxisNonPromptScore, thnAxisPtWiderBins}; + if (doEfficiencyCorr) { registry.add("D0D0bar_oneMassHypo/hMassVsPtEffCorr", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("D0D0bar_oneMassHypo/hMassVsPtD0EffCorr", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -527,20 +479,26 @@ struct FemtoUniversePairTaskTrackD0 { registry.add("D0D0bar_doubleMassHypo/hMassVsPtD0bar", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); } // Histograms for BDT score classes' check - registry.add("DebugBdt/hBdtScore1VsStatus", ";BDT score;status", {HistType::kTH2F, {axisBdtScore, axisSelStatus}}); - registry.add("DebugBdt/hBdtScore2VsStatus", ";BDT score;status", {HistType::kTH2F, {axisBdtScore, axisSelStatus}}); - registry.add("DebugBdt/hBdtScore3VsStatus", ";BDT score;status", {HistType::kTH2F, {axisBdtScore, axisSelStatus}}); - if (applyMLOpt) { - registry.add("D0D0bar_MLSel/hMassVsPt1", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt2", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt3", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt4", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt5", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt6", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt7", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt8", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt9", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("D0D0bar_MLSel/hMassVsPt10", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("DebugBdt/hBdtScore0", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); + registry.add("DebugBdt/hBdtScore1", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); + registry.add("DebugBdt/hBdtScore2", ";BDT score;Entries", {HistType::kTH1F, {axisBdtScore}}); + registry.add("DebugBdtMcReco/hBdtScoresVsPtD0", "Thn for reconstructed D0 candidates", HistType::kTHnSparseD, axesTHnMc); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtD0bar", "Thn for reconstructed D0bar candidates", HistType::kTHnSparseD, axesTHnMc); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtD0bar"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0", "Thn for reconstructed prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0", "Thn for reconstructed non-prompt D0 candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar", "Thn for reconstructed prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"))->Sumw2(); + registry.add("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar", "Thn for reconstructed non-prompt D0bar candidates", HistType::kTHnSparseD, axesTHnFD); + registry.get(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"))->Sumw2(); + if (ConfFill.fillBDTvsPtVsMass) { + registry.add("DebugBdt/hBdtScoresVsMassVsPtD0", "Thn for D0 candidates", HistType::kTHnSparseD, axesTHn); + registry.get(HIST("DebugBdt/hBdtScoresVsMassVsPtD0"))->Sumw2(); + registry.add("DebugBdt/hBdtScoresVsMassVsPtD0bar", "Thn for D0bar candidates", HistType::kTHnSparseD, axesTHn); + registry.get(HIST("DebugBdt/hBdtScoresVsMassVsPtD0bar"))->Sumw2(); } // MC Reco mcRecoRegistry.add("hMcRecD0", "MC Reco all D0s;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); @@ -549,16 +507,16 @@ struct FemtoUniversePairTaskTrackD0 { mcRecoRegistry.add("hMcRecD0PromptPt", "MC Reco prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0NonPrompt", "MC Reco non-prompt D0s;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0NonPromptPt", "MC Reco non-prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); - mcRecoRegistry.add("hMcRecD0Phi", "MC Reco all D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); - mcRecoRegistry.add("hMcRecD0PromptPhi", "MC Reco prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); - mcRecoRegistry.add("hMcRecD0NonPromptPhi", "MC Reco non-prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); + mcRecoRegistry.add("hMcRecD0Phi", "MC Reco all D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); + mcRecoRegistry.add("hMcRecD0PromptPhi", "MC Reco prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); + mcRecoRegistry.add("hMcRecD0NonPromptPhi", "MC Reco non-prompt D0s;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); mcRecoRegistry.add("hMcRecD0bar", "MC Reco all D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barPt", "MC Reco all D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0barPrompt", "MC Reco prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barPromptPt", "MC Reco prompt D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcRecoRegistry.add("hMcRecD0barNonPrompt", "MC Reco non-prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcRecoRegistry.add("hMcRecD0barNonPromptPt", "MC Reco non-prompt D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); - mcRecoRegistry.add("hMcRecD0barPhi", "MC Reco all D0bars;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., o2::constants::math::TwoPI}}}); + mcRecoRegistry.add("hMcRecD0barPhi", "MC Reco all D0bars;#varphi (rad); counts", {HistType::kTH1F, {{80, 0., TwoPI}}}); // Inv. mass histograms mcRecoRegistry.add("hMassVsPtD0Sig", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); mcRecoRegistry.add("hMassVsPtD0Refl", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -571,7 +529,7 @@ struct FemtoUniversePairTaskTrackD0 { mcRecoRegistry.add("hMassVsPtD0barPrompt", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); mcRecoRegistry.add("hMassVsPtD0barNonPrompt", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); // Histograms for D0/D0bar correlated backgrounds - if (fillCorrBkgs) { + if (ConfFill.fillCorrBkgs) { mcRecoRegistry.add("hMassVsPtD0ToPiKaPi", "2-prong candidates;inv. mass (#pi^{+} K^{-} #pi^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); mcRecoRegistry.add("hMassVsPtD0ToPiPi", "2-prong candidates;inv. mass (#pi^{+} #pi^{-}) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); mcRecoRegistry.add("hMassVsPtD0ToPiPiPi", "2-prong candidates;inv. mass (#pi^{+} #pi^{-} #pi^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {confInvMassBins, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -600,16 +558,20 @@ struct FemtoUniversePairTaskTrackD0 { mcTruthRegistry.add("hMcGenD0Pt", "MC Truth all D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcTruthRegistry.add("hMcGenD0Prompt", "MC Truth prompt D0s;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcTruthRegistry.add("hMcGenD0PromptPt", "MC Truth prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); + mcTruthRegistry.add("hMcGenD0PromptPtLessBins", "MC Truth prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vFivePtBins}}); mcTruthRegistry.add("hMcGenD0NonPrompt", "MC Truth non-prompt D0s;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcTruthRegistry.add("hMcGenD0NonPromptPt", "MC Truth non-prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); + mcTruthRegistry.add("hMcGenD0NonPromptPtLessBins", "MC Truth non-prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vFivePtBins}}); mcTruthRegistry.add("hMcGenD0bar", "MC Truth all D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcTruthRegistry.add("hMcGenD0barPt", "MC Truth all D0bars;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); mcTruthRegistry.add("hMcGenD0barPrompt", "MC Truth prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcTruthRegistry.add("hMcGenD0barPromptPt", "MC Truth prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); + mcTruthRegistry.add("hMcGenD0barPromptPtLessBins", "MC Truth prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vFivePtBins}}); mcTruthRegistry.add("hMcGenD0barNonPrompt", "MC Truth non-prompt D0bars;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {400, -1.0, 1.0}}}); mcTruthRegistry.add("hMcGenD0barNonPromptPt", "MC Truth non-prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vbins}}); - mcTruthRegistry.add("hMcGenAllPositivePt", "MC Truth all positive;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{360, 0, 36}}}); - mcTruthRegistry.add("hMcGenAllNegativePt", "MC Truth all negative;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{360, 0, 36}}}); + mcTruthRegistry.add("hMcGenD0barNonPromptPtLessBins", "MC Truth non-prompt D0s;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {vFivePtBins}}); + mcTruthRegistry.add("hMcGenAllPositivePt", "MC Truth all positive;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); + mcTruthRegistry.add("hMcGenAllNegativePt", "MC Truth all negative;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); mcTruthRegistry.add("hMcGenKpPtVsEta", "MC Truth K+;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); mcTruthRegistry.add("hMcGenKmPtVsEta", "MC Truth K-;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); mcTruthRegistry.add("hMcGenPipPtVsEta", "MC Truth #pi+;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); @@ -623,8 +585,8 @@ struct FemtoUniversePairTaskTrackD0 { mcTruthRegistry.add("hMcGenPrPt", "MC Truth proton;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); mcTruthRegistry.add("hMcGenAntiPrPt", "MC Truth antiproton;#it{p}_{T} (GeV/c); counts", {HistType::kTH1F, {{500, 0, 5}}}); - trackHistoPartTrack.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, ConfBothTracks.confIsMC, ConfTrack.confPDGCodeTrack); - trackHistoPartD0D0bar.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarInvMassBins, ConfBothTracks.confIsMC, ConfDmesons.confPDGCodeD0); + trackHistoPartTrack.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, ConfBothTracks.confIsMC, ConfTrack.confPDGCodeTrack, ConfBothTracks.confIsDebug); + trackHistoPartD0D0bar.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarInvMassBins, ConfBothTracks.confIsMC, ConfDmesons.confPDGCodeD0, ConfBothTracks.confIsDebug); mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); mixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); @@ -637,163 +599,18 @@ struct FemtoUniversePairTaskTrackD0 { softPionRemoval.init(&qaRegistry); pairCleaner.init(&qaRegistry); - if (confIsCPR.value) { - pairCloseRejection.init(&resultRegistry, &qaRegistry, ConfBothTracks.confDeltaEtaAxis, ConfBothTracks.confDeltaPhiStarAxis, confCPRdeltaPhiCutMin.value, confCPRdeltaPhiCutMax.value, confCPRdeltaEtaCutMin.value, confCPRdeltaEtaCutMax.value, confCPRChosenRadii.value, confCPRPlotPerRadii.value); + if (ConfCPR.confIsCPR.value) { + pairCloseRejection.init(&resultRegistry, &qaRegistry, ConfBothTracks.confDeltaEtaAxis, ConfBothTracks.confDeltaPhiStarAxis, ConfCPR.confCPRdeltaPhiCutMin.value, ConfCPR.confCPRdeltaPhiCutMax.value, ConfCPR.confCPRdeltaEtaCutMin.value, ConfCPR.confCPRdeltaEtaCutMax.value, ConfCPR.confCPRChosenRadii.value, ConfCPR.confCPRPlotPerRadii.value); } - - vPIDTrack = ConfTrack.confPIDTrack.value; - kNsigma = ConfBothTracks.confTrkPIDnSigmaMax.value; } template - void fillCollision(CollisionType col) + void fillCollision(CollisionType const& col) { mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); } - void processD0MLOptBg(o2::aod::FdCollision const& col, FemtoFullParticles const&) - { - auto groupD0D0barCands = partsAllDmesons->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - - // loop over selected D0/D0bar candidates - for (auto const& charmCand : groupD0D0barCands) { - // D0 candidates - if (charmCand.mLambda() > 0.0f && charmCand.mAntiLambda() < 0.0f) { - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt1"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt2"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 2.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt3"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 3.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt4"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 4.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt5"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 5.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt6"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 6.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt7"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 7.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt8"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 8.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt9"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 9.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt10"), charmCand.mLambda(), charmCand.pt()); - } - // DObar candidates - if (charmCand.mLambda() < 0.0f && charmCand.mAntiLambda() > 0.0f) { - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt1"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt2"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 2.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt3"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 3.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt4"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 4.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt5"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 5.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt6"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 6.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt7"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 7.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt8"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 8.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt9"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 9.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt10"), charmCand.mAntiLambda(), charmCand.pt()); - } - } - } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processD0MLOptBg, "Enable filling QA plots for ML Bg D0/D0bar selection optimization", false); - - void processD0MLOptBgAndPrompt(o2::aod::FdCollision const& col, FemtoFullParticles const&) - { - auto groupD0D0barCands = partsAllDmesons->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - - // loop over selected D0/D0bar candidates - for (auto const& charmCand : groupD0D0barCands) { - // D0 candidates - if (charmCand.decayVtxY() > ConfMlOpt.confClass2PromptProbStart) { - if (charmCand.mLambda() > 0.0f && charmCand.mAntiLambda() < 0.0f) { - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt1"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt2"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 2.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt3"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 3.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt4"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 4.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt5"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 5.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt6"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 6.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt7"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 7.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt8"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 8.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt9"), charmCand.mLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 9.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt10"), charmCand.mLambda(), charmCand.pt()); - } - // DObar candidates - if (charmCand.mLambda() < 0.0f && charmCand.mAntiLambda() > 0.0f) { - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt1"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt2"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 2.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt3"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 3.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt4"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 4.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt5"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 5.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt6"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 6.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt7"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 7.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt8"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 8.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt9"), charmCand.mAntiLambda(), charmCand.pt()); - if (charmCand.tempFitVar() < ConfMlOpt.confClass1BgProbStart + 9.0 * ConfMlOpt.confClass1BgProbStep) - registry.fill(HIST("D0D0bar_MLSel/hMassVsPt10"), charmCand.mAntiLambda(), charmCand.pt()); - } - } - } - } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processD0MLOptBgAndPrompt, "Enable filling QA plots for ML Bg and Prompt D0/D0bar selection optimization", false); - - void processQAD0D0barSel(o2::aod::FdCollision const& col, FemtoFullParticles const&) - { - auto groupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - - // loop over selected D0 candidates - for (auto const& d0cand : groupPartsD0s) { - - qaRegistry.fill(HIST("QA_D0D0barSelection/hInvMassD0"), d0cand.mLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection/hPtD0"), d0cand.pt()); - } - // loop over selected D0bar candidates - for (auto const& d0barcand : groupPartsD0bars) { - - qaRegistry.fill(HIST("QA_D0D0barSelection/hInvMassD0bar"), d0barcand.mAntiLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection/hPtD0bar"), d0barcand.pt()); - } - - // loop over selected D0 candidates from SB regions - for (auto const& d0SB : groupPartsD0sFromSB) { - - qaRegistry.fill(HIST("QA_D0D0barSelection_SB/hInvMassD0"), d0SB.mLambda()); - qaRegistry.fill(HIST("QA_D0D0barSelection_SB/hPtD0"), d0SB.pt()); - } - } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processQAD0D0barSel, "Enable filling QA plots for selected D0/D0bar cand.", true); - void processAllDmesons(o2::aod::FdCollision const& col, FemtoFullParticles const&) { auto groupPartsAllD0D0barCands = partsAllDmesons->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); @@ -804,10 +621,11 @@ struct FemtoUniversePairTaskTrackD0 { registry.fill(HIST("hPtD0D0bar"), d0d0bar.pt()); registry.fill(HIST("hPhiD0D0bar"), d0d0bar.phi()); registry.fill(HIST("hEtaD0D0bar"), d0d0bar.eta()); + registry.fill(HIST("hYD0D0bar"), d0d0bar.transRadius()); // BDT score classes - registry.fill(HIST("DebugBdt/hBdtScore1VsStatus"), d0d0bar.decayVtxX(), 1); - registry.fill(HIST("DebugBdt/hBdtScore2VsStatus"), d0d0bar.decayVtxY(), 1); - registry.fill(HIST("DebugBdt/hBdtScore3VsStatus"), d0d0bar.decayVtxZ(), 1); + registry.fill(HIST("DebugBdt/hBdtScore0"), d0d0bar.decayVtxX()); + registry.fill(HIST("DebugBdt/hBdtScore1"), d0d0bar.decayVtxY()); + registry.fill(HIST("DebugBdt/hBdtScore2"), d0d0bar.decayVtxZ()); weight = 1.0f; if (doEfficiencyCorr) { @@ -824,8 +642,8 @@ struct FemtoUniversePairTaskTrackD0 { } } if (d0d0bar.mAntiLambda() > 0.0f) { - registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPtEffCorr"), d0d0bar.mLambda(), d0d0bar.pt(), weight); - registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPtD0barEffCorr"), d0d0bar.mLambda(), d0d0bar.pt(), weight); + registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPtEffCorr"), d0d0bar.mAntiLambda(), d0d0bar.pt(), weight); + registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPtD0barEffCorr"), d0d0bar.mAntiLambda(), d0d0bar.pt(), weight); if (d0d0bar.mLambda() < 0.0f) { registry.fill(HIST("D0D0bar_oneMassHypo/hMassVsPtEffCorr"), d0d0bar.mAntiLambda(), d0d0bar.pt(), weight); registry.fill(HIST("D0D0bar_oneMassHypo/hMassVsPtD0barEffCorr"), d0d0bar.mAntiLambda(), d0d0bar.pt(), weight); @@ -838,6 +656,9 @@ struct FemtoUniversePairTaskTrackD0 { if (d0d0bar.mLambda() > 0.0f) { registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPt"), d0d0bar.mLambda(), d0d0bar.pt()); registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPtD0"), d0d0bar.mLambda(), d0d0bar.pt()); + if (ConfFill.fillBDTvsPtVsMass) { + registry.fill(HIST("DebugBdt/hBdtScoresVsMassVsPtD0"), d0d0bar.decayVtxY(), d0d0bar.decayVtxZ(), d0d0bar.mLambda(), d0d0bar.pt()); + } if (d0d0bar.mAntiLambda() < 0.0f) { registry.fill(HIST("D0D0bar_oneMassHypo/hMassVsPt"), d0d0bar.mLambda(), d0d0bar.pt()); registry.fill(HIST("D0D0bar_oneMassHypo/hMassVsPtD0"), d0d0bar.mLambda(), d0d0bar.pt()); @@ -847,8 +668,11 @@ struct FemtoUniversePairTaskTrackD0 { } } if (d0d0bar.mAntiLambda() > 0.0f) { - registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPt"), d0d0bar.mLambda(), d0d0bar.pt()); - registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPtD0bar"), d0d0bar.mLambda(), d0d0bar.pt()); + registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPt"), d0d0bar.mAntiLambda(), d0d0bar.pt()); + registry.fill(HIST("D0D0bar_doubleMassHypo/hMassVsPtD0bar"), d0d0bar.mAntiLambda(), d0d0bar.pt()); + if (ConfFill.fillBDTvsPtVsMass) { + registry.fill(HIST("DebugBdt/hBdtScoresVsMassVsPtD0bar"), d0d0bar.decayVtxY(), d0d0bar.decayVtxZ(), d0d0bar.mAntiLambda(), d0d0bar.pt()); + } if (d0d0bar.mLambda() < 0.0f) { registry.fill(HIST("D0D0bar_oneMassHypo/hMassVsPt"), d0d0bar.mAntiLambda(), d0d0bar.pt()); registry.fill(HIST("D0D0bar_oneMassHypo/hMassVsPtD0bar"), d0d0bar.mAntiLambda(), d0d0bar.pt()); @@ -909,7 +733,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupPartsTrack, PartitionType groupPartsD0, PartType parts, float magFieldTesla, int multCol) + void doSameEvent(PartitionType const& groupPartsTrack, PartitionType const& groupPartsD0, PartType const& parts, float magFieldTesla, int multCol) { /// Histogramming same event @@ -917,7 +741,7 @@ struct FemtoUniversePairTaskTrackD0 { trackHistoPartD0D0bar.fillQA(d0candidate); } - float tpcNSigmaPr, tofNSigmaPr, tpcNSigmaPi, tofNSigmaPi, tpcNSigmaKa, tofNSigmaKa; + float tpcNSigmaPr = 0.0f, tofNSigmaPr = 0.0f, tpcNSigmaPi = 0.0f, tofNSigmaPi = 0.0f, tpcNSigmaKa = 0.0f, tofNSigmaKa = 0.0f; if (!ConfTrack.confIsSame) { for (auto const& track : groupPartsTrack) { @@ -966,7 +790,7 @@ struct FemtoUniversePairTaskTrackD0 { } } // // Close Pair Rejection - if (confIsCPR.value) { + if (ConfCPR.confIsCPR.value) { if (pairCloseRejection.isClosePair(track, d0candidate, parts, magFieldTesla, femto_universe_container::EventType::same)) { continue; } @@ -984,31 +808,31 @@ struct FemtoUniversePairTaskTrackD0 { } } - /// process function for to call doSameEvent with Data - /// call this process function if you need D0/D0bar candidates which pass only one mass hypothesis + /// process function to call doSameEvent with Data + /// call this process function if you D0-track pairs /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable - void processSameEvent(o2::aod::FdCollision const& col, - FemtoFullParticles const& parts) + void processSameEventSigD0(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) { fillCollision(col); - auto theGroupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - switch (confChooseD0trackCorr) { - case 0: - doSameEvent(theGroupPartsTrack, theGroupPartsD0s, parts, col.magField(), col.multNtr()); - break; - case 1: - doSameEvent(theGroupPartsTrack, theGroupPartsD0bars, parts, col.magField(), col.multNtr()); - break; - default: - break; - } + doSameEvent(groupedTracks, groupedD0s, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSigD0, "Enable processing same event for signal D0-track pairs", false); + + void processSameEventSigD0bar(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0bars, parts, col.magField(), col.multNtr()); } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEvent, "Enable processing same event", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSigD0bar, "Enable processing same event for signal D0bar-track pairs", false); /// process function for to call doSameEvent with Data /// call this process function to include candidates which pass as well the selection for both D0 and D0bar candidates @@ -1020,8 +844,8 @@ struct FemtoUniversePairTaskTrackD0 { fillCollision(col); auto theGroupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0s = partsAllD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto theGroupPartsD0bars = partsAllD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto theGroupPartsD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto theGroupPartsD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); switch (confChooseD0trackCorr) { case 0: @@ -1036,17 +860,39 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventWithDoubleHypo, "Enable processing same event", false); - /// process function for to call doSameEvent with Data - /// call this process to obtain the function for D0/D0bar candidates from side-band regions + /// process function to call doSameEvent with Data + /// call processSameEventSideBandD0 to obtain the function for D0 candidates from side-band regions /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable + void processSameEventSideBandD0(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0s = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0s, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSideBandD0, "Enable processing same event for sideband D0-track pairs", false); + + void processSameEventSideBandD0bar(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) + { + fillCollision(col); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupedD0bars = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + doSameEvent(groupedTracks, groupedD0bars, parts, col.magField(), col.multNtr()); + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processSameEventSideBandD0bar, "Enable processing same event for sideband D0bar-track pairs", false); + void processSameEventSB(o2::aod::FdCollision const& col, FemtoFullParticles const& parts) { fillCollision(col); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - auto groupPartsD0barsFromSB = partsD0barsFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsD0sFromSB = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsD0barsFromSB = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); switch (confChooseD0trackCorr) { case 0: @@ -1065,7 +911,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Monte Carlo Reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processSameEventMCRecoSignal(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMCRecoSignal(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); @@ -1090,7 +936,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table (Monte Carlo Reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processSameEventMCRecoSB(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMCRecoSB(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); @@ -1115,7 +961,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param col subscribe to the collision table /// \param parts subscribe to the joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// \param FdMCParticles subscribe to the Monte Carlo truth table - void processSameEventMcTruth(aod::FdCollision const& col, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processSameEventMcTruth(aod::FdCollision const& col, FemtoMcParticles const& parts, aod::FdMCParticles const&) { fillCollision(col); /// !!! needs to be finished -> add possibility to change between D0 and D0bar @@ -1147,7 +993,7 @@ struct FemtoUniversePairTaskTrackD0 { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsTrack, PartitionType groupPartsD0, PartType parts, float magFieldTesla, int multCol) + void doMixedEvent(PartitionType const& groupPartsTrack, PartitionType const& groupPartsD0, PartType const& parts, float magFieldTesla, int multCol) { for (auto const& [track, d0candidate] : combinations(CombinationsFullIndexPolicy(groupPartsTrack, groupPartsD0))) { @@ -1166,7 +1012,7 @@ struct FemtoUniversePairTaskTrackD0 { } } // // Close Pair Rejection - if (confIsCPR.value) { + if (ConfCPR.confIsCPR.value) { if (pairCloseRejection.isClosePair(track, d0candidate, parts, magFieldTesla, femto_universe_container::EventType::mixed)) { continue; } @@ -1186,20 +1032,18 @@ struct FemtoUniversePairTaskTrackD0 { } /// process function for to call doMixedEvent with Data - /// call this process function if you need D0/D0bar candidates which pass only one mass hypothesis + /// call this process function if you need D0-track ME function /// @param cols subscribe to the collisions table (Data) /// @param parts subscribe to the femtoUniverseParticleTable - void processMixedEvent(o2::aod::FdCollisions const& cols, - FemtoFullParticles const& parts) + void processMixedEventSigD0(o2::aod::FdCollisions const& cols, FemtoFullParticles const& parts) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multNtr(); mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); - auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto theGroupPartsD0s = partsD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto theGroupPartsD0bars = partsD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupedD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1207,22 +1051,31 @@ struct FemtoUniversePairTaskTrackD0 { if (magFieldTesla1 != magFieldTesla2) { continue; } - /// \todo before mixing we should check whether both collisions contain a pair of particles! - // if (partsD0.size() == 0 || kNPart2Evt1 == 0 || kNPart1Evt2 == 0 || partsTrack.size() == 0 ) continue; + doMixedEvent(groupedTracks, groupedD0s, parts, magFieldTesla1, multiplicityCol); + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventSigD0, "Enable processing signal D0-track mixed events", false); - switch (confChooseD0trackCorr) { - case 0: - doMixedEvent(groupPartsTrack, theGroupPartsD0s, parts, magFieldTesla1, multiplicityCol); - break; - case 1: - doMixedEvent(groupPartsTrack, theGroupPartsD0bars, parts, magFieldTesla1, multiplicityCol); - break; - default: - break; + void processMixedEventSigD0bar(o2::aod::FdCollisions const& cols, FemtoFullParticles const& parts) + { + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { + + const int multiplicityCol = collision1.multNtr(); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); + + auto groupedTracks = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupedD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; } + doMixedEvent(groupedTracks, groupedD0bars, parts, magFieldTesla1, multiplicityCol); } } - PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEvent, "Enable processing mixed events", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventSigD0bar, "Enable processing signal D0bar-track mixed events", false); /// process function for to call doMixedEvent with Data /// call this process function to include candidates which pass as well the selection for both D0 and D0bar candidates @@ -1237,8 +1090,8 @@ struct FemtoUniversePairTaskTrackD0 { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto theGroupPartsD0s = partsAllD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto theGroupPartsD0bars = partsAllD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto theGroupPartsD0s = partSigD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto theGroupPartsD0bars = partSigD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1275,8 +1128,8 @@ struct FemtoUniversePairTaskTrackD0 { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); auto groupPartsTrack = partsTrack->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - auto groupPartsD0sFromSB = partsD0sFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto groupPartsD0barsFromSB = partsD0barsFromSB->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsD0sFromSB = partSidebandD0s->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsD0barsFromSB = partSidebandD0bars->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1304,7 +1157,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCRecoSignal(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCRecoSignal(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1340,7 +1193,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCRecoSB(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCRecoSB(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1376,7 +1229,7 @@ struct FemtoUniversePairTaskTrackD0 { /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) /// @param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth /// @param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table - void processMixedEventMCTruth(aod::FdCollisions const& cols, FemtoMcRecoParticles const& parts, aod::FdMCParticles const&) + void processMixedEventMCTruth(aod::FdCollisions const& cols, FemtoMcParticles const& parts, aod::FdMCParticles const&) { for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { @@ -1408,7 +1261,7 @@ struct FemtoUniversePairTaskTrackD0 { } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMixedEventMCTruth, "MC Truth - enable processing mixed events for gen. D0/D0bar", false); - void processMcReco(FemtoMcRecoParticles const& recoParts, aod::FdMCParticles const& mcParts) + void processMcReco(FemtoMcParticles const& recoParts, aod::FdMCParticles const& mcParts) { for (auto const& part : recoParts) { auto mcPartId = part.fdMCParticleId(); @@ -1419,85 +1272,91 @@ struct FemtoUniversePairTaskTrackD0 { weight = 1.0f; // filling the histograms for identified hadrons - if (part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) { + if ((part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && (std::abs(part.eta()) < ConfTrack.confTrackEtaMax)) { if (part.sign() > 0) { - if ((mcpart.pdgMCTruth() == PDG_t::kProton) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kProton) && isProtonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton))) { mcRecoRegistry.fill(HIST("hMcRecPrPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPrPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == PDG_t::kPiPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kPiPlus) && isPionNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion))) { mcRecoRegistry.fill(HIST("hMcRecPipPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPipPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == PDG_t::kKPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == PDG_t::kKPlus) && isKaonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { mcRecoRegistry.fill(HIST("hMcRecKpPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecKpPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } } else if (part.sign() < 0) { - if ((mcpart.pdgMCTruth() == -PDG_t::kProton) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kProton) && isProtonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton))) { mcRecoRegistry.fill(HIST("hMcRecAntiPrPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecAntiPrPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == -PDG_t::kPiPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kPiPlus) && isPionNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion))) { mcRecoRegistry.fill(HIST("hMcRecPimPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecPimPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } - if ((mcpart.pdgMCTruth() == -PDG_t::kKPlus) && (part.pt() > ConfTrack.confTrackLowPtCut) && (part.pt() < ConfTrack.confTrackHighPtCut) && isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if ((mcpart.pdgMCTruth() == -PDG_t::kKPlus) && isKaonNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { mcRecoRegistry.fill(HIST("hMcRecKmPt"), part.pt()); if (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary) { mcRecoRegistry.fill(HIST("hMcRecKmPtGenEtaGen"), mcpart.pt(), mcpart.eta()); } } } - } else if ((part.partType() == aod::femtouniverseparticle::ParticleType::kD0) && (part.pt() > ConfDmesons.confMinPtD0D0barReco) && (part.pt() < ConfDmesons.confMaxPtD0D0barReco)) { + } else if ((part.partType() == aod::femtouniverseparticle::ParticleType::kD0) && (part.pt() > ConfDmesons.confMinPtD0D0barReco) && (part.pt() < ConfDmesons.confMaxPtD0D0barReco) && (part.decayVtxZ() < ConfMlProb.confMlProbNonPromptMax)) { + if (mcpart.pdgMCTruth() == ConfDmesons.confPDGCodeD0) { mcRecoRegistry.fill(HIST("hMcRecD0"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0Pt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0Phi"), part.phi()); + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); + if (part.tpcNClsFound() == 0) { // prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0Prompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0PromptPt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0PromptPhi"), part.phi()); + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } else if (part.tpcNClsFound() == 1) { // non-prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0NonPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0NonPromptPt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0NonPromptPhi"), part.phi()); + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } } else if (mcpart.pdgMCTruth() == ConfDmesons.confPDGCodeD0bar) { mcRecoRegistry.fill(HIST("hMcRecD0bar"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0barPt"), part.pt()); mcRecoRegistry.fill(HIST("hMcRecD0barPhi"), part.phi()); + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); + if (part.tpcNClsFound() == 0) { // prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0barPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0barPromptPt"), part.pt()); + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } else if (part.tpcNClsFound() == 1) { // non-prompt candidates mcRecoRegistry.fill(HIST("hMcRecD0barNonPrompt"), part.pt(), part.eta()); mcRecoRegistry.fill(HIST("hMcRecD0barNonPromptPt"), part.pt()); + registry.fill(HIST("DebugBdtMcReco/hBdtScoresVsPtNonPromptD0bar"), part.decayVtxX(), part.decayVtxY(), part.decayVtxZ(), part.pt()); } } } - // if (isParticleNSigma(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { - // hTrackDCA.fillQA(part); - //} This part required change } } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMcReco, "Process MC reco data", false); - void processMcRecoD0InvMass(FemtoMcRecoParticles const& recoParts) + void processMcRecoD0InvMass(FemtoMcParticles const& recoParts) { for (auto const& part : recoParts) { // filling the histograms for identified hadrons - if ((part.partType() == aod::femtouniverseparticle::ParticleType::kD0) && (part.pt() > ConfDmesons.confMinPtD0D0barReco) && (part.pt() < ConfDmesons.confMaxPtD0D0barReco)) { + if ((part.partType() == aod::femtouniverseparticle::ParticleType::kD0) && (part.pt() > ConfDmesons.confMinPtD0D0barReco) && (part.pt() < ConfDmesons.confMaxPtD0D0barReco) && (part.decayVtxZ() < ConfMlProb.confMlProbNonPromptMax)) { // getting the efficiency value if (doEfficiencyCorr) { weight = efficiencyCalculator.getWeight(ParticleNo::TWO, part.pt()); @@ -1513,7 +1372,7 @@ struct FemtoUniversePairTaskTrackD0 { } else { mcRecoRegistry.fill(HIST("hMassVsPtD0Bkg"), part.mLambda(), part.pt(), weight); } - if (fillCorrBkgs) { + if (ConfFill.fillCorrBkgs) { if (part.sign() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0) { // D0 -> pi+K-pi0 mcRecoRegistry.fill(HIST("hMassVsPtD0ToPiKaPi"), part.mLambda(), part.pt(), weight); } else if (part.sign() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiPi) { // D0 -> pi+pi- @@ -1537,15 +1396,15 @@ struct FemtoUniversePairTaskTrackD0 { } else { mcRecoRegistry.fill(HIST("hMassVsPtD0barBkg"), part.mAntiLambda(), part.pt(), weight); } - if (fillCorrBkgs) { + if (ConfFill.fillCorrBkgs) { if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiKPi0) { // D0 -> pi+K-pi0 - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiKaPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiKaPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiPi) { // D0 -> pi+pi- - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiPiPi0) { // D0 -> pi+pi-pi0 - mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPiPi"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToPiPiPi"), part.mAntiLambda(), part.pt(), weight); } else if (part.sign() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToKK) { // D0 -> K+K- - mcRecoRegistry.fill(HIST("hMassVsPtD0barToKaKa"), part.mLambda(), part.pt(), weight); + mcRecoRegistry.fill(HIST("hMassVsPtD0barToKaKa"), part.mAntiLambda(), part.pt(), weight); } } if (part.tpcNClsFound() == 0) { // prompt candidates @@ -1562,28 +1421,17 @@ struct FemtoUniversePairTaskTrackD0 { void processMcTruth(aod::FDParticles const& parts) { for (auto const& part : parts) { - if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { + if (part.partType() != static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { continue; } int pdgCode = static_cast(part.tempFitVar()); - int8_t hfFlagMcGen = static_cast(part.mLambda()); + auto hfFlagMcGen = static_cast(part.mLambda()); const auto& pdgParticle = pdgMC->GetParticle(pdgCode); if (!pdgParticle) { continue; } - - if (pdgParticle->Charge() > 0.0) { - mcTruthRegistry.fill(HIST("hMcGenAllPositivePt"), part.pt()); - } - if (pdgCode == PDG_t::kPiPlus) { - mcTruthRegistry.fill(HIST("hMcGenPipPtVsEta"), part.pt(), part.eta()); - mcTruthRegistry.fill(HIST("hMcGenPipPt"), part.pt()); - } - if (pdgCode == PDG_t::kKPlus) { - mcTruthRegistry.fill(HIST("hMcGenKpPtVsEta"), part.pt(), part.eta()); - mcTruthRegistry.fill(HIST("hMcGenKpPt"), part.pt()); - } + // filling the histograms for generated D0 and D0bar mesons if (pdgCode == o2::constants::physics::Pdg::kD0) { if (std::abs(hfFlagMcGen) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { mcTruthRegistry.fill(HIST("hMcGenD0"), part.pt(), part.eta()); @@ -1591,12 +1439,45 @@ struct FemtoUniversePairTaskTrackD0 { if (static_cast(part.mAntiLambda()) == genPromptD0) { mcTruthRegistry.fill(HIST("hMcGenD0Prompt"), part.pt(), part.eta()); mcTruthRegistry.fill(HIST("hMcGenD0PromptPt"), part.pt()); + mcTruthRegistry.fill(HIST("hMcGenD0PromptPtLessBins"), part.pt()); } else if (static_cast(part.mAntiLambda()) == genNonPromptD0) { mcTruthRegistry.fill(HIST("hMcGenD0NonPrompt"), part.pt(), part.eta()); mcTruthRegistry.fill(HIST("hMcGenD0NonPromptPt"), part.pt()); + mcTruthRegistry.fill(HIST("hMcGenD0NonPromptPtLessBins"), part.pt()); } } } + if (pdgCode == o2::constants::physics::Pdg::kD0Bar) { + if (std::abs(hfFlagMcGen) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + mcTruthRegistry.fill(HIST("hMcGenD0bar"), part.pt(), part.eta()); + mcTruthRegistry.fill(HIST("hMcGenD0barPt"), part.pt()); + if (static_cast(part.mAntiLambda()) == genPromptD0) { + mcTruthRegistry.fill(HIST("hMcGenD0barPrompt"), part.pt(), part.eta()); + mcTruthRegistry.fill(HIST("hMcGenD0barPromptPt"), part.pt()); + mcTruthRegistry.fill(HIST("hMcGenD0barPromptPtLessBins"), part.pt()); + } else if (static_cast(part.mAntiLambda()) == genNonPromptD0) { + mcTruthRegistry.fill(HIST("hMcGenD0barNonPrompt"), part.pt(), part.eta()); + mcTruthRegistry.fill(HIST("hMcGenD0barNonPromptPt"), part.pt()); + mcTruthRegistry.fill(HIST("hMcGenD0barNonPromptPtLessBins"), part.pt()); + } + } + } + // filling the histograms for identified hadrons + if (part.pt() < ConfTrack.confTrackLowPtCut || part.pt() > ConfTrack.confTrackHighPtCut || std::abs(part.eta()) > ConfTrack.confTrackEtaMax) { + continue; + } + if (pdgParticle->Charge() > 0.0) { + mcTruthRegistry.fill(HIST("hMcGenAllPositivePt"), part.pt()); + } + if (pdgCode == PDG_t::kPiPlus) { + mcTruthRegistry.fill(HIST("hMcGenPipPtVsEta"), part.pt(), part.eta()); + mcTruthRegistry.fill(HIST("hMcGenPipPt"), part.pt()); + } + if (pdgCode == PDG_t::kKPlus) { + mcTruthRegistry.fill(HIST("hMcGenKpPtVsEta"), part.pt(), part.eta()); + mcTruthRegistry.fill(HIST("hMcGenKpPt"), part.pt()); + } + if (pdgCode == PDG_t::kProton) { mcTruthRegistry.fill(HIST("hMcGenPrPtVsEta"), part.pt(), part.eta()); mcTruthRegistry.fill(HIST("hMcGenPrPt"), part.pt()); @@ -1613,19 +1494,6 @@ struct FemtoUniversePairTaskTrackD0 { mcTruthRegistry.fill(HIST("hMcGenKmPtVsEta"), part.pt(), part.eta()); mcTruthRegistry.fill(HIST("hMcGenKmPt"), part.pt()); } - if (pdgCode == o2::constants::physics::Pdg::kD0Bar) { - if (std::abs(hfFlagMcGen) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - mcTruthRegistry.fill(HIST("hMcGenD0bar"), part.pt(), part.eta()); - mcTruthRegistry.fill(HIST("hMcGenD0barPt"), part.pt()); - if (static_cast(part.mAntiLambda()) == genPromptD0) { - mcTruthRegistry.fill(HIST("hMcGenD0barPrompt"), part.pt(), part.eta()); - mcTruthRegistry.fill(HIST("hMcGenD0barPromptPt"), part.pt()); - } else if (static_cast(part.mAntiLambda()) == genNonPromptD0) { - mcTruthRegistry.fill(HIST("hMcGenD0barNonPrompt"), part.pt(), part.eta()); - mcTruthRegistry.fill(HIST("hMcGenD0barNonPromptPt"), part.pt()); - } - } - } if (pdgCode == -PDG_t::kProton) { mcTruthRegistry.fill(HIST("hMcGenAntiPrPtVsEta"), part.pt(), part.eta()); mcTruthRegistry.fill(HIST("hMcGenAntiPrPt"), part.pt()); @@ -1633,6 +1501,55 @@ struct FemtoUniversePairTaskTrackD0 { } } PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processMcTruth, "Process MC truth data", false); + + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoUniverseParticleTable + void processPurityQA(aod::FdCollision const& col, FemtoFullSigmaParticles const&) + { + fillCollision(col); + + auto theGroupPartsOne = partsTrackFullSigma->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + for (const auto& part : theGroupPartsOne) { + + if (!isParticleNSigma(part.p(), part.tpcFullNSigmaPr(), part.tofFullNSigmaPr(), part.tpcFullNSigmaPi(), part.tofFullNSigmaPi(), part.tpcFullNSigmaKa(), part.tofFullNSigmaKa())) { + continue; + } + if (ConfTrack.confDoPartNsigmaRejection && isParticleNSigmaRejected(part.p(), part.tpcFullNSigmaPi(), part.tofFullNSigmaPi(), part.tpcFullNSigmaKa(), part.tofFullNSigmaKa(), part.tpcFullNSigmaPr(), part.tofFullNSigmaPr())) { + continue; + } + + trackHistoPartTrack.fillQA(part); + if (ConfFill.fillTrackQA) { + qaRegistry.fill(HIST("TrackQA/hCharge"), part.sign()); + qaRegistry.fill(HIST("TrackQA/hTPCfindable"), part.tpcNClsFindable()); + qaRegistry.fill(HIST("TrackQA/hTPCfound"), part.tpcNClsFound()); + qaRegistry.fill(HIST("TrackQA/hTPCcrossedOverFindable"), part.tpcCrossedRowsOverFindableCls()); + qaRegistry.fill(HIST("TrackQA/hTPCcrossedRows"), part.tpcNClsCrossedRows()); + qaRegistry.fill(HIST("TrackQA/hTPCfindableVsCrossed"), part.tpcNClsFindable(), part.tpcNClsCrossedRows()); + qaRegistry.fill(HIST("TrackQA/hTPCshared"), part.tpcNClsShared()); + qaRegistry.fill(HIST("TrackQA/hTPCfractionSharedCls"), part.tpcFractionSharedCls()); + qaRegistry.fill(HIST("TrackQA/hITSclusters"), part.itsNCls()); + qaRegistry.fill(HIST("TrackQA/hITSclustersIB"), part.itsNClsInnerBarrel()); + qaRegistry.fill(HIST("TrackQA/hDCAz"), part.pt(), part.dcaZ()); + qaRegistry.fill(HIST("TrackQA/hTPCdEdX"), part.p(), part.tpcSignal()); + } + if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kProton) { + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPr"), part.pt(), part.tpcFullNSigmaPr()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPr"), part.pt(), part.tofFullNSigmaPr()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPr"), part.pt(), std::hypot(part.tpcFullNSigmaPr(), part.tofFullNSigmaPr())); + } else if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kPiPlus) { + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCPi"), part.pt(), part.tpcFullNSigmaPi()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFPi"), part.pt(), part.tofFullNSigmaPi()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombPi"), part.pt(), std::hypot(part.tpcFullNSigmaPi(), part.tofFullNSigmaPi())); + } else if (std::abs(ConfTrack.confPDGCodeTrack) == PDG_t::kKPlus) { + qaRegistry.fill(HIST("PurityQA/nSigmaFullTPCKa"), part.pt(), part.tpcFullNSigmaKa()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullTOFKa"), part.pt(), part.tofFullNSigmaKa()); + qaRegistry.fill(HIST("PurityQA/nSigmaFullCombKa"), part.pt(), std::hypot(part.tpcFullNSigmaKa(), part.tofFullNSigmaKa())); + } + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackD0, processPurityQA, "Enable processing QA for purity estimation", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx index 1580b4bcfe7..037c88b9837 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackNucleus.cxx @@ -191,7 +191,7 @@ struct FemtoUniversePairTaskTrackNucleus { ConfigurableAxis confMtBins3D{"confMtBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "Multiplicity binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confKstarBins{"confKstarBins", {1500, 0., 6.}, "Binning kstar"}; ConfigurableAxis confKtBins{"confKtBins", {150, 0., 9.}, "Binning kT"}; @@ -491,7 +491,7 @@ struct FemtoUniversePairTaskTrackNucleus { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { mixQARegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multV0M()})); eventHisto.fillQA(col); @@ -510,7 +510,7 @@ struct FemtoUniversePairTaskTrackNucleus { /// \param pairType describes charge of correlation pair (plus-plus (1), minus-minus (2), plus-minus (3), minus-plus (4)) /// \param fillQA enables filling of QA histograms template - void doSameEvent(PartitionType groupTrack, PartitionType groupNucleus, PartType parts, float magFieldTesla, int multCol, int pairType, bool /*fillQA*/) + void doSameEvent(const PartitionType& groupTrack, const PartitionType& groupNucleus, const PartType& parts, float magFieldTesla, int multCol, int pairType, bool /*fillQA*/) { for (const auto& part : groupTrack) { if (!isParticleNsigma((int8_t)1, part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Deuteron), trackCuts.getNsigmaTOF(part, o2::track::PID::Deuteron), part.tpcSignal())) { @@ -680,7 +680,7 @@ struct FemtoUniversePairTaskTrackNucleus { /// \param multCol multiplicity of the collision /// \param pairType describes charge of correlation pair (plus-minus (1), plus-plus (2), minus-minus (3)) template - void doMixedEvent(PartitionType groupTrack, PartitionType groupNucleus, PartType parts, float magFieldTesla, int multCol, int pairType) + void doMixedEvent(const PartitionType& groupTrack, const PartitionType& groupNucleus, const PartType& parts, float magFieldTesla, int multCol, int pairType) { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupTrack, groupNucleus))) { diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx index 5fc9dd9c59a..7aa4e4bc2c4 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackPhi.cxx @@ -26,6 +26,7 @@ #include "PWGCF/FemtoUniverse/DataModel/FemtoDerived.h" #include +#include #include #include #include @@ -43,6 +44,9 @@ #include #include +#include +#include + #include #include #include @@ -59,18 +63,30 @@ using namespace o2::soa; namespace { -// static constexpr int NPart = 2; -// static constexpr int NCuts = 5; -static const std::vector partNames{"PhiCandidate", "Track"}; -static const std::vector cutNames{"MaxPt", "PIDthr", "nSigmaTPC", "nSigmaTPCTOF", "MaxP"}; -// static const float cutsTable[NPart][NCuts]{ //unused variable -// {4.05f, 1.f, 3.f, 3.f, 100.f}, -// {4.05f, 1.f, 3.f, 3.f, 100.f}}; +const std::vector partNames{"PhiCandidate", "Track"}; +const std::vector cutNames{"MaxPt", "PIDthr", "nSigmaTPC", "nSigmaTPCTOF", "MaxP"}; + +// Constants for pT / momentum +constexpr float kMomCutLow = 0.5f; +constexpr float kMomCutKaonLow = 0.3f; +constexpr float kMomCutKaonMid = 0.45f; +constexpr float kMomCutKaonHigh = 0.55f; +constexpr float kMomCutKaonMax = 1.5f; + +// Constants for nSigma cuts +constexpr float kNSigmaStrict = 1.0f; +constexpr float kNSigmaMedium = 2.0f; +constexpr float kNSigmaStandard = 3.0f; + +// Helper constants and indices +constexpr int kInvalidMCPartId = -1; +constexpr int kPhiDaughterOffsetPos = 2; +constexpr int kPhiDaughterOffsetNeg = 1; } // namespace struct FemtoUniversePairTaskTrackPhi { - Service pdgMC; + Service pdgMC = {}; using FilteredFemtoFullParticles = soa::Join; @@ -80,83 +96,88 @@ struct FemtoUniversePairTaskTrackPhi { using FemtoRecoParticles = soa::Join; Preslice perColMC = aod::femtouniverseparticle::fdCollisionId; - Configurable ConfZVertexCut{"ConfZVertexCut", 10.f, "Event sel: Maximum z-Vertex (cm)"}; - Configurable ConfNEventsMix{"ConfNEventsMix", 5, "Number of events for mixing"}; - Filter collisionFilter = (nabs(aod::collision::posZ) < ConfZVertexCut); + Configurable confZVertexCut{"confZVertexCut", 10.f, "Event sel: Maximum z-Vertex (cm)"}; + // Configurable confNEventsMix{"confNEventsMix", 5, "Number of events for mixing"}; + Filter collisionFilter = (nabs(aod::collision::posZ) < confZVertexCut); using FilteredFDCollisions = soa::Filtered; using FilteredFDCollision = FilteredFDCollisions::iterator; - Configurable ConfCPRIsEnabled{"ConfCPRIsEnabled", false, "Close Pair Rejection"}; - Configurable ConfCPRPlotPerRadii{"ConfCPRPlotPerRadii", false, "Plot CPR per radii"}; - Configurable ConfCPRdeltaPhiCutMax{"ConfCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; - Configurable ConfCPRdeltaPhiCutMin{"ConfCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; - Configurable ConfCPRdeltaEtaCutMax{"ConfCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; - Configurable ConfCPRdeltaEtaCutMin{"ConfCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; - Configurable ConfCPRInvMassCutMin{"ConfCPRInvMassCutMin", 1.014, "Invariant mass (low) cut for Close Pair Rejection"}; - Configurable ConfCPRInvMassCutMax{"ConfCPRInvMassCutMax", 1.026, "Invariant mass (high) cut for Close Pair Rejection"}; - Configurable ConfCPRChosenRadii{"ConfCPRChosenRadii", 0.80, "Delta Eta cut for Close Pair Rejection"}; + Configurable confCPRIsEnabled{"confCPRIsEnabled", false, "Close Pair Rejection"}; + Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; + Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; + Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; + Configurable confCPRInvMassCutMin{"confCPRInvMassCutMin", 1.014, "Invariant mass (low) cut for Close Pair Rejection"}; + Configurable confCPRInvMassCutMax{"confCPRInvMassCutMax", 1.026, "Invariant mass (high) cut for Close Pair Rejection"}; + Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.80, "Delta Eta cut for Close Pair Rejection"}; ConfigurableAxis confDeltaEtaAxis{"confDeltaEtaAxis", {100, -0.15, 0.15}, "DeltaEta"}; ConfigurableAxis confDeltaPhiStarAxis{"confDeltaPhiStarAxis", {100, -0.15, 0.15}, "DeltaPhiStar"}; /// Table for both particles - Configurable ConfPIDProtonNsigmaCombined{"ConfPIDProtonNsigmaCombined", 3.0, "TPC and TOF Proton Sigma (combined) for momentum > 0.5"}; - Configurable ConfPIDProtonNsigmaTPC{"ConfPIDProtonNsigmaTPC", 3.0, "TPC Proton Sigma for momentum < 0.5"}; - Configurable ConfPIDKaonNsigmaReject{"ConfPIDKaonNsigmaReject", 3.0, "Reject if particle could be a Kaon combined nsigma value."}; - Configurable ConfPIDPionNsigmaReject{"ConfPIDPionNsigmaReject", 3.0, "Reject if particle could be a Pion combined nsigma value."}; - Configurable ConfPIDProtonNsigmaReject{"ConfPIDProtonNsigmaReject", 3.0, "Reject if particle could be a Proton combined nsigma value."}; - Configurable ConfPIDPionNsigmaCombined{"ConfPIDPionNsigmaCombined", 3.0, "TPC and TOF Pion Sigma (combined) for momentum > 0.5"}; - Configurable ConfPIDPionNsigmaTPC{"ConfPIDPionNsigmaTPC", 3.0, "TPC Pion Sigma for momentum < 0.5"}; - Configurable ConfIsMC{"ConfIsMC", false, "Enable additional Histograms in the case of a MonteCarlo Run"}; - Configurable ConfUse3D{"ConfUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; - Configurable ConfBinsPhi{"ConfBinsPhi", 29, "Number of phi bins in deta dphi"}; - Configurable ConfBinsEta{"ConfBinsEta", 29, "Number of eta bins in deta dphi"}; + Configurable confPIDProtonNsigmaCombined{"confPIDProtonNsigmaCombined", 3.0, "TPC and TOF Proton Sigma (combined) for momentum > 0.5"}; + Configurable confPIDProtonNsigmaTPC{"confPIDProtonNsigmaTPC", 3.0, "TPC Proton Sigma for momentum < 0.5"}; + Configurable confPIDKaonNsigmaReject{"confPIDKaonNsigmaReject", 3.0, "Reject if particle could be a Kaon combined nsigma value."}; + Configurable confPIDPionNsigmaReject{"confPIDPionNsigmaReject", 3.0, "Reject if particle could be a Pion combined nsigma value."}; + Configurable confPIDProtonNsigmaReject{"confPIDProtonNsigmaReject", 3.0, "Reject if particle could be a Proton combined nsigma value."}; + Configurable confPIDPionNsigmaCombined{"confPIDPionNsigmaCombined", 3.0, "TPC and TOF Pion Sigma (combined) for momentum > 0.5"}; + Configurable confPIDPionNsigmaTPC{"confPIDPionNsigmaTPC", 3.0, "TPC Pion Sigma for momentum < 0.5"}; + Configurable confIsMC{"confIsMC", false, "Enable additional Histograms in the case of a MonteCarlo Run"}; + Configurable confUse3D{"confUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; + Configurable confBinsPhi{"confBinsPhi", 29, "Number of phi bins in deta dphi"}; + Configurable confBinsEta{"confBinsEta", 29, "Number of eta bins in deta dphi"}; /// Particle 1 --- IDENTIFIED TRACK - Configurable ConfTrackPDGCode{"ConfTrackPDGCode", 2212, "Particle 2 - PDG code"}; - Configurable ConfTrackSign{"ConfTrackSign", 1, "Track sign"}; - Configurable ConfTrackIsIdentified{"ConfTrackIsIdentified", true, "Enable PID for the track"}; - Configurable ConfTrackIsRejected{"ConfTrackIsRejected", true, "Enable PID rejection for the track other species than the identified one."}; - Configurable ConfTrackPtPIDLimit{"ConfTrackPtPIDLimit", 0.5, "Momentum threshold for change of the PID method (from using TPC to TPC and TOF)."}; - Configurable ConfTrackPtLow{"ConfTrackPtLow", 0.5, "Lower limit of the hadron pT."}; - Configurable ConfTrackPtHigh{"ConfTrackPtHigh", 2.5, "Higher limit of the hadron pT."}; + Configurable confTrackPDGCode{"confTrackPDGCode", 2212, "Particle 2 - PDG code"}; + Configurable confTrackSign{"confTrackSign", 1, "Track sign"}; + Configurable confTrackIsIdentified{"confTrackIsIdentified", true, "Enable PID for the track"}; + Configurable confTrackIsRejected{"confTrackIsRejected", true, "Enable PID rejection for the track other species than the identified one."}; + Configurable confTrackPtPIDLimit{"confTrackPtPIDLimit", 0.5, "Momentum threshold for change of the PID method (from using TPC to TPC and TOF)."}; + Configurable confTrackPtLow{"confTrackPtLow", 0.5, "Lower limit of the hadron pT."}; + Configurable confTrackPtHigh{"confTrackPtHigh", 2.5, "Higher limit of the hadron pT."}; + Configurable confTrackUseRun3PIDforKaons{"confTrackUseRun3PIDforKaons", true, "Use Run3 PID for kaons from Veronika Barbasova's AN (https://alice-notes.web.cern.ch/node/1758). If this is on the other PID methods are ignored for kaons."}; /// Partitions for the track (particle 1) - Partition partsTrack = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && - (aod::femtouniverseparticle::sign == ConfTrackSign) && - (aod::femtouniverseparticle::pt > ConfTrackPtLow) && - (aod::femtouniverseparticle::pt < ConfTrackPtHigh); + Partition partsTrack = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && + (aod::femtouniverseparticle::sign == confTrackSign) && + (aod::femtouniverseparticle::pt > confTrackPtLow) && + (aod::femtouniverseparticle::pt < confTrackPtHigh); - Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && - (aod::femtouniverseparticle::sign == ConfTrackSign) && - (aod::femtouniverseparticle::pt > ConfTrackPtLow) && - (aod::femtouniverseparticle::pt < ConfTrackPtHigh); + Partition partsTrackMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)) && + (aod::femtouniverseparticle::sign == confTrackSign) && + (aod::femtouniverseparticle::pt > confTrackPtLow) && + (aod::femtouniverseparticle::pt < confTrackPtHigh); /// Particle 2 --- PHI MESON - Configurable ConfPhiPtLow{"ConfPhiPtLow", 0.8, "Lower limit of the Phi pT."}; - Configurable ConfPhiPtHigh{"ConfPhiPtHigh", 4.0, "Higher limit of the Phi pT."}; + Configurable confPhiPtLow{"confPhiPtLow", 0.8, "Lower limit of the Phi pT."}; + Configurable confPhiPtHigh{"confPhiPtHigh", 4.0, "Higher limit of the Phi pT."}; Configurable confInvMassLowLimitPhi{"confInvMassLowLimitPhi", 1.011, "Lower limit of the Phi invariant mass"}; // change that to do invariant mass cut Configurable confInvMassUpLimitPhi{"confInvMassUpLimitPhi", 1.027, "Upper limit of the Phi invariant mass"}; + Configurable confFillMinvBackground{"confFillMinvBackground", false, "Fill background histograms for Phi invariant mass."}; + Configurable confFillMinvBackgroundKpKm{"confFillMinvBackgroundKpKm", false, "Fill background histograms for K+K- invariant mass."}; + Configurable confFillMinvBackgroundKpKp{"confFillMinvBackgroundKpKp", false, "Fill background histograms for K+K+ invariant mass."}; + Configurable confFillMinvBackgroundKmKm{"confFillMinvBackgroundKmKm", false, "Fill background histograms for K-K- invariant mass."}; /// Partitions for the Phi meson (particle 2) - Partition partsPhi = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhi)) && - (aod::femtouniverseparticle::pt > ConfPhiPtLow) && - (aod::femtouniverseparticle::pt < ConfPhiPtHigh) && + Partition partsPhi = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhi)) && + (aod::femtouniverseparticle::pt > confPhiPtLow) && + (aod::femtouniverseparticle::pt < confPhiPtHigh) && (aod::femtouniverseparticle::tempFitVar > confInvMassLowLimitPhi) && (aod::femtouniverseparticle::tempFitVar < confInvMassUpLimitPhi); - Partition partsPhiMCReco = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhi)) && - (aod::femtouniverseparticle::pt > ConfPhiPtLow) && - (aod::femtouniverseparticle::pt < ConfPhiPtHigh) && + Partition partsPhiMCReco = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhi)) && + (aod::femtouniverseparticle::pt > confPhiPtLow) && + (aod::femtouniverseparticle::pt < confPhiPtHigh) && (aod::femtouniverseparticle::tempFitVar > confInvMassLowLimitPhi) && (aod::femtouniverseparticle::tempFitVar < confInvMassUpLimitPhi); /// Partitions for Phi daughters kPhiChild - Partition partsPhiDaugh = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhiChild)); - Partition> partsPhiDaughMC = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kPhiChild)); + Partition partsPhiDaugh = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhiChild)); + Partition> partsPhiDaughMC = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kPhiChild)); // Partition for K+K- minv - Partition partsKaons = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)); - Partition> partsKaonsMC = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)); + Partition partsKaons = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)); + Partition> partsKaonsMC = (aod::femtouniverseparticle::partType == static_cast(aod::femtouniverseparticle::ParticleType::kTrack)); /// Histogramming for particle 1 FemtoUniverseParticleHisto trackHistoPartTrack; @@ -169,21 +190,21 @@ struct FemtoUniversePairTaskTrackPhi { FemtoUniverseEventHisto eventHisto; /// particle part - ConfigurableAxis ConfBinsTempFitVar{"ConfBinsTempFitVar", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; - ConfigurableAxis ConfBinsTempFitVarInvMass{"ConfBinsTempFitVarInvMass", {6000, 0.9, 4.0}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; - ConfigurableAxis ConfBinsTempFitVarpT{"ConfBinsTempFitVarpT", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot"}; - ConfigurableAxis ConfBinsTempFitVarPDG{"ConfBinsTempFitVarPDG", {6000, -2300, 2300}, "Binning of the PDG code in the pT vs. TempFitVar plot"}; - ConfigurableAxis ConfBinsTempFitVarDCA{"ConfBinsTempFitVarDCA", {300, -3.0, 3.0}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis confBinsTempFitVar{"confBinsTempFitVar", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis confBinsTempFitVarInvMass{"confBinsTempFitVarInvMass", {6000, 0.9, 4.0}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis confBinsTempFitVarpT{"confBinsTempFitVarpT", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot"}; + ConfigurableAxis confBinsTempFitVarPDG{"confBinsTempFitVarPDG", {6000, -2300, 2300}, "Binning of the PDG code in the pT vs. TempFitVar plot"}; + ConfigurableAxis confBinsTempFitVarDCA{"confBinsTempFitVarDCA", {300, -3.0, 3.0}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; /// Correlation part - ConfigurableAxis ConfBinsMult{"ConfBinsMult", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f, 64.0f, 68.0f, 72.0f, 76.0f, 80.0f, 84.0f, 88.0f, 92.0f, 96.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; // \todo to be obtained from the hash task - ConfigurableAxis ConfBinsVtx{"ConfBinsVtx", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ConfigurableAxis ConfBins3DmT{"ConfBins3DmT", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ConfigurableAxis ConfBins3Dmult{"ConfBins3Dmult", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + ConfigurableAxis confBinsMult{"confBinsMult", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f, 64.0f, 68.0f, 72.0f, 76.0f, 80.0f, 84.0f, 88.0f, 92.0f, 96.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; // \todo to be obtained from the hash task + ConfigurableAxis confBinsVtx{"confBinsVtx", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis confBins3DmT{"confBins3DmT", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + ConfigurableAxis confBins3Dmult{"confBins3Dmult", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ConfigurableAxis ConfBinskstar{"ConfBinskstar", {1500, 0., 6.}, "binning kstar"}; - ConfigurableAxis ConfBinskT{"ConfBinskT", {150, 0., 9.}, "binning kT"}; - ConfigurableAxis ConfBinsmT{"ConfBinsmT", {225, 0., 7.5}, "binning mT"}; + ConfigurableAxis confBinskstar{"confBinskstar", {1500, 0., 6.}, "binning kstar"}; + ConfigurableAxis confBinskT{"confBinskT", {150, 0., 9.}, "binning kT"}; + ConfigurableAxis confBinsmT{"confBinsmT", {225, 0., 7.5}, "binning mT"}; FemtoUniverseContainer sameEventCont; FemtoUniverseContainer mixedEventCont; @@ -201,158 +222,92 @@ struct FemtoUniversePairTaskTrackPhi { HistogramRegistry registryDCA{"registryDCA", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry registryMCpT{"registryMCpT", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; - ColumnBinningPolicy colBinning{{ConfBinsVtx, ConfBinsMult}, true}; + ColumnBinningPolicy colBinning{{confBinsVtx, confBinsMult}}; HistogramRegistry effCorrRegistry{"EfficiencyCorrection", {}, OutputObjHandlingPolicy::AnalysisObject}; EffCorConfigurableGroup effCorConfGroup; EfficiencyCorrection effCorrection{&effCorConfGroup}; - float weight = 1; + float mWeight = 1.0f; // PID for protons bool isProtonNSigma(float mom, float nsigmaTPCPr, float nsigmaTOFPr) // previous version from: https://github.com/alisw/AliPhysics/blob/master/PWGCF/FEMTOSCOPY/AliFemtoUser/AliFemtoMJTrackCut.cxx { - if (mom < ConfTrackPtPIDLimit) { - if (std::abs(nsigmaTPCPr) < ConfPIDProtonNsigmaTPC) { - return true; - } else { - return false; - } - } else if (mom > ConfTrackPtPIDLimit) { - if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfPIDProtonNsigmaCombined) { - return true; - } else { - return false; - } + if (mom < confTrackPtPIDLimit) { + return std::abs(nsigmaTPCPr) < confPIDProtonNsigmaTPC; } - return false; + return std::hypot(nsigmaTOFPr, nsigmaTPCPr) < confPIDProtonNsigmaCombined; } bool isProtonRejected(float mom, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK) { - if (mom < 0.5) { + if (mom < kMomCutLow) { return true; } - if (mom > 0.5) { - if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfPIDPionNsigmaReject) { - return true; - } else if (std::hypot(nsigmaTOFK, nsigmaTPCK) < ConfPIDKaonNsigmaReject) { - return true; - } else { - return false; - } - } else { - return false; - } + return std::hypot(nsigmaTOFPi, nsigmaTPCPi) < confPIDPionNsigmaReject || std::hypot(nsigmaTOFK, nsigmaTPCK) < confPIDKaonNsigmaReject; } - bool isKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) + bool isKaonNSigma(float mom, bool hasTOF, float nsigmaTPCK, float nsigmaTOFK) { - if (mom < 0.3) { // 0.0-0.3 - if (std::abs(nsigmaTPCK) < 3.0) { - return true; - } else { - return false; + if (confTrackUseRun3PIDforKaons) { + if (mom < kMomCutLow) { + return std::abs(nsigmaTPCK) < kNSigmaStandard; } - } else if (mom < 0.45) { // 0.30 - 0.45 - if (std::abs(nsigmaTPCK) < 2.0) { - return true; - } else { - return false; + if (hasTOF) { // if TOF is available, use combine nsigma + return std::hypot(nsigmaTOFK, nsigmaTPCK) < kNSigmaStandard; } - } else if (mom < 0.55) { // 0.45-0.55 - if (std::abs(nsigmaTPCK) < 1.0) { - return true; - } else { - return false; - } - } else if (mom < 1.5) { // 0.55-1.5 (now we use TPC and TOF) - if ((std::abs(nsigmaTOFK) < 3.0) && (std::abs(nsigmaTPCK) < 3.0)) { - { - return true; - } - } else { - return false; - } - } else if (mom > 1.5) { // 1.5 - - if ((std::abs(nsigmaTOFK) < 2.0) && (std::abs(nsigmaTPCK) < 3.0)) { - return true; - } else { - return false; - } - } else { - return false; + // if TOF is not available, use TPC nsigma + return std::abs(nsigmaTPCK) < kNSigmaStandard; + } + + if (mom < kMomCutKaonLow) { // 0.0-0.3 + return std::abs(nsigmaTPCK) < kNSigmaStandard; + } + if (mom < kMomCutKaonMid) { // 0.30 - 0.45 + return std::abs(nsigmaTPCK) < kNSigmaMedium; } + if (mom < kMomCutKaonHigh) { // 0.45-0.55 + return std::abs(nsigmaTPCK) < kNSigmaStrict; + } + if (mom < kMomCutKaonMax) { // 0.55-1.5 (now we use TPC and TOF) + return std::hypot(nsigmaTOFK, nsigmaTPCK) < kNSigmaStandard; + } + if (mom > kMomCutKaonMax) { // 1.5 - + return (std::abs(nsigmaTOFK) < kNSigmaMedium) && (std::abs(nsigmaTPCK) < kNSigmaStandard); + } + return false; } bool isKaonRejected(float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi) { - if (mom < 0.5) { - if (std::abs(nsigmaTPCPi) < ConfPIDPionNsigmaReject) { - return true; - } else if (std::abs(nsigmaTPCPr) < ConfPIDProtonNsigmaReject) { - return true; - } - } - if (mom > 0.5) { - if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfPIDPionNsigmaReject) { - return true; - } else if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfPIDProtonNsigmaReject) { - return true; - } else { - return false; - } - } else { - return false; + if (mom < kMomCutLow) { + return (std::abs(nsigmaTPCPi) < confPIDPionNsigmaReject) || (std::abs(nsigmaTPCPr) < confPIDProtonNsigmaReject); } + return (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < confPIDPionNsigmaReject) || (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < confPIDProtonNsigmaReject); } bool isPionNSigma(float mom, float nsigmaTPCPi, float nsigmaTOFPi) { - if (true) { - if (mom < 0.5) { - if (std::abs(nsigmaTPCPi) < ConfPIDPionNsigmaTPC) { - return true; - } else { - return false; - } - } else if (mom > 0.5) { - if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < ConfPIDPionNsigmaCombined) { - return true; - } else { - return false; - } - } + if (mom < kMomCutLow) { + return (std::abs(nsigmaTPCPi) < confPIDPionNsigmaTPC); } - return false; + return (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < confPIDPionNsigmaCombined); } bool isPionRejected(float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCK, float nsigmaTOFK) { - if (mom < 0.5) { - if (std::abs(nsigmaTPCK) < ConfPIDKaonNsigmaReject) { - return true; - } else if (std::abs(nsigmaTPCPr) < ConfPIDProtonNsigmaReject) { - return true; - } - } - if (mom > 0.5) { - if (std::hypot(nsigmaTOFK, nsigmaTPCK) < ConfPIDKaonNsigmaReject) { - return true; - } else if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < ConfPIDProtonNsigmaReject) { - return true; - } else { - return false; - } - } else { - return false; + if (mom < kMomCutLow) { + return (std::abs(nsigmaTPCK) < confPIDKaonNsigmaReject) || (std::abs(nsigmaTPCPr) < confPIDProtonNsigmaReject); } + return (std::hypot(nsigmaTOFK, nsigmaTPCK) < confPIDKaonNsigmaReject) || (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < confPIDProtonNsigmaReject); } bool isParticleNSigmaAccepted(float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK) { - switch (ConfTrackPDGCode) { + bool hasTOF = std::isfinite(nsigmaTOFPr) || std::isfinite(nsigmaTOFPi) || std::isfinite(nsigmaTOFK); + + switch (confTrackPDGCode) { case 2212: // Proton case -2212: // anty Proton return isProtonNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); @@ -363,7 +318,7 @@ struct FemtoUniversePairTaskTrackPhi { break; case 321: // Kaon+ case -321: // Kaon- - return isKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); + return isKaonNSigma(mom, hasTOF, nsigmaTPCK, nsigmaTOFK); break; default: return false; @@ -372,7 +327,7 @@ struct FemtoUniversePairTaskTrackPhi { bool isParticleNSigmaRejected(float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK) { - switch (ConfTrackPDGCode) { + switch (confTrackPDGCode) { case 2212: // Proton case -2212: // anty Proton return isProtonRejected(mom, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCK, nsigmaTOFK); @@ -398,8 +353,8 @@ struct FemtoUniversePairTaskTrackPhi { void init(InitContext&) { - if (ConfIsMC) { - hTrackDCA.init(®istryDCA, ConfBinsTempFitVarpT, ConfBinsTempFitVarDCA, true, ConfTrackPDGCode, true); + if (confIsMC) { + hTrackDCA.init(®istryDCA, confBinsTempFitVarpT, confBinsTempFitVarDCA, true, confTrackPDGCode, true); registryMCpT.add("MCReco/C_phi_pT", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); registryMCpT.add("MCReco/NC_phi_pT", "; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{100, 0, 10}}); @@ -411,9 +366,9 @@ struct FemtoUniversePairTaskTrackPhi { effCorrection.init( &effCorrRegistry, { - static_cast(ConfBinsTempFitVarpT), - {ConfBinsEta, -1, 1}, - ConfBinsMult, + static_cast(confBinsTempFitVarpT), + {confBinsEta, -1, 1}, + confBinsMult, }); eventHisto.init(&qaRegistry); @@ -433,6 +388,7 @@ struct FemtoUniversePairTaskTrackPhi { registryPhiMinvBackground.add("InvariantMassKpKp", "; invariant mass K+K+; Counts", kTH1F, {{6000, 0.9, 4.0}}); registryPhiMinvBackground.add("InvariantMassKmKm", "; invariant mass K-K-; Counts", kTH1F, {{6000, 0.9, 4.0}}); + registryPhiMinvBackground.add("InvariantMassKpKm", "; invariant mass K+K-; Counts", kTH1F, {{6000, 0.9, 4.0}}); qaRegistry.add("Hadron_pos/nSigmaTPCPr", "; #it{p} (GeV/#it{c}); n#sigma_{TPCPr}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); qaRegistry.add("Hadron_pos/nSigmaTOFPr", "; #it{p} (GeV/#it{c}); n#sigma_{TOFPr}", kTH2F, {{100, 0, 10}, {200, -4.975, 5.025}}); @@ -455,12 +411,12 @@ struct FemtoUniversePairTaskTrackPhi { registryMCtruth.add("MCtruthKp", "MC truth K+;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthKpPt", "MC truth kaons positive;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // K- - registryMCtruth.add("MCtruthKm", "MC truth protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); + registryMCtruth.add("MCtruthKm", "MC truth kaons negative;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthKmPt", "MC truth kaons negative;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // p registryMCtruth.add("MCtruthPpos", "MC truth protons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthPposPt", "MC truth protons;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); - // pbar + // p- registryMCtruth.add("MCtruthPneg", "MC truth antiprotons;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCtruth.add("MCtruthPnegPt", "MC truth antiproton;#it{p}_{T} (GeV/c)", {HistType::kTH1F, {{500, 0, 5}}}); // phi @@ -473,37 +429,37 @@ struct FemtoUniversePairTaskTrackPhi { // p registryMCreco.add("MCrecoPpos", "MC reco proton;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("MCrecoPposPt", "MC reco proton; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{500, 0, 5}}); - // pbar + // p- registryMCreco.add("MCrecoPneg", "MC reco antiproton;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("MCrecoPnegPt", "MC reco antiproton; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{500, 0, 5}}); // phi registryMCreco.add("MCrecoPhi", "MC reco Phi;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); registryMCreco.add("MCrecoPhiPt", "MC reco Phi; #it{p_T} (GeV/#it{c}); Counts", kTH1F, {{500, 0, 5}}); - trackHistoPartPhi.init(&qaRegistry, ConfBinsTempFitVarpT, ConfBinsTempFitVarInvMass, ConfIsMC, 333); - trackHistoPartTrack.init(&qaRegistry, ConfBinsTempFitVarpT, ConfBinsTempFitVar, ConfIsMC, ConfTrackPDGCode); + trackHistoPartPhi.init(&qaRegistry, confBinsTempFitVarpT, confBinsTempFitVarInvMass, confIsMC, 333); + trackHistoPartTrack.init(&qaRegistry, confBinsTempFitVarpT, confBinsTempFitVar, confIsMC, confTrackPDGCode); mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); mixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); - sameEventCont.init(&resultRegistry, ConfBinskstar, ConfBinsMult, ConfBinskT, ConfBinsmT, ConfBins3Dmult, ConfBins3DmT, ConfBinsEta, ConfBinsPhi, ConfIsMC, ConfUse3D); - mixedEventCont.init(&resultRegistry, ConfBinskstar, ConfBinsMult, ConfBinskT, ConfBinsmT, ConfBins3Dmult, ConfBins3DmT, ConfBinsEta, ConfBinsPhi, ConfIsMC, ConfUse3D); + sameEventCont.init(&resultRegistry, confBinskstar, confBinsMult, confBinskT, confBinsmT, confBins3Dmult, confBins3DmT, confBinsEta, confBinsPhi, confIsMC, confUse3D); + mixedEventCont.init(&resultRegistry, confBinskstar, confBinsMult, confBinskT, confBinsmT, confBins3Dmult, confBins3DmT, confBinsEta, confBinsPhi, confIsMC, confUse3D); - sameEventCont.setPDGCodes(333, ConfTrackPDGCode); - mixedEventCont.setPDGCodes(333, ConfTrackPDGCode); + sameEventCont.setPDGCodes(333, confTrackPDGCode); + mixedEventCont.setPDGCodes(333, confTrackPDGCode); pairCleaner.init(&qaRegistry); - if (ConfCPRIsEnabled) { - pairCloseRejection.init(&resultRegistry, &qaRegistry, confDeltaEtaAxis, confDeltaPhiStarAxis, ConfCPRdeltaPhiCutMin, ConfCPRdeltaPhiCutMax, ConfCPRdeltaEtaCutMin, ConfCPRdeltaEtaCutMax, ConfCPRChosenRadii, ConfCPRPlotPerRadii, ConfCPRInvMassCutMin, ConfCPRInvMassCutMax); + if (confCPRIsEnabled) { + pairCloseRejection.init(&resultRegistry, &qaRegistry, confDeltaEtaAxis, confDeltaPhiStarAxis, confCPRdeltaPhiCutMin, confCPRdeltaPhiCutMax, confCPRdeltaEtaCutMin, confCPRdeltaEtaCutMax, confCPRChosenRadii, confCPRPlotPerRadii, confCPRInvMassCutMin, confCPRInvMassCutMax); } } template - void doSameEvent(PartitionType groupPartsTrack, PartitionType groupPartsPhi, PartType parts, float magFieldTesla, int multCol, [[maybe_unused]] MCParticles mcParts = nullptr) + void doSameEvent(const PartitionType& groupPartsTrack, const PartitionType& groupPartsPhi, const PartType& parts, float magFieldTesla, int multCol, [[maybe_unused]] const MCParticles& mcParts = nullptr) { for (auto const& phicandidate : groupPartsPhi) { // TODO: add phi meson minv cut here - const auto& posChild = parts.iteratorAt(phicandidate.index() - 2); + const auto& posChild = parts.iteratorAt(phicandidate.index() - kPhiDaughterOffsetPos); float tpcNSigmaKp = trackCuts.getNsigmaTPC(posChild, o2::track::PID::Kaon); float tofNSigmaKp = trackCuts.getNsigmaTOF(posChild, o2::track::PID::Kaon); qaRegistry.fill(HIST("PhiDaugh_pos/nSigmaTPC"), posChild.p(), tpcNSigmaKp); @@ -513,7 +469,7 @@ struct FemtoUniversePairTaskTrackPhi { qaRegistry.fill(HIST("PhiDaugh_pos/eta"), posChild.eta()); qaRegistry.fill(HIST("PhiDaugh_pos/phi"), posChild.phi()); - const auto& negChild = parts.iteratorAt(phicandidate.index() - 1); + const auto& negChild = parts.iteratorAt(phicandidate.index() - kPhiDaughterOffsetNeg); float tpcNSigmaKm = trackCuts.getNsigmaTPC(negChild, o2::track::PID::Kaon); float tofNSigmaKm = trackCuts.getNsigmaTOF(negChild, o2::track::PID::Kaon); qaRegistry.fill(HIST("PhiDaugh_neg/nSigmaTPC"), negChild.p(), tpcNSigmaKm); @@ -526,7 +482,7 @@ struct FemtoUniversePairTaskTrackPhi { trackHistoPartPhi.fillQA(phicandidate); if constexpr (isMC) { // reco - effCorrection.fillRecoHist(phicandidate, 333); + effCorrection.fillRecoHist(phicandidate, o2::constants::physics::Pdg::kPhi); } } @@ -538,11 +494,11 @@ struct FemtoUniversePairTaskTrackPhi { float tpcNSigmaPr = trackCuts.getNsigmaTPC(track, o2::track::PID::Proton); float tofNSigmaPr = trackCuts.getNsigmaTOF(track, o2::track::PID::Proton); - if (ConfTrackIsIdentified) { + if (confTrackIsIdentified) { if (!isParticleNSigmaAccepted(track.p(), tpcNSigmaPr, tofNSigmaPr, tpcNSigmaPi, tofNSigmaPi, tpcNSigmaKa, tofNSigmaKa)) { continue; } - if (ConfTrackIsRejected) { + if (confTrackIsRejected) { if (isParticleNSigmaRejected(track.p(), tpcNSigmaPr, tofNSigmaPr, tpcNSigmaPi, tofNSigmaPi, tpcNSigmaKa, tofNSigmaKa)) { continue; } @@ -566,26 +522,26 @@ struct FemtoUniversePairTaskTrackPhi { trackHistoPartTrack.fillQA(track); if constexpr (isMC) { - effCorrection.fillRecoHist(track, ConfTrackPDGCode); + effCorrection.fillRecoHist(track, confTrackPDGCode); } } /// Now build the combinations for (auto const& [track, phicandidate] : combinations(CombinationsFullIndexPolicy(groupPartsTrack, groupPartsPhi))) { - if (ConfTrackIsIdentified) { + if (confTrackIsIdentified) { if (!isParticleNSigmaAccepted(track.p(), trackCuts.getNsigmaTPC(track, o2::track::PID::Proton), trackCuts.getNsigmaTOF(track, o2::track::PID::Proton), trackCuts.getNsigmaTPC(track, o2::track::PID::Pion), trackCuts.getNsigmaTOF(track, o2::track::PID::Pion), trackCuts.getNsigmaTPC(track, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(track, o2::track::PID::Kaon))) { continue; } } - if (ConfTrackIsRejected) { + if (confTrackIsRejected) { if (isParticleNSigmaRejected(track.p(), trackCuts.getNsigmaTPC(track, o2::track::PID::Proton), trackCuts.getNsigmaTOF(track, o2::track::PID::Proton), trackCuts.getNsigmaTPC(track, o2::track::PID::Pion), trackCuts.getNsigmaTOF(track, o2::track::PID::Pion), trackCuts.getNsigmaTPC(track, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(track, o2::track::PID::Kaon))) { continue; } } // Close Pair Rejection - if (ConfCPRIsEnabled) { + if (confCPRIsEnabled) { if (pairCloseRejection.isClosePair(track, phicandidate, parts, magFieldTesla, femto_universe_container::EventType::same)) { continue; } @@ -595,59 +551,69 @@ struct FemtoUniversePairTaskTrackPhi { if (!pairCleaner.isCleanPair(track, phicandidate, parts)) { continue; } - weight = effCorrection.getWeight(ParticleNo::ONE, phicandidate) * effCorrection.getWeight(ParticleNo::TWO, track); - sameEventCont.setPair(track, phicandidate, multCol, ConfUse3D, weight); + mWeight = effCorrection.getWeight(ParticleNo::ONE, phicandidate) * effCorrection.getWeight(ParticleNo::TWO, track); + sameEventCont.setPair(track, phicandidate, multCol, confUse3D, mWeight); } - // // Used for better fitting of invariant mass background. - - // TLorentzVector part1Vec; - // TLorentzVector part2Vec; - - // float mMassOne = o2::constants::physics::MassKPlus; - // float mMassTwo = o2::constants::physics::MassKMinus; - - // for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsKaons, groupPartsKaons))) { - // if ((kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { - // part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); - // part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); - // TLorentzVector sumVec(part1Vec); - // sumVec += part2Vec; - // registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); - // } - // if ((kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { - // part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); - // part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); - - // TLorentzVector sumVec(part1Vec); - // sumVec += part2Vec; - // registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); - // } - // } + if (confFillMinvBackground) { + // Used for better fitting of invariant mass background. + + TLorentzVector part1Vec; + TLorentzVector part2Vec; + + float mMassOne = o2::constants::physics::MassKPlus; + float mMassTwo = o2::constants::physics::MassKMinus; + + for (auto const& [kaon1, kaon2] : combinations(CombinationsStrictlyUpperIndexPolicy(partsKaons, partsKaons))) { + if ((confFillMinvBackgroundKpKp) && (kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == 1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassOne); + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKp"), sumVec.M()); + } + if ((confFillMinvBackgroundKmKm) && (kaon1.mAntiLambda() == -1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassTwo); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKmKm"), sumVec.M()); + } + if ((confFillMinvBackgroundKpKm) && (kaon1.mAntiLambda() == 1) && (kaon2.mAntiLambda() == -1)) { + part1Vec.SetPtEtaPhiM(kaon1.pt(), kaon1.eta(), kaon1.phi(), mMassOne); + part2Vec.SetPtEtaPhiM(kaon2.pt(), kaon2.eta(), kaon2.phi(), mMassTwo); + + TLorentzVector sumVec(part1Vec); + sumVec += part2Vec; + registryPhiMinvBackground.fill(HIST("InvariantMassKpKm"), sumVec.M()); + } + } + } } template - void doMixedEvent(PartitionType groupPartsTrack, PartitionType groupPartsPhi, PartType parts, float magFieldTesla, int multCol, [[maybe_unused]] MCParticles mcParts = nullptr) + void doMixedEvent(const PartitionType& groupPartsTrack, const PartitionType& groupPartsPhi, const PartType& parts, float magFieldTesla, int multCol, [[maybe_unused]] const MCParticles& mcParts = nullptr) { for (auto const& [track, phicandidate] : combinations(CombinationsFullIndexPolicy(groupPartsTrack, groupPartsPhi))) { - if (ConfTrackIsIdentified) { + if (confTrackIsIdentified) { if (!isParticleNSigmaAccepted(track.p(), trackCuts.getNsigmaTPC(track, o2::track::PID::Proton), trackCuts.getNsigmaTOF(track, o2::track::PID::Proton), trackCuts.getNsigmaTPC(track, o2::track::PID::Pion), trackCuts.getNsigmaTOF(track, o2::track::PID::Pion), trackCuts.getNsigmaTPC(track, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(track, o2::track::PID::Kaon))) { continue; } - if (ConfTrackIsRejected) { + if (confTrackIsRejected) { if (isParticleNSigmaRejected(track.p(), trackCuts.getNsigmaTPC(track, o2::track::PID::Proton), trackCuts.getNsigmaTOF(track, o2::track::PID::Proton), trackCuts.getNsigmaTPC(track, o2::track::PID::Pion), trackCuts.getNsigmaTOF(track, o2::track::PID::Pion), trackCuts.getNsigmaTPC(track, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(track, o2::track::PID::Kaon))) { continue; } } } - if (ConfCPRIsEnabled) { + if (confCPRIsEnabled) { if (pairCloseRejection.isClosePair(track, phicandidate, parts, magFieldTesla, femto_universe_container::EventType::mixed)) { continue; } } - weight = effCorrection.getWeight(ParticleNo::ONE, phicandidate) * effCorrection.getWeight(ParticleNo::TWO, track); - mixedEventCont.setPair(track, phicandidate, multCol, ConfUse3D, weight); + mWeight = effCorrection.getWeight(ParticleNo::ONE, phicandidate) * effCorrection.getWeight(ParticleNo::TWO, track); + mixedEventCont.setPair(track, phicandidate, multCol, confUse3D, mWeight); } } @@ -718,8 +684,9 @@ struct FemtoUniversePairTaskTrackPhi { void processMCTruth(aod::FDParticles const& parts) { for (auto const& part : parts) { - if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) + if (part.partType() != static_cast(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { continue; + } int pdgCode = static_cast(part.pidCut()); const auto& pdgParticle = pdgMC->GetParticle(pdgCode); @@ -727,25 +694,26 @@ struct FemtoUniversePairTaskTrackPhi { continue; } - if (pdgCode == ConfTrackPDGCode) { + if (pdgCode == confTrackPDGCode) { effCorrection.fillTruthHist(part); } // charge + if (pdgParticle->Charge() > 0.0) { registryMCtruth.fill(HIST("MCtruthAllPositivePt"), part.pt()); - if (pdgCode == 2212) { + if (pdgCode == kProton) { registryMCtruth.fill(HIST("MCtruthPpos"), part.pt(), part.eta()); registryMCtruth.fill(HIST("MCtruthPposPt"), part.pt()); continue; - } else if (pdgCode == 321) { + } + if (pdgCode == kKPlus) { registryMCtruth.fill(HIST("MCtruthKp"), part.pt(), part.eta()); registryMCtruth.fill(HIST("MCtruthKpPt"), part.pt()); continue; } } // charge 0 - if (pdgCode == 333) { + if (pdgCode == o2::constants::physics::Pdg::kPhi) { registryMCtruth.fill(HIST("MCtruthPhi"), part.pt(), part.eta()); registryMCtruth.fill(HIST("MCtruthPhiPt"), part.pt()); effCorrection.fillTruthHist(part); @@ -756,11 +724,12 @@ struct FemtoUniversePairTaskTrackPhi { if (pdgParticle->Charge() < 0.0) { registryMCtruth.fill(HIST("MCtruthAllNegativePt"), part.pt()); - if (pdgCode == -321) { + if (pdgCode == kKMinus) { registryMCtruth.fill(HIST("MCtruthKm"), part.pt(), part.eta()); registryMCtruth.fill(HIST("MCtruthKmPt"), part.pt()); continue; - } else if (pdgCode == -2212) { + } + if (pdgCode == kProtonBar) { registryMCtruth.fill(HIST("MCtruthPneg"), part.pt(), part.eta()); registryMCtruth.fill(HIST("MCtruthPnegPt"), part.pt()); continue; @@ -774,36 +743,38 @@ struct FemtoUniversePairTaskTrackPhi { { for (auto const& part : parts) { auto mcPartId = part.fdMCParticleId(); - if (mcPartId == -1) + if (mcPartId == kInvalidMCPartId) { continue; // no MC particle + } const auto& mcpart = mcparts.iteratorAt(mcPartId); - if (mcpart.pdgMCTruth() == ConfTrackPDGCode && (part.pt() > ConfTrackPtLow) && (part.pt() < ConfTrackPtHigh) && isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if (mcpart.pdgMCTruth() == confTrackPDGCode && (part.pt() > confTrackPtLow) && (part.pt() < confTrackPtHigh) && isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { registryMCpT.fill(HIST("MCReco/NC_p_pT"), part.pt()); float weightTrack = effCorrection.getWeight(ParticleNo::TWO, part); registryMCpT.fill(HIST("MCReco/C_p_pT"), part.pt(), weightTrack); } - if ((mcpart.pdgMCTruth() == 333) && (part.partType() == aod::femtouniverseparticle::ParticleType::kPhi) && (part.pt() > ConfPhiPtLow) && (part.pt() < ConfPhiPtHigh)) { + if ((mcpart.pdgMCTruth() == o2::constants::physics::Pdg::kPhi) && (part.partType() == aod::femtouniverseparticle::ParticleType::kPhi) && (part.pt() > confPhiPtLow) && (part.pt() < confPhiPtHigh)) { registryMCpT.fill(HIST("MCReco/NC_phi_pT"), part.pt()); float weightPhi = effCorrection.getWeight(ParticleNo::ONE, part); registryMCpT.fill(HIST("MCReco/C_phi_pT"), part.pt(), weightPhi); } - if (isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) + if (isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { hTrackDCA.fillQA(part); - if ((part.partType() == aod::femtouniverseparticle::ParticleType::kPhi) && (mcpart.pdgMCTruth() == 333) && (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary)) { + } + if ((part.partType() == aod::femtouniverseparticle::ParticleType::kPhi) && (mcpart.pdgMCTruth() == o2::constants::physics::Pdg::kPhi) && (mcpart.partOriginMCTruth() == aod::femtouniverse_mc_particle::ParticleOriginMCTruth::kPrimary)) { registryMCreco.fill(HIST("MCrecoPhi"), mcpart.pt(), mcpart.eta()); // phi registryMCreco.fill(HIST("MCrecoPhiPt"), mcpart.pt()); } else if (part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) { if (part.sign() > 0) { registryMCreco.fill(HIST("MCrecoAllPositivePt"), mcpart.pt()); - if (mcpart.pdgMCTruth() == 2212 && isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if (mcpart.pdgMCTruth() == kProton && isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { registryMCreco.fill(HIST("MCrecoPpos"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("MCrecoPposPt"), mcpart.pt()); } } else if (part.sign() < 0) { registryMCreco.fill(HIST("MCrecoAllNegativePt"), mcpart.pt()); - if (mcpart.pdgMCTruth() == -2212 && isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if (mcpart.pdgMCTruth() == kProtonBar && isParticleNSigmaAccepted(part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { registryMCreco.fill(HIST("MCrecoPneg"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("MCrecoPnegPt"), mcpart.pt()); } diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx index 1678d2747b5..4b410fb8fca 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack.cxx @@ -121,7 +121,7 @@ struct FemtoUniversePairTaskTrackTrack { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -185,7 +185,7 @@ struct FemtoUniversePairTaskTrackTrack { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -202,7 +202,7 @@ struct FemtoUniversePairTaskTrackTrack { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol) + void doSameEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol) { /// Histogramming same event @@ -325,7 +325,7 @@ struct FemtoUniversePairTaskTrackTrack { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol) { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx index 3474825dfc9..def18f1deb1 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx @@ -176,8 +176,8 @@ struct femtoUniversePairTaskTrackTrack3DMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {300, -1.5, 1.5}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -450,7 +450,7 @@ struct femtoUniversePairTaskTrackTrack3DMultKtExtended { } template - void fillCollision(CollisionType col, bool IsCent) + void fillCollision(const CollisionType& col, bool IsCent) { if (IsCent) { MixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinningCent.getBin({col.posZ(), col.multV0M()})); @@ -471,7 +471,7 @@ struct femtoUniversePairTaskTrackTrack3DMultKtExtended { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, int ContType, bool fillQA, [[maybe_unused]] MCParticles mcParts = nullptr) + void doSameEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol, int ContType, bool fillQA, [[maybe_unused]] const MCParticles& mcParts = nullptr) { /// Histogramming same event @@ -718,7 +718,7 @@ struct femtoUniversePairTaskTrackTrack3DMultKtExtended { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, int ContType) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol, int ContType) { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx index 971ac763115..fe90355be5b 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackExtended.cxx @@ -166,7 +166,7 @@ struct FemtoUniversePairTaskTrackTrackExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -373,7 +373,7 @@ struct FemtoUniversePairTaskTrackTrackExtended { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -381,7 +381,7 @@ struct FemtoUniversePairTaskTrackTrackExtended { template requires IsOneOrTwo - auto doMCTruth(auto parts, int partPDG, int partCharge) -> void + auto doMCTruth(const auto& parts, int partPDG, int partCharge) -> void { for (const auto& particle : parts) { auto pdgCode = static_cast(particle.pidCut()); @@ -413,7 +413,7 @@ struct FemtoUniversePairTaskTrackTrackExtended { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol) + void doSameEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol) { // variables for particle swapping bool swpart = fNeventsProcessed % 2; @@ -648,7 +648,7 @@ struct FemtoUniversePairTaskTrackTrackExtended { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol) { // variables for particle swapping diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx index 145393dc438..b0802f6521b 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMC.cxx @@ -157,7 +157,7 @@ struct femtoUniversePairTaskTrackTrackMC { ConfigurableAxis ConfmTBins3D{"ConfmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis ConfmultBins3D{"ConfmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}, true}; + ColumnBinningPolicy colBinning{{ConfVtxBins, ConfMultBins}}; ConfigurableAxis ConfkstarBins{"ConfkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis ConfkTBins{"ConfkTBins", {150, 0., 9.}, "binning kT"}; @@ -411,7 +411,7 @@ struct femtoUniversePairTaskTrackTrackMC { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { MixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -428,7 +428,7 @@ struct femtoUniversePairTaskTrackTrackMC { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, int ContType, bool fillQA) + void doSameEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol, int ContType, bool fillQA) { /// Histogramming same event @@ -596,7 +596,7 @@ struct femtoUniversePairTaskTrackTrackMC { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, int ContType) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol, int ContType) { for (auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx index 30666e44c8b..297cc03cfbd 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMcTruth.cxx @@ -106,7 +106,7 @@ struct FemtoUniversePairTaskTrackTrackMcTruth { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confMultBins3D{"confMultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -148,7 +148,7 @@ struct FemtoUniversePairTaskTrackTrackMcTruth { } template - void fillCollision(CollisionType col) + void fillCollision(const CollisionType& col) { mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multNtr()})); eventHisto.fillQA(col); @@ -165,7 +165,7 @@ struct FemtoUniversePairTaskTrackTrackMcTruth { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float /*magFieldTesla*/, int multCol) + void doSameEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float /*magFieldTesla*/, int multCol) { bool swpart = fNeventsProcessed % 2; fNeventsProcessed++; @@ -257,7 +257,7 @@ struct FemtoUniversePairTaskTrackTrackMcTruth { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType /*parts*/, float /*magFieldTesla*/, int multCol) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& /*parts*/, float /*magFieldTesla*/, int multCol) { bool swpart = fNeventsProcessed % 2; fNeventsProcessed++; diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx index 11d4de68fc7..e1f2f92f773 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackMultKtExtended.cxx @@ -28,12 +28,13 @@ #include "PWGCF/FemtoUniverse/Core/FemtoUniverseTrackSelection.h" #include "PWGCF/FemtoUniverse/DataModel/FemtoDerived.h" +#include "Common/DataModel/TrackSelectionTables.h" + #include #include #include #include #include -#include #include #include #include @@ -46,6 +47,7 @@ #include #include +#include #include #include @@ -60,44 +62,62 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; -namespace -{ -static constexpr int Npart = 2; -static constexpr int Ncuts = 5; -static const std::vector partNames{"PartOne", "PartTwo"}; -static const std::vector cutNames{"MaxPt", "PIDthr", "nSigmaTPC", "nSigmaTPCTOF", "MaxP"}; -static const float cutsTable[Npart][Ncuts]{{4.05f, 1.f, 3.f, 3.f, 100.f}, {4.05f, 1.f, 3.f, 3.f, 100.f}}; -} // namespace - struct FemtoUniversePairTaskTrackTrackMultKtExtended { - Service pdg; + Service pdg{}; + double randCompareValue = 0.5; + TRandom2* randgen = new TRandom2(); /// Particle selection part /// Table for both particles struct : o2::framework::ConfigurableGroup { Configurable isKaonNsigma{"isKaonNsigma", false, "Enable a strict cut selection for K+ and K-"}; + Configurable isNsigmaRectangular{"isNsigmaRectangular", false, "Apply rectangular TPC and TOF nSigma cut, instead of a combined one (TPC confIsAddITSNsigma{"confIsAddITSNsigma", false, "Apply cuts on ITS nSigma"}; + + Configurable confNsigmaTPCITSCombined{"confNsigmaTPCITSCombined", 3.0f, "TPC and ITS nSigma (combined) for momentum < confTOFpMin"}; Configurable confNsigmaCombined{"confNsigmaCombined", 3.0f, "TPC and TOF Pion Sigma (combined) for momentum > confTOFpMin"}; Configurable confNsigmaTPC{"confNsigmaTPC", 3.0f, "TPC Pion Sigma for momentum < confTOFpMin"}; + Configurable confNsigmaKaonRejection{"confNsigmaKaonRejection", 0, "In proton selection reject tracks with combined Nsigma confNsigmaPionRejection{"confNsigmaPionRejection", 0, "In proton selection reject tracks with combined Nsigma confTPCNsigmaKaonRejection{"confTPCNsigmaKaonRejection", 0, "In proton selection reject tracks with TPC Nsigma confTPCNsigmaPionRejection{"confTPCNsigmaPionRejection", 0, "In proton selection reject tracks with TPC Nsigma confTOFNsigmaKaonRejection{"confTOFNsigmaKaonRejection", 0, "In proton selection reject tracks with TOF Nsigma confTOFNsigmaPionRejection{"confTOFNsigmaPionRejection", 0, "In proton selection reject tracks with TOF Nsigma confTOFpMinRej{"confTOFpMinRej", 0.5f, "Min. momentum for which TOF is required for PID for rejection."}; + Configurable isNsigmaRejectionCombined{"isNsigmaRejectionCombined", true, "True: reject based on combined nSigma, false: reject based on TPC or TOF nSigma (confTOFpMinRej)."}; + Configurable confTOFpMin{"confTOFpMin", 0.5f, "Min. momentum for which TOF is required for PID."}; Configurable confEtaMax{"confEtaMax", 0.8f, "Higher limit for |Eta| (the same for both particles)"}; - Configurable> confCutTable{"confCutTable", {cutsTable[0], Npart, Ncuts, partNames, cutNames}, "Particle selections"}; - Configurable confNspecies{"confNspecies", 2, "Number of particle spieces with PID info"}; Configurable confIsMC{"confIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; - Configurable> confTrkPIDnSigmaMax{"confTrkPIDnSigmaMax", std::vector{4.f, 3.f, 2.f}, "This configurable needs to be the same as the one used in the producer task"}; Configurable confUse3D{"confUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; + Configurable confOnlyPrimaryMCPair{"confOnlyPrimaryMCPair", false, "Fill MC pair histograms only with primary particles"}; + + Configurable confTrkTPCnclsMin{"confTrkTPCnclsMin", 0, "Min. TPC clusters (if stricter than in the Producer)"}; + Configurable confTrkTPCcRowsMin{"confTrkTPCcRowsMin", 0, "Min. TPC crossed rows (if stricter than in the Producer)"}; + Configurable confDcaXYCustom1FilterCut{"confDcaXYCustom1FilterCut", 10, "Value for [1] custom DCAxy cut -> |DCAxy| < [1] + [2]/pT (if stricter than in the Producer)"}; + Configurable confDcaXYCustom2FilterCut{"confDcaXYCustom2FilterCut", 0, "Value for [2] custom DCAxy cut -> |DCAxy| < [1] + [2]/pT (if stricter than in the Producer)"}; } twotracksconfigs; using FemtoFullParticles = soa::Join; + using FemtoFullParticlesITS = soa::Join; + using FemtoFullSigmaParticles = soa::Join; + using FemtoFullSigmaParticlesITS = soa::Join; // Filters for selecting particles (both p1 and p2) Filter trackAdditionalfilter = (nabs(aod::femtouniverseparticle::eta) < twotracksconfigs.confEtaMax); // example filtering on configurable using FilteredFemtoFullParticles = soa::Filtered; + using FilteredFemtoFullSigmaParticles = soa::Filtered; + using FilteredFemtoFullParticlesITS = soa::Filtered; + using FilteredFemtoFullSigmaParticlesITS = soa::Filtered; // using FilteredFemtoFullParticles = FemtoFullParticles; //if no filtering is applied uncomment this option SliceCache cache; Preslice perCol = aod::femtouniverseparticle::fdCollisionId; + Preslice perColSigma = aod::femtouniverseparticle::fdCollisionId; + Preslice perColITS = aod::femtouniverseparticle::fdCollisionId; + Preslice perColSigmaITS = aod::femtouniverseparticle::fdCollisionId; using FemtoTruthParticles = soa::Filtered>; Preslice perColMCTruth = aod::femtouniverseparticle::fdCollisionId; @@ -105,23 +125,28 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { using FemtoRecoParticles = soa::Filtered>; Preslice perColMC = aod::femtouniverseparticle::fdCollisionId; + using FemtoRecoParticlesITS = soa::Filtered>; + Preslice perColMCITS = aod::femtouniverseparticle::fdCollisionId; + /// Particle 1 struct : o2::framework::ConfigurableGroup { Configurable confPDGCodePartOne{"confPDGCodePartOne", 211, "Particle 1 -- PDG code"}; - // Configurable confCutPartOne{"confCutPartOne", 5542474, "Particle 1 -- Selection bit from cutCulator"}; - Configurable confPIDPartOne{"confPIDPartOne", 2, "Particle 1 -- Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector>int>> Configurable confpLowPart1{"confpLowPart1", 0.14, "Lower limit for Pt for the first particle"}; Configurable confPtHighPart1{"confPtHighPart1", 1.5, "Higher limit for Pt for the first particle"}; Configurable confChargePart1{"confChargePart1", 1, "Particle 1 sign"}; } trackonefilter; /// Partition for particle 1 - Partition partsOne = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt < trackonefilter.confPtHighPart1 && aod::femtouniverseparticle::pt > trackonefilter.confpLowPart1; + Partition partsOne = (nabs(aod::track::dcaXY) < (twotracksconfigs.confDcaXYCustom1FilterCut + twotracksconfigs.confDcaXYCustom2FilterCut / aod::femtouniverseparticle::pt)) && (aod::femtouniverseparticle::tpcNClsFound) > as(twotracksconfigs.confTrkTPCnclsMin) && (aod::femtouniverseparticle::tpcNClsCrossedRows) > as(twotracksconfigs.confTrkTPCcRowsMin) && (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt trackonefilter.confpLowPart1; + Partition partsOneITS = (nabs(aod::track::dcaXY) < (twotracksconfigs.confDcaXYCustom1FilterCut + twotracksconfigs.confDcaXYCustom2FilterCut / aod::femtouniverseparticle::pt)) && (aod::femtouniverseparticle::tpcNClsFound) > as(twotracksconfigs.confTrkTPCnclsMin) && (aod::femtouniverseparticle::tpcNClsCrossedRows) > as(twotracksconfigs.confTrkTPCcRowsMin) && (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt trackonefilter.confpLowPart1; + + Partition partsOneSigma = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt < trackonefilter.confPtHighPart1 && aod::femtouniverseparticle::pt > trackonefilter.confpLowPart1; + Partition partsOneSigmaITS = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt < trackonefilter.confPtHighPart1 && aod::femtouniverseparticle::pt > trackonefilter.confpLowPart1; Partition partsOneMC = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt < trackonefilter.confPtHighPart1 && aod::femtouniverseparticle::pt > trackonefilter.confpLowPart1; + Partition partsOneMCITS = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt < trackonefilter.confPtHighPart1 && aod::femtouniverseparticle::pt > trackonefilter.confpLowPart1; Partition partsOneMCTruth = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack) && aod::femtouniverseparticle::pt < trackonefilter.confPtHighPart1 && aod::femtouniverseparticle::pt > trackonefilter.confpLowPart1; - ; /// Histogramming for particle 1 FemtoUniverseParticleHisto trackHistoPartOne; @@ -129,17 +154,20 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { /// Particle 2 struct : o2::framework::ConfigurableGroup { Configurable confPDGCodePartTwo{"confPDGCodePartTwo", 211, "Particle 2 -- PDG code"}; - // Configurable confCutPartTwo{"confCutPartTwo", 5542474, "Particle 2 -- Selection bit"}; - Configurable confPIDPartTwo{"confPIDPartTwo", 2, "Particle 2 -- Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector> Configurable confpLowPart2{"confpLowPart2", 0.14, "Lower limit for Pt for the second particle"}; Configurable confPtHighPart2{"confPtHighPart2", 1.5, "Higher limit for Pt for the second particle"}; Configurable confChargePart2{"confChargePart2", -1, "Particle 2 sign"}; } tracktwofilter; /// Partition for particle 2 - Partition partsTwo = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2)) && aod::femtouniverseparticle::pt < tracktwofilter.confPtHighPart2 && aod::femtouniverseparticle::pt > tracktwofilter.confpLowPart2; + Partition partsTwo = (nabs(aod::track::dcaXY) < (twotracksconfigs.confDcaXYCustom1FilterCut + twotracksconfigs.confDcaXYCustom2FilterCut / aod::femtouniverseparticle::pt)) && (aod::femtouniverseparticle::tpcNClsFound > as(twotracksconfigs.confTrkTPCnclsMin)) && aod::femtouniverseparticle::tpcNClsCrossedRows > as(twotracksconfigs.confTrkTPCcRowsMin) && (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2) && aod::femtouniverseparticle::pt tracktwofilter.confpLowPart2; + Partition partsTwoITS = (nabs(aod::track::dcaXY) < (twotracksconfigs.confDcaXYCustom1FilterCut + twotracksconfigs.confDcaXYCustom2FilterCut / aod::femtouniverseparticle::pt)) && (aod::femtouniverseparticle::tpcNClsFound > as(twotracksconfigs.confTrkTPCnclsMin)) && aod::femtouniverseparticle::tpcNClsCrossedRows > as(twotracksconfigs.confTrkTPCcRowsMin) && (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2) && aod::femtouniverseparticle::pt tracktwofilter.confpLowPart2; + + Partition partsTwoSigma = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2)) && aod::femtouniverseparticle::pt < tracktwofilter.confPtHighPart2 && aod::femtouniverseparticle::pt > tracktwofilter.confpLowPart2; + Partition partsTwoSigmaITS = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2)) && aod::femtouniverseparticle::pt < tracktwofilter.confPtHighPart2 && aod::femtouniverseparticle::pt > tracktwofilter.confpLowPart2; Partition partsTwoMC = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack) && (aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2)) && aod::femtouniverseparticle::pt < tracktwofilter.confPtHighPart2 && aod::femtouniverseparticle::pt > tracktwofilter.confpLowPart2; + Partition partsTwoMCITS = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack) && (aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2)) && aod::femtouniverseparticle::pt < tracktwofilter.confPtHighPart2 && aod::femtouniverseparticle::pt > tracktwofilter.confpLowPart2; Partition partsTwoMCTruth = aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack) && aod::femtouniverseparticle::pt < tracktwofilter.confPtHighPart2 && aod::femtouniverseparticle::pt > tracktwofilter.confpLowPart2; @@ -149,10 +177,6 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { /// Histogramming for Event FemtoUniverseEventHisto eventHisto; - /// The configurables need to be passed to an std::vector - int vPIDPartOne, vPIDPartTwo; - std::vector kNsigma; - /// Event part struct : o2::framework::ConfigurableGroup { Configurable confV0MLow{"confV0MLow", 0.0, "Lower limit for V0M multiplicity"}; @@ -166,32 +190,38 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { // Filter trackAdditionalfilter = (nabs(aod::femtouniverseparticle::eta) < twotracksconfigs.confEtaMax); // example filtering on configurable /// Particle part - ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; - ConfigurableAxis confTempFitVarpTBins{"confTempFitVarpTBins", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot"}; + struct : o2::framework::ConfigurableGroup { + ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis confTempFitVarpTBins{"confTempFitVarpTBins", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot"}; + ConfigurableAxis confTOFnSigmaBins{"confTOFnSigmaBins", {200, -4.975, 5.025}, "Binning of the TOF Nsigma vs. pT vs. centrality plot"}; + } partBinning; /// Correlation part ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 32.0f, 36.0f, 40.0f, 44.0f, 48.0f, 52.0f, 56.0f, 60.0f, 64.0f, 68.0f, 72.0f, 76.0f, 80.0f, 84.0f, 88.0f, 92.0f, 96.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity or centrality"}; // \todo to be obtained from the hash task ConfigurableAxis confMultKstarBins{"confMultKstarBins", {VARIABLE_WIDTH, 0.0f, 13.0f, 20.0f, 30.0f, 40.0f, 50.0f, 100.0f, 99999.f}, "Bins for kstar analysis in multiplicity or centrality bins (10 is maximum)"}; ConfigurableAxis confKtKstarBins{"confKtKstarBins", {VARIABLE_WIDTH, 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 2.0f, 99999.f}, "Bins for kstar analysis in kT bins (10 is maximum)"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - - ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; - ConfigurableAxis confkstarBins{"confkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; ConfigurableAxis confmTBins{"confmTBins", {225, 0., 7.5}, "binning mT"}; Configurable confNEventsMix{"confNEventsMix", 5, "Number of events for mixing"}; - Configurable confIsCPR{"confIsCPR", true, "Close Pair Rejection"}; - Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; - Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; - Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; - Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; - Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; - Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.80, "Chosen CPR radii"}; - Configurable confRandomizeCPR{"confRandomizeCPR", false, "Fill Dete-Dphi histograms in random order: p1, p2 or p2, p1"}; + ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; + + struct : o2::framework::ConfigurableGroup { + Configurable confIsCPR{"confIsCPR", true, "Close Pair Rejection"}; + Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; + Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; + Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; + Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.80, "Chosen CPR radii"}; + Configurable confRandomizeCPR{"confRandomizeCPR", false, "Fill Dete-Dphi histograms in random order: p1, p2 or p2, p1"}; + ConfigurableAxis confDeltaEtaAxis{"confDeltaEtaAxis", {100, -0.15, 0.15}, "DeltaEta"}; + ConfigurableAxis confDeltaPhiStarAxis{"confDeltaPhiStarAxis", {100, -0.15, 0.15}, "DeltaPhiStar"}; + } cprConfigs; Configurable isPairIdentical{"isPairIdentical", true, "'true' for identical particles, 'false' for non-identical particles"}; struct : o2::framework::ConfigurableGroup { @@ -208,9 +238,6 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { Configurable confMaxEEMinv{"confMaxEEMinv", 0.002, "Max. minv of e-e+ pair for gamma pair rejection"}; Configurable confMaxDTheta{"confMaxDTheta", 0.008, "Max. DeltaTheta of pair for gamma pair rejection"}; - ConfigurableAxis confDeltaEtaAxis{"confDeltaEtaAxis", {100, -0.15, 0.15}, "DeltaEta"}; - ConfigurableAxis confDeltaPhiStarAxis{"confDeltaPhiStarAxis", {100, -0.15, 0.15}, "DeltaPhiStar"}; - FemtoUniverseFemtoContainer sameEventCont; FemtoUniverseFemtoContainer mixedEventCont; @@ -255,115 +282,226 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { HistogramRegistry sphericityRegistry{"SphericityHisto", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - TRandom2* randgen; - /// TPC Pion/Kaon/Proton Sigma selection (general) - bool isNSigma(float mom, float nsigmaTPC, float nsigmaTOF) + bool isNSigma(float mom, float nsigmaTPC, float nsigmaTOF, float nsigmaITS = -999) { // |nsigma_TPC| < 3 for p < 0.5 GeV/c // |nsigma_combined| < 3 for p > 0.5 // using configurables: // confTOFpMin -- momentum value when we start using TOF; set to 1000 if TOF not needed + // confNsigmaTPCITSCombined -> TPC and ITS nSigma combined for momentum TPC Sigma for momentum < confTOFpMin // confNsigmaCombined -> TPC and TOF Sigma (combined) for momentum > confTOFpMin + if (twotracksconfigs.isNsigmaRectangular) { + if (mom < twotracksconfigs.confTOFpMin) { + if (twotracksconfigs.confIsAddITSNsigma) { + return (std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC && std::abs(nsigmaITS) < twotracksconfigs.confNsigmaTPCITSCombined); + } + return std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC; + } + return (std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC && std::abs(nsigmaTOF) < twotracksconfigs.confNsigmaCombined); + } if (mom < twotracksconfigs.confTOFpMin) { + if (twotracksconfigs.confIsAddITSNsigma) { + return std::hypot(nsigmaITS, nsigmaTPC) < twotracksconfigs.confNsigmaTPCITSCombined; + } return std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC; - } else { - return std::hypot(nsigmaTOF, nsigmaTPC) < twotracksconfigs.confNsigmaCombined; } + return std::hypot(nsigmaTOF, nsigmaTPC) < twotracksconfigs.confNsigmaCombined; + } + + bool isNSigmaProton(float mom, float nsigmaTPC, float nsigmaTOF, float nsigmaTPCkaon, float nsigmaTOFkaon, float nsigmaTPCpion, float nsigmaTOFpion, float nsigmaITS = -999) + { + // with additional nSigma kaon and pion rejection + if (mom < twotracksconfigs.confTOFpMinRej) { // rejection based on TPC + if (std::abs(nsigmaTPCkaon) < twotracksconfigs.confTPCNsigmaKaonRejection || std::abs(nsigmaTPCpion) < twotracksconfigs.confTPCNsigmaPionRejection) { + return false; + } + } else if (twotracksconfigs.isNsigmaRejectionCombined) { // rejection based on TPC and TOF combined + if (std::hypot(nsigmaTOFkaon, nsigmaTPCkaon) < twotracksconfigs.confNsigmaKaonRejection || std::hypot(nsigmaTOFpion, nsigmaTPCpion) < twotracksconfigs.confNsigmaPionRejection) { + return false; + } + } else if (std::abs(nsigmaTOFkaon) < twotracksconfigs.confTOFNsigmaKaonRejection || std::abs(nsigmaTOFpion) < twotracksconfigs.confTOFNsigmaPionRejection) { + return false; // rejection based on TOF + } + + if (twotracksconfigs.isNsigmaRectangular) { + if (mom < twotracksconfigs.confTOFpMin) { + if (twotracksconfigs.confIsAddITSNsigma) { + return (std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC && std::abs(nsigmaITS) < twotracksconfigs.confNsigmaTPCITSCombined); + } + return std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC; + } + return (std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC && std::abs(nsigmaTOF) < twotracksconfigs.confNsigmaCombined); + } + + if (mom < twotracksconfigs.confTOFpMin) { + if (twotracksconfigs.confIsAddITSNsigma) { + return std::hypot(nsigmaITS, nsigmaTPC) < twotracksconfigs.confNsigmaTPCITSCombined; + } + return std::abs(nsigmaTPC) < twotracksconfigs.confNsigmaTPC; + } + return std::hypot(nsigmaTOF, nsigmaTPC) < twotracksconfigs.confNsigmaCombined; } /// TPC Kaon Sigma selection (stricter cuts for K+ and K-) -- based on Run2 results bool isKaonNsigma(float mom, float nsigmaTPCK, float nsigmaTOFK) { - double nSigmaTPCRanges[3] = {1, 2, 3}; - double nSigmaTOFRanges[2] = {1.5, 2}; - double momRanges[4] = {0.4, 0.45, 0.8, 1.5}; + std::array nSigmaTPCRanges = {{1, 2, 3}}; + std::array nSigmaTOFRanges = {{1.5, 2}}; + std::array momRanges = {{0.4, 0.45, 0.8, 1.5}}; - if (twotracksconfigs.isKaonNsigma == true) { + if (twotracksconfigs.isKaonNsigma) { if (mom < momRanges[0]) { return std::abs(nsigmaTPCK) < nSigmaTPCRanges[1]; - } else if (mom > momRanges[0] && mom < momRanges[1]) { + } + if (mom > momRanges[0] && mom < momRanges[1]) { return std::abs(nsigmaTPCK) < nSigmaTPCRanges[0]; - } else if (mom > momRanges[1] && mom < momRanges[2]) { + } + if (mom > momRanges[1] && mom < momRanges[2]) { return (std::abs(nsigmaTPCK) < nSigmaTPCRanges[2] && std::abs(nsigmaTOFK) < nSigmaTOFRanges[1]); - } else if (mom > momRanges[2] && mom < momRanges[3]) { + } + if (mom > momRanges[2] && mom < momRanges[3]) { return (std::abs(nsigmaTPCK) < nSigmaTPCRanges[2] && std::abs(nsigmaTOFK) < nSigmaTOFRanges[0]); - } else { - return false; } - } else { - return isNSigma(mom, nsigmaTPCK, nsigmaTOFK); + return false; } + return isNSigma(mom, nsigmaTPCK, nsigmaTOFK); } - bool isParticleNSigma(int8_t particle_number, float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK) + template + bool isParticleNSigma(int8_t particle_number, PartType const& part) { + float mom = part.p(); + float nsigmaTPCPr = trackCuts.getNsigmaTPC(part, o2::track::PID::Proton); + float nsigmaTOFPr = trackCuts.getNsigmaTOF(part, o2::track::PID::Proton); + float nsigmaITSPr = -999; + + float nsigmaTPCK = trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon); + float nsigmaTOFK = trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon); + float nsigmaITSK = -999; + + float nsigmaTPCPi = trackCuts.getNsigmaTPC(part, o2::track::PID::Pion); + float nsigmaTOFPi = trackCuts.getNsigmaTOF(part, o2::track::PID::Pion); + float nsigmaITSPi = -999; + + if constexpr (requires(PartType& part) { part.tpcFullNSigmaPr(); }) { + nsigmaTPCPr = part.tpcFullNSigmaPr(); + nsigmaTOFPr = part.tofFullNSigmaPr(); + nsigmaTPCK = part.tpcFullNSigmaKa(); + nsigmaTOFK = part.tofFullNSigmaKa(); + nsigmaTPCPi = part.tpcFullNSigmaPi(); + nsigmaTOFPi = part.tofFullNSigmaPi(); + } + + if constexpr (requires(PartType& part) { part.itsNSigmaPr(); }) { + nsigmaITSPr = trackCuts.getNsigmaITS(part, o2::track::PID::Proton); + nsigmaITSK = trackCuts.getNsigmaITS(part, o2::track::PID::Kaon); + nsigmaITSPi = trackCuts.getNsigmaITS(part, o2::track::PID::Pion); + } + enum ParticleNumber { partOne = 1, partTwo = 2 }; if (particle_number == partOne) { switch (trackonefilter.confPDGCodePartOne) { - case 2212: // Proton+ - case -2212: // Proton- - return isNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); - break; - case 211: // Pion+ - case -211: // Pion- - case 111: // Pion 0 - return isNSigma(mom, nsigmaTPCPi, nsigmaTOFPi); - break; - case 321: // Kaon+ - case -321: // Kaon- - return isKaonNsigma(mom, nsigmaTPCK, nsigmaTOFK); - break; - case 130: // Kaon 0 LONG - case 310: // Kaon 0 SHORT - return isNSigma(mom, nsigmaTPCK, nsigmaTOFK); - break; + case kProton: + case kProtonBar: + return isNSigmaProton(mom, nsigmaTPCPr, nsigmaTOFPr, nsigmaTPCK, nsigmaTOFK, nsigmaTPCPi, nsigmaTOFPi, nsigmaITSPr); + case kPiPlus: + case kPiMinus: + case kPi0: + return isNSigma(mom, nsigmaTPCPi, nsigmaTOFPi, nsigmaITSPi); + case kKPlus: + case kKMinus: + case kK0Long: + case kK0Short: + return isNSigma(mom, nsigmaTPCK, nsigmaTOFK, nsigmaITSK); default: return false; } - return false; } else if (particle_number == partTwo) { switch (tracktwofilter.confPDGCodePartTwo) { - case 2212: // Proton+ - case -2212: // Proton- - return isNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); - break; - case 211: // Pion+ - case -211: // Pion- - case 111: // Pion 0 - return isNSigma(mom, nsigmaTPCPi, nsigmaTOFPi); - break; - case 321: // Kaon+ - case -321: // Kaon- - return isKaonNsigma(mom, nsigmaTPCK, nsigmaTOFK); - break; - case 130: // Kaon 0 LONG - case 310: // Kaon 0 SHORT - return isNSigma(mom, nsigmaTPCK, nsigmaTOFK); - break; + case kProton: + case kProtonBar: + return isNSigmaProton(mom, nsigmaTPCPr, nsigmaTOFPr, nsigmaTPCK, nsigmaTOFK, nsigmaTPCPi, nsigmaTOFPi, nsigmaITSPr); + case kPiPlus: + case kPiMinus: + case kPi0: + return isNSigma(mom, nsigmaTPCPi, nsigmaTOFPi, nsigmaITSPi); + case kKPlus: + case kKMinus: + case kK0Long: + case kK0Short: + return isNSigma(mom, nsigmaTPCK, nsigmaTOFK, nsigmaITSK); + default: return false; } - return false; } else { LOGF(fatal, "Wrong number of particle chosen! It should be 1 or 2. It is -> %d", particle_number); } return false; } + template + float whichTOFNSigma(int PDGcode, PartType const& part) + { + switch (PDGcode) { + case kProton: + case kProtonBar: + return part.tofFullNSigmaPr(); + case kPiPlus: + case kPiMinus: + case kPi0: + return part.tofFullNSigmaPi(); + case kKPlus: + case kKMinus: + case kK0Long: + case kK0Short: + return part.tofFullNSigmaKa(); + default: + return -999; + } + } + template + float whichTPCNSigma(int PDGcode, PartType const& part) + { + switch (PDGcode) { + case kProton: + case kProtonBar: + return part.tpcFullNSigmaPr(); + case kPiPlus: + case kPiMinus: + case kPi0: + return part.tpcFullNSigmaPi(); + case kKPlus: + case kKMinus: + case kK0Long: + case kK0Short: + return part.tpcFullNSigmaKa(); + default: + return -999; + } + } + void init(InitContext&) { - colBinning = ColumnBinningPolicy{{confVtxBins, confMultBins}, true}; + colBinning = ColumnBinningPolicy{{confVtxBins, confMultBins}}; eventHisto.init(&qaRegistry); - trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true); + trackHistoPartOne.init(&qaRegistry, partBinning.confTempFitVarpTBins, partBinning.confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true); - trackHistoPartTwo.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, tracktwofilter.confPDGCodePartTwo, true); + trackHistoPartTwo.init(&qaRegistry, partBinning.confTempFitVarpTBins, partBinning.confTempFitVarBins, twotracksconfigs.confIsMC, tracktwofilter.confPDGCodePartTwo, true); + if (doprocessPurityQA || doprocessPurityQAWithITS) { + qaRegistry.add("Tracks_one/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}; Centrality", kTH3F, {partBinning.confTempFitVarpTBins, partBinning.confTOFnSigmaBins, confMultBins}); + qaRegistry.add("Tracks_two/nSigmaTOF", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}; Centrality", kTH3F, {partBinning.confTempFitVarpTBins, partBinning.confTOFnSigmaBins, confMultBins}); + + qaRegistry.add("Tracks_one/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}; Centrality", kTH3F, {partBinning.confTempFitVarpTBins, partBinning.confTOFnSigmaBins, confMultBins}); + qaRegistry.add("Tracks_two/nSigmaTPC", "; #it{p} (GeV/#it{c}); n#sigma_{TOF}; Centrality", kTH3F, {partBinning.confTempFitVarpTBins, partBinning.confTOFnSigmaBins, confMultBins}); + } if (confFillDebug) { sphericityRegistry.add("sphericity", ";Sphericity;Entries", kTH1F, {{150, 0.0, 3, "Sphericity"}}); mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); @@ -435,17 +573,13 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { } pairCleaner.init(&qaRegistry); - if (confIsCPR.value) { - pairCloseRejection.init(&resultRegistry, &qaRegistry, confDeltaEtaAxis, confDeltaPhiStarAxis, confCPRdeltaPhiCutMin.value, confCPRdeltaPhiCutMax.value, confCPRdeltaEtaCutMin.value, confCPRdeltaEtaCutMax.value, confCPRChosenRadii.value, confCPRPlotPerRadii.value); + if (cprConfigs.confIsCPR.value) { + pairCloseRejection.init(&resultRegistry, &qaRegistry, cprConfigs.confDeltaEtaAxis, cprConfigs.confDeltaPhiStarAxis, cprConfigs.confCPRdeltaPhiCutMin.value, cprConfigs.confCPRdeltaPhiCutMax.value, cprConfigs.confCPRdeltaEtaCutMin.value, cprConfigs.confCPRdeltaEtaCutMax.value, cprConfigs.confCPRChosenRadii.value, cprConfigs.confCPRPlotPerRadii.value); } - - vPIDPartOne = trackonefilter.confPIDPartOne.value; - vPIDPartTwo = tracktwofilter.confPIDPartTwo.value; - kNsigma = twotracksconfigs.confTrkPIDnSigmaMax.value; } template - void fillCollision(CollisionType col) + void fillCollision(CollisionType const& col) { if (confFillDebug) { mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({col.posZ(), col.multV0M()})); @@ -466,16 +600,15 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { /// \param pairType describes charge of correlation pair (plus-minus (1), plus-plus (2), minus-minus (3)) /// \param fillQA enables filling of QA histograms template - void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, int pairType, bool fillQA) + void doSameEvent(PartitionType const& groupPartsOne, PartitionType const& groupPartsTwo, PartType const& parts, float magFieldTesla, float multCol, int pairType, bool fillQA) { - enum PairTypes { type1 = 1, type2 = 2, type3 = 3 }; /// Histogramming same event if ((pairType == type1 || pairType == type2) && fillQA) { for (const auto& part : groupPartsOne) { - if (!isParticleNSigma((int8_t)1, part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)1, part)) { continue; } trackHistoPartOne.fillQA(part); @@ -484,7 +617,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { if ((pairType == type1 || pairType == type3) && fillQA) { for (const auto& part : groupPartsTwo) { - if (!isParticleNSigma((int8_t)2, part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)2, part)) { continue; } trackHistoPartTwo.fillQA(part); @@ -496,11 +629,11 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { /// Now build the combinations for non-identical particle pairs for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (!isParticleNSigma((int8_t)1, p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)1, p1)) { continue; } - if (!isParticleNSigma((int8_t)2, p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)2, p2)) { continue; } @@ -513,16 +646,16 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { continue; } - if (confIsCPR.value) { - double rand; + if (cprConfigs.confIsCPR.value) { + double rand = 0; auto part1 = p1; auto part2 = p2; - if (confRandomizeCPR) { + if (cprConfigs.confRandomizeCPR) { randgen = new TRandom2(0); rand = randgen->Rndm(); - if (rand > 0.5) { + if (rand > randCompareValue) { part1 = p2; part2 = p1; } @@ -536,32 +669,33 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); - sameEventCont.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + sameEventCont.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { sameEventMultCont.fill(kstar, multCol, kT); + } } } else { /// Now build the combinations for identical particles pairs for (const auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsOne, groupPartsOne))) { - if (!isParticleNSigma((int8_t)2, p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)2, p1)) { continue; } - if (!isParticleNSigma((int8_t)2, p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)2, p2)) { continue; } - if (confIsCPR.value) { - double rand; + if (cprConfigs.confIsCPR.value) { + double rand = 0; auto part1 = p1; auto part2 = p2; - if (confRandomizeCPR) { + if (cprConfigs.confRandomizeCPR) { randgen = new TRandom2(0); rand = randgen->Rndm(); - if (rand > 0.5) { + if (rand > randCompareValue) { part1 = p2; part2 = p1; } @@ -579,40 +713,44 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { switch (pairType) { case 2: { - if (isPairIdentical == true) { + if (isPairIdentical) { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass1); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass1); - sameEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + sameEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { sameEventMultContPP.fill(kstar, multCol, kT); + } } else { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); - sameEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + sameEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { sameEventMultContPP.fill(kstar, multCol, kT); + } } break; } case 3: { - if (isPairIdentical == true) { + if (isPairIdentical) { float kstar = FemtoUniverseMath::getkstar(p1, mass2, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass2, p2, mass2); - sameEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + sameEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { sameEventMultContMM.fill(kstar, multCol, kT); + } } else { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); - sameEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + sameEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { sameEventMultContMM.fill(kstar, multCol, kT); + } } break; @@ -624,6 +762,84 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { } } + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoUniverseParticleTable + void processPurityQA(soa::Filtered::iterator const& col, + FilteredFemtoFullSigmaParticles const&) + { + fillCollision(col); + if (confFillDebug) { + sphericityRegistry.fill(HIST("sphericity"), col.sphericity()); + } + + auto thegroupPartsOne = partsOneSigma->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto thegroupPartsTwo = partsTwoSigma->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + float const multCol = col.multV0M(); + + for (const auto& part : thegroupPartsOne) { + if (!isParticleNSigma((int8_t)1, part)) { + continue; + } + + trackHistoPartOne.fillQA(part); + qaRegistry.fill(HIST("Tracks_one/nSigmaTOF"), part.pt(), whichTOFNSigma(trackonefilter.confPDGCodePartOne, part), multCol); + qaRegistry.fill(HIST("Tracks_one/nSigmaTPC"), part.pt(), whichTPCNSigma(trackonefilter.confPDGCodePartOne, part), multCol); + } + + if (!isPairIdentical) { + // fill second + for (const auto& part : thegroupPartsTwo) { + if (!isParticleNSigma((int8_t)2, part)) { + continue; + } + trackHistoPartTwo.fillQA(part); + qaRegistry.fill(HIST("Tracks_two/nSigmaTOF"), part.pt(), whichTOFNSigma(tracktwofilter.confPDGCodePartTwo, part), multCol); + qaRegistry.fill(HIST("Tracks_two/nSigmaTPC"), part.pt(), whichTPCNSigma(trackonefilter.confPDGCodePartOne, part), multCol); + } + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processPurityQA, "Enable processing QA for purity estimation", false); + + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoUniverseParticleTable + void processPurityQAWithITS(soa::Filtered::iterator const& col, + FilteredFemtoFullSigmaParticlesITS const&) + { + fillCollision(col); + if (confFillDebug) { + sphericityRegistry.fill(HIST("sphericity"), col.sphericity()); + } + + auto thegroupPartsOne = partsOneSigmaITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto thegroupPartsTwo = partsTwoSigmaITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + float const multCol = col.multV0M(); + + for (const auto& part : thegroupPartsOne) { + if (!isParticleNSigma((int8_t)1, part)) { + continue; + } + + trackHistoPartOne.fillQA(part); + qaRegistry.fill(HIST("Tracks_one/nSigmaTOF"), part.pt(), whichTOFNSigma(trackonefilter.confPDGCodePartOne, part), multCol); + qaRegistry.fill(HIST("Tracks_one/nSigmaTPC"), part.pt(), whichTPCNSigma(trackonefilter.confPDGCodePartOne, part), multCol); + } + + if (!isPairIdentical) { + // fill second + for (const auto& part : thegroupPartsTwo) { + if (!isParticleNSigma((int8_t)2, part)) { + continue; + } + trackHistoPartTwo.fillQA(part); + qaRegistry.fill(HIST("Tracks_two/nSigmaTOF"), part.pt(), whichTOFNSigma(tracktwofilter.confPDGCodePartTwo, part), multCol); + qaRegistry.fill(HIST("Tracks_two/nSigmaTPC"), part.pt(), whichTPCNSigma(trackonefilter.confPDGCodePartOne, part), multCol); + } + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processPurityQAWithITS, "Enable processing QA for purity estimation", false); + /// process function for to call doSameEvent with Data /// \param col subscribe to the collision table (Data) /// \param parts subscribe to the femtoUniverseParticleTable @@ -644,13 +860,44 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { doSameEvent(thegroupPartsOne, thegroupPartsTwo, parts, col.magField(), col.multV0M(), 1, fillQA); fillQA = false; } - if (processPair.cfgProcessPP) + if (processPair.cfgProcessPP) { doSameEvent(thegroupPartsOne, thegroupPartsOne, parts, col.magField(), col.multV0M(), 2, fillQA); - if (processPair.cfgProcessMM) + } + if (processPair.cfgProcessMM) { doSameEvent(thegroupPartsTwo, thegroupPartsTwo, parts, col.magField(), col.multV0M(), 3, fillQA); + } } PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processSameEvent, "Enable processing same event", true); + /// process function for to call doSameEvent with Data + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoUniverseParticleTable + void processSameEventWithITS(soa::Filtered::iterator const& col, + FilteredFemtoFullParticlesITS const& parts) + { + fillCollision(col); + if (confFillDebug) { + sphericityRegistry.fill(HIST("sphericity"), col.sphericity()); + } + + auto thegroupPartsOne = partsOneITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto thegroupPartsTwo = partsTwoITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + bool fillQA = true; + + if (processPair.cfgProcessPM) { + doSameEvent(thegroupPartsOne, thegroupPartsTwo, parts, col.magField(), col.multV0M(), 1, fillQA); + fillQA = false; + } + if (processPair.cfgProcessPP) { + doSameEvent(thegroupPartsOne, thegroupPartsOne, parts, col.magField(), col.multV0M(), 2, fillQA); + } + if (processPair.cfgProcessMM) { + doSameEvent(thegroupPartsTwo, thegroupPartsTwo, parts, col.magField(), col.multV0M(), 3, fillQA); + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processSameEventWithITS, "Enable processing same event", false); + /// process function for to call doSameEvent with Monte Carlo /// \param col subscribe to the collision table (Monte Carlo Reconstructed reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLabels to access Monte Carlo truth @@ -669,10 +916,12 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { doSameEvent(thegroupPartsOne, thegroupPartsTwo, parts, col.magField(), col.multV0M(), 1, fillQA); fillQA = false; } - if (processPair.cfgProcessPP) + if (processPair.cfgProcessPP) { doSameEvent(thegroupPartsOne, thegroupPartsOne, parts, col.magField(), col.multV0M(), 2, fillQA); - if (processPair.cfgProcessMM) + } + if (processPair.cfgProcessMM) { doSameEvent(thegroupPartsTwo, thegroupPartsTwo, parts, col.magField(), col.multV0M(), 3, fillQA); + } } PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processSameEventMC, "Enable processing same event for Monte Carlo", false); @@ -688,16 +937,15 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { /// \param multCol multiplicity of the collision /// \param pairType describes charge of correlation pair (plus-minus (1), plus-plus (2), minus-minus (3)) template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, int pairType) + void doMixedEvent(PartitionType const& groupPartsOne, PartitionType const& groupPartsTwo, PartType const& parts, float magFieldTesla, float multCol, int pairType) { - for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (!isParticleNSigma((int8_t)2, p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)2, p1)) { continue; } - if (!isParticleNSigma((int8_t)2, p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon))) { + if (!isParticleNSigma((int8_t)2, p2)) { continue; } @@ -705,16 +953,16 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { continue; } - if (confIsCPR.value) { - double rand; + if (cprConfigs.confIsCPR.value) { + double rand = 0; auto part1 = p1; auto part2 = p2; - if (confRandomizeCPR) { + if (cprConfigs.confRandomizeCPR) { randgen = new TRandom2(0); rand = randgen->Rndm(); - if (rand > 0.5) { + if (rand > randCompareValue) { part1 = p2; part2 = p1; } @@ -730,47 +978,52 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); - mixedEventCont.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + mixedEventCont.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { mixedEventMultCont.fill(kstar, multCol, kT); + } break; } case 2: { - if (isPairIdentical == true) { + if (isPairIdentical) { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass1); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass1); - mixedEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + mixedEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { mixedEventMultContPP.fill(kstar, multCol, kT); + } } else { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); - mixedEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + mixedEventContPP.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { mixedEventMultContPP.fill(kstar, multCol, kT); + } } break; } case 3: { - if (isPairIdentical == true) { + if (isPairIdentical) { float kstar = FemtoUniverseMath::getkstar(p1, mass2, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass2, p2, mass2); - mixedEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + mixedEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { mixedEventMultContMM.fill(kstar, multCol, kT); + } } else { float kstar = FemtoUniverseMath::getkstar(p1, mass1, p2, mass2); float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); - mixedEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D); - if (cfgProcessMultBins) + mixedEventContMM.setPair(p1, p2, multCol, twotracksconfigs.confUse3D, twotracksconfigs.confOnlyPrimaryMCPair); + if (cfgProcessMultBins) { mixedEventMultContMM.fill(kstar, multCol, kT); + } } break; @@ -789,7 +1042,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { - const int multiplicityCol = collision1.multV0M(); + const float multiplicityCol = collision1.multV0M(); if (confFillDebug) { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); } @@ -820,6 +1073,45 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { } PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processMixedEvent, "Enable processing mixed events", true); + /// process function for to call doMixedEvent with Data + /// \param cols subscribe to the collisions table (Data) + /// \param parts subscribe to the femtoUniverseParticleTable + void processMixedEventWithITS(soa::Filtered const& cols, + FilteredFemtoFullParticlesITS const& parts) + { + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { + + const float multiplicityCol = collision1.multV0M(); + if (confFillDebug) { + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); + } + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; + } + + if (processPair.cfgProcessPM) { + auto groupPartsOne = partsOneITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwoITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, parts, magFieldTesla1, multiplicityCol, 1); + } + if (processPair.cfgProcessPP) { + auto groupPartsOne = partsOneITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsOneITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, parts, magFieldTesla1, multiplicityCol, 2); + } + if (processPair.cfgProcessMM) { + auto groupPartsOne = partsTwoITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwoITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, parts, magFieldTesla1, multiplicityCol, 3); + } + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processMixedEventWithITS, "Enable processing mixed events", false); + /// brief process function for to call doMixedEvent with Monte Carlo /// \param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth @@ -830,7 +1122,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { - const int multiplicityCol = collision1.multV0M(); + const float multiplicityCol = collision1.multV0M(); if (confFillDebug) { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); } @@ -862,6 +1154,76 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { } PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processMixedEventMC, "Enable processing mixed events MC reco", false); + template + void doFractions(PartType const& p1, PartType const& p2, PartsType const& parts, PartsMcType const& mparts, float magFieldTesla, int partType) + { + if (!isParticleNSigma((int8_t)2, p1) || !isParticleNSigma((int8_t)2, p2)) { + return; + } + + if (cprConfigs.confIsCPR.value) { + double rand = 0; + auto part1 = p1; + auto part2 = p2; + + if (cprConfigs.confRandomizeCPR) { + randgen = new TRandom2(0); + rand = randgen->Rndm(); + + if (rand > randCompareValue) { + part1 = p2; + part2 = p1; + } + delete randgen; + } + if (pairCloseRejection.isClosePair(part1, part2, parts, magFieldTesla, femto_universe_container::EventType::mixed)) { + return; + } + } + switch (partType) { + case 1: + mixedMultRegistryPM.fill(HIST("MCreco/motherParticle"), p1.motherPDG(), p2.motherPDG()); + break; + case 2: + mixedMultRegistryPP.fill(HIST("MCreco/motherParticle"), p1.motherPDG(), p2.motherPDG()); + break; + case 3: + mixedMultRegistryMM.fill(HIST("MCreco/motherParticle"), p1.motherPDG(), p2.motherPDG()); + break; + default: + break; + } + auto mcPartId1 = p1.fdMCParticleId(); + auto mcPartId2 = p2.fdMCParticleId(); + if (mcPartId1 == -1 || mcPartId2 == -1) { + return; + } + const auto& mcParticle1 = mparts.iteratorAt(mcPartId1); + const auto& mcParticle2 = mparts.iteratorAt(mcPartId2); + switch (partType) { + case 1: + if ((trackonefilter.confPDGCodePartOne != mcParticle1.pdgMCTruth()) || (tracktwofilter.confPDGCodePartTwo != mcParticle2.pdgMCTruth())) { + return; + } + mixedMultRegistryPM.fill(HIST("MCreco/motherParticlePDGCheck"), p1.motherPDG(), p2.motherPDG()); + break; + case 2: + if ((trackonefilter.confPDGCodePartOne != mcParticle1.pdgMCTruth()) || (trackonefilter.confPDGCodePartOne != mcParticle2.pdgMCTruth())) { + return; + } + mixedMultRegistryPP.fill(HIST("MCreco/motherParticlePDGCheck"), p1.motherPDG(), p2.motherPDG()); + break; + case 3: + if ((tracktwofilter.confPDGCodePartTwo != mcParticle1.pdgMCTruth()) || (tracktwofilter.confPDGCodePartTwo != mcParticle2.pdgMCTruth())) { + return; + } + mixedMultRegistryMM.fill(HIST("MCreco/motherParticlePDGCheck"), p1.motherPDG(), p2.motherPDG()); + break; + default: + break; + } + } + /// brief process function for to call process fractions with Monte Carlo reco /// \param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth @@ -870,75 +1232,50 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { FemtoRecoParticles const& parts, o2::aod::FdMCParticles const& mparts) { - - auto doFractions = [&](auto& p1, auto& p2, auto& magFieldTesla, int partType) -> void { - if (!isParticleNSigma((int8_t)2, p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon))) { - return; + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { + const float multiplicityCol = collision1.multV0M(); + if (confFillDebug) { + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); } + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); - if (!isParticleNSigma((int8_t)2, p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon))) { - return; + if (magFieldTesla1 != magFieldTesla2) { + continue; } - if (confIsCPR.value) { - double rand; - auto part1 = p1; - auto part2 = p2; - - if (confRandomizeCPR) { - randgen = new TRandom2(0); - rand = randgen->Rndm(); + auto groupPartsOne = partsOneMC->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwoMC->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); - if (rand > 0.5) { - part1 = p2; - part2 = p1; - } - delete randgen; - } - if (pairCloseRejection.isClosePair(part1, part2, parts, magFieldTesla, femto_universe_container::EventType::mixed)) { - return; + if (processPair.cfgProcessPM) { + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + doFractions(p1, p2, parts, mparts, magFieldTesla1, 1); } } - switch (partType) { - case 1: - mixedMultRegistryPM.fill(HIST("MCreco/motherParticle"), p1.motherPDG(), p2.motherPDG()); - break; - case 2: - mixedMultRegistryPP.fill(HIST("MCreco/motherParticle"), p1.motherPDG(), p2.motherPDG()); - break; - case 3: - mixedMultRegistryMM.fill(HIST("MCreco/motherParticle"), p1.motherPDG(), p2.motherPDG()); - break; + if (processPair.cfgProcessPP) { + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsOne))) { + doFractions(p1, p2, parts, mparts, magFieldTesla1, 2); + } } - auto mcPartId1 = p1.fdMCParticleId(); - if (mcPartId1 == -1) - return; - auto mcPartId2 = p2.fdMCParticleId(); - if (mcPartId2 == -1) - return; - const auto& mcParticle1 = mparts.iteratorAt(mcPartId1); - const auto& mcParticle2 = mparts.iteratorAt(mcPartId2); - switch (partType) { - case 1: - if ((trackonefilter.confPDGCodePartOne != mcParticle1.pdgMCTruth()) || (tracktwofilter.confPDGCodePartTwo != mcParticle2.pdgMCTruth())) - return; - mixedMultRegistryPM.fill(HIST("MCreco/motherParticlePDGCheck"), p1.motherPDG(), p2.motherPDG()); - break; - case 2: - if ((trackonefilter.confPDGCodePartOne != mcParticle1.pdgMCTruth()) || (trackonefilter.confPDGCodePartOne != mcParticle2.pdgMCTruth())) - return; - mixedMultRegistryPP.fill(HIST("MCreco/motherParticlePDGCheck"), p1.motherPDG(), p2.motherPDG()); - break; - case 3: - if ((tracktwofilter.confPDGCodePartTwo != mcParticle1.pdgMCTruth()) || (tracktwofilter.confPDGCodePartTwo != mcParticle2.pdgMCTruth())) - return; - mixedMultRegistryMM.fill(HIST("MCreco/motherParticlePDGCheck"), p1.motherPDG(), p2.motherPDG()); - break; + if (processPair.cfgProcessMM) { + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsTwo, groupPartsTwo))) { + doFractions(p1, p2, parts, mparts, magFieldTesla1, 3); + } } - }; + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processFractions, "Enable processing fractions for MC reco", false); + /// brief process function for to call process fractions with Monte Carlo reco + /// \param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) + /// \param parts subscribe to joined table FemtoUniverseParticles and FemtoUniverseMCLables to access Monte Carlo truth + /// \param FemtoUniverseMCParticles subscribe to the Monte Carlo truth table + void processFractionsWithITS(o2::aod::FdCollisions const& cols, + FemtoRecoParticlesITS const& parts, + o2::aod::FdMCParticles const& mparts) + { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { - const int multiplicityCol = collision1.multV0M(); + const float multiplicityCol = collision1.multV0M(); if (confFillDebug) { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); } @@ -949,27 +1286,27 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { continue; } - auto groupPartsOne = partsOneMC->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); - auto groupPartsTwo = partsTwoMC->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + auto groupPartsOne = partsOneMCITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwoMCITS->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); if (processPair.cfgProcessPM) { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - doFractions(p1, p2, magFieldTesla1, 1); + doFractions(p1, p2, parts, mparts, magFieldTesla1, 1); } } if (processPair.cfgProcessPP) { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsOne))) { - doFractions(p1, p2, magFieldTesla1, 2); + doFractions(p1, p2, parts, mparts, magFieldTesla1, 2); } } if (processPair.cfgProcessMM) { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsTwo, groupPartsTwo))) { - doFractions(p1, p2, magFieldTesla1, 3); + doFractions(p1, p2, parts, mparts, magFieldTesla1, 3); } } } } - PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processFractions, "Enable processing fractions for MC reco", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackMultKtExtended, processFractionsWithITS, "Enable processing fractions for MC reco", false); /// This function processes fills histograms for fractions analysis /// \todo the trivial loops over the collisions and tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... @@ -979,7 +1316,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { /// \param groupPartsOne partition for the first particle passed by the process function /// \param groupPartsTwo partition for the second particle passed by the process function template - void doFractionsMCTruth(PartitionType groupPartsOne, PartitionType groupPartsTwo, int ContType) + void doFractionsMCTruth(PartitionType const& groupPartsOne, PartitionType const& groupPartsTwo, int ContType) { switch (ContType) { case 1: { @@ -1034,7 +1371,7 @@ struct FemtoUniversePairTaskTrackTrackMultKtExtended { { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, confNEventsMix, -1, cols, cols)) { - const int multiplicityCol = collision1.multV0M(); + const float multiplicityCol = collision1.multV0M(); if (confFillDebug) { mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), multiplicityCol})); } diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx index f33b8ff24a7..3f622883254 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtended.cxx @@ -61,22 +61,27 @@ using namespace o2::soa; namespace { -static constexpr int nPart = 2; -static constexpr int nCuts = 5; +static constexpr int NPart = 2; +static constexpr int NCuts = 5; static const std::vector partNames{"PartOne", "PartTwo"}; static const std::vector cutNames{"MaxPt", "PIDthr", "nSigmaTPC", "nSigmaTPCTOF", "MaxP"}; -static const float cutsTable[nPart][nCuts]{ +static const float cutsTable[NPart][NCuts]{ {4.05f, 1.f, 3.f, 3.f, 100.f}, {4.05f, 1.f, 3.f, 3.f, 100.f}}; } // namespace +enum ParticleNumber { + First, + Second +}; + enum PairType { PlusMinus, PlusPlus, MinusMinus }; -struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { +struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended { Service pdg; @@ -88,13 +93,25 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { Configurable confNsigmaTPC{"confNsigmaTPC", 3.0f, "TPC Pion Sigma for momentum < confTOFPtMin"}; Configurable confNsigmaITSTPCCombined{"confNsigmaITSTPCCombined", 3.0f, "ITS and TPC Pion Sigma (combined) for momentum < confTOFPtMin"}; Configurable confIsElReject{"confIsElReject", false, "Is electron rejection activated"}; - Configurable confIsAddITSNsigma{"confIsAddITSNsigma", false, "Is ITS Pions nsigma added"}; Configurable confNsigmaTPCElRejectMin{"confNsigmaTPCElRejectMin", 2.0f, "TPC Electron SigmaMin for momentum < confTOFPtMin"}; Configurable confNsigmaTPCElRejectMax{"confNsigmaTPCElRejectMax", 2.0f, "TPC Electron SigmaMax for momentum < confTOFPtMin"}; Configurable confTOFPtMin{"confTOFPtMin", 0.5f, "Min. Pt for which TOF is required for PID."}; Configurable confEtaMax{"confEtaMax", 0.8f, "Higher limit for |Eta| (the same for both particles)"}; - Configurable> confCutTable{"confCutTable", {cutsTable[0], nPart, nCuts, partNames, cutNames}, "Particle selections"}; + Configurable confKaonPIDP1{"confKaonPIDP1", 0.3f, "mom range 1 for kaon PID."}; + Configurable confKaonPIDP2{"confKaonPIDP2", 0.45f, "mom range 2 for kaon PID."}; + Configurable confKaonPIDP3{"confKaonPIDP3", 0.55f, "mom range 3 for kaon PID."}; + Configurable confKaonPIDP4{"confKaonPIDP4", 1.5f, "mom range 4 for kaon PID."}; + + Configurable confKaonTPCNsigma1{"confKaonTPCNsigma1", 3.0f, "TPC Kaon NSigma for mom range 1."}; + Configurable confKaonTPCNsigma2{"confKaonTPCNsigma2", 2.0f, "TPC Kaon NSigma for mom range 2."}; + Configurable confKaonTPCNsigma3{"confKaonTPCNsigma3", 1.0f, "TPC Kaon NSigma for mom range 3."}; + Configurable confKaonTPCNsigma4{"confKaonTPCNsigma4", 3.0f, "TPC Kaon NSigma for mom range 4."}; + Configurable confKaonTPCNsigma5{"confKaonTPCNsigma5", 2.0f, "TPC Kaon NSigma for mom range 5."}; + Configurable confKaonTOFNsigma4{"confKaonTOFNsigma4", 3.0f, "TOF Kaon NSigma for mom range 4."}; + Configurable confKaonTOFNsigma5{"confKaonTOFNsigma5", 3.0f, "TOF Kaon NSigma for mom range 5."}; + + Configurable> confCutTable{"confCutTable", {cutsTable[0], NPart, NCuts, partNames, cutNames}, "Particle selections"}; Configurable confNspecies{"confNspecies", 2, "Number of particle spieces with PID info"}; Configurable confIsMC{"confIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; Configurable> confTrkPIDnSigmaMax{"confTrkPIDnSigmaMax", std::vector{4.f, 3.f, 2.f}, "This Configurable needs to be the same as the one used in the producer task"}; @@ -120,6 +137,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { Configurable confPairFracSharedTPCclsMin{"confPairFracSharedTPCclsMin", 0.0, "Min. fraction of TPC shared clusters between two closed tracks"}; Configurable confPairFracSharedTPCclsMax{"confPairFracSharedTPCclsMax", 1.0, "Miax. fraction of TPC shared clusters between two closed tracks"}; Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.80, "Delta Eta cut for Close Pair Rejection"}; + Configurable confRandomizeCPR{"confRandomizeCPR", false, "Fill Deta-Dphi histograms for PlusMinus pair in random order: p1, p2 or p2, p1"}; Configurable confUseCCImCut{"confUseCCImCut", false, "Fill SH within specific quadrants of qout-qside"}; Configurable confMinqTcut{"confMinqTcut", 0.005, "Min. qT cut on filling SH"}; Configurable confUse1stand3rd{"confUse1stand3rd", false, "Use first and third quadrants of qout-qside"}; @@ -134,6 +152,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { Configurable confIsWeight{"confIsWeight", true, "Fill quantum weight"}; Configurable confisIdenPRF{"confisIdenPRF", false, "Choosing identical or non-identical pairs in PRF"}; Configurable confIsfilldEtadPhiTPCcls{"confIsfilldEtadPhiTPCcls", false, "Filling of 3D histogram PairFracSharedTPCcls vs. dEta vs. dPhiStar"}; + Configurable confRandValue{"confRandValue", 0.5, "Value of random-number generator to shuffle pair"}; Configurable> confCPRdeltaEtaCutMaxVec{"confCPRdeltaEtaCutMaxVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for max delta eta cut in CPR"}; Configurable> confCPRdeltaEtaCutMinVec{"confCPRdeltaEtaCutMinVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for min delta eta cut in CPR"}; @@ -147,9 +166,10 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { ConfigurableAxis confDeltaEtaAxis{"confDeltaEtaAxis", {100, -0.15, 0.15}, "DeltaEta"}; ConfigurableAxis confDeltaPhiStarAxis{"confDeltaPhiStarAxis", {100, -0.15, 0.15}, "DeltaPhiStar"}; + Configurable confIsDebug{"confIsDebug", true, "Fill additional histograms"}; } twotracksconfigs; - using FemtoFullParticles = soa::Join; + using FemtoFullParticles = soa::Join; // Filters for selecting particles (both p1 and p2) Filter trackAdditionalfilter = ((nabs(aod::femtouniverseparticle::eta) < twotracksconfigs.confEtaMax) && (aod::track::dcaXY <= twotracksconfigs.confTrkDCAxyMax) && @@ -160,14 +180,13 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { (aod::track::tpcNClsShared <= static_cast(twotracksconfigs.confTrkTPCsClsMax))); using FilteredFemtoFullParticles = soa::Filtered; - // using FilteredFemtoFullParticles = FemtoFullParticles; //if no filtering is applied uncomment this optionconfIsCPRkT - using FemtoRecoParticles = soa::Join; + using FemtoRecoParticles = soa::Join; using FilteredFemtoRecoParticles = soa::Filtered; SliceCache cache; Preslice perCol = aod::femtouniverseparticle::fdCollisionId; - using FemtoTruthParticles = soa::Join; + using FemtoTruthParticles = soa::Join; Preslice perColMCTruth = aod::femtouniverseparticle::fdCollisionId; /// Particle 1 @@ -232,7 +251,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { // Filter trackAdditionalfilter = (nabs(aod::femtouniverseparticle::eta) < twotracksconfigs.confEtaMax); // example filtering on Configurable /// Particle part - ConfigurableAxis confTempFitVarBins{"confDTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; ConfigurableAxis confTempFitVarpTBins{"confTempFitVarpTBins", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot"}; /// Correlation part @@ -245,8 +264,8 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}, true}; - ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}, true}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}}; ConfigurableAxis confkstarBins{"confkstarBins", {60, 0.0, 0.3}, "binning kstar"}; ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; @@ -274,10 +293,10 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { FemtoUniverseSHContainer sameEventContMM; FemtoUniverseSHContainer mixedEventContMM; - FemtoUniverseContainer sameEventCont1D_PP; - FemtoUniverseContainer mixedEventCont1D_PP; - FemtoUniverseContainer sameEventCont1D_MM; - FemtoUniverseContainer mixedEventCont1D_MM; + FemtoUniverseContainer sameEventCont1dpp; + FemtoUniverseContainer mixedEventCont1dpp; + FemtoUniverseContainer sameEventCont1dmm; + FemtoUniverseContainer mixedEventCont1dmm; FemtoUniversePairCleaner pairCleaner; FemtoUniverseDetaDphiStar pairCloseRejection; @@ -302,21 +321,21 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { HistogramRegistry resultRegistryPM{"CorrelationsPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry resultRegistryPP{"CorrelationsPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry resultRegistryMM{"CorrelationsMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry MixQaRegistry{"MixQaRegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry mixQaRegistry{"mixQaRegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry SameMultRegistryPM{"SameMultRegistryPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry MixedMultRegistryPM{"MixedMultRegistryPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry sameMultRegistryPM{"sameMultRegistryPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixedMultRegistryPM{"mixedMultRegistryPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry SameMultRegistryPP{"SameMultRegistryPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry MixedMultRegistryPP{"MixedMultRegistryPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry sameMultRegistryPP{"sameMultRegistryPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixedMultRegistryPP{"mixedMultRegistryPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry SameMultRegistryMM{"SameMultRegistryMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry MixedMultRegistryMM{"MixedMultRegistryMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry sameMultRegistryMM{"sameMultRegistryMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixedMultRegistryMM{"mixedMultRegistryMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; TRandom2* randgen; // PID for protons - bool IsProtonNSigma(float mom, float nsigmaTPCPr, float nsigmaTOFPr) // previous version from: https://github.com/alisw/AliPhysics/blob/master/PWGCF/FEMTOSCOPY/AliFemtoUser/AliFemtoMJTrackCut.cxx + bool isProtonNSigma(float mom, float nsigmaTPCPr, float nsigmaTOFPr) // previous version from: https://github.com/alisw/AliPhysics/blob/master/PWGCF/FEMTOSCOPY/AliFemtoUser/AliFemtoMJTrackCut.cxx { //|nsigma_TPC| < 3 for p < 0.5 GeV/c //|nsigma_combined| < 3 for p > 0.5 @@ -342,36 +361,36 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { return false; } - bool IsKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) + bool isKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) { - if (mom < 0.3) { // 0.0-0.3 - if (std::abs(nsigmaTPCK) < 3.0) { + if (mom < twotracksconfigs.confKaonPIDP1) { // 0.0-0.3 + if (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTPCNsigma1) { return true; } else { return false; } - } else if (mom < 0.45) { // 0.30 - 0.45 - if (std::abs(nsigmaTPCK) < 2.0) { + } else if (mom < twotracksconfigs.confKaonPIDP2) { // 0.30 - 0.45 + if (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTPCNsigma2) { return true; } else { return false; } - } else if (mom < 0.55) { // 0.45-0.55 - if (std::abs(nsigmaTPCK) < 1.0) { + } else if (mom < twotracksconfigs.confKaonPIDP3) { // 0.45-0.55 + if (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTPCNsigma3) { return true; } else { return false; } - } else if (mom < 1.5) { // 0.55-1.5 (now we use TPC and TOF) - if ((std::abs(nsigmaTOFK) < 3.0) && (std::abs(nsigmaTPCK) < 3.0)) { + } else if (mom < twotracksconfigs.confKaonPIDP4) { // 0.55-1.5 (now we use TPC and TOF) + if ((std::abs(nsigmaTOFK) < twotracksconfigs.confKaonTPCNsigma4) && (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTOFNsigma4)) { { return true; } } else { return false; } - } else if (mom > 1.5) { // 1.5 - - if ((std::abs(nsigmaTOFK) < 2.0) && (std::abs(nsigmaTPCK) < 3.0)) { + } else if (mom > twotracksconfigs.confKaonPIDP4) { // 1.5 - + if ((std::abs(nsigmaTOFK) < twotracksconfigs.confKaonTPCNsigma5) && (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTOFNsigma5)) { return true; } else { return false; @@ -381,7 +400,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } } - bool IsPionNSigma(float mom, float nsigmaITSPi, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCElReject) + bool isPionNSigma(float mom, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCElReject) { //|nsigma_TPC| < 3 for p < 0.5 GeV/c //|nsigma_combined| < 3 for p > 0.5 @@ -398,12 +417,6 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } else { return false; } - } else if (twotracksconfigs.confIsAddITSNsigma) { - if (std::hypot(nsigmaITSPi, nsigmaTPCPi) < twotracksconfigs.confNsigmaITSTPCCombined) { - return true; - } else { - return false; - } } else { if ((std::abs(nsigmaTPCPi) < twotracksconfigs.confNsigmaTPC)) { return true; @@ -422,46 +435,46 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { return false; } - bool IsParticleNSigma(int8_t particle_number, float mom, float nsigmaITSPi, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK, float nsigmaTPCElReject) + bool isParticleNSigma(ParticleNumber particle_number, float mom, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK, float nsigmaTPCElReject) { - if (particle_number == 1) { + if (particle_number == ParticleNumber::First) { switch (trackonefilter.confPDGCodePartOne) { case 2212: // Proton case -2212: // Antiproton - return IsProtonNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); + return isProtonNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); break; case 211: // Pion+ case -211: // Pion- - return IsPionNSigma(mom, nsigmaITSPi, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCElReject); + return isPionNSigma(mom, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCElReject); break; case 321: // Kaon+ case -321: // Kaon- - return IsKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); + return isKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); break; default: return false; } return false; - } else if (particle_number == 2) { + } else if (particle_number == ParticleNumber::Second) { switch (tracktwofilter.confPDGCodePartTwo) { case 2212: // Proton case -2212: // Antiproton - return IsProtonNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); + return isProtonNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); break; case 211: // Pion+ case -211: // Pion- - return IsPionNSigma(mom, nsigmaITSPi, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCElReject); + return isPionNSigma(mom, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCElReject); break; case 321: // Kaon+ case -321: // Kaon- - return IsKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); + return isKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); break; default: return false; } return false; } else { - LOGF(fatal, "Wrong number of particle chosen! It should be 1 or 2. It is -> %d", particle_number); + LOGF(fatal, "Wrong number of particle chosen! It should be 1 or 2."); } return false; } @@ -469,16 +482,21 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { void init(InitContext&) { - colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}, true}; - colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}, true}; + colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}}; + colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}}; eventHisto.init(&qaRegistry); - trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true, std::nullopt, twotracksconfigs.confIsAddITSNsigma); + trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true); - trackHistoPartTwo.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, tracktwofilter.confPDGCodePartTwo, true, std::nullopt, twotracksconfigs.confIsAddITSNsigma); + trackHistoPartTwo.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, tracktwofilter.confPDGCodePartTwo, true); - MixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); - MixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + mixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + + if (twotracksconfigs.confIsDebug) { + qaRegistry.add("Tracks_one/hPtpcVsP", ";p GeV/c; (p_{TPC}-p)/p", kTH2F, {{800, 0, 4}, {400, -1, 1}}); + qaRegistry.add("Tracks_two/hPtpcVsP", ";p GeV/c; (p_{TPC}-p)/p", kTH2F, {{800, 0, 4}, {400, -1, 1}}); + } mass1 = pdg->Mass(trackonefilter.confPDGCodePartOne); mass2 = pdg->Mass(tracktwofilter.confPDGCodePartTwo); @@ -490,8 +508,10 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { sameEventCont.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); mixedEventCont.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); } else { - sameEventMultCont.init(&SameMultRegistryPM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); - mixedEventMultCont.init(&MixedMultRegistryPM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + sameEventMultCont.init(&sameMultRegistryPM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + mixedEventMultCont.init(&mixedMultRegistryPM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + sameEventMultCont.setPairMass(mass1, mass2); + mixedEventMultCont.setPairMass(mass1, mass2); } } @@ -502,13 +522,15 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { sameEventContPP.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); mixedEventContPP.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); } else { - sameEventMultContPP.init(&SameMultRegistryPP, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); - mixedEventMultContPP.init(&MixedMultRegistryPP, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); - } - sameEventCont1D_PP.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); - sameEventCont1D_PP.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); - mixedEventCont1D_PP.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); - mixedEventCont1D_PP.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + sameEventMultContPP.init(&sameMultRegistryPP, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + mixedEventMultContPP.init(&mixedMultRegistryPP, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + sameEventMultContPP.setPairMass(mass1, mass2); + mixedEventMultContPP.setPairMass(mass1, mass2); + } + sameEventCont1dpp.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + sameEventCont1dpp.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + mixedEventCont1dpp.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + mixedEventCont1dpp.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); } if (cfgProcessMM) { @@ -518,13 +540,15 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { sameEventContMM.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); mixedEventContMM.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); } else { - sameEventMultContMM.init(&SameMultRegistryMM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); - mixedEventMultContMM.init(&MixedMultRegistryMM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); - } - sameEventCont1D_MM.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); - sameEventCont1D_MM.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); - mixedEventCont1D_MM.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); - mixedEventCont1D_MM.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + sameEventMultContMM.init(&sameMultRegistryMM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + mixedEventMultContMM.init(&mixedMultRegistryMM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + sameEventMultContMM.setPairMass(mass1, mass2); + mixedEventMultContMM.setPairMass(mass1, mass2); + } + sameEventCont1dmm.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + sameEventCont1dmm.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + mixedEventCont1dmm.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + mixedEventCont1dmm.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); } pairCleaner.init(&qaRegistry); @@ -539,12 +563,12 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } template - void fillCollision(CollisionType col, bool IsCent) + void fillCollision(const CollisionType& col, bool IsCent) { if (IsCent) { - MixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinningCent.getBin({col.posZ(), col.multV0M()})); + mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinningCent.getBin({col.posZ(), col.multV0M()})); } else { - MixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinningNtr.getBin({col.posZ(), col.multNtr()})); + mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinningNtr.getBin({col.posZ(), col.multNtr()})); } eventHisto.fillQA(col); } @@ -560,30 +584,30 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, PartType parts, float magFieldTesla, int multCol, PairType contType, bool fillQA) + void doSameEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol, PairType contType, bool fillQA) { /// Histogramming same event if ((contType == PairType::PlusMinus || contType == PairType::PlusPlus) && fillQA) { for (const auto& part : groupPartsOne) { - if (!IsParticleNSigma((int8_t)1, part.p(), trackCuts.getNsigmaITS(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::First, part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Electron))) { continue; } trackHistoPartOne.fillQA(part); - if (twotracksconfigs.confIsAddITSNsigma) { - trackHistoPartOne.fillQAITSPID<>(part); - } trackHistoPartOne.fillQAMisIden(part, trackonefilter.confPDGCodePartOne); + if (twotracksconfigs.confIsDebug) { + qaRegistry.fill(HIST("Tracks_one/hPtpcVsP"), part.p(), (part.tpcInnerParam() - part.p()) / part.p()); + } } } if ((contType == PairType::PlusMinus || contType == PairType::MinusMinus) && fillQA) { for (const auto& part : groupPartsTwo) { - if (!IsParticleNSigma((int8_t)2, part.p(), trackCuts.getNsigmaITS(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::Second, part.p(), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Electron))) { continue; } trackHistoPartTwo.fillQA(part); - if (twotracksconfigs.confIsAddITSNsigma) { - trackHistoPartTwo.fillQAITSPID<>(part); + if (twotracksconfigs.confIsDebug) { + qaRegistry.fill(HIST("Tracks_two/hPtpcVsP"), part.p(), (part.tpcInnerParam() - part.p()) / part.p()); } } } @@ -593,11 +617,11 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { /// Now build the combinations for non-identical particle pairs for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (!IsParticleNSigma((int8_t)1, p1.p(), trackCuts.getNsigmaITS(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::First, p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { continue; } - if (!IsParticleNSigma((int8_t)2, p2.p(), trackCuts.getNsigmaITS(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::Second, p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { continue; } @@ -616,18 +640,31 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { continue; } + auto part1 = p1; + auto part2 = p2; + + if (twotracksconfigs.confRandomizeCPR) { + double rand; + rand = randgen->Rndm(); + + if (rand > twotracksconfigs.confRandValue) { + part1 = p2; + part2 = p1; + } + } + if (twotracksconfigs.confIsCPR.value) { if (twotracksconfigs.confIsCPRatITS && twotracksconfigs.confCPRFracMax.value) { - if (pairCloseRejection.isClosePairAtITS(p1, p2, magFieldTesla, femto_universe_container::EventType::same)) { + if (pairCloseRejection.isClosePairAtITS(part1, part2, magFieldTesla, femto_universe_container::EventType::same)) { continue; } } else { if (twotracksconfigs.confIsCPRkT) { - if (pairCloseRejection.isClosePairkT(p1, p2, femto_universe_container::EventType::same, kT, twotracksconfigs.confIsCircularCut, twotracksconfigs.confCPRDphiAvgOrDist, magFieldTesla, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsfilldEtadPhiTPCcls, pairFractionTPCsCls)) { + if (pairCloseRejection.isClosePairkT(part1, part2, femto_universe_container::EventType::same, kT, twotracksconfigs.confIsCircularCut, twotracksconfigs.confCPRDphiAvgOrDist, magFieldTesla, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsfilldEtadPhiTPCcls, pairFractionTPCsCls)) { continue; } } else { - if (pairCloseRejection.isClosePairFrac(p1, p2, magFieldTesla, femto_universe_container::EventType::same, twotracksconfigs.confCPRDphiAvgOrDist, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsCircularCut)) { + if (pairCloseRejection.isClosePairFrac(part1, part2, magFieldTesla, femto_universe_container::EventType::same, twotracksconfigs.confCPRDphiAvgOrDist, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsCircularCut)) { continue; } } @@ -644,11 +681,11 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } else { for (const auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsOne, groupPartsOne))) { - if (!IsParticleNSigma((int8_t)2, p1.p(), trackCuts.getNsigmaITS(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::Second, p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { continue; } - if (!IsParticleNSigma((int8_t)2, p2.p(), trackCuts.getNsigmaITS(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::Second, p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { continue; } @@ -670,7 +707,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { auto part1 = p1; auto part2 = p2; - if (rand > 0.5) { + if (rand > twotracksconfigs.confRandValue) { part1 = p2; part2 = p1; } @@ -724,7 +761,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { float weight = 1.0f; - sameEventCont1D_PP.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + sameEventCont1dpp.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } if (twotracksconfigs.confIsFillAngqLCMS) { kv = std::sqrt(f3d[1] * f3d[1] + f3d[2] * f3d[2] + f3d[3] * f3d[3]); @@ -750,7 +787,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { float weight = 1.0f; - sameEventCont1D_MM.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + sameEventCont1dmm.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } if (twotracksconfigs.confIsFillAngqLCMS) { kv = std::sqrt(f3d[1] * f3d[1] + f3d[2] * f3d[2] + f3d[3] * f3d[3]); @@ -802,7 +839,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } delete randgen; } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processSameEvent, "Enable processing same event", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processSameEvent, "Enable processing same event", true); /// process function for to call doSameEvent with Monte Carlo /// \param col subscribe to the collision table (Monte Carlo Reconstructed reconstructed) @@ -842,7 +879,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } delete randgen; } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processSameEventMC, "Enable processing same event for Monte Carlo", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processSameEventMC, "Enable processing same event for Monte Carlo", false); /// This function processes the same event and takes care of all the histogramming /// \todo the trivial loops over the tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... @@ -855,7 +892,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { /// @param magFieldTesla magnetic field of the collision /// @param multCol multiplicity of the collision template - void doSameEventMCTruth(PartitionType groupPartsOne, PartitionType groupPartsTwo, int multCol, PairType contType, bool fillQA) + void doSameEventMCTruth(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, int multCol, PairType contType, bool fillQA) { randgen = new TRandom2(0); @@ -915,7 +952,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { auto part1 = p1; auto part2 = p2; - if (rand > 0.5) { + if (rand > twotracksconfigs.confRandValue) { part1 = p2; part2 = p1; } @@ -925,7 +962,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { if (twotracksconfigs.confIs1D) { f3d = FemtoUniverseMath::newpairfunc(part1, mass1, part2, mass2, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); float weight = f3d[5]; - sameEventCont1D_PP.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + sameEventCont1dpp.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } else { sameEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); } @@ -936,7 +973,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { if (twotracksconfigs.confIs1D) { f3d = FemtoUniverseMath::newpairfunc(part1, mass1, part2, mass2, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); float weight = f3d[5]; - sameEventCont1D_MM.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + sameEventCont1dmm.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } else { sameEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); } @@ -995,7 +1032,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } } } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processSameEventMCTruth, "Enable processing same event for MC truth", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processSameEventMCTruth, "Enable processing same event for MC truth", false); /// This function processes the mixed event /// \todo the trivial loops over the collisions and tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... @@ -1008,16 +1045,16 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEvent(PartitionType groupPartsOne, PartitionType groupPartsTwo, float magFieldTesla, int multCol, PairType contType) + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, float magFieldTesla, int multCol, PairType contType) { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (!IsParticleNSigma((int8_t)2, p1.p(), trackCuts.getNsigmaITS(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::Second, p1.p(), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { continue; } - if (!IsParticleNSigma((int8_t)2, p2.p(), trackCuts.getNsigmaITS(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { + if (!isParticleNSigma(ParticleNumber::Second, p2.p(), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { continue; } @@ -1042,9 +1079,11 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { auto part1 = p1; auto part2 = p2; - if (rand > 0.5) { - part1 = p2; - part2 = p1; + if (!PairType::PlusMinus || twotracksconfigs.confRandomizeCPR) { + if (rand > twotracksconfigs.confRandValue) { + part1 = p2; + part2 = p1; + } } if (twotracksconfigs.confIsCPR.value) { @@ -1097,7 +1136,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { float weight = 1.0f; - mixedEventCont1D_PP.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + mixedEventCont1dpp.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } if (twotracksconfigs.confIsFillAngqLCMS) { @@ -1124,7 +1163,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { float weight = 1.0f; - mixedEventCont1D_MM.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + mixedEventCont1dmm.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } if (twotracksconfigs.confIsFillAngqLCMS) { kv = std::sqrt(f3d[1] * f3d[1] + f3d[2] * f3d[2] + f3d[3] * f3d[3]); @@ -1158,7 +1197,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningCent, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multV0M(); - MixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningCent.getBin({collision1.posZ(), multiplicityCol})); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningCent.getBin({collision1.posZ(), multiplicityCol})); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1185,7 +1224,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } delete randgen; } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventCent, "Enable processing mixed events for centrality", true); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventCent, "Enable processing mixed events for centrality", true); /// process function for to call doMixedEvent with Data /// @param cols subscribe to the collisions table (Data) @@ -1206,7 +1245,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningNtr, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multNtr(); - MixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1233,7 +1272,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } delete randgen; } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventNtr, "Enable processing mixed events for centrality", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventNtr, "Enable processing mixed events for centrality", false); /// brief process function for to call doMixedEvent with Monte Carlo /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) @@ -1256,7 +1295,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningCent, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multV0M(); - MixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningCent.getBin({collision1.posZ(), multiplicityCol})); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningCent.getBin({collision1.posZ(), multiplicityCol})); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1285,7 +1324,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } delete randgen; } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventMCCent, "Enable processing mixed events MC", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventMCCent, "Enable processing mixed events MC", false); /// brief process function for to call doMixedEvent with Monte Carlo /// @param cols subscribe to the collisions table (Monte Carlo Reconstructed reconstructed) @@ -1308,7 +1347,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningNtr, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multV0M(); - MixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); @@ -1337,7 +1376,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } delete randgen; } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventMCNtr, "Enable processing mixed events MC", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventMCNtr, "Enable processing mixed events MC", false); /// This function processes the mixed event /// \todo the trivial loops over the collisions and tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... @@ -1350,7 +1389,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { /// \param magFieldTesla magnetic field of the collision /// \param multCol multiplicity of the collision template - void doMixedEventMCTruth(PartitionType groupPartsOne, PartitionType groupPartsTwo, int multCol, PairType contType) + void doMixedEventMCTruth(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, int multCol, PairType contType) { randgen = new TRandom2(0); for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { @@ -1370,7 +1409,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { auto part1 = p1; auto part2 = p2; - if (rand > 0.5) { + if (rand > twotracksconfigs.confRandValue) { part1 = p2; part2 = p1; } @@ -1383,7 +1422,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { case PairType::PlusPlus: { if (twotracksconfigs.confIs1D) { float weight = 1.0; - mixedEventCont1D_PP.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + mixedEventCont1dpp.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } else { mixedEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); } @@ -1393,7 +1432,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { case PairType::MinusMinus: { if (twotracksconfigs.confIs1D) { float weight = 1.0; - mixedEventCont1D_MM.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + mixedEventCont1dmm.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); } else { mixedEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); } @@ -1425,7 +1464,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningNtr, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multNtr(); - MixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); if (cfgProcessPM) { auto groupPartsOne = partsOneMCTruth->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1444,7 +1483,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } } } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventNtrMCTruth, "Enable processing MC Truth mixed events for multiplicity", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventNtrMCTruth, "Enable processing MC Truth mixed events for multiplicity", false); /// process function for to call doMixedEvent with Data /// @param cols subscribe to the collisions table (Data) @@ -1464,7 +1503,7 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningCent, confNEventsMix, -1, cols, cols)) { const int multiplicityCol = collision1.multV0M(); - MixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); if (cfgProcessPM) { auto groupPartsOne = partsOneMCTruth->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1483,13 +1522,13 @@ struct femtoUniversePairTaskTrackTrackSpherHarMultKtExtended { } } } - PROCESS_SWITCH(femtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventCentMCTruth, "Enable processing MC Truth mixed events for multiplicity", false); + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtended, processMixedEventCentMCTruth, "Enable processing MC Truth mixed events for multiplicity", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), }; return workflow; } diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx new file mode 100644 index 00000000000..10ababfb0a5 --- /dev/null +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx @@ -0,0 +1,1066 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file femtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid.cxx +/// \brief Tasks that reads the track tables used for the pairing and builds pairs of two tracks and compute relative pair-momentum in three dimesnions +/// \remark This file is inherited from ~/FemtoUniverse/Tasks/femtoUniversePairTaskTrackTrack3DMultKtExtended.cxx on 17/06/2024 +/// \author Pritam Chakraborty, WUT Warsaw, pritam.chakraborty@pw.edu.pl + +#include "PWGCF/FemtoUniverse/Core/FemtoUniverseContainer.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniverseDetaDphiStar.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniverseEventHisto.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniverseMath.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniversePairCleaner.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniversePairSHCentMultKt.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniverseParticleHisto.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniverseSHContainer.h" +#include "PWGCF/FemtoUniverse/Core/FemtoUniverseTrackSelection.h" +#include "PWGCF/FemtoUniverse/DataModel/FemtoDerived.h" + +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::analysis::femto_universe; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +namespace +{ +static constexpr int NPart = 2; +static constexpr int NCuts = 5; +static const std::vector partNames{"PartOne", "PartTwo"}; +static const std::vector cutNames{"MaxPt", "PIDthr", "nSigmaTPC", "nSigmaTPCTOF", "MaxP"}; +static const float cutsTable[NPart][NCuts]{ + {4.05f, 1.f, 3.f, 3.f, 100.f}, + {4.05f, 1.f, 3.f, 3.f, 100.f}}; +} // namespace + +enum ParticleNumber { + First, + Second +}; + +enum PairType { + PlusMinus, + PlusPlus, + MinusMinus +}; + +struct FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid { + + Service pdg; + + /// Particle selection part + + /// Table for both particles + struct : o2::framework::ConfigurableGroup { + Configurable confNsigmaCombined{"confNsigmaCombined", 3.0f, "TPC and TOF Pion Sigma (combined) for momentum > confTOFPtMin"}; + Configurable confNsigmaTPC{"confNsigmaTPC", 3.0f, "TPC Pion Sigma for momentum < confTOFPtMin"}; + Configurable confNsigmaITSTPCCombined{"confNsigmaITSTPCCombined", 3.0f, "ITS and TPC Pion Sigma (combined) for momentum < confTOFPtMin"}; + Configurable confIsElReject{"confIsElReject", false, "Is electron rejection activated"}; + Configurable confIsAddITSNsigma{"confIsAddITSNsigma", false, "Is ITS Pions nsigma added"}; + Configurable confNsigmaTPCElRejectMin{"confNsigmaTPCElRejectMin", 2.0f, "TPC Electron SigmaMin for momentum < confTOFPtMin"}; + Configurable confNsigmaTPCElRejectMax{"confNsigmaTPCElRejectMax", 2.0f, "TPC Electron SigmaMax for momentum < confTOFPtMin"}; + Configurable confTOFPtMin{"confTOFPtMin", 0.5f, "Min. Pt for which TOF is required for PID."}; + Configurable confEtaMax{"confEtaMax", 0.8f, "Higher limit for |Eta| (the same for both particles)"}; + + Configurable confKaonPIDP1{"confKaonPIDP1", 0.3f, "mom range 1 for kaon PID."}; + Configurable confKaonPIDP2{"confKaonPIDP2", 0.45f, "mom range 2 for kaon PID."}; + Configurable confKaonPIDP3{"confKaonPIDP3", 0.55f, "mom range 3 for kaon PID."}; + Configurable confKaonPIDP4{"confKaonPIDP4", 1.5f, "mom range 4 for kaon PID."}; + + Configurable confKaonTPCNsigma1{"confKaonTPCNsigma1", 3.0f, "TPC Kaon NSigma for mom range 1."}; + Configurable confKaonTPCNsigma2{"confKaonTPCNsigma2", 2.0f, "TPC Kaon NSigma for mom range 2."}; + Configurable confKaonTPCNsigma3{"confKaonTPCNsigma3", 1.0f, "TPC Kaon NSigma for mom range 3."}; + Configurable confKaonTPCNsigma4{"confKaonTPCNsigma4", 3.0f, "TPC Kaon NSigma for mom range 4."}; + Configurable confKaonTPCNsigma5{"confKaonTPCNsigma5", 2.0f, "TPC Kaon NSigma for mom range 5."}; + Configurable confKaonTOFNsigma4{"confKaonTOFNsigma4", 3.0f, "TOF Kaon NSigma for mom range 4."}; + Configurable confKaonTOFNsigma5{"confKaonTOFNsigma5", 3.0f, "TOF Kaon NSigma for mom range 5."}; + + Configurable> confCutTable{"confCutTable", {cutsTable[0], NPart, NCuts, partNames, cutNames}, "Particle selections"}; + Configurable confNspecies{"confNspecies", 2, "Number of particle spieces with PID info"}; + Configurable confIsMC{"confIsMC", false, "Enable additional Histogramms in the case of a MonteCarlo Run"}; + Configurable> confTrkPIDnSigmaMax{"confTrkPIDnSigmaMax", std::vector{4.f, 3.f, 2.f}, "This Configurable needs to be the same as the one used in the producer task"}; + Configurable confUse3D{"confUse3D", false, "Enable three dimensional histogramms (to be used only for analysis with high statistics): k* vs mT vs multiplicity"}; + Configurable confPhiBins{"confPhiBins", 29, "Number of phi bins in deta dphi"}; + Configurable confEtaBins{"confEtaBins", 29, "Number of eta bins in deta dphi"}; + Configurable confIsCPR{"confIsCPR", true, "Close Pair Rejection"}; + Configurable confIsCPRatITS{"confIsCPRatITS", false, "Close Pair check at ITS"}; + Configurable confCPRPlotPerRadii{"confCPRPlotPerRadii", false, "Plot CPR per radii"}; + Configurable confCPRdeltaPhiCutMax{"confCPRdeltaPhiCutMax", 0.0, "Delta Phi max cut for Close Pair Rejection"}; + Configurable confCPRdeltaPhiCutMin{"confCPRdeltaPhiCutMin", 0.0, "Delta Phi min cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMax{"confCPRdeltaEtaCutMax", 0.0, "Delta Eta max cut for Close Pair Rejection"}; + Configurable confCPRdeltaEtaCutMin{"confCPRdeltaEtaCutMin", 0.0, "Delta Eta min cut for Close Pair Rejection"}; + Configurable confIsCPRkT{"confIsCPRkT", true, "kT dependent deltaEta-deltaPhi cut for Close Pair Rejection"}; + Configurable confIsCPRFraction{"confIsCPRFraction", true, "Close Pair Rejection with fraction of merged points"}; + Configurable confTrkDCAxyMax{"confTrkDCAxyMax", 0.2, "Max DCA in xy "}; + Configurable confTrkDCAzMax{"confTrkDCAzMax", 0.2, "Max DCA in z "}; + Configurable confTrkTPCcRowsMin{"confTrkTPCcRowsMin", 80, "Min of TPC crossed rows"}; + Configurable confTrkTPCfClsMin{"confTrkTPCfClsMin", 0.83, "Min. fraction of crossed rows/findable TPC clusters"}; + Configurable confTrkTPCfracsClsMax{"confTrkTPCfracsClsMax", 1.0, "Max of fraction of TPC shared cluster "}; + Configurable confTrkTPCnClsMin{"confTrkTPCnClsMin", 80, "Min number of TPC clusters"}; + Configurable confTrkTPCsClsMax{"confTrkTPCsClsMax", 160, "Max number of TPC shared clusters"}; + Configurable confPairFracSharedTPCclsMin{"confPairFracSharedTPCclsMin", 0.0, "Min. fraction of TPC shared clusters between two closed tracks"}; + Configurable confPairFracSharedTPCclsMax{"confPairFracSharedTPCclsMax", 1.0, "Miax. fraction of TPC shared clusters between two closed tracks"}; + Configurable confCPRChosenRadii{"confCPRChosenRadii", 0.80, "Delta Eta cut for Close Pair Rejection"}; + Configurable confUseCCImCut{"confUseCCImCut", false, "Fill SH within specific quadrants of qout-qside"}; + Configurable confMinqTcut{"confMinqTcut", 0.005, "Min. qT cut on filling SH"}; + Configurable confUse1stand3rd{"confUse1stand3rd", false, "Use first and third quadrants of qout-qside"}; + Configurable confUse2ndand4th{"confUse2ndand4th", false, "Use second and fourth quadrants of qout-qside"}; + Configurable confIsFillAngqLCMS{"confIsFillAngqLCMS", true, "Fill qLCMS vs dEta vs dPhi"}; + Configurable confCPRDistMax{"confCPRDistMax", 0.0, "Max. radial seperation between two closed-pairs"}; + Configurable confCPRFracMax{"confCPRFracMax", 0.0, "Max. allowed fraction bad to all TPC points of radial seperation between two closed-pairs"}; + Configurable confCPRDphiAvgOrDist{"confCPRDphiAvgOrDist", true, "Close Pair Rejection by radial or angular seperation"}; + Configurable confIsCircularCut{"confIsCircularCut", true, "Close Pair Rejection within circular area"}; + Configurable confIs1D{"confIs1D", true, "Filling 1D 2k* dist. in MC truth"}; + Configurable confisIdenLCMS{"confisIdenLCMS", true, "Choosing identical or non-identical pairs in LCMS"}; + Configurable confIsWeight{"confIsWeight", true, "Fill quantum weight"}; + Configurable confisIdenPRF{"confisIdenPRF", false, "Choosing identical or non-identical pairs in PRF"}; + Configurable confIsfilldEtadPhiTPCcls{"confIsfilldEtadPhiTPCcls", false, "Filling of 3D histogram PairFracSharedTPCcls vs. dEta vs. dPhiStar"}; + Configurable confRandValue{"confRandValue", 0.5, "Value of random-number generator to shuffle pair"}; + + Configurable> confCPRdeltaEtaCutMaxVec{"confCPRdeltaEtaCutMaxVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for max delta eta cut in CPR"}; + Configurable> confCPRdeltaEtaCutMinVec{"confCPRdeltaEtaCutMinVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for min delta eta cut in CPR"}; + Configurable> confCPRdeltaPhiCutMaxVec{"confCPRdeltaPhiCutMaxVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for max delta phi cut in CPR"}; + Configurable> confCPRdeltaPhiCutMinVec{"confCPRdeltaPhiCutMinVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for min delta phi cut in CPR"}; + + Configurable> confCPRdeltaEtaCutFractionMaxVec{"confCPRdeltaEtaCutFractionMaxVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for max delta eta cut in CPR Fraction"}; + Configurable> confCPRdeltaEtaCutFractionMinVec{"confCPRdeltaEtaCutFractionMinVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for min delta eta cut in CPR Fraction"}; + Configurable> confCPRdeltaPhiCutFractionMaxVec{"confCPRdeltaPhiCutFractionMaxVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for max delta phi cut in CPR Fraction"}; + Configurable> confCPRdeltaPhiCutFractionMinVec{"confCPRdeltaPhiCutFractionMinVec", std::vector{0.0f, 0.0f, 0.0f, 0.0f}, "Bins for min delta phi cut in CPR Fraction"}; + + ConfigurableAxis confDeltaEtaAxis{"confDeltaEtaAxis", {100, -0.15, 0.15}, "DeltaEta"}; + ConfigurableAxis confDeltaPhiStarAxis{"confDeltaPhiStarAxis", {100, -0.15, 0.15}, "DeltaPhiStar"}; + } twotracksconfigs; + + using FemtoFullParticles = soa::Join; + // Filters for selecting particles (both p1 and p2) + Filter trackAdditionalfilter = ((nabs(aod::femtouniverseparticle::eta) < twotracksconfigs.confEtaMax) && + (aod::track::dcaXY <= twotracksconfigs.confTrkDCAxyMax) && + (aod::track::dcaZ <= twotracksconfigs.confTrkDCAzMax) && + (aod::femtouniverseparticle::tpcNClsCrossedRows >= static_cast(twotracksconfigs.confTrkTPCcRowsMin)) && + (aod::femtouniverseparticle::tpcFractionSharedCls <= twotracksconfigs.confTrkTPCfracsClsMax) && + (aod::femtouniverseparticle::tpcNClsFound >= static_cast(twotracksconfigs.confTrkTPCnClsMin)) && + (aod::track::tpcNClsShared <= static_cast(twotracksconfigs.confTrkTPCsClsMax))); + + using FilteredFemtoFullParticles = soa::Filtered; + + SliceCache cache; + Preslice perCol = aod::femtouniverseparticle::fdCollisionId; + + /// Particle 1 + struct : o2::framework::ConfigurableGroup { + Configurable confPDGCodePartOne{"confPDGCodePartOne", 211, "Particle 1 - PDG code"}; + Configurable confPIDPartOne{"confPIDPartOne", 2, "Particle 1 - Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector>int>> + Configurable confPtLowPart1{"confPtLowPart1", 0.14, "Lower limit for Pt for the first particle"}; + Configurable confPtHighPart1{"confPtHighPart1", 1.5, "Higher limit for Pt for the first particle"}; + Configurable confChargePart1{"confChargePart1", 1, "Particle 1 sign"}; + } trackonefilter; + + /// Partition for particle 1 + Partition partsOne = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && aod::femtouniverseparticle::sign == as(trackonefilter.confChargePart1) && aod::femtouniverseparticle::pt < trackonefilter.confPtHighPart1 && aod::femtouniverseparticle::pt > trackonefilter.confPtLowPart1; + + /// Histogramming for particle 1 + FemtoUniverseParticleHisto trackHistoPartOne; + + /// Particle 2 + struct : o2::framework::ConfigurableGroup { + Configurable confPDGCodePartTwo{"confPDGCodePartTwo", 211, "Particle 2 - PDG code"}; + // Configurable confCutPartTwo{"confCutPartTwo", 5542474, "Particle 2 - Selection bit"}; + Configurable confPIDPartTwo{"confPIDPartTwo", 2, "Particle 2 - Read from cutCulator"}; // we also need the possibility to specify whether the bit is true/false ->std>>vector> + + Configurable confPtLowPart2{"confPtLowPart2", 0.14, "Lower limit for Pt for the second particle"}; + Configurable confPtHighPart2{"confPtHighPart2", 1.5, "Higher limit for Pt for the second particle"}; + Configurable confChargePart2{"confChargePart2", -1, "Particle 2 sign"}; + } tracktwofilter; + + /// Partition for particle 2 + Partition partsTwo = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == as(tracktwofilter.confChargePart2)) && aod::femtouniverseparticle::pt < tracktwofilter.confPtHighPart2 && aod::femtouniverseparticle::pt > tracktwofilter.confPtLowPart2; + + /// Histogramming for particle 2 + FemtoUniverseParticleHisto trackHistoPartTwo; + + /// Histogramming for Event + FemtoUniverseEventHisto eventHisto; + + /// The Configurables need to be passed to an std::vector + int vPIDPartOne, vPIDPartTwo; + std::vector kNsigma; + + /// Event part + Configurable confV0MLow{"confV0MLow", 0.0, "Lower limit for V0M multiplicity"}; + Configurable confV0MHigh{"confV0MHigh", 25000.0, "Upper limit for V0M multiplicity"}; + Configurable confTPCOccupancyLow{"confTPCOccupancyLow", 0, "Lower limit for TPC occupancy"}; + Configurable confTPCOccupancyHigh{"confTPCOccupancyHigh", 500, "Higher limit for TPC occupancy"}; + Configurable confIntRateLow{"confIntRateLow", 0.0, "Lower limit for interaction rate"}; + Configurable confIntRateHigh{"confIntRateHigh", 10000.0, "Higher limit for interaction rate"}; + Configurable confIsCent{"confIsCent", true, "Condition to choose centrality of multiplicity for mixing"}; + + Filter collfilterFDtable = (o2::aod::femtouniversecollision::multV0M > confV0MLow) && (o2::aod::femtouniversecollision::multV0M < confV0MHigh); + Filter collfilterFDExttable = (o2::aod::femtouniversecollision::interactionRate > confIntRateLow) && (o2::aod::femtouniversecollision::interactionRate < confIntRateHigh) && + (o2::aod::femtouniversecollision::occupancy >= confTPCOccupancyLow) && (o2::aod::femtouniversecollision::occupancy < confTPCOccupancyHigh); + using FilteredFDCollisions = soa::Filtered>; + using FilteredFDCollision = FilteredFDCollisions::iterator; + // Filter trackAdditionalfilter = (nabs(aod::femtouniverseparticle::eta) < twotracksconfigs.confEtaMax); // example filtering on Configurable + + /// Particle part + ConfigurableAxis confTempFitVarBins{"confTempFitVarBins", {300, -0.15, 0.15}, "binning of the TempFitVar in the pT vs. TempFitVar plot"}; + ConfigurableAxis confTempFitVarpTBins{"confTempFitVarpTBins", {20, 0.5, 4.05}, "pT binning of the pT vs. TempFitVar plot"}; + + /// Correlation part + ConfigurableAxis confMultBinsCent{"confMultBinsCent", {VARIABLE_WIDTH, 0.0f, 2.5f, 5.0f, 7.5f, 10.0f, 12.5f, 15.0f, 17.5f, 20.0f, 22.5f, 25.0f, 27.5f, 30.0f, 32.5f, 35.0f, 37.5f, 40.0f, 42.5f, 45.0f, 47.5f, 50.0f, 52.5f, 55.0f, 57.5f, 60.0f, 62.5f, 65.0f, 67.5f, 70.5f, 75.0f, 80.0f, 90.0f, 100.0f, 99999.f}, "Mixing bins - centrality"}; + ConfigurableAxis confMultBinsMult{"confMultBinsMult", {VARIABLE_WIDTH, 0.0f, 400.0f, 800.0f, 1200.0f, 1600.0f, 2000.0f, 2500.0f, 3000.0f, 3500.0f, 4000.0f, 4500.0f, 5000.0f, 6000.0f, 7000.0f, 8000.0f, 9000.0f, 10000.0f, 11000.0f, 12000.0f, 13000.0f, 14000.0f, 15000.0f, 16000.0f, 17000.0f, 18000.0f, 99999.f}, "Mixing bins - centrality"}; + ConfigurableAxis confMultKstarBins{"confMultKstarBins", {VARIABLE_WIDTH, 0.0f, 200.0f}, "Bins for kstar analysis in multiplicity or centrality bins (10 is maximum)"}; + ConfigurableAxis confKtKstarBins{"confKtKstarBins", {VARIABLE_WIDTH, 0.1f, 0.2f, 0.3f, 0.4f}, "Bins for kstar analysis in kT bins"}; + ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + + ConfigurableAxis confmTBins3D{"confmTBins3D", {VARIABLE_WIDTH, 1.02f, 1.14f, 1.20f, 1.26f, 1.38f, 1.56f, 1.86f, 4.50f}, "mT Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + ConfigurableAxis confmultBins3D{"confmultBins3D", {VARIABLE_WIDTH, 0.0f, 20.0f, 30.0f, 40.0f, 99999.0f}, "multiplicity Binning for the 3Dimensional plot: k* vs multiplicity vs mT (set <> to true in order to use)"}; + + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBinsCent}}; + ColumnBinningPolicy colBinningNtr{{confVtxBins, confMultBinsMult}}; + + ConfigurableAxis confkstarBins{"confkstarBins", {60, 0.0, 0.3}, "binning kstar"}; + ConfigurableAxis confkTBins{"confkTBins", {150, 0., 9.}, "binning kT"}; + ConfigurableAxis confmTBins{"confmTBins", {225, 0., 7.5}, "binning mT"}; + Configurable confIsLCMS{"confIsLCMS", true, "Choosing LCMS or PRF"}; + Configurable confNEventsMix{"confNEventsMix", 5, "Number of events for mixing"}; + Configurable confLMax{"confLMax", 2, "Maximum value of l"}; + Configurable cfgProcessPM{"cfgProcessPM", false, "Process particles of the opposite charge"}; + Configurable cfgProcessPP{"cfgProcessPP", true, "Process particles of the same, positice charge"}; + Configurable cfgProcessMM{"cfgProcessMM", true, "Process particles of the same, positice charge"}; + Configurable cfgProcessMultBins{"cfgProcessMultBins", true, "Process kstar histograms in multiplicity bins (in multiplicity bins)"}; + Configurable cfgProcessKtBins{"cfgProcessKtBins", true, "Process kstar histograms in kT bins (if cfgProcessMultBins is set false, this will not be processed regardless this Configurable state)"}; + Configurable cfgProcessKtMt3DCF{"cfgProcessKtMt3DCF", false, "Process 3D histograms in kT and Mult bins"}; + + Configurable confRejectGammaPair{"confRejectGammaPair", false, "Additional check to reject e+e- pairs base on theta and minv"}; + Configurable confMaxEEMinv{"confMaxEEMinv", 0.002, "Max. minv of e-e+ pair for gamma pair rejection"}; + Configurable confMaxDTheta{"confMaxDTheta", 0.008, "Max. DeltaTheta of pair for gamma pair rejection"}; + + FemtoUniverseSHContainer sameEventCont; + FemtoUniverseSHContainer mixedEventCont; + + FemtoUniverseSHContainer sameEventContPP; + FemtoUniverseSHContainer mixedEventContPP; + + FemtoUniverseSHContainer sameEventContMM; + FemtoUniverseSHContainer mixedEventContMM; + + FemtoUniverseContainer sameEventCont1dpp; + FemtoUniverseContainer mixedEventCont1dpp; + FemtoUniverseContainer sameEventCont1dmm; + FemtoUniverseContainer mixedEventCont1dmm; + + FemtoUniversePairCleaner pairCleaner; + FemtoUniverseDetaDphiStar pairCloseRejection; + FemtoUniverseTrackSelection trackCuts; + + PairSHCentMultKt sameEventMultCont; + PairSHCentMultKt mixedEventMultCont; + + PairSHCentMultKt sameEventMultContPP; + PairSHCentMultKt mixedEventMultContPP; + + PairSHCentMultKt sameEventMultContMM; + PairSHCentMultKt mixedEventMultContMM; + + float mass1 = -1; + float mass2 = -1; + + /// Histogram output + HistogramRegistry qaRegistry{"TrackQA", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry resultRegistry{"Correlations", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry resultRegistry1D{"Correlations1D", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry resultRegistryPM{"CorrelationsPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry resultRegistryPP{"CorrelationsPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry resultRegistryMM{"CorrelationsMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixQaRegistry{"mixQaRegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + HistogramRegistry sameMultRegistryPM{"sameMultRegistryPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixedMultRegistryPM{"mixedMultRegistryPM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + HistogramRegistry sameMultRegistryPP{"sameMultRegistryPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixedMultRegistryPP{"mixedMultRegistryPP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + HistogramRegistry sameMultRegistryMM{"sameMultRegistryMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixedMultRegistryMM{"mixedMultRegistryMM", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + TRandom2* randgen; + + // PID for protons + bool isProtonNSigma(float mom, float nsigmaTPCPr, float nsigmaTOFPr) // previous version from: https://github.com/alisw/AliPhysics/blob/master/PWGCF/FEMTOSCOPY/AliFemtoUser/AliFemtoMJTrackCut.cxx + { + //|nsigma_TPC| < 3 for p < 0.5 GeV/c + //|nsigma_combined| < 3 for p > 0.5 + + // using Configurables: + // confTOFPtMin - momentum value when we start using TOF; set to 1000 if TOF not needed + // confNsigmaTPC -> TPC Sigma for momentum < 0.5 + // confNsigmaCombined -> TPC and TOF Sigma (combined) for momentum > 0.5 + + if (mom < twotracksconfigs.confTOFPtMin) { + if (std::abs(nsigmaTPCPr) < twotracksconfigs.confNsigmaTPC) { + return true; + } else { + return false; + } + } else { + if (std::hypot(nsigmaTOFPr, nsigmaTPCPr) < twotracksconfigs.confNsigmaCombined) { + return true; + } else { + return false; + } + } + return false; + } + + bool isKaonNSigma(float mom, float nsigmaTPCK, float nsigmaTOFK) + { + if (mom < twotracksconfigs.confKaonPIDP1) { // 0.0-0.3 + if (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTPCNsigma1) { + return true; + } else { + return false; + } + } else if (mom < twotracksconfigs.confKaonPIDP2) { // 0.30 - 0.45 + if (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTPCNsigma2) { + return true; + } else { + return false; + } + } else if (mom < twotracksconfigs.confKaonPIDP3) { // 0.45-0.55 + if (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTPCNsigma3) { + return true; + } else { + return false; + } + } else if (mom < twotracksconfigs.confKaonPIDP4) { // 0.55-1.5 (now we use TPC and TOF) + if ((std::abs(nsigmaTOFK) < twotracksconfigs.confKaonTPCNsigma4) && (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTOFNsigma4)) { + { + return true; + } + } else { + return false; + } + } else if (mom > twotracksconfigs.confKaonPIDP4) { // 1.5 - + if ((std::abs(nsigmaTOFK) < twotracksconfigs.confKaonTPCNsigma5) && (std::abs(nsigmaTPCK) < twotracksconfigs.confKaonTOFNsigma5)) { + return true; + } else { + return false; + } + } else { + return false; + } + } + + bool isPionNSigma(float mom, float nsigmaITSPi, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCElReject) + { + //|nsigma_TPC| < 3 for p < 0.5 GeV/c + //|nsigma_combined| < 3 for p > 0.5 + + // using Configurables: + // confTOFPtMin - momentum value when we start using TOF; set to 1000 if TOF not needed + // confNsigmaTPC -> TPC Sigma for momentum < 0.5 + // confNsigmaCombined -> TPC and TOF Pion Sigma (combined) for momentum > 0.5 + if (true) { + if (mom < twotracksconfigs.confTOFPtMin) { + if (twotracksconfigs.confIsElReject) { + if ((std::abs(nsigmaTPCPi) < twotracksconfigs.confNsigmaTPC) && (nsigmaTPCElReject < twotracksconfigs.confNsigmaTPCElRejectMin || nsigmaTPCElReject > twotracksconfigs.confNsigmaTPCElRejectMax)) { + return true; + } else { + return false; + } + } else if (twotracksconfigs.confIsAddITSNsigma) { + if (std::hypot(nsigmaITSPi, nsigmaTPCPi) < twotracksconfigs.confNsigmaITSTPCCombined) { + return true; + } else { + return false; + } + } else { + if ((std::abs(nsigmaTPCPi) < twotracksconfigs.confNsigmaTPC)) { + return true; + } else { + return false; + } + } + } else { + if (std::hypot(nsigmaTOFPi, nsigmaTPCPi) < twotracksconfigs.confNsigmaCombined) { + return true; + } else { + return false; + } + } + } + return false; + } + + bool isParticleNSigma(ParticleNumber particle_number, float mom, float nsigmaITSPi, float nsigmaTPCPr, float nsigmaTOFPr, float nsigmaTPCPi, float nsigmaTOFPi, float nsigmaTPCK, float nsigmaTOFK, float nsigmaTPCElReject) + { + if (particle_number == ParticleNumber::First) { + switch (trackonefilter.confPDGCodePartOne) { + case 2212: // Proton + case -2212: // Antiproton + return isProtonNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); + break; + case 211: // Pion+ + case -211: // Pion- + return isPionNSigma(mom, nsigmaITSPi, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCElReject); + break; + case 321: // Kaon+ + case -321: // Kaon- + return isKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); + break; + default: + return false; + } + return false; + } else if (particle_number == ParticleNumber::Second) { + switch (tracktwofilter.confPDGCodePartTwo) { + case 2212: // Proton + case -2212: // Antiproton + return isProtonNSigma(mom, nsigmaTPCPr, nsigmaTOFPr); + break; + case 211: // Pion+ + case -211: // Pion- + return isPionNSigma(mom, nsigmaITSPi, nsigmaTPCPi, nsigmaTOFPi, nsigmaTPCElReject); + break; + case 321: // Kaon+ + case -321: // Kaon- + return isKaonNSigma(mom, nsigmaTPCK, nsigmaTOFK); + break; + default: + return false; + } + return false; + } else { + LOGF(fatal, "Wrong number of particle chosen! It should be First or Second."); + } + return false; + } + + void init(InitContext&) + { + + colBinningCent = ColumnBinningPolicy{{confVtxBins, confMultBinsCent}}; + colBinningNtr = ColumnBinningPolicy{{confVtxBins, confMultBinsMult}}; + + eventHisto.init(&qaRegistry); + trackHistoPartOne.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, trackonefilter.confPDGCodePartOne, true, std::nullopt, twotracksconfigs.confIsAddITSNsigma); + + trackHistoPartTwo.init(&qaRegistry, confTempFitVarpTBins, confTempFitVarBins, twotracksconfigs.confIsMC, tracktwofilter.confPDGCodePartTwo, true, std::nullopt, twotracksconfigs.confIsAddITSNsigma); + + mixQaRegistry.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + mixQaRegistry.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{120, -0.5, 119.5}}); + + mass1 = pdg->Mass(trackonefilter.confPDGCodePartOne); + mass2 = pdg->Mass(tracktwofilter.confPDGCodePartTwo); + + if (cfgProcessPM) { + if (!cfgProcessKtMt3DCF) { + sameEventCont.init(&resultRegistryPM, confkstarBins, confLMax); + mixedEventCont.init(&resultRegistryPM, confkstarBins, confLMax); + sameEventCont.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + mixedEventCont.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + } else { + sameEventMultCont.init(&sameMultRegistryPM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + mixedEventMultCont.init(&mixedMultRegistryPM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + sameEventMultCont.setPairMass(mass1, mass2); + mixedEventMultCont.setPairMass(mass1, mass2); + } + } + + if (cfgProcessPP) { + if (!cfgProcessKtMt3DCF) { + sameEventContPP.init(&resultRegistryPP, confkstarBins, confLMax); + mixedEventContPP.init(&resultRegistryPP, confkstarBins, confLMax); + sameEventContPP.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + mixedEventContPP.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + } else { + sameEventMultContPP.init(&sameMultRegistryPP, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + mixedEventMultContPP.init(&mixedMultRegistryPP, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + sameEventMultContPP.setPairMass(mass1, mass2); + mixedEventMultContPP.setPairMass(mass1, mass2); + } + sameEventCont1dpp.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + sameEventCont1dpp.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + mixedEventCont1dpp.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + mixedEventCont1dpp.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + } + + if (cfgProcessMM) { + if (!cfgProcessKtMt3DCF) { + sameEventContMM.init(&resultRegistryMM, confkstarBins, confLMax); + mixedEventContMM.init(&resultRegistryMM, confkstarBins, confLMax); + sameEventContMM.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + mixedEventContMM.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + } else { + sameEventMultContMM.init(&sameMultRegistryMM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + mixedEventMultContMM.init(&mixedMultRegistryMM, confkstarBins, confMultKstarBins, confKtKstarBins, confLMax, twotracksconfigs.confIs1D); + sameEventMultContMM.setPairMass(mass1, mass2); + mixedEventMultContMM.setPairMass(mass1, mass2); + } + sameEventCont1dmm.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + sameEventCont1dmm.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + mixedEventCont1dmm.init(&resultRegistry1D, confkstarBins, confMultBinsCent, confkTBins, confmTBins, confmultBins3D, confmTBins3D, twotracksconfigs.confEtaBins, twotracksconfigs.confPhiBins, twotracksconfigs.confIsMC, twotracksconfigs.confUse3D); + mixedEventCont1dmm.setPDGCodes(trackonefilter.confPDGCodePartOne, tracktwofilter.confPDGCodePartTwo); + } + + pairCleaner.init(&qaRegistry); + if (twotracksconfigs.confIsCPR.value) { + pairCloseRejection.init(&resultRegistry, &qaRegistry, twotracksconfigs.confDeltaEtaAxis, twotracksconfigs.confDeltaPhiStarAxis, twotracksconfigs.confCPRdeltaPhiCutMin.value, twotracksconfigs.confCPRdeltaPhiCutMax.value, twotracksconfigs.confCPRdeltaEtaCutMin.value, twotracksconfigs.confCPRdeltaEtaCutMax.value, twotracksconfigs.confCPRChosenRadii.value, twotracksconfigs.confCPRPlotPerRadii.value); + pairCloseRejection.init_kT(&resultRegistry, confKtKstarBins, twotracksconfigs.confDeltaEtaAxis, twotracksconfigs.confDeltaPhiStarAxis, twotracksconfigs.confCPRdeltaPhiCutMinVec.value, twotracksconfigs.confCPRdeltaPhiCutMaxVec.value, twotracksconfigs.confCPRdeltaEtaCutMinVec.value, twotracksconfigs.confCPRdeltaEtaCutMaxVec.value, twotracksconfigs.confCPRdeltaPhiCutFractionMinVec.value, twotracksconfigs.confCPRdeltaPhiCutFractionMaxVec.value, twotracksconfigs.confCPRdeltaEtaCutFractionMinVec.value, twotracksconfigs.confCPRdeltaEtaCutFractionMaxVec.value, twotracksconfigs.confIsfilldEtadPhiTPCcls); + } + + vPIDPartOne = trackonefilter.confPIDPartOne.value; + vPIDPartTwo = tracktwofilter.confPIDPartTwo.value; + kNsigma = twotracksconfigs.confTrkPIDnSigmaMax.value; + } + + template + void fillCollision(const CollisionType& col, bool IsCent) + { + if (IsCent) { + mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinningCent.getBin({col.posZ(), col.multV0M()})); + } else { + mixQaRegistry.fill(HIST("MixingQA/hSECollisionBins"), colBinningNtr.getBin({col.posZ(), col.multNtr()})); + } + eventHisto.fillQA(col); + } + + /// This function processes the same event and takes care of all the histogramming + /// \todo the trivial loops over the tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... + /// @tparam PartitionType + /// @tparam PartType + /// @tparam isMC: enables Monte Carlo truth specific histograms + /// @param groupPartsOne partition for the first particle passed by the process function + /// @param groupPartsTwo partition for the second particle passed by the process function + /// @param parts femtoUniverseParticles table (in case of Monte Carlo joined with FemtoUniverseMCLabels) + /// @param magFieldTesla magnetic field of the collision + /// @param multCol multiplicity of the collision + template + void doSameEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, const PartType& parts, float magFieldTesla, int multCol, PairType contType, bool fillQA) + { + /// Histogramming same event + if ((contType == PairType::PlusMinus || contType == PairType::PlusPlus) && fillQA) { + for (const auto& part : groupPartsOne) { + if (!isParticleNSigma(ParticleNumber::First, part.p(), trackCuts.getNsigmaITS(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Electron))) { + continue; + } + trackHistoPartOne.fillQA(part); + if (twotracksconfigs.confIsAddITSNsigma) { + trackHistoPartOne.fillQAITSPID<>(part); + } + trackHistoPartOne.fillQAMisIden(part, trackonefilter.confPDGCodePartOne); + } + } + + if ((contType == PairType::PlusMinus || contType == PairType::MinusMinus) && fillQA) { + for (const auto& part : groupPartsTwo) { + if (!isParticleNSigma(ParticleNumber::Second, part.p(), trackCuts.getNsigmaITS(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Proton), trackCuts.getNsigmaTOF(part, o2::track::PID::Proton), trackCuts.getNsigmaTPC(part, o2::track::PID::Pion), trackCuts.getNsigmaTOF(part, o2::track::PID::Pion), trackCuts.getNsigmaTPC(part, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(part, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(part, o2::track::PID::Electron))) { + continue; + } + trackHistoPartTwo.fillQA(part); + if (twotracksconfigs.confIsAddITSNsigma) { + trackHistoPartTwo.fillQAITSPID<>(part); + } + } + } + + if (contType == PairType::PlusMinus) { + + /// Now build the combinations for non-identical particle pairs + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + + if (!isParticleNSigma(ParticleNumber::First, p1.p(), trackCuts.getNsigmaITS(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { + continue; + } + + if (!isParticleNSigma(ParticleNumber::Second, p2.p(), trackCuts.getNsigmaITS(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { + continue; + } + + if (confRejectGammaPair && pairCloseRejection.isGammaPair(p1, p2, confMaxEEMinv, confMaxDTheta)) { + continue; + } + + float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); + float lastElement = confKtKstarBins.value.back(); + float firstRealElement = confKtKstarBins.value[1]; + + if (kT < firstRealElement || kT > lastElement) + continue; + float pairFractionTPCsCls = static_cast((p1.tpcNClsShared() + p2.tpcNClsShared())) / static_cast((p1.tpcNClsFound() + p2.tpcNClsFound())); + if (pairFractionTPCsCls < twotracksconfigs.confPairFracSharedTPCclsMin.value || pairFractionTPCsCls >= twotracksconfigs.confPairFracSharedTPCclsMax.value) { + continue; + } + + if (twotracksconfigs.confIsCPR.value) { + if (twotracksconfigs.confIsCPRatITS && twotracksconfigs.confCPRFracMax.value) { + if (pairCloseRejection.isClosePairAtITS(p1, p2, magFieldTesla, femto_universe_container::EventType::same)) { + continue; + } + } else { + if (twotracksconfigs.confIsCPRkT) { + if (pairCloseRejection.isClosePairkT(p1, p2, femto_universe_container::EventType::same, kT, twotracksconfigs.confIsCircularCut, twotracksconfigs.confCPRDphiAvgOrDist, magFieldTesla, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsfilldEtadPhiTPCcls, pairFractionTPCsCls)) { + continue; + } + } else { + if (pairCloseRejection.isClosePairFrac(p1, p2, magFieldTesla, femto_universe_container::EventType::same, twotracksconfigs.confCPRDphiAvgOrDist, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsCircularCut)) { + continue; + } + } + } + } + + // track cleaning + if (!pairCleaner.isCleanPair(p1, p2, parts)) { + continue; + } + + sameEventMultCont.fillMultNumDen(p1, p2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } else { + for (const auto& [p1, p2] : combinations(CombinationsStrictlyUpperIndexPolicy(groupPartsOne, groupPartsOne))) { + + if (!isParticleNSigma(ParticleNumber::Second, p1.p(), trackCuts.getNsigmaITS(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { + continue; + } + + if (!isParticleNSigma(ParticleNumber::Second, p2.p(), trackCuts.getNsigmaITS(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { + continue; + } + + float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); + float lastElement = confKtKstarBins.value.back(); + float firstRealElement = confKtKstarBins.value[1]; + + if (kT < firstRealElement || kT > lastElement) + continue; + + float pairFractionTPCsCls = static_cast((p1.tpcNClsShared() + p2.tpcNClsShared())) / static_cast((p1.tpcNClsFound() + p2.tpcNClsFound())); + if (pairFractionTPCsCls < twotracksconfigs.confPairFracSharedTPCclsMin.value || pairFractionTPCsCls >= twotracksconfigs.confPairFracSharedTPCclsMax.value) { + continue; + } + + double rand; + rand = randgen->Rndm(); + + auto part1 = p1; + auto part2 = p2; + + if (rand > twotracksconfigs.confRandValue) { + part1 = p2; + part2 = p1; + } + + if (twotracksconfigs.confIsCPR.value) { + if (twotracksconfigs.confIsCPRatITS && twotracksconfigs.confCPRFracMax.value) { + if (pairCloseRejection.isClosePairAtITS(part1, part2, magFieldTesla, femto_universe_container::EventType::same)) { + continue; + } + } else { + if (twotracksconfigs.confIsCPRkT) { + if (pairCloseRejection.isClosePairkT(part1, part2, femto_universe_container::EventType::same, kT, twotracksconfigs.confIsCircularCut, twotracksconfigs.confCPRDphiAvgOrDist, magFieldTesla, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsfilldEtadPhiTPCcls, pairFractionTPCsCls)) { + continue; + } + if (twotracksconfigs.confIsCPRFraction) { + if (pairCloseRejection.isClosePairFractionkT(part1, part2, femto_universe_container::EventType::same, kT, magFieldTesla, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax)) { + continue; + } + } + } else { + if (pairCloseRejection.isClosePairFrac(part1, part2, magFieldTesla, femto_universe_container::EventType::same, twotracksconfigs.confCPRDphiAvgOrDist, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsCircularCut)) { + continue; + } + } + } + } + + // track cleaning + if (!pairCleaner.isCleanPair(part1, part2, parts)) { + continue; + } + + std::vector f3d; + double kv; + float outsideref = 0.0; + switch (contType) { + case PairType::PlusPlus: { + f3d = FemtoUniverseMath::newpairfunc(part1, mass1, part2, mass2, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + if (!twotracksconfigs.confUseCCImCut) { + sameEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } else { + if (twotracksconfigs.confUse1stand3rd) { + if ((f3d[1] >= outsideref && f3d[2] >= outsideref) || (f3d[1] < outsideref && f3d[2] < outsideref)) { + sameEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } else if (twotracksconfigs.confUse2ndand4th) { + if ((f3d[1] < outsideref && f3d[2] >= outsideref) || (f3d[1] >= outsideref && f3d[2] < outsideref)) { + sameEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } + } + if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { + float weight = 1.0f; + sameEventCont1dpp.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + } + if (twotracksconfigs.confIsFillAngqLCMS) { + kv = std::sqrt(f3d[1] * f3d[1] + f3d[2] * f3d[2] + f3d[3] * f3d[3]); + pairCloseRejection.ClosePairqLCMS(part1, part2, magFieldTesla, femto_universe_container::EventType::same, kv); + } + break; + } + + case PairType::MinusMinus: { + f3d = FemtoUniverseMath::newpairfunc(part1, mass1, part2, mass2, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + if (!twotracksconfigs.confUseCCImCut) { + sameEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } else { + if (twotracksconfigs.confUse1stand3rd) { + if ((f3d[1] >= outsideref && f3d[2] >= outsideref) || (f3d[1] < outsideref && f3d[2] < outsideref)) { + sameEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } else if (twotracksconfigs.confUse2ndand4th) { + if ((f3d[1] < outsideref && f3d[2] >= outsideref) || (f3d[1] >= outsideref && f3d[2] < outsideref)) { + sameEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::same, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } + } + if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { + float weight = 1.0f; + sameEventCont1dmm.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + } + if (twotracksconfigs.confIsFillAngqLCMS) { + kv = std::sqrt(f3d[1] * f3d[1] + f3d[2] * f3d[2] + f3d[3] * f3d[3]); + pairCloseRejection.ClosePairqLCMS(part1, part2, magFieldTesla, femto_universe_container::EventType::same, kv); + } + break; + } + default: + break; + } + } + } + } + + /// process function for to call doSameEvent with Data + /// \param col subscribe to the collision table (Data) + /// \param parts subscribe to the femtoUniverseParticleTable + void processSameEvent(FilteredFDCollision const& col, + FilteredFemtoFullParticles const& parts) + { + fillCollision(col, confIsCent); + + auto thegroupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto thegroupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + bool fillQA = true; + randgen = new TRandom2(0); + + if (confIsCent) { + if (cfgProcessPM) { + doSameEvent(thegroupPartsOne, thegroupPartsTwo, parts, col.magField(), col.multV0M(), PairType::PlusMinus, fillQA); + } + if (cfgProcessPP) { + doSameEvent(thegroupPartsOne, thegroupPartsOne, parts, col.magField(), col.multV0M(), PairType::PlusPlus, fillQA); + } + if (cfgProcessMM) { + doSameEvent(thegroupPartsTwo, thegroupPartsTwo, parts, col.magField(), col.multV0M(), PairType::MinusMinus, fillQA); + } + } else { + if (cfgProcessPM) { + doSameEvent(thegroupPartsOne, thegroupPartsTwo, parts, col.magField(), col.multNtr(), PairType::PlusMinus, fillQA); + } + if (cfgProcessPP) { + doSameEvent(thegroupPartsOne, thegroupPartsOne, parts, col.magField(), col.multNtr(), PairType::PlusPlus, fillQA); + } + if (cfgProcessMM) { + doSameEvent(thegroupPartsTwo, thegroupPartsTwo, parts, col.magField(), col.multNtr(), PairType::MinusMinus, fillQA); + } + } + delete randgen; + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid, processSameEvent, "Enable processing same event", true); + + /// This function processes the mixed event + /// \todo the trivial loops over the collisions and tracks should be factored out since they will be common to all combinations of T-T, T-V0, V0-V0, ... + /// \tparam PartitionType + /// \tparam PartType + /// \tparam isMC: enables Monte Carlo truth specific histograms + /// \param groupPartsOne partition for the first particle passed by the process function + /// \param groupPartsTwo partition for the second particle passed by the process function + /// \param parts femtoUniverseParticles table (in case of Monte Carlo joined with FemtoUniverseMCLabels) + /// \param magFieldTesla magnetic field of the collision + /// \param multCol multiplicity of the collision + template + void doMixedEvent(const PartitionType& groupPartsOne, const PartitionType& groupPartsTwo, float magFieldTesla, int multCol, PairType contType) + { + + for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + + if (!isParticleNSigma(ParticleNumber::Second, p1.p(), trackCuts.getNsigmaITS(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p1, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p1, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p1, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p1, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p1, o2::track::PID::Electron))) { + continue; + } + + if (!isParticleNSigma(ParticleNumber::Second, p2.p(), trackCuts.getNsigmaITS(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Proton), trackCuts.getNsigmaTOF(p2, o2::track::PID::Proton), trackCuts.getNsigmaTPC(p2, o2::track::PID::Pion), trackCuts.getNsigmaTOF(p2, o2::track::PID::Pion), trackCuts.getNsigmaTPC(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTOF(p2, o2::track::PID::Kaon), trackCuts.getNsigmaTPC(p2, o2::track::PID::Electron))) { + continue; + } + + if (confRejectGammaPair && pairCloseRejection.isGammaPair(p1, p2, confMaxEEMinv, confMaxDTheta)) { + continue; + } + + float kT = FemtoUniverseMath::getkT(p1, mass1, p2, mass2); + float lastElement = confKtKstarBins.value.back(); + float firstRealElement = confKtKstarBins.value[1]; + + if (kT < firstRealElement || kT > lastElement) + continue; + + float pairFractionTPCsCls = static_cast((p1.tpcNClsShared() + p2.tpcNClsShared())) / static_cast((p1.tpcNClsFound() + p2.tpcNClsFound())); + if (pairFractionTPCsCls < twotracksconfigs.confPairFracSharedTPCclsMin.value || pairFractionTPCsCls >= twotracksconfigs.confPairFracSharedTPCclsMax.value) { + continue; + } + + double rand; + rand = randgen->Rndm(); + auto part1 = p1; + auto part2 = p2; + + if (rand > twotracksconfigs.confRandValue) { + part1 = p2; + part2 = p1; + } + + if (twotracksconfigs.confIsCPR.value) { + if (twotracksconfigs.confIsCPRatITS && twotracksconfigs.confCPRFracMax.value) { + if (pairCloseRejection.isClosePairAtITS(part1, part2, magFieldTesla, femto_universe_container::EventType::mixed)) { + continue; + } + } else { + if (twotracksconfigs.confIsCPRkT) { + if (pairCloseRejection.isClosePairkT(part1, part2, femto_universe_container::EventType::mixed, kT, twotracksconfigs.confIsCircularCut, twotracksconfigs.confCPRDphiAvgOrDist, magFieldTesla, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsfilldEtadPhiTPCcls, pairFractionTPCsCls)) { + continue; + } + if (twotracksconfigs.confIsCPRFraction) { + if (pairCloseRejection.isClosePairFractionkT(part1, part2, femto_universe_container::EventType::mixed, kT, magFieldTesla, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax)) { + continue; + } + } + } else { + if (pairCloseRejection.isClosePairFrac(part1, part2, magFieldTesla, femto_universe_container::EventType::mixed, twotracksconfigs.confCPRDphiAvgOrDist, twotracksconfigs.confCPRDistMax, twotracksconfigs.confCPRFracMax, twotracksconfigs.confIsCircularCut)) { + continue; + } + } + } + } + + std::vector f3d; + double kv; + float outsideref = 0.0; + + switch (contType) { + case PairType::PlusMinus: { + mixedEventMultCont.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + break; + } + + case PairType::PlusPlus: { + f3d = FemtoUniverseMath::newpairfunc(part1, mass1, part2, mass2, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + if (!twotracksconfigs.confUseCCImCut) { + mixedEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } else { + if (twotracksconfigs.confUse1stand3rd) { + if ((f3d[1] >= outsideref && f3d[2] >= outsideref) || (f3d[1] < outsideref && f3d[2] < outsideref)) { + mixedEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } else if (twotracksconfigs.confUse2ndand4th) { + if ((f3d[1] < outsideref && f3d[2] >= outsideref) || (f3d[1] >= outsideref && f3d[2] < outsideref)) { + mixedEventMultContPP.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } + } + if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { + float weight = 1.0f; + mixedEventCont1dpp.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + } + + if (twotracksconfigs.confIsFillAngqLCMS) { + kv = std::sqrt(f3d[1] * f3d[1] + f3d[2] * f3d[2] + f3d[3] * f3d[3]); + pairCloseRejection.ClosePairqLCMS(part1, part2, magFieldTesla, femto_universe_container::EventType::mixed, kv); + } + break; + } + + case PairType::MinusMinus: { + f3d = FemtoUniverseMath::newpairfunc(part1, mass1, part2, mass2, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + if (!twotracksconfigs.confUseCCImCut) { + mixedEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } else { + if (twotracksconfigs.confUse1stand3rd) { + if ((f3d[1] >= outsideref && f3d[2] >= outsideref) || (f3d[1] < outsideref && f3d[2] < outsideref)) { + mixedEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } else if (twotracksconfigs.confUse2ndand4th) { + if ((f3d[1] < outsideref && f3d[2] >= outsideref) || (f3d[1] >= outsideref && f3d[2] < outsideref)) { + mixedEventMultContMM.fillMultNumDen(part1, part2, femto_universe_sh_container::EventType::mixed, 2, multCol, kT, twotracksconfigs.confisIdenLCMS, twotracksconfigs.confIs1D, twotracksconfigs.confIsWeight, twotracksconfigs.confisIdenPRF); + } + } + } + if (twotracksconfigs.confIsMC || twotracksconfigs.confUse3D) { + float weight = 1.0f; + mixedEventCont1dmm.setPair(part1, part2, multCol, twotracksconfigs.confUse3D, weight, twotracksconfigs.confisIdenLCMS); + } + if (twotracksconfigs.confIsFillAngqLCMS) { + kv = std::sqrt(f3d[1] * f3d[1] + f3d[2] * f3d[2] + f3d[3] * f3d[3]); + pairCloseRejection.ClosePairqLCMS(part1, part2, magFieldTesla, femto_universe_container::EventType::mixed, kv); + } + break; + } + + default: + break; + } + } + } + + /// process function for to call doMixedEvent with Data + /// @param cols subscribe to the collisions table (Data) + /// @param subscribe to the femtoUniverseParticleTable + void processMixedEventCent(FilteredFDCollisions const& cols, + FilteredFemtoFullParticles const&) + { + randgen = new TRandom2(0); + PairType pairType = PairType::PlusMinus; + if (cfgProcessPM) { + pairType = PairType::PlusMinus; + } else if (cfgProcessPP) { + pairType = PairType::PlusPlus; + } else if (cfgProcessMM) { + pairType = PairType::MinusMinus; + } + + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningCent, confNEventsMix, -1, cols, cols)) { + + const int multiplicityCol = collision1.multV0M(); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningCent.getBin({collision1.posZ(), multiplicityCol})); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; + } + + if (cfgProcessPM) { + auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, magFieldTesla1, multiplicityCol, pairType); + } + if (cfgProcessPP) { + auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, magFieldTesla1, multiplicityCol, pairType); + } + if (cfgProcessMM) { + auto groupPartsOne = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, magFieldTesla1, multiplicityCol, pairType); + } + } + delete randgen; + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid, processMixedEventCent, "Enable processing mixed events for centrality", true); + + /// process function for to call doMixedEvent with Data + /// @param cols subscribe to the collisions table (Data) + /// @param parts subscribe to the femtoUniverseParticleTable + void processMixedEventNtr(FilteredFDCollisions const& cols, + FilteredFemtoFullParticles const&) + { + randgen = new TRandom2(0); + PairType pairType = PairType::PlusMinus; + if (cfgProcessPM) { + pairType = PairType::PlusMinus; + } else if (cfgProcessPP) { + pairType = PairType::PlusPlus; + } else if (cfgProcessMM) { + pairType = PairType::MinusMinus; + } + + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningNtr, confNEventsMix, -1, cols, cols)) { + + const int multiplicityCol = collision1.multNtr(); + mixQaRegistry.fill(HIST("MixingQA/hMECollisionBins"), colBinningNtr.getBin({collision1.posZ(), multiplicityCol})); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + if (magFieldTesla1 != magFieldTesla2) { + continue; + } + + if (cfgProcessPM) { + auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, magFieldTesla1, multiplicityCol, pairType); + } + if (cfgProcessPP) { + auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, magFieldTesla1, multiplicityCol, pairType); + } + if (cfgProcessMM) { + auto groupPartsOne = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); + auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); + doMixedEvent(groupPartsOne, groupPartsTwo, magFieldTesla1, multiplicityCol, pairType); + } + } + delete randgen; + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackTrackSpherHarMultKtExtendedItsPid, processMixedEventNtr, "Enable processing mixed events for centrality", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{ + adaptAnalysisTask(cfgc), + }; + return workflow; +} diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx index ed3237ed0bc..1e3738446d9 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Extended.cxx @@ -25,8 +25,10 @@ #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include @@ -48,6 +50,7 @@ #include #include +#include #include #include #include @@ -71,7 +74,7 @@ using namespace o2::analysis::femto_universe::efficiency_correction; struct FemtoUniversePairTaskTrackV0Extended { - Service pdgMC; + Service pdgMC{}; SliceCache cache; using FemtoFullParticles = soa::Join; @@ -95,21 +98,28 @@ struct FemtoUniversePairTaskTrackV0Extended { Configurable confChargePart1{"confChargePart1", 0, "sign of particle 1"}; Configurable confHPtPart1{"confHPtPart1", 4.0f, "higher limit for pt of particle 1"}; Configurable confLPtPart1{"confLPtPart1", 0.3f, "lower limit for pt of particle 1"}; + Configurable confTrkTPCnclsMin{"confTrkTPCnclsMin", 0, "Min. TPC clusters (if stricter than in Producer)"}; + Configurable confTrkTPCcRowsMin{"confTrkTPCcRowsMin", 0, "Min. TPC crossed rows (if stricter than in Producer)"}; + Configurable confDcaXYCustom1FilterCut{"confDcaXYCustom1FilterCut", 10, "Value for [1] custom DCAxy cut -> |DCAxy| < [1] + [2]/pT (if stricter than in Producer)"}; + Configurable confDcaXYCustom2FilterCut{"confDcaXYCustom2FilterCut", 0, "Value for [2] custom DCAxy cut -> |DCAxy| < [1] + [2]/pT (if stricter than in Producer)"}; } ConfTrkSelection; - Configurable confmom{"confmom", 0.5, "momentum threshold for particle identification using TOF"}; - Configurable confNsigmaTPCParticle{"confNsigmaTPCParticle", 3.0, "TPC Sigma for particle momentum < confmom"}; - Configurable confNsigmaTPCDaughter{"confNsigmaTPCDaughter", 3.0, "TPC Sigma for daughter"}; - - Configurable confNsigmaTOFParticle{"confNsigmaTOFParticle", 3.0, "TOF Sigma for particle (daugh & bach) momentum > Confmom"}; - Configurable confNsigmaCombinedParticle{"confNsigmaCombinedParticle", 3.0, "TPC and TOF Sigma (combined) for particle momentum > confmom"}; + struct : o2::framework::ConfigurableGroup { + Configurable confmom{"confmom", 0.5, "momentum threshold for particle identification using TOF"}; + Configurable confNsigmaTPCParticle{"confNsigmaTPCParticle", 3.0, "TPC Sigma for particle momentum < ConfNSigmaSelection.confmom"}; + Configurable confNsigmaTPCDaughter{"confNsigmaTPCDaughter", 3.0, "TPC Sigma for daughter"}; + Configurable confNsigmaTOFParticle{"confNsigmaTOFParticle", 3.0, "TOF Sigma for particle (daugh & bach) momentum > ConfNSigmaSelection.confmom"}; + Configurable confNsigmaCombinedParticle{"confNsigmaCombinedParticle", 3.0, "TPC and TOF Sigma (combined) for particle momentum > ConfNSigmaSelection.confmom"}; + Configurable confNsigmaKaonRejection{"confNsigmaKaonRejection", 0, "In proton selection reject tracks with kaon Nsigma confNsigmaPionRejection{"confNsigmaPionRejection", 0, "In proton selection reject tracks with pion Nsigma ; using FilteredFDCollision = FilteredFDCollisions::iterator; /// Partition for particle 1 using extended table (track) - Partition partsOneFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == as(ConfTrkSelection.confChargePart1)) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < ConfTrkSelection.confHPtPart1) && (aod::femtouniverseparticle::pt > ConfTrkSelection.confLPtPart1); + Partition partsOneFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == as(ConfTrkSelection.confChargePart1)) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < ConfTrkSelection.confHPtPart1) && (aod::femtouniverseparticle::pt > ConfTrkSelection.confLPtPart1) && (nabs(aod::track::dcaXY) < (ConfTrkSelection.confDcaXYCustom1FilterCut + ConfTrkSelection.confDcaXYCustom2FilterCut / aod::femtouniverseparticle::pt)) && (aod::femtouniverseparticle::tpcNClsFound) > as(ConfTrkSelection.confTrkTPCnclsMin) && (aod::femtouniverseparticle::tpcNClsCrossedRows) > as(ConfTrkSelection.confTrkTPCcRowsMin); Partition partsOneMCFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < ConfTrkSelection.confHPtPart1) && (aod::femtouniverseparticle::pt > ConfTrkSelection.confLPtPart1); Partition partsOneMCRecoFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kTrack)) && (aod::femtouniverseparticle::sign == as(ConfTrkSelection.confChargePart1)) && (nabs(aod::femtouniverseparticle::eta) < confEta) && (aod::femtouniverseparticle::pt < ConfTrkSelection.confHPtPart1) && (aod::femtouniverseparticle::pt > ConfTrkSelection.confLPtPart1); @@ -144,8 +154,20 @@ struct FemtoUniversePairTaskTrackV0Extended { Configurable confSeparateInvMassCheck{"confSeparateInvMassCheck", false, "Apply additional cut separate for mLambda and mAntiLambda"}; } ConfV0Selection; + struct : o2::framework::ConfigurableGroup { + Configurable confV0MinCosPA{"confV0MinCosPA", 0, "Min. V0 cosine of piontinh angle (if stricter than in Producer)"}; + Configurable confV0DaughMaxDCA{"confV0DaughMaxDCA", 1000, "Max. DCA of V0 daughters (if stricter than in Producer)"}; + Configurable confV0MinTransRadius{"confV0MinTransRadius", 0, "Min. transverse radius of V0 (if stricter than in Producer)"}; + Configurable confV0MaxRadius{"confV0MaxRadius", 1000, "Max. decay radius of V0 (if stricter than in Producer)"}; + Configurable confV0DaughMinPvDCAxy{"confV0DaughMinPvDCAxy", 0, "Min. DCAxy to primary vertex of V0 daughters (if stricter than in Producer)"}; // to już w córkach, nie da się dać w partycji niestety + Configurable confV0DaughMinTPCCls{"confV0DaughMinTPCCls", 0, "Min. number of TPC clusters for V0 daughters (if stricter than in Producer)"}; + } additionalcutsV0; + /// Partition for particle 2 using extended table - Partition partsTwoFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kV0)) && (aod::femtouniverseparticle::pt < ConfV0Selection.confHPtPart2) && (aod::femtouniverseparticle::pt > ConfV0Selection.confLPtPart2); + Partition + partsTwoFull = (aod::femtouniverseparticle::decayVtxX < additionalcutsV0.confV0MaxRadius && aod::femtouniverseparticle::decayVtxY < additionalcutsV0.confV0MaxRadius && aod::femtouniverseparticle::decayVtxZ < additionalcutsV0.confV0MaxRadius && + aod::femtouniverseparticle::transRadius > additionalcutsV0.confV0MinTransRadius && aod::femtouniverseparticle::daughDCA < additionalcutsV0.confV0DaughMaxDCA && aod::femtouniverseparticle::tempFitVar > additionalcutsV0.confV0MinCosPA && + aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kV0) && (aod::femtouniverseparticle::pt < ConfV0Selection.confHPtPart2) && (aod::femtouniverseparticle::pt > ConfV0Selection.confLPtPart2)); Partition partsTwoMCFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) && (aod::femtouniverseparticle::pt < ConfV0Selection.confHPtPart2) && (aod::femtouniverseparticle::pt > ConfV0Selection.confLPtPart2); Partition partsTwoMCRecoFull = (aod::femtouniverseparticle::partType == uint8_t(aod::femtouniverseparticle::ParticleType::kV0)) && (aod::femtouniverseparticle::pt < ConfV0Selection.confHPtPart2) && (aod::femtouniverseparticle::pt > ConfV0Selection.confLPtPart2); @@ -202,13 +224,17 @@ struct FemtoUniversePairTaskTrackV0Extended { } ConfCPR; // Efficiency - Configurable confLocalEfficiency{"confLocalEfficiency", "", "Local path to efficiency .root file"}; + struct : o2::framework::ConfigurableGroup { + Configurable confLocalEfficiency{"confLocalEfficiency", "", "Local path to efficiency .root file"}; + Configurable confCCDBNoLaterThanTrack{"confCCDBNoLaterThanTrack", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable confCCDBNoLaterThanV0{"confCCDBNoLaterThanV0", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + } ccdbEffLoader; EffCorConfigurableGroup effCorConfGroup; EfficiencyCorrection effCorrection{&effCorConfGroup}; - static constexpr unsigned int V0ChildTable[][2] = {{0, 1}, {1, 0}, {1, 1}}; // Table to select the V0 children - static constexpr double v0InvMass[] = {1.115, 1.115, 0.497}; // Table to select invariant mass of V0s + static constexpr std::array, 3> V0ChildTable = {{{0, 1}, {1, 0}, {1, 1}}}; // Table to select the V0 children + static constexpr std::array V0InvMass = {o2::constants::physics::MassLambda0, o2::constants::physics::MassLambda0Bar, o2::constants::physics::MassK0}; // Table to select invariant mass of V0s FemtoUniverseContainer sameEventCont; FemtoUniverseContainer mixedEventCont; @@ -227,37 +253,37 @@ struct FemtoUniversePairTaskTrackV0Extended { std::unique_ptr plocalEffFile; std::unique_ptr pEffHistp1; std::unique_ptr pEffHistp2; - Service ccdb; + Service ccdb{}; - TRandom2* randgen; + TRandom2* randgen = 0; bool isNSigmaCombined(float mom, float nsigmaTPCParticle, float nsigmaTOFParticle, bool hasTOF) { - if (mom <= confmom) { - return (std::abs(nsigmaTPCParticle) < confNsigmaTPCParticle); - } else if (hasTOF == 1) { - return (std::hypot(nsigmaTOFParticle, nsigmaTPCParticle) < confNsigmaCombinedParticle); - } else { - return false; + if (mom <= ConfNSigmaSelection.confmom) { + return (std::abs(nsigmaTPCParticle) < ConfNSigmaSelection.confNsigmaTPCParticle); + } + if (hasTOF) { + return (std::hypot(nsigmaTOFParticle, nsigmaTPCParticle) < ConfNSigmaSelection.confNsigmaCombinedParticle); } + return false; } template bool isNSigmaCombinedBitmask(float mom, const T& part) { - if (mom <= confmom) { + if (mom <= ConfNSigmaSelection.confmom) { return ((part.pidCut() & (1u << ConfTrkSelection.confTrackChoicePartOne)) != 0); - } else if ((part.pidCut() & 512u) != 0) { + } + if ((part.pidCut() & 512u) != 0) { return ((part.pidCut() & (64u << ConfTrkSelection.confTrackChoicePartOne)) != 0); - } else { - return false; } + return false; } bool invMLambda(float invMassLambda, float invMassAntiLambda, int V0Type) { if (ConfV0Selection.confSeparateInvMassCheck) { - const float pMass = V0Type ? invMassAntiLambda : invMassLambda; + const float pMass = (V0Type == 1) ? invMassAntiLambda : invMassLambda; if (pMass < ConfV0Selection.confV0InvMassLowLimit || pMass > ConfV0Selection.confV0InvMassUpLimit) { return false; } @@ -271,51 +297,56 @@ struct FemtoUniversePairTaskTrackV0Extended { bool isNSigmaTPC(float nsigmaTPCParticle) { - if (std::abs(nsigmaTPCParticle) < confNsigmaTPCDaughter) { - return true; - } else { - return false; - } + return std::abs(nsigmaTPCParticle) < ConfNSigmaSelection.confNsigmaTPCDaughter; } bool isNSigmaTOF(float mom, float nsigmaTOFParticle, bool hasTOF) { // Cut only on daughter tracks, that have TOF signal - if (mom > confmom && hasTOF == 1) { - if (std::abs(nsigmaTOFParticle) < confNsigmaTOFParticle) { - return true; - } else { - return false; - } - } else { - return true; + if (mom > ConfNSigmaSelection.confmom && hasTOF) { + return std::abs(nsigmaTOFParticle) < ConfNSigmaSelection.confNsigmaTOFParticle; } + return true; } template bool isParticleCombined(const T& part, int id) { - const float tpcNSigmas[3] = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; - const float tofNSigmas[3] = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; - + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + enum particleID { protonId, + pionId, + kaonId }; + + if (id == protonId) { + if (part.p() < ConfNSigmaSelection.confmom) { + if (std::abs(tpcNSigmas[kaonId]) < ConfNSigmaSelection.confNsigmaKaonRejection || std::abs(tpcNSigmas[pionId]) < ConfNSigmaSelection.confNsigmaPionRejection) { + return false; + } + } else if (std::hypot(tofNSigmas[kaonId], tpcNSigmas[kaonId]) < ConfNSigmaSelection.confNsigmaKaonRejection || std::hypot(tofNSigmas[pionId], tpcNSigmas[pionId]) < ConfNSigmaSelection.confNsigmaPionRejection) { + return false; + } + } return isNSigmaCombined(part.p(), tpcNSigmas[id], tofNSigmas[id], (part.pidCut() & 512u) != 0); } template - bool isParticleTPC(const T& part, int id, float* partSigma = 0) + bool isParticleTPC(const T& part, int id, float* partSigma = nullptr) { - const float tpcNSigmas[3] = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; - if (partSigma) + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + if (partSigma) { *partSigma = tpcNSigmas[id]; + } return isNSigmaTPC(tpcNSigmas[id]); } template - bool isParticleTOF(const T& part, int id, float* partSigma = 0) + bool isParticleTOF(const T& part, int id, float* partSigma = nullptr) { - const float tofNSigmas[3] = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; - if (partSigma) + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + if (partSigma) { *partSigma = tofNSigmas[id]; + } return isNSigmaTOF(part.p(), tofNSigmas[id], (part.pidCut() & 512u) != 0); } @@ -402,6 +433,21 @@ struct FemtoUniversePairTaskTrackV0Extended { registryMCreco.add("mothersReco/motherParticle", "pair fractions;part1 mother PDG;part2 mother PDG", {HistType::kTH2F, {confMotherPDGBins, confMotherPDGBins}}); registryMCreco.add("mothersReco/motherParticlePDGCheck", "pair fractions;part1 mother PDG;part2 mother PDG", {HistType::kTH2F, {confMotherPDGBins, confMotherPDGBins}}); } + + if (doprocessFractionsSingleReco) { + registryMCreco.add("mothersReco/motherParticleTrack", "; mother PDG", {HistType::kTH1F, {confMotherPDGBins}}); + registryMCreco.add("mothersReco/motherParticleTrackPDGCheck", "; mother PDG", {HistType::kTH1F, {confMotherPDGBins}}); + registryMCreco.add("mothersReco/motherParticleV0", "; mother PDG", {HistType::kTH1F, {confMotherPDGBins}}); + registryMCreco.add("mothersReco/motherParticleV0PDGCheck", "; mother PDG", {HistType::kTH1F, {confMotherPDGBins}}); + } + + if (doprocessFractionsSingleTruth) { + registryMCtruth.add("mothersTruth/motherParticleTrack", "; mother PDG", {HistType::kTH1F, {confMotherPDGBins}}); + registryMCtruth.add("mothersTruth/mcProcessTrack", " ; VMC physics code", {HistType::kTH1F, {{50, 0, 50}}}); + registryMCtruth.add("mothersTruth/motherParticleV0", "; mother PDG", {HistType::kTH1F, {confMotherPDGBins}}); + registryMCtruth.add("mothersTruth/mcProcessV0", " ; VMC physics code", {HistType::kTH1F, {{50, 0, 50}}}); + } + sameEventCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); sameEventCont.setPDGCodes(ConfTrkSelection.confTrkPDGCodePartOne, ConfV0Selection.confV0PDGCodePartTwo); mixedEventCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); @@ -414,12 +460,13 @@ struct FemtoUniversePairTaskTrackV0Extended { pairCloseRejectionV0.init(&resultRegistry, &qaRegistry, ConfCPR.confDeltaEtaAxis, ConfCPR.confDeltaPhiStarAxis, ConfCPR.confCPRdeltaPhiCutMin.value, ConfCPR.confCPRdeltaPhiCutMax.value, ConfCPR.confCPRdeltaEtaCutMin.value, ConfCPR.confCPRdeltaEtaCutMax.value, ConfCPR.confCPRChosenRadii.value, ConfCPR.confCPRPlotPerRadii.value); } - if (!confLocalEfficiency.value.empty()) { - plocalEffFile = std::unique_ptr(TFile::Open(confLocalEfficiency.value.c_str(), "read")); - if (!plocalEffFile || plocalEffFile.get()->IsZombie()) - LOGF(fatal, "Could not load efficiency histogram from %s", confLocalEfficiency.value.c_str()); + if (!ccdbEffLoader.confLocalEfficiency.value.empty()) { + plocalEffFile = std::unique_ptr(TFile::Open(ccdbEffLoader.confLocalEfficiency.value.c_str(), "read")); + if (!plocalEffFile || plocalEffFile.get()->IsZombie()) { + LOGF(fatal, "Could not load efficiency histogram from %s", ccdbEffLoader.confLocalEfficiency.value.c_str()); + } if (doprocessSameEvent || doprocessSameEventBitmask || doprocessMixedEvent || doprocessMixedEventBitmask) { - pEffHistp1 = (ConfTrkSelection.confChargePart1 > 0) ? std::unique_ptr(plocalEffFile.get()->Get("PrPlus")) : std::unique_ptr(plocalEffFile.get()->Get("PrMinus")); // note: works only for protons for now + pEffHistp1 = (ConfTrkSelection.confChargePart1 > 0) ? std::unique_ptr(plocalEffFile.get()->Get("PrPlus")) : std::unique_ptr(plocalEffFile.get()->Get("PrMinus")); // note: works only for protons pEffHistp2 = (ConfV0Selection.confV0Type1 == 0) ? std::unique_ptr(plocalEffFile.get()->Get("Lambda")) : std::unique_ptr(plocalEffFile.get()->Get("AntiLambda")); LOGF(info, "Loaded efficiency histograms for track-V0."); } else if (doprocessSameEventV0 || doprocessSameEventV0Bitmask || doprocessMixedEventV0 || doprocessMixedEventV0Bitmask) { @@ -432,15 +479,14 @@ struct FemtoUniversePairTaskTrackV0Extended { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - ccdb->setCreatedNotAfter(now); + ccdb->setCreatedNotAfter(std::max(ccdbEffLoader.confCCDBNoLaterThanTrack.value, ccdbEffLoader.confCCDBNoLaterThanV0.value)); if (doprocessSameEvent || doprocessSameEventBitmask || doprocessMixedEvent || doprocessMixedEventBitmask) { - pEffHistp1 = (ConfTrkSelection.confChargePart1 > 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/PrPlus", now)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/PrMinus", now)); // note: works only for protons for now - pEffHistp2 = (ConfV0Selection.confV0Type1 == 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/Lambda", now)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/AntiLambda", now)); + pEffHistp1 = (ConfTrkSelection.confChargePart1 > 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/PrPlus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/PrMinus", ccdbEffLoader.confCCDBNoLaterThanTrack.value)); // note: works only for protons + pEffHistp2 = (ConfV0Selection.confV0Type1 == 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/Lambda", ccdbEffLoader.confCCDBNoLaterThanV0.value)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/AntiLambda", ccdbEffLoader.confCCDBNoLaterThanV0.value)); LOGF(info, "Loaded efficiency histograms for track-V0 from CCDB."); } else if (doprocessSameEventV0 || doprocessSameEventV0Bitmask || doprocessMixedEventV0 || doprocessMixedEventV0Bitmask) { - pEffHistp1 = (ConfV0Selection.confV0Type1 == 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/Lambda", now)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/AntiLambda", now)); - pEffHistp2 = (ConfV0Selection.confV0Type2 == 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/Lambda", now)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/AntiLambda", now)); + pEffHistp1 = (ConfV0Selection.confV0Type1 == 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/Lambda", ccdbEffLoader.confCCDBNoLaterThanV0.value)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/AntiLambda", ccdbEffLoader.confCCDBNoLaterThanV0.value)); + pEffHistp2 = (ConfV0Selection.confV0Type2 == 0) ? std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/Lambda", ccdbEffLoader.confCCDBNoLaterThanV0.value)) : std::unique_ptr(ccdb->getForTimeStamp(effCorConfGroup.confEffCorCCDBPath.value + "/AntiLambda", ccdbEffLoader.confCCDBNoLaterThanV0.value)); LOGF(info, "Loaded efficiency histograms for V0-V0 from CCDB."); } } @@ -448,29 +494,39 @@ struct FemtoUniversePairTaskTrackV0Extended { effCorrection.init(&qaRegistry, {static_cast(ConfV0Selection.confV0TempFitVarpTBins), {confEtaBins, -2, 2}, confMultBins}); } + template + bool checkDaughter(PartType const& posChild, PartType const& negChild) + { + if (std::abs(posChild.dcaXY()) < additionalcutsV0.confV0DaughMinPvDCAxy || std::abs(negChild.dcaXY()) < additionalcutsV0.confV0DaughMinPvDCAxy) { + return false; + } + return (posChild.tpcNClsFound() > additionalcutsV0.confV0DaughMinTPCCls && negChild.tpcNClsFound() > additionalcutsV0.confV0DaughMinTPCCls); + } + template - using hasSigma = decltype(std::declval().tpcNSigmaStorePr()); + using HasSigma = decltype(std::declval().tpcNSigmaStorePr()); /// This function processes the same event for track - V0 template - void doSameEvent(FilteredFDCollision const& col, PartType const& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] MCParticles mcParts = nullptr) + void doSameEvent(FilteredFDCollision const& col, PartType const& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { const auto& magFieldTesla = col.magField(); auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : col.multNtr(); eventHisto.fillQA(col); - float v0DaughPtLowTable[3][2] = {{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}; - float v0DaughPtHighTable[3][2] = {{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}; + std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; + std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; /// Histogramming same event for (const auto& part : groupPartsTwo) { - if (!invMLambda(part.mLambda(), part.mAntiLambda(), ConfV0Selection.confV0Type1)) + if (!invMLambda(part.mLambda(), part.mAntiLambda(), ConfV0Selection.confV0Type1)) { continue; + } const auto& posChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); @@ -479,27 +535,34 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; - - if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { + continue; + } + if (!checkDaughter(posChild, negChild)) { continue; + } trackHistoPartTwo.fillQA(part); posChildHistos.fillQA(posChild); negChildHistos.fillQA(negChild); } else { - if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type1 == 0) && (part.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (part.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (part.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type1 == 0) && (part.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (part.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (part.pidCut() & 48) != 48)) { continue; + } } else { - if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } } trackHistoPartTwo.fillQA(part); posChildHistos.fillQA(posChild); @@ -508,13 +571,14 @@ struct FemtoUniversePairTaskTrackV0Extended { } for (const auto& part : groupPartsOne) { - if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { /// PID plot for particle 1 - const float tpcNSigmas[3] = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; - const float tofNSigmas[3] = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; + const std::array tpcNSigmas = {aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStoreKa())}; + const std::array tofNSigmas = {aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStoreKa())}; - if (!isNSigmaCombined(part.p(), tpcNSigmas[ConfTrkSelection.confTrackChoicePartOne], tofNSigmas[ConfTrkSelection.confTrackChoicePartOne], (part.pidCut() & 512u) != 0)) + if (!isParticleCombined(part, ConfTrkSelection.confTrackChoicePartOne)) { continue; + } if (part.sign() > 0) { qaRegistry.fill(HIST("Tracks_pos/nSigmaTPC"), part.p(), tpcNSigmas[ConfTrkSelection.confTrackChoicePartOne]); qaRegistry.fill(HIST("Tracks_pos/nSigmaTOF"), part.p(), tofNSigmas[ConfTrkSelection.confTrackChoicePartOne]); @@ -525,27 +589,33 @@ struct FemtoUniversePairTaskTrackV0Extended { trackHistoPartOneNeg.fillQA(part); } } else { - if (!isNSigmaCombinedBitmask(part.p(), part)) + if (!isNSigmaCombinedBitmask(part.p(), part)) { continue; - if (ConfTrkSelection.confChargePart1 > 0) + } + if (ConfTrkSelection.confChargePart1 > 0) { trackHistoPartOnePos.fillQA(part); - if (ConfTrkSelection.confChargePart1 < 0) + } + if (ConfTrkSelection.confChargePart1 < 0) { trackHistoPartOnePos.fillQA(part); + } } } /// Now build the combinations for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { // Lambda invariant mass cut - if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type1)) + if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type1)) { continue; + } /// PID using stored binned nsigma - if constexpr (std::experimental::is_detected::value) { - if (!isParticleCombined(p1, ConfTrkSelection.confTrackChoicePartOne)) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleCombined(p1, ConfTrkSelection.confTrackChoicePartOne)) { continue; + } } else { - if (!isNSigmaCombinedBitmask(p1.p(), p1)) + if (!isNSigmaCombinedBitmask(p1.p(), p1)) { continue; + } } // track cleaning if (!pairCleaner.isCleanPair(p1, p2, parts)) { @@ -559,23 +629,32 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { + continue; + } + + if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; + } - if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if (!checkDaughter(posChild, negChild)) { continue; + } } else { - if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type1 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p2.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type1 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p2.pidCut() & 48) != 48)) { continue; + } } else { - if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } } } @@ -586,31 +665,34 @@ struct FemtoUniversePairTaskTrackV0Extended { } float weight = 1.0f; - if (pEffHistp1) + if (pEffHistp1) { weight = pEffHistp1.get()->GetBinContent(pEffHistp1->FindBin(p1.pt(), p1.eta())) * pEffHistp2.get()->GetBinContent(pEffHistp2->FindBin(p2.pt(), p2.eta())); - if constexpr (std::is_same::value) + } + if constexpr (std::is_same::value) { sameEventCont.setPair(p1, p2, multCol, confUse3D, weight); - else + } else { sameEventCont.setPair(p1, p2, multCol, confUse3D, weight); + } } } /// This function processes the same event for V0 - V0 template - void doSameEventV0(FilteredFDCollision const& col, PartType const& parts, PartitionType& groupPartsTwo, [[maybe_unused]] MCParticles mcParts = nullptr) + void doSameEventV0(FilteredFDCollision const& col, PartType const& parts, PartitionType& groupPartsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { const auto& magFieldTesla = col.magField(); - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : col.multNtr(); eventHisto.fillQA(col); - float v0DaughPtLowTable[3][2] = {{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}; - float v0DaughPtHighTable[3][2] = {{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}; + std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; + std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; /// Histogramming same event for first V0 particle for (const auto& part : groupPartsTwo) { - if (!invMLambda(part.mLambda(), part.mAntiLambda(), ConfV0Selection.confV0Type1)) + if (!invMLambda(part.mLambda(), part.mAntiLambda(), ConfV0Selection.confV0Type1)) { continue; + } const auto& posChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); if (posChild.pt() < v0DaughPtLowTable[ConfV0Selection.confV0Type1][0] || negChild.pt() < v0DaughPtLowTable[ConfV0Selection.confV0Type1][1] || posChild.pt() > v0DaughPtHighTable[ConfV0Selection.confV0Type1][0] || negChild.pt() > v0DaughPtHighTable[ConfV0Selection.confV0Type1][1]) { @@ -618,11 +700,16 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Check daughters of first V0 particle - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { + continue; + } + if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; - if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!checkDaughter(posChild, negChild)) { continue; + } trackHistoV0Type1.fillQABase(part, HIST("V0Type1")); posChildV0Type1.fillQABase(posChild, HIST("posChildV0Type1")); @@ -630,14 +717,17 @@ struct FemtoUniversePairTaskTrackV0Extended { qaRegistry.fill(HIST("V0Type1/hInvMassLambdaVsCent"), multCol, part.mLambda()); qaRegistry.fill(HIST("V0Type1/hInvMassAntiLambdaVsCent"), multCol, part.mAntiLambda()); } else { - if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type1 == 0) && (part.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (part.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (part.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type1 == 0) && (part.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (part.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (part.pidCut() & 48) != 48)) { continue; + } } else { - if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } } trackHistoV0Type1.fillQABase(part, HIST("V0Type1")); posChildV0Type1.fillQABase(posChild, HIST("posChildV0Type1")); @@ -650,8 +740,9 @@ struct FemtoUniversePairTaskTrackV0Extended { /// Histogramming same event for second V0 particle for (const auto& part : groupPartsTwo) { - if (!invMLambda(part.mLambda(), part.mAntiLambda(), ConfV0Selection.confV0Type2)) + if (!invMLambda(part.mLambda(), part.mAntiLambda(), ConfV0Selection.confV0Type2)) { continue; + } const auto& posChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); if (posChild.pt() < v0DaughPtLowTable[ConfV0Selection.confV0Type2][0] || negChild.pt() < v0DaughPtLowTable[ConfV0Selection.confV0Type2][1] || posChild.pt() > v0DaughPtHighTable[ConfV0Selection.confV0Type2][0] || negChild.pt() > v0DaughPtHighTable[ConfV0Selection.confV0Type2][1]) { @@ -659,11 +750,16 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Check daughters of second V0 particle - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type2][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type2][1])) { + continue; + } + if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type2][1])) { continue; - if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type2][1])) + } + if (!checkDaughter(posChild, negChild)) { continue; + } trackHistoV0Type2.fillQABase(part, HIST("V0Type2")); posChildV0Type2.fillQABase(posChild, HIST("posChildV0Type2")); @@ -671,14 +767,17 @@ struct FemtoUniversePairTaskTrackV0Extended { qaRegistry.fill(HIST("V0Type2/hInvMassLambdaVsCent"), multCol, part.mLambda()); qaRegistry.fill(HIST("V0Type2/hInvMassAntiLambdaVsCent"), multCol, part.mAntiLambda()); } else { - if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) + if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type2 == 0) && (part.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (part.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type2 == 2) && (part.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type2 == 0) && (part.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (part.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type2 == 2) && (part.pidCut() & 48) != 48)) { continue; + } } else { - if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) + if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) { continue; + } } trackHistoV0Type2.fillQABase(part, HIST("V0Type2")); posChildV0Type2.fillQABase(posChild, HIST("posChildV0Type2")); @@ -691,17 +790,19 @@ struct FemtoUniversePairTaskTrackV0Extended { auto pairDuplicateCheckFunc = [&](auto& p1, auto& p2) -> void { // V0 inv mass cut for p1 - if (!invMLambda(p1.mLambda(), p1.mAntiLambda(), ConfV0Selection.confV0Type1)) + if (!invMLambda(p1.mLambda(), p1.mAntiLambda(), ConfV0Selection.confV0Type1)) { return; + } // V0 inv mass cut for p2 - if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type2)) + if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type2)) { return; + } // track cleaning & checking for duplicate pairs if (!pairCleanerV0.isCleanPair(p1, p2, parts)) { - // mark for rejection the cascade that shares a daughter with another cascade and has an invariant mass further from default value + // mark for rejection the v0 that shares a daughter with another v0 and has an invariant mass further from default value. Set confV0DuplCosPA as TRUE to do the same check with cosPA instead. if (!ConfV0Selection.confV0DuplCosPA) { - if (std::abs(p1.mLambda() - v0InvMass[ConfV0Selection.confV0Type1]) < std::abs(p2.mLambda() - v0InvMass[ConfV0Selection.confV0Type2])) { + if (std::abs(p1.mLambda() - V0InvMass[ConfV0Selection.confV0Type1]) < std::abs(p2.mLambda() - V0InvMass[ConfV0Selection.confV0Type2])) { v0Duplicates.insert(p2.globalIndex()); } else { v0Duplicates.insert(p1.globalIndex()); @@ -717,14 +818,17 @@ struct FemtoUniversePairTaskTrackV0Extended { }; auto pairProcessFunc = [&](auto& p1, auto& p2) -> bool { - if (v0Duplicates.contains(p1.globalIndex()) || v0Duplicates.contains(p2.globalIndex())) + if (v0Duplicates.contains(p1.globalIndex()) || v0Duplicates.contains(p2.globalIndex())) { return false; + } // Lambda invariant mass cut for p1 - if (!invMLambda(p1.mLambda(), p1.mAntiLambda(), ConfV0Selection.confV0Type1)) + if (!invMLambda(p1.mLambda(), p1.mAntiLambda(), ConfV0Selection.confV0Type1)) { return false; + } // Lambda invariant mass cut for p2 - if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type2)) + if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type2)) { return false; + } const auto& posChild1 = parts.iteratorAt(p1.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild1 = parts.iteratorAt(p1.globalIndex() - 1 - parts.begin().globalIndex()); @@ -733,20 +837,28 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// p1 daughters that do not pass this condition are not selected - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) { return false; - if (!isParticleTOF(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!isParticleTOF(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) { return false; + } + if (!checkDaughter(posChild1, negChild1)) { + return false; + } } else { - if ((posChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { return false; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type1 == 0) && (p1.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p1.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p1.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type1 == 0) && (p1.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p1.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p1.pidCut() & 48) != 48)) { return false; + } } else { - if ((posChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { return false; + } } } @@ -757,33 +869,42 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// p2 daughters that do not pass this condition are not selected - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) { return false; - if (!isParticleTOF(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) + } + if (!isParticleTOF(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) { return false; + } + if (!checkDaughter(posChild2, negChild2)) { + return false; + } } else { - if ((posChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) + if ((posChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) { return false; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type2 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type2 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type2 == 2) && (p2.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type2 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type2 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type2 == 2) && (p2.pidCut() & 48) != 48)) { return false; + } } else { - if ((posChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) + if ((posChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) { return false; + } } } if (ConfCPR.confIsCPR.value) { - double rand; + double rand = 0.0; auto part1 = p1; auto part2 = p2; + double randomizationValue = 0.5; if (ConfCPR.confRandomizeCPR) { randgen = new TRandom2(0); rand = randgen->Rndm(); - if (rand > 0.5) { + if (rand > randomizationValue) { part1 = p2; part2 = p1; } @@ -791,15 +912,17 @@ struct FemtoUniversePairTaskTrackV0Extended { } if (ConfCPR.confRectV0V0CPR && pairCloseRejectionV0.isClosePair(part1, part2, parts, magFieldTesla, femto_universe_container::EventType::same)) { return false; - } else if (!ConfCPR.confRectV0V0CPR && pairCloseRejectionV0.isClosePair(part1, part2, parts, magFieldTesla, femto_universe_container::EventType::same)) { + } + if (!ConfCPR.confRectV0V0CPR && pairCloseRejectionV0.isClosePair(part1, part2, parts, magFieldTesla, femto_universe_container::EventType::same)) { return false; } } - if constexpr (std::is_same::value) + if constexpr (std::is_same::value) { sameEventCont.setPair(p1, p2, multCol, confUse3D); - else + } else { sameEventCont.setPair(p1, p2, multCol, confUse3D); + } return true; }; @@ -880,22 +1003,24 @@ struct FemtoUniversePairTaskTrackV0Extended { auto groupPartsOne = partsOneMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); auto groupPartsTwo = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : col.multNtr(); eventHisto.fillQA(col); /// Histogramming same event for (const auto& part : groupPartsTwo) { int pdgCode = static_cast(part.pidCut()); - if ((ConfV0Selection.confV0Type1 == 0 && pdgCode != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode != kLambda0Bar)) + if ((ConfV0Selection.confV0Type1 == 0 && pdgCode != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode != kLambda0Bar)) { continue; + } trackHistoPartTwo.fillQA(part); } for (const auto& part : groupPartsOne) { int pdgCode = static_cast(part.pidCut()); - if (pdgCode != ConfTrkSelection.confTrkPDGCodePartOne) + if (pdgCode != ConfTrkSelection.confTrkPDGCodePartOne) { continue; + } const auto& pdgParticle = pdgMC->GetParticle(pdgCode); if (!pdgParticle) { continue; @@ -910,11 +1035,13 @@ struct FemtoUniversePairTaskTrackV0Extended { /// Now build the combinations for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (static_cast(p1.pidCut()) != ConfTrkSelection.confTrkPDGCodePartOne) + if (static_cast(p1.pidCut()) != ConfTrkSelection.confTrkPDGCodePartOne) { continue; + } int pdgCode2 = static_cast(p2.pidCut()); - if ((ConfV0Selection.confV0Type1 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode2 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type1 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode2 != kLambda0Bar)) { continue; + } // track cleaning if (ConfCPR.confIsCPR.value) { if (pairCloseRejection.isClosePair(p1, p2, parts, magFieldTesla, femto_universe_container::EventType::same)) { @@ -931,7 +1058,7 @@ struct FemtoUniversePairTaskTrackV0Extended { void processMCSameEventV0(FilteredFDCollision const& col, FemtoFullParticles const& parts) { auto groupPartsTwo = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - const int multCol = confUseCent ? col.multV0M() : col.multNtr(); + const int multCol = confUseCent ? static_cast(col.multV0M()) : col.multNtr(); eventHisto.fillQA(col); @@ -960,11 +1087,13 @@ struct FemtoUniversePairTaskTrackV0Extended { auto pairProcessFunc = [&](auto& p1, auto& p2) -> void { int pdgCode1 = static_cast(p1.pidCut()); - if ((ConfV0Selection.confV0Type1 == 0 && pdgCode1 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode1 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type1 == 0 && pdgCode1 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode1 != kLambda0Bar)) { return; + } int pdgCode2 = static_cast(p2.pidCut()); - if ((ConfV0Selection.confV0Type2 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && pdgCode2 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type2 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && pdgCode2 != kLambda0Bar)) { return; + } sameEventCont.setPair(p1, p2, multCol, confUse3D); }; /// Now build the combinations @@ -985,16 +1114,16 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes the mixed event for track - V0 template - void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] MCParticles mcParts = nullptr) + void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsOne, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; - float v0DaughPtLowTable[3][2] = {{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}; - float v0DaughPtHighTable[3][2] = {{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}; + std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; + std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); @@ -1008,15 +1137,18 @@ struct FemtoUniversePairTaskTrackV0Extended { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { // Lambda invariant mass cut - if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type1)) + if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type1)) { continue; + } /// PID using stored binned nsigma - if constexpr (std::experimental::is_detected::value) { - if (!isParticleCombined(p1, ConfTrkSelection.confTrackChoicePartOne)) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleCombined(p1, ConfTrkSelection.confTrackChoicePartOne)) { continue; + } } else { - if (!isNSigmaCombinedBitmask(p1.p(), p1)) + if (!isNSigmaCombinedBitmask(p1.p(), p1)) { continue; + } } const auto& posChild = parts.iteratorAt(p2.globalIndex() - 2 - parts.begin().globalIndex()); @@ -1026,20 +1158,28 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; - if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; + } + if (!checkDaughter(posChild, negChild)) { + continue; + } } else { - if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type1 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p2.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type1 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p2.pidCut() & 48) != 48)) { continue; + } } else { - if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } } } @@ -1053,13 +1193,14 @@ struct FemtoUniversePairTaskTrackV0Extended { } } float weight = 1.0f; - if (pEffHistp1) + if (pEffHistp1) { weight = pEffHistp1.get()->GetBinContent(pEffHistp1->FindBin(p1.pt(), p1.eta())) * pEffHistp2.get()->GetBinContent(pEffHistp2->FindBin(p2.pt(), p2.eta())); - - if constexpr (std::is_same::value) + } + if constexpr (std::is_same::value) { mixedEventCont.setPair(p1, p2, multCol, confUse3D, weight); - else + } else { mixedEventCont.setPair(p1, p2, multCol, confUse3D, weight); + } } }; @@ -1078,16 +1219,16 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes the mixed event for V0 - V0 template - void doMixedEventV0(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsTwo, [[maybe_unused]] MCParticles mcParts = nullptr) + void doMixedEventV0(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partsTwo, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; - float v0DaughPtLowTable[3][2] = {{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}; - float v0DaughPtHighTable[3][2] = {{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}; + std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; + std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); auto groupPartsOne = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); @@ -1117,20 +1258,28 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; - if (!isParticleTOF(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!isParticleTOF(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; + } + if (!checkDaughter(posChild1, negChild1)) { + continue; + } } else { - if ((posChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type1 == 0) && (p1.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p1.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p1.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type1 == 0) && (p1.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type1 == 1) && (p1.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type1 == 2) && (p1.pidCut() & 48) != 48)) { continue; + } } else { - if ((posChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) + if ((posChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][0])) == 0 || (negChild1.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type1][1])) == 0) { continue; + } } } @@ -1141,20 +1290,28 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if constexpr (std::experimental::is_detected::value) { - if (!isParticleTPC(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) + if constexpr (std::experimental::is_detected::value) { + if (!isParticleTPC(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) { + continue; + } + if (!isParticleTOF(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) { continue; - if (!isParticleTOF(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) + } + if (!checkDaughter(posChild2, negChild2)) { continue; + } } else { - if ((posChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) + if ((posChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (1u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if (((ConfV0Selection.confV0Type2 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type2 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type2 == 2) && (p2.pidCut() & 48) != 48)) + if (((ConfV0Selection.confV0Type2 == 0) && (p2.pidCut() & 3) != 3) || ((ConfV0Selection.confV0Type2 == 1) && (p2.pidCut() & 12) != 12) || ((ConfV0Selection.confV0Type2 == 2) && (p2.pidCut() & 48) != 48)) { continue; + } } else { - if ((posChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) + if ((posChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][0])) == 0 || (negChild2.pidCut() & (8u << V0ChildTable[ConfV0Selection.confV0Type2][1])) == 0) { continue; + } } } @@ -1163,15 +1320,16 @@ struct FemtoUniversePairTaskTrackV0Extended { continue; } if (ConfCPR.confIsCPR.value) { - double rand; + double rand = 0.0; auto part1 = p1; auto part2 = p2; + double randomizationValue = 0.5; if (ConfCPR.confRandomizeCPR) { randgen = new TRandom2(0); rand = randgen->Rndm(); - if (rand > 0.5) { + if (rand > randomizationValue) { part1 = p2; part2 = p1; } @@ -1179,15 +1337,17 @@ struct FemtoUniversePairTaskTrackV0Extended { } if (ConfCPR.confRectV0V0CPR && pairCloseRejectionV0.isClosePair(part1, part2, parts, magFieldTesla1, femto_universe_container::EventType::mixed)) { continue; - } else if (!ConfCPR.confRectV0V0CPR && pairCloseRejectionV0.isClosePair(part1, part2, parts, magFieldTesla1, femto_universe_container::EventType::mixed)) { + } + if (!ConfCPR.confRectV0V0CPR && pairCloseRejectionV0.isClosePair(part1, part2, parts, magFieldTesla1, femto_universe_container::EventType::mixed)) { continue; } } - if constexpr (std::is_same::value) + if constexpr (std::is_same::value) { mixedEventCont.setPair(p1, p2, multCol, confUse3D); - else + } else { mixedEventCont.setPair(p1, p2, multCol, confUse3D); + } } }; @@ -1243,11 +1403,11 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes MC mixed events for Track - V0 void processMCMixedEvent(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); auto groupPartsOne = partsOneMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); @@ -1259,11 +1419,13 @@ struct FemtoUniversePairTaskTrackV0Extended { return; } for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (static_cast(p1.pidCut()) != ConfTrkSelection.confTrkPDGCodePartOne) + if (static_cast(p1.pidCut()) != ConfTrkSelection.confTrkPDGCodePartOne) { continue; + } int pdgCode2 = static_cast(p2.pidCut()); - if ((ConfV0Selection.confV0Type1 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode2 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type1 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode2 != kLambda0Bar)) { continue; + } if (ConfCPR.confIsCPR.value) { if (pairCloseRejection.isClosePair(p1, p2, parts, magFieldTesla1, femto_universe_container::EventType::mixed)) { continue; @@ -1291,22 +1453,24 @@ struct FemtoUniversePairTaskTrackV0Extended { /// This function processes MC mixed events for V0 - V0 void processMCMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { - const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); + const int multCol = confUseCent ? static_cast(collision1.multV0M()) : collision1.multNtr(); auto groupPartsOne = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); auto groupPartsTwo = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision2.globalIndex(), cache); for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { int pdgCode1 = static_cast(p1.pidCut()); - if ((ConfV0Selection.confV0Type1 == 0 && pdgCode1 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode1 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type1 == 0 && pdgCode1 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode1 != kLambda0Bar)) { continue; + } int pdgCode2 = static_cast(p2.pidCut()); - if ((ConfV0Selection.confV0Type2 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && pdgCode2 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type2 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && pdgCode2 != kLambda0Bar)) { continue; + } mixedEventCont.setPair(p1, p2, multCol, confUse3D); } }; @@ -1332,8 +1496,9 @@ struct FemtoUniversePairTaskTrackV0Extended { void processMCTruth(aod::FDParticles const& parts) { for (const auto& part : parts) { - if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) + if (part.partType() != uint8_t(aod::femtouniverseparticle::ParticleType::kMCTruthTrack)) { continue; + } int pdgCode = static_cast(part.pidCut()); const auto& pdgParticle = pdgMC->GetParticle(pdgCode); @@ -1344,7 +1509,8 @@ struct FemtoUniversePairTaskTrackV0Extended { if (pdgCode == kLambda0) { registryMCtruth.fill(HIST("plus/MCtruthLambda"), part.pt(), part.eta()); continue; - } else if (pdgCode == kLambda0Bar) { + } + if (pdgCode == kLambda0Bar) { registryMCtruth.fill(HIST("minus/MCtruthLambda"), part.pt(), part.eta()); continue; } @@ -1379,11 +1545,11 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractions(FilteredFDCollisions const& cols, FemtoRecoParticles const& parts, aod::FdMCParticles const& mcparts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; - float v0DaughPtLowTable[3][2] = {{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}; - float v0DaughPtHighTable[3][2] = {{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}; + std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; + std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsOneMCRecoFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1398,11 +1564,13 @@ struct FemtoUniversePairTaskTrackV0Extended { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { // Lambda invariant mass cut - if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type1)) + if (!invMLambda(p2.mLambda(), p2.mAntiLambda(), ConfV0Selection.confV0Type1)) { continue; + } /// PID using stored binned nsigma - if (!isParticleCombined(p1, ConfTrkSelection.confTrackChoicePartOne)) + if (!isParticleCombined(p1, ConfTrkSelection.confTrackChoicePartOne)) { continue; + } const auto& posChild = parts.iteratorAt(p2.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(p2.globalIndex() - 1 - parts.begin().globalIndex()); @@ -1411,10 +1579,12 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; - if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; + } // track cleaning if (!pairCleaner.isCleanPair(p1, p2, parts)) { @@ -1428,11 +1598,13 @@ struct FemtoUniversePairTaskTrackV0Extended { registryMCreco.fill(HIST("mothersReco/motherParticle"), p1.motherPDG(), p2.motherPDG()); auto mcPartId1 = p1.fdMCParticleId(); - if (mcPartId1 == -1) + if (mcPartId1 == -1) { continue; + } auto mcPartId2 = p2.fdMCParticleId(); - if (mcPartId2 == -1) + if (mcPartId2 == -1) { continue; + } const auto& mcParticle1 = mcparts.iteratorAt(mcPartId1); const auto& mcParticle2 = mcparts.iteratorAt(mcPartId2); if (mcParticle1.pdgMCTruth() == ConfTrkSelection.confTrkPDGCodePartOne && mcParticle2.pdgMCTruth() == ConfV0Selection.confV0PDGCodePartTwo) { @@ -1449,11 +1621,11 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsV0(FilteredFDCollisions const& cols, FemtoRecoParticles const& parts, aod::FdMCParticles const& mcparts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; - float v0DaughPtLowTable[3][2] = {{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}; - float v0DaughPtHighTable[3][2] = {{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}; + std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; + std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsTwoMCRecoFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1483,10 +1655,12 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if (!isParticleTPC(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) + if (!isParticleTPC(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; - if (!isParticleTOF(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!isParticleTOF(posChild1, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild1, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; + } const auto& posChild2 = parts.iteratorAt(p2.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild2 = parts.iteratorAt(p2.globalIndex() - 1 - parts.begin().globalIndex()); @@ -1495,25 +1669,28 @@ struct FemtoUniversePairTaskTrackV0Extended { } /// Daughters that do not pass this condition are not selected - if (!isParticleTPC(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) + if (!isParticleTPC(posChild2, V0ChildTable[ConfV0Selection.confV0Type2][0]) || !isParticleTPC(negChild2, V0ChildTable[ConfV0Selection.confV0Type2][1])) { continue; - if (!isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type1][1])) + } + if (!isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild2, V0ChildTable[ConfV0Selection.confV0Type1][1])) { continue; + } // track cleaning if (!pairCleanerV0.isCleanPair(p1, p2, parts)) { continue; } if (ConfCPR.confIsCPR.value) { - double rand; + double rand = 0.0; auto part1 = p1; auto part2 = p2; + double randomizationValue = 0.5; if (ConfCPR.confRandomizeCPR) { randgen = new TRandom2(0); rand = randgen->Rndm(); - if (rand > 0.5) { + if (rand > randomizationValue) { part1 = p2; part2 = p1; } @@ -1526,17 +1703,21 @@ struct FemtoUniversePairTaskTrackV0Extended { registryMCreco.fill(HIST("mothersReco/motherParticle"), p1.motherPDG(), p2.motherPDG()); auto mcPartId1 = p1.fdMCParticleId(); - if (mcPartId1 == -1) + if (mcPartId1 == -1) { continue; + } auto mcPartId2 = p2.fdMCParticleId(); - if (mcPartId2 == -1) + if (mcPartId2 == -1) { continue; + } const auto& mcParticle1 = mcparts.iteratorAt(mcPartId1); const auto& mcParticle2 = mcparts.iteratorAt(mcPartId2); - if ((ConfV0Selection.confV0Type1 == 0 && mcParticle1.pdgMCTruth() != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && mcParticle1.pdgMCTruth() != kLambda0Bar)) + if ((ConfV0Selection.confV0Type1 == 0 && mcParticle1.pdgMCTruth() != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && mcParticle1.pdgMCTruth() != kLambda0Bar)) { continue; - if ((ConfV0Selection.confV0Type2 == 0 && mcParticle2.pdgMCTruth() != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && mcParticle2.pdgMCTruth() != kLambda0Bar)) + } + if ((ConfV0Selection.confV0Type2 == 0 && mcParticle2.pdgMCTruth() != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && mcParticle2.pdgMCTruth() != kLambda0Bar)) { continue; + } registryMCreco.fill(HIST("mothersReco/motherParticlePDGCheck"), p1.motherPDG(), p2.motherPDG()); } @@ -1556,8 +1737,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsMCTruth(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsOneMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1567,10 +1748,12 @@ struct FemtoUniversePairTaskTrackV0Extended { int pdgCode1 = static_cast(p1.pidCut()); int pdgCode2 = static_cast(p2.pidCut()); - if (pdgCode1 != ConfTrkSelection.confTrkPDGCodePartOne) + if (pdgCode1 != ConfTrkSelection.confTrkPDGCodePartOne) { continue; - if (pdgCode2 != ConfV0Selection.confV0PDGCodePartTwo) + } + if (pdgCode2 != ConfV0Selection.confV0PDGCodePartTwo) { continue; + } registryMCtruth.fill(HIST("mothersTruth/motherParticle"), p1.tempFitVar(), p2.tempFitVar()); registryMCtruth.fill(HIST("mothersTruth/mcProcess"), p1.cut(), p2.cut()); @@ -1591,8 +1774,8 @@ struct FemtoUniversePairTaskTrackV0Extended { void processPairFractionsMCTruthV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { auto groupPartsOne = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, collision1.globalIndex(), cache); @@ -1600,11 +1783,13 @@ struct FemtoUniversePairTaskTrackV0Extended { for (const auto& [p1, p2] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { int pdgCode1 = static_cast(p1.pidCut()); - if ((ConfV0Selection.confV0Type1 == 0 && pdgCode1 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode1 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type1 == 0 && pdgCode1 != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && pdgCode1 != kLambda0Bar)) { continue; + } int pdgCode2 = static_cast(p2.pidCut()); - if ((ConfV0Selection.confV0Type2 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && pdgCode2 != kLambda0Bar)) + if ((ConfV0Selection.confV0Type2 == 0 && pdgCode2 != kLambda0) || (ConfV0Selection.confV0Type2 == 1 && pdgCode2 != kLambda0Bar)) { continue; + } registryMCtruth.fill(HIST("mothersTruth/motherParticle"), p1.tempFitVar(), p2.tempFitVar()); registryMCtruth.fill(HIST("mothersTruth/mcProcess"), p1.cut(), p2.cut()); @@ -1623,66 +1808,160 @@ struct FemtoUniversePairTaskTrackV0Extended { } PROCESS_SWITCH(FemtoUniversePairTaskTrackV0Extended, processPairFractionsMCTruthV0, "Process MC data to obtain pair fractions for V0V0 MC truth pairs", false); + void processFractionsSingleReco(FilteredFDCollision const& col, FemtoRecoParticles const& parts, aod::FdMCParticles const& mcparts) + { + auto groupPartsOne = partsOneMCRecoFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsTwo = partsTwoMCRecoFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + + std::array, 3> v0DaughPtLowTable = {{{ConfV0Selection.confLPtChildProton, ConfV0Selection.confLPtChildPion}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildProton}, {ConfV0Selection.confLPtChildPion, ConfV0Selection.confLPtChildPion}}}; + std::array, 3> v0DaughPtHighTable = {{{ConfV0Selection.confHPtChildProton, ConfV0Selection.confHPtChildPion}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildProton}, {ConfV0Selection.confHPtChildPion, ConfV0Selection.confHPtChildPion}}}; + + for (const auto& part : groupPartsTwo) { + if (!invMLambda(part.mLambda(), part.mAntiLambda(), ConfV0Selection.confV0Type1)) { + continue; + } + const auto& posChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); + const auto& negChild = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); + + if (posChild.pt() < v0DaughPtLowTable[ConfV0Selection.confV0Type1][0] || negChild.pt() < v0DaughPtLowTable[ConfV0Selection.confV0Type1][1] || posChild.pt() > v0DaughPtHighTable[ConfV0Selection.confV0Type1][0] || negChild.pt() > v0DaughPtHighTable[ConfV0Selection.confV0Type1][1]) { + continue; + } + + if (!isParticleTPC(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTPC(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { + continue; + } + + if (!isParticleTOF(posChild, V0ChildTable[ConfV0Selection.confV0Type1][0]) || !isParticleTOF(negChild, V0ChildTable[ConfV0Selection.confV0Type1][1])) { + continue; + } + + registryMCreco.fill(HIST("mothersReco/motherParticleV0"), part.motherPDG()); + auto mcPartId1 = part.fdMCParticleId(); + if (mcPartId1 == -1) { + continue; + } + const auto& mcParticle1 = mcparts.iteratorAt(mcPartId1); + if ((ConfV0Selection.confV0Type1 == 0 && mcParticle1.pdgMCTruth() != kLambda0) || (ConfV0Selection.confV0Type1 == 1 && mcParticle1.pdgMCTruth() != kLambda0Bar)) { + continue; + } + registryMCreco.fill(HIST("mothersReco/motherParticleV0PDGCheck"), part.motherPDG()); + } + + for (const auto& part : groupPartsOne) { + if (!isParticleCombined(part, ConfTrkSelection.confTrackChoicePartOne)) { + continue; + } + registryMCreco.fill(HIST("mothersReco/motherParticleTrack"), part.motherPDG()); + auto mcPartId1 = part.fdMCParticleId(); + if (mcPartId1 == -1) { + continue; + } + const auto& mcParticle1 = mcparts.iteratorAt(mcPartId1); + if (mcParticle1.pdgMCTruth() != ConfTrkSelection.confTrkPDGCodePartOne) { + continue; + } + registryMCreco.fill(HIST("mothersReco/motherParticleTrackPDGCheck"), part.motherPDG()); + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackV0Extended, processFractionsSingleReco, "Process MC data to obtain fractions for V0 and protons particles", false); + + void processFractionsSingleTruth(FilteredFDCollision const& col, FemtoFullParticles const&) + { + auto groupPartsOne = partsOneMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + auto groupPartsTwo = partsTwoMCFull->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); + // MC truth + for (const auto& part : groupPartsOne) { + int pdgCode1 = static_cast(part.pidCut()); + if (pdgCode1 != ConfTrkSelection.confTrkPDGCodePartOne) { + continue; + } + registryMCtruth.fill(HIST("mothersTruth/motherParticleTrack"), part.tempFitVar()); + registryMCtruth.fill(HIST("mothersTruth/mcProcessTrack"), part.cut()); + } + for (const auto& part : groupPartsTwo) { + int pdgCode2 = static_cast(part.pidCut()); + if (pdgCode2 != ConfV0Selection.confV0PDGCodePartTwo) { + continue; + } + registryMCtruth.fill(HIST("mothersTruth/motherParticleV0"), part.tempFitVar()); + registryMCtruth.fill(HIST("mothersTruth/mcProcessV0"), part.cut()); + } + } + PROCESS_SWITCH(FemtoUniversePairTaskTrackV0Extended, processFractionsSingleTruth, "Process MC data to obtain fractions for V0 and protons particles", false); + template void doMCReco(PartType const& parts, aod::FdMCParticles const& mcparts) { for (const auto& part : parts) { auto mcPartId = part.fdMCParticleId(); - if (mcPartId == -1) + if (mcPartId == -1) { continue; // no MC particle + } const auto& mcpart = mcparts.iteratorAt(mcPartId); // if (part.partType() == aod::femtouniverseparticle::ParticleType::kV0) { if (mcpart.pdgMCTruth() == kLambda0) { - if (!invMLambda(part.mLambda(), part.mAntiLambda(), 0)) + if (!invMLambda(part.mLambda(), part.mAntiLambda(), 0)) { continue; + } const auto& posChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); - if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { /// Daughters that do not pass this condition are not selected - if (!isParticleTPC(posChild, 0) || !isParticleTPC(negChild, 1)) + if (!isParticleTPC(posChild, 0) || !isParticleTPC(negChild, 1)) { continue; + } - if (!isParticleTOF(posChild, 0) || !isParticleTOF(negChild, 1)) + if (!isParticleTOF(posChild, 0) || !isParticleTOF(negChild, 1)) { continue; + } } else { - if ((posChild.pidCut() & (1u << 0)) == 0 || (negChild.pidCut() & (1u << 1)) == 0) + if ((posChild.pidCut() & (1u << 0)) == 0 || (negChild.pidCut() & (1u << 1)) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if ((part.pidCut() & 3) != 3) + if ((part.pidCut() & 3) != 3) { continue; + } } else { - if ((posChild.pidCut() & (8u << 0)) == 0 || (negChild.pidCut() & (8u << 1)) == 0) + if ((posChild.pidCut() & (8u << 0)) == 0 || (negChild.pidCut() & (8u << 1)) == 0) { continue; + } } } registryMCreco.fill(HIST("plus/MCrecoLambda"), mcpart.pt(), mcpart.eta()); // lambda } else if (mcpart.pdgMCTruth() == kLambda0Bar) { - if (!invMLambda(part.mLambda(), part.mAntiLambda(), 1)) + if (!invMLambda(part.mLambda(), part.mAntiLambda(), 1)) { continue; + } const auto& posChild = parts.iteratorAt(part.globalIndex() - 2 - parts.begin().globalIndex()); const auto& negChild = parts.iteratorAt(part.globalIndex() - 1 - parts.begin().globalIndex()); - if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { /// Daughters that do not pass this condition are not selected - if (!isParticleTPC(posChild, 1) || !isParticleTPC(negChild, 0)) + if (!isParticleTPC(posChild, 1) || !isParticleTPC(negChild, 0)) { continue; + } - if (!isParticleTOF(posChild, 1) || !isParticleTOF(negChild, 0)) + if (!isParticleTOF(posChild, 1) || !isParticleTOF(negChild, 0)) { continue; + } } else { - if ((posChild.pidCut() & (1u << 1)) == 0 || (negChild.pidCut() & (1u << 0)) == 0) + if ((posChild.pidCut() & (1u << 1)) == 0 || (negChild.pidCut() & (1u << 0)) == 0) { continue; + } if (ConfV0Selection.confUseStrangenessTOF) { - if ((part.pidCut() & 12) != 12) + if ((part.pidCut() & 12) != 12) { continue; + } } else { - if ((posChild.pidCut() & (8u << 1)) == 0 || (negChild.pidCut() & (8u << 0)) == 0) + if ((posChild.pidCut() & (8u << 1)) == 0 || (negChild.pidCut() & (8u << 0)) == 0) { continue; + } } } registryMCreco.fill(HIST("minus/MCrecoLambda"), mcpart.pt(), mcpart.eta()); // anti-lambda @@ -1691,13 +1970,15 @@ struct FemtoUniversePairTaskTrackV0Extended { if (part.mAntiLambda() > 0) { // mAntiLambda is the sign here registryMCreco.fill(HIST("plus/MCrecoAllPt"), mcpart.pt()); if (mcpart.pdgMCTruth() == kPiPlus) { - if constexpr (std::experimental::is_detected::value) { - if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), (part.pidCut() & 512u) != 0)) + if constexpr (std::experimental::is_detected::value) { + if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), (part.pidCut() & 512u) != 0)) { continue; + } } else { if ((part.pidCut() & 512u) != 0) { - if ((part.pidCut() & 128u) == 0) // 128 for pion combined + if ((part.pidCut() & 128u) == 0) { // 128 for pion combined continue; + } } else if ((part.pidCut() & 2u) == 0) { continue; } @@ -1705,12 +1986,14 @@ struct FemtoUniversePairTaskTrackV0Extended { registryMCreco.fill(HIST("plus/MCrecoPi"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("plus/MCrecoPiPt"), mcpart.pt()); } else if (mcpart.pdgMCTruth() == kProton) { - if constexpr (std::experimental::is_detected::value) { - if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) + if constexpr (std::experimental::is_detected::value) { + if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) { continue; + } } else { - if (!isNSigmaCombinedBitmask(part.p(), part)) + if (!isNSigmaCombinedBitmask(part.p(), part)) { continue; + } } registryMCreco.fill(HIST("plus/MCrecoPr"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("plus/MCrecoPrPt"), mcpart.pt()); @@ -1718,13 +2001,15 @@ struct FemtoUniversePairTaskTrackV0Extended { } else if (part.mAntiLambda() < 0) { registryMCreco.fill(HIST("minus/MCrecoAllPt"), mcpart.pt()); if (mcpart.pdgMCTruth() == kPiMinus) { - if constexpr (std::experimental::is_detected::value) { - if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), (part.pidCut() & 512u) != 0)) + if constexpr (std::experimental::is_detected::value) { + if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()), (part.pidCut() & 512u) != 0)) { continue; + } } else { if ((part.pidCut() & 512u) != 0) { - if ((part.pidCut() & 128u) == 0) // 128 for pion combined + if ((part.pidCut() & 128u) == 0) { // 128 for pion combined continue; + } } else if ((part.pidCut() & 2u) == 0) { continue; } @@ -1732,12 +2017,14 @@ struct FemtoUniversePairTaskTrackV0Extended { registryMCreco.fill(HIST("minus/MCrecoPi"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("minus/MCrecoPiPt"), mcpart.pt()); } else if (mcpart.pdgMCTruth() == kProtonBar) { - if constexpr (std::experimental::is_detected::value) { - if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) + if constexpr (std::experimental::is_detected::value) { + if (!isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()), (part.pidCut() & 512u) != 0)) { continue; + } } else { - if (!isNSigmaCombinedBitmask(part.p(), part)) + if (!isNSigmaCombinedBitmask(part.p(), part)) { continue; + } } registryMCreco.fill(HIST("minus/MCrecoPr"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("minus/MCrecoPrPt"), mcpart.pt()); diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx index 972f511e926..f7501d0388c 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskTrackV0Helicity.cxx @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -181,13 +182,22 @@ struct FemtoUniversePairTaskTrackV0Helicity { } twotracksconfigs; /// Helicity ranges and configurables - Configurable cfgProcessHel{"cfgProcessHel", true, "Process particle pairs from all helicity ranges"}; - Configurable cfgProcessHel1{"cfgProcessHel1", false, "Process particle pairs from the helicity range 1"}; // 1.0 >= cosineTheta >= 0.1 - Configurable cfgProcessHel2{"cfgProcessHel2", false, "Process particle pairs from the helicity range 2"}; // 0.1 > cosineTheta >= -0.1 - Configurable cfgProcessHel3{"cfgProcessHel3", false, "Process particle pairs from the helicity range 3"}; // -0.1 > cosineTheta >= -0.5 - Configurable cfgProcessHel4{"cfgProcessHel4", false, "Process particle pairs from the helicity range 4"}; // -0.5 > cosineTheta >= -1.0 - ConfigurableAxis confInvMassMotherpTBinsHel{"confInvMassMotherpTBinsHel", {5, 0, 5}, "pT binning in the pT vs. InvMassMother plot for helicity"}; - ConfigurableAxis confInvMassMotherBinsHel{"confInvMassMotherBinsHel", {1000, 0.8, 1.4}, "InvMassMother binning in the pT vs. InvMassMother plot for helicity"}; + struct : o2::framework::ConfigurableGroup { + Configurable cfgProcessHel{"cfgProcessHel", true, "Process particle pairs from all helicity ranges"}; + Configurable cfgProcessHel1{"cfgProcessHel1", false, "Process particle pairs from the helicity range 1"}; // 1.0 >= cosineTheta >= 0.1 + Configurable cfgProcessHel2{"cfgProcessHel2", false, "Process particle pairs from the helicity range 2"}; // 0.1 > cosineTheta >= -0.1 + Configurable cfgProcessHel3{"cfgProcessHel3", false, "Process particle pairs from the helicity range 3"}; // -0.1 > cosineTheta >= -0.5 + Configurable cfgProcessHel4{"cfgProcessHel4", false, "Process particle pairs from the helicity range 4"}; // -0.5 > cosineTheta >= -1.0 + Configurable confLimitHel0{"confLimitHel0", 1.0, "Highest value of the helicity angle"}; + Configurable confLimitHel1{"confLimitHel1", 0.1, "Border value between ranges 1 and 2"}; + Configurable confLimitHel2{"confLimitHel2", -0.1, "Border value between ranges 2 and 3"}; + Configurable confLimitHel3{"confLimitHel3", -0.5, "Border value between ranges 3 and 4"}; + Configurable confLimitHel4{"confLimitHel4", -1.0, "Lowest value of the helicity angle"}; + ConfigurableAxis confInvMassMotherpTBinsHel{"confInvMassMotherpTBinsHel", {5, 0, 5}, "pT binning in the pT vs. InvMassMother plot for helicity"}; + ConfigurableAxis confInvMassMotherBinsHel{"confInvMassMotherBinsHel", {1000, 0.8, 1.4}, "InvMassMother binning in the pT vs. InvMassMother plot for helicity"}; + Configurable isRealK0Short{"isRealK0Short", true, "If true, fill invariant mass for real K0Short; if false, fill invariant mass as if dealing with Lambda (needed for estimation of K0Short fraction in the Lambda sample)"}; + ConfigurableAxis confInvMassK0Short{"confInvMassK0Short", {1000, 0.2, 0.8}, "Invariant mass binning for K0 Short"}; // o2-linter: disable=lowerCamelCase (consistency with generally accepted particle name) + } helicityconfigs; /// Efficiency Configurable confLocalEfficiency{"confLocalEfficiency", "", "Local path to efficiency .root file"}; @@ -317,10 +327,20 @@ struct FemtoUniversePairTaskTrackV0Helicity { thetaRegistry.add("Theta/NegativeChild/hThetaPt", " ; p_{T} (GeV/#it{c}); cos(#theta)", kTH2F, {{100, 0, 10}, {110, -1.1, 1.1}}); thetaRegistry.add("Theta/NegativeChild/hThetaEta", " ; #eta; cos(#theta)", kTH2F, {{100, -1, 1}, {110, -1.1, 1.1}}); thetaRegistry.add("Theta/NegativeChild/hThetaPhi", " ; #phi; cos(#theta)", kTH2F, {{100, -1, 7}, {110, -1.1, 1.1}}); - thetaRegistry.add("Theta/Mother/hInvMassMotherHel1", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {confInvMassMotherpTBinsHel, confInvMassMotherBinsHel}); - thetaRegistry.add("Theta/Mother/hInvMassMotherHel2", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {confInvMassMotherpTBinsHel, confInvMassMotherBinsHel}); - thetaRegistry.add("Theta/Mother/hInvMassMotherHel3", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {confInvMassMotherpTBinsHel, confInvMassMotherBinsHel}); - thetaRegistry.add("Theta/Mother/hInvMassMotherHel4", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {confInvMassMotherpTBinsHel, confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/Data/hInvMassLambdaHel1", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {helicityconfigs.confInvMassMotherpTBinsHel, helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/Data/hInvMassLambdaHel2", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {helicityconfigs.confInvMassMotherpTBinsHel, helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/Data/hInvMassLambdaHel3", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {helicityconfigs.confInvMassMotherpTBinsHel, helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/Data/hInvMassLambdaHel4", " ; p_{T} (GeV/#it{c}); M_{#Lambda};", kTH2F, {helicityconfigs.confInvMassMotherpTBinsHel, helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassLambdaMC", " ; M_{#Lambda}; ;", kTH1F, {helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassLambdaMCHel1", " ; M_{#Lambda}; ;", kTH1F, {helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassLambdaMCHel2", " ; M_{#Lambda}; ;", kTH1F, {helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassLambdaMCHel3", " ; M_{#Lambda}; ;", kTH1F, {helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassLambdaMCHel4", " ; M_{#Lambda}; ;", kTH1F, {helicityconfigs.confInvMassMotherBinsHel}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassK0ShortMC", " ; M_{K^{0}_{S}}; ;", kTH1F, {helicityconfigs.confInvMassK0Short}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassK0ShortMCHel1", " ; M_{K^{0}_{S}}; ;", kTH1F, {helicityconfigs.confInvMassK0Short}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassK0ShortMCHel2", " ; M_{K^{0}_{S}}; ;", kTH1F, {helicityconfigs.confInvMassK0Short}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassK0ShortMCHel3", " ; M_{K^{0}_{S}}; ;", kTH1F, {helicityconfigs.confInvMassK0Short}); + thetaRegistry.add("Theta/Mother/MCReco/hInvMassK0ShortMCHel4", " ; M_{K^{0}_{S}}; ;", kTH1F, {helicityconfigs.confInvMassK0Short}); /// MC Truth registryMCtruth.add("plus/MCtruthLambda", "MC truth Lambdas;#it{p}_{T} (GeV/c); #eta", {HistType::kTH2F, {{500, 0, 5}, {400, -1.0, 1.0}}}); @@ -384,7 +404,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { registryMCreco.add("ThetaMCReco/NegativeChild/hThetaPhi", " ; #phi; cos(#theta)", kTH2F, {{100, -1, 7}, {110, -1.1, 1.1}}); /// Correlations - if (cfgProcessHel) { + if (helicityconfigs.cfgProcessHel) { sameEventCont.init(&resultRegistry, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); sameEventCont.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); @@ -392,7 +412,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { mixedEventCont.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); } - if (cfgProcessHel1) { + if (helicityconfigs.cfgProcessHel1) { sameEventContHel1.init(&resultRegistryHel1, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); sameEventContHel1.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); @@ -400,7 +420,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { mixedEventContHel1.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); } - if (cfgProcessHel2) { + if (helicityconfigs.cfgProcessHel2) { sameEventContHel2.init(&resultRegistryHel2, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); sameEventContHel2.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); @@ -408,7 +428,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { mixedEventContHel2.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); } - if (cfgProcessHel3) { + if (helicityconfigs.cfgProcessHel3) { sameEventContHel3.init(&resultRegistryHel3, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); sameEventContHel3.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); @@ -416,7 +436,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { mixedEventContHel3.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); } - if (cfgProcessHel4) { + if (helicityconfigs.cfgProcessHel4) { sameEventContHel4.init(&resultRegistryHel4, confkstarBins, confMultBins, confkTBins, confmTBins, confMultBins3D, confmTBins3D, confEtaBins, confPhiBins, confIsMC, confUse3D); sameEventContHel4.setPDGCodes(trackconfigs.confTrkPDGCodePartOne, V0configs.confV0PDGCodePartTwo); @@ -450,8 +470,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { } /// This function processes the same event for Track-V0 - template - void doSameEvent(FilteredFDCollision const& col, PartType const& parts, PartitionType& groupPartsOne, PartitionType& groupPartsTwo, int helRange, [[maybe_unused]] MCParticles mcParts = nullptr) + template + void doSameEvent(FilteredFDCollision const& col, PartType const& parts, PartitionType& groupPartsOne, PartitionType& groupPartsTwo, int helRange, [[maybe_unused]] const MCParticles& mcParts = nullptr) { const auto& magFieldTesla = col.magField(); @@ -488,14 +508,62 @@ struct FemtoUniversePairTaskTrackV0Helicity { thetaRegistry.fill(HIST("Theta/NegativeChild/hThetaEta"), negChild.eta(), cosineTheta); thetaRegistry.fill(HIST("Theta/NegativeChild/hThetaPhi"), negChild.phi(), cosineTheta); - if (cosineTheta <= 1.0 && cosineTheta >= 0.1) - thetaRegistry.fill(HIST("Theta/Mother/hInvMassMotherHel1"), part.pt(), part.mLambda()); - else if (cosineTheta < 0.1 && cosineTheta >= -0.1) - thetaRegistry.fill(HIST("Theta/Mother/hInvMassMotherHel2"), part.pt(), part.mLambda()); - else if (cosineTheta < -0.1 && cosineTheta >= -0.5) - thetaRegistry.fill(HIST("Theta/Mother/hInvMassMotherHel3"), part.pt(), part.mLambda()); - else if (cosineTheta < -0.5 && cosineTheta >= -1) - thetaRegistry.fill(HIST("Theta/Mother/hInvMassMotherHel4"), part.pt(), part.mLambda()); + if (cosineTheta <= helicityconfigs.confLimitHel0 && cosineTheta >= helicityconfigs.confLimitHel1) + thetaRegistry.fill(HIST("Theta/Mother/Data/hInvMassLambdaHel1"), part.pt(), part.mLambda()); + else if (cosineTheta < helicityconfigs.confLimitHel1 && cosineTheta >= helicityconfigs.confLimitHel2) + thetaRegistry.fill(HIST("Theta/Mother/Data/hInvMassLambdaHel2"), part.pt(), part.mLambda()); + else if (cosineTheta < helicityconfigs.confLimitHel2 && cosineTheta >= helicityconfigs.confLimitHel3) + thetaRegistry.fill(HIST("Theta/Mother/Data/hInvMassLambdaHel3"), part.pt(), part.mLambda()); + else if (cosineTheta < helicityconfigs.confLimitHel3 && cosineTheta >= helicityconfigs.confLimitHel4) + thetaRegistry.fill(HIST("Theta/Mother/Data/hInvMassLambdaHel4"), part.pt(), part.mLambda()); + + /// Histogramming for MC Reco to calculate fraction of K0Short in the Lambda sample for each helicity bin + if constexpr (confIsMC) { + if (part.has_fdMCParticle()) { + if ((part.fdMCParticle()).pdgMCTruth() == kLambda0) + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassLambdaMC"), part.mLambda()); + else if ((part.fdMCParticle()).pdgMCTruth() == kK0Short) + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMC"), part.mKaon()); + + if (cosineTheta <= helicityconfigs.confLimitHel0 && cosineTheta >= helicityconfigs.confLimitHel1) { + if ((part.fdMCParticle()).pdgMCTruth() == kLambda0) { + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassLambdaMCHel1"), part.mLambda()); + } else if ((part.fdMCParticle()).pdgMCTruth() == kK0Short) { + if (helicityconfigs.isRealK0Short) + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel1"), part.mKaon()); + else + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel1"), part.mLambda()); + } + } else if (cosineTheta < helicityconfigs.confLimitHel1 && cosineTheta >= helicityconfigs.confLimitHel2) { + if ((part.fdMCParticle()).pdgMCTruth() == kLambda0) { + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassLambdaMCHel2"), part.mLambda()); + } else if ((part.fdMCParticle()).pdgMCTruth() == kK0Short) { + if (helicityconfigs.isRealK0Short) + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel2"), part.mKaon()); + else + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel2"), part.mLambda()); + } + } else if (cosineTheta < helicityconfigs.confLimitHel2 && cosineTheta >= helicityconfigs.confLimitHel3) { + if ((part.fdMCParticle()).pdgMCTruth() == kLambda0) { + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassLambdaMCHel3"), part.mLambda()); + } else if ((part.fdMCParticle()).pdgMCTruth() == kK0Short) { + if (helicityconfigs.isRealK0Short) + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel3"), part.mKaon()); + else + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel3"), part.mLambda()); + } + } else if (cosineTheta < helicityconfigs.confLimitHel3 && cosineTheta >= helicityconfigs.confLimitHel4) { + if ((part.fdMCParticle()).pdgMCTruth() == kLambda0) { + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassLambdaMCHel4"), part.mLambda()); + } else if ((part.fdMCParticle()).pdgMCTruth() == kK0Short) { + if (helicityconfigs.isRealK0Short) + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel4"), part.mKaon()); + else + thetaRegistry.fill(HIST("Theta/Mother/MCReco/hInvMassK0ShortMCHel4"), part.mLambda()); + } + } + } + } } for (const auto& part : groupPartsOne) { @@ -560,28 +628,28 @@ struct FemtoUniversePairTaskTrackV0Helicity { } case 1: { - if (cosineTheta <= 1.0 && cosineTheta >= 0.1) + if (cosineTheta < helicityconfigs.confLimitHel0 && cosineTheta >= helicityconfigs.confLimitHel1) sameEventContHel1.setPair(p1, p2, multCol, confUse3D, weight); break; } case 2: { - if (cosineTheta < 0.1 && cosineTheta >= -0.1) + if (cosineTheta < helicityconfigs.confLimitHel1 && cosineTheta >= helicityconfigs.confLimitHel2) sameEventContHel2.setPair(p1, p2, multCol, confUse3D, weight); break; } case 3: { - if (cosineTheta < -0.1 && cosineTheta >= -0.5) + if (cosineTheta < helicityconfigs.confLimitHel2 && cosineTheta >= helicityconfigs.confLimitHel3) sameEventContHel3.setPair(p1, p2, multCol, confUse3D, weight); break; } case 4: { - if (cosineTheta < -0.5 && cosineTheta >= -1.0) + if (cosineTheta < helicityconfigs.confLimitHel3 && cosineTheta >= helicityconfigs.confLimitHel4) sameEventContHel4.setPair(p1, p2, multCol, confUse3D, weight); break; @@ -598,20 +666,20 @@ struct FemtoUniversePairTaskTrackV0Helicity { auto groupPartsOne = partsOne->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); auto groupPartsTwo = partsTwo->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - if (cfgProcessHel) - doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 0); + if (helicityconfigs.cfgProcessHel) + doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 0); - if (cfgProcessHel1) - doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 1); + if (helicityconfigs.cfgProcessHel1) + doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 1); - if (cfgProcessHel2) - doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 2); + if (helicityconfigs.cfgProcessHel2) + doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 2); - if (cfgProcessHel3) - doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 3); + if (helicityconfigs.cfgProcessHel3) + doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 3); - if (cfgProcessHel4) - doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 4); + if (helicityconfigs.cfgProcessHel4) + doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 4); } PROCESS_SWITCH(FemtoUniversePairTaskTrackV0Helicity, processSameEvent, "Enable processing same event for track - V0", false); @@ -619,7 +687,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { { auto groupPartsOne = partsOneMCReco->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); auto groupPartsTwo = partsTwoMCReco->sliceByCached(aod::femtouniverseparticle::fdCollisionId, col.globalIndex(), cache); - doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 0, mcparts); + doSameEvent(col, parts, groupPartsOne, groupPartsTwo, 0, mcparts); } PROCESS_SWITCH(FemtoUniversePairTaskTrackV0Helicity, processSameEventMCReco, "Enable processing same event for track - V0 MC Reco", false); @@ -796,10 +864,10 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes the mixed event for track - V0 template - void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partitionOne, PartitionType& partitionTwo, int helRange, [[maybe_unused]] MCParticles mcParts = nullptr) + void doMixedEvent(FilteredFDCollisions const& cols, PartType const& parts, PartitionType& partitionOne, PartitionType& partitionTwo, int helRange, [[maybe_unused]] const MCParticles& mcParts = nullptr) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -858,28 +926,28 @@ struct FemtoUniversePairTaskTrackV0Helicity { } case 1: { - if (cosineTheta <= 1.0 && cosineTheta >= 0.1) + if (cosineTheta < helicityconfigs.confLimitHel0 && cosineTheta >= helicityconfigs.confLimitHel1) mixedEventContHel1.setPair(p1, p2, multCol, confUse3D, weight); break; } case 2: { - if (cosineTheta < 0.1 && cosineTheta >= -0.1) + if (cosineTheta < helicityconfigs.confLimitHel1 && cosineTheta >= helicityconfigs.confLimitHel2) mixedEventContHel2.setPair(p1, p2, multCol, confUse3D, weight); break; } case 3: { - if (cosineTheta < -0.1 && cosineTheta >= -0.5) + if (cosineTheta < helicityconfigs.confLimitHel2 && cosineTheta >= helicityconfigs.confLimitHel3) mixedEventContHel3.setPair(p1, p2, multCol, confUse3D, weight); break; } case 4: { - if (cosineTheta < -0.5 && cosineTheta >= -1.0) + if (cosineTheta < helicityconfigs.confLimitHel3 && cosineTheta >= helicityconfigs.confLimitHel4) mixedEventContHel4.setPair(p1, p2, multCol, confUse3D, weight); break; @@ -906,19 +974,19 @@ struct FemtoUniversePairTaskTrackV0Helicity { void processMixedEvent(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - if (cfgProcessHel) + if (helicityconfigs.cfgProcessHel) doMixedEvent(cols, parts, partsOne, partsTwo, 0); - if (cfgProcessHel1) + if (helicityconfigs.cfgProcessHel1) doMixedEvent(cols, parts, partsOne, partsTwo, 1); - if (cfgProcessHel2) + if (helicityconfigs.cfgProcessHel2) doMixedEvent(cols, parts, partsOne, partsTwo, 2); - if (cfgProcessHel3) + if (helicityconfigs.cfgProcessHel3) doMixedEvent(cols, parts, partsOne, partsTwo, 3); - if (cfgProcessHel4) + if (helicityconfigs.cfgProcessHel4) doMixedEvent(cols, parts, partsOne, partsTwo, 4); } PROCESS_SWITCH(FemtoUniversePairTaskTrackV0Helicity, processMixedEvent, "Enable processing mixed event for track - V0", false); @@ -932,8 +1000,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes the mixed event for V0 - V0 void processMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1000,8 +1068,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes MC mixed events for Track - V0 void processMCMixedEvent(FilteredFDCollisions const& cols, FemtoFullParticles const& parts) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1048,8 +1116,8 @@ struct FemtoUniversePairTaskTrackV0Helicity { /// This function processes MC mixed events for V0 - V0 void processMCMixedEventV0(FilteredFDCollisions const& cols, FemtoFullParticles const& /*parts*/) { - ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinningMult{{confVtxBins, confMultBins}}; + ColumnBinningPolicy colBinningCent{{confVtxBins, confMultBins}}; auto mixedCollProcessFunc = [&](auto& collision1, auto& collision2) -> void { const int multCol = confUseCent ? collision1.multV0M() : collision1.multNtr(); @@ -1099,7 +1167,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { if (!pdgParticle) continue; - if (pdgCode == 3122) { + if (pdgCode == kLambda0) { registryMCtruth.fill(HIST("plus/MCtruthLambda"), part.pt(), part.eta()); // Helicity angle @@ -1125,7 +1193,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { registryMCtruth.fill(HIST("ThetaMCTruth/NegativeChild/hThetaPhi"), negChild.phi(), cosineTheta); continue; - } else if (pdgCode == -3122) { + } else if (pdgCode == -kLambda0) { registryMCtruth.fill(HIST("minus/MCtruthLambda"), part.pt(), part.eta()); continue; } @@ -1133,11 +1201,11 @@ struct FemtoUniversePairTaskTrackV0Helicity { if (pdgParticle->Charge() > 0.0) { registryMCtruth.fill(HIST("plus/MCtruthAllPt"), part.pt()); } - if (pdgCode == 211) { + if (pdgCode == kPiPlus) { registryMCtruth.fill(HIST("plus/MCtruthPi"), part.pt(), part.eta()); registryMCtruth.fill(HIST("plus/MCtruthPiPt"), part.pt()); } - if (pdgCode == 2212) { + if (pdgCode == kProton) { registryMCtruth.fill(HIST("plus/MCtruthPr"), part.pt(), part.eta()); registryMCtruth.fill(HIST("plus/MCtruthPrPt"), part.pt()); } @@ -1145,11 +1213,11 @@ struct FemtoUniversePairTaskTrackV0Helicity { if (pdgParticle->Charge() < 0.0) { registryMCtruth.fill(HIST("minus/MCtruthAllPt"), part.pt()); } - if (pdgCode == -211) { + if (pdgCode == kPiMinus) { registryMCtruth.fill(HIST("minus/MCtruthPi"), part.pt(), part.eta()); registryMCtruth.fill(HIST("minus/MCtruthPiPt"), part.pt()); } - if (pdgCode == -2212) { + if (pdgCode == -kProton) { registryMCtruth.fill(HIST("minus/MCtruthPr"), part.pt(), part.eta()); registryMCtruth.fill(HIST("minus/MCtruthPrPt"), part.pt()); } @@ -1166,7 +1234,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { const auto& mcpart = mcparts.iteratorAt(mcPartId); // if (part.partType() == aod::femtouniverseparticle::ParticleType::kV0) { - if (mcpart.pdgMCTruth() == 3122) { + if (mcpart.pdgMCTruth() == kLambda0) { const auto& posChild = parts.iteratorAt(part.globalIndex() - 2); const auto& negChild = parts.iteratorAt(part.globalIndex() - 1); /// Daughters that do not pass this condition are not selected @@ -1181,7 +1249,7 @@ struct FemtoUniversePairTaskTrackV0Helicity { registryMCreco.fill(HIST("plus/MCrecoLambdaChildPi"), mcpartChild.pt(), mcpartChild.eta()); // lambda pion child } } - } else if (mcpart.pdgMCTruth() == -3122) { + } else if (mcpart.pdgMCTruth() == -kLambda0) { const auto& posChild = parts.iteratorAt(part.globalIndex() - 2); const auto& negChild = parts.iteratorAt(part.globalIndex() - 1); /// Daughters that do not pass this condition are not selected @@ -1200,10 +1268,10 @@ struct FemtoUniversePairTaskTrackV0Helicity { } else if (part.partType() == aod::femtouniverseparticle::ParticleType::kTrack) { if (part.sign() > 0) { registryMCreco.fill(HIST("plus/MCrecoAllPt"), mcpart.pt()); - if (mcpart.pdgMCTruth() == 211 && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()))) { + if (mcpart.pdgMCTruth() == kPiPlus && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()))) { registryMCreco.fill(HIST("plus/MCrecoPi"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("plus/MCrecoPiPt"), mcpart.pt()); - } else if (mcpart.pdgMCTruth() == 2212 && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()))) { + } else if (mcpart.pdgMCTruth() == kProton && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()))) { registryMCreco.fill(HIST("plus/MCrecoPr"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("plus/MCrecoPrPt"), mcpart.pt()); } @@ -1211,10 +1279,10 @@ struct FemtoUniversePairTaskTrackV0Helicity { if (part.sign() < 0) { registryMCreco.fill(HIST("minus/MCrecoAllPt"), mcpart.pt()); - if (mcpart.pdgMCTruth() == -211 && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()))) { + if (mcpart.pdgMCTruth() == kPiMinus && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePi()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePi()))) { registryMCreco.fill(HIST("minus/MCrecoPi"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("minus/MCrecoPiPt"), mcpart.pt()); - } else if (mcpart.pdgMCTruth() == -2212 && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()))) { + } else if (mcpart.pdgMCTruth() == -kProton && isNSigmaCombined(part.p(), aod::pidtpc_tiny::binning::unPackInTable(part.tpcNSigmaStorePr()), aod::pidtof_tiny::binning::unPackInTable(part.tofNSigmaStorePr()))) { registryMCreco.fill(HIST("minus/MCrecoPr"), mcpart.pt(), mcpart.eta()); registryMCreco.fill(HIST("minus/MCrecoPrPt"), mcpart.pt()); } diff --git a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx index 45d430a626a..47666260743 100644 --- a/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx +++ b/PWGCF/FemtoUniverse/Tasks/femtoUniversePairTaskV0CascadeExtended.cxx @@ -381,7 +381,7 @@ struct FemtoUniversePairTaskV0CascadeExtended { template void doMixedEvent(const FilteredFDCollisions& cols, const TableType& parts, PartitionType& partsOne, PartitionType& partsTwo) { - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, cols, cols)) { const int multCol = collision1.multNtr(); diff --git a/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h b/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h index 360587d82ef..69e16841ce0 100644 --- a/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h +++ b/PWGCF/FemtoWorld/Core/FemtoWorldTrackSelection.h @@ -96,7 +96,7 @@ class FemtoWorldTrackSelection : public FemtoWorldObjectSelection - -#include -#include -#include - -namespace o2::analysis::femtoWorld -{ - -enum kPIDselection { - k3d5sigma = 0, - k3sigma = 1, - k2d5sigma = 2 -}; - -enum kDetector { - kTPC = 0, - kTPCTOF = 1, - kNdetectors = 2 -}; - -/// internal function that returns the kPIDselection element corresponding to a specifica n-sigma value -/// \param nSigma number of sigmas for PID -/// \param vNsigma vector with the number of sigmas of interest -/// \return kPIDselection corresponding to n-sigma -kPIDselection getPIDselection(const float nSigma, const std::vector& vNsigma) -{ - for (int i = 0; i < (int)vNsigma.size(); i++) { - if (abs(nSigma - vNsigma[i]) < 1e-3) { - return static_cast(i); - } - } - LOG(info) << "Invalid value of nSigma: " << nSigma << ". Standard 3 sigma returned." << std::endl; - return kPIDselection::k3sigma; -} - -/// function that checks whether the PID selection specified in the vectors is fulfilled -/// \param pidcut Bit-wise container for the PID -/// \param vSpecies vector with ID corresponding to the selected species (output from cutculator) -/// \param nSpecies number of available selected species (output from cutculator) -/// \param nSigma number of sigma selection fo PID -/// \param vNsigma vector with available n-sigma selections for PID -/// \param kDetector enum corresponding to the PID technique -/// \return Whether the PID selection specified in the vectors is fulfilled -bool isPIDSelected(aod::femtoworldparticle::cutContainerType const& pidcut, std::vector const& vSpecies, int nSpecies, float nSigma, const std::vector& vNsigma, const kDetector iDet = kDetector::kTPC) -{ - bool pidSelection = true; - kPIDselection iNsigma = getPIDselection(nSigma, vNsigma); - for (auto iSpecies : vSpecies) { - //\todo we also need the possibility to specify whether the bit is true/false ->std>>vector> - // if (!((pidcut >> it.first) & it.second)) { - int bit_to_check = nSpecies * kDetector::kNdetectors * iNsigma + iSpecies * kDetector::kNdetectors + iDet; - if (!(pidcut & (1UL << bit_to_check))) { - pidSelection = false; - } - } - return pidSelection; -}; - -/// function that checks whether the PID selection specified in the vectors is fulfilled, depending on the momentum TPC or TPC+TOF PID is conducted -/// \param pidcut Bit-wise container for the PID -/// \param momentum Momentum of the track -/// \param pidThresh Momentum threshold that separates between TPC and TPC+TOF PID -/// \param vSpecies Vector with the species of interest (number returned by the CutCulator) -/// \param nSpecies number of available selected species (output from cutculator) -/// \param nSigmaTPC Number of TPC sigmas for selection -/// \param nSigmaTPCTOF Number of TPC+TOF sigmas for selection (circular selection) -/// \return Whether the PID selection is fulfilled -bool isFullPIDSelected(aod::femtoworldparticle::cutContainerType const& pidCut, float const momentum, float const pidThresh, std::vector const& vSpecies, int nSpecies, const std::vector& vNsigma = {3.5, 3., 2.5}, const float nSigmaTPC = 3.5, const float nSigmaTPCTOF = 3.5) -{ - bool pidSelection = true; - if (momentum < pidThresh) { - /// TPC PID only - pidSelection = isPIDSelected(pidCut, vSpecies, nSpecies, nSigmaTPC, vNsigma, kDetector::kTPC); - } else { - /// TPC + TOF PID - pidSelection = isPIDSelected(pidCut, vSpecies, nSpecies, nSigmaTPCTOF, vNsigma, kDetector::kTPCTOF); - } - return pidSelection; -}; - -} // namespace o2::analysis::femtoWorld - -#endif // PWGCF_FEMTOWORLD_CORE_FEMTOWORLDUTILS_H_ diff --git a/PWGCF/FemtoWorld/Core/FemtoWorldV0Selection.h b/PWGCF/FemtoWorld/Core/FemtoWorldV0Selection.h index 6338329a776..9530a118ae5 100644 --- a/PWGCF/FemtoWorld/Core/FemtoWorldV0Selection.h +++ b/PWGCF/FemtoWorld/Core/FemtoWorldV0Selection.h @@ -88,7 +88,7 @@ class FemtoWorldV0Selection : public FemtoWorldObjectSelection void init(o2::framework::HistogramRegistry* registry); @@ -472,9 +472,7 @@ std::array FemtoWorldV0Selection::getCutContainer(C const& sign = -1.; } else if (abs(nSigmaPrPos) < nSigmaPIDMax && abs(nSigmaPiNeg) < nSigmaPIDMax && diffAntiLambda < diffLambda) { sign = 1.; - } - // if it happens that none of these are true, ignore the invariant mass - else { + } else { // if it happens that none of these are true, ignore the invariant mass if (abs(nSigmaPrNeg) < nSigmaPIDMax && abs(nSigmaPiPos) < nSigmaPIDMax) { sign = -1.; } else if (abs(nSigmaPrPos) < nSigmaPIDMax && abs(nSigmaPiNeg) < nSigmaPIDMax) { diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx index 28ef5d0ea9e..0a0ba927a22 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerReducedTask.cxx @@ -37,6 +37,8 @@ #include #include +#include + using namespace o2; using namespace o2::analysis::femtoWorld; using namespace o2::framework; @@ -129,7 +131,7 @@ struct femtoWorldProducerReducedTask { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); } diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx index 3a41924c7b3..78758a6c380 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTask.cxx @@ -240,7 +240,6 @@ struct femtoWorldProducerTask { int mRunNumber; float mMagField; Service ccdb; /// Accessing the CCDB - HfHelper hfHelper; void init(InitContext&) { @@ -348,7 +347,7 @@ struct femtoWorldProducerTask { } /// Function to retrieve the nominal mgnetic field in kG (0.1T) and convert it directly to T - void getMagneticFieldTesla(aod::BCsWithTimestamps::iterator bc) + void getMagneticFieldTesla(const aod::BCsWithTimestamps::iterator& bc) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable // get magnetic field for run @@ -1168,7 +1167,7 @@ struct femtoWorldProducerTask { if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { continue; } - if (config.yCandMax >= 0. && std::abs(hfHelper.yD0(candidate)) > config.yCandMax) { + if (config.yCandMax >= 0. && std::abs(HfHelper::yD0(candidate)) > config.yCandMax) { continue; } @@ -1297,11 +1296,11 @@ struct femtoWorldProducerTask { candidate.eta(), candidate.phi(), candidate.p(), - 0., // general mass - hfHelper.invMassD0ToPiK(candidate), // D0mass - hfHelper.invMassD0barToKPi(candidate), // D0bar mass - candidate.isSelD0(), // D0 flag - candidate.isSelD0bar(), // D0bar flag + 0., // general mass + HfHelper::invMassD0ToPiK(candidate), // D0mass + HfHelper::invMassD0barToKPi(candidate), // D0bar mass + candidate.isSelD0(), // D0 flag + candidate.isSelD0bar(), // D0bar flag aod::femtoworldparticle::ParticleType::kD0D0bar, 0., // cutContainerV0.at(femtoWorldV0Selection::V0ContainerPosition::kV0), 0, @@ -2115,7 +2114,7 @@ struct femtoWorldProducerTask { if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { continue; } - if (config.yCandMax >= 0. && std::abs(hfHelper.yD0(candidate)) > config.yCandMax) { + if (config.yCandMax >= 0. && std::abs(HfHelper::yD0(candidate)) > config.yCandMax) { continue; } @@ -2246,11 +2245,11 @@ struct femtoWorldProducerTask { candidate.eta(), candidate.phi(), candidate.p(), - 0., // general mass - hfHelper.invMassD0ToPiK(candidate), // D0mass - hfHelper.invMassD0barToKPi(candidate), // D0bar mass - candidate.isSelD0(), // D0 flag - candidate.isSelD0bar(), // D0bar flag + 0., // general mass + HfHelper::invMassD0ToPiK(candidate), // D0mass + HfHelper::invMassD0barToKPi(candidate), // D0bar mass + candidate.isSelD0(), // D0 flag + candidate.isSelD0bar(), // D0bar flag aod::femtoworldparticle::ParticleType::kD0D0bar, 0., // cutContainerV0.at(femtoWorldV0Selection::V0ContainerPosition::kV0), 0, diff --git a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx index 091afad61ba..72e78653f44 100644 --- a/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx +++ b/PWGCF/FemtoWorld/TableProducer/femtoWorldProducerTaskV0Only.cxx @@ -35,9 +35,12 @@ #include #include -#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include +#include + using namespace o2; using namespace o2::analysis::femtoWorld; using namespace o2::framework; @@ -180,7 +183,7 @@ struct femtoWorldProducerTaskV0Only { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); } diff --git a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx index 138573a1763..3fd9e35e397 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoPairLambdaAntilambda.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/collisionHistManager.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" +#include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/pairHistManager.h" #include "PWGCF/Femto/Core/particleCleaner.h" #include "PWGCF/Femto/Core/partitions.h" @@ -63,6 +64,8 @@ struct FemtoPairLambdaAntilambda { using FemtoLambdasWithLabel = o2::soa::Join; using FemtoK0shortsWithLabel = o2::soa::Join; + using FemtoMcParticlesWithLabel = o2::soa::Join; + o2::framework::SliceCache cache; // setup collisions @@ -71,10 +74,14 @@ struct FemtoPairLambdaAntilambda { colhistmanager::ConfCollisionBinning confCollisionBinning; // setup for daughters - trackhistmanager::ConfV0PosDauBinning confPosDauBinning; - trackhistmanager::ConfV0NegDauBinning confNegDauBinning; + trackhistmanager::ConfV01PosDauBinning confV01PosDauBinning; + trackhistmanager::ConfV01NegDauBinning confV01NegDauBinning; + trackhistmanager::ConfV02PosDauBinning confV02PosDauBinning; + trackhistmanager::ConfV02NegDauBinning confV02NegDauBinning; // setup lambdas + // selection 1 and 2 are sign-separated (lambda / antilambda), so confMixing.sameSpecies + // has to be set to false when running lambda-antilambda v0builder::ConfLambdaSelection1 confLambdaSelection; v0builder::ConfLambdaSelection2 confLambdaSelection2; particlecleaner::ConfLambdaCleaner1 confLambdaCleaner; @@ -102,14 +109,21 @@ struct FemtoPairLambdaAntilambda { // setup pairs pairhistmanager::ConfPairBinning confPairBinning; pairhistmanager::ConfPairCuts confPairCuts; + paircleaner::ConfPairCleanerBinning confPairCleaner; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixLambda1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixLambda2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixLambda1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixLambda2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixLambda1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixLambda2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -121,12 +135,18 @@ struct FemtoPairLambdaAntilambda { pairLambdaLambdaBuilder; pairbuilder::PairV0V0Builder< - v0histmanager::PrefixK0short1, - trackhistmanager::PrefixV01PosDaughter, - trackhistmanager::PrefixV01NegDaughter, - v0histmanager::PrefixK0short2, - trackhistmanager::PrefixV02PosDaughter, - trackhistmanager::PrefixV02NegDaughter, + v0histmanager::PrefixK0short1Se, + trackhistmanager::PrefixV01PosDaughterSe, + trackhistmanager::PrefixV01NegDaughterSe, + v0histmanager::PrefixK0short2Se, + trackhistmanager::PrefixV02PosDaughterSe, + trackhistmanager::PrefixV02NegDaughterSe, + v0histmanager::PrefixK0short1Me, + trackhistmanager::PrefixV01PosDaughterMe, + trackhistmanager::PrefixV01NegDaughterMe, + v0histmanager::PrefixK0short2Me, + trackhistmanager::PrefixV02PosDaughterMe, + trackhistmanager::PrefixV02NegDaughterMe, pairhistmanager::PrefixV0V0Se, pairhistmanager::PrefixV0V0Me, closepairrejection::PrefixV0V0PosSe, @@ -141,9 +161,9 @@ struct FemtoPairLambdaAntilambda { std::vector defaultVtxBins{10, -10, 10}; std::vector defaultMultBins{50, 0, 200}; std::vector defaultCentBins{10, 0, 100}; - o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}, true}; - o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}, true}; + o2::framework::ColumnBinningPolicy mixBinsVtxMult{{defaultVtxBins, defaultMultBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxCent{{defaultVtxBins, defaultCentBins}}; + o2::framework::ColumnBinningPolicy mixBinsVtxMultCent{{defaultVtxBins, defaultMultBins, defaultCentBins}}; pairhistmanager::ConfMixing confMixing; o2::framework::HistogramRegistry hRegistry{"FemtoV0V0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; @@ -154,119 +174,116 @@ struct FemtoPairLambdaAntilambda { void init(o2::framework::InitContext&) { - // TODO: implement lambda-k0short - bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaSameEvent || doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortSameEvent; - bool processMc = doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaSameEventMc || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortSameEventMc; - - if (processData && processMc) { - LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; - } - - bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc; - bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; + bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || + doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent; - if (processLambdaLambda && processK0shortK0short) { - LOG(fatal) << "Both lambda-lambda and k0short-k0short processing is enabled. Breaking..."; - } + bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || + doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc; + bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || + doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here - mixBinsVtxMult = {{confMixing.vtxBins, confMixing.multBins.value}, true}; - mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}, true}; - mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}, true}; + mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; + mixBinsVtxCent = {{confMixing.vtxBins.value, confMixing.centBins.value}}; + mixBinsVtxMultCent = {{confMixing.vtxBins.value, confMixing.multBins.value, confMixing.centBins.value}}; // setup histograms std::map> colHistSpec; - std::map> trackHistSpec; - std::map> posDauSpec; - std::map> negDauSpec; + std::map> posDauSpec1; + std::map> negDauSpec1; + std::map> posDauSpec2; + std::map> negDauSpec2; std::map> lambdaHistSpec; std::map> k0shortHistSpec; std::map> pairV0V0HistSpec; std::map> cprHistSpecPos = closepairrejection::makeCprHistSpecMap(confCprPos); std::map> cprHistSpecNeg = closepairrejection::makeCprHistSpecMap(confCprNeg); + std::map> pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); if (processData) { colHistSpec = colhistmanager::makeColHistSpecMap(confCollisionBinning); - posDauSpec = trackhistmanager::makeTrackHistSpecMap(confPosDauBinning); - negDauSpec = trackhistmanager::makeTrackHistSpecMap(confNegDauBinning); + posDauSpec1 = trackhistmanager::makeTrackHistSpecMap(confV01PosDauBinning); + negDauSpec1 = trackhistmanager::makeTrackHistSpecMap(confV01NegDauBinning); + posDauSpec2 = trackhistmanager::makeTrackHistSpecMap(confV02PosDauBinning); + negDauSpec2 = trackhistmanager::makeTrackHistSpecMap(confV02NegDauBinning); + pairV0V0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); + if (processLambdaLambda) { lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); - pairV0V0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection, confLambdaSelection2, confLambdaCleaner, confLambdaCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection, confLambdaSelection2, confLambdaCleaner, confLambdaCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, lambdaHistSpec, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } - // setup for k0short - if (doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent) { + if (processK0shortK0short) { k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); - pairV0V0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); - pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection, confK0shortSelection, confK0shortCleaner, confK0shortCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection, confK0shortSelection, confK0shortCleaner, confK0shortCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, k0shortHistSpec, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } } else { colHistSpec = colhistmanager::makeColMcHistSpecMap(confCollisionBinning); - posDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confPosDauBinning); - negDauSpec = trackhistmanager::makeTrackMcHistSpecMap(confNegDauBinning); + posDauSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confV01PosDauBinning); + negDauSpec1 = trackhistmanager::makeTrackMcHistSpecMap(confV01NegDauBinning); + posDauSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confV02PosDauBinning); + negDauSpec2 = trackhistmanager::makeTrackMcHistSpecMap(confV02NegDauBinning); + pairV0V0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); + if (processLambdaLambda) { lambdaHistSpec = v0histmanager::makeV0McHistSpecMap(confLambdaBinning); - pairV0V0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection, confLambdaSelection2, confLambdaCleaner, confLambdaCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, lambdaHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + pairLambdaLambdaBuilder.init(&hRegistry, confCollisionBinning, confLambdaSelection, confLambdaSelection2, confLambdaCleaner, confLambdaCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, lambdaHistSpec, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } - // setup for k0short - if (doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent) { + if (processK0shortK0short) { k0shortHistSpec = v0histmanager::makeV0McHistSpecMap(confK0shortBinning); - pairV0V0HistSpec = pairhistmanager::makePairMcHistSpecMap(confPairBinning, confMixing); - pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection, confK0shortSelection, confK0shortCleaner, confK0shortCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, k0shortHistSpec, posDauSpec, negDauSpec, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg); + pairK0shortK0shortBuilder.init(&hRegistry, confCollisionBinning, confK0shortSelection, confK0shortSelection, confK0shortCleaner, confK0shortCleaner, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, k0shortHistSpec, posDauSpec1, negDauSpec1, posDauSpec2, negDauSpec2, pairV0V0HistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } } - }; + } void processLambdaLambdaSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoLambdas const& lambdas) { - pairLambdaLambdaBuilder.processSameEvent(col, tracks, lambdas, lambdaPartition, lambdaPartition2, cache); + pairLambdaLambdaBuilder.processSameEvent(col, tracks, lambdas, lambdaPartition, lambdaPartition2, cache); } - PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaSameEvent, "Enable processing same event processing for lambda-lambda", true); + PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaSameEvent, "Enable processing same event processing for lambda-antilambda", true); - void processLambdaLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processLambdaLambdaSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& lambdas, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairLambdaLambdaBuilder.processSameEvent(col, mcCols, tracks, lambdas, lambdaWithLabelPartition, lambdaWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); + pairLambdaLambdaBuilder.processSameEvent(col, mcCols, tracks, lambdas, lambdaWithLabelPartition, lambdaWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache); } - PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaSameEventMc, "Enable processing same event processing for lambda-lambda with mc information", false); + PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaSameEventMc, "Enable processing same event processing for lambda-antilambda with mc information", false); - void processLambdaLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& lambdas) + void processLambdaLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { - pairLambdaLambdaBuilder.processMixedEvent(cols, tracks, lambdas, lambdaPartition, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairLambdaLambdaBuilder.processMixedEvent(cols, tracks, lambdaPartition, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } - PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaMixedEvent, "Enable processing mixed event processing for lambda-lambda", true); + PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaMixedEvent, "Enable processing mixed event processing for lambda-antilambda", true); - void processLambdaLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processLambdaLambdaMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoLambdasWithLabel const& /*lambdas*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairLambdaLambdaBuilder.processMixedEvent(cols, mcCols, tracks, lambdaWithLabelPartition, lambdaWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairLambdaLambdaBuilder.processMixedEvent(cols, mcCols, tracks, lambdaWithLabelPartition, lambdaWithLabelPartition2, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } - PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaMixedEventMc, "Enable processing mixed event processing for lambda-lambda with mc information", false); + PROCESS_SWITCH(FemtoPairLambdaAntilambda, processLambdaLambdaMixedEventMc, "Enable processing mixed event processing for lambda-antilambda with mc information", false); void processK0shortK0shortSameEvent(FilteredFemtoCollision const& col, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) { - pairK0shortK0shortBuilder.processSameEvent(col, tracks, k0shorts, k0shortPartition, k0shortPartition, cache); + pairK0shortK0shortBuilder.processSameEvent(col, tracks, k0shorts, k0shortPartition, k0shortPartition, cache); } PROCESS_SWITCH(FemtoPairLambdaAntilambda, processK0shortK0shortSameEvent, "Enable processing same event processing for k0short-k0short", false); - void processK0shortK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processK0shortK0shortSameEventMc(FilteredFemtoCollisionWithLabel const& col, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& k0shorts, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairK0shortK0shortBuilder.processSameEvent(col, mcCols, tracks, k0shorts, k0shortWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); + pairK0shortK0shortBuilder.processSameEvent(col, mcCols, tracks, k0shorts, k0shortWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache); } PROCESS_SWITCH(FemtoPairLambdaAntilambda, processK0shortK0shortSameEventMc, "Enable processing same event processing for k0short-k0short with mc information", false); - void processK0shortK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& k0shorts) + void processK0shortK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { - pairK0shortK0shortBuilder.processMixedEvent(cols, tracks, k0shorts, k0shortPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairK0shortK0shortBuilder.processMixedEvent(cols, tracks, k0shortPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairLambdaAntilambda, processK0shortK0shortMixedEvent, "Enable processing mixed event processing for k0short-k0short", false); - void processK0shortK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, o2::aod::FMcParticles const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) + void processK0shortK0shortMixedEventMc(FilteredFemtoCollisionsWithLabel const& cols, o2::aod::FMcCols const& mcCols, FemtoTracksWithLabel const& tracks, FemtoK0shortsWithLabel const& /*k0shorts*/, FemtoMcParticlesWithLabel const& mcParticles, o2::aod::FMcMothers const& mcMothers, o2::aod::FMcPartMoths const& mcPartonicMothers) { - pairK0shortK0shortBuilder.processMixedEvent(cols, mcCols, tracks, k0shortWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + pairK0shortK0shortBuilder.processMixedEvent(cols, mcCols, tracks, k0shortWithLabelPartition, k0shortWithLabelPartition, mcParticles, mcMothers, mcPartonicMothers, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); } PROCESS_SWITCH(FemtoPairLambdaAntilambda, processK0shortK0shortMixedEventMc, "Enable processing mixed event processing for k0short-k0short with mc information", false); }; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx index c3c0d0a9dcb..40ddbac7a6d 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskPionPion.cxx @@ -157,7 +157,7 @@ struct femtoWorldPairTaskPionPion { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; @@ -269,7 +269,7 @@ struct femtoWorldPairTaskPionPion { // Function to build combinations template - void CombineParticles(T1 groupPartsOne, T1 groupPartsTwo, T2 cont, T3 parts, T4 magFieldTesla, int multCol, int sameOrMixed) + void CombineParticles(const T1& groupPartsOne, const T1& groupPartsTwo, T2 cont, const T3& parts, T4 magFieldTesla, int multCol, int sameOrMixed) { if (sameOrMixed == 1) { for (auto& [p1, p2] : combinations(groupPartsOne, groupPartsTwo)) { diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx index 6ded175155f..44e36a37e98 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskProtonAntiproton.cxx @@ -153,7 +153,7 @@ struct femtoWorldPairTaskProtonAntiproton { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx index 5ca9109e6e7..1357578f0fa 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackD0.cxx @@ -367,7 +367,7 @@ struct femtoWorldPairTaskTrackD0 { void processMixedEvent(o2::aod::FemtoWorldCollisions& cols, o2::aod::FemtoWorldParticles&) { - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, ConfNEventsMix, -1, cols, cols)) { diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx index 426ca4af935..c339defa68e 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackPhi.cxx @@ -327,7 +327,7 @@ struct femtoWorldPairTaskTrackPhi { void processMixedEvent(o2::aod::FemtoWorldCollisions& cols, o2::aod::FemtoWorldParticles&) { - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, ConfNEventsMix, -1, cols, cols)) { diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx index 1711a4fdc48..b8e514a06fe 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPairTaskTrackTrack.cxx @@ -156,7 +156,7 @@ struct femtoWorldPairTaskTrackTrack { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; @@ -214,32 +214,39 @@ struct femtoWorldPairTaskTrackTrack { if (mom > 0.5) { // if (TMath::Hypot( nsigmaTOFP, nsigmaTPCP )/TMath::Sqrt(2) < 3.0) if (mom < 2.0) { - if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma) + if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma) { return true; - } else if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma2) + } + } else if (TMath::Hypot(nsigmaTOFK, nsigmaTPCK) < fNsigma2) { return true; + } } else { - if (TMath::Abs(nsigmaTPCK) < fNsigma) + if (TMath::Abs(nsigmaTPCK) < fNsigma) { return true; + } } } else { - if (mom < 0.4) { if (nsigmaTOFK < -999.) { - if (TMath::Abs(nsigmaTPCK) < 2.0) + if (TMath::Abs(nsigmaTPCK) < 2.0) { return true; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } else if (mom >= 0.4 && mom <= 0.6) { if (nsigmaTOFK < -999.) { - if (TMath::Abs(nsigmaTPCK) < 2.0) + if (TMath::Abs(nsigmaTPCK) < 2.0) { return true; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } else if (nsigmaTOFK < -999.) { return false; - } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) + } else if (TMath::Abs(nsigmaTOFK) < 3.0 && TMath::Abs(nsigmaTPCK) < 3.0) { return true; + } } return false; } diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx index 248225ecd46..95bd3db42ec 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPionAllPairTask.cxx @@ -115,8 +115,8 @@ struct FemtoWorldIdenticalPionPair { // Configurable> CfgVtxBins{"CfgVtxBins",{-10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f},"Mixing bins - z-vertex"}; // Configurable> CfgCentBins{"CfgCentBins",{5.0f,10.0f,20.0f,30.0f,40.0f,50.0f},"Centrality Bins"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; - ColumnBinningPolicy colBinning2{{CfgVtxBins, CfgCentBinsMixing}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; + ColumnBinningPolicy colBinning2{{CfgVtxBins, CfgCentBinsMixing}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx b/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx index fdedf6e0870..200ea018be9 100644 --- a/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx +++ b/PWGCF/FemtoWorld/Tasks/femtoWorldPionPairTaskTrackTrack.cxx @@ -157,7 +157,7 @@ struct femtoWorldPionPairTaskTrackTrack { ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}, true}; + ColumnBinningPolicy colBinning{{CfgVtxBins, CfgMultBins}}; ConfigurableAxis CfgkstarBins{"CfgkstarBins", {1500, 0., 6.}, "binning kstar"}; ConfigurableAxis CfgkTBins{"CfgkTBins", {150, 0., 9.}, "binning kT"}; diff --git a/PWGCF/Flow/TableProducer/zdcQVectors.cxx b/PWGCF/Flow/TableProducer/zdcQVectors.cxx index ea9339394f4..0aade4713d6 100644 --- a/PWGCF/Flow/TableProducer/zdcQVectors.cxx +++ b/PWGCF/Flow/TableProducer/zdcQVectors.cxx @@ -20,6 +20,8 @@ #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" #include #include @@ -41,18 +43,19 @@ #include #include +#include #include #include #include #include +#include +#include #include #include #include #include -#include - -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) using namespace o2; using namespace o2::framework; @@ -64,39 +67,25 @@ using namespace o2::constants::math; namespace o2::analysis::qvectortask { -int counter = 0; // Define histogrm names here to use same names for creating and later uploading and retrieving data from ccdb // Energy calibration: -std::vector namesEcal(10, ""); -std::vector> names(5, std::vector()); //(1x 4d 4x 1d) -std::vector namesTS; // for timestamo recentering -std::vector vnames = {"hvertex_vx", "hvertex_vy"}; +const std::array namesEcal = {{"hZNA_mean_t0_cent", "hZNA_mean_t1_cent", "hZNA_mean_t2_cent", "hZNA_mean_t3_cent", "hZNA_mean_t4_cent", + "hZNC_mean_t0_cent", "hZNC_mean_t1_cent", "hZNC_mean_t2_cent", "hZNC_mean_t3_cent", "hZNC_mean_t4_cent"}}; +const std::array, 5> names = {{{{"hQXA_mean_Cent_V_run", "hQYA_mean_Cent_V_run", "hQXC_mean_Cent_V_run", "hQYC_mean_Cent_V_run"}}, + {{"hQXA_mean_cent_run", "hQYA_mean_cent_run", "hQXC_mean_cent_run", "hQYC_mean_cent_run"}}, + {{"hQXA_mean_vx_run", "hQYA_mean_vx_run", "hQXC_mean_vx_run", "hQYC_mean_vx_run"}}, + {{"hQXA_mean_vy_run", "hQYA_mean_vy_run", "hQXC_mean_vy_run", "hQYC_mean_vy_run"}}, + {{"hQXA_mean_vz_run", "hQYA_mean_vz_run", "hQXC_mean_vz_run", "hQYC_mean_vz_run"}}}}; +const std::array namesTS = {{"hQXA_mean_timestamp_run", "hQYA_mean_timestamp_run", "hQXC_mean_timestamp_run", "hQYC_mean_timestamp_run"}}; // for timestamp recentering +const std::array vnames = {"hvertex_vx", "hvertex_vy"}; // https://alice-notes.web.cern.ch/system/files/notes/analysis/620/017-May-31-analysis_note-ALICE_analysis_note_v2.pdf -std::vector pxZDC = {-1.75, 1.75, -1.75, 1.75}; -std::vector pyZDC = {-1.75, -1.75, 1.75, 1.75}; -double alphaZDC = 0.395; - -// q-vectors before (q) and after (qRec) recentering. -std::vector q(4); // start values of [QxA, QyA, QxC, QyC] -std::vector qNoEq(4); // start values of [QxA, QyA, QxC, QyC] - -// for energy calibration -std::vector eZN(8); // uncalibrated energy for the 2x4 towers (a1, a2, a3, a4, c1, c2, c3, c4) -std::vector meanEZN(10); // mean energies from calibration histos (common A, t1-4 A,common C, t1-4C) -std::vector e(8, 0.); // calibrated energies (a1, a2, a3, a4, c1, c2, c3, c4)) - -// Define variables needed to do the recentring steps. -float centrality = 0; -int runnumber = 0; -int lastRunNumber = 0; -std::vector v(3, 0); // vx, vy, vz -bool isSelected = true; -std::vector cents; // centrality estimaters -uint64_t timestamp = 0; -double rsTimestamp = 0; +const std::array pxZDC = {-1.75, 1.75, -1.75, 1.75}; +const std::array pyZDC = {-1.75, -1.75, 1.75, 1.75}; +const double alphaZDC = 0.395; +const int totalTowers = 10; } // namespace o2::analysis::qvectortask using namespace o2::analysis::qvectortask; @@ -106,10 +95,10 @@ struct ZdcQVectors { Produces spTableZDC; struct : ConfigurableGroup { - Configurable cfgEvtUseRCTFlagChecker{"cfgEvtUseRCTFlagChecker", false, "Evt sel: use RCT flag checker"}; + Configurable cfgEvtUseRCTFlagChecker{"cfgEvtUseRCTFlagChecker", true, "Evt sel: use RCT flag checker"}; Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label (CBT, CBT_hadronPID)"}; // all Labels can be found in Common/CCDB/RCTSelectionFlags.h - Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; - Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", true, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; } rctFlags; RCTFlagsChecker rctChecker; @@ -122,16 +111,12 @@ struct ZdcQVectors { } EvSel; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgRecenterForTimestamp, bool, false, "Add 1D recentering for timestamp"); + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_RecenterForTimestamp, bool, false, "Add 1D recentering for timestamp"); O2_DEFINE_CONFIGURABLE(cfgCCDBdir_Timestamp, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Timestamp", "CCDB directory for Timestamp recentering"); } extraTS; ConfigurableAxis axisCent{"axisCent", {90, 0, 90}, "Centrality axis in 1% bins"}; - ConfigurableAxis axisCent10{"axisCent10", {9, 0, 90}, "Centrality axis in 10% bins"}; ConfigurableAxis axisQ{"axisQ", {100, -2, 2}, "Q vector (xy) in ZDC"}; - ConfigurableAxis axisVxBig{"axisVxBig", {3, -0.01, 0.01}, "for Pos X of collision"}; - ConfigurableAxis axisVyBig{"axisVyBig", {3, -0.01, 0.01}, "for Pos Y of collision"}; - ConfigurableAxis axisVzBig{"axisVzBig", {3, -10, 10}, "for Pos Z of collision"}; ConfigurableAxis axisVx{"axisVx", {100, -0.01, 0.01}, "for Pos X of collision"}; ConfigurableAxis axisVy{"axisVy", {100, -0.01, 0.01}, "for Pos Y of collision"}; ConfigurableAxis axisVz{"axisVz", {100, -10, 10}, "for vz of collision"}; @@ -146,21 +131,40 @@ struct ZdcQVectors { O2_DEFINE_CONFIGURABLE(cfgVtxZ, float, 10.0f, "Accepted z-vertex range") O2_DEFINE_CONFIGURABLE(cfgMagField, float, 99999, "Configurable magnetic field; default CCDB will be queried") - O2_DEFINE_CONFIGURABLE(cfgEnergyCal, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Energy", "ccdb path for energy calibration histos") - O2_DEFINE_CONFIGURABLE(cfgMeanv, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/vmean", "ccdb path for mean v histos") - O2_DEFINE_CONFIGURABLE(cfgMinEntriesSparseBin, int, 100, "Minimal number of entries allowed in 4D recentering histogram to use for recentering.") - O2_DEFINE_CONFIGURABLE(cfgRec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5", "ccdb path for recentering histos"); + O2_DEFINE_CONFIGURABLE(cfgMinEntriesSparseBin, int, 1000, "Minimal number of entries allowed in 4D recentering histogram to use for recentering.") O2_DEFINE_CONFIGURABLE(cfgFillHistRegistry, bool, true, "Fill common registry with histograms"); O2_DEFINE_CONFIGURABLE(cfgFillCutAnalysis, bool, true, "Fill cut analysis with histograms"); O2_DEFINE_CONFIGURABLE(cfgFillNothing, bool, false, "Disable ALL Histograms -> ONLY use to reduce memory"); - O2_DEFINE_CONFIGURABLE(cfgNoGain, bool, false, "Do not apply gain correction to ZDC energy calibration"); - + O2_DEFINE_CONFIGURABLE(cfgNoGain, bool, true, "Do not apply gain correction to ZDC energy calibration"); + O2_DEFINE_CONFIGURABLE(cfgNIterationsAfterShift, int, 1, "Number of iterations to perform after shift"); + + O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAxy, float, 0.2, "Cut on DCA in the transverse direction (cm)"); + O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAz, float, 0.2, "Cut on DCA in the longitudinal direction (cm)"); + O2_DEFINE_CONFIGURABLE(cfgTrackSelsPtmin, float, 0.2, "minimum pt (GeV/c)"); + O2_DEFINE_CONFIGURABLE(cfgTrackSelsPtmax, float, 10, "maximum pt (GeV/c)"); + O2_DEFINE_CONFIGURABLE(cfgTrackSelsEta, float, 0.8, "eta cut"); + + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_EnergyCal, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Energy", "ccdb path for energy calibration histos") + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_Meanv, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/vmean", "ccdb path for mean v histos") + O2_DEFINE_CONFIGURABLE(cfgCCDBDir_Rec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5", "ccdb path for recentering histos"); O2_DEFINE_CONFIGURABLE(cfgCCDBdir_Shift, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/Shift", "CCDB directory for Shift ZDC"); + O2_DEFINE_CONFIGURABLE(cfgCCDBdir_ShiftRec, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/noGain/AfterShift", "CCDB directory for recentering after Shift ZDC"); + + Configurable> cfgSelVec{"cfgSelVec", std::vector{1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1}, "Put 1 for every event selection from SelectionCriteria that is used in flowSP"}; + Configurable> cfgEvSelsMultPv{"cfgEvSelsMultPv", std::vector{2223.49, -75.1444, 0.963572, -0.00570399, 1.34877e-05, 3790.99, -137.064, 2.13044, -0.017122, 5.82834e-05}, "Multiplicity cuts (PV) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; + Configurable> cfgEvSelsMult{"cfgEvSelsMult", std::vector{1301.56, -41.4615, 0.478224, -0.00239449, 4.46966e-06, 2967.6, -102.927, 1.47488, -0.0106534, 3.28622e-05}, "Multiplicity cuts (Global) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; + + // Track selection DCA cut + std::unique_ptr fDCACut = std::make_unique("fDCACut", "0.0105 + 0.035 / TMath::Power(x,1.1)", 0, 100); // define my..... // Filter collisionFilter = nabs(aod::collision::posZ) <; - using UsedCollisions = soa::Join; + + using UsedCollisions = soa::Join; using BCsRun3 = soa::Join; + Filter trackFilter = nabs(aod::track::eta) < cfgTrackSelsEta && aod::track::pt > cfgTrackSelsPtmin&& aod::track::pt < cfgTrackSelsPtmax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && nabs(aod::track::dcaXY) < cfgTrackSelsDCAxy&& nabs(aod::track::dcaZ) < cfgTrackSelsDCAz; + using UnfilteredTracks = soa::Join; + using UsedTracks = soa::Filtered; enum SelectionCriteria { evSel_FilteredEvent, @@ -178,6 +182,7 @@ struct ZdcQVectors { evSel_kIsGoodITSLayer0123, evSel_RCTFlagsZDC, evSel_CentCuts, + evSel_MultCut, nEventSelections }; @@ -185,30 +190,48 @@ struct ZdcQVectors { kEnergyCal, kMeanv, kRec, - kTimestamp + kTimestamp, + kRecShift, + nCalibModes }; // Define output HistogramRegistry registry{"Registry"}; - Service ccdb; + // Event selection cuts + std::unique_ptr fPhiCutLow = nullptr; + std::unique_ptr fPhiCutHigh = nullptr; + std::unique_ptr fMultPVCutLow = nullptr; + std::unique_ptr fMultPVCutHigh = nullptr; + std::unique_ptr fMultCutLow = nullptr; + std::unique_ptr fMultCutHigh = nullptr; + std::unique_ptr fMultMultPVCut = nullptr; + + Service ccdb{}; // keep track of calibration histos for each given step and iteration struct Calib { - std::vector calibList = std::vector(4, nullptr); // [0] Enerfy cal, [1] vmean, [2] recentering, [3] timestamp - std::vector calibfilesLoaded = std::vector(4, false); - int atStep = 0; - int atIteration = 0; + std::vector calibList = std::vector(5, nullptr); // [0] Enerfy cal, [1] vmean, [2] recentering, [3] timestamp, [4] recenter after shift Correction + std::vector calibfilesLoaded = std::vector(5, false); TProfile3D* shiftprofileC = nullptr; TProfile3D* shiftprofileA = nullptr; bool isShiftProfileFound = false; + int lastRunNumber = 0; + int runnumber = 0; + float centrality = 0; + double rsTimestamp = 0; + uint64_t timestamp = 0; + std::vector v = {0, 0, 0}; + bool isSelected = 0; + int atIteration = 0; } cal; enum FillType { kBefore, - kAfter + kAfter, + kAfterShift }; void init(InitContext const&) @@ -246,42 +269,29 @@ struct ZdcQVectors { registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_CentCuts + 1, "Cenrality range"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_kIsGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_kIsGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_MultCut + 1, "Mult & MultPV"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_isSelectedZDC + 1, "isSelected"); - int totalTowers = 10; - int totalTowersPerSide = 5; - for (int tower = 0; tower < totalTowers; tower++) { - namesEcal[tower] = TString::Format("hZN%s_mean_t%i_cent", sides[(tower < totalTowersPerSide) ? 0 : 1], tower % 5); - } - - for (const auto& side : sides) { - for (const auto& coord : capCOORDS) { - names[0].push_back(TString::Format("hQ%s%s_mean_Cent_V_run", coord, side)); - names[1].push_back(TString::Format("hQ%s%s_mean_cent_run", coord, side)); - names[2].push_back(TString::Format("hQ%s%s_mean_vx_run", coord, side)); - names[3].push_back(TString::Format("hQ%s%s_mean_vy_run", coord, side)); - names[4].push_back(TString::Format("hQ%s%s_mean_vz_run", coord, side)); - namesTS.push_back(TString::Format("hQ%s%s_mean_timestamp_run", coord, side)); - } // end of capCOORDS - } - if (!cfgFillNothing) { if (cfgFillHistRegistry) { - registry.add(Form("QA/before/hSPplaneA"), "hSPplaneA", kTH2D, {axisPsiA, axisCent10}); - registry.add(Form("QA/before/hSPplaneC"), "hSPplaneC", kTH2D, {axisPsiC, axisCent10}); - registry.add(Form("QA/before/hSPplaneFull"), "hSPplaneFull", kTH2D, {{100, -PI, PI}, axisCent10}); + registry.add(Form("QA/before/hSPplaneA"), "hSPplaneA", kTH2D, {axisPsiA, axisCent}); + registry.add(Form("QA/before/hSPplaneC"), "hSPplaneC", kTH2D, {axisPsiC, axisCent}); + registry.add(Form("QA/before/hSPplaneFull"), "hSPplaneFull", kTH2D, {{100, -PI, PI}, axisCent}); + if (!cfgCCDBdir_ShiftRec.value.empty()) { + registry.addClone("QA/before/", "QA/afterShift/"); + } for (const auto& side : sides) { - registry.add(Form("QA/before/hZN%s_Qx_vs_Qy", side), Form("hZN%s_Qx_vs_Qy", side), kTH2F, {axisQ, axisQ}); + registry.add(Form("recentering/before/hZN%s_Qx_vs_Qy", side), Form("hZN%s_Qx_vs_Qy", side), kTH2F, {axisQ, axisQ}); } for (const auto& COORD1 : capCOORDS) { for (const auto& COORD2 : capCOORDS) { // Now we get: & vs. Centrality - registry.add(Form("QA/before/hQ%sA_Q%sC_vs_cent", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_cent", COORD1, COORD2), kTProfile, {axisCent}); - registry.add(Form("QA/before/hQ%sA_Q%sC_vs_vx", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_vx", COORD1, COORD2), kTProfile, {axisVx}); - registry.add(Form("QA/before/hQ%sA_Q%sC_vs_vy", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_vy", COORD1, COORD2), kTProfile, {axisVy}); - registry.add(Form("QA/before/hQ%sA_Q%sC_vs_vz", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_vz", COORD1, COORD2), kTProfile, {axisVz}); - registry.add(Form("QA/before/hQ%sA_Q%sC_vs_timestamp", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_timestamp", COORD1, COORD2), kTProfile, {axisTimestamp}); + registry.add(Form("recentering/before/hQ%sA_Q%sC_vs_cent", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_cent", COORD1, COORD2), kTProfile, {axisCent}); + registry.add(Form("recentering/before/hQ%sA_Q%sC_vs_vx", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_vx", COORD1, COORD2), kTProfile, {axisVx}); + registry.add(Form("recentering/before/hQ%sA_Q%sC_vs_vy", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_vy", COORD1, COORD2), kTProfile, {axisVy}); + registry.add(Form("recentering/before/hQ%sA_Q%sC_vs_vz", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_vz", COORD1, COORD2), kTProfile, {axisVz}); + registry.add(Form("recentering/before/hQ%sA_Q%sC_vs_timestamp", COORD1, COORD2), Form("hQ%sA_Q%sC_vs_timestamp", COORD1, COORD2), kTProfile, {axisTimestamp}); } } @@ -289,15 +299,20 @@ struct ZdcQVectors { // Sides is {A,C} and capcoords is {X,Y} for (const auto& side : sides) { for (const auto& coord : capCOORDS) { - registry.add(Form("QA/before/hQ%s%s_vs_cent", coord, side), Form("hQ%s%s_vs_cent", coord, side), {HistType::kTProfile, {axisCent10}}); - registry.add(Form("QA/before/hQ%s%s_vs_vx", coord, side), Form("hQ%s%s_vs_vx", coord, side), {HistType::kTProfile, {axisVx}}); - registry.add(Form("QA/before/hQ%s%s_vs_vy", coord, side), Form("hQ%s%s_vs_vy", coord, side), {HistType::kTProfile, {axisVy}}); - registry.add(Form("QA/before/hQ%s%s_vs_vz", coord, side), Form("hQ%s%s_vs_vz", coord, side), {HistType::kTProfile, {axisVz}}); - registry.add(Form("QA/before/hQ%s%s_vs_timestamp", coord, side), Form("hQ%s%s_vs_timestamp", coord, side), {HistType::kTProfile, {axisTimestamp}}); - registry.add(Form("QA/Q%s%s_vs_iteration", coord, side), Form("hQ%s%s_vs_iteration", coord, side), {HistType::kTH2D, {{25, 0, 25}, axisQ}}); + registry.add(Form("recentering/before/hQ%s%s_vs_cent", coord, side), Form("hQ%s%s_vs_cent", coord, side), {HistType::kTProfile, {axisCent}}); + registry.add(Form("recentering/before/hQ%s%s_vs_vx", coord, side), Form("hQ%s%s_vs_vx", coord, side), {HistType::kTProfile, {axisVx}}); + registry.add(Form("recentering/before/hQ%s%s_vs_vy", coord, side), Form("hQ%s%s_vs_vy", coord, side), {HistType::kTProfile, {axisVy}}); + registry.add(Form("recentering/before/hQ%s%s_vs_vz", coord, side), Form("hQ%s%s_vs_vz", coord, side), {HistType::kTProfile, {axisVz}}); + registry.add(Form("recentering/before/hQ%s%s_vs_timestamp", coord, side), Form("hQ%s%s_vs_timestamp", coord, side), {HistType::kTProfile, {axisTimestamp}}); + registry.add(Form("recentering/Q%s%s_vs_iteration", coord, side), Form("hQ%s%s_vs_iteration", coord, side), {HistType::kTH2D, {{100, 0, 100}, axisQ}}); } // end of capCOORDS } // end of sides + registry.add("recentering/before/ZNA_Qx_vs_Centrality", "ZNA_Qx_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); + registry.add("recentering/before/ZNA_Qy_vs_Centrality", "ZNA_Qy_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); + registry.add("recentering/before/ZNC_Qx_vs_Centrality", "ZNC_Qx_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); + registry.add("recentering/before/ZNC_Qy_vs_Centrality", "ZNC_Qy_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); + registry.add("QA/centrality_before", "centrality_before", kTH1D, {{100, 0, 100}}); registry.add("QA/centrality_after", "centrality_after", kTH1D, {{100, 0, 100}}); @@ -315,51 +330,39 @@ struct ZdcQVectors { registry.add("QA/shift/DeltaPsiZDCC", "DeltaPsiZDCC", kTH2D, {axisPsiCShifted, axisPsiC}); registry.add("QA/shift/DeltaPsiZDCAC", "DeltaPsiZDCAC", kTH2D, {axisPsiA, axisPsiC}); - registry.add("QA/before/ZNA_pmC", "ZNA_pmC", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNA_pm1", "ZNA_pm1", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNA_pm2", "ZNA_pm2", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNA_pm3", "ZNA_pm3", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNA_pm4", "ZNA_pm4", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNA_pmC", "ZNA_pmC", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNA_pm1", "ZNA_pm1", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNA_pm2", "ZNA_pm2", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNA_pm3", "ZNA_pm3", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNA_pm4", "ZNA_pm4", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNC_pmC", "ZNC_pmC", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNC_pm1", "ZNC_pm1", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNC_pm2", "ZNC_pm2", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNC_pm3", "ZNC_pm3", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNC_pm4", "ZNC_pm4", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNC_pmC", "ZNC_pmC", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNC_pm1", "ZNC_pm1", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNC_pm2", "ZNC_pm2", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNC_pm3", "ZNC_pm3", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNC_pm4", "ZNC_pm4", kTProfile, {{1, 0, 1.}}); registry.add("QA/before/ZNA_Qx", "ZNA_Qx", kTProfile, {{1, 0, 1.}}); registry.add("QA/before/ZNA_Qy", "ZNA_Qy", kTProfile, {{1, 0, 1.}}); registry.add("QA/before/ZNC_Qx", "ZNC_Qx", kTProfile, {{1, 0, 1.}}); registry.add("QA/before/ZNC_Qy", "ZNC_Qy", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNA_Qx_vs_Centrality", "ZNA_Qx_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); - registry.add("QA/before/ZNA_Qy_vs_Centrality", "ZNA_Qy_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); - registry.add("QA/before/ZNC_Qx_vs_Centrality", "ZNC_Qx_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); - registry.add("QA/before/ZNC_Qy_vs_Centrality", "ZNC_Qy_vs_Centrality", kTH2D, {{100, 0, 100}, {200, -2, 2}}); - - registry.add("QA/before/ZNA_pmC_vs_Centrality", "ZNA_pmC_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); - registry.add("QA/before/ZNA_pmSUM_vs_Centrality", "ZNA_pmSUM_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); - registry.add("QA/before/ZNA_pm1_vs_Centrality", "ZNA_pm1_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - registry.add("QA/before/ZNA_pm2_vs_Centrality", "ZNA_pm2_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - registry.add("QA/before/ZNA_pm3_vs_Centrality", "ZNA_pm3_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - registry.add("QA/before/ZNA_pm4_vs_Centrality", "ZNA_pm4_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - - registry.add("QA/before/ZNC_pmC_vs_Centrality", "ZNC_pmC_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); - registry.add("QA/before/ZNC_pmSUM_vs_Centrality", "ZNC_pmSUM_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); - registry.add("QA/before/ZNC_pm1_vs_Centrality", "ZNC_pm1_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - registry.add("QA/before/ZNC_pm2_vs_Centrality", "ZNC_pm2_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - registry.add("QA/before/ZNC_pm3_vs_Centrality", "ZNC_pm3_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - registry.add("QA/before/ZNC_pm4_vs_Centrality", "ZNC_pm4_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); - - registry.addClone("QA/before/", "QA/after/"); - - registry.add("QA/before/ZNA_Qx_noEq", "ZNA_Qx_noEq", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNA_Qy_noEq", "ZNA_Qy_noEq", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNC_Qx_noEq", "ZNC_Qx_noEq", kTProfile, {{1, 0, 1.}}); - registry.add("QA/before/ZNC_Qy_noEq", "ZNC_Qy_noEq", kTProfile, {{1, 0, 1.}}); + registry.add("QA/ZNA_pmC_vs_Centrality", "ZNA_pmC_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); + registry.add("QA/ZNA_pmSUM_vs_Centrality", "ZNA_pmSUM_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); + registry.add("QA/ZNA_pm1_vs_Centrality", "ZNA_pm1_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); + registry.add("QA/ZNA_pm2_vs_Centrality", "ZNA_pm2_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); + registry.add("QA/ZNA_pm3_vs_Centrality", "ZNA_pm3_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); + registry.add("QA/ZNA_pm4_vs_Centrality", "ZNA_pm4_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); + + registry.add("QA/ZNC_pmC_vs_Centrality", "ZNC_pmC_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); + registry.add("QA/ZNC_pmSUM_vs_Centrality", "ZNC_pmSUM_vs_Centrality", kTH2D, {{100, 0, 100}, {300, 0, 300}}); + registry.add("QA/ZNC_pm1_vs_Centrality", "ZNC_pm1_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); + registry.add("QA/ZNC_pm2_vs_Centrality", "ZNC_pm2_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); + registry.add("QA/ZNC_pm3_vs_Centrality", "ZNC_pm3_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); + registry.add("QA/ZNC_pm4_vs_Centrality", "ZNC_pm4_vs_Centrality", kTH2D, {{100, 0, 100}, {100, 0, 1}}); } - // Tower mean energies vs. centrality used for tower gain equalisation + // Tower mean energies vs centrality used for tower gain equalisation for (int tower = 0; tower < totalTowers; tower++) { registry.add(Form("Energy/%s", namesEcal[tower].Data()), Form("%s", namesEcal[tower].Data()), kTProfile2D, {{1, 0, 1}, axisCent}); } @@ -384,42 +387,70 @@ struct ZdcQVectors { registry.add("CutAnalysis/hvertex_vy", "hvertex_vy", kTProfile2D, {{1, 0., 1.}, {nEventSelections + 5, 0, nEventSelections + 5}}); registry.add("CutAnalysis/hvertex_vz", "hvertex_vz", kTProfile2D, {{1, 0., 1.}, {nEventSelections + 5, 0, nEventSelections + 5}}); } + + if (!cfgCCDBdir_ShiftRec.value.empty()) { + registry.addClone("recentering/before/", "recentering/afterShift/"); + } + registry.addClone("recentering/before/", "recentering/after/"); + registry.addClone("QA/before/", "QA/after/"); + } + + fMultPVCutLow = std::make_unique("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + fMultPVCutHigh = std::make_unique("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + fMultCutLow = std::make_unique("fMultCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + fMultCutHigh = std::make_unique("fMultCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + + std::vector paramsMultPVCut = cfgEvSelsMultPv; + std::vector paramsMultCut = cfgEvSelsMult; + + uint64_t nParams = 10; + + if (paramsMultPVCut.size() < nParams) { + LOGF(fatal, "cfg.cEvSelsMultPv not set properly.. size = %d (should be 10) --> Check your config files!", paramsMultPVCut.size()); + } else if (paramsMultCut.size() < nParams) { + LOGF(fatal, "cfg.cEvSelsMult not set properly.. size = %d (should be 10) --> Check your config files!", paramsMultCut.size()); + } else { + fMultPVCutLow->SetParameters(paramsMultPVCut[0], paramsMultPVCut[1], paramsMultPVCut[2], paramsMultPVCut[3], paramsMultPVCut[4]); + fMultPVCutHigh->SetParameters(paramsMultPVCut[5], paramsMultPVCut[6], paramsMultPVCut[7], paramsMultPVCut[8], paramsMultPVCut[9]); + fMultCutLow->SetParameters(paramsMultCut[0], paramsMultCut[1], paramsMultCut[2], paramsMultCut[3], paramsMultCut[4]); + fMultCutHigh->SetParameters(paramsMultCut[5], paramsMultCut[6], paramsMultCut[7], paramsMultCut[8], paramsMultCut[9]); } } double rescaleTimestamp(uint64_t timestamp, int runnumber) { - auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); - auto duration = ccdb.getRunDuration(runnumber); + auto& cc = o2::ccdb::BasicCCDBManager::instance(); + auto duration = cc.getRunDuration(runnumber); double ts = (static_cast(timestamp - duration.first) / static_cast(duration.second - duration.first)) * 100.0; return ts; } template - inline void fillCutAnalysis(TCollision collision, TZdc zdcBC, int evSel) + inline void fillCutAnalysis(const TCollision& collision, const TZdc& zdcBC, int evSel) { registry.fill(HIST("hEventCount"), evSel); // FT0C is the default centrality estimator - if (!cfgFillCutAnalysis || cfgFillNothing) + if (!cfgFillCutAnalysis || cfgFillNothing) { return; + } // Add default with different centrality estimators as well // Here we fill the Energy and mean vx, vy vz histograms with an extra dimension for all the event selections used. - registry.get(HIST("CutAnalysis/hvertex_vx"))->Fill(Form("%d", runnumber), evSel, collision.posX()); - registry.get(HIST("CutAnalysis/hvertex_vy"))->Fill(Form("%d", runnumber), evSel, collision.posY()); - registry.get(HIST("CutAnalysis/hvertex_vz"))->Fill(Form("%d", runnumber), evSel, collision.posZ()); - - registry.get(HIST("CutAnalysis/hZNA_mean_t0_cent"))->Fill(centrality, evSel, zdcBC.energyCommonZNA(), 1); - registry.get(HIST("CutAnalysis/hZNA_mean_t1_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNA()[0], 1); - registry.get(HIST("CutAnalysis/hZNA_mean_t2_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNA()[1], 1); - registry.get(HIST("CutAnalysis/hZNA_mean_t3_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNA()[2], 1); - registry.get(HIST("CutAnalysis/hZNA_mean_t4_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNA()[3], 1); - registry.get(HIST("CutAnalysis/hZNC_mean_t0_cent"))->Fill(centrality, evSel, zdcBC.energyCommonZNC(), 1); - registry.get(HIST("CutAnalysis/hZNC_mean_t1_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNC()[0], 1); - registry.get(HIST("CutAnalysis/hZNC_mean_t2_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNC()[1], 1); - registry.get(HIST("CutAnalysis/hZNC_mean_t3_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNC()[2], 1); - registry.get(HIST("CutAnalysis/hZNC_mean_t4_cent"))->Fill(centrality, evSel, zdcBC.energySectorZNC()[3], 1); + registry.get(HIST("CutAnalysis/hvertex_vx"))->Fill(Form("%d", cal.runnumber), evSel, collision.posX()); + registry.get(HIST("CutAnalysis/hvertex_vy"))->Fill(Form("%d", cal.runnumber), evSel, collision.posY()); + registry.get(HIST("CutAnalysis/hvertex_vz"))->Fill(Form("%d", cal.runnumber), evSel, collision.posZ()); + + registry.get(HIST("CutAnalysis/hZNA_mean_t0_cent"))->Fill(cal.centrality, evSel, zdcBC.energyCommonZNA(), 1); + registry.get(HIST("CutAnalysis/hZNA_mean_t1_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNA()[0], 1); + registry.get(HIST("CutAnalysis/hZNA_mean_t2_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNA()[1], 1); + registry.get(HIST("CutAnalysis/hZNA_mean_t3_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNA()[2], 1); + registry.get(HIST("CutAnalysis/hZNA_mean_t4_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNA()[3], 1); + registry.get(HIST("CutAnalysis/hZNC_mean_t0_cent"))->Fill(cal.centrality, evSel, zdcBC.energyCommonZNC(), 1); + registry.get(HIST("CutAnalysis/hZNC_mean_t1_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNC()[0], 1); + registry.get(HIST("CutAnalysis/hZNC_mean_t2_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNC()[1], 1); + registry.get(HIST("CutAnalysis/hZNC_mean_t3_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNC()[2], 1); + registry.get(HIST("CutAnalysis/hZNC_mean_t4_cent"))->Fill(cal.centrality, evSel, zdcBC.energySectorZNC()[3], 1); if (evSel == nEventSelections) { int centCounter = 0; @@ -448,10 +479,10 @@ struct ZdcQVectors { } template - uint16_t eventSelected(TCollision collision, TBunchCrossing bunchCrossing) + uint16_t eventSelected(const TCollision& collision, const TBunchCrossing& bunchCrossing, bool& isEventSelected, const int& multTrk) { uint16_t selectionBits = 0; - bool selected; + bool selected = false; // Define selection criteria // If event is selected (passed the cut), set the corresponding bit in the selectionBits variable @@ -462,12 +493,16 @@ struct ZdcQVectors { if (selected) { selectionBits |= static_cast(0x1u << evSel_Zvtx); fillCutAnalysis(collision, bunchCrossing, evSel_Zvtx); + } else if (cfgSelVec.value[evSel_Zvtx]) { + isEventSelected = false; } selected = collision.sel8(); if (selected) { selectionBits |= static_cast(0x1u << evSel_sel8); fillCutAnalysis(collision, bunchCrossing, evSel_sel8); + } else if (cfgSelVec.value[evSel_sel8]) { + isEventSelected = false; } auto occupancy = collision.trackOccupancyInTimeRange(); @@ -475,59 +510,113 @@ struct ZdcQVectors { if (selected) { selectionBits |= static_cast(0x1u << evSel_occupancy); fillCutAnalysis(collision, bunchCrossing, evSel_occupancy); + } else if (cfgSelVec.value[evSel_occupancy]) { + isEventSelected = false; } selected = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); if (selected) { selectionBits |= static_cast(0x1u << evSel_kNoSameBunchPileup); fillCutAnalysis(collision, bunchCrossing, evSel_kNoSameBunchPileup); + } else if (cfgSelVec.value[evSel_kNoSameBunchPileup]) { + isEventSelected = false; } selected = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); if (selected) { selectionBits |= static_cast(0x1u << evSel_kIsGoodZvtxFT0vsPV); fillCutAnalysis(collision, bunchCrossing, evSel_kIsGoodZvtxFT0vsPV); + } else if (cfgSelVec.value[evSel_kIsGoodZvtxFT0vsPV]) { + isEventSelected = false; } selected = collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); if (selected) { selectionBits |= static_cast(0x1u << evSel_kNoCollInTimeRangeStandard); fillCutAnalysis(collision, bunchCrossing, evSel_kNoCollInTimeRangeStandard); + } else if (cfgSelVec.value[evSel_kNoCollInTimeRangeStandard]) { + isEventSelected = false; } selected = collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow); if (selected) { selectionBits |= static_cast(0x1u << evSel_kNoCollInTimeRangeNarrow); fillCutAnalysis(collision, bunchCrossing, evSel_kNoCollInTimeRangeNarrow); + } else if (cfgSelVec.value[evSel_kNoCollInTimeRangeNarrow]) { + isEventSelected = false; } selected = collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC); if (selected) { selectionBits |= static_cast(0x1u << evSel_kIsVertexITSTPC); fillCutAnalysis(collision, bunchCrossing, evSel_kIsVertexITSTPC); + } else if (cfgSelVec.value[evSel_kIsVertexITSTPC]) { + isEventSelected = false; } selected = collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); if (selected) { selectionBits |= static_cast(0x1u << evSel_kIsGoodITSLayersAll); fillCutAnalysis(collision, bunchCrossing, evSel_kIsGoodITSLayersAll); + } else if (cfgSelVec.value[evSel_kIsGoodITSLayersAll]) { + isEventSelected = false; } selected = collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123); if (selected) { selectionBits |= static_cast(0x1u << evSel_kIsGoodITSLayer0123); fillCutAnalysis(collision, bunchCrossing, evSel_kIsGoodITSLayer0123); + } else if (cfgSelVec.value[evSel_kIsGoodITSLayer0123]) { + isEventSelected = false; } selected = rctChecker(collision); if (selected) { selectionBits |= static_cast(0x1u << evSel_RCTFlagsZDC); fillCutAnalysis(collision, bunchCrossing, evSel_RCTFlagsZDC); + } else if (cfgSelVec.value[evSel_RCTFlagsZDC]) { + isEventSelected = false; + } + + float vtxz = -999; + if (collision.numContrib() > 1) { + vtxz = collision.posZ(); + float zRes = std::sqrt(collision.covZZ()); + float minzRes = 0.25; + int maxNumContrib = 20; + if (zRes > minzRes && collision.numContrib() < maxNumContrib) { + vtxz = -999; + } + } + + auto multNTracksPV = collision.multNTracksPV(); + selected = true; + + if (vtxz > cfgVtxZ || vtxz < -cfgVtxZ) { + selected = false; + } + if (multNTracksPV < fMultPVCutLow->Eval(collision.centFT0C())) { + selected = false; + } + if (multNTracksPV > fMultPVCutHigh->Eval(collision.centFT0C())) { + selected = false; + } + if (multTrk < fMultCutLow->Eval(collision.centFT0C())) { + selected = false; + } + if (multTrk > fMultCutHigh->Eval(collision.centFT0C())) { + selected = false; + } + + if (selected) { + selectionBits |= static_cast(0x1u << evSel_MultCut); + fillCutAnalysis(collision, bunchCrossing, evSel_MultCut); + } else if (cfgSelVec.value[evSel_MultCut]) { + isEventSelected = false; } // Fill for centrality estimators! fillCutAnalysis(collision, bunchCrossing, nEventSelections); - return selectionBits; } @@ -536,79 +625,80 @@ struct ZdcQVectors { { // loop for filling multiple histograms with different naming patterns // Always fill the uncentered "raw" Q-vector histos! - if (cfgFillNothing) + if (cfgFillNothing) { return; - static constexpr std::string_view Time[] = {"before", "after"}; - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hZNA_Qx_vs_Qy"), qxa, qya); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hZNC_Qx_vs_Qy"), qxc, qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_cent"), centrality, qxa * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_cent"), centrality, qya * qyc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_cent"), centrality, qya * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_cent"), centrality, qxa * qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_vs_cent"), centrality, qxa); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_vs_cent"), centrality, qya); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXC_vs_cent"), centrality, qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYC_vs_cent"), centrality, qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_vs_vx"), v[0], qxa); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_vs_vx"), v[0], qya); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXC_vs_vx"), v[0], qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYC_vs_vx"), v[0], qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_vx"), v[0], qxa * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_vx"), v[0], qya * qyc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_vx"), v[0], qya * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_vx"), v[0], qxa * qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_vs_vy"), v[1], qxa); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_vs_vy"), v[1], qya); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXC_vs_vy"), v[1], qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYC_vs_vy"), v[1], qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_vy"), v[1], qxa * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_vy"), v[1], qya * qyc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_vy"), v[1], qya * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_vy"), v[1], qxa * qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_vs_vz"), v[2], qxa); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_vs_vz"), v[2], qya); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXC_vs_vz"), v[2], qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYC_vs_vz"), v[2], qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_vz"), v[2], qxa * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_vz"), v[2], qya * qyc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_vz"), v[2], qya * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_vz"), v[2], qxa * qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_vs_timestamp"), rsTimestamp, qxa); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_vs_timestamp"), rsTimestamp, qya); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXC_vs_timestamp"), rsTimestamp, qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYC_vs_timestamp"), rsTimestamp, qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_timestamp"), rsTimestamp, qxa * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_timestamp"), rsTimestamp, qya * qyc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_timestamp"), rsTimestamp, qya * qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_timestamp"), rsTimestamp, qxa * qyc); - - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/ZNA_Qx_vs_Centrality"), centrality, qxa); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/ZNA_Qy_vs_Centrality"), centrality, qya); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/ZNC_Qx_vs_Centrality"), centrality, qxc); - registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/ZNC_Qy_vs_Centrality"), centrality, qyc); + } + static constexpr std::array Time = {"before", "after", "afterShift"}; + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hZNA_Qx_vs_Qy"), qxa, qya); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hZNC_Qx_vs_Qy"), qxc, qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_cent"), centrality, qxa * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_cent"), centrality, qya * qyc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_cent"), centrality, qya * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_cent"), centrality, qxa * qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_vs_cent"), centrality, qxa); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_vs_cent"), centrality, qya); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXC_vs_cent"), centrality, qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYC_vs_cent"), centrality, qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_vs_vx"), v[0], qxa); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_vs_vx"), v[0], qya); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXC_vs_vx"), v[0], qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYC_vs_vx"), v[0], qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_vx"), v[0], qxa * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_vx"), v[0], qya * qyc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_vx"), v[0], qya * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_vx"), v[0], qxa * qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_vs_vy"), v[1], qxa); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_vs_vy"), v[1], qya); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXC_vs_vy"), v[1], qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYC_vs_vy"), v[1], qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_vy"), v[1], qxa * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_vy"), v[1], qya * qyc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_vy"), v[1], qya * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_vy"), v[1], qxa * qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_vs_vz"), v[2], qxa); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_vs_vz"), v[2], qya); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXC_vs_vz"), v[2], qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYC_vs_vz"), v[2], qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_vz"), v[2], qxa * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_vz"), v[2], qya * qyc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_vz"), v[2], qya * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_vz"), v[2], qxa * qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_vs_timestamp"), rsTimestamp, qxa); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_vs_timestamp"), rsTimestamp, qya); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXC_vs_timestamp"), rsTimestamp, qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYC_vs_timestamp"), rsTimestamp, qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QXC_vs_timestamp"), rsTimestamp, qxa * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QYC_vs_timestamp"), rsTimestamp, qya * qyc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQYA_QXC_vs_timestamp"), rsTimestamp, qya * qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/hQXA_QYC_vs_timestamp"), rsTimestamp, qxa * qyc); + + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/ZNA_Qx_vs_Centrality"), centrality, qxa); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/ZNA_Qy_vs_Centrality"), centrality, qya); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/ZNC_Qx_vs_Centrality"), centrality, qxc); + registry.fill(HIST("recentering/") + HIST(Time[ft]) + HIST("/ZNC_Qy_vs_Centrality"), centrality, qyc); // add psi!! - double psiA = 1.0 * std::atan2(qxc, qxa); + double psiA = 1.0 * std::atan2(qya, qxa); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneA"), psiA, centrality, 1); - double psiC = 1.0 * std::atan2(qyc, qya); + double psiC = 1.0 * std::atan2(qyc, qxc); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneC"), psiC, centrality, 1); - double psiFull = 1.0 * std::atan2(qxc + qyc, qxa + qya); + double psiFull = 1.0 * std::atan2(qya + qyc, qxa + qxc); registry.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hSPplaneFull"), psiFull, centrality, 1); } template - void loadCalibrations(std::string ccdb_dir) + void loadCalibrations(const std::string& ccdb_dir, uint64_t timestamp) { // iteration = 0 (Energy calibration) -> step 0 only // iteration 1,2,3,4,5 = recentering -> 5 steps per iteration (1x 4D + 4x 1D) @@ -617,12 +707,11 @@ struct ZdcQVectors { return; } - if (ccdb_dir.empty() == false) { + if (!ccdb_dir.empty()) { cal.calibList[cm] = ccdb->getForTimeStamp(ccdb_dir, timestamp); cal.calibfilesLoaded[cm] = true; LOGF(info, "Loaded calibration histos from %s", ccdb_dir.c_str()); if (cm == kRec) { - cal.atStep = 5; cal.atIteration = 5; } } @@ -636,21 +725,25 @@ struct ZdcQVectors { if (cm == kEnergyCal || cm == kMeanv) { TList* list = cal.calibList[cm]; - hist = reinterpret_cast(list->FindObject(Form("%s", objName))); + hist = dynamic_cast(list->FindObject(Form("%s", objName))); } else if (cm == kTimestamp) { - TList* list = reinterpret_cast(cal.calibList[cm]->FindObject(Form("it%i_step%i", iteration, step))); - hist = reinterpret_cast(list->FindObject(Form("%s", objName))); - } else if (cm == kRec) { - TList* list = reinterpret_cast(cal.calibList[cm]->FindObject(Form("it%i_step%i", iteration, step))); + auto list = dynamic_cast(cal.calibList[cm]->FindObject(Form("it%i_step%i", iteration, step))); if (!list) { LOGF(fatal, "No calibration list for iteration %i and step %i", iteration, step); } - hist = reinterpret_cast(list->FindObject(Form("%s", objName))); + hist = dynamic_cast(list->FindObject(Form("%s", objName))); + if (!hist) { + LOGF(fatal, "No calibration histo for iteration %i and step %i -> %s", iteration, step, objName); + } + } else if (cm == kRec || cm == kRecShift) { + auto list = dynamic_cast(cal.calibList[cm]->FindObject(Form("it%i_step%i", iteration, step))); + if (!list) { + LOGF(fatal, "No calibration list for iteration %i and step %i", iteration, step); + } + hist = dynamic_cast(list->FindObject(Form("%s", objName))); if (!hist) { LOGF(fatal, "No calibration histo for iteration %i and step %i -> %s", iteration, step, objName); } - cal.atStep = step; - cal.atIteration = iteration; } if (!hist) { LOGF(fatal, "%s not available.. Abort..", objName); @@ -658,56 +751,62 @@ struct ZdcQVectors { if (hist->InheritsFrom("TProfile2D")) { // needed for energy calibration! - TProfile2D* h = reinterpret_cast(hist); - TString name = h->GetName(); - int binrunnumber = h->GetXaxis()->FindBin(TString::Format("%d", runnumber)); - int bin = h->GetYaxis()->FindBin(centrality); - calibConstant = h->GetBinContent(binrunnumber, bin); - } else if (hist->InheritsFrom("TProfile")) { - TProfile* h = reinterpret_cast(hist); - TString name = h->GetName(); - int bin{}; - if (name.Contains("mean_vx")) { - bin = h->GetXaxis()->FindBin(v[0]); + auto h = dynamic_cast(hist); + if (h) { + TString name = h->GetName(); + int binrunnumber = h->GetXaxis()->FindBin(TString::Format("%d", cal.runnumber)); + int bin = h->GetYaxis()->FindBin(cal.centrality); + calibConstant = h->GetBinContent(binrunnumber, bin); } - if (name.Contains("mean_vy")) { - bin = h->GetXaxis()->FindBin(v[1]); - } - if (name.Contains("mean_vz")) { - bin = h->GetXaxis()->FindBin(v[2]); - } - if (name.Contains("mean_cent")) { - bin = h->GetXaxis()->FindBin(centrality); - } - if (name.Contains("vertex")) { - bin = h->GetXaxis()->FindBin(TString::Format("%i", runnumber)); - } - if (name.Contains("timestamp")) { - bin = h->GetXaxis()->FindBin(rsTimestamp); + } else if (hist->InheritsFrom("TProfile")) { + auto h = dynamic_cast(hist); + if (h) { + TString name = h->GetName(); + int bin{}; + if (name.Contains("mean_vx")) { + bin = h->GetXaxis()->FindBin(cal.v[0]); + } + if (name.Contains("mean_vy")) { + bin = h->GetXaxis()->FindBin(cal.v[1]); + } + if (name.Contains("mean_vz")) { + bin = h->GetXaxis()->FindBin(cal.v[2]); + } + if (name.Contains("mean_cent")) { + bin = h->GetXaxis()->FindBin(cal.centrality); + } + if (name.Contains("vertex")) { + bin = h->GetXaxis()->FindBin(TString::Format("%i", cal.runnumber)); + } + if (name.Contains("timestamp")) { + bin = h->GetXaxis()->FindBin(cal.rsTimestamp); + } + calibConstant = h->GetBinContent(bin); } - calibConstant = h->GetBinContent(bin); } else if (hist->InheritsFrom("THnSparse")) { std::vector sparsePars; - THnSparseD* h = reinterpret_cast(hist); - sparsePars.push_back(h->GetAxis(0)->FindBin(centrality)); - sparsePars.push_back(h->GetAxis(1)->FindBin(v[0])); - sparsePars.push_back(h->GetAxis(2)->FindBin(v[1])); - sparsePars.push_back(h->GetAxis(3)->FindBin(v[2])); - - for (std::size_t i = 0; i < sparsePars.size(); i++) { - h->GetAxis(i)->SetRange(sparsePars[i], sparsePars[i]); - } + auto h = dynamic_cast(hist); + if (h) { + sparsePars.push_back(h->GetAxis(0)->FindBin(cal.centrality)); + sparsePars.push_back(h->GetAxis(1)->FindBin(cal.v[0])); + sparsePars.push_back(h->GetAxis(2)->FindBin(cal.v[1])); + sparsePars.push_back(h->GetAxis(3)->FindBin(cal.v[2])); + + for (std::size_t i = 0; i < sparsePars.size(); i++) { + h->GetAxis(i)->SetRange(sparsePars[i], sparsePars[i]); + } - TH1D* tempProj = h->Projection(4); - calibConstant = tempProj->GetMean(); + auto tempProj = h->Projection(4); + calibConstant = tempProj->GetMean(); - if (tempProj->GetEntries() < cfgMinEntriesSparseBin) { - LOGF(debug, "1 entry in sparse bin! Not used... (increase binsize)"); - calibConstant = 0; - isSelected = false; - } + if (tempProj->GetEntries() < cfgMinEntriesSparseBin) { + LOGF(debug, "1 entry in sparse bin! Not used... (increase binsize)"); + calibConstant = 0; + cal.isSelected = false; + } - delete tempProj; + delete tempProj; + } } return calibConstant; @@ -715,7 +814,8 @@ struct ZdcQVectors { void process(UsedCollisions::iterator const& collision, BCsRun3 const& /*bcs*/, - aod::Zdcs const& /*zdcs*/) + aod::Zdcs const& /*zdcs*/, + UsedTracks const& tracks) { // for Q-vector calculation // A[0] & C[1] @@ -723,55 +823,85 @@ struct ZdcQVectors { std::vector xEnZN(2, 0.), xEnZN_noEq(2, 0.); std::vector yEnZN(2, 0.), yEnZN_noEq(2, 0.); - isSelected = true; + cal.isSelected = true; - std::vector centralities; + std::vector cents; auto cent = collision.centFT0C(); - - centralities.push_back(collision.centFT0C()); + cents.push_back(collision.centFT0C()); if (cfgFT0Cvariant1) { - centralities.push_back(collision.centFT0CVariant1()); - if (cfgUseSecondCent) + cents.push_back(collision.centFT0CVariant1()); + if (cfgUseSecondCent) { cent = collision.centFT0CVariant1(); + } } if (cfgFT0M) { - centralities.push_back(collision.centFT0M()); - if (cfgUseSecondCent) + cents.push_back(collision.centFT0M()); + if (cfgUseSecondCent) { cent = collision.centFT0M(); + } } if (cfgFV0A) { - centralities.push_back(collision.centFV0A()); - if (cfgUseSecondCent) + cents.push_back(collision.centFV0A()); + if (cfgUseSecondCent) { cent = collision.centFV0A(); + } } if (cfgNGlobal) { - centralities.push_back(collision.centNGlobal()); - if (cfgUseSecondCent) + cents.push_back(collision.centNGlobal()); + if (cfgUseSecondCent) { cent = collision.centNGlobal(); + } } - v = {collision.posX(), collision.posY(), collision.posZ()}; - cents = centralities; - centrality = cent; + std::vector v = {collision.posX(), collision.posY(), collision.posZ()}; + cal.v = v; const auto& foundBC = collision.foundBC_as(); - runnumber = foundBC.runNumber(); + auto runnumber = foundBC.runNumber(); + cal.runnumber = runnumber; + cal.centrality = cent; + + // load new calibrations for new runs only + if (runnumber != cal.lastRunNumber) { + cal.calibfilesLoaded[kEnergyCal] = false; + cal.calibList[kEnergyCal] = nullptr; + + cal.calibfilesLoaded[kMeanv] = false; + cal.calibList[kMeanv] = nullptr; + + cal.calibfilesLoaded[kRec] = false; + cal.calibList[kRec] = nullptr; - if (cfgFillHistRegistry && !cfgFillNothing) + cal.calibfilesLoaded[kTimestamp] = false; + cal.calibList[kTimestamp] = nullptr; + + cal.calibfilesLoaded[kRecShift] = false; + cal.calibList[kRecShift] = nullptr; + + cal.isShiftProfileFound = false; + cal.shiftprofileC = nullptr; + cal.shiftprofileA = nullptr; + + cal.atIteration = 0; + } + + if (cfgFillHistRegistry && !cfgFillNothing) { registry.fill(HIST("QA/centrality_before"), cent); + } registry.fill(HIST("hEventCount"), evSel_FilteredEvent); - timestamp = foundBC.timestamp(); - rsTimestamp = rescaleTimestamp(timestamp, runnumber); + uint64_t timestamp = foundBC.timestamp(); + auto rsTimestamp = rescaleTimestamp(timestamp, runnumber); + cal.timestamp = timestamp; + cal.rsTimestamp = rsTimestamp; if (!foundBC.has_zdc()) { - isSelected = false; - spTableZDC(runnumber, cents, v, foundBC.timestamp(), 0, 0, 0, 0, isSelected, 0); - counter++; - lastRunNumber = runnumber; + cal.isSelected = false; + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), 0, 0, 0, 0, cal.isSelected, 0); + cal.lastRunNumber = runnumber; return; } registry.fill(HIST("hEventCount"), evSel_BCHasZDC); @@ -782,6 +912,11 @@ struct ZdcQVectors { int nTowers = 8; int nTowersPerSide = 4; + // for energy calibration + std::array eZN{}; // uncalibrated energy for the 2x4 towers (a1, a2, a3, a4, c1, c2, c3, c4) + std::array meanEZN{}; // mean energies from calibration histos (common A, t1-4 A,common C, t1-4C) + std::array e{}; // calibrated energies (a1, a2, a3, a4, c1, c2, c3, c4)) + for (int tower = 0; tower < nTowers; tower++) { eZN[tower] = (tower < nTowersPerSide) ? zdcCol.energySectorZNA()[tower] : zdcCol.energySectorZNC()[tower % nTowersPerSide]; } @@ -790,105 +925,80 @@ struct ZdcQVectors { bool isZNChit = true; for (int i = 0; i < nTowers; ++i) { - if (i < nTowersPerSide && eZN[i] <= 0) + if (i < nTowersPerSide && eZN[i] <= 0) { isZNAhit = false; - if (i >= nTowersPerSide && eZN[i] <= 0) + } + if (i >= nTowersPerSide && eZN[i] <= 0) { isZNChit = false; + } } - if (zdcCol.energyCommonZNA() <= 0) + if (zdcCol.energyCommonZNA() <= 0) { isZNAhit = false; - if (zdcCol.energyCommonZNC() <= 0) + } + if (zdcCol.energyCommonZNC() <= 0) { isZNChit = false; + } // if ZNA or ZNC not hit correctly.. do not use event in q-vector calculation if (!isZNAhit || !isZNChit) { - counter++; - isSelected = false; - spTableZDC(runnumber, cents, v, foundBC.timestamp(), 0, 0, 0, 0, isSelected, 0); - lastRunNumber = runnumber; + cal.isSelected = false; + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), 0, 0, 0, 0, cal.isSelected, 0); + cal.lastRunNumber = runnumber; return; } registry.fill(HIST("hEventCount"), evSel_isSelectedZDC); - uint16_t eventSelectionFlags = eventSelected(collision, foundBC.zdc()); + // Use this bool to check for given set of event selections of Q-vectors would be selected + // Enable plotting only if event would be selected + bool isEventSelected = true; + + uint16_t eventSelectionFlags = eventSelected(collision, foundBC.zdc(), isEventSelected, tracks.size()); // ALWAYS use these event selections - if (cent < EvSel.cfgCentMin || cent > EvSel.cfgCentMax || !collision.sel8() || std::abs(collision.posZ()) > cfgVtxZ) { + if (cent < EvSel.cfgCentMin || cent > EvSel.cfgCentMax || std::abs(collision.posZ()) > cfgVtxZ || !collision.sel8()) { // event not selected - isSelected = false; - spTableZDC(runnumber, cents, v, foundBC.timestamp(), 0, 0, 0, 0, isSelected, eventSelectionFlags); - counter++; - lastRunNumber = runnumber; + cal.isSelected = false; + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), 0, 0, 0, 0, cal.isSelected, eventSelectionFlags); + cal.lastRunNumber = runnumber; return; } registry.fill(HIST("hEventCount"), evSel_CentCuts); - // load new calibrations for new runs only - if (runnumber != lastRunNumber) { - cal.calibfilesLoaded[0] = false; - cal.calibList[0] = nullptr; - - cal.calibfilesLoaded[1] = false; - cal.calibList[1] = nullptr; - - cal.calibfilesLoaded[2] = false; - cal.calibList[2] = nullptr; - - cal.calibfilesLoaded[3] = false; - cal.calibList[3] = nullptr; - - cal.isShiftProfileFound = false; - cal.shiftprofileC = nullptr; - cal.shiftprofileA = nullptr; - } - // load the calibration histos for iteration 0 step 0 (Energy Calibration) - loadCalibrations(cfgEnergyCal.value); - - if (!cal.calibfilesLoaded[0]) { - if (counter < 1) { - LOGF(info, " --> No Energy calibration files found.. -> Only Energy calibration will be done. "); - } + if (!cfgNoGain) { + loadCalibrations(cfgCCDBDir_EnergyCal.value, timestamp); } + // load the calibrations for the mean v - loadCalibrations(cfgMeanv.value); + loadCalibrations(cfgCCDBDir_Meanv.value, timestamp); - if (!cfgFillNothing) { + if (!cfgFillNothing && isEventSelected) { registry.get(HIST("vmean/hvertex_vx"))->Fill(Form("%d", runnumber), v[0]); registry.get(HIST("vmean/hvertex_vy"))->Fill(Form("%d", runnumber), v[1]); registry.get(HIST("vmean/hvertex_vz"))->Fill(Form("%d", runnumber), v[2]); // Fill to get mean energy per tower in 1% centrality bins - if (isZNAhit) { - registry.get(HIST("Energy/hZNA_mean_t0_cent"))->Fill(Form("%d", runnumber), cent, zdcCol.energyCommonZNA(), 1); - registry.get(HIST("Energy/hZNA_mean_t1_cent"))->Fill(Form("%d", runnumber), cent, eZN[0], 1); - registry.get(HIST("Energy/hZNA_mean_t2_cent"))->Fill(Form("%d", runnumber), cent, eZN[1], 1); - registry.get(HIST("Energy/hZNA_mean_t3_cent"))->Fill(Form("%d", runnumber), cent, eZN[2], 1); - registry.get(HIST("Energy/hZNA_mean_t4_cent"))->Fill(Form("%d", runnumber), cent, eZN[3], 1); - } - if (isZNChit) { - registry.get(HIST("Energy/hZNC_mean_t0_cent"))->Fill(Form("%d", runnumber), cent, zdcCol.energyCommonZNC(), 1); - registry.get(HIST("Energy/hZNC_mean_t1_cent"))->Fill(Form("%d", runnumber), cent, eZN[4], 1); - registry.get(HIST("Energy/hZNC_mean_t2_cent"))->Fill(Form("%d", runnumber), cent, eZN[5], 1); - registry.get(HIST("Energy/hZNC_mean_t3_cent"))->Fill(Form("%d", runnumber), cent, eZN[6], 1); - registry.get(HIST("Energy/hZNC_mean_t4_cent"))->Fill(Form("%d", runnumber), cent, eZN[7], 1); - } - } - // Do not continue if Energy calibration is not loaded - if (!cal.calibfilesLoaded[0]) { - counter++; - isSelected = false; - spTableZDC(runnumber, cents, v, foundBC.timestamp(), 0, 0, 0, 0, isSelected, eventSelectionFlags); - lastRunNumber = runnumber; - return; + registry.get(HIST("Energy/hZNA_mean_t0_cent"))->Fill(Form("%d", runnumber), cent, zdcCol.energyCommonZNA(), 1); + registry.get(HIST("Energy/hZNA_mean_t1_cent"))->Fill(Form("%d", runnumber), cent, eZN[0], 1); + registry.get(HIST("Energy/hZNA_mean_t2_cent"))->Fill(Form("%d", runnumber), cent, eZN[1], 1); + registry.get(HIST("Energy/hZNA_mean_t3_cent"))->Fill(Form("%d", runnumber), cent, eZN[2], 1); + registry.get(HIST("Energy/hZNA_mean_t4_cent"))->Fill(Form("%d", runnumber), cent, eZN[3], 1); + + registry.get(HIST("Energy/hZNC_mean_t0_cent"))->Fill(Form("%d", runnumber), cent, zdcCol.energyCommonZNC(), 1); + registry.get(HIST("Energy/hZNC_mean_t1_cent"))->Fill(Form("%d", runnumber), cent, eZN[4], 1); + registry.get(HIST("Energy/hZNC_mean_t2_cent"))->Fill(Form("%d", runnumber), cent, eZN[5], 1); + registry.get(HIST("Energy/hZNC_mean_t3_cent"))->Fill(Form("%d", runnumber), cent, eZN[6], 1); + registry.get(HIST("Energy/hZNC_mean_t4_cent"))->Fill(Form("%d", runnumber), cent, eZN[7], 1); } // Now start gain equalisation! // Fill the list with calibration constants. - for (int tower = 0; tower < (nTowers + 2); tower++) { - meanEZN[tower] = getCorrection(namesEcal[tower].Data()); + if (!cfgNoGain) { + for (int tower = 0; tower < (nTowers + 2); tower++) { + meanEZN[tower] = getCorrection(namesEcal[tower].Data()); + } } // Use the calibration constants but now only loop over towers 1-4 @@ -896,14 +1006,18 @@ struct ZdcQVectors { std::vector towersNocom = {1, 2, 3, 4, 6, 7, 8, 9}; for (const auto& tower : towersNocom) { - if (meanEZN[tower] > 0) { - double ecommon = (tower > nTowersPerSide) ? meanEZN[5] : meanEZN[0]; - e[calibtower] = eZN[calibtower] * (0.25 * ecommon) / meanEZN[tower]; + if (cfgNoGain) { + e[calibtower] = eZN[calibtower]; + } else { + if (meanEZN[tower] > 0) { + double ecommon = (tower > nTowersPerSide) ? meanEZN[5] : meanEZN[0]; + e[calibtower] = eZN[calibtower] * (0.25 * ecommon) / meanEZN[tower]; + } } calibtower++; } - if (cfgFillHistRegistry && !cfgFillNothing) { + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { for (int i = 0; i < nTowersPerSide; i++) { float bincenter = i + .5; registry.fill(HIST("QA/ZNA_Energy"), bincenter, eZN[i]); @@ -911,60 +1025,35 @@ struct ZdcQVectors { registry.fill(HIST("QA/ZNC_Energy"), bincenter, eZN[i + 4]); registry.fill(HIST("QA/ZNC_Energy"), bincenter + 4, e[i + 4]); - registry.get(HIST("QA/before/ZNA_pmC"))->Fill(Form("%d", runnumber), meanEZN[0]); - registry.get(HIST("QA/before/ZNA_pm1"))->Fill(Form("%d", runnumber), eZN[0]); - registry.get(HIST("QA/before/ZNA_pm2"))->Fill(Form("%d", runnumber), eZN[1]); - registry.get(HIST("QA/before/ZNA_pm3"))->Fill(Form("%d", runnumber), eZN[2]); - registry.get(HIST("QA/before/ZNA_pm4"))->Fill(Form("%d", runnumber), eZN[3]); - - registry.get(HIST("QA/before/ZNC_pmC"))->Fill(Form("%d", runnumber), meanEZN[5]); - registry.get(HIST("QA/before/ZNC_pm1"))->Fill(Form("%d", runnumber), eZN[4]); - registry.get(HIST("QA/before/ZNC_pm2"))->Fill(Form("%d", runnumber), eZN[5]); - registry.get(HIST("QA/before/ZNC_pm3"))->Fill(Form("%d", runnumber), eZN[6]); - registry.get(HIST("QA/before/ZNC_pm4"))->Fill(Form("%d", runnumber), eZN[7]); - - registry.get(HIST("QA/after/ZNA_pm1"))->Fill(Form("%d", runnumber), e[0]); - registry.get(HIST("QA/after/ZNA_pm2"))->Fill(Form("%d", runnumber), e[1]); - registry.get(HIST("QA/after/ZNA_pm3"))->Fill(Form("%d", runnumber), e[2]); - registry.get(HIST("QA/after/ZNA_pm4"))->Fill(Form("%d", runnumber), e[3]); - registry.get(HIST("QA/after/ZNC_pm1"))->Fill(Form("%d", runnumber), e[4]); - registry.get(HIST("QA/after/ZNC_pm2"))->Fill(Form("%d", runnumber), e[5]); - registry.get(HIST("QA/after/ZNC_pm3"))->Fill(Form("%d", runnumber), e[6]); - registry.get(HIST("QA/after/ZNC_pm4"))->Fill(Form("%d", runnumber), e[7]); - - double sumZNAbefore = eZN[0] + eZN[1] + eZN[2] + eZN[3]; - double sumZNAafter = e[0] + e[1] + e[2] + e[3]; - - double sumZNCbefore = eZN[4] + eZN[5] + eZN[6] + eZN[7]; - double sumZNCafter = e[4] + e[5] + e[6] + e[7]; - - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNA_pmC_vs_Centrality"), centrality, zdcCol.energyCommonZNA()); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNA_pmSUM_vs_Centrality"), centrality, sumZNAbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNA_pm1_vs_Centrality"), centrality, eZN[0] / sumZNAbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNA_pm2_vs_Centrality"), centrality, eZN[1] / sumZNAbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNA_pm3_vs_Centrality"), centrality, eZN[2] / sumZNAbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNA_pm4_vs_Centrality"), centrality, eZN[3] / sumZNAbefore); - - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNC_pmC_vs_Centrality"), centrality, zdcCol.energyCommonZNC()); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNC_pmSUM_vs_Centrality"), centrality, sumZNCbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNC_pm1_vs_Centrality"), centrality, eZN[4] / sumZNCbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNC_pm2_vs_Centrality"), centrality, eZN[5] / sumZNCbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNC_pm3_vs_Centrality"), centrality, eZN[6] / sumZNCbefore); - registry.fill(HIST("QA/") + HIST("before") + HIST("/ZNC_pm4_vs_Centrality"), centrality, eZN[7] / sumZNCbefore); - - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNA_pmC_vs_Centrality"), centrality, zdcCol.energyCommonZNA()); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNA_pmSUM_vs_Centrality"), centrality, sumZNAafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNA_pm1_vs_Centrality"), centrality, e[0] / sumZNAafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNA_pm2_vs_Centrality"), centrality, e[1] / sumZNAafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNA_pm3_vs_Centrality"), centrality, e[2] / sumZNAafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNA_pm4_vs_Centrality"), centrality, e[3] / sumZNAafter); - - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNC_pmC_vs_Centrality"), centrality, zdcCol.energyCommonZNC()); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNC_pmSUM_vs_Centrality"), centrality, sumZNCafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNC_pm1_vs_Centrality"), centrality, e[4] / sumZNCafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNC_pm2_vs_Centrality"), centrality, e[5] / sumZNCafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNC_pm3_vs_Centrality"), centrality, e[6] / sumZNCafter); - registry.fill(HIST("QA/") + HIST("after") + HIST("/ZNC_pm4_vs_Centrality"), centrality, e[7] / sumZNCafter); + registry.get(HIST("QA/ZNA_pmC"))->Fill(Form("%d", runnumber), zdcCol.energyCommonZNA()); + registry.get(HIST("QA/ZNC_pmC"))->Fill(Form("%d", runnumber), zdcCol.energyCommonZNC()); + registry.get(HIST("QA/ZNA_pm1"))->Fill(Form("%d", runnumber), e[0]); + registry.get(HIST("QA/ZNA_pm2"))->Fill(Form("%d", runnumber), e[1]); + registry.get(HIST("QA/ZNA_pm3"))->Fill(Form("%d", runnumber), e[2]); + registry.get(HIST("QA/ZNA_pm4"))->Fill(Form("%d", runnumber), e[3]); + registry.get(HIST("QA/ZNC_pm1"))->Fill(Form("%d", runnumber), e[4]); + registry.get(HIST("QA/ZNC_pm2"))->Fill(Form("%d", runnumber), e[5]); + registry.get(HIST("QA/ZNC_pm3"))->Fill(Form("%d", runnumber), e[6]); + registry.get(HIST("QA/ZNC_pm4"))->Fill(Form("%d", runnumber), e[7]); + + double sumZNA = e[0] + e[1] + e[2] + e[3]; + double sumZNC = e[4] + e[5] + e[6] + e[7]; + + registry.fill(HIST("QA/ZNA_pmC_vs_Centrality"), cent, zdcCol.energyCommonZNA()); + registry.fill(HIST("QA/ZNA_pmSUM_vs_Centrality"), cent, sumZNA); + + registry.fill(HIST("QA/ZNC_pmC_vs_Centrality"), cent, zdcCol.energyCommonZNC()); + registry.fill(HIST("QA/ZNC_pmSUM_vs_Centrality"), cent, sumZNC); + + registry.fill(HIST("QA/ZNA_pm1_vs_Centrality"), cent, e[0] / sumZNA); + registry.fill(HIST("QA/ZNA_pm2_vs_Centrality"), cent, e[1] / sumZNA); + registry.fill(HIST("QA/ZNA_pm3_vs_Centrality"), cent, e[2] / sumZNA); + registry.fill(HIST("QA/ZNA_pm4_vs_Centrality"), cent, e[3] / sumZNA); + + registry.fill(HIST("QA/ZNC_pm1_vs_Centrality"), cent, e[4] / sumZNC); + registry.fill(HIST("QA/ZNC_pm2_vs_Centrality"), cent, e[5] / sumZNC); + registry.fill(HIST("QA/ZNC_pm3_vs_Centrality"), cent, e[6] / sumZNC); + registry.fill(HIST("QA/ZNC_pm4_vs_Centrality"), cent, e[7] / sumZNC); } } @@ -976,70 +1065,62 @@ struct ZdcQVectors { sumZN[side] += energy; xEnZN[side] += (side == 0) ? -1.0 * pxZDC[sector] * energy : pxZDC[sector] * energy; yEnZN[side] += pyZDC[sector] * energy; - - // Also calculate the Q-vector for the non-equalized energy - double energyNoEq = std::pow(eZN[tower], alphaZDC); - sumZN_noEq[side] += energyNoEq; - xEnZN_noEq[side] += (side == 0) ? -1.0 * pxZDC[sector] * energyNoEq : pxZDC[sector] * energyNoEq; - yEnZN_noEq[side] += pyZDC[sector] * energyNoEq; } // "QXA", "QYA", "QXC", "QYC" + std::array q = {0, 0, 0, 0}; + int sides = 2; for (int i = 0; i < sides; ++i) { if (sumZN[i] > 0) { q[i * 2] = xEnZN[i] / sumZN[i]; // for QXA[0] and QXC[2] q[i * 2 + 1] = yEnZN[i] / sumZN[i]; // for QYA[1] and QYC[3] } - if (sumZN_noEq[i] > 0) { - qNoEq[i * 2] = xEnZN_noEq[i] / sumZN_noEq[i]; // for QXA[0] and QXC[2] - qNoEq[i * 2 + 1] = yEnZN_noEq[i] / sumZN_noEq[i]; // for QYA[1] and QYC[3] - } } - if (cfgFillHistRegistry && !cfgFillNothing) { + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { registry.get(HIST("QA/before/ZNA_Qx"))->Fill(Form("%d", runnumber), q[0]); registry.get(HIST("QA/before/ZNA_Qy"))->Fill(Form("%d", runnumber), q[1]); registry.get(HIST("QA/before/ZNC_Qx"))->Fill(Form("%d", runnumber), q[2]); registry.get(HIST("QA/before/ZNC_Qy"))->Fill(Form("%d", runnumber), q[3]); - - registry.get(HIST("QA/before/ZNA_Qx_noEq"))->Fill(Form("%d", runnumber), qNoEq[0]); - registry.get(HIST("QA/before/ZNA_Qy_noEq"))->Fill(Form("%d", runnumber), qNoEq[1]); - registry.get(HIST("QA/before/ZNC_Qx_noEq"))->Fill(Form("%d", runnumber), qNoEq[2]); - registry.get(HIST("QA/before/ZNC_Qy_noEq"))->Fill(Form("%d", runnumber), qNoEq[3]); - } - - if (cfgNoGain) { - q = qNoEq; } if (cal.calibfilesLoaded[1]) { - v[0] = v[0] - getCorrection(vnames[0].Data()); - v[1] = v[1] - getCorrection(vnames[1].Data()); + cal.v[0] = v[0] - getCorrection(vnames[0].Data()); + cal.v[1] = v[1] - getCorrection(vnames[1].Data()); } else { LOGF(warning, " --> No mean V found.. -> THis wil lead to wrong axis for vx, vy (will be created in vmean/)"); return; } - loadCalibrations(cfgRec.value); + // Load in TList with all recentering histos + loadCalibrations(cfgCCDBDir_Rec.value, timestamp); - if (extraTS.cfgRecenterForTimestamp) { - loadCalibrations(extraTS.cfgCCDBdir_Timestamp.value); + // If CCDB dir given load recentering for extra step Timestamp + if (extraTS.cfgCCDBDir_RecenterForTimestamp) { + loadCalibrations(extraTS.cfgCCDBdir_Timestamp.value, timestamp); } - std::vector qRec(q); + // If CCDB dir given load recentering after shift correction. + if (!cfgCCDBdir_ShiftRec.value.empty()) { + loadCalibrations(cfgCCDBdir_ShiftRec.value, timestamp); + } + + std::array qRec(q); if (cal.atIteration == 0) { - if (isSelected && cfgFillHistRegistry) - fillCommonRegistry(q[0], q[1], q[2], q[3], v, centrality, rsTimestamp); + if (cal.isSelected && cfgFillHistRegistry && isEventSelected) { + fillCommonRegistry(q[0], q[1], q[2], q[3], cal.v, cent, rsTimestamp); + } - spTableZDC(runnumber, cents, v, foundBC.timestamp(), q[0], q[1], q[2], q[3], isSelected, eventSelectionFlags); - counter++; - lastRunNumber = runnumber; + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), q[0], q[1], q[2], q[3], cal.isSelected, eventSelectionFlags); + cal.lastRunNumber = runnumber; return; - } else { - if (cfgFillHistRegistry) - fillCommonRegistry(q[0], q[1], q[2], q[3], v, centrality, rsTimestamp); + } + + if (cfgFillHistRegistry && isEventSelected) { + fillCommonRegistry(q[0], q[1], q[2], q[3], cal.v, cent, rsTimestamp); + } // vector of 4 std::vector corrQxA; @@ -1057,6 +1138,13 @@ struct ZdcQVectors { corrQyA.push_back(getCorrection(names[0][1].Data(), it, 1)); corrQxC.push_back(getCorrection(names[0][2].Data(), it, 1)); corrQyC.push_back(getCorrection(names[0][3].Data(), it, 1)); + + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, q[0] - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, q[1] - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, q[2] - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, q[3] - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } pb++; for (int step = 2; step <= nSteps; step++) { @@ -1064,14 +1152,29 @@ struct ZdcQVectors { corrQyA.push_back(getCorrection(names[step - 1][1].Data(), it, step)); corrQxC.push_back(getCorrection(names[step - 1][2].Data(), it, step)); corrQyC.push_back(getCorrection(names[step - 1][3].Data(), it, step)); + + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, q[0] - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, q[1] - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, q[2] - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, q[3] - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } + pb++; } - if (extraTS.cfgRecenterForTimestamp) { + if (extraTS.cfgCCDBDir_RecenterForTimestamp) { corrQxA.push_back(getCorrection(namesTS[0].Data(), it, 6)); corrQyA.push_back(getCorrection(namesTS[1].Data(), it, 6)); corrQxC.push_back(getCorrection(namesTS[2].Data(), it, 6)); corrQyC.push_back(getCorrection(namesTS[3].Data(), it, 6)); + + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, q[0] - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, q[1] - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, q[2] - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, q[3] - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } pb++; } } @@ -1086,15 +1189,6 @@ struct ZdcQVectors { qRec[2] -= totalCorrectionQxC; qRec[3] -= totalCorrectionQyC; - if (cfgFillHistRegistry && !cfgFillNothing) { - for (int cor = 0; cor < pb; cor++) { - registry.get(HIST("QA/QXA_vs_iteration"))->Fill(cor, qRec[0]); - registry.get(HIST("QA/QYA_vs_iteration"))->Fill(cor, qRec[1]); - registry.get(HIST("QA/QXC_vs_iteration"))->Fill(cor, qRec[2]); - registry.get(HIST("QA/QYC_vs_iteration"))->Fill(cor, qRec[3]); - } - } - // do shift for psi. double psiZDCA = 1.0 * std::atan2(qRec[1], qRec[0]); double psiZDCC = 1.0 * std::atan2(qRec[3], qRec[2]); @@ -1107,11 +1201,17 @@ struct ZdcQVectors { double deltaPsiZDCA = 0; double deltaPsiZDCC = 0; - if (!cfgCCDBdir_Shift.value.empty() && cal.isShiftProfileFound == false) { + if (!cfgCCDBdir_Shift.value.empty() && !cal.isShiftProfileFound) { LOGF(info, "Getting shift profile from CCDB for runnumber: %d", runnumber); - TList* hcorrList = ccdb->getForTimeStamp(cfgCCDBdir_Shift.value, foundBC.timestamp()); - cal.shiftprofileC = reinterpret_cast(hcorrList->FindObject("ShiftZDCC")); - cal.shiftprofileA = reinterpret_cast(hcorrList->FindObject("ShiftZDCA")); + auto hcorrList = ccdb->getForTimeStamp(cfgCCDBdir_Shift.value, foundBC.timestamp()); + auto shiftProfileC = dynamic_cast(hcorrList->FindObject("ShiftZDCC")); + if (shiftProfileC) { + cal.shiftprofileC = shiftProfileC; + } + auto shiftProfileA = dynamic_cast(hcorrList->FindObject("ShiftZDCA")); + if (shiftProfileA) { + cal.shiftprofileA = shiftProfileA; + } if (!cal.shiftprofileC || !cal.shiftprofileA) { LOGF(error, "Shift profile not found in CCDB for runnumber: %d", runnumber); cal.isShiftProfileFound = false; @@ -1122,11 +1222,11 @@ struct ZdcQVectors { } for (int ishift = 1; ishift <= nshift; ishift++) { - if (!cfgFillNothing) { - registry.fill(HIST("shift/ShiftZDCC"), centrality, 0.5, ishift - 0.5, std::sin(ishift * 1.0 * psiZDCC)); - registry.fill(HIST("shift/ShiftZDCC"), centrality, 1.5, ishift - 0.5, std::cos(ishift * 1.0 * psiZDCC)); - registry.fill(HIST("shift/ShiftZDCA"), centrality, 0.5, ishift - 0.5, std::sin(ishift * 1.0 * psiZDCA)); - registry.fill(HIST("shift/ShiftZDCA"), centrality, 1.5, ishift - 0.5, std::cos(ishift * 1.0 * psiZDCA)); + if (!cfgFillNothing && isEventSelected) { + registry.fill(HIST("shift/ShiftZDCC"), cent, 0.5, ishift - 0.5, std::sin(ishift * 1.0 * psiZDCC)); + registry.fill(HIST("shift/ShiftZDCC"), cent, 1.5, ishift - 0.5, std::cos(ishift * 1.0 * psiZDCC)); + registry.fill(HIST("shift/ShiftZDCA"), cent, 0.5, ishift - 0.5, std::sin(ishift * 1.0 * psiZDCA)); + registry.fill(HIST("shift/ShiftZDCA"), cent, 1.5, ishift - 0.5, std::cos(ishift * 1.0 * psiZDCA)); } } @@ -1137,19 +1237,23 @@ struct ZdcQVectors { if (cal.isShiftProfileFound) { for (int ishift = 1; ishift <= nshift; ishift++) { - int binshiftxZDCC = cal.shiftprofileC->FindBin(centrality, 0.5, ishift - 0.5); // bin 0.5 - int binshiftyZDCC = cal.shiftprofileC->FindBin(centrality, 1.5, ishift - 0.5); - int binshiftxZDCA = cal.shiftprofileA->FindBin(centrality, 0.5, ishift - 0.5); - int binshiftyZDCA = cal.shiftprofileA->FindBin(centrality, 1.5, ishift - 0.5); + int binshiftxZDCC = cal.shiftprofileC->FindBin(cent, 0.5, ishift - 0.5); // bin 0.5 + int binshiftyZDCC = cal.shiftprofileC->FindBin(cent, 1.5, ishift - 0.5); + int binshiftxZDCA = cal.shiftprofileA->FindBin(cent, 0.5, ishift - 0.5); + int binshiftyZDCA = cal.shiftprofileA->FindBin(cent, 1.5, ishift - 0.5); - if (binshiftxZDCC > 0) + if (binshiftxZDCC > 0) { coeffshiftxZDCC = cal.shiftprofileC->GetBinContent(binshiftxZDCC); - if (binshiftyZDCC > 0) + } + if (binshiftyZDCC > 0) { coeffshiftyZDCC = cal.shiftprofileC->GetBinContent(binshiftyZDCC); - if (binshiftxZDCA > 0) + } + if (binshiftxZDCA > 0) { coeffshiftxZDCA = cal.shiftprofileA->GetBinContent(binshiftxZDCA); - if (binshiftyZDCA > 0) + } + if (binshiftyZDCA > 0) { coeffshiftyZDCA = cal.shiftprofileA->GetBinContent(binshiftyZDCA); + } deltaPsiZDCC += ((2 / (1.0 * ishift)) * (-1.0 * coeffshiftxZDCC * std::cos(ishift * 1.0 * psiZDCC) + coeffshiftyZDCC * std::sin(ishift * 1.0 * psiZDCC))); deltaPsiZDCA += ((2 / (1.0 * ishift)) * (-1.0 * coeffshiftxZDCA * std::cos(ishift * 1.0 * psiZDCA) + coeffshiftyZDCA * std::sin(ishift * 1.0 * psiZDCA))); @@ -1163,12 +1267,12 @@ struct ZdcQVectors { psiZDCCshift = std::atan2(std::sin(psiZDCCshift), std::cos(psiZDCCshift)); psiZDCAshift = std::atan2(std::sin(psiZDCAshift), std::cos(psiZDCAshift)); - if (cfgFillHistRegistry && !cfgFillNothing) { - registry.fill(HIST("QA/shift/psiZDCA"), psiZDCA, centrality); - registry.fill(HIST("QA/shift/psiZDCC"), psiZDCC, centrality); + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.fill(HIST("QA/shift/psiZDCA"), psiZDCA, cent); + registry.fill(HIST("QA/shift/psiZDCC"), psiZDCC, cent); registry.fill(HIST("QA/shift/psiZDCAC"), psiZDCA, psiZDCC); - registry.fill(HIST("QA/shift/psiZDCA_shift"), psiZDCAshift, centrality); - registry.fill(HIST("QA/shift/psiZDCC_shift"), psiZDCCshift, centrality); + registry.fill(HIST("QA/shift/psiZDCA_shift"), psiZDCAshift, cent); + registry.fill(HIST("QA/shift/psiZDCC_shift"), psiZDCCshift, cent); registry.fill(HIST("QA/shift/psiZDCAC_shift"), psiZDCAshift, psiZDCCshift); registry.fill(HIST("QA/shift/DeltaPsiZDCA"), psiZDCAshift, psiZDCA); registry.fill(HIST("QA/shift/DeltaPsiZDCC"), psiZDCCshift, psiZDCC); @@ -1180,24 +1284,90 @@ struct ZdcQVectors { double qXcShift = std::hypot(qRec[2], qRec[3]) * std::cos(psiZDCCshift); double qYcShift = std::hypot(qRec[2], qRec[3]) * std::sin(psiZDCCshift); - if (isSelected && cfgFillHistRegistry && !cfgFillNothing) { - fillCommonRegistry(qRec[0], qRec[1], qRec[2], qRec[3], v, centrality, rsTimestamp); - registry.fill(HIST("QA/centrality_after"), centrality); - registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); - registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); - registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); - registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); + if (!cal.calibfilesLoaded[kRecShift]) { + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); + registry.fill(HIST("QA/centrality_after"), cent); + registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); + registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); + registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); + registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); + } + + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); + qRec = {0, 0, 0, 0}; + + cal.lastRunNumber = runnumber; + return; } + // vector of 4 + corrQxA.clear(); + corrQyA.clear(); + corrQxC.clear(); + corrQyC.clear(); - spTableZDC(runnumber, cents, v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, isSelected, eventSelectionFlags); - qRec.clear(); + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); + } + + for (int it = 1; it <= cfgNIterationsAfterShift; it++) { + corrQxA.push_back(getCorrection(names[0][0].Data(), it, 1)); + corrQyA.push_back(getCorrection(names[0][1].Data(), it, 1)); + corrQxC.push_back(getCorrection(names[0][2].Data(), it, 1)); + corrQyC.push_back(getCorrection(names[0][3].Data(), it, 1)); + + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, qXaShift - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, qYaShift - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, qXcShift - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, qYcShift - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } + pb++; + + for (int step = 2; step <= nSteps; step++) { + corrQxA.push_back(getCorrection(names[step - 1][0].Data(), it, step)); + corrQyA.push_back(getCorrection(names[step - 1][1].Data(), it, step)); + corrQxC.push_back(getCorrection(names[step - 1][2].Data(), it, step)); + corrQyC.push_back(getCorrection(names[step - 1][3].Data(), it, step)); + + if (cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + registry.get(HIST("recentering/QXA_vs_iteration"))->Fill(pb + 1, q[0] - std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0)); + registry.get(HIST("recentering/QYA_vs_iteration"))->Fill(pb + 1, q[1] - std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0)); + registry.get(HIST("recentering/QXC_vs_iteration"))->Fill(pb + 1, q[2] - std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0)); + registry.get(HIST("recentering/QYC_vs_iteration"))->Fill(pb + 1, q[3] - std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0)); + } + + pb++; + } + + double totalCorrectionQxAshift = std::accumulate(corrQxA.begin(), corrQxA.end(), 0.0); + double totalCorrectionQyAshift = std::accumulate(corrQyA.begin(), corrQyA.end(), 0.0); + double totalCorrectionQxCshift = std::accumulate(corrQxC.begin(), corrQxC.end(), 0.0); + double totalCorrectionQyCshift = std::accumulate(corrQyC.begin(), corrQyC.end(), 0.0); + + qXaShift -= totalCorrectionQxAshift; + qYaShift -= totalCorrectionQyAshift; + qXcShift -= totalCorrectionQxCshift; + qYcShift -= totalCorrectionQyCshift; + + if (cal.isSelected && cfgFillHistRegistry && !cfgFillNothing && isEventSelected) { + fillCommonRegistry(qXaShift, qYaShift, qXcShift, qYcShift, cal.v, cent, rsTimestamp); + registry.fill(HIST("QA/centrality_after"), cent); + registry.get(HIST("QA/after/ZNA_Qx"))->Fill(Form("%d", runnumber), qXaShift); + registry.get(HIST("QA/after/ZNA_Qy"))->Fill(Form("%d", runnumber), qYaShift); + registry.get(HIST("QA/after/ZNC_Qx"))->Fill(Form("%d", runnumber), qXcShift); + registry.get(HIST("QA/after/ZNC_Qy"))->Fill(Form("%d", runnumber), qYcShift); + } + + spTableZDC(runnumber, cents, cal.v, foundBC.timestamp(), qXaShift, qYaShift, qXcShift, qYcShift, cal.isSelected, eventSelectionFlags); + qRec = {0, 0, 0, 0}; + + cal.lastRunNumber = runnumber; + return; + } - counter++; - lastRunNumber = runnumber; - return; - } LOGF(warning, "We return without saving table... -> THis is a problem"); - lastRunNumber = runnumber; + cal.lastRunNumber = runnumber; } // end of process }; diff --git a/PWGCF/Flow/Tasks/CMakeLists.txt b/PWGCF/Flow/Tasks/CMakeLists.txt index cb080629efb..ad5dbd56ba3 100644 --- a/PWGCF/Flow/Tasks/CMakeLists.txt +++ b/PWGCF/Flow/Tasks/CMakeLists.txt @@ -26,7 +26,7 @@ o2physics_add_dpl_workflow(flow-runby-run o2physics_add_dpl_workflow(flow-mc SOURCES flowMc.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-qa @@ -99,6 +99,11 @@ o2physics_add_dpl_workflow(flow-gfw-ese PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(flow-fluc-gfw-pp + SOURCES flowFlucGfwPp.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(flow-event-plane SOURCES flowEventPlane.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -108,3 +113,13 @@ o2physics_add_dpl_workflow(pid-flow-pt-corr SOURCES pidFlowPtCorr.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(flow-directed-flow-task + SOURCES flowDirectedFlowTask.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(flow-transmutation + SOURCES flowTransmutation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/Flow/Tasks/flowAnalysisGF.cxx b/PWGCF/Flow/Tasks/flowAnalysisGF.cxx index 551f54545b9..ff45727cfb8 100644 --- a/PWGCF/Flow/Tasks/flowAnalysisGF.cxx +++ b/PWGCF/Flow/Tasks/flowAnalysisGF.cxx @@ -415,7 +415,7 @@ struct flowAnalysisGF { } template - bool eventSelected(TCollision collision, const int& multTrk, const float& centrality) + bool eventSelected(const TCollision& collision, const int& multTrk, const float& centrality) { if (cfgTVXinTRD) { if (collision.alias_bit(kTVXinTRD)) { @@ -487,7 +487,7 @@ struct flowAnalysisGF { } template - bool trackSelected(TTrack track, const int& field) + bool trackSelected(const TTrack& track, const int& field) { double phimodn = track.phi(); if (field < 0) // for negative polarity field @@ -545,7 +545,7 @@ struct flowAnalysisGF { } template - void processCollision(datatype dt, TCollision const& collision, TTracks tracks, const float& centrality, const int& field) + void processCollision(datatype dt, TCollision const& collision, const TTracks& tracks, const float& centrality, const int& field) { if (tracks.size() < 1) @@ -648,7 +648,7 @@ struct flowAnalysisGF { } template - inline void FillGFW(TrackObject track, float weff, float wacc) + inline void FillGFW(const TrackObject& track, float weff, float wacc) { fFCpt->fill(weff, track.pt()); bool WithinPtPOI = (ptpoilow < track.pt()) && (track.pt() < ptpoiup); // within POI pT range @@ -663,7 +663,7 @@ struct flowAnalysisGF { } template - inline void FillTrackQA(TrackObject track, const float vtxz) + inline void FillTrackQA(const TrackObject& track, const float vtxz) { if constexpr (framework::has_type_v) { registry.fill(HIST("trackMCGen/phi_eta_vtxZ_gen"), track.phi(), track.eta(), vtxz); @@ -685,7 +685,7 @@ struct flowAnalysisGF { } template - inline void FillEventQA(CollisionObject collision, TracksObject tracks) + inline void FillEventQA(const CollisionObject& collision, const TracksObject& tracks) { if constexpr (framework::has_type_v) { @@ -700,7 +700,7 @@ struct flowAnalysisGF { } template - inline void RunProcess(CollisionObject collision, TracksObject tracks) + inline void RunProcess(const CollisionObject& collision, const TracksObject& tracks) { float centrality; diff --git a/PWGCF/Flow/Tasks/flowDirectedFlowTask.cxx b/PWGCF/Flow/Tasks/flowDirectedFlowTask.cxx new file mode 100644 index 00000000000..23f18f3acba --- /dev/null +++ b/PWGCF/Flow/Tasks/flowDirectedFlowTask.cxx @@ -0,0 +1,587 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author Junlee Kim (jikim1290@gmail.com) +/// \file flowDirectedFlowTask.cxx +/// \brief Task for q1-dependent directed flow and global polarization +/// \since May 2026 + +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/SPCalibrationTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +using namespace o2::constants::physics; + +struct flowDirectedFlowTask { + Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable nolaterthan{"nolaterthan", -1, "Latest acceptable CCDB timestamp; -1 = current"}; + + Configurable cfgPVSel{"cfgPVSel", true, "flag for the primary vertex selection"}; + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgAddEvtSelPileup{"cfgAddEvtSelPileup", true, "flag for the pilup rejection"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + + Configurable cfgq1mode{"cfgq1mode", 0, "0: |QC-QA|, 1: |QA|, 2: |QC|, 3: 0.5*(|QA|+|QC|)"}; + + Configurable cfgUseAccCorr{"cfgUseAccCorr", true, "Use acceptance correction profile"}; + Configurable cfgAccPathL{"cfgAccPathL", "Users/p/prottay/My/Object/acccorrL", "CCDB path to Lambda acceptance correction"}; + Configurable cfgAccPathAL{"cfgAccPathAL", "Users/p/prottay/My/Object/acccorrAL", "CCDB path to AntiLambda acceptance correction"}; + + Configurable cfgV0PtMin{"cfgV0PtMin", 0.f, "Minimum V0 pT"}; + Configurable cfgV0Rap{"cfgV0Rap", 0.8f, "V0 rapidity range"}; + Configurable cfgV0DCADaughMax{"cfgV0DCADaughMax", 0.2, "Maximum DCA between V0 daughters"}; + Configurable cfgV0CPAMin{"cfgV0CPAMin", 0.9998, "Minimum V0 CPA"}; + Configurable cfgV0TranRadV0Min{"cfgV0TranRadV0Min", 1.5f, "Minimum V0 transverse radius"}; + Configurable cfgV0TranRadV0Max{"cfgV0TranRadV0Max", 100.f, "Maximum V0 transverse radius"}; + Configurable cfgMaxV0DCA{"cfgMaxV0DCA", 1.2, "Maximum V0 DCA to PV"}; + Configurable cfgMinV0DCAPr{"cfgMinV0DCAPr", 0.05, "Minimum proton DCA to PV"}; + Configurable cfgMinV0DCAPi{"cfgMinV0DCAPi", 0.05, "Minimum pion DCA to PV"}; + Configurable cfgMaxV0LifeTime{"cfgMaxV0LifeTime", 20, "Maximum V0 lifetime"}; + + Configurable cfgCutPT{"cfgCutPT", 0.15, "PT cut on daughter track"}; + Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut on daughter track"}; + + Configurable cfgDaughEta{"cfgDaughEta", 0.8f, "Daughter eta range"}; + Configurable cfgDaughPrPt{"cfgDaughPrPt", 0.4f, "Minimum daughter proton pT"}; + Configurable cfgDaughPiPt{"cfgDaughPiPt", 0.2f, "Minimum daughter pion pT"}; + Configurable cfgDaughTPCnclsMin{"cfgDaughTPCnclsMin", 50.f, "Minimum TPC found clusters"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Minimum TPC crossed rows"}; + Configurable cfgRcrfc{"cfgRcrfc", 0.8f, "TPC crossed rows / findable clusters"}; + Configurable cfgDaughPIDCuts{"cfgDaughPIDCuts", 3.f, "TPC PID nsigma cut"}; + + Configurable cfgTrackPtMin{"cfgTrackPtMin", 0.2f, "Minimum track pT for v1"}; + Configurable cfgTrackPtMax{"cfgTrackPtMax", 5.0f, "Maximum track pT for v1"}; + Configurable cfgTrackEtaMax{"cfgTrackEtaMax", 0.8f, "Maximum |eta| for v1"}; + Configurable cfgMinTPCClustersTrack{"cfgMinTPCClustersTrack", 70, "Minimum TPC clusters for v1 tracks"}; + + struct : ConfigurableGroup { + Configurable cfgRequireRCTFlagChecker{"cfgRequireRCTFlagChecker", true, "Check event quality in run condition table"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", true, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + } rctCut; + + float massLambda = o2::constants::physics::MassLambda0; + float massPr = o2::constants::physics::MassProton; + float massPi = o2::constants::physics::MassPionCharged; + + ConfigurableAxis centAxis{"centAxis", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 80.}, "Centrality (%)"}; + ConfigurableAxis ptAxis{"ptAxis", {VARIABLE_WIDTH, 0.2, 0.5, 1., 1.5, 2., 2.5, 3., 4., 5., 6.5, 8., 10.}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis etaAxis{"etaAxis", {VARIABLE_WIDTH, -0.8, -0.4, -0.2, 0., 0.2, 0.4, 0.8}, "#eta"}; + ConfigurableAxis massAxis{"massAxis", {50, 1.09, 1.14}, "#it{M} (GeV/#it{c}^{2})"}; + ConfigurableAxis polAxis{"polAxis", {200, -3.0, 3.0}, "polarization observable"}; + ConfigurableAxis spAxis{"spAxis", {200, -3.0, 3.0}, "SP observable"}; + ConfigurableAxis q1Axis{"q1Axis", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 3., 5., 10.}, "q_{1}^{ZDC}"}; + ConfigurableAxis qAxis{"qAxis", {100, -3., 3.}, "Q"}; + ConfigurableAxis resAxis{"resAxis", {200, -1., 1.}, "resolution / correlation"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Service ccdb; + TProfile2D* accprofileL = nullptr; + TProfile2D* accprofileAL = nullptr; + + int currentRunNumber = -999; + int lastRunNumber = -999; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter acceptanceFilter = (nabs(aod::track::eta) < cfgCutEta && nabs(aod::track::pt) > cfgCutPT); + + RCTFlagsChecker rctChecker; + + using EventCandidates = soa::Filtered>; + using AllTrackCandidates = soa::Filtered>; + using ResoV0s = aod::V0Datas; + + void init(InitContext&) + { + ccdb->setURL(cfgUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + histos.add("hCentrality", "Centrality", kTH1F, {{centAxis}}); + histos.add("hQ1ZDCvscent", "q1 vs centrality", kTH2F, {{centAxis}, {q1Axis}}); + histos.add("hQxAvscent", "Qx A vs centrality", kTH2F, {{centAxis}, {qAxis}}); + histos.add("hQyAvscent", "Qy A vs centrality", kTH2F, {{centAxis}, {qAxis}}); + histos.add("hQxCvscent", "Qx C vs centrality", kTH2F, {{centAxis}, {qAxis}}); + histos.add("hQyCvscent", "Qy C vs centrality", kTH2F, {{centAxis}, {qAxis}}); + + histos.add("hpResCosAC", "cos(#Psi_{A}-#Psi_{C}) vs centrality", kTH3F, {{centAxis}, {resAxis}, {q1Axis}}); + histos.add("hpDotAC", "Q_{A}#upoint Q_{C} vs centrality", kTH3F, {{centAxis}, {qAxis}, {q1Axis}}); + histos.add("hpResDotAC", "Q_{A}#upoint Q_{C} vs centrality", kTH3F, {{centAxis}, {resAxis}, {q1Axis}}); + histos.add("hpQxAQxC", "QxA QxC", kTH3F, {{centAxis}, {qAxis}, {q1Axis}}); + histos.add("hpQyAQyC", "QyA QyC", kTH3F, {{centAxis}, {qAxis}, {q1Axis}}); + histos.add("hpQxAQyC", "QxA QyC", kTH3F, {{centAxis}, {qAxis}, {q1Axis}}); + histos.add("hpQxCQyA", "QxC QyA", kTH3F, {{centAxis}, {qAxis}, {q1Axis}}); + + std::vector axesV1SP = {centAxis, ptAxis, etaAxis, q1Axis, spAxis}; + std::vector axesV1EP = {centAxis, ptAxis, etaAxis, q1Axis, polAxis}; + + histos.add("hV1SPFullQ1", "charged v1 SP numerator u#upoint(QC-QA) vs q1", HistType::kTHnSparseF, axesV1SP, true); + histos.add("hV1SPxxFullQ1", "charged v1 SP numerator u#upoint(QC-QA) vs q1 for x", HistType::kTHnSparseF, axesV1SP, true); + histos.add("hV1SPyyFullQ1", "charged v1 SP numerator u#upoint(QC-QA) vs q1 for y", HistType::kTHnSparseF, axesV1SP, true); + histos.add("hV1SPAQ1", "charged v1 SP numerator u#upointQA vs q1", HistType::kTHnSparseF, axesV1SP, true); + histos.add("hV1SPCQ1", "charged v1 SP numerator u#upointQC vs q1", HistType::kTHnSparseF, axesV1SP, true); + + histos.add("hV1EPFullQ1", "charged v1 EP QA cos(phi-PsiZDC) vs q1", HistType::kTHnSparseF, axesV1EP, true); + histos.add("hV1EPAQ1", "charged v1 EP QA cos(phi-PsiA) vs q1", HistType::kTHnSparseF, axesV1EP, true); + histos.add("hV1EPCQ1", "charged v1 EP QA cos(phi-PsiC) vs q1", HistType::kTHnSparseF, axesV1EP, true); + + std::vector axesPolSPQ1 = {massAxis, ptAxis, spAxis, centAxis, q1Axis}; + + histos.add("hSparseLambdaPolSPQ1", "Lambda SP polarization numerator vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaPolSPuxQ1", "Lambda SP polarization numerator vs q1 for x", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaPolSPuyQ1", "Lambda SP polarization numerator vs q1 for y", HistType::kTHnSparseF, axesPolSPQ1, true); + + histos.add("hSparseLambdaPolSPAQ1", "Lambda SP polarization numerator A vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaPolSPCQ1", "Lambda SP polarization numerator C vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + + histos.add("hSparseAntiLambdaPolSPQ1", "AntiLambda SP polarization numerator vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaPolSPuxQ1", "AntiLambda SP polarization numerator vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaPolSPuyQ1", "AntiLambda SP polarization numerator vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + + histos.add("hSparseAntiLambdaPolSPAQ1", "AntiLambda SP polarization numerator A vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaPolSPCQ1", "AntiLambda SP polarization numerator C vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + + histos.add("hSparseLambdaPolEPQ1", "Lambda EP QA sin(phi*-PsiZDC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaPolEPAQ1", "Lambda EP QA sin(phi*-PsiA) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaPolEPCQ1", "Lambda EP QA sin(phi*-PsiC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaPolEPQ1", "AntiLambda EP QA sin(phi*-PsiZDC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaPolEPAQ1", "AntiLambda EP QA sin(phi*-PsiA) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaPolEPCQ1", "AntiLambda EP QA sin(phi*-PsiC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + + histos.add("hSparseLambdaCosPsiQ1", "Lambda cos(PsiZDC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaSinPsiQ1", "Lambda sin(PsiZDC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaCosPsiQ1", "AntiLambda cos(PsiZDC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaSinPsiQ1", "AntiLambda sin(PsiZDC) vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + + histos.add("hSparseLambdaCorrSinPhiStarQ1", "Lambda sin(phi*) acceptance correction vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaCorrCosPhiStarQ1", "Lambda cos(phi*) acceptance correction vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaCorrSinThetaStarQ1", "Lambda sin(theta*) acceptance correction vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaAvgUxQ1", "Lambda vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseLambdaAvgUyQ1", "Lambda vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + + histos.add("hSparseAntiLambdaCorrSinPhiStarQ1", "AntiLambda sin(phi*) acceptance correction vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaCorrCosPhiStarQ1", "AntiLambda cos(phi*) acceptance correction vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaCorrSinThetaStarQ1", "AntiLambda sin(theta*) acceptance correction vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaAvgUxQ1", "AntiLambda vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + histos.add("hSparseAntiLambdaAvgUyQ1", "AntiLambda vs q1", HistType::kTHnSparseF, axesPolSPQ1, true); + } + + template + bool eventSelected(const TCollision& collision) + { + if (!collision.sel8()) { + return 0; + } + if (!collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return 0; + } + if (!collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return 0; + } + if (cfgPVSel && std::abs(collision.posZ()) > cfgCutVertex) { + return 0; + } + if (cfgAddEvtSelPileup && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return 0; + } + if (collision.trackOccupancyInTimeRange() > cfgMaxOccupancy || collision.trackOccupancyInTimeRange() < cfgMinOccupancy) { + return 0; + } + if (rctCut.cfgRequireRCTFlagChecker && !rctChecker(collision)) { + return 0; + } + + return 1; + } + + template + bool getZDCQ1(Collision const& collision, float& qxA, float& qyA, float& qxC, float& qyC, + float& psiA, float& psiC, float& psiFull, float& q1) + { + const float rawQxA = collision.qxZDCA(); + const float rawQyA = collision.qyZDCA(); + const float rawQxC = collision.qxZDCC(); + const float rawQyC = collision.qyZDCC(); + + psiA = collision.psiZDCA(); + psiC = collision.psiZDCC(); + + const float magA = std::sqrt(rawQxA * rawQxA + rawQyA * rawQyA); + const float magC = std::sqrt(rawQxC * rawQxC + rawQyC * rawQyC); + qxA = magA * std::cos(psiA); + qyA = magA * std::sin(psiA); + qxC = magC * std::cos(psiC); + qyC = magC * std::sin(psiC); + + const float qxFull = qxC - qxA; + const float qyFull = qyC - qyA; + psiFull = std::atan2(qyFull, qxFull); + + const float q1A = std::sqrt(qxA * qxA + qyA * qyA); + const float q1C = std::sqrt(qxC * qxC + qyC * qyC); + const float q1Full = std::sqrt(qxFull * qxFull + qyFull * qyFull); + const float q1Mean = 0.5 * (q1A + q1C); + + if (cfgq1mode == 1) { + q1 = q1A; + } else if (cfgq1mode == 2) { + q1 = q1C; + } else if (cfgq1mode == 3) { + q1 = q1Mean; + } else if (cfgq1mode == 4) { + q1 = std::sqrt(std::pow(qxA + qxC, 2) + std::pow(qyA + qyC, 2)); + } else { + q1 = q1Full; + } + + return std::isfinite(qxA) && std::isfinite(qyA) && std::isfinite(qxC) && std::isfinite(qyC) && std::isfinite(psiA) && std::isfinite(psiC) && std::isfinite(psiFull) && std::isfinite(q1); + } + + template + bool selectV1Track(Track const& track) + { + if (!track.isGlobalTrack()) { + return false; + } + if (track.pt() < cfgTrackPtMin || track.pt() > cfgTrackPtMax) { + return false; + } + if (std::abs(track.eta()) > cfgTrackEtaMax) { + return false; + } + if (track.tpcNClsFound() < cfgMinTPCClustersTrack) { + return false; + } + return true; + } + + template + bool isCompatible(V0 const& v0, int pid) + { + // pid = 0: Lambda, pid = 1: AntiLambda + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + + if (pid == 0 && (v0.positivept() < cfgDaughPrPt || v0.negativept() < cfgDaughPiPt)) { + return false; + } + if (pid == 1 && (v0.positivept() < cfgDaughPiPt || v0.negativept() < cfgDaughPrPt)) { + return false; + } + if (std::abs(v0.positiveeta()) > cfgDaughEta || std::abs(v0.negativeeta()) > cfgDaughEta) { + return false; + } + + if (posTrack.tpcNClsCrossedRows() < cfgTPCcluster || negTrack.tpcNClsCrossedRows() < cfgTPCcluster) { + return false; + } + if (posTrack.tpcNClsFound() < cfgDaughTPCnclsMin || negTrack.tpcNClsFound() < cfgDaughTPCnclsMin) { + return false; + } + if (posTrack.tpcCrossedRowsOverFindableCls() < cfgRcrfc || negTrack.tpcCrossedRowsOverFindableCls() < cfgRcrfc) { + return false; + } + + if (pid == 0 && (std::abs(posTrack.tpcNSigmaPr()) > cfgDaughPIDCuts || std::abs(negTrack.tpcNSigmaPi()) > cfgDaughPIDCuts)) { + return false; + } + if (pid == 1 && (std::abs(posTrack.tpcNSigmaPi()) > cfgDaughPIDCuts || std::abs(negTrack.tpcNSigmaPr()) > cfgDaughPIDCuts)) { + return false; + } + + if (pid == 0 && (std::abs(v0.dcapostopv()) < cfgMinV0DCAPr || std::abs(v0.dcanegtopv()) < cfgMinV0DCAPi)) { + return false; + } + if (pid == 1 && (std::abs(v0.dcapostopv()) < cfgMinV0DCAPi || std::abs(v0.dcanegtopv()) < cfgMinV0DCAPr)) { + return false; + } + return true; + } + + template + bool selectV0(Collision const& collision, V0 const& v0) + { + const float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massLambda; + if (std::abs(v0.dcav0topv()) > cfgMaxV0DCA) { + return false; + } + if (v0.pt() < cfgV0PtMin) { + return false; + } + if (std::abs(v0.dcaV0daughters()) > cfgV0DCADaughMax) { + return false; + } + if (v0.v0cosPA() < cfgV0CPAMin) { + return false; + } + if (v0.v0radius() < cfgV0TranRadV0Min || v0.v0radius() > cfgV0TranRadV0Max) { + return false; + } + if (std::abs(ctauLambda) > cfgMaxV0LifeTime) { + return false; + } + if (std::abs(v0.yLambda()) > cfgV0Rap) { + return false; + } + return true; + } + + float acceptanceWeight(bool isLambda, float eta, float pt) + { + if (!cfgUseAccCorr) { + return 1.0; + } + TProfile2D* prof = isLambda ? accprofileL : accprofileAL; + if (!prof) { + return 1.0; + } + const int binx = prof->GetXaxis()->FindBin(eta); + const int biny = prof->GetYaxis()->FindBin(pt); + const float acc = prof->GetBinContent(binx, biny); + if (acc <= 0.0 || !std::isfinite(acc)) { + return 1.0; + } + return acc; + } + + template + void fillPolarization(bool isLambda, V0 const& v0, float centrality, float q1, float psiA, float psiC, float psiFull, float qxA, float qyA, float qxC, float qyC) + { + ROOT::Math::PxPyPzMVector daughter; + ROOT::Math::PxPyPzMVector pion; + float mass = 0.0; + + if (isLambda) { + daughter = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), massPr); + pion = ROOT::Math::PxPyPzMVector(v0.pxneg(), v0.pyneg(), v0.pzneg(), massPi); + mass = v0.mLambda(); + } else { + daughter = ROOT::Math::PxPyPzMVector(v0.pxneg(), v0.pyneg(), v0.pzneg(), massPr); + pion = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), massPi); + mass = v0.mAntiLambda(); + } + + const auto hyperon = daughter + pion; + ROOT::Math::Boost boostToRest(hyperon.BoostToCM()); + const auto daughterStar = boostToRest(daughter); + + float phiStar = std::atan2(daughterStar.Py(), daughterStar.Px()); + const float ux = std::cos(phiStar); + const float uy = std::sin(phiStar); + + const float cosThetaStar = daughterStar.Pz() / daughterStar.P(); + const float sinThetaStar = std::sqrt(std::max(0.0, 1.0 - cosThetaStar * cosThetaStar)); + const float sinPhiStar = std::sin(phiStar); + const float cosPhiStar = std::cos(phiStar); + + float polEP_A = std::sin(phiStar - psiA); + float polEP_C = std::sin(phiStar - psiC); + float polEP = std::sin(phiStar - psiFull); + + const float qxFull = qxC - qxA; + const float qyFull = qyC - qyA; + + float polSP = uy * qxFull - ux * qyFull; + float polSP_A = uy * qxA - ux * qyA; + float polSP_C = uy * qxC - ux * qyC; + + float accDen = acceptanceWeight(isLambda, v0.eta(), v0.pt()); + float wgt = 1.0; // efficiency + + polSP /= accDen; + polSP_A /= accDen; + polSP_C /= accDen; + + polEP /= accDen; + polEP_A /= accDen; + polEP_C /= accDen; + + const float cosPsi = std::cos(psiFull); + const float sinPsi = std::sin(psiFull); + + if (isLambda) { + histos.fill(HIST("hSparseLambdaPolSPQ1"), mass, v0.pt(), polSP, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaPolSPuyQ1"), mass, v0.pt(), uy * qxFull / accDen, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaPolSPuxQ1"), mass, v0.pt(), ux * qyFull / accDen, centrality, q1, wgt); + + histos.fill(HIST("hSparseLambdaPolSPAQ1"), mass, v0.pt(), polSP_A, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaPolSPCQ1"), mass, v0.pt(), polSP_C, centrality, q1, wgt); + + histos.fill(HIST("hSparseLambdaPolEPQ1"), mass, v0.pt(), polEP, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaPolEPAQ1"), mass, v0.pt(), polEP_A, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaPolEPCQ1"), mass, v0.pt(), polEP_C, centrality, q1, wgt); + + histos.fill(HIST("hSparseLambdaCosPsiQ1"), mass, v0.pt(), cosPsi, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaSinPsiQ1"), mass, v0.pt(), sinPsi, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaCorrSinPhiStarQ1"), mass, v0.pt(), sinPhiStar, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaCorrCosPhiStarQ1"), mass, v0.pt(), cosPhiStar, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaCorrSinThetaStarQ1"), mass, v0.pt(), sinThetaStar, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaAvgUxQ1"), mass, v0.pt(), ux, centrality, q1, wgt); + histos.fill(HIST("hSparseLambdaAvgUyQ1"), mass, v0.pt(), uy, centrality, q1, wgt); + } else { + histos.fill(HIST("hSparseAntiLambdaPolSPQ1"), mass, v0.pt(), polSP, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaPolSPuxQ1"), mass, v0.pt(), uy * qxFull / accDen, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaPolSPuyQ1"), mass, v0.pt(), ux * qyFull / accDen, centrality, q1, wgt); + + histos.fill(HIST("hSparseAntiLambdaPolSPAQ1"), mass, v0.pt(), polSP_A, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaPolSPCQ1"), mass, v0.pt(), polSP_C, centrality, q1, wgt); + + histos.fill(HIST("hSparseAntiLambdaPolEPQ1"), mass, v0.pt(), polEP, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaPolEPAQ1"), mass, v0.pt(), polEP_A, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaPolEPCQ1"), mass, v0.pt(), polEP_C, centrality, q1, wgt); + + histos.fill(HIST("hSparseAntiLambdaCosPsiQ1"), mass, v0.pt(), cosPsi, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaSinPsiQ1"), mass, v0.pt(), sinPsi, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaCorrSinPhiStarQ1"), mass, v0.pt(), sinPhiStar, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaCorrCosPhiStarQ1"), mass, v0.pt(), cosPhiStar, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaCorrSinThetaStarQ1"), mass, v0.pt(), sinThetaStar, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaAvgUxQ1"), mass, v0.pt(), ux, centrality, q1, wgt); + histos.fill(HIST("hSparseAntiLambdaAvgUyQ1"), mass, v0.pt(), uy, centrality, q1, wgt); + } + } + + void processData(EventCandidates::iterator const& collision, AllTrackCandidates const& tracks, ResoV0s const& v0s, aod::BCsWithTimestamps const&) + { + if (!eventSelected(collision)) { + return; + } + const float centrality = collision.centFT0C(); + + if (cfgUseAccCorr) { + auto bc = collision.bc_as(); + currentRunNumber = bc.runNumber(); + if (currentRunNumber != lastRunNumber) { + accprofileL = ccdb->getForTimeStamp(cfgAccPathL.value, bc.timestamp()); + accprofileAL = ccdb->getForTimeStamp(cfgAccPathAL.value, bc.timestamp()); + lastRunNumber = currentRunNumber; + } + } + + float qxA = 0.0, qyA = 0.0, qxC = 0.0, qyC = 0.0; + float psiA = 0.0, psiC = 0.0, psiFull = 0.0, q1 = 0.0; + if (!getZDCQ1(collision, qxA, qyA, qxC, qyC, psiA, psiC, psiFull, q1)) { + return; + } + + histos.fill(HIST("hCentrality"), centrality); + histos.fill(HIST("hQ1ZDCvscent"), centrality, q1); + histos.fill(HIST("hQxAvscent"), centrality, qxA); + histos.fill(HIST("hQyAvscent"), centrality, qyA); + histos.fill(HIST("hQxCvscent"), centrality, qxC); + histos.fill(HIST("hQyCvscent"), centrality, qyC); + + float magA = std::sqrt(qxA * qxA + qyA * qyA); + float magC = std::sqrt(qxC * qxC + qyC * qyC); + float qxFull = qxC - qxA; + float qyFull = qyC - qyA; + float dotAC = qxA * qxC + qyA * qyC; + float resDot = dotAC / (magA * magC); + + histos.fill(HIST("hpResCosAC"), centrality, std::cos(psiA - psiC), q1); + histos.fill(HIST("hpDotAC"), centrality, dotAC, q1); + histos.fill(HIST("hpResDotAC"), centrality, resDot, q1); + histos.fill(HIST("hpQxAQxC"), centrality, qxA * qxC, q1); + histos.fill(HIST("hpQyAQyC"), centrality, qyA * qyC, q1); + histos.fill(HIST("hpQxAQyC"), centrality, qxA * qyC, q1); + histos.fill(HIST("hpQxCQyA"), centrality, qxC * qyA, q1); + + for (const auto& track : tracks) { + if (!selectV1Track(track)) { + continue; + } + + float phi = track.phi(); + float ux = std::cos(phi); + float uy = std::sin(phi); + + float v1SPFull = ux * qxFull + uy * qyFull; + float v1SPA = ux * qxA + uy * qyA; + float v1SPC = ux * qxC + uy * qyC; + + histos.fill(HIST("hV1SPFullQ1"), centrality, track.pt(), track.eta(), q1, v1SPFull); + histos.fill(HIST("hV1SPxxFullQ1"), centrality, track.pt(), track.eta(), q1, ux * qxFull); + histos.fill(HIST("hV1SPyyFullQ1"), centrality, track.pt(), track.eta(), q1, uy * qyFull); + + histos.fill(HIST("hV1SPAQ1"), centrality, track.pt(), track.eta(), q1, v1SPA); + histos.fill(HIST("hV1SPCQ1"), centrality, track.pt(), track.eta(), q1, v1SPC); + + histos.fill(HIST("hV1EPFullQ1"), centrality, track.pt(), track.eta(), q1, std::cos(phi - psiFull)); + histos.fill(HIST("hV1EPAQ1"), centrality, track.pt(), track.eta(), q1, std::cos(phi - psiA)); + histos.fill(HIST("hV1EPCQ1"), centrality, track.pt(), track.eta(), q1, std::cos(phi - psiC)); + } + + for (const auto& v0 : v0s) { + bool lambdaTag = isCompatible(v0, 0); + bool antiLambdaTag = isCompatible(v0, 1); + if (!lambdaTag && !antiLambdaTag) { + continue; + } + if (!selectV0(collision, v0)) { + continue; + } + if (lambdaTag) { + fillPolarization(true, v0, centrality, q1, psiA, psiC, psiFull, qxA, qyA, qxC, qyC); + } + if (antiLambdaTag) { + fillPolarization(false, v0, centrality, q1, psiA, psiC, psiFull, qxA, qyA, qxC, qyC); + } + } + } + PROCESS_SWITCH(flowDirectedFlowTask, processData, "Process for data", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/Flow/Tasks/flowEfficiencyCasc.cxx b/PWGCF/Flow/Tasks/flowEfficiencyCasc.cxx index 246e811614f..abf517ed95c 100644 --- a/PWGCF/Flow/Tasks/flowEfficiencyCasc.cxx +++ b/PWGCF/Flow/Tasks/flowEfficiencyCasc.cxx @@ -89,7 +89,7 @@ struct FlowEfficiencyCasc { AxisSpec axisK0sMass = {400, 0.4f, 0.6f, "Inv. Mass (GeV)"}; AxisSpec axisLambdaMass = {160, 1.08f, 1.16f, "Inv. Mass (GeV)"}; - using MyCollisions = soa::Join; + using MyCollisions = soa::Join; using MyMcCollisions = soa::Join; using CascMCCandidates = soa::Join; using V0MCCandidates = soa::Join; @@ -145,7 +145,7 @@ struct FlowEfficiencyCasc { registry.add("QAhisto/Casc/hqadcaCascV0dauafter", "", {HistType::kTH1D, {{100, 0, 1}}}); } template - bool eventSelected(TCollision collision) + bool eventSelected(const TCollision& collision) { if (collision.alias_bit(kTVXinTRD)) { // TRD triggered @@ -345,7 +345,7 @@ struct FlowEfficiencyCasc { } PROCESS_SWITCH(FlowEfficiencyCasc, processRec, "process reconstructed information", true); - void processGen(MyMcCollisions::iterator const&, soa::SmallGroups> const& coll, const soa::SmallGroups>& cascMCs, const soa::SmallGroups>& v0MCs) + void processGen(MyMcCollisions::iterator const&, soa::SmallGroups> const& coll, const soa::SmallGroups>& cascMCs, const soa::SmallGroups>& v0MCs) { registry.fill(HIST("mcEventCounter"), 0.5); int rectracknum = 0; diff --git a/PWGCF/Flow/Tasks/flowEsePHe3.cxx b/PWGCF/Flow/Tasks/flowEsePHe3.cxx index 6e90802ef9d..a1ddcf4d45b 100644 --- a/PWGCF/Flow/Tasks/flowEsePHe3.cxx +++ b/PWGCF/Flow/Tasks/flowEsePHe3.cxx @@ -432,7 +432,7 @@ struct FlowEsePHe3 { Produces eseTable; template - float getNSigmaTPCSelfBB(const TrackType track, uint8_t POI) + float getNSigmaTPCSelfBB(const TrackType& track, uint8_t POI) { bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; float correctedTpcInnerParam = (heliumPID && cfgCompensatePIDinTracking) ? track.tpcInnerParam() / 2 : track.tpcInnerParam(); @@ -479,7 +479,7 @@ struct FlowEsePHe3 { } template - float getNSigmaTPC(const TrackType track, uint8_t POI) + float getNSigmaTPC(const TrackType& track, uint8_t POI) { switch (POI) { case ese_parameters::kProton: { @@ -518,7 +518,7 @@ struct FlowEsePHe3 { } template - float getNSigmaTOF(const TrackType track, uint8_t POI) + float getNSigmaTOF(const TrackType& track, uint8_t POI) { switch (POI) { case ese_parameters::kProton: { @@ -548,7 +548,7 @@ struct FlowEsePHe3 { } template - float getNSigmaITS(const TrackType track, uint8_t POI) + float getNSigmaITS(const TrackType& track, uint8_t POI) { switch (POI) { case ese_parameters::kProton: { @@ -720,7 +720,7 @@ struct FlowEsePHe3 { } template - bool trackSelBasic(const TrackType track) + bool trackSelBasic(const TrackType& track) { if ((track.pt() < cfgMinPtPID) || (track.pt() > cfgMaxPtPID)) return false; @@ -775,7 +775,7 @@ struct FlowEsePHe3 { } template - void fillEventQAhistBe(const CollType collision, const int multTrk, const float centrality) + void fillEventQAhistBe(const CollType& collision, const int multTrk, const float centrality) { histsESE.fill(HIST("EventQA/hist_globalTracks_centT0C_before"), centrality, multTrk); histsESE.fill(HIST("EventQA/hist_PVTracks_centT0C_before"), centrality, collision.multNTracksPV()); @@ -787,7 +787,7 @@ struct FlowEsePHe3 { } template - void fillEventQAhistAf(const CollType collision, const int multTrk, const float centrality) + void fillEventQAhistAf(const CollType& collision, const int multTrk, const float centrality) { histsESE.fill(HIST("EventQA/hist_globalTracks_centT0C_after"), centrality, multTrk); histsESE.fill(HIST("EventQA/hist_PVTracks_centT0C_after"), centrality, collision.multNTracksPV()); @@ -799,7 +799,7 @@ struct FlowEsePHe3 { } template - void fillTrackQAhist(const TrackType track) + void fillTrackQAhist(const TrackType& track) { bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; float correctedTpcInnerParam = (heliumPID && cfgCompensatePIDinTracking) ? track.tpcInnerParam() / 2 : track.tpcInnerParam(); diff --git a/PWGCF/Flow/Tasks/flowEseTask.cxx b/PWGCF/Flow/Tasks/flowEseTask.cxx index d14ad602849..b5cfe97f7b4 100644 --- a/PWGCF/Flow/Tasks/flowEseTask.cxx +++ b/PWGCF/Flow/Tasks/flowEseTask.cxx @@ -9,12 +9,14 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \author Junlee Kim (jikim1290@gmail.com) +/// \author Junlee Kim (jikim1290@gmail.com) & Shiqi Wang(shiqi.wang@cern.ch) /// \file flowEseTask.cxx /// \brief Task for flow and event shape engineering correlation with other observation. /// \since 2023-05-15 /// \version 1.0 +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGMM/Mult/DataModel/Index.h" // for Particles2Tracks table @@ -46,18 +48,25 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include #include +#include #include using namespace o2; @@ -68,7 +77,7 @@ using namespace o2::constants::physics; struct FlowEseTask { // using EventCandidates = soa::Filtered>; - using EventCandidates = soa::Join; + using EventCandidates = soa::Join; using TrackCandidates = soa::Join; using V0TrackCandidate = aod::V0Datas; @@ -82,7 +91,7 @@ struct FlowEseTask { "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; } cfgCcdbParam; - Service ccdb; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; Configurable cfgCentSel{"cfgCentSel", 80., "Centrality selection"}; @@ -120,6 +129,7 @@ struct FlowEseTask { Configurable cfgnMods{"cfgnMods", 1, "The number of modulations of interest starting from 2"}; Configurable cfgNQvec{"cfgNQvec", 7, "The number of total Qvectors for looping over the task"}; + Configurable cfgEseHarmonic{"cfgEseHarmonic", 2, "Harmonic used only for ESE analysis (2 or 3)"}; Configurable cfgQvecDetName{"cfgQvecDetName", "FT0C", "The name of detector to be analyzed"}; Configurable cfgQvecRefAName{"cfgQvecRefAName", "TPCpos", "The name of detector for reference A"}; @@ -139,85 +149,219 @@ struct FlowEseTask { Configurable cfgAccCor{"cfgAccCor", false, "flag to apply acceptance correction"}; Configurable cfgAccCorPath{"cfgAccCorPath", "", "path for pseudo acceptance correction"}; + struct : ConfigurableGroup { + Configurable cfgGfwEffCor{"cfgGfwEffCor", false, "flag to apply charged-track efficiency correction for GFW"}; + Configurable cfgGfwEffCorPath{"cfgGfwEffCorPath", "", "CCDB path to charged-track efficiency correction for GFW"}; + Configurable cfgGfwAccCor{"cfgGfwAccCor", false, "flag to apply charged-track acceptance correction for GFW"}; + Configurable cfgGfwAccCorPath{"cfgGfwAccCorPath", "", "CCDB path to charged-track acceptance correction for GFW"}; + } cfgGfwParam; + Configurable cfgCalcCum{"cfgCalcCum", false, "flag to calculate cumulants of cossin"}; Configurable cfgCalcCum1{"cfgCalcCum1", false, "flag to calculate cumulants of coscos"}; Configurable cfgRapidityDep{"cfgRapidityDep", false, "flag for rapidity dependent study"}; Configurable cfgAccAzimuth{"cfgAccAzimuth", false, "flag for azimuth closure study"}; + Configurable cfgFullCheck{"cfgFullCheck", true, "flag for full hist"}; + Configurable cfgMultCor{"cfgMultCor", false, "flag for different Mult choice"}; + Configurable> cfgQ2PercentileCuts{"cfgQ2PercentileCuts", + {63.633132f, 92.417875f, 116.584440f, 139.217742f, 161.810257f, 185.609391f, 212.240958f, 244.765093f, 292.045106f, + 57.593461f, 82.544764f, 102.874824f, 121.420485f, 139.519402f, 158.204340f, 178.728019f, 203.334468f, 238.370537f, + 49.132498f, 70.010553f, 86.825107f, 102.036480f, 116.776740f, 131.910217f, 148.447115f, 168.145964f, 195.993568f, + 38.966476f, 55.866796f, 69.630500f, 82.179489f, 94.412155f, 107.016988f, 120.821718f, 137.304969f, 160.612468f, + 29.476997f, 42.628632f, 53.544991f, 63.651863f, 73.628370f, 84.009092f, 95.488749f, 109.295584f, 128.965135f, + 21.658770f, 31.478901f, 39.740751f, 47.485032f, 55.216668f, 63.362554f, 72.466583f, 83.551694f, 99.548536f, + 15.370223f, 22.386745f, 28.322567f, 33.920535f, 39.547603f, 45.520061f, 52.251446f, 60.535625f, 72.660540f, + 10.351031f, 15.094899f, 19.114684f, 22.915848f, 26.751668f, 30.837985f, 35.471803f, 41.208444f, 49.696178f}, + "p10-p90 q2 cuts in each 10% centrality interval from 0 to 80%"}; + Configurable> cfgQ3PercentileCuts{"cfgQ3PercentileCuts", {}, "p10-p90 q3 cuts in each 10% centrality interval from 0 to 80%"}; + ConfigurableAxis massAxis{"massAxis", {30, 1.1, 1.13}, "Invariant mass axis"}; ConfigurableAxis ptAxis{"ptAxis", {VARIABLE_WIDTH, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.5, 8.0, 10.0, 100.0}, "Transverse momentum bins"}; - ConfigurableAxis ptFullAxis{"ptFullAxis", {VARIABLE_WIDTH, -5.0, -4.0, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, -0.2, 0, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0}, "Transverse momentum bins"}; ConfigurableAxis centAxis{"centAxis", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 100}, "Centrality interval"}; ConfigurableAxis cosAxis{"cosAxis", {110, -1.05, 1.05}, "Cosine axis"}; ConfigurableAxis rapAxis{"rapAxis", {10, -0.5, 0.5}, "Rapidity axis"}; ConfigurableAxis qqAxis{"qqAxis", {100, -0.1, 0.1}, "qq axis"}; - ConfigurableAxis lowerQAxis{"lowerQAxis", {800, 0, 800}, "result of q2"}; - ConfigurableAxis multNumAxis{"multNumAxis", {300, 0, 2700}, "mult num"}; - ConfigurableAxis qvecAxis{"qvecAxis", {300, -1, 1}, "range of Qvector component"}; - ConfigurableAxis qvec2Axis{"qvec2Axis", {600, 0, 600}, "range of Qvector Module"}; - - static constexpr float kMinAmplitudeThreshold = 1e-5f; - static constexpr int kShiftLevel = 10; - static constexpr int kLambdaId = 3122; - static constexpr std::array kCorrLevel = {2, 3, 4, 1}; - static constexpr std::array kCentBoundaries = {0.0f, 3.49f, 4.93f, 6.98f, 8.55f, 9.87f, 11.0f, 12.1f, 13.1f, 14.0f}; - static constexpr std::array kCentValues = {2.5f, 7.5f, 15.0f, 25.0f, 35.0f, 45.0f, 55.0f, 65.0f, 75.0f}; - static constexpr std::array, 8> kLowQvec = {{{121, 196}, {110, 172}, {93, 143}, {74, 117}, {58, 92}, {43, 70}, {31, 50}, {21, 34}}}; - static constexpr float kEtaAcceptance = 0.8f; - static constexpr float kCentUpperLimit = 80.0f; + ConfigurableAxis multAxis{"multAxis", {300, 0, 2700}, "multiplicity"}; + ConfigurableAxis q2QaAxis{"q2QaAxis", {8000, 0.0, 800.0}, "q2 axis for QA"}; + + static constexpr float MinAmplitudeThreshold = 1e-5f; + static constexpr int ShiftLevel = 10; + static constexpr int LambdaId = 3122; + static constexpr int SecondHarmonic = 2; + static constexpr int ThirdHarmonic = 3; + static constexpr std::array CorrLevel = {2, 3, 4, 1}; + static constexpr std::array CentBoundaries = {0.0f, 3.49f, 4.93f, 6.98f, 8.55f, 9.87f, 11.0f, 12.1f, 13.1f, 14.0f}; + static constexpr std::array CentValues = {2.5f, 7.5f, 15.0f, 25.0f, 35.0f, 45.0f, 55.0f, 65.0f, 75.0f}; + static constexpr float EtaAcceptance = 0.8f; + static constexpr float CentUpperLimit = 80.0f; + static constexpr int NEseCentBins = 8; + static constexpr int NEseGroups = 10; + static constexpr int NEseCutsPerCentBin = NEseGroups - 1; + static constexpr int GFWRefMask = 1; + static constexpr int GFWPoiMask = 2; + static constexpr float EseCentMin = 0.0f; + static constexpr float EseCentMax = 80.0f; + static constexpr float EseCentBinWidth = 10.0f; + + TH2* histEventCountEseGroup = nullptr; + std::unordered_map histEse; EventPlaneHelper helperEP; TF1* fMultPVCutLow = nullptr; TF1* fMultPVCutHigh = nullptr; - int detId; - int refAId; - int refBId; + int detId = -1; + int refAId = -1; + int refBId = -1; - int qvecDetInd; - int qvecRefAInd; - int qvecRefBInd; + int qvecDetInd = -1; + int qvecRefAInd = -1; + int qvecRefBInd = -1; - float centrality; + float centrality = -1.0f; - double angle; - double psi; - double relphi; + double angle = 0.0; + double psi = 0.0; + double relphi = 0.0; + double productPhi = 0.0; int currentRunNumber = -999; int lastRunNumber = -999; - std::vector shiftprofile{}; + std::vector shiftprofile; TProfile2D* effMap = nullptr; TProfile2D* accMap = nullptr; + struct { + int lastGfwCorrectionRunNumber = -999; + TH1D* gfwEffMap = nullptr; + GFWWeights* gfwAccWeights = nullptr; + TProfile2D* histRefQn = nullptr; + TProfile3D* histPoiNQn = nullptr; + TProfile3D* histPoiPQn = nullptr; + GFW* fGFW = new GFW(); + GFW::CorrConfig corrRef22; + GFW::CorrConfig corrPoiN22; + GFW::CorrConfig corrPoiP22; + GFW::CorrConfig corrRef32; + GFW::CorrConfig corrPoiN32; + GFW::CorrConfig corrPoiP32; + } gfwState; std::string fullCCDBShiftCorrPath; template int getDetId(const T& name) { - if (name.value == "FT0C") { - return 0; - } else if (name.value == "FT0A") { + if (name.value == "FT0A") { return 1; - } else if (name.value == "FT0M") { + } + if (name.value == "FT0M") { return 2; - } else if (name.value == "FV0A") { + } + if (name.value == "FV0A") { return 3; - } else if (name.value == "TPCpos") { + } + if (name.value == "TPCpos") { return 4; - } else if (name.value == "TPCneg") { + } + if (name.value == "TPCneg") { return 5; - } else if (name.value == "TPCall") { + } + if (name.value == "TPCall") { return 6; - } else { - return 0; } + return 0; + } + + int eseCentBin(float cent) const + { + if (cent < EseCentMin || cent >= EseCentMax) { + return -1; + } + return static_cast(cent / EseCentBinWidth); + } + + std::string eseGroupSuffix(int group) const + { + if (group < 0 || group >= NEseGroups) { + return {}; + } + return Form("q%dp%02d_%02d", cfgEseHarmonic.value, group * 10, (group + 1) * 10); + } + + const std::vector& esePercentileCuts() const + { + return cfgEseHarmonic.value == SecondHarmonic ? cfgQ2PercentileCuts.value : cfgQ3PercentileCuts.value; + } + + template + void addEseHistogram(const std::string& name, HistType type, const std::vector& axes) + { + histEse[name] = histos.add(name, "", type, axes).get(); + } + + template + T* getEseHistogram(const std::string& name) + { + const auto histogram = histEse.find(name); + if (histogram == histEse.end()) { + LOGF(fatal, "Could not find ESE histogram %s", name.c_str()); + return nullptr; + } + return static_cast(histogram->second); + } + + int eseGroup(float cent, double qn) const + { + const int centBin = eseCentBin(cent); + if (centBin < 0 || !std::isfinite(qn)) { + return -1; + } + const auto& cuts = esePercentileCuts(); + const int offset = centBin * NEseCutsPerCentBin; + for (int iCut = 0; iCut < NEseCutsPerCentBin; ++iCut) { + if (qn < cuts.at(offset + iCut)) { + return iCut; + } + } + return NEseGroups - 1; + } + + template + double getEseQ(TCollision const& collision) + { + const int harmonicIndex = cfgEseHarmonic.value - SecondHarmonic; + if (collision.qvecFT0CReVec().size() <= static_cast(harmonicIndex) || collision.qvecFT0CImVec().size() <= static_cast(harmonicIndex)) { + LOGF(fatal, "FT0C Q-vector table does not contain harmonic %d", cfgEseHarmonic.value); + } + const double qx = collision.qvecFT0CReVec()[harmonicIndex]; + const double qy = collision.qvecFT0CImVec()[harmonicIndex]; + if (cfgMultCor) { + return std::sqrt(qx * qx + qy * qy) * collision.sumAmplFT0C() / std::sqrt(collision.multFT0C()); + } + return std::sqrt(qx * qx + qy * qy) * std::sqrt(collision.sumAmplFT0C()); } void init(o2::framework::InitContext&) { + if (cfgEseHarmonic.value != SecondHarmonic && cfgEseHarmonic.value != ThirdHarmonic) { + LOGF(fatal, "cfgEseHarmonic must be 2 or 3, got %d", cfgEseHarmonic.value); + } + const auto& eseCuts = esePercentileCuts(); + if (eseCuts.size() != NEseCentBins * NEseCutsPerCentBin) { + LOGF(fatal, "cfgQ%dPercentileCuts must contain %d values, got %d", cfgEseHarmonic.value, NEseCentBins * NEseCutsPerCentBin, static_cast(eseCuts.size())); + } + for (int iCent = 0; iCent < NEseCentBins; ++iCent) { + const int offset = iCent * NEseCutsPerCentBin; + for (int iCut = 0; iCut < NEseCutsPerCentBin; ++iCut) { + const float cut = eseCuts.at(offset + iCut); + if (!std::isfinite(cut) || (iCut > 0 && cut <= eseCuts.at(offset + iCut - 1))) { + LOGF(fatal, "Invalid q%d percentile cut at centrality bin %d, cut %d", cfgEseHarmonic.value, iCent, iCut); + } + } + } + AxisSpec centQaAxis = {80, 0.0, 80.0}; AxisSpec pVzQaAxis = {300, -15.0, 15.0}; AxisSpec epAxis = {6, 0.0, o2::constants::math::TwoPI}; @@ -228,28 +372,38 @@ struct FlowEseTask { AxisSpec shiftAxis = {10, 0, 10, "shift"}; AxisSpec basisAxis = {20, 0, 20, "basis"}; + AxisSpec eseGroupAxis = {10, 0.0, 10.0, Form("q_{%d} percentile group", cfgEseHarmonic.value)}; - histos.add(Form("histQvecV2"), "", {HistType::kTH3F, {qvecAxis, qvecAxis, centAxis}}); - histos.add(Form("histMult_Cent"), "", {HistType::kTH2F, {multNumAxis, centAxis}}); - histos.add(Form("histQvecCent"), "", {HistType::kTH2F, {lowerQAxis, centAxis}}); + histos.add("histQvecCent", "", {HistType::kTH2F, {q2QaAxis, centQaAxis}}); + histEventCountEseGroup = histos.add(Form("histEventCountQ%dGroup", cfgEseHarmonic.value), "", HistType::kTH2F, {centQaAxis, eseGroupAxis}).get(); histos.add(Form("histVertex"), "", {HistType::kTHnSparseF, {vertexAxis, vertexAxis, vertexAxis, centAxis}}); - histos.add(Form("histV2"), "", {HistType::kTHnSparseF, {centAxis, ptAxis, cosAxis, qvec2Axis}}); - histos.add(Form("histV2_lambda"), "", {HistType::kTHnSparseF, {centAxis, ptAxis, cosAxis, qvec2Axis, massAxis}}); - histos.add(Form("histV2_alambda"), "", {HistType::kTHnSparseF, {centAxis, ptAxis, cosAxis, qvec2Axis, massAxis}}); + for (int iGroup = 0; iGroup < NEseGroups; ++iGroup) { + const auto suffix = eseGroupSuffix(iGroup); + addEseHistogram(Form("histV%d_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTHnSparseF, {centAxis, ptAxis, cosAxis}); + addEseHistogram(Form("psi%d/h_lambda_cos_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}); + addEseHistogram(Form("psi%d/h_alambda_cos_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}); + addEseHistogram(Form("psi%d/h_lambda_cos2_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}); + addEseHistogram(Form("psi%d/h_alambda_cos2_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}); + addEseHistogram(Form("psi%d/h_lambda_cossin_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}); + addEseHistogram(Form("psi%d/h_alambda_cossin_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}); + } histos.add("QA/CentDist", "", {HistType::kTH1F, {centQaAxis}}); histos.add("QA/PVzDist", "", {HistType::kTH1F, {pVzQaAxis}}); + histos.add("psi2/h_Ref03Gap22", "", {HistType::kTProfile, {centAxis}}); + histos.add("psi2/h_PoiN03Gap22", "", {HistType::kTProfile2D, {ptAxis, centAxis}}); + histos.add("psi2/h_PoiP03Gap22", "", {HistType::kTProfile2D, {ptAxis, centAxis}}); + histos.add("psi3/h_Ref03Gap32", "", {HistType::kTProfile, {centAxis}}); + histos.add("psi3/h_PoiN03Gap32", "", {HistType::kTProfile2D, {ptAxis, centAxis}}); + histos.add("psi3/h_PoiP03Gap32", "", {HistType::kTProfile2D, {ptAxis, centAxis}}); + gfwState.histRefQn = histos.add(Form("psi%d/h_Ref03Gap%d2VsQ%d", cfgEseHarmonic.value, cfgEseHarmonic.value, cfgEseHarmonic.value), "", HistType::kTProfile2D, {centAxis, eseGroupAxis}).get(); + gfwState.histPoiNQn = histos.add(Form("psi%d/h_PoiN03Gap%d2VsQ%d", cfgEseHarmonic.value, cfgEseHarmonic.value, cfgEseHarmonic.value), "", HistType::kTProfile3D, {ptAxis, centAxis, eseGroupAxis}).get(); + gfwState.histPoiPQn = histos.add(Form("psi%d/h_PoiP03Gap%d2VsQ%d", cfgEseHarmonic.value, cfgEseHarmonic.value, cfgEseHarmonic.value), "", HistType::kTProfile3D, {ptAxis, centAxis, eseGroupAxis}).get(); for (auto i = 2; i < cfgnMods + 2; i++) { histos.add(Form("psi%d/h_lambda_cos", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, epAxis}}); histos.add(Form("psi%d/h_alambda_cos", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, epAxis}}); - histos.add(Form("psi%d/h_lambda_cos_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - histos.add(Form("psi%d/h_alambda_cos_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - histos.add(Form("psi%d/h_lambda_cos2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, epAxis}}); histos.add(Form("psi%d/h_alambda_cos2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, epAxis}}); - histos.add(Form("psi%d/h_lambda_cos2_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - histos.add(Form("psi%d/h_alambda_cos2_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - if (cfgRapidityDep) { histos.add(Form("psi%d/h_lambda_cos2_rap", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, rapAxis}}); histos.add(Form("psi%d/h_alambda_cos2_rap", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, rapAxis}}); @@ -257,9 +411,8 @@ struct FlowEseTask { histos.add(Form("psi%d/h_lambda_cossin", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); histos.add(Form("psi%d/h_alambda_cossin", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); - - histos.add(Form("psi%d/h_lambda_cossin_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - histos.add(Form("psi%d/h_alambda_cossin_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); + histos.add(Form("psi%d/h_lambda_cossin_SP", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); + histos.add(Form("psi%d/h_alambda_cossin_SP", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); if (cfgAccAzimuth) { histos.add(Form("psi%d/h_lambda_coscos", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); @@ -268,13 +421,8 @@ struct FlowEseTask { histos.add(Form("psi%d/h_lambda_vncos", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); histos.add(Form("psi%d/h_lambda_vnsin", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); - histos.add(Form("psi%d/h_lambda_vncos_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - histos.add(Form("psi%d/h_lambda_vnsin_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - histos.add(Form("psi%d/h_alambda_vncos", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); histos.add(Form("psi%d/h_alambda_vnsin", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis}}); - histos.add(Form("psi%d/h_alambda_vncos_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); - histos.add(Form("psi%d/h_alambda_vnsin_q2", i), "", {HistType::kTHnSparseF, {massAxis, ptAxis, cosAxis, centAxis, qvec2Axis}}); } histos.add("QA/ptspec_l", "", {HistType::kTH3F, {massAxis, ptAxis, centAxis}}); histos.add("QA/ptspec_al", "", {HistType::kTH3F, {massAxis, ptAxis, centAxis}}); @@ -370,7 +518,6 @@ struct FlowEseTask { histos.add(Form("psi%d/QA/EPRes_Det_RefA", i), "", {HistType::kTH2F, {centQaAxis, cosAxis}}); histos.add(Form("psi%d/QA/EPRes_Det_RefB", i), "", {HistType::kTH2F, {centQaAxis, cosAxis}}); histos.add(Form("psi%d/QA/EPRes_RefA_RefB", i), "", {HistType::kTH2F, {centQaAxis, cosAxis}}); - histos.add(Form("psi%d/QA/EP_FT0C_shifted", i), "", {HistType::kTH2F, {centQaAxis, epQaAxis}}); histos.add(Form("psi%d/QA/EP_FT0A_shifted", i), "", {HistType::kTH2F, {centQaAxis, epQaAxis}}); histos.add(Form("psi%d/QA/EP_FV0A_shifted", i), "", {HistType::kTH2F, {centQaAxis, epQaAxis}}); @@ -379,6 +526,12 @@ struct FlowEseTask { histos.add(Form("psi%d/QA/EPRes_FT0C_FV0A_shifted", i), "", {HistType::kTH2F, {centQaAxis, cosAxis}}); histos.add(Form("psi%d/QA/EPRes_FT0A_FV0A_shifted", i), "", {HistType::kTH2F, {centQaAxis, cosAxis}}); } + for (int iGroup = 0; iGroup < NEseGroups; ++iGroup) { + const auto suffix = eseGroupSuffix(iGroup); + addEseHistogram(Form("psi%d/QA/EPRes_Det_RefA_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTH2F, {centQaAxis, cosAxis}); + addEseHistogram(Form("psi%d/QA/EPRes_Det_RefB_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTH2F, {centQaAxis, cosAxis}); + addEseHistogram(Form("psi%d/QA/EPRes_RefA_RefB_%s", cfgEseHarmonic.value, suffix.c_str()), HistType::kTH2F, {centQaAxis, cosAxis}); + } } if (doprocessMcItsTpc) { @@ -395,6 +548,9 @@ struct FlowEseTask { for (auto i = 2; i < cfgnMods + 2; i++) { histos.add(Form("psi%d/ShiftFIT", i), "", kTProfile3D, {centQaAxis, basisAxis, shiftAxis}); } + if (cfgEseHarmonic.value >= cfgnMods.value + 2) { + histos.add(Form("psi%d/ShiftFIT", cfgEseHarmonic.value), "", kTProfile3D, {centQaAxis, basisAxis, shiftAxis}); + } } detId = getDetId(cfgQvecDetName); @@ -413,6 +569,21 @@ struct FlowEseTask { fMultPVCutHigh = new TF1("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x + 2.5*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); fMultPVCutHigh->SetParameters(2834.66, -87.0127, 0.915126, -0.00330136, 332.513, -12.3476, 0.251663, -0.00272819, 1.12242e-05); + const int nPtBins = histos.get(HIST("psi2/h_PoiN03Gap22"))->GetXaxis()->GetNbins(); + gfwState.fGFW->AddRegion("refN03", -0.8, -0.15, 1, GFWRefMask); + gfwState.fGFW->AddRegion("refP03", 0.15, 0.8, 1, GFWRefMask); + gfwState.fGFW->AddRegion("poiN03", -0.8, -0.15, nPtBins + 1, GFWPoiMask); + gfwState.fGFW->AddRegion("poiP03", 0.15, 0.8, nPtBins + 1, GFWPoiMask); + + gfwState.corrRef22 = gfwState.fGFW->GetCorrelatorConfig("refN03 {2} refP03 {-2}", "Ref03Gap22", false); + gfwState.corrPoiN22 = gfwState.fGFW->GetCorrelatorConfig("poiN03 {2} refP03 {-2}", "PoiN03Gap22", true); + gfwState.corrPoiP22 = gfwState.fGFW->GetCorrelatorConfig("poiP03 {2} refN03 {-2}", "PoiP03Gap22", true); + gfwState.corrRef32 = gfwState.fGFW->GetCorrelatorConfig("refN03 {3} refP03 {-3}", "Ref03Gap32", false); + gfwState.corrPoiN32 = gfwState.fGFW->GetCorrelatorConfig("poiN03 {3} refP03 {-3}", "PoiN03Gap32", true); + gfwState.corrPoiP32 = gfwState.fGFW->GetCorrelatorConfig("poiP03 {3} refN03 {-3}", "PoiP03Gap32", true); + + gfwState.fGFW->CreateRegions(); + ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -427,7 +598,7 @@ struct FlowEseTask { ROOT::Math::PxPyPzMVector protonVec, pionVec, LambdaVec, protonBoostedVec, pionBoostedVec; template - bool eventSelected(TCollision collision) + bool eventSelected(TCollision const& collision) { if (!collision.sel8()) { return 0; @@ -467,31 +638,42 @@ struct FlowEseTask { template bool selectionV0(TCollision const& collision, V0 const& candidate, int lambdaTag) { - if (candidate.v0radius() < cfgv0radiusMin) + if (candidate.v0radius() < cfgv0radiusMin) { return false; + } if (lambdaTag) { - if (std::abs(candidate.dcapostopv()) < cfgDCAPrToPVMin) + if (std::abs(candidate.dcapostopv()) < cfgDCAPrToPVMin) { return false; - if (std::abs(candidate.dcanegtopv()) < cfgDCAPiToPVMin) + } + if (std::abs(candidate.dcanegtopv()) < cfgDCAPiToPVMin) { return false; - } else if (!lambdaTag) { - if (std::abs(candidate.dcapostopv()) < cfgDCAPiToPVMin) + } + } else { + if (std::abs(candidate.dcapostopv()) < cfgDCAPiToPVMin) { return false; - if (std::abs(candidate.dcanegtopv()) < cfgDCAPrToPVMin) + } + if (std::abs(candidate.dcanegtopv()) < cfgDCAPrToPVMin) { return false; + } } - if (candidate.v0cosPA() < cfgv0CosPA) + if (candidate.v0cosPA() < cfgv0CosPA) { return false; - if (std::abs(candidate.dcaV0daughters()) > cfgDCAV0Dau) + } + if (std::abs(candidate.dcaV0daughters()) > cfgDCAV0Dau) { return false; - if (candidate.pt() < cfgV0PtMin) + } + if (candidate.pt() < cfgV0PtMin) { return false; - if (candidate.yLambda() < cfgV0EtaMin) + } + if (candidate.yLambda() < cfgV0EtaMin) { return false; - if (candidate.yLambda() > cfgV0EtaMax) + } + if (candidate.yLambda() > cfgV0EtaMax) { return false; - if (candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massLambda > cfgV0LifeTime) + } + if (candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massLambda > cfgV0LifeTime) { return false; + } return true; } @@ -499,57 +681,75 @@ struct FlowEseTask { template bool isSelectedV0Daughter(T const& track, int pid) // pid 0: proton, pid 1: pion { - if (track.tpcNClsFound() < cfgDaughTPCnclsMin) + if (track.tpcNClsFound() < cfgDaughTPCnclsMin) { return false; - if (pid == 0 && std::abs(track.tpcNSigmaPr()) > cfgDaughPIDCutsTPCPr) + } + if (pid == 0 && std::abs(track.tpcNSigmaPr()) > cfgDaughPIDCutsTPCPr) { return false; - if (pid == 1 && std::abs(track.tpcNSigmaPi()) > cfgDaughPIDCutsTPCPi) + } + if (pid == 1 && std::abs(track.tpcNSigmaPi()) > cfgDaughPIDCutsTPCPi) { return false; - if (track.eta() > cfgDaughEtaMax) + } + if (track.eta() > cfgDaughEtaMax) { return false; - if (track.eta() < cfgDaughEtaMin) + } + if (track.eta() < cfgDaughEtaMin) { return false; - if (pid == 0 && track.pt() < cfgDaughPrPt) + } + if (pid == 0 && track.pt() < cfgDaughPrPt) { return false; - if (pid == 1 && track.pt() < cfgDaughPiPt) + } + if (pid == 1 && track.pt() < cfgDaughPiPt) { return false; + } return true; } double safeATan2(double y, double x) { - if (x != 0) + if (x != 0) { return std::atan2(y, x); - if (y == 0) + } + if (y == 0) { return 0; - if (y > 0) + } + if (y > 0) { return o2::constants::math::PIHalf; - else - return -o2::constants::math::PIHalf; + } + return -o2::constants::math::PIHalf; } template bool selectionTrack(TrackType const& track) { - if (track.pt() < cfgMinPt) + if (track.pt() < cfgMinPt) { return false; - if (std::abs(track.eta()) > cfgMaxEta) + } + if (std::abs(track.eta()) > cfgMaxEta) { return false; - if (!track.passedITSNCls()) + } + if (!track.passedITSNCls()) { return false; - if (!track.passedITSChi2NDF()) + } + if (!track.passedITSChi2NDF()) { return false; - if (!track.passedITSHits()) + } + if (!track.passedITSHits()) { return false; - if (!track.passedTPCCrossedRowsOverNCls()) + } + if (!track.passedTPCCrossedRowsOverNCls()) { return false; - if (!track.passedTPCChi2NDF()) + } + if (!track.passedTPCChi2NDF()) { return false; - if (!track.passedDCAxy()) + } + if (!track.passedDCAxy()) { return false; - if (!track.passedDCAz()) + } + if (!track.passedDCAz()) { return false; + } return true; } @@ -561,8 +761,8 @@ struct FlowEseTask { qvecRefAInd = refAId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; qvecRefBInd = refBId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; - for (int ishift = 1; ishift <= kShiftLevel; ishift++) { - if (nmode == kCorrLevel[0]) { + for (int ishift = 1; ishift <= ShiftLevel; ishift++) { + if (nmode == CorrLevel[0]) { histos.fill(HIST("psi2/ShiftFIT"), centrality, 0.5, ishift - 0.5, std::sin(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode))); histos.fill(HIST("psi2/ShiftFIT"), centrality, 1.5, ishift - 0.5, std::cos(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode))); @@ -571,7 +771,7 @@ struct FlowEseTask { histos.fill(HIST("psi2/ShiftFIT"), centrality, 4.5, ishift - 0.5, std::sin(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode))); histos.fill(HIST("psi2/ShiftFIT"), centrality, 5.5, ishift - 0.5, std::cos(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode))); - } else if (nmode == kCorrLevel[1]) { + } else if (nmode == CorrLevel[1]) { histos.fill(HIST("psi3/ShiftFIT"), centrality, 0.5, ishift - 0.5, std::sin(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode))); histos.fill(HIST("psi3/ShiftFIT"), centrality, 1.5, ishift - 0.5, std::cos(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode))); @@ -580,7 +780,7 @@ struct FlowEseTask { histos.fill(HIST("psi3/ShiftFIT"), centrality, 4.5, ishift - 0.5, std::sin(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode))); histos.fill(HIST("psi3/ShiftFIT"), centrality, 5.5, ishift - 0.5, std::cos(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode))); - } else if (nmode == kCorrLevel[2]) { + } else if (nmode == CorrLevel[2]) { histos.fill(HIST("psi4/ShiftFIT"), centrality, 0.5, ishift - 0.5, std::sin(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode))); histos.fill(HIST("psi4/ShiftFIT"), centrality, 1.5, ishift - 0.5, std::cos(ishift * static_cast(nmode) * std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode))); @@ -600,10 +800,11 @@ struct FlowEseTask { qvecRefAInd = refAId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; qvecRefBInd = refBId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; - if (collision.qvecAmp()[detId] < kMinAmplitudeThreshold || collision.qvecAmp()[refAId] < kMinAmplitudeThreshold || collision.qvecAmp()[refBId] < kMinAmplitudeThreshold) + if (collision.qvecAmp()[detId] < MinAmplitudeThreshold || collision.qvecAmp()[refAId] < MinAmplitudeThreshold || collision.qvecAmp()[refBId] < MinAmplitudeThreshold) { return; + } - if (nmode == kCorrLevel[0]) { + if (nmode == CorrLevel[0]) { histos.fill(HIST("psi2/QA/EP_Det"), centrality, std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode)); histos.fill(HIST("psi2/QA/EP_RefA"), centrality, std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]) / static_cast(nmode)); histos.fill(HIST("psi2/QA/EP_RefB"), centrality, std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode)); @@ -619,7 +820,7 @@ struct FlowEseTask { histos.fill(HIST("psi2/QA/EPRes_Det_RefA"), centrality, std::cos(std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) - std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]))); histos.fill(HIST("psi2/QA/EPRes_Det_RefB"), centrality, std::cos(std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) - std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]))); histos.fill(HIST("psi2/QA/EPRes_RefA_RefB"), centrality, std::cos(std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]) - std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]))); - } else if (nmode == kCorrLevel[1]) { + } else if (nmode == CorrLevel[1]) { histos.fill(HIST("psi3/QA/EP_Det"), centrality, std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode)); histos.fill(HIST("psi3/QA/EP_RefA"), centrality, std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]) / static_cast(nmode)); histos.fill(HIST("psi3/QA/EP_RefB"), centrality, std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode)); @@ -635,7 +836,7 @@ struct FlowEseTask { histos.fill(HIST("psi3/QA/EPRes_Det_RefA"), centrality, std::cos(std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) - std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]))); histos.fill(HIST("psi3/QA/EPRes_Det_RefB"), centrality, std::cos(std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) - std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]))); histos.fill(HIST("psi3/QA/EPRes_RefA_RefB"), centrality, std::cos(std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]) - std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]))); - } else if (nmode == kCorrLevel[2]) { + } else if (nmode == CorrLevel[2]) { histos.fill(HIST("psi4/QA/EP_Det"), centrality, std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode)); histos.fill(HIST("psi4/QA/EP_RefA"), centrality, std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]) / static_cast(nmode)); histos.fill(HIST("psi4/QA/EP_RefB"), centrality, std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode)); @@ -661,7 +862,7 @@ struct FlowEseTask { auto psidefFT0C = std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode); auto psidefFT0A = std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]) / static_cast(nmode); auto psidefFV0A = std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode); - for (int ishift = 1; ishift <= kShiftLevel; ishift++) { + for (int ishift = 1; ishift <= ShiftLevel; ishift++) { auto coeffshiftxFT0C = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(centrality, 0.5, ishift - 0.5)); auto coeffshiftyFT0C = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(centrality, 1.5, ishift - 0.5)); auto coeffshiftxFT0A = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(centrality, 2.5, ishift - 0.5)); @@ -673,7 +874,7 @@ struct FlowEseTask { deltapsiFT0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFT0A * std::cos(ishift * static_cast(nmode) * psidefFT0A) + coeffshiftyFT0A * std::sin(ishift * static_cast(nmode) * psidefFT0A))); deltapsiFV0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFV0A * std::cos(ishift * static_cast(nmode) * psidefFV0A) + coeffshiftyFV0A * std::sin(ishift * static_cast(nmode) * psidefFV0A))); } - if (nmode == kCorrLevel[0]) { + if (nmode == CorrLevel[0]) { histos.fill(HIST("psi2/QA/EP_FT0C_shifted"), centrality, psidefFT0C + deltapsiFT0C); histos.fill(HIST("psi2/QA/EP_FT0A_shifted"), centrality, psidefFT0A + deltapsiFT0A); histos.fill(HIST("psi2/QA/EP_FV0A_shifted"), centrality, psidefFV0A + deltapsiFV0A); @@ -681,8 +882,7 @@ struct FlowEseTask { histos.fill(HIST("psi2/QA/EPRes_FT0C_FT0A_shifted"), centrality, std::cos(static_cast(nmode) * (psidefFT0C + deltapsiFT0C - psidefFT0A - deltapsiFT0A))); histos.fill(HIST("psi2/QA/EPRes_FT0C_FV0A_shifted"), centrality, std::cos(static_cast(nmode) * (psidefFT0C + deltapsiFT0C - psidefFV0A - deltapsiFV0A))); histos.fill(HIST("psi2/QA/EPRes_FT0A_FV0A_shifted"), centrality, std::cos(static_cast(nmode) * (psidefFT0A + deltapsiFT0A - psidefFV0A - deltapsiFV0A))); - - } else if (nmode == kCorrLevel[1]) { + } else if (nmode == CorrLevel[1]) { histos.fill(HIST("psi3/QA/EP_FT0C_shifted"), centrality, psidefFT0C + deltapsiFT0C); histos.fill(HIST("psi3/QA/EP_FT0A_shifted"), centrality, psidefFT0A + deltapsiFT0A); histos.fill(HIST("psi3/QA/EP_FV0A_shifted"), centrality, psidefFV0A + deltapsiFV0A); @@ -690,7 +890,7 @@ struct FlowEseTask { histos.fill(HIST("psi3/QA/EPRes_FT0C_FT0A_shifted"), centrality, std::cos(static_cast(nmode) * (psidefFT0C + deltapsiFT0C - psidefFT0A - deltapsiFT0A))); histos.fill(HIST("psi3/QA/EPRes_FT0C_FV0A_shifted"), centrality, std::cos(static_cast(nmode) * (psidefFT0C + deltapsiFT0C - psidefFV0A - deltapsiFV0A))); histos.fill(HIST("psi3/QA/EPRes_FT0A_FV0A_shifted"), centrality, std::cos(static_cast(nmode) * (psidefFT0A + deltapsiFT0A - psidefFV0A - deltapsiFV0A))); - } else if (nmode == kCorrLevel[2]) { + } else if (nmode == CorrLevel[2]) { histos.fill(HIST("psi4/QA/EP_FT0C_shifted"), centrality, psidefFT0C + deltapsiFT0C); histos.fill(HIST("psi4/QA/EP_FT0A_shifted"), centrality, psidefFT0A + deltapsiFT0A); histos.fill(HIST("psi4/QA/EP_FV0A_shifted"), centrality, psidefFV0A + deltapsiFV0A); @@ -702,28 +902,174 @@ struct FlowEseTask { } } + template + void fillEseEPQA(TCollision const& collision, int eseGroupIndex) + { + if (eseGroupIndex < 0 || !cfgQAv0) { + return; + } + const int nmode = cfgEseHarmonic.value; + const int detIndex = detId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; + const int refAIndex = refAId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; + const int refBIndex = refBId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; + if (collision.qvecAmp()[detId] < MinAmplitudeThreshold || collision.qvecAmp()[refAId] < MinAmplitudeThreshold || collision.qvecAmp()[refBId] < MinAmplitudeThreshold) { + return; + } + + const double detPhase = std::atan2(collision.qvecIm()[detIndex], collision.qvecRe()[detIndex]); + const double refAPhase = std::atan2(collision.qvecIm()[refAIndex], collision.qvecRe()[refAIndex]); + const double refBPhase = std::atan2(collision.qvecIm()[refBIndex], collision.qvecRe()[refBIndex]); + const auto suffix = eseGroupSuffix(eseGroupIndex); + auto* histEseEPResDetRefA = getEseHistogram(Form("psi%d/QA/EPRes_Det_RefA_%s", cfgEseHarmonic.value, suffix.c_str())); + auto* histEseEPResDetRefB = getEseHistogram(Form("psi%d/QA/EPRes_Det_RefB_%s", cfgEseHarmonic.value, suffix.c_str())); + auto* histEseEPResRefARefB = getEseHistogram(Form("psi%d/QA/EPRes_RefA_RefB_%s", cfgEseHarmonic.value, suffix.c_str())); + histEseEPResDetRefA->Fill(centrality, std::cos(detPhase - refAPhase)); + histEseEPResDetRefB->Fill(centrality, std::cos(detPhase - refBPhase)); + histEseEPResRefARefB->Fill(centrality, std::cos(refAPhase - refBPhase)); + } + template - void fillHistograms(TCollision const& collision, V0 const& V0s, TrackType const& track, int nmode) + void fillHistograms(TCollision const& collision, V0 const& V0s, TrackType const& track, int nmode, int eseGroupIndex = -1, bool fillRegular = true, bool fillGfw = false) { qvecDetInd = detId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; qvecRefAInd = refAId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; qvecRefBInd = refBId * 4 + 3 + (nmode - 2) * cfgNQvec * 4; + const bool fillEse = eseGroupIndex >= 0 && nmode == cfgEseHarmonic.value; + + THnSparse* histEseVn = nullptr; + double esePlane = 0.0; + if (fillEse) { + const auto suffix = eseGroupSuffix(eseGroupIndex); + histEseVn = getEseHistogram(Form("histV%d_%s", cfgEseHarmonic.value, suffix.c_str())); + const int harmonicIndex = nmode - 2; + esePlane = helperEP.GetEventPlane(collision.qvecFT0CReVec()[harmonicIndex], collision.qvecFT0CImVec()[harmonicIndex], nmode); + } - for (const auto& trk : track) { - if (!selectionTrack(trk)) { - continue; + TProfile2D* histPoiN22 = nullptr; + if (fillGfw) { + gfwState.fGFW->Clear(); + histPoiN22 = histos.get(HIST("psi2/h_PoiN03Gap22")).get(); + } + if (fillEse || fillGfw) { + for (const auto& trk : track) { + if (!selectionTrack(trk)) { + continue; + } + if (fillEse) { + const std::array values = {centrality, trk.pt(), std::cos(static_cast(nmode) * (trk.phi() - esePlane))}; + histEseVn->Fill(values.data()); + } + if (!fillGfw) { + continue; + } + double weff = 1.0; + double wacc = 1.0; + if (cfgGfwParam.cfgGfwEffCor && gfwState.gfwEffMap != nullptr) { + const double efficiency = gfwState.gfwEffMap->GetBinContent(gfwState.gfwEffMap->FindBin(trk.pt())); + if (efficiency <= 0.0) { + continue; + } + weff = 1.0 / efficiency; + } + if (cfgGfwParam.cfgGfwAccCor && gfwState.gfwAccWeights != nullptr) { + wacc = gfwState.gfwAccWeights->getNUA(trk.phi(), trk.eta(), collision.posZ()); + } + const double trackWeight = weff * wacc; + const int ptBin = histPoiN22->GetXaxis()->FindBin(trk.pt()) - 1; + gfwState.fGFW->Fill(trk.eta(), ptBin, trk.phi(), trackWeight, GFWRefMask); + gfwState.fGFW->Fill(trk.eta(), ptBin, trk.phi(), trackWeight, GFWPoiMask); } - if (nmode == kCorrLevel[0]) { - histos.fill(HIST("histV2"), collision.centFT0C(), trk.pt(), - std::cos(static_cast(nmode) * (trk.phi() - helperEP.GetEventPlane(collision.qvecFT0CReVec()[0], collision.qvecFT0CImVec()[0], nmode))), - std::sqrt(collision.qvecFT0CReVec()[0] * collision.qvecFT0CReVec()[0] + collision.qvecFT0CImVec()[0] * collision.qvecFT0CImVec()[0]) * std::sqrt(collision.sumAmplFT0C())); + } + + if (fillGfw) { + double denominator = gfwState.fGFW->Calculate(gfwState.corrRef22, 0, true).real(); + if (denominator > 0.0) { + const double correlation = gfwState.fGFW->Calculate(gfwState.corrRef22, 0, false).real() / denominator; + if (std::abs(correlation) < 1.0) { + histos.fill(HIST("psi2/h_Ref03Gap22"), centrality, correlation, denominator); + if (fillEse && cfgEseHarmonic.value == SecondHarmonic) { + gfwState.histRefQn->Fill(centrality, eseGroupIndex + 0.5, correlation, denominator); + } + } + } + denominator = gfwState.fGFW->Calculate(gfwState.corrRef32, 0, true).real(); + if (denominator > 0.0) { + const double correlation = gfwState.fGFW->Calculate(gfwState.corrRef32, 0, false).real() / denominator; + if (std::abs(correlation) < 1.0) { + histos.fill(HIST("psi3/h_Ref03Gap32"), centrality, correlation, denominator); + if (fillEse && cfgEseHarmonic.value == ThirdHarmonic) { + gfwState.histRefQn->Fill(centrality, eseGroupIndex + 0.5, correlation, denominator); + } + } + } + + for (int iPt = 1; iPt <= histPoiN22->GetXaxis()->GetNbins(); ++iPt) { + const double pt = histPoiN22->GetXaxis()->GetBinCenter(iPt); + denominator = gfwState.fGFW->Calculate(gfwState.corrPoiN22, iPt - 1, true).real(); + if (denominator > 0.0) { + const double correlation = gfwState.fGFW->Calculate(gfwState.corrPoiN22, iPt - 1, false).real() / denominator; + if (std::abs(correlation) < 1.0) { + histos.fill(HIST("psi2/h_PoiN03Gap22"), pt, centrality, correlation, denominator); + if (fillEse && cfgEseHarmonic.value == SecondHarmonic) { + gfwState.histPoiNQn->Fill(pt, centrality, eseGroupIndex + 0.5, correlation, denominator); + } + } + } + denominator = gfwState.fGFW->Calculate(gfwState.corrPoiP22, iPt - 1, true).real(); + if (denominator > 0.0) { + const double correlation = gfwState.fGFW->Calculate(gfwState.corrPoiP22, iPt - 1, false).real() / denominator; + if (std::abs(correlation) < 1.0) { + histos.fill(HIST("psi2/h_PoiP03Gap22"), pt, centrality, correlation, denominator); + if (fillEse && cfgEseHarmonic.value == SecondHarmonic) { + gfwState.histPoiPQn->Fill(pt, centrality, eseGroupIndex + 0.5, correlation, denominator); + } + } + } + denominator = gfwState.fGFW->Calculate(gfwState.corrPoiN32, iPt - 1, true).real(); + if (denominator > 0.0) { + const double correlation = gfwState.fGFW->Calculate(gfwState.corrPoiN32, iPt - 1, false).real() / denominator; + if (std::abs(correlation) < 1.0) { + histos.fill(HIST("psi3/h_PoiN03Gap32"), pt, centrality, correlation, denominator); + if (fillEse && cfgEseHarmonic.value == ThirdHarmonic) { + gfwState.histPoiNQn->Fill(pt, centrality, eseGroupIndex + 0.5, correlation, denominator); + } + } + } + denominator = gfwState.fGFW->Calculate(gfwState.corrPoiP32, iPt - 1, true).real(); + if (denominator > 0.0) { + const double correlation = gfwState.fGFW->Calculate(gfwState.corrPoiP32, iPt - 1, false).real() / denominator; + if (std::abs(correlation) < 1.0) { + histos.fill(HIST("psi3/h_PoiP03Gap32"), pt, centrality, correlation, denominator); + if (fillEse && cfgEseHarmonic.value == ThirdHarmonic) { + gfwState.histPoiPQn->Fill(pt, centrality, eseGroupIndex + 0.5, correlation, denominator); + } + } + } } } - histos.fill(HIST("histQvecCent"), std::sqrt(collision.qvecFT0CReVec()[0] * collision.qvecFT0CReVec()[0] + collision.qvecFT0CImVec()[0] * collision.qvecFT0CImVec()[0]) * std::sqrt(collision.sumAmplFT0C()), centrality); - histos.fill(HIST("histQvecV2"), collision.qvecFT0CReVec()[0], collision.qvecFT0CImVec()[0], collision.centFT0C()); - histos.fill(HIST("histMult_Cent"), collision.sumAmplFT0C(), collision.centFT0C()); - histos.fill(HIST("histVertex"), collision.posX(), collision.posY(), collision.posZ(), collision.centFT0C()); + if (!fillRegular && !cfgFullCheck) { + return; + } + if (fillRegular) { + histos.fill(HIST("histVertex"), collision.posX(), collision.posY(), collision.posZ(), collision.centFT0C()); + } + + THnSparse* histEseLambdaCos = nullptr; + THnSparse* histEseAntiLambdaCos = nullptr; + THnSparse* histEseLambdaCos2 = nullptr; + THnSparse* histEseAntiLambdaCos2 = nullptr; + THnSparse* histEseLambdaCosSin = nullptr; + THnSparse* histEseAntiLambdaCosSin = nullptr; + if (fillEse && cfgFullCheck) { + const auto suffix = eseGroupSuffix(eseGroupIndex); + histEseLambdaCos = getEseHistogram(Form("psi%d/h_lambda_cos_%s", cfgEseHarmonic.value, suffix.c_str())); + histEseAntiLambdaCos = getEseHistogram(Form("psi%d/h_alambda_cos_%s", cfgEseHarmonic.value, suffix.c_str())); + histEseLambdaCos2 = getEseHistogram(Form("psi%d/h_lambda_cos2_%s", cfgEseHarmonic.value, suffix.c_str())); + histEseAntiLambdaCos2 = getEseHistogram(Form("psi%d/h_alambda_cos2_%s", cfgEseHarmonic.value, suffix.c_str())); + histEseLambdaCosSin = getEseHistogram(Form("psi%d/h_lambda_cossin_%s", cfgEseHarmonic.value, suffix.c_str())); + histEseAntiLambdaCosSin = getEseHistogram(Form("psi%d/h_alambda_cossin_%s", cfgEseHarmonic.value, suffix.c_str())); + } for (const auto& v0 : V0s) { auto postrack = v0.template posTrack_as(); @@ -735,7 +1081,7 @@ struct FlowEseTask { double nTPCSigmaNegPr = negtrack.tpcNSigmaPr(); double nTPCSigmaPosPi = postrack.tpcNSigmaPi(); - if (cfgQAv0 && nmode == kCorrLevel[0]) { + if (fillRegular && cfgQAv0 && nmode == CorrLevel[0]) { histos.fill(HIST("QA/nsigma_tpc_pt_ppr"), postrack.pt(), nTPCSigmaPosPr); histos.fill(HIST("QA/nsigma_tpc_pt_ppi"), postrack.pt(), nTPCSigmaPosPi); @@ -753,27 +1099,21 @@ struct FlowEseTask { aLambdaTag = 1; } - if (lambdaTag == aLambdaTag) + if (lambdaTag == aLambdaTag) { continue; + } - if (!selectionV0(collision, v0, lambdaTag)) + if (!selectionV0(collision, v0, lambdaTag)) { continue; + } if (lambdaTag) { protonVec = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), massPr); pionVec = ROOT::Math::PxPyPzMVector(v0.pxneg(), v0.pyneg(), v0.pzneg(), massPi); - histos.fill(HIST("histV2_lambda"), collision.centFT0C(), v0.pt(), - std::cos(static_cast(nmode) * (v0.phi() - helperEP.GetEventPlane(collision.qvecFT0CReVec()[0], collision.qvecFT0CImVec()[0], nmode))), - std::sqrt(collision.qvecFT0CReVec()[0] * collision.qvecFT0CReVec()[0] + collision.qvecFT0CImVec()[0] * collision.qvecFT0CImVec()[0]) * std::sqrt(collision.sumAmplFT0C()), - v0.mLambda()); } if (aLambdaTag) { protonVec = ROOT::Math::PxPyPzMVector(v0.pxneg(), v0.pyneg(), v0.pzneg(), massPr); pionVec = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), massPi); - histos.fill(HIST("histV2_alambda"), collision.centFT0C(), v0.pt(), - std::cos(static_cast(nmode) * (v0.phi() - helperEP.GetEventPlane(collision.qvecFT0CReVec()[0], collision.qvecFT0CImVec()[0], nmode))), - std::sqrt(collision.qvecFT0CReVec()[0] * collision.qvecFT0CReVec()[0] + collision.qvecFT0CImVec()[0] * collision.qvecFT0CImVec()[0]) * std::sqrt(collision.sumAmplFT0C()), - v0.mAntiLambda()); } LambdaVec = protonVec + pionVec; LambdaVec.SetM(massLambda); @@ -784,6 +1124,8 @@ struct FlowEseTask { angle = protonBoostedVec.Pz() / protonBoostedVec.P(); psi = safeATan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode); relphi = TVector2::Phi_0_2pi(static_cast(nmode) * (LambdaVec.Phi() - psi)); + productPhi = std::sin(static_cast(nmode) * LambdaVec.Phi()) * collision.qvecRe()[qvecDetInd] - + std::cos(static_cast(nmode) * LambdaVec.Phi()) * collision.qvecIm()[qvecDetInd]; if (cfgShiftCorr) { auto deltapsiFT0C = 0.0; @@ -793,7 +1135,7 @@ struct FlowEseTask { auto psidefFT0C = std::atan2(collision.qvecIm()[qvecDetInd], collision.qvecRe()[qvecDetInd]) / static_cast(nmode); auto psidefFT0A = std::atan2(collision.qvecIm()[qvecRefAInd], collision.qvecRe()[qvecRefAInd]) / static_cast(nmode); auto psidefFV0A = std::atan2(collision.qvecIm()[qvecRefBInd], collision.qvecRe()[qvecRefBInd]) / static_cast(nmode); - for (int ishift = 1; ishift <= kShiftLevel; ishift++) { + for (int ishift = 1; ishift <= ShiftLevel; ishift++) { auto coeffshiftxFT0C = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(centrality, 0.5, ishift - 0.5)); auto coeffshiftyFT0C = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(centrality, 1.5, ishift - 0.5)); auto coeffshiftxFT0A = shiftprofile.at(nmode - 2)->GetBinContent(shiftprofile.at(nmode - 2)->FindBin(centrality, 2.5, ishift - 0.5)); @@ -813,43 +1155,59 @@ struct FlowEseTask { continue; } - if (lambdaTag) { - histos.fill(HIST("QA/ptspec_l"), v0.mLambda(), v0.pt(), centrality); - if (cfgEffCor) { - histos.fill(HIST("QA/ptspecCor_l"), v0.mLambda(), v0.pt(), centrality, - 1.0 / effMap->GetBinContent(effMap->GetXaxis()->FindBin(v0.pt()), effMap->GetYaxis()->FindBin(centrality))); + if (fillRegular) { + if (lambdaTag) { + histos.fill(HIST("QA/ptspec_l"), v0.mLambda(), v0.pt(), centrality); + if (cfgEffCor) { + histos.fill(HIST("QA/ptspecCor_l"), v0.mLambda(), v0.pt(), centrality, + 1.0 / effMap->GetBinContent(effMap->GetXaxis()->FindBin(v0.pt()), effMap->GetYaxis()->FindBin(centrality))); + } } - } - if (aLambdaTag) { - histos.fill(HIST("QA/ptspec_al"), v0.mAntiLambda(), v0.pt(), centrality); - if (cfgEffCor) { - histos.fill(HIST("QA/ptspecCor_al"), v0.mAntiLambda(), v0.pt(), centrality, - 1.0 / effMap->GetBinContent(effMap->GetXaxis()->FindBin(v0.pt()), effMap->GetYaxis()->FindBin(centrality))); + if (aLambdaTag) { + histos.fill(HIST("QA/ptspec_al"), v0.mAntiLambda(), v0.pt(), centrality); + if (cfgEffCor) { + histos.fill(HIST("QA/ptspecCor_al"), v0.mAntiLambda(), v0.pt(), centrality, + 1.0 / effMap->GetBinContent(effMap->GetXaxis()->FindBin(v0.pt()), effMap->GetYaxis()->FindBin(centrality))); + } } } double weight = 1.0; weight *= cfgEffCor ? 1.0 / effMap->GetBinContent(effMap->GetXaxis()->FindBin(v0.pt()), effMap->GetYaxis()->FindBin(centrality)) : 1.; weight *= cfgAccCor ? 1.0 / accMap->GetBinContent(accMap->GetXaxis()->FindBin(v0.pt()), accMap->GetYaxis()->FindBin(v0.yLambda())) : 1.; + if (fillEse && cfgFullCheck) { + const double mass = lambdaTag ? v0.mLambda() : v0.mAntiLambda(); + const std::array cosValues = {mass, v0.pt(), angle * weight, centrality}; + const std::array cos2Values = {mass, v0.pt(), angle * angle, centrality}; + const std::array cosSinValues = {mass, v0.pt(), angle * std::sin(relphi) * weight, centrality}; + if (lambdaTag) { + histEseLambdaCos->Fill(cosValues.data()); + histEseLambdaCos2->Fill(cos2Values.data()); + histEseLambdaCosSin->Fill(cosSinValues.data()); + } else { + histEseAntiLambdaCos->Fill(cosValues.data()); + histEseAntiLambdaCos2->Fill(cos2Values.data()); + histEseAntiLambdaCosSin->Fill(cosSinValues.data()); + } + } + if (!fillRegular) { + continue; + } + double qvecMag = 1.0; - if (cfgUSESP) + if (cfgUSESP) { qvecMag *= std::sqrt(std::pow(collision.qvecIm()[3 + (nmode - 2) * 28], 2) + std::pow(collision.qvecRe()[3 + (nmode - 2) * 28], 2)); + } - if (nmode == kCorrLevel[0]) { //////////// - double q2 = std::sqrt(collision.qvecFT0CReVec()[0] * collision.qvecFT0CReVec()[0] + collision.qvecFT0CImVec()[0] * collision.qvecFT0CImVec()[0]) * std::sqrt(collision.sumAmplFT0C()); + if (nmode == CorrLevel[0] && cfgFullCheck) { //////////// if (lambdaTag) { histos.fill(HIST("psi2/h_lambda_cos"), v0.mLambda(), v0.pt(), angle * weight, centrality, relphi); histos.fill(HIST("psi2/h_lambda_cos2"), v0.mLambda(), v0.pt(), angle * angle, centrality, relphi); histos.fill(HIST("psi2/h_lambda_cossin"), v0.mLambda(), v0.pt(), angle * std::sin(relphi) * weight, centrality); + histos.fill(HIST("psi2/h_lambda_cossin_SP"), v0.mLambda(), v0.pt(), angle * productPhi * weight, centrality); histos.fill(HIST("psi2/h_lambda_vncos"), v0.mLambda(), v0.pt(), qvecMag * std::cos(relphi) * weight, centrality); histos.fill(HIST("psi2/h_lambda_vnsin"), v0.mLambda(), v0.pt(), std::sin(relphi), centrality); - histos.fill(HIST("psi2/h_lambda_cos_q2"), v0.mLambda(), v0.pt(), angle * weight, centrality, q2); - histos.fill(HIST("psi2/h_lambda_cos2_q2"), v0.mLambda(), v0.pt(), angle * angle, centrality, q2); - histos.fill(HIST("psi2/h_lambda_cossin_q2"), v0.mLambda(), v0.pt(), angle * std::sin(relphi) * weight, centrality, q2); - histos.fill(HIST("psi2/h_lambda_vncos_q2"), v0.mLambda(), v0.pt(), qvecMag * std::cos(relphi) * weight, centrality, q2); - histos.fill(HIST("psi2/h_lambda_vnsin_q2"), v0.mLambda(), v0.pt(), std::sin(relphi), centrality, q2); - if (cfgRapidityDep) { histos.fill(HIST("psi2/h_lambda_cos2_rap"), v0.mLambda(), v0.pt(), angle * angle, centrality, v0.yLambda(), weight); } @@ -896,15 +1254,10 @@ struct FlowEseTask { histos.fill(HIST("psi2/h_alambda_cos"), v0.mAntiLambda(), v0.pt(), angle * weight, centrality, relphi); histos.fill(HIST("psi2/h_alambda_cos2"), v0.mAntiLambda(), v0.pt(), angle * angle, centrality, relphi); histos.fill(HIST("psi2/h_alambda_cossin"), v0.mAntiLambda(), v0.pt(), angle * std::sin(relphi) * weight, centrality); + histos.fill(HIST("psi2/h_alambda_cossin_SP"), v0.mAntiLambda(), v0.pt(), angle * productPhi * weight, centrality); histos.fill(HIST("psi2/h_alambda_vncos"), v0.mAntiLambda(), v0.pt(), qvecMag * std::cos(relphi) * weight, centrality); histos.fill(HIST("psi2/h_alambda_vnsin"), v0.mAntiLambda(), v0.pt(), std::sin(relphi), centrality); - histos.fill(HIST("psi2/h_alambda_cos_q2"), v0.mAntiLambda(), v0.pt(), angle * weight, centrality, q2); - histos.fill(HIST("psi2/h_alambda_cos2_q2"), v0.mAntiLambda(), v0.pt(), angle * angle, centrality, q2); - histos.fill(HIST("psi2/h_alambda_cossin_q2"), v0.mAntiLambda(), v0.pt(), angle * std::sin(relphi) * weight, centrality, q2); - histos.fill(HIST("psi2/h_alambda_vncos_q2"), v0.mAntiLambda(), v0.pt(), qvecMag * std::cos(relphi) * weight, centrality, q2); - histos.fill(HIST("psi2/h_alambda_vnsin_q2"), v0.mAntiLambda(), v0.pt(), std::sin(relphi), centrality, q2); - if (cfgRapidityDep) { histos.fill(HIST("psi2/h_alambda_cos2_rap"), v0.mAntiLambda(), v0.pt(), angle * angle, centrality, v0.yLambda(), weight); } @@ -947,7 +1300,7 @@ struct FlowEseTask { histos.fill(HIST("psi2/QA/sinPhi_al"), v0.mAntiLambda(), v0.pt(), std::sin(v0.phi() * 2.0), centrality); } } - } else if (nmode == kCorrLevel[1]) { + } else if (nmode == CorrLevel[1]) { if (lambdaTag) { histos.fill(HIST("psi3/h_lambda_cos"), v0.mLambda(), v0.pt(), angle * weight, centrality, relphi); histos.fill(HIST("psi3/h_lambda_cos2"), v0.mLambda(), v0.pt(), angle * angle, centrality, relphi); @@ -978,7 +1331,7 @@ struct FlowEseTask { histos.fill(HIST("psi3/h_alambda_coscos"), v0.mAntiLambda(), v0.pt(), angle * std::cos(relphi), centrality, weight); } } - } else if (nmode == kCorrLevel[2]) { + } else if (nmode == CorrLevel[2]) { if (lambdaTag) { histos.fill(HIST("psi4/h_lambda_cos"), v0.mLambda(), v0.pt(), angle * weight, centrality, relphi); histos.fill(HIST("psi4/h_lambda_cos2"), v0.mLambda(), v0.pt(), angle * angle, centrality, relphi); @@ -1017,9 +1370,9 @@ struct FlowEseTask { TrackCandidates const& tracks, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) { - if (cfgCentEst == kCorrLevel[3]) { + if (cfgCentEst == CorrLevel[3]) { centrality = collision.centFT0C(); - } else if (cfgCentEst == kCorrLevel[0]) { + } else if (cfgCentEst == CorrLevel[0]) { centrality = collision.centFT0M(); } if (!eventSelected(collision)) { @@ -1027,13 +1380,20 @@ struct FlowEseTask { } histos.fill(HIST("QA/CentDist"), centrality, 1.0); histos.fill(HIST("QA/PVzDist"), collision.posZ(), 1.0); + const double eseQ = getEseQ(collision); + histos.fill(HIST("histQvecCent"), eseQ, centrality); + const int eseGroupIndex = eseGroup(centrality, eseQ); + if (eseGroupIndex >= 0) { + histEventCountEseGroup->Fill(centrality, eseGroupIndex + 0.5); + } if (cfgShiftCorr) { auto bc = collision.bc_as(); currentRunNumber = bc.runNumber(); if (currentRunNumber != lastRunNumber) { shiftprofile.clear(); - for (int i = 2; i < cfgnMods + 2; i++) { + const int maxShiftHarmonic = std::max(cfgnMods.value + 1, cfgEseHarmonic.value); + for (int i = 2; i <= maxShiftHarmonic; i++) { fullCCDBShiftCorrPath = cfgShiftPath; fullCCDBShiftCorrPath += "/v"; fullCCDBShiftCorrPath += std::to_string(i); @@ -1050,6 +1410,29 @@ struct FlowEseTask { if (cfgAccCor) { accMap = ccdb->getForTimeStamp(cfgAccCorPath.value, bc.timestamp()); } + if (bc.runNumber() != gfwState.lastGfwCorrectionRunNumber) { + if (cfgGfwParam.cfgGfwEffCor && !cfgGfwParam.cfgGfwEffCorPath.value.empty()) { + gfwState.gfwEffMap = ccdb->getForTimeStamp(cfgGfwParam.cfgGfwEffCorPath.value, bc.timestamp()); + if (gfwState.gfwEffMap == nullptr) { + LOGF(fatal, "Could not load charged-track efficiency histogram from %s", cfgGfwParam.cfgGfwEffCorPath.value.c_str()); + } + } + if (cfgGfwParam.cfgGfwAccCor && !cfgGfwParam.cfgGfwAccCorPath.value.empty()) { + gfwState.gfwAccWeights = ccdb->getForTimeStamp(cfgGfwParam.cfgGfwAccCorPath.value, bc.timestamp()); + if (gfwState.gfwAccWeights == nullptr) { + LOGF(fatal, "Could not load charged-track acceptance weights from %s", cfgGfwParam.cfgGfwAccCorPath.value.c_str()); + } + } + gfwState.lastGfwCorrectionRunNumber = bc.runNumber(); + } + + fillEseEPQA(collision, eseGroupIndex); + if (cfgShiftCorrDef && cfgEseHarmonic.value >= cfgnMods.value + 2) { + fillShiftCorrection(collision, cfgEseHarmonic.value); + } + if (eseGroupIndex >= 0 && cfgEseHarmonic.value >= cfgnMods.value + 2) { + fillHistograms(collision, V0s, tracks, cfgEseHarmonic.value, eseGroupIndex, false, true); + } for (int i = 2; i < cfgnMods + 2; i++) { if (cfgShiftCorrDef) { fillShiftCorrection(collision, i); @@ -1057,7 +1440,8 @@ struct FlowEseTask { if (cfgQAv0) { fillEPQA(collision, i); } - fillHistograms(collision, V0s, tracks, i); + const bool fillGfw = eseGroupIndex >= 0 ? i == cfgEseHarmonic.value : i == SecondHarmonic; + fillHistograms(collision, V0s, tracks, i, i == cfgEseHarmonic.value ? eseGroupIndex : -1, true, fillGfw); } // FIXME: need to fill different histograms for different harmonic } PROCESS_SWITCH(FlowEseTask, processData, "Process Event for data", true); @@ -1068,37 +1452,37 @@ struct FlowEseTask { float imp = mcCollision.impactParameter(); float evPhi = mcCollision.eventPlaneAngle() / 2.0; float centclass = -999; - if (imp >= kCentBoundaries[0] && imp < kCentBoundaries[1]) { - centclass = kCentValues[0]; + if (imp >= CentBoundaries[0] && imp < CentBoundaries[1]) { + centclass = CentValues[0]; } - if (imp >= kCentBoundaries[1] && imp < kCentBoundaries[2]) { - centclass = kCentValues[1]; + if (imp >= CentBoundaries[1] && imp < CentBoundaries[2]) { + centclass = CentValues[1]; } - if (imp >= kCentBoundaries[2] && imp < kCentBoundaries[3]) { - centclass = kCentValues[2]; + if (imp >= CentBoundaries[2] && imp < CentBoundaries[3]) { + centclass = CentValues[2]; } - if (imp >= kCentBoundaries[3] && imp < kCentBoundaries[4]) { - centclass = kCentValues[3]; + if (imp >= CentBoundaries[3] && imp < CentBoundaries[4]) { + centclass = CentValues[3]; } - if (imp >= kCentBoundaries[4] && imp < kCentBoundaries[5]) { - centclass = kCentValues[4]; + if (imp >= CentBoundaries[4] && imp < CentBoundaries[5]) { + centclass = CentValues[4]; } - if (imp >= kCentBoundaries[5] && imp < kCentBoundaries[6]) { - centclass = kCentValues[5]; + if (imp >= CentBoundaries[5] && imp < CentBoundaries[6]) { + centclass = CentValues[5]; } - if (imp >= kCentBoundaries[6] && imp < kCentBoundaries[7]) { - centclass = kCentValues[6]; + if (imp >= CentBoundaries[6] && imp < CentBoundaries[7]) { + centclass = CentValues[6]; } - if (imp >= kCentBoundaries[7] && imp < kCentBoundaries[8]) { - centclass = kCentValues[7]; + if (imp >= CentBoundaries[7] && imp < CentBoundaries[8]) { + centclass = CentValues[7]; } - if (imp >= kCentBoundaries[8] && imp < kCentBoundaries[9]) { - centclass = kCentValues[8]; + if (imp >= CentBoundaries[8] && imp < CentBoundaries[9]) { + centclass = CentValues[8]; } int nCh = 0; - if (centclass > 0 && centclass < kCentUpperLimit) { + if (centclass > 0 && centclass < CentUpperLimit) { // event within range histos.fill(HIST("hImpactParameter"), imp); histos.fill(HIST("hEventPlaneAngle"), evPhi); @@ -1106,12 +1490,15 @@ struct FlowEseTask { float deltaPhi = mcParticle.phi() - mcCollision.eventPlaneAngle(); // focus on bulk: e, mu, pi, k, p int pdgCode = std::abs(mcParticle.pdgCode()); - if (pdgCode != kLambdaId) + if (pdgCode != LambdaId) { continue; - if (!mcParticle.isPhysicalPrimary()) + } + if (!mcParticle.isPhysicalPrimary()) { continue; - if (std::abs(mcParticle.eta()) > kEtaAcceptance) // main acceptance + } + if (std::abs(mcParticle.eta()) > EtaAcceptance) { // main acceptance continue; + } histos.fill(HIST("hSparseMCGenWeight"), centclass, RecoDecay::constrainAngle(deltaPhi, 0, 2), std::pow(std::cos(2.0 * RecoDecay::constrainAngle(deltaPhi, 0, 2)), 2.0), mcParticle.pt(), mcParticle.eta()); nCh++; bool validGlobal = false; diff --git a/PWGCF/Flow/Tasks/flowEventPlane.cxx b/PWGCF/Flow/Tasks/flowEventPlane.cxx index b0c97685508..1605f2a0836 100644 --- a/PWGCF/Flow/Tasks/flowEventPlane.cxx +++ b/PWGCF/Flow/Tasks/flowEventPlane.cxx @@ -12,13 +12,13 @@ /// \file flowEventPlane.cxx /// \brief Flow calculation using event plane. /// \author Yash Patley +/// Samta Arora #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" -#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -47,7 +47,9 @@ #include #include +#include #include +#include #include #include #include @@ -61,6 +63,31 @@ using namespace o2::constants::math; namespace o2::aod { +namespace colspcalib +{ +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); +DECLARE_SOA_COLUMN(Cent, cent, float); +DECLARE_SOA_COLUMN(Vx, vx, float); +DECLARE_SOA_COLUMN(Vy, vy, float); +DECLARE_SOA_COLUMN(Vz, vz, float); +DECLARE_SOA_COLUMN(ZnaEnergyCommon, znaEnergyCommon, float); +DECLARE_SOA_COLUMN(ZncEnergyCommon, zncEnergyCommon, float); +DECLARE_SOA_COLUMN(ZnaEnergy, znaEnergy, std::vector); +DECLARE_SOA_COLUMN(ZncEnergy, zncEnergy, std::vector); +} // namespace colspcalib +DECLARE_SOA_TABLE(ColSpCalib, "AOD", "COLSPCALIB", o2::soa::Index<>, + colspcalib::RunNumber, + colspcalib::Timestamp, + colspcalib::Cent, + colspcalib::Vx, + colspcalib::Vy, + colspcalib::Vz, + colspcalib::ZnaEnergyCommon, + colspcalib::ZncEnergyCommon, + colspcalib::ZnaEnergy, + colspcalib::ZncEnergy); + namespace colspext { DECLARE_SOA_COLUMN(SelColFlag, selColFlag, bool); @@ -69,7 +96,7 @@ DECLARE_SOA_COLUMN(Ya, ya, float); DECLARE_SOA_COLUMN(Xc, xc, float); DECLARE_SOA_COLUMN(Yc, yc, float); } // namespace colspext -DECLARE_SOA_TABLE(ColSPExt, "AOD", "COLSPEXT", o2::soa::Index<>, +DECLARE_SOA_TABLE(ColSPExt, "AOD", "COLSPSEXT", o2::soa::Index<>, colspext::SelColFlag, colspext::Xa, colspext::Ya, @@ -124,17 +151,158 @@ enum ParticleType { kNPart }; -enum ResoType { - kPhi0 = 0, - kKStar -}; - enum V0Type { kK0S = 0, kLambda, kAntiLambda }; +struct SpCalibTableProducer { + // Table producer + Produces colSpCalibTable; + + // Configurables + // Collisions + Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ cut"}; + Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ cut"}; + Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; + Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cCentBins{"cCentBins", 100, "Number of bins in centrality axis"}; + Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; + Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; + Configurable cMinOccupancy{"cMinOccupancy", 0, "Minimum FT0C Occupancy"}; + Configurable cMaxOccupancy{"cMaxOccupancy", 1e6, "Maximum FT0C Occupancy"}; + Configurable capplyTdcCut{"capplyTdcCut", false, "Flag for TDC cut"}; + Configurable ctdcZnMin{"ctdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable ctdcZnMax{"ctdcZnMax", 2.5, "Max ZN TDC cut"}; + + // Histogram Registry. + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Run number + uint64_t runNum = 0, timestamp = 0; + float mult = 0., cent = 0., posX = 0., posY = 0., posZ = 0.; + + void init(InitContext const&) + { + // Histogram collision + histos.add("hCent", "Centrality", kTH1F, {{cCentBins, 0., 100., "FT0C%"}}); + histos.add("hVz", "V_{z}", kTH1F, {{100, -10., 10., "V_{z}(cm)"}}); + } + + template + bool selCollision(C const& col) + { + if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { // VtxZ selection + return false; + } + + if (cSel8Trig && !col.sel8()) { // Sel8 selection + return false; + } + + cent = col.centFT0C(); + if (cent <= cMinCent || cent >= cMaxCent) { // Centrality selection + return false; + } + + if (col.ft0cOccupancyInTimeRange() < cMinOccupancy || col.ft0cOccupancyInTimeRange() > cMaxOccupancy) { // Occupancy cut + return false; + } + + if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { // Pile-up rejection + return false; + } + + if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { // ZvtxFT0 vs PV + return false; + } + + if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { // All ITS layer active + return false; + } + + // Set Multiplicity + mult = col.multTPC(); + + return true; + } + + template + bool selZdcHit(C const& zdc) + { + if (capplyTdcCut) { // a narrow TDC window is set + if ((zdc.timeZNC() <= ctdcZnMin) || (zdc.timeZNC() >= ctdcZnMax)) { + return false; + } + if ((zdc.timeZNA() <= ctdcZnMin) || (zdc.timeZNA() >= ctdcZnMax)) { + return false; + } + } + return true; + } + + template + void analyzeCollision(B const& bc, C const& collision) + { + // Check zdc + if (!bc.has_zdc()) { + return; + } + + // Event selection + if (!selCollision(collision)) { + return; + } + posX = collision.posX(); + posY = collision.posY(); + posZ = collision.posZ(); + + auto zdc = bc.zdc(); + if (!selZdcHit(zdc)) { + return; + } + + std::vector znaEnergy, zncEnergy; + for (int i = 0; i < kXYAC; ++i) { + znaEnergy.push_back(zdc.energySectorZNA()[i]); + zncEnergy.push_back(zdc.energySectorZNC()[i]); + } + auto znaEnergyCommon = zdc.energyCommonZNA(); + auto zncEnergyCommon = zdc.energyCommonZNC(); + + // check energy deposits + if (znaEnergyCommon <= 0 || zncEnergyCommon <= 0 || znaEnergy[0] <= 0 || znaEnergy[1] <= 0 || znaEnergy[2] <= 0 || znaEnergy[3] <= 0 || zncEnergy[0] <= 0 || zncEnergy[1] <= 0 || zncEnergy[2] <= 0 || zncEnergy[3] <= 0) { + return; + } + + // Fill collision table + histos.fill(HIST("hCent"), cent); + histos.fill(HIST("hVz"), posZ); + colSpCalibTable(runNum, timestamp, cent, posX, posY, posZ, znaEnergyCommon, zncEnergyCommon, znaEnergy, zncEnergy); + } + + using BCsRun3 = soa::Join; + using CollisionsRun3 = soa::Join; + + void processDummy(CollisionsRun3::iterator const&) {} + + PROCESS_SWITCH(SpCalibTableProducer, processDummy, "Dummy process", true); + + void processFEP(CollisionsRun3::iterator const& collision, BCsRun3 const&, aod::Zdcs const&) + { + // Get bunch crossing + auto bc = collision.template foundBC_as(); + runNum = collision.template foundBC_as().runNumber(); + timestamp = collision.template foundBC_as().timestamp(); + analyzeCollision(bc, collision); + } + + PROCESS_SWITCH(SpCalibTableProducer, processFEP, "Flow Event Plane Table Producer", false); +}; + struct SpectatorPlaneTableProducer { // Table producer Produces colSPExtTable; @@ -146,45 +314,48 @@ struct SpectatorPlaneTableProducer { Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ cut"}; Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cCentBins{"cCentBins", 100, "Number of bins in centrality axis"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; - Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "Trigger Time and Vertex Selection"}; - Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; - Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; - Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; - Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; Configurable cMinOccupancy{"cMinOccupancy", 0, "Minimum FT0C Occupancy"}; Configurable cMaxOccupancy{"cMaxOccupancy", 1e6, "Maximum FT0C Occupancy"}; + Configurable capplyTdcCut{"capplyTdcCut", false, "Flag for TDC cut"}; + Configurable ctdcZnMin{"ctdcZnMin", -2.5, "Min ZN TDC cut"}; + Configurable ctdcZnMax{"ctdcZnMax", 2.5, "Max ZN TDC cut"}; - // Gain calibration - Configurable cDoGainCalib{"cDoGainCalib", false, "Gain Calib Flag"}; - Configurable cUseAlphaZDC{"cUseAlphaZDC", true, "Use Alpha ZDC"}; + ConfigurableAxis axisVarCent{"axisVarCent", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100}, "Variable width centrality bins"}; // Coarse binning factor - Configurable cAxisCBF{"cAxisCBF", 5, "Coarse Bin Factor"}; + Configurable cAxisCBF{"cAxisCBF", 10, "Coarse Bin Factor"}; // Cent Vx Vy Vz Bins - Configurable cAxisCentBins{"cAxisCentBins", 20, "NBins Centrality"}; - Configurable cAxisVxyBins{"cAxisVxyBins", 20, "NBins Vx Vy"}; - Configurable cAxisVzBins{"cAxisVzBins", 20, "NBins Vz"}; - Configurable cAxisVxMin{"cAxisVxMin", -0.06, "Vx Min"}; - Configurable cAxisVxMax{"cAxisVxMax", -0.02, "Vx Max"}; + Configurable cAxisCentBins{"cAxisCentBins", 50, "NBins Centrality"}; + Configurable cAxisVxyBins{"cAxisVxyBins", 50, "NBins Vx Vy"}; + Configurable cAxisVzBins{"cAxisVzBins", 50, "NBins Vz"}; + Configurable cAxisVxMin{"cAxisVxMin", -0.01, "Vx Min"}; + Configurable cAxisVxMax{"cAxisVxMax", 0.01, "Vx Max"}; Configurable cAxisVyMin{"cAxisVyMin", -0.01, "Vy Min"}; - Configurable cAxisVyMax{"cAxisVyMax", 0.006, "Vy Max"}; + Configurable cAxisVyMax{"cAxisVyMax", 0.01, "Vy Max"}; + Configurable cRecentVxVy{"cRecentVxVy", true, "Vx / Vy recentering"}; // Corrections - Configurable cApplyRecentCorr{"cApplyRecentCorr", false, "Apply recentering"}; - Configurable> cCorrFlagVector{"cCorrFlagVector", {0, 0, 0, 0, 0, 0}, "Correction Flag"}; + + Configurable cLoadCorrection{"cLoadCorrection", true, "Load corrections"}; + Configurable cDoGainCalib{"cDoGainCalib", true, "Gain Calib Flag"}; + Configurable cUseAlphaZDC{"cUseAlphaZDC", true, "Use Alpha ZDC"}; + Configurable cApplyRecentCorr{"cApplyRecentCorr", true, "Apply recentering"}; + Configurable> cCorrFlagVector{"cCorrFlagVector", {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, "Correction Flag"}; // CCDB Configurable cCcdbUrl{"cCcdbUrl", "http://ccdb-test.cern.ch:8080", "url of ccdb"}; Configurable cCcdbPath{"cCcdbPath", "Users/y/ypatley/DFOO", "Path for ccdb-object"}; + Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; // Tracks Configurable cTrackMinPt{"cTrackMinPt", 0.1, "p_{T} minimum"}; Configurable cTrackMaxPt{"cTrackMaxPt", 10.0, "p_{T} maximum"}; - Configurable cNEtaBins{"cNEtaBins", 7, "# of eta bins"}; Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; Configurable cTrackGlobal{"cTrackGlobal", true, "Global Track"}; Configurable cTrackDcaXYCut{"cTrackDcaXYCut", 0.1, "DcaXY Cut"}; @@ -202,13 +373,13 @@ struct SpectatorPlaneTableProducer { Configurable cProtonPtCut{"cProtonPtCut", 1.1, "Proton TPC pT cutoff"}; // Initialize CCDB Service - Service ccdbService; + Service ccdbService{}; // Histogram registry: an object to hold your histograms HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Global objects - const float zdcDenThrs = 1e-4; + float zdcDenThrs = 1e-4; float cent = 0., mult = 0.; float posX = 0., posY = 0., posZ = 0.; std::vector> vCoarseCorrHistNames = { @@ -225,9 +396,11 @@ struct SpectatorPlaneTableProducer { // Container for histograms struct CorrectionHistContainer { - std::array hGainCalib; - std::array, 4>, 6> vCoarseCorrHist; - std::array, 4>, 6> vFineCorrHist; + TProfile* hVx = nullptr; + TProfile* hVy = nullptr; + std::array hGainCalib{}; + std::array, 4>, 14> vCoarseCorrHist{}; + std::array, 4>, 14> vFineCorrHist{}; } CorrectionHistContainer; // Run number @@ -238,11 +411,13 @@ struct SpectatorPlaneTableProducer { // Set CCDB url ccdbService->setURL(cCcdbUrl.value); ccdbService->setCaching(true); + ccdbService->setLocalObjectValidityChecking(); + ccdbService->setCreatedNotAfter(nolaterthan.value); // Define axes const AxisSpec axisZDCEnergy{500, 0, 500, "ZD[AC] Signal"}; - const AxisSpec axisCent{100, 0., 100, "FT0C%"}; + const AxisSpec axisCent{cCentBins, 0., 100, "FT0C%"}; const AxisSpec axisVx{cAxisVxyBins, cAxisVxMin, cAxisVxMax, "V_{X}(cm)"}; const AxisSpec axisVy{cAxisVxyBins, cAxisVyMin, cAxisVyMax, "V_{Y}(cm)"}; const AxisSpec axisVz{cAxisVzBins, cMinZVtx, cMaxZVtx, "V_{Z}(cm)"}; @@ -271,6 +446,10 @@ struct SpectatorPlaneTableProducer { histos.add("Event/hVy", "V_{y}", kTH1F, {axisVy}); histos.add("Event/hVz", "V_{z}", kTH1F, {axisVz}); + // Vx / Vy Recent + histos.add("Event/hVxVsCent", " Vs Cent", kTProfile, {axisCent}); + histos.add("Event/hVyVsCent", " Vs Cent", kTProfile, {axisCent}); + // Gain calib histos.add("QA/GainCalib/hZNASignal", "ZNA Signal", kTH2F, {{4, 0, 4}, {axisZDCEnergy}}); histos.add("QA/GainCalib/hZNCSignal", "ZNC Signal", kTH2F, {{4, 0, 4}, {axisZDCEnergy}}); @@ -317,8 +496,10 @@ struct SpectatorPlaneTableProducer { // Directed flow QXY vector histos.add("DF/hQaQc", "X^{A}_{1}X^{C}_{1} + Y^{A}_{1}Y^{C}_{1}", kTProfile, {axisCent}); + histos.add("DF/hQaQcVarCent", "X^{A}_{1}X^{C}_{1} + Y^{A}_{1}Y^{C}_{1}", kTProfile, {axisVarCent}); } + // Select collsion template bool selCollision(C const& col) { @@ -339,22 +520,6 @@ struct SpectatorPlaneTableProducer { return false; } - if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { // Time and Vertex trigger - return false; - } - - if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { // Time frame border - return false; - } - - if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { // ITS Readout frame border - return false; - } - - if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { // ITS+TPC Vertex - return false; - } - if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { // Pile-up rejection return false; } @@ -368,20 +533,42 @@ struct SpectatorPlaneTableProducer { } // Set Multiplicity - mult = col.multNTracksHasTPC(); + mult = col.multTPC(); + + return true; + } + template + bool selZdcHit(C const& zdc) + { + if (capplyTdcCut) { // a narrow TDC window is set + if ((zdc.timeZNC() <= ctdcZnMin) || (zdc.timeZNC() >= ctdcZnMax)) { + return false; + } + if ((zdc.timeZNA() <= ctdcZnMin) || (zdc.timeZNA() >= ctdcZnMax)) { + return false; + } + } return true; } // Load Gain Calibrations and ZDC Q-Vector Recentering Corrections void loadCorrections() { + // Load Vx / Vy recentering + if (cRecentVxVy) { + std::string ccdbPath = static_cast(cCcdbPath) + "/VxVyRecent" + "/Run" + std::to_string(cRunNum); + auto ccdbObj = ccdbService->getForTimeStamp(ccdbPath, nolaterthan.value); + CorrectionHistContainer.hVx = dynamic_cast(ccdbObj->FindObject("hVx")); + CorrectionHistContainer.hVy = dynamic_cast(ccdbObj->FindObject("hVy")); + } + // Load ZDC gain calibration if (cDoGainCalib) { std::string ccdbPath = static_cast(cCcdbPath) + "/GainCalib" + "/Run" + std::to_string(cRunNum); - auto ccdbObj = ccdbService->getForTimeStamp(ccdbPath, -1); - CorrectionHistContainer.hGainCalib[0] = reinterpret_cast(ccdbObj->FindObject("hZNASignal")); - CorrectionHistContainer.hGainCalib[1] = reinterpret_cast(ccdbObj->FindObject("hZNCSignal")); + auto ccdbObj = ccdbService->getForTimeStamp(ccdbPath, nolaterthan.value); + CorrectionHistContainer.hGainCalib[0] = dynamic_cast(ccdbObj->FindObject("hZNASignal")); + CorrectionHistContainer.hGainCalib[1] = dynamic_cast(ccdbObj->FindObject("hZNCSignal")); } // Load shift corrections for ZDC Q-Vectors @@ -407,7 +594,7 @@ struct SpectatorPlaneTableProducer { std::string ccdbPath = static_cast(cCcdbPath) + "/CorrItr_" + std::to_string(i + 1) + "/Run" + std::to_string(cRunNum); // Get object from CCDB - auto ccdbObject = ccdbService->getForTimeStamp(ccdbPath, -1); + auto ccdbObject = ccdbService->getForTimeStamp(ccdbPath, nolaterthan.value); // Check CCDB Object if (!ccdbObject) { @@ -422,9 +609,9 @@ struct SpectatorPlaneTableProducer { int cntry = 0; for (auto const& y : x) { if (corrType == kFineCorr) { - CorrectionHistContainer.vFineCorrHist[i][cntrx][cntry] = reinterpret_cast(ccdbObject->FindObject(y.c_str())); + CorrectionHistContainer.vFineCorrHist[i][cntrx][cntry] = dynamic_cast(ccdbObject->FindObject(y.c_str())); } else { - CorrectionHistContainer.vCoarseCorrHist[i][cntrx][cntry] = reinterpret_cast(ccdbObject->FindObject(y.c_str())); + CorrectionHistContainer.vCoarseCorrHist[i][cntrx][cntry] = dynamic_cast(ccdbObject->FindObject(y.c_str())); } ++cntry; } @@ -439,8 +626,8 @@ struct SpectatorPlaneTableProducer { { float vA = 0., vC = 0.; for (int i = 0; i < static_cast(eA.size()); ++i) { - vA = CorrectionHistContainer.hGainCalib[0]->GetBinContent(CorrectionHistContainer.hGainCalib[0]->FindBin(i + 0.5, vz + 0.00001)); - vC = CorrectionHistContainer.hGainCalib[1]->GetBinContent(CorrectionHistContainer.hGainCalib[1]->FindBin(i + 0.5, vz + 0.00001)); + vA = CorrectionHistContainer.hGainCalib[0]->GetBinContent(CorrectionHistContainer.hGainCalib[0]->FindBin(i + 0.5, vz)); + vC = CorrectionHistContainer.hGainCalib[1]->GetBinContent(CorrectionHistContainer.hGainCalib[1]->FindBin(i + 0.5, vz)); eA[i] *= vA; eC[i] *= vC; } @@ -449,16 +636,16 @@ struct SpectatorPlaneTableProducer { std::vector getAvgCorrFactors(int const& itr, CorrectionType const& corrType, std::array const& vCollParam) { std::vector vAvgOutput = {0., 0., 0., 0.}; - int binarray[4]; + std::array binarray{}; if (corrType == kCoarseCorr) { int cntrx = 0; for (auto const& v : CorrectionHistContainer.vCoarseCorrHist[itr]) { for (auto const& h : v) { - binarray[kCent] = h->GetAxis(kCent)->FindBin(vCollParam[kCent] + 0.0001); - binarray[kVx] = h->GetAxis(kVx)->FindBin(vCollParam[kVx] + 0.0001); - binarray[kVy] = h->GetAxis(kVy)->FindBin(vCollParam[kVy] + 0.0001); - binarray[kVz] = h->GetAxis(kVz)->FindBin(vCollParam[kVz] + 0.0001); - vAvgOutput[cntrx] += h->GetBinContent(h->GetBin(binarray)); + binarray[kCent] = h->GetAxis(kCent)->FindBin(vCollParam[kCent]); + binarray[kVx] = h->GetAxis(kVx)->FindBin(vCollParam[kVx]); + binarray[kVy] = h->GetAxis(kVy)->FindBin(vCollParam[kVy]); + binarray[kVz] = h->GetAxis(kVz)->FindBin(vCollParam[kVz]); + vAvgOutput[cntrx] += h->GetBinContent(h->GetBin(binarray.data())); } ++cntrx; } @@ -467,7 +654,7 @@ struct SpectatorPlaneTableProducer { for (auto const& v : CorrectionHistContainer.vFineCorrHist[itr]) { int cntry = 0; for (auto const& h : v) { - vAvgOutput[cntrx] += h->GetBinContent(h->GetXaxis()->FindBin(vCollParam[cntry] + 0.0001)); + vAvgOutput[cntrx] += h->GetBinContent(h->GetXaxis()->FindBin(vCollParam[cntry])); ++cntry; } ++cntrx; @@ -477,7 +664,7 @@ struct SpectatorPlaneTableProducer { return vAvgOutput; } - void applyCorrection(std::array const& inputParam, std::array& outputParam) + void applyCorrection(const std::array& inputParam, std::array& outputParam) { std::vector vCorrFlags = static_cast>(cCorrFlagVector); int nitr = vCorrFlags.size(); @@ -500,7 +687,7 @@ struct SpectatorPlaneTableProducer { // Get averages std::vector vAvg = getAvgCorrFactors(i, corrType, inputParam); - // Apply correction + // Apply recentering outputParam[kXa] -= vAvg[kXa]; outputParam[kYa] -= vAvg[kYa]; outputParam[kXc] -= vAvg[kXc]; @@ -539,6 +726,11 @@ struct SpectatorPlaneTableProducer { template bool analyzeCollision(B const& bc, C const& collision, std::array& vSP) { + // Check ZDC + if (!bc.has_zdc()) { + return false; + } + // Event selection if (!selCollision(collision)) { return false; @@ -548,34 +740,47 @@ struct SpectatorPlaneTableProducer { posZ = collision.posZ(); std::array vCollParam = {cent, posX, posY, posZ}; - // Fill event QA - histos.fill(HIST("Event/hCent"), cent); - histos.fill(HIST("Event/hVx"), posX); - histos.fill(HIST("Event/hVy"), posY); - histos.fill(HIST("Event/hVz"), posZ); + // Zdc information + auto zdc = bc.zdc(); - // check zdc - if (!bc.has_zdc()) { + if (!selZdcHit(zdc)) { return false; } - - auto zdc = bc.zdc(); auto znaEnergy = zdc.energySectorZNA(); auto zncEnergy = zdc.energySectorZNC(); auto znaEnergyCommon = zdc.energyCommonZNA(); auto zncEnergyCommon = zdc.energyCommonZNC(); - // check energy deposits + // Change in 3rd tower energy due to spurious peaks + zncEnergy[2] = zncEnergyCommon - zncEnergy[0] - zncEnergy[1] - zncEnergy[3]; + + // Check energy deposits if (znaEnergyCommon <= 0 || zncEnergyCommon <= 0 || znaEnergy[0] <= 0 || znaEnergy[1] <= 0 || znaEnergy[2] <= 0 || znaEnergy[3] <= 0 || zncEnergy[0] <= 0 || zncEnergy[1] <= 0 || zncEnergy[2] <= 0 || zncEnergy[3] <= 0) { return false; } + // Selected Collision with required ZDC + // Fill avg Vx / Vy + histos.fill(HIST("Event/hVxVsCent"), cent, posX); + histos.fill(HIST("Event/hVyVsCent"), cent, posY); + + // Apply Vx / Vy recentering + if (cRecentVxVy) { + vCollParam[kVx] -= CorrectionHistContainer.hVx->GetBinContent(CorrectionHistContainer.hVx->FindBin(cent)); + vCollParam[kVy] -= CorrectionHistContainer.hVy->GetBinContent(CorrectionHistContainer.hVy->FindBin(cent)); + } + + // Apply Vx / Vy selection [-10., 10.] mm + if (std::abs(vCollParam[kVx]) >= cAxisVxMax || std::abs(vCollParam[kVy]) >= cAxisVyMax) { + return false; + } + // Fill gain calib histograms + histos.fill(HIST("QA/hZNAEnergyCommon"), vCollParam[kVz], znaEnergyCommon); + histos.fill(HIST("QA/hZNCEnergyCommon"), vCollParam[kVz], zncEnergyCommon); for (int iCh = 0; iCh < kXYAC; ++iCh) { histos.fill(HIST("QA/hZNASignal"), iCh + 0.5, vCollParam[kVz], znaEnergy[iCh]); histos.fill(HIST("QA/hZNCSignal"), iCh + 0.5, vCollParam[kVz], zncEnergy[iCh]); - histos.fill(HIST("QA/hZNAEnergyCommon"), vCollParam[kVz], znaEnergyCommon); - histos.fill(HIST("QA/hZNCEnergyCommon"), vCollParam[kVz], zncEnergyCommon); } // Do gain calibration @@ -589,9 +794,15 @@ struct SpectatorPlaneTableProducer { histos.fill(HIST("QA/GainCalib/hZNCSignal"), iCh + 0.5, zncEnergy[iCh]); } + // Fill event QA + histos.fill(HIST("Event/hCent"), vCollParam[kCent]); + histos.fill(HIST("Event/hVx"), vCollParam[kVx]); + histos.fill(HIST("Event/hVy"), vCollParam[kVy]); + histos.fill(HIST("Event/hVz"), vCollParam[kVz]); + auto alphaZDC = 0.395; - const double x[4] = {-1.75, 1.75, -1.75, 1.75}; - const double y[4] = {-1.75, -1.75, 1.75, 1.75}; + std::array x = {-1.75, 1.75, -1.75, 1.75}; + std::array y = {-1.75, -1.75, 1.75, 1.75}; // Calculate X and Y float znaXNum = 0., znaYNum = 0., zncXNum = 0., zncYNum = 0.; @@ -682,24 +893,29 @@ struct SpectatorPlaneTableProducer { std::vector vPtCut = {cPionPtCut, cKaonPtCut, cProtonPtCut}; std::vector vTpcNsig = {std::abs(track.tpcNSigmaPi()), std::abs(track.tpcNSigmaKa()), std::abs(track.tpcNSigmaPr())}; std::vector vTofNsig = {std::abs(track.tofNSigmaPi()), std::abs(track.tofNSigmaKa()), std::abs(track.tofNSigmaPr())}; - bool retFlag = false; - if (partType == kPi && checkTrackPid(vPtCut[kPi], track.pt(), vTpcNsig, vTofNsig, track.hasTOF())) { - retFlag = true; - } else if (partType == kKa && checkTrackPid(vPtCut[kKa], track.pt(), vTpcNsig, vTofNsig, track.hasTOF())) { - retFlag = true; - } else if (partType == kPr && checkTrackPid(vPtCut[kPr], track.pt(), vTpcNsig, vTofNsig, track.hasTOF())) { - retFlag = true; - } else { - return false; + if (partType == kPi) { + return checkTrackPid(vPtCut[kPi], track.pt(), vTpcNsig, vTofNsig, track.hasTOF()); } - return retFlag; + if (partType == kKa) { + return checkTrackPid(vPtCut[kKa], track.pt(), vTpcNsig, vTofNsig, track.hasTOF()); + } + + if (partType == kPr) { + return checkTrackPid(vPtCut[kPr], track.pt(), vTpcNsig, vTofNsig, track.hasTOF()); + } + + return false; } using BCsRun3 = soa::Join; - using CollisionsRun3 = soa::Join; - using Tracks = soa::Join; + using CollisionsRun3 = soa::Join; + using Tracks = soa::Join; + + void processDummy(CollisionsRun3::iterator const&) {} + + PROCESS_SWITCH(SpectatorPlaneTableProducer, processDummy, "Dummy process", true); void processSpectatorPlane(CollisionsRun3::iterator const& collision, BCsRun3 const&, aod::Zdcs const&) { @@ -707,7 +923,7 @@ struct SpectatorPlaneTableProducer { auto bc = collision.template foundBC_as(); cRunNum = collision.template foundBC_as().runNumber(); - if (lRunNum != cRunNum) { + if (cLoadCorrection && lRunNum != cRunNum) { loadCorrections(); } @@ -735,13 +951,14 @@ struct SpectatorPlaneTableProducer { // Directed flow QXY vector float qac = (vSP[kXa] * vSP[kXc]) + (vSP[kYa] * vSP[kYc]); histos.fill(HIST("DF/hQaQc"), cent, qac); + histos.fill(HIST("DF/hQaQcVarCent"), cent, qac); } // Fill table colSPExtTable(colSPExtFlag, vSP[kXa], vSP[kYa], vSP[kXc], vSP[kYc]); } - PROCESS_SWITCH(SpectatorPlaneTableProducer, processSpectatorPlane, "Spectator Plane Process", true); + PROCESS_SWITCH(SpectatorPlaneTableProducer, processSpectatorPlane, "Spectator Plane Process", false); void processIdHadrons(Tracks const& tracks) { @@ -759,7 +976,10 @@ struct SpectatorPlaneTableProducer { struct FlowEventPlane { // Tracks - Configurable cNEtaBins{"cNEtaBins", 5, "# of eta bins"}; + Configurable cEtaBinsCharged{"cEtaBinsCharged", 5, "# of eta bins"}; + Configurable cEtaBinsHadrons{"cEtaBinsHadrons", 5, "# of eta bins"}; + Configurable cEtaBinsStrange{"cEtaBinsStrange", 5, "# of eta bins"}; + Configurable cEtaCut{"cEtaCut", 0.8, "Rapidity cut"}; // Pi,Ka,Pr Configurable cMinPtPi{"cMinPtPi", 0.2, "Pion min pT"}; @@ -767,10 +987,7 @@ struct FlowEventPlane { Configurable cMinPtPr{"cMinPtPr", 0.5, "Proton min pT"}; // Resonance - Configurable cNRapBins{"cNRapBins", 5, "# of y bins"}; Configurable cPhiInvMassBins{"cPhiInvMassBins", 500, "# of Phi mass bins"}; - Configurable cKStarInvMassBins{"cKStarInvMassBins", 200, "# of Phi mass bins"}; - Configurable cResRapCut{"cResRapCut", 0.5, "Resonance rapidity cut"}; // V0 // Tracks @@ -780,52 +997,49 @@ struct FlowEventPlane { // V0s Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; + Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 70, "TPC Min Crossed Rows"}; Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.01, "Minimum Proton DCAr to PV"}; Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.1, "Minimum Pion DCAr to PV"}; Configurable cDcaV0Dau{"cDcaV0Dau", 1., "DCA between V0 daughters"}; - Configurable cDcaV0ToPv{"cDcaV0ToPv", 0.1, "DCA V0 to PV"}; Configurable cMinV0Radius{"cMinV0Radius", 0.5, "Minimum V0 radius from PV"}; Configurable cK0ShortCTau{"cK0ShortCTau", 20.0, "Decay length cut K0Short"}; Configurable cLambdaCTau{"cLambdaCTau", 30.0, "Decay length cut Lambda"}; - Configurable cK0ShortCosPA{"cK0ShortCosPA", 0.998, "K0Short CosPA"}; - Configurable cLambdaCosPA{"cLambdaCosPA", 0.998, "Lambda CosPA"}; + Configurable cK0ShortCosPA{"cK0ShortCosPA", 0.97, "K0Short CosPA"}; + Configurable cLambdaCosPA{"cLambdaCosPA", 0.98, "Lambda CosPA"}; Configurable cK0SMassRej{"cK0SMassRej", 0.01, "Reject K0Short Candidates"}; Configurable cArmPodSel{"cArmPodSel", 0.2, "Armentros-Podolanski Selection for K0S"}; - Configurable cV0RapCut{"cV0RapCut", 0.8, "V0 rap cut"}; - Configurable cK0SMinPt{"cK0SMinPt", 0.4, "K0S Min pT"}; + Configurable cK0SMinPt{"cK0SMinPt", 0.5, "K0S Min pT"}; Configurable cK0SMaxPt{"cK0SMaxPt", 6.0, "K0S Max pT"}; Configurable cLambdaMinPt{"cLambdaMinPt", 0.6, "Lambda Min pT"}; Configurable cLambdaMaxPt{"cLambdaMaxPt", 6.0, "Lambda Max pT"}; + ConfigurableAxis axisVarCent{"axisVarCent", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100}, "Variable width centrality bins"}; + // Histogram registry: an object to hold your histograms HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Global objects float cent = 0.; std::array vSP = {0., 0., 0., 0.}; - std::map> mResoDauMass = {{kPhi0, {MassKaonCharged, MassKaonCharged}}, {kKStar, {MassPionCharged, MassKaonCharged}}}; - std::map mResoMass = {{kPhi0, MassPhi}, {kKStar, MassKaonCharged}}; - std::map mV0Ctau = {{kK0S, cK0ShortCTau}, {kLambda, cLambdaCTau}, {kAntiLambda, cLambdaCTau}}; - std::map mV0CosPA = {{kK0S, cK0ShortCosPA}, {kLambda, cLambdaCosPA}, {kAntiLambda, cLambdaCosPA}}; void init(InitContext const&) { // Define axes - const AxisSpec axisCent{100, 0., 100, "FT0C%"}; + // const AxisSpec axisCent{100, 0., 100, "FT0C%"}; const AxisSpec axisXYac{600, -6, 6, "Q^{t}Q^{p}"}; const AxisSpec axisV1{400, -4, 4, "v_{1}"}; const AxisSpec axisTrackPt{100, 0., 10., "p_{T} (GeV/#it{c})"}; - const AxisSpec axisTrackEta{cNEtaBins, -0.8, 0.8, "#eta"}; + const AxisSpec axisTrackEtaCharged{cEtaBinsCharged, -0.8, 0.8, "#etaCharged"}; + const AxisSpec axisTrackEtaHadrons{cEtaBinsHadrons, -0.8, 0.8, "#etaHadrons"}; + const AxisSpec axisTrackEtaStrange{cEtaBinsStrange, -0.8, 0.8, "#etaStrange"}; const AxisSpec axisTrackDcaXY{60, -0.15, 0.15, "DCA_{XY}"}; const AxisSpec axisTrackDcaZ{230, -1.15, 1.15, "DCA_{XY}"}; const AxisSpec axisTrackdEdx{360, 20, 200, "#frac{dE}{dx}"}; const AxisSpec axisTrackNSigma{161, -4.025, 4.025, {"n#sigma"}}; - const AxisSpec axisTrackRap{cNRapBins, -0.5, 0.5, "y"}; const AxisSpec axisPhiInvMass{cPhiInvMassBins, 0.99, 1.12, "M_{KK} (GeV/#it{c}^{2}"}; - const AxisSpec axisKStarInvMass{cKStarInvMassBins, 0.8, 1.2, "M_{#piK} (GeV/#it{c}^{2}"}; const AxisSpec axisMomPID(80, 0, 4, "p_{T} (GeV/#it{c})"); const AxisSpec axisNsigma(401, -10.025, 10.025, {"n#sigma"}); const AxisSpec axisdEdx(360, 20, 200, "#frac{dE}{dx}"); @@ -847,12 +1061,12 @@ struct FlowEventPlane { histos.add("TrackQA/hPtDcaXY", "DCA_{XY} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaXY}); histos.add("TrackQA/hPtDcaZ", "DCA_{Z} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaZ}); histos.add("TrackQA/hTrackTPCdEdX", "hTrackTPCdEdX", kTH2F, {axisMomPID, axisdEdx}); - histos.add("DF/hAQu", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQu", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hAQuPos", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQuPos", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hAQuNeg", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("DF/hCQuNeg", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisCent, axisTrackEta}); + histos.add("DF/hAQu", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQu", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hAQuPos", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQuPos", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hAQuNeg", "u_{x}X^{A}_{1} + u_{y}Y^{A}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); + histos.add("DF/hCQuNeg", "u_{x}X^{C}_{1} + u_{y}Y^{C}_{1}", kTProfile2D, {axisVarCent, axisTrackEtaCharged}); } // Identified hadrons @@ -861,28 +1075,22 @@ struct FlowEventPlane { histos.add("PartId/Pion/hTOFSignal", "#beta_{TOF} vs p_{T}", kTH2F, {axisMomPID, axisTrackTofSignal}); histos.add("PartId/Pion/hTPCNSigma", "n#sigma_{TPC} vs p_{T}", kTH2F, {axisMomPID, axisTrackNSigma}); histos.add("PartId/Pion/hTOFNSigma", "n#sigma_{TOF} vs p_{T}", kTH2F, {axisMomPID, axisTrackNSigma}); - histos.add("PartId/Pion/hAQuPos", "PartId/Pion/hAQuPos", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hAQuNeg", "PartId/Pion/hAQuNeg", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hCQuPos", "PartId/Pion/hCQuPos", kTProfile2D, {axisCent, axisTrackEta}); - histos.add("PartId/Pion/hCQuNeg", "PartId/Pion/hCQuNeg", kTProfile2D, {axisCent, axisTrackEta}); + histos.add("PartId/Pion/hAQuPos", "PartId/Pion/hAQuPos", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hAQuNeg", "PartId/Pion/hAQuNeg", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hCQuPos", "PartId/Pion/hCQuPos", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); + histos.add("PartId/Pion/hCQuNeg", "PartId/Pion/hCQuNeg", kTProfile2D, {axisVarCent, axisTrackEtaHadrons}); histos.addClone("PartId/Pion/", "PartId/Kaon/"); histos.addClone("PartId/Pion/", "PartId/Proton/"); } // Resonance if (doprocessResoFlow) { - histos.add("Reso/Phi/hSigCentEtaInvMass", "hUSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/hBkgCentEtaInvMass", "hLSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Sig/hQuA", "hPhiQuA", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Sig/hQuC", "hPhiQuC", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Bkg/hQuA", "hPhiQuA", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/Phi/Bkg/hQuC", "hPhiQuC", kTProfile3D, {axisCent, axisTrackEta, axisPhiInvMass}); - histos.add("Reso/KStar/hSigCentEtaInvMass", "hUSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisKStarInvMass}); - histos.add("Reso/KStar/hBkgCentEtaInvMass", "hLSCentEtaInvMass", kTH3F, {axisCent, axisTrackEta, axisKStarInvMass}); - histos.add("Reso/KStar/Sig/hQuA", "hKStarQuA", kTProfile3D, {axisCent, axisTrackEta, axisKStarInvMass}); - histos.add("Reso/KStar/Sig/hQuC", "hKStarQuC", kTProfile3D, {axisCent, axisTrackEta, axisKStarInvMass}); - histos.add("Reso/KStar/Bkg/hQuA", "hKStarQuA", kTProfile3D, {axisCent, axisTrackEta, axisKStarInvMass}); - histos.add("Reso/KStar/Bkg/hQuC", "hKStarQuC", kTProfile3D, {axisCent, axisTrackEta, axisKStarInvMass}); + histos.add("Reso/Phi/hBkgCentEtaInvMass", "hLSCentEtaInvMass", kTH3F, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/hSigCentEtaInvMass", "hUSCentEtaInvMass", kTH3F, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Sig/hQuA", "hPhiQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Sig/hQuC", "hPhiQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Bkg/hQuA", "hPhiQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); + histos.add("Reso/Phi/Bkg/hQuC", "hPhiQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisPhiInvMass}); } // Lambda @@ -902,14 +1110,14 @@ struct FlowEventPlane { histos.add("V0/Lambda/QA/hPosNsigPiVsP", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisNsigma}); histos.add("V0/Lambda/QA/hNegNsigPiVsP", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisNsigma}); histos.addClone("V0/Lambda/", "V0/K0Short/"); - histos.add("V0/Lambda/hMassVsRap", "hMassVsRap", kTH3F, {axisCent, axisLambdaInvMass, axisTrackEta}); - histos.add("V0/Lambda/Flow/hQuA", "hQuA", kTProfile3D, {axisCent, axisTrackRap, axisLambdaInvMass}); - histos.add("V0/Lambda/Flow/hQuC", "hQuC", kTProfile3D, {axisCent, axisTrackRap, axisLambdaInvMass}); + histos.add("V0/Lambda/hMassVsRap", "hMassVsRap", kTH3F, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); + histos.add("V0/Lambda/Flow/hQuA", "hQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); + histos.add("V0/Lambda/Flow/hQuC", "hQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisLambdaInvMass}); histos.addClone("V0/Lambda/", "V0/AntiLambda/"); histos.addClone("V0/Lambda/", "V0/LambdaAntiLambda/"); - histos.add("V0/K0Short/hMassVsRap", "hMassVsRap", kTH3F, {axisCent, axisK0ShortInvMass, axisTrackEta}); - histos.add("V0/K0Short/Flow/hQuA", "hQuA", kTProfile3D, {axisCent, axisTrackRap, axisK0ShortInvMass}); - histos.add("V0/K0Short/Flow/hQuC", "hQuC", kTProfile3D, {axisCent, axisTrackRap, axisK0ShortInvMass}); + histos.add("V0/K0Short/hMassVsRap", "hMassVsRap", kTH3F, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); + histos.add("V0/K0Short/Flow/hQuA", "hQuA", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); + histos.add("V0/K0Short/Flow/hQuC", "hQuC", kTProfile3D, {axisVarCent, axisTrackEtaStrange, axisK0ShortInvMass}); } } @@ -939,7 +1147,7 @@ struct FlowEventPlane { float ux = 0., uy = 0., v1a = 0., v1c = 0.; float tpcNsigma = 0., tofNsigma = 0.; for (auto const& track : tracks) { - static constexpr std::string_view SubDir[] = {"Pion/", "Kaon/", "Proton/"}; + static constexpr auto SubDir = std::array{"Pion/", "Kaon/", "Proton/"}; if (part == kPi && track.pt() > cMinPtPi) { tpcNsigma = track.tpcNSigmaPi(); tofNsigma = track.tofNSigmaPi(); @@ -979,7 +1187,7 @@ struct FlowEventPlane { bool selV0DauTracks(V const& v0, T const& postrack, T const& negtrack) { // Kinematic selection - if (postrack.pt() <= cTrackPtCut || negtrack.pt() <= cTrackPtCut || std::abs(postrack.eta()) >= cTrackEtaCut || std::abs(negtrack.eta()) >= cTrackEtaCut) { + if (postrack.pt() <= cTrackPtCut || negtrack.pt() <= cTrackPtCut || std::abs(postrack.eta()) >= cTrackEtaCut || std::abs(negtrack.eta()) >= cTrackEtaCut || postrack.tpcNClsCrossedRows() <= cMinTpcCrossedRows || negtrack.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { return false; } @@ -1023,17 +1231,13 @@ struct FlowEventPlane { break; } - if (std::abs(tpcNSigmaDau1) >= cTpcNsigmaCut || std::abs(tpcNSigmaDau2) >= cTpcNsigmaCut) { - return false; - } - - return true; + return (std::abs(tpcNSigmaDau1) < cTpcNsigmaCut && std::abs(tpcNSigmaDau2) < cTpcNsigmaCut); } template void fillV0QAHist(C const& col, V const& v0, T const&) { - static constexpr std::string_view SubDir[] = {"V0/K0Short/QA/", "V0/Lambda/QA/", "V0/AntiLambda/QA/"}; + static constexpr auto SubDir = std::array{"V0/K0Short/QA/", "V0/Lambda/QA/", "V0/AntiLambda/QA/"}; // daugthers auto postrack = v0.template posTrack_as(); @@ -1064,31 +1268,21 @@ struct FlowEventPlane { histos.fill(HIST(SubDir[part]) + HIST("hNegNsigPiVsP"), negtrack.tpcInnerParam(), negtrack.tpcNSigmaPi()); } - template - void getResoFlow(T const& tracks1, T const& tracks2, std::array const& vSP) + template + void getResoFlow(T const& tracks1, T const& tracks2) { float ux = 0., uy = 0., v1a = 0., v1c = 0.; - std::array vMassDau = mResoDauMass.at(rt); - for (auto const& [track1, track2] : soa::combinations(soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - // Discard same track - if (track1.index() == track2.index()) { - continue; - } - - // Discard same charge track - if (track1.sign() == track2.sign()) { - continue; - } + for (auto const& [track1, track2] : soa::combinations(soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { // Apply pseudo-rapidity acceptance std::array v = {track1.px() + track2.px(), track1.py() + track2.py(), track1.pz() + track2.pz()}; - if (std::abs(RecoDecay::eta(v)) >= cResRapCut) { + if (std::abs(RecoDecay::eta(v)) >= cEtaCut) { continue; } // Reconstruct resonance float p = RecoDecay::p((track1.px() + track2.px()), (track1.py() + track2.py()), (track1.pz() + track2.pz())); - float e = RecoDecay::e(track1.px(), track1.py(), track1.pz(), vMassDau[0]) + RecoDecay::e(track2.px(), track2.py(), track2.pz(), vMassDau[1]); + float e = RecoDecay::e(track1.px(), track1.py(), track1.pz(), MassKaonCharged) + RecoDecay::e(track2.px(), track2.py(), track2.pz(), MassKaonCharged); float m = std::sqrt(RecoDecay::m2(p, e)); // Get directed flow @@ -1097,35 +1291,23 @@ struct FlowEventPlane { v1a = ux * vSP[kXa] + uy * vSP[kYa]; v1c = ux * vSP[kXc] + uy * vSP[kYc]; - // Histograms - static constexpr std::string_view SubDir[] = {"Reso/Phi/", "Reso/KStar/"}; - // Fill signal histogram - histos.fill(HIST(SubDir[rt]) + HIST("hSigCentEtaInvMass"), cent, RecoDecay::eta(v), m); - histos.fill(HIST(SubDir[rt]) + HIST("Sig/hQuA"), cent, RecoDecay::eta(v), m, v1a); - histos.fill(HIST(SubDir[rt]) + HIST("Sig/hQuC"), cent, RecoDecay::eta(v), m, v1c); - - // Get background - p = RecoDecay::p((track1.px() - track2.px()), (track1.py() - track2.py()), (track1.pz() - track2.pz())); - m = std::sqrt(RecoDecay::m2(p, e)); - v[0] = track1.px() - track2.px(); - v[1] = track1.py() - track2.py(); - v[2] = track1.pz() - track2.pz(); - ux = std::cos(RecoDecay::phi(v)); - uy = std::sin(RecoDecay::phi(v)); - v1a = ux * vSP[kXa] + uy * vSP[kYa]; - v1c = ux * vSP[kXc] + uy * vSP[kYc]; - - // Fill bkg histogram - histos.fill(HIST(SubDir[rt]) + HIST("hBkgCentEtaInvMass"), cent, RecoDecay::eta(v), m); - histos.fill(HIST(SubDir[rt]) + HIST("Bkg/hQuA"), cent, RecoDecay::eta(v), m, v1a); - histos.fill(HIST(SubDir[rt]) + HIST("Bkg/hQuC"), cent, RecoDecay::eta(v), m, v1c); + if (track1.sign() != track2.sign()) { + histos.fill(HIST("Reso/Phi/hSigCentEtaInvMass"), cent, RecoDecay::eta(v), m); + histos.fill(HIST("Reso/Phi/Sig/hQuA"), cent, RecoDecay::eta(v), m, v1a); + histos.fill(HIST("Reso/Phi/Sig/hQuC"), cent, RecoDecay::eta(v), m, v1c); + } else { + // Fill bkg histogram + histos.fill(HIST("Reso/Phi/hBkgCentEtaInvMass"), cent, RecoDecay::eta(v), m); + histos.fill(HIST("Reso/Phi/Bkg/hQuA"), cent, RecoDecay::eta(v), m, v1a); + histos.fill(HIST("Reso/Phi/Bkg/hQuC"), cent, RecoDecay::eta(v), m, v1c); + } } } using CollisionsRun3 = soa::Join; - using Tracks = soa::Join; - using TracksV0s = soa::Join; + using Tracks = soa::Join; + using TracksV0s = soa::Join; // Partitions SliceCache cache; @@ -1134,7 +1316,7 @@ struct FlowEventPlane { Partition kaonTrackPartition = (aod::tracksid::isCharged == true) && (aod::tracksid::isKaon == true); Partition protonTrackPartition = (aod::tracksid::isCharged == true) && (aod::tracksid::isProton == true); - void processDummy(CollisionsRun3::iterator const&) {} + void processDummy(aod::Collisions const&) {} PROCESS_SWITCH(FlowEventPlane, processDummy, "Dummy process", true); @@ -1197,12 +1379,10 @@ struct FlowEventPlane { } // Track partitions - auto pionTracks = pionTrackPartition->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); auto kaonTracks = kaonTrackPartition->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); // Resonance flow - getResoFlow(kaonTracks, kaonTracks, vSP); - getResoFlow(pionTracks, kaonTracks, vSP); + getResoFlow(kaonTracks, kaonTracks); } PROCESS_SWITCH(FlowEventPlane, processResoFlow, "Resonance flow process", false); @@ -1215,7 +1395,7 @@ struct FlowEventPlane { // Loop over v0s for (auto const& v0 : V0s) { // Topological and kinematic selections - if (std::abs(v0.eta()) >= cV0RapCut || v0.dcaV0daughters() >= cDcaV0Dau || v0.dcav0topv() >= cDcaV0ToPv || v0.v0radius() <= cMinV0Radius || v0.v0Type() != cV0TypeSelection) { + if (std::abs(v0.eta()) >= cEtaCut || v0.dcaV0daughters() >= cDcaV0Dau || v0.v0radius() <= cMinV0Radius || v0.v0Type() != cV0TypeSelection) { continue; } @@ -1232,31 +1412,31 @@ struct FlowEventPlane { float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassLambda0; // K0Short - if (selV0DauTracks(v0, postrack, negtrack) && v0.v0cosPA() > mV0CosPA.at(kK0S) && ctauK0Short < mV0Ctau.at(kK0S) && v0.qtarm() >= cArmPodSel * std::abs(v0.alpha()) && v0.pt() >= cK0SMinPt && v0.pt() < cK0SMaxPt) { + if (selV0DauTracks(v0, postrack, negtrack) && v0.v0cosPA() > cK0ShortCosPA && ctauK0Short < cK0ShortCTau && v0.qtarm() >= cArmPodSel * std::abs(v0.alpha()) && v0.pt() >= cK0SMinPt && v0.pt() < cK0SMaxPt) { fillV0QAHist(collision, v0, tracks); - histos.fill(HIST("V0/K0Short/hMassVsRap"), cent, v0.mK0Short(), v0.eta()); + histos.fill(HIST("V0/K0Short/hMassVsRap"), cent, v0.eta(), v0.mK0Short()); histos.fill(HIST("V0/K0Short/Flow/hQuA"), cent, v0.eta(), v0.mK0Short(), v1a); histos.fill(HIST("V0/K0Short/Flow/hQuC"), cent, v0.eta(), v0.mK0Short(), v1c); } // Lambda - if (selV0DauTracks(v0, postrack, negtrack) && v0.v0cosPA() > mV0CosPA.at(kLambda) && ctauLambda < mV0Ctau.at(kLambda) && std::abs(v0.mK0Short() - MassK0Short) >= cK0SMassRej && v0.pt() >= cLambdaMinPt && v0.pt() < cLambdaMaxPt) { + if (selV0DauTracks(v0, postrack, negtrack) && v0.v0cosPA() > cLambdaCosPA && ctauLambda < cLambdaCTau && std::abs(v0.mK0Short() - MassK0Short) >= cK0SMassRej && v0.qtarm() < cArmPodSel * std::abs(v0.alpha()) && v0.alpha() > 0 && v0.pt() >= cLambdaMinPt && v0.pt() < cLambdaMaxPt) { fillV0QAHist(collision, v0, tracks); - histos.fill(HIST("V0/Lambda/hMassVsRap"), cent, v0.mLambda(), v0.eta()); + histos.fill(HIST("V0/Lambda/hMassVsRap"), cent, v0.eta(), v0.mLambda()); histos.fill(HIST("V0/Lambda/Flow/hQuA"), cent, v0.eta(), v0.mLambda(), v1a); histos.fill(HIST("V0/Lambda/Flow/hQuC"), cent, v0.eta(), v0.mLambda(), v1c); - histos.fill(HIST("V0/LambdaAntiLambda/hMassVsRap"), cent, v0.mLambda(), v0.eta()); + histos.fill(HIST("V0/LambdaAntiLambda/hMassVsRap"), cent, v0.eta(), v0.mLambda()); histos.fill(HIST("V0/LambdaAntiLambda/Flow/hQuA"), cent, v0.eta(), v0.mLambda(), v1a); histos.fill(HIST("V0/LambdaAntiLambda/Flow/hQuC"), cent, v0.eta(), v0.mLambda(), v1c); } // AntiLambda - if (selV0DauTracks(v0, postrack, negtrack) && v0.v0cosPA() > mV0CosPA.at(kAntiLambda) && ctauLambda < mV0Ctau.at(kAntiLambda) && std::abs(v0.mK0Short() - MassK0Short) >= cK0SMassRej && v0.pt() >= cLambdaMinPt && v0.pt() < cLambdaMaxPt) { + if (selV0DauTracks(v0, postrack, negtrack) && v0.v0cosPA() > cLambdaCosPA && ctauLambda < cLambdaCTau && std::abs(v0.mK0Short() - MassK0Short) >= cK0SMassRej && v0.qtarm() < cArmPodSel * std::abs(v0.alpha()) && v0.alpha() < 0 && v0.pt() >= cLambdaMinPt && v0.pt() < cLambdaMaxPt) { fillV0QAHist(collision, v0, tracks); - histos.fill(HIST("V0/AntiLambda/hMassVsRap"), cent, v0.mAntiLambda(), v0.eta()); + histos.fill(HIST("V0/AntiLambda/hMassVsRap"), cent, v0.eta(), v0.mAntiLambda()); histos.fill(HIST("V0/AntiLambda/Flow/hQuA"), cent, v0.eta(), v0.mAntiLambda(), v1a); histos.fill(HIST("V0/AntiLambda/Flow/hQuC"), cent, v0.eta(), v0.mAntiLambda(), v1c); - histos.fill(HIST("V0/LambdaAntiLambda/hMassVsRap"), cent, v0.mAntiLambda(), v0.eta()); + histos.fill(HIST("V0/LambdaAntiLambda/hMassVsRap"), cent, v0.eta(), v0.mAntiLambda()); histos.fill(HIST("V0/LambdaAntiLambda/Flow/hQuA"), cent, v0.eta(), v0.mAntiLambda(), v1a); histos.fill(HIST("V0/LambdaAntiLambda/Flow/hQuC"), cent, v0.eta(), v0.mAntiLambda(), v1c); } @@ -1268,6 +1448,7 @@ struct FlowEventPlane { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; } diff --git a/PWGCF/Flow/Tasks/flowFlucGfwPp.cxx b/PWGCF/Flow/Tasks/flowFlucGfwPp.cxx new file mode 100644 index 00000000000..ef7b945af75 --- /dev/null +++ b/PWGCF/Flow/Tasks/flowFlucGfwPp.cxx @@ -0,0 +1,1664 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowFlucGfwPp.cxx +/// \brief GFW task for Event Shape Engineering studies in pp collisions +/// \author Wenya Wu, TUM, wenya.wu@cern.ch, David Krylenkov, TUM + +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWConfig.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::analysis::genericframework; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; + +namespace o2::analysis::gfwflowflucpp +{ +std::vector ptbinning = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}; +float ptpoilow = 0.2, ptpoiup = 10.0; +float ptreflow = 0.2, ptrefup = 3.0; +float ptlow = 0.2, ptup = 10.0; +int etabins = 16; +float etalow = -0.8, etaup = 0.8; +int vtxZbins = 40; +float vtxZlow = -10.0, vtxZup = 10.0; +int phibins = 72; +float philow = 0.0; +float phiup = o2::constants::math::TwoPI; +int nchbins = 300; +float nchlow = 0; +float nchup = 3000; +std::vector centbinning(90); +int nBootstrap = 10; +GFWRegions regions; +GFWCorrConfigs configs; +std::vector multGlobalCorrCutPars; +std::vector multPVCorrCutPars; +std::vector multGlobalPVCorrCutPars; +std::vector multGlobalV0ACutPars; +std::vector multGlobalT0ACutPars; +std::vector firstRunsOfFill; +} // namespace o2::analysis::gfwflowflucpp + +struct FlowFlucGfwPp { + static constexpr int kInvalidQnBin = -999; + static constexpr float kInvalidQnSeparator = -999.f; + static constexpr float kTPCSubeventEtaGapHalfWidth = 0.1f; + + static constexpr int kRequireBothEtaSides = 1; + static constexpr int kRequireFullFourParticleTracks = 2; + static constexpr int kRequireTwoTracksInBothEtaSides = 4; + static constexpr int kRequireTwoTracksInThreeEtaRegions = 8; + + static constexpr int kMinTracksForFourParticleCorrelation = 4; + static constexpr int kMinTracksPerEtaSideForGapCorrelation = 2; + static constexpr int kMinTracksPerEtaRegionForThreeSubevents = 2; + + static constexpr int EllipticQVectorHarmonic = 2; + static constexpr int TriangularQVectorHarmonic = 3; + static constexpr int PtEfficiencyHistogramDimension = 1; + static constexpr int PtEtaEfficiencyHistogramDimension = 2; + + O2_DEFINE_CONFIGURABLE(cfgNbootstrap, int, 10, "Number of subsamples") + O2_DEFINE_CONFIGURABLE(cfgIsMC, bool, false, "Is MC event") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A, 4:NTPV, 5:NGlobals") + O2_DEFINE_CONFIGURABLE(cfgUseNch, bool, false, "Do correlations as function of Nch") + O2_DEFINE_CONFIGURABLE(cfgQvecQA, bool, false, "Enable filling QA for q-Vec of TPC") + O2_DEFINE_CONFIGURABLE(cfgFillWeights, bool, false, "Fill NUA weights") + O2_DEFINE_CONFIGURABLE(cfgRunByRun, bool, false, "Fill histograms on a run-by-run basis") + O2_DEFINE_CONFIGURABLE(cfgFillFlowRunByRun, bool, false, "Fill flow profile run-by-run (only for v22)") + O2_DEFINE_CONFIGURABLE(cfgTimeDependent, bool, false, "Fill output as function of time (for contamination studies)") + O2_DEFINE_CONFIGURABLE(cfgFirstRunsOfFill, std::vector, {}, "First runs of a fill for time dependent analysis") + O2_DEFINE_CONFIGURABLE(cfgFillQA, bool, false, "Fill QA histograms") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgAcceptance, std::string, "", "CCDB path to acceptance object") + O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"); + O2_DEFINE_CONFIGURABLE(cfgDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"); + O2_DEFINE_CONFIGURABLE(cfgDCAz, float, 2, "Cut on DCA in the longitudinal direction (cm)"); + O2_DEFINE_CONFIGURABLE(cfgNTPCCls, float, 50, "Cut on number of TPC clusters found"); + O2_DEFINE_CONFIGURABLE(cfgNTPCXrows, float, 70, "Cut on number of TPC crossed rows"); + O2_DEFINE_CONFIGURABLE(cfgMinNITSCls, float, 5, "Cut on minimum number of ITS clusters found"); + O2_DEFINE_CONFIGURABLE(cfgChi2PrITSCls, float, 36, "Cut on chi^2 per ITS clusters found"); + O2_DEFINE_CONFIGURABLE(cfgChi2PrTPCCls, float, 2.5, "Cut on chi^2 per TPC clusters found"); + O2_DEFINE_CONFIGURABLE(cfgPtmin, float, 0.2, "minimum pt (GeV/c)"); + O2_DEFINE_CONFIGURABLE(cfgPtmax, float, 10, "maximum pt (GeV/c)"); + O2_DEFINE_CONFIGURABLE(cfgEta, float, 0.8, "eta cut"); + O2_DEFINE_CONFIGURABLE(cfgVtxZ, float, 10, "vertex cut (cm)"); + O2_DEFINE_CONFIGURABLE(cfgNoSameBunchPileupCut, bool, false, "kNoSameBunchPileupCut"); + O2_DEFINE_CONFIGURABLE(cfgIsGoodZvtxFT0vsPV, bool, false, "kIsGoodZvtxFT0vsPV"); + O2_DEFINE_CONFIGURABLE(cfgIsGoodITSLayersAll, bool, false, "kIsGoodITSLayersAll"); + O2_DEFINE_CONFIGURABLE(cfgNoCollInTimeRangeStandard, bool, false, "kNoCollInTimeRangeStandard"); + O2_DEFINE_CONFIGURABLE(cfgDoOccupancySel, bool, false, "Bool for event selection on detector occupancy"); + O2_DEFINE_CONFIGURABLE(cfgOccupancySelection, int, 5000, "Max occupancy selection, -999 to disable"); + O2_DEFINE_CONFIGURABLE(cfgMultCut, bool, false, "Use additional event cut on mult correlations"); + O2_DEFINE_CONFIGURABLE(cfgTVXinTRD, bool, false, "Use kTVXinTRD (reject TRD triggered events)"); + O2_DEFINE_CONFIGURABLE(cfgIsVertexITSTPC, bool, true, "Selects collisions with at least one ITS-TPC track"); + O2_DEFINE_CONFIGURABLE(cfgMagField, float, 99999, "Configurable magnetic field; default CCDB will be queried"); + O2_DEFINE_CONFIGURABLE(cfgFixedMultMin, int, 1, "Minimum for fixed nch range"); + O2_DEFINE_CONFIGURABLE(cfgUseMultiplicityFlowWeights, bool, true, "Enable or disable the use of multiplicity-based event weighting"); + O2_DEFINE_CONFIGURABLE(cfgFixedMultMax, int, 3000, "Maximum for fixed nch range"); + O2_DEFINE_CONFIGURABLE(cfgConsistentEventFlag, int, 0, "Flag to select consistent events - 0: off, 1: v2{2} gap calculable, 2: v2{4} full calculable, 4: v2{4} gap calculable, 8: v2{4} 3sub calculable"); + Configurable> cfgMultGlobalCutPars{"cfgMultGlobalCutPars", std::vector{2272.16, -76.6932, 1.01204, -0.00631545, 1.59868e-05, 136.336, -4.97006, 0.121199, -0.0015921, 7.66197e-06}, "Global vs FT0C multiplicity cut parameter values"}; + Configurable> cfgMultPVCutPars{"cfgMultPVCutPars", std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}, "PV vs FT0C multiplicity cut parameter values"}; + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.223013, 0.715849, 0.664242, 0.0829653, -0.000503733, 1.21185e-06}, "Global vs PV multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultCorrHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCorrLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCorrCutFunction, std::string, "[0] + [1]*x + 3*([2] + [3]*x + [4]*x*x + [5]*x*x*x)", "Functional for global vs pv multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgNumQnBins, int, 10, "Number of qn bins"); + O2_DEFINE_CONFIGURABLE(cfgCentMax, float, 100, "Maximum of centrality or multiplicity"); + O2_DEFINE_CONFIGURABLE(cfgEvtSelCent, bool, true, "Choose event selector as centrality(true) or multicplity(false)"); + O2_DEFINE_CONFIGURABLE(cfgUseNegativeEtaHalfForq2, bool, true, "If true, use -eta half for qn selection; otherwise use +eta half"); + O2_DEFINE_CONFIGURABLE(cfgQnSelectionHarmonic, int, 2, "Harmonic n used to build the reduced q_n vector for event shape selection, use 2 for q2 and 3 for q3"); + O2_DEFINE_CONFIGURABLE(cfgQnHistMax, float, 6., "Upper range for q_n calibration histograms"); + O2_DEFINE_CONFIGURABLE(cfgQnTrkAbsEtaMax, float, 0.5, "Upper range for abs eta of tracks contributing to q_n"); + O2_DEFINE_CONFIGURABLE(cfgBypassQnSelection, bool, false, "Bypass q_n event shape selection and fill one integral q-bin"); + O2_DEFINE_CONFIGURABLE(cfgMinPtOnTPC, float, 0.2, "minimum transverse momentum selection for TPC tracks participating in Q-vector reconstruction"); + O2_DEFINE_CONFIGURABLE(cfgMaxPtOnTPC, float, 5., "maximum transverse momentum selection for TPC tracks participating in Q-vector reconstruction"); + O2_DEFINE_CONFIGURABLE(cfgEtaMax, float, 0.8, "Maximum pseudorapidiy for charged track"); + O2_DEFINE_CONFIGURABLE(cfgEtaMin, float, -0.8, "Minimum pseudorapidiy for charged track"); + Configurable> qnBinSeparator{"qnBinSeparator", std::vector{-999.f, -999.f, -999.f}, "Qn bin separator"}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgMultGlobalASideCorrCutFunction, std::string, "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x + [10]*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", "Functional for global vs V0A multiplicity low correlation cut"); + Configurable> cfgMultGlobalV0ACutPars{"cfgMultGlobalV0ACutPars", std::vector{567.785, 172.715, 0.77888, -0.00693466, 1.40564e-05, 679.853, 66.8068, -0.444332, 0.00115002, -4.92064e-07}, "Global vs FV0A multiplicity cut parameter values"}; + Configurable> cfgMultGlobalT0ACutPars{"cfgMultGlobalT0ACutPars", std::vector{241.618, 61.8402, 0.348049, -0.00306078, 6.20357e-06, 315.235, 29.1491, -0.188639, 0.00044528, -9.08912e-08}, "Global vs FT0A multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalV0ALowSigma, float, -3, "Number of sigma deviations below expected value in global vs V0A correlation"); + O2_DEFINE_CONFIGURABLE(cfgGlobalV0AHighSigma, float, 4, "Number of sigma deviations above expected value in global vs V0A correlation"); + O2_DEFINE_CONFIGURABLE(cfgGlobalT0ALowSigma, float, -3., "Number of sigma deviations below expected value in global vs T0A correlation"); + O2_DEFINE_CONFIGURABLE(cfgGlobalT0AHighSigma, float, 4, "Number of sigma deviations above expected value in global vs T0A correlation"); + } cfgGlobalAsideCorrCuts; + + Configurable cfgGfwBinning{"cfgGfwBinning", + {40, 16, 72, 300, 0, 3000, 0.2, 10.0, 0.2, 3.0, {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5, 5.5, 6, 7, 8, 9, 10}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}}, + "Configuration for binning"}; + + Configurable cfgRegions{"cfgRegions", + { + {"refN", "refP", "refFull"}, + {-0.8, 0.4, -0.8}, + {-0.4, 0.8, 0.8}, + {0, 0, 0}, // pT bins + {1, 1, 1} // bitmask + }, + "Configurations for GFW regions"}; + + Configurable cfgCorrConfig{"cfgCorrConfig", + {{"refN {2 -2}", + "refN {2 2 -2 -2}", + "refN {2 2 2 -2 -2 -2}", + "refN {2 2 2 2 -2 -2 -2 -2}", + "refP {2 -2}", + "refP {2 2 -2 -2}", + "refP {2 2 2 -2 -2 -2}", + "refP {2 2 2 2 -2 -2 -2 -2}", + "refN {2} refP {-2}", + "refN {2 2} refP {-2 -2}", + "refFull {2 -2}", + "refFull {2 2 -2 -2}", + "refFull {2 2 2 -2 -2 -2}", + "refFull {2 2 2 2 -2 -2 -2 -2}"}, + {"ChNeg22", + "ChNeg24", + "ChNeg26", + "ChNeg28", + "ChPos22", + "ChPos24", + "ChPos26", + "ChPos28", + "ChGap22", + "ChGap24", + "ChFull22", + "ChFull24", + "ChFull26", + "ChFull28"}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + "Configurations for pp ESE v2 cumulants"}; + + // Connect to ccdb + Service ccdb; + + struct Config { + TH1* mEfficiency = nullptr; + GFWWeights* mAcceptance; + bool correctionsLoaded = false; + } cfg; + + // Define output + OutputObj fFC{FlowContainer("FlowContainer")}; + OutputObj fFCgen{FlowContainer("FlowContainer_gen")}; + HistogramRegistry registry{"registry"}; + + // QA outputs + std::map>> th1sList; + std::map>> tpfsList; + std::map>> th3sList; + enum OutputTH1Names { + hPhi = 0, + hEta, + hVtxZ, + hMult, + hCent, + hEventSel, + kCount_TH1Names + }; + enum OutputTProfileNames { + pfCorr22 = 0, + kCount_TProfileNames + }; + // NUA outputs + enum OutputTH3Names { + hNUAref = 0, + kCount_TH3Names + }; + enum CentEstimators { + kCentFT0C = 0, + kCentFT0CVariant1, + kCentFT0M, + kCentFV0A, + kCentNTPV, + kCentNGlobal + }; + enum EventSelFlags { + kFilteredEvent = 1, + kSel8, + kOccupancy, + kTVXTRD, + kNoSamebunchPU, + kZVtxFT0PV, + kNoCollTRStd, + kVtxITSTPC, + kGoodITSLayers, + kMultCuts, + kTrackCent + }; + + // Define global variables + // Generic Framework + GFW* fGFW = new GFW(); + std::vector corrconfigs; + + TRandom3* fRndm = new TRandom3(0); + TAxis* fSecondAxis; + int lastRun = -1; + std::vector::iterator firstRunOfCurrentFill; + std::vector runNumbers; + + std::string getShapeSel() const + { + return "ese"; + } + + int getNQnOutputBins() + { + return static_cast(cfgBypassQnSelection) ? 1 : static_cast(cfgNumQnBins); + } + + // region indices for consistency flag + int posRegionIndex = -1; + int negRegionIndex = -1; + int fullRegionIndex = -1; + int midRegionIndex = -1; + + // Event selection cuts - Alex + TF1* fMultPVCutLow = nullptr; + TF1* fMultPVCutHigh = nullptr; + TF1* fMultCutLow = nullptr; + TF1* fMultCutHigh = nullptr; + TF1* fMultPVGlobalCutHigh = nullptr; + TF1* fMultGlobalV0ACutLow = nullptr; + TF1* fMultGlobalV0ACutHigh = nullptr; + TF1* fMultGlobalT0ACutLow = nullptr; + TF1* fMultGlobalT0ACutHigh = nullptr; + + TF1* fPtDepDCAxy = nullptr; + + o2::framework::expressions::Filter collisionFilter = nabs(aod::collision::posZ) < cfgVtxZ; + o2::framework::expressions::Filter trackFilter = nabs(aod::track::eta) < cfgEta && aod::track::pt > cfgPtmin&& aod::track::pt < cfgPtmax && (aod::track::itsChi2NCl < cfgChi2PrITSCls) && (aod::track::tpcChi2NCl < cfgChi2PrTPCCls) && nabs(aod::track::dcaZ) < cfgDCAz; + + o2::framework::expressions::Filter mcCollFilter = nabs(aod::mccollision::posZ) < cfgVtxZ; + o2::framework::expressions::Filter mcParticlesFilter = (aod::mcparticle::eta > o2::analysis::gfwflowflucpp::etalow && aod::mcparticle::eta < o2::analysis::gfwflowflucpp::etaup && aod::mcparticle::pt > o2::analysis::gfwflowflucpp::ptlow && aod::mcparticle::pt < o2::analysis::gfwflowflucpp::ptup); + + using GFWTracks = soa::Filtered>; + using GFWTracksMC = soa::Filtered>; + + void init(InitContext const&) + { + LOGF(info, "FlowFlucGfwPp::init()"); + if (static_cast(cfgMinPtOnTPC) < static_cast(cfgPtmin) || + static_cast(cfgMaxPtOnTPC) > static_cast(cfgPtmax) || + static_cast(cfgEtaMax) > static_cast(cfgEta) || + static_cast(cfgEtaMin) < -static_cast(cfgEta)) { + LOGF(warning, + "The Q-vector loop sees only tracks that passed the main trackFilter. " + "[pt %.3g, %.3g], [eta %.3g, %.3g] and input cuts [pt %.3g, %.3g], |eta|<%.3g", + static_cast(cfgMinPtOnTPC), + static_cast(cfgMaxPtOnTPC), + static_cast(cfgEtaMin), + static_cast(cfgEtaMax), + static_cast(cfgPtmin), + static_cast(cfgPtmax), + static_cast(cfgEta)); + } + o2::analysis::gfwflowflucpp::regions.SetNames(cfgRegions->GetNames()); + o2::analysis::gfwflowflucpp::regions.SetEtaMin(cfgRegions->GetEtaMin()); + o2::analysis::gfwflowflucpp::regions.SetEtaMax(cfgRegions->GetEtaMax()); + o2::analysis::gfwflowflucpp::regions.SetpTDifs(cfgRegions->GetpTDifs()); + o2::analysis::gfwflowflucpp::regions.SetBitmasks(cfgRegions->GetBitmasks()); + o2::analysis::gfwflowflucpp::configs.SetCorrs(cfgCorrConfig->GetCorrs()); + o2::analysis::gfwflowflucpp::configs.SetHeads(cfgCorrConfig->GetHeads()); + o2::analysis::gfwflowflucpp::configs.SetpTDifs(cfgCorrConfig->GetpTDifs()); + o2::analysis::gfwflowflucpp::configs.SetpTCorrMasks(cfgCorrConfig->GetpTCorrMasks()); + + o2::analysis::gfwflowflucpp::regions.Print(); + o2::analysis::gfwflowflucpp::configs.Print(); + + o2::analysis::gfwflowflucpp::ptbinning = cfgGfwBinning->GetPtBinning(); + // o2::analysis::gfwflowflucpp::ptpoilow = cfgGfwBinning->GetPtPOImin(); + // o2::analysis::gfwflowflucpp::ptpoiup = cfgGfwBinning->GetPtPOImax(); + o2::analysis::gfwflowflucpp::ptreflow = cfgGfwBinning->GetPtRefMin(); + o2::analysis::gfwflowflucpp::ptrefup = cfgGfwBinning->GetPtRefMax(); + o2::analysis::gfwflowflucpp::ptlow = cfgPtmin; + o2::analysis::gfwflowflucpp::ptup = cfgPtmax; + o2::analysis::gfwflowflucpp::etabins = cfgGfwBinning->GetEtaBins(); + o2::analysis::gfwflowflucpp::vtxZbins = cfgGfwBinning->GetVtxZbins(); + o2::analysis::gfwflowflucpp::phibins = cfgGfwBinning->GetPhiBins(); + o2::analysis::gfwflowflucpp::philow = 0.0f; + o2::analysis::gfwflowflucpp::phiup = o2::constants::math::TwoPI; + o2::analysis::gfwflowflucpp::nchbins = cfgGfwBinning->GetNchBins(); + o2::analysis::gfwflowflucpp::nchlow = cfgGfwBinning->GetNchMin(); + o2::analysis::gfwflowflucpp::nchup = cfgGfwBinning->GetNchMax(); + o2::analysis::gfwflowflucpp::centbinning = cfgGfwBinning->GetCentBinning(); + cfgGfwBinning->Print(); + + o2::analysis::gfwflowflucpp::multGlobalCorrCutPars = cfgMultGlobalCutPars; + o2::analysis::gfwflowflucpp::multPVCorrCutPars = cfgMultPVCutPars; + o2::analysis::gfwflowflucpp::multGlobalPVCorrCutPars = cfgMultGlobalPVCutPars; + o2::analysis::gfwflowflucpp::multGlobalV0ACutPars = cfgGlobalAsideCorrCuts.cfgMultGlobalV0ACutPars; + o2::analysis::gfwflowflucpp::multGlobalT0ACutPars = cfgGlobalAsideCorrCuts.cfgMultGlobalT0ACutPars; + o2::analysis::gfwflowflucpp::firstRunsOfFill = cfgFirstRunsOfFill; + if (cfgTimeDependent && !std::is_sorted(o2::analysis::gfwflowflucpp::firstRunsOfFill.begin(), o2::analysis::gfwflowflucpp::firstRunsOfFill.end())) { + std::sort(o2::analysis::gfwflowflucpp::firstRunsOfFill.begin(), o2::analysis::gfwflowflucpp::firstRunsOfFill.end()); + } + firstRunOfCurrentFill = o2::analysis::gfwflowflucpp::firstRunsOfFill.begin(); + + AxisSpec phiAxis = {o2::analysis::gfwflowflucpp::phibins, o2::analysis::gfwflowflucpp::philow, o2::analysis::gfwflowflucpp::phiup, "#phi"}; + AxisSpec etaAxis = {o2::analysis::gfwflowflucpp::etabins, -cfgEta, cfgEta, "#eta"}; + AxisSpec vtxAxis = {o2::analysis::gfwflowflucpp::vtxZbins, -cfgVtxZ, cfgVtxZ, "Vtx_{z} (cm)"}; + AxisSpec ptAxis = {o2::analysis::gfwflowflucpp::ptbinning, "#it{p}_{T} GeV/#it{c}"}; + + std::string sCentralityEstimator; + std::map centEstimatorMap = { + {kCentFT0C, "FT0C"}, + {kCentFT0CVariant1, "FT0C variant 1"}, + {kCentFT0M, "FT0M"}, + {kCentFV0A, "FV0A"}, + {kCentNTPV, "NTPV"}, + {kCentNGlobal, "NGlobals"}}; + sCentralityEstimator = centEstimatorMap.at(cfgCentEstimator); + sCentralityEstimator += " centrality (%)"; + AxisSpec centAxis = {o2::analysis::gfwflowflucpp::centbinning, sCentralityEstimator.c_str()}; + std::vector nchbinning; + int nchskip = (o2::analysis::gfwflowflucpp::nchup - o2::analysis::gfwflowflucpp::nchlow) / o2::analysis::gfwflowflucpp::nchbins; + for (int i = 0; i <= o2::analysis::gfwflowflucpp::nchbins; ++i) { + nchbinning.push_back(nchskip * i + o2::analysis::gfwflowflucpp::nchlow + 0.5); + } + AxisSpec nchAxis = {nchbinning, "N_{ch}"}; + std::vector bbinning(201); + std::generate(bbinning.begin(), bbinning.end(), [n = -0.1, step = 0.1]() mutable { + n += step; + return n; + }); + AxisSpec bAxis = {bbinning, "#it{b}"}; + AxisSpec t0cAxis = {1000, 0, 10000, "N_{ch} (T0C)"}; + AxisSpec t0aAxis = {300, 0, 30000, "N_{ch} (T0A)"}; + AxisSpec v0aAxis = {800, 0, 80000, "N_{ch} (V0A)"}; + AxisSpec multpvAxis = {600, 0, 600, "N_{ch} (PV)"}; + AxisSpec dcaZAXis = {200, -2, 2, "DCA_{z} (cm)"}; + AxisSpec dcaXYAXis = {200, -0.5, 0.5, "DCA_{xy} (cm)"}; + std::vector timebinning(289); + std::generate(timebinning.begin(), timebinning.end(), [n = -24 / 288., step = 24 / 288.]() mutable { + n += step; + return n; + }); + AxisSpec timeAxis = {timebinning, "time (hrs)"}; + + AxisSpec multAxis = (cfgTimeDependent) ? timeAxis : (cfgUseNch) ? nchAxis + : centAxis; + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + int ptbins = o2::analysis::gfwflowflucpp::ptbinning.size() - 1; + fSecondAxis = (cfgTimeDependent) ? new TAxis(timeAxis.binEdges.size() - 1, &(timeAxis.binEdges[0])) : new TAxis(ptbins, &o2::analysis::gfwflowflucpp::ptbinning[0]); + + if (static_cast(cfgBypassQnSelection)) { + LOGF(info, "Bypassing q_n event shape selection, all accepted events will be filled into the integral bin ese_0"); + if (static_cast(cfgNumQnBins) != 1) { + LOGF(warning, "cfgBypassQnSelection is on, cfgNumQnBins=%d will be ignored and only one output q-bin will be made", static_cast(cfgNumQnBins)); + } + } else { + LOGF(info, "Event-shape selector uses q_%d from the %s eta half", + static_cast(cfgQnSelectionHarmonic), + static_cast(cfgUseNegativeEtaHalfForq2) ? "negative" : "positive"); + } + + if (cfgQvecQA && (doprocessData || doprocessq2)) { + // qVectorsTable-equivalent TPC-track QA for the in-task raw TPC Q-vector loop. + AxisSpec qVecAxisPt = {40, 0.0, 4.0}; + AxisSpec qVecAxisEta = {32, -0.8, 0.8}; + AxisSpec qVecAxisPhi = {32, 0, constants::math::TwoPI}; + AxisSpec qVecAxisCent = {20, 0, 100}; + AxisSpec qVecAxisMulti = {20, 0, 1000}; + AxisSpec qVecAxis = {20, -10, 10}; + registry.add("qvecQA/ChTracks", ";pT;#eta;#phi", {HistType::kTHnSparseF, {qVecAxisPt, qVecAxisEta, qVecAxisPhi}}); + registry.add("qvecQA/CountEvt", ";Centrality;TrkSize;TrkSel;MultNTrkPV", {HistType::kTHnSparseF, {qVecAxisCent, qVecAxisMulti, qVecAxisMulti, qVecAxisMulti}}); + registry.add("qvecQA/QxQy", ";QxAll;QyAll;QxNeg;QyNeg;QxPos;QyPos", {HistType::kTHnSparseF, {qVecAxis, qVecAxis, qVecAxis, qVecAxis, qVecAxis, qVecAxis}}); + } + + if (doprocessq2) { + const int qnHarmonic = static_cast(cfgQnSelectionHarmonic); + const double qnHistMax = static_cast(cfgQnHistMax); + + if (qnHarmonic == EllipticQVectorHarmonic) { + registry.add("mq2/eventcounter", "", HistType::kTH1F, {{10, 0, 10}}); + registry.add("mq2/h2_cent_q2_etapos", ";Centrality;#it{q}_{2}^{#eta pos};", HistType::kTH2D, {{100, 0, 100}, {600, 0, qnHistMax}}); + registry.add("mq2/h2_cent_q2_etaneg", ";Centrality;#it{q}_{2}^{#eta neg};", HistType::kTH2D, {{100, 0, 100}, {600, 0, qnHistMax}}); + registry.add("mq2/h2_mult_q2_etapos", ";Multiplicity;#it{q}_{2}^{#eta pos};", HistType::kTH2D, {{150, 0, 150}, {600, 0, qnHistMax}}); + registry.add("mq2/h2_mult_q2_etaneg", ";Multiplicity;#it{q}_{2}^{#eta neg};", HistType::kTH2D, {{150, 0, 150}, {600, 0, qnHistMax}}); + } + + if (qnHarmonic == TriangularQVectorHarmonic) { + registry.add("mq3/eventcounter", "", HistType::kTH1F, {{10, 0, 10}}); + registry.add("mq3/h2_cent_q3_etapos", ";Centrality;#it{q}_{3}^{#eta pos};", HistType::kTH2D, {{100, 0, 100}, {600, 0, qnHistMax}}); + registry.add("mq3/h2_cent_q3_etaneg", ";Centrality;#it{q}_{3}^{#eta neg};", HistType::kTH2D, {{100, 0, 100}, {600, 0, qnHistMax}}); + registry.add("mq3/h2_mult_q3_etapos", ";Multiplicity;#it{q}_{3}^{#eta pos};", HistType::kTH2D, {{150, 0, 150}, {600, 0, qnHistMax}}); + registry.add("mq3/h2_mult_q3_etaneg", ";Multiplicity;#it{q}_{3}^{#eta neg};", HistType::kTH2D, {{150, 0, 150}, {600, 0, qnHistMax}}); + } + } + + if (doprocessData || doprocessMC) { + registry.add("trackQA/before/phi_eta_vtxZ", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registry.add("trackQA/before/pt_dcaXY_dcaZ", "", {HistType::kTH3D, {ptAxis, dcaXYAXis, dcaZAXis}}); + registry.add("trackQA/before/nch_pt", "#it{p}_{T} vs multiplicity; N_{ch}; #it{p}_{T}", {HistType::kTH2D, {nchAxis, ptAxis}}); + registry.add("trackQA/before/chi2prTPCcls", "#chi^{2}/cluster for the TPC track segment; #chi^{2}/TPC cluster", {HistType::kTH1D, {{100, 0., 5.}}}); + registry.add("trackQA/before/chi2prITScls", "#chi^{2}/cluster for the ITS track; #chi^{2}/ITS cluster", {HistType::kTH1D, {{100, 0., 50.}}}); + registry.add("trackQA/before/nTPCClusters", "Number of found TPC clusters; TPC N_{cls}; Counts", {HistType::kTH1D, {{100, 40, 180}}}); + registry.add("trackQA/before/nITSClusters", "Number of found ITS clusters; ITS N_{cls}; Counts", {HistType::kTH1D, {{100, 0, 20}}}); + registry.add("trackQA/before/nTPCCrossedRows", "Number of crossed TPC Rows; TPC X-rows; Counts", {HistType::kTH1D, {{100, 40, 180}}}); + + registry.addClone("trackQA/before/", "trackQA/after/"); + registry.add("trackQA/after/pt_ref", "", {HistType::kTH1D, {{100, o2::analysis::gfwflowflucpp::ptreflow, o2::analysis::gfwflowflucpp::ptrefup}}}); + registry.add("trackQA/after/pt_poi", "", {HistType::kTH1D, {{100, o2::analysis::gfwflowflucpp::ptpoilow, o2::analysis::gfwflowflucpp::ptpoiup}}}); + + registry.add("eventQA/before/multiplicity", "", {HistType::kTH1D, {nchAxis}}); + if (cfgTimeDependent) { + registry.add("eventQA/before/multiplicity_time", "Multiplicity vs time; time (hrs); N_{ch}", {HistType::kTH2D, {timeAxis, nchAxis}}); + registry.add("eventQA/before/multT0C_time", "T0C Multiplicity vs time; time (hrs); N_{ch} (T0C)", {HistType::kTH2D, {timeAxis, t0cAxis}}); + registry.add("eventQA/before/multT0A_time", "T0A Multiplicity vs time; time (hrs); N_{ch} (T0A)", {HistType::kTH2D, {timeAxis, t0aAxis}}); + registry.add("eventQA/before/multV0A_time", "V0A Multiplicity vs time; time (hrs); N_{ch} (V0A)", {HistType::kTH2D, {timeAxis, v0aAxis}}); + registry.add("eventQA/before/multPV_time", "PV Multiplicity vs time; time (hrs); N_{ch} (PV)", {HistType::kTH2D, {timeAxis, multpvAxis}}); + } + registry.add("eventQA/before/globalTracks_PVTracks", "", {HistType::kTH2D, {multpvAxis, nchAxis}}); + registry.add("eventQA/before/globalTracks_multT0A", "", {HistType::kTH2D, {t0aAxis, nchAxis}}); + registry.add("eventQA/before/globalTracks_multV0A", "", {HistType::kTH2D, {v0aAxis, nchAxis}}); + registry.add("eventQA/before/multV0A_multT0A", "", {HistType::kTH2D, {t0aAxis, v0aAxis}}); + + registry.add("eventQA/before/centrality", "", {HistType::kTH1D, {centAxis}}); + registry.add("eventQA/before/globalTracks_centT0C", "", {HistType::kTH2D, {centAxis, nchAxis}}); + registry.add("eventQA/before/PVTracks_centT0C", "", {HistType::kTH2D, {centAxis, multpvAxis}}); + registry.add("eventQA/before/multT0C_centT0C", "", {HistType::kTH2D, {centAxis, t0cAxis}}); + + registry.add("eventQA/before/centT0M_centT0C", "", {HistType::kTH2D, {centAxis, centAxis}}); + registry.add("eventQA/before/centV0A_centT0C", "", {HistType::kTH2D, {centAxis, centAxis}}); + registry.add("eventQA/before/centGlobal_centT0C", "", {HistType::kTH2D, {centAxis, centAxis}}); + registry.add("eventQA/before/centNTPV_centT0C", "", {HistType::kTH2D, {centAxis, centAxis}}); + if (cfgIsMC || doprocessMC) { + registry.add("MCReco/trackQA/pt_eta", "matched reconstructed primary particles;#it{p}_{T};#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + registry.add("MCGen/trackQA/phi_eta_vtxZ", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registry.add("MCGen/trackQA/pt_eta", "generated primary particles;#it{p}_{T};#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + registry.add("MCGen/trackQA/nch_pt", "#it{p}_{T} vs multiplicity; N_{ch}; #it{p}_{T}", {HistType::kTH2D, {nchAxis, ptAxis}}); + registry.add("MCGen/trackQA/pt_ref", "", {HistType::kTH1D, {{100, o2::analysis::gfwflowflucpp::ptreflow, o2::analysis::gfwflowflucpp::ptrefup}}}); + registry.add("MCGen/trackQA/pt_poi", "", {HistType::kTH1D, {{100, o2::analysis::gfwflowflucpp::ptpoilow, o2::analysis::gfwflowflucpp::ptpoiup}}}); + } + + registry.addClone("eventQA/before/", "eventQA/after/"); + registry.add("eventQA/eventSel", "Number of Events;; Counts", {HistType::kTH1D, {{11, 0.5, 11.5}}}); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kFilteredEvent, "Filtered event"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kSel8, "sel8"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kOccupancy, "occupancy"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kTVXTRD, "kTVXinTRD"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoSamebunchPU, "kNoSameBunchPileup"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kZVtxFT0PV, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoCollTRStd, "kNoCollInTimeRangeStandard"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kVtxITSTPC, "kIsVertexITSTPC"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kGoodITSLayers, "kIsGoodITSLayersAll"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kMultCuts, "after Mult cuts"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kTrackCent, "has track + within cent"); + if (!cfgRunByRun && cfgFillWeights) { + registry.add("phi_eta_vtxz_ref", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + } + } + + if (o2::analysis::gfwflowflucpp::regions.GetSize() < 0) + LOGF(error, "Configuration contains vectors of different size - check the GFWRegions configurable"); + for (auto i(0); i < o2::analysis::gfwflowflucpp::regions.GetSize(); ++i) { + fGFW->AddRegion(o2::analysis::gfwflowflucpp::regions.GetNames()[i], o2::analysis::gfwflowflucpp::regions.GetEtaMin()[i], o2::analysis::gfwflowflucpp::regions.GetEtaMax()[i], (o2::analysis::gfwflowflucpp::regions.GetpTDifs()[i]) ? ptbins + 1 : 1, o2::analysis::gfwflowflucpp::regions.GetBitmasks()[i]); + } + for (auto i = 0; i < o2::analysis::gfwflowflucpp::configs.GetSize(); ++i) { + corrconfigs.push_back(fGFW->GetCorrelatorConfig(o2::analysis::gfwflowflucpp::configs.GetCorrs()[i], o2::analysis::gfwflowflucpp::configs.GetHeads()[i], o2::analysis::gfwflowflucpp::configs.GetpTDifs()[i])); + } + if (corrconfigs.empty()) + LOGF(error, "Configuration contains vectors of different size - check the GFWCorrConfig configurable"); + fGFW->CreateRegions(); + TObjArray* oba = new TObjArray(); + addConfigObjectsToObjArray(oba, corrconfigs); + fFC->SetName("FlowContainer"); + fFC->SetXAxis(fSecondAxis); + fFC->Initialize(oba, multAxis, cfgNbootstrap); + + fFCgen->SetName("FlowContainer_gen"); + fFCgen->SetXAxis(fSecondAxis); + fFCgen->Initialize(oba, multAxis, cfgNbootstrap); + + delete oba; + + fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgDCAxy->c_str()), 0.001, 100); + fPtDepDCAxy->SetParameter(0, cfgDCAxyNSigma); + LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", cfgDCAxy->c_str())); + if (cfgUseAdditionalEventCut) { + fMultPVCutLow = new TF1("fMultPVCutLow", cfgMultCorrLowCutFunction->c_str(), 0, 100); + fMultPVCutLow->SetParameters(&(o2::analysis::gfwflowflucpp::multPVCorrCutPars[0])); + fMultPVCutHigh = new TF1("fMultPVCutHigh", cfgMultCorrHighCutFunction->c_str(), 0, 100); + fMultPVCutHigh->SetParameters(&(o2::analysis::gfwflowflucpp::multPVCorrCutPars[0])); + fMultCutLow = new TF1("fMultCutLow", cfgMultCorrLowCutFunction->c_str(), 0, 100); + fMultCutLow->SetParameters(&(o2::analysis::gfwflowflucpp::multGlobalCorrCutPars[0])); + fMultCutHigh = new TF1("fMultCutHigh", cfgMultCorrHighCutFunction->c_str(), 0, 100); + fMultCutHigh->SetParameters(&(o2::analysis::gfwflowflucpp::multGlobalCorrCutPars[0])); + fMultPVGlobalCutHigh = new TF1("fMultPVGlobalCutHigh", cfgMultGlobalPVCorrCutFunction->c_str(), 0, nchbinning.back()); + fMultPVGlobalCutHigh->SetParameters(&(o2::analysis::gfwflowflucpp::multGlobalPVCorrCutPars[0])); + + LOGF(info, "Global V0A function: %s in range 0-%g", cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), v0aAxis.binEdges.back()); + fMultGlobalV0ACutLow = new TF1("fMultGlobalV0ACutLow", cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, v0aAxis.binEdges.back()); + for (std::size_t i = 0; i < o2::analysis::gfwflowflucpp::multGlobalV0ACutPars.size(); ++i) + fMultGlobalV0ACutLow->SetParameter(i, o2::analysis::gfwflowflucpp::multGlobalV0ACutPars[i]); + fMultGlobalV0ACutLow->SetParameter(o2::analysis::gfwflowflucpp::multGlobalV0ACutPars.size(), cfgGlobalAsideCorrCuts.cfgGlobalV0ALowSigma); + for (int i = 0; i < fMultGlobalV0ACutLow->GetNpar(); ++i) + LOGF(info, "fMultGlobalV0ACutLow par %d = %g", i, fMultGlobalV0ACutLow->GetParameter(i)); + + fMultGlobalV0ACutHigh = new TF1("fMultGlobalV0ACutHigh", cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, v0aAxis.binEdges.back()); + for (std::size_t i = 0; i < o2::analysis::gfwflowflucpp::multGlobalV0ACutPars.size(); ++i) + fMultGlobalV0ACutHigh->SetParameter(i, o2::analysis::gfwflowflucpp::multGlobalV0ACutPars[i]); + fMultGlobalV0ACutHigh->SetParameter(o2::analysis::gfwflowflucpp::multGlobalV0ACutPars.size(), cfgGlobalAsideCorrCuts.cfgGlobalV0AHighSigma); + for (int i = 0; i < fMultGlobalV0ACutHigh->GetNpar(); ++i) + LOGF(info, "fMultGlobalV0ACutHigh par %d = %g", i, fMultGlobalV0ACutHigh->GetParameter(i)); + + LOGF(info, "Global T0A function: %s", cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str()); + fMultGlobalT0ACutLow = new TF1("fMultGlobalT0ACutLow", cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, t0aAxis.binEdges.back()); + for (std::size_t i = 0; i < o2::analysis::gfwflowflucpp::multGlobalT0ACutPars.size(); ++i) + fMultGlobalT0ACutLow->SetParameter(i, o2::analysis::gfwflowflucpp::multGlobalT0ACutPars[i]); + fMultGlobalT0ACutLow->SetParameter(o2::analysis::gfwflowflucpp::multGlobalT0ACutPars.size(), cfgGlobalAsideCorrCuts.cfgGlobalT0ALowSigma); + for (int i = 0; i < fMultGlobalT0ACutLow->GetNpar(); ++i) + LOGF(info, "fMultGlobalT0ACutLow par %d = %g", i, fMultGlobalT0ACutLow->GetParameter(i)); + + fMultGlobalT0ACutHigh = new TF1("fMultGlobalT0ACutHigh", cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, t0aAxis.binEdges.back()); + for (std::size_t i = 0; i < o2::analysis::gfwflowflucpp::multGlobalT0ACutPars.size(); ++i) + fMultGlobalT0ACutHigh->SetParameter(i, o2::analysis::gfwflowflucpp::multGlobalT0ACutPars[i]); + fMultGlobalT0ACutHigh->SetParameter(o2::analysis::gfwflowflucpp::multGlobalT0ACutPars.size(), cfgGlobalAsideCorrCuts.cfgGlobalT0AHighSigma); + for (int i = 0; i < fMultGlobalT0ACutHigh->GetNpar(); ++i) + LOGF(info, "fMultGlobalT0ACutHigh par %d = %g", i, fMultGlobalT0ACutHigh->GetParameter(i)); + } + + if (cfgConsistentEventFlag) { + const auto& names = cfgRegions->GetNames(); + auto findRegionIndex = [&](const std::string& name) { + auto it = std::find(names.begin(), names.end(), name); + return (it != names.end()) ? std::distance(names.begin(), it) : -1; + }; + posRegionIndex = findRegionIndex("refP"); + negRegionIndex = findRegionIndex("refN"); + fullRegionIndex = findRegionIndex("refFull"); + midRegionIndex = findRegionIndex("refMid"); + } + } + + static constexpr std::string_view FillTimeName[] = {"before/", "after/"}; + + enum QAFillTime { + kBefore, + kAfter + }; + + int getMagneticField(uint64_t timestamp) + { + static o2::parameters::GRPMagField* grpo = nullptr; + if (grpo == nullptr) { + // grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); + grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + } + return grpo->getNominalL3Field(); + } + + void loadCorrections(aod::BCsWithTimestamps::iterator const& bc) + { + uint64_t timestamp = bc.timestamp(); + if (!cfgRunByRun && cfg.correctionsLoaded) + return; + if (!cfgAcceptance.value.empty()) { + std::string runstr = (cfgRunByRun) ? "RunByRun/" : ""; + cfg.mAcceptance = ccdb->getForTimeStamp(cfgAcceptance.value + runstr, timestamp); + } + if (!cfgEfficiency.value.empty()) { + cfg.mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + if (cfg.mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram from %s", cfgEfficiency.value.c_str()); + } + if (cfg.mEfficiency->GetDimension() != PtEfficiencyHistogramDimension && cfg.mEfficiency->GetDimension() != PtEtaEfficiencyHistogramDimension) { + LOGF(fatal, "Efficiency object from %s has unsupported dimension %d. Expected pT or pT-eta.", cfgEfficiency.value.c_str(), cfg.mEfficiency->GetDimension()); + } + LOGF(info, "Loaded %dD efficiency histogram from %s (%p)", cfg.mEfficiency->GetDimension(), cfgEfficiency.value.c_str(), (void*)cfg.mEfficiency); + } + cfg.correctionsLoaded = true; + } + + template + double getAcceptance(const TTrack& track, const double& vtxz) + { + double wacc = 1; + if (cfg.mAcceptance) + wacc = cfg.mAcceptance->getNUA(track.phi(), track.eta(), vtxz); + return wacc; + } + + template + double getEfficiency(const TTrack& track) + { + double eff = 1.; + if (cfg.mEfficiency) { + if (cfg.mEfficiency->GetDimension() == PtEtaEfficiencyHistogramDimension) { + auto* efficiencyPtEta = dynamic_cast(cfg.mEfficiency); + if (efficiencyPtEta == nullptr) { + LOGF(fatal, "Loaded pT-eta efficiency object is not a TH2"); + return -1.; + } + eff = efficiencyPtEta->GetBinContent(efficiencyPtEta->FindBin(track.pt(), track.eta())); + } else { + eff = cfg.mEfficiency->GetBinContent(cfg.mEfficiency->FindBin(track.pt())); + } + } + if (!std::isfinite(eff) || eff <= 0) + return -1.; + else + return 1. / eff; + } + + template + bool eventSelected(const TCollision& collision, const int& multTrk, const float& centrality, const int& run) + { + if (cfgTVXinTRD) { + if (collision.alias_bit(kTVXinTRD)) { + // TRD triggered + // "CMTVX-B-NOPF-TRD,minbias_TVX" + return 0; + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kTVXTRD); + } + if (cfgRunByRun && run != -1) + th1sList[run][hEventSel]->Fill(kTVXTRD); + } + if (cfgNoSameBunchPileupCut) { + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return 0; + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kNoSamebunchPU); + } + if (cfgRunByRun && run != -1) + th1sList[run][hEventSel]->Fill(kNoSamebunchPU); + } + if (cfgIsGoodZvtxFT0vsPV) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return 0; + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kZVtxFT0PV); + } + if (cfgRunByRun && run != -1) + th1sList[run][hEventSel]->Fill(kZVtxFT0PV); + } + if (cfgNoCollInTimeRangeStandard) { + if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // Rejection of the collisions which have other events nearby + return 0; + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kNoCollTRStd); + } + if (cfgRunByRun && run != -1) + th1sList[run][hEventSel]->Fill(kNoCollTRStd); + } + + if (cfgIsVertexITSTPC) { + if (!collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + // selects collisions with at least one ITS-TPC track, and thus rejects vertices built from ITS-only tracks + return 0; + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kVtxITSTPC); + } + if (cfgRunByRun && run != -1) + th1sList[run][hEventSel]->Fill(kVtxITSTPC); + } + + if (cfgIsGoodITSLayersAll) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return 0; + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kGoodITSLayers); + } + if (cfgRunByRun && run != -1) + th1sList[run][hEventSel]->Fill(kGoodITSLayers); + } + + float vtxz = -999; + if (collision.numContrib() > 1) { + vtxz = collision.posZ(); + float zRes = std::sqrt(collision.covZZ()); + float minZRes = 0.25; + int minNContrib = 20; + if (zRes > minZRes && collision.numContrib() < minNContrib) + vtxz = -999; + } + auto multNTracksPV = collision.multNTracksPV(); + + if (vtxz > o2::analysis::gfwflowflucpp::vtxZup || vtxz < o2::analysis::gfwflowflucpp::vtxZlow) + return 0; + + if (cfgMultCut && cfgUseAdditionalEventCut) { + if (multNTracksPV < fMultPVCutLow->Eval(centrality)) + return 0; + if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) + return 0; + if (multTrk < fMultCutLow->Eval(centrality)) + return 0; + if (multTrk > fMultCutHigh->Eval(centrality)) + return 0; + if (multTrk > fMultPVGlobalCutHigh->Eval(collision.multNTracksPV())) + return 0; + + if (!(cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) < fMultGlobalV0ACutLow->Eval(multTrk)) + return 0; + if (!(cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) > fMultGlobalV0ACutHigh->Eval(multTrk)) + return 0; + if (!(cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) < fMultGlobalT0ACutLow->Eval(multTrk)) + return 0; + if (!(cfgGlobalAsideCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) > fMultGlobalT0ACutHigh->Eval(multTrk)) + return 0; + if (cfgRunByRun && run != -1) + th1sList[run][hEventSel]->Fill(kMultCuts); + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kMultCuts); + } + return 1; + } + + template + bool trackSelected(const TTrack& track) + { + if (cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) + return false; + return ((track.tpcNClsCrossedRows() >= cfgNTPCXrows) && (track.tpcNClsFound() >= cfgNTPCCls) && (track.itsNCls() >= cfgMinNITSCls)); + } + + enum DataType { + kReco, + kGen + }; + + template + void fillWeights(const TTrack& track, const double vtxz, const int& run) + { + if (cfgRunByRun) + th3sList[run][hNUAref]->Fill(track.phi(), track.eta(), vtxz); + else + registry.fill(HIST("phi_eta_vtxz_ref"), track.phi(), track.eta(), vtxz); + return; + } + + void createRunByRunHistograms(const int& run) + { + AxisSpec phiAxis = {o2::analysis::gfwflowflucpp::phibins, o2::analysis::gfwflowflucpp::philow, o2::analysis::gfwflowflucpp::phiup, "#phi"}; + AxisSpec etaAxis = {o2::analysis::gfwflowflucpp::etabins, -cfgEta, cfgEta, "#eta"}; + AxisSpec vtxAxis = {o2::analysis::gfwflowflucpp::vtxZbins, -cfgVtxZ, cfgVtxZ, "Vtx_{z} (cm)"}; + AxisSpec nchAxis = {o2::analysis::gfwflowflucpp::nchbins, o2::analysis::gfwflowflucpp::nchlow, o2::analysis::gfwflowflucpp::nchup, "N_{ch}"}; + AxisSpec centAxis = {o2::analysis::gfwflowflucpp::centbinning, "Centrality (%)"}; + std::vector> histos(kCount_TH1Names); + histos[hPhi] = registry.add(Form("%d/phi", run), "", {HistType::kTH1D, {phiAxis}}); + histos[hEta] = registry.add(Form("%d/eta", run), "", {HistType::kTH1D, {etaAxis}}); + histos[hVtxZ] = registry.add(Form("%d/vtxz", run), "", {HistType::kTH1D, {vtxAxis}}); + histos[hMult] = registry.add(Form("%d/mult", run), "", {HistType::kTH1D, {nchAxis}}); + histos[hCent] = registry.add(Form("%d/cent", run), "", {HistType::kTH1D, {centAxis}}); + if (cfgFillFlowRunByRun) { + std::vector> profiles(kCount_TProfileNames); + profiles[pfCorr22] = registry.add(Form("%d/corr22", run), "", {HistType::kTProfile, {(cfgUseNch) ? nchAxis : centAxis}}); + tpfsList.insert(std::make_pair(run, profiles)); + } + histos[hEventSel] = registry.add(Form("%d/eventSel", run), "Number of Events;; Counts", {HistType::kTH1D, {{11, 0.5, 11.5}}}); + histos[hEventSel]->GetXaxis()->SetBinLabel(kFilteredEvent, "Filtered event"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kSel8, "sel8"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kOccupancy, "occupancy"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kTVXTRD, "kTVXinTRD"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kNoSamebunchPU, "kNoSameBunchPileup"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kZVtxFT0PV, "kIsGoodZvtxFT0vsPV"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kNoCollTRStd, "kNoCollInTimeRangeStandard"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kVtxITSTPC, "kIsVertexITSTPC"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kGoodITSLayers, "kIsGoodITSLayersAll"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kMultCuts, "after Mult cuts"); + histos[hEventSel]->GetXaxis()->SetBinLabel(kTrackCent, "has track + within cent"); + th1sList.insert(std::make_pair(run, histos)); + std::vector> histos3d(kCount_TH3Names); + histos3d[hNUAref] = registry.add(Form("%d/phi_eta_vtxz_ref", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + th3sList.insert(std::make_pair(run, histos3d)); + return; + } + + void addConfigObjectsToObjArray(TObjArray* oba, const std::vector& configs) + { + const auto shapeSel = getShapeSel(); + const int nQnOutputBins = getNQnOutputBins(); + for (auto it = configs.begin(); it != configs.end(); ++it) { + for (int jese = 0; jese < nQnOutputBins; ++jese) { + std::string name = Form("%s_%d_%s", shapeSel.c_str(), jese, it->Head.c_str()); + std::string title = it->Head + std::string("_ese"); + oba->Add(new TNamed(name.c_str(), title.c_str())); + } + } + } + + template + void fillOutputContainers(const float& centmult, const double& rndm, const int& qPtmp, const int& run = 0) + { + const auto shapeSel = getShapeSel(); + + for (uint l_ind = 0; l_ind < corrconfigs.size(); ++l_ind) { + auto dnx = fGFW->Calculate(corrconfigs.at(l_ind), 0, kTRUE).real(); + if (dnx == 0) + continue; + + auto val = fGFW->Calculate(corrconfigs.at(l_ind), 0, kFALSE).real() / dnx; + if (std::abs(val) >= 1.) + continue; + + std::string profName = Form("%s_%d_%s", shapeSel.c_str(), qPtmp, corrconfigs.at(l_ind).Head.c_str()); + + if constexpr (dt == kGen) { + fFCgen->FillProfile(profName.c_str(), + centmult, + val, + cfgUseMultiplicityFlowWeights ? dnx : 1.0, + rndm); + } else { + fFC->FillProfile(profName.c_str(), + centmult, + val, + cfgUseMultiplicityFlowWeights ? dnx : 1.0, + rndm); + + if (cfgRunByRun && cfgFillFlowRunByRun && l_ind == 0) { + tpfsList[run][pfCorr22]->Fill(centmult, + val, + cfgUseMultiplicityFlowWeights ? dnx : 1.0); + } + } + } + } + + struct XAxis { + float centrality; + int64_t multiplicity; + double time; + }; + + struct AcceptedTracks { + int nPos; + int nNeg; + int nFull; + int nMid; + }; + + struct InTaskTPCQVector { + float qVectTPCPos[2] = {0.f, 0.f}; // Always computed + float qVectTPCNeg[2] = {0.f, 0.f}; // Always computed + float qVectTPCAll[2] = {0.f, 0.f}; // Always computed + + int nTrkTPCPos = 0; + int nTrkTPCNeg = 0; + int nTrkTPCAll = 0; + + std::vector trkTPCPosLabel{}; + std::vector trkTPCNegLabel{}; + std::vector trkTPCAllLabel{}; + }; + + template + bool selTrack(const TrackType& track) + { + if (track.pt() < cfgMinPtOnTPC) + return false; + if (track.pt() > cfgMaxPtOnTPC) + return false; + if (!track.passedITSNCls()) + return false; + if (!track.passedITSChi2NDF()) + return false; + if (!track.passedITSHits()) + return false; + if (!track.passedTPCCrossedRowsOverNCls()) + return false; + if (!track.passedTPCChi2NDF()) + return false; + if (!track.passedDCAxy()) + return false; + if (!track.passedDCAz()) + return false; + + return true; + } + + template + InTaskTPCQVector calcQVec(const Nmode nMode, const TrackType& tracks, const TCollision& collision) + { + InTaskTPCQVector qvec; + + double nTrkSel = 0.; + for (auto const& trk : tracks) { + if (!selTrack(trk)) { + continue; + } + if (trk.eta() > cfgEtaMax) { + continue; + } + if (trk.eta() < cfgEtaMin) { + continue; + } + qvec.qVectTPCAll[0] += trk.pt() * std::cos(trk.phi() * nMode); + qvec.qVectTPCAll[1] += trk.pt() * std::sin(trk.phi() * nMode); + qvec.trkTPCAllLabel.push_back(trk.globalIndex()); + qvec.nTrkTPCAll++; + nTrkSel++; + if (std::abs(trk.eta()) < kTPCSubeventEtaGapHalfWidth) { + continue; + } + if (cfgQvecQA) { + registry.fill(HIST("qvecQA/ChTracks"), trk.pt(), trk.eta(), trk.phi()); + } + + if (trk.eta() > 0 && std::fabs(trk.eta()) < cfgQnTrkAbsEtaMax) { + // In qVectorsTable this branch is additionally guarded by useDetector["QvectorTPCposs"] || useDetector["QvectorBPoss"]. + // Here TPCpos is always computed because the downstream ESE selector can require it. + qvec.qVectTPCPos[0] += trk.pt() * std::cos(trk.phi() * nMode); + qvec.qVectTPCPos[1] += trk.pt() * std::sin(trk.phi() * nMode); + qvec.trkTPCPosLabel.push_back(trk.globalIndex()); + qvec.nTrkTPCPos++; + } else if (trk.eta() < 0 && std::fabs(trk.eta()) < cfgQnTrkAbsEtaMax) { + // In qVectorsTable this branch is additionally guarded by useDetector["QvectorTPCnegs"] || useDetector["QvectorBNegs"]. + // Here TPCneg is always computed because the downstream ESE selector can require it. + qvec.qVectTPCNeg[0] += trk.pt() * std::cos(trk.phi() * nMode); + qvec.qVectTPCNeg[1] += trk.pt() * std::sin(trk.phi() * nMode); + qvec.trkTPCNegLabel.push_back(trk.globalIndex()); + qvec.nTrkTPCNeg++; + } + } + + if (cfgQvecQA) { + registry.fill(HIST("qvecQA/CountEvt"), collision.centFT0C(), tracks.size(), nTrkSel, collision.multNTracksPV()); + registry.fill(HIST("qvecQA/QxQy"), qvec.qVectTPCAll[0], qvec.qVectTPCAll[1], qvec.qVectTPCNeg[0], qvec.qVectTPCNeg[1], qvec.qVectTPCPos[0], qvec.qVectTPCPos[1]); + } + + return qvec; + } + + float computeqnVec(InTaskTPCQVector const& qvec, bool useNegativeEtaHalf) + { + if (qvec.nTrkTPCPos <= 0 || qvec.nTrkTPCNeg <= 0) { + return -1.f; + } + + if (useNegativeEtaHalf) { + return std::hypot(qvec.qVectTPCNeg[0], qvec.qVectTPCNeg[1]) / std::sqrt(static_cast(qvec.nTrkTPCNeg)); + } + return std::hypot(qvec.qVectTPCPos[0], qvec.qVectTPCPos[1]) / std::sqrt(static_cast(qvec.nTrkTPCPos)); + } + + /// \return the 1-d qn-vector separator to 2-d + std::vector> getQnBinSeparator2D(std::vector flat, const int numQnBins = 10) + { + size_t nBins = numQnBins + 1; + + if (flat.empty() || flat.size() % nBins != 0) { + LOGP(error, "ConfQnBinSeparator size = {} is not divisible by {}", + flat.size(), nBins); + return {{kInvalidQnSeparator, kInvalidQnSeparator}}; + } + + size_t nCent = flat.size() / nBins; + std::vector> res(nCent, std::vector(nBins)); + + for (size_t i = 0; i < nCent; ++i) { + for (size_t j = 0; j < nBins; ++j) { + res[i][j] = flat[i * nBins + j]; + } + } + return res; + } + + /// Get the bin number of qn-vector(FT0C) of an event + /// \param centBinWidth centrality bin width, example: per 1%, per 10% ... + /// \return bin number of qn-vector of the event + // add a param : bool doFillHisto ? + int myqnBin(float centrality, float centMax, float qn, std::vector qnBinSprt, const int numQnBins, float centBinWidth = 1.f) + { + auto twoDSeparator = getQnBinSeparator2D(std::move(qnBinSprt), numQnBins); + if (twoDSeparator.empty() || twoDSeparator[0][0] == kInvalidQnSeparator) { + LOGP(warning, "ConfQnBinSeparator not set, using default fallback!"); + return kInvalidQnBin; // safe fallback + } + + int qnBin = kInvalidQnBin; + int mycentBin = static_cast(centrality / centBinWidth); + if (mycentBin >= static_cast(centMax / centBinWidth)) + return qnBin; + + if (mycentBin > static_cast(twoDSeparator.size()) - 1) + return qnBin; + + for (int iqn(0); iqn < static_cast(twoDSeparator[mycentBin].size()) - 1; ++iqn) { + if (qn > twoDSeparator[mycentBin][iqn] && qn <= twoDSeparator[mycentBin][iqn + 1]) { + qnBin = iqn; + break; + } else { + continue; + } + } + + return qnBin; + } + + template + void processCollision(const TCollision& collision, const TTracks& tracks, const XAxis& xaxis, const int& run, const int& qPtmp) + { + if (tracks.size() < 1) + return; + if (dt != kGen && xaxis.centrality >= 0 && + (xaxis.centrality < o2::analysis::gfwflowflucpp::centbinning.front() || + xaxis.centrality > o2::analysis::gfwflowflucpp::centbinning.back())) + return; + if (xaxis.multiplicity < cfgFixedMultMin || xaxis.multiplicity > cfgFixedMultMax) + return; + + if (dt != kGen) { + if (cfgFillQA) { + registry.fill(HIST("eventQA/eventSel"), kTrackCent); + } + if (cfgRunByRun) + th1sList[run][hEventSel]->Fill(kTrackCent); + } + + if (cfgFillQA && xaxis.centrality >= 0) + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + if (cfgFillQA) + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + + float vtxz = collision.posZ(); + if (dt != kGen && cfgRunByRun) { + th1sList[run][hVtxZ]->Fill(vtxz); + th1sList[run][hMult]->Fill(xaxis.multiplicity); + th1sList[run][hCent]->Fill(xaxis.centrality); + } + + fGFW->Clear(); + float lRandom = fRndm->Rndm(); + + AcceptedTracks acceptedTracks{0, 0, 0, 0}; + + for (const auto& track : tracks) { + processTrack(track, vtxz, xaxis.multiplicity, run, acceptedTracks); + } + + if (cfgConsistentEventFlag & kRequireBothEtaSides) + if (!acceptedTracks.nPos || !acceptedTracks.nNeg) + return; + if (cfgConsistentEventFlag & kRequireFullFourParticleTracks) + if (acceptedTracks.nFull < kMinTracksForFourParticleCorrelation) + return; + if (cfgConsistentEventFlag & kRequireTwoTracksInBothEtaSides) + if (acceptedTracks.nPos < kMinTracksPerEtaSideForGapCorrelation || + acceptedTracks.nNeg < kMinTracksPerEtaSideForGapCorrelation) + return; + if (cfgConsistentEventFlag & kRequireTwoTracksInThreeEtaRegions) + if (acceptedTracks.nPos < kMinTracksPerEtaRegionForThreeSubevents || + acceptedTracks.nMid < kMinTracksPerEtaRegionForThreeSubevents || + acceptedTracks.nNeg < kMinTracksPerEtaRegionForThreeSubevents) + return; + + fillOutputContainers
(cfgUseNch ? static_cast(xaxis.multiplicity) : xaxis.centrality, + lRandom, qPtmp, run); + } + + template + void processGenCollision(const TCollision& collision, const TParticles& particles, const int& mcCollisionId, const XAxis& xaxis, const int& run, const int& qPtmp) + { + if (xaxis.multiplicity < cfgFixedMultMin || xaxis.multiplicity > cfgFixedMultMax) + return; + + if (cfgFillQA && xaxis.centrality >= 0) + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + if (cfgFillQA) + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + + fGFW->Clear(); + float lRandom = fRndm->Rndm(); + float vtxz = collision.posZ(); + + AcceptedTracks acceptedTracks{0, 0, 0, 0}; + for (const auto& particle : particles) { + if (particle.mcCollisionId() != mcCollisionId) + continue; + processTrack(particle, vtxz, xaxis.multiplicity, run, acceptedTracks); + } + + if (cfgConsistentEventFlag & kRequireBothEtaSides) + if (!acceptedTracks.nPos || !acceptedTracks.nNeg) + return; + if (cfgConsistentEventFlag & kRequireFullFourParticleTracks) + if (acceptedTracks.nFull < kMinTracksForFourParticleCorrelation) + return; + if (cfgConsistentEventFlag & kRequireTwoTracksInBothEtaSides) + if (acceptedTracks.nPos < kMinTracksPerEtaSideForGapCorrelation || + acceptedTracks.nNeg < kMinTracksPerEtaSideForGapCorrelation) + return; + if (cfgConsistentEventFlag & kRequireTwoTracksInThreeEtaRegions) + if (acceptedTracks.nPos < kMinTracksPerEtaRegionForThreeSubevents || + acceptedTracks.nMid < kMinTracksPerEtaRegionForThreeSubevents || + acceptedTracks.nNeg < kMinTracksPerEtaRegionForThreeSubevents) + return; + + fillOutputContainers(cfgUseNch ? static_cast(xaxis.multiplicity) : xaxis.centrality, + lRandom, qPtmp, run); + } + + bool isStable(int pdg) + { + if (std::abs(pdg) == PDG_t::kPiPlus) + return true; + if (std::abs(pdg) == PDG_t::kKPlus) + return true; + if (std::abs(pdg) == PDG_t::kProton) + return true; + if (std::abs(pdg) == PDG_t::kElectron) + return true; + if (std::abs(pdg) == PDG_t::kMuonMinus) + return true; + return false; + } + + template + void fillAcceptedTracks(const TTrack& track, AcceptedTracks& acceptedTracks) + { + if (posRegionIndex >= 0 && track.eta() > o2::analysis::gfwflowflucpp::regions.GetEtaMin()[posRegionIndex] && track.eta() < o2::analysis::gfwflowflucpp::regions.GetEtaMax()[posRegionIndex]) + ++acceptedTracks.nPos; + if (negRegionIndex >= 0 && track.eta() > o2::analysis::gfwflowflucpp::regions.GetEtaMin()[negRegionIndex] && track.eta() < o2::analysis::gfwflowflucpp::regions.GetEtaMax()[negRegionIndex]) + ++acceptedTracks.nNeg; + if (fullRegionIndex >= 0 && track.eta() > o2::analysis::gfwflowflucpp::regions.GetEtaMin()[fullRegionIndex] && track.eta() < o2::analysis::gfwflowflucpp::regions.GetEtaMax()[fullRegionIndex]) + ++acceptedTracks.nFull; + if (midRegionIndex >= 0 && track.eta() > o2::analysis::gfwflowflucpp::regions.GetEtaMin()[midRegionIndex] && track.eta() < o2::analysis::gfwflowflucpp::regions.GetEtaMax()[midRegionIndex]) + ++acceptedTracks.nMid; + } + + template + inline void processTrack(TTrack const& track, const float& vtxz, const int& multiplicity, const int& run, AcceptedTracks& acceptedTracks) + { + if constexpr (framework::has_type_v) { + if (track.mcParticleId() < 0 || !(track.has_mcParticle())) + return; + + auto mcParticle = track.mcParticle(); + if (!mcParticle.isPhysicalPrimary()) + return; + if (!isStable(mcParticle.pdgCode())) + return; + if (cfgFillQA && cfgIsMC) { + fillTrackQA(track, vtxz); + registry.fill(HIST("trackQA/before/nch_pt"), multiplicity, track.pt()); + } + if (!trackSelected(track)) + return; + + if (cfgFillWeights) { + fillWeights(track, vtxz, run); + } else { + fillGFW(track, vtxz); + fillAcceptedTracks(track, acceptedTracks); + } + + if (cfgFillQA) { + fillTrackQA(track, vtxz); + registry.fill(HIST("MCReco/trackQA/pt_eta"), track.pt(), track.eta()); + registry.fill(HIST("trackQA/after/nch_pt"), multiplicity, track.pt()); + if (cfgRunByRun) { + th1sList[run][hPhi]->Fill(track.phi()); + th1sList[run][hEta]->Fill(track.eta()); + } + } + + } else if constexpr (framework::has_type_v) { + if (!track.isPhysicalPrimary() || !isStable(track.pdgCode())) + return; + if (std::abs(track.eta()) > cfgEta) + return; + if (track.pt() < cfgPtmin || track.pt() > cfgPtmax) + return; + + fillGFW(track, vtxz); + fillAcceptedTracks(track, acceptedTracks); + if (cfgFillQA && cfgIsMC) { + fillTrackQA(track, vtxz); + registry.fill(HIST("MCGen/trackQA/pt_eta"), track.pt(), track.eta()); + registry.fill(HIST("MCGen/trackQA/nch_pt"), multiplicity, track.pt()); + } + } else { + if (cfgFillQA) { + fillTrackQA(track, vtxz); + registry.fill(HIST("trackQA/before/nch_pt"), multiplicity, track.pt()); + } + if (!trackSelected(track)) + return; + + if (cfgFillWeights) { + fillWeights(track, vtxz, run); + } else { + fillGFW(track, vtxz); + fillAcceptedTracks(track, acceptedTracks); + } + if (cfgFillQA) { + fillTrackQA(track, vtxz); + registry.fill(HIST("trackQA/after/nch_pt"), multiplicity, track.pt()); + if (cfgRunByRun) { + th1sList[run][hPhi]->Fill(track.phi()); + th1sList[run][hEta]->Fill(track.eta()); + th3sList[run][hNUAref]->Fill(track.phi(), track.eta(), vtxz, getAcceptance(track, vtxz)); + } + } + } + return; + } + + template + inline void fillGFW(const TTrack& track, const double& vtxz) + { + bool withinPtRef = (track.pt() > o2::analysis::gfwflowflucpp::ptreflow && track.pt() < o2::analysis::gfwflowflucpp::ptrefup); + bool withinPtPOI = (track.pt() > o2::analysis::gfwflowflucpp::ptpoilow && track.pt() < o2::analysis::gfwflowflucpp::ptpoiup); + if (!withinPtPOI && !withinPtRef) + return; + double weff = (dt == kGen) ? 1. : getEfficiency(track); + if (weff < 0) + return; + + double wacc = (dt == kGen) ? 1. : getAcceptance(track, vtxz); + if (withinPtRef) + fGFW->Fill(track.eta(), fSecondAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 1); + if (withinPtPOI) + fGFW->Fill(track.eta(), fSecondAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 2); + if (withinPtRef && withinPtPOI) + fGFW->Fill(track.eta(), fSecondAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 4); + return; + } + + template + inline void fillTrackQA(const TTrack& track, const float vtxz) + { + if constexpr (dt == kGen) { + registry.fill(HIST("MCGen/trackQA/phi_eta_vtxZ"), track.phi(), track.eta(), vtxz); + registry.fill(HIST("MCGen/trackQA/pt_ref"), track.pt()); + registry.fill(HIST("MCGen/trackQA/pt_poi"), track.pt()); + } else { + double wacc = getAcceptance(track, vtxz); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ"), track.phi(), track.eta(), vtxz, (ft == kAfter) ? wacc : 1.0); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + + if (ft == kAfter) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_ref"), track.pt()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_poi"), track.pt()); + } + } + } + + template + float getCentrality(const TCollision& collision) + { + switch (cfgCentEstimator) { + case kCentFT0C: + return collision.centFT0C(); + case kCentFT0CVariant1: + return collision.centFT0CVariant1(); + case kCentFT0M: + return collision.centFT0M(); + case kCentFV0A: + return collision.centFV0A(); + case kCentNTPV: + return collision.centNTPV(); + case kCentNGlobal: + return collision.centNGlobal(); + default: + return collision.centFT0C(); + } + } + + template + inline void fillEventQA(const TCollision& collision, XAxis xaxis) + { + if constexpr (framework::has_type_v) { + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), xaxis.multiplicity); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("centT0M_centT0C"), collision.centFT0C(), collision.centFT0M()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("centV0A_centT0C"), collision.centFT0C(), collision.centFV0A()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("centGlobal_centT0C"), collision.centFT0C(), collision.centNGlobal()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("centNTPV_centT0C"), collision.centFT0C(), collision.centNTPV()); + } + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), xaxis.multiplicity); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), xaxis.multiplicity); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), xaxis.multiplicity); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); + if (cfgTimeDependent) { + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multiplicity_time"), xaxis.time, xaxis.multiplicity); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multT0C_time"), xaxis.time, collision.multFT0C()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multT0A_time"), xaxis.time, collision.multFT0A()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multV0A_time"), xaxis.time, collision.multFV0A()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multPV_time"), xaxis.time, collision.multNTracksPV()); + } + return; + } + + double getTimeSinceStartOfFill(uint64_t timestamp, int firstRun) + { + auto runDuration = ccdb->getRunDuration(firstRun); + uint64_t tsSOF = runDuration.first; + uint64_t diff = timestamp - tsSOF; + return static_cast(diff) / 3600000.0; + } + + void fillQnEventCounter(float count) + { + const int qnHarmonic = static_cast(cfgQnSelectionHarmonic); + + if (qnHarmonic == EllipticQVectorHarmonic) { + registry.fill(HIST("mq2/eventcounter"), count); + } else if (qnHarmonic == TriangularQVectorHarmonic) { + registry.fill(HIST("mq3/eventcounter"), count); + } + } + + void fillQnCalibrationHistograms(float centrality, float multiplicity, float qnPos, float qnNeg) + { + const int qnHarmonic = static_cast(cfgQnSelectionHarmonic); + + if (qnHarmonic == EllipticQVectorHarmonic) { + registry.fill(HIST("mq2/h2_cent_q2_etapos"), centrality, qnPos); + registry.fill(HIST("mq2/h2_cent_q2_etaneg"), centrality, qnNeg); + registry.fill(HIST("mq2/h2_mult_q2_etapos"), multiplicity, qnPos); + registry.fill(HIST("mq2/h2_mult_q2_etaneg"), multiplicity, qnNeg); + } else if (qnHarmonic == TriangularQVectorHarmonic) { + registry.fill(HIST("mq3/h2_cent_q3_etapos"), centrality, qnPos); + registry.fill(HIST("mq3/h2_cent_q3_etaneg"), centrality, qnNeg); + registry.fill(HIST("mq3/h2_mult_q3_etapos"), multiplicity, qnPos); + registry.fill(HIST("mq3/h2_mult_q3_etaneg"), multiplicity, qnNeg); + } + } + + void processData(soa::Filtered>::iterator const& collision, + aod::BCsWithTimestamps const&, GFWTracks const& tracks) + { + auto bc = collision.bc_as(); + int run = bc.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + if (cfgRunByRun) { + if (std::find(runNumbers.begin(), runNumbers.end(), run) == runNumbers.end()) { + LOGF(info, "Creating histograms for run %d", run); + createRunByRunHistograms(run); + runNumbers.push_back(run); + } + if (!cfgFillWeights) + loadCorrections(bc); + } + } + if (!cfgFillWeights && !cfgRunByRun) + loadCorrections(bc); + + registry.fill(HIST("eventQA/eventSel"), kFilteredEvent); + if (cfgRunByRun) + th1sList[run][hEventSel]->Fill(kFilteredEvent); + + if (!collision.sel8()) + return; + + registry.fill(HIST("eventQA/eventSel"), kSel8); + if (cfgRunByRun) + th1sList[run][hEventSel]->Fill(kSel8); + + if (cfgDoOccupancySel) { + int occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < 0 || occupancy > cfgOccupancySelection) + return; + } + + registry.fill(HIST("eventQA/eventSel"), kOccupancy); + if (cfgRunByRun) + th1sList[run][hEventSel]->Fill(kOccupancy); + + const XAxis xaxis{ + getCentrality(collision), + collision.multNTracksPV(), + (cfgTimeDependent) ? getTimeSinceStartOfFill(bc.timestamp(), *firstRunOfCurrentFill) : -1.0}; + + if (cfgTimeDependent && run == *firstRunOfCurrentFill && + firstRunOfCurrentFill != o2::analysis::gfwflowflucpp::firstRunsOfFill.end() - 1) + ++firstRunOfCurrentFill; + + if (cfgFillQA) + fillEventQA(collision, xaxis); + + registry.fill(HIST("eventQA/before/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/before/multiplicity"), xaxis.multiplicity); + + if (!eventSelected(collision, xaxis.multiplicity, xaxis.centrality, run)) + return; + + if (cfgFillQA) + fillEventQA(collision, xaxis); + + int qPtmp = 0; + if (!static_cast(cfgBypassQnSelection)) { + const auto qvecTPC = calcQVec(static_cast(cfgQnSelectionHarmonic), tracks, collision); + float qn = computeqnVec(qvecTPC, cfgUseNegativeEtaHalfForq2); + if (qn < 0) + return; + + qPtmp = myqnBin(cfgEvtSelCent ? xaxis.centrality : xaxis.multiplicity, + cfgCentMax, qn, qnBinSeparator, cfgNumQnBins); + if (qPtmp < 0) + return; + } + + processCollision(collision, tracks, xaxis, run, qPtmp); + } + PROCESS_SWITCH(FlowFlucGfwPp, processData, "Process analysis for non-derived data", false); + + void processq2(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, GFWTracks const& tracks) + { + float count{0.5}; + fillQnEventCounter(count++); + if (!collision.sel8()) { + return; + } + fillQnEventCounter(count++); + if (cfgDoOccupancySel) { + int occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < 0 || occupancy > cfgOccupancySelection) { + return; + } + } + fillQnEventCounter(count++); + + const XAxis xaxis{getCentrality(collision), collision.multNTracksPV(), -1.0}; + if (!eventSelected(collision, xaxis.multiplicity, xaxis.centrality, -1)) + return; + + const auto centr = xaxis.centrality; + const auto multi = xaxis.multiplicity; + const auto qvecTPC = calcQVec(static_cast(cfgQnSelectionHarmonic), tracks, collision); + const auto qvecPos = computeqnVec(qvecTPC, false); + const auto qvecNeg = computeqnVec(qvecTPC, true); + + if (!std::isfinite(qvecPos) || !std::isfinite(qvecNeg) || qvecPos < 0 || qvecNeg < 0) { + return; + } + + fillQnEventCounter(count++); + fillQnCalibrationHistograms(centr, multi, qvecPos, qvecNeg); + } + PROCESS_SWITCH(FlowFlucGfwPp, processq2, "Process analysis for filling q_n-vector calibration histograms", true); + + void processMC(soa::Filtered>::iterator const& collision, + aod::BCsWithTimestamps const&, GFWTracksMC const& tracks, aod::McCollisions const&, aod::McParticles const& mcParticles) + { + auto bc = collision.bc_as(); + int run = bc.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + if (cfgRunByRun) { + if (std::find(runNumbers.begin(), runNumbers.end(), run) == runNumbers.end()) { + LOGF(info, "Creating histograms for run %d", run); + createRunByRunHistograms(run); + runNumbers.push_back(run); + } + if (!cfgFillWeights) + loadCorrections(bc); + } + } + if (!cfgFillWeights && !cfgRunByRun) + loadCorrections(bc); + + registry.fill(HIST("eventQA/eventSel"), kFilteredEvent); + if (cfgRunByRun) + th1sList[run][hEventSel]->Fill(kFilteredEvent); + + if (!collision.sel8()) + return; + + registry.fill(HIST("eventQA/eventSel"), kSel8); + if (cfgRunByRun) + th1sList[run][hEventSel]->Fill(kSel8); + + if (cfgDoOccupancySel) { + int occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < 0 || occupancy > cfgOccupancySelection) + return; + } + + registry.fill(HIST("eventQA/eventSel"), kOccupancy); + if (cfgRunByRun) + th1sList[run][hEventSel]->Fill(kOccupancy); + + const XAxis xaxis{ + getCentrality(collision), + collision.multNTracksPV(), + (cfgTimeDependent) ? getTimeSinceStartOfFill(bc.timestamp(), *firstRunOfCurrentFill) : -1.0}; + + if (cfgTimeDependent && run == *firstRunOfCurrentFill && + firstRunOfCurrentFill != o2::analysis::gfwflowflucpp::firstRunsOfFill.end() - 1) + ++firstRunOfCurrentFill; + + if (cfgFillQA) + fillEventQA(collision, xaxis); + + registry.fill(HIST("eventQA/before/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/before/multiplicity"), xaxis.multiplicity); + + if (!eventSelected(collision, xaxis.multiplicity, xaxis.centrality, run)) + return; + + if (cfgFillQA) + fillEventQA(collision, xaxis); + + processCollision(collision, tracks, xaxis, run, 0); + + if (!collision.has_mcCollision()) + return; + + const auto genCollision = collision.template mcCollision_as(); + processGenCollision(genCollision, mcParticles, collision.mcCollisionId(), xaxis, run, 0); + } + PROCESS_SWITCH(FlowFlucGfwPp, processMC, "Process analysis for Monte-Carlo data", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/Flow/Tasks/flowGfwEse.cxx b/PWGCF/Flow/Tasks/flowGfwEse.cxx index 307168d24d3..af7e0bb725f 100644 --- a/PWGCF/Flow/Tasks/flowGfwEse.cxx +++ b/PWGCF/Flow/Tasks/flowGfwEse.cxx @@ -618,7 +618,7 @@ struct FlowGfwEse { } template - double getAcceptance(TTrack track, const double& vtxz) + double getAcceptance(const TTrack& track, const double& vtxz) { double wacc = 1; if (cfg.mAcceptance) @@ -627,7 +627,7 @@ struct FlowGfwEse { } template - double getEfficiency(TTrack track) + double getEfficiency(const TTrack& track) { double eff = 1.; if (cfg.mEfficiency) @@ -639,7 +639,7 @@ struct FlowGfwEse { } template - bool eventSelected(TCollision collision, const int& multTrk, const float& centrality, const int& run) + bool eventSelected(const TCollision& collision, const int& multTrk, const float& centrality, const int& run) { if (cfgTVXinTRD) { if (collision.alias_bit(kTVXinTRD)) { @@ -742,7 +742,7 @@ struct FlowGfwEse { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { if (cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) return false; @@ -755,7 +755,7 @@ struct FlowGfwEse { }; template - void fillWeights(const TTrack track, const double vtxz, const int& run) + void fillWeights(const TTrack& track, const double vtxz, const int& run) { if (cfgRunByRun) th3sList[run][hNUAref]->Fill(track.phi(), track.eta(), vtxz); @@ -850,7 +850,7 @@ struct FlowGfwEse { }; template - void processCollision(TCollision collision, TTracks tracks, const XAxis& xaxis, const int& run) + void processCollision(const TCollision& collision, const TTracks& tracks, const XAxis& xaxis, const int& run) { if (tracks.size() < 1) return; @@ -950,7 +950,7 @@ struct FlowGfwEse { } template - void fillAcceptedTracks(TTrack track, AcceptedTracks& acceptedTracks) + void fillAcceptedTracks(const TTrack& track, AcceptedTracks& acceptedTracks) { if (posRegionIndex >= 0 && track.eta() > o2::analysis::gfwflowese::regions.GetEtaMin()[posRegionIndex] && track.eta() < o2::analysis::gfwflowese::regions.GetEtaMax()[posRegionIndex]) ++acceptedTracks.nPos; @@ -1038,7 +1038,7 @@ struct FlowGfwEse { } template - inline void fillGFW(TTrack track, const double& vtxz, DensityCorr densitycorrections) + inline void fillGFW(const TTrack& track, const double& vtxz, DensityCorr densitycorrections) { bool withinPtRef = (track.pt() > o2::analysis::gfwflowese::ptreflow && track.pt() < o2::analysis::gfwflowese::ptrefup); bool withinPtPOI = (track.pt() > o2::analysis::gfwflowese::ptpoilow && track.pt() < o2::analysis::gfwflowese::ptpoiup); @@ -1070,7 +1070,7 @@ struct FlowGfwEse { } template - inline void fillPtSums(TTrack track) + inline void fillPtSums(const TTrack& track) { double weff = (dt == kGen) ? 1. : getEfficiency(track); if (weff < 0) @@ -1081,7 +1081,7 @@ struct FlowGfwEse { } template - inline void fillTrackQA(TTrack track, const float vtxz) + inline void fillTrackQA(const TTrack& track, const float vtxz) { if constexpr (dt == kGen) { registry.fill(HIST("MCGen/trackQA/phi_eta_vtxZ"), track.phi(), track.eta(), vtxz); @@ -1106,7 +1106,7 @@ struct FlowGfwEse { } template - float getCentrality(TCollision collision) + float getCentrality(const TCollision& collision) { switch (cfgCentEstimator) { case kCentFT0C: @@ -1129,7 +1129,7 @@ struct FlowGfwEse { } template - inline void fillEventQA(TCollision collision, XAxis xaxis) + inline void fillEventQA(const TCollision& collision, XAxis xaxis) { if constexpr (framework::has_type_v) { registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), xaxis.multiplicity); diff --git a/PWGCF/Flow/Tasks/flowGfwOmegaXi.cxx b/PWGCF/Flow/Tasks/flowGfwOmegaXi.cxx index d57a69bf3f0..1f886490fcb 100644 --- a/PWGCF/Flow/Tasks/flowGfwOmegaXi.cxx +++ b/PWGCF/Flow/Tasks/flowGfwOmegaXi.cxx @@ -110,14 +110,21 @@ struct FlowGfwOmegaXi { std::string prefix = "v0BuilderOpts"; // topological cut for V0 O2_DEFINE_CONFIGURABLE(cfgv0_radius, float, 5.0f, "minimum decay radius") + O2_DEFINE_CONFIGURABLE(cfgv0_radiusmax, float, 100.0f, "maximum decay radius") O2_DEFINE_CONFIGURABLE(cfgv0_v0cospa, float, 0.995f, "minimum cosine of pointing angle") - O2_DEFINE_CONFIGURABLE(cfgv0_dcadautopv, float, 0.1f, "minimum daughter DCA to PV") + O2_DEFINE_CONFIGURABLE(cfgv0_dcak0daupitopv, float, 0.01f, "minimum daughter pion DCA to PV") + O2_DEFINE_CONFIGURABLE(cfgv0_dcadaupitopv, float, 0.01f, "minimum daughter pion DCA to PV") + O2_DEFINE_CONFIGURABLE(cfgv0_dcadauprtopv, float, 0.1f, "minimum daughter proton DCA to PV") O2_DEFINE_CONFIGURABLE(cfgv0_dcav0dau, float, 0.5f, "maximum DCA among V0 daughters") - O2_DEFINE_CONFIGURABLE(cfgv0_mk0swindow, float, 0.1f, "Invariant mass window of K0s") - O2_DEFINE_CONFIGURABLE(cfgv0_mlambdawindow, float, 0.04f, "Invariant mass window of lambda") + O2_DEFINE_CONFIGURABLE(cfgv0_mk0slow, float, 0.4f, "minimun invariant mass cut of K0s") + O2_DEFINE_CONFIGURABLE(cfgv0_mk0shigh, float, 0.6f, "maximum invariant mass cut of K0s") + O2_DEFINE_CONFIGURABLE(cfgv0_mlambdalow, float, 1.08f, "minimun invariant mass cut of lambda") + O2_DEFINE_CONFIGURABLE(cfgv0_mlambdahigh, float, 1.16f, "maximum invariant mass cut of lambda") O2_DEFINE_CONFIGURABLE(cfgv0_ArmPodocut, float, 0.2f, "Armenteros Podolski cut for K0") O2_DEFINE_CONFIGURABLE(cfgv0_compmassrejLambda, float, 0.01f, "competing mass rejection of lambda") O2_DEFINE_CONFIGURABLE(cfgv0_compmassrejK0s, float, 0.005f, "competing mass rejection of K0s") + O2_DEFINE_CONFIGURABLE(cfgv0_ctauK0s, float, 20, "Max ctau cut of K0s") + O2_DEFINE_CONFIGURABLE(cfgv0_ctauLambda, float, 30, "Max ctau cut of Lambda") } v0BuilderOpts; struct : ConfigurableGroup { @@ -158,9 +165,11 @@ struct FlowGfwOmegaXi { O2_DEFINE_CONFIGURABLE(cfgCutPtPIDbachMin, float, 0.15f, "Minimal pT for daughter PID") O2_DEFINE_CONFIGURABLE(cfgCutPtPIDdauLaPrMin, float, 0.15f, "Minimal pT for daughter PID") O2_DEFINE_CONFIGURABLE(cfgCutPtPIDdauLaPiMin, float, 0.15f, "Minimal pT for daughter PID") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "Minimal pT for daughter PID") + O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "Maximal DCAz for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, float, 0.1f, "Maximal DCAxy for tracks") // track quality selections for daughter track - O2_DEFINE_CONFIGURABLE(cfgITSNCls, int, 3, "check minimum number of ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgMaxITSNCls, int, 5, "check maximum number of ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgMinITSNCls, int, 0, "check minimum number of ITS clusters") O2_DEFINE_CONFIGURABLE(cfgChITSNCls, int, 5, "check minimum number of ITS clusters") O2_DEFINE_CONFIGURABLE(cfgTPCNCls, int, 50, "check minimum number of TPC hits") O2_DEFINE_CONFIGURABLE(cfgTPCCrossedRows, int, 70, "check minimum number of TPC crossed rows") @@ -178,7 +187,8 @@ struct FlowGfwOmegaXi { O2_DEFINE_CONFIGURABLE(cfgDoIsGoodITSLayersAll, bool, true, "check kIsGoodITSLayersAll") O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 500, "High cut on TPC occupancy") O2_DEFINE_CONFIGURABLE(cfgDoZResAndNumContribCut, bool, true, "check kNoTimeFrameBorder") - O2_DEFINE_CONFIGURABLE(cfgDoMultPVCut, bool, true, "do multNTracksPV vs cent cut") + O2_DEFINE_CONFIGURABLE(cfgDoMultPVCut, bool, false, "do multNTracksPV vs cent cut") + O2_DEFINE_CONFIGURABLE(cfgDoMultTPCCut, bool, false, "do multNTracksTPC vs cent cut") O2_DEFINE_CONFIGURABLE(cfgMultPVCut, std::vector, (std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}), "Used MultPVCut function parameter") O2_DEFINE_CONFIGURABLE(cfgDoV0AT0Acut, bool, true, "do V0A-T0A cut") O2_DEFINE_CONFIGURABLE(cfgCutminIR, float, -1, "cut min IR") @@ -194,6 +204,8 @@ struct FlowGfwOmegaXi { O2_DEFINE_CONFIGURABLE(cfgLocDenParaK0s, std::vector, (std::vector{-0.00043057, -3.2435, -0.000385085, -2.97687, -0.000350298, -2.81502, -0.000326159, -2.71091, -0.000299563, -2.65448, -0.000294284, -2.60865, -0.000277938, -2.589, -0.000277091, -2.56983, -0.000272783, -2.56825, -0.000252706, -2.58996, -0.000247834, -2.63158, -0.00024379, -2.76976, -0.000286468, -2.92484, -0.000310149, -3.27746}), "Local density efficiency function parameter for K0s, exp(Ax + B)") O2_DEFINE_CONFIGURABLE(cfgLocDenParaLambda, std::vector, (std::vector{-0.000510948, -4.4846, -0.000460629, -4.14465, -0.000433729, -3.94173, -0.000412751, -3.81839, -0.000411211, -3.72502, -0.000401511, -3.68426, -0.000407461, -3.67005, -0.000379371, -3.71153, -0.000392828, -3.73214, -0.000403996, -3.80717, -0.000403376, -3.90917, -0.000354624, -4.34629, -0.000477606, -4.66307, -0.000541139, -4.61364}), "Local density efficiency function parameter for Lambda, exp(Ax + B)") O2_DEFINE_CONFIGURABLE(cfgRunNumbers, std::vector, (std::vector{544095, 544098, 544116, 544121, 544122, 544123, 544124}), "Preconfigured run numbers") + O2_DEFINE_CONFIGURABLE(cfgEtagapEdge, std::vector, (std::vector{-0.8, -0.4, 0.4, 0.8}), "sub-event eta range A: ([0], [1]) and C: ([2], [3])") + O2_DEFINE_CONFIGURABLE(cfgEtagapEdgeREF, std::vector, (std::vector{-0.8, -0.4, 0.4, 0.8}), "sub-event eta range for REF. A: ([0], [1]) and C: ([2], [3])") // switch O2_DEFINE_CONFIGURABLE(cfgDoAccEffCorr, bool, false, "do acc and eff corr") O2_DEFINE_CONFIGURABLE(cfgDoLocDenCorr, bool, false, "do local density corr") @@ -205,6 +217,7 @@ struct FlowGfwOmegaXi { O2_DEFINE_CONFIGURABLE(cfgOutputLocDenWeights, bool, false, "Fill and output local density weights") O2_DEFINE_CONFIGURABLE(cfgOutputQA, bool, false, "do QA") O2_DEFINE_CONFIGURABLE(cfgUseT0MCent, bool, false, "Use T0M cent") + O2_DEFINE_CONFIGURABLE(cfgcheckhole, bool, true, "Check and reject vtxz-eta-phi hole") ConfigurableAxis cfgaxisVertex{"cfgaxisVertex", {20, -10, 10}, "vertex axis for histograms"}; ConfigurableAxis cfgaxisPhi{"cfgaxisPhi", {60, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; @@ -225,7 +238,7 @@ struct FlowGfwOmegaXi { AxisSpec axisMultiplicity{{0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90}, "Centrality (%)"}; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < trkQualityOpts.cfgCutEta.value) && (aod::track::pt > trkQualityOpts.cfgCutPtPOIMin.value) && (aod::track::pt < trkQualityOpts.cfgCutPtPOIMax.value) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < trkQualityOpts.cfgCutDCAz.value); + Filter trackFilter = (nabs(aod::track::eta) < trkQualityOpts.cfgCutEta.value) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); using TracksPID = soa::Join; using AodTracks = soa::Filtered>; // tracks filter @@ -255,6 +268,8 @@ struct FlowGfwOmegaXi { std::vector cfgMultPVCutPara; std::vector cfgmassbins; std::vector runNumbers; + std::vector etagapEdge; + std::vector etagapEdgeREF; std::map>> th1sList; std::map>> th3sList; enum OutputTH1Names { @@ -327,6 +342,8 @@ struct FlowGfwOmegaXi { cfgNSigma = cfgNSigmapid; cfgmassbins = cfgMassBins; cfgMultPVCutPara = evtSeleOpts.cfgMultPVCut; + etagapEdge = cfgEtagapEdge; + etagapEdgeREF = cfgEtagapEdgeREF; // Set the pt, mult and phi Axis; o2::framework::AxisSpec axisPt = cfgaxisPt; @@ -361,6 +378,11 @@ struct FlowGfwOmegaXi { fLambdaMass = new TAxis(cfgmassbins[1], 1.08, 1.16); + AxisSpec axisOmegaMass = {80, 1.63f, 1.71f, "Inv. Mass (GeV)"}; + AxisSpec axisXiMass = {80, 1.29f, 1.37f, "Inv. Mass (GeV)"}; + AxisSpec axisK0sMass = {400, 0.4f, 0.6f, "Inv. Mass (GeV)"}; + AxisSpec axisLambdaMass = {160, 1.08f, 1.16f, "Inv. Mass (GeV)"}; + // Add some output objects to the histogram registry registry.add("hPhi", "", {HistType::kTH1D, {cfgaxisPhi}}); registry.add("hPhicorr", "", {HistType::kTH1D, {cfgaxisPhi}}); @@ -375,7 +397,7 @@ struct FlowGfwOmegaXi { registry.add("hEta", "", {HistType::kTH1D, {cfgaxisEta}}); registry.add("hVtxZ", "", {HistType::kTH1D, {cfgaxisVertex}}); registry.add("hMult", "", {HistType::kTH1D, {cfgaxisNch}}); - registry.add("hMultTPC", "", {HistType::kTH1D, {cfgaxisNch}}); + registry.add("hCentvsMultTPC", "", {HistType::kTH2D, {{100, 0, 100}, cfgaxisNch}}); registry.add("hCent", "", {HistType::kTH1D, {{90, 0, 90}}}); registry.add("hNTracksPVvsCentrality", "", {HistType::kTH2D, {{5000, 0, 5000}, {100, 0, 100}}}); registry.add("hmultFV0AvsmultFT0A", "", {HistType::kTH2D, {{5000, 0, 5000}, {5000, 0, 5000}}}); @@ -412,6 +434,10 @@ struct FlowGfwOmegaXi { registry.get(HIST("correction/hRunNumberPhiEtaVertexXi"))->GetAxis(0)->SetBinLabel(idx, std::to_string(runNumbers[idx - 1]).c_str()); registry.get(HIST("correction/hRunNumberPhiEtaVertexOmega"))->GetAxis(0)->SetBinLabel(idx, std::to_string(runNumbers[idx - 1]).c_str()); } + registry.add("correction/hPhiEtaInvmassK0s", "", {HistType::kTH3D, {cfgaxisPhi, cfgaxisEta, axisK0sMass}}); + registry.add("correction/hPhiEtaInvmassLambda", "", {HistType::kTH3D, {cfgaxisPhi, cfgaxisEta, axisLambdaMass}}); + registry.add("correction/hPhiEtaInvmassXi", "", {HistType::kTH3D, {cfgaxisPhi, cfgaxisEta, axisXiMass}}); + registry.add("correction/hPhiEtaInvmassOmega", "", {HistType::kTH3D, {cfgaxisPhi, cfgaxisEta, axisOmegaMass}}); } registry.add("hEventCount", "", {HistType::kTH1D, {{14, 0, 14}}}); @@ -424,7 +450,7 @@ struct FlowGfwOmegaXi { registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(7, "after kIsGoodZvtxFT0vsPV"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(8, "after kNoCollInTimeRangeStandard"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(9, "after kIsGoodITSLayersAll"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(10, "after MultPVCut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(10, "after MultvsCentCut"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(11, "after TPC occupancy cut"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(12, "after V0AT0Acut"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(13, "after IRmincut"); @@ -541,7 +567,7 @@ struct FlowGfwOmegaXi { registry.add("K0sc22dpt", ";pt ; C_{2}{2} ", {HistType::kTProfile3D, {cfgaxisPtK0s, cfgaxisK0sMassforflow, axisMultiplicity}}); registry.add("Lambdac22dpt", ";pt ; C_{2}{2} ", {HistType::kTProfile3D, {cfgaxisPtLambda, cfgaxisLambdaMassforflow, axisMultiplicity}}); registry.add("K0sc24dpt", ";pt ; C_{2}{4} ", {HistType::kTProfile3D, {cfgaxisPtK0s, cfgaxisK0sMassforflow, axisMultiplicity}}); - registry.add("Lambdac24dpt", ";pt ; C_{2}{4} ", {HistType::kTProfile3D, {cfgaxisPtK0s, cfgaxisLambdaMassforflow, axisMultiplicity}}); + registry.add("Lambdac24dpt", ";pt ; C_{2}{4} ", {HistType::kTProfile3D, {cfgaxisPtLambda, cfgaxisLambdaMassforflow, axisMultiplicity}}); registry.add("K0sc22Fulldpt", ";pt ; C_{2}{2} ", {HistType::kTProfile3D, {cfgaxisPtK0s, cfgaxisK0sMassforflow, axisMultiplicity}}); registry.add("Lambdac22Fulldpt", ";pt ; C_{2}{2} ", {HistType::kTProfile3D, {cfgaxisPtLambda, cfgaxisLambdaMassforflow, axisMultiplicity}}); // v3 @@ -585,10 +611,6 @@ struct FlowGfwOmegaXi { registry.add("MC/Lambdac22dptMC", ";pt ; C_{2}{2} ", {HistType::kTProfile2D, {cfgaxisPtLambda, axisMultiplicity}}); } // InvMass(GeV) of casc and v0 - AxisSpec axisOmegaMass = {80, 1.63f, 1.71f, "Inv. Mass (GeV)"}; - AxisSpec axisXiMass = {80, 1.29f, 1.37f, "Inv. Mass (GeV)"}; - AxisSpec axisK0sMass = {400, 0.4f, 0.6f, "Inv. Mass (GeV)"}; - AxisSpec axisLambdaMass = {160, 1.08f, 1.16f, "Inv. Mass (GeV)"}; if (cfgOutputCasc) { registry.add("InvMassXi_all", "", {HistType::kTHnSparseF, {cfgaxisPtXi, axisXiMass, cfgaxisEta, axisMultiplicity}}); registry.add("InvMassOmega_all", "", {HistType::kTHnSparseF, {cfgaxisPtOmega, axisOmegaMass, cfgaxisEta, axisMultiplicity}}); @@ -625,42 +647,46 @@ struct FlowGfwOmegaXi { } // Data - fGFW->AddRegion("reffull", -0.8, 0.8, 1, 1); // ("name", etamin, etamax, ptbinnum, bitmask)eta region -0.8 to 0.8 - fGFW->AddRegion("refN10", -0.8, -0.4, 1, 1); - fGFW->AddRegion("refP10", 0.4, 0.8, 1, 1); - // POI - fGFW->AddRegion("poiN10dpt", -0.8, -0.4, nPtBins, 32); - fGFW->AddRegion("poiP10dpt", 0.4, 0.8, nPtBins, 32); - fGFW->AddRegion("poifulldpt", -0.8, 0.8, nPtBins, 32); - fGFW->AddRegion("poioldpt", -0.8, 0.8, nPtBins, 1); - - int nXiptMassBins = nXiPtBins * cfgmassbins[2]; - fGFW->AddRegion("poiXiPdpt", 0.4, 0.8, nXiptMassBins, 2); - fGFW->AddRegion("poiXiNdpt", -0.8, -0.4, nXiptMassBins, 2); - fGFW->AddRegion("poiXifulldpt", -0.8, 0.8, nXiptMassBins, 2); - int nOmegaptMassBins = nXiPtBins * cfgmassbins[3]; - fGFW->AddRegion("poiOmegaPdpt", 0.4, 0.8, nOmegaptMassBins, 4); - fGFW->AddRegion("poiOmegaNdpt", -0.8, -0.4, nOmegaptMassBins, 4); - fGFW->AddRegion("poiOmegafulldpt", -0.8, 0.8, nOmegaptMassBins, 4); + fGFW->AddRegion("reffull", etagapEdgeREF[0], etagapEdgeREF[3], 1, 1); // ("name", etamin, etamax, ptbinnum, bitmask)eta region -0.8 to 0.8 int nK0sptMassBins = nK0sPtBins * cfgmassbins[0]; - fGFW->AddRegion("poiK0sPdpt", 0.4, 0.8, nK0sptMassBins, 8); - fGFW->AddRegion("poiK0sNdpt", -0.8, -0.4, nK0sptMassBins, 8); - fGFW->AddRegion("poiK0sfulldpt", -0.8, 0.8, nK0sptMassBins, 8); int nLambdaptMassBins = nLambdaPtBins * cfgmassbins[1]; - fGFW->AddRegion("poiLambdaPdpt", 0.4, 0.8, nLambdaptMassBins, 16); - fGFW->AddRegion("poiLambdaNdpt", -0.8, -0.4, nLambdaptMassBins, 16); - fGFW->AddRegion("poiLambdafulldpt", -0.8, 0.8, nLambdaptMassBins, 16); + int nXiptMassBins = nXiPtBins * cfgmassbins[2]; + int nOmegaptMassBins = nXiPtBins * cfgmassbins[3]; + + fGFW->AddRegion("refN10", etagapEdgeREF[0], etagapEdgeREF[1], 1, 1); + fGFW->AddRegion("refP10", etagapEdgeREF[2], etagapEdgeREF[3], 1, 1); + + fGFW->AddRegion("poiN10dpt", etagapEdge[0], etagapEdge[1], nPtBins + 1, 32); + fGFW->AddRegion("poiP10dpt", etagapEdge[2], etagapEdge[3], nPtBins + 1, 32); + fGFW->AddRegion("poifulldpt", etagapEdge[0], etagapEdge[3], nPtBins + 1, 32); + fGFW->AddRegion("poioldpt", etagapEdge[0], etagapEdge[3], nPtBins + 1, 1); + + fGFW->AddRegion("poiXiPdpt", etagapEdge[2], etagapEdge[3], nXiptMassBins + 1, 2); + fGFW->AddRegion("poiXiNdpt", etagapEdge[0], etagapEdge[1], nXiptMassBins + 1, 2); + fGFW->AddRegion("poiXifulldpt", etagapEdge[0], etagapEdge[3], nXiptMassBins + 1, 2); + + fGFW->AddRegion("poiOmegaPdpt", etagapEdge[2], etagapEdge[3], nOmegaptMassBins + 1, 4); + fGFW->AddRegion("poiOmegaNdpt", etagapEdge[0], etagapEdge[1], nOmegaptMassBins + 1, 4); + fGFW->AddRegion("poiOmegafulldpt", etagapEdge[0], etagapEdge[3], nOmegaptMassBins + 1, 4); + + fGFW->AddRegion("poiK0sPdpt", etagapEdge[2], etagapEdge[3], nK0sptMassBins + 1, 8); + fGFW->AddRegion("poiK0sNdpt", etagapEdge[0], etagapEdge[1], nK0sptMassBins + 1, 8); + fGFW->AddRegion("poiK0sfulldpt", etagapEdge[0], etagapEdge[3], nK0sptMassBins + 1, 8); + + fGFW->AddRegion("poiLambdaPdpt", etagapEdge[2], etagapEdge[3], nLambdaptMassBins + 1, 16); + fGFW->AddRegion("poiLambdaNdpt", etagapEdge[0], etagapEdge[1], nLambdaptMassBins + 1, 16); + fGFW->AddRegion("poiLambdafulldpt", etagapEdge[0], etagapEdge[3], nLambdaptMassBins + 1, 16); // MC - fGFW->AddRegion("refN10MC", -0.8, -0.4, 1, 64); - fGFW->AddRegion("refP10MC", 0.4, 0.8, 1, 64); - fGFW->AddRegion("poiXiPdptMC", 0.4, 0.8, nXiptMassBins, 128); - fGFW->AddRegion("poiXiNdptMC", -0.8, -0.4, nXiptMassBins, 128); - fGFW->AddRegion("poiOmegaPdptMC", 0.4, 0.8, nOmegaptMassBins, 256); - fGFW->AddRegion("poiOmegaNdptMC", -0.8, -0.4, nOmegaptMassBins, 256); - fGFW->AddRegion("poiK0sPdptMC", 0.4, 0.8, nK0sptMassBins, 512); - fGFW->AddRegion("poiK0sNdptMC", -0.8, -0.4, nK0sptMassBins, 512); - fGFW->AddRegion("poiLambdaPdptMC", 0.4, 0.8, nLambdaptMassBins, 1024); - fGFW->AddRegion("poiLambdaNdptMC", -0.8, -0.4, nLambdaptMassBins, 1024); + fGFW->AddRegion("refN10MC", etagapEdgeREF[0], etagapEdgeREF[1], 1, 64); + fGFW->AddRegion("refP10MC", etagapEdgeREF[2], etagapEdgeREF[3], 1, 64); + fGFW->AddRegion("poiXiPdptMC", etagapEdge[2], etagapEdge[3], nXiptMassBins, 128); + fGFW->AddRegion("poiXiNdptMC", etagapEdge[0], etagapEdge[1], nXiptMassBins, 128); + fGFW->AddRegion("poiOmegaPdptMC", etagapEdge[2], etagapEdge[3], nOmegaptMassBins, 256); + fGFW->AddRegion("poiOmegaNdptMC", etagapEdge[0], etagapEdge[1], nOmegaptMassBins, 256); + fGFW->AddRegion("poiK0sPdptMC", etagapEdge[2], etagapEdge[3], nK0sptMassBins, 512); + fGFW->AddRegion("poiK0sNdptMC", etagapEdge[0], etagapEdge[1], nK0sptMassBins, 512); + fGFW->AddRegion("poiLambdaPdptMC", etagapEdge[2], etagapEdge[3], nLambdaptMassBins, 1024); + fGFW->AddRegion("poiLambdaNdptMC", etagapEdge[0], etagapEdge[1], nLambdaptMassBins, 1024); // pushback // Data // v2 @@ -758,7 +784,7 @@ struct FlowGfwOmegaXi { } // input shared_ptr - void fillProfile(const GFW::CorrConfig& corrconf, std::shared_ptr TProfile, const double& cent) + void fillProfile(const GFW::CorrConfig& corrconf, const std::shared_ptr& TProfile, const double& cent) { double dnx, val; dnx = fGFW->Calculate(corrconf, 0, kTRUE).real(); @@ -863,7 +889,7 @@ struct FlowGfwOmegaXi { } // input shared_ptr - void fillProfilepTMass(const GFW::CorrConfig& corrconf, std::shared_ptr TProfile3D, const int& ptbin, const int& PDGCode, const float& cent) + void fillProfilepTMass(const GFW::CorrConfig& corrconf, const std::shared_ptr& TProfile3D, const int& ptbin, const int& PDGCode, const float& cent) { int nMassBins = 0; int nptbins = 0; @@ -929,7 +955,7 @@ struct FlowGfwOmegaXi { } template - bool setCurrentParticleWeights(float& weight_nue, float& weight_nua, TrackObject track, float vtxz, int ispecies) + bool setCurrentParticleWeights(float& weight_nue, float& weight_nua, const TrackObject& track, float vtxz, int ispecies) { float eff = 1.; int nspecies = 5; @@ -938,15 +964,22 @@ struct FlowGfwOmegaXi { if (eff <= 0) return false; weight_nue = 1. / eff; - if (mAcceptance.size() == static_cast(nspecies)) + if (mAcceptance.size() == static_cast(nspecies)) { weight_nua = mAcceptance[ispecies]->getNUA(track.phi(), track.eta(), vtxz); - else + if (cfgcheckhole) { + float checkleft = mAcceptance[ispecies]->getNUA(1, track.eta(), vtxz); + float checkright = mAcceptance[ispecies]->getNUA(2, track.eta(), vtxz); + if (weight_nua == 1 && checkleft == 1 && checkright == 1) + return false; + } + } else { weight_nua = 1; + } return true; } template - bool setCurrentLocalDensityWeights(float& weight_loc, TrackObject track, double locDensity, int ispecies) + bool setCurrentLocalDensityWeights(float& weight_loc, const TrackObject& track, double locDensity, int ispecies) { auto cfgLocDenPara = (std::vector>){cfgLocDenParaK0s, cfgLocDenParaLambda, cfgLocDenParaXi, cfgLocDenParaOmega}; int ptbin = fXiPtAxis->FindBin(track.pt()); @@ -964,7 +997,7 @@ struct FlowGfwOmegaXi { // event selection template - bool eventSelected(TCollision collision, const float centrality, float interactionRate = -1) + bool eventSelected(const TCollision& collision, const float centrality, float interactionRate = -1) { if (evtSeleOpts.cfgDoTVXinTRD.value && collision.alias_bit(kTVXinTRD)) { // TRD triggered @@ -1030,6 +1063,15 @@ struct FlowGfwOmegaXi { return false; } + int nMultTPC = collision.multTPC(); + registry.fill(HIST("hCentvsMultTPC"), centrality, nMultTPC); + if (evtSeleOpts.cfgDoMultTPCCut.value) { + if (nMultTPC < fMultPVCutLow->Eval(centrality)) + return false; + if (nMultTPC > fMultPVCutHigh->Eval(centrality)) + return false; + } + registry.fill(HIST("hEventCount"), 9.5); if (occupancy > evtSeleOpts.cfgCutOccupancyHigh.value) @@ -1060,7 +1102,7 @@ struct FlowGfwOmegaXi { { o2::aod::ITSResponse itsResponse; int nTot = tracks.size(); - float nMultTPC = collision.multTPC(); + int nMultTPC = collision.multTPC(); auto bc = collision.bc_as(); int runNumber = bc.runNumber(); double interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 1.e-3; @@ -1096,7 +1138,6 @@ struct FlowGfwOmegaXi { float vtxz = collision.posZ(); registry.fill(HIST("hVtxZ"), vtxz); registry.fill(HIST("hMult"), nTot); - registry.fill(HIST("hMultTPC"), nMultTPC); registry.fill(HIST("hCent"), cent); float weff = 1; @@ -1111,6 +1152,11 @@ struct FlowGfwOmegaXi { } if ((track.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) || (track.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) || (track.itsNCls() <= trkQualityOpts.cfgChITSNCls.value)) continue; + if ((track.pt() < trkQualityOpts.cfgCutPtPOIMin.value) || (track.pt() > trkQualityOpts.cfgCutPtPOIMax.value)) + continue; + if ((std::fabs(track.dcaZ()) > trkQualityOpts.cfgCutDCAz.value) || (std::fabs(track.dcaXY()) > trkQualityOpts.cfgCutDCAxy.value)) + continue; + registry.fill(HIST("hPhi"), track.phi()); registry.fill(HIST("hPhicorr"), track.phi(), wacc); registry.fill(HIST("hEta"), track.eta()); @@ -1156,6 +1202,10 @@ struct FlowGfwOmegaXi { continue; if (v0negdau.pt() < trkQualityOpts.cfgCutPtDauMin.value || v0negdau.pt() > trkQualityOpts.cfgCutPtDauMax.value) continue; + if (std::fabs(v0posdau.eta()) > trkQualityOpts.cfgCutEta.value) + continue; + if (std::fabs(v0negdau.eta()) > trkQualityOpts.cfgCutEta.value) + continue; // fill QA if (cfgOutputQA) { @@ -1165,7 +1215,7 @@ struct FlowGfwOmegaXi { // K0short if (v0.pt() > trkQualityOpts.cfgCutPtK0sMin.value && v0.pt() < trkQualityOpts.cfgCutPtK0sMax.value) { if (v0.qtarm() / std::fabs(v0.alpha()) > v0BuilderOpts.cfgv0_ArmPodocut.value && - std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0BuilderOpts.cfgv0_mk0swindow.value && + v0.mK0Short() > v0BuilderOpts.cfgv0_mk0slow.value && v0.mK0Short() < v0BuilderOpts.cfgv0_mk0shigh.value && (std::fabs(v0posdau.tpcNSigmaPi()) < cfgNSigma[0] && std::fabs(v0negdau.tpcNSigmaPi()) < cfgNSigma[0]) && ((std::fabs(v0.tofNSigmaK0PiPlus()) < cfgNSigma[3] || v0posdau.pt() < lowpt) && (std::fabs(v0.tofNSigmaK0PiMinus()) < cfgNSigma[3] || v0negdau.pt() < lowpt)) && ((std::fabs(itsResponse.nSigmaITS(v0posdau)) < cfgNSigma[6]) || v0posdau.pt() > lowpt) && ((std::fabs(itsResponse.nSigmaITS(v0negdau)) < cfgNSigma[6]) || v0negdau.pt() > lowpt)) { @@ -1178,14 +1228,14 @@ struct FlowGfwOmegaXi { } // Lambda and antiLambda if (v0.pt() > trkQualityOpts.cfgCutPtLambdaMin.value && v0.pt() < trkQualityOpts.cfgCutPtLambdaMax.value) { - if (std::fabs(v0.mLambda() - o2::constants::physics::MassLambda) < v0BuilderOpts.cfgv0_mlambdawindow.value && + if (v0.mLambda() > v0BuilderOpts.cfgv0_mlambdalow.value && v0.mLambda() < v0BuilderOpts.cfgv0_mlambdahigh.value && (std::fabs(v0posdau.tpcNSigmaPr()) < cfgNSigma[1] && std::fabs(v0negdau.tpcNSigmaPi()) < cfgNSigma[0]) && ((std::fabs(v0.tofNSigmaLaPr()) < cfgNSigma[4] || v0posdau.pt() < lowpt) && (std::fabs(v0.tofNSigmaLaPi()) < cfgNSigma[3] || v0negdau.pt() < lowpt)) && ((std::fabs(itsResponse.nSigmaITS(v0posdau)) < cfgNSigma[7]) || v0posdau.pt() > lowpt) && ((std::fabs(itsResponse.nSigmaITS(v0negdau)) < cfgNSigma[6]) || v0negdau.pt() > lowpt)) { registry.fill(HIST("InvMassLambda_all"), v0.pt(), v0.mLambda(), v0.eta(), cent); isLambda = true; } - if (std::fabs(v0.mAntiLambda() - o2::constants::physics::MassLambda) < v0BuilderOpts.cfgv0_mlambdawindow.value && + if (v0.mAntiLambda() > v0BuilderOpts.cfgv0_mlambdalow.value && v0.mAntiLambda() < v0BuilderOpts.cfgv0_mlambdahigh.value && (std::fabs(v0negdau.tpcNSigmaPr()) < cfgNSigma[1] && std::fabs(v0posdau.tpcNSigmaPi()) < cfgNSigma[0]) && ((std::fabs(v0.tofNSigmaALaPr()) < cfgNSigma[4] || v0negdau.pt() < lowpt) && (std::fabs(v0.tofNSigmaALaPi()) < cfgNSigma[3] || v0posdau.pt() < lowpt)) && ((std::fabs(itsResponse.nSigmaITS(v0negdau)) < cfgNSigma[7]) || v0posdau.pt() > lowpt) && ((std::fabs(itsResponse.nSigmaITS(v0posdau)) < cfgNSigma[6]) || v0negdau.pt() > lowpt)) { @@ -1220,9 +1270,13 @@ struct FlowGfwOmegaXi { if (!isK0s && !isLambda && !isALambda) continue; // track quality check - if (v0posdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (v0posdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) continue; - if (v0negdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (v0negdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + continue; + if (v0posdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) + continue; + if (v0negdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) continue; if (v0posdau.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) continue; @@ -1238,19 +1292,47 @@ struct FlowGfwOmegaXi { if (!v0negdau.hasTPC() || !v0negdau.hasITS()) continue; } + float ctau = 0; + if (isK0s) { + ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short; + if (ctau > v0BuilderOpts.cfgv0_ctauK0s.value) + isK0s = false; + if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcak0daupitopv.value) + continue; + if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcak0daupitopv.value) + continue; + } + if (isLambda || isALambda) { + ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + if (ctau > v0BuilderOpts.cfgv0_ctauLambda.value) { + isLambda = false; + isALambda = false; + } + if (isLambda) { + if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcadauprtopv.value) + continue; + if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcadaupitopv.value) + continue; + } else { + if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcadaupitopv.value) + continue; + if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcadauprtopv.value) + continue; + } + } // // topological cut if (v0.v0radius() < v0BuilderOpts.cfgv0_radius.value) continue; + if (v0.v0radius() > v0BuilderOpts.cfgv0_radiusmax.value) + continue; if (v0.v0cosPA() < v0BuilderOpts.cfgv0_v0cospa.value) continue; if (v0.dcaV0daughters() > v0BuilderOpts.cfgv0_dcav0dau.value) continue; - if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcadautopv.value) - continue; - if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcadautopv.value) - continue; if (isK0s && std::fabs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0BuilderOpts.cfgv0_compmassrejLambda.value) isK0s = false; + if (isK0s && std::fabs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0BuilderOpts.cfgv0_compmassrejLambda.value) + isK0s = false; if (isLambda && std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0BuilderOpts.cfgv0_compmassrejK0s.value) isLambda = false; if (isALambda && std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0BuilderOpts.cfgv0_compmassrejK0s.value) @@ -1303,8 +1385,6 @@ struct FlowGfwOmegaXi { registry.fill(HIST("hPhiK0s"), v0.phi()); registry.fill(HIST("hPhiK0scorr"), v0.phi(), wacc); fGFW->Fill(v0.eta(), fK0sPtAxis->FindBin(v0.pt()) - 1 + ((fK0sMass->FindBin(v0.mK0Short()) - 1) * nK0sPtBins), v0.phi(), wacc * weff * wloc, 8); - if (fK0sPtAxis->FindBin(v0.pt()) - 1 == 6) - fGFW->Fill(v0.eta(), (fK0sMass->FindBin(v0.mK0Short()) - 1), v0.phi(), wacc * weff * wloc, 2048); if (cfgOutputNUAWeights) fWeightsK0s->fill(v0.phi(), v0.eta(), vtxz, v0.pt(), cent, 0); if (cfgOutputrunbyrun) { @@ -1313,6 +1393,7 @@ struct FlowGfwOmegaXi { th1sList[runNumber][hPhiK0scorr]->Fill(v0.phi(), wacc); } registry.fill(HIST("correction/hRunNumberPhiEtaVertexK0s"), matchedPosition, v0.phi(), v0.eta(), vtxz); + registry.fill(HIST("correction/hPhiEtaInvmassK0s"), v0.phi(), v0.eta(), v0.mK0Short()); } } if (isLambda || isALambda) { @@ -1346,6 +1427,10 @@ struct FlowGfwOmegaXi { th1sList[runNumber][hPhiLambdacorr]->Fill(v0.phi(), wacc); } registry.fill(HIST("correction/hRunNumberPhiEtaVertexLambda"), matchedPosition, v0.phi(), v0.eta(), vtxz); + if (isLambda) + registry.fill(HIST("correction/hPhiEtaInvmassLambda"), v0.phi(), v0.eta(), v0.mLambda()); + if (isALambda) + registry.fill(HIST("correction/hPhiEtaInvmassLambda"), v0.phi(), v0.eta(), v0.mAntiLambda()); } } } @@ -1367,6 +1452,12 @@ struct FlowGfwOmegaXi { continue; if (negdau.pt() < trkQualityOpts.cfgCutPtDauMin.value || negdau.pt() > trkQualityOpts.cfgCutPtDauMax.value) continue; + if (std::fabs(bachelor.eta()) > trkQualityOpts.cfgCutEta.value) + continue; + if (std::fabs(posdau.eta()) > trkQualityOpts.cfgCutEta.value) + continue; + if (std::fabs(negdau.eta()) > trkQualityOpts.cfgCutEta.value) + continue; // Omega and antiOmega if (casc.pt() > trkQualityOpts.cfgCutPtOmegaMin.value && casc.pt() < trkQualityOpts.cfgCutPtOmegaMax.value) { @@ -1479,11 +1570,17 @@ struct FlowGfwOmegaXi { if (std::fabs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascBuilderOpts.cfgcasc_mlambdawindow.value) continue; // track quality check - if (bachelor.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (bachelor.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) continue; - if (posdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (posdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) continue; - if (negdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (negdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) + continue; + if (bachelor.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + continue; + if (posdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + continue; + if (negdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) continue; if (bachelor.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) continue; @@ -1574,6 +1671,7 @@ struct FlowGfwOmegaXi { th1sList[runNumber][hPhiOmegacorr]->Fill(casc.phi(), wacc); } registry.fill(HIST("correction/hRunNumberPhiEtaVertexOmega"), matchedPosition, casc.phi(), casc.eta(), vtxz); + registry.fill(HIST("correction/hPhiEtaInvmassOmega"), casc.phi(), casc.eta(), casc.mOmega()); } } if (isXi) { @@ -1603,6 +1701,7 @@ struct FlowGfwOmegaXi { th1sList[runNumber][hPhiXicorr]->Fill(casc.phi(), wacc); } registry.fill(HIST("correction/hRunNumberPhiEtaVertexXi"), matchedPosition, casc.phi(), casc.eta(), vtxz); + registry.fill(HIST("correction/hPhiEtaInvmassXi"), casc.phi(), casc.eta(), casc.mXi()); } } } @@ -1920,11 +2019,17 @@ struct FlowGfwOmegaXi { } } // track quality check - if (bachelor.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (bachelor.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) + continue; + if (posdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) continue; - if (posdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (negdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) continue; - if (negdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (bachelor.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + continue; + if (posdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + continue; + if (negdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) continue; if (bachelor.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) continue; @@ -2105,9 +2210,13 @@ struct FlowGfwOmegaXi { } } // // track quality check - if (v0posdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (v0posdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + continue; + if (v0negdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) continue; - if (v0negdau.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (v0posdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) + continue; + if (v0negdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) continue; if (v0posdau.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) continue; @@ -2130,10 +2239,25 @@ struct FlowGfwOmegaXi { continue; if (v0.dcaV0daughters() > v0BuilderOpts.cfgv0_dcav0dau.value) continue; - if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcadautopv.value) - continue; - if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcadautopv.value) - continue; + if (pdgCode == kK0Short) { + if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcak0daupitopv.value) + continue; + if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcak0daupitopv.value) + continue; + } + if (pdgCode == kLambda0 || pdgCode == kLambda0Bar) { + if (pdgCode == kLambda0) { + if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcadauprtopv.value) + continue; + if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcadaupitopv.value) + continue; + } else { + if (std::fabs(v0.dcapostopv()) < v0BuilderOpts.cfgv0_dcadaupitopv.value) + continue; + if (std::fabs(v0.dcanegtopv()) < v0BuilderOpts.cfgv0_dcadauprtopv.value) + continue; + } + } // fill QA after cut if (cfgOutputQA) { if (pdgCode == kK0Short) { @@ -2165,7 +2289,6 @@ struct FlowGfwOmegaXi { // K0short if (std::abs(pdgCode) == kK0Short) { if (v0.qtarm() / std::fabs(v0.alpha()) > v0BuilderOpts.cfgv0_ArmPodocut.value && - std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0BuilderOpts.cfgv0_mk0swindow.value && (std::fabs(v0posdau.tpcNSigmaPi()) < cfgNSigma[0] && std::fabs(v0negdau.tpcNSigmaPi()) < cfgNSigma[0])) { if (cfgDoAccEffCorr) setCurrentParticleWeights(weff, wacc, v0, vtxz, 1); @@ -2181,8 +2304,7 @@ struct FlowGfwOmegaXi { } } // Lambda and antiLambda - if (std::fabs(v0.mLambda() - o2::constants::physics::MassLambda) < v0BuilderOpts.cfgv0_mlambdawindow.value && - (std::fabs(v0posdau.tpcNSigmaPr()) < cfgNSigma[1] && std::fabs(v0negdau.tpcNSigmaPi()) < cfgNSigma[0])) { + if ((std::fabs(v0posdau.tpcNSigmaPr()) < cfgNSigma[1] && std::fabs(v0negdau.tpcNSigmaPi()) < cfgNSigma[0])) { if (std::abs(pdgCode) == kLambda0) { if (cfgDoAccEffCorr) setCurrentParticleWeights(weff, wacc, v0, vtxz, 2); @@ -2196,8 +2318,7 @@ struct FlowGfwOmegaXi { } fGFW->Fill(v0Eta, fLambdaPtAxis->FindBin(v0Pt) - 1, v0Phi, wacc * weff * wloc, 16); } - } else if (std::fabs(v0.mLambda() - o2::constants::physics::MassLambda) < v0BuilderOpts.cfgv0_mlambdawindow.value && - (std::fabs(v0negdau.tpcNSigmaPr()) < cfgNSigma[1] && std::fabs(v0posdau.tpcNSigmaPi()) < cfgNSigma[0])) { + } else if ((std::fabs(v0negdau.tpcNSigmaPr()) < cfgNSigma[1] && std::fabs(v0posdau.tpcNSigmaPi()) < cfgNSigma[0])) { if (std::abs(pdgCode) == kLambda0) { if (cfgDoAccEffCorr) setCurrentParticleWeights(weff, wacc, v0, vtxz, 2); diff --git a/PWGCF/Flow/Tasks/flowGfwTask.cxx b/PWGCF/Flow/Tasks/flowGfwTask.cxx index 898861b8996..3e43254956c 100644 --- a/PWGCF/Flow/Tasks/flowGfwTask.cxx +++ b/PWGCF/Flow/Tasks/flowGfwTask.cxx @@ -662,7 +662,7 @@ struct FlowGfwTask { return; } - void fillProfile(const GFW::CorrConfig& corrconf, std::shared_ptr tarName, const double& cent) + void fillProfile(const GFW::CorrConfig& corrconf, const std::shared_ptr& tarName, const double& cent) { double dnx, val; dnx = fGFW->Calculate(corrconf, 0, kTRUE).real(); @@ -762,7 +762,7 @@ struct FlowGfwTask { } template - bool eventSelected(o2::aod::mult::MultNTracksPV, TCollision collision, const int multTrk, const float centrality) + bool eventSelected(o2::aod::mult::MultNTracksPV, const TCollision& collision, const int multTrk, const float centrality) { if (cfgNoTimeFrameBorder) { if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { @@ -867,7 +867,7 @@ struct FlowGfwTask { } template - bool trackSelected(TTrack track, const int field) + bool trackSelected(const TTrack& track, const int field) { double phimodn = track.phi(); if (field < 0) // for negative polarity field @@ -887,7 +887,7 @@ struct FlowGfwTask { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { if (cfgDCAzPt && (std::fabs(track.dcaZ()) > (0.004f + 0.013f / track.pt()))) return false; diff --git a/PWGCF/Flow/Tasks/flowMc.cxx b/PWGCF/Flow/Tasks/flowMc.cxx index 42be93715a0..8248b44433f 100644 --- a/PWGCF/Flow/Tasks/flowMc.cxx +++ b/PWGCF/Flow/Tasks/flowMc.cxx @@ -20,6 +20,8 @@ #include "PWGMM/Mult/DataModel/Index.h" // for Particles2Tracks table #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/TrackSelection.h" #include "Common/Core/TrackSelectionDefaults.h" @@ -58,13 +60,15 @@ #include #include #include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) struct FlowMc { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -80,15 +84,17 @@ struct FlowMc { O2_DEFINE_CONFIGURABLE(cfgCutPtRefMax, float, 3.0f, "Maximal pT for ref tracks") O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "Minimal pT for poi tracks") O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 10.0f, "Maximal pT for poi tracks") - O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, int, 50, "minimum TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutITSclu, int, 5, "minimum ITS clusters") O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCcrossedrows, float, 70.0f, "minimum TPC crossed rows") + O2_DEFINE_CONFIGURABLE(cfgCutTPCcrossedrows, int, 70, "minimum TPC crossed rows") O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, float, 0.2f, "DCAxy cut for tracks") O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"); O2_DEFINE_CONFIGURABLE(cfgDCAxyFunction, std::string, "(0.0015+0.005/(x^1.1))", "Functional form of pt-dependent DCAxy cut"); O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "DCAz cut for tracks") O2_DEFINE_CONFIGURABLE(cfgCutDCAzPtDepEnabled, bool, false, "switch of DCAz pt dependent cut") + O2_DEFINE_CONFIGURABLE(cfgDCAzFunction, std::string, "(0.0015+0.005/(x^1.1))", "Functional form of pt-dependent DCAz cut"); + O2_DEFINE_CONFIGURABLE(cfgDCAzNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the z direction"); O2_DEFINE_CONFIGURABLE(cfgEnableITSCuts, bool, true, "switch of enabling ITS based track selection cuts") O2_DEFINE_CONFIGURABLE(cfgTrkSelRun3ITSMatch, bool, false, "GlobalTrackRun3ITSMatching::Run3ITSall7Layers selection") O2_DEFINE_CONFIGURABLE(cfgFlowAcceptance, std::string, "", "CCDB path to acceptance object") @@ -96,13 +102,16 @@ struct FlowMc { O2_DEFINE_CONFIGURABLE(cfgCentVsIPTruth, std::string, "", "CCDB path to centrality vs IP truth") O2_DEFINE_CONFIGURABLE(cfgIsGlobalTrack, bool, false, "Use global tracks instead of hasTPC&&hasITS") O2_DEFINE_CONFIGURABLE(cfgK0Lambda0Enabled, bool, false, "Add K0 and Lambda0, for bulk particle efficiency please keep off") + O2_DEFINE_CONFIGURABLE(cfgAcceptSecondaries, bool, false, "Accept secondary particles produced from decays") + O2_DEFINE_CONFIGURABLE(cfgRequireTOF, bool, false, "Require that reconstructed tracks have TOF for resonance decays") O2_DEFINE_CONFIGURABLE(cfgFlowCumulantEnabled, bool, false, "switch of calculating flow") O2_DEFINE_CONFIGURABLE(cfgFlowCumulantNbootstrap, int, 30, "Number of subsamples") O2_DEFINE_CONFIGURABLE(cfgTrackDensityCorrUse, bool, false, "Use track density efficiency correction") O2_DEFINE_CONFIGURABLE(cfgTrackDensityCorrSlopeFactor, float, 1.0f, "A factor to scale the track density efficiency slope") O2_DEFINE_CONFIGURABLE(cfgRecoEvRejectMC, bool, false, "reject both MC and Reco events when reco do not pass") O2_DEFINE_CONFIGURABLE(cfgRecoEvSel8, bool, false, "require sel8 for reconstruction events") - O2_DEFINE_CONFIGURABLE(cfgRecoEvkIsGoodITSLayersAll, bool, false, "require kIsGoodITSLayersAll for reconstruction events") + O2_DEFINE_CONFIGURABLE(cfgRecoEvkIsGoodITSLayers, bool, true, "cut time intervals with dead ITS staves") + O2_DEFINE_CONFIGURABLE(cfgRecoEvkIsGoodITSLayersFlag, int, 0, "0: kIsGoodITSLayersAll; 1: kIsGoodITSLayer0123; 2: kIsGoodITSLayer3") O2_DEFINE_CONFIGURABLE(cfgRecoEvkNoSameBunchPileup, bool, false, "require kNoSameBunchPileup for reconstruction events") O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") @@ -110,6 +119,14 @@ struct FlowMc { O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking', 'CBT_hadronPID' ") + O2_DEFINE_CONFIGURABLE(cfgIRFetch, bool, false, "Get interaction rate from CCDB") + O2_DEFINE_CONFIGURABLE(cfgIRCutEnabled, bool, false, "Use events with low interaction rate") + O2_DEFINE_CONFIGURABLE(cfgIRMax, float, 50.0f, "maximum interaction rate (kHz)") + O2_DEFINE_CONFIGURABLE(cfgIRMin, float, 0.0f, "minimum interaction rate (kHz)") + O2_DEFINE_CONFIGURABLE(cfgOccupancyEnabled, bool, true, "Occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgOccupancyMax, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgOccupancyMin, int, 0, "Low cut on TPC occupancy") Configurable> cfgTrackDensityP0{"cfgTrackDensityP0", std::vector{0.6003720411, 0.6152630970, 0.6288860646, 0.6360694031, 0.6409494798, 0.6450540203, 0.6482117301, 0.6512592056, 0.6640008690, 0.6862631416, 0.7005738691, 0.7106567432, 0.7170728333}, "parameter 0 for track density efficiency correction"}; Configurable> cfgTrackDensityP1{"cfgTrackDensityP1", std::vector{-1.007592e-05, -8.932635e-06, -9.114538e-06, -1.054818e-05, -1.220212e-05, -1.312304e-05, -1.376433e-05, -1.412813e-05, -1.289562e-05, -1.050065e-05, -8.635725e-06, -7.380821e-06, -6.201250e-06}, "parameter 1 for track density efficiency correction"}; @@ -136,6 +153,7 @@ struct FlowMc { // Additional filters for tracks TrackSelection myTrackSel; TF1* fPtDepDCAxy = nullptr; + TF1* fPtDepDCAz = nullptr; // Cent vs IP TH1D* mCentVsIPTruth = nullptr; @@ -147,13 +165,19 @@ struct FlowMc { bool correctionsLoaded = false; std::vector funcEff; - TH1D* hFindPtBin; - TF1* funcV2; - TF1* funcV3; - TF1* funcV4; + TH1D* hFindPtBin = nullptr; + TF1* funcV2 = nullptr; + TF1* funcV3 = nullptr; + TF1* funcV4 = nullptr; + enum GoodITSLayersFlag { + kITSLayersAll, + kITSLayer0123, + kITSLayer3, + kCount_ITSLayersFlag + }; // Connect to ccdb - Service ccdb; + Service ccdb{}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; OutputObj fWeights{GFWWeights("weights")}; @@ -161,11 +185,18 @@ struct FlowMc { OutputObj fFCReco{FlowContainer("FlowContainerReco")}; GFW* fGFWTrue = new GFW(); GFW* fGFWReco = new GFW(); - TAxis* fPtAxis; + TAxis* fPtAxis = nullptr; std::vector corrconfigsTruth; std::vector corrconfigsReco; TRandom3* fRndm = new TRandom3(0); double epsilon = 1e-6; + int mRunNumber{-1}; + uint64_t mSOR{0}; + double mMinSeconds{-1.}; + std::unordered_map gHadronicRate; + ctpRateFetcher mRateFetcher; + TH2* gCurrentHadronicRate = nullptr; + RCTFlagsChecker rctChecker{"CBT"}; void init(InitContext&) { @@ -187,6 +218,7 @@ struct FlowMc { // pT histograms histos.add("hImpactParameter", "hImpactParameter", HistType::kTH1D, {axisB}); histos.add("hNchVsImpactParameter", "hNchVsImpactParameter", HistType::kTH2D, {axisB, axisNch}); + histos.add("hNchRecoVsNchGen", "hNchRecoVsNchGen; Recoconstucted Nch; Genenerated Nch", HistType::kTH2D, {axisNch, axisNch}); histos.add("hEventPlaneAngle", "hEventPlaneAngle", HistType::kTH1D, {axisPhi}); histos.add("hPtVsPhiGenerated", "hPtVsPhiGenerated", HistType::kTH2D, {axisPhi, axisPt}); histos.add("hPtVsPhiGlobal", "hPtVsPhiGlobal", HistType::kTH2D, {axisPhi, axisPt}); @@ -223,6 +255,12 @@ struct FlowMc { histos.add("hPtNchGlobalK0", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); histos.add("hPtNchGeneratedLambda", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); histos.add("hPtNchGlobalLambda", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedK0Pions", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalK0Pions", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedLambdaPions", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalLambdaPions", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedLambdaProtons", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalLambdaProtons", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); histos.add("hPtMCGen", "Monte Carlo Truth; pT (GeV/c);", {HistType::kTH1D, {axisPt}}); histos.add("hEtaPtVtxzMCGen", "Monte Carlo Truth; #eta; p_{T} (GeV/c); V_{z} (cm);", {HistType::kTH3D, {axisEta, axisPt, axisVertex}}); histos.add("hPtMCGlobal", "Monte Carlo Global; pT (GeV/c);", {HistType::kTH1D, {axisPt}}); @@ -233,7 +271,7 @@ struct FlowMc { o2::framework::AxisSpec axis = axisPt; int nPtBins = axis.binEdges.size() - 1; - double* ptBins = &(axis.binEdges)[0]; + double* ptBins = axis.binEdges.data(); fPtAxis = new TAxis(nPtBins, ptBins); if (cfgOutputNUAWeights) { @@ -242,13 +280,15 @@ struct FlowMc { } if (cfgFlowCumulantEnabled) { - TObjArray* oba = new TObjArray(); + auto oba = new TObjArray(); oba->Add(new TNamed("ChFull22", "ChFull22")); - for (auto i = 0; i < fPtAxis->GetNbins(); i++) + for (auto i = 0; i < fPtAxis->GetNbins(); i++) { oba->Add(new TNamed(Form("ChFull22_pt_%i", i + 1), "ChFull22_pTDiff")); + } oba->Add(new TNamed("Ch10Gap22", "Ch10Gap22")); - for (auto i = 0; i < fPtAxis->GetNbins(); i++) + for (auto i = 0; i < fPtAxis->GetNbins(); i++) { oba->Add(new TNamed(Form("Ch10Gap22_pt_%i", i + 1), "Ch10Gap22_pTDiff")); + } fFCTrue->SetName("FlowContainerTrue"); fFCTrue->SetXAxis(fPtAxis); fFCTrue->Initialize(oba, axisCentrality, cfgFlowCumulantNbootstrap); @@ -301,14 +341,19 @@ struct FlowMc { } myTrackSel.SetMinNClustersTPC(cfgCutTPCclu); myTrackSel.SetMinNCrossedRowsTPC(cfgCutTPCcrossedrows); - if (cfgEnableITSCuts) + if (cfgEnableITSCuts) { myTrackSel.SetMinNClustersITS(cfgCutITSclu); - if (!cfgCutDCAzPtDepEnabled) + } + if (!cfgCutDCAzPtDepEnabled) { myTrackSel.SetMaxDcaZ(cfgCutDCAz); - + } else { + fPtDepDCAz = new TF1("ptDepDCAxy", Form("[0]*%s", cfgDCAzFunction->c_str()), 0.001, 100); + fPtDepDCAz->SetParameter(0, cfgDCAzNSigma); + LOGF(info, "DCAz pt-dependence function: %s", Form("[0]*%s", cfgDCAzFunction->c_str())); + } if (cfgTrackDensityCorrUse) { std::vector pTEffBins = {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0}; - hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, &pTEffBins[0]); + hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, pTEffBins.data()); funcEff.resize(pTEffBins.size() - 1); // LHC24g3 Eff std::vector f1p0 = cfgTrackDensityP0; @@ -324,25 +369,30 @@ struct FlowMc { funcV4 = new TF1("funcV4", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); funcV4->SetParameters(0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10); } + if (!cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSelRCTflags.value); // override initialzation + } } void loadCorrections(uint64_t timestamp) { - if (correctionsLoaded) + if (correctionsLoaded) { return; - if (cfgFlowAcceptance.value.empty() == false) { + } + if (!cfgFlowAcceptance.value.empty()) { mAcceptance = ccdb->getForTimeStamp(cfgFlowAcceptance, timestamp); - if (mAcceptance) - LOGF(info, "Loaded acceptance weights from %s (%p)", cfgFlowAcceptance.value.c_str(), (void*)mAcceptance); - else - LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgFlowAcceptance.value.c_str(), (void*)mAcceptance); + if (mAcceptance) { + LOGF(info, "Loaded acceptance weights from %s (%p)", cfgFlowAcceptance.value.c_str(), static_cast(mAcceptance)); + } else { + LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgFlowAcceptance.value.c_str(), static_cast(mAcceptance)); + } } - if (cfgFlowEfficiency.value.empty() == false) { + if (!cfgFlowEfficiency.value.empty()) { mEfficiency = ccdb->getForTimeStamp(cfgFlowEfficiency, timestamp); if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgFlowEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgFlowEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgFlowEfficiency.value.c_str(), static_cast(mEfficiency)); } correctionsLoaded = true; } @@ -350,61 +400,69 @@ struct FlowMc { bool setCurrentParticleWeights(float& weight_nue, float& weight_nua, float phi, float eta, float pt, float vtxz) { float eff = 1.; - if (mEfficiency) + if (mEfficiency) { eff = mEfficiency->GetBinContent(mEfficiency->FindBin(pt)); - else + } else { eff = 1.0; - if (eff == 0) + } + if (eff == 0) { return false; + } weight_nue = 1. / eff; - if (mAcceptance) + if (mAcceptance) { weight_nua = mAcceptance->getNUA(phi, eta, vtxz); - else + } else { weight_nua = 1; + } return true; } void fillFC(GFW* fGFW, bool isMCTruth, const GFW::CorrConfig& corrconf, const double& cent, const double& rndm) { - double dnx, val; + double dnx = 0, val = 0; dnx = fGFW->Calculate(corrconf, 0, kTRUE).real(); if (!corrconf.pTDif) { - if (dnx == 0) + if (dnx == 0) { return; + } val = fGFW->Calculate(corrconf, 0, kFALSE).real() / dnx; if (std::fabs(val) < 1) { - if (isMCTruth) + if (isMCTruth) { fFCTrue->FillProfile(corrconf.Head.c_str(), cent, val, dnx, rndm); - else + } else { fFCReco->FillProfile(corrconf.Head.c_str(), cent, val, dnx, rndm); + } } return; } for (auto i = 1; i <= fPtAxis->GetNbins(); i++) { dnx = fGFW->Calculate(corrconf, i - 1, kTRUE).real(); - if (dnx == 0) + if (dnx == 0) { continue; + } val = fGFW->Calculate(corrconf, i - 1, kFALSE).real() / dnx; if (std::fabs(val) < 1) { - if (isMCTruth) + if (isMCTruth) { fFCTrue->FillProfile(Form("%s_pt_%i", corrconf.Head.c_str(), i), cent, val, dnx, rndm); - else + } else { fFCReco->FillProfile(Form("%s_pt_%i", corrconf.Head.c_str(), i), cent, val, dnx, rndm); + } } } - return; } void loadCentVsIPTruth(uint64_t timestamp) { - if (centVsIPTruthLoaded) + if (centVsIPTruthLoaded) { return; - if (cfgCentVsIPTruth.value.empty() == false) { + } + if (!cfgCentVsIPTruth.value.empty()) { mCentVsIPTruth = ccdb->getForTimeStamp(cfgCentVsIPTruth, timestamp); - if (mCentVsIPTruth) - LOGF(info, "Loaded CentVsIPTruth weights from %s (%p)", cfgCentVsIPTruth.value.c_str(), (void*)mCentVsIPTruth); - else + if (mCentVsIPTruth) { + LOGF(info, "Loaded CentVsIPTruth weights from %s (%p)", cfgCentVsIPTruth.value.c_str(), static_cast(mCentVsIPTruth)); + } else { LOGF(fatal, "Failed to load CentVsIPTruth weights from %s", cfgCentVsIPTruth.value.c_str()); + } centVsIPTruthLoaded = true; } else { @@ -412,8 +470,25 @@ struct FlowMc { } } + void initHadronicRate(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + if (!gHadronicRate.contains(mRunNumber)) { + auto runDuration = ccdb->getRunDuration(mRunNumber); + mSOR = runDuration.first; + mMinSeconds = std::floor(mSOR * 1.e-3); /// round tsSOR to the highest integer lower than tsSOR + double maxSec = std::ceil(runDuration.second * 1.e-3); /// round tsEOR to the lowest integer higher than tsEOR + const AxisSpec axisSeconds{static_cast((maxSec - mMinSeconds) / 20.f), 0, maxSec - mMinSeconds, "Seconds since SOR"}; + gHadronicRate[mRunNumber] = histos.add(Form("HadronicRate/%i", mRunNumber), ";Time since SOR (s);Hadronic rate (kHz)", kTH2D, {axisSeconds, {510, 0., 51.}}).get(); + } + gCurrentHadronicRate = gHadronicRate[mRunNumber]; + } + template - bool eventSelected(TCollision collision) + bool eventSelected(const TCollision& collision) { if (std::fabs(collision.posZ()) > cfgCutVertex) { return 0; @@ -421,15 +496,32 @@ struct FlowMc { if (cfgRecoEvSel8 && !collision.sel8()) { return 0; } + if (cfgOccupancyEnabled) { + auto occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < cfgOccupancyMin || occupancy > cfgOccupancyMax) { + return 0; + } + } + if (!cfgEvSelRCTflags.value.empty() && !rctChecker(*collision)) { + return 0; + } if (cfgRecoEvkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof return 0; } - if (cfgRecoEvkIsGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (cfgRecoEvkIsGoodITSLayers) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + if (cfgRecoEvkIsGoodITSLayersFlag == kITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return 0; + } + if (cfgRecoEvkIsGoodITSLayersFlag == kITSLayer0123 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return 0; + } + if (cfgRecoEvkIsGoodITSLayersFlag == kITSLayer3 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer3)) { + return 0; + } } if (cfgRecoEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference @@ -458,9 +550,9 @@ struct FlowMc { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { - if (cfgCutDCAzPtDepEnabled && (track.dcaZ() > (0.004f + 0.013f / track.pt()))) { + if (cfgCutDCAzPtDepEnabled && (std::fabs(track.dcaZ()) > fPtDepDCAz->Eval(track.pt()))) { return false; } return myTrackSel.IsSelected(track); @@ -476,12 +568,22 @@ struct FlowMc { int64_t nCh = 0; int64_t nChGlobal = 0; + int64_t nChGen = 0; float centrality = 0; float lRandom = fRndm->Rndm(); float weff = 1.; float wacc = 1.; auto bc = mcCollision.bc_as(); loadCorrections(bc.timestamp()); + if (cfgIRFetch) { + initHadronicRate(bc); + double hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), mRunNumber, "ZNC hadronic") * 1.e-3; // + double seconds = bc.timestamp() * 1.e-3 - mMinSeconds; + if (cfgIRCutEnabled && (hadronicRate < cfgIRMin || hadronicRate > cfgIRMax)) { // cut on hadronic rate + return; + } + gCurrentHadronicRate->Fill(seconds, hadronicRate); + } if (collisions.size() > -1) { histos.fill(HIST("numberOfRecoCollisions"), collisions.size()); // number of times coll was reco-ed @@ -492,8 +594,9 @@ struct FlowMc { } histos.fill(HIST("RecoEventCounter"), 1.5); for (auto const& collision : collisions) { - if (!eventSelected(collision)) + if (!eventSelected(collision)) { return; + } } histos.fill(HIST("RecoEventCounter"), 2.5); } @@ -519,12 +622,16 @@ struct FlowMc { double q4x = 0, q4y = 0; for (auto const& mcParticle : mcParticles) { int pdgCode = std::abs(mcParticle.pdgCode()); - if (pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != kKPlus && pdgCode != PDG_t::kProton) + if (pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != kKPlus && pdgCode != PDG_t::kProton) { continue; - if (!mcParticle.isPhysicalPrimary()) + } + if (!mcParticle.isPhysicalPrimary()) { continue; - if (std::fabs(mcParticle.eta()) > cfgCutEta) // main acceptance + } + if (std::fabs(mcParticle.eta()) > cfgCutEta) { // main acceptance continue; + } + nChGen++; if (mcParticle.has_tracks()) { auto const& tracks = mcParticle.tracks_as(); for (auto const& track : tracks) { @@ -551,6 +658,10 @@ struct FlowMc { } } } + if (nChGlobal > 0 && nChGen > 0) { + // fill only when Nch is not zero + histos.fill(HIST("hNchRecoVsNchGen"), nChGlobal, nChGen); + } if (cfgTrackDensityCorrUse && cfgFlowCumulantEnabled) { psi2Est = std::atan2(q2y, q2x) / 2.; psi3Est = std::atan2(q3y, q3x) / 3.; @@ -567,13 +678,21 @@ struct FlowMc { if (cfgK0Lambda0Enabled) { extraPDGType = (pdgCode != PDG_t::kK0Short && pdgCode != PDG_t::kLambda0); } - if (extraPDGType && pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != kKPlus && pdgCode != PDG_t::kProton) + if (extraPDGType && pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != kKPlus && pdgCode != PDG_t::kProton) { continue; + } - if (!mcParticle.isPhysicalPrimary()) + bool isPhysicalPrimary = mcParticle.isPhysicalPrimary(); + const int producedByDecay = 4; + bool isSecondary = (mcParticle.has_mothers() && mcParticle.getProcess() == producedByDecay); + bool isAcceptedSecondary = (cfgAcceptSecondaries) ? isSecondary : false; + if (!isPhysicalPrimary && !isAcceptedSecondary) { continue; - if (std::fabs(mcParticle.eta()) > cfgCutEta) // main acceptance + } + + if (std::fabs(mcParticle.eta()) > cfgCutEta) { // main acceptance continue; + } float deltaPhi = mcParticle.phi() - mcCollision.eventPlaneAngle(); deltaPhi = RecoDecay::constrainAngle(deltaPhi); @@ -582,21 +701,41 @@ struct FlowMc { histos.fill(HIST("hPtNchGenerated"), mcParticle.pt(), nChGlobal); histos.fill(HIST("hPtMCGen"), mcParticle.pt()); histos.fill(HIST("hEtaPtVtxzMCGen"), mcParticle.eta(), mcParticle.pt(), vtxz); - if (pdgCode == PDG_t::kPiPlus) + if (pdgCode == PDG_t::kPiPlus) { histos.fill(HIST("hPtNchGeneratedPion"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kKPlus) + } + if (pdgCode == PDG_t::kKPlus) { histos.fill(HIST("hPtNchGeneratedKaon"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kProton) + } + if (pdgCode == PDG_t::kProton) { histos.fill(HIST("hPtNchGeneratedProton"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kK0Short) + } + if (pdgCode == PDG_t::kK0Short) { histos.fill(HIST("hPtNchGeneratedK0"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kLambda0) + } + if (pdgCode == PDG_t::kLambda0) { histos.fill(HIST("hPtNchGeneratedLambda"), mcParticle.pt(), nChGlobal); - + } + if (mcParticle.has_daughters()) { + for (const auto& d : mcParticle.template daughters_as()) { + if (std::abs(d.pdgCode()) == PDG_t::kPiPlus) { + if (pdgCode == PDG_t::kK0Short) { + histos.fill(HIST("hPtNchGeneratedK0Pions"), d.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kLambda0) { + histos.fill(HIST("hPtNchGeneratedLambdaPions"), d.pt(), nChGlobal); + } + } + if (pdgCode == PDG_t::kLambda0 && std::abs(d.pdgCode()) == PDG_t::kProton) { + histos.fill(HIST("hPtNchGeneratedLambdaProtons"), d.pt(), nChGlobal); + } + } + } nCh++; bool validGlobal = false; bool validTrack = false; + bool validTOFTrack = false; bool validTPCTrack = false; bool validITSTrack = false; bool validITSABTrack = false; @@ -614,6 +753,9 @@ struct FlowMc { if (!cfgIsGlobalTrack && track.hasTPC() && track.hasITS()) { validGlobal = true; } + if (track.hasTOF() && validGlobal) { + validTOFTrack = true; + } if (track.hasTPC() || track.hasITS()) { validTrack = true; } @@ -628,13 +770,14 @@ struct FlowMc { } } } - bool withinPtRef = (cfgCutPtRefMin < mcParticle.pt()) && (mcParticle.pt() < cfgCutPtRefMax); // within RF pT range bool withinPtPOI = (cfgCutPtPOIMin < mcParticle.pt()) && (mcParticle.pt() < cfgCutPtPOIMax); // within POI pT range - if (cfgOutputNUAWeights && withinPtRef) + if (cfgOutputNUAWeights && withinPtRef) { fWeights->fill(mcParticle.phi(), mcParticle.eta(), vtxz, mcParticle.pt(), 0, 0); - if (!setCurrentParticleWeights(weff, wacc, mcParticle.phi(), mcParticle.eta(), mcParticle.pt(), vtxz)) + } + if (!setCurrentParticleWeights(weff, wacc, mcParticle.phi(), mcParticle.eta(), mcParticle.pt(), vtxz)) { continue; + } if (cfgTrackDensityCorrUse && cfgFlowCumulantEnabled && withinPtRef) { double fphi = v2 * std::cos(2 * (mcParticle.phi() - psi2Est)) + v3 * std::cos(3 * (mcParticle.phi() - psi3Est)) + v4 * std::cos(4 * (mcParticle.phi() - psi4Est)); fphi = (1 + 2 * fphi); @@ -650,20 +793,26 @@ struct FlowMc { } if (cfgFlowCumulantEnabled) { - if (withinPtRef) + if (withinPtRef) { fGFWTrue->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 1); - if (withinPtPOI) + } + if (withinPtPOI) { fGFWTrue->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 2); - if (withinPtPOI && withinPtRef) + } + if (withinPtPOI && withinPtRef) { fGFWTrue->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 4); + } if (validGlobal) { - if (withinPtRef) + if (withinPtRef) { fGFWReco->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 1); - if (withinPtPOI) + } + if (withinPtPOI) { fGFWReco->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 2); - if (withinPtPOI && withinPtRef) + } + if (withinPtPOI && withinPtRef) { fGFWReco->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 4); + } } } @@ -684,16 +833,40 @@ struct FlowMc { histos.fill(HIST("hPtNchGlobal"), mcParticle.pt(), nChGlobal); histos.fill(HIST("hPtMCGlobal"), mcParticle.pt()); histos.fill(HIST("hEtaPtVtxzMCGlobal"), mcParticle.eta(), mcParticle.pt(), vtxz); - if (pdgCode == PDG_t::kPiPlus) + if (pdgCode == PDG_t::kPiPlus) { histos.fill(HIST("hPtNchGlobalPion"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kKPlus) + } + if (pdgCode == PDG_t::kKPlus) { histos.fill(HIST("hPtNchGlobalKaon"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kProton) + } + if (pdgCode == PDG_t::kProton) { histos.fill(HIST("hPtNchGlobalProton"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kK0Short) + } + if (pdgCode == PDG_t::kK0Short) { histos.fill(HIST("hPtNchGlobalK0"), mcParticle.pt(), nChGlobal); - if (pdgCode == PDG_t::kLambda0) + } + if (pdgCode == PDG_t::kLambda0) { histos.fill(HIST("hPtNchGlobalLambda"), mcParticle.pt(), nChGlobal); + } + if (!cfgRequireTOF || validTOFTrack) { + if (mcParticle.has_mothers()) { + for (const auto& m : mcParticle.template mothers_as()) { + if (!m.isPhysicalPrimary()) { + continue; + } + if (pdgCode == PDG_t::kPiPlus) { + if (m.pdgCode() == PDG_t::kK0Short) { + histos.fill(HIST("hPtNchGlobalK0Pions"), mcParticle.pt(), nChGlobal); + } + if (m.pdgCode() == PDG_t::kLambda0) { + histos.fill(HIST("hPtNchGlobalLambdaPions"), mcParticle.pt(), nChGlobal); + } + } + if (pdgCode == PDG_t::kProton && m.pdgCode() == PDG_t::kLambda0) + histos.fill(HIST("hPtNchGlobalLambdaProtons"), mcParticle.pt(), nChGlobal); + } + } + } } // if any track present, fill if (validTrack) diff --git a/PWGCF/Flow/Tasks/flowPbpbPikp.cxx b/PWGCF/Flow/Tasks/flowPbpbPikp.cxx index de29ae33e9f..8abf37f3f09 100644 --- a/PWGCF/Flow/Tasks/flowPbpbPikp.cxx +++ b/PWGCF/Flow/Tasks/flowPbpbPikp.cxx @@ -150,7 +150,7 @@ struct FlowPbpbPikp { std::vector eventCuts; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtPOIMin) && (aod::track::pt < cfgCutPtPOIMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); + Filter trackFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtPOIMin) && (aod::track::pt < cfgCutPtPOIMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); using AodCollisions = soa::Filtered>; using AodTracksWithoutBayes = soa::Filtered>; @@ -474,7 +474,7 @@ struct FlowPbpbPikp { } template - void fillQA(const TCollision collision, const TTrack track, int pidIndex, double wacc) + void fillQA(const TCollision& collision, const TTrack& track, int pidIndex, double wacc) { histos.fill(HIST("partCount"), pidIndex - 1, collision.centFT0C(), track.pt()); switch (pidIndex) { @@ -521,7 +521,7 @@ struct FlowPbpbPikp { } // end of fillQA template - int getNsigmaPIDTpcTof(TTrack track) + int getNsigmaPIDTpcTof(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -552,7 +552,7 @@ struct FlowPbpbPikp { } template - int getNsigmaPIDAssymmetric(TTrack track) + int getNsigmaPIDAssymmetric(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -706,7 +706,7 @@ struct FlowPbpbPikp { } template - double getAcceptance(TTrack track, const TCollision collision, int index) + double getAcceptance(const TTrack& track, const TCollision& collision, int index) { // 0 = ref, 1 = ch, 2 = pi, 3 = ka, 4 = pr if (index < 0 || index >= kCount_OutputSpecies) { return 1; @@ -735,7 +735,7 @@ struct FlowPbpbPikp { } template - void fillWeights(const TTrack track, const TCollision collision, const int& pid_index, const int& run) + void fillWeights(const TTrack& track, const TCollision& collision, const int& pid_index, const int& run) { double cent = collision.centFT0C(); double vtxz = collision.posZ(); @@ -783,7 +783,7 @@ struct FlowPbpbPikp { } template - bool selectionEvent(TCollision collision, const int mult, const float cent) + bool selectionEvent(const TCollision& collision, const int mult, const float cent) { histos.fill(HIST("hEventCount"), kFilteredEvents); if (!collision.sel8()) { diff --git a/PWGCF/Flow/Tasks/flowPidCme.cxx b/PWGCF/Flow/Tasks/flowPidCme.cxx index 46cbb863bde..1194b1d517c 100644 --- a/PWGCF/Flow/Tasks/flowPidCme.cxx +++ b/PWGCF/Flow/Tasks/flowPidCme.cxx @@ -230,14 +230,14 @@ struct FillPIDcolums { }; template - bool selTrackPid(const TrackType track) + bool selTrackPid(const TrackType& track) { if ((track.pt() < cfgMinPtPID) || (track.pt() > cfgMaxPtPID)) return false; if (std::abs(track.eta()) > cfgMaxEtaPID) return false; if (cfgRequireGlobalTrack) { - if (!(track.isGlobalTrackSDD() == (uint8_t) true)) + if (!(track.isGlobalTrackSDD() == (uint8_t)true)) return false; } if (cfgUseCostomTrackCuts) { @@ -2624,7 +2624,7 @@ struct FlowPidCme { } template - bool selTrack(const TrackType track, float centrality) + bool selTrack(const TrackType& track, float centrality) { if (cfgkOpenDebugPIDCME) { LOGF(info, "====================Entering track selection============================="); diff --git a/PWGCF/Flow/Tasks/flowPtEfficiency.cxx b/PWGCF/Flow/Tasks/flowPtEfficiency.cxx index fadb727ba77..e22a4c36566 100644 --- a/PWGCF/Flow/Tasks/flowPtEfficiency.cxx +++ b/PWGCF/Flow/Tasks/flowPtEfficiency.cxx @@ -391,7 +391,7 @@ struct FlowPtEfficiency { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { if (cfgkIsTrackGlobal && !track.isGlobalTrack()) { return false; diff --git a/PWGCF/Flow/Tasks/flowQa.cxx b/PWGCF/Flow/Tasks/flowQa.cxx index ff8c227fa28..2221f27a321 100644 --- a/PWGCF/Flow/Tasks/flowQa.cxx +++ b/PWGCF/Flow/Tasks/flowQa.cxx @@ -494,7 +494,7 @@ struct FlowQa { } template - bool eventSelected(TCollision collision) + bool eventSelected(const TCollision& collision) { registry.fill(HIST("hEventCountSpecific"), 0.5); if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -546,7 +546,7 @@ struct FlowQa { } template - void eventCounterQA(TCollision collision) + void eventCounterQA(const TCollision& collision) { registry.fill(HIST("hEventCountTentative"), 0.5); // Regradless of the event selection, fill the event counter histograms @@ -568,7 +568,7 @@ struct FlowQa { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { // track type selection bool passTrackTypeSelection = false; diff --git a/PWGCF/Flow/Tasks/flowRunbyRun.cxx b/PWGCF/Flow/Tasks/flowRunbyRun.cxx index 149ec2b87e2..bf4bb37d581 100644 --- a/PWGCF/Flow/Tasks/flowRunbyRun.cxx +++ b/PWGCF/Flow/Tasks/flowRunbyRun.cxx @@ -285,7 +285,7 @@ struct FlowRunbyRun { } template - void fillProfile(const GFW::CorrConfig& corrconf, std::shared_ptr profile, const double& cent) + void fillProfile(const GFW::CorrConfig& corrconf, const std::shared_ptr& profile, const double& cent) { double dnx, val; dnx = fGFW->Calculate(corrconf, 0, kTRUE).real(); @@ -441,7 +441,7 @@ struct FlowRunbyRun { } template - bool eventSelected(TCollision collision, const int multTrk, const float centrality, const int runNumber) + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const int runNumber) { th1sList[runNumber][hEventCountSpecific]->Fill(0.5); if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -519,7 +519,7 @@ struct FlowRunbyRun { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { if (cfgCutDCAzPtDepEnabled && (std::fabs(track.dcaZ()) > (0.004f + 0.013f / track.pt()))) return false; diff --git a/PWGCF/Flow/Tasks/flowSP.cxx b/PWGCF/Flow/Tasks/flowSP.cxx index f1b672c76e6..26b96ec9c80 100644 --- a/PWGCF/Flow/Tasks/flowSP.cxx +++ b/PWGCF/Flow/Tasks/flowSP.cxx @@ -53,11 +53,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -70,106 +72,112 @@ using namespace o2::framework::expressions; using namespace o2::aod::rctsel; // using namespace o2::analysis; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) struct FlowSP { - RCTFlagsChecker rctChecker; + // event selection configurable group struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgEvtUseRCTFlagChecker, bool, false, "Evt sel: use RCT flag checker"); - O2_DEFINE_CONFIGURABLE(cfgEvtRCTFlagCheckerLabel, std::string, "CBT_hadronPID", "Evt sel: RCT flag checker label (CBT, CBT_hadronPID)"); // all Labels can be found in Common/CCDB/RCTSelectionFlags.h - O2_DEFINE_CONFIGURABLE(cfgEvtRCTFlagCheckerZDCCheck, bool, false, "Evt sel: RCT flag checker ZDC check"); - O2_DEFINE_CONFIGURABLE(cfgEvtRCTFlagCheckerLimitAcceptAsBad, bool, false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"); - } rctFlags; + O2_DEFINE_CONFIGURABLE(cEvtUseRCTFlagChecker, bool, true, "Evt sel: use RCT flag checker"); + O2_DEFINE_CONFIGURABLE(cEvtRCTFlagCheckerLabel, std::string, "CBT_hadronPID", "Evt sel: RCT flag checker label (CBT, CBT_hadronPID)"); // all Labels can be found in Common/CCDB/RCTSelectionFlags.h + O2_DEFINE_CONFIGURABLE(cEvtRCTFlagCheckerZDCCheck, bool, true, "Evt sel: RCT flag checker ZDC check"); + O2_DEFINE_CONFIGURABLE(cEvtRCTFlagCheckerLimitAcceptAsBad, bool, true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"); + O2_DEFINE_CONFIGURABLE(cEvSelsUseAdditionalEventCut, bool, true, "Bool to enable Additional Event Cut"); + O2_DEFINE_CONFIGURABLE(cEvSelsMaxOccupancy, int, 10000, "Maximum occupancy of selected events"); + O2_DEFINE_CONFIGURABLE(cEvSelsMinOccupancy, int, 0, "Minimum occupancy of selected events"); + O2_DEFINE_CONFIGURABLE(cEvSelsNoSameBunchPileupCut, bool, true, "kNoSameBunchPileupCut"); + O2_DEFINE_CONFIGURABLE(cEvSelsIsGoodZvtxFT0vsPV, bool, true, "kIsGoodZvtxFT0vsPV"); + O2_DEFINE_CONFIGURABLE(cEvSelsNoCollInTimeRangeStandard, bool, true, "kNoCollInTimeRangeStandard"); + O2_DEFINE_CONFIGURABLE(cEvSelsNoCollInTimeRangeNarrow, bool, false, "kNoCollInTimeRangeNarrow"); + O2_DEFINE_CONFIGURABLE(cEvSelsDoOccupancySel, bool, true, "Bool for event selection on detector occupancy"); + O2_DEFINE_CONFIGURABLE(cEvSelsIsVertexITSTPC, bool, true, "Selects collisions with at least one ITS-TPC track"); + O2_DEFINE_CONFIGURABLE(cEvSelsIsGoodITSLayersAll, bool, true, "Cut time intervals with dead ITS staves"); + O2_DEFINE_CONFIGURABLE(cEvSelsIsGoodITSLayer0123, bool, false, "Cut time intervals with dead ITS staves"); + + // QA Plots + O2_DEFINE_CONFIGURABLE(cFillEventQA, bool, false, "Fill histograms for event QA"); + O2_DEFINE_CONFIGURABLE(cFillTrackQA, bool, false, "Fill histograms for track QA"); + O2_DEFINE_CONFIGURABLE(cFillPIDQA, bool, false, "Fill histograms for PID QA"); + O2_DEFINE_CONFIGURABLE(cFillEventPlaneQA, bool, false, "Fill histograms for Event Plane QA"); + O2_DEFINE_CONFIGURABLE(cFillQABefore, bool, false, "Fill QA histograms before cuts, only for processData"); + O2_DEFINE_CONFIGURABLE(cFillMeanPT, bool, false, "Fill histograms for mean PX/PT"); + O2_DEFINE_CONFIGURABLE(cFillMeanPTextra, bool, false, "Fill histograms for mean PX/PT extra"); + O2_DEFINE_CONFIGURABLE(cUseCentAveragePt, bool, false, "Use in 1% centrality intervals and not cent average"); + O2_DEFINE_CONFIGURABLE(cFillWithMCParticle, bool, false, "Fill histograms with MCParticle instead of Track"); + // Flags to make and fill histograms + O2_DEFINE_CONFIGURABLE(cFillGeneralV1Histos, bool, true, "Fill histograms for vn analysis"); + O2_DEFINE_CONFIGURABLE(cFillMixedHarmonics, bool, true, "Flag to make and fill histos for mixed harmonics"); + O2_DEFINE_CONFIGURABLE(cFillEventPlane, bool, false, "Flag to make and fill histos with Event Plane"); + O2_DEFINE_CONFIGURABLE(cFillXandYterms, bool, false, "Flag to make and fill histos for with separate x and y terms for SPM"); + O2_DEFINE_CONFIGURABLE(cFillChargeDependence, bool, true, "Flag to make and fill histos for charge dependent flow"); + O2_DEFINE_CONFIGURABLE(cFillChargeDependenceQA, bool, true, "Flag to make and fill QA histos for charge dependent flow"); + O2_DEFINE_CONFIGURABLE(cFillPID, bool, false, "Flag to make and fill histos for PID flow"); + // Centrality Estimators -> standard is FT0C + O2_DEFINE_CONFIGURABLE(cCentFT0Cvariant1, bool, false, "Set centrality estimator to CentFT0Cvariant1"); + O2_DEFINE_CONFIGURABLE(cCentFT0M, bool, false, "Set centrality estimator to CentFT0M"); + O2_DEFINE_CONFIGURABLE(cCentFV0A, bool, false, "Set centrality estimator to CentFV0A"); + O2_DEFINE_CONFIGURABLE(cCentNGlobal, bool, false, "Set centrality estimator to CentNGlobal"); + // Standard selections + O2_DEFINE_CONFIGURABLE(cTrackSelsDCAxy, float, 0.2, "Cut on DCA in the transverse direction (cm)"); + O2_DEFINE_CONFIGURABLE(cTrackSelsDCAz, float, 0.2, "Cut on DCA in the longitudinal direction (cm)"); + O2_DEFINE_CONFIGURABLE(cTrackSelsNcls, float, 70, "Cut on number of TPC clusters found"); + O2_DEFINE_CONFIGURABLE(cTrackSelsFshcls, float, 0.4, "Cut on fraction of shared TPC clusters found"); + O2_DEFINE_CONFIGURABLE(cTrackSelsPtmin, float, 0.2, "minimum pt (GeV/c)"); + O2_DEFINE_CONFIGURABLE(cTrackSelsPtmax, float, 10, "maximum pt (GeV/c)"); + O2_DEFINE_CONFIGURABLE(cTrackSelsEta, float, 0.8, "eta cut"); + O2_DEFINE_CONFIGURABLE(cIsMCReco, bool, false, "Is MC Reco"); + O2_DEFINE_CONFIGURABLE(cEvSelsVtxZ, float, 10, "vertex cut (cm)"); + O2_DEFINE_CONFIGURABLE(cMagField, float, 99999, "Configurable magnetic field;default CCDB will be queried"); + O2_DEFINE_CONFIGURABLE(cCentMin, float, 0, "Minimum cenrality for selected events"); + O2_DEFINE_CONFIGURABLE(cCentMax, float, 90, "Maximum cenrality for selected events"); + O2_DEFINE_CONFIGURABLE(cFilterLeptons, bool, true, "Filter out leptons from MCGenerated by requiring |pdgCode| > 100"); + O2_DEFINE_CONFIGURABLE(cDoGeneratedInReco, bool, true, "Do MC Generated inside the MCReco process function on mcCollisions"); + // NUA and NUE weights + O2_DEFINE_CONFIGURABLE(cFillWeights, bool, true, "Fill NUA weights"); + O2_DEFINE_CONFIGURABLE(cFillWeightsPOS, bool, true, "Fill NUA weights only for positive charges"); + O2_DEFINE_CONFIGURABLE(cFillWeightsNEG, bool, true, "Fill NUA weights only for negative charges"); + O2_DEFINE_CONFIGURABLE(cUseNUA1D, bool, true, "Use 1D NUA weights (only phi)"); + O2_DEFINE_CONFIGURABLE(cUseNUA2D, bool, false, "Use 2D NUA weights (phi and eta)"); + O2_DEFINE_CONFIGURABLE(cUseNUE2D, bool, false, "Use 2D NUE weights"); + O2_DEFINE_CONFIGURABLE(cUseNUE3D, bool, true, "Use 3D NUE weights (pt, eta, centrality)"); + O2_DEFINE_CONFIGURABLE(cUseNUE2Deta, bool, false, "Use 2D NUE weights TRUE: (pt and eta) FALSE: (pt and centrality)"); + // Additional track Selections + O2_DEFINE_CONFIGURABLE(cTrackSelsUseAdditionalTrackCut, bool, false, "Bool to enable Additional Track Cut"); + O2_DEFINE_CONFIGURABLE(cTrackSelsDoDCApt, bool, true, "Apply Pt dependent DCAz cut"); + O2_DEFINE_CONFIGURABLE(cTrackSelsDCAfunc, std::string, "0.0105 + 0.035 / TMath::Power(x,1.1)", "Function for pT-dependent DCA-cut (Default y = 0.0105 + 0.035 / TMath::Power(x,1.1))"); + O2_DEFINE_CONFIGURABLE(cTrackSelsPIDNsigma, float, 2.0, "nSigma cut for PID"); + O2_DEFINE_CONFIGURABLE(cTrackSelDoTrackQAvsCent, bool, true, "Do track selection QA plots as function of centrality"); + // harmonics for v coefficients + O2_DEFINE_CONFIGURABLE(cHarm, int, 1, "Flow harmonic n for ux and uy: (Cos(n*phi), Sin(n*phi))"); + O2_DEFINE_CONFIGURABLE(cHarmMixed, int, 2, "Flow harmonic n for ux and uy in mixed harmonics (MH): (Cos(n*phi), Sin(n*phi))"); + // settings for CCDB data + O2_DEFINE_CONFIGURABLE(cCCDBdir_QQ, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/meanQQ/noGain", "ccdb dir for average QQ values in 1% centrality bins"); + O2_DEFINE_CONFIGURABLE(cCCDBdir_SP, std::string, "", "ccdb dir for average event plane resolution in 1% centrality bins"); + O2_DEFINE_CONFIGURABLE(cCCDB_NUA, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/noGain/Default", "ccdb dir for NUA corrections"); + O2_DEFINE_CONFIGURABLE(cCCDB_NUE, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/NUE/Default", "ccdb dir for NUE corrections (pt)"); + O2_DEFINE_CONFIGURABLE(cCCDB_NUE2D, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/NUE/2D", "ccdb dir for NUE 2D corrections (pt, eta)"); + O2_DEFINE_CONFIGURABLE(cCCDB_NUE3D, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/NUE3D/Default", "ccdb dir for NUE 3D corrections (pt, eta, centrality)"); + O2_DEFINE_CONFIGURABLE(cCCDBdir_centrality, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/centWeight/Default", "ccdb dir for Centrality corrections"); + O2_DEFINE_CONFIGURABLE(cCCDBdir_meanPt, std::string, "", "ccdb dir for Mean Pt corrections"); + + // Configurables containing vector + O2_DEFINE_CONFIGURABLE(cUsePredeFinedSigma, bool, true, "Use one of the pre-defines settings for the multiplicity vs. centrality plots"); + O2_DEFINE_CONFIGURABLE(cUsePredeFinedSigmaYear, int, 2023, "Predifine what year you want to use (2023/2024)"); + O2_DEFINE_CONFIGURABLE(cUsePredeFinedSigmaNsigma, int, 2, "Sigma used for cuts (1,2,3)"); + Configurable> cEvSelsMultPv{"cEvSelsMultPv", std::vector{2223.49, -75.1444, 0.963572, -0.00570399, 1.34877e-05, 3790.99, -137.064, 2.13044, -0.017122, 5.82834e-05}, "Multiplicity cuts (PV) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; + Configurable> cEvSelsMult{"cEvSelsMult", std::vector{1301.56, -41.4615, 0.478224, -0.00239449, 4.46966e-06, 2967.6, -102.927, 1.47488, -0.0106534, 3.28622e-05}, "Multiplicity cuts (Global) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; + Configurable> cPtBinning{"cPtBinning", std::vector{0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "pT binning for vn"}; - // event selection configurable group - O2_DEFINE_CONFIGURABLE(cfgEvSelsUseAdditionalEventCut, bool, true, "Bool to enable Additional Event Cut"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsMaxOccupancy, int, 10000, "Maximum occupancy of selected events"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsMinOccupancy, int, 0, "Minimum occupancy of selected events"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsNoSameBunchPileupCut, bool, true, "kNoSameBunchPileupCut"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsIsGoodZvtxFT0vsPV, bool, true, "kIsGoodZvtxFT0vsPV"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsNoCollInTimeRangeStandard, bool, true, "kNoCollInTimeRangeStandard"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsNoCollInTimeRangeNarrow, bool, true, "kNoCollInTimeRangeNarrow"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsDoOccupancySel, bool, true, "Bool for event selection on detector occupancy"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsIsVertexITSTPC, bool, true, "Selects collisions with at least one ITS-TPC track"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsIsGoodITSLayersAll, bool, true, "Cut time intervals with dead ITS staves"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsIsGoodITSLayer0123, bool, true, "Cut time intervals with dead ITS staves"); - - // QA Plots - O2_DEFINE_CONFIGURABLE(cfgFillEventQA, bool, false, "Fill histograms for event QA"); - O2_DEFINE_CONFIGURABLE(cfgFillTrackQA, bool, false, "Fill histograms for track QA"); - O2_DEFINE_CONFIGURABLE(cfgFillPIDQA, bool, false, "Fill histograms for PID QA"); - O2_DEFINE_CONFIGURABLE(cfgFillEventPlaneQA, bool, false, "Fill histograms for Event Plane QA"); - O2_DEFINE_CONFIGURABLE(cfgFillQABefore, bool, false, "Fill QA histograms before cuts, only for processData"); - O2_DEFINE_CONFIGURABLE(cfgFillMeanPT, bool, false, "Fill histograms for mean PX/PT"); - O2_DEFINE_CONFIGURABLE(cfgUseCentAveragePt, bool, false, "Use in 1% centrality intervals and not ecent average"); - // Flags to make and fill histograms - O2_DEFINE_CONFIGURABLE(cfgFillGeneralV1Histos, bool, true, "Fill histograms for vn analysis"); - O2_DEFINE_CONFIGURABLE(cfgFillMixedHarmonics, bool, true, "Flag to make and fill histos for mixed harmonics"); - O2_DEFINE_CONFIGURABLE(cfgFillEventPlane, bool, false, "Flag to make and fill histos with Event Plane"); - O2_DEFINE_CONFIGURABLE(cfgFillXandYterms, bool, false, "Flag to make and fill histos for with separate x and y terms for SPM"); - O2_DEFINE_CONFIGURABLE(cfgFillChargeDependence, bool, true, "Flag to make and fill histos for charge dependent flow"); - O2_DEFINE_CONFIGURABLE(cfgFillChargeDependenceQA, bool, true, "Flag to make and fill QA histos for charge dependent flow"); - O2_DEFINE_CONFIGURABLE(cfgFillPID, bool, false, "Flag to make and fill histos for PID flow"); - // Centrality Estimators -> standard is FT0C - O2_DEFINE_CONFIGURABLE(cfgCentFT0Cvariant1, bool, false, "Set centrality estimator to cfgCentFT0Cvariant1"); - O2_DEFINE_CONFIGURABLE(cfgCentFT0M, bool, false, "Set centrality estimator to cfgCentFT0M"); - O2_DEFINE_CONFIGURABLE(cfgCentFV0A, bool, false, "Set centrality estimator to cfgCentFV0A"); - O2_DEFINE_CONFIGURABLE(cfgCentNGlobal, bool, false, "Set centrality estimator to cfgCentNGlobal"); - // Standard selections - O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAxy, float, 0.2, "Cut on DCA in the transverse direction (cm)"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAz, float, 2, "Cut on DCA in the longitudinal direction (cm)"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsNcls, float, 70, "Cut on number of TPC clusters found"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsFshcls, float, 0.4, "Cut on fraction of shared TPC clusters found"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsPtmin, float, 0.2, "minimum pt (GeV/c)"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsPtmax, float, 10, "maximum pt (GeV/c)"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsEta, float, 0.8, "eta cut"); - O2_DEFINE_CONFIGURABLE(cfgEvSelsVtxZ, float, 10, "vertex cut (cm)"); - O2_DEFINE_CONFIGURABLE(cfgMagField, float, 99999, "Configurable magnetic field;default CCDB will be queried"); - O2_DEFINE_CONFIGURABLE(cfgCentMin, float, 0, "Minimum cenrality for selected events"); - O2_DEFINE_CONFIGURABLE(cfgCentMax, float, 90, "Maximum cenrality for selected events"); - O2_DEFINE_CONFIGURABLE(cfgFilterLeptons, bool, true, "Filter out leptons from MCGenerated by requiring |pdgCode| > 100"); - // NUA and NUE weights - O2_DEFINE_CONFIGURABLE(cfgFillWeights, bool, true, "Fill NUA weights"); - O2_DEFINE_CONFIGURABLE(cfgFillWeightsPOS, bool, true, "Fill NUA weights only for positive charges"); - O2_DEFINE_CONFIGURABLE(cfgFillWeightsNEG, bool, true, "Fill NUA weights only for negative charges"); - O2_DEFINE_CONFIGURABLE(cfgUseNUA1D, bool, false, "Use 1D NUA weights (only phi)"); - O2_DEFINE_CONFIGURABLE(cfgUseNUA2D, bool, true, "Use 2D NUA weights (phi and eta)"); - O2_DEFINE_CONFIGURABLE(cfgUseNUE2D, bool, true, "Use 2D NUE weights"); - O2_DEFINE_CONFIGURABLE(cfgUseNUE2Deta, bool, true, "Use 2D NUE weights TRUE: (pt and eta) FALSE: (pt and centrality)"); - // Additional track Selections - O2_DEFINE_CONFIGURABLE(cfgTrackSelsUseAdditionalTrackCut, bool, false, "Bool to enable Additional Track Cut"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsDoDCApt, bool, false, "Apply Pt dependent DCAz cut"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCApt1, float, 0.1, "DcaZ < const + (a * b) / pt^1.1 -> this sets a"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCApt2, float, 0.035, "DcaZ < const + (a * b) / pt^1.1 -> this sets b"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsDCAptConsMin, float, 0.1, "DcaZ < const + (a * b) / pt^1.1 -> this sets const"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelsPIDNsigma, float, 2.0, "nSigma cut for PID"); - O2_DEFINE_CONFIGURABLE(cfgTrackSelDoTrackQAvsCent, bool, true, "Do track selection QA plots as function of centrality"); - // harmonics for v coefficients - O2_DEFINE_CONFIGURABLE(cfgHarm, int, 1, "Flow harmonic n for ux and uy: (Cos(n*phi), Sin(n*phi))"); - O2_DEFINE_CONFIGURABLE(cfgHarmMixed, int, 2, "Flow harmonic n for ux and uy in mixed harmonics (MH): (Cos(n*phi), Sin(n*phi))"); - // settings for CCDB data - O2_DEFINE_CONFIGURABLE(cfgCCDBdir_QQ, std::string, "Users/c/ckoster/ZDC/LHC23_PbPb_pass5/meanQQ/Default", "ccdb dir for average QQ values in 1% centrality bins"); - O2_DEFINE_CONFIGURABLE(cfgCCDBdir_SP, std::string, "", "ccdb dir for average event plane resolution in 1% centrality bins"); - O2_DEFINE_CONFIGURABLE(cfgCCDB_NUA, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/Default", "ccdb dir for NUA corrections"); - O2_DEFINE_CONFIGURABLE(cfgCCDB_NUE, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/NUE/Default", "ccdb dir for NUE corrections (pt)"); - O2_DEFINE_CONFIGURABLE(cfgCCDB_NUE2D, std::string, "Users/c/ckoster/flowSP/LHC23_PbPb_pass5/NUE/2D", "ccdb dir for NUE 2D corrections (eta, pt)"); - O2_DEFINE_CONFIGURABLE(cfgCCDBdir_centrality, std::string, "", "ccdb dir for Centrality corrections"); - O2_DEFINE_CONFIGURABLE(cfgCCDBdir_meanPt, std::string, "", "ccdb dir for Mean Pt corrections"); - // Confogirable axis - ConfigurableAxis axisCentrality{"axisCentrality", {20, 0, 100}, "Centrality bins for vn "}; - ConfigurableAxis axisNch = {"axisNch", {400, 0, 4000}, "Global N_{ch}"}; - ConfigurableAxis axisMultpv = {"axisMultpv", {400, 0, 4000}, "N_{ch} (PV)"}; - // Configurables containing vector - Configurable> cfgEvSelsMultPv{"cfgEvSelsMultPv", std::vector{2223.49, -75.1444, 0.963572, -0.00570399, 1.34877e-05, 3790.99, -137.064, 2.13044, -0.017122, 5.82834e-05}, "Multiplicity cuts (PV) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; - Configurable> cfgEvSelsMult{"cfgEvSelsMult", std::vector{1301.56, -41.4615, 0.478224, -0.00239449, 4.46966e-06, 2967.6, -102.927, 1.47488, -0.0106534, 3.28622e-05}, "Multiplicity cuts (Global) first 5 parameters cutLOW last 5 cutHIGH (Default is +-2sigma pass5) "}; - - Filter collisionFilter = nabs(aod::collision::posZ) < cfgEvSelsVtxZ; - Filter trackFilter = nabs(aod::track::eta) < cfgTrackSelsEta && aod::track::pt > cfgTrackSelsPtmin&& aod::track::pt < cfgTrackSelsPtmax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && nabs(aod::track::dcaXY) < cfgTrackSelsDCAxy&& nabs(aod::track::dcaZ) < cfgTrackSelsDCAz; - Filter trackFilterMC = nabs(aod::mcparticle::eta) < cfgTrackSelsEta && aod::mcparticle::pt > cfgTrackSelsPtmin&& aod::mcparticle::pt < cfgTrackSelsPtmax; + } cfg; + + RCTFlagsChecker rctChecker; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfg.cEvSelsVtxZ; + Filter trackFilter = nabs(aod::track::eta) < cfg.cTrackSelsEta && aod::track::pt > cfg.cTrackSelsPtmin&& aod::track::pt < cfg.cTrackSelsPtmax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true) || cfg.cIsMCReco) && nabs(aod::track::dcaXY) < cfg.cTrackSelsDCAxy&& nabs(aod::track::dcaZ) < cfg.cTrackSelsDCAz; + Filter trackFilterMC = nabs(aod::mcparticle::eta) < cfg.cTrackSelsEta && aod::mcparticle::pt > cfg.cTrackSelsPtmin&& aod::mcparticle::pt < cfg.cTrackSelsPtmax; using GeneralCollisions = soa::Join; - using UnfilteredTracksPID = soa::Join; - using UnfilteredTracks = soa::Join; + using UnfilteredTracksPID = soa::Join; + using UnfilteredTracks = soa::Join; using UsedTracks = soa::Filtered; using UsedTracksPID = soa::Filtered; @@ -189,15 +197,16 @@ struct FlowSP { Preslice trackPerCollision = aod::track::collisionId; // Connect to ccdb - Service ccdb; - Service pdg; + Service ccdb{}; + Service pdg{}; // struct to hold the correction histos/ struct Config { - std::vector mEfficiency = {}; - std::vector mEfficiency2D = {}; - std::vector mAcceptance = {}; - std::vector mAcceptance2D = {}; + std::vector mEfficiency; + std::vector mEfficiency2D; + std::vector mEfficiency3D; + std::vector mAcceptance; + std::vector mAcceptance2D; bool correctionsLoaded = false; int lastRunNumber = 0; @@ -213,7 +222,7 @@ struct FlowSP { bool clCentrality = false; bool clMeanPt = false; - } cfg; + } conf; struct SPMvars { std::vector> wacc = {{{0, 1.0}, {1, 1.0}, {2, 1.0}, {3, 1.0}}, {{0, 1.0}, {1, 1.0}, {2, 1.0}, {3, 1.0}}, {{0, 1.0}, {1, 1.0}, {2, 1.0}, {3, 1.0}}}; // int for part species, float for weight vector for kIncl, kPos, kNeg @@ -250,6 +259,21 @@ struct FlowSP { double meanPxC = 0; } spm; + struct PtMaps { + std::unique_ptr meanPTMap = std::make_unique("meanPTMap", "meanPTMap", 8, -0.8, 0.8); + std::unique_ptr meanPTMapPos = std::make_unique("meanPTMapPos", "meanPTMapPos", 8, -0.8, 0.8); + std::unique_ptr meanPTMapNeg = std::make_unique("meanPTMapNeg", "meanPTMapNeg", 8, -0.8, 0.8); + + std::unique_ptr relPxA = std::make_unique("relPxA", "relPxA", 8, -0.8, 0.8); + std::unique_ptr relPxC = std::make_unique("relPxC", "relPxC", 8, -0.8, 0.8); + + std::unique_ptr relPxANeg = std::make_unique("relPxANeg", "relPxANeg", 8, -0.8, 0.8); + std::unique_ptr relPxAPos = std::make_unique("relPxAPos", "relPxAPos", 8, -0.8, 0.8); + + std::unique_ptr relPxCNeg = std::make_unique("relPxCNeg", "relPxCNeg", 8, -0.8, 0.8); + std::unique_ptr relPxCPos = std::make_unique("relPxCPos", "relPxCPos", 8, -0.8, 0.8); + } ptmaps; + OutputObj fWeights{GFWWeights("weights")}; OutputObj fWeightsPOS{GFWWeights("weights_positive")}; OutputObj fWeightsNEG{GFWWeights("weights_negative")}; @@ -258,13 +282,16 @@ struct FlowSP { HistogramRegistry histos{"QAhistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; // Event selection cuts - TF1* fPhiCutLow = nullptr; - TF1* fPhiCutHigh = nullptr; - TF1* fMultPVCutLow = nullptr; - TF1* fMultPVCutHigh = nullptr; - TF1* fMultCutLow = nullptr; - TF1* fMultCutHigh = nullptr; - TF1* fMultMultPVCut = nullptr; + std::unique_ptr fPhiCutLow = nullptr; + std::unique_ptr fPhiCutHigh = nullptr; + std::unique_ptr fMultPVCutLow = nullptr; + std::unique_ptr fMultPVCutHigh = nullptr; + std::unique_ptr fMultCutLow = nullptr; + std::unique_ptr fMultCutHigh = nullptr; + std::unique_ptr fMultMultPVCut = nullptr; + + // Track selection DCA cut + std::unique_ptr fDCACut = std::make_unique("fDCACut", cfg.cTrackSelsDCAfunc.value.c_str(), 0, 100); enum SelectionCriteria { evSel_FilteredEvent, @@ -288,12 +315,12 @@ struct FlowSP { trackSel_ZeroCharge, trackSel_Eta, trackSel_Pt, + trackSel_NCls, + trackSel_TPCBoundary, trackSel_DCAxy, trackSel_DCAz, - trackSel_GlobalTracks, - trackSel_NCls, trackSel_FshCls, - trackSel_TPCBoundary, + trackSel_GlobalTracks, trackSel_ParticleWeights, nTrackSelections }; @@ -325,9 +352,9 @@ struct FlowSP { nParticleTypes }; - static constexpr std::string_view Charge[] = {"incl/", "pos/", "neg/"}; - static constexpr std::string_view Species[] = {"", "pion/", "kaon/", "proton/"}; - static constexpr std::string_view Time[] = {"before/", "after/"}; + static constexpr std::array Charge = {"incl/", "pos/", "neg/"}; + static constexpr std::array Species = {"", "pion/", "kaon/", "proton/"}; + static constexpr std::array Time = {"before/", "after/"}; void init(InitContext const&) { @@ -345,6 +372,7 @@ struct FlowSP { AxisSpec axisPhi = {60, 0, constants::math::TwoPI, "#varphi"}; AxisSpec axisEta = {64, -1.6, 1.6, "#eta"}; AxisSpec axisEtaVn = {8, -.8, .8, "#eta"}; + AxisSpec axisCentrality = {20, 0, 100, "Centrality (%)"}; AxisSpec axisVx = {40, -0.01, 0.01, "v_{x}"}; AxisSpec axisVy = {40, -0.01, 0.01, "v_{y}"}; AxisSpec axisVz = {40, -10, 10, "v_{z}"}; @@ -360,15 +388,15 @@ struct FlowSP { AxisSpec axisBeta = {150, 0, 1.5, "Beta for PID"}; AxisSpec axisCharge = {3, 0, 3, "Charge: 0 = inclusive, 1 = positive, 2 = negative"}; AxisSpec axisPx = {100, -0.05, 0.05, "p_{x} (GeV/c)"}; + AxisSpec axisNch = {400, 0, 4000, "Global N_{ch}"}; + AxisSpec axisMultpv = {400, 0, 4000, "N_{ch} (PV)"}; + AxisSpec axisPt = {cfg.cPtBinning, "#it{p}_{T} GeV/#it{c}"}; + int ptbins = cfg.cPtBinning->size() - 1; - std::vector ptbinning = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}; - AxisSpec axisPt = {ptbinning, "#it{p}_{T} GeV/#it{c}"}; - - int ptbins = ptbinning.size() - 1; - - rctChecker.init(rctFlags.cfgEvtRCTFlagCheckerLabel, rctFlags.cfgEvtRCTFlagCheckerZDCCheck, rctFlags.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + rctChecker.init(cfg.cEvtRCTFlagCheckerLabel, cfg.cEvtRCTFlagCheckerZDCCheck, cfg.cEvtRCTFlagCheckerLimitAcceptAsBad); histos.add("hCentrality", "Centrality; Centrality (%); ", {HistType::kTH1D, {axisCent}}); + histos.add("hCentralityCW", "Centrality; Centrality (%); Weighted counts", {HistType::kTH1D, {axisCent}}); histos.add("hEventCount", "Number of Event; Cut; #Events Passed Cut", {HistType::kTH1D, {{nEventSelections, 0, nEventSelections}}}); histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(evSel_FilteredEvent + 1, "Filtered events"); @@ -398,23 +426,23 @@ struct FlowSP { histos.get(HIST("hTrackCount"))->GetXaxis()->SetBinLabel(trackSel_ZeroCharge + 1, "Only charged"); histos.get(HIST("hTrackCount"))->GetXaxis()->SetBinLabel(trackSel_ParticleWeights + 1, "Apply weights"); - if (cfgFillWeights) { + if (cfg.cFillWeights) { registry.add("weights2D/hPhi_Eta_vz", "", kTH3D, {axisPhi, axisEta, axisVz}); registry.add("weights2D/hPhi_Eta_vz_positive", "", kTH3D, {axisPhi, axisEta, axisVz}); registry.add("weights2D/hPhi_Eta_vz_negative", "", kTH3D, {axisPhi, axisEta, axisVz}); // define output objects - fWeights->setPtBins(ptbins, &ptbinning[0]); + fWeights->setPtBins(ptbins, &cfg.cPtBinning.value[0]); fWeights->init(true, false); - fWeightsPOS->setPtBins(ptbins, &ptbinning[0]); + fWeightsPOS->setPtBins(ptbins, &cfg.cPtBinning.value[0]); fWeightsPOS->init(true, false); - fWeightsNEG->setPtBins(ptbins, &ptbinning[0]); + fWeightsNEG->setPtBins(ptbins, &cfg.cPtBinning.value[0]); fWeightsNEG->init(true, false); } - if (cfgFillEventQA) { + if (cfg.cFillEventQA) { histos.add("QA/after/hCentFT0C", " ; Cent FT0C (%); ", {HistType::kTH1D, {axisCent}}); histos.add("QA/after/hCentFT0M", "; Cent FT0M (%); ", {HistType::kTH1D, {axisCent}}); histos.add("QA/after/hCentFV0A", "; Cent FV0A (%); ", {HistType::kTH1D, {axisCent}}); @@ -431,7 +459,7 @@ struct FlowSP { histos.add("QA/after/CentFT0C_vs_CentFV0A", " ; Cent FT0C (%); Cent FV0A (%) ", {HistType::kTH2D, {axisCent, axisCent}}); histos.add("QA/after/CentFT0C_vs_CentNGlobal", " ; Cent FT0C (%); Cent NGlobal (%) ", {HistType::kTH2D, {axisCent, axisCent}}); - if (cfgFillEventPlaneQA && doprocessData) { + if (cfg.cFillEventPlaneQA && doprocessData) { histos.add("QA/after/PsiA_vs_Cent", "", {HistType::kTH2D, {axisPhiPlane, axisCent}}); histos.add("QA/after/PsiC_vs_Cent", "", {HistType::kTH2D, {axisPhiPlane, axisCent}}); histos.add("QA/after/PsiFull_vs_Cent", "", {HistType::kTH2D, {axisPhiPlane, axisCent}}); @@ -444,17 +472,16 @@ struct FlowSP { histos.add("QA/after/PsiA_vs_Vz", "", {HistType::kTH2D, {axisPhiPlane, axisVz}}); histos.add("QA/after/PsiC_vs_Vz", "", {HistType::kTH2D, {axisPhiPlane, axisVz}}); histos.add("QA/after/PsiFull_vs_Vz", "", {HistType::kTH2D, {axisPhiPlane, axisVz}}); - // histos.add("QA/after/DeltaPsivsPx", "", {HistType::kTH3D, {axisCent, axisPhiPlane, axisPx}}); } - if (cfgFillQABefore) { + if (cfg.cFillQABefore) { histos.addClone("QA/after/", "QA/before/"); } } if (doprocessData || doprocessMCReco || doprocessDataPID) { - if (cfgFillTrackQA) { + if (cfg.cFillTrackQA) { histos.add("incl/QA/after/pt_phi", "", {HistType::kTH2D, {axisPt, axisPhiMod}}); histos.add("incl/QA/after/hPhi_Eta_vz", "", kTH3D, {axisPhi, axisEta, axisVz}); histos.add("incl/QA/after/hPhi_Eta_vz_corrected", "", kTH3D, {axisPhi, axisEta, axisVz}); @@ -465,7 +492,7 @@ struct FlowSP { histos.add("incl/QA/after/hCrossedRows_vs_SharedClusters", "", {HistType::kTH2D, {axisCl, axisShCl}}); histos.add("incl/QA/after/hMeanPtEta", "", {HistType::kTProfile2D, {axisEta, axisCent}}); - if (cfgTrackSelDoTrackQAvsCent) { + if (cfg.cTrackSelDoTrackQAvsCent) { histos.add("incl/QA/after/hPt_Eta", "", kTH3D, {axisPt, axisEta, axisCent}); histos.add("incl/QA/after/hPt_Eta_uncorrected", "", kTH3D, {axisPt, axisEta, axisCent}); histos.add("incl/QA/after/hPhi_Eta", "", kTH3D, {axisPhi, axisEta, axisCent}); @@ -475,11 +502,12 @@ struct FlowSP { histos.add("incl/QA/after/hPhi_Eta_Pt_corrected", "", kTH3D, {axisPhi, axisEta, axisPt}); } - if (cfgFillQABefore) + if (cfg.cFillQABefore) { histos.addClone("incl/QA/after/", "incl/QA/before/"); + } } - if (cfgFillPIDQA && doprocessDataPID) { + if (cfg.cFillPIDQA && doprocessDataPID) { histos.add("hPIDcounts", "", kTH2D, {{{4, 0, 4}, axisPt}}); histos.get(HIST("hPIDcounts"))->GetXaxis()->SetBinLabel(1, "UFO"); histos.get(HIST("hPIDcounts"))->GetXaxis()->SetBinLabel(2, "Pion"); @@ -494,7 +522,7 @@ struct FlowSP { histos.add("incl/pion/QA/after/hNsigmaTPC_pt", "", {HistType::kTH2D, {axisPt, axisNsigma}}); histos.add("incl/pion/QA/after/hNsigmaTOF_pt", "", {HistType::kTH2D, {axisPt, axisNsigma}}); - if (cfgTrackSelDoTrackQAvsCent) { + if (cfg.cTrackSelDoTrackQAvsCent) { histos.add("incl/pion/QA/after/hPt_Eta", "", kTH3D, {axisPt, axisEta, axisCent}); histos.add("incl/pion/QA/after/hPt_Eta_uncorrected", "", kTH3D, {axisPt, axisEta, axisCent}); histos.add("incl/pion/QA/after/hPhi_Eta", "", kTH3D, {axisPhi, axisEta, axisCent}); @@ -511,7 +539,7 @@ struct FlowSP { histos.add("incl/pion/QA/after/hCrossedRows_pt", "", {HistType::kTH2D, {axisPt, axisCl}}); histos.add("incl/pion/QA/after/hCrossedRows_vs_SharedClusters", "", {HistType::kTH2D, {axisCl, axisShCl}}); - if (cfgFillQABefore) { + if (cfg.cFillQABefore) { histos.addClone("incl/pion/QA/after/", "incl/pion/QA/before/"); } @@ -519,16 +547,29 @@ struct FlowSP { histos.addClone("incl/pion/", "incl/proton/"); } - if (cfgFillEventQA) { + if (cfg.cFillEventQA) { histos.add("QA/hCentFull", " ; Centrality (%); ", {HistType::kTH1D, {axisCent}}); } if (doprocessMCReco) { - registry.add("trackMCReco/after/hIsPhysicalPrimary", "", {HistType::kTH2D, {{2, 0, 2}, axisCentrality}}); - registry.add("trackMCReco/hTrackSize_unFiltered", "", {HistType::kTH2D, {{100, 0, 200000}, axisCentrality}}); - registry.add("trackMCReco/hTrackSize_Filtered", "", {HistType::kTH2D, {{100, 0, 20000}, axisCentrality}}); - registry.get(HIST("trackMCReco/after/hIsPhysicalPrimary"))->GetXaxis()->SetBinLabel(1, "Secondary"); - registry.get(HIST("trackMCReco/after/hIsPhysicalPrimary"))->GetXaxis()->SetBinLabel(2, "Primary"); + registry.add("trackMCReco/after/incl/hIsPhysicalPrimary", "", {HistType::kTH3D, {axisCentrality, axisEta, axisPt}}); + registry.add("trackMCReco/after/incl/hIsNotPhysicalPrimary", "", {HistType::kTH3D, {axisCentrality, axisEta, axisPt}}); + registry.add("trackMCReco/incl/hPtMCPtTrack", "", {HistType::kTH2D, {axisPt, axisPt}}); + registry.add("trackMCReco/incl/hEtaMCEtaTrack", "", {HistType::kTH2D, {axisEta, axisEta}}); + registry.add("trackMCReco/incl/hPtPerTrackSelection", "", {HistType::kTH2D, {axisPt, {nTrackSelections, 0, nTrackSelections}}}); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_Eta + 1, "Eta"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_Pt + 1, "Pt"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_DCAxy + 1, "DCAxy"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_DCAz + 1, "DCAz"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_GlobalTracks + 1, "GlobalTracks"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_NCls + 1, "nClusters TPC"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_FshCls + 1, "Frac. sh. Cls TPC"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_TPCBoundary + 1, "TPC Boundary"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_ZeroCharge + 1, "Only charged"); + registry.get(HIST("trackMCReco/incl/hPtPerTrackSelection"))->GetYaxis()->SetBinLabel(trackSel_ParticleWeights + 1, "Apply weights"); + + registry.add("trackMCReco/hTrackSize_unFiltered", "", {HistType::kTH2D, {{100, 0, 4000}, axisCentrality}}); + registry.add("trackMCReco/hTrackSize_Filtered", "", {HistType::kTH2D, {{100, 0, 4000}, axisCentrality}}); registry.add("trackMCReco/after/incl/hPt_hadron", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCReco/after/incl/hPt_proton", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCReco/after/incl/hPt_pion", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); @@ -536,6 +577,8 @@ struct FlowSP { // Clone into particles and before/after registry.addClone("trackMCReco/after/incl/", "trackMCReco/after/pos/"); registry.addClone("trackMCReco/after/incl/", "trackMCReco/after/neg/"); + registry.addClone("trackMCReco/incl/", "trackMCReco/neg/"); + registry.addClone("trackMCReco/incl/", "trackMCReco/pos/"); registry.addClone("trackMCReco/after/", "trackMCReco/before/"); } @@ -546,8 +589,10 @@ struct FlowSP { registry.add("QQCorrelations/qAXqCY", "", kTProfile, {axisCent}); registry.add("QQCorrelations/qAYqCX", "", kTProfile, {axisCent}); registry.add("QQCorrelations/qAXYqCXY", "", kTProfile, {axisCent}); + registry.add("shift/ShiftZDCC", "ShiftZDCC", kTProfile3D, {{100, 0, 100}, {2, 0, 2}, {10, 0, 10}}); + registry.add("shift/ShiftZDCA", "ShiftZDCA", kTProfile3D, {{100, 0, 100}, {2, 0, 2}, {10, 0, 10}}); - if (cfgFillGeneralV1Histos) { + if (cfg.cFillGeneralV1Histos) { // track properties per centrality and per eta, pt bin registry.add("incl/vnCodd", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnAodd", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); @@ -555,50 +600,53 @@ struct FlowSP { registry.add("incl/vnA", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnCSetPlane", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnASetPlane", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); + registry.add("incl/vnOdd", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); + registry.add("incl/vnEven", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); } - if (cfgFillMeanPT) { - registry.add("incl/meanPT/meanRelPtA", "", kTProfile2D, {axisEtaVn, axisCentrality}); - registry.add("incl/meanPT/meanRelPtC", "", kTProfile2D, {axisEtaVn, axisCentrality}); - - registry.add("incl/meanPT/hMeanPtEtaCent", "", kTProfile2D, {axisEtaVn, axisCent}); - registry.add("incl/meanPT/ptV1A", "", kTProfile2D, {axisEtaVn, axisCent}); - registry.add("incl/meanPT/ptV1C", "", kTProfile2D, {axisEtaVn, axisCent}); - registry.add("incl/meanPT/ptV1Aodd", "", kTProfile2D, {axisEtaVn, axisCent}); - registry.add("incl/meanPT/ptV1Codd", "", kTProfile2D, {axisEtaVn, axisCent}); - registry.add("incl/meanPT/hMeanPtCent", "", kTProfile, {axisCent}); - + if (cfg.cFillMeanPT) { registry.add("incl/meanPT/ptV1A3D", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); registry.add("incl/meanPT/ptV1C3D", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); - registry.add("incl/meanPT/ptV1A3Dx", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); - registry.add("incl/meanPT/ptV1C3Dx", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); + if (cfg.cFillMeanPTextra) { + registry.add("incl/meanPT/meanRelPtA", "", kTProfile2D, {axisEtaVn, axisCentrality}); + registry.add("incl/meanPT/meanRelPtC", "", kTProfile2D, {axisEtaVn, axisCentrality}); - registry.add("incl/meanPT/ptV1A3Dy", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); - registry.add("incl/meanPT/ptV1C3Dy", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); + registry.add("incl/meanPT/hMeanPtEtaCent", "", kTProfile2D, {axisEtaVn, axisCent}); + registry.add("incl/meanPT/ptV1A", "", kTProfile2D, {axisEtaVn, axisCent}); + registry.add("incl/meanPT/ptV1C", "", kTProfile2D, {axisEtaVn, axisCent}); + registry.add("incl/meanPT/ptV1Aodd", "", kTProfile2D, {axisEtaVn, axisCent}); + registry.add("incl/meanPT/ptV1Codd", "", kTProfile2D, {axisEtaVn, axisCent}); + registry.add("incl/meanPT/hMeanPtCent", "", kTProfile, {axisCent}); - registry.add("incl/meanPT/meanPxA", "", kTH3D, {axisCent, axisPhiPlane, axisPx}); - registry.add("incl/meanPT/meanPxC", "", kTH3D, {axisCent, axisPhiPlane, axisPx}); + registry.add("incl/meanPT/ptV1A3Dx", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); + registry.add("incl/meanPT/ptV1C3Dx", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); + + registry.add("incl/meanPT/ptV1A3Dy", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); + registry.add("incl/meanPT/ptV1C3Dy", "", kTProfile3D, {axisPt, axisEtaVn, axisCent}); + + registry.add("incl/meanPT/meanPxA", "", kTH3D, {axisCent, axisPhiPlane, axisPx}); + registry.add("incl/meanPT/meanPxC", "", kTH3D, {axisCent, axisPhiPlane, axisPx}); + } } - if (cfgFillXandYterms) { + if (cfg.cFillXandYterms) { registry.add("incl/vnAx", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnAy", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnCx", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnCy", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); } - if (cfgFillMixedHarmonics) { + if (cfg.cFillMixedHarmonics) { registry.add("incl/MH/vnAxCxUx_MH", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/MH/vnAyCyUx_MH", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/MH/vnAxCyUy_MH", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/MH/vnAyCxUy_MH", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); } - if (cfgFillEventPlane) { + if (cfg.cFillEventPlane) { registry.add("incl/vnA_EP", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnC_EP", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/vnFull_EP", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); } - if (cfgFillEventPlaneQA) { - histos.add("QA/hSPplaneA", "hSPplaneA", kTH1D, {axisPhiPlane}); - histos.add("QA/hSPplaneC", "hSPplaneC", kTH1D, {axisPhiPlane}); + if (cfg.cFillEventPlaneQA) { + histos.add("QA/hSPplaneAC", "hSPplaneAC", kTH2D, {axisPhiPlane, axisPhiPlane}); histos.add("QA/hSPplaneFull", "hSPplaneFull", kTH1D, {axisPhiPlane}); histos.add("QA/hCosPhiACosPhiC", "hCosPhiACosPhiC; Centrality(%); #LT Cos(#Psi^{A})Cos(#Psi^{C})#GT", kTProfile, {axisCent}); histos.add("QA/hSinPhiASinPhiC", "hSinPhiASinPhiC; Centrality(%); #LT Sin(#Psi^{A})Sin(#Psi^{C})#GT", kTProfile, {axisCent}); @@ -609,7 +657,7 @@ struct FlowSP { } // end of doProcessData - if (cfgFillChargeDependence || cfgFillChargeDependenceQA) { + if (cfg.cFillChargeDependence || cfg.cFillChargeDependenceQA) { LOGF(info, "Cloning charge dependence histograms"); registry.addClone("incl/", "pos/"); registry.addClone("incl/", "neg/"); @@ -619,7 +667,7 @@ struct FlowSP { } if (doprocessDataPID) { - if (cfgFillGeneralV1Histos) { + if (cfg.cFillGeneralV1Histos) { registry.add("incl/pion/vnCodd", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/vnAodd", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/vnC", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); @@ -628,18 +676,18 @@ struct FlowSP { registry.add("incl/vnASetPlane", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); } - if (cfgFillEventPlane) { + if (cfg.cFillEventPlane) { registry.add("incl/pion/vnA_EP", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/vnC_EP", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/vnFull_EP", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); } - if (cfgFillXandYterms) { + if (cfg.cFillXandYterms) { registry.add("incl/pion/vnAx", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/vnAy", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/vnCx", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/vnCy", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); } - if (cfgFillMixedHarmonics) { + if (cfg.cFillMixedHarmonics) { registry.add("incl/pion/MH/vnAxCxUx_MH", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/MH/vnAyCyUx_MH", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); registry.add("incl/pion/MH/vnAxCyUy_MH", "", kTProfile3D, {axisPt, axisEtaVn, axisCentrality}); @@ -647,40 +695,79 @@ struct FlowSP { } registry.addClone("incl/pion/", "incl/proton/"); registry.addClone("incl/pion/", "incl/kaon/"); - if (cfgFillChargeDependence) { + if (cfg.cFillChargeDependence) { registry.addClone("incl/", "pos/"); registry.addClone("incl/", "neg/"); } } - - } else if (doprocessMCGen) { + } + if (doprocessMCGen || doprocessMCReco) { registry.add("trackMCGen/nCollReconstructedPerMcCollision", "", {HistType::kTH1D, {{10, -5, 5}}}); registry.add("trackMCGen/after/incl/hPt_hadron", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_proton", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_pion", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/hPt_kaon", "", {HistType::kTH3D, {axisPt, axisEta, axisCentrality}}); registry.add("trackMCGen/after/incl/phi_eta_vtxZ_gen", "", {HistType::kTH3D, {axisPhi, axisEta, axisVz}}); + registry.addClone("trackMCGen/after/incl/", "trackMCGen/after/pos/"); registry.addClone("trackMCGen/after/incl/", "trackMCGen/after/neg/"); registry.addClone("trackMCGen/after/", "trackMCGen/before/"); } - if (cfgEvSelsUseAdditionalEventCut) { - fMultPVCutLow = new TF1("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - fMultPVCutHigh = new TF1("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - fMultCutLow = new TF1("fMultCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - fMultCutHigh = new TF1("fMultCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - - std::vector paramsMultPVCut = cfgEvSelsMultPv; - std::vector paramsMultCut = cfgEvSelsMult; + if (cfg.cEvSelsUseAdditionalEventCut) { + fMultPVCutLow = std::make_unique("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + fMultPVCutHigh = std::make_unique("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + fMultCutLow = std::make_unique("fMultCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + fMultCutHigh = std::make_unique("fMultCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + + std::vector paramsMultPVCut; + std::vector paramsMultCut; + int y2023 = 2023; + int y2024 = 2024; + std::array nSigma = {1, 2, 3}; + + if (cfg.cUsePredeFinedSigma) { + if (cfg.cUsePredeFinedSigmaYear == y2023) { + if (cfg.cUsePredeFinedSigmaNsigma == nSigma[0]) { + paramsMultPVCut = {2615.47, -90.5747, 1.25125, -0.00847075, 2.41183e-05, 3399.72, -121.652, 1.84077, -0.0142886, 4.71449e-05}; + paramsMultCut = {1716.84, -56.5663, 0.715202, -0.00426007, 1.05075e-05, 2550.82, -87.4873, 1.22205, -0.00852644, 2.54248e-05}; + } else if (cfg.cUsePredeFinedSigmaNsigma == nSigma[1]) { + paramsMultPVCut = {2223.49, -75.1444, 0.963572, -0.00570399, 1.34877e-05, 3790.99, -137.064, 2.13044, -0.017122, 5.82834e-05}; + paramsMultCut = {1301.56, -41.4615, 0.478224, -0.00239449, 4.46966e-06, 2967.6, -102.927, 1.47488, -0.0106534, 3.28622e-05}; + } else if (cfg.cUsePredeFinedSigmaNsigma == nSigma[2]) { + paramsMultPVCut = {1837.75, -60.852, 0.724331, -0.00366975, 6.47562e-06, 4182.12, -152.459, 2.41955, -0.0199481, 6.93894e-05}; + paramsMultCut = {885.976, -26.3397, 0.240114, -0.000496168, -1.82704e-06, 3384.43, -118.377, 1.72823, -0.0127887, 4.03432e-05}; + } else { + LOGF(fatal, "nSigma can only be 1-3 please reset the variable or give the parameters manually and set cfg.cUsePredeFinedSigma to FALSE"); + } + } else if (cfg.cUsePredeFinedSigmaYear == y2024) { + if (cfg.cUsePredeFinedSigmaNsigma == nSigma[0]) { + paramsMultPVCut = {2726.93, -100.128, 1.45046, -0.0099354, 2.71182e-05, 3404.72, -126.569, 1.92500, -0.0142653, 4.31645e-05}; + paramsMultCut = {1858.77, -66.6070, 0.929146, -0.00606961, 1.57639e-05, 2672.43, -96.7708, 1.39109, -0.00942498, 2.54268e-05}; + } else if (cfg.cUsePredeFinedSigmaNsigma == nSigma[1]) { + paramsMultPVCut = {2390.04, -87.3154, 1.23176, -0.00806869, 2.06624e-05, 3744.26, -139.927, 2.16863, -0.0165329, 5.17269e-05}; + paramsMultCut = {1451.23, -51.4314, 0.694609, -0.00433959, 1.06698e-05, 3080.42, -112.071, 1.63166, -0.0112533, 3.10348e-05}; + } else if (cfg.cUsePredeFinedSigmaNsigma == nSigma[2]) { + paramsMultPVCut = {2053.64, -74.5950, 1.01563, -0.00621473, 1.41276e-05, 4083.79, -153.304, 2.41333, -0.0188198, 6.03974e-05}; + paramsMultCut = {1042.50, -35.9374, 0.440681, -0.00222218, 3.20643e-06, 3488.53, -127.396, 1.87339, -0.0131007, 3.67434e-05}; + } else { + LOGF(fatal, "cUsePredeFinedSigmaNsigma can only be 1-3 please reset the variable or give the parameters manually and set cUsePredeFinedSigma to FALSE"); + } + } else { + LOGF(fatal, "cUsePredeFinedSigmaYear can only be 2023/2024 please reset the variable or give the parameters manually and set cUsePredeFinedSigma to FALSE"); + } + } else { + paramsMultPVCut = cfg.cEvSelsMultPv; + paramsMultCut = cfg.cEvSelsMult; + } - // number of parameters required in cfgEvSelsMultPv and cfgEvSelsMult. (5 Low + 5 High) + // number of parameters required in cfg.cEvSelsMultPv and cfg.cEvSelsMult. (5 Low + 5 High) uint64_t nParams = 10; if (paramsMultPVCut.size() < nParams) { - LOGF(fatal, "cfgEvSelsMultPv not set properly.. size = %d (should be 10) --> Check your config files!", paramsMultPVCut.size()); + LOGF(fatal, "cfg.cEvSelsMultPv not set properly.. size = %d (should be 10) --> Check your config files!", paramsMultPVCut.size()); } else if (paramsMultCut.size() < nParams) { - LOGF(fatal, "cfgEvSelsMult not set properly.. size = %d (should be 10) --> Check your config files!", paramsMultCut.size()); + LOGF(fatal, "cfg.cEvSelsMult not set properly.. size = %d (should be 10) --> Check your config files!", paramsMultCut.size()); } else { fMultPVCutLow->SetParameters(paramsMultPVCut[0], paramsMultPVCut[1], paramsMultPVCut[2], paramsMultPVCut[3], paramsMultPVCut[4]); fMultPVCutHigh->SetParameters(paramsMultPVCut[5], paramsMultPVCut[6], paramsMultPVCut[7], paramsMultPVCut[8], paramsMultPVCut[9]); @@ -689,9 +776,9 @@ struct FlowSP { } } - if (cfgTrackSelsUseAdditionalTrackCut) { - fPhiCutLow = new TF1("fPhiCutLow", "0.06/x+pi/18.0-0.06", 0, 100); - fPhiCutHigh = new TF1("fPhiCutHigh", "0.1/x+pi/18.0+0.06", 0, 100); + if (cfg.cTrackSelsUseAdditionalTrackCut) { + fPhiCutLow = std::make_unique("fPhiCutLow", "0.06/x+pi/18.0-0.06", 0, 100); + fPhiCutHigh = std::make_unique("fPhiCutHigh", "0.1/x+pi/18.0+0.06", 0, 100); } } // end of init @@ -701,13 +788,14 @@ struct FlowSP { int etaind = hNUA->GetYaxis()->FindBin(eta); int vzind = hNUA->GetZaxis()->FindBin(vtxz); float weight = hNUA->GetBinContent(xind, etaind, vzind); - if (weight != 0) + if (weight != 0) { return 1. / weight; + } return 1; } template - ParticleType getTrackPID(TrackObject track) + ParticleType getTrackPID(const TrackObject& track) { float usedNSigmaPi = -1; @@ -736,19 +824,17 @@ struct FlowSP { ParticleType valPID = kUnidentified; for (const auto& nsigma : usedNSigma) { - if (std::abs(nsigma.first) < cfgTrackSelsPIDNsigma) { + if (std::abs(nsigma.first) < cfg.cTrackSelsPIDNsigma) { valPID = nsigma.second; nIdentified++; } } - if (nIdentified == 0) { - return kUnidentified; // No PID match found - } else if (nIdentified == 1) { + if (nIdentified == 1) { return valPID; - } else { - return kUnidentified; // Multiple PID matches found } + + return kUnidentified; // Multiple PID matches found } int getMagneticField(uint64_t timestamp) @@ -783,88 +869,111 @@ struct FlowSP { void loadCorrections(uint64_t timestamp) { // corrections saved on CCDB as TList {incl, pos, neg} of GFWWeights (acc) TH1D (eff) objects! - if (cfg.correctionsLoaded) + if (conf.correctionsLoaded) { return; + } int nWeights = 3; - if (cfgUseNUA1D) { - if (cfgCCDB_NUA.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfgCCDB_NUA, timestamp); - cfg.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights"))); - cfg.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights_positive"))); - cfg.mAcceptance.push_back(reinterpret_cast(listCorrections->FindObject("weights_negative"))); - int sizeAcc = cfg.mAcceptance.size(); - if (sizeAcc < nWeights) - LOGF(fatal, "Could not load acceptance weights from %s", cfgCCDB_NUA.value.c_str()); - else - LOGF(info, "Loaded acceptance weights from %s", cfgCCDB_NUA.value.c_str()); + if (cfg.cUseNUA1D) { + if (!cfg.cCCDB_NUA.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights"))); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights_positive"))); + conf.mAcceptance.push_back(dynamic_cast(listCorrections->FindObject("weights_negative"))); + int sizeAcc = conf.mAcceptance.size(); + if (sizeAcc < nWeights) { + LOGF(fatal, "Could not load acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); + } else { + LOGF(info, "Loaded acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); + } } else { - LOGF(info, "cfgCCDB_NUA empty! No corrections loaded"); + LOGF(info, "cfg.cCCDB_NUA empty! No corrections loaded"); } - } else if (cfgUseNUA2D) { - if (cfgCCDB_NUA.value.empty() == false) { - TH3D* hNUA2D = ccdb->getForTimeStamp(cfgCCDB_NUA, timestamp); + } else if (cfg.cUseNUA2D) { + if (!cfg.cCCDB_NUA.value.empty()) { + auto* hNUA2D = ccdb->getForTimeStamp(cfg.cCCDB_NUA, timestamp); if (!hNUA2D) { - LOGF(fatal, "Could not load acceptance weights from %s", cfgCCDB_NUA.value.c_str()); + LOGF(fatal, "Could not load acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); } else { - LOGF(info, "Loaded acceptance weights from %s", cfgCCDB_NUA.value.c_str()); - cfg.mAcceptance2D.push_back(hNUA2D); + LOGF(info, "Loaded acceptance weights from %s", cfg.cCCDB_NUA.value.c_str()); + conf.mAcceptance2D.push_back(hNUA2D); } } else { - LOGF(info, "cfgCCDB_NUA empty! No corrections loaded"); + LOGF(info, "cfg.cCCDB_NUA empty! No corrections loaded"); } } // Get Efficiency correction - if (cfgCCDB_NUE.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfgCCDB_NUE, timestamp); - cfg.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - cfg.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - cfg.mEfficiency.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); - int sizeEff = cfg.mEfficiency.size(); - if (sizeEff < nWeights) - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCCDB_NUE.value.c_str()); - else - LOGF(info, "Loaded efficiency histogram from %s", cfgCCDB_NUE.value.c_str()); + if (!cfg.cCCDB_NUE.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE, timestamp); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); + int sizeEff = conf.mEfficiency.size(); + if (sizeEff < nWeights) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); + } else { + LOGF(info, "Loaded efficiency histogram from %s", cfg.cCCDB_NUE.value.c_str()); + } } else { - LOGF(info, "cfgCCDB_NUE empty! No corrections loaded"); + LOGF(info, "cfg.cCCDB_NUE empty! No corrections loaded"); } // Get Efficiency correction - if (cfgCCDB_NUE2D.value.empty() == false) { - TList* listCorrections = ccdb->getForTimeStamp(cfgCCDB_NUE2D, timestamp); - cfg.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency"))); - cfg.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_pos"))); - cfg.mEfficiency2D.push_back(reinterpret_cast(listCorrections->FindObject("Efficiency_neg"))); - int sizeEff = cfg.mEfficiency2D.size(); + if (!cfg.cCCDB_NUE2D.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE2D, timestamp); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency2D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); + int sizeEff = conf.mEfficiency2D.size(); + if (sizeEff < nWeights) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); + } else { + LOGF(info, "Loaded efficiency histogram from %s", cfg.cCCDB_NUE.value.c_str()); + } + } else { + LOGF(info, "cfg.cCCDB_NUE2 empty! No corrections loaded"); + } + + if (!cfg.cCCDB_NUE3D.value.empty()) { + auto* listCorrections = ccdb->getForTimeStamp(cfg.cCCDB_NUE3D, timestamp); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency"))); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_pos"))); + conf.mEfficiency3D.push_back(dynamic_cast(listCorrections->FindObject("Efficiency_neg"))); + int sizeEff = conf.mEfficiency3D.size(); if (sizeEff < nWeights) - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCCDB_NUE.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfg.cCCDB_NUE.value.c_str()); else - LOGF(info, "Loaded efficiency histogram from %s", cfgCCDB_NUE.value.c_str()); + LOGF(info, "Loaded efficiency histogram from %s", cfg.cCCDB_NUE.value.c_str()); } else { - LOGF(info, "cfgCCDB_NUE2 empty! No corrections loaded"); + LOGF(info, "cfg.cCCDB_NUE2 empty! No corrections loaded"); } - cfg.correctionsLoaded = true; + conf.correctionsLoaded = true; } // From Generic Framework bool setCurrentParticleWeights(int pID, int spec, const float& phi, const float& eta, const float& pt, const float& vtxz, const float& centrality) { float eff = 1.; - int sizeEff = cfg.mEfficiency.size(); + int sizeEff = conf.mEfficiency.size(); if (sizeEff > pID) { - if (cfgUseNUE2D) { + if (cfg.cUseNUE3D) { + int binx = conf.mEfficiency3D[pID]->GetXaxis()->FindBin(pt); + int biny = conf.mEfficiency3D[pID]->GetYaxis()->FindBin(eta); + int binz = conf.mEfficiency3D[pID]->GetZaxis()->FindBin(centrality); + eff = conf.mEfficiency3D[pID]->GetBinContent(binx, biny, binz); + } else if (cfg.cUseNUE2D) { int binx; int biny; - if (cfgUseNUE2Deta) { - biny = cfg.mEfficiency2D[pID]->GetYaxis()->FindBin(pt); - binx = cfg.mEfficiency2D[pID]->GetXaxis()->FindBin(eta); + if (cfg.cUseNUE2Deta) { + biny = conf.mEfficiency2D[pID]->GetYaxis()->FindBin(pt); + binx = conf.mEfficiency2D[pID]->GetXaxis()->FindBin(eta); } else { - binx = cfg.mEfficiency2D[pID]->GetXaxis()->FindBin(pt); - biny = cfg.mEfficiency2D[pID]->GetYaxis()->FindBin(centrality); + binx = conf.mEfficiency2D[pID]->GetXaxis()->FindBin(pt); + biny = conf.mEfficiency2D[pID]->GetYaxis()->FindBin(centrality); } - eff = cfg.mEfficiency2D[pID]->GetBinContent(binx, biny); + eff = conf.mEfficiency2D[pID]->GetBinContent(binx, biny); } else { - eff = cfg.mEfficiency[pID]->GetBinContent(cfg.mEfficiency[pID]->FindBin(pt)); + eff = conf.mEfficiency[pID]->GetBinContent(conf.mEfficiency[pID]->FindBin(pt)); } } else { eff = 1.0; @@ -874,16 +983,16 @@ struct FlowSP { spm.weff[pID][spec] = 1. / eff; - if (cfgUseNUA1D) { - int sizeAcc = cfg.mAcceptance.size(); + if (cfg.cUseNUA1D) { + int sizeAcc = conf.mAcceptance.size(); if (sizeAcc > pID) { - spm.wacc[pID][spec] = cfg.mAcceptance[pID]->getNUA(phi, eta, vtxz); + spm.wacc[pID][spec] = conf.mAcceptance[pID]->getNUA(phi, eta, vtxz); } else { spm.wacc[pID][spec] = 1; } - } else if (cfgUseNUA2D) { - if (cfg.mAcceptance2D.size() > 0) { - spm.wacc[pID][spec] = getNUA2D(cfg.mAcceptance2D[0], eta, phi, vtxz); + } else if (cfg.cUseNUA2D) { + if (conf.mAcceptance2D.size() > 0) { + spm.wacc[pID][spec] = getNUA2D(conf.mAcceptance2D[0], eta, phi, vtxz); } else { spm.wacc[pID][spec] = 1; } @@ -891,304 +1000,400 @@ struct FlowSP { return true; } + template + inline void fillPrimaryHistos(const McParticleObject& mcparticle) + { + + if (mcparticle.isPhysicalPrimary()) { + registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsPhysicalPrimary"), spm.centrality, mcparticle.eta(), mcparticle.pt()); + } else { + registry.fill(HIST("trackMCReco/") + HIST(Time[ft]) + HIST(Charge[ct]) + HIST("hIsNotPhysicalPrimary"), spm.centrality, mcparticle.eta(), mcparticle.pt()); + } + } + template - bool eventSelected(TCollision collision, const int& multTrk) + bool eventSelected(const TCollision& collision, const int& multTrk) { - if (!collision.sel8()) + if (!collision.sel8()) { return 0; + } histos.fill(HIST("hEventCount"), evSel_sel8); - if (rctFlags.cfgEvtUseRCTFlagChecker && !rctChecker(collision)) + if (cfg.cEvtUseRCTFlagChecker && !rctChecker(collision)) { return 0; + } histos.fill(HIST("hEventCount"), evSel_RCTFlagsZDC); // Occupancy - if (cfgEvSelsDoOccupancySel) { + if (cfg.cEvSelsDoOccupancySel) { auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy > cfgEvSelsMaxOccupancy || occupancy < cfgEvSelsMinOccupancy) { + if (occupancy > cfg.cEvSelsMaxOccupancy || occupancy < cfg.cEvSelsMinOccupancy) { return 0; } - histos.fill(HIST("hEventCount"), evSel_occupancy); } + histos.fill(HIST("hEventCount"), evSel_occupancy); - if (cfgEvSelsNoSameBunchPileupCut) { + if (cfg.cEvSelsNoSameBunchPileupCut) { if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); } - if (cfgEvSelsIsGoodZvtxFT0vsPV) { + histos.fill(HIST("hEventCount"), evSel_kNoSameBunchPileup); + if (cfg.cEvSelsIsGoodZvtxFT0vsPV) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); } - if (cfgEvSelsNoCollInTimeRangeStandard) { + histos.fill(HIST("hEventCount"), evSel_kIsGoodZvtxFT0vsPV); + if (cfg.cEvSelsNoCollInTimeRangeStandard) { if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // Rejection of the collisions which have other events nearby return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); } - if (cfgEvSelsNoCollInTimeRangeNarrow) { + histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeStandard); + if (cfg.cEvSelsNoCollInTimeRangeNarrow) { if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { // Rejection of the collisions which have other events nearby return 0; } - histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeNarrow); } - if (cfgEvSelsIsVertexITSTPC) { + histos.fill(HIST("hEventCount"), evSel_kNoCollInTimeRangeNarrow); + if (cfg.cEvSelsIsVertexITSTPC) { if (!collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { // selects collisions with at least one ITS-TPC track, and thus rejects vertices built from ITS-only tracks return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsVertexITSTPC); } + histos.fill(HIST("hEventCount"), evSel_kIsVertexITSTPC); - if (cfgEvSelsIsGoodITSLayersAll) { + if (cfg.cEvSelsIsGoodITSLayersAll) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // New event selection bits to cut time intervals with dead ITS staves // https://indico.cern.ch/event/1493023/ (09-01-2025) return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); } - if (cfgEvSelsIsGoodITSLayer0123) { + histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayersAll); + if (cfg.cEvSelsIsGoodITSLayer0123) { if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { return 0; } - histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayer0123); } + histos.fill(HIST("hEventCount"), evSel_kIsGoodITSLayer0123); - if (cfgEvSelsUseAdditionalEventCut) { + if (cfg.cEvSelsUseAdditionalEventCut) { float vtxz = -999; if (collision.numContrib() > 1) { vtxz = collision.posZ(); float zRes = std::sqrt(collision.covZZ()); float minzRes = 0.25; int maxNumContrib = 20; - if (zRes > minzRes && collision.numContrib() < maxNumContrib) + if (zRes > minzRes && collision.numContrib() < maxNumContrib) { vtxz = -999; + } } auto multNTracksPV = collision.multNTracksPV(); - if (vtxz > cfgEvSelsVtxZ || vtxz < -cfgEvSelsVtxZ) + if (vtxz > cfg.cEvSelsVtxZ || vtxz < -cfg.cEvSelsVtxZ) { return 0; - if (multNTracksPV < fMultPVCutLow->Eval(collision.centFT0C())) + } + if (multNTracksPV < fMultPVCutLow->Eval(collision.centFT0C())) { return 0; - if (multNTracksPV > fMultPVCutHigh->Eval(collision.centFT0C())) + } + if (multNTracksPV > fMultPVCutHigh->Eval(collision.centFT0C())) { return 0; - if (multTrk < fMultCutLow->Eval(collision.centFT0C())) + } + if (multTrk < fMultCutLow->Eval(collision.centFT0C())) { return 0; - if (multTrk > fMultCutHigh->Eval(collision.centFT0C())) + } + if (multTrk > fMultCutHigh->Eval(collision.centFT0C())) { return 0; - - histos.fill(HIST("hEventCount"), evSel_MultCuts); + } } + histos.fill(HIST("hEventCount"), evSel_MultCuts); return 1; } template - bool trackSelected(TrackObject track, const int& field) + bool trackSelected(const TrackObject& track, const int& field) { - if (std::fabs(track.eta()) > cfgTrackSelsEta) + auto fillSpectraStudyMCReco = [&](TrackSelections sel) { + if constexpr (o2::framework::has_type_v) { + if (!track.has_mcParticle()) { + return; + } + const auto mcParticle = track.template mcParticle_as(); + if (doprocessMCReco && mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("trackMCReco/incl/hPtPerTrackSelection"), mcParticle.pt(), sel); + if (track.sign() > 0) { + registry.fill(HIST("trackMCReco/pos/hPtPerTrackSelection"), mcParticle.pt(), sel); + } else if (track.sign() < 0) { + registry.fill(HIST("trackMCReco/neg/hPtPerTrackSelection"), mcParticle.pt(), sel); + } + } + } + }; + + if (std::fabs(track.eta()) > cfg.cTrackSelsEta) { return false; + } histos.fill(HIST("hTrackCount"), trackSel_Eta); + fillSpectraStudyMCReco(trackSel_Eta); - if (track.pt() < cfgTrackSelsPtmin || track.pt() > cfgTrackSelsPtmax) + if (track.pt() < cfg.cTrackSelsPtmin || track.pt() > cfg.cTrackSelsPtmax) { return false; - + } histos.fill(HIST("hTrackCount"), trackSel_Pt); + fillSpectraStudyMCReco(trackSel_Pt); - if (track.dcaXY() > cfgTrackSelsDCAxy) - return false; - - histos.fill(HIST("hTrackCount"), trackSel_DCAxy); - - if (track.dcaZ() > cfgTrackSelsDCAz) - return false; - - if (cfgTrackSelsDoDCApt && std::fabs(track.dcaZ()) > (cfgTrackSelsDCAptConsMin + (cfgTrackSelsDCApt1 * cfgTrackSelsDCApt2) / (std::pow(track.pt(), 1.1)))) - return false; - - histos.fill(HIST("hTrackCount"), trackSel_DCAz); - - if (track.tpcNClsCrossedRows() < cfgTrackSelsNcls) + if (track.tpcNClsCrossedRows() < cfg.cTrackSelsNcls) { return false; + } histos.fill(HIST("hTrackCount"), trackSel_NCls); - - if (track.tpcFractionSharedCls() > cfgTrackSelsFshcls) - return false; - histos.fill(HIST("hTrackCount"), trackSel_FshCls); + fillSpectraStudyMCReco(trackSel_NCls); double phimodn = track.phi(); - if (field < 0) // for negative polarity field + if (field < 0) { phimodn = o2::constants::math::TwoPI - phimodn; - if (track.sign() < 0) // for negative charge + } + if (track.sign() < 0) { phimodn = o2::constants::math::TwoPI - phimodn; - if (phimodn < 0) + } + if (phimodn < 0) { LOGF(warning, "phi < 0: %g", phimodn); + } phimodn += o2::constants::math::PI / 18.0; // to center gap in the middle phimodn = fmod(phimodn, o2::constants::math::PI / 9.0); - if (cfgFillTrackQA && cfgFillQABefore) + if (cfg.cFillTrackQA && cfg.cFillQABefore) { histos.fill(HIST("incl/QA/before/pt_phi"), track.pt(), phimodn); + } - if (cfgTrackSelsUseAdditionalTrackCut) { - if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) + if (cfg.cTrackSelsUseAdditionalTrackCut) { + if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) { return false; // reject track + } } - if (cfgFillTrackQA) + if (cfg.cFillTrackQA) { histos.fill(HIST("incl/QA/after/pt_phi"), track.pt(), phimodn); + } histos.fill(HIST("hTrackCount"), trackSel_TPCBoundary); + fillSpectraStudyMCReco(trackSel_TPCBoundary); + + // Only fill primary/secondary histos for MC data. + if constexpr (o2::framework::has_type_v) { + auto mcParticle = track.template mcParticle_as(); + + fillPrimaryHistos(mcParticle); + if (spm.charge == kPositive) { + fillPrimaryHistos(mcParticle); + } else { + fillPrimaryHistos(mcParticle); + } + + if (std::fabs(track.dcaXY()) > cfg.cTrackSelsDCAxy || std::fabs(track.dcaXY()) > fDCACut->Eval(track.pt())) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAxy); + fillSpectraStudyMCReco(trackSel_DCAxy); + + if (std::fabs(track.dcaZ()) > cfg.cTrackSelsDCAz || (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > fDCACut->Eval(track.pt()))) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAz); + fillSpectraStudyMCReco(trackSel_DCAz); + + fillPrimaryHistos(mcParticle); + if (spm.charge == kPositive) { + fillPrimaryHistos(mcParticle); + } else { + fillPrimaryHistos(mcParticle); + } + } else { // Only apply DCA-cuts for data + if (std::fabs(track.dcaXY()) > cfg.cTrackSelsDCAxy || std::fabs(track.dcaXY()) > fDCACut->Eval(track.pt())) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAxy); + + if (std::fabs(track.dcaZ()) > cfg.cTrackSelsDCAz || (cfg.cTrackSelsDoDCApt && std::fabs(track.dcaZ()) > fDCACut->Eval(track.pt()))) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_DCAz); + } + + if (track.tpcFractionSharedCls() > cfg.cTrackSelsFshcls) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_FshCls); + fillSpectraStudyMCReco(trackSel_FshCls); + + if (!track.isGlobalTrack()) { + return false; + } + histos.fill(HIST("hTrackCount"), trackSel_GlobalTracks); + fillSpectraStudyMCReco(trackSel_GlobalTracks); + return true; } template - inline void fillEventQA(CollisionObject collision, TracksObject tracks) + inline void fillEventQA(const CollisionObject& collision, const TracksObject& tracks) { - if (!cfgFillEventQA) + if (!cfg.cFillEventQA) { return; + } - static constexpr std::string_view Time[] = {"before", "after"}; - - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hCentFT0C"), collision.centFT0C(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hCentNGlobal"), collision.centNGlobal(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hCentFT0M"), collision.centFT0M(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/hCentFV0A"), collision.centFV0A(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/globalTracks_centT0C"), collision.centFT0C(), tracks.size(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/globalTracks_multT0A"), collision.multFT0A(), tracks.size(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/globalTracks_multV0A"), collision.multFV0A(), tracks.size(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/multV0A_multT0A"), collision.multFT0A(), collision.multFV0A(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/multT0C_centT0C"), collision.centFT0C(), collision.multFT0C(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/CentFT0C_vs_CentFT0Cvariant1"), collision.centFT0C(), collision.centFT0CVariant1(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/CentFT0C_vs_CentFT0M"), collision.centFT0C(), collision.centFT0M(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/CentFT0C_vs_CentFV0A"), collision.centFT0C(), collision.centFV0A(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/CentFT0C_vs_CentNGlobal"), collision.centFT0C(), collision.centNGlobal(), spm.centWeight); - - if (cfgFillEventPlaneQA) { + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentFT0C"), collision.centFT0C(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentNGlobal"), collision.centNGlobal(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentFT0M"), collision.centFT0M(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("hCentFV0A"), collision.centFV0A(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), tracks.size(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), tracks.size(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), tracks.size(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("CentFT0C_vs_CentFT0Cvariant1"), collision.centFT0C(), collision.centFT0CVariant1(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("CentFT0C_vs_CentFT0M"), collision.centFT0C(), collision.centFT0M(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("CentFT0C_vs_CentFV0A"), collision.centFT0C(), collision.centFV0A(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("CentFT0C_vs_CentNGlobal"), collision.centFT0C(), collision.centNGlobal(), spm.centWeight); + + if (cfg.cFillEventPlaneQA) { if constexpr (o2::framework::has_type_v) { double psiA = 1.0 * std::atan2(collision.qyA(), collision.qxA()); double psiC = 1.0 * std::atan2(collision.qyC(), collision.qxC()); double psiFull = 1.0 * std::atan2(collision.qyA() + collision.qyC(), collision.qxA() + collision.qxC()); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiA_vs_Cent"), psiA, collision.centFT0C(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiC_vs_Cent"), psiC, collision.centFT0C(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiFull_vs_Cent"), psiFull, collision.centFT0C(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiA_vs_Vx"), psiA, collision.vertex()[0], spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiC_vs_Vx"), psiC, collision.vertex()[0], spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiFull_vs_Vx"), psiFull, collision.vertex()[0], spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiA_vs_Vy"), psiA, collision.vertex()[1], spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiC_vs_Vy"), psiC, collision.vertex()[1], spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiFull_vs_Vy"), psiFull, collision.vertex()[1], spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiA_vs_Vz"), psiA, collision.posZ(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiC_vs_Vz"), psiC, collision.posZ(), spm.centWeight); - histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/PsiFull_vs_Vz"), psiFull, collision.posZ(), spm.centWeight); - // histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("/DeltaPsivsPx"), spm.centrality, psiA - psiC - constants::math::PI, track.px(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiA_vs_Cent"), psiA, collision.centFT0C(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiC_vs_Cent"), psiC, collision.centFT0C(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiFull_vs_Cent"), psiFull, collision.centFT0C(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiA_vs_Vx"), psiA, collision.vertex()[0], spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiC_vs_Vx"), psiC, collision.vertex()[0], spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiFull_vs_Vx"), psiFull, collision.vertex()[0], spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiA_vs_Vy"), psiA, collision.vertex()[1], spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiC_vs_Vy"), psiC, collision.vertex()[1], spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiFull_vs_Vy"), psiFull, collision.vertex()[1], spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiA_vs_Vz"), psiA, collision.posZ(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiC_vs_Vz"), psiC, collision.posZ(), spm.centWeight); + histos.fill(HIST("QA/") + HIST(Time[ft]) + HIST("PsiFull_vs_Vz"), psiFull, collision.posZ(), spm.centWeight); } } return; } template - inline void fillHistograms(TrackObject track) + inline void fillHistograms(const TrackObject& track) { double weight = spm.wacc[ct][pt] * spm.weff[ct][pt] * spm.centWeight; - int scale = 1.0; - int minusQ = -1.0; + float scale = 1.0; + float minusQ = -1.0; if (track.eta() < 0) scale = -1.0; - if (cfgFillGeneralV1Histos) { - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnAodd"), track.pt(), track.eta(), spm.centrality, scale * (spm.uy * spm.qyA + spm.ux * spm.qxA) / std::sqrt(std::fabs(spm.corrQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCodd"), track.pt(), track.eta(), spm.centrality, scale * (spm.uy * spm.qyC + spm.ux * spm.qxC) / std::sqrt(std::fabs(spm.corrQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnA"), track.pt(), track.eta(), spm.centrality, (spm.uy * spm.qyA + spm.ux * spm.qxA) / std::sqrt(std::fabs(spm.corrQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnC"), track.pt(), track.eta(), spm.centrality, (spm.uy * spm.qyC + spm.ux * spm.qxC) / std::sqrt(std::fabs(spm.corrQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCSetPlane"), track.pt(), track.eta(), spm.centrality, (spm.uy + spm.ux) / std::sqrt(std::fabs(spm.corrQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnASetPlane"), track.pt(), track.eta(), spm.centrality, (minusQ * spm.ux - spm.uy) / std::sqrt(std::fabs(spm.corrQQ)), weight); + const double invSqrtQQ = 1.0 / std::sqrt(std::fabs(spm.corrQQ)); + const double invSqrtQQx = 1.0 / std::sqrt(std::fabs(spm.corrQQx)); + const double invSqrtQQy = 1.0 / std::sqrt(std::fabs(spm.corrQQy)); + const double uqA = spm.uy * spm.qyA + spm.ux * spm.qxA; + const double uqC = spm.uy * spm.qyC + spm.ux * spm.qxC; + const double invMeanPtQQ = 1.0 / spm.meanPtWeight; + + if (cfg.cFillGeneralV1Histos) { + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnAodd"), track.pt(), track.eta(), spm.centrality, scale * (uqA)*invSqrtQQ, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCodd"), track.pt(), track.eta(), spm.centrality, scale * (uqC)*invSqrtQQ, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnOdd"), track.pt(), track.eta(), spm.centrality, scale * 0.5 * ((uqA) - (uqC)) * invSqrtQQ, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnEven"), track.pt(), track.eta(), spm.centrality, 0.5 * ((uqA) + (uqC)) * invSqrtQQ, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnA"), track.pt(), track.eta(), spm.centrality, (uqA)*invSqrtQQ, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnC"), track.pt(), track.eta(), spm.centrality, (uqC)*invSqrtQQ, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCSetPlane"), track.pt(), track.eta(), spm.centrality, (spm.uy + spm.ux) * invSqrtQQ, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnASetPlane"), track.pt(), track.eta(), spm.centrality, (minusQ * spm.ux - spm.uy) * invSqrtQQ, weight); } - if (cfgFillMixedHarmonics) { + if (cfg.cFillMixedHarmonics) { registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("MH/vnAxCxUx_MH"), track.pt(), track.eta(), spm.centrality, (spm.uxMH * spm.qxA * spm.qxC) / spm.corrQQx, weight); registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("MH/vnAyCyUx_MH"), track.pt(), track.eta(), spm.centrality, (spm.uxMH * spm.qyA * spm.qyC) / spm.corrQQy, weight); registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("MH/vnAxCyUy_MH"), track.pt(), track.eta(), spm.centrality, (spm.uyMH * spm.qxA * spm.qyC) / spm.corrQQx, weight); registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("MH/vnAyCxUy_MH"), track.pt(), track.eta(), spm.centrality, (spm.uyMH * spm.qyA * spm.qxC) / spm.corrQQy, weight); } - if (cfgFillXandYterms) { - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnAx"), track.pt(), track.eta(), spm.centrality, (spm.ux * spm.qxA) / std::sqrt(std::fabs(spm.corrQQx)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnAy"), track.pt(), track.eta(), spm.centrality, (spm.uy * spm.qyA) / std::sqrt(std::fabs(spm.corrQQy)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCx"), track.pt(), track.eta(), spm.centrality, (spm.ux * spm.qxC) / std::sqrt(std::fabs(spm.corrQQx)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCy"), track.pt(), track.eta(), spm.centrality, (spm.uy * spm.qyC) / std::sqrt(std::fabs(spm.corrQQy)), weight); + if (cfg.cFillXandYterms) { + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnAx"), track.pt(), track.eta(), spm.centrality, (spm.ux * spm.qxA) * invSqrtQQx, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnAy"), track.pt(), track.eta(), spm.centrality, (spm.uy * spm.qyA) * invSqrtQQy, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCx"), track.pt(), track.eta(), spm.centrality, (spm.ux * spm.qxC) * invSqrtQQx, weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnCy"), track.pt(), track.eta(), spm.centrality, (spm.uy * spm.qyC) * invSqrtQQy, weight); } - if (cfgFillEventPlane) { // only fill for inclusive! + if (cfg.cFillEventPlane) { // only fill for inclusive! registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnA_EP"), track.pt(), track.eta(), spm.centrality, spm.vnA, weight); registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnC_EP"), track.pt(), track.eta(), spm.centrality, spm.vnC, weight); registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("vnFull_EP"), track.pt(), track.eta(), spm.centrality, spm.vnFull, weight); } - if (cfgFillMeanPT) { - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/hMeanPtEtaCent"), track.eta(), spm.centrality, track.pt(), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/hMeanPtCent"), spm.centrality, track.pt(), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A"), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyA + spm.ux * spm.qxA) / (std::sqrt(std::fabs(spm.corrQQ)) * spm.meanPtWeight)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C"), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyC + spm.ux * spm.qxC) / (std::sqrt(std::fabs(spm.corrQQ)) * spm.meanPtWeight)), weight); + if (cfg.cFillMeanPT) { + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A3D"), track.pt(), track.eta(), spm.centrality, track.pt() * ((uqA) * (invSqrtQQ * invMeanPtQQ)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C3D"), track.pt(), track.eta(), spm.centrality, track.pt() * ((uqC) * (invSqrtQQ * invMeanPtQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1Aodd"), track.eta(), spm.centrality, track.pt() * scale * ((spm.uy * spm.qyA + spm.ux * spm.qxA) / (std::sqrt(std::fabs(spm.corrQQ)) * spm.meanPtWeight)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1Codd"), track.eta(), spm.centrality, track.pt() * scale * ((spm.uy * spm.qyC + spm.ux * spm.qxC) / (std::sqrt(std::fabs(spm.corrQQ)) * spm.meanPtWeight)), weight); + if (cfg.cFillMeanPTextra) { + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/hMeanPtEtaCent"), track.eta(), spm.centrality, track.pt(), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/hMeanPtCent"), spm.centrality, track.pt(), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A"), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyA + spm.ux * spm.qxA) * (invSqrtQQ * invMeanPtQQ)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C"), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyC + spm.ux * spm.qxC) * (invSqrtQQ * invMeanPtQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A3D"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyA + spm.ux * spm.qxA) / (std::sqrt(std::fabs(spm.corrQQ)) * spm.meanPtWeight)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C3D"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyC + spm.ux * spm.qxC) / (std::sqrt(std::fabs(spm.corrQQ)) * spm.meanPtWeight)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1Aodd"), track.eta(), spm.centrality, track.pt() * scale * ((spm.uy * spm.qyA + spm.ux * spm.qxA) * (invSqrtQQ * invMeanPtQQ)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1Codd"), track.eta(), spm.centrality, track.pt() * scale * ((spm.uy * spm.qyC + spm.ux * spm.qxC) * (invSqrtQQ * invMeanPtQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A3Dx"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.ux * spm.qxA) / (std::sqrt(std::fabs(spm.corrQQx)) * spm.meanPtWeight)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C3Dx"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.ux * spm.qxC) / (std::sqrt(std::fabs(spm.corrQQx)) * spm.meanPtWeight)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A3Dx"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.ux * spm.qxA) * (invSqrtQQx * invMeanPtQQ)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C3Dx"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.ux * spm.qxC) * (invSqrtQQx * invMeanPtQQ)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A3Dy"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyA) / (std::sqrt(std::fabs(spm.corrQQy)) * spm.meanPtWeight)), weight); - registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C3Dy"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyC) / (std::sqrt(std::fabs(spm.corrQQy)) * spm.meanPtWeight)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1A3Dy"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyA) * (invSqrtQQy * invMeanPtQQ)), weight); + registry.fill(HIST(Charge[ct]) + HIST(Species[pt]) + HIST("meanPT/ptV1C3Dy"), track.pt(), track.eta(), spm.centrality, track.pt() * ((spm.uy * spm.qyC) * (invSqrtQQy * invMeanPtQQ)), weight); + } } } template - inline void fillTrackQA(TrackObject track) + inline void fillTrackQA(const TrackObject& track) { - if (!cfgFillTrackQA) + if (!cfg.cFillTrackQA) { return; + } + + double weight = spm.wacc[ct][par] * spm.weff[ct][par] * spm.centWeight; - static constexpr std::string_view Time[] = {"before/", "after/"}; - // NOTE: species[kUnidentified] = "" (when nocfgTrackSelDo) { - if (cfgTrackSelDoTrackQAvsCent) { - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPt_Eta"), track.pt(), track.eta(), spm.centrality, spm.wacc[ct][par] * spm.weff[ct][par]); + // NOTE: species[kUnidentified] = "" (when nocfg.cTrackSelDo) { + if (cfg.cTrackSelDoTrackQAvsCent) { + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPt_Eta"), track.pt(), track.eta(), spm.centrality, weight); histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPt_Eta_uncorrected"), track.pt(), track.eta(), spm.centrality); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta"), track.phi(), track.eta(), spm.centrality, spm.wacc[ct][par] * spm.weff[ct][par]); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta"), track.phi(), track.eta(), spm.centrality, weight); histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta_uncorrected"), track.phi(), track.eta(), spm.centrality); } else { histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta_Pt"), track.phi(), track.eta(), track.pt()); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta_Pt_corrected"), track.phi(), track.eta(), track.pt(), spm.wacc[ct][par] * spm.weff[ct][par]); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta_Pt_corrected"), track.phi(), track.eta(), track.pt(), weight); } histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta_vz"), track.phi(), track.eta(), spm.vz); histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hPhi_Eta_vz_corrected"), track.phi(), track.eta(), spm.vz, spm.wacc[ct][par]); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hDCAxy_pt"), track.pt(), track.dcaXY(), spm.wacc[ct][par] * spm.weff[ct][par]); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hDCAz_pt"), track.pt(), track.dcaZ(), spm.wacc[ct][par] * spm.weff[ct][par]); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hSharedClusters_pt"), track.pt(), track.tpcFractionSharedCls(), spm.wacc[ct][par] * spm.weff[ct][par]); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hCrossedRows_pt"), track.pt(), track.tpcNClsCrossedRows(), spm.wacc[ct][par] * spm.weff[ct][par]); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hCrossedRows_vs_SharedClusters"), track.tpcNClsCrossedRows(), track.tpcFractionSharedCls(), spm.wacc[ct][par] * spm.weff[ct][par]); - histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hMeanPtEta"), track.eta(), spm.centrality, track.pt(), spm.wacc[ct][par] * spm.weff[ct][par]); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hDCAxy_pt"), track.pt(), track.dcaXY(), weight); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hDCAz_pt"), track.pt(), track.dcaZ(), weight); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hSharedClusters_pt"), track.pt(), track.tpcFractionSharedCls(), weight); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hCrossedRows_pt"), track.pt(), track.tpcNClsCrossedRows(), weight); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hCrossedRows_vs_SharedClusters"), track.tpcNClsCrossedRows(), track.tpcFractionSharedCls(), weight); + histos.fill(HIST(Charge[ct]) + HIST(Species[par]) + HIST("QA/") + HIST(Time[ft]) + HIST("hMeanPtEta"), track.eta(), spm.centrality, track.pt(), weight); } template - inline void fillPIDQA(TrackObject track) + inline void fillPIDQA(const TrackObject& track) { - if (!cfgFillTrackQA) + if (!cfg.cFillTrackQA) { return; + } if constexpr (framework::has_type_v) { histos.fill(HIST(Charge[ct]) + HIST("pion/") + HIST("QA/") + HIST(Time[ft]) + HIST("hNsigmaTOF_pt"), track.pt(), track.tofNSigmaPi()); histos.fill(HIST(Charge[ct]) + HIST("pion/") + HIST("QA/") + HIST(Time[ft]) + HIST("hNsigmaTPC_pt"), track.pt(), track.tpcNSigmaPi()); @@ -1202,9 +1407,8 @@ struct FlowSP { } template - inline void fillMCPtHistos(TrackObject track, int pdgCode) + inline void fillMCPtHistos(const TrackObject& track, int pdgCode) { - static constexpr std::string_view Time[] = {"before/", "after/"}; static constexpr std::string_view Mode[] = {"Gen/", "Reco/"}; registry.fill(HIST("trackMC") + HIST(Mode[md]) + HIST(Time[ft]) + HIST("incl/hPt_hadron"), track.pt(), track.eta(), spm.centrality); @@ -1238,25 +1442,13 @@ struct FlowSP { } } - template - inline void fillPrimaryHistos(McParticleObject mcparticle) - { - static constexpr std::string_view Time[] = {"/before", "/after"}; - - if (!mcparticle.isPhysicalPrimary()) { - registry.fill(HIST("trackMCReco") + HIST(Time[ft]) + HIST("/hIsPhysicalPrimary"), 0, spm.centrality); - } else { - registry.fill(HIST("trackMCReco") + HIST(Time[ft]) + HIST("/hIsPhysicalPrimary"), 1, spm.centrality); - } - } - template - void fillAllQA(TrackObject track) + void fillAllQA(const TrackObject& track) { fillTrackQA(track); fillPIDQA(track); - if (cfgFillChargeDependenceQA) { + if (cfg.cFillChargeDependenceQA) { switch (spm.charge) { case kPositive: { fillTrackQA(track); @@ -1278,37 +1470,44 @@ struct FlowSP { histos.fill(HIST("hEventCount"), evSel_FilteredEvent); auto bc = collision.bc_as(); int standardMagField = 99999; - auto field = (cfgMagField == standardMagField) ? getMagneticField(bc.timestamp()) : cfgMagField; - - if (bc.runNumber() != cfg.lastRunNumber) { - cfg.correctionsLoaded = false; - cfg.clCentrality = false; - cfg.lastRunNumber = bc.runNumber(); - cfg.mAcceptance.clear(); - LOGF(info, "Size of mAcceptance: %i (should be 0)", (int)cfg.mAcceptance.size()); + auto field = (cfg.cMagField == standardMagField) ? getMagneticField(bc.timestamp()) : cfg.cMagField; + + if (bc.runNumber() != conf.lastRunNumber) { + conf.correctionsLoaded = false; + conf.clCentrality = false; + conf.lastRunNumber = bc.runNumber(); + conf.mAcceptance.clear(); + LOGF(info, "Size of mAcceptance: %i (should be 0)", (int)conf.mAcceptance.size()); } - if (cfgFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(collision, tracks); + } loadCorrections(bc.timestamp()); spm.centrality = collision.centFT0C(); - if (cfgCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfgCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfgCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfgCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (!eventSelected(collision, tracks.size())) + if (!eventSelected(collision, tracks.size())) { return; + } - if (!collision.isSelected()) // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC + if (!collision.isSelected()) { // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC return; + } histos.fill(HIST("hEventCount"), evSel_isSelectedZDC); // Always fill centrality histogram after event selections! @@ -1325,6 +1524,8 @@ struct FlowSP { spm.psiA = 1.0 * std::atan2(spm.qyA, spm.qxA); spm.psiC = 1.0 * std::atan2(spm.qyC, spm.qxC); + int nshift = 10; // no. of iterations + // https://twiki.cern.ch/twiki/pub/ALICE/DirectedFlowAnalysisNote/vn_ZDC_ALICE_INT_NOTE_version02.pdf spm.psiFull = 1.0 * std::atan2(spm.qyA + spm.qyC, spm.qxA + spm.qxC); @@ -1335,13 +1536,18 @@ struct FlowSP { registry.fill(HIST("QQCorrelations/qAXYqCXY"), spm.centrality, spm.qyA * spm.qxC + spm.qxA * spm.qyC); registry.fill(HIST("QQCorrelations/qAqCX"), spm.centrality, spm.qxA * spm.qxC); registry.fill(HIST("QQCorrelations/qAqCY"), spm.centrality, spm.qyA * spm.qyC); + for (int ishift = 1; ishift <= nshift; ishift++) { + registry.fill(HIST("shift/ShiftZDCC"), spm.centrality, 0.5, ishift - 0.5, std::sin(ishift * 1.0 * spm.psiC)); + registry.fill(HIST("shift/ShiftZDCC"), spm.centrality, 1.5, ishift - 0.5, std::cos(ishift * 1.0 * spm.psiC)); + registry.fill(HIST("shift/ShiftZDCA"), spm.centrality, 0.5, ishift - 0.5, std::sin(ishift * 1.0 * spm.psiA)); + registry.fill(HIST("shift/ShiftZDCA"), spm.centrality, 1.5, ishift - 0.5, std::cos(ishift * 1.0 * spm.psiA)); + } - if (cfgFillEventQA) { + if (cfg.cFillEventQA) { histos.fill(HIST("QA/hCentFull"), spm.centrality, 1); } - if (cfgFillEventPlaneQA) { - histos.fill(HIST("QA/hSPplaneA"), spm.psiA, 1); - histos.fill(HIST("QA/hSPplaneC"), spm.psiC, 1); + if (cfg.cFillEventPlaneQA) { + histos.fill(HIST("QA/hSPplaneAC"), spm.psiA, spm.psiC, 1); histos.fill(HIST("QA/hSPplaneFull"), spm.psiFull, 1); histos.fill(HIST("QA/hCosPhiACosPhiC"), spm.centrality, std::cos(spm.psiA) * std::cos(spm.psiC)); histos.fill(HIST("QA/hSinPhiASinPhiC"), spm.centrality, std::sin(spm.psiA) * std::sin(spm.psiC)); @@ -1350,8 +1556,9 @@ struct FlowSP { histos.fill(HIST("QA/hFullEvPlaneRes"), spm.centrality, -1 * std::cos(spm.psiA - spm.psiC)); } - if (spm.centrality > cfgCentMax || spm.centrality < cfgCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } histos.fill(HIST("hEventCount"), evSel_CentCuts); @@ -1359,88 +1566,96 @@ struct FlowSP { // Only load once! // If not loaded set to 1 - if (cfgCCDBdir_QQ.value.empty() == false) { - if (!cfg.clQQ) { - TList* hcorrList = ccdb->getForTimeStamp(cfgCCDBdir_QQ.value, bc.timestamp()); - cfg.hcorrQQ = reinterpret_cast(hcorrList->FindObject("qAqCXY")); - cfg.hcorrQQx = reinterpret_cast(hcorrList->FindObject("qAqCX")); - cfg.hcorrQQy = reinterpret_cast(hcorrList->FindObject("qAqCY")); - cfg.clQQ = true; + if (!cfg.cCCDBdir_QQ.value.empty()) { + if (!conf.clQQ) { + auto* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); + conf.hcorrQQ = dynamic_cast(hcorrList->FindObject("qAqCXY")); + conf.hcorrQQx = dynamic_cast(hcorrList->FindObject("qAqCX")); + conf.hcorrQQy = dynamic_cast(hcorrList->FindObject("qAqCY")); + conf.clQQ = true; } - spm.corrQQ = cfg.hcorrQQ->GetBinContent(cfg.hcorrQQ->FindBin(spm.centrality)); - spm.corrQQx = cfg.hcorrQQx->GetBinContent(cfg.hcorrQQx->FindBin(spm.centrality)); - spm.corrQQy = cfg.hcorrQQy->GetBinContent(cfg.hcorrQQy->FindBin(spm.centrality)); + spm.corrQQ = conf.hcorrQQ->GetBinContent(conf.hcorrQQ->FindBin(spm.centrality)); + spm.corrQQx = conf.hcorrQQx->GetBinContent(conf.hcorrQQx->FindBin(spm.centrality)); + spm.corrQQy = conf.hcorrQQy->GetBinContent(conf.hcorrQQy->FindBin(spm.centrality)); } double evPlaneRes = 1.; - if (cfgCCDBdir_SP.value.empty() == false) { - if (!cfg.clEvPlaneRes) { - cfg.hEvPlaneRes = ccdb->getForTimeStamp(cfgCCDBdir_SP.value, bc.timestamp()); - cfg.clEvPlaneRes = true; + if (!cfg.cCCDBdir_SP.value.empty()) { + if (!conf.clEvPlaneRes) { + conf.hEvPlaneRes = ccdb->getForTimeStamp(cfg.cCCDBdir_SP.value, bc.timestamp()); + conf.clEvPlaneRes = true; } - evPlaneRes = cfg.hEvPlaneRes->GetBinContent(cfg.hEvPlaneRes->FindBin(spm.centrality)); - if (evPlaneRes < 0) + evPlaneRes = conf.hEvPlaneRes->GetBinContent(conf.hEvPlaneRes->FindBin(spm.centrality)); + if (evPlaneRes < 0) { LOGF(fatal, " > 0 for centrality %.2f! Cannot determine resolution.. Change centrality ranges!!!", spm.centrality); + } evPlaneRes = std::sqrt(evPlaneRes); } spm.centWeight = 1.; - if (cfgCCDBdir_centrality.value.empty() == false) { - if (!cfg.clCentrality) { - cfg.hCentrality = ccdb->getForTimeStamp(cfgCCDBdir_centrality.value, bc.timestamp()); - cfg.clCentrality = true; + if (!cfg.cCCDBdir_centrality.value.empty()) { + if (!conf.clCentrality) { + conf.hCentrality = ccdb->getForTimeStamp(cfg.cCCDBdir_centrality.value, bc.timestamp()); + conf.clCentrality = true; } - double centW = cfg.hCentrality->GetBinContent(cfg.hCentrality->FindBin(spm.centrality)); - if (centW < 0) { + double centW = conf.hCentrality->GetBinContent(conf.hCentrality->FindBin(spm.centrality)); + if (centW <= 0) { spm.centWeight = 0.; LOGF(fatal, "Centrality weight cannot be negative .. setting to 0. for (%.2f)", spm.centrality); + } else { + spm.centWeight = centW; } } + // Always fill centrality histogram after event selections! + histos.fill(HIST("hCentralityCW"), spm.centrality, spm.centWeight); + fillEventQA(collision, tracks); - TProfile* meanPTMap = new TProfile("meanPTMap", "meanPTMap", 8, -0.8, 0.8); - TProfile* meanPTMapPos = new TProfile("meanPTMapPos", "meanPTMapPos", 8, -0.8, 0.8); - TProfile* meanPTMapNeg = new TProfile("meanPTMapNeg", "meanPTMapNeg", 8, -0.8, 0.8); + ptmaps.meanPTMap->Reset(); + ptmaps.meanPTMapPos->Reset(); + ptmaps.meanPTMapNeg->Reset(); - TProfile* relPxA = new TProfile("relPxA", "relPxA", 8, -0.8, 0.8); - TProfile* relPxC = new TProfile("relPxC", "relPxC", 8, -0.8, 0.8); + ptmaps.relPxA->Reset(); + ptmaps.relPxC->Reset(); - TProfile* relPxANeg = new TProfile("relPxANeg", "relPxANeg", 8, -0.8, 0.8); - TProfile* relPxAPos = new TProfile("relPxAPos", "relPxAPos", 8, -0.8, 0.8); + ptmaps.relPxANeg->Reset(); + ptmaps.relPxAPos->Reset(); - TProfile* relPxCNeg = new TProfile("relPxCNeg", "relPxCNeg", 8, -0.8, 0.8); - TProfile* relPxCPos = new TProfile("relPxCPos", "relPxCPos", 8, -0.8, 0.8); + ptmaps.relPxCNeg->Reset(); + ptmaps.relPxCPos->Reset(); double sumPxAEvent = 0; - int meanPxEventCount = 0; + double meanPxEventCount = 0; double sumPxCEvent = 0; for (const auto& track : tracks) { - if (track.sign() == 0) + if (track.sign() == 0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); spm.charge = ((track.sign() > 0)) ? kPositive : kNegative; - if (cfgFillQABefore) { + if (cfg.cFillQABefore) { fillAllQA(track); } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { continue; + } spm.meanPtWeight = 1.0; - if (cfgCCDBdir_meanPt.value.empty() == false) { - if (!cfg.clMeanPt) { - cfg.hMeanPt = ccdb->getForTimeStamp(cfgCCDBdir_meanPt.value, bc.timestamp()); - cfg.clMeanPt = true; + if (!cfg.cCCDBdir_meanPt.value.empty()) { + if (!conf.clMeanPt) { + conf.hMeanPt = ccdb->getForTimeStamp(cfg.cCCDBdir_meanPt.value, bc.timestamp()); + conf.clMeanPt = true; } - int etaBin = cfg.hMeanPt->GetXaxis()->FindBin(track.eta()); - int centBin = cfg.hMeanPt->GetYaxis()->FindBin(spm.centrality); - double weight = cfg.hMeanPt->GetBinContent(etaBin, centBin); + int etaBin = conf.hMeanPt->GetXaxis()->FindBin(track.eta()); + int centBin = conf.hMeanPt->GetYaxis()->FindBin(spm.centrality); + double weight = conf.hMeanPt->GetBinContent(etaBin, centBin); if (weight > 0) { spm.meanPtWeight = 1.0 / weight; } else { @@ -1454,40 +1669,42 @@ struct FlowSP { // Fill NUA weights (last 0 is for Data see GFWWeights class (not a weight)) // ToDo: Add pi, ka, proton here! - if (cfgFillWeights) { + if (cfg.cFillWeights) { fWeights->fill(phi, track.eta(), spm.vz, track.pt(), spm.centrality, 0); registry.fill(HIST("weights2D/hPhi_Eta_vz"), phi, track.eta(), spm.vz, 1); } - if (cfgFillWeightsPOS && spm.charge == kPositive) { + if (cfg.cFillWeightsPOS && spm.charge == kPositive) { fWeightsPOS->fill(phi, track.eta(), spm.vz, track.pt(), spm.centrality, 0); registry.fill(HIST("weights2D/hPhi_Eta_vz_positive"), phi, track.eta(), spm.vz, 1); } - if (cfgFillWeightsNEG && spm.charge == kNegative) { + if (cfg.cFillWeightsNEG && spm.charge == kNegative) { fWeightsNEG->fill(phi, track.eta(), spm.vz, track.pt(), spm.centrality, 0); registry.fill(HIST("weights2D/hPhi_Eta_vz_negative"), phi, track.eta(), spm.vz, 1); } // Set weff and wacc for inclusive, negative and positive hadrons - if (!setCurrentParticleWeights(kInclusive, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) + if (!setCurrentParticleWeights(kInclusive, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) { continue; - if (!setCurrentParticleWeights(spm.charge, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) + } + if (!setCurrentParticleWeights(spm.charge, kUnidentified, phi, track.eta(), track.pt(), vtxz, spm.centrality)) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ParticleWeights); fillAllQA(track); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spm.ux = std::cos(cfgHarm * phi); - spm.uy = std::sin(cfgHarm * phi); + spm.ux = std::cos(cfg.cHarm * phi); + spm.uy = std::sin(cfg.cHarm * phi); - spm.uxMH = std::cos(cfgHarmMixed * phi); - spm.uyMH = std::sin(cfgHarmMixed * phi); + spm.uxMH = std::cos(cfg.cHarmMixed * phi); + spm.uyMH = std::sin(cfg.cHarmMixed * phi); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spm.vnA = std::cos(cfgHarm * (phi - spm.psiA)) / evPlaneRes; - spm.vnC = std::cos(cfgHarm * (phi - spm.psiC)) / evPlaneRes; - spm.vnFull = std::cos(cfgHarm * (phi - spm.psiFull)) / evPlaneRes; + spm.vnA = std::cos(cfg.cHarm * (phi - spm.psiA)) / evPlaneRes; + spm.vnC = std::cos(cfg.cHarm * (phi - spm.psiC)) / evPlaneRes; + spm.vnFull = std::cos(cfg.cHarm * (phi - spm.psiFull)) / evPlaneRes; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1496,7 +1713,7 @@ struct FlowSP { fillHistograms(track); - if (cfgFillChargeDependence) { + if (cfg.cFillChargeDependence) { switch (spm.charge) { case kPositive: fillHistograms(track); @@ -1518,56 +1735,61 @@ struct FlowSP { sumPxCEvent += spm.meanPxC * weightIncl; meanPxEventCount += weightIncl; - meanPTMap->Fill(track.eta(), track.pt(), weightIncl); - relPxA->Fill(track.eta(), drelPxA, weightIncl); - relPxC->Fill(track.eta(), drelPxC, weightIncl); + ptmaps.meanPTMap->Fill(track.eta(), track.pt(), weightIncl); + ptmaps.relPxA->Fill(track.eta(), drelPxA, weightIncl); + ptmaps.relPxC->Fill(track.eta(), drelPxC, weightIncl); if (spm.charge == kPositive) { - meanPTMapPos->Fill(track.eta(), track.pt(), weightPos); - relPxAPos->Fill(track.eta(), drelPxA, weightPos); - relPxCPos->Fill(track.eta(), drelPxC, weightPos); + ptmaps.meanPTMapPos->Fill(track.eta(), track.pt(), weightPos); + ptmaps.relPxAPos->Fill(track.eta(), drelPxA, weightPos); + ptmaps.relPxCPos->Fill(track.eta(), drelPxC, weightPos); } if (spm.charge == kNegative) { - meanPTMapNeg->Fill(track.eta(), track.pt(), weightNeg); - relPxANeg->Fill(track.eta(), drelPxA, weightNeg); - relPxCNeg->Fill(track.eta(), drelPxC, weightNeg); + ptmaps.meanPTMapNeg->Fill(track.eta(), track.pt(), weightNeg); + ptmaps.relPxANeg->Fill(track.eta(), drelPxA, weightNeg); + ptmaps.relPxCNeg->Fill(track.eta(), drelPxC, weightNeg); } } // end of track loop - double meanPxAEvent = sumPxAEvent / meanPxEventCount; - double meanPxCEvent = sumPxCEvent / meanPxEventCount; + double meanPxAEvent = 0; + double meanPxCEvent = 0; + + if (meanPxEventCount > 0) { + meanPxAEvent = sumPxAEvent / meanPxEventCount; + meanPxCEvent = sumPxCEvent / meanPxEventCount; + } - if (cfgFillMeanPT) { + if (cfg.cFillMeanPTextra) { registry.fill(HIST("incl/meanPT/meanPxA"), spm.centrality, spm.psiA - spm.psiC, meanPxAEvent); registry.fill(HIST("incl/meanPT/meanPxC"), spm.centrality, spm.psiA - spm.psiC, meanPxCEvent); } // Now we want to fill the final relPt histogram // Loop over all eta and fill bins - if (cfgFillMeanPT) { + if (cfg.cFillMeanPTextra) { int nBinsEta = 8; for (int etabin = 1; etabin <= nBinsEta; etabin++) { // eta bin is 1 --> Find centbin!! - double eta = meanPTMap->GetXaxis()->GetBinCenter(etabin); - double meanPt = meanPTMap->GetBinContent(etabin); - double meanPtPos = meanPTMapPos->GetBinContent(etabin); - double meanPtNeg = meanPTMapNeg->GetBinContent(etabin); + double eta = ptmaps.meanPTMap->GetXaxis()->GetBinCenter(etabin); + double meanPt = ptmaps.meanPTMap->GetBinContent(etabin); + double meanPtPos = ptmaps.meanPTMapPos->GetBinContent(etabin); + double meanPtNeg = ptmaps.meanPTMapNeg->GetBinContent(etabin); - double drelPxA = relPxA->GetBinContent(etabin); - double drelPxANeg = relPxANeg->GetBinContent(etabin); - double drelPxAPos = relPxAPos->GetBinContent(etabin); + double drelPxA = ptmaps.relPxA->GetBinContent(etabin); + double drelPxANeg = ptmaps.relPxANeg->GetBinContent(etabin); + double drelPxAPos = ptmaps.relPxAPos->GetBinContent(etabin); - double drelPxC = relPxC->GetBinContent(etabin); - double drelPxCNeg = relPxCNeg->GetBinContent(etabin); - double drelPxCPos = relPxCPos->GetBinContent(etabin); + double drelPxC = ptmaps.relPxC->GetBinContent(etabin); + double drelPxCNeg = ptmaps.relPxCNeg->GetBinContent(etabin); + double drelPxCPos = ptmaps.relPxCPos->GetBinContent(etabin); if (meanPt != 0) { registry.fill(HIST("incl/meanPT/meanRelPtA"), eta, spm.centrality, drelPxA / meanPt, spm.centWeight); registry.fill(HIST("incl/meanPT/meanRelPtC"), eta, spm.centrality, drelPxC / meanPt, spm.centWeight); - if (cfgFillChargeDependence) { + if (cfg.cFillChargeDependence) { registry.fill(HIST("neg/meanPT/meanRelPtA"), eta, spm.centrality, drelPxANeg / meanPtNeg, spm.centWeight); registry.fill(HIST("pos/meanPT/meanRelPtA"), eta, spm.centrality, drelPxAPos / meanPtPos, spm.centWeight); registry.fill(HIST("neg/meanPT/meanRelPtC"), eta, spm.centrality, drelPxCNeg / meanPtNeg, spm.centWeight); @@ -1576,16 +1798,6 @@ struct FlowSP { } } } - - delete meanPTMap; - delete meanPTMapPos; - delete meanPTMapNeg; - delete relPxA; - delete relPxANeg; - delete relPxAPos; - delete relPxC; - delete relPxCNeg; - delete relPxCPos; } PROCESS_SWITCH(FlowSP, processData, "Process analysis for non-derived data", true); @@ -1596,37 +1808,43 @@ struct FlowSP { histos.fill(HIST("hEventCount"), evSel_FilteredEvent); auto bc = collision.bc_as(); int standardMagField = 99999; - auto field = (cfgMagField == standardMagField) ? getMagneticField(bc.timestamp()) : cfgMagField; - - if (bc.runNumber() != cfg.lastRunNumber) { - cfg.correctionsLoaded = false; - cfg.clCentrality = false; - cfg.lastRunNumber = bc.runNumber(); - cfg.mAcceptance.clear(); - LOGF(info, "Size of mAcceptance: %i (should be 0)", (int)cfg.mAcceptance.size()); + auto field = (cfg.cMagField == standardMagField) ? getMagneticField(bc.timestamp()) : cfg.cMagField; + + if (bc.runNumber() != conf.lastRunNumber) { + conf.correctionsLoaded = false; + conf.clCentrality = false; + conf.lastRunNumber = bc.runNumber(); + conf.mAcceptance.clear(); + LOGF(info, "Size of mAcceptance: %i (should be 0)", (int)conf.mAcceptance.size()); } - if (cfgFillQABefore) + if (cfg.cFillQABefore) fillEventQA(collision, tracks); loadCorrections(bc.timestamp()); spm.centrality = collision.centFT0C(); - if (cfgCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfgCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfgCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfgCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (!eventSelected(collision, tracks.size())) + if (!eventSelected(collision, tracks.size())) { return; + } - if (!collision.isSelected()) // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC + if (!collision.isSelected()) { // selected by ZDCQVectors task (checks signal in ZDC) --> only possible in data not MC return; + } histos.fill(HIST("hEventCount"), evSel_isSelectedZDC); // Always fill centrality histogram after event selections! @@ -1645,50 +1863,51 @@ struct FlowSP { // https://twiki.cern.ch/twiki/pub/ALICE/DirectedFlowAnalysisNote/vn_ZDC_ALICE_INT_NOTE_version02.pdf spm.psiFull = 1.0 * std::atan2(spm.qyA + spm.qyC, spm.qxA + spm.qxC); - if (spm.centrality > cfgCentMax || spm.centrality < cfgCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } // Load correlations and SP resolution needed for Scalar Product and event plane methods. // Only load once! // If not loaded set to 1 - if (cfgCCDBdir_QQ.value.empty() == false) { - if (!cfg.clQQ) { - TList* hcorrList = ccdb->getForTimeStamp(cfgCCDBdir_QQ.value, bc.timestamp()); - cfg.hcorrQQ = reinterpret_cast(hcorrList->FindObject("qAqCXY")); - cfg.hcorrQQx = reinterpret_cast(hcorrList->FindObject("qAqCX")); - cfg.hcorrQQy = reinterpret_cast(hcorrList->FindObject("qAqCY")); - cfg.clQQ = true; + if (!cfg.cCCDBdir_QQ.value.empty()) { + if (!conf.clQQ) { + auto* hcorrList = ccdb->getForTimeStamp(cfg.cCCDBdir_QQ.value, bc.timestamp()); + conf.hcorrQQ = dynamic_cast(hcorrList->FindObject("qAqCXY")); + conf.hcorrQQx = dynamic_cast(hcorrList->FindObject("qAqCX")); + conf.hcorrQQy = dynamic_cast(hcorrList->FindObject("qAqCY")); + conf.clQQ = true; } - spm.corrQQ = cfg.hcorrQQ->GetBinContent(cfg.hcorrQQ->FindBin(spm.centrality)); - spm.corrQQx = cfg.hcorrQQx->GetBinContent(cfg.hcorrQQx->FindBin(spm.centrality)); - spm.corrQQy = cfg.hcorrQQy->GetBinContent(cfg.hcorrQQy->FindBin(spm.centrality)); + spm.corrQQ = conf.hcorrQQ->GetBinContent(conf.hcorrQQ->FindBin(spm.centrality)); + spm.corrQQx = conf.hcorrQQx->GetBinContent(conf.hcorrQQx->FindBin(spm.centrality)); + spm.corrQQy = conf.hcorrQQy->GetBinContent(conf.hcorrQQy->FindBin(spm.centrality)); } double evPlaneRes = 1.; - if (cfgCCDBdir_SP.value.empty() == false) { - if (!cfg.clEvPlaneRes) { - cfg.hEvPlaneRes = ccdb->getForTimeStamp(cfgCCDBdir_SP.value, bc.timestamp()); - cfg.clEvPlaneRes = true; + if (!cfg.cCCDBdir_SP.value.empty()) { + if (!conf.clEvPlaneRes) { + conf.hEvPlaneRes = ccdb->getForTimeStamp(cfg.cCCDBdir_SP.value, bc.timestamp()); + conf.clEvPlaneRes = true; } - evPlaneRes = cfg.hEvPlaneRes->GetBinContent(cfg.hEvPlaneRes->FindBin(spm.centrality)); - if (evPlaneRes < 0) + evPlaneRes = conf.hEvPlaneRes->GetBinContent(conf.hEvPlaneRes->FindBin(spm.centrality)); + if (evPlaneRes < 0) { LOGF(fatal, " > 0 for centrality %.2f! Cannot determine resolution.. Change centrality ranges!!!", spm.centrality); + } evPlaneRes = std::sqrt(evPlaneRes); } spm.centWeight = 1.; - if (cfgCCDBdir_centrality.value.empty() == false) { - if (!cfg.clCentrality) { - cfg.hCentrality = ccdb->getForTimeStamp(cfgCCDBdir_centrality.value, bc.timestamp()); - cfg.clCentrality = true; - } - double centW = cfg.hCentrality->GetBinContent(cfg.hCentrality->FindBin(spm.centrality)); - if (centW < 0) { - spm.centWeight = 1. / centW; - } else { + if (!cfg.cCCDBdir_centrality.value.empty()) { + if (!conf.clCentrality) { + conf.hCentrality = ccdb->getForTimeStamp(cfg.cCCDBdir_centrality.value, bc.timestamp()); + conf.clCentrality = true; + } + double centW = conf.hCentrality->GetBinContent(conf.hCentrality->FindBin(spm.centrality)); + + if (centW <= 0) { + spm.centWeight = 0; LOGF(fatal, "Centrality weight cannot be negative .. setting to 0. for (%.2f)", spm.centrality); - spm.centWeight = 0.; } } @@ -1696,18 +1915,19 @@ struct FlowSP { for (const auto& track : tracks) { - ParticleType trackPID = (cfgFillPID || cfgFillPIDQA) ? getTrackPID(track) : kUnidentified; + ParticleType trackPID = (cfg.cFillPID || cfg.cFillPIDQA) ? getTrackPID(track) : kUnidentified; histos.fill(HIST("hPIDcounts"), trackPID, track.pt()); - if (track.sign() == 0) + if (track.sign() == 0) { continue; + } histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); spm.charge = ((track.sign() > 0)) ? kPositive : kNegative; - if (cfgFillQABefore) { + if (cfg.cFillQABefore) { switch (trackPID) { case kPions: fillAllQA(track); @@ -1723,8 +1943,9 @@ struct FlowSP { } } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { continue; + } // constrain angle to 0 -> [0,0+2pi] auto phi = RecoDecay::constrainAngle(track.phi(), 0); @@ -1744,16 +1965,16 @@ struct FlowSP { } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spm.ux = std::cos(cfgHarm * phi); - spm.uy = std::sin(cfgHarm * phi); + spm.ux = std::cos(cfg.cHarm * phi); + spm.uy = std::sin(cfg.cHarm * phi); - spm.uxMH = std::cos(cfgHarmMixed * phi); - spm.uyMH = std::sin(cfgHarmMixed * phi); + spm.uxMH = std::cos(cfg.cHarmMixed * phi); + spm.uyMH = std::sin(cfg.cHarmMixed * phi); // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spm.vnA = std::cos(cfgHarm * (phi - spm.psiA)) / evPlaneRes; - spm.vnC = std::cos(cfgHarm * (phi - spm.psiC)) / evPlaneRes; - spm.vnFull = std::cos(cfgHarm * (phi - spm.psiFull)) / evPlaneRes; + spm.vnA = std::cos(cfg.cHarm * (phi - spm.psiA)) / evPlaneRes; + spm.vnC = std::cos(cfg.cHarm * (phi - spm.psiC)) / evPlaneRes; + spm.vnFull = std::cos(cfg.cHarm * (phi - spm.psiFull)) / evPlaneRes; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1773,7 +1994,7 @@ struct FlowSP { break; } - if (cfgFillChargeDependence) { + if (cfg.cFillChargeDependence) { switch (spm.charge) { case kPositive: { switch (trackPID) { @@ -1814,36 +2035,42 @@ struct FlowSP { PROCESS_SWITCH(FlowSP, processDataPID, "Process analysis for non-derived data with PID", false); - void processMCReco(CC const& collision, aod::BCsWithTimestamps const&, TCs const& tracks, FilteredTCs const& filteredTracks, aod::McParticles const&) + void processMCReco(CC const& collision, aod::BCsWithTimestamps const&, TCs const& tracks, FilteredTCs const& filteredTracks, MCs const& McParts, aod::McCollisions const&) { auto bc = collision.template bc_as(); int standardMagField = 99999; - auto field = (cfgMagField == standardMagField) ? getMagneticField(bc.timestamp()) : cfgMagField; + auto field = (cfg.cMagField == standardMagField) ? getMagneticField(bc.timestamp()) : cfg.cMagField; spm.vz = collision.posZ(); spm.centrality = collision.centFT0C(); - if (cfgCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = collision.centFT0CVariant1(); - if (cfgCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = collision.centFT0M(); - if (cfgCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = collision.centFV0A(); - if (cfgCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = collision.centNGlobal(); + } - if (cfgFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(collision, filteredTracks); + } - if (!eventSelected(collision, filteredTracks.size())) + if (!eventSelected(collision, filteredTracks.size())) { return; + } - if (spm.centrality > cfgCentMax || spm.centrality < cfgCentMin) + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { return; + } histos.fill(HIST("hEventCount"), evSel_CentCuts); if (!collision.has_mcCollision()) { - LOGF(info, "No mccollision found for this collision"); return; } @@ -1852,29 +2079,36 @@ struct FlowSP { registry.fill(HIST("trackMCReco/hTrackSize_unFiltered"), tracks.size(), spm.centrality); registry.fill(HIST("trackMCReco/hTrackSize_Filtered"), filteredTracks.size(), spm.centrality); - for (const auto& track : filteredTracks) { - auto mcParticle = track.mcParticle(); - if (track.sign() == 0.0) + for (const auto& track : tracks) { + + if (!track.has_mcParticle()) { continue; - histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); + } - fillMCPtHistos(track, mcParticle.pdgCode()); - fillPrimaryHistos(mcParticle); + auto mcParticle = track.mcParticle_as(); - if (!mcParticle.isPhysicalPrimary()) + if (track.sign() == 0.0) { continue; + } + histos.fill(HIST("hTrackCount"), trackSel_ZeroCharge); + + if (cfg.cFillWithMCParticle) { + fillMCPtHistos(mcParticle, mcParticle.pdgCode()); + } else { + fillMCPtHistos(track, mcParticle.pdgCode()); + } spm.charge = (track.sign() > 0) ? kPositive : kNegative; int minVal = 100; - if (cfgFilterLeptons && std::abs(mcParticle.pdgCode()) < minVal) { + if (cfg.cFilterLeptons && std::abs(mcParticle.pdgCode()) < minVal) { continue; } // This neglects PID (for now) later use getPID like in data. - if (cfgFillQABefore) { + if (cfg.cFillQABefore) { fillAllQA(track); - if (cfgFillPIDQA) { + if (cfg.cFillPIDQA) { switch (std::abs(mcParticle.pdgCode())) { case kPiPlus: fillAllQA(track); @@ -1889,14 +2123,33 @@ struct FlowSP { } } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { continue; + } - fillMCPtHistos(track, mcParticle.pdgCode()); + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + + registry.fill(HIST("trackMCReco/incl/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/incl/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); + if (spm.charge == kPositive) { + registry.fill(HIST("trackMCReco/pos/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/pos/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); + } else if (spm.charge == kNegative) { + registry.fill(HIST("trackMCReco/neg/hPtMCPtTrack"), mcParticle.pt(), track.pt()); + registry.fill(HIST("trackMCReco/neg/hEtaMCEtaTrack"), mcParticle.eta(), track.eta()); + } + + if (cfg.cFillWithMCParticle) { + fillMCPtHistos(mcParticle, mcParticle.pdgCode()); + } else { + fillMCPtHistos(track, mcParticle.pdgCode()); + } fillAllQA(track); - if (cfgFillPIDQA) { + if (cfg.cFillPIDQA) { switch (std::abs(mcParticle.pdgCode())) { case kPions: fillAllQA(track); @@ -1910,9 +2163,61 @@ struct FlowSP { } } - fillPrimaryHistos(mcParticle); - } // end of track loop + + if (cfg.cDoGeneratedInReco) { + + auto mcCollision = collision.mcCollision(); + float vtxz = mcCollision.posZ(); + + // get McParticles which belong to mccollision + auto partSlice = McParts.sliceBy(partPerMcCollision, mcCollision.globalIndex()); + + for (const auto& particle : partSlice) { + + if (!particle.isPhysicalPrimary()) { + continue; + } + + auto pdgCode = particle.pdgCode(); + auto pdgInfo = pdg->GetParticle(pdgCode); + + if (std::abs(pdgInfo->Charge()) < 1) { + continue; + } + + spm.charge = (pdgInfo->Charge() > 0) ? kPositive : kNegative; + + int minVal = 100; + if (cfg.cFilterLeptons && std::abs(pdgCode) < minVal) { + continue; + } + + fillMCPtHistos(particle, pdgCode); + + registry.fill(HIST("trackMCGen/before/incl/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + + if (spm.charge == kPositive) { + registry.fill(HIST("trackMCGen/before/pos/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } else { + registry.fill(HIST("trackMCGen/before/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } + + if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) { + continue; + } + + fillMCPtHistos(particle, pdgCode); + + registry.fill(HIST("trackMCGen/after/incl/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + + if (spm.charge == kPositive) { + registry.fill(HIST("trackMCGen/after/pos/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } else { + registry.fill(HIST("trackMCGen/after/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); + } + } + } } PROCESS_SWITCH(FlowSP, processMCReco, "Process analysis for MC reconstructed events", false); @@ -1937,20 +2242,27 @@ struct FlowSP { // get tracks that belong to reconstructed collision auto trackSlice = tracks.sliceBy(trackPerCollision, col.globalIndex()); + // get filtered tracks that belong to reconstructed collision + // mcReco uses filtered tracks auto filteredTrackSlice = filteredTracks.sliceBy(trackPerCollision, col.globalIndex()); spm.centrality = col.centFT0C(); - if (cfgCentFT0Cvariant1) + if (cfg.cCentFT0Cvariant1) { spm.centrality = col.centFT0CVariant1(); - if (cfgCentFT0M) + } + if (cfg.cCentFT0M) { spm.centrality = col.centFT0M(); - if (cfgCentFV0A) + } + if (cfg.cCentFV0A) { spm.centrality = col.centFV0A(); - if (cfgCentNGlobal) + } + if (cfg.cCentNGlobal) { spm.centrality = col.centNGlobal(); + } - if (cfgFillQABefore) + if (cfg.cFillQABefore) { fillEventQA(col, filteredTrackSlice); + } if (trackSlice.size() < 1) { colSelected = false; @@ -1961,7 +2273,7 @@ struct FlowSP { continue; } - if (spm.centrality > cfgCentMax || spm.centrality < cfgCentMin) { + if (spm.centrality > cfg.cCentMax || spm.centrality < cfg.cCentMin) { colSelected = false; continue; } @@ -1971,25 +2283,28 @@ struct FlowSP { } // leave reconstructed collision loop - if (!colSelected) + if (!colSelected) { continue; + } float vtxz = mcCollision.posZ(); for (const auto& particle : partSlice) { - if (!particle.isPhysicalPrimary()) + if (!particle.isPhysicalPrimary()) { continue; + } auto pdgCode = particle.pdgCode(); auto pdgInfo = pdg->GetParticle(pdgCode); - if (std::abs(pdgInfo->Charge()) < 1) + if (std::abs(pdgInfo->Charge()) < 1) { continue; + } spm.charge = (pdgInfo->Charge() > 0) ? kPositive : kNegative; int minVal = 100; - if (cfgFilterLeptons && std::abs(pdgCode) < minVal) { + if (cfg.cFilterLeptons && std::abs(pdgCode) < minVal) { continue; } @@ -2003,8 +2318,9 @@ struct FlowSP { registry.fill(HIST("trackMCGen/before/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); } - if (particle.eta() < -cfgTrackSelsEta || particle.eta() > cfgTrackSelsEta || particle.pt() < cfgTrackSelsPtmin || particle.pt() > cfgTrackSelsPtmax) + if (particle.eta() < -cfg.cTrackSelsEta || particle.eta() > cfg.cTrackSelsEta || particle.pt() < cfg.cTrackSelsPtmin || particle.pt() > cfg.cTrackSelsPtmax) { continue; + } fillMCPtHistos(particle, pdgCode); @@ -2015,7 +2331,7 @@ struct FlowSP { } else { registry.fill(HIST("trackMCGen/after/neg/phi_eta_vtxZ_gen"), particle.phi(), particle.eta(), vtxz); } - } + } // end of parts } } PROCESS_SWITCH(FlowSP, processMCGen, "Process analysis for MC generated events", false); diff --git a/PWGCF/Flow/Tasks/flowTask.cxx b/PWGCF/Flow/Tasks/flowTask.cxx index f71fb84d902..2471b77f282 100644 --- a/PWGCF/Flow/Tasks/flowTask.cxx +++ b/PWGCF/Flow/Tasks/flowTask.cxx @@ -21,6 +21,7 @@ #include "PWGCF/GenericFramework/Core/GFWWeights.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -75,10 +76,12 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::analysis::genericframework; +using namespace o2::analysis::genericframework::eventweight; +using namespace o2::aod::rctsel; #define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; static constexpr double LongArrayDouble[4][2] = {{-2.0, -2.0}, {-2.0, -2.0}, {-2.0, -2.0}, {-2.0, -2.0}}; -static constexpr float TrackCutArray[6][2] = {{2.5f, 2.5f}, {50.0f, 50.0f}, {70.0f, 70.0f}, {5.0f, 5.0f}, {2.0f, 2.0f}, {7.0f, 7.0f}}; +static constexpr float TrackCutArray[7][2] = {{2.5f, 2.5f}, {50.0f, 50.0f}, {70.0f, 70.0f}, {5.0f, 5.0f}, {2.0f, 2.0f}, {7.0f, 7.0f}, {0.f, 0.f}}; struct FlowTask { @@ -97,7 +100,8 @@ struct FlowTask { O2_DEFINE_CONFIGURABLE(cfgEtaVnPt, float, 0.4, "eta range for pt in vn-pt correlations") Configurable> cfgPtPtGaps{"cfgPtPtGaps", {LongArrayDouble[0], 4, 2, {"subevent 1", "subevent 2", "subevent 3", "subevent 4"}, {"etamin", "etamax"}}, "{etamin,etamax} for all ptpt-subevents"}; O2_DEFINE_CONFIGURABLE(cfgEtaGapPtPtEnabled, bool, false, "switch of subevent pt-pt correlations") - Configurable> cfgTrackCuts{"cfgTrackCuts", {TrackCutArray[0], 6, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAz", "DCAxy Nsigma"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {TrackCutArray[0], 7, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAz", "DCAxy Nsigma", "DCAz Nsigma(override)"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; + std::array, 7> trackCuts{}; enum TrackCut { // enum for labelledArray track selection kChi2prTPCcls = 0, // max chi2 per TPC clusters @@ -105,12 +109,19 @@ struct FlowTask { kTPCCrossedRows, // minimum TPC crossed rows kITSclu, // minimum ITS found clusters kDCAz, // max DCA to vertex z - kDCAxyNSigma // 0: disable; Cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track + kDCAxyNSigma, // 0: disable; Cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track + kDCAzNSigma // 0: disable; Cut on number of sigma deviations from expected DCA in the transverse direction }; enum TrackCutGroup { kTrCutNch = 0, kTrCutObs = 1 }; + enum GoodITSLayersFlag { + kITSLayersAll, + kITSLayer0123, + kITSLayer3, + kCount_ITSLayersFlag + }; // Additional events selection flags O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, false, "rejects collisions which are associated with the same found-by-T0 bunch crossing") @@ -118,10 +129,12 @@ struct FlowTask { O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersAll, bool, true, "cut time intervals with dead ITS staves") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayers, bool, true, "cut time intervals with dead ITS staves") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersFlag, int, 0, "0: kIsGoodITSLayersAll; 1: kIsGoodITSLayer0123; 2: kIsGoodITSLayer3") O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking', 'CBT_hadronPID' ") O2_DEFINE_CONFIGURABLE(cfgGetInteractionRate, bool, false, "Get interaction rate from CCDB") O2_DEFINE_CONFIGURABLE(cfgUseInteractionRateCut, bool, false, "Use events with low interaction rate") O2_DEFINE_CONFIGURABLE(cfgCutMaxIR, float, 50.0f, "maximum interaction rate (kHz)") @@ -147,6 +160,7 @@ struct FlowTask { O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") O2_DEFINE_CONFIGURABLE(cfgEfficiencyForNch, std::string, "", "CCDB path to efficiency object, only for Nch correction") O2_DEFINE_CONFIGURABLE(cfgAcceptance, std::string, "", "CCDB path to acceptance object") + O2_DEFINE_CONFIGURABLE(cfgUserPtVnEvWeightEnabled, bool, false, "0: use unity weight; 1: use multiplicity weight") O2_DEFINE_CONFIGURABLE(cfgUseSmallMemory, bool, false, "Use small memory mode") O2_DEFINE_CONFIGURABLE(cfgConsistentEventFlag, int, 0, "Flag to select consistent events - 0: off, 1: v2{2} gap calculable, 2: v2{4} full calculable, 4: v2{4} gap calculable, 8: v2{4} 3sub calculable") Configurable> cfgConsistentEventVector{"cfgConsistentEventVector", std::vector{-0.8, -0.5, -0.4, 0.4, 0.5, 0.8}, "eta regions: left(min,max), mid(min,max), right(min,max)"}; @@ -204,7 +218,10 @@ struct FlowTask { // Functional form of pt-dependent DCAxy cut TF1* fPtDepDCAxy = nullptr; TF1* fPtDepDCAxyForNch = nullptr; + TF1* fPtDepDCAz = nullptr; + TF1* fPtDepDCAzForNch = nullptr; O2_DEFINE_CONFIGURABLE(cfgDCAxyFunc, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"); + O2_DEFINE_CONFIGURABLE(cfgDCAzFunc, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAz cut"); } cfgFuncParas; struct : ConfigurableGroup { @@ -227,6 +244,14 @@ struct FlowTask { std::vector nPt; // fraction of particles in each pT bin in one event std::vector> listNptV2; std::vector> listPtX; + O2_DEFINE_CONFIGURABLE(cfgManualCk, bool, false, "Produce pt-pt variance") + O2_DEFINE_CONFIGURABLE(cfgNewPtContainer, bool, false, "Switch to new version of flowPtContainer. This switch will be removed after validation of new container.") + Configurable cfgNpcRegions{"cfgNpcRegions", {{"refN", "refP", "refMid", "refFull"}, {-0.8, 0.4, -0.4, -0.8}, {-0.4, 0.8, 0.4, 0.8}, {0, 0, 0, 0}, {1, 1, 1, 1}}, "Configurations for GFW regions"}; + Configurable cfgNpcPtVnCorrConfig{"cfgNpcPtVnCorrConfig", {{"refP {pt} refN {pt}", "refP {2} refMid {pt} refN {-2}", "refP {2} refMid {pt pt} refN {-2}", "refFull {pt}", "refFull {pt pt}", "refFull {pt pt pt}", "refFull {pt pt pt pt}", "refP {pt} refMid {pt} refN {pt}"}, {"ChGapPtPt", "ChGap22pt", "ChGap22ptpt", "ChFullPt", "ChFullPtPt", "ChFullPtPtPt", "ChFullPtPtPtPt", "ChGapPtPtPtABC"}, {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for vn-pt correlations based on new version flowPtContainer"}; + Configurable> cfgNpcGFWCorr{"cfgNpcGFWCorr", std::vector{"refN {2} refP {-2}", "refN {3} refP {-3}"}, "GFW corrconfig for new version flowPtContainer"}; + GFWRegions npcRegions; + GFWCorrConfigs npcPtVnCorrConfig; + GFW npcGfw; } cfgAdditionObs; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "pt axis for histograms"}; @@ -236,7 +261,7 @@ struct FlowTask { ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -1, 1}, "DCA_{xy} (cm)"}; Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVertex) && (aod::cent::centFT0C > cfgCentFT0CMin) && (aod::cent::centFT0C < cfgCentFT0CMax); - Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; // Filter for MCcollisions @@ -271,7 +296,7 @@ struct FlowTask { std::vector corrconfigs; GFWCorrConfigs gfwConfigs; std::vector corrconfigsPtVn; - TAxis* fPtAxis; + TAxis* fPtAxis = nullptr; TRandom3* fRndm = new TRandom3(0); std::vector> etagapsPtPt; std::vector>> bootstrapArray; @@ -308,14 +333,15 @@ struct FlowTask { double mMinSeconds{-1.}; std::unordered_map gHadronicRate; ctpRateFetcher mRateFetcher; - TH2* gCurrentHadronicRate; + TH2* gCurrentHadronicRate = nullptr; + RCTFlagsChecker rctChecker{"CBT"}; // phi-EP correction std::vector funcEff; - TH1D* hFindPtBin; - TF1* funcV2; - TF1* funcV3; - TF1* funcV4; + TH1D* hFindPtBin = nullptr; + TF1* funcV2 = nullptr; + TF1* funcV3 = nullptr; + TF1* funcV4 = nullptr; void init(InitContext const&) { @@ -339,19 +365,21 @@ struct FlowTask { registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(3, "after supicious Runs removal"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(4, "after additional event cut"); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(5, "after correction loads"); - registry.add("hEventCountSpecific", "Number of Event;; Count", {HistType::kTH1D, {{12, 0, 12}}}); + registry.add("hEventCountSpecific", "Number of Event;; Count", {HistType::kTH1D, {{13, 0, 13}}}); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(1, "after sel8"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(2, "kNoSameBunchPileup"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(3, "kNoITSROFrameBorder"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(5, "kIsGoodZvtxFT0vsPV"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(6, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(7, "kIsGoodITSLayersAll"); + std::string itsLayersFlag[kCount_ITSLayersFlag] = {"ITSLayersAll", "ITSLayers0123", "ITSLayer3"}; + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(7, Form("kIsGood%s", itsLayersFlag[cfgEvSelkIsGoodITSLayersFlag].c_str())); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(8, "kNoCollInRofStandard"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(9, "kNoHighMultCollInPrevRof"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(10, "occupancy"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(11, "MultCorrelation"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(12, "cfgEvSelV0AT0ACut"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(13, "RCTflags"); registry.add("hVtxZ", "Vexter Z distribution", {HistType::kTH1D, {axisVertex}}); registry.add("hMult", "Multiplicity distribution", {HistType::kTH1D, {{3000, 0.5, 3000.5}}}); std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); @@ -396,8 +424,8 @@ struct FlowTask { registry.add("hnTPCClu", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); registry.add("hnITSClu", "Number of found ITS clusters", {HistType::kTH1D, {{100, 0, 20}}}); registry.add("hnTPCCrossedRow", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); - registry.add("hDCAz", "DCAz after cuts; DCAz (cm); Pt", {HistType::kTH2D, {{200, -0.5, 0.5}, {200, 0, 5}}}); - registry.add("hDCAxy", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH2D, {{200, -0.5, 0.5}, {200, 0, 5}}}); + registry.add("hDCAz", "DCAz after cuts; DCAz (cm); Pt", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); + registry.add("hDCAxy", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); registry.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisNch, axisNch}}); registry.add("hMeanPt", "", {HistType::kTProfile, {axisIndependent}}); registry.add("hMeanPtWithinGap08", "", {HistType::kTProfile, {axisIndependent}}); @@ -420,6 +448,13 @@ struct FlowTask { registry.add("hPtA_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); registry.add("hPtB_afterDptCut", "p_{T} distribution", {HistType::kTH1D, {axisPt}}); } + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { + registry.add("ptpt_1sub1_1sub3_2pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub1_1pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub1_2pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub3_1pc", "", {HistType::kTProfile, {axisIndependent}}); + registry.add("pt_1sub3_2pc", "", {HistType::kTProfile, {axisIndependent}}); + } if (doprocessMCGen) { registry.add("MCGen/MChPhi", "#phi distribution", {HistType::kTH1D, {axisPhi}}); registry.add("MCGen/MChEta", "#eta distribution", {HistType::kTH1D, {axisEta}}); @@ -640,10 +675,42 @@ struct FlowTask { // Mask 1: vn-[pT], 3: vn-[pT^2], 7: vn-[pT^3], 15: vn-[pT^4] gfwConfigs.SetpTCorrMasks(cfgUserPtVnCorrConfig->GetpTCorrMasks()); gfwConfigs.Print(); - fFCpt->setUseCentralMoments(cfgUseCentralMoments); - fFCpt->setUseGapMethod(true); - fFCpt->initialise(axisIndependent, cfgMpar, gfwConfigs, cfgNbootstrap); - if (cfgEtaGapPtPtEnabled) { + if (!cfgUserIO.cfgUserPtVnEvWeightEnabled) + fFCpt->setEventWeight(EventWeight::UnityWeight); + else + fFCpt->setEventWeight(EventWeight::TupleWeight); + if (cfgAdditionObs.cfgNewPtContainer) { + cfgAdditionObs.npcRegions.SetNames(cfgAdditionObs.cfgNpcRegions->GetNames()); + cfgAdditionObs.npcRegions.SetEtaMin(cfgAdditionObs.cfgNpcRegions->GetEtaMin()); + cfgAdditionObs.npcRegions.SetEtaMax(cfgAdditionObs.cfgNpcRegions->GetEtaMax()); + cfgAdditionObs.npcRegions.SetpTDifs(cfgAdditionObs.cfgNpcRegions->GetpTDifs()); + cfgAdditionObs.npcRegions.SetBitmasks(cfgAdditionObs.cfgNpcRegions->GetBitmasks()); + cfgAdditionObs.npcPtVnCorrConfig.SetCorrs(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetCorrs()); + cfgAdditionObs.npcPtVnCorrConfig.SetHeads(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetHeads()); + cfgAdditionObs.npcPtVnCorrConfig.SetpTDifs(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetpTDifs()); + cfgAdditionObs.npcPtVnCorrConfig.SetpTCorrMasks(cfgAdditionObs.cfgNpcPtVnCorrConfig->GetpTCorrMasks()); + cfgAdditionObs.npcRegions.Print(); + cfgAdditionObs.npcPtVnCorrConfig.Print(); + fFCpt->initialise(axisIndependent, cfgAdditionObs.npcRegions, cfgAdditionObs.npcPtVnCorrConfig, cfgNbootstrap); + + for (int region = 0; region < cfgAdditionObs.npcRegions.GetSize(); ++region) { + cfgAdditionObs.npcGfw.AddRegion(cfgAdditionObs.npcRegions.GetNames()[region], cfgAdditionObs.npcRegions.GetEtaMin()[region], + cfgAdditionObs.npcRegions.GetEtaMax()[region], 1, + cfgAdditionObs.npcRegions.GetBitmasks()[region]); + } + std::vector npcGFWCorr = cfgAdditionObs.cfgNpcGFWCorr; + if (!npcGFWCorr.empty()) { + for (uint i = 0; i < npcGFWCorr.size(); i++) { + cfgAdditionObs.npcGfw.GetCorrelatorConfig(npcGFWCorr.at(i).c_str()); + } + } + cfgAdditionObs.npcGfw.CreateRegions(); + } else { + fFCpt->setUseCentralMoments(cfgUseCentralMoments); + fFCpt->setUseGapMethod(true); + fFCpt->initialise(axisIndependent, cfgMpar, gfwConfigs, cfgNbootstrap); + } + if (!cfgAdditionObs.cfgNewPtContainer && cfgEtaGapPtPtEnabled) { for (int i = 0; i < 4; ++i) { // o2-linter: disable=magic-number (maximum of 4 subevents) if (cfgPtPtGaps->getData()[i][0] < -1. || cfgPtPtGaps->getData()[i][1] < -1.) continue; @@ -660,6 +727,10 @@ struct FlowTask { if (doprocessMCGen) { fFCptgen->setUseCentralMoments(cfgUseCentralMoments); fFCptgen->setUseGapMethod(true); + if (!cfgUserIO.cfgUserPtVnEvWeightEnabled) + fFCptgen->setEventWeight(EventWeight::UnityWeight); + else + fFCptgen->setEventWeight(EventWeight::TupleWeight); fFCptgen->initialise(axisIndependent, cfgMpar, gfwConfigs, cfgNbootstrap); if (cfgEtaGapPtPtEnabled) fFCptgen->initialiseSubevent(axisIndependent, cfgMpar, etagapsPtPt.size(), cfgNbootstrap); @@ -721,18 +792,44 @@ struct FlowTask { funcV4->SetParameters(0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10); } - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs]) { + readMatrix(cfgTrackCuts->getData(), trackCuts); + if (trackCuts[kDCAxyNSigma][kTrCutObs]) { cfgFuncParas.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgFuncParas.cfgDCAxyFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAxy->SetParameter(0, cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs]); - LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs], cfgFuncParas.cfgDCAxyFunc->c_str())); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[kDCAxyNSigma][kTrCutObs]); + LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", trackCuts[kDCAxyNSigma][kTrCutObs], cfgFuncParas.cfgDCAxyFunc->c_str())); } - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch]) { + if (trackCuts[kDCAxyNSigma][kTrCutNch]) { cfgFuncParas.fPtDepDCAxyForNch = new TF1("ptDepDCAxyForNch", Form("[0]*%s", cfgFuncParas.cfgDCAxyFunc->c_str()), 0.001, 1000); - cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch]); - LOGF(info, "DCAxy pt-dependence function for Nch: %s", Form("%0.1f * %s", cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch], cfgFuncParas.cfgDCAxyFunc->c_str())); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[kDCAxyNSigma][kTrCutNch]); + LOGF(info, "DCAxy pt-dependence function for Nch: %s", Form("%0.1f * %s", trackCuts[kDCAxyNSigma][kTrCutNch], cfgFuncParas.cfgDCAxyFunc->c_str())); + } + if (trackCuts[kDCAzNSigma][kTrCutObs]) { + cfgFuncParas.fPtDepDCAz = new TF1("ptDepDCAz", Form("[0]*%s", cfgFuncParas.cfgDCAzFunc->c_str()), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[kDCAzNSigma][kTrCutObs]); + LOGF(info, "DCAz pt-dependence function: %s", Form("%0.1f * %s", trackCuts[kDCAzNSigma][kTrCutObs], cfgFuncParas.cfgDCAzFunc->c_str())); + } + if (trackCuts[kDCAzNSigma][kTrCutNch]) { + cfgFuncParas.fPtDepDCAzForNch = new TF1("ptDepDCAzForNch", Form("[0]*%s", cfgFuncParas.cfgDCAzFunc->c_str()), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[kDCAzNSigma][kTrCutNch]); + LOGF(info, "DCAz pt-dependence function for Nch: %s", Form("%0.1f * %s", trackCuts[kDCAzNSigma][kTrCutNch], cfgFuncParas.cfgDCAzFunc->c_str())); + } + if (!cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSelRCTflags.value.c_str()); // override initialzation } } + template + void readMatrix(Array2D const& mat, P& array) + { + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; + } + void createOutputObjectsForRun(int runNumber) { const AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; @@ -821,7 +918,7 @@ struct FlowTask { } template - inline void fillPtSums(TTrack track, float weff) + inline void fillPtSums(const TTrack& track, float weff) { if (std::abs(track.eta()) < cfgEtaVnPt) { (dt == kGen) ? fFCptgen->fill(1., track.pt()) : fFCpt->fill(weff, track.pt()); @@ -870,30 +967,30 @@ struct FlowTask { if (cfgUserIO.cfgAcceptance.value.empty() == false) { mAcceptance = ccdb->getForTimeStamp(cfgUserIO.cfgAcceptance, timestamp); if (mAcceptance) - LOGF(info, "Loaded acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(info, "Loaded acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), static_cast(mAcceptance)); else - LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgUserIO.cfgAcceptance.value.c_str(), static_cast(mAcceptance)); } if (cfgUserIO.cfgEfficiency.value.empty() == false) { mEfficiency = ccdb->getForTimeStamp(cfgUserIO.cfgEfficiency, timestamp); if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgUserIO.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } if (cfgUserIO.cfgEfficiencyForNch.value.empty() == false) { mEfficiencyForNch = ccdb->getForTimeStamp(cfgUserIO.cfgEfficiencyForNch, timestamp); if (mEfficiencyForNch == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgUserIO.cfgEfficiencyForNch.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiencyForNch.value.c_str(), (void*)mEfficiencyForNch); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgUserIO.cfgEfficiencyForNch.value.c_str(), static_cast(mEfficiencyForNch)); } if (cfgAdditionObs.cfgDptDisEnable && cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.empty() == false) { cfgAdditionObs.hEvAvgMeanPt = ccdb->getForTimeStamp(cfgAdditionObs.cfgDptDishEvAvgMeanPt, timestamp); if (cfgAdditionObs.hEvAvgMeanPt == nullptr) { LOGF(fatal, "Could not load mean pT histogram from %s", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str()); } - LOGF(info, "Loaded mean pT histogram from %s (%p)", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str(), (void*)cfgAdditionObs.hEvAvgMeanPt); + LOGF(info, "Loaded mean pT histogram from %s (%p)", cfgAdditionObs.cfgDptDishEvAvgMeanPt.value.c_str(), static_cast(cfgAdditionObs.hEvAvgMeanPt)); } if (cfgAdditionObs.cfgDptDisEnable && cfgAdditionObs.cfgDptDisSelectionSwitch > kNoDptCut) { if (cfgAdditionObs.cfgDptDisCutLow.value.empty() == false) { @@ -901,14 +998,14 @@ struct FlowTask { if (cfgAdditionObs.fDptDisCutLow == nullptr) { LOGF(fatal, "Could not load dptDis low cut histogram from %s", cfgAdditionObs.cfgDptDisCutLow.value.c_str()); } - LOGF(info, "Loaded dptDis low cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutLow.value.c_str(), (void*)cfgAdditionObs.fDptDisCutLow); + LOGF(info, "Loaded dptDis low cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutLow.value.c_str(), static_cast(cfgAdditionObs.fDptDisCutLow)); } if (cfgAdditionObs.cfgDptDisCutHigh.value.empty() == false) { cfgAdditionObs.fDptDisCutHigh = ccdb->getForTimeStamp(cfgAdditionObs.cfgDptDisCutHigh, timestamp); if (cfgAdditionObs.fDptDisCutHigh == nullptr) { LOGF(fatal, "Could not load dptDis high cut histogram from %s", cfgAdditionObs.cfgDptDisCutHigh.value.c_str()); } - LOGF(info, "Loaded dptDis high cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutHigh.value.c_str(), (void*)cfgAdditionObs.fDptDisCutHigh); + LOGF(info, "Loaded dptDis high cut histogram from %s (%p)", cfgAdditionObs.cfgDptDisCutHigh.value.c_str(), static_cast(cfgAdditionObs.fDptDisCutHigh)); } } correctionsLoaded = true; @@ -946,7 +1043,7 @@ struct FlowTask { } template - bool eventSelected(TCollision collision, const int multTrk, const float centrality) + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality) { registry.fill(HIST("hEventCountSpecific"), 0.5); if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -979,12 +1076,17 @@ struct FlowTask { } if (cfgEvSelkNoCollInTimeRangeStandard) registry.fill(HIST("hEventCountSpecific"), 5.5); - if (cfgEvSelkIsGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (cfgEvSelkIsGoodITSLayers) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + if (cfgEvSelkIsGoodITSLayersFlag == kITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + return 0; + if (cfgEvSelkIsGoodITSLayersFlag == kITSLayer0123 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) + return 0; + if (cfgEvSelkIsGoodITSLayersFlag == kITSLayer3 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer3)) + return 0; } - if (cfgEvSelkIsGoodITSLayersAll) + if (cfgEvSelkIsGoodITSLayers) registry.fill(HIST("hEventCountSpecific"), 6.5); if (cfgEvSelkNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold @@ -1041,6 +1143,11 @@ struct FlowTask { if (cfgFuncParas.cfgEvSelV0AT0ACut) registry.fill(HIST("hEventCountSpecific"), 11.5); + if (!cfgEvSelRCTflags.value.empty() && !rctChecker(*collision)) + return 0; + if (!cfgEvSelRCTflags.value.empty()) + registry.fill(HIST("hEventCountSpecific"), 12.5); + return 1; } @@ -1059,23 +1166,27 @@ struct FlowTask { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutObs] && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) + if (trackCuts[kDCAxyNSigma][kTrCutObs] && cfgFuncParas.fPtDepDCAxy != nullptr && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) return false; - return ((track.tpcNClsFound() >= cfgTrackCuts->getData()[kTPCclu][kTrCutObs]) && (track.tpcNClsCrossedRows() >= cfgTrackCuts->getData()[kTPCCrossedRows][kTrCutObs]) && (track.itsNCls() >= cfgTrackCuts->getData()[kITSclu][kTrCutObs]) && (track.tpcChi2NCl() < cfgTrackCuts->getData()[kChi2prTPCcls][kTrCutObs]) && (std::fabs(track.dcaZ()) < cfgTrackCuts->getData()[kDCAz][kTrCutObs])); + if (trackCuts[kDCAzNSigma][kTrCutObs] && cfgFuncParas.fPtDepDCAz != nullptr && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt()))) + return false; + return ((track.tpcNClsFound() >= trackCuts[kTPCclu][kTrCutObs]) && (track.tpcNClsCrossedRows() >= trackCuts[kTPCCrossedRows][kTrCutObs]) && (track.itsNCls() >= trackCuts[kITSclu][kTrCutObs]) && (track.tpcChi2NCl() < trackCuts[kChi2prTPCcls][kTrCutObs]) && (std::fabs(track.dcaZ()) < trackCuts[kDCAz][kTrCutObs])); } template - bool trackSelectedForNch(TTrack track) + bool trackSelectedForNch(const TTrack& track) { - if (cfgTrackCuts->getData()[kDCAxyNSigma][kTrCutNch] && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) + if (trackCuts[kDCAxyNSigma][kTrCutNch] && cfgFuncParas.fPtDepDCAxyForNch != nullptr && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) + return false; + if (trackCuts[kDCAzNSigma][kTrCutNch] && cfgFuncParas.fPtDepDCAzForNch != nullptr && (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt()))) return false; - return ((track.tpcNClsFound() >= cfgTrackCuts->getData()[kTPCclu][kTrCutNch]) && (track.tpcNClsCrossedRows() >= cfgTrackCuts->getData()[kTPCCrossedRows][kTrCutNch]) && (track.itsNCls() >= cfgTrackCuts->getData()[kITSclu][kTrCutNch]) && (track.tpcChi2NCl() < cfgTrackCuts->getData()[kChi2prTPCcls][kTrCutNch]) && (std::fabs(track.dcaZ()) < cfgTrackCuts->getData()[kDCAz][kTrCutNch])); + return ((track.tpcNClsFound() >= trackCuts[kTPCclu][kTrCutNch]) && (track.tpcNClsCrossedRows() >= trackCuts[kTPCCrossedRows][kTrCutNch]) && (track.itsNCls() >= trackCuts[kITSclu][kTrCutNch]) && (track.tpcChi2NCl() < trackCuts[kChi2prTPCcls][kTrCutNch]) && (std::fabs(track.dcaZ()) < trackCuts[kDCAz][kTrCutNch])); } template - bool rejectionTPCoverlap(TTrack track, const int field) + bool rejectionTPCoverlap(const TTrack& track, const int field) { double phimodn = track.phi(); if (field < 0) // for negative polarity field @@ -1202,6 +1313,9 @@ struct FlowTask { fGFW->Clear(); fFCpt->clearVector(); cfgAdditionObs.nPt.clear(); + if (cfgAdditionObs.cfgNewPtContainer) { + cfgAdditionObs.npcGfw.Clear(); + } if (cfgGetInteractionRate) { initHadronicRate(bc); double hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), mRunNumber, "ZNC hadronic") * 1.e-3; // @@ -1235,8 +1349,13 @@ struct FlowTask { double weffEventWithinGap08 = 0., weffEventSquareWithinGap08 = 0.; double sumPtsquareWsquareWithinGap08 = 0., sumPtWsquareWithinGap08 = 0.; double nTracksCorrected = 0; + double mpt_sub1 = 0; + double mpt_sub1_weffEv = 0; + double mpt_sub3 = 0; + double mpt_sub3_weffEv = 0; int magnetfield = 0; float independent = cent; + float nTracksUncorrected = 0; if (cfgUseNch) independent = static_cast(tracks.size()); if (cfgFuncParas.cfgShowTPCsectorOverlap) { @@ -1280,6 +1399,7 @@ struct FlowTask { for (const auto& track : tracks) { if (trackSelectedForNch(track) && setNchEffWeights(weffForNch, track.pt())) { nTracksCorrected += weffForNch; + nTracksUncorrected++; } if (!trackSelected(track)) continue; @@ -1349,6 +1469,16 @@ struct FlowTask { weffEventWithinGap08 += weff; weffEventSquareWithinGap08 += weff * weff; } + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { + if (track.eta() >= etagapsPtPt[0].first && track.eta() <= etagapsPtPt[0].second) { + mpt_sub1 += weff * track.pt(); + mpt_sub1_weffEv += weff; + } + if (track.eta() >= etagapsPtPt[2].first && track.eta() <= etagapsPtPt[2].second) { + mpt_sub3 += weff * track.pt(); + mpt_sub3_weffEv += weff; + } + } } if (cfgUserIO.cfgConsistentEventFlag && consistentEventVector.size() == 6) { // o2-linter: disable=magic-number (size match) acceptedTracks.nFull += 1; @@ -1365,12 +1495,25 @@ struct FlowTask { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 2); if (withinPtPOI && withinPtRef) fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 4); - if (cfgUsePtRef && withinPtRef) - fillPtSums(track, weff); - if (!cfgUsePtRef && withinPtPOI) - fillPtSums(track, weff); + if (cfgAdditionObs.cfgNewPtContainer) { + if (cfgUsePtRef && withinPtRef) + fFCpt->fill(track.eta(), weff, track.pt(), 1); + if (!cfgUsePtRef && withinPtPOI) + fFCpt->fill(track.eta(), weff, track.pt(), 1); + if (withinPtRef) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 1); + if (withinPtPOI) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 2); + if (withinPtPOI && withinPtRef) + cfgAdditionObs.npcGfw.Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), wacc * weff, 4); + } else { + if (cfgUsePtRef && withinPtRef) + fillPtSums(track, weff); + if (!cfgUsePtRef && withinPtPOI) + fillPtSums(track, weff); + } } - registry.fill(HIST("hTrackCorrection2d"), tracks.size(), nTracksCorrected); + registry.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); if (cfgUseNch && cfgUseNchCorrected) { independent = nTracksCorrected; } @@ -1406,7 +1549,7 @@ struct FlowTask { } registry.fill(HIST("hNormDeltaPt_X_afterCut"), normDeltaPt, independent, weffEvent); if (ptEtaVec.size() > 0) { - for (auto trptEta : ptEtaVec) { + for (auto const& trptEta : ptEtaVec) { registry.fill(HIST("hPt_afterDptCut"), trptEta.first); if (trptEta.second < -1. * cfgAdditionObs.cfgDptDisEtaGapQA) registry.fill(HIST("hPtA_afterDptCut"), trptEta.first); @@ -1421,13 +1564,12 @@ struct FlowTask { if (weffEvent) { registry.fill(HIST("hMeanPt"), independent, ptSum / weffEvent, weffEvent); } - if (weffEventWithinGap08) - registry.fill(HIST("hMeanPtWithinGap08"), independent, ptSum_Gap08 / weffEventWithinGap08, 1.0); - if (weffEventWithinGap08) + if (weffEventWithinGap08) { + registry.fill(HIST("hMeanPtWithinGap08"), independent, ptSum_Gap08 / weffEventWithinGap08, cfgUserIO.cfgUserPtVnEvWeightEnabled ? weffEventWithinGap08 : 1.); bootstrapArray[sampleIndex][kMeanPtWithinGap08]->Fill(independent, ptSum_Gap08 / weffEventWithinGap08, 1.0); - // c22_gap8 * pt_withGap8 - if (weffEventWithinGap08) + // c22_gap8 * pt_withGap8 fillpTvnProfile(corrconfigs.at(7), ptSum_Gap08, weffEventWithinGap08, HIST("c22_gap08_Weff"), HIST("c22_gap08_trackMeanPt"), independent); + } // PtVariance if (weffEventDiffWithGap08) { registry.fill(HIST("PtVariance_partA_WithinGap08"), independent, @@ -1438,6 +1580,25 @@ struct FlowTask { weffEventDiffWithGap08); } + if (!cfgAdditionObs.cfgNewPtContainer && cfgAdditionObs.cfgManualCk) { + // 2pc event weight: + double ev2pc = mpt_sub1_weffEv * mpt_sub3_weffEv; + // manual pt-pt variance + if (mpt_sub1_weffEv) { + mpt_sub1 = mpt_sub1 / mpt_sub1_weffEv; + registry.fill(HIST("pt_1sub1_1pc"), independent, mpt_sub1, cfgUserIO.cfgUserPtVnEvWeightEnabled ? mpt_sub1_weffEv : 1.); + } + if (mpt_sub3_weffEv) { + mpt_sub3 = mpt_sub3 / mpt_sub3_weffEv; + registry.fill(HIST("pt_1sub3_1pc"), independent, mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? mpt_sub3_weffEv : 1.); + } + if (mpt_sub1_weffEv && mpt_sub3_weffEv) { + registry.fill(HIST("ptpt_1sub1_1sub3_2pc"), independent, mpt_sub1 * mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + registry.fill(HIST("pt_1sub1_2pc"), independent, mpt_sub1, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + registry.fill(HIST("pt_1sub3_2pc"), independent, mpt_sub3, cfgUserIO.cfgUserPtVnEvWeightEnabled ? ev2pc : 1.); + } + } + // v02 if (cfgAdditionObs.cfgV02Enabled && weffEventFrac > 0) { for (auto ipt = 0; ipt < fPtAxis->GetNbins(); ipt++) { @@ -1452,7 +1613,11 @@ struct FlowTask { fillFC(corrconfigs.at(l_ind), independent, lRandom); } // Filling pt Container - fillPtContainers(independent, lRandom); + if (cfgAdditionObs.cfgNewPtContainer) { + fFCpt->fillPtProfiles(cfgAdditionObs.npcGfw, independent, lRandom); + } else { + fillPtContainers(independent, lRandom); + } } PROCESS_SWITCH(FlowTask, processData, "Process analysis for non-derived data", true); @@ -1513,9 +1678,8 @@ struct FlowTask { ptSum_Gap08 += mcParticle.pt(); count_Gap08 += 1.; } - } - if (withinPtRef) fGFW->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), 1., 1); + } if (withinPtPOI) fGFW->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), 1., 2); if (withinPtPOI && withinPtRef) @@ -1527,7 +1691,7 @@ struct FlowTask { } if (count_Gap08 > 0) - registry.fill(HIST("hMeanPtWithinGap08_MC"), independent, ptSum_Gap08 / count_Gap08, 1.0); + registry.fill(HIST("hMeanPtWithinGap08_MC"), independent, ptSum_Gap08 / count_Gap08, cfgUserIO.cfgUserPtVnEvWeightEnabled ? count_Gap08 : 1.); int sampleIndex = static_cast(cfgNbootstrap * lRandom); if (count_Gap08 > 0) bootstrapArray[sampleIndex][kMeanPtWithinGap08_MC]->Fill(independent, ptSum_Gap08 / count_Gap08, 1.0); diff --git a/PWGCF/Flow/Tasks/flowTransmutation.cxx b/PWGCF/Flow/Tasks/flowTransmutation.cxx new file mode 100644 index 00000000000..fa0fe8210f4 --- /dev/null +++ b/PWGCF/Flow/Tasks/flowTransmutation.cxx @@ -0,0 +1,636 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowTransmutation.cxx +/// \author Vojtech Machacek (vojtech.machacek@cern.ch) +/// \since Oct/05/2026 +/// \brief Task to measure flow observables with cumulants method in transmuted events. + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) +static constexpr std::array, 7> LongArrayFloat = {{{{2.5, 2.5}}, {{50.0, 50.0}}, {{70.0, 70.0}}, {{5.0, 5.0}}, {{7.0, 7.0}}, {{7.0, 7.0}}, {{0., 0.}}}}; +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + return; +} +struct FlowTransmutation { + struct : ConfigurableGroup{ + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT") + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "CBT", "keep empty to disable, usage: 'CBT', 'CBT_hadronPID' ") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersFlag, int, 0, "0: Disabled; 1: kIsGoodITSLayersAll; 2: kIsGoodITSLayer0123; 3: kIsGoodITSLayer3") + O2_DEFINE_CONFIGURABLE(cfgEvSelUsage, bool, true, "Use event selection cuts: (0/1)") + O2_DEFINE_CONFIGURABLE(cfgEvSel8, bool, true, "rejects collisions which are not selected by sel8") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, true, "rejects collisions which are associated with the same found-by-T0 bunch crossing") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, true, "reject events at ITS ROF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, true, "reject events at TF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, true, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, true, "no collisions in specified time range") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, true, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, true, "veto an event if FT0C amplitude in previous ITS ROF is above threshold")} cfgEventSelection; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentCutEnabled, bool, true, "Enable global multiplicity vs centrality cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs centrality lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs centrality upper cut"); + Configurable> cfgGlobalMultCentCutPars{"cfgGlobalMultCentCutPars", std::vector{177.409, -5.06256, 0.0695392, -0.000539089, 0.00000177447, 27.3677, -0.427405, 0.00452115, -0.0000382143, 1.13518e-7}, "Global multiplicity vs centrality cut values"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultCutEnabled, bool, true, "Enable global multiplicity vs PV multiplicity cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for global multiplicity vs PV multiplicity correlation lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultPVMultHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for global multiplicity vs PV multiplicity correlation upper cut"); + Configurable> cfgGlobalMultPVMultCutPars{"cfgGlobalMultPVMultCutPars", std::vector{-0.0893236, 0.721511, 2.05725, 0.0244317}, "global multiplicity vs PV multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultCutEnabled, bool, true, "Enable global multiplicity vs V0A multiplicity cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs V0A multiplicity correlation lower cut"); + O2_DEFINE_CONFIGURABLE(cfgGlobalMultV0AMultHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for global multiplicity vs V0A multiplicity correlation upper cut"); + Configurable> cfgGlobalMultV0AMultCutPars{"cfgGlobalMultV0AMultCutPars", std::vector{485.385, 191.561, 0.296174, -0.00282594, 0.00000497112, 370.073, 102.096, -1.00597, 0.00405269, -0.00000520521}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgPVMultCentCutEnabled, bool, true, "Enable primary vertex multiplicity vs centrality cut"); + O2_DEFINE_CONFIGURABLE(cfgPVMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for primary vertex multiplicity vs centrality upper cut"); + O2_DEFINE_CONFIGURABLE(cfgPVMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for primary vertex multiplicity vs centrality lower cut"); + Configurable> cfgPVMultCentCutCutPars{"cfgPVMultCentCutPars", std::vector{245.882, -6.90255, 0.0930817, -0.000710842, 0.00000231095, 35.9647, -0.530632, 0.00513954, -0.0000411738, 1.10106e-7}, "Primary vertex multiplicity vs centrality cut parameter values"}; + } cfgMultiplicityCorrelationCut; + O2_DEFINE_CONFIGURABLE(cfgNUAWeightsRunByRun, bool, true, "Fill NUA weights run by run") + O2_DEFINE_CONFIGURABLE(cfgGetNumberOfCorr, bool, false, "Get histograms with number of correlations") + Configurable> cfgFirstFillRunsList{"cfgFirstFillRunsList", std::vector{}, "First run of the fill for time-dependent analysis, keep empty to disable"}; + O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "Maximal pT for all tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for all tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 3.0f, "Maximal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "Minimal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "function for pT dependent DCAxy cut, keep empty for const. cut based on DCAxy (Nsigma)") + O2_DEFINE_CONFIGURABLE(cfgCutDCAz, std::string, "(0.0026+0.005/(x^1.01))", "function for pT dependent DCAz cut, keep empty for const. cut based on DCAz (Nsigma)") + Configurable> cfgRemovedRunList{"cfgRemovedRunList", std::vector{-1}, "excluded run numbers"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 7, 2, {"chi2 per TPCcls", "TPC cluster", "TPC crossed rows", "ITS cluster", "DCAxy (Nsigma)", "DCAz (Nsigma)", "DCAz (cm) override"}, {"Nch", "Observable"}}, "separate Nch and observable track selections"}; + ConfigurableAxis binningNch{"binningNch", {500, 0, 500}, "binning for N_{ch} histograms"}; + ConfigurableAxis binningTime{"binningTime", {12, 0, 12}, "binning for time-dependent histograms transmutation QA"}; + ConfigurableAxis binningPt{"binningPt", {VARIABLE_WIDTH, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}, "binning for pT histograms"}; + HistogramRegistry registryQA{"registryQA"}; + HistogramRegistry registryCorr{"registryCorr"}; + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEventSelection.cfgCutVertex); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax); + Service ccdb{}; + RCTFlagsChecker rctChecker{"CBT"}; + enum TrackSelectionGroup { + TrkSelNch = 0, + TrkSelObs, + TrkCounter + }; + enum TrackCut { + Chi2prTPCcls = 0, // max chi2 per TPC clusters + TPCclu, // minimum TPC found clusters + TPCCrossedRows, // minimum TPC crossed rows + ITSclu, // minimum ITS found clusters + DCAxyNsigma, // cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track; set cfgCutDCAxy to "1" to change the cut to a fixed one + DCAzNsigma, // cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track; set cfgCutDCAxy to "1" to change the cut to a fixed one + DCAz, // constant DCAz cut, set to 0 to override with pT dependent one + Count_TrackCut + }; + enum CentEstimators { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT, + // Count the total number of enum + Count_CentEstimators + }; + enum GoodITSLayersFlag { + Disabled = 0, + ITSLayersAll, + ITSLayer0123, + ITSLayer3, + Count_ITSLayersFlag + }; + enum EventSelectionCuts { + FilteredEvent = 1, + Sel8, + RCTflags, + IsGoodITSLayers, + NoSameBunchPileup, + NoITSROFrameBorder, + NoTimeFrameBorder, + IsGoodZvtxFT0vsPV, + NoCollInTimeRangeStandard, + NoCollInRofStandard, + NoHighMultCollInPrevRof, + MultCorrelation, + BadRunsRemoved + }; + enum EtaGapsSettings { + NoGap = -1, + Gap00, + Gap02, + Gap04, + Gap06, + Gap08, + Gap10, + Gap12, + Gap14, + EtaGapCounter + }; + enum QAFillTime { + BeforeSel, + AfterSel + }; + struct EventCut { + bool enabled; + int histBin; + int flag; // just store the enum + }; + struct TrackRegion { + float forward; + float backward; + float gap; + }; + static constexpr std::array FillTimeName = {"beforeEventSelQA/", "afterEventSelQA/"}; + static constexpr std::array FillTrackName = {"cutNch/", "cutObs/"}; + std::array eventSelectionCuts{}; + std::array, 7> trackSel{}; + TF1* fGlobalMultPVMultLowCut = nullptr; + TF1* fGlobalMultPVMultHighCut = nullptr; + TF1* fGlobalMultV0AMultLowCut = nullptr; + TF1* fGlobalMultV0AMultHighCut = nullptr; + TF1* fGlobalMultCentLowCut = nullptr; + TF1* fGlobalMultCentHighCut = nullptr; + TF1* fPVMultCentLowCut = nullptr; + TF1* fPVMultCentHighCut = nullptr; + TH1D* hEff = nullptr; + std::array fDCAzCut{}; + std::array fDCAxyCut{}; + std::vector runNumbers; + std::map> th3sPerRun; + int firstRunOfFill = -1; + int getCorrectITSLayersFlag(int flag) + { + switch (flag) { + case ITSLayersAll: + return o2::aod::evsel::kIsGoodITSLayersAll; + case ITSLayer0123: + return o2::aod::evsel::kIsGoodITSLayer0123; + case ITSLayer3: + return o2::aod::evsel::kIsGoodITSLayer3; + default: + return 0; // Disabled + } + } + void init(InitContext const&) + { + AxisSpec axisVertex{40, -20, 20, "Vtxz (cm)"}; + AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; + AxisSpec axisEta{40, -1., 1., "#eta"}; + AxisSpec axisCent{100, 0., 100., "Centrality"}; + AxisSpec axisT0C = {1000, 0, 50000, "N_{ch} (T0C)"}; + AxisSpec axisT0A = {1800, 0, 180000, "N_{ch} (T0A)"}; + AxisSpec axisV0A = {1800, 0, 180000, "N_{ch} (V0A)"}; + AxisSpec axisNch = {binningNch, "N_{ch}"}; + AxisSpec axisPt = {binningPt, "p_{T} (GeV/c)"}; + registryQA.add("cutNch/hTrackPhi", "Track #varphi distribution;#varphi;Count", {HistType::kTH1D, {axisPhi}}); + registryQA.add("cutNch/hTrackEta", "Track #eta distribution;#eta;Count", {HistType::kTH1D, {axisEta}}); + registryQA.add("cutNch/hPt", "p_{T} distribution for reconstructed particles in a given pT range;p_{T};Counts", {HistType::kTH1D, {axisPt}}); + registryQA.add("cutNch/hChi2prTPCcls", "#chi^{2}/cluster for the TPC track segment;#chi^{2}/cluster;Counts", {HistType::kTH1D, {{100, 0., 5.}}}); + registryQA.add("cutNch/hChi2prITScls", "#chi^{2}/cluster for the ITS track;#chi^{2}/cluster;Counts", {HistType::kTH1D, {{100, 0., 50.}}}); + registryQA.add("cutNch/hnTPCClu", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); + registryQA.add("cutNch/hnITSClu", "Number of found ITS clusters", {HistType::kTH1D, {{100, 0, 20}}}); + registryQA.add("cutNch/hnTPCCrossedRow", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); + registryQA.add("cutNch/hDCAz", "DCAz after cuts; DCAz (cm); p_{T}", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); + registryQA.add("cutNch/hDCAxy", "DCAxy after cuts; DCAxy (cm); p_{T}", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); + registryQA.add("cutNch/hPhiEtaVtxZ", ";#varphi;#eta;v_{z}", {HistType::kTH3D, {axisPhi, {32, -0.8, 0.8}, {40, -10, 10}}}); + registryQA.addClone("cutNch/", "cutObs/"); + registryQA.add("hVtxZ", "Vertex Z distribution;Vtxz (cm);Count", {HistType::kTH1D, {axisVertex}}); + registryQA.add("hCentrality", "Centrality distribution;Centrality;Count", {HistType::kTH1D, {axisCent}}); + registryQA.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{13, 0.5, 13.5}}}); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvent, "Filtered event"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(Sel8, "after sel8"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(RCTflags, "RCTflags"); + std::array itsLayersFlag = {"ITSLayers-Disabled", "ITSLayersAll", "ITSLayers0123", "ITSLayer3"}; + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodITSLayers, Form("kIsGood%s", itsLayersFlag[cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag].c_str())); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoSameBunchPileup, "kNoSameBunchPileup"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoITSROFrameBorder, "kNoITSROFrameBorder"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoTimeFrameBorder, "kNoTimeFrameBorder"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV, "kIsGoodZvtxFT0vsPV"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard, "kNoCollInTimeRangeStandard"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoCollInRofStandard, "kNoCollInRofStandard"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(NoHighMultCollInPrevRof, "kNoHighMultCollInPrevRof"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(MultCorrelation, "MultCorrelation"); + registryQA.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(BadRunsRemoved, "BadRunsRemoved"); + registryQA.add("beforeEventSelQA/globalTracks_centT0C", "before event selection;FT0C centrality (%);mulplicity global tracks", {HistType::kTH2D, {axisCent, axisNch}}); + registryQA.add("beforeEventSelQA/PVTracks_centT0C", "before event selection;FT0C centrality (%);mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_PVTracks", "before event selection;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisNch, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_multT0A", "before event selection;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisNch}}); + registryQA.add("beforeEventSelQA/globalTracks_multV0A", "before event selection;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisNch}}); + registryQA.add("beforeEventSelQA/multV0A_multT0A", "before event selection;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + registryQA.add("beforeEventSelQA/multT0C_centT0C", "before event selection;FT0C centrality (%);mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + if (cfgEventSelection.cfgEvSelUsage) { + registryQA.addClone("beforeEventSelQA/", "afterEventSelQA/"); + } + eventSelectionCuts = { + EventCut({.enabled = (cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag != Disabled), .histBin = IsGoodITSLayers, .flag = getCorrectITSLayersFlag(cfgEventSelection.cfgEvSelkIsGoodITSLayersFlag)}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoSameBunchPileup, .histBin = NoSameBunchPileup, .flag = o2::aod::evsel::kNoSameBunchPileup}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoITSROFrameBorder, .histBin = NoITSROFrameBorder, .flag = o2::aod::evsel::kNoITSROFrameBorder}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoTimeFrameBorder, .histBin = NoTimeFrameBorder, .flag = o2::aod::evsel::kNoTimeFrameBorder}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkIsGoodZvtxFT0vsPV, .histBin = IsGoodZvtxFT0vsPV, .flag = o2::aod::evsel::kIsGoodZvtxFT0vsPV}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoCollInTimeRangeStandard, .histBin = NoCollInTimeRangeStandard, .flag = o2::aod::evsel::kNoCollInTimeRangeStandard}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoCollInRofStandard, .histBin = NoCollInRofStandard, .flag = o2::aod::evsel::kNoCollInRofStandard}), + EventCut({.enabled = cfgEventSelection.cfgEvSelkNoHighMultCollInPrevRof, .histBin = NoHighMultCollInPrevRof, .flag = o2::aod::evsel::kNoHighMultCollInPrevRof})}; + if (cfgEventSelection.cfgEvSelUsage) { + LOGF(info, "Event selection cuts are enabled"); + LOGF(info, "Sel8 is %senabled", (cfgEventSelection.cfgEvSel8) ? "" : "not "); + if (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEventSelection.cfgEvSelRCTflags.value); + LOGF(info, "RCT is %senabled", (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) ? "" : "not "); + LOGF(info, "RCT selection flag: %s", cfgEventSelection.cfgEvSelRCTflags.value.c_str()); + } + for (const auto& cut : eventSelectionCuts) { + LOGF(info, "Flag %d is %senabled", cut.histBin, (cut.enabled) ? "" : "not "); + } + if (cfgMultiplicityCorrelationCut.cfgEvSelMultCorrelation) { + LOGF(info, "Multiplicity correlation cuts are enabled"); + if (cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutEnabled) { + LOGF(info, "Global multiplicity vs PV multiplicity cut is enabled: %f", axisNch.binEdges.back()); + fGlobalMultPVMultHighCut = new TF1("fGlobalMultPVMultHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultHighCutFunction->c_str(), 0, axisNch.binEdges.back()); + fGlobalMultPVMultHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.data()); + fGlobalMultPVMultLowCut = new TF1("fGlobalMultPVMultLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultLowCutFunction->c_str(), 0, axisNch.binEdges.back()); + fGlobalMultPVMultLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.data()); + LOGF(info, "Global multiplicity vs PV multiplicity Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs PV multiplicity Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs PV multiplicity cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultPVMultLowCut par %zu = %g", i, fGlobalMultPVMultLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultPVMultHighCut par %zu = %g", i, fGlobalMultPVMultHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutEnabled) { + LOGF(info, Form("Global multiplicity vs V0A multiplicity cut is enabled: %f ", axisV0A.binEdges.back())); + + fGlobalMultV0AMultHighCut = new TF1("fGlobalMultV0AMultHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultHighCutFunction->c_str(), 0, axisV0A.binEdges.back()); + fGlobalMultV0AMultHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.data()); + fGlobalMultV0AMultLowCut = new TF1("fGlobalMultV0AMultLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultLowCutFunction->c_str(), 0, axisV0A.binEdges.back()); + fGlobalMultV0AMultLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.data()); + LOGF(info, "Global multiplicity vs V0A multiplicity Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs V0A multiplicity Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs V0A multiplicity cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultV0AMultLowCut par %zu = %g", i, fGlobalMultV0AMultLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultV0AMultHighCut par %zu = %g", i, fGlobalMultV0AMultHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutEnabled) { + LOGF(info, "Global multiplicity vs centrality cut is enabled"); + fGlobalMultCentHighCut = new TF1("fGlobalMultCentHighCut", cfgMultiplicityCorrelationCut.cfgGlobalMultCentHighCutFunction->c_str(), 0, 100); + fGlobalMultCentHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.data()); + fGlobalMultCentLowCut = new TF1("fGlobalMultCentLowCut", cfgMultiplicityCorrelationCut.cfgGlobalMultCentLowCutFunction->c_str(), 0, 100); + fGlobalMultCentLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.data()); + LOGF(info, "Global multiplicity vs centrality Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultCentHighCutFunction->c_str()); + LOGF(info, "Global multiplicity vs centrality Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgGlobalMultCentLowCutFunction->c_str()); + LOGF(info, "Global multiplicity vs centrality cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutPars.value.size(); ++i) { + LOGF(info, "fGlobalMultCentLowCut par %zu = %g", i, fGlobalMultCentLowCut->GetParameter(i)); + LOGF(info, "fGlobalMultCentHighCut par %zu = %g", i, fGlobalMultCentHighCut->GetParameter(i)); + } + } + if (cfgMultiplicityCorrelationCut.cfgPVMultCentCutEnabled) { + LOGF(info, "PV multiplicity vs centrality cut is enabled"); + fPVMultCentHighCut = new TF1("fPVMultCentHighCut", cfgMultiplicityCorrelationCut.cfgPVMultCentHighCutFunction->c_str(), 0, 100); + fPVMultCentHighCut->SetParameters(cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.data()); + fPVMultCentLowCut = new TF1("fPVMultCentLowCut", cfgMultiplicityCorrelationCut.cfgPVMultCentLowCutFunction->c_str(), 0, 100); + fPVMultCentLowCut->SetParameters(cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.data()); + LOGF(info, "PV multiplicity vs centrality Upper Cut: %s", cfgMultiplicityCorrelationCut.cfgPVMultCentHighCutFunction->c_str()); + LOGF(info, "PV multiplicity vs centrality Lower Cut: %s", cfgMultiplicityCorrelationCut.cfgPVMultCentLowCutFunction->c_str()); + LOGF(info, "PV multiplicity vs centrality cut parameters:"); + for (std::size_t i = 0; i < cfgMultiplicityCorrelationCut.cfgPVMultCentCutCutPars.value.size(); ++i) { + LOGF(info, "fPVMultCentLowCut par %zu = %g", i, fPVMultCentLowCut->GetParameter(i)); + LOGF(info, "fPVMultCentHighCut par %zu = %g", i, fPVMultCentHighCut->GetParameter(i)); + } + } + } + } else { + LOGF(warning, "Event selection cuts are disabled"); + } + readMatrix(cfgTrackCuts->getData(), trackSel); + if (trackSel[DCAz][TrkSelNch] == 0) { + fDCAzCut[TrkSelNch] = new TF1("pT-DCAzNch", Form("[0]*%s", cfgCutDCAz->c_str()), 0.001, 100); + fDCAzCut[TrkSelNch]->SetParameter(0, trackSel[DCAzNsigma][TrkSelNch]); + LOGF(info, Form("pT-dependent DCAz cut has been selcted: [0]*%s", cfgCutDCAz->c_str())); + LOGF(info, Form("pT-dependent DCAz cut has been selcted: %f*%s", fDCAzCut[TrkSelNch]->GetParameter(0), cfgCutDCAz->c_str())); + } + if (trackSel[DCAz][TrkSelObs] == 0) { + fDCAzCut[TrkSelObs] = new TF1("pT-DCAzObs", Form("[0]*%s", cfgCutDCAz->c_str()), 0.001, 100); + fDCAzCut[TrkSelObs]->SetParameter(0, trackSel[DCAzNsigma][TrkSelObs]); + } + fDCAxyCut[TrkSelNch] = new TF1("pT-DCAxyNch", Form("[0]*%s", cfgCutDCAxy->c_str()), 0.001, 100); + fDCAxyCut[TrkSelNch]->SetParameter(0, trackSel[DCAxyNsigma][TrkSelNch]); + fDCAxyCut[TrkSelObs] = new TF1("pT-DCAxyObs", Form("[0]*%s", cfgCutDCAxy->c_str()), 0.001, 100); + fDCAxyCut[TrkSelObs]->SetParameter(0, trackSel[DCAxyNsigma][TrkSelObs]); + LOGF(info, Form("pT-dependent DCAxy cut for Nch : %f*%s", fDCAxyCut[TrkSelObs]->GetParameter(0), cfgCutDCAxy->c_str())); + std::array etaGapsFlag = {"2-sub |#Delta#eta|>0", "2-sub |#Delta#eta|>0.2", "2-sub |#Delta#eta|>0.4", "2-sub |#Delta#eta|>0.6", "2-sub |#Delta#eta|>0.8", "2-sub |#Delta#eta|>1.0", "2-sub |#Delta#eta|>1.2", "2-sub |#Delta#eta|>1.4"}; + if (cfgGetNumberOfCorr) { + registryCorr.add("hTwoParticleCorrelations", "2 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hFourParticleCorrelations", "4 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hSixParticleCorrelations", "6 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + registryCorr.add("hEightParticleCorrelations", "8 particle correlations; Nch;", {HistType::kTH2F, {axisNch, {8, -0.5, 7.5}}}); + for (uint iSet = 0; iSet < static_cast(EtaGapCounter); iSet++) { + registryCorr.get(HIST("hTwoParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hFourParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hSixParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + registryCorr.get(HIST("hEightParticleCorrelations"))->GetYaxis()->SetBinLabel(iSet + 1, etaGapsFlag[iSet].c_str()); + } + } + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + if (!cfgFirstFillRunsList.value.empty()) { + registryQA.add("multT0A_multT0C", "FT0A-C multiplicity correlation;N_{ch}(FT0A);N_{ch}(FT0C);t[h]", {HistType::kTH3D, {axisT0A, axisT0C, binningTime}}); + firstRunOfFill = *std::min_element(cfgFirstFillRunsList.value.begin(), cfgFirstFillRunsList.value.end()); + } + } + template + float getCentrality(const TCollision& collision) + { + switch (cfgEventSelection.cfgCentEstimator) { + case CentFT0C: + return collision.centFT0C(); + case CentFT0CVariant1: + return collision.centFT0CVariant1(); + case CentFT0M: + return collision.centFT0M(); + case CentFV0A: + return collision.centFV0A(); + case CentNTPV: + return collision.centNTPV(); + case CentNGlobal: + return collision.centNGlobal(); + case CentMFT: + return collision.centMFT(); + default: + return collision.centFT0C(); + } + } + template + bool selectMultiplicityCorrelation(const TCollision& collision, const int multTrk, const float& centrality) + { + auto multNTracksPV = collision.multNTracksPV(); + if (cfgMultiplicityCorrelationCut.cfgGlobalMultPVMultCutEnabled) { + if (multTrk > fGlobalMultPVMultHighCut->Eval(multNTracksPV) || multTrk < fGlobalMultPVMultLowCut->Eval(multNTracksPV)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultV0AMultCutEnabled) { + if (collision.multFV0A() > fGlobalMultV0AMultHighCut->Eval(multTrk) || collision.multFV0A() < fGlobalMultV0AMultLowCut->Eval(multTrk)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgGlobalMultCentCutEnabled) { + if (multTrk > fGlobalMultCentHighCut->Eval(centrality) || multTrk < fGlobalMultCentLowCut->Eval(centrality)) { + return false; + } + } + if (cfgMultiplicityCorrelationCut.cfgPVMultCentCutEnabled) { + if (multNTracksPV > fPVMultCentHighCut->Eval(centrality) || multNTracksPV < fPVMultCentLowCut->Eval(centrality)) { + return false; + } + } + return true; + } + template + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const int runNumber) + { + if (!cfgEventSelection.cfgEvSelUsage) { + return true; + } + if (cfgEventSelection.cfgEvSel8) { + if (!collision.sel8()) { + return false; + } + registryQA.fill(HIST("hEventCount"), Sel8); + } + if (!cfgEventSelection.cfgEvSelRCTflags.value.empty()) { + if (!rctChecker(collision)) { + return false; + } + registryQA.fill(HIST("hEventCount"), RCTflags); + } + for (auto& cut : eventSelectionCuts) { + if (!cut.enabled) { + continue; + } + if (!collision.selection_bit(cut.flag)) { + return false; + } + registryQA.fill(HIST("hEventCount"), cut.histBin); + } + if (cfgMultiplicityCorrelationCut.cfgEvSelMultCorrelation) { + if (!selectMultiplicityCorrelation(collision, multTrk, centrality)) { + return false; + } + registryQA.fill(HIST("hEventCount"), MultCorrelation); + } + if (std::find(cfgRemovedRunList.value.begin(), cfgRemovedRunList.value.end(), runNumber) != cfgRemovedRunList.value.end()) { + return false; + } + registryQA.fill(HIST("hEventCount"), BadRunsRemoved); + return true; + } + template + bool trackSelected(const TTracks& track) + { + if (trackSel[DCAz][sg] == 0 && std::fabs(track.dcaZ()) > fDCAzCut[sg]->Eval(track.pt())) { + return false; + } + if (trackSel[DCAz][sg] != 0 && std::fabs(track.dcaZ()) > trackSel[DCAz][sg]) { + return false; + } + return ((track.tpcChi2NCl() < trackSel[Chi2prTPCcls][sg]) && (track.tpcNClsFound() >= trackSel[TPCclu][sg]) && (track.itsNCls() >= trackSel[ITSclu][sg]) && (track.tpcNClsCrossedRows() >= trackSel[TPCCrossedRows][sg]) && (std::fabs(track.dcaXY()) < fDCAxyCut[sg]->Eval(track.pt()))); + } + template + inline void fillEventQA(const TCollision& collision, const TTracks& tracks) + { + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), tracks.size()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); + registryQA.fill(HIST(FillTimeName[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C()); + } + template + inline void fillTrackQA(const TTracks& track, const float& vtxz) + { + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hTrackPhi"), track.phi()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hTrackEta"), track.eta()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hPhiEtaVtxZ"), track.phi(), track.eta(), vtxz); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hPt"), track.pt()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hChi2prTPCcls"), track.tpcChi2NCl()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hChi2prITScls"), track.itsChi2NCl()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnTPCClu"), track.tpcNClsFound()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnITSClu"), track.itsNCls()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hnTPCCrossedRow"), track.tpcNClsCrossedRows()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hDCAz"), track.dcaZ(), track.pt()); + registryQA.fill(HIST(FillTrackName[sg]) + HIST("hDCAxy"), track.dcaXY(), track.pt()); + } + template + inline void fillCorrProfile(const TTrack& track, std::array& trackRegion) + { + float eta = track.eta(); + for (uint iRegion = 0; iRegion < static_cast(EtaGapCounter); iRegion++) { + if (std::fabs(eta) > trackRegion[iRegion].gap) { + if (eta > 0) { + trackRegion[iRegion].forward++; + } else { + trackRegion[iRegion].backward++; + } + } + } + } + double getTimeSinceStartOfFill(uint64_t timestamp, int firstRun) + { + auto runDuration = ccdb->getRunDuration(firstRun); + uint64_t tsSOF = runDuration.first; + uint64_t diff = timestamp - tsSOF; + return static_cast(diff) / 3600000.0; + } + using FilteredTracks = soa::Filtered>; + using FilteredCollisions = soa::Filtered>; + void process(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const&, FilteredTracks const& tracks) + { + auto bc = collision.bc_as(); + int run = bc.runNumber(); + int nch = 0; + std::array trackCorr{TrackRegion{.forward = 0, .backward = 0, .gap = 0.}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.1}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.2}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.3}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.4}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.5}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.6}, TrackRegion{.forward = 0, .backward = 0, .gap = 0.7}}; + registryQA.fill(HIST("hEventCount"), FilteredEvent); + if (tracks.size() < 1) { + return; + } + fillEventQA(collision, tracks); + const float cent = getCentrality(collision); + const float vtxz = collision.posZ(); + if (!eventSelected(collision, tracks.size(), cent, run)) { + return; + } + if (cfgEventSelection.cfgEvSelUsage) { + fillEventQA(collision, tracks); + } + if (!cfgFirstFillRunsList.value.empty()) { + if (std::find(cfgFirstFillRunsList.value.begin(), cfgFirstFillRunsList.value.end(), run) != cfgFirstFillRunsList.value.end() && firstRunOfFill != run) { + firstRunOfFill = run; + } + registryQA.fill(HIST("multT0A_multT0C"), collision.multFT0A(), collision.multFT0C(), getTimeSinceStartOfFill(bc.timestamp(), firstRunOfFill)); + } + registryQA.fill(HIST("hVtxZ"), vtxz); + registryQA.fill(HIST("hCentrality"), cent); + if (std::find(runNumbers.begin(), runNumbers.end(), run) == runNumbers.end()) { + if (cfgNUAWeightsRunByRun) { + std::shared_ptr histNUA = registryQA.add(Form("NUA/hPhiEtaVtxZ_%i", run), "", {HistType::kTH3D, {{60, 0.0, constants::math::TwoPI}, {32, -0.8, 0.8}, {40, -10, 10}}}); + th3sPerRun.insert(std::make_pair(run, histNUA)); + } + runNumbers.push_back(run); + } + for (const auto& track : tracks) { + + if (trackSelected(track)) { + fillTrackQA(track, vtxz); + } + if (!trackSelected(track)) { + continue; + } + fillTrackQA(track, vtxz); + if (cfgGetNumberOfCorr) { + fillCorrProfile(track, trackCorr); + + nch++; + } + if (cfgNUAWeightsRunByRun) { + th3sPerRun[run]->Fill(track.phi(), track.eta(), vtxz); + } + } + float binContent = 0; + float nCorrelations = 0; + if (cfgGetNumberOfCorr) { + for (uint iRegion = 0; iRegion < static_cast(EtaGapCounter); iRegion++) { + if (trackCorr[iRegion].forward > 0 && trackCorr[iRegion].backward > 0) { + binContent = registryCorr.get(HIST("hTwoParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * trackCorr[iRegion].backward; + registryCorr.get(HIST("hTwoParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 1 && trackCorr[iRegion].backward > 1) { + binContent = registryCorr.get(HIST("hFourParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) / 4.; + registryCorr.get(HIST("hFourParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 2 && trackCorr[iRegion].backward > 2) { + binContent = registryCorr.get(HIST("hSixParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * (trackCorr[iRegion].forward - 2) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) * (trackCorr[iRegion].backward - 2) / 36.; + registryCorr.get(HIST("hSixParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + if (trackCorr[iRegion].forward > 3 && trackCorr[iRegion].backward > 3) { + binContent = registryCorr.get(HIST("hEightParticleCorrelations"))->GetBinContent(nch, iRegion + 1); + nCorrelations = trackCorr[iRegion].forward * (trackCorr[iRegion].forward - 1) * (trackCorr[iRegion].forward - 2) * (trackCorr[iRegion].forward - 3) * trackCorr[iRegion].backward * (trackCorr[iRegion].backward - 1) * (trackCorr[iRegion].backward - 2) * (trackCorr[iRegion].backward - 3) / 576; + registryCorr.get(HIST("hEightParticleCorrelations"))->SetBinContent(nch, iRegion + 1, binContent + nCorrelations); + } + } + } + } +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/Flow/Tasks/flowZdcEnergy.cxx b/PWGCF/Flow/Tasks/flowZdcEnergy.cxx index 742fd5248c0..e1366124622 100644 --- a/PWGCF/Flow/Tasks/flowZdcEnergy.cxx +++ b/PWGCF/Flow/Tasks/flowZdcEnergy.cxx @@ -42,6 +42,36 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +namespace o2::aod +{ +namespace zdctree +{ +DECLARE_SOA_COLUMN(Znac, znac, float); +DECLARE_SOA_COLUMN(Zna1, zna1, float); +DECLARE_SOA_COLUMN(Zna2, zna2, float); +DECLARE_SOA_COLUMN(Zna3, zna3, float); +DECLARE_SOA_COLUMN(Zna4, zna4, float); +DECLARE_SOA_COLUMN(Zncc, zncc, float); +DECLARE_SOA_COLUMN(Znc1, znc1, float); +DECLARE_SOA_COLUMN(Znc2, znc2, float); +DECLARE_SOA_COLUMN(Znc3, znc3, float); +DECLARE_SOA_COLUMN(Znc4, znc4, float); +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); +} // namespace zdctree +DECLARE_SOA_TABLE(ZdcTree, "AOD", "ZDCTREE", + zdctree::Znac, + zdctree::Zna1, + zdctree::Zna2, + zdctree::Zna3, + zdctree::Zna4, + zdctree::Zncc, + zdctree::Znc1, + zdctree::Znc2, + zdctree::Znc3, + zdctree::Znc4, + zdctree::Multiplicity); +} // namespace o2::aod + #define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; struct FlowZdcEnergy { @@ -73,6 +103,7 @@ struct FlowZdcEnergy { Service ccdb; HistogramRegistry registry{"registry"}; + Produces zdcTree; Filter trackFilter = nabs(aod::track::eta) < cfgEtaMax && aod::track::pt > cfgPtMin&& aod::track::pt < cfgPtMax&& nabs(aod::track::dcaXY) < cfgDcaXYMax&& nabs(aod::track::dcaZ) < cfgDcaZMax; using UsedTracks = soa::Filtered>; @@ -204,6 +235,18 @@ struct FlowZdcEnergy { const float energySectorZNC3 = zdc.energySectorZNC()[2]; const float energySectorZNC4 = zdc.energySectorZNC()[3]; + zdcTree(energyCommonZNA, + energySectorZNA1, + energySectorZNA2, + energySectorZNA3, + energySectorZNA4, + energyCommonZNC, + energySectorZNC1, + energySectorZNC2, + energySectorZNC3, + energySectorZNC4, + multiTPC); + const float sumEnergyZNA = energySectorZNA1 + energySectorZNA2 + energySectorZNA3 + energySectorZNA4; const float sumEnergyZNC = energySectorZNC1 + energySectorZNC2 + energySectorZNC3 + energySectorZNC4; diff --git a/PWGCF/Flow/Tasks/flowZdcTask.cxx b/PWGCF/Flow/Tasks/flowZdcTask.cxx index ee62667dae8..0186d851235 100644 --- a/PWGCF/Flow/Tasks/flowZdcTask.cxx +++ b/PWGCF/Flow/Tasks/flowZdcTask.cxx @@ -52,38 +52,35 @@ using namespace o2::aod::mult; using namespace o2::constants::math; using namespace o2::aod::evsel; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; - struct FlowZdcTask { SliceCache cache; - - O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") - Configurable eventSelection{"eventSelection", 1, "event selection"}; + Configurable useFT0M{"useFT0M", true, "FT0M for centrality"}; + Configurable useFT0C{"useFT0C", false, "FT0C for centrality"}; + Configurable useGlbTracks{"useGlbTracks", false, "GlbTracks for centrality"}; + Configurable cfgCutVertex{"cfgCutVertex", 10.0, "Accepted z-vertex range"}; Configurable maxZem{"maxZem", 3099.5, "Max ZEM signal"}; // for ZDC info and analysis - Configurable nBinsAmp{"nBinsAmp", 1025, "nbinsAmp"}; - Configurable nBinsADC{"nBinsADC", 1000, "nbinsADC"}; Configurable maxZn{"maxZn", 125.5, "Max ZN signal"}; Configurable maxZp{"maxZp", 125.5, "Max ZP signal"}; // configs for process QA - Configurable nBinsNch{"nBinsNch", 2501, "N bins Nch (|eta|<0.8)"}; + Configurable nBinsNch{"nBinsNch", 3501, "N bins Nch (|eta|<0.8)"}; Configurable nBinsAmpFT0{"nBinsAmpFT0", 100, "N bins FT0 amp"}; Configurable maxAmpFT0{"maxAmpFT0", 2500, "Max FT0 amp"}; Configurable maxAmpFT0M{"maxAmpFT0M", 2500, "Max FT0M amp"}; - Configurable nBinsAmpFV0{"nBinsAmpFV0", 100, "N bins FV0 amp"}; - Configurable maxAmpFV0{"maxAmpFV0", 2000, "Max FV0 amp"}; + Configurable nBinsAmpFT0M{"nBinsAmpFT0M", 100, "N bins FT0M amp"}; + Configurable nBinsAmpFT0C{"nBinsAmpFT0C", 100, "N bins FT0C amp"}; + Configurable nBinsGlbTracks{"nBinsGlbTracks", 100, "N bins global tracks"}; Configurable nBinsZDC{"nBinsZDC", 400, "nBinsZDC"}; + Configurable nBinsZP{"nBinsZP", 50, "nBinsZP"}; + Configurable nBinsZN{"nBinsZN", 50, "nBinsZN"}; Configurable minNch{"minNch", 0, "Min Nch (|eta|<0.8)"}; Configurable maxNch{"maxNch", 2500, "Max Nch (|eta|<0.8)"}; Configurable nBinsTDC{"nBinsTDC", 150, "nbinsTDC"}; Configurable nBinsCent{"nBinsCent", 10, "nBinsCent"}; - Configurable minTdcZn{"minTdcZn", 4.0, "minimum TDC for ZN"}; - Configurable maxTdcZn{"maxTdcZn", 4.0, "maximum TDC for ZN"}; - Configurable minTdcZp{"minTdcZp", -4.0, "minimum TDC for ZP"}; - Configurable maxTdcZp{"maxTdcZp", -4.0, "maximum TDC for ZP"}; - Configurable applyZdcCorrection{"applyZdcCorrection", false, "Apply ZP correction?"}; - Configurable zpaCoeff{"zpaCoeff", 0.021f, "Coefficient a in zpa correction"}; - Configurable zpcCoeff{"zpcCoeff", 0.021f, "Coefficient b in zpc correction"}; + Configurable minTdcZn{"minTdcZn", -2.0, "minimum TDC for ZN"}; + Configurable maxTdcZn{"maxTdcZn", 2.0, "maximum TDC for ZN"}; + Configurable minTdcZp{"minTdcZp", -2.0, "minimum TDC for ZP"}; + Configurable maxTdcZp{"maxTdcZp", 2.0, "maximum TDC for ZP"}; Configurable posZcut{"posZcut", +10.0, "z-vertex position cut"}; Configurable posYcut{"posYcut", +10.0, "y-vertex position cut"}; Configurable posXcut{"posXcut", +10.0, "x-vertex position cut"}; @@ -95,6 +92,7 @@ struct FlowZdcTask { // event selection Configurable isNoCollInTimeRangeStrict{"isNoCollInTimeRangeStrict", true, "isNoCollInTimeRangeStrict?"}; Configurable isNoCollInTimeRangeStandard{"isNoCollInTimeRangeStandard", false, "isNoCollInTimeRangeStandard?"}; + Configurable isNoSameBunchPileup{"isNoSameBunchPileup", true, "isNoSameBunchPileup?"}; Configurable isNoCollInRofStrict{"isNoCollInRofStrict", true, "isNoCollInRofStrict?"}; Configurable isNoCollInRofStandard{"isNoCollInRofStandard", false, "isNoCollInRofStandard?"}; Configurable isNoHighMultCollInPrevRof{"isNoHighMultCollInPrevRof", true, "isNoHighMultCollInPrevRof?"}; @@ -161,7 +159,7 @@ struct FlowZdcTask { // Begin Histogram Registry HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Service ccdb; + Service ccdb{}; void init(InitContext const&) { @@ -206,36 +204,26 @@ struct FlowZdcTask { xAxis->SetBinLabel(16, "Within TDC cut?"); if (doprocessQA) { - histos.add("ZNVsFT0A", ";T0A (#times 1/100);ZNA+ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZNVsFT0C", ";T0C (#times 1/100);ZNA+ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZNVsFT0M", ";T0A+T0C (#times 1/100);ZNA+ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("CommonZNVsFT0M", ";T0A+T0C (#times 1/100);ZNA+ZNC Common Energy;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZNVsCent", ";T0C cent;ZNA + ZNC Amplitude;", kTH2F, {{{nBinsCent, 0., maxCent}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPVsFT0A", ";T0A (#times 1/100);ZPA+ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("ZPVsFT0C", ";T0C (#times 1/100);ZPA+ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("ZPVsFT0M", ";T0A+T0C (#times 1/100);ZPA+ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("ZPVsCent", ";T0C cent;ZPA + ZPC Amplitude;", kTH2F, {{{nBinsCent, 0., maxCent}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("CommonZPVsFT0M", ";T0A+T0C (#times 1/100);ZPA+ZPC Common Energy;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("ZNAVsFT0A", ";T0A (#times 1/100);ZNA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); + if (useFT0M) { + histos.add("ZPAZNAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + histos.add("ZPCZNCVsFT0M", ";T0A+T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsAmpFT0M, 0., maxAmpFT0M}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + } + if (useFT0C) { + histos.add("ZPAZNAVsFT0C", ";T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsAmpFT0C, 0., maxAmpFT0}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + histos.add("ZPCZNCVsFT0C", ";T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsAmpFT0C, 0., maxAmpFT0}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + } + if (useGlbTracks) { + histos.add("ZPAZNAVsGlbTracks", ";N_{global tracks} (|#eta|<0.8);ZPA Amplitude;ZNA Amplitude;", kTH3F, {{{nBinsGlbTracks, minNch, maxNch}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + histos.add("ZPCZNCVsGlbTracks", ";N_{global tracks} (|#eta|<0.8);ZPC Amplitude;ZNC Amplitude;", kTH3F, {{{nBinsGlbTracks, minNch, maxNch}, {nBinsZP, -0.5, maxZp}, {nBinsZN, -0.5, maxZn}}}); + } histos.add("ZNAVsFT0C", ";T0C (#times 1/100);ZNA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZNAVsCent", ";T0C cent;ZNA Amplitude;", kTH2F, {{{nBinsCent, 0., maxCent}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNAVsFT0M", ";T0A+T0C (#times 1/100);ZNA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZNCVsFT0A", ";T0A (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNCVsFT0C", ";T0C (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZn}}}); histos.add("ZNCVsFT0M", ";T0A+T0C (#times 1/100);ZNC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZNCVsCent", ";T0C cent;ZNC Amplitude;", kTH2F, {{{nBinsCent, 0., maxCent}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPAZNAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3D, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPCZNCVsFT0M", ";T0A+T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3D, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPAZNAVsFT0C", ";T0C (#times 1/100);ZPA Amplitude;ZNA Amplitude;", kTH3D, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPCZNCVsFT0C", ";T0C (#times 1/100);ZPC Amplitude;ZNC Amplitude;", kTH3D, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}, {nBinsZDC, -0.5, maxZn}}}); - histos.add("ZPAVsFT0A", ";T0A (#times 1/100);ZPA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPAVsFT0C", ";T0C (#times 1/100);ZPA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPAVsFT0M", ";T0A+T0C (#times 1/100);ZPA Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("ZPAVsCent", ";T0C cent;ZPA Amplitude;", kTH2F, {{{nBinsCent, 0., maxCent}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("ZPCVsFT0A", ";T0A (#times 1/100);ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPCVsFT0C", ";T0C (#times 1/100);ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZPCVsFT0M", ";T0A+T0C (#times 1/100);ZPC Amplitude;", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsZDC, -0.5, maxZp}}}); - histos.add("ZPCVsCent", ";T0C cent;ZPC Amplitude;", kTH2F, {{{nBinsCent, 0., maxCent}, {nBinsZDC, -0.5, maxZp}}}); histos.add("ZN", ";ZNA+ZNC;Entries;", kTH1F, {{nBinsZDC, -0.5, maxZn}}); histos.add("ZNA", ";ZNA Amplitude;Entries;", kTH1F, {{nBinsZDC, -0.5, maxZn}}); histos.add("ZPA", ";ZPA Amplitude;Entries;", kTH1F, {{nBinsZDC, -0.5, maxZp}}); @@ -269,13 +257,9 @@ struct FlowZdcTask { histos.add("debunch", ";t_{ZDC}-t_{ZDA};t_{ZDC}+t_{ZDA}", kTH2F, {{{nBinsTDC, minTdcZn, maxTdcZn}, {nBinsTDC, minTdcZp, maxTdcZp}}}); histos.add("GlbTracks", "Nch", kTH1F, {{nBinsNch, minNch, maxNch}}); histos.add("ampFT0C", ";T0C (#times 1/100);", kTH1F, {{nBinsAmpFT0, 0., maxAmpFT0}}); - histos.add("ampFT0A", ";T0A (#times 1/100);", kTH1F, {{nBinsAmpFT0, 0., maxAmpFT0}}); histos.add("ampFT0M", ";T0A+T0C (#times 1/100);", kTH1F, {{nBinsAmpFT0, 0., maxAmpFT0M}}); - histos.add("ampFV0A", ";V0A (#times 1/100);", kTH1F, {{nBinsAmpFV0, 0., maxAmpFV0}}); histos.add("NchVsFT0C", ";T0C (#times 1/100, -3.3 < #eta < -2.1);#it{N}_{ch} (|#eta|<0.8);", kTH2F, {{{nBinsAmpFT0, 0., 950.}, {nBinsNch, minNch, maxNch}}}); histos.add("NchVsFT0M", ";T0A+T0C (#times 1/100, -3.3 < #eta < -2.1 and 3.5 < #eta < 4.9);#it{N}_{ch} (|#eta|<0.8);", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0M}, {nBinsNch, minNch, maxNch}}}); - histos.add("NchVsFT0A", ";T0A (#times 1/100, 3.5 < #eta < 4.9);#it{N}_{ch} (|#eta|<0.8);", kTH2F, {{{nBinsAmpFT0, 0., maxAmpFT0}, {nBinsNch, minNch, maxNch}}}); - histos.add("NchVsFV0A", ";V0A (#times 1/100, 2.2 < #eta < 5);#it{N}_{ch} (|#eta|<0.8);", kTH2F, {{{nBinsAmpFV0, 0., maxAmpFV0}, {nBinsNch, minNch, maxNch}}}); histos.add("NchVsEt", ";#it{E}_{T} (|#eta|<0.8);#LTITS+TPC tracks#GT (|#eta|<0.8);", kTH2F, {{{nBinsNch, minNch, maxNch}, {nBinsNch, minNch, maxNch}}}); histos.add("NchVsMeanPt", ";#it{N}_{ch} (|#eta|<0.8);#LT[#it{p}_{T}]#GT (|#eta|<0.8);", kTProfile, {{nBinsNch, minNch, maxNch}}); histos.add("NchVsNPV", ";#it{N}_{PV} (|#eta|<1);ITS+TPC tracks (|#eta|<0.8);", kTH2F, {{{300, -0.5, 5999.5}, {nBinsNch, minNch, maxNch}}}); @@ -284,8 +268,6 @@ struct FlowZdcTask { histos.add("ZNAVsNch", ";#it{N}_{ch} (|#eta|<0.8);ZNA;", kTH2F, {{{nBinsNch, minNch, maxNch}, {nBinsZDC, minNch, maxZn}}}); histos.add("ZPAVsNch", ";#it{N}_{ch} (|#eta|<0.8);ZPA;", kTH2F, {{{nBinsNch, minNch, maxNch}, {nBinsZDC, minNch, maxZp}}}); histos.add("ZPCVsNch", ";#it{N}_{ch} (|#eta|<0.8);ZPA;", kTH2F, {{{nBinsNch, minNch, maxNch}, {nBinsZDC, minNch, maxZp}}}); - histos.add("ZNVsNch", ";#it{N}_{ch} (|#eta|<0.8);ZNA+ZNC;", kTH2F, {{{nBinsNch, minNch, maxNch}, {nBinsZDC, minNch, maxZn}}}); - histos.add("ZPVsNch", ";#it{N}_{ch} (|#eta|<0.8);ZPA+ZPC;", kTH2F, {{{nBinsNch, minNch, maxNch}, {nBinsZDC, minNch, maxZp}}}); } if (doprocessZdc) { histos.add("ampZna", ";ZNA Amplitude;Entries;", kTH1F, {{nBinsZDC, -0.5, maxZn}}); @@ -329,10 +311,12 @@ struct FlowZdcTask { } histos.fill(HIST("hEventCounter"), EvCutLabel::SelEigth); - if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return false; + if (isNoSameBunchPileup) { + if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("hEventCounter"), EvCutLabel::NoSameBunchPileup); } - histos.fill(HIST("hEventCounter"), EvCutLabel::NoSameBunchPileup); if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; @@ -401,14 +385,15 @@ struct FlowZdcTask { } histos.fill(HIST("hEventCounter"), EvCutLabel::VtxZ); if (isApplyRadialCut) { - if (std::fabs(col.posX()) > posXcut && (std::fabs(col.posY()) > posYcut)) + if (std::fabs(col.posX()) > posXcut || std::fabs(col.posY()) > posYcut) { return false; + } } return true; } - void processQA(ColEvSels::iterator const& collision, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FV0As const& /*fv0as*/, aod::FT0s const& /*ft0s*/, TheFilteredTracks const& tracks) + void processQA(ColEvSels::iterator const& collision, BCsRun3 const& /*bcs*/, aod::Zdcs const& /*zdcsData*/, aod::FT0s const& /*ft0s*/, TheFilteredTracks const& tracks) { const auto& foundBC = collision.foundBC_as(); if (!isEventSelected(collision)) { @@ -419,9 +404,8 @@ struct FlowZdcTask { } histos.fill(HIST("hEventCounter"), EvCutLabel::Zdc); auto zdc = foundBC.zdc(); - auto cent = collision.centFT0C(); - float aT0A = 0., aT0C = 0., aV0A = 0.; + float aT0A = 0., aT0C = 0.; if (foundBC.has_ft0()) { for (const auto& amplitude : foundBC.ft0().amplitudeA()) { aT0A += amplitude; @@ -431,11 +415,6 @@ struct FlowZdcTask { } } histos.fill(HIST("hEventCounter"), EvCutLabel::TZero); - if (foundBC.has_fv0a()) { - for (const auto& amplitude : foundBC.fv0a().amplitude()) { - aV0A += amplitude; - } - } const double normT0M{(aT0A + aT0C) / 100.}; float et = 0., meanpt = 0.; int itsTracks = 0, glbTracks = 0; @@ -507,12 +486,8 @@ struct FlowZdcTask { histos.fill(HIST("T0Ccent"), collision.centFT0C()); histos.fill(HIST("GlbTracks"), glbTracks); histos.fill(HIST("ampFT0C"), aT0C / 100.); - histos.fill(HIST("ampFT0A"), aT0A / 100.); histos.fill(HIST("ampFT0M"), (aT0A + aT0C) / 100.); - histos.fill(HIST("ampFV0A"), aV0A / 100.); // charged particle correlations - histos.fill(HIST("NchVsFV0A"), aV0A / 100., glbTracks); - histos.fill(HIST("NchVsFT0A"), aT0A / 100., glbTracks); histos.fill(HIST("NchVsFT0C"), aT0C / 100., glbTracks); histos.fill(HIST("NchVsFT0M"), (aT0A + aT0C) / 100., glbTracks); histos.fill(HIST("hNchvsNPV"), collision.multNTracksPVeta1(), tracks.size()); @@ -533,14 +508,6 @@ struct FlowZdcTask { float commonSumZna = zdc.energyCommonZNA(); float commonSumZpc = zdc.energyCommonZPC(); float commonSumZpa = zdc.energyCommonZPA(); - if (applyZdcCorrection) { - const float a = zpaCoeff; - const float b = zpcCoeff; - zpA = zpA - a * znA; - commonSumZpa = commonSumZpa - a * commonSumZna; - zpC = zpC - b * znC; - commonSumZpc = commonSumZpc - b * commonSumZnc; - } float aZEM1{zdc.amplitudeZEM1()}; float aZEM2{zdc.amplitudeZEM2()}; float sumZEMs{aZEM1 + aZEM2}; @@ -553,72 +520,68 @@ struct FlowZdcTask { if ((tZNA >= minTdcZn) && (tZNA <= maxTdcZn)) { histos.fill(HIST("ZNA"), znA); histos.fill(HIST("ZNACommon"), commonSumZna); - histos.fill(HIST("ZNAVsFT0A"), aT0A / 100., znA); histos.fill(HIST("ZNAVsFT0C"), aT0C / 100., znA); histos.fill(HIST("ZNAVsFT0M"), (aT0A + aT0C) / 100., znA); - histos.fill(HIST("ZNAVsCent"), cent, znA); histos.fill(HIST("ZNAVsNch"), glbTracks, znA); } if ((tZNC >= minTdcZn) && (tZNC <= maxTdcZn)) { histos.fill(HIST("ZNC"), znC); histos.fill(HIST("ZNCCommon"), commonSumZnc); - histos.fill(HIST("ZNCVsFT0A"), aT0A / 100., znC); histos.fill(HIST("ZNCVsFT0C"), aT0C / 100., znC); histos.fill(HIST("ZNCVsFT0M"), (aT0A + aT0C) / 100., znC); - histos.fill(HIST("ZNCVsCent"), cent, znC); histos.fill(HIST("ZNCVsNch"), glbTracks, znC); } if ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp)) { histos.fill(HIST("ZPA"), zpA); histos.fill(HIST("ZPACommon"), commonSumZpa); - histos.fill(HIST("ZPAVsFT0A"), aT0A / 100., zpA); histos.fill(HIST("ZPAVsFT0C"), aT0C / 100., zpA); histos.fill(HIST("ZPAVsFT0M"), (aT0A + aT0C) / 100., zpA); - histos.fill(HIST("ZPAVsCent"), cent, zpA); histos.fill(HIST("ZPAVsNch"), glbTracks, zpA); } if ((tZPC >= minTdcZp) && (tZPC <= maxTdcZp)) { histos.fill(HIST("ZPC"), zpC); histos.fill(HIST("ZPCCommon"), commonSumZpc); - histos.fill(HIST("ZPCVsFT0A"), aT0A / 100., zpC); histos.fill(HIST("ZPCVsFT0C"), aT0C / 100., zpC); histos.fill(HIST("ZPCVsFT0M"), (aT0A + aT0C) / 100., zpC); - histos.fill(HIST("ZPCVsCent"), cent, zpC); histos.fill(HIST("ZPCVsNch"), glbTracks, zpC); } - if (((tZNA >= minTdcZn) && (tZNA <= maxTdcZn)) && ((tZNC >= minTdcZn) && (tZNC <= maxTdcZn))) + if (((tZNA >= minTdcZn) && (tZNA <= maxTdcZn)) && ((tZNC >= minTdcZn) && (tZNC <= maxTdcZn))) { histos.fill(HIST("ZNVsZEM"), sumZEMs, sumZNs); + } if (((tZNA >= minTdcZn) && (tZNA <= maxTdcZn)) && ((tZNC >= minTdcZn) && (tZNC <= maxTdcZn))) { histos.fill(HIST("ZNAVsZNC"), znC, znA); histos.fill(HIST("CommonZNAVsZNC"), commonSumZnc, commonSumZna); histos.fill(HIST("ZN"), znA + znC); - histos.fill(HIST("ZNVsFT0C"), aT0C / 100., znA + znC); - histos.fill(HIST("ZNVsFT0M"), (aT0A + aT0C) / 100., znA + znC); - histos.fill(HIST("CommonZNVsFT0M"), (aT0A + aT0C) / 100., commonSumZna + commonSumZnc); - histos.fill(HIST("ZNVsCent"), cent, znA + znC); - histos.fill(HIST("ZNVsNch"), glbTracks, znA + znC); } if (((tZPA >= minTdcZp) && (tZPA <= maxTdcZp)) && ((tZPC >= minTdcZp) && (tZPC <= maxTdcZp))) { histos.fill(HIST("ZPAVsZPC"), zpC, zpA); histos.fill(HIST("CommonZPAVsZPC"), commonSumZpc, commonSumZpa); - histos.fill(HIST("ZPVsFT0A"), aT0A / 100., zpA + zpC); - histos.fill(HIST("ZPVsFT0C"), aT0C / 100., zpA + zpC); - histos.fill(HIST("ZPVsFT0M"), (aT0A + aT0C) / 100., zpA + zpC); - histos.fill(HIST("CommonZPVsFT0M"), (aT0A + aT0C) / 100., commonSumZpa + commonSumZpc); - histos.fill(HIST("ZPVsCent"), cent, zpA + zpC); - histos.fill(HIST("ZPVsNch"), glbTracks, zpA + zpC); } if (((tZNA >= minTdcZn) && (tZNA <= maxTdcZn)) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) { histos.fill(HIST("ZNAVsZPA"), zpA, znA); histos.fill(HIST("CommonZNAVsZPA"), commonSumZpa, commonSumZna); - histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); - histos.fill(HIST("ZPAZNAVsFT0C"), (aT0C) / 100., zpA, znA); + if (useFT0M) { + histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); + } + if (useFT0C) { + histos.fill(HIST("ZPAZNAVsFT0C"), aT0C / 100., zpA, znA); + } + if (useGlbTracks) { + histos.fill(HIST("ZPAZNAVsGlbTracks"), glbTracks, zpA, znA); + } } if (((tZNC >= minTdcZn) && (tZNC <= maxTdcZn)) && ((tZPC >= minTdcZp) && (tZPC <= maxTdcZp))) { histos.fill(HIST("ZNCVsZPC"), zpC, znC); histos.fill(HIST("CommonZNCVsZPC"), commonSumZpc, commonSumZnc); - histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); - histos.fill(HIST("ZPCZNCVsFT0C"), (aT0C) / 100., zpC, znC); + if (useFT0M) { + histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); + } + if (useFT0C) { + histos.fill(HIST("ZPCZNCVsFT0C"), aT0C / 100., zpC, znC); + } + if (useGlbTracks) { + histos.fill(HIST("ZPCZNCVsGlbTracks"), glbTracks, zpC, znC); + } } } else { histos.fill(HIST("ZNA"), znA); @@ -639,39 +602,30 @@ struct FlowZdcTask { histos.fill(HIST("ZPACommon"), commonSumZpa); histos.fill(HIST("ZPCCommon"), commonSumZpc); histos.fill(HIST("ZN"), znA + znC); - histos.fill(HIST("ZPVsFT0A"), aT0A / 100., zpA + zpC); - histos.fill(HIST("ZPVsFT0C"), aT0C / 100., zpA + zpC); - histos.fill(HIST("ZPVsFT0M"), (aT0A + aT0C) / 100., zpA + zpC); - histos.fill(HIST("CommonZPVsFT0M"), (aT0A + aT0C) / 100., commonSumZpa + commonSumZpc); - histos.fill(HIST("ZPAVsFT0A"), aT0A / 100., zpA); histos.fill(HIST("ZPAVsFT0C"), aT0C / 100., zpA); histos.fill(HIST("ZPAVsFT0M"), (aT0A + aT0C) / 100., zpA); - histos.fill(HIST("ZNVsFT0C"), aT0C / 100., znA + znC); - histos.fill(HIST("ZNVsFT0M"), (aT0A + aT0C) / 100., znA + znC); - histos.fill(HIST("CommonZNVsFT0M"), (aT0A + aT0C) / 100., commonSumZna + commonSumZnc); - histos.fill(HIST("ZPCVsFT0A"), aT0A / 100., zpC); histos.fill(HIST("ZPCVsFT0C"), aT0C / 100., zpC); histos.fill(HIST("ZPCVsFT0M"), (aT0A + aT0C) / 100., zpC); - histos.fill(HIST("ZNCVsFT0A"), aT0A / 100., znC); histos.fill(HIST("ZNCVsFT0C"), aT0C / 100., znC); histos.fill(HIST("ZNCVsFT0M"), (aT0A + aT0C) / 100., znC); - histos.fill(HIST("ZNAVsFT0A"), aT0A / 100., znA); histos.fill(HIST("ZNAVsFT0C"), aT0C / 100., znA); histos.fill(HIST("ZNAVsFT0M"), (aT0A + aT0C) / 100., znA); - histos.fill(HIST("ZNAVsCent"), cent, znA); - histos.fill(HIST("ZNCVsCent"), cent, znC); - histos.fill(HIST("ZPVsCent"), cent, zpA + zpC); - histos.fill(HIST("ZPAVsCent"), cent, zpA); - histos.fill(HIST("ZPCVsCent"), cent, zpC); histos.fill(HIST("ZPAVsNch"), glbTracks, zpA); histos.fill(HIST("ZPCVsNch"), glbTracks, zpC); - histos.fill(HIST("ZNVsNch"), glbTracks, znA + znC); histos.fill(HIST("ZNCVsNch"), glbTracks, znC); histos.fill(HIST("ZNAVsNch"), glbTracks, znA); - histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); - histos.fill(HIST("ZPAZNAVsFT0C"), (aT0C) / 100., zpA, znA); - histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); - histos.fill(HIST("ZPCZNCVsFT0C"), (aT0C) / 100., zpC, znC); + if (useFT0M) { + histos.fill(HIST("ZPAZNAVsFT0M"), (aT0A + aT0C) / 100., zpA, znA); + histos.fill(HIST("ZPCZNCVsFT0M"), (aT0A + aT0C) / 100., zpC, znC); + } + if (useFT0C) { + histos.fill(HIST("ZPAZNAVsFT0C"), aT0C / 100., zpA, znA); + histos.fill(HIST("ZPCZNCVsFT0C"), aT0C / 100., zpC, znC); + } + if (useGlbTracks) { + histos.fill(HIST("ZPAZNAVsGlbTracks"), glbTracks, zpA, znA); + histos.fill(HIST("ZPCZNCVsGlbTracks"), glbTracks, zpC, znC); + } } histos.fill(HIST("ZEM1"), aZEM1); histos.fill(HIST("ZEM2"), aZEM2); @@ -726,16 +680,21 @@ struct FlowZdcTask { histos.fill(HIST("ampZpc"), zpC); histos.fill(HIST("commonZpc"), commonSumZpc); } - if (((tZNC >= minTdcZn) && (tZNC <= maxTdcZn)) && ((tZNA >= minTdcZn) && (tZNA <= maxTdcZn))) + if (((tZNC >= minTdcZn) && (tZNC <= maxTdcZn)) && ((tZNA >= minTdcZn) && (tZNA <= maxTdcZn))) { histos.fill(HIST("ZnVsZem"), sumZEMs, znC + znA); - if (((tZNC >= minTdcZn) && (tZNC <= maxTdcZn)) && ((tZNA >= minTdcZn) && (tZNA <= maxTdcZn))) + } + if (((tZNC >= minTdcZn) && (tZNC <= maxTdcZn)) && ((tZNA >= minTdcZn) && (tZNA <= maxTdcZn))) { histos.fill(HIST("ZnaVsZnc"), znA, znC); - if (((tZPC >= minTdcZp) && (tZPC <= maxTdcZp)) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) + } + if (((tZPC >= minTdcZp) && (tZPC <= maxTdcZp)) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) { histos.fill(HIST("ZpaVsZpc"), zpA, zpC); - if ((tZNA >= minTdcZn) && (tZNA <= maxTdcZn) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) + } + if ((tZNA >= minTdcZn) && (tZNA <= maxTdcZn) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) { histos.fill(HIST("ZnaVsZpa"), znA, zpA); - if ((tZNC >= minTdcZn) && (tZNC <= maxTdcZn) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) + } + if ((tZNC >= minTdcZn) && (tZNC <= maxTdcZn) && ((tZPA >= minTdcZp) && (tZPA <= maxTdcZp))) { histos.fill(HIST("ZncVsZpc"), znC, zpC); + } } else { histos.fill(HIST("ampZna"), znA); histos.fill(HIST("ampZnc"), znC); diff --git a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx index 4c2a50c2049..5189ecd236d 100644 --- a/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx +++ b/PWGCF/Flow/Tasks/pidFlowPtCorr.cxx @@ -21,7 +21,13 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/CollisionAssociation.h" +#include "Common/Core/TableHelper.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseITS.h" @@ -30,12 +36,18 @@ #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -44,195 +56,837 @@ #include #include +#include #include +#include #include #include -#include #include #include #include #include #include +#include #include #include -#include - -#include +#include #include +#include #include #include +#include #include -#include +#include +#include #include #include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +namespace o2::aod +{ +namespace pidflowtimetrack +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_INDEX_COLUMN(Track, track); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(HasITS, hasITS, bool); +DECLARE_SOA_COLUMN(HasTPC, hasTPC, bool); +DECLARE_SOA_COLUMN(ItsNCls, itsNCls, uint8_t); +DECLARE_SOA_COLUMN(ItsChi2NCl, itsChi2NCl, float); +DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, int); +DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, int); +DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); +// Raw public-associator count, before PV locking or closest-target selection. +DECLARE_SOA_COLUMN(NumCompatibleCollisions, numCompatibleCollisions, unsigned int); +} // namespace pidflowtimetrack + +// At most one accepted target per source track, ordered by collision. +// Kinematics/DCA belong to the target vertex; detector quality belongs to the source. +// Baseline selection is applied here; analysis quality/pT cuts stay in the consumers. +DECLARE_SOA_TABLE(PidFlowTimeTracks, "AOD", "PFTIMETRACK", + o2::soa::Index<>, pidflowtimetrack::CollisionId, pidflowtimetrack::TrackId, + pidflowtimetrack::Pt, pidflowtimetrack::Eta, pidflowtimetrack::Phi, + pidflowtimetrack::DcaXY, pidflowtimetrack::DcaZ, + pidflowtimetrack::HasITS, pidflowtimetrack::HasTPC, + pidflowtimetrack::ItsNCls, pidflowtimetrack::ItsChi2NCl, + pidflowtimetrack::TpcNClsCrossedRows, pidflowtimetrack::TpcNClsFound, + pidflowtimetrack::TpcChi2NCl, pidflowtimetrack::NumCompatibleCollisions); +} // namespace o2::aod + +namespace +{ +// Keep the corrconfigs vector layout in one named index table. Namespace +// grouping avoids storing compile-time constants as analysis-task members. +namespace corrConfigIndex +{ +namespace charged +{ +enum : std::size_t { + kC22Gap = 0, + kC24 = 1, + kC22Full = 2, + kC32Gap = 3, + kC34Gap = 4, + kC24Gap = 52 +}; +} // namespace charged + +namespace pion +{ +enum : std::size_t { + kC22GapA = 5, + kC22GapB = 6, + kC24A = 11, + kC24B = 12, + kC32GapA = 17, + kC32GapB = 18, + kC34A = 23, + kC34B = 24, + kC22Pure = 29, + kC32Pure = 32, + kC22FullA = 35, + kC22FullB = 36, + kC24PIDChargedGap = 48 +}; +} // namespace pion + +namespace kaon +{ +enum : std::size_t { + kC22GapA = 7, + kC22GapB = 8, + kC24A = 13, + kC24B = 14, + kC32GapA = 19, + kC32GapB = 20, + kC34A = 25, + kC34B = 26, + kC22Pure = 30, + kC32Pure = 33, + kC22FullA = 37, + kC22FullB = 38, + kC24PIDChargedGap = 49 +}; +} // namespace kaon + +namespace proton +{ +enum : std::size_t { + kC22GapA = 9, + kC22GapB = 10, + kC24A = 15, + kC24B = 16, + kC32GapA = 21, + kC32GapB = 22, + kC34A = 27, + kC34B = 28, + kC22Pure = 31, + kC32Pure = 34, + kC22FullA = 39, + kC22FullB = 40, + kC24PIDChargedGap = 50 +}; +} // namespace proton + +// Unidentified correlations are appended after the original configurations +// so the charged and identified-particle vector layout remains unchanged. +namespace unidentified +{ +enum : std::size_t { + kC22GapA = 41, + kC22GapB = 42, + kC24A = 43, + kC24B = 44, + kC22Pure = 45, + kC22FullA = 46, + kC22FullB = 47, + kC24PIDChargedGap = 51 +}; +} // namespace unidentified + +enum : std::size_t { + kCount = 53 +}; +} // namespace corrConfigIndex + +// GFW region bitmasks are grouped by particle type and region role. POI and +// overlap use distinct bits because the overlap weight can differ from POI. +namespace gfwBitMask +{ +namespace charged +{ +enum : int { + kReference = 1 << 0 +}; +} // namespace charged + +namespace pion +{ +enum : int { + kPOI = 1 << 1, + kOverlap = 1 << 4 +}; +} // namespace pion + +namespace kaon +{ +enum : int { + kPOI = 1 << 2, + kOverlap = 1 << 5 +}; +} // namespace kaon + +namespace proton +{ +enum : int { + kPOI = 1 << 3, + kOverlap = 1 << 6 +}; +} // namespace proton + +namespace unidentified +{ +enum : int { + kPOI = 1 << 7, + kOverlap = 1 << 8 +}; +} // namespace unidentified +} // namespace gfwBitMask + +// Only target-dependent kinematics change; detector quality and MC labels stay +// on the source row. This view deliberately has no PID-table dependency. +template +struct TimeAssociatedChargedTrack : Track { + explicit TimeAssociatedChargedTrack(Track const& source) + : Track(source), targetPt(source.pt()), targetEta(source.eta()), targetPhi(source.phi()), targetDCAxy(source.dcaXY()), targetDCAz(source.dcaZ()) + { + } + float targetPt, targetEta, targetPhi, targetDCAxy, targetDCAz; + [[nodiscard]] float pt() const { return targetPt; } + [[nodiscard]] float eta() const { return targetEta; } + [[nodiscard]] float phi() const { return targetPhi; } + [[nodiscard]] float dcaXY() const { return targetDCAxy; } + [[nodiscard]] float dcaZ() const { return targetDCAz; } +}; +template +T timeAssociationOption(InitContext& context, std::string const& task, std::string const& option) +{ + T value{}; + if (!o2::common::core::getTaskOptionValue(context, task, option, value, false)) { + LOGF(fatal, "Charged time association needs option %s from producer %s", option.c_str(), task.c_str()); + } + return value; +} + +} // namespace + +// Shared charged-only producer: public TrackAssoc supplies time compatibility; +// lock PV contributors to their original collision and select the closest-time +// candidate for other tracks, then evaluate the target for flow, NUA and NUE. +struct PidFlowChargedTimeProducer { + Produces timeTracks; + // Original-vertex DCA/eta must not preselect candidates for another vertex. + using SourceTracks = soa::Join; + Service ccdb{}; + HistogramRegistry registry{"registry"}; + OutputObj chargedTimeAssociationConfig{TNamed("ChargedTimeAssociationConfig", "disabled")}; + TrackSelection timeGlobalSelection; + TrackSelection timeSDDSelection; + bool timeIsRun3 = false; + int timeFieldRun = -1; + uint64_t timeFieldTimestamp = 0; + float timeL3Current = 0.f; + float timeDipoleCurrent = 0.f; + bool timeUniformField = false; + bool cfgTimeAssociationQA = true; + int timeUsePVAssociation = aod::track_association::PVContrReassocOpt::OnlySameBc; + int timeMaxPvContributorsForLowMultReassoc = 10; + float timeNSigma = 4.f; + float timeMargin = 0.f; + float cutEta = 0.8f; + float cutVertex = 10.f; + + void init(InitContext& context) + { + if (doprocessDummy.value == doprocessTimeTracks.value) { + LOGF(fatal, "Enable exactly one of processDummy and processTimeTracks in pid-flow-charged-time-producer"); + } + const bool needed = timeAssociationOption(context, "pid-flow-pt-corr", "processDataTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processChargedDifferentialTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "fillCorrectionGraphTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processMCTimeAssociation") || + timeAssociationOption(context, "pid-flow-pt-corr", "processTimeAssociationComparison"); + if (needed != doprocessTimeTracks.value) { + LOGF(fatal, "Enable processTimeTracks (and disable processDummy) exactly when a charged time consumer is enabled"); + } + if (!doprocessTimeTracks) { + return; + } + cfgTimeAssociationQA = timeAssociationOption(context, "pid-flow-pt-corr", "cfgTimeAssociationQA"); + timeUsePVAssociation = timeAssociationOption(context, "track-to-collision-association", "usePVAssociation"); + timeMaxPvContributorsForLowMultReassoc = timeAssociationOption(context, "track-to-collision-association", "maxPvContributorsForLowMultReassoc"); + timeNSigma = timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat"); + timeMargin = timeAssociationOption(context, "track-to-collision-association", "timeMargin"); + cutEta = timeAssociationOption(context, "pid-flow-pt-corr", "trkQualityOpts.cfgCutEta"); + cutVertex = timeAssociationOption(context, "pid-flow-pt-corr", "cfgCutVertex"); + ccdb->setURL(timeAssociationOption(context, "pid-flow-pt-corr", "cfgurl")); + ccdb->setCaching(true); + ccdb->setCreatedNotAfter(timeAssociationOption(context, "pid-flow-pt-corr", "cfgnolaterthan")); + if (!timeAssociationOption(context, "track-to-collision-association", "processAssocWithTime") || + timeAssociationOption(context, "track-to-collision-association", "processStandardAssoc") || + timeAssociationOption(context, "track-to-collision-association", "setTrackSelections") != 0) { + LOGF(fatal, "Use track-to-collision-association: processAssocWithTime=true, processStandardAssoc=false, setTrackSelections=0"); + } + + // Inherit the effective upstream selection rather than introducing a + // second set of global-track cuts with different hidden DCA defaults. + timeIsRun3 = timeAssociationOption(context, "track-selection", "isRun3"); + const int matching = timeAssociationOption(context, "track-selection", "itsMatching"); + const int dcaSetup = timeAssociationOption(context, "track-selection", "dcaSetup"); + const bool compatibilityIU = timeAssociationOption(context, "track-selection", "compatibilityIU"); + if (timeIsRun3) { + using Matching = TrackSelection::GlobalTrackRun3ITSMatching; + const std::array modes{Matching::Run3ITSibAny, Matching::Run3ITSibAny, Matching::Run3ITSallAny, Matching::Run3ITSall7Layers, Matching::Run3ITSibFirst}; + if (matching < 0 || matching >= static_cast(modes.size())) { + LOGF(fatal, "Unsupported track-selection itsMatching=%d", matching); + } + timeGlobalSelection = getGlobalTrackSelectionRun3ITSMatch(modes[matching], dcaSetup); + timeGlobalSelection.SetTrackType(compatibilityIU ? aod::track::TrackTypeEnum::TrackIU : aod::track::TrackTypeEnum::Track); + } else { + if (matching != 0) { + LOGF(fatal, "Run 2 time association requires track-selection itsMatching=0"); + } + timeGlobalSelection = getGlobalTrackSelection(); + } + timeSDDSelection = getGlobalTrackSelectionSDD(); + const auto ptMin = timeAssociationOption(context, "track-selection", "ptMin"); + const auto ptMax = timeAssociationOption(context, "track-selection", "ptMax"); + const auto etaMin = timeAssociationOption(context, "track-selection", "etaMin"); + const auto etaMax = timeAssociationOption(context, "track-selection", "etaMax"); + for (auto const& selection : {&timeGlobalSelection, &timeSDDSelection}) { + selection->SetPtRange(ptMin, ptMax); + selection->SetEtaRange(etaMin, etaMax); + } + std::string metadata = "association=TrackAssoc;table=PidFlowTimeTracks;charged-only;PID=none;local-density=none;source=Tracks;propagation=target-DCA,USEMatCorrNONE;PV=locked-original,not-refitted;nonPV=minimum-absolute-BC-corrected-deltaTime;tie=lowest-collisionId;selection=before-cuts,no-fallback;candidate-count=raw-TrackAssoc"; + metadata += ";isRun3=" + std::to_string(static_cast(timeIsRun3)) + ";itsMatching=" + std::to_string(matching) + ";dcaSetup=" + std::to_string(dcaSetup) + ";compatibilityIU=" + std::to_string(static_cast(compatibilityIU)); + metadata += ";pt=" + std::to_string(ptMin) + "," + std::to_string(ptMax) + ";eta=" + std::to_string(etaMin) + "," + std::to_string(etaMax); + metadata += ";producer.nSigmaForTimeCompat=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "nSigmaForTimeCompat")); + metadata += ";producer.timeMargin=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "timeMargin")); + metadata += ";producer.bcWindowForOneSigma=" + std::to_string(timeAssociationOption(context, "track-to-collision-association", "bcWindowForOneSigma")); + metadata += ";producer.usePVAssociation=" + std::to_string(timeUsePVAssociation); + metadata += ";producer.maxPvContributorsForLowMultReassoc=" + std::to_string(timeMaxPvContributorsForLowMultReassoc); + metadata += ";producer.includeUnassigned=" + std::to_string(static_cast(timeAssociationOption(context, "track-to-collision-association", "includeUnassigned"))); + chargedTimeAssociationConfig->SetTitle(metadata.c_str()); + LOG(info) << "Charged time association: " << metadata; + if (!cfgTimeAssociationQA) { + return; + } + registry.add("timeAssociation/candidatesPerTrack", "Time-compatible collisions per source track before event/track cuts;N_{coll}^{comp};Track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("timeAssociation/candidatesPerTrackSourceBaseline", "Source global/SDD + finite kinematics/DCA + eta only;N_{coll}^{comp};Track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("timeAssociation/candidatesVsSourceType", "All source tracks;N_{coll}^{comp};Source type", {HistType::kTH2D, {{101, -0.5, 100.5}, {3, -0.5, 2.5}}}); + registry.add("timeAssociation/candidatesVsAmbiguousBC", "AmbiguousTracks BC status (also for assigned tracks);N_{coll}^{comp};Ambiguous BC status", {HistType::kTH2D, {{101, -0.5, 100.5}, {3, -0.5, 2.5}}}); + registry.add("timeAssociation/candidatesVsRawTimeStatus", "Stored track time/resolution before PV substitution;N_{coll}^{comp};Raw time status", {HistType::kTH2D, {{101, -0.5, 100.5}, {4, -0.5, 3.5}}}); + registry.add("timeAssociation/candidatesVsOriginalTimeTest", "Fine time test at original collision; excludes coarse BC/search checks;N_{coll}^{comp};Original time test", {HistType::kTH2D, {{101, -0.5, 100.5}, {6, -0.5, 5.5}}}); + registry.add("timeAssociation/candidatesVsAcceptedTargets", "Raw candidates vs accepted PV-locked/closest-time target after producer cuts;N_{coll}^{comp} (raw TrackAssoc);N accepted targets (0 or 1)", {HistType::kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}}); + registry.add("timeAssociation/zeroCandidatePtEta", "Zero-candidate tracks with finite source pT/eta;Source p_{T} (GeV/c);Source #eta", {HistType::kTH2D, {{200, 0., 20.}, {100, -5., 5.}}}); + const auto setLabels = [](TAxis* axis, auto const& names) { + for (std::size_t bin = 0; bin < names.size(); ++bin) { + axis->SetBinLabel(bin + 1, names[bin]); + } + }; + setLabels(registry.get(HIST("timeAssociation/candidatesVsSourceType"))->GetYaxis(), + std::array{"no original collision", "assigned non-PV", "assigned PV contributor"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsAmbiguousBC"))->GetYaxis(), + std::array{"no ambiguous row", "invalid/empty BC slice", "valid BC slice"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsRawTimeStatus"))->GetYaxis(), + std::array{"finite time, res > 0", "finite time, res = 0", "finite time, res < 0", "nonfinite time or res"}); + setLabels(registry.get(HIST("timeAssociation/candidatesVsOriginalTimeTest"))->GetYaxis(), + std::array{"no original collision", "invalid effective time/res", "fails fine time test", "passes, original pair absent", "passes, original pair present", "invalid original collision index"}); + registry.add("timeAssociation/pairStatus", "Raw pairs and selected targets (target stages after vertex cut; stages overlap);stage;count", {HistType::kTH1D, {{10, 0, 10}}}); + const std::array labels{"producer pair", "duplicate pair", "same collision", "different collision", "unassigned source", "propagation failed", "baseline rejected", "baseline accepted", "PV locked target", "non-PV closest-time target"}; + for (std::size_t bin = 0; bin < labels.size(); ++bin) { + registry.get(HIST("timeAssociation/pairStatus"))->GetXaxis()->SetBinLabel(bin + 1, labels[bin]); + } + } + + template + void setChargedTimeField(GRP const* grp) + { + auto* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + if (!field) { + o2::base::Propagator::initFieldFromGRP(grp); + } else if (!field->TestBit(o2::field::MagneticField::kOverrideGRP) && + (timeFieldRun < 0 || timeL3Current != grp->getL3Current() || timeDipoleCurrent != grp->getDipoleCurrent() || timeUniformField != grp->getFieldUniformity())) { + // Keep the field object alive: Propagator can already hold its pointer. + field->rescaleField(grp->getL3Current(), grp->getDipoleCurrent(), grp->getFieldUniformity()); + } + o2::base::Propagator::Instance()->updateField(); + timeL3Current = grp->getL3Current(); + timeDipoleCurrent = grp->getDipoleCurrent(); + timeUniformField = grp->getFieldUniformity(); + } + + void prepareChargedTimeField(aod::BCsWithTimestamps::iterator const& bc) + { + if (timeFieldRun == bc.runNumber() && timeFieldTimestamp == bc.timestamp()) { + return; + } + if (timeIsRun3) { + auto* grp = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + if (!grp) { + LOGF(fatal, "Missing GRPMagField for charged time association"); + } + setChargedTimeField(grp); + } else { + auto* grp = ccdb->getForTimeStamp("GLO/GRP/GRP", bc.timestamp()); + if (!grp) { + LOGF(fatal, "Missing GRPObject for charged time association"); + } + setChargedTimeField(grp); + } + timeFieldRun = bc.runNumber(); + timeFieldTimestamp = bc.timestamp(); + } + + template + std::vector chargedTimeMultiplicity(aod::TrackAssoc const& pairs, TTracks const& tracks, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, aod::AmbiguousTracks const& ambiguousTracks) + { + std::vector counts(tracks.size(), 0); + std::vector lastCollision(tracks.size(), -1); + std::vector hasOriginalPair(cfgTimeAssociationQA ? tracks.size() : 0, false); + for (auto const& pair : pairs) { + const int64_t index = pair.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size())) { + LOGF(fatal, "TrackAssoc track index is outside the full source track table"); + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 0.5); + if (lastCollision[index] == pair.collisionId()) { + registry.fill(HIST("timeAssociation/pairStatus"), 1.5); + } + } + // TrackAssoc is grouped by collision. Count distinct target collisions, + // including those that will later fail event or target-track cuts. + if (lastCollision[index] != pair.collisionId()) { + ++counts[index]; + lastCollision[index] = pair.collisionId(); + } + if (cfgTimeAssociationQA && pair.collisionId() == tracks.rawIteratorAt(index).collisionId()) { + hasOriginalPair[index] = true; + } + } + if (!cfgTimeAssociationQA) { + return counts; + } + // Cache the first ambiguous row, matching the upstream lookup. Do not + // dereference its BC slice until its endpoints have been checked. + std::vector ambiguousBCStatus(tracks.size(), 0); + for (auto const& ambiguous : ambiguousTracks) { + const int64_t index = ambiguous.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || ambiguousBCStatus[index] != 0) { + continue; + } + auto const& ids = ambiguous.bcIds(); + const bool valid = ambiguous.has_bc() && ids[0] >= 0 && ids[1] >= ids[0] && ids[1] < bcs.size(); + ambiguousBCStatus[index] = valid ? 2 : 1; + } + // One unweighted entry per source track, including zero candidates, before + // ownership resolution or any target event/track selection. + for (auto const& track : tracks) { + const int64_t index = track.globalIndex() - tracks.offset(); + const auto count = counts[index]; + registry.fill(HIST("timeAssociation/candidatesPerTrack"), count); + const int sourceType = !track.has_collision() ? 0 : (track.isPVContributor() ? 2 : 1); + registry.fill(HIST("timeAssociation/candidatesVsSourceType"), count, sourceType); + registry.fill(HIST("timeAssociation/candidatesVsAmbiguousBC"), count, ambiguousBCStatus[index]); + const bool finiteTime = std::isfinite(track.trackTime()) && std::isfinite(track.trackTimeRes()); + const int rawTimeStatus = !finiteTime ? 3 : (track.trackTimeRes() > 0.f ? 0 : (track.trackTimeRes() == 0.f ? 1 : 2)); + registry.fill(HIST("timeAssociation/candidatesVsRawTimeStatus"), count, rawTimeStatus); + const bool finite = std::isfinite(track.pt()) && std::isfinite(track.eta()) && std::isfinite(track.phi()) && std::isfinite(track.dcaXY()) && std::isfinite(track.dcaZ()); + if (finite && (track.isGlobalTrack() || track.isGlobalTrackSDD()) && std::fabs(track.eta()) < cutEta) { + // Diagnostic at the source vertex only; not the final flow selection. + registry.fill(HIST("timeAssociation/candidatesPerTrackSourceBaseline"), count); + } + if (count == 0 && std::isfinite(track.pt()) && std::isfinite(track.eta())) { + registry.fill(HIST("timeAssociation/zeroCandidatePtEta"), track.pt(), track.eta()); + } + int originalTimeStatus = 0; + if (track.has_collision()) { + const int64_t collisionIndex = track.collisionId() - collisions.offset(); + originalTimeStatus = 5; + if (collisionIndex >= 0 && collisionIndex < collisions.size()) { + auto collision = collisions.rawIteratorAt(collisionIndex); + // Match the public associator's PV-time substitution, including mode 2. + const bool usePVTime = track.isPVContributor() && + (timeUsePVAssociation == aod::track_association::PVContrReassocOpt::OnlySameBc || + (timeUsePVAssociation == aod::track_association::PVContrReassocOpt::SameBcAndLowMult && + collision.numContrib() > timeMaxPvContributorsForLowMultReassoc)); + const float trackTime = usePVTime ? collision.collisionTime() : track.trackTime(); + const float trackRes = usePVTime ? o2::constants::lhc::LHCBunchSpacingNS : track.trackTimeRes(); + const float collisionRes = collision.collisionTimeRes(); + originalTimeStatus = 1; + if (std::isfinite(trackTime) && std::isfinite(trackRes) && trackRes >= 0.f && std::isfinite(collision.collisionTime()) && std::isfinite(collisionRes) && collisionRes >= 0.f) { + float threshold = trackRes; + if (!usePVTime) { + threshold = TESTBIT(track.flags(), aod::track::TrackTimeResIsRange) + ? trackRes + timeNSigma * std::sqrt(collisionRes * collisionRes) + timeMargin + : timeNSigma * std::sqrt(collisionRes * collisionRes + trackRes * trackRes) + timeMargin; + } + // Same BC as the original collision, hence bcOffset = 0. This + // checks only fine time compatibility, not the upstream BC cache + // or iterator window. A pass with no pair needs upstream study. + originalTimeStatus = std::fabs(trackTime - collision.collisionTime()) < threshold ? (hasOriginalPair[index] ? 4 : 3) : 2; + } + } + } + registry.fill(HIST("timeAssociation/candidatesVsOriginalTimeTest"), count, originalTimeStatus); + } + return counts; + } + + template + std::vector> selectChargedTimeTargets(aod::TrackAssoc const& pairs, TTracks const& tracks, + aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, aod::AmbiguousTracks const& ambiguousTracks) + { + std::vector targetIndices(tracks.size(), -1); + std::vector referenceBC(tracks.size(), -1); + std::vector bestDistance(tracks.size(), std::numeric_limits::infinity()); + const auto globalBC = [&bcs](int64_t id) -> int64_t { + const int64_t index = id - bcs.offset(); + if (index < 0 || index >= static_cast(bcs.size())) { + return -1; + } + return static_cast(bcs.rawIteratorAt(index).globalBC()); + }; + for (auto const& track : tracks) { + const int64_t index = track.globalIndex() - tracks.offset(); + const int64_t original = track.collisionId() - collisions.offset(); + if (!track.has_collision() || original < 0 || original >= static_cast(collisions.size())) { + continue; + } + if (track.isPVContributor()) { + // A PV contributor defines this vertex. Restore its original pair even + // when the public time associator supplied zero candidates. + targetIndices[index] = original; + } else { + referenceBC[index] = globalBC(collisions.rawIteratorAt(original).bcId()); + } + } + // Unassigned tracks use the first BC of their first ambiguous row, exactly + // as in the public associator; never replace an invalid first row by another. + std::vector seenAmbiguous(tracks.size(), false); + for (auto const& ambiguous : ambiguousTracks) { + const int64_t index = ambiguous.trackId() - tracks.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || seenAmbiguous[index]) { + continue; + } + seenAmbiguous[index] = true; + auto const& ids = ambiguous.bcIds(); + if (!tracks.rawIteratorAt(index).has_collision() && ambiguous.has_bc() && ids[1] >= ids[0] && globalBC(ids[1]) >= 0) { + referenceBC[index] = globalBC(ids[0]); + } + } + for (auto const& pair : pairs) { + const int64_t index = pair.trackId() - tracks.offset(); + const int64_t target = pair.collisionId() - collisions.offset(); + if (index < 0 || index >= static_cast(tracks.size()) || target < 0 || target >= static_cast(collisions.size())) { + LOGF(fatal, "TrackAssoc index is outside the full source track/collision tables"); + } + auto track = tracks.rawIteratorAt(index); + if (track.isPVContributor() || referenceBC[index] < 0) { + continue; + } + auto collision = collisions.rawIteratorAt(target); + const int64_t collisionBC = globalBC(collision.bcId()); + if (collisionBC < 0) { + continue; + } + // Compare in a common BC reference. Subtract the integer BCs before the + // floating-point conversion to retain precision for large global BCs. + const double deltaTime = static_cast(track.trackTime()) - collision.collisionTime() + + static_cast(referenceBC[index] - collisionBC) * o2::constants::lhc::LHCBunchSpacingNS; + const double distance = std::abs(deltaTime); + if (std::isfinite(distance) && (distance < bestDistance[index] || (distance == bestDistance[index] && target < targetIndices[index]))) { + targetIndices[index] = target; + bestDistance[index] = distance; + } + } + // Resolve ownership before any event/vertex, propagation or quality cuts. + // A rejected best target has no fallback; each source can appear only once. + std::vector> selected(collisions.size()); + for (std::size_t index = 0; index < targetIndices.size(); ++index) { + if (targetIndices[index] >= 0) { + selected[targetIndices[index]].push_back(index); + } + } + return selected; + } + + template + void fillChargedTimeTracks(TCollision const& collision, std::vector const& selected, TTracks const& tracks, std::vector const& counts, std::vector& acceptedCounts) + { + bool fieldReady = false; + for (const auto index : selected) { + auto source = tracks.rawIteratorAt(index); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), source.isPVContributor() ? 8.5 : 9.5); + } + TimeAssociatedChargedTrack candidate(source); + const bool sameCollision = source.collisionId() == collision.globalIndex(); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), sameCollision ? 2.5 : 3.5); + } + if (cfgTimeAssociationQA && !source.has_collision()) { + registry.fill(HIST("timeAssociation/pairStatus"), 4.5); + } + if (!sameCollision) { + if (!fieldReady) { + prepareChargedTimeField(collision.template bc_as()); + fieldReady = true; + } + auto parameters = getTrackPar(source); + std::array dca{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, parameters, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, &dca)) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 5.5); + } + continue; + } + candidate.targetPt = parameters.getPt(); + candidate.targetEta = parameters.getEta(); + candidate.targetPhi = parameters.getPhi(); + candidate.targetDCAxy = dca[0]; + candidate.targetDCAz = dca[1]; + } + const bool finite = std::isfinite(candidate.pt()) && std::isfinite(candidate.eta()) && std::isfinite(candidate.phi()) && std::isfinite(candidate.dcaXY()) && std::isfinite(candidate.dcaZ()); + const bool baseline = finite && (sameCollision ? (source.isGlobalTrack() || source.isGlobalTrackSDD()) : (timeGlobalSelection.IsSelected(candidate) || (!timeIsRun3 && timeSDDSelection.IsSelected(candidate)))); + if (!baseline || std::fabs(candidate.eta()) >= cutEta) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 6.5); + } + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/pairStatus"), 7.5); + ++acceptedCounts[index]; + } + timeTracks(collision.globalIndex(), source.globalIndex(), + candidate.pt(), candidate.eta(), candidate.phi(), candidate.dcaXY(), candidate.dcaZ(), + source.hasITS(), source.hasTPC(), source.itsNCls(), source.itsChi2NCl(), + source.tpcNClsCrossedRows(), source.tpcNClsFound(), source.tpcChi2NCl(), counts[index]); + } + } + + void processTimeTracks(aod::Collisions const& collisions, aod::BCsWithTimestamps const& bcs, + aod::TrackAssoc const& pairs, SourceTracks const& sources, aod::AmbiguousTracks const& ambiguousTracks) + { + // Count all producer candidates before vertex, event or target-track cuts. + auto counts = chargedTimeMultiplicity(pairs, sources, collisions, bcs, ambiguousTracks); + auto selected = selectChargedTimeTargets(pairs, sources, collisions, bcs, ambiguousTracks); + std::vector acceptedCounts(cfgTimeAssociationQA ? sources.size() : 0, 0); + for (auto const& collision : collisions) { + if (std::fabs(collision.posZ()) < cutVertex) { + fillChargedTimeTracks(collision, selected[collision.globalIndex() - collisions.offset()], sources, counts, acceptedCounts); + } + } + if (cfgTimeAssociationQA) { + for (std::size_t index = 0; index < counts.size(); ++index) { + registry.fill(HIST("timeAssociation/candidatesVsAcceptedTargets"), counts[index], acceptedCounts[index]); + } + } + } + PROCESS_SWITCH(PidFlowChargedTimeProducer, processTimeTracks, "Build shared charged time-associated track table; no PID", false); + + void processDummy(aod::Collisions const&) {} + PROCESS_SWITCH(PidFlowChargedTimeProducer, processDummy, "Idle entry for original collisionId analysis", true); +}; struct PidFlowPtCorr { // configurable double minVal4Float = 1e-3; - static constexpr std::size_t NSpecies{3}; - O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5, "Chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgDeltaPhiLocDen, int, 3, "Number of delta phi for local density, 200 bins in 2 pi") + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgCutChi2prTPCcls{"cfgCutChi2prTPCcls", 2.5, "Chi2 per TPC clusters"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; + Configurable cfgUseMultiplicity{"cfgUseMultiplicity", false, "Use raw PV-contributor count in |eta| < 0.8 instead of centrality for output axes and NUE lookup; configure axisMultiplicity and disable cfgDoMultPVCut"}; + Configurable cfgTimeAssociationQA{"cfgTimeAssociationQA", true, "Create and fill time-association producer, comparison and MC QA; keep event counters and flow/NUA/NUE outputs"}; + ConfigurableAxis cfgaxisAssociationNch{"cfgaxisAssociationNch", {601, -5., 6005.}, "Association QA Nch (default bin width: 10 tracks; includes zero)"}; + ConfigurableAxis cfgaxisAssociationChangedNch{"cfgaxisAssociationChangedNch", {1001, -0.5, 1000.5}, "Association QA lost/gained track count"}; + ConfigurableAxis cfgaxisAssociationDeltaNch{"cfgaxisAssociationDeltaNch", {4001, -2000.5, 2000.5}, "Association QA Nch(time) minus Nch(original)"}; + ConfigurableAxis cfgaxisAssociationOccupancy{"cfgaxisAssociationOccupancy", {200, -0.5, 19999.5}, "Association QA track occupancy (-1 goes to underflow)"}; struct : ConfigurableGroup { std::string prefix = "trkQualityOpts"; // track selections - O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") - O2_DEFINE_CONFIGURABLE(cfgRangeEta, float, 0.4f, "Eta range for mean Pt") - O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for ref tracks") - O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "Maximal pT for ref tracks") + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable cfgRangeEta{"cfgRangeEta", 0.4f, "Independent |eta| half-width for mean-pT particles"}; + Configurable cfgCutPtMin{"cfgCutPtMin", 0.2f, "Minimal pT for ref tracks"}; + Configurable cfgCutPtMax{"cfgCutPtMax", 10.0f, "Maximal pT for ref tracks"}; + Configurable cfgCutPtMinPi{"cfgCutPtMinPi", 0.2f, "Minimal pT for pion POI"}; + Configurable cfgCutPtMaxPi{"cfgCutPtMaxPi", 10.0f, "Maximal pT for pion POI"}; + Configurable cfgCutPtMinKa{"cfgCutPtMinKa", 0.2f, "Minimal pT for kaon POI"}; + Configurable cfgCutPtMaxKa{"cfgCutPtMaxKa", 10.0f, "Maximal pT for kaon POI"}; + Configurable cfgCutPtMinPr{"cfgCutPtMinPr", 0.2f, "Minimal pT for proton POI"}; + Configurable cfgCutPtMaxPr{"cfgCutPtMaxPr", 10.0f, "Maximal pT for proton POI"}; // track quality selections for daughter track - O2_DEFINE_CONFIGURABLE(cfgITSNCls, int, 5, "check minimum number of ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgTPCNCls, int, 50, "check minimum number of TPC hits") - O2_DEFINE_CONFIGURABLE(cfgTPCCrossedRows, int, 70, "check minimum number of TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgITSChi2NDF, double, 2.5, "check ITS Chi2NDF") - O2_DEFINE_CONFIGURABLE(cfgCheckGlobalTrack, bool, false, "check global track") + Configurable cfgITSNCls{"cfgITSNCls", 5, "check minimum number of ITS clusters"}; + Configurable cfgTPCNCls{"cfgTPCNCls", 50, "check minimum number of TPC hits"}; + Configurable cfgTPCCrossedRows{"cfgTPCCrossedRows", 70, "check minimum number of TPC crossed rows"}; + Configurable cfgITSChi2NDF{"cfgITSChi2NDF", 2.5, "check ITS Chi2NDF"}; + Configurable cfgCheckGlobalTrack{"cfgCheckGlobalTrack", false, "check global track"}; } trkQualityOpts; struct : ConfigurableGroup { std::string prefix = "evtSelOpts"; - O2_DEFINE_CONFIGURABLE(cfgDoTVXinTRD, bool, false, "check kTVXinTRD") - O2_DEFINE_CONFIGURABLE(cfgDoNoTimeFrameBorder, bool, true, "check kNoTimeFrameBorder") - O2_DEFINE_CONFIGURABLE(cfgDoNoITSROFrameBorder, bool, true, "check kNoITSROFrameBorder") - O2_DEFINE_CONFIGURABLE(cfgDoNoSameBunchPileup, bool, true, "check kNoITSROFrameBorder") - O2_DEFINE_CONFIGURABLE(cfgDoIsGoodZvtxFT0vsPV, bool, true, "check kIsGoodZvtxFT0vsPV") - O2_DEFINE_CONFIGURABLE(cfgDoNoCollInTimeRangeStandard, bool, true, "check kNoCollInTimeRangeStandard") - O2_DEFINE_CONFIGURABLE(cfgDoIsGoodITSLayersAll, bool, true, "check kIsGoodITSLayersAll") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 3000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgDoMultPVCut, bool, true, "do multNTracksPV vs cent cut") - O2_DEFINE_CONFIGURABLE(cfgMultPVCut, std::vector, (std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}), "Used MultPVCut function parameter") - O2_DEFINE_CONFIGURABLE(cfgDoV0AT0Acut, bool, true, "do V0A-T0A cut") - O2_DEFINE_CONFIGURABLE(cfgCutminIR, float, -1, "cut min IR") - O2_DEFINE_CONFIGURABLE(cfgCutmaxIR, float, 3000, "cut max IR") + Configurable cfgDoTVXinTRD{"cfgDoTVXinTRD", false, "check kTVXinTRD"}; + Configurable cfgDoNoTimeFrameBorder{"cfgDoNoTimeFrameBorder", true, "check kNoTimeFrameBorder"}; + Configurable cfgDoNoITSROFrameBorder{"cfgDoNoITSROFrameBorder", true, "check kNoITSROFrameBorder"}; + Configurable cfgDoNoSameBunchPileup{"cfgDoNoSameBunchPileup", true, "check kNoITSROFrameBorder"}; + Configurable cfgDoIsGoodZvtxFT0vsPV{"cfgDoIsGoodZvtxFT0vsPV", true, "check kIsGoodZvtxFT0vsPV"}; + Configurable cfgDoNoCollInTimeRangeStandard{"cfgDoNoCollInTimeRangeStandard", true, "check kNoCollInTimeRangeStandard"}; + Configurable cfgDoIsGoodITSLayersAll{"cfgDoIsGoodITSLayersAll", true, "check kIsGoodITSLayersAll"}; + Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; + Configurable cfgDoMultPVCut{"cfgDoMultPVCut", true, "do multNTracksPV vs cent cut"}; + Configurable> cfgMultPVCut{"cfgMultPVCut", (std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}), "Used MultPVCut function parameter"}; + Configurable cfgDoV0AT0Acut{"cfgDoV0AT0Acut", true, "do V0A-T0A cut"}; + Configurable cfgCutminIR{"cfgCutminIR", -1, "cut min IR"}; + Configurable cfgCutmaxIR{"cfgCutmaxIR", 3000, "cut max IR"}; + Configurable cfgIRSource{"cfgIRSource", "ZNC hadronic", "Interaction-rate source for CTP rate fetcher. Use T0VTX for pp/OO and ZNC hadronic for Pb-Pb; empty disables fetching"}; } evtSeleOpts; struct : ConfigurableGroup { std::string prefix = "dcaCutOpts"; TF1* fPtDepDCAxy = nullptr; - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") - O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 0, "0: disable; Cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track"); - O2_DEFINE_CONFIGURABLE(cfgDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut") + Configurable cfgCutDCAz{"cfgCutDCAz", 2.0f, "max DCA to vertex z"}; + Configurable cfgDCAxyNSigma{"cfgDCAxyNSigma", 0, "0: disable; Cut on number of sigma deviations from expected DCA in the transverse direction, nsigma=7 is the same with global track"}; + Configurable cfgDCAxy{"cfgDCAxy", "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"}; } dcaCutOpts; - O2_DEFINE_CONFIGURABLE(cfgNSigmapid, std::vector, (std::vector{3, 3, 3, 9, 9, 9, 9, 9, 9}), "tpc, tof and its NSigma for Pion Proton Kaon") - struct : ConfigurableGroup { std::string prefix = "correctionPathOpts"; - O2_DEFINE_CONFIGURABLE(cfgAcceptancePath, std::vector, (std::vector{"Users/f/fcui/NUA/NUAREFPartical", "Users/f/fcui/NUA/NUAK0s", "Users/f/fcui/NUA/NUALambda", "Users/f/fcui/NUA/NUAXi", "Users/f/fcui/NUA/NUAOmega"}), "CCDB path to acceptance object") - - O2_DEFINE_CONFIGURABLE(cfgEfficiencyPath, std::vector, (std::vector{"PathtoRef"}), "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgEfficiency2DPath, std::vector, (std::vector{"PathtoRef"}), "CCDB path to efficiency(pt, cent) object") - O2_DEFINE_CONFIGURABLE(cfgPidEfficiencyPath, std::vector, (std::vector{"PathtoRef"}), "Pi, Ka, Pr, CCDB path to PID efficiency(pt, cent) object") + Configurable> cfgAcceptancePath{"cfgAcceptancePath", (std::vector{"Users/f/fcui/NUA/NUAREFPartical", "Users/f/fcui/NUA/NUAK0s", "Users/f/fcui/NUA/NUALambda", "Users/f/fcui/NUA/NUAXi", "Users/f/fcui/NUA/NUAOmega"}), "CCDB path to acceptance object"}; - O2_DEFINE_CONFIGURABLE(cfgEfficiencyPath4ITSOnly, std::vector, (std::vector{"PathtoRef"}), "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgEfficiency2DPath4ITSOnly, std::vector, (std::vector{"PathtoRef"}), "CCDB path to efficiency(pt, cent) object") - O2_DEFINE_CONFIGURABLE(cfgPidEfficiencyPath4ITSOnly, std::vector, (std::vector{"PathtoRef"}), "Pi, Ka, Pr, CCDB path to PID efficiency(pt, cent) object") + Configurable> cfgAcceptancePathWithPt{"cfgAcceptancePathWithPt", (std::vector{"Users/f/fcui/NUA/NUAREFParticalWithPt"}), "CCDB path to acceptance THnSparse(phi,eta,vz,pt) object, same content as cfgAcceptancePath but with an extra pt axis"}; - O2_DEFINE_CONFIGURABLE(cfgNUEOption, int, 1, "do NUE, 1: use 1D eff, 2: Using Eff(pt, cent), 3: use pid 1D eff, 4: use pid 2D eff, other: dont do NUE") + Configurable> cfgEfficiencyPath{"cfgEfficiencyPath", (std::vector{"PathtoRef"}), "CCDB path to efficiency object"}; } correctionPathOpts; - O2_DEFINE_CONFIGURABLE(cfgRunNumbers, std::vector, (std::vector{544095, 544098, 544116, 544121, 544122, 544123, 544124}), "Preconfigured run numbers") - O2_DEFINE_CONFIGURABLE(cfgEtaGap, float, 0.4, "eta gap for cumulant calculation, note that gap is -0.4 ~ 0.4 total 0.8, note that eta range for meanpt calculation needs to be within etagap") - O2_DEFINE_CONFIGURABLE(cfgFlowNbootstrap, int, 30, "Number of subsamples for bootstrap") + Configurable> cfgRunNumbers{"cfgRunNumbers", (std::vector{544095, 544098, 544116, 544121, 544122, 544123, 544124}), "Preconfigured run numbers"}; + Configurable cfgEtaGap{"cfgEtaGap", 0.4, "Eta-gap half-width for flow particles; 0.4 selects (-etaMax,-0.4) and (0.4,etaMax)"}; + Configurable cfgFlowNbootstrap{"cfgFlowNbootstrap", 30, "Number of subsamples for bootstrap"}; + + struct : ConfigurableGroup { + std::string prefix = "chargedDifferentialOpts"; + Configurable cfgRefPtMin{"cfgRefPtMin", 0.2f, "Charged differential v2{2}: reference pT lower bound (GeV/c), independent of integrated flow"}; + Configurable cfgRefPtMax{"cfgRefPtMax", 3.0f, "Charged differential v2{2}: reference pT upper bound (GeV/c), independent of integrated flow"}; + ConfigurableAxis cfgaxisPt{"cfgaxisPt", {VARIABLE_WIDTH, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0}, "Charged differential v2{2}: POI pT binning and acceptance [first edge, last edge) in GeV/c"}; + } chargedDifferentialOpts; // switch struct : ConfigurableGroup { std::string prefix = "switchsOpts"; - O2_DEFINE_CONFIGURABLE(cfgDoLocDenCorr, bool, false, "do local density corr"); - O2_DEFINE_CONFIGURABLE(cfgOutputrunbyrun, bool, false, "OPEN IF USE FUNCTION(fillcorrectiongraph) Fill and output NUA weights run by run"); - O2_DEFINE_CONFIGURABLE(cfgOutPutMC, bool, false, "Fill MC graphs, note that if the processMCgen is open,this MUST be open"); - O2_DEFINE_CONFIGURABLE(cfgOutputQA, bool, false, "OPEN IF USE FUNCTION(detectorPidQa) do QA"); - O2_DEFINE_CONFIGURABLE(cfgDebugMyCode, bool, false, "output some graph for code debug"); - O2_DEFINE_CONFIGURABLE(cfgOutPutPtSpectra, bool, false, "output pt spectra for data, MC and RECO"); - O2_DEFINE_CONFIGURABLE(cfgCheck2MethodDiff, bool, false, "check difference between v2' && v2''"); - O2_DEFINE_CONFIGURABLE(cfgUseITSOnly4MeanPt, bool, false, "use ITS only to calculate mean pt"); - O2_DEFINE_CONFIGURABLE(cfgClosureTest, int, 0, "choose (val) percent particle from charged to pass Pion PID selection"); + Configurable cfgOutputrunbyrun{"cfgOutputrunbyrun", false, "OPEN IF USE FUNCTION(fillcorrectiongraph) Fill and output NUA weights run by run"}; + Configurable cfgOutPutMC{"cfgOutPutMC", false, "Fill MC graphs, note that if the processMCgen is open,this MUST be open"}; + Configurable cfgOutputQA{"cfgOutputQA", false, "OPEN IF USE FUNCTION(detectorPidQa) do QA"}; + Configurable cfgDebugMyCode{"cfgDebugMyCode", false, "output some graph for code debug"}; + Configurable cfgOutPutPtSpectra{"cfgOutPutPtSpectra", false, "output pt spectra for data, MC and RECO"}; + Configurable cfgCheck2MethodDiff{"cfgCheck2MethodDiff", false, "check difference between v2' && v2''"}; + Configurable cfgClosureTest{"cfgClosureTest", 0, "Nonzero enables extra pion mean-pT weight QA profiles"}; + Configurable cfgOutPutMC1D{"cfgOutPutMC1D", true, "Fill MC graphs, note that if the processMCgen is open,this MUST be open"}; + Configurable cfgAddPidResponseMatrixHistograms{"cfgAddPidResponseMatrixHistograms", false, "Add PID response matrix histograms; enable together with processPidResponseMatrix"}; + Configurable cfgAddC22DeltaPtHistograms{"cfgAddC22DeltaPtHistograms", false, "Add histograms for processDataC22DeltaPt; enable together with processDataC22DeltaPt"}; + Configurable cfgAddPidPtCorrelationProfiles{"cfgAddPidPtCorrelationProfiles", false, "Add FlowContainer profiles for processPidPtCorrelations; enable together with processPidPtCorrelations"}; + Configurable cfgC22DeltaPtUsePure{"cfgC22DeltaPtUsePure", false, "true: use PID POI-POI Pure profiles; false: use PID POI-ref and ref-ref profiles"}; + Configurable cfgAddMeanPtCentNbsHistograms{"cfgAddMeanPtCentNbsHistograms", false, "Add meanptCentNbs TProfile3D histograms"}; + // These switches suppress result allocation and filling only. Charged reference + // Q-vectors are still calculated when a PID or unidentified output needs them. + Configurable cfgOutputCharged{"cfgOutputCharged", true, "Output charged-particle flow, mean-pT, and c22DeltaPt observables"}; + Configurable cfgOutputPion{"cfgOutputPion", true, "Output pion flow, mean-pT, and c22DeltaPt observables"}; + Configurable cfgOutputKaon{"cfgOutputKaon", true, "Output kaon flow, mean-pT, and c22DeltaPt observables"}; + Configurable cfgOutputProton{"cfgOutputProton", true, "Output proton flow, mean-pT, and c22DeltaPt observables"}; + Configurable cfgOutputUnidentified{"cfgOutputUnidentified", true, "Output PID-failed or ambiguous flow, mean-pT, and c22DeltaPt observables"}; + + Configurable cfgProcessQAOutput{"cfgProcessQAOutput", false, "QA plots for processQA"}; + Configurable cfgUseNUAWithPt{"cfgUseNUAWithPt", false, "false: use original GFWWeights (phi,eta,vz) NUA; true: use THnSparse (phi,eta,vz,pt) NUA loaded from cfgAcceptancePathWithPt"}; } switchsOpts; - /** - * @brief cfg for PID pt range - * @details default datas are from run2, note that separate pi-k and k-p needs to use difference pt range - */ - // separate pi and k + // TOF momentum ranges used by circle PID. struct : ConfigurableGroup { std::string prefix = "pidPtRangeOpts"; - O2_DEFINE_CONFIGURABLE(cfgPtMin4ITSPiKa, float, 0.2, "pt min for ITS to separate pi and k"); - O2_DEFINE_CONFIGURABLE(cfgPtMax4ITSPiKa, float, 0.8, "pt max for ITS to separate pi and k"); - O2_DEFINE_CONFIGURABLE(cfgPtMin4TOFPiKa, float, 0.5, "pt min for TOF to separate pi and k"); - O2_DEFINE_CONFIGURABLE(cfgPtMax4TOFPiKa, float, 3.0, "pt max for TOF to separate pi and k"); - O2_DEFINE_CONFIGURABLE(cfgPtMin4TPCPiKa, float, 0.2, "pt min for TPC to separate pi and k"); - O2_DEFINE_CONFIGURABLE(cfgPtMax4TPCPiKa, float, 0.6, "pt max for TPC to separate pi and k"); - // end separate pi and k - - // separate k-p - O2_DEFINE_CONFIGURABLE(cfgPtMin4ITSKaPr, float, 0.4, "pt min for ITS to separate k and p"); - O2_DEFINE_CONFIGURABLE(cfgPtMax4ITSKaPr, float, 1.4, "pt max for ITS to separate k and p"); - O2_DEFINE_CONFIGURABLE(cfgPtMin4TOFKaPr, float, 0.6, "pt min for TOF to separate k and p"); - O2_DEFINE_CONFIGURABLE(cfgPtMax4TOFKaPr, float, 3.0, "pt max for TOF to separate k and p"); - O2_DEFINE_CONFIGURABLE(cfgPtMin4TPCKaPr, float, 0.2, "pt min for TPC to separate k and p"); - O2_DEFINE_CONFIGURABLE(cfgPtMax4TPCKaPr, float, 1.0, "pt max for TPC to separate k and p"); + Configurable cfgPtMin4TOFPiKa{"cfgPtMin4TOFPiKa", 0.5, "pt min for TOF to separate pi and k"}; + Configurable cfgPtMax4TOFPiKa{"cfgPtMax4TOFPiKa", 3.0, "pt max for TOF to separate pi and k"}; + Configurable cfgPtMin4TOFKaPr{"cfgPtMin4TOFKaPr", 0.6, "pt min for TOF to separate k and p"}; + Configurable cfgPtMax4TOFKaPr{"cfgPtMax4TOFKaPr", 3.0, "pt max for TOF to separate k and p"}; } pidPtRangeOpts; - // end separate k-p - // end cfg for PID pt range struct : ConfigurableGroup { std::string prefix = "circleCutOpts"; - // Switch to enable/disable circular cut PID - O2_DEFINE_CONFIGURABLE(cfgUseCircleCutPID, bool, true, "Enable circular cut PID method"); - // TOF pT threshold: above this pt, TOF signal is required - O2_DEFINE_CONFIGURABLE(cfgCircleCutTofPtCut, float, 0.5, "TOF pT threshold for requiring TOF signal"); + Configurable cfgCircleCutTofPtCut{"cfgCircleCutTofPtCut", 0.5, "TOF pT threshold for requiring TOF signal"}; // Circular cut (TPC+TOF) values (radius squared, default = 2^2 = 4) - O2_DEFINE_CONFIGURABLE(cfgCircleCutSigmaPi, float, 4.0, "Circular cut radius squared for Pion (TPC+TOF)"); - O2_DEFINE_CONFIGURABLE(cfgCircleCutSigmaKa, float, 4.0, "Circular cut radius squared for Kaon (TPC+TOF)"); - O2_DEFINE_CONFIGURABLE(cfgCircleCutSigmaPr, float, 4.0, "Circular cut radius squared for Proton (TPC+TOF)"); - - // TPC-only fallback cut values (|nsigma| < cut) - O2_DEFINE_CONFIGURABLE(cfgCircleCutTPCPi, float, 2.0, "TPC-only nsigma cut for Pion"); - O2_DEFINE_CONFIGURABLE(cfgCircleCutTPCKa, float, 2.0, "TPC-only nsigma cut for Kaon"); - O2_DEFINE_CONFIGURABLE(cfgCircleCutTPCPr, float, 2.0, "TPC-only nsigma cut for Proton"); + Configurable cfgCircleCutSigmaPi{"cfgCircleCutSigmaPi", 4.0, "Circular cut radius squared for Pion (TPC+TOF)"}; + Configurable cfgCircleCutSigmaKa{"cfgCircleCutSigmaKa", 4.0, "Circular cut radius squared for Kaon (TPC+TOF)"}; + Configurable cfgCircleCutSigmaPr{"cfgCircleCutSigmaPr", 4.0, "Circular cut radius squared for Proton (TPC+TOF)"}; + + // TPC fallback cuts, combined with ITS below cfgCircleCutITSPtMax. + Configurable cfgCircleCutTPCPi{"cfgCircleCutTPCPi", 2.0, "TPC fallback nsigma cut for Pion"}; + Configurable cfgCircleCutTPCKa{"cfgCircleCutTPCKa", 2.0, "TPC fallback nsigma cut for Kaon"}; + Configurable cfgCircleCutTPCPr{"cfgCircleCutTPCPr", 2.0, "TPC fallback nsigma cut for Proton"}; + Configurable cfgCircleCutITSPtMax{"cfgCircleCutITSPtMax", 0.5, "Require ITS PID below this pT (GeV/c); 0 disables"}; + Configurable cfgCircleCutITSPi{"cfgCircleCutITSPi", 2.0, "Low-pT ITS absolute nsigma cut for Pion"}; + Configurable cfgCircleCutITSKa{"cfgCircleCutITSKa", 2.0, "Low-pT ITS absolute nsigma cut for Kaon"}; + Configurable cfgCircleCutITSPr{"cfgCircleCutITSPr", 2.0, "Low-pT ITS absolute nsigma cut for Proton"}; } circleCutOpts; + Configurable cfgTPCPIDClustersMinFunction{"cfgTPCPIDClustersMinFunction", "0", "Minimum TPC PID clusters as a ROOT formula; x = tpcNClsFound(), numeric coefficients, 0 imposes no lower bound"}; + std::unique_ptr fTPCPIDClustersMin; + struct : ConfigurableGroup { std::string prefix = "particleAbundanceOpts"; ConfigurableAxis cfgaxisAbundancePi{"cfgaxisAbundancePi", {100, 0, 1100}, "axis for Abundance Pi"}; ConfigurableAxis cfgaxisAbundanceKa{"cfgaxisAbundanceKa", {100, 0, 200}, "axis for Abundance ka"}; ConfigurableAxis cfgaxisAbundancePr{"cfgaxisAbundancePr", {100, 0, 50}, "axis for Abundance Pr"}; - O2_DEFINE_CONFIGURABLE(cfgOutPutAbundanceDis, bool, false, "out put hists for pid particle Abundance QA"); + Configurable cfgOutPutAbundanceDis{"cfgOutPutAbundanceDis", false, "out put hists for pid particle Abundance QA"}; + Configurable cfgOutputAbundanceYields{"cfgOutputAbundanceYields", false, "Output detector-PID abundance yields for pion, kaon, proton, and unidentified"}; } particleAbundanceOpts; ConfigurableAxis cfgaxisVertex{"cfgaxisVertex", {20, -10, 10}, "vertex axis for histograms"}; ConfigurableAxis cfgaxisPhi{"cfgaxisPhi", {60, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; ConfigurableAxis cfgaxisEta{"cfgaxisEta", {40, -1., 1.}, "eta axis for histograms"}; ConfigurableAxis cfgaxisPt{"cfgaxisPt", {VARIABLE_WIDTH, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.20, 2.40, 2.60, 2.80, 3.00, 3.50, 4.00, 4.50, 5.00, 5.50, 6.00, 10.0}, "pt (GeV)"}; + ConfigurableAxis cfgaxisTPCDedx{"cfgaxisTPCDedx", {500, 0.0f, 1000.0f}, "d#it{E}/d#it{x} axis for detector-PID QA"}; + ConfigurableAxis cfgaxisTOFDelta{"cfgaxisTOFDelta", {200, -1000.0f, 1000.0f}, "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} axis for detector-PID QA"}; + ConfigurableAxis cfgaxisTOFTime{"cfgaxisTOFTime", {10000, 0.0f, 2.0e6f}, "TOF #it{t} - #it{t}_{ev} (ps) axis for detector-PID QA"}; + ConfigurableAxis cfgaxisTPCPIDClusters{"cfgaxisTPCPIDClusters", {201, -0.5, 200.5}, "TPC clusters used for PID"}; + ConfigurableAxis cfgaxisPIDNSigma{"cfgaxisPIDNSigma", {240, -12., 12.}, "Signed TPC n-sigma axis for PID-quality QA"}; + ConfigurableAxis cfgaxisPIDMomentum{"cfgaxisPIDMomentum", {1000, 0., 10.}, "Total momentum for ITS cluster-size QA (GeV/c)"}; + ConfigurableAxis cfgaxisITSNSigma{"cfgaxisITSNSigma", {161, -12.075, 12.075}, "Signed ITS n-sigma; zero at bin center"}; + ConfigurableAxis cfgaxisITSPt{"cfgaxisITSPt", {300, 0., 3.}, "Fine pT axis for ITS response QA (GeV/c)"}; + ConfigurableAxis cfgaxisITSClusterSize{"cfgaxisITSClusterSize", {101, -0.075, 15.075}, "Truncated mean ITS cluster size divided by cosh(eta)"}; ConfigurableAxis cfgaxisMeanPt{"cfgaxisMeanPt", {300, 0, 3}, "pt (GeV)"}; ConfigurableAxis cfgaxisNch{"cfgaxisNch", {3000, 0.5, 3000.5}, "Nch"}; - ConfigurableAxis cfgaxisLocalDensity{"cfgaxisLocalDensity", {200, 0, 600}, "local density"}; ConfigurableAxis cfgaxisRun{"cfgaxisRun", {7, 0, 7}, "axis of runs in the data"}; - Configurable> cfgTrackDensityP0{"cfgTrackDensityP0", std::vector{0.7217476707, 0.7384792571, 0.7542625668, 0.7640680200, 0.7701951667, 0.7755299053, 0.7805901710, 0.7849446786, 0.7957356586, 0.8113039262, 0.8211968966, 0.8280558878, 0.8329342135}, "parameter 0 for track density efficiency correction"}; - Configurable> cfgTrackDensityP1{"cfgTrackDensityP1", std::vector{-2.169488e-05, -2.191913e-05, -2.295484e-05, -2.556538e-05, -2.754463e-05, -2.816832e-05, -2.846502e-05, -2.843857e-05, -2.705974e-05, -2.477018e-05, -2.321730e-05, -2.203315e-05, -2.109474e-05}, "parameter 1 for track density efficiency correction"}; - Configurable> cfgTrackDensityV2P{"cfgTrackDensityV2P", std::vector{0.0186111, 0.00351907, -4.38264e-05, 1.35383e-07, -3.96266e-10}, "parameter of v2(cent) for track density efficiency correction"}; - Configurable> cfgTrackDensityV3P{"cfgTrackDensityV3P", std::vector{0.0174056, 0.000703329, -1.45044e-05, 1.91991e-07, -1.62137e-09}, "parameter of v2(cent) for track density efficiency correction"}; - Configurable> cfgTrackDensityV4P{"cfgTrackDensityV4P", std::vector{0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10}, "parameter of v2(cent) for track density efficiency correction"}; struct : ConfigurableGroup { std::string prefix = "meanptC22GraphOpts"; ConfigurableAxis cfgaxisBootstrap{"cfgaxisBootstrap", {30, 0, 30}, "cfgaxisBootstrap"}; } meanptC22GraphOpts; - AxisSpec axisMultiplicity{{0, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80, 90}, "Centrality (%)"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 80, 90}, "Centrality (%) or raw PV multiplicity when cfgUseMultiplicity is enabled"}; // configurable @@ -240,13 +894,17 @@ struct PidFlowPtCorr { // filter and using // data Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < trkQualityOpts.cfgCutEta.value); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::eta) < trkQualityOpts.cfgCutEta.value); using TracksPID = soa::Join; + using TracksPIDForDetectorPidQA = soa::Join; // data tracks filter using AodTracks = soa::Filtered>; + using AodTracksForDetectorPidQA = soa::Filtered>; // data collisions filter - using AodCollisions = soa::Filtered>; + using AodCollisions = soa::Filtered>; + + using ChargedTimeCollisionsMC = soa::Filtered>; // MC Filter mccollisionFilter = nabs(aod::mccollision::posZ) < cfgCutVertex; @@ -264,43 +922,90 @@ struct PidFlowPtCorr { // others Preslice perCollision = aod::track::collisionId; + Preslice timeTracksPerCollision = aod::pidflowtimetrack::collisionId; + Preslice particlesPerMCCollision = aod::mcparticle::mcCollisionId; // Connect to ccdb - Service ccdb; + Service ccdb{}; ctpRateFetcher rateFetcher; - O2_DEFINE_CONFIGURABLE(cfgnolaterthan, int64_t, std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object") - O2_DEFINE_CONFIGURABLE(cfgurl, std::string, "http://alice-ccdb.cern.ch", "url of the ccdb repository") - + Configurable cfgnolaterthan{"cfgnolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable cfgurl{"cfgurl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; // Define output HistogramRegistry registry{"registry"}; // val used for bootstrap TRandom3* fRndm = new TRandom3(0); OutputObj fFCCh{FlowContainer("FlowContainerCharged")}; + OutputObj fFCChDifferential{FlowContainer("FlowContainerChargedDifferential")}; OutputObj fFCPi{FlowContainer("FlowContainerPi")}; OutputObj fFCKa{FlowContainer("FlowContainerKa")}; OutputObj fFCPr{FlowContainer("FlowContainerPr")}; + OutputObj fFCUnidentified{FlowContainer("FlowContainerUnidentified")}; + OutputObj fFCPidPtCorr{FlowContainer("FlowContainerPidPtCorr")}; + bool timeAcceptanceLoaded = false; + bool timeEfficiencyLoaded = false; // end val used for bootstrap // define global variables GFW* fGFW = new GFW(); // GFW class used from main src std::vector corrconfigs; + // Independent reference pT range and Q-vectors for charged differential v2{2}. + // Group runtime state to stay below the GCC DPL limit of 100 task members. + struct { + std::unique_ptr gfw; + std::unique_ptr ptAxis; + std::vector configs; + } chargedDifferential; + enum ChargedDifferentialMask { + kDifferentialReference = 1, + kDifferentialPOI = 2 + }; std::vector cfgAcceptance; + std::vector cfgAcceptanceWithPt; std::vector cfgMultPVCutPara; - std::vector cfgNSigma; std::vector runNumbers; - std::map>> th1sList; - std::map>> th3sList; + enum MyParticleType { kCharged = 0, kPion, kKaon, kProton, + kUnidentified, kNumberOfParticles }; + bool isParticleOutputEnabled(MyParticleType type) + { + switch (type) { + case MyParticleType::kCharged: + return switchsOpts.cfgOutputCharged.value; + case MyParticleType::kPion: + return switchsOpts.cfgOutputPion.value; + case MyParticleType::kKaon: + return switchsOpts.cfgOutputKaon.value; + case MyParticleType::kProton: + return switchsOpts.cfgOutputProton.value; + case MyParticleType::kUnidentified: + return switchsOpts.cfgOutputUnidentified.value; + default: + return false; + } + } + + enum CentEstimators { + kCentFT0C = 0, + kCentFT0CVariant1, + kCentFT0M, + kCentFV0A, + kCentNTPV, + kCentNGlobal, + kCentMFT, + // Count the total number of enum + kCount_CentEstimators + }; + enum OutputTH1Names { // here are TProfiles for vn-pt correlations that are not implemented in GFW hPhi = 0, @@ -319,13 +1024,33 @@ struct PidFlowPtCorr { funcFillCorrectionGraph, funcProcessReco, funcProcessSim, + funcProcessQA, + funcProcessPidResponseMatrix, + funcProcessDataC22DeltaPt, + funcProcessPidPtCorrelations, + funcProcessDataTimeAssociation, + funcFillCorrectionGraphTimeAssociation, + funcProcessMCTimeAssociation, + funcProcessTimeAssociationComparison, + funcProcessChargedDifferentialTimeAssociation, funcNumber }; + enum PidResponseBin { + kPidRespPion = 0, + kPidRespKaon = 1, + kPidRespProton = 2, + kPidRespOther = 3, + kPidRespNTrueBins = 4, + kPidRespUnidentified = kPidRespOther, + kPidRespNRecoBins = kPidRespNTrueBins + }; + // graphs for NUE / NUA std::vector mAcceptance; - std::vector mEfficiency; - std::vector mEfficiency4ITSOnly; + std::vector mEfficiency; + // NUA with extra pt axis: THnSparse(phi, eta, vz, pt), same content as mAcceptance but pt-differential + std::vector mAcceptanceWithPt; bool correctionsLoaded = false; @@ -334,66 +1059,244 @@ struct PidFlowPtCorr { TF1* fT0AV0AMean = nullptr; TF1* fT0AV0ASigma = nullptr; - // Declare the pt, mult and phi Axis; - int nPtBins = 0; - TAxis* fPtAxis = nullptr; + // Index order is pion, kaon, proton; stage 0 is before PID, stage 1 is + // after the current unique PID assignment. The registry owns the objects. + struct PidQualityQAHists { + std::array tpcPIDClustersVsPt{}; + std::array tpcNSigmaVsPIDClusters{}; + std::array itsNSigmaVsPt{}; + std::array tpcVsITSNSigma{}; + TH2* itsClusterSizeVsP = nullptr; + TH2* itsClusterSizeVsPt = nullptr; + TProfile* meanITSClusterSizeVsP = nullptr; + TProfile* meanITSClusterSizeVsPt = nullptr; + }; + std::array pidQualityQAHists{}; - TAxis* fMultAxis = nullptr; + const char* getActivityAxisTitle() const + { + return cfgUseMultiplicity.value ? "N_{tracks}^{PV} (|#eta| < 0.8)" : "Centrality (%)"; + } - std::vector funcEff; - TH1D* hFindPtBin; - TF1* funcV2; - TF1* funcV3; - TF1* funcV4; + void initPidQualityQA() + { + const AxisSpec ptAxis{cfgaxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec itsPtAxis{cfgaxisITSPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec etaAxis{cfgaxisEta, "#eta"}; + const AxisSpec pAxis{cfgaxisPIDMomentum, "#it{p} (GeV/#it{c})"}; + const AxisSpec centAxis{axisMultiplicity, getActivityAxisTitle()}; + const AxisSpec clusterAxis{cfgaxisTPCPIDClusters, "TPC clusters used for PID"}; + const AxisSpec foundClusterAxis{cfgaxisTPCPIDClusters, "TPC found clusters"}; + const AxisSpec tpcNSigmaAxis{cfgaxisPIDNSigma, "n#sigma_{TPC}"}; + const AxisSpec itsNSigmaAxis{cfgaxisITSNSigma, "n#sigma_{ITS}"}; + const AxisSpec itsSizeAxis{cfgaxisITSClusterSize, "#LTITS cluster size#GT_{trunc}/cosh(#eta)"}; + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersAll", "Before PID;#it{p}_{T} (GeV/#it{c});TPC clusters used for PID", {HistType::kTH2F, {ptAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersUnidentified", "PID failed or ambiguous;#it{p}_{T} (GeV/#it{c});TPC clusters used for PID", {HistType::kTH2F, {ptAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/tpcPIDClustersVsFound", "Before PID;TPC found clusters;TPC clusters used for PID", {HistType::kTH2F, {foundClusterAxis, clusterAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeAll", "Before PID;#it{p} (GeV/#it{c});#LTITS cluster size#GT_{trunc}/cosh(#eta)", {HistType::kTH2F, {pAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeAllVsPt", "Before PID;#it{p}_{T} (GeV/#it{c});#LTITS cluster size#GT_{trunc}/cosh(#eta)", {HistType::kTH2F, {itsPtAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsClusterSizeVsPEta", "Before PID: ITS response versus momentum and eta", {HistType::kTHnSparseF, {pAxis, etaAxis, itsSizeAxis}}); + registry.add("DetectorPidQA/PIDQuality/itsResponseStatus", "ITS QA validity;status;tracks", {HistType::kTH1D, {{4, 0., 4.}}}); + auto* statusAxis = registry.get(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"))->GetXaxis(); + statusAxis->SetBinLabel(1, "Fewer than 2 cluster sizes"); + statusAxis->SetBinLabel(2, "Invalid momentum or eta"); + statusAxis->SetBinLabel(3, "Nonfinite ITS response"); + statusAxis->SetBinLabel(4, "All ITS hypotheses finite"); + + constexpr std::array species{"Pi", "Ka", "Pr"}; + constexpr std::array stages{"BeforePID", "AfterPID"}; + std::array expectedITSResponse{}; + for (std::size_t i = 0; i < species.size(); ++i) { + auto& h = pidQualityQAHists[i]; + for (std::size_t stage = 0; stage < stages.size(); ++stage) { + const std::string path = std::string("DetectorPidQA/PIDQuality/") + stages[stage] + "/" + species[i] + "/"; + h.tpcPIDClustersVsPt[stage] = registry.add(path + "tpcPIDClustersVsPt", "TPC PID cluster count", HistType::kTH2F, {ptAxis, clusterAxis}).get(); + h.tpcNSigmaVsPIDClusters[stage] = registry.add(path + "tpcNSigmaVsPIDClusters", "TPC PID response versus PID cluster count", HistType::kTHnSparseF, {ptAxis, centAxis, clusterAxis, tpcNSigmaAxis}).get(); + h.itsNSigmaVsPt[stage] = registry.add(path + "itsNSigmaVsPt", "ITS response for this mass hypothesis", HistType::kTH2F, {itsPtAxis, itsNSigmaAxis}).get(); + h.tpcVsITSNSigma[stage] = registry.add(path + "tpcVsITSNSigma", "TPC versus ITS response for this mass hypothesis", HistType::kTHnSparseF, {ptAxis, centAxis, tpcNSigmaAxis, itsNSigmaAxis}).get(); + } + const std::string path = std::string("DetectorPidQA/PIDQuality/AfterPID/") + species[i] + "/"; + h.itsClusterSizeVsP = registry.add(path + "itsClusterSizeVsP", "ITS cluster size after current PID", HistType::kTH2F, {pAxis, itsSizeAxis}).get(); + h.itsClusterSizeVsPt = registry.add(path + "itsClusterSizeVsPt", "ITS cluster size after current PID", HistType::kTH2F, {itsPtAxis, itsSizeAxis}).get(); + h.meanITSClusterSizeVsP = registry.add(path + "meanITSClusterSizeVsP", "Measured mean ITS cluster size after current PID;#it{p} (GeV/#it{c});#LTITS signal#GT", HistType::kTProfile, {pAxis}).get(); + h.meanITSClusterSizeVsPt = registry.add(path + "meanITSClusterSizeVsPt", "Measured mean ITS cluster size after current PID;#it{p}_{T} (GeV/#it{c});#LTITS signal#GT", HistType::kTProfile, {itsPtAxis}).get(); + const std::string expectedPath = std::string("DetectorPidQA/PIDQuality/Expected/") + species[i] + "/itsClusterSizeVsP"; + expectedITSResponse[i] = registry.add(expectedPath, "ITS expected mean (model, not measured data)", HistType::kTProfile, {pAxis}).get(); + } + // Identical axes and response parameters preserve model values on merge. + // Entries count model samples per worker, not tracks; do not normalize. + // Sample bin centers; the response is defined only at positive momentum. + for (int bin = 1; bin <= expectedITSResponse[0]->GetNbinsX(); ++bin) { + const float p = expectedITSResponse[0]->GetXaxis()->GetBinCenter(bin); + if (p <= 0.f) { + continue; + } + const std::array expected{ + o2::aod::ITSResponse::expSignal(p), + o2::aod::ITSResponse::expSignal(p), + o2::aod::ITSResponse::expSignal(p)}; + for (std::size_t i = 0; i < expected.size(); ++i) { + if (std::isfinite(expected[i])) { + expectedITSResponse[i]->Fill(p, expected[i]); + } + } + } + } - // hists for debug - struct { - std::shared_ptr hPtEffWeight = 0; - std::shared_ptr hPtCentEffWeight = 0; - std::shared_ptr hRunNumberPhiEtaVertexWeight = 0; - } debugHist; - // end hist for debug - - // hists for QA runbyrun - struct qaHist { - TH1* hPhiRunByRunBefore = 0; - TH1* hPhiRunByRunAfter = 0; - - TH2* hITSnclsVsPhi = 0; - TH2* hITSChi2VsPhi = 0; - - qaHist(TH1* hPhiRunByRunBefore, TH1* hPhiRunByRunAfter, TH2* hITSnclsVsPhi, TH2* hITSChi2VsPhi) - : hPhiRunByRunBefore(hPhiRunByRunBefore), hPhiRunByRunAfter(hPhiRunByRunAfter), hITSnclsVsPhi(hITSnclsVsPhi), hITSChi2VsPhi(hITSChi2VsPhi) - { + template + void fillPidQualityQA(TrackObject const& track, float cent, int pid) + { + // Called only by detectorPidQA, once per quality-selected track. All + // counts have unit weight; PID decisions come from getPidConfigurable. + const float pt = track.pt(); + const int nPIDClusters = track.tpcNClsPID(); + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersAll"), pt, nPIDClusters); + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersVsFound"), track.tpcNClsFound(), nPIDClusters); + if (pid < 0) { + registry.fill(HIST("DetectorPidQA/PIDQuality/tpcPIDClustersUnidentified"), pt, nPIDClusters); } - }; - std::vector qaHistVector; - // end hists for QA runbyrun + + constexpr std::array species{MyParticleType::kPion, MyParticleType::kKaon, MyParticleType::kProton}; + const std::array tpcNSigma{track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + for (std::size_t i = 0; i < species.size(); ++i) { + if (!isWithinPOIPtRange(species[i], pt)) { + continue; + } + for (std::size_t stage = 0; stage < 2; ++stage) { + if (stage == 1 && pid != species[i]) { + continue; + } + auto& h = pidQualityQAHists[i]; + h.tpcPIDClustersVsPt[stage]->Fill(pt, nPIDClusters); + if (std::isfinite(tpcNSigma[i])) { + const std::array values{pt, cent, static_cast(nPIDClusters), tpcNSigma[i]}; + h.tpcNSigmaVsPIDClusters[stage]->Fill(values.data()); + } + } + } + + // averageClusterSize removes the largest cluster and divides by N-1. + const uint32_t clusterSizes = track.itsClusterSizes(); + int nSizeLayers = 0; + for (int layer = 0; layer < 7; ++layer) { + nSizeLayers += ((clusterSizes >> (4 * layer)) & 0xf) != 0; + } + if (!track.hasITS() || nSizeLayers < 2) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), 0.5); + return; + } + const float momentum = track.p(); + if (!std::isfinite(momentum) || momentum <= 0.f || !std::isfinite(track.eta())) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), 1.5); + return; + } + const float clusterSize = o2::aod::ITSResponse::averageClusterSize(clusterSizes) / std::cosh(track.eta()); + // Reuse the response used by the low-pT ITS PID; do not change + // its global parameters or initialize a different data/MC response here. + const std::array itsNSigma{ + o2::aod::ITSResponse::nSigmaITS(track), + o2::aod::ITSResponse::nSigmaITS(track), + o2::aod::ITSResponse::nSigmaITS(track)}; + const bool allFinite = std::isfinite(clusterSize) && std::all_of(itsNSigma.begin(), itsNSigma.end(), [](float value) { return std::isfinite(value); }); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsResponseStatus"), allFinite ? 3.5 : 2.5); + if (std::isfinite(clusterSize)) { + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeAll"), momentum, clusterSize); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeAllVsPt"), pt, clusterSize); + registry.fill(HIST("DetectorPidQA/PIDQuality/itsClusterSizeVsPEta"), momentum, track.eta(), clusterSize); + } + for (std::size_t i = 0; i < species.size(); ++i) { + if (!isWithinPOIPtRange(species[i], pt)) { + continue; + } + auto& h = pidQualityQAHists[i]; + if (pid == species[i] && std::isfinite(clusterSize)) { + h.itsClusterSizeVsP->Fill(momentum, clusterSize); + h.itsClusterSizeVsPt->Fill(pt, clusterSize); + h.meanITSClusterSizeVsP->Fill(momentum, clusterSize); + h.meanITSClusterSizeVsPt->Fill(pt, clusterSize); + } + if (!std::isfinite(itsNSigma[i])) { + continue; + } + for (std::size_t stage = 0; stage < 2; ++stage) { + if (stage == 1 && pid != species[i]) { + continue; + } + h.itsNSigmaVsPt[stage]->Fill(pt, itsNSigma[i]); + if (std::isfinite(tpcNSigma[i])) { + const std::array values{pt, cent, tpcNSigma[i], itsNSigma[i]}; + h.tpcVsITSNSigma[stage]->Fill(values.data()); + } + } + } + } // others - void init(InitContext const&) // Initialization + void initChargedTimeAssociation(InitContext& context) + { + if (doprocessData || dofillCorrectionGraph || doprocessReco || doprocessSim || doprocessReco1D || doprocessSim1D || + doprocessDataC22DeltaPt || doprocessPidPtCorrelations || doprocessMCClosure || doprocessQA || dodetectorPidQA || doprocessPidResponseMatrix) { + LOGF(fatal, "Charged time mode: explicitly disable all original process switches to avoid mixed association/PID samples"); + } + if (!switchsOpts.cfgOutputCharged || switchsOpts.cfgOutputPion || switchsOpts.cfgOutputKaon || switchsOpts.cfgOutputProton || switchsOpts.cfgOutputUnidentified) { + LOGF(fatal, "Charged time mode requires cfgOutputCharged=true and all four PID/unidentified output switches=false"); + } + if (dofillCorrectionGraphTimeAssociation && !switchsOpts.cfgOutputrunbyrun) { + LOGF(fatal, "fillCorrectionGraphTimeAssociation requires switchsOpts.cfgOutputrunbyrun=true"); + } + if (doprocessMCTimeAssociation && !switchsOpts.cfgOutPutMC) { + LOGF(fatal, "processMCTimeAssociation requires switchsOpts.cfgOutPutMC=true"); + } + if (!timeAssociationOption(context, "pid-flow-charged-time-producer", "processTimeTracks") || + timeAssociationOption(context, "pid-flow-charged-time-producer", "processDummy")) { + LOGF(fatal, "Charged time mode requires pid-flow-charged-time-producer: processTimeTracks=true, processDummy=false"); + } + } + + void init(InitContext& context) // Initialization { + const bool timeMode = doprocessDataTimeAssociation || doprocessChargedDifferentialTimeAssociation || dofillCorrectionGraphTimeAssociation || doprocessMCTimeAssociation || doprocessTimeAssociationComparison; + if (timeMode) { + initChargedTimeAssociation(context); + } + const AxisSpec activityAxis{axisMultiplicity, getActivityAxisTitle()}; + // FlowContainer's AxisSpec overload expects a full array of bin edges. + // Use its uniform-axis overload for fixed-width axisMultiplicity settings. + const auto initializeFlowContainer = [&](auto& container, TObjArray* profiles) { + if (activityAxis.nBins.has_value()) { + container->Initialize(profiles, activityAxis.nBins.value(), activityAxis.binEdges.front(), activityAxis.binEdges.back(), cfgFlowNbootstrap); + } else { + container->Initialize(profiles, activityAxis, cfgFlowNbootstrap); + } + container->GetProfile()->GetXaxis()->SetTitle(getActivityAxisTitle()); + if (auto* subprofiles = container->GetSubProfiles()) { + for (auto* profile : *subprofiles) { + static_cast(profile)->GetXaxis()->SetTitle(getActivityAxisTitle()); + } + } + }; + // init and add lots of graphs according to switch ccdb->setURL(cfgurl.value); ccdb->setCaching(true); ccdb->setCreatedNotAfter(cfgnolaterthan.value); cfgAcceptance = correctionPathOpts.cfgAcceptancePath.value; + cfgAcceptanceWithPt = correctionPathOpts.cfgAcceptancePathWithPt.value; - cfgNSigma = cfgNSigmapid; cfgMultPVCutPara = evtSeleOpts.cfgMultPVCut; - // Set the pt, mult and phi Axis; - o2::framework::AxisSpec axisPt = cfgaxisPt; - nPtBins = axisPt.binEdges.size() - 1; - fPtAxis = new TAxis(nPtBins, &(axisPt.binEdges)[0]); + if (!timeMode) { + fTPCPIDClustersMin = std::make_unique("pidFlowPtCorrTPCPIDClustersMin", cfgTPCPIDClustersMinFunction.value.c_str(), 0., 256.); + if (!fTPCPIDClustersMin->IsValid() || fTPCPIDClustersMin->GetNpar() != 0) { + LOGF(fatal, "Invalid cfgTPCPIDClustersMinFunction: use a ROOT formula in x = tpcNClsFound() with numeric coefficients and no free parameters"); + } + } - o2::framework::AxisSpec axisMult = axisMultiplicity; - int nMultBins = axisMult.binEdges.size() - 1; - fMultAxis = new TAxis(nMultBins, &(axisMult.binEdges)[0]); + // Set the pt, mult and phi Axis; - if (dcaCutOpts.cfgDCAxyNSigma) { + if (dcaCutOpts.cfgDCAxyNSigma > 0) { dcaCutOpts.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", dcaCutOpts.cfgDCAxy->c_str()), 0.001, 1000); dcaCutOpts.fPtDepDCAxy->SetParameter(0, dcaCutOpts.cfgDCAxyNSigma); LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", dcaCutOpts.cfgDCAxy->c_str())); @@ -402,79 +1305,93 @@ struct PidFlowPtCorr { // Add some output objects to the histogram registry registry.add("hPhi", "", {HistType::kTH1D, {cfgaxisPhi}}); registry.add("hPhicorr", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhiPi", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhicorrPi", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhiKa", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhicorrKa", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhiPr", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhicorrPr", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhiCorrNUANUE", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhiCorrNUANUEPi", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhiCorrNUANUEKa", "", {HistType::kTH1D, {cfgaxisPhi}}); + registry.add("hPhiCorrNUANUEPr", "", {HistType::kTH1D, {cfgaxisPhi}}); registry.add("hEta", "", {HistType::kTH1D, {cfgaxisEta}}); registry.add("hVtxZ", "", {HistType::kTH1D, {cfgaxisVertex}}); registry.add("hMult", "", {HistType::kTH1D, {cfgaxisNch}}); registry.add("hMultTPC", "", {HistType::kTH1D, {cfgaxisNch}}); - registry.add("hCent", "", {HistType::kTH1D, {{90, 0, 90}}}); + std::string hCentTitle = cfgUseMultiplicity.value ? "PV multiplicity distribution" : "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); + const AxisSpec hCentAxis = cfgUseMultiplicity.value ? activityAxis : AxisSpec{90, 0., 90., getActivityAxisTitle()}; + registry.add("hCent", hCentTitle.c_str(), {HistType::kTH1D, {hCentAxis}}); registry.add("hPt", "", {HistType::kTH1D, {cfgaxisPt}}); - registry.add("hNTracksPVvsCentrality", "", {HistType::kTH2D, {{5000, 0, 5000}, axisMultiplicity}}); + registry.add("hPtCorr", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("hPtPi", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("hPtCorrPi", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("hPtKa", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("hPtCorrKa", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("hPtPr", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("hPtCorrPr", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("hNTracksPVvsCentrality", "", {HistType::kTH2D, {{5000, 0, 5000}, activityAxis}}); registry.add("hNchUnCorrectedVSNchCorrected", "", {HistType::kTH2D, {cfgaxisNch, cfgaxisNch}}); runNumbers = cfgRunNumbers; - // TPC vs TOF vs its, comparation graphs, check the PID performance in difference pt + // Minimal detector-PID performance QA. if (switchsOpts.cfgOutputQA.value) { - registry.add("DetectorPidPerformace/TPCvsTOF/Pi", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - registry.add("DetectorPidPerformace/TPCvsTOF/Pr", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - registry.add("DetectorPidPerformace/TPCvsTOF/Ka", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - - registry.add("DetectorPidPerformace/TPCvsITS/Pi", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - registry.add("DetectorPidPerformace/TPCvsITS/Pr", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - registry.add("DetectorPidPerformace/TPCvsITS/Ka", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - - registry.add("DetectorPidPerformace/ITSvsTOF/Pi", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - registry.add("DetectorPidPerformace/ITSvsTOF/Pr", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - registry.add("DetectorPidPerformace/ITSvsTOF/Ka", "", {HistType::kTH3D, {{320, -20, 20}, {320, -20, 20}, cfgaxisPt}}); - // end TPC vs TOF vs its, comparation graphs - - // run by run QA hists - /** - * @brief create QA hist for each run - * @note include: - * 1. hPhi before collision cut - * 2. hPhi after collision cut - * > graph below are after collision cut < - * 3. ITS ncls vs Phi - * 4. ITS chi2 vs Phi - * @param runNumbers - */ - for (const auto& oneRun : runNumbers) { - TH1* hPhiRunByRunBefore = registry.add(Form("RunByRunQA%d/hPhiBefore", oneRun), "", {HistType::kTH1D, {cfgaxisPhi}}).get(); - TH1* hPhiRunByRunAfter = registry.add(Form("RunByRunQA%d/hPhiAfter", oneRun), "", {HistType::kTH1D, {cfgaxisPhi}}).get(); - - TH2* hITSnclsVsPhi = registry.add(Form("RunByRunQA%d/hITSnclsVsPhi", oneRun), "", {HistType::kTH2D, {cfgaxisPhi, {8, 0, 8}}}).get(); - TH2* hITSChi2VsPhi = registry.add(Form("RunByRunQA%d/hITSChi2VsPhi", oneRun), "", {HistType::kTH2D, {cfgaxisPhi, {100, 0, 10}}}).get(); - - qaHistVector.emplace_back(qaHist(hPhiRunByRunBefore, hPhiRunByRunAfter, hITSnclsVsPhi, hITSChi2VsPhi)); - } - // end run by run QA hists + initPidQualityQA(); + registry.add("DetectorPidQA/TPC/dEdxPi", "TPC d#it{E}/d#it{x} (pion)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); + registry.add("DetectorPidQA/TPC/dEdxKa", "TPC d#it{E}/d#it{x} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); + registry.add("DetectorPidQA/TPC/dEdxPr", "TPC d#it{E}/d#it{x} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); + registry.add("DetectorPidQA/TPC/dEdxAll", "TPC d#it{E}/d#it{x} (all particles)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTPCDedx}}); + + registry.add("DetectorPidQA/TOF/deltaPi", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (pion)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); + registry.add("DetectorPidQA/TOF/deltaKa", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (kaon)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); + registry.add("DetectorPidQA/TOF/deltaPr", "TOF #it{t} - #it{t}_{ev} - #it{t}_{exp} (proton)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFDelta}}); + registry.add("DetectorPidQA/TOF/tofTimeAll", "TOF #it{t} - #it{t}_{ev} (all particles)", {HistType::kTH2F, {cfgaxisPt, cfgaxisTOFTime}}); } // cfgoutputqa if (switchsOpts.cfgOutPutMC.value) { // hist for NUE correction - registry.add("correction/hPtCentMcRec", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGen", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRec", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGen", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // ITS only - registry.add("correction/hPtCentMcRec4ITSOnly", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); // hist for Pi eff - registry.add("correction/hPtCentMcRecPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenPi", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // hist for Ka eff - registry.add("correction/hPtCentMcRecKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenKa", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); // hist for Pr eff - registry.add("correction/hPtCentMcRecPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("correction/hPtCentMcGenPr", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("correction/hPtCentMcRecPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + // Charged-track reconstruction efficiency split by MC true species. + // Detector PID is deliberately not used for these numerators. + registry.add("correction/hPtCentMcRecTruePi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTrueKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTruePr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcRecTrueOther", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTruePi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTrueKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTruePr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("correction/hPtCentMcGenTrueOther", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); } // cfgoutputMC - if (switchsOpts.cfgOutPutPtSpectra.value) { - registry.add("ptSpectra/hPtCentData", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountData", "", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountMcGen", "", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("ptSpectra/hCentEventCountMcRec", "", {HistType::kTH1D, {axisMultiplicity}}); + if (switchsOpts.cfgOutPutMC1D.value) { + registry.add("correction1D/hPtMCRec1D", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("correction1D/hPtMCRec1DPi", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("correction1D/hPtMCRec1DKa", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("correction1D/hPtMCRec1DPr", "", {HistType::kTH1D, {cfgaxisPt}}); + + registry.add("correction1D/hPtMCGen1D", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("correction1D/hPtMCGen1DPi", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("correction1D/hPtMCGen1DKa", "", {HistType::kTH1D, {cfgaxisPt}}); + registry.add("correction1D/hPtMCGen1DPr", "", {HistType::kTH1D, {cfgaxisPt}}); + } - registry.add("ptSpectra/hPtCentData4ITSOnly", "", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + if (switchsOpts.cfgOutPutPtSpectra.value) { + registry.add("ptSpectra/hPtCentData", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("ptSpectra/hCentEventCountData", "", {HistType::kTH1D, {activityAxis}}); + registry.add("ptSpectra/hCentEventCountMcGen", "", {HistType::kTH1D, {activityAxis}}); + registry.add("ptSpectra/hCentEventCountMcRec", "", {HistType::kTH1D, {activityAxis}}); registry.add("c22PrimeVsc22/Pi", "", {HistType::kTH2D, {{100, 0., 0.01}, {100, 0., 0.01}}}); registry.add("c22PrimeVsc22/Ka", "", {HistType::kTH2D, {{100, 0., 0.01}, {100, 0., 0.01}}}); @@ -483,10 +1400,17 @@ struct PidFlowPtCorr { if (switchsOpts.cfgOutputrunbyrun.value) { // hist for NUA - registry.add("correction/hRunNumberPhiEtaVertex", "", {HistType::kTHnSparseF, {cfgaxisRun, cfgaxisPhi, cfgaxisEta, cfgaxisVertex}}); + registry.add("correction/hRunNumberPhiEtaVertex", "", {HistType::kTHnSparseF, {cfgaxisRun, cfgaxisPhi, cfgaxisEta, cfgaxisVertex, cfgaxisPt}}); + registry.add("correction/hRunNumberPhiEtaVertexPion", "", {HistType::kTHnSparseF, {cfgaxisRun, cfgaxisPhi, cfgaxisEta, cfgaxisVertex, cfgaxisPt}}); + registry.add("correction/hRunNumberPhiEtaVertexKaon", "", {HistType::kTHnSparseF, {cfgaxisRun, cfgaxisPhi, cfgaxisEta, cfgaxisVertex, cfgaxisPt}}); + registry.add("correction/hRunNumberPhiEtaVertexProton", "", {HistType::kTHnSparseF, {cfgaxisRun, cfgaxisPhi, cfgaxisEta, cfgaxisVertex, cfgaxisPt}}); + // set "correction/hRunNumberPhiEtaVertex" axis0 label for (uint64_t idx = 1; idx <= runNumbers.size(); idx++) { registry.get(HIST("correction/hRunNumberPhiEtaVertex"))->GetAxis(0)->SetBinLabel(idx, std::to_string(runNumbers[idx - 1]).c_str()); + registry.get(HIST("correction/hRunNumberPhiEtaVertexPion"))->GetAxis(0)->SetBinLabel(idx, std::to_string(runNumbers[idx - 1]).c_str()); + registry.get(HIST("correction/hRunNumberPhiEtaVertexKaon"))->GetAxis(0)->SetBinLabel(idx, std::to_string(runNumbers[idx - 1]).c_str()); + registry.get(HIST("correction/hRunNumberPhiEtaVertexProton"))->GetAxis(0)->SetBinLabel(idx, std::to_string(runNumbers[idx - 1]).c_str()); } // end set "correction/hRunNumberPhiEtaVertex" axis0 label } // cfgooutputrunbyrun @@ -496,6 +1420,12 @@ struct PidFlowPtCorr { registry.add("abundance/hNumOfKaEventCount", "", {HistType::kTH1D, {particleAbundanceOpts.cfgaxisAbundanceKa}}); registry.add("abundance/hNumOfPrEventCount", "", {HistType::kTH1D, {particleAbundanceOpts.cfgaxisAbundancePr}}); } + if (particleAbundanceOpts.cfgOutputAbundanceYields.value) { + registry.add("abundance/hPtCentDataPi", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataKa", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataPr", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + registry.add("abundance/hPtCentDataUnidentified", "", {HistType::kTH2D, {cfgaxisPt, activityAxis}}); + } // set bin label for hEventCount // processdata @@ -522,18 +1452,131 @@ struct PidFlowPtCorr { registry.addClone("hEventCount/processData", "hEventCount/processReco"); // processSim registry.addClone("hEventCount/processData", "hEventCount/processSim"); + // processPidResponseMatrix + registry.addClone("hEventCount/processData", "hEventCount/processPidResponseMatrix"); + if (timeMode) { + auto originalCounter = registry.get(HIST("hEventCount/processData")); + for (auto const& name : {"hEventCount/processDataTimeAssociation", "hEventCount/fillCorrectionGraphTimeAssociation", "hEventCount/processMCTimeAssociation", "hEventCount/processChargedDifferentialTimeAssociation"}) { + auto counter = registry.add(name, "Target collisions;selection;collisions", {HistType::kTH1D, {{15, 0, 15}}}); + for (int bin = 1; bin <= originalCounter->GetNbinsX(); ++bin) { + counter->GetXaxis()->SetBinLabel(bin, originalCounter->GetXaxis()->GetBinLabel(bin)); + } + counter->GetXaxis()->SetBinLabel(15, "has associated baseline track"); + } + if (doprocessMCTimeAssociation && cfgTimeAssociationQA) { + registry.add("timeAssociation/mcEventStatus", "MC event sample;stage;target collisions", {HistType::kTH1D, {{4, 0, 4}}}); + const std::array mcLabels{"target collision", "has MC label", "one reco per MC in vertex acceptance", "accepted for reco and gen"}; + for (std::size_t bin = 0; bin < mcLabels.size(); ++bin) { + registry.get(HIST("timeAssociation/mcEventStatus"))->GetXaxis()->SetBinLabel(bin + 1, mcLabels[bin]); + } + registry.add("timeAssociation/mcTrackStatus", "Selected charged tracks;truth category;tracks", {HistType::kTH1D, {{4, 0, 4}}}); + const std::array mcLabelsTrack{"no MC particle", "other MC collision", "same MC outside primary truth sample", "matched primary numerator"}; + for (std::size_t bin = 0; bin < mcLabelsTrack.size(); ++bin) { + registry.get(HIST("timeAssociation/mcTrackStatus"))->GetXaxis()->SetBinLabel(bin + 1, mcLabelsTrack[bin]); + } + registry.add("timeAssociation/hPtCentMcSelected", "All selected reassociated tracks;p_{T};centrality", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + registry.add("timeAssociation/hPtCentMcOtherCollision", "Tracks from a different true event;p_{T};centrality", {HistType::kTH2D, {cfgaxisPt, axisMultiplicity}}); + } + } + if (doprocessTimeAssociationComparison && cfgTimeAssociationQA) { + registry.addClone("hEventCount/processData", "hEventCount/processTimeAssociationComparison"); + initTimeAssociationComparisonQA(); + } + if (switchsOpts.cfgAddPidPtCorrelationProfiles.value) { + registry.addClone("hEventCount/processData", "hEventCount/processPidPtCorrelations"); + } - registry.add("hInteractionRate", "", {HistType::kTH1D, {{1000, 0, 1000}}}); - // end set bin label for eventcount + if (switchsOpts.cfgAddC22DeltaPtHistograms.value) { + registry.addClone("hEventCount/processData", "hEventCount/processDataC22DeltaPt"); - // flow container setup - // cumulant of flow - // fill TObjArray for charged - TObjArray* oba4Ch = new TObjArray(); - oba4Ch->Add(new TNamed("c22", "c22")); - oba4Ch->Add(new TNamed("c32", "c32")); - oba4Ch->Add(new TNamed("c24", "c24")); - oba4Ch->Add(new TNamed("c34", "c34")); + // Store the ensemble mean pT required for the offline delta[pT]/<[pT]> + // transformation. The profile weight is the NUE-corrected particle count. + if (switchsOpts.cfgOutputCharged.value) { + registry.add("c22DeltaPt/c22dmeanptCharged", Form("Charged c_{2}{2} versus charged event [p_{T}];%s;[p_{T}]_{ch} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/hMeanPtCharged", Form("Charged mean p_{T};%s;Bootstrap subsample;[p_{T}]_{ch} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputPion.value) { + registry.add("c22DeltaPt/hMeanPtPion", Form("Pion mean p_{T};%s;Bootstrap subsample;[p_{T}]_{#pi} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptPionRefRef", Form("Ref-ref c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputKaon.value) { + registry.add("c22DeltaPt/hMeanPtKaon", Form("Kaon mean p_{T};%s;Bootstrap subsample;[p_{T}]_{K} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptKaonRefRef", Form("Ref-ref c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputProton.value) { + registry.add("c22DeltaPt/hMeanPtProton", Form("Proton mean p_{T};%s;Bootstrap subsample;[p_{T}]_{p} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptProtonRefRef", Form("Ref-ref c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputUnidentified.value) { + registry.add("c22DeltaPt/hMeanPtUnidentified", Form("Unidentified mean p_{T};%s;Bootstrap subsample;[p_{T}]_{unidentified} (GeV/#it{c})", getActivityAxisTitle()), {HistType::kTProfile2D, {activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("c22DeltaPt/c22dmeanptUnidentifiedRefRef", Form("Ref-ref c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + + // Ref-ref is required in either PID mode: Pure/ref-ref or POI-ref/ref-ref. + // Separate copies are binned by the event mean pT of each PID species. + // Select one PID correlation method at initialization so disabled-mode + // profiles are neither allocated nor filled. + if (switchsOpts.cfgC22DeltaPtUsePure.value) { + if (switchsOpts.cfgOutputPion.value) { + registry.add("c22DeltaPt/c22dmeanptPionPure", Form("Pion POI-POI c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputKaon.value) { + registry.add("c22DeltaPt/c22dmeanptKaonPure", Form("Kaon POI-POI c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputProton.value) { + registry.add("c22DeltaPt/c22dmeanptProtonPure", Form("Proton POI-POI c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputUnidentified.value) { + registry.add("c22DeltaPt/c22dmeanptUnidentifiedPure", Form("Unidentified POI-POI c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + } else { + // For each PID species, compare POI-ref and ref-ref using the same PID mean-pT axis. + if (switchsOpts.cfgOutputPion.value) { + registry.add("c22DeltaPt/c22dmeanptPionPOIRef", Form("Pion POI-ref c_{2}{2} versus pion event [p_{T}];%s;[p_{T}]_{#pi} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputKaon.value) { + registry.add("c22DeltaPt/c22dmeanptKaonPOIRef", Form("Kaon POI-ref c_{2}{2} versus kaon event [p_{T}];%s;[p_{T}]_{K} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputProton.value) { + registry.add("c22DeltaPt/c22dmeanptProtonPOIRef", Form("Proton POI-ref c_{2}{2} versus proton event [p_{T}];%s;[p_{T}]_{p} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputUnidentified.value) { + registry.add("c22DeltaPt/c22dmeanptUnidentifiedPOIRef", Form("Unidentified POI-ref c_{2}{2} versus unidentified event [p_{T}];%s;[p_{T}]_{unidentified} (GeV/#it{c});Bootstrap subsample;c_{2}{2}", getActivityAxisTitle()), {HistType::kTProfile3D, {activityAxis, cfgaxisMeanPt, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + } + } + + registry.add("hInteractionRate", "", {HistType::kTH1D, {{1000, 0, 1000}}}); + // end set bin label for eventcount + + if (switchsOpts.cfgAddPidResponseMatrixHistograms.value) { + registry.add("pidResponseMatrix/hPidResponseMatrix", "PID response matrix;true PID;reconstructed PID", {HistType::kTH2D, {{4, -0.5, 3.5}, {4, -0.5, 3.5}}}); + registry.add("pidResponseMatrix/hPidResponseMatrixRecPtCent", Form("PID response matrix vs reconstructed pT and event activity;true PID;reconstructed PID;p_{T}^{rec} (GeV/#it{c});%s", getActivityAxisTitle()), {HistType::kTHnSparseF, {{4, -0.5, 3.5}, {4, -0.5, 3.5}, cfgaxisPt, activityAxis}}); + + auto setPidResponseLabels = [](TAxis* axis, bool includeUnidentified) { + axis->SetBinLabel(kPidRespPion + 1, "Pion"); + axis->SetBinLabel(kPidRespKaon + 1, "Kaon"); + axis->SetBinLabel(kPidRespProton + 1, "Proton"); + if (includeUnidentified) { + axis->SetBinLabel(kPidRespUnidentified + 1, "Unidentified"); + } else { + axis->SetBinLabel(kPidRespOther + 1, "Other"); + } + }; + setPidResponseLabels(registry.get(HIST("pidResponseMatrix/hPidResponseMatrix"))->GetXaxis(), false); + setPidResponseLabels(registry.get(HIST("pidResponseMatrix/hPidResponseMatrix"))->GetYaxis(), true); + setPidResponseLabels(registry.get(HIST("pidResponseMatrix/hPidResponseMatrixRecPtCent"))->GetAxis(0), false); + setPidResponseLabels(registry.get(HIST("pidResponseMatrix/hPidResponseMatrixRecPtCent"))->GetAxis(1), true); + } + + // flow container setup + // cumulant of flow + // fill TObjArray for charged + auto* oba4Ch = new TObjArray(); + oba4Ch->Add(new TNamed("c22", "c22")); + oba4Ch->Add(new TNamed("c32", "c32")); + oba4Ch->Add(new TNamed("c24", "c24")); + oba4Ch->Add(new TNamed("c34", "c34")); oba4Ch->Add(new TNamed("c22Full", "c22Full")); oba4Ch->Add(new TNamed("c22TrackWeight", "c22TrackWeight")); oba4Ch->Add(new TNamed("c32TrackWeight", "c32TrackWeight")); @@ -545,111 +1588,246 @@ struct PidFlowPtCorr { oba4Ch->Add(new TNamed("ptSquareAve", "ptSquareAve")); oba4Ch->Add(new TNamed("ptAve", "ptAve")); oba4Ch->Add(new TNamed("hMeanPt", "hMeanPt")); + + oba4Ch->Add(new TNamed("covV2PtWeightOne", "covV2PtWeightOne")); + oba4Ch->Add(new TNamed("c22TrackWeightOne", "c22TrackWeightOne")); + oba4Ch->Add(new TNamed("hMeanPtWeightOne", "hMeanPtWeightOne")); + oba4Ch->Add(new TNamed("ptAveWeightOne", "ptAveWeightOne")); + oba4Ch->Add(new TNamed("ptSquareAveWeightOne", "ptSquareAveWeightOne")); + + oba4Ch->Add(new TNamed("hMeanPtWeightFull", "hMeanPtWeightFull")); // end fill TObjArray for charged + // Clone the shared profiles before appending the charged-only SC baseline. + auto* oba4PID = dynamic_cast(oba4Ch->Clone()); + oba4Ch->Add(new TNamed("c24Gap", "c24Gap")); + // init fFCCh - fFCCh->SetName("FlowContainerCharged"); - fFCCh->Initialize(oba4Ch, axisMultiplicity, cfgFlowNbootstrap); + if (switchsOpts.cfgOutputCharged.value) { + fFCCh->SetName("FlowContainerCharged"); + initializeFlowContainer(fFCCh, oba4Ch); + } // end init fFCCh + if (doprocessChargedDifferentialTimeAssociation) { + const double refPtMin = chargedDifferentialOpts.cfgRefPtMin.value; + const double refPtMax = chargedDifferentialOpts.cfgRefPtMax.value; + const double etaMax = trkQualityOpts.cfgCutEta.value; + const double etaGap = cfgEtaGap.value; + if (!std::isfinite(refPtMin) || !std::isfinite(refPtMax) || refPtMin < 0. || refPtMax <= refPtMin || + !std::isfinite(etaMax) || !std::isfinite(etaGap) || etaGap < 0. || etaGap >= etaMax) { + LOGF(fatal, "Charged differential v2{2} requires 0 <= cfgRefPtMin < cfgRefPtMax and 0 <= cfgEtaGap < cfgCutEta"); + } + const AxisSpec ptAxis{chargedDifferentialOpts.cfgaxisPt, "#it{p}_{T} (GeV/#it{c})"}; + if (ptAxis.binEdges.size() < 2 || ptAxis.binEdges.front() < 0. || + std::any_of(ptAxis.binEdges.begin(), ptAxis.binEdges.end(), [](double edge) { return !std::isfinite(edge); }) || + std::adjacent_find(ptAxis.binEdges.begin(), ptAxis.binEdges.end(), [](double left, double right) { return left >= right; }) != ptAxis.binEdges.end() || + (ptAxis.nBins.has_value() && ptAxis.nBins.value() <= 0)) { + LOGF(fatal, "chargedDifferentialOpts.cfgaxisPt must define a finite, nonnegative, strictly increasing pT axis with at least one bin"); + } + if (ptAxis.nBins.has_value()) { + chargedDifferential.ptAxis = std::make_unique(ptAxis.nBins.value(), ptAxis.binEdges.front(), ptAxis.binEdges.back()); + } else { + chargedDifferential.ptAxis = std::make_unique(static_cast(ptAxis.binEdges.size()) - 1, ptAxis.binEdges.data()); + } + chargedDifferential.ptAxis->SetTitle("#it{p}_{T} (GeV/#it{c})"); + const int ptBins = chargedDifferential.ptAxis->GetNbins(); + chargedDifferential.gfw = std::make_unique(); + chargedDifferential.gfw->AddRegion("refN", -etaMax, -etaGap, 1, kDifferentialReference); + chargedDifferential.gfw->AddRegion("refP", etaGap, etaMax, 1, kDifferentialReference); + chargedDifferential.gfw->AddRegion("poiN", -etaMax, -etaGap, ptBins, kDifferentialPOI); + chargedDifferential.gfw->AddRegion("poiP", etaGap, etaMax, ptBins, kDifferentialPOI); + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("refN {2} refP {-2}", "ChGap22", false)); + // Disjoint eta sides remove self-pairs even when POI and reference pT overlap. + // Both orientations contribute to the same profile with their own pair weight. + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("poiN {2} refP {-2}", "ChGap22", true)); + chargedDifferential.configs.push_back(chargedDifferential.gfw->GetCorrelatorConfig("poiP {2} refN {-2}", "ChGap22", true)); + chargedDifferential.gfw->CreateRegions(); + + TObjArray differentialProfiles; + differentialProfiles.SetOwner(true); + differentialProfiles.Add(new TNamed("ChGap22", "Reference-reference <2>, independent differential reference pT range")); + for (int bin = 1; bin <= ptBins; ++bin) { + differentialProfiles.Add(new TNamed(Form("ChGap22_pt_%d", bin), "Charged POI-reference <2> versus pT")); + } + fFCChDifferential->SetXAxis(chargedDifferential.ptAxis.get()); + initializeFlowContainer(fFCChDifferential, &differentialProfiles); + } + // init fFCPID // note that need to add c22pure and c32pure - TObjArray* oba4PID = reinterpret_cast(oba4Ch->Clone()); oba4PID->Add(new TNamed("c22pure", "c22pure")); oba4PID->Add(new TNamed("c32pure", "c32pure")); + oba4PID->Add(new TNamed("covV2PtPID", "covV2PtPID")); + oba4PID->Add(new TNamed("c22TrackWeightPID", "c22TrackWeightPID")); - fFCPi->SetName("FlowContainerPi"); - fFCPi->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + oba4PID->Add(new TNamed("hMeanPtWeightCharged", "hMeanPtWeightCharged")); + // Raw mixed <4> for SC/NSC; reuse PID c22pure and charged c22 as <2>. + oba4PID->Add(new TNamed("c24PIDChargedGap", "c24PIDChargedGap")); - fFCKa->SetName("FlowContainerKa"); - fFCKa->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); - - fFCPr->SetName("FlowContainerPr"); - fFCPr->Initialize(oba4PID, axisMultiplicity, cfgFlowNbootstrap); + if (switchsOpts.cfgOutputPion.value) { + fFCPi->SetName("FlowContainerPi"); + initializeFlowContainer(fFCPi, oba4PID); + } + if (switchsOpts.cfgOutputKaon.value) { + fFCKa->SetName("FlowContainerKa"); + initializeFlowContainer(fFCKa, oba4PID); + } + if (switchsOpts.cfgOutputProton.value) { + fFCPr->SetName("FlowContainerPr"); + initializeFlowContainer(fFCPr, oba4PID); + } + if (switchsOpts.cfgOutputUnidentified.value) { + fFCUnidentified->SetName("FlowContainerUnidentified"); + initializeFlowContainer(fFCUnidentified, oba4PID); + } // end init fFCPID - registry.add("c22dmeanpt", ";Centrality (%) ; C_{2}{2} ", {HistType::kTProfile2D, {axisMultiplicity, cfgaxisMeanPt}}); - registry.add("pi/c22dmeanpt", ";Centrality (%) ; C_{2}{2} ", {HistType::kTProfile2D, {axisMultiplicity, cfgaxisMeanPt}}); - registry.add("ka/c22dmeanpt", ";Centrality (%) ; C_{2}{2} ", {HistType::kTProfile2D, {axisMultiplicity, cfgaxisMeanPt}}); - registry.add("pr/c22dmeanpt", ";Centrality (%) ; C_{2}{2} ", {HistType::kTProfile2D, {axisMultiplicity, cfgaxisMeanPt}}); - - // init tprofile3d for <2'> - meanpt - // charged - registry.add("meanptCentNbs/hCharged", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - // end charged - - // pid - registry.add("meanptCentNbs/hChargedPionWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedPionFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPion", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanptWeightPidflow", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - - if (switchsOpts.cfgClosureTest.value != 0) { - registry.add("meanptCentNbs/hPionMeanptWeightC22pure", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanptWeightMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hPionMeanptWeightC22prime", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - } - - registry.add("meanptCentNbs/hChargedKaonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedKaonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hKaon", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hKaonMeanptWeightPidflow", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - - registry.add("meanptCentNbs/hChargedProtonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hChargedProtonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hProton", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - registry.add("meanptCentNbs/hProtonMeanptWeightPidflow", "", {HistType::kTProfile3D, {cfgaxisMeanPt, axisMultiplicity, meanptC22GraphOpts.cfgaxisBootstrap}}); - // end pid - // end init tprofile3d for <2'> - meanpt + if (switchsOpts.cfgAddPidPtCorrelationProfiles.value) { + auto* oba4PidPtCorr = new TObjArray(); + oba4PidPtCorr->Add(new TNamed("meanPtPi", "meanPtPi")); + oba4PidPtCorr->Add(new TNamed("meanPtKa", "meanPtKa")); + oba4PidPtCorr->Add(new TNamed("meanPtPr", "meanPtPr")); + oba4PidPtCorr->Add(new TNamed("ptProductPiPi", "ptProductPiPi")); + oba4PidPtCorr->Add(new TNamed("ptPiInPiPiPairs", "ptPiInPiPiPairs")); + oba4PidPtCorr->Add(new TNamed("ptProductKaKa", "ptProductKaKa")); + oba4PidPtCorr->Add(new TNamed("ptKaInKaKaPairs", "ptKaInKaKaPairs")); + oba4PidPtCorr->Add(new TNamed("ptProductPrPr", "ptProductPrPr")); + oba4PidPtCorr->Add(new TNamed("ptPrInPrPrPairs", "ptPrInPrPrPairs")); + oba4PidPtCorr->Add(new TNamed("ptProductPiKa", "ptProductPiKa")); + oba4PidPtCorr->Add(new TNamed("ptPiInPiKaPairs", "ptPiInPiKaPairs")); + oba4PidPtCorr->Add(new TNamed("ptKaInPiKaPairs", "ptKaInPiKaPairs")); + oba4PidPtCorr->Add(new TNamed("ptProductPiPr", "ptProductPiPr")); + oba4PidPtCorr->Add(new TNamed("ptPiInPiPrPairs", "ptPiInPiPrPairs")); + oba4PidPtCorr->Add(new TNamed("ptPrInPiPrPairs", "ptPrInPiPrPairs")); + oba4PidPtCorr->Add(new TNamed("ptProductKaPr", "ptProductKaPr")); + oba4PidPtCorr->Add(new TNamed("ptKaInKaPrPairs", "ptKaInKaPrPairs")); + oba4PidPtCorr->Add(new TNamed("ptPrInKaPrPairs", "ptPrInKaPrPairs")); + fFCPidPtCorr->SetName("FlowContainerPidPtCorr"); + initializeFlowContainer(fFCPidPtCorr, oba4PidPtCorr); + } + + if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { + // TProfile3D axes: event mean pT, centrality, and bootstrap subsample. + if (switchsOpts.cfgOutputCharged.value) { + registry.add("meanptCentNbs/hCharged", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputPion.value) { + registry.add("meanptCentNbs/hChargedPionWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedPionFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPion", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + if (switchsOpts.cfgClosureTest.value != 0) { + registry.add("meanptCentNbs/hPionMeanptWeightC22pure", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hPionMeanptWeightC22prime", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + } + if (switchsOpts.cfgOutputKaon.value) { + registry.add("meanptCentNbs/hChargedKaonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedKaonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hKaon", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hKaonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputProton.value) { + registry.add("meanptCentNbs/hChargedProtonWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedProtonFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hProton", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hProtonMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + if (switchsOpts.cfgOutputUnidentified.value) { + registry.add("meanptCentNbs/hChargedUnidentifiedWithNpair", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hChargedUnidentifiedFull", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hUnidentified", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + registry.add("meanptCentNbs/hUnidentifiedMeanpt", "", {HistType::kTProfile3D, {cfgaxisMeanPt, activityAxis, meanptC22GraphOpts.cfgaxisBootstrap}}); + } + } + + /// @note init QA plot for processQA + if (switchsOpts.cfgProcessQAOutput.value) { + /// @note track QA + registry.add("hProcessQA/hDCAz", "DCAz after collision cuts; DCAz (cm); Pt", {HistType::kTH2D, {{200, -5, 5}, {200, 0, 3}}}); + registry.add("hProcessQA/hDCAxy", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH2D, {{200, -0.5, 0.5}, {200, 0, 5}}}); + + registry.add("hProcessQA/hChi2prTPCcls", "#chi^{2}/cluster for the TPC track segment", {HistType::kTH1D, {{100, 0., 5.}}}); + registry.add("hProcessQA/hChi2prITScls", "#chi^{2}/cluster for the ITS track", {HistType::kTH1D, {{100, 0., 50.}}}); + + registry.add("hProcessQA/hnTPCClu", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); + registry.add("hProcessQA/hnITSClu", "Number of found ITS clusters", {HistType::kTH1D, {{10, 0, 10}}}); + registry.add("hProcessQA/hnTPCCrossedRow", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); + // end track QA + + /// @note evetn QA + registry.add("hProcessQA/centVsMult", Form("Event activity versus input track count;%s;Input track count", getActivityAxisTitle()), {HistType::kTH2D, {activityAxis, cfgaxisNch}}); + registry.add("hProcessQA/IR", "", {HistType::kTH1D, {{100, 0, 100}}}); + registry.add("hProcessQA/Occupacy", "", {HistType::kTH1D, {{1000, 0, 10000}}}); + registry.add("hProcessQA/occupancyVsCent", Form("Track occupancy versus event activity;%s;Track occupancy in time range", getActivityAxisTitle()), {HistType::kTH2D, {activityAxis, {1000, 0, 10000}}}); + // end evetn QA + } + // end init QA plot for processQA // fgfw set up and correlation config setup // Data stored in fGFW double etaMax = trkQualityOpts.cfgCutEta.value; double etaGap = cfgEtaGap; - // bit mask: 0000001 for CHARGED PARTICLES - fGFW->AddRegion("reffull", -etaMax, etaMax, 1, 1); // ("name", etamin, etamax, ptbinnum, bitmask)eta region -0.8 to 0.8 - fGFW->AddRegion("refN08", -etaMax, -etaGap, 1, 1); - fGFW->AddRegion("refP08", etaGap, etaMax, 1, 1); - fGFW->AddRegion("refN", -etaMax, 0, 1, 1); - fGFW->AddRegion("refP", 0, etaMax, 1, 1); - - // bit mask: 0000010 for PIONS - fGFW->AddRegion("poiPiN08", -etaMax, -etaGap, 1, 2); - fGFW->AddRegion("poiPiP08", etaGap, etaMax, 1, 2); - fGFW->AddRegion("poiPiN", -etaMax, 0, 1, 2); - fGFW->AddRegion("poiPiP", 0, etaMax, 1, 2); - - // bit mask: 0010000 for overlap pions - fGFW->AddRegion("olPiN", -etaMax, 0, 1, 16); - fGFW->AddRegion("olPiP", 0, etaMax, 1, 16); - - // bit mask: 0000100 for KAONS - fGFW->AddRegion("poiKaN08", -etaMax, -etaGap, 1, 4); - fGFW->AddRegion("poiKaP08", etaGap, etaMax, 1, 4); - fGFW->AddRegion("poiKaN", -etaMax, 0, 1, 4); - fGFW->AddRegion("poiKaP", 0, etaMax, 1, 4); - - // bit mask: 0100000 for overlap kaons - fGFW->AddRegion("olKaN", -etaMax, 0, 1, 32); - fGFW->AddRegion("olKaP", 0, etaMax, 1, 32); - - // bit mask: 0001000 for PROTONS - fGFW->AddRegion("poiPrN08", -etaMax, -etaGap, 1, 8); - fGFW->AddRegion("poiPrP08", etaGap, etaMax, 1, 8); - fGFW->AddRegion("poiPrN", -etaMax, 0, 1, 8); - fGFW->AddRegion("poiPrP", 0, etaMax, 1, 8); - - // bit mask: 1000000 for overlap protons - fGFW->AddRegion("olPrN", -etaMax, 0, 1, 64); - fGFW->AddRegion("olPrP", 0, etaMax, 1, 64); + // Charged reference regions. + fGFW->AddRegion("reffull", -etaMax, etaMax, 1, gfwBitMask::charged::kReference); // ("name", etamin, etamax, ptbinnum, bitmask)eta region -0.8 to 0.8 + fGFW->AddRegion("refN08", -etaMax, -etaGap, 1, gfwBitMask::charged::kReference); + fGFW->AddRegion("refP08", etaGap, etaMax, 1, gfwBitMask::charged::kReference); + fGFW->AddRegion("refN", -etaMax, 0, 1, gfwBitMask::charged::kReference); + fGFW->AddRegion("refP", 0, etaMax, 1, gfwBitMask::charged::kReference); + + // Pion POI and overlap regions. + fGFW->AddRegion("poiPiN08", -etaMax, -etaGap, 1, gfwBitMask::pion::kPOI); + fGFW->AddRegion("poiPiP08", etaGap, etaMax, 1, gfwBitMask::pion::kPOI); + fGFW->AddRegion("poiPiN", -etaMax, 0, 1, gfwBitMask::pion::kPOI); + fGFW->AddRegion("poiPiP", 0, etaMax, 1, gfwBitMask::pion::kPOI); + + fGFW->AddRegion("olPiN", -etaMax, 0, 1, gfwBitMask::pion::kOverlap); + fGFW->AddRegion("olPiP", 0, etaMax, 1, gfwBitMask::pion::kOverlap); + fGFW->AddRegion("olPiN08", -etaMax, -etaGap, 1, gfwBitMask::pion::kOverlap); + fGFW->AddRegion("olPiP08", etaGap, etaMax, 1, gfwBitMask::pion::kOverlap); + + // Kaon POI and overlap regions. + fGFW->AddRegion("poiKaN08", -etaMax, -etaGap, 1, gfwBitMask::kaon::kPOI); + fGFW->AddRegion("poiKaP08", etaGap, etaMax, 1, gfwBitMask::kaon::kPOI); + fGFW->AddRegion("poiKaN", -etaMax, 0, 1, gfwBitMask::kaon::kPOI); + fGFW->AddRegion("poiKaP", 0, etaMax, 1, gfwBitMask::kaon::kPOI); + + fGFW->AddRegion("olKaN", -etaMax, 0, 1, gfwBitMask::kaon::kOverlap); + fGFW->AddRegion("olKaP", 0, etaMax, 1, gfwBitMask::kaon::kOverlap); + fGFW->AddRegion("olKaN08", -etaMax, -etaGap, 1, gfwBitMask::kaon::kOverlap); + fGFW->AddRegion("olKaP08", etaGap, etaMax, 1, gfwBitMask::kaon::kOverlap); + + // Proton POI and overlap regions. + fGFW->AddRegion("poiPrN08", -etaMax, -etaGap, 1, gfwBitMask::proton::kPOI); + fGFW->AddRegion("poiPrP08", etaGap, etaMax, 1, gfwBitMask::proton::kPOI); + fGFW->AddRegion("poiPrN", -etaMax, 0, 1, gfwBitMask::proton::kPOI); + fGFW->AddRegion("poiPrP", 0, etaMax, 1, gfwBitMask::proton::kPOI); + + fGFW->AddRegion("olPrN", -etaMax, 0, 1, gfwBitMask::proton::kOverlap); + fGFW->AddRegion("olPrP", 0, etaMax, 1, gfwBitMask::proton::kOverlap); + fGFW->AddRegion("olPrN08", -etaMax, -etaGap, 1, gfwBitMask::proton::kOverlap); + fGFW->AddRegion("olPrP08", etaGap, etaMax, 1, gfwBitMask::proton::kOverlap); + + // Tracks for which the PID selection returns -1. They use the charged + // pT range and corrections, but have independent POI and overlap masks. + fGFW->AddRegion("poiUnidentifiedN08", -etaMax, -etaGap, 1, gfwBitMask::unidentified::kPOI); + fGFW->AddRegion("poiUnidentifiedP08", etaGap, etaMax, 1, gfwBitMask::unidentified::kPOI); + fGFW->AddRegion("poiUnidentifiedN", -etaMax, 0, 1, gfwBitMask::unidentified::kPOI); + fGFW->AddRegion("poiUnidentifiedP", 0, etaMax, 1, gfwBitMask::unidentified::kPOI); + fGFW->AddRegion("olUnidentifiedN", -etaMax, 0, 1, gfwBitMask::unidentified::kOverlap); + fGFW->AddRegion("olUnidentifiedP", 0, etaMax, 1, gfwBitMask::unidentified::kOverlap); + fGFW->AddRegion("olUnidentifiedN08", -etaMax, -etaGap, 1, gfwBitMask::unidentified::kOverlap); + fGFW->AddRegion("olUnidentifiedP08", etaGap, etaMax, 1, gfwBitMask::unidentified::kOverlap); // end data region add // pushback // Data corrconfigs.push_back(fGFW->GetCorrelatorConfig("refP08 {2} refN08 {-2}", "Ref08Gap22", false)); // 0 - corrconfigs.push_back(fGFW->GetCorrelatorConfig("refN {2 2} refP {-2 -2}", "Ref0Gap24", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("refN {2} refP {-2}", "Ref0Gap22", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("reffull {2 2 -2 -2}", "RefFull24", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("reffull {2 -2}", "RefFull22", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("refP08 {3} refN08 {-3}", "Ref08Gap32", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("refP08 {3 3} refN08 {-3 -3}", "Ref08Gap34", false)); @@ -659,12 +1837,12 @@ struct PidFlowPtCorr { corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaP08 {2} refN08 {-2}", "Kaon08gap22b", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN08 {2} refP08 {-2}", "Prot08gap22a", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrP08 {2} refN08 {-2}", "Prot08gap22b", false)); // 10 - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN refN | olPiN {2 2} refP {-2 -2}", "Pion0gap24a", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiP refP | olPiP {2 2} refN {-2 -2}", "Pion0gap24b", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN refN | olKaN {2 2} refP {-2 -2}", "Kaon0gap24a", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaP refP | olKaP {2 2} refN {-2 -2}", "Kaon0gap24b", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN refN | olPrN {2 2} refP {-2 -2}", "Prot0gap24a", false)); // 15 - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrP refP | olPrP {2 2} refN {-2 -2}", "Prot0gap24b", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN reffull | olPiN {2 2 -2 -2}", "PionFull24a", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiP reffull | olPiP {2 2 -2 -2}", "PionFull24b", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN reffull | olKaN {2 2 -2 -2}", "KaonFull24a", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaP reffull | olKaP {2 2 -2 -2}", "KaonFull24b", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN reffull | olPrN {2 2 -2 -2}", "ProtFull24a", false)); // 15 + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrP reffull | olPrP {2 2 -2 -2}", "ProtFull24b", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN08 {3} refP08 {-3}", "Pion08gap32a", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiP08 {3} refN08 {-3}", "Pion08gap32b", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN08 {3} refP08 {-3}", "Kaon08gap32a", false)); @@ -681,17 +1859,38 @@ struct PidFlowPtCorr { corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN08 {2} poiPiP08 {-2}", "PiPi08gap22", false)); corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN08 {2} poiKaP08 {-2}", "KaKa08gap22", false)); // 30 corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN08 {2} poiPrP08 {-2}", "PrPr08gap22", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN08 {3} poiPiP08 {-3}", "PiPi08gap22", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN08 {3} poiKaP08 {-3}", "KaKa08gap22", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN08 {3} poiPrP08 {-3}", "PrPr08gap22", false)); - - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN {2} refP {-2}", "Pion0gap22a", false)); // 35 - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiP {2} refN {-2}", "Pion0gap22b", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN {2} refP {-2}", "Kaon0gap22a", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaP {2} refN {-2}", "Kaon0gap22b", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN {2} refP {-2}", "Prot0gap22a", false)); - corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrP {2} refN {-2}", "Prot0gap22b", false)); // 40 - + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN08 {3} poiPiP08 {-3}", "PiPi08gap33", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN08 {3} poiKaP08 {-3}", "KaKa08gap33", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN08 {3} poiPrP08 {-3}", "PrPr08gap33", false)); + + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN reffull | olPiN {2 -2}", "PionFull22a", false)); // 35 + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiP reffull | olPiP {2 -2}", "PionFull22b", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN reffull | olKaN {2 -2}", "KaonFull22a", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaP reffull | olKaP {2 -2}", "KaonFull22b", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN reffull | olPrN {2 -2}", "ProtFull22a", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrP reffull | olPrP {2 -2}", "ProtFull22b", false)); // 40 + + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN08 {2} refP08 {-2}", "Unidentified08gap22a", false)); // 41 + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedP08 {2} refN08 {-2}", "Unidentified08gap22b", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN reffull | olUnidentifiedN {2 2 -2 -2}", "UnidentifiedFull24a", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedP reffull | olUnidentifiedP {2 2 -2 -2}", "UnidentifiedFull24b", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN08 {2} poiUnidentifiedP08 {-2}", "UnidentifiedPure08gap22", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN reffull | olUnidentifiedN {2 -2}", "UnidentifiedFull22a", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedP reffull | olUnidentifiedP {2 -2}", "UnidentifiedFull22b", false)); // 47 + + // One POI and one reference on each eta side, with distinct tracks. + // Each overlap region has the same eta range as its POI/reference pair. + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPiN08 refN08 | olPiN08 {2 2} poiPiP08 refP08 | olPiP08 {-2 -2}", "PionPIDChargedGap24", false)); // 48 + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiKaN08 refN08 | olKaN08 {2 2} poiKaP08 refP08 | olKaP08 {-2 -2}", "KaonPIDChargedGap24", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiPrN08 refN08 | olPrN08 {2 2} poiPrP08 refP08 | olPrP08 {-2 -2}", "ProtonPIDChargedGap24", false)); + corrconfigs.push_back(fGFW->GetCorrelatorConfig("poiUnidentifiedN08 refN08 | olUnidentifiedN08 {2 2} poiUnidentifiedP08 refP08 | olUnidentifiedP08 {-2 -2}", "UnidentifiedPIDChargedGap24", false)); // 51 + + // Charged-charged SC baseline: two distinct reference tracks per eta side. + corrconfigs.push_back(fGFW->GetCorrelatorConfig("refN08 {2 2} refP08 {-2 -2}", "Ref08Gap24", false)); // 52 + + if (corrconfigs.size() != corrConfigIndex::kCount) { + LOGF(fatal, "Correlation index table expects %zu configurations, but %zu were created", static_cast(corrConfigIndex::kCount), corrconfigs.size()); + } fGFW->CreateRegions(); // finalize the initialization // params @@ -705,167 +1904,10 @@ struct PidFlowPtCorr { fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); - - std::vector pTEffBins = {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0}; - hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, &pTEffBins[0]); - funcEff.resize(pTEffBins.size() - 1); - - std::vector f1p0 = cfgTrackDensityP0; - std::vector f1p1 = cfgTrackDensityP1; - for (uint ifunc = 0; ifunc < pTEffBins.size() - 1; ifunc++) { - funcEff[ifunc] = new TF1(Form("funcEff%i", ifunc), "[0]+[1]*x", 0, 3000); - funcEff[ifunc]->SetParameters(f1p0[ifunc], f1p1[ifunc]); - } - std::vector v2para = cfgTrackDensityV2P; - std::vector v3para = cfgTrackDensityV3P; - std::vector v4para = cfgTrackDensityV4P; - funcV2 = new TF1("funcV2", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV2->SetParameters(v2para[0], v2para[1], v2para[2], v2para[3], v2para[4]); - funcV3 = new TF1("funcV3", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV3->SetParameters(v3para[0], v3para[1], v3para[2], v3para[3], v3para[4]); - funcV4 = new TF1("funcV4", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); - funcV4->SetParameters(v4para[0], v4para[1], v4para[2], v4para[3], v4para[4]); } // pid utils function - /** - * @brief Identify whether the input track is a Pion - * - * @param track Input track object to be identified - * @return true The track is identified as Pion - * @return false The track is NOT identified as Pion - * @note The result is the logical AND of valid detector Sigma checks. - * If the track pt is out of a detector's valid range, that detector's check is skipped. - */ - template - bool isPion(TrackObject const& track) - { - if (switchsOpts.cfgClosureTest.value != 0) { - float rnd4test = fRndm->Rndm() * 100; - if (rnd4test < switchsOpts.cfgClosureTest.value) { - return true; - } - } // closure test - - bool resultPion = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaPi()); - const float tpcSigma = std::fabs(track.tpcNSigmaPi()); - const float pt = track.pt(); - - // ITS detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4ITSPiKa.value && pt < pidPtRangeOpts.cfgPtMax4ITSPiKa.value) { - resultPion &= (itsSigma < cfgNSigma[6]); - } - // end ITS - - // TOF detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TOFPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TOFPiKa.value) { - resultPion &= (tofSigma < cfgNSigma[3]); - } - // end TOF - - // TPC detector check (pi-k separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TPCPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TPCPiKa.value) { - resultPion &= (tpcSigma < cfgNSigma[0]); - } - // end TPC - - return resultPion; - } - - /** - * @brief Identify whether the input track is a Proton - * - * @param track Input track object to be identified - * @return true The track is identified as Proton - * @return false The track is NOT identified as Proton - * @note The result is the logical AND of valid detector Sigma checks. - * If the track pt is out of a detector's valid range, that detector's check is skipped. - */ - template - bool isProton(TrackObject const& track) - { - bool resultProton = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaPr()); - const float tpcSigma = std::fabs(track.tpcNSigmaPr()); - const float pt = track.pt(); - - // ITS detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4ITSKaPr.value && pt < pidPtRangeOpts.cfgPtMax4ITSKaPr.value) { - resultProton &= (itsSigma < cfgNSigma[7]); - } - // end ITS - - // TOF detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TOFKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TOFKaPr.value) { - resultProton &= (tofSigma < cfgNSigma[4]); - } - // end TOF - - // TPC detector check (k-p separation pt range) - if (pt > pidPtRangeOpts.cfgPtMin4TPCKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TPCKaPr.value) { - resultProton &= (tpcSigma < cfgNSigma[1]); - } - // end TPC - - return resultProton; - } - - /** - * @brief Identify whether the input track is a Kaon (separate from Pion and Proton) - * - * @param track Input track object (aod::Track) to be identified - * @return true The track is identified as Kaon - * @return false The track is NOT identified as Kaon - * @note The result is the logical AND of valid detector Sigma checks. - * Only pt range that overlaps with both pi-k and k-p separation ranges is checked. - * If the track pt is out of the overlapping range, that detector's check is skipped. - */ - template - bool isKaon(TrackObject const& track) - { - bool resultKaon = true; - - // Declare ITSResponse object internally to get ITS Sigma - o2::aod::ITSResponse itsResponse; - // Extract sigma values and pt from track - const float itsSigma = std::fabs(itsResponse.nSigmaITS(track)); - const float tofSigma = std::fabs(track.tofNSigmaKa()); - const float tpcSigma = std::fabs(track.tpcNSigmaKa()); - const float pt = track.pt(); - - // ITS detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4ITSKaPr.value && pt > pidPtRangeOpts.cfgPtMin4ITSPiKa.value && pt < pidPtRangeOpts.cfgPtMax4ITSKaPr.value && pt < pidPtRangeOpts.cfgPtMax4ITSPiKa.value) { - resultKaon &= (itsSigma < cfgNSigma[8]); - } - // end ITS - - // TOF detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4TOFKaPr.value && pt > pidPtRangeOpts.cfgPtMin4TOFPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TOFKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TOFPiKa.value) { - resultKaon &= (tofSigma < cfgNSigma[5]); - } - // end TOF - - // TPC detector check (overlap of pi-k and k-p separation pt ranges) - if (pt > pidPtRangeOpts.cfgPtMin4TPCKaPr.value && pt > pidPtRangeOpts.cfgPtMin4TPCPiKa.value && pt < pidPtRangeOpts.cfgPtMax4TPCKaPr.value && pt < pidPtRangeOpts.cfgPtMax4TPCPiKa.value) { - resultKaon &= (tpcSigma < cfgNSigma[2]); - } - // end TPC - - return resultKaon; - } - template int getPidWithCircleCut(TrackObject const& track) { @@ -890,10 +1932,9 @@ struct PidFlowPtCorr { const float ptMax = pidPtRangeOpts.cfgPtMax4TOFPiKa.value; if (pt > ptMin && pt < ptMax) { - isPionBool = std::hypot(tpcNsigma, tofNsigma) < circleCutOpts.cfgCircleCutSigmaPi.value; } else { - // Fallback: TPC only cut + // TPC fallback; the low-pT ITS requirement is applied below. isPionBool = std::fabs(tpcNsigma) < circleCutOpts.cfgCircleCutTPCPi.value; } } @@ -935,6 +1976,24 @@ struct PidFlowPtCorr { } } + // Apply ITS to every low-pT candidate before resolving ambiguities. + if (pt < circleCutOpts.cfgCircleCutITSPtMax.value) { + const uint32_t clusterSizes = track.itsClusterSizes(); + int nSizeLayers = 0; + for (int layer = 0; layer < 7; ++layer) { + nSizeLayers += ((clusterSizes >> (4 * layer)) & 0xf) != 0; + } + if (!track.hasITS() || nSizeLayers < 2 || !std::isfinite(track.p()) || track.p() <= 0.f || !std::isfinite(track.eta())) { + return -1; + } + const auto passesITS = [](float nsigma, float cut) { + return std::isfinite(nsigma) && std::fabs(nsigma) < cut; + }; + isPionBool = isPionBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSPi.value); + isKaonBool = isKaonBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSKa.value); + isProtonBool = isProtonBool && passesITS(o2::aod::ITSResponse::nSigmaITS(track), circleCutOpts.cfgCircleCutITSPr.value); + } + // ------------------------------ // Ambiguity rejection // ------------------------------ @@ -948,13 +2007,86 @@ struct PidFlowPtCorr { // ------------------------------ if (isPionBool) { return MyParticleType::kPion; - } else if (isKaonBool) { + } + if (isKaonBool) { return MyParticleType::kKaon; - } else if (isProtonBool) { + } + if (isProtonBool) { return MyParticleType::kProton; - } else { + } + return -1; + } + + /** + * @brief Apply the TPC PID cluster cut, then circle PID with low-pT ITS + * + * @tparam TrackObject + * @param track + * @return MyParticleType + */ + template + int getPidConfigurable(TrackObject const& track) + { + // PID quality changes the species assignment, not charged track selection. + const double minPIDClusters = fTPCPIDClustersMin->Eval(track.tpcNClsFound()); + if (!std::isfinite(minPIDClusters) || track.tpcNClsPID() < minPIDClusters) { return -1; } + + return getPidWithCircleCut(track); + } + + bool isWithinRefPtRange(float pt) + { + return (pt > trkQualityOpts.cfgCutPtMin.value) && (pt < trkQualityOpts.cfgCutPtMax.value); + } + + bool isWithinPOIPtRange(int pid, float pt) + { + switch (pid) { + case MyParticleType::kPion: + return (pt > trkQualityOpts.cfgCutPtMinPi.value) && (pt < trkQualityOpts.cfgCutPtMaxPi.value); + case MyParticleType::kKaon: + return (pt > trkQualityOpts.cfgCutPtMinKa.value) && (pt < trkQualityOpts.cfgCutPtMaxKa.value); + case MyParticleType::kProton: + return (pt > trkQualityOpts.cfgCutPtMinPr.value) && (pt < trkQualityOpts.cfgCutPtMaxPr.value); + default: + return false; + } + } + + int getPidResponseTrueBin(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kPiPlus: + return kPidRespPion; + case PDG_t::kKPlus: + return kPidRespKaon; + case PDG_t::kProton: + return kPidRespProton; + default: + return kPidRespOther; + } + } + + int getPidResponseRecoBin(int recoPid) + { + switch (recoPid) { + case MyParticleType::kPion: + return kPidRespPion; + case MyParticleType::kKaon: + return kPidRespKaon; + case MyParticleType::kProton: + return kPidRespProton; + default: + return kPidRespUnidentified; + } + } + + template + int getTrueSpeciesBin(McParticle const& particle) + { + return getPidResponseTrueBin(particle.pdgCode()); } // pid util function @@ -966,11 +2098,12 @@ struct PidFlowPtCorr { double NpairA = fGFW->Calculate(corrconfA, 0, true).real(); double NpairB = fGFW->Calculate(corrconfB, 0, true).real(); - if (NpairA == 0 && NpairB == 0) + if (NpairA == 0 && NpairB == 0) { return 0; + } - double ChC22A = NpairA ? fGFW->Calculate(corrconfA, 0, false).real() / NpairA : 0.; - double ChC22B = NpairB ? fGFW->Calculate(corrconfB, 0, false).real() / NpairB : 0.; + double ChC22A = NpairA > 0 ? fGFW->Calculate(corrconfA, 0, false).real() / NpairA : 0.; + double ChC22B = NpairB > 0 ? fGFW->Calculate(corrconfB, 0, false).real() / NpairB : 0.; double ChC22 = (ChC22A * NpairA + ChC22B * NpairB) / (NpairA + NpairB); @@ -1023,19 +2156,29 @@ struct PidFlowPtCorr { */ void fillFC4PtC22(const double& cent, const double& ptSum, const double& nch, const double& rndm) { - double dnx, val; + if (!isParticleOutputEnabled(MyParticleType::kCharged)) { + return; + } + double dnx = 0.0, val = 0.0; - dnx = fGFW->Calculate(corrconfigs.at(0), 0, true).real(); - if (dnx == 0) + dnx = fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, true).real(); + if (dnx == 0) { return; + } // <2> - val = fGFW->Calculate(corrconfigs.at(0), 0, false).real() / dnx; - if (std::fabs(val) >= 1) + val = fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, false).real() / dnx; + if (std::fabs(val) >= 1) { return; + } + + // These large TProfile3D objects are optional; keep FlowContainer output independent of this switch. + if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { + registry.fill(HIST("meanptCentNbs/hCharged"), ptSum / nch, cent, rndm * cfgFlowNbootstrap, val, nch * dnx); + registry.fill(HIST("meanptCentNbs/hChargedMeanpt"), ptSum / nch, cent, rndm * cfgFlowNbootstrap, ptSum / nch, 1.); + } - registry.fill(HIST("meanptCentNbs/hCharged"), ptSum / nch, cent, rndm * cfgFlowNbootstrap, val, nch * dnx); - registry.fill(HIST("meanptCentNbs/hChargedMeanpt"), ptSum / nch, cent, rndm * cfgFlowNbootstrap, ptSum / nch, nch * dnx * val); + fFCCh->FillProfile("hMeanPtWeightFull", cent, (ptSum / nch), nch * dnx, rndm); } /** @@ -1049,42 +2192,55 @@ struct PidFlowPtCorr { */ void fillFC4PtC22(const double& cent, const double& rndm, MyParticleType type, const double& pidPtSum, const double& nPid) { + if (!isParticleOutputEnabled(type)) { + return; + } // <2> - double dnx, val; + double dnx = 0.0, val = 0.0; - dnx = fGFW->Calculate(corrconfigs.at(0), 0, true).real(); - if (dnx == 0) + dnx = fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, true).real(); + if (dnx == 0) { return; + } // <2> - val = fGFW->Calculate(corrconfigs.at(0), 0, false).real() / dnx; - if (std::fabs(val) >= 1) + val = fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, false).real() / dnx; + if (std::fabs(val) >= 1) { return; + } double pidc22 = 0; double npairPid = 0; switch (type) { case MyParticleType::kPion: - pidc22 = getPidC22InOneEvent(corrconfigs.at(5), corrconfigs.at(6)); - if (pidc22 == 0) + pidc22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::pion::kC22GapA), corrconfigs.at(corrConfigIndex::pion::kC22GapB)); + if (pidc22 == 0) { return; + } - npairPid = fGFW->Calculate(corrconfigs.at(5), 0, true).real() + fGFW->Calculate(corrconfigs.at(6), 0, true).real(); - if (npairPid == 0) + npairPid = fGFW->Calculate(corrconfigs.at(corrConfigIndex::pion::kC22GapA), 0, true).real() + fGFW->Calculate(corrconfigs.at(corrConfigIndex::pion::kC22GapB), 0, true).real(); + if (npairPid == 0) { return; + } + + if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { + registry.fill(HIST("meanptCentNbs/hPion"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidc22, nPid * npairPid); + registry.fill(HIST("meanptCentNbs/hChargedPionFull"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx * nPid); + registry.fill(HIST("meanptCentNbs/hChargedPionWithNpair"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx); + registry.fill(HIST("meanptCentNbs/hPionMeanpt"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, 1.); + } - registry.fill(HIST("meanptCentNbs/hPion"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidc22, nPid * npairPid); - registry.fill(HIST("meanptCentNbs/hChargedPionFull"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx * nPid); - registry.fill(HIST("meanptCentNbs/hChargedPionWithNpair"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx); - registry.fill(HIST("meanptCentNbs/hPionMeanptWeightPidflow"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, nPid * npairPid * pidc22 * pidc22 / val); + fFCPi->FillProfile("hMeanPtWeightFull", cent, (pidPtSum / nPid), nPid * npairPid, rndm); + fFCPi->FillProfile("hMeanPtWeightCharged", cent, (pidPtSum / nPid), dnx * nPid, rndm); - if (switchsOpts.cfgClosureTest.value != 0) { - double npair4c22pure = fGFW->Calculate(corrconfigs.at(29), 0, true).real(); - if (npair4c22pure > minVal4Float) + if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value && switchsOpts.cfgClosureTest.value != 0) { + double npair4c22pure = fGFW->Calculate(corrconfigs.at(corrConfigIndex::pion::kC22Pure), 0, true).real(); + if (npair4c22pure > minVal4Float) { registry.fill(HIST("meanptCentNbs/hPionMeanptWeightC22pure"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, - nPid * npairPid * fGFW->Calculate(corrconfigs.at(29), 0, false).real() / npair4c22pure); + nPid * npairPid * fGFW->Calculate(corrconfigs.at(corrConfigIndex::pion::kC22Pure), 0, false).real() / npair4c22pure); + } registry.fill(HIST("meanptCentNbs/hPionMeanptWeightMeanpt"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, @@ -1100,39 +2256,75 @@ struct PidFlowPtCorr { // end pion case MyParticleType::kKaon: - pidc22 = getPidC22InOneEvent(corrconfigs.at(7), corrconfigs.at(8)); - if (pidc22 == 0) + pidc22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::kaon::kC22GapA), corrconfigs.at(corrConfigIndex::kaon::kC22GapB)); + if (pidc22 == 0) { return; + } - npairPid = fGFW->Calculate(corrconfigs.at(7), 0, true).real() + fGFW->Calculate(corrconfigs.at(8), 0, true).real(); - if (npairPid == 0) + npairPid = fGFW->Calculate(corrconfigs.at(corrConfigIndex::kaon::kC22GapA), 0, true).real() + fGFW->Calculate(corrconfigs.at(corrConfigIndex::kaon::kC22GapB), 0, true).real(); + if (npairPid == 0) { return; + } + + if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { + registry.fill(HIST("meanptCentNbs/hKaon"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidc22, nPid * npairPid); + registry.fill(HIST("meanptCentNbs/hChargedKaonFull"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx * nPid); + registry.fill(HIST("meanptCentNbs/hChargedKaonWithNpair"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx); + registry.fill(HIST("meanptCentNbs/hKaonMeanpt"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, 1.); + } - registry.fill(HIST("meanptCentNbs/hKaon"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidc22, nPid * npairPid); - registry.fill(HIST("meanptCentNbs/hChargedKaonFull"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx * nPid); - registry.fill(HIST("meanptCentNbs/hChargedKaonWithNpair"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx); - registry.fill(HIST("meanptCentNbs/hKaonMeanptWeightPidflow"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, nPid * npairPid * pidc22 * pidc22 / val); + fFCKa->FillProfile("hMeanPtWeightFull", cent, (pidPtSum / nPid), nPid * npairPid, rndm); + fFCKa->FillProfile("hMeanPtWeightCharged", cent, (pidPtSum / nPid), dnx * nPid, rndm); break; // end kaon case MyParticleType::kProton: - pidc22 = getPidC22InOneEvent(corrconfigs.at(9), corrconfigs.at(10)); - if (pidc22 == 0) + pidc22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::proton::kC22GapA), corrconfigs.at(corrConfigIndex::proton::kC22GapB)); + if (pidc22 == 0) { return; + } - npairPid = fGFW->Calculate(corrconfigs.at(9), 0, true).real() + fGFW->Calculate(corrconfigs.at(10), 0, true).real(); - if (npairPid == 0) + npairPid = fGFW->Calculate(corrconfigs.at(corrConfigIndex::proton::kC22GapA), 0, true).real() + fGFW->Calculate(corrconfigs.at(corrConfigIndex::proton::kC22GapB), 0, true).real(); + if (npairPid == 0) { return; + } + + if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { + registry.fill(HIST("meanptCentNbs/hProton"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidc22, nPid * npairPid); + registry.fill(HIST("meanptCentNbs/hChargedProtonFull"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx * nPid); + registry.fill(HIST("meanptCentNbs/hChargedProtonWithNpair"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx); + registry.fill(HIST("meanptCentNbs/hProtonMeanpt"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, 1.); + } - registry.fill(HIST("meanptCentNbs/hProton"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidc22, nPid * npairPid); - registry.fill(HIST("meanptCentNbs/hChargedProtonFull"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx * nPid); - registry.fill(HIST("meanptCentNbs/hChargedProtonWithNpair"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx); - registry.fill(HIST("meanptCentNbs/hProtonMeanptWeightPidflow"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, nPid * npairPid * pidc22 * pidc22 / val); + fFCPr->FillProfile("hMeanPtWeightFull", cent, (pidPtSum / nPid), nPid * npairPid, rndm); + fFCPr->FillProfile("hMeanPtWeightCharged", cent, (pidPtSum / nPid), dnx * nPid, rndm); break; // end proton + case MyParticleType::kUnidentified: + pidc22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::unidentified::kC22GapA), corrconfigs.at(corrConfigIndex::unidentified::kC22GapB)); + if (pidc22 == 0) { + return; + } + + npairPid = fGFW->Calculate(corrconfigs.at(corrConfigIndex::unidentified::kC22GapA), 0, true).real() + fGFW->Calculate(corrconfigs.at(corrConfigIndex::unidentified::kC22GapB), 0, true).real(); + if (npairPid == 0) { + return; + } + + if (switchsOpts.cfgAddMeanPtCentNbsHistograms.value) { + registry.fill(HIST("meanptCentNbs/hUnidentified"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidc22, nPid * npairPid); + registry.fill(HIST("meanptCentNbs/hChargedUnidentifiedFull"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx * nPid); + registry.fill(HIST("meanptCentNbs/hChargedUnidentifiedWithNpair"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, val, dnx); + registry.fill(HIST("meanptCentNbs/hUnidentifiedMeanpt"), pidPtSum / nPid, cent, rndm * cfgFlowNbootstrap, pidPtSum / nPid, 1.); + } + + fFCUnidentified->FillProfile("hMeanPtWeightFull", cent, pidPtSum / nPid, nPid * npairPid, rndm); + fFCUnidentified->FillProfile("hMeanPtWeightCharged", cent, pidPtSum / nPid, dnx * nPid, rndm); + break; + default: return; break; @@ -1142,15 +2334,19 @@ struct PidFlowPtCorr { bool fillFC(MyParticleType type, const GFW::CorrConfig& corrconf, const double& cent, const double& rndm, const char* tarName) { - double dnx, val; + if (!isParticleOutputEnabled(type)) { + return false; + } + double dnx = 0.0; // calculate #sum exp{i * 0 (#phi_{i} - #phi_{j})} == N_{pairs} // note that weight is ignored in the formula but not in the calculation, for c24 is similar dnx = fGFW->Calculate(corrconf, 0, true).real(); - if (dnx == 0) + if (dnx == 0) { return false; + } if (!corrconf.pTDif) { // #sum exp{i * 2 * (#phi_{i} - #phi_{j})} / N_{pairs} == < 2 > - val = fGFW->Calculate(corrconf, 0, false).real() / dnx; + double val = fGFW->Calculate(corrconf, 0, false).real() / dnx; if (std::fabs(val) < 1) { // NOTE that dnx is WEIGHT switch (type) { @@ -1167,6 +2363,9 @@ struct PidFlowPtCorr { case MyParticleType::kProton: fFCPr->FillProfile(tarName, cent, val, dnx, rndm); break; + case MyParticleType::kUnidentified: + fFCUnidentified->FillProfile(tarName, cent, val, dnx, rndm); + break; default: LOGF(warning, "particle not found"); @@ -1189,45 +2388,57 @@ struct PidFlowPtCorr { template void fillProfile(const GFW::CorrConfig& corrconf, const ConstStr& tarName, const double& cent) { - double dnx, val; // calculate #sum exp{i * 0 (#phi_{i} - #phi_{j})} == N_{pairs} // note that weight is ignored in the formula but not in the calculation, for c24 is similar - dnx = fGFW->Calculate(corrconf, 0, true).real(); - if (dnx == 0) + double dnx = fGFW->Calculate(corrconf, 0, true).real(); + if (dnx == 0) { return; + } if (!corrconf.pTDif) { // #sum exp{i * 2 * (#phi_{i} - #phi_{j})} / N_{pairs} == < 2 > - val = fGFW->Calculate(corrconf, 0, false).real() / dnx; + double val = fGFW->Calculate(corrconf, 0, false).real() / dnx; if (std::fabs(val) < 1) { // NOTE that dnx is WEIGHT registry.fill(tarName, cent, val, dnx); return; } } - return; } - void fillFCvnpt(MyParticleType type, const GFW::CorrConfig& corrconf, const double& cent, const double& rndm, const double& ptSum, const double& nch, const char* tarName) + void fillFCvnpt(MyParticleType type, + const GFW::CorrConfig& corrconf, + const double& cent, + const double& rndm, + const double& ptSum, + const double& nch, + const char* tarName, + bool useWeightOne = false) { - double dnx, val; - dnx = fGFW->Calculate(corrconf, 0, true).real(); - if (dnx == 0) + if (!isParticleOutputEnabled(type)) { return; - val = fGFW->Calculate(corrconf, 0, false).real() / dnx; + } + double dnx = fGFW->Calculate(corrconf, 0, true).real(); + if (dnx == 0) { + return; + } + double val = fGFW->Calculate(corrconf, 0, false).real() / dnx; if (std::fabs(val) < 1) { switch (type) { case MyParticleType::kCharged: - fFCCh->FillProfile(tarName, cent, val * (ptSum / nch), dnx * nch, rndm); + fFCCh->FillProfile(tarName, cent, val * (ptSum / nch), useWeightOne ? 1.0 : dnx * nch, rndm); break; case MyParticleType::kPion: - fFCPi->FillProfile(tarName, cent, val * (ptSum / nch), dnx * nch, rndm); + fFCPi->FillProfile(tarName, cent, val * (ptSum / nch), useWeightOne ? 1.0 : dnx * nch, rndm); break; case MyParticleType::kKaon: - fFCKa->FillProfile(tarName, cent, val * (ptSum / nch), dnx * nch, rndm); + fFCKa->FillProfile(tarName, cent, val * (ptSum / nch), useWeightOne ? 1.0 : dnx * nch, rndm); break; case MyParticleType::kProton: - fFCPr->FillProfile(tarName, cent, val * (ptSum / nch), dnx * nch, rndm); + fFCPr->FillProfile(tarName, cent, val * (ptSum / nch), useWeightOne ? 1.0 : dnx * nch, rndm); + break; + case MyParticleType::kUnidentified: + fFCUnidentified->FillProfile(tarName, cent, val * (ptSum / nch), useWeightOne ? 1.0 : dnx * nch, rndm); break; default: @@ -1235,8 +2446,6 @@ struct PidFlowPtCorr { break; } } - - return; } /** @@ -1255,28 +2464,53 @@ struct PidFlowPtCorr { template void fillProfilevnpt(const GFW::CorrConfig& corrconf, const ConstStr& tarName, const double& cent, const double& ptSum, const double& nch, const double& meanPt = 0) { - double dnx, val; + double dnx = 0.0, val = 0.0; dnx = fGFW->Calculate(corrconf, 0, true).real(); - if (dnx == 0) + if (dnx == 0) { return; + } val = fGFW->Calculate(corrconf, 0, false).real() / dnx; - if (std::fabs(val) < 1) + if (std::fabs(val) < 1) { registry.fill(tarName, cent, val * (ptSum / nch - meanPt), dnx * nch); - return; + } } template - void fillProfilePOIvnpt(const GFW::CorrConfig& corrconf, const ConstStr& tarName, const double& cent, const double& ptSum, const double& nch) + void fillProfilePOIvnpt(const GFW::CorrConfig& corrconf, const ConstStr& tarName, const double& cent, const double& ptSum, const double& nch, const double& rndm) { - double dnx, val; + double dnx = 0.0, val = 0.0; dnx = fGFW->Calculate(corrconf, 0, true).real(); - if (dnx == 0) + if (dnx == 0) { return; + } val = fGFW->Calculate(corrconf, 0, false).real() / dnx; - if (std::fabs(val) < 1) - registry.fill(tarName, cent, ptSum / nch, val, dnx); - return; + if (std::fabs(val) < 1) { + registry.fill(tarName, cent, ptSum / nch, rndm * cfgFlowNbootstrap, val, dnx); + } + } + + /** + * @brief Fill one event-level POI-ref value formed from the two eta orientations. + * @details The two correlators share one profile entry. Their numerators and pair + * counts are added before division. The PID mean pT is only the profile + * coordinate, so the profile is weighted by the combined pair count alone. + */ + template + void fillProfilePOIvnpt(const GFW::CorrConfig& corrconfA, const GFW::CorrConfig& corrconfB, const ConstStr& tarName, const double& cent, const double& ptSum, const double& nPid, const double& rndm) + { + const double dnxA = fGFW->Calculate(corrconfA, 0, true).real(); + const double dnxB = fGFW->Calculate(corrconfB, 0, true).real(); + const double dnx = dnxA + dnxB; + if (dnx == 0.) { + return; + } + + const double numerator = fGFW->Calculate(corrconfA, 0, false).real() + fGFW->Calculate(corrconfB, 0, false).real(); + const double val = numerator / dnx; + if (std::fabs(val) < 1.) { + registry.fill(tarName, cent, ptSum / nPid, rndm * cfgFlowNbootstrap, val, dnx); + } } // fill fgfw helper @@ -1291,226 +2525,257 @@ struct PidFlowPtCorr { */ void loadCorrections(uint64_t timestamp) { - if (correctionsLoaded) + if (correctionsLoaded) { return; + } int nspecies = 1; + int totalSpecies = 4; + /// @note if put 4 path in cfg then load charged, pi, ka, pr + // else only load charegd + // if path size != 1 or 4, nothing will be loaded + // load NUA if (cfgAcceptance.size() == static_cast(nspecies)) { - for (int i = 0; i <= nspecies - 1; i++) { + for (int i = 0; i < nspecies; i++) { mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance[i], timestamp)); } - if (mAcceptance.size() == static_cast(nspecies)) + if (mAcceptance.size() == static_cast(nspecies)) { LOGF(info, "Loaded acceptance weights"); - else + } else { LOGF(warning, "Could not load acceptance weights"); + } + } + + if (cfgAcceptance.size() == static_cast(totalSpecies)) { + for (int i = 0; i <= totalSpecies - 1; i++) { + mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance[i], timestamp)); + } + if (mAcceptance.size() == static_cast(totalSpecies)) { + LOGF(info, "Loaded acceptance weights * 4"); + } else { + LOGF(warning, "Could not load acceptance weights * 4"); + } } + // end load NUA - /// @note dont modify load NUA!, just modify NUE - // here needs to load 2 NUE graph selected from 6 ccdb path - /// @note when using eff 2D, convert TH1* to (TH2D *) - // pid eff is not available now... - std::vector effPath1D = correctionPathOpts.cfgEfficiencyPath.value; - std::vector effPath2D = correctionPathOpts.cfgEfficiency2DPath.value; - std::vector effPathPid = correctionPathOpts.cfgPidEfficiencyPath.value; - - std::vector effPath1D4ITSOnly = correctionPathOpts.cfgEfficiencyPath4ITSOnly.value; - std::vector effPath2D4ITSOnly = correctionPathOpts.cfgEfficiency2DPath4ITSOnly.value; - std::vector effPathPid4ITSOnly = correctionPathOpts.cfgPidEfficiencyPath4ITSOnly.value; - - switch (correctionPathOpts.cfgNUEOption.value) { - case 1: // use 1D eff - // load 1d eff for global track - if (effPath1D.size() != static_cast(1)) { - LOGF(warning, "eff path 1d size != 1, skip eff 1d load"); - break; + // load NUA with pt axis (THnSparse: phi, eta, vz, pt), only if cfgUseNUAWithPt is enabled + // same convention as the GFWWeights NUA above: 1 path -> charged only, 4 paths -> charged, pi, ka, pr + if (switchsOpts.cfgUseNUAWithPt.value) { + if (cfgAcceptanceWithPt.size() == static_cast(nspecies)) { + for (int i = 0; i < nspecies; i++) { + mAcceptanceWithPt.push_back(ccdb->getForTimeStamp(cfgAcceptanceWithPt[i], timestamp)); } - mEfficiency.push_back(ccdb->getForTimeStamp(effPath1D[0], timestamp)); - if (mEfficiency.size() == static_cast(1)) { - LOGF(info, "Loaded efficiency histogram"); + if (mAcceptanceWithPt.size() == static_cast(nspecies)) { + LOGF(info, "Loaded acceptance weights with pt axis"); } else { - LOGF(fatal, "Could not load efficiency histogram"); + LOGF(warning, "Could not load acceptance weights with pt axis"); } - // end load 1d eff for global track + } - // load 1d eff for ITS only - if (effPath1D4ITSOnly.size() != static_cast(1)) { - LOGF(warning, "eff path for its 1d size != 1, skip its eff 1d load"); - break; + if (cfgAcceptanceWithPt.size() == static_cast(totalSpecies)) { + for (int i = 0; i <= totalSpecies - 1; i++) { + mAcceptanceWithPt.push_back(ccdb->getForTimeStamp(cfgAcceptanceWithPt[i], timestamp)); } - mEfficiency4ITSOnly.push_back(ccdb->getForTimeStamp(effPath1D4ITSOnly[0], timestamp)); - if (mEfficiency4ITSOnly.size() == static_cast(1)) { - LOGF(info, "Loaded ITS only efficiency histogram"); + if (mAcceptanceWithPt.size() == static_cast(totalSpecies)) { + LOGF(info, "Loaded acceptance weights with pt axis * 4"); } else { - LOGF(fatal, "Could not load ITS only efficiency histogram"); + LOGF(warning, "Could not load acceptance weights with pt axis * 4"); } - // end load 1d eff for its only - - break; - // end use 1d eff + } + } + // end load NUA with pt axis - case 2: // Use 2D eff - // load 2d eff for global track - if (effPath2D.size() != static_cast(1)) { - LOGF(warning, "eff path 2d size != 1, skip eff 2d load"); - break; - } - mEfficiency.push_back(ccdb->getForTimeStamp(effPath2D[0], timestamp)); - if (mEfficiency.size() == static_cast(1)) { - LOGF(info, "Loaded 2D efficiency histogram"); - } else { - LOGF(fatal, "Could not load 2D efficiency histogram"); - } - // end load 2d eff for global track - - // load 2d eff for ITS only - if (effPath2D4ITSOnly.size() != static_cast(1)) { - LOGF(warning, "eff path for its 2d size != 1, skip its eff 2d load"); - break; - } - mEfficiency4ITSOnly.push_back(ccdb->getForTimeStamp(effPath2D4ITSOnly[0], timestamp)); - if (mEfficiency4ITSOnly.size() == static_cast(1)) { - LOGF(info, "Loaded ITS only 2D efficiency histogram"); - } else { - LOGF(fatal, "Could not load ITS only 2D efficiency histogram"); - } - // end load 2d eff for its only - - break; - // end use 2d eff - - case 3: // use pid 1D eff - // load pid 1d eff for ITS + global track - if (effPathPid.size() != static_cast(3)) { - LOGF(warning, "eff path pid 1d size != 3, skip pid eff 1d load"); - break; - } - for (std::size_t i = 0; i < NSpecies; i++) { - mEfficiency.push_back(ccdb->getForTimeStamp(effPathPid[i], timestamp)); - } - if (mEfficiency.size() == static_cast(3)) { - LOGF(info, "Loaded PID efficiency histogram"); - } else { - LOGF(fatal, "Could not load PID efficiency histogram"); - } - // end load pid 1d eff for ITS + global track + /// @note dont modify load NUA!, just modify NUE + // here needs to load 2 NUE graph selected from 6 ccdb path + /// @note when using eff 2D, convert TH1* to (TH2D *) + // pid eff is not available now... + // Load NUE (PID Efficiency) + std::vector effPath = correctionPathOpts.cfgEfficiencyPath.value; - // load pid 1d eff for ITS only - if (effPathPid4ITSOnly.size() != static_cast(3)) { - LOGF(warning, "eff path for its pid 1d size != 3, skip its pid eff 1d load"); - break; - } - for (std::size_t i = 0; i < NSpecies; i++) { - mEfficiency4ITSOnly.push_back(ccdb->getForTimeStamp(effPathPid4ITSOnly[i], timestamp)); - } - if (mEfficiency4ITSOnly.size() == static_cast(3)) { - LOGF(info, "Loaded ITS only PID efficiency histogram"); - } else { - LOGF(fatal, "Could not load ITS only PID efficiency histogram"); - } - // end load pid 1d eff for its only + if (effPath.size() == static_cast(nspecies)) { + for (int i = 0; i < nspecies; i++) { + mEfficiency.push_back(ccdb->getForTimeStamp(effPath[i], timestamp)); + } + if (mEfficiency.size() == static_cast(nspecies)) { + LOGF(info, "Loaded PID efficiency histogram"); + } else { + LOGF(warning, "Could not load PID efficiency histogram"); + } + } - break; - // end use pid 1d eff + if (effPath.size() == static_cast(totalSpecies)) { + for (int i = 0; i <= totalSpecies - 1; i++) { + mEfficiency.push_back(ccdb->getForTimeStamp(effPath[i], timestamp)); + } + if (mEfficiency.size() == static_cast(totalSpecies)) { + LOGF(info, "Loaded PID efficiency histogram * 4"); + } else { + LOGF(warning, "Could not load PID efficiency histogram * 4"); + } + } - default: // not do NUE - break; - } // end switch load eff + // End load PID efficiency for ITS + global track correctionsLoaded = true; } - template - bool setParticleNUAWeight(float& weight_nua, TrackObject& track, float vtxz) + /** + * @brief get NUA weight from a THnSparse(phi, eta, vz, pt), same convention as GFWWeights::getNUA + * (bin content is the normalized acceptance, 0-1, so the correction weight is 1/content) + * + * @param hist THnSparse with axes (phi, eta, vz, pt), may be nullptr + */ + float getNUAFromTHn(THnSparse* hist, float phi, float eta, float vz, float pt) { - if (mAcceptance.size() == static_cast(1)) - weight_nua = mAcceptance[0]->getNUA(track.phi(), track.eta(), vtxz); - else - weight_nua = 1; - return true; + if (!hist) { + return 1; + } + std::array bins{}; + bins[0] = hist->GetAxis(0)->FindBin(phi); + bins[1] = hist->GetAxis(1)->FindBin(eta); + bins[2] = hist->GetAxis(2)->FindBin(vz); + bins[3] = hist->GetAxis(3)->FindBin(pt); + double weight = hist->GetBinContent(bins.data()); + if (weight != 0) { + return 1. / weight; + } + return 1; } /** - * @brief Set the Particle Nue Weight, for global track and ITS track, use different eff path + * @brief Set the Particle NUA, note that it works only when macceptance size >= 1, + * macceptance size can only be 1 or 4 + * @note controlled by switchsOpts.cfgUseNUAWithPt: false uses the original GFWWeights + * (phi,eta,vz) NUA (mAcceptance); true uses the THnSparse (phi,eta,vz,pt) NUA (mAcceptanceWithPt) * * @tparam TrackObject - * @param weight_nue + * @param weight_nua * @param track - * @param cent - * @param isGlobalTrack - * @return true weight setted succesfully, note that weight == 1 also return true; - * @return false eff got from graph == 0 + * @param vtxz + * @return true + * @return false */ template - bool setParticleNUEWeight(float& weight_nue, TrackObject& track, double cent, bool isGlobalTrack) + bool setParticleNUAWeight(float& weight_nua, TrackObject& track, float vtxz) { - float eff = 1.; + if (switchsOpts.cfgUseNUAWithPt.value) { + if (mAcceptanceWithPt.size() == static_cast(1) || mAcceptanceWithPt.size() == static_cast(4)) { + weight_nua = getNUAFromTHn(mAcceptanceWithPt[0], track.phi(), track.eta(), vtxz, track.pt()); + } else { + weight_nua = 1; + } + return true; + } + if (mAcceptance.size() == static_cast(1) || mAcceptance.size() == static_cast(4)) { + weight_nua = mAcceptance[0]->getNUA(track.phi(), track.eta(), vtxz); + } else { + weight_nua = 1; + } + return true; + } - uint64_t sizeOfEffVec = mEfficiency.size(); - uint64_t sizeOfEffVec4ITS = mEfficiency4ITSOnly.size(); + template + bool setParticleNUAWeight(float& weight_nua, TrackObject& track, float vtxz, int pid) + { + if (switchsOpts.cfgUseNUAWithPt.value) { + if (mAcceptanceWithPt.size() == static_cast(4)) { + switch (pid) { + case MyParticleType::kPion: + weight_nua = getNUAFromTHn(mAcceptanceWithPt[1], track.phi(), track.eta(), vtxz, track.pt()); + break; + case MyParticleType::kKaon: + weight_nua = getNUAFromTHn(mAcceptanceWithPt[2], track.phi(), track.eta(), vtxz, track.pt()); + break; + case MyParticleType::kProton: + weight_nua = getNUAFromTHn(mAcceptanceWithPt[3], track.phi(), track.eta(), vtxz, track.pt()); + break; - TH2* eff2D = 0; - TH2* eff2D4ITS = 0; + default: + weight_nua = 1; + break; + } + } else { + weight_nua = 1; + } + return true; + } + if (mAcceptance.size() == static_cast(4)) { - /// @note 1. size check 2. load eff 3. dividezero check - switch (correctionPathOpts.cfgNUEOption.value) { - case 1: // 1d - if (sizeOfEffVec != 1) + switch (pid) { + case MyParticleType::kPion: + weight_nua = mAcceptance[1]->getNUA(track.phi(), track.eta(), vtxz); break; - if (sizeOfEffVec4ITS != 1) + case MyParticleType::kKaon: + weight_nua = mAcceptance[2]->getNUA(track.phi(), track.eta(), vtxz); + break; + case MyParticleType::kProton: + weight_nua = mAcceptance[3]->getNUA(track.phi(), track.eta(), vtxz); break; - if (isGlobalTrack) - eff = mEfficiency[0]->GetBinContent(mEfficiency[0]->FindBin(track.pt())); - else - eff = mEfficiency4ITSOnly[0]->GetBinContent(mEfficiency4ITSOnly[0]->FindBin(track.pt())); + default: + weight_nua = 1; + break; + } - if (eff == 0.) - return false; + } else { + weight_nua = 1; + } + return true; + } - break; - // end 1d + template + bool setParticleNUEWeight(float& weight_nue, TrackObject& track, double cent) + { + if (mEfficiency.size() == static_cast(1) || mEfficiency.size() == static_cast(4)) { + TH2* eff2D = mEfficiency[0]; + int ptBin = eff2D->GetXaxis()->FindBin(track.pt()); + int centBin = eff2D->GetYaxis()->FindBin(cent); + float eff = eff2D->GetBinContent(ptBin, centBin); + + if (eff == 0.) { + weight_nue = 1.; + return true; + } + weight_nue = 1. / eff; + } else { + weight_nue = 1.; + } + return true; + } - case 2: // 2d - if (sizeOfEffVec != 1) + template + bool setParticleNUEWeight(float& weight_nue, TrackObject& track, double cent, int pid) + { + if (mEfficiency.size() == static_cast(4)) { + TH2* eff2D = nullptr; + switch (pid) { + case MyParticleType::kPion: + eff2D = mEfficiency[1]; break; - if (sizeOfEffVec4ITS != 1) + case MyParticleType::kKaon: + eff2D = mEfficiency[2]; break; - - eff2D = dynamic_cast(mEfficiency[0]); - eff2D4ITS = dynamic_cast(mEfficiency4ITSOnly[0]); - - if (!eff2D || !eff2D4ITS) { - LOGF(warning, "failed while converting TH1 to TH2! skip eff apply"); + case MyParticleType::kProton: + eff2D = mEfficiency[3]; break; - } - - if (isGlobalTrack) { - int ptBin = eff2D->GetXaxis()->FindBin(track.pt()); - int centBin = eff2D->GetYaxis()->FindBin(cent); - eff = eff2D->GetBinContent(ptBin, centBin); - } else { - int ptBin = eff2D4ITS->GetXaxis()->FindBin(track.pt()); - int centBin = eff2D4ITS->GetYaxis()->FindBin(cent); - eff = eff2D4ITS->GetBinContent(ptBin, centBin); - } - - if (eff == 0.) - return false; - - break; - // end 2d + default: + weight_nue = 1.; + return true; + } - /// @todo add pid NUE eff - case 3: // pid - break; - // end pid + int ptBin = eff2D->GetXaxis()->FindBin(track.pt()); + int centBin = eff2D->GetYaxis()->FindBin(cent); + float eff = eff2D->GetBinContent(ptBin, centBin); - default: - break; + if (eff == 0.) { + weight_nue = 1.; + return true; + } + weight_nue = 1. / eff; + } else { + weight_nue = 1.; } - - weight_nue = 1. / eff; return true; } @@ -1534,16 +2799,20 @@ struct PidFlowPtCorr { bool particleSelected(mcParticle& particle) { // eta - if (std::fabs(particle.eta()) > trkQualityOpts.cfgCutEta.value) + if (std::fabs(particle.eta()) > trkQualityOpts.cfgCutEta.value) { return false; + } // pt - if (particle.pt() < trkQualityOpts.cfgCutPtMin.value) + if (particle.pt() < trkQualityOpts.cfgCutPtMin.value) { return false; - if (particle.pt() > trkQualityOpts.cfgCutPtMax.value) + } + if (particle.pt() > trkQualityOpts.cfgCutPtMax.value) { return false; + } // stable particle - if (!isStable(particle.pdgCode())) + if (!isStable(particle.pdgCode())) { return false; + } return true; } @@ -1558,28 +2827,34 @@ struct PidFlowPtCorr { * * @tparam TTrack * @param track + * @param checkPt if false, skip the internal ref-pt cut (cfgCutPtMin/cfgCutPtMax); needed + * so callers can apply an independent POI pt range instead (e.g. processData) * @return true pass selection * @return false reject track */ template - bool trackSelectedGlobal(TTrack& track) + bool trackSelectedGlobal(TTrack& track, bool checkPt = true) { // dca xy cut // note that z cut is in filter - if (dcaCutOpts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > dcaCutOpts.fPtDepDCAxy->Eval(track.pt()))) + if (dcaCutOpts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > dcaCutOpts.fPtDepDCAxy->Eval(track.pt()))) { return false; + } // pt - if (!((track.pt() > trkQualityOpts.cfgCutPtMin.value) && (track.pt() < trkQualityOpts.cfgCutPtMax.value))) + if (checkPt && !((track.pt() > trkQualityOpts.cfgCutPtMin.value) && (track.pt() < trkQualityOpts.cfgCutPtMax.value))) { return false; + } // eta - if (std::fabs(track.eta()) > trkQualityOpts.cfgCutEta.value) + if (std::fabs(track.eta()) > trkQualityOpts.cfgCutEta.value) { return false; + } // dca z - if (std::fabs(track.dcaZ()) > dcaCutOpts.cfgCutDCAz.value) + if (std::fabs(track.dcaZ()) > dcaCutOpts.cfgCutDCAz.value) { return false; + } return true; } @@ -1598,12 +2873,14 @@ struct PidFlowPtCorr { bool trackSelected4ITS(TTrack& track) { // its ncls cut - if (track.itsNCls() <= trkQualityOpts.cfgITSNCls.value) + if (track.itsNCls() <= trkQualityOpts.cfgITSNCls.value) { return false; + } // its chi2 - if (track.itsChi2NCl() > trkQualityOpts.cfgITSChi2NDF.value) + if (track.itsChi2NCl() > trkQualityOpts.cfgITSChi2NDF.value) { return false; + } return true; } @@ -1623,18 +2900,27 @@ struct PidFlowPtCorr { bool trackSelected4TPC(TTrack& track) { // tpc crossedrows cut - if (track.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) + if (track.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) { return false; + } // tpc ncls cut - if (track.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) + if (track.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) { return false; + } // tpc chi2 - if (track.tpcChi2NCl() > cfgCutChi2prTPCcls) + if (track.tpcChi2NCl() > cfgCutChi2prTPCcls) { return false; + } return true; } + template + bool trackSelectedForFlow(TTrack& track) + { + return trackSelectedGlobal(track, false) && track.hasITS() && track.hasTPC() && trackSelected4ITS(track) && trackSelected4TPC(track); + } + // track cut // event selection functions @@ -1663,11 +2949,82 @@ struct PidFlowPtCorr { case MyFunctionName::funcProcessSim: registry.fill(HIST("hEventCount/processSim"), position); break; + case MyFunctionName::funcProcessPidResponseMatrix: + registry.fill(HIST("hEventCount/processPidResponseMatrix"), position); + break; + case MyFunctionName::funcProcessDataC22DeltaPt: + registry.fill(HIST("hEventCount/processDataC22DeltaPt"), position); + break; + case MyFunctionName::funcProcessPidPtCorrelations: + registry.fill(HIST("hEventCount/processPidPtCorrelations"), position); + break; + case MyFunctionName::funcProcessDataTimeAssociation: + registry.fill(HIST("hEventCount/processDataTimeAssociation"), position); + break; + case MyFunctionName::funcFillCorrectionGraphTimeAssociation: + registry.fill(HIST("hEventCount/fillCorrectionGraphTimeAssociation"), position); + break; + case MyFunctionName::funcProcessMCTimeAssociation: + registry.fill(HIST("hEventCount/processMCTimeAssociation"), position); + break; + case MyFunctionName::funcProcessTimeAssociationComparison: + registry.fill(HIST("hEventCount/processTimeAssociationComparison"), position); + break; + case MyFunctionName::funcProcessChargedDifferentialTimeAssociation: + registry.fill(HIST("hEventCount/processChargedDifferentialTimeAssociation"), position); + break; default: - LOGF(warning, "could not find event count graph"); + // LOGF(warning, "could not find event count graph"); + break; + } + } + + /** + * @brief Get the event activity used for output binning and NUE lookup. + * @note Multiplicity mode uses raw PV contributors in |eta| < 0.8. + * It requires matching multiplicity axes and NUE maps; the caller + * must disable the centrality-calibrated cfgDoMultPVCut. + */ + template + float getCentrality(TCollision const& collision) + { + if (cfgUseMultiplicity.value) { + return collision.multNTracksPV(); + } + return getPercentileCentrality(collision); + } + + template + float getPercentileCentrality(TCollision const& collision) + { + float cent = -1; + switch (cfgCentEstimator) { + case kCentFT0C: + cent = collision.centFT0C(); + break; + case kCentFT0CVariant1: + cent = collision.centFT0CVariant1(); + break; + case kCentFT0M: + cent = collision.centFT0M(); + break; + case kCentFV0A: + cent = collision.centFV0A(); break; + case kCentNTPV: + cent = collision.centNTPV(); + break; + case kCentNGlobal: + cent = collision.centNGlobal(); + break; + case kCentMFT: + cent = collision.centMFT(); + break; + default: + cent = collision.centFT0C(); } + return cent; } /** @@ -1740,51 +3097,121 @@ struct PidFlowPtCorr { float zRes = std::sqrt(collision.covZZ()); double zResMin = 0.25; int numContMax = 20; - if (zRes > zResMin && collision.numContrib() < numContMax) + if (zRes > zResMin && collision.numContrib() < numContMax) { vtxz = -999; + } } auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (std::fabs(vtxz) > cfgCutVertex) + if (std::fabs(vtxz) > cfgCutVertex) { return false; + } - registry.fill(HIST("hNTracksPVvsCentrality"), multNTracksPV, centrality); + // The comparison has its own counter; do not double-fill physics event QA. + if (funcName != funcProcessTimeAssociationComparison) { + registry.fill(HIST("hNTracksPVvsCentrality"), multNTracksPV, centrality); + } if (evtSeleOpts.cfgDoMultPVCut.value) { - if (multNTracksPV < fMultPVCutLow->Eval(centrality)) + if (multNTracksPV < fMultPVCutLow->Eval(centrality)) { return false; - if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) + } + if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) { return false; + } } fillEventCountHelper(funcName, 9.5); - if (occupancy > evtSeleOpts.cfgCutOccupancyHigh.value) + if (occupancy > evtSeleOpts.cfgCutOccupancyHigh.value) { return 0; + } fillEventCountHelper(funcName, 10.5); // V0A T0A 5 sigma cut if (evtSeleOpts.cfgDoV0AT0Acut.value) { int nsigma = 5; - if (std::fabs(collision.multFV0A() - fT0AV0AMean->Eval(collision.multFT0A())) > nsigma * fT0AV0ASigma->Eval(collision.multFT0A())) + if (std::fabs(collision.multFV0A() - fT0AV0AMean->Eval(collision.multFT0A())) > nsigma * fT0AV0ASigma->Eval(collision.multFT0A())) { return 0; + } } fillEventCountHelper(funcName, 11.5); - registry.fill(HIST("hInteractionRate"), interactionRate); - if (interactionRate > 0 && interactionRate < evtSeleOpts.cfgCutminIR.value) + if (funcName != funcProcessTimeAssociationComparison) { + registry.fill(HIST("hInteractionRate"), interactionRate); + } + if (evtSeleOpts.cfgCutminIR.value >= 0 && interactionRate > 0 && interactionRate < evtSeleOpts.cfgCutminIR.value) { return false; + } fillEventCountHelper(funcName, 12.5); - if (interactionRate > evtSeleOpts.cfgCutmaxIR.value) + if (evtSeleOpts.cfgCutmaxIR.value >= 0 && interactionRate > evtSeleOpts.cfgCutmaxIR.value) { return false; + } fillEventCountHelper(funcName, 13.5); return true; } + double getInteractionRate(uint64_t timestamp, int runNumber) + { + const bool useMinIRCut = evtSeleOpts.cfgCutminIR.value >= 0; + const bool useMaxIRCut = evtSeleOpts.cfgCutmaxIR.value >= 0; + if ((!useMinIRCut && !useMaxIRCut) || evtSeleOpts.cfgIRSource.value.empty()) { + return -1.; + } + return rateFetcher.fetch(ccdb.service, timestamp, runNumber, evtSeleOpts.cfgIRSource.value) * 1.e-3; + } + // event selection // main functions + void loadChargedTimeCorrections(uint64_t timestamp, bool acceptance = true) + { + // Preserve the existing one/four-path convention, but load only charged. + constexpr std::size_t FullCorrectionSpecies = 4; + if (!timeEfficiencyLoaded) { + auto const& paths = correctionPathOpts.cfgEfficiencyPath.value; + if (paths.size() == 1 || paths.size() == FullCorrectionSpecies) { + auto* efficiency = ccdb->getForTimeStamp(paths.front(), timestamp); + if (!efficiency) { + LOGF(fatal, "Missing charged time-association efficiency: %s", paths.front().c_str()); + } + mEfficiency.push_back(efficiency); + } + timeEfficiencyLoaded = true; + } + if (acceptance && !timeAcceptanceLoaded) { + if (switchsOpts.cfgUseNUAWithPt) { + if (cfgAcceptanceWithPt.size() == 1 || cfgAcceptanceWithPt.size() == FullCorrectionSpecies) { + auto* weights = ccdb->getForTimeStamp(cfgAcceptanceWithPt.front(), timestamp); + if (!weights) { + LOGF(fatal, "Missing charged time-association NUA: %s", cfgAcceptanceWithPt.front().c_str()); + } + mAcceptanceWithPt.push_back(weights); + } + } else if (cfgAcceptance.size() == 1 || cfgAcceptance.size() == FullCorrectionSpecies) { + auto* weights = ccdb->getForTimeStamp(cfgAcceptance.front(), timestamp); + if (!weights) { + LOGF(fatal, "Missing charged time-association NUA: %s", cfgAcceptance.front().c_str()); + } + mAcceptance.push_back(weights); + } + timeAcceptanceLoaded = true; + } + } + + template + bool chargedTimeEventSelected(TCollision const& collision, MyFunctionName name) + { + fillEventCountHelper(name, 0.5); + if (!collision.sel8()) { + return false; + } + fillEventCountHelper(name, 1.5); + auto bc = collision.template bc_as(); + return eventSelected(collision, getCentrality(collision), getInteractionRate(bc.timestamp(), bc.runNumber()), name); + } + void processData(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracks const& tracks) { // sub region, init @@ -1794,21 +3221,24 @@ struct PidFlowPtCorr { float nMultTPC = collision.multTPC(); auto bc = collision.bc_as(); int runNumber = bc.runNumber(); - double interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 1.e-3; + double interactionRate = getInteractionRate(bc.timestamp(), runNumber); // end init // collision cut // include : 1.track.size 2.collision.sel8 3. evenSelected registry.fill(HIST("hEventCount/processData"), 0.5); - if (nTot < 1) + if (nTot < 1) { return; + } fGFW->Clear(); - const auto cent = collision.centFT0C(); - if (!collision.sel8()) + const auto cent = getCentrality(collision); + if (!collision.sel8()) { return; + } registry.fill(HIST("hEventCount/processData"), 1.5); - if (!eventSelected(collision, cent, interactionRate)) + if (!eventSelected(collision, cent, interactionRate)) { return; + } // end collision cut // pt spectra @@ -1825,33 +3255,6 @@ struct PidFlowPtCorr { registry.fill(HIST("hCent"), cent); // end correction - double psi2Est = 0, psi3Est = 0, psi4Est = 0; - float wEPeff = 1; - double v2 = 0, v3 = 0, v4 = 0; - if (switchsOpts.cfgDoLocDenCorr.value) { - double q2x = 0, q2y = 0; - double q3x = 0, q3y = 0; - double q4x = 0, q4y = 0; - for (const auto& track : tracks) { - // pt cut - bool withinPtRef = (trkQualityOpts.cfgCutPtMin.value < track.pt()) && (track.pt() < trkQualityOpts.cfgCutPtMax.value); // within RF pT rang - if (withinPtRef) { - q2x += std::cos(2 * track.phi()); - q2y += std::sin(2 * track.phi()); - q3x += std::cos(3 * track.phi()); - q3y += std::sin(3 * track.phi()); - q4x += std::cos(4 * track.phi()); - q4y += std::sin(4 * track.phi()); - } - } - psi2Est = std::atan2(q2y, q2x) / 2.; - psi3Est = std::atan2(q3y, q3x) / 3.; - psi4Est = std::atan2(q4y, q4x) / 4.; - v2 = funcV2->Eval(cent); - v3 = funcV3->Eval(cent); - v4 = funcV4->Eval(cent); - } // cfgDoLocDenCorr - double ptSum = 0; double ptSumw2 = 0; double nch = 0; @@ -1868,128 +3271,99 @@ struct PidFlowPtCorr { double pionPtSum = 0; double kaonPtSum = 0; double protonPtSum = 0; + double unidentifiedPtSum = 0; double nPionWeighted = 0; double nKaonWeighted = 0; double nProtonWeighted = 0; + double nUnidentifiedWeighted = 0; double pionPtSumw2 = 0; double kaonPtSumw2 = 0; double protonPtSumw2 = 0; + double unidentifiedPtSumw2 = 0; double nPionSquare = 0; double nKaonSquare = 0; double nProtonSquare = 0; + double nUnidentifiedSquare = 0; double pionPtSquareSum = 0; double kaonPtSquareSum = 0; double protonPtSquareSum = 0; + double unidentifiedPtSquareSum = 0; // end val for pid particles - /// @note calculate pt - /// use ITS only + // Calculate mean-pT moments and PID-particle sums. for (const auto& track : tracks) { float weff = 1; - // do nue - if (switchsOpts.cfgUseITSOnly4MeanPt.value) - setParticleNUEWeight(weff, track, cent, false); - else - setParticleNUEWeight(weff, track, cent, true); - // end do nue - - // track cut ITS only - if (!trackSelectedGlobal(track)) - continue; - if (!track.hasITS()) + // The reference and POI pT ranges are applied independently below. + if (!trackSelectedForFlow(track)) { continue; - if (!trackSelected4ITS(track)) - continue; - - if (switchsOpts.cfgUseITSOnly4MeanPt.value) { - if (track.hasTPC()) - continue; - } else { - if (!track.hasTPC()) - continue; - if (!trackSelected4TPC(track)) - continue; } - // end track cut its only - if (switchsOpts.cfgOutPutPtSpectra.value) { - registry.fill(HIST("ptSpectra/hPtCentData4ITSOnly"), track.pt(), cent); - } + // do nue + setParticleNUEWeight(weff, track, cent); + // end do nue // calculate ncharged(nch with weight) and pt - if (std::fabs(track.eta()) < trkQualityOpts.cfgRangeEta.value) { + bool withinPtRef = isWithinRefPtRange(track.pt()); + if (withinPtRef && std::fabs(track.eta()) < trkQualityOpts.cfgRangeEta.value) { nch += weff; nchSquare += weff * weff; ptSum += weff * track.pt(); ptSumw2 += weff * weff * track.pt(); ptSquareSum += weff * weff * track.pt() * track.pt(); + } + // end calculate nch and pt + // Calculate identified POIs with species corrections. Tracks returning + // pid == -1 form the unidentified POI and use charged pT/NUE settings. + if (std::fabs(track.eta()) < trkQualityOpts.cfgRangeEta.value) { // ------------------------------ // Unified PID logic (configurable) // ------------------------------ - int pid = -1; - if (circleCutOpts.cfgUseCircleCutPID.value) { - // Use circular cut (already handles ambiguity rejection) - pid = getPidWithCircleCut(track); - } else { - // Use normal cut (with manual ambiguity rejection) - bool isPi = isPion(track); - bool isKa = isKaon(track); - bool isPr = isProton(track); - int nCandidates = isPi + isKa + isPr; - if (nCandidates == 1) { - if (isPi) - pid = MyParticleType::kPion; - else if (isKa) - pid = MyParticleType::kKaon; - else if (isPr) - pid = MyParticleType::kProton; + int pid = getPidConfigurable(track); + if (pid == -1 && withinPtRef) { + nUnidentifiedWeighted += weff; + nUnidentifiedSquare += weff * weff; + unidentifiedPtSum += weff * track.pt(); + unidentifiedPtSumw2 += weff * weff * track.pt(); + unidentifiedPtSquareSum += weff * weff * track.pt() * track.pt(); + } else if (isWithinPOIPtRange(pid, track.pt())) { + float weffPid = 1.; + setParticleNUEWeight(weffPid, track, cent, pid); + if (pid == MyParticleType::kPion) { + nPionWeighted += weffPid; + nPionSquare += weffPid * weffPid; + pionPtSum += weffPid * track.pt(); + pionPtSumw2 += weffPid * weffPid * track.pt(); + pionPtSquareSum += weffPid * weffPid * track.pt() * track.pt(); + } else if (pid == MyParticleType::kKaon) { + nKaonWeighted += weffPid; + nKaonSquare += weffPid * weffPid; + kaonPtSum += weffPid * track.pt(); + kaonPtSumw2 += weffPid * weffPid * track.pt(); + kaonPtSquareSum += weffPid * weffPid * track.pt() * track.pt(); + } else if (pid == MyParticleType::kProton) { + nProtonWeighted += weffPid; + nProtonSquare += weffPid * weffPid; + protonPtSum += weffPid * track.pt(); + protonPtSumw2 += weffPid * weffPid * track.pt(); + protonPtSquareSum += weffPid * weffPid * track.pt() * track.pt(); } - // else: pid remains -1 (ambiguous or none) } - - // Fill PID variables based on unified result - if (pid == MyParticleType::kPion) { - nPionWeighted += weff; - nPionSquare += weff * weff; - pionPtSum += weff * track.pt(); - pionPtSumw2 += weff * weff * track.pt(); - pionPtSquareSum += weff * weff * track.pt() * track.pt(); - } else if (pid == MyParticleType::kKaon) { - nKaonWeighted += weff; - nKaonSquare += weff * weff; - kaonPtSum += weff * track.pt(); - kaonPtSumw2 += weff * weff * track.pt(); - kaonPtSquareSum += weff * weff * track.pt() * track.pt(); - } else if (pid == MyParticleType::kProton) { - nProtonWeighted += weff; - nProtonSquare += weff * weff; - protonPtSum += weff * track.pt(); - protonPtSumw2 += weff * weff * track.pt(); - protonPtSquareSum += weff * weff * track.pt() * track.pt(); - } - // else: do nothing (ambiguous or not identified) } - // end calculate nch and pt - - nchCorrectedPassed += weff; - nchPassedSelection += 1; + // end calculate POI sums - if (weff == 1. && switchsOpts.cfgDebugMyCode.value) { - LOGF(info, "weff is 1, if nue opt is open and this message appears a lot, check!"); + if (withinPtRef) { + nchCorrectedPassed += weff; + nchPassedSelection += 1; } } // end pt calculation using ITS only - if (switchsOpts.cfgDebugMyCode.value) { - LOGF(info, Form("its only track num %f", nchPassedSelection)); - } - int totalGlobalTrack = 0; // calculate number of pid particle int numOfPi = 0; @@ -2004,38 +3378,13 @@ struct PidFlowPtCorr { // do NUE && NUA setParticleNUAWeight(wacc, track, vtxz); - setParticleNUEWeight(weff, track, cent, true); + setParticleNUEWeight(weff, track, cent); // end do NUE && NUA - if (switchsOpts.cfgDoLocDenCorr.value) { - bool withinPtRef = (trkQualityOpts.cfgCutPtMin.value < track.pt()) && (track.pt() < trkQualityOpts.cfgCutPtMax.value); - if (withinPtRef) { - double fphi = v2 * std::cos(2 * (track.phi() - psi2Est)) + v3 * std::cos(3 * (track.phi() - psi3Est)) + v4 * std::cos(4 * (track.phi() - psi4Est)); - fphi = (1 + 2 * fphi); - int pTBinForEff = hFindPtBin->FindBin(track.pt()); - if (pTBinForEff >= 1 && pTBinForEff <= hFindPtBin->GetNbinsX()) { - wEPeff = funcEff[pTBinForEff - 1]->Eval(fphi * tracks.size()); - if (wEPeff > 0.) { - wEPeff = 1. / wEPeff; - weff *= wEPeff; - } - } - } - } // cfgDoLocDenCorr - - // track cut, global + ITS + TPC - if (!trackSelectedGlobal(track)) + // The reference and POI pT ranges are applied independently below. + if (!trackSelectedForFlow(track)) { continue; - if (!track.hasITS()) - continue; - if (!track.hasTPC()) - continue; - if (!trackSelected4ITS(track)) - continue; - if (!trackSelected4TPC(track)) - continue; - - // end track cut + } totalGlobalTrack++; if (switchsOpts.cfgDebugMyCode.value && weff == 1.) { @@ -2046,62 +3395,89 @@ struct PidFlowPtCorr { LOGF(info, "wacc for global track is 1, if NUA is open and this appears alot, check!"); } - // fill QA hist - registry.fill(HIST("hPhi"), track.phi()); - registry.fill(HIST("hPhicorr"), track.phi(), wacc); - registry.fill(HIST("hEta"), track.eta()); - registry.fill(HIST("hPt"), track.pt()); - // end fill QA hist + bool withinPtRefGlobal = isWithinRefPtRange(track.pt()); + if (withinPtRefGlobal) { + // fill QA hist + registry.fill(HIST("hPhi"), track.phi()); + registry.fill(HIST("hPhicorr"), track.phi(), wacc); + registry.fill(HIST("hPhiCorrNUANUE"), track.phi(), wacc * weff); + registry.fill(HIST("hEta"), track.eta()); + registry.fill(HIST("hPt"), track.pt()); + registry.fill(HIST("hPtCorr"), track.pt(), weff); + // end fill QA hist + + // pt spectra + if (switchsOpts.cfgOutPutPtSpectra.value) { + registry.fill(HIST("ptSpectra/hPtCentData"), track.pt(), cent); + } - // pt spectra - if (switchsOpts.cfgOutPutPtSpectra.value) { - registry.fill(HIST("ptSpectra/hPtCentData"), track.pt(), cent); + // Fill the charged reference regions. + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::charged::kReference); //(eta, ptbin, phi, wacc*weff, bitmask) } - // fill GFW - // bit mask 1: fill CHARGED PARTICLES - fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, 1); //(eta, ptbin, phi, wacc*weff, bitmask) - // ------------------------------ // Unified PID logic (configurable) // ------------------------------ - int pid = -1; - if (circleCutOpts.cfgUseCircleCutPID.value) { - // Use circular cut (already handles ambiguity rejection) - pid = getPidWithCircleCut(track); - } else { - // Use normal cut (with manual ambiguity rejection) - bool isPi = isPion(track); - bool isKa = isKaon(track); - bool isPr = isProton(track); - int nCandidates = isPi + isKa + isPr; - if (nCandidates == 1) { - if (isPi) - pid = MyParticleType::kPion; - else if (isKa) - pid = MyParticleType::kKaon; - else if (isPr) - pid = MyParticleType::kProton; - } - // else: pid remains -1 (ambiguous or none) - } - - // Fill GFW and counters based on unified result - if (pid == MyParticleType::kPion) { - // bitmask 18: 0010010 - fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, 18); - numOfPi++; - } else if (pid == MyParticleType::kKaon) { - // bitmask 36: 0100100 - fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, 36); - numOfKa++; - } else if (pid == MyParticleType::kProton) { - // bitmask 72: 1001000 - fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, 72); - numOfPr++; + int pid = getPidConfigurable(track); + if (particleAbundanceOpts.cfgOutputAbundanceYields.value && withinPtRefGlobal) { + if (pid == MyParticleType::kPion) { + registry.fill(HIST("abundance/hPtCentDataPi"), track.pt(), cent); + } else if (pid == MyParticleType::kKaon) { + registry.fill(HIST("abundance/hPtCentDataKa"), track.pt(), cent); + } else if (pid == MyParticleType::kProton) { + registry.fill(HIST("abundance/hPtCentDataPr"), track.pt(), cent); + } else { + registry.fill(HIST("abundance/hPtCentDataUnidentified"), track.pt(), cent); + } + } + if (pid == -1 && withinPtRefGlobal) { + // Unidentified tracks have no PID-specific correction. Reuse exactly + // the charged NUA/NUE weights. + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::unidentified::kPOI); + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::unidentified::kOverlap, wacc * weff); + } else if (isWithinPOIPtRange(pid, track.pt())) { + float waccPid = 1; + float weffPid = 1; + this->setParticleNUAWeight(waccPid, track, vtxz, pid); + this->setParticleNUEWeight(weffPid, track, cent, pid); + + // Fill GFW and counters using the same species-specific POI pT range. + if (pid == MyParticleType::kPion) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kPOI); + // Only tracks in both pT selections contribute to POI-ref overlap. + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kOverlap, wacc * weff); + } + registry.fill(HIST("hPhiPi"), track.phi()); + registry.fill(HIST("hPhicorrPi"), track.phi(), waccPid); + registry.fill(HIST("hPhiCorrNUANUEPi"), track.phi(), waccPid * weffPid); + registry.fill(HIST("hPtPi"), track.pt()); + registry.fill(HIST("hPtCorrPi"), track.pt(), weffPid); + numOfPi++; + } else if (pid == MyParticleType::kKaon) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kPOI); + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kOverlap, wacc * weff); + } + registry.fill(HIST("hPhiKa"), track.phi()); + registry.fill(HIST("hPhicorrKa"), track.phi(), waccPid); + registry.fill(HIST("hPhiCorrNUANUEKa"), track.phi(), waccPid * weffPid); + registry.fill(HIST("hPtKa"), track.pt()); + registry.fill(HIST("hPtCorrKa"), track.pt(), weffPid); + numOfKa++; + } else if (pid == MyParticleType::kProton) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kPOI); + if (withinPtRefGlobal) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kOverlap, wacc * weff); + } + registry.fill(HIST("hPhiPr"), track.phi()); + registry.fill(HIST("hPhicorrPr"), track.phi(), waccPid); + registry.fill(HIST("hPhiCorrNUANUEPr"), track.phi(), waccPid * weffPid); + registry.fill(HIST("hPtPr"), track.pt()); + registry.fill(HIST("hPtCorrPr"), track.pt(), weffPid); + numOfPr++; + } } - // else: do nothing (ambiguous or not identified) - // end fill GFW } // end track loop for v2 calculation // sub region, fill graphs after 2 loop on all tracks @@ -2119,189 +3495,1320 @@ struct PidFlowPtCorr { // fill hist using fGFW if (nch > 0) { - fillFC(MyParticleType::kCharged, corrconfigs.at(0), cent, rndm, "c22"); - fillFC(MyParticleType::kCharged, corrconfigs.at(1), cent, rndm, "c24"); - fillFC(MyParticleType::kCharged, corrconfigs.at(2), cent, rndm, "c22Full"); - fillFC(MyParticleType::kCharged, corrconfigs.at(3), cent, rndm, "c32"); - fillFC(MyParticleType::kCharged, corrconfigs.at(4), cent, rndm, "c34"); - - fillFC(MyParticleType::kPion, corrconfigs.at(35), cent, rndm, "c22Full"); - fillFC(MyParticleType::kPion, corrconfigs.at(36), cent, rndm, "c22Full"); - fillFC(MyParticleType::kKaon, corrconfigs.at(37), cent, rndm, "c22Full"); - fillFC(MyParticleType::kKaon, corrconfigs.at(38), cent, rndm, "c22Full"); - fillFC(MyParticleType::kProton, corrconfigs.at(39), cent, rndm, "c22Full"); - fillFC(MyParticleType::kProton, corrconfigs.at(40), cent, rndm, "c22Full"); - - fillFC(MyParticleType::kPion, corrconfigs.at(11), cent, rndm, "c24"); - fillFC(MyParticleType::kPion, corrconfigs.at(12), cent, rndm, "c24"); - fillFC(MyParticleType::kKaon, corrconfigs.at(13), cent, rndm, "c24"); - fillFC(MyParticleType::kKaon, corrconfigs.at(14), cent, rndm, "c24"); - fillFC(MyParticleType::kProton, corrconfigs.at(15), cent, rndm, "c24"); - fillFC(MyParticleType::kProton, corrconfigs.at(16), cent, rndm, "c24"); - - fillFC(MyParticleType::kPion, corrconfigs.at(17), cent, rndm, "c32"); - fillFC(MyParticleType::kPion, corrconfigs.at(18), cent, rndm, "c32"); - fillFC(MyParticleType::kKaon, corrconfigs.at(19), cent, rndm, "c32"); - fillFC(MyParticleType::kKaon, corrconfigs.at(20), cent, rndm, "c32"); - fillFC(MyParticleType::kProton, corrconfigs.at(21), cent, rndm, "c32"); - fillFC(MyParticleType::kProton, corrconfigs.at(22), cent, rndm, "c32"); - - fillFC(MyParticleType::kPion, corrconfigs.at(23), cent, rndm, "c34"); - fillFC(MyParticleType::kPion, corrconfigs.at(24), cent, rndm, "c34"); - fillFC(MyParticleType::kKaon, corrconfigs.at(25), cent, rndm, "c34"); - fillFC(MyParticleType::kKaon, corrconfigs.at(26), cent, rndm, "c34"); - fillFC(MyParticleType::kProton, corrconfigs.at(27), cent, rndm, "c34"); - fillFC(MyParticleType::kProton, corrconfigs.at(28), cent, rndm, "c34"); - - bool filledPi = fillFC(MyParticleType::kPion, corrconfigs.at(29), cent, rndm, "c22pure"); - bool filledKa = fillFC(MyParticleType::kKaon, corrconfigs.at(30), cent, rndm, "c22pure"); - bool filledPr = fillFC(MyParticleType::kProton, corrconfigs.at(31), cent, rndm, "c22pure"); - - fillFC(MyParticleType::kPion, corrconfigs.at(32), cent, rndm, "c32pure"); - fillFC(MyParticleType::kKaon, corrconfigs.at(33), cent, rndm, "c32pure"); - fillFC(MyParticleType::kProton, corrconfigs.at(34), cent, rndm, "c32pure"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, "c22"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, "c24"); + // Raw gap <4> for SC(ch,ch); reuse c22 for both two-particle factors. + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24Gap), cent, rndm, "c24Gap"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, "c22Full"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, "c32"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, "c34"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22FullA), cent, rndm, "c22Full"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22FullB), cent, rndm, "c22Full"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22FullA), cent, rndm, "c22Full"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22FullB), cent, rndm, "c22Full"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22FullA), cent, rndm, "c22Full"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22FullB), cent, rndm, "c22Full"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22FullA), cent, rndm, "c22Full"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22FullB), cent, rndm, "c22Full"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC24A), cent, rndm, "c24"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC24B), cent, rndm, "c24"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC24A), cent, rndm, "c24"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC24B), cent, rndm, "c24"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC24A), cent, rndm, "c24"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC24B), cent, rndm, "c24"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC24A), cent, rndm, "c24"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC24B), cent, rndm, "c24"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32GapA), cent, rndm, "c32"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32GapB), cent, rndm, "c32"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32GapA), cent, rndm, "c32"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32GapB), cent, rndm, "c32"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32GapA), cent, rndm, "c32"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32GapB), cent, rndm, "c32"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC34A), cent, rndm, "c34"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC34B), cent, rndm, "c34"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC34A), cent, rndm, "c34"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC34B), cent, rndm, "c34"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC34A), cent, rndm, "c34"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC34B), cent, rndm, "c34"); + + bool filledPi = fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, "c22pure"); + bool filledKa = fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, "c22pure"); + bool filledPr = fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, "c22pure"); + bool filledUnidentified = fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), cent, rndm, "c22pure"); + + // SC/NSC components only: the mixed raw <4> uses its GFW tuple weight + // and the same bootstrap draw as the existing c22pure and charged c22. + // Existing <2> profiles retain their own valid-event samples/weights. + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC24PIDChargedGap), cent, rndm, "c24PIDChargedGap"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, "c32pure"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, "c32pure"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, "c32pure"); + + if (filledPi || !switchsOpts.cfgCheck2MethodDiff.value) { + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22GapA), cent, rndm, "c22"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22GapB), cent, rndm, "c22"); + } + if (filledKa || !switchsOpts.cfgCheck2MethodDiff.value) { + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22GapA), cent, rndm, "c22"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22GapB), cent, rndm, "c22"); + } + if (filledPr || !switchsOpts.cfgCheck2MethodDiff.value) { + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22GapA), cent, rndm, "c22"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22GapB), cent, rndm, "c22"); + } + if (filledUnidentified || !switchsOpts.cfgCheck2MethodDiff.value) { + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22GapA), cent, rndm, "c22"); + fillFC(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22GapB), cent, rndm, "c22"); + } + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeightOne", true); + + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, nch, nch, "c24TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, nch, nch, "c22FullTrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, nch, nch, "c34TrackWeight"); + + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), cent, rndm, nch, nch, "c22TrackWeight"); + + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, nch, nch, "c32TrackWeight"); + + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2PtWeightOne", true); + + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), cent, rndm, ptSum, nch, "covV2Pt"); + + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, ptSum, nch, "covV3Pt"); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, ptSum, nch, "covV3Pt"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, ptSum, nch, "covV3Pt"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, ptSum, nch, "covV3Pt"); + + fillFC4PtC22(cent, ptSum, nch, rndm); + if (nPionWeighted > 0) { + fillFC4PtC22(cent, rndm, MyParticleType::kPion, pionPtSum, nPionWeighted); + } + + if (nKaonWeighted > 0) { + fillFC4PtC22(cent, rndm, MyParticleType::kKaon, kaonPtSum, nKaonWeighted); + } + + if (nProtonWeighted > 0) { + fillFC4PtC22(cent, rndm, MyParticleType::kProton, protonPtSum, nProtonWeighted); + } + if (nUnidentifiedWeighted > 0) { + fillFC4PtC22(cent, rndm, MyParticleType::kUnidentified, unidentifiedPtSum, nUnidentifiedWeighted); + } + + if (switchsOpts.cfgOutPutPtSpectra.value) { + // charged calculation + double NpairCharged = fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, true).real(); + double chargedC22 = NpairCharged > 0 ? fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, false).real() / NpairCharged : 0; + // end charged calculation + + // pi + double pidChargedC22Pi = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::pion::kC22GapA), corrconfigs.at(corrConfigIndex::pion::kC22GapB)); + if (pidChargedC22Pi > 0 && chargedC22 > 0) { + registry.fill(HIST("c22PrimeVsc22/Pi"), pidChargedC22Pi, chargedC22); + } + // end pi + + // Ka + double pidKaonC22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::kaon::kC22GapA), corrconfigs.at(corrConfigIndex::kaon::kC22GapB)); + if (pidKaonC22 > 0 && chargedC22 > 0) { + registry.fill(HIST("c22PrimeVsc22/Ka"), pidKaonC22, chargedC22); + } + // end Ka + + // Pr + double pidProtonC22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::proton::kC22GapA), corrconfigs.at(corrConfigIndex::proton::kC22GapB)); + if (pidProtonC22 > 0 && chargedC22 > 0) { + registry.fill(HIST("c22PrimeVsc22/Pr"), pidProtonC22, chargedC22); + } + // end Pr + } + + if (switchsOpts.cfgOutputCharged.value) { + fFCCh->FillProfile("hMeanPt", cent, (ptSum / nch), nch, rndm); + fFCCh->FillProfile("hMeanPtWeightOne", cent, (ptSum / nch), 1., rndm); + } + + if (nPionWeighted > 0 && switchsOpts.cfgOutputPion.value) { + fFCPi->FillProfile("hMeanPt", cent, (pionPtSum / nPionWeighted), nPionWeighted, rndm); + fFCPi->FillProfile("hMeanPtWeightOne", cent, (pionPtSum / nPionWeighted), 1., rndm); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, pionPtSum, nPionWeighted, "covV2PtPID"); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, nPionWeighted, nPionWeighted, "c22TrackWeightPID"); + } + + if (nKaonWeighted > 0 && switchsOpts.cfgOutputKaon.value) { + fFCKa->FillProfile("hMeanPt", cent, (kaonPtSum / nKaonWeighted), nKaonWeighted, rndm); + fFCKa->FillProfile("hMeanPtWeightOne", cent, (kaonPtSum / nKaonWeighted), 1., rndm); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, kaonPtSum, nKaonWeighted, "covV2PtPID"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, nKaonWeighted, nKaonWeighted, "c22TrackWeightPID"); + } + + if (nProtonWeighted > 0 && switchsOpts.cfgOutputProton.value) { + fFCPr->FillProfile("hMeanPt", cent, (protonPtSum / nProtonWeighted), nProtonWeighted, rndm); + fFCPr->FillProfile("hMeanPtWeightOne", cent, (protonPtSum / nProtonWeighted), 1., rndm); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, protonPtSum, nProtonWeighted, "covV2PtPID"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, nProtonWeighted, nProtonWeighted, "c22TrackWeightPID"); + } + + if (nUnidentifiedWeighted > 0 && switchsOpts.cfgOutputUnidentified.value) { + fFCUnidentified->FillProfile("hMeanPt", cent, unidentifiedPtSum / nUnidentifiedWeighted, nUnidentifiedWeighted, rndm); + fFCUnidentified->FillProfile("hMeanPtWeightOne", cent, unidentifiedPtSum / nUnidentifiedWeighted, 1., rndm); + fillFCvnpt(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), cent, rndm, unidentifiedPtSum, nUnidentifiedWeighted, "covV2PtPID"); + fillFCvnpt(MyParticleType::kUnidentified, corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), cent, rndm, nUnidentifiedWeighted, nUnidentifiedWeighted, "c22TrackWeightPID"); + } + + double nchDiff = nch * nch - nchSquare; + if (nchDiff > minVal4Float && switchsOpts.cfgOutputCharged.value) { + fFCCh->FillProfile("ptSquareAve", cent, + (ptSum * ptSum - ptSquareSum) / nchDiff, + nchDiff, rndm); + + fFCCh->FillProfile("ptSquareAveWeightOne", cent, + (ptSum * ptSum - ptSquareSum) / nchDiff, + 1., rndm); + + fFCCh->FillProfile("ptAve", cent, + (nch * ptSum - ptSumw2) / nchDiff, + nchDiff, rndm); + + fFCCh->FillProfile("ptAveWeightOne", cent, + (nch * ptSum - ptSumw2) / nchDiff, + 1., rndm); + } + + double pionDiff = nPionWeighted * nPionWeighted - nPionSquare; + if (pionDiff > minVal4Float && switchsOpts.cfgOutputPion.value) { + fFCPi->FillProfile("ptSquareAve", cent, + (pionPtSum * pionPtSum - pionPtSquareSum) / pionDiff, + pionDiff, rndm); + + fFCPi->FillProfile("ptSquareAveWeightOne", cent, + (pionPtSum * pionPtSum - pionPtSquareSum) / pionDiff, + 1., rndm); + + fFCPi->FillProfile("ptAve", cent, + (nPionWeighted * pionPtSum - pionPtSumw2) / pionDiff, + pionDiff, rndm); + + fFCPi->FillProfile("ptAveWeightOne", cent, + (nPionWeighted * pionPtSum - pionPtSumw2) / pionDiff, + 1., rndm); + } + + double kaonDiff = nKaonWeighted * nKaonWeighted - nKaonSquare; + if (kaonDiff > minVal4Float && switchsOpts.cfgOutputKaon.value) { + fFCKa->FillProfile("ptSquareAve", cent, + (kaonPtSum * kaonPtSum - kaonPtSquareSum) / kaonDiff, + kaonDiff, rndm); + + fFCKa->FillProfile("ptSquareAveWeightOne", cent, + (kaonPtSum * kaonPtSum - kaonPtSquareSum) / kaonDiff, + 1., rndm); + + fFCKa->FillProfile("ptAve", cent, + (nKaonWeighted * kaonPtSum - kaonPtSumw2) / kaonDiff, + kaonDiff, rndm); + + fFCKa->FillProfile("ptAveWeightOne", cent, + (nKaonWeighted * kaonPtSum - kaonPtSumw2) / kaonDiff, + 1., rndm); + } + + double protonDiff = nProtonWeighted * nProtonWeighted - nProtonSquare; + if (protonDiff > minVal4Float && switchsOpts.cfgOutputProton.value) { + fFCPr->FillProfile("ptSquareAve", cent, + (protonPtSum * protonPtSum - protonPtSquareSum) / protonDiff, + protonDiff, rndm); + + fFCPr->FillProfile("ptSquareAveWeightOne", cent, + (protonPtSum * protonPtSum - protonPtSquareSum) / protonDiff, + 1., rndm); + + fFCPr->FillProfile("ptAve", cent, + (nProtonWeighted * protonPtSum - protonPtSumw2) / protonDiff, + protonDiff, rndm); + + fFCPr->FillProfile("ptAveWeightOne", cent, + (nProtonWeighted * protonPtSum - protonPtSumw2) / protonDiff, + 1., rndm); + } + + double unidentifiedDiff = nUnidentifiedWeighted * nUnidentifiedWeighted - nUnidentifiedSquare; + if (unidentifiedDiff > minVal4Float && switchsOpts.cfgOutputUnidentified.value) { + fFCUnidentified->FillProfile("ptSquareAve", cent, + (unidentifiedPtSum * unidentifiedPtSum - unidentifiedPtSquareSum) / unidentifiedDiff, + unidentifiedDiff, rndm); + fFCUnidentified->FillProfile("ptSquareAveWeightOne", cent, + (unidentifiedPtSum * unidentifiedPtSum - unidentifiedPtSquareSum) / unidentifiedDiff, + 1., rndm); + fFCUnidentified->FillProfile("ptAve", cent, + (nUnidentifiedWeighted * unidentifiedPtSum - unidentifiedPtSumw2) / unidentifiedDiff, + unidentifiedDiff, rndm); + fFCUnidentified->FillProfile("ptAveWeightOne", cent, + (nUnidentifiedWeighted * unidentifiedPtSum - unidentifiedPtSumw2) / unidentifiedDiff, + 1., rndm); + } + + } // end fill hist using fillProfile + } + PROCESS_SWITCH(PidFlowPtCorr, processData, "", true); + + void initTimeAssociationComparisonQA() + { + constexpr const char* selection = "Common selected events, final charged selection, unit weights"; + registry.add("associationComparison/hEventAvailability", "Common events including empty track sets;Centrality (%);Track availability", {HistType::kTH2D, {axisMultiplicity, {4, -0.5, 3.5}}}); + registry.add("associationComparison/hNchOriginalVsTime", "Final charged multiplicity;N_{ch} original;N_{ch} time;Event count", {HistType::kTH2D, {cfgaxisAssociationNch, cfgaxisAssociationNch}}); + registry.add("associationComparison/hLostVsGained", "Track membership changes;Lost tracks;Gained tracks;Event count", {HistType::kTH2D, {cfgaxisAssociationChangedNch, cfgaxisAssociationChangedNch}}); + registry.add("associationComparison/hDeltaNchVsCent", "Multiplicity change;Centrality (%);N_{ch}^{time}-N_{ch}^{original};Event count", {HistType::kTH2D, {axisMultiplicity, cfgaxisAssociationDeltaNch}}); + registry.add("associationComparison/hDeltaNchVsOccupancy", "Multiplicity change;Track occupancy;N_{ch}^{time}-N_{ch}^{original};Event count", {HistType::kTH2D, {cfgaxisAssociationOccupancy, cfgaxisAssociationDeltaNch}}); + registry.add("associationComparison/hSetChangeVsCent", "Nonempty union only;Centrality (%);(lost+gained)/(common+lost+gained);Event count", {HistType::kTH2D, {axisMultiplicity, {101, -0.005, 1.005}}}); + registry.add("associationComparison/hSetChangeVsOccupancy", "Nonempty union only;Track occupancy;(lost+gained)/(common+lost+gained);Event count", {HistType::kTH2D, {cfgaxisAssociationOccupancy, {101, -0.005, 1.005}}}); + registry.add("associationComparison/pSetChangeVsCent", "Event mean, nonempty union only;Centrality (%);Mean set-change fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pSetChangeVsOccupancy", "Event mean, nonempty union only;Track occupancy;Mean set-change fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + registry.add("associationComparison/pLostFractionVsCent", "Event mean, original Nch > 0;Centrality (%);Mean lost/original fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pLostFractionVsOccupancy", "Event mean, original Nch > 0;Track occupancy;Mean lost/original fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + registry.add("associationComparison/pGainedFractionVsCent", "Event mean, time Nch > 0;Centrality (%);Mean gained/time fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("associationComparison/pGainedFractionVsOccupancy", "Event mean, time Nch > 0;Track occupancy;Mean gained/time fraction", {HistType::kTProfile, {cfgaxisAssociationOccupancy}}); + // Category projections provide original/time denominators for lost/gained + // spectra ratios. Entries count track-target pairs, not distinct sources. + registry.add("associationComparison/hPtByCategory", "Final tracks per target collision;p_{T} (GeV/c);Track category;Track-target count", {HistType::kTH2D, {cfgaxisPt, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hEtaByCategory", "Final tracks per target collision;#eta;Track category;Track-target count", {HistType::kTH2D, {cfgaxisEta, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hPhiByCategory", "Final tracks per target collision;#varphi;Track category;Track-target count", {HistType::kTH2D, {cfgaxisPhi, {5, -0.5, 4.5}}}); + registry.add("associationComparison/hFinalCollisionsOriginal", "All source tracks once per dataframe;N selected collisions (original);Source track count", {HistType::kTH1D, {{2, -0.5, 1.5}}}); + registry.add("associationComparison/hFinalCollisionsTime", "All source tracks once per dataframe;N selected collisions (time);Source track count", {HistType::kTH1D, {{101, -0.5, 100.5}}}); + registry.add("associationComparison/hFinalCollisionsOriginalVsTime", "All source tracks once per dataframe;N selected collisions (original);N selected collisions (time);Source track count", {HistType::kTH2D, {{2, -0.5, 1.5}, {101, -0.5, 100.5}}}); + const auto setLabels = [](TAxis* axis, auto const& labels) { + for (std::size_t index = 0; index < labels.size(); ++index) { + axis->SetBinLabel(index + 1, labels[index]); + } + }; + setLabels(registry.get(HIST("associationComparison/hEventAvailability"))->GetYaxis(), + std::array{"both empty", "original only", "time only", "both nonempty"}); + const std::array categories{"original", "time", "common", "lost", "gained"}; + setLabels(registry.get(HIST("associationComparison/hPtByCategory"))->GetYaxis(), categories); + setLabels(registry.get(HIST("associationComparison/hEtaByCategory"))->GetYaxis(), categories); + setLabels(registry.get(HIST("associationComparison/hPhiByCategory"))->GetYaxis(), categories); + LOG(info) << "Association comparison: " << selection << "; original=collisionId, time=PidFlowTimeTracks; PV=locked-original, nonPV=closest-time"; + } + + // Evaluate both associations in the same event loop without PID or correction + // weights. Keep the full source table to include zero-use tracks in usage QA. + void processTimeAssociationComparison(AodCollisions const& collisions, aod::BCsWithTimestamps const&, + PidFlowChargedTimeProducer::SourceTracks const& sources, aod::PidFlowTimeTracks const& associatedTracks) + { + if (!cfgTimeAssociationQA) { + return; + } + std::vector originalUses(sources.size(), 0), timeUses(sources.size(), 0); + const auto sourceIndex = [&](int64_t trackId) { + const int64_t index = trackId - sources.offset(); + if (index < 0 || index >= sources.size()) { + LOGF(fatal, "Association comparison track index is outside the full source track table"); + } + return index; + }; + const auto fillKinematics = [&](auto const& track, int category) { + registry.fill(HIST("associationComparison/hPtByCategory"), track.pt(), category); + registry.fill(HIST("associationComparison/hEtaByCategory"), track.eta(), category); + registry.fill(HIST("associationComparison/hPhiByCategory"), track.phi(), category); + }; + for (auto const& collision : collisions) { + // No track-presence requirement: both methods share this event sample. + if (!chargedTimeEventSelected(collision, funcProcessTimeAssociationComparison)) { + continue; + } + auto originalTracks = sources.sliceBy(perCollision, collision.globalIndex()); + auto timeTracks = associatedTracks.sliceBy(timeTracksPerCollision, collision.globalIndex()); + std::unordered_set originalIds, timeIds; + for (auto const& track : originalTracks) { + // Reproduce processData's framework filter and final charged cuts, + // without requiring or evaluating any PID tables. + if (!(track.isGlobalTrack() || track.isGlobalTrackSDD() != 0) || !(std::fabs(track.eta()) < trkQualityOpts.cfgCutEta.value) || + !trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + if (originalIds.insert(track.globalIndex()).second) { + ++originalUses[sourceIndex(track.globalIndex())]; + fillKinematics(track, 0); + } + } + unsigned int common = 0; + for (auto const& track : timeTracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt()) || !timeIds.insert(track.trackId()).second) { + continue; + } + ++timeUses[sourceIndex(track.trackId())]; + fillKinematics(track, 1); + if (originalIds.contains(track.trackId())) { + ++common; + fillKinematics(track, 2); + } else { + fillKinematics(track, 4); + } + } + for (auto const& track : originalTracks) { + if (originalIds.contains(track.globalIndex()) && !timeIds.contains(track.globalIndex())) { + fillKinematics(track, 3); + } + } + const auto nOriginal = originalIds.size(); + const auto nTime = timeIds.size(); + const auto lost = nOriginal - common; + const auto gained = nTime - common; + const auto unionSize = common + lost + gained; + const double cent = getCentrality(collision); + const auto occupancy = collision.trackOccupancyInTimeRange(); + const double deltaNch = static_cast(nTime) - static_cast(nOriginal); + const int availability = (nOriginal > 0 ? 1 : 0) + (nTime > 0 ? 2 : 0); + registry.fill(HIST("associationComparison/hEventAvailability"), cent, availability); + registry.fill(HIST("associationComparison/hNchOriginalVsTime"), nOriginal, nTime); + registry.fill(HIST("associationComparison/hLostVsGained"), lost, gained); + registry.fill(HIST("associationComparison/hDeltaNchVsCent"), cent, deltaNch); + registry.fill(HIST("associationComparison/hDeltaNchVsOccupancy"), occupancy, deltaNch); + if (unionSize > 0) { + const double changeFraction = static_cast(lost + gained) / unionSize; + registry.fill(HIST("associationComparison/hSetChangeVsCent"), cent, changeFraction); + registry.fill(HIST("associationComparison/hSetChangeVsOccupancy"), occupancy, changeFraction); + registry.fill(HIST("associationComparison/pSetChangeVsCent"), cent, changeFraction); + registry.fill(HIST("associationComparison/pSetChangeVsOccupancy"), occupancy, changeFraction); + } + if (nOriginal > 0) { + const double lostFraction = static_cast(lost) / nOriginal; + registry.fill(HIST("associationComparison/pLostFractionVsCent"), cent, lostFraction); + registry.fill(HIST("associationComparison/pLostFractionVsOccupancy"), occupancy, lostFraction); + } + if (nTime > 0) { + const double gainedFraction = static_cast(gained) / nTime; + registry.fill(HIST("associationComparison/pGainedFractionVsCent"), cent, gainedFraction); + registry.fill(HIST("associationComparison/pGainedFractionVsOccupancy"), occupancy, gainedFraction); + } + } + for (std::size_t index = 0; index < originalUses.size(); ++index) { + registry.fill(HIST("associationComparison/hFinalCollisionsOriginal"), originalUses[index]); + registry.fill(HIST("associationComparison/hFinalCollisionsTime"), timeUses[index]); + registry.fill(HIST("associationComparison/hFinalCollisionsOriginalVsTime"), originalUses[index], timeUses[index]); + } + } + PROCESS_SWITCH(PidFlowPtCorr, processTimeAssociationComparison, "Compare original collisionId and time-associated final charged tracks; no PID/NUA/NUE", false); + + // Independent charged-only counterpart of processData. All moment/profile + // definitions below retain the original charged weights and bootstrap draw. + void processDataTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcProcessDataTimeAssociation)) { + return; + } + if (tracks.size() == 0) { + return; + } + fillEventCountHelper(funcProcessDataTimeAssociation, 14.5); + const double cent = getCentrality(collision); + const float vtxz = collision.posZ(); + const float rndm = fRndm->Rndm(); + auto bc = collision.bc_as(); + loadChargedTimeCorrections(bc.timestamp()); + fGFW->Clear(); + registry.fill(HIST("hVtxZ"), vtxz); + registry.fill(HIST("hMult"), tracks.size()); + registry.fill(HIST("hMultTPC"), collision.multTPC()); + registry.fill(HIST("hCent"), cent); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hCentEventCountData"), cent); + } + + double ptSum = 0., ptSumw2 = 0., nch = 0., nchSquare = 0., ptSquareSum = 0.; + double nchPassedSelection = 0., nchCorrectedPassed = 0.; + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f; + setParticleNUEWeight(weff, track, cent); + nchPassedSelection += 1.; + nchCorrectedPassed += weff; + if (std::fabs(track.eta()) < trkQualityOpts.cfgRangeEta.value) { + nch += weff; + nchSquare += weff * weff; + ptSum += weff * track.pt(); + ptSumw2 += weff * weff * track.pt(); + ptSquareSum += weff * weff * track.pt() * track.pt(); + } + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f, wacc = 1.f; + setParticleNUEWeight(weff, track, cent); + setParticleNUAWeight(wacc, track, vtxz); + registry.fill(HIST("hPhi"), track.phi()); + registry.fill(HIST("hPhicorr"), track.phi(), wacc); + registry.fill(HIST("hPhiCorrNUANUE"), track.phi(), wacc * weff); + registry.fill(HIST("hEta"), track.eta()); + registry.fill(HIST("hPt"), track.pt()); + registry.fill(HIST("hPtCorr"), track.pt(), weff); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hPtCentData"), track.pt(), cent); + } + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::charged::kReference); + } + registry.fill(HIST("hNchUnCorrectedVSNchCorrected"), nchPassedSelection, nchCorrectedPassed); + if (nch <= 0.) { + return; + } + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, "c22"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, "c24"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24Gap), cent, rndm, "c24Gap"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, "c22Full"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, "c32"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, "c34"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeightOne", true); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, nch, nch, "c24TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, nch, nch, "c22FullTrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, nch, nch, "c34TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2PtWeightOne", true); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, ptSum, nch, "covV3Pt"); + fillFC4PtC22(cent, ptSum, nch, rndm); + fFCCh->FillProfile("hMeanPt", cent, ptSum / nch, nch, rndm); + fFCCh->FillProfile("hMeanPtWeightOne", cent, ptSum / nch, 1., rndm); + const double nchDiff = nch * nch - nchSquare; + if (nchDiff > minVal4Float) { + fFCCh->FillProfile("ptSquareAve", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptSquareAveWeightOne", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, 1., rndm); + fFCCh->FillProfile("ptAve", cent, (nch * ptSum - ptSumw2) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptAveWeightOne", cent, (nch * ptSum - ptSumw2) / nchDiff, 1., rndm); + } + if (switchsOpts.cfgAddC22DeltaPtHistograms) { + registry.fill(HIST("c22DeltaPt/hMeanPtCharged"), cent, rndm * cfgFlowNbootstrap, ptSum / nch, nch); + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptCharged"), cent, ptSum, nch, rndm); + } + } + PROCESS_SWITCH(PidFlowPtCorr, processDataTimeAssociation, "Charged flow and mean-pT using time-based TrackAssoc; no PID", false); + + // Store raw ensemble correlators, not event-by-event normalized v2. + // FlowContainer: SetIDName("ChGap"), then GetVN2VsPt(2, centMin, centMax). + void processChargedDifferentialTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcProcessChargedDifferentialTimeAssociation) || tracks.size() == 0) { + return; + } + fillEventCountHelper(funcProcessChargedDifferentialTimeAssociation, 14.5); + const double cent = getCentrality(collision); + const double rndm = fRndm->Rndm(); + auto bc = collision.bc_as(); + loadChargedTimeCorrections(bc.timestamp()); + chargedDifferential.gfw->Clear(); + const int ptBins = chargedDifferential.ptAxis->GetNbins(); + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track)) { + continue; + } + const double pt = track.pt(); + const bool isReference = pt > chargedDifferentialOpts.cfgRefPtMin.value && pt < chargedDifferentialOpts.cfgRefPtMax.value; + const int ptBin = chargedDifferential.ptAxis->FindFixBin(pt) - 1; + const bool isPOI = ptBin >= 0 && ptBin < ptBins; + if (!isReference && !isPOI) { + continue; + } + float weff = 1.f, wacc = 1.f; + setParticleNUEWeight(weff, track, cent); + setParticleNUAWeight(wacc, track, collision.posZ()); + const double weight = static_cast(weff) * wacc; + if (isReference) { + chargedDifferential.gfw->Fill(track.eta(), 0, track.phi(), weight, kDifferentialReference); + } + if (isPOI) { + chargedDifferential.gfw->Fill(track.eta(), ptBin, track.phi(), weight, kDifferentialPOI); + } + } + for (auto const& config : chargedDifferential.configs) { + const int bins = config.pTDif ? ptBins : 1; + for (int bin = 0; bin < bins; ++bin) { + const double pairs = chargedDifferential.gfw->Calculate(config, bin, true).real(); + if (!std::isfinite(pairs) || pairs <= 0.) { + continue; + } + const double correlator = chargedDifferential.gfw->Calculate(config, bin, false).real() / pairs; + if (std::isfinite(correlator)) { + const std::string name = config.pTDif ? config.Head + "_pt_" + std::to_string(bin + 1) : config.Head; + fFCChDifferential->FillProfile(name.c_str(), cent, correlator, pairs, rndm); + } + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processChargedDifferentialTimeAssociation, "Charged differential v2{2}(pT) using time-based TrackAssoc; independent reference pT and POI bins, no PID", false); + + void fillCorrectionGraphTimeAssociation(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& tracks) + { + if (!chargedTimeEventSelected(collision, funcFillCorrectionGraphTimeAssociation)) { + return; + } + if (tracks.size() == 0) { + return; + } + fillEventCountHelper(funcFillCorrectionGraphTimeAssociation, 14.5); + auto bc = collision.bc_as(); + auto run = std::find(runNumbers.begin(), runNumbers.end(), bc.runNumber()); + if (run == runNumbers.end()) { + return; + } + const int runIndex = std::distance(runNumbers.begin(), run); + const double cent = getCentrality(collision); + if (switchsOpts.cfgUseNUAWithPt) { + loadChargedTimeCorrections(bc.timestamp(), false); + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + float weff = 1.f; + if (switchsOpts.cfgUseNUAWithPt) { + setParticleNUEWeight(weff, track, cent); + } + registry.fill(HIST("correction/hRunNumberPhiEtaVertex"), runIndex, track.phi(), track.eta(), collision.posZ(), track.pt(), weff); + } + } + PROCESS_SWITCH(PidFlowPtCorr, fillCorrectionGraphTimeAssociation, "Charged NUA source using time-based TrackAssoc; no PID", false); + + // Fill reco numerator and generated denominator together, so they use the + // very same accepted target event. A different true collision is contamination, + // not successful reconstruction of a particle in this denominator. + void processMCTimeAssociation(ChargedTimeCollisionsMC const& collisions, aod::BCsWithTimestamps const&, aod::PidFlowTimeTracks const& associatedTracks, + aod::McCollisions const& mcCollisions, aod::McParticles const& particles, aod::McTrackLabels const& labels) + { + std::vector recoPerMC(mcCollisions.size(), 0); + for (auto const& collision : collisions) { + if (collision.has_mcCollision()) { + ++recoPerMC[collision.mcCollisionId() - mcCollisions.offset()]; + } + } + for (auto const& collision : collisions) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 0.5); + } + if (!collision.has_mcCollision()) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 1.5); + } + const int64_t mcIndex = collision.mcCollisionId() - mcCollisions.offset(); + auto mcCollision = mcCollisions.rawIteratorAt(mcIndex); + if (recoPerMC[mcIndex] != 1 || std::fabs(mcCollision.posZ()) >= cfgCutVertex.value) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 2.5); + } + if (!chargedTimeEventSelected(collision, funcProcessMCTimeAssociation)) { + continue; + } + auto tracks = associatedTracks.sliceBy(timeTracksPerCollision, collision.globalIndex()); + if (tracks.size() == 0) { + continue; + } + fillEventCountHelper(funcProcessMCTimeAssociation, 14.5); + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcEventStatus"), 3.5); + } + const double cent = getCentrality(collision); + if (switchsOpts.cfgOutPutPtSpectra) { + registry.fill(HIST("ptSpectra/hCentEventCountMcRec"), cent); + registry.fill(HIST("ptSpectra/hCentEventCountMcGen"), cent); + } + for (auto const& particle : particles.sliceBy(particlesPerMCCollision, collision.mcCollisionId())) { + if (particle.isPhysicalPrimary() && particleSelected(particle)) { + registry.fill(HIST("correction/hPtCentMcGen"), particle.pt(), cent); + } + } + for (auto const& track : tracks) { + if (!trackSelectedForFlow(track) || !isWithinRefPtRange(track.pt())) { + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/hPtCentMcSelected"), track.pt(), cent); + } + const int64_t labelIndex = track.trackId() - labels.offset(); + if (labelIndex < 0 || labelIndex >= labels.size()) { + LOGF(fatal, "Time-associated source track index is outside McTrackLabels"); + } + auto source = labels.rawIteratorAt(labelIndex); + if (!source.has_mcParticle()) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 0.5); + } + continue; + } + auto particle = source.mcParticle_as(); + if (particle.mcCollisionId() != collision.mcCollisionId()) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 1.5); + registry.fill(HIST("timeAssociation/hPtCentMcOtherCollision"), track.pt(), cent); + } + continue; + } + if (!particle.isPhysicalPrimary() || !particleSelected(particle)) { + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 2.5); + } + continue; + } + if (cfgTimeAssociationQA) { + registry.fill(HIST("timeAssociation/mcTrackStatus"), 3.5); + } + registry.fill(HIST("correction/hPtCentMcRec"), track.pt(), cent); + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processMCTimeAssociation, "Charged NUE reco/gen with common time-association event sample; no detector PID", false); + + /** + * @brief Fill charged, identified-particle, and unidentified-particle c22 versus event mean pT. + * @note Each profile uses the mean pT of the same particle sample on its pT axis: + * charged, pion, kaon, proton, or PID-failed/ambiguous unidentified tracks. + * The corresponding FlowContainer hMeanPt + * observable provides the ensemble mean pT needed to convert that axis to + * delta[pT]/<[pT]> offline. + */ + void processDataC22DeltaPt(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracks const& tracks) + { + if (!switchsOpts.cfgAddC22DeltaPtHistograms.value) { + return; + } + const double rndm = fRndm->Rndm(); + + /// @note event selection + registry.fill(HIST("hEventCount/processDataC22DeltaPt"), 0.5); + if (tracks.size() < 1) { + return; + } + + fGFW->Clear(); + const auto cent = getCentrality(collision); + if (!collision.sel8()) { + return; + } + registry.fill(HIST("hEventCount/processDataC22DeltaPt"), 1.5); + + auto bc = collision.bc_as(); + const double interactionRate = getInteractionRate(bc.timestamp(), bc.runNumber()); + if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcProcessDataC22DeltaPt)) { + return; + } + // event selection + + loadCorrections(bc.timestamp()); + + const float vtxz = collision.posZ(); + + // Event-wise mean-pT numerators and NUE-corrected particle counts. Charged + // particles use the reference pT range; each PID species uses its own POI pT range. + double ptSum = 0.; + double nch = 0.; + double pionPtSum = 0.; + double kaonPtSum = 0.; + double protonPtSum = 0.; + double unidentifiedPtSum = 0.; + double nPionWeighted = 0.; + double nKaonWeighted = 0.; + double nProtonWeighted = 0.; + double nUnidentifiedWeighted = 0.; + + for (const auto& track : tracks) { + // The mean-pT samples use the same track-quality and eta selections. The pT + // selection and NUE correction are then applied independently for charged and PID. + if (!trackSelectedForFlow(track) || std::fabs(track.eta()) >= trkQualityOpts.cfgRangeEta.value) { + continue; + } + + float weff = 1.f; + setParticleNUEWeight(weff, track, cent); + const bool withinPtRef = isWithinRefPtRange(track.pt()); + if (withinPtRef) { + nch += weff; + ptSum += weff * track.pt(); + } + + const int pid = getPidConfigurable(track); + if (pid == -1 && withinPtRef) { + nUnidentifiedWeighted += weff; + unidentifiedPtSum += weff * track.pt(); + continue; + } + if (!isWithinPOIPtRange(pid, track.pt())) { + continue; + } + + float weffPid = 1.f; + setParticleNUEWeight(weffPid, track, cent, pid); + if (pid == MyParticleType::kPion) { + nPionWeighted += weffPid; + pionPtSum += weffPid * track.pt(); + } else if (pid == MyParticleType::kKaon) { + nKaonWeighted += weffPid; + kaonPtSum += weffPid * track.pt(); + } else if (pid == MyParticleType::kProton) { + nProtonWeighted += weffPid; + protonPtSum += weffPid * track.pt(); + } + } + if (nch <= 0.) { + return; + } + + // Fill the charged reference and particle-specific POI regions in a separate pass. + for (const auto& track : tracks) { + if (!trackSelectedForFlow(track)) { + continue; + } + + float weff = 1.f; + float wacc = 1.f; + setParticleNUEWeight(weff, track, cent); + setParticleNUAWeight(wacc, track, vtxz); + + const bool withinPtRef = isWithinRefPtRange(track.pt()); + if (withinPtRef) { + /// @note passed selection: global cut + pt cut + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::charged::kReference); + } + + const int pid = getPidConfigurable(track); + if (pid == -1 && withinPtRef) { + fGFW->Fill(track.eta(), 0, track.phi(), wacc * weff, gfwBitMask::unidentified::kPOI); + continue; + } + if (!isWithinPOIPtRange(pid, track.pt())) { + continue; + } + + float weffPid = 1.f; + float waccPid = 1.f; + setParticleNUEWeight(weffPid, track, cent, pid); + setParticleNUAWeight(waccPid, track, vtxz, pid); + + if (pid == MyParticleType::kPion) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::pion::kPOI); + } else if (pid == MyParticleType::kKaon) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::kaon::kPOI); + } else if (pid == MyParticleType::kProton) { + fGFW->Fill(track.eta(), 0, track.phi(), waccPid * weffPid, gfwBitMask::proton::kPOI); + } + } + + // All profiles from one event use the same bootstrap subsample. Mean-pT profiles + // retain the NUE-corrected particle-count weight used by FlowContainer hMeanPt. + if (switchsOpts.cfgOutputCharged.value) { + registry.fill(HIST("c22DeltaPt/hMeanPtCharged"), cent, rndm * cfgFlowNbootstrap, ptSum / nch, nch); + } + if (nPionWeighted > 0. && switchsOpts.cfgOutputPion.value) { + registry.fill(HIST("c22DeltaPt/hMeanPtPion"), cent, rndm * cfgFlowNbootstrap, pionPtSum / nPionWeighted, nPionWeighted); + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptPionRefRef"), cent, pionPtSum, nPionWeighted, rndm); + } + if (nKaonWeighted > 0. && switchsOpts.cfgOutputKaon.value) { + registry.fill(HIST("c22DeltaPt/hMeanPtKaon"), cent, rndm * cfgFlowNbootstrap, kaonPtSum / nKaonWeighted, nKaonWeighted); + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptKaonRefRef"), cent, kaonPtSum, nKaonWeighted, rndm); + } + if (nProtonWeighted > 0. && switchsOpts.cfgOutputProton.value) { + registry.fill(HIST("c22DeltaPt/hMeanPtProton"), cent, rndm * cfgFlowNbootstrap, protonPtSum / nProtonWeighted, nProtonWeighted); + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptProtonRefRef"), cent, protonPtSum, nProtonWeighted, rndm); + } + if (nUnidentifiedWeighted > 0. && switchsOpts.cfgOutputUnidentified.value) { + registry.fill(HIST("c22DeltaPt/hMeanPtUnidentified"), cent, rndm * cfgFlowNbootstrap, unidentifiedPtSum / nUnidentifiedWeighted, nUnidentifiedWeighted); + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptUnidentifiedRefRef"), cent, unidentifiedPtSum, nUnidentifiedWeighted, rndm); + } + + // Charged and PID-binned ref-ref are common to both modes. The numerator is + // selected between PID Pure and POI-ref by cfgC22DeltaPtUsePure. + if (switchsOpts.cfgOutputCharged.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::charged::kC22Gap), HIST("c22DeltaPt/c22dmeanptCharged"), cent, ptSum, nch, rndm); + } + if (switchsOpts.cfgC22DeltaPtUsePure.value) { + if (nPionWeighted > 0. && switchsOpts.cfgOutputPion.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::pion::kC22Pure), HIST("c22DeltaPt/c22dmeanptPionPure"), cent, pionPtSum, nPionWeighted, rndm); + } + if (nKaonWeighted > 0. && switchsOpts.cfgOutputKaon.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::kaon::kC22Pure), HIST("c22DeltaPt/c22dmeanptKaonPure"), cent, kaonPtSum, nKaonWeighted, rndm); + } + if (nProtonWeighted > 0. && switchsOpts.cfgOutputProton.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::proton::kC22Pure), HIST("c22DeltaPt/c22dmeanptProtonPure"), cent, protonPtSum, nProtonWeighted, rndm); + } + if (nUnidentifiedWeighted > 0. && switchsOpts.cfgOutputUnidentified.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::unidentified::kC22Pure), HIST("c22DeltaPt/c22dmeanptUnidentifiedPure"), cent, unidentifiedPtSum, nUnidentifiedWeighted, rndm); + } + } else { + if (nPionWeighted > 0. && switchsOpts.cfgOutputPion.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::pion::kC22GapA), corrconfigs.at(corrConfigIndex::pion::kC22GapB), HIST("c22DeltaPt/c22dmeanptPionPOIRef"), cent, pionPtSum, nPionWeighted, rndm); + } + if (nKaonWeighted > 0. && switchsOpts.cfgOutputKaon.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::kaon::kC22GapA), corrconfigs.at(corrConfigIndex::kaon::kC22GapB), HIST("c22DeltaPt/c22dmeanptKaonPOIRef"), cent, kaonPtSum, nKaonWeighted, rndm); + } + if (nProtonWeighted > 0. && switchsOpts.cfgOutputProton.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::proton::kC22GapA), corrconfigs.at(corrConfigIndex::proton::kC22GapB), HIST("c22DeltaPt/c22dmeanptProtonPOIRef"), cent, protonPtSum, nProtonWeighted, rndm); + } + if (nUnidentifiedWeighted > 0. && switchsOpts.cfgOutputUnidentified.value) { + fillProfilePOIvnpt(corrconfigs.at(corrConfigIndex::unidentified::kC22GapA), corrconfigs.at(corrConfigIndex::unidentified::kC22GapB), HIST("c22DeltaPt/c22dmeanptUnidentifiedPOIRef"), cent, unidentifiedPtSum, nUnidentifiedWeighted, rndm); + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processDataC22DeltaPt, "Fill c22dmeanpt profiles", false); + + void fillPidPtSelfMoments(const char* productName, + const char* singlePtName, + const double cent, + const double rndm, + const double sumWeight, + const double sumPt, + const double sumWeight2, + const double sumPtWeight2, + const double sumPt2Weight2) + { + const double pairWeight = sumWeight * sumWeight - sumWeight2; + if (pairWeight <= minVal4Float) { + return; + } + fFCPidPtCorr->FillProfile(productName, cent, + (sumPt * sumPt - sumPt2Weight2) / pairWeight, + pairWeight, rndm); + fFCPidPtCorr->FillProfile(singlePtName, cent, + (sumWeight * sumPt - sumPtWeight2) / pairWeight, + pairWeight, rndm); + } + + void fillPidPtCrossMoments(const char* productName, + const char* alphaPtName, + const char* betaPtName, + const double cent, + const double rndm, + const double alphaWeight, + const double alphaPtSum, + const double betaWeight, + const double betaPtSum) + { + if (alphaWeight <= 0. || betaWeight <= 0.) { + return; + } + const double pairWeight = alphaWeight * betaWeight; + fFCPidPtCorr->FillProfile(productName, cent, + alphaPtSum * betaPtSum / pairWeight, + pairWeight, rndm); + fFCPidPtCorr->FillProfile(alphaPtName, cent, + alphaPtSum / alphaWeight, + pairWeight, rndm); + fFCPidPtCorr->FillProfile(betaPtName, cent, + betaPtSum / betaWeight, + pairWeight, rndm); + } + + void processPidPtCorrelations(AodCollisions::iterator const& collision, + aod::BCsWithTimestamps const&, + AodTracks const& tracks) + { + if (!switchsOpts.cfgAddPidPtCorrelationProfiles.value) { + return; + } + registry.fill(HIST("hEventCount/processPidPtCorrelations"), 0.5); + if (tracks.size() < 1 || !collision.sel8()) { + return; + } + registry.fill(HIST("hEventCount/processPidPtCorrelations"), 1.5); + + const auto cent = getCentrality(collision); + auto bc = collision.bc_as(); + const double interactionRate = getInteractionRate(bc.timestamp(), bc.runNumber()); + if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcProcessPidPtCorrelations)) { + return; + } + + loadCorrections(bc.timestamp()); + const double rndm = fRndm->Rndm(); + std::array sumWeight{}; + std::array sumPt{}; + std::array sumWeight2{}; + std::array sumPtWeight2{}; + std::array sumPt2Weight2{}; + + for (const auto& track : tracks) { + if (!trackSelectedForFlow(track)) { + continue; + } + const int pid = getPidConfigurable(track); + if (pid < MyParticleType::kPion || pid > MyParticleType::kProton || !isWithinPOIPtRange(pid, track.pt())) { + continue; + } + + float weight = 1.f; + setParticleNUEWeight(weight, track, cent, pid); + const auto index = static_cast(pid - MyParticleType::kPion); + const double particleWeight = weight; + const double weight2 = particleWeight * particleWeight; + const double pt = track.pt(); + sumWeight[index] += particleWeight; + sumPt[index] += particleWeight * pt; + sumWeight2[index] += weight2; + sumPtWeight2[index] += weight2 * pt; + sumPt2Weight2[index] += weight2 * pt * pt; + } + + constexpr std::size_t kPi = 0; + constexpr std::size_t kKa = 1; + constexpr std::size_t kPr = 2; + if (sumWeight[kPi] > 0.) { + fFCPidPtCorr->FillProfile("meanPtPi", cent, sumPt[kPi] / sumWeight[kPi], sumWeight[kPi], rndm); + } + if (sumWeight[kKa] > 0.) { + fFCPidPtCorr->FillProfile("meanPtKa", cent, sumPt[kKa] / sumWeight[kKa], sumWeight[kKa], rndm); + } + if (sumWeight[kPr] > 0.) { + fFCPidPtCorr->FillProfile("meanPtPr", cent, sumPt[kPr] / sumWeight[kPr], sumWeight[kPr], rndm); + } + + fillPidPtSelfMoments("ptProductPiPi", "ptPiInPiPiPairs", cent, rndm, + sumWeight[kPi], sumPt[kPi], sumWeight2[kPi], sumPtWeight2[kPi], sumPt2Weight2[kPi]); + fillPidPtSelfMoments("ptProductKaKa", "ptKaInKaKaPairs", cent, rndm, + sumWeight[kKa], sumPt[kKa], sumWeight2[kKa], sumPtWeight2[kKa], sumPt2Weight2[kKa]); + fillPidPtSelfMoments("ptProductPrPr", "ptPrInPrPrPairs", cent, rndm, + sumWeight[kPr], sumPt[kPr], sumWeight2[kPr], sumPtWeight2[kPr], sumPt2Weight2[kPr]); + + fillPidPtCrossMoments("ptProductPiKa", "ptPiInPiKaPairs", "ptKaInPiKaPairs", cent, rndm, + sumWeight[kPi], sumPt[kPi], sumWeight[kKa], sumPt[kKa]); + fillPidPtCrossMoments("ptProductPiPr", "ptPiInPiPrPairs", "ptPrInPiPrPairs", cent, rndm, + sumWeight[kPi], sumPt[kPi], sumWeight[kPr], sumPt[kPr]); + fillPidPtCrossMoments("ptProductKaPr", "ptKaInKaPrPairs", "ptPrInKaPrPairs", cent, rndm, + sumWeight[kKa], sumPt[kKa], sumWeight[kPr], sumPt[kPr]); + } + PROCESS_SWITCH(PidFlowPtCorr, processPidPtCorrelations, "Calculate self-contained PID mean-pT correlations", false); + + /** + * @brief Run the flow calculation on generated MC particles for a closure test. + * @note Reconstructed collisions only provide centrality or raw PV multiplicity. The selection is + * deliberately truth-level: MC vertex filter, physical-primary particles, kinematics, + * and PDG PID. No data event, track-quality, detector-PID, NUA, or NUE cuts are applied. + */ + void processMCClosure(FilteredMcCollisions::iterator const& mcCollision, + aod::BCsWithTimestamps const&, + soa::SmallGroups> const& collisions, + FilteredMcParticles const& mcParticles, + FilteredTracksWithMCLabel const&) + { + registry.fill(HIST("hEventCount/processData"), 0.5); + // A generated event must be associated with exactly one reconstructed collision. + // Otherwise the same truth particles would be filled more than once. + if (collisions.size() != 1 || mcParticles.size() <= 0) { + return; + } + + for (const auto& collision : collisions) { + const float cent = getCentrality(collision); + const float vtxz = mcCollision.posZ(); + const float rndm = fRndm->Rndm(); + const int nTot = mcParticles.size(); + + fGFW->Clear(); + registry.fill(HIST("hEventCount/processData"), 1.5); + registry.fill(HIST("hVtxZ"), vtxz); + registry.fill(HIST("hMult"), nTot); + registry.fill(HIST("hCent"), cent); + if (switchsOpts.cfgOutPutPtSpectra.value) { + registry.fill(HIST("ptSpectra/hCentEventCountData"), cent); + } + + double ptSum = 0., ptSumw2 = 0., nch = 0., nchSquare = 0., ptSquareSum = 0.; + double pionPtSum = 0., kaonPtSum = 0., protonPtSum = 0.; + double nPionWeighted = 0., nKaonWeighted = 0., nProtonWeighted = 0.; + double pionPtSumw2 = 0., kaonPtSumw2 = 0., protonPtSumw2 = 0.; + double nPionSquare = 0., nKaonSquare = 0., nProtonSquare = 0.; + double pionPtSquareSum = 0., kaonPtSquareSum = 0., protonPtSquareSum = 0.; + int numOfPi = 0, numOfKa = 0, numOfPr = 0; + + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary() || !isStable(mcParticle.pdgCode()) || std::fabs(mcParticle.eta()) > trkQualityOpts.cfgCutEta.value) { + continue; + } + + const float pt = mcParticle.pt(); + const bool withinPtRef = pt > trkQualityOpts.cfgCutPtMin.value && pt < trkQualityOpts.cfgCutPtMax.value; + const bool withinMeanPtEta = std::fabs(mcParticle.eta()) < trkQualityOpts.cfgRangeEta.value; + const int absPdg = std::abs(mcParticle.pdgCode()); + int pid = -1; + if (absPdg == PDG_t::kPiPlus) { + pid = MyParticleType::kPion; + } else if (absPdg == PDG_t::kKPlus) { + pid = MyParticleType::kKaon; + } else if (absPdg == PDG_t::kProton) { + pid = MyParticleType::kProton; + } + + if (withinPtRef && withinMeanPtEta) { + nch += 1.; + nchSquare += 1.; + ptSum += pt; + ptSumw2 += pt; + ptSquareSum += pt * pt; + } + + if (withinMeanPtEta && isWithinPOIPtRange(pid, pt)) { + if (pid == MyParticleType::kPion) { + nPionWeighted += 1.; + nPionSquare += 1.; + pionPtSum += pt; + pionPtSumw2 += pt; + pionPtSquareSum += pt * pt; + } else if (pid == MyParticleType::kKaon) { + nKaonWeighted += 1.; + nKaonSquare += 1.; + kaonPtSum += pt; + kaonPtSumw2 += pt; + kaonPtSquareSum += pt * pt; + } else if (pid == MyParticleType::kProton) { + nProtonWeighted += 1.; + nProtonSquare += 1.; + protonPtSum += pt; + protonPtSumw2 += pt; + protonPtSquareSum += pt * pt; + } + } + + if (withinPtRef) { + registry.fill(HIST("hPhi"), mcParticle.phi()); + registry.fill(HIST("hPhicorr"), mcParticle.phi()); + registry.fill(HIST("hPhiCorrNUANUE"), mcParticle.phi()); + registry.fill(HIST("hEta"), mcParticle.eta()); + registry.fill(HIST("hPt"), pt); + registry.fill(HIST("hPtCorr"), pt); + if (switchsOpts.cfgOutPutPtSpectra.value) { + registry.fill(HIST("ptSpectra/hPtCentData"), pt, cent); + } + fGFW->Fill(mcParticle.eta(), 0, mcParticle.phi(), 1., gfwBitMask::charged::kReference); + } + + if (!isWithinPOIPtRange(pid, pt)) { + continue; + } + if (pid == MyParticleType::kPion) { + fGFW->Fill(mcParticle.eta(), 0, mcParticle.phi(), 1., gfwBitMask::pion::kPOI); + fGFW->Fill(mcParticle.eta(), 0, mcParticle.phi(), 1., gfwBitMask::pion::kOverlap, 1.); + registry.fill(HIST("hPhiPi"), mcParticle.phi()); + registry.fill(HIST("hPhicorrPi"), mcParticle.phi()); + registry.fill(HIST("hPhiCorrNUANUEPi"), mcParticle.phi()); + registry.fill(HIST("hPtPi"), pt); + registry.fill(HIST("hPtCorrPi"), pt); + ++numOfPi; + } else if (pid == MyParticleType::kKaon) { + fGFW->Fill(mcParticle.eta(), 0, mcParticle.phi(), 1., gfwBitMask::kaon::kPOI); + fGFW->Fill(mcParticle.eta(), 0, mcParticle.phi(), 1., gfwBitMask::kaon::kOverlap, 1.); + registry.fill(HIST("hPhiKa"), mcParticle.phi()); + registry.fill(HIST("hPhicorrKa"), mcParticle.phi()); + registry.fill(HIST("hPhiCorrNUANUEKa"), mcParticle.phi()); + registry.fill(HIST("hPtKa"), pt); + registry.fill(HIST("hPtCorrKa"), pt); + ++numOfKa; + } else if (pid == MyParticleType::kProton) { + fGFW->Fill(mcParticle.eta(), 0, mcParticle.phi(), 1., gfwBitMask::proton::kPOI); + fGFW->Fill(mcParticle.eta(), 0, mcParticle.phi(), 1., gfwBitMask::proton::kOverlap, 1.); + registry.fill(HIST("hPhiPr"), mcParticle.phi()); + registry.fill(HIST("hPhicorrPr"), mcParticle.phi()); + registry.fill(HIST("hPhiCorrNUANUEPr"), mcParticle.phi()); + registry.fill(HIST("hPtPr"), pt); + registry.fill(HIST("hPtCorrPr"), pt); + ++numOfPr; + } + } + + if (particleAbundanceOpts.cfgOutPutAbundanceDis) { + registry.fill(HIST("abundance/hNumOfPiEventCount"), numOfPi); + registry.fill(HIST("abundance/hNumOfKaEventCount"), numOfKa); + registry.fill(HIST("abundance/hNumOfPrEventCount"), numOfPr); + } + registry.fill(HIST("hNchUnCorrectedVSNchCorrected"), nch, nch); + + if (nch <= 0.) { + continue; + } + + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, "c22"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, "c24"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, "c22Full"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, "c32"); + fillFC(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, "c34"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22FullA), cent, rndm, "c22Full"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22FullB), cent, rndm, "c22Full"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22FullA), cent, rndm, "c22Full"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22FullB), cent, rndm, "c22Full"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22FullA), cent, rndm, "c22Full"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22FullB), cent, rndm, "c22Full"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC24A), cent, rndm, "c24"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC24B), cent, rndm, "c24"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC24A), cent, rndm, "c24"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC24B), cent, rndm, "c24"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC24A), cent, rndm, "c24"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC24B), cent, rndm, "c24"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32GapA), cent, rndm, "c32"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32GapB), cent, rndm, "c32"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32GapA), cent, rndm, "c32"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32GapB), cent, rndm, "c32"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32GapA), cent, rndm, "c32"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32GapB), cent, rndm, "c32"); + + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC34A), cent, rndm, "c34"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC34B), cent, rndm, "c34"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC34A), cent, rndm, "c34"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC34B), cent, rndm, "c34"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC34A), cent, rndm, "c34"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC34B), cent, rndm, "c34"); + + const bool filledPi = fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, "c22pure"); + const bool filledKa = fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, "c22pure"); + const bool filledPr = fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, "c22pure"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, "c32pure"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, "c32pure"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, "c32pure"); if (filledPi || !switchsOpts.cfgCheck2MethodDiff.value) { - fillFC(MyParticleType::kPion, corrconfigs.at(5), cent, rndm, "c22"); - fillFC(MyParticleType::kPion, corrconfigs.at(6), cent, rndm, "c22"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22GapA), cent, rndm, "c22"); + fillFC(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22GapB), cent, rndm, "c22"); } if (filledKa || !switchsOpts.cfgCheck2MethodDiff.value) { - fillFC(MyParticleType::kKaon, corrconfigs.at(7), cent, rndm, "c22"); - fillFC(MyParticleType::kKaon, corrconfigs.at(8), cent, rndm, "c22"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22GapA), cent, rndm, "c22"); + fillFC(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22GapB), cent, rndm, "c22"); } if (filledPr || !switchsOpts.cfgCheck2MethodDiff.value) { - fillFC(MyParticleType::kProton, corrconfigs.at(9), cent, rndm, "c22"); - fillFC(MyParticleType::kProton, corrconfigs.at(10), cent, rndm, "c22"); - } - fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(0), cent, rndm, nch, nch, "c22TrackWeight"); - fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(1), cent, rndm, nch, nch, "c24TrackWeight"); - fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(2), cent, rndm, nch, nch, "c22FullTrackWeight"); - fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(3), cent, rndm, nch, nch, "c32TrackWeight"); - fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(4), cent, rndm, nch, nch, "c34TrackWeight"); - - fillFCvnpt(MyParticleType::kPion, corrconfigs.at(29), cent, rndm, nch, nch, "c22TrackWeight"); - fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(30), cent, rndm, nch, nch, "c22TrackWeight"); - fillFCvnpt(MyParticleType::kProton, corrconfigs.at(31), cent, rndm, nch, nch, "c22TrackWeight"); - - fillFCvnpt(MyParticleType::kPion, corrconfigs.at(32), cent, rndm, nch, nch, "c32TrackWeight"); - fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(33), cent, rndm, nch, nch, "c32TrackWeight"); - fillFCvnpt(MyParticleType::kProton, corrconfigs.at(34), cent, rndm, nch, nch, "c32TrackWeight"); - - fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(0), cent, rndm, ptSum, nch, "covV2Pt"); - fillFCvnpt(MyParticleType::kPion, corrconfigs.at(29), cent, rndm, ptSum, nch, "covV2Pt"); - fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(30), cent, rndm, ptSum, nch, "covV2Pt"); - fillFCvnpt(MyParticleType::kProton, corrconfigs.at(31), cent, rndm, ptSum, nch, "covV2Pt"); - - fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(3), cent, rndm, ptSum, nch, "covV3Pt"); - fillFCvnpt(MyParticleType::kPion, corrconfigs.at(32), cent, rndm, ptSum, nch, "covV3Pt"); - fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(33), cent, rndm, ptSum, nch, "covV3Pt"); - fillFCvnpt(MyParticleType::kProton, corrconfigs.at(34), cent, rndm, ptSum, nch, "covV3Pt"); - - fillProfilePOIvnpt(corrconfigs.at(0), HIST("c22dmeanpt"), cent, ptSum, nch); - fillProfilePOIvnpt(corrconfigs.at(5), HIST("pi/c22dmeanpt"), cent, ptSum, nch); - fillProfilePOIvnpt(corrconfigs.at(6), HIST("pi/c22dmeanpt"), cent, ptSum, nch); - fillProfilePOIvnpt(corrconfigs.at(7), HIST("ka/c22dmeanpt"), cent, ptSum, nch); - fillProfilePOIvnpt(corrconfigs.at(8), HIST("ka/c22dmeanpt"), cent, ptSum, nch); - fillProfilePOIvnpt(corrconfigs.at(9), HIST("pr/c22dmeanpt"), cent, ptSum, nch); - fillProfilePOIvnpt(corrconfigs.at(10), HIST("pr/c22dmeanpt"), cent, ptSum, nch); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22GapA), cent, rndm, "c22"); + fillFC(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22GapB), cent, rndm, "c22"); + } + + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, nch, nch, "c22TrackWeightOne", true); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC24), cent, rndm, nch, nch, "c24TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Full), cent, rndm, nch, nch, "c22FullTrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC34Gap), cent, rndm, nch, nch, "c34TrackWeight"); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, nch, nch, "c22TrackWeight"); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, nch, nch, "c32TrackWeight"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC22Gap), cent, rndm, ptSum, nch, "covV2PtWeightOne", true); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, ptSum, nch, "covV2Pt"); + fillFCvnpt(MyParticleType::kCharged, corrconfigs.at(corrConfigIndex::charged::kC32Gap), cent, rndm, ptSum, nch, "covV3Pt"); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC32Pure), cent, rndm, ptSum, nch, "covV3Pt"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC32Pure), cent, rndm, ptSum, nch, "covV3Pt"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC32Pure), cent, rndm, ptSum, nch, "covV3Pt"); fillFC4PtC22(cent, ptSum, nch, rndm); - if (nPionWeighted > 0) + if (nPionWeighted > 0.) { fillFC4PtC22(cent, rndm, MyParticleType::kPion, pionPtSum, nPionWeighted); - - if (nKaonWeighted > 0) + } + if (nKaonWeighted > 0.) { fillFC4PtC22(cent, rndm, MyParticleType::kKaon, kaonPtSum, nKaonWeighted); - - if (nProtonWeighted > 0) + } + if (nProtonWeighted > 0.) { fillFC4PtC22(cent, rndm, MyParticleType::kProton, protonPtSum, nProtonWeighted); + } - if (switchsOpts.cfgOutPutPtSpectra.value) { - // charged calculation - double NpairCharged = fGFW->Calculate(corrconfigs.at(0), 0, true).real(); - double chargedC22 = NpairCharged > 0 ? fGFW->Calculate(corrconfigs.at(0), 0, false).real() / NpairCharged : 0; - // end charged calculation + if (switchsOpts.cfgOutputCharged.value) { + fFCCh->FillProfile("hMeanPt", cent, ptSum / nch, nch, rndm); + fFCCh->FillProfile("hMeanPtWeightOne", cent, ptSum / nch, 1., rndm); + } + if (nPionWeighted > 0. && switchsOpts.cfgOutputPion.value) { + fFCPi->FillProfile("hMeanPt", cent, pionPtSum / nPionWeighted, nPionWeighted, rndm); + fFCPi->FillProfile("hMeanPtWeightOne", cent, pionPtSum / nPionWeighted, 1., rndm); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, pionPtSum, nPionWeighted, "covV2PtPID"); + fillFCvnpt(MyParticleType::kPion, corrconfigs.at(corrConfigIndex::pion::kC22Pure), cent, rndm, nPionWeighted, nPionWeighted, "c22TrackWeightPID"); + } + if (nKaonWeighted > 0. && switchsOpts.cfgOutputKaon.value) { + fFCKa->FillProfile("hMeanPt", cent, kaonPtSum / nKaonWeighted, nKaonWeighted, rndm); + fFCKa->FillProfile("hMeanPtWeightOne", cent, kaonPtSum / nKaonWeighted, 1., rndm); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, kaonPtSum, nKaonWeighted, "covV2PtPID"); + fillFCvnpt(MyParticleType::kKaon, corrconfigs.at(corrConfigIndex::kaon::kC22Pure), cent, rndm, nKaonWeighted, nKaonWeighted, "c22TrackWeightPID"); + } + if (nProtonWeighted > 0. && switchsOpts.cfgOutputProton.value) { + fFCPr->FillProfile("hMeanPt", cent, protonPtSum / nProtonWeighted, nProtonWeighted, rndm); + fFCPr->FillProfile("hMeanPtWeightOne", cent, protonPtSum / nProtonWeighted, 1., rndm); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, protonPtSum, nProtonWeighted, "covV2PtPID"); + fillFCvnpt(MyParticleType::kProton, corrconfigs.at(corrConfigIndex::proton::kC22Pure), cent, rndm, nProtonWeighted, nProtonWeighted, "c22TrackWeightPID"); + } - // pi - double pidChargedC22Pi = getPidC22InOneEvent(corrconfigs.at(5), corrconfigs.at(6)); - if (pidChargedC22Pi > 0 && chargedC22 > 0) + if (switchsOpts.cfgOutPutPtSpectra.value) { + const double nPairCharged = fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, true).real(); + const double chargedC22 = nPairCharged > 0. ? fGFW->Calculate(corrconfigs.at(corrConfigIndex::charged::kC22Gap), 0, false).real() / nPairCharged : 0.; + const double pidChargedC22Pi = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::pion::kC22GapA), corrconfigs.at(corrConfigIndex::pion::kC22GapB)); + const double pidKaonC22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::kaon::kC22GapA), corrconfigs.at(corrConfigIndex::kaon::kC22GapB)); + const double pidProtonC22 = getPidC22InOneEvent(corrconfigs.at(corrConfigIndex::proton::kC22GapA), corrconfigs.at(corrConfigIndex::proton::kC22GapB)); + if (pidChargedC22Pi > 0. && chargedC22 > 0.) { registry.fill(HIST("c22PrimeVsc22/Pi"), pidChargedC22Pi, chargedC22); - // end pi - - // Ka - double pidKaonC22 = getPidC22InOneEvent(corrconfigs.at(7), corrconfigs.at(8)); - if (pidKaonC22 > 0 && chargedC22 > 0) + } + if (pidKaonC22 > 0. && chargedC22 > 0.) { registry.fill(HIST("c22PrimeVsc22/Ka"), pidKaonC22, chargedC22); - // end Ka - - // Pr - double pidProtonC22 = getPidC22InOneEvent(corrconfigs.at(9), corrconfigs.at(10)); - if (pidProtonC22 > 0 && chargedC22 > 0) + } + if (pidProtonC22 > 0. && chargedC22 > 0.) { registry.fill(HIST("c22PrimeVsc22/Pr"), pidProtonC22, chargedC22); - // end Pr + } } - fFCCh->FillProfile("hMeanPt", cent, (ptSum / nch), nch, rndm); - - if (nPionWeighted > 0) - fFCPi->FillProfile("hMeanPt", cent, (pionPtSum / nPionWeighted), nPionWeighted, rndm); - - if (nKaonWeighted > 0) - fFCKa->FillProfile("hMeanPt", cent, (kaonPtSum / nKaonWeighted), nKaonWeighted, rndm); - - if (nProtonWeighted > 0) - fFCPr->FillProfile("hMeanPt", cent, (protonPtSum / nProtonWeighted), nProtonWeighted, rndm); - - double nchDiff = nch * nch - nchSquare; - if (nchDiff > minVal4Float) { - fFCCh->FillProfile("ptSquareAve", cent, - (ptSum * ptSum - ptSquareSum) / nchDiff, - nchDiff, rndm); - - fFCCh->FillProfile("ptAve", cent, - (nch * ptSum - ptSumw2) / nchDiff, - nchDiff, rndm); + const double nchDiff = nch * nch - nchSquare; + if (nchDiff > minVal4Float && switchsOpts.cfgOutputCharged.value) { + fFCCh->FillProfile("ptSquareAve", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptSquareAveWeightOne", cent, (ptSum * ptSum - ptSquareSum) / nchDiff, 1., rndm); + fFCCh->FillProfile("ptAve", cent, (nch * ptSum - ptSumw2) / nchDiff, nchDiff, rndm); + fFCCh->FillProfile("ptAveWeightOne", cent, (nch * ptSum - ptSumw2) / nchDiff, 1., rndm); } - - double pionDiff = nPionWeighted * nPionWeighted - nPionSquare; - if (pionDiff > minVal4Float) { - fFCPi->FillProfile("ptSquareAve", cent, - (pionPtSum * pionPtSum - pionPtSquareSum) / pionDiff, - pionDiff, rndm); - - fFCPi->FillProfile("ptAve", cent, - (nPionWeighted * pionPtSum - pionPtSumw2) / pionDiff, - pionDiff, rndm); + const double pionDiff = nPionWeighted * nPionWeighted - nPionSquare; + if (pionDiff > minVal4Float && switchsOpts.cfgOutputPion.value) { + fFCPi->FillProfile("ptSquareAve", cent, (pionPtSum * pionPtSum - pionPtSquareSum) / pionDiff, pionDiff, rndm); + fFCPi->FillProfile("ptSquareAveWeightOne", cent, (pionPtSum * pionPtSum - pionPtSquareSum) / pionDiff, 1., rndm); + fFCPi->FillProfile("ptAve", cent, (nPionWeighted * pionPtSum - pionPtSumw2) / pionDiff, pionDiff, rndm); + fFCPi->FillProfile("ptAveWeightOne", cent, (nPionWeighted * pionPtSum - pionPtSumw2) / pionDiff, 1., rndm); } - - double kaonDiff = nKaonWeighted * nKaonWeighted - nKaonSquare; - if (kaonDiff > minVal4Float) { - fFCKa->FillProfile("ptSquareAve", cent, - (kaonPtSum * kaonPtSum - kaonPtSquareSum) / kaonDiff, - kaonDiff, rndm); - - fFCKa->FillProfile("ptAve", cent, - (nKaonWeighted * kaonPtSum - kaonPtSumw2) / kaonDiff, - kaonDiff, rndm); + const double kaonDiff = nKaonWeighted * nKaonWeighted - nKaonSquare; + if (kaonDiff > minVal4Float && switchsOpts.cfgOutputKaon.value) { + fFCKa->FillProfile("ptSquareAve", cent, (kaonPtSum * kaonPtSum - kaonPtSquareSum) / kaonDiff, kaonDiff, rndm); + fFCKa->FillProfile("ptSquareAveWeightOne", cent, (kaonPtSum * kaonPtSum - kaonPtSquareSum) / kaonDiff, 1., rndm); + fFCKa->FillProfile("ptAve", cent, (nKaonWeighted * kaonPtSum - kaonPtSumw2) / kaonDiff, kaonDiff, rndm); + fFCKa->FillProfile("ptAveWeightOne", cent, (nKaonWeighted * kaonPtSum - kaonPtSumw2) / kaonDiff, 1., rndm); } - - double protonDiff = nProtonWeighted * nProtonWeighted - nProtonSquare; - if (protonDiff > minVal4Float) { - fFCPr->FillProfile("ptSquareAve", cent, - (protonPtSum * protonPtSum - protonPtSquareSum) / protonDiff, - protonDiff, rndm); - - fFCPr->FillProfile("ptAve", cent, - (nProtonWeighted * protonPtSum - protonPtSumw2) / protonDiff, - protonDiff, rndm); + const double protonDiff = nProtonWeighted * nProtonWeighted - nProtonSquare; + if (protonDiff > minVal4Float && switchsOpts.cfgOutputProton.value) { + fFCPr->FillProfile("ptSquareAve", cent, (protonPtSum * protonPtSum - protonPtSquareSum) / protonDiff, protonDiff, rndm); + fFCPr->FillProfile("ptSquareAveWeightOne", cent, (protonPtSum * protonPtSum - protonPtSquareSum) / protonDiff, 1., rndm); + fFCPr->FillProfile("ptAve", cent, (nProtonWeighted * protonPtSum - protonPtSumw2) / protonDiff, protonDiff, rndm); + fFCPr->FillProfile("ptAveWeightOne", cent, (nProtonWeighted * protonPtSum - protonPtSumw2) / protonDiff, 1., rndm); } - - } // end fill hist using fillProfile + } } - PROCESS_SWITCH(PidFlowPtCorr, processData, "", true); + PROCESS_SWITCH(PidFlowPtCorr, processMCClosure, "Run truth-level MC flow closure", false); /** - * @brief this function is used to fill THn hist for NUA correction and for NUE correction - * @details hist THn: (runNumberIDX, phi, eta, Vz), note that different runNumber will be put in the same hist + * @brief Fill THnSparse histograms used to derive NUA weights + * @details Histograms use (runNumberIDX, phi, eta, Vz, pT); cfgUseNUAWithPt enables NUE weighting before filling * * @param collision * @param tracks @@ -2312,23 +4819,30 @@ struct PidFlowPtCorr { int nTot = tracks.size(); auto bc = collision.bc_as(); int runNumber = bc.runNumber(); - double interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 1.e-3; + double interactionRate = getInteractionRate(bc.timestamp(), runNumber); // end init // collision cut // include : 1.track.size 2.collision.sel8 3. evenSelected registry.fill(HIST("hEventCount/fillCorrectionGraph"), 0.5); - if (nTot < 1) + if (nTot < 1) { return; + } fGFW->Clear(); - const auto cent = collision.centFT0C(); - if (!collision.sel8()) + const auto cent = getCentrality(collision); + if (!collision.sel8()) { return; + } registry.fill(HIST("hEventCount/fillCorrectionGraph"), 1.5); - if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcFillCorrectionGraph)) + if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcFillCorrectionGraph)) { return; + } // end collision cut + if (switchsOpts.cfgUseNUAWithPt.value) { + loadCorrections(bc.timestamp()); + } + // loop the vector, find the place to put (phi eta Vz) // if the run number is new, create one int matchedPosition = -1; @@ -2346,117 +4860,279 @@ struct PidFlowPtCorr { // loop all the track for (const auto& track : tracks) { // track cut - if (!trackSelectedGlobal(track)) + if (!trackSelectedGlobal(track, false)) { continue; - if (!track.hasITS()) + } + if (!track.hasITS()) { continue; - if (!track.hasTPC()) + } + if (!track.hasTPC()) { continue; - if (!trackSelected4ITS(track)) + } + if (!trackSelected4ITS(track)) { continue; - if (!trackSelected4TPC(track)) + } + if (!trackSelected4TPC(track)) { continue; + } // end track cut // fill the THn - registry.fill(HIST("correction/hRunNumberPhiEtaVertex"), matchedPosition, track.phi(), track.eta(), collision.posZ()); + if (isWithinRefPtRange(track.pt())) { + float weightNUE = 1.f; + if (switchsOpts.cfgUseNUAWithPt.value) { + setParticleNUEWeight(weightNUE, track, cent); + } + registry.fill(HIST("correction/hRunNumberPhiEtaVertex"), matchedPosition, track.phi(), track.eta(), collision.posZ(), track.pt(), weightNUE); + } + + int pid = this->getPidConfigurable(track); + if (!isWithinPOIPtRange(pid, track.pt())) { + continue; + } + float weightNUEPid = 1.f; + if (switchsOpts.cfgUseNUAWithPt.value) { + setParticleNUEWeight(weightNUEPid, track, cent, pid); + } + switch (pid) { + case MyParticleType::kPion: + registry.fill(HIST("correction/hRunNumberPhiEtaVertexPion"), matchedPosition, track.phi(), track.eta(), collision.posZ(), track.pt(), weightNUEPid); + break; + + case MyParticleType::kKaon: + registry.fill(HIST("correction/hRunNumberPhiEtaVertexKaon"), matchedPosition, track.phi(), track.eta(), collision.posZ(), track.pt(), weightNUEPid); + break; + + case MyParticleType::kProton: + registry.fill(HIST("correction/hRunNumberPhiEtaVertexProton"), matchedPosition, track.phi(), track.eta(), collision.posZ(), track.pt(), weightNUEPid); + break; + + default: + break; + } // end fill the THn } // end loop all the track } PROCESS_SWITCH(PidFlowPtCorr, fillCorrectionGraph, "", true); - /** - * @brief this main function is used to check the PID performance of ITS TOC TPC, also used to do QA - * @note open switch outputQA if use it - * - * @param collision - * @param tracks - */ - void detectorPidQA(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracks const& tracks) + void processQA(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracks const& tracks) { - // cut and correction - o2::aod::ITSResponse itsResponse; + if (!switchsOpts.cfgProcessQAOutput.value) { + LOGF(fatal, "processQAOutput no open!"); + } + + /// @note init && collision cut int nTot = tracks.size(); auto bc = collision.bc_as(); int runNumber = bc.runNumber(); - double interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 1.e-3; + double interactionRate = getInteractionRate(bc.timestamp(), runNumber); // end init - // loop the vector, find the place to put - int matchedPosition = -1; - for (uint64_t idxPosition = 0; idxPosition < runNumbers.size(); idxPosition++) { - if (runNumbers[idxPosition] == runNumber) { - matchedPosition = idxPosition; - break; - } + /// @note collision cut + // include : 1.track.size 2.collision.sel8 3. evenSelected + if (nTot < 1) { + return; } - if (matchedPosition == -1) { + + const auto cent = getCentrality(collision); + if (!collision.sel8()) { return; } - // end find place to put run data - // fill some QA graph before collision cut + /// @note event qa + registry.fill(HIST("hProcessQA/centVsMult"), cent, tracks.size()); + registry.fill(HIST("hProcessQA/IR"), interactionRate); + registry.fill(HIST("hProcessQA/Occupacy"), collision.trackOccupancyInTimeRange()); + registry.fill(HIST("hProcessQA/occupancyVsCent"), cent, collision.trackOccupancyInTimeRange()); + // end event qa + + // i dont want to fill event count again as it's filled in other 5 function + if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcProcessQA)) { + return; + } + // end init && collision cut + + /// @note track loop for (const auto& track : tracks) { - qaHistVector[matchedPosition].hPhiRunByRunBefore->Fill(track.phi()); + /// @note select global track + // track cut isnt applied, this is track QA function! + if (!track.hasITS()) { + continue; + } + if (!track.hasTPC()) { + continue; + } + // end select global track + + /// @note fill QA graphs + registry.fill(HIST("hProcessQA/hDCAz"), track.dcaZ(), track.pt()); + registry.fill(HIST("hProcessQA/hDCAxy"), track.dcaXY(), track.pt()); + + registry.fill(HIST("hProcessQA/hChi2prTPCcls"), track.tpcChi2NCl()); + registry.fill(HIST("hProcessQA/hChi2prITScls"), track.itsChi2NCl()); + + registry.fill(HIST("hProcessQA/hnTPCClu"), track.tpcNClsFound()); + registry.fill(HIST("hProcessQA/hnITSClu"), track.itsNCls()); + registry.fill(HIST("hProcessQA/hnTPCCrossedRow"), track.tpcNClsCrossedRows()); + // end fill QA grahps + } + // end track loop + } + PROCESS_SWITCH(PidFlowPtCorr, processQA, "", true); + + /** + * @brief PID-performance QA for TPC, TOF, PID clusters and ITS response. + * @note open switch outputQA if use it + * + * @param collision + * @param tracks + */ + void detectorPidQA(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracksForDetectorPidQA const& tracks) + { + if (!switchsOpts.cfgOutputQA.value) { + return; } - // end fill some QA graph before collision cut + + // cut and correction + int nTot = tracks.size(); + auto bc = collision.bc_as(); + int runNumber = bc.runNumber(); + double interactionRate = getInteractionRate(bc.timestamp(), runNumber); + // end init // collision cut registry.fill(HIST("hEventCount/detectorPidQA"), 0.5); - if (nTot < 1) + if (nTot < 1) { return; + } fGFW->Clear(); - const auto cent = collision.centFT0C(); - if (!collision.sel8()) + const auto cent = getCentrality(collision); + if (!collision.sel8()) { return; + } registry.fill(HIST("hEventCount/detectorPidQA"), 1.5); - if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcDetectorPidQA)) + if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcDetectorPidQA)) { return; + } // loadCorrections(bc.timestamp()); // end cut and correction - // fill some QA graph after collision cut + // start filling graphs for (const auto& track : tracks) { - qaHistVector[matchedPosition].hPhiRunByRunAfter->Fill(track.phi()); - qaHistVector[matchedPosition].hITSnclsVsPhi->Fill(track.phi(), track.itsNCls()); - qaHistVector[matchedPosition].hITSChi2VsPhi->Fill(track.phi(), track.itsChi2NCl()); + // Use the same track-quality selection as processData, but do not + // require a PID hypothesis for the all-particle QA histograms. + if (!trackSelectedForFlow(track)) { + continue; + } + const float pt = track.pt(); + if (std::abs(track.sign()) == 1) { + registry.fill(HIST("DetectorPidQA/TPC/dEdxAll"), pt, track.tpcSignal()); + if (track.hasTOF()) { + registry.fill(HIST("DetectorPidQA/TOF/tofTimeAll"), pt, track.tofSignal() - track.tofEvTime()); + } + } + + const int pid = getPidConfigurable(track); + fillPidQualityQA(track, cent, pid); + // The existing species-dependent TPC/TOF histograms require the current + // unique PID assignment and its corresponding POI pT range. + if (!isWithinPOIPtRange(pid, pt)) { + continue; + } + + switch (pid) { + case MyParticleType::kPion: + registry.fill(HIST("DetectorPidQA/TPC/dEdxPi"), pt, track.tpcSignal()); + if (track.hasTOF()) { + registry.fill(HIST("DetectorPidQA/TOF/deltaPi"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); + } + break; + case MyParticleType::kKaon: + registry.fill(HIST("DetectorPidQA/TPC/dEdxKa"), pt, track.tpcSignal()); + if (track.hasTOF()) { + registry.fill(HIST("DetectorPidQA/TOF/deltaKa"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); + } + break; + case MyParticleType::kProton: + registry.fill(HIST("DetectorPidQA/TPC/dEdxPr"), pt, track.tpcSignal()); + if (track.hasTOF()) { + registry.fill(HIST("DetectorPidQA/TOF/deltaPr"), pt, o2::aod::pidutils::tofExpSignalDiff(track)); + } + break; + default: + break; + } + } // end filling graphs + } + PROCESS_SWITCH(PidFlowPtCorr, detectorPidQA, "", true); + + /** + * @brief Fill PID response matrix with MC truth PID versus reconstructed PID. + * @note The reconstructed PID uses getPidConfigurable(), so it follows the same PID cuts + * as the flow and efficiency parts of this task. Reco bin Unidentified contains + * tracks that pass quality cuts but fail or ambiguously pass PID selection. + */ + void processPidResponseMatrix(FilteredCollisionsWithMCLabel::iterator const& collision, + aod::BCsWithTimestamps const&, + FilteredTracksWithMCLabel const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + if (!switchsOpts.cfgAddPidResponseMatrixHistograms.value) { + return; + } + registry.fill(HIST("hEventCount/processPidResponseMatrix"), 0.5); + if (tracks.size() <= 0) { + return; + } + if (!collision.sel8()) { + return; + } + registry.fill(HIST("hEventCount/processPidResponseMatrix"), 1.5); + + const auto cent = getCentrality(collision); + auto bc = collision.bc_as(); + int runNumber = bc.runNumber(); + double interactionRate = getInteractionRate(bc.timestamp(), runNumber); + + if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcProcessPidResponseMatrix)) { + return; } - // end fill some QA graph after collision cut - // start filling graphs for (const auto& track : tracks) { - // track cut - if (!trackSelectedGlobal(track)) + if (!trackSelectedGlobal(track)) { continue; - if (!track.hasITS()) + } + if (!track.hasITS()) { continue; - if (!track.hasTPC()) + } + if (!track.hasTPC()) { continue; - if (!trackSelected4ITS(track)) + } + if (!trackSelected4ITS(track)) { continue; - if (!trackSelected4TPC(track)) + } + if (!trackSelected4TPC(track)) { continue; - // end track cut - - // TPC TOF - registry.fill(HIST("DetectorPidPerformace/TPCvsTOF/Pi"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); - registry.fill(HIST("DetectorPidPerformace/TPCvsTOF/Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - registry.fill(HIST("DetectorPidPerformace/TPCvsTOF/Ka"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); + } + if (!track.has_mcParticle()) { + continue; + } - // TPC ITS - registry.fill(HIST("DetectorPidPerformace/TPCvsITS/Pi"), track.tpcNSigmaPi(), itsResponse.nSigmaITS(track), track.pt()); - registry.fill(HIST("DetectorPidPerformace/TPCvsITS/Pr"), track.tpcNSigmaPr(), itsResponse.nSigmaITS(track), track.pt()); - registry.fill(HIST("DetectorPidPerformace/TPCvsITS/Ka"), track.tpcNSigmaKa(), itsResponse.nSigmaITS(track), track.pt()); + auto mcParticle = track.mcParticle(); + if (!mcParticle.isPhysicalPrimary() || !particleSelected(mcParticle)) { + continue; + } - // ITS vs TOF - registry.fill(HIST("DetectorPidPerformace/ITSvsTOF/Pi"), itsResponse.nSigmaITS(track), track.tofNSigmaPi(), track.pt()); - registry.fill(HIST("DetectorPidPerformace/ITSvsTOF/Pr"), itsResponse.nSigmaITS(track), track.tofNSigmaPr(), track.pt()); - registry.fill(HIST("DetectorPidPerformace/ITSvsTOF/Ka"), itsResponse.nSigmaITS(track), track.tofNSigmaKa(), track.pt()); + const int truePidBin = getPidResponseTrueBin(mcParticle.pdgCode()); + const int recoPidBin = getPidResponseRecoBin(getPidConfigurable(track)); - } // end filling graphs + registry.fill(HIST("pidResponseMatrix/hPidResponseMatrix"), truePidBin, recoPidBin); + registry.fill(HIST("pidResponseMatrix/hPidResponseMatrixRecPtCent"), truePidBin, recoPidBin, track.pt(), cent); + } } - PROCESS_SWITCH(PidFlowPtCorr, detectorPidQA, "", true); + PROCESS_SWITCH(PidFlowPtCorr, processPidResponseMatrix, "Fill PID response matrix from MC truth and reconstructed PID", false); /** * @brief this function is used to fill Reco particles in order to calculate pt eff @@ -2473,19 +5149,22 @@ struct PidFlowPtCorr { { // init && cut registry.fill(HIST("hEventCount/processReco"), 0.5); - if (tracks.size() <= 0) + if (tracks.size() <= 0) { return; - if (!collision.sel8()) + } + if (!collision.sel8()) { return; + } registry.fill(HIST("hEventCount/processReco"), 1.5); - const auto cent = collision.centFT0C(); + const auto cent = getCentrality(collision); auto bc = collision.bc_as(); int runNumber = bc.runNumber(); - double interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 1.e-3; + double interactionRate = getInteractionRate(bc.timestamp(), runNumber); - if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcProcessReco)) + if (!eventSelected(collision, cent, interactionRate, MyFunctionName::funcProcessReco)) { return; + } // end init && cut if (switchsOpts.cfgOutPutPtSpectra.value) { @@ -2495,43 +5174,40 @@ struct PidFlowPtCorr { // loop tracks for (const auto& track : tracks) { // track cut - if (!trackSelectedGlobal(track)) + if (!trackSelectedGlobal(track)) { continue; + } // end track cut // loop track's matched MC particle if (track.has_mcParticle()) { auto mcParticle = track.mcParticle(); // fill graph - if (particleSelected(mcParticle)) { + if (particleSelected(mcParticle) && mcParticle.isPhysicalPrimary()) { /// @note global track, fill rec hist if (track.hasITS() && track.hasTPC() && trackSelected4ITS(track) && trackSelected4TPC(track)) { // graph for all particles registry.fill(HIST("correction/hPtCentMcRec"), track.pt(), cent); + switch (getTrueSpeciesBin(mcParticle)) { + case kPidRespPion: + registry.fill(HIST("correction/hPtCentMcRecTruePi"), track.pt(), cent); + break; + case kPidRespKaon: + registry.fill(HIST("correction/hPtCentMcRecTrueKa"), track.pt(), cent); + break; + case kPidRespProton: + registry.fill(HIST("correction/hPtCentMcRecTruePr"), track.pt(), cent); + break; + default: + registry.fill(HIST("correction/hPtCentMcRecTrueOther"), track.pt(), cent); + break; + } + // ------------------------------ // Unified PID logic (configurable) // ------------------------------ - int pid = -1; - if (circleCutOpts.cfgUseCircleCutPID.value) { - // Use circular cut (already handles ambiguity rejection) - pid = getPidWithCircleCut(track); - } else { - // Use normal cut (with manual ambiguity rejection) - bool isPi = isPion(track); - bool isKa = isKaon(track); - bool isPr = isProton(track); - int nCandidates = isPi + isKa + isPr; - if (nCandidates == 1) { - if (isPi) - pid = MyParticleType::kPion; - else if (isKa) - pid = MyParticleType::kKaon; - else if (isPr) - pid = MyParticleType::kProton; - } - // else: pid remains -1 (ambiguous or none) - } + int pid = getPidConfigurable(track); // Fill MC reco histograms based on unified result if (pid == MyParticleType::kPion) { @@ -2545,12 +5221,6 @@ struct PidFlowPtCorr { // end identify particle and fill graph } // end global track, fill rec hist - - /// @note ITS track. fill rec hist - if (track.hasITS() && !track.hasTPC() && trackSelected4ITS(track)) { - registry.fill(HIST("correction/hPtCentMcRec4ITSOnly"), track.pt(), cent); - } - // end ITS track. fill rec hist } // end fill graph } @@ -2574,29 +5244,35 @@ struct PidFlowPtCorr { FilteredTracksWithMCLabel const& tracks) { registry.fill(HIST("hEventCount/processSim"), 0.5); - if (collisions.size() <= 0) + // Keep the generated denominator aligned with one reconstructed collision. + // Multiple associations would otherwise fill the same MC particles repeatedly. + if (collisions.size() != 1) { return; + } // cut && init // loop all the collisions reco matched to MC - double cent = -1; + for (const auto& oneColl : collisions) { auto bc = oneColl.bc_as(); int runNumber = bc.runNumber(); - double interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 1.e-3; - cent = oneColl.centFT0C(); + double interactionRate = getInteractionRate(bc.timestamp(), runNumber); + double cent = getCentrality(oneColl); auto groupedTracks = tracks.sliceBy(perCollision, oneColl.globalIndex()); - if (groupedTracks.size() <= 0) + if (groupedTracks.size() <= 0) { continue; + } // collision cut - if (!oneColl.sel8()) + if (!oneColl.sel8()) { continue; + } registry.fill(HIST("hEventCount/processSim"), 1.5); - if (!eventSelected(oneColl, cent, interactionRate, MyFunctionName::funcProcessSim)) + if (!eventSelected(oneColl, cent, interactionRate, MyFunctionName::funcProcessSim)) { continue; + } // end collision cut if (switchsOpts.cfgOutPutPtSpectra.value) { @@ -2610,6 +5286,21 @@ struct PidFlowPtCorr { // graph for all particles registry.fill(HIST("correction/hPtCentMcGen"), mcParticle.pt(), cent); + switch (getTrueSpeciesBin(mcParticle)) { + case kPidRespPion: + registry.fill(HIST("correction/hPtCentMcGenTruePi"), mcParticle.pt(), cent); + break; + case kPidRespKaon: + registry.fill(HIST("correction/hPtCentMcGenTrueKa"), mcParticle.pt(), cent); + break; + case kPidRespProton: + registry.fill(HIST("correction/hPtCentMcGenTruePr"), mcParticle.pt(), cent); + break; + default: + registry.fill(HIST("correction/hPtCentMcGenTrueOther"), mcParticle.pt(), cent); + break; + } + // identify particle and fill graph if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { registry.fill(HIST("correction/hPtCentMcGenPi"), mcParticle.pt(), cent); @@ -2631,11 +5322,97 @@ struct PidFlowPtCorr { } PROCESS_SWITCH(PidFlowPtCorr, processSim, "function used to do pt eff, NOTE (OutPutMc, processReco, processSim) should be open", true); + void processReco1D(FilteredCollisionsWithMCLabel::iterator const& collision, + aod::BCsWithTimestamps const&, + FilteredTracksWithMCLabel const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + if (!collision.sel8()) { + return; + } + if (tracks.size() < 1) { + return; + } + + for (const auto& track : tracks) { + + if (!trackSelectedGlobal(track)) { + continue; + } + if (!track.hasITS()) { + continue; + } + if (!track.hasTPC()) { + continue; + } + if (!trackSelected4ITS(track)) { + continue; + } + if (!trackSelected4TPC(track)) { + continue; + } + + if (track.has_mcParticle()) { + auto mcParticle = track.mcParticle(); + + if (isStable(mcParticle.pdgCode())) { + registry.fill(HIST("correction1D/hPtMCRec1D"), track.pt()); + + switch (std::abs(mcParticle.pdgCode())) { + case PDG_t::kPiPlus: + registry.fill(HIST("correction1D/hPtMCRec1DPi"), track.pt()); + break; + case PDG_t::kKPlus: + registry.fill(HIST("correction1D/hPtMCRec1DKa"), track.pt()); + break; + case PDG_t::kProton: + registry.fill(HIST("correction1D/hPtMCRec1DPr"), track.pt()); + break; + default: + break; + } // switch PDGCODE + } // stable + } // track.has_mcparticle + } // end track loop + } + PROCESS_SWITCH(PidFlowPtCorr, processReco1D, "process reconstructed information", true); + + void processSim1D(FilteredMcCollisions::iterator const&, + aod::BCsWithTimestamps const&, + soa::SmallGroups> const& collisions, + FilteredMcParticles const& mcParticles, + FilteredTracksWithMCLabel const&) + { + if (collisions.size() > -1) { + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary() && isStable(mcParticle.pdgCode())) { + registry.fill(HIST("correction1D/hPtMCGen1D"), mcParticle.pt()); + switch (std::abs(mcParticle.pdgCode())) { + case PDG_t::kPiPlus: + registry.fill(HIST("correction1D/hPtMCGen1DPi"), mcParticle.pt()); + break; + case PDG_t::kKPlus: + registry.fill(HIST("correction1D/hPtMCGen1DKa"), mcParticle.pt()); + break; + case PDG_t::kProton: + registry.fill(HIST("correction1D/hPtMCGen1DPr"), mcParticle.pt()); + break; + default: + break; + } + } + } + } + } + PROCESS_SWITCH(PidFlowPtCorr, processSim1D, "process pure simulation information", true); + // main function }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; } diff --git a/PWGCF/Flow/Tasks/resonancesGfwFlow.cxx b/PWGCF/Flow/Tasks/resonancesGfwFlow.cxx index 5749da5843c..9909d54112d 100644 --- a/PWGCF/Flow/Tasks/resonancesGfwFlow.cxx +++ b/PWGCF/Flow/Tasks/resonancesGfwFlow.cxx @@ -241,7 +241,7 @@ struct ResonancesGfwFlow { ConfigurableAxis axisParticles{"axisParticles", {3, 0, 3}, "axis for different hadrons"}; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtPOIMin) && (aod::track::pt < cfgCutPtPOIMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); + Filter trackFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz) && (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtPOIMin) && (aod::track::pt < cfgCutPtPOIMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); using AodCollisions = soa::Filtered>; using AodTracksWithoutBayes = soa::Filtered>; @@ -575,7 +575,7 @@ struct ResonancesGfwFlow { return nIndex; } - void fillProfileBoot(const GFW::CorrConfig& corrconf, std::shared_ptr profile, const double& cent) + void fillProfileBoot(const GFW::CorrConfig& corrconf, const std::shared_ptr& profile, const double& cent) { double dnx, val; if (!corrconf.pTDif) { @@ -590,7 +590,7 @@ struct ResonancesGfwFlow { return; } - void fillProfileBoot3D(const GFW::CorrConfig& corrconf, std::shared_ptr profile, const double& cent, TAxis* partaxis) + void fillProfileBoot3D(const GFW::CorrConfig& corrconf, const std::shared_ptr& profile, const double& cent, TAxis* partaxis) { double dnx, val; for (int i = 1; i <= fPtAxis->GetNbins(); i++) { @@ -672,7 +672,7 @@ struct ResonancesGfwFlow { } template - int getNsigmaPIDTpcTof(TTrack track) + int getNsigmaPIDTpcTof(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -703,7 +703,7 @@ struct ResonancesGfwFlow { } template - int getNsigmaPIDAssymmetric(TTrack track) + int getNsigmaPIDAssymmetric(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -796,7 +796,7 @@ struct ResonancesGfwFlow { } template - double getAcceptance(TTrack track, const TCollision collision, int pid_index_reso) + double getAcceptance(const TTrack& track, const TCollision& collision, int pid_index_reso) { // 0 = k0, 1 = lambda, 2 = phi, 3 = anti-lambda, 4 = ref if (pid_index_reso < 0 || pid_index_reso >= kCount_OutputSpecies) { return 1; @@ -825,7 +825,7 @@ struct ResonancesGfwFlow { } template - double getAcceptancePhi(vector mom, const TCollision collision, int pid_index_reso) + double getAcceptancePhi(const vector& mom, const TCollision& collision, int pid_index_reso) { // 0 = k0, 1 = lambda, 2 = phi, 3 = anti-lambda, 4 = ref if (pid_index_reso < 0 || pid_index_reso >= kCount_OutputSpecies) { return 1; @@ -856,7 +856,7 @@ struct ResonancesGfwFlow { } template - void fillWeights(const TTrack track, const TCollision collision, const int& pid_index_reso) + void fillWeights(const TTrack& track, const TCollision& collision, const int& pid_index_reso) { double cent = collision.centFT0C(); double vtxz = collision.posZ(); @@ -922,7 +922,7 @@ struct ResonancesGfwFlow { } template - void resurrectPhi(TTrack trackplus, TTrack trackminus, const TCollision collision, vector plusdaug, vector minusdaug, vector mom, double plusmass, const ConstStr& hist) + void resurrectPhi(const TTrack& trackplus, const TTrack& trackminus, const TCollision& collision, vector plusdaug, vector minusdaug, vector mom, double plusmass, const ConstStr& hist) { for (auto const& [partplus, partminus] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(trackplus, trackminus))) { histos.fill(HIST("hPhiCount"), 0.5); @@ -982,7 +982,7 @@ struct ResonancesGfwFlow { } template - void likeSignPhi(TTrack track, const TCollision collision, double plusmass, const ConstStr& hist) + void likeSignPhi(const TTrack& track, const TCollision& collision, double plusmass, const ConstStr& hist) { ROOT::Math::PxPyPzMVector daug1, daug2, mom; for (auto const& [part1, part2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(track, track))) { @@ -1188,7 +1188,7 @@ struct ResonancesGfwFlow { } template - bool selectionEvent(TCollision collision, const int mult, const float cent) + bool selectionEvent(const TCollision& collision, const int mult, const float cent) { histos.fill(HIST("hEventCount"), kFilteredEvents); if (!collision.sel8()) { diff --git a/PWGCF/GenericFramework/Core/CMakeLists.txt b/PWGCF/GenericFramework/Core/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/PWGCF/GenericFramework/Core/FlowContainer.cxx b/PWGCF/GenericFramework/Core/FlowContainer.cxx index daa553374f2..4b94468bde6 100644 --- a/PWGCF/GenericFramework/Core/FlowContainer.cxx +++ b/PWGCF/GenericFramework/Core/FlowContainer.cxx @@ -9,6 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file FlowContainer.cxx +/// \brief Container class to store and calculate multi-particle azimuthal correlations and cumulants +/// \author Emil Gorm Dahlbæk Nielsen + #include "FlowContainer.h" #include "PWGCF/GenericFramework/Core/ProfileSubset.h" @@ -30,7 +34,9 @@ #include #include -#include +#include +#include +#include #include ClassImp(FlowContainer); @@ -66,22 +72,22 @@ FlowContainer::~FlowContainer() delete fProf; delete fProfRand; }; -void FlowContainer::Initialize(TObjArray* inputList, const o2::framework::AxisSpec axis, int nRandom) +void FlowContainer::Initialize(TObjArray* inputList, const o2::framework::AxisSpec& axis, int nRandom) { std::vector multiBins = axis.binEdges; int nMultiBins = axis.nBins.value_or(0); if (nMultiBins <= 0) nMultiBins = multiBins.size() - 1; if (nMultiBins <= 0) { - printf("Multiplicity axis does not exist"); + LOGF(error, "Multiplicity axis does not exist"); return; } if (!inputList) { - printf("Input list not specified\n"); + LOGF(warning, "Input list not specified"); return; } if (inputList->GetEntries() < 1) { - printf("Input list empty!\n"); + LOGF(warning, "Input list empty!"); return; } fProf = new TProfile2D(Form("%s_CorrProfile", this->GetName()), "CorrProfile", nMultiBins, &multiBins[0], inputList->GetEntries(), 0.5, inputList->GetEntries() + 0.5); @@ -101,11 +107,11 @@ void FlowContainer::Initialize(TObjArray* inputList, const o2::framework::AxisSp void FlowContainer::Initialize(TObjArray* inputList, int nMultiBins, double MultiMin, double MultiMax, int nRandom) { if (!inputList) { - printf("Input list not specified\n"); + LOGF(warning, "Input list not specified"); return; } if (inputList->GetEntries() < 1) { - printf("Input list empty!\n"); + LOGF(warning, "Input list empty!"); return; } fProf = new TProfile2D(Form("%s_CorrProfile", this->GetName()), "CorrProfile", nMultiBins, MultiMin, MultiMax, inputList->GetEntries(), 0.5, inputList->GetEntries() + 0.5); @@ -138,7 +144,7 @@ void FlowContainer::SetXAxis(TAxis* inax) fXAxis = dynamic_cast(inax->Clone("pTAxis")); bool success = CreateBinsFromAxis(fXAxis); if (!success) - printf("Something went wrong setting the x axis!\n"); + LOGF(warning, "Something went wrong setting the x axis!"); } void FlowContainer::SetXAxis() { @@ -161,7 +167,7 @@ int FlowContainer::FillProfile(const char* hname, double multi, double corr, dou return -1; int yin = fProf->GetYaxis()->FindBin(hname); if (!yin) { - printf("Could not find bin %s\n", hname); + LOGF(info, "Could not find bin %s\n", hname); return -1; } fProf->Fill(multi, yin, corr, w); @@ -176,23 +182,23 @@ void FlowContainer::OverrideProfileErrors(TProfile2D* inpf) int nBinsX = fProf->GetNbinsX(); int nBinsY = fProf->GetNbinsY(); if ((inpf->GetNbinsX() != nBinsX) || (inpf->GetNbinsY() != nBinsY)) { - printf("Number of bins in two profiles do not match, not doing anything\n"); + LOGF(info, "Number of bins in two profiles do not match, not doing anything\n"); return; } if (!inpf->GetBinSumw2()->fArray) { - printf("Input profile has no BinSumw2()! Returning\n"); + LOGF(info, "Input profile has no BinSumw2()! Returning\n"); return; } if (!fProf->GetBinSumw2()->fArray) fProf->Sumw2(); double* sumw2Prof = fProf->GetSumw2()->fArray; - double* sumw2Targ = inpf->GetSumw2()->fArray; + const double* sumw2Targ = inpf->GetSumw2()->fArray; double* binsw2Prof = fProf->GetBinSumw2()->fArray; - double* binsw2Targ = inpf->GetBinSumw2()->fArray; + const double* binsw2Targ = inpf->GetBinSumw2()->fArray; double* farrProf = fProf->fArray; for (int ix = 1; ix <= nBinsX; ix++) { double xval = fProf->GetXaxis()->GetBinCenter(ix); - printf("Processing x-bin %i\n", ix); + LOGF(info, "Processing x-bin %i\n", ix); for (int iy = 1; iy <= nBinsY; iy++) { double yval = fProf->GetYaxis()->GetBinCenter(iy); int binno = fProf->FindBin(xval, yval); @@ -244,34 +250,38 @@ Long64_t FlowContainer::Merge(TCollection* collist) void FlowContainer::ReadAndMerge(const char* filelist) { - FILE* flist = fopen(filelist, "r"); - char str[150]; - int nFiles = 0; - while (fscanf(flist, "%s\n", str) == 1) - nFiles++; - rewind(flist); - if (nFiles == 0) { - printf("No files to read!\n"); + if (!filelist) { + LOGF(error, "File list path is null!"); + return; + } + std::ifstream input(filelist); + if (!input) { + LOGF(error, "Could not open file list %s!", filelist); return; } - for (int i = 0; i < nFiles; i++) { - auto retVal = fscanf(flist, "%s\n", str); - (void)retVal; - TFile* tf = new TFile(str, "READ"); - if (tf->IsZombie()) { - printf("Could not open file %s!\n", str); - tf->Close(); + + std::string filename; + bool hasFiles = false; + while (input >> filename) { + hasFiles = true; + TFile tf(filename.c_str(), "READ"); + if (tf.IsZombie()) { + LOGF(info, "Could not open file %s!", filename.c_str()); continue; } - PickAndMerge(tf); - tf->Close(); + PickAndMerge(&tf); + } + if (input.bad()) { + LOGF(error, "Error reading file list %s!", filelist); + } else if (!hasFiles) { + LOGF(info, "No files to read!"); } } void FlowContainer::PickAndMerge(TFile* tfi) { FlowContainer* lfc = dynamic_cast(tfi->Get(this->GetName())); if (!lfc) { - printf("Could not pick up the %s from %s\n", this->GetName(), tfi->GetName()); + LOGF(info, "Could not pick up the %s from %s", this->GetName(), tfi->GetName()); return; } TProfile2D* spro = lfc->GetProfile(); @@ -313,13 +323,13 @@ bool FlowContainer::OverrideBinsWithZero(int xb1, int yb1, int xb2, int yb2) bool FlowContainer::OverrideMainWithSub(int ind, bool ExcludeChosen) { if (!fProfRand) { - printf("Cannot override main profile with a randomized one. Random profile array does not exist.\n"); + LOGF(info, "Cannot override main profile with a randomized one. Random profile array does not exist."); return kFALSE; } if (!ExcludeChosen) { TProfile2D* tarprof = dynamic_cast(fProfRand->At(ind)); if (!tarprof) { - printf("Target random histogram does not exist.\n"); + LOGF(info, "Target random histogram does not exist."); return kFALSE; } TString ts(fProf->GetName()); @@ -345,7 +355,7 @@ bool FlowContainer::OverrideMainWithSub(int ind, bool ExcludeChosen) bool FlowContainer::RandomizeProfile(int nSubsets) { if (!fProfRand) { - printf("Cannot randomize profile, random array does not exist.\n"); + LOGF(info, "Cannot randomize profile, random array does not exist."); return kFALSE; } int l_Subsets = nSubsets ? nSubsets : fProfRand->GetEntries(); @@ -381,7 +391,7 @@ bool FlowContainer::CreateStatisticsProfile(StatisticsType StatType, int arg) } void FlowContainer::SetIDName(TString newname) { - fIDName = newname; + fIDName = std::move(newname); } TProfile* FlowContainer::GetCorrXXVsMulti(const char* order, int l_pti) { @@ -393,7 +403,7 @@ TProfile* FlowContainer::GetCorrXXVsMulti(const char* order, int l_pti) const char* ybinlab = Form("%s%s%s", l_name.Data(), order, ptpf); int ybinno = fProf->GetYaxis()->FindBin(ybinlab); if (ybinno < 0) { - printf("Could not find %s!\n", ybinlab); + LOGF(info, "Could not find %s!", ybinlab); return 0; } TProfile* rethist = dynamic_cast(fProf->ProfileX("temp_prof", ybinno, ybinno)); @@ -405,6 +415,10 @@ TProfile* FlowContainer::GetCorrXXVsMulti(const char* order, int l_pti) } delete rethist; } + if (!retSubset) { + LOGF(error, "Correlation profile is null"); + return nullptr; + } if (fMultiRebin > 0) { TString temp_name(retSubset->GetName()); TProfile* tempprof = dynamic_cast(retSubset->Clone("tempProfile")); @@ -426,7 +440,6 @@ TH1D* FlowContainer::GetCorrXXVsPt(const char* order, double lminmulti, double l } if (lmaxmulti > lminmulti) maxm = fProf->GetXaxis()->FindBin(lmaxmulti - 0.001); - ProfileSubset* rhProfSub = new ProfileSubset(*fProf); TString l_name(""); Ssiz_t l_pos = 0; while (fIDName.Tokenize(l_name, l_pos)) { @@ -435,20 +448,19 @@ TH1D* FlowContainer::GetCorrXXVsPt(const char* order, double lminmulti, double l int ybn1 = fProf->GetYaxis()->FindBin(ybl1.Data()); int ybn2 = fProf->GetYaxis()->FindBin(ybl2.Data()); if (fNbinsPt != (ybn2 - ybn1 + 1)) { - printf("fNbinsPt is not matching the num of found histograms"); + LOGF(info, "fNbinsPt is not matching the num of found histograms"); return nullptr; } - TProfile* profY = rhProfSub->ProfileY("profY", minm, maxm); + const TString temporaryTag = Form("%s_%s_%.3f_%.3f", fIDName.Data(), order, lminmulti, lmaxmulti); + TProfile* profY = fProf->ProfileY(Form("profY_%s", temporaryTag.Data()), minm, maxm); TH1D* histY = ProfToHist(profY); - TH1D* hist = new TH1D("temphist", "temphist", fNbinsPt, fbinsPt); + delete profY; + TH1D* hist = new TH1D(Form("temphist_%s", temporaryTag.Data()), "temphist", fNbinsPt, fbinsPt); for (int ibin = 1; ibin <= hist->GetNbinsX(); ibin++) { - TString bLabel = rhProfSub->GetYaxis()->GetBinLabel(ibin + ybn1 - 1); - hist->GetXaxis()->SetBinLabel(ibin, bLabel.Data()); hist->SetBinContent(ibin, histY->GetBinContent(ibin + ybn1 - 1)); hist->SetBinError(ibin, histY->GetBinError(ibin + ybn1 - 1)); } delete histY; - delete rhProfSub; return hist; } return nullptr; @@ -479,7 +491,7 @@ TH1D* FlowContainer::GetHistCorrXXVsPt(const char* order, double lminmulti, doub { TH1D* rethist = GetCorrXXVsPt(order, lminmulti, lmaxmulti); if (!rethist) { - printf("GetCorrXXVsPt return nullptr!"); + LOGF(info, "GetCorrXXVsPt return nullptr!"); return nullptr; } TProfile* refflow = GetRefFlowProfile(order, lminmulti, lmaxmulti); @@ -517,9 +529,9 @@ TH1D* FlowContainer::GetCN2VsX(int n, bool onPt, double arg1, double arg2) corrN2 = GetHistCorrXXVsMulti(Form("%i2", n), static_cast(arg1)); corrN2->SetName(Form("Corr_%s", corrN2->GetName())); TH1D* rethist = GetCN2(corrN2); - TString* nam = new TString(corrN2->GetName()); + const TString nam(corrN2->GetName()); delete corrN2; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -535,10 +547,10 @@ TH1D* FlowContainer::GetCN2VsX(int n, bool onPt, double arg1, double arg2) TH1D* FlowContainer::GetVN2VsX(int n, bool onPt, double arg1, double arg2) { TH1D* corrh = GetCN2VsX(n, onPt, arg1, arg2); - TString* nam = new TString(corrh->GetName()); + const TString nam(corrh->GetName()); TH1D* rethist = GetVN2(corrh); delete corrh; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -548,7 +560,6 @@ TH1D* FlowContainer::GetVN2VsX(int n, bool onPt, double arg1, double arg2) } else { rethist->SetTitle(Form(";#it{N}_{tr};v_{%i}{2}", n)); } - delete nam; return rethist; } @@ -698,10 +709,10 @@ TH1D* FlowContainer::GetCN4VsX(int n, bool onPt, double arg1, double arg2) corrN4->SetName(Form("Corr_%s", corrN4->GetName())); } TH1D* rethist = GetCN4(corrN4, corrN2); - TString* nam = new TString(corrN4->GetName()); + const TString nam(corrN4->GetName()); delete corrN2; delete corrN4; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -772,9 +783,9 @@ TH1D* FlowContainer::GetVN4VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN4VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN4(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -804,9 +815,9 @@ TH1D* FlowContainer::GetCN6VsX(int n, bool onPt, double arg1, double arg2) TH1D* rethist = GetCN6(corrN6, corrN4, corrN2); delete corrN2; delete corrN4; - TString* nam = new TString(corrN6->GetName()); + const TString nam(corrN6->GetName()); delete corrN6; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -822,9 +833,9 @@ TH1D* FlowContainer::GetVN6VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN6VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN6(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -858,9 +869,9 @@ TH1D* FlowContainer::GetCN8VsX(int n, bool onPt, double arg1, double arg2) delete corrN2; delete corrN4; delete corrN6; - TString* nam = new TString(corrN8->GetName()); + const TString nam(corrN8->GetName()); delete corrN8; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -876,9 +887,9 @@ TH1D* FlowContainer::GetVN8VsX(int n, bool onPt, double arg1, double arg2) { TH1D* temph = GetCN8VsX(n, onPt, arg1, arg2); TH1D* rethist = GetVN8(temph); - TString* nam = new TString(temph->GetName()); + const TString nam(temph->GetName()); delete temph; - rethist->SetName(nam->Data()); + rethist->SetName(nam.Data()); if (onPt) { int bins = fProf->GetXaxis()->FindBin(arg1); int bins2 = fProf->GetXaxis()->FindBin(arg2); @@ -890,23 +901,24 @@ TH1D* FlowContainer::GetVN8VsX(int n, bool onPt, double arg1, double arg2) } return rethist; } + TH1D* FlowContainer::GetCNN(int n, int c, bool onPt, double arg1, double arg2) { - if (c == 8) + if (c == kEightParticleOrder) return GetCN8VsX(n, onPt, arg1, arg2); - if (c == 6) + if (c == kSixParticleOrder) return GetCN6VsX(n, onPt, arg1, arg2); - if (c == 4) + if (c == kFourParticleOrder) return GetCN4VsX(n, onPt, arg1, arg2); return GetCN2VsX(n, onPt, arg1, arg2); }; TH1D* FlowContainer::GetVNN(int n, int c, bool onPt, double arg1, double arg2) { - if (c == 8) + if (c == kEightParticleOrder) return GetVN8VsX(n, onPt, arg1, arg2); - if (c == 6) + if (c == kSixParticleOrder) return GetVN6VsX(n, onPt, arg1, arg2); - if (c == 4) + if (c == kFourParticleOrder) return GetVN4VsX(n, onPt, arg1, arg2); return GetVN2VsX(n, onPt, arg1, arg2); }; @@ -919,7 +931,7 @@ TProfile* FlowContainer::GetRefFlowProfile(const char* order, double m1, double if (nStopBin < nStartBin) nStopBin = fProf->GetXaxis()->GetNbins(); int nBins = nStopBin - nStartBin + 1; - double* l_bins = new double[nBins + 1]; + std::vector l_bins(nBins + 1); for (int i = 0; i <= nBins; i++) l_bins[i] = i; TProfile* retpf = 0; @@ -930,7 +942,7 @@ TProfile* FlowContainer::GetRefFlowProfile(const char* order, double m1, double while (fIDName.Tokenize(l_name, l_pos)) { l_name.Append(order); int ybin = fProf->GetYaxis()->FindBin(l_name.Data()); - TProfile* tempprof = rhSubset->GetSubset(kTRUE, "tempprof", ybin, ybin, nBins, l_bins); + TProfile* tempprof = rhSubset->GetSubset(kTRUE, "tempprof", ybin, ybin, nBins, l_bins.data()); if (!retpf) retpf = dynamic_cast(tempprof->Clone("RefFlowProf")); else @@ -1051,12 +1063,14 @@ double FlowContainer::CN6Error(double cor6e, double cor4, double cor4e, double c { if (!fPropagateErrors) return 0; - double inters[3]; + + constexpr int kCN6Terms = 3; + double inters[kCN6Terms]; inters[0] = cor6e; inters[1] = -9 * cor2 * cor4e; inters[2] = (-9 * cor4 + 36 * cor2 * cor2) * cor2e; double sum = 0; - for (int i = 0; i < 3; i++) + for (int i = 0; i < kCN6Terms; i++) sum += (inters[i] * inters[i]); return TMath::Sqrt(sum); }; @@ -1070,14 +1084,15 @@ double FlowContainer::DN6Error(double d6e, double d4, double d4e, double d2, { if (!fPropagateErrors) return 0; - double inters[5]; + constexpr int kDN6Terms = 5; + double inters[kDN6Terms]; inters[0] = d6e; inters[1] = -6 * c2 * d4e; inters[2] = (-3 * c4 + 12 * c2 * c2) * d2e; inters[3] = -3 * d2 * c4e; inters[4] = (-6 * d4 + 24 * d2 * c2) * c2e; double sum = 0; - for (int i = 0; i < 5; i++) + for (int i = 0; i < kDN6Terms; i++) sum += (inters[i] * inters[i]); return TMath::Sqrt(sum); }; @@ -1126,13 +1141,14 @@ double FlowContainer::CN8Error(double cor8e, double cor6, double cor6e, { if (!fPropagateErrors) return 0; - double parts[4]; + constexpr int kCN8Terms = 4; + double parts[kCN8Terms]; parts[0] = cor8e; parts[1] = -16 * cor2 * cor6e; parts[2] = (-36 * cor4 + 144 * cor2 * cor2) * cor4e; parts[3] = (-16 * cor6 + 288 * cor4 * cor2 + 576 * cor2 * cor2 * cor2) * cor2e; double retval = 0; - for (int i = 0; i < 4; i++) + for (int i = 0; i < kCN8Terms; i++) retval += TMath::Power(parts[i], 2); return TMath::Sqrt(retval); }; @@ -1147,7 +1163,8 @@ double FlowContainer::DN8Error(double d8e, double d6, double d6e, double d4, { if (!fPropagateErrors) return 0; - double parts[7]; + constexpr int kDN8Terms = 7; + double parts[kDN8Terms]; parts[0] = d8e; // d/d8' parts[1] = -12 * c2 * d6e; // d/d6' parts[2] = -4 * d2 * c6e; // d/d6 @@ -1156,7 +1173,7 @@ double FlowContainer::DN8Error(double d8e, double d6, double d6e, double d4, parts[5] = (-4 * c6 + 72 * c4 * c2 - 144 * c2 * c2 * c2) * d2e; parts[6] = (-12 * d6 + 144 * d4 * c2 + 72 * c4 * d2 - 432 * d2 * c2 * c2) * c2e; double retval = 0; - for (int i = 0; i < 7; i++) + for (int i = 0; i < kDN8Terms; i++) retval += TMath::Power(parts[i], 2); return TMath::Sqrt(retval); }; @@ -1197,26 +1214,6 @@ void FlowContainer::SetPtRebin(int nbins, double* binedges) { fPtRebin = nbins; fPtRebinEdges = binedges; - return; - int fPtRebin = 0; - // double *lPtRebinEdges=binedges; - if (!fbinsPt) - SetXAxis(); - for (int i = 0; i < nbins; i++) - if (binedges[i] < fbinsPt[0] || binedges[i] > fbinsPt[fNbinsPt - 1]) - continue; - else - fPtRebin++; - if (fPtRebinEdges) - delete[] fPtRebinEdges; - fPtRebinEdges = new double[fPtRebin]; - fPtRebin = 0; - for (int i = 0; i < nbins; i++) - if (binedges[i] < fbinsPt[0] || binedges[i] > fbinsPt[fNbinsPt]) - continue; - else - fPtRebinEdges[fPtRebin++] = binedges[i]; - // fPtRebin--; } void FlowContainer::SetMultiRebin(int nbins, double* binedges) { @@ -1243,5 +1240,5 @@ double* FlowContainer::GetMultiRebin(int& nbins) double* retBins = new double[fMultiRebin + 1]; for (int i = 0; i <= nbins; i++) retBins[i] = fMultiRebinEdges[i]; - return fMultiRebinEdges; + return retBins; } diff --git a/PWGCF/GenericFramework/Core/FlowContainer.h b/PWGCF/GenericFramework/Core/FlowContainer.h index 941442a941e..c3ac46fc704 100644 --- a/PWGCF/GenericFramework/Core/FlowContainer.h +++ b/PWGCF/GenericFramework/Core/FlowContainer.h @@ -40,7 +40,7 @@ class FlowContainer : public TNamed enum StatisticsType { kSingleSample, kJackKnife, kBootstrap }; - void Initialize(TObjArray* inputList, const o2::framework::AxisSpec axis, int nRandomized = 0); + void Initialize(TObjArray* inputList, const o2::framework::AxisSpec& axis, int nRandomized = 0); void Initialize(TObjArray* inputList, int nMultiBins, double MultiMin, double MultiMax, int nRandomized = 0); bool CreateBinsFromAxis(TAxis* inax); void SetXAxis(TAxis* inax); @@ -165,6 +165,12 @@ class FlowContainer : public TNamed double* fbinsPt; //! Do not store; stored in fXAxis bool fPropagateErrors; //! do not store TProfile* GetRefFlowProfile(const char* order, double m1 = -1, double m2 = -1); + + private: + static constexpr int kTwoParticleOrder = 2; + static constexpr int kFourParticleOrder = 4; + static constexpr int kSixParticleOrder = 6; + static constexpr int kEightParticleOrder = 8; ClassDef(FlowContainer, 2); }; diff --git a/PWGCF/GenericFramework/Core/FlowPtContainer.cxx b/PWGCF/GenericFramework/Core/FlowPtContainer.cxx index 3e6c594643d..d76b8a6bb39 100644 --- a/PWGCF/GenericFramework/Core/FlowPtContainer.cxx +++ b/PWGCF/GenericFramework/Core/FlowPtContainer.cxx @@ -16,6 +16,7 @@ #include "FlowPtContainer.h" #include "PWGCF/GenericFramework/Core/BootstrapProfile.h" +#include "PWGCF/GenericFramework/Core/GFW.h" #include "PWGCF/GenericFramework/Core/GFWConfig.h" #include @@ -31,43 +32,172 @@ #include #include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include using namespace o2::analysis::genericframework; using namespace o2::analysis::genericframework::eventweight; +using o2::analysis::genericframework::flowptcontainer::PtCorrConfig; +using o2::analysis::genericframework::flowptcontainer::PtMomentConfig; -FlowPtContainer::FlowPtContainer() : fCMTermList(0), - fCorrList(0), - fCovList(0), - fSubList(0), - fSubCMList(0), - fCumulantList(0), - fCentralMomentList(0), +namespace +{ +constexpr std::size_t TwoParticleOrder = 2; +constexpr int ParityDivisor = 2; + +bool parseInteger(std::string_view token, int& value) +{ + if (!token.empty() && token.front() == '+') { + token.remove_prefix(1); + } + const auto result = std::from_chars(token.data(), token.data() + token.size(), value); + return !token.empty() && result.ec == std::errc{} && result.ptr == token.data() + token.size(); +} + +std::optional>> replacePtTerms(std::string& expression, const std::string& head) +{ + std::vector> isPt; + size_t cursor = 0; + while (true) { + auto openBrace = expression.find('{', cursor); + if (openBrace == std::string::npos) { + break; + } + if (openBrace > 0 && std::isspace(static_cast(expression[openBrace - 1])) == 0) { + expression.insert(openBrace++, 1, ' '); + } + const auto closeBrace = expression.find('}', openBrace + 1); + if (closeBrace == std::string::npos || expression.find('{', openBrace + 1) < closeBrace) { + LOGF(error, "Invalid pT correlation %s: unmatched brace", head.c_str()); + return std::nullopt; + } + + std::string terms = expression.substr(openBrace + 1, closeBrace - openBrace - 1); + std::replace(terms.begin(), terms.end(), ',', ' '); + std::replace(terms.begin(), terms.end(), ';', ' '); + std::istringstream stream(terms); + std::string replacement; + std::string token; + std::vector groupIsPt; + while (stream >> token) { + int harmonic = 0; + const bool tokenIsPt = token == "pt"; + if (!tokenIsPt && !parseInteger(token, harmonic)) { + LOGF(error, "Invalid pT correlation %s: unknown observable %s", head.c_str(), token.c_str()); + return std::nullopt; + } + if (!replacement.empty()) { + replacement += ' '; + } + replacement += tokenIsPt ? "0" : token; + groupIsPt.push_back(tokenIsPt); + } + if (groupIsPt.empty()) { + LOGF(error, "Invalid pT correlation %s: empty observable group", head.c_str()); + return std::nullopt; + } + + expression.replace(openBrace + 1, closeBrace - openBrace - 1, replacement); + cursor = openBrace + replacement.size() + 2; + isPt.push_back(std::move(groupIsPt)); + } + + if (isPt.empty()) { + LOGF(error, "Invalid pT correlation %s: no observable groups", head.c_str()); + return std::nullopt; + } + return isPt; +} + +std::string makeFullTopologyName(const std::string& head, std::uint64_t activeFactors, std::uint64_t allFactors, bool hasFlow, int ptOrder) +{ + if (activeFactors == allFactors) { + return head; + } + + std::string name = head + "_full"; + if (hasFlow && (activeFactors & std::uint64_t{1}) != 0) { + name += "_flow"; + } + const int ptOffset = hasFlow ? 1 : 0; + for (int i = 0; i < ptOrder; ++i) { + if ((activeFactors & (std::uint64_t{1} << (ptOffset + i))) != 0) { + name += "_pt" + std::to_string(i + 1); + } + } + return name; +} + +double binomialCoefficient(int n, int k) +{ + k = std::min(k, n - k); + double result = 1.; + for (int i = 1; i <= k; ++i) { + result *= static_cast(n - k + i) / i; + } + return result; +} + +std::string makePostprocessName(std::string_view observable, const char* head, int ind) +{ + return std::string{head} + "_" + std::string{observable} + "_" + std::to_string(ind); +} + +bool sameMomentConfigs(const std::vector& first, const std::vector& second) +{ + if (first.size() != second.size()) { + return false; + } + for (size_t config = 0; config < first.size(); ++config) { + if (first[config].head != second[config].head || first[config].groups.size() != second[config].groups.size()) { + return false; + } + for (size_t group = 0; group < first[config].groups.size(); ++group) { + const auto& lhs = first[config].groups[group]; + const auto& rhs = second[config].groups[group]; + if (lhs.region != rhs.region || lhs.order != rhs.order || lhs.meanProfile != rhs.meanProfile) { + return false; + } + } + } + return true; +} +} // namespace + +FlowPtContainer::FlowPtContainer() : fCMTermList(nullptr), + fCorrList(nullptr), + fCovList(nullptr), + fSubList(nullptr), + fSubCMList(nullptr), + fCumulantList(nullptr), + fCentralMomentList(nullptr), mpar(0), nSubevents(0), fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), fUseGap(false), - sumP(), - insub(), - corrNum(), - corrNumSub(), - corrDen(), - corrDenSub(), - cmVal(), - cmValSub(), - cmDen(), - cmDenSub(), - arr(), - warr(), - subevents() {} + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) +{ +} FlowPtContainer::~FlowPtContainer() { delete fCMTermList; @@ -77,93 +207,415 @@ FlowPtContainer::~FlowPtContainer() delete fSubCMList; delete fCumulantList; delete fCentralMomentList; + delete fPtCentralMomentList; + delete fPtCumulantList; }; FlowPtContainer::FlowPtContainer(const char* name) : TNamed(name, name), - fCMTermList(0), - fCorrList(0), - fCovList(0), - fSubList(0), - fSubCMList(0), - fCumulantList(0), - fCentralMomentList(0), + fCMTermList(nullptr), + fCorrList(nullptr), + fCovList(nullptr), + fSubList(nullptr), + fSubCMList(nullptr), + fCumulantList(nullptr), + fCentralMomentList(nullptr), mpar(0), nSubevents(0), fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), fUseGap(false), - sumP(), - insub(), - corrNum(), - corrNumSub(), - corrDen(), - corrDenSub(), - cmVal(), - cmValSub(), - cmDen(), - cmDenSub(), - arr(), - warr(), - subevents() {} + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) +{ +} FlowPtContainer::FlowPtContainer(const char* name, const char* title) : TNamed(name, title), - fCMTermList(0), - fCorrList(0), - fCovList(0), - fSubList(0), - fSubCMList(0), - fCumulantList(0), - fCentralMomentList(0), + fCMTermList(nullptr), + fCorrList(nullptr), + fCovList(nullptr), + fSubList(nullptr), + fSubCMList(nullptr), + fCumulantList(nullptr), + fCentralMomentList(nullptr), mpar(0), nSubevents(0), fillCounter(0), fEventWeight(EventWeight::UnityWeight), fUseCentralMoments(true), fUseGap(false), - sumP(), - insub(), - corrNum(), - corrNumSub(), - corrDen(), - corrDenSub(), - cmVal(), - cmValSub(), - cmDen(), - cmDenSub(), - arr(), - warr(), - subevents() {} -void FlowPtContainer::initialise(const o2::framework::AxisSpec axis, const int& m, const GFWCorrConfigs& configs, const int& nsub) + fPtCentralMomentList(nullptr), + fPtCumulantList(nullptr) +{ +} +void FlowPtContainer::initialise(const o2::framework::AxisSpec& axis, const GFWRegions& regions, const GFWCorrConfigs& configs, const int& nsub) +{ + const auto numberOfRegions = regions.GetNames().size(); + const auto numberOfConfigs = configs.GetCorrs().size(); + if (regions.GetEtaMin().size() != numberOfRegions || regions.GetEtaMax().size() != numberOfRegions || + regions.GetpTDifs().size() != numberOfRegions || regions.GetBitmasks().size() != numberOfRegions || + configs.GetHeads().size() != numberOfConfigs || configs.GetpTDifs().size() != numberOfConfigs) { + LOGF(error, "Cannot initialize %s: GFW configuration vectors have different sizes", GetName()); + return; + } + if (nsub < 0) { + LOGF(error, "Cannot initialize %s: number of subsamples cannot be negative", GetName()); + return; + } + + std::vector multiBins = axis.binEdges; + int numberOfBins = axis.nBins.value_or(0); + const bool isVariableBinning = numberOfBins <= 0; + if (isVariableBinning) { + numberOfBins = multiBins.size() - 1; + } + const size_t expectedEdges = isVariableBinning ? static_cast(numberOfBins + 1) : 2u; + if (numberOfBins <= 0 || multiBins.size() != expectedEdges) { + LOGF(error, "Cannot initialize %s: invalid multiplicity axis", GetName()); + return; + } + + GFW parser; + for (size_t i = 0; i < numberOfRegions; ++i) { + parser.AddRegion(regions.GetNames()[i], regions.GetEtaMin()[i], regions.GetEtaMax()[i], regions.GetpTDifs()[i] != 0 ? 2 : 1, regions.GetBitmasks()[i]); + } + if (parser.fRegions.size() != numberOfRegions) { + LOGF(error, "Cannot initialize %s: invalid GFW region", GetName()); + return; + } + + struct ParsedPtRequest { + GFW::CorrConfig config; + std::vector> isPt; + std::vector> factors; + std::vector> ptTerms; + int ptOrder{}; + bool hasFlow{}; + std::uint64_t allFactors{}; + }; + + std::vector requests; + requests.reserve(numberOfConfigs); + std::unordered_set correlationHeads; + std::vector regionMaxOrders(numberOfRegions, 0); + int maxPtOrder = 0; + for (size_t i = 0; i < numberOfConfigs; ++i) { + if (configs.GetpTDifs()[i] != 0) { + LOGF(error, "Invalid pT correlation %s: differential configurations are not supported", configs.GetHeads()[i].c_str()); + return; + } + std::string expression = configs.GetCorrs()[i]; + auto isPt = replacePtTerms(expression, configs.GetHeads()[i]); + if (!isPt) { + return; + } + auto config = parser.GetCorrelatorConfig(expression, configs.GetHeads()[i], configs.GetpTDifs()[i] != 0); + if (config.Regs.size() != isPt->size() || config.Hars.size() != isPt->size() || config.Overlap.size() != isPt->size() || config.ptInd.size() != isPt->size()) { + LOGF(error, "Invalid pT correlation %s", config.Head.c_str()); + return; + } + if (std::any_of(config.ptInd.begin(), config.ptInd.end(), [](int ptIndex) { return ptIndex >= 0; })) { + LOGF(error, "Invalid pT correlation %s: explicit pT-bin indices are not supported", config.Head.c_str()); + return; + } + int ptOrder = 0; + bool hasFlow = false; + std::vector> ptTerms; + std::vector> factors(isPt->size()); + for (size_t group = 0; group < isPt->size(); ++group) { + if (config.Regs[group].empty() || config.Hars[group].size() != isPt->at(group).size()) { + LOGF(error, "Invalid pT correlation %s", config.Head.c_str()); + return; + } + const bool groupHasPt = std::any_of(isPt->at(group).begin(), isPt->at(group).end(), [](bool value) { return value; }); + const bool groupHasFlow = std::any_of(isPt->at(group).begin(), isPt->at(group).end(), [](bool value) { return !value; }); + if (groupHasPt && groupHasFlow) { + LOGF(error, "Invalid pT correlation %s: mixed flow and pt terms in one group are not supported", config.Head.c_str()); + return; + } + if (groupHasPt && (config.Regs[group].size() != 1 || config.Overlap[group] >= 0)) { + LOGF(error, "Invalid pT correlation %s: pt groups must contain exactly one region without an explicit overlap", config.Head.c_str()); + return; + } + factors[group].resize(isPt->at(group).size()); + for (size_t term = 0; term < isPt->at(group).size(); ++term) { + if (isPt->at(group)[term]) { + ptTerms.emplace_back(group, term); + ++ptOrder; + } else { + hasFlow = true; + } + } + } + if (ptOrder == 0) { + LOGF(error, "Invalid pT correlation %s: configuration does not contain pt", config.Head.c_str()); + return; + } + if (config.Head.empty() || !correlationHeads.insert(config.Head).second) { + LOGF(error, "Cannot initialize %s: correlation heads must be non-empty and unique", GetName()); + return; + } + + const int factorCount = ptOrder + (hasFlow ? 1 : 0); + if (factorCount >= std::numeric_limits::digits) { + LOGF(error, "Invalid pT correlation %s: too many observable factors", config.Head.c_str()); + return; + } + const int ptOffset = hasFlow ? 1 : 0; + for (int ptIndex = 0; ptIndex < ptOrder; ++ptIndex) { + const auto [group, term] = ptTerms[ptIndex]; + factors[group][term] = ptOffset + ptIndex; + } + if (hasFlow) { + for (size_t group = 0; group < isPt->size(); ++group) { + if (!isPt->at(group).front()) { + std::fill(factors[group].begin(), factors[group].end(), 0); + } + } + } + const std::uint64_t allFactors = (std::uint64_t{1} << factorCount) - 1; + for (size_t group = 0; group < isPt->size(); ++group) { + if (!isPt->at(group).front()) { + continue; + } + const int order = isPt->at(group).size(); + for (const int& region : config.Regs[group]) { + regionMaxOrders[region] = std::max(regionMaxOrders[region], order); + } + if (config.Overlap[group] >= 0) { + regionMaxOrders[config.Overlap[group]] = std::max(regionMaxOrders[config.Overlap[group]], order); + } + } + + maxPtOrder = std::max(maxPtOrder, ptOrder); + requests.push_back({.config = std::move(config), + .isPt = std::move(*isPt), + .factors = std::move(factors), + .ptTerms = std::move(ptTerms), + .ptOrder = ptOrder, + .hasFlow = hasFlow, + .allFactors = allFactors}); + } + + struct NaturalMean { + int region{}; + int ptIndex{}; + bool pTDif{}; + std::string profile; + }; + std::vector naturalMeans; + auto getPtRegion = [](const ParsedPtRequest& request, int ptIndex) { + const auto group = request.ptTerms[ptIndex].first; + return request.config.Regs[group].front(); + }; + auto findNaturalMean = [&](const ParsedPtRequest& request, int ptIndex) { + const auto group = request.ptTerms[ptIndex].first; + const int region = getPtRegion(request, ptIndex); + return std::find_if(naturalMeans.begin(), naturalMeans.end(), [&](const auto& mean) { + return mean.region == region && mean.ptIndex == request.config.ptInd[group] && mean.pTDif == request.config.pTDif; + }); + }; + + // Prefer explicitly requested one-particle observables as the shared native means, + // irrespective of their order in the configuration list. + for (const auto& request : requests) { + if (request.hasFlow || request.ptOrder != 1 || findNaturalMean(request, 0) != naturalMeans.end()) { + continue; + } + const auto group = request.ptTerms.front().first; + naturalMeans.push_back({.region = getPtRegion(request, 0), + .ptIndex = request.config.ptInd[group], + .pTDif = request.config.pTDif, + .profile = request.config.Head}); + } + + std::vector parsedConfigs; + parsedConfigs.reserve(requests.size()); + std::vector momentConfigs; + momentConfigs.reserve(requests.size()); + for (const auto& request : requests) { + const int ptOffset = request.hasFlow ? 1 : 0; + PtCorrConfig parsed{ + .head = request.config.Head, + .allFactors = request.allFactors, + .hasFlow = request.hasFlow, + .profiles = {}, + .meanProfiles = std::vector(request.ptOrder)}; + + // Factors in one single-region pT group are exchangeable. Their full-topology + // terms therefore depend only on how many factors are active. + std::vector> factorClasses; + if (request.hasFlow) { + factorClasses.push_back({0}); + } + for (size_t group = 0; group < request.isPt.size(); ++group) { + if (!request.isPt[group].front()) { + continue; + } + factorClasses.push_back(request.factors[group]); + } + + std::vector activeMasks{0}; + for (const auto& factors : factorClasses) { + const size_t previousSize = activeMasks.size(); + for (size_t i = 0; i < previousSize; ++i) { + std::uint64_t addedFactors = 0; + for (const int& factor : factors) { + addedFactors |= std::uint64_t{1} << factor; + activeMasks.push_back(activeMasks[i] | addedFactors); + } + } + } + std::sort(activeMasks.begin(), activeMasks.end()); + parsed.profiles.reserve(activeMasks.size() + request.ptOrder); + + // Inactive factors stay in the tuple as harmonic-zero multiplicity insertions, + // so every subset retains the event weight of the full correlation. + auto addFullTopologyProfile = [&](std::uint64_t activeFactors) { + auto profileConfig = request.config; + if (request.hasFlow && (activeFactors & std::uint64_t{1}) == 0) { + for (size_t group = 0; group < request.isPt.size(); ++group) { + if (!request.isPt[group].front()) { + std::fill(profileConfig.Hars[group].begin(), profileConfig.Hars[group].end(), 0); + } + } + } + profileConfig.Head = makeFullTopologyName(request.config.Head, activeFactors, request.allFactors, request.hasFlow, request.ptOrder); + parsed.profiles.push_back({.config = std::move(profileConfig), + .isPt = request.isPt, + .factors = request.factors, + .activeFactors = activeFactors, + .usesFullTopology = true}); + }; + + addFullTopologyProfile(request.allFactors); + for (const auto& activeFactors : activeMasks) { + if (activeFactors != 0 && activeFactors != request.allFactors) { + addFullTopologyProfile(activeFactors); + } + } + + // Each distinct pT source has one naturally weighted mean. Explicit one-particle + // requests provide that mean directly; otherwise the first consumer books it. + for (int ptIndex = 0; ptIndex < request.ptOrder; ++ptIndex) { + const std::uint64_t factor = std::uint64_t{1} << (ptOffset + ptIndex); + if (factor == request.allFactors) { + parsed.meanProfiles[ptIndex] = request.config.Head; + continue; + } + auto existingMean = findNaturalMean(request, ptIndex); + if (existingMean != naturalMeans.end()) { + parsed.meanProfiles[ptIndex] = existingMean->profile; + continue; + } + + const auto group = request.ptTerms[ptIndex].first; + GFW::CorrConfig meanConfig; + meanConfig.Regs = {{getPtRegion(request, ptIndex)}}; + meanConfig.Hars = {{0}}; + meanConfig.Overlap = {-1}; + meanConfig.ptInd = {request.config.ptInd[group]}; + meanConfig.pTDif = request.config.pTDif; + meanConfig.Head = request.config.Head + "_mean_pt" + std::to_string(ptIndex + 1); + parsed.meanProfiles[ptIndex] = meanConfig.Head; + naturalMeans.push_back({.region = meanConfig.Regs.front().front(), + .ptIndex = meanConfig.ptInd.front(), + .pTDif = meanConfig.pTDif, + .profile = meanConfig.Head}); + parsed.profiles.push_back({.config = std::move(meanConfig), + .isPt = {{true}}, + .factors = {{ptOffset + ptIndex}}, + .activeFactors = factor, + .usesFullTopology = false}); + } + + if (!request.hasFlow) { + PtMomentConfig momentConfig{ + .head = request.config.Head, + .groups = {}}; + momentConfig.groups.reserve(request.isPt.size()); + for (size_t group = 0; group < request.isPt.size(); ++group) { + const int firstFactor = request.factors[group].front(); + const int region = request.config.Regs[group].front(); + momentConfig.groups.push_back({.region = regions.GetNames()[region], + .order = static_cast(request.factors[group].size()), + .meanProfile = parsed.meanProfiles[firstFactor]}); + } + momentConfigs.push_back(std::move(momentConfig)); + } + + parsedConfigs.push_back(std::move(parsed)); + } + + auto* profiles = new TList(); + profiles->SetOwner(kTRUE); + std::unordered_set profileNames; + for (const auto& parsed : parsedConfigs) { + for (const auto& profile : parsed.profiles) { + const auto& head = profile.config.Head; + if (!profileNames.insert(head).second) { + LOGF(error, "Cannot initialize %s: generated profile name %s is not unique", GetName(), head.c_str()); + delete profiles; + return; + } + if (isVariableBinning) { + profiles->Add(new BootstrapProfile(head.c_str(), head.c_str(), numberOfBins, multiBins.data())); + } else { + profiles->Add(new BootstrapProfile(head.c_str(), head.c_str(), numberOfBins, multiBins.front(), multiBins.back())); + } + } + } + if (nsub > 0) { + for (int i = 0; i < profiles->GetEntries(); ++i) { + dynamic_cast(profiles->At(i))->InitializeSubsamples(nsub); + } + } + + delete fCorrList; + fCorrList = profiles; + fPtRegions = regions; + fPtCorrConfigs = std::move(parsedConfigs); + fPtMomentConfigs = std::move(momentConfigs); + clearPtPostprocessLists(); + fPtRegionMoments.clear(); + fPtRegionMoments.reserve(regionMaxOrders.size()); + for (const int& order : regionMaxOrders) { + fPtRegionMoments.push_back({.maxOrder = order, + .values = std::vector((order + 1) * (order + 1), 0.)}); + } + mpar = maxPtOrder; + nSubevents = static_cast(numberOfRegions); + LOGF(info, "Container %s initialized with %zu pT correlations, %i profiles and %i subsamples", GetName(), numberOfConfigs, profiles->GetEntries(), nsub); +} +void FlowPtContainer::initialise(const o2::framework::AxisSpec& axis, const int& maxOrder, const GFWCorrConfigs& configs, const int& nsub) { arr.resize(3 * 3 * 3 * 3); warr.resize(3 * 3 * 3 * 3); - if (!mpar) - mpar = m; + if (mpar == 0) { + mpar = maxOrder; + } std::vector multiBins = axis.binEdges; int nMultiBins = axis.nBins.value_or(0); - if (nMultiBins <= 0) + if (nMultiBins <= 0) { nMultiBins = multiBins.size() - 1; + } if (nMultiBins <= 0) { LOGF(warning, "Multiplicity axis does not exist"); return; } - if (fCMTermList) - delete fCMTermList; + delete fCMTermList; fCMTermList = new TList(); fCMTermList->SetOwner(kTRUE); - if (fCorrList) - delete fCorrList; + delete fCorrList; fCorrList = new TList(); fCorrList->SetOwner(kTRUE); - if (fCovList) - delete fCovList; + delete fCovList; fCovList = new TList(); fCovList->SetOwner(kTRUE); for (int m = 0; m < mpar; ++m) { - fCorrList->Add(new BootstrapProfile(Form("mpt%i", m + 1), Form("mpt%i", m + 1), nMultiBins, &multiBins[0])); + fCorrList->Add(new BootstrapProfile(Form("mpt%i", m + 1), Form("mpt%i", m + 1), nMultiBins, multiBins.data())); } - for (int m = 0; m < 4; ++m) { + for (int m = 0; m < centralMomentMaxOrder; ++m) { for (int i = 0; i <= m; ++i) { - fCMTermList->Add(new BootstrapProfile(Form("cm%i_Mpt%i", m + 1, i), Form("cm%i_Mpt%i", m + 1, i), nMultiBins, &multiBins[0])); + fCMTermList->Add(new BootstrapProfile(Form("cm%i_Mpt%i", m + 1, i), Form("cm%i_Mpt%i", m + 1, i), nMultiBins, multiBins.data())); } } if (fUseGap) { @@ -172,86 +624,87 @@ void FlowPtContainer::initialise(const o2::framework::AxisSpec axis, const int& for (int i = 0; i < configs.GetSize(); ++i) { fCovFirstIndex[i] = obsIndex; for (auto m(1); m <= mpar; ++m) { - if (!(configs.GetpTCorrMasks()[i] & (1 << (m - 1)))) + if ((configs.GetpTCorrMasks()[i] & (1 << (m - 1))) == 0) { continue; + } if (fUseCentralMoments) { for (auto j = 0; j <= m; ++j) { - fCovList->Add(new BootstrapProfile(Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), nMultiBins, &multiBins[0])); + fCovList->Add(new BootstrapProfile(Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), nMultiBins, multiBins.data())); obsIndex++; } } else { - fCovList->Add(new BootstrapProfile(Form("%spt%i", configs.GetHeads()[i].c_str(), m), Form("%spt%i", configs.GetHeads()[i].c_str(), m), nMultiBins, &multiBins[0])); + fCovList->Add(new BootstrapProfile(Form("%spt%i", configs.GetHeads()[i].c_str(), m), Form("%spt%i", configs.GetHeads()[i].c_str(), m), nMultiBins, multiBins.data())); obsIndex++; } } } } else { if (fUseCentralMoments) { - fCovList->Add(new BootstrapProfile("ChFull24pt2_Mpt0", "ChFull24pt2_Mpt0", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull24pt2_Mpt1", "ChFull24pt2_Mpt1", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull24pt2_Mpt2", "ChFull24pt2_Mpt2", nMultiBins, &multiBins[0])); - - fCovList->Add(new BootstrapProfile("ChFull24pt1_Mpt0", "ChFull24pt1_Mpt0", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull24pt1_Mpt1", "ChFull24pt1_Mpt1", nMultiBins, &multiBins[0])); - - fCovList->Add(new BootstrapProfile("ChFull22pt2_Mpt0", "ChFull22pt2_Mpt0", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt2_Mpt1", "ChFull22pt2_Mpt1", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt2_Mpt2", "ChFull22pt2_Mpt2", nMultiBins, &multiBins[0])); - - fCovList->Add(new BootstrapProfile("ChFull22pt1_Mpt0", "ChFull22pt1_Mpt0", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt1_Mpt1", "ChFull22pt1_Mpt1", nMultiBins, &multiBins[0])); - - fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt0", "ChFull22pt3_Mpt0", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt1", "ChFull22pt3_Mpt1", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt2", "ChFull22pt3_Mpt2", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt3", "ChFull22pt3_Mpt3", nMultiBins, &multiBins[0])); - - fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt0", "ChFull22pt4_Mpt0", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt1", "ChFull22pt4_Mpt1", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt2", "ChFull22pt4_Mpt2", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt3", "ChFull22pt4_Mpt3", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt4", "ChFull22pt4_Mpt4", nMultiBins, &multiBins[0])); + fCovList->Add(new BootstrapProfile("ChFull24pt2_Mpt0", "ChFull24pt2_Mpt0", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull24pt2_Mpt1", "ChFull24pt2_Mpt1", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull24pt2_Mpt2", "ChFull24pt2_Mpt2", nMultiBins, multiBins.data())); + + fCovList->Add(new BootstrapProfile("ChFull24pt1_Mpt0", "ChFull24pt1_Mpt0", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull24pt1_Mpt1", "ChFull24pt1_Mpt1", nMultiBins, multiBins.data())); + + fCovList->Add(new BootstrapProfile("ChFull22pt2_Mpt0", "ChFull22pt2_Mpt0", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt2_Mpt1", "ChFull22pt2_Mpt1", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt2_Mpt2", "ChFull22pt2_Mpt2", nMultiBins, multiBins.data())); + + fCovList->Add(new BootstrapProfile("ChFull22pt1_Mpt0", "ChFull22pt1_Mpt0", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt1_Mpt1", "ChFull22pt1_Mpt1", nMultiBins, multiBins.data())); + + fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt0", "ChFull22pt3_Mpt0", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt1", "ChFull22pt3_Mpt1", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt2", "ChFull22pt3_Mpt2", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt3_Mpt3", "ChFull22pt3_Mpt3", nMultiBins, multiBins.data())); + + fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt0", "ChFull22pt4_Mpt0", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt1", "ChFull22pt4_Mpt1", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt2", "ChFull22pt4_Mpt2", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt3", "ChFull22pt4_Mpt3", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt4_Mpt4", "ChFull22pt4_Mpt4", nMultiBins, multiBins.data())); } else { - fCovList->Add(new BootstrapProfile("ChFull24pt2", "ChFull24pt2", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull24pt1", "ChFull24pt1", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt2", "ChFull22pt2", nMultiBins, &multiBins[0])); - fCovList->Add(new BootstrapProfile("ChFull22pt1", "ChFull22pt1", nMultiBins, &multiBins[0])); + fCovList->Add(new BootstrapProfile("ChFull24pt2", "ChFull24pt2", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull24pt1", "ChFull24pt1", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt2", "ChFull22pt2", nMultiBins, multiBins.data())); + fCovList->Add(new BootstrapProfile("ChFull22pt1", "ChFull22pt1", nMultiBins, multiBins.data())); } } - if (nsub) { - for (int i = 0; i < fCorrList->GetEntries(); ++i) + if (nsub != 0) { + for (int i = 0; i < fCorrList->GetEntries(); ++i) { dynamic_cast(fCorrList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fCMTermList->GetEntries(); ++i) + } + for (int i = 0; i < fCMTermList->GetEntries(); ++i) { dynamic_cast(fCMTermList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fCovList->GetEntries(); ++i) + } + for (int i = 0; i < fCovList->GetEntries(); ++i) { dynamic_cast(fCovList->At(i))->InitializeSubsamples(nsub); + } } LOGF(info, "Container %s initialized with m = %i\n and %i subsamples", this->GetName(), mpar, nsub); - return; }; -void FlowPtContainer::initialise(int nbinsx, double* xbins, const int& m, const GFWCorrConfigs& configs, const int& nsub) +void FlowPtContainer::initialise(int nbinsx, double* xbins, const int& maxOrder, const GFWCorrConfigs& configs, const int& nsub) { arr.resize(3 * 3 * 5 * 5); warr.resize(3 * 3 * 5 * 5); - if (!mpar) - mpar = m; - if (fCMTermList) - delete fCMTermList; + if (mpar == 0) { + mpar = maxOrder; + } + delete fCMTermList; fCMTermList = new TList(); fCMTermList->SetOwner(kTRUE); - if (fCorrList) - delete fCorrList; + delete fCorrList; fCorrList = new TList(); fCorrList->SetOwner(kTRUE); - if (fCovList) - delete fCovList; + delete fCovList; fCovList = new TList(); fCovList->SetOwner(kTRUE); for (int m = 0; m < mpar; ++m) { fCorrList->Add(new BootstrapProfile(Form("mpt%i", m + 1), Form("mpt%i", m + 1), nbinsx, xbins)); } - for (int m = 0; m < 4; ++m) { + for (int m = 0; m < centralMomentMaxOrder; ++m) { for (int i = 0; i <= m; ++i) { fCMTermList->Add(new BootstrapProfile(Form("cm%i_Mpt%i", m + 1, i), Form("cm%i_Mpt%i", m + 1, i), nbinsx, xbins)); } @@ -262,8 +715,9 @@ void FlowPtContainer::initialise(int nbinsx, double* xbins, const int& m, const for (int i = 0; i < configs.GetSize(); ++i) { fCovFirstIndex[i] = obsIndex; for (auto m(1); m <= mpar; ++m) { - if (!(configs.GetpTCorrMasks()[i] & (1 << (m - 1)))) + if ((configs.GetpTCorrMasks()[i] & (1 << (m - 1))) == 0) { continue; + } if (fUseCentralMoments) { for (auto j = 0; j <= m; ++j) { fCovList->Add(new BootstrapProfile(Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), nbinsx, xbins)); @@ -308,38 +762,39 @@ void FlowPtContainer::initialise(int nbinsx, double* xbins, const int& m, const fCovList->Add(new BootstrapProfile("ChFull22pt1", "ChFull22pt1", nbinsx, xbins)); } } - if (nsub) { - for (int i = 0; i < fCorrList->GetEntries(); ++i) + if (nsub != 0) { + for (int i = 0; i < fCorrList->GetEntries(); ++i) { dynamic_cast(fCorrList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fCMTermList->GetEntries(); ++i) + } + for (int i = 0; i < fCMTermList->GetEntries(); ++i) { dynamic_cast(fCMTermList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fCovList->GetEntries(); ++i) + } + for (int i = 0; i < fCovList->GetEntries(); ++i) { dynamic_cast(fCovList->At(i))->InitializeSubsamples(nsub); + } } LOGF(info, "Container %s initialized with m = %i\n", this->GetName(), mpar); }; -void FlowPtContainer::initialise(int nbinsx, double xlow, double xhigh, const int& m, const GFWCorrConfigs& configs, const int& nsub) +void FlowPtContainer::initialise(int nbinsx, double xlow, double xhigh, const int& maxOrder, const GFWCorrConfigs& configs, const int& nsub) { arr.resize(3 * 3 * 5 * 5); warr.resize(3 * 3 * 5 * 5); - if (!mpar) - mpar = m; - if (fCMTermList) - delete fCMTermList; + if (mpar == 0) { + mpar = maxOrder; + } + delete fCMTermList; fCMTermList = new TList(); fCMTermList->SetOwner(kTRUE); - if (fCorrList) - delete fCorrList; + delete fCorrList; fCorrList = new TList(); fCorrList->SetOwner(kTRUE); - if (fCovList) - delete fCovList; + delete fCovList; fCovList = new TList(); fCovList->SetOwner(kTRUE); for (int m = 0; m < mpar; ++m) { fCorrList->Add(new BootstrapProfile(Form("mpt%i", m + 1), Form("mpt%i", m + 1), nbinsx, xlow, xhigh)); } - for (int m = 0; m < 4; ++m) { + for (int m = 0; m < centralMomentMaxOrder; ++m) { for (int i = 0; i <= m; ++i) { fCMTermList->Add(new BootstrapProfile(Form("cm%i_Mpt%i", m + 1, i), Form("cm%i_Mpt%i", m + 1, i), nbinsx, xlow, xhigh)); } @@ -350,8 +805,9 @@ void FlowPtContainer::initialise(int nbinsx, double xlow, double xhigh, const in for (int i = 0; i < configs.GetSize(); ++i) { fCovFirstIndex[i] = obsIndex; for (auto m(1); m <= mpar; ++m) { - if (!(configs.GetpTCorrMasks()[i] & (1 << (m - 1)))) + if ((configs.GetpTCorrMasks()[i] & (1 << (m - 1))) == 0) { continue; + } if (fUseCentralMoments) { for (auto j = 0; j <= m; ++j) { fCovList->Add(new BootstrapProfile(Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), Form("%spt%i_Mpt%i", configs.GetHeads()[i].c_str(), m, j), nbinsx, xlow, xhigh)); @@ -396,45 +852,49 @@ void FlowPtContainer::initialise(int nbinsx, double xlow, double xhigh, const in fCovList->Add(new BootstrapProfile("ChFull22pt1", "ChFull22pt1", nbinsx, xlow, xhigh)); } } - if (nsub) { - for (int i = 0; i < fCorrList->GetEntries(); ++i) + if (nsub != 0) { + for (int i = 0; i < fCorrList->GetEntries(); ++i) { dynamic_cast(fCorrList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fCMTermList->GetEntries(); ++i) + } + for (int i = 0; i < fCMTermList->GetEntries(); ++i) { dynamic_cast(fCMTermList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fCovList->GetEntries(); ++i) + } + for (int i = 0; i < fCovList->GetEntries(); ++i) { dynamic_cast(fCovList->At(i))->InitializeSubsamples(nsub); + } } LOGF(info, "Container %s initialized with m = %i\n", this->GetName(), mpar); }; -void FlowPtContainer::initialiseSubevent(const o2::framework::AxisSpec axis, const int& m, const int& nsubev, const int& nsub) +void FlowPtContainer::initialiseSubevent(const o2::framework::AxisSpec& axis, const int& maxOrder, const int& nsubev, const int& nsub) { if (nsubev < 1) { LOGF(fatal, "Need at least one subevent"); return; } nSubevents = nsubev; - if (!mpar) - mpar = m; + if (mpar == 0) { + mpar = maxOrder; + } std::vector multiBins = axis.binEdges; int nMultiBins = axis.nBins.value_or(0); - if (nMultiBins <= 0) + if (nMultiBins <= 0) { nMultiBins = multiBins.size() - 1; + } if (nMultiBins <= 0) { LOGF(warning, "Multiplicity axis does not exist"); return; } - - if (fSubList) - delete fSubList; + delete fSubList; fSubList = new TList(); fSubList->SetOwner(kTRUE); // Get all possible subevent combinations given m particles and nsubev subevents - also considering not using all m particles, e.g. all lower orders std::vector current; - getSubevents(m, nsubev + 1, current, subevents); + getSubevents(mpar, nsubev + 1, current, subevents); // remove unused "extra" subevent - for (auto& subevent : subevents) + for (auto& subevent : subevents) { // o2-linter: disable=const-ref-in-for-loop (modified through pop_back()) subevent.pop_back(); + } subevents.erase(subevents.begin(), subevents.begin() + 1); std::vector histnames; @@ -448,64 +908,68 @@ void FlowPtContainer::initialiseSubevent(const o2::framework::AxisSpec axis, con } histnames.push_back(outstr); } - for (const auto& name : histnames) - fSubList->Add(new BootstrapProfile(name.c_str(), this->GetTitle(), nMultiBins, &multiBins[0])); - - if (fSubCMList) - delete fSubCMList; + for (const auto& name : histnames) { + fSubList->Add(new BootstrapProfile(name.c_str(), this->GetTitle(), nMultiBins, multiBins.data())); + } + delete fSubCMList; fSubCMList = new TList(); fSubCMList->SetOwner(kTRUE); - for (int subEv = 0; subEv < 2; ++subEv) { - for (int m = 0; m < 4; ++m) { + const int maxSubEv = 2; + for (int subEv = 0; subEv < maxSubEv; ++subEv) { + for (int m = 0; m < centralMomentMaxOrder; ++m) { for (int i = 0; i <= m; ++i) { - fSubCMList->Add(new BootstrapProfile(Form("cm%i_sub%i_Mpt%i", m + 1, subEv + 1, i), this->GetTitle(), nMultiBins, &multiBins[0])); + fSubCMList->Add(new BootstrapProfile(Form("cm%i_sub%i_Mpt%i", m + 1, subEv + 1, i), this->GetTitle(), nMultiBins, multiBins.data())); } } } - for (int m = 2; m <= 4; ++m) { + for (int m = 2; m <= centralMomentMaxOrder; ++m) { for (int first = 1; first < m; ++first) { for (int second = first; second < m; ++second) { - if (first > second) + if (first > second) { continue; + } int fourth = m - second; for (int third = 1; third < m; ++third) { - if (third > fourth) + if (third > fourth) { continue; - fSubCMList->Add(new BootstrapProfile(Form("cm%i_%i%isub1_%i%isub2", m, first, second, third, fourth), this->GetTitle(), nMultiBins, &multiBins[0])); + } + fSubCMList->Add(new BootstrapProfile(Form("cm%i_%i%isub1_%i%isub2", m, first, second, third, fourth), this->GetTitle(), nMultiBins, multiBins.data())); } } } } - if (nsub) { - for (int i = 0; i < fSubList->GetEntries(); ++i) + if (nsub != 0) { + for (int i = 0; i < fSubList->GetEntries(); ++i) { dynamic_cast(fSubList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fSubCMList->GetEntries(); ++i) + } + for (int i = 0; i < fSubCMList->GetEntries(); ++i) { dynamic_cast(fSubCMList->At(i))->InitializeSubsamples(nsub); + } } LOGF(info, "Container %s initialized Subevents and %i subsamples", this->GetName(), nsub); } -void FlowPtContainer::initialiseSubevent(int nbinsx, double* xbins, const int& m, const int& nsubev, const int& nsub) +void FlowPtContainer::initialiseSubevent(int nbinsx, double* xbins, const int& maxOrder, const int& nsubev, const int& nsub) { if (nsubev < 1) { LOGF(fatal, "Need at least one subevent"); return; } nSubevents = nsubev; - if (!mpar) - mpar = m; - - if (fSubList) - delete fSubList; + if (mpar == 0) { + mpar = maxOrder; + } + delete fSubList; fSubList = new TList(); fSubList->SetOwner(kTRUE); // Get all possible subevent combinations given m particles and nsubev subevents - also considering not using all m particles, e.g. all lower orders std::vector current; - getSubevents(m, nsubev + 1, current, subevents); + getSubevents(mpar, nsubev + 1, current, subevents); // remove unused "extra" subevent - for (auto& subevent : subevents) + for (auto& subevent : subevents) { // o2-linter: disable=const-ref-in-for-loop (modified through pop_back()) subevent.pop_back(); + } subevents.erase(subevents.begin(), subevents.begin() + 1); std::vector histnames; @@ -519,54 +983,58 @@ void FlowPtContainer::initialiseSubevent(int nbinsx, double* xbins, const int& m } histnames.push_back(outstr); } - for (const auto& name : histnames) + for (const auto& name : histnames) { fSubList->Add(new BootstrapProfile(name.c_str(), this->GetTitle(), nbinsx, xbins)); - - if (fSubCMList) - delete fSubCMList; + } + delete fSubCMList; fSubCMList = new TList(); fSubCMList->SetOwner(kTRUE); - for (int subEv = 0; subEv < 2; ++subEv) { - for (int m = 0; m < 4; ++m) { + const int maxSubEv = 2; + for (int subEv = 0; subEv < maxSubEv; ++subEv) { + for (int m = 0; m < centralMomentMaxOrder; ++m) { for (int i = 0; i <= m; ++i) { fSubCMList->Add(new BootstrapProfile(Form("cm%i_sub%i_Mpt%i", m + 1, subEv + 1, i), this->GetTitle(), nbinsx, xbins)); } } } - for (int m = 2; m <= 4; ++m) { + for (int m = 2; m <= centralMomentMaxOrder; ++m) { for (int first = 1; first < m; ++first) { for (int second = first; second < m; ++second) { - if (first > second) + if (first > second) { continue; + } int fourth = m - second; for (int third = 1; third < m; ++third) { - if (third > fourth) + if (third > fourth) { continue; + } fSubCMList->Add(new BootstrapProfile(Form("cm%i_%i%isub1_%i%isub2", m, first, second, third, fourth), this->GetTitle(), nbinsx, xbins)); } } } } - if (nsub) { - for (int i = 0; i < fSubList->GetEntries(); ++i) + if (nsub != 0) { + for (int i = 0; i < fSubList->GetEntries(); ++i) { dynamic_cast(fSubList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fSubCMList->GetEntries(); ++i) + } + for (int i = 0; i < fSubCMList->GetEntries(); ++i) { dynamic_cast(fSubCMList->At(i))->InitializeSubsamples(nsub); + } } LOGF(info, "Container %s initialized Subevents and %i subsamples", this->GetName(), nsub); } -void FlowPtContainer::initialiseSubevent(int nbinsx, double xlow, double xhigh, const int& m, const int& nsubev, const int& nsub) +void FlowPtContainer::initialiseSubevent(int nbinsx, double xlow, double xhigh, const int& maxOrder, const int& nsubev, const int& nsub) { if (nsubev < 1) { LOGF(fatal, "Need at least one subevent"); return; } nSubevents = nsubev; - if (!mpar) - mpar = m; - if (fSubList) - delete fSubList; + if (mpar == 0) { + mpar = maxOrder; + } + delete fSubList; fSubList = new TList(); fSubList->SetOwner(kTRUE); @@ -574,8 +1042,9 @@ void FlowPtContainer::initialiseSubevent(int nbinsx, double xlow, double xhigh, std::vector current; getSubevents(mpar, nsubev + 1, current, subevents); // remove unused "extra" subevent - for (auto& subevent : subevents) + for (auto& subevent : subevents) { // o2-linter: disable=const-ref-in-for-loop (modified through pop_back()) subevent.pop_back(); + } subevents.erase(subevents.begin(), subevents.begin() + 1); std::vector histnames; @@ -589,39 +1058,43 @@ void FlowPtContainer::initialiseSubevent(int nbinsx, double xlow, double xhigh, } histnames.push_back(outstr); } - for (const auto& name : histnames) + for (const auto& name : histnames) { fSubList->Add(new BootstrapProfile(name.c_str(), this->GetTitle(), nbinsx, xlow, xhigh)); - - if (fSubCMList) - delete fSubCMList; + } + delete fSubCMList; fSubCMList = new TList(); fSubCMList->SetOwner(kTRUE); - for (int subEv = 0; subEv < 2; ++subEv) { - for (int m = 0; m < 4; ++m) { + const int maxSubEv = 2; + for (int subEv = 0; subEv < maxSubEv; ++subEv) { + for (int m = 0; m < centralMomentMaxOrder; ++m) { for (int i = 0; i <= m; ++i) { fSubCMList->Add(new BootstrapProfile(Form("cm%i_sub%i_Mpt%i", m + 1, subEv + 1, i), this->GetTitle(), nbinsx, xlow, xhigh)); } } } - for (int m = 2; m <= 4; ++m) { + for (int m = 2; m <= centralMomentMaxOrder; ++m) { for (int first = 1; first < m; ++first) { for (int second = first; second < m; ++second) { - if (first > second) + if (first > second) { continue; + } int fourth = m - second; for (int third = 1; third < m; ++third) { - if (third > fourth) + if (third > fourth) { continue; + } fSubCMList->Add(new BootstrapProfile(Form("cm%i_%i%isub1_%i%isub2", m, first, second, third, fourth), this->GetTitle(), nbinsx, xlow, xhigh)); } } } } - if (nsub) { - for (int i = 0; i < fSubList->GetEntries(); ++i) + if (nsub != 0) { + for (int i = 0; i < fSubList->GetEntries(); ++i) { dynamic_cast(fSubList->At(i))->InitializeSubsamples(nsub); - for (int i = 0; i < fSubCMList->GetEntries(); ++i) + } + for (int i = 0; i < fSubCMList->GetEntries(); ++i) { dynamic_cast(fSubCMList->At(i))->InitializeSubsamples(nsub); + } } LOGF(info, "Container %s initialized Subevents and %i subsamples", this->GetName(), nsub); } @@ -630,7 +1103,94 @@ void FlowPtContainer::fill(const double& w, const double& pt) for (size_t i = 0; i < sumP.size(); ++i) { sumP[i] += std::pow(w, i % (mpar + 1)) * std::pow(pt, i / (mpar + 1)); } - return; +} +void FlowPtContainer::fill(const double& eta, const double& w, const double& pt, int mask) +{ + int maxOrder = 0; + for (const auto& moments : fPtRegionMoments) { + maxOrder = std::max(maxOrder, moments.maxOrder); + } + if (maxOrder == 0) { + return; + } + + std::vector weightPowers(maxOrder + 1, 1.); + std::vector ptPowers(maxOrder + 1, 1.); + for (int power = 1; power <= maxOrder; ++power) { + weightPowers[power] = weightPowers[power - 1] * w; + ptPowers[power] = ptPowers[power - 1] * pt; + } + + for (size_t region = 0; region < fPtRegionMoments.size(); ++region) { + auto& moments = fPtRegionMoments[region]; + const bool inEtaRange = fPtRegions.GetEtaMin()[region] < eta && eta < fPtRegions.GetEtaMax()[region]; + const bool maskMatches = (fPtRegions.GetBitmasks()[region] & mask) != 0; + if (moments.maxOrder == 0 || !inEtaRange || !maskMatches) { + continue; + } + for (int weightPower = 1; weightPower <= moments.maxOrder; ++weightPower) { + for (int ptPower = 0; ptPower <= weightPower; ++ptPower) { + moments.values[weightPower * (moments.maxOrder + 1) + ptPower] += weightPowers[weightPower] * ptPowers[ptPower]; + } + } + } +} +void FlowPtContainer::clearPtRegionMoments() +{ + for (auto& moments : fPtRegionMoments) { // o2-linter: disable=const-ref-in-for-loop (contents are reset) + std::fill(moments.values.begin(), moments.values.end(), 0.); + } +} +void FlowPtContainer::clearPtPostprocessLists() +{ + delete fPtCentralMomentList; + fPtCentralMomentList = nullptr; + delete fPtCumulantList; + fPtCumulantList = nullptr; +} +double FlowPtContainer::getPtMoment(int region, int weightPower, int ptPower) const +{ + if (region < 0 || region >= static_cast(fPtRegionMoments.size())) { + return 0.; + } + const auto& moments = fPtRegionMoments[region]; + if (weightPower < 1 || weightPower > moments.maxOrder || ptPower < 0 || ptPower > weightPower) { + return 0.; + } + return moments.values[weightPower * (moments.maxOrder + 1) + ptPower]; +} +double FlowPtContainer::recursivePtCorrelation(int poi, int ref, int overlap, std::vector& weightPowers, std::vector& ptPowers) const +{ + if (weightPowers.empty() || weightPowers.size() != ptPowers.size()) { + return 0.; + } + if (weightPowers.front() != 1 && overlap >= 0) { + poi = overlap; + } + if (weightPowers.size() == 1) { + return getPtMoment(poi, weightPowers.front(), ptPowers.front()); + } + if (weightPowers.size() == TwoParticleOrder) { + const double product = getPtMoment(poi, weightPowers[0], ptPowers[0]) * getPtMoment(ref, weightPowers[1], ptPowers[1]); + const double selfCorrelation = overlap >= 0 ? getPtMoment(overlap, weightPowers[0] + weightPowers[1], ptPowers[0] + ptPowers[1]) : 0.; + return product - selfCorrelation; + } + + const int lastWeightPower = weightPowers.back(); + const int lastPtPower = ptPowers.back(); + weightPowers.pop_back(); + ptPowers.pop_back(); + double result = recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers) * getPtMoment(ref, lastWeightPower, lastPtPower); + for (int term = static_cast(weightPowers.size()) - 1; term >= 0; --term) { + weightPowers[term] += lastWeightPower; + ptPowers[term] += lastPtPower; + result -= recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers); + weightPowers[term] -= lastWeightPower; + ptPowers[term] -= lastPtPower; + } + weightPowers.push_back(lastWeightPower); + ptPowers.push_back(lastPtPower); + return result; } void FlowPtContainer::fillSub(const double& w, const double& pt, int subIndex) { @@ -663,17 +1223,18 @@ void FlowPtContainer::calculateCorrelations() corrNum[m] = sumNum; corrDen[m] = sumDenum; } - return; } void FlowPtContainer::calculateSubeventCorrelations() { corrNumSub.clear(); corrNumSub.resize(nSubevents, std::vector(mpar + 1, 0)); - for (auto& corrnum : corrNumSub) + for (auto& corrnum : corrNumSub) { // o2-linter: disable=const-ref-in-for-loop (assigned a value) corrnum[0] = 1.0; + } corrDenSub.resize(nSubevents, std::vector(mpar + 1, 0)); - for (auto& corrden : corrDenSub) + for (auto& corrden : corrDenSub) { // o2-linter: disable=const-ref-in-for-loop (assigned a value) corrden[0] = 1.0; + } for (int subIndex = 0; subIndex < nSubevents; ++subIndex) { double sumNum = 0.0; @@ -693,7 +1254,6 @@ void FlowPtContainer::calculateSubeventCorrelations() corrDenSub[subIndex][m] = sumDenum; } } - return; } void FlowPtContainer::fillPtProfiles(const double& centmult, const double& rn) { @@ -702,7 +1262,96 @@ void FlowPtContainer::fillPtProfiles(const double& centmult, const double& rn) dynamic_cast(fCorrList->At(m - 1))->FillProfile(centmult, corrNum[m] / corrDen[m], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : corrDen[m], rn); } } - return; +} +void FlowPtContainer::fillPtProfiles(GFW& gfw, const double& centmult, const double& rn) +{ + if (!fCorrList) { + return; + } + clearPtPostprocessLists(); + for (const auto& correlation : fPtCorrConfigs) { + for (const auto& profileConfig : correlation.profiles) { + std::complex numerator{1., 0.}; + double denominator = 1.; + for (size_t group = 0; group < profileConfig.config.Regs.size(); ++group) { + if (profileConfig.isPt[group].front()) { + const int poi = profileConfig.config.Regs[group].front(); + const int ref = profileConfig.config.Regs[group].size() > 1 ? profileConfig.config.Regs[group][1] : poi; + int overlap = profileConfig.config.Overlap[group]; + if (overlap < 0 && poi == ref) { + overlap = ref; + } + std::vector weightPowers(profileConfig.isPt[group].size(), 1); + std::vector ptPowers; + ptPowers.reserve(profileConfig.isPt[group].size()); + for (const int& factor : profileConfig.factors[group]) { + ptPowers.push_back((profileConfig.activeFactors & (std::uint64_t{1} << factor)) != 0 ? 1 : 0); + } + auto denominatorPtPowers = std::vector(ptPowers.size(), 0); + denominator *= recursivePtCorrelation(poi, ref, overlap, weightPowers, denominatorPtPowers); + numerator *= recursivePtCorrelation(poi, ref, overlap, weightPowers, ptPowers); + } else { + GFW::CorrConfig groupConfig; + groupConfig.Regs = {profileConfig.config.Regs[group]}; + groupConfig.Hars = {profileConfig.config.Hars[group]}; + groupConfig.Overlap = {profileConfig.config.Overlap[group]}; + groupConfig.ptInd = {profileConfig.config.ptInd[group]}; + groupConfig.pTDif = profileConfig.config.pTDif; + numerator *= gfw.Calculate(groupConfig, 0, false); + denominator *= gfw.Calculate(groupConfig, 0, true).real(); + } + } + if (!std::isfinite(numerator.real()) || !std::isfinite(numerator.imag()) || !std::isfinite(denominator) || denominator <= 0.) { + continue; + } + auto* profile = dynamic_cast(fCorrList->FindObject(profileConfig.config.Head.c_str())); + if (!profile) { + LOGF(error, "pT profile %s has not been booked", profileConfig.config.Head.c_str()); + continue; + } + const double eventWeight = fEventWeight == EventWeight::UnityWeight ? 1. : denominator; + profile->FillProfile(centmult, numerator.real() / denominator, eventWeight, rn); + } + } +} +bool FlowPtContainer::addPtProfile(const char* name, int observableOrder) +{ + if (!fCorrList || !name || (name[0] == 0) || observableOrder < 1 || observableOrder > mpar) { + LOGF(error, "Cannot add pT profile %s for order %d", name ? name : "(null)", observableOrder); + return false; + } + const std::string profileName{name}; + if (fCorrList->FindObject(profileName.c_str())) { + LOGF(error, "pT profile %s already exists", profileName.c_str()); + return false; + } + auto* original = dynamic_cast(fCorrList->At(observableOrder - 1)); + const auto* axis = original->GetXaxis(); + BootstrapProfile* profile = nullptr; + if (axis->GetXbins()->GetSize() != 0) { + profile = new BootstrapProfile(profileName.c_str(), profileName.c_str(), axis->GetNbins(), axis->GetXbins()->GetArray()); + } else { + profile = new BootstrapProfile(profileName.c_str(), profileName.c_str(), axis->GetNbins(), axis->GetXmin(), axis->GetXmax()); + } + if (original->fListOfEntries) { + profile->InitializeSubsamples(original->fListOfEntries->GetEntries()); + } + fCorrList->Add(profile); + return true; +} +bool FlowPtContainer::fillPtProfile(const char* name, int observableOrder, double mult, double eventWeight, double rn) +{ + if (!fCorrList || !name || observableOrder < 1 || observableOrder > mpar || + static_cast(observableOrder) >= corrDen.size() || corrDen[observableOrder] == 0. || eventWeight == 0.) { + return false; + } + auto* profile = dynamic_cast(fCorrList->FindObject(name)); + if (!profile) { + LOGF(error, "pT profile %s has not been booked", name); + return false; + } + profile->FillProfile(mult, corrNum[observableOrder] / corrDen[observableOrder], eventWeight, rn); + return true; } void FlowPtContainer::fillSubeventPtProfiles(const double& centmult, const double& rn) { @@ -716,10 +1365,10 @@ void FlowPtContainer::fillSubeventPtProfiles(const double& centmult, const doubl if (corrDenSub[subIndex][m] == 0) { valid = false; break; - } else { - val *= corrNumSub[subIndex][m] / corrDenSub[subIndex][m]; - dn *= corrDenSub[subIndex][m]; } + val *= corrNumSub[subIndex][m] / corrDenSub[subIndex][m]; + dn *= corrDenSub[subIndex][m]; + ++subIndex; } if (valid) { @@ -727,15 +1376,14 @@ void FlowPtContainer::fillSubeventPtProfiles(const double& centmult, const doubl } ++histCounter; } - return; } void FlowPtContainer::fillVnPtCorrProfiles(const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask) { - if (!mask) { + if (mask == 0u) { return; } for (auto m(1); m <= mpar; ++m) { - if (!(mask & (1 << (m - 1)))) { + if ((mask & (1 << (m - 1))) == 0) { continue; } if (corrDen[m] != 0) { @@ -743,16 +1391,16 @@ void FlowPtContainer::fillVnPtCorrProfiles(const double& centmult, const double& } ++fillCounter; } - return; } void FlowPtContainer::fillVnDeltaPtProfiles(const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask) { - if (!mask) { + if (mask == 0u) { return; } for (auto m(1); m <= mpar; ++m) { - if (!(mask & (1 << (m - 1)))) + if ((mask & (1 << (m - 1))) == 0) { continue; + } for (auto i = 0; i <= m; ++i) { if (cmDen[m] != 0) { dynamic_cast(fCovList->At(fillCounter))->FillProfile(centmult, flowval * ((i == m) ? cmVal[0] : cmVal[m * (m - 1) / 2 + i + 1]), (fEventWeight == UnityWeight) ? 1.0 : flowtuples * cmDen[m], rn); @@ -760,16 +1408,15 @@ void FlowPtContainer::fillVnDeltaPtProfiles(const double& centmult, const double ++fillCounter; } } - return; } void FlowPtContainer::fillVnPtCorrProfiles(const int configIndex, const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask) { - if (!mask) { + if (mask == 0u) { return; } int startIndex = fCovFirstIndex[configIndex]; for (auto m(1); m <= mpar; ++m) { - if (!(mask & (1 << (m - 1)))) { + if ((mask & (1 << (m - 1))) == 0) { continue; } if (corrDen[m] != 0) { @@ -777,110 +1424,150 @@ void FlowPtContainer::fillVnPtCorrProfiles(const int configIndex, const double& } ++startIndex; } - return; } void FlowPtContainer::fillVnDeltaPtProfiles(const int configIndex, const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask) { - if (!mask) { + if (mask == 0u) { return; } - int startIndex = fCovFirstIndex[configIndex]; + + if (configIndex < 0 || static_cast(configIndex) >= fCovFirstIndex.size()) { + LOGF(error, "Invalid configuration index %d", configIndex); + return; + } + int profileIndex = fCovFirstIndex[configIndex]; for (auto m(1); m <= mpar; ++m) { - if (!(mask & (1 << (m - 1)))) + if ((mask & (1 << (m - 1))) == 0) { continue; - for (auto i = 0; i <= m; ++i) { - if (cmDen[m] != 0) { - dynamic_cast(fCovList->At(startIndex))->FillProfile(centmult, flowval * ((i == m) ? cmVal[0] : cmVal[m * (m - 1) / 2 + i + 1]), (fEventWeight == UnityWeight) ? 1.0 : flowtuples * cmDen[m], rn); + } + for (auto i = 0; i <= m; ++i, ++profileIndex) { + const size_t cmIndex = (i == m) ? 0u : static_cast(m * (m - 1) / 2 + i + 1); + + if (static_cast(m) >= cmDen.size() || cmIndex >= cmVal.size() || cmDen[m] == 0.) { + continue; + } + + if (!fCovList || profileIndex >= fCovList->GetEntries()) { + LOGF(error, "Profile index %d out of range (entries=%d, config=%d, m=%d)", profileIndex, fCovList ? fCovList->GetEntries() : 0, configIndex, m); + continue; } - ++startIndex; + + auto* profile = dynamic_cast(fCovList->At(profileIndex)); + + if (!profile) { + LOGF(error, "Missing BootstrapProfile at index %d for config %d", profileIndex, configIndex); + continue; + } + profile->FillProfile(centmult, flowval * cmVal[cmIndex], (fEventWeight == UnityWeight) ? 1.0 : flowtuples * cmDen[m], rn); } } - return; } void FlowPtContainer::fillVnPtCorrStdProfiles(const double& centmult, const double& rn) { double wAABBCC = getStdAABBCC(warr); - if (wAABBCC != 0) + if (wAABBCC != 0) { dynamic_cast(fCovList->At(0))->FillProfile(centmult, getStdAABBCC(arr) / wAABBCC, (fEventWeight == UnityWeight) ? 1.0 : wAABBCC, rn); + } double wAABBC = getStdAABBC(warr); - if (wAABBC != 0) + if (wAABBC != 0) { dynamic_cast(fCovList->At(1))->FillProfile(centmult, getStdAABBCC(arr) / wAABBC, (fEventWeight == UnityWeight) ? 1.0 : wAABBC, rn); + } double wABCC = getStdAABBC(warr); - if (wABCC != 0) + if (wABCC != 0) { dynamic_cast(fCovList->At(2))->FillProfile(centmult, getStdABCC(arr) / wABCC, (fEventWeight == UnityWeight) ? 1.0 : wABCC, rn); + } double wABC = getStdABC(warr); - if (wABC != 0) + if (wABC != 0) { dynamic_cast(fCovList->At(3))->FillProfile(centmult, getStdABC(arr) / wABC, (fEventWeight == UnityWeight) ? 1.0 : wABC, rn); - return; + } } void FlowPtContainer::fillVnDeltaPtStdProfiles(const double& centmult, const double& rn) { double wAABBCC = getStdAABBCC(warr); - if (wAABBCC != 0) + if (wAABBCC != 0) { dynamic_cast(fCovList->At(0))->FillProfile(centmult, getStdAABBCC(arr) / wAABBCC, (fEventWeight == UnityWeight) ? 1.0 : wAABBCC, rn); + } double wAABBCD = getStdAABBCD(warr); - if (wAABBCD != 0) + if (wAABBCD != 0) { dynamic_cast(fCovList->At(1))->FillProfile(centmult, getStdAABBCD(arr) / wAABBCD, (fEventWeight == UnityWeight) ? 1.0 : wAABBCD, rn); + } double wAABBDD = getStdAABBDD(warr); - if (wAABBDD != 0) + if (wAABBDD != 0) { dynamic_cast(fCovList->At(2))->FillProfile(centmult, getStdAABBDD(arr) / wAABBDD, (fEventWeight == UnityWeight) ? 1.0 : wAABBDD, rn); + } double wAABBC = getStdAABBC(warr); - if (wAABBC != 0) + if (wAABBC != 0) { dynamic_cast(fCovList->At(3))->FillProfile(centmult, getStdAABBC(arr) / wAABBC, (fEventWeight == UnityWeight) ? 1.0 : wAABBC, rn); + } double wAABBD = getStdAABBD(warr); - if (wAABBD != 0) + if (wAABBD != 0) { dynamic_cast(fCovList->At(4))->FillProfile(centmult, getStdAABBD(arr) / wAABBD, (fEventWeight == UnityWeight) ? 1.0 : wAABBD, rn); + } double wABCC = getStdABCC(warr); - if (wABCC != 0) + if (wABCC != 0) { dynamic_cast(fCovList->At(5))->FillProfile(centmult, getStdABCC(arr) / wABCC, (fEventWeight == UnityWeight) ? 1.0 : wABCC, rn); + } double wABCD = getStdABCD(warr); - if (wABCD != 0) + if (wABCD != 0) { dynamic_cast(fCovList->At(6))->FillProfile(centmult, getStdABCD(arr) / wABCD, (fEventWeight == UnityWeight) ? 1.0 : wABCD, rn); + } double wABDD = getStdABDD(warr); - if (wABDD != 0) + if (wABDD != 0) { dynamic_cast(fCovList->At(7))->FillProfile(centmult, getStdABDD(arr) / wABDD, (fEventWeight == UnityWeight) ? 1.0 : wABDD, rn); + } double wABC = getStdABC(warr); - if (wABC != 0) + if (wABC != 0) { dynamic_cast(fCovList->At(8))->FillProfile(centmult, getStdABC(arr) / wABC, (fEventWeight == UnityWeight) ? 1.0 : wABC, rn); + } double wABD = getStdABD(warr); - if (wABD != 0) + if (wABD != 0) { dynamic_cast(fCovList->At(9))->FillProfile(centmult, getStdABD(arr) / wABD, (fEventWeight == UnityWeight) ? 1.0 : wABD, rn); + } double wABCCCC = getStdABCCCC(warr); - if (wABCCCC != 0.) + if (wABCCCC != 0.) { dynamic_cast(fCovList->At(14))->FillProfile(centmult, getStdABCCCC(arr) / wABCCCC, (fEventWeight == UnityWeight) ? 1. : wABCCCC, rn); + } double wABCCCD = getStdABCCCD(warr); - if (wABCCCD != 0.) + if (wABCCCD != 0.) { dynamic_cast(fCovList->At(15))->FillProfile(centmult, getStdABCCCD(arr) / wABCCCD, (fEventWeight == UnityWeight) ? 1. : wABCCCD, rn); + } double wABCCDD = getStdABCCDD(warr); - if (wABCCDD != 0.) + if (wABCCDD != 0.) { dynamic_cast(fCovList->At(16))->FillProfile(centmult, getStdABCCDD(arr) / wABCCDD, (fEventWeight == UnityWeight) ? 1. : wABCCDD, rn); + } double wABCDDD = getStdABCDDD(warr); - if (wABCDDD != 0.) + if (wABCDDD != 0.) { dynamic_cast(fCovList->At(17))->FillProfile(centmult, getStdABCDDD(arr) / wABCDDD, (fEventWeight == UnityWeight) ? 1. : wABCDDD, rn); + } double wABDDDD = getStdABDDDD(warr); - if (wABDDDD != 0.) + if (wABDDDD != 0.) { dynamic_cast(fCovList->At(18))->FillProfile(centmult, getStdABDDDD(arr) / wABDDDD, (fEventWeight == UnityWeight) ? 1. : wABDDDD, rn); + } double wABCCC = getStdABCCC(warr); - if (wABCCC != 0.) + if (wABCCC != 0.) { dynamic_cast(fCovList->At(10))->FillProfile(centmult, getStdABCCC(arr) / wABCCC, (fEventWeight == UnityWeight) ? 1. : wABCCC, rn); + } double wABCCD = getStdABCCD(warr); - if (wABCCD != 0.) + if (wABCCD != 0.) { dynamic_cast(fCovList->At(11))->FillProfile(centmult, getStdABCCD(arr) / wABCCD, (fEventWeight == UnityWeight) ? 1. : wABCCD, rn); + } double wABCDD = getStdABCDD(warr); - if (wABCDD != 0.) + if (wABCDD != 0.) { dynamic_cast(fCovList->At(12))->FillProfile(centmult, getStdABCDD(arr) / wABCDD, (fEventWeight == UnityWeight) ? 1. : wABCDD, rn); + } double wABDDD = getStdABDDD(warr); - if (wABDDD != 0.) + if (wABDDD != 0.) { dynamic_cast(fCovList->At(13))->FillProfile(centmult, getStdABDDD(arr) / wABDDD, (fEventWeight == UnityWeight) ? 1. : wABDDD, rn); - return; + } } void FlowPtContainer::fillCMProfiles(const double& centmult, const double& rn) { - if (sumP[getVectorIndex(0, 0)] == 0) + if (sumP[getVectorIndex(0, 0)] == 0) { return; + } // 0th order correlation cmDen.push_back(1.); cmVal.push_back(1.); @@ -889,26 +1576,30 @@ void FlowPtContainer::fillCMProfiles(const double& centmult, const double& rn) cmDen.push_back(sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] - sumP[getVectorIndex(2, 0)]); cmDen.push_back(sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] - 3 * sumP[getVectorIndex(2, 0)] * sumP[getVectorIndex(1, 0)] + 2 * sumP[getVectorIndex(3, 0)]); cmDen.push_back(sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] - 6 * sumP[getVectorIndex(2, 0)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] + 8 * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(3, 0)] + 3 * sumP[getVectorIndex(2, 0)] * sumP[getVectorIndex(2, 0)] - 6 * sumP[getVectorIndex(4, 0)]); - if (mpar < 1 || cmDen[1] == 0) + if (mpar < 1 || cmDen[1] == 0) { return; + } cmVal.push_back(sumP[getVectorIndex(1, 1)] / cmDen[1]); dynamic_cast(fCMTermList->At(0))->FillProfile(centmult, cmVal[1], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[1], rn); - if (mpar < 2 || sumP[getVectorIndex(2, 0)] == 0 || cmDen[2] == 0) + if (mpar < 2 || sumP[getVectorIndex(2, 0)] == 0 || cmDen[2] == 0) { // o2-linter: disable=magic-number (less than order 2) return; + } cmVal.push_back(1 / cmDen[2] * (sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] - sumP[getVectorIndex(2, 2)])); dynamic_cast(fCMTermList->At(1))->FillProfile(centmult, cmVal[2], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[2], rn); cmVal.push_back(-2 * 1 / cmDen[2] * (sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 1)] - sumP[getVectorIndex(2, 1)])); dynamic_cast(fCMTermList->At(2))->FillProfile(centmult, cmVal[3], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[2], rn); - if (mpar < 3 || sumP[getVectorIndex(3, 0)] == 0 || cmDen[3] == 0) + if (mpar < 3 || sumP[getVectorIndex(3, 0)] == 0 || cmDen[3] == 0) { // o2-linter: disable=magic-number (less than order 3) return; + } cmVal.push_back(1 / cmDen[3] * (sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] - 3 * sumP[getVectorIndex(2, 2)] * sumP[getVectorIndex(1, 1)] + 2 * sumP[getVectorIndex(3, 3)])); dynamic_cast(fCMTermList->At(3))->FillProfile(centmult, cmVal[4], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[3], rn); cmVal.push_back(-3 * 1 / cmDen[3] * (sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 0)] - 2 * sumP[getVectorIndex(2, 1)] * sumP[getVectorIndex(1, 1)] + 2 * sumP[getVectorIndex(3, 2)] - sumP[getVectorIndex(2, 2)] * sumP[getVectorIndex(1, 0)])); dynamic_cast(fCMTermList->At(4))->FillProfile(centmult, cmVal[5], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[3], rn); cmVal.push_back(3 * 1 / cmDen[3] * (sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] - 2 * sumP[getVectorIndex(2, 1)] * sumP[getVectorIndex(1, 0)] + 2 * sumP[getVectorIndex(3, 1)] - sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(2, 0)])); dynamic_cast(fCMTermList->At(5))->FillProfile(centmult, cmVal[6], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[3], rn); - if (mpar < 4 || sumP[getVectorIndex(4, 0)] == 0 || cmDen[4] == 0) + if (mpar < 4 || sumP[getVectorIndex(4, 0)] == 0 || cmDen[4] == 0) { // o2-linter: disable=magic-number (less than order 4) return; + } cmVal.push_back(1 / cmDen[4] * (sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] - 6 * sumP[getVectorIndex(2, 2)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] + 3 * sumP[getVectorIndex(2, 2)] * sumP[getVectorIndex(2, 2)] + 8 * sumP[getVectorIndex(3, 3)] * sumP[getVectorIndex(1, 1)] - 6 * sumP[getVectorIndex(4, 4)])); dynamic_cast(fCMTermList->At(6))->FillProfile(centmult, cmVal[7], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[4], rn); cmVal.push_back(-4 * 1 / cmDen[4] * (sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 0)] - 3 * sumP[getVectorIndex(2, 2)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 0)] - 3 * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(2, 1)] + 3 * sumP[getVectorIndex(2, 2)] * sumP[getVectorIndex(2, 1)] + 2 * sumP[getVectorIndex(3, 3)] * sumP[getVectorIndex(1, 0)] + 6 * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(3, 2)] - 6 * sumP[getVectorIndex(4, 3)])); @@ -917,15 +1608,17 @@ void FlowPtContainer::fillCMProfiles(const double& centmult, const double& rn) dynamic_cast(fCMTermList->At(8))->FillProfile(centmult, cmVal[9], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[4], rn); cmVal.push_back(-4 * 1 / cmDen[4] * (sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] - 3 * sumP[getVectorIndex(2, 1)] * sumP[getVectorIndex(1, 0)] * sumP[getVectorIndex(1, 0)] - 3 * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(2, 0)] * sumP[getVectorIndex(1, 0)] + 3 * sumP[getVectorIndex(2, 1)] * sumP[getVectorIndex(2, 0)] + 2 * sumP[getVectorIndex(1, 1)] * sumP[getVectorIndex(3, 0)] + 6 * sumP[getVectorIndex(3, 1)] * sumP[getVectorIndex(1, 0)] - 6 * sumP[getVectorIndex(4, 1)])); dynamic_cast(fCMTermList->At(9))->FillProfile(centmult, cmVal[10], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDen[4], rn); - return; } void FlowPtContainer::fillCMSubeventProfiles(const double& centmult, const double& rn) { // do I need to add an extra return statement here to match fillCMProfiles? - if (mpar < 1) + if (mpar < 1) { return; - if (nSubevents < 2) + } + const int minSubevents = 2; + if (nSubevents < minSubevents) { return; + } int indOffset = 0; for (int im = 1; im <= mpar; im++) { @@ -958,7 +1651,7 @@ void FlowPtContainer::fillCMSubeventProfiles(const double& centmult, const doubl dynamic_cast(fSubCMList->At(indOffset + 0))->FillProfile(centmult, cmValSub[nSubevents - 1][1], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[nSubevents - 1][1], rn); } - if (mpar >= 2) { + if (mpar >= 2) { // o2-linter: disable=magic-number (greater than order 2) if (insub[0][getVectorIndex(2, 0)] != 0 && cmDenSub[0][2] != 0) { cmValSub[0].push_back(1 / cmDenSub[0][2] * (insub[0][getVectorIndex(1, 1)] * insub[0][getVectorIndex(1, 1)] - insub[0][getVectorIndex(2, 2)])); dynamic_cast(fSubCMList->At(1))->FillProfile(centmult, cmValSub[0][2], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[0][2], rn); @@ -971,10 +1664,10 @@ void FlowPtContainer::fillCMSubeventProfiles(const double& centmult, const doubl cmValSub[nSubevents - 1].push_back(1 / cmDenSub[nSubevents - 1][2] * (insub[nSubevents - 1][getVectorIndex(1, 0)] * insub[nSubevents - 1][getVectorIndex(1, 1)] - insub[nSubevents - 1][getVectorIndex(2, 1)])); dynamic_cast(fSubCMList->At(indOffset + 2))->FillProfile(centmult, cmValSub[nSubevents - 1][3], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[nSubevents - 1][2], rn); } - validMpar[0] = true; + validMpar[0] = 1; } - if (mpar >= 3) { + if (mpar >= 3) { // o2-linter: disable=magic-number (greater than order 3) if (insub[0][getVectorIndex(3, 0)] != 0 && cmDenSub[0][3] != 0) { cmValSub[0].push_back(1 / cmDenSub[0][3] * (insub[0][getVectorIndex(1, 1)] * insub[0][getVectorIndex(1, 1)] * insub[0][getVectorIndex(1, 1)] - 3 * insub[0][getVectorIndex(2, 2)] * insub[0][getVectorIndex(1, 1)] + 2 * insub[0][getVectorIndex(3, 3)])); dynamic_cast(fSubCMList->At(3))->FillProfile(centmult, cmValSub[0][4], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[0][3], rn); @@ -991,9 +1684,9 @@ void FlowPtContainer::fillCMSubeventProfiles(const double& centmult, const doubl cmValSub[nSubevents - 1].push_back(1 / cmDenSub[nSubevents - 1][3] * (insub[nSubevents - 1][getVectorIndex(1, 1)] * insub[nSubevents - 1][getVectorIndex(1, 0)] * insub[nSubevents - 1][getVectorIndex(1, 0)] - 2 * insub[nSubevents - 1][getVectorIndex(2, 1)] * insub[nSubevents - 1][getVectorIndex(1, 0)] + 2 * insub[nSubevents - 1][getVectorIndex(3, 1)] - insub[nSubevents - 1][getVectorIndex(1, 1)] * insub[nSubevents - 1][getVectorIndex(2, 0)])); dynamic_cast(fSubCMList->At(indOffset + 5))->FillProfile(centmult, cmValSub[nSubevents - 1][6], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[nSubevents - 1][3], rn); } - validMpar[1] = true; + validMpar[1] = 1; } - if (mpar >= 4) { + if (mpar >= 4) { // o2-linter: disable=magic-number (greater than order 4) if (insub[0][getVectorIndex(4, 0)] != 0 && cmDenSub[0][4] != 0) { cmValSub[0].push_back(1 / cmDenSub[0][4] * (insub[0][getVectorIndex(1, 1)] * insub[0][getVectorIndex(1, 1)] * insub[0][getVectorIndex(1, 1)] * insub[0][getVectorIndex(1, 1)] - 6 * insub[0][getVectorIndex(2, 2)] * insub[0][getVectorIndex(1, 1)] * insub[0][getVectorIndex(1, 1)] + 3 * insub[0][getVectorIndex(2, 2)] * insub[0][getVectorIndex(2, 2)] + 8 * insub[0][getVectorIndex(3, 3)] * insub[0][getVectorIndex(1, 1)] - 6 * insub[0][getVectorIndex(4, 4)])); dynamic_cast(fSubCMList->At(6))->FillProfile(centmult, cmValSub[0][7], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[0][4], rn); @@ -1014,20 +1707,23 @@ void FlowPtContainer::fillCMSubeventProfiles(const double& centmult, const doubl cmValSub[nSubevents - 1].push_back(1 / cmDenSub[nSubevents - 1][4] * (insub[nSubevents - 1][getVectorIndex(1, 1)] * insub[nSubevents - 1][getVectorIndex(1, 0)] * insub[nSubevents - 1][getVectorIndex(1, 0)] * insub[nSubevents - 1][getVectorIndex(1, 0)] - 3 * insub[nSubevents - 1][getVectorIndex(2, 1)] * insub[nSubevents - 1][getVectorIndex(1, 0)] * insub[nSubevents - 1][getVectorIndex(1, 0)] - 3 * insub[nSubevents - 1][getVectorIndex(1, 1)] * insub[nSubevents - 1][getVectorIndex(2, 0)] * insub[nSubevents - 1][getVectorIndex(1, 0)] + 3 * insub[nSubevents - 1][getVectorIndex(2, 1)] * insub[nSubevents - 1][getVectorIndex(2, 0)] + 2 * insub[nSubevents - 1][getVectorIndex(1, 1)] * insub[nSubevents - 1][getVectorIndex(3, 0)] + 6 * insub[nSubevents - 1][getVectorIndex(3, 1)] * insub[nSubevents - 1][getVectorIndex(1, 0)] - 6 * insub[nSubevents - 1][getVectorIndex(4, 1)])); dynamic_cast(fSubCMList->At(indOffset + 9))->FillProfile(centmult, cmValSub[nSubevents - 1][10], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[nSubevents - 1][4], rn); } - validMpar[2] = true; + validMpar[2] = 1; } // Fill cross terms - for (int m = 2; m <= 4; ++m) { - if (!validMpar[m - 2]) + for (int m = 2; m <= centralMomentMaxOrder; ++m) { + if (validMpar[m - 2] == 0) { continue; + } for (int first = 1; first < m; ++first) { for (int second = first; second < m; ++second) { - if (first > second) + if (first > second) { continue; + } int fourth = m - second; for (int third = 1; third < m; ++third) { - if (third > fourth) + if (third > fourth) { continue; + } if (insub[0][getVectorIndex(m, 0)] != 0 && insub[nSubevents - 1][getVectorIndex(m, 0)] != 0 && cmDenSub[0][m] * cmDenSub[nSubevents - 1][m] != 0) { dynamic_cast(fSubCMList->FindObject(Form("cm%i_%i%isub1_%i%isub2", m, first, second, third, fourth)))->FillProfile(centmult, cmValSub[0][second * (second - 1) / 2 + second - first + 1] * cmValSub[nSubevents - 1][fourth * (fourth - 1) / 2 + fourth - third + 1], (fEventWeight == EventWeight::UnityWeight) ? 1.0 : cmDenSub[0][m] * cmDenSub[nSubevents - 1][m], rn); } @@ -1035,11 +1731,11 @@ void FlowPtContainer::fillCMSubeventProfiles(const double& centmult, const doubl } } } - return; } void FlowPtContainer::fillArray(FillType a, FillType b, double c, double d) { - for (int idx = 0; idx < 225; ++idx) { + const int nTerms = 3 * 3 * 5 * 5; + for (int idx = 0; idx < nTerms; ++idx) { int i = idx % 3; int j = ((idx - i) / 3) % 3; int k = ((idx - j * 3 - i) / 9) % 5; @@ -1052,7 +1748,6 @@ void FlowPtContainer::fillArray(FillType a, FillType b, double c, double d) LOGF(error, "FillType variant should hold same type for a and b during single function c"); } } - return; } template double FlowPtContainer::getStdAABBCC(T& inarr) @@ -1678,125 +2373,425 @@ double FlowPtContainer::orderedAddition(std::vector vec) } void FlowPtContainer::rebinMulti(int nbins) { + clearPtPostprocessLists(); if (fCMTermList) { - for (int i = 0; i < fCMTermList->GetEntries(); i++) + for (int i = 0; i < fCMTermList->GetEntries(); i++) { dynamic_cast(fCMTermList->At(i))->RebinMulti(nbins); + } } if (fCorrList) { - for (int i = 0; i < fCorrList->GetEntries(); i++) + for (int i = 0; i < fCorrList->GetEntries(); i++) { dynamic_cast(fCorrList->At(i))->RebinMulti(nbins); + } } if (fCovList) { - for (int i = 0; i < fCovList->GetEntries(); i++) + for (int i = 0; i < fCovList->GetEntries(); i++) { dynamic_cast(fCovList->At(i))->RebinMulti(nbins); + } } if (fSubList) { - for (int i = 0; i < fSubList->GetEntries(); i++) + for (int i = 0; i < fSubList->GetEntries(); i++) { dynamic_cast(fSubList->At(i))->RebinMulti(nbins); + } } if (fSubCMList) { - for (int i = 0; i < fSubCMList->GetEntries(); i++) + for (int i = 0; i < fSubCMList->GetEntries(); i++) { dynamic_cast(fSubCMList->At(i))->RebinMulti(nbins); + } } - return; } void FlowPtContainer::rebinMulti(int nbins, double* binedges) { + clearPtPostprocessLists(); if (fCMTermList) { - for (int i = 0; i < fCMTermList->GetEntries(); i++) + for (int i = 0; i < fCMTermList->GetEntries(); i++) { dynamic_cast(fCMTermList->At(i))->RebinMulti(nbins, binedges); + } } if (fCorrList) { - for (int i = 0; i < fCorrList->GetEntries(); i++) + for (int i = 0; i < fCorrList->GetEntries(); i++) { dynamic_cast(fCorrList->At(i))->RebinMulti(nbins, binedges); + } } if (fCovList) { - for (int i = 0; i < fCovList->GetEntries(); i++) + for (int i = 0; i < fCovList->GetEntries(); i++) { dynamic_cast(fCovList->At(i))->RebinMulti(nbins, binedges); + } } if (fSubList) { - for (int i = 0; i < fSubList->GetEntries(); i++) + for (int i = 0; i < fSubList->GetEntries(); i++) { dynamic_cast(fSubList->At(i))->RebinMulti(nbins, binedges); + } } if (fSubCMList) { - for (int i = 0; i < fSubCMList->GetEntries(); i++) + for (int i = 0; i < fSubCMList->GetEntries(); i++) { dynamic_cast(fSubCMList->At(i))->RebinMulti(nbins, binedges); + } } - return; } TH1* FlowPtContainer::getCorrHist(int ind, int m) { return dynamic_cast(fCorrList->FindObject(Form("mpt%i", m)))->getHist(ind); } +TH1* FlowPtContainer::getPtCentralMomentHist(const char* head, int ind) +{ + if (!head || head[0] == 0 || !fCorrList) { + return nullptr; + } + + const std::string resultName = makePostprocessName("ptcentral", head, ind); + if (fPtCentralMomentList) { + if (auto* result = dynamic_cast(fPtCentralMomentList->FindObject(resultName.c_str()))) { + return result; + } + } + + const auto config = std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return candidate.head == head; }); + if (config == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT central moment %s: no pure-pT postprocessing configuration was stored", head); + return nullptr; + } + + int order = 0; + for (const auto& group : config->groups) { + order += group.order; + } + if (order < 1 || order >= std::numeric_limits::digits) { + LOGF(error, "Cannot calculate pT central moment %s: invalid order %i", head, order); + return nullptr; + } + + auto getProfileHist = [&](const std::string& name) -> std::unique_ptr { + auto* profile = dynamic_cast(fCorrList->FindObject(name.c_str())); + if (!profile) { + LOGF(error, "Cannot calculate pT central moment %s: profile %s is missing", head, name.c_str()); + return nullptr; + } + std::unique_ptr histogram{profile->getHist(ind)}; + if (histogram) { + histogram->SetDirectory(nullptr); + } + return histogram; + }; + + auto shape = getProfileHist(config->head); + if (!shape) { + return nullptr; + } + auto* result = dynamic_cast(shape->Clone(resultName.c_str())); + result->SetDirectory(nullptr); + result->Reset(); + + const std::uint64_t allFactors = (std::uint64_t{1} << order) - 1; + std::vector activeCounts(config->groups.size(), 0); + bool finished = false; + while (!finished) { + int activeOrder = 0; + int factorOffset = 0; + double coefficient = 1.; + std::uint64_t activeFactors = 0; + std::map meanPowers; + for (size_t groupIndex = 0; groupIndex < config->groups.size(); ++groupIndex) { + const auto& group = config->groups[groupIndex]; + const int active = activeCounts[groupIndex]; + activeOrder += active; + coefficient *= binomialCoefficient(group.order, active); + for (int factor = 0; factor < active; ++factor) { + activeFactors |= std::uint64_t{1} << (factorOffset + factor); + } + if (active < group.order) { + meanPowers[group.meanProfile] += group.order - active; + } + factorOffset += group.order; + } + + std::unique_ptr term; + if (activeOrder == 0) { + term.reset(dynamic_cast(shape->Clone())); + term->SetDirectory(nullptr); + term->Reset(); + for (int bin = 1; bin <= term->GetNbinsX(); ++bin) { + if (shape->GetBinContent(bin) != 0. || shape->GetBinError(bin) != 0.) { + term->SetBinContent(bin, 1.); + } + } + } else { + term = getProfileHist(makeFullTopologyName(config->head, activeFactors, allFactors, false, order)); + if (!term) { + delete result; + return nullptr; + } + term->SetDirectory(nullptr); + } + + for (const auto& [meanName, power] : meanPowers) { + auto mean = getProfileHist(meanName); + if (!mean) { + delete result; + return nullptr; + } + mean->SetDirectory(nullptr); + if (power > 1) { + for (int bin = 0; bin <= mean->GetNbinsX() + 1; ++bin) { + const double value = mean->GetBinContent(bin); + const double error = mean->GetBinError(bin); + mean->SetBinContent(bin, std::pow(value, power)); + mean->SetBinError(bin, std::abs(power * std::pow(value, power - 1) * error)); + } + } + term->Multiply(mean.get()); + } + + if ((order - activeOrder) % ParityDivisor != 0) { + coefficient = -coefficient; + } + result->Add(term.get(), coefficient); + + size_t groupIndex = 0; + for (; groupIndex < activeCounts.size(); ++groupIndex) { + if (++activeCounts[groupIndex] <= config->groups[groupIndex].order) { + break; + } + activeCounts[groupIndex] = 0; + } + finished = groupIndex == activeCounts.size(); + } + + if (!fPtCentralMomentList) { + fPtCentralMomentList = new TList(); + fPtCentralMomentList->SetOwner(kTRUE); + } + fPtCentralMomentList->Add(result); + return result; +} +TH1* FlowPtContainer::getPtCumulantHist(const char* head, int ind) +{ + if (!head || head[0] == 0 || !fCorrList) { + return nullptr; + } + + const std::string resultName = makePostprocessName("ptcumulant", head, ind); + if (fPtCumulantList) { + if (auto* result = dynamic_cast(fPtCumulantList->FindObject(resultName.c_str()))) { + return result; + } + } + + const auto config = std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return candidate.head == head; }); + if (config == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT cumulant %s: no pure-pT postprocessing configuration was stored", head); + return nullptr; + } + + std::vector factorGroups; + for (size_t groupIndex = 0; groupIndex < config->groups.size(); ++groupIndex) { + factorGroups.insert(factorGroups.end(), config->groups[groupIndex].order, groupIndex); + } + const int order = factorGroups.size(); + if (order < 1) { + return nullptr; + } + + if (order == 1) { + auto* profile = dynamic_cast(fCorrList->FindObject(config->groups.front().meanProfile.c_str())); + if (!profile) { + LOGF(error, "Cannot calculate pT cumulant %s: profile %s is missing", head, config->groups.front().meanProfile.c_str()); + return nullptr; + } + auto* result = profile->getHist(ind); + if (!result) { + return nullptr; + } + result->SetName(resultName.c_str()); + result->SetDirectory(nullptr); + if (!fPtCumulantList) { + fPtCumulantList = new TList(); + fPtCumulantList->SetOwner(kTRUE); + } + fPtCumulantList->Add(result); + return result; + } + + auto* centralMoment = getPtCentralMomentHist(head, ind); + if (!centralMoment) { + return nullptr; + } + auto* result = dynamic_cast(centralMoment->Clone(resultName.c_str())); + result->SetDirectory(nullptr); + result->Reset(); + + using Topology = std::vector>; + auto topology = [](const PtMomentConfig& candidate, const std::vector* counts = nullptr) { + Topology value; + for (size_t groupIndex = 0; groupIndex < candidate.groups.size(); ++groupIndex) { + const int count = counts ? counts->at(groupIndex) : candidate.groups[groupIndex].order; + if (count > 0) { + value.emplace_back(candidate.groups[groupIndex].region, count); + } + } + std::sort(value.begin(), value.end()); + return value; + }; + auto findMomentConfig = [&](const std::vector& counts) { + const auto required = topology(*config, &counts); + return std::find_if(fPtMomentConfigs.begin(), fPtMomentConfigs.end(), [&](const auto& candidate) { return topology(candidate) == required; }); + }; + auto describeTopology = [&](const std::vector& counts) { + std::string description; + for (const auto& [region, count] : topology(*config, &counts)) { + if (!description.empty()) { + description += " "; + } + description += region + "{" + std::to_string(count) + "pt}"; + } + return description; + }; + + bool failed = false; + std::vector blockForFactor(order, 0); + std::function addPartitions = [&](int factor, int highestBlock) { + if (failed) { + return; + } + if (factor < order) { + for (int block = 0; block <= highestBlock + 1; ++block) { + blockForFactor[factor] = block; + addPartitions(factor + 1, std::max(highestBlock, block)); + } + return; + } + + const int numberOfBlocks = highestBlock + 1; + std::vector> blockCounts(numberOfBlocks, std::vector(config->groups.size(), 0)); + std::vector blockSizes(numberOfBlocks, 0); + for (int factorIndex = 0; factorIndex < order; ++factorIndex) { + const int block = blockForFactor[factorIndex]; + ++blockCounts[block][factorGroups[factorIndex]]; + ++blockSizes[block]; + } + if (std::any_of(blockSizes.begin(), blockSizes.end(), [](int size) { return size == 1; })) { + return; + } + + std::unique_ptr term; + for (int block = 0; block < numberOfBlocks; ++block) { + const PtMomentConfig* blockConfig = &*config; + if (numberOfBlocks > 1) { + const auto found = findMomentConfig(blockCounts[block]); + if (found == fPtMomentConfigs.end()) { + LOGF(error, "Cannot calculate pT cumulant %s: missing lower-order correlation %s", head, describeTopology(blockCounts[block]).c_str()); + failed = true; + return; + } + blockConfig = &*found; + } + auto* blockMoment = getPtCentralMomentHist(blockConfig->head.c_str(), ind); + if (!blockMoment) { + failed = true; + return; + } + if (!term) { + term.reset(dynamic_cast(blockMoment->Clone())); + term->SetDirectory(nullptr); + } else { + term->Multiply(blockMoment); + } + } + + double coefficient = 1.; + for (int i = 2; i < numberOfBlocks; ++i) { + coefficient *= i; + } + if (numberOfBlocks % ParityDivisor == 0) { + coefficient = -coefficient; + } + result->Add(term.get(), coefficient); + }; + + blockForFactor.front() = 0; + addPartitions(1, 0); + if (failed) { + delete result; + return nullptr; + } + + if (!fPtCumulantList) { + fPtCumulantList = new TList(); + fPtCumulantList->SetOwner(kTRUE); + } + fPtCumulantList->Add(result); + return result; +} TH1* FlowPtContainer::getCentralMomentHist(int ind, int m) { - if (!fCentralMomentList) + if (!fCentralMomentList) { createCentralMomentList(); - if (!fCentralMomentList) - return 0; - if (ind + 1 < fCentralMomentList->GetEntries()) + } + if (!fCentralMomentList) { + return nullptr; + } + if (ind + 1 < fCentralMomentList->GetEntries()) { return dynamic_cast(fCentralMomentList->FindObject(Form("cm%i_%i", m, ind))); - return 0; + } + return nullptr; } void FlowPtContainer::createCentralMomentList() { - if (fCentralMomentList) - delete fCentralMomentList; + delete fCentralMomentList; fCentralMomentList = new TList(); fCentralMomentList->SetOwner(); - for (auto m(1); m <= 4; ++m) { - for (int i = -1; i < reinterpret_cast(fCMTermList->At(0))->getNSubs(); ++i) { - TH1* hMpt = reinterpret_cast(fCMTermList->At(0))->getHist(i); + for (auto m(1); m <= centralMomentMaxOrder; ++m) { + for (int i = -1; i < dynamic_cast(fCMTermList->At(0))->getNSubs(); ++i) { + TH1* hMpt = dynamic_cast(fCMTermList->At(0))->getHist(i); std::vector hTs; for (int j = 0; j < m; ++j) { dynamic_cast(fCMTermList->FindObject(Form("cm%i_Mpt%i", m, j)))->SetErrorOption("g"); - hTs.push_back(reinterpret_cast(fCMTermList->FindObject(Form("cm%i_Mpt%i", m, j)))->getHist(i)); + hTs.push_back(dynamic_cast(fCMTermList->FindObject(Form("cm%i_Mpt%i", m, j)))->getHist(i)); } calculateCentralMomentHists(hTs, i, m, hMpt); } } - return; } void FlowPtContainer::calculateCentralMomentHists(std::vector inh, int ind, int m, TH1* hMpt) { - TH1* reth = reinterpret_cast(inh[0]->Clone(Form("cm%i_%i", m, ind))); + TH1* reth = dynamic_cast(inh[0]->Clone(Form("cm%i_%i", m, ind))); for (auto i(1); i < m; ++i) { TH1* mptPow = raiseHistToPower(hMpt, i); inh[i]->Multiply(mptPow); reth->Add(inh[i]); } TH1* mptLast = raiseHistToPower(hMpt, m); - reth->Add(mptLast, (m % 2) ? (-1) : 1); + reth->Add(mptLast, ((m % 2) != 0) ? (-1) : 1); fCentralMomentList->Add(reth); - return; } TH1* FlowPtContainer::getCumulantHist(int ind, int m) { - if (!fCumulantList) + if (!fCumulantList) { createCumulantList(); - if (!fCumulantList) - return 0; - if (ind + 1 < fCumulantList->GetEntries()) - return reinterpret_cast(fCumulantList->At((ind + 1) * mpar + m - 1)); - return 0; + } + if (!fCumulantList) { + return nullptr; + } + if (ind + 1 < fCumulantList->GetEntries()) { + return dynamic_cast(fCumulantList->At((ind + 1) * mpar + m - 1)); + } + return nullptr; } void FlowPtContainer::createCumulantList() { - if (fCumulantList) - delete fCumulantList; + delete fCumulantList; fCumulantList = new TList(); fCumulantList->SetOwner(); //((BootstrapProfile*)fCorrList->At(0))->PresetWeights((BootstrapProfile*)fCorrList->At(mpar-1)); - for (int i = -1; i < reinterpret_cast(fCorrList->At(0))->getNSubs(); ++i) { + for (int i = -1; i < dynamic_cast(fCorrList->At(0))->getNSubs(); ++i) { std::vector hTs; for (int j = 0; j < mpar; ++j) { dynamic_cast(fCorrList->FindObject(Form("mpt%i", j + 1)))->SetErrorOption("g"); - hTs.push_back(reinterpret_cast(fCorrList->FindObject(Form("mpt%i", j + 1)))->getHist(i)); + hTs.push_back(dynamic_cast(fCorrList->FindObject(Form("mpt%i", j + 1)))->getHist(i)); } calculateCumulantHists(hTs, i); } //((BootstrapProfile*)fCorrList->At(0))->PresetWeights(0); - return; } void FlowPtContainer::calculateCumulantHists(std::vector inh, int ind) { @@ -1815,65 +2810,79 @@ void FlowPtContainer::calculateCumulantHists(std::vector inh, int ind) // delete hWeights; fCumulantList->Add(dynamic_cast(reth->Clone(Form("kappa%i_%i", m, ind)))); } - return; } -Long64_t FlowPtContainer::Merge(TCollection* collist) +Long64_t FlowPtContainer::Merge(TCollection* collist) // o2-linter: disable=name/function-variable (Keep name consistent with ROOT streamer merging) { - if (!fCorrList || !fCMTermList) - return 0; + delete fCumulantList; + fCumulantList = nullptr; + delete fCentralMomentList; + fCentralMomentList = nullptr; + clearPtPostprocessLists(); + Long64_t nmerged = 0; TIter allPTC(collist); - FlowPtContainer* lPTC = 0; + FlowPtContainer* lPTC = nullptr; while ((lPTC = dynamic_cast(allPTC()))) { + if (!lPTC->fPtMomentConfigs.empty()) { + if (fPtMomentConfigs.empty()) { + fPtMomentConfigs = lPTC->fPtMomentConfigs; + } else if (!sameMomentConfigs(fPtMomentConfigs, lPTC->fPtMomentConfigs)) { + LOGF(error, "Cannot merge FlowPtContainer %s: pT postprocessing configurations differ", GetName()); + continue; + } + } else if (!fPtMomentConfigs.empty()) { + LOGF(error, "Cannot merge FlowPtContainer %s: incoming pT postprocessing configuration is missing", GetName()); + continue; + } + TList* tCMTerm = lPTC->fCMTermList; TList* tCorr = lPTC->fCorrList; TList* tCov = lPTC->fCovList; TList* tSub = lPTC->fSubList; TList* tSubCM = lPTC->fSubCMList; - TList* tCum = lPTC->fCumulantList; - TList* tCM = lPTC->fCentralMomentList; + bool merged = false; if (tCMTerm) { - if (!fCMTermList) + if (!fCMTermList) { fCMTermList = dynamic_cast(tCMTerm->Clone()); - else + } else { mergeBSLists(fCMTermList, tCMTerm); - nmerged++; + } + merged = true; } if (tCorr) { - if (!fCorrList) + if (!fCorrList) { fCorrList = dynamic_cast(tCorr->Clone()); - else + } else { mergeBSLists(fCorrList, tCorr); + } + merged = true; } if (tCov) { - if (!fCovList) + if (!fCovList) { fCovList = dynamic_cast(tCov->Clone()); - else + } else { mergeBSLists(fCovList, tCov); - } - if (tCum) { - if (!fCumulantList) - fCumulantList = dynamic_cast(tCum->Clone()); - else - mergeBSLists(fCumulantList, tCum); - } - if (tCM) { - if (!fCentralMomentList) - fCentralMomentList = dynamic_cast(tCM->Clone()); - else - mergeBSLists(fCentralMomentList, tCM); + } + merged = true; } if (tSub) { - if (!fSubList) + if (!fSubList) { fSubList = dynamic_cast(tSub->Clone()); - else + } else { mergeBSLists(fSubList, tSub); + } + merged = true; } if (tSubCM) { - if (!fSubCMList) + if (!fSubCMList) { fSubCMList = dynamic_cast(tSubCM->Clone()); - else + } else { mergeBSLists(fSubCMList, tSubCM); + } + merged = true; + } + if (merged) { + ++nmerged; } } return nmerged; @@ -1885,8 +2894,8 @@ void FlowPtContainer::mergeBSLists(TList* source, TList* target) return; } for (int i = 0; i < source->GetEntries(); i++) { - BootstrapProfile* lObj = dynamic_cast(source->At(i)); - BootstrapProfile* tObj = dynamic_cast(target->At(i)); + auto* lObj = dynamic_cast(source->At(i)); + auto* tObj = dynamic_cast(target->At(i)); lObj->MergeBS(tObj); } } @@ -1905,19 +2914,19 @@ TH1* FlowPtContainer::raiseHistToPower(TH1* inh, double p) } return reth; } -void FlowPtContainer::getSubevents(int k, int n, std::vector& current, std::vector>& subevents) +void FlowPtContainer::getSubevents(int k, int n, std::vector& current, std::vector>& outputSubevents) { if (n == 1) { // Last box gets all remaining objects current.push_back(k); - subevents.push_back(current); + outputSubevents.push_back(current); current.pop_back(); return; } for (int i = 0; i <= k; ++i) { current.push_back(i); - getSubevents(k - i, n - 1, current, subevents); + getSubevents(k - i, n - 1, current, outputSubevents); current.pop_back(); } } diff --git a/PWGCF/GenericFramework/Core/FlowPtContainer.h b/PWGCF/GenericFramework/Core/FlowPtContainer.h index 748d4e57cb5..6bd33833f7e 100644 --- a/PWGCF/GenericFramework/Core/FlowPtContainer.h +++ b/PWGCF/GenericFramework/Core/FlowPtContainer.h @@ -16,6 +16,7 @@ #ifndef PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ #define PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ +#include "GFW.h" #include "GFWConfig.h" #include @@ -28,8 +29,10 @@ #include #include +#include #include #include +#include #include #include @@ -41,22 +44,61 @@ enum EventWeight { }; }; +namespace o2::analysis::genericframework::flowptcontainer +{ +struct PtProfileConfig { + GFW::CorrConfig config; + std::vector> isPt; + std::vector> factors; + std::uint64_t activeFactors{}; // Flow, when present, is bit 0; pT terms follow in expression order. + bool usesFullTopology{}; +}; + +struct PtCorrConfig { + std::string head; + std::uint64_t allFactors{}; + bool hasFlow{}; + std::vector profiles; + std::vector meanProfiles; +}; + +struct PtRegionMoments { + int maxOrder{}; + std::vector values; +}; + +struct PtMomentGroup { + std::string region; + int order{}; + std::string meanProfile; + ClassDefNV(PtMomentGroup, 1); +}; + +struct PtMomentConfig { + std::string head; + std::vector groups; + ClassDefNV(PtMomentConfig, 1); +}; +} // namespace o2::analysis::genericframework::flowptcontainer + class FlowPtContainer : public TNamed { public: using FillType = std::variant, double>; FlowPtContainer(); explicit FlowPtContainer(const char* name); - ~FlowPtContainer(); + ~FlowPtContainer() override; FlowPtContainer(const char* name, const char* title); - void initialise(const o2::framework::AxisSpec axis, const int& m, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); - void initialise(int nbinsx, double* xbins, const int& m, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); - void initialise(int nbinsx, double xlow, double xhigh, const int& m, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); + void initialise(const o2::framework::AxisSpec& axis, const o2::analysis::genericframework::GFWRegions& regions, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); + void initialise(const o2::framework::AxisSpec& axis, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); + void initialise(int nbinsx, double* xbins, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); + void initialise(int nbinsx, double xlow, double xhigh, const int& maxOrder, const o2::analysis::genericframework::GFWCorrConfigs& configs, const int& nsub = 10); // initial pt-pt correlations with two subevents - void initialiseSubevent(const o2::framework::AxisSpec axis, const int& m, const int& nsubev = 2, const int& nsub = 10); - void initialiseSubevent(int nbinsx, double* xbins, const int& m, const int& nsubev = 2, const int& nsub = 10); - void initialiseSubevent(int nbinsx, double xlow, double xhigh, const int& m, const int& nsubev = 2, const int& nsub = 10); + void initialiseSubevent(const o2::framework::AxisSpec& axis, const int& maxOrder, const int& nsubev = 2, const int& nsub = 10); + void initialiseSubevent(int nbinsx, double* xbins, const int& maxOrder, const int& nsubev = 2, const int& nsub = 10); + void initialiseSubevent(int nbinsx, double xlow, double xhigh, const int& maxOrder, const int& nsubev = 2, const int& nsub = 10); void fill(const double& w, const double& pt); + void fill(const double& eta, const double& w, const double& pt, int mask); void fillSub(const double& w, const double& pt, int subIndex); void fillSub1(const double& w, const double& pt) { fillSub(w, pt, 0); } void fillSub2(const double& w, const double& pt) { fillSub(w, pt, nSubevents - 1); } @@ -66,32 +108,39 @@ class FlowPtContainer : public TNamed void calculateCorrelations(); void calculateSubeventCorrelations(); void calculateCMTerms(); - void fillPtProfiles(const double& lMult, const double& rn); - void fillSubeventPtProfiles(const double& lMult, const double& rn); - void fillVnPtCorrProfiles(const double& lMult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask); + void fillPtProfiles(const double& centmult, const double& rn); + void fillPtProfiles(GFW& gfw, const double& centmult, const double& rn); + // Book and fill a separate pT observable with a weight calculated by the task. + // The task can supply a separate event weight for the same observable. + bool addPtProfile(const char* name, int observableOrder); + bool fillPtProfile(const char* name, int observableOrder, double mult, double eventWeight, double rn); + void fillSubeventPtProfiles(const double& centmult, const double& rn); + void fillVnPtCorrProfiles(const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask); void fillVnDeltaPtProfiles(const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask); - void fillVnPtCorrProfiles(const int configIndex, const double& lMult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask); + void fillVnPtCorrProfiles(const int configIndex, const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask); void fillVnDeltaPtProfiles(const int configIndex, const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask); void fillVnDeltaPtStdProfiles(const double& centmult, const double& rn); void fillVnPtCorrStdProfiles(const double& centmult, const double& rn); void fillVnPtProfiles(const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask) { - if (fUseCentralMoments) + if (fUseCentralMoments) { fillVnDeltaPtProfiles(centmult, flowval, flowtuples, rn, mask); - else + } else { fillVnPtCorrProfiles(centmult, flowval, flowtuples, rn, mask); + } } void fillVnPtProfiles(const int configIndex, const double& centmult, const double& flowval, const double& flowtuples, const double& rn, uint8_t mask) { - if (fUseCentralMoments) + if (fUseCentralMoments) { fillVnDeltaPtProfiles(configIndex, centmult, flowval, flowtuples, rn, mask); - else + } else { fillVnPtCorrProfiles(configIndex, centmult, flowval, flowtuples, rn, mask); + } } void skipVnPtProfiles(uint8_t mask) { for (auto m(1); m <= mpar; ++m) { - if (!(mask & (1 << (m - 1)))) { + if ((mask & (1 << (m - 1))) == 0) { continue; } if (fUseCentralMoments) { @@ -102,17 +151,17 @@ class FlowPtContainer : public TNamed ++fillCounter; } } - return; } void fillVnPtStdProfiles(const double& centmult, const double& rn) { - if (fUseCentralMoments) + if (fUseCentralMoments) { fillVnDeltaPtStdProfiles(centmult, rn); - else + } else { fillVnPtCorrStdProfiles(centmult, rn); + } } - void fillCMProfiles(const double& lMult, const double& rn); - void fillCMSubeventProfiles(const double& lMult, const double& rn); + void fillCMProfiles(const double& centmult, const double& rn); + void fillCMSubeventProfiles(const double& centmult, const double& rn); TList* getCorrList() { return fCorrList; } TList* getCMTermList() { return fCMTermList; } TList* getCovList() { return fCovList; } @@ -126,6 +175,9 @@ class FlowPtContainer : public TNamed TH1* getCentralMomentHist(int ind, int m); TH1* getCumulantHist(int ind, int m); TH1* getCorrHist(int ind, int m); + // Postprocess a pure-pT configuration by head. ind = -1 selects the main profile. + TH1* getPtCentralMomentHist(const char* head, int ind = -1); + TH1* getPtCumulantHist(const char* head, int ind = -1); int getMpar() { return mpar; } Long64_t Merge(TCollection* collist); double orderedAddition(std::vector vec); @@ -150,6 +202,7 @@ class FlowPtContainer : public TNamed arr.resize(3 * 3 * 5 * 5, {0.0, 0.0}); warr.clear(); warr.resize(3 * 3 * 5 * 5, 0.0); + clearPtRegionMoments(); }; TList* fCMTermList; @@ -221,10 +274,21 @@ class FlowPtContainer : public TNamed double getStdABDDD(T& inarr); private: - std::vector> subevents; + static const int centralMomentMaxOrder = 4; + o2::analysis::genericframework::GFWRegions fPtRegions; //! + std::vector fPtCorrConfigs; //! + std::vector fPtRegionMoments; //! + std::vector fPtMomentConfigs; + TList* fPtCentralMomentList; //! + TList* fPtCumulantList; //! + std::vector> subevents; //! + void clearPtPostprocessLists(); + void clearPtRegionMoments(); + [[nodiscard]] double getPtMoment(int region, int weightPower, int ptPower) const; + double recursivePtCorrelation(int poi, int ref, int overlap, std::vector& weightPowers, std::vector& ptPowers) const; void getSubevents(int k, int n, std::vector& current, std::vector>& subevents); - static constexpr float FactorialArray[9] = {1., 1., 2., 6., 24., 120., 720., 5040., 40320.}; - static constexpr int SignArray[9] = {1, -1, 1, -1, 1, -1, 1, -1, 1}; - ClassDef(FlowPtContainer, 2); + static constexpr std::array FactorialArray = {1., 1., 2., 6., 24., 120., 720., 5040., 40320.}; + static constexpr std::array SignArray = {1, -1, 1, -1, 1, -1, 1, -1, 1}; + ClassDefOverride(FlowPtContainer, 3); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_FLOWPTCONTAINER_H_ diff --git a/PWGCF/GenericFramework/Core/GFW.cxx b/PWGCF/GenericFramework/Core/GFW.cxx index 823d93c5d39..9a4918af00c 100644 --- a/PWGCF/GenericFramework/Core/GFW.cxx +++ b/PWGCF/GenericFramework/Core/GFW.cxx @@ -9,12 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFW.cxx +/// \brief Initialize regions and calculate n-particle correlations +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFW.h" #include "PWGCF/GenericFramework/Core/GFWPowerArray.h" +#include + #include -#include +#include #include #include #include @@ -22,27 +28,37 @@ using std::complex; using std::pair; using std::string; -using std::vector; + +namespace +{ +constexpr std::size_t TwoParticleOrder = 2; +constexpr std::size_t ThreeParticleOrder = 3; +} // namespace GFW::GFW() : fInitialized(false) {} +void GFW::Region::PrintStructure() +{ + LOGF(info, "%s: eta [%f.. %f].", rName.c_str(), EtaMin, EtaMax); +} + GFW::~GFW() { for (auto pItr = fCumulants.begin(); pItr != fCumulants.end(); ++pItr) pItr->DestroyComplexVectorArray(); }; -void GFW::AddRegion(string refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { if (lNpT < 1) { - printf("Number of pT bins cannot be less than 1! Not adding anything.\n"); + LOGF(warning, "Number of pT bins cannot be less than 1! Not adding anything."); return; } if (lEtaMin >= lEtaMax) { - printf("Eta min. cannot be more than eta max! Not adding...\n"); + LOGF(warning, "Eta min. cannot be more than eta max! Not adding..."); return; } if (refName == "") { - printf("Region must have a name!\n"); + LOGF(warning, "Region must have a name!"); return; } Region lOneRegion; @@ -56,23 +72,30 @@ void GFW::AddRegion(string refName, double lEtaMin, double lEtaMax, int lNpT, in lOneRegion.BitMask = BitMask; // Bit mask AddRegion(lOneRegion); }; -void GFW::AddRegion(string refName, vector lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, const std::vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { + const auto oldSize = fRegions.size(); AddRegion(refName, lEtaMin, lEtaMax, lNpT, BitMask); + if (fRegions.size() == oldSize) + return; (fRegions.end() - 1)->Nhar = static_cast(lNparVec.size()); (fRegions.end() - 1)->NparVec = lNparVec; (fRegions.end() - 1)->powsDefined = true; }; -void GFW::AddRegion(string refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - vector tVec = {}; + std::vector tVec = {}; + if (lNhar > 0) + tVec.reserve(lNhar); for (int i = 0; i < lNhar; i++) tVec.push_back(lNpar); AddRegion(refName, tVec, lEtaMin, lEtaMax, lNpT, BitMask); }; -void GFW::AddRegion(string refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) +void GFW::AddRegion(const string& refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask) { - vector tVec = {}; + std::vector tVec = {}; + if (lNhar > 0) + tVec.reserve(lNhar); for (int i = 0; i < lNhar; i++) tVec.push_back(lNparVec[i]); AddRegion(refName, tVec, lEtaMin, lEtaMax, lNpT, BitMask); @@ -84,14 +107,13 @@ int GFW::CreateRegions() fCumulants.clear(); InitializePowerArrays(); if (fRegions.size() < 1) { - printf("No regions set. Skipping...\n"); + LOGF(warning, "No regions set. Skipping..."); return 0; } int nRegions = 0; for (auto pItr = fRegions.begin(); pItr != fRegions.end(); pItr++) { - GFWCumulant* lCumulant = new GFWCumulant(); - lCumulant->CreateComplexVectorArrayVarPower(pItr->Nhar, pItr->NparVec, pItr->NpT); - fCumulants.push_back(*lCumulant); + fCumulants.emplace_back(); + fCumulants.back().CreateComplexVectorArrayVarPower(pItr->Nhar, pItr->NparVec, pItr->NpT); ++nRegions; } if (nRegions) @@ -114,22 +136,25 @@ complex GFW::TwoRec(int n1, int n2, int p1, int p2, int ptbin, GFWCumula complex formula = part1 * part2 - part3; return formula; }; -complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, vector& hars) +complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars) { - vector pows; + std::vector pows; + pows.reserve(hars.size()); for (int i = 0; i < static_cast(hars.size()); i++) pows.push_back(1); return RecursiveCorr(qpoi, qref, qol, ptbin, hars, pows); }; -complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, vector& hars, vector& pows) +complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars, std::vector& pows) { + if (hars.empty() || pows.empty()) + return complex(0, 0); if ((pows.at(0) != 1) && qol) qpoi = qol; // if the power of POI is not unity, then always use overlap (if defined). // Only valid for 1 particle of interest though! - if (hars.size() < 2) + if (hars.size() < TwoParticleOrder) return qpoi->Vec(hars.at(0), pows.at(0), ptbin); - if (hars.size() < 3) + if (hars.size() < ThreeParticleOrder) return TwoRec(hars.at(0), hars.at(1), pows.at(0), pows.at(1), ptbin, qpoi, qref, qol); int harlast = hars.at(hars.size() - 1); int powlast = pows.at(pows.size() - 1); @@ -141,7 +166,7 @@ complex GFW::RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumu for (int i = harSize - 1; i >= 0; i--) { // checking if current configuration is a permutation of the next one. // Need to have more than 2 harmonics though, otherwise it doesn't make sense. - if (i > 2) { // only makes sense when we have more than two harmonics remaining + if (i > static_cast(TwoParticleOrder)) { // only makes sense when we have more than two harmonics remaining if (hars.at(i) == hars.at(i - 1) && pows.at(i) == pows.at(i - 1)) { // if it is a permutation, then increase degeneracy and continue; lDegeneracy++; continue; @@ -181,30 +206,28 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) // Then make sure we don't have any double-spaces: while (s_index(config, " ") > -1) s_replace_all(config, " ", " "); - vector regs; - vector hars; int sz1 = 0; int szend = 0; string ts, ts2; CorrConfig ReturnConfig; // Fetch region descriptor if (!s_tokenize(config, ts, szend, "{")) { - printf("Could not find any harmonics!\n"); + LOGF(warning, "Could not find any harmonics!"); return ReturnConfig; } szend = 0; int counter = 0; while (s_tokenize(config, ts, szend, "{")) { counter++; - ReturnConfig.Regs.push_back(vector{}); - ReturnConfig.Hars.push_back(vector{}); + ReturnConfig.Regs.push_back(std::vector{}); + ReturnConfig.Hars.push_back(std::vector{}); ReturnConfig.Overlap.push_back(-1); // initially, assume no overlap // Check if there's a particular pT bin I should be using here. If so, store it (otherwise, it's bin 0) int ptbin = -1; int sz2 = 0; if (s_contains(ts, "(")) { if (!s_contains(ts, ")")) { - printf("Missing \")\" in the configurator. Returning...\n"); + LOGF(warning, "Missing \")\" in the configurator. Returning..."); return ReturnConfig; } sz2 = s_index(ts, "("); @@ -231,7 +254,7 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) if (ts2 == " " || ts2 == "") continue; if (ind < 0) { - printf("Could not find region named %s!\n", ts2.c_str()); + LOGF(warning, "Could not find region named %s!", ts2.c_str()); break; } if (!isOverlap) @@ -246,14 +269,14 @@ GFW::CorrConfig GFW::GetCorrelatorConfig(string config, string head, bool ptdif) while (s_tokenize(harstr, ts, dummys, " ")) ReturnConfig.Hars.at(counter - 1).push_back(stoi(ts)); } - ReturnConfig.Head = head; + ReturnConfig.Head = std::move(head); ReturnConfig.pTDif = ptdif; // ReturnConfig.pTbin = ptbin; fListOfCFGs.push_back(ReturnConfig); return ReturnConfig; }; -complex GFW::Calculate(int poi, int ref, vector hars, int ptbin) +complex GFW::Calculate(int poi, int ref, std::vector hars, int ptbin) { GFWCumulant* qref = &fCumulants.at(ref); GFWCumulant* qpoi = &fCumulants.at(poi); @@ -305,13 +328,13 @@ complex GFW::Calculate(CorrConfig corconf, int ptbin, bool SetHarmsToZer } return retval; }; -vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& incfg) +std::vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& incfg) { - vector>> retPair; + std::vector>> retPair; for (int iR = 0; iR < static_cast(incfg.Regs.size()); iR++) { if (static_cast(incfg.Regs[iR].size()) > 1) { - retPair.push_back(make_pair(incfg.Regs[iR][0], vector{incfg.Hars[iR][0]})); // If we have a PoI, then it comes with the first harmonic - retPair.push_back(make_pair(incfg.Regs[iR][1], incfg.Hars[iR])); // Then the second is ref. with full harmonics + retPair.push_back(make_pair(incfg.Regs[iR][0], std::vector{incfg.Hars[iR][0]})); // If we have a PoI, then it comes with the first harmonic + retPair.push_back(make_pair(incfg.Regs[iR][1], incfg.Hars[iR])); // Then the second is ref. with full harmonics } else { retPair.push_back(make_pair(incfg.Regs[iR][0], incfg.Hars[iR])); // Otherwise, it's only ref with all harmonics } @@ -322,28 +345,28 @@ vector>> GFW::GetHarmonicsSingleConfig(const CorrConfig& i }; void GFW::InitializePowerArrays() { - vector>> harSets(static_cast(fRegions.size())); + std::vector>> harSets(static_cast(fRegions.size())); for (const CorrConfig& lConf : fListOfCFGs) { auto HarPerReg = GetHarmonicsSingleConfig(lConf); - for (auto oneHar : HarPerReg) + for (const auto& oneHar : HarPerReg) harSets[oneHar.first].push_back(oneHar.second); } // Now, loop through all combinations of different harmonics for each region and calculate power arrays for (int i = 0; i < static_cast(harSets.size()); i++) { if (fRegions[i].powsDefined) continue; // Only do if powers have not been externally defined - vector powerArray = GFWPowerArray::GetPowerArray(harSets[i]); + std::vector powerArray = GFWPowerArray::GetPowerArray(harSets[i]); fRegions[i].Nhar = static_cast(powerArray.size()); fRegions[i].NparVec = powerArray; fRegions[i].powsDefined = true; } }; -complex GFW::Calculate(int poi, vector hars) +complex GFW::Calculate(int poi, std::vector hars) { GFWCumulant* qpoi = &fCumulants.at(poi); return RecursiveCorr(qpoi, qpoi, qpoi, 0, hars); }; -int GFW::FindRegionByName(string refName) +int GFW::FindRegionByName(const string& refName) { for (int i = 0; i < static_cast(fRegions.size()); i++) if (fRegions.at(i).rName == refName) @@ -351,13 +374,14 @@ int GFW::FindRegionByName(string refName) return -1; }; // String processing: -int GFW::s_index(string& instr, const string& pattern, const int& spos) +int GFW::s_index(const string& instr, const string& pattern, const int& spos) { - return instr.find(pattern, spos); + const auto position = instr.find(pattern, spos); + return position == string::npos ? -1 : static_cast(position); }; -bool GFW::s_contains(string& instr, const string& pattern) +bool GFW::s_contains(const string& instr, const string& pattern) { - return (s_index(instr, pattern) > -1); + return instr.find(pattern) != string::npos; }; void GFW::s_replace(string& instr, const string& pattern1, const string& pattern2, const int& spos) { @@ -374,7 +398,7 @@ void GFW::s_replace_all(string& instr, const string& pattern1, const string& pat lpos = s_index(instr, pattern1, lpos); } }; -bool GFW::s_tokenize(string& instr, string& subs, int& spos, const string& delim) +bool GFW::s_tokenize(const string& instr, string& subs, int& spos, const string& delim) { if (spos < 0 || spos >= static_cast(instr.size())) { spos = -1; diff --git a/PWGCF/GenericFramework/Core/GFW.h b/PWGCF/GenericFramework/Core/GFW.h index 3263e5c4836..288ea0ed634 100644 --- a/PWGCF/GenericFramework/Core/GFW.h +++ b/PWGCF/GenericFramework/Core/GFW.h @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GFW.h/.cxx +/// \file GFW.h /// \brief Class steers the initialization and calculation of n-particle correlations. Uses recursive function, all terms are calculated only once. /// \author Emil Gorm Nielsen (ack. V. Vislavicius), NBI, emil.gorm.nielsen@cern.ch @@ -19,7 +19,6 @@ #include "GFWCumulant.h" #include -#include #include #include #include @@ -28,7 +27,8 @@ class GFW { public: struct Region { - int Nhar, NpT; + int Nhar = 0; + int NpT = 0; std::vector NparVec{}; double EtaMin = -999; double EtaMax = -999; @@ -39,7 +39,7 @@ class GFW { return EtaMin < a.EtaMin; }; - void PrintStructure() { printf("%s: eta [%f.. %f].", rName.c_str(), EtaMin, EtaMax); } + void PrintStructure(); }; struct CorrConfig { std::vector> Regs{}; @@ -50,13 +50,15 @@ class GFW std::string Head = ""; }; GFW(); + GFW(const GFW&) = delete; + GFW& operator=(const GFW&) = delete; ~GFW(); std::vector fRegions; std::vector fCumulants; - void AddRegion(std::string refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask); - void AddRegion(std::string refName, std::vector lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy - void AddRegion(std::string refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, all powers are the same - void AddRegion(std::string refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, array instead of a vector + void AddRegion(const std::string& refName, double lEtaMin, double lEtaMax, int lNpT, int BitMask); + void AddRegion(const std::string& refName, const std::vector& lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy + void AddRegion(const std::string& refName, int lNhar, int lNpar, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, all powers are the same + void AddRegion(const std::string& refName, int lNhar, int* lNparVec, double lEtaMin, double lEtaMax, int lNpT, int BitMask); // Legacy support, array instead of a vector int CreateRegions(); void Fill(double eta, int ptin, double phi, double weight, int mask, double secondWeight = -1); void Clear(); @@ -71,18 +73,18 @@ class GFW std::complex TwoRec(int n1, int n2, int p1, int p2, int ptbin, GFWCumulant*, GFWCumulant*, GFWCumulant*); std::complex RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars, std::vector& pows); // POI, Ref. flow, overlapping region std::complex RecursiveCorr(GFWCumulant* qpoi, GFWCumulant* qref, GFWCumulant* qol, int ptbin, std::vector& hars); // POI, Ref. flow, overlapping region - void AddRegion(Region inreg) { fRegions.push_back(inreg); } + void AddRegion(const Region& inreg) { fRegions.push_back(inreg); } Region GetRegion(int index) { return fRegions.at(index); } - int FindRegionByName(std::string refName); + int FindRegionByName(const std::string& refName); std::vector>> GetHarmonicsSingleConfig(const CorrConfig&); // Calculating functions: std::complex Calculate(int poi, int ref, std::vector hars, int ptbin = 0); // For differential, need POI and reference std::complex Calculate(int poi, std::vector hars); // For integrated case // Operations on strings. Equivalent to TString operations, but one to rid of root dependence - int s_index(std::string& instr, const std::string& pattern, const int& spos = 0); - bool s_contains(std::string& instr, const std::string& pattern); + int s_index(const std::string& instr, const std::string& pattern, const int& spos = 0); + bool s_contains(const std::string& instr, const std::string& pattern); void s_replace(std::string& instr, const std::string& pattern1, const std::string& pattern2, const int& spos = 0); void s_replace_all(std::string& instr, const std::string& pattern1, const std::string& pattern2); - bool s_tokenize(std::string& instr, std::string& substr, int& spos, const std::string& delim); + bool s_tokenize(const std::string& instr, std::string& substr, int& spos, const std::string& delim); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_GFW_H_ diff --git a/PWGCF/GenericFramework/Core/GFWConfig.h b/PWGCF/GenericFramework/Core/GFWConfig.h index 1bc1811c30a..41efe336efa 100644 --- a/PWGCF/GenericFramework/Core/GFWConfig.h +++ b/PWGCF/GenericFramework/Core/GFWConfig.h @@ -53,6 +53,7 @@ int CheckSameSize(const std::vector& first, const std::vector&... rest) class GFWBinningCuts { public: + // cppcheck-suppress noExplicitConstructor GFWBinningCuts(int vtxzbins_ = 40, int etabins_ = 16, int phibins_ = 72, int nchbins_ = 300, float nchmin_ = 0, float nchmax_ = 3000, float ptpoimin_ = 0.2, float ptpoimax_ = 10., float ptrefmin_ = 0.2, float ptrefmax_ = 3., std::vector ptbinning_ = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, @@ -144,6 +145,7 @@ class GFWBinningCuts class GFWRegions { public: + // cppcheck-suppress noExplicitConstructor GFWRegions(std::vector names_ = {"refN", "refP", "refFull"}, std::vector etaminvals_ = {-0.8, 0.4, -0.8}, std::vector etamaxvals_ = {-0.4, 0.8, 0.8}, std::vector pTDifs_ = {0, 0, 0}, @@ -189,6 +191,7 @@ class GFWRegions class GFWCorrConfigs { public: + // cppcheck-suppress noExplicitConstructor GFWCorrConfigs(std::vector corrs_ = {"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}", "refFull {2 -2}", "refFull {2 2 -2 -2}"}, std::vector heads_ = {"ChGap22", "ChGap32", "ChGap42", "ChFull22", "ChFull24"}, diff --git a/PWGCF/GenericFramework/Core/GFWCumulant.cxx b/PWGCF/GenericFramework/Core/GFWCumulant.cxx index 3a8b1c4d172..f3168eb4bf9 100644 --- a/PWGCF/GenericFramework/Core/GFWCumulant.cxx +++ b/PWGCF/GenericFramework/Core/GFWCumulant.cxx @@ -13,6 +13,7 @@ #include #include +#include #include using std::complex; @@ -103,7 +104,7 @@ void GFWCumulant::CreateComplexVectorArrayVarPower(int N, vector PowVec, in fPow = 0; fPt = Pt; fFilledPts = new bool[Pt]; - fPowVec = PowVec; + fPowVec = std::move(PowVec); fQvector = new complex**[fPt]; for (int i = 0; i < fPt; i++) { fQvector[i] = new complex*[fN]; diff --git a/PWGCF/GenericFramework/Core/GFWPowerArray.cxx b/PWGCF/GenericFramework/Core/GFWPowerArray.cxx index c3aeaf43edb..5ebd708f38f 100644 --- a/PWGCF/GenericFramework/Core/GFWPowerArray.cxx +++ b/PWGCF/GenericFramework/Core/GFWPowerArray.cxx @@ -9,107 +9,119 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFWPowerArray.cxx +/// \brief Compute required powers of Q-vectors for harmonic sets +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFWPowerArray.h" +#include + +#include #include -#include +#include #include +#include +#include #include #include -using std::string; -using std::vector; - int GFWPowerArray::getHighestHarmonic(const HarSet& inhar) { // Highest possible harmonic: sum of same-sign harmonics - int maxPos = 0, maxNeg = 0; - for (int val : inhar) + std::int64_t maxPos = 0, maxNeg = 0; + for (const int& val : inhar) { if (val > 0) maxPos += val; else - maxNeg += abs(val); - return maxPos > maxNeg ? maxPos : maxNeg; + maxNeg += std::abs(static_cast(val)); + if (maxPos >= std::numeric_limits::max() || maxNeg >= std::numeric_limits::max()) + throw std::overflow_error("Harmonic sum exceeds the supported range"); + } + return static_cast(std::max(maxPos, maxNeg)); }; -HarSet GFWPowerArray::TrimVec(HarSet hars, int ind) +HarSet GFWPowerArray::trimVec(const HarSet& hars, int ind) { HarSet retVec = hars; retVec.erase(retVec.begin() + ind); return retVec; }; -HarSet GFWPowerArray::AddConstant(HarSet hars, int offset) +HarSet GFWPowerArray::addConstant(const HarSet& hars, int offset) { HarSet retVec = hars; - for (int& val : retVec) + for (int& val : retVec) // o2-linter: disable=const-ref-in-for-loop (updates each harmonic) val += offset; return retVec; }; -void GFWPowerArray::FlushVectorToMaster(HarSet& masterVector, HarSet& comVec, const int& MaxPower) +void GFWPowerArray::flushVectorToMaster(HarSet& masterVector, const HarSet& comVec, int maxPower) { - int nPartLoc = MaxPower - comVec.size() + 1; - for (auto& val : comVec) { - int absVal = abs(val); + int nPartLoc = maxPower - static_cast(comVec.size()) + 1; + for (const int& val : comVec) { + int absVal = std::abs(val); if (masterVector.at(absVal) < nPartLoc) { masterVector.at(absVal) = nPartLoc; } } }; -void GFWPowerArray::RecursiveFunction(HarSet& masterVector, HarSet hars, int offset, const int& MaxPower) +void GFWPowerArray::recursiveFunction(HarSet& masterVector, const HarSet& hars, int offset, int maxPower, std::size_t startIndex) { - HarSet compVec = AddConstant(hars, offset); - FlushVectorToMaster(masterVector, compVec, MaxPower); - for (size_t i = 0; i < hars.size(); i++) - RecursiveFunction(masterVector, TrimVec(hars, i), offset + hars.at(i), MaxPower); - ; + HarSet compVec = addConstant(hars, offset); + flushVectorToMaster(masterVector, compVec, maxPower); + for (std::size_t i = startIndex; i < hars.size(); i++) + recursiveFunction(masterVector, trimVec(hars, static_cast(i)), offset + hars.at(i), maxPower, i); }; -void GFWPowerArray::PrintVector(const HarSet& singleSet) +void GFWPowerArray::printVector(const HarSet& singleSet) { - int vcSize = static_cast(singleSet.size()); - if (!vcSize) - printf("Vector is empty!\n"); - printf("{%i", singleSet[0]); - for (int i = 1; i < vcSize; i++) - printf(", %i", singleSet[i]); - printf("}\n"); + if (singleSet.empty()) { + LOGF(info, "Vector is empty!"); + return; + } + std::string output = "{" + std::to_string(singleSet[0]); + for (std::size_t i = 1; i < singleSet.size(); i++) + output += ", " + std::to_string(singleSet[i]); + output += "}"; + LOGF(info, "%s", output.c_str()); } -HarSet GFWPowerArray::GetPowerArray(vector inHarmonics) +HarSet GFWPowerArray::GetPowerArray(const std::vector& inHarmonics) // o2-linter: disable=name/function-variable (preserve existing public API) { // First, find maximum number of particle correlations ( = max power) and maximum (sum of) harmonics - int MaxHar = 0; - int nMaxPart = 0; - for (HarSet singleSet : inHarmonics) { + int maxHar = 0; + int maxPart = 0; + for (const HarSet& singleSet : inHarmonics) { int harSum = getHighestHarmonic(singleSet); - MaxHar = harSum > MaxHar ? harSum : MaxHar; + maxHar = harSum > maxHar ? harSum : maxHar; } - // Make a vector with MaxHar+1 entries (entry 0 for sum=0) - HarSet retVec = HarSet(MaxHar + 1); + // Make a vector with maxHar+1 entries (entry 0 for sum=0) + HarSet retVec = HarSet(maxHar + 1); // Then loop over all combinations and calculate max powers - for (HarSet singleSet : inHarmonics) { + for (const HarSet& singleSet : inHarmonics) { + if (singleSet.size() >= static_cast(std::numeric_limits::max())) + throw std::overflow_error("Harmonic set exceeds the supported particle count"); int lNPart = static_cast(singleSet.size()); // Total number of particles correlated - RecursiveFunction(retVec, singleSet, 0, lNPart); + recursiveFunction(retVec, singleSet, 0, lNPart, 0); // Harmonic sum = 0 is a special case. In principle all 0 cases with non-zero harmonics are captured by the function above, but to calculate normalization, we set all harmonics to 0. This means that sum=0 power is the max number of harmonics/particles being correlated - nMaxPart = (lNPart > nMaxPart) ? lNPart : nMaxPart; + maxPart = (lNPart > maxPart) ? lNPart : maxPart; } // Override the sum=0 power with the number of correlated particles - if (retVec[0] < nMaxPart) - retVec[0] = nMaxPart; + if (retVec[0] < maxPart) + retVec[0] = maxPart; // Need an extra power ( = 0) for all non-zero powers - for (int& val : retVec) + for (int& val : retVec) // o2-linter: disable=const-ref-in-for-loop (increments the required powers) if (val != 0) val++; return retVec; }; -void GFWPowerArray::PowerArrayTest() +void GFWPowerArray::PowerArrayTest() // o2-linter: disable=name/function-variable (preserve existing public API) { - vector AllHars = { + std::vector allHars = { HarSet{2}, HarSet{3}, HarSet{2, 2}, HarSet{3, 3}}; - printf("Input harmonics are:\n"); - for (HarSet inSet : AllHars) - PrintVector(inSet); - printf("The configuration of powers must then be:\n"); - auto vc = GetPowerArray(AllHars); - PrintVector(vc); + LOGF(info, "Input harmonics are:"); + for (const HarSet& inSet : allHars) + printVector(inSet); + LOGF(info, "The configuration of powers must then be:"); + auto vc = GetPowerArray(allHars); + printVector(vc); }; diff --git a/PWGCF/GenericFramework/Core/GFWPowerArray.h b/PWGCF/GenericFramework/Core/GFWPowerArray.h index 30d5444e727..cce3945d3f5 100644 --- a/PWGCF/GenericFramework/Core/GFWPowerArray.h +++ b/PWGCF/GenericFramework/Core/GFWPowerArray.h @@ -9,29 +9,29 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file GFWPowerArray.h/.cxx +/// \file GFWPowerArray.h /// \brief Class to compute necessary powers of Q-vectors based on input correlations /// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch #ifndef PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ #define PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ -#include +#include #include typedef std::vector HarSet; class GFWPowerArray { public: - static HarSet GetPowerArray(std::vector inHarmonics); - static void PowerArrayTest(); + static HarSet GetPowerArray(const std::vector& inHarmonics); // o2-linter: disable=name/function-variable (preserve existing public API) + static void PowerArrayTest(); // o2-linter: disable=name/function-variable (preserve existing public API) private: static int getHighestHarmonic(const HarSet& inhar); - static HarSet TrimVec(HarSet hars, int ind); - static HarSet AddConstant(HarSet hars, int offset); - static void FlushVectorToMaster(HarSet& masterVector, HarSet& comVec, const int& MaxPower); - static void RecursiveFunction(HarSet& masterVector, HarSet hars, int offset, const int& MaxPower); - static void PrintVector(const HarSet& singleSet); + static HarSet trimVec(const HarSet& hars, int ind); + static HarSet addConstant(const HarSet& hars, int offset); + static void flushVectorToMaster(HarSet& masterVector, const HarSet& comVec, int maxPower); + static void recursiveFunction(HarSet& masterVector, const HarSet& hars, int offset, int maxPower, std::size_t startIndex); + static void printVector(const HarSet& singleSet); }; #endif // PWGCF_GENERICFRAMEWORK_CORE_GFWPOWERARRAY_H_ diff --git a/PWGCF/GenericFramework/Core/GFWWeights.cxx b/PWGCF/GenericFramework/Core/GFWWeights.cxx index a0074866f5a..b707e252764 100644 --- a/PWGCF/GenericFramework/Core/GFWWeights.cxx +++ b/PWGCF/GenericFramework/Core/GFWWeights.cxx @@ -9,6 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file GFWWeights.cxx +/// \brief Implementation of corrections for the Generic Framework +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch + #include "GFWWeights.h" #include @@ -25,7 +29,49 @@ #include +#include #include +#include + +namespace +{ +constexpr int MCGenType = 2; + +TObjArray* cloneArray(TObjArray* source) +{ + if (!source) + return 0; + TObjArray* result = new TObjArray(); + result->SetName(source->GetName()); + result->SetOwner(kTRUE); + for (int i = 0; i < source->GetEntries(); i++) { + if (!source->At(i)) + continue; + TH3D* hist = reinterpret_cast(source->At(i)->Clone()); + hist->SetDirectory(0); + result->AddAt(hist, i); + } + return result; +} + +TH3D* cloneTH3D(TH3D* source) +{ + if (!source) + return 0; + TH3D* result = reinterpret_cast(source->Clone()); + result->SetDirectory(0); + return result; +} + +TH1D* cloneTH1D(TH1D* source) +{ + if (!source) + return 0; + TH1D* result = reinterpret_cast(source->Clone()); + result->SetDirectory(0); + return result; +} +} // namespace GFWWeights::GFWWeights() : TNamed("", ""), fDataFilled(kFALSE), @@ -49,6 +95,42 @@ GFWWeights::GFWWeights(const char* name) : TNamed(name, name), fAccInt(0), fNbinsPt(0), fbinsPt(0) {} +GFWWeights::GFWWeights(const GFWWeights& other) : TNamed(other), + fDataFilled(other.fDataFilled), + fMCFilled(other.fMCFilled), + fW_data(cloneArray(other.fW_data)), + fW_mcrec(cloneArray(other.fW_mcrec)), + fW_mcgen(cloneArray(other.fW_mcgen)), + fEffInt(cloneTH3D(other.fEffInt)), + fIntEff(cloneTH1D(other.fIntEff)), + fAccInt(cloneTH3D(other.fAccInt)), + fNbinsPt(other.fNbinsPt), + fbinsPt(0) +{ + if (other.fbinsPt) { + fbinsPt = new double[fNbinsPt + 1]; + for (int i = 0; i <= fNbinsPt; i++) + fbinsPt[i] = other.fbinsPt[i]; + } +} +GFWWeights& GFWWeights::operator=(const GFWWeights& other) +{ + if (this == &other) + return *this; + GFWWeights copy(other); + TNamed::operator=(other); + std::swap(fDataFilled, copy.fDataFilled); + std::swap(fMCFilled, copy.fMCFilled); + std::swap(fW_data, copy.fW_data); + std::swap(fW_mcrec, copy.fW_mcrec); + std::swap(fW_mcgen, copy.fW_mcgen); + std::swap(fEffInt, copy.fEffInt); + std::swap(fIntEff, copy.fIntEff); + std::swap(fAccInt, copy.fAccInt); + std::swap(fNbinsPt, copy.fNbinsPt); + std::swap(fbinsPt, copy.fbinsPt); + return *this; +} GFWWeights::~GFWWeights() { delete fW_data; @@ -78,14 +160,25 @@ void GFWWeights::init(bool AddData, bool AddMC) fbinsPt[1] = 1e6; } if (AddData) { + delete fAccInt; + fAccInt = 0; + delete fW_data; fW_data = new TObjArray(); fW_data->SetName("GFWWeights_Data"); fW_data->SetOwner(kTRUE); const char* tnd = getBinName(0, 0, Form("data_%s", this->GetName())); - fW_data->Add(new TH3D(tnd, ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10)); + TH3D* hist = new TH3D(tnd, ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10); // o2-linter: disable=external-pi (ROOT histogram axis uses double-precision two pi) + hist->SetDirectory(0); + fW_data->Add(hist); fDataFilled = kTRUE; } if (AddMC) { + delete fEffInt; + delete fIntEff; + fEffInt = 0; + fIntEff = 0; + delete fW_mcrec; + delete fW_mcgen; fW_mcrec = new TObjArray(); fW_mcrec->SetName("GFWWeights_MCRec"); fW_mcgen = new TObjArray(); @@ -94,8 +187,12 @@ void GFWWeights::init(bool AddData, bool AddMC) fW_mcgen->SetOwner(kTRUE); const char* tnr = getBinName(0, 0, "mcrec"); // all integrated over cent. anyway const char* tng = getBinName(0, 0, "mcgen"); // all integrated over cent. anyway - fW_mcrec->Add(new TH3D(tnr, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10)); - fW_mcgen->Add(new TH3D(tng, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10)); + TH3D* rec = new TH3D(tnr, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10); + TH3D* gen = new TH3D(tng, ";#it{p}_{T};#eta;v_{z}", fNbinsPt, 0, 20, 64, -1.6, 1.6, 40, -10, 10); + rec->SetDirectory(0); + gen->SetDirectory(0); + fW_mcrec->Add(rec); + fW_mcgen->Add(gen); reinterpret_cast(fW_mcrec->At(fW_mcrec->GetEntries() - 1))->GetXaxis()->Set(fNbinsPt, fbinsPt); reinterpret_cast(fW_mcgen->At(fW_mcgen->GetEntries() - 1))->GetXaxis()->Set(fNbinsPt, fbinsPt); fMCFilled = kTRUE; @@ -114,7 +211,7 @@ void GFWWeights::fill(double phi, double eta, double vz, double pt, double /*cen tar = fW_mcrec; pf = "mcrec"; } - if (htype == 2) { + if (htype == MCGenType) { tar = fW_mcgen; pf = "mcgen"; } @@ -122,8 +219,11 @@ void GFWWeights::fill(double phi, double eta, double vz, double pt, double /*cen return; TH3D* th3 = reinterpret_cast(tar->FindObject(getBinName(0, 0, pf))); // pT bin 0, V0M bin 0, since all integrated if (!th3) { - if (!htype) - tar->Add(new TH3D(getBinName(0, 0, pf), ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10)); // 0,0 since all integrated + if (!htype) { + TH3D* hist = new TH3D(getBinName(0, 0, pf), ";#varphi;#eta;v_{z}", 60, 0, TMath::TwoPi(), 64, -1.6, 1.6, 40, -10, 10); // o2-linter: disable=external-pi (ROOT histogram axis uses double-precision two pi) + hist->SetDirectory(0); + tar->Add(hist); + } th3 = reinterpret_cast(tar->At(tar->GetEntries() - 1)); } th3->Fill(htype ? pt : phi, eta, vz, weight); @@ -134,13 +234,13 @@ double GFWWeights::getWeight(double phi, double eta, double vz, double pt, doubl const char* pf = ""; if (htype == 0) { tar = fW_data; - pf = "data"; + pf = Form("data_%s", this->GetName()); } if (htype == 1) { tar = fW_mcrec; pf = "mcrec"; } - if (htype == 2) { + if (htype == MCGenType) { tar = fW_mcgen; pf = "mcgen"; } @@ -161,6 +261,8 @@ double GFWWeights::getNUA(double phi, double eta, double vz) { if (!fAccInt) createNUA(); + if (!fAccInt) + return 1; int xind = fAccInt->GetXaxis()->FindBin(phi); int etaind = fAccInt->GetYaxis()->FindBin(eta); int vzind = fAccInt->GetZaxis()->FindBin(vz); @@ -173,6 +275,8 @@ double GFWWeights::getNUE(double pt, double eta, double vz) { if (!fEffInt) createNUE(); + if (!fEffInt) + return 1; int xind = fEffInt->GetXaxis()->FindBin(pt); int etaind = fEffInt->GetYaxis()->FindBin(eta); int vzind = fEffInt->GetZaxis()->FindBin(vz); @@ -212,7 +316,7 @@ void GFWWeights::mcToEfficiency() }; void GFWWeights::rebinNUA(int nX, int nY, int nZ) { - if (fW_data->GetEntries() < 1) + if (!fW_data || fW_data->GetEntries() < 1) return; for (int i = 0; i < fW_data->GetEntries(); i++) { reinterpret_cast(fW_data->At(i))->RebinX(nX); @@ -227,44 +331,47 @@ void GFWWeights::createNUA(bool IntegrateOverCentAndPt) return; } TH1D* h1; - if (fW_data->GetEntries() < 1) + if (!fW_data || fW_data->GetEntries() < 1) return; - if (IntegrateOverCentAndPt) { - if (fAccInt) - delete fAccInt; - fAccInt = reinterpret_cast(fW_data->At(0)->Clone("IntegratedAcceptance")); - fAccInt->Sumw2(); - for (int etai = 1; etai <= fAccInt->GetNbinsY(); etai++) { - fAccInt->GetYaxis()->SetRange(etai, etai); + if (fAccInt) + delete fAccInt; + fAccInt = reinterpret_cast(fW_data->At(0)->Clone("IntegratedAcceptance")); + fAccInt->SetDirectory(0); + fAccInt->Sumw2(); + for (int etai = 1; etai <= fAccInt->GetNbinsY(); etai++) { + fAccInt->GetYaxis()->SetRange(etai, etai); + if (fAccInt->Integral() < 1) + continue; + for (int vzi = 1; vzi <= fAccInt->GetNbinsZ(); vzi++) { + fAccInt->GetZaxis()->SetRange(vzi, vzi); if (fAccInt->Integral() < 1) continue; - for (int vzi = 1; vzi <= fAccInt->GetNbinsZ(); vzi++) { - fAccInt->GetZaxis()->SetRange(vzi, vzi); - if (fAccInt->Integral() < 1) - continue; - h1 = reinterpret_cast(fAccInt->Project3D("x")); - double maxv = h1->GetMaximum(); - for (int phii = 1; phii <= h1->GetNbinsX(); phii++) { - fAccInt->SetBinContent(phii, etai, vzi, fAccInt->GetBinContent(phii, etai, vzi) / maxv); - fAccInt->SetBinError(phii, etai, vzi, fAccInt->GetBinError(phii, etai, vzi) / maxv); - } - delete h1; + h1 = reinterpret_cast(fAccInt->Project3D("x")); + double maxv = h1->GetMaximum(); + for (int phii = 1; phii <= h1->GetNbinsX(); phii++) { + fAccInt->SetBinContent(phii, etai, vzi, fAccInt->GetBinContent(phii, etai, vzi) / maxv); + fAccInt->SetBinError(phii, etai, vzi, fAccInt->GetBinError(phii, etai, vzi) / maxv); } - fAccInt->GetZaxis()->SetRange(1, fAccInt->GetNbinsZ()); + delete h1; } - fAccInt->GetYaxis()->SetRange(1, fAccInt->GetNbinsY()); - return; + fAccInt->GetZaxis()->SetRange(1, fAccInt->GetNbinsZ()); } + fAccInt->GetYaxis()->SetRange(1, fAccInt->GetNbinsY()); }; TH1D* GFWWeights::getdNdPhi() { + if (!fW_data || !fW_data->GetEntries()) + return 0; TH3D* temph = reinterpret_cast(fW_data->At(0)->Clone("tempH3")); TH1D* reth = reinterpret_cast(temph->Project3D("x")); reth->SetName("RetHist"); + reth->SetDirectory(0); delete temph; double max = reth->GetMaximum(); - if (max == 0) + if (max == 0) { + delete reth; return 0; + } for (int phi = 1; phi <= reth->GetNbinsX(); phi++) { if (reth->GetBinContent(phi) == 0) continue; @@ -281,32 +388,40 @@ void GFWWeights::createNUE(bool IntegrateOverCentrality) } TH3D* num = 0; TH3D* den = 0; - if (fW_mcrec->GetEntries() < 1 || fW_mcgen->GetEntries() < 1) + if (!fW_mcrec || !fW_mcgen || fW_mcrec->GetEntries() < 1 || fW_mcgen->GetEntries() < 1) return; - if (IntegrateOverCentrality) { - num = reinterpret_cast(fW_mcrec->At(0)); - den = reinterpret_cast(fW_mcgen->At(0)); - num->Sumw2(); - den->Sumw2(); - num->RebinY(2); - den->RebinY(2); - num->RebinZ(5); - den->RebinZ(5); - fEffInt = reinterpret_cast(num->Clone("Efficiency_Integrated")); - fEffInt->Divide(den); - return; - } + num = reinterpret_cast(fW_mcrec->At(0)); + den = reinterpret_cast(fW_mcgen->At(0)); + num->Sumw2(); + den->Sumw2(); + num->RebinY(2); + den->RebinY(2); + num->RebinZ(5); + den->RebinZ(5); + delete fEffInt; + fEffInt = reinterpret_cast(num->Clone("Efficiency_Integrated")); + fEffInt->SetDirectory(0); + fEffInt->Divide(den); }; -void GFWWeights::readAndMerge(TString filelinks, TString listName, bool addData, bool addRec, bool addGen) +void GFWWeights::readAndMerge(const TString& filelinks, const TString& listName, bool addData, bool addRec, bool addGen) { FILE* flist = fopen(filelinks.Data(), "r"); + if (!flist) { + LOGF(warning, "Could not open file list %s!\n", filelinks.Data()); + return; + } char str[150]; int nFiles = 0; - while (fscanf(flist, "%s\n", str) == 1) + while (fscanf(flist, "%149s", str) == 1) nFiles++; - rewind(flist); + if (fseek(flist, 0, SEEK_SET) != 0) { + LOGF(warning, "Could not rewind file list %s!\n", filelinks.Data()); + fclose(flist); + return; + } if (nFiles == 0) { LOGF(info, "No files to read!\n"); + fclose(flist); return; } if (!fW_data && addData) { @@ -326,19 +441,31 @@ void GFWWeights::readAndMerge(TString filelinks, TString listName, bool addData, } TFile* tf = 0; for (int i = 0; i < nFiles; i++) { - auto retVal = fscanf(flist, "%s\n", str); - (void)retVal; + if (fscanf(flist, "%149s", str) != 1) + break; tf = new TFile(str, "READ"); if (tf->IsZombie()) { LOGF(warning, "Could not open file %s!\n", str); tf->Close(); + delete tf; + continue; + } + TObject* obj = tf->Get(listName.Data()); + TList* tl = dynamic_cast(obj); + if (!tl) { + LOGF(warning, "Could not fetch list %s from %s\n", listName.Data(), str); + delete obj; + tf->Close(); + delete tf; continue; } - TList* tl = reinterpret_cast(tf->Get(listName.Data())); - GFWWeights* tw = reinterpret_cast(tl->FindObject(this->GetName())); + GFWWeights* tw = dynamic_cast(tl->FindObject(this->GetName())); if (!tw) { LOGF(warning, "Could not fetch weights object from %s\n", str); + tl->Delete(); + delete tl; tf->Close(); + delete tf; continue; } if (addData) @@ -347,9 +474,14 @@ void GFWWeights::readAndMerge(TString filelinks, TString listName, bool addData, addArray(fW_mcrec, tw->getRecArray()); if (addGen) addArray(fW_mcgen, tw->getGenArray()); - tf->Close(); + tl->Remove(tw); delete tw; + tl->Delete(); + delete tl; + tf->Close(); + delete tf; } + fclose(flist); }; void GFWWeights::addArray(TObjArray* targ, TObjArray* sour) { @@ -373,15 +505,20 @@ void GFWWeights::overwriteNUA() { if (!fAccInt) createNUA(); + if (!fAccInt || !fW_data || !fW_data->GetEntries()) + return; TString ts(fW_data->At(0)->GetName()); TH3D* trash = reinterpret_cast(fW_data->RemoveAt(0)); delete trash; - fW_data->Add(reinterpret_cast(fAccInt->Clone(ts.Data()))); + TH3D* hist = reinterpret_cast(fAccInt->Clone(ts.Data())); + hist->SetDirectory(0); + fW_data->AddAt(hist, 0); delete fAccInt; + fAccInt = 0; } -Long64_t GFWWeights::Merge(TCollection* collist) +Long64_t GFWWeights::Merge(TCollection* collist) // o2-linter: disable=root/entity,name/function-variable (ROOT Merge requires this signature) { - Long64_t nmerged = 0; + Long64_t nmerged = 0; // o2-linter: disable=root/entity (matches ROOT Merge return type) if (!fW_data) { fW_data = new TObjArray(); fW_data->SetName("Weights_Data"); @@ -397,12 +534,12 @@ Long64_t GFWWeights::Merge(TCollection* collist) fW_mcgen->SetName("Weights_MCGen"); fW_mcgen->SetOwner(kTRUE); } - GFWWeights* l_w = 0; - TIter all_w(collist); - while ((l_w = (reinterpret_cast(all_w())))) { - addArray(fW_data, l_w->getDataArray()); - addArray(fW_mcrec, l_w->getRecArray()); - addArray(fW_mcgen, l_w->getGenArray()); + GFWWeights* weight = 0; + TIter allWeights(collist); + while ((weight = (reinterpret_cast(allWeights())))) { + addArray(fW_data, weight->getDataArray()); + addArray(fW_mcrec, weight->getRecArray()); + addArray(fW_mcgen, weight->getGenArray()); nmerged++; } return nmerged; @@ -433,8 +570,10 @@ TH1D* GFWWeights::getIntegratedEfficiencyHist() den->Add(reinterpret_cast(fW_mcgen->At(i))); TH1D* num1d = reinterpret_cast(num->Project3D("x")); num1d->SetName("retHist"); + num1d->SetDirectory(0); num1d->Sumw2(); TH1D* den1d = reinterpret_cast(den->Project3D("x")); + den1d->SetDirectory(0); den1d->Sumw2(); num1d->Divide(den1d); delete num; @@ -462,6 +601,8 @@ double GFWWeights::getIntegratedEfficiency(double pt) } TH1D* GFWWeights::getEfficiency(double etamin, double etamax, double vzmin, double vzmax) { + if (!fW_mcrec || !fW_mcgen || !fW_mcrec->GetEntries() || !fW_mcgen->GetEntries()) + return 0; TH3D* num = reinterpret_cast(fW_mcrec->At(0)->Clone("Numerator")); for (int i = 1; i < fW_mcrec->GetEntries(); i++) num->Add(reinterpret_cast(fW_mcrec->At(i))); @@ -478,6 +619,8 @@ TH1D* GFWWeights::getEfficiency(double etamin, double etamax, double vzmin, doub den->GetZaxis()->SetRange(vz1, vz2); TH1D* num1d = reinterpret_cast(num->Project3D("x")); TH1D* den1d = reinterpret_cast(den->Project3D("x")); + num1d->SetDirectory(0); + den1d->SetDirectory(0); delete num; delete den; num1d->Sumw2(); @@ -498,15 +641,21 @@ void GFWWeights::mergeWeights(GFWWeights* other) } void GFWWeights::setTH3D(TH3D* th3d) { + if (!th3d) + return; if (!fW_data) { fW_data = new TObjArray(); fW_data->SetName("GFWWeights_Data"); fW_data->SetOwner(kTRUE); - fW_data->Add(th3d); + TH3D* hist = reinterpret_cast(th3d->Clone()); + hist->SetDirectory(0); + fW_data->Add(hist); return; } TString ts(fW_data->At(0)->GetName()); TH3D* trash = reinterpret_cast(fW_data->RemoveAt(0)); delete trash; - fW_data->Add(reinterpret_cast(th3d->Clone(ts.Data()))); + TH3D* hist = reinterpret_cast(th3d->Clone(ts.Data())); + hist->SetDirectory(0); + fW_data->AddAt(hist, 0); } diff --git a/PWGCF/GenericFramework/Core/GFWWeights.h b/PWGCF/GenericFramework/Core/GFWWeights.h index 9cecbd337e6..bf255a731e5 100644 --- a/PWGCF/GenericFramework/Core/GFWWeights.h +++ b/PWGCF/GenericFramework/Core/GFWWeights.h @@ -31,6 +31,8 @@ class GFWWeights : public TNamed public: GFWWeights(); explicit GFWWeights(const char* name); + GFWWeights(const GFWWeights& other); + GFWWeights& operator=(const GFWWeights& other); ~GFWWeights(); void init(bool AddData = kTRUE, bool AddM = kTRUE); void fill(double phi, double eta, double vz, double pt, double cent, int htype, double weight = 1); // htype: 0 for data, 1 for mc rec, 2 for mc gen @@ -51,27 +53,27 @@ class GFWWeights : public TNamed double getIntegratedEfficiency(double pt); void setDataFilled(bool newval) { fDataFilled = newval; } void setMCFilled(bool newval) { fMCFilled = newval; } - void readAndMerge(TString filelinks, TString listName = "OutputList", bool addData = kTRUE, bool addRec = kTRUE, bool addGen = kTRUE); + void readAndMerge(const TString& filelinks, const TString& listName = "OutputList", bool addData = kTRUE, bool addRec = kTRUE, bool addGen = kTRUE); void setPtBins(int Nbins, double* bins); - Long64_t Merge(TCollection* collist); + Long64_t Merge(TCollection* collist); // o2-linter: disable=root/entity,name/function-variable (ROOT Merge requires this signature) void rebinNUA(int nX = 1, int nY = 2, int nZ = 5); void overwriteNUA(); TH1D* getdNdPhi(); TH1D* getEfficiency(double etamin, double etamax, double vzmin, double vzmax); void mergeWeights(GFWWeights* other); - void setTH3D(TH3D* th3d); + void setTH3D(TH3D* th3d); // Stores a clone of the input histogram private: bool fDataFilled; bool fMCFilled; - TObjArray* fW_data; - TObjArray* fW_mcrec; - TObjArray* fW_mcgen; - TH3D* fEffInt; //! - TH1D* fIntEff; //! - TH3D* fAccInt; //! - int fNbinsPt; //! do not store - double* fbinsPt; //! do not store + TObjArray* fW_data; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TObjArray* fW_mcrec; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TObjArray* fW_mcgen; // o2-linter: disable=name/function-variable (persisted ROOT member name) + TH3D* fEffInt; //! + TH1D* fIntEff; //! + TH3D* fAccInt; //! + int fNbinsPt; //! do not store + double* fbinsPt; //! do not store void addArray(TObjArray* targ, TObjArray* sour); const char* getBinName(double /*ptv*/, double /*v0mv*/, const char* pf = "") { diff --git a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h index 3fea9523064..a9a3da8c05e 100644 --- a/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h +++ b/PWGCF/GenericFramework/Core/GenericFrameworkLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ -#define PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -28,5 +27,7 @@ #pragma link C++ class o2::analysis::genericframework::GFWBinningCuts + ; #pragma link C++ class o2::analysis::genericframework::GFWRegions + ; #pragma link C++ class o2::analysis::genericframework::GFWCorrConfigs + ; - -#endif // PWGCF_GENERICFRAMEWORK_CORE_GENERICFRAMEWORKLINKDEF_H_ +#pragma link C++ class o2::analysis::genericframework::flowptcontainer::PtMomentGroup + ; +#pragma link C++ class o2::analysis::genericframework::flowptcontainer::PtMomentConfig + ; +#pragma link C++ class std::vector < o2::analysis::genericframework::flowptcontainer::PtMomentGroup> + ; +#pragma link C++ class std::vector < o2::analysis::genericframework::flowptcontainer::PtMomentConfig> + ; diff --git a/PWGCF/GenericFramework/DataModel/GfwDerived.h b/PWGCF/GenericFramework/DataModel/GfwDerived.h new file mode 100644 index 00000000000..9fc0edf7f5f --- /dev/null +++ b/PWGCF/GenericFramework/DataModel/GfwDerived.h @@ -0,0 +1,146 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GfwDerived.h +/// \brief Derived collision and track tables for the GFW v02 flow analysis. +/// +/// Selection columns are bit masks. A later analysis keeps a row when every bit +/// set in its mask is also set on the row (`ncheckbit` / `(value & mask) == mask`). +/// PID is stored only as bits. The nominal, tight, and loose windows are fixed +/// at production. +/// +/// \author Maxim Virta, AU, maxim.virta@cern.ch + +#ifndef PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ +#define PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ + +#include +#include + +#include + +#include + +namespace o2::aod +{ +namespace gfwcollision +{ +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality estimator chosen at production +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Global-track multiplicity, multNTracksGlobal +DECLARE_SOA_COLUMN(EventSel, eventSel, uint16_t); //! Event-selection bitmask, see EventSelection + +/// One bit per event-level decision. Bits record that the decision passed. +/// sel8 is required by the producer and is not stored here. +enum EventSelection : uint16_t { + kNoSameBunchPileup = BIT(0), + kIsGoodZvtxFT0vsPV = BIT(1), + kNoCollInTimeRangeStandard = BIT(2), + kNoCollInRofStandard = BIT(3), + kNoHighMultCollInPrevRof = BIT(4), + kIsVertexITSTPC = BIT(5), + kIsGoodITSLayersAll = BIT(6), + kNoTimeFrameBorder = BIT(7), + kNoITSROFrameBorder = BIT(8), + kOccupancy = BIT(9), //! track occupancy is within the production threshold + kNotTVXinTRD = BIT(10), //! collision is not a TRD-triggered TVX event + kGoodVertex = BIT(11), //! PV contributor and z-resolution quality + kMultCorr = BIT(12) //! multiplicity-correlation cut +}; + +inline constexpr uint16_t DefaultEventMask = 0; +} // namespace gfwcollision + +DECLARE_SOA_TABLE(GfwCollisions, "AOD", "GFWCOLLISION", //! + o2::soa::Index<>, + bc::RunNumber, + collision::PosZ, + gfwcollision::Centrality, + gfwcollision::Multiplicity, + timestamp::Timestamp, + gfwcollision::EventSel); +using GfwCollision = GfwCollisions::iterator; + +namespace gfwtrack +{ +DECLARE_SOA_INDEX_COLUMN(GfwCollision, gfwCollision); //! Parent derived collision +DECLARE_SOA_COLUMN(Pt, pt, float); //! pT (GeV/c) +DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity +DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuthal angle +DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); //! Track has a TOF match +DECLARE_SOA_COLUMN(TrackSel, trackSel, uint32_t); //! Track and PID bitmask, see TrackSelection + +/// Track-quality bits occupy 0-13. TPC PID bits occupy 14-22 and TOF PID bits occupy 23-31. +/// Global-track, ITS-cluster, and ITS-chi2 requirements are applied by the producer and are not stored. +/// DCA bits are |DCA| <= nSigma times a pT-dependent shape. +/// A PID bit is set when low * scale < nSigma < up * scale. +/// Above the TOF pT cut, every PID bit stays unset when the track has no TOF match. +/// Within each detector, PidWindowBits covers π/K/p, followed by the tight window and then the loose window. +enum TrackSelection : uint32_t { + kDCAxy = BIT(0), //! |DCAxy| within the nominal nSigma times f_xy(pT) + kDCAxyTight = BIT(1), + kDCAxyLoose = BIT(2), + kDCAz = BIT(3), //! |DCAz| within the nominal nSigma times f_z(pT) + kDCAzTight = BIT(4), + kDCAzLoose = BIT(5), // DCA bit selections + + kTPCXrows = BIT(6), + kTPCXrowsTight = BIT(7), + kTPCXrowsLoose = BIT(8), + kTPCCls = BIT(9), + kTPCClsTight = BIT(10), + kTPCClsLoose = BIT(11), + kChi2TPC = BIT(12), + kChi2TPCLoose = BIT(13), // TPC bit selections + + // TPC PID selections: nominal, tight, then loose + kTpcPi = BIT(14), + kTpcKa = BIT(15), + kTpcPr = BIT(16), + kTpcPiTight = BIT(17), + kTpcKaTight = BIT(18), + kTpcPrTight = BIT(19), + kTpcPiLoose = BIT(20), + kTpcKaLoose = BIT(21), + kTpcPrLoose = BIT(22), + // TOF PID selections: nominal, tight, then loose + kTofPi = BIT(23), + kTofKa = BIT(24), + kTofPr = BIT(25), + kTofPiTight = BIT(26), + kTofKaTight = BIT(27), + kTofPrTight = BIT(28), + kTofPiLoose = BIT(29), + kTofKaLoose = BIT(30), + kTofPrLoose = BIT(31) +}; + +/// Quality bits corresponding to the default flowGfwV02 track filter plus trackSelected(). +inline constexpr uint32_t NominalMask = static_cast(kDCAxy | kDCAz | kTPCXrows | kTPCCls | kChi2TPC); +/// Loosest quality bits. Production can optionally require these while still leaving the nominal cuts free. +inline constexpr uint32_t LooseSkimMask = static_cast(kDCAxyLoose | kDCAzLoose | kTPCXrowsLoose | kTPCClsLoose | kChi2TPCLoose); +/// Tracks that enter the multiplicity-correlation count. Global track and ITS chi2 are already required by the producer. +inline constexpr uint32_t MultCorrTrackMask = static_cast(kDCAz | kChi2TPC); + +inline constexpr int PidWindowBits = 3; +inline constexpr int PidWindowNominal = 0; +inline constexpr int PidWindowTight = 1; +inline constexpr int PidWindowLoose = 2; +} // namespace gfwtrack + +DECLARE_SOA_TABLE(GfwTracks, "AOD", "GFWTRACK", //! + o2::soa::Index<>, + gfwtrack::GfwCollisionId, + gfwtrack::Pt, gfwtrack::Eta, gfwtrack::Phi, + gfwtrack::HasTOF, gfwtrack::TrackSel); +using GfwTrack = GfwTracks::iterator; +} // namespace o2::aod + +#endif // PWGCF_GENERICFRAMEWORK_DATAMODEL_GFWDERIVED_H_ diff --git a/PWGCF/GenericFramework/TableProducer/CMakeLists.txt b/PWGCF/GenericFramework/TableProducer/CMakeLists.txt index bbfd7adac2b..ab012ec889e 100644 --- a/PWGCF/GenericFramework/TableProducer/CMakeLists.txt +++ b/PWGCF/GenericFramework/TableProducer/CMakeLists.txt @@ -8,3 +8,8 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(flow-gfw-derived-producer + SOURCES flowGfwDerivedProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx b/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx new file mode 100644 index 00000000000..1bfed843783 --- /dev/null +++ b/PWGCF/GenericFramework/TableProducer/flowGfwDerivedProducer.cxx @@ -0,0 +1,443 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowGfwDerivedProducer.cxx +/// \brief Produce the GFW derived dataset used by flowGfwV02. +/// +/// Keeps sel8 collisions and global tracks that pass the ITS cluster and ITS chi2 +/// cuts. Stores the event mask and the combined track and PID bitmask. PID bits +/// stay unset above the TOF pT cut when the track has no TOF match. Run it after +/// event selection, track selection, multiplicity, centrality, and the TPC/TOF PID +/// helpers (pid-tpc, pid-tof-full, pid-tof-beta). +/// +/// \author Maxim Virta, NBI, maxim.virta@cern.ch + +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +constexpr float VertexResMax = 0.25f; +constexpr int VertexContribMin = 20; +constexpr float DcaShapeMinPt = 0.001f; +constexpr float DcaShapeMaxPt = 100.f; +constexpr float DcaZReferenceNsigma = 7.f; +constexpr float NsigmaScaleNominal = 1.f; +constexpr float NsigmaScaleTight = 2.f / 3.f; +constexpr float NsigmaScaleLoose = 4.f / 3.f; +constexpr uint32_t NsigmaRows = 6; +constexpr uint32_t NsigmaCols = 3; +constexpr int NsigmaUpPi = 0; +constexpr int NsigmaUpKa = 1; +constexpr int NsigmaUpPr = 2; +constexpr int NsigmaLowPi = 3; +constexpr int NsigmaLowKa = 4; +constexpr int NsigmaLowPr = 5; +constexpr int NsigmaColTpc = 0; +constexpr int NsigmaColTof = 1; +constexpr std::array, NsigmaRows> NsigmaDefault = {{{{1.1f, 2.1f, 3.1f}}, {{1.2f, 2.2f, 3.2f}}, {{1.3f, 2.3f, 3.3f}}, {{-1.1f, -2.1f, -3.1f}}, {{-1.2f, -2.2f, -3.2f}}, {{-1.3f, -2.3f, -3.3f}}}}; +} // namespace + +struct FlowGfwDerivedProducer { + using CollisionsWithInfo = soa::Join; + using TracksWithPid = soa::Join; + + enum CentEstimator : int { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT + }; + + Configurable cfgCutVertex{"cfgCutVertex", 10.f, "Maximum |z| of collisions written to the derived dataset (cm)"}; + Configurable cfgPtMin{"cfgPtMin", 0.2f, "Minimum track pT written to the derived dataset (GeV/c)"}; + Configurable cfgPtMax{"cfgPtMax", 10.f, "Maximum track pT written to the derived dataset (GeV/c)"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "Maximum |eta| of tracks written to the derived dataset"}; + Configurable cfgSkipEmptyEvents{"cfgSkipEmptyEvents", true, "Do not write collisions that keep no tracks"}; + Configurable cfgRequireLooseTrackBits{"cfgRequireLooseTrackBits", false, "Keep only tracks that pass the loose DCA and TPC-crossed-rows bits"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A; 4:NTPV; 5:NGlobal; 6:MFT"}; + Configurable cfgOccupancyMax{"cfgOccupancyMax", 2000, "Maximum track occupancy for the occupancy bit; negative disables the cut"}; + + Configurable cfgDCAxyNSigma{"cfgDCAxyNSigma", 7.f, "Nominal pT-dependent DCAxy nSigma. Non-positive disables the nominal bit"}; + Configurable cfgDCAxyNSigmaTight{"cfgDCAxyNSigmaTight", 5.f, "Tight pT-dependent DCAxy nSigma"}; + Configurable cfgDCAxyNSigmaLoose{"cfgDCAxyNSigmaLoose", 10.f, "Loose pT-dependent DCAxy nSigma"}; + Configurable cfgDCAxy{"cfgDCAxy", "(0.0026+0.005/(x^1.01))", "1-sigma shape of the pT-dependent DCAxy cut; a bit passes when |DCAxy| <= nSigma*shape(pT)"}; + Configurable cfgDCAzNSigma{"cfgDCAzNSigma", 7.f, "Nominal DCAz nSigma relative to the 7-sigma pT shape. Non-positive disables the nominal bit"}; + Configurable cfgDCAzNSigmaTight{"cfgDCAzNSigmaTight", 5.f, "Tight DCAz nSigma"}; + Configurable cfgDCAzNSigmaLoose{"cfgDCAzNSigmaLoose", 10.f, "Loose DCAz nSigma"}; + Configurable cfgDCAz{"cfgDCAz", "(0.0105+0.0350/(x^1.1))", "7-sigma pT-dependent DCAz shape; a bit passes when |DCAz| <= (nSigma/7)*shape(pT)"}; + Configurable cfgNTPCXrows{"cfgNTPCXrows", 70.f, "Nominal minimum number of TPC crossed rows"}; + Configurable cfgNTPCXrowsTight{"cfgNTPCXrowsTight", 80.f, "Tight minimum number of TPC crossed rows"}; + Configurable cfgNTPCXrowsLoose{"cfgNTPCXrowsLoose", 60.f, "Loose minimum number of TPC crossed rows"}; + Configurable cfgNTPCCls{"cfgNTPCCls", 50.f, "Nominal minimum number of found TPC clusters"}; + Configurable cfgNTPCClsTight{"cfgNTPCClsTight", 60.f, "Tight minimum number of found TPC clusters"}; + Configurable cfgNTPCClsLoose{"cfgNTPCClsLoose", 40.f, "Loose minimum number of found TPC clusters"}; + Configurable cfgMinNITSCls{"cfgMinNITSCls", 5.f, "Minimum number of ITS clusters required to keep a track"}; + Configurable cfgChi2PrTPCCls{"cfgChi2PrTPCCls", 2.5f, "Nominal maximum chi2 per TPC cluster"}; + Configurable cfgChi2PrTPCClsLoose{"cfgChi2PrTPCClsLoose", 4.f, "Loose maximum chi2 per TPC cluster"}; + Configurable cfgChi2PrITSCls{"cfgChi2PrITSCls", 36.f, "Maximum chi2 per ITS cluster required to keep a track"}; + + Configurable> cfgMultGlobalCutPars{"cfgMultGlobalCutPars", std::vector{2272.16, -76.6932, 1.01204, -0.00631545, 1.59868e-05, 136.336, -4.97006, 0.121199, -0.0015921, 7.66197e-06}, "Global vs FT0C multiplicity cut parameters"}; + Configurable> cfgMultPVCutPars{"cfgMultPVCutPars", std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}, "PV vs FT0C multiplicity cut parameters"}; + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.223013, 0.715849, 0.664242, 0.0829653, -0.000503733, 1.21185e-06}, "Global vs PV multiplicity cut parameters"}; + Configurable cfgMultCorrHighCutFunction{"cfgMultCorrHighCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Upper multiplicity-correlation cut"}; + Configurable cfgMultCorrLowCutFunction{"cfgMultCorrLowCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Lower multiplicity-correlation cut"}; + Configurable cfgMultGlobalPVCorrCutFunction{"cfgMultGlobalPVCorrCutFunction", "[0] + [1]*x + 3*([2] + [3]*x + [4]*x*x + [5]*x*x*x)", "Global vs PV multiplicity-correlation cut"}; + Configurable cfgTofPtCut{"cfgTofPtCut", 0.5f, "Above this pT, PID bits stay unset when the track has no TOF match"}; + Configurable> nSigmas{"nSigmas", {NsigmaDefault.front().data(), NsigmaRows, NsigmaCols, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Nominal nSigma windows for TPC, TOF, and ITS. ITS is accepted so the analysis JSON can be reused and is not stored as a bit"}; + Configurable cfgNsigmaScaleTight{"cfgNsigmaScaleTight", static_cast(NsigmaScaleTight), "Scale applied to the nominal nSigma bounds for the tight PID bits"}; + Configurable cfgNsigmaScaleLoose{"cfgNsigmaScaleLoose", static_cast(NsigmaScaleLoose), "Scale applied to the nominal nSigma bounds for the loose PID bits"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter trackFilter = (aod::track::pt > cfgPtMin) && (aod::track::pt < cfgPtMax) && (nabs(aod::track::eta) < cfgEtaMax); + + Produces outputCollisions; + Produces outputTracks; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::unique_ptr fDcaXyShape; + std::unique_ptr fDcaZShape; + std::array tpcNsigmaCut{}; + std::array tofNsigmaCut{}; + std::unique_ptr fMultPVCutLow; + std::unique_ptr fMultPVCutHigh; + std::unique_ptr fMultCutLow; + std::unique_ptr fMultCutHigh; + std::unique_ptr fMultPVGlobalCutHigh; + + void init(InitContext const&) + { + const AxisSpec axisCounter{1, 0., 1., ""}; + registry.add("events", "Written derived collisions", HistType::kTH1F, {axisCounter}); + registry.add("tracks", "Written derived tracks", HistType::kTH1F, {axisCounter}); + + const std::string dcaXyFormula = "[0]*" + cfgDCAxy.value; + fDcaXyShape = std::make_unique("gfwDerivedDcaXy", dcaXyFormula.c_str(), DcaShapeMinPt, DcaShapeMaxPt); + fDcaXyShape->SetParameter(0, 1.f); + fDcaZShape = std::make_unique("gfwDerivedDcaZ", cfgDCAz.value.c_str(), DcaShapeMinPt, DcaShapeMaxPt); + + const auto nsigma = nSigmas->getData(); + for (uint32_t row = 0; row < NsigmaRows; ++row) { + tpcNsigmaCut[row] = nsigma(row, NsigmaColTpc); + tofNsigmaCut[row] = nsigma(row, NsigmaColTof); + } + + constexpr double MultCorrMaxCent = 100.; + constexpr double MultCorrMaxNch = 3000.; + fMultPVCutLow = std::make_unique("gfwDerivedMultPVCutLow", cfgMultCorrLowCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultPVCutLow->SetParameters(cfgMultPVCutPars.value.data()); + fMultPVCutHigh = std::make_unique("gfwDerivedMultPVCutHigh", cfgMultCorrHighCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultPVCutHigh->SetParameters(cfgMultPVCutPars.value.data()); + fMultCutLow = std::make_unique("gfwDerivedMultCutLow", cfgMultCorrLowCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultCutLow->SetParameters(cfgMultGlobalCutPars.value.data()); + fMultCutHigh = std::make_unique("gfwDerivedMultCutHigh", cfgMultCorrHighCutFunction.value.c_str(), 0., MultCorrMaxCent); + fMultCutHigh->SetParameters(cfgMultGlobalCutPars.value.data()); + fMultPVGlobalCutHigh = std::make_unique("gfwDerivedMultPVGlobalCutHigh", cfgMultGlobalPVCorrCutFunction.value.c_str(), 0., MultCorrMaxNch); + fMultPVGlobalCutHigh->SetParameters(cfgMultGlobalPVCutPars.value.data()); + } + + template + float centrality(TCollision const& collision) const + { + switch (cfgCentEstimator.value) { + case CentFT0C: + return collision.centFT0C(); + case CentFT0CVariant1: + return collision.centFT0CVariant1(); + case CentFT0M: + return collision.centFT0M(); + case CentFV0A: + return collision.centFV0A(); + case CentNTPV: + return collision.centNTPV(); + case CentNGlobal: + return collision.centNGlobal(); + case CentMFT: + return collision.centMFT(); + default: + return collision.centFT0C(); + } + } + + template + bool vertexQuality(TCollision const& collision) const + { + if (collision.numContrib() <= 1) { + return false; + } + const float zRes = std::sqrt(collision.covZZ()); + return !(zRes > VertexResMax && collision.numContrib() < VertexContribMin); + } + + template + bool multiplicityCorrelated(TCollision const& collision, float cent, float multTrk) const + { + const float multPV = collision.multNTracksPV(); + if (multPV < fMultPVCutLow->Eval(cent) || multPV > fMultPVCutHigh->Eval(cent)) { + return false; + } + if (multTrk < fMultCutLow->Eval(cent) || multTrk > fMultCutHigh->Eval(cent)) { + return false; + } + return multTrk <= fMultPVGlobalCutHigh->Eval(multPV); + } + + template + uint16_t packEventSel(TCollision const& collision, float cent, float multTrk) const + { + uint16_t bits = 0; + if (collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + bits |= aod::gfwcollision::kNoSameBunchPileup; + } + if (collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + bits |= aod::gfwcollision::kIsGoodZvtxFT0vsPV; + } + if (collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) { + bits |= aod::gfwcollision::kNoCollInTimeRangeStandard; + } + if (collision.selection_bit(aod::evsel::kNoCollInRofStandard)) { + bits |= aod::gfwcollision::kNoCollInRofStandard; + } + if (collision.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) { + bits |= aod::gfwcollision::kNoHighMultCollInPrevRof; + } + if (collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + bits |= aod::gfwcollision::kIsVertexITSTPC; + } + if (collision.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + bits |= aod::gfwcollision::kIsGoodITSLayersAll; + } + if (collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + bits |= aod::gfwcollision::kNoTimeFrameBorder; + } + if (collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + bits |= aod::gfwcollision::kNoITSROFrameBorder; + } + if (cfgOccupancyMax.value < 0 || collision.trackOccupancyInTimeRange() <= cfgOccupancyMax.value) { + bits |= aod::gfwcollision::kOccupancy; + } + if (!collision.alias_bit(kTVXinTRD)) { + bits |= aod::gfwcollision::kNotTVXinTRD; + } + if (vertexQuality(collision)) { + bits |= aod::gfwcollision::kGoodVertex; + } + if (multiplicityCorrelated(collision, cent, multTrk)) { + bits |= aod::gfwcollision::kMultCorr; + } + return bits; + } + + template + bool withinDcaXy(TTrack const& track, float nSigma) const + { + return std::abs(track.dcaXY()) <= nSigma * fDcaXyShape->Eval(track.pt()); + } + + template + bool withinDcaZ(TTrack const& track, float nSigma) const + { + return std::abs(track.dcaZ()) <= (nSigma / DcaZReferenceNsigma) * fDcaZShape->Eval(track.pt()); + } + + template + bool passProducerTrackCuts(TTrack const& track) const + { + const bool global = track.isGlobalTrack() || track.isGlobalTrackSDD() != 0; + return global && track.itsNCls() >= cfgMinNITSCls.value && track.itsChi2NCl() <= cfgChi2PrITSCls.value; + } + + template + uint32_t packTrackSel(TTrack const& track) const + { + uint32_t bits = 0; + if (cfgDCAxyNSigma.value <= 0.f || withinDcaXy(track, cfgDCAxyNSigma.value)) { + bits |= aod::gfwtrack::kDCAxy; + } + if (withinDcaXy(track, cfgDCAxyNSigmaTight.value)) { + bits |= aod::gfwtrack::kDCAxyTight; + } + if (withinDcaXy(track, cfgDCAxyNSigmaLoose.value)) { + bits |= aod::gfwtrack::kDCAxyLoose; + } + if (cfgDCAzNSigma.value <= 0.f || withinDcaZ(track, cfgDCAzNSigma.value)) { + bits |= aod::gfwtrack::kDCAz; + } + if (withinDcaZ(track, cfgDCAzNSigmaTight.value)) { + bits |= aod::gfwtrack::kDCAzTight; + } + if (withinDcaZ(track, cfgDCAzNSigmaLoose.value)) { + bits |= aod::gfwtrack::kDCAzLoose; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrows.value) { + bits |= aod::gfwtrack::kTPCXrows; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrowsTight.value) { + bits |= aod::gfwtrack::kTPCXrowsTight; + } + if (track.tpcNClsCrossedRows() >= cfgNTPCXrowsLoose.value) { + bits |= aod::gfwtrack::kTPCXrowsLoose; + } + if (track.tpcNClsFound() >= cfgNTPCCls.value) { + bits |= aod::gfwtrack::kTPCCls; + } + if (track.tpcNClsFound() >= cfgNTPCClsTight.value) { + bits |= aod::gfwtrack::kTPCClsTight; + } + if (track.tpcNClsFound() >= cfgNTPCClsLoose.value) { + bits |= aod::gfwtrack::kTPCClsLoose; + } + if (track.tpcChi2NCl() <= cfgChi2PrTPCCls.value) { + bits |= aod::gfwtrack::kChi2TPC; + } + if (track.tpcChi2NCl() <= cfgChi2PrTPCClsLoose.value) { + bits |= aod::gfwtrack::kChi2TPCLoose; + } + return bits; + } + + static bool insideNsigma(float nsigma, float up, float low, float scale) + { + return nsigma < up * scale && nsigma > low * scale; + } + + template + uint32_t packPidWindow(TTrack const& track, float scale) const + { + if (track.pt() > cfgTofPtCut.value && !track.hasTOF()) { + return 0; + } + uint32_t bits = 0; + if (insideNsigma(track.tpcNSigmaPi(), tpcNsigmaCut[NsigmaUpPi], tpcNsigmaCut[NsigmaLowPi], scale)) { + bits |= aod::gfwtrack::kTpcPi; + } + if (insideNsigma(track.tpcNSigmaKa(), tpcNsigmaCut[NsigmaUpKa], tpcNsigmaCut[NsigmaLowKa], scale)) { + bits |= aod::gfwtrack::kTpcKa; + } + if (insideNsigma(track.tpcNSigmaPr(), tpcNsigmaCut[NsigmaUpPr], tpcNsigmaCut[NsigmaLowPr], scale)) { + bits |= aod::gfwtrack::kTpcPr; + } + if (insideNsigma(track.tofNSigmaPi(), tofNsigmaCut[NsigmaUpPi], tofNsigmaCut[NsigmaLowPi], scale)) { + bits |= aod::gfwtrack::kTofPi; + } + if (insideNsigma(track.tofNSigmaKa(), tofNsigmaCut[NsigmaUpKa], tofNsigmaCut[NsigmaLowKa], scale)) { + bits |= aod::gfwtrack::kTofKa; + } + if (insideNsigma(track.tofNSigmaPr(), tofNsigmaCut[NsigmaUpPr], tofNsigmaCut[NsigmaLowPr], scale)) { + bits |= aod::gfwtrack::kTofPr; + } + return bits; + } + + template + uint32_t packPidSel(TTrack const& track) const + { + const uint32_t nominal = packPidWindow(track, NsigmaScaleNominal); + const uint32_t tight = packPidWindow(track, cfgNsigmaScaleTight.value); + const uint32_t loose = packPidWindow(track, cfgNsigmaScaleLoose.value); + // Nominal TPC bits start at bit 14 and nominal TOF bits at bit 23. Shifting by one window moves each detector into its tight or loose block. + return nominal | (tight << (aod::gfwtrack::PidWindowTight * aod::gfwtrack::PidWindowBits)) | (loose << (aod::gfwtrack::PidWindowLoose * aod::gfwtrack::PidWindowBits)); + } + + template + void fillTrack(TTrack const& track, uint32_t bits) + { + outputTracks(outputCollisions.lastIndex(), track.pt(), track.eta(), track.phi(), + track.hasTOF(), bits | packPidSel(track)); + registry.fill(HIST("tracks"), 0.5); + } + + void process(soa::Filtered::iterator const& collision, aod::BCsWithTimestamps const&, soa::Filtered const& tracks) + { + if (!collision.sel8()) { + return; + } + + int nKept = 0; + int nForMultCorr = 0; + for (const auto& track : tracks) { + if (!passProducerTrackCuts(track)) { + continue; + } + const uint32_t bits = packTrackSel(track); + const bool passLoose = !cfgRequireLooseTrackBits || ((bits & aod::gfwtrack::LooseSkimMask) == aod::gfwtrack::LooseSkimMask); + if (!passLoose) { + continue; + } + ++nKept; + if ((bits & aod::gfwtrack::MultCorrTrackMask) == aod::gfwtrack::MultCorrTrackMask) { + ++nForMultCorr; + } + } + if (nKept == 0 && cfgSkipEmptyEvents) { + return; + } + + const auto bc = collision.bc_as(); + const float cent = centrality(collision); + outputCollisions(bc.runNumber(), collision.posZ(), cent, static_cast(collision.multNTracksGlobal()), bc.timestamp(), packEventSel(collision, cent, static_cast(nForMultCorr))); + registry.fill(HIST("events"), 0.5); + + for (const auto& track : tracks) { + if (!passProducerTrackCuts(track)) { + continue; + } + const uint32_t bits = packTrackSel(track); + const bool passLoose = !cfgRequireLooseTrackBits || ((bits & aod::gfwtrack::LooseSkimMask) == aod::gfwtrack::LooseSkimMask); + if (!passLoose) { + continue; + } + fillTrack(track, bits); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/GenericFramework/Tasks/CMakeLists.txt b/PWGCF/GenericFramework/Tasks/CMakeLists.txt index 6238192e1be..90b65bdba93 100644 --- a/PWGCF/GenericFramework/Tasks/CMakeLists.txt +++ b/PWGCF/GenericFramework/Tasks/CMakeLists.txt @@ -18,7 +18,18 @@ o2physics_add_dpl_workflow(flow-gfw-light-ions SOURCES flowGfwLightIons.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(flow-gfw-v02 SOURCES flowGfwV02.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore - COMPONENT_NAME Analysis) \ No newline at end of file + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(flow-gfw-nonflow + SOURCES flowGfwNonflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::GFWCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(energy-flow-gfw + SOURCES energyFlowGfw.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCCDB O2Physics::AnalysisCore O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx new file mode 100644 index 00000000000..41fdfa963ec --- /dev/null +++ b/PWGCF/GenericFramework/Tasks/energyFlowGfw.cxx @@ -0,0 +1,1033 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file enegyFlowGfw.cxx +/// \brief Charged-particle transverse-energy correlators in Xe-Xe and Pb-Pb collisions +/// \author Emil Gorm Dahlbæk Nielsen + +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWConfig.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::genericframework; + +struct EnergyFlowGfw { + enum EnergyWeightMode : int { + PtWeight = 0, + MtWeight, + FullEtWeight + }; + + enum ParticleId : int { + Unidentified = 0, + Pion, + Kaon, + Proton + }; + + enum ActivityAxisMode : int { + CentralityAxis = 0, + NchAxis, + ZdcSpectatorAxis + }; + + enum AodSelectionStep : uint8_t { + AllAodEvents = 0, + TriggerSelected, + CentralitySelected, + HasValidZdc, + SpectatorSelected + }; + + enum ZdcQaStep : uint8_t { + MatchedZdc = 0, + FiniteCommonEnergy, + NonnegativeCommonEnergy, + HasZnTiming, + HasZpTiming, + HasAllTiming + }; + + enum EfficiencyStep : uint8_t { + EfficiencyGenerated = 0, + EfficiencyReconstructed, + EfficiencyStepCount + }; + + enum EfficiencySpecies : uint8_t { + EfficiencyCharged = 0, + EfficiencyPion, + EfficiencyKaon, + EfficiencyProton, + EfficiencySpeciesCount + }; + + struct : ConfigurableGroup { + Configurable vertexZ{"vertexZ", 10.f, "Accepted |z-vertex| (cm)"}; + Configurable> ptCuts{"ptCuts", {0.2f, 5.f}, "Track pT cuts (GeV/c)"}; + Configurable etaMax{"etaMax", 0.8f, "Maximum absolute track pseudorapidity"}; + } cuts; + + struct : ConfigurableGroup { + Configurable weightMode{"weightMode", FullEtWeight, "Energy weight: 0=pT, 1=mT, 2=E/cosh(eta)"}; + Configurable nSigmaCut{"nSigmaCut", 3.f, "TPC or combined TPC+TOF PID selection in n-sigma"}; + Configurable tofPtCut{"tofPtCut", 0.5f, "Require TOF PID above this pT (GeV/c)"}; + Configurable strictPid{"strictPid", true, "Mark tracks compatible with multiple species as unidentified"}; + Configurable unidentifiedMass{"unidentifiedMass", o2::constants::physics::MassPionCharged, "Mass assigned to unidentified charged tracks (GeV/c^2)"}; + } energy; + + Configurable cfgRegions{"cfgRegions", {{"refA", "refB"}, {-0.8, 0.4}, {-0.4, 0.8}, {0, 0}, {1, 1}}, "GFW region definitions"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refA {0}", "refB {0}", "refA {0} refB {0}", "refA {1} refB {-1}", "refA {2} refB {-2}", "refA {3} refB {-3}", "refA {4} refB {-4}", "refA {5} refB {-5}", "refA {6} refB {-6}"}, {"F0A", "F0B", "F0AF0B", "EECGap12", "EECGap22", "EECGap32", "EECGap42", "EECGap52", "EECGap62"}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}}, "GFW correlations to calculate"}; + Configurable cfgBootstrapSamples{"cfgBootstrapSamples", 10, "Number of FlowContainer bootstrap subsamples; zero disables bootstrapping"}; + Configurable cfgActivityAxis{"cfgActivityAxis", CentralityAxis, "FlowContainer x-axis: 0=centrality, 1=number of filtered global tracks, 2=corrected ZDC spectator estimate"}; + Configurable cfgMaxCentrality{"cfgMaxCentrality", 100.f, "Maximum accepted centrality percentile"}; + Configurable cfgFillEfficiency{"fillEfficiency", false, "Fill MC tracking efficiency; ignored by data processes"}; + Configurable cfgFillAcceptance{"fillAcceptance", false, "Fill GFW acceptance weights instead of running the reconstructed analysis"}; + Configurable cfgUseRun2EventSelectionWithoutZdc{"cfgUseRun2EventSelectionWithoutZdc", false, "For Run 2 data, require kINT7 and every CCDB-enabled barrel sel7 criterion except the ZDC beam-beam criteria; retain the ZDC beam-gas vetoes"}; + + Configurable cfgSpectatorEnergyPerNucleon{"cfgSpectatorEnergyPerNucleon", 1.f, "ZDC energy corresponding to one spectator nucleon"}; + Configurable cfgMaxTotalSpectators{"cfgMaxTotalSpectators", 500.f, "Maximum accepted spectator count from ZNA+ZNC+ZPA+ZPC"}; + Configurable cfgZdcResponseZNA{"cfgZdcResponseZNA", 1.f, "ZNA efficiency/acceptance response factor alpha; corrected energy is E_ZNA/alpha_ZNA"}; + Configurable cfgZdcResponseZNC{"cfgZdcResponseZNC", 1.f, "ZNC efficiency/acceptance response factor alpha; corrected energy is E_ZNC/alpha_ZNC"}; + Configurable cfgZdcResponseZPA{"cfgZdcResponseZPA", 1.f, "ZPA efficiency/acceptance response factor alpha; corrected energy is E_ZPA/alpha_ZPA"}; + Configurable cfgZdcResponseZPC{"cfgZdcResponseZPC", 1.f, "ZPC efficiency/acceptance response factor alpha; corrected energy is E_ZPC/alpha_ZPC"}; + Configurable cfgZdcMaxAbsTimeForQa{"cfgZdcMaxAbsTimeForQa", 100.f, "Maximum absolute ZDC time considered available in QA; excludes missing-time sentinels"}; + + struct : ConfigurableGroup { + Configurable cfgEfficiencyPath{"cfgEfficiencyPath", "", "CCDB path to efficiency object"}; + Configurable cfgUsePIDEfficiencies{"cfgUsePIDEfficiencies", false, "Use separate charged, pion, kaon, and proton efficiencies"}; + } cfgEfficiency; + Configurable acceptancePath{"acceptancePath", "", "CCDB path to GFW acceptance weights"}; + Configurable acceptanceRunByRun{"acceptanceRunByRun", false, "Load acceptance weights from the RunByRun subdirectory"}; + + ConfigurableAxis axisVertex{"axisVertex", {10, -10., 10.}, "z-vertex (cm)"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.1}, "centrality"}; + ConfigurableAxis axisNGlobalTracks{"axisNGlobalTracks", {500, 0., 5000.}, "number of filtered global tracks"}; + ConfigurableAxis axisPt{"axisPt", {100, 0., 5.}, "pT (GeV/c)"}; + ConfigurableAxis axisEnergy{"axisEnergy", {200, 0., 20.}, "transverse-energy weight (GeV)"}; + ConfigurableAxis axisTotalSpectators{"axisTotalSpectators", {500, 0., 500.}, "total spectator count"}; + ConfigurableAxis axisSpectatorActivity{"axisSpectatorActivity", {500, 0., 500.}, "spectator count used by the FlowContainer"}; + ConfigurableAxis axisZdcNeutronEnergy{"axisZdcNeutronEnergy", {410, -2000., 80000.}, "ZDC neutron common energy"}; + ConfigurableAxis axisZdcProtonEnergy{"axisZdcProtonEnergy", {310, -1000., 30000.}, "ZDC proton common energy"}; + ConfigurableAxis axisZdcTotalEnergy{"axisZdcTotalEnergy", {410, -5000., 200000.}, "summed ZDC common energy"}; + ConfigurableAxis axisZdcZemEnergy{"axisZdcZemEnergy", {210, -100., 2000.}, "summed ZEM energy"}; + ConfigurableAxis axisZdcTime{"axisZdcTime", {600, -15., 15.}, "ZDC time (ns)"}; + ConfigurableAxis axisZdcTimeCombination{"axisZdcTimeCombination", {400, -20., 20.}, "ZDC time sum or difference (ns)"}; + ConfigurableAxis axisZdcTrackMultiplicity{"axisZdcTrackMultiplicity", {400, 0., 4000.}, "number of filtered global tracks"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < cuts.vertexZ; + Filter trackFilter = (nabs(aod::track::eta) < cuts.etaMax) && (aod::track::pt > cuts.ptCuts->first) && (aod::track::pt < cuts.ptCuts->second) && + ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))); + + using CollisionsRun2 = soa::Filtered>; + using CollisionsRun2MC = soa::Join; + using CollisionsRun3 = soa::Filtered>; + using CollisionsRun3MC = soa::Join; + using Tracks = soa::Filtered>; + using TracksMC = soa::Filtered>; + using BCsRun2 = soa::Join; + using BCsRun3 = soa::Join; + + Preslice tracksPerCollision = aod::track::collisionId; + + OutputObj flowContainer{"FlowContainer"}; + OutputObj acceptanceWeights{GFWWeights("weights")}; + HistogramRegistry registry{"registry"}; + Service ccdb{}; + Service pdgDB{}; + + std::unique_ptr gfw{std::make_unique()}; + std::unique_ptr random{std::make_unique(0)}; + std::vector corrConfigs; + std::vector zeroHarmonicConfigs; + int gfwMask = 0; + + TH1* efficiency = nullptr; + std::vector pidEfficiencies; + bool efficiencyLoaded = false; + GFWWeights* acceptance = nullptr; + int acceptanceRunNumber = -1; + + void init(InitContext const&) + { + const int enabledProcesses = static_cast(doprocessRun2) + static_cast(doprocessRun2MC) + static_cast(doprocessRun3) + static_cast(doprocessRun3MC); + if (enabledProcesses > 1) { + LOGF(fatal, "Enable only one input process: Run 2 data, Run 2 MC, Run 3 data, or Run 3 MC"); + } + if (cuts.ptCuts->first >= cuts.ptCuts->second || cuts.etaMax <= 0.f) { + LOGF(fatal, "Require ptMin < ptMax and etaMax > 0"); + } + if (energy.weightMode < PtWeight || energy.weightMode > FullEtWeight) { + LOGF(fatal, "weightMode must be 0 (pT), 1 (mT), or 2 (full ET)"); + } + if (energy.nSigmaCut <= 0.f || energy.tofPtCut < 0.f || energy.unidentifiedMass < 0.f) { + LOGF(fatal, "PID n-sigma and mass settings must be non-negative, with nSigmaCut > 0"); + } + if (cfgBootstrapSamples < 0) { + LOGF(fatal, "cfgBootstrapSamples must not be negative"); + } + if (cfgActivityAxis < CentralityAxis || cfgActivityAxis > ZdcSpectatorAxis) { + LOGF(fatal, "cfgActivityAxis must be 0 (centrality), 1 (filtered global tracks), or 2 (corrected ZDC spectators)"); + } + if (!std::isfinite(cfgMaxCentrality.value) || cfgMaxCentrality < 0.f || cfgSpectatorEnergyPerNucleon <= 0.f || cfgMaxTotalSpectators < 0.f || cfgZdcResponseZNA <= 0.f || cfgZdcResponseZNC <= 0.f || cfgZdcResponseZPA <= 0.f || cfgZdcResponseZPC <= 0.f || cfgZdcMaxAbsTimeForQa <= 0.f) { + LOGF(fatal, "The maximum centrality must be finite and non-negative, the spectator energy scale, ZDC response factors, and ZDC QA time range must be positive, and the spectator threshold non-negative"); + } + if (cfgMaxCentrality > 100.f) { + LOGF(warning, "cfgMaxCentrality is %.1f (> 100); dummy centrality values will be accepted. Use the Nch or spectator activity axis when calibrated centrality is unavailable", cfgMaxCentrality.value); + } + + const int numberOfRegions = cfgRegions->GetSize(); + if (numberOfRegions <= 0 || cfgCorrConfig->GetSize() <= 0) { + LOGF(fatal, "GFW regions and correlations must be non-empty and internally consistent"); + } + + float largestRegionEta = 0.f; + for (int index = 0; index < numberOfRegions; ++index) { + const float etaMin = cfgRegions->GetEtaMin()[index]; + const float etaMax = cfgRegions->GetEtaMax()[index]; + if (!std::isfinite(etaMin) || !std::isfinite(etaMax) || etaMin >= etaMax) { + LOGF(fatal, "GFW region %s must have finite etaMin < etaMax", cfgRegions->GetNames()[index].c_str()); + } + if (cfgRegions->GetpTDifs()[index] != 0 || cfgRegions->GetBitmasks()[index] == 0) { + LOGF(fatal, "This task supports only pT-integrated regions with non-zero bit masks"); + } + largestRegionEta = std::max({largestRegionEta, std::abs(etaMin), std::abs(etaMax)}); + gfwMask |= cfgRegions->GetBitmasks()[index]; + gfw->AddRegion(cfgRegions->GetNames()[index], etaMin, etaMax, 1, cfgRegions->GetBitmasks()[index]); + } + if (largestRegionEta > cuts.etaMax) { + LOGF(fatal, "All GFW regions must be contained within the selected track eta range"); + } + + auto profileNames = std::make_unique(); + profileNames->SetOwner(true); + corrConfigs.reserve(cfgCorrConfig->GetSize()); + zeroHarmonicConfigs.reserve(cfgCorrConfig->GetSize()); + for (int index = 0; index < cfgCorrConfig->GetSize(); ++index) { + if (cfgCorrConfig->GetpTDifs()[index] != 0) { + LOGF(fatal, "This task supports only pT-integrated GFW correlations"); + } + auto config = gfw->GetCorrelatorConfig(cfgCorrConfig->GetCorrs()[index], cfgCorrConfig->GetHeads()[index], false); + if (config.Regs.empty() || config.Head.empty()) { + LOGF(fatal, "Invalid GFW correlation configuration at index %d", index); + } + const bool zeroHarmonic = std::all_of(config.Hars.begin(), config.Hars.end(), [](const auto& harmonics) { + return std::all_of(harmonics.begin(), harmonics.end(), [](int harmonic) { return harmonic == 0; }); + }); + zeroHarmonicConfigs.push_back(zeroHarmonic); + if (!zeroHarmonic) { + profileNames->Add(new TNamed(config.Head.c_str(), config.Head.c_str())); + } + const std::string rawName = config.Head + "_raw"; + profileNames->Add(new TNamed(rawName.c_str(), rawName.c_str())); + corrConfigs.push_back(std::move(config)); + } + gfw->CreateRegions(); + flowContainer.setObject(new FlowContainer("FlowContainer")); + const AxisSpec activityAxis = [this]() { + if (cfgActivityAxis.value == NchAxis) { + return AxisSpec{axisNGlobalTracks, "N_{global tracks}"}; + } + if (cfgActivityAxis.value == ZdcSpectatorAxis) { + return AxisSpec{axisSpectatorActivity, "N_{spectators}"}; + } + return AxisSpec{axisCentrality, "centrality (%)"}; + }(); + if (activityAxis.nBins.has_value()) { + flowContainer->Initialize(profileNames.get(), activityAxis.nBins.value(), activityAxis.binEdges.front(), activityAxis.binEdges.back(), cfgBootstrapSamples); + } else { + flowContainer->Initialize(profileNames.get(), activityAxis, cfgBootstrapSamples); + } + if (cfgFillAcceptance) { + acceptanceWeights->init(true, false); + } + + const AxisSpec etaAxis{32, -cuts.etaMax.value, cuts.etaMax.value, "#eta"}; + registry.add("event/centrality", "Accepted events;centrality;events", HistType::kTH1F, {axisCentrality}); + registry.add("event/activity", "Accepted events;event activity;events", HistType::kTH1F, {activityAxis}); + registry.add("event/vertex", "Accepted events;z-vtx (cm);events", HistType::kTH1F, {axisVertex}); + registry.add("event/aodSelection", "AOD event selection;selection;events", HistType::kTH1F, {{5, -0.5, 4.5}}); + registry.add("event/totalSpectators", "ZDC spectator estimate;N_{spectators};events", HistType::kTH1F, {axisTotalSpectators}); + registry.add("track/ptEta", "Selected tracks;p_{T} (GeV/c);#eta", HistType::kTH2F, {axisPt, etaAxis}); + registry.add("track/energy", "Energy entering the GFW;E_{T} (GeV);tracks", HistType::kTH1F, {axisEnergy}); + registry.add("track/pid", "PID mass assignment;species;tracks", HistType::kTH1F, {{4, -0.5, 3.5}}); + + if (cfgFillEfficiency && (doprocessRun2MC || doprocessRun3MC)) { + const AxisSpec efficiencySpeciesAxis{EfficiencySpeciesCount, -0.5, static_cast(EfficiencySpeciesCount) - 0.5, "particle"}; + const AxisSpec efficiencyStepAxis{EfficiencyStepCount, -0.5, static_cast(EfficiencyStepCount) - 0.5, "generated/reconstructed"}; + registry.add("Efficiency/efficiencyHist", "MC tracking efficiency;#it{p}_{T}^{MC};centrality (%);particle;step", HistType::kTHnSparseF, {axisPt, axisCentrality, efficiencySpeciesAxis, efficiencyStepAxis}); + auto efficiencyHistogram = registry.get(HIST("Efficiency/efficiencyHist")); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyCharged + 1, "charged"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyPion + 1, "pion"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyKaon + 1, "kaon"); + efficiencyHistogram->GetAxis(2)->SetBinLabel(EfficiencyProton + 1, "proton"); + efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyGenerated + 1, "generated"); + efficiencyHistogram->GetAxis(3)->SetBinLabel(EfficiencyReconstructed + 1, "reconstructed"); + } + + registry.add("zdc/qaSelection", "ZDC QA availability;condition;events", HistType::kTH1F, {{6, -0.5, 5.5}}); + registry.add("zdc/energyZNA", "Triggered events with matched ZDC;E_{ZNA};events", HistType::kTH1F, {axisZdcNeutronEnergy}); + registry.add("zdc/energyZNC", "Triggered events with matched ZDC;E_{ZNC};events", HistType::kTH1F, {axisZdcNeutronEnergy}); + registry.add("zdc/energyZPA", "Triggered events with matched ZDC;E_{ZPA};events", HistType::kTH1F, {axisZdcProtonEnergy}); + registry.add("zdc/energyZPC", "Triggered events with matched ZDC;E_{ZPC};events", HistType::kTH1F, {axisZdcProtonEnergy}); + registry.add("zdc/energyZEM", "Triggered events with matched ZDC;E_{ZEM1}+E_{ZEM2};events", HistType::kTH1F, {axisZdcZemEnergy}); + registry.add("zdc/timeZNA", "Triggered events with matched ZDC;t_{ZNA} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZNC", "Triggered events with matched ZDC;t_{ZNC} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZPA", "Triggered events with matched ZDC;t_{ZPA} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZPC", "Triggered events with matched ZDC;t_{ZPC} (ns);events", HistType::kTH1F, {axisZdcTime}); + registry.add("zdc/timeZnSumVsDiff", "Triggered events with ZNA and ZNC timing;t_{ZNA}-t_{ZNC} (ns);t_{ZNA}+t_{ZNC} (ns)", HistType::kTH2F, {axisZdcTimeCombination, axisZdcTimeCombination}); + registry.add("zdc/energyZnaVsZnc", "Triggered events with finite ZN energies;E_{ZNC};E_{ZNA}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/energyZpaVsZpc", "Triggered events with finite ZP energies;E_{ZPC};E_{ZPA}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/energyZnVsZem", "Triggered events with finite ZN and ZEM energies;E_{ZEM1}+E_{ZEM2};E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisZdcZemEnergy, axisZdcTotalEnergy}); + registry.add("zdc/energyZnaVsTime", "Triggered events with matched ZDC;t_{ZNA} (ns);E_{ZNA}", HistType::kTH2F, {axisZdcTime, axisZdcNeutronEnergy}); + registry.add("zdc/energyZncVsTime", "Triggered events with matched ZDC;t_{ZNC} (ns);E_{ZNC}", HistType::kTH2F, {axisZdcTime, axisZdcNeutronEnergy}); + registry.add("zdc/energyZpaVsTime", "Triggered events with matched ZDC;t_{ZPA} (ns);E_{ZPA}", HistType::kTH2F, {axisZdcTime, axisZdcProtonEnergy}); + registry.add("zdc/energyZpcVsTime", "Triggered events with matched ZDC;t_{ZPC} (ns);E_{ZPC}", HistType::kTH2F, {axisZdcTime, axisZdcProtonEnergy}); + registry.add("zdc/commonVsSectorZNA", "Triggered events with finite ZNA energies;E_{ZNA}^{common};#Sigma E_{ZNA}^{sectors}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/commonVsSectorZNC", "Triggered events with finite ZNC energies;E_{ZNC}^{common};#Sigma E_{ZNC}^{sectors}", HistType::kTH2F, {axisZdcNeutronEnergy, axisZdcNeutronEnergy}); + registry.add("zdc/commonVsSectorZPA", "Triggered events with finite ZPA energies;E_{ZPA}^{common};#Sigma E_{ZPA}^{sectors}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/commonVsSectorZPC", "Triggered events with finite ZPC energies;E_{ZPC}^{common};#Sigma E_{ZPC}^{sectors}", HistType::kTH2F, {axisZdcProtonEnergy, axisZdcProtonEnergy}); + registry.add("zdc/energyZnVsNGlobalTracks", "Triggered events with finite ZN energies;N_{global tracks};E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/energyZpVsNGlobalTracks", "Triggered events with finite ZP energies;N_{global tracks};E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/energyTotalVsNGlobalTracks", "Triggered events with finite common energies;N_{global tracks};#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisZdcTrackMultiplicity, axisZdcTotalEnergy}); + registry.add("zdc/spectatorEstimateVsNGlobalTracks", "Triggered events with finite common energies;N_{global tracks};N_{spectators} estimate", HistType::kTH2F, {axisZdcTrackMultiplicity, axisTotalSpectators}); + registry.add("zdc/energyZnVsCentrality", "Triggered events with finite ZN energies;centrality (%);E_{ZNA}+E_{ZNC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/energyZpVsCentrality", "Triggered events with finite ZP energies;centrality (%);E_{ZPA}+E_{ZPC}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/energyTotalVsCentrality", "Triggered events with finite common energies;centrality (%);#Sigma E_{ZDC}^{common}", HistType::kTH2F, {axisCentrality, axisZdcTotalEnergy}); + registry.add("zdc/spectatorEstimateVsCentrality", "Triggered events with finite common energies;centrality (%);N_{spectators} estimate", HistType::kTH2F, {axisCentrality, axisTotalSpectators}); + registry.add("zdc/meanZNAByNGlobalTracks", "Mean ZNA response;N_{global tracks};#LT E_{ZNA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZNCByNGlobalTracks", "Mean ZNC response;N_{global tracks};#LT E_{ZNC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZPAByNGlobalTracks", "Mean ZPA response;N_{global tracks};#LT E_{ZPA} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZPCByNGlobalTracks", "Mean ZPC response;N_{global tracks};#LT E_{ZPC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZnByNGlobalTracks", "Mean neutron-ZDC response;N_{global tracks};#LT E_{ZNA}+E_{ZNC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZpByNGlobalTracks", "Mean proton-ZDC response;N_{global tracks};#LT E_{ZPA}+E_{ZPC} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanTotalByNGlobalTracks", "Mean total ZDC response;N_{global tracks};#LT #Sigma E_{ZDC}^{common} #GT", HistType::kTProfile, {axisZdcTrackMultiplicity}); + registry.add("zdc/meanZNAByCentrality", "Mean ZNA response;centrality (%);#LT E_{ZNA} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZNCByCentrality", "Mean ZNC response;centrality (%);#LT E_{ZNC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZPAByCentrality", "Mean ZPA response;centrality (%);#LT E_{ZPA} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZPCByCentrality", "Mean ZPC response;centrality (%);#LT E_{ZPC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZnByCentrality", "Mean neutron-ZDC response;centrality (%);#LT E_{ZNA}+E_{ZNC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanZpByCentrality", "Mean proton-ZDC response;centrality (%);#LT E_{ZPA}+E_{ZPC} #GT", HistType::kTProfile, {axisCentrality}); + registry.add("zdc/meanTotalByCentrality", "Mean total ZDC response;centrality (%);#LT #Sigma E_{ZDC}^{common} #GT", HistType::kTProfile, {axisCentrality}); + + auto selectionHistogram = registry.get(HIST("event/aodSelection")); + selectionHistogram->GetXaxis()->SetBinLabel(AllAodEvents + 1, "all"); + selectionHistogram->GetXaxis()->SetBinLabel(TriggerSelected + 1, "event selection"); + selectionHistogram->GetXaxis()->SetBinLabel(CentralitySelected + 1, "centrality threshold"); + selectionHistogram->GetXaxis()->SetBinLabel(HasValidZdc + 1, "valid ZDC"); + selectionHistogram->GetXaxis()->SetBinLabel(SpectatorSelected + 1, "spectator threshold"); + auto zdcQaHistogram = registry.get(HIST("zdc/qaSelection")); + zdcQaHistogram->GetXaxis()->SetBinLabel(MatchedZdc + 1, "matched ZDC"); + zdcQaHistogram->GetXaxis()->SetBinLabel(FiniteCommonEnergy + 1, "finite common energies"); + zdcQaHistogram->GetXaxis()->SetBinLabel(NonnegativeCommonEnergy + 1, "nonnegative common energies"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasZnTiming + 1, "ZNA and ZNC timing"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasZpTiming + 1, "ZPA and ZPC timing"); + zdcQaHistogram->GetXaxis()->SetBinLabel(HasAllTiming + 1, "all timing"); + auto pidHistogram = registry.get(HIST("track/pid")); + pidHistogram->GetXaxis()->SetBinLabel(Unidentified + 1, "unidentified"); + pidHistogram->GetXaxis()->SetBinLabel(Pion + 1, "pion"); + pidHistogram->GetXaxis()->SetBinLabel(Kaon + 1, "kaon"); + pidHistogram->GetXaxis()->SetBinLabel(Proton + 1, "proton"); + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + const auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + template + float totalSpectators(TZdc const& zdc) const + { + const std::array energies{zdc.energyCommonZNA(), zdc.energyCommonZNC(), zdc.energyCommonZPA(), zdc.energyCommonZPC()}; + const std::array responseFactors{cfgZdcResponseZNA.value, cfgZdcResponseZNC.value, cfgZdcResponseZPA.value, cfgZdcResponseZPC.value}; + float totalEnergy = 0.f; + for (int index = 0; index < 4; ++index) { + const float value = energies[index]; + if (!std::isfinite(value)) { + return -1.f; + } + totalEnergy += value / responseFactors[index]; + } + const float spectators = totalEnergy / cfgSpectatorEnergyPerNucleon.value; + // Preserve signed pedestal fluctuations in the sum, then enforce the physical boundary. + return std::isfinite(spectators) ? std::max(0.f, spectators) : -1.f; + } + + template + void fillZdcQa(TZdc const& zdc, float numberOfGlobalTracks, float centrality) + { + registry.fill(HIST("zdc/qaSelection"), MatchedZdc); + + const std::array commonEnergies{zdc.energyCommonZNA(), zdc.energyCommonZNC(), zdc.energyCommonZPA(), zdc.energyCommonZPC()}; + const std::array times{zdc.timeZNA(), zdc.timeZNC(), zdc.timeZPA(), zdc.timeZPC()}; + const bool validCentrality = std::isfinite(centrality) && centrality >= 0.f && centrality <= 100.f; + const bool allFinite = std::all_of(commonEnergies.begin(), commonEnergies.end(), [](float value) { return std::isfinite(value); }); + const bool allNonnegative = allFinite && std::all_of(commonEnergies.begin(), commonEnergies.end(), [](float value) { return value >= 0.f; }); + if (allFinite) { + registry.fill(HIST("zdc/qaSelection"), FiniteCommonEnergy); + } + if (allNonnegative) { + registry.fill(HIST("zdc/qaSelection"), NonnegativeCommonEnergy); + } + + const auto hasTiming = [this](float value) { return std::isfinite(value) && std::abs(value) < cfgZdcMaxAbsTimeForQa.value; }; + const bool hasZnTiming = hasTiming(times[0]) && hasTiming(times[1]); + const bool hasZpTiming = hasTiming(times[2]) && hasTiming(times[3]); + if (hasZnTiming) { + registry.fill(HIST("zdc/qaSelection"), HasZnTiming); + registry.fill(HIST("zdc/timeZnSumVsDiff"), times[0] - times[1], times[0] + times[1]); + } + if (hasZpTiming) { + registry.fill(HIST("zdc/qaSelection"), HasZpTiming); + } + if (hasZnTiming && hasZpTiming) { + registry.fill(HIST("zdc/qaSelection"), HasAllTiming); + } + + if (std::isfinite(commonEnergies[0])) { + registry.fill(HIST("zdc/energyZNA"), commonEnergies[0]); + registry.fill(HIST("zdc/meanZNAByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[0]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZNAByCentrality"), centrality, commonEnergies[0]); + } + } + if (std::isfinite(commonEnergies[1])) { + registry.fill(HIST("zdc/energyZNC"), commonEnergies[1]); + registry.fill(HIST("zdc/meanZNCByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[1]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZNCByCentrality"), centrality, commonEnergies[1]); + } + } + if (std::isfinite(commonEnergies[2])) { + registry.fill(HIST("zdc/energyZPA"), commonEnergies[2]); + registry.fill(HIST("zdc/meanZPAByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[2]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZPAByCentrality"), centrality, commonEnergies[2]); + } + } + if (std::isfinite(commonEnergies[3])) { + registry.fill(HIST("zdc/energyZPC"), commonEnergies[3]); + registry.fill(HIST("zdc/meanZPCByNGlobalTracks"), numberOfGlobalTracks, commonEnergies[3]); + if (validCentrality) { + registry.fill(HIST("zdc/meanZPCByCentrality"), centrality, commonEnergies[3]); + } + } + + if (hasTiming(times[0])) { + registry.fill(HIST("zdc/timeZNA"), times[0]); + } + if (hasTiming(times[1])) { + registry.fill(HIST("zdc/timeZNC"), times[1]); + } + if (hasTiming(times[2])) { + registry.fill(HIST("zdc/timeZPA"), times[2]); + } + if (hasTiming(times[3])) { + registry.fill(HIST("zdc/timeZPC"), times[3]); + } + if (hasTiming(times[0]) && std::isfinite(commonEnergies[0])) { + registry.fill(HIST("zdc/energyZnaVsTime"), times[0], commonEnergies[0]); + } + if (hasTiming(times[1]) && std::isfinite(commonEnergies[1])) { + registry.fill(HIST("zdc/energyZncVsTime"), times[1], commonEnergies[1]); + } + if (hasTiming(times[2]) && std::isfinite(commonEnergies[2])) { + registry.fill(HIST("zdc/energyZpaVsTime"), times[2], commonEnergies[2]); + } + if (hasTiming(times[3]) && std::isfinite(commonEnergies[3])) { + registry.fill(HIST("zdc/energyZpcVsTime"), times[3], commonEnergies[3]); + } + + const auto sumSectors = [](const auto& sectors) { + float sum = 0.f; + for (const float value : sectors) { + if (!std::isfinite(value)) { + return std::numeric_limits::quiet_NaN(); + } + sum += value; + } + return sum; + }; + const std::array sectorSums{sumSectors(zdc.energySectorZNA()), sumSectors(zdc.energySectorZNC()), sumSectors(zdc.energySectorZPA()), sumSectors(zdc.energySectorZPC())}; + if (std::isfinite(commonEnergies[0]) && std::isfinite(sectorSums[0])) { + registry.fill(HIST("zdc/commonVsSectorZNA"), commonEnergies[0], sectorSums[0]); + } + if (std::isfinite(commonEnergies[1]) && std::isfinite(sectorSums[1])) { + registry.fill(HIST("zdc/commonVsSectorZNC"), commonEnergies[1], sectorSums[1]); + } + if (std::isfinite(commonEnergies[2]) && std::isfinite(sectorSums[2])) { + registry.fill(HIST("zdc/commonVsSectorZPA"), commonEnergies[2], sectorSums[2]); + } + if (std::isfinite(commonEnergies[3]) && std::isfinite(sectorSums[3])) { + registry.fill(HIST("zdc/commonVsSectorZPC"), commonEnergies[3], sectorSums[3]); + } + + const float zem1 = zdc.energyZEM1(); + const float zem2 = zdc.energyZEM2(); + const bool finiteZem = std::isfinite(zem1) && std::isfinite(zem2); + const float zemSum = finiteZem ? zem1 + zem2 : std::numeric_limits::quiet_NaN(); + if (finiteZem) { + registry.fill(HIST("zdc/energyZEM"), zemSum); + } + if (std::isfinite(commonEnergies[0]) && std::isfinite(commonEnergies[1])) { + const float znSum = commonEnergies[0] + commonEnergies[1]; + registry.fill(HIST("zdc/energyZnaVsZnc"), commonEnergies[1], commonEnergies[0]); + registry.fill(HIST("zdc/energyZnVsNGlobalTracks"), numberOfGlobalTracks, znSum); + registry.fill(HIST("zdc/meanZnByNGlobalTracks"), numberOfGlobalTracks, znSum); + if (validCentrality) { + registry.fill(HIST("zdc/energyZnVsCentrality"), centrality, znSum); + registry.fill(HIST("zdc/meanZnByCentrality"), centrality, znSum); + } + if (finiteZem) { + registry.fill(HIST("zdc/energyZnVsZem"), zemSum, znSum); + } + } + if (std::isfinite(commonEnergies[2]) && std::isfinite(commonEnergies[3])) { + const float zpSum = commonEnergies[2] + commonEnergies[3]; + registry.fill(HIST("zdc/energyZpaVsZpc"), commonEnergies[3], commonEnergies[2]); + registry.fill(HIST("zdc/energyZpVsNGlobalTracks"), numberOfGlobalTracks, zpSum); + registry.fill(HIST("zdc/meanZpByNGlobalTracks"), numberOfGlobalTracks, zpSum); + if (validCentrality) { + registry.fill(HIST("zdc/energyZpVsCentrality"), centrality, zpSum); + registry.fill(HIST("zdc/meanZpByCentrality"), centrality, zpSum); + } + } + if (allFinite) { + const float totalEnergy = commonEnergies[0] + commonEnergies[1] + commonEnergies[2] + commonEnergies[3]; + registry.fill(HIST("zdc/energyTotalVsNGlobalTracks"), numberOfGlobalTracks, totalEnergy); + registry.fill(HIST("zdc/meanTotalByNGlobalTracks"), numberOfGlobalTracks, totalEnergy); + if (validCentrality) { + registry.fill(HIST("zdc/energyTotalVsCentrality"), centrality, totalEnergy); + registry.fill(HIST("zdc/meanTotalByCentrality"), centrality, totalEnergy); + } + const float spectators = totalSpectators(zdc); + if (spectators >= 0.f) { + registry.fill(HIST("zdc/spectatorEstimateVsNGlobalTracks"), numberOfGlobalTracks, spectators); + if (validCentrality) { + registry.fill(HIST("zdc/spectatorEstimateVsCentrality"), centrality, spectators); + } + } + } + } + + template + bool eventSelected(TCollision const& collision, bool triggerSelected, float numberOfGlobalTracks, float centrality, float& spectators) + { + spectators = -1.f; + registry.fill(HIST("event/aodSelection"), AllAodEvents); + if (!triggerSelected) { + return false; + } + registry.fill(HIST("event/aodSelection"), TriggerSelected); + + if (!collision.has_foundBC()) { + return false; + } + const auto& bc = collision.template foundBC_as(); + if (!bc.has_zdc()) { + return false; + } + const auto& zdc = bc.zdc(); + fillZdcQa(zdc, numberOfGlobalTracks, centrality); + + if (!std::isfinite(centrality) || centrality < 0.f || centrality > cfgMaxCentrality) { + return false; + } + registry.fill(HIST("event/aodSelection"), CentralitySelected); + + spectators = totalSpectators(zdc); + if (spectators < 0.f) { + return false; + } + registry.fill(HIST("event/aodSelection"), HasValidZdc); + registry.fill(HIST("event/totalSpectators"), spectators); + if (spectators > cfgMaxTotalSpectators) { + return false; + } + registry.fill(HIST("event/aodSelection"), SpectatorSelected); + return true; + } + + static bool isZdcBeamBeamSelectionFlag(int flag) + { + return flag == aod::evsel::kIsBBZNA || flag == aod::evsel::kIsBBZNC || flag == aod::evsel::kIsBBZAC; + } + + template + bool run2EventSelected(TCollision const& collision) + { + if (!collision.alias_bit(kINT7)) { + return false; + } + if (!cfgUseRun2EventSelectionWithoutZdc) { + return collision.sel7(); + } + if (!collision.has_foundBC()) { + return false; + } + + const auto& bc = collision.template foundBC_as(); + auto* selectionParameters = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", bc.timestamp()); + if (!selectionParameters) { + LOGF(fatal, "Could not load the Run 2 event-selection parameters"); + } + const bool* enabledSelections = selectionParameters->getSelection(0); + for (int flag = 0; flag < aod::evsel::kNsel; ++flag) { + if (isZdcBeamBeamSelectionFlag(flag)) { + continue; + } + if (enabledSelections[flag] && !collision.selection_bit(flag)) { + return false; + } + } + return true; + } + + void loadEfficiency(uint64_t timestamp) + { + if (efficiencyLoaded) { + return; + } + if (cfgEfficiency.cfgEfficiencyPath.value.empty()) { + efficiencyLoaded = true; + return; + } + + if (!cfgEfficiency.cfgUsePIDEfficiencies) { + efficiency = ccdb->getForTimeStamp(cfgEfficiency.cfgEfficiencyPath.value, timestamp); + if (!efficiency) { + LOGF(fatal, "Could not load efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); + } else { + static constexpr std::array EfficiencySpeciesNames{"ch", "pi", "ka", "pr"}; + pidEfficiencies.reserve(EfficiencySpeciesNames.size()); + for (const auto& speciesName : EfficiencySpeciesNames) { + const std::string path = cfgEfficiency.cfgEfficiencyPath.value + "/" + speciesName; + pidEfficiencies.push_back(ccdb->getForTimeStamp(path, timestamp)); + if (!pidEfficiencies.back()) { + LOGF(fatal, "Could not load PID efficiency histogram from %s", path.c_str()); + } + LOGF(info, "Loaded PID efficiency histogram from %s", path.c_str()); + } + } + efficiencyLoaded = true; + } + + void loadAcceptance(uint64_t timestamp, int runNumber) + { + if (acceptancePath.value.empty()) { + acceptance = nullptr; + return; + } + if (acceptance && (!acceptanceRunByRun || acceptanceRunNumber == runNumber)) { + return; + } + std::string path = acceptancePath.value; + if (acceptanceRunByRun) { + if (path.back() != '/') { + path += '/'; + } + path += "RunByRun/"; + } + acceptance = ccdb->getForTimeStamp(path, timestamp); + if (!acceptance || !acceptance->isDataFilled()) { + LOGF(fatal, "Could not load populated GFW acceptance weights from %s", path.c_str()); + } + acceptanceRunNumber = runNumber; + } + + double getEfficiency(float pt, int pid) const + { + TH1* efficiencyHistogram = efficiency; + if (cfgEfficiency.cfgUsePIDEfficiencies.value) { + if (pidEfficiencies.empty()) { + return 1.; + } + efficiencyHistogram = pidEfficiencies[pid]; + } else if (!efficiencyHistogram) { + return 1.; + } + + const double value = efficiencyHistogram->GetBinContent(efficiencyHistogram->FindBin(pt)); + return value == 0. ? -1. : 1. / value; + } + + template + int particleId(TTrack const& track) const + { + const std::array tpc{track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array selected = tpc; + if (track.pt() > energy.tofPtCut.value) { + if (!track.hasTOF()) { + return Unidentified; + } + selected = {std::hypot(tpc[0], track.tofNSigmaPi()), + std::hypot(tpc[1], track.tofNSigmaKa()), + std::hypot(tpc[2], track.tofNSigmaPr())}; + } + + int bestSpecies = Unidentified; + int compatibleSpecies = 0; + float bestNSigma = energy.nSigmaCut.value; + for (int species = 0; species < 3; ++species) { + const float absoluteNSigma = std::abs(selected[species]); + if (absoluteNSigma >= energy.nSigmaCut.value) { + continue; + } + ++compatibleSpecies; + if (absoluteNSigma < bestNSigma) { + bestNSigma = absoluteNSigma; + bestSpecies = species + 1; + } + } + if (energy.strictPid.value && compatibleSpecies > 1) { + return Unidentified; + } + return bestSpecies; + } + + double massForParticle(int pid) const + { + switch (pid) { + case Pion: + return o2::constants::physics::MassPionCharged; + case Kaon: + return o2::constants::physics::MassKPlus; + case Proton: + return o2::constants::physics::MassProton; + default: + return energy.unidentifiedMass.value; + } + } + + template + double transverseEnergy(TTrack const& track, int pid) const + { + if (energy.weightMode.value == PtWeight) { + return track.pt(); + } + const double mass = massForParticle(pid); + if (energy.weightMode.value == MtWeight) { + return std::hypot(track.pt(), mass); + } + return std::hypot(track.pt(), mass / std::cosh(track.eta())); + } + + template + bool isEfficiencyParticle(TParticle const& particle) const + { + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) >= cuts.etaMax.value || particle.pt() <= cuts.ptCuts.value.first || particle.pt() >= cuts.ptCuts.value.second) { + return false; + } + const auto* pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + return pdgParticle && pdgParticle->Charge() != 0.; + } + + template + int efficiencySpecies(TParticle const& particle) const + { + const int absolutePdg = std::abs(particle.pdgCode()); + if (absolutePdg == PDG_t::kPiPlus) { + return EfficiencyPion; + } + if (absolutePdg == PDG_t::kKPlus) { + return EfficiencyKaon; + } + if (absolutePdg == PDG_t::kProton) { + return EfficiencyProton; + } + return EfficiencyCharged; + } + + template + void fillEfficiencyParticle(TParticle const& particle, float centrality, EfficiencyStep step) + { + if (!isEfficiencyParticle(particle)) { + return; + } + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, EfficiencyCharged, step); + const int species = efficiencySpecies(particle); + if (species != EfficiencyCharged) { + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, species, step); + } + } + + template + void fillTrackingEfficiency(int mcCollisionIndex, TParticles const& particles, TTracks const& tracks, float centrality) + { + for (const auto& particle : particles) { + if (particle.mcCollisionId() != mcCollisionIndex) { + continue; + } + fillEfficiencyParticle(particle, centrality, EfficiencyGenerated); + } + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto& particle = track.mcParticle(); + if (particle.mcCollisionId() != mcCollisionIndex) { + continue; + } + fillEfficiencyParticle(particle, centrality, EfficiencyReconstructed); + } + } + + template + void fillAcceptanceWeights(TCollision const& collision, TTracks const& tracks, float centrality) + { + for (const auto& track : tracks) { + acceptanceWeights->fill(track.phi(), track.eta(), collision.posZ(), track.pt(), centrality, 0); + } + } + + template + void processCollision(TCollision const& collision, TTracks const& tracks, float centrality, float spectators) + { + float activity = centrality; + if (cfgActivityAxis.value == NchAxis) { + activity = static_cast(tracks.size()); + } else if (cfgActivityAxis.value == ZdcSpectatorAxis) { + activity = spectators; + } + if (!std::isfinite(activity)) { + return; + } + + gfw->Clear(); + for (const auto& track : tracks) { + const bool needsParticleId = energy.weightMode.value != PtWeight || cfgEfficiency.cfgUsePIDEfficiencies; + const int pid = needsParticleId ? particleId(track) : Unidentified; + const double et = transverseEnergy(track, pid); + const double weff = getEfficiency(track.pt(), pid); + const double wacc = acceptance ? acceptance->getNUA(track.phi(), track.eta(), collision.posZ()) : 1.; + if (!std::isfinite(et) || !std::isfinite(weff) || !std::isfinite(wacc) || et <= 0. || weff <= 0. || wacc <= 0.) { + continue; + } + gfw->Fill(track.eta(), 0, track.phi(), et * weff * wacc, gfwMask); + registry.fill(HIST("track/ptEta"), track.pt(), track.eta()); + registry.fill(HIST("track/energy"), et); + if (energy.weightMode.value != PtWeight) { + registry.fill(HIST("track/pid"), pid); + } + } + + registry.fill(HIST("event/centrality"), centrality); + registry.fill(HIST("event/activity"), activity); + registry.fill(HIST("event/vertex"), collision.posZ()); + fillFlowContainer(activity); + } + + void fillFlowContainer(float activity) + { + const double randomNumber = random->Rndm(); + for (std::size_t index = 0; index < corrConfigs.size(); ++index) { + const auto& config = corrConfigs[index]; + if (zeroHarmonicConfigs[index]) { + const double raw = gfw->Calculate(config, 0, false).real(); + if (std::isfinite(raw) && raw > 0.) { + const std::string rawName = config.Head + "_raw"; + flowContainer->FillProfile(rawName.c_str(), activity, raw, 1., randomNumber); + } + continue; + } + + const double denominator = gfw->Calculate(config, 0, true).real(); + if (!std::isfinite(denominator) || denominator <= 0.) { + continue; + } + const double raw = gfw->Calculate(config, 0, false).real(); + if (!std::isfinite(raw)) { + continue; + } + const std::string rawName = config.Head + "_raw"; + flowContainer->FillProfile(rawName.c_str(), activity, raw, 1., randomNumber); + flowContainer->FillProfile(config.Head.c_str(), activity, raw / denominator, denominator, randomNumber); + } + } + + void processRun2(CollisionsRun2::iterator const& collision, Tracks const& tracks, BCsRun2 const&, aod::Zdcs const&) + { + float spectators = -1.f; + if (!eventSelected(collision, run2EventSelected(collision), static_cast(tracks.size()), collision.centRun2V0M(), spectators)) { + return; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, tracks, collision.centRun2V0M()); + return; + } + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, tracks, collision.centRun2V0M(), spectators); + } + PROCESS_SWITCH(EnergyFlowGfw, processRun2, "Process Run 2 AOD data", false); + + void processRun2MC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + BCsRun2 const&, + aod::Zdcs const&, + TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + if (collisions.size() != 1) { + return; + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) >= cuts.vertexZ.value) { + return; + } + const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + float spectators = -1.f; + if (!eventSelected(collision, collision.alias_bit(kINT7) && collision.sel7(), static_cast(groupedTracks.size()), collision.centRun2V0M(), spectators)) { + return; + } + + bool filledCorrections = false; + if (cfgFillEfficiency) { + fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centRun2V0M()); + filledCorrections = true; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, groupedTracks, collision.centRun2V0M()); + filledCorrections = true; + } + if (filledCorrections) { + return; + } + + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, groupedTracks, collision.centRun2V0M(), spectators); + return; + } + } + PROCESS_SWITCH(EnergyFlowGfw, processRun2MC, "Process matched Run 2 MC for efficiency, acceptance, or reconstructed closure", false); + + void processRun3(CollisionsRun3::iterator const& collision, Tracks const& tracks, BCsRun3 const&, aod::Zdcs const&) + { + float spectators = -1.f; + if (!eventSelected(collision, collision.sel8(), static_cast(tracks.size()), collision.centFT0C(), spectators)) { + return; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, tracks, collision.centFT0C()); + return; + } + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, tracks, collision.centFT0C(), spectators); + } + PROCESS_SWITCH(EnergyFlowGfw, processRun3, "Process Run 3 AOD data", true); + + void processRun3MC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, + BCsRun3 const&, + aod::Zdcs const&, + TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + if (collisions.size() != 1) { + return; + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) >= cuts.vertexZ.value) { + return; + } + const auto groupedTracks = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + float spectators = -1.f; + if (!eventSelected(collision, collision.sel8(), static_cast(groupedTracks.size()), collision.centFT0C(), spectators)) { + return; + } + + bool filledCorrections = false; + if (cfgFillEfficiency) { + fillTrackingEfficiency(mcCollision.globalIndex(), mcParticles, groupedTracks, collision.centFT0C()); + filledCorrections = true; + } + if (cfgFillAcceptance) { + fillAcceptanceWeights(collision, groupedTracks, collision.centFT0C()); + filledCorrections = true; + } + if (filledCorrections) { + return; + } + + const auto& bc = collision.foundBC_as(); + loadEfficiency(bc.timestamp()); + loadAcceptance(bc.timestamp(), bc.runNumber()); + processCollision(collision, groupedTracks, collision.centFT0C(), spectators); + return; + } + } + PROCESS_SWITCH(EnergyFlowGfw, processRun3MC, "Process matched Run 3 MC for efficiency, acceptance, or reconstructed closure", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx b/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx index 804ca8d5975..da76264b369 100644 --- a/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGenericFramework.cxx @@ -13,22 +13,14 @@ /// \brief Task to analyse angular and transverse momentum correlations with GFW /// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch -#include "FlowContainer.h" -#include "FlowPtContainer.h" -#include "GFW.h" -#include "GFWConfig.h" -#include "GFWCumulant.h" -#include "GFWPowerArray.h" -#include "GFWWeights.h" -#include "GFWWeightsList.h" - -#include "PWGLF/DataModel/EPCalibrationTables.h" +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/FlowPtContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWConfig.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/Core/RecoDecay.h" -#include "Common/Core/TrackSelection.h" -#include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -37,76 +29,73 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" -#include "CommonConstants/PhysicsConstants.h" -#include "Framework/ASoAHelpers.h" -#include "Framework/AnalysisDataModel.h" -#include "Framework/AnalysisTask.h" -#include "Framework/HistogramRegistry.h" -#include "Framework/RunningWorkflowInfo.h" -#include "Framework/StepTHn.h" -#include "Framework/runDataProcessing.h" -#include "ReconstructionDataFormats/PID.h" -#include "ReconstructionDataFormats/Track.h" #include +#include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include #include #include #include +#include + +#include + +#include #include +#include +#include +#include #include +#include +#include +#include #include +#include +#include #include -#include +#include +#include #include +#include +#include #include #include using namespace o2; using namespace o2::framework; +using namespace o2::analysis::genericframework; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) -namespace o2::analysis::gfw -{ -std::vector ptbinning = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}; -float ptpoilow = 0.2, ptpoiup = 10.0; -float ptreflow = 0.2, ptrefup = 3.0; -float ptlow = 0.2, ptup = 10.0; -int etabins = 16; -float etalow = -0.8, etaup = 0.8; -int vtxZbins = 40; -float vtxZlow = -10.0, vtxZup = 10.0; -int phibins = 72; -float philow = 0.0; -float phiup = o2::constants::math::TwoPI; -int nchbins = 300; -float nchlow = 0; -float nchup = 3000; -std::vector centbinning(90); -int nBootstrap = 10; -genericframework::GFWRegions regions; -genericframework::GFWCorrConfigs configs; -genericframework::GFWCorrConfigs configsV02; -genericframework::GFWCorrConfigs configsV0; -std::vector> etagapsPtPt; -std::vector multGlobalCorrCutPars; -std::vector multPVCorrCutPars; -std::vector multGlobalPVCorrCutPars; -std::vector multGlobalV0ACutPars; -std::vector multGlobalT0ACutPars; -} // namespace o2::analysis::gfw +constexpr float DefaultMagneticFieldCut = 99999.f; template auto projectMatrix(Array2D const& mat, std::array& array1, std::array& array2, std::array& array3) { - for (auto j = 0; j < static_cast(mat.cols); ++j) { - array1[j] = mat(0, j); - array2[j] = mat(1, j); - array3[j] = mat(2, j); + for (auto j = 0; j < static_cast(mat.rows); ++j) { + array1[j] = mat(j, 0); + array2[j] = mat(j, 1); + array3[j] = mat(j, 2); } return; } @@ -122,62 +111,86 @@ auto readMatrix(Array2D const& mat, P& array) return; } -static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {2.1, 2.2, 2.3, -2.1, -2.2, -2.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {3.1, 3.2, 3.3, -3.1, -3.2, -3.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}}; -static constexpr int LongArrayInt[3][20] = {{1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1}, {2, 2, 2, -2, -2, -2, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1}, {3, 3, 3, -3, -3, -3, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, 1}}; -static constexpr double LongArrayDouble[4][2] = {{-0.8, -0.5}, {0.5, 0.8}, {-2, -2}, {-2, -2}}; +// static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {2.1, 2.2, 2.3, -2.1, -2.2, -2.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {3.1, 3.2, 3.3, -3.1, -3.2, -3.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}}; +// static constexpr int LongArrayInt[3][20] = {{1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {3, 3, 3, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}}; +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; +static constexpr std::array, 4> LongArrayDouble = {{{{-0.8, -0.5}}, {{0.5, 0.8}}, {{-2, -2}}, {{-2, -2}}}}; struct FlowGenericFramework { - O2_DEFINE_CONFIGURABLE(cfgUseNewNpt, bool, true, "Use alternative filling for fractions") O2_DEFINE_CONFIGURABLE(cfgNbootstrap, int, 10, "Number of subsamples") O2_DEFINE_CONFIGURABLE(cfgMpar, int, 8, "Highest order of pt-pt correlations") O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A, 4:NTPV, 5:NGlobal, 6:MFT") - O2_DEFINE_CONFIGURABLE(cfgUseNch, bool, false, "Do correlations as function of Nch") - O2_DEFINE_CONFIGURABLE(cfgUseNchCorrection, int, 1, "Use correction for Nch; 0: Use size of tracks table, 1: Use efficiency-corrected Nch values, 2: Use uncorrected Nch values"); - O2_DEFINE_CONFIGURABLE(cfgFillWeights, bool, false, "Fill NUA weights") - O2_DEFINE_CONFIGURABLE(cfgRunByRun, bool, false, "Fill histograms on a run-by-run basis") - O2_DEFINE_CONFIGURABLE(cfgFillQA, bool, false, "Fill QA histograms") - O2_DEFINE_CONFIGURABLE(cfgMultCut, bool, false, "Use additional event cut on mult correlations"); - O2_DEFINE_CONFIGURABLE(cfgUseCentralMoments, bool, true, "Use central moments in vn-pt calculations") - O2_DEFINE_CONFIGURABLE(cfgUsePID, bool, true, "Enable PID information") - O2_DEFINE_CONFIGURABLE(cfgUseGapMethod, bool, false, "Use gap method in vn-pt calculations") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgUsePIDEfficiencies, bool, false, "Use species dependent efficiencies") + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgRunByRun, bool, false, "Use run-by-run NUA"); + O2_DEFINE_CONFIGURABLE(cfgFillWeights, bool, false, "Fill NUA weights"); + O2_DEFINE_CONFIGURABLE(cfgFillQA, bool, false, "Fill QA histograms"); + O2_DEFINE_CONFIGURABLE(cfgFillV0QA, bool, true, "Fill QA histograms for V0 reconstruction"); + O2_DEFINE_CONFIGURABLE(cfgFillRunByRunQA, bool, false, "Fill histograms on a run-by-run basis"); + O2_DEFINE_CONFIGURABLE(cfgAnalyseChargedHadrons, bool, true, "Store radial-flow and fraction outputs for charged tracks, pions, kaons, and protons"); + O2_DEFINE_CONFIGURABLE(cfgAnalyseK0Lambda, bool, true, "Store radial-flow and fraction outputs for K0 and Lambda candidates"); + O2_DEFINE_CONFIGURABLE(cfgUsePID, bool, true, "Enable PID information"); + O2_DEFINE_CONFIGURABLE(cfgUsePIDTotal, bool, false, "use fraction of PID total"); + } cfgFill; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgUseCentralMoments, bool, true, "Use central moments in vn-pt calculations"); + O2_DEFINE_CONFIGURABLE(cfgUseGapMethod, bool, false, "Use gap method in vn-pt calculations"); + O2_DEFINE_CONFIGURABLE(cfgUsePtCorrWeights, bool, true, "Enable or disable the use of multiplicity-based event weighting for pt-pt correlations"); + O2_DEFINE_CONFIGURABLE(cfgUseMultiplicityFlowWeights, bool, true, "Enable or disable the use of multiplicity-based event weighting for azimuthal correlations"); + O2_DEFINE_CONFIGURABLE(cfgUseMultiplicityFractionWeights, bool, false, "Enable or disable the use of multiplicity-based event weighting for the spectral fraction"); + O2_DEFINE_CONFIGURABLE(cfgStoreCombinedFractionWeights, bool, false, "Store additional V02 fractions weighted by flow tuples times dnsV02, and V0 fractions and mean pT weighted by corrDen[1] times dnsV0"); + Configurable> cfgFlowProfileWeightModes{"cfgFlowProfileWeightModes", {}, "Per cfgCorrConfig: 0 uses the flow tuple denominator, 1 uses that denominator times dnsV02; empty keeps legacy behavior"}; + } cfgMethod; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEfficiencyPath, std::string, "", "CCDB path to efficiency object"); + O2_DEFINE_CONFIGURABLE(cfgUsePIDEfficiencies, bool, false, "Use species dependent efficiencies"); + O2_DEFINE_CONFIGURABLE(cfgUse2DEfficiency, bool, false, "Toggle the use of 2D (pt, centrality) efficiency versus centrality integrated efficiency"); + O2_DEFINE_CONFIGURABLE(cfgUseDensityDependentCorrection, bool, false, "Use density dependent efficiency correction based on Run 2 measurements"); + } cfgEfficiency; O2_DEFINE_CONFIGURABLE(cfgAcceptance, std::string, "", "CCDB path to acceptance object") - O2_DEFINE_CONFIGURABLE(cfgPtmin, float, 0.2, "minimum pt (GeV/c)"); - O2_DEFINE_CONFIGURABLE(cfgPtmax, float, 10, "maximum pt (GeV/c)"); - Configurable> cfgEta{"cfgEta", {-0.8, 0.8}, "eta cut"}; - Configurable> cfgEtaNch{"cfgEtaNch", {-0.5, 0.5}, "eta cut for nch selection"}; - Configurable> cfgEtaPtPt{"cfgEtaPtPt", {-0.5, 0.5}, "eta for pt-pt correlation"}; - Configurable> cfgEtaV0Daughters{"cfgEtaV0Daughters", {-0.5, 0.5}, "eta cut on V0 daughter particles"}; - Configurable> cfgPtPtGaps{"cfgPtPtGaps", {LongArrayDouble[0], 4, 2, {"subevent 1", "subevent 2", "subevent 3", "subevent 4"}, {"etamin", "etamax"}}, "{etamin,etamax} for all ptpt-subevents"}; - O2_DEFINE_CONFIGURABLE(cfgUsePIDTotal, bool, false, "use fraction of PID total"); + struct : ConfigurableGroup { + Configurable> cfgEtaNptV02{"cfgEtaNptV02", {-0.5, 0.5}, "eta cut for v02 fractional spectra"}; + Configurable> cfgEtaNptV0{"cfgEtaNptV0", {-0.5, 0.5}, "eta cut for v0 fractional spectra"}; + Configurable> cfgEta{"cfgEta", {-0.8, 0.8}, "eta cut"}; + Configurable> cfgEtaNch{"cfgEtaNch", {-0.5, 0.5}, "eta cut for nch selection"}; + Configurable> cfgEtaPtPt{"cfgEtaPtPt", {-0.5, 0.5}, "eta for pt-pt correlation"}; + O2_DEFINE_CONFIGURABLE(cfgPtMin, float, 0.2, "minimum pt (GeV/c)"); + O2_DEFINE_CONFIGURABLE(cfgPtmax, float, 10, "maximum pt (GeV/c)"); + } cfgKinematics; + Configurable> cfgPtPtGaps{"cfgPtPtGaps", {LongArrayDouble.front().data(), 4, 2, {"subevent 1", "subevent 2", "subevent 3", "subevent 4"}, {"etamin", "etamax"}}, "{etamin,etamax} for all ptpt-subevents"}; O2_DEFINE_CONFIGURABLE(cfgVtxZ, float, 10, "vertex cut (cm)"); struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"); + O2_DEFINE_CONFIGURABLE(cfgDCAxyPtDep, std::string, "(0.0105 + 0.0350/(x^1.1))", "Functional form of pt-dependent 7 sigma DCAxy cut"); + O2_DEFINE_CONFIGURABLE(cfgDCAzPtDep, std::string, "(0.0105 + 0.0350/(x^1.1))", "Functional form of pt-dependent DCAz cut"); O2_DEFINE_CONFIGURABLE(cfgDCAz, float, 2, "Cut on DCA in the longitudinal direction (cm)"); O2_DEFINE_CONFIGURABLE(cfgNTPCCls, float, 50, "Cut on number of TPC clusters found"); O2_DEFINE_CONFIGURABLE(cfgNTPCXrows, float, 70, "Cut on number of TPC crossed rows"); + O2_DEFINE_CONFIGURABLE(cfgNTPCXrowsDaughters, float, 70, "Cut on number of TPC crossed rows of V0 daughters"); O2_DEFINE_CONFIGURABLE(cfgMinNITSCls, float, 5, "Cut on minimum number of ITS clusters found"); O2_DEFINE_CONFIGURABLE(cfgChi2PrITSCls, float, 36, "Cut on chi^2 per ITS clusters found"); O2_DEFINE_CONFIGURABLE(cfgChi2PrTPCCls, float, 2.5, "Cut on chi^2 per TPC clusters found"); O2_DEFINE_CONFIGURABLE(cfgTPCSectorCut, bool, false, "Cut on pt-phi distribution"); } cfgTrackCuts; struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgNoSameBunchPileupCut, bool, true, "kNoSameBunchPileupCut"); - O2_DEFINE_CONFIGURABLE(cfgIsGoodZvtxFT0vsPV, bool, true, "kIsGoodZvtxFT0vsPV"); - O2_DEFINE_CONFIGURABLE(cfgIsGoodITSLayersAll, bool, true, "kIsGoodITSLayersAll"); - O2_DEFINE_CONFIGURABLE(cfgNoCollInTimeRangeStandard, bool, true, "kNoCollInTimeRangeStandard"); - O2_DEFINE_CONFIGURABLE(cfgNoCollInRofStandard, bool, true, "kNoCollInRofStandard"); - O2_DEFINE_CONFIGURABLE(cfgNoHighMultCollInPrevRof, bool, true, "kNoHighMultCollInPrevRof"); - O2_DEFINE_CONFIGURABLE(cfgNoITSROFrameBorder, bool, true, "kNoITSROFrameBorder"); - O2_DEFINE_CONFIGURABLE(cfgNoTimeFrameBorder, bool, true, "kNoTimeFrameBorder"); - O2_DEFINE_CONFIGURABLE(cfgTVXinTRD, bool, true, "kTVXinTRD - Use kTVXinTRD (reject TRD triggered events)"); - O2_DEFINE_CONFIGURABLE(cfgIsVertexITSTPC, bool, true, "kIsVertexITSTPC - Selects collisions with at least one ITS-TPC track"); + O2_DEFINE_CONFIGURABLE(cfgNoSameBunchPileupCut, bool, true, "NoSameBunchPileupCut"); + O2_DEFINE_CONFIGURABLE(cfgIsGoodZvtxFT0vsPV, bool, true, "IsGoodZvtxFT0vsPV"); + O2_DEFINE_CONFIGURABLE(cfgIsGoodITSLayersAll, bool, true, "IsGoodITSLayersAll"); + O2_DEFINE_CONFIGURABLE(cfgNoCollInTimeRangeStandard, bool, true, "NoCollInTimeRangeStandard"); + O2_DEFINE_CONFIGURABLE(cfgNoCollInRofStandard, bool, true, "NoCollInRofStandard"); + O2_DEFINE_CONFIGURABLE(cfgNoHighMultCollInPrevRof, bool, true, "NoHighMultCollInPrevRof"); + O2_DEFINE_CONFIGURABLE(cfgNoITSROFrameBorder, bool, true, "NoITSROFrameBorder"); + O2_DEFINE_CONFIGURABLE(cfgNoTimeFrameBorder, bool, true, "NoTimeFrameBorder"); + O2_DEFINE_CONFIGURABLE(cfgTVXinTRD, bool, true, "TVXinTRD - Use TVXinTRD (reject TRD triggered events)"); + O2_DEFINE_CONFIGURABLE(cfgIsVertexITSTPC, bool, true, "IsVertexITSTPC - Selects collisions with at least one ITS-TPC track"); } cfgEventCutFlags; - O2_DEFINE_CONFIGURABLE(cfgOccupancySelection, int, 2000, "Max occupancy selection, -999 to disable"); - O2_DEFINE_CONFIGURABLE(cfgDoOccupancySel, bool, true, "Bool for event selection on detector occupancy"); - O2_DEFINE_CONFIGURABLE(cfgMagField, float, 99999, "Configurable magnetic field; default CCDB will be queried"); - O2_DEFINE_CONFIGURABLE(cfgUseDensityDependentCorrection, bool, false, "Use density dependent efficiency correction based on Run 2 measurements"); + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgUseNch, bool, false, "Do correlations as function of Nch"); + O2_DEFINE_CONFIGURABLE(cfgUseNchCorrection, int, 1, "Use correction for Nch; 0: Use size of tracks table, 1: Use efficiency-corrected Nch values, 2: Use uncorrected Nch values"); + O2_DEFINE_CONFIGURABLE(cfgOccupancySelection, int, -999, "Max occupancy selection, -999 to disable"); + O2_DEFINE_CONFIGURABLE(cfgMagField, float, 99999, "Configurable magnetic field; default CCDB will be queried"); + O2_DEFINE_CONFIGURABLE(cfgMultCut, bool, false, "Use additional event cut on mult correlations"); + } cfgEventSelection; Configurable> cfgTrackDensityP0{"cfgTrackDensityP0", std::vector{0.7217476707, 0.7384792571, 0.7542625668, 0.7640680200, 0.7701951667, 0.7755299053, 0.7805901710, 0.7849446786, 0.7957356586, 0.8113039262, 0.8211968966, 0.8280558878, 0.8329342135}, "parameter 0 for track density efficiency correction"}; Configurable> cfgTrackDensityP1{"cfgTrackDensityP1", std::vector{-2.169488e-05, -2.191913e-05, -2.295484e-05, -2.556538e-05, -2.754463e-05, -2.816832e-05, -2.846502e-05, -2.843857e-05, -2.705974e-05, -2.477018e-05, -2.321730e-05, -2.203315e-05, -2.109474e-05}, "parameter 1 for track density efficiency correction"}; struct : ConfigurableGroup { @@ -195,18 +208,18 @@ struct FlowGenericFramework { O2_DEFINE_CONFIGURABLE(cfgGlobalT0ALowSigma, float, -3., "Number of sigma deviations below expected value in global vs T0A correlation"); O2_DEFINE_CONFIGURABLE(cfgGlobalT0AHighSigma, float, 4, "Number of sigma deviations above expected value in global vs T0A correlation"); } cfgMultCorrCuts; + Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"pos_pi", "pos_ka", "pos_pr", "neg_pi", "neg_ka", "neg_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable> resonanceCuts{"resonanceCuts", {LongArrayFloat.front().data(), 14, 3, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "DCAxDaughters", "Lifetime", "RadiusMin", "RadiusMax", "Rapidity", "ArmPodMin", "MassRejection"}, {"K0", "Lambda", "Phi"}}, "Labeled array (float) for various cuts on resonances"}; + Configurable> resonanceSwitches{"resonanceSwitches", {LongArrayInt.front().data(), 8, 3, {"UseParticle", "UseCosPA", "NMassBins", "UseDCAxDaughters", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompetingMassRejection"}, {"K0", "Lambda", "Phi"}}, "Labeled array (int) for various cuts on resonances"}; + struct : ConfigurableGroup { - Configurable> nSigmas{"nSigmas", {LongArrayFloat[0], 3, 6, {"TPC", "TOF", "ITS"}, {"pos_pi", "pos_ka", "pos_pr", "neg_pi", "neg_ka", "neg_pr"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; - Configurable> resonanceCuts{"resonanceCuts", {LongArrayFloat[0], 3, 11, {"K0", "Lambda", "Phi"}, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "Rapidity"}}, "Labeled array (float) for various cuts on resonances"}; - Configurable> resonanceSwitches{"resonanceSwitches", {LongArrayInt[0], 3, 6, {"K0", "Lambda", "Phi"}, {"UseParticle", "UseCosPA", "NMassBins", "DCABetDaug", "UseProperLifetime", "UseV0Radius"}}, "Labeled array (int) for various cuts on resonances"}; - O2_DEFINE_CONFIGURABLE(cfgUseLsPhi, bool, true, "Use LikeSign for Phi v2") - O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") - O2_DEFINE_CONFIGURABLE(cfgFakeKaonCut, float, 0.1f, "Maximum difference in measured momentum and TPC inner ring momentum of particle") - O2_DEFINE_CONFIGURABLE(cfgUseAsymmetricPID, bool, false, "Use asymmetric PID cuts") - O2_DEFINE_CONFIGURABLE(cfgTPCNsigmaCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") - O2_DEFINE_CONFIGURABLE(cfgUseStrictPID, bool, true, "Use strict PID cuts for TPC") + O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection"); + O2_DEFINE_CONFIGURABLE(cfgDaughterPIDRejection, bool, true, "Reject daughters if not consistent with expected K0/Lambda decay products"); + O2_DEFINE_CONFIGURABLE(cfgUseAsymmetricPID, bool, false, "Use asymmetric PID cuts"); + O2_DEFINE_CONFIGURABLE(cfgTPCNsigmaCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons"); + O2_DEFINE_CONFIGURABLE(cfgUseStrictPID, bool, true, "Use strict PID cuts for TPC"); O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.5, "pt cut on TOF for PID"); - O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification"); O2_DEFINE_CONFIGURABLE(cfgK0SignalMin, float, 0.48, "Minimum cut on K0 mT signal"); O2_DEFINE_CONFIGURABLE(cfgK0SignalMax, float, 0.51, "Maximum cut on K0 mT signal"); O2_DEFINE_CONFIGURABLE(cfgLambdaSignalMin, float, 1.1, "Minimum cut on Lambda mT signal"); @@ -221,91 +234,141 @@ struct FlowGenericFramework { O2_DEFINE_CONFIGURABLE(cfgLambdaSideBand2Max, float, 1.6, "Maximum cut on Lambda side band 2"); } cfgPIDCuts; - Configurable cfgGFWBinning{"cfgGFWBinning", {40, 16, 72, 300, 0, 3000, 0.2, 10.0, 0.2, 3.0, {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5, 5.5, 6, 7, 8, 9, 10}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}}, "Configuration for binning"}; - Configurable cfgRegions{"cfgRegions", {{"refN", "refP", "refFull"}, {-0.8, 0.4, -0.8}, {-0.4, 0.8, 0.8}, {0, 0, 0}, {1, 1, 1}}, "Configurations for GFW regions"}; + Configurable cfgGFWBinning{"cfgGFWBinning", {40, 16, 72, 300, 0, 3000, 0.2, 10.0, 0.2, 3.0, {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5, 5.5, 6, 7, 8, 9, 10}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}}, "Configuration for binning"}; + Configurable cfgRegions{"cfgRegions", {{"refN", "refP", "refFull"}, {-0.8, 0.4, -0.8}, {-0.4, 0.8, 0.8}, {0, 0, 0}, {1, 1, 1}}, "Configurations for GFW regions"}; - Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}", "refFull {2 -2}", "refFull {2 2 -2 -2}"}, {"ChGap22", "ChGap32", "ChGap42", "ChFull22", "ChFull24"}, {0, 0, 0, 0, 0}, {15, 1, 1, 0, 0}}, "Configurations for each correlation to calculate"}; - Configurable cfgCorrConfigV02{"cfgCorrConfigV02", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}"}, {"ChGap22", "ChGap32", "ChGap42"}, {1, 1, 1}, {0, 0, 0}}, "Configurations for each radial flow correlation to calculate"}; - Configurable cfgCorrConfigV0{"cfgCorrConfigV0", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}"}, {"ChGap22", "ChGap32", "ChGap42"}, {1, 1, 1}, {0, 0, 0}}, "Configurations for each radial flow correlation to calculate"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}", "refFull {2 -2}", "refFull {2 2 -2 -2}"}, {"ChGap22", "ChGap32", "ChGap42", "ChFull22", "ChFull24"}, {0, 0, 0, 0, 0}, {15, 1, 1, 0, 0}}, "Configurations for each correlation to calculate"}; + Configurable cfgCorrConfigV02{"cfgCorrConfigV02", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}"}, {"ChGap22", "ChGap32", "ChGap42"}, {1, 1, 1}, {0, 0, 0}}, "Configurations for each radial flow correlation to calculate"}; + Configurable cfgCorrConfigV0{"cfgCorrConfigV0", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}"}, {"ChGap22", "ChGap32", "ChGap42"}, {1, 1, 1}, {0, 0, 0}}, "Configurations for each radial flow correlation to calculate"}; ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisFeeddownXiPt{"axisFeeddownXiPt", {VARIABLE_WIDTH, 0, 0.1, 0.5, 0.9, 1.1, 1.3, 1.5, 1.7, 1.9, 2.1, 2.3, 2.5, 2.7, 2.9, 3.9, 4.9, 5.9, 9.9, 16.0}, "Xi pt axis for Lambda feeddown correction"}; + + struct GFWMemberCache { + std::vector ptbinning = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}; + float ptpoilow = 0.2; + float ptpoiup = 10.0; + float ptreflow = 0.2; + float ptrefup = 3.0; + float ptlow = 0.2; + float ptup = 10.0; + int etabins = 100; + float etalow = -1; + float etaup = 1; + int vtxZbins = 40; + float vtxZlow = -10.0; + float vtxZup = 10.0; + int phibins = 72; + float philow = 0.0; + float phiup = o2::constants::math::TwoPI; + int nchbins = 300; + float nchlow = 0; + float nchup = 3000; + std::vector centbinning = std::vector(90); + GFWRegions regions; + GFWCorrConfigs configs; + GFWCorrConfigs configsV02; + GFWCorrConfigs configsV0; + std::vector> etagapsPtPt; + std::vector multGlobalCorrCutPars; + std::vector multPVCorrCutPars; + std::vector multGlobalPVCorrCutPars; + std::vector multGlobalV0ACutPars; + std::vector multGlobalT0ACutPars; + } gfwMemberCache; // Connect to ccdb - Service ccdb; + Service ccdb{}; + Service pdgDB{}; o2::aod::ITSResponse itsResponse; struct Config { - TH1D* mEfficiency = nullptr; - std::vector mPIDEfficiencies; + TH1* mEfficiency = nullptr; + std::vector mPIDEfficiencies; std::vector mAcceptance; + std::map>> mAcceptanceByRun; bool correctionsLoaded = false; } cfg; // Define output - OutputObj fFC{FlowContainer("FlowContainer")}; - OutputObj fFCpt{FlowPtContainer("FlowPtContainer")}; - OutputObj fFCgen{FlowContainer("FlowContainer_gen")}; + OutputObj fFC{"FlowContainer"}; + OutputObj fFCpt{"FlowPtContainer"}; + OutputObj fFCgen{"FlowContainer_gen"}; + OutputObj fFCptGen{"FlowPtContainer_gen"}; HistogramRegistry registry{"registry"}; - - std::array, 3> resoCutVals; - std::array, 3> resoSwitchVals; - std::array tofNsigmaCut; - std::array itsNsigmaCut; - std::array tpcNsigmaCut; + HistogramRegistry registryGen{"registryGen"}; + HistogramRegistry registryQA{"registryQA"}; + + std::array, 14> resoCutVals{}; + std::array, 8> resoSwitchVals{}; + std::array tofNsigmaCut{}; + std::array itsNsigmaCut{}; + std::array tpcNsigmaCut{}; + + enum FractionSetup { + FractionV02 = 0, + FractionV0, + FractionSetupCount + }; + enum EfficiencyStep { + EfficiencyGenerated = 0, + EfficiencyReconstructed, + EfficiencyStepCount + }; // QA outputs std::map>> th1sList; std::map>> th3sList; - std::vector> histosNpt; - std::vector> histosResoNpt; + std::array>, FractionSetupCount> histosNpt; + std::array>, FractionSetupCount> histosResoNpt; enum OutputTH1Names { - hPhi = 0, - hEta, - hVtxZ, - hMult, - hCent, - hEventSel, - kCount_TH1Names + Phi = 0, + Eta, + VtxZ, + Mult, + Cent, + EventSel, + TH1NameCount }; // NUA outputs enum OutputTH3Names { - hNUAref = 0, - hNUAch, - hNUApi, - hNUAka, - hNUApr, - hPtPhiMult, - kCount_TH3Names + NUAref = 0, + NUAch, + NUApi, + NUAka, + NUApr, + PtPhiMult, + TH3NameCount }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, - kCentNTPV, - kCentNGlobal, - kCentMFT + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT }; - std::map centNamesMap = {{kCentFT0C, "FT0C"}, {kCentFT0CVariant1, "FT0C variant1"}, {kCentFT0M, "FT0M"}, {kCentFV0A, "FV0A"}, {kCentNTPV, "NTPV"}, {kCentNGlobal, "NGlobal"}, {kCentMFT, "MFT"}}; + std::map centNamesMap = {{CentFT0C, "FT0C"}, {CentFT0CVariant1, "FT0C variant1"}, {CentFT0M, "FT0M"}, {CentFV0A, "FV0A"}, {CentNTPV, "NTPV"}, {CentNGlobal, "NGlobal"}, {CentMFT, "MFT"}}; enum EventSelFlags { - kFilteredEvent = 1, - kSel8, - kOccupancy, - kTVXinTRD, - kNoSameBunchPileup, - kIsGoodZvtxFT0vsPV, - kNoCollInTimeRangeStandard, - kNoCollInRofStandard, - kNoHighMultCollInPrevRof, - kNoTimeFrameBorder, - kNoITSROFrameBorder, - kIsVertexITSTPC, - kIsGoodITSLayersAll, - kMultCuts, - kTrackCent + FilteredEvent = 1, + Sel8, + Occupancy, + TVXinTRD, + NoSameBunchPileup, + IsGoodZvtxFT0vsPV, + NoCollInTimeRangeStandard, + NoCollInRofStandard, + NoHighMultCollInPrevRof, + NoTimeFrameBorder, + NoITSROFrameBorder, + IsVertexITSTPC, + IsGoodITSLayersAll, + MultCuts, + TrackCent }; struct EventCut { bool enabled; @@ -314,66 +377,117 @@ struct FlowGenericFramework { }; std::vector eventcutflags; enum Particles { - PIONS, - KAONS, - PROTONS + Pions, + Kaons, + Protons }; enum ParticleIDs { - CHARGEDID = 0, - PIONID, - KAONID, - PROTONID, - SPECIESCOUNT + ChargedID = 0, + PionID, + KaonID, + ProtonID, + SpeciesCount + }; + enum EfficiencySpecies { + EfficiencyCharged = 0, + EfficiencyPion, + EfficiencyKaon, + EfficiencyProton, + EfficiencyK0, + EfficiencyLambda, + EfficiencySpeciesCount + }; + enum V0EfficiencyStage { + V0EfficiencyCandidate = 0, + V0EfficiencyDaughterMCLabels, + V0EfficiencyDaughterMothers, + V0EfficiencyCommonPrimaryMother, + V0EfficiencyMotherSpecies, + V0EfficiencyMotherRapidity, + V0EfficiencySelection, + V0EfficiencyGeneratedDaughterEta, + V0EfficiencyRecoDaughterEta, + V0EfficiencyFilled, + V0EfficiencyStageCount + }; + enum V0EfficiencyDaughterPdgCategory { + V0EfficiencyDaughterOther = 0, + V0EfficiencyDaughterPiPlus, + V0EfficiencyDaughterPiMinus, + V0EfficiencyDaughterProton, + V0EfficiencyDaughterAntiProton, + V0EfficiencyDaughterElectron, + V0EfficiencyDaughterPositron, + V0EfficiencyDaughterKaon, + V0EfficiencyDaughterPdgCategoryCount + }; + enum LambdaFeeddownSpecies { + FeeddownLambdaFromXiMinus = 0, + FeeddownAntiLambdaFromXiPlus, + LambdaFeeddownSpeciesCount }; enum ResoIDs { - K0SIDEBAND1 = 0, - K0SIGNAL, - K0SIDEBAND2, - LAMBDASIDEBAND1, - LAMBDASIGNAL, - LAMBDASIDEBAND2, - RESOCOUNT + K0Sideband1 = 0, + K0Signal, + K0Sideband2, + LambdaSideband1, + LambdaSignal, + LambdaSideband2, + ResonanceCount + }; + enum DataType { + Reco, + Gen }; enum OutputSpecies { K0 = 0, - LAMBDA = 1, - PHI = 2, - ANLAMBDA = 3, - REF = 4, - kCount_OutputSpecies + Lambda, + PhiMeson, + LambdaBar, + RefParticle, + OutputSpeciesCount }; enum ParticleCuts { - kCosPA = 0, - kMassMin, - kMassMax, - kPosTrackPt, - kNegTrackPt, - kDCAPosToPVMin, - kDCANegToPVMin, - kLifeTime, - kRadiusMin, - kRadiusMax, - kRapidity + CosPA = 0, + MassMin, + MassMax, + PosTrackPt, + NegTrackPt, + DCAPosToPVMin, + DCANegToPVMin, + DCAxDaughters, + LifeTime, + RadiusMin, + RadiusMax, + Rapidity, + ArmPodMin, + MassRejection }; enum ParticleSwitches { - kUseParticle = 0, - kUseCosPA, - kMassBins, - kDCABetDaug, - kUseProperLifetime, - kUseV0Radius + UseParticle = 0, + UseCosPA, + MassBins, + UseDCAxDaughters, + UseProperLifetime, + UseV0Radius, + UseArmPodCut, + UseCompetingMassRejection }; - enum V0Selection { - kFillCandidate = 1, - kFillDaughterPt, - kFillMassCut, - kFillRapidityCut, - kFillDCAtoPV, - kFillDCAxDaughters, - kFillV0Radius, - kFillCosPA, - kFillProperLifetime, - kFillDaughterTrackSelection + enum V0SelectionFlags { + FillCandidate = 1, + FillDaughterPt, + FillMassCut, + FillRapidityCut, + FillDCAtoPV, + FillDCAxDaughters, + FillV0Radius, + FillCosPA, + FillProperLifetime, + FillArmPodCut, + FillCompetingMass, + FillDaughterTrackSelected, + FillV02DaughterTrackSelection, + FillV0DaughterTrackSelection }; // Define global variables @@ -385,25 +499,24 @@ struct FlowGenericFramework { std::vector corrconfigsV0; TRandom3* fRndm = new TRandom3(0); - TAxis* fPtAxis; + TAxis* fPtAxis = nullptr; int lastRun = -1; std::vector runNumbers; // Density dependent eff correction std::vector funcEff; - TH1D* hFindPtBin; - TF1* funcV2; - TF1* funcV3; - TF1* funcV4; + TH1D* hFindPtBin = nullptr; + TF1* funcV2 = nullptr; + TF1* funcV3 = nullptr; + TF1* funcV4 = nullptr; struct DensityCorr { - double psi2Est; - double psi3Est; - double psi4Est; - double v2; - double v3; - double v4; - int density; - DensityCorr() : psi2Est(0.), psi3Est(0.), psi4Est(0.), v2(0.), v3(0.), v4(0.), density(0) {} + double psi2Est = 0.; + double psi3Est = 0.; + double psi4Est = 0.; + double v2 = 0.; + double v3 = 0.; + double v4 = 0.; + int density = 0; }; // Event selection cuts - multiplicity correlation @@ -421,94 +534,108 @@ struct FlowGenericFramework { TF1* fPhiCutLow = nullptr; TF1* fPhiCutHigh = nullptr; + // Track selection - DCA functions + TF1* fPtDepDCAxy = nullptr; + TF1* fPtDepDCAz = nullptr; + void init(InitContext const&) { LOGF(info, "FlowGenericFramework::init()"); - o2::analysis::gfw::regions.SetNames(cfgRegions->GetNames()); - o2::analysis::gfw::regions.SetEtaMin(cfgRegions->GetEtaMin()); - o2::analysis::gfw::regions.SetEtaMax(cfgRegions->GetEtaMax()); - o2::analysis::gfw::regions.SetpTDifs(cfgRegions->GetpTDifs()); - o2::analysis::gfw::regions.SetBitmasks(cfgRegions->GetBitmasks()); - o2::analysis::gfw::configs.SetCorrs(cfgCorrConfig->GetCorrs()); - o2::analysis::gfw::configs.SetHeads(cfgCorrConfig->GetHeads()); - o2::analysis::gfw::configs.SetpTDifs(cfgCorrConfig->GetpTDifs()); - o2::analysis::gfw::configs.SetpTCorrMasks(cfgCorrConfig->GetpTCorrMasks()); - o2::analysis::gfw::regions.Print(); - o2::analysis::gfw::configs.Print(); - o2::analysis::gfw::configsV02.SetCorrs(cfgCorrConfigV02->GetCorrs()); - o2::analysis::gfw::configsV02.SetHeads(cfgCorrConfigV02->GetHeads()); - o2::analysis::gfw::configsV02.SetpTDifs(cfgCorrConfigV02->GetpTDifs()); - o2::analysis::gfw::configsV02.SetpTCorrMasks(cfgCorrConfigV02->GetpTCorrMasks()); - o2::analysis::gfw::configsV02.Print(); - o2::analysis::gfw::configsV0.SetCorrs(cfgCorrConfigV0->GetCorrs()); - o2::analysis::gfw::configsV0.SetHeads(cfgCorrConfigV0->GetHeads()); - o2::analysis::gfw::configsV0.SetpTDifs(cfgCorrConfigV0->GetpTDifs()); - o2::analysis::gfw::configsV0.SetpTCorrMasks(cfgCorrConfigV0->GetpTCorrMasks()); - o2::analysis::gfw::configsV0.Print(); - o2::analysis::gfw::ptbinning = cfgGFWBinning->GetPtBinning(); - o2::analysis::gfw::ptpoilow = cfgGFWBinning->GetPtPOImin(); - o2::analysis::gfw::ptpoiup = cfgGFWBinning->GetPtPOImax(); - o2::analysis::gfw::ptreflow = cfgGFWBinning->GetPtRefMin(); - o2::analysis::gfw::ptrefup = cfgGFWBinning->GetPtRefMax(); - o2::analysis::gfw::ptlow = cfgPtmin; - o2::analysis::gfw::ptup = cfgPtmax; - o2::analysis::gfw::etabins = cfgGFWBinning->GetEtaBins(); - o2::analysis::gfw::vtxZbins = cfgGFWBinning->GetVtxZbins(); - o2::analysis::gfw::phibins = cfgGFWBinning->GetPhiBins(); - o2::analysis::gfw::philow = 0.0f; - o2::analysis::gfw::phiup = o2::constants::math::TwoPI; - o2::analysis::gfw::nchbins = cfgGFWBinning->GetNchBins(); - o2::analysis::gfw::nchlow = cfgGFWBinning->GetNchMin(); - o2::analysis::gfw::nchup = cfgGFWBinning->GetNchMax(); - o2::analysis::gfw::centbinning = cfgGFWBinning->GetCentBinning(); + if (!cfgFill.cfgAnalyseChargedHadrons && !cfgFill.cfgAnalyseK0Lambda) { + LOGF(fatal, "Enable at least one radial-flow analysis channel"); + } + gfwMemberCache.regions.SetNames(cfgRegions->GetNames()); + gfwMemberCache.regions.SetEtaMin(cfgRegions->GetEtaMin()); + gfwMemberCache.regions.SetEtaMax(cfgRegions->GetEtaMax()); + gfwMemberCache.regions.SetpTDifs(cfgRegions->GetpTDifs()); + gfwMemberCache.regions.SetBitmasks(cfgRegions->GetBitmasks()); + gfwMemberCache.configs.SetCorrs(cfgCorrConfig->GetCorrs()); + gfwMemberCache.configs.SetHeads(cfgCorrConfig->GetHeads()); + gfwMemberCache.configs.SetpTDifs(cfgCorrConfig->GetpTDifs()); + gfwMemberCache.configs.SetpTCorrMasks(cfgCorrConfig->GetpTCorrMasks()); + gfwMemberCache.regions.Print(); + gfwMemberCache.configs.Print(); + gfwMemberCache.configsV02.SetCorrs(cfgCorrConfigV02->GetCorrs()); + gfwMemberCache.configsV02.SetHeads(cfgCorrConfigV02->GetHeads()); + gfwMemberCache.configsV02.SetpTDifs(cfgCorrConfigV02->GetpTDifs()); + gfwMemberCache.configsV02.SetpTCorrMasks(cfgCorrConfigV02->GetpTCorrMasks()); + gfwMemberCache.configsV02.Print(); + gfwMemberCache.configsV0.SetCorrs(cfgCorrConfigV0->GetCorrs()); + gfwMemberCache.configsV0.SetHeads(cfgCorrConfigV0->GetHeads()); + gfwMemberCache.configsV0.SetpTDifs(cfgCorrConfigV0->GetpTDifs()); + gfwMemberCache.configsV0.SetpTCorrMasks(cfgCorrConfigV0->GetpTCorrMasks()); + gfwMemberCache.configsV0.Print(); + gfwMemberCache.ptbinning = cfgGFWBinning->GetPtBinning(); + gfwMemberCache.ptpoilow = cfgGFWBinning->GetPtPOImin(); + gfwMemberCache.ptpoiup = cfgGFWBinning->GetPtPOImax(); + gfwMemberCache.ptreflow = cfgGFWBinning->GetPtRefMin(); + gfwMemberCache.ptrefup = cfgGFWBinning->GetPtRefMax(); + gfwMemberCache.ptlow = cfgKinematics.cfgPtMin; + gfwMemberCache.ptup = cfgKinematics.cfgPtmax; + gfwMemberCache.etabins = cfgGFWBinning->GetEtaBins(); + gfwMemberCache.vtxZbins = cfgGFWBinning->GetVtxZbins(); + gfwMemberCache.phibins = cfgGFWBinning->GetPhiBins(); + gfwMemberCache.philow = 0.0f; + gfwMemberCache.phiup = o2::constants::math::TwoPI; + gfwMemberCache.nchbins = cfgGFWBinning->GetNchBins(); + gfwMemberCache.nchlow = cfgGFWBinning->GetNchMin(); + gfwMemberCache.nchup = cfgGFWBinning->GetNchMax(); + gfwMemberCache.centbinning = cfgGFWBinning->GetCentBinning(); cfgGFWBinning->Print(); - LOGF(info, "Eta cuts: Filter [%.1f,%.1f] | Nch [%.1f,%.1f] | Pt-Pt [%.1f, %.1f] | V0 daughters [%.1f, %.1f]", cfgEta->first, cfgEta->second, cfgEtaNch->first, cfgEtaNch->second, cfgEtaPtPt->first, cfgEtaPtPt->second, cfgEtaV0Daughters->first, cfgEtaV0Daughters->second); - o2::analysis::gfw::multGlobalCorrCutPars = cfgMultCorrCuts.cfgMultGlobalCutPars; - o2::analysis::gfw::multPVCorrCutPars = cfgMultCorrCuts.cfgMultPVCutPars; - o2::analysis::gfw::multGlobalPVCorrCutPars = cfgMultCorrCuts.cfgMultGlobalPVCutPars; - o2::analysis::gfw::multGlobalV0ACutPars = cfgMultCorrCuts.cfgMultGlobalV0ACutPars; - o2::analysis::gfw::multGlobalT0ACutPars = cfgMultCorrCuts.cfgMultGlobalT0ACutPars; - - projectMatrix(cfgPIDCuts.nSigmas->getData(), tpcNsigmaCut, tofNsigmaCut, itsNsigmaCut); - readMatrix(cfgPIDCuts.resonanceCuts->getData(), resoCutVals); - readMatrix(cfgPIDCuts.resonanceSwitches->getData(), resoSwitchVals); + LOGF(info, "Eta cuts: Filter [%.1f,%.1f] | Nch [%.1f,%.1f] | Npt v02 [%.1f, %.1f] | Npt v0 [%.1f, %.1f] | Pt-Pt [%.1f, %.1f]", cfgKinematics.cfgEta->first, cfgKinematics.cfgEta->second, cfgKinematics.cfgEtaNch->first, cfgKinematics.cfgEtaNch->second, cfgKinematics.cfgEtaNptV02->first, cfgKinematics.cfgEtaNptV02->second, cfgKinematics.cfgEtaNptV0->first, cfgKinematics.cfgEtaNptV0->second, cfgKinematics.cfgEtaPtPt->first, cfgKinematics.cfgEtaPtPt->second); + gfwMemberCache.multGlobalCorrCutPars = cfgMultCorrCuts.cfgMultGlobalCutPars; + gfwMemberCache.multPVCorrCutPars = cfgMultCorrCuts.cfgMultPVCutPars; + gfwMemberCache.multGlobalPVCorrCutPars = cfgMultCorrCuts.cfgMultGlobalPVCutPars; + gfwMemberCache.multGlobalV0ACutPars = cfgMultCorrCuts.cfgMultGlobalV0ACutPars; + gfwMemberCache.multGlobalT0ACutPars = cfgMultCorrCuts.cfgMultGlobalT0ACutPars; + + projectMatrix(nSigmas->getData(), tpcNsigmaCut, tofNsigmaCut, itsNsigmaCut); + readMatrix(resonanceCuts->getData(), resoCutVals); + readMatrix(resonanceSwitches->getData(), resoSwitchVals); printResoCuts(); - for (int i = 0; i < 4; ++i) { // o2-linter: disable=magic-number (maximum of 4 subevents) - if (cfgPtPtGaps->getData()[i][0] < -1. || cfgPtPtGaps->getData()[i][1] < -1.) + const auto& ptPtGaps = cfgPtPtGaps->getData(); + + for (uint32_t i = 0; i < ptPtGaps.rows; ++i) { + const auto etaMin = ptPtGaps(i, 0); + const auto etaMax = ptPtGaps(i, 1); + + if (etaMin < -1. || etaMax < -1.) { continue; - o2::analysis::gfw::etagapsPtPt.push_back(std::make_pair(cfgPtPtGaps->getData()[i][0], cfgPtPtGaps->getData()[i][1])); + } + + gfwMemberCache.etagapsPtPt.emplace_back(etaMin, etaMax); } - for (const auto& [etamin, etamax] : o2::analysis::gfw::etagapsPtPt) { + for (const auto& [etamin, etamax] : gfwMemberCache.etagapsPtPt) { LOGF(info, "pt-pt subevent: {%.1f,%.1f}", etamin, etamax); } // Setup event cuts - eventcutflags.push_back({cfgEventCutFlags.cfgNoSameBunchPileupCut, kNoSameBunchPileup, o2::aod::evsel::kNoSameBunchPileup}); - eventcutflags.push_back({cfgEventCutFlags.cfgIsGoodZvtxFT0vsPV, kIsGoodZvtxFT0vsPV, o2::aod::evsel::kIsGoodZvtxFT0vsPV}); - eventcutflags.push_back({cfgEventCutFlags.cfgNoCollInTimeRangeStandard, kNoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInTimeRangeStandard}); - eventcutflags.push_back({cfgEventCutFlags.cfgNoCollInRofStandard, kNoCollInRofStandard, o2::aod::evsel::kNoCollInRofStandard}); - eventcutflags.push_back({cfgEventCutFlags.cfgNoHighMultCollInPrevRof, kNoHighMultCollInPrevRof, o2::aod::evsel::kNoHighMultCollInPrevRof}); - eventcutflags.push_back({cfgEventCutFlags.cfgNoTimeFrameBorder, kNoTimeFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}); - eventcutflags.push_back({cfgEventCutFlags.cfgNoITSROFrameBorder, kNoITSROFrameBorder, o2::aod::evsel::kNoITSROFrameBorder}); - eventcutflags.push_back({cfgEventCutFlags.cfgIsVertexITSTPC, kIsVertexITSTPC, o2::aod::evsel::kIsVertexITSTPC}); - eventcutflags.push_back({cfgEventCutFlags.cfgIsGoodITSLayersAll, kIsGoodITSLayersAll, o2::aod::evsel::kIsGoodITSLayersAll}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoSameBunchPileupCut, NoSameBunchPileup, o2::aod::evsel::kNoSameBunchPileup}); + eventcutflags.push_back({cfgEventCutFlags.cfgIsGoodZvtxFT0vsPV, IsGoodZvtxFT0vsPV, o2::aod::evsel::kIsGoodZvtxFT0vsPV}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoCollInTimeRangeStandard, NoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInTimeRangeStandard}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoCollInRofStandard, NoCollInRofStandard, o2::aod::evsel::kNoCollInRofStandard}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoHighMultCollInPrevRof, NoHighMultCollInPrevRof, o2::aod::evsel::kNoHighMultCollInPrevRof}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoTimeFrameBorder, NoTimeFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoITSROFrameBorder, NoITSROFrameBorder, o2::aod::evsel::kNoITSROFrameBorder}); + eventcutflags.push_back({cfgEventCutFlags.cfgIsVertexITSTPC, IsVertexITSTPC, o2::aod::evsel::kIsVertexITSTPC}); + eventcutflags.push_back({cfgEventCutFlags.cfgIsGoodITSLayersAll, IsGoodITSLayersAll, o2::aod::evsel::kIsGoodITSLayersAll}); for (const auto& cut : eventcutflags) { LOGF(info, "Flag %d is %senabled", cut.histBin, (cut.enabled) ? "" : "not "); } - AxisSpec phiAxis = {o2::analysis::gfw::phibins, o2::analysis::gfw::philow, o2::analysis::gfw::phiup, "#phi"}; + AxisSpec phiAxis = {gfwMemberCache.phibins, gfwMemberCache.philow, gfwMemberCache.phiup, "#phi"}; AxisSpec phiModAxis = {100, 0, constants::math::PI / 9, "fmod(#varphi,#pi/9)"}; - AxisSpec etaAxis = {o2::analysis::gfw::etabins, cfgEta->first, cfgEta->second, "#eta"}; - AxisSpec vtxAxis = {o2::analysis::gfw::vtxZbins, -cfgVtxZ, cfgVtxZ, "Vtx_{z} (cm)"}; - AxisSpec ptAxis = {o2::analysis::gfw::ptbinning, "#it{p}_{T} GeV/#it{c}"}; + AxisSpec etaAxis = {gfwMemberCache.etabins, cfgKinematics.cfgEta->first, cfgKinematics.cfgEta->second, "#eta"}; + AxisSpec vtxAxis = {gfwMemberCache.vtxZbins, -cfgVtxZ, cfgVtxZ, "Vtx_{z} (cm)"}; + AxisSpec ptAxis = {gfwMemberCache.ptbinning, "#it{p}_{T} GeV/#it{c}"}; std::string sCentralityEstimator = centNamesMap[cfgCentEstimator] + " centrality (%)"; - AxisSpec centAxis = {o2::analysis::gfw::centbinning, sCentralityEstimator.c_str()}; + AxisSpec centAxis = {gfwMemberCache.centbinning, sCentralityEstimator.c_str()}; std::vector nchbinning; - int nchskip = (o2::analysis::gfw::nchup - o2::analysis::gfw::nchlow) / o2::analysis::gfw::nchbins; - for (int i = 0; i <= o2::analysis::gfw::nchbins; ++i) { - nchbinning.push_back(nchskip * i + o2::analysis::gfw::nchlow + 0.5); + const double nchskip = (gfwMemberCache.nchup - gfwMemberCache.nchlow) / gfwMemberCache.nchbins; + for (int i = 0; i <= gfwMemberCache.nchbins; ++i) { + nchbinning.push_back(nchskip * i + gfwMemberCache.nchlow + 0.5); } AxisSpec nchAxis = {nchbinning, "N_{ch}"}; AxisSpec bAxis = {200, 0, 20, "#it{b}"}; @@ -517,11 +644,19 @@ struct FlowGenericFramework { AxisSpec v0aAxis = {1800, 0, 180000, "N_{ch} (V0A)"}; AxisSpec multpvAxis = {3500, 0, 3500, "N_{ch} (PV)"}; AxisSpec occAxis = {500, 0, 5000, "occupancy"}; - AxisSpec multAxis = (doprocessOnTheFly && !cfgUseNch) ? bAxis : (cfgUseNch) ? nchAxis - : centAxis; + AxisSpec multAxis = (doprocessOnTheFly) ? bAxis : centAxis; + if (cfgEventSelection.cfgUseNch) { + multAxis = nchAxis; + } AxisSpec dcaZAXis = {200, -2, 2, "DCA_{z} (cm)"}; AxisSpec dcaXYAXis = {200, -1, 1, "DCA_{xy} (cm)"}; AxisSpec singleCount = {1, 0, 1}; + AxisSpec efficiencyParticleAxis = {EfficiencySpeciesCount, -0.5, static_cast(EfficiencySpeciesCount) - 0.5, "particle"}; + AxisSpec efficiencyStepAxis = {EfficiencyStepCount, -0.5, static_cast(EfficiencyStepCount) - 0.5, "generated/reconstructed"}; + AxisSpec v0EfficiencyStageAxis = {V0EfficiencyStageCount, -0.5, static_cast(V0EfficiencyStageCount) - 0.5, "stage"}; + AxisSpec v0EfficiencyDebugEtaAxis = {160, -4.f, 4.f, "#eta"}; + AxisSpec v0EfficiencyDaughterPdgAxis = {V0EfficiencyDaughterPdgCategoryCount, -0.5, static_cast(V0EfficiencyDaughterPdgCategoryCount) - 0.5, "daughter PDG category"}; + AxisSpec lambdaFeeddownSpeciesAxis = {LambdaFeeddownSpeciesCount, -0.5, static_cast(LambdaFeeddownSpeciesCount) - 0.5, "species"}; ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -529,248 +664,427 @@ struct FlowGenericFramework { int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); - int ptbins = o2::analysis::gfw::ptbinning.size() - 1; - fPtAxis = new TAxis(ptbins, &o2::analysis::gfw::ptbinning[0]); - - if (doprocessMCGen || doprocessOnTheFly) { - registry.add("MCGen/before/pt_gen", "", {HistType::kTH1D, {ptAxis}}); - registry.add("MCGen/before/phi_eta_vtxZ_gen", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - registry.addClone("MCGen/before/", "MCGen/after/"); - if (doprocessOnTheFly) - registry.add("MCGen/impactParameter", "", {HistType::kTH2D, {{bAxis, nchAxis}}}); - } - if (doprocessMCReco || doprocessData || doprocessRun2) { - registry.add("trackQA/before/phi_eta_vtxZ", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - registry.add("trackQA/before/pt_dcaXY_dcaZ", "", {HistType::kTH3D, {ptAxis, dcaXYAXis, dcaZAXis}}); - registry.add("trackQA/before/pt_phi", "", {HistType::kTH2D, {ptAxis, phiModAxis}}); - registry.add("trackQA/before/chi2prTPCcls", "#chi^{2}/cluster for the TPC track segment", {HistType::kTH1D, {{100, 0., 5.}}}); - registry.add("trackQA/before/chi2prITScls", "#chi^{2}/cluster for the ITS track", {HistType::kTH1D, {{100, 0., 50.}}}); - registry.add("trackQA/before/nTPCClusters", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); - registry.add("trackQA/before/nITSClusters", "Number of found ITS clusters", {HistType::kTH1D, {{100, 0, 20}}}); - registry.add("trackQA/before/nTPCCrossedRows", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); - - registry.addClone("trackQA/before/", "trackQA/after/"); - registry.add("trackQA/after/pt_ref", "; #it{p}_{T}; Counts", {HistType::kTH1D, {{100, o2::analysis::gfw::ptreflow, o2::analysis::gfw::ptrefup}}}); - registry.add("trackQA/after/pt_poi", "; #it{p}_{T}; Counts", {HistType::kTH1D, {{100, o2::analysis::gfw::ptpoilow, o2::analysis::gfw::ptpoiup}}}); - registry.add("trackQA/after/Nch_corrected", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); - registry.add("trackQA/after/Nch_uncorrected", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); - registry.add("trackQA/after/etaNch", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); - registry.add("trackQA/after/etaPtPt", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); - registry.add("trackQA/after/etaV0Daughters", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); - - LOGF(info, "Using alternative filling for pt fractions: %d", static_cast(cfgUseNewNpt)); - histosNpt.reserve(SPECIESCOUNT); - histosNpt[CHARGEDID] = registry.add("nptCh", "; #it{p}_{T} (GeV/#it{c}; Count)", {HistType::kTH1D, {ptAxis}}); - histosNpt[PIONID] = registry.add("nptPi", "; #it{p}_{T} (GeV/#it{c}; Count)", {HistType::kTH1D, {ptAxis}}); - histosNpt[KAONID] = registry.add("nptKa", "; #it{p}_{T} (GeV/#it{c}; Count)", {HistType::kTH1D, {ptAxis}}); - histosNpt[PROTONID] = registry.add("nptPr", "; #it{p}_{T} (GeV/#it{c}; Count)", {HistType::kTH1D, {ptAxis}}); - histosResoNpt.reserve(RESOCOUNT); - histosResoNpt[K0SIDEBAND1] = registry.add("nptK0SB1", "; #it{p}_{T} (GeV/#it{c}; Count", {HistType::kTH1D, {ptAxis}}); - histosResoNpt[K0SIGNAL] = registry.add("nptK0Sig", "; #it{p}_{T} (GeV/#it{c}; Count", {HistType::kTH1D, {ptAxis}}); - histosResoNpt[K0SIDEBAND2] = registry.add("nptK0SB2", "; #it{p}_{T} (GeV/#it{c}; Count", {HistType::kTH1D, {ptAxis}}); - histosResoNpt[LAMBDASIDEBAND1] = registry.add("nptLambdaSB1", "; #it{p}_{T} (GeV/#it{c}; Count", {HistType::kTH1D, {ptAxis}}); - histosResoNpt[LAMBDASIGNAL] = registry.add("nptLambdaSig", "; #it{p}_{T} (GeV/#it{c}; Count", {HistType::kTH1D, {ptAxis}}); - histosResoNpt[LAMBDASIDEBAND2] = registry.add("nptLambdaSB2", "; #it{p}_{T} (GeV/#it{c}; Count", {HistType::kTH1D, {ptAxis}}); - - registry.add("eventQA/before/centrality", "; centrality (%); Counts", {HistType::kTH1D, {centAxis}}); - registry.add("eventQA/before/multiplicity", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); - registry.add("eventQA/before/globalTracks_centT0C", "; FT0C centrality (%); N_{global}", {HistType::kTH2D, {centAxis, nchAxis}}); - registry.add("eventQA/before/PVTracks_centT0C", "; FT0C centrality (%); N_{PV}", {HistType::kTH2D, {centAxis, multpvAxis}}); - registry.add("eventQA/before/globalTracks_PVTracks", "; N_{PV}; N_{global}", {HistType::kTH2D, {multpvAxis, nchAxis}}); - registry.add("eventQA/before/globalTracks_multT0A", "; multT0A; N_{global}", {HistType::kTH2D, {t0aAxis, nchAxis}}); - registry.add("eventQA/before/globalTracks_multV0A", "; multV0A; N_{global}", {HistType::kTH2D, {v0aAxis, nchAxis}}); - registry.add("eventQA/before/multV0A_multT0A", "; multV0A; multT0A", {HistType::kTH2D, {t0aAxis, v0aAxis}}); - registry.add("eventQA/before/multT0C_centT0C", "; multT0C; FT0C centrality (%)", {HistType::kTH2D, {centAxis, t0cAxis}}); - registry.add("eventQA/before/occ_mult_cent", "; occupancy; N_{ch}; centrality (%)", {HistType::kTH3D, {occAxis, nchAxis, centAxis}}); - registry.addClone("eventQA/before/", "eventQA/after/"); - registry.add("eventQA/eventSel", "Number of Events;; Counts", {HistType::kTH1D, {{15, 0.5, 15.5}}}); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kFilteredEvent, "Filtered event"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kSel8, "sel8"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kOccupancy, "occupancy"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kTVXinTRD, "kTVXinTRD"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoSameBunchPileup, "kNoSameBunchPileup"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kIsGoodZvtxFT0vsPV, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoCollInTimeRangeStandard, "kNoCollInTimeRangeStandard"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoCollInRofStandard, "kNoCollInRofStandard"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoHighMultCollInPrevRof, "kNoHighMultCollInPrevRof"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoTimeFrameBorder, "kNoTimeFrameBorder"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kNoITSROFrameBorder, "kNoITSROFrameBorder"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kIsVertexITSTPC, "kIsVertexITSTPC"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kIsGoodITSLayersAll, "kIsGoodITSLayersAll"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kMultCuts, "after Mult cuts"); - registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kTrackCent, "has track + within cent"); - - registry.add("npt_ch", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_pi", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_ka", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_pr", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_K0_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_K0_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_K0_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_Lambda_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_Lambda_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("npt_Lambda_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); - - if (!cfgRunByRun) { - if (cfgUsePID) { - registry.add("phi_eta_vtxz_ref", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - registry.add("phi_eta_vtxz_ch", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - registry.add("phi_eta_vtxz_pi", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - registry.add("phi_eta_vtxz_ka", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - registry.add("phi_eta_vtxz_pr", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - } else { - registry.add("phi_eta_vtxz_ref", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + const int ptbins = static_cast(gfwMemberCache.ptbinning.size() - 1); + fPtAxis = new TAxis(ptbins, gfwMemberCache.ptbinning.data()); + + if (doprocessMCGen || doprocessOnTheFly || doprocessMC) { + if (cfgFill.cfgFillQA) { + registryQA.add("MCGen/before/pt_gen", "", {HistType::kTH1D, {ptAxis}}); + registryQA.add("MCGen/before/phi_eta_vtxZ_gen", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registryQA.addClone("MCGen/before/", "MCGen/after/"); + if (doprocessOnTheFly) { + registryQA.add("MCGen/impactParameter", "", {HistType::kTH2D, {{bAxis, nchAxis}}}); + } + } + } + if (doprocessEfficiency) { + registry.add("Efficiency/efficiencyHist", "; #it{p}_{T}^{MC}; Centrality (%)", {HistType::kTHnSparseF, {{ptAxis, centAxis, efficiencyParticleAxis, efficiencyStepAxis}}}); + registry.add("Efficiency/v0RecoDebug", "; #it{p}_{T}; Centrality (%)", {HistType::kTHnSparseF, {{ptAxis, centAxis, efficiencyParticleAxis, v0EfficiencyStageAxis}}}); + registry.add("Efficiency/v0RecoDaughterEtaDebug", "; Centrality (%); particle; #eta^{MC}_{pos}; #eta^{MC}_{neg}; #eta^{reco}_{pos}; #eta^{reco}_{neg}", {HistType::kTHnSparseF, {{centAxis, efficiencyParticleAxis, v0EfficiencyDebugEtaAxis, v0EfficiencyDebugEtaAxis, v0EfficiencyDebugEtaAxis, v0EfficiencyDebugEtaAxis}}}); + registry.add("Efficiency/v0RecoDaughterPdgDebug", "; Centrality (%); particle; PDG^{MC}_{pos}; PDG^{MC}_{neg}", {HistType::kTHnSparseF, {{centAxis, efficiencyParticleAxis, v0EfficiencyDaughterPdgAxis, v0EfficiencyDaughterPdgAxis}}}); + registry.add("Efficiency/lambdaFeeddownRecoXi", "; #it{p}_{T}^{#Lambda, reco}; #it{p}_{T}^{#Xi, gen}; Centrality (%)", {HistType::kTHnSparseF, {{ptAxis, axisFeeddownXiPt, centAxis, lambdaFeeddownSpeciesAxis}}}); + registry.add("Efficiency/lambdaFeeddownGeneratedXi", "; #it{p}_{T}^{#Xi, gen}; Centrality (%)", {HistType::kTHnSparseF, {{axisFeeddownXiPt, centAxis, lambdaFeeddownSpeciesAxis}}}); + } + if (doprocessMCReco || doprocessData || doprocessRun2 || doprocessEfficiency || doprocessMC) { + if (cfgFill.cfgFillQA) { + registryQA.add("trackQA/before/phi_eta_vtxZ", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registryQA.add("trackQA/before/pt_dcaXY_dcaZ", "", {HistType::kTH3D, {ptAxis, dcaXYAXis, dcaZAXis}}); + registryQA.add("trackQA/before/pt_phi", "", {HistType::kTH2D, {ptAxis, phiModAxis}}); + registryQA.add("trackQA/before/chi2prTPCcls", "#chi^{2}/cluster for the TPC track segment", {HistType::kTH1D, {{100, 0., 5.}}}); + registryQA.add("trackQA/before/chi2prITScls", "#chi^{2}/cluster for the ITS track", {HistType::kTH1D, {{100, 0., 50.}}}); + registryQA.add("trackQA/before/nTPCClusters", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); + registryQA.add("trackQA/before/nITSClusters", "Number of found ITS clusters", {HistType::kTH1D, {{100, 0, 20}}}); + registryQA.add("trackQA/before/nTPCCrossedRows", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); + + registryQA.addClone("trackQA/before/", "trackQA/after/"); + registryQA.add("trackQA/after/pt_ref", "; #it{p}_{T}; Counts", {HistType::kTH1D, {{100, gfwMemberCache.ptreflow, gfwMemberCache.ptrefup}}}); + registryQA.add("trackQA/after/pt_poi", "; #it{p}_{T}; Counts", {HistType::kTH1D, {{100, gfwMemberCache.ptpoilow, gfwMemberCache.ptpoiup}}}); + registryQA.add("trackQA/after/Nch_corrected", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); + registryQA.add("trackQA/after/Nch_uncorrected", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); + registryQA.add("trackQA/after/etaNch", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); + registryQA.add("trackQA/after/etaPtPt", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); + if (!cfgFill.cfgFillRunByRunQA) { + if (cfgFill.cfgUsePID) { + registryQA.add("phi_eta_vtxz_ref", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registryQA.add("phi_eta_vtxz_ch", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registryQA.add("phi_eta_vtxz_pi", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registryQA.add("phi_eta_vtxz_ka", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registryQA.add("phi_eta_vtxz_pr", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + } else { + registryQA.add("phi_eta_vtxz_ref", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + } + } + registryQA.add("eventQA/before/globalTracks_centT0C", "; FT0C centrality (%); N_{global}", {HistType::kTH2D, {centAxis, nchAxis}}); + registryQA.add("eventQA/before/PVTracks_centT0C", "; FT0C centrality (%); N_{PV}", {HistType::kTH2D, {centAxis, multpvAxis}}); + registryQA.add("eventQA/before/globalTracks_PVTracks", "; N_{PV}; N_{global}", {HistType::kTH2D, {multpvAxis, nchAxis}}); + registryQA.add("eventQA/before/globalTracks_multT0A", "; multT0A; N_{global}", {HistType::kTH2D, {t0aAxis, nchAxis}}); + registryQA.add("eventQA/before/globalTracks_multV0A", "; multV0A; N_{global}", {HistType::kTH2D, {v0aAxis, nchAxis}}); + registryQA.add("eventQA/before/multV0A_multT0A", "; multV0A; multT0A", {HistType::kTH2D, {t0aAxis, v0aAxis}}); + registryQA.add("eventQA/before/multT0C_centT0C", "; multT0C; FT0C centrality (%)", {HistType::kTH2D, {centAxis, t0cAxis}}); + registryQA.add("eventQA/before/occ_mult_cent", "; occupancy; N_{ch}; centrality (%)", {HistType::kTH3D, {occAxis, nchAxis, centAxis}}); + } + registryQA.add("eventQA/before/centrality", "; centrality (%); Counts", {HistType::kTH1D, {centAxis}}); + registryQA.add("eventQA/before/multiplicity", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); + registryQA.addClone("eventQA/before/", "eventQA/after/"); + registryQA.add("eventQA/eventSel", "Number of Events;; Counts", {HistType::kTH1D, {{15, 0.5, 15.5}}}); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(FilteredEvent, "Filtered event"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(Sel8, "sel8"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(Occupancy, "occupancy"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(TVXinTRD, "TVXinTRD"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoSameBunchPileup, "NoSameBunchPileup"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV, "IsGoodZvtxFT0vsPV"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard, "NoCollInTimeRangeStandard"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoCollInRofStandard, "NoCollInRofStandard"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoHighMultCollInPrevRof, "NoHighMultCollInPrevRof"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoTimeFrameBorder, "NoTimeFrameBorder"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoITSROFrameBorder, "NoITSROFrameBorder"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(IsVertexITSTPC, "IsVertexITSTPC"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(IsGoodITSLayersAll, "IsGoodITSLayersAll"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(MultCuts, "after Mult cuts"); + registryQA.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(TrackCent, "has track + within cent"); + + AxisSpec axisK0Mass = {resoSwitchVals[MassBins][K0], resoCutVals[MassMin][K0], resoCutVals[MassMax][K0]}; + AxisSpec axisLambdaMass = {resoSwitchVals[MassBins][Lambda], resoCutVals[MassMin][Lambda], resoCutVals[MassMax][Lambda]}; + AxisSpec yAxis = {100, -1, 1}; + // QA histograms for V0s + if (cfgFill.cfgAnalyseK0Lambda && cfgFill.cfgFillV0QA && (resoSwitchVals[UseParticle][K0] != 0 || resoSwitchVals[UseParticle][Lambda] != 0)) { + registryQA.add("trackQA/after/etaV02", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); + registryQA.add("trackQA/after/etaV0", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); + } + if (cfgFill.cfgAnalyseK0Lambda && resoSwitchVals[UseParticle][K0] != 0) { + if (cfgFill.cfgFillV0QA) { + registryQA.add("K0/PiPlusTPC_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); + registryQA.add("K0/PiMinusTPC_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); + registryQA.add("K0/PiPlusTOF_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); + registryQA.add("K0/PiMinusTOF_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); + registryQA.add("K0/hK0Phi", "", {HistType::kTH1D, {phiAxis}}); + registryQA.add("K0/hK0EtaY", "", {HistType::kTH2D, {etaAxis, yAxis}}); + registryQA.add("K0/hK0AP", "", {HistType::kTH2D, {{100, -1, 1}, {250, 0, 0.5}}}); + registryQA.add("K0/hK0Mass_sparse", "", {HistType::kTHnSparseF, {{axisK0Mass, ptAxis, nchAxis}}}); + registryQA.add("K0/hK0s", "", {HistType::kTH1D, {singleCount}}); + registryQA.add("K0/hK0s_corrected", "", {HistType::kTH1D, {singleCount}}); } + registryQA.add("K0/hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{14, 0.5, 14.5}}}); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate, "K0 candidates"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt, "Daughter pt"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut, "Mass cut"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillRapidityCut, "Rapidity cut"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillDCAtoPV, "DCA to PV"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillDCAxDaughters, "DCA between daughters"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillV0Radius, "V0radius"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillCosPA, "CosPA"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillProperLifetime, "Proper lifetime"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillArmPodCut, "Armenteros-Podolanski cut"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillCompetingMass, "Competing mass rejection"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected, "Daughter track selected"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillV02DaughterTrackSelection, "v02 Daughter eta selection"); + registryQA.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(FillV0DaughterTrackSelection, "v0 Daughter eta selection"); } - AxisSpec axisK0Mass = {resoSwitchVals[K0][kMassBins], resoCutVals[K0][kMassMin], resoCutVals[K0][kMassMax]}; - AxisSpec axisLambdaMass = {resoSwitchVals[LAMBDA][kMassBins], resoCutVals[LAMBDA][kMassMin], resoCutVals[LAMBDA][kMassMax]}; + if (cfgFill.cfgAnalyseK0Lambda && resoSwitchVals[UseParticle][Lambda] != 0) { + if (cfgFill.cfgFillV0QA) { + registryQA.add("Lambda/PrPlusTPC_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); + registryQA.add("Lambda/PiMinusTPC_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); + registryQA.add("Lambda/PrPlusTOF_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); + registryQA.add("Lambda/PiMinusTOF_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); + registryQA.add("Lambda/hLambdaPhi", "", {HistType::kTH1D, {phiAxis}}); + registryQA.add("Lambda/hLambdaEtaY", "", {HistType::kTH2D, {etaAxis, yAxis}}); + registryQA.add("Lambda/hLambdaAP", "", {HistType::kTH2D, {{100, -1, 1}, {250, 0, 0.5}}}); + registryQA.add("Lambda/hLambdaMass_sparse", "", {HistType::kTHnSparseF, {{axisLambdaMass, ptAxis, nchAxis}}}); + registryQA.add("Lambda/PiPlusTPC_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); + registryQA.add("Lambda/PrMinusTPC_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); + registryQA.add("Lambda/PiPlusTOF_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); + registryQA.add("Lambda/PrMinusTOF_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); + registryQA.add("Lambda/hAntiLambdaPhi", "", {HistType::kTH1D, {phiAxis}}); + registryQA.add("Lambda/hAntiLambdaEtaY", "", {HistType::kTH2D, {etaAxis, yAxis}}); + registryQA.add("Lambda/hAntiLambdaAP", "", {HistType::kTH2D, {{100, -1, 1}, {250, 0, 0.5}}}); + registryQA.add("Lambda/hAntiLambdaMass_sparse", "", {HistType::kTHnSparseF, {{axisLambdaMass, ptAxis, nchAxis}}}); + registryQA.add("Lambda/hLambdas", "", {HistType::kTH1D, {singleCount}}); + registryQA.add("Lambda/hLambdas_corrected", "", {HistType::kTH1D, {singleCount}}); + } + registryQA.add("Lambda/hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{14, 0.5, 14.5}}}); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate, "Lambda candidates"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt, "Daughter pt"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut, "Mass cut"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillRapidityCut, "Rapidity cut"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAtoPV, "DCA to PV"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAxDaughters, "DCA between daughters"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0Radius, "V0radius"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillCosPA, "CosPA"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillProperLifetime, "Proper lifetime"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillArmPodCut, "Armenteros-Podolanski cut"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillCompetingMass, "Competing mass rejection"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected, "Daughter track selected"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillV02DaughterTrackSelection, "v02 Daughter track selection"); + registryQA.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0DaughterTrackSelection, "v0 Daughter track selection"); + } + } + if (!doprocessEfficiency) { + if (doprocessData || doprocessRun2 || doprocessMCReco || doprocessMC) { + if (cfgFill.cfgAnalyseChargedHadrons) { + registry.add("npt_v02_ch", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_pi", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_ka", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_pr", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_ch", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_pi", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_ka", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_pr", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + } + if (cfgFill.cfgAnalyseK0Lambda) { + registry.add("npt_v02_K0_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_K0_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_K0_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_Lambda_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_Lambda_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v02_Lambda_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_K0_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_K0_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_K0_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_Lambda_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_Lambda_sb1", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.add("npt_v0_Lambda_sb2", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + } + } + if (doprocessMCGen || doprocessOnTheFly || doprocessMC) { + if (cfgFill.cfgAnalyseChargedHadrons) { + registryGen.add("MCGen/npt_v02_ch", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v02_pi", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v02_ka", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v02_pr", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v0_ch", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v0_pi", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v0_ka", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v0_pr", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + } + if (cfgFill.cfgAnalyseK0Lambda) { + registryGen.add("MCGen/npt_v02_K0_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v02_Lambda_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v0_K0_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registryGen.add("MCGen/npt_v0_Lambda_sig", "; #it{p}_{T} (GeV/#it{c}; ; centrality (%); fraction)", {HistType::kTProfile2D, {ptAxis, centAxis}}); + } + } + if (cfgMethod.cfgStoreCombinedFractionWeights) { + for (const auto& name : {"npt_v02_ch", "npt_v02_pi", "npt_v02_ka", "npt_v02_pr", + "npt_v0_ch", "npt_v0_pi", "npt_v0_ka", "npt_v0_pr", + "npt_v02_K0_sig", "npt_v02_K0_sb1", "npt_v02_K0_sb2", + "npt_v02_Lambda_sig", "npt_v02_Lambda_sb1", "npt_v02_Lambda_sb2", + "npt_v0_K0_sig", "npt_v0_K0_sb1", "npt_v0_K0_sb2", + "npt_v0_Lambda_sig", "npt_v0_Lambda_sb1", "npt_v0_Lambda_sb2"}) { + const std::string source{name}; + const bool resonance = source.find("_K0") != std::string::npos || source.find("_Lambda") != std::string::npos; + if (resonance ? !cfgFill.cfgAnalyseK0Lambda : !cfgFill.cfgAnalyseChargedHadrons) { + continue; + } + const std::string target = source + (source.starts_with("npt_v02_") ? "_w3pc" : "_w2pc"); + if (doprocessData || doprocessRun2 || doprocessMCReco || doprocessMC) { + registry.addClone(source, target); + } + if ((doprocessMCGen || doprocessOnTheFly || doprocessMC) && source.find("_sb") == std::string::npos) { + registryGen.addClone("MCGen/" + source, "MCGen/" + target); + } + } + } + } + for (auto setup = 0; setup < FractionSetupCount; ++setup) { + const char* setupName = (setup == FractionV02) ? "V02" : "V0"; + histosNpt[setup].resize(SpeciesCount); + histosNpt[setup][ChargedID] = std::make_unique(Form("npt%sCh", setupName), "; #it{p}_{T} (GeV/#it{c}; Count)", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosNpt[setup][PionID] = std::make_unique(Form("npt%sPi", setupName), "; #it{p}_{T} (GeV/#it{c}; Count)", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosNpt[setup][KaonID] = std::make_unique(Form("npt%sKa", setupName), "; #it{p}_{T} (GeV/#it{c}; Count)", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosNpt[setup][ProtonID] = std::make_unique(Form("npt%sPr", setupName), "; #it{p}_{T} (GeV/#it{c}; Count)", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + + if (cfgFill.cfgAnalyseK0Lambda) { + histosResoNpt[setup].resize(ResonanceCount); + histosResoNpt[setup][K0Sideband1] = std::make_unique(Form("npt%sK0SB1", setupName), "; #it{p}_{T} (GeV/#it{c}; Count", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosResoNpt[setup][K0Signal] = std::make_unique(Form("npt%sK0Sig", setupName), "; #it{p}_{T} (GeV/#it{c}; Count", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosResoNpt[setup][K0Sideband2] = std::make_unique(Form("npt%sK0SB2", setupName), "; #it{p}_{T} (GeV/#it{c}; Count", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosResoNpt[setup][LambdaSideband1] = std::make_unique(Form("npt%sLambdaSB1", setupName), "; #it{p}_{T} (GeV/#it{c}; Count", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosResoNpt[setup][LambdaSignal] = std::make_unique(Form("npt%sLambdaSig", setupName), "; #it{p}_{T} (GeV/#it{c}; Count", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + histosResoNpt[setup][LambdaSideband2] = std::make_unique(Form("npt%sLambdaSB2", setupName), "; #it{p}_{T} (GeV/#it{c}; Count", ptAxis.binEdges.size() - 1, ptAxis.binEdges.data()); + } + } - // QA histograms for V0s - if (resoSwitchVals[K0][kUseParticle]) { - registry.add("K0/PiPlusTPC_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); - registry.add("K0/PiMinusTPC_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); - registry.add("K0/PiPlusTOF_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); - registry.add("K0/PiMinusTOF_K0", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); - registry.add("K0/hK0Phi", "", {HistType::kTH1D, {phiAxis}}); - registry.add("K0/hK0Eta", "", {HistType::kTH1D, {etaAxis}}); - registry.add("K0/hK0Mass_sparse", "", {HistType::kTHnSparseF, {{axisK0Mass, ptAxis, nchAxis}}}); - registry.add("K0/hK0s", "", {HistType::kTH1D, {singleCount}}); - registry.add("K0/hK0s_corrected", "", {HistType::kTH1D, {singleCount}}); - - registry.add("K0/hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{10, 0.5, 10.5}}}); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillCandidate, "K0 candidates"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillDaughterPt, "Daughter pt"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillMassCut, "Mass cut"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillRapidityCut, "Rapidity cut"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillDCAtoPV, "DCA to PV"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillDCAxDaughters, "DCA between daughters"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillV0Radius, "V0radius"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillCosPA, "CosPA"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillProperLifetime, "Proper lifetime"); - registry.get(HIST("K0/hK0Count"))->GetXaxis()->SetBinLabel(kFillDaughterTrackSelection, "Daughter track selection"); - } - - if (resoSwitchVals[LAMBDA][kUseParticle]) { - registry.add("Lambda/PrPlusTPC_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); - registry.add("Lambda/PiMinusTPC_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); - registry.add("Lambda/PrPlusTOF_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); - registry.add("Lambda/PiMinusTOF_L", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); - registry.add("Lambda/hLambdaPhi", "", {HistType::kTH1D, {phiAxis}}); - registry.add("Lambda/hLambdaEta", "", {HistType::kTH1D, {etaAxis}}); - registry.add("Lambda/hLambdaMass_sparse", "", {HistType::kTHnSparseF, {{axisLambdaMass, ptAxis, nchAxis}}}); - registry.add("Lambda/PiPlusTPC_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); - registry.add("Lambda/PrMinusTPC_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTPC}}}); - registry.add("Lambda/PiPlusTOF_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); - registry.add("Lambda/PrMinusTOF_AL", "", {HistType::kTH2D, {{ptAxis, axisNsigmaTOF}}}); - registry.add("Lambda/hAntiLambdaPhi", "", {HistType::kTH1D, {phiAxis}}); - registry.add("Lambda/hAntiLambdaEta", "", {HistType::kTH1D, {etaAxis}}); - registry.add("Lambda/hAntiLambdaMass_sparse", "", {HistType::kTHnSparseF, {{axisLambdaMass, ptAxis, nchAxis}}}); - registry.add("Lambda/hLambdas", "", {HistType::kTH1D, {singleCount}}); - registry.add("Lambda/hLambdas_corrected", "", {HistType::kTH1D, {singleCount}}); - - registry.add("Lambda/hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{10, 0.5, 10.5}}}); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillCandidate, "Lambda candidates"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillDaughterPt, "Daughter pt"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillMassCut, "Mass cut"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillRapidityCut, "Rapidity cut"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillDCAtoPV, "DCA to PV"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillDCAxDaughters, "DCA between daughters"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillV0Radius, "V0radius"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillCosPA, "CosPA"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillProperLifetime, "Proper lifetime"); - registry.get(HIST("Lambda/hLambdaCount"))->GetXaxis()->SetBinLabel(kFillDaughterTrackSelection, "Daughter track selection"); - } - } - - if (o2::analysis::gfw::regions.GetSize() < 0) + if (gfwMemberCache.regions.GetSize() < 0) { LOGF(error, "Configuration contains vectors of different size - check the GFWRegions configurable"); - for (auto i(0); i < o2::analysis::gfw::regions.GetSize(); ++i) { - fGFW->AddRegion(o2::analysis::gfw::regions.GetNames()[i], o2::analysis::gfw::regions.GetEtaMin()[i], o2::analysis::gfw::regions.GetEtaMax()[i], (o2::analysis::gfw::regions.GetpTDifs()[i]) ? ptbins + 1 : 1, o2::analysis::gfw::regions.GetBitmasks()[i]); } - for (auto i = 0; i < o2::analysis::gfw::configs.GetSize(); ++i) { - corrconfigs.push_back(fGFW->GetCorrelatorConfig(o2::analysis::gfw::configs.GetCorrs()[i], o2::analysis::gfw::configs.GetHeads()[i], o2::analysis::gfw::configs.GetpTDifs()[i])); + for (auto i(0); i < gfwMemberCache.regions.GetSize(); ++i) { + fGFW->AddRegion(gfwMemberCache.regions.GetNames()[i], gfwMemberCache.regions.GetEtaMin()[i], gfwMemberCache.regions.GetEtaMax()[i], (gfwMemberCache.regions.GetpTDifs()[i] != 0) ? ptbins + 1 : 1, gfwMemberCache.regions.GetBitmasks()[i]); } - if (corrconfigs.empty()) + for (auto i = 0; i < gfwMemberCache.configs.GetSize(); ++i) { + corrconfigs.push_back(fGFW->GetCorrelatorConfig(gfwMemberCache.configs.GetCorrs()[i], gfwMemberCache.configs.GetHeads()[i], gfwMemberCache.configs.GetpTDifs()[i] != 0)); + } + if (corrconfigs.empty()) { LOGF(error, "Configuration contains vectors of different size - check the GFWCorrConfig configurable"); + } + const auto& flowWeightModes = cfgMethod.cfgFlowProfileWeightModes.value; + if (!flowWeightModes.empty() && flowWeightModes.size() != corrconfigs.size()) { + LOGF(fatal, "cfgFlowProfileWeightModes must have one entry per cfgCorrConfig"); + } + for (const int& mode : flowWeightModes) { + if (mode != 0 && mode != 1) { + LOGF(fatal, "Invalid flow profile weight mode %d", mode); + } + } // Radial flow configs - for (auto i = 0; i < o2::analysis::gfw::configsV02.GetSize(); ++i) { - corrconfigsV02.push_back(fGFW->GetCorrelatorConfig(o2::analysis::gfw::configsV02.GetCorrs()[i], o2::analysis::gfw::configsV02.GetHeads()[i], o2::analysis::gfw::configsV02.GetpTDifs()[i])); + for (auto i = 0; i < gfwMemberCache.configsV02.GetSize(); ++i) { + corrconfigsV02.push_back(fGFW->GetCorrelatorConfig(gfwMemberCache.configsV02.GetCorrs()[i], gfwMemberCache.configsV02.GetHeads()[i], gfwMemberCache.configsV02.GetpTDifs()[i] != 0)); } - if (corrconfigsV02.empty()) + if (corrconfigsV02.empty()) { LOGF(error, "Radial (V02) configuration contains vectors of different size - check the GFWCorrConfig configurable"); - for (auto i = 0; i < o2::analysis::gfw::configsV0.GetSize(); ++i) { - corrconfigsV0.push_back(fGFW->GetCorrelatorConfig(o2::analysis::gfw::configsV0.GetCorrs()[i], o2::analysis::gfw::configsV0.GetHeads()[i], o2::analysis::gfw::configsV0.GetpTDifs()[i])); } - if (corrconfigsV0.empty()) + for (auto i = 0; i < gfwMemberCache.configsV0.GetSize(); ++i) { + corrconfigsV0.push_back(fGFW->GetCorrelatorConfig(gfwMemberCache.configsV0.GetCorrs()[i], gfwMemberCache.configsV0.GetHeads()[i], gfwMemberCache.configsV0.GetpTDifs()[i] != 0)); + } + if (corrconfigsV0.empty()) { LOGF(error, "Radial (V0) configuration contains vectors of different size - check the GFWCorrConfig configurable"); + } + const auto requiredChargedConfigs = static_cast(SpeciesCount); + const size_t requiredResonanceConfigs = requiredChargedConfigs + static_cast(ResonanceCount); + if (cfgFill.cfgAnalyseChargedHadrons && (corrconfigsV02.size() < requiredChargedConfigs || corrconfigsV0.size() < requiredChargedConfigs)) { + LOGF(fatal, "Charged-hadron analysis requires at least %zu V02 and V0 correlation configurations", requiredChargedConfigs); + } + if (cfgFill.cfgAnalyseK0Lambda && (corrconfigsV02.size() < requiredResonanceConfigs || corrconfigsV0.size() < requiredResonanceConfigs)) { + LOGF(fatal, "K0/Lambda analysis requires at least %zu V02 and V0 correlation configurations", requiredResonanceConfigs); + } fGFW->CreateRegions(); - TObjArray* oba = new TObjArray(); - addConfigObjectsToObjArray(oba, corrconfigs); - addConfigObjectsToObjArray(oba, corrconfigsV02); - addConfigObjectsToObjArray(oba, corrconfigsV0); + const size_t v02ResonanceEnd = std::min(requiredResonanceConfigs, corrconfigsV02.size()); + const size_t v0ResonanceEnd = std::min(requiredResonanceConfigs, corrconfigsV0.size()); + auto oba = new TObjArray(); + addConfigObjectsToObjArray(oba, corrconfigs, 0, corrconfigs.size()); + if (cfgFill.cfgAnalyseChargedHadrons) { + addConfigObjectsToObjArray(oba, corrconfigsV02, 0, std::min(SpeciesCount, corrconfigsV02.size())); + } + if (cfgFill.cfgAnalyseK0Lambda) { + addConfigObjectsToObjArray(oba, corrconfigsV02, std::min(SpeciesCount, corrconfigsV02.size()), v02ResonanceEnd); + } + if (cfgFill.cfgAnalyseChargedHadrons) { + addConfigObjectsToObjArray(oba, corrconfigsV0, 0, std::min(SpeciesCount, corrconfigsV0.size())); + } + if (cfgFill.cfgAnalyseK0Lambda) { + addConfigObjectsToObjArray(oba, corrconfigsV0, std::min(SpeciesCount, corrconfigsV0.size()), v0ResonanceEnd); + } - if (doprocessData || doprocessRun2 || doprocessMCReco) { - fFC->SetName("FlowContainer"); + if (doprocessData || doprocessRun2 || doprocessMCReco || doprocessMC) { + fFC.setObject(new FlowContainer("FlowContainer")); fFC->SetXAxis(fPtAxis); fFC->Initialize(oba, multAxis, cfgNbootstrap); + + fFCpt.setObject(new FlowPtContainer("FlowPtContainer")); + fFCpt->setEventWeight(cfgMethod.cfgUsePtCorrWeights ? eventweight::TupleWeight : eventweight::UnityWeight); + fFCpt->setUseCentralMoments(cfgMethod.cfgUseCentralMoments); + fFCpt->setUseGapMethod(cfgMethod.cfgUseGapMethod); + fFCpt->initialise(multAxis, cfgMpar, gfwMemberCache.configs, cfgNbootstrap); + if (cfgMethod.cfgStoreCombinedFractionWeights) { + for (const auto& name : {"mpt1_w2pc_ch", "mpt1_w2pc_pi", "mpt1_w2pc_ka", "mpt1_w2pc_pr"}) { + if (!cfgFill.cfgAnalyseChargedHadrons && std::string_view{name} != "mpt1_w2pc_ch") { + continue; + } + if (!fFCpt->addPtProfile(name, 1)) { + LOGF(fatal, "Could not add mean-pT profile %s", name); + } + } + } + fFCpt->initialiseSubevent(multAxis, cfgMpar, gfwMemberCache.etagapsPtPt.size(), cfgNbootstrap); + } + delete oba; + auto obaGen = new TObjArray(); + addConfigObjectsToObjArray(obaGen, corrconfigs, 0, corrconfigs.size()); + if (cfgFill.cfgAnalyseChargedHadrons) { + addConfigObjectsToObjArray(obaGen, corrconfigsV02, 0, std::min(SpeciesCount, corrconfigsV02.size())); } - if (doprocessMCGen || doprocessOnTheFly) { - fFCgen->SetName("FlowContainer_gen"); + if (cfgFill.cfgAnalyseK0Lambda) { + addConfigObjectsToObjArray(obaGen, corrconfigsV02, std::min(SpeciesCount, corrconfigsV02.size()), v02ResonanceEnd, true); + } + if (cfgFill.cfgAnalyseChargedHadrons) { + addConfigObjectsToObjArray(obaGen, corrconfigsV0, 0, std::min(SpeciesCount, corrconfigsV0.size())); + } + if (cfgFill.cfgAnalyseK0Lambda) { + addConfigObjectsToObjArray(obaGen, corrconfigsV0, std::min(SpeciesCount, corrconfigsV0.size()), v0ResonanceEnd, true); + } + if (doprocessMCGen || doprocessOnTheFly || doprocessMC) { + fFCgen.setObject(new FlowContainer("FlowContainer_gen")); fFCgen->SetXAxis(fPtAxis); - fFCgen->Initialize(oba, multAxis, cfgNbootstrap); + fFCgen->Initialize(obaGen, multAxis, cfgNbootstrap); + + fFCptGen.setObject(new FlowPtContainer("FlowPtContainer_gen")); + fFCptGen->setEventWeight(cfgMethod.cfgUsePtCorrWeights ? eventweight::TupleWeight : eventweight::UnityWeight); + fFCptGen->setUseCentralMoments(cfgMethod.cfgUseCentralMoments); + fFCptGen->setUseGapMethod(cfgMethod.cfgUseGapMethod); + fFCptGen->initialise(multAxis, cfgMpar, gfwMemberCache.configs, cfgNbootstrap); + if (cfgMethod.cfgStoreCombinedFractionWeights) { + for (const auto& name : {"mpt1_w2pc_ch", "mpt1_w2pc_pi", "mpt1_w2pc_ka", "mpt1_w2pc_pr"}) { + if (!cfgFill.cfgAnalyseChargedHadrons && std::string_view{name} != "mpt1_w2pc_ch") { + continue; + } + if (!fFCptGen->addPtProfile(name, 1)) { + LOGF(fatal, "Could not add generated mean-pT profile %s", name); + } + } + } + fFCptGen->initialiseSubevent(multAxis, cfgMpar, gfwMemberCache.etagapsPtPt.size(), cfgNbootstrap); + } + delete obaGen; + fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgTrackCuts.cfgDCAxyPtDep->c_str()), 0.001, 100); + fPtDepDCAxy->SetParameter(0, cfgTrackCuts.cfgDCAxyNSigma / 7.); + LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", cfgTrackCuts.cfgDCAxyPtDep->c_str())); + if (!cfgTrackCuts.cfgDCAzPtDep.value.empty()) { + fPtDepDCAz = new TF1("ptDepDCAz", Form("%s", cfgTrackCuts.cfgDCAzPtDep->c_str()), 0.001, 100); + LOGF(info, "DCAz pt-dependence function: %s", Form("%s", cfgTrackCuts.cfgDCAzPtDep->c_str())); } - delete oba; - - fFCpt->setUseCentralMoments(cfgUseCentralMoments); - fFCpt->setUseGapMethod(cfgUseGapMethod); - fFCpt->initialise(multAxis, cfgMpar, o2::analysis::gfw::configs, cfgNbootstrap); - fFCpt->initialiseSubevent(multAxis, cfgMpar, o2::analysis::gfw::etagapsPtPt.size(), cfgNbootstrap); // Multiplicity correlation cuts - if (cfgMultCut) { + if (cfgEventSelection.cfgMultCut) { fMultPVCutLow = new TF1("fMultPVCutLow", cfgMultCorrCuts.cfgMultCorrLowCutFunction->c_str(), 0, 100); - fMultPVCutLow->SetParameters(&(o2::analysis::gfw::multPVCorrCutPars[0])); + fMultPVCutLow->SetParameters(gfwMemberCache.multPVCorrCutPars.data()); fMultPVCutHigh = new TF1("fMultPVCutHigh", cfgMultCorrCuts.cfgMultCorrHighCutFunction->c_str(), 0, 100); - fMultPVCutHigh->SetParameters(&(o2::analysis::gfw::multPVCorrCutPars[0])); + fMultPVCutHigh->SetParameters(gfwMemberCache.multPVCorrCutPars.data()); fMultCutLow = new TF1("fMultCutLow", cfgMultCorrCuts.cfgMultCorrLowCutFunction->c_str(), 0, 100); - fMultCutLow->SetParameters(&(o2::analysis::gfw::multGlobalCorrCutPars[0])); + fMultCutLow->SetParameters(gfwMemberCache.multGlobalCorrCutPars.data()); fMultCutHigh = new TF1("fMultCutHigh", cfgMultCorrCuts.cfgMultCorrHighCutFunction->c_str(), 0, 100); - fMultCutHigh->SetParameters(&(o2::analysis::gfw::multGlobalCorrCutPars[0])); + fMultCutHigh->SetParameters(gfwMemberCache.multGlobalCorrCutPars.data()); fMultPVGlobalCutHigh = new TF1("fMultPVGlobalCutHigh", cfgMultCorrCuts.cfgMultGlobalPVCorrCutFunction->c_str(), 0, nchbinning.back()); - fMultPVGlobalCutHigh->SetParameters(&(o2::analysis::gfw::multGlobalPVCorrCutPars[0])); + fMultPVGlobalCutHigh->SetParameters(gfwMemberCache.multGlobalPVCorrCutPars.data()); LOGF(info, "Global V0A function: %s in range 0-%g", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), v0aAxis.binEdges.back()); fMultGlobalV0ACutLow = new TF1("fMultGlobalV0ACutLow", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, v0aAxis.binEdges.back()); - for (std::size_t i = 0; i < o2::analysis::gfw::multGlobalV0ACutPars.size(); ++i) - fMultGlobalV0ACutLow->SetParameter(i, o2::analysis::gfw::multGlobalV0ACutPars[i]); - fMultGlobalV0ACutLow->SetParameter(o2::analysis::gfw::multGlobalV0ACutPars.size(), cfgMultCorrCuts.cfgGlobalV0ALowSigma); - for (int i = 0; i < fMultGlobalV0ACutLow->GetNpar(); ++i) + for (std::size_t i = 0; i < gfwMemberCache.multGlobalV0ACutPars.size(); ++i) { + fMultGlobalV0ACutLow->SetParameter(i, gfwMemberCache.multGlobalV0ACutPars[i]); + } + fMultGlobalV0ACutLow->SetParameter(gfwMemberCache.multGlobalV0ACutPars.size(), cfgMultCorrCuts.cfgGlobalV0ALowSigma); + for (int i = 0; i < fMultGlobalV0ACutLow->GetNpar(); ++i) { LOGF(info, "fMultGlobalV0ACutLow par %d = %g", i, fMultGlobalV0ACutLow->GetParameter(i)); + } fMultGlobalV0ACutHigh = new TF1("fMultGlobalV0ACutHigh", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, v0aAxis.binEdges.back()); - for (std::size_t i = 0; i < o2::analysis::gfw::multGlobalV0ACutPars.size(); ++i) - fMultGlobalV0ACutHigh->SetParameter(i, o2::analysis::gfw::multGlobalV0ACutPars[i]); - fMultGlobalV0ACutHigh->SetParameter(o2::analysis::gfw::multGlobalV0ACutPars.size(), cfgMultCorrCuts.cfgGlobalV0AHighSigma); - for (int i = 0; i < fMultGlobalV0ACutHigh->GetNpar(); ++i) + for (std::size_t i = 0; i < gfwMemberCache.multGlobalV0ACutPars.size(); ++i) { + fMultGlobalV0ACutHigh->SetParameter(i, gfwMemberCache.multGlobalV0ACutPars[i]); + } + fMultGlobalV0ACutHigh->SetParameter(gfwMemberCache.multGlobalV0ACutPars.size(), cfgMultCorrCuts.cfgGlobalV0AHighSigma); + for (int i = 0; i < fMultGlobalV0ACutHigh->GetNpar(); ++i) { LOGF(info, "fMultGlobalV0ACutHigh par %d = %g", i, fMultGlobalV0ACutHigh->GetParameter(i)); + } LOGF(info, "Global T0A function: %s", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str()); fMultGlobalT0ACutLow = new TF1("fMultGlobalT0ACutLow", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, t0aAxis.binEdges.back()); - for (std::size_t i = 0; i < o2::analysis::gfw::multGlobalT0ACutPars.size(); ++i) - fMultGlobalT0ACutLow->SetParameter(i, o2::analysis::gfw::multGlobalT0ACutPars[i]); - fMultGlobalT0ACutLow->SetParameter(o2::analysis::gfw::multGlobalT0ACutPars.size(), cfgMultCorrCuts.cfgGlobalT0ALowSigma); - for (int i = 0; i < fMultGlobalT0ACutLow->GetNpar(); ++i) + for (std::size_t i = 0; i < gfwMemberCache.multGlobalT0ACutPars.size(); ++i) { + fMultGlobalT0ACutLow->SetParameter(i, gfwMemberCache.multGlobalT0ACutPars[i]); + } + fMultGlobalT0ACutLow->SetParameter(gfwMemberCache.multGlobalT0ACutPars.size(), cfgMultCorrCuts.cfgGlobalT0ALowSigma); + for (int i = 0; i < fMultGlobalT0ACutLow->GetNpar(); ++i) { LOGF(info, "fMultGlobalT0ACutLow par %d = %g", i, fMultGlobalT0ACutLow->GetParameter(i)); + } fMultGlobalT0ACutHigh = new TF1("fMultGlobalT0ACutHigh", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, t0aAxis.binEdges.back()); - for (std::size_t i = 0; i < o2::analysis::gfw::multGlobalT0ACutPars.size(); ++i) - fMultGlobalT0ACutHigh->SetParameter(i, o2::analysis::gfw::multGlobalT0ACutPars[i]); - fMultGlobalT0ACutHigh->SetParameter(o2::analysis::gfw::multGlobalT0ACutPars.size(), cfgMultCorrCuts.cfgGlobalT0AHighSigma); - for (int i = 0; i < fMultGlobalT0ACutHigh->GetNpar(); ++i) + for (std::size_t i = 0; i < gfwMemberCache.multGlobalT0ACutPars.size(); ++i) { + fMultGlobalT0ACutHigh->SetParameter(i, gfwMemberCache.multGlobalT0ACutPars[i]); + } + fMultGlobalT0ACutHigh->SetParameter(gfwMemberCache.multGlobalT0ACutPars.size(), cfgMultCorrCuts.cfgGlobalT0AHighSigma); + for (int i = 0; i < fMultGlobalT0ACutHigh->GetNpar(); ++i) { LOGF(info, "fMultGlobalT0ACutHigh par %d = %g", i, fMultGlobalT0ACutHigh->GetParameter(i)); + } } if (cfgTrackCuts.cfgTPCSectorCut) { @@ -778,9 +1092,9 @@ struct FlowGenericFramework { fPhiCutHigh = new TF1("fPhiCutHigh", "0.1/x+pi/18.0+0.06", 0, 100); } // Density dependent corrections - if (cfgUseDensityDependentCorrection) { + if (cfgEfficiency.cfgUseDensityDependentCorrection) { std::vector pTEffBins = {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0}; - hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, &pTEffBins[0]); + hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, pTEffBins.data()); funcEff.resize(pTEffBins.size() - 1); // LHC24g3 Eff std::vector f1p0 = cfgTrackDensityP0; @@ -798,7 +1112,7 @@ struct FlowGenericFramework { } } - static constexpr std::string_view FillTimeName[] = {"before/", "after/"}; + static constexpr std::array FillTimeName = {"before/", "after/"}; void printResoCuts() { @@ -814,7 +1128,7 @@ struct FlowGenericFramework { size_t maxValLen = 0; for (size_t r = 0; r < nRows; ++r) { std::ostringstream oss; - oss << std::fixed << std::setprecision(2) << valuesMatrix[r][c]; + oss << std::fixed << std::setprecision(3) << valuesMatrix[r][c]; maxValLen = std::max(maxValLen, oss.str().length()); } colWidths[c] = std::max(lbl.labels_cols[c].size(), maxValLen); @@ -822,15 +1136,17 @@ struct FlowGenericFramework { // Determine row label width size_t rowLabelWidth = 0; - for (const auto& rowName : lbl.labels_rows) + for (const auto& rowName : lbl.labels_rows) { rowLabelWidth = std::max(rowLabelWidth, rowName.size()); + } rowLabelWidth += 2; // for ": " // Print header std::ostringstream header; header << std::setw(rowLabelWidth) << " "; - for (size_t c = 0; c < nCols; ++c) + for (size_t c = 0; c < nCols; ++c) { header << std::setw(colWidths[c]) << lbl.labels_cols[c] << " "; + } LOGF(info, "%s", header.str().c_str()); // Print rows @@ -838,7 +1154,7 @@ struct FlowGenericFramework { std::ostringstream line; line << std::setw(rowLabelWidth) << (lbl.labels_rows[r] + ":"); for (size_t c = 0; c < nCols; ++c) { - line << std::setw(colWidths[c]) << std::fixed << std::setprecision(2) << valuesMatrix[r][c] << " "; + line << std::setw(colWidths[c]) << std::fixed << std::setprecision(3) << valuesMatrix[r][c] << " "; } LOGF(info, "%s", line.str().c_str()); } @@ -846,37 +1162,58 @@ struct FlowGenericFramework { // ----- nSigma PID ----- // Map arrays into a 2D vector - std::vector> nSigmaVals = { - std::vector(tpcNsigmaCut.begin(), tpcNsigmaCut.end()), - std::vector(tofNsigmaCut.begin(), tofNsigmaCut.end()), - std::vector(itsNsigmaCut.begin(), itsNsigmaCut.end())}; - printTable(cfgPIDCuts.nSigmas.value, nSigmaVals, "nSigma PID Cuts"); + std::vector> nSigmaVals(6, std::vector(3)); + for (size_t i = 0; i < nSigmaVals.size(); i++) { + nSigmaVals[i][0] = tpcNsigmaCut[i]; // TPC + nSigmaVals[i][1] = tofNsigmaCut[i]; // TOF + nSigmaVals[i][2] = itsNsigmaCut[i]; // ITS + } + printTable(nSigmas.value, nSigmaVals, "nSigma PID Cuts"); // ----- Resonance Cuts ----- std::vector> resoCutsVals(resoCutVals.size()); - for (size_t r = 0; r < resoCutVals.size(); ++r) + for (size_t r = 0; r < resoCutVals.size(); ++r) { resoCutsVals[r] = std::vector(resoCutVals[r].begin(), resoCutVals[r].end()); - printTable(cfgPIDCuts.resonanceCuts.value, resoCutsVals, "Resonance Cuts"); + } + printTable(resonanceCuts.value, resoCutsVals, "Resonance Cuts"); // ----- Resonance Switches ----- std::vector> resoSwitchValsF(resoSwitchVals.size()); - for (size_t r = 0; r < resoSwitchVals.size(); ++r) + for (size_t r = 0; r < resoSwitchVals.size(); ++r) { resoSwitchValsF[r] = std::vector(resoSwitchVals[r].begin(), resoSwitchVals[r].end()); - printTable(cfgPIDCuts.resonanceSwitches.value, resoSwitchValsF, "Resonance Switches"); + } + printTable(resonanceSwitches.value, resoSwitchValsF, "Resonance Switches"); } enum QAFillTime { - kBefore, - kAfter + Before, + After }; - void addConfigObjectsToObjArray(TObjArray* oba, const std::vector& configs) + template + void addConfigObjectsToObjArray(TObjArray* oba, const std::vector& configs, size_t first, size_t last, bool resonanceConfigs = false) { - for (auto it = configs.begin(); it != configs.end(); ++it) { + if (first > last || last > configs.size()) { + LOGF(fatal, "Invalid correlation configuration range [%zu, %zu) for %zu entries", first, last, configs.size()); + } + if constexpr (dt == Gen) { + if (resonanceConfigs && first < static_cast(SpeciesCount)) { + LOGF(fatal, "Generated resonance configurations must start at index %d or later", static_cast(SpeciesCount)); + } + } + for (auto it = configs.begin() + first; it != configs.begin() + last; ++it) { + if constexpr (dt == Gen) { + if (resonanceConfigs) { + const auto resonanceIndex = static_cast(std::distance(configs.begin(), it)) - SpeciesCount; + if (resonanceIndex != K0Signal && resonanceIndex != LambdaSignal) { + continue; + } + } + } if (it->pTDif) { std::string suffix = "_ptDiff"; for (auto i = 0; i < fPtAxis->GetNbins(); ++i) { std::string index = Form("_pt_%i", i + 1); - oba->Add(new TNamed(it->Head.c_str() + index, it->Head.c_str() + suffix)); + oba->Add(new TNamed(it->Head + index, it->Head + suffix)); } } else { oba->Add(new TNamed(it->Head.c_str(), it->Head.c_str())); @@ -904,35 +1241,75 @@ struct FlowGenericFramework { void loadCorrections(aod::BCsWithTimestamps::iterator const& bc) { uint64_t timestamp = bc.timestamp(); - if (!cfgRunByRun && cfg.correctionsLoaded) + if (!cfgFill.cfgRunByRun && cfg.correctionsLoaded) { return; + } if (!cfgAcceptance.value.empty()) { - std::string runstr = (cfgRunByRun) ? "RunByRun/" : ""; + const int acceptanceKey = cfgFill.cfgRunByRun ? bc.runNumber() : 0; + auto cachedAcceptance = cfg.mAcceptanceByRun.find(acceptanceKey); + if (cachedAcceptance == cfg.mAcceptanceByRun.end()) { + std::vector> acceptanceWeights; + auto loadAcceptance = [&](const std::string& path) { + auto* weights = ccdb->getForTimeStamp(path, timestamp); + if (weights == nullptr) { + LOGF(fatal, "Could not load acceptance weights from %s", path.c_str()); + return; + } + auto* clonedWeights = dynamic_cast(weights->Clone()); + if (clonedWeights == nullptr) { + LOGF(fatal, "Could not clone acceptance weights from %s", path.c_str()); + return; + } + acceptanceWeights.emplace_back(clonedWeights); + }; + + std::string runstr = (cfgFill.cfgRunByRun) ? "RunByRun/" : ""; + if (cfgFill.cfgUsePID) { + loadAcceptance(cfgAcceptance.value + runstr + "ref/"); + loadAcceptance(cfgAcceptance.value + runstr + "ch/"); + loadAcceptance(cfgAcceptance.value + runstr + "pi/"); + loadAcceptance(cfgAcceptance.value + runstr + "ka/"); + loadAcceptance(cfgAcceptance.value + runstr + "pr/"); + } else { + loadAcceptance(cfgAcceptance.value + runstr); + } + cachedAcceptance = cfg.mAcceptanceByRun.emplace(acceptanceKey, std::move(acceptanceWeights)).first; + LOGF(info, "Loaded acceptance weights for run %d", bc.runNumber()); + } + cfg.mAcceptance.clear(); - if (cfgUsePID) { - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "ref/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "ch/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "pi/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "ka/", timestamp)); - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr + "pr/", timestamp)); - } else { - cfg.mAcceptance.push_back(ccdb->getForTimeStamp(cfgAcceptance.value + runstr, timestamp)); + cfg.mAcceptance.reserve(cachedAcceptance->second.size()); + for (const auto& weights : cachedAcceptance->second) { + cfg.mAcceptance.push_back(weights.get()); } } - if (!cfgEfficiency.value.empty()) { - if (!cfgUsePIDEfficiencies) { - cfg.mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + // Run-by-run efficiencies are not supported at the moment + if (cfg.correctionsLoaded) { + return; + } + if (!cfgEfficiency.cfgEfficiencyPath.value.empty()) { + if (!cfgEfficiency.cfgUsePIDEfficiencies) { + if (cfgEfficiency.cfgUse2DEfficiency) { + cfg.mEfficiency = ccdb->getForTimeStamp(cfgEfficiency.cfgEfficiencyPath, timestamp); + } else { + cfg.mEfficiency = ccdb->getForTimeStamp(cfgEfficiency.cfgEfficiencyPath, timestamp); + } if (cfg.mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)cfg.mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value.c_str()); } else { - std::vector species = {"ch", "pi", "ka", "pr"}; + std::vector species = {"ch", "pi", "ka", "pr", "k0", "lambda"}; for (const auto& sp : species) { - cfg.mPIDEfficiencies.push_back(ccdb->getForTimeStamp(cfgEfficiency.value + "/" + sp, timestamp)); - if (cfg.mPIDEfficiencies.back() == nullptr) - LOGF(fatal, "Could not load PID efficiency histograms from %s", cfgEfficiency.value + "/" + sp); - LOGF(info, "Loaded PID efficiency histogram from %s", cfgEfficiency.value + "/" + sp); + if (cfgEfficiency.cfgUse2DEfficiency) { + cfg.mPIDEfficiencies.push_back(ccdb->getForTimeStamp(cfgEfficiency.cfgEfficiencyPath.value + "/" + sp, timestamp)); + } else { + cfg.mPIDEfficiencies.push_back(ccdb->getForTimeStamp(cfgEfficiency.cfgEfficiencyPath.value + "/" + sp, timestamp)); + } + if (cfg.mPIDEfficiencies.back() == nullptr) { + LOGF(fatal, "Could not load PID efficiency histograms from %s", cfgEfficiency.cfgEfficiencyPath.value + "/" + sp); + } + LOGF(info, "Loaded PID efficiency histogram from %s", cfgEfficiency.cfgEfficiencyPath.value + "/" + sp); } } } @@ -940,32 +1317,46 @@ struct FlowGenericFramework { } template - double getAcceptance(TTrack track, const double& vtxz, int index) + double getAcceptance(const TTrack& track, const double& vtxz, int index) { // 0 ref, 1 ch, 2 pi, 3 ka, 4 pr double wacc = 1; - if (!cfg.mAcceptance.empty()) + if (!cfg.mAcceptance.empty()) { wacc = cfg.mAcceptance[index]->getNUA(track.phi(), track.eta(), vtxz); + } return wacc; } template - double getEfficiency(TTrack track, int pidIndex = 0) - { //-1 ref, 0 ch, 1 pi, 2 ka, 3 pr + double getEfficiency(const TTrack& track, const float& centrality, int pidIndex = 0) + { //-1 ref, 0 ch, 1 pi, 2 ka, 3 pr, 4 k0, 5 lambda double eff = 1.; - if (!cfgUsePIDEfficiencies) { - if (cfg.mEfficiency) - eff = cfg.mEfficiency->GetBinContent(cfg.mEfficiency->FindBin(track.pt())); + if (!cfgEfficiency.cfgUsePIDEfficiencies) { + if (!cfg.mEfficiency) { + return eff; + } + if (cfgEfficiency.cfgUse2DEfficiency) { + eff = dynamic_cast(cfg.mEfficiency)->GetBinContent(dynamic_cast(cfg.mEfficiency)->FindBin(track.pt(), centrality)); + } else { + eff = dynamic_cast(cfg.mEfficiency)->GetBinContent(dynamic_cast(cfg.mEfficiency)->FindBin(track.pt())); + } } else { - eff = cfg.mPIDEfficiencies[pidIndex]->GetBinContent(cfg.mPIDEfficiencies[pidIndex]->FindBin(track.pt())); + if (cfg.mPIDEfficiencies.empty()) { + return eff; + } + if (cfgEfficiency.cfgUse2DEfficiency) { + eff = dynamic_cast(cfg.mPIDEfficiencies[pidIndex])->GetBinContent(dynamic_cast(cfg.mPIDEfficiencies[pidIndex])->FindBin(track.pt(), centrality)); + } else { + eff = dynamic_cast(cfg.mPIDEfficiencies[pidIndex])->GetBinContent(dynamic_cast(cfg.mPIDEfficiencies[pidIndex])->FindBin(track.pt())); + } } - if (eff == 0) + if (eff == 0) { return -1.; - else - return 1. / eff; + } + return 1. / eff; } template - int getNsigmaPID(TTrack track) + int getNsigmaPID(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -975,21 +1366,24 @@ struct FlowGenericFramework { // Choose which nSigma to use std::array nSigmaToUse = (track.pt() > cfgPIDCuts.cfgTofPtCut && track.hasTOF()) ? nSigmaCombined : nSigmaTPC; - if (track.pt() > cfgPIDCuts.cfgTofPtCut && !track.hasTOF()) + if (track.pt() > cfgPIDCuts.cfgTofPtCut && !track.hasTOF()) { return 0; + } const int numSpecies = 3; int pidCount = 0; // Select particle with the lowest nsigma for (int i = 0; i < numSpecies; ++i) { if (std::abs(nSigmaToUse[i]) < nsigma) { - if (pidCount > 0 && cfgPIDCuts.cfgUseStrictPID) + if (pidCount > 0 && cfgPIDCuts.cfgUseStrictPID) { return 0; // more than one particle with low nsigma + } pidCount++; pid = i; - if (!cfgPIDCuts.cfgUseStrictPID) + if (!cfgPIDCuts.cfgUseStrictPID) { nsigma = std::abs(nSigmaToUse[i]); + } } } @@ -997,7 +1391,7 @@ struct FlowGenericFramework { } template - int getNsigmaPIDAssymmetric(TTrack track) + int getNsigmaPIDAssymmetric(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -1008,7 +1402,7 @@ struct FlowGenericFramework { std::array nSigmaToUse = cfgPIDCuts.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS std::array detectorNsigmaCut = cfgPIDCuts.cfgUseItsPID ? itsNsigmaCut : tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - bool isPion, isKaon, isProton; + bool isPion = false, isKaon = false, isProton = false; bool isDetectedPion = nSigmaToUse[0] < detectorNsigmaCut[0] && nSigmaToUse[0] > detectorNsigmaCut[0 + 3]; bool isDetectedKaon = nSigmaToUse[1] < detectorNsigmaCut[1] && nSigmaToUse[1] > detectorNsigmaCut[1 + 3]; bool isDetectedProton = nSigmaToUse[2] < detectorNsigmaCut[2] && nSigmaToUse[2] > detectorNsigmaCut[2 + 3]; @@ -1019,7 +1413,9 @@ struct FlowGenericFramework { if (track.pt() > cfgPIDCuts.cfgTofPtCut && !track.hasTOF()) { return 0; - } else if (track.pt() > cfgPIDCuts.cfgTofPtCut && track.hasTOF()) { + } + + if (track.pt() > cfgPIDCuts.cfgTofPtCut && track.hasTOF()) { isPion = isTofPion && isDetectedPion; isKaon = isTofKaon && isDetectedKaon; isProton = isTofProton && isDetectedProton; @@ -1034,11 +1430,11 @@ struct FlowGenericFramework { } if (isPion) { - pid = PIONS; + pid = Pions; } else if (isKaon) { - pid = KAONS; + pid = Kaons; } else if (isProton) { - pid = PROTONS; + pid = Protons; } else { return 0; // no particle satisfies the criteria } @@ -1047,42 +1443,48 @@ struct FlowGenericFramework { } template - bool eventSelected(TCollision collision, const int multTrk, const float& centrality, const int run) + bool eventSelected(const TCollision& collision, const int multTrk, const float& centrality, const int run) { // Cut on trigger alias if (cfgEventCutFlags.cfgTVXinTRD) { - if (collision.alias_bit(kTVXinTRD)) { + if (collision.alias_bit(TVXinTRD)) { // TRD triggered // "CMTVX-B-NOPF-TRD,minbias_TVX" return false; } - registry.fill(HIST("eventQA/eventSel"), kTVXinTRD); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(kTVXinTRD); + registryQA.fill(HIST("eventQA/eventSel"), TVXinTRD); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(TVXinTRD); + } } // Cut on event selection flags for (const auto& cut : eventcutflags) { - if (!cut.enabled) + if (!cut.enabled) { continue; - if (!collision.selection_bit(cut.flag)) + } + if (!collision.selection_bit(cut.flag)) { return false; - registry.fill(HIST("eventQA/eventSel"), cut.histBin); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(cut.histBin); + } + registryQA.fill(HIST("eventQA/eventSel"), cut.histBin); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(cut.histBin); + } } // Cut on vertex - if (!selectVertex(collision)) + if (!selectVertex(collision)) { return false; + } // Cut on multiplicity correlations - data driven - if (cfgMultCut) { - if (!selectMultiplicityCorrelation(collision, multTrk, centrality, run)) + if (cfgEventSelection.cfgMultCut) { + if (!selectMultiplicityCorrelation(collision, multTrk, centrality, run)) { return false; + } } return true; } template - bool selectVertex(TCollision collision) + bool selectVertex(const TCollision& collision) { float vtxz = -999; if (collision.numContrib() > 1) { @@ -1090,337 +1492,725 @@ struct FlowGenericFramework { float zRes = std::sqrt(collision.covZZ()); float minZRes = 0.25; int minNContrib = 20; - if (zRes > minZRes && collision.numContrib() < minNContrib) + if (zRes > minZRes && collision.numContrib() < minNContrib) { vtxz = -999; + } } - if (vtxz > o2::analysis::gfw::vtxZup || vtxz < o2::analysis::gfw::vtxZlow) + if (vtxz > gfwMemberCache.vtxZup || vtxz < gfwMemberCache.vtxZlow) { return false; - else - return true; + } + + return true; } template - bool selectMultiplicityCorrelation(TCollision collision, const int multTrk, const float& centrality, const int run) + bool selectMultiplicityCorrelation(const TCollision& collision, const int multTrk, const float& centrality, const int run) { auto multNTracksPV = collision.multNTracksPV(); - if (multNTracksPV < fMultPVCutLow->Eval(centrality)) + if (multNTracksPV < fMultPVCutLow->Eval(centrality)) { return false; - if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) + } + if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) { return false; - if (multTrk < fMultCutLow->Eval(centrality)) + } + if (multTrk < fMultCutLow->Eval(centrality)) { return false; - if (multTrk > fMultCutHigh->Eval(centrality)) + } + if (multTrk > fMultCutHigh->Eval(centrality)) { return false; - if (multTrk > fMultPVGlobalCutHigh->Eval(collision.multNTracksPV())) + } + if (multTrk > fMultPVGlobalCutHigh->Eval(collision.multNTracksPV())) { return false; + } - if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) < fMultGlobalV0ACutLow->Eval(multTrk)) + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) < fMultGlobalV0ACutLow->Eval(multTrk)) { return false; - if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) > fMultGlobalV0ACutHigh->Eval(multTrk)) + } + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) > fMultGlobalV0ACutHigh->Eval(multTrk)) { return false; - if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) < fMultGlobalT0ACutLow->Eval(multTrk)) + } + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) < fMultGlobalT0ACutLow->Eval(multTrk)) { return false; - if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) > fMultGlobalT0ACutHigh->Eval(multTrk)) + } + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) > fMultGlobalT0ACutHigh->Eval(multTrk)) { return false; - registry.fill(HIST("eventQA/eventSel"), kMultCuts); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(kMultCuts); + } + registryQA.fill(HIST("eventQA/eventSel"), MultCuts); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(MultCuts); + } return true; } template - bool trackSelected(TTrack track, const int field) + bool trackSelected(const TTrack& track, const int field) { if (cfgTrackCuts.cfgTPCSectorCut) { double phimodn = track.phi(); - if (field < 0) // for negative polarity field + if (field < 0) { // for negative polarity field phimodn = o2::constants::math::TwoPI - phimodn; - if (track.sign() < 0) // for negative charge + } + if (track.sign() < 0) { // for negative charge phimodn = o2::constants::math::TwoPI - phimodn; - if (phimodn < 0) + } + if (phimodn < 0) { LOGF(warning, "phi < 0: %g", phimodn); + } phimodn += o2::constants::math::PI / 18.0; // to center gap in the middle phimodn = fmod(phimodn, o2::constants::math::PI / 9.0); - if (cfgFillQA) - registry.fill(HIST("trackQA/before/pt_phi"), track.pt(), phimodn); - if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("trackQA/before/pt_phi"), track.pt(), phimodn); + } + if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) { return false; // reject track - if (cfgFillQA) - registry.fill(HIST("trackQA/after/pt_phi"), track.pt(), phimodn); + } + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("trackQA/after/pt_phi"), track.pt(), phimodn); + } + } + if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) { + return false; } - if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > cfgTrackCuts.cfgDCAxyNSigma / 7. * (0.0105f + 0.0350f / std::pow(track.pt(), 1.1)))) + if (!cfgTrackCuts.cfgDCAzPtDep.value.empty() && std::fabs(track.dcaZ()) > fPtDepDCAz->Eval(track.pt())) { return false; + } return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); } template - bool nchSelected(TTrack track) + bool nchSelected(const TTrack& track) { - if (std::fabs(track.dcaXY()) > (0.0105f + 0.035f / std::pow(track.pt(), 1.1))) + // Renormalise to default cut + const float defaultNsigma = 7; + if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > defaultNsigma / cfgTrackCuts.cfgDCAxyNSigma * fPtDepDCAxy->Eval(track.pt()))) { return false; - return ((track.tpcNClsCrossedRows() >= 70) && (track.tpcNClsFound() >= 50) && (track.itsNCls() >= 5)); // o2-linter: disable=magic-number (hard coded default cuts) + } + if (!cfgTrackCuts.cfgDCAzPtDep.value.empty() && std::fabs(track.dcaZ()) > fPtDepDCAz->Eval(track.pt())) { + return false; + } + int tpcNClsCrossedRowsDefault = 70; + int tpcNClsFoundDefault = 50; + int itsNclsDefault = 5; + return ((track.tpcNClsCrossedRows() >= tpcNClsCrossedRowsDefault) && (track.tpcNClsFound() >= tpcNClsFoundDefault) && (track.itsNCls() >= itsNclsDefault)); } - enum DataType { - kReco, - kGen - }; - template - void fillWeights(const TTrack track, const double vtxz, const int pid_index, const int run) + void fillWeights(const TTrack& track, const double vtxz, const int pid_index, const int run) { - if (cfgUsePID) { - double ptpidmins[] = {o2::analysis::gfw::ptpoilow, o2::analysis::gfw::ptpoilow, 0.3, 0.5}; // min pt for ch, pi, ka, pr - double ptpidmaxs[] = {o2::analysis::gfw::ptpoiup, o2::analysis::gfw::ptpoiup, 6.0, 6.0}; // max pt for ch, pi, ka, pr - bool withinPtPOI = (ptpidmins[pid_index] < track.pt()) && (track.pt() < ptpidmaxs[pid_index]); // within POI pT range - bool withinPtRef = (o2::analysis::gfw::ptreflow < track.pt()) && (track.pt() < o2::analysis::gfw::ptrefup); // within RF pT range - if (cfgRunByRun) { - if (withinPtRef && !pid_index) - th3sList[run][hNUAref]->Fill(track.phi(), track.eta(), vtxz); // pt-subset of charged particles for ref flow - if (withinPtPOI) - th3sList[run][hNUAch + pid_index]->Fill(track.phi(), track.eta(), vtxz); // charged and id'ed particle weights + if (cfgFill.cfgUsePID) { + std::array ptpidmins = {gfwMemberCache.ptpoilow, gfwMemberCache.ptpoilow, 0.3, 0.5}; // min pt for ch, pi, ka, pr + std::array ptpidmaxs = {gfwMemberCache.ptpoiup, gfwMemberCache.ptpoiup, 6.0, 6.0}; // max pt for ch, pi, ka, pr + bool withinPtPOI = (ptpidmins[pid_index] < track.pt()) && (track.pt() < ptpidmaxs[pid_index]); // within POI pT range + bool withinPtRef = (gfwMemberCache.ptreflow < track.pt()) && (track.pt() < gfwMemberCache.ptrefup); // within RF pT range + if (cfgFill.cfgFillRunByRunQA) { + if (withinPtRef && !pid_index) { + th3sList[run][NUAref]->Fill(track.phi(), track.eta(), vtxz); // pt-subset of charged particles for ref flow + } + if (withinPtPOI) { + th3sList[run][NUAch + pid_index]->Fill(track.phi(), track.eta(), vtxz); // charged and id'ed particle weights + } } else { - if (withinPtRef && !pid_index) - registry.fill(HIST("phi_eta_vtxz_ref"), track.phi(), track.eta(), vtxz); // pt-subset of charged particles for ref flow + if (withinPtRef && !pid_index) { + registryQA.fill(HIST("phi_eta_vtxz_ref"), track.phi(), track.eta(), vtxz); // pt-subset of charged particles for ref flow + } if (withinPtPOI) { switch (pid_index) { case 0: - registry.fill(HIST("phi_eta_vtxz_ch"), track.phi(), track.eta(), vtxz); // charged particle weights + registryQA.fill(HIST("phi_eta_vtxz_ch"), track.phi(), track.eta(), vtxz); // charged particle weights break; case 1: - registry.fill(HIST("phi_eta_vtxz_pi"), track.phi(), track.eta(), vtxz); // pion weights + registryQA.fill(HIST("phi_eta_vtxz_pi"), track.phi(), track.eta(), vtxz); // pion weights break; case 2: - registry.fill(HIST("phi_eta_vtxz_ka"), track.phi(), track.eta(), vtxz); // kaon weights + registryQA.fill(HIST("phi_eta_vtxz_ka"), track.phi(), track.eta(), vtxz); // kaon weights break; case 3: - registry.fill(HIST("phi_eta_vtxz_pr"), track.phi(), track.eta(), vtxz); // proton weights + registryQA.fill(HIST("phi_eta_vtxz_pr"), track.phi(), track.eta(), vtxz); // proton weights + break; + default: break; } } } } else { - if (cfgRunByRun) - th3sList[run][hNUAref]->Fill(track.phi(), track.eta(), vtxz); - else - registry.fill(HIST("phi_eta_vtxz_ref"), track.phi(), track.eta(), vtxz); + if (cfgFill.cfgFillRunByRunQA) { + th3sList[run][NUAref]->Fill(track.phi(), track.eta(), vtxz); + } else { + registryQA.fill(HIST("phi_eta_vtxz_ref"), track.phi(), track.eta(), vtxz); + } } - return; } void createRunByRunHistograms(const int run) { - AxisSpec phiAxis = {o2::analysis::gfw::phibins, o2::analysis::gfw::philow, o2::analysis::gfw::phiup, "#phi"}; + LOGF(info, "Creating histograms for run %d", run); + AxisSpec phiAxis = {gfwMemberCache.phibins, gfwMemberCache.philow, gfwMemberCache.phiup, "#phi"}; AxisSpec phiModAxis = {100, 0, constants::math::PI / 9, "fmod(#varphi,#pi/9)"}; - AxisSpec etaAxis = {o2::analysis::gfw::etabins, cfgEta->first, cfgEta->second, "#eta"}; - AxisSpec vtxAxis = {o2::analysis::gfw::vtxZbins, -cfgVtxZ, cfgVtxZ, "Vtx_{z} (cm)"}; - AxisSpec nchAxis = {o2::analysis::gfw::nchbins, o2::analysis::gfw::nchlow, o2::analysis::gfw::nchup, "N_{ch}"}; - AxisSpec centAxis = {o2::analysis::gfw::centbinning, "Centrality (%)"}; - AxisSpec ptAxis = {o2::analysis::gfw::ptbinning, "#it{p}_{T} GeV/#it{c}"}; - std::vector> histos(kCount_TH1Names); - histos[hPhi] = registry.add(Form("%d/phi", run), "", {HistType::kTH1D, {phiAxis}}); - histos[hEta] = registry.add(Form("%d/eta", run), "", {HistType::kTH1D, {etaAxis}}); - histos[hVtxZ] = registry.add(Form("%d/vtxz", run), "", {HistType::kTH1D, {vtxAxis}}); - histos[hMult] = registry.add(Form("%d/mult", run), "", {HistType::kTH1D, {nchAxis}}); - histos[hCent] = registry.add(Form("%d/cent", run), "", {HistType::kTH1D, {centAxis}}); - histos[hEventSel] = registry.add(Form("%d/eventSel", run), "Number of Events;; Counts", {HistType::kTH1D, {{11, 0, 11}}}); - histos[hEventSel]->GetXaxis()->SetBinLabel(1, "Filtered event"); - histos[hEventSel]->GetXaxis()->SetBinLabel(2, "sel8"); - histos[hEventSel]->GetXaxis()->SetBinLabel(3, "occupancy"); - histos[hEventSel]->GetXaxis()->SetBinLabel(4, "kTVXinTRD"); - histos[hEventSel]->GetXaxis()->SetBinLabel(5, "kNoSameBunchPileup"); - histos[hEventSel]->GetXaxis()->SetBinLabel(6, "kIsGoodZvtxFT0vsPV"); - histos[hEventSel]->GetXaxis()->SetBinLabel(7, "kNoCollInTimeRangeStandard"); - histos[hEventSel]->GetXaxis()->SetBinLabel(8, "kIsVertexITSTPC"); - histos[hEventSel]->GetXaxis()->SetBinLabel(9, "kIsGoodITSLayersAll"); - histos[hEventSel]->GetXaxis()->SetBinLabel(10, "after Mult cuts"); - histos[hEventSel]->GetXaxis()->SetBinLabel(11, "has track + within cent"); + AxisSpec etaAxis = {gfwMemberCache.etabins, cfgKinematics.cfgEta->first, cfgKinematics.cfgEta->second, "#eta"}; + AxisSpec vtxAxis = {gfwMemberCache.vtxZbins, -cfgVtxZ, cfgVtxZ, "Vtx_{z} (cm)"}; + AxisSpec nchAxis = {gfwMemberCache.nchbins, gfwMemberCache.nchlow, gfwMemberCache.nchup, "N_{ch}"}; + AxisSpec centAxis = {gfwMemberCache.centbinning, "Centrality (%)"}; + AxisSpec ptAxis = {gfwMemberCache.ptbinning, "#it{p}_{T} GeV/#it{c}"}; + std::vector> histos(TH1NameCount); + histos[Phi] = registryQA.add(Form("%d/phi", run), "", {HistType::kTH1D, {phiAxis}}); + histos[Eta] = registryQA.add(Form("%d/eta", run), "", {HistType::kTH1D, {etaAxis}}); + histos[VtxZ] = registryQA.add(Form("%d/vtxz", run), "", {HistType::kTH1D, {vtxAxis}}); + histos[Mult] = registryQA.add(Form("%d/mult", run), "", {HistType::kTH1D, {nchAxis}}); + histos[Cent] = registryQA.add(Form("%d/cent", run), "", {HistType::kTH1D, {centAxis}}); + histos[EventSel] = registryQA.add(Form("%d/eventSel", run), "Number of Events;; Counts", {HistType::kTH1D, {{15, 0.5, 15.5}}}); + histos[EventSel]->GetXaxis()->SetBinLabel(FilteredEvent, "Filtered event"); + histos[EventSel]->GetXaxis()->SetBinLabel(Sel8, "sel8"); + histos[EventSel]->GetXaxis()->SetBinLabel(Occupancy, "occupancy"); + histos[EventSel]->GetXaxis()->SetBinLabel(TVXinTRD, "TVXinTRD"); + histos[EventSel]->GetXaxis()->SetBinLabel(NoSameBunchPileup, "NoSameBunchPileup"); + histos[EventSel]->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV, "IsGoodZvtxFT0vsPV"); + histos[EventSel]->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard, "NoCollInTimeRangeStandard"); + histos[EventSel]->GetXaxis()->SetBinLabel(NoCollInRofStandard, "NoCollInRofStandard"); + histos[EventSel]->GetXaxis()->SetBinLabel(NoHighMultCollInPrevRof, "NoHighMultCollInPrevRof"); + histos[EventSel]->GetXaxis()->SetBinLabel(NoTimeFrameBorder, "NoTimeFrameBorder"); + histos[EventSel]->GetXaxis()->SetBinLabel(NoITSROFrameBorder, "NoITSROFrameBorder"); + histos[EventSel]->GetXaxis()->SetBinLabel(IsVertexITSTPC, "IsVertexITSTPC"); + histos[EventSel]->GetXaxis()->SetBinLabel(IsGoodITSLayersAll, "IsGoodITSLayersAll"); + histos[EventSel]->GetXaxis()->SetBinLabel(MultCuts, "after Mult cuts"); + histos[EventSel]->GetXaxis()->SetBinLabel(TrackCent, "has track + within cent"); th1sList.insert(std::make_pair(run, histos)); - std::vector> histos3d(kCount_TH3Names); - if (cfgUsePID) { - histos3d[hNUAref] = registry.add(Form("%d/phi_eta_vtxz_ref", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - histos3d[hNUAch] = registry.add(Form("%d/phi_eta_vtxz_ch", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - histos3d[hNUApi] = registry.add(Form("%d/phi_eta_vtxz_pi", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - histos3d[hNUAka] = registry.add(Form("%d/phi_eta_vtxz_ka", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); - histos3d[hNUApr] = registry.add(Form("%d/phi_eta_vtxz_pr", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + std::vector> histos3d(TH3NameCount); + if (cfgFill.cfgUsePID) { + histos3d[NUAref] = registryQA.add(Form("%d/phi_eta_vtxz_ref", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + histos3d[NUAch] = registryQA.add(Form("%d/phi_eta_vtxz_ch", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + histos3d[NUApi] = registryQA.add(Form("%d/phi_eta_vtxz_pi", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + histos3d[NUAka] = registryQA.add(Form("%d/phi_eta_vtxz_ka", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + histos3d[NUApr] = registryQA.add(Form("%d/phi_eta_vtxz_pr", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); } else { - histos3d[hNUAref] = registry.add(Form("%d/phi_eta_vtxz_ref", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + histos3d[NUAref] = registryQA.add(Form("%d/phi_eta_vtxz_ref", run), "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); } - histos3d[hPtPhiMult] = registry.add(Form("%d/pt_phi_mult", run), "", {HistType::kTH3D, {ptAxis, phiModAxis, (cfgUseNch) ? nchAxis : centAxis}}); + histos3d[PtPhiMult] = registryQA.add(Form("%d/pt_phi_mult", run), "", {HistType::kTH3D, {ptAxis, phiModAxis, (cfgEventSelection.cfgUseNch) ? nchAxis : centAxis}}); th3sList.insert(std::make_pair(run, histos3d)); - return; } struct AcceptedTracks { - explicit AcceptedTracks(std::size_t nptbins) - : pidtotal{0, 0, 0}, - nch(nptbins, 0.f), - npi(nptbins, 0.f), - nka(nptbins, 0.f), - npr(nptbins, 0.f) - { - } - float total = 0; unsigned int totaluncorr = 0; - - std::vector pidtotal; - std::vector nch; - std::vector npi; - std::vector nka; - std::vector npr; }; + using NptHistos = std::vector>; + using NptDenominators = std::array; + + NptDenominators getNptDenominators(const NptHistos& nptHistos) + { + NptDenominators dns = {nptHistos[ChargedID]->Integral(), nptHistos[ChargedID]->Integral(), nptHistos[ChargedID]->Integral(), nptHistos[ChargedID]->Integral()}; + if (cfgFill.cfgUsePIDTotal) { + dns[PionID] = nptHistos[PionID]->Integral(); + dns[KaonID] = nptHistos[KaonID]->Integral(); + dns[ProtonID] = nptHistos[ProtonID]->Integral(); + } + return dns; + } + + template + void fillFractionProfile(const TRecoName& recoName, const TGenName& genName, Args... args) + { + if constexpr (dt == Gen) { + registryGen.fill(genName, args...); + } else { + registry.fill(recoName, args...); + } + } + + template + void fillCombinedFractionProfile(FractionSetup setup, int index, bool resonance, double pt, double centmult, double fraction, double weight) + { + if (!cfgMethod.cfgStoreCombinedFractionWeights || weight <= 0. || + (resonance ? !cfgFill.cfgAnalyseK0Lambda : !cfgFill.cfgAnalyseChargedHadrons)) { + return; + } + if constexpr (dt == Gen) { + if (resonance && index != K0Signal && index != LambdaSignal) { + return; + } + } + if (setup == FractionV02) { + if (resonance) { + switch (index) { + case 0: + fillFractionProfile
(HIST("npt_v02_K0_sb1_w3pc"), HIST("MCGen/npt_v02_K0_sb1_w3pc"), pt, centmult, fraction, weight); + break; + case 1: + fillFractionProfile
(HIST("npt_v02_K0_sig_w3pc"), HIST("MCGen/npt_v02_K0_sig_w3pc"), pt, centmult, fraction, weight); + break; + case 2: + fillFractionProfile
(HIST("npt_v02_K0_sb2_w3pc"), HIST("MCGen/npt_v02_K0_sb2_w3pc"), pt, centmult, fraction, weight); + break; + case 3: + fillFractionProfile
(HIST("npt_v02_Lambda_sb1_w3pc"), HIST("MCGen/npt_v02_Lambda_sb1_w3pc"), pt, centmult, fraction, weight); + break; + case 4: + fillFractionProfile
(HIST("npt_v02_Lambda_sig_w3pc"), HIST("MCGen/npt_v02_Lambda_sig_w3pc"), pt, centmult, fraction, weight); + break; + case 5: + fillFractionProfile
(HIST("npt_v02_Lambda_sb2_w3pc"), HIST("MCGen/npt_v02_Lambda_sb2_w3pc"), pt, centmult, fraction, weight); + break; + default: + return; + } + } else { + switch (index) { + case 0: + fillFractionProfile
(HIST("npt_v02_ch_w3pc"), HIST("MCGen/npt_v02_ch_w3pc"), pt, centmult, fraction, weight); + break; + case 1: + fillFractionProfile
(HIST("npt_v02_pi_w3pc"), HIST("MCGen/npt_v02_pi_w3pc"), pt, centmult, fraction, weight); + break; + case 2: + fillFractionProfile
(HIST("npt_v02_ka_w3pc"), HIST("MCGen/npt_v02_ka_w3pc"), pt, centmult, fraction, weight); + break; + case 3: + fillFractionProfile
(HIST("npt_v02_pr_w3pc"), HIST("MCGen/npt_v02_pr_w3pc"), pt, centmult, fraction, weight); + break; + default: + return; + } + } + } else { + if (resonance) { + switch (index) { + case 0: + fillFractionProfile
(HIST("npt_v0_K0_sb1_w2pc"), HIST("MCGen/npt_v0_K0_sb1_w2pc"), pt, centmult, fraction, weight); + break; + case 1: + fillFractionProfile
(HIST("npt_v0_K0_sig_w2pc"), HIST("MCGen/npt_v0_K0_sig_w2pc"), pt, centmult, fraction, weight); + break; + case 2: + fillFractionProfile
(HIST("npt_v0_K0_sb2_w2pc"), HIST("MCGen/npt_v0_K0_sb2_w2pc"), pt, centmult, fraction, weight); + break; + case 3: + fillFractionProfile
(HIST("npt_v0_Lambda_sb1_w2pc"), HIST("MCGen/npt_v0_Lambda_sb1_w2pc"), pt, centmult, fraction, weight); + break; + case 4: + fillFractionProfile
(HIST("npt_v0_Lambda_sig_w2pc"), HIST("MCGen/npt_v0_Lambda_sig_w2pc"), pt, centmult, fraction, weight); + break; + case 5: + fillFractionProfile
(HIST("npt_v0_Lambda_sb2_w2pc"), HIST("MCGen/npt_v0_Lambda_sb2_w2pc"), pt, centmult, fraction, weight); + break; + default: + return; + } + } else { + switch (index) { + case 0: + fillFractionProfile
(HIST("npt_v0_ch_w2pc"), HIST("MCGen/npt_v0_ch_w2pc"), pt, centmult, fraction, weight); + break; + case 1: + fillFractionProfile
(HIST("npt_v0_pi_w2pc"), HIST("MCGen/npt_v0_pi_w2pc"), pt, centmult, fraction, weight); + break; + case 2: + fillFractionProfile
(HIST("npt_v0_ka_w2pc"), HIST("MCGen/npt_v0_ka_w2pc"), pt, centmult, fraction, weight); + break; + case 3: + fillFractionProfile
(HIST("npt_v0_pr_w2pc"), HIST("MCGen/npt_v0_pr_w2pc"), pt, centmult, fraction, weight); + break; + default: + return; + } + } + } + } + + template + void fillNptRegistry(FractionSetup setup, const float& centmult, const NptHistos& nptHistos, const NptDenominators& dns) + { + if (!cfgFill.cfgAnalyseChargedHadrons || dns[ChargedID] <= 0) { + return; + } + + for (int i = 1; i <= fPtAxis->GetNbins(); ++i) { + if (setup == FractionV02) { + fillFractionProfile
(HIST("npt_v02_ch"), HIST("MCGen/npt_v02_ch"), fPtAxis->GetBinCenter(i), centmult, nptHistos[ChargedID]->GetBinContent(i) / dns[ChargedID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[ChargedID] : 1.0); + if (dns[PionID] > 0) { + fillFractionProfile
(HIST("npt_v02_pi"), HIST("MCGen/npt_v02_pi"), fPtAxis->GetBinCenter(i), centmult, nptHistos[PionID]->GetBinContent(i) / dns[PionID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[PionID] : 1.0); + } + if (dns[KaonID] > 0) { + fillFractionProfile
(HIST("npt_v02_ka"), HIST("MCGen/npt_v02_ka"), fPtAxis->GetBinCenter(i), centmult, nptHistos[KaonID]->GetBinContent(i) / dns[KaonID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[KaonID] : 1.0); + } + if (dns[ProtonID] > 0) { + fillFractionProfile
(HIST("npt_v02_pr"), HIST("MCGen/npt_v02_pr"), fPtAxis->GetBinCenter(i), centmult, nptHistos[ProtonID]->GetBinContent(i) / dns[ProtonID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[ProtonID] : 1.0); + } + } else { + fillFractionProfile
(HIST("npt_v0_ch"), HIST("MCGen/npt_v0_ch"), fPtAxis->GetBinCenter(i), centmult, nptHistos[ChargedID]->GetBinContent(i) / dns[ChargedID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[ChargedID] : 1.0); + if (dns[PionID] > 0) { + fillFractionProfile
(HIST("npt_v0_pi"), HIST("MCGen/npt_v0_pi"), fPtAxis->GetBinCenter(i), centmult, nptHistos[PionID]->GetBinContent(i) / dns[PionID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[PionID] : 1.0); + } + if (dns[KaonID] > 0) { + fillFractionProfile
(HIST("npt_v0_ka"), HIST("MCGen/npt_v0_ka"), fPtAxis->GetBinCenter(i), centmult, nptHistos[KaonID]->GetBinContent(i) / dns[KaonID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[KaonID] : 1.0); + } + if (dns[ProtonID] > 0) { + fillFractionProfile
(HIST("npt_v0_pr"), HIST("MCGen/npt_v0_pr"), fPtAxis->GetBinCenter(i), centmult, nptHistos[ProtonID]->GetBinContent(i) / dns[ProtonID], cfgMethod.cfgUseMultiplicityFractionWeights ? dns[ProtonID] : 1.0); + } + } + } + } + + void fillNptHistos(NptHistos& nptHistos, const float pt, const int pidIndex, const double weightCh, const double weightPid) + { + if (weightCh > 0) { + nptHistos[ChargedID]->Fill(pt, weightCh); + } + if (pidIndex == PionID && weightPid > 0) { + nptHistos[PionID]->Fill(pt, weightPid); + } + if (pidIndex == KaonID && weightPid > 0) { + nptHistos[KaonID]->Fill(pt, weightPid); + } + if (pidIndex == ProtonID && weightPid > 0) { + nptHistos[ProtonID]->Fill(pt, weightPid); + } + } + + void fillNptHistosForEta(const float eta, const float pt, const int pidIndex, const double weightCh, const double weightPid) + { + if (eta > cfgKinematics.cfgEtaNptV02->first && eta < cfgKinematics.cfgEtaNptV02->second) { + fillNptHistos(histosNpt[FractionV02], pt, pidIndex, weightCh, weightPid); + } + if (eta > cfgKinematics.cfgEtaNptV0->first && eta < cfgKinematics.cfgEtaNptV0->second) { + fillNptHistos(histosNpt[FractionV0], pt, pidIndex, weightCh, weightPid); + } + } + template - void fillOutputContainers(const float& centmult, const double& rndm, AcceptedTracks acceptedtracks) + void fillOutputContainers(const float& centmult, const double& rndm) { - fFCpt->calculateCorrelations(); - fFCpt->calculateSubeventCorrelations(); - fFCpt->fillPtProfiles(centmult, rndm); - fFCpt->fillSubeventPtProfiles(centmult, rndm); - fFCpt->fillCMProfiles(centmult, rndm); - fFCpt->fillCMSubeventProfiles(centmult, rndm); - if (!cfgUseGapMethod) - fFCpt->fillVnPtStdProfiles(centmult, rndm); + auto& flowContainer = (dt == Gen) ? fFCgen : fFC; + auto& flowPtContainer = (dt == Gen) ? fFCptGen : fFCpt; + const auto& nptV02 = histosNpt[FractionV02]; + const auto& nptV0 = histosNpt[FractionV0]; + const auto dnsV02 = getNptDenominators(nptV02); + const auto dnsV0 = getNptDenominators(nptV0); + const auto& flowWeightModes = cfgMethod.cfgFlowProfileWeightModes.value; + auto useV02Denominator = [&](uint index) { + return flowWeightModes.empty() ? (cfgMethod.cfgUseMultiplicityFractionWeights && corrconfigs[index].Head.find("3pcW") != std::string::npos) : flowWeightModes[index] == 1; + }; + + flowPtContainer->calculateCorrelations(); + flowPtContainer->calculateSubeventCorrelations(); + flowPtContainer->fillPtProfiles(centmult, rndm); + flowPtContainer->fillSubeventPtProfiles(centmult, rndm); + flowPtContainer->fillCMProfiles(centmult, rndm); + flowPtContainer->fillCMSubeventProfiles(centmult, rndm); + if (!cfgMethod.cfgUseGapMethod) { + flowPtContainer->fillVnPtStdProfiles(centmult, rndm); + } for (uint l_ind = 0; l_ind < corrconfigs.size(); ++l_ind) { if (!corrconfigs.at(l_ind).pTDif) { auto dnx = fGFW->Calculate(corrconfigs.at(l_ind), 0, kTRUE).real(); - if (dnx == 0) + if (dnx == 0) { continue; + } auto val = fGFW->Calculate(corrconfigs.at(l_ind), 0, kFALSE).real() / dnx; if (std::abs(val) < 1) { - (dt == kGen) ? fFCgen->FillProfile(corrconfigs.at(l_ind).Head.c_str(), centmult, val, dnx, rndm) : fFC->FillProfile(corrconfigs.at(l_ind).Head.c_str(), centmult, val, dnx, rndm); - if (cfgUseGapMethod) { - fFCpt->fillVnPtProfiles(centmult, val, dnx, rndm, o2::analysis::gfw::configs.GetpTCorrMasks()[l_ind]); + if (useV02Denominator(l_ind)) { + dnx *= dnsV02[ChargedID]; + } + flowContainer->FillProfile(corrconfigs.at(l_ind).Head.c_str(), centmult, val, cfgMethod.cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); + if (cfgMethod.cfgUseGapMethod) { + flowPtContainer->fillVnPtProfiles(centmult, val, dnx, rndm, gfwMemberCache.configs.GetpTCorrMasks()[l_ind]); } } continue; } for (int i = 1; i <= fPtAxis->GetNbins(); i++) { auto dnx = fGFW->Calculate(corrconfigs.at(l_ind), i - 1, kTRUE).real(); - if (dnx == 0) + if (dnx == 0) { continue; + } auto val = fGFW->Calculate(corrconfigs.at(l_ind), i - 1, kFALSE).real() / dnx; - if (std::abs(val) < 1) - (dt == kGen) ? fFCgen->FillProfile(Form("%s_pt_%i", corrconfigs.at(l_ind).Head.c_str(), i), centmult, val, dnx, rndm) : fFC->FillProfile(Form("%s_pt_%i", corrconfigs.at(l_ind).Head.c_str(), i), centmult, val, dnx, rndm); - } - } - - double chtotal = (cfgUseNchCorrection) ? acceptedtracks.total : acceptedtracks.totaluncorr; - // calculate fractions - std::vector> inputs = {acceptedtracks.nch, acceptedtracks.npi, acceptedtracks.nka, acceptedtracks.npr}; - std::vector> fractions; - fractions.reserve(inputs.size()); - int pidcounter = 0; - for (auto& vec : inputs) { // o2-linter: disable=const-ref-in-for-loop (modified through transform) - fractions.emplace_back(); - fractions.back().reserve(vec.size()); - - double total = chtotal; - if (cfgUsePIDTotal) - total = (pidcounter) ? acceptedtracks.pidtotal[pidcounter - 1] : chtotal; - - if (total == 0.) { - ++pidcounter; - continue; + if (std::abs(val) < 1) { + if (useV02Denominator(l_ind)) { + dnx *= dnsV02[ChargedID]; + } + flowContainer->FillProfile(Form("%s_pt_%i", corrconfigs.at(l_ind).Head.c_str(), i), centmult, val, cfgMethod.cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); + } } - std::transform(vec.begin(), vec.end(), - std::back_inserter(fractions.back()), - [&](double x) { return x / total; }); - ++pidcounter; } - if (cfgUseNewNpt) { - for (int i = 1; i <= fPtAxis->GetNbins(); ++i) { - registry.fill(HIST("npt_ch"), fPtAxis->GetBinCenter(i), centmult, histosNpt[CHARGEDID]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_pi"), fPtAxis->GetBinCenter(i), centmult, histosNpt[PIONID]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_ka"), fPtAxis->GetBinCenter(i), centmult, histosNpt[KAONID]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_pr"), fPtAxis->GetBinCenter(i), centmult, histosNpt[PROTONID]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - } - } else { - for (std::size_t i = 0; i < fractions[0].size(); ++i) - registry.fill(HIST("npt_ch"), fPtAxis->GetBinCenter(i + 1), centmult, fractions[0][i]); - for (std::size_t i = 0; i < fractions[1].size(); ++i) - registry.fill(HIST("npt_pi"), fPtAxis->GetBinCenter(i + 1), centmult, fractions[1][i]); - for (std::size_t i = 0; i < fractions[2].size(); ++i) - registry.fill(HIST("npt_ka"), fPtAxis->GetBinCenter(i + 1), centmult, fractions[2][i]); - for (std::size_t i = 0; i < fractions[3].size(); ++i) - registry.fill(HIST("npt_pr"), fPtAxis->GetBinCenter(i + 1), centmult, fractions[3][i]); - } - if (corrconfigsV02.size() < SPECIESCOUNT) // - return; - - for (uint l_ind = 0; l_ind < SPECIESCOUNT; ++l_ind) { - for (int i = 1; i <= fPtAxis->GetNbins(); i++) { - auto dnx = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kTRUE).real(); - if (dnx == 0) + fillNptRegistry
(FractionV02, centmult, nptV02, dnsV02); + fillNptRegistry
(FractionV0, centmult, nptV0, dnsV0); + if (cfgMethod.cfgStoreCombinedFractionWeights && flowPtContainer->corrDen[1] > 0.) { + constexpr std::array ProfileNames = {"mpt1_w2pc_ch", "mpt1_w2pc_pi", "mpt1_w2pc_ka", "mpt1_w2pc_pr"}; + for (int species = 0; species < SpeciesCount; ++species) { + if (species != ChargedID && !cfgFill.cfgAnalyseChargedHadrons) { continue; - auto val = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kFALSE).real() / dnx; - if (std::abs(val) < 1) - (dt == kGen) ? fFCgen->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centmult, val * ((cfgUseNewNpt) ? histosNpt[l_ind]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions[l_ind][i - 1]), dnx, rndm) : fFC->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centmult, val * ((cfgUseNewNpt) ? histosNpt[l_ind]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions[l_ind][i - 1]), dnx, rndm); + } + flowPtContainer->fillPtProfile(ProfileNames[species], 1, centmult, flowPtContainer->corrDen[1] * dnsV0[species], rndm); } } - if (corrconfigsV0.size() < SPECIESCOUNT) + if (cfgFill.cfgAnalyseChargedHadrons && corrconfigsV02.size() < SpeciesCount) { return; + } - double mpt = 0; - if (cfgEtaPtPt->first * cfgEtaPtPt->second >= 0) { - if (fFCpt->corrDen[1] == 0.) - return; - mpt = fFCpt->corrNum[1] / fFCpt->corrDen[1]; - } else { - if (fFCpt->corrDenSub[0][1] == 0. || fFCpt->corrDenSub[1][1] == 0.) - return; - double mpt_sub1 = fFCpt->corrNumSub[0][1] / fFCpt->corrDenSub[0][1]; - double mpt_sub2 = fFCpt->corrNumSub[1][1] / fFCpt->corrDenSub[1][1]; - mpt = 0.5 * (mpt_sub1 + mpt_sub2); + if (cfgFill.cfgAnalyseChargedHadrons && dnsV02[ChargedID] > 0) { + for (uint l_ind = 0; l_ind < SpeciesCount; ++l_ind) { + for (int i = 1; i <= fPtAxis->GetNbins(); i++) { + auto dnx = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kTRUE).real(); + if (dnx == 0) { + continue; + } + auto val = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kFALSE).real() / dnx; + if (std::abs(val) < 1 && dnsV02[l_ind] > 0) { + fillCombinedFractionProfile
(FractionV02, l_ind, false, fPtAxis->GetBinCenter(i), centmult, nptV02[l_ind]->GetBinContent(i) / dnsV02[l_ind], dnx * dnsV02[l_ind]); + if (cfgMethod.cfgUseMultiplicityFractionWeights) { + dnx *= dnsV02[l_ind]; + } + flowContainer->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centmult, val * nptV02[l_ind]->GetBinContent(i) / dnsV02[l_ind], cfgMethod.cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); + } + } + } + } + + if (!cfgFill.cfgAnalyseChargedHadrons) { + return; + } + if (corrconfigsV0.size() < SpeciesCount) { + return; + } + + if (flowPtContainer->corrDen[1] == 0.) { + return; } - if (std::isnan(mpt)) + double dnx = flowPtContainer->corrDen[1]; + double mpt = flowPtContainer->corrNum[1] / dnx; + if (std::isnan(mpt)) { return; - for (uint l_ind = 0; l_ind < SPECIESCOUNT; ++l_ind) { + } + + for (uint l_ind = 0; l_ind < SpeciesCount; ++l_ind) { for (int i = 1; i <= fPtAxis->GetNbins(); i++) { - (dt == kGen) ? fFCgen->FillProfile(Form("%s_pt_%i", corrconfigsV0.at(l_ind).Head.c_str(), i), centmult, mpt * ((cfgUseNewNpt) ? histosNpt[l_ind]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions[l_ind][i - 1]), 1., rndm) : fFC->FillProfile(Form("%s_pt_%i", corrconfigsV0.at(l_ind).Head.c_str(), i), centmult, mpt * ((cfgUseNewNpt) ? histosNpt[l_ind]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions[l_ind][i - 1]), 1., rndm); + if (dnsV0[l_ind] > 0) { + fillCombinedFractionProfile
(FractionV0, l_ind, false, fPtAxis->GetBinCenter(i), centmult, nptV0[l_ind]->GetBinContent(i) / dnsV0[l_ind], dnx * dnsV0[l_ind]); + double profileWeight = dnx; + if (cfgMethod.cfgUseMultiplicityFractionWeights) { + profileWeight *= dnsV0[l_ind]; + } + flowContainer->FillProfile(Form("%s_pt_%i", corrconfigsV0.at(l_ind).Head.c_str(), i), centmult, mpt * nptV0[l_ind]->GetBinContent(i) / dnsV0[l_ind], cfgMethod.cfgUsePtCorrWeights ? profileWeight : 1.0, rndm); + } + } + } + } + + template + void fillResonanceOutput(FractionSetup setup, const float& centmult, const double& rndm) + { + if (!cfgFill.cfgAnalyseK0Lambda) { + return; + } + auto& flowContainer = (dt == Gen) ? fFCgen : fFC; + const auto& flowPtContainer = (dt == Gen) ? *fFCptGen : *fFCpt; + + if (setup == FractionV02) { + if (histosNpt[FractionV02][ChargedID]->Integral() <= 0) { + return; + } + + double dnK0SB1 = histosNpt[FractionV02][ChargedID]->Integral(); + double dnK0Sig = histosNpt[FractionV02][ChargedID]->Integral(); + double dnK0SB2 = histosNpt[FractionV02][ChargedID]->Integral(); + double dnLambdaSB1 = histosNpt[FractionV02][ChargedID]->Integral(); + double dnLambdaSig = histosNpt[FractionV02][ChargedID]->Integral(); + double dnLambdaSB2 = histosNpt[FractionV02][ChargedID]->Integral(); + + if (cfgFill.cfgUsePIDTotal) { + dnK0SB1 = histosResoNpt[FractionV02][K0Sideband1]->Integral(); + dnK0Sig = histosResoNpt[FractionV02][K0Signal]->Integral(); + dnK0SB2 = histosResoNpt[FractionV02][K0Sideband2]->Integral(); + dnLambdaSB1 = histosResoNpt[FractionV02][LambdaSideband1]->Integral(); + dnLambdaSig = histosResoNpt[FractionV02][LambdaSignal]->Integral(); + dnLambdaSB2 = histosResoNpt[FractionV02][LambdaSideband2]->Integral(); + } + + for (int i = 1; i <= fPtAxis->GetNbins(); ++i) { + if constexpr (dt != Gen) { + if (dnK0SB1 > 0) { + fillFractionProfile
(HIST("npt_v02_K0_sb1"), HIST("MCGen/npt_v02_K0_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Sideband1]->GetBinContent(i) / dnK0SB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB1 : 1.); + } + if (dnK0SB2 > 0) { + fillFractionProfile
(HIST("npt_v02_K0_sb2"), HIST("MCGen/npt_v02_K0_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Sideband2]->GetBinContent(i) / dnK0SB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB2 : 1.); + } + if (dnLambdaSB1 > 0) { + fillFractionProfile
(HIST("npt_v02_Lambda_sb1"), HIST("MCGen/npt_v02_Lambda_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSideband1]->GetBinContent(i) / dnLambdaSB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB1 : 1.); + } + if (dnLambdaSB2 > 0) { + fillFractionProfile
(HIST("npt_v02_Lambda_sb2"), HIST("MCGen/npt_v02_Lambda_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSideband2]->GetBinContent(i) / dnLambdaSB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB2 : 1.); + } + } + if (dnK0Sig > 0) { + fillFractionProfile
(HIST("npt_v02_K0_sig"), HIST("MCGen/npt_v02_K0_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][K0Signal]->GetBinContent(i) / dnK0Sig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0Sig : 1.); + } + if (dnLambdaSig > 0) { + fillFractionProfile
(HIST("npt_v02_Lambda_sig"), HIST("MCGen/npt_v02_Lambda_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][LambdaSignal]->GetBinContent(i) / dnLambdaSig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSig : 1.); + } + } + + std::array dns = {dnK0SB1, dnK0Sig, dnK0SB2, dnLambdaSB1, dnLambdaSig, dnLambdaSB2}; + + const size_t resonanceConfigEnd = std::min(static_cast(SpeciesCount) + static_cast(ResonanceCount), corrconfigsV02.size()); + for (size_t l_ind = SpeciesCount; l_ind < resonanceConfigEnd; ++l_ind) { + const auto resonanceIndex = l_ind - SpeciesCount; + if constexpr (dt == Gen) { + if (resonanceIndex != K0Signal && resonanceIndex != LambdaSignal) { + continue; + } + } + for (int i = 1; i <= fPtAxis->GetNbins(); i++) { + auto dnx = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kTRUE).real(); + if (dnx == 0) { + continue; + } + auto val = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kFALSE).real() / dnx; + if (std::abs(val) < 1 && dns[resonanceIndex] > 0) { + fillCombinedFractionProfile
(FractionV02, resonanceIndex, true, fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV02][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex], dnx * dns[resonanceIndex]); + if (cfgMethod.cfgUseMultiplicityFractionWeights) { + dnx *= dns[resonanceIndex]; + } + flowContainer->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centmult, val * histosResoNpt[FractionV02][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex], cfgMethod.cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); + } + } + } + } else { + if (histosNpt[FractionV0][ChargedID]->Integral() <= 0) { + return; + } + + double dnK0SB1 = histosNpt[FractionV0][ChargedID]->Integral(); + double dnK0Sig = histosNpt[FractionV0][ChargedID]->Integral(); + double dnK0SB2 = histosNpt[FractionV0][ChargedID]->Integral(); + double dnLambdaSB1 = histosNpt[FractionV0][ChargedID]->Integral(); + double dnLambdaSig = histosNpt[FractionV0][ChargedID]->Integral(); + double dnLambdaSB2 = histosNpt[FractionV0][ChargedID]->Integral(); + + if (cfgFill.cfgUsePIDTotal) { + dnK0SB1 = histosResoNpt[FractionV0][K0Sideband1]->Integral(); + dnK0Sig = histosResoNpt[FractionV0][K0Signal]->Integral(); + dnK0SB2 = histosResoNpt[FractionV0][K0Sideband2]->Integral(); + dnLambdaSB1 = histosResoNpt[FractionV0][LambdaSideband1]->Integral(); + dnLambdaSig = histosResoNpt[FractionV0][LambdaSignal]->Integral(); + dnLambdaSB2 = histosResoNpt[FractionV0][LambdaSideband2]->Integral(); + } + + for (int i = 1; i <= fPtAxis->GetNbins(); ++i) { + if constexpr (dt != Gen) { + if (dnK0SB1 > 0) { + fillFractionProfile
(HIST("npt_v0_K0_sb1"), HIST("MCGen/npt_v0_K0_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Sideband1]->GetBinContent(i) / dnK0SB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB1 : 1.); + } + if (dnK0SB2 > 0) { + fillFractionProfile
(HIST("npt_v0_K0_sb2"), HIST("MCGen/npt_v0_K0_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Sideband2]->GetBinContent(i) / dnK0SB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0SB2 : 1.); + } + if (dnLambdaSB1 > 0) { + fillFractionProfile
(HIST("npt_v0_Lambda_sb1"), HIST("MCGen/npt_v0_Lambda_sb1"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSideband1]->GetBinContent(i) / dnLambdaSB1, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB1 : 1.); + } + if (dnLambdaSB2 > 0) { + fillFractionProfile
(HIST("npt_v0_Lambda_sb2"), HIST("MCGen/npt_v0_Lambda_sb2"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSideband2]->GetBinContent(i) / dnLambdaSB2, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSB2 : 1.); + } + } + if (dnK0Sig > 0) { + fillFractionProfile
(HIST("npt_v0_K0_sig"), HIST("MCGen/npt_v0_K0_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][K0Signal]->GetBinContent(i) / dnK0Sig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnK0Sig : 1.); + } + if (dnLambdaSig > 0) { + fillFractionProfile
(HIST("npt_v0_Lambda_sig"), HIST("MCGen/npt_v0_Lambda_sig"), fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][LambdaSignal]->GetBinContent(i) / dnLambdaSig, cfgMethod.cfgUseMultiplicityFractionWeights ? dnLambdaSig : 1.); + } + } + + std::array dns = {dnK0SB1, dnK0Sig, dnK0SB2, dnLambdaSB1, dnLambdaSig, dnLambdaSB2}; + + if (flowPtContainer.corrDenSub[0][1] == 0. || flowPtContainer.corrDenSub[1][1] == 0.) { + return; + } + + double mpt = 0; + double dnx = 0; + if (cfgKinematics.cfgEtaPtPt->first * cfgKinematics.cfgEtaPtPt->second >= 0) { + if (flowPtContainer.corrDen[1] == 0.) { + return; + } + dnx = flowPtContainer.corrDen[1]; + mpt = flowPtContainer.corrNum[1] / dnx; + } else { + if (flowPtContainer.corrDenSub[0][1] == 0. || flowPtContainer.corrDenSub[1][1] == 0.) { + return; + } + double mptSub1 = flowPtContainer.corrNumSub[0][1] / flowPtContainer.corrDenSub[0][1]; + double mptSub2 = flowPtContainer.corrNumSub[1][1] / flowPtContainer.corrDenSub[1][1]; + dnx = 0.5 * (flowPtContainer.corrDenSub[0][1] + flowPtContainer.corrDenSub[1][1]); + mpt = 0.5 * (mptSub1 + mptSub2); + } + + if (std::isnan(mpt)) { + return; + } + + const size_t resonanceConfigEnd = std::min(static_cast(SpeciesCount) + static_cast(ResonanceCount), corrconfigsV0.size()); + for (size_t l_ind = SpeciesCount; l_ind < resonanceConfigEnd; ++l_ind) { + const auto resonanceIndex = l_ind - SpeciesCount; + if constexpr (dt == Gen) { + if (resonanceIndex != K0Signal && resonanceIndex != LambdaSignal) { + continue; + } + } + for (int i = 1; i <= fPtAxis->GetNbins(); i++) { + if (dns[resonanceIndex] <= 0) { + continue; + } + double profileWeight = dnx; + if (cfgMethod.cfgUseMultiplicityFractionWeights) { + profileWeight *= dns[resonanceIndex]; + } + const double value = mpt * histosResoNpt[FractionV0][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex]; + const double weight = cfgMethod.cfgUsePtCorrWeights ? profileWeight : 1.0; + fillCombinedFractionProfile
(FractionV0, resonanceIndex, true, fPtAxis->GetBinCenter(i), centmult, histosResoNpt[FractionV0][resonanceIndex]->GetBinContent(i) / dns[resonanceIndex], flowPtContainer.corrDen[1] * dns[resonanceIndex]); + flowContainer->FillProfile(Form("%s_pt_%i", corrconfigsV0.at(l_ind).Head.c_str(), i), centmult, value, weight, rndm); + } } } - return; } template - void processCollision(TCollision collision, TTracks tracks, TV0s v0s, const float& centrality, const int field, const int run) + void processCollision(const TCollision& collision, const TTracks& tracks, const TV0s& v0s, const float& centrality, const int field, const int run) { - if (tracks.size() < 1) + if (tracks.size() < 1) { return; - if (dt != kGen && (centrality < o2::analysis::gfw::centbinning.front() || centrality > o2::analysis::gfw::centbinning.back())) + } + if (dt != Gen && (centrality < gfwMemberCache.centbinning.front() || centrality > gfwMemberCache.centbinning.back())) { return; - if (dt != kGen) { - registry.fill(HIST("eventQA/eventSel"), kTrackCent); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(kTrackCent); + } + if (dt != Gen) { + registryQA.fill(HIST("eventQA/eventSel"), TrackCent); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(TrackCent); + } } float vtxz = collision.posZ(); - if (dt != kGen && cfgRunByRun) { - th1sList[run][hVtxZ]->Fill(vtxz); - th1sList[run][hMult]->Fill(tracks.size()); - th1sList[run][hCent]->Fill(centrality); + if (dt != Gen && cfgFill.cfgFillRunByRunQA) { + th1sList[run][VtxZ]->Fill(vtxz); + th1sList[run][Mult]->Fill(tracks.size()); + th1sList[run][Cent]->Fill(centrality); } fGFW->Clear(); - fFCpt->clearVector(); + auto& flowPtContainer = (dt == Gen) ? fFCptGen : fFCpt; + flowPtContainer->clearVector(); float lRandom = fRndm->Rndm(); // be cautious, this only works for Pb-Pb // esimate the Event plane and vn for this event DensityCorr densitycorrections; - if (cfgUseDensityDependentCorrection) { + if (cfgEfficiency.cfgUseDensityDependentCorrection) { double psi2Est = 0, psi3Est = 0, psi4Est = 0; double v2 = 0, v3 = 0, v4 = 0; double q2x = 0, q2y = 0; double q3x = 0, q3y = 0; double q4x = 0, q4y = 0; for (const auto& track : tracks) { - bool withinPtRef = (o2::analysis::gfw::ptreflow < track.pt()) && (track.pt() < o2::analysis::gfw::ptrefup); // within RF pT rang + bool withinPtRef = (gfwMemberCache.ptreflow < track.pt()) && (track.pt() < gfwMemberCache.ptrefup); // within RF pT rang if (withinPtRef) { q2x += std::cos(2 * track.phi()); q2y += std::sin(2 * track.phi()); @@ -1445,18 +2235,23 @@ struct FlowGenericFramework { densitycorrections.density = tracks.size(); } // process tracks - AcceptedTracks acceptedTracks(o2::analysis::gfw::ptbinning.size() - 1); + AcceptedTracks acceptedTracks; // Reset fraction histograms per event - for (const auto& h : histosNpt) - h->Reset("ICESM"); + for (const auto& vec : histosNpt) { + for (const auto& h : vec) { + h->Reset("ICESM"); + } + } for (const auto& track : tracks) { - processTrack(track, vtxz, field, run, densitycorrections, acceptedTracks); + processTrack(track, vtxz, field, run, densitycorrections, centrality, acceptedTracks); + } + if (dt != Gen && cfgFill.cfgFillQA) { + registryQA.fill(HIST("trackQA/after/Nch_corrected"), acceptedTracks.total); + registryQA.fill(HIST("trackQA/after/Nch_uncorrected"), acceptedTracks.totaluncorr); } - registry.fill(HIST("trackQA/after/Nch_corrected"), acceptedTracks.total); - registry.fill(HIST("trackQA/after/Nch_uncorrected"), acceptedTracks.totaluncorr); - int multiplicity = 0; - switch (cfgUseNchCorrection) { + float multiplicity = 0.f; + switch (cfgEventSelection.cfgUseNchCorrection) { case 0: multiplicity = tracks.size(); break; @@ -1471,595 +2266,653 @@ struct FlowGenericFramework { break; } - if (cfgFillWeights) + if (cfgFill.cfgFillWeights) { return; + } - fillOutputContainers
((cfgUseNch) ? multiplicity : centrality, lRandom, acceptedTracks); - - // Reset fraction histograms per event - for (const auto& h : histosResoNpt) - h->Reset("ICESM"); + fillOutputContainers
((cfgEventSelection.cfgUseNch) ? multiplicity : centrality, lRandom); - std::vector> nptResonances(6, std::vector(o2::analysis::gfw::ptbinning.size())); - // Process V0s - for (const auto& v0 : v0s) { - if (resoSwitchVals[K0][kUseParticle]) { - double weff = 1; - if (selectK0(collision, v0, centrality, weff)) { - int ptBinIndex = fPtAxis->FindBin(v0.pt()) - 1; - if (!(ptBinIndex < 0 || ptBinIndex >= static_cast(o2::analysis::gfw::ptbinning.size()))) { - if (v0.mK0Short() > cfgPIDCuts.cfgK0SideBand1Min && v0.mK0Short() < cfgPIDCuts.cfgK0SideBand1Max) { - nptResonances[0][ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosResoNpt[K0SIDEBAND1]->Fill(v0.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - if (v0.mK0Short() > cfgPIDCuts.cfgK0SignalMin && v0.mK0Short() < cfgPIDCuts.cfgK0SignalMax) { - nptResonances[1][ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosResoNpt[K0SIGNAL]->Fill(v0.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - if (v0.mK0Short() > cfgPIDCuts.cfgK0SideBand2Min && v0.mK0Short() < cfgPIDCuts.cfgK0SideBand2Max) { - nptResonances[2][ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosResoNpt[K0SIDEBAND2]->Fill(v0.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - } - } - } - // Add lambdabar - if (resoSwitchVals[LAMBDA][kUseParticle]) { - double weff = 1.; - if (selectLambda(collision, v0, centrality, weff)) { - int ptBinIndex = fPtAxis->FindBin(v0.pt()) - 1; - if (!(ptBinIndex < 0 || ptBinIndex >= static_cast(o2::analysis::gfw::ptbinning.size()))) { - if (v0.mLambda() > cfgPIDCuts.cfgLambdaSideBand1Min && v0.mLambda() < cfgPIDCuts.cfgLambdaSideBand1Max) { - nptResonances[3][ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosResoNpt[LAMBDASIDEBAND1]->Fill(v0.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - if (v0.mLambda() > cfgPIDCuts.cfgLambdaSignalMin && v0.mLambda() < cfgPIDCuts.cfgLambdaSignalMax) { - nptResonances[4][ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosResoNpt[LAMBDASIGNAL]->Fill(v0.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - if (v0.mLambda() > cfgPIDCuts.cfgLambdaSideBand2Min && v0.mLambda() < cfgPIDCuts.cfgLambdaSideBand2Max) { - nptResonances[5][ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosResoNpt[LAMBDASIDEBAND2]->Fill(v0.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - } + if (cfgFill.cfgAnalyseK0Lambda) { + // Reset fraction histograms per event + for (const auto& vec : histosResoNpt) { + for (const auto& h : vec) { + h->Reset("ICESM"); } } - } - double chtotal = (cfgUseNchCorrection) ? acceptedTracks.total : acceptedTracks.totaluncorr; - // calculate fractions - std::vector> fractions_resonances = nptResonances; - int pidcounter = 0; - for (auto& vec : fractions_resonances) { // o2-linter: disable=const-ref-in-for-loop (modified through transform) - double total = chtotal; - if (cfgUsePIDTotal) - total = (pidcounter) ? std::accumulate(vec.begin(), vec.end(), 0.f) : chtotal; - - if (total == 0.) { - ++pidcounter; - continue; - } - std::transform(vec.begin(), vec.end(), vec.begin(), - [&](float x) { return x / total; }); - ++pidcounter; - } - if (cfgUseNewNpt) { - for (int i = 1; i <= fPtAxis->GetNbins(); ++i) { - if (histosNpt[CHARGEDID]->Integral() <= 0) - continue; - registry.fill(HIST("npt_K0_sb1"), fPtAxis->GetBinCenter(i), centrality, histosResoNpt[K0SIDEBAND1]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_K0_sig"), fPtAxis->GetBinCenter(i), centrality, histosResoNpt[K0SIGNAL]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_K0_sb2"), fPtAxis->GetBinCenter(i), centrality, histosResoNpt[K0SIDEBAND2]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_Lambda_sb1"), fPtAxis->GetBinCenter(i), centrality, histosResoNpt[LAMBDASIDEBAND1]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_Lambda_sig"), fPtAxis->GetBinCenter(i), centrality, histosResoNpt[LAMBDASIGNAL]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - registry.fill(HIST("npt_Lambda_sb2"), fPtAxis->GetBinCenter(i), centrality, histosResoNpt[LAMBDASIDEBAND2]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral()); - } - } else { - for (std::size_t i = 0; i < fractions_resonances[0].size(); ++i) - registry.fill(HIST("npt_K0_sb1"), fPtAxis->GetBinCenter(i + 1), centrality, fractions_resonances[0][i]); - for (std::size_t i = 0; i < fractions_resonances[2].size(); ++i) - registry.fill(HIST("npt_K0_sb2"), fPtAxis->GetBinCenter(i + 1), centrality, fractions_resonances[2][i]); - for (std::size_t i = 0; i < fractions_resonances[1].size(); ++i) - registry.fill(HIST("npt_K0_sig"), fPtAxis->GetBinCenter(i + 1), centrality, fractions_resonances[1][i]); - for (std::size_t i = 0; i < fractions_resonances[3].size(); ++i) - registry.fill(HIST("npt_Lambda_sb1"), fPtAxis->GetBinCenter(i + 1), centrality, fractions_resonances[3][i]); - for (std::size_t i = 0; i < fractions_resonances[5].size(); ++i) - registry.fill(HIST("npt_Lambda_sb2"), fPtAxis->GetBinCenter(i + 1), centrality, fractions_resonances[5][i]); - for (std::size_t i = 0; i < fractions_resonances[4].size(); ++i) - registry.fill(HIST("npt_Lambda_sig"), fPtAxis->GetBinCenter(i + 1), centrality, fractions_resonances[4][i]); - } - for (uint l_ind = 4; l_ind < corrconfigsV02.size(); ++l_ind) { - if (histosNpt[CHARGEDID]->Integral() <= 0 && cfgUseNewNpt) - continue; - for (int i = 1; i <= fPtAxis->GetNbins(); i++) { - auto dnx = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kTRUE).real(); - if (dnx == 0) - continue; - auto val = fGFW->Calculate(corrconfigsV02.at(l_ind), i - 1, kFALSE).real() / dnx; - if (std::abs(val) < 1) - (dt == kGen) ? fFCgen->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centrality, val * ((cfgUseNewNpt) ? histosResoNpt[l_ind - 4]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions_resonances[l_ind - 4][i - 1]), dnx, lRandom) : fFC->FillProfile(Form("%s_pt_%i", corrconfigsV02.at(l_ind).Head.c_str(), i), centrality, val * ((cfgUseNewNpt) ? histosResoNpt[l_ind - 4]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions_resonances[l_ind - 4][i - 1]), dnx, lRandom); + // Process V0s only for reconstructed-track workflows. + if constexpr (dt != Gen) { + for (const auto& v0 : v0s) { + processV0(v0, tracks, collision, centrality); + } + } else { + for (const auto& particle : tracks) { + processV0(particle, tracks, collision, centrality); + } } - } - - if (fFCpt->corrDenSub[0][1] == 0. || fFCpt->corrDenSub[1][1] == 0.) - return; - double mpt_sub1 = fFCpt->corrNumSub[0][1] / fFCpt->corrDenSub[0][1]; - double mpt_sub2 = fFCpt->corrNumSub[1][1] / fFCpt->corrDenSub[1][1]; - double mpt = 0.5 * (mpt_sub1 + mpt_sub2); - if (std::isnan(mpt)) - return; - - for (uint l_ind = 4; l_ind < corrconfigsV0.size(); ++l_ind) { - if (histosNpt[CHARGEDID]->Integral() <= 0 && cfgUseNewNpt) - continue; - for (int i = 1; i <= fPtAxis->GetNbins(); i++) { - (dt == kGen) ? fFCgen->FillProfile(Form("%s_pt_%i", corrconfigsV0.at(l_ind).Head.c_str(), i), centrality, mpt * ((cfgUseNewNpt) ? histosResoNpt[l_ind - 4]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions_resonances[l_ind - 4][i - 1]), 1.0, lRandom) : fFC->FillProfile(Form("%s_pt_%i", corrconfigsV0.at(l_ind).Head.c_str(), i), centrality, mpt * ((cfgUseNewNpt) ? histosResoNpt[l_ind - 4]->GetBinContent(i) / histosNpt[CHARGEDID]->Integral() : fractions_resonances[l_ind - 4][i - 1]), 1.0, lRandom); + for (auto setup = 0; setup < FractionSetupCount; ++setup) { + auto fractionSetup = static_cast(setup); + fillResonanceOutput
(fractionSetup, (cfgEventSelection.cfgUseNch) ? multiplicity : centrality, lRandom); } } } template - inline void processTrack(TTrack const& track, const float& vtxz, const int field, const int run, DensityCorr densitycorrections, AcceptedTracks& acceptedTracks) + inline void processTrack(const TTrack& track, const float& vtxz, const int field, const int run, const DensityCorr& densitycorrections, const float& centrality, AcceptedTracks& acceptedTracks) { if constexpr (framework::has_type_v) { - if (track.mcParticleId() < 0 || !(track.has_mcParticle())) + if (track.mcParticleId() < 0 || !(track.has_mcParticle())) { return; - + } auto mcParticle = track.mcParticle(); - if (!mcParticle.isPhysicalPrimary()) + if (!mcParticle.isPhysicalPrimary()) { return; - if (cfgFillQA) - fillTrackQA(track, vtxz); - - if (mcParticle.eta() < o2::analysis::gfw::etalow || mcParticle.eta() > o2::analysis::gfw::etaup || mcParticle.pt() < o2::analysis::gfw::ptlow || mcParticle.pt() > o2::analysis::gfw::ptup) + } + if (cfgFill.cfgFillQA) { + fillTrackQA(track, vtxz); + } + if (mcParticle.eta() < gfwMemberCache.etalow || mcParticle.eta() > gfwMemberCache.etaup || mcParticle.pt() < gfwMemberCache.ptlow || mcParticle.pt() > gfwMemberCache.ptup) { return; - + } // Select tracks with nominal cuts always - if (!nchSelected(track)) + if (!nchSelected(track)) { return; - - double weffCh = getEfficiency(track, 0); - if (track.eta() > cfgEtaNch->first && track.eta() < cfgEtaNch->second) { - if (weffCh > 0) - acceptedTracks.total += (cfgUseNchCorrection) ? weffCh : 1.0; + } + double weffCh = getEfficiency(track, centrality, 0); + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { + if (weffCh > 0) { + acceptedTracks.total += (cfgEventSelection.cfgUseNchCorrection) ? weffCh : 1.0; + } ++acceptedTracks.totaluncorr; } - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { return; + } int pidIndex = 0; - if (cfgUsePID) { - if (std::abs(mcParticle.pdgCode()) == kPiPlus) - pidIndex = PIONID; - if (std::abs(mcParticle.pdgCode()) == kKPlus) - pidIndex = KAONID; - if (std::abs(mcParticle.pdgCode()) == kProton) - pidIndex = PROTONID; + if (std::abs(mcParticle.pdgCode()) == kPiPlus) { + pidIndex = PionID; } - - if (track.eta() > cfgEtaNch->first && track.eta() < cfgEtaNch->second) { - double weff = getEfficiency(track, pidIndex); - - if (pidIndex && weff > 0) - acceptedTracks.pidtotal[pidIndex - 1] += weff; - - int ptBinIndex = fPtAxis->FindBin(track.pt()) - 1; - if (!(ptBinIndex < 0 || ptBinIndex >= static_cast(o2::analysis::gfw::ptbinning.size()))) { - if (weffCh > 0) { - acceptedTracks.nch[ptBinIndex] += (cfgUseNchCorrection) ? weffCh : 1.0; - histosNpt[CHARGEDID]->Fill(track.pt(), (cfgUseNchCorrection) ? weffCh : 1.0); - } - if (pidIndex == PIONID && weff > 0) { - acceptedTracks.npi[ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosNpt[PIONID]->Fill(track.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - if (pidIndex == KAONID && weff > 0) { - acceptedTracks.nka[ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosNpt[KAONID]->Fill(track.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - if (pidIndex == PROTONID && weff > 0) { - acceptedTracks.npr[ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosNpt[PROTONID]->Fill(track.pt(), (cfgUseNchCorrection) ? weff : 1.0); - } - } + if (std::abs(mcParticle.pdgCode()) == kKPlus) { + pidIndex = KaonID; } - - if (cfgFillWeights) { + if (std::abs(mcParticle.pdgCode()) == kProton) { + pidIndex = ProtonID; + } + double weff = getEfficiency(track, centrality, pidIndex); + double nptWeightCh = (weffCh > 0) ? ((cfgEventSelection.cfgUseNchCorrection) ? weffCh : 1.0) : -1.0; + double nptWeightPid = (weff > 0) ? ((cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0) : -1.0; + fillNptHistosForEta(track.eta(), track.pt(), pidIndex, nptWeightCh, nptWeightPid); + if (cfgFill.cfgFillWeights) { fillWeights(mcParticle, vtxz, 0, run); } else { - fillPtSums(track, vtxz); - fillGFW(mcParticle, vtxz, pidIndex, densitycorrections); + fillPtSums(track, centrality, vtxz); + fillGFW(mcParticle, centrality, vtxz, pidIndex, densitycorrections); } - - if (cfgFillQA) { - fillTrackQA(track, vtxz); - if (cfgRunByRun) { - th1sList[run][hPhi]->Fill(track.phi()); - th1sList[run][hEta]->Fill(track.eta()); + if (cfgFill.cfgFillQA) { + fillTrackQA(track, vtxz); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][Phi]->Fill(track.phi()); + th1sList[run][Eta]->Fill(track.eta()); } } } else if constexpr (framework::has_type_v) { - if (!track.isPhysicalPrimary()) + if (!track.isPhysicalPrimary()) { return; - if (cfgFillQA) - fillTrackQA(track, vtxz); - - if (track.eta() < o2::analysis::gfw::etalow || track.eta() > o2::analysis::gfw::etaup || track.pt() < o2::analysis::gfw::ptlow || track.pt() > o2::analysis::gfw::ptup) + } + auto pdgParticle = pdgDB->GetParticle(track.pdgCode()); + if (!pdgParticle || pdgParticle->Charge() == 0.) { return; + } + if (cfgFill.cfgFillQA) { + fillTrackQA(track, vtxz); + } + if (track.eta() < gfwMemberCache.etalow || track.eta() > gfwMemberCache.etaup || track.pt() < gfwMemberCache.ptlow || track.pt() > gfwMemberCache.ptup) { + return; + } int pidIndex = 0; - if (cfgUsePID) { - if (std::abs(track.pdgCode()) == kPiPlus) - pidIndex = 1; - if (std::abs(track.pdgCode()) == kKPlus) - pidIndex = 2; - if (std::abs(track.pdgCode()) == kProton) - pidIndex = 3; + if (std::abs(track.pdgCode()) == kPiPlus) { + pidIndex = PionID; + } + if (std::abs(track.pdgCode()) == kKPlus) { + pidIndex = KaonID; + } + if (std::abs(track.pdgCode()) == kProton) { + pidIndex = ProtonID; } - if (track.eta() > cfgEtaNch->first && track.eta() < cfgEtaNch->second) { + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { ++acceptedTracks.total; ++acceptedTracks.totaluncorr; - - if (pidIndex) - acceptedTracks.pidtotal[pidIndex - 1] += 1; - int ptBinIndex = fPtAxis->FindBin(track.pt()) - 1; - - if (!(ptBinIndex < 0 || ptBinIndex >= static_cast(o2::analysis::gfw::ptbinning.size()))) { - acceptedTracks.nch[ptBinIndex] += 1.0; - histosNpt[CHARGEDID]->Fill(track.pt()); - if (pidIndex == PIONID) { - acceptedTracks.npi[ptBinIndex] += 1.0; - histosNpt[PIONID]->Fill(track.pt()); - } - if (pidIndex == KAONID) { - acceptedTracks.nka[ptBinIndex] += 1.0; - histosNpt[KAONID]->Fill(track.pt()); - } - if (pidIndex == PROTONID) { - acceptedTracks.npr[ptBinIndex] += 1.0; - histosNpt[PROTONID]->Fill(track.pt()); - } - } } + fillNptHistosForEta(track.eta(), track.pt(), pidIndex, 1.0, 1.0); - fillPtSums(track, vtxz); - fillGFW(track, vtxz, pidIndex, densitycorrections); + fillPtSums(track, centrality, vtxz); + fillGFW(track, centrality, vtxz, pidIndex, densitycorrections); + if (cfgFill.cfgFillQA) { + fillTrackQA(track, vtxz); + } - if (cfgFillQA) - fillTrackQA(track, vtxz); } else { - if (cfgFillQA) - fillTrackQA(track, vtxz); + if (cfgFill.cfgFillQA) { + fillTrackQA(track, vtxz); + } // Select tracks with nominal cuts always - if (!nchSelected(track)) + if (!nchSelected(track)) { return; - - double weffCh = getEfficiency(track, 0); - if (track.eta() > cfgEtaNch->first && track.eta() < cfgEtaNch->second) { - if (weffCh > 0) - acceptedTracks.total += (cfgUseNchCorrection) ? weffCh : 1.0; + } + double weffCh = getEfficiency(track, centrality, 0); + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { + if (weffCh > 0) { + acceptedTracks.total += (cfgEventSelection.cfgUseNchCorrection) ? weffCh : 1.0; + } ++acceptedTracks.totaluncorr; } - - if (!trackSelected(track, field)) + if (!trackSelected(track, field)) { return; + } // int pidIndex = 0; - // if (cfgUsePID) Need PID for v02 + // if (cfgFill.cfgUsePID) Need PID for v02 int pidIndex = getNsigmaPID(track); - if (track.eta() > cfgEtaNch->first && track.eta() < cfgEtaNch->second) { - double weff = getEfficiency(track, pidIndex); - if (pidIndex && weff > 0) - acceptedTracks.pidtotal[pidIndex - 1] += weff; + double weff = getEfficiency(track, centrality, pidIndex); + double nptWeightCh = (weffCh > 0) ? ((cfgEventSelection.cfgUseNchCorrection) ? weffCh : 1.0) : -1.0; + double nptWeightPid = (weff > 0) ? ((cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0) : -1.0; + fillNptHistosForEta(track.eta(), track.pt(), pidIndex, nptWeightCh, nptWeightPid); + if (cfgFill.cfgFillWeights) { + fillWeights(track, vtxz, pidIndex, run); + } else { + fillPtSums(track, centrality, vtxz); + fillGFW(track, centrality, vtxz, pidIndex, densitycorrections); + } + if (cfgFill.cfgFillQA) { + fillTrackQA(track, vtxz); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][Phi]->Fill(track.phi()); + th1sList[run][Eta]->Fill(track.eta()); + } + } + } + } - int ptBinIndex = fPtAxis->FindBin(track.pt()) - 1; + template + inline void processV0(const TV0& v0, const TTracks& tracks, const TCollision& collision, const float& centrality) + { + if constexpr (dt == Reco) { + using V0TrackTable = std::decay_t; + auto postrack = v0.template posTrack_as(); + auto negtrack = v0.template negTrack_as(); - if (!(ptBinIndex < 0 || ptBinIndex >= static_cast(o2::analysis::gfw::ptbinning.size()))) { - if (weffCh > 0) { - acceptedTracks.nch[ptBinIndex] += (cfgUseNchCorrection) ? weffCh : 1.0; - histosNpt[CHARGEDID]->Fill(track.pt(), (cfgUseNchCorrection) ? weffCh : 1.0); + if (resoSwitchVals[UseParticle][K0] != 0) { + double weff = 1; + bool fillK0 = true; + if (cfgEfficiency.cfgUsePIDEfficiencies) { + weff = getEfficiency(v0, centrality, 4); + if (weff < 0) { + fillK0 = false; } - if (pidIndex == PIONID && weff > 0) { - acceptedTracks.npi[ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosNpt[PIONID]->Fill(track.pt(), (cfgUseNchCorrection) ? weff : 1.0); + } + if (fillK0) { + auto k0Selection = selectK0(collision, v0, tracks); + if (k0Selection.selected) { + for (auto setup = 0; setup < FractionSetupCount; ++setup) { + auto fractionSetup = static_cast(setup); + if (!selectionV0DaughterEta(postrack, fractionSetup) || !selectionV0DaughterEta(negtrack, fractionSetup)) { + continue; + } + registryQA.fill(HIST("K0/hK0Count"), (fractionSetup == FractionV02) ? FillV02DaughterTrackSelection : FillV0DaughterTrackSelection); + + if (cfgFill.cfgFillV0QA && fractionSetup == FractionV02) { + fillV0QA(k0Selection, v0, postrack, negtrack, centrality, weff); + } + + if (v0.mK0Short() > cfgPIDCuts.cfgK0SideBand1Min && v0.mK0Short() < cfgPIDCuts.cfgK0SideBand1Max) { + histosResoNpt[fractionSetup][K0Sideband1]->Fill(v0.pt(), (cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0); + } + if (v0.mK0Short() > cfgPIDCuts.cfgK0SignalMin && v0.mK0Short() < cfgPIDCuts.cfgK0SignalMax) { + histosResoNpt[fractionSetup][K0Signal]->Fill(v0.pt(), (cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0); + } + if (v0.mK0Short() > cfgPIDCuts.cfgK0SideBand2Min && v0.mK0Short() < cfgPIDCuts.cfgK0SideBand2Max) { + histosResoNpt[fractionSetup][K0Sideband2]->Fill(v0.pt(), (cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0); + } + } } - if (pidIndex == KAONID && weff > 0) { - acceptedTracks.nka[ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosNpt[KAONID]->Fill(track.pt(), (cfgUseNchCorrection) ? weff : 1.0); + } + } + if (resoSwitchVals[UseParticle][Lambda] != 0) { + double weff = 1.; + bool fillLambda = true; + if (cfgEfficiency.cfgUsePIDEfficiencies) { + weff = getEfficiency(v0, centrality, 5); + if (weff < 0) { + fillLambda = false; } - if (pidIndex == PROTONID && weff > 0) { - acceptedTracks.npr[ptBinIndex] += (cfgUseNchCorrection) ? weff : 1.0; - histosNpt[PROTONID]->Fill(track.pt(), (cfgUseNchCorrection) ? weff : 1.0); + } + if (fillLambda) { + auto lambdaSelection = selectLambda(collision, v0, tracks); + if (lambdaSelection.selected) { + for (auto setup = 0; setup < FractionSetupCount; ++setup) { + auto fractionSetup = static_cast(setup); + if (!selectionLambdaDaughterEta(postrack, negtrack, lambdaSelection, fractionSetup)) { + continue; + } + registryQA.fill(HIST("Lambda/hLambdaCount"), (fractionSetup == FractionV02) ? FillV02DaughterTrackSelection : FillV0DaughterTrackSelection); + + if (cfgFill.cfgFillV0QA && fractionSetup == FractionV02) { + fillV0QA(lambdaSelection, v0, postrack, negtrack, centrality, weff); + } + const double lambdaMass = lambdaSelection.isL ? v0.mLambda() : v0.mAntiLambda(); + if (lambdaMass > cfgPIDCuts.cfgLambdaSideBand1Min && lambdaMass < cfgPIDCuts.cfgLambdaSideBand1Max) { + histosResoNpt[fractionSetup][LambdaSideband1]->Fill(v0.pt(), (cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0); + } + if (v0.mLambda() > cfgPIDCuts.cfgLambdaSignalMin && v0.mLambda() < cfgPIDCuts.cfgLambdaSignalMax) { + histosResoNpt[fractionSetup][LambdaSignal]->Fill(v0.pt(), (cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0); + } + if (v0.mLambda() > cfgPIDCuts.cfgLambdaSideBand2Min && v0.mLambda() < cfgPIDCuts.cfgLambdaSideBand2Max) { + histosResoNpt[fractionSetup][LambdaSideband2]->Fill(v0.pt(), (cfgEventSelection.cfgUseNchCorrection) ? weff : 1.0); + } + } } } } + } else { + if (!v0.isPhysicalPrimary()) { + return; + } + if (!v0.has_daughters()) { + return; + } + + bool isK0 = (v0.pdgCode() == PDG_t::kK0Short); + bool isLambda = (v0.pdgCode() == PDG_t::kLambda0); + + if (!isK0 && !isLambda) { + return; + } + + // To match reconstructed - check that daughters are within v02/v0 eta acceptance + bool isWithinV0Acceptance = true; + bool isWithinV02Acceptance = true; + for (const auto& d : v0.template daughters_as()) { + + if (d.eta() < cfgKinematics.cfgEtaNptV02->first || d.eta() > cfgKinematics.cfgEtaNptV02->second) { + isWithinV02Acceptance = false; + } + if (d.eta() < cfgKinematics.cfgEtaNptV0->first || d.eta() > cfgKinematics.cfgEtaNptV0->second) { + isWithinV0Acceptance = false; + } + } - if (cfgFillWeights) { - fillWeights(track, vtxz, pidIndex, run); - } else { - fillPtSums(track, vtxz); - fillGFW(track, vtxz, pidIndex, densitycorrections); + if (isK0) { + if (isWithinV02Acceptance) { + histosResoNpt[FractionV02][K0Signal]->Fill(v0.pt(), 1.0); + } + if (isWithinV0Acceptance) { + histosResoNpt[FractionV0][K0Signal]->Fill(v0.pt(), 1.0); + } } - if (cfgFillQA) { - fillTrackQA(track, vtxz); - if (cfgRunByRun) { - th1sList[run][hPhi]->Fill(track.phi()); - th1sList[run][hEta]->Fill(track.eta()); + if (isLambda) { + if (isWithinV02Acceptance) { + histosResoNpt[FractionV02][LambdaSignal]->Fill(v0.pt(), 1.0); + } + if (isWithinV0Acceptance) { + histosResoNpt[FractionV0][LambdaSignal]->Fill(v0.pt(), 1.0); } } } } + std::pair getFractionEtaRange(FractionSetup setup) + { + return (setup == FractionV02) ? std::make_pair(cfgKinematics.cfgEtaNptV02->first, cfgKinematics.cfgEtaNptV02->second) : std::make_pair(cfgKinematics.cfgEtaNptV0->first, cfgKinematics.cfgEtaNptV0->second); + } + + struct V0Selection { + bool selected = false; + bool isK0 = false; + bool isL = false; + bool isAL = false; + }; + template - bool selectionV0Daughter(TTrack const& track, int pid) + bool selectionV0Daughter(const TTrack& track, int pid) { - if (!(track.itsNCls() > cfgTrackCuts.cfgMinNITSCls)) - return 0; - if (!track.hasTPC()) + if (track.tpcNClsCrossedRows() < cfgTrackCuts.cfgNTPCXrowsDaughters) { return false; - if (track.tpcNClsFound() < cfgTrackCuts.cfgNTPCCls) - return false; - if (!(track.tpcNClsCrossedRows() > cfgTrackCuts.cfgNTPCXrows)) - return 0; - - if (cfgPIDCuts.cfgUseOnlyTPC) { - if (pid == PIONS && std::abs(track.tpcNSigmaPi()) > cfgPIDCuts.cfgTPCNsigmaCut) - return false; - if (pid == KAONS && std::abs(track.tpcNSigmaKa()) > cfgPIDCuts.cfgTPCNsigmaCut) - return false; - if (pid == PROTONS && std::abs(track.tpcNSigmaPr()) > cfgPIDCuts.cfgTPCNsigmaCut) + } + // Only accept daughters consistent with the expected identities of K0 or Lambda decay. + if (cfgPIDCuts.cfgDaughterPIDRejection) { + if (cfgPIDCuts.cfgUseOnlyTPC) { + if (pid == Pions) { + return std::abs(track.tpcNSigmaPi()) <= cfgPIDCuts.cfgTPCNsigmaCut; + } + if (pid == Protons) { + return std::abs(track.tpcNSigmaPr()) <= cfgPIDCuts.cfgTPCNsigmaCut; + } return false; - } else { + } int partIndex = cfgPIDCuts.cfgUseAsymmetricPID ? getNsigmaPIDAssymmetric(track) : getNsigmaPID(track); int pidIndex = partIndex - 1; // 0 = pion, 1 = kaon, 2 = proton - if (pidIndex != pid) + if (pid != Pions && pid != Protons) { + return false; + } + if (pidIndex != pid) { return false; + } } + return true; + } - // Eta cuts on daughter particles to remove self-correlations with correlated observables - if (track.eta() < cfgEtaV0Daughters->first || track.eta() > cfgEtaV0Daughters->second) + template + bool selectionV0DaughterEta(const TTrack& track, FractionSetup setup) + { + const auto [etaMin, etaMax] = getFractionEtaRange(setup); + if (track.eta() < etaMin || track.eta() > etaMax) { return false; - registry.fill(HIST("trackQA/after/etaV0Daughters"), track.eta()); + } + if (cfgFill.cfgFillV0QA) { + if (setup == FractionV02) { + registryQA.fill(HIST("trackQA/after/etaV02"), track.eta()); + } + if (setup == FractionV0) { + registryQA.fill(HIST("trackQA/after/etaV0"), track.eta()); + } + } return true; } - template - bool selectK0(TCollision const& collision, TV0 const& v0, const double& centrality, double& weff) + template + bool selectionLambdaDaughterEta(const TTrack& postrack, const TTrack& negtrack, const V0Selection& lambdaSelection, FractionSetup setup) { - - double massK0s = v0.mK0Short(); - - auto postrack = v0.template posTrack_as(); - auto negtrack = v0.template negTrack_as(); - - registry.fill(HIST("K0/hK0Count"), kFillCandidate); - if (postrack.pt() < resoCutVals[K0][kPosTrackPt] || negtrack.pt() < resoCutVals[K0][kNegTrackPt]) - return false; - registry.fill(HIST("K0/hK0Count"), kFillDaughterPt); - if (massK0s < resoCutVals[K0][kMassMin] && massK0s > resoCutVals[K0][kMassMax]) - return false; - registry.fill(HIST("K0/hK0Count"), kFillMassCut); - // Rapidity correction - if (v0.yK0Short() > resoCutVals[K0][kRapidity]) - return false; - registry.fill(HIST("K0/hK0Count"), kFillRapidityCut); - // DCA cuts for K0short - if (std::abs(v0.dcapostopv()) < resoCutVals[K0][kDCAPosToPVMin] || std::abs(v0.dcanegtopv()) < resoCutVals[K0][kDCANegToPVMin]) - return false; - registry.fill(HIST("K0/hK0Count"), kFillDCAtoPV); - if (std::abs(v0.dcaV0daughters()) > resoSwitchVals[K0][kDCABetDaug]) - return false; - registry.fill(HIST("K0/hK0Count"), kFillDCAxDaughters); - // v0 radius cuts - if (resoSwitchVals[K0][kUseV0Radius] && (v0.v0radius() < resoCutVals[K0][kRadiusMin] || v0.v0radius() > resoCutVals[K0][kRadiusMax])) - return false; - registry.fill(HIST("K0/hK0Count"), kFillV0Radius); - // cosine pointing angle cuts - if (v0.v0cosPA() < resoCutVals[K0][kCosPA]) + if (lambdaSelection.isL && (!selectionV0DaughterEta(postrack, setup) || !selectionV0DaughterEta(negtrack, setup))) { return false; - registry.fill(HIST("K0/hK0Count"), kFillCosPA); - // Proper lifetime - if (resoSwitchVals[K0][kUseProperLifetime] && v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massK0Short > resoCutVals[K0][kLifeTime]) - return false; - registry.fill(HIST("K0/hK0Count"), kFillProperLifetime); - if (!selectionV0Daughter(postrack, PIONS) || !selectionV0Daughter(negtrack, PIONS)) + } + if (lambdaSelection.isAL && (!selectionV0DaughterEta(postrack, setup) || !selectionV0DaughterEta(negtrack, setup))) { return false; - registry.fill(HIST("K0/hK0Count"), kFillDaughterTrackSelection); - - registry.fill(HIST("K0/hK0Mass_sparse"), massK0s, v0.pt(), centrality); - registry.fill(HIST("K0/hK0Phi"), v0.phi()); - registry.fill(HIST("K0/hK0Eta"), v0.eta()); - registry.fill(HIST("K0/PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaKa()); - registry.fill(HIST("K0/PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaKa()); - registry.fill(HIST("K0/PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaKa()); - registry.fill(HIST("K0/PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaKa()); - - registry.fill(HIST("K0/hK0s"), 0.5, 1); - if (cfgUsePIDEfficiencies) { - double weffDaughter1 = getEfficiency(postrack, 1); - double weffDaughter2 = getEfficiency(negtrack, 1); - weff = weffDaughter1 * weffDaughter2; - if (weff > 0) - registry.fill(HIST("K0/hK0s_corrected"), 0.5, weff); } - return true; } - template - bool selectLambda(TCollision const& collision, TV0 const& v0, const double& centrality, double& weff) + template + V0Selection selectK0(const TCollision& collision, const TV0& v0, const TTracks&) { - bool isL = false; // Is lambda candidate - bool isAL = false; // Is anti-lambda candidate + using V0TrackTable = std::decay_t; - double mlambda = v0.mLambda(); - double mantilambda = v0.mAntiLambda(); + V0Selection selection; + + double massK0s = v0.mK0Short(); + + auto postrack = v0.template posTrack_as(); + auto negtrack = v0.template negTrack_as(); + + registryQA.fill(HIST("K0/hK0Count"), FillCandidate); + if (postrack.pt() < resoCutVals[PosTrackPt][K0] || negtrack.pt() < resoCutVals[NegTrackPt][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillDaughterPt); + if (massK0s < resoCutVals[MassMin][K0] || massK0s > resoCutVals[MassMax][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillMassCut); + // Rapidity correction + if (std::abs(v0.yK0Short()) > resoCutVals[Rapidity][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillRapidityCut); + // DCA cuts for K0short + if (std::abs(v0.dcapostopv()) < resoCutVals[DCAPosToPVMin][K0] || std::abs(v0.dcanegtopv()) < resoCutVals[DCANegToPVMin][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillDCAtoPV); + if (resoSwitchVals[UseDCAxDaughters][K0] != 0 && std::abs(v0.dcaV0daughters()) > resoCutVals[DCAxDaughters][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillDCAxDaughters); + // v0 radius cuts + if (resoSwitchVals[UseV0Radius][K0] != 0 && (v0.v0radius() < resoCutVals[RadiusMin][K0] || v0.v0radius() > resoCutVals[RadiusMax][K0])) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillV0Radius); + // cosine pointing angle cuts + if (v0.v0cosPA() < resoCutVals[CosPA][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillCosPA); + // Proper lifetime + if (resoSwitchVals[UseProperLifetime][K0] != 0 && v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massK0Short > resoCutVals[LifeTime][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillProperLifetime); + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][K0] != 0 && (v0.qtarm() / std::abs(v0.alpha())) < resoCutVals[ArmPodMin][K0]) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillArmPodCut); + if (resoSwitchVals[UseCompetingMassRejection][K0] != 0) { + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < resoCutVals[MassRejection][K0]) { + return selection; + } + if (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < resoCutVals[MassRejection][K0]) { + return selection; + } + } + registryQA.fill(HIST("K0/hK0Count"), FillCompetingMass); + if (!selectionV0Daughter(postrack, Pions) || !selectionV0Daughter(negtrack, Pions)) { + return selection; + } + registryQA.fill(HIST("K0/hK0Count"), FillDaughterTrackSelected); + selection.selected = true; + selection.isK0 = true; + if (cfgFill.cfgFillV0QA) { + registryQA.fill(HIST("K0/hK0AP"), v0.alpha(), v0.qtarm()); + } + return selection; + } + + template + V0Selection selectLambda(const TCollision& collision, const TV0& v0, const TTracks&, bool fillSelectionQA = true) + { + using V0TrackTable = std::decay_t; + V0Selection selection; + + double mlambda = v0.mLambda(); + double mantilambda = v0.mAntiLambda(); // separate the positive and negative V0 daughters - auto postrack = v0.template posTrack_as(); - auto negtrack = v0.template negTrack_as(); + auto postrack = v0.template posTrack_as(); + auto negtrack = v0.template negTrack_as(); - registry.fill(HIST("Lambda/hLambdaCount"), kFillCandidate); - if (postrack.pt() < resoCutVals[LAMBDA][kPosTrackPt] || negtrack.pt() < resoCutVals[LAMBDA][kNegTrackPt]) - return false; + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillCandidate); + } + if (postrack.pt() < resoCutVals[PosTrackPt][Lambda] || negtrack.pt() < resoCutVals[NegTrackPt][Lambda]) { + return selection; + } - registry.fill(HIST("Lambda/hLambdaCount"), kFillDaughterPt); - if (mlambda > resoCutVals[LAMBDA][kMassMin] && mlambda < resoCutVals[LAMBDA][kMassMax]) - isL = true; - if (mantilambda > resoCutVals[LAMBDA][kMassMin] && mantilambda < resoCutVals[LAMBDA][kMassMax]) - isAL = true; + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillDaughterPt); + } + if (mlambda > resoCutVals[MassMin][Lambda] && mlambda < resoCutVals[MassMax][Lambda]) { + selection.isL = true; + } + if (mantilambda > resoCutVals[MassMin][Lambda] && mantilambda < resoCutVals[MassMax][Lambda]) { + selection.isAL = true; + } - if (!isL && !isAL) { - return false; + if (!selection.isL && !selection.isAL) { + return selection; + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillMassCut); } - registry.fill(HIST("Lambda/hLambdaCount"), kFillMassCut); // Rapidity correction - if (v0.yLambda() > resoCutVals[LAMBDA][kRapidity]) - return false; - registry.fill(HIST("Lambda/hLambdaCount"), kFillRapidityCut); + if (std::abs(v0.yLambda()) > resoCutVals[Rapidity][Lambda]) { + return selection; + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillRapidityCut); + } // DCA cuts for lambda and antilambda - if (isL) { - if (std::abs(v0.dcapostopv()) < resoCutVals[LAMBDA][kDCAPosToPVMin] || std::abs(v0.dcanegtopv()) < resoCutVals[LAMBDA][kDCANegToPVMin]) - return false; + if (selection.isL) { + if (std::abs(v0.dcapostopv()) < resoCutVals[DCAPosToPVMin][Lambda] || std::abs(v0.dcanegtopv()) < resoCutVals[DCANegToPVMin][Lambda]) { + return selection; + } } - if (isAL) { - if (std::abs(v0.dcapostopv()) < resoCutVals[LAMBDA][kDCANegToPVMin] || std::abs(v0.dcanegtopv()) < resoCutVals[LAMBDA][kDCAPosToPVMin]) - return false; + if (selection.isAL) { + if (std::abs(v0.dcapostopv()) < resoCutVals[DCANegToPVMin][Lambda] || std::abs(v0.dcanegtopv()) < resoCutVals[DCAPosToPVMin][Lambda]) { + return selection; + } + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCAxDaughters][Lambda] != 0 && std::abs(v0.dcaV0daughters()) > resoCutVals[DCAxDaughters][Lambda]) { + return selection; + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillDCAxDaughters); } - registry.fill(HIST("Lambda/hLambdaCount"), kFillDCAtoPV); - if (std::abs(v0.dcaV0daughters()) > resoSwitchVals[LAMBDA][kDCABetDaug]) - return false; - registry.fill(HIST("Lambda/hLambdaCount"), kFillDCAxDaughters); // v0 radius cuts - if (resoSwitchVals[LAMBDA][kUseV0Radius] && (v0.v0radius() < resoCutVals[LAMBDA][kRadiusMin] || v0.v0radius() > resoCutVals[LAMBDA][kRadiusMax])) - return false; - registry.fill(HIST("Lambda/hLambdaCount"), kFillV0Radius); + if (resoSwitchVals[UseV0Radius][Lambda] != 0 && (v0.v0radius() < resoCutVals[RadiusMin][Lambda] || v0.v0radius() > resoCutVals[RadiusMax][Lambda])) { + return selection; + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillV0Radius); + } // cosine pointing angle cuts - if (v0.v0cosPA() < resoCutVals[LAMBDA][kCosPA]) - return false; - registry.fill(HIST("Lambda/hLambdaCount"), kFillCosPA); + if (v0.v0cosPA() < resoCutVals[CosPA][Lambda]) { + return selection; + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillCosPA); + } // Proper lifetime - if (resoSwitchVals[LAMBDA][kUseProperLifetime] && v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massLambda > resoCutVals[LAMBDA][kLifeTime]) - return false; - registry.fill(HIST("Lambda/hLambdaCount"), kFillProperLifetime); - if (isL) { - if (!selectionV0Daughter(postrack, PROTONS) || !selectionV0Daughter(negtrack, PIONS)) - return false; + if (resoSwitchVals[UseProperLifetime][Lambda] != 0 && v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massLambda > resoCutVals[LifeTime][Lambda]) { + return selection; } - if (isAL) { - if (!selectionV0Daughter(postrack, PIONS) || !selectionV0Daughter(negtrack, PROTONS)) - return false; + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillProperLifetime); } - registry.fill(HIST("Lambda/hLambdaCount"), kFillDaughterTrackSelection); - - if (isL) { - registry.fill(HIST("Lambda/hLambdaMass_sparse"), mlambda, v0.pt(), centrality); - registry.fill(HIST("Lambda/hLambdaPhi"), v0.phi()); - registry.fill(HIST("Lambda/hLambdaEta"), v0.eta()); - registry.fill(HIST("Lambda/PrPlusTPC_L"), postrack.pt(), postrack.tpcNSigmaKa()); - registry.fill(HIST("Lambda/PrPlusTOF_L"), postrack.pt(), postrack.tofNSigmaKa()); - registry.fill(HIST("Lambda/PiMinusTPC_L"), negtrack.pt(), negtrack.tpcNSigmaKa()); - registry.fill(HIST("Lambda/PiMinusTOF_L"), negtrack.pt(), negtrack.tofNSigmaKa()); - - registry.fill(HIST("Lambda/hLambdas"), 0.5, 1); - if (cfgUsePIDEfficiencies) { - double weffDaughter1 = getEfficiency(postrack, 3); - double weffDaughter2 = getEfficiency(negtrack, 1); - weff = weffDaughter1 * weffDaughter2; - if (weff > 0) - registry.fill(HIST("Lambda/hLambdas_corrected"), 0.5, weff); - } - } - if (isAL) { - registry.fill(HIST("Lambda/hAntiLambdaMass_sparse"), mantilambda, v0.pt(), centrality); - registry.fill(HIST("Lambda/hAntiLambdaPhi"), v0.phi()); - registry.fill(HIST("Lambda/hAntiLambdaEta"), v0.eta()); - registry.fill(HIST("Lambda/PiPlusTPC_AL"), postrack.pt(), postrack.tpcNSigmaKa()); - registry.fill(HIST("Lambda/PiPlusTOF_AL"), postrack.pt(), postrack.tofNSigmaKa()); - registry.fill(HIST("Lambda/PrMinusTPC_AL"), negtrack.pt(), negtrack.tpcNSigmaKa()); - registry.fill(HIST("Lambda/PrMinusTOF_AL"), negtrack.pt(), negtrack.tofNSigmaKa()); - - registry.fill(HIST("Lambda/hLambdas"), 0.5, 1); - if (cfgUsePIDEfficiencies) { - double weffDaughter1 = getEfficiency(postrack, 1); - double weffDaughter2 = getEfficiency(negtrack, 3); - weff = weffDaughter1 * weffDaughter2; - if (weff > 0) - registry.fill(HIST("Lambda/hLambdas_corrected"), 0.5, weff); + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][Lambda] != 0 && (v0.qtarm() / std::abs(v0.alpha())) < resoCutVals[ArmPodMin][Lambda]) { + return selection; + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillArmPodCut); + } + if (resoSwitchVals[UseCompetingMassRejection][Lambda] != 0) { + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < resoCutVals[MassRejection][Lambda]) { + return selection; } } - return true; + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillCompetingMass); + } + if (selection.isL) { + if (!selectionV0Daughter(postrack, Protons) || !selectionV0Daughter(negtrack, Pions)) { + return selection; + } + if (fillSelectionQA && cfgFill.cfgFillV0QA) { + registryQA.fill(HIST("Lambda/hLambdaAP"), v0.alpha(), v0.qtarm()); + } + } + if (selection.isAL) { + if (!selectionV0Daughter(postrack, Pions) || !selectionV0Daughter(negtrack, Protons)) { + return selection; + } + if (fillSelectionQA && cfgFill.cfgFillV0QA) { + registryQA.fill(HIST("Lambda/hAntiLambdaAP"), v0.alpha(), v0.qtarm()); + } + } + if (fillSelectionQA) { + registryQA.fill(HIST("Lambda/hLambdaCount"), FillDaughterTrackSelected); + } + selection.selected = true; + + return selection; } template - inline void fillPtSums(TTrack track, const double& vtxz) + inline void fillPtSums(const TTrack& track, const float& centrality, const double& vtxz) { - double wacc = (dt == kGen) ? 1. : getAcceptance(track, vtxz, 0); - double weff = (dt == kGen) ? 1. : getEfficiency(track); - if (weff < 0) + auto& flowPtContainer = (dt == Gen) ? fFCptGen : fFCpt; + double wacc = (dt == Gen) ? 1. : getAcceptance(track, vtxz, 0); + double weff = (dt == Gen) ? 1. : getEfficiency(track, centrality); + if (weff < 0) { return; + } // Fill the nominal sums - if (track.eta() > cfgEtaPtPt->first && track.eta() < cfgEtaPtPt->second) - fFCpt->fill(weff, track.pt()); + if (track.eta() > cfgKinematics.cfgEtaPtPt->first && track.eta() < cfgKinematics.cfgEtaPtPt->second) { + flowPtContainer->fill(weff, track.pt()); + } // Fill the subevent sums std::size_t index = 0; - for (const auto& [etamin, etamax] : o2::analysis::gfw::etagapsPtPt) { + for (const auto& [etamin, etamax] : gfwMemberCache.etagapsPtPt) { if (etamin < track.eta() && track.eta() < etamax) { - fFCpt->fillSub(weff, track.pt(), index); + flowPtContainer->fillSub(weff, track.pt(), index); } ++index; } - if (!cfgUseGapMethod) { + if (!cfgMethod.cfgUseGapMethod) { std::complex q2p = {weff * wacc * std::cos(2 * track.phi()), weff * wacc * std::sin(2 * track.phi())}; std::complex q2n = {weff * wacc * std::cos(-2 * track.phi()), weff * wacc * std::sin(-2 * track.phi())}; - fFCpt->fillArray(q2p, q2n, weff * track.pt(), weff); - fFCpt->fillArray(weff * wacc, weff * wacc, weff, weff); + flowPtContainer->fillArray(q2p, q2n, weff * track.pt(), weff); + flowPtContainer->fillArray(weff * wacc, weff * wacc, weff, weff); } } template - inline void fillGFW(TTrack track, const double& vtxz, int pid_index, DensityCorr densitycorrections) + inline void fillGFW(const TTrack& track, const float& centrality, const double& vtxz, int pid_index, const DensityCorr& densitycorrections) { - if (cfgUsePID) { // Analysing POI flow with id'ed particles - double ptmins[] = {o2::analysis::gfw::ptpoilow, o2::analysis::gfw::ptpoilow, 0.3, 0.5}; - double ptmaxs[] = {o2::analysis::gfw::ptpoiup, o2::analysis::gfw::ptpoiup, 6.0, 6.0}; - bool withinPtRef = (track.pt() > o2::analysis::gfw::ptreflow && track.pt() < o2::analysis::gfw::ptrefup); + if (cfgFill.cfgUsePID) { // Analysing POI flow with id'ed particles + std::array ptmins = {gfwMemberCache.ptpoilow, gfwMemberCache.ptpoilow, 0.3, 0.5}; + std::array ptmaxs = {gfwMemberCache.ptpoiup, gfwMemberCache.ptpoiup, 6.0, 6.0}; + bool withinPtRef = (track.pt() > gfwMemberCache.ptreflow && track.pt() < gfwMemberCache.ptrefup); bool withinPtPOI = (track.pt() > ptmins[pid_index] && track.pt() < ptmaxs[pid_index]); bool withinPtNch = (track.pt() > ptmins[0] && track.pt() < ptmaxs[0]); - if (!withinPtPOI && !withinPtRef) + if (!withinPtPOI && !withinPtRef) { return; + } - double waccRef = (dt == kGen) ? 1. : getAcceptance(track, vtxz, 0); - double waccPOI = (dt == kGen) ? 1. : withinPtPOI ? getAcceptance(track, vtxz, pid_index + 1) - : getAcceptance(track, vtxz, 0); // - if (withinPtRef && withinPtPOI && pid_index) + double waccRef = (dt == Gen) ? 1. : getAcceptance(track, vtxz, 0); + double waccPOI = (dt == Gen) ? 1. : withinPtPOI ? getAcceptance(track, vtxz, pid_index + 1) + : getAcceptance(track, vtxz, 0); // + if (withinPtRef && withinPtPOI && pid_index) { waccRef = waccPOI; // if particle is both (then it's overlap), override ref with POI - if (withinPtRef) + } + if (withinPtRef) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), waccRef, 1); - if (withinPtPOI && pid_index) + } + if (withinPtPOI && pid_index) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), waccPOI, (1 << (pid_index + 1))); - if (withinPtNch) + } + if (withinPtNch) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), waccPOI, 2); - if (withinPtPOI && withinPtRef && pid_index) + } + if (withinPtPOI && withinPtRef && pid_index) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), waccPOI, (1 << (pid_index + 5))); - if (withinPtNch && withinPtRef) + } + if (withinPtNch && withinPtRef) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), waccPOI, 32); + } } else { // Analysing only integrated flow - bool withinPtRef = (track.pt() > o2::analysis::gfw::ptreflow && track.pt() < o2::analysis::gfw::ptrefup); - bool withinPtPOI = (track.pt() > o2::analysis::gfw::ptpoilow && track.pt() < o2::analysis::gfw::ptpoiup); - if (!withinPtPOI && !withinPtRef) + bool withinPtRef = (track.pt() > gfwMemberCache.ptreflow && track.pt() < gfwMemberCache.ptrefup); + bool withinPtPOI = (track.pt() > gfwMemberCache.ptpoilow && track.pt() < gfwMemberCache.ptpoiup); + if (!withinPtPOI && !withinPtRef) { return; - double weff = (dt == kGen) ? 1. : getEfficiency(track, 0); - if (weff < 0) + } + double weff = (dt == Gen) ? 1. : getEfficiency(track, centrality, 0); + if (weff < 0) { return; - if (cfgUseDensityDependentCorrection && withinPtRef && dt != kGen) { + } + if (cfgEfficiency.cfgUseDensityDependentCorrection && withinPtRef && dt != Gen) { double fphi = densitycorrections.v2 * std::cos(2 * (track.phi() - densitycorrections.psi2Est)) + densitycorrections.v3 * std::cos(3 * (track.phi() - densitycorrections.psi3Est)) + densitycorrections.v4 * std::cos(4 * (track.phi() - densitycorrections.psi4Est)); fphi = (1 + 2 * fphi); int pTBinForEff = hFindPtBin->FindBin(track.pt()); @@ -2071,90 +2924,140 @@ struct FlowGenericFramework { } } } - double wacc = (dt == kGen) ? 1. : getAcceptance(track, vtxz, 0); - if (withinPtRef) + double wacc = (dt == Gen) ? 1. : getAcceptance(track, vtxz, 0); + if (withinPtRef) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 1); - if (withinPtPOI) + } + if (withinPtPOI) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 2); - if (withinPtRef && withinPtPOI) + } + if (withinPtRef && withinPtPOI) { fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 4); + } } - return; } template - inline void fillTrackQA(TTrack track, const float vtxz) + inline void fillTrackQA(const TTrack& track, const float vtxz) { - if constexpr (dt == kGen) { - registry.fill(HIST("MCGen/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ_gen"), track.phi(), track.eta(), vtxz); - registry.fill(HIST("MCGen/") + HIST(FillTimeName[ft]) + HIST("pt_gen"), track.pt()); + if constexpr (dt == Gen) { + registryQA.fill(HIST("MCGen/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ_gen"), track.phi(), track.eta(), vtxz); + registryQA.fill(HIST("MCGen/") + HIST(FillTimeName[ft]) + HIST("pt_gen"), track.pt()); } else { double wacc = getAcceptance(track, vtxz, 0); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ"), track.phi(), track.eta(), vtxz, (ft == kAfter) ? wacc : 1.0); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ"), track.phi(), track.eta(), vtxz, (ft == After) ? wacc : 1.0); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); - if (ft == kAfter) { - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_ref"), track.pt()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_poi"), track.pt()); + if (ft == After) { + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_ref"), track.pt()); + registryQA.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_poi"), track.pt()); - if (track.eta() > cfgEtaNch->first && track.eta() < cfgEtaNch->second) - registry.fill(HIST("trackQA/after/etaNch"), track.eta()); - if (track.eta() > cfgEtaPtPt->first && track.eta() < cfgEtaPtPt->second) - registry.fill(HIST("trackQA/after/etaPtPt"), track.eta()); + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { + registryQA.fill(HIST("trackQA/after/etaNch"), track.eta()); + } + if (track.eta() > cfgKinematics.cfgEtaPtPt->first && track.eta() < cfgKinematics.cfgEtaPtPt->second && cfgFill.cfgFillQA) { + registryQA.fill(HIST("trackQA/after/etaPtPt"), track.eta()); + } + } + } + } + + template + inline void fillV0QA(const V0Selection& selection, TV0 const& v0, const TTrack& postrack, const TTrack& negtrack, const float& centrality, const float& weff) + { + if (selection.isK0) { + registryQA.fill(HIST("K0/hK0Mass_sparse"), v0.mK0Short(), v0.pt(), centrality); + registryQA.fill(HIST("K0/hK0Phi"), v0.phi()); + registryQA.fill(HIST("K0/hK0EtaY"), v0.eta(), v0.yK0Short()); + registryQA.fill(HIST("K0/PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaKa()); + registryQA.fill(HIST("K0/PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaKa()); + registryQA.fill(HIST("K0/PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaKa()); + registryQA.fill(HIST("K0/PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaKa()); + + registryQA.fill(HIST("K0/hK0s"), 0.5, 1); + if (cfgEfficiency.cfgUsePIDEfficiencies) { + registryQA.fill(HIST("K0/hK0s_corrected"), 0.5, weff); + } + } + if (selection.isL) { + registryQA.fill(HIST("Lambda/hLambdaMass_sparse"), v0.mLambda(), v0.pt(), centrality); + registryQA.fill(HIST("Lambda/hLambdaPhi"), v0.phi()); + registryQA.fill(HIST("Lambda/hLambdaEtaY"), v0.eta(), v0.yLambda()); + registryQA.fill(HIST("Lambda/PrPlusTPC_L"), postrack.pt(), postrack.tpcNSigmaKa()); + registryQA.fill(HIST("Lambda/PrPlusTOF_L"), postrack.pt(), postrack.tofNSigmaKa()); + registryQA.fill(HIST("Lambda/PiMinusTPC_L"), negtrack.pt(), negtrack.tpcNSigmaKa()); + registryQA.fill(HIST("Lambda/PiMinusTOF_L"), negtrack.pt(), negtrack.tofNSigmaKa()); + } + if (selection.isAL) { + registryQA.fill(HIST("Lambda/hAntiLambdaMass_sparse"), v0.mAntiLambda(), v0.pt(), centrality); + registryQA.fill(HIST("Lambda/hAntiLambdaPhi"), v0.phi()); + registryQA.fill(HIST("Lambda/hAntiLambdaEtaY"), v0.eta(), v0.yLambda()); + registryQA.fill(HIST("Lambda/PiPlusTPC_AL"), postrack.pt(), postrack.tpcNSigmaKa()); + registryQA.fill(HIST("Lambda/PiPlusTOF_AL"), postrack.pt(), postrack.tofNSigmaKa()); + registryQA.fill(HIST("Lambda/PrMinusTPC_AL"), negtrack.pt(), negtrack.tpcNSigmaKa()); + registryQA.fill(HIST("Lambda/PrMinusTOF_AL"), negtrack.pt(), negtrack.tofNSigmaKa()); + } + if (selection.isL || selection.isAL) { + registryQA.fill(HIST("Lambda/hLambdas"), 0.5, 1); + if (cfgEfficiency.cfgUsePIDEfficiencies) { + registryQA.fill(HIST("Lambda/hLambdas_corrected"), 0.5, weff); } } } template - float getCentrality(TCollision collision) + float getCentrality(const TCollision& collision) { switch (cfgCentEstimator) { - case kCentFT0C: + case CentFT0C: return collision.centFT0C(); - case kCentFT0CVariant1: + case CentFT0CVariant1: return collision.centFT0CVariant1(); - case kCentFT0M: + case CentFT0M: return collision.centFT0M(); - case kCentFV0A: + case CentFV0A: return collision.centFV0A(); - case kCentNTPV: + case CentNTPV: return collision.centNTPV(); - case kCentNGlobal: + case CentNGlobal: return collision.centNGlobal(); - case kCentMFT: + case CentMFT: return collision.centMFT(); default: return collision.centFT0C(); } } - template - inline void fillEventQA(CollisionObject collision, TracksObject tracks) + template + inline void fillEventQA(const TCollision& collision, const TTracks& tracks) { - registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), tracks.size()); - registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV()); - registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); - registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), tracks.size()); - registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), tracks.size()); - registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); - registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C()); - return; + registryQA.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), tracks.size()); + registryQA.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV()); + registryQA.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); + registryQA.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), tracks.size()); + registryQA.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), tracks.size()); + registryQA.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); + registryQA.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C()); } o2::framework::expressions::Filter collisionFilter = nabs(aod::collision::posZ) < cfgVtxZ; - o2::framework::expressions::Filter trackFilter = (aod::track::eta > cfgEta->first) && (aod::track::eta < cfgEta->second) && (aod::track::pt > cfgPtmin) && (aod::track::pt < cfgPtmax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::itsChi2NCl < cfgTrackCuts.cfgChi2PrITSCls) && (aod::track::tpcChi2NCl < cfgTrackCuts.cfgChi2PrTPCCls) && nabs(aod::track::dcaZ) < cfgTrackCuts.cfgDCAz; + o2::framework::expressions::Filter trackFilter = (aod::track::eta > cfgKinematics.cfgEta->first) && (aod::track::eta < cfgKinematics.cfgEta->second) && (aod::track::pt > cfgKinematics.cfgPtMin) && (aod::track::pt < cfgKinematics.cfgPtmax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::itsChi2NCl < cfgTrackCuts.cfgChi2PrITSCls) && (aod::track::tpcChi2NCl < cfgTrackCuts.cfgChi2PrTPCCls) && nabs(aod::track::dcaZ) < cfgTrackCuts.cfgDCAz; using GFWCollisions = soa::Filtered>; + using GFWMCCollisions = soa::Join; // using GFWTracks = soa::Filtered>; using GFWTracks = soa::Filtered>; + using GFWMCTracks = soa::Filtered>; SliceCache cache; + Preslice mcTracksPerCollision = aod::track::collisionId; + Preslice mcV0sPerCollision = aod::v0data::collisionId; Partition posTracks = aod::track::signed1Pt > 0.0f; Partition negTracks = aod::track::signed1Pt < 0.0f; @@ -2169,150 +3072,683 @@ struct FlowGenericFramework { if (run != lastRun) { lastRun = run; LOGF(info, "run = %d", run); - if (cfgRunByRun) { + if (cfgFill.cfgRunByRun) { if (std::find(runNumbers.begin(), runNumbers.end(), run) == runNumbers.end()) { - LOGF(info, "Creating histograms for run %d", run); - createRunByRunHistograms(run); + if (cfgFill.cfgFillRunByRunQA) { + createRunByRunHistograms(run); + } runNumbers.push_back(run); } else { LOGF(info, "run %d already in runNumbers", run); } - if (!cfgFillWeights) + if (!cfgFill.cfgFillWeights) { loadCorrections(bc); + } } } - if (!cfgFillWeights && !cfgRunByRun) + if (!cfgFill.cfgFillWeights && !cfgFill.cfgRunByRun) { loadCorrections(bc); + } + registryQA.fill(HIST("eventQA/eventSel"), 0.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(0.5); + } + if (!collision.sel8()) { + return; + } + registryQA.fill(HIST("eventQA/eventSel"), 1.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(1.5); + } + float centrality = getCentrality(collision); + if (cfgEventSelection.cfgOccupancySelection >= 0) { + int occupancy = collision.trackOccupancyInTimeRange(); + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("eventQA/before/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + if (occupancy < 0 || occupancy > cfgEventSelection.cfgOccupancySelection) { + return; + } + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("eventQA/after/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + } + registryQA.fill(HIST("eventQA/eventSel"), 2.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(2.5); + } + if (cfgFill.cfgFillQA) { + fillEventQA(collision, tracks); + } + registryQA.fill(HIST("eventQA/before/centrality"), centrality); + registryQA.fill(HIST("eventQA/before/multiplicity"), tracks.size()); + if (!eventSelected(collision, tracks.size(), centrality, run)) { + return; + } + if (cfgFill.cfgFillQA) { + fillEventQA(collision, tracks); + } + registryQA.fill(HIST("eventQA/after/centrality"), centrality); + registryQA.fill(HIST("eventQA/after/multiplicity"), tracks.size()); + // Get magnetic field polarity + auto field = (cfgEventSelection.cfgMagField == DefaultMagneticFieldCut) ? getMagneticField(bc.timestamp()) : static_cast(cfgEventSelection.cfgMagField); - registry.fill(HIST("eventQA/eventSel"), 0.5); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(0.5); + processCollision(collision, tracks, v0s, centrality, field, run); + } + PROCESS_SWITCH(FlowGenericFramework, processData, "Process analysis for non-derived data", true); - if (!collision.sel8()) + void processMC(soa::Filtered::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, aod::BCsWithTimestamps const&, GFWMCTracks const& tracks, aod::V0Datas const& v0s) + { + if (collisions.size() != 1) { return; + } + int run = 0; + float centrality = -1; + for (const auto& collision : collisions) { + centrality = getCentrality(collision); + } + processCollision(mcCollision, particles, v0s, centrality, -999, run); - registry.fill(HIST("eventQA/eventSel"), 1.5); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(1.5); + for (const auto& collision : collisions) { + auto bc = collision.bc_as(); + run = bc.runNumber(); + if (run != lastRun) { + lastRun = run; + if (cfgFill.cfgFillRunByRunQA) { + createRunByRunHistograms(run); + } + if (!cfgFill.cfgFillWeights && cfgFill.cfgRunByRun) { + loadCorrections(bc); + } + } + if (!cfgFill.cfgFillWeights && !cfgFill.cfgRunByRun) { + loadCorrections(bc); + } - float centrality = getCentrality(collision); + registryQA.fill(HIST("eventQA/eventSel"), 0.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(0.5); + } - if (cfgDoOccupancySel) { - int occupancy = collision.trackOccupancyInTimeRange(); - registry.fill(HIST("eventQA/before/occ_mult_cent"), occupancy, tracks.size(), centrality); - if (occupancy < 0 || occupancy > cfgOccupancySelection) + if (!collision.sel8()) { return; - registry.fill(HIST("eventQA/after/occ_mult_cent"), occupancy, tracks.size(), centrality); - } - registry.fill(HIST("eventQA/eventSel"), 2.5); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(2.5); - if (cfgFillQA) - fillEventQA(collision, tracks); - registry.fill(HIST("eventQA/before/centrality"), centrality); - registry.fill(HIST("eventQA/before/multiplicity"), tracks.size()); - if (!eventSelected(collision, tracks.size(), centrality, run)) - return; - if (cfgFillQA) - fillEventQA(collision, tracks); - registry.fill(HIST("eventQA/after/centrality"), centrality); - registry.fill(HIST("eventQA/after/multiplicity"), tracks.size()); + } - // Get magnetic field polarity - auto field = (cfgMagField == 99999) ? getMagneticField(bc.timestamp()) : cfgMagField; // o2-linter: disable=magic-number (hard coded default cut) + registryQA.fill(HIST("eventQA/eventSel"), 1.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(1.5); + } + + const auto recoCentrality = getCentrality(collision); - processCollision(collision, tracks, v0s, centrality, field, run); + if (cfgEventSelection.cfgOccupancySelection >= 0) { + int occupancy = collision.trackOccupancyInTimeRange(); + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("eventQA/before/occ_mult_cent"), occupancy, tracks.size(), recoCentrality); + } + if (occupancy < 0 || occupancy > cfgEventSelection.cfgOccupancySelection) { + return; + } + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("eventQA/after/occ_mult_cent"), occupancy, tracks.size(), recoCentrality); + } + } + registryQA.fill(HIST("eventQA/eventSel"), 2.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(2.5); + } + + if (cfgFill.cfgFillQA) { + fillEventQA(collision, tracks); + } + if (!eventSelected(collision, tracks.size(), recoCentrality, run)) { + return; + } + if (cfgFill.cfgFillQA) { + fillEventQA(collision, tracks); + } + + auto field = (cfgEventSelection.cfgMagField == DefaultMagneticFieldCut) ? getMagneticField(bc.timestamp()) : static_cast(cfgEventSelection.cfgMagField); + const auto groupedTracks = tracks.sliceBy(mcTracksPerCollision, collision.globalIndex()); + const auto groupedV0s = v0s.sliceBy(mcV0sPerCollision, collision.globalIndex()); + processCollision(collision, groupedTracks, groupedV0s, recoCentrality, field, run); + } } - PROCESS_SWITCH(FlowGenericFramework, processData, "Process analysis for non-derived data", true); + PROCESS_SWITCH(FlowGenericFramework, processMC, "Process analysis for MC reconstructed and generated events simultaneously", false); - void processMCReco(GFWCollisions::iterator const& collision, aod::BCsWithTimestamps const&, soa::Filtered> const& tracks, aod::McParticles const&, aod::V0Datas const& v0s) + void processMCReco(GFWCollisions::iterator const& collision, aod::BCsWithTimestamps const&, GFWMCTracks const& tracks, aod::McParticles const&, aod::V0Datas const& v0s) { auto bc = collision.bc_as(); int run = bc.runNumber(); if (run != lastRun) { lastRun = run; - if (cfgRunByRun) + if (cfgFill.cfgFillRunByRunQA) { createRunByRunHistograms(run); + } } - registry.fill(HIST("eventQA/eventSel"), 0.5); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(0.5); + registryQA.fill(HIST("eventQA/eventSel"), 0.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(0.5); + } - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - registry.fill(HIST("eventQA/eventSel"), 1.5); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(1.5); + registryQA.fill(HIST("eventQA/eventSel"), 1.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(1.5); + } const auto centrality = getCentrality(collision); - if (cfgDoOccupancySel) { + if (cfgEventSelection.cfgOccupancySelection >= 0) { int occupancy = collision.trackOccupancyInTimeRange(); - registry.fill(HIST("eventQA/before/occ_mult_cent"), occupancy, tracks.size(), centrality); - if (occupancy < 0 || occupancy > cfgOccupancySelection) + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("eventQA/before/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + if (occupancy < 0 || occupancy > cfgEventSelection.cfgOccupancySelection) { return; - registry.fill(HIST("eventQA/after/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("eventQA/after/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + } + registryQA.fill(HIST("eventQA/eventSel"), 2.5); + if (cfgFill.cfgFillRunByRunQA) { + th1sList[run][EventSel]->Fill(2.5); } - registry.fill(HIST("eventQA/eventSel"), 2.5); - if (cfgRunByRun) - th1sList[run][hEventSel]->Fill(2.5); - if (cfgFillQA) - fillEventQA(collision, tracks); - if (!eventSelected(collision, tracks.size(), centrality, run)) + if (cfgFill.cfgFillQA) { + fillEventQA(collision, tracks); + } + if (!eventSelected(collision, tracks.size(), centrality, run)) { return; - if (cfgFillQA) - fillEventQA(collision, tracks); + } + if (cfgFill.cfgFillQA) { + fillEventQA(collision, tracks); + } - if (!cfgFillWeights) + if (!cfgFill.cfgFillWeights) { loadCorrections(bc); - - auto field = (cfgMagField == 99999) ? getMagneticField(bc.timestamp()) : cfgMagField; // o2-linter: disable=magic-number (hard coded default cut) - processCollision(collision, tracks, v0s, centrality, field, run); + } + auto field = (cfgEventSelection.cfgMagField == DefaultMagneticFieldCut) ? getMagneticField(bc.timestamp()) : static_cast(cfgEventSelection.cfgMagField); + processCollision(collision, tracks, v0s, centrality, field, run); } PROCESS_SWITCH(FlowGenericFramework, processMCReco, "Process analysis for MC reconstructed events", false); o2::framework::expressions::Filter mcCollFilter = nabs(aod::mccollision::posZ) < cfgVtxZ; void processMCGen(soa::Filtered::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles, aod::V0Datas const& v0s) { - if (collisions.size() != 1) + if (collisions.size() != 1) { return; + } float centrality = -1; for (const auto& collision : collisions) { centrality = getCentrality(collision); } int run = 0; - processCollision(mcCollision, particles, v0s, centrality, -999, run); + processCollision(mcCollision, particles, v0s, centrality, -999, run); } PROCESS_SWITCH(FlowGenericFramework, processMCGen, "Process analysis for MC generated events", false); void processOnTheFly(soa::Filtered::iterator const& mcCollision, aod::McParticles const& mcParticles, aod::V0Datas const& v0s) { int run = 0; - registry.fill(HIST("MCGen/impactParameter"), mcCollision.impactParameter(), mcParticles.size()); - processCollision(mcCollision, mcParticles, v0s, mcCollision.impactParameter(), -999, run); + if (cfgFill.cfgFillQA) { + registryQA.fill(HIST("MCGen/impactParameter"), mcCollision.impactParameter(), mcParticles.size()); + } + processCollision(mcCollision, mcParticles, v0s, mcCollision.impactParameter(), -999, run); } PROCESS_SWITCH(FlowGenericFramework, processOnTheFly, "Process analysis for MC on-the-fly generated events", false); + template + bool isGeneratedEfficiencyV0(const TParticle& particle, int pdgCode, int resonanceIndex) + { + return particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == pdgCode && std::abs(particle.y()) < resoCutVals[Rapidity][resonanceIndex]; + } + + template + bool isGeneratedEfficiencyTrack(const TParticle& particle) + { + auto pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + return pdgParticle && pdgParticle->Charge() != 0 && particle.isPhysicalPrimary() && particle.eta() > gfwMemberCache.etalow && particle.eta() < gfwMemberCache.etaup && particle.pt() > gfwMemberCache.ptlow && particle.pt() < gfwMemberCache.ptup; + } + + template + int getEfficiencyTruthPID(const TParticle& particle) + { + if (std::abs(particle.pdgCode()) == kPiPlus) { + return EfficiencyPion; + } + if (std::abs(particle.pdgCode()) == kKPlus) { + return EfficiencyKaon; + } + if (std::abs(particle.pdgCode()) == kProton) { + return EfficiencyProton; + } + return EfficiencyCharged; + } + + template + void fillGeneratedEfficiencyTrack(const TParticle& particle, float centrality) + { + if (!isGeneratedEfficiencyTrack(particle)) { + return; + } + if (particle.eta() <= cfgKinematics.cfgEta->first || particle.eta() >= cfgKinematics.cfgEta->second) { + return; + } + + const int pidIndex = getEfficiencyTruthPID(particle); + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, EfficiencyCharged, EfficiencyGenerated); + if (pidIndex != EfficiencyCharged) { + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, pidIndex, EfficiencyGenerated); + } + } + + template + void fillGeneratedEfficiencyV0(const TParticle& particle, int particleIndex, float centrality) + { + if (!particle.has_daughters()) { + return; + } + for (const auto& daughter : particle.template daughters_as()) { + if (daughter.eta() <= cfgKinematics.cfgEta->first || daughter.eta() >= cfgKinematics.cfgEta->second) { + return; + } + } + registry.fill(HIST("Efficiency/efficiencyHist"), particle.pt(), centrality, particleIndex, EfficiencyGenerated); + } + + template + bool isGeneratedFeeddownXi(const TParticle& particle) + { + return particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kXiMinus && std::abs(particle.y()) < resoCutVals[Rapidity][Lambda]; + } + + template + int getLambdaFeeddownSpeciesFromXi(const TParticle& xiParticle) + { + return xiParticle.pdgCode() == PDG_t::kXiMinus ? FeeddownLambdaFromXiMinus : FeeddownAntiLambdaFromXiPlus; + } + + template + void fillGeneratedLambdaFeeddownXi(const TParticle& particle, float centrality) + { + if (resoSwitchVals[UseParticle][Lambda] == 0) { + return; + } + if (!isGeneratedFeeddownXi(particle)) { + return; + } + registry.fill(HIST("Efficiency/lambdaFeeddownGeneratedXi"), particle.pt(), centrality, getLambdaFeeddownSpeciesFromXi(particle)); + } + + template + bool isEfficiencyEventSelected(const TCollision& collision, const int multTrk, float& centrality) + { + if (!collision.sel8()) { + return false; + } + centrality = getCentrality(collision); + if (centrality < gfwMemberCache.centbinning.front() || centrality > gfwMemberCache.centbinning.back()) { + return false; + } + if (cfgEventSelection.cfgOccupancySelection >= 0) { + int occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < 0 || occupancy > cfgEventSelection.cfgOccupancySelection) { + return false; + } + } + const int run = 0; + return eventSelected(collision, multTrk, centrality, run); + } + + template + bool isWithinEfficiencyEtaAcceptance(const TTrack& track) + { + return track.eta() > cfgKinematics.cfgEta->first && track.eta() < cfgKinematics.cfgEta->second; + } + + template + int getV0EfficiencyDaughterPdgCategory(const TParticle& particle) + { + const int pdgCode = particle.pdgCode(); + if (pdgCode == kPiPlus) { + return V0EfficiencyDaughterPiPlus; + } + if (pdgCode == -kPiPlus) { + return V0EfficiencyDaughterPiMinus; + } + if (pdgCode == kProton) { + return V0EfficiencyDaughterProton; + } + if (pdgCode == -kProton) { + return V0EfficiencyDaughterAntiProton; + } + if (pdgCode == kElectron) { + return V0EfficiencyDaughterElectron; + } + if (pdgCode == -kElectron) { + return V0EfficiencyDaughterPositron; + } + if (std::abs(pdgCode) == kKPlus) { + return V0EfficiencyDaughterKaon; + } + return V0EfficiencyDaughterOther; + } + + template + void fillV0EfficiencyDaughterDebug(int particleIndex, float centrality, const TParticle& posMcParticle, const TParticle& negMcParticle, const TTrack& postrack, const TTrack& negtrack) + { + registry.fill(HIST("Efficiency/v0RecoDaughterEtaDebug"), centrality, particleIndex, posMcParticle.eta(), negMcParticle.eta(), postrack.eta(), negtrack.eta()); + registry.fill(HIST("Efficiency/v0RecoDaughterPdgDebug"), centrality, particleIndex, getV0EfficiencyDaughterPdgCategory(posMcParticle), getV0EfficiencyDaughterPdgCategory(negMcParticle)); + } + + void fillV0EfficiencyRecoDebug(float pt, float centrality, int particleIndex, int stage) + { + registry.fill(HIST("Efficiency/v0RecoDebug"), pt, centrality, particleIndex, stage); + } + + template + void fillEnabledV0EfficiencyRecoDebug(const TV0& v0, float centrality, int stage) + { + if (resoSwitchVals[UseParticle][K0] != 0) { + fillV0EfficiencyRecoDebug(v0.pt(), centrality, EfficiencyK0, stage); + } + if (resoSwitchVals[UseParticle][Lambda] != 0) { + fillV0EfficiencyRecoDebug(v0.pt(), centrality, EfficiencyLambda, stage); + } + } + + template + void fillLambdaFeeddownReco(const TV0& v0, const TCollision& collision, const TTracks& tracks, float centrality) + { + if (resoSwitchVals[UseParticle][Lambda] == 0) { + return; + } + + using V0TrackTable = std::decay_t; + auto postrack = v0.template posTrack_as(); + auto negtrack = v0.template negTrack_as(); + + if (!postrack.has_mcParticle() || !negtrack.has_mcParticle()) { + return; + } + + auto posMcParticle = postrack.template mcParticle_as(); + auto negMcParticle = negtrack.template mcParticle_as(); + if (!posMcParticle.has_mothers() || !negMcParticle.has_mothers()) { + return; + } + + for (const auto& posMother : posMcParticle.template mothers_as()) { + for (const auto& negMother : negMcParticle.template mothers_as()) { + if (posMother.globalIndex() != negMother.globalIndex() || std::abs(posMother.pdgCode()) != PDG_t::kLambda0) { + continue; + } + + if (!posMother.has_mothers()) { + continue; + } + for (const auto& xiMother : posMother.template mothers_as()) { + if (!isGeneratedFeeddownXi(xiMother)) { + continue; + } + + const bool isLambda = posMother.pdgCode() == PDG_t::kLambda0 && xiMother.pdgCode() == PDG_t::kXiMinus; + const bool isAntiLambda = posMother.pdgCode() == -PDG_t::kLambda0 && xiMother.pdgCode() == -PDG_t::kXiMinus; + if (!isLambda && !isAntiLambda) { + continue; + } + + auto selection = selectLambda(collision, v0, tracks, false); + if (!selection.selected || (isLambda && !selection.isL) || (isAntiLambda && !selection.isAL)) { + return; + } + if (!isWithinEfficiencyEtaAcceptance(posMcParticle) || !isWithinEfficiencyEtaAcceptance(negMcParticle)) { + return; + } + if (!isWithinEfficiencyEtaAcceptance(postrack) || !isWithinEfficiencyEtaAcceptance(negtrack)) { + return; + } + + registry.fill(HIST("Efficiency/lambdaFeeddownRecoXi"), v0.pt(), xiMother.pt(), centrality, getLambdaFeeddownSpeciesFromXi(xiMother)); + return; + } + } + } + } + + template + void fillEfficiencyRecoTrack(const TTrack& track, int field, float centrality) + { + if (track.mcParticleId() < 0 || !track.has_mcParticle()) { + return; + } + auto mcParticle = track.mcParticle(); + if (!isGeneratedEfficiencyTrack(mcParticle)) { + return; + } + if (!trackSelected(track, field)) { + return; + } + if (!isWithinEfficiencyEtaAcceptance(mcParticle) || !isWithinEfficiencyEtaAcceptance(track)) { + return; + } + + const int pidIndex = getEfficiencyTruthPID(mcParticle); + registry.fill(HIST("Efficiency/efficiencyHist"), mcParticle.pt(), centrality, EfficiencyCharged, EfficiencyReconstructed); + if (pidIndex != EfficiencyCharged) { + registry.fill(HIST("Efficiency/efficiencyHist"), mcParticle.pt(), centrality, pidIndex, EfficiencyReconstructed); + } + } + + template + void fillEfficiencyRecoV0(const TV0& v0, const TCollision& collision, const TTracks& tracks, float centrality) + { + using V0TrackTable = std::decay_t; + if (cfgFill.cfgFillV0QA) { + fillEnabledV0EfficiencyRecoDebug(v0, centrality, V0EfficiencyCandidate); + } + + auto postrack = v0.template posTrack_as(); + auto negtrack = v0.template negTrack_as(); + + if (!postrack.has_mcParticle() || !negtrack.has_mcParticle()) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillEnabledV0EfficiencyRecoDebug(v0, centrality, V0EfficiencyDaughterMCLabels); + } + + auto posMcParticle = postrack.template mcParticle_as(); + auto negMcParticle = negtrack.template mcParticle_as(); + if (!posMcParticle.has_mothers() || !negMcParticle.has_mothers()) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillEnabledV0EfficiencyRecoDebug(v0, centrality, V0EfficiencyDaughterMothers); + } + + for (const auto& posMother : posMcParticle.template mothers_as()) { + for (const auto& negMother : negMcParticle.template mothers_as()) { + if (posMother.globalIndex() != negMother.globalIndex() || !posMother.isPhysicalPrimary()) { + continue; + } + + int pdgCode = std::abs(posMother.pdgCode()); + if (pdgCode == PDG_t::kK0Short && resoSwitchVals[UseParticle][K0] != 0) { + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyK0, V0EfficiencyCommonPrimaryMother); + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyK0, V0EfficiencyMotherSpecies); + } + if (std::abs(posMother.y()) >= resoCutVals[Rapidity][K0]) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyK0, V0EfficiencyMotherRapidity); + } + auto selection = selectK0(collision, v0, tracks); + if (!selection.selected) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyK0, V0EfficiencySelection); + fillV0EfficiencyDaughterDebug(EfficiencyK0, centrality, posMcParticle, negMcParticle, postrack, negtrack); + } + if (!isWithinEfficiencyEtaAcceptance(posMcParticle) || !isWithinEfficiencyEtaAcceptance(negMcParticle)) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyK0, V0EfficiencyGeneratedDaughterEta); + } + if (!isWithinEfficiencyEtaAcceptance(postrack) || !isWithinEfficiencyEtaAcceptance(negtrack)) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyK0, V0EfficiencyRecoDaughterEta); + } + registry.fill(HIST("Efficiency/efficiencyHist"), posMother.pt(), centrality, EfficiencyK0, EfficiencyReconstructed); + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyK0, V0EfficiencyFilled); + } + return; + } + + if (pdgCode == PDG_t::kLambda0 && resoSwitchVals[UseParticle][Lambda] != 0) { + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyLambda, V0EfficiencyCommonPrimaryMother); + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyLambda, V0EfficiencyMotherSpecies); + } + if (std::abs(posMother.y()) >= resoCutVals[Rapidity][Lambda]) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyLambda, V0EfficiencyMotherRapidity); + } + auto selection = selectLambda(collision, v0, tracks); + if (!selection.selected) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyLambda, V0EfficiencySelection); + fillV0EfficiencyDaughterDebug(EfficiencyLambda, centrality, posMcParticle, negMcParticle, postrack, negtrack); + } + if (!isWithinEfficiencyEtaAcceptance(posMcParticle) || !isWithinEfficiencyEtaAcceptance(negMcParticle)) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyLambda, V0EfficiencyGeneratedDaughterEta); + } + if (!isWithinEfficiencyEtaAcceptance(postrack) || !isWithinEfficiencyEtaAcceptance(negtrack)) { + return; + } + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyLambda, V0EfficiencyRecoDaughterEta); + } + registry.fill(HIST("Efficiency/efficiencyHist"), posMother.pt(), centrality, EfficiencyLambda, EfficiencyReconstructed); + if (cfgFill.cfgFillV0QA) { + fillV0EfficiencyRecoDebug(posMother.pt(), centrality, EfficiencyLambda, V0EfficiencyFilled); + } + return; + } + } + } + } + + void processEfficiency(soa::Filtered::iterator const& mcCollision, soa::SmallGroups const& collisions, aod::BCsWithTimestamps const&, GFWMCTracks const& tracks, aod::V0Datas const& v0s, aod::McParticles const& mcParticles) + { + bool hasSelectedCollision = false; + int bestNumContrib = -1; + int bestCollisionIndex = -1; + float selectedCentrality = -1.f; + + for (const auto& collision : collisions) { + int tracksThisCollision = 0; + for (const auto& track : tracks) { + if (track.collisionId() == collision.globalIndex()) { + ++tracksThisCollision; + } + } + float centrality = -1.f; + if (!isEfficiencyEventSelected(collision, tracksThisCollision, centrality)) { + continue; + } + if (!hasSelectedCollision || collision.numContrib() > bestNumContrib) { + hasSelectedCollision = true; + bestNumContrib = collision.numContrib(); + bestCollisionIndex = collision.globalIndex(); + selectedCentrality = centrality; + } + } + if (!hasSelectedCollision) { + return; + } + + for (const auto& particle : mcParticles) { + if (particle.mcCollisionId() != mcCollision.globalIndex()) { + continue; + } + fillGeneratedEfficiencyTrack(particle, selectedCentrality); + if (cfgFill.cfgFillV0QA) { + fillGeneratedLambdaFeeddownXi(particle, selectedCentrality); + } + if (isGeneratedEfficiencyV0(particle, PDG_t::kK0Short, K0) && resoSwitchVals[UseParticle][K0] != 0) { + fillGeneratedEfficiencyV0(particle, EfficiencyK0, selectedCentrality); + } + if (isGeneratedEfficiencyV0(particle, PDG_t::kLambda0, Lambda) && resoSwitchVals[UseParticle][Lambda] != 0) { + fillGeneratedEfficiencyV0(particle, EfficiencyLambda, selectedCentrality); + } + } + + for (const auto& collision : collisions) { + if (collision.globalIndex() != bestCollisionIndex) { + continue; + } + auto bc = collision.template bc_as(); + auto field = (cfgEventSelection.cfgMagField == DefaultMagneticFieldCut) ? getMagneticField(bc.timestamp()) : static_cast(cfgEventSelection.cfgMagField); + for (const auto& track : tracks) { + if (track.collisionId() != bestCollisionIndex) { + continue; + } + fillEfficiencyRecoTrack(track, field, selectedCentrality); + } + for (const auto& v0 : v0s) { + if (v0.collisionId() != bestCollisionIndex) { + continue; + } + if (cfgFill.cfgFillV0QA) { + fillLambdaFeeddownReco(v0, collision, tracks, selectedCentrality); + } + fillEfficiencyRecoV0(v0, collision, tracks, selectedCentrality); + } + break; + } + } + PROCESS_SWITCH(FlowGenericFramework, processEfficiency, "Process reconstruction efficiency for charged particles, K0Short, and Lambda", false); + void processRun2(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, GFWTracks const& tracks, aod::V0Datas const& v0s) { auto bc = collision.bc_as(); int run = bc.runNumber(); if (run != lastRun) { lastRun = run; - if (cfgRunByRun) + if (cfgFill.cfgFillRunByRunQA) { createRunByRunHistograms(run); + } } - if (!collision.sel7()) + if (!collision.sel7()) { return; + } const auto centrality = collision.centRun2V0M(); - if (!cfgFillWeights) + if (!cfgFill.cfgFillWeights) { loadCorrections(bc); - - auto field = (cfgMagField == 99999) ? getMagneticField(bc.timestamp()) : cfgMagField; // o2-linter: disable=magic-number (hard coded default cut) - processCollision(collision, tracks, v0s, centrality, field, run); + } + auto field = (cfgEventSelection.cfgMagField == DefaultMagneticFieldCut) ? getMagneticField(bc.timestamp()) : static_cast(cfgEventSelection.cfgMagField); + processCollision(collision, tracks, v0s, centrality, field, run); } PROCESS_SWITCH(FlowGenericFramework, processRun2, "Process analysis for Run 2 converted data", false); }; diff --git a/PWGCF/GenericFramework/Tasks/flowGfwLightIons.cxx b/PWGCF/GenericFramework/Tasks/flowGfwLightIons.cxx index 3cee62515b7..02db3917bf4 100644 --- a/PWGCF/GenericFramework/Tasks/flowGfwLightIons.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGfwLightIons.cxx @@ -667,7 +667,7 @@ struct FlowGfwLightIons { } template - double getAcceptance(TTrack track, const double& vtxz) + double getAcceptance(const TTrack& track, const double& vtxz) { double wacc = 1; if (cfg.mAcceptance) @@ -676,7 +676,7 @@ struct FlowGfwLightIons { } template - double getEfficiency(TTrack track) + double getEfficiency(const TTrack& track) { double eff = 1.; if (cfg.mEfficiency) @@ -688,7 +688,7 @@ struct FlowGfwLightIons { } template - bool eventSelected(TCollision collision, const int& multTrk, const float& centrality, const int& run) + bool eventSelected(const TCollision& collision, const int& multTrk, const float& centrality, const int& run) { if (cfgEventCutFlags.cfgTVXinTRD) { if (collision.alias_bit(kTVXinTRD)) { @@ -829,7 +829,7 @@ struct FlowGfwLightIons { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { if (cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) return false; @@ -842,7 +842,7 @@ struct FlowGfwLightIons { }; template - void fillWeights(const TTrack track, const double vtxz, const int& run) + void fillWeights(const TTrack& track, const double vtxz, const int& run) { if (cfgRunByRun) th3sList[run][hNUAref]->Fill(track.phi(), track.eta(), vtxz); @@ -966,7 +966,7 @@ struct FlowGfwLightIons { }; template - void processCollision(TCollision collision, TTracks tracks, const XAxis& xaxis, const int& run) + void processCollision(const TCollision& collision, const TTracks& tracks, const XAxis& xaxis, const int& run) { if (tracks.size() < 1) return; @@ -1071,7 +1071,7 @@ struct FlowGfwLightIons { } template - void fillAcceptedTracks(TTrack track, AcceptedTracks& acceptedTracks) + void fillAcceptedTracks(const TTrack& track, AcceptedTracks& acceptedTracks) { if (posRegionIndex >= 0 && track.eta() > o2::analysis::gfw::regions.GetEtaMin()[posRegionIndex] && track.eta() < o2::analysis::gfw::regions.GetEtaMax()[posRegionIndex]) ++acceptedTracks.nPos; @@ -1159,7 +1159,7 @@ struct FlowGfwLightIons { } template - inline void fillGFW(TTrack track, const double& vtxz, DensityCorr densitycorrections) + inline void fillGFW(const TTrack& track, const double& vtxz, DensityCorr densitycorrections) { bool withinPtRef = (track.pt() > o2::analysis::gfw::ptreflow && track.pt() < o2::analysis::gfw::ptrefup); bool withinPtPOI = (track.pt() > o2::analysis::gfw::ptpoilow && track.pt() < o2::analysis::gfw::ptpoiup); @@ -1191,7 +1191,7 @@ struct FlowGfwLightIons { } template - inline void fillPtSums(TTrack track) + inline void fillPtSums(const TTrack& track) { if (track.pt() < o2::analysis::gfw::ptreflow || track.pt() > o2::analysis::gfw::ptrefup) return; @@ -1225,7 +1225,7 @@ struct FlowGfwLightIons { } template - inline void fillTrackQA(TTrack track, const float vtxz) + inline void fillTrackQA(const TTrack& track, const float vtxz) { if constexpr (dt == kGen) { registry.fill(HIST("MCGen/trackQA/phi_eta_vtxZ"), track.phi(), track.eta(), vtxz); @@ -1250,7 +1250,7 @@ struct FlowGfwLightIons { } template - float getCentrality(TCollision collision) + float getCentrality(const TCollision& collision) { switch (cfgCentEstimator) { case kCentFT0C: @@ -1273,7 +1273,7 @@ struct FlowGfwLightIons { } template - inline void fillEventQA(TCollision collision, XAxis xaxis) + inline void fillEventQA(const TCollision& collision, XAxis xaxis) { if constexpr (framework::has_type_v) { registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), xaxis.multiplicity); diff --git a/PWGCF/GenericFramework/Tasks/flowGfwNonflow.cxx b/PWGCF/GenericFramework/Tasks/flowGfwNonflow.cxx new file mode 100644 index 00000000000..cdccf5bd2fd --- /dev/null +++ b/PWGCF/GenericFramework/Tasks/flowGfwNonflow.cxx @@ -0,0 +1,1357 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowGfwNonflow.cxx +/// \brief Task to analyse scaled non-flow subtraction of flow cumulants +/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch +/// \since 06/07/2026 + +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/FlowPtContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWConfig.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::analysis::genericframework; + +constexpr float DefaultMagneticFieldCut = 99999.f; +static constexpr std::array, 4> LongArrayDouble = {{{{-0.8, -0.5}}, {{0.5, 0.8}}, {{-2, -2}}, {{-2, -2}}}}; + +struct FlowGfwNonflow { + Configurable cfgNbootstrap{"cfgNbootstrap", 10, "Number of subsamples"}; + Configurable cfgMpar{"cfgMpar", 4, "Highest order of pt-pt correlations"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A, 4:NTPV, 5:NGlobal, 6:MFT"}; + Configurable cfgUseNch{"cfgUseNch", false, "Do correlations as function of Nch"}; + Configurable cfgUseNchCorrection{"cfgUseNchCorrection", 1, "Nch used on the x-axis; 0: tracks table size, 1: efficiency-corrected, 2: accepted reconstructed, 3: Reco.-gen. response-matrix corrected"}; + Configurable cfgRunByRun{"cfgRunByRun", false, "Use run-by-run NUA"}; + Configurable cfgFillQA{"cfgFillQA", false, "Fill QA histograms"}; + Configurable cfgUseCentralMoments{"cfgUseCentralMoments", true, "Use central moments in vn-pt calculations"}; + Configurable cfgUseMultiplicityFlowWeights{"cfgUseMultiplicityFlowWeights", true, "Enable or disable the use of multiplicity-based event weighting"}; + struct : ConfigurableGroup { + Configurable cfgEfficiencyPath{"cfgEfficiencyPath", "", "CCDB path to efficiency object"}; + Configurable cfgUse2DEfficiency{"cfgUse2DEfficiency", false, "Toggle the use of 2D (pt, centrality) efficiency versus centrality integrated efficiency"}; + Configurable cfgNchResponsePath{"cfgNchResponsePath", "", "CCDB path to TH2 response matrix (reconstructed Nch on x, generated Nch on y)"}; + Configurable cfgAcceptancePath{"cfgAcceptancePath", "", "CCDB path to acceptance object"}; + } cfgCorrections; + struct : ConfigurableGroup { + Configurable> cfgEta{"cfgEta", {-0.8, 0.8}, "eta cut"}; + Configurable> cfgEtaNch{"cfgEtaNch", {-0.5, 0.5}, "eta cut for nch selection"}; + Configurable> cfgEtaPtPt{"cfgEtaPtPt", {-0.5, 0.5}, "eta for pt-pt correlation"}; + Configurable> cfgPtCut{"cfgPtCut", {0.2, 5.0}, "minimum and maximum pt (GeV/c)"}; + } cfgKinematics; + Configurable> cfgPtPtGaps{"cfgPtPtGaps", {LongArrayDouble.front().data(), 4, 2, {"subevent 1", "subevent 2", "subevent 3", "subevent 4"}, {"etamin", "etamax"}}, "{etamin,etamax} for all ptpt-subevents"}; + struct : ConfigurableGroup { + Configurable cfgDCAxyNSigma{"cfgDCAxyNSigma", 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"}; + Configurable cfgDCAxyPtDep{"cfgDCAxyPtDep", "(0.0105 + 0.0350/(x^1.1))", "Functional form of pt-dependent 7 sigma DCAxy cut"}; + Configurable cfgDCAzPtDep{"cfgDCAzPtDep", "(0.0105 + 0.0350/(x^1.1))", "Functional form of pt-dependent DCAz cut"}; + Configurable cfgDCAz{"cfgDCAz", 2, "Cut on DCA in the longitudinal direction (cm)"}; + Configurable cfgNTPCCls{"cfgNTPCCls", 50, "Cut on number of TPC clusters found"}; + Configurable cfgNTPCXrows{"cfgNTPCXrows", 70, "Cut on number of TPC crossed rows"}; + Configurable cfgMinNITSCls{"cfgMinNITSCls", 5, "Cut on minimum number of ITS clusters found"}; + Configurable cfgChi2PrITSCls{"cfgChi2PrITSCls", 36, "Cut on chi^2 per ITS clusters found"}; + Configurable cfgChi2PrTPCCls{"cfgChi2PrTPCCls", 2.5, "Cut on chi^2 per TPC clusters found"}; + Configurable cfgTPCSectorCut{"cfgTPCSectorCut", false, "Cut on pt-phi distribution"}; + } cfgTrackCuts; + struct : ConfigurableGroup { + Configurable cfgNoSameBunchPileupCut{"cfgNoSameBunchPileupCut", true, "NoSameBunchPileupCut"}; + Configurable cfgIsGoodZvtxFT0vsPV{"cfgIsGoodZvtxFT0vsPV", true, "IsGoodZvtxFT0vsPV"}; + Configurable cfgIsGoodITSLayersAll{"cfgIsGoodITSLayersAll", true, "IsGoodITSLayersAll"}; + Configurable cfgNoCollInTimeRangeStandard{"cfgNoCollInTimeRangeStandard", true, "NoCollInTimeRangeStandard"}; + Configurable cfgNoCollInRofStandard{"cfgNoCollInRofStandard", true, "NoCollInRofStandard"}; + Configurable cfgNoHighMultCollInPrevRof{"cfgNoHighMultCollInPrevRof", true, "NoHighMultCollInPrevRof"}; + Configurable cfgNoITSROFrameBorder{"cfgNoITSROFrameBorder", true, "NoITSROFrameBorder"}; + Configurable cfgNoTimeFrameBorder{"cfgNoTimeFrameBorder", true, "NoTimeFrameBorder"}; + Configurable cfgTVXinTRD{"cfgTVXinTRD", true, "TVXinTRD - Use TVXinTRD (reject TRD triggered events)"}; + Configurable cfgIsVertexITSTPC{"cfgIsVertexITSTPC", true, "IsVertexITSTPC - Selects collisions with at least one ITS-TPC track"}; + } cfgEventCutFlags; + struct : ConfigurableGroup { + Configurable cfgOccupancySelection{"cfgOccupancySelection", 2000, "Max occupancy selection, -999 to disable"}; + Configurable cfgDoOccupancySel{"cfgDoOccupancySel", true, "Bool for event selection on detector occupancy"}; + Configurable cfgMagField{"cfgMagField", 99999, "Configurable magnetic field; default CCDB will be queried"}; + Configurable cfgMultCut{"cfgMultCut", false, "Use additional event cut on mult correlations"}; + Configurable cfgVtxZ{"cfgVtxZ", 10, "vertex cut (cm)"}; + } cfgEventSelection; + struct : ConfigurableGroup { + Configurable> cfgMultGlobalCutPars{"cfgMultGlobalCutPars", std::vector{2272.16, -76.6932, 1.01204, -0.00631545, 1.59868e-05, 136.336, -4.97006, 0.121199, -0.0015921, 7.66197e-06}, "Global vs FT0C multiplicity cut parameter values"}; + Configurable> cfgMultPVCutPars{"cfgMultPVCutPars", std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}, "PV vs FT0C multiplicity cut parameter values"}; + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.223013, 0.715849, 0.664242, 0.0829653, -0.000503733, 1.21185e-06}, "Global vs PV multiplicity cut parameter values"}; + Configurable cfgMultCorrHighCutFunction{"cfgMultCorrHighCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"}; + Configurable cfgMultCorrLowCutFunction{"cfgMultCorrLowCutFunction", "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"}; + Configurable cfgMultGlobalPVCorrCutFunction{"cfgMultGlobalPVCorrCutFunction", "[0] + [1]*x + 3*([2] + [3]*x + [4]*x*x + [5]*x*x*x)", "Functional for global vs pv multiplicity correlation cut"}; + Configurable cfgMultGlobalASideCorrCutFunction{"cfgMultGlobalASideCorrCutFunction", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x + [10]*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", "Functional for global vs V0A multiplicity low correlation cut"}; + Configurable> cfgMultGlobalV0ACutPars{"cfgMultGlobalV0ACutPars", std::vector{567.785, 172.715, 0.77888, -0.00693466, 1.40564e-05, 679.853, 66.8068, -0.444332, 0.00115002, -4.92064e-07}, "Global vs FV0A multiplicity cut parameter values"}; + Configurable> cfgMultGlobalT0ACutPars{"cfgMultGlobalT0ACutPars", std::vector{241.618, 61.8402, 0.348049, -0.00306078, 6.20357e-06, 315.235, 29.1491, -0.188639, 0.00044528, -9.08912e-08}, "Global vs FT0A multiplicity cut parameter values"}; + Configurable cfgGlobalV0ALowSigma{"cfgGlobalV0ALowSigma", -3, "Number of sigma deviations below expected value in global vs V0A correlation"}; + Configurable cfgGlobalV0AHighSigma{"cfgGlobalV0AHighSigma", 4, "Number of sigma deviations above expected value in global vs V0A correlation"}; + Configurable cfgGlobalT0ALowSigma{"cfgGlobalT0ALowSigma", -3., "Number of sigma deviations below expected value in global vs T0A correlation"}; + Configurable cfgGlobalT0AHighSigma{"cfgGlobalT0AHighSigma", 4, "Number of sigma deviations above expected value in global vs T0A correlation"}; + } cfgMultCorrCuts; + + Configurable cfgGFWBinning{"cfgGFWBinning", {40, 16, 72, 300, 0, 3000, 0.2, 10.0, 0.2, 3.0, {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5, 5.5, 6, 7, 8, 9, 10}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}}, "Configuration for binning"}; + Configurable cfgRegions{"cfgRegions", {{"refN", "refP", "refFull"}, {-0.8, 0.4, -0.8}, {-0.4, 0.8, 0.8}, {0, 0, 0}, {1, 1, 1}}, "Configurations for GFW regions"}; + + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}", "refFull {2 -2}", "refFull {2 2 -2 -2}"}, {"ChGap22", "ChGap32", "ChGap42", "ChFull22", "ChFull24"}, {0, 0, 0, 0, 0}, {1, 1, 1, 0, 0}}, "Configurations for each correlation to calculate"}; + + struct GFWMemberCache { + std::vector ptbinning = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}; + float ptpoilow = 0.2; + float ptpoiup = 10.0; + float ptreflow = 0.2; + float ptrefup = 3.0; + float ptlow = 0.2; + float ptup = 10.0; + int etabins = 16; + float etalow = -0.8; + float etaup = 0.8; + int vtxZbins = 40; + int phibins = 72; + float philow = 0.0; + float phiup = o2::constants::math::TwoPI; + int nchbins = 300; + float nchlow = 0; + float nchup = 3000; + std::vector centbinning = std::vector(90); + GFWRegions regions; + GFWCorrConfigs configs; + std::vector> etagapsPtPt; + std::vector multGlobalCorrCutPars; + std::vector multPVCorrCutPars; + std::vector multGlobalPVCorrCutPars; + std::vector multGlobalV0ACutPars; + std::vector multGlobalT0ACutPars; + } gfwMemberCache; + + // Connect to ccdb + Service ccdb{}; + + struct Config { + TH1* mEfficiency = nullptr; + TH2* mNchResponse = nullptr; + std::vector mAcceptance; + bool correctionsLoaded = false; + } correctionsConfig; + + struct MultipletConfig { + std::size_t representativeConfigIndex = 0; + int order = 0; + std::shared_ptr profile; + }; + + std::map effectiveMultiplets; + + // Outputs + OutputObj fFC{FlowContainer("FlowContainer")}; + OutputObj fFCgen{FlowContainer("FlowContainer_gen")}; + OutputObj fFCpt{FlowPtContainer("FlowPtContainer")}; + HistogramRegistry registry{"registry"}; + + // Enums + enum CentEstimators { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M, + CentFV0A, + CentNTPV, + CentNGlobal, + CentMFT + }; + std::map centNamesMap = {{CentFT0C, "FT0C"}, {CentFT0CVariant1, "FT0C variant1"}, {CentFT0M, "FT0M"}, {CentFV0A, "FV0A"}, {CentNTPV, "NTPV"}, {CentNGlobal, "NGlobal"}, {CentMFT, "MFT"}}; + enum EventSelFlags { + FilteredEvent = 1, + Sel8, + Occupancy, + TVXinTRD, + NoSameBunchPileup, + IsGoodZvtxFT0vsPV, + NoCollInTimeRangeStandard, + NoCollInRofStandard, + NoHighMultCollInPrevRof, + NoTimeFrameBorder, + NoITSROFrameBorder, + IsVertexITSTPC, + IsGoodITSLayersAll, + MultCuts, + TrackCent + }; + struct EventCut { + bool enabled; + int histBin; + int flag; // just store the enum + }; + std::vector eventcutflags; + enum ParticleIDs { + ChargedID = 0, + PionID, + KaonID, + ProtonID, + SpeciesCount + }; + enum NchSelector { + TableSize, + Corrected, + Uncorrected, + ResponseMatrixCorrected + }; + + // Generic Framework + GFW* fGFW = new GFW(); + std::vector corrconfigs; + TRandom3* fRndm = new TRandom3(0); + TAxis* fPtAxis = nullptr; + int lastRun = -1; + std::vector multipletKeys; + + // Track selection - DCA functions + TF1* fPtDepDCAxy = nullptr; + TF1* fPtDepDCAz = nullptr; + + // Event selection cuts - multiplicity correlation + TF1* fMultPVCutLow = nullptr; + TF1* fMultPVCutHigh = nullptr; + TF1* fMultCutLow = nullptr; + TF1* fMultCutHigh = nullptr; + TF1* fMultPVGlobalCutHigh = nullptr; + TF1* fMultGlobalV0ACutLow = nullptr; + TF1* fMultGlobalV0ACutHigh = nullptr; + TF1* fMultGlobalT0ACutLow = nullptr; + TF1* fMultGlobalT0ACutHigh = nullptr; + + // Track selection - pt-phi cuts + TF1* fPhiCutLow = nullptr; + TF1* fPhiCutHigh = nullptr; + + void init(InitContext const&) + { + gfwMemberCache.regions.SetNames(cfgRegions->GetNames()); + gfwMemberCache.regions.SetEtaMin(cfgRegions->GetEtaMin()); + gfwMemberCache.regions.SetEtaMax(cfgRegions->GetEtaMax()); + gfwMemberCache.regions.SetpTDifs(cfgRegions->GetpTDifs()); + gfwMemberCache.regions.SetBitmasks(cfgRegions->GetBitmasks()); + gfwMemberCache.configs.SetCorrs(cfgCorrConfig->GetCorrs()); + gfwMemberCache.configs.SetHeads(cfgCorrConfig->GetHeads()); + gfwMemberCache.configs.SetpTDifs(cfgCorrConfig->GetpTDifs()); + gfwMemberCache.configs.SetpTCorrMasks(cfgCorrConfig->GetpTCorrMasks()); + gfwMemberCache.regions.Print(); + gfwMemberCache.configs.Print(); + gfwMemberCache.ptbinning = cfgGFWBinning->GetPtBinning(); + gfwMemberCache.ptpoilow = cfgGFWBinning->GetPtPOImin(); + gfwMemberCache.ptpoiup = cfgGFWBinning->GetPtPOImax(); + gfwMemberCache.ptreflow = cfgGFWBinning->GetPtRefMin(); + gfwMemberCache.ptrefup = cfgGFWBinning->GetPtRefMax(); + gfwMemberCache.ptlow = cfgKinematics.cfgPtCut->first; + gfwMemberCache.ptup = cfgKinematics.cfgPtCut->second; + gfwMemberCache.etabins = cfgGFWBinning->GetEtaBins(); + gfwMemberCache.vtxZbins = cfgGFWBinning->GetVtxZbins(); + gfwMemberCache.phibins = cfgGFWBinning->GetPhiBins(); + gfwMemberCache.philow = 0.0f; + gfwMemberCache.phiup = o2::constants::math::TwoPI; + gfwMemberCache.nchbins = cfgGFWBinning->GetNchBins(); + gfwMemberCache.nchlow = cfgGFWBinning->GetNchMin(); + gfwMemberCache.nchup = cfgGFWBinning->GetNchMax(); + gfwMemberCache.centbinning = cfgGFWBinning->GetCentBinning(); + cfgGFWBinning->Print(); + gfwMemberCache.multGlobalCorrCutPars = cfgMultCorrCuts.cfgMultGlobalCutPars; + gfwMemberCache.multPVCorrCutPars = cfgMultCorrCuts.cfgMultPVCutPars; + gfwMemberCache.multGlobalPVCorrCutPars = cfgMultCorrCuts.cfgMultGlobalPVCutPars; + gfwMemberCache.multGlobalV0ACutPars = cfgMultCorrCuts.cfgMultGlobalV0ACutPars; + gfwMemberCache.multGlobalT0ACutPars = cfgMultCorrCuts.cfgMultGlobalT0ACutPars; + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + // Setup event cuts + eventcutflags.push_back({cfgEventCutFlags.cfgNoSameBunchPileupCut, NoSameBunchPileup, o2::aod::evsel::kNoSameBunchPileup}); + eventcutflags.push_back({cfgEventCutFlags.cfgIsGoodZvtxFT0vsPV, IsGoodZvtxFT0vsPV, o2::aod::evsel::kIsGoodZvtxFT0vsPV}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoCollInTimeRangeStandard, NoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInTimeRangeStandard}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoCollInRofStandard, NoCollInRofStandard, o2::aod::evsel::kNoCollInRofStandard}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoHighMultCollInPrevRof, NoHighMultCollInPrevRof, o2::aod::evsel::kNoHighMultCollInPrevRof}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoTimeFrameBorder, NoTimeFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}); + eventcutflags.push_back({cfgEventCutFlags.cfgNoITSROFrameBorder, NoITSROFrameBorder, o2::aod::evsel::kNoITSROFrameBorder}); + eventcutflags.push_back({cfgEventCutFlags.cfgIsVertexITSTPC, IsVertexITSTPC, o2::aod::evsel::kIsVertexITSTPC}); + eventcutflags.push_back({cfgEventCutFlags.cfgIsGoodITSLayersAll, IsGoodITSLayersAll, o2::aod::evsel::kIsGoodITSLayersAll}); + for (const auto& cut : eventcutflags) { + LOGF(info, "Flag %d is %senabled", cut.histBin, (cut.enabled) ? "" : "not "); + } + + AxisSpec phiAxis = {gfwMemberCache.phibins, gfwMemberCache.philow, gfwMemberCache.phiup, "#phi"}; + AxisSpec phiModAxis = {100, 0, constants::math::PI / 9, "fmod(#varphi,#pi/9)"}; + AxisSpec etaAxis = {gfwMemberCache.etabins, cfgKinematics.cfgEta->first, cfgKinematics.cfgEta->second, "#eta"}; + AxisSpec vtxAxis = {gfwMemberCache.vtxZbins, -cfgEventSelection.cfgVtxZ, cfgEventSelection.cfgVtxZ, "Vtx_{z} (cm)"}; + AxisSpec ptAxis = {gfwMemberCache.ptbinning, "#it{p}_{T} GeV/#it{c}"}; + std::string sCentralityEstimator = centNamesMap[cfgCentEstimator] + " centrality (%)"; + AxisSpec centAxis = {gfwMemberCache.centbinning, sCentralityEstimator.c_str()}; + std::vector nchbinning; + const double nchskip = (gfwMemberCache.nchup - gfwMemberCache.nchlow) / gfwMemberCache.nchbins; + for (int i = 0; i <= gfwMemberCache.nchbins; ++i) { + nchbinning.push_back(nchskip * i + gfwMemberCache.nchlow + 0.5); + } + AxisSpec nchAxis = {nchbinning, "N_{ch}"}; + AxisSpec bAxis = {200, 0, 20, "#it{b}"}; + AxisSpec t0cAxis = {1000, 0, 50000, "N_{ch} (T0C)"}; + AxisSpec t0aAxis = {1800, 0, 180000, "N_{ch} (T0A)"}; + AxisSpec v0aAxis = {1800, 0, 180000, "N_{ch} (V0A)"}; + AxisSpec multpvAxis = {3500, 0, 3500, "N_{ch} (PV)"}; + AxisSpec occAxis = {500, 0, 5000, "occupancy"}; + AxisSpec multAxis = (cfgUseNch) ? nchAxis : centAxis; + AxisSpec dcaZAXis = {200, -2, 2, "DCA_{z} (cm)"}; + AxisSpec dcaXYAXis = {200, -1, 1, "DCA_{xy} (cm)"}; + + if (cfgFillQA) { + if (doprocessMCGen) { + registry.add("MCGen/before/phi_eta_vtxZ_gen", "; #varphi; #eta; V_{z}", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registry.add("MCGen/before/pt_gen", "; #it{p}_{T}", {HistType::kTH1D, {ptAxis}}); + registry.addClone("MCGen/before/", "MCGen/after/"); + } else { + registry.add("trackQA/before/phi_eta_vtxZ", "; #varphi; #eta; V_{z}", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); + registry.add("trackQA/before/pt_dcaXY_dcaZ", "; #it{p}_{T}; DCA_#it{xy}; DCA_#it{z}", {HistType::kTH3D, {ptAxis, dcaXYAXis, dcaZAXis}}); + registry.add("trackQA/before/pt_phi", "; #it{p}_{T}; fmod(#varphi,#pi/9)", {HistType::kTH2D, {ptAxis, phiModAxis}}); + registry.add("trackQA/before/chi2prTPCcls", "#chi^{2}/cluster for the TPC track segment", {HistType::kTH1D, {{100, 0., 5.}}}); + registry.add("trackQA/before/chi2prITScls", "#chi^{2}/cluster for the ITS track", {HistType::kTH1D, {{100, 0., 50.}}}); + registry.add("trackQA/before/nTPCClusters", "Number of found TPC clusters", {HistType::kTH1D, {{100, 40, 180}}}); + registry.add("trackQA/before/nITSClusters", "Number of found ITS clusters", {HistType::kTH1D, {{100, 0, 20}}}); + registry.add("trackQA/before/nTPCCrossedRows", "Number of crossed TPC Rows", {HistType::kTH1D, {{100, 40, 180}}}); + registry.addClone("trackQA/before/", "trackQA/after/"); + registry.add("trackQA/after/pt_ref", "; #it{p}_{T}; Counts", {HistType::kTH1D, {{100, gfwMemberCache.ptreflow, gfwMemberCache.ptrefup}}}); + registry.add("trackQA/after/pt_poi", "; #it{p}_{T}; Counts", {HistType::kTH1D, {{100, gfwMemberCache.ptpoilow, gfwMemberCache.ptpoiup}}}); + registry.add("trackQA/after/Nch_corrected", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); + registry.add("trackQA/after/Nch_uncorrected", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); + registry.add("trackQA/after/etaNch", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); + registry.add("trackQA/after/etaPtPt", "; #eta; Counts", {HistType::kTH1D, {etaAxis}}); + + registry.add("eventQA/before/globalTracks_centT0C", "; FT0C centrality (%); N_{global}", {HistType::kTH2D, {centAxis, nchAxis}}); + registry.add("eventQA/before/PVTracks_centT0C", "; FT0C centrality (%); N_{PV}", {HistType::kTH2D, {centAxis, multpvAxis}}); + registry.add("eventQA/before/globalTracks_PVTracks", "; N_{PV}; N_{global}", {HistType::kTH2D, {multpvAxis, nchAxis}}); + registry.add("eventQA/before/globalTracks_multT0A", "; multT0A; N_{global}", {HistType::kTH2D, {t0aAxis, nchAxis}}); + registry.add("eventQA/before/globalTracks_multV0A", "; multV0A; N_{global}", {HistType::kTH2D, {v0aAxis, nchAxis}}); + registry.add("eventQA/before/multV0A_multT0A", "; multT0A; multV0A", {HistType::kTH2D, {t0aAxis, v0aAxis}}); + registry.add("eventQA/before/multT0C_centT0C", "; FT0C centrality (%); multT0C", {HistType::kTH2D, {centAxis, t0cAxis}}); + registry.add("eventQA/before/occ_mult_cent", "; occupancy; N_{ch}; centrality (%)", {HistType::kTH3D, {occAxis, nchAxis, centAxis}}); + } + } + if (doprocessMCReco) { + registry.add("MCReco/Nch_reco_gen", "; N_{ch}^{reco}; N_{ch}^{gen}", {HistType::kTH2D, {nchAxis, nchAxis}}); + } + registry.add("eventQA/before/centrality", "; centrality (%); Counts", {HistType::kTH1D, {centAxis}}); + registry.add("eventQA/before/multiplicity", "; N_{ch}; Counts", {HistType::kTH1D, {nchAxis}}); + registry.addClone("eventQA/before/", "eventQA/after/"); + registry.add("eventQA/eventSel", "Number of Events;; Counts", {HistType::kTH1D, {{15, 0.5, 15.5}}}); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(FilteredEvent, "Filtered event"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(Sel8, "sel8"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(Occupancy, "occupancy"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(TVXinTRD, "TVXinTRD"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoSameBunchPileup, "NoSameBunchPileup"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(IsGoodZvtxFT0vsPV, "IsGoodZvtxFT0vsPV"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoCollInTimeRangeStandard, "NoCollInTimeRangeStandard"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoCollInRofStandard, "NoCollInRofStandard"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoHighMultCollInPrevRof, "NoHighMultCollInPrevRof"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoTimeFrameBorder, "NoTimeFrameBorder"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(NoITSROFrameBorder, "NoITSROFrameBorder"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(IsVertexITSTPC, "IsVertexITSTPC"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(IsGoodITSLayersAll, "IsGoodITSLayersAll"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(MultCuts, "after Mult cuts"); + registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(TrackCent, "has track + within cent"); + + const int ptbins = static_cast(gfwMemberCache.ptbinning.size() - 1); + fPtAxis = new TAxis(ptbins, gfwMemberCache.ptbinning.data()); + + if (gfwMemberCache.regions.GetSize() < 0) { + LOGF(error, "Configuration contains vectors of different size - check the GFWRegions configurable"); + } + for (auto i(0); i < gfwMemberCache.regions.GetSize(); ++i) { + fGFW->AddRegion(gfwMemberCache.regions.GetNames()[i], gfwMemberCache.regions.GetEtaMin()[i], gfwMemberCache.regions.GetEtaMax()[i], (gfwMemberCache.regions.GetpTDifs()[i] != 0) ? ptbins + 1 : 1, gfwMemberCache.regions.GetBitmasks()[i]); + } + for (auto i = 0; i < gfwMemberCache.configs.GetSize(); ++i) { + corrconfigs.push_back(fGFW->GetCorrelatorConfig(gfwMemberCache.configs.GetCorrs()[i], gfwMemberCache.configs.GetHeads()[i], gfwMemberCache.configs.GetpTDifs()[i] != 0)); + } + if (corrconfigs.empty()) { + LOGF(error, "Configuration contains vectors of different size - check the GFWCorrConfig configurable"); + } + fGFW->CreateRegions(); + auto oba = new TObjArray(); + addConfigObjectsToObjArray(oba, corrconfigs); + fFC->SetName("FlowContainer"); + fFC->SetXAxis(fPtAxis); + fFC->Initialize(oba, multAxis, cfgNbootstrap); + fFCgen->SetName("FlowContainer_gen"); + fFCgen->SetXAxis(fPtAxis); + fFCgen->Initialize(oba, multAxis, cfgNbootstrap); + delete oba; + + // Identify unique multiplet setups and populate with identifying keys + identifyUniqueMultipletKeys(multipletKeys, corrconfigs, multAxis); + LOGF(info, "Unique multiplet keys for current configuration setup"); + for (const auto& key : multipletKeys) { + LOGF(info, key); + } + + const auto& ptPtGaps = cfgPtPtGaps->getData(); + for (uint32_t i = 0; i < ptPtGaps.rows; ++i) { + const auto etaMin = ptPtGaps(i, 0); + const auto etaMax = ptPtGaps(i, 1); + + if (etaMin < -1. || etaMax < -1.) { + continue; + } + + gfwMemberCache.etagapsPtPt.emplace_back(etaMin, etaMax); + } + for (const auto& [etamin, etamax] : gfwMemberCache.etagapsPtPt) { + LOGF(info, "pt-pt subevent: {%.1f,%.1f}", etamin, etamax); + } + + fFCpt->setUseCentralMoments(cfgUseCentralMoments); + fFCpt->setUseGapMethod(true); + fFCpt->initialise(multAxis, cfgMpar, gfwMemberCache.configs, cfgNbootstrap); + fFCpt->initialiseSubevent(multAxis, cfgMpar, gfwMemberCache.etagapsPtPt.size(), cfgNbootstrap); + + fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgTrackCuts.cfgDCAxyPtDep->c_str()), 0.001, 100); + fPtDepDCAxy->SetParameter(0, cfgTrackCuts.cfgDCAxyNSigma / 7.); + LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", cfgTrackCuts.cfgDCAxyPtDep->c_str())); + if (!cfgTrackCuts.cfgDCAzPtDep.value.empty()) { + fPtDepDCAz = new TF1("ptDepDCAz", Form("%s", cfgTrackCuts.cfgDCAzPtDep->c_str()), 0.001, 100); + LOGF(info, "DCAz pt-dependence function: %s", Form("%s", cfgTrackCuts.cfgDCAzPtDep->c_str())); + } + + // Multiplicity correlation cuts + if (cfgEventSelection.cfgMultCut) { + fMultPVCutLow = new TF1("fMultPVCutLow", cfgMultCorrCuts.cfgMultCorrLowCutFunction->c_str(), 0, 100); + fMultPVCutLow->SetParameters(gfwMemberCache.multPVCorrCutPars.data()); + fMultPVCutHigh = new TF1("fMultPVCutHigh", cfgMultCorrCuts.cfgMultCorrHighCutFunction->c_str(), 0, 100); + fMultPVCutHigh->SetParameters(gfwMemberCache.multPVCorrCutPars.data()); + fMultCutLow = new TF1("fMultCutLow", cfgMultCorrCuts.cfgMultCorrLowCutFunction->c_str(), 0, 100); + fMultCutLow->SetParameters(gfwMemberCache.multGlobalCorrCutPars.data()); + fMultCutHigh = new TF1("fMultCutHigh", cfgMultCorrCuts.cfgMultCorrHighCutFunction->c_str(), 0, 100); + fMultCutHigh->SetParameters(gfwMemberCache.multGlobalCorrCutPars.data()); + fMultPVGlobalCutHigh = new TF1("fMultPVGlobalCutHigh", cfgMultCorrCuts.cfgMultGlobalPVCorrCutFunction->c_str(), 0, nchbinning.back()); + fMultPVGlobalCutHigh->SetParameters(gfwMemberCache.multGlobalPVCorrCutPars.data()); + + LOGF(info, "Global V0A function: %s in range 0-%g", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), v0aAxis.binEdges.back()); + fMultGlobalV0ACutLow = new TF1("fMultGlobalV0ACutLow", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, v0aAxis.binEdges.back()); + for (std::size_t i = 0; i < gfwMemberCache.multGlobalV0ACutPars.size(); ++i) { + fMultGlobalV0ACutLow->SetParameter(i, gfwMemberCache.multGlobalV0ACutPars[i]); + } + fMultGlobalV0ACutLow->SetParameter(gfwMemberCache.multGlobalV0ACutPars.size(), cfgMultCorrCuts.cfgGlobalV0ALowSigma); + for (int i = 0; i < fMultGlobalV0ACutLow->GetNpar(); ++i) { + LOGF(info, "fMultGlobalV0ACutLow par %d = %g", i, fMultGlobalV0ACutLow->GetParameter(i)); + } + + fMultGlobalV0ACutHigh = new TF1("fMultGlobalV0ACutHigh", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, v0aAxis.binEdges.back()); + for (std::size_t i = 0; i < gfwMemberCache.multGlobalV0ACutPars.size(); ++i) { + fMultGlobalV0ACutHigh->SetParameter(i, gfwMemberCache.multGlobalV0ACutPars[i]); + } + fMultGlobalV0ACutHigh->SetParameter(gfwMemberCache.multGlobalV0ACutPars.size(), cfgMultCorrCuts.cfgGlobalV0AHighSigma); + for (int i = 0; i < fMultGlobalV0ACutHigh->GetNpar(); ++i) { + LOGF(info, "fMultGlobalV0ACutHigh par %d = %g", i, fMultGlobalV0ACutHigh->GetParameter(i)); + } + + LOGF(info, "Global T0A function: %s", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str()); + fMultGlobalT0ACutLow = new TF1("fMultGlobalT0ACutLow", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, t0aAxis.binEdges.back()); + for (std::size_t i = 0; i < gfwMemberCache.multGlobalT0ACutPars.size(); ++i) { + fMultGlobalT0ACutLow->SetParameter(i, gfwMemberCache.multGlobalT0ACutPars[i]); + } + fMultGlobalT0ACutLow->SetParameter(gfwMemberCache.multGlobalT0ACutPars.size(), cfgMultCorrCuts.cfgGlobalT0ALowSigma); + for (int i = 0; i < fMultGlobalT0ACutLow->GetNpar(); ++i) { + LOGF(info, "fMultGlobalT0ACutLow par %d = %g", i, fMultGlobalT0ACutLow->GetParameter(i)); + } + + fMultGlobalT0ACutHigh = new TF1("fMultGlobalT0ACutHigh", cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->c_str(), 0, t0aAxis.binEdges.back()); + for (std::size_t i = 0; i < gfwMemberCache.multGlobalT0ACutPars.size(); ++i) { + fMultGlobalT0ACutHigh->SetParameter(i, gfwMemberCache.multGlobalT0ACutPars[i]); + } + fMultGlobalT0ACutHigh->SetParameter(gfwMemberCache.multGlobalT0ACutPars.size(), cfgMultCorrCuts.cfgGlobalT0AHighSigma); + for (int i = 0; i < fMultGlobalT0ACutHigh->GetNpar(); ++i) { + LOGF(info, "fMultGlobalT0ACutHigh par %d = %g", i, fMultGlobalT0ACutHigh->GetParameter(i)); + } + } + } + + static constexpr std::array FillTimeName = {"before/", "after/"}; + + void addConfigObjectsToObjArray(TObjArray* oba, const std::vector& configs) + { + for (auto it = configs.begin(); it != configs.end(); ++it) { + if (it->pTDif) { + std::string suffix = "_ptDiff"; + for (auto i = 0; i < fPtAxis->GetNbins(); ++i) { + std::string index = Form("_pt_%i", i + 1); + oba->Add(new TNamed(it->Head + index, it->Head + suffix)); + } + } else { + oba->Add(new TNamed(it->Head.c_str(), it->Head.c_str())); + } + } + } + + void identifyUniqueMultipletKeys(std::vector& keys, const std::vector& configs, const AxisSpec& xAxis) + { + std::set uniqueKeys(keys.begin(), keys.end()); + const auto& regionNames = gfwMemberCache.regions.GetNames(); + + for (std::size_t configIndex = 0; configIndex < configs.size(); ++configIndex) { + const auto& config = configs[configIndex]; + // TODO: Support pT-differential and explicit-overlap configurations. + if (config.pTDif) { + continue; + } + + if (config.Regs.size() != config.Hars.size()) { + LOGF(error, "Cannot construct multiplet key for %s: %zu region groups but %zu harmonic groups", config.Head.c_str(), config.Regs.size(), config.Hars.size()); + continue; + } + + std::string key; + int totalOrder = 0; + bool valid = true; + + for (std::size_t group = 0; group < config.Regs.size(); ++group) { + const auto& regionIndices = config.Regs[group]; + const auto& harmonics = config.Hars[group]; + + if (regionIndices.size() != 1 || harmonics.empty()) { + LOGF(error, "Cannot construct simple multiplet key for %s, group %zu: expected one region and at least one harmonic", config.Head.c_str(), group); + valid = false; + break; + } + + const int regionIndex = regionIndices.front(); + if (regionIndex < 0 || + static_cast(regionIndex) >= regionNames.size()) { + LOGF(error, "Invalid region index %d in configuration %s", regionIndex, config.Head.c_str()); + valid = false; + break; + } + + const int regionOrder = static_cast(harmonics.size()); + totalOrder += regionOrder; + + if (!key.empty()) { + key += "_"; + } + + key += regionNames[regionIndex]; + key += "_"; + key += std::to_string(regionOrder); + } + + if (!valid || key.empty()) { + continue; + } + + key += "_"; + key += std::to_string(totalOrder); + + if (uniqueKeys.insert(key).second) { + keys.push_back(std::move(key)); + MultipletConfig currentMultipletConfig{.representativeConfigIndex = configIndex, .order = totalOrder, .profile = registry.add(Form("%s", keys.back().c_str()), Form("; centrality ; N_{%d}", totalOrder), {HistType::kTProfile, {xAxis}})}; + effectiveMultiplets[keys.back()] = currentMultipletConfig; + } + } + } + + int getMagneticField(uint64_t timestamp) + { + // TODO done only once (and not per run). Will be replaced by CCDBConfigurable + // static o2::parameters::GRPObject* grpo = nullptr; + static o2::parameters::GRPMagField* grpo = nullptr; + if (grpo == nullptr) { + // grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); + grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + } + return grpo->getNominalL3Field(); + } + + void loadCorrections(aod::BCsWithTimestamps::iterator const& bc) + { + uint64_t timestamp = bc.timestamp(); + if (!cfgRunByRun && correctionsConfig.correctionsLoaded) { + return; + } + if (!cfgCorrections.cfgAcceptancePath.value.empty()) { + std::string runstr = (cfgRunByRun) ? "RunByRun/" : ""; + correctionsConfig.mAcceptance.clear(); + correctionsConfig.mAcceptance.push_back(ccdb->getForTimeStamp(cfgCorrections.cfgAcceptancePath.value + runstr, timestamp)); + } + // Run-by-run efficiencies are not supported at the moment + if (correctionsConfig.correctionsLoaded) { + return; + } + if (!cfgCorrections.cfgEfficiencyPath.value.empty()) { + if (cfgCorrections.cfgUse2DEfficiency) { + correctionsConfig.mEfficiency = ccdb->getForTimeStamp(cfgCorrections.cfgEfficiencyPath, timestamp); + } else { + correctionsConfig.mEfficiency = ccdb->getForTimeStamp(cfgCorrections.cfgEfficiencyPath, timestamp); + } + if (correctionsConfig.mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram from %s", cfgCorrections.cfgEfficiencyPath.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgCorrections.cfgEfficiencyPath.value.c_str()); + } + if (!cfgCorrections.cfgNchResponsePath.value.empty()) { + correctionsConfig.mNchResponse = ccdb->getForTimeStamp(cfgCorrections.cfgNchResponsePath, timestamp); + if (correctionsConfig.mNchResponse == nullptr) { + LOGF(fatal, "Could not load Nch response matrix from %s", cfgCorrections.cfgNchResponsePath.value.c_str()); + } + LOGF(info, "Loaded Nch response matrix from %s", cfgCorrections.cfgNchResponsePath.value.c_str()); + } else if (cfgUseNchCorrection == NchSelector::ResponseMatrixCorrected) { + LOGF(fatal, "cfgUseNchCorrection=3 requires cfgNchResponsePath"); + } + correctionsConfig.correctionsLoaded = true; + } + + float getResponseCorrectedNch(const unsigned int multReconstructed) const + { + if (!correctionsConfig.mNchResponse) { + return multReconstructed; + } + const auto* response = correctionsConfig.mNchResponse; + const int recoBin = response->GetXaxis()->FindFixBin(multReconstructed); + if (recoBin < 1 || recoBin > response->GetNbinsX()) { + LOGF(warn, "Reconstructed Nch %u is outside the response matrix; using the uncorrected value", multReconstructed); + return multReconstructed; + } + double sumWeights = 0.; + double sumGeneratedNch = 0.; + for (int genBin = 1; genBin <= response->GetNbinsY(); ++genBin) { + const double weight = response->GetBinContent(recoBin, genBin); + sumWeights += weight; + sumGeneratedNch += weight * response->GetYaxis()->GetBinCenter(genBin); + } + if (sumWeights <= 0.) { + LOGF(warn, "Response matrix has no entries for reconstructed Nch %u; using the uncorrected value", multReconstructed); + return multReconstructed; + } + return sumGeneratedNch / sumWeights; + } + + template + double getAcceptance(const TTrack& track, const double& vtxz) + { // 0 ref, 1 ch, 2 pi, 3 ka, 4 pr + double wacc = 1; + if (!correctionsConfig.mAcceptance.empty()) { + wacc = correctionsConfig.mAcceptance[0]->getNUA(track.phi(), track.eta(), vtxz); + } + return wacc; + } + + template + double getEfficiency(const TTrack& track, const float& centrality) + { //-1 ref, 0 ch, 1 pi, 2 ka, 3 pr, 4 k0, 5 lambda + + if (!correctionsConfig.mEfficiency) { + return 1; + } + + int bin = 0; + + if (cfgCorrections.cfgUse2DEfficiency) { + auto* effHist = dynamic_cast(correctionsConfig.mEfficiency); + if (!effHist) { + LOGF(error, "Efficiency object at %s is not a TH2D", cfgCorrections.cfgEfficiencyPath.value.c_str()); + return -1.; + } + bin = effHist->FindBin(track.pt(), centrality); + if (!bin) { + return -1.; + } + const double eff = effHist->GetBinContent(bin); + if (!std::isfinite(eff) || eff <= 0.) { + return -1.; + } + return 1. / eff; + } + + auto* effHist = dynamic_cast(correctionsConfig.mEfficiency); + if (!effHist) { + LOGF(error, "Efficiency object at %s is not a TH1D", cfgCorrections.cfgEfficiencyPath.value.c_str()); + return -1.; + } + bin = effHist->FindBin(track.pt()); + if (!bin) { + return -1.; + } + const double eff = effHist->GetBinContent(bin); + if (!std::isfinite(eff) || eff <= 0.) { + return -1.; + } + return 1. / eff; + } + + template + bool eventSelected(const TCollision& collision, const int multTrk, const float& centrality) + { + // Cut on trigger alias + if (cfgEventCutFlags.cfgTVXinTRD) { + if (collision.alias_bit(TVXinTRD)) { + // TRD triggered + // "CMTVX-B-NOPF-TRD,minbias_TVX" + return false; + } + registry.fill(HIST("eventQA/eventSel"), TVXinTRD); + } + // Cut on event selection flags + for (const auto& cut : eventcutflags) { + if (!cut.enabled) { + continue; + } + if (!collision.selection_bit(cut.flag)) { + return false; + } + registry.fill(HIST("eventQA/eventSel"), cut.histBin); + } + // Cut on vertex + if (!selectVertex(collision)) { + return false; + } + // Cut on multiplicity correlations - data driven + if (cfgEventSelection.cfgMultCut) { + if (!selectMultiplicityCorrelation(collision, multTrk, centrality)) { + return false; + } + } + return true; + } + + template + bool selectVertex(const TCollision& collision) + { + float vtxz = -999; + if (collision.numContrib() > 1) { + vtxz = collision.posZ(); + float zRes = std::sqrt(collision.covZZ()); + float minZRes = 0.25; + int minNContrib = 20; + if (zRes > minZRes && collision.numContrib() < minNContrib) { + vtxz = -999; + } + } + if (vtxz > cfgEventSelection.cfgVtxZ || vtxz < -cfgEventSelection.cfgVtxZ) { + return false; + } + + return true; + } + + template + bool selectMultiplicityCorrelation(const TCollision& collision, const int multTrk, const float& centrality) + { + auto multNTracksPV = collision.multNTracksPV(); + if (multNTracksPV < fMultPVCutLow->Eval(centrality)) { + return false; + } + if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) { + return false; + } + if (multTrk < fMultCutLow->Eval(centrality)) { + return false; + } + if (multTrk > fMultCutHigh->Eval(centrality)) { + return false; + } + if (multTrk > fMultPVGlobalCutHigh->Eval(collision.multNTracksPV())) { + return false; + } + + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) < fMultGlobalV0ACutLow->Eval(multTrk)) { + return false; + } + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFV0A()) > fMultGlobalV0ACutHigh->Eval(multTrk)) { + return false; + } + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) < fMultGlobalT0ACutLow->Eval(multTrk)) { + return false; + } + if (!(cfgMultCorrCuts.cfgMultGlobalASideCorrCutFunction->empty()) && static_cast(collision.multFT0A()) > fMultGlobalT0ACutHigh->Eval(multTrk)) { + return false; + } + registry.fill(HIST("eventQA/eventSel"), MultCuts); + return true; + } + + template + bool trackSelected(const TTrack& track, const int field) + { + if (cfgTrackCuts.cfgTPCSectorCut) { + double phimodn = track.phi(); + if (field < 0) { // for negative polarity field + phimodn = o2::constants::math::TwoPI - phimodn; + } + if (track.sign() < 0) { // for negative charge + phimodn = o2::constants::math::TwoPI - phimodn; + } + if (phimodn < 0) { + LOGF(warning, "phi < 0: %g", phimodn); + } + + phimodn += o2::constants::math::PI / 18.0; // to center gap in the middle + phimodn = fmod(phimodn, o2::constants::math::PI / 9.0); + if (cfgFillQA) { + registry.fill(HIST("trackQA/before/pt_phi"), track.pt(), phimodn); + } + if (phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt())) { + return false; // reject track + } + if (cfgFillQA) { + registry.fill(HIST("trackQA/after/pt_phi"), track.pt(), phimodn); + } + } + if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) { + return false; + } + if (!cfgTrackCuts.cfgDCAzPtDep.value.empty() && std::fabs(track.dcaZ() > fPtDepDCAz->Eval(track.pt()))) { + return false; + } + return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); + } + + template + bool nchSelected(const TTrack& track) + { + // Renormalise to default cut + const float defaultNsigma = 7; + if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > defaultNsigma / cfgTrackCuts.cfgDCAxyNSigma * fPtDepDCAxy->Eval(track.pt()))) { + return false; + } + if (!cfgTrackCuts.cfgDCAzPtDep.value.empty() && std::fabs(track.dcaZ() > fPtDepDCAz->Eval(track.pt()))) { + return false; + } + int tpcNClsCrossedRowsDefault = 70; + int tpcNClsFoundDefault = 50; + int itsNclsDefault = 5; + return ((track.tpcNClsCrossedRows() >= tpcNClsCrossedRowsDefault) && (track.tpcNClsFound() >= tpcNClsFoundDefault) && (track.itsNCls() >= itsNclsDefault)); + } + + enum DataType { + Reco, + Gen + }; + + template + void fillOutputContainers(const float& centmult, const double& rndm, const float& multiplicity) + { + fFCpt->calculateCorrelations(); + fFCpt->calculateSubeventCorrelations(); + fFCpt->fillPtProfiles(centmult, rndm); + fFCpt->fillSubeventPtProfiles(centmult, rndm); + fFCpt->fillCMProfiles(centmult, rndm); + fFCpt->fillCMSubeventProfiles(centmult, rndm); + + for (const auto& [key, multiplet] : effectiveMultiplets) { + const auto& config = corrconfigs.at(multiplet.representativeConfigIndex); + const double dnx = fGFW->Calculate(config, 0, true).real(); + + if (dnx > 0.) { + const double multPower = std::pow(multiplicity, multiplet.order - 1); + if (multPower > 0.) { + multiplet.profile->Fill(centmult, 1. / multPower, dnx); + } + } + } + + for (std::size_t l_ind = 0; l_ind < corrconfigs.size(); ++l_ind) { + if (!corrconfigs.at(l_ind).pTDif) { + auto dnx = fGFW->Calculate(corrconfigs.at(l_ind), 0, true).real(); + if (dnx == 0) { + continue; + } + auto val = fGFW->Calculate(corrconfigs.at(l_ind), 0, false).real() / dnx; + if (std::abs(val) < 1) { + fFC->FillProfile(corrconfigs.at(l_ind).Head.c_str(), centmult, val, cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); + fFCpt->fillVnPtProfiles(static_cast(l_ind), centmult, val, dnx, rndm, gfwMemberCache.configs.GetpTCorrMasks()[l_ind]); + } + continue; + } + for (int i = 1; i <= fPtAxis->GetNbins(); i++) { + auto dnx = fGFW->Calculate(corrconfigs.at(l_ind), i - 1, true).real(); + if (dnx == 0) { + continue; + } + auto val = fGFW->Calculate(corrconfigs.at(l_ind), i - 1, false).real() / dnx; + if (std::abs(val) < 1) { + fFC->FillProfile(Form("%s_pt_%i", corrconfigs.at(l_ind).Head.c_str(), i), centmult, val, cfgUseMultiplicityFlowWeights ? dnx : 1.0, rndm); + } + } + } + } + + template + inline void fillPtSums(const TTrack& track, const float& centrality) + { + double weff = (dt == Gen) ? 1. : getEfficiency(track, centrality); + if (weff < 0) { + return; + } + // Fill the nominal sums + if (track.eta() > cfgKinematics.cfgEtaPtPt->first && track.eta() < cfgKinematics.cfgEtaPtPt->second) { + fFCpt->fill(weff, track.pt()); + } + // Fill the subevent sums + std::size_t index = 0; + for (const auto& [etamin, etamax] : gfwMemberCache.etagapsPtPt) { + if (etamin < track.eta() && track.eta() < etamax) { + fFCpt->fillSub(weff, track.pt(), index); + } + ++index; + } + } + + template + inline void fillGFW(const TTrack& track, const float& centrality, const double& vtxz) + { + bool withinPtRef = (track.pt() > gfwMemberCache.ptreflow && track.pt() < gfwMemberCache.ptrefup); + bool withinPtPOI = (track.pt() > gfwMemberCache.ptpoilow && track.pt() < gfwMemberCache.ptpoiup); + if (!withinPtPOI && !withinPtRef) { + return; + } + double weff = (dt == Gen) ? 1. : getEfficiency(track, centrality); + if (weff < 0) { + return; + } + double wacc = (dt == Gen) ? 1. : getAcceptance(track, vtxz); + if (withinPtRef) { + fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 1); + } + if (withinPtPOI) { + fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 2); + } + if (withinPtRef && withinPtPOI) { + fGFW->Fill(track.eta(), fPtAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 4); + } + } + + template + float getCentrality(const TCollision& collision) + { + switch (cfgCentEstimator) { + case CentFT0C: + return collision.centFT0C(); + case CentFT0CVariant1: + return collision.centFT0CVariant1(); + case CentFT0M: + return collision.centFT0M(); + case CentFV0A: + return collision.centFV0A(); + case CentNTPV: + return collision.centNTPV(); + case CentNGlobal: + return collision.centNGlobal(); + case CentMFT: + return collision.centMFT(); + default: + return collision.centFT0C(); + } + } + + enum QAFillTime { + Before, + After + }; + + template + inline void fillTrackQA(const TTrack& track, const float vtxz) + { + if constexpr (dt == Gen) { + registry.fill(HIST("MCGen/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ_gen"), track.phi(), track.eta(), vtxz); + registry.fill(HIST("MCGen/") + HIST(FillTimeName[ft]) + HIST("pt_gen"), track.pt()); + } else { + double wacc = getAcceptance(track, vtxz); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ"), track.phi(), track.eta(), vtxz, (ft == After) ? wacc : 1.0); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + + if (ft == After) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_ref"), track.pt()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_poi"), track.pt()); + + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { + registry.fill(HIST("trackQA/after/etaNch"), track.eta()); + } + if (track.eta() > cfgKinematics.cfgEtaPtPt->first && track.eta() < cfgKinematics.cfgEtaPtPt->second && cfgFillQA) { + registry.fill(HIST("trackQA/after/etaPtPt"), track.eta()); + } + } + } + } + + template + inline void fillEventQA(const TCollision& collision, const TTracks& tracks) + { + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), tracks.size()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("PVTracks_centT0C"), collision.centFT0C(), collision.multNTracksPV()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_PVTracks"), collision.multNTracksPV(), tracks.size()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), tracks.size()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), tracks.size()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); + registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multT0C_centT0C"), collision.centFT0C(), collision.multFT0C()); + } + + struct AcceptedTracks { + float total = 0; + unsigned int totaluncorr = 0; + }; + + template + void processCollision(const TCollision& collision, const TTracks& tracks, const float& centrality, const float& field, const int generatedNch = -1) + { + if (tracks.size() < 1) { + return; + } + if (dt != Gen && (centrality < gfwMemberCache.centbinning.front() || centrality > gfwMemberCache.centbinning.back())) { + return; + } + if (dt != Gen) { + registry.fill(HIST("eventQA/eventSel"), TrackCent); + } + float vtxz = collision.posZ(); + + fGFW->Clear(); + fFCpt->clearVector(); + + float lRandom = fRndm->Rndm(); + + // process tracks + AcceptedTracks acceptedTracks; + for (const auto& track : tracks) { + processTrack(track, vtxz, field, centrality, acceptedTracks); + } + if constexpr (dt == Reco) { + if (generatedNch >= 0) { + registry.fill(HIST("MCReco/Nch_reco_gen"), acceptedTracks.totaluncorr, generatedNch); + } + } + if (dt != Gen && cfgFillQA) { + registry.fill(HIST("trackQA/after/Nch_corrected"), acceptedTracks.total); + registry.fill(HIST("trackQA/after/Nch_uncorrected"), acceptedTracks.totaluncorr); + } + + float multiplicity = 0.f; + switch (cfgUseNchCorrection) { + case NchSelector::TableSize: + multiplicity = tracks.size(); + break; + case NchSelector::Corrected: + multiplicity = acceptedTracks.total; + break; + case NchSelector::Uncorrected: + multiplicity = acceptedTracks.totaluncorr; + break; + case NchSelector::ResponseMatrixCorrected: + multiplicity = (dt == Gen) ? acceptedTracks.totaluncorr : getResponseCorrectedNch(acceptedTracks.totaluncorr); + break; + default: + multiplicity = tracks.size(); + break; + } + + fillOutputContainers
((cfgUseNch) ? multiplicity : centrality, lRandom, multiplicity); + } + + template + inline void processTrack(const TTrack& track, const float& vtxz, const float& field, const float& centrality, AcceptedTracks& acceptedTracks) + { + if constexpr (framework::has_type_v) { + if (track.mcParticleId() < 0 || !(track.has_mcParticle())) { + return; + } + auto mcParticle = track.mcParticle(); + if (!mcParticle.isPhysicalPrimary()) { + return; + } + if (cfgFillQA) { + fillTrackQA(track, vtxz); + } + if (mcParticle.eta() < gfwMemberCache.etalow || mcParticle.eta() > gfwMemberCache.etaup || mcParticle.pt() < gfwMemberCache.ptlow || mcParticle.pt() > gfwMemberCache.ptup) { + return; + } + // Select tracks with nominal cuts always + if (!nchSelected(track)) { + return; + } + double weffCh = getEfficiency(track, centrality); + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { + if (weffCh > 0) { + acceptedTracks.total += (cfgUseNchCorrection) ? weffCh : 1.0; + } + ++acceptedTracks.totaluncorr; + } + + if (!trackSelected(track, field)) { + return; + } + + fillPtSums(track, centrality); + fillGFW(mcParticle, centrality, vtxz); + if (cfgFillQA) { + fillTrackQA(track, vtxz); + } + + } else if constexpr (framework::has_type_v) { + if (!track.isPhysicalPrimary()) { + return; + } + if (cfgFillQA) { + fillTrackQA(track, vtxz); + } + if (track.eta() < gfwMemberCache.etalow || track.eta() > gfwMemberCache.etaup || track.pt() < gfwMemberCache.ptlow || track.pt() > gfwMemberCache.ptup) { + return; + } + + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { + ++acceptedTracks.total; + ++acceptedTracks.totaluncorr; + } + + fillPtSums(track, centrality); + fillGFW(track, centrality, vtxz); + if (cfgFillQA) { + fillTrackQA(track, vtxz); + } + + } else { + if (cfgFillQA) { + fillTrackQA(track, vtxz); + } + // Select tracks with nominal cuts always + if (!nchSelected(track)) { + return; + } + double weffCh = getEfficiency(track, centrality); + if (track.eta() > cfgKinematics.cfgEtaNch->first && track.eta() < cfgKinematics.cfgEtaNch->second) { + if (weffCh > 0) { + acceptedTracks.total += (cfgUseNchCorrection) ? weffCh : 1.0; + } + ++acceptedTracks.totaluncorr; + } + if (!trackSelected(track, field)) { + return; + } + + fillPtSums(track, centrality); + fillGFW(track, centrality, vtxz); + + if (cfgFillQA) { + fillTrackQA(track, vtxz); + } + } + } + + o2::framework::expressions::Filter collisionFilter = nabs(aod::collision::posZ) < cfgEventSelection.cfgVtxZ; + o2::framework::expressions::Filter trackFilter = (aod::track::eta > cfgKinematics.cfgEta->first) && (aod::track::eta < cfgKinematics.cfgEta->second) && (aod::track::pt > cfgKinematics.cfgPtCut->first) && (aod::track::pt < cfgKinematics.cfgPtCut->second) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::itsChi2NCl < cfgTrackCuts.cfgChi2PrITSCls) && (aod::track::tpcChi2NCl < cfgTrackCuts.cfgChi2PrTPCCls) && nabs(aod::track::dcaZ) < cfgTrackCuts.cfgDCAz; + + using GFWCollisions = soa::Filtered>; + using GFWMCCollisions = soa::Join; + using FilteredGFWMCCollisions = soa::Filtered; + using GFWTracks = soa::Filtered>; + using GFWMCTracks = soa::Filtered>; + Preslice particlesPerMcCollision = aod::mcparticle::mcCollisionId; + + SliceCache cache; + Partition posTracks = aod::track::signed1Pt > 0.0f; + Partition negTracks = aod::track::signed1Pt < 0.0f; + + double massKaPlus = o2::constants::physics::MassKPlus; + double massLambda = o2::constants::physics::MassLambda; + double massK0Short = o2::constants::physics::MassK0Short; + + void processData(GFWCollisions::iterator const& collision, aod::BCsWithTimestamps const&, GFWTracks const& tracks) + { + auto bc = collision.bc_as(); + int run = bc.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + if (cfgRunByRun) { + loadCorrections(bc); + } + } + if (!cfgRunByRun) { + loadCorrections(bc); + } + registry.fill(HIST("eventQA/eventSel"), 0.5); + if (!collision.sel8()) { + return; + } + registry.fill(HIST("eventQA/eventSel"), 1.5); + + float centrality = getCentrality(collision); + if (cfgEventSelection.cfgDoOccupancySel) { + int occupancy = collision.trackOccupancyInTimeRange(); + if (cfgFillQA) { + registry.fill(HIST("eventQA/before/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + if (occupancy < 0 || occupancy > cfgEventSelection.cfgOccupancySelection) { + return; + } + if (cfgFillQA) { + registry.fill(HIST("eventQA/after/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + } + registry.fill(HIST("eventQA/eventSel"), 2.5); + + if (cfgFillQA) { + fillEventQA(collision, tracks); + } + registry.fill(HIST("eventQA/before/centrality"), centrality); + registry.fill(HIST("eventQA/before/multiplicity"), tracks.size()); + if (!eventSelected(collision, tracks.size(), centrality)) { + return; + } + if (cfgFillQA) { + fillEventQA(collision, tracks); + } + registry.fill(HIST("eventQA/after/centrality"), centrality); + registry.fill(HIST("eventQA/after/multiplicity"), tracks.size()); + // Get magnetic field polarity + auto field = (cfgEventSelection.cfgMagField == DefaultMagneticFieldCut) ? getMagneticField(bc.timestamp()) : static_cast(cfgEventSelection.cfgMagField); + + processCollision(collision, tracks, centrality, field); + } + PROCESS_SWITCH(FlowGfwNonflow, processData, "Process analysis for non-derived data", true); + + void processMCReco(FilteredGFWMCCollisions::iterator const& collision, aod::BCsWithTimestamps const&, GFWMCTracks const& tracks, aod::McParticles const& particles) + { + auto bc = collision.bc_as(); + int run = bc.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + if (cfgRunByRun) { + loadCorrections(bc); + } + } + if (!cfgRunByRun) { + loadCorrections(bc); + } + + registry.fill(HIST("eventQA/eventSel"), 0.5); + + if (!collision.sel8()) { + return; + } + + registry.fill(HIST("eventQA/eventSel"), 1.5); + + const auto centrality = getCentrality(collision); + + if (cfgEventSelection.cfgDoOccupancySel) { + int occupancy = collision.trackOccupancyInTimeRange(); + registry.fill(HIST("eventQA/before/occ_mult_cent"), occupancy, tracks.size(), centrality); + if (occupancy < 0 || occupancy > cfgEventSelection.cfgOccupancySelection) { + return; + } + registry.fill(HIST("eventQA/after/occ_mult_cent"), occupancy, tracks.size(), centrality); + } + registry.fill(HIST("eventQA/eventSel"), 2.5); + + if (cfgFillQA) { + fillEventQA(collision, tracks); + } + if (!eventSelected(collision, tracks.size(), centrality)) { + return; + } + if (cfgFillQA) { + fillEventQA(collision, tracks); + } + unsigned int generatedNch = 0; + const auto particlesThisCollision = particles.sliceBy(particlesPerMcCollision, collision.mcCollisionId()); + for (const auto& particle : particlesThisCollision) { + if (particle.isPhysicalPrimary() && particle.eta() > cfgKinematics.cfgEtaNch->first && particle.eta() < cfgKinematics.cfgEtaNch->second && particle.pt() > gfwMemberCache.ptlow && particle.pt() < gfwMemberCache.ptup) { + ++generatedNch; + } + } + loadCorrections(bc); + auto field = (cfgEventSelection.cfgMagField == DefaultMagneticFieldCut) ? getMagneticField(bc.timestamp()) : static_cast(cfgEventSelection.cfgMagField); + processCollision(collision, tracks, centrality, field, generatedNch); + } + PROCESS_SWITCH(FlowGfwNonflow, processMCReco, "Process analysis for MC reconstructed events", false); + + o2::framework::expressions::Filter mcCollFilter = nabs(aod::mccollision::posZ) < cfgEventSelection.cfgVtxZ; + void processMCGen(soa::Filtered::iterator const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& particles) + { + if (collisions.size() != 1) { + return; + } + float centrality = -1; + for (const auto& collision : collisions) { + centrality = getCentrality(collision); + } + processCollision(mcCollision, particles, centrality, -999); + } + PROCESS_SWITCH(FlowGfwNonflow, processMCGen, "Process analysis for MC generated events", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx index 1b420167d5e..90ae8e00762 100644 --- a/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx +++ b/PWGCF/GenericFramework/Tasks/flowGfwV02.cxx @@ -11,16 +11,17 @@ // /// \file flowGfwV02.cxx /// \brief Skeleton copy of flowGfwLightIons with empty function bodies -/// \author Emil Gorm Nielsen, NBI, emil.gorm.nielsen@cern.ch +/// \author Maxim Virta, NBI, maxim.virta@cern.ch #include "PWGCF/DataModel/CorrelationsDerived.h" -#include "PWGCF/GenericFramework/Core/FlowContainer.h" #include "PWGCF/GenericFramework/Core/GFW.h" #include "PWGCF/GenericFramework/Core/GFWConfig.h" #include "PWGCF/GenericFramework/Core/GFWWeights.h" +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" #include "PWGCF/JCorran/DataModel/JCatalyst.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -51,13 +52,13 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -67,43 +68,53 @@ #include #include #include -#include #include using namespace o2; using namespace o2::framework; using namespace analysis::genericframework; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; -static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {2.1, 2.2, 2.3, -2.1, -2.2, -2.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {3.1, 3.2, 3.3, -3.1, -3.2, -3.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +// Rows: UpCut_pi, UpCut_ka, UpCut_pr, LowCut_pi, LowCut_ka, LowCut_pr. Columns: TPC, TOF, ITS. +static constexpr std::array, 6> HighPurityNsigma = {{{{1.5f, 0.f, 0.f}}, {{0.f, 1.5f, 0.f}}, {{0.f, 1.5f, 0.f}}, {{0.f, -1.5f, 0.f}}, {{-1.5f, 0.f, 0.f}}, {{-1.5f, 0.f, 0.f}}}}; -namespace o2::analysis::gfw +template +auto projectMatrix(Array2D const& mat, std::array& array1, std::array& array2, std::array& array3) { -std::vector ptbinning = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}; -float ptpoilow = 0.2, ptpoiup = 10.0; -float ptreflow = 0.2, ptrefup = 3.0; -float ptlow = 0.2, ptup = 10.0; -int etabins = 16; -float etalow = -0.8, etaup = 0.8; -int vtxZbins = 40; -float vtxZlow = -10.0, vtxZup = 10.0; -int phibins = 72; -float philow = 0.0; -float phiup = o2::constants::math::TwoPI; -int nchbins = 300; -float nchlow = 0; -float nchup = 3000; -std::vector centbinning(90); -int nBootstrap = 10; -std::vector> etagapsPtPt; -GFWRegions regions; -GFWCorrConfigs configs; -std::vector multGlobalCorrCutPars; -std::vector multPVCorrCutPars; -std::vector multGlobalPVCorrCutPars; -} // namespace o2::analysis::gfw + for (auto j = 0; j < static_cast(mat.rows); ++j) { + array1[j] = mat(j, 0); + array2[j] = mat(j, 1); + array3[j] = mat(j, 2); + } +} struct FlowGfwV02 { + + struct GFWMemberCache { + std::vector ptbinning = {0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.5, 4, 5, 6, 8, 10}; + float ptpoilow = 0.2, ptpoiup = 10.0; + float ptreflow = 0.2, ptrefup = 3.0; + float ptlow = 0.2, ptup = 10.0; + int etabins = 16; + float etalow = -0.8, etaup = 0.8; + int vtxZbins = 40; + float vtxZlow = -10.0, vtxZup = 10.0; + int phibins = 72; + float philow = 0.0; + float phiup = o2::constants::math::TwoPI; + int nchbins = 300; + float nchlow = 0; + float nchup = 300; + std::vector centbinning{90}; + int nBootstrap = 10; + GFWRegions regions; + GFWCorrConfigs configs; + std::vector multGlobalCorrCutPars; + std::vector multPVCorrCutPars; + std::vector multGlobalPVCorrCutPars; + } gfwMemberCache; + O2_DEFINE_CONFIGURABLE(cfgNbootstrap, int, 10, "Number of subsamples") O2_DEFINE_CONFIGURABLE(cfgMpar, int, 4, "Highest order of pt-pt correlations") O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") @@ -117,12 +128,17 @@ struct FlowGfwV02 { O2_DEFINE_CONFIGURABLE(cfgFixedMultMax, int, 3000, "Maximum for fixed nch range"); O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.5f, "Minimum pt to use TOF N-sigma") O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgUseHighPurityPID, bool, false, "Use high purity PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgHighPurityPtMin, float, 3.f, "Minimum pt (GeV/c) for high purity PID") O2_DEFINE_CONFIGURABLE(cfgGetNsigmaQA, bool, true, "Get QA histograms for selection of pions, kaons, and protons") O2_DEFINE_CONFIGURABLE(cfgGetdEdx, bool, true, "Get dEdx histograms for pions, kaons, and protons") O2_DEFINE_CONFIGURABLE(cfgUseMultiplicityFlowWeights, bool, true, "Enable or disable the use of multiplicity-based event weighting"); + O2_DEFINE_CONFIGURABLE(cfgUseMultiplicityFracWeights, bool, false, "Enable or disable the use of multiplicity-based event weighting for pT fraction"); O2_DEFINE_CONFIGURABLE(cfgNormalizeByCharged, bool, true, "Enable or disable the normalization by charged particles"); O2_DEFINE_CONFIGURABLE(cfgConsistentEventFlag, int, 15, "Flag for consistent event selection"); O2_DEFINE_CONFIGURABLE(cfgMultCut, bool, true, "Use additional event cut on mult correlations"); + O2_DEFINE_CONFIGURABLE(cfgUseV02, bool, false, "Use V02 analysis"); + O2_DEFINE_CONFIGURABLE(cfgUseV0, bool, false, "Use V0 analysis"); // Event selection cuts struct : ConfigurableGroup { @@ -138,6 +154,16 @@ struct FlowGfwV02 { O2_DEFINE_CONFIGURABLE(cfgIsVertexITSTPC, bool, true, "kIsVertexITSTPC - Selects collisions with at least one ITS-TPC track"); } cfgEventCutFlags; + // Event selection cuts + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEtaSubAMin, float, -0.8, "Minimum eta for subevent A"); + O2_DEFINE_CONFIGURABLE(cfgEtaSubAMax, float, -0.5, "Maximum eta for subevent A"); + O2_DEFINE_CONFIGURABLE(cfgEtaSubBMin, float, 0.5, "Minimum eta for subevent B"); + O2_DEFINE_CONFIGURABLE(cfgEtaSubBMax, float, 0.8, "Maximum eta for subevent B"); + O2_DEFINE_CONFIGURABLE(cfgEtaSubCMin, float, -0.4, "Minimum eta for subevent C"); + O2_DEFINE_CONFIGURABLE(cfgEtaSubCMax, float, 0.4, "Maximum eta for subevent C"); + } cfgSubeventCuts; + struct : ConfigurableGroup { Configurable> cfgMultGlobalCutPars{"cfgMultGlobalCutPars", std::vector{2272.16, -76.6932, 1.01204, -0.00631545, 1.59868e-05, 136.336, -4.97006, 0.121199, -0.0015921, 7.66197e-06}, "Global vs FT0C multiplicity cut parameter values"}; Configurable> cfgMultPVCutPars{"cfgMultPVCutPars", std::vector{3074.43, -106.192, 1.46176, -0.00968364, 2.61923e-05, 182.128, -7.43492, 0.193901, -0.00256715, 1.22594e-05}, "PV vs FT0C multiplicity cut parameter values"}; @@ -177,8 +203,9 @@ struct FlowGfwV02 { Configurable cfgGFWBinning{"cfgGFWBinning", {40, 16, 72, 300, 0, 3000, 0.2, 10.0, 0.2, 5.0, {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3, 3.25, 3.5, 3.75, 4, 4.5, 5}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90}}, "Configuration for binning"}; Configurable cfgRegions{"cfgRegions", {{"refN", "refP", "refFull", "refMid", "piP", "kaP", "prP"}, {-0.8, 0.5, -0.8, -0.4, 0.5, 0.5, 0.5}, {-0.5, 0.8, 0.8, 0.4, 0.8, 0.8, 0.8}, {0, 0, 0, 0, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1}}, "Configurations for GFW regions"}; - Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "piP {2} refN {-2}", "kaP {2} refN {-2}", "prP {2} refN {-2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}"}, {"ChGap22", "PiGap22", "KaGap22", "PrGap22", "ChFull22", "nchCh", "nchPi", "nchKa", "nchPr", "v02ptCh", "v02ptPi", "v02ptKa", "v02ptPr"}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {15, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for each correlation to calculate"}; - Configurable> nSigmas{"nSigmas", {LongArrayFloat[0], 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "refP {2 2} refN {-2 -2}", "piP {2} refN {-2}", "kaP {2} refN {-2}", "prP {2} refN {-2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}", "refFull {2 -2}"}, {"ChGap22", "ChGap24", "PiGap22", "KaGap22", "PrGap22", "ChFull22", "nchCh", "nchPi", "nchKa", "nchPr", "v02ptCh", "v02ptPi", "v02ptKa", "v02ptPr"}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {15, 15, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, "Configurations for each correlation to calculate"}; + Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable> nSigmasHighPurity{"nSigmasHighPurity", {HighPurityNsigma.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "High purity n-sigma windows for TPC, TOF, ITS for pions, kaons, and protons"}; struct : ConfigurableGroup { Configurable cfgZvtxMax{"cfgZvtxMax", 10.0f, "Maximum primary vertex cut applied for the events."}; @@ -188,20 +215,25 @@ struct FlowGfwV02 { // // Filters to be applied to the received data. // // The analysis assumes the data has been subjected to a QA of its selection, // // and thus only the final distributions of the data for analysis are saved. + // Derived-data track mask. Every listed track bit is required; see gfwtrack::TrackSelection. + // The event mask is built from cfgEventCutFlags when a GFW derived process is enabled. + Configurable> cfgBitmaskSelections{"cfgBitmaskSelections", {0, 3, 6, 9, 12}, "Enabled TrackSelection bit indices for GFW derived data. DCA 0-5, TPC track 6-13, TPC PID 14-22, TOF PID 23-31. The default is the nominal quality selection"}; + O2_DEFINE_CONFIGURABLE(cfgPidWindow, int, static_cast(o2::aod::gfwtrack::PidWindowNominal), "PID nSigma window for GFW derived data: 0 nominal, 1 tight, 2 loose"); + o2::framework::expressions::Filter collFilter = (nabs(aod::collision::posZ) < cfgEventCuts.cfgZvtxMax); o2::framework::expressions::Filter trackFilter = (aod::track::pt > cfgTrackCuts.cfgPtMin) && (aod::track::pt < cfgTrackCuts.cfgPtMax) && nabs(aod::track::eta) < cfgTrackCuts.cfgEtaMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::itsChi2NCl < cfgTrackCuts.cfgChi2PrITSCls) && (aod::track::tpcChi2NCl < cfgTrackCuts.cfgChi2PrTPCCls) && nabs(aod::track::dcaZ) < cfgTrackCuts.cfgDCAz; + o2::framework::expressions::Filter gfwTrackFilter = (aod::gfwtrack::pt > cfgTrackCuts.cfgPtMin) && (aod::gfwtrack::pt < cfgTrackCuts.cfgPtMax) && (nabs(aod::gfwtrack::eta) < cfgTrackCuts.cfgEtaMax); // Connect to ccdb - Service ccdb; + Service ccdb{}; struct Config { - TH1D* mEfficiency[4] = {nullptr, nullptr, nullptr, nullptr}; - GFWWeights* mAcceptance; + std::array mEfficiency{nullptr, nullptr, nullptr, nullptr}; + GFWWeights* mAcceptance = nullptr; bool correctionsLoaded = false; - } cfg; + } cfg{}; // Define output - OutputObj fFC{FlowContainer("FlowContainer")}; HistogramRegistry registry{"registry"}; enum CentEstimators { @@ -248,10 +280,15 @@ struct FlowGfwV02 { struct PIDState { o2::aod::ITSResponse itsResponse; - std::array tofNsigmaCut; - std::array itsNsigmaCut; - std::array tpcNsigmaCut; + std::array tofNsigmaCut{}; + std::array itsNsigmaCut{}; + std::array tpcNsigmaCut{}; + std::array highPurityTofNsigmaCut{}; + std::array highPurityItsNsigmaCut{}; + std::array highPurityTpcNsigmaCut{}; std::array, 4> hPtMid{}; + std::array, 4> hPtForward{}; + std::array, 4> hPtBackward{}; }; PIDState pidStates; @@ -261,12 +298,10 @@ struct FlowGfwV02 { std::unique_ptr fMultCutLow; std::unique_ptr fMultCutHigh; std::unique_ptr fMultPVGlobalCutHigh; - std::unique_ptr fMultGlobalV0ACutLow; - std::unique_ptr fMultGlobalV0ACutHigh; - std::unique_ptr fMultGlobalT0ACutLow; - std::unique_ptr fMultGlobalT0ACutHigh; std::unique_ptr fPtDepDCAxy; + uint32_t trackBitMask = o2::aod::gfwtrack::NominalMask; + uint16_t eventBitMask = o2::aod::gfwcollision::DefaultEventMask; using GFWTracks = soa::Filtered>; @@ -274,7 +309,8 @@ struct FlowGfwV02 { PidCharged = 0, PidPions, PidKaons, - PidProtons + PidProtons, + PidTotal }; enum PiKpArrayIndex { IndPionUp = 0, @@ -284,35 +320,68 @@ struct FlowGfwV02 { IndKaonLow, IndProtonLow }; - enum DetectorType { - kTPC = 0, - kTOF, - kITS - }; + + // Required gfwcollision::EventSel bits for derived data, taken from the event-cut configurables. + uint16_t eventSelectionMask() + { + uint16_t mask = 0; + if (cfgEventCutFlags.cfgNoSameBunchPileupCut) { + mask |= o2::aod::gfwcollision::kNoSameBunchPileup; + } + if (cfgEventCutFlags.cfgIsGoodZvtxFT0vsPV) { + mask |= o2::aod::gfwcollision::kIsGoodZvtxFT0vsPV; + } + if (cfgEventCutFlags.cfgNoCollInTimeRangeStandard) { + mask |= o2::aod::gfwcollision::kNoCollInTimeRangeStandard; + } + if (cfgEventCutFlags.cfgNoCollInRofStandard) { + mask |= o2::aod::gfwcollision::kNoCollInRofStandard; + } + if (cfgEventCutFlags.cfgNoHighMultCollInPrevRof) { + mask |= o2::aod::gfwcollision::kNoHighMultCollInPrevRof; + } + if (cfgEventCutFlags.cfgIsVertexITSTPC) { + mask |= o2::aod::gfwcollision::kIsVertexITSTPC; + } + if (cfgEventCutFlags.cfgIsGoodITSLayersAll) { + mask |= o2::aod::gfwcollision::kIsGoodITSLayersAll; + } + if (cfgEventCutFlags.cfgNoTimeFrameBorder) { + mask |= o2::aod::gfwcollision::kNoTimeFrameBorder; + } + if (cfgEventCutFlags.cfgNoITSROFrameBorder) { + mask |= o2::aod::gfwcollision::kNoITSROFrameBorder; + } + if (cfgEventCutFlags.cfgTVXinTRD) { + mask |= o2::aod::gfwcollision::kNotTVXinTRD; + } + if (cfgTrackCuts.cfgOccupancySelection >= 0) { + mask |= o2::aod::gfwcollision::kOccupancy; + } + if (cfgUseAdditionalEventCut) { + mask |= o2::aod::gfwcollision::kGoodVertex; + if (cfgMultCut) { + mask |= o2::aod::gfwcollision::kMultCorr; + } + } + return mask; + } void init(InitContext const&) { + trackBitMask = 0; + for (const int bit : cfgBitmaskSelections.value) { + if (bit >= 0 && bit < 32) { + trackBitMask |= static_cast(1u << bit); + } + } + if (doprocessDerived || doprocessDerivedCorrected) { + eventBitMask = eventSelectionMask(); + LOGF(info, "GFW derived event-selection mask: %u", static_cast(eventBitMask)); + } - pidStates.tpcNsigmaCut[IndPionUp] = nSigmas->getData()[IndPionUp][kTPC]; - pidStates.tpcNsigmaCut[IndKaonUp] = nSigmas->getData()[IndKaonUp][kTPC]; - pidStates.tpcNsigmaCut[IndProtonUp] = nSigmas->getData()[IndProtonUp][kTPC]; - pidStates.tpcNsigmaCut[IndPionLow] = nSigmas->getData()[IndPionLow][kTPC]; - pidStates.tpcNsigmaCut[IndKaonLow] = nSigmas->getData()[IndKaonLow][kTPC]; - pidStates.tpcNsigmaCut[IndProtonLow] = nSigmas->getData()[IndProtonLow][kTPC]; - - pidStates.tofNsigmaCut[IndPionUp] = nSigmas->getData()[IndPionUp][kTOF]; - pidStates.tofNsigmaCut[IndKaonUp] = nSigmas->getData()[IndKaonUp][kTOF]; - pidStates.tofNsigmaCut[IndProtonUp] = nSigmas->getData()[IndProtonUp][kTOF]; - pidStates.tofNsigmaCut[IndPionLow] = nSigmas->getData()[IndPionLow][kTOF]; - pidStates.tofNsigmaCut[IndKaonLow] = nSigmas->getData()[IndKaonLow][kTOF]; - pidStates.tofNsigmaCut[IndProtonLow] = nSigmas->getData()[IndProtonLow][kTOF]; - - pidStates.itsNsigmaCut[IndPionUp] = nSigmas->getData()[IndPionUp][kITS]; - pidStates.itsNsigmaCut[IndKaonUp] = nSigmas->getData()[IndKaonUp][kITS]; - pidStates.itsNsigmaCut[IndProtonUp] = nSigmas->getData()[IndProtonUp][kITS]; - pidStates.itsNsigmaCut[IndPionLow] = nSigmas->getData()[IndPionLow][kITS]; - pidStates.itsNsigmaCut[IndKaonLow] = nSigmas->getData()[IndKaonLow][kITS]; - pidStates.itsNsigmaCut[IndProtonLow] = nSigmas->getData()[IndProtonLow][kITS]; + projectMatrix(nSigmas->getData(), pidStates.tpcNsigmaCut, pidStates.tofNsigmaCut, pidStates.itsNsigmaCut); + projectMatrix(nSigmasHighPurity->getData(), pidStates.highPurityTpcNsigmaCut, pidStates.highPurityTofNsigmaCut, pidStates.highPurityItsNsigmaCut); if (cfgGetNsigmaQA) { if (cfgUseItsPID) { @@ -335,60 +404,79 @@ struct FlowGfwV02 { registry.addClone("QA_PID/before/", "QA_PID/after/"); } - o2::analysis::gfw::regions.SetNames(cfgRegions->GetNames()); - o2::analysis::gfw::regions.SetEtaMin(cfgRegions->GetEtaMin()); - o2::analysis::gfw::regions.SetEtaMax(cfgRegions->GetEtaMax()); - o2::analysis::gfw::regions.SetpTDifs(cfgRegions->GetpTDifs()); - o2::analysis::gfw::regions.SetBitmasks(cfgRegions->GetBitmasks()); - o2::analysis::gfw::configs.SetCorrs(cfgCorrConfig->GetCorrs()); - o2::analysis::gfw::configs.SetHeads(cfgCorrConfig->GetHeads()); - o2::analysis::gfw::configs.SetpTDifs(cfgCorrConfig->GetpTDifs()); - o2::analysis::gfw::configs.SetpTCorrMasks(cfgCorrConfig->GetpTCorrMasks()); - o2::analysis::gfw::regions.Print(); - o2::analysis::gfw::configs.Print(); - o2::analysis::gfw::ptbinning = cfgGFWBinning->GetPtBinning(); - o2::analysis::gfw::ptpoilow = cfgGFWBinning->GetPtPOImin(); - o2::analysis::gfw::ptpoiup = cfgGFWBinning->GetPtPOImax(); - o2::analysis::gfw::ptreflow = cfgGFWBinning->GetPtRefMin(); - o2::analysis::gfw::ptrefup = cfgGFWBinning->GetPtRefMax(); - o2::analysis::gfw::ptlow = cfgTrackCuts.cfgPtMin; - o2::analysis::gfw::ptup = cfgTrackCuts.cfgPtMax; - o2::analysis::gfw::etabins = cfgGFWBinning->GetEtaBins(); - o2::analysis::gfw::vtxZbins = cfgGFWBinning->GetVtxZbins(); - o2::analysis::gfw::phibins = cfgGFWBinning->GetPhiBins(); - o2::analysis::gfw::philow = 0.0f; - o2::analysis::gfw::phiup = o2::constants::math::TwoPI; - o2::analysis::gfw::nchbins = cfgGFWBinning->GetNchBins(); - o2::analysis::gfw::nchlow = cfgGFWBinning->GetNchMin(); - o2::analysis::gfw::nchup = cfgGFWBinning->GetNchMax(); - o2::analysis::gfw::centbinning = cfgGFWBinning->GetCentBinning(); + gfwMemberCache.regions.SetNames(cfgRegions->GetNames()); + gfwMemberCache.regions.SetEtaMin(cfgRegions->GetEtaMin()); + gfwMemberCache.regions.SetEtaMax(cfgRegions->GetEtaMax()); + gfwMemberCache.regions.SetpTDifs(cfgRegions->GetpTDifs()); + gfwMemberCache.regions.SetBitmasks(cfgRegions->GetBitmasks()); + gfwMemberCache.configs.SetCorrs(cfgCorrConfig->GetCorrs()); + gfwMemberCache.configs.SetHeads(cfgCorrConfig->GetHeads()); + gfwMemberCache.configs.SetpTDifs(cfgCorrConfig->GetpTDifs()); + gfwMemberCache.configs.SetpTCorrMasks(cfgCorrConfig->GetpTCorrMasks()); + gfwMemberCache.regions.Print(); + gfwMemberCache.configs.Print(); + gfwMemberCache.ptbinning = cfgGFWBinning->GetPtBinning(); + gfwMemberCache.ptpoilow = cfgGFWBinning->GetPtPOImin(); + gfwMemberCache.ptpoiup = cfgGFWBinning->GetPtPOImax(); + gfwMemberCache.ptreflow = cfgGFWBinning->GetPtRefMin(); + gfwMemberCache.ptrefup = cfgGFWBinning->GetPtRefMax(); + gfwMemberCache.ptlow = cfgTrackCuts.cfgPtMin; + gfwMemberCache.ptup = cfgTrackCuts.cfgPtMax; + gfwMemberCache.etabins = cfgGFWBinning->GetEtaBins(); + gfwMemberCache.vtxZbins = cfgGFWBinning->GetVtxZbins(); + gfwMemberCache.phibins = cfgGFWBinning->GetPhiBins(); + gfwMemberCache.philow = 0.0f; + gfwMemberCache.phiup = o2::constants::math::TwoPI; + gfwMemberCache.nchbins = cfgGFWBinning->GetNchBins(); + gfwMemberCache.nchlow = cfgGFWBinning->GetNchMin(); + gfwMemberCache.nchup = cfgGFWBinning->GetNchMax(); + gfwMemberCache.centbinning = cfgGFWBinning->GetCentBinning(); + gfwMemberCache.nBootstrap = cfgNbootstrap; cfgGFWBinning->Print(); - o2::analysis::gfw::multGlobalCorrCutPars = cfgMultCorrCuts.cfgMultGlobalCutPars; - o2::analysis::gfw::multPVCorrCutPars = cfgMultCorrCuts.cfgMultPVCutPars; - o2::analysis::gfw::multGlobalPVCorrCutPars = cfgMultCorrCuts.cfgMultGlobalPVCutPars; + gfwMemberCache.multGlobalCorrCutPars = cfgMultCorrCuts.cfgMultGlobalCutPars; + gfwMemberCache.multPVCorrCutPars = cfgMultCorrCuts.cfgMultPVCutPars; + gfwMemberCache.multGlobalPVCorrCutPars = cfgMultCorrCuts.cfgMultGlobalPVCutPars; // Initialise pt spectra histograms for different particles - pidStates.hPtMid[PidCharged] = std::make_unique("hPtMid_charged", "hPtMid_charged", o2::analysis::gfw::ptbinning.size() - 1, &o2::analysis::gfw::ptbinning[0]); - pidStates.hPtMid[PidPions] = std::make_unique("hPtMid_pions", "hPtMid_pions", o2::analysis::gfw::ptbinning.size() - 1, &o2::analysis::gfw::ptbinning[0]); - pidStates.hPtMid[PidKaons] = std::make_unique("hPtMid_kaons", "hPtMid_kaons", o2::analysis::gfw::ptbinning.size() - 1, &o2::analysis::gfw::ptbinning[0]); - pidStates.hPtMid[PidProtons] = std::make_unique("hPtMid_protons", "hPtMid_protons", o2::analysis::gfw::ptbinning.size() - 1, &o2::analysis::gfw::ptbinning[0]); + pidStates.hPtMid[PidCharged] = std::make_unique("hPtMid_charged", "hPtMid_charged", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtMid[PidPions] = std::make_unique("hPtMid_pions", "hPtMid_pions", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtMid[PidKaons] = std::make_unique("hPtMid_kaons", "hPtMid_kaons", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtMid[PidProtons] = std::make_unique("hPtMid_protons", "hPtMid_protons", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); pidStates.hPtMid[PidCharged]->SetDirectory(nullptr); pidStates.hPtMid[PidPions]->SetDirectory(nullptr); pidStates.hPtMid[PidKaons]->SetDirectory(nullptr); pidStates.hPtMid[PidProtons]->SetDirectory(nullptr); - AxisSpec phiAxis = {o2::analysis::gfw::phibins, o2::analysis::gfw::philow, o2::analysis::gfw::phiup, "#phi"}; - AxisSpec etaAxis = {o2::analysis::gfw::etabins, -cfgTrackCuts.cfgEtaMax, cfgTrackCuts.cfgEtaMax, "#eta"}; - AxisSpec vtxAxis = {o2::analysis::gfw::vtxZbins, -cfgEventCuts.cfgZvtxMax, cfgEventCuts.cfgZvtxMax, "Vtx_{z} (cm)"}; - AxisSpec ptAxis = {o2::analysis::gfw::ptbinning, "#it{p}_{T} GeV/#it{c}"}; + pidStates.hPtForward[PidCharged] = std::make_unique("hPtForward_charged", "hPtForward_charged", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtForward[PidPions] = std::make_unique("hPtForward_pions", "hPtForward_pions", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtForward[PidKaons] = std::make_unique("hPtForward_kaons", "hPtForward_kaons", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtForward[PidProtons] = std::make_unique("hPtForward_protons", "hPtForward_protons", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtForward[PidCharged]->SetDirectory(nullptr); + pidStates.hPtForward[PidPions]->SetDirectory(nullptr); + pidStates.hPtForward[PidKaons]->SetDirectory(nullptr); + pidStates.hPtForward[PidProtons]->SetDirectory(nullptr); + + pidStates.hPtBackward[PidCharged] = std::make_unique("hPtBackward_charged", "hPtBackward_charged", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtBackward[PidPions] = std::make_unique("hPtBackward_pions", "hPtBackward_pions", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtBackward[PidKaons] = std::make_unique("hPtBackward_kaons", "hPtBackward_kaons", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtBackward[PidProtons] = std::make_unique("hPtBackward_protons", "hPtBackward_protons", gfwMemberCache.ptbinning.size() - 1, gfwMemberCache.ptbinning.data()); + pidStates.hPtBackward[PidCharged]->SetDirectory(nullptr); + pidStates.hPtBackward[PidPions]->SetDirectory(nullptr); + pidStates.hPtBackward[PidKaons]->SetDirectory(nullptr); + pidStates.hPtBackward[PidProtons]->SetDirectory(nullptr); + + AxisSpec phiAxis = {gfwMemberCache.phibins, gfwMemberCache.philow, gfwMemberCache.phiup, "#phi"}; + AxisSpec etaAxis = {gfwMemberCache.etabins, -cfgTrackCuts.cfgEtaMax, cfgTrackCuts.cfgEtaMax, "#eta"}; + AxisSpec vtxAxis = {gfwMemberCache.vtxZbins, -cfgEventCuts.cfgZvtxMax, cfgEventCuts.cfgZvtxMax, "Vtx_{z} (cm)"}; + AxisSpec ptAxis = {gfwMemberCache.ptbinning, "#it{p}_{T} GeV/#it{c}"}; std::string sCentralityEstimator = "FT0C centrality (%)"; - AxisSpec centAxis = {o2::analysis::gfw::centbinning, sCentralityEstimator.c_str()}; + AxisSpec centAxis = {gfwMemberCache.centbinning, sCentralityEstimator.c_str()}; std::vector nchbinning; - int nchskip = (o2::analysis::gfw::nchup - o2::analysis::gfw::nchlow) / o2::analysis::gfw::nchbins; - for (int i = 0; i <= o2::analysis::gfw::nchbins; ++i) { - nchbinning.push_back(nchskip * i + o2::analysis::gfw::nchlow + 0.5); + const double nchskip = (gfwMemberCache.nchup - gfwMemberCache.nchlow) / static_cast(gfwMemberCache.nchbins); + for (int i = 0; i <= gfwMemberCache.nchbins; ++i) { + nchbinning.push_back(nchskip * i + gfwMemberCache.nchlow + 0.5); } AxisSpec nchAxis = {nchbinning, "N_{ch}"}; AxisSpec t0cAxis = {1000, 0, 10000, "N_{ch} (T0C)"}; @@ -397,9 +485,44 @@ struct FlowGfwV02 { AxisSpec multpvAxis = {600, 0, 600, "N_{ch} (PV)"}; AxisSpec dcaZAxis = {200, -2, 2, "DCA_{z} (cm)"}; AxisSpec dcaXYAxis = {200, -0.5, 0.5, "DCA_{xy} (cm)"}; + AxisSpec bsAxis = {gfwMemberCache.nBootstrap, -0.5, gfwMemberCache.nBootstrap - 0.5, "Bootstrap Index"}; + + // V0 spectra + registry.add("analysis/charged/v0AB", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + registry.add("analysis/charged/v0BA", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + registry.add("analysis/charged/nchA", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + registry.add("analysis/charged/nchB", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + + // V02 spectra + registry.add("analysis/charged/v22npt", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + registry.add("analysis/charged/nchC", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + + if (cfgUseMultiplicityFracWeights) { + registry.add("analysis/charged/nchA2pc", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + registry.add("analysis/charged/nchB2pc", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + registry.add("analysis/charged/nchC3pc", "", {HistType::kTProfile3D, {bsAxis, ptAxis, centAxis}}); + } - registry.add("v02pt", "", {HistType::kTProfile2D, {ptAxis, centAxis}}); - registry.add("nchMid", "", {HistType::kTProfile2D, {ptAxis, centAxis}}); + registry.addClone("analysis/charged/", "analysis/pion/"); + registry.addClone("analysis/charged/", "analysis/kaon/"); + registry.addClone("analysis/charged/", "analysis/proton/"); + + // Only charged particles + registry.add("analysis/charged/v22pt", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/ptA", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/ptB", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/ptAB", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/ptC", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v22", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v24", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + + if (cfgUseMultiplicityFracWeights) { + registry.add("analysis/charged/ptA2pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/ptB2pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/ptC3pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v223pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + registry.add("analysis/charged/v224pc", "", {HistType::kTProfile2D, {bsAxis, centAxis}}); + } ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -408,15 +531,15 @@ struct FlowGfwV02 { int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); - int ptbins = o2::analysis::gfw::ptbinning.size() - 1; - fSecondAxis = std::make_unique(ptbins, &o2::analysis::gfw::ptbinning[0]); + int ptbins = gfwMemberCache.ptbinning.size() - 1; + fSecondAxis = std::make_unique(ptbins, gfwMemberCache.ptbinning.data()); // QA histograms registry.add("trackQA/before/phi_eta_vtxZ", "", {HistType::kTH3D, {phiAxis, etaAxis, vtxAxis}}); registry.add("trackQA/before/pt_dcaXY_dcaZ", "", {HistType::kTH3D, {ptAxis, dcaXYAxis, dcaZAxis}}); registry.add("trackQA/before/nch_pt", "#it{p}_{T} vs multiplicity; N_{ch}; #it{p}_{T}", {HistType::kTH2D, {nchAxis, ptAxis}}); - registry.add("trackQA/after/pt_ref", "", {HistType::kTH1D, {{100, o2::analysis::gfw::ptreflow, o2::analysis::gfw::ptrefup}}}); - registry.add("trackQA/after/pt_poi", "", {HistType::kTH1D, {{100, o2::analysis::gfw::ptpoilow, o2::analysis::gfw::ptpoiup}}}); + registry.add("trackQA/after/pt_ref", "", {HistType::kTH1D, {{100, gfwMemberCache.ptreflow, gfwMemberCache.ptrefup}}}); + registry.add("trackQA/after/pt_poi", "", {HistType::kTH1D, {{100, gfwMemberCache.ptpoilow, gfwMemberCache.ptpoiup}}}); registry.add("trackQA/before/chi2prTPCcls", "#chi^{2}/cluster for the TPC track segment; #chi^{2}/TPC cluster", {HistType::kTH1D, {{100, 0., 5.}}}); registry.add("trackQA/before/chi2prITScls", "#chi^{2}/cluster for the ITS track; #chi^{2}/ITS cluster", {HistType::kTH1D, {{100, 0., 50.}}}); registry.add("trackQA/before/nTPCClusters", "Number of found TPC clusters; TPC N_{cls}; Counts", {HistType::kTH1D, {{100, 40, 180}}}); @@ -461,27 +584,26 @@ struct FlowGfwV02 { registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kMultCuts, "after Mult cuts"); registry.get(HIST("eventQA/eventSel"))->GetXaxis()->SetBinLabel(kTrackCent, "has track + within cent"); - if (o2::analysis::gfw::regions.GetSize() < 0) + if (gfwMemberCache.regions.GetSize() < 0) { LOGF(error, "Configuration contains vectors of different size - check the GFWRegions configurable"); - for (auto i(0); i < o2::analysis::gfw::regions.GetSize(); ++i) { - fGFW->AddRegion(o2::analysis::gfw::regions.GetNames()[i], o2::analysis::gfw::regions.GetEtaMin()[i], o2::analysis::gfw::regions.GetEtaMax()[i], (o2::analysis::gfw::regions.GetpTDifs()[i]) ? ptbins + 1 : 1, o2::analysis::gfw::regions.GetBitmasks()[i]); } - for (auto i = 0; i < o2::analysis::gfw::configs.GetSize(); ++i) { - corrconfigs.push_back(fGFW->GetCorrelatorConfig(o2::analysis::gfw::configs.GetCorrs()[i], o2::analysis::gfw::configs.GetHeads()[i], o2::analysis::gfw::configs.GetpTDifs()[i])); + for (auto i(0); i < gfwMemberCache.regions.GetSize(); ++i) { + fGFW->AddRegion(gfwMemberCache.regions.GetNames()[i], gfwMemberCache.regions.GetEtaMin()[i], gfwMemberCache.regions.GetEtaMax()[i], (gfwMemberCache.regions.GetpTDifs()[i] != 0) ? ptbins + 1 : 1, gfwMemberCache.regions.GetBitmasks()[i]); + } + for (auto i = 0; i < gfwMemberCache.configs.GetSize(); ++i) { + corrconfigs.push_back(fGFW->GetCorrelatorConfig(gfwMemberCache.configs.GetCorrs()[i], gfwMemberCache.configs.GetHeads()[i], gfwMemberCache.configs.GetpTDifs()[i] != 0)); } - if (corrconfigs.empty()) + if (corrconfigs.empty()) { LOGF(error, "Configuration contains vectors of different size - check the GFWCorrConfig configurable"); + } fGFW->CreateRegions(); - TObjArray* oba = new TObjArray(); + auto* oba = new TObjArray(); oba->SetOwner(kTRUE); addConfigObjectsToObjArray(oba, corrconfigs); LOGF(info, "Number of correlators: %d", oba->GetEntries()); - fFC->SetName("FlowContainer"); - fFC->SetXAxis(fSecondAxis.get()); - fFC->Initialize(oba, centAxis, cfgNbootstrap); delete oba; - if (cfgConsistentEventFlag) { + if (cfgConsistentEventFlag != 0) { posRegionIndex = [&]() { auto begin = cfgRegions->GetNames().begin(); auto end = cfgRegions->GetNames().end(); @@ -510,22 +632,22 @@ struct FlowGfwV02 { if (cfgUseAdditionalEventCut) { fMultPVCutLow = std::make_unique("fMultPVCutLow", cfgMultCorrCuts.cfgMultCorrLowCutFunction->c_str(), 0, 100); - fMultPVCutLow->SetParameters(&(o2::analysis::gfw::multPVCorrCutPars[0])); + fMultPVCutLow->SetParameters(gfwMemberCache.multPVCorrCutPars.data()); fMultPVCutHigh = std::make_unique("fMultPVCutHigh", cfgMultCorrCuts.cfgMultCorrHighCutFunction->c_str(), 0, 100); - fMultPVCutHigh->SetParameters(&(o2::analysis::gfw::multPVCorrCutPars[0])); + fMultPVCutHigh->SetParameters(gfwMemberCache.multPVCorrCutPars.data()); fMultCutLow = std::make_unique("fMultCutLow", cfgMultCorrCuts.cfgMultCorrLowCutFunction->c_str(), 0, 100); - fMultCutLow->SetParameters(&(o2::analysis::gfw::multGlobalCorrCutPars[0])); + fMultCutLow->SetParameters(gfwMemberCache.multGlobalCorrCutPars.data()); fMultCutHigh = std::make_unique("fMultCutHigh", cfgMultCorrCuts.cfgMultCorrHighCutFunction->c_str(), 0, 100); - fMultCutHigh->SetParameters(&(o2::analysis::gfw::multGlobalCorrCutPars[0])); + fMultCutHigh->SetParameters(gfwMemberCache.multGlobalCorrCutPars.data()); fMultPVGlobalCutHigh = std::make_unique("fMultPVGlobalCutHigh", cfgMultCorrCuts.cfgMultGlobalPVCorrCutFunction->c_str(), 0, nchbinning.back()); - fMultPVGlobalCutHigh->SetParameters(&(o2::analysis::gfw::multGlobalPVCorrCutPars[0])); + fMultPVGlobalCutHigh->SetParameters(gfwMemberCache.multGlobalPVCorrCutPars.data()); } // Set DCAxy cut fPtDepDCAxy = std::make_unique("ptDepDCAxy", Form("[0]*%s", cfgTrackCuts.cfgDCAxy->c_str()), 0.001, 100); fPtDepDCAxy->SetParameter(0, cfgTrackCuts.cfgDCAxyNSigma); } - static constexpr std::string_view FillTimeName[] = {"before/", "after/"}; + static constexpr std::array FillTimeName = {"before/", "after/"}; enum QAFillTime { kBefore, kAfter @@ -538,7 +660,7 @@ struct FlowGfwV02 { std::string suffix = "_ptDiff"; for (auto i = 0; i < fSecondAxis->GetNbins(); ++i) { std::string index = Form("_pt_%i", i + 1); - oba->Add(new TNamed(it->Head.c_str() + index, it->Head.c_str() + suffix)); + oba->Add(new TNamed(it->Head + index, it->Head + suffix)); } } else { oba->Add(new TNamed(it->Head.c_str(), it->Head.c_str())); @@ -547,36 +669,80 @@ struct FlowGfwV02 { } template - int getNsigmaPID(TTrack track) + int getNsigmaPID(TTrack const& track) { - // Computing Nsigma arrays for pion, kaon, and protons - std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; - std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; - std::array nSigmaITS = {pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track)}; int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton - - std::array nSigmaToUse = cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - std::array detectorNsigmaCut = cfgUseItsPID ? pidStates.itsNsigmaCut : pidStates.tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - - bool isPion, isKaon, isProton; - bool isDetectedPion = nSigmaToUse[IndPionUp] < detectorNsigmaCut[IndPionUp] && nSigmaToUse[IndPionUp] > detectorNsigmaCut[IndPionLow]; - bool isDetectedKaon = nSigmaToUse[IndKaonUp] < detectorNsigmaCut[IndKaonUp] && nSigmaToUse[IndKaonUp] > detectorNsigmaCut[IndKaonLow]; - bool isDetectedProton = nSigmaToUse[IndProtonUp] < detectorNsigmaCut[IndProtonUp] && nSigmaToUse[IndProtonUp] > detectorNsigmaCut[IndProtonLow]; - - bool isTofPion = nSigmaTOF[IndPionUp] < pidStates.tofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.tofNsigmaCut[IndPionLow]; - bool isTofKaon = nSigmaTOF[IndKaonUp] < pidStates.tofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.tofNsigmaCut[IndKaonLow]; - bool isTofProton = nSigmaTOF[IndProtonUp] < pidStates.tofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.tofNsigmaCut[IndProtonLow]; - - if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + bool isPion = false, isKaon = false, isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + if constexpr (requires { track.trackSel(); }) { + const int window = cfgPidWindow; + const int shift = (window == aod::gfwtrack::PidWindowTight || window == aod::gfwtrack::PidWindowLoose) ? window * aod::gfwtrack::PidWindowBits : aod::gfwtrack::PidWindowNominal; + const uint32_t trackSel = track.trackSel(); + isDetectedPion = (trackSel & (static_cast(aod::gfwtrack::kTpcPi) << shift)) != 0; + isDetectedKaon = (trackSel & (static_cast(aod::gfwtrack::kTpcKa) << shift)) != 0; + isDetectedProton = (trackSel & (static_cast(aod::gfwtrack::kTpcPr) << shift)) != 0; + isTofPion = (trackSel & (static_cast(aod::gfwtrack::kTofPi) << shift)) != 0; + isTofKaon = (trackSel & (static_cast(aod::gfwtrack::kTofKa) << shift)) != 0; + isTofProton = (trackSel & (static_cast(aod::gfwtrack::kTofPr) << shift)) != 0; + } else if constexpr (requires { track.tpcNSigmaPi(); track.tofNSigmaPi(); }) { + const std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + const std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + constexpr bool HasItsResponse = requires { + track.itsClusterSizes(); + track.p(); + }; + if (cfgUseHighPurityPID && track.pt() > cfgHighPurityPtMin) { + const bool isHighPurityPion = nSigmaTPC[IndPionUp] < pidStates.highPurityTpcNsigmaCut[IndPionUp] && nSigmaTPC[IndPionUp] > pidStates.highPurityTpcNsigmaCut[IndPionLow] && nSigmaTOF[IndPionUp] < pidStates.highPurityTofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.highPurityTofNsigmaCut[IndPionLow]; + const bool isHighPurityKaon = nSigmaTPC[IndKaonUp] < pidStates.highPurityTpcNsigmaCut[IndKaonUp] && nSigmaTPC[IndKaonUp] > pidStates.highPurityTpcNsigmaCut[IndKaonLow] && nSigmaTOF[IndKaonUp] < pidStates.highPurityTofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.highPurityTofNsigmaCut[IndKaonLow]; + const bool isHighPurityProton = nSigmaTPC[IndProtonUp] < pidStates.highPurityTpcNsigmaCut[IndProtonUp] && nSigmaTPC[IndProtonUp] > pidStates.highPurityTpcNsigmaCut[IndProtonLow] && nSigmaTOF[IndProtonUp] < pidStates.highPurityTofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.highPurityTofNsigmaCut[IndProtonLow]; + if (isHighPurityPion) { + return PidPions; + } + if (isHighPurityKaon) { + return PidKaons; + } + if (isHighPurityProton) { + return PidProtons; + } + return -1; + } + std::array nSigmaITS{}; + if constexpr (HasItsResponse) { + nSigmaITS = {pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track), pidStates.itsResponse.nSigmaITS(track)}; + } + const bool useItsPid = cfgUseItsPID && HasItsResponse; + const std::array nSigmaToUse = useItsPid ? nSigmaITS : nSigmaTPC; + const std::array detectorNsigmaCut = useItsPid ? pidStates.itsNsigmaCut : pidStates.tpcNsigmaCut; + isDetectedPion = nSigmaToUse[IndPionUp] < detectorNsigmaCut[IndPionUp] && nSigmaToUse[IndPionUp] > detectorNsigmaCut[IndPionLow]; + isDetectedKaon = nSigmaToUse[IndKaonUp] < detectorNsigmaCut[IndKaonUp] && nSigmaToUse[IndKaonUp] > detectorNsigmaCut[IndKaonLow]; + isDetectedProton = nSigmaToUse[IndProtonUp] < detectorNsigmaCut[IndProtonUp] && nSigmaToUse[IndProtonUp] > detectorNsigmaCut[IndProtonLow]; + isTofPion = nSigmaTOF[IndPionUp] < pidStates.tofNsigmaCut[IndPionUp] && nSigmaTOF[IndPionUp] > pidStates.tofNsigmaCut[IndPionLow]; + isTofKaon = nSigmaTOF[IndKaonUp] < pidStates.tofNsigmaCut[IndKaonUp] && nSigmaTOF[IndKaonUp] > pidStates.tofNsigmaCut[IndKaonLow]; + isTofProton = nSigmaTOF[IndProtonUp] < pidStates.tofNsigmaCut[IndProtonUp] && nSigmaTOF[IndProtonUp] > pidStates.tofNsigmaCut[IndProtonLow]; + } else { return -1; - } else if (track.pt() > cfgTofPtCut && track.hasTOF()) { - isPion = isTofPion && isDetectedPion; - isKaon = isTofKaon && isDetectedKaon; - isProton = isTofProton && isDetectedProton; + } + + if constexpr (requires { track.hasTOF(); }) { + if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } } else { - isPion = isDetectedPion; - isKaon = isDetectedKaon; - isProton = isDetectedProton; + return -1; } if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { @@ -599,8 +765,9 @@ struct FlowGfwV02 { void loadCorrections(aod::BCsWithTimestamps::iterator const& bc) { uint64_t timestamp = bc.timestamp(); - if (cfg.correctionsLoaded) + if (cfg.correctionsLoaded) { return; + } if (!cfgAcceptance.value.empty()) { cfg.mAcceptance = ccdb->getForTimeStamp(cfgAcceptance.value, timestamp); } @@ -609,17 +776,17 @@ struct FlowGfwV02 { if (cfg.mEfficiency[PidCharged] == nullptr) { LOGF(fatal, "Could not load efficiency histogram from %s", cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)cfg.mEfficiency[PidCharged]); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(cfg.mEfficiency[PidCharged])); } if (cfgPIDEfficiency) { const std::array pidStrings = {"ch", "pi", "ka", "pr"}; - for (int i = 1; i < 4; i++) { + for (int i = 1; i < PidTotal; i++) { cfg.mEfficiency[i] = ccdb->getForTimeStamp(cfgEfficiency.value + pidStrings[i], timestamp); if (cfg.mEfficiency[i] == nullptr) { - LOGF(fatal, "Could not load PID efficiency histogram from %s", cfgEfficiency.value + pidStrings[i].c_str()); + LOGF(fatal, "Could not load PID efficiency histogram from %s", (cfgEfficiency.value + pidStrings[i]).c_str()); } - LOGF(info, "Loaded PID efficiency histogram from %s (%p)", cfgEfficiency.value + pidStrings[i].c_str(), (void*)cfg.mEfficiency[i]); + LOGF(info, "Loaded PID efficiency histogram from %s (%p)", (cfgEfficiency.value + pidStrings[i]).c_str(), static_cast(cfg.mEfficiency[i])); } } cfg.correctionsLoaded = true; @@ -627,8 +794,9 @@ struct FlowGfwV02 { void loadCorrections(int runnumber) { - if (cfg.correctionsLoaded) + if (cfg.correctionsLoaded) { return; + } if (!cfgAcceptance.value.empty()) { cfg.mAcceptance = ccdb->getForRun(cfgAcceptance.value, runnumber); } @@ -638,50 +806,69 @@ struct FlowGfwV02 { cfg.correctionsLoaded = true; } + // JWeights, filled by jflucWeightsLoader from a local file or CCDB, stores the same + // correction used by flowJSPCAnalysis: acceptance is 1/weightNUA and efficiency is weightEff. template - double getJTrackAcceptance(TTrack track) + double getJTrackAcceptance(TTrack const& track) { double wacc = 1; - if constexpr (requires { track.weightNUA(); }) - wacc = 1. / track.weightNUA(); + if constexpr (requires { track.weightNUA(); }) { + const double nua = track.weightNUA(); + wacc = (nua == 0.) ? 1. : 1. / nua; + } return wacc; } template - double getJTrackEfficiency(TTrack track) + double getJTrackEfficiency(TTrack const& track) { double eff = 1.; - if constexpr (requires { track.weightEff(); }) + if constexpr (requires { track.weightEff(); }) { eff = track.weightEff(); + } return eff; } template - double getAcceptance(TTrack track, const double& vtxz) + double getAcceptance(TTrack const& track, const double& vtxz) { - double wacc = 1; - if (cfg.mAcceptance) - wacc = cfg.mAcceptance->getNUA(track.phi(), track.eta(), vtxz); - return wacc; + if constexpr (requires { track.weightNUA(); }) { + return getJTrackAcceptance(track); + } else { + double wacc = 1; + if (cfg.mAcceptance) { + wacc = cfg.mAcceptance->getNUA(track.phi(), track.eta(), vtxz); + } + return wacc; + } } template - double getEfficiency(TTrack track, const int& pid = PidCharged) + double getEfficiency(TTrack const& track, const int& pid = PidCharged) { - double eff = 1.; - if (cfg.mEfficiency[pid]) - eff = cfg.mEfficiency[pid]->GetBinContent(cfg.mEfficiency[pid]->FindBin(track.pt())); - if (eff == 0) - return -1.; - else + if constexpr (requires { track.weightEff(); }) { + const double eff = getJTrackEfficiency(track); + if (eff == 0.) { + return -1.; + } + return eff; + } else { + double eff = 1.; + if (cfg.mEfficiency[pid]) { + eff = cfg.mEfficiency[pid]->GetBinContent(cfg.mEfficiency[pid]->FindBin(track.pt())); + } + if (eff == 0) { + return -1.; + } return 1. / eff; + } } template - bool eventSelected(TCollision collision, const int& multTrk, const float& centrality) + bool eventSelected(TCollision const& collision, const int& multTrk, const float& centrality) { if (cfgEventCutFlags.cfgTVXinTRD) { - if (collision.alias_bit(kTVXinTRD)) { + if (collision.alias_bit(::kTVXinTRD)) { // TRD triggered // "CMTVX-B-NOPF-TRD,minbias_TVX" return 0; @@ -763,25 +950,32 @@ struct FlowGfwV02 { float zRes = std::sqrt(collision.covZZ()); float minZRes = 0.25; int minNContrib = 20; - if (zRes > minZRes && collision.numContrib() < minNContrib) + if (zRes > minZRes && collision.numContrib() < minNContrib) { vtxz = -999; + } } auto multNTracksPV = collision.multNTracksPV(); - if (vtxz > o2::analysis::gfw::vtxZup || vtxz < o2::analysis::gfw::vtxZlow) + if (vtxz > gfwMemberCache.vtxZup || vtxz < gfwMemberCache.vtxZlow) { return 0; + } if (cfgMultCut) { - if (multNTracksPV < fMultPVCutLow->Eval(centrality)) + if (multNTracksPV < fMultPVCutLow->Eval(centrality)) { return 0; - if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) + } + if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) { return 0; - if (multTrk < fMultCutLow->Eval(centrality)) + } + if (multTrk < fMultCutLow->Eval(centrality)) { return 0; - if (multTrk > fMultCutHigh->Eval(centrality)) + } + if (multTrk > fMultCutHigh->Eval(centrality)) { return 0; - if (multTrk > fMultPVGlobalCutHigh->Eval(collision.multNTracksPV())) + } + if (multTrk > fMultPVGlobalCutHigh->Eval(collision.multNTracksPV())) { return 0; + } registry.fill(HIST("eventQA/eventSel"), kMultCuts); } return 1; @@ -795,88 +989,200 @@ struct FlowGfwV02 { int getPIDIndex(const std::string& corrconfig) { - if (boost::ifind_first(corrconfig, "pi")) + if (!boost::ifind_first(corrconfig, "pi").empty()) { return PidPions; - if (boost::ifind_first(corrconfig, "ka")) + } + if (!boost::ifind_first(corrconfig, "ka").empty()) { return PidKaons; - if (boost::ifind_first(corrconfig, "pr")) + } + if (!boost::ifind_first(corrconfig, "pr").empty()) { return PidProtons; + } return PidCharged; } - GFW::CorrConfig getRelevantCorrName(const int& pidInd) - { - if (pidInd == PidPions) - return fGFW->GetCorrelatorConfig("piP {2} refN {-2}", "PiGap22", kFALSE); - if (pidInd == PidKaons) - return fGFW->GetCorrelatorConfig("kaP {2} refN {-2}", "KaGap22", kFALSE); - if (pidInd == PidProtons) - return fGFW->GetCorrelatorConfig("prP {2} refN {-2}", "PrGap22", kFALSE); - return fGFW->GetCorrelatorConfig("refP {2} refN {-2}", "ChGap22", kFALSE); - } - template - void fillOutputContainers(const float& centmult, const double& rndm, const int& /*run*/ = 0) + void fillOutputContainers(const float& centmult) { - for (uint l_ind = 0; l_ind < corrconfigs.size(); ++l_ind) { - if (!corrconfigs.at(l_ind).pTDif) { - auto dnx = fGFW->Calculate(corrconfigs.at(l_ind), 0, kTRUE).real(); - if (dnx == 0) - continue; - auto val = fGFW->Calculate(corrconfigs.at(l_ind), 0, kFALSE).real() / dnx; - - if (std::abs(val) < 1) { - fFC->FillProfile(corrconfigs.at(l_ind).Head.c_str(), centmult, val, (cfgUseMultiplicityFlowWeights) ? dnx : 1.0, rndm); + constexpr double Threshold = 1.01; + constexpr double MinDnxAB = 1e-8; // skip events with vanishing V22 weight + + int bootstrap = fRndm->Integer(gfwMemberCache.nBootstrap); + // Calculate V02 + if (cfgUseV02) { + double v22npt = 0; + double ptMeanMid = pidStates.hPtMid[PidCharged]->GetMean(); + double ptFractionMid = 0.; + double dnxAB = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); // V22 weight for AB + double dnxABAB = fGFW->Calculate(corrconfigs.at(1), 0, kTRUE).real(); // V22 weight for ABAB + + // Check if the V22 weight is valid + const bool validAB = dnxAB >= MinDnxAB; + const bool validABAB = dnxABAB >= MinDnxAB; + const double valAB = validAB ? fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnxAB : 0.; + const double valABAB = validABAB ? fGFW->Calculate(corrconfigs.at(1), 0, kFALSE).real() / dnxABAB : 0.; + const bool acceptAB = validAB && std::isfinite(valAB) && std::abs(valAB) <= Threshold; + const bool acceptABAB = validABAB && std::isfinite(valABAB) && std::abs(valABAB) <= Threshold; + if (acceptAB && acceptABAB) { + double v22pt = valAB * ptMeanMid; + double weightAB = (cfgUseMultiplicityFlowWeights) ? dnxAB : 1.0; + double weightABAB = (cfgUseMultiplicityFlowWeights) ? dnxABAB : 1.0; + double weightC = 1.0; + // Calculate V02 for each particle type + for (int pid = 0; pid < PidTotal; pid++) { + int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; + if (!(pidStates.hPtMid[normIndex]->Integral() > 0)) { + continue; // Mid pT distribution is not defined + } + if (cfgUseMultiplicityFracWeights) { + weightC = pidStates.hPtMid[PidCharged]->Integral(); // Mean pt/npt C weight + } + for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { + ptFractionMid = pidStates.hPtMid[pid]->GetBinContent(i) / pidStates.hPtMid[normIndex]->Integral(); + v22npt = valAB * ptFractionMid; + + switch (pid) { + case PidCharged: + registry.fill(HIST("analysis/charged/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/charged/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidPions: + registry.fill(HIST("analysis/pion/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/pion/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/pion/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidKaons: + registry.fill(HIST("analysis/kaon/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/kaon/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/kaon/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + case PidProtons: + registry.fill(HIST("analysis/proton/v22npt"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v22npt, weightC * weightAB); + registry.fill(HIST("analysis/proton/nchC"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/proton/nchC3pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionMid, weightC * weightAB); + } + break; + default: + break; + } + } } - continue; - } - // Fill pt profiles for different particles - int pidInd = getPIDIndex(corrconfigs.at(l_ind).Head.c_str()); - - // Find the corresponding non-pT-differential correlation configuration - GFW::CorrConfig corrName = getRelevantCorrName(pidInd); // May be used later for QA - - auto dnx = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); - if (dnx == 0) - continue; - auto val = fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnx; - double ebyeWeight = (cfgUseMultiplicityFlowWeights) ? dnx : 1.0; - for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { - if (corrconfigs.at(l_ind).Head.find("nch") != std::string::npos) { - ebyeWeight = 1.0; - val = 1.0; + // Calculate NCv22pt + if (cfgUseMultiplicityFracWeights) { + weightC = pidStates.hPtMid[PidCharged]->Integral(); } - double ptFraction = 0; - int normIndex = (cfgNormalizeByCharged) ? PidCharged : pidInd; // Configured to normalize by charged particles or the selected particle - if (pidStates.hPtMid[normIndex]->Integral() > 0) { - ptFraction = pidStates.hPtMid[pidInd]->GetBinContent(i) / pidStates.hPtMid[normIndex]->Integral(); - if (std::abs(val) < 1.01) - fFC->FillProfile(Form("%s_pt_%i", corrconfigs.at(l_ind).Head.c_str(), i), centmult, val * ptFraction, ebyeWeight, rndm); + + registry.fill(HIST("analysis/charged/v22pt"), bootstrap, centmult, v22pt, weightC * weightAB); + registry.fill(HIST("analysis/charged/v22"), bootstrap, centmult, valAB, weightAB); + registry.fill(HIST("analysis/charged/v24"), bootstrap, centmult, valABAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC"), bootstrap, centmult, ptMeanMid, weightC); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/v223pc"), bootstrap, centmult, valAB, weightC * weightAB); + registry.fill(HIST("analysis/charged/v224pc"), bootstrap, centmult, valAB, weightABAB); + registry.fill(HIST("analysis/charged/ptC3pc"), bootstrap, centmult, ptMeanMid, weightC * weightAB); } } - } - // Fill the profiles for each pT bin - auto dnx = fGFW->Calculate(corrconfigs.at(0), 0, kTRUE).real(); - if (dnx == 0) - return; - auto val = fGFW->Calculate(corrconfigs.at(0), 0, kFALSE).real() / dnx; - for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { - double ptFraction = 0; - if (pidStates.hPtMid[PidCharged]->Integral() > 0) { - ptFraction = pidStates.hPtMid[PidCharged]->GetBinContent(i) / pidStates.hPtMid[PidCharged]->Integral(); - if (std::abs(val) < 1) - registry.fill(HIST("v02pt"), fSecondAxis->GetBinCenter(i), centmult, val * ptFraction, (cfgUseMultiplicityFlowWeights) ? dnx : 1.0); - registry.fill(HIST("nchMid"), fSecondAxis->GetBinCenter(i), centmult, ptFraction); + } // End of V02 + + // Calculate V0 + if (cfgUseV0) { + double v0corrAB = 0; + double v0corrBA = 0; + double ptMeanForward = pidStates.hPtForward[PidCharged]->GetMean(); + double ptMeanBackward = pidStates.hPtBackward[PidCharged]->GetMean(); + double ptFractionForward = 0.; + double ptFractionBackward = 0.; + double weightA = 1.0; + double weightB = 1.0; + for (int pid = 0; pid < PidTotal; pid++) { + int normIndex = (cfgNormalizeByCharged) ? PidCharged : pid; + if (!(pidStates.hPtForward[normIndex]->Integral() > 0) || !(pidStates.hPtBackward[normIndex]->Integral() > 0)) { + continue; // Forward or backward pT distribution is not defined + } + if (cfgUseMultiplicityFracWeights) { + weightA = pidStates.hPtForward[PidCharged]->Integral(); + weightB = pidStates.hPtBackward[PidCharged]->Integral(); + } + for (int i = 1; i <= fSecondAxis->GetNbins(); i++) { + ptFractionForward = pidStates.hPtForward[pid]->GetBinContent(i) / pidStates.hPtForward[normIndex]->Integral(); + ptFractionBackward = pidStates.hPtBackward[pid]->GetBinContent(i) / pidStates.hPtBackward[normIndex]->Integral(); + v0corrAB = ptFractionForward * ptMeanBackward; + v0corrBA = ptFractionBackward * ptMeanForward; + switch (pid) { + case PidCharged: + registry.fill(HIST("analysis/charged/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/charged/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/charged/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/charged/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/charged/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); + } + break; + case PidPions: + registry.fill(HIST("analysis/pion/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/pion/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/pion/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/pion/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/pion/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/pion/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); + } + break; + case PidKaons: + registry.fill(HIST("analysis/kaon/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/kaon/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/kaon/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/kaon/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/kaon/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/kaon/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); + } + break; + case PidProtons: + registry.fill(HIST("analysis/proton/v0AB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrAB, weightA * weightB); + registry.fill(HIST("analysis/proton/v0BA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, v0corrBA, weightA * weightB); + registry.fill(HIST("analysis/proton/nchA"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA); + registry.fill(HIST("analysis/proton/nchB"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightB); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/proton/nchA2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionForward, weightA * weightB); + registry.fill(HIST("analysis/proton/nchB2pc"), bootstrap, fSecondAxis->GetBinCenter(i), centmult, ptFractionBackward, weightA * weightB); + } + break; + default: + break; + } + } + } // End of PID dependent parts of V0 + + // Calculate PID independent parts of V0 + if (cfgUseMultiplicityFracWeights) { + weightA = pidStates.hPtForward[PidCharged]->Integral(); + weightB = pidStates.hPtBackward[PidCharged]->Integral(); } - } - return; - } + registry.fill(HIST("analysis/charged/ptA"), bootstrap, centmult, ptMeanForward, weightA); + registry.fill(HIST("analysis/charged/ptB"), bootstrap, centmult, ptMeanBackward, weightB); + if (cfgUseMultiplicityFracWeights) { + registry.fill(HIST("analysis/charged/ptA2pc"), bootstrap, centmult, ptMeanForward, weightA * weightB); + registry.fill(HIST("analysis/charged/ptB2pc"), bootstrap, centmult, ptMeanBackward, weightA * weightB); + } + registry.fill(HIST("analysis/charged/ptAB"), bootstrap, centmult, ptMeanForward * ptMeanBackward, weightA * weightB); + + } // End of V0 + } // End of Filling struct XAxis { float centrality; int64_t multiplicity; - double time; }; struct AcceptedTracks { @@ -887,77 +1193,128 @@ struct FlowGfwV02 { }; template - void processCollision(TCollision collision, TTracks tracks, const XAxis& xaxis, const int& run) + void processCollision(TCollision const& collision, TTracks const& tracks, const XAxis& xaxis) { float vtxz = collision.posZ(); - if (tracks.size() < 1) + if (tracks.size() < 1) { return; - if (xaxis.centrality >= 0 && (xaxis.centrality < o2::analysis::gfw::centbinning.front() || xaxis.centrality > o2::analysis::gfw::centbinning.back())) + } + if (xaxis.centrality >= 0 && (xaxis.centrality < gfwMemberCache.centbinning.front() || xaxis.centrality > gfwMemberCache.centbinning.back())) { return; - if (xaxis.multiplicity < cfgFixedMultMin || xaxis.multiplicity > cfgFixedMultMax) + } + if (xaxis.multiplicity < cfgFixedMultMin || xaxis.multiplicity > cfgFixedMultMax) { return; + } fGFW->Clear(); pidStates.hPtMid[PidCharged]->Reset(); pidStates.hPtMid[PidPions]->Reset(); pidStates.hPtMid[PidKaons]->Reset(); pidStates.hPtMid[PidProtons]->Reset(); - - float lRandom = fRndm->Rndm(); + pidStates.hPtBackward[PidCharged]->Reset(); + pidStates.hPtBackward[PidPions]->Reset(); + pidStates.hPtBackward[PidKaons]->Reset(); + pidStates.hPtBackward[PidProtons]->Reset(); + pidStates.hPtForward[PidCharged]->Reset(); + pidStates.hPtForward[PidPions]->Reset(); + pidStates.hPtForward[PidKaons]->Reset(); + pidStates.hPtForward[PidProtons]->Reset(); // Loop over tracks and check if they are accepted - AcceptedTracks acceptedTracks{0, 0, 0, 0}; + AcceptedTracks acceptedTracks{.nPos = 0, .nNeg = 0, .nFull = 0, .nMid = 0}; for (const auto& track : tracks) { - processTrack(track, vtxz, xaxis.multiplicity, run, acceptedTracks); - if (track.eta() > -0.4 && track.eta() < 0.4) + processTrack(track, vtxz, xaxis.multiplicity, acceptedTracks); + if (track.eta() > cfgSubeventCuts.cfgEtaSubCMin && track.eta() < cfgSubeventCuts.cfgEtaSubCMax) { pidStates.hPtMid[PidCharged]->Fill(track.pt(), getEfficiency(track, PidCharged)); + } + if (track.eta() > cfgSubeventCuts.cfgEtaSubAMin && track.eta() < cfgSubeventCuts.cfgEtaSubAMax) { // add mean pT + pidStates.hPtBackward[PidCharged]->Fill(track.pt(), getEfficiency(track, PidCharged)); + } + if (track.eta() > cfgSubeventCuts.cfgEtaSubBMin && track.eta() < cfgSubeventCuts.cfgEtaSubBMax) { // add mean pT + pidStates.hPtForward[PidCharged]->Fill(track.pt(), getEfficiency(track, PidCharged)); + } // If PID is identified, fill pt spectrum for the corresponding particle int pidInd = getNsigmaPID(track); - if (pidInd != -1 && track.eta() > -0.4 && track.eta() < 0.4) { - if (cfgPIDEfficiency) + if (pidInd != -1 && track.eta() > cfgSubeventCuts.cfgEtaSubCMin && track.eta() < cfgSubeventCuts.cfgEtaSubCMax) { + if (cfgPIDEfficiency) { pidStates.hPtMid[pidInd]->Fill(track.pt(), getEfficiency(track, pidInd)); - else + } else { pidStates.hPtMid[pidInd]->Fill(track.pt(), getEfficiency(track, PidCharged)); // Default to charged particles if PID efficiency is not used + } + } + if (pidInd != -1 && track.eta() > cfgSubeventCuts.cfgEtaSubAMin && track.eta() < cfgSubeventCuts.cfgEtaSubAMax) { + if (cfgPIDEfficiency) { + pidStates.hPtBackward[pidInd]->Fill(track.pt(), getEfficiency(track, pidInd)); + } else { + pidStates.hPtBackward[pidInd]->Fill(track.pt(), getEfficiency(track, PidCharged)); // Default to charged particles if PID efficiency is not used + } + } + if (pidInd != -1 && track.eta() > cfgSubeventCuts.cfgEtaSubBMin && track.eta() < cfgSubeventCuts.cfgEtaSubBMax) { + if (cfgPIDEfficiency) { + pidStates.hPtForward[pidInd]->Fill(track.pt(), getEfficiency(track, pidInd)); + } else { + pidStates.hPtForward[pidInd]->Fill(track.pt(), getEfficiency(track, PidCharged)); // Default to charged particles if PID efficiency is not used + } } } - if (cfgConsistentEventFlag & 1) - if (!acceptedTracks.nPos || !acceptedTracks.nNeg) + if (cfgConsistentEventFlag & 1) { + if (!acceptedTracks.nPos || !acceptedTracks.nNeg) { return; - if (cfgConsistentEventFlag & 2) - if (acceptedTracks.nFull < 4) // o2-linter: disable=magic-number (at least four tracks in full acceptance) + } + } + if (cfgConsistentEventFlag & 2) { + if (acceptedTracks.nFull < 4) { // o2-linter: disable=magic-number (at least four tracks in full acceptance) return; - if (cfgConsistentEventFlag & 4) - if (acceptedTracks.nPos < 2 || acceptedTracks.nNeg < 2) // o2-linter: disable=magic-number (at least two tracks in each subevent) + } + } + if (cfgConsistentEventFlag & 4) { + if (acceptedTracks.nPos < 2 || acceptedTracks.nNeg < 2) { // o2-linter: disable=magic-number (at least two tracks in each subevent) return; - if (cfgConsistentEventFlag & 8) - if (acceptedTracks.nPos < 2 || acceptedTracks.nMid < 2 || acceptedTracks.nNeg < 2) // o2-linter: disable=magic-number (at least two tracks in all three subevents) + } + } + if (cfgConsistentEventFlag & 8) { + if (acceptedTracks.nPos < 2 || acceptedTracks.nMid < 2 || acceptedTracks.nNeg < 2) { // o2-linter: disable=magic-number (at least two tracks in all three subevents) return; + } + } // Fill output containers - fillOutputContainers
(xaxis.centrality, lRandom, run); + fillOutputContainers
(xaxis.centrality); } template - void fillAcceptedTracks(TTrack track, AcceptedTracks& acceptedTracks) + void fillAcceptedTracks(TTrack const& track, AcceptedTracks& acceptedTracks) { - if (posRegionIndex >= 0 && track.eta() > o2::analysis::gfw::regions.GetEtaMin()[posRegionIndex] && track.eta() < o2::analysis::gfw::regions.GetEtaMax()[posRegionIndex]) + if (posRegionIndex >= 0 && track.eta() > gfwMemberCache.regions.GetEtaMin()[posRegionIndex] && track.eta() < gfwMemberCache.regions.GetEtaMax()[posRegionIndex]) { ++acceptedTracks.nPos; - if (negRegionIndex >= 0 && track.eta() > o2::analysis::gfw::regions.GetEtaMin()[negRegionIndex] && track.eta() < o2::analysis::gfw::regions.GetEtaMax()[negRegionIndex]) + } + if (negRegionIndex >= 0 && track.eta() > gfwMemberCache.regions.GetEtaMin()[negRegionIndex] && track.eta() < gfwMemberCache.regions.GetEtaMax()[negRegionIndex]) { ++acceptedTracks.nNeg; - if (fullRegionIndex >= 0 && track.eta() > o2::analysis::gfw::regions.GetEtaMin()[fullRegionIndex] && track.eta() < o2::analysis::gfw::regions.GetEtaMax()[fullRegionIndex]) + } + if (fullRegionIndex >= 0 && track.eta() > gfwMemberCache.regions.GetEtaMin()[fullRegionIndex] && track.eta() < gfwMemberCache.regions.GetEtaMax()[fullRegionIndex]) { ++acceptedTracks.nFull; - if (midRegionIndex >= 0 && track.eta() > o2::analysis::gfw::regions.GetEtaMin()[midRegionIndex] && track.eta() < o2::analysis::gfw::regions.GetEtaMax()[midRegionIndex]) + } + if (midRegionIndex >= 0 && track.eta() > gfwMemberCache.regions.GetEtaMin()[midRegionIndex] && track.eta() < gfwMemberCache.regions.GetEtaMax()[midRegionIndex]) { ++acceptedTracks.nMid; + } } template - bool trackSelected(TTrack track) + bool trackSelected(TTrack const& track) { - if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) - return false; - return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); + if constexpr (requires { track.trackSel(); }) { + const uint32_t mask = trackBitMask; + return (track.trackSel() & mask) == mask; + } else if constexpr (requires { track.dcaXY(); track.tpcNClsCrossedRows(); track.tpcNClsFound(); track.itsNCls(); }) { + if (cfgTrackCuts.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) { + return false; + } + return ((track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgNTPCXrows) && (track.tpcNClsFound() >= cfgTrackCuts.cfgNTPCCls) && (track.itsNCls() >= cfgTrackCuts.cfgMinNITSCls)); + } else { + return true; + } } template - float getCentrality(TCollision collision) + float getCentrality(TCollision const& collision) { switch (cfgCentEstimator) { case kCentFT0C: @@ -980,7 +1337,7 @@ struct FlowGfwV02 { } template - inline void processTrack(TTrack const& track, const float& vtxz, const int& multiplicity, const int& /*run*/, AcceptedTracks& acceptedTracks) + inline void processTrack(TTrack const& track, const float& vtxz, const int& multiplicity, AcceptedTracks& acceptedTracks) { if (cfgFillQA) { @@ -988,11 +1345,13 @@ struct FlowGfwV02 { registry.fill(HIST("trackQA/before/nch_pt"), multiplicity, track.pt()); } - if (cfgGetNsigmaQA) + if (cfgGetNsigmaQA) { fillPidQA(track, getNsigmaPID(track)); + } - if (!trackSelected(track)) + if (!trackSelected(track)) { return; + } fillGFW(track, vtxz); // Fill GFW fillAcceptedTracks(track, acceptedTracks); // Fill accepted tracks @@ -1001,125 +1360,124 @@ struct FlowGfwV02 { registry.fill(HIST("trackQA/after/nch_pt"), multiplicity, track.pt()); } - if (cfgGetNsigmaQA) + if (cfgGetNsigmaQA) { fillPidQA(track, getNsigmaPID(track)); + } } template - inline void fillGFW(TTrack track, const double& vtxz) + inline void fillGFW(TTrack const& track, const double& vtxz) { int pidInd = getNsigmaPID(track); - bool withinPtRef = (track.pt() > o2::analysis::gfw::ptreflow && track.pt() < o2::analysis::gfw::ptrefup); - bool withinPtPOI = (track.pt() > o2::analysis::gfw::ptpoilow && track.pt() < o2::analysis::gfw::ptpoiup); + bool withinPtRef = (track.pt() > gfwMemberCache.ptreflow && track.pt() < gfwMemberCache.ptrefup); + bool withinPtPOI = (track.pt() > gfwMemberCache.ptpoilow && track.pt() < gfwMemberCache.ptpoiup); - if (!withinPtPOI && !withinPtRef) + if (!withinPtPOI && !withinPtRef) { return; + } double weff = getEfficiency(track, PidCharged); - if (weff < 0) + if (weff < 0) { return; + } double wacc = getAcceptance(track, vtxz); // Fill cumulants for different particles // ***Need to add proper weights for each particle!*** - if (withinPtRef) + if (withinPtRef) { fGFW->Fill(track.eta(), fSecondAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, 1); - if (withinPtPOI && pidInd == PidPions) + } + if (withinPtPOI && pidInd == PidPions) { fGFW->Fill(track.eta(), fSecondAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, PidPions + 1); - if (withinPtPOI && pidInd == PidKaons) + } + if (withinPtPOI && pidInd == PidKaons) { fGFW->Fill(track.eta(), fSecondAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, PidKaons + 1); - if (withinPtPOI && pidInd == PidProtons) + } + if (withinPtPOI && pidInd == PidProtons) { fGFW->Fill(track.eta(), fSecondAxis->FindBin(track.pt()) - 1, track.phi(), weff * wacc, PidProtons + 1); - return; + } } template - inline void fillPidQA(TTrack track, const int& pid) + void fillPidDedxQA(TTrack const& track, const int& pid) { - // Fill Nsigma QA - if (!cfgUseItsPID) { - if (ft == kBefore) { - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); - } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); - } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); + if constexpr (requires { track.tpcSignal(); track.tpcExpSigmaPi(); track.tpcExpSigmaKa(); track.tpcExpSigmaPr(); }) { + if (!cfgGetdEdx) { + return; + } + if (ft == kBefore || pid == PidPions) { + const double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); + } + if (ft == kBefore || pid == PidKaons) { + const double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); + } + if (ft == kBefore || pid == PidProtons) { + const double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); + } + } + } - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); - } - } else if (ft == kAfter) { - if (pid == PidPions) { + template + inline void fillPidQA(TTrack const& track, const int& pid) + { + // Fill Nsigma QA. The derived tables store PID bits and have no nSigma columns. + if constexpr (requires { track.tpcNSigmaPi(); track.tofNSigmaPi(); }) { + if (!cfgUseItsPID) { + if (ft == kBefore) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_pions"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_pions"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_pions"), track.pt(), track.tpcExpSigmaPi(), track.tofNSigmaPi()); - } - } - if (pid == PidKaons) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); - - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_kaons"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_kaons"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_kaons"), track.pt(), track.tpcExpSigmaKa(), track.tofNSigmaKa()); - } - } - if (pid == PidProtons) { registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TpcdEdx_ptwise_protons"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpTpcdEdx_ptwise_protons"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("ExpSigma_ptwise_protons"), track.pt(), track.tpcExpSigmaPr(), track.tofNSigmaPr()); + } else if (ft == kAfter) { + if (pid == PidPions) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_pions"), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt()); + } + if (pid == PidKaons) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_kaons"), track.tpcNSigmaKa(), track.tofNSigmaKa(), track.pt()); + } + if (pid == PidProtons) { + registry.fill(HIST("QA_PID/") + HIST(FillTimeName[ft]) + HIST("TofTpcNsigma_protons"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); } } + fillPidDedxQA(track, pid); } } } template - inline void fillTrackQA(TTrack track, const float vtxz) + inline void fillTrackQA(TTrack const& track, const float vtxz) { double wacc = getAcceptance(track, vtxz); registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("phi_eta_vtxZ"), track.phi(), track.eta(), vtxz, (ft == kAfter) ? wacc : 1.0); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + if constexpr (requires { track.dcaXY(); track.dcaZ(); }) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_dcaXY_dcaZ"), track.pt(), track.dcaXY(), track.dcaZ()); + } - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); - registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + if constexpr (requires { track.tpcChi2NCl(); track.itsChi2NCl(); track.tpcNClsFound(); track.itsNCls(); track.tpcNClsCrossedRows(); }) { + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prTPCcls"), track.tpcChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("chi2prITScls"), track.itsChi2NCl()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCClusters"), track.tpcNClsFound()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nITSClusters"), track.itsNCls()); + registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("nTPCCrossedRows"), track.tpcNClsCrossedRows()); + } if (ft == kAfter) { registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_ref"), track.pt()); registry.fill(HIST("trackQA/") + HIST(FillTimeName[ft]) + HIST("pt_poi"), track.pt()); } - return; } template - inline void fillEventQA(TCollision collision, XAxis xaxis) + inline void fillEventQA(TCollision const& collision, XAxis xaxis) { if constexpr (framework::has_type_v) { registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_centT0C"), collision.centFT0C(), xaxis.multiplicity); @@ -1135,7 +1493,6 @@ struct FlowGfwV02 { registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multT0A"), collision.multFT0A(), xaxis.multiplicity); registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("globalTracks_multV0A"), collision.multFV0A(), xaxis.multiplicity); registry.fill(HIST("eventQA/") + HIST(FillTimeName[ft]) + HIST("multV0A_multT0A"), collision.multFT0A(), collision.multFV0A()); - return; } double getTimeSinceStartOfFill(uint64_t, int) { return 0.0; } @@ -1151,25 +1508,28 @@ struct FlowGfwV02 { loadCorrections(bc); registry.fill(HIST("eventQA/eventSel"), kFilteredEvent); - if (!collision.sel8()) + if (!collision.sel8()) { return; + } registry.fill(HIST("eventQA/eventSel"), kSel8); registry.fill(HIST("eventQA/eventSel"), kOccupancy); // Add occupancy selection later - const XAxis xaxis{getCentrality(collision), tracks.size(), -1.0}; + const XAxis xaxis{.centrality = getCentrality(collision), .multiplicity = tracks.size()}; if (cfgFillQA) { fillEventQA(collision, xaxis); registry.fill(HIST("eventQA/before/centrality"), xaxis.centrality); registry.fill(HIST("eventQA/before/multiplicity"), xaxis.multiplicity); } - if (cfgUseAdditionalEventCut && !eventSelected(collision, xaxis.multiplicity, xaxis.centrality)) + if (cfgUseAdditionalEventCut && !eventSelected(collision, xaxis.multiplicity, xaxis.centrality)) { return; - if (cfgFillQA) + } + if (cfgFillQA) { fillEventQA(collision, xaxis); + } registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); - processCollision(collision, tracks, xaxis, run); + processCollision(collision, tracks, xaxis); } PROCESS_SWITCH(FlowGfwV02, processData, "Process analysis for non-derived data", true); @@ -1181,12 +1541,12 @@ struct FlowGfwV02 { LOGF(info, "run = %d", run); } loadCorrections(run); - const XAxis xaxis{collision.multiplicity(), tracks.size(), -1.0}; + const XAxis xaxis{.centrality = collision.multiplicity(), .multiplicity = tracks.size()}; registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); - // processCollision(collision, tracks, xaxis, run); + // processCollision(collision, tracks, xaxis); } PROCESS_SWITCH(FlowGfwV02, processCFDerived, "Process analysis for CF derived data", false); void processCFDerivedCorrected(aod::CFCollision const& collision, soa::Filtered> const& tracks) @@ -1196,12 +1556,57 @@ struct FlowGfwV02 { lastRun = run; LOGF(info, "run = %d", run); } - const XAxis xaxis{collision.multiplicity(), tracks.size(), -1.0}; + const XAxis xaxis{.centrality = collision.multiplicity(), .multiplicity = tracks.size()}; + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + processCollision(collision, tracks, xaxis); + } + PROCESS_SWITCH(FlowGfwV02, processCFDerivedCorrected, "Process CF derived data with JWeights acceptance and efficiency corrections", false); + + void processDerived(aod::GfwCollision const& collision, soa::Filtered const& tracks) + { + int run = collision.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + } + loadCorrections(run); + + if ((collision.eventSel() & eventBitMask) != eventBitMask) { + return; + } + if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { + return; + } + + const XAxis xaxis{.centrality = static_cast(cfgCentralityFactor * collision.centrality()), .multiplicity = static_cast(collision.multiplicity())}; + registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); + registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); + processCollision(collision, tracks, xaxis); + } + PROCESS_SWITCH(FlowGfwV02, processDerived, "Process analysis for GFW derived data", false); + + void processDerivedCorrected(aod::GfwCollision const& collision, soa::Filtered> const& tracks) + { + int run = collision.runNumber(); + if (run != lastRun) { + lastRun = run; + LOGF(info, "run = %d", run); + } + + if ((collision.eventSel() & eventBitMask) != eventBitMask) { + return; + } + if (std::abs(collision.posZ()) > cfgEventCuts.cfgZvtxMax) { + return; + } + + const XAxis xaxis{.centrality = static_cast(cfgCentralityFactor * collision.centrality()), .multiplicity = static_cast(collision.multiplicity())}; registry.fill(HIST("eventQA/after/centrality"), xaxis.centrality); registry.fill(HIST("eventQA/after/multiplicity"), xaxis.multiplicity); - // processCollision(collision, tracks, xaxis, run); + processCollision(collision, tracks, xaxis); } - PROCESS_SWITCH(FlowGfwV02, processCFDerivedCorrected, "Process analysis for CF derived data with corrections", false); + PROCESS_SWITCH(FlowGfwV02, processDerivedCorrected, "Process GFW derived data with JWeights acceptance and efficiency corrections", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx index 0c1927d9579..3533e4c4987 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.cxx @@ -119,11 +119,25 @@ void FlowJSPCAnalysis::calculateCorrelators(const int fCentBin) // Histogram filling fillHistograms(fCentBin, j, correlationNum, correlationDenom, weightCorrelationNum, weightCorrelationDenom); + } - correlationNum = 0.; - weightCorrelationNum = 0.; - correlationDenom = 0.; - weightCorrelationDenom = 0.; + // Effective multiplicity N_m (arXiv:2606.10258 Eq. IV.2 / manuscript eq:Neff): + // 1/N_m^{m-1} = sum_i M_m(N_i) N_i^{-(m-1)} / sum_i M_m(N_i), + // with M_m = N(N-1)...(N-m+1) = fCorrelDenoms[m-1] and N = Re(Q_{0,1}). + // TProfile stores _comb; invert offline as N_m = content^{-1/(m-1)}. + // 3SPC → fN3 (m=3); 4SPC → fN4 (m=4). + if (mHistRegistry && qvecs) { + const double nSel = qvecs->QvectorQC[0][1].Re(); + const float centX = static_cast(fCentBin) + 0.5f; + if (nSel > 0.0 && std::isfinite(nSel)) { + if (mWhichSPC == 0 && fCorrelDenoms[2] > 0.0) { + const double invN3Pow = 1.0 / (nSel * nSel); // N^{-(3-1)} + mHistRegistry->fill(HIST("fN3"), centX, invN3Pow, fCorrelDenoms[2]); + } else if (mWhichSPC == 1 && fCorrelDenoms[3] > 0.0) { + const double invN4Pow = 1.0 / (nSel * nSel * nSel); // N^{-(4-1)} + mHistRegistry->fill(HIST("fN4"), centX, invN4Pow, fCorrelDenoms[3]); + } + } } } diff --git a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h index 59a677dccd7..425c78ee9d0 100644 --- a/PWGCF/JCorran/Core/FlowJSPCAnalysis.h +++ b/PWGCF/JCorran/Core/FlowJSPCAnalysis.h @@ -29,6 +29,7 @@ #include +#include #include #include #include @@ -42,6 +43,27 @@ class FlowJSPCAnalysis int getCentBin(float cValue); using JQVectorsT = JQVectors; + /// Fill-grid size for Q_{n,p}: (v8 * nPartDen)+1 harmonics, nPartDen+1 powers. + static constexpr uint32_t Nh3p = 49; // 3-particle SPC, 6-particle denominator + static constexpr uint32_t Nk3p = 7; + static constexpr uint32_t Nh4p = 65; // 4-particle SPC, 8-particle denominator + static constexpr uint32_t Nk4p = 9; + static constexpr uint32_t NhFull = 113; + static constexpr uint32_t NkFull = 15; + void qVectorGrid(int whichSPC, uint32_t& nhUse, uint32_t& nkUse) + { + mWhichSPC = whichSPC; + if (whichSPC == 0) { + nhUse = Nh3p; + nkUse = Nk3p; + } else if (whichSPC == 1) { + nhUse = Nh4p; + nkUse = Nk4p; + } else { + nhUse = NhFull; + nkUse = NkFull; + } + } inline void setQvectors(const JQVectorsT* _qvecs) { qvecs = _qvecs; } void correlation(int c_nPart, int c_nHarmo, int* harmo, double* correlData); void calculateCorrelators(const int fCentBin); @@ -57,6 +79,10 @@ class FlowJSPCAnalysis return; } mHistRegistry->add("FullCentrality", "FullCentrality", o2::framework::HistType::kTH1D, {{100, 0., 100.}}, true); + // Stores _comb = 1/N_m^{m-1} (arXiv:2606.10258 Eq. IV.2). + // Invert offline: N_m = content^{-1/(m-1)}. Weight is M_m. + mHistRegistry->add("fN3", "1/N_{3}^{2};centrality class;1/N_{3}^{2}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); + mHistRegistry->add("fN4", "1/N_{4}^{3};centrality class;1/N_{4}^{3}", {o2::framework::HistType::kTProfile, {{9, 0., 9.}}}, true); mHistRegistry->add("Centrality_0/fResults", "Numerators and denominators", {o2::framework::HistType::kTProfile, {{24, 0., 24.}}}, true); mHistRegistry->add("Centrality_0/fCovResults", "Covariance N*D", {o2::framework::HistType::kTProfile, {{48, 0., 48.}}}, true); mHistRegistry->add("Centrality_0/phiBefore", "Phi before", {o2::framework::HistType::kTH1D, {{100, 0., o2::constants::math::TwoPI}}}, true); @@ -67,13 +93,13 @@ class FlowJSPCAnalysis } } - void setCorrSet(int obsInd, int harmo[8]) + void setCorrSet(int obsInd, int const harmo[8]) { for (int i = 0; i < 8; i++) { fHarmosArray[obsInd][i] = harmo[i]; } } - void setFullCorrSet(int harmo[12][8]) + void setFullCorrSet(int const harmo[12][8]) { memcpy(fHarmosArray, harmo, sizeof(int) * 12 * 8); } @@ -92,13 +118,14 @@ class FlowJSPCAnalysis private: const int mNqHarmos = 113; ///< Highest harmo for Q(n,p): (v8*14part)+1. const int mNqPowers = 15; ///< Max power for Q(n,p): 14part+1. - const JQVectorsT* qvecs; + const JQVectorsT* qvecs = nullptr; o2::framework::HistogramRegistry* mHistRegistry = nullptr; - int fHarmosArray[12][8]; + int fHarmosArray[12][8] = {{0}}; - double fCorrelDenoms[14]; + double fCorrelDenoms[14] = {0}; + int mWhichSPC = 0; ClassDefNV(FlowJSPCAnalysis, 1); }; diff --git a/PWGCF/JCorran/Core/FlowJSPCObservables.h b/PWGCF/JCorran/Core/FlowJSPCObservables.h index 440d70c8bf2..e5db30a94e7 100644 --- a/PWGCF/JCorran/Core/FlowJSPCObservables.h +++ b/PWGCF/JCorran/Core/FlowJSPCObservables.h @@ -38,42 +38,53 @@ class FlowJSPCObservables switch (index) { case 0: { LOGF(info, "Computing three harmonic SPC"); - int harmonicArray01[maxNrComb][8] = { - {3, 6, -3, -3, 0, 0, 0, 0}, - {3, 4, -2, -2, 0, 0, 0, 0}, + // fResults slot j: num at 2j+0.5, denom at 2j+1.5. arXiv:2606.10258 nonflow refs in unused slots. + int const harmonicArray01[maxNrComb][8] = { + {3, 6, -3, -3, 0, 0, 0, 0}, // 0: C633 = + {3, 4, -2, -2, 0, 0, 0, 0}, // 1: C422 = {3, 8, -4, -4, 0, 0, 0, 0}, - {3, 2, 4, -6, 0, 0, 0, 0}, - {3, 2, 3, -5, 0, 0, 0, 0}, + {3, 2, 4, -6, 0, 0, 0, 0}, // 3: C246 = + {3, 2, 3, -5, 0, 0, 0, 0}, // 4: C235 = {3, 3, 4, -7, 0, 0, 0, 0}, // These are three harmonic SPC!! {3, 2, 5, -7, 0, 0, 0, 0}, // These are three harmonic SPC!! {3, 3, 5, -8, 0, 0, 0, 0}, // These are three harmonic SPC!! - {0, 6, -2, -2, -2, 0, 0, 0}, - {0, 2, -3, -4, 5, 0, 0, 0}, - {0, 2, -3, -3, 4, 0, 0, 0}, - {0, 3, 3, -2, -2, -2, 0, 0}}; + // {0, 6, -2, -2, -2, 0, 0, 0}, + // {0, 2, -3, -4, 5, 0, 0, 0}, + // {0, 2, -3, -3, 4, 0, 0, 0}, + // {0, 3, 3, -2, -2, -2, 0, 0}, + {3, 1, 1, -2, 0, 0, 0, 0}, // 8: C112 = , Eqs. (IV.7), (IV.18) + {3, 1, 2, -3, 0, 0, 0, 0}, // 9: C123 = , Eqs. (IV.8), (IV.18) + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}}; memcpy(harmonicArray, harmonicArray01, sizeof(int) * maxNrComb * 8); } break; case 1: { LOGF(info, "Computing four harmonic SPC"); - int harmonicArray02[maxNrComb][8] = { - {4, 6, -2, -2, -2, 0, 0, 0}, + // fResults slot j: num at 2j+0.5, denom at 2j+1.5. arXiv:2606.10258: c1{4}=<<4>>-2<<2>>^2 after averaging, Eq. (IV.6). + int const harmonicArray02[maxNrComb][8] = { + {4, 6, -2, -2, -2, 0, 0, 0}, // 0: C6222 = {4, 2, -3, -4, 5, 0, 0, 0}, {4, 2, -3, -3, 4, 0, 0, 0}, {4, 2, 2, 3, -7, 0, 0, 0}, // These are three harmonic SPC!! {4, 2, 2, 4, -8, 0, 0, 0}, // These are three harmonic SPC!! {4, 2, 7, -4, -5, 0, 0, 0}, {4, 3, -4, -4, 5, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + // {0, 0, 0, 0, 0, 0, 0, 0}, + {4, 1, 1, -1, -1, 0, 0, 0}, // 7: <<4>>_{1,1,-1,-1} for c1{4} + {2, 1, -1, 0, 0, 0, 0, 0}, // 8: <<2>>_{1,-1} = + {3, 1, 1, -2, 0, 0, 0, 0}, // 9: C112 on the 4-particle sample (mixed-order Eq. (IV.19)) {0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}}; memcpy(harmonicArray, harmonicArray02, sizeof(int) * maxNrComb * 8); } break; case 2: { LOGF(info, "Computing five and six harmonic SPC"); - int harmonicArray03[maxNrComb][8] = { + int const harmonicArray03[maxNrComb][8] = { {5, 3, 3, -2, -2, -2, 0, 0}, {5, 2, 2, -3, 4, -5, 0, 0}, {5, 2, 3, 3, -4, -4, 0, 0}, @@ -90,7 +101,7 @@ class FlowJSPCObservables } break; case 3: { LOGF(info, "Computing slected five harmonic SPC"); - int harmonicArray04[maxNrComb][8] = { + int const harmonicArray04[maxNrComb][8] = { {5, 3, 3, -2, -2, -2, 0, 0}, {0, 2, 2, -3, 4, -5, 0, 0}, {5, 2, 3, 3, -4, -4, 0, 0}, diff --git a/PWGCF/JCorran/Core/JCORRANLinkDef.h b/PWGCF/JCorran/Core/JCORRANLinkDef.h index d0e9bef308d..e48231cbdf1 100644 --- a/PWGCF/JCorran/Core/JCORRANLinkDef.h +++ b/PWGCF/JCorran/Core/JCORRANLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ -#define PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -22,5 +21,3 @@ #pragma link C++ class FlowJSPCAnalysis + ; #pragma link C++ class FlowJSPCObservables + ; #pragma link C++ class JEPFlowAnalysis + ; - -#endif // PWGCF_JCORRAN_CORE_JCORRANLINKDEF_H_ diff --git a/PWGCF/JCorran/Core/JQVectors.h b/PWGCF/JCorran/Core/JQVectors.h index d0ba705c32f..aadb821fc2b 100644 --- a/PWGCF/JCorran/Core/JQVectors.h +++ b/PWGCF/JCorran/Core/JQVectors.h @@ -19,6 +19,8 @@ #include +#include +#include #include #include @@ -52,11 +54,16 @@ class JQVectors : public std::conditional_t, JQ using hasInvMass = decltype(std::declval().invMass()); template - inline void Calculate(JInputClass& inputInst, float etamin, float etamax, float massMin = 0.0f, float massMax = 999.9f) + inline void Calculate(JInputClass& inputInst, float etamin, float etamax, float massMin = 0.0f, float massMax = 999.9f, + uint32_t nhUse = nh, uint32_t nkUse = nk) { + // nhUse/nkUse limit the filled (harmonic, power) grid. Defaults keep the full template size. + const uint32_t nH = std::min(nhUse, nh); + const uint32_t nK = std::min(nkUse, nk); + // calculate Q-vector for QC method ( no subgroup ) - for (UInt_t ih = 0; ih < nh; ++ih) { - for (UInt_t ik = 0; ik < nk; ++ik) { + for (UInt_t ih = 0; ih < nH; ++ih) { + for (UInt_t ik = 0; ik < nK; ++ik) { QvectorQC[ih][ik] = Q(0, 0); if constexpr (gap) { for (UInt_t isub = 0; isub < 2; ++isub) @@ -64,7 +71,7 @@ class JQVectors : public std::conditional_t, JQ } } } - for (auto& track : inputInst) { + for (auto const& track : inputInst) { if (track.eta() < -etamax || track.eta() > etamax) continue; using JInputClassIter = typename JInputClass::iterator; @@ -74,10 +81,15 @@ class JQVectors : public std::conditional_t, JQ } UInt_t isub = (UInt_t)(track.eta() > 0.0); - for (UInt_t ih = 0; ih < nh; ++ih) { + const Double_t phi = track.phi(); + const Double_t c1 = TMath::Cos(phi); + const Double_t s1 = TMath::Sin(phi); + Double_t cn = 1.0; // cos(ih * phi), ih = 0 + Double_t sn = 0.0; // sin(ih * phi) + for (UInt_t ih = 0; ih < nH; ++ih) { Double_t tf = 1.0; - for (UInt_t ik = 0; ik < nk; ++ik) { - Q q(tf * TMath::Cos(ih * track.phi()), tf * TMath::Sin(ih * track.phi())); + for (UInt_t ik = 0; ik < nK; ++ik) { + Q q(tf * cn, tf * sn); QvectorQC[ih][ik] += q; if constexpr (gap) { @@ -90,6 +102,10 @@ class JQVectors : public std::conditional_t, JQ if constexpr (std::experimental::is_detected::value) tf *= track.weightEff(); } + const Double_t cnNext = cn * c1 - sn * s1; + const Double_t snNext = cn * s1 + sn * c1; + cn = cnNext; + sn = snNext; } } } diff --git a/PWGCF/JCorran/Tasks/CMakeLists.txt b/PWGCF/JCorran/Tasks/CMakeLists.txt index 465083509d0..1cd594f8f19 100644 --- a/PWGCF/JCorran/Tasks/CMakeLists.txt +++ b/PWGCF/JCorran/Tasks/CMakeLists.txt @@ -39,6 +39,16 @@ o2physics_add_dpl_workflow(epdzeroflow-analysis PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(j-v02-dmeson-analysis + SOURCES jV02DmesonAnalysis.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::JCorran + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(j-v02-dmeson-toy + SOURCES jV02DmesonToy.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(j-fluc-efficiency-task SOURCES jFlucEfficiencyTask.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::JCorran diff --git a/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx b/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx index 6892ab58658..cfe7f931a39 100644 --- a/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx +++ b/PWGCF/JCorran/Tasks/flowJSPCAnalysis.cxx @@ -109,6 +109,8 @@ struct flowJSPCAnalysis { std::unique_ptr multCutFormula; std::array multCutFormulaParamIndex; + uint32_t mNhUse = FlowJSPCAnalysis::NhFull; + uint32_t mNkUse = FlowJSPCAnalysis::NkFull; void init(InitContext const&) { @@ -118,6 +120,8 @@ struct flowJSPCAnalysis { spcObservables.setSPCObservables(cfgWhichSPC); spcAnalysis.setFullCorrSet(spcObservables.harmonicArray); + spcAnalysis.qVectorGrid(cfgWhichSPC, mNhUse, mNkUse); + LOGF(info, "Q-vector fill grid: nh=%u nk=%u (cfgWhichSPC=%d)", mNhUse, mNkUse, cfgWhichSPC.value); histManager.setHistRegistryQA(&qaHistRegistry); histManager.setDebugLog(false); @@ -186,7 +190,7 @@ struct flowJSPCAnalysis { if (cfgFillQA) histManager.fillEventQA<1>(collision, cBin, cent, nTracks); - jqvecs.Calculate(tracks, 0.0, cfgTrackCuts.cfgEtaMax); + jqvecs.Calculate(tracks, 0.0, cfgTrackCuts.cfgEtaMax, 0.0f, 999.9f, mNhUse, mNkUse); spcAnalysis.setQvectors(&jqvecs); spcAnalysis.calculateCorrelators(cBin); } diff --git a/PWGCF/JCorran/Tasks/jEPDzeroFlowAnalysis.cxx b/PWGCF/JCorran/Tasks/jEPDzeroFlowAnalysis.cxx index 0240170f245..baeedae8cf0 100644 --- a/PWGCF/JCorran/Tasks/jEPDzeroFlowAnalysis.cxx +++ b/PWGCF/JCorran/Tasks/jEPDzeroFlowAnalysis.cxx @@ -119,7 +119,7 @@ struct jEPDzeroFlowAnalysis { } template - bool eventSelected(TCollision collision) + bool eventSelected(const TCollision& collision) { if (!collision.sel8()) { return false; diff --git a/PWGCF/JCorran/Tasks/jEPFlowAnalysis.cxx b/PWGCF/JCorran/Tasks/jEPFlowAnalysis.cxx index 0038553d347..436da2a42e0 100644 --- a/PWGCF/JCorran/Tasks/jEPFlowAnalysis.cxx +++ b/PWGCF/JCorran/Tasks/jEPFlowAnalysis.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \author Maxim Virta (maxim.virta@cern.ch) +/// \author Maxim Virta (maxim.virta@cern.ch), junlee kim (junlee.kim@cern.ch) /// \brief flow measurement with q-vectors /// \file jEPFlowAnalysis.cxx /// \since Jul 2024 @@ -17,15 +17,20 @@ #include "PWGCF/JCorran/Core/FlowJHistManager.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/EventPlaneHelper.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FT0Corrected.h" #include "Common/DataModel/Qvectors.h" #include "Common/DataModel/TrackSelectionTables.h" #include #include #include +#include +#include #include #include #include @@ -35,48 +40,73 @@ #include #include #include +#include #include #include -#include +#include +#include #include #include #include +#include +#include #include #include +#include #include +#include #include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +using namespace o2::constants::physics; using namespace std; -using MyCollisions = soa::Join; +using MyCollisions = soa::Join; using MyTracks = soa::Join; using MyCollisionsMC = soa::Join; using MyTracksMC = soa::Join; -struct jEPFlowAnalysis { +struct JEPFlowAnalysis { + enum Q2selMethod { + kNosel = 0, + kHsel, + kHistsel + }; + + Service pdg{}; HistogramRegistry epFlowHistograms{"EPFlow", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; EventPlaneHelper helperEP; + o2::ft0::Geometry ft0geom; + o2::fv0::Geometry* fv0geom = nullptr; FlowJHistManager histManager; bool debug = kFALSE; - Service ccdb; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; struct : ConfigurableGroup { - Configurable cfgURL{"cfgURL", + Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; - Configurable nolaterthan{"ccdb-no-later-than", + Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; } cfgCcdbParam; + struct : ConfigurableGroup { + Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + } rctCut; + RCTFlagsChecker rctChecker; + // Set Configurables here struct : ConfigurableGroup { Configurable cfgPtMin{"cfgPtMin", 0.2f, "Minimum pT used for track seletion."}; @@ -91,6 +121,9 @@ struct jEPFlowAnalysis { Configurable cfgEffCor{"cfgEffCor", false, "flag for efficiency correction"}; Configurable cfgEffCorDir{"cfgEffCorDir", "Users/n/nmallick/Run3OO/Eff/LHC25h3b_FT0C", "path for efficiency correction"}; + Configurable cfgGainEq{"cfgGainEq", false, "flag for gain equalization"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Users/j/junlee/Qvector/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgTrkSelFlag{"cfgTrkSelFlag", true, "flag for track selection"}; Configurable cfgSystStudy{"cfgSystStudy", false, "flag for syst study"}; Configurable cfgITSNCls{"cfgITSNCls", 5, "minimum number of its clusters"}; @@ -108,14 +141,36 @@ struct jEPFlowAnalysis { Configurable cfgShiftPath{"cfgShiftPath", "Users/j/junlee/Qvector/QvecCalib/Shift", "Path for Shift"}; Configurable cfgVertexZ{"cfgVertexZ", 10.0, "Maximum vertex Z selection"}; + Configurable cfgq2analysis{"cfgq2analysis", 0, "ese analysis selection mode"}; + Configurable> cfgMultq2SelBin{"cfgMultq2SelBin", {0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 100}, ""}; + Configurable> cfgMultq2high{"cfgMultq2high", {}, ""}; + Configurable> cfgMultq2low{"cfgMultq2low", {}, ""}; + Configurable cfgQ2SelFrac{"cfgQ2SelFrac", 0.5, "ese analysis q2 selection with cfgq2analysis=2"}; + + Configurable cfgJetSubEvtSel{"cfgJetSubEvtSel", 0, "0: none, 1: Ratio, 2: relative ratio"}; + Configurable> cfgJetSubEvlSelVar{"cfgJetSubEvlSelVar", {}, ""}; + + Configurable cfgQselHistPath{"cfgQselHistPath", "", "CCDB path for q2 histogram"}; + Configurable cfgEventQAonly{"cfgEventQAonly", false, "event loop only"}; + + Configurable cfgSelEvtTwoHP{"cfgSelEvtTwoHP", false, "event selection with two high pT"}; + Configurable cfgHighPtSel{"cfgHighPtSel", 5.0, "pT threshold with cfgSelEvtTwoHP"}; + Configurable cfgTwoLPAngle{"cfgTwoLPAngle", 0.5, "azimuthal difference between two LP"}; + Configurable cfgEtaBalancing{"cfgEtaBalancing", 0.5, "pseudorapidity difference between two LP"}; + Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; Configurable cfgRefAName{"cfgRefAName", "TPCPos", "The name of detector for reference A"}; Configurable cfgRefBName{"cfgRefBName", "TPCNeg", "The name of detector for reference B"}; ConfigurableAxis cfgAxisCent{"cfgAxisCent", {100, 0, 100}, ""}; ConfigurableAxis cfgAxisPt{"cfgAxisPt", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 15.0, 30.0, 50.0, 70.0, 100.0}, ""}; + ConfigurableAxis cfgAxisEta{"cfgAxisEta", {20, -1, 1}, ""}; ConfigurableAxis cfgAxisCos{"cfgAxisCos", {102, -1.02, 1.02}, ""}; ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {200, -5.0, 5.0}, ""}; + ConfigurableAxis cfgAxisQ2{"cfgAxisQ2", {100, 0, 10}, ""}; + ConfigurableAxis cfgAxisAmp{"cfgAxisAmp", {100, 0, 1e5}, ""}; + ConfigurableAxis cfgAxisAmpR{"cfgAxisAmpR", {VARIABLE_WIDTH, 0.0, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1.0}, ""}; + ConfigurableAxis cfgAxisActR{"cfgAxisActR", {100, 0, 100}, ""}; ConfigurableAxis cfgAxisCentMC{"cfgAxisCentMC", {5, 0, 100}, ""}; ConfigurableAxis cfgAxisVtxZMC{"cfgAxisVtxZMC", {20, -10, 10}, ""}; @@ -125,74 +180,181 @@ struct jEPFlowAnalysis { Filter trackFilter = (aod::track::pt > cfgTrackCuts.cfgPtMin) && (nabs(aod::track::eta) < cfgTrackCuts.cfgEtaMax); - int detId; - int refAId; - int refBId; - int harmInd; + int detId = 0; + int refAId = 4; + int refBId = 5; + int harmInd = 0; int currentRunNumber = -999; int lastRunNumber = -999; - float cent; + float cent = -1.; + + float minQvecAmp = 1e-5; + float minChg = 0.1; + float q2Mag = -1.; - std::vector shiftprofile{}; + float activity = -1.; + float qOvecM = -1.; + + float leadingPt = -1.; + float leadingPhi = -999.; + float leadingEta = -999.; + + float subleadingPt = -1.; + float subleadingPhi = -999.; + float subleadingEta = -999.; + + std::vector shiftprofile; std::string fullCCDBShiftCorrPath; THn* effMap = nullptr; + TH3F* q2Map = nullptr; + float q2selHigh = 100.; + float q2selLow = 0.; + + int nHighPt = 0; + int minnHighPt = 2; + + std::vector ft0RelGainConst; + std::vector fv0RelGainConst; + + bool q2sel(float q2, bool isHigh) + { + auto it = std::upper_bound(cfgMultq2SelBin->begin(), cfgMultq2SelBin->end(), cent); + int idx = std::distance(cfgMultq2SelBin->begin(), it) - 1; + + if (idx < 0) { + idx = 0; + } + + if (isHigh) { + if (idx >= static_cast(cfgMultq2high->size())) { + idx = cfgMultq2high->size() - 1; + } + float sel = cfgMultq2high->at(idx); + return q2 > sel; + } + + if (idx >= static_cast(cfgMultq2low->size())) { + idx = cfgMultq2low->size() - 1; + } + float sel = cfgMultq2low->at(idx); + return q2 < sel; + } template int getdetId(const T& name) { if (name.value == "FT0C") { return 0; - } else if (name.value == "FT0A") { + } + if (name.value == "FT0A") { return 1; - } else if (name.value == "FT0M") { + } + if (name.value == "FT0M") { return 2; - } else if (name.value == "FV0A") { + } + if (name.value == "FV0A") { return 3; - } else if (name.value == "TPCPos") { + } + if (name.value == "TPCPos") { return 4; - } else if (name.value == "TPCNeg") { + } + if (name.value == "TPCNeg") { return 5; - } else if (name.value == "TPCTot") { + } + if (name.value == "TPCTot") { return 6; - } else { - return 0; } + return 0; } template bool eventSel(const Col& coll) { - if (std::abs(coll.posZ()) > cfgVertexZ) + if (std::abs(coll.posZ()) > cfgVertexZ) { return false; + } + switch (cfgEvtSel) { case 0: // Sel8 - if (!coll.sel8()) + if (!coll.sel8()) { return false; + } break; case 1: // PbPb standard - if (!coll.sel8() || !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) + if (!coll.sel8() || !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; + } break; case 2: // PbPb with pileup if (!coll.sel8() || !coll.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard) || - !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) + !coll.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; + } break; case 3: // Small systems (OO, NeNe, pp) - if (!coll.sel8() || !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) + if (!coll.sel8() || !coll.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; + } break; + + default: + return false; } // Check occupancy - if (coll.trackOccupancyInTimeRange() > cfgMaxOccupancy || coll.trackOccupancyInTimeRange() < cfgMinOccupancy) + if (coll.trackOccupancyInTimeRange() > cfgMaxOccupancy || coll.trackOccupancyInTimeRange() < cfgMinOccupancy) { return false; - + } + if (rctCut.requireRCTFlagChecker && !rctChecker(coll)) { + return false; + } return true; } + template + float calcFT0CRawQVecMag(const Col& coll, int nMode) + { + float ampFT0C = 0.f; + + if (!coll.has_foundFT0()) { + return false; + } + + auto ft0 = coll.foundFT0(); + TComplex qVecFT0C(0., 0.); + + for (std::size_t iChC = 0; iChC < ft0.channelC().size(); ++iChC) { + int ft0CChId = ft0.channelC()[iChC] + 96; + float ampl = ft0.amplitudeC()[iChC] / (cfgGainEq ? ft0RelGainConst[ft0CChId] : 1.); + helperEP.SumQvectors(0, ft0CChId, ampl, nMode, qVecFT0C, ampFT0C, ft0geom, fv0geom); + } + + return qVecFT0C.Rho(); + } + + template + float calcFT0CLocalActivity(const Col& coll) + { + float amp = 0.0; + float amp2 = 0.0; + if (!coll.has_foundFT0()) { + return false; + } + + auto ft0 = coll.foundFT0(); + + for (std::size_t iChC = 0; iChC < ft0.channelC().size(); ++iChC) { + int ft0CChId = ft0.channelC()[iChC] + 96; + float ampl = ft0.amplitudeC()[iChC] / (cfgGainEq ? ft0RelGainConst[ft0CChId] : 1.); + amp += ampl; + amp2 += ampl * ampl; + } + + return amp2 / (amp * amp); + } + template uint8_t trackSel(const Trk& track) { @@ -225,9 +387,9 @@ struct jEPFlowAnalysis { template void fillvn(const Col& coll, const Trk& tracks) { - float eps[3] = {0.}; - float qx_shifted[3] = {0.}; - float qy_shifted[3] = {0.}; + std::array eps{}; + std::array qx_shifted{}; + std::array qy_shifted{}; for (int i = 0; i < cfgnMode; i++) { // loop over different harmonic orders harmInd = cfgnTotalSystem * 4 * (i) + 3; // harmonic index to access corresponding Q-vector as all Q-vectors are in same vector @@ -278,6 +440,63 @@ struct jEPFlowAnalysis { float resNumB = helperEP.GetResolution(eps[0], eps[2], i + 2); float resDenom = helperEP.GetResolution(eps[1], eps[2], i + 2); + q2Mag = std::sqrt(std::pow(qx_shifted[0], 2) + std::pow(qy_shifted[0], 2)); + + if (cfgq2analysis == kHistsel) { + q2selHigh = q2Map->GetBinContent(q2Map->GetXaxis()->FindBin(i + 2), q2Map->GetYaxis()->FindBin(cent), q2Map->GetZaxis()->FindBin(cfgQ2SelFrac)); + q2selLow = q2Map->GetBinContent(q2Map->GetXaxis()->FindBin(i + 2), q2Map->GetYaxis()->FindBin(cent), q2Map->GetZaxis()->FindBin(1. - cfgQ2SelFrac)); + } + + if (i == 0) { + leadingPt = 0.0; + leadingPhi = 0.0; + leadingEta = 0.0; + + subleadingPt = 0.0; + subleadingPhi = 0.0; + subleadingEta = 0.0; + + nHighPt = 0; + } + + if (cfgSelEvtTwoHP && i == 0) { + for (const auto& track : tracks) { + if (cfgTrkSelFlag && trackSel(track) != 0) { + continue; + } + + if (leadingPt < track.pt()) { + subleadingPt = leadingPt; + subleadingPhi = leadingPhi; + subleadingEta = leadingEta; + + leadingPt = track.pt(); + leadingPhi = track.phi(); + leadingEta = track.eta(); + } else if (track.pt() > subleadingPt) { + subleadingPt = track.pt(); + subleadingPhi = track.phi(); + subleadingEta = track.eta(); + } + + if (track.pt() > cfgHighPtSel) { + nHighPt++; + } + } + } + + if (cfgSelEvtTwoHP && nHighPt < minnHighPt) { + continue; + } + + if (cfgSelEvtTwoHP && std::abs(RecoDecay::constrainAngle(leadingPhi - subleadingPhi, 0) - constants::math::PI) > cfgTwoLPAngle) { + continue; + } + + if (cfgSelEvtTwoHP && std::abs(leadingEta + subleadingEta) > cfgEtaBalancing) { + continue; + } + epFlowHistograms.fill(HIST("EpDet"), i + 2, cent, eps[0]); epFlowHistograms.fill(HIST("EpRefA"), i + 2, cent, eps[1]); epFlowHistograms.fill(HIST("EpRefB"), i + 2, cent, eps[2]); @@ -286,6 +505,8 @@ struct jEPFlowAnalysis { epFlowHistograms.fill(HIST("EpResDetRefB"), i + 2, cent, resNumB); epFlowHistograms.fill(HIST("EpResRefARefB"), i + 2, cent, resDenom); + epFlowHistograms.fill(HIST("hQ2"), i + 2, cent, q2Mag); + epFlowHistograms.fill(HIST("EpResQvecDetRefAxx"), i + 2, cent, qx_shifted[0] * qx_shifted[1] + qy_shifted[0] * qy_shifted[1]); epFlowHistograms.fill(HIST("EpResQvecDetRefAxy"), i + 2, cent, qx_shifted[1] * qy_shifted[0] - qx_shifted[0] * qy_shifted[1]); epFlowHistograms.fill(HIST("EpResQvecDetRefBxx"), i + 2, cent, qx_shifted[0] * qx_shifted[2] + qy_shifted[0] * qy_shifted[2]); @@ -293,9 +514,45 @@ struct jEPFlowAnalysis { epFlowHistograms.fill(HIST("EpResQvecRefARefBxx"), i + 2, cent, qx_shifted[1] * qx_shifted[2] + qy_shifted[1] * qy_shifted[2]); epFlowHistograms.fill(HIST("EpResQvecRefARefBxy"), i + 2, cent, qx_shifted[2] * qy_shifted[1] - qx_shifted[1] * qy_shifted[2]); + if (cfgJetSubEvtSel) { + epFlowHistograms.fill(HIST("EpResQvecEvslDetRefAxx"), i + 2, cent, qx_shifted[0] * qx_shifted[1] + qy_shifted[0] * qy_shifted[1]); + epFlowHistograms.fill(HIST("EpResQvecEvslDetRefBxx"), i + 2, cent, qx_shifted[0] * qx_shifted[2] + qy_shifted[0] * qy_shifted[2]); + epFlowHistograms.fill(HIST("EpResQvecEvslRefARefBxx"), i + 2, cent, qx_shifted[1] * qx_shifted[2] + qy_shifted[1] * qy_shifted[2]); + } + + if (cfgq2analysis == kHsel) { + if (q2sel(q2Mag, true)) { + epFlowHistograms.fill(HIST("EpResQvecDetRefAxx_q2high"), i + 2, cent, qx_shifted[0] * qx_shifted[1] + qy_shifted[0] * qy_shifted[1]); + epFlowHistograms.fill(HIST("EpResQvecDetRefBxx_q2high"), i + 2, cent, qx_shifted[0] * qx_shifted[2] + qy_shifted[0] * qy_shifted[2]); + epFlowHistograms.fill(HIST("EpResQvecRefARefBxx_q2high"), i + 2, cent, qx_shifted[1] * qx_shifted[2] + qy_shifted[1] * qy_shifted[2]); + } else if (q2sel(q2Mag, false)) { + epFlowHistograms.fill(HIST("EpResQvecDetRefAxx_q2low"), i + 2, cent, qx_shifted[0] * qx_shifted[1] + qy_shifted[0] * qy_shifted[1]); + epFlowHistograms.fill(HIST("EpResQvecDetRefBxx_q2low"), i + 2, cent, qx_shifted[0] * qx_shifted[2] + qy_shifted[0] * qy_shifted[2]); + epFlowHistograms.fill(HIST("EpResQvecRefARefBxx_q2low"), i + 2, cent, qx_shifted[1] * qx_shifted[2] + qy_shifted[1] * qy_shifted[2]); + } + } else if (cfgq2analysis == kHistsel) { + if (q2Mag > q2selHigh) { + epFlowHistograms.fill(HIST("EpResQvecDetRefAxx_q2high"), i + 2, cent, qx_shifted[0] * qx_shifted[1] + qy_shifted[0] * qy_shifted[1]); + epFlowHistograms.fill(HIST("EpResQvecDetRefBxx_q2high"), i + 2, cent, qx_shifted[0] * qx_shifted[2] + qy_shifted[0] * qy_shifted[2]); + epFlowHistograms.fill(HIST("EpResQvecRefARefBxx_q2high"), i + 2, cent, qx_shifted[1] * qx_shifted[2] + qy_shifted[1] * qy_shifted[2]); + } else if (q2Mag < q2selLow) { + epFlowHistograms.fill(HIST("EpResQvecDetRefAxx_q2low"), i + 2, cent, qx_shifted[0] * qx_shifted[1] + qy_shifted[0] * qy_shifted[1]); + epFlowHistograms.fill(HIST("EpResQvecDetRefBxx_q2low"), i + 2, cent, qx_shifted[0] * qx_shifted[2] + qy_shifted[0] * qy_shifted[2]); + epFlowHistograms.fill(HIST("EpResQvecRefARefBxx_q2low"), i + 2, cent, qx_shifted[1] * qx_shifted[2] + qy_shifted[1] * qy_shifted[2]); + } + } + + if (cfgEventQAonly) { + continue; + } + + leadingPt = 0.0; + leadingPhi = 0.0; + leadingEta = 0.0; for (const auto& track : tracks) { - if (cfgTrkSelFlag && trackSel(track)) + if (cfgTrkSelFlag && trackSel(track) != 0) { continue; + } if (cfgEffCor) { weight = getEfficiencyCorrection(effMap, track.eta(), track.pt(), cent, coll.posZ()); @@ -307,31 +564,60 @@ struct jEPFlowAnalysis { epFlowHistograms.fill(HIST("vncos"), i + 2, cent, track.pt(), vn, weight); epFlowHistograms.fill(HIST("vnsin"), i + 2, cent, track.pt(), vnSin, weight); - epFlowHistograms.fill(HIST("SPvnxx"), i + 2, cent, track.pt(), (std::cos(track.phi() * static_cast(i + 2)) * qx_shifted[0] + std::sin(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); - epFlowHistograms.fill(HIST("SPvnxy"), i + 2, cent, track.pt(), (std::sin(track.phi() * static_cast(i + 2)) * qx_shifted[0] - std::cos(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); + epFlowHistograms.fill(HIST("SPvnxx"), i + 2, cent, track.pt(), track.eta(), (std::cos(track.phi() * static_cast(i + 2)) * qx_shifted[0] + std::sin(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); + epFlowHistograms.fill(HIST("SPvnxy"), i + 2, cent, track.pt(), track.eta(), (std::sin(track.phi() * static_cast(i + 2)) * qx_shifted[0] - std::cos(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); + + if (cfgq2analysis == kHsel) { + if (q2sel(q2Mag, true)) { + epFlowHistograms.fill(HIST("SPvnxx_q2high"), i + 2, cent, track.pt(), track.eta(), (std::cos(track.phi() * static_cast(i + 2)) * qx_shifted[0] + std::sin(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); + } else if (q2sel(q2Mag, false)) { + epFlowHistograms.fill(HIST("SPvnxx_q2low"), i + 2, cent, track.pt(), track.eta(), (std::cos(track.phi() * static_cast(i + 2)) * qx_shifted[0] + std::sin(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); + } + } else if (cfgq2analysis == kHistsel) { + if (q2Mag > q2selHigh) { + epFlowHistograms.fill(HIST("SPvnxx_q2high"), i + 2, cent, track.pt(), track.eta(), (std::cos(track.phi() * static_cast(i + 2)) * qx_shifted[0] + std::sin(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); + } else if (q2Mag < q2selLow) { + epFlowHistograms.fill(HIST("SPvnxx_q2low"), i + 2, cent, track.pt(), track.eta(), (std::cos(track.phi() * static_cast(i + 2)) * qx_shifted[0] + std::sin(track.phi() * static_cast(i + 2)) * qy_shifted[0]), weight); + } + } + } + if (i == 0) { // second harmonic only + epFlowHistograms.fill(HIST("hQoverM"), cent, leadingPt, qOvecM); + epFlowHistograms.fill(HIST("hActivity"), cent, leadingPt, activity); + + epFlowHistograms.fill(HIST("hQoverM2M"), cent, coll.qvecAmp()[detId], qOvecM); + epFlowHistograms.fill(HIST("hQoverM2Q2"), cent, q2Mag, qOvecM); + + epFlowHistograms.fill(HIST("hQoverMdphi"), cent, RecoDecay::constrainAngle(leadingPhi - eps[0], -constants::math::PI), qOvecM); + epFlowHistograms.fill(HIST("hActivitydphi"), cent, RecoDecay::constrainAngle(leadingPhi - eps[0], -constants::math::PI), leadingPt, activity); } } } double getEfficiencyCorrection(THn* eff, float eta, float pt, float multiplicity, float posZ) { - int effVars[4]; + std::array effVars{}; effVars[0] = eff->GetAxis(0)->FindBin(eta); effVars[1] = eff->GetAxis(1)->FindBin(pt); effVars[2] = eff->GetAxis(2)->FindBin(multiplicity); effVars[3] = eff->GetAxis(3)->FindBin(posZ); - return eff->GetBinContent(effVars); + return eff->GetBinContent(effVars.data()); } void init(InitContext const&) { - ccdb->setURL(cfgCcdbParam.cfgURL); + ccdb->setURL(cfgCcdbParam.cfgUrl); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + fv0geom = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); + ft0geom.calculateChannelCenter(); + detId = getdetId(cfgDetName); refAId = getdetId(cfgRefAName); refBId = getdetId(cfgRefBName); @@ -342,8 +628,13 @@ struct jEPFlowAnalysis { AxisSpec axisCent{cfgAxisCent, "cent"}; AxisSpec axisPt{cfgAxisPt, "pT"}; + AxisSpec axisEta{cfgAxisEta, "eta"}; AxisSpec axisCos{cfgAxisCos, "cos"}; AxisSpec axisQvec{cfgAxisQvec, "Qvec"}; + AxisSpec axisQ2{cfgAxisQ2, "Q2"}; + AxisSpec axisAmp{cfgAxisAmp, "M"}; + AxisSpec axisAmpR{cfgAxisAmpR, "QoverM"}; + AxisSpec axisActR{cfgAxisActR, "Activity"}; AxisSpec axisCentMC{cfgAxisCentMC, "cent"}; AxisSpec axisVtxZMC{cfgAxisVtxZMC, "vtxz"}; @@ -359,6 +650,19 @@ struct jEPFlowAnalysis { epFlowHistograms.add("EpResDetRefB", "", {HistType::kTH3F, {axisMod, axisCent, axisEvtPl}}); epFlowHistograms.add("EpResRefARefB", "", {HistType::kTH3F, {axisMod, axisCent, axisEvtPl}}); + epFlowHistograms.add("hQ2", "", {HistType::kTH3F, {axisMod, axisCent, axisQ2}}); + + epFlowHistograms.add("hQoverMCnt", "", {HistType::kTH2F, {axisCent, axisAmpR}}); + epFlowHistograms.add("hActivityCnt", "", {HistType::kTH2F, {axisCent, axisActR}}); + + epFlowHistograms.add("hQoverM", "", {HistType::kTH3F, {axisCent, axisPt, axisAmpR}}); + epFlowHistograms.add("hQoverMdphi", "", {HistType::kTH3F, {axisCent, axisEvtPl, axisAmpR}}); + epFlowHistograms.add("hQoverM2M", "", {HistType::kTH3F, {axisCent, axisAmp, axisAmpR}}); + epFlowHistograms.add("hQoverM2Q2", "", {HistType::kTH3F, {axisCent, axisQ2, axisAmpR}}); + + epFlowHistograms.add("hActivity", "", {HistType::kTH3F, {axisCent, axisPt, axisActR}}); + epFlowHistograms.add("hActivitydphi", "", {HistType::kTHnSparseF, {axisCent, axisEvtPl, axisPt, axisActR}}); + epFlowHistograms.add("vncos", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisCos}}); epFlowHistograms.add("vnsin", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisCos}}); @@ -369,8 +673,25 @@ struct jEPFlowAnalysis { epFlowHistograms.add("EpResQvecRefARefBxx", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); epFlowHistograms.add("EpResQvecRefARefBxy", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); - epFlowHistograms.add("SPvnxx", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisQvec}}); - epFlowHistograms.add("SPvnxy", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisQvec}}); + epFlowHistograms.add("EpResQvecEvslDetRefAxx", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + epFlowHistograms.add("EpResQvecEvslDetRefBxx", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + epFlowHistograms.add("EpResQvecEvslRefARefBxx", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + + if (cfgq2analysis != 0) { + epFlowHistograms.add("EpResQvecDetRefAxx_q2high", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + epFlowHistograms.add("EpResQvecDetRefBxx_q2high", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + epFlowHistograms.add("EpResQvecRefARefBxx_q2high", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + epFlowHistograms.add("EpResQvecDetRefAxx_q2low", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + epFlowHistograms.add("EpResQvecDetRefBxx_q2low", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + epFlowHistograms.add("EpResQvecRefARefBxx_q2low", "", {HistType::kTH3F, {axisMod, axisCent, axisQvec}}); + } + + epFlowHistograms.add("SPvnxx", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisEta, axisQvec}}); + epFlowHistograms.add("SPvnxy", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisEta, axisQvec}}); + if (cfgq2analysis != 0) { + epFlowHistograms.add("SPvnxx_q2high", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisEta, axisQvec}}); + epFlowHistograms.add("SPvnxx_q2low", "", {HistType::kTHnSparseF, {axisMod, axisCent, axisPt, axisEta, axisQvec}}); + } epFlowHistograms.add("hCentrality", "", {HistType::kTH1F, {axisCent}}); epFlowHistograms.add("hVertex", "", {HistType::kTH1F, {axisVertex}}); @@ -380,11 +701,12 @@ struct jEPFlowAnalysis { epFlowHistograms.add("MC/hPartRec", "", {kTHnSparseF, {cfgAxisCentMC, cfgAxisVtxZMC, cfgAxisEtaMC, cfgAxisPhiMC, cfgAxisPtMC}}); } - void processDefault(MyCollisions::iterator const& coll, soa::Filtered const& tracks, aod::BCsWithTimestamps const&) + void processDefault(MyCollisions::iterator const& coll, soa::Filtered const& tracks, aod::BCsWithTimestamps const&, aod::FT0s const&) { if (cfgAddEvtSel) { - if (!eventSel(coll)) + if (!eventSel(coll)) { return; + } } if (cfgEffCor) { @@ -396,6 +718,56 @@ struct jEPFlowAnalysis { } } + if (cfgGainEq) { + auto bc = coll.bc_as(); + currentRunNumber = bc.runNumber(); + auto timestamp = bc.timestamp(); + + std::string fullPath; + if (currentRunNumber != lastRunNumber) { + ft0RelGainConst.clear(); + fv0RelGainConst.clear(); + ft0RelGainConst = {}; + fv0RelGainConst = {}; + + fullPath = cfgGainEqPath; + fullPath += "/FT0"; + const int nPixelsFT0 = 208; + const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!objft0Gain) { + for (auto i{0u}; i < nPixelsFT0; i++) { + ft0RelGainConst.push_back(1.); + } + } else { + ft0RelGainConst = *(objft0Gain); + } + + fullPath = cfgGainEqPath; + fullPath += "/FV0"; + const int nChannelsFV0 = 48; + const auto objfv0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!objfv0Gain) { + for (auto i{0u}; i < nChannelsFV0; i++) { + fv0RelGainConst.push_back(1.); + } + } else { + fv0RelGainConst = *(objfv0Gain); + } + + lastRunNumber = currentRunNumber; + } + } + + if (cfgq2analysis == kHistsel) { + auto bc = coll.bc_as(); + currentRunNumber = bc.runNumber(); + if (currentRunNumber != lastRunNumber) { + std::string fullPath; + fullPath = cfgQselHistPath; + q2Map = ccdb->getForTimeStamp(cfgQselHistPath, bc.timestamp()); + } + } + cent = coll.cent(); epFlowHistograms.fill(HIST("hCentrality"), cent); epFlowHistograms.fill(HIST("hVertex"), coll.posZ()); @@ -416,12 +788,29 @@ struct jEPFlowAnalysis { } } - if (coll.qvecAmp()[detId] < 1e-5 || coll.qvecAmp()[refAId] < 1e-5 || coll.qvecAmp()[refBId] < 1e-5) + if (coll.qvecAmp()[detId] < minQvecAmp || coll.qvecAmp()[refAId] < minQvecAmp || coll.qvecAmp()[refBId] < minQvecAmp) { return; + } + + qOvecM = calcFT0CRawQVecMag(coll, 2) / coll.qvecAmp()[detId]; // second order + activity = calcFT0CLocalActivity(coll); + epFlowHistograms.fill(HIST("hQoverMCnt"), cent, qOvecM); + epFlowHistograms.fill(HIST("hActivityCnt"), cent, activity); + + if ((cfgJetSubEvtSel & 1) != 0) { + if (cfgJetSubEvlSelVar->at(0) < qOvecM) { + return; + } + } + if ((cfgJetSubEvtSel & 2) != 0) { + if (cfgJetSubEvlSelVar->at(1) < activity) { + return; + } + } fillvn(coll, tracks); } - PROCESS_SWITCH(jEPFlowAnalysis, processDefault, "default process", true); + PROCESS_SWITCH(JEPFlowAnalysis, processDefault, "default process", true); void processMCRec(MyCollisionsMC::iterator const& coll, MyTracksMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) { @@ -430,11 +819,12 @@ struct jEPFlowAnalysis { } if (cfgAddEvtSel) { - if (!eventSel(coll)) + if (!eventSel(coll)) { return; + } } - float cent = coll.centFT0C(); + cent = coll.centFT0C(); if (cfgEffCor) { auto bc = coll.bc_as(); @@ -445,12 +835,12 @@ struct jEPFlowAnalysis { } } - for (auto trk : tracks) { + for (const auto& trk : tracks) { if (!trk.has_mcParticle()) { continue; } - if (trackSel(trk)) { + if (trackSel(trk) != 0) { continue; } @@ -461,12 +851,13 @@ struct jEPFlowAnalysis { } } } - PROCESS_SWITCH(jEPFlowAnalysis, processMCRec, "process for MC", false); + PROCESS_SWITCH(JEPFlowAnalysis, processMCRec, "process for MC", false); void processMCGen(MyCollisionsMC::iterator const& coll, aod::McParticles const& mcParticles, aod::McCollisions const&) { - if (!coll.has_mcCollision()) + if (!coll.has_mcCollision()) { return; + } const auto mcColl = coll.mcCollision(); if (cfgAddEvtSel) { @@ -475,30 +866,32 @@ struct jEPFlowAnalysis { } } - float cent = coll.centFT0C(); + cent = coll.centFT0C(); - for (auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.eta()) > cfgTrackCuts.cfgEtaMax) + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.eta()) > cfgTrackCuts.cfgEtaMax) { continue; + } - auto* p = TDatabasePDG::Instance()->GetParticle(mcParticle.pdgCode()); - if (p) { - if (std::abs(p->Charge()) < 1e-1) { + auto p = pdg->GetParticle(mcParticle.pdgCode()); + if (p != nullptr) { + if (std::abs(p->Charge()) < minChg) { continue; } } - if (!mcParticle.isPhysicalPrimary()) + if (!mcParticle.isPhysicalPrimary()) { continue; + } epFlowHistograms.fill(HIST("MC/hPartGen"), cent, mcColl.posZ(), mcParticle.eta(), mcParticle.phi(), mcParticle.pt()); } } - PROCESS_SWITCH(jEPFlowAnalysis, processMCGen, "process for MC", false); + PROCESS_SWITCH(JEPFlowAnalysis, processMCGen, "process for MC", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx new file mode 100644 index 00000000000..e93b7ec3f7a --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonAnalysis.cxx @@ -0,0 +1,561 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonAnalysis.cxx +/// \brief D0 v02(pT) on the JCorran derived tables +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// v02(pT) = _c / (_c ). +/// X and Y are the corrected Q2 of two forward detectors (default FT0A x FT0C) +/// from the combined Qvectors table, with the same indexing as jEPDzeroFlowAnalysis. +/// Z_D(pT) = N_D(pT) / N_ch. N_D comes from CF2ProngTracks (D0 and D0bar). +/// N_ch comes from CFTracks. With JWeights (jflucWeightsLoader, hadron NUE only), +/// sum 1/epsilon_h into N_ch for hadrons and optionally for D0 denominators +/// (processReferenceCorrected / processD0HadronNch). Full D0 candidate efficiency +/// (J2ProngWeights, processD0Corrected) is not wired in the default train yet. +/// Both derived tables are indexed to CFCollisions, +/// so the original collision is matched through CFCollRefs (one row per kept +/// collision, written by filter-correlations with cfgTransientTables). +/// The D azimuth is not part of the Q-vector. +/// Do not divide by an extra resolution: it cancels in the ratio. +/// +/// Definition: arXiv:2506.18690, Appendix A. + +#include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/JCorran/DataModel/JCatalyst.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct JV02DmesonAnalysis { + enum Detector { + kFT0C = 0, + kFT0A, + kFT0M, + kFV0A, + kTPCpos, + kTPCneg, + kTPCall + }; + static constexpr int NDetectors = 7; + static constexpr int NCorrections = 4; + static constexpr int FullyCorrected = 3; + static constexpr int StepAccepted = 5; + static constexpr float InvalidQvector = 900.f; + + using MyCollisions = soa::Join; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable cfgCentEst{"cfgCentEst", "FT0C", "Centrality estimator: FT0C or FT0M"}; + Configurable cfgRefAName{"cfgRefAName", "FT0A", "Reference detector A: FT0A, FT0C, FT0M, FV0A, TPCpos, TPCneg, TPCall"}; + Configurable cfgRefBName{"cfgRefBName", "FT0C", "Reference detector B"}; + Configurable cfgQvecMode{"cfgQvecMode", 0, "Harmonic slot in qvector-table cfgnMods. 0 is v2 when cfgnMods starts at 2"}; + Configurable cfgNSubsample{"cfgNSubsample", 10, "Number of bootstrap subsamples"}; + Configurable cfgSeed{"cfgSeed", 1, "Seed for the subsample index"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "Maximum track occupancy"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "Minimum track occupancy"}; + Configurable cfgVtxZ{"cfgVtxZ", 10.f, "Maximum |z_vtx| (cm)"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum centrality"}; + Configurable cfgCentMax{"cfgCentMax", 80.f, "Maximum centrality"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "Maximum |eta| of CF tracks used for N_ch"}; + Configurable cfgPtMinCh{"cfgPtMinCh", 0.2f, "Minimum charged-track pT (GeV/c)"}; + Configurable cfgPtMaxCh{"cfgPtMaxCh", 3.f, "Maximum charged-track pT (GeV/c)"}; + Configurable cfgPtMinD{"cfgPtMinD", 1.f, "Minimum D0 pT (GeV/c)"}; + Configurable cfgPtMaxD{"cfgPtMaxD", 24.f, "Maximum D0 pT (GeV/c)"}; + Configurable cfgMinNch{"cfgMinNch", 10.f, "Minimum N_ch"}; + Configurable cfgMinSumAmpl{"cfgMinSumAmpl", 1e-4f, "Minimum Q-vector amplitude"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "Require sel8"}; + Configurable cfgCutNoSameBunchPileup{"cfgCutNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; + Configurable cfgCutGoodZvtxFT0vsPV{"cfgCutGoodZvtxFT0vsPV", true, "Require kIsGoodZvtxFT0vsPV"}; + Configurable cfgCutNoTimeFrameBorder{"cfgCutNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; + Configurable cfgCutNoITSROFrameBorder{"cfgCutNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; + Configurable cfgRejectDaughters{"cfgRejectDaughters", false, "Drop D0 daughter CF tracks from N_ch"}; + Configurable cfgWeightEffIsEfficiency{"cfgWeightEffIsEfficiency", true, "JWeights.weightEff is epsilon (multiply counts by 1/epsilon). Set false if the table already stores 1/epsilon."}; + + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {8, 0., 80.}, "Centrality axis"}; + ConfigurableAxis cfgAxisPtD{"cfgAxisPtD", {VARIABLE_WIDTH, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 8.f, 12.f, 16.f, 24.f}, "D0 pT axis"}; + ConfigurableAxis cfgAxisPtHad{"cfgAxisPtHad", {VARIABLE_WIDTH, 0.2f, 0.4f, 0.6f, 0.8f, 1.f, 1.2f, 1.5f, 2.f, 2.5f, 3.f}, "Charged-hadron pT axis"}; + ConfigurableAxis cfgAxisMass{"cfgAxisMass", {70, 1.70, 2.05}, "D0 invariant-mass axis"}; + + int mRefA = kFT0A; + int mRefB = kFT0C; + int mNSubsample = 1; + Preslice mPerCFTrack = aod::cftrack::cfCollisionId; + Preslice mPerCFCand = aod::cftrack::cfCollisionId; + const TAxis* mHadPtAxis = nullptr; + std::vector mHadCounts; + std::mt19937 mRng{1}; + std::uniform_int_distribution mSampleDist; + + template + using hasWeightEff = decltype(std::declval().weightEff()); + + template + float inverseEfficiencyWeight(const TTrack& track) const + { + if constexpr (std::experimental::is_detected::value) { + const float w = track.weightEff(); + if (w <= 0.f || !std::isfinite(w)) { + return 0.f; + } + return cfgWeightEffIsEfficiency.value ? 1.f / w : w; + } + return 1.f; + } + + int detectorId(const std::string& name) const + { + if (name == "FT0C") { + return kFT0C; + } + if (name == "FT0A") { + return kFT0A; + } + if (name == "FT0M") { + return kFT0M; + } + if (name == "FV0A") { + return kFV0A; + } + if (name == "TPCpos") { + return kTPCpos; + } + if (name == "TPCneg") { + return kTPCneg; + } + if (name == "TPCall") { + return kTPCall; + } + return -1; + } + + void init(InitContext const&) + { + mRefA = detectorId(cfgRefAName.value); + mRefB = detectorId(cfgRefBName.value); + if (mRefA < 0 || mRefB < 0 || mRefA == mRefB) { + LOGF(fatal, "cfgRefAName and cfgRefBName must be two different detectors"); + } + mNSubsample = cfgNSubsample > 0 ? cfgNSubsample.value : 1; + mRng.seed(static_cast(cfgSeed)); + mSampleDist = std::uniform_int_distribution(0, mNSubsample - 1); + + const AxisSpec subsampleAxis{mNSubsample, 0., static_cast(mNSubsample), "subsample"}; + const AxisSpec qaAxis{6, 0.5, 6.5, "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted"}; + + if (doprocessReference || doprocessReferenceCorrected) { + histos.add("hadron/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("hadron/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("hadron/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("hadron/Prof_Z", "Z = n(p_{T})/N_{ch};centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ", "XY Z;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile2D, {cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XY_sub", "Bulk #LTXY#GT per subsample;subsample;centrality (%);#LTXY#GT", {HistType::kTProfile2D, {subsampleAxis, cfgAxisCent}}); + histos.add("hadron/Prof_Z_sub", "Z per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + histos.add("hadron/Prof_XYZ_sub", "XYZ per subsample;subsample;centrality (%);p_{T} (GeV/c);#LTXYZ#GT", {HistType::kTProfile3D, {subsampleAxis, cfgAxisCent, cfgAxisPtHad}}); + mHadPtAxis = histos.get(HIST("hadron/Prof_Z"))->GetYaxis(); + mHadCounts.assign(mHadPtAxis->GetNbins(), 0.f); + } + if (doprocessReference && doprocessReferenceCorrected) { + LOGF(fatal, "Enable only one of processReference and processReferenceCorrected"); + } + if (doprocessD0 + doprocessD0HadronNch + doprocessD0Corrected > 1) { + LOGF(fatal, "Enable only one of processD0, processD0HadronNch, and processD0Corrected"); + } + if (doprocessD0 || doprocessD0HadronNch || doprocessD0Corrected) { + histos.add("D0/hEventQa", "1 seen; 2 sel8; 3 vertex; 4 quality; 5 centrality; 6 accepted", {HistType::kTH1F, {qaAxis}}); + histos.add("D0/hEvents", "Selected events;centrality (%);events", {HistType::kTH1F, {cfgAxisCent}}); + histos.add("D0/Prof_XY", "Bulk #LTXY#GT;centrality (%);#LTXY#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/Prof_Nch", "N_{ch};centrality (%);#LTN_{ch}#GT", {HistType::kTProfile, {cfgAxisCent}}); + histos.add("D0/hMassRaw", "Weight 1/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorr", "Weight XY/N_{ch};centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTH3F, {cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassRawSub", "Weight 1/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + histos.add("D0/hMassCorrSub", "Weight XY/N_{ch};subsample;centrality (%);p_{T} (GeV/c);m (GeV/c^{2})", {HistType::kTHnSparseF, {subsampleAxis, cfgAxisCent, cfgAxisPtD, cfgAxisMass}}, true); + } + } + + template + float centralityOf(const TCollision& collision) + { + if (cfgCentEst.value == "FT0M") { + return collision.centFT0M(); + } + return collision.centFT0C(); + } + + template + int passedEventStep(const TCollision& collision, float& centrality) + { + if (cfgRequireSel8 && !collision.sel8()) { + return 1; + } + if (std::abs(collision.posZ()) > cfgVtxZ) { + return 2; + } + const bool rejected = + (cfgCutNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) || + (cfgCutGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) || + (cfgCutNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) || + (cfgCutNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) || + collision.trackOccupancyInTimeRange() > cfgMaxOccupancy || + collision.trackOccupancyInTimeRange() < cfgMinOccupancy; + if (rejected) { + return 3; + } + centrality = centralityOf(collision); + if (centrality < cfgCentMin || centrality > cfgCentMax) { + return 4; + } + return 5; + } + + template + bool referenceProduct(const TCollision& collision, float& xy) + { + const auto& re = collision.qvecRe(); + const auto& im = collision.qvecIm(); + const auto& amp = collision.qvecAmp(); + const int idxA = cfgQvecMode * NDetectors * NCorrections + mRefA * NCorrections + FullyCorrected; + const int idxB = cfgQvecMode * NDetectors * NCorrections + mRefB * NCorrections + FullyCorrected; + const int ampA = cfgQvecMode * NDetectors + mRefA; + const int ampB = cfgQvecMode * NDetectors + mRefB; + if (idxA >= static_cast(re.size()) || idxB >= static_cast(im.size()) || ampA >= static_cast(amp.size()) || ampB >= static_cast(amp.size())) { + return false; + } + if (!(amp[ampA] > cfgMinSumAmpl) || !(amp[ampB] > cfgMinSumAmpl)) { + return false; + } + if (re[idxA] > InvalidQvector || re[idxB] > InvalidQvector) { + return false; + } + xy = re[idxA] * re[idxB] + im[idxA] * im[idxB]; + return std::isfinite(xy); + } + + template + float countCharged(const TTracks& tracks, const std::unordered_set& daughters, bool differential) + { + float nch = 0.f; + if (differential) { + std::fill(mHadCounts.begin(), mHadCounts.end(), 0.f); + } + for (const auto& track : tracks) { + const float pt = track.pt(); + if (track.sign() == 0 || pt < cfgPtMinCh || pt >= cfgPtMaxCh || std::abs(track.eta()) > cfgEtaMax) { + continue; + } + if (cfgRejectDaughters && daughters.contains(static_cast(track.globalIndex()))) { + continue; + } + float w = 1.f; + if constexpr (kApplyEff) { + w = inverseEfficiencyWeight(track); + if (w <= 0.f) { + continue; + } + } + nch += w; + if (differential && mHadPtAxis != nullptr) { + const int bin = mHadPtAxis->FindBin(pt); + if (bin >= 1 && bin <= mHadPtAxis->GetNbins()) { + mHadCounts[bin - 1] += w; + } + } + } + return nch; + } + + static bool isD0Decay(uint8_t decay) + { + return decay == aod::cf2prongtrack::D0ToPiK || decay == aod::cf2prongtrack::D0barToKPi || decay == aod::cf2prongtrack::D0barToKPiExclusive; + } + + template + static int cfCollisionIndex(const TRefs& refs) + { + if (refs.size() != 1) { + return -1; + } + return static_cast(refs.begin().globalIndex()); + } + + void processReference(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("hadron/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + const std::unordered_set noDaughters; + const float nch = countCharged(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("hadron/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + histos.fill(HIST("hadron/hEvents"), cent); + histos.fill(HIST("hadron/Prof_XY"), cent, xy); + histos.fill(HIST("hadron/Prof_Nch"), cent, nch); + histos.fill(HIST("hadron/Prof_XY_sub"), static_cast(sample), cent, xy); + const int nBins = mHadPtAxis->GetNbins(); + for (int ibin = 1; ibin <= nBins; ++ibin) { + const auto pt = static_cast(mHadPtAxis->GetBinCenter(ibin)); + const float z = mHadCounts[ibin - 1] / nch; + histos.fill(HIST("hadron/Prof_Z"), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ"), cent, pt, xy * z); + histos.fill(HIST("hadron/Prof_Z_sub"), static_cast(sample), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ_sub"), static_cast(sample), cent, pt, xy * z); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processReference, "Charged-hadron v02(pT) closure", true); + + void processReferenceCorrected(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("hadron/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + const std::unordered_set noDaughters; + const float nch = countCharged, true>(tracks.sliceBy(mPerCFTrack, cfId), noDaughters, true); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("hadron/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + histos.fill(HIST("hadron/hEvents"), cent); + histos.fill(HIST("hadron/Prof_XY"), cent, xy); + histos.fill(HIST("hadron/Prof_Nch"), cent, nch); + histos.fill(HIST("hadron/Prof_XY_sub"), static_cast(sample), cent, xy); + const int nBins = mHadPtAxis->GetNbins(); + for (int ibin = 1; ibin <= nBins; ++ibin) { + const auto pt = static_cast(mHadPtAxis->GetBinCenter(ibin)); + const float z = mHadCounts[ibin - 1] / nch; + histos.fill(HIST("hadron/Prof_Z"), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ"), cent, pt, xy * z); + histos.fill(HIST("hadron/Prof_Z_sub"), static_cast(sample), cent, pt, z); + histos.fill(HIST("hadron/Prof_XYZ_sub"), static_cast(sample), cent, pt, xy * z); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processReferenceCorrected, "Charged-hadron v02 with JWeights efficiency", false); + + void processD0(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, aod::CFTracks const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + const float wRaw = 1.f / nch; + const float wCorr = xy / nch; + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0, "D0 and D0bar v02(pT) from CF2ProngTracks", true); + + void processD0HadronNch(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, aod::CF2ProngTracks const& candidates, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged, true>(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + const float wRaw = 1.f / nch; + const float wCorr = xy / nch; + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0HadronNch, "D0 v02 with epsilon_h-weighted N_ch only (no D0 efficiency)", false); + + void processD0Corrected(MyCollisions::iterator const& collision, aod::BCs const&, aod::CFCollisions const&, aod::CFCollRefs const& refs, + soa::Join const& candidates, soa::Join const& tracks) + { + float cent = -1.f; + float xy = 0.f; + const int step = passedEventStep(collision, cent); + for (int qaBin = 1; qaBin <= step; ++qaBin) { + histos.fill(HIST("D0/hEventQa"), static_cast(qaBin)); + } + const int cfId = cfCollisionIndex(refs); + if (step < StepAccepted || cfId < 0 || !referenceProduct(collision, xy)) { + return; + } + auto eventCandidates = candidates.sliceBy(mPerCFCand, cfId); + auto eventTracks = tracks.sliceBy(mPerCFTrack, cfId); + + std::unordered_set daughters; + if (cfgRejectDaughters) { + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + daughters.insert(static_cast(cand.cfTrackProng0Id())); + daughters.insert(static_cast(cand.cfTrackProng1Id())); + } + } + const float nch = countCharged, true>(eventTracks, daughters, false); + if (nch < cfgMinNch) { + return; + } + histos.fill(HIST("D0/hEventQa"), 6.f); + const int sample = mSampleDist(mRng); + const auto sampleCoord = static_cast(sample); + histos.fill(HIST("D0/hEvents"), cent); + histos.fill(HIST("D0/Prof_XY"), cent, xy); + histos.fill(HIST("D0/Prof_Nch"), cent, nch); + + for (const auto& cand : eventCandidates) { + if (!isD0Decay(cand.decay())) { + continue; + } + const float pt = cand.pt(); + if (pt < cfgPtMinD || pt >= cfgPtMaxD) { + continue; + } + const float wInvD = inverseEfficiencyWeight(cand); + if (wInvD <= 0.f) { + continue; + } + const float wRaw = wInvD / nch; + const float wCorr = xy * wInvD / nch; + const float mass = cand.invMass(); + histos.fill(HIST("D0/hMassRaw"), cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorr"), cent, pt, mass, wCorr); + histos.fill(HIST("D0/hMassRawSub"), sampleCoord, cent, pt, mass, wRaw); + histos.fill(HIST("D0/hMassCorrSub"), sampleCoord, cent, pt, mass, wCorr); + } + } + PROCESS_SWITCH(JV02DmesonAnalysis, processD0Corrected, "D0 v02 with JWeights and J2ProngWeights efficiency", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx new file mode 100644 index 00000000000..e4d3f614849 --- /dev/null +++ b/PWGCF/JCorran/Tasks/jV02DmesonToy.cxx @@ -0,0 +1,160 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jV02DmesonToy.cxx +/// \brief One timeframe of synthetic collisions for JV02DmesonAnalysis. +/// \author Dong Jo Kim (djkim@jyu.fi) +/// +/// 200 events at centrality 25. N_ch = 20. +/// Even events: XY = 0.08, 6 tracks in pT [1.0, 1.2), 4 D0 at mass 1.864 and pT 3.5. +/// Odd events: XY = 0.02, 2 tracks in pT [1.0, 1.2), 1 D0 at the same mass and pT. +/// The remaining charged tracks sit at pT 0.3. +/// Expected charged-hadron v02 in the 1.1 GeV/c bin is 0.3. +/// Expected D0 v02, with an empty sideband, is 0.36. + +#include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/JCorran/DataModel/JCatalyst.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct JV02DmesonToy { + Produces bcs; + Produces collisions; + Produces evsels; + Produces centsC; + Produces centsM; + Produces qvectors; + Produces cfCollisions; + Produces cfRefs; + Produces cfTracks; + Produces cfCandidates; + Produces jWeights; + + void fill() + { + constexpr int NEvents = 200; + constexpr int NDetectors = 7; + constexpr int NCorrections = 4; + constexpr int FullyCorrected = 3; + constexpr int Ft0C = 0; + constexpr int Ft0A = 1; + constexpr int EventPair = 2; + constexpr float Centrality = 25.f; + constexpr float Nch = 20.f; + const uint64_t selection = + (uint64_t{1} << aod::evsel::kNoSameBunchPileup) | + (uint64_t{1} << aod::evsel::kIsGoodZvtxFT0vsPV) | + (uint64_t{1} << aod::evsel::kNoTimeFrameBorder) | + (uint64_t{1} << aod::evsel::kNoITSROFrameBorder); + + std::vector re(NDetectors * NCorrections, 0.f); + std::vector im(NDetectors * NCorrections, 0.f); + std::vector amp(NDetectors, 0.f); + amp[Ft0A] = 1.f; + amp[Ft0C] = 1.f; + + for (int ievt = 0; ievt < NEvents; ++ievt) { + const bool high = ievt % EventPair == 0; + const float xy = high ? 0.08f : 0.02f; + const float q = std::sqrt(xy); + re[Ft0C * NCorrections + FullyCorrected] = q; + re[Ft0A * NCorrections + FullyCorrected] = q; + + bcs(544116, static_cast(ievt), uint64_t{0}, uint64_t{0}); + collisions(ievt, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, uint16_t{0}, 1.f, uint16_t{20}, 0.f, 0.f); + evsels(uint32_t{0}, selection, uint32_t{0}, false, true, ievt, -1, -1, -1, -1, 100, 0.f, 0.f); + centsC(Centrality); + centsM(Centrality); + qvectors(Centrality, true, re, im, amp); + cfCollisions(544116, 0.f, Nch, uint64_t{0}); + cfRefs(ievt); + + const int nSignalTracks = high ? 6 : 2; + for (int itrk = 0; itrk < static_cast(Nch); ++itrk) { + const float pt = itrk < nSignalTracks ? 1.1f : 0.3f; + const int8_t sign = itrk % EventPair == 0 ? int8_t{1} : int8_t{-1}; + cfTracks(ievt, pt, 0.f, 0.f, sign, uint8_t{1}); + // Closure for hadron efficiency: signal pT bin eps=0.5, rest eps=1 (v02 unchanged if all eps equal). + const float eff = pt > 1.f ? 0.5f : 1.f; + jWeights(1.f, eff); + } + const int nD0 = high ? 4 : 1; + for (int icand = 0; icand < nD0; ++icand) { + cfCandidates(ievt, 0, 1, 3.5f, 0.f, 0.f, static_cast(o2::constants::physics::MassD0), static_cast(aod::cf2prongtrack::D0ToPiK)); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const&) +{ + return WorkflowSpec{ + DataProcessorSpec{ + .name = "jv02-dmeson-toy", + .inputs = {}, + .outputs = { + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputForTable::spec(), + OutputSpec{"TFN", "TFNumber"}, + OutputSpec{"TFF", "TFFilename"}, + }, + .algorithm = AlgorithmSpec{adaptStateless([](ProcessingContext& pc) { + JV02DmesonToy toy; + toy.bcs.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.collisions.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.evsels.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.centsC.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.centsM.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.qvectors.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfCollisions.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfRefs.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfTracks.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.cfCandidates.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.jWeights.resetCursor(pc.outputs().make(OutputForTable::ref())); + toy.fill(); + pc.outputs().make(Output{"TFN", "TFNumber"}) = 1; + pc.outputs().make(Output{"TFF", "TFFilename"}) = std::string{"toy"}; + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + })}, + }, + }; +} diff --git a/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx b/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx index 02088eab148..bbc5d528faf 100644 --- a/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx +++ b/PWGCF/JCorran/Tasks/jflucWeightsLoader.cxx @@ -16,6 +16,7 @@ /// The weights are loaded from the local files or CCDB and stored in the JWeights table. #include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/GenericFramework/DataModel/GfwDerived.h" #include "PWGCF/JCorran/DataModel/JCatalyst.h" #include @@ -116,14 +117,16 @@ struct JflucWeightsLoader { void init(InitContext const&) { - if (!doprocessLoadWeights && !doprocessLoadWeightsCF && !doprocessJNUACreatorCFDerived) { + if (!doprocessLoadWeights && !doprocessLoadWeightsCF && !doprocessLoadWeightsGFW && !doprocessJNUACreatorCFDerived) { return; } - if (doprocessLoadWeights && doprocessLoadWeightsCF) - LOGF(fatal, "Only one of JTracks or CFTracks processing can be enabled at a time."); + const int nWeightProcesses = static_cast(doprocessLoadWeights) + static_cast(doprocessLoadWeightsCF) + static_cast(doprocessLoadWeightsGFW); + if (nWeightProcesses > 1) { + LOGF(fatal, "Only one of JTracks, CFTracks, or GFWTracks weight loading can be enabled at a time."); + } - if (doprocessLoadWeights || doprocessLoadWeightsCF) { + if (doprocessLoadWeights || doprocessLoadWeightsCF || doprocessLoadWeightsGFW) { // NUA corrections from local file or CCDB if (cfgPathPhiWeights.value.substr(0, 8) == "local://") { LOGF(info, "Using NUA corrections locally from: %s", cfgPathPhiWeights.value.substr(8).c_str()); @@ -172,7 +175,7 @@ struct JflucWeightsLoader { ccdb->setFatalWhenNull(false); } } - } // doprocessLoadWeights || doprocessLoadWeightsCF + } // doprocessLoadWeights || doprocessLoadWeightsCF || doprocessLoadWeightsGFW if (doprocessJNUACreatorCFDerived) { qaHistRegistry.add("trackType", "trackType;trackType;counts", HistType::kTH1F, {{65, -0.5, 64.5}}); @@ -276,6 +279,12 @@ struct JflucWeightsLoader { } PROCESS_SWITCH(JflucWeightsLoader, processLoadWeightsCF, "Load weights histograms for CF derived data table", true); + void processLoadWeightsGFW(aod::GfwCollision const& collision, aod::GfwTracks const& tracks) + { + loadWeights(output, collision, tracks); + } + PROCESS_SWITCH(JflucWeightsLoader, processLoadWeightsGFW, "Load weights histograms for GFW derived data table", false); + Produces output2p; void processLoadWeightsCF2Prong(aod::CFCollision const& collision, aod::CF2ProngTracks const& tracks2p) { diff --git a/PWGCF/MultiparticleCorrelations/Core/MuPa-Configurables.h b/PWGCF/MultiparticleCorrelations/Core/MuPa-Configurables.h index 9f9304d4d1e..aaf6d543fc7 100644 --- a/PWGCF/MultiparticleCorrelations/Core/MuPa-Configurables.h +++ b/PWGCF/MultiparticleCorrelations/Core/MuPa-Configurables.h @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file MuPa-Configurables.h -/// \brief ... TBI 20250425 +/// \brief Task to calculate multiparticle correlations and related observables /// \author Ante.Bilandzic@cern.ch #ifndef PWGCF_MULTIPARTICLECORRELATIONS_CORE_MUPA_CONFIGURABLES_H_ @@ -231,9 +231,9 @@ struct : ConfigurableGroup { Configurable cfUseDiffPhiPtWeights{"cfUseDiffPhiPtWeights", false, "use or not differential phi(pt) weights"}; Configurable cfUseDiffPhiEtaWeights{"cfUseDiffPhiEtaWeights", false, "use or not differential phi(eta) weights"}; Configurable> cfWhichDiffPhiWeights{"cfWhichDiffPhiWeights", {"1-wPhi", "1-wPt", "1-wEta", "1-wCharge", "1-wCentrality", "1-wVertexZ"}, "use (1) or do not use (0) differential phi weight for particular dimension. If only phi is set to 1, integrated phi weights are used. If phi is set to 0, ALL dimensions are switched off (yes!)"}; - Configurable> cfWhichDiffPtWeights{"cfWhichDiffPtWeights", {"0-wPt", "0-wEta", "0-wCharge", "0-wCentrality"}, "use (1) or do not use (0) differential pt weight for particular dimension. If only pt is set to 1, integrated pt weights are used. If pt is set to 0, ALL dimensions are switched off (yes!)"}; - Configurable> cfWhichDiffEtaWeights{"cfWhichDiffEtaWeights", {"0-wEta", "0-wPt", "0-wCharge", "0-wCentrality"}, "use (1) or do not use (0) differential eta weight for particular dimension. If only eta is set to 1, integrated eta weights are used. If eta is set to 0, ALL dimensions are switched off (yes!)"}; - Configurable> cfWhichDiffChargeWeights{"cfWhichDiffChargeWeights", {"0-wCharge", "0-wPt", "0-wEta", "0-wCentrality"}, "use (1) or do not use (0) differential charge weight for particular dimension. If only charge is set to 1, integrated charge weights are used. If charge is set to 0, ALL dimensions are switched off (yes!)"}; + Configurable> cfWhichDiffPtWeights{"cfWhichDiffPtWeights", {"0-wPt", "0-wEta", "0-wCharge", "0-wCentrality", "0-wVertexZ"}, "use (1) or do not use (0) differential pt weight for particular dimension. If only pt is set to 1, integrated pt weights are used. If pt is set to 0, ALL dimensions are switched off (yes!)"}; + Configurable> cfWhichDiffEtaWeights{"cfWhichDiffEtaWeights", {"0-wEta", "0-wPt", "0-wCharge", "0-wCentrality", "0-wVertexZ"}, "use (1) or do not use (0) differential eta weight for particular dimension. If only eta is set to 1, integrated eta weights are used. If eta is set to 0, ALL dimensions are switched off (yes!)"}; + Configurable> cfWhichDiffChargeWeights{"cfWhichDiffChargeWeights", {"0-wCharge", "0-wPt", "0-wEta", "0-wCentrality", "0-wVertexZ"}, "use (1) or do not use (0) differential charge weight for particular dimension. If only charge is set to 1, integrated charge weights are used. If charge is set to 0, ALL dimensions are switched off (yes!)"}; Configurable cfFileWithWeights{"cfFileWithWeights", "/home/abilandz/DatasetsO2/weights.root", "path to external ROOT file which holds all particle weights in O2 format"}; // for AliEn file prepend "/alice/cern.ch/", for CCDB prepend "/alice-ccdb.cern.ch" } cf_pw; diff --git a/PWGCF/MultiparticleCorrelations/Core/MuPa-DataMembers.h b/PWGCF/MultiparticleCorrelations/Core/MuPa-DataMembers.h index 32df4c726ee..cf999f7f8c6 100644 --- a/PWGCF/MultiparticleCorrelations/Core/MuPa-DataMembers.h +++ b/PWGCF/MultiparticleCorrelations/Core/MuPa-DataMembers.h @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file MuPa-DataMembers.h -/// \brief ... TBI 20250425 +/// \brief Task to calculate multiparticle correlations and related observables /// \author Ante.Bilandzic@cern.ch #ifndef PWGCF_MULTIPARTICLECORRELATIONS_CORE_MUPA_DATAMEMBERS_H_ @@ -86,7 +86,8 @@ struct TaskConfiguration { // || es.fCalculateEtaSeparationsAsFunctionOf[AFO_PT] bool fCalculate2DAsFunctionOf[eAsFunctionOf2D_N] = {false}; //! See example above for 1D case + enum for 2D details bool fCalculate3DAsFunctionOf[eAsFunctionOf3D_N] = {false}; //! See example above for 1D case + enum for 3D details - TDatabasePDG* fDatabasePDG = NULL; // booked only when MC info is available. There is a standard memory blow-up when booked, therefore I need to request also fUseDatabasePDG = true + TDatabasePDG* fDatabasePDG = NULL; // o2-linter: disable=pdg/database (using until o2::framework::O2DatabasePDG lazy initialization is provided) + // booked only when MC info is available. There is a standard memory blow-up when booked, therefore I need to request also fUseDatabasePDG = true // TBI 20250625 replace eventually with the service O2DatabasePDG, when memory consumption problem is resolved bool fUseSetBinLabel = false; // until SetBinLabel(...) large memory consumption is resolved, do not use hist->SetBinLabel(...), see ROOT Forum // See also local executable PostprocessLabels.C @@ -165,7 +166,7 @@ struct QualityAssurance { bool fBookQACorrelationsVsHistograms2D[eQACorrelationsVsHistograms2D_N] = {true}; // book or not this 2D histogram, see configurable cfBookQACorrelationsVsHistograms2D float fQACorrelationsVsHistogramsBins2D[eQACorrelationsVsHistograms2D_N][2][3] = {{{0.}}}; // [type - see enum][x,y][nBins,min,max] TString fQACorrelationsVsHistogramsName2D[eQACorrelationsVsHistograms2D_N] = {""}; // name of fQACorrelationsVsHistograms2D, determined programatically from other 1D names, to ease bookkeeping - int fQACorrelationsVsHistogramsMinMaxHarmonic[2]; // book only for MinMaxHarmonic[0] <= harmonics < MinMaxHarmonic[1] + int fQACorrelationsVsHistogramsMinMaxHarmonic[2] = {0}; // book only for MinMaxHarmonic[0] <= harmonics < MinMaxHarmonic[1] TList* fQACorrelationsVsInteractionRateVsList = NULL; //!GetBinContent(x,y); histSwapped->Fill(y,x); eMultiplicity_vs_NContributors, @@ -461,12 +466,12 @@ enum eQAEventHistograms2D { eQAEventHistograms2D_N }; -enum eQAParticleHistograms2D { +enum EnQAParticleHistograms2D { ePt_vs_dcaXY, eQAParticleHistograms2D_N }; -enum eQAParticleEventHistograms2D { +enum EnQAParticleEventHistograms2D { // In this category I do correlation vs. some-event-property. // The < ... > goes over all particles in that event. // All < ... > over particles are calculated with helper TProfile fQAParticleEventProEbyE @@ -484,7 +489,7 @@ enum eQAParticleEventHistograms2D { eQAParticleEventHistograms2D_N }; -enum eQAParticleEventProEbyE { +enum EnQAParticleEventProEbyE { eitsNClsEbyE = 1, // Labels average in a given event (therefore "EbyE" is appended). Yes, from one, because it runs over bin content and entries in TProfile for most of the time. eitsNClsNegEtaEbyE, // in a given event for eta < 0 eitsNClsPosEtaEbyE, // in a given event for eta > 0 @@ -515,7 +520,7 @@ enum eQAParticleEventProEbyE { eQAParticleEventProEbyE_N }; -enum eQACorrelationsVsHistograms2D { +enum EnQACorrelationsVsHistograms2D { // In this category I correlate <2> vs. some-event-property. // For instance: <2> vs. ref. mult // <2> vs. , where is calculated from all particles in that event (so in this sense, it's an event property as well) @@ -546,7 +551,7 @@ enum eQACorrelationsVsHistograms2D { eQACorrelationsVsHistograms2D_N }; -enum eQACorrelationsVsInteractionRateVsProfiles2D_N { +enum EnQACorrelationsVsInteractionRateVsProfiles2D { // In this category I fill <2> in 2D profile spanned by IR vs. some-other-observable (IR is always x axis) // For instance: <2> is filled in TProfile2D spanned by IR vs. CurrentRunDuration (crd) // <2> is filled in TProfile2D spanned by IR vs. , where is calculated from all particles in that event @@ -567,7 +572,7 @@ enum eQACorrelationsVsInteractionRateVsProfiles2D_N { eQACorrelationsVsInteractionRateVsProfiles2D_N }; -enum eReferenceMultiplicityEstimators { +enum EnReferenceMultiplicityEstimators { // Run 3: eMultTPC = 0, eMultFV0M, // ref. mult from helper task o2-analysis-multiplicity-table @@ -580,7 +585,7 @@ enum eReferenceMultiplicityEstimators { eReferenceMultiplicityEstimators_N }; -enum eCentralityEstimators { +enum EnCentralityEstimators { // Run 3: eCentFT0C = 0, eCentFT0CVariant1, @@ -594,19 +599,19 @@ enum eCentralityEstimators { eCentralityEstimators_N }; -enum eOccupancyEstimators { +enum EnOccupancyEstimators { eTrackOccupancyInTimeRange, // from helper task o2-analysis-event-selection, see also IA's presentation in https://indico.cern.ch/event/1464946, slide 38 eFT0COccupancyInTimeRange, // from helper task o2-analysis-event-selection eOccupancyEstimators_N }; -enum eEventCounter { +enum EnEventCounter { eTotal, // total number of events, before any cuts are applied eProcessed, // number of processed events, i.e. number of events which survived cuts and on which analysis have been performed eEventCounter_N }; -enum eSpecificCuts { +enum EnSpecificCuts { // Run 3: eLHC23zzh, eLHC24ar, @@ -620,11 +625,28 @@ enum eSpecificCuts { eSpecificCuts_N }; -enum eRunTime { +enum EnRunTime { eStartOfRun = 0, // in abs. seconds since Unix epoch eEndOfRun, // in abs. seconds since Unix epoch eDurationInSec, // in seconds eRunTime_N }; +enum EnOrder { // TBI 20260602 : I need this enum somewhat artificially to silent o2_linter : Avoid magic numbers in expressions + eOrder_1 = 1, + eOrder_2, + eOrder_3, + eOrder_4, + eOrder_5, + eOrder_6, + eOrder_7, + eOrder_8, + eOrder_9, + eOrder_10, + eOrder_11, + eOrder_12, + // ... + eOrder_N +}; + #endif // PWGCF_MULTIPARTICLECORRELATIONS_CORE_MUPA_ENUMS_H_ diff --git a/PWGCF/MultiparticleCorrelations/Core/MuPa-GlobalConstants.h b/PWGCF/MultiparticleCorrelations/Core/MuPa-GlobalConstants.h index adacaf76282..3574658fbd2 100644 --- a/PWGCF/MultiparticleCorrelations/Core/MuPa-GlobalConstants.h +++ b/PWGCF/MultiparticleCorrelations/Core/MuPa-GlobalConstants.h @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file MuPa-GlobalConstants.h -/// \brief ... TBI 20250425 +/// \brief Task to calculate multiparticle correlations and related observables /// \author Ante.Bilandzic@cern.ch #ifndef PWGCF_MULTIPARTICLECORRELATIONS_CORE_MUPA_GLOBALCONSTANTS_H_ @@ -23,5 +23,9 @@ const int gMaxNoBinsKine = 1000; // max number of bins for differentia const int gMaxBinsDiffWeights = 100; // max number of bins for differential weights, see MakeWeights.C const int gMaxNumberEtaSeparations = 9; // max number of different eta separations used to calculated 2p corr. with eta separations const int gMaxNumberSparseDimensions = 10; // max number of dimensions in sparse histograms +const int gOrderIsotropic = 4; // if set to 4, i consider 2p, 4p, 6p, and 8p isotropic correlators +const int grsN = 2; // rec or sim + silencing o2_linter : Avoid magic numbers in expressions +const int gbaN = 2; // before or after + silencing o2_linter : Avoid magic numbers in expressions +const int gewN = 2; // number of eta windows + silencing o2_linter : Avoid magic numbers in expressions #endif // PWGCF_MULTIPARTICLECORRELATIONS_CORE_MUPA_GLOBALCONSTANTS_H_ diff --git a/PWGCF/MultiparticleCorrelations/Core/MuPa-MemberFunctions.h b/PWGCF/MultiparticleCorrelations/Core/MuPa-MemberFunctions.h index 09b8968976a..3a3a34fccb9 100644 --- a/PWGCF/MultiparticleCorrelations/Core/MuPa-MemberFunctions.h +++ b/PWGCF/MultiparticleCorrelations/Core/MuPa-MemberFunctions.h @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file MuPa-MemberFunctions.h -/// \brief ... TBI 20250425 +/// \brief Task to calculate multiparticle correlations and related observables /// \author Ante.Bilandzic@cern.ch #ifndef PWGCF_MULTIPARTICLECORRELATIONS_CORE_MUPA_MEMBERFUNCTIONS_H_ @@ -36,7 +36,7 @@ void bookBaseList() // e) Add configured base TProfile to the list. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book base TList: @@ -63,7 +63,7 @@ void bookBaseList() fBasePro->GetXaxis()->SetBinLabel(eRunNumber, Form("fRunNumber = %s", "__RUN_NUMBER__")); // I have to do it this way via placeholder, because run number is available only when i start to process data. - // Then, I replace placeholder with run number in PropagateRunNumber(...) + // Then, I replace placeholder with run number in propagateRunNumber(...) fBasePro->GetXaxis()->SetBinLabel(eDryRun, "fDryRun"); fBasePro->Fill(eDryRun, static_cast(tc.fDryRun)); @@ -140,7 +140,7 @@ void bookBaseList() yAxisTitle += TString::Format("%d:fRunNumber = %s; ", static_cast(eRunNumber), "__RUN_NUMBER__"); // I have to do it this way via placeholder, because run number is available only when i start to process data. - // Then, I replace placeholder with run number in PropagateRunNumber(...) + // Then, I replace placeholder with run number in propagateRunNumber(...) yAxisTitle += TString::Format("%d:fDryRun; ", static_cast(eDryRun)); fBasePro->Fill(eDryRun, static_cast(tc.fDryRun)); @@ -226,7 +226,7 @@ void bookBaseList() fBaseList->Add(fBasePro); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookBaseList() @@ -269,7 +269,7 @@ void defaultConfiguration() tc.fVerbose = cf_tc.cfVerbose; if (tc.fVerbose) { - StartFunction(__FUNCTION__); // yes, here + startFunction(__FUNCTION__); // yes, here } tc.fVerboseUtility = cf_tc.cfVerboseUtility; tc.fVerboseForEachParticle = cf_tc.cfVerboseForEachParticle; @@ -281,12 +281,12 @@ void defaultConfiguration() tc.fProcess[eProcessRec] = doprocessRec; tc.fProcess[eProcessRecSim] = doprocessRecSim; tc.fProcess[eProcessSim] = doprocessSim; - tc.fProcess[eProcessRec_Run2] = doprocessRec_Run2; - tc.fProcess[eProcessRecSim_Run2] = doprocessRecSim_Run2; - tc.fProcess[eProcessSim_Run2] = doprocessSim_Run2; - tc.fProcess[eProcessRec_Run1] = doprocessRec_Run1; - tc.fProcess[eProcessRecSim_Run1] = doprocessRecSim_Run1; - tc.fProcess[eProcessSim_Run1] = doprocessSim_Run1; + tc.fProcess[eProcessRecRun2] = doprocessRecRun2; + tc.fProcess[eProcessRecSimRun2] = doprocessRecSimRun2; + tc.fProcess[eProcessSimRun2] = doprocessSimRun2; + tc.fProcess[eProcessRecRun1] = doprocessRecRun1; + tc.fProcess[eProcessRecSimRun1] = doprocessRecSimRun1; + tc.fProcess[eProcessSimRun1] = doprocessSimRun1; tc.fProcess[eProcessTest] = doprocessTest; tc.fProcess[eProcessQA] = doprocessQA; tc.fProcess[eProcessHepMChi] = doprocessHepMChi; @@ -299,23 +299,23 @@ void defaultConfiguration() // TBI 20240512 See also if I need to extand subscription, both in the definition of CollisionSim and TrackSim } - if (tc.fProcess[eProcessSim_Run2]) { - LOGF(fatal, "\033[1;31m%s at line %d - processSim_Run2(...) is not implemented/validated yet \033[0m", __FUNCTION__, __LINE__); + if (tc.fProcess[eProcessSimRun2]) { + LOGF(fatal, "\033[1;31m%s at line %d - processSimRun2(...) is not implemented/validated yet \033[0m", __FUNCTION__, __LINE__); // TBI 20240517 see above comments for eProcessSim , most likely they also apply for this case } - if (tc.fProcess[eProcessRecSim_Run1]) { - LOGF(fatal, "\033[1;31m%s at line %d - processRecSim_Run1(...) is not implemented/validated yet \033[0m", __FUNCTION__, __LINE__); + if (tc.fProcess[eProcessRecSimRun1]) { + LOGF(fatal, "\033[1;31m%s at line %d - processRecSimRun1(...) is not implemented/validated yet \033[0m", __FUNCTION__, __LINE__); } - if (tc.fProcess[eProcessSim_Run1]) { - LOGF(fatal, "\033[1;31m%s at line %d - processSim_Run1(...) is not implemented/validated yet \033[0m", __FUNCTION__, __LINE__); + if (tc.fProcess[eProcessSimRun1]) { + LOGF(fatal, "\033[1;31m%s at line %d - processSimRun1(...) is not implemented/validated yet \033[0m", __FUNCTION__, __LINE__); } // Set automatically generic flags, from above individual flags: - tc.fProcess[eGenericRec] = tc.fProcess[eProcessRec] || tc.fProcess[eProcessRec_Run2] || tc.fProcess[eProcessRec_Run1] || tc.fProcess[eProcessTest] || tc.fProcess[eProcessQA]; - tc.fProcess[eGenericRecSim] = tc.fProcess[eProcessRecSim] || tc.fProcess[eProcessRecSim_Run2] || tc.fProcess[eProcessRecSim_Run1]; - tc.fProcess[eGenericSim] = tc.fProcess[eProcessSim] || tc.fProcess[eProcessSim_Run2] || tc.fProcess[eProcessSim_Run1]; + tc.fProcess[eGenericRec] = tc.fProcess[eProcessRec] || tc.fProcess[eProcessRecRun2] || tc.fProcess[eProcessRecRun1] || tc.fProcess[eProcessTest] || tc.fProcess[eProcessQA]; + tc.fProcess[eGenericRecSim] = tc.fProcess[eProcessRecSim] || tc.fProcess[eProcessRecSimRun2] || tc.fProcess[eProcessRecSimRun1]; + tc.fProcess[eGenericSim] = tc.fProcess[eProcessSim] || tc.fProcess[eProcessSimRun2] || tc.fProcess[eProcessSimRun1]; // Set automatically tc.fWhichProcess from above individual flags: if (tc.fProcess[eProcessRec]) { @@ -324,17 +324,17 @@ void defaultConfiguration() tc.fWhichProcess = "ProcessRecSim"; } else if (tc.fProcess[eProcessSim]) { tc.fWhichProcess = "ProcessSim"; - } else if (tc.fProcess[eProcessRec_Run2]) { + } else if (tc.fProcess[eProcessRecRun2]) { tc.fWhichProcess = "ProcessRec_Run2"; - } else if (tc.fProcess[eProcessRecSim_Run2]) { + } else if (tc.fProcess[eProcessRecSimRun2]) { tc.fWhichProcess = "ProcessRecSim_Run2"; - } else if (tc.fProcess[eProcessSim_Run2]) { + } else if (tc.fProcess[eProcessSimRun2]) { tc.fWhichProcess = "ProcessSim_Run2"; - } else if (tc.fProcess[eProcessRec_Run1]) { + } else if (tc.fProcess[eProcessRecRun1]) { tc.fWhichProcess = "ProcessRec_Run1"; - } else if (tc.fProcess[eProcessRecSim_Run1]) { + } else if (tc.fProcess[eProcessRecSimRun1]) { tc.fWhichProcess = "ProcessRecSim_Run1"; - } else if (tc.fProcess[eProcessSim_Run1]) { + } else if (tc.fProcess[eProcessSimRun1]) { tc.fWhichProcess = "ProcessSim_Run1"; } else if (tc.fProcess[eProcessTest]) { tc.fWhichProcess = "ProcessTest"; @@ -523,7 +523,7 @@ void defaultConfiguration() // *) Use configurable array cfCalculateCorrelationsAsFunctionOf, to specify vs which observable correlations will be calculated (flags 1 or 0). // Supported format: "0-someName" and "1-someName", where "-" is a field separator. - // Ordering of the flags in that array is interpreted through ordering of enums in enum eAsFunctionOf. + // Ordering of the flags in that array is interpreted through ordering of enums in enum EnAsFunctionOf. auto lCalculateCorrelationsAsFunctionOf = cf_mupa.cfCalculateCorrelationsAsFunctionOf.value; // this is now the local version of that string array from configurable. if (lCalculateCorrelationsAsFunctionOf.size() != eAsFunctionOf_N) { LOGF(info, "\033[1;31m lCalculateCorrelationsAsFunctionOf.size() = %d\033[0m", lCalculateCorrelationsAsFunctionOf.size()); @@ -532,16 +532,16 @@ void defaultConfiguration() } // I append "&& mupa.fCalculateCorrelations" below, to switch off calculation of all correlations with one common flag: - mupa.fCalculateCorrelationsAsFunctionOf[AFO_INTEGRATED] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_INTEGRATED]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_MULTIPLICITY] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_MULTIPLICITY]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_CENTRALITY] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_CENTRALITY]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_PT] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_PT]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_ETA] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_ETA]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_OCCUPANCY] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_OCCUPANCY]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_INTERACTIONRATE] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_INTERACTIONRATE]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_CURRENTRUNDURATION] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_CURRENTRUNDURATION]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_VZ] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_VZ]) && mupa.fCalculateCorrelations; - mupa.fCalculateCorrelationsAsFunctionOf[AFO_CHARGE] = Alright(lCalculateCorrelationsAsFunctionOf[AFO_CHARGE]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_INTEGRATED] = alright(lCalculateCorrelationsAsFunctionOf[AFO_INTEGRATED]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_MULTIPLICITY] = alright(lCalculateCorrelationsAsFunctionOf[AFO_MULTIPLICITY]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_CENTRALITY] = alright(lCalculateCorrelationsAsFunctionOf[AFO_CENTRALITY]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_PT] = alright(lCalculateCorrelationsAsFunctionOf[AFO_PT]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_ETA] = alright(lCalculateCorrelationsAsFunctionOf[AFO_ETA]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_OCCUPANCY] = alright(lCalculateCorrelationsAsFunctionOf[AFO_OCCUPANCY]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_INTERACTIONRATE] = alright(lCalculateCorrelationsAsFunctionOf[AFO_INTERACTIONRATE]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_CURRENTRUNDURATION] = alright(lCalculateCorrelationsAsFunctionOf[AFO_CURRENTRUNDURATION]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_VZ] = alright(lCalculateCorrelationsAsFunctionOf[AFO_VZ]) && mupa.fCalculateCorrelations; + mupa.fCalculateCorrelationsAsFunctionOf[AFO_CHARGE] = alright(lCalculateCorrelationsAsFunctionOf[AFO_CHARGE]) && mupa.fCalculateCorrelations; // ... // *) Test0: @@ -550,7 +550,7 @@ void defaultConfiguration() // *) Use configurable array cfCalculateTest0AsFunctionOf, to specify vs which observable Test0 will be calculated (flags 1 or 0). // Supported format: "0-someName" and "1-someName", where "-" is a field separator. - // Ordering of the flags in that array is interpreted through ordering of enums in enum eAsFunctionOf. + // Ordering of the flags in that array is interpreted through ordering of enums in enum EnAsFunctionOf. auto lCalculateTest0AsFunctionOf = cf_t0.cfCalculateTest0AsFunctionOf.value; // this is now the local version of that string array from configurable. if (lCalculateTest0AsFunctionOf.size() != eAsFunctionOf_N) { LOGF(info, "\033[1;31m lCalculateTest0AsFunctionOf.size() = %d\033[0m", lCalculateTest0AsFunctionOf.size()); @@ -559,16 +559,16 @@ void defaultConfiguration() } // I append "&& t0.fCalculateTest0" below, to switch off calculation of all Test0 with one common flag: - t0.fCalculateTest0AsFunctionOf[AFO_INTEGRATED] = Alright(lCalculateTest0AsFunctionOf[AFO_INTEGRATED]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_MULTIPLICITY] = Alright(lCalculateTest0AsFunctionOf[AFO_MULTIPLICITY]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_CENTRALITY] = Alright(lCalculateTest0AsFunctionOf[AFO_CENTRALITY]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_PT] = Alright(lCalculateTest0AsFunctionOf[AFO_PT]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_ETA] = Alright(lCalculateTest0AsFunctionOf[AFO_ETA]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_OCCUPANCY] = Alright(lCalculateTest0AsFunctionOf[AFO_OCCUPANCY]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_INTERACTIONRATE] = Alright(lCalculateTest0AsFunctionOf[AFO_INTERACTIONRATE]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_CURRENTRUNDURATION] = Alright(lCalculateTest0AsFunctionOf[AFO_CURRENTRUNDURATION]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_VZ] = Alright(lCalculateTest0AsFunctionOf[AFO_VZ]) && t0.fCalculateTest0; - t0.fCalculateTest0AsFunctionOf[AFO_CHARGE] = Alright(lCalculateTest0AsFunctionOf[AFO_CHARGE]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_INTEGRATED] = alright(lCalculateTest0AsFunctionOf[AFO_INTEGRATED]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_MULTIPLICITY] = alright(lCalculateTest0AsFunctionOf[AFO_MULTIPLICITY]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_CENTRALITY] = alright(lCalculateTest0AsFunctionOf[AFO_CENTRALITY]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_PT] = alright(lCalculateTest0AsFunctionOf[AFO_PT]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_ETA] = alright(lCalculateTest0AsFunctionOf[AFO_ETA]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_OCCUPANCY] = alright(lCalculateTest0AsFunctionOf[AFO_OCCUPANCY]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_INTERACTIONRATE] = alright(lCalculateTest0AsFunctionOf[AFO_INTERACTIONRATE]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_CURRENTRUNDURATION] = alright(lCalculateTest0AsFunctionOf[AFO_CURRENTRUNDURATION]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_VZ] = alright(lCalculateTest0AsFunctionOf[AFO_VZ]) && t0.fCalculateTest0; + t0.fCalculateTest0AsFunctionOf[AFO_CHARGE] = alright(lCalculateTest0AsFunctionOf[AFO_CHARGE]) && t0.fCalculateTest0; // ... // 2D: @@ -585,13 +585,13 @@ void defaultConfiguration() } // I append "&& t0.fCalculate2DTest0" below, to switch off calculation of all Test0 with one common flag: - t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_PT] = Alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_PT]) && t0.fCalculate2DTest0; - t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_ETA] = Alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_ETA]) && t0.fCalculate2DTest0; - t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_CHARGE] = Alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_CHARGE]) && t0.fCalculate2DTest0; - t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_VZ] = Alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_VZ]) && t0.fCalculate2DTest0; - t0.fCalculate2DTest0AsFunctionOf[AFO_PT_ETA] = Alright(lCalculate2DTest0AsFunctionOf[AFO_PT_ETA]) && t0.fCalculate2DTest0; - t0.fCalculate2DTest0AsFunctionOf[AFO_PT_CHARGE] = Alright(lCalculate2DTest0AsFunctionOf[AFO_PT_CHARGE]) && t0.fCalculate2DTest0; - t0.fCalculate2DTest0AsFunctionOf[AFO_ETA_CHARGE] = Alright(lCalculate2DTest0AsFunctionOf[AFO_ETA_CHARGE]) && t0.fCalculate2DTest0; + t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_PT] = alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_PT]) && t0.fCalculate2DTest0; + t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_ETA] = alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_ETA]) && t0.fCalculate2DTest0; + t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_CHARGE] = alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_CHARGE]) && t0.fCalculate2DTest0; + t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_VZ] = alright(lCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_VZ]) && t0.fCalculate2DTest0; + t0.fCalculate2DTest0AsFunctionOf[AFO_PT_ETA] = alright(lCalculate2DTest0AsFunctionOf[AFO_PT_ETA]) && t0.fCalculate2DTest0; + t0.fCalculate2DTest0AsFunctionOf[AFO_PT_CHARGE] = alright(lCalculate2DTest0AsFunctionOf[AFO_PT_CHARGE]) && t0.fCalculate2DTest0; + t0.fCalculate2DTest0AsFunctionOf[AFO_ETA_CHARGE] = alright(lCalculate2DTest0AsFunctionOf[AFO_ETA_CHARGE]) && t0.fCalculate2DTest0; // ... @@ -609,13 +609,13 @@ void defaultConfiguration() } // I append "&& t0.fCalculate3DTest0" below, to switch off calculation of all Test0 with one common flag: - t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_ETA] = Alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_ETA]) && t0.fCalculate3DTest0; - t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_CHARGE] = Alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_CHARGE]) && t0.fCalculate3DTest0; - t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_VZ] = Alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_VZ]) && t0.fCalculate3DTest0; - t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_VZ] = Alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_VZ]) && t0.fCalculate3DTest0; - t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_CHARGE] = Alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_CHARGE]) && t0.fCalculate3DTest0; - t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_VZ_CHARGE] = Alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_VZ_CHARGE]) && t0.fCalculate3DTest0; - t0.fCalculate3DTest0AsFunctionOf[AFO_PT_ETA_CHARGE] = Alright(lCalculate3DTest0AsFunctionOf[AFO_PT_ETA_CHARGE]) && t0.fCalculate3DTest0; + t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_ETA] = alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_ETA]) && t0.fCalculate3DTest0; + t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_CHARGE] = alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_CHARGE]) && t0.fCalculate3DTest0; + t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_VZ] = alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_VZ]) && t0.fCalculate3DTest0; + t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_VZ] = alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_VZ]) && t0.fCalculate3DTest0; + t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_CHARGE] = alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_CHARGE]) && t0.fCalculate3DTest0; + t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_VZ_CHARGE] = alright(lCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_VZ_CHARGE]) && t0.fCalculate3DTest0; + t0.fCalculate3DTest0AsFunctionOf[AFO_PT_ETA_CHARGE] = alright(lCalculate3DTest0AsFunctionOf[AFO_PT_ETA_CHARGE]) && t0.fCalculate3DTest0; // ... @@ -641,17 +641,17 @@ void defaultConfiguration() } for (int dpw = 0; dpw < eDiffPhiWeights_N; dpw++) { // "differential phi weight" if (TString(lWhichDiffPhiWeights[dpw]).Contains("wPhi")) { - pw.fUseDiffPhiWeights[wPhiPhiAxis] = Alright(lWhichDiffPhiWeights[dpw]); // if I pass "1-Phi" => true, "0-Phi" => false + pw.fUseDiffPhiWeights[wPhiPhiAxis] = alright(lWhichDiffPhiWeights[dpw]); // if I pass "1-Phi" => true, "0-Phi" => false } else if (TString(lWhichDiffPhiWeights[dpw]).Contains("wPt")) { - pw.fUseDiffPhiWeights[wPhiPtAxis] = Alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; // I chain here with wPhiPhiAxis , so that I can switch off all differential phi weights, if phi itself is not set to true + pw.fUseDiffPhiWeights[wPhiPtAxis] = alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; // I chain here with wPhiPhiAxis , so that I can switch off all differential phi weights, if phi itself is not set to true } else if (TString(lWhichDiffPhiWeights[dpw]).Contains("wEta")) { - pw.fUseDiffPhiWeights[wPhiEtaAxis] = Alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; + pw.fUseDiffPhiWeights[wPhiEtaAxis] = alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; } else if (TString(lWhichDiffPhiWeights[dpw]).Contains("wCharge")) { - pw.fUseDiffPhiWeights[wPhiChargeAxis] = Alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; + pw.fUseDiffPhiWeights[wPhiChargeAxis] = alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; } else if (TString(lWhichDiffPhiWeights[dpw]).Contains("wCentrality")) { - pw.fUseDiffPhiWeights[wPhiCentralityAxis] = Alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; + pw.fUseDiffPhiWeights[wPhiCentralityAxis] = alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; } else if (TString(lWhichDiffPhiWeights[dpw]).Contains("wVertexZ") || TString(lWhichDiffPhiWeights[dpw]).Contains("wVertex_z")) { // TBI 20250402 I keep "wVertex_z" here just in case I still have somewhere dependency on it, remove eventually - pw.fUseDiffPhiWeights[wPhiVertexZAxis] = Alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; + pw.fUseDiffPhiWeights[wPhiVertexZAxis] = alright(lWhichDiffPhiWeights[dpw]) && pw.fUseDiffPhiWeights[wPhiPhiAxis]; } else { LOGF(fatal, "\033[1;31m%s at line %d : The setting %s in configurable cfWhichDiffPhiWeights is not supported yet. See enum eDiffPhiWeights . \n \033[0m", __FUNCTION__, __LINE__, TString(lWhichDiffPhiWeights[dpw]).Data()); } @@ -666,13 +666,15 @@ void defaultConfiguration() } for (int dpw = 0; dpw < eDiffPtWeights_N; dpw++) { // "differential pt weight" if (TString(lWhichDiffPtWeights[dpw]).Contains("wPt")) { - pw.fUseDiffPtWeights[wPtPtAxis] = Alright(lWhichDiffPtWeights[dpw]); // if I pass "1-Pt" => true, "0-Pt" => false + pw.fUseDiffPtWeights[wPtPtAxis] = alright(lWhichDiffPtWeights[dpw]); // if I pass "1-Pt" => true, "0-Pt" => false } else if (TString(lWhichDiffPtWeights[dpw]).Contains("wEta")) { - pw.fUseDiffPtWeights[wPtEtaAxis] = Alright(lWhichDiffPtWeights[dpw]) && pw.fUseDiffPtWeights[wPtPtAxis]; + pw.fUseDiffPtWeights[wPtEtaAxis] = alright(lWhichDiffPtWeights[dpw]) && pw.fUseDiffPtWeights[wPtPtAxis]; } else if (TString(lWhichDiffPtWeights[dpw]).Contains("wCharge")) { - pw.fUseDiffPtWeights[wPtChargeAxis] = Alright(lWhichDiffPtWeights[dpw]) && pw.fUseDiffPtWeights[wPtPtAxis]; + pw.fUseDiffPtWeights[wPtChargeAxis] = alright(lWhichDiffPtWeights[dpw]) && pw.fUseDiffPtWeights[wPtPtAxis]; } else if (TString(lWhichDiffPtWeights[dpw]).Contains("wCentrality")) { - pw.fUseDiffPtWeights[wPtCentralityAxis] = Alright(lWhichDiffPtWeights[dpw]) && pw.fUseDiffPtWeights[wPtPtAxis]; + pw.fUseDiffPtWeights[wPtCentralityAxis] = alright(lWhichDiffPtWeights[dpw]) && pw.fUseDiffPtWeights[wPtPtAxis]; + } else if (TString(lWhichDiffPtWeights[dpw]).Contains("wVertexZ")) { + pw.fUseDiffPtWeights[wPtVertexZAxis] = alright(lWhichDiffPtWeights[dpw]) && pw.fUseDiffPtWeights[wPtPtAxis]; } else { LOGF(fatal, "\033[1;31m%s at line %d : The setting %s in configurable cfWhichDiffPtWeights is not supported yet. See enum eDiffPtWeights . \n \033[0m", __FUNCTION__, __LINE__, TString(lWhichDiffPtWeights[dpw]).Data()); } @@ -687,13 +689,15 @@ void defaultConfiguration() } for (int dpw = 0; dpw < eDiffEtaWeights_N; dpw++) { // "differential eta weight" if (TString(lWhichDiffEtaWeights[dpw]).Contains("wEta")) { - pw.fUseDiffEtaWeights[wEtaEtaAxis] = Alright(lWhichDiffEtaWeights[dpw]); // if I pass "1-Eta" => true, "0-Eta" => false + pw.fUseDiffEtaWeights[wEtaEtaAxis] = alright(lWhichDiffEtaWeights[dpw]); // if I pass "1-Eta" => true, "0-Eta" => false } else if (TString(lWhichDiffEtaWeights[dpw]).Contains("wPt")) { - pw.fUseDiffEtaWeights[wEtaPtAxis] = Alright(lWhichDiffEtaWeights[dpw]) && pw.fUseDiffEtaWeights[wEtaEtaAxis]; + pw.fUseDiffEtaWeights[wEtaPtAxis] = alright(lWhichDiffEtaWeights[dpw]) && pw.fUseDiffEtaWeights[wEtaEtaAxis]; } else if (TString(lWhichDiffEtaWeights[dpw]).Contains("wCharge")) { - pw.fUseDiffEtaWeights[wEtaChargeAxis] = Alright(lWhichDiffEtaWeights[dpw]) && pw.fUseDiffEtaWeights[wEtaEtaAxis]; + pw.fUseDiffEtaWeights[wEtaChargeAxis] = alright(lWhichDiffEtaWeights[dpw]) && pw.fUseDiffEtaWeights[wEtaEtaAxis]; } else if (TString(lWhichDiffEtaWeights[dpw]).Contains("wCentrality")) { - pw.fUseDiffEtaWeights[wEtaCentralityAxis] = Alright(lWhichDiffEtaWeights[dpw]) && pw.fUseDiffEtaWeights[wEtaEtaAxis]; + pw.fUseDiffEtaWeights[wEtaCentralityAxis] = alright(lWhichDiffEtaWeights[dpw]) && pw.fUseDiffEtaWeights[wEtaEtaAxis]; + } else if (TString(lWhichDiffEtaWeights[dpw]).Contains("wVertexZ")) { + pw.fUseDiffEtaWeights[wEtaVertexZAxis] = alright(lWhichDiffEtaWeights[dpw]) && pw.fUseDiffEtaWeights[wEtaEtaAxis]; } else { LOGF(fatal, "\033[1;31m%s at line %d : The setting %s in configurable cfWhichDiffEtaWeights is not supported yet. See enum eDiffEtaWeights . \n \033[0m", __FUNCTION__, __LINE__, TString(lWhichDiffEtaWeights[dpw]).Data()); } @@ -708,13 +712,15 @@ void defaultConfiguration() } for (int dpw = 0; dpw < eDiffChargeWeights_N; dpw++) { // "differential charge weight" if (TString(lWhichDiffChargeWeights[dpw]).Contains("wCharge")) { - pw.fUseDiffChargeWeights[wChargeChargeAxis] = Alright(lWhichDiffChargeWeights[dpw]); // if I pass "1-Charge" => true, "0-Charge" => false + pw.fUseDiffChargeWeights[wChargeChargeAxis] = alright(lWhichDiffChargeWeights[dpw]); // if I pass "1-Charge" => true, "0-Charge" => false } else if (TString(lWhichDiffChargeWeights[dpw]).Contains("wPt")) { - pw.fUseDiffChargeWeights[wChargePtAxis] = Alright(lWhichDiffChargeWeights[dpw]) && pw.fUseDiffChargeWeights[wChargeChargeAxis]; + pw.fUseDiffChargeWeights[wChargePtAxis] = alright(lWhichDiffChargeWeights[dpw]) && pw.fUseDiffChargeWeights[wChargeChargeAxis]; } else if (TString(lWhichDiffChargeWeights[dpw]).Contains("wEta")) { - pw.fUseDiffChargeWeights[wChargeEtaAxis] = Alright(lWhichDiffChargeWeights[dpw]) && pw.fUseDiffChargeWeights[wChargeChargeAxis]; + pw.fUseDiffChargeWeights[wChargeEtaAxis] = alright(lWhichDiffChargeWeights[dpw]) && pw.fUseDiffChargeWeights[wChargeChargeAxis]; } else if (TString(lWhichDiffChargeWeights[dpw]).Contains("wCentrality")) { - pw.fUseDiffChargeWeights[wChargeCentralityAxis] = Alright(lWhichDiffChargeWeights[dpw]) && pw.fUseDiffChargeWeights[wChargeChargeAxis]; + pw.fUseDiffChargeWeights[wChargeCentralityAxis] = alright(lWhichDiffChargeWeights[dpw]) && pw.fUseDiffChargeWeights[wChargeChargeAxis]; + } else if (TString(lWhichDiffChargeWeights[dpw]).Contains("wVertexZ")) { + pw.fUseDiffChargeWeights[wChargeVertexZAxis] = alright(lWhichDiffChargeWeights[dpw]) && pw.fUseDiffChargeWeights[wChargeChargeAxis]; } else { LOGF(fatal, "\033[1;31m%s at line %d : The setting %s in configurable cfWhichDiffChargeWeights is not supported yet. See enum eDiffChargeWeights . \n \033[0m", __FUNCTION__, __LINE__, TString(lWhichDiffChargeWeights[dpw]).Data()); } @@ -723,7 +729,7 @@ void defaultConfiguration() // **) Only for debugging purposes, print all enabled weights: if (tc.fVerbose) { LOGF(info, "\033[1;31m%s at line %d : printing current status of all weights flags\033[0m", __FUNCTION__, __LINE__); - PrintAllWeightsFlags(); + printAllWeightsFlags(); } // if (tc.fVerbose) { // **) File holding all particle weights: @@ -748,7 +754,7 @@ void defaultConfiguration() if (lApplyNUAPDF.size() != eNUAPDF_N) { LOGF(info, "\033[1;31m lApplyNUAPDF.size() = %d\033[0m", lApplyNUAPDF.size()); LOGF(info, "\033[1;31m eNUAPDF_N = %d\033[0m", static_cast(eNUAPDF_N)); - LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfApplyNUAPDF, and number of entries in enum eNUAPDF \n \033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfApplyNUAPDF, and number of entries in enum EnNUAPDF \n \033[0m", __FUNCTION__, __LINE__); } nua.fApplyNUAPDF[ePhiNUAPDF] = static_cast(lApplyNUAPDF[ePhiNUAPDF]); nua.fApplyNUAPDF[ePtNUAPDF] = static_cast(lApplyNUAPDF[ePtNUAPDF]); @@ -762,7 +768,7 @@ void defaultConfiguration() if (lUseDefaultNUAPDF.size() != eNUAPDF_N) { LOGF(info, "\033[1;31m lUseDefaultNUAPDF.size() = %d\033[0m", lUseDefaultNUAPDF.size()); LOGF(info, "\033[1;31m eNUAPDF_N = %d\033[0m", static_cast(eNUAPDF_N)); - LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfUseDefaultNUAPDF, and number of entries in enum eNUAPDF \n \033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfUseDefaultNUAPDF, and number of entries in enum EnNUAPDF \n \033[0m", __FUNCTION__, __LINE__); } nua.fUseDefaultNUAPDF[ePhiNUAPDF] = static_cast(lUseDefaultNUAPDF[ePhiNUAPDF]); nua.fUseDefaultNUAPDF[ePtNUAPDF] = static_cast(lUseDefaultNUAPDF[ePtNUAPDF]); @@ -786,12 +792,12 @@ void defaultConfiguration() if (lCustomNUAPDFHistNames.size() != eNUAPDF_N) { LOGF(info, "\033[1;31m lCustomNUAPDFHistNames.size() = %d\033[0m", lCustomNUAPDFHistNames.size()); LOGF(info, "\033[1;31m eNUAPDF_N = %d\033[0m", static_cast(eNUAPDF_N)); - LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfCustomNUAPDFHistNames, and number of entries in enum eNUAPDF \n \033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfCustomNUAPDFHistNames, and number of entries in enum EnNUAPDF \n \033[0m", __FUNCTION__, __LINE__); } if (!nua.fUseDefaultNUAPDF[ePhiNUAPDF]) { nua.fCustomNUAPDFHistNames[ePhiNUAPDF] = new TString(lCustomNUAPDFHistNames[ePhiNUAPDF]); - this->GetHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), ePhiNUAPDF); + this->getHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), ePhiNUAPDF); if (!nua.fCustomNUAPDF[ePhiNUAPDF]) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -799,21 +805,21 @@ void defaultConfiguration() if (!nua.fUseDefaultNUAPDF[ePtNUAPDF]) { nua.fCustomNUAPDFHistNames[ePtNUAPDF] = new TString(lCustomNUAPDFHistNames[ePtNUAPDF]); - this->GetHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), ePtNUAPDF); + this->getHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), ePtNUAPDF); if (!nua.fCustomNUAPDF[ePtNUAPDF]) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } } if (!nua.fUseDefaultNUAPDF[eEtaNUAPDF]) { nua.fCustomNUAPDFHistNames[eEtaNUAPDF] = new TString(lCustomNUAPDFHistNames[eEtaNUAPDF]); - this->GetHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), eEtaNUAPDF); + this->getHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), eEtaNUAPDF); if (!nua.fCustomNUAPDF[eEtaNUAPDF]) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } } if (!nua.fUseDefaultNUAPDF[eChargeNUAPDF]) { nua.fCustomNUAPDFHistNames[eChargeNUAPDF] = new TString(lCustomNUAPDFHistNames[eChargeNUAPDF]); - this->GetHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), eChargeNUAPDF); + this->getHistogramWithCustomNUA(nua.fFileWithCustomNUA.Data(), eChargeNUAPDF); if (!nua.fCustomNUAPDF[eChargeNUAPDF]) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -839,7 +845,7 @@ void defaultConfiguration() // *) Results histograms: // **) Fixed-length or variable-length binning: - // Remark: keep ordering in sync with enum eAsFunctionOf: + // Remark: keep ordering in sync with enum EnAsFunctionOf: cf_res.cfUseVariableLengthMultBins ? res.fUseResultsProVariableLengthBins[AFO_MULTIPLICITY] = true : res.fUseResultsProVariableLengthBins[AFO_MULTIPLICITY] = false; cf_res.cfUseVariableLengthCentBins ? res.fUseResultsProVariableLengthBins[AFO_CENTRALITY] = true : res.fUseResultsProVariableLengthBins[AFO_CENTRALITY] = false; cf_res.cfUseVariableLengthPtBins ? res.fUseResultsProVariableLengthBins[AFO_PT] = true : res.fUseResultsProVariableLengthBins[AFO_PT] = false; @@ -849,7 +855,7 @@ void defaultConfiguration() cf_res.cfUseVariableLengthVzBins ? res.fUseResultsProVariableLengthBins[AFO_VZ] = true : res.fUseResultsProVariableLengthBins[AFO_VZ] = false; // **) Define axis titles: - // Remark: keep ordering in sync with enum eAsFunctionOf + // Remark: keep ordering in sync with enum EnAsFunctionOf // 1D: res.fResultsProXaxisTitle[AFO_INTEGRATED] = "integrated"; res.fResultsProRawName[AFO_INTEGRATED] = "int"; // this is how it appears simplified in the hist name when saved to the file @@ -910,7 +916,7 @@ void defaultConfiguration() qa.fOccupancyEstimatorName[eFT0COccupancyInTimeRange] = "FT0COccupancyInTimeRange"; // **) Names of QA 2D event histograms: - // Remark: Do NOT use FancyFormatting here, only later in bookQAHistograms() for axis titles! + // Remark: Do NOT use fancyFormatting here, only later in bookQAHistograms() for axis titles! qa.fEventHistogramsName2D[eMultiplicity_vs_ReferenceMultiplicity] = Form("%s_vs_%s", eh.fEventHistogramsName[eMultiplicity].Data(), eh.fEventHistogramsName[eReferenceMultiplicity].Data()); qa.fEventHistogramsName2D[eMultiplicity_vs_NContributors] = Form("%s_vs_%s", eh.fEventHistogramsName[eMultiplicity].Data(), eh.fEventHistogramsName[eNContributors].Data()); qa.fEventHistogramsName2D[eMultiplicity_vs_Centrality] = Form("%s_vs_%s", eh.fEventHistogramsName[eMultiplicity].Data(), eh.fEventHistogramsName[eCentrality].Data()); @@ -1056,7 +1062,7 @@ void defaultConfiguration() // *) Use configurable array cfCalculateEtaSeparationsAsFunctionOf, to specify vs which observable EtaSeparations will be calculated (flags 1 or 0). // Supported format: "0-someName" and "1-someName", where "-" is a field separator. - // Ordering of the flags in that array is interpreted through ordering of enums in enum eAsFunctionOf. + // Ordering of the flags in that array is interpreted through ordering of enums in enum EnAsFunctionOf. auto lCalculateEtaSeparationsAsFunctionOf = cf_es.cfCalculateEtaSeparationsAsFunctionOf.value; // this is now the local version of that string array from configurable. if (lCalculateEtaSeparationsAsFunctionOf.size() != eAsFunctionOf_N) { LOGF(info, "\033[1;31m lCalculateEtaSeparationsAsFunctionOf.size() = %d\033[0m", lCalculateEtaSeparationsAsFunctionOf.size()); @@ -1065,16 +1071,16 @@ void defaultConfiguration() } // I append "&& es.fCalculateEtaSeparations" below, to switch off calculation of all correlations with one common flag: - es.fCalculateEtaSeparationsAsFunctionOf[AFO_INTEGRATED] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_INTEGRATED]) && es.fCalculateEtaSeparations; - es.fCalculateEtaSeparationsAsFunctionOf[AFO_MULTIPLICITY] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_MULTIPLICITY]) && es.fCalculateEtaSeparations; - es.fCalculateEtaSeparationsAsFunctionOf[AFO_CENTRALITY] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_CENTRALITY]) && es.fCalculateEtaSeparations; - es.fCalculateEtaSeparationsAsFunctionOf[AFO_PT] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_PT]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_INTEGRATED] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_INTEGRATED]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_MULTIPLICITY] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_MULTIPLICITY]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_CENTRALITY] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_CENTRALITY]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_PT] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_PT]) && es.fCalculateEtaSeparations; es.fCalculateEtaSeparationsAsFunctionOf[AFO_ETA] = false; // yes, in this context this one doesn't make sense - es.fCalculateEtaSeparationsAsFunctionOf[AFO_OCCUPANCY] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_OCCUPANCY]) && es.fCalculateEtaSeparations; - es.fCalculateEtaSeparationsAsFunctionOf[AFO_INTERACTIONRATE] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_INTERACTIONRATE]) && es.fCalculateEtaSeparations; - es.fCalculateEtaSeparationsAsFunctionOf[AFO_CURRENTRUNDURATION] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_CURRENTRUNDURATION]) && es.fCalculateEtaSeparations; - es.fCalculateEtaSeparationsAsFunctionOf[AFO_VZ] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_VZ]) && es.fCalculateEtaSeparations; - es.fCalculateEtaSeparationsAsFunctionOf[AFO_CHARGE] = Alright(lCalculateEtaSeparationsAsFunctionOf[AFO_CHARGE]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_OCCUPANCY] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_OCCUPANCY]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_INTERACTIONRATE] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_INTERACTIONRATE]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_CURRENTRUNDURATION] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_CURRENTRUNDURATION]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_VZ] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_VZ]) && es.fCalculateEtaSeparations; + es.fCalculateEtaSeparationsAsFunctionOf[AFO_CHARGE] = alright(lCalculateEtaSeparationsAsFunctionOf[AFO_CHARGE]) && es.fCalculateEtaSeparations; // ... if (es.fCalculateEtaSeparations) { @@ -1098,7 +1104,7 @@ void defaultConfiguration() } for (int h = 0; h < static_cast(lEtaSeparationsSkipHarmonics.size()); h++) { - es.fEtaSeparationsSkipHarmonics[h] = Alright(lEtaSeparationsSkipHarmonics[h]); + es.fEtaSeparationsSkipHarmonics[h] = alright(lEtaSeparationsSkipHarmonics[h]); } } // if(es.fCalculateEtaSeparations) { @@ -1107,40 +1113,40 @@ void defaultConfiguration() // TBI 20250601 I have to do it without loop, until I provide support for 2D and 3D to Correlations and EtaSeparations // Test0 and Correlations: - if (mupa.fCalculateCorrelationsAsFunctionOf[AfoKineMap1D(PTq)] || t0.fCalculateTest0AsFunctionOf[AfoKineMap1D(PTq)]) { + if (mupa.fCalculateCorrelationsAsFunctionOf[afoKineMap1D(PTq)] || t0.fCalculateTest0AsFunctionOf[afoKineMap1D(PTq)]) { qv.fCalculateqvectorsKine[PTq] = true; } - if (mupa.fCalculateCorrelationsAsFunctionOf[AfoKineMap1D(ETAq)] || t0.fCalculateTest0AsFunctionOf[AfoKineMap1D(ETAq)]) { + if (mupa.fCalculateCorrelationsAsFunctionOf[afoKineMap1D(ETAq)] || t0.fCalculateTest0AsFunctionOf[afoKineMap1D(ETAq)]) { qv.fCalculateqvectorsKine[ETAq] = true; } - if (mupa.fCalculateCorrelationsAsFunctionOf[AfoKineMap1D(CHARGEq)] || t0.fCalculateTest0AsFunctionOf[AfoKineMap1D(CHARGEq)]) { + if (mupa.fCalculateCorrelationsAsFunctionOf[afoKineMap1D(CHARGEq)] || t0.fCalculateTest0AsFunctionOf[afoKineMap1D(CHARGEq)]) { qv.fCalculateqvectorsKine[CHARGEq] = true; } - if (t0.fCalculate2DTest0AsFunctionOf[AfoKineMap2D(PT_ETAq)]) { + if (t0.fCalculate2DTest0AsFunctionOf[afoKineMap2D(PT_ETAq)]) { qv.fCalculateqvectorsKine[PT_ETAq] = true; } - if (t0.fCalculate2DTest0AsFunctionOf[AfoKineMap2D(PT_CHARGEq)]) { + if (t0.fCalculate2DTest0AsFunctionOf[afoKineMap2D(PT_CHARGEq)]) { qv.fCalculateqvectorsKine[PT_CHARGEq] = true; } - if (t0.fCalculate2DTest0AsFunctionOf[AfoKineMap2D(ETA_CHARGEq)]) { + if (t0.fCalculate2DTest0AsFunctionOf[afoKineMap2D(ETA_CHARGEq)]) { qv.fCalculateqvectorsKine[ETA_CHARGEq] = true; } - if (t0.fCalculate3DTest0AsFunctionOf[AfoKineMap3D(PT_ETA_CHARGEq)]) { + if (t0.fCalculate3DTest0AsFunctionOf[afoKineMap3D(PT_ETA_CHARGEq)]) { qv.fCalculateqvectorsKine[PT_ETA_CHARGEq] = true; } // Eta separations: - if (es.fCalculateEtaSeparationsAsFunctionOf[AfoKineMap1D(PTq)]) { + if (es.fCalculateEtaSeparationsAsFunctionOf[afoKineMap1D(PTq)]) { qv.fCalculateqvectorsKineEtaSeparations[PTq] = true; } qv.fCalculateqvectorsKineEtaSeparations[ETAq] = false; // yes, this one is alwas set explicitly to false - if (es.fCalculateEtaSeparationsAsFunctionOf[AfoKineMap1D(CHARGEq)]) { + if (es.fCalculateEtaSeparationsAsFunctionOf[afoKineMap1D(CHARGEq)]) { qv.fCalculateqvectorsKineEtaSeparations[CHARGEq] = true; } qv.fCalculateqvectorsKineEtaSeparations[PT_ETAq] = false; // yes, this one is alwas set explicitly to false // TBI 20250617 comment in this branch, when i implement support for 2D eta separations. - // if (es.fCalculate2DEtaSeparationsAsFunctionOf[AfoKineMap2D(PT_CHARGEq)]) { + // if (es.fCalculate2DEtaSeparationsAsFunctionOf[afoKineMap2D(PT_CHARGEq)]) { // qv.fCalculateqvectorsKineEtaSeparations[PT_CHARGEq] = true; // } @@ -1169,14 +1175,14 @@ void defaultConfiguration() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void defaultConfiguration() //============================================================ -void PrintAllWeightsFlags() +void printAllWeightsFlags() { // Simple utility function, to print current status of all weights flags. @@ -1197,11 +1203,11 @@ void PrintAllWeightsFlags() LOGF(info, "\033[1;33m %d \033[0m", pw.fUseDiffChargeWeights[dpw]); } -} // void PrintAllWeightsFlags() +} // void printAllWeightsFlags() //============================================================ -bool Alright(TString s) +bool alright(TString s) { // Simple utility function, which for a string formatted "0-someName" returns false, and for "1-someName" returns true. @@ -1209,7 +1215,7 @@ bool Alright(TString s) // b) Do the thing. if (tc.fVerboseUtility) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;32m TString s = %s\033[0m", s.Data()); } @@ -1219,9 +1225,11 @@ bool Alright(TString s) TObjArray* oa = s.Tokenize("-"); if (!oa) { LOGF(fatal, "\033[1;31m%s at line %d : oa is NULL , s = %s\033[0m", __FUNCTION__, __LINE__, s.Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } + int nEntriesExpected = 2; // string format is "[01]-someName", with "-" serving as IFS int nEntries = oa->GetEntries(); - if (2 != nEntries) { + if (nEntriesExpected != nEntries) { LOGF(fatal, "\033[1;31m%s at line %d : string expected in this function must be formatted as \"0-someName\" or \"1-someName\" => s = %s\033[0m", __FUNCTION__, __LINE__, s.Data()); } @@ -1238,12 +1246,12 @@ bool Alright(TString s) } if (tc.fVerboseUtility) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return returnValue; -} // bool Alright(const char* name) +} // bool alright(const char* name) //============================================================ @@ -1259,7 +1267,7 @@ void defaultBooking() // f) QA. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Event histograms 1D: @@ -1269,12 +1277,12 @@ void defaultBooking() // *) By default all event histograms are booked. If you do not want particular event histogram to be booked, // use configurable array cfBookEventHistograms, where you can specify name of the histogram accompanied with flags 1 (book) or 0 (do not book). // Supported format: "0-someName" and "1-someName", where "-" is a field separator. - // Ordering of the flags in that array is interpreted through ordering of enums in enum eEventHistograms. + // Ordering of the flags in that array is interpreted through ordering of enums in enum EEventHistograms. auto lBookEventHistograms = cf_eh.cfBookEventHistograms.value; // this is now the local version of that string array from configurable. if (lBookEventHistograms.size() != eEventHistograms_N) { LOGF(info, "\033[1;31m lBookEventHistograms.size() = %d\033[0m", lBookEventHistograms.size()); LOGF(info, "\033[1;31m eEventHistograms_N) = %d\033[0m", static_cast(eEventHistograms_N)); - LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfBookEventHistograms, and number of entries in enum eEventHistograms \n \033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfBookEventHistograms, and number of entries in enum EEventHistograms \n \033[0m", __FUNCTION__, __LINE__); } // *) Insanity check on the content and ordering of histogram names in the initialization in configurable cfBookEventHistograms: @@ -1287,21 +1295,21 @@ void defaultBooking() } // I append "&& eh.fFillEventHistograms" below, to switch off booking of all event histograms with one common flag: - eh.fBookEventHistograms[eNumberOfEvents] = Alright(lBookEventHistograms[eNumberOfEvents]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eTotalMultiplicity] = Alright(lBookEventHistograms[eTotalMultiplicity]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eMultiplicity] = Alright(lBookEventHistograms[eMultiplicity]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eReferenceMultiplicity] = Alright(lBookEventHistograms[eReferenceMultiplicity]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eCentrality] = Alright(lBookEventHistograms[eCentrality]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eVertexX] = Alright(lBookEventHistograms[eVertexX]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eVertexY] = Alright(lBookEventHistograms[eVertexY]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eVertexZ] = Alright(lBookEventHistograms[eVertexZ]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eNContributors] = Alright(lBookEventHistograms[eNContributors]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eImpactParameter] = Alright(lBookEventHistograms[eImpactParameter]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eEventPlaneAngle] = Alright(lBookEventHistograms[eEventPlaneAngle]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eOccupancy] = Alright(lBookEventHistograms[eOccupancy]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eInteractionRate] = Alright(lBookEventHistograms[eInteractionRate]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eCurrentRunDuration] = Alright(lBookEventHistograms[eCurrentRunDuration]) && eh.fFillEventHistograms; - eh.fBookEventHistograms[eMultMCNParticlesEta08] = Alright(lBookEventHistograms[eMultMCNParticlesEta08]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eNumberOfEvents] = alright(lBookEventHistograms[eNumberOfEvents]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eTotalMultiplicity] = alright(lBookEventHistograms[eTotalMultiplicity]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eMultiplicity] = alright(lBookEventHistograms[eMultiplicity]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eReferenceMultiplicity] = alright(lBookEventHistograms[eReferenceMultiplicity]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eCentrality] = alright(lBookEventHistograms[eCentrality]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eVertexX] = alright(lBookEventHistograms[eVertexX]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eVertexY] = alright(lBookEventHistograms[eVertexY]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eVertexZ] = alright(lBookEventHistograms[eVertexZ]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eNContributors] = alright(lBookEventHistograms[eNContributors]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eImpactParameter] = alright(lBookEventHistograms[eImpactParameter]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eEventPlaneAngle] = alright(lBookEventHistograms[eEventPlaneAngle]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eOccupancy] = alright(lBookEventHistograms[eOccupancy]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eInteractionRate] = alright(lBookEventHistograms[eInteractionRate]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eCurrentRunDuration] = alright(lBookEventHistograms[eCurrentRunDuration]) && eh.fFillEventHistograms; + eh.fBookEventHistograms[eMultMCNParticlesEta08] = alright(lBookEventHistograms[eMultMCNParticlesEta08]) && eh.fFillEventHistograms; // b) Event histograms 2D: // TBI 20240515 Ideally, all 2D shall go to QA group, see below @@ -1330,24 +1338,24 @@ void defaultBooking() } // I append "&& ph.fFillParticleHistograms" below, to switch off booking of all 1D particle histograms with one common flag: - ph.fBookParticleHistograms[ePhi] = Alright(lBookParticleHistograms[ePhi]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[ePt] = Alright(lBookParticleHistograms[ePt]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[eEta] = Alright(lBookParticleHistograms[eEta]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[eCharge] = Alright(lBookParticleHistograms[eCharge]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcNClsFindable] = Alright(lBookParticleHistograms[etpcNClsFindable]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcNClsShared] = Alright(lBookParticleHistograms[etpcNClsShared]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[eitsChi2NCl] = Alright(lBookParticleHistograms[eitsChi2NCl]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcNClsFound] = Alright(lBookParticleHistograms[etpcNClsFound]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcNClsCrossedRows] = Alright(lBookParticleHistograms[etpcNClsCrossedRows]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[eitsNCls] = Alright(lBookParticleHistograms[eitsNCls]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[eitsNClsInnerBarrel] = Alright(lBookParticleHistograms[eitsNClsInnerBarrel]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcCrossedRowsOverFindableCls] = Alright(lBookParticleHistograms[etpcCrossedRowsOverFindableCls]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcFoundOverFindableCls] = Alright(lBookParticleHistograms[etpcFoundOverFindableCls]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcFractionSharedCls] = Alright(lBookParticleHistograms[etpcFractionSharedCls]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[etpcChi2NCl] = Alright(lBookParticleHistograms[etpcChi2NCl]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[edcaXY] = Alright(lBookParticleHistograms[edcaXY]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[edcaZ] = Alright(lBookParticleHistograms[edcaZ]) && ph.fFillParticleHistograms; - ph.fBookParticleHistograms[ePDG] = Alright(lBookParticleHistograms[ePDG]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[ePhi] = alright(lBookParticleHistograms[ePhi]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[ePt] = alright(lBookParticleHistograms[ePt]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[eEta] = alright(lBookParticleHistograms[eEta]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[eCharge] = alright(lBookParticleHistograms[eCharge]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcNClsFindable] = alright(lBookParticleHistograms[etpcNClsFindable]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcNClsShared] = alright(lBookParticleHistograms[etpcNClsShared]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[eitsChi2NCl] = alright(lBookParticleHistograms[eitsChi2NCl]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcNClsFound] = alright(lBookParticleHistograms[etpcNClsFound]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcNClsCrossedRows] = alright(lBookParticleHistograms[etpcNClsCrossedRows]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[eitsNCls] = alright(lBookParticleHistograms[eitsNCls]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[eitsNClsInnerBarrel] = alright(lBookParticleHistograms[eitsNClsInnerBarrel]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcCrossedRowsOverFindableCls] = alright(lBookParticleHistograms[etpcCrossedRowsOverFindableCls]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcFoundOverFindableCls] = alright(lBookParticleHistograms[etpcFoundOverFindableCls]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcFractionSharedCls] = alright(lBookParticleHistograms[etpcFractionSharedCls]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[etpcChi2NCl] = alright(lBookParticleHistograms[etpcChi2NCl]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[edcaXY] = alright(lBookParticleHistograms[edcaXY]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[edcaZ] = alright(lBookParticleHistograms[edcaZ]) && ph.fFillParticleHistograms; + ph.fBookParticleHistograms[ePDG] = alright(lBookParticleHistograms[ePDG]) && ph.fFillParticleHistograms; // Remark #1: I do not need here anythig for etrackCutFlagFb1, etrackCutFlagFb2, ... eisGlobalTrack, because they are booleans // Remark #2: Nothing special here for ePtDependentDCAxyParameterization, because that is a string. @@ -1375,8 +1383,8 @@ void defaultBooking() } // I append "&& ph.fFillParticleHistograms2D" below, to switch off booking of all 2D particle histograms with one common flag: - ph.fBookParticleHistograms2D[ePhiPt] = Alright(lBookParticleHistograms2D[ePhiPt]) && ph.fFillParticleHistograms2D; - ph.fBookParticleHistograms2D[ePhiEta] = Alright(lBookParticleHistograms2D[ePhiEta]) && ph.fFillParticleHistograms2D; + ph.fBookParticleHistograms2D[ePhiPt] = alright(lBookParticleHistograms2D[ePhiPt]) && ph.fFillParticleHistograms2D; + ph.fBookParticleHistograms2D[ePhiEta] = alright(lBookParticleHistograms2D[ePhiEta]) && ph.fFillParticleHistograms2D; // e) Particle sparse histograms: auto lRebinSparse = (std::vector)cf_ph.cfRebinSparse; @@ -1393,18 +1401,21 @@ void defaultBooking() ph.fRebinSparse[eDWPt][wPtEtaAxis] = lRebinSparse[wPhiEtaAxis]; ph.fRebinSparse[eDWPt][wPtChargeAxis] = lRebinSparse[wPhiChargeAxis]; ph.fRebinSparse[eDWPt][wPtCentralityAxis] = lRebinSparse[wPhiCentralityAxis]; + ph.fRebinSparse[eDWPt][wPtVertexZAxis] = lRebinSparse[wPhiVertexZAxis]; // ... ph.fRebinSparse[eDWEta][wEtaEtaAxis] = lRebinSparse[wPhiEtaAxis]; // yes, wPhiEtaAxis is on the RHS, becase I defined ordering of cfRebinSparse by using ordering in eDiffPhiWeights ph.fRebinSparse[eDWEta][wEtaPtAxis] = lRebinSparse[wPhiPtAxis]; ph.fRebinSparse[eDWEta][wEtaChargeAxis] = lRebinSparse[wPhiChargeAxis]; ph.fRebinSparse[eDWEta][wEtaCentralityAxis] = lRebinSparse[wPhiCentralityAxis]; + ph.fRebinSparse[eDWEta][wEtaVertexZAxis] = lRebinSparse[wPhiVertexZAxis]; // ... ph.fRebinSparse[eDWCharge][wChargeChargeAxis] = lRebinSparse[wPhiChargeAxis]; // yes, wPhiChargeAxis is on the RHS, becase I defined ordering of cfRebinSparse by using ordering in eDiffPhiWeights ph.fRebinSparse[eDWCharge][wChargePtAxis] = lRebinSparse[wPhiPtAxis]; ph.fRebinSparse[eDWCharge][wChargeEtaAxis] = lRebinSparse[wPhiEtaAxis]; ph.fRebinSparse[eDWCharge][wChargeCentralityAxis] = lRebinSparse[wPhiCentralityAxis]; + ph.fRebinSparse[eDWCharge][wChargeVertexZAxis] = lRebinSparse[wPhiVertexZAxis]; // ... // *) Categories of sparse histograms: @@ -1424,6 +1435,7 @@ void defaultBooking() TObjArray* oa = TString(lBookParticleSparseHistograms[name]).Tokenize("-"); if (!oa) { LOGF(fatal, "\033[1;31m%s at line %d : name = %s\033[0m", __FUNCTION__, __LINE__, TString(lBookParticleSparseHistograms[name]).Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } if (!ph.fParticleSparseHistogramsName[name].EndsWith(oa->At(1)->GetName())) { LOGF(fatal, "\033[1;31m%s at line %d : Wrong content or ordering of contents in configurable cfBookParticleSparseHistograms => name = %d, lBookParticleSparseHistograms[name] = \"%s\", ph.fParticleSparseHistogramsName[name] = \"%s\" \n Check if you are using an up to date tag. \033[0m", __FUNCTION__, __LINE__, name, TString(lBookParticleSparseHistograms[name]).Data(), ph.fParticleSparseHistogramsName[name].Data()); @@ -1431,10 +1443,10 @@ void defaultBooking() delete oa; } // Remark: below exceptionally I do not append the common flag with &&, since each of these flags already stands for one category - ph.fBookParticleSparseHistograms[eDWPhi] = Alright(lBookParticleSparseHistograms[eDWPhi]); - ph.fBookParticleSparseHistograms[eDWPt] = Alright(lBookParticleSparseHistograms[eDWPt]); - ph.fBookParticleSparseHistograms[eDWEta] = Alright(lBookParticleSparseHistograms[eDWEta]); - ph.fBookParticleSparseHistograms[eDWCharge] = Alright(lBookParticleSparseHistograms[eDWCharge]); + ph.fBookParticleSparseHistograms[eDWPhi] = alright(lBookParticleSparseHistograms[eDWPhi]); + ph.fBookParticleSparseHistograms[eDWPt] = alright(lBookParticleSparseHistograms[eDWPt]); + ph.fBookParticleSparseHistograms[eDWEta] = alright(lBookParticleSparseHistograms[eDWEta]); + ph.fBookParticleSparseHistograms[eDWCharge] = alright(lBookParticleSparseHistograms[eDWCharge]); // f) QA: @@ -1461,40 +1473,40 @@ void defaultBooking() } // I append "&& qa.fFillQAEventHistograms2D" below, to switch off booking of all 2D event histograms with one common flag: - qa.fBookQAEventHistograms2D[eMultiplicity_vs_ReferenceMultiplicity] = Alright(lBookQAEventHistograms2D[eMultiplicity_vs_ReferenceMultiplicity]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eMultiplicity_vs_NContributors] = Alright(lBookQAEventHistograms2D[eMultiplicity_vs_NContributors]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eMultiplicity_vs_Centrality] = Alright(lBookQAEventHistograms2D[eMultiplicity_vs_Centrality]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eMultiplicity_vs_VertexZ] = Alright(lBookQAEventHistograms2D[eMultiplicity_vs_VertexZ]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eMultiplicity_vs_Occupancy] = Alright(lBookQAEventHistograms2D[eMultiplicity_vs_Occupancy]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eMultiplicity_vs_InteractionRate] = Alright(lBookQAEventHistograms2D[eMultiplicity_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_NContributors] = Alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_NContributors]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_Centrality] = Alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_Centrality]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_VertexZ] = Alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_VertexZ]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_Occupancy] = Alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_Occupancy]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_InteractionRate] = Alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eNContributors_vs_Centrality] = Alright(lBookQAEventHistograms2D[eNContributors_vs_Centrality]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eNContributors_vs_VertexZ] = Alright(lBookQAEventHistograms2D[eNContributors_vs_VertexZ]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eNContributors_vs_Occupancy] = Alright(lBookQAEventHistograms2D[eNContributors_vs_Occupancy]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eNContributors_vs_InteractionRate] = Alright(lBookQAEventHistograms2D[eNContributors_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentrality_vs_VertexZ] = Alright(lBookQAEventHistograms2D[eCentrality_vs_VertexZ]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentrality_vs_Occupancy] = Alright(lBookQAEventHistograms2D[eCentrality_vs_Occupancy]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentrality_vs_ImpactParameter] = Alright(lBookQAEventHistograms2D[eCentrality_vs_ImpactParameter]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentrality_vs_InteractionRate] = Alright(lBookQAEventHistograms2D[eCentrality_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eVertexZ_vs_Occupancy] = Alright(lBookQAEventHistograms2D[eVertexZ_vs_Occupancy]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eVertexZ_vs_InteractionRate] = Alright(lBookQAEventHistograms2D[eVertexZ_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = Alright(lBookQAEventHistograms2D[eMultiplicity_vs_FT0CAmplitudeOnFoundBC]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = Alright(lBookQAEventHistograms2D[eCentFT0C_vs_FT0CAmplitudeOnFoundBC]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentrality_vs_CentralitySim] = Alright(lBookQAEventHistograms2D[eCentrality_vs_CentralitySim]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eMultNTracksPV_vs_MultNTracksGlobal] = Alright(lBookQAEventHistograms2D[eMultNTracksPV_vs_MultNTracksGlobal]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentFT0CVariant1] = Alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentFT0CVariant1]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentFT0M] = Alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentFT0M]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentFV0A] = Alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentFV0A]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentNTPV] = Alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentNTPV]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentNGlobal] = Alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentNGlobal]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentFT0M_vs_CentNTPV] = Alright(lBookQAEventHistograms2D[eCentFT0M_vs_CentNTPV]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCentRun2V0M_vs_CentRun2SPDTracklets] = Alright(lBookQAEventHistograms2D[eCentRun2V0M_vs_CentRun2SPDTracklets]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = Alright(lBookQAEventHistograms2D[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange]) && qa.fFillQAEventHistograms2D; - qa.fBookQAEventHistograms2D[eCurrentRunDuration_vs_InteractionRate] = Alright(lBookQAEventHistograms2D[eCurrentRunDuration_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultiplicity_vs_ReferenceMultiplicity] = alright(lBookQAEventHistograms2D[eMultiplicity_vs_ReferenceMultiplicity]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultiplicity_vs_NContributors] = alright(lBookQAEventHistograms2D[eMultiplicity_vs_NContributors]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultiplicity_vs_Centrality] = alright(lBookQAEventHistograms2D[eMultiplicity_vs_Centrality]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultiplicity_vs_VertexZ] = alright(lBookQAEventHistograms2D[eMultiplicity_vs_VertexZ]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultiplicity_vs_Occupancy] = alright(lBookQAEventHistograms2D[eMultiplicity_vs_Occupancy]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultiplicity_vs_InteractionRate] = alright(lBookQAEventHistograms2D[eMultiplicity_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_NContributors] = alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_NContributors]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_Centrality] = alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_Centrality]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_VertexZ] = alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_VertexZ]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_Occupancy] = alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_Occupancy]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eReferenceMultiplicity_vs_InteractionRate] = alright(lBookQAEventHistograms2D[eReferenceMultiplicity_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eNContributors_vs_Centrality] = alright(lBookQAEventHistograms2D[eNContributors_vs_Centrality]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eNContributors_vs_VertexZ] = alright(lBookQAEventHistograms2D[eNContributors_vs_VertexZ]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eNContributors_vs_Occupancy] = alright(lBookQAEventHistograms2D[eNContributors_vs_Occupancy]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eNContributors_vs_InteractionRate] = alright(lBookQAEventHistograms2D[eNContributors_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentrality_vs_VertexZ] = alright(lBookQAEventHistograms2D[eCentrality_vs_VertexZ]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentrality_vs_Occupancy] = alright(lBookQAEventHistograms2D[eCentrality_vs_Occupancy]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentrality_vs_ImpactParameter] = alright(lBookQAEventHistograms2D[eCentrality_vs_ImpactParameter]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentrality_vs_InteractionRate] = alright(lBookQAEventHistograms2D[eCentrality_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eVertexZ_vs_Occupancy] = alright(lBookQAEventHistograms2D[eVertexZ_vs_Occupancy]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eVertexZ_vs_InteractionRate] = alright(lBookQAEventHistograms2D[eVertexZ_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = alright(lBookQAEventHistograms2D[eMultiplicity_vs_FT0CAmplitudeOnFoundBC]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = alright(lBookQAEventHistograms2D[eCentFT0C_vs_FT0CAmplitudeOnFoundBC]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentrality_vs_CentralitySim] = alright(lBookQAEventHistograms2D[eCentrality_vs_CentralitySim]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eMultNTracksPV_vs_MultNTracksGlobal] = alright(lBookQAEventHistograms2D[eMultNTracksPV_vs_MultNTracksGlobal]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentFT0CVariant1] = alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentFT0CVariant1]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentFT0M] = alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentFT0M]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentFV0A] = alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentFV0A]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentNTPV] = alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentNTPV]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentFT0C_vs_CentNGlobal] = alright(lBookQAEventHistograms2D[eCentFT0C_vs_CentNGlobal]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentFT0M_vs_CentNTPV] = alright(lBookQAEventHistograms2D[eCentFT0M_vs_CentNTPV]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCentRun2V0M_vs_CentRun2SPDTracklets] = alright(lBookQAEventHistograms2D[eCentRun2V0M_vs_CentRun2SPDTracklets]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = alright(lBookQAEventHistograms2D[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange]) && qa.fFillQAEventHistograms2D; + qa.fBookQAEventHistograms2D[eCurrentRunDuration_vs_InteractionRate] = alright(lBookQAEventHistograms2D[eCurrentRunDuration_vs_InteractionRate]) && qa.fFillQAEventHistograms2D; // **) QA 2D particle histograms: qa.fFillQAParticleHistograms2D = cf_qa.cfFillQAParticleHistograms2D; @@ -1518,7 +1530,7 @@ void defaultBooking() } // I append "&& qa.fFillQAParticleHistograms2D" below, to switch off booking of all 2D particle histograms with one common flag: - qa.fBookQAParticleHistograms2D[ePt_vs_dcaXY] = Alright(lBookQAParticleHistograms2D[ePt_vs_dcaXY]) && qa.fFillQAParticleHistograms2D; + qa.fBookQAParticleHistograms2D[ePt_vs_dcaXY] = alright(lBookQAParticleHistograms2D[ePt_vs_dcaXY]) && qa.fFillQAParticleHistograms2D; // **) QA 2D particle event histograms: qa.fFillQAParticleEventHistograms2D = cf_qa.cfFillQAParticleEventHistograms2D; @@ -1542,16 +1554,16 @@ void defaultBooking() } // I append "&& qa.fFillQAParticleEventHistograms2D" below, to switch off booking of all 2D particle event histograms with one common flag: - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsEbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsEbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsNegEtaEbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsPosEtaEbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0804EbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0804EbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0400EbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0400EbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0004EbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0004EbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0408EbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0408EbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0005EbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0005EbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0510EbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0510EbyE]) && qa.fFillQAParticleEventHistograms2D; - qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt1050EbyE] = Alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt1050EbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsEbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsEbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsNegEtaEbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_itsNClsPosEtaEbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0804EbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0804EbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0400EbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0400EbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0004EbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0004EbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0408EbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Eta0408EbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0005EbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0005EbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0510EbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt0510EbyE]) && qa.fFillQAParticleEventHistograms2D; + qa.fBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt1050EbyE] = alright(lBookQAParticleEventHistograms2D[eCurrentRunDuration_vs_Pt1050EbyE]) && qa.fFillQAParticleEventHistograms2D; // **) QA 2D "correlations vs." histograms: qa.fFillQACorrelationsVsHistograms2D = cf_qa.cfFillQACorrelationsVsHistograms2D; @@ -1575,27 +1587,27 @@ void defaultBooking() } // I append "&& qa.fFillQACorrelationsVsHistograms2D" below, to switch off booking of all 2D "correlations vs." histograms with one common flag: - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_Multiplicity] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_Multiplicity]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_ReferenceMultiplicity] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_ReferenceMultiplicity]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_Centrality] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_Centrality]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_Multiplicity] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_Multiplicity]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_ReferenceMultiplicity] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_ReferenceMultiplicity]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_Centrality] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_Centrality]) && qa.fFillQACorrelationsVsHistograms2D; // ..... - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPhi] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPhi]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPt] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPt]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanEta] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanEta]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanCharge] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanCharge]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFindable] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFindable]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsShared] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsShared]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsChi2NCl] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsChi2NCl]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFound] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFound]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsCrossedRows] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsCrossedRows]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNCls] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNCls]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNClsInnerBarrel] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNClsInnerBarrel]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFoundOverFindableCls] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFoundOverFindableCls]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFractionSharedCls] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFractionSharedCls]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcChi2NCl] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcChi2NCl]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaXY] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaXY]) && qa.fFillQACorrelationsVsHistograms2D; - qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaZ] = Alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaZ]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPhi] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPhi]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPt] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanPt]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanEta] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanEta]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanCharge] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanCharge]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFindable] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFindable]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsShared] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsShared]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsChi2NCl] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsChi2NCl]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFound] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsFound]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsCrossedRows] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcNClsCrossedRows]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNCls] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNCls]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNClsInnerBarrel] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeanitsNClsInnerBarrel]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFoundOverFindableCls] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFoundOverFindableCls]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFractionSharedCls] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcFractionSharedCls]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcChi2NCl] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeantpcChi2NCl]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaXY] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaXY]) && qa.fFillQACorrelationsVsHistograms2D; + qa.fBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaZ] = alright(lBookQACorrelationsVsHistograms2D[eCorrelations_vs_MeandcaZ]) && qa.fFillQACorrelationsVsHistograms2D; // ..... // *) min and max harmonics for which this series of histograms will be booked: @@ -1629,17 +1641,17 @@ void defaultBooking() } // I append "&& qa.fFillQACorrelationsVsInteractionRateVsProfiles2D" below, to switch off booking of all 2D "correlations vs. IR vs. " profiles with one common flag: - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_CurrentRunDuration]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Multiplicity] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Multiplicity]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Centrality] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Centrality]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_CurrentRunDuration]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Multiplicity] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Multiplicity]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Centrality] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_Centrality]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; // ..... - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPhi] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPhi]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPt] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPt]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPt]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanEta] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanEta]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; - qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = Alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanEta]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPhi] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPhi]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPt] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanPt]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanPt]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanEta] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_MeanEta]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; + qa.fBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = alright(lBookQACorrelationsVsInteractionRateVsProfiles2D[eCorrelationsVsInteractionRate_vs_SigmaMeanEta]) && qa.fFillQACorrelationsVsInteractionRateVsProfiles2D; // ..... @@ -1655,7 +1667,7 @@ void defaultBooking() // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void defaultBooking() @@ -1680,7 +1692,7 @@ void defaultBinning() // e) Variable-length binning for results histograms set via MuPa-Configurables.h. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Default binning for event histograms: @@ -1809,9 +1821,9 @@ void defaultBinning() ph.fParticleHistogramsBins[edcaZ][1] = -10.; ph.fParticleHistogramsBins[edcaZ][2] = 10.; - ph.fParticleHistogramsBins[ePDG][0] = 2000; - ph.fParticleHistogramsBins[ePDG][1] = -1000.; - ph.fParticleHistogramsBins[ePDG][2] = 1000.; + ph.fParticleHistogramsBins[ePDG][0] = 2000; // o2-linter: disable=pdg/explicit-code (false positive, only array indices are hardwired here) + ph.fParticleHistogramsBins[ePDG][1] = -1000.; // o2-linter: disable=pdg/explicit-code (false positive, only array indices are hardwired here) + ph.fParticleHistogramsBins[ePDG][2] = 1000.; // o2-linter: disable=pdg/explicit-code (false positive, only array indices are hardwired here) // c) Default binning for particle histograms 2D: // At the moment, for fixed binning, I just re-use the binning of corresponding 1D histograms. @@ -1840,58 +1852,58 @@ void defaultBinning() // *) Binning vs. multiplicity: if (res.fUseResultsProVariableLengthBins[AFO_MULTIPLICITY]) { - this->InitializeVariableLengthBins(AFO_MULTIPLICITY); + this->initializeVariableLengthBins(AFO_MULTIPLICITY); } else { - this->InitializeFixedLengthBins(AFO_MULTIPLICITY); + this->initializeFixedLengthBins(AFO_MULTIPLICITY); } // *) Binning vs. centrality: if (res.fUseResultsProVariableLengthBins[AFO_CENTRALITY]) { - this->InitializeVariableLengthBins(AFO_CENTRALITY); + this->initializeVariableLengthBins(AFO_CENTRALITY); } else { - this->InitializeFixedLengthBins(AFO_CENTRALITY); + this->initializeFixedLengthBins(AFO_CENTRALITY); } // *) Binning vs. pt: if (res.fUseResultsProVariableLengthBins[AFO_PT]) { - this->InitializeVariableLengthBins(AFO_PT); + this->initializeVariableLengthBins(AFO_PT); } else { - this->InitializeFixedLengthBins(AFO_PT); + this->initializeFixedLengthBins(AFO_PT); } // *) Binning vs. eta: if (res.fUseResultsProVariableLengthBins[AFO_ETA]) { - this->InitializeVariableLengthBins(AFO_ETA); + this->initializeVariableLengthBins(AFO_ETA); } else { - this->InitializeFixedLengthBins(AFO_ETA); + this->initializeFixedLengthBins(AFO_ETA); } // *) Binning vs. occupancy: if (res.fUseResultsProVariableLengthBins[AFO_OCCUPANCY]) { - this->InitializeVariableLengthBins(AFO_OCCUPANCY); + this->initializeVariableLengthBins(AFO_OCCUPANCY); } else { - this->InitializeFixedLengthBins(AFO_OCCUPANCY); + this->initializeFixedLengthBins(AFO_OCCUPANCY); } // *) Binning vs. interaction rate: if (res.fUseResultsProVariableLengthBins[AFO_INTERACTIONRATE]) { - this->InitializeVariableLengthBins(AFO_INTERACTIONRATE); + this->initializeVariableLengthBins(AFO_INTERACTIONRATE); } else { - this->InitializeFixedLengthBins(AFO_INTERACTIONRATE); + this->initializeFixedLengthBins(AFO_INTERACTIONRATE); } // *) Binning vs. current run duration: if (res.fUseResultsProVariableLengthBins[AFO_CURRENTRUNDURATION]) { - this->InitializeVariableLengthBins(AFO_CURRENTRUNDURATION); + this->initializeVariableLengthBins(AFO_CURRENTRUNDURATION); } else { - this->InitializeFixedLengthBins(AFO_CURRENTRUNDURATION); + this->initializeFixedLengthBins(AFO_CURRENTRUNDURATION); } // *) Binning vs. vertex z position: if (res.fUseResultsProVariableLengthBins[AFO_VZ]) { - this->InitializeVariableLengthBins(AFO_VZ); + this->initializeVariableLengthBins(AFO_VZ); } else { - this->InitializeFixedLengthBins(AFO_VZ); + this->initializeFixedLengthBins(AFO_VZ); } // *) Binning vs. particle charge => binning is always the same nBins = 2 in (-2.,2), so that the center of bins is at +/- 1: @@ -1905,154 +1917,156 @@ void defaultBinning() // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void defaultBinning() //============================================================ -void InitializeFixedLengthBins(eAsFunctionOf AFO) +void initializeFixedLengthBins(EnAsFunctionOf afo) { // This is a helper function to suppress code bloat in defaultBinning(). // It merely initalizes res.fResultsProFixedLengthBins[...] from corresponding configurables + a few other minor thingies. // I do not have here AFO_INTEGRATED and AFO_CHARGE, because for them binning is always the same, i.e. no need for configurable. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Common local vector for all fixed-length bins: - std::vector lFixedLength_bins; + std::vector lFixedLengthBins; - switch (AFO) { + switch (afo) { case AFO_MULTIPLICITY: { - lFixedLength_bins = cf_res.cfFixedLengthMultBins.value; + lFixedLengthBins = cf_res.cfFixedLengthMultBins.value; break; } case AFO_CENTRALITY: { - lFixedLength_bins = cf_res.cfFixedLengthCentBins.value; + lFixedLengthBins = cf_res.cfFixedLengthCentBins.value; break; } case AFO_PT: { - lFixedLength_bins = cf_res.cfFixedLengthPtBins.value; + lFixedLengthBins = cf_res.cfFixedLengthPtBins.value; break; } case AFO_ETA: { - lFixedLength_bins = cf_res.cfFixedLengthEtaBins.value; + lFixedLengthBins = cf_res.cfFixedLengthEtaBins.value; break; } case AFO_OCCUPANCY: { - lFixedLength_bins = cf_res.cfFixedLengthOccuBins.value; + lFixedLengthBins = cf_res.cfFixedLengthOccuBins.value; break; } case AFO_INTERACTIONRATE: { - lFixedLength_bins = cf_res.cfFixedLengthIRBins.value; + lFixedLengthBins = cf_res.cfFixedLengthIRBins.value; break; } case AFO_CURRENTRUNDURATION: { - lFixedLength_bins = cf_res.cfFixedLengthCRDBins.value; + lFixedLengthBins = cf_res.cfFixedLengthCRDBins.value; break; } case AFO_VZ: { - lFixedLength_bins = cf_res.cfFixedLengthVzBins.value; + lFixedLengthBins = cf_res.cfFixedLengthVzBins.value; break; } // ... default: { - LOGF(fatal, "\033[1;31m%s at line %d : This enum AFO = %d is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(AFO)); + LOGF(fatal, "\033[1;31m%s at line %d : This enum afo = %d is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(afo)); break; } - } // switch(AFO) + } // switch(afo) - // From this point onward, the code is the same for any AFO variable: - if (lFixedLength_bins.size() != 3) { + // From this point onward, the code is the same for any afo variable: + uint8_t lFixedLengthBinsExpected = 3; // In this array, I expect three entries: nBins, min, max + TBI 20260714 I use uint8_t as a type to silent MegaLinter + if (lFixedLengthBins.size() != lFixedLengthBinsExpected) { LOGF(fatal, "in function \033[1;31m%s at line %d => The array cfFixedLength_bins must have have 3 entries: {nBins, min, max} \n \033[0m", __FUNCTION__, __LINE__); } - res.fResultsProBinEdges[AFO] = ArrayWithBinEdges(lFixedLength_bins[0], lFixedLength_bins[1], lFixedLength_bins[2]); + res.fResultsProBinEdges[afo] = arrayWithBinEdges(lFixedLengthBins[0], lFixedLengthBins[1], lFixedLengthBins[2]); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void InitializeFixedLengthBins(eAsFunctionOf AFO) +} // void initializeFixedLengthBins(EnAsFunctionOf afo) //============================================================ -void InitializeVariableLengthBins(eAsFunctionOf AFO) +void initializeVariableLengthBins(EnAsFunctionOf afo) { // This is a helper function to suppress code bloat in defaultBinning(). // It merely initalizes res.fResultsProVariableLengthBins[...] from corresponding configurables + a few other minor thingies. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Common local vector for all variable-length bins: - std::vector lVariableLength_bins; + std::vector lVariableLengthBins; - switch (AFO) { + switch (afo) { case AFO_MULTIPLICITY: { - lVariableLength_bins = cf_res.cfVariableLengthMultBins.value; + lVariableLengthBins = cf_res.cfVariableLengthMultBins.value; break; } case AFO_CENTRALITY: { - lVariableLength_bins = cf_res.cfVariableLengthCentBins.value; + lVariableLengthBins = cf_res.cfVariableLengthCentBins.value; break; } case AFO_PT: { - lVariableLength_bins = cf_res.cfVariableLengthPtBins.value; + lVariableLengthBins = cf_res.cfVariableLengthPtBins.value; break; } case AFO_ETA: { - lVariableLength_bins = cf_res.cfVariableLengthEtaBins.value; + lVariableLengthBins = cf_res.cfVariableLengthEtaBins.value; break; } case AFO_OCCUPANCY: { - lVariableLength_bins = cf_res.cfVariableLengthOccuBins.value; + lVariableLengthBins = cf_res.cfVariableLengthOccuBins.value; break; } case AFO_INTERACTIONRATE: { - lVariableLength_bins = cf_res.cfVariableLengthIRBins.value; + lVariableLengthBins = cf_res.cfVariableLengthIRBins.value; break; } case AFO_CURRENTRUNDURATION: { - lVariableLength_bins = cf_res.cfVariableLengthCRDBins.value; + lVariableLengthBins = cf_res.cfVariableLengthCRDBins.value; break; } case AFO_VZ: { - lVariableLength_bins = cf_res.cfVariableLengthVzBins.value; + lVariableLengthBins = cf_res.cfVariableLengthVzBins.value; break; } // ... default: { - LOGF(fatal, "\033[1;31m%s at line %d : This enum AFO = %d is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(AFO)); + LOGF(fatal, "\033[1;31m%s at line %d : This enum afo = %d is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(afo)); break; } - } // switch(AFO) + } // switch(afo) - // From this point onward, the code is the same for any AFO variable: - if (lVariableLength_bins.size() < 2) { + // From this point onward, the code is the same for any afo variable: + uint8_t lVariableLengthBinsMinExpected = 2; // In this array, I should have at least 2 entries, otherwise, it's pointless... + TBI 20260714 I use uint8_t as a type to silent MegaLinter + if (lVariableLengthBins.size() < lVariableLengthBinsMinExpected) { LOGF(fatal, "in function \033[1;31m%s at line %d => The array cfVariableLength_bins must have at least 2 entries \n \033[0m", __FUNCTION__, __LINE__); } - res.fResultsProBinEdges[AFO] = new TArrayD(lVariableLength_bins.size(), lVariableLength_bins.data()); + res.fResultsProBinEdges[afo] = new TArrayD(lVariableLengthBins.size(), lVariableLengthBins.data()); if (tc.fVerbose) { - LOGF(info, "\033[1;32m %s : variable-length %s bins \033[0m", __FUNCTION__, res.fResultsProXaxisTitle[AFO].Data()); - for (int i = 0; i < res.fResultsProBinEdges[AFO]->GetSize(); i++) { - LOGF(info, "\033[1;32m [%d] : %f \033[0m", i, res.fResultsProBinEdges[AFO]->GetAt(i)); + LOGF(info, "\033[1;32m %s : variable-length %s bins \033[0m", __FUNCTION__, res.fResultsProXaxisTitle[afo].Data()); + for (int i = 0; i < res.fResultsProBinEdges[afo]->GetSize(); i++) { + LOGF(info, "\033[1;32m [%d] : %f \033[0m", i, res.fResultsProBinEdges[afo]->GetAt(i)); } } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void InitializeVariableLengthBins(eAsFunctionOf AFO) +} // void initializeVariableLengthBins(EnAsFunctionOf afo) //============================================================ -void CastStringIntoArray(int AFO) +void castStringIntoArray(int afo) { // Temporary function, to be removed eventually. Here temporarily I am casting e.g. a string "1.0,2.0,5.0" into corresponding TArrayD. @@ -2060,26 +2074,24 @@ void CastStringIntoArray(int AFO) if (tc.fVerbose) { LOGF(info, "\033[1;32m%s\033[0m", __FUNCTION__); + LOGF(info, "\033[1;32m Casting a string %s into TArrayD .... \033[0m", res.fResultsProVariableLengthBinsString[afo].Data()); } - if (tc.fVerbose) { - LOGF(info, "\033[1;32m Casting a string %s into TArrayD .... \033[0m", res.fResultsProVariableLengthBinsString[AFO].Data()); - } - - TObjArray* oa = res.fResultsProVariableLengthBinsString[AFO].Tokenize(","); + TObjArray* oa = res.fResultsProVariableLengthBinsString[afo].Tokenize(","); if (!oa) { - LOGF(fatal, "in function \033[1;31m%s at line %d \n fResultsProVariableLengthBinsString[AFO] = %s\033[0m", __FUNCTION__, __LINE__, res.fResultsProVariableLengthBinsString[AFO].Data()); + LOGF(fatal, "in function \033[1;31m%s at line %d \n fResultsProVariableLengthBinsString[afo] = %s\033[0m", __FUNCTION__, __LINE__, res.fResultsProVariableLengthBinsString[afo].Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } int nEntries = oa->GetEntries(); - res.fResultsProVariableLengthBins[AFO] = new TArrayF(nEntries); + res.fResultsProVariableLengthBins[afo] = new TArrayF(nEntries); for (int i = 0; i < nEntries; i++) { - res.fResultsProVariableLengthBins[AFO]->AddAt(TString(oa->At(i)->GetName()).Atof(), i); + res.fResultsProVariableLengthBins[afo]->AddAt(TString(oa->At(i)->GetName()).Atof(), i); } delete oa; // yes, otherwise it's a memory leak if (tc.fVerbose) { - for (int i = 0; i < res.fResultsProVariableLengthBins[AFO]->GetSize(); i++) { - LOGF(info, "\033[1;32m [%d] : %f \033[0m", i, res.fResultsProVariableLengthBins[AFO]->At(i)); + for (int i = 0; i < res.fResultsProVariableLengthBins[afo]->GetSize(); i++) { + LOGF(info, "\033[1;32m [%d] : %f \033[0m", i, res.fResultsProVariableLengthBins[afo]->At(i)); } } @@ -2087,7 +2099,7 @@ void CastStringIntoArray(int AFO) LOGF(info, "\033[1;32m Done! \033[0m"); } -} // void CastStringIntoArray(int AFO) +} // void castStringIntoArray(int afo) //============================================================ @@ -2099,18 +2111,18 @@ void defaultCuts() // b) Default particle cuts. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Default event cuts: - // *) Use or do not use a cut enumerated in eEventHistograms + eEventCuts. + // *) Use or do not use a cut enumerated in EEventHistograms + eEventCuts. // Default cuts are set in configurable cfUseEventCuts auto lUseEventCuts = (std::vector)cf_ec.cfUseEventCuts; if (lUseEventCuts.size() != eEventCuts_N) { LOGF(info, "\033[1;31m lUseEventCuts.size() = %d\033[0m", lUseEventCuts.size()); LOGF(info, "\033[1;31m eEventCuts_N = %d\033[0m", static_cast(eEventCuts_N)); - LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfUseEventCuts, and number of entries in enum eEventHistograms + eEventCuts \n \033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d : Mismatch in the number of flags in configurable cfUseEventCuts, and number of entries in enum EEventHistograms + eEventCuts \n \033[0m", __FUNCTION__, __LINE__); } // *) Insanity check on the content and ordering of event cuts in the initialization in configurable cfUseEventCuts: @@ -2122,59 +2134,59 @@ void defaultCuts() } } - // *) from enum eEventHistograms: - ec.fUseEventCuts[eNumberOfEvents] = Alright(lUseEventCuts[eNumberOfEvents]); // total number of events (before event cuts) - ec.fUseEventCuts[eTotalMultiplicity] = Alright(lUseEventCuts[eTotalMultiplicity]); - ec.fUseEventCuts[eMultiplicity] = Alright(lUseEventCuts[eMultiplicity]); - ec.fUseEventCuts[eReferenceMultiplicity] = Alright(lUseEventCuts[eReferenceMultiplicity]); - ec.fUseEventCuts[eCentrality] = Alright(lUseEventCuts[eCentrality]); - ec.fUseEventCuts[eVertexX] = Alright(lUseEventCuts[eVertexX]); - ec.fUseEventCuts[eVertexY] = Alright(lUseEventCuts[eVertexY]); - ec.fUseEventCuts[eVertexZ] = Alright(lUseEventCuts[eVertexZ]); - ec.fUseEventCuts[eNContributors] = Alright(lUseEventCuts[eNContributors]); - ec.fUseEventCuts[eImpactParameter] = Alright(lUseEventCuts[eImpactParameter]); - ec.fUseEventCuts[eEventPlaneAngle] = Alright(lUseEventCuts[eEventPlaneAngle]); - ec.fUseEventCuts[eOccupancy] = Alright(lUseEventCuts[eOccupancy]); - ec.fUseEventCuts[eInteractionRate] = Alright(lUseEventCuts[eInteractionRate]); - ec.fUseEventCuts[eCurrentRunDuration] = Alright(lUseEventCuts[eCurrentRunDuration]); - ec.fUseEventCuts[eMultMCNParticlesEta08] = Alright(lUseEventCuts[eMultMCNParticlesEta08]); + // *) from enum EEventHistograms: + ec.fUseEventCuts[eNumberOfEvents] = alright(lUseEventCuts[eNumberOfEvents]); // total number of events (before event cuts) + ec.fUseEventCuts[eTotalMultiplicity] = alright(lUseEventCuts[eTotalMultiplicity]); + ec.fUseEventCuts[eMultiplicity] = alright(lUseEventCuts[eMultiplicity]); + ec.fUseEventCuts[eReferenceMultiplicity] = alright(lUseEventCuts[eReferenceMultiplicity]); + ec.fUseEventCuts[eCentrality] = alright(lUseEventCuts[eCentrality]); + ec.fUseEventCuts[eVertexX] = alright(lUseEventCuts[eVertexX]); + ec.fUseEventCuts[eVertexY] = alright(lUseEventCuts[eVertexY]); + ec.fUseEventCuts[eVertexZ] = alright(lUseEventCuts[eVertexZ]); + ec.fUseEventCuts[eNContributors] = alright(lUseEventCuts[eNContributors]); + ec.fUseEventCuts[eImpactParameter] = alright(lUseEventCuts[eImpactParameter]); + ec.fUseEventCuts[eEventPlaneAngle] = alright(lUseEventCuts[eEventPlaneAngle]); + ec.fUseEventCuts[eOccupancy] = alright(lUseEventCuts[eOccupancy]); + ec.fUseEventCuts[eInteractionRate] = alright(lUseEventCuts[eInteractionRate]); + ec.fUseEventCuts[eCurrentRunDuration] = alright(lUseEventCuts[eCurrentRunDuration]); + ec.fUseEventCuts[eMultMCNParticlesEta08] = alright(lUseEventCuts[eMultMCNParticlesEta08]); // *) from enum eEventCuts: - ec.fUseEventCuts[eTrigger] = Alright(lUseEventCuts[eTrigger]); - ec.fUseEventCuts[eSel7] = Alright(lUseEventCuts[eSel7]); - ec.fUseEventCuts[eSel8] = Alright(lUseEventCuts[eSel8]); - ec.fUseEventCuts[eMultiplicityEstimator] = Alright(lUseEventCuts[eMultiplicityEstimator]); - ec.fUseEventCuts[eCentralityEstimator] = Alright(lUseEventCuts[eCentralityEstimator]); - ec.fUseEventCuts[eReferenceMultiplicityEstimator] = Alright(lUseEventCuts[eReferenceMultiplicityEstimator]); - ec.fUseEventCuts[eSelectedEvents] = Alright(lUseEventCuts[eSelectedEvents]); // selected number of events (after all event cuts) - ec.fUseEventCuts[eNoSameBunchPileup] = Alright(lUseEventCuts[eNoSameBunchPileup]); - ec.fUseEventCuts[eIsGoodZvtxFT0vsPV] = Alright(lUseEventCuts[eIsGoodZvtxFT0vsPV]); - ec.fUseEventCuts[eIsVertexITSTPC] = Alright(lUseEventCuts[eIsVertexITSTPC]); - ec.fUseEventCuts[eIsVertexTOFmatched] = Alright(lUseEventCuts[eIsVertexTOFmatched]); - ec.fUseEventCuts[eIsVertexTRDmatched] = Alright(lUseEventCuts[eIsVertexTRDmatched]); - ec.fUseEventCuts[eNoCollInTimeRangeStrict] = Alright(lUseEventCuts[eNoCollInTimeRangeStrict]); - ec.fUseEventCuts[eNoCollInTimeRangeStandard] = Alright(lUseEventCuts[eNoCollInTimeRangeStandard]); - ec.fUseEventCuts[eNoCollInRofStrict] = Alright(lUseEventCuts[eNoCollInRofStrict]); - ec.fUseEventCuts[eNoCollInRofStandard] = Alright(lUseEventCuts[eNoCollInRofStandard]); - ec.fUseEventCuts[eNoHighMultCollInPrevRof] = Alright(lUseEventCuts[eNoHighMultCollInPrevRof]); - ec.fUseEventCuts[eIsGoodITSLayer3] = Alright(lUseEventCuts[eIsGoodITSLayer3]); - ec.fUseEventCuts[eIsGoodITSLayer0123] = Alright(lUseEventCuts[eIsGoodITSLayer0123]); - ec.fUseEventCuts[eIsGoodITSLayersAll] = Alright(lUseEventCuts[eIsGoodITSLayersAll]); - ec.fUseEventCuts[eOccupancyEstimator] = Alright(lUseEventCuts[eOccupancyEstimator]); - ec.fUseEventCuts[eMinVertexDistanceFromIP] = Alright(lUseEventCuts[eMinVertexDistanceFromIP]); - ec.fUseEventCuts[eNoPileupTPC] = Alright(lUseEventCuts[eNoPileupTPC]); - ec.fUseEventCuts[eNoPileupFromSPD] = Alright(lUseEventCuts[eNoPileupFromSPD]); - ec.fUseEventCuts[eNoSPDOnVsOfPileup] = Alright(lUseEventCuts[eNoSPDOnVsOfPileup]); - ec.fUseEventCuts[eRefMultVsNContrUp] = Alright(lUseEventCuts[eRefMultVsNContrUp]); - ec.fUseEventCuts[eRefMultVsNContrLow] = Alright(lUseEventCuts[eRefMultVsNContrLow]); - ec.fUseEventCuts[eCentralityCorrelationsCut] = Alright(lUseEventCuts[eCentralityCorrelationsCut]); - ec.fUseEventCuts[eFT0Bad] = Alright(lUseEventCuts[eFT0Bad]); - ec.fUseEventCuts[eITSBad] = Alright(lUseEventCuts[eITSBad]); - ec.fUseEventCuts[eITSLimAccMCRepr] = Alright(lUseEventCuts[eITSLimAccMCRepr]); - ec.fUseEventCuts[eTPCBadTracking] = Alright(lUseEventCuts[eTPCBadTracking]); - ec.fUseEventCuts[eTPCLimAccMCRepr] = Alright(lUseEventCuts[eTPCLimAccMCRepr]); - ec.fUseEventCuts[eTPCBadPID] = Alright(lUseEventCuts[eTPCBadPID]); - ec.fUseEventCuts[eCentralityWeights] = Alright(lUseEventCuts[eCentralityWeights]); + ec.fUseEventCuts[eTrigger] = alright(lUseEventCuts[eTrigger]); + ec.fUseEventCuts[eSel7] = alright(lUseEventCuts[eSel7]); + ec.fUseEventCuts[eSel8] = alright(lUseEventCuts[eSel8]); + ec.fUseEventCuts[eMultiplicityEstimator] = alright(lUseEventCuts[eMultiplicityEstimator]); + ec.fUseEventCuts[eCentralityEstimator] = alright(lUseEventCuts[eCentralityEstimator]); + ec.fUseEventCuts[eReferenceMultiplicityEstimator] = alright(lUseEventCuts[eReferenceMultiplicityEstimator]); + ec.fUseEventCuts[eSelectedEvents] = alright(lUseEventCuts[eSelectedEvents]); // selected number of events (after all event cuts) + ec.fUseEventCuts[eNoSameBunchPileup] = alright(lUseEventCuts[eNoSameBunchPileup]); + ec.fUseEventCuts[eIsGoodZvtxFT0vsPV] = alright(lUseEventCuts[eIsGoodZvtxFT0vsPV]); + ec.fUseEventCuts[eIsVertexITSTPC] = alright(lUseEventCuts[eIsVertexITSTPC]); + ec.fUseEventCuts[eIsVertexTOFmatched] = alright(lUseEventCuts[eIsVertexTOFmatched]); + ec.fUseEventCuts[eIsVertexTRDmatched] = alright(lUseEventCuts[eIsVertexTRDmatched]); + ec.fUseEventCuts[eNoCollInTimeRangeStrict] = alright(lUseEventCuts[eNoCollInTimeRangeStrict]); + ec.fUseEventCuts[eNoCollInTimeRangeStandard] = alright(lUseEventCuts[eNoCollInTimeRangeStandard]); + ec.fUseEventCuts[eNoCollInRofStrict] = alright(lUseEventCuts[eNoCollInRofStrict]); + ec.fUseEventCuts[eNoCollInRofStandard] = alright(lUseEventCuts[eNoCollInRofStandard]); + ec.fUseEventCuts[eNoHighMultCollInPrevRof] = alright(lUseEventCuts[eNoHighMultCollInPrevRof]); + ec.fUseEventCuts[eIsGoodITSLayer3] = alright(lUseEventCuts[eIsGoodITSLayer3]); + ec.fUseEventCuts[eIsGoodITSLayer0123] = alright(lUseEventCuts[eIsGoodITSLayer0123]); + ec.fUseEventCuts[eIsGoodITSLayersAll] = alright(lUseEventCuts[eIsGoodITSLayersAll]); + ec.fUseEventCuts[eOccupancyEstimator] = alright(lUseEventCuts[eOccupancyEstimator]); + ec.fUseEventCuts[eMinVertexDistanceFromIP] = alright(lUseEventCuts[eMinVertexDistanceFromIP]); + ec.fUseEventCuts[eNoPileupTPC] = alright(lUseEventCuts[eNoPileupTPC]); + ec.fUseEventCuts[eNoPileupFromSPD] = alright(lUseEventCuts[eNoPileupFromSPD]); + ec.fUseEventCuts[eNoSPDOnVsOfPileup] = alright(lUseEventCuts[eNoSPDOnVsOfPileup]); + ec.fUseEventCuts[eRefMultVsNContrUp] = alright(lUseEventCuts[eRefMultVsNContrUp]); + ec.fUseEventCuts[eRefMultVsNContrLow] = alright(lUseEventCuts[eRefMultVsNContrLow]); + ec.fUseEventCuts[eCentralityCorrelationsCut] = alright(lUseEventCuts[eCentralityCorrelationsCut]); + ec.fUseEventCuts[eFT0Bad] = alright(lUseEventCuts[eFT0Bad]); + ec.fUseEventCuts[eITSBad] = alright(lUseEventCuts[eITSBad]); + ec.fUseEventCuts[eITSLimAccMCRepr] = alright(lUseEventCuts[eITSLimAccMCRepr]); + ec.fUseEventCuts[eTPCBadTracking] = alright(lUseEventCuts[eTPCBadTracking]); + ec.fUseEventCuts[eTPCLimAccMCRepr] = alright(lUseEventCuts[eTPCLimAccMCRepr]); + ec.fUseEventCuts[eTPCBadPID] = alright(lUseEventCuts[eTPCBadPID]); + ec.fUseEventCuts[eCentralityWeights] = alright(lUseEventCuts[eCentralityWeights]); // **) event cuts defined via booleans: ec.fUseEventCuts[eSel7] = ec.fUseEventCuts[eSel7] && cf_ec.cfUseSel7; @@ -2314,33 +2326,33 @@ void defaultCuts() } // *) from enum eParticleHistograms: - pc.fUseParticleCuts[ePhi] = Alright(lUseParticleCuts[ePhi]); - pc.fUseParticleCuts[ePt] = Alright(lUseParticleCuts[ePt]); - pc.fUseParticleCuts[eEta] = Alright(lUseParticleCuts[eEta]); - pc.fUseParticleCuts[eCharge] = Alright(lUseParticleCuts[eCharge]); - pc.fUseParticleCuts[etpcNClsFindable] = Alright(lUseParticleCuts[etpcNClsFindable]); - pc.fUseParticleCuts[etpcNClsShared] = Alright(lUseParticleCuts[etpcNClsShared]); - pc.fUseParticleCuts[eitsChi2NCl] = Alright(lUseParticleCuts[eitsChi2NCl]); - pc.fUseParticleCuts[etpcNClsFound] = Alright(lUseParticleCuts[etpcNClsFound]); - pc.fUseParticleCuts[etpcNClsCrossedRows] = Alright(lUseParticleCuts[etpcNClsCrossedRows]); - pc.fUseParticleCuts[eitsNCls] = Alright(lUseParticleCuts[eitsNCls]); - pc.fUseParticleCuts[eitsNClsInnerBarrel] = Alright(lUseParticleCuts[eitsNClsInnerBarrel]); - pc.fUseParticleCuts[etpcCrossedRowsOverFindableCls] = Alright(lUseParticleCuts[etpcCrossedRowsOverFindableCls]); - pc.fUseParticleCuts[etpcFoundOverFindableCls] = Alright(lUseParticleCuts[etpcFoundOverFindableCls]); - pc.fUseParticleCuts[etpcFractionSharedCls] = Alright(lUseParticleCuts[etpcFractionSharedCls]); - pc.fUseParticleCuts[etpcChi2NCl] = Alright(lUseParticleCuts[etpcChi2NCl]); - pc.fUseParticleCuts[edcaXY] = Alright(lUseParticleCuts[edcaXY]); - pc.fUseParticleCuts[edcaZ] = Alright(lUseParticleCuts[edcaZ]); - pc.fUseParticleCuts[ePDG] = Alright(lUseParticleCuts[ePDG]); - pc.fUseParticleCuts[etrackCutFlag] = Alright(lUseParticleCuts[etrackCutFlag]); - pc.fUseParticleCuts[etrackCutFlagFb1] = Alright(lUseParticleCuts[etrackCutFlagFb1]); - pc.fUseParticleCuts[etrackCutFlagFb2] = Alright(lUseParticleCuts[etrackCutFlagFb2]); - pc.fUseParticleCuts[eisQualityTrack] = Alright(lUseParticleCuts[eisQualityTrack]); - pc.fUseParticleCuts[eisPrimaryTrack] = Alright(lUseParticleCuts[eisPrimaryTrack]); - pc.fUseParticleCuts[eisInAcceptanceTrack] = Alright(lUseParticleCuts[eisInAcceptanceTrack]); - pc.fUseParticleCuts[eisGlobalTrack] = Alright(lUseParticleCuts[eisGlobalTrack]); - pc.fUseParticleCuts[eisPVContributor] = Alright(lUseParticleCuts[eisPVContributor]); - pc.fUseParticleCuts[ePtDependentDCAxyParameterization] = Alright(lUseParticleCuts[ePtDependentDCAxyParameterization]); + pc.fUseParticleCuts[ePhi] = alright(lUseParticleCuts[ePhi]); + pc.fUseParticleCuts[ePt] = alright(lUseParticleCuts[ePt]); + pc.fUseParticleCuts[eEta] = alright(lUseParticleCuts[eEta]); + pc.fUseParticleCuts[eCharge] = alright(lUseParticleCuts[eCharge]); + pc.fUseParticleCuts[etpcNClsFindable] = alright(lUseParticleCuts[etpcNClsFindable]); + pc.fUseParticleCuts[etpcNClsShared] = alright(lUseParticleCuts[etpcNClsShared]); + pc.fUseParticleCuts[eitsChi2NCl] = alright(lUseParticleCuts[eitsChi2NCl]); + pc.fUseParticleCuts[etpcNClsFound] = alright(lUseParticleCuts[etpcNClsFound]); + pc.fUseParticleCuts[etpcNClsCrossedRows] = alright(lUseParticleCuts[etpcNClsCrossedRows]); + pc.fUseParticleCuts[eitsNCls] = alright(lUseParticleCuts[eitsNCls]); + pc.fUseParticleCuts[eitsNClsInnerBarrel] = alright(lUseParticleCuts[eitsNClsInnerBarrel]); + pc.fUseParticleCuts[etpcCrossedRowsOverFindableCls] = alright(lUseParticleCuts[etpcCrossedRowsOverFindableCls]); + pc.fUseParticleCuts[etpcFoundOverFindableCls] = alright(lUseParticleCuts[etpcFoundOverFindableCls]); + pc.fUseParticleCuts[etpcFractionSharedCls] = alright(lUseParticleCuts[etpcFractionSharedCls]); + pc.fUseParticleCuts[etpcChi2NCl] = alright(lUseParticleCuts[etpcChi2NCl]); + pc.fUseParticleCuts[edcaXY] = alright(lUseParticleCuts[edcaXY]); + pc.fUseParticleCuts[edcaZ] = alright(lUseParticleCuts[edcaZ]); + pc.fUseParticleCuts[ePDG] = alright(lUseParticleCuts[ePDG]); + pc.fUseParticleCuts[etrackCutFlag] = alright(lUseParticleCuts[etrackCutFlag]); + pc.fUseParticleCuts[etrackCutFlagFb1] = alright(lUseParticleCuts[etrackCutFlagFb1]); + pc.fUseParticleCuts[etrackCutFlagFb2] = alright(lUseParticleCuts[etrackCutFlagFb2]); + pc.fUseParticleCuts[eisQualityTrack] = alright(lUseParticleCuts[eisQualityTrack]); + pc.fUseParticleCuts[eisPrimaryTrack] = alright(lUseParticleCuts[eisPrimaryTrack]); + pc.fUseParticleCuts[eisInAcceptanceTrack] = alright(lUseParticleCuts[eisInAcceptanceTrack]); + pc.fUseParticleCuts[eisGlobalTrack] = alright(lUseParticleCuts[eisGlobalTrack]); + pc.fUseParticleCuts[eisPVContributor] = alright(lUseParticleCuts[eisPVContributor]); + pc.fUseParticleCuts[ePtDependentDCAxyParameterization] = alright(lUseParticleCuts[ePtDependentDCAxyParameterization]); // **) particles cuts defined via booleans: pc.fUseParticleCuts[etrackCutFlag] = pc.fUseParticleCuts[etrackCutFlag] && cf_pc.cftrackCutFlag; @@ -2429,7 +2441,7 @@ void defaultCuts() pc.fsParticleCuts[ePtDependentDCAxyParameterization] = cf_pc.cfPtDependentDCAxyParameterization; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void defaultCuts() @@ -2448,12 +2460,12 @@ void specificCuts(TString whichSpecificCuts) // b) Implementation of analysis-specific cuts. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Mapping: // I need this mapping, for the switch statement below. TBI 20241120 I could introduce a utility function for this as well... - eSpecificCuts specificCuts = eSpecificCuts_N; + EnSpecificCuts specificCuts = eSpecificCuts_N; if (whichSpecificCuts.EqualTo("LHC23zzh")) { specificCuts = eLHC23zzh; } else if (whichSpecificCuts.EqualTo("LHC24ar")) { @@ -2804,7 +2816,7 @@ void specificCuts(TString whichSpecificCuts) } // switch (specificCuts) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void specificCuts(const char* specificCutsName) @@ -2841,7 +2853,7 @@ void insanityChecksOnDefinitionsOfConfigurables() // o) Results histograms. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Task configuration: @@ -2896,7 +2908,7 @@ void insanityChecksOnDefinitionsOfConfigurables() // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // insanityChecksOnDefinitionsOfConfigurables() @@ -2919,7 +2931,7 @@ void insanityChecksBeforeBooking() // h) Insanity checks on results histograms. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Insanity checks on configuration: @@ -2962,7 +2974,7 @@ void insanityChecksBeforeBooking() // **) Enforce that if the fixed number of randomly selected tracks is used that Toy NUA is disabled: if (tc.fFixedNumberOfRandomlySelectedTracks > 0 && (nua.fApplyNUAPDF[ePhiNUAPDF] || nua.fApplyNUAPDF[ePtNUAPDF] || nua.fApplyNUAPDF[eEtaNUAPDF] || nua.fApplyNUAPDF[eChargeNUAPDF])) { - LOGF(fatal, "\033[1;31m%s at line %d : Not supported at the moment: use FixedNumberOfRandomlySelectedTracks + Toy NUA enabled.\nI cannot in an easy way ensure that ParticleCuts behave exactly the same in the Main and Banishment loops, because e.g. I call consequtively for same partcile gRandom->Uniform(...) in ParticleCuts, and that can't work.\033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d : Not supported at the moment: use FixedNumberOfRandomlySelectedTracks + Toy NUA enabled.\nI cannot in an easy way ensure that particleCuts behave exactly the same in the Main and Banishment loops, because e.g. I call consequtively for same partcile gRandom->Uniform(...) in particleCuts, and that can't work.\033[0m", __FUNCTION__, __LINE__); } // **) When it comes to DCAxy cut, ensure that either flat or pt-dependent cut is used, but not both: @@ -2973,18 +2985,18 @@ void insanityChecksBeforeBooking() // **) Insanity check on individual flags: Make sure that only one process is set to true. // If 2 or more are true, then corresponding process function is executed over ALL data, then another process(...) function, etc. // Re-think this if it's possible to run different process(...)'s concurently over the same data. - if (static_cast(tc.fProcess[eProcessRec]) + static_cast(tc.fProcess[eProcessRecSim]) + static_cast(tc.fProcess[eProcessSim]) + static_cast(tc.fProcess[eProcessRec_Run2]) + static_cast(tc.fProcess[eProcessRecSim_Run2]) + static_cast(tc.fProcess[eProcessSim_Run2]) + static_cast(tc.fProcess[eProcessRec_Run1]) + static_cast(tc.fProcess[eProcessRecSim_Run1]) + static_cast(tc.fProcess[eProcessSim_Run1]) > 1) { - LOGF(info, "\033[1;31m Only one flag can be true: tc.fProcess[eProcessRec] = %d, tc.fProcess[eProcessRecSim] = %d, tc.fProcess[eProcessSim] = %d, tc.fProcess[eProcessRec_Run2] = %d, tc.fProcess[eProcessRecSim_Run2] = %d, tc.fProcess[eProcessSim_Run2] = %d, tc.fProcess[eProcessRec_Run1] = %d, tc.fProcess[eProcessRecSim_Run1] = %d, tc.fProcess[eProcessSim_Run1] = %d \033[0m", static_cast(tc.fProcess[eProcessRec]), static_cast(tc.fProcess[eProcessRecSim]), static_cast(tc.fProcess[eProcessSim]), static_cast(tc.fProcess[eProcessRec_Run2]), static_cast(tc.fProcess[eProcessRecSim_Run2]), static_cast(tc.fProcess[eProcessSim_Run2]), static_cast(tc.fProcess[eProcessRec_Run1]), static_cast(tc.fProcess[eProcessRecSim_Run1]), static_cast(tc.fProcess[eProcessSim_Run1])); + if (static_cast(tc.fProcess[eProcessRec]) + static_cast(tc.fProcess[eProcessRecSim]) + static_cast(tc.fProcess[eProcessSim]) + static_cast(tc.fProcess[eProcessRecRun2]) + static_cast(tc.fProcess[eProcessRecSimRun2]) + static_cast(tc.fProcess[eProcessSimRun2]) + static_cast(tc.fProcess[eProcessRecRun1]) + static_cast(tc.fProcess[eProcessRecSimRun1]) + static_cast(tc.fProcess[eProcessSimRun1]) > 1) { + LOGF(info, "\033[1;31m Only one flag can be true: tc.fProcess[eProcessRec] = %d, tc.fProcess[eProcessRecSim] = %d, tc.fProcess[eProcessSim] = %d, tc.fProcess[eProcessRecRun2] = %d, tc.fProcess[eProcessRecSimRun2] = %d, tc.fProcess[eProcessSimRun2] = %d, tc.fProcess[eProcessRecRun1] = %d, tc.fProcess[eProcessRecSimRun1] = %d, tc.fProcess[eProcessSimRun1] = %d \033[0m", static_cast(tc.fProcess[eProcessRec]), static_cast(tc.fProcess[eProcessRecSim]), static_cast(tc.fProcess[eProcessSim]), static_cast(tc.fProcess[eProcessRecRun2]), static_cast(tc.fProcess[eProcessRecSimRun2]), static_cast(tc.fProcess[eProcessSimRun2]), static_cast(tc.fProcess[eProcessRecRun1]), static_cast(tc.fProcess[eProcessRecSimRun1]), static_cast(tc.fProcess[eProcessSimRun1])); LOGF(fatal, "in function \033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } // **) Insanity checks on event cuts: // **) This check is meant to prevent the case when I want to bailout for max number of events, but I do not fill event histograms: - if (ec.fdEventCuts[eNumberOfEvents][eMax] < 1e6) { // TBI 20241011 Do I need to tune 1000000000 - // If I do not want to bail out when max number of events is reached, then in the configurable I have e.g. cfNumberOfEvents{"cfNumberOfEvents", {-1, 1000000000} - // So if the upper limit is set to some number < 1e6, I want to bail out for that number of events. - // TBI 20241011 this is a bit shaky, but nevermind now... + // Remark: If I do not want to bail out when max number of events is reached, then in the configurable I have to set e.g. cfNumberOfEvents{"cfNumberOfEvents", {-1, 1000000000} + // So if the upper limit is set to some number < 1e9, I want to bail out for that number of events. + int lMaxNumberOfEvents = 1e9; + if (ec.fdEventCuts[eNumberOfEvents][eMax] < lMaxNumberOfEvents) { if (!eh.fBookEventHistograms[eNumberOfEvents]) { LOGF(fatal, "\033[1;31m%s at line %d : Bailout for max number of events cannot be done, unless eh.fBookEventHistograms[eNumberOfEvents] is true.\033[0m", __FUNCTION__, __LINE__); } @@ -3001,7 +3013,7 @@ void insanityChecksBeforeBooking() } // Validated and supported Run 2 triggers: - if (tc.fProcess[eProcessRec_Run2]) { + if (tc.fProcess[eProcessRecRun2]) { if (!ec.fsEventCuts[eTrigger].EqualTo("kINT7")) { // LOGF(fatal, "\033[1;31m%s at line %d : trigger \"%s\" is not internally validated/supported yet for Run 2. Add it to the list of supported triggers, if you really want to use that one.\033[0m", __FUNCTION__, __LINE__, ec.fsEventCuts[eTrigger].Data()); } @@ -3020,7 +3032,7 @@ void insanityChecksBeforeBooking() } // **) Enforce the usage of particular trigger for this dataset: - // if (tc.fProcess[eProcessRec_Run2]) { + // if (tc.fProcess[eProcessRecRun2]) { // TBI 20250115 Not really sure I need this - if I want to run only "Rec" over Monte Carlo, then obviously the condition below is pointless. // Also here I need to be able automaticaly to determine whether I am processing real data or Monte Carlo, from the dataset itself. // TBI 20240517 for the time being, here I am enforcing that "kINT7" is mandatory for Run 2 @@ -3071,22 +3083,22 @@ void insanityChecksBeforeBooking() // b) Ensure that Run 1/2 specific cuts and flags are used only in Run 1/2 (both data and sim): // **) Ensure that eSel7 is used only for converted Run 2 and Run 1 (both data and sim): if (ec.fUseEventCuts[eSel7]) { - if (!(tc.fProcess[eProcessRec_Run2] || tc.fProcess[eProcessRecSim_Run2] || tc.fProcess[eProcessSim_Run2] || tc.fProcess[eProcessRec_Run1] || tc.fProcess[eProcessRecSim_Run1] || tc.fProcess[eProcessSim_Run1])) { + if (!(tc.fProcess[eProcessRecRun2] || tc.fProcess[eProcessRecSimRun2] || tc.fProcess[eProcessSimRun2] || tc.fProcess[eProcessRecRun1] || tc.fProcess[eProcessRecSimRun1] || tc.fProcess[eProcessSimRun1])) { LOGF(fatal, "\033[1;31m%s at line %d : use fSel7 for Run 2 data and MC\033[0m", __FUNCTION__, __LINE__); } } // **) Supported reference multiplicity estimators for Run 1 and 2 are enlisted here: - if (tc.fProcess[eProcessRec_Run2] || tc.fProcess[eProcessRecSim_Run2] || tc.fProcess[eProcessRec_Run1] || tc.fProcess[eProcessRecSim_Run1]) { + if (tc.fProcess[eProcessRecRun2] || tc.fProcess[eProcessRecSimRun2] || tc.fProcess[eProcessRecRun1] || tc.fProcess[eProcessRecSimRun1]) { if (!(ec.fsEventCuts[eReferenceMultiplicityEstimator].EqualTo("MultTracklets", TString::kIgnoreCase))) { LOGF(fatal, "\033[1;31m%s at line %d : reference multiplicity estimator = %s is not supported for Run 1 and 2 analysis.\nUse \"MultTracklets\"\033[0m", __FUNCTION__, __LINE__, ec.fsEventCuts[eReferenceMultiplicityEstimator].Data()); } - } else if (tc.fProcess[eProcessSim_Run2] || tc.fProcess[eProcessSim_Run1]) { + } else if (tc.fProcess[eProcessSimRun2] || tc.fProcess[eProcessSimRun1]) { LOGF(fatal, "\033[1;31m%s at line %d : eProcessSim is not validated yet \033[0m", __FUNCTION__, __LINE__); } // **) Supported centrality estimators for Run 1 and 2 are enlisted here: - if (tc.fProcess[eProcessRec_Run2] || tc.fProcess[eProcessRecSim_Run2] || tc.fProcess[eProcessSim_Run2] || tc.fProcess[eProcessRec_Run1] || tc.fProcess[eProcessRecSim_Run1] || tc.fProcess[eProcessSim_Run1]) { + if (tc.fProcess[eProcessRecRun2] || tc.fProcess[eProcessRecSimRun2] || tc.fProcess[eProcessSimRun2] || tc.fProcess[eProcessRecRun1] || tc.fProcess[eProcessRecSimRun1] || tc.fProcess[eProcessSimRun1]) { if (!(ec.fsEventCuts[eCentralityEstimator].EqualTo("centRun2V0M", TString::kIgnoreCase) || ec.fsEventCuts[eCentralityEstimator].EqualTo("centRun2SPDTracklets", TString::kIgnoreCase))) { LOGF(fatal, "\033[1;31m%s at line %d : centrality estimator = %s is not supported for converted Run 2 and Run 1 analysis.\nUse either \"centRun2V0M\" or \"centRun2SPDTracklets\" (case sensitive!) \033[0m", __FUNCTION__, __LINE__, ec.fsEventCuts[eCentralityEstimator].Data()); @@ -3094,7 +3106,7 @@ void insanityChecksBeforeBooking() } // **) Protection against particle cuts which are available, but not yet validated, or are meaningless, in Run 2 and 1: - if (tc.fProcess[eProcessRec_Run2] || tc.fProcess[eProcessRecSim_Run2] || tc.fProcess[eProcessSim_Run2] || tc.fProcess[eProcessRec_Run1] || tc.fProcess[eProcessRecSim_Run1] || tc.fProcess[eProcessSim_Run1]) { + if (tc.fProcess[eProcessRecRun2] || tc.fProcess[eProcessRecSimRun2] || tc.fProcess[eProcessSimRun2] || tc.fProcess[eProcessRecRun1] || tc.fProcess[eProcessRecSimRun1] || tc.fProcess[eProcessSimRun1]) { if (pc.fUseParticleCuts[etrackCutFlag]) { LOGF(fatal, "\033[1;31m%s at line %d : particle cut etrackCutFlag is not validated, as of 20250113 it has no effect in Run 2 and Run 1 \033[0m", __FUNCTION__, __LINE__); } @@ -3109,7 +3121,7 @@ void insanityChecksBeforeBooking() // **) Print a warning if kINT7 trigger is not used in reconstructed Run 2: // TBI 20250318 shall I expand the check also to Run 1? In 2011 there were dedicated kCentral and kSemiCentral triggers only... // TBI 20250318 shall I make it fatal instead? Without this trigger, a lot of histos are just meaningles (e.g. nContributores vs centrality, etc.) - if (tc.fProcess[eProcessRec_Run2]) { + if (tc.fProcess[eProcessRecRun2]) { if (!(ec.fUseEventCuts[eTrigger] && ec.fsEventCuts[eTrigger].EqualTo("kINT7"))) { LOGF(warning, "\033[1;31m%s at line %d : kINT7 trigger in Run 2 is not selected - by default it should be used.\033[0m", __FUNCTION__, __LINE__); } @@ -3117,7 +3129,7 @@ void insanityChecksBeforeBooking() // **) Bail out if kINT7 trigger is used in Run 2 Monte Carlo: // TBI 20250318 shall I expand the check also to Run 1? In 2011 there were dedicated kCentral and kSemiCentral triggers only... - if (tc.fProcess[eProcessRecSim_Run2] || tc.fProcess[eProcessSim_Run2]) { + if (tc.fProcess[eProcessRecSimRun2] || tc.fProcess[eProcessSimRun2]) { if (ec.fUseEventCuts[eTrigger] && ec.fsEventCuts[eTrigger].EqualTo("kINT7")) { LOGF(fatal, "\033[1;31m%s at line %d : kINT7 trigger in Run 2 Monte Carlo is not validated - use it at your own peril.\033[0m", __FUNCTION__, __LINE__); } @@ -3285,7 +3297,8 @@ void insanityChecksBeforeBooking() // **) Protection on particle cuts which can be used only in Run 3: // trackCutFlag, trackCutFlagFb1, trackCutFlagFb2 => use only one at the time - if (static_cast(pc.fUseParticleCuts[etrackCutFlag]) + static_cast(pc.fUseParticleCuts[etrackCutFlagFb1]) + static_cast(pc.fUseParticleCuts[etrackCutFlagFb2]) >= 2) { + int trackCutFlagMaxUsed = 1; + if (static_cast(pc.fUseParticleCuts[etrackCutFlag]) + static_cast(pc.fUseParticleCuts[etrackCutFlagFb1]) + static_cast(pc.fUseParticleCuts[etrackCutFlagFb2]) > trackCutFlagMaxUsed) { LOGF(fatal, "\033[1;31m%s at line %d : use only one of trackCutFlag, trackCutFlagFb1, trackCutFlagFb2 at time. \033[0m", __FUNCTION__, __LINE__); } @@ -3343,10 +3356,6 @@ void insanityChecksBeforeBooking() LOGF(fatal, "\033[1;31m%s at line %d : rescaling with theoretical input is not supported when cross-check is done with nested loops. \033[0m", __FUNCTION__, __LINE__); } - if (ec.fsEventCuts[eMultiplicityEstimator].EqualTo("ReferenceMultiplicity", TString::kIgnoreCase)) { - LOGF(fatal, "\033[1;31m%s at line %d : in IV eMultiplicityEstimator cannot be set to \"ReferenceMultiplicity\" (yet) \033[0m", __FUNCTION__, __LINE__); - } - if (!tc.fCalculateAsFunctionOf[AFO_PT] && pc.fUseParticleCuts[ePt]) { LOGF(fatal, "\033[1;31m%s at line %d : in IV you do not calculate vs pt, but the cut on pt is on in IV\033[0m", __FUNCTION__, __LINE__); } @@ -3376,7 +3385,7 @@ void insanityChecksBeforeBooking() // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void insanityChecksBeforeBooking() @@ -3394,7 +3403,7 @@ void insanityChecksAfterBooking() // ... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Insanity checks on booking: @@ -3458,7 +3467,7 @@ void insanityChecksAfterBooking() // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void insanityChecksAfterBooking() @@ -3475,10 +3484,10 @@ void purgeAfterBooking() // ... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } - return; // TBI 20250625 the code below is not ready yet, because I still use these 2D and 3D histos in FillqvectorNdim(...) + return; // TBI 20250625 the code below is not ready yet, because I still use these 2D and 3D histos in fillqvectorNdim(...) // a) Purge results histograms and related objects: if (!res.fSaveResultsHistograms) { @@ -3498,20 +3507,20 @@ void purgeAfterBooking() } // if(!res.fSaveResultsHistograms) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void purgeAfterBooking() //============================================================ -bool Skip(int recOrSim) +bool skip(int recOrSim) { // Decide here whether a certain histogram, etc., will be booked and used both for eRec and eSim. // Same for cuts. if (tc.fVerboseUtility) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Insanity check: @@ -3536,7 +3545,7 @@ bool Skip(int recOrSim) return false; // by default, I do not skip anything -} // bool Skip(int recOrSim) +} // bool skip(int recOrSim) //============================================================ @@ -3562,7 +3571,7 @@ void bookAndNestAllLists() // *) Results. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) QA: @@ -3696,7 +3705,7 @@ void bookAndNestAllLists() fBaseList->Add(res.fResultsList); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookAndNestAllLists() @@ -3718,7 +3727,8 @@ void bookQAHistograms() // g) Book specific QA 2D "correlations vs. IR vs. " profiles. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); + LOGF(info, "\033[1;33m%s: !!!! WARNING !!!! With too many 2D histograms with double precision, the code will crash in terminate (\"... shmem: could not create a message of size ...\") . Locally, you can circumvent this while testing by calling Bailout() explicitly. !!!! WARNING !!!! \033[0m", __FUNCTION__); } // *) Print the warning message, because with too many 2D histograms with double precision, the code crashes in terminate, due to: @@ -3730,10 +3740,6 @@ void bookQAHistograms() // [1450742:multiparticle-correlations-a-b]: Backtrace by DPL: // - if (tc.fVerbose) { - LOGF(info, "\033[1;33m%s: !!!! WARNING !!!! With too many 2D histograms with double precision, the code will crash in terminate (\"... shmem: could not create a message of size ...\") . Locally, you can circumvent this while testing by calling Bailout() explicitly. !!!! WARNING !!!! \033[0m", __FUNCTION__); - } - // a) Book the profile holding flags: qa.fQAHistogramsPro = new TProfile("fQAHistogramsPro", "flags for QA histograms", 7, 0., 7.); qa.fQAHistogramsPro->SetStats(false); @@ -3807,375 +3813,375 @@ void bookQAHistograms() // c) Book specific QA 2D event histograms: // Binning of 2D event histos: TBI 20240503 see if you can automate all this - int nBins_x_Event[eQAEventHistograms2D_N] = {0}; - double min_x_Event[eQAEventHistograms2D_N] = {0.}; - double max_x_Event[eQAEventHistograms2D_N] = {0.}; - TString title_x_Event[eQAEventHistograms2D_N] = {""}; - int nBins_y_Event[eQAEventHistograms2D_N] = {0}; - double min_y_Event[eQAEventHistograms2D_N] = {0.}; - double max_y_Event[eQAEventHistograms2D_N] = {0.}; - TString title_y_Event[eQAEventHistograms2D_N] = {""}; + int nBinsXEvent[eQAEventHistograms2D_N] = {0}; + double minXEvent[eQAEventHistograms2D_N] = {0.}; + double maxXEvent[eQAEventHistograms2D_N] = {0.}; + TString titleXEvent[eQAEventHistograms2D_N] = {""}; + int nBinsYEvent[eQAEventHistograms2D_N] = {0}; + double minYEvent[eQAEventHistograms2D_N] = {0.}; + double maxYEvent[eQAEventHistograms2D_N] = {0.}; + TString titleYEvent[eQAEventHistograms2D_N] = {""}; // *) "Multiplicity_vs_ReferenceMultiplicity": - nBins_x_Event[eMultiplicity_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_x_Event[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultiplicity_vs_ReferenceMultiplicity] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); - nBins_y_Event[eMultiplicity_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); - min_y_Event[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_y_Event[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_y_Event[eMultiplicity_vs_ReferenceMultiplicity] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsXEvent[eMultiplicity_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minXEvent[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultiplicity_vs_ReferenceMultiplicity] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYEvent[eMultiplicity_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); + minYEvent[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxYEvent[eMultiplicity_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleYEvent[eMultiplicity_vs_ReferenceMultiplicity] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); // *) "Multiplicity_vs_NContributors": - nBins_x_Event[eMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_x_Event[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultiplicity_vs_NContributors] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); - nBins_y_Event[eMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); - min_y_Event[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][1]; - max_y_Event[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][2]; - title_y_Event[eMultiplicity_vs_NContributors] = FancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); + nBinsXEvent[eMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minXEvent[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultiplicity_vs_NContributors] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYEvent[eMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); + minYEvent[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][1]; + maxYEvent[eMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][2]; + titleYEvent[eMultiplicity_vs_NContributors] = fancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); // *) "Multiplicity_vs_Centrality": - nBins_x_Event[eMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_x_Event[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultiplicity_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); - nBins_y_Event[eMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eMultiplicity_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsXEvent[eMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minXEvent[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultiplicity_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYEvent[eMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eMultiplicity_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); // *) "Multiplicity_vs_VertexZ": - nBins_x_Event[eMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_x_Event[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultiplicity_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); - nBins_y_Event[eMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); - min_y_Event[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; - max_y_Event[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; - title_y_Event[eMultiplicity_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); + nBinsXEvent[eMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minXEvent[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultiplicity_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYEvent[eMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); + minYEvent[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; + maxYEvent[eMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; + titleYEvent[eMultiplicity_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); // *) "Multiplicity_vs_Occupancy": - nBins_x_Event[eMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_x_Event[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultiplicity_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); - nBins_y_Event[eMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); - min_y_Event[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; - max_y_Event[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; - title_y_Event[eMultiplicity_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); + nBinsXEvent[eMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minXEvent[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultiplicity_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYEvent[eMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); + minYEvent[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; + maxYEvent[eMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; + titleYEvent[eMultiplicity_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); // *) "Multiplicity_vs_InteractionRate": - nBins_x_Event[eMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_x_Event[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultiplicity_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); - nBins_y_Event[eMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); - min_y_Event[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; - max_y_Event[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; - title_y_Event[eMultiplicity_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); + nBinsXEvent[eMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minXEvent[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultiplicity_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYEvent[eMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); + minYEvent[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; + maxYEvent[eMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; + titleYEvent[eMultiplicity_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); // *) "ReferenceMultiplicity_vs_NContributors": // TBI 20250401 I use this one to calculate quantiles for HMO cut, therefore I keep it refined for the time being - nBins_x_Event[eReferenceMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0]); - min_x_Event[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_x_Event[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_x_Event[eReferenceMultiplicity_vs_NContributors] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); - nBins_y_Event[eReferenceMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eNContributors][0]); - min_y_Event[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][1]; - max_y_Event[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][2]; - title_y_Event[eReferenceMultiplicity_vs_NContributors] = FancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); + nBinsXEvent[eReferenceMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0]); + minXEvent[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxXEvent[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleXEvent[eReferenceMultiplicity_vs_NContributors] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsYEvent[eReferenceMultiplicity_vs_NContributors] = static_cast(eh.fEventHistogramsBins[eNContributors][0]); + minYEvent[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][1]; + maxYEvent[eReferenceMultiplicity_vs_NContributors] = eh.fEventHistogramsBins[eNContributors][2]; + titleYEvent[eReferenceMultiplicity_vs_NContributors] = fancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); // *) "ReferenceMultiplicity_vs_Centrality": - nBins_x_Event[eReferenceMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); - min_x_Event[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_x_Event[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_x_Event[eReferenceMultiplicity_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); - nBins_y_Event[eReferenceMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eReferenceMultiplicity_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsXEvent[eReferenceMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); + minXEvent[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxXEvent[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleXEvent[eReferenceMultiplicity_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsYEvent[eReferenceMultiplicity_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eReferenceMultiplicity_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eReferenceMultiplicity_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); // *) "ReferenceMultiplicity_vs_VertexZ": - nBins_x_Event[eReferenceMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); - min_x_Event[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_x_Event[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_x_Event[eReferenceMultiplicity_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); - nBins_y_Event[eReferenceMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); - min_y_Event[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; - max_y_Event[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; - title_y_Event[eReferenceMultiplicity_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); + nBinsXEvent[eReferenceMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); + minXEvent[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxXEvent[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleXEvent[eReferenceMultiplicity_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsYEvent[eReferenceMultiplicity_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); + minYEvent[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; + maxYEvent[eReferenceMultiplicity_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; + titleYEvent[eReferenceMultiplicity_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); // *) "ReferenceMultiplicity_vs_Occupancy": - nBins_x_Event[eReferenceMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); - min_x_Event[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_x_Event[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_x_Event[eReferenceMultiplicity_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); - nBins_y_Event[eReferenceMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); - min_y_Event[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; - max_y_Event[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; - title_y_Event[eReferenceMultiplicity_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); + nBinsXEvent[eReferenceMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); + minXEvent[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxXEvent[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleXEvent[eReferenceMultiplicity_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsYEvent[eReferenceMultiplicity_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); + minYEvent[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; + maxYEvent[eReferenceMultiplicity_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; + titleYEvent[eReferenceMultiplicity_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); // *) "ReferenceMultiplicity_vs_InteractionRate": - nBins_x_Event[eReferenceMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); - min_x_Event[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_x_Event[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_x_Event[eReferenceMultiplicity_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); - nBins_y_Event[eReferenceMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); - min_y_Event[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; - max_y_Event[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; - title_y_Event[eReferenceMultiplicity_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); + nBinsXEvent[eReferenceMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); + minXEvent[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxXEvent[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleXEvent[eReferenceMultiplicity_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsYEvent[eReferenceMultiplicity_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); + minYEvent[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; + maxYEvent[eReferenceMultiplicity_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; + titleYEvent[eReferenceMultiplicity_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); // *) "NContributors_vs_Centrality": - nBins_x_Event[eNContributors_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); - min_x_Event[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eNContributors][1]; - max_x_Event[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eNContributors][2]; - title_x_Event[eNContributors_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); - nBins_y_Event[eNContributors_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eNContributors_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsXEvent[eNContributors_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); + minXEvent[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eNContributors][1]; + maxXEvent[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eNContributors][2]; + titleXEvent[eNContributors_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); + nBinsYEvent[eNContributors_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eNContributors_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eNContributors_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); // *) "NContributors_vs_VertexZ": - nBins_x_Event[eNContributors_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); - min_x_Event[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eNContributors][1]; - max_x_Event[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eNContributors][2]; - title_x_Event[eNContributors_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); - nBins_y_Event[eNContributors_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); - min_y_Event[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; - max_y_Event[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; - title_y_Event[eNContributors_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); + nBinsXEvent[eNContributors_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); + minXEvent[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eNContributors][1]; + maxXEvent[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eNContributors][2]; + titleXEvent[eNContributors_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); + nBinsYEvent[eNContributors_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); + minYEvent[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; + maxYEvent[eNContributors_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; + titleYEvent[eNContributors_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); // *) "NContributors_vs_Occupancy": - nBins_x_Event[eNContributors_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); - min_x_Event[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eNContributors][1]; - max_x_Event[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eNContributors][2]; - title_x_Event[eNContributors_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); - nBins_y_Event[eNContributors_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); - min_y_Event[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; - max_y_Event[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; - title_y_Event[eNContributors_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); + nBinsXEvent[eNContributors_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); + minXEvent[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eNContributors][1]; + maxXEvent[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eNContributors][2]; + titleXEvent[eNContributors_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); + nBinsYEvent[eNContributors_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); + minYEvent[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; + maxYEvent[eNContributors_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; + titleYEvent[eNContributors_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); // *) "NContributors_vs_InteractionRate": - nBins_x_Event[eNContributors_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); - min_x_Event[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eNContributors][1]; - max_x_Event[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eNContributors][2]; - title_x_Event[eNContributors_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); - nBins_y_Event[eNContributors_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); - min_y_Event[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; - max_y_Event[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; - title_y_Event[eNContributors_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); + nBinsXEvent[eNContributors_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eNContributors][0] / qa.fRebin); + minXEvent[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eNContributors][1]; + maxXEvent[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eNContributors][2]; + titleXEvent[eNContributors_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eNContributors].Data()); + nBinsYEvent[eNContributors_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); + minYEvent[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; + maxYEvent[eNContributors_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; + titleYEvent[eNContributors_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); // *) "Centrality_vs_VertexZ": - nBins_x_Event[eCentrality_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentrality_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); - nBins_y_Event[eCentrality_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); - min_y_Event[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; - max_y_Event[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; - title_y_Event[eCentrality_vs_VertexZ] = FancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); + nBinsXEvent[eCentrality_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentrality_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsYEvent[eCentrality_vs_VertexZ] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); + minYEvent[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][1]; + maxYEvent[eCentrality_vs_VertexZ] = eh.fEventHistogramsBins[eVertexZ][2]; + titleYEvent[eCentrality_vs_VertexZ] = fancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); // *) "Centrality_vs_Occupancy": - nBins_x_Event[eCentrality_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentrality_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); - nBins_y_Event[eCentrality_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); - min_y_Event[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; - max_y_Event[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; - title_y_Event[eCentrality_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); + nBinsXEvent[eCentrality_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentrality_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsYEvent[eCentrality_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); + minYEvent[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; + maxYEvent[eCentrality_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; + titleYEvent[eCentrality_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); // *) "Centrality_vs_ImpactParameter": - nBins_x_Event[eCentrality_vs_ImpactParameter] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentrality_vs_ImpactParameter] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); - nBins_y_Event[eCentrality_vs_ImpactParameter] = static_cast(eh.fEventHistogramsBins[eImpactParameter][0] / qa.fRebin); - min_y_Event[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eImpactParameter][1]; - max_y_Event[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eImpactParameter][2]; - title_y_Event[eCentrality_vs_ImpactParameter] = FancyFormatting(eh.fEventHistogramsName[eImpactParameter].Data()); + nBinsXEvent[eCentrality_vs_ImpactParameter] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentrality_vs_ImpactParameter] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsYEvent[eCentrality_vs_ImpactParameter] = static_cast(eh.fEventHistogramsBins[eImpactParameter][0] / qa.fRebin); + minYEvent[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eImpactParameter][1]; + maxYEvent[eCentrality_vs_ImpactParameter] = eh.fEventHistogramsBins[eImpactParameter][2]; + titleYEvent[eCentrality_vs_ImpactParameter] = fancyFormatting(eh.fEventHistogramsName[eImpactParameter].Data()); // *) "Centrality_vs_InteractionRate": - nBins_x_Event[eCentrality_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentrality_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); - nBins_y_Event[eCentrality_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); - min_y_Event[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; - max_y_Event[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; - title_y_Event[eCentrality_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); + nBinsXEvent[eCentrality_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentrality_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsYEvent[eCentrality_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); + minYEvent[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; + maxYEvent[eCentrality_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; + titleYEvent[eCentrality_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); // *) "VertexZ_vs_Occupancy": - nBins_x_Event[eVertexZ_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); - min_x_Event[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eVertexZ][1]; - max_x_Event[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eVertexZ][2]; - title_x_Event[eVertexZ_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); - nBins_y_Event[eVertexZ_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); - min_y_Event[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; - max_y_Event[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; - title_y_Event[eVertexZ_vs_Occupancy] = FancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); + nBinsXEvent[eVertexZ_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); + minXEvent[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eVertexZ][1]; + maxXEvent[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eVertexZ][2]; + titleXEvent[eVertexZ_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); + nBinsYEvent[eVertexZ_vs_Occupancy] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); + minYEvent[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][1]; + maxYEvent[eVertexZ_vs_Occupancy] = eh.fEventHistogramsBins[eOccupancy][2]; + titleYEvent[eVertexZ_vs_Occupancy] = fancyFormatting(eh.fEventHistogramsName[eOccupancy].Data()); // *) "VertexZ_vs_InteractionRate": - nBins_x_Event[eVertexZ_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); - min_x_Event[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eVertexZ][1]; - max_x_Event[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eVertexZ][2]; - title_x_Event[eVertexZ_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); - nBins_y_Event[eVertexZ_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); - min_y_Event[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; - max_y_Event[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; - title_y_Event[eVertexZ_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); + nBinsXEvent[eVertexZ_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eVertexZ][0]); + minXEvent[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eVertexZ][1]; + maxXEvent[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eVertexZ][2]; + titleXEvent[eVertexZ_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eVertexZ].Data()); + nBinsYEvent[eVertexZ_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); + minYEvent[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; + maxYEvent[eVertexZ_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; + titleYEvent[eVertexZ_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); // *) "Multiplicity_vs_FT0CAmplitudeOnFoundBC": - // nBins_x_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0]); - nBins_x_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 2000.; // TBI 20250331 hardwired value - min_x_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); - nBins_y_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 1000; // TBI 20250331 hardwired value - min_y_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 0.; // TBI 20250331 hardwired value - max_y_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 100000.; // TBI 20250331 hardwired value - title_y_Event[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = "FT0CAmplitudeOnFoundBC"; // TBI 20250331 hardwired string + // nBinsXEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0]); + nBinsXEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 2000.; // TBI 20250331 hardwired value + minXEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 1000; // TBI 20250331 hardwired value + minYEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 0.; // TBI 20250331 hardwired value + maxYEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = 100000.; // TBI 20250331 hardwired value + titleYEvent[eMultiplicity_vs_FT0CAmplitudeOnFoundBC] = "FT0CAmplitudeOnFoundBC"; // TBI 20250331 hardwired string // *) "CentFT0C_vs_FT0CAmplitudeOnFoundBC": - nBins_x_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eCentrality][0]; // yes, eCentrality, not eCentFT0C, just think of it ! - min_x_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); - nBins_y_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = 1000; // TBI 20250331 hardwired value - min_y_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = 0.; // TBI 20250331 hardwired value - max_y_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = 100000.; // TBI 20250331 hardwired value - title_y_Event[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = "FT0CAmplitudeOnFoundBC"; // TBI 20250331 hardwired string + nBinsXEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eCentrality][0]; // yes, eCentrality, not eCentFT0C, just think of it ! + minXEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); + nBinsYEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = 1000; // TBI 20250331 hardwired value + minYEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = 0.; // TBI 20250331 hardwired value + maxYEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = 100000.; // TBI 20250331 hardwired value + titleYEvent[eCentFT0C_vs_FT0CAmplitudeOnFoundBC] = "FT0CAmplitudeOnFoundBC"; // TBI 20250331 hardwired string // *) "Centrality_vs_CentralitySim": - nBins_x_Event[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][0]; - min_x_Event[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentrality_vs_CentralitySim] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); - nBins_y_Event[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][0]; - min_y_Event[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentrality_vs_CentralitySim] = "simulated centrality (calculated from IP)"; // TBI 20250331 hardwired string + nBinsXEvent[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][0]; + minXEvent[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentrality_vs_CentralitySim] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsYEvent[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][0]; + minYEvent[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentrality_vs_CentralitySim] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentrality_vs_CentralitySim] = "simulated centrality (calculated from IP)"; // TBI 20250331 hardwired string // *) "MultNTracksPV_vs_MultNTracksGlobal": - nBins_x_Event[eMultNTracksPV_vs_MultNTracksGlobal] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_x_Event[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_x_Event[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_x_Event[eMultNTracksPV_vs_MultNTracksGlobal] = FancyFormatting(qa.fReferenceMultiplicityEstimatorName[eMultNTracksPV].Data()); - nBins_y_Event[eMultNTracksPV_vs_MultNTracksGlobal] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_y_Event[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_y_Event[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_y_Event[eMultNTracksPV_vs_MultNTracksGlobal] = FancyFormatting(qa.fReferenceMultiplicityEstimatorName[eMultNTracksGlobal].Data()); + nBinsXEvent[eMultNTracksPV_vs_MultNTracksGlobal] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minXEvent[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxXEvent[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleXEvent[eMultNTracksPV_vs_MultNTracksGlobal] = fancyFormatting(qa.fReferenceMultiplicityEstimatorName[eMultNTracksPV].Data()); + nBinsYEvent[eMultNTracksPV_vs_MultNTracksGlobal] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minYEvent[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxYEvent[eMultNTracksPV_vs_MultNTracksGlobal] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleYEvent[eMultNTracksPV_vs_MultNTracksGlobal] = fancyFormatting(qa.fReferenceMultiplicityEstimatorName[eMultNTracksGlobal].Data()); // *) "CentFT0C_vs_CentFT0CVariant1": - nBins_x_Event[eCentFT0C_vs_CentFT0CVariant1] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentFT0C_vs_CentFT0CVariant1] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); - nBins_y_Event[eCentFT0C_vs_CentFT0CVariant1] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentFT0C_vs_CentFT0CVariant1] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0CVariant1].Data()); + nBinsXEvent[eCentFT0C_vs_CentFT0CVariant1] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentFT0C_vs_CentFT0CVariant1] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); + nBinsYEvent[eCentFT0C_vs_CentFT0CVariant1] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentFT0C_vs_CentFT0CVariant1] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentFT0C_vs_CentFT0CVariant1] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0CVariant1].Data()); // *) "CentFT0C_vs_CentFT0M": - nBins_x_Event[eCentFT0C_vs_CentFT0M] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentFT0C_vs_CentFT0M] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); - nBins_y_Event[eCentFT0C_vs_CentFT0M] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentFT0C_vs_CentFT0M] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0M].Data()); + nBinsXEvent[eCentFT0C_vs_CentFT0M] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentFT0C_vs_CentFT0M] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); + nBinsYEvent[eCentFT0C_vs_CentFT0M] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentFT0C_vs_CentFT0M] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentFT0C_vs_CentFT0M] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0M].Data()); // *) "CentFT0C_vs_CentFV0A": - nBins_x_Event[eCentFT0C_vs_CentFV0A] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentFT0C_vs_CentFV0A] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); - nBins_y_Event[eCentFT0C_vs_CentFV0A] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentFT0C_vs_CentFV0A] = FancyFormatting(qa.fCentralityEstimatorName[eCentFV0A].Data()); + nBinsXEvent[eCentFT0C_vs_CentFV0A] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentFT0C_vs_CentFV0A] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); + nBinsYEvent[eCentFT0C_vs_CentFV0A] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentFT0C_vs_CentFV0A] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentFT0C_vs_CentFV0A] = fancyFormatting(qa.fCentralityEstimatorName[eCentFV0A].Data()); // *) "CentFT0C_vs_CentNTPV": - nBins_x_Event[eCentFT0C_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentFT0C_vs_CentNTPV] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); - nBins_y_Event[eCentFT0C_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentFT0C_vs_CentNTPV] = FancyFormatting(qa.fCentralityEstimatorName[eCentNTPV].Data()); + nBinsXEvent[eCentFT0C_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentFT0C_vs_CentNTPV] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); + nBinsYEvent[eCentFT0C_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentFT0C_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentFT0C_vs_CentNTPV] = fancyFormatting(qa.fCentralityEstimatorName[eCentNTPV].Data()); // *) "CentFT0C_vs_CentNGlobal": - nBins_x_Event[eCentFT0C_vs_CentNGlobal] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentFT0C_vs_CentNGlobal] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); - nBins_y_Event[eCentFT0C_vs_CentNGlobal] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentFT0C_vs_CentNGlobal] = FancyFormatting(qa.fCentralityEstimatorName[eCentNGlobal].Data()); + nBinsXEvent[eCentFT0C_vs_CentNGlobal] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentFT0C_vs_CentNGlobal] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0C].Data()); + nBinsYEvent[eCentFT0C_vs_CentNGlobal] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentFT0C_vs_CentNGlobal] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentFT0C_vs_CentNGlobal] = fancyFormatting(qa.fCentralityEstimatorName[eCentNGlobal].Data()); // *) "CentFT0M_vs_CentNTPV": - nBins_x_Event[eCentFT0M_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentFT0M_vs_CentNTPV] = FancyFormatting(qa.fCentralityEstimatorName[eCentFT0M].Data()); - nBins_y_Event[eCentFT0M_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentFT0M_vs_CentNTPV] = FancyFormatting(qa.fCentralityEstimatorName[eCentNTPV].Data()); + nBinsXEvent[eCentFT0M_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentFT0M_vs_CentNTPV] = fancyFormatting(qa.fCentralityEstimatorName[eCentFT0M].Data()); + nBinsYEvent[eCentFT0M_vs_CentNTPV] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentFT0M_vs_CentNTPV] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentFT0M_vs_CentNTPV] = fancyFormatting(qa.fCentralityEstimatorName[eCentNTPV].Data()); // *) "CentRun2V0M_vs_CentRun2SPDTracklets": - nBins_x_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_x_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][1]; - max_x_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][2]; - title_x_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = FancyFormatting(qa.fCentralityEstimatorName[eCentRun2V0M].Data()); - nBins_y_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_Event[eCentRun2V0M_vs_CentRun2SPDTracklets] = FancyFormatting(qa.fCentralityEstimatorName[eCentRun2SPDTracklets].Data()); + nBinsXEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minXEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][1]; + maxXEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][2]; + titleXEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = fancyFormatting(qa.fCentralityEstimatorName[eCentRun2V0M].Data()); + nBinsYEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][1]; + maxYEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = eh.fEventHistogramsBins[eCentrality][2]; + titleYEvent[eCentRun2V0M_vs_CentRun2SPDTracklets] = fancyFormatting(qa.fCentralityEstimatorName[eCentRun2SPDTracklets].Data()); // *) "TrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange": - nBins_x_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); - min_x_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][1]; - max_x_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][2]; - title_x_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = FancyFormatting(qa.fOccupancyEstimatorName[eTrackOccupancyInTimeRange].Data()); - nBins_y_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); - min_y_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][1]; - max_y_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][2]; - title_y_Event[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = FancyFormatting(qa.fOccupancyEstimatorName[eFT0COccupancyInTimeRange].Data()); + nBinsXEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); + minXEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][1]; + maxXEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][2]; + titleXEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = fancyFormatting(qa.fOccupancyEstimatorName[eTrackOccupancyInTimeRange].Data()); + nBinsYEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = static_cast(eh.fEventHistogramsBins[eOccupancy][0] / qa.fRebin); + minYEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][1]; + maxYEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = eh.fEventHistogramsBins[eOccupancy][2]; + titleYEvent[eTrackOccupancyInTimeRange_vs_FT0COccupancyInTimeRange] = fancyFormatting(qa.fOccupancyEstimatorName[eFT0COccupancyInTimeRange].Data()); // *) "CurrentRunDuration_vs_InteractionRate": - nBins_x_Event[eCurrentRunDuration_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0] / qa.fRebin); - min_x_Event[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_Event[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_Event[eCurrentRunDuration_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_Event[eCurrentRunDuration_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); - min_y_Event[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; - max_y_Event[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; - title_y_Event[eCurrentRunDuration_vs_InteractionRate] = FancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); + nBinsXEvent[eCurrentRunDuration_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0] / qa.fRebin); + minXEvent[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXEvent[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXEvent[eCurrentRunDuration_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYEvent[eCurrentRunDuration_vs_InteractionRate] = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); + minYEvent[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][1]; + maxYEvent[eCurrentRunDuration_vs_InteractionRate] = eh.fEventHistogramsBins[eInteractionRate][2]; + titleYEvent[eCurrentRunDuration_vs_InteractionRate] = fancyFormatting(eh.fEventHistogramsName[eInteractionRate].Data()); // ... - // *) Quick insanity check on title_x_Event and title_y_Event: + // *) Quick insanity check on titleXEvent and titleYEvent: for (int t = 0; t < eQAEventHistograms2D_N; t++) { - // **) title_x_Event: + // **) titleXEvent: if (tc.fVerbose) { - LOGF(info, "\033[1;32m title_x_Event[%d] = %s \033[0m", t, title_x_Event[t].Data()); + LOGF(info, "\033[1;32m titleXEvent[%d] = %s \033[0m", t, titleXEvent[t].Data()); } - if (title_x_Event[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_x_Event[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleXEvent[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleXEvent[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } - // **) title_y_Event: + // **) titleYEvent: if (tc.fVerbose) { - LOGF(info, "\033[1;32m title_y_Event[%d] = %s \033[0m", t, title_y_Event[t].Data()); + LOGF(info, "\033[1;32m titleYEvent[%d] = %s \033[0m", t, titleYEvent[t].Data()); } - if (title_y_Event[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_y_Event[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleYEvent[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleYEvent[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } } // for (int t = 0; t < eQAEventHistograms2D_N; t++) { @@ -4186,21 +4192,21 @@ void bookQAHistograms() if (!qa.fBookQAEventHistograms2D[t]) { continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { // Special treatment for eMultiplicity => I will never fill this one before the cuts, if Multiplicity = SelectedTracks, obviously: - if (ba == eBefore && (title_x_Event[t].BeginsWith("Multiplicity") || title_y_Event[t].BeginsWith("Multiplicity")) && ec.fsEventCuts[eMultiplicityEstimator].EqualTo("SelectedTracks", TString::kIgnoreCase)) { + if (ba == eBefore && (titleXEvent[t].BeginsWith("Multiplicity") || titleYEvent[t].BeginsWith("Multiplicity")) && ec.fsEventCuts[eMultiplicityEstimator].EqualTo("SelectedTracks", TString::kIgnoreCase)) { // TBI 20241123 what remains ill-defined is the case when Multiplicity != SelectedTracks , check that further // TBI 20241123 not sure if checking with BeginsWith(...) x2 is robust enough - // TBI 20241123 just like I have Skip(rs), introduce the same thingie for "ba" counter + propagate to other member functions + // TBI 20241123 just like I have skip(rs), introduce the same thingie for "ba" counter + propagate to other member functions // TBI 20241124 there is a corner case when eMultiplicityEstimator itself is "ReferenceMultiplicity" => all 2D QA booked both before and after cuts, // but it's filled trivially before the cuts, because Multiplicity is always 0. Re-think this at some point. continue; @@ -4209,10 +4215,10 @@ void bookQAHistograms() // valgrind --tool=massif => ~9.8 MiB (Last check: 20250315) qa.fQAEventHistograms2D[t][rs][ba] = new TH2F( TString::Format("fQAEventHistograms2D[%s][%s][%s]", qa.fEventHistogramsName2D[t].Data(), gc.srs[rs].Data(), gc.sba[ba].Data()), - TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in PropagateRunNumber(...) - nBins_x_Event[t], min_x_Event[t], max_x_Event[t], nBins_y_Event[t], min_y_Event[t], max_y_Event[t]); - qa.fQAEventHistograms2D[t][rs][ba]->GetXaxis()->SetTitle(title_x_Event[t].Data()); - qa.fQAEventHistograms2D[t][rs][ba]->GetYaxis()->SetTitle(title_y_Event[t].Data()); + TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in propagateRunNumber(...) + nBinsXEvent[t], minXEvent[t], maxXEvent[t], nBinsYEvent[t], minYEvent[t], maxYEvent[t]); + qa.fQAEventHistograms2D[t][rs][ba]->GetXaxis()->SetTitle(titleXEvent[t].Data()); + qa.fQAEventHistograms2D[t][rs][ba]->GetYaxis()->SetTitle(titleYEvent[t].Data()); qa.fQAEventHistograms2D[t][rs][ba]->SetLineColor(ec.fBeforeAfterColor[ba]); qa.fQAEventHistograms2D[t][rs][ba]->SetFillColor(ec.fBeforeAfterColor[ba] - 10); qa.fQAEventHistograms2D[t][rs][ba]->SetOption("col"); @@ -4225,34 +4231,34 @@ void bookQAHistograms() // c) Book specific QA 2D particle histograms: // Binning of 2D particle histos: TBI 20240503 see if you can automate all this - int nBins_x_Particle[eQAParticleHistograms2D_N] = {0}; - double min_x_Particle[eQAParticleHistograms2D_N] = {0.}; - double max_x_Particle[eQAParticleHistograms2D_N] = {0.}; - TString title_x_Particle[eQAParticleHistograms2D_N] = {""}; - int nBins_y_Particle[eQAParticleHistograms2D_N] = {0}; - double min_y_Particle[eQAParticleHistograms2D_N] = {0.}; - double max_y_Particle[eQAParticleHistograms2D_N] = {0.}; - TString title_y_Particle[eQAParticleHistograms2D_N] = {""}; + int nBinsXParticle[eQAParticleHistograms2D_N] = {0}; + double minXParticle[eQAParticleHistograms2D_N] = {0.}; + double maxXParticle[eQAParticleHistograms2D_N] = {0.}; + TString titleXParticle[eQAParticleHistograms2D_N] = {""}; + int nBinsYParticle[eQAParticleHistograms2D_N] = {0}; + double minYParticle[eQAParticleHistograms2D_N] = {0.}; + double maxYParticle[eQAParticleHistograms2D_N] = {0.}; + TString titleYParticle[eQAParticleHistograms2D_N] = {""}; // *) "pt_vs_dcaXY": - nBins_x_Particle[ePt_vs_dcaXY] = static_cast(ph.fParticleHistogramsBins[ePt][0]); // TBI 20240702 add support for rebinning - min_x_Particle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[ePt][1]; - max_x_Particle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[ePt][2]; - title_x_Particle[ePt_vs_dcaXY] = FancyFormatting(ph.fParticleHistogramsName[ePt].Data()); - nBins_y_Particle[ePt_vs_dcaXY] = static_cast(ph.fParticleHistogramsBins[edcaXY][0]); // TBI 20240702 add support for rebinning - min_y_Particle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[edcaXY][1]; - max_y_Particle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[edcaXY][2]; - title_y_Particle[ePt_vs_dcaXY] = FancyFormatting(ph.fParticleHistogramsName[edcaXY].Data()); + nBinsXParticle[ePt_vs_dcaXY] = static_cast(ph.fParticleHistogramsBins[ePt][0]); // TBI 20240702 add support for rebinning + minXParticle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[ePt][1]; + maxXParticle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[ePt][2]; + titleXParticle[ePt_vs_dcaXY] = fancyFormatting(ph.fParticleHistogramsName[ePt].Data()); + nBinsYParticle[ePt_vs_dcaXY] = static_cast(ph.fParticleHistogramsBins[edcaXY][0]); // TBI 20240702 add support for rebinning + minYParticle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[edcaXY][1]; + maxYParticle[ePt_vs_dcaXY] = ph.fParticleHistogramsBins[edcaXY][2]; + titleYParticle[ePt_vs_dcaXY] = fancyFormatting(ph.fParticleHistogramsName[edcaXY].Data()); // ... - // *) Quick insanity check on title_x_Particle and title_y_Particle: + // *) Quick insanity check on titleXParticle and titleYParticle: for (int t = 0; t < eQAParticleHistograms2D_N; t++) { - if (title_x_Particle[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_x_Particle[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleXParticle[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleXParticle[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } - if (title_y_Particle[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_y_Particle[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleYParticle[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleYParticle[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } } @@ -4262,23 +4268,23 @@ void bookQAHistograms() if (!qa.fBookQAParticleHistograms2D[t]) { continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { // valgrind --tool=massif => ~30.6 MiB (Last check: 20250315) qa.fQAParticleHistograms2D[t][rs][ba] = new TH2F( TString::Format("fQAParticleHistograms2D[%s][%s][%s]", qa.fParticleHistogramsName2D[t].Data(), gc.srs[rs].Data(), gc.sba[ba].Data()), - TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in PropagateRunNumber(...) - nBins_x_Particle[t], min_x_Particle[t], max_x_Particle[t], nBins_y_Particle[t], min_y_Particle[t], max_y_Particle[t]); + TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in propagateRunNumber(...) + nBinsXParticle[t], minXParticle[t], maxXParticle[t], nBinsYParticle[t], minYParticle[t], maxYParticle[t]); - qa.fQAParticleHistograms2D[t][rs][ba]->GetXaxis()->SetTitle(title_x_Particle[t].Data()); - qa.fQAParticleHistograms2D[t][rs][ba]->GetYaxis()->SetTitle(title_y_Particle[t].Data()); + qa.fQAParticleHistograms2D[t][rs][ba]->GetXaxis()->SetTitle(titleXParticle[t].Data()); + qa.fQAParticleHistograms2D[t][rs][ba]->GetYaxis()->SetTitle(titleYParticle[t].Data()); qa.fQAParticleHistograms2D[t][rs][ba]->SetLineColor(ec.fBeforeAfterColor[ba]); qa.fQAParticleHistograms2D[t][rs][ba]->SetFillColor(ec.fBeforeAfterColor[ba] - 10); qa.fQAParticleHistograms2D[t][rs][ba]->SetOption("col"); @@ -4290,125 +4296,125 @@ void bookQAHistograms() // e) Book specific QA 2D particle event histograms: // Binning of 2D particle event histos: - int nBins_x_ParticleEvent[eQAParticleEventHistograms2D_N] = {0}; - double min_x_ParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; - double max_x_ParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; - TString title_x_ParticleEvent[eQAParticleEventHistograms2D_N] = {""}; - int nBins_y_ParticleEvent[eQAParticleEventHistograms2D_N] = {0}; - double min_y_ParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; - double max_y_ParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; - TString title_y_ParticleEvent[eQAParticleEventHistograms2D_N] = {""}; + int nBinsXParticleEvent[eQAParticleEventHistograms2D_N] = {0}; + double minXParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; + double maxXParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; + TString titleXParticleEvent[eQAParticleEventHistograms2D_N] = {""}; + int nBinsYParticleEvent[eQAParticleEventHistograms2D_N] = {0}; + double minYParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; + double maxYParticleEvent[eQAParticleEventHistograms2D_N] = {0.}; + TString titleYParticleEvent[eQAParticleEventHistograms2D_N] = {""}; // *) "eCurrentRunDuration_vs_itsNClsEbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - // nBins_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = static_cast(ph.fParticleHistogramsBins[eitsNCls][0]); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = 100; - min_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = ph.fParticleHistogramsBins[eitsNCls][1]; - max_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = ph.fParticleHistogramsBins[eitsNCls][2]; - title_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = TString::Format("#LT%s#GT", FancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + // nBinsYParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = static_cast(ph.fParticleHistogramsBins[eitsNCls][0]); + nBinsYParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = 100; + minYParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = ph.fParticleHistogramsBins[eitsNCls][1]; + maxYParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = ph.fParticleHistogramsBins[eitsNCls][2]; + titleYParticleEvent[eCurrentRunDuration_vs_itsNClsEbyE] = TString::Format("#LT%s#GT", fancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data())); // *) "eCurrentRunDuration_vs_itsNClsNegEtaEbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = 100; - min_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][1]; - max_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][2]; - title_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = TString::Format("#LT%s#GT, #eta < 0", FancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = 100; + minYParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][1]; + maxYParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][2]; + titleYParticleEvent[eCurrentRunDuration_vs_itsNClsNegEtaEbyE] = TString::Format("#LT%s#GT, #eta < 0", fancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data())); // *) "eCurrentRunDuration_vs_itsNClsPosEtaEbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = 100; - min_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][1]; - max_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][2]; - title_y_ParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = TString::Format("#LT%s#GT, #eta > 0", FancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = 100; + minYParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][1]; + maxYParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = ph.fParticleHistogramsBins[eitsNCls][2]; + titleYParticleEvent[eCurrentRunDuration_vs_itsNClsPosEtaEbyE] = TString::Format("#LT%s#GT, #eta > 0", fancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data())); // *) "eCurrentRunDuration_vs_Eta0804EbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = 80; - min_y_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = -1.0; // TBI 20241214 intentionally temporarily overshooting, to trace down overflow and underflow, if any - max_y_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = 1.0; // TBI 20241214 intentionally temporarily overshooting, to trace down overflow and underflow, if any - title_y_ParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = TString::Format("#LT%s#GT, -0.8 < #eta < -0.4", FancyFormatting(ph.fParticleHistogramsName[eEta].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = 80; + minYParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = -1.0; // TBI 20241214 intentionally temporarily overshooting, to trace down overflow and underflow, if any + maxYParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = 1.0; // TBI 20241214 intentionally temporarily overshooting, to trace down overflow and underflow, if any + titleYParticleEvent[eCurrentRunDuration_vs_Eta0804EbyE] = TString::Format("#LT%s#GT, -0.8 < #eta < -0.4", fancyFormatting(ph.fParticleHistogramsName[eEta].Data())); // *) "eCurrentRunDuration_vs_Eta0400EbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = 80; - min_y_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = -1.0; - max_y_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = 1.0; - title_y_ParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = TString::Format("#LT%s#GT, -0.4 < #eta < 0.0", FancyFormatting(ph.fParticleHistogramsName[eEta].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = 80; + minYParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = -1.0; + maxYParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = 1.0; + titleYParticleEvent[eCurrentRunDuration_vs_Eta0400EbyE] = TString::Format("#LT%s#GT, -0.4 < #eta < 0.0", fancyFormatting(ph.fParticleHistogramsName[eEta].Data())); // *) "eCurrentRunDuration_vs_Eta0004EbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = 80; - min_y_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = -1.0; - max_y_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = 1.0; - title_y_ParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = TString::Format("#LT%s#GT, 0.0 < #eta < 0.4", FancyFormatting(ph.fParticleHistogramsName[eEta].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = 80; + minYParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = -1.0; + maxYParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = 1.0; + titleYParticleEvent[eCurrentRunDuration_vs_Eta0004EbyE] = TString::Format("#LT%s#GT, 0.0 < #eta < 0.4", fancyFormatting(ph.fParticleHistogramsName[eEta].Data())); // *) "eCurrentRunDuration_vs_Eta0408EbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = 80; - min_y_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = -1.0; - max_y_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = 1.0; - title_y_ParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = TString::Format("#LT%s#GT, 0.4 < #eta < 0.8", FancyFormatting(ph.fParticleHistogramsName[eEta].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = 80; + minYParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = -1.0; + maxYParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = 1.0; + titleYParticleEvent[eCurrentRunDuration_vs_Eta0408EbyE] = TString::Format("#LT%s#GT, 0.4 < #eta < 0.8", fancyFormatting(ph.fParticleHistogramsName[eEta].Data())); // *) "eCurrentRunDuration_vs_Pt0005EbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = 400; - min_y_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = 0.0; // TBI 20241214 intentionally temporarilyovershooting, to trace down overflow and underflow, if any - max_y_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = 10.0; // TBI 20241214 intentionally temporarily overshooting, to trace down overflow and underflow, if any - title_y_ParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = TString::Format("#LT%s#GT, 0.0 < p_{T} < 0.5 GeV/c", FancyFormatting(ph.fParticleHistogramsName[ePt].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = 400; + minYParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = 0.0; // TBI 20241214 intentionally temporarilyovershooting, to trace down overflow and underflow, if any + maxYParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = 10.0; // TBI 20241214 intentionally temporarily overshooting, to trace down overflow and underflow, if any + titleYParticleEvent[eCurrentRunDuration_vs_Pt0005EbyE] = TString::Format("#LT%s#GT, 0.0 < p_{T} < 0.5 GeV/c", fancyFormatting(ph.fParticleHistogramsName[ePt].Data())); // *) "eCurrentRunDuration_vs_Pt0510EbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = 400; - min_y_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = 0.0; - max_y_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = 10.0; - title_y_ParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = TString::Format("#LT%s#GT, 0.5 < p_{T} < 1.0 GeV/c", FancyFormatting(ph.fParticleHistogramsName[ePt].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = 400; + minYParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = 0.0; + maxYParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = 10.0; + titleYParticleEvent[eCurrentRunDuration_vs_Pt0510EbyE] = TString::Format("#LT%s#GT, 0.5 < p_{T} < 1.0 GeV/c", fancyFormatting(ph.fParticleHistogramsName[ePt].Data())); // *) "eCurrentRunDuration_vs_Pt1050EbyE": - nBins_x_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); - min_x_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_x_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_x_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); - nBins_y_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = 400; - min_y_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = 0.0; - max_y_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = 10.0; - title_y_ParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = TString::Format("#LT%s#GT, 1.0 < p_{T} < 5.0 GeV/c", FancyFormatting(ph.fParticleHistogramsName[ePt].Data())); + nBinsXParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0]); + minXParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxXParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleXParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = 400; + minYParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = 0.0; + maxYParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = 10.0; + titleYParticleEvent[eCurrentRunDuration_vs_Pt1050EbyE] = TString::Format("#LT%s#GT, 1.0 < p_{T} < 5.0 GeV/c", fancyFormatting(ph.fParticleHistogramsName[ePt].Data())); // ... - // *) Quick insanity check on title_x_ParticleEvent and title_y_ParticleEvent: + // *) Quick insanity check on titleXParticleEvent and titleYParticleEvent: for (int t = 0; t < eQAParticleEventHistograms2D_N; t++) { - if (title_x_ParticleEvent[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_x_ParticleEvent[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleXParticleEvent[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleXParticleEvent[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } - if (title_y_ParticleEvent[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_y_ParticleEvent[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleYParticleEvent[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleYParticleEvent[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } } @@ -4418,14 +4424,14 @@ void bookQAHistograms() if (!qa.fBookQAParticleEventHistograms2D[t]) { continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (ba == eBefore) { // TBI 20241214 re-think if I need these additional QA particle event histos before cuts @@ -4435,11 +4441,11 @@ void bookQAHistograms() // valgrind --tool=massif => ~70.2 MiB (Last check: 20250315) qa.fQAParticleEventHistograms2D[t][rs][ba] = new TH2F( TString::Format("fQAParticleEventHistograms2D[%s][%s][%s]", qa.fQAParticleEventHistogramsName2D[t].Data(), gc.srs[rs].Data(), gc.sba[ba].Data()), - TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in PropagateRunNumber(...) - nBins_x_ParticleEvent[t], min_x_ParticleEvent[t], max_x_ParticleEvent[t], nBins_y_ParticleEvent[t], min_y_ParticleEvent[t], max_y_ParticleEvent[t]); + TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in propagateRunNumber(...) + nBinsXParticleEvent[t], minXParticleEvent[t], maxXParticleEvent[t], nBinsYParticleEvent[t], minYParticleEvent[t], maxYParticleEvent[t]); - qa.fQAParticleEventHistograms2D[t][rs][ba]->GetXaxis()->SetTitle(title_x_ParticleEvent[t].Data()); - qa.fQAParticleEventHistograms2D[t][rs][ba]->GetYaxis()->SetTitle(title_y_ParticleEvent[t].Data()); + qa.fQAParticleEventHistograms2D[t][rs][ba]->GetXaxis()->SetTitle(titleXParticleEvent[t].Data()); + qa.fQAParticleEventHistograms2D[t][rs][ba]->GetYaxis()->SetTitle(titleYParticleEvent[t].Data()); qa.fQAParticleEventHistograms2D[t][rs][ba]->SetLineColor(ec.fBeforeAfterColor[ba]); qa.fQAParticleEventHistograms2D[t][rs][ba]->SetFillColor(ec.fBeforeAfterColor[ba] - 10); qa.fQAParticleEventHistograms2D[t][rs][ba]->SetOption("col"); @@ -4449,14 +4455,14 @@ void bookQAHistograms() } // for(int t=0;t(eh.fEventHistogramsBins[eMultiplicity][0] / 10); // TBI 20250331 here I have temporarily hardwired rebin value - min_y_CorrelationsVs[eCorrelations_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_y_CorrelationsVs[eCorrelations_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_y_CorrelationsVs[eCorrelations_vs_Multiplicity] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_Multiplicity] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / 10); // TBI 20250331 here I have temporarily hardwired rebin value + minYCorrelationsVs[eCorrelations_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxYCorrelationsVs[eCorrelations_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleYCorrelationsVs[eCorrelations_vs_Multiplicity] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); // *) "eCorrelations_vs_ReferenceMultiplicity": - nBins_y_CorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / 10); // TBI 20250331 here I have temporarily hardwired rebin value - min_y_CorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_y_CorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_y_CorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / 10); // TBI 20250331 here I have temporarily hardwired rebin value + minYCorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxYCorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleYCorrelationsVs[eCorrelations_vs_ReferenceMultiplicity] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); // *) "eCorrelations_vs_Centrality": - nBins_y_CorrelationsVs[eCorrelations_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_CorrelationsVs[eCorrelations_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_CorrelationsVs[eCorrelations_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_CorrelationsVs[eCorrelations_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYCorrelationsVs[eCorrelations_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; + maxYCorrelationsVs[eCorrelations_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; + titleYCorrelationsVs[eCorrelations_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); // ..... // *) "eCorrelations_vs_MeanPhi": - nBins_y_CorrelationsVs[eCorrelations_vs_MeanPhi] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeanPhi] = 2.; - max_y_CorrelationsVs[eCorrelations_vs_MeanPhi] = 4.; - title_y_CorrelationsVs[eCorrelations_vs_MeanPhi] = FancyFormatting(ph.fParticleHistogramsName[ePhi].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeanPhi] = 200; + minYCorrelationsVs[eCorrelations_vs_MeanPhi] = 2.; + maxYCorrelationsVs[eCorrelations_vs_MeanPhi] = 4.; + titleYCorrelationsVs[eCorrelations_vs_MeanPhi] = fancyFormatting(ph.fParticleHistogramsName[ePhi].Data()); // *) "eCorrelations_vs_MeanPt": - nBins_y_CorrelationsVs[eCorrelations_vs_MeanPt] = 100; - min_y_CorrelationsVs[eCorrelations_vs_MeanPt] = 0.0; - max_y_CorrelationsVs[eCorrelations_vs_MeanPt] = 2.0; - title_y_CorrelationsVs[eCorrelations_vs_MeanPt] = FancyFormatting(ph.fParticleHistogramsName[ePt].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeanPt] = 100; + minYCorrelationsVs[eCorrelations_vs_MeanPt] = 0.0; + maxYCorrelationsVs[eCorrelations_vs_MeanPt] = 2.0; + titleYCorrelationsVs[eCorrelations_vs_MeanPt] = fancyFormatting(ph.fParticleHistogramsName[ePt].Data()); // *) "eCorrelations_vs_MeanEta": - nBins_y_CorrelationsVs[eCorrelations_vs_MeanEta] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeanEta] = -0.3; - max_y_CorrelationsVs[eCorrelations_vs_MeanEta] = 0.3; - title_y_CorrelationsVs[eCorrelations_vs_MeanEta] = FancyFormatting(ph.fParticleHistogramsName[eEta].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeanEta] = 200; + minYCorrelationsVs[eCorrelations_vs_MeanEta] = -0.3; + maxYCorrelationsVs[eCorrelations_vs_MeanEta] = 0.3; + titleYCorrelationsVs[eCorrelations_vs_MeanEta] = fancyFormatting(ph.fParticleHistogramsName[eEta].Data()); // *) "eCorrelations_vs_MeanCharge": - nBins_y_CorrelationsVs[eCorrelations_vs_MeanCharge] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeanCharge] = -1.; - max_y_CorrelationsVs[eCorrelations_vs_MeanCharge] = 1.; - title_y_CorrelationsVs[eCorrelations_vs_MeanCharge] = FancyFormatting(ph.fParticleHistogramsName[eCharge].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeanCharge] = 200; + minYCorrelationsVs[eCorrelations_vs_MeanCharge] = -1.; + maxYCorrelationsVs[eCorrelations_vs_MeanCharge] = 1.; + titleYCorrelationsVs[eCorrelations_vs_MeanCharge] = fancyFormatting(ph.fParticleHistogramsName[eCharge].Data()); // *) "eCorrelations_vs_MeantpcNClsFindable": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = 400; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = 50.; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = 250.; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = FancyFormatting(ph.fParticleHistogramsName[etpcNClsFindable].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = 400; + minYCorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = 50.; + maxYCorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = 250.; + titleYCorrelationsVs[eCorrelations_vs_MeantpcNClsFindable] = fancyFormatting(ph.fParticleHistogramsName[etpcNClsFindable].Data()); // *) "eCorrelations_vs_MeantpcNClsShared": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = 500; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = 0.; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = 100.; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = FancyFormatting(ph.fParticleHistogramsName[etpcNClsShared].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = 500; + minYCorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = 0.; + maxYCorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = 100.; + titleYCorrelationsVs[eCorrelations_vs_MeantpcNClsShared] = fancyFormatting(ph.fParticleHistogramsName[etpcNClsShared].Data()); // *) "eCorrelations_vs_MeanitsChi2NCl": - nBins_y_CorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = 0.; - max_y_CorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = 4.; - title_y_CorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = FancyFormatting(ph.fParticleHistogramsName[eitsChi2NCl].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = 200; + minYCorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = 0.; + maxYCorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = 4.; + titleYCorrelationsVs[eCorrelations_vs_MeanitsChi2NCl] = fancyFormatting(ph.fParticleHistogramsName[eitsChi2NCl].Data()); // *) "eCorrelations_vs_MeantpcNClsFound": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = 400; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = 50.; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = 250.; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = FancyFormatting(ph.fParticleHistogramsName[etpcNClsFound].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = 400; + minYCorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = 50.; + maxYCorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = 250.; + titleYCorrelationsVs[eCorrelations_vs_MeantpcNClsFound] = fancyFormatting(ph.fParticleHistogramsName[etpcNClsFound].Data()); // *) "eCorrelations_vs_MeantpcNClsCrossedRows": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = 400; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = 50.; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = 250.; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = FancyFormatting(ph.fParticleHistogramsName[etpcNClsCrossedRows].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = 400; + minYCorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = 50.; + maxYCorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = 250.; + titleYCorrelationsVs[eCorrelations_vs_MeantpcNClsCrossedRows] = fancyFormatting(ph.fParticleHistogramsName[etpcNClsCrossedRows].Data()); // *) "eCorrelations_vs_MeanitsNCls": - nBins_y_CorrelationsVs[eCorrelations_vs_MeanitsNCls] = 500; - min_y_CorrelationsVs[eCorrelations_vs_MeanitsNCls] = 0.; - max_y_CorrelationsVs[eCorrelations_vs_MeanitsNCls] = 10.; - title_y_CorrelationsVs[eCorrelations_vs_MeanitsNCls] = FancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeanitsNCls] = 500; + minYCorrelationsVs[eCorrelations_vs_MeanitsNCls] = 0.; + maxYCorrelationsVs[eCorrelations_vs_MeanitsNCls] = 10.; + titleYCorrelationsVs[eCorrelations_vs_MeanitsNCls] = fancyFormatting(ph.fParticleHistogramsName[eitsNCls].Data()); // *) "eCorrelations_vs_MeanitsNClsInnerBarrel": - nBins_y_CorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = 400; - min_y_CorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = 0.; - max_y_CorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = 4.; - title_y_CorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = FancyFormatting(ph.fParticleHistogramsName[eitsNClsInnerBarrel].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = 400; + minYCorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = 0.; + maxYCorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = 4.; + titleYCorrelationsVs[eCorrelations_vs_MeanitsNClsInnerBarrel] = fancyFormatting(ph.fParticleHistogramsName[eitsNClsInnerBarrel].Data()); // *) "eCorrelations_vs_MeantpcCrossedRowsOverFindableCls": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = 0.; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = 2.; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = FancyFormatting(ph.fParticleHistogramsName[etpcCrossedRowsOverFindableCls].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = 200; + minYCorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = 0.; + maxYCorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = 2.; + titleYCorrelationsVs[eCorrelations_vs_MeantpcCrossedRowsOverFindableCls] = fancyFormatting(ph.fParticleHistogramsName[etpcCrossedRowsOverFindableCls].Data()); // *) "eCorrelations_vs_MeantpcFoundOverFindableCls": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = 0.8; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = 1.2; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = FancyFormatting(ph.fParticleHistogramsName[etpcFoundOverFindableCls].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = 200; + minYCorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = 0.8; + maxYCorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = 1.2; + titleYCorrelationsVs[eCorrelations_vs_MeantpcFoundOverFindableCls] = fancyFormatting(ph.fParticleHistogramsName[etpcFoundOverFindableCls].Data()); // *) "eCorrelations_vs_MeantpcFractionSharedCls": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = 500; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = 0.; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = 1.; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = FancyFormatting(ph.fParticleHistogramsName[etpcFractionSharedCls].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = 500; + minYCorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = 0.; + maxYCorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = 1.; + titleYCorrelationsVs[eCorrelations_vs_MeantpcFractionSharedCls] = fancyFormatting(ph.fParticleHistogramsName[etpcFractionSharedCls].Data()); // *) "eCorrelations_vs_MeantpcChi2NCl": - nBins_y_CorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = 0.; - max_y_CorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = 2.; - title_y_CorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = FancyFormatting(ph.fParticleHistogramsName[etpcChi2NCl].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = 200; + minYCorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = 0.; + maxYCorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = 2.; + titleYCorrelationsVs[eCorrelations_vs_MeantpcChi2NCl] = fancyFormatting(ph.fParticleHistogramsName[etpcChi2NCl].Data()); // *) "eCorrelations_vs_MeandcaXY": - nBins_y_CorrelationsVs[eCorrelations_vs_MeandcaXY] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeandcaXY] = -0.2; - max_y_CorrelationsVs[eCorrelations_vs_MeandcaXY] = 0.2; - title_y_CorrelationsVs[eCorrelations_vs_MeandcaXY] = FancyFormatting(ph.fParticleHistogramsName[edcaXY].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeandcaXY] = 200; + minYCorrelationsVs[eCorrelations_vs_MeandcaXY] = -0.2; + maxYCorrelationsVs[eCorrelations_vs_MeandcaXY] = 0.2; + titleYCorrelationsVs[eCorrelations_vs_MeandcaXY] = fancyFormatting(ph.fParticleHistogramsName[edcaXY].Data()); // *) "eCorrelations_vs_MeandcaZ": - nBins_y_CorrelationsVs[eCorrelations_vs_MeandcaZ] = 200; - min_y_CorrelationsVs[eCorrelations_vs_MeandcaZ] = -0.2; - max_y_CorrelationsVs[eCorrelations_vs_MeandcaZ] = 0.2; - title_y_CorrelationsVs[eCorrelations_vs_MeandcaZ] = FancyFormatting(ph.fParticleHistogramsName[edcaZ].Data()); + nBinsYCorrelationsVs[eCorrelations_vs_MeandcaZ] = 200; + minYCorrelationsVs[eCorrelations_vs_MeandcaZ] = -0.2; + maxYCorrelationsVs[eCorrelations_vs_MeandcaZ] = 0.2; + titleYCorrelationsVs[eCorrelations_vs_MeandcaZ] = fancyFormatting(ph.fParticleHistogramsName[edcaZ].Data()); // ..... - // *) Quick insanity check on title_x_CorrelationsVs and title_y_CorrelationsVs: - if (title_x_CorrelationsVs.EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_x_CorrelationsVs is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__); + // *) Quick insanity check on titleXCorrelationsVs and titleYCorrelationsVs: + if (titleXCorrelationsVs.EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleXCorrelationsVs is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__); } for (int t = 0; t < eQACorrelationsVsHistograms2D_N; t++) { - if (title_y_CorrelationsVs[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_y_CorrelationsVs[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleYCorrelationsVs[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleYCorrelationsVs[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } } @@ -4630,21 +4636,21 @@ void bookQAHistograms() continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } // valgrind --tool=massif => ~58.7 MiB (Last check: 20250315) qa.fQACorrelationsVsHistograms2D[t][h][rs] = new TH2F( TString::Format("fQACorrelationsVsHistograms2D[%s][%d][%s]", qa.fQACorrelationsVsHistogramsName2D[t].Data(), h, gc.srs[rs].Data()), - TString::Format("%s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data()), // __RUN_NUMBER__ is handled in PropagateRunNumber(...) - nBins_x_CorrelationsVs, min_x_CorrelationsVs, max_x_CorrelationsVs, nBins_y_CorrelationsVs[t], min_y_CorrelationsVs[t], max_y_CorrelationsVs[t]); + TString::Format("%s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data()), // __RUN_NUMBER__ is handled in propagateRunNumber(...) + nBinsXCorrelationsVs, minXCorrelationsVs, maxXCorrelationsVs, nBinsYCorrelationsVs[t], minYCorrelationsVs[t], maxYCorrelationsVs[t]); - qa.fQACorrelationsVsHistograms2D[t][h][rs]->GetXaxis()->SetTitle(TString::Format("%s (harmonic = %d)", title_x_CorrelationsVs.Data(), h + 1)); - qa.fQACorrelationsVsHistograms2D[t][h][rs]->GetYaxis()->SetTitle(title_y_CorrelationsVs[t].Data()); + qa.fQACorrelationsVsHistograms2D[t][h][rs]->GetXaxis()->SetTitle(TString::Format("%s (harmonic = %d)", titleXCorrelationsVs.Data(), h + 1)); + qa.fQACorrelationsVsHistograms2D[t][h][rs]->GetYaxis()->SetTitle(titleYCorrelationsVs[t].Data()); qa.fQACorrelationsVsHistograms2D[t][h][rs]->SetLineColor(ec.fBeforeAfterColor[eAfter]); qa.fQACorrelationsVsHistograms2D[t][h][rs]->SetFillColor(ec.fBeforeAfterColor[eAfter] - 10); qa.fQACorrelationsVsHistograms2D[t][h][rs]->SetOption("col"); @@ -4659,86 +4665,86 @@ void bookQAHistograms() // Binning of 2D"correlations vs. IR vs. " profiles: // Remark: I use the same binning for x axis (it's interaction rate) for all 2D QA profiles in this category, therefore here implementation in shorter. - int nBins_x_CorrelationsVsInteractionRateVs = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); - double min_x_CorrelationsVsInteractionRateVs = eh.fEventHistogramsBins[eInteractionRate][1]; - double max_x_CorrelationsVsInteractionRateVs = eh.fEventHistogramsBins[eInteractionRate][2]; - TString title_x_CorrelationsVsInteractionRateVs = "interaction rate"; - int nBins_y_CorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {0}; - double min_y_CorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {0.}; - double max_y_CorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {0.}; - TString title_y_CorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {""}; + int nBinsXCorrelationsVsInteractionRateVs = static_cast(eh.fEventHistogramsBins[eInteractionRate][0] / qa.fRebin); + double minXCorrelationsVsInteractionRateVs = eh.fEventHistogramsBins[eInteractionRate][1]; + double maxXCorrelationsVsInteractionRateVs = eh.fEventHistogramsBins[eInteractionRate][2]; + TString titleXCorrelationsVsInteractionRateVs = "interaction rate"; + int nBinsYCorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {0}; + double minYCorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {0.}; + double maxYCorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {0.}; + TString titleYCorrelationsVsInteractionRateVs[eQACorrelationsVsInteractionRateVsProfiles2D_N] = {""}; // *) "eCorrelationsVsInteractionRate_vs_CurrentRunDuration": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0] / qa.fRebin); - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = FancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = static_cast(eh.fEventHistogramsBins[eCurrentRunDuration][0] / qa.fRebin); + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = eh.fEventHistogramsBins[eCurrentRunDuration][1]; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = eh.fEventHistogramsBins[eCurrentRunDuration][2]; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_CurrentRunDuration] = fancyFormatting(eh.fEventHistogramsName[eCurrentRunDuration].Data()); // *) "eCorrelationsVsInteractionRate_vs_Multiplicity": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][1]; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][2]; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = FancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = static_cast(eh.fEventHistogramsBins[eMultiplicity][0] / qa.fRebin); + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][1]; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = eh.fEventHistogramsBins[eMultiplicity][2]; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Multiplicity] = fancyFormatting(eh.fEventHistogramsName[eMultiplicity].Data()); // *) "eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = FancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = static_cast(eh.fEventHistogramsBins[eReferenceMultiplicity][0] / qa.fRebin); + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][1]; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = eh.fEventHistogramsBins[eReferenceMultiplicity][2]; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_ReferenceMultiplicity] = fancyFormatting(eh.fEventHistogramsName[eReferenceMultiplicity].Data()); // *) "eCorrelationsVsInteractionRate_vs_Centrality": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = FancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = static_cast(eh.fEventHistogramsBins[eCentrality][0]); + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][1]; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = eh.fEventHistogramsBins[eCentrality][2]; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_Centrality] = fancyFormatting(eh.fEventHistogramsName[eCentrality].Data()); // ..... // *) "eCorrelationsVsInteractionRate_vs_MeanPhi": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = 200; - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = 0.; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = o2::constants::math::TwoPI; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = FancyFormatting(ph.fParticleHistogramsName[ePhi].Data()); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = 200; + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = 0.; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = o2::constants::math::TwoPI; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPhi] = fancyFormatting(ph.fParticleHistogramsName[ePhi].Data()); // *) "eCorrelationsVsInteractionRate_vs_SigmaMeanPhi": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = 200; - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = 0.; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = 1.0; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = TString::Format("#sigma_{%s}", FancyFormatting(ph.fParticleHistogramsName[ePhi].Data())); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = 200; + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = 0.; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = 1.0; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPhi] = TString::Format("#sigma_{%s}", fancyFormatting(ph.fParticleHistogramsName[ePhi].Data())); // *) "eCorrelationsVsInteractionRate_vs_MeanPt": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = 200; - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = 0.0; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = 2.0; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = FancyFormatting(ph.fParticleHistogramsName[ePt].Data()); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = 200; + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = 0.0; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = 2.0; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanPt] = fancyFormatting(ph.fParticleHistogramsName[ePt].Data()); // *) "eCorrelationsVsInteractionRate_vs_SigmaMeanPt": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = 200; - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = 0.; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = 1.0; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = TString::Format("#sigma_{%s}", FancyFormatting(ph.fParticleHistogramsName[ePt].Data())); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = 200; + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = 0.; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = 1.0; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanPt] = TString::Format("#sigma_{%s}", fancyFormatting(ph.fParticleHistogramsName[ePt].Data())); // *) "eCorrelationsVsInteractionRate_vs_MeanEta": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = 600; - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = -0.5; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = 0.5; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = FancyFormatting(ph.fParticleHistogramsName[eEta].Data()); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = 600; + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = -0.5; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = 0.5; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_MeanEta] = fancyFormatting(ph.fParticleHistogramsName[eEta].Data()); // *) "eCorrelationsVsInteractionRate_vs_SigmaMeanEta": - nBins_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = 200; - min_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = 0.; - max_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = 1.0; - title_y_CorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = TString::Format("#sigma_{%s}", FancyFormatting(ph.fParticleHistogramsName[eEta].Data())); + nBinsYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = 200; + minYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = 0.; + maxYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = 1.0; + titleYCorrelationsVsInteractionRateVs[eCorrelationsVsInteractionRate_vs_SigmaMeanEta] = TString::Format("#sigma_{%s}", fancyFormatting(ph.fParticleHistogramsName[eEta].Data())); // ..... - // *) Quick insanity check on title_x_CorrelationsVsInteractionRateVs and title_y_CorrelationsVsInteractionRateVs: - if (title_x_CorrelationsVsInteractionRateVs.EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_x_CorrelationsVsInteractionRateVs is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__); + // *) Quick insanity check on titleXCorrelationsVsInteractionRateVs and titleYCorrelationsVsInteractionRateVs: + if (titleXCorrelationsVsInteractionRateVs.EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleXCorrelationsVsInteractionRateVs is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__); } for (int t = 0; t < eQACorrelationsVsInteractionRateVsProfiles2D_N; t++) { - if (title_y_CorrelationsVsInteractionRateVs[t].EqualTo("")) { - LOGF(fatal, "\033[1;31m%s at line %d : title_y_CorrelationsVsInteractionRateVs[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); + if (titleYCorrelationsVsInteractionRateVs[t].EqualTo("")) { + LOGF(fatal, "\033[1;31m%s at line %d : titleYCorrelationsVsInteractionRateVs[%d] is not set, check corresponding enum \033[0m", __FUNCTION__, __LINE__, t); } } @@ -4755,23 +4761,23 @@ void bookQAHistograms() continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } // TBI 20250317 documet here the output of profiling using valgrind --tool=massif qa.fQACorrVsIRVsProfiles2D[t][h][rs] = new TProfile2D( TString::Format("fQACorrVsIRVsProfiles2D[%s][%d][%s]", qa.fQACorrelationsVsInteractionRateVsProfilesName2D[t].Data(), h, gc.srs[rs].Data()), - TString::Format("%s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data()), // __RUN_NUMBER__ is handled in PropagateRunNumber(...) - nBins_x_CorrelationsVsInteractionRateVs, min_x_CorrelationsVsInteractionRateVs, max_x_CorrelationsVsInteractionRateVs, nBins_y_CorrelationsVsInteractionRateVs[t], min_y_CorrelationsVsInteractionRateVs[t], max_y_CorrelationsVsInteractionRateVs[t]); + TString::Format("%s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data()), // __RUN_NUMBER__ is handled in propagateRunNumber(...) + nBinsXCorrelationsVsInteractionRateVs, minXCorrelationsVsInteractionRateVs, maxXCorrelationsVsInteractionRateVs, nBinsYCorrelationsVsInteractionRateVs[t], minYCorrelationsVsInteractionRateVs[t], maxYCorrelationsVsInteractionRateVs[t]); TString tmp = qa.fQACorrVsIRVsProfiles2D[t][h][rs]->GetTitle(); // translating e.g. "544114, reconstructed" into "<<2>> (harmonic = 2), 544114, reconstructed" qa.fQACorrVsIRVsProfiles2D[t][h][rs]->SetTitle(TString::Format("#LT#LT2#GT#GT (harmonic = %d), %s", h + 1, tmp.Data())); - qa.fQACorrVsIRVsProfiles2D[t][h][rs]->GetXaxis()->SetTitle(title_x_CorrelationsVsInteractionRateVs.Data()); - qa.fQACorrVsIRVsProfiles2D[t][h][rs]->GetYaxis()->SetTitle(title_y_CorrelationsVsInteractionRateVs[t].Data()); + qa.fQACorrVsIRVsProfiles2D[t][h][rs]->GetXaxis()->SetTitle(titleXCorrelationsVsInteractionRateVs.Data()); + qa.fQACorrVsIRVsProfiles2D[t][h][rs]->GetYaxis()->SetTitle(titleYCorrelationsVsInteractionRateVs[t].Data()); qa.fQACorrVsIRVsProfiles2D[t][h][rs]->SetLineColor(ec.fBeforeAfterColor[eAfter]); qa.fQACorrVsIRVsProfiles2D[t][h][rs]->SetFillColor(ec.fBeforeAfterColor[eAfter] - 10); qa.fQACorrVsIRVsProfiles2D[t][h][rs]->SetOption("col"); @@ -4785,7 +4791,7 @@ void bookQAHistograms() // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookQAHistograms() @@ -4801,7 +4807,7 @@ void bookEventHistograms() // c) Book specific event histograms 2D. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -4845,19 +4851,19 @@ void bookEventHistograms() // b) Book specific control event histograms 1D: // ... - for (int t = 0; t < eEventHistograms_N; t++) // type, see enum eEventHistograms + for (int t = 0; t < eEventHistograms_N; t++) // type, see enum EEventHistograms { if (!eh.fBookEventHistograms[t]) { continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { // Special treatment for eMultiplicity => I will never fill this one before the cuts, if Multiplicity = SelectedTracks, obviously: @@ -4867,19 +4873,19 @@ void bookEventHistograms() } eh.fEventHistograms[t][rs][ba] = new TH1F( TString::Format("fEventHistograms[%s][%s][%s]", eh.fEventHistogramsName[t].Data(), gc.srs[rs].Data(), gc.sba[ba].Data()), - TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in PropagateRunNumber(...) + TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), // __RUN_NUMBER__ is handled in propagateRunNumber(...) static_cast(eh.fEventHistogramsBins[t][0]), eh.fEventHistogramsBins[t][1], eh.fEventHistogramsBins[t][2]); - eh.fEventHistograms[t][rs][ba]->GetXaxis()->SetTitle(FancyFormatting(eh.fEventHistogramsName[t].Data())); + eh.fEventHistograms[t][rs][ba]->GetXaxis()->SetTitle(fancyFormatting(eh.fEventHistogramsName[t].Data())); eh.fEventHistograms[t][rs][ba]->SetLineColor(ec.fBeforeAfterColor[ba]); eh.fEventHistograms[t][rs][ba]->SetFillColor(ec.fBeforeAfterColor[ba] - 10); eh.fEventHistogramsList->Add(eh.fEventHistograms[t][rs][ba]); } // for(int ba=0;ba<2;ba++) } // for(int rs=0;rs<2;rs++) // reco/sim - } // for(int t=0;tAdd(ec.fEventCutsPro); // b) Book event cut counter maps: - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { // If I am analyzing only reconstructed data, do not book maps for simulated, and vice versa. if ((tc.fProcess[eGenericRec] && rs == eSim) || (tc.fProcess[eGenericSim] && rs == eRec)) { @@ -4956,10 +4962,10 @@ void bookEventCutsHistograms() // c) Book event cut counter histograms: // ... - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } @@ -4976,12 +4982,12 @@ void bookEventCutsHistograms() ec.fEventCutCounterHist[rs][cc]->SetFillColor(eFillColor); ec.fEventCutCounterHist[rs][cc]->GetXaxis()->SetLabelSize(0.025); - // Remark: Bin labels are set later in a dry call to EventCuts, to accomodate sequential event cut counting + // Remark: Bin labels are set later in a dry call to eventCuts, to accomodate sequential event cut counting ec.fEventCutsList->Add(ec.fEventCutCounterHist[rs][cc]); } // for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int rs = 0; rs < grsN; rs++) // reco/sim // d) Book the formulas for all event cuts defined via mathematical expressions: @@ -5001,9 +5007,9 @@ void bookEventCutsHistograms() // Algorithm: 1. ec.fsEventCuts[eRefMultVsNContrUp].Data() is linear expression in the mandatory format "p0 + p1*x". // 2. Then I extract p0 and p1, and store them in float fdEventCutsFormulas[eEventCutsFormulas_N][2] = {{0.}} - // 3. Those values are then used in temporary function float RefMultVsNContr(const float &refMult, ...) + // 3. Those values are then used in temporary function float refMultVsNContr(const float &refMult, ...) - this->GetP0P1(ec.fsEventCuts[eRefMultVsNContrUp].ReplaceAll(" ", ""), eRefMultVsNContrUp_Formula); + this->getP0P1(ec.fsEventCuts[eRefMultVsNContrUp].ReplaceAll(" ", ""), eRefMultVsNContrUp_Formula); } // else @@ -5024,20 +5030,20 @@ void bookEventCutsHistograms() // This is a workaround to evaluate upper boundary of HMO cut, until large-memory consumption with TFormula is resolved. // Algorithm: See above for eRefMultVsNContrUp - this->GetP0P1(ec.fsEventCuts[eRefMultVsNContrLow].ReplaceAll(" ", ""), eRefMultVsNContrLow_Formula); + this->getP0P1(ec.fsEventCuts[eRefMultVsNContrLow].ReplaceAll(" ", ""), eRefMultVsNContrLow_Formula); } } // if (ec.fUseEventCuts[eRefMultVsNContrLow]) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookEventCutsHistograms() //============================================================ -void GetP0P1(const char* formula, eEventCutsFormulas whichCutFormula) +void getP0P1(const char* formula, EnEventCutsFormulas whichCutFormula) { // This is a sort of temporary parser, which extracts from linear expression p0 + p1*x coefficients p0 and p1, and stores them into float fdEventCutsFormulas[eEventCutsFormulas_N][2] = {{0.}} // Remark #0: I need all this gym until large memory consumption with TFormula is resolved; @@ -5045,26 +5051,26 @@ void GetP0P1(const char* formula, eEventCutsFormulas whichCutFormula) // Remark #2: If I need to go beyond p1, use recursion instead. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } TString signP0 = ""; TString signP1 = ""; - TString P0 = ""; - TString P1 = ""; + TString p0 = ""; + TString p1 = ""; - // P0 signature parser: + // p0 signature parser: int offset = 0; if (TString(formula[0]).EqualTo("-")) { signP0 = "-"; offset = 1; } - // P0 parser + P1 signature parser: + // p0 parser + p1 signature parser: int c1 = 0; for (int c = 0 + offset; c < static_cast(strlen(formula)); c++) { if (!(TString(formula[c]).EqualTo("-") || TString(formula[c]).EqualTo("+") || TString(formula[c]).EqualTo("*"))) { - P0 += formula[c]; + p0 += formula[c]; continue; } else { signP1 = formula[c]; @@ -5072,41 +5078,41 @@ void GetP0P1(const char* formula, eEventCutsFormulas whichCutFormula) break; } } - // P1 parser: + // p1 parser: for (int c = c1 + 1; c < static_cast(strlen(formula)); c++) { if (TString(formula[c]).EqualTo("-") || TString(formula[c]).EqualTo("+") || TString(formula[c]).EqualTo("*")) { break; } - P1 += formula[c]; + p1 += formula[c]; } // Okay, finally merge signature and value, cast into floats and store: - signP0 += P0; + signP0 += p0; ec.fdEventCutsFormulas[whichCutFormula][0] = signP0.Atof(); - signP1 += P1; + signP1 += p1; ec.fdEventCutsFormulas[whichCutFormula][1] = signP1.Atof(); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void GetP0P1(const char *formula, eEventCutsFormulas whichCutFormula) +} // void getP0P1(const char *formula, EnEventCutsFormulas whichCutFormula) //============================================================ -float RefMultVsNContr(const float& refMult, eEventCutsFormulas whichCutFormula) +float refMultVsNContr(const float& refMult, EnEventCutsFormulas whichCutFormula) { // Temporary workaround for p0 + p1 * x formula until large memory consumption with TFormula is resolved. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Evaluate p0 + p1 * x: return ec.fdEventCutsFormulas[whichCutFormula][0] + (ec.fdEventCutsFormulas[whichCutFormula][1]) * refMult; -} // float RefMultVsNContr(const float &refMult, eEventCutsFormulas whichCutFormula) +} // float refMultVsNContr(const float &refMult, EnEventCutsFormulas whichCutFormula) //============================================================ @@ -5121,7 +5127,7 @@ void bookParticleHistograms() // e) Book specific particle sparse histograms (n-dimensions). if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -5170,10 +5176,10 @@ void bookParticleHistograms() if (!ph.fBookParticleHistograms[t]) { continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } @@ -5184,19 +5190,19 @@ void bookParticleHistograms() } } - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { ph.fParticleHistograms[t][rs][ba] = new TH1F(TString::Format("fParticleHistograms[%s][%s][%s]", ph.fParticleHistogramsName[t].Data(), gc.srs[rs].Data(), gc.sba[ba].Data()), TString::Format("%s, %s, %s", "__RUN_NUMBER__", gc.srsLong[rs].Data(), gc.sbaLong[ba].Data()), static_cast(ph.fParticleHistogramsBins[t][0]), ph.fParticleHistogramsBins[t][1], ph.fParticleHistogramsBins[t][2]); ph.fParticleHistograms[t][rs][ba]->SetLineColor(ec.fBeforeAfterColor[ba]); ph.fParticleHistograms[t][rs][ba]->SetFillColor(ec.fBeforeAfterColor[ba] - 10); - ph.fParticleHistograms[t][rs][ba]->GetXaxis()->SetTitle(FancyFormatting(ph.fParticleHistogramsName[t].Data())); + ph.fParticleHistograms[t][rs][ba]->GetXaxis()->SetTitle(fancyFormatting(ph.fParticleHistogramsName[t].Data())); ph.fParticleHistograms[t][rs][ba]->SetMinimum(1.e-4); // so that I can switch to log scale, even if some bins are empty // Remark: For empty histograms, when plotting interactively, because of this line, I will get // E-TCanvas::Range: illegal world coordinates range .... // But it's harmless, because in any case I do not care about the content of empty histogram... - ph.fParticleHistograms[t][rs][ba]->SetOption("hist"); // do not plot marker and error (see BanishmentLoopOverParticles why errors are not reliable) for each bin, only content + filled area. + ph.fParticleHistograms[t][rs][ba]->SetOption("hist"); // do not plot marker and error (see banishmentLoopOverParticles why errors are not reliable) for each bin, only content + filled area. ph.fParticleHistogramsList->Add(ph.fParticleHistograms[t][rs][ba]); } // for(int ba=0;ba<2;ba++) } // for(int rs=0;rs<2;rs++) // reco/sim @@ -5204,8 +5210,8 @@ void bookParticleHistograms() // c) Book specific particle histograms 2D: // keep ordering in sync. with enum eParticleHistograms2D - TString stitleX2D[] = {FancyFormatting(ph.fParticleHistogramsName[ePhi].Data()), FancyFormatting(ph.fParticleHistogramsName[ePhi].Data())}; - TString stitleY2D[] = {FancyFormatting(ph.fParticleHistogramsName[ePt].Data()), FancyFormatting(ph.fParticleHistogramsName[eEta].Data())}; + TString stitleX2D[] = {fancyFormatting(ph.fParticleHistogramsName[ePhi].Data()), fancyFormatting(ph.fParticleHistogramsName[ePhi].Data())}; + TString stitleY2D[] = {fancyFormatting(ph.fParticleHistogramsName[ePt].Data()), fancyFormatting(ph.fParticleHistogramsName[eEta].Data())}; if (sizeof(stitleX2D) / sizeof(stitleX2D[0]) != eParticleHistograms2D_N) { LOGF(info, "\033[1;31m mismatch - add same number of names for 2D particle histograms as you have data members \033[0m"); @@ -5225,20 +5231,20 @@ void bookParticleHistograms() if (!ph.fBookParticleHistograms2D[t]) { continue; } - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { // optional variable-length binning for y-axis (for supported observables): if (ph.fParticleHistogramsName2D[t].EqualTo("Phi_vs_Pt") && res.fUseResultsProVariableLengthBins[AFO_PT]) { - // Remark: placeholder __RUN_NUMBER__ is handled in PropagateRunNumber(...) + // Remark: placeholder __RUN_NUMBER__ is handled in propagateRunNumber(...) // *) variable-length binning for phi vs pt, but only in pt axis: ph.fParticleHistograms2D[t][rs][ba] = new TH2D(TString::Format("fParticleHistograms2D[%s][%s][%s]", ph.fParticleHistogramsName2D[t].Data(), gc.srs[rs].Data(), gc.sba[ba].Data()), @@ -5290,7 +5296,7 @@ void bookParticleHistograms() // Keep this convention in sync with what I am doing in the macro MakeWeightsFromSparse(...) // Remark 5: Do not mix below eh.fEventHistograms and ph.fParticleHistograms - // **) eDiffWeightCategory = eDWPhi: + // **) EnDiffWeightCategory = eDWPhi: TAxis* lAxis = NULL; // local helper TAxis, to convert in one line the booking of fixed-length array into array of corresponding bin edges @@ -5303,7 +5309,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPhi][wPhiPhiAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiPhiAxis] = FancyFormatting("Phi"); + ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiPhiAxis] = fancyFormatting("Phi"); // ***) pt-axis for diff phi weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPhi][wPhiPtAxis] = static_cast(ph.fParticleHistogramsBins[ePt][0] / ph.fRebinSparse[eDWPhi][wPhiPtAxis]); @@ -5314,7 +5320,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPhi][wPhiPtAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiPtAxis] = FancyFormatting("Pt"); + ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiPtAxis] = fancyFormatting("Pt"); // ***) eta-axis for diff phi weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPhi][wPhiEtaAxis] = static_cast(ph.fParticleHistogramsBins[eEta][0] / ph.fRebinSparse[eDWPhi][wPhiEtaAxis]); @@ -5325,7 +5331,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPhi][wPhiEtaAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiEtaAxis] = FancyFormatting("Eta"); + ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiEtaAxis] = fancyFormatting("Eta"); // ***) charge-axis for diff phi weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPhi][wPhiChargeAxis] = static_cast(ph.fParticleHistogramsBins[eCharge][0] / ph.fRebinSparse[eDWPhi][wPhiChargeAxis]); @@ -5336,7 +5342,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPhi][wPhiChargeAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiChargeAxis] = FancyFormatting("Charge"); + ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiChargeAxis] = fancyFormatting("Charge"); // ***) centrality-axis for diff phi weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPhi][wPhiCentralityAxis] = static_cast(eh.fEventHistogramsBins[eCentrality][0] / ph.fRebinSparse[eDWPhi][wPhiCentralityAxis]); @@ -5347,7 +5353,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPhi][wPhiCentralityAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here FancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. + ph.fParticleSparseHistogramsAxisTitle[eDWPhi][wPhiCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here fancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. // ***) VertexZ-axis for diff phi weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPhi][wPhiVertexZAxis] = static_cast(eh.fEventHistogramsBins[eVertexZ][0] / ph.fRebinSparse[eDWPhi][wPhiVertexZAxis]); @@ -5362,7 +5368,7 @@ void bookParticleHistograms() // ... - // **) eDiffWeightCategory = eDWPt: + // **) EnDiffWeightCategory = eDWPt: // ***) pt-axis for diff pt weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPt][wPtPtAxis] = static_cast(ph.fParticleHistogramsBins[ePt][0] / ph.fRebinSparse[eDWPt][wPtPtAxis]); @@ -5373,7 +5379,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPt][wPtPtAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtPtAxis] = FancyFormatting("Pt"); + ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtPtAxis] = fancyFormatting("Pt"); // ***) eta-axis for diff pt weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPt][wPtEtaAxis] = static_cast(ph.fParticleHistogramsBins[eEta][0] / ph.fRebinSparse[eDWPt][wPtEtaAxis]); @@ -5384,7 +5390,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPt][wPtEtaAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtEtaAxis] = FancyFormatting("Eta"); + ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtEtaAxis] = fancyFormatting("Eta"); // ***) charge-axis for diff pt weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPt][wPtChargeAxis] = static_cast(ph.fParticleHistogramsBins[eCharge][0] / ph.fRebinSparse[eDWPt][wPtChargeAxis]); @@ -5395,7 +5401,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPt][wPtChargeAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtChargeAxis] = FancyFormatting("Charge"); + ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtChargeAxis] = fancyFormatting("Charge"); // ***) centrality-axis for diff pt weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWPt][wPtCentralityAxis] = static_cast(eh.fEventHistogramsBins[eCentrality][0] / ph.fRebinSparse[eDWPt][wPtCentralityAxis]); @@ -5406,11 +5412,22 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWPt][wPtCentralityAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here FancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. + ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here fancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. + + // ***) vertexZ-axis for diff pt weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: + ph.fParticleSparseHistogramsNBins[eDWPt][wPtVertexZAxis] = static_cast(eh.fEventHistogramsBins[eVertexZ][0] / ph.fRebinSparse[eDWPt][wPtVertexZAxis]); + lAxis = new TAxis(ph.fParticleSparseHistogramsNBins[eDWPt][wPtVertexZAxis], eh.fEventHistogramsBins[eVertexZ][1], eh.fEventHistogramsBins[eVertexZ][2]); + ph.fParticleSparseHistogramsBinEdges[eDWPt][wPtVertexZAxis] = new TArrayD(1 + ph.fParticleSparseHistogramsNBins[eDWPt][wPtVertexZAxis]); + for (int bin = 1; bin <= lAxis->GetNbins(); bin++) { + ph.fParticleSparseHistogramsBinEdges[eDWPt][wPtVertexZAxis]->AddAt(lAxis->GetBinLowEdge(bin), bin - 1); + } + ph.fParticleSparseHistogramsBinEdges[eDWPt][wPtVertexZAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin + delete lAxis; + ph.fParticleSparseHistogramsAxisTitle[eDWPt][wPtVertexZAxis] = "VertexZ"; // ... - // **) eDiffWeightCategory = eDWEta: + // **) EnDiffWeightCategory = eDWEta: // ***) eta-axis for diff eta weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWEta][wEtaEtaAxis] = static_cast(ph.fParticleHistogramsBins[eEta][0] / ph.fRebinSparse[eDWEta][wEtaEtaAxis]); @@ -5421,7 +5438,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWEta][wEtaEtaAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaEtaAxis] = FancyFormatting("Eta"); + ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaEtaAxis] = fancyFormatting("Eta"); // ***) pt-axis for diff eta weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWEta][wEtaPtAxis] = static_cast(ph.fParticleHistogramsBins[ePt][0] / ph.fRebinSparse[eDWEta][wEtaPtAxis]); @@ -5432,7 +5449,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWEta][wEtaPtAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaPtAxis] = FancyFormatting("Pt"); + ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaPtAxis] = fancyFormatting("Pt"); // ***) charge-axis for diff eta weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWEta][wEtaChargeAxis] = static_cast(ph.fParticleHistogramsBins[eCharge][0] / ph.fRebinSparse[eDWEta][wEtaChargeAxis]); @@ -5443,7 +5460,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWEta][wEtaChargeAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaChargeAxis] = FancyFormatting("Charge"); + ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaChargeAxis] = fancyFormatting("Charge"); // ***) centrality-axis for diff eta weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWEta][wEtaCentralityAxis] = static_cast(eh.fEventHistogramsBins[eCentrality][0] / ph.fRebinSparse[eDWEta][wEtaCentralityAxis]); @@ -5454,11 +5471,21 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWEta][wEtaCentralityAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here FancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. + ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here fancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. + // ***) vertexZ-axis for diff eta weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: + ph.fParticleSparseHistogramsNBins[eDWEta][wEtaVertexZAxis] = static_cast(eh.fEventHistogramsBins[eVertexZ][0] / ph.fRebinSparse[eDWEta][wEtaVertexZAxis]); + lAxis = new TAxis(ph.fParticleSparseHistogramsNBins[eDWEta][wEtaVertexZAxis], eh.fEventHistogramsBins[eVertexZ][1], eh.fEventHistogramsBins[eVertexZ][2]); + ph.fParticleSparseHistogramsBinEdges[eDWEta][wEtaVertexZAxis] = new TArrayD(1 + ph.fParticleSparseHistogramsNBins[eDWEta][wEtaVertexZAxis]); + for (int bin = 1; bin <= lAxis->GetNbins(); bin++) { + ph.fParticleSparseHistogramsBinEdges[eDWEta][wEtaVertexZAxis]->AddAt(lAxis->GetBinLowEdge(bin), bin - 1); + } + ph.fParticleSparseHistogramsBinEdges[eDWEta][wEtaVertexZAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin + delete lAxis; + ph.fParticleSparseHistogramsAxisTitle[eDWEta][wEtaVertexZAxis] = "VertexZ"; // ... - // **) eDiffWeightCategory = eDWCharge: + // **) EnDiffWeightCategory = eDWCharge: // ***) charge-axis for diff charge weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWCharge][wChargeChargeAxis] = static_cast(ph.fParticleHistogramsBins[eCharge][0] / ph.fRebinSparse[eDWCharge][wChargeChargeAxis]); @@ -5469,7 +5496,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWCharge][wChargeChargeAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargeChargeAxis] = FancyFormatting("charge"); + ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargeChargeAxis] = fancyFormatting("charge"); // ***) pt-axis for diff charge weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWCharge][wChargePtAxis] = static_cast(ph.fParticleHistogramsBins[ePt][0] / ph.fRebinSparse[eDWCharge][wChargePtAxis]); @@ -5480,7 +5507,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWCharge][wChargePtAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargePtAxis] = FancyFormatting("Pt"); + ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargePtAxis] = fancyFormatting("Pt"); // ***) eta-axis for diff charge weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWCharge][wChargeEtaAxis] = static_cast(ph.fParticleHistogramsBins[eEta][0] / ph.fRebinSparse[eDWCharge][wChargeEtaAxis]); @@ -5491,7 +5518,7 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWCharge][wChargeEtaAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargeEtaAxis] = FancyFormatting("Eta"); + ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargeEtaAxis] = fancyFormatting("Eta"); // ***) centrality-axis for diff charge weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: ph.fParticleSparseHistogramsNBins[eDWCharge][wChargeCentralityAxis] = static_cast(eh.fEventHistogramsBins[eCentrality][0] / ph.fRebinSparse[eDWCharge][wChargeCentralityAxis]); @@ -5502,41 +5529,52 @@ void bookParticleHistograms() } ph.fParticleSparseHistogramsBinEdges[eDWCharge][wChargeCentralityAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin delete lAxis; - ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargeCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here FancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. + ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargeCentralityAxis] = "Centrality"; // TBI 20250222 I cannot call here fancyFormatting for "Centrality", because ec.fsEventCuts[eCentralityEstimator] is still not fetched and set from configurable. Re-think how to proceed for this specific case. + + // ***) vertexZ-axis for diff charge weights - at the moment I support only fixed-length binning from control histograms, which optionally can be made finer or coarser with cfRebinSparse configurable: + ph.fParticleSparseHistogramsNBins[eDWCharge][wChargeVertexZAxis] = static_cast(eh.fEventHistogramsBins[eVertexZ][0] / ph.fRebinSparse[eDWCharge][wChargeVertexZAxis]); + lAxis = new TAxis(ph.fParticleSparseHistogramsNBins[eDWCharge][wChargeVertexZAxis], eh.fEventHistogramsBins[eVertexZ][1], eh.fEventHistogramsBins[eVertexZ][2]); + ph.fParticleSparseHistogramsBinEdges[eDWCharge][wChargeVertexZAxis] = new TArrayD(1 + ph.fParticleSparseHistogramsNBins[eDWCharge][wChargeVertexZAxis]); + for (int bin = 1; bin <= lAxis->GetNbins(); bin++) { + ph.fParticleSparseHistogramsBinEdges[eDWCharge][wChargeVertexZAxis]->AddAt(lAxis->GetBinLowEdge(bin), bin - 1); + } + ph.fParticleSparseHistogramsBinEdges[eDWCharge][wChargeVertexZAxis]->AddAt(lAxis->GetBinLowEdge(1 + lAxis->GetNbins()), lAxis->GetNbins()); // special treatment for last bin + delete lAxis; + ph.fParticleSparseHistogramsAxisTitle[eDWCharge][wChargeVertexZAxis] = "VertexZ"; // ... // e) Book specific particle sparse histograms (n-dimensions): if (ph.fBookParticleSparseHistograms[eDWPhi]) { - BookParticleSparseHistograms(eDWPhi); + bookParticleSparseHistograms(eDWPhi); } if (ph.fBookParticleSparseHistograms[eDWPt]) { - BookParticleSparseHistograms(eDWPt); + bookParticleSparseHistograms(eDWPt); } if (ph.fBookParticleSparseHistograms[eDWEta]) { - BookParticleSparseHistograms(eDWEta); + bookParticleSparseHistograms(eDWEta); } if (ph.fBookParticleSparseHistograms[eDWCharge]) { - BookParticleSparseHistograms(eDWCharge); + bookParticleSparseHistograms(eDWCharge); } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookParticleHistograms() //============================================================ -void BookParticleSparseHistograms(eDiffWeightCategory dwc) +void bookParticleSparseHistograms(EnDiffWeightCategory dwc) { // This is a helper function for bookParticleHistograms(), merely to reduce code bloat. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Determine number of dimensions for sparse histogram for this differential weight category: @@ -5571,13 +5609,13 @@ void BookParticleSparseHistograms(eDiffWeightCategory dwc) } // *) Book THnSparse with correct number of bins for each dimension, but void bin edges: - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (eBefore == ba && !ph.fFillParticleSparseHistogramsBeforeCuts) { @@ -5596,18 +5634,18 @@ void BookParticleSparseHistograms(eDiffWeightCategory dwc) // *) Finally, add the fully configured THnSparse to its TList: ph.fParticleHistogramsList->Add(ph.fParticleSparseHistograms[dwc][rs][ba]); - } // for (int ba = 0; ba < 2; ba++) // before/after cuts + } // for (int ba = 0; ba < gbaN; ba++) // before/after cuts - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int rs = 0; rs < grsN; rs++) // reco/sim // *) Clean up: delete nBins; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void BookParticleSparseHistograms() +} // void bookParticleSparseHistograms() //============================================================ @@ -5621,7 +5659,7 @@ void bookParticleCutsHistograms() // d) Book the formula for pt-dependent DCAxy cut. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -5667,7 +5705,7 @@ void bookParticleCutsHistograms() pc.fParticleCutsList->Add(pc.fParticleCutsPro); // b) Book particle cut counter maps: - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { // If I am analyzing only reconstructed data, do not book maps for simulated, and vice versa. if ((tc.fProcess[eGenericRec] && rs == eSim) || (tc.fProcess[eGenericSim] && rs == eRec)) { @@ -5679,10 +5717,10 @@ void bookParticleCutsHistograms() // c) Book the particle cut counter (absolute): // ... - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - if (Skip(rs)) { + if (skip(rs)) { continue; } @@ -5700,12 +5738,12 @@ void bookParticleCutsHistograms() pc.fParticleCutCounterHist[rs][cc]->SetLineColor(eColor); pc.fParticleCutCounterHist[rs][cc]->SetFillColor(eFillColor); pc.fParticleCutCounterHist[rs][cc]->GetXaxis()->SetLabelSize(0.025); - // Remark: Bin labels are set later in a dry call to ParticleCuts, to accomodate sequential particle cut counting + // Remark: Bin labels are set later in a dry call to particleCuts, to accomodate sequential particle cut counting pc.fParticleCutsList->Add(pc.fParticleCutCounterHist[rs][cc]); } // for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int rs = 0; rs < grsN; rs++) // reco/sim // d) Book the formula for pt-dependent DCAxy cut: if (pc.fUseParticleCuts[ePtDependentDCAxyParameterization]) { @@ -5720,7 +5758,7 @@ void bookParticleCutsHistograms() } // if(pc.fUseParticleCuts[ePtDependentDCAxyParameterization]) { if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookParticleCutsHistograms() @@ -5737,7 +5775,7 @@ void bookQvectorHistograms() // d) ... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -5799,19 +5837,19 @@ void bookQvectorHistograms() if (qv.fCalculateqvectorsKineAny) { qv.fNumberOfKineBins.resize(eqvectorKine_N); // this is the light object, so I can hardwire here eqvectorKine_N // then, in NumberOfKineVectors() i will store bins ONLY for qvectorKine which were requested. - // Therefore, final ordering in will correspond to the ordering in enum eqvectorKine ONLY if all kine vectors were requested. + // Therefore, final ordering in will correspond to the ordering in enum EnqvectorKine ONLY if all kine vectors were requested. // Otherwise, I fill here bins only for kine vectors which were requested, in consequtive order, starting from 0. int dim1 = eqvectorKine_N; // int dim2 = number of kine bins => I calculate this one dynamically for each qVectorKine, see the loop below - NumberOfKineBins(); // here I calculate and fill qv.fNumberOfKineBins[ ... ] + numberOfKineBins(); // here I calculate and fill qv.fNumberOfKineBins[ ... ] int dim3 = gMaxHarmonic * gMaxCorrelator + 1; // TBI 20250601 I could dinamically allocate this one as well, but this will trigger another major re-design, and it's not rally a big deal int dim4 = gMaxCorrelator + 1; // TBI 20250601 I could dinamically allocate this one as well, but this will trigger another major re-design, and it's not really a big deal qv.fqvector.resize(dim1); qv.fqvectorEntries.resize(dim1); - for (int i = 0; i < dim1; ++i) { // here I am looping over entries in enum eqvectorKine + for (int i = 0; i < dim1; ++i) { // here I am looping over entries in enum EnqvectorKine if (qv.fCalculateqvectorsKine[i]) { qv.fqvector[i].resize(qv.fNumberOfKineBins[i]); // yes, qv.fNumberOfKineBins[i] => for each qvectorkine I calculate and dynamically allocate only necessary bins qv.fqvectorEntries[i].resize(qv.fNumberOfKineBins[i]); @@ -5845,20 +5883,20 @@ void bookQvectorHistograms() // here I am calculating for each dimensions how many entries I need in a given analysis if (es.fCalculateEtaSeparations) { - int dim1 = 2; // -eta or eta - int dim2 = eqvectorKine_N; - // int dim3 = number of kine bins => I calculated this one dynamically for each qVectorKine, see the loop below - // NumberOfKineBins(); // here I calculate and fill qv.fNumberOfKineBins[ ... ] => I did it already above for qv.fqvector - int dim4 = gMaxHarmonic; - int dim5 = gMaxNumberEtaSeparations; - qv.fqabVector.resize(dim1); - qv.fmab.resize(dim1); + int esDim1 = 2; // -eta or eta + int esDim2 = eqvectorKine_N; + // int esDim3 = number of kine bins => I calculated this one dynamically for each qVectorKine, see the loop below + // numberOfKineBins(); // here I calculate and fill qv.fNumberOfKineBins[ ... ] => I did it already above for qv.fqvector + int esDim4 = gMaxHarmonic; + int esDim5 = gMaxNumberEtaSeparations; + qv.fqabVector.resize(esDim1); + qv.fmab.resize(esDim1); - for (int i = 0; i < dim1; ++i) { // here I am looping over -eta or eta - qv.fqabVector[i].resize(dim2); - qv.fmab[i].resize(dim2); + for (int i = 0; i < esDim1; ++i) { // here I am looping over -eta or eta + qv.fqabVector[i].resize(esDim2); + qv.fmab[i].resize(esDim2); - for (int j = 0; j < dim2; ++j) { // here I am looping over entries in enum eqvectorKine + for (int j = 0; j < esDim2; ++j) { // here I am looping over entries in enum EnqvectorKine if (qv.fCalculateqvectorsKineEtaSeparations[j]) { qv.fqabVector[i][j].resize(qv.fNumberOfKineBins[j]); // yes, qv.fNumberOfKineBins[j] => for each qvectorkine I calculate and dynamically allocate only necessary bins @@ -5871,42 +5909,42 @@ void bookQvectorHistograms() for (int k = 0; k < qv.fNumberOfKineBins[j]; ++k) { if (qv.fCalculateqvectorsKineEtaSeparations[j]) { - qv.fqabVector[i][j][k].resize(dim4); - qv.fmab[i][j][k].resize(dim5); // yes, directly dim5, because this one doesn't depend on harmonics + qv.fqabVector[i][j][k].resize(esDim4); + qv.fmab[i][j][k].resize(esDim5); // yes, directly esDim5, because this one doesn't depend on harmonics } else { // calculus for this kine variable is not needed, I am ironing out this dimension // I have already in the previous loop ironed out for qv.fCalculateqvectorsKineEtaSeparations[j] = false, so no need to do it here again // TBI 20250620 validate what happens here } - for (int l = 0; l < dim4; ++l) { // loop over harmonics + for (int l = 0; l < esDim4; ++l) { // loop over harmonics if (qv.fCalculateqvectorsKineEtaSeparations[j] && !es.fEtaSeparationsSkipHarmonics[l]) { - qv.fqabVector[i][j][k][l].resize(dim5); + qv.fqabVector[i][j][k][l].resize(esDim5); // no need to resize qv.fmab here, because this one doesn't depend on harmonics } else { // calculus for this kine variable is not needed, I am ironing out this dimension // I have already in the previous loop ironed out for qv.fCalculateqvectorsKineEtaSeparations[j] = false, so no need to do it here again // TBI 20250620 validate what happens here } - } // for (int l = 0; l < dim4; ++l) + } // for (int l = 0; l < esDim4; ++l) } // for (int k = 0; k < qv.fNumberOfKineBins[j]; ++k) - } // for (int j = 0; j < dim2; ++j) - } // for (int i = 0; i < dim1; ++i) + } // for (int j = 0; j < esDim2; ++j) + } // for (int i = 0; i < esDim1; ++i) } // if(es.fCalculateEtaSeparations) } // if(qv.fCalculateqvectorsKineAny) // c) Book multiplicity distributions in A and B, for each eta separation: if (es.fCalculateEtaSeparations) { - TString sEtaSep[2] = {"A", "B"}; // A <=> -eta , B <=> + eta - TString sEtaSepLong[2] = {TString::Format("%.2f < #eta <", pc.fdParticleCuts[eEta][eMin]), TString::Format("< #eta < %.2f", pc.fdParticleCuts[eEta][eMax])}; + TString sEtaSep[gewN] = {"A", "B"}; // A <=> -eta , B <=> + eta + TString sEtaSepLong[gewN] = {TString::Format("%.2f < #eta <", pc.fdParticleCuts[eEta][eMin]), TString::Format("< #eta < %.2f", pc.fdParticleCuts[eEta][eMax])}; // yes, here I define first the part of intervals as etaCutMin < eta < "subevent boundary", and "subevent" boundary < eta < etaCutMax // Then below in the loop, I inject for "subevent boundary" the corresponding fEtaSeparationsValues (divided by 2, becaus it's symmetric round 0) - for (int ab = 0; ab < 2; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta - for (int rs = 0; rs < 2; rs++) { // reco/sim - if (Skip(rs)) { + for (int ab = 0; ab < gewN; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta + for (int rs = 0; rs < grsN; rs++) { // reco/sim + if (skip(rs)) { continue; } - for (int ba = 0; ba < 2; ba++) { // before/after cuts + for (int ba = 0; ba < gbaN; ba++) { // before/after cuts if (eBefore == ba) { continue; // it make sense to fill these histos only for "eAfter", because Q-vectors are not filled for "eBefore" } @@ -5922,7 +5960,7 @@ void bookQvectorHistograms() // Remark: For empty histograms, when plotting interactively, because of this line, I will get // E-TCanvas::Range: illegal world coordinates range .... // But it's harmless, because in any case I do not care about the content of empty histogram... - qv.fMabDist[ab][rs][ba][e]->SetOption("hist"); // do not plot marker and error (see BanishmentLoopOverParticles why errors are not reliable) for each bin, only content + filled area. + qv.fMabDist[ab][rs][ba][e]->SetOption("hist"); // do not plot marker and error (see banishmentLoopOverParticles why errors are not reliable) for each bin, only content + filled area. qv.fQvectorList->Add(qv.fMabDist[ab][rs][ba][e]); } } @@ -5933,20 +5971,20 @@ void bookQvectorHistograms() // c) ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookQvectorHistograms() //============================================================ -void NumberOfKineBins() +void numberOfKineBins() { // Helper function called only in void bookQvectorHistograms(), if kine analysis was requested. // I calculate for each requested kine vector the number of kine bins => this is stored in dynamically allocated array qv.fNumberOfKineBins. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // 1D kine: @@ -5963,27 +6001,27 @@ void NumberOfKineBins() // 2D kine: if (qv.fCalculateqvectorsKine[PT_ETAq]) { - qv.fNumberOfKineBins[PT_ETAq] = (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + qv.fNumberOfKineBins[PT_ETAq] = (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below } if (qv.fCalculateqvectorsKine[PT_CHARGEq]) { - qv.fNumberOfKineBins[PT_CHARGEq] = (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + qv.fNumberOfKineBins[PT_CHARGEq] = (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below } if (qv.fCalculateqvectorsKine[ETA_CHARGEq]) { - qv.fNumberOfKineBins[ETA_CHARGEq] = (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + qv.fNumberOfKineBins[ETA_CHARGEq] = (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below } // ... // 3D kine: if (qv.fCalculateqvectorsKine[PT_ETA_CHARGEq]) { - qv.fNumberOfKineBins[PT_ETA_CHARGEq] = (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsY() + 2) * (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + qv.fNumberOfKineBins[PT_ETA_CHARGEq] = (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsY() + 2) * (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below } // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void NumberOfKineBins() +} // void numberOfKineBins() //============================================================ @@ -5997,7 +6035,7 @@ void bookCorrelationsHistograms() // d) Few quick insanity checks on booking. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -6044,15 +6082,14 @@ void bookCorrelationsHistograms() } // b) Common local labels: - TString oVariable[4] = { + TString oVariable[gOrderIsotropic] = { "#varphi_{1}-#varphi_{2}", "#varphi_{1}+#varphi_{2}-#varphi_{3}-#varphi_{4}", "#varphi_{1}+#varphi_{2}+#varphi_{3}-#varphi_{4}-#varphi_{5}-#varphi_{6}", - "#varphi_{1}+#varphi_{2}+#varphi_{3}+#varphi_{4}-#varphi_{5}-#varphi_{6}-" - "#varphi_{7}-#varphi_{8}"}; + "#varphi_{1}+#varphi_{2}+#varphi_{3}+#varphi_{4}-#varphi_{5}-#varphi_{6}-#varphi_{7}-#varphi_{8}"}; // c) Histograms: - for (int k = 0; k < 4; k++) // order [2p=0,4p=1,6p=2,8p=3] + for (int k = 0; k < gOrderIsotropic; k++) // order [2p=0,4p=1,6p=2,8p=3] { for (int n = 0; n < gMaxHarmonic; n++) // harmonic { @@ -6075,23 +6112,23 @@ void bookCorrelationsHistograms() mupa.fCorrelationsPro[k][n][v]->SetStats(false); mupa.fCorrelationsPro[k][n][v]->Sumw2(); - mupa.fCorrelationsPro[k][n][v]->GetXaxis()->SetTitle(FancyFormatting(res.fResultsProXaxisTitle[v].Data())); + mupa.fCorrelationsPro[k][n][v]->GetXaxis()->SetTitle(fancyFormatting(res.fResultsProXaxisTitle[v].Data())); mupa.fCorrelationsPro[k][n][v]->GetYaxis()->SetTitle(Form("#LT#LTcos[%s(%s)]#GT#GT", 1 == n + 1 ? "" : Form("%d", n + 1), oVariable[k].Data())); mupa.fCorrelationsList->Add(mupa.fCorrelationsPro[k][n][v]); } } // for (int n = 0; n < gMaxHarmonic; n++) // harmonic - } // for (int k = 0; k < 4; k++) // order [2p=0,4p=1,6p=2,8p=3] + } // for (int k = 0; k < gOrderIsotropic; k++) // order [2p=0,4p=1,6p=2,8p=3] // d) Few quick insanity checks on booking: if (mupa.fCorrelationsPro[0][0][AFO_INTEGRATED] && !TString(mupa.fCorrelationsPro[0][0][AFO_INTEGRATED]->GetXaxis()->GetTitle()).EqualTo("integrated")) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (mupa.fCorrelationsPro[0][0][AFO_PT] && !TString(mupa.fCorrelationsPro[0][0][AFO_PT]->GetXaxis()->GetTitle()).EqualTo("p_{T}")) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // bookCorrelationsHistograms() @@ -6108,11 +6145,11 @@ void bookWeightsHistograms() // d) Sparse histograms for differential phi weights. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: - pw.fWeightsFlagsPro = new TProfile("fWeightsFlagsPro", "flags for particle weights", 23, 0., 23.); + pw.fWeightsFlagsPro = new TProfile("fWeightsFlagsPro", "flags for particle weights", 26, 0., 26.); pw.fWeightsFlagsPro->SetStats(false); pw.fWeightsFlagsPro->SetLineColor(eColor); pw.fWeightsFlagsPro->SetFillColor(eFillColor); @@ -6123,8 +6160,8 @@ void bookWeightsHistograms() pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(1, "w_{#varphi}"); pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(2, "w_{p_{t}}"); pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(3, "w_{#eta}"); - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(4, "(w_{#varphi})_{| p_{T}}"); // TBI 20241019 not sure if this is the final notation, keep in sync with void SetDiffWeightsHist(...) - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(5, "(w_{#varphi})_{| #eta}"); // TBI 20241019 not sure if this is the final notation, keep in sync with void SetDiffWeightsHist(...) + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(4, "(w_{#varphi})_{| p_{T}}"); // TBI 20241019 not sure if this is the final notation, keep in sync with void setDiffWeightsHist(...) + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(5, "(w_{#varphi})_{| #eta}"); // TBI 20241019 not sure if this is the final notation, keep in sync with void setDiffWeightsHist(...) // **) differential phi weights using sparse (keep in sync with enum eDiffPhiWeights): pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(6, "(w_{#varphi})_{phi axis (sparse)}"); @@ -6139,18 +6176,21 @@ void bookWeightsHistograms() pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(13, "(w_{p_{T}})_{eta axis (sparse)}"); pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(14, "(w_{p_{T}})_{charge axis (sparse)}"); pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(15, "(w_{p_{T}})_{centrality axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(16, "(w_{p_{T}})_{VertexZ axis (sparse)}"); // **) differential eta weights using sparse (keep in sync with enum eDiffEtaWeights): - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(16, "(w_{#eta})_{eta axis (sparse)}"); - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(17, "(w_{#eta})_{pt axis (sparse)}"); - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(18, "(w_{#eta})_{charge axis (sparse)}"); - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(19, "(w_{#eta})_{centrality axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(17, "(w_{#eta})_{eta axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(18, "(w_{#eta})_{pt axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(19, "(w_{#eta})_{charge axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(20, "(w_{#eta})_{centrality axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(21, "(w_{#eta})_{VertexZ axis (sparse)}"); // **) differential charge weights using sparse (keep in sync with enum eDiffChargeWeights): - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(20, "(w_{charge})_{charge axis (sparse)}"); - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(21, "(w_{charge})_{pt axis (sparse)}"); - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(22, "(w_{charge})_{eta axis (sparse)}"); - pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(23, "(w_{charge})_{centrality axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(22, "(w_{charge})_{charge axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(23, "(w_{charge})_{pt axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(24, "(w_{charge})_{eta axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(25, "(w_{charge})_{centrality axis (sparse)}"); + pw.fWeightsFlagsPro->GetXaxis()->SetBinLabel(26, "(w_{charge})_{VertexZ axis (sparse)}"); } else { @@ -6176,18 +6216,21 @@ void bookWeightsHistograms() yAxisTitle += TString::Format("%d:(w_{p_{T}})_{eta axis (sparse)}; ", 13); yAxisTitle += TString::Format("%d:(w_{p_{T}})_{charge axis (sparse)}; ", 14); yAxisTitle += TString::Format("%d:(w_{p_{T}})_{centrality axis (sparse)}; ", 15); + yAxisTitle += TString::Format("%d:(w_{p_{T}})_{VertexZ axis (sparse)}; ", 16); // **) differential eta weights using sparse (keep in sync with enum eDiffEtaWeights): - yAxisTitle += TString::Format("%d:(w_{#eta})_{eta axis (sparse)}; ", 16); - yAxisTitle += TString::Format("%d:(w_{#eta})_{pt axis (sparse)}; ", 17); - yAxisTitle += TString::Format("%d:(w_{#eta})_{charge axis (sparse)}; ", 18); - yAxisTitle += TString::Format("%d:(w_{#eta})_{centrality axis (sparse)}; ", 19); + yAxisTitle += TString::Format("%d:(w_{#eta})_{eta axis (sparse)}; ", 17); + yAxisTitle += TString::Format("%d:(w_{#eta})_{pt axis (sparse)}; ", 18); + yAxisTitle += TString::Format("%d:(w_{#eta})_{charge axis (sparse)}; ", 19); + yAxisTitle += TString::Format("%d:(w_{#eta})_{centrality axis (sparse)}; ", 20); + yAxisTitle += TString::Format("%d:(w_{#eta})_{VertexZ axis (sparse)}; ", 21); // **) differential charge weights using sparse (keep in sync with enum eDiffChargeWeights): - yAxisTitle += TString::Format("%d:(w_{charge})_{charge axis (sparse)}; ", 20); - yAxisTitle += TString::Format("%d:(w_{charge})_{pt axis (sparse)}; ", 21); - yAxisTitle += TString::Format("%d:(w_{charge})_{eta axis (sparse)}; ", 22); - yAxisTitle += TString::Format("%d:(w_{charge})_{centrality axis (sparse)}; ", 23); + yAxisTitle += TString::Format("%d:(w_{charge})_{charge axis (sparse)}; ", 22); + yAxisTitle += TString::Format("%d:(w_{charge})_{pt axis (sparse)}; ", 23); + yAxisTitle += TString::Format("%d:(w_{charge})_{eta axis (sparse)}; ", 24); + yAxisTitle += TString::Format("%d:(w_{charge})_{centrality axis (sparse)}; ", 25); + yAxisTitle += TString::Format("%d:(w_{charge})_{VertexZ axis (sparse)}; ", 26); // ... @@ -6253,33 +6296,42 @@ void bookWeightsHistograms() if (pw.fUseDiffPtWeights[wPtCentralityAxis]) { pw.fWeightsFlagsPro->Fill(14.5, 1.); } + if (pw.fUseDiffPtWeights[wPtVertexZAxis]) { + pw.fWeightsFlagsPro->Fill(15.5, 1.); + } // **) differential eta weights using sparse: if (pw.fUseDiffEtaWeights[wEtaEtaAxis]) { - pw.fWeightsFlagsPro->Fill(15.5, 1.); + pw.fWeightsFlagsPro->Fill(16.5, 1.); } if (pw.fUseDiffEtaWeights[wEtaPtAxis]) { - pw.fWeightsFlagsPro->Fill(16.5, 1.); + pw.fWeightsFlagsPro->Fill(17.5, 1.); } if (pw.fUseDiffEtaWeights[wEtaChargeAxis]) { - pw.fWeightsFlagsPro->Fill(17.5, 1.); + pw.fWeightsFlagsPro->Fill(18.5, 1.); } if (pw.fUseDiffEtaWeights[wEtaCentralityAxis]) { - pw.fWeightsFlagsPro->Fill(18.5, 1.); + pw.fWeightsFlagsPro->Fill(19.5, 1.); + } + if (pw.fUseDiffEtaWeights[wEtaVertexZAxis]) { + pw.fWeightsFlagsPro->Fill(20.5, 1.); } // **) differential charge weights using sparse: if (pw.fUseDiffChargeWeights[wChargeChargeAxis]) { - pw.fWeightsFlagsPro->Fill(19.5, 1.); + pw.fWeightsFlagsPro->Fill(21.5, 1.); } if (pw.fUseDiffChargeWeights[wChargePtAxis]) { - pw.fWeightsFlagsPro->Fill(20.5, 1.); + pw.fWeightsFlagsPro->Fill(22.5, 1.); } if (pw.fUseDiffChargeWeights[wChargeEtaAxis]) { - pw.fWeightsFlagsPro->Fill(21.5, 1.); + pw.fWeightsFlagsPro->Fill(23.5, 1.); } if (pw.fUseDiffChargeWeights[wChargeCentralityAxis]) { - pw.fWeightsFlagsPro->Fill(22.5, 1.); + pw.fWeightsFlagsPro->Fill(24.5, 1.); + } + if (pw.fUseDiffChargeWeights[wChargeVertexZAxis]) { + pw.fWeightsFlagsPro->Fill(25.5, 1.); } pw.fWeightsList->Add(pw.fWeightsFlagsPro); @@ -6296,7 +6348,7 @@ void bookWeightsHistograms() // Same comment applies as for b) => add sparse histograms to the list, only after they are cloned from external files. if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookWeightsHistograms() @@ -6311,7 +6363,7 @@ void bookCentralityWeightsHistograms() // b) Histograms for centrality weights. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -6359,7 +6411,7 @@ void bookCentralityWeightsHistograms() // Therefore, add histos with centrality weights to this list only after they are cloned from external files. if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookCentralityWeightsHistograms() @@ -6376,7 +6428,7 @@ void bookNestedLoopsHistograms() // d) Few quick insanity checks on booking. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -6476,9 +6528,9 @@ void bookNestedLoopsHistograms() // 2D kine: // **) vs. (pt,eta): - if (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]) { + if (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]->GetNbinsY() + 2); + nBins = (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[PT_ETAq][b][0] = new TArrayD(iMaxSize); @@ -6487,9 +6539,9 @@ void bookNestedLoopsHistograms() } // **) vs. (pt,charge): - if (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]) { + if (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]->GetNbinsY() + 2); + nBins = (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[PT_CHARGEq][b][0] = new TArrayD(iMaxSize); @@ -6498,9 +6550,9 @@ void bookNestedLoopsHistograms() } // **) vs. (eta,charge): - if (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]) { + if (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]->GetNbinsY() + 2); + nBins = (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[ETA_CHARGEq][b][0] = new TArrayD(iMaxSize); @@ -6512,9 +6564,9 @@ void bookNestedLoopsHistograms() // 3D kine: // **) vs. (pt,eta,charge): - if (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]) { + if (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsY() + 2) * (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsZ() + 2); + nBins = (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsY() + 2) * (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[PT_ETA_CHARGEq][b][0] = new TArrayD(iMaxSize); @@ -6526,19 +6578,18 @@ void bookNestedLoopsHistograms() } // if (nl.fCalculateKineCustomNestedLoops) - // b) Common local labels (keep 'em in sync with bookCorrelationsHistograms()) - TString oVariable[4] = { + // b) Common local labels (keep 'em in sync with bookCorrelationsHistograms()): + TString oVariable[gOrderIsotropic] = { "#varphi_{1}-#varphi_{2}", "#varphi_{1}+#varphi_{2}-#varphi_{3}-#varphi_{4}", "#varphi_{1}+#varphi_{2}+#varphi_{3}-#varphi_{4}-#varphi_{5}-#varphi_{6}", - "#varphi_{1}+#varphi_{2}+#varphi_{3}+#varphi_{4}-#varphi_{5}-#varphi_{6}-" - "#varphi_{7}-#varphi_{8}"}; + "#varphi_{1}+#varphi_{2}+#varphi_{3}+#varphi_{4}-#varphi_{5}-#varphi_{6}-#varphi_{7}-#varphi_{8}"}; // c) Book what needs to be booked: if (!(nl.fCalculateNestedLoops)) { // TBI 20240404 for the time being, I can keep it here, but eventualy it will have to go elsewhere return; } - for (int k = 0; k < 4; k++) // order [2p=0,4p=1,6p=2,8p=3] + for (int k = 0; k < gOrderIsotropic; k++) // order [2p=0,4p=1,6p=2,8p=3] { // TBI 20240405 I could break here, with respect to what nl.fMaxNestedLoop was set to @@ -6569,20 +6620,20 @@ void bookNestedLoopsHistograms() } // for(int v=0;v<5;v++) // variable [0=integrated,1=vs. // multiplicity,2=vs. centrality] } // for (int n = 0; n < gMaxHarmonic; n++) // harmonic - } // for (int k = 0; k < 4; k++) // order [2p=0,4p=1,6p=2,8p=3] + } // for (int k = 0; k < gOrderIsotropic; k++) // order [2p=0,4p=1,6p=2,8p=3] // d) Few quick insanity checks on booking: if (nl.fNestedLoopsPro[0][0][AFO_INTEGRATED] && !TString(nl.fNestedLoopsPro[0][0][AFO_INTEGRATED]->GetXaxis()->GetTitle()).EqualTo("integrated")) { LOGF(info, "\033[1;33mnl.fNestedLoopsPro[0][0][AFO_INTEGRATED]->GetXaxis()->GetTitle() = %s \033[0m", nl.fNestedLoopsPro[0][0][AFO_INTEGRATED]->GetXaxis()->GetTitle()); - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (nl.fNestedLoopsPro[0][0][AFO_PT] && !TString(nl.fNestedLoopsPro[0][0][AFO_PT]->GetXaxis()->GetTitle()).EqualTo("pt")) { // I do not need here fancy formatting LOGF(info, "\033[1;33mnl.fNestedLoopsPro[0][0][AFO_PT]->GetXaxis()->GetTitle() = %s \033[0m", nl.fNestedLoopsPro[0][0][AFO_PT]->GetXaxis()->GetTitle()); - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookNestedLoopsHistograms() @@ -6598,7 +6649,7 @@ void bookNUAHistograms() // c) Histograms. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -6682,7 +6733,7 @@ void bookNUAHistograms() } // b) Common local labels: - TString sVariable[eNUAPDF_N] = {"#varphi", "p_{t}", "#eta", "charge"}; // has to be in sync with the ordering of enum eNUAPDF + TString sVariable[eNUAPDF_N] = {"#varphi", "p_{t}", "#eta", "charge"}; // has to be in sync with the ordering of enum EnNUAPDF // c) Histograms: for (int pdf = 0; pdf < eNUAPDF_N; pdf++) // use pdfs for NUA in (phi, pt, eta, charge, ...) @@ -6770,7 +6821,7 @@ void bookNUAHistograms() nua.fNUAList->Add(nua.fCustomNUAPDF[pdf]); } // if(!nua.fCustomNUAPDF[pdf]) - // Get the max values of pdfs, so that later in Accept(...) there is no loss of efficiency, when I would need to calculate the same thing for each particle: + // Get the max values of pdfs, so that later in accept(...) there is no loss of efficiency, when I would need to calculate the same thing for each particle: if (!nua.fUseDefaultNUAPDF[pdf] && nua.fCustomNUAPDF[pdf]) { // pdf is a loop variable // custom, user-provided pdf via TH1D object: nua.fMaxValuePDF[pdf] = nua.fCustomNUAPDF[pdf]->GetMaximum(); @@ -6782,7 +6833,7 @@ void bookNUAHistograms() } // for(int pdf=0;pdf check further @@ -6975,18 +7026,18 @@ void InternalValidation() // c) Delete persistent objects. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Set and propagate some fake run number: tc.fRunNumber = "123456"; - PropagateRunNumber(); + propagateRunNumber(); // *) Fetch the weights for this particular run number. Do it only once. // TBI 20231012 If eventualy I can access programatically run number in init(...) at run time, this shall go there. if (!pw.fParticleWeightsAreFetched) { if (pw.fUseWeights[wPHI] || pw.fUseWeights[wPT] || pw.fUseWeights[wETA] || pw.fUseDiffWeights[wPHIPT] || pw.fUseDiffWeights[wPHIETA]) { - GetParticleWeights(); + getParticleWeights(); pw.fParticleWeightsAreFetched = true; } @@ -6994,7 +7045,7 @@ void InternalValidation() if (pw.fUseDiffPhiWeights[wPhiPhiAxis]) { // Yes, I check only the first flag. This way, I can switch off all differential phi weights by setting 0-wPhi in config. // On the other hand, it doesn't make sense to calculate differential phi weights without having phi axis. // At any point I shall be able to fall back to integrated phi weights, that corresponds to the case wheh "1-wPhi" and all others are "0-w..." - GetParticleWeights(); + getParticleWeights(); pw.fParticleWeightsAreFetched = true; } @@ -7002,7 +7053,7 @@ void InternalValidation() if (pw.fUseDiffPtWeights[wPtPtAxis]) { // Yes, I check only the first flag. This way, I can switch off all differential pt weights by setting 0-wPt in config. // On the other hand, it doesn't make sense to calculate differential pt weights without having pt axis. // At any point I shall be able to fall back to integrated pt weights, that corresponds to the case wheh "1-wPt" and all others are "0-w..." - GetParticleWeights(); + getParticleWeights(); pw.fParticleWeightsAreFetched = true; } @@ -7010,7 +7061,7 @@ void InternalValidation() if (pw.fUseDiffEtaWeights[wEtaEtaAxis]) { // Yes, I check only the first flag. This way, I can switch off all differential eta weights by setting 0-wEta in config. // On the other hand, it doesn't make sense to calculate differential eta weights without having eta axis. // At any point I shall be able to fall back to integrated eta weights, that corresponds to the case wheh "1-wEta" and all others are "0-w..." - GetParticleWeights(); + getParticleWeights(); pw.fParticleWeightsAreFetched = true; } @@ -7018,7 +7069,7 @@ void InternalValidation() if (pw.fUseDiffChargeWeights[wChargeChargeAxis]) { // Yes, I check only the first flag. This way, I can switch off all differential Charge weights by setting 0-wCharge in config. // On the other hand, it doesn't make sense to calculate differential charge weights without having charge axis. // At any point I shall be able to fall back to integrated charge weights, that corresponds to the case wheh "1-wCharge" and all others are "0-w..." - GetParticleWeights(); + getParticleWeights(); pw.fParticleWeightsAreFetched = true; } @@ -7053,7 +7104,8 @@ void InternalValidation() if (tc.fVerbose) { LOGF(info, "=> This is initial configuration for p.d.f. used in internal validation:"); - for (int h = 0; h < 2 * gMaxHarmonic; h++) { + int nHarmonics = 2 * gMaxHarmonic; // "Avoid magic numbers in expressions" thingie... + for (int h = 0; h < nHarmonics; h++) { LOGF(info, Form("%d %s = %f", h, fPhiPDF->GetParName(h), fPhiPDF->GetParameter(h))); } LOGF(info, "Remark: Parameter [18] at the moment is reaction plane.\n"); @@ -7083,9 +7135,9 @@ void InternalValidation() } else if (iv.fHarmonicsOptionInternalValidation->EqualTo("persistent")) { // For this option, three selected vn's (v1,v2,v3) are correlated in the same way as in "correlated" case, but in addition, the persistent - // non-vanishing correlation among SPCs Psi1, Psi2 and Psi3 is introduced, in the same way as in arXiv:1901.06968, Sec. II D. + // non-vanishing correlation among SPCs psi1, psi2 and psi3 is introduced, in the same way as in arXiv:1901.06968, Sec. II D. - // Remark: In this example, there is no Reaction Plane, instead Psi1 and Psi2 are sampled uniformly, and the equation for Psi3 is hardwired, + // Remark: In this example, there is no Reaction Plane, instead psi1 and psi2 are sampled uniformly, and the equation for psi3 is hardwired, // to introduce strong and persistent SPC correlation, see arXiv:1901.06968, Sec. II D. // Keep also in sync with const int gMaxHarmonic = 9; in *GlobalConstants.h @@ -7095,9 +7147,9 @@ void InternalValidation() // [0] => v1 // [1] => v2 // [2] => v3 - // [3] => Psi1 - // [4] => Psi2 - // [5] => Psi3 + // [3] => psi1 + // [4] => psi2 + // [5] => psi3 fPhiPDF->SetParName(0, "v_{1}"); fPhiPDF->SetParName(1, "v_{2}"); fPhiPDF->SetParName(2, "v_{3}"); @@ -7167,7 +7219,7 @@ void InternalValidation() for (int e = 0; e < static_cast(iv.fnEventsInternalValidation); e++) { // b0) Reset ebye quantities: - ResetEventByEventQuantities(); + resetEventByEventQuantities(); // b1) Determine multiplicity, centrality, reaction plane and configure p.d.f. for azimuthal angles if harmonics are not constant e-by-e: int nMult = static_cast(gRandom->Uniform(iv.fMultRangeInternalValidation[eMin], iv.fMultRangeInternalValidation[eMax])); @@ -7196,11 +7248,14 @@ void InternalValidation() ebye.fVz = static_cast(gRandom->Uniform(-20., 20.)); // this is perfectly fine for this exercise ebye.fFT0CAmplitudeOnFoundBC = static_cast(gRandom->Uniform(0., 100000.)); // this is perfectly fine for this exercise ebye.fImpactParameter = static_cast(gRandom->Uniform(0., 20.)); // this is perfectly fine for this exercise + ebye.fReferenceMultiplicity = static_cast(nMult); // this is perfectly fine for this exercise // b2) Fill event histograms before cuts: if (eh.fFillEventHistograms) { !eh.fEventHistograms[eNumberOfEvents][eSim][eBefore] ? true : eh.fEventHistograms[eNumberOfEvents][eSim][eBefore]->Fill(0.5); - !eh.fEventHistograms[eTotalMultiplicity][eSim][eBefore] ? true : eh.fEventHistograms[eTotalMultiplicity][eSim][eBefore]->Fill(nMult); + !eh.fEventHistograms[eTotalMultiplicity][eSim][eBefore] ? true : eh.fEventHistograms[eTotalMultiplicity][eSim][eBefore]->Fill(nMult); // TBI 20260427 check if I can remove this line, after I added refMult in the line below + // Remark: I cannot fill here eh.fEventHistograms[eMultiplicity], because eMultiplicity can be number of selected tracks, which are determined only in the loop below + !eh.fEventHistograms[eReferenceMultiplicity][eSim][eBefore] ? true : eh.fEventHistograms[eReferenceMultiplicity][eSim][eBefore]->Fill(ebye.fReferenceMultiplicity); !eh.fEventHistograms[eCentrality][eSim][eBefore] ? true : eh.fEventHistograms[eCentrality][eSim][eBefore]->Fill(ebye.fCentrality); !eh.fEventHistograms[eOccupancy][eSim][eBefore] ? true : eh.fEventHistograms[eOccupancy][eSim][eBefore]->Fill(ebye.fOccupancy); !eh.fEventHistograms[eInteractionRate][eSim][eBefore] ? true : eh.fEventHistograms[eInteractionRate][eSim][eBefore]->Fill(ebye.fInteractionRate); @@ -7231,13 +7286,13 @@ void InternalValidation() fPhiPDF->SetParameter(2, v3); // Persistent symmetry plane correlation: - double Psi1 = gRandom->Uniform(0., o2::constants::math::TwoPI); - double Psi2 = gRandom->Uniform(0., o2::constants::math::TwoPI); - double Psi3 = (1. / 3.) * (o2::constants::math::PIQuarter + 2. * Psi2 + Psi1); // see arXiv:1901.06968, Sec. II D. + double psi1 = gRandom->Uniform(0., o2::constants::math::TwoPI); + double psi2 = gRandom->Uniform(0., o2::constants::math::TwoPI); + double psi3 = (1. / 3.) * (o2::constants::math::PIQuarter + 2. * psi2 + psi1); // see arXiv:1901.06968, Sec. II D. // o2::constants::math::PIQuarter = 0.25f * PI - fPhiPDF->SetParameter(3, Psi1); - fPhiPDF->SetParameter(4, Psi2); - fPhiPDF->SetParameter(5, Psi3); + fPhiPDF->SetParameter(3, psi1); + fPhiPDF->SetParameter(4, psi2); + fPhiPDF->SetParameter(5, psi3); // Remark: reaction plane is not needed for case "persistent" @@ -7245,10 +7300,10 @@ void InternalValidation() // b3) Loop over particles: // *) Define min and max ranges for sampling: - double dPt_min = res.fResultsPro[AFO_PT]->GetXaxis()->GetBinLowEdge(1); // yes, low edge of first bin is pt min - double dPt_max = res.fResultsPro[AFO_PT]->GetXaxis()->GetBinLowEdge(1 + res.fResultsPro[AFO_PT]->GetNbinsX()); // yes, low edge of overflow bin is max pt - double dEta_min = res.fResultsPro[AFO_ETA]->GetXaxis()->GetBinLowEdge(1); // yes, low edge of first bin is eta min - double dEta_max = res.fResultsPro[AFO_ETA]->GetXaxis()->GetBinLowEdge(1 + res.fResultsPro[AFO_ETA]->GetNbinsX()); // yes, low edge of overflow bin is max eta + double dPtMin = res.fResultsPro[AFO_PT]->GetXaxis()->GetBinLowEdge(1); // yes, low edge of first bin is pt min + double dPtMax = res.fResultsPro[AFO_PT]->GetXaxis()->GetBinLowEdge(1 + res.fResultsPro[AFO_PT]->GetNbinsX()); // yes, low edge of overflow bin is max pt + double dEtaMin = res.fResultsPro[AFO_ETA]->GetXaxis()->GetBinLowEdge(1); // yes, low edge of first bin is eta min + double dEtaMax = res.fResultsPro[AFO_ETA]->GetXaxis()->GetBinLowEdge(1 + res.fResultsPro[AFO_ETA]->GetNbinsX()); // yes, low edge of overflow bin is max eta for (int p = 0; p < nMult; p++) { // Remark 0: I have to sample first pt, eta, charge, only then phi, because I allow the possibility that some harmonics depend on kinematics, e.g. v2(pt) or v2(pt,eta), etc. @@ -7258,7 +7313,7 @@ void InternalValidation() t0.fCalculate2DTest0AsFunctionOf[AFO_CENTRALITY_PT] || t0.fCalculate2DTest0AsFunctionOf[AFO_PT_ETA] || t0.fCalculate2DTest0AsFunctionOf[AFO_PT_CHARGE] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_ETA] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_CHARGE] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_VZ] || t0.fCalculate3DTest0AsFunctionOf[AFO_PT_ETA_CHARGE]) { - pbyp.fPt = gRandom->Uniform(dPt_min, dPt_max); + pbyp.fPt = gRandom->Uniform(dPtMin, dPtMax); } if (mupa.fCalculateCorrelationsAsFunctionOf[AFO_ETA] || t0.fCalculateTest0AsFunctionOf[AFO_ETA] || es.fCalculateEtaSeparations || @@ -7266,7 +7321,7 @@ void InternalValidation() t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_ETA] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_CHARGE] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_VZ] || t0.fCalculate3DTest0AsFunctionOf[AFO_PT_ETA_CHARGE]) { // Yes, I have to use here es.fCalculateEtaSeparations , and not some differential flag, like for pt case above - pbyp.fEta = gRandom->Uniform(dEta_min, dEta_max); + pbyp.fEta = gRandom->Uniform(dEtaMin, dEtaMax); } if (mupa.fCalculateCorrelationsAsFunctionOf[AFO_CHARGE] || t0.fCalculateTest0AsFunctionOf[AFO_CHARGE] || es.fCalculateEtaSeparationsAsFunctionOf[AFO_CHARGE] || @@ -7291,18 +7346,20 @@ void InternalValidation() // pt dependence: for pt < fV2vsPtCutOff v2 increases linearly, for pt >= fV2vsPtCutOff v2 = fV2vsPtMax, see Eq. (32) in arXiv:1312.3572 // eta dependence is defined as 0.4 - (1/4) eta^2, so that v2(eta) = 0.24 at eta = +-0.8, and v2(eta) = 0.40 at eta = 0 (keep in sync with details above) // to increase significance, I multiply by factor of 2 the sampled v2(pt,eta) - float v2 = 0.; // this is the actual v2(pt,eta) for the current particle - pbyp.fPt < fV2vsPtCutOff ? v2 = 2. * (pbyp.fPt * fV2vsPtMax / fV2vsPtCutOff) * (0.4 - (1. / 4.) * pbyp.fEta * pbyp.fEta) : v2 = 2. * fV2vsPtMax * (0.4 - (1. / 4.) * pbyp.fEta * pbyp.fEta); - if (v2 < 0. || v2 > 0.5) { - LOGF(fatal, "\033[1;31m%s at line %d : v2 = %f\033[0m", __FUNCTION__, __LINE__, v2); + float v2Diff = 0.; // this is the actual v2(pt,eta) or even more differentially v2(pt,eta,charge) for the current particle + pbyp.fPt < fV2vsPtCutOff ? v2Diff = 2. * (pbyp.fPt * fV2vsPtMax / fV2vsPtCutOff) * (0.4 - (1. / 4.) * pbyp.fEta * pbyp.fEta) : v2Diff = 2. * fV2vsPtMax * (0.4 - (1. / 4.) * pbyp.fEta * pbyp.fEta); + float v2MinAllowed = 0.; + float v2MaxAllowed = 0.5; + if (v2Diff < v2MinAllowed || v2Diff > v2MaxAllowed) { + LOGF(fatal, "\033[1;31m%s at line %d : v2Diff = %f\033[0m", __FUNCTION__, __LINE__, v2Diff); } if (iv.fHarmonicsOptionInternalValidation->EqualTo("ptEtaDependent")) { - fPhiPDF->SetParameter(2, v2); // set v2(pt,eta) for this particle + fPhiPDF->SetParameter(2, v2Diff); // set v2(pt,eta) for this particle } else if (iv.fHarmonicsOptionInternalValidation->EqualTo("ptEtaChargeDependent")) { if (pbyp.fCharge > 0.) { - v2 *= 0.75; // trim down by 25% only v2 of positive particles in this toy model + v2Diff *= 0.75; // trim down by 25% only v2 of positive particles in this toy model } - fPhiPDF->SetParameter(2, v2); // set v2(pt,eta,charge) for this particle + fPhiPDF->SetParameter(2, v2Diff); // set v2(pt,eta,charge) for this particle } } @@ -7310,7 +7367,7 @@ void InternalValidation() pbyp.fPhi = fPhiPDF->GetRandom(); // *) Fill few selected particle histograms before cuts here directly: - // Remark: I do not call FillParticleHistograms(track, eBefore), as I do not want to bother to make here full 'track' object, etc., just to fill simple kine info: + // Remark: I do not call fillParticleHistograms(track, eBefore), as I do not want to bother to make here full 'track' object, etc., just to fill simple kine info: if (ph.fFillParticleHistograms || ph.fFillParticleHistograms2D) { // 1D: !ph.fParticleHistograms[ePhi][eSim][eBefore] ? true : ph.fParticleHistograms[ePhi][eSim][eBefore]->Fill(pbyp.fPhi); @@ -7332,20 +7389,20 @@ void InternalValidation() // **) eDWPt : here the fundamental 0-th axis never to be projected out is "pt" if (ph.fBookParticleSparseHistograms[eDWPt]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffPtWeights - double vector[eDiffPtWeights_N] = {pbyp.fPt, pbyp.fEta, pbyp.fCharge, ebye.fCentrality}; + double vector[eDiffPtWeights_N] = {pbyp.fPt, pbyp.fEta, pbyp.fCharge, ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWPt][eSim][eBefore]->Fill(vector); } // **) eDWEta : here the fundamental 0-th axis never to be projected out is "eta" if (ph.fBookParticleSparseHistograms[eDWEta]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffEtaWeights - double vector[eDiffEtaWeights_N] = {pbyp.fEta, pbyp.fPt, pbyp.fCharge, ebye.fCentrality}; + double vector[eDiffEtaWeights_N] = {pbyp.fEta, pbyp.fPt, pbyp.fCharge, ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWEta][eSim][eBefore]->Fill(vector); } // **) eDWCharge : here the fundamental 0-th axis never to be projected out is "charge" if (ph.fBookParticleSparseHistograms[eDWCharge]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffChargeWeights - double vector[eDiffChargeWeights_N] = {pbyp.fCharge, pbyp.fPt, pbyp.fEta, ebye.fCentrality}; + double vector[eDiffChargeWeights_N] = {pbyp.fCharge, pbyp.fPt, pbyp.fEta, ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWCharge][eSim][eBefore]->Fill(vector); } @@ -7377,21 +7434,21 @@ void InternalValidation() } // *) NUA: - if (nua.fApplyNUAPDF[ePhiNUAPDF] && !Accept(pbyp.fPhi, ePhiNUAPDF)) { + if (nua.fApplyNUAPDF[ePhiNUAPDF] && !accept(pbyp.fPhi, ePhiNUAPDF)) { continue; } - if (nua.fApplyNUAPDF[ePtNUAPDF] && !Accept(pbyp.fPt, ePtNUAPDF)) { + if (nua.fApplyNUAPDF[ePtNUAPDF] && !accept(pbyp.fPt, ePtNUAPDF)) { continue; } - if (nua.fApplyNUAPDF[eEtaNUAPDF] && !Accept(pbyp.fEta, eEtaNUAPDF)) { + if (nua.fApplyNUAPDF[eEtaNUAPDF] && !accept(pbyp.fEta, eEtaNUAPDF)) { continue; } - if (nua.fApplyNUAPDF[eChargeNUAPDF] && !Accept(pbyp.fCharge, eChargeNUAPDF)) { + if (nua.fApplyNUAPDF[eChargeNUAPDF] && !accept(pbyp.fCharge, eChargeNUAPDF)) { continue; } // *) Fill few selected particle histograms after cuts here directly here: - // Remark: I do not call FillParticleHistograms(track, eAfter), as I do not want to bother to make here full 'track' object, etc., just to fill simple kine info: + // Remark: I do not call fillParticleHistograms(track, eAfter), as I do not want to bother to make here full 'track' object, etc., just to fill simple kine info: if (ph.fFillParticleHistograms || ph.fFillParticleHistograms2D) { // 1D: !ph.fParticleHistograms[ePhi][eSim][eAfter] ? true : ph.fParticleHistograms[ePhi][eSim][eAfter]->Fill(pbyp.fPhi); @@ -7411,48 +7468,48 @@ void InternalValidation() // **) eDWPt : here the fundamental 0-th axis never to be projected out is "pt" if (ph.fBookParticleSparseHistograms[eDWPt]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffPtWeights - double vector[eDiffPtWeights_N] = {pbyp.fPt, pbyp.fEta, pbyp.fCharge, ebye.fCentrality}; + double vector[eDiffPtWeights_N] = {pbyp.fPt, pbyp.fEta, pbyp.fCharge, ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWPt][eSim][eAfter]->Fill(vector); } // **) eDWEta : here the fundamental 0-th axis never to be projected out is "eta" if (ph.fBookParticleSparseHistograms[eDWEta]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffEtaWeights - double vector[eDiffEtaWeights_N] = {pbyp.fEta, pbyp.fPt, pbyp.fCharge, ebye.fCentrality}; + double vector[eDiffEtaWeights_N] = {pbyp.fEta, pbyp.fPt, pbyp.fCharge, ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWEta][eSim][eAfter]->Fill(vector); } // **) eDWCharge : here the fundamental 0-th axis never to be projected out is "charge" if (ph.fBookParticleSparseHistograms[eDWCharge]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffChargeWeights - double vector[eDiffChargeWeights_N] = {pbyp.fCharge, pbyp.fPt, pbyp.fEta, ebye.fCentrality}; + double vector[eDiffChargeWeights_N] = {pbyp.fCharge, pbyp.fPt, pbyp.fEta, ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWCharge][eSim][eAfter]->Fill(vector); } } // if (ph.fFillParticleHistograms || ph.fFillParticleHistograms2D) - // Remark: Keep in sync all calls and flags below with the ones in MainLoopOverParticles(). + // Remark: Keep in sync all calls and flags below with the ones in mainLoopOverParticles(). // *) Integrated Q-vectors: if (qv.fCalculateQvectors || es.fCalculateEtaSeparations) { // This is now the new approach, with sparse histograms: // **) particle arguments are passed by reference // **) event observables (centrality, vertex z, ...), I do not need to pass as arguments, as I have data members for them (ebye.fCentrality, ebye.Vz, ...) - // **) I decide within FillQvectorFromSparse(...) whether and which weights are used. So yes, I use this one, despite its name, even when weights are NOT used + // **) I decide within fillQvectorFromSparse(...) whether and which weights are used. So yes, I use this one, despite its name, even when weights are NOT used // (there is no real performance penalty) - // **) Legacy function FillQvector(...) is obsolete as of 20250714, since I can get both integrated and differential wights from sparse histograms. - this->FillQvectorFromSparse(); + // **) Legacy function fillQvector(...) is obsolete as of 20250714, since I can get both integrated and differential wights from sparse histograms. + this->fillQvectorFromSparse(); } - // *) Differential q-vectors (keep in sync with the code in MainLoopOverParticles(...)): + // *) Differential q-vectors (keep in sync with the code in mainLoopOverParticles(...)): // TBI 20260210 I need here a flag if this calculus is needed at all - this->Fillqvectors(); // within this function, i call FillqvectorFromSparse(...), for each differential q-vector separately + this->fillqvectors(); // within this function, i call fillqvectorFromSparse(...), for each differential q-vector separately // *) Fill nested loops containers: if (nl.fCalculateNestedLoops || nl.fCalculateCustomNestedLoops) { - this->FillNestedLoopsContainers(ebye.fSelectedTracks); + this->fillNestedLoopsContainers(ebye.fSelectedTracks); } // *) Counter of selected tracks in the current event: - // Remark: This has to go after FillNestedLoopsContainers(...), because ebye.fSelectedTracks is used as a particle index there. + // Remark: This has to go after fillNestedLoopsContainers(...), because ebye.fSelectedTracks is used as a particle index there. ebye.fSelectedTracks++; if (ebye.fSelectedTracks >= ec.fdEventCuts[eMultiplicity][eMax]) { break; @@ -7461,7 +7518,7 @@ void InternalValidation() } // for(int p=0;p(); + fillSubeventMultiplicities(); } // b5) Calculate everything for selected events and particles: - CalculateEverything(); + calculateEverything(); // *) Reset event-by-event quantities: - ResetEventByEventQuantities(); + resetEventByEventQuantities(); // *) Print info on the current event number (within current real event): LOGF(info, " Event # %d/%d (within current real event, running internal validation) ....", e + 1, static_cast(iv.fnEventsInternalValidation)); // *) Determine all event counters: - DetermineEventCounters(); + determineEventCounters(); // *) Sequential bailout: After each tc.fSequentialBailout events, I bail out: if (iv.fInternalValidationForceBailout && tc.fSequentialBailout > 0 && eh.fEventCounter[eProcessed] > 0 && 0 == eh.fEventCounter[eProcessed] % tc.fSequentialBailout) { - BailOut(); + bailOut(); } // *) If I reached max number of events, ignore the remaining collisions: - if (MaxNumberOfEvents(eAfter)) { + if (maxNumberOfEvents(eAfter)) { if (iv.fInternalValidationForceBailout) { - BailOut(true); + bailOut(true); } } @@ -7510,7 +7567,7 @@ void InternalValidation() // *) Print info on the current event number (total): if (tc.fVerboseEventCounter) { - PrintEventCounter(eAfter); + printEventCounter(eAfter); } // c) Delete persistent objects: @@ -7522,20 +7579,20 @@ void InternalValidation() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void InternalValidation() +} // void internalValidation() //============================================================ -bool Accept(const double& value, int var) +bool accept(const double& value, int var) { // Given the acceptance profile for this observable, accept or not that observable for the analysis. // Use in Toy NUA studies. - // Remark: var corresponds to the field in enum eNUAPDF { ePhiNUAPDF, ePtNUAPDF, eEtaNUAPDF, eChargeNUAPDF }; - // Therefore, always call this function as e.g. Accept(someAngle, ePhiNUAPDF) or Accept(somePt, ePtNUAPDF), etc. + // Remark: var corresponds to the field in enum EnNUAPDF { ePhiNUAPDF, ePtNUAPDF, eEtaNUAPDF, eChargeNUAPDF }; + // Therefore, always call this function as e.g. accept(someAngle, ePhiNUAPDF) or accept(somePt, ePtNUAPDF), etc. if (tc.fVerboseForEachParticle) { LOGF(info, "\033[1;32m%s\033[0m", __FUNCTION__); @@ -7568,7 +7625,7 @@ bool Accept(const double& value, int var) return bAccept; -} // bool Accept(const double &value, int var) +} // bool accept(const double &value, int var) //============================================================ @@ -7584,7 +7641,7 @@ void bookTest0Histograms() // f) Few quick insanity checks on booking (cherry-picking). if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -7636,7 +7693,7 @@ void bookTest0Histograms() } // b) Book placeholder and make sure all labels are stored in the placeholder: - this->StoreLabelsInPlaceholder(); + this->storeLabelsInPlaceholder(); // c) Book what needs to be booked for 1D: if (t0.fCalculateTest0) { @@ -7665,10 +7722,10 @@ void bookTest0Histograms() t0.fTest0Pro[mo][mi][v]->SetStats(false); t0.fTest0Pro[mo][mi][v]->Sumw2(); t0.fTest0Pro[mo][mi][v]->SetTitle(t0.fTest0Labels[mo][mi]->Data()); - t0.fTest0Pro[mo][mi][v]->GetXaxis()->SetTitle(FancyFormatting(res.fResultsProXaxisTitle[v].Data())); + t0.fTest0Pro[mo][mi][v]->GetXaxis()->SetTitle(fancyFormatting(res.fResultsProXaxisTitle[v].Data())); t0.fTest0List->Add(t0.fTest0Pro[mo][mi][v]); // yes, this has to be here - } // for(int v=0;vGetName(); TObjArray* oa = rawName.Tokenize("[]"); - if (oa->GetEntries() != 2) { + int nEntries = 2; // expected number of entries after this string is tokenized + if (oa->GetEntries() != nEntries) { LOGF(fatal, "\033[1;31m%s at line %d : oa->GetEntries() = %d \033[0m", __FUNCTION__, __LINE__, oa->GetEntries()); } rawName = oa->At(1)->GetName(); // basically: fResultsPro2D[cent_pt] => cent_pt @@ -7713,11 +7771,11 @@ void bookTest0Histograms() t0.fTest0Pro2D[mo][mi][v]->SetStats(false); t0.fTest0Pro2D[mo][mi][v]->Sumw2(); t0.fTest0Pro2D[mo][mi][v]->SetTitle(t0.fTest0Labels[mo][mi]->Data()); - t0.fTest0Pro2D[mo][mi][v]->GetXaxis()->SetTitle(FancyFormatting(res.fResultsPro2D[v]->GetXaxis()->GetTitle())); - t0.fTest0Pro2D[mo][mi][v]->GetYaxis()->SetTitle(FancyFormatting(res.fResultsPro2D[v]->GetYaxis()->GetTitle())); + t0.fTest0Pro2D[mo][mi][v]->GetXaxis()->SetTitle(fancyFormatting(res.fResultsPro2D[v]->GetXaxis()->GetTitle())); + t0.fTest0Pro2D[mo][mi][v]->GetYaxis()->SetTitle(fancyFormatting(res.fResultsPro2D[v]->GetYaxis()->GetTitle())); t0.fTest0List->Add(t0.fTest0Pro2D[mo][mi][v]); // yes, this has to be here - } // for(int v=0;vGetName(); TObjArray* oa = rawName.Tokenize("[]"); - if (oa->GetEntries() != 2) { + int nEntries = 2; // expected number of entries after this string is tokenized + if (oa->GetEntries() != nEntries) { LOGF(fatal, "\033[1;31m%s at line %d : oa->GetEntries() = %d \033[0m", __FUNCTION__, __LINE__, oa->GetEntries()); } rawName = oa->At(1)->GetName(); // basically: fResultsPro2D[cent_pt_eta] => cent_pt_eta @@ -7763,12 +7822,12 @@ void bookTest0Histograms() t0.fTest0Pro3D[mo][mi][v]->SetStats(false); t0.fTest0Pro3D[mo][mi][v]->Sumw2(); t0.fTest0Pro3D[mo][mi][v]->SetTitle(t0.fTest0Labels[mo][mi]->Data()); - t0.fTest0Pro3D[mo][mi][v]->GetXaxis()->SetTitle(FancyFormatting(res.fResultsPro3D[v]->GetXaxis()->GetTitle())); - t0.fTest0Pro3D[mo][mi][v]->GetYaxis()->SetTitle(FancyFormatting(res.fResultsPro3D[v]->GetYaxis()->GetTitle())); - t0.fTest0Pro3D[mo][mi][v]->GetZaxis()->SetTitle(FancyFormatting(res.fResultsPro3D[v]->GetZaxis()->GetTitle())); + t0.fTest0Pro3D[mo][mi][v]->GetXaxis()->SetTitle(fancyFormatting(res.fResultsPro3D[v]->GetXaxis()->GetTitle())); + t0.fTest0Pro3D[mo][mi][v]->GetYaxis()->SetTitle(fancyFormatting(res.fResultsPro3D[v]->GetYaxis()->GetTitle())); + t0.fTest0Pro3D[mo][mi][v]->GetZaxis()->SetTitle(fancyFormatting(res.fResultsPro3D[v]->GetZaxis()->GetTitle())); t0.fTest0List->Add(t0.fTest0Pro3D[mo][mi][v]); // yes, this has to be here - } // for(int v=0;vGetXaxis()->GetTitle()).EqualTo("integrated")) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (t0.fTest0Pro[0][0][AFO_PT] && !TString(t0.fTest0Pro[0][0][AFO_PT]->GetXaxis()->GetTitle()).EqualTo("p_{T}")) { - LOGF(fatal, "\033[1;31m%s at line %d : x-axis title = %s\033[0m", __FUNCTION__, __LINE__, t0.fTest0Pro[0][0][AFO_PT]->GetXaxis()->GetTitle()); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d : x-axis title = %s\033[0m", __FUNCTION__, __LINE__, t0.fTest0Pro[0][0][AFO_PT]->GetXaxis()->GetTitle()); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } // 2D: if (t0.fTest0Pro2D[0][0][AFO_CENTRALITY_PT] && !(TString(t0.fTest0Pro2D[0][0][AFO_CENTRALITY_PT]->GetXaxis()->GetTitle()).Contains("centrality", TString::kIgnoreCase) && TString(t0.fTest0Pro2D[0][0][AFO_CENTRALITY_PT]->GetYaxis()->GetTitle()).EqualTo("p_{T}"))) { - LOGF(fatal, "\033[1;31m%s at line %d : x-axis title = %s, y-axis title = %s\033[0m", __FUNCTION__, __LINE__, t0.fTest0Pro2D[0][0][AFO_CENTRALITY_PT]->GetXaxis()->GetTitle(), t0.fTest0Pro2D[0][0][AFO_CENTRALITY_PT]->GetYaxis()->GetTitle()); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d : x-axis title = %s, y-axis title = %s\033[0m", __FUNCTION__, __LINE__, t0.fTest0Pro2D[0][0][AFO_CENTRALITY_PT]->GetXaxis()->GetTitle(), t0.fTest0Pro2D[0][0][AFO_CENTRALITY_PT]->GetYaxis()->GetTitle()); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } // 3D: if (t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA] && !(TString(t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetXaxis()->GetTitle()).Contains("centrality", TString::kIgnoreCase) && TString(t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetYaxis()->GetTitle()).EqualTo("p_{T}") && TString(t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetZaxis()->GetTitle()).EqualTo("#eta"))) { - LOGF(fatal, "\033[1;31m%s at line %d : x-axis title = %s, y-axis title = %s, z-axis title = %s\033[0m", __FUNCTION__, __LINE__, t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetXaxis()->GetTitle(), t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetYaxis()->GetTitle(), t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetZaxis()->GetTitle()); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d : x-axis title = %s, y-axis title = %s, z-axis title = %s\033[0m", __FUNCTION__, __LINE__, t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetXaxis()->GetTitle(), t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetYaxis()->GetTitle(), t0.fTest0Pro3D[0][0][AFO_CENTRALITY_PT_ETA]->GetZaxis()->GetTitle()); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookTest0Histograms() @@ -7809,7 +7868,7 @@ void bookEtaSeparationsHistograms() // c) Few quick insanity checks on booking. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -7881,22 +7940,22 @@ void bookEtaSeparationsHistograms() es.fEtaSeparationsPro[h][e][v]->SetStats(false); es.fEtaSeparationsPro[h][e][v]->Sumw2(); es.fEtaSeparationsPro[h][e][v]->SetTitle(Form("%d -%d, |#Delta#eta| > %.2f", h + 1, h + 1, es.fEtaSeparationsValues[e])); - es.fEtaSeparationsPro[h][e][v]->GetXaxis()->SetTitle(FancyFormatting(res.fResultsProXaxisTitle[v].Data())); + es.fEtaSeparationsPro[h][e][v]->GetXaxis()->SetTitle(fancyFormatting(res.fResultsProXaxisTitle[v].Data())); es.fEtaSeparationsList->Add(es.fEtaSeparationsPro[h][e][v]); // yes, this has to be here - } // for(int v=0;vGetXaxis()->GetTitle()).EqualTo("integrated")) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (es.fEtaSeparationsPro[0][0][AFO_PT] && !TString(es.fEtaSeparationsPro[0][0][AFO_PT]->GetXaxis()->GetTitle()).EqualTo("p_{T}")) { - LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum eAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // ordering in enum EnAsFunctionOf is not the same as in TString fResultsProXaxisTitle[eAsFunctionOf_N] } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookEtaSeparationsHistograms() @@ -7914,7 +7973,7 @@ void bookResultsHistograms() // d) Book (and optionaly save) results histograms 3D. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the profile holding flags: @@ -8155,14 +8214,14 @@ void bookResultsHistograms() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookResultsHistograms() //============================================================ -TArrayD* ArrayWithBinEdges(int nBins, float min, float max) +TArrayD* arrayWithBinEdges(int nBins, float min, float max) { // Helper function to determine concrete bin edges, when the fixed-size binning was specified with nBins in (min, max). @@ -8170,7 +8229,7 @@ TArrayD* ArrayWithBinEdges(int nBins, float min, float max) // b) Okay, do the thing. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Insanity check on arguments: @@ -8187,12 +8246,12 @@ TArrayD* ArrayWithBinEdges(int nBins, float min, float max) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return binEdges; -} // TArrayD *ArrayWithBinEdges(int nBins, float min, float max) +} // TArrayD *arrayWithBinEdges(int nBins, float min, float max) //============================================================ @@ -8205,7 +8264,7 @@ void bookTheRest() // *) ... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Book the timer: @@ -8217,55 +8276,56 @@ void bookTheRest() // b) Book TDatabasePDG: if (tc.fUseDatabasePDG) { - tc.fDatabasePDG = new TDatabasePDG(); // there is a standard memory blow-up here + // Remark: there is a standard memory blow-up here TBI 20260714 switch eventualy to o2::framework::O2DatabasePDG when lazy initialization is provided + tc.fDatabasePDG = new TDatabasePDG(); // o2-linter: disable=pdg/database (using until o2::framework::O2DatabasePDG lazy initialization is provided) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void bookTheRest() //============================================================ -template -void Preprocess(T1 const& collision, T2 const& bcs) +template +void preprocess(T1 const& collision, T2 const& bcs) { // Do all thingies before starting to process data (e.g. count number of events, fetch the run number, get the weights for this run number, etc.). if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Determine all event counters: - DetermineEventCounters(); + determineEventCounters(); // *) Sequential bailout: After each tc.fSequentialBailout events, I bail out: if (tc.fSequentialBailout > 0 && eh.fEventCounter[eProcessed] > 0 && 0 == eh.fEventCounter[eProcessed] % tc.fSequentialBailout) { - BailOut(); + bailOut(); } // *) If I reached max number of events, ignore the remaining collisions: - if (MaxNumberOfEvents(eAfter) || MaxNumberOfEvents(eBefore)) { // TBI 20240510 this is a bit confusing, implemented this way. Shall I split off? - BailOut(true); + if (maxNumberOfEvents(eAfter) || maxNumberOfEvents(eBefore)) { // TBI 20240510 this is a bit confusing, implemented this way. Shall I split off? + bailOut(true); } // *) Determine and propagate run number info to already booked objects: if (!tc.fRunNumberIsDetermined) { - DetermineRunNumber(collision, bcs); - PropagateRunNumber(); + determineRunNumber(collision, bcs); + propagateRunNumber(); } if (tc.fDoAdditionalInsanityChecks && tc.fRunNumberIsDetermined) { - CheckCurrentRunNumber(collision, bcs); + checkCurrentRunNumber(collision, bcs); } - // *) Check whether this run shall be skipped later from further processing in Steer(): + // *) Check whether this run shall be skipped later from further processing in steer(): if (!tc.fSkipTheseRuns.EqualTo("")) { // If tc.fSkipTheseRuns is not empty, that means it holds comma-separated list of runs to be skipped. Let's check it out... - SkipThisRun(); // I set inside the data member tc.fSkipRun , which serves then as a switch later all over the place + skipThisRun(); // I set inside the data member tc.fSkipRun , which serves then as a switch later all over the place if (tc.fSkipRun) { - return; // yes, I bail out immediately from Preprocess, so that I do not waste time on fetching weights for this run + return; // yes, I bail out immediately from preprocess, so that I do not waste time on fetching weights for this run } // TBI 20250316 Same comment here: At the moment I can access run number info only in process(...), but not in init(...) // Once I can access run number info in init(...), this function shall be called in init(...), not in process(...) @@ -8277,7 +8337,7 @@ void Preprocess(T1 const& collision, T2 const& bcs) // integrated weights and differentials weights without sparse histograms (the latter is becoming obsolete): if (pw.fUseWeights[wPHI] || pw.fUseWeights[wPT] || pw.fUseWeights[wETA] || pw.fUseDiffWeights[wPHIPT] || pw.fUseDiffWeights[wPHIETA]) { - GetParticleWeights(); + getParticleWeights(); pw.fParticleWeightsAreFetched = true; } @@ -8287,7 +8347,7 @@ void Preprocess(T1 const& collision, T2 const& bcs) // On the other hand, it doesn't make sense to calculate differential phi weights without having phi axis. // At any point I shall be able to fall back e.g. to integrated phi weights, that corresponds to the case wheh "1-wPhi" and all others are "0-w..." // Same for differential pt or eta weights. - GetParticleWeights(); + getParticleWeights(); pw.fParticleWeightsAreFetched = true; } @@ -8297,21 +8357,21 @@ void Preprocess(T1 const& collision, T2 const& bcs) // TBI 20231012 If eventualy I can access programatically run number in init(...) at run time, this shall go there. if (!cw.fCentralityWeightsAreFetched) { if (cw.fUseCentralityWeights) { - GetCentralityWeights(); + getCentralityWeights(); cw.fCentralityWeightsAreFetched = true; } } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void Preprocess(T1 const& collision, T2 const& bcs) +} // template void preprocess(T1 const& collision, T2 const& bcs) //============================================================ -template -void DetermineRunNumber(T1 const& collision, T2 const&) +template +void determineRunNumber(T1 const& collision, T2 const&) { // Determine run number and all related thingies. // Make sure in process(...) that this function is called only once. @@ -8320,10 +8380,10 @@ void DetermineRunNumber(T1 const& collision, T2 const&) // Once I can access run number info in init(...), this function shall be called in init(...), not in process(...) // a) Determine run number for Run 3 and Run 2 real data; - // b) Determine run number for the rest. TBI 20241126 differentiate this support as well, e.g. for eRecSim and eSim. + // b) Determine run number for the rest. TBI 20241126 differentiate this support as well, e.g. for EnRecSim and eSim. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Determine run number for Run 3 and Run 2 real data; @@ -8361,25 +8421,25 @@ void DetermineRunNumber(T1 const& collision, T2 const&) tc.fRunNumber = "123456"; } else { // b) Determine run number for the rest. - // TBI 20241126 differentiate this support as well, e.g. for eRecSim and eSim. + // TBI 20241126 differentiate this support as well, e.g. for EnRecSim and eSim. LOGF(fatal, "\033[1;31m%s at line %d : bc.runNumber() is not validated yet for this case\033[0m", __FUNCTION__, __LINE__); } tc.fRunNumberIsDetermined = true; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void DetermineRunNumber(T1 const& collision, T2 const&) +} // template void determineRunNumber(T1 const& collision, T2 const&) //============================================================ -void PropagateRunNumber() +void propagateRunNumber() { // Propagate run number info to already booked objects, wherever it's relevant. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Do some local insanity checks: @@ -8400,7 +8460,7 @@ void PropagateRunNumber() TString histTitle = ""; // *) event cuts: - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter { @@ -8416,11 +8476,11 @@ void PropagateRunNumber() } // *) event histograms 1D: - for (int t = 0; t < eEventHistograms_N; t++) // type, see enum eEventHistograms + for (int t = 0; t < eEventHistograms_N; t++) // type, see enum EEventHistograms { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!eh.fEventHistograms[t][rs][ba]) { continue; @@ -8432,14 +8492,14 @@ void PropagateRunNumber() } } // for(int ba=0;ba<2;ba++) } // for(int rs=0;rs<2;rs++) // reco/sim - } // for(int t=0;tSetTitle(histTitle.Data()); } - } // for (int ba = 0; ba < 2; ba++) // before/after cuts + } // for (int ba = 0; ba < gbaN; ba++) // before/after cuts } // for(int rs=0;rs<2;rs++) // reco/sim - } // for (int t = 0; t < eDiffWeightCategory; t++) // category, see enum eDiffWeightCategory + } // for (int t = 0; t < EnDiffWeightCategory; t++) // category, see enum EnDiffWeightCategory // *) "correlations vs." histograms 2D: for (int t = 0; t < eQACorrelationsVsHistograms2D_N; t++) // type, see enum eCorrelationsVsHistograms2D { for (int h = 0; h < gMaxHarmonic; h++) { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { if (!qa.fQACorrelationsVsHistograms2D[t][h][rs]) { continue; @@ -8532,7 +8592,7 @@ void PropagateRunNumber() for (int t = 0; t < eQACorrelationsVsInteractionRateVsProfiles2D_N; t++) // type, see enum eCorrelationsVsInteractionRateVsProfiles2D { for (int h = 0; h < gMaxHarmonic; h++) { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { if (!qa.fQACorrVsIRVsProfiles2D[t][h][rs]) { continue; @@ -8547,7 +8607,7 @@ void PropagateRunNumber() } // for (int t = 0; t < eQACorrelationsVsInteractionRateVsProfiles2D_N; t++) // type, see enum eCorrelationsVsInteractionRateVsProfiles2D // *) particle cuts: - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter { @@ -8565,9 +8625,9 @@ void PropagateRunNumber() // *) particle histograms 1D: for (int t = 0; t < eParticleHistograms_N; t++) // type, see enum eParticleHistograms { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!ph.fParticleHistograms[t][rs][ba]) { continue; @@ -8584,9 +8644,9 @@ void PropagateRunNumber() // *) particle histograms 2D: for (int t = 0; t < eParticleHistograms2D_N; t++) // type, see enum eParticleHistograms2D { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!ph.fParticleHistograms2D[t][rs][ba]) { continue; @@ -8601,9 +8661,9 @@ void PropagateRunNumber() } // for(int t=0;t -eta , ab = 1 <=> + eta - for (int rs = 0; rs < 2; rs++) { // reco/sim - for (int ba = 0; ba < 2; ba++) { // before/after cuts + for (int ab = 0; ab < gewN; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta + for (int rs = 0; rs < grsN; rs++) { // reco/sim + for (int ba = 0; ba < gbaN; ba++) { // before/after cuts for (int e = 0; e < gMaxNumberEtaSeparations; e++) { // eta separation if (!qv.fMabDist[ab][rs][ba][e]) { continue; @@ -8619,24 +8679,24 @@ void PropagateRunNumber() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // PropagateRunNumber() +} // propagateRunNumber() //============================================================ -template -void CheckCurrentRunNumber(T1 const& collision, T2 const&) +template +void checkCurrentRunNumber(T1 const& collision, T2 const&) { // Insanity check for the current run number and related thingies. // Used only during validation. // a) Support for Run 3 and Run 2 real data; - // b) The rest. TBI 20241126 differentiate this support as well, e.g. for eRecSim and eSim. But Run 2 and Run 1 most likely will stay as before + // b) The rest. TBI 20241126 differentiate this support as well, e.g. for EnRecSim and eSim. But Run 2 and Run 1 most likely will stay as before if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Support for Run 3 and Run 2 real data: @@ -8689,14 +8749,14 @@ void CheckCurrentRunNumber(T1 const& collision, T2 const&) } // to else if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void CheckCurrentRunNumber(T1 const& collision, T2 const&) +} // template void checkCurrentRunNumber(T1 const& collision, T2 const&) //============================================================ -void ResetEventByEventQuantities() +void resetEventByEventQuantities() { // Reset all global event-by-event quantities here: @@ -8707,7 +8767,7 @@ void ResetEventByEventQuantities() // e) QA. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Event-by-event quantities: @@ -8715,7 +8775,7 @@ void ResetEventByEventQuantities() ebye.fMultiplicity = 0.; ebye.fReferenceMultiplicity = 0.; ebye.fCentrality = 0.; - // ebye.fCentralitySim = 0.; // TBI 20250429 special treatment, because I access this one before Steer(...) is called in .cxx . Re-think how to handle this one + // ebye.fCentralitySim = 0.; // TBI 20250429 special treatment, because I access this one before steer(...) is called in .cxx . Re-think how to handle this one // But if I do not access it in .cxx, in any case I skip that collision, so I think it is just fine not to reset it here ebye.fOccupancy = 0.; ebye.fInteractionRate = 0.; @@ -8729,7 +8789,7 @@ void ResetEventByEventQuantities() if (qv.fCalculateQvectors) { // b0) generic Q-vector: - ResetQ(); + resetQ(); // b1) integrated Q-vector: for (int h = 0; h < gMaxHarmonic * gMaxCorrelator + 1; h++) { for (int wp = 0; wp < gMaxCorrelator + 1; wp++) // weight power @@ -8741,7 +8801,7 @@ void ResetEventByEventQuantities() } // if (qv.fCalculateQvectors) if (qv.fCalculateqvectorsKineAny) { - ResetQ(); // TBI 20250601 do I really need this one here. It doesn't hurt, though... + resetQ(); // TBI 20250601 do I really need this one here. It doesn't hurt, though... // Remark: It's important to validate this reset with nested loops e-by-e and for all events. for (int i = 0; i < static_cast(qv.fqvector.size()); ++i) { for (int j = 0; j < static_cast(qv.fqvector[i].size()); ++j) { @@ -8758,7 +8818,7 @@ void ResetEventByEventQuantities() // b3) integrated Q-vector needed for calculations with eta separations: if (es.fCalculateEtaSeparations) { - for (int ab = 0; ab < 2; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta + for (int ab = 0; ab < gewN; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta for (int h = 0; h < gMaxHarmonic; h++) { if (es.fEtaSeparationsSkipHarmonics[h]) { continue; @@ -8768,7 +8828,7 @@ void ResetEventByEventQuantities() } } } - for (int ab = 0; ab < 2; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta + for (int ab = 0; ab < gewN; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta for (int e = 0; e < gMaxNumberEtaSeparations; e++) { // eta separation qv.fMab[ab][e] = 0.; } @@ -8846,8 +8906,8 @@ void ResetEventByEventQuantities() // 2D kine: // **) vs. (pt,eta): - if (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(PT_ETAq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + if (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested + nBins = (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(PT_ETAq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[PT_ETAq][b][0]->Reset(); nl.ftaNestedLoopsKine[PT_ETAq][b][1]->Reset(); @@ -8855,8 +8915,8 @@ void ResetEventByEventQuantities() } // **) vs. (pt,charge): - if (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(PT_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + if (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested + nBins = (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(PT_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[PT_CHARGEq][b][0]->Reset(); nl.ftaNestedLoopsKine[PT_CHARGEq][b][1]->Reset(); @@ -8864,8 +8924,8 @@ void ResetEventByEventQuantities() } // **) vs. (eta,charge): - if (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[AfoKineMap2D(ETA_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + if (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested + nBins = (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro2D[afoKineMap2D(ETA_CHARGEq)]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[ETA_CHARGEq][b][0]->Reset(); nl.ftaNestedLoopsKine[ETA_CHARGEq][b][1]->Reset(); @@ -8876,8 +8936,8 @@ void ResetEventByEventQuantities() // 3D kine: // **) vs. (pt,eta,charge): - if (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested - nBins = (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsY() + 2) * (res.fResultsPro3D[AfoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + if (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]) { // this is safe, because this one shall be booked if any of Correlations, Test0, EtaSeparations, etc., was requested + nBins = (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsX() + 2) * (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsY() + 2) * (res.fResultsPro3D[afoKineMap3D(PT_ETA_CHARGEq)]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below for (int b = 0; b < nBins; b++) { // loop over lineralized global bins nl.ftaNestedLoopsKine[PT_ETA_CHARGEq][b][0]->Reset(); nl.ftaNestedLoopsKine[PT_ETA_CHARGEq][b][1]->Reset(); @@ -8895,9 +8955,9 @@ void ResetEventByEventQuantities() } // e) QA: - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (qa.fQAParticleEventProEbyE[rs][ba]) { qa.fQAParticleEventProEbyE[rs][ba]->Reset(); @@ -8906,38 +8966,38 @@ void ResetEventByEventQuantities() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void ResetEventByEventQuantities() +} // void resetEventByEventQuantities() //============================================================ -template -void EventCutsCounters(T1 const& collision, T2 const& tracks) +template +void eventCutsCounters(T1 const& collision, T2 const& tracks) { // Use this function to fill absolute and sequential event cut counters. Use only during QA, as this is computationally heavy. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Establish ordering of binning in event cut counters histograms, which resembles ordering of event cuts implementation: if (!ec.fEventCutCounterBinLabelingIsDone) { - ec.fEventCutCounterBinNumber[eRec] = 1; // remember that I cannot use 'rs' here as an index, because the enum eRecSim covers separately Run 1 and Run 2, etc. + ec.fEventCutCounterBinNumber[eRec] = 1; // remember that I cannot use 'rs' here as an index, because the enum EnRecSim covers separately Run 1 and Run 2, etc. ec.fEventCutCounterBinNumber[eSim] = 1; - EventCuts(collision, tracks, eCutCounterBinning); // dry call, to establish the map fEventCutCounterMap and its inverse + eventCuts(collision, tracks, eCutCounterBinning); // dry call, to establish the map fEventCutCounterMap and its inverse - // **) Special treatment for event cuts implemented outside of EventCuts(), like eMultiplicity: - // Algorithm: I simply add eMultiplicity at the end of what was esatablished by now in the above call EventCuts(collision, tracks, eCutCounterBinning) + // **) Special treatment for event cuts implemented outside of eventCuts(), like eMultiplicity: + // Algorithm: I simply add eMultiplicity at the end of what was esatablished by now in the above call eventCuts(collision, tracks, eCutCounterBinning) // unless proven it shall be done some other way. - if (ec.fEventCutCounterMap[eRec]) { // TBI 20240414 also here have to hardcode 'eRec', because 'rs' spans over all enums in eRecSim => I definitely need 'generic Rec' case, perhaps via TExMap ? + if (ec.fEventCutCounterMap[eRec]) { // TBI 20240414 also here have to hardcode 'eRec', because 'rs' spans over all enums in EnRecSim => I definitely need 'generic Rec' case, perhaps via TExMap ? // But I have already tc.fProcess[eGenericRec] and tc.fProcess[eGenericRecSim], available, shall I simply re-use them? ec.fEventCutCounterMap[eRec]->Add(ec.fEventCutCounterBinNumber[eRec], eMultiplicity); ec.fEventCutCounterMapInverse[eRec]->Add(eMultiplicity, ec.fEventCutCounterBinNumber[eRec]); ec.fEventCutCounterBinNumber[eRec]++; // yes } - if (ec.fEventCutCounterMap[eSim]) { // TBI 20240414 also here have to hardcode 'eSim', because 'rs' spans over all enums in eRecSim => I definitely need 'generic Rec' case, perhaps via TExMap ? + if (ec.fEventCutCounterMap[eSim]) { // TBI 20240414 also here have to hardcode 'eSim', because 'rs' spans over all enums in EnRecSim => I definitely need 'generic Rec' case, perhaps via TExMap ? // But I have already tc.fProcess[eGenericRec] and tc.fProcess[eGenericRecSim], available, shall I simply re-use them? ec.fEventCutCounterMap[eSim]->Add(ec.fEventCutCounterBinNumber[eSim], eMultiplicity); ec.fEventCutCounterMapInverse[eSim]->Add(eMultiplicity, ec.fEventCutCounterBinNumber[eSim]); @@ -8945,7 +9005,7 @@ void EventCutsCounters(T1 const& collision, T2 const& tracks) } // **) Map this ordering into bin labels of actual histograms for event cut counters: - for (int rec_sim = 0; rec_sim < 2; rec_sim++) // reco/sim => I use here exceptionally different var 'rec_sim', not the shadow 'rs' in the template parameter + for (int rec_sim = 0; rec_sim < grsN; rec_sim++) // reco/sim => I use here exceptionally different var 'rec_sim', not the shadow 'rs' in the template parameter { for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter { @@ -8956,7 +9016,7 @@ void EventCutsCounters(T1 const& collision, T2 const& tracks) if (tc.fUseSetBinLabel) { for (int bin = 1; bin < ec.fEventCutCounterBinNumber[rec_sim]; bin++) // implemented and used cuts in this analysis { - ec.fEventCutCounterHist[rec_sim][cc]->GetXaxis()->SetBinLabel(bin, FancyFormatting(ec.fEventCutName[ec.fEventCutCounterMap[rec_sim]->GetValue(bin)].Data())); + ec.fEventCutCounterHist[rec_sim][cc]->GetXaxis()->SetBinLabel(bin, fancyFormatting(ec.fEventCutName[ec.fEventCutCounterMap[rec_sim]->GetValue(bin)].Data())); } for (int bin = ec.fEventCutCounterBinNumber[rec_sim]; bin <= eEventCuts_N; bin++) // implemented, but unused cuts in this analysis { @@ -8972,7 +9032,7 @@ void EventCutsCounters(T1 const& collision, T2 const& tracks) TString yAxisTitle = ""; for (int bin = 1; bin < ec.fEventCutCounterBinNumber[rec_sim]; bin++) // implemented and used cuts in this analysis { - yAxisTitle += TString::Format("%d:%s; ", bin, FancyFormatting(ec.fEventCutName[ec.fEventCutCounterMap[rec_sim]->GetValue(bin)].Data())); + yAxisTitle += TString::Format("%d:%s; ", bin, fancyFormatting(ec.fEventCutName[ec.fEventCutCounterMap[rec_sim]->GetValue(bin)].Data())); } for (int bin = ec.fEventCutCounterBinNumber[rec_sim]; bin <= eEventCuts_N; bin++) // implemented, but unused cuts in this analysis { @@ -9010,7 +9070,7 @@ void EventCutsCounters(T1 const& collision, T2 const& tracks) if (ec.fUseEventCutCounterAbsolute) { ec.fEventCutCounterBinNumber[eRec] = 1; ec.fEventCutCounterBinNumber[eSim] = 1; - EventCuts(collision, tracks, eCutCounterAbsolute); + eventCuts(collision, tracks, eCutCounterAbsolute); // **) Special treatments: // a) eMultiplicity: It doesn't make sense to treat this one in eCutCounterAbsolute @@ -9020,24 +9080,24 @@ void EventCutsCounters(T1 const& collision, T2 const& tracks) if (ec.fUseEventCutCounterSequential) { ec.fEventCutCounterBinNumber[eRec] = 1; ec.fEventCutCounterBinNumber[eSim] = 1; - EventCuts(collision, tracks, eCutCounterSequential); + eventCuts(collision, tracks, eCutCounterSequential); // **) Special treatments: - // a) eMultiplicity: Since cut on eMultiplicity is implenented outside of EventCuts - // I call EventCut(rs, eMultiplicity, eCutCounterSequential) directly where its implemented. + // a) eMultiplicity: Since cut on eMultiplicity is implenented outside of eventCuts + // I call eventCut(rs, eMultiplicity, eCutCounterSequential) directly where its implemented. // Add same treatment for other special cases, but do not forget above to expand **) Special treatment for event cuts ... } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void EventCutsCounters(T1 const& collision, T2 const& tracks, eCutModus cutModus) +} // template void eventCutsCounters(T1 const& collision, T2 const& tracks, EnCutModus cutModus) //============================================================ -template -bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) +template +bool eventCuts(T1 const& collision, T2 const& tracks, EnCutModus cutModus) { // Event cuts on reconstructed and simulated data. Supports event cut counters, both absolute and sequential. // There is also a related enum eEventCuts. @@ -9052,18 +9112,18 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // f) Event cuts on simulated (Run 1 and 2 specific); // In case there is some corner case between Run 1 and Run 2, simply branch further this one // *) Event cuts for Test case. - // 44:EventCuts + // 44:eventCuts if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Event cuts on reconstructed, and corresponding MC truth simulated (common to Run 3, Run 2 and Run 1) ... if constexpr (rs == eRec || rs == eRecAndSim || rs == eRec_Run2 || rs == eRecAndSim_Run2 || rs == eRec_Run1 || rs == eRecAndSim_Run1 || rs == eQA) { - // *) NumberOfEvents: => this event cut is implemented directly in Steer(...) + // *) NumberOfEvents: => this event cut is implemented directly in steer(...) - // *) SelectedEvents: => this event cut is implemented directly in Steer(...) + // *) SelectedEvents: => this event cut is implemented directly in steer(...) // *) Offline trigger: // Remark from documentation: Bypass this check if you analyse MC or continuous Run3 data. @@ -9074,13 +9134,13 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // TBI 20240517 I didn't validate this trigger on Run 1, in fact, I have added protection against its usage in InsanityChecks. if (ec.fUseEventCuts[eTrigger]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eTrigger, eCutCounterBinning); + eventCut(eRec, eTrigger, eCutCounterBinning); } else if (ec.fsEventCuts[eTrigger].EqualTo("kINT7") && !collision.alias_bit(kINT7)) { // Validated only for Run 2 - if (!EventCut(eRec, eTrigger, cutModus)) { + if (!eventCut(eRec, eTrigger, cutModus)) { return false; } } else if (ec.fsEventCuts[eTrigger].EqualTo("kTVXinTRD") && !collision.alias_bit(kTVXinTRD)) { // Validated only for Run 3 - if (!EventCut(eRec, eTrigger, cutModus)) { + if (!eventCut(eRec, eTrigger, cutModus)) { return false; } } @@ -9092,9 +9152,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) Sel8: // see definition in Common/TableProducer/eventSelection.cxx if (ec.fUseEventCuts[eSel8]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eSel8, eCutCounterBinning); + eventCut(eRec, eSel8, eCutCounterBinning); } else if (!collision.sel8()) { - if (!EventCut(eRec, eSel8, cutModus)) { + if (!eventCut(eRec, eSel8, cutModus)) { return false; } } @@ -9103,37 +9163,37 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) TotalMultiplicity: if (ec.fUseEventCuts[eTotalMultiplicity]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eTotalMultiplicity, eCutCounterBinning); + eventCut(eRec, eTotalMultiplicity, eCutCounterBinning); } else if (tracks.size() < ec.fdEventCuts[eTotalMultiplicity][eMin] || tracks.size() > ec.fdEventCuts[eTotalMultiplicity][eMax] || std::abs(tracks.size() - ec.fdEventCuts[eTotalMultiplicity][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eTotalMultiplicity, cutModus)) { + if (!eventCut(eRec, eTotalMultiplicity, cutModus)) { return false; } } } // *) Multiplicity: - // Remark: This cut is implemented directly in Steer(...), because I allow the possibility that ebye.fMultiplicity = ebye.fSelectedTracks . + // Remark: This cut is implemented directly in steer(...), because I allow the possibility that ebye.fMultiplicity = ebye.fSelectedTracks . // In fact, that will be true in most cases of practical interest. // *) Reference multiplicity: - // Remark: In this member function, reference multiplicity is just a number, and any specific setting for Run 3, 2, or 1 is already done in DetermineReferenceMultiplicity(...) + // Remark: In this member function, reference multiplicity is just a number, and any specific setting for Run 3, 2, or 1 is already done in determineReferenceMultiplicity(...) if (ec.fUseEventCuts[eReferenceMultiplicity]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eReferenceMultiplicity, eCutCounterBinning); + eventCut(eRec, eReferenceMultiplicity, eCutCounterBinning); } else if (ebye.fReferenceMultiplicity < ec.fdEventCuts[eReferenceMultiplicity][eMin] || ebye.fReferenceMultiplicity > ec.fdEventCuts[eReferenceMultiplicity][eMax] || std::abs(ebye.fReferenceMultiplicity - ec.fdEventCuts[eReferenceMultiplicity][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eReferenceMultiplicity, cutModus)) { + if (!eventCut(eRec, eReferenceMultiplicity, cutModus)) { return false; } } } // *) Centrality: - // Remark: In this member function, centrality is just a number, and any specific setting for Run 3, 2, or 1 is already done in DetermineCentrality(...) + // Remark: In this member function, centrality is just a number, and any specific setting for Run 3, 2, or 1 is already done in determineCentrality(...) if (ec.fUseEventCuts[eCentrality]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eCentrality, eCutCounterBinning); + eventCut(eRec, eCentrality, eCutCounterBinning); } else if (ebye.fCentrality < ec.fdEventCuts[eCentrality][eMin] || ebye.fCentrality > ec.fdEventCuts[eCentrality][eMax] || std::abs(ebye.fCentrality - ec.fdEventCuts[eCentrality][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eCentrality, cutModus)) { + if (!eventCut(eRec, eCentrality, cutModus)) { return false; } } @@ -9142,9 +9202,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) VertexX: if (ec.fUseEventCuts[eVertexX]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eVertexX, eCutCounterBinning); + eventCut(eRec, eVertexX, eCutCounterBinning); } else if (collision.posX() < ec.fdEventCuts[eVertexX][eMin] || collision.posX() > ec.fdEventCuts[eVertexX][eMax] || std::abs(collision.posX() - ec.fdEventCuts[eVertexX][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eVertexX, cutModus)) { + if (!eventCut(eRec, eVertexX, cutModus)) { return false; } } @@ -9153,9 +9213,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) VertexY: if (ec.fUseEventCuts[eVertexY]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eVertexY, eCutCounterBinning); + eventCut(eRec, eVertexY, eCutCounterBinning); } else if (collision.posY() < ec.fdEventCuts[eVertexY][eMin] || collision.posY() > ec.fdEventCuts[eVertexY][eMax] || std::abs(collision.posY() - ec.fdEventCuts[eVertexY][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eVertexY, cutModus)) { + if (!eventCut(eRec, eVertexY, cutModus)) { return false; } } @@ -9164,9 +9224,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) VertexZ: if (ec.fUseEventCuts[eVertexZ]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eVertexZ, eCutCounterBinning); + eventCut(eRec, eVertexZ, eCutCounterBinning); } else if (collision.posZ() < ec.fdEventCuts[eVertexZ][eMin] || collision.posZ() > ec.fdEventCuts[eVertexZ][eMax] || std::abs(collision.posZ() - ec.fdEventCuts[eVertexZ][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eVertexZ, cutModus)) { + if (!eventCut(eRec, eVertexZ, cutModus)) { return false; } } @@ -9175,9 +9235,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) MinVertexDistanceFromIP (minimal vertex distance from nominal Interaction Point). If vertex is closer that this value, this event is rejected: if (ec.fUseEventCuts[eMinVertexDistanceFromIP]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eMinVertexDistanceFromIP, eCutCounterBinning); + eventCut(eRec, eMinVertexDistanceFromIP, eCutCounterBinning); } else if (std::sqrt(std::pow(collision.posX(), 2.) + std::pow(collision.posY(), 2.) + std::pow(collision.posZ(), 2.)) < ec.fdEventCuts[eMinVertexDistanceFromIP][eMin]) { - if (!EventCut(eRec, eMinVertexDistanceFromIP, cutModus)) { + if (!eventCut(eRec, eMinVertexDistanceFromIP, cutModus)) { return false; } } @@ -9186,9 +9246,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NContributors: if (ec.fUseEventCuts[eNContributors]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNContributors, eCutCounterBinning); + eventCut(eRec, eNContributors, eCutCounterBinning); } else if (collision.numContrib() < ec.fdEventCuts[eNContributors][eMin] || collision.numContrib() > ec.fdEventCuts[eNContributors][eMax] || std::abs(collision.numContrib() - ec.fdEventCuts[eNContributors][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eNContributors, cutModus)) { + if (!eventCut(eRec, eNContributors, cutModus)) { return false; } } @@ -9197,9 +9257,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) RefMultVsNContrUp: if (ec.fUseEventCuts[eRefMultVsNContrUp]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eRefMultVsNContrUp, eCutCounterBinning); - } else if (collision.numContrib() > (tc.fUseFormula ? ec.fEventCutsFormulas[eRefMultVsNContrUp_Formula]->Eval(ebye.fReferenceMultiplicity) : RefMultVsNContr(ebye.fReferenceMultiplicity, eRefMultVsNContrUp_Formula))) { - if (!EventCut(eRec, eRefMultVsNContrUp, cutModus)) { + eventCut(eRec, eRefMultVsNContrUp, eCutCounterBinning); + } else if (collision.numContrib() > (tc.fUseFormula ? ec.fEventCutsFormulas[eRefMultVsNContrUp_Formula]->Eval(ebye.fReferenceMultiplicity) : refMultVsNContr(ebye.fReferenceMultiplicity, eRefMultVsNContrUp_Formula))) { + if (!eventCut(eRec, eRefMultVsNContrUp, cutModus)) { return false; } } @@ -9208,9 +9268,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) RefMultVsNContrLow: if (ec.fUseEventCuts[eRefMultVsNContrLow]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eRefMultVsNContrLow, eCutCounterBinning); - } else if (collision.numContrib() < (tc.fUseFormula ? ec.fEventCutsFormulas[eRefMultVsNContrLow_Formula]->Eval(ebye.fReferenceMultiplicity) : RefMultVsNContr(ebye.fReferenceMultiplicity, eRefMultVsNContrLow_Formula))) { - if (!EventCut(eRec, eRefMultVsNContrLow, cutModus)) { + eventCut(eRec, eRefMultVsNContrLow, eCutCounterBinning); + } else if (collision.numContrib() < (tc.fUseFormula ? ec.fEventCutsFormulas[eRefMultVsNContrLow_Formula]->Eval(ebye.fReferenceMultiplicity) : refMultVsNContr(ebye.fReferenceMultiplicity, eRefMultVsNContrLow_Formula))) { + if (!eventCut(eRec, eRefMultVsNContrLow, cutModus)) { return false; } } @@ -9219,9 +9279,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) CentralityCorrelationsCut: if (ec.fUseEventCuts[eCentralityCorrelationsCut]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eCentralityCorrelationsCut, eCutCounterBinning); - } else if (!CentralityCorrelationCut()) { - if (!EventCut(eRec, eCentralityCorrelationsCut, cutModus)) { + eventCut(eRec, eCentralityCorrelationsCut, eCutCounterBinning); + } else if (!centralityCorrelationCut()) { + if (!eventCut(eRec, eCentralityCorrelationsCut, cutModus)) { return false; } } @@ -9239,7 +9299,7 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) } // In this branch I can cut additionally and directly on corresponding MC truth simulated, e.g. on collision.mcCollision().posZ(). - // In case I implement something here, remember to switch from eRec to eSim when calling e.g. EventCut(...) + // In case I implement something here, remember to switch from eRec to eSim when calling e.g. eventCut(...) // ... @@ -9254,14 +9314,14 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // Remark #2: In this branch 'collision' is always o2::aod::McCollision, see https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo if constexpr (rs == eSim || rs == eSim_Run2 || rs == eSim_Run1) { - // *) NumberOfEvents: => this event cut is implemented directly in Steer(...) + // *) NumberOfEvents: => this event cut is implemented directly in steer(...) // *) Impact parameter: if (ec.fUseEventCuts[eImpactParameter]) { if (cutModus == eCutCounterBinning) { - EventCut(eSim, eImpactParameter, eCutCounterBinning); + eventCut(eSim, eImpactParameter, eCutCounterBinning); } else if (collision.impactParameter() < ec.fdEventCuts[eImpactParameter][eMin] || collision.impactParameter() > ec.fdEventCuts[eImpactParameter][eMax] || std::abs(collision.impactParameter() - ec.fdEventCuts[eImpactParameter][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eSim, eImpactParameter, cutModus)) { + if (!eventCut(eSim, eImpactParameter, cutModus)) { return false; } } @@ -9270,9 +9330,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) Event plane angle: if (ec.fUseEventCuts[eEventPlaneAngle]) { if (cutModus == eCutCounterBinning) { - EventCut(eSim, eEventPlaneAngle, eCutCounterBinning); + eventCut(eSim, eEventPlaneAngle, eCutCounterBinning); } else if (collision.eventPlaneAngle() < ec.fdEventCuts[eEventPlaneAngle][eMin] || collision.eventPlaneAngle() > ec.fdEventCuts[eEventPlaneAngle][eMax] || std::abs(collision.eventPlaneAngle() - ec.fdEventCuts[eEventPlaneAngle][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eSim, eEventPlaneAngle, cutModus)) { + if (!eventCut(eSim, eEventPlaneAngle, cutModus)) { return false; } } @@ -9282,14 +9342,14 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // TBI 20240509 check what is the Monte Carlo analogy for tracks.size() // *) Multiplicity: - // Remark: This cut is implemented directly in Steer(...) TBI 20240508 check how to implement this one with the current re-write + // Remark: This cut is implemented directly in steer(...) TBI 20240508 check how to implement this one with the current re-write - // *) Centrality: this centrality is calculated directly from impact parameter, i.e. this is centrality at simulated level, see DetermineCentrality(...) what I do for thre "eSim*" cases. + // *) Centrality: this centrality is calculated directly from impact parameter, i.e. this is centrality at simulated level, see determineCentrality(...) what I do for thre "eSim*" cases. if (ec.fUseEventCuts[eCentrality]) { if (cutModus == eCutCounterBinning) { - EventCut(eSim, eCentrality, eCutCounterBinning); + eventCut(eSim, eCentrality, eCutCounterBinning); } else if (ebye.fCentrality < ec.fdEventCuts[eCentrality][eMin] || ebye.fCentrality > ec.fdEventCuts[eCentrality][eMax] || std::abs(ebye.fCentrality - ec.fdEventCuts[eCentrality][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eSim, eCentrality, cutModus)) { + if (!eventCut(eSim, eCentrality, cutModus)) { return false; } } @@ -9298,9 +9358,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) VertexX: if (ec.fUseEventCuts[eVertexX]) { if (cutModus == eCutCounterBinning) { - EventCut(eSim, eVertexX, eCutCounterBinning); + eventCut(eSim, eVertexX, eCutCounterBinning); } else if (collision.posX() < ec.fdEventCuts[eVertexX][eMin] || collision.posX() > ec.fdEventCuts[eVertexX][eMax] || std::abs(collision.posX() - ec.fdEventCuts[eVertexX][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eSim, eVertexX, cutModus)) { + if (!eventCut(eSim, eVertexX, cutModus)) { return false; } } @@ -9309,9 +9369,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) VertexY: if (ec.fUseEventCuts[eVertexY]) { if (cutModus == eCutCounterBinning) { - EventCut(eSim, eVertexY, eCutCounterBinning); + eventCut(eSim, eVertexY, eCutCounterBinning); } else if (collision.posY() < ec.fdEventCuts[eVertexY][eMin] || collision.posY() > ec.fdEventCuts[eVertexY][eMax] || std::abs(collision.posY() - ec.fdEventCuts[eVertexY][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eSim, eVertexY, cutModus)) { + if (!eventCut(eSim, eVertexY, cutModus)) { return false; } } @@ -9320,9 +9380,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) VertexZ: if (ec.fUseEventCuts[eVertexZ]) { if (cutModus == eCutCounterBinning) { - EventCut(eSim, eVertexZ, eCutCounterBinning); + eventCut(eSim, eVertexZ, eCutCounterBinning); } else if (collision.posZ() < ec.fdEventCuts[eVertexZ][eMin] || collision.posZ() > ec.fdEventCuts[eVertexZ][eMax] || std::abs(collision.posZ() - ec.fdEventCuts[eVertexZ][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eSim, eVertexZ, cutModus)) { + if (!eventCut(eSim, eVertexZ, cutModus)) { return false; } } @@ -9331,9 +9391,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) MinVertexDistanceFromIP (minimal vertex distance from nominal Interaction Point). If vertex is closer that this value, this event is rejected: if (ec.fUseEventCuts[eMinVertexDistanceFromIP]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eMinVertexDistanceFromIP, eCutCounterBinning); + eventCut(eRec, eMinVertexDistanceFromIP, eCutCounterBinning); } else if (std::sqrt(std::pow(collision.posX(), 2.) + std::pow(collision.posY(), 2.) + std::pow(collision.posZ(), 2.)) < ec.fdEventCuts[eMinVertexDistanceFromIP][eMin]) { - if (!EventCut(eRec, eMinVertexDistanceFromIP, cutModus)) { + if (!eventCut(eRec, eMinVertexDistanceFromIP, cutModus)) { return false; } } @@ -9341,7 +9401,7 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) Sel8: TBI 20240509 - // *) SelectedEvents: => this event cut is implemented directly in Steer(...) + // *) SelectedEvents: => this event cut is implemented directly in steer(...) // ... @@ -9361,9 +9421,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) Occupancy: if (ec.fUseEventCuts[eOccupancy]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eOccupancy, eCutCounterBinning); + eventCut(eRec, eOccupancy, eCutCounterBinning); } else if (ebye.fOccupancy < ec.fdEventCuts[eOccupancy][eMin] || ebye.fOccupancy > ec.fdEventCuts[eOccupancy][eMax] || std::abs(ebye.fOccupancy - ec.fdEventCuts[eOccupancy][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eOccupancy, cutModus)) { + if (!eventCut(eRec, eOccupancy, cutModus)) { return false; } } @@ -9372,9 +9432,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) InteractionRate: if (ec.fUseEventCuts[eInteractionRate]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eInteractionRate, eCutCounterBinning); + eventCut(eRec, eInteractionRate, eCutCounterBinning); } else if (ebye.fInteractionRate < ec.fdEventCuts[eInteractionRate][eMin] || ebye.fInteractionRate > ec.fdEventCuts[eInteractionRate][eMax] || std::abs(ebye.fInteractionRate - ec.fdEventCuts[eInteractionRate][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eInteractionRate, cutModus)) { + if (!eventCut(eRec, eInteractionRate, cutModus)) { return false; } } @@ -9383,9 +9443,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) CurrentRunDuration: // TBI 20241128 check if I can use this one also on Run 2 and Run 1, most likely not if (ec.fUseEventCuts[eCurrentRunDuration]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eCurrentRunDuration, eCutCounterBinning); + eventCut(eRec, eCurrentRunDuration, eCutCounterBinning); } else if (ebye.fCurrentRunDuration < ec.fdEventCuts[eCurrentRunDuration][eMin] || ebye.fCurrentRunDuration > ec.fdEventCuts[eCurrentRunDuration][eMax] || std::abs(ebye.fCurrentRunDuration - ec.fdEventCuts[eCurrentRunDuration][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eCurrentRunDuration, cutModus)) { + if (!eventCut(eRec, eCurrentRunDuration, cutModus)) { return false; } } @@ -9394,9 +9454,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoSameBunchPileup: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoSameBunchPileup]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoSameBunchPileup, eCutCounterBinning); + eventCut(eRec, eNoSameBunchPileup, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - if (!EventCut(eRec, eNoSameBunchPileup, cutModus)) { + if (!eventCut(eRec, eNoSameBunchPileup, cutModus)) { return false; } } @@ -9405,9 +9465,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) IsGoodZvtxFT0vsPV: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eIsGoodZvtxFT0vsPV]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eIsGoodZvtxFT0vsPV, eCutCounterBinning); + eventCut(eRec, eIsGoodZvtxFT0vsPV, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - if (!EventCut(eRec, eIsGoodZvtxFT0vsPV, cutModus)) { + if (!eventCut(eRec, eIsGoodZvtxFT0vsPV, cutModus)) { return false; } } @@ -9416,9 +9476,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) IsVertexITSTPC: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eIsVertexITSTPC]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eIsVertexITSTPC, eCutCounterBinning); + eventCut(eRec, eIsVertexITSTPC, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - if (!EventCut(eRec, eIsVertexITSTPC, cutModus)) { + if (!eventCut(eRec, eIsVertexITSTPC, cutModus)) { return false; } } @@ -9427,9 +9487,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) IsVertexTOFmatched: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eIsVertexTOFmatched]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eIsVertexTOFmatched, eCutCounterBinning); + eventCut(eRec, eIsVertexTOFmatched, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { - if (!EventCut(eRec, eIsVertexTOFmatched, cutModus)) { + if (!eventCut(eRec, eIsVertexTOFmatched, cutModus)) { return false; } } @@ -9438,9 +9498,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) IsVertexTRDmatched: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eIsVertexTRDmatched]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eIsVertexTRDmatched, eCutCounterBinning); + eventCut(eRec, eIsVertexTRDmatched, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { - if (!EventCut(eRec, eIsVertexTRDmatched, cutModus)) { + if (!eventCut(eRec, eIsVertexTRDmatched, cutModus)) { return false; } } @@ -9449,9 +9509,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoCollInTimeRangeStrict: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoCollInTimeRangeStrict]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoCollInTimeRangeStrict, eCutCounterBinning); + eventCut(eRec, eNoCollInTimeRangeStrict, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { - if (!EventCut(eRec, eNoCollInTimeRangeStrict, cutModus)) { + if (!eventCut(eRec, eNoCollInTimeRangeStrict, cutModus)) { return false; } } @@ -9460,9 +9520,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoCollInTimeRangeStandard: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoCollInTimeRangeStandard]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoCollInTimeRangeStandard, eCutCounterBinning); + eventCut(eRec, eNoCollInTimeRangeStandard, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - if (!EventCut(eRec, eNoCollInTimeRangeStandard, cutModus)) { + if (!eventCut(eRec, eNoCollInTimeRangeStandard, cutModus)) { return false; } } @@ -9471,9 +9531,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoCollInRofStrict: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoCollInRofStrict]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoCollInRofStrict, eCutCounterBinning); + eventCut(eRec, eNoCollInRofStrict, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { - if (!EventCut(eRec, eNoCollInRofStrict, cutModus)) { + if (!eventCut(eRec, eNoCollInRofStrict, cutModus)) { return false; } } @@ -9482,9 +9542,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoCollInRofStandard: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoCollInRofStandard]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoCollInRofStandard, eCutCounterBinning); + eventCut(eRec, eNoCollInRofStandard, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - if (!EventCut(eRec, eNoCollInRofStandard, cutModus)) { + if (!eventCut(eRec, eNoCollInRofStandard, cutModus)) { return false; } } @@ -9493,9 +9553,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoHighMultCollInPrevRof: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoHighMultCollInPrevRof]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoHighMultCollInPrevRof, eCutCounterBinning); + eventCut(eRec, eNoHighMultCollInPrevRof, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { - if (!EventCut(eRec, eNoHighMultCollInPrevRof, cutModus)) { + if (!eventCut(eRec, eNoHighMultCollInPrevRof, cutModus)) { return false; } } @@ -9504,9 +9564,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) IsGoodITSLayer3: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eIsGoodITSLayer3]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eIsGoodITSLayer3, eCutCounterBinning); + eventCut(eRec, eIsGoodITSLayer3, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer3)) { - if (!EventCut(eRec, eIsGoodITSLayer3, cutModus)) { + if (!eventCut(eRec, eIsGoodITSLayer3, cutModus)) { return false; } } @@ -9515,9 +9575,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) IsGoodITSLayer0123: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eIsGoodITSLayer0123]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eIsGoodITSLayer0123, eCutCounterBinning); + eventCut(eRec, eIsGoodITSLayer0123, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - if (!EventCut(eRec, eIsGoodITSLayer0123, cutModus)) { + if (!eventCut(eRec, eIsGoodITSLayer0123, cutModus)) { return false; } } @@ -9526,9 +9586,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) IsGoodITSLayersAll: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eIsGoodITSLayersAll]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eIsGoodITSLayersAll, eCutCounterBinning); + eventCut(eRec, eIsGoodITSLayersAll, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - if (!EventCut(eRec, eIsGoodITSLayersAll, cutModus)) { + if (!eventCut(eRec, eIsGoodITSLayersAll, cutModus)) { return false; } } @@ -9537,9 +9597,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) FT0Bad: // see O2Physics/Common/CCDB/RCTSelectionFlags.h if (ec.fUseEventCuts[eFT0Bad]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eFT0Bad, eCutCounterBinning); + eventCut(eRec, eFT0Bad, eCutCounterBinning); } else if (collision.rct_bit(o2::aod::rctsel::kFT0Bad)) { - if (!EventCut(eRec, eFT0Bad, cutModus)) { + if (!eventCut(eRec, eFT0Bad, cutModus)) { return false; } } @@ -9548,9 +9608,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) ITSBad: // see O2Physics/Common/CCDB/RCTSelectionFlags.h if (ec.fUseEventCuts[eITSBad]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eITSBad, eCutCounterBinning); + eventCut(eRec, eITSBad, eCutCounterBinning); } else if (collision.rct_bit(o2::aod::rctsel::kITSBad)) { - if (!EventCut(eRec, eITSBad, cutModus)) { + if (!eventCut(eRec, eITSBad, cutModus)) { return false; } } @@ -9559,9 +9619,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) ITSLimAccMCRepr: // see O2Physics/Common/CCDB/RCTSelectionFlags.h if (ec.fUseEventCuts[eITSLimAccMCRepr]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eITSLimAccMCRepr, eCutCounterBinning); + eventCut(eRec, eITSLimAccMCRepr, eCutCounterBinning); } else if (collision.rct_bit(o2::aod::rctsel::kITSLimAccMCRepr)) { - if (!EventCut(eRec, eITSLimAccMCRepr, cutModus)) { + if (!eventCut(eRec, eITSLimAccMCRepr, cutModus)) { return false; } } @@ -9570,9 +9630,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) TPCBadTracking: // see O2Physics/Common/CCDB/RCTSelectionFlags.h if (ec.fUseEventCuts[eTPCBadTracking]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eTPCBadTracking, eCutCounterBinning); + eventCut(eRec, eTPCBadTracking, eCutCounterBinning); } else if (collision.rct_bit(o2::aod::rctsel::kTPCBadTracking)) { - if (!EventCut(eRec, eTPCBadTracking, cutModus)) { + if (!eventCut(eRec, eTPCBadTracking, cutModus)) { return false; } } @@ -9581,9 +9641,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) TPCLimAccMCRepr: // see O2Physics/Common/CCDB/RCTSelectionFlags.h if (ec.fUseEventCuts[eTPCLimAccMCRepr]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eTPCLimAccMCRepr, eCutCounterBinning); + eventCut(eRec, eTPCLimAccMCRepr, eCutCounterBinning); } else if (collision.rct_bit(o2::aod::rctsel::kTPCLimAccMCRepr)) { - if (!EventCut(eRec, eTPCLimAccMCRepr, cutModus)) { + if (!eventCut(eRec, eTPCLimAccMCRepr, cutModus)) { return false; } } @@ -9592,9 +9652,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) TPCBadPID: // see O2Physics/Common/CCDB/RCTSelectionFlags.h if (ec.fUseEventCuts[eTPCBadPID]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eTPCBadPID, eCutCounterBinning); + eventCut(eRec, eTPCBadPID, eCutCounterBinning); } else if (collision.rct_bit(o2::aod::rctsel::kTPCBadPID)) { - if (!EventCut(eRec, eTPCBadPID, cutModus)) { + if (!eventCut(eRec, eTPCBadPID, cutModus)) { return false; } } @@ -9608,9 +9668,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // Yes, I have to replicate for this special event cut the same code, since in each case it has to be applied at the very end. if (ec.fUseEventCuts[eCentralityWeights]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eCentralityWeights, eCutCounterBinning); - } else if (gRandom->Uniform(0, 1) > CentralityWeight(ebye.fCentrality)) { // yes, since centralityWeight is normalized probability (see CentralityWeight(...)) - if (!EventCut(eRec, eCentralityWeights, cutModus)) { + eventCut(eRec, eCentralityWeights, eCutCounterBinning); + } else if (gRandom->Uniform(0, 1) > centralityWeight(ebye.fCentrality)) { // yes, since centralityWeight is normalized probability (see centralityWeight(...)) + if (!eventCut(eRec, eCentralityWeights, cutModus)) { return false; } } @@ -9629,7 +9689,7 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // In this branch I can cut additionally and directly on corresponding MC truth simulated. // Remark: I implement here only the event cuts which are not already in group a) above, and which make sense only for Run 3 data. - // In case I implement something here, remember to switch from eRec to eSim when calling e.g. EventCut(...) + // In case I implement something here, remember to switch from eRec to eSim when calling e.g. eventCut(...) // ... @@ -9658,9 +9718,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) Sel7: if (ec.fUseEventCuts[eSel7]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eSel7, eCutCounterBinning); + eventCut(eRec, eSel7, eCutCounterBinning); } else if (!collision.sel7()) { - if (!EventCut(eRec, eSel7, cutModus)) { + if (!eventCut(eRec, eSel7, cutModus)) { return false; } } @@ -9669,9 +9729,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoPileupTPC: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoPileupTPC]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoPileupTPC, eCutCounterBinning); + eventCut(eRec, eNoPileupTPC, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoPileupTPC)) { - if (!EventCut(eRec, eNoPileupTPC, cutModus)) { + if (!eventCut(eRec, eNoPileupTPC, cutModus)) { return false; } } @@ -9680,9 +9740,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoPileupFromSPD: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoPileupFromSPD]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoPileupFromSPD, eCutCounterBinning); + eventCut(eRec, eNoPileupFromSPD, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoPileupFromSPD)) { - if (!EventCut(eRec, eNoPileupFromSPD, cutModus)) { + if (!eventCut(eRec, eNoPileupFromSPD, cutModus)) { return false; } } @@ -9691,9 +9751,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) NoSPDOnVsOfPileup: // see O2Physics/Common/CCDB/EventSelectionParams.cxx (and .h for better documentation) if (ec.fUseEventCuts[eNoSPDOnVsOfPileup]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eNoSPDOnVsOfPileup, eCutCounterBinning); + eventCut(eRec, eNoSPDOnVsOfPileup, eCutCounterBinning); } else if (!collision.selection_bit(o2::aod::evsel::kNoSPDOnVsOfPileup)) { - if (!EventCut(eRec, eNoSPDOnVsOfPileup, cutModus)) { + if (!eventCut(eRec, eNoSPDOnVsOfPileup, cutModus)) { return false; } } @@ -9707,9 +9767,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // Yes, I have to replicate for this special event cut the same code, since in each case it has to be applied at the very end. if (ec.fUseEventCuts[eCentralityWeights]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eCentralityWeights, eCutCounterBinning); - } else if (gRandom->Uniform(0, 1) > CentralityWeight(ebye.fCentrality)) { // yes, since centralityWeight is normalized probability (see CentralityWeight(...)) - if (!EventCut(eRec, eCentralityWeights, cutModus)) { + eventCut(eRec, eCentralityWeights, eCutCounterBinning); + } else if (gRandom->Uniform(0, 1) > centralityWeight(ebye.fCentrality)) { // yes, since centralityWeight is normalized probability (see centralityWeight(...)) + if (!eventCut(eRec, eCentralityWeights, cutModus)) { return false; } } @@ -9728,7 +9788,7 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // In this branch I can cut additionally and directly on corresponding MC truth simulated. // Remark: I implement here only the event cuts which are not already in group a) above, and which make sense only for Run 1 and 2 data. - // In case I implement something here, remember to switch from eRec to eSim when calling e.g. EventCut(...) + // In case I implement something here, remember to switch from eRec to eSim when calling e.g. eventCut(...) // ... @@ -9758,9 +9818,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) TotalMultiplicity: if (ec.fUseEventCuts[eTotalMultiplicity]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eTotalMultiplicity, eCutCounterBinning); + eventCut(eRec, eTotalMultiplicity, eCutCounterBinning); } else if (tracks.size() < ec.fdEventCuts[eTotalMultiplicity][eMin] || tracks.size() > ec.fdEventCuts[eTotalMultiplicity][eMax] || std::abs(tracks.size() - ec.fdEventCuts[eTotalMultiplicity][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eTotalMultiplicity, cutModus)) { + if (!eventCut(eRec, eTotalMultiplicity, cutModus)) { return false; } } @@ -9769,9 +9829,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) VertexZ: if (ec.fUseEventCuts[eVertexZ]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eVertexZ, eCutCounterBinning); + eventCut(eRec, eVertexZ, eCutCounterBinning); } else if (collision.posZ() < ec.fdEventCuts[eVertexZ][eMin] || collision.posZ() > ec.fdEventCuts[eVertexZ][eMax] || std::abs(collision.posZ() - ec.fdEventCuts[eVertexZ][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eVertexZ, cutModus)) { + if (!eventCut(eRec, eVertexZ, cutModus)) { return false; } } @@ -9780,9 +9840,9 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) // *) Centrality: if (ec.fUseEventCuts[eCentrality]) { if (cutModus == eCutCounterBinning) { - EventCut(eRec, eCentrality, eCutCounterBinning); + eventCut(eRec, eCentrality, eCutCounterBinning); } else if (ebye.fCentrality < ec.fdEventCuts[eCentrality][eMin] || ebye.fCentrality > ec.fdEventCuts[eCentrality][eMax] || std::abs(ebye.fCentrality - ec.fdEventCuts[eCentrality][eMax]) < tc.fFloatingPointPrecision) { - if (!EventCut(eRec, eCentrality, cutModus)) { + if (!eventCut(eRec, eCentrality, cutModus)) { return false; } } @@ -9794,52 +9854,48 @@ bool EventCuts(T1 const& collision, T2 const& tracks, eCutModus cutModus) return true; -} // template bool EventCuts(T1 const& collision, T2 const& tracks) +} // template bool eventCuts(T1 const& collision, T2 const& tracks) //============================================================ -bool EventCut(int rs, int eventCut, eCutModus cutModus) +bool eventCut(int rs, int iEventCut, EnCutModus cutModus) { - // Helper function to reduce code bloat in EventCuts(). It's meant to be used only in EventCuts(). - // It can be used also in exceptional cases outside of EventCuts(), like for eMultiplicity, but use with care. - // For instance, I can call EventCut(eRec, eCentrality, eCutCounterSequential) directly, only if I have checked that + // Helper function to reduce code bloat in eventCuts(). It's meant to be used only in eventCuts(). + // It can be used also in exceptional cases outside of eventCuts(), like for eMultiplicity, but use with care. + // For instance, I can call eventCut(eRec, eCentrality, eCutCounterSequential) directly, only if I have checked that // fUseEventCutCounterSequential is true, etc. - // Remark: Remember that as a second argument I cannot use enum eEventCuts, because here in one go I take both enum eEventCuts and enum eEventHistograms . + // Remark: Remember that as a second argument I cannot use enum eEventCuts, because here in one go I take both enum eEventCuts and enum EEventHistograms . // *) Insanity checks on arguments: if (!(0 == rs || 1 == rs)) { LOGF(fatal, "\033[1;31m%s at line %d : 'rs' must be generic Rec or Sim index, rs = %d \033[0m", __FUNCTION__, __LINE__, rs); } - if (eventCut >= eEventCuts_N) { - LOGF(fatal, "\033[1;31m%s at line %d : eventCut >= eEventCuts_N, eventCut = %d , eEventCuts_N = %d \033[0m", __FUNCTION__, __LINE__, eventCut, static_cast(eEventCuts_N)); + if (iEventCut >= eEventCuts_N) { + LOGF(fatal, "\033[1;31m%s at line %d : iEventCut >= eEventCuts_N, iEventCut = %d , eEventCuts_N = %d \033[0m", __FUNCTION__, __LINE__, iEventCut, static_cast(eEventCuts_N)); } // *) Do the thing: switch (cutModus) { case eCut: { if (tc.fVerboseEventCut) { - LOGF(info, "\033[1;31mEvent didn't survive the cut: %s\033[0m", ec.fEventCutName[eventCut].Data()); + LOGF(info, "\033[1;31mEvent didn't survive the cut: %s\033[0m", ec.fEventCutName[iEventCut].Data()); } return false; - break; } case eCutCounterBinning: { - ec.fEventCutCounterMap[rs]->Add(ec.fEventCutCounterBinNumber[rs], eventCut); - ec.fEventCutCounterMapInverse[rs]->Add(eventCut, ec.fEventCutCounterBinNumber[rs]); + ec.fEventCutCounterMap[rs]->Add(ec.fEventCutCounterBinNumber[rs], iEventCut); + ec.fEventCutCounterMapInverse[rs]->Add(iEventCut, ec.fEventCutCounterBinNumber[rs]); ec.fEventCutCounterBinNumber[rs]++; // yes return true; - break; } case eCutCounterAbsolute: { - ec.fEventCutCounterHist[rs][eAbsolute]->Fill(ec.fEventCutCounterMapInverse[rs]->GetValue(eventCut)); - return true; // yes, so that I can proceed with another cut in EventCuts - break; + ec.fEventCutCounterHist[rs][eAbsolute]->Fill(ec.fEventCutCounterMapInverse[rs]->GetValue(iEventCut)); + return true; // yes, so that I can proceed with another cut in eventCuts } case eCutCounterSequential: { - ec.fEventCutCounterHist[rs][eSequential]->Fill(ec.fEventCutCounterMapInverse[rs]->GetValue(eventCut)); - return false; // yes, so that I bail out from EventCuts - break; + ec.fEventCutCounterHist[rs][eSequential]->Fill(ec.fEventCutCounterMapInverse[rs]->GetValue(iEventCut)); + return false; // yes, so that I bail out from eventCuts } default: { LOGF(fatal, "\033[1;31m%s at line %d : This cutModus = %d is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(cutModus)); @@ -9849,16 +9905,16 @@ bool EventCut(int rs, int eventCut, eCutModus cutModus) return false; // obsolete, but it suppresses the warning... -} // bool EventCut(int rs, int eventCut, eCutModus cutModus) +} // bool eventCut(int rs, int iEventCut, EnCutModus cutModus) //============================================================ -bool RemainingEventCuts() +bool remainingEventCuts() { // Remaining event cuts which can be applied ONLY after the main loop over particles. // For instance, cut on total number of selected particles (eMultiplicity). - // Remark #1: Whichever cut I implement here, update EventCutsCounters(...) for that cut (like I did for eMultiplicity, as a sort of template). - // Remark #2: I do not have here templated arguments like in EventCuts(), because I do not anticipate using any getter from the framework directly here. + // Remark #1: Whichever cut I implement here, update eventCutsCounters(...) for that cut (like I did for eMultiplicity, as a sort of template). + // Remark #2: I do not have here templated arguments like in eventCuts(), because I do not anticipate using any getter from the framework directly here. // Remark #3: With the current implementation, I support here only eCutCounterSequential, i.e. eCutCounterAbsolute is not supported for cuts applied here. // a) Determine if this function was called for generic rec or generic sim: @@ -9866,7 +9922,7 @@ bool RemainingEventCuts() // ... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Determine if this function was called for generic rec or generic sim: @@ -9881,10 +9937,10 @@ bool RemainingEventCuts() // *) Multiplicity: (see documentation for ebye.fMultiplicity for its definition) if (ec.fUseEventCuts[eMultiplicity]) { if (ebye.fMultiplicity < ec.fdEventCuts[eMultiplicity][eMin] || ebye.fMultiplicity > ec.fdEventCuts[eMultiplicity][eMax] || std::abs(ebye.fMultiplicity - ec.fdEventCuts[eMultiplicity][eMax]) < tc.fFloatingPointPrecision) { - // Remark: I have to implement RemainingEventCuts() in a slightly different way as EventCuts() - EventCut(rs, eMultiplicity, eCut); // just a printout that this event didn't survive this cut - if (ec.fUseEventCutCounterSequential) { // yes, this is important. Otherwise fEventCutCounterHist can be used in EventCut(...), even though it's NULL - EventCut(rs, eMultiplicity, eCutCounterSequential); + // Remark: I have to implement remainingEventCuts() in a slightly different way as eventCuts() + eventCut(rs, eMultiplicity, eCut); // just a printout that this event didn't survive this cut + if (ec.fUseEventCutCounterSequential) { // yes, this is important. Otherwise fEventCutCounterHist can be used in eventCut(...), even though it's NULL + eventCut(rs, eMultiplicity, eCutCounterSequential); } return false; } @@ -9892,12 +9948,12 @@ bool RemainingEventCuts() return true; -} // bool RemainingEventCuts() +} // bool remainingEventCuts() //============================================================ -template -void FillSubeventMultiplicities() +template +void fillSubeventMultiplicities() { // Fill subevent (defined via eta separation) multiplicities. @@ -9908,12 +9964,12 @@ void FillSubeventMultiplicities() // becase Q-vectors are not filled before the event cuts. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Fill reconstructed (common to Run 3, Run 2 and Run 1 + Test mode): if constexpr (rs == eRec || rs == eRecAndSim || rs == eRec_Run2 || rs == eRecAndSim_Run2 || rs == eRec_Run1 || rs == eRecAndSim_Run1 || rs == eTest || rs == eQA) { - for (int ab = 0; ab < 2; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta + for (int ab = 0; ab < gewN; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta for (int e = 0; e < gMaxNumberEtaSeparations; e++) { // eta separation !qv.fMabDist[ab][eRec][eAfter][e] ? true : qv.fMabDist[ab][eRec][eAfter][e]->Fill(qv.fMab[ab][e]); } @@ -9922,7 +9978,7 @@ void FillSubeventMultiplicities() // b) Fill only simulated (common to Run 3, Run 2 and Run 1): if constexpr (rs == eSim || rs == eSim_Run2 || rs == eSim_Run1) { - for (int ab = 0; ab < 2; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta + for (int ab = 0; ab < gewN; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta for (int e = 0; e < gMaxNumberEtaSeparations; e++) { // eta separation !qv.fMabDist[ab][eSim][eAfter][e] ? true : qv.fMabDist[ab][eSim][eAfter][e]->Fill(qv.fMab[ab][e]); } @@ -9930,15 +9986,15 @@ void FillSubeventMultiplicities() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void FillSubeventMultiplicities() +} // void fillSubeventMultiplicities() //============================================================ -template -void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) +template +void fillEventHistograms(T1 const& collision, T2 const& tracks, EnBeforeAfter ba) { // Fill all event histograms for reconstructed or simulated data. QA event histograms are also filled here. @@ -9953,7 +10009,7 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) // Remark: in most cases, all histogram which depend on eMultiplicity are booked only for "after", because by default, Multiplicity = SelectedTracks. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Fill reconstructed ... (common to Run 3, Run 2 and Run 1): @@ -9989,7 +10045,7 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) // Calculate quickly 2-p correlation in harmonic h for this event: TBI 20250114 shall I add this also to some EbyE variable? There is no really much of a code bloat for the time being... // Flush 'n' fill the generic Q-vectors: - ResetQ(); + resetQ(); int lMaxCorrelator = 2; // used only here locally for (int h = 0; h < gMaxHarmonic * lMaxCorrelator + 1; h++) { for (int wp = 0; wp < lMaxCorrelator + 1; wp++) // weight power @@ -9999,10 +10055,10 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) } for (int h = 1; h <= gMaxHarmonic; h++) { - TComplex two = Two(h, -h); - double twoC = two.Re(); // cos - // double twoS = two.Im(); // sin - double wTwo = Two(0, 0).Re(); // Weight is 'number of combinations' by default TBI + TComplex twoComplex = two(h, -h); + double twoC = twoComplex.Re(); // cos + // double twoS = twoComplex.Im(); // sin + double wTwo = two(0, 0).Re(); // weight is 'number of combinations' by default TBI // 20220809 add support for other weights if (!(wTwo > 0.0)) { LOGF(fatal, "In function \033[1;31m%s at line %d : wTwo = %f <=0. ebye.fSelectedTracks = %d.\nDid you forget to enable fCalculateQvectors = true?\033[0m", __FUNCTION__, __LINE__, wTwo, ebye.fSelectedTracks); @@ -10032,7 +10088,7 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) } // Flush the generic Q-vectors: - ResetQ(); + resetQ(); } // if (qa.fFillQACorrelationsVsHistograms2D && qa.fQAParticleEventProEbyE[eRec][ba] && ba == eAfter) @@ -10075,13 +10131,13 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) !eh.fEventHistograms[eEventPlaneAngle][eSim][ba] ? true : eh.fEventHistograms[eEventPlaneAngle][eSim][ba]->Fill(collision.eventPlaneAngle()); // yes, because in this branch 'collision' is always aod::McCollision !eh.fEventHistograms[eMultiplicity][eSim][ba] ? true : eh.fEventHistograms[eMultiplicity][eSim][ba]->Fill(ebye.fMultiplicity); !eh.fEventHistograms[eCentrality][eSim][ba] ? true : eh.fEventHistograms[eCentrality][eSim][ba]->Fill(ebye.fCentrality); // ebye.fCentrality = ebye.fCentralitySim in any case in this branch - // eh.fEventHistograms[eReferenceMultiplicity][eSim][ba]->Fill(ebye.fReferenceMultiplicity); // TBI 20241123 this case is still not supported in DetermineReferenceMultiplicity() + // eh.fEventHistograms[eReferenceMultiplicity][eSim][ba]->Fill(ebye.fReferenceMultiplicity); // TBI 20241123 this case is still not supported in determineReferenceMultiplicity() // eh.fEventHistograms[eTotalMultiplicity][eSim][ba]->Fill(tracks.size()); // TBI 20231030 check further how to use the same thing for 'sim' } // Eta separations: if (es.fCalculateEtaSeparations) { - for (int ab = 0; ab < 2; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta + for (int ab = 0; ab < gewN; ab++) { // ab = 0 <=> -eta , ab = 1 <=> + eta for (int e = 0; e < gMaxNumberEtaSeparations; e++) { // eta separation !qv.fMabDist[ab][eSim][ba][e] ? true : qv.fMabDist[ab][eSim][ba][e]->Fill(qv.fMab[ab][e]); } @@ -10154,7 +10210,7 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) // Calculate quickly 2-p correlation in harmonic h for this event: TBI 20250114 shall I add this also to some EbyE variable? There is no really much of a code bloat for the time being... // Flush 'n' fill the generic Q-vectors: - ResetQ(); + resetQ(); int lMaxCorrelator = 2; // used only here locally for (int h = 0; h < gMaxHarmonic * lMaxCorrelator + 1; h++) { for (int wp = 0; wp < lMaxCorrelator + 1; wp++) // weight power @@ -10164,10 +10220,10 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) } for (int h = 1; h <= gMaxHarmonic; h++) { - TComplex two = Two(h, -h); - double twoC = two.Re(); // cos - // double twoS = two.Im(); // sin - double wTwo = Two(0, 0).Re(); // Weight is 'number of combinations' by default TBI + TComplex twoComplex = two(h, -h); + double twoC = twoComplex.Re(); // cos + // double twoS = twoComplex.Im(); // sin + double wTwo = two(0, 0).Re(); // weight is 'number of combinations' by default TBI // 20220809 add support for other weights if (!(wTwo > 0.0)) { LOGF(fatal, "In function \033[1;31m%s at line %d : wTwo = %f <=0. ebye.fSelectedTracks = %d.\nDid you forget to enable fCalculateQvectors = true?\033[0m", __FUNCTION__, __LINE__, wTwo, ebye.fSelectedTracks); @@ -10188,7 +10244,7 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) } // Flush the generic Q-vectors: - ResetQ(); + resetQ(); } // if (qa.fFillQACorrelationsVsInteractionRateVsProfiles2D && qa.fQAParticleEventProEbyE[eRec][ba] && ba == eAfter) { @@ -10263,14 +10319,14 @@ void FillEventHistograms(T1 const& collision, T2 const& tracks, eBeforeAfter ba) } // if constexpr (rs == eTest) { if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void FillEventHistograms(...) +} // template void fillEventHistograms(...) //============================================================ -void CheckUnderflowAndOverflow() +void checkUnderflowAndOverflow() { // Check and bail out if in event and particle histograms there are entries which went to underflow or overflow bins. @@ -10287,15 +10343,15 @@ void CheckUnderflowAndOverflow() // g) QA Particle event histograms 2D. if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Event histograms 1D: - for (int t = 0; t < eEventHistograms_N; t++) // type, see enum eEventHistograms + for (int t = 0; t < eEventHistograms_N; t++) // type, see enum EEventHistograms { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!eh.fEventHistograms[t][rs][ba]) { continue; @@ -10314,9 +10370,9 @@ void CheckUnderflowAndOverflow() // c) Particle histograms 1D: for (int t = 0; t < eParticleHistograms_N; t++) // type, see enum eParticleHistograms { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!ph.fParticleHistograms[t][rs][ba]) { continue; @@ -10332,9 +10388,9 @@ void CheckUnderflowAndOverflow() // d) Particle histograms 2D: for (int t = 0; t < eParticleHistograms2D_N; t++) // type, see enum eParticleHistograms2D { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!ph.fParticleHistograms2D[t][rs][ba]) { continue; @@ -10360,16 +10416,16 @@ void CheckUnderflowAndOverflow() LOGF(fatal, "\033[1;31m%s at line %d : overflow in y variable in fParticleHistograms2D[%d][%d][%d], for binX = %d => optimize default binning for this histogram\033[0m", __FUNCTION__, __LINE__, t, rs, ba, binX); } } // for (int binX = 0; binX <= ph.fParticleHistograms2D[t][rs][ba]->GetNbinsX(); binX++) { - } // for (int ba = 0; ba < 2; ba++) // before/after cuts - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int ba = 0; ba < gbaN; ba++) // before/after cuts + } // for (int rs = 0; rs < grsN; rs++) // reco/sim } // for (int t = 0; t < eParticleHistograms2D_N; t++) // type, see enum eParticleHistograms2D // e) QA Event histograms 2D: for (int t = 0; t < eQAEventHistograms2D_N; t++) // type, see enum eQAEventHistograms2D { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!qa.fQAEventHistograms2D[t][rs][ba]) { continue; @@ -10395,16 +10451,16 @@ void CheckUnderflowAndOverflow() LOGF(fatal, "\033[1;31m%s at line %d : overflow in y variable in fEventHistograms2D[%d][%d][%d], for binX = %d => optimize default binning for this histogram\033[0m", __FUNCTION__, __LINE__, t, rs, ba, binX); } } // for (int binX = 0; binX <= qa.fQAEventHistograms2D[t][rs][ba]->GetNbinsX(); binX++) { - } // for (int ba = 0; ba < 2; ba++) // before/after cuts - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int ba = 0; ba < gbaN; ba++) // before/after cuts + } // for (int rs = 0; rs < grsN; rs++) // reco/sim } // for (int t = 0; t < eQAEventHistograms2D_N; t++) // type, see enum eQAEventHistograms2D // f) QA Particle histograms 2D: for (int t = 0; t < eQAParticleHistograms2D_N; t++) // type, see enum eQAParticleHistograms2D { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!qa.fQAParticleHistograms2D[t][rs][ba]) { continue; @@ -10430,17 +10486,17 @@ void CheckUnderflowAndOverflow() LOGF(fatal, "\033[1;31m%s at line %d : overflow in y variable in fParticleHistograms2D[%d][%d][%d], for binX = %d => optimize default binning for this histogram\033[0m", __FUNCTION__, __LINE__, t, rs, ba, binX); } } // for (int binX = 0; binX <= qa.fQAParticleHistograms2D[t][rs][ba]->GetNbinsX(); binX++) { - } // for (int ba = 0; ba < 2; ba++) // before/after cuts - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int ba = 0; ba < gbaN; ba++) // before/after cuts + } // for (int rs = 0; rs < grsN; rs++) // reco/sim } // for (int t = 0; t < eQAParticleHistograms2D_N; t++) // type, see enum eParticleHistograms2D // g) QA Particle event histograms 2D: // TBI 20241212 I never validated this code block for (int t = 0; t < eQAParticleEventHistograms2D_N; t++) // type, see enum eQAParticleEventHistograms2D { - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { - for (int ba = 0; ba < 2; ba++) // before/after cuts + for (int ba = 0; ba < gbaN; ba++) // before/after cuts { if (!qa.fQAParticleEventHistograms2D[t][rs][ba]) { continue; @@ -10466,20 +10522,20 @@ void CheckUnderflowAndOverflow() LOGF(fatal, "\033[1;31m%s at line %d : overflow in y variable in fParticleEventHistograms2D[%d][%d][%d], for binX = %d => optimize default binning for this histogram\033[0m", __FUNCTION__, __LINE__, t, rs, ba, binX); } } // for (int binX = 0; binX <= qa.fQAParticleEventHistograms2D[t][rs][ba]->GetNbinsX(); binX++) { - } // for (int ba = 0; ba < 2; ba++) // before/after cuts - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int ba = 0; ba < gbaN; ba++) // before/after cuts + } // for (int rs = 0; rs < grsN; rs++) // reco/sim } // for (int t = 0; t < eQAParticleEventHistograms2D_N; t++) // type, see enum eParticleEventHistograms2D if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void CheckUnderflowAndOverflow() +} // void checkUnderflowAndOverflow() //============================================================ -template -bool ValidTrack(T const& track) +template +bool validTrack(T const& track) { // Before I start applying any track cuts, check if this is a valid track. // For instance, Run 2 or Run 1 tracklets are NOT valid tracks, as they carry no pt information, and in this function they are filtered out. @@ -10491,7 +10547,7 @@ bool ValidTrack(T const& track) // c) Additional validity checks for all tracks (in Run 3, 2 and 1), use only during debugging. if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, " track.phi() = %f", track.phi()); LOGF(info, " track.pt() = %f", track.pt()); LOGF(info, " track.eta() = %f", track.eta()); @@ -10545,16 +10601,16 @@ bool ValidTrack(T const& track) // *) All checks above survived, then it's a valid track: return true; -} // template bool ValidTrack(T const& track) +} // template bool validTrack(T const& track) //============================================================ -float GetCentralityPercentile(TString ce) +float getCentralityPercentile(TString ce) { - // Helper function for CentralityCorrelationCut(), to reduce the code bloat there. + // Helper function for centralityCorrelationCut(), to reduce the code bloat there. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // TBI 20250331 shall I add some insanity check on centrality estimator "ce" @@ -10590,30 +10646,30 @@ float GetCentralityPercentile(TString ce) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return centralityPercentile; -} // float GetCentralityPercentile(TString ce) +} // float getCentralityPercentile(TString ce) //============================================================ -bool CentralityCorrelationCut() +bool centralityCorrelationCut() { // If centrality correlation cut was requested, in this function i decide whether the current event survives it or not. - // This function is called only in EventCuts(...). I implemented it here separately merely to keep code in EventCuts(...) as clean as possible. - // If makes sense to call this function, only if in DetermineCentrality(...) I have filled in qa.Centrality . + // This function is called only in eventCuts(...). I implemented it here separately merely to keep code in eventCuts(...) as clean as possible. + // If makes sense to call this function, only if in determineCentrality(...) I have filled in qa.Centrality . // TBI 20250331 There is a bit of performance loss, because I need 2 centrality estimators to calculate correlation cut, and I fill in qa.Centrality values // for all centrality estimators. But this way I can chain several centrality correlation cuts in the future with AND condition, if necessary. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;33m%s at line %d : ec.fsEventCuts[eCentralityCorrelationsCut] = %s \033[0m", __FUNCTION__, __LINE__, ec.fsEventCuts[eCentralityCorrelationsCut].Data()); } - bool alright = true; // this local variable holds the return value for this function + bool bAlright = true; // this local variable holds the return value for this function // Algorithm: I extract e.g. from "CentFT0C_CentFT0M" that the first estimator is "CentFT0C" and second "CentFT0M", and for each estimator I fetch the corresponding centrality percentile: if (!ec.fsEventCuts[eCentralityCorrelationsCut].Contains("_")) { @@ -10621,7 +10677,8 @@ bool CentralityCorrelationCut() } TObjArray* oa = ec.fsEventCuts[eCentralityCorrelationsCut].Tokenize("_"); // TBI 20250331 let's see for how long I can use "_" safely as IFS ... - if (!oa || 2 != oa->GetEntries()) { + int nEntries = 2; // expected number of entries after this string is tokenized + if (!oa || nEntries != oa->GetEntries()) { LOGF(fatal, "\033[1;31m%s at line %d : oa is NULL , s = %s. Example format is e.g. \"CentFT0C_CentFT0M\"\033[0m", __FUNCTION__, __LINE__, ec.fsEventCuts[eCentralityCorrelationsCut].Data()); } @@ -10630,22 +10687,22 @@ bool CentralityCorrelationCut() LOGF(info, "\033[1;33m%s at line %d : oa->At(1)->GetName() = %s \033[0m", __FUNCTION__, __LINE__, oa->At(1)->GetName()); } - ec.fCentralityValues[0] = GetCentralityPercentile(oa->At(0)->GetName()); - ec.fCentralityValues[1] = GetCentralityPercentile(oa->At(1)->GetName()); + ec.fCentralityValues[0] = getCentralityPercentile(oa->At(0)->GetName()); + ec.fCentralityValues[1] = getCentralityPercentile(oa->At(1)->GetName()); delete oa; // yes // Okay, do the thing: - // *) "Relative" <=> |(firstEstimator-secondEstimator)/(firstEstimator+secondEstimator)| > treshold => reject the event => alright = false - // *) "Absolute" <=> |firstEstimator-secondEstimator| > treshold => reject the event => alright = false + // *) "Relative" <=> |(firstEstimator-secondEstimator)/(firstEstimator+secondEstimator)| > treshold => reject the event => bAlright = false + // *) "Absolute" <=> |firstEstimator-secondEstimator| > treshold => reject the event => bAlright = false // *) ... if (ec.fCentralityValues[0] > 0. && ec.fCentralityValues[1] > 0.) { if (ec.fCentralityCorrelationsCutVersion.EqualTo("Relative")) { if (std::abs((ec.fCentralityValues[0] - ec.fCentralityValues[1]) / (ec.fCentralityValues[0] + ec.fCentralityValues[1])) > ec.fCentralityCorrelationsCutTreshold) { - alright = false; + bAlright = false; } } else if (ec.fCentralityCorrelationsCutVersion.EqualTo("Absolute")) { if (std::abs((ec.fCentralityValues[0] - ec.fCentralityValues[1])) > ec.fCentralityCorrelationsCutTreshold) { - alright = false; + bAlright = false; } } else { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); @@ -10653,33 +10710,33 @@ bool CentralityCorrelationCut() } // if(ec.fCentralityValues[0] > 0. && ec.fCentralityValues[1] > 0.) if (tc.fVerbose) { - LOGF(info, "\033[1;33m%s at line %d : %f, %f, %f, %d \033[0m", __FUNCTION__, __LINE__, ec.fCentralityValues[0], ec.fCentralityValues[1], ec.fCentralityCorrelationsCutTreshold, static_cast(alright)); - ExitFunction(__FUNCTION__); + LOGF(info, "\033[1;33m%s at line %d : %f, %f, %f, %d \033[0m", __FUNCTION__, __LINE__, ec.fCentralityValues[0], ec.fCentralityValues[1], ec.fCentralityCorrelationsCutTreshold, static_cast(bAlright)); + exitFunction(__FUNCTION__); } - return alright; + return bAlright; -} // bool CentralityCorrelationCut() +} // bool centralityCorrelationCut() //============================================================ -template -void ParticleCutsCounters(T const& track) +template +void particleCutsCounters(T const& track) { // Use this function to fill absolute and sequential particle cut counters. Use only during QA, as this is computationally heavy (I mean really). if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Establish ordering of binning in particle cut counters histograms, which resembles ordering of particle cuts implementation: if (!pc.fParticleCutCounterBinLabelingIsDone) { - pc.fParticleCutCounterBinNumber[eRec] = 1; // remember that I cannot use 'rs' here as an index, because the enum eRecSim covers separately Run 1 and Run 2, etc. + pc.fParticleCutCounterBinNumber[eRec] = 1; // remember that I cannot use 'rs' here as an index, because the enum EnRecSim covers separately Run 1 and Run 2, etc. pc.fParticleCutCounterBinNumber[eSim] = 1; - ParticleCuts(track, eCutCounterBinning); // dry call, to establish the map fParticleCutCounterMap and its inverse + particleCuts(track, eCutCounterBinning); // dry call, to establish the map fParticleCutCounterMap and its inverse // **) Map this ordering into bin labels of actual histograms for particle cut counters: - for (int rec_sim = 0; rec_sim < 2; rec_sim++) // reco/sim => I use here exceptionally different var 'rec_sim', not the shadow 'rs' in the template parameter + for (int rec_sim = 0; rec_sim < grsN; rec_sim++) // reco/sim => I use here exceptionally different var 'rec_sim', not the shadow 'rs' in the template parameter { for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter { @@ -10691,7 +10748,7 @@ void ParticleCutsCounters(T const& track) for (int bin = 1; bin < pc.fParticleCutCounterBinNumber[rec_sim]; bin++) // implemented and used particle cuts in this analysis { - pc.fParticleCutCounterHist[rec_sim][cc]->GetXaxis()->SetBinLabel(bin, FancyFormatting(pc.fParticleCutName[pc.fParticleCutCounterMap[rec_sim]->GetValue(bin)].Data())); + pc.fParticleCutCounterHist[rec_sim][cc]->GetXaxis()->SetBinLabel(bin, fancyFormatting(pc.fParticleCutName[pc.fParticleCutCounterMap[rec_sim]->GetValue(bin)].Data())); } for (int bin = pc.fParticleCutCounterBinNumber[rec_sim]; bin <= eParticleCuts_N; bin++) // implemented, but unused particle cuts in this analysis { @@ -10708,7 +10765,7 @@ void ParticleCutsCounters(T const& track) TString yAxisTitle = ""; for (int bin = 1; bin < pc.fParticleCutCounterBinNumber[rec_sim]; bin++) // implemented and used cuts in this analysis { - yAxisTitle += TString::Format("%d:%s; ", bin, FancyFormatting(pc.fParticleCutName[pc.fParticleCutCounterMap[rec_sim]->GetValue(bin)].Data())); + yAxisTitle += TString::Format("%d:%s; ", bin, fancyFormatting(pc.fParticleCutName[pc.fParticleCutCounterMap[rec_sim]->GetValue(bin)].Data())); } for (int bin = pc.fParticleCutCounterBinNumber[rec_sim]; bin <= eParticleCuts_N; bin++) // implemented, but unused cuts in this analysis { @@ -10743,22 +10800,22 @@ void ParticleCutsCounters(T const& track) if (pc.fUseParticleCutCounterAbsolute) { pc.fParticleCutCounterBinNumber[eRec] = 1; pc.fParticleCutCounterBinNumber[eSim] = 1; - ParticleCuts(track, eCutCounterAbsolute); + particleCuts(track, eCutCounterAbsolute); } // *) Particle cut counter (sequential): if (pc.fUseParticleCutCounterSequential) { pc.fParticleCutCounterBinNumber[eRec] = 1; pc.fParticleCutCounterBinNumber[eSim] = 1; - ParticleCuts(track, eCutCounterSequential); + particleCuts(track, eCutCounterSequential); } -} // template void ParticleCutsCounters(T const& track) +} // template void particleCutsCounters(T const& track) //============================================================ -template -bool ParticleCuts(T const& track, eCutModus cutModus) +template +bool particleCuts(T const& track, EnCutModus cutModus) { // Particle cuts on reconstructed and simulated data. Supports particle cut counters, both absolute and sequential. // There is also a related enum eParticleCuts. @@ -10777,7 +10834,7 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // :pc if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Particle cuts on reconstructed, and corresponding MC truth simulated (common to Run 3, Run 2 and Run 1) ... @@ -10786,9 +10843,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Phi: if (pc.fUseParticleCuts[ePhi]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, ePhi, eCutCounterBinning); + particleCut(eRec, ePhi, eCutCounterBinning); } else if (track.phi() < pc.fdParticleCuts[ePhi][eMin] || track.phi() > pc.fdParticleCuts[ePhi][eMax] || std::abs(track.phi() - pc.fdParticleCuts[ePhi][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, ePhi, cutModus)) { + if (!particleCut(eRec, ePhi, cutModus)) { return false; } } @@ -10797,9 +10854,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Pt: if (pc.fUseParticleCuts[ePt]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, ePt, eCutCounterBinning); + particleCut(eRec, ePt, eCutCounterBinning); } else if (track.pt() < pc.fdParticleCuts[ePt][eMin] || track.pt() > pc.fdParticleCuts[ePt][eMax] || std::abs(track.pt() - pc.fdParticleCuts[ePt][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, ePt, cutModus)) { + if (!particleCut(eRec, ePt, cutModus)) { return false; } } @@ -10808,9 +10865,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Eta: if (pc.fUseParticleCuts[eEta]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eEta, eCutCounterBinning); + particleCut(eRec, eEta, eCutCounterBinning); } else if (track.eta() < pc.fdParticleCuts[eEta][eMin] || track.eta() > pc.fdParticleCuts[eEta][eMax] || std::abs(track.eta() - pc.fdParticleCuts[eEta][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, eEta, cutModus)) { + if (!particleCut(eRec, eEta, cutModus)) { return false; } } @@ -10819,11 +10876,11 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Charge: if (pc.fUseParticleCuts[eCharge]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eCharge, eCutCounterBinning); + particleCut(eRec, eCharge, eCutCounterBinning); } else if (0 == track.sign() || track.sign() < pc.fdParticleCuts[eCharge][eMin] || track.sign() > pc.fdParticleCuts[eCharge][eMax]) { // With first condition, I always throw away neutral particles. // I can use safely == here, because track.sign() returns short int. - if (!ParticleCut(eRec, eCharge, cutModus)) { + if (!particleCut(eRec, eCharge, cutModus)) { return false; } } @@ -10832,9 +10889,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcNClsFindable: if (pc.fUseParticleCuts[etpcNClsFindable]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcNClsFindable, eCutCounterBinning); + particleCut(eRec, etpcNClsFindable, eCutCounterBinning); } else if (track.tpcNClsFindable() < pc.fdParticleCuts[etpcNClsFindable][eMin] || track.tpcNClsFindable() > pc.fdParticleCuts[etpcNClsFindable][eMax]) { - if (!ParticleCut(eRec, etpcNClsFindable, cutModus)) { + if (!particleCut(eRec, etpcNClsFindable, cutModus)) { return false; } } @@ -10843,9 +10900,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcNClsShared: if (pc.fUseParticleCuts[etpcNClsShared]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcNClsShared, eCutCounterBinning); + particleCut(eRec, etpcNClsShared, eCutCounterBinning); } else if (track.tpcNClsShared() < pc.fdParticleCuts[etpcNClsShared][eMin] || track.tpcNClsShared() > pc.fdParticleCuts[etpcNClsShared][eMax]) { - if (!ParticleCut(eRec, etpcNClsShared, cutModus)) { + if (!particleCut(eRec, etpcNClsShared, cutModus)) { return false; } } @@ -10854,9 +10911,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) itsChi2NCl if (pc.fUseParticleCuts[eitsChi2NCl]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eitsChi2NCl, eCutCounterBinning); + particleCut(eRec, eitsChi2NCl, eCutCounterBinning); } else if (track.itsChi2NCl() < pc.fdParticleCuts[eitsChi2NCl][eMin] || track.itsChi2NCl() > pc.fdParticleCuts[eitsChi2NCl][eMax]) { - if (!ParticleCut(eRec, eitsChi2NCl, cutModus)) { + if (!particleCut(eRec, eitsChi2NCl, cutModus)) { return false; } } @@ -10865,9 +10922,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcNClsFound: if (pc.fUseParticleCuts[etpcNClsFound]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcNClsFound, eCutCounterBinning); + particleCut(eRec, etpcNClsFound, eCutCounterBinning); } else if (track.tpcNClsFound() < pc.fdParticleCuts[etpcNClsFound][eMin] || track.tpcNClsFound() > pc.fdParticleCuts[etpcNClsFound][eMax]) { - if (!ParticleCut(eRec, etpcNClsFound, cutModus)) { + if (!particleCut(eRec, etpcNClsFound, cutModus)) { return false; } } @@ -10876,9 +10933,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcNClsCrossedRows: if (pc.fUseParticleCuts[etpcNClsCrossedRows]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcNClsCrossedRows, eCutCounterBinning); + particleCut(eRec, etpcNClsCrossedRows, eCutCounterBinning); } else if (track.tpcNClsCrossedRows() < pc.fdParticleCuts[etpcNClsCrossedRows][eMin] || track.tpcNClsCrossedRows() > pc.fdParticleCuts[etpcNClsCrossedRows][eMax]) { - if (!ParticleCut(eRec, etpcNClsCrossedRows, cutModus)) { + if (!particleCut(eRec, etpcNClsCrossedRows, cutModus)) { return false; } } @@ -10887,9 +10944,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) itsNCls: if (pc.fUseParticleCuts[eitsNCls]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eitsNCls, eCutCounterBinning); + particleCut(eRec, eitsNCls, eCutCounterBinning); } else if (track.itsNCls() < pc.fdParticleCuts[eitsNCls][eMin] || track.itsNCls() > pc.fdParticleCuts[eitsNCls][eMax]) { - if (!ParticleCut(eRec, eitsNCls, cutModus)) { + if (!particleCut(eRec, eitsNCls, cutModus)) { return false; } } @@ -10898,9 +10955,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) itsNClsInnerBarrel: if (pc.fUseParticleCuts[eitsNClsInnerBarrel]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eitsNClsInnerBarrel, eCutCounterBinning); + particleCut(eRec, eitsNClsInnerBarrel, eCutCounterBinning); } else if (track.itsNClsInnerBarrel() < pc.fdParticleCuts[eitsNClsInnerBarrel][eMin] || track.itsNClsInnerBarrel() > pc.fdParticleCuts[eitsNClsInnerBarrel][eMax]) { - if (!ParticleCut(eRec, eitsNClsInnerBarrel, cutModus)) { + if (!particleCut(eRec, eitsNClsInnerBarrel, cutModus)) { return false; } } @@ -10909,9 +10966,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcCrossedRowsOverFindableCls: if (pc.fUseParticleCuts[etpcCrossedRowsOverFindableCls]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcCrossedRowsOverFindableCls, eCutCounterBinning); + particleCut(eRec, etpcCrossedRowsOverFindableCls, eCutCounterBinning); } else if (track.tpcCrossedRowsOverFindableCls() < pc.fdParticleCuts[etpcCrossedRowsOverFindableCls][eMin] || track.tpcCrossedRowsOverFindableCls() > pc.fdParticleCuts[etpcCrossedRowsOverFindableCls][eMax] || std::abs(track.tpcCrossedRowsOverFindableCls() - pc.fdParticleCuts[etpcCrossedRowsOverFindableCls][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, etpcCrossedRowsOverFindableCls, cutModus)) { + if (!particleCut(eRec, etpcCrossedRowsOverFindableCls, cutModus)) { return false; } } @@ -10920,9 +10977,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcFoundOverFindableCls: if (pc.fUseParticleCuts[etpcFoundOverFindableCls]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcFoundOverFindableCls, eCutCounterBinning); + particleCut(eRec, etpcFoundOverFindableCls, eCutCounterBinning); } else if (track.tpcFoundOverFindableCls() < pc.fdParticleCuts[etpcFoundOverFindableCls][eMin] || track.tpcFoundOverFindableCls() > pc.fdParticleCuts[etpcFoundOverFindableCls][eMax] || std::abs(track.tpcFoundOverFindableCls() - pc.fdParticleCuts[etpcFoundOverFindableCls][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, etpcFoundOverFindableCls, cutModus)) { + if (!particleCut(eRec, etpcFoundOverFindableCls, cutModus)) { return false; } } @@ -10931,9 +10988,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcFractionSharedCls: if (pc.fUseParticleCuts[etpcFractionSharedCls]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcFractionSharedCls, eCutCounterBinning); + particleCut(eRec, etpcFractionSharedCls, eCutCounterBinning); } else if (track.tpcFractionSharedCls() < pc.fdParticleCuts[etpcFractionSharedCls][eMin] || track.tpcFractionSharedCls() > pc.fdParticleCuts[etpcFractionSharedCls][eMax] || std::abs(track.tpcFractionSharedCls() - pc.fdParticleCuts[etpcFractionSharedCls][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, etpcFractionSharedCls, cutModus)) { + if (!particleCut(eRec, etpcFractionSharedCls, cutModus)) { return false; } } @@ -10942,9 +10999,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) tpcChi2NCl: if (pc.fUseParticleCuts[etpcChi2NCl]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etpcChi2NCl, eCutCounterBinning); + particleCut(eRec, etpcChi2NCl, eCutCounterBinning); } else if (track.tpcChi2NCl() < pc.fdParticleCuts[etpcChi2NCl][eMin] || track.tpcChi2NCl() > pc.fdParticleCuts[etpcChi2NCl][eMax] || std::abs(track.tpcChi2NCl() - pc.fdParticleCuts[etpcChi2NCl][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, etpcChi2NCl, cutModus)) { + if (!particleCut(eRec, etpcChi2NCl, cutModus)) { return false; } } @@ -10953,9 +11010,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) dcaXY: if (pc.fUseParticleCuts[edcaXY]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, edcaXY, eCutCounterBinning); + particleCut(eRec, edcaXY, eCutCounterBinning); } else if (track.dcaXY() < pc.fdParticleCuts[edcaXY][eMin] || track.dcaXY() > pc.fdParticleCuts[edcaXY][eMax] || std::abs(track.dcaXY() - pc.fdParticleCuts[edcaXY][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, edcaXY, cutModus)) { + if (!particleCut(eRec, edcaXY, cutModus)) { return false; } } @@ -10964,9 +11021,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) dcaZ: if (pc.fUseParticleCuts[edcaZ]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, edcaZ, eCutCounterBinning); + particleCut(eRec, edcaZ, eCutCounterBinning); } else if (track.dcaZ() < pc.fdParticleCuts[edcaZ][eMin] || track.dcaZ() > pc.fdParticleCuts[edcaZ][eMax] || std::abs(track.dcaZ() - pc.fdParticleCuts[edcaZ][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, edcaZ, cutModus)) { + if (!particleCut(eRec, edcaZ, cutModus)) { return false; } } @@ -10975,9 +11032,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) trackCutFlag: if (pc.fUseParticleCuts[etrackCutFlag]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etrackCutFlag, eCutCounterBinning); + particleCut(eRec, etrackCutFlag, eCutCounterBinning); } else if (!track.trackCutFlag()) { - if (!ParticleCut(eRec, etrackCutFlag, cutModus)) { + if (!particleCut(eRec, etrackCutFlag, cutModus)) { return false; } } @@ -10986,9 +11043,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) trackCutFlagFb1: if (pc.fUseParticleCuts[etrackCutFlagFb1]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etrackCutFlagFb1, eCutCounterBinning); + particleCut(eRec, etrackCutFlagFb1, eCutCounterBinning); } else if (!track.trackCutFlagFb1()) { - if (!ParticleCut(eRec, etrackCutFlagFb1, cutModus)) { + if (!particleCut(eRec, etrackCutFlagFb1, cutModus)) { return false; } } @@ -10997,9 +11054,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) trackCutFlagFb2: if (pc.fUseParticleCuts[etrackCutFlagFb2]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, etrackCutFlagFb2, eCutCounterBinning); + particleCut(eRec, etrackCutFlagFb2, eCutCounterBinning); } else if (!track.trackCutFlagFb2()) { - if (!ParticleCut(eRec, etrackCutFlagFb2, cutModus)) { + if (!particleCut(eRec, etrackCutFlagFb2, cutModus)) { return false; } } @@ -11008,9 +11065,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) isQualityTrack: if (pc.fUseParticleCuts[eisQualityTrack]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eisQualityTrack, eCutCounterBinning); + particleCut(eRec, eisQualityTrack, eCutCounterBinning); } else if (!track.isQualityTrack()) { - if (!ParticleCut(eRec, eisQualityTrack, cutModus)) { + if (!particleCut(eRec, eisQualityTrack, cutModus)) { return false; } } @@ -11019,9 +11076,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) isPrimaryTrack: if (pc.fUseParticleCuts[eisPrimaryTrack]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eisPrimaryTrack, eCutCounterBinning); + particleCut(eRec, eisPrimaryTrack, eCutCounterBinning); } else if (!track.isPrimaryTrack()) { - if (!ParticleCut(eRec, eisPrimaryTrack, cutModus)) { + if (!particleCut(eRec, eisPrimaryTrack, cutModus)) { return false; } } @@ -11030,9 +11087,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) isInAcceptanceTrack: if (pc.fUseParticleCuts[eisInAcceptanceTrack]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eisInAcceptanceTrack, eCutCounterBinning); + particleCut(eRec, eisInAcceptanceTrack, eCutCounterBinning); } else if (!track.isInAcceptanceTrack()) { - if (!ParticleCut(eRec, eisInAcceptanceTrack, cutModus)) { + if (!particleCut(eRec, eisInAcceptanceTrack, cutModus)) { return false; } } @@ -11041,9 +11098,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) isGlobalTrack: if (pc.fUseParticleCuts[eisGlobalTrack]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eisGlobalTrack, eCutCounterBinning); + particleCut(eRec, eisGlobalTrack, eCutCounterBinning); } else if (!track.isGlobalTrack()) { - if (!ParticleCut(eRec, eisGlobalTrack, cutModus)) { + if (!particleCut(eRec, eisGlobalTrack, cutModus)) { return false; } } @@ -11052,9 +11109,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) isPVContributor: if (pc.fUseParticleCuts[eisPVContributor]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eisPVContributor, eCutCounterBinning); + particleCut(eRec, eisPVContributor, eCutCounterBinning); } else if (!track.isPVContributor()) { - if (!ParticleCut(eRec, eisPVContributor, cutModus)) { + if (!particleCut(eRec, eisPVContributor, cutModus)) { return false; } } @@ -11063,9 +11120,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) PtDependentDCAxyParameterization: if (pc.fUseParticleCuts[ePtDependentDCAxyParameterization]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, ePtDependentDCAxyParameterization, eCutCounterBinning); + particleCut(eRec, ePtDependentDCAxyParameterization, eCutCounterBinning); } else if (std::abs(track.dcaXY()) > pc.fPtDependentDCAxyFormula->Eval(track.pt())) { - if (!ParticleCut(eRec, ePtDependentDCAxyParameterization, cutModus)) { + if (!particleCut(eRec, ePtDependentDCAxyParameterization, cutModus)) { return false; } } @@ -11085,14 +11142,14 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // auto mcParticle = track.mcParticle(); // corresponding MC truth simulated particle // In this branch I can cut additionally and directly on corresponding MC truth simulated, e.g. on mcParticle.pt() - // In case I implement something here, remember to switch from eRec to eSim when calling e.g. ParticleCut(...) + // In case I implement something here, remember to switch from eRec to eSim when calling e.g. particleCut(...) // // *) Phi: TBI 2024-511 re-think if i really cut directly on MC truth kine and other info and keep it in sync with what I did in AliPhysics // if (pc.fUseParticleCuts[ePhi]) { // if (cutModus == eCutCounterBinning) { - // ParticleCut(eSim, ePhi, eCutCounterBinning); + // particleCut(eSim, ePhi, eCutCounterBinning); // } else if (mcParticle.phi() < pc.fdParticleCuts[ePhi][eMin] || mcParticle.phi() > pc.fdParticleCuts[ePhi][eMax]) { - // if (!ParticleCut(eSim, ePhi, cutModus)) { + // if (!particleCut(eSim, ePhi, cutModus)) { // return false; // } // } @@ -11102,10 +11159,10 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // if (pc.fUseParticleCuts[eCharge]) { // if (cutModus == eCutCounterBinning) { - // ParticleCut(eSim, eCharge, eCutCounterBinning); + // particleCut(eSim, eCharge, eCutCounterBinning); // } else if (0 == mcParticle.sign() || mcParticle.sign() < pc.fdParticleCuts[eCharge][eMin] || mcParticle.sign() > pc.fdParticleCuts[eCharge][eMax]) { // // TBI 20240511 with first condition, I always throw away neutral particles, so for the time being that is hardcoded - // if (!ParticleCut(eSim, eCharge, cutModus)) { + // if (!particleCut(eSim, eCharge, cutModus)) { // return false; // } // } @@ -11129,9 +11186,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Phi: if (pc.fUseParticleCuts[ePhi]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eSim, ePhi, eCutCounterBinning); + particleCut(eSim, ePhi, eCutCounterBinning); } else if (track.phi() < pc.fdParticleCuts[ePhi][eMin] || track.phi() > pc.fdParticleCuts[ePhi][eMax] || std::abs(track.phi() - pc.fdParticleCuts[ePhi][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eSim, ePhi, cutModus)) { + if (!particleCut(eSim, ePhi, cutModus)) { return false; } } @@ -11140,9 +11197,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Pt: if (pc.fUseParticleCuts[ePt]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eSim, ePt, eCutCounterBinning); + particleCut(eSim, ePt, eCutCounterBinning); } else if (track.pt() < pc.fdParticleCuts[ePt][eMin] || track.pt() > pc.fdParticleCuts[ePt][eMax] || std::abs(track.pt() - pc.fdParticleCuts[ePt][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eSim, ePt, cutModus)) { + if (!particleCut(eSim, ePt, cutModus)) { return false; } } @@ -11151,9 +11208,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Eta: if (pc.fUseParticleCuts[eEta]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eSim, eEta, eCutCounterBinning); + particleCut(eSim, eEta, eCutCounterBinning); } else if (track.eta() < pc.fdParticleCuts[eEta][eMin] || track.eta() > pc.fdParticleCuts[eEta][eMax] || std::abs(track.eta() - pc.fdParticleCuts[eEta][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eSim, eEta, cutModus)) { + if (!particleCut(eSim, eEta, cutModus)) { return false; } } @@ -11167,16 +11224,16 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // Yes, I have to check the 2nd condition, because e.g. for PDG code 1000010020 (deuteron), GetParticle(...) returns NULL charge = tc.fDatabasePDG->GetParticle(track.pdgCode())->Charge() / 3.; // yes, divided by 3. Fundamental unit of charge is associated with quarks if (tc.fVerboseForEachParticle) { - LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! There is a large memory blow-up when using TDatabasePDG !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); + LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! There is a large memory blow-up when using PDG database !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); } } if (cutModus == eCutCounterBinning) { - ParticleCut(eSim, eCharge, eCutCounterBinning); + particleCut(eSim, eCharge, eCutCounterBinning); } else if (0 == static_cast(charge) || charge < pc.fdParticleCuts[eCharge][eMin] || charge > pc.fdParticleCuts[eCharge][eMax]) { // TBI 20250611 with first condition, I always throw away neutral particles when O2DatabasePDG is used. // However due to initialization charge = 0. that way I throw all particles when O2DatabasePDG is NOT used. // Therefore, when O2DatabasePDG is NOT used, I have to disable cut on charge, since that info is not available. - if (!ParticleCut(eSim, eCharge, cutModus)) { + if (!particleCut(eSim, eCharge, cutModus)) { return false; } } @@ -11187,10 +11244,10 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) PDG code: if (pc.fUseParticleCuts[ePDG]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eSim, ePDG, eCutCounterBinning); + particleCut(eSim, ePDG, eCutCounterBinning); } else if (track.pdgCode() < pc.fdParticleCuts[ePDG][eMin] || track.pdgCode() > pc.fdParticleCuts[ePDG][eMax]) { // TBI 20250611 I need to generalize this, e.g. add support to process more that one PDG code (e.g. 2212 and -2212, etc.) - if (!ParticleCut(eSim, ePDG, cutModus)) { + if (!particleCut(eSim, ePDG, cutModus)) { return false; } } @@ -11282,9 +11339,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Phi: if (pc.fUseParticleCuts[ePhi]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, ePhi, eCutCounterBinning); + particleCut(eRec, ePhi, eCutCounterBinning); } else if (track.phi() < pc.fdParticleCuts[ePhi][eMin] || track.phi() > pc.fdParticleCuts[ePhi][eMax] || std::abs(track.phi() - pc.fdParticleCuts[ePhi][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, ePhi, cutModus)) { + if (!particleCut(eRec, ePhi, cutModus)) { return false; } } @@ -11293,9 +11350,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Pt: if (pc.fUseParticleCuts[ePt]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, ePt, eCutCounterBinning); + particleCut(eRec, ePt, eCutCounterBinning); } else if (track.pt() < pc.fdParticleCuts[ePt][eMin] || track.pt() > pc.fdParticleCuts[ePt][eMax] || std::abs(track.pt() - pc.fdParticleCuts[ePt][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, ePt, cutModus)) { + if (!particleCut(eRec, ePt, cutModus)) { return false; } } @@ -11304,9 +11361,9 @@ bool ParticleCuts(T const& track, eCutModus cutModus) // *) Eta: if (pc.fUseParticleCuts[eEta]) { if (cutModus == eCutCounterBinning) { - ParticleCut(eRec, eEta, eCutCounterBinning); + particleCut(eRec, eEta, eCutCounterBinning); } else if (track.eta() < pc.fdParticleCuts[eEta][eMin] || track.eta() > pc.fdParticleCuts[eEta][eMax] || std::abs(track.eta() - pc.fdParticleCuts[eEta][eMax]) < tc.fFloatingPointPrecision) { - if (!ParticleCut(eRec, eEta, cutModus)) { + if (!particleCut(eRec, eEta, cutModus)) { return false; } } @@ -11339,16 +11396,16 @@ bool ParticleCuts(T const& track, eCutModus cutModus) dCharge = track.sign(); // Apply NUA on these kine variables: - if (nua.fApplyNUAPDF[ePhiNUAPDF] && !Accept(dPhi, ePhiNUAPDF)) { + if (nua.fApplyNUAPDF[ePhiNUAPDF] && !accept(dPhi, ePhiNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[ePtNUAPDF] && !Accept(dPt, ePtNUAPDF)) { + if (nua.fApplyNUAPDF[ePtNUAPDF] && !accept(dPt, ePtNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[eEtaNUAPDF] && !Accept(dEta, eEtaNUAPDF)) { + if (nua.fApplyNUAPDF[eEtaNUAPDF] && !accept(dEta, eEtaNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[eChargeNUAPDF] && !Accept(dCharge, eChargeNUAPDF)) { + if (nua.fApplyNUAPDF[eChargeNUAPDF] && !accept(dCharge, eChargeNUAPDF)) { return false; } @@ -11370,16 +11427,16 @@ bool ParticleCuts(T const& track, eCutModus cutModus) } // Apply NUA on these kine variables: - if (nua.fApplyNUAPDF[ePhiNUAPDF] && !Accept(dPhi, ePhiNUAPDF)) { + if (nua.fApplyNUAPDF[ePhiNUAPDF] && !accept(dPhi, ePhiNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[ePtNUAPDF] && !Accept(dPt, ePtNUAPDF)) { + if (nua.fApplyNUAPDF[ePtNUAPDF] && !accept(dPt, ePtNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[eEtaNUAPDF] && !Accept(dEta, eEtaNUAPDF)) { + if (nua.fApplyNUAPDF[eEtaNUAPDF] && !accept(dEta, eEtaNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[eChargeNUAPDF] && !Accept(dCharge, eChargeNUAPDF)) { + if (nua.fApplyNUAPDF[eChargeNUAPDF] && !accept(dCharge, eChargeNUAPDF)) { return false; } @@ -11400,16 +11457,16 @@ bool ParticleCuts(T const& track, eCutModus cutModus) } // Apply NUA on these kine variables: - if (nua.fApplyNUAPDF[ePhiNUAPDF] && !Accept(dPhi, ePhiNUAPDF)) { + if (nua.fApplyNUAPDF[ePhiNUAPDF] && !accept(dPhi, ePhiNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[ePtNUAPDF] && !Accept(dPt, ePtNUAPDF)) { + if (nua.fApplyNUAPDF[ePtNUAPDF] && !accept(dPt, ePtNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[eEtaNUAPDF] && !Accept(dEta, eEtaNUAPDF)) { + if (nua.fApplyNUAPDF[eEtaNUAPDF] && !accept(dEta, eEtaNUAPDF)) { return false; } - if (nua.fApplyNUAPDF[eChargeNUAPDF] && !Accept(dCharge, eChargeNUAPDF)) { + if (nua.fApplyNUAPDF[eChargeNUAPDF] && !accept(dCharge, eChargeNUAPDF)) { return false; } } // if constexpr (rs == eSim || rs == eSim_Run2 || rs == eSim_Run1) { @@ -11418,40 +11475,36 @@ bool ParticleCuts(T const& track, eCutModus cutModus) return true; -} // template bool ParticleCuts(T const& track, eCutModus cutModus) +} // template bool particleCuts(T const& track, EnCutModus cutModus) //============================================================ -bool ParticleCut(int rs, int particleCut, eCutModus cutModus) +bool particleCut(int rs, int iParticleCut, EnCutModus cutModus) { - // Helper function to reduce code bloat in ParticleCuts(). It's meant to be used only in ParticleCuts(). + // Helper function to reduce code bloat in particleCuts(). It's meant to be used only in particleCuts(). // Remark: Remember that as a second argument I cannot use enum eParticleCuts, because here in one go I take both enum eParticleCuts and enum eParticleHistograms . switch (cutModus) { case eCut: { if (tc.fVerboseForEachParticle) { - LOGF(info, "\033[1;31mParticle didn't pass the cut: %s\033[0m", pc.fParticleCutName[particleCut].Data()); + LOGF(info, "\033[1;31mParticle didn't pass the cut: %s\033[0m", pc.fParticleCutName[iParticleCut].Data()); } return false; - break; } case eCutCounterBinning: { - pc.fParticleCutCounterMap[rs]->Add(pc.fParticleCutCounterBinNumber[rs], particleCut); - pc.fParticleCutCounterMapInverse[rs]->Add(particleCut, pc.fParticleCutCounterBinNumber[rs]); + pc.fParticleCutCounterMap[rs]->Add(pc.fParticleCutCounterBinNumber[rs], iParticleCut); + pc.fParticleCutCounterMapInverse[rs]->Add(iParticleCut, pc.fParticleCutCounterBinNumber[rs]); pc.fParticleCutCounterBinNumber[rs]++; // yes return true; - break; } case eCutCounterAbsolute: { - pc.fParticleCutCounterHist[rs][eAbsolute]->Fill(pc.fParticleCutCounterMapInverse[rs]->GetValue(particleCut)); - return true; // yes, so that I can proceed with another cut in ParticleCuts - break; + pc.fParticleCutCounterHist[rs][eAbsolute]->Fill(pc.fParticleCutCounterMapInverse[rs]->GetValue(iParticleCut)); + return true; // yes, so that I can proceed with another cut in particleCuts } case eCutCounterSequential: { - pc.fParticleCutCounterHist[rs][eSequential]->Fill(pc.fParticleCutCounterMapInverse[rs]->GetValue(particleCut)); - return false; // yes, so that I bail out from ParticleCuts - break; + pc.fParticleCutCounterHist[rs][eSequential]->Fill(pc.fParticleCutCounterMapInverse[rs]->GetValue(iParticleCut)); + return false; // yes, so that I bail out from particleCuts } default: { LOGF(fatal, "\033[1;31m%s at line %d : This cutModus = %d is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(cutModus)); @@ -11461,12 +11514,12 @@ bool ParticleCut(int rs, int particleCut, eCutModus cutModus) return false; // obsolete, but it suppresses the warning... -} // bool ParticleCut(int rs, int particleCut, eCutModus cutModus) +} // bool particleCut(int rs, int iParticleCut, EnCutModus cutModus) //============================================================ -template -void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) +template +void fillParticleHistograms(T const& track, EnBeforeAfter ba, int weight = 1) { // Fill all particle histograms for reconstructed and simulated data. @@ -11478,17 +11531,17 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) // f) Fill only simulated (Run 1 and 2 specific); // In case there is some corner case between Run 1 and Run 2, simply branch further this one // i) Test case. - // Remark #1: Why weight is introduced as 3rd argument, see explanation in BanishmentLoopOverParticles. + // Remark #1: Why weight is introduced as 3rd argument, see explanation in banishmentLoopOverParticles. // The efficiency loss for default fill between weight = 1 and previous implementation with no weight, is negligible. - // Remark #2: After calling BanishmentLoopOverParticles, the GetMean(), GetRMS(), GetStdDev(), skewness, kurtosis, etc., of histogram are unaffected. + // Remark #2: After calling banishmentLoopOverParticles, the GetMean(), GetRMS(), GetStdDev(), skewness, kurtosis, etc., of histogram are unaffected. // But GetMeanErorr(), GetRMSError(), etc. are affected. // For instance, GetMean() remains the same, because (x+y)/(1+1) = (x+y+z-z)(1+1+1-1). But whenever weight in the formula is taken directly to some higher power, - // like in the calculation of GetMeanError(), this idea with BanishmentLoopOverParticles is not applicable (also when I enable Setw2() in histograms). + // like in the calculation of GetMeanError(), this idea with banishmentLoopOverParticles is not applicable (also when I enable Setw2() in histograms). // Since from particle histograms I only care about the number of entries, I rarely need even GetMean(), and basically never GetMeanError(), - // I use BanishmentLoopOverParticles . Alternatively, I would need new set of histograms, fill them separately, etc. + // I use banishmentLoopOverParticles . Alternatively, I would need new set of histograms, fill them separately, etc. if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if (tc.fInsanityCheckForEachParticle) { @@ -11500,7 +11553,7 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) // a) Fill reconstructed ... (common to Run 3, Run 2 and Run 1): if constexpr (rs == eRec || rs == eRecAndSim || rs == eRec_Run2 || rs == eRecAndSim_Run2 || rs == eRec_Run1 || rs == eRecAndSim_Run1 || rs == eQA) { // Remark: Remember to use only eRec and eSim as array indices in histos, also for rs == eRecAndSim, etc. TBI 20240504 shall I introduce generic enum egRec and egSim for this sake? - // TBI 20240414 also here have to hardcode 'eRec', because 'rs' spans over all enums in eRecSim => I definitely need 'generic Rec' case, perhaps via TExMap ? + // TBI 20240414 also here have to hardcode 'eRec', because 'rs' spans over all enums in EnRecSim => I definitely need 'generic Rec' case, perhaps via TExMap ? // But I have already tc.fProcess[eGenericRec] and tc.fProcess[eGenericRecSim], available, shall I simply re-use them? // 1D: @@ -11548,19 +11601,19 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) // **) eDWPt : here the fundamental 0-th axis never to be projected out is "pt" if (ph.fBookParticleSparseHistograms[eDWPt]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffPtWeights - double vector[eDiffPtWeights_N] = {track.pt(), track.eta(), static_cast(track.sign()), ebye.fCentrality}; + double vector[eDiffPtWeights_N] = {track.pt(), track.eta(), static_cast(track.sign()), ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWPt][eRec][ba]->Fill(vector, weight); } // **) eDWEta : here the fundamental 0-th axis never to be projected out is "eta" if (ph.fBookParticleSparseHistograms[eDWEta]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffEtaWeights - double vector[eDiffEtaWeights_N] = {track.eta(), track.pt(), static_cast(track.sign()), ebye.fCentrality}; + double vector[eDiffEtaWeights_N] = {track.eta(), track.pt(), static_cast(track.sign()), ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWEta][eRec][ba]->Fill(vector, weight); } // **) eDWCharge : here the fundamental 0-th axis never to be projected out is "charge" if (ph.fBookParticleSparseHistograms[eDWCharge]) { // Remark: It is mandatory that ordering in initialization here resembles the ordering in enum eDiffChargeWeights - double vector[eDiffChargeWeights_N] = {static_cast(track.sign()), track.pt(), track.eta(), ebye.fCentrality}; + double vector[eDiffChargeWeights_N] = {static_cast(track.sign()), track.pt(), track.eta(), ebye.fCentrality, ebye.fVz}; ph.fParticleSparseHistograms[eDWCharge][eRec][ba]->Fill(vector, weight); } } // if (ba == eAfter ... ) { @@ -11579,21 +11632,25 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(eitsNClsPosEtaEbyE) - 0.5, track.itsNCls(), weight); } - if (-0.8 < track.eta() && track.eta() < -0.4) { + const int nEtaIntervals = 5; + float etaIntervals[nEtaIntervals] = {-0.8, -0.4, 0.0, 0.4, 0.8}; + if (etaIntervals[0] < track.eta() && track.eta() < etaIntervals[1]) { qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(eEta0804EbyE) - 0.5, track.eta(), weight); - } else if (-0.4 < track.eta() && track.eta() < 0.0) { + } else if (etaIntervals[1] < track.eta() && track.eta() < etaIntervals[2]) { qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(eEta0400EbyE) - 0.5, track.eta(), weight); - } else if (0.0 < track.eta() && track.eta() < 0.4) { + } else if (etaIntervals[2] < track.eta() && track.eta() < etaIntervals[3]) { qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(eEta0004EbyE) - 0.5, track.eta(), weight); - } else if (0.4 < track.eta() && track.eta() < 0.8) { + } else if (etaIntervals[3] < track.eta() && track.eta() < etaIntervals[4]) { qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(eEta0408EbyE) - 0.5, track.eta(), weight); } - if (0.0 < track.pt() && track.pt() < 0.5) { + const int nPtIntervals = 4; + float ptIntervals[nPtIntervals] = {0.0, 0.5, 1.0, 5.0}; + if (ptIntervals[0] < track.pt() && track.pt() < ptIntervals[1]) { qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(ePt0005EbyE) - 0.5, track.pt(), weight); - } else if (0.5 < track.pt() && track.pt() < 1.0) { + } else if (ptIntervals[1] < track.pt() && track.pt() < ptIntervals[2]) { qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(ePt0510EbyE) - 0.5, track.pt(), weight); - } else if (1.0 < track.pt() && track.pt() < 5.0) { + } else if (ptIntervals[2] < track.pt() && track.pt() < ptIntervals[3]) { qa.fQAParticleEventProEbyE[eRec][ba]->Fill(static_cast(ePt1050EbyE) - 0.5, track.pt(), weight); } @@ -11699,6 +11756,7 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) } double vector[eDiffPhiWeights_N] = {mcParticle.phi(), mcParticle.pt(), mcParticle.eta(), charge, ebye.fCentralitySim, 0.}; // TBI 20250611 I do nothing for vertex z, I could trivially extend ebye.fVz also for "sim" dimension => I set it to 0 temporarily here, until that's done. + // TBI 20260422 If I ever do it, propagate that change also in 3 sparse histograms below ph.fParticleSparseHistograms[eDWPhi][eSim][ba]->Fill(vector, weight); } // **) eDWPt : here the fundamental 0-th axis never to be projected out is "pt" @@ -11711,7 +11769,7 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) // Yes, I have to check the 2nd condition, because e.g. for PDG code 1000010020 (deuteron), GetParticle(...) returns NULL charge = tc.fDatabasePDG->GetParticle(mcParticle.pdgCode())->Charge() / 3.; // yes, divided by 3. Fundamental unit of charge is associated with quarks } - double vector[eDiffPtWeights_N] = {mcParticle.pt(), mcParticle.eta(), charge, ebye.fCentralitySim}; + double vector[eDiffPtWeights_N] = {mcParticle.pt(), mcParticle.eta(), charge, ebye.fCentralitySim, 0.}; ph.fParticleSparseHistograms[eDWPt][eSim][ba]->Fill(vector, weight); } // **) eDWEta : here the fundamental 0-th axis never to be projected out is "eta" @@ -11724,7 +11782,7 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) // Yes, I have to check the 2nd condition, because e.g. for PDG code 1000010020 (deuteron), GetParticle(...) returns NULL charge = tc.fDatabasePDG->GetParticle(mcParticle.pdgCode())->Charge() / 3.; // yes, divided by 3. Fundamental unit of charge is associated with quarks } - double vector[eDiffEtaWeights_N] = {mcParticle.eta(), mcParticle.pt(), charge, ebye.fCentralitySim}; + double vector[eDiffEtaWeights_N] = {mcParticle.eta(), mcParticle.pt(), charge, ebye.fCentralitySim, 0.}; ph.fParticleSparseHistograms[eDWEta][eSim][ba]->Fill(vector, weight); } // **) eDWCharge : here the fundamental 0-th axis never to be projected out is "charge" @@ -11737,7 +11795,7 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) // Yes, I have to check the 2nd condition, because e.g. for PDG code 1000010020 (deuteron), GetParticle(...) returns NULL charge = tc.fDatabasePDG->GetParticle(mcParticle.pdgCode())->Charge() / 3.; // yes, divided by 3. Fundamental unit of charge is associated with quarks } - double vector[eDiffChargeWeights_N] = {charge, mcParticle.pt(), mcParticle.eta(), ebye.fCentralitySim}; + double vector[eDiffChargeWeights_N] = {charge, mcParticle.pt(), mcParticle.eta(), ebye.fCentralitySim, 0.}; ph.fParticleSparseHistograms[eDWCharge][eSim][ba]->Fill(vector, weight); } @@ -11751,7 +11809,7 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) // b) Fill only simulated (common to Run 3, Run 2 and Run 1): // Remark #1: This branch is relevant when processing ONLY simulated data at generator level. // Remark #2: In this branch, 'track' is always TracksSim = aod::McParticles, see https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo . - // Remark #3: This code is completely irrelevant for InternalValidation(), because few selected histos I fill directly there. + // Remark #3: This code is completely irrelevant for internalValidation(), because few selected histos I fill directly there. if constexpr (rs == eSim || rs == eSim_Run2 || rs == eSim_Run1) { // 1D: if (ph.fFillParticleHistograms) { @@ -11866,11 +11924,11 @@ void FillParticleHistograms(T const& track, eBeforeAfter ba, int weight = 1) } } // if constexpr (rs == eTest) { -} // template void FillParticleHistograms(...) +} // template void fillParticleHistograms(...) //============================================================ -void CalculateCorrelations() +void calculateCorrelations() { // Calculate analytically multiparticle correlations from Q-vectors. // In this method, only isotropic correlations for which all harmonics are the same are evaluated. @@ -11880,11 +11938,13 @@ void CalculateCorrelations() // c) Flush the generic Q-vectors. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } + int nSelectedTracksBareMinimum = -1; // TBI 20260602 : I need this below somewhat artificially to silent o2_linter : Avoid magic numbers in expressions + // a) Flush 'n' fill the generic Q-vectors: - ResetQ(); + resetQ(); for (int h = 0; h < gMaxHarmonic * gMaxCorrelator + 1; h++) { for (int wp = 0; wp < gMaxCorrelator + 1; wp++) // weight power { @@ -11896,16 +11956,17 @@ void CalculateCorrelations() for (int h = 1; h <= gMaxHarmonic; h++) // harmonic { // 2p: - if (ebye.fSelectedTracks < 2) { + nSelectedTracksBareMinimum = 2; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } if (tc.fVerbose) { LOGF(info, " calculating 2-particle correlations ...."); } - TComplex two = Two(h, -h); - double twoC = two.Re(); // cos - // double twoS = two.Im(); // sin - double wTwo = Two(0, 0).Re(); // Weight is 'number of combinations' by default TBI + TComplex twoComplex = two(h, -h); + double twoC = twoComplex.Re(); // cos + // double twoS = twoComplex.Im(); // sin + double wTwo = two(0, 0).Re(); // weight is 'number of combinations' by default TBI // 20220809 add support for other weights if (wTwo > 0.0) { twoC /= wTwo; @@ -11918,7 +11979,7 @@ void CalculateCorrelations() TArrayI* harmonics = new TArrayI(2); harmonics->SetAt(h, 0); harmonics->SetAt(-h, 1); - double nestedLoopValue = this->CalculateCustomNestedLoops(harmonics); + double nestedLoopValue = this->calculateCustomNestedLoops(harmonics); if (std::abs(nestedLoopValue) > 0. && std::abs(twoC - nestedLoopValue) > tc.fFloatingPointPrecision) { LOGF(fatal, "\033[1;31m%s at line %d : nestedLoopValue = %f is not the same as twoC = %f\033[0m", __FUNCTION__, __LINE__, nestedLoopValue, twoC); } else { @@ -11939,7 +12000,7 @@ void CalculateCorrelations() } // vs. multiplicity: if (mupa.fCorrelationsPro[0][h - 1][AFO_MULTIPLICITY]) { - mupa.fCorrelationsPro[0][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fSelectedTracks + 0.5, twoC, wTwo); + mupa.fCorrelationsPro[0][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fMultiplicity + 0.5, twoC, wTwo); } // vs. centrality: if (mupa.fCorrelationsPro[0][h - 1][AFO_CENTRALITY]) { @@ -11963,16 +12024,17 @@ void CalculateCorrelations() } // 4p: - if (ebye.fSelectedTracks < 4) { + nSelectedTracksBareMinimum = 4; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { continue; } // yes, continue, because I can still calculate 2-p in other harmonics! if (tc.fVerbose) { LOGF(info, " calculating 4-particle correlations ...."); } - TComplex four = Four(h, h, -h, -h); - double fourC = four.Re(); // cos - // double fourS = four.Im(); // sin - double wFour = Four(0, 0, 0, 0).Re(); // Weight is 'number of combinations' by default TBI_20210515 add support for other weights + TComplex fourComplex = four(h, h, -h, -h); + double fourC = fourComplex.Re(); // cos + // double fourS = fourComplex.Im(); // sin + double wFour = four(0, 0, 0, 0).Re(); // weight is 'number of combinations' by default TBI_20210515 add support for other weights if (wFour > 0.0) { fourC /= wFour; } else { @@ -11987,7 +12049,7 @@ void CalculateCorrelations() harmonics->SetAt(h, 1); harmonics->SetAt(-h, 2); harmonics->SetAt(-h, 3); - double nestedLoopValue = this->CalculateCustomNestedLoops(harmonics); + double nestedLoopValue = this->calculateCustomNestedLoops(harmonics); if (std::abs(nestedLoopValue) > 0. && std::abs(fourC - nestedLoopValue) > tc.fFloatingPointPrecision) { LOGF(fatal, "\033[1;31m%s at line %d : nestedLoopValue = %f is not the same as fourC = %f\033[0m", __FUNCTION__, __LINE__, nestedLoopValue, fourC); } else { @@ -12008,7 +12070,7 @@ void CalculateCorrelations() } // vs. multiplicity: if (mupa.fCorrelationsPro[1][h - 1][AFO_MULTIPLICITY]) { - mupa.fCorrelationsPro[1][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fSelectedTracks + 0.5, fourC, wFour); + mupa.fCorrelationsPro[1][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fMultiplicity + 0.5, fourC, wFour); } // vs. centrality: if (mupa.fCorrelationsPro[1][h - 1][AFO_CENTRALITY]) { @@ -12032,16 +12094,17 @@ void CalculateCorrelations() } // 6p: - if (ebye.fSelectedTracks < 6) { + nSelectedTracksBareMinimum = 6; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { continue; } // yes, continue, because I can still calculate 2-p and 4-p in other harmonics! if (tc.fVerbose) { LOGF(info, " calculating 6-particle correlations ...."); } - TComplex six = Six(h, h, h, -h, -h, -h); - double sixC = six.Re(); // cos - // double sixS = six.Im(); // sin - double wSix = Six(0, 0, 0, 0, 0, 0).Re(); // Weight is 'number of combinations' by default TBI_20210515 add support for other weights + TComplex sixComplex = six(h, h, h, -h, -h, -h); + double sixC = sixComplex.Re(); // cos + // double sixS = sixComplex.Im(); // sin + double wSix = six(0, 0, 0, 0, 0, 0).Re(); // weight is 'number of combinations' by default TBI_20210515 add support for other weights if (wSix > 0.0) { sixC /= wSix; } else { @@ -12058,7 +12121,7 @@ void CalculateCorrelations() harmonics->SetAt(-h, 3); harmonics->SetAt(-h, 4); harmonics->SetAt(-h, 5); - double nestedLoopValue = this->CalculateCustomNestedLoops(harmonics); + double nestedLoopValue = this->calculateCustomNestedLoops(harmonics); if (std::abs(nestedLoopValue) > 0. && std::abs(sixC - nestedLoopValue) > tc.fFloatingPointPrecision) { LOGF(fatal, "\033[1;31m%s at line %d : nestedLoopValue = %f is not the same as sixC = %f\033[0m", __FUNCTION__, __LINE__, nestedLoopValue, sixC); } else { @@ -12079,7 +12142,7 @@ void CalculateCorrelations() } // vs. multiplicity: if (mupa.fCorrelationsPro[2][h - 1][AFO_MULTIPLICITY]) { - mupa.fCorrelationsPro[2][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fSelectedTracks + 0.5, sixC, wSix); + mupa.fCorrelationsPro[2][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fMultiplicity + 0.5, sixC, wSix); } // vs. centrality: if (mupa.fCorrelationsPro[2][h - 1][AFO_CENTRALITY]) { @@ -12103,16 +12166,17 @@ void CalculateCorrelations() } // 8p: - if (ebye.fSelectedTracks < 8) { + nSelectedTracksBareMinimum = 8; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { continue; } // yes, continue, because I can still calculate 2-p, 4-p and 6-p in other harmonics! if (tc.fVerbose) { LOGF(info, " calculating 8-particle correlations ...."); } - TComplex eight = Eight(h, h, h, h, -h, -h, -h, -h); - double eightC = eight.Re(); // cos - // double eightS = eight.Im(); // sin - double wEight = Eight(0, 0, 0, 0, 0, 0, 0, 0).Re(); // Weight is 'number of combinations' by default TBI_20210515 add support for other weights + TComplex eightComplex = eight(h, h, h, h, -h, -h, -h, -h); + double eightC = eightComplex.Re(); // cos + // double eightS = eightComplex.Im(); // sin + double wEight = eight(0, 0, 0, 0, 0, 0, 0, 0).Re(); // weight is 'number of combinations' by default TBI_20210515 add support for other weights if (wEight > 0.0) { eightC /= wEight; } else { @@ -12131,7 +12195,7 @@ void CalculateCorrelations() harmonics->SetAt(-h, 5); harmonics->SetAt(-h, 6); harmonics->SetAt(-h, 7); - double nestedLoopValue = this->CalculateCustomNestedLoops(harmonics); + double nestedLoopValue = this->calculateCustomNestedLoops(harmonics); if (std::abs(nestedLoopValue) > 0. && std::abs(eightC - nestedLoopValue) > tc.fFloatingPointPrecision) { LOGF(fatal, "\033[1;31m%s at line %d : nestedLoopValue = %f is not the same as eightC = %f\033[0m", __FUNCTION__, __LINE__, nestedLoopValue, eightC); } else { @@ -12152,7 +12216,7 @@ void CalculateCorrelations() } // vs. multiplicity: if (mupa.fCorrelationsPro[3][h - 1][AFO_MULTIPLICITY]) { - mupa.fCorrelationsPro[3][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fSelectedTracks + 0.5, eightC, wEight); + mupa.fCorrelationsPro[3][h - 1][AFO_MULTIPLICITY]->Fill(ebye.fMultiplicity + 0.5, eightC, wEight); } // vs. centrality: if (mupa.fCorrelationsPro[3][h - 1][AFO_CENTRALITY]) { @@ -12177,29 +12241,29 @@ void CalculateCorrelations() } // for(int h=1;h<=gMaxHarmonic;h++) // harmonic // c) Flush the generic Q-vectors: - ResetQ(); + resetQ(); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void CalculateCorrelations() +} // void calculateCorrelations() //============================================================ -void CalculateKineCorrelations(eAsFunctionOf AFO_variable) +void calculateKineCorrelations(EnAsFunctionOf AFO_variable) { // Calculate analytically differential multiparticle correlations from Q-vectors. - // TBI 20250702 I need declare this function obsolete, and move to CalculateKineCorrelationsNdim, see what I did in CalculateKineTest0Ndim + // TBI 20250702 I need declare this function obsolete, and move to CalculateKineCorrelationsNdim, see what I did in calculateKineTest0Ndim // After that, finalize this function and add the standard support for kine calculus. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) ... - eqvectorKine qvKine = eqvectorKine_N; // which eqvectorKine enum + EnqvectorKine qvKine = eqvectorKine_N; // which EnqvectorKine enum // int nBins = -1; // TBI 20241111 temporarily commented out just to suppress warnings switch (AFO_variable) { @@ -12236,14 +12300,14 @@ void CalculateKineCorrelations(eAsFunctionOf AFO_variable) // ... if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void CalculateKineCorrelations(eAsFunctionOf AFO_variable) +} // void calculateKineCorrelations(EnAsFunctionOf AFO_variable) //============================================================ -void CalculateTest0() +void calculateTest0() { // Calculate Test0. @@ -12252,11 +12316,11 @@ void CalculateTest0() // c) Flush the generic Q-vectors. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Flush 'n' fill the generic Q-vectors: - ResetQ(); + resetQ(); for (int h = 0; h < gMaxHarmonic * gMaxCorrelator + 1; h++) { for (int wp = 0; wp < gMaxCorrelator + 1; wp++) // weight power { @@ -12295,102 +12359,115 @@ void CalculateTest0() delete oa; // yes, otherwise it's a memory leak } - switch (mo + 1) // which order? yes, mo+1 + int nSelectedTracksBareMinimum = -1; // TBI 20260602 : I need this below somewhat artificially to silent o2_linter : Avoid magic numbers in expressions + switch (mo + 1) // which order? yes, mo+1 { case 1: - if (ebye.fSelectedTracks < 1) { + nSelectedTracksBareMinimum = 1; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = One(n[0]).Re(); - weight = One(0).Re(); + correlation = one(n[0]).Re(); + weight = one(0).Re(); break; case 2: - if (ebye.fSelectedTracks < 2) { + nSelectedTracksBareMinimum = 2; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Two(n[0], n[1]).Re(); - weight = Two(0, 0).Re(); + correlation = two(n[0], n[1]).Re(); + weight = two(0, 0).Re(); break; case 3: - if (ebye.fSelectedTracks < 3) { + nSelectedTracksBareMinimum = 3; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Three(n[0], n[1], n[2]).Re(); - weight = Three(0, 0, 0).Re(); + correlation = three(n[0], n[1], n[2]).Re(); + weight = three(0, 0, 0).Re(); break; case 4: - if (ebye.fSelectedTracks < 4) { + nSelectedTracksBareMinimum = 4; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Four(n[0], n[1], n[2], n[3]).Re(); - weight = Four(0, 0, 0, 0).Re(); + correlation = four(n[0], n[1], n[2], n[3]).Re(); + weight = four(0, 0, 0, 0).Re(); break; case 5: - if (ebye.fSelectedTracks < 5) { + nSelectedTracksBareMinimum = 5; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Five(n[0], n[1], n[2], n[3], n[4]).Re(); - weight = Five(0, 0, 0, 0, 0).Re(); + correlation = five(n[0], n[1], n[2], n[3], n[4]).Re(); + weight = five(0, 0, 0, 0, 0).Re(); break; case 6: - if (ebye.fSelectedTracks < 6) { + nSelectedTracksBareMinimum = 6; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Six(n[0], n[1], n[2], n[3], n[4], n[5]).Re(); - weight = Six(0, 0, 0, 0, 0, 0).Re(); + correlation = six(n[0], n[1], n[2], n[3], n[4], n[5]).Re(); + weight = six(0, 0, 0, 0, 0, 0).Re(); break; case 7: - if (ebye.fSelectedTracks < 7) { + nSelectedTracksBareMinimum = 7; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Seven(n[0], n[1], n[2], n[3], n[4], n[5], n[6]).Re(); - weight = Seven(0, 0, 0, 0, 0, 0, 0).Re(); + correlation = seven(n[0], n[1], n[2], n[3], n[4], n[5], n[6]).Re(); + weight = seven(0, 0, 0, 0, 0, 0, 0).Re(); break; case 8: - if (ebye.fSelectedTracks < 8) { + nSelectedTracksBareMinimum = 8; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Eight(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]).Re(); - weight = Eight(0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = eight(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]).Re(); + weight = eight(0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 9: - if (ebye.fSelectedTracks < 9) { + nSelectedTracksBareMinimum = 9; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Nine(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8]).Re(); - weight = Nine(0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = nine(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8]).Re(); + weight = nine(0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 10: - if (ebye.fSelectedTracks < 10) { + nSelectedTracksBareMinimum = 10; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Ten(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9]).Re(); - weight = Ten(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = ten(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9]).Re(); + weight = ten(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 11: - if (ebye.fSelectedTracks < 11) { + nSelectedTracksBareMinimum = 11; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Eleven(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10]).Re(); - weight = Eleven(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = eleven(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10]).Re(); + weight = eleven(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 12: - if (ebye.fSelectedTracks < 12) { + nSelectedTracksBareMinimum = 12; + if (ebye.fSelectedTracks < nSelectedTracksBareMinimum) { return; } - correlation = Twelve(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10], n[11]).Re(); - weight = Twelve(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = twelve(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10], n[11]).Re(); + weight = twelve(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; default: @@ -12408,7 +12485,7 @@ void CalculateTest0() for (int i = 0; i < mo + 1; i++) { harmonics->SetAt(n[i], i); } - double nestedLoopValue = this->CalculateCustomNestedLoops(harmonics); + double nestedLoopValue = this->calculateCustomNestedLoops(harmonics); if (!(std::abs(nestedLoopValue) > 0.)) { LOGF(info, " ebye check (integrated) with CustomNestedLoops was NOT calculated for %d-p Test0 corr. %s", mo + 1, t0.fTest0Labels[mo][mi]->Data()); } else if (std::abs(nestedLoopValue) > 0. && std::abs(correlation / weight - nestedLoopValue) > tc.fFloatingPointPrecision) { @@ -12426,9 +12503,9 @@ void CalculateTest0() for (int i = 0; i < mo + 1; i++) { harmonics->SetAt(n[i], i); } - TComplex theoreticalValue = this->TheoreticalValue(harmonics, iv.fInternalValidationVnPsin[eVn], iv.fInternalValidationVnPsin[ePsin]); - if (std::abs(theoreticalValue.Re()) > 0.) { - correlation /= theoreticalValue.Re(); + TComplex tv = this->theoreticalValue(harmonics, iv.fInternalValidationVnPsin[eVn], iv.fInternalValidationVnPsin[ePsin]); + if (std::abs(tv.Re()) > 0.) { + correlation /= tv.Re(); } // TBI 20240424 for the time being, I do not do anything with imaginary part, but I could eventually... delete harmonics; @@ -12486,30 +12563,30 @@ void CalculateTest0() } // for(int mo=0;mo 0.)) { LOGF(fatal, "\033[1;31m%s at line %d : is perhaps order of some requested correlator bigger than the number of particles? Correlator = %s \033[0m", __FUNCTION__, __LINE__, t0.fTest0Labels[mo][mi]->Data()); } - double nestedLoopValue = this->CalculateKineCustomNestedLoops(harmonics, AFO_variable, b); + double nestedLoopValue = this->calculateKineCustomNestedLoops(harmonics, AFO_variable, b); if (!(std::abs(nestedLoopValue) > 0.)) { - LOGF(info, " e-b-e check with CalculateKineCustomNestedLoops was NOT calculated for %d-p Test0 corr. %s, bin = %d", mo + 1, t0.fTest0Labels[mo][mi]->Data(), b + 1); + LOGF(info, " e-b-e check with calculateKineCustomNestedLoops was NOT calculated for %d-p Test0 corr. %s, bin = %d", mo + 1, t0.fTest0Labels[mo][mi]->Data(), b + 1); } else if (std::abs(nestedLoopValue) > 0. && std::abs(correlation / weight - nestedLoopValue) > tc.fFloatingPointPrecision) { LOGF(fatal, "\033[1;31m%s at line %d : correlator: %s \n correlation: %f \n custom loop: %f \033[0m", __FUNCTION__, __LINE__, t0.fTest0Labels[mo][mi]->Data(), correlation / weight, nestedLoopValue); } else { - LOGF(info, "\033[1;32m ebye check (differential) with CalculateKineCustomNestedLoops is OK for %d-p Test0 corr. %s, bin = %d\033[0m", mo + 1, t0.fTest0Labels[mo][mi]->Data(), b + 1); + LOGF(info, "\033[1;32m ebye check (differential) with calculateKineCustomNestedLoops is OK for %d-p Test0 corr. %s, bin = %d\033[0m", mo + 1, t0.fTest0Labels[mo][mi]->Data(), b + 1); } delete harmonics; harmonics = NULL; @@ -12674,9 +12751,9 @@ void CalculateKineTest0(eAsFunctionOf AFO_variable) for (int i = 0; i < mo + 1; i++) { harmonics->SetAt(n[i], i); } - TComplex theoreticalValue = TheoreticalValue(harmonics, iv.fInternalValidationVnPsin[eVn], iv.fInternalValidationVnPsin[ePsin]); - if (std::abs(theoreticalValue.Re()) > 0.) { - correlation /= theoreticalValue.Re(); + TComplex tv = theoreticalValue(harmonics, iv.fInternalValidationVnPsin[eVn], iv.fInternalValidationVnPsin[ePsin]); + if (std::abs(tv.Re()) > 0.) { + correlation /= tv.Re(); } // TBI 20240424 for the time being, I do not do anything with imaginary part, but I could eventually... delete harmonics; @@ -12690,7 +12767,7 @@ void CalculateKineTest0(eAsFunctionOf AFO_variable) LOGF(info, "\n\033[1;33m b = %d \033[0m\n", b); LOGF(info, "\n\033[1;33m qvKine = %d \033[0m\n", static_cast(qvKine)); LOGF(info, "\n\033[1;33m event weight = %e \033[0m\n", weight); - LOGF(info, "\n\033[1;33m sum of particle weights = %e \033[0m\n", One(0).Re()); + LOGF(info, "\n\033[1;33m sum of particle weights = %e \033[0m\n", one(0).Re()); LOGF(info, "\n\033[1;33m correlation = %f \033[0m\n", correlation); LOGF(info, "\n\033[1;33m t0.fTest0Pro[mo][mi][AFO_variable]->GetTitle() = %s \033[0m\n", t0.fTest0Pro[mo][mi][AFO_variable]->GetTitle()); LOGF(info, "\n\033[1;33m [mo][mi][AFO_variable] = [%d][%d][%d] \033[0m\n", mo, mi, static_cast(AFO_variable)); @@ -12735,23 +12812,23 @@ void CalculateKineTest0(eAsFunctionOf AFO_variable) } // for(int b=0;b(kineVarChoice), StringKineMap(kineVarChoice).Data(), Ndim); + startFunction(__FUNCTION__); + LOGF(info, "\033[1;31m%s kineVarChoice = %d (%s), Ndim = %d \033[0m", __FUNCTION__, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), Ndim); } int nBins = -1; @@ -12759,11 +12836,11 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) switch (Ndim) { case 1: { - eAsFunctionOf AFO_var = AfoKineMap1D(kineVarChoice); - if (res.fResultsPro[AFO_var]) { - nBins = res.fResultsPro[AFO_var]->GetNbinsX() + 2; // + 2 means that I take into account overflow and underflow, then skip it in the loop below. + EnAsFunctionOf afoVar = afoKineMap1D(kineVarChoice); + if (res.fResultsPro[afoVar]) { + nBins = res.fResultsPro[afoVar]->GetNbinsX() + 2; // + 2 means that I take into account overflow and underflow, then skip it in the loop below. if (tc.fVerbose) { - LOGF(info, "\033[1;31m%s nBins = %d, kineVarChoice = %d (%s), Ndim = %d \033[0m", __FUNCTION__, nBins, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), Ndim); + LOGF(info, "\033[1;31m%s nBins = %d, kineVarChoice = %d (%s), Ndim = %d \033[0m", __FUNCTION__, nBins, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), Ndim); } } @@ -12771,11 +12848,11 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) } case 2: { - eAsFunctionOf2D AFO_var = AfoKineMap2D(kineVarChoice); - if (res.fResultsPro2D[AFO_var]) { - nBins = (res.fResultsPro2D[AFO_var]->GetNbinsX() + 2) * (res.fResultsPro2D[AFO_var]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + EnAsFunctionOf2D afoVar = afoKineMap2D(kineVarChoice); + if (res.fResultsPro2D[afoVar]) { + nBins = (res.fResultsPro2D[afoVar]->GetNbinsX() + 2) * (res.fResultsPro2D[afoVar]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below if (tc.fVerbose) { - LOGF(info, "\033[1;31m%s nBins = %d, kineVarChoice = %d (%s), Ndim = %d \033[0m", __FUNCTION__, nBins, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), Ndim); + LOGF(info, "\033[1;31m%s nBins = %d, kineVarChoice = %d (%s), Ndim = %d \033[0m", __FUNCTION__, nBins, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), Ndim); } } @@ -12783,11 +12860,11 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) } case 3: { - eAsFunctionOf3D AFO_var = AfoKineMap3D(kineVarChoice); - if (res.fResultsPro3D[AFO_var]) { - nBins = (res.fResultsPro3D[AFO_var]->GetNbinsX() + 2) * (res.fResultsPro3D[AFO_var]->GetNbinsY() + 2) * (res.fResultsPro3D[AFO_var]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + EnAsFunctionOf3D afoVar = afoKineMap3D(kineVarChoice); + if (res.fResultsPro3D[afoVar]) { + nBins = (res.fResultsPro3D[afoVar]->GetNbinsX() + 2) * (res.fResultsPro3D[afoVar]->GetNbinsY() + 2) * (res.fResultsPro3D[afoVar]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below if (tc.fVerbose) { - LOGF(info, "\033[1;31m%s nBins = %d, kineVarChoice = %d (%s), Ndim = %d \033[0m", __FUNCTION__, nBins, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), Ndim); + LOGF(info, "\033[1;31m%s nBins = %d, kineVarChoice = %d (%s), Ndim = %d \033[0m", __FUNCTION__, nBins, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), Ndim); } } break; @@ -12807,14 +12884,14 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) if (tc.fVerbose) { // TBI 20250701 temporary check, remove eventually LOGF(info, "\033[1;31m%s b = %d \033[0m", __FUNCTION__, b); - Trace(__FUNCTION__, __LINE__); + trace(__FUNCTION__, __LINE__); } // *) Check if this bin is overflow or underflow, or otherwise if it's empty: // Well, I already checked that when filling fqvector, if this global bin is overflow or underflow, qvector and number of entries shall be empty for that bin, so I am checking for that: if (0 == qv.fqvectorEntries[kineVarChoice][b]) { if (tc.fVerbose) { - LOGF(info, "\033[1;31m%s no entries in bin = %d, for kineVarChoice = %d (%s). Just skipping this bin (this is most likely underflow or overflow global bin)\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data()); + LOGF(info, "\033[1;31m%s no entries in bin = %d, for kineVarChoice = %d (%s). Just skipping this bin (this is most likely underflow or overflow global bin)\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data()); } continue; } @@ -12828,7 +12905,7 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) // cross-check integrated results for correlations, by rebinning the final kine results. if (qv.fqvectorEntries[kineVarChoice][b] < ec.fdEventCuts[eMultiplicity][eMin] || qv.fqvectorEntries[kineVarChoice][b] > ec.fdEventCuts[eMultiplicity][eMax] || std::abs(qv.fqvectorEntries[kineVarChoice][b] - ec.fdEventCuts[eMultiplicity][eMax]) < tc.fFloatingPointPrecision) { if (tc.fVerbose) { - LOGF(info, "\033[1;31m%s eMultiplicity cut in kine bin = %b, for kineVarChoice = %d (%s), there are only %d selected particles in this kine bin\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), qv.fqvectorEntries[kineVarChoice][b]); + LOGF(info, "\033[1;31m%s eMultiplicity cut in kine bin = %b, for kineVarChoice = %d (%s), there are only %d selected particles in this kine bin\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), qv.fqvectorEntries[kineVarChoice][b]); } continue; } @@ -12846,7 +12923,7 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) // I leave it like this, unless proven otherwise. if (tc.fVerbose) { // TBI 20250701 temporary check, remove eventually - Trace(__FUNCTION__, __LINE__); + trace(__FUNCTION__, __LINE__); } // *) Okay, let's do transparently the differential calculus, whether it's 1D, 2D, 3D, ...: @@ -12876,63 +12953,63 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) switch (mo + 1) // which order? yes, mo+1 { case 1: - correlation = One(n[0]).Re(); - weight = One(0).Re(); + correlation = one(n[0]).Re(); + weight = one(0).Re(); break; case 2: - correlation = Two(n[0], n[1]).Re(); - weight = Two(0, 0).Re(); + correlation = two(n[0], n[1]).Re(); + weight = two(0, 0).Re(); break; case 3: - correlation = Three(n[0], n[1], n[2]).Re(); - weight = Three(0, 0, 0).Re(); + correlation = three(n[0], n[1], n[2]).Re(); + weight = three(0, 0, 0).Re(); break; case 4: - correlation = Four(n[0], n[1], n[2], n[3]).Re(); - weight = Four(0, 0, 0, 0).Re(); + correlation = four(n[0], n[1], n[2], n[3]).Re(); + weight = four(0, 0, 0, 0).Re(); break; case 5: - correlation = Five(n[0], n[1], n[2], n[3], n[4]).Re(); - weight = Five(0, 0, 0, 0, 0).Re(); + correlation = five(n[0], n[1], n[2], n[3], n[4]).Re(); + weight = five(0, 0, 0, 0, 0).Re(); break; case 6: - correlation = Six(n[0], n[1], n[2], n[3], n[4], n[5]).Re(); - weight = Six(0, 0, 0, 0, 0, 0).Re(); + correlation = six(n[0], n[1], n[2], n[3], n[4], n[5]).Re(); + weight = six(0, 0, 0, 0, 0, 0).Re(); break; case 7: - correlation = Seven(n[0], n[1], n[2], n[3], n[4], n[5], n[6]).Re(); - weight = Seven(0, 0, 0, 0, 0, 0, 0).Re(); + correlation = seven(n[0], n[1], n[2], n[3], n[4], n[5], n[6]).Re(); + weight = seven(0, 0, 0, 0, 0, 0, 0).Re(); break; case 8: - correlation = Eight(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]).Re(); - weight = Eight(0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = eight(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]).Re(); + weight = eight(0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 9: - correlation = Nine(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8]).Re(); - weight = Nine(0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = nine(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8]).Re(); + weight = nine(0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 10: - correlation = Ten(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9]).Re(); - weight = Ten(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = ten(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9]).Re(); + weight = ten(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 11: - correlation = Eleven(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10]).Re(); - weight = Eleven(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = eleven(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10]).Re(); + weight = eleven(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; case 12: - correlation = Twelve(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10], n[11]).Re(); - weight = Twelve(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); + correlation = twelve(n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10], n[11]).Re(); + weight = twelve(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0).Re(); break; default: @@ -12948,21 +13025,21 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) if (!(weight > 0.)) { LOGF(fatal, "\033[1;31m%s at line %d : is perhaps order of some requested correlator bigger than the number of particles? Correlator = %s \033[0m", __FUNCTION__, __LINE__, t0.fTest0Labels[mo][mi]->Data()); } - double nestedLoopValue = this->CalculateKineCustomNestedLoops(harmonics, kineVarChoice, b); - PrintBinEdgesKine(kineVarChoice, b); + double nestedLoopValue = this->calculateKineCustomNestedLoops(harmonics, kineVarChoice, b); + printBinEdgesKine(kineVarChoice, b); if (!(std::abs(nestedLoopValue) > 0.)) { - LOGF(info, " e-b-e check with CalculateKineCustomNestedLoops was NOT calculated for %d-p Test0 corr. %s, kineVarChoice (eqvectorKine) = %d (%s), bin = %d", mo + 1, t0.fTest0Labels[mo][mi]->Data(), static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), b); + LOGF(info, " e-b-e check with calculateKineCustomNestedLoops was NOT calculated for %d-p Test0 corr. %s, kineVarChoice (EnqvectorKine) = %d (%s), bin = %d", mo + 1, t0.fTest0Labels[mo][mi]->Data(), static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), b); } else if (std::abs(nestedLoopValue) > 0. && std::abs(correlation / weight - nestedLoopValue) > tc.fFloatingPointPrecision) { LOGF(fatal, "\033[1;31m%s at line %d : correlator: %s \n correlation: %f \n custom loop: %f \033[0m", __FUNCTION__, __LINE__, t0.fTest0Labels[mo][mi]->Data(), correlation / weight, nestedLoopValue); } else { - LOGF(info, "\033[1;32m ebye check (differential) with CalculateKineCustomNestedLoops is OK for %d-p Test0 corr. %s, kineVarChoice (eqvectorKine) = %d (%s), bin = %d, nParticles in this bin = %d\033[0m", mo + 1, t0.fTest0Labels[mo][mi]->Data(), static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), b, qv.fqvectorEntries[kineVarChoice][b]); + LOGF(info, "\033[1;32m ebye check (differential) with calculateKineCustomNestedLoops is OK for %d-p Test0 corr. %s, kineVarChoice (EnqvectorKine) = %d (%s), bin = %d, nParticles in this bin = %d\033[0m", mo + 1, t0.fTest0Labels[mo][mi]->Data(), static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), b, qv.fqvectorEntries[kineVarChoice][b]); } delete harmonics; harmonics = NULL; } // if(nl.fCalculateKineCustomNestedLoops) if (tc.fVerbose) { // TBI 20250701 temporary check, remove eventually - Trace(__FUNCTION__, __LINE__); + trace(__FUNCTION__, __LINE__); } // To ease comparison, rescale with theoretical value. Now all Test0 results shall be at 1: @@ -12971,9 +13048,9 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) for (int i = 0; i < mo + 1; i++) { harmonics->SetAt(n[i], i); } - TComplex theoreticalValue = TheoreticalValue(harmonics, iv.fInternalValidationVnPsin[eVn], iv.fInternalValidationVnPsin[ePsin]); - if (std::abs(theoreticalValue.Re()) > 0.) { - correlation /= theoreticalValue.Re(); + TComplex tv = theoreticalValue(harmonics, iv.fInternalValidationVnPsin[eVn], iv.fInternalValidationVnPsin[ePsin]); + if (std::abs(tv.Re()) > 0.) { + correlation /= tv.Re(); } // TBI 20240424 for the time being, I do not do anything with imaginary part, but I could eventually... delete harmonics; @@ -12981,7 +13058,7 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) } // if(fUseInternalValidation && fRescaleWithTheoreticalInput) if (tc.fVerbose) { // TBI 20250701 temporary check, remove eventually - Trace(__FUNCTION__, __LINE__); + trace(__FUNCTION__, __LINE__); } // Insanity check for the event weight: @@ -12991,29 +13068,29 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) LOGF(info, "\n\033[1;33m b = %d \033[0m\n", b); LOGF(info, "\n\033[1;33m kineVarChoice = %d \033[0m\n", static_cast(kineVarChoice)); LOGF(info, "\n\033[1;33m event weight = %e \033[0m\n", weight); - LOGF(info, "\n\033[1;33m sum of particle weights = %e \033[0m\n", One(0).Re()); + LOGF(info, "\n\033[1;33m sum of particle weights = %e \033[0m\n", one(0).Re()); LOGF(info, "\n\033[1;33m correlation = %f \033[0m\n", correlation); switch (Ndim) { case 1: { - eAsFunctionOf AFO_var = AfoKineMap1D(kineVarChoice); - LOGF(info, "\n\033[1;33m t0.fTest0Pro[mo][mi][AFO_variable]->GetTitle() = %s \033[0m\n", t0.fTest0Pro[mo][mi][AFO_var]->GetTitle()); - LOGF(info, "\n\033[1;33m [mo][mi][AFO_variable] = [%d][%d][%d] \033[0m\n", mo, mi, static_cast(AFO_var)); + EnAsFunctionOf afoVar = afoKineMap1D(kineVarChoice); + LOGF(info, "\n\033[1;33m t0.fTest0Pro[mo][mi][AFO_variable]->GetTitle() = %s \033[0m\n", t0.fTest0Pro[mo][mi][afoVar]->GetTitle()); + LOGF(info, "\n\033[1;33m [mo][mi][AFO_variable] = [%d][%d][%d] \033[0m\n", mo, mi, static_cast(afoVar)); break; } case 2: { - eAsFunctionOf2D AFO_var = AfoKineMap2D(kineVarChoice); - LOGF(info, "\n\033[1;33m t0.fTest0Pro2D[mo][mi][AFO_variable]->GetTitle() = %s \033[0m\n", t0.fTest0Pro2D[mo][mi][AFO_var]->GetTitle()); - LOGF(info, "\n\033[1;33m [mo][mi][AFO_variable] = [%d][%d][%d] \033[0m\n", mo, mi, static_cast(AFO_var)); + EnAsFunctionOf2D afoVar = afoKineMap2D(kineVarChoice); + LOGF(info, "\n\033[1;33m t0.fTest0Pro2D[mo][mi][AFO_variable]->GetTitle() = %s \033[0m\n", t0.fTest0Pro2D[mo][mi][afoVar]->GetTitle()); + LOGF(info, "\n\033[1;33m [mo][mi][AFO_variable] = [%d][%d][%d] \033[0m\n", mo, mi, static_cast(afoVar)); break; } case 3: { - eAsFunctionOf3D AFO_var = AfoKineMap3D(kineVarChoice); - LOGF(info, "\n\033[1;33m t0.fTest0Pro3D[mo][mi][AFO_variable]->GetTitle() = %s \033[0m\n", t0.fTest0Pro3D[mo][mi][AFO_var]->GetTitle()); - LOGF(info, "\n\033[1;33m [mo][mi][AFO_variable] = [%d][%d][%d] \033[0m\n", mo, mi, static_cast(AFO_var)); + EnAsFunctionOf3D afoVar = afoKineMap3D(kineVarChoice); + LOGF(info, "\n\033[1;33m t0.fTest0Pro3D[mo][mi][AFO_variable]->GetTitle() = %s \033[0m\n", t0.fTest0Pro3D[mo][mi][afoVar]->GetTitle()); + LOGF(info, "\n\033[1;33m [mo][mi][AFO_variable] = [%d][%d][%d] \033[0m\n", mo, mi, static_cast(afoVar)); break; } @@ -13220,33 +13297,33 @@ void CalculateKineTest0Ndim(eqvectorKine kineVarChoice, int Ndim) } // for(int b=0;b kineVarChoice (eqvectorKine) = %d, global (ordinary) bin %d <=> (%f, %f)", static_cast(kineVarChoice), bin, res.fResultsPro[AfoKineMap1D(kineVarChoice)]->GetBinLowEdge(bin), res.fResultsPro[AfoKineMap1D(kineVarChoice)]->GetBinLowEdge(bin + 1)); + LOGF(info, " => kineVarChoice (EnqvectorKine) = %d, global (ordinary) bin %d <=> (%f, %f)", static_cast(kineVarChoice), bin, res.fResultsPro[afoKineMap1D(kineVarChoice)]->GetBinLowEdge(bin), res.fResultsPro[afoKineMap1D(kineVarChoice)]->GetBinLowEdge(bin + 1)); break; } @@ -13260,9 +13337,9 @@ void PrintBinEdgesKine(eqvectorKine kineVarChoice, int bin) int binX = -1; int binY = -1; int binZ = -1; // dummy for 2D case - res.fResultsPro2D[AfoKineMap2D(kineVarChoice)]->GetBinXYZ(bin, binX, binY, binZ); + res.fResultsPro2D[afoKineMap2D(kineVarChoice)]->GetBinXYZ(bin, binX, binY, binZ); - LOGF(info, " => kineVarChoice (eqvectorKine) = %d, global bin %d = (%d, %d) <=> (%f, %f) x (%f, %f)", static_cast(kineVarChoice), bin, binX, binY, res.fResultsPro2D[AfoKineMap2D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX), res.fResultsPro2D[AfoKineMap2D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX + 1), res.fResultsPro2D[AfoKineMap2D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY), res.fResultsPro2D[AfoKineMap2D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY + 1)); + LOGF(info, " => kineVarChoice (EnqvectorKine) = %d, global bin %d = (%d, %d) <=> (%f, %f) x (%f, %f)", static_cast(kineVarChoice), bin, binX, binY, res.fResultsPro2D[afoKineMap2D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX), res.fResultsPro2D[afoKineMap2D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX + 1), res.fResultsPro2D[afoKineMap2D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY), res.fResultsPro2D[afoKineMap2D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY + 1)); break; } @@ -13274,9 +13351,9 @@ void PrintBinEdgesKine(eqvectorKine kineVarChoice, int bin) int binX = -1; int binY = -1; int binZ = -1; - res.fResultsPro3D[AfoKineMap3D(kineVarChoice)]->GetBinXYZ(bin, binX, binY, binZ); + res.fResultsPro3D[afoKineMap3D(kineVarChoice)]->GetBinXYZ(bin, binX, binY, binZ); - LOGF(info, " => kineVarChoice (eqvectorKine) = %d, global bin %d = (%d, %d, %d) <=> (%f, %f) x (%f, %f) x (%f, %f)", static_cast(kineVarChoice), bin, binX, binY, binZ, res.fResultsPro3D[AfoKineMap3D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX), res.fResultsPro3D[AfoKineMap3D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX + 1), res.fResultsPro3D[AfoKineMap3D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY), res.fResultsPro3D[AfoKineMap3D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY + 1), res.fResultsPro3D[AfoKineMap3D(kineVarChoice)]->GetZaxis()->GetBinLowEdge(binZ), res.fResultsPro3D[AfoKineMap3D(kineVarChoice)]->GetZaxis()->GetBinLowEdge(binZ + 1)); + LOGF(info, " => kineVarChoice (EnqvectorKine) = %d, global bin %d = (%d, %d, %d) <=> (%f, %f) x (%f, %f) x (%f, %f)", static_cast(kineVarChoice), bin, binX, binY, binZ, res.fResultsPro3D[afoKineMap3D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX), res.fResultsPro3D[afoKineMap3D(kineVarChoice)]->GetXaxis()->GetBinLowEdge(binX + 1), res.fResultsPro3D[afoKineMap3D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY), res.fResultsPro3D[afoKineMap3D(kineVarChoice)]->GetYaxis()->GetBinLowEdge(binY + 1), res.fResultsPro3D[afoKineMap3D(kineVarChoice)]->GetZaxis()->GetBinLowEdge(binZ), res.fResultsPro3D[afoKineMap3D(kineVarChoice)]->GetZaxis()->GetBinLowEdge(binZ + 1)); break; } @@ -13289,21 +13366,21 @@ void PrintBinEdgesKine(eqvectorKine kineVarChoice, int bin) } // switch(AFO_variable) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void PrintBinEdgesKine() +} // void printBinEdgesKine() //============================================================ -void CalculateEtaSeparations() +void calculateEtaSeparations() { // Calculate correlations with pseudorapidity separations. // Remark: this is a port and generalization of void AliFlowAnalysisWithMultiparticleCorrelations::CalculateEtaGaps(AliFlowEventSimple *anEvent) if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Calculate 2-p correlations with eta separations from Qa (-eta, index [0]) and Qb (+eta, index [1]) vectors: @@ -13369,26 +13446,22 @@ void CalculateEtaSeparations() } // for (int h = 0; h < gMaxHarmonic; h++) { if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void CalculateEtaSeparations() +} // void calculateEtaSeparations() //============================================================ -void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) +void calculateKineEtaSeparationsNdim(EnqvectorKine kineVarChoice, int Ndim) { // Calculate analytically N-dimensional kine eta separations from differential q-vectors. - if (tc.fVerbose) { - StartFunction(__FUNCTION__); - } - // This is a replacement for the legacy function CalculateKineEtaSeparations(...), which is as of 20250620 deemed obsolete. - // Remember that here I changed design, and pass enum eqvectorKine as an argument, not any longer enum eAsFunctionOf. + // Remember that here I changed design, and pass enum EnqvectorKine as an argument, not any longer enum EnAsFunctionOf. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } int nBins = -1; @@ -13396,27 +13469,27 @@ void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) switch (Ndim) { case 1: { - eAsFunctionOf AFO_var = AfoKineMap1D(kineVarChoice); - if (res.fResultsPro[AFO_var]) { - nBins = res.fResultsPro[AFO_var]->GetNbinsX() + 2; // + 2 means that I take into account overflow and underflow, then skip it in the loop below. + EnAsFunctionOf afoVar = afoKineMap1D(kineVarChoice); + if (res.fResultsPro[afoVar]) { + nBins = res.fResultsPro[afoVar]->GetNbinsX() + 2; // + 2 means that I take into account overflow and underflow, then skip it in the loop below. } break; } case 2: { - eAsFunctionOf2D AFO_var = AfoKineMap2D(kineVarChoice); - if (res.fResultsPro2D[AFO_var]) { - nBins = (res.fResultsPro2D[AFO_var]->GetNbinsX() + 2) * (res.fResultsPro2D[AFO_var]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + EnAsFunctionOf2D afoVar = afoKineMap2D(kineVarChoice); + if (res.fResultsPro2D[afoVar]) { + nBins = (res.fResultsPro2D[afoVar]->GetNbinsX() + 2) * (res.fResultsPro2D[afoVar]->GetNbinsY() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below } break; } case 3: { - eAsFunctionOf3D AFO_var = AfoKineMap3D(kineVarChoice); - if (res.fResultsPro3D[AFO_var]) { - nBins = (res.fResultsPro3D[AFO_var]->GetNbinsX() + 2) * (res.fResultsPro3D[AFO_var]->GetNbinsY() + 2) * (res.fResultsPro3D[AFO_var]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below + EnAsFunctionOf3D afoVar = afoKineMap3D(kineVarChoice); + if (res.fResultsPro3D[afoVar]) { + nBins = (res.fResultsPro3D[afoVar]->GetNbinsX() + 2) * (res.fResultsPro3D[afoVar]->GetNbinsY() + 2) * (res.fResultsPro3D[afoVar]->GetNbinsZ() + 2); // + 2 means that I take into account overflow and underflow, then skip it in the loop below } break; } @@ -13446,13 +13519,13 @@ void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) // *) Check if this bin is overflow or underflow, or otherwise if it's empty: if (!(qv.fmab[0][kineVarChoice][b][e] > 0. && qv.fmab[1][kineVarChoice][b][e] > 0.)) { if (tc.fVerbose) { - LOGF(info, "\033[1;31m%s no entries in kine bin = %b, for kineVarChoice = %d (%s), and for eta separation index = %d. Just skipping this kine bin (this is most likely underflow or overflow global kine bin)\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), e); + LOGF(info, "\033[1;31m%s no entries in kine bin = %b, for kineVarChoice = %d (%s), and for eta separation index = %d. Just skipping this kine bin (this is most likely underflow or overflow global kine bin)\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), e); } continue; } // *) Ensures that in each kine bin, with eta separations, I have the same cut on number of particles, like in integrated analysis: - // 1. see corrresponding remarks in CalculateKineTest0Ndim( ... ); + // 1. see corrresponding remarks in calculateKineTest0Ndim( ... ); // 2. note that here I apply the cut on the sum A + B (it's safe, because corner cases when mult. is 0 in A or in B, is already removed with previous cut here) if ((qv.fmab[0][kineVarChoice][b][e] + qv.fmab[1][kineVarChoice][b][e]) < ec.fdEventCuts[eMultiplicity][eMin] || @@ -13460,7 +13533,7 @@ void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) std::abs(qv.fmab[0][kineVarChoice][b][e] + qv.fmab[1][kineVarChoice][b][e] - ec.fdEventCuts[eMultiplicity][eMax]) < tc.fFloatingPointPrecision) { if (tc.fVerbose) { - LOGF(info, "\033[1;31m%s eMultiplicity cut in kine bin = %b, for kineVarChoice = %d (%s), and for eta separation index = %d. There are only %d selected particles in both eta separated intervals in this kine bin\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), e, qv.fqvectorEntries[kineVarChoice][b]); + LOGF(info, "\033[1;31m%s eMultiplicity cut in kine bin = %b, for kineVarChoice = %d (%s), and for eta separation index = %d. There are only %d selected particles in both eta separated intervals in this kine bin\033[0m", __FUNCTION__, b, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), e, qv.fqvectorEntries[kineVarChoice][b]); } continue; } @@ -13482,8 +13555,8 @@ void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) } // finally, fill 1D case: - if (es.fEtaSeparationsPro[h][e][AfoKineMap1D(kineVarChoice)]) { - es.fEtaSeparationsPro[h][e][AfoKineMap1D(kineVarChoice)]->Fill(es.fEtaSeparationsPro[h][e][AfoKineMap1D(kineVarChoice)]->GetXaxis()->GetBinCenter(b), correlation / weight, weight); + if (es.fEtaSeparationsPro[h][e][afoKineMap1D(kineVarChoice)]) { + es.fEtaSeparationsPro[h][e][afoKineMap1D(kineVarChoice)]->Fill(es.fEtaSeparationsPro[h][e][afoKineMap1D(kineVarChoice)]->GetXaxis()->GetBinCenter(b), correlation / weight, weight); } // TBI 20250620 I need to add support eventually also for 2D and 3D cases. @@ -13495,10 +13568,10 @@ void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) if (tc.fDoAdditionalInsanityChecks) { for (int h = 0; h < gMaxHarmonic; h++) { for (int e = 0; e < gMaxNumberEtaSeparations; e++) { - if (es.fEtaSeparationsPro[h][e][AfoKineMap1D(kineVarChoice)] && std::abs(es.fEtaSeparationsPro[h][e][AfoKineMap1D(kineVarChoice)]->GetBinContent(0)) > 0.) { + if (es.fEtaSeparationsPro[h][e][afoKineMap1D(kineVarChoice)] && std::abs(es.fEtaSeparationsPro[h][e][afoKineMap1D(kineVarChoice)]->GetBinContent(0)) > 0.) { LOGF(fatal, "\033[1;31m%s at line %d : underflow is not empty \033[0m", __FUNCTION__, __LINE__); } - if (es.fEtaSeparationsPro[h][e][AfoKineMap1D(kineVarChoice)] && std::abs(es.fEtaSeparationsPro[h][e][AfoKineMap1D(kineVarChoice)]->GetBinContent(nBins - 1)) > 0.) { + if (es.fEtaSeparationsPro[h][e][afoKineMap1D(kineVarChoice)] && std::abs(es.fEtaSeparationsPro[h][e][afoKineMap1D(kineVarChoice)]->GetBinContent(nBins - 1)) > 0.) { LOGF(fatal, "\033[1;31m%s at line %d : overflow is not empty \033[0m", __FUNCTION__, __LINE__); } } @@ -13506,19 +13579,19 @@ void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) } // if (tc.fDoAdditionalInsanityChecks) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void CalculateKineEtaSeparationsNdim(eqvectorKine kineVarChoice, int Ndim) +} // void calculateKineEtaSeparationsNdim(EnqvectorKine kineVarChoice, int Ndim) //============================================================ -void FillNestedLoopsContainers(const int& particleIndex) +void fillNestedLoopsContainers(const int& particleIndex) { // Fill into the nested loop containers the current particle. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if (tc.fInsanityCheckForEachParticle) { @@ -13539,7 +13612,7 @@ void FillNestedLoopsContainers(const int& particleIndex) // *) Fill container for weights: if (nl.ftaNestedLoops[1]) { - // TBI 20240501 there is a bit of efficiency loss here, because I access Weight() again here. + // TBI 20240501 there is a bit of efficiency loss here, because I access particleWeight() again here. // But it doesn't matter really, in any case I evaluate nested loops only for small M during debugging. // Otherwise, just promote weights to data members, and initialize them only once for a given particle. double wPhi = 1.; @@ -13548,45 +13621,45 @@ void FillNestedLoopsContainers(const int& particleIndex) double wCharge = 1.; if (pw.fUseDiffPhiWeights[wPhiPhiAxis]) { // yes, 0th axis serves as a common boolean for this category - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } if (pw.fUseDiffPtWeights[wPtPtAxis]) { // yes, 0th axis serves as a common boolean for this category - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } if (pw.fUseDiffEtaWeights[wEtaEtaAxis]) { // yes, 0th axis serves as a common boolean for this category - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } if (pw.fUseDiffChargeWeights[wChargeChargeAxis]) { // yes, 0th axis serves as a common boolean for this category - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } if (pw.fUseWeights[wPHI]) { // TBI 20260216 obsolete, remove eventually - wPhi = Weight(pbyp.fPhi, wPHI); + wPhi = particleWeight(pbyp.fPhi, wPHI); } if (pw.fUseWeights[wPT]) { // TBI 20260216 obsolete, remove eventually - wPt = Weight(pbyp.fPt, wPT); + wPt = particleWeight(pbyp.fPt, wPT); } if (pw.fUseWeights[wETA]) { // TBI 20260216 obsolete, remove eventually - wEta = Weight(pbyp.fEta, wETA); + wEta = particleWeight(pbyp.fEta, wETA); } nl.ftaNestedLoops[1]->AddAt(wPhi * wPt * wEta * wCharge, particleIndex); // remember that the 2nd argument here must start from 0 } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void FillNestedLoopsContainers(const int& particleIndex) +} // void fillNestedLoopsContainers(const int& particleIndex) //============================================================ -void CalculateNestedLoops() +void calculateNestedLoops() { // Calculate correlations with nested loops. @@ -13596,17 +13669,23 @@ void CalculateNestedLoops() // d) 8-particle nested loops. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } LOGF(info, " ebye.fSelectedTracks = %d", ebye.fSelectedTracks); int nParticles = ebye.fSelectedTracks; + int lMinNL = -1; // TBI 20260602 : I need this below somewhat artificially to silent o2_linter : Avoid magic numbers in expressions + int lMaxNL = -1; // TBI 20260602 : I need this below somewhat artificially to silent o2_linter : Avoid magic numbers in expressions + int nParticlesBareMinimum = -1; // TBI 20260602 : I need this below somewhat artificially to silent o2_linter : Avoid magic numbers in expressions // a) 2-particle nested loops: - if (nParticles < 2) { + nParticlesBareMinimum = 2; + if (nParticles < nParticlesBareMinimum) { return; } - if (nl.fMaxNestedLoop > 0 && nl.fMaxNestedLoop < 2) { + lMinNL = 0; + lMaxNL = 2; + if (nl.fMaxNestedLoop > lMinNL && nl.fMaxNestedLoop < lMaxNL) { return; } LOGF(info, " Calculating 2-p correlations with nested loops .... "); @@ -13663,10 +13742,13 @@ void CalculateNestedLoops() LOGF(info, " Done! "); // b) 4-particle nested loops: - if (nParticles < 4) { + nParticlesBareMinimum = 4; + if (nParticles < nParticlesBareMinimum) { return; } - if (nl.fMaxNestedLoop > 0 && nl.fMaxNestedLoop < 4) { + lMinNL = 0; + lMaxNL = 4; + if (nl.fMaxNestedLoop > lMinNL && nl.fMaxNestedLoop < lMaxNL) { return; } LOGF(info, " Calculating 4-p correlations with nested loops .... "); @@ -13728,10 +13810,13 @@ void CalculateNestedLoops() LOGF(info, " Done! "); // c) 6-particle nested loops: - if (nParticles < 6) { + nParticlesBareMinimum = 6; + if (nParticles < nParticlesBareMinimum) { return; } - if (nl.fMaxNestedLoop > 0 && nl.fMaxNestedLoop < 6) { + lMinNL = 0; + lMaxNL = 6; + if (nl.fMaxNestedLoop > lMinNL && nl.fMaxNestedLoop < lMaxNL) { return; } LOGF(info, " Calculating 6-p correlations with nested loops .... "); @@ -13807,10 +13892,13 @@ void CalculateNestedLoops() LOGF(info, " Done! "); // d) 8-particle nested loops: - if (nParticles < 8) { + nParticlesBareMinimum = 8; + if (nParticles < nParticlesBareMinimum) { return; } - if (nl.fMaxNestedLoop > 0 && nl.fMaxNestedLoop < 8) { + lMinNL = 0; + lMaxNL = 8; + if (nl.fMaxNestedLoop > lMinNL && nl.fMaxNestedLoop < lMaxNL) { return; } LOGF(info, " Calculating 8-p correlations with nested loops .... "); @@ -13899,21 +13987,21 @@ void CalculateNestedLoops() } // for(int i1=0; i1>-particle correlations ====\033[0m", 2 * (o + 1)); for (int h = 0; h < gMaxHarmonic; h++) { for (int b = 1; b <= nBinsQV; b++) { @@ -13956,14 +14044,14 @@ void ComparisonNestedLoopsVsCorrelations() } // for (int v = 0; v < 3; v++) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void ComparisonNestedLoopsVsCorrelations() +} // void comparisonNestedLoopsVsCorrelations() //============================================================ -TComplex Q(int n, int wp) +TComplex Q(int n, int wp) // o2-linter: disable=name/function-variable (This is deeply entrenched, in all analytic expressions below, I call this function under this name) { // Using the fact that Q{-n,p} = Q{n,p}^*. @@ -13972,11 +14060,11 @@ TComplex Q(int n, int wp) } return TComplex::Conjugate(qv.fQ[-n][wp]); -} // TComplex FlowWithMultiparticleCorrelationsTask::Q(int n, int wp) +} // TComplex Q(int n, int wp) //============================================================ -TComplex One(int n1) +TComplex one(int n1) { // Generic expression . @@ -13984,11 +14072,11 @@ TComplex One(int n1) return one; -} // TComplex FlowWithMultiparticleCorrelationsTask::One(int n1) +} // TComplex one(int n1) //============================================================ -TComplex Two(int n1, int n2) +TComplex two(int n1, int n2) { // Generic two-particle correlation . @@ -13996,11 +14084,11 @@ TComplex Two(int n1, int n2) return two; -} // TComplex FlowWithMultiparticleCorrelationsTask::Two(int n1, int n2) +} // TComplex two(int n1, int n2) //============================================================ -TComplex Three(int n1, int n2, int n3) +TComplex three(int n1, int n2, int n3) { // Generic three-particle correlation . @@ -14010,11 +14098,11 @@ TComplex Three(int n1, int n2, int n3) return three; -} // TComplex Three(int n1, int n2, int n3) +} // TComplex three(int n1, int n2, int n3) //============================================================ -TComplex Four(int n1, int n2, int n3, int n4) +TComplex four(int n1, int n2, int n3, int n4) { // Generic four-particle correlation . @@ -14031,11 +14119,11 @@ TComplex Four(int n1, int n2, int n3, int n4) return four; -} // TComplex Four(int n1, int n2, int n3, int n4) +} // TComplex four(int n1, int n2, int n3, int n4) //============================================================ -TComplex Five(int n1, int n2, int n3, int n4, int n5) +TComplex five(int n1, int n2, int n3, int n4, int n5) { // Generic five-particle correlation . @@ -14043,11 +14131,11 @@ TComplex Five(int n1, int n2, int n3, int n4, int n5) return five; -} // TComplex Five(int n1, int n2, int n3, int n4, int n5) +} // TComplex five(int n1, int n2, int n3, int n4, int n5) //============================================================ -TComplex Six(int n1, int n2, int n3, int n4, int n5, int n6) +TComplex six(int n1, int n2, int n3, int n4, int n5, int n6) { // Generic six-particle correlation . @@ -14055,95 +14143,95 @@ TComplex Six(int n1, int n2, int n3, int n4, int n5, int n6) return six; -} // TComplex Six(int n1, int n2, int n3, int n4, int n5, int n6) +} // TComplex six(int n1, int n2, int n3, int n4, int n5, int n6) //============================================================ -TComplex Seven(int n1, int n2, int n3, int n4, int n5, int n6, int n7) +TComplex seven(int n1, int n2, int n3, int n4, int n5, int n6, int n7) { // Generic seven-particle correlation . int harmonic[7] = {n1, n2, n3, n4, n5, n6, n7}; - TComplex seven = Recursion(7, harmonic); + TComplex seven = recursion(7, harmonic); return seven; -} // end of TComplex Seven(int n1, int n2, int n3, int n4, int n5, int n6, int n7) +} // end of TComplex seven(int n1, int n2, int n3, int n4, int n5, int n6, int n7) //============================================================ -TComplex Eight(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8) +TComplex eight(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8) { // Generic eight-particle correlation . int harmonic[8] = {n1, n2, n3, n4, n5, n6, n7, n8}; - TComplex eight = Recursion(8, harmonic); + TComplex eight = recursion(8, harmonic); return eight; -} // end of Eight(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8) +} // end of eight(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8) //============================================================ -TComplex Nine(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9) +TComplex nine(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9) { // Generic nine-particle correlation . int harmonic[9] = {n1, n2, n3, n4, n5, n6, n7, n8, n9}; - TComplex nine = Recursion(9, harmonic); + TComplex nine = recursion(9, harmonic); return nine; -} // end of TComplex Nine(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9) +} // end of TComplex nine(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9) //============================================================ -TComplex Ten(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10) +TComplex ten(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10) { // Generic ten-particle correlation . int harmonic[10] = {n1, n2, n3, n4, n5, n6, n7, n8, n9, n10}; - TComplex ten = Recursion(10, harmonic); + TComplex ten = recursion(10, harmonic); return ten; -} // end of TComplex Ten(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10) +} // end of TComplex ten(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10) //============================================================ -TComplex Eleven(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11) +TComplex eleven(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11) { // Generic eleven-particle correlation . int harmonic[11] = {n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11}; - TComplex eleven = Recursion(11, harmonic); + TComplex eleven = recursion(11, harmonic); return eleven; -} // end of TComplex Eleven(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11) +} // end of TComplex eleven(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11) //============================================================ -TComplex Twelve(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11, int n12) +TComplex twelve(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11, int n12) { // Generic twelve-particle correlation . int harmonic[12] = {n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12}; - TComplex twelve = Recursion(12, harmonic); + TComplex twelve = recursion(12, harmonic); return twelve; -} // end of TComplex Twelve(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11, int n12) +} // end of TComplex twelve(int n1, int n2, int n3, int n4, int n5, int n6, int n7, int n8, int n9, int n10, int n11, int n12) //============================================================ -TComplex Recursion(int n, int* harmonic, int mult = 1, int skip = 0) +TComplex recursion(int n, int* harmonic, int mult = 1, int iSkip = 0) { // Calculate multi-particle correlators by using recursion (an improved faster version) originally developed by // Kristjan Gulbrandsen (gulbrand@nbi.dk). @@ -14152,8 +14240,8 @@ TComplex Recursion(int n, int* harmonic, int mult = 1, int skip = 0) TComplex c(Q(harmonic[nm1], mult)); if (nm1 == 0) return c; - c *= Recursion(nm1, harmonic); - if (nm1 == skip) + c *= recursion(nm1, harmonic); + if (nm1 == iSkip) return c; int multp1 = mult + 1; @@ -14162,16 +14250,16 @@ TComplex Recursion(int n, int* harmonic, int mult = 1, int skip = 0) int hhold = harmonic[counter1]; harmonic[counter1] = harmonic[nm2]; harmonic[nm2] = hhold + harmonic[nm1]; - TComplex c2(Recursion(nm1, harmonic, multp1, nm2)); + TComplex c2(recursion(nm1, harmonic, multp1, nm2)); int counter2 = n - 3; - while (counter2 >= skip) { + while (counter2 >= iSkip) { harmonic[nm2] = harmonic[counter1]; harmonic[counter1] = hhold; ++counter1; hhold = harmonic[counter1]; harmonic[counter1] = harmonic[nm2]; harmonic[nm2] = hhold + harmonic[nm1]; - c2 += Recursion(nm1, harmonic, multp1, counter2); + c2 += recursion(nm1, harmonic, multp1, counter2); --counter2; } harmonic[nm2] = harmonic[counter1]; @@ -14181,17 +14269,17 @@ TComplex Recursion(int n, int* harmonic, int mult = 1, int skip = 0) return c - c2; return c - static_cast(mult) * c2; -} // TComplex Recursion(int n, int* harmonic, int mult = 1, int skip = 0) +} // TComplex recursion(int n, int* harmonic, int mult = 1, int iSkip = 0) //============================================================ -void ResetQ() +void resetQ() { // Reset the components of generic Q-vectors. Use it whenever you call the // standard functions for correlations, for some custom Q-vectors. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } for (int h = 0; h < gMaxHarmonic * gMaxCorrelator + 1; h++) { @@ -14202,22 +14290,26 @@ void ResetQ() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void ResetQ() +} // void resetQ() //============================================================ -void SetWeightsHist(TH1D* const hist, eWeights whichWeight) +void setWeightsHist(TH1D* const hist, EnWeights whichWeight) { // Copy histogram holding weights from an external file to the corresponding data member. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Finally: + if (!hist) { + LOGF(fatal, "in function \033[1;31m%s at line %d \n hist is NULL \033[0m", __FUNCTION__, __LINE__); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter + } hist->SetDirectory(0); pw.fWeightsHist[whichWeight] = reinterpret_cast(hist); @@ -14226,7 +14318,7 @@ void SetWeightsHist(TH1D* const hist, eWeights whichWeight) } // Cosmetics: TBI 20240216 do I really want to overwrite initial cosmetics, perhaps this shall go better into MakeWeights.C ? - // Or I could move all this to GetHistogramWithWeights, where in any case I am setting e.g. histogram title, etc. + // Or I could move all this to getHistogramWithWeights, where in any case I am setting e.g. histogram title, etc. TString sVariable[eWeights_N] = {"#varphi", "p_{t}", "#eta"}; // [phi,pt,eta] TString sWeights[eWeights_N] = {"w_{#varphi}", "w_{p_{t}}", "w_{#eta}"}; pw.fWeightsHist[whichWeight]->SetStats(false); @@ -14235,34 +14327,38 @@ void SetWeightsHist(TH1D* const hist, eWeights whichWeight) pw.fWeightsHist[whichWeight]->SetFillColor(eFillColor); pw.fWeightsHist[whichWeight]->SetLineColor(eColor); if (!pw.fWeightsList) { - LOGF(fatal, "\033[1;31m%s at line %d: fWeightsList is NULL. That means that you have called SetWeightsHist(...) in init(), before this TList was booked.\033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d: fWeightsList is NULL. That means that you have called setWeightsHist(...) in init(), before this TList was booked.\033[0m", __FUNCTION__, __LINE__); } - pw.fWeightsList->Add(pw.fWeightsHist[whichWeight]); // This is working at the moment, because I am fetching all weights in Preprocess(), which is called after init() + pw.fWeightsList->Add(pw.fWeightsHist[whichWeight]); // This is working at the moment, because I am fetching all weights in preprocess(), which is called after init() // But if eventually it will be possible to fetch run number programatically in init(), I will have to re-think this line. // Flag: pw.fUseWeights[whichWeight] = true; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void SetWeightsHist(TH1D* const hist, eWeights whichWeight) +} // void setWeightsHist(TH1D* const hist, EnWeights whichWeight) //============================================================ -void SetDiffWeightsHist(TH1D* const hist, eDiffWeights whichDiffWeight, int bin) +void setDiffWeightsHist(TH1D* const hist, EnDiffWeights whichDiffWeight, int bin) { // Copy histogram holding differential weights from an external file to the corresponding data member. - // Remark: Do not edit histogram title here, because that's done in GetHistogramWithWeights(), because I have "filePath" info there locally. + // Remark: Do not edit histogram title here, because that's done in getHistogramWithWeights(), because I have "filePath" info there locally. // Only if I promote "filePath" to data members, re-think the design of this function, and what goes where. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Finally: + if (!hist) { + LOGF(fatal, "in function \033[1;31m%s at line %d \n hist is NULL \033[0m", __FUNCTION__, __LINE__); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter + } hist->SetDirectory(0); pw.fDiffWeightsHist[whichDiffWeight][bin] = reinterpret_cast(hist); @@ -14271,7 +14367,7 @@ void SetDiffWeightsHist(TH1D* const hist, eDiffWeights whichDiffWeight, int bin) } // Cosmetics: TBI 20240216 do I really want to overwrite initial cosmetics, perhaps this shall go better into MakeWeights.C ? - // Or I could move all this to GetHistogramWithWeights, where in any case I am setting e.g. histogram title, etc. + // Or I could move all this to getHistogramWithWeights, where in any case I am setting e.g. histogram title, etc. TString sVariable[eDiffWeights_N] = {"#varphi", "#varphi"}; // yes, for the time being, x-axis is always phi TString sWeights[eDiffWeights_N] = {"(w_{#varphi})_{| p_{T}}", "(w_{#varphi})_{| #eta}"}; pw.fDiffWeightsHist[whichDiffWeight][bin]->SetStats(false); @@ -14279,7 +14375,7 @@ void SetDiffWeightsHist(TH1D* const hist, eDiffWeights whichDiffWeight, int bin) pw.fDiffWeightsHist[whichDiffWeight][bin]->GetYaxis()->SetTitle(sWeights[whichDiffWeight].Data()); pw.fDiffWeightsHist[whichDiffWeight][bin]->SetFillColor(eFillColor); pw.fDiffWeightsHist[whichDiffWeight][bin]->SetLineColor(eColor); - pw.fWeightsList->Add(pw.fDiffWeightsHist[whichDiffWeight][bin]); // This is working at the moment, because I am fetching all weights in Preprocess(), which is called after init() + pw.fWeightsList->Add(pw.fDiffWeightsHist[whichDiffWeight][bin]); // This is working at the moment, because I am fetching all weights in preprocess(), which is called after init() // But if eventually it will be possible to fetch run number programatically in init(), I will have to re-think this line. // Flag: @@ -14289,22 +14385,22 @@ void SetDiffWeightsHist(TH1D* const hist, eDiffWeights whichDiffWeight, int bin) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // SetDiffWeightsHist(TH1D* const hist, const char *variable, int bin) +} // setDiffWeightsHist(TH1D* const hist, const char *variable, int bin) //============================================================ -void SetDiffWeightsSparse(THnSparseF* const sparse, eDiffWeightCategory dwc) +void setDiffWeightsSparse(THnSparseF* const sparse, EnDiffWeightCategory dwc) { // Copy sparse histogram holding differential phi, pt, eta, etc., weights from an external file to the corresponding data member. - // Remark: Do not edit sparse histogram title here, because that's done in GetHistogramWithWeights(), because I have "filePath" info there locally. + // Remark: Do not edit sparse histogram title here, because that's done in getHistogramWithWeights(), because I have "filePath" info there locally. // Only if I promote "filePath" to data members, re-think the design of this function, and what goes where. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Finally: @@ -14325,10 +14421,10 @@ void SetDiffWeightsSparse(THnSparseF* const sparse, eDiffWeightCategory dwc) pw.fWeightsList->Add(pw.fDiffWeightsSparse[dwc]); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void SetDiffWeightsSparse(THnSparseF* const sparse, eDiffWeightCategory dwc) +} // void setDiffWeightsSparse(THnSparseF* const sparse, EnDiffWeightCategory dwc) //============================================================ @@ -14340,7 +14436,7 @@ void insanitizeDiffWeightsSparse(THnSparseF* const sparse) // TBI 20260223 : I am doing one unnecessary extra check in each if statement below to prevent rounding problem - check this further if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if (!sparse) { @@ -14359,7 +14455,7 @@ void insanitizeDiffWeightsSparse(THnSparseF* const sparse) } // check all supported observables: - if (!axisTitle.compare("#varphi")) { // I have to negate, becase compare() returns 0 if strings are equal + keep in sync hardwired string here with what i have in FancyFormatting(...) + if (!axisTitle.compare("#varphi")) { // I have to negate, becase compare() returns 0 if strings are equal + keep in sync hardwired string here with what i have in fancyFormatting(...) // check lower boundary: if ((pc.fdParticleCuts[ePhi][eMin] < sparse->GetAxis(d)->GetBinLowEdge(1)) && (std::abs(sparse->GetAxis(d)->GetBinLowEdge(1) - pc.fdParticleCuts[ePhi][eMin]) > tc.fFloatingPointPrecision)) { @@ -14407,7 +14503,7 @@ void insanitizeDiffWeightsSparse(THnSparseF* const sparse) LOGF(fatal, "\033[1;31m%s at line %d : axis %d (Charge) of sparse %s has upper boundary %f, while upper cut on that variable is %f. This means that for some particles I won't be able to fetch weights from this sparse. \033[0m", __FUNCTION__, __LINE__, d, sparse->GetName(), sparse->GetAxis(d)->GetBinUpEdge(sparse->GetAxis(d)->GetNbins()), pc.fdParticleCuts[eCharge][eMax]); } - } else if (!axisTitle.find("Centrality")) { // I have to use here find() instead, because title also contains centrality estimator name, e.g. "Centality (FT0C)" + } else if (!axisTitle.starts_with("Centrality")) { // I have to use here axisTitle.starts_with("Centrality"), because title also contains centrality estimator name, e.g. "Centality (FT0C)". If I ever reformat the axis title, see if I need to use instead axisTitle.find("Centrality") // check lower boundary: if ((ec.fdEventCuts[eCentrality][eMin] < sparse->GetAxis(d)->GetBinLowEdge(1)) && (std::abs(sparse->GetAxis(d)->GetBinLowEdge(1) - ec.fdEventCuts[eCentrality][eMin]) > tc.fFloatingPointPrecision)) { @@ -14419,7 +14515,7 @@ void insanitizeDiffWeightsSparse(THnSparseF* const sparse) LOGF(fatal, "\033[1;31m%s at line %d : axis %d (Centrality) of sparse %s has upper boundary %f, while upper cut on that variable is %f. This means that for some events I won't be able to fetch particles weights from this sparse. \033[0m", __FUNCTION__, __LINE__, d, sparse->GetName(), sparse->GetAxis(d)->GetBinUpEdge(sparse->GetAxis(d)->GetNbins()), ec.fdEventCuts[eCentrality][eMax]); } - } else if (!(axisTitle.compare("V_{z}")) || !(axisTitle.compare("VertexZ"))) { // TBI 20260217 I use indeed "VertexZ" for the time being, not sure why I didn't use here also FancyFormatting. But it doesn't hurt to add check for both + } else if (!(axisTitle.compare("V_{z}")) || !(axisTitle.compare("VertexZ"))) { // TBI 20260217 I use indeed "VertexZ" for the time being, not sure why I didn't use here also fancyFormatting. But it doesn't hurt to add check for both // check lower boundary: if ((ec.fdEventCuts[eVertexZ][eMin] < sparse->GetAxis(d)->GetBinLowEdge(1)) && (std::abs(sparse->GetAxis(d)->GetBinLowEdge(1) - ec.fdEventCuts[eVertexZ][eMin]) > tc.fFloatingPointPrecision)) { @@ -14440,22 +14536,26 @@ void insanitizeDiffWeightsSparse(THnSparseF* const sparse) } // for(int d = 0; d < nDim; d++) { if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } // void insanitizeDiffWeightsSparse(THnSparseF* const sparse) //============================================================ -void SetCentralityWeightsHist(TH1D* const hist) +void setCentralityWeightsHist(TH1D* const hist) { // Copy histogram holding weights from an external file to the corresponding data member. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Finally: + if (!hist) { + LOGF(fatal, "in function \033[1;31m%s at line %d \n hist is NULL \033[0m", __FUNCTION__, __LINE__); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter + } hist->SetDirectory(0); cw.fCentralityWeightsHist = reinterpret_cast(hist); @@ -14464,61 +14564,57 @@ void SetCentralityWeightsHist(TH1D* const hist) } // Cosmetics: TBI 20240216 do I really want to overwrite initial cosmetics, perhaps this shall go better into MakeCentralityWeights.C ? - // Or I could move all this to GetHistogramWithCentralityWeights, where in any case I am setting e.g. histogram title, etc. + // Or I could move all this to getHistogramWithCentralityWeights, where in any case I am setting e.g. histogram title, etc. cw.fCentralityWeightsHist->SetStats(false); cw.fCentralityWeightsHist->GetXaxis()->SetTitle("Centrality percentile"); cw.fCentralityWeightsHist->GetYaxis()->SetTitle(Form("Centrality weight (%s)", ec.fsEventCuts[eCentralityEstimator].Data())); cw.fCentralityWeightsHist->SetFillColor(eFillColor); cw.fCentralityWeightsHist->SetLineColor(eColor); if (!cw.fCentralityWeightsList) { - LOGF(fatal, "\033[1;31m%s at line %d: fCentralityWeightsList is NULL. That means that you have called SetCentralityWeightsHist(...) in init(), before this TList was booked.\033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d: fCentralityWeightsList is NULL. That means that you have called setCentralityWeightsHist(...) in init(), before this TList was booked.\033[0m", __FUNCTION__, __LINE__); } - cw.fCentralityWeightsList->Add(cw.fCentralityWeightsHist); // This is working at the moment, because I am fetching all centrality weights in Preprocess(), which is called after init() + cw.fCentralityWeightsList->Add(cw.fCentralityWeightsHist); // This is working at the moment, because I am fetching all centrality weights in preprocess(), which is called after init() // But if eventually it will be possible to fetch run number programatically in init(), I will have to re-think this line. // Flag: cw.fUseCentralityWeights = true; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void SetCentralityWeightsHist(TH1D* const hist) +} // void setCentralityWeightsHist(TH1D* const hist) //============================================================ -TH1D* GetWeightsHist(eWeights whichWeight) +TH1D* getWeightsHist(EnWeights whichWeight) { // The standard getter. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } - // ... - - if (tc.fVerbose) { - ExitFunction(__FUNCTION__); - } + // TBI 20260714 To silent MegaLinter, i removed exitFunction(__FUNCTION__); => only if I add some more code here, bring it back // Finally: return pw.fWeightsHist[whichWeight]; -} // TH1D* GetWeightsHist(eWeights whichWeigh) +} // TH1D* getWeightsHist(EnWeights whichWeigh) //============================================================ -TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const char* variable, int bin = -1) +TH1D* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* variable, int bin = -1) { // Get and return histogram with weights from an external file. // If bin > 0, differential weights for that bin are searched for. // If bin = -1, integrated weights are searched for, i.e. in this case "bin" variable has no effect. - // I do it this way, so as to condense GetHistogramWithWeights(...) and GetHistogramWithDiffWeights(...) from MuPa class in + // I do it this way, so as to condense getHistogramWithWeights(...) and GetHistogramWithDiffWeights(...) from MuPa class in // one routine here, so that I do not duplicate code related to CCDB access, etc. // TBI 20240504: Here I can keep const char* variable , i.e. no need to switch to enums, because this function is called only once, at init. // Nevertheless, I could switch to enums and make it more general, i.e. I could introduce additional data members and configurables, - // for the names of histograms with weights. Like I did it in void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) + // for the names of histograms with weights. Like I did it in void getHistogramWithCustomNUA(const char* filePath, EnNUAPDF variable) // TBI 20241021 Strictly speaking, I do not need to pass here first 2 arguments, "filePath" and "runNumber", because they are initialized at call from data members. // But since this function is called only once, it's not an important performance loss. But re-think the design here eventually. @@ -14534,7 +14630,7 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const // h) Clone histogram and delete baseList (realising back the memory). if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;33m filePath = %s\033[0m", filePath); LOGF(info, "\033[1;33m runNumber = %s\033[0m", runNumber); LOGF(info, "\033[1;33m variable = %s\033[0m", variable); @@ -14572,7 +14668,7 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const if (bFileIsInAliEn) { // d) Handle the AliEn case: - TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); if (!alien) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -14590,11 +14686,11 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -14625,11 +14721,11 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -14662,11 +14758,11 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { // baseList->ls(); LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, tc.fRunNumber.Data()); @@ -14676,7 +14772,7 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const } // else { // g) The final touch on histogram with weights: - TString histName = ""; + TString histName; if (-1 == bin) { // Integrated weights: if (!(TString(variable).EqualTo("phi") || TString(variable).EqualTo("pt") || TString(variable).EqualTo("eta"))) { @@ -14690,16 +14786,17 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const // if the previous search failed, descend recursively also into the nested lists: if (!hist) { LOGF(info, "\033[1;33m%s at line %d : previous attempt failed, fetching instead recursively hist with name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); - hist = reinterpret_cast(GetObjectFromList(listWithRuns, histName.Data())); + hist = reinterpret_cast(getObjectFromList(listWithRuns, histName.Data())); } if (!hist) { histName = TString::Format("%s", variable); // yes, for some simple tests I can have only histogram named e.g. 'phi' LOGF(info, "\033[1;33m%s at line %d : last attempt, fetching instead hist with trivial name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); - hist = reinterpret_cast(GetObjectFromList(listWithRuns, histName.Data())); + hist = reinterpret_cast(getObjectFromList(listWithRuns, histName.Data())); } if (!hist) { listWithRuns->ls(); LOGF(fatal, "\033[1;31m%s at line %d : couldn't fetch hist with name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } hist->SetDirectory(0); hist->SetTitle(Form("%s, %s", filePath, runNumber)); // I have to do it here, because only here I have "filePath" av @@ -14716,16 +14813,17 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const // if the previous search failed, descend recursively also into the nested lists: if (!hist) { LOGF(info, "\033[1;33m%s at line %d : previous attempt failed, fetching instead recursively hist with name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); - hist = reinterpret_cast(GetObjectFromList(listWithRuns, Form("%s[%d]_%s", variable, bin, tc.fTaskName.Data()))); + hist = reinterpret_cast(getObjectFromList(listWithRuns, Form("%s[%d]_%s", variable, bin, tc.fTaskName.Data()))); } if (!hist) { histName = TString::Format("%s[%d]", variable, bin); // yes, for some simple tests I can have only histogram named e.g. 'phipt[0]' LOGF(info, "\033[1;33m%s at line %d : last attempt, fetching instead hist with trivial name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); - hist = reinterpret_cast(GetObjectFromList(listWithRuns, histName.Data())); + hist = reinterpret_cast(getObjectFromList(listWithRuns, histName.Data())); } if (!hist) { listWithRuns->ls(); LOGF(fatal, "\033[1;31m%s at line %d : couldn't fetch hist with name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } // *) insanity check for differential weights => check if boundaries of current bin are the same as bin boundaries for which these weights were calculated. @@ -14735,36 +14833,37 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const TObjArray* oa = TString(hist->GetTitle()).Tokenize(" "); if (!oa) { LOGF(fatal, "in function \033[1;31m%s at line %d \n hist->GetTitle() = %s\033[0m", __FUNCTION__, __LINE__, hist->GetTitle()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } int nEntries = oa->GetEntries(); // I need to figure out corresponding variable from results histograms and its formatting: - eAsFunctionOf AFO = eAsFunctionOf_N; + EnAsFunctionOf afo = eAsFunctionOf_N; const char* lVariableName = ""; if (TString(variable).EqualTo("phipt")) { - AFO = AFO_PT; - lVariableName = FancyFormatting("Pt"); + afo = AFO_PT; + lVariableName = fancyFormatting("Pt"); } else if (TString(variable).EqualTo("phieta")) { - AFO = AFO_ETA; - lVariableName = FancyFormatting("Eta"); + afo = AFO_ETA; + lVariableName = fancyFormatting("Eta"); } else { LOGF(fatal, "\033[1;31m%s at line %d : name = %s is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(variable)); } // Get min and max value for bin, stored locally: - float min = res.fResultsPro[AFO]->GetBinLowEdge(bin + 1); - float max = res.fResultsPro[AFO]->GetBinLowEdge(bin + 2); + float min = res.fResultsPro[afo]->GetBinLowEdge(bin + 1); + float max = res.fResultsPro[afo]->GetBinLowEdge(bin + 2); if (min > max) { - LOGF(fatal, "\033[1;33m min = %f, max = %f, res.fResultsPro[AFO]->GetName() = %s\033[0m", min, max, res.fResultsPro[AFO]->GetName()); + LOGF(fatal, "\033[1;33m min = %f, max = %f, res.fResultsPro[afo]->GetName() = %s\033[0m", min, max, res.fResultsPro[afo]->GetName()); } // Compare with min and max value stored in external weights.root file using MakeWeights.C: if (!(std::abs(TString(oa->At(nEntries - 1)->GetName()).Atof() - max) < tc.fFloatingPointPrecision)) { - LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[AFO]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[AFO]->GetName()); + LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[afo]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[afo]->GetName()); LOGF(fatal, "in function \033[1;31m%s at line %d : mismatch in upper bin boundaries \n from title = %f , local = %f\033[0m", __FUNCTION__, __LINE__, TString(oa->At(nEntries - 1)->GetName()).Atof(), max); } if (!(std::abs(TString(oa->At(nEntries - 5)->GetName()).Atof() - min) < tc.fFloatingPointPrecision)) { - LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[AFO]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[AFO]->GetName()); + LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[afo]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[afo]->GetName()); LOGF(fatal, "in function \033[1;31m%s at line %d : mismatch in lower bin boundaries \n from title = %f , local = %f\033[0m", __FUNCTION__, __LINE__, TString(oa->At(nEntries - 5)->GetName()).Atof(), min); } delete oa; // yes, otherwise it's a memory leak @@ -14784,16 +14883,16 @@ TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const delete baseList; // release back the memory if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return histClone; -} // TH1D* GetHistogramWithWeights(const char* filePath, const char* runNumber, const char* variable, int bin = -1) +} // TH1D* getHistogramWithWeights(const char* filePath, const char* runNumber, const char* variable, int bin = -1) //============================================================ -THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runNumber, const char* whichCategory, const char* whichDimensions) +THnSparseF* getSparseHistogramWithWeights(const char* filePath, const char* runNumber, const char* whichCategory, const char* whichDimensions) { // Get and return sparse histogram with weights from an external file. @@ -14809,7 +14908,7 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN // TBI 20240504: Here I can keep const char* variable , i.e. no need to switch to enums, because this function is called only once, at init. // Nevertheless, I could switch to enums and make it more general, i.e. I could introduce additional data members and configurables, - // for the names of histograms with weights. Like I did it in void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) + // for the names of histograms with weights. Like I did it in void getHistogramWithCustomNUA(const char* filePath, EnNUAPDF variable) // TBI 20241021 Strictly speaking, I do not need to pass here first 2 arguments, "filePath" and "runNumber", because they are initialized at call from data members. // But since this function is called only once, it's not an important performance loss. But re-think the design here eventually. @@ -14825,7 +14924,7 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN // h) Clone histogram and delete baseList (realising back the memory). if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;33m filePath = %s\033[0m", filePath); LOGF(info, "\033[1;33m runNumber = %s\033[0m", runNumber); LOGF(info, "\033[1;33m whichDimensions = %s\033[0m", whichDimensions); @@ -14862,7 +14961,7 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN if (bFileIsInAliEn) { // d) Handle the AliEn case: - TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); if (!alien) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -14879,11 +14978,11 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -14914,11 +15013,11 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -14951,11 +15050,11 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { // baseList->ls(); LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, tc.fRunNumber.Data()); @@ -14965,7 +15064,7 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN } // else { // g) The final touch on sparse histogram with weights: - TString sparseHistName = ""; + TString sparseHistName; if (TString(whichDimensions).EqualTo("")) { sparseHistName = TString::Format("%s_multiparticle-correlations-a-b", whichCategory); } else if (TString(whichDimensions).BeginsWith("_")) { // TBI 20250215 alternativelly, I can remove leading "_" before calling this function @@ -14990,11 +15089,11 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN // 2. if the previous search failed, descend recursively into the nested lists: if (!sparseHist) { LOGF(info, "\033[1;33m%s at line %d : previous attempt failed, fetching instead recursively sparse histogram with the name \"%s\"\033[0m", __FUNCTION__, __LINE__, sparseHistName.Data()); - sparseHist = reinterpret_cast(GetObjectFromList(listWithRuns, sparseHistName.Data())); + sparseHist = reinterpret_cast(getObjectFromList(listWithRuns, sparseHistName.Data())); if (!sparseHist) { // try once again by chopping off "multiparticle-correlations-a-b_" from name: TString tmp = sparseHistName; // yes, because "ReplaceAll" below replaces in-place, and I will need sparseHistName unmodified still later - sparseHist = reinterpret_cast(GetObjectFromList(listWithRuns, tmp.ReplaceAll("multiparticle-correlations-a-b_", ""))); + sparseHist = reinterpret_cast(getObjectFromList(listWithRuns, tmp.ReplaceAll("multiparticle-correlations-a-b_", ""))); } } @@ -15003,6 +15102,7 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN LOGF(info, "\033[1;33m%s at line %d : runNumber = \"%s\"\033[0m", __FUNCTION__, __LINE__, runNumber); listWithRuns->ls(); LOGF(fatal, "\033[1;31m%s at line %d : couldn't fetch sparse histogram with name = %s from this list\033[0m", __FUNCTION__, __LINE__, sparseHistName.Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } sparseHist->SetTitle(Form("%s, %s", filePath, runNumber)); // I have to do it here, because only here I have "filePath" available @@ -15021,32 +15121,32 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN // int nEntries = oa->GetEntries(); // // // I need to figure out corresponding variable from results histograms and its formatting: - // eAsFunctionOf AFO = eAsFunctionOf_N; + // EnAsFunctionOf afo = eAsFunctionOf_N; // const char* lVariableName = ""; // if (TString(variable).EqualTo("phipt")) { - // AFO = AFO_PT; - // lVariableName = FancyFormatting("Pt"); + // afo = AFO_PT; + // lVariableName = fancyFormatting("Pt"); // } else if (TString(variable).EqualTo("phieta")) { - // AFO = AFO_ETA; - // lVariableName = FancyFormatting("Eta"); + // afo = AFO_ETA; + // lVariableName = fancyFormatting("Eta"); // } else { // LOGF(fatal, "\033[1;31m%s at line %d : name = %s is not supported yet. \033[0m", __FUNCTION__, __LINE__, static_cast(variable)); // } // // // Get min and max value for bin, stored locally: - // float min = res.fResultsPro[AFO]->GetBinLowEdge(bin + 1); - // float max = res.fResultsPro[AFO]->GetBinLowEdge(bin + 2); + // float min = res.fResultsPro[afo]->GetBinLowEdge(bin + 1); + // float max = res.fResultsPro[afo]->GetBinLowEdge(bin + 2); // if (min > max) { - // LOGF(fatal, "\033[1;33m min = %f, max = %f, res.fResultsPro[AFO]->GetName() = %s\033[0m", min, max, res.fResultsPro[AFO]->GetName()); + // LOGF(fatal, "\033[1;33m min = %f, max = %f, res.fResultsPro[afo]->GetName() = %s\033[0m", min, max, res.fResultsPro[afo]->GetName()); // } // // // Compare with min and max value stored in external weights.root file using MakeWeights.C: // if (!(std::abs(TString(oa->At(nEntries - 1)->GetName()).Atof() - max) < tc.fFloatingPointPrecision)) { - // LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[AFO]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[AFO]->GetName()); + // LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[afo]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[afo]->GetName()); // LOGF(fatal, "in function \033[1;31m%s at line %d : mismatch in upper bin boundaries \n from title = %f , local = %f\033[0m", __FUNCTION__, __LINE__, TString(oa->At(nEntries - 1)->GetName()).Atof(), max); // } // if (!(std::abs(TString(oa->At(nEntries - 5)->GetName()).Atof() - min) < tc.fFloatingPointPrecision)) { - // LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[AFO]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[AFO]->GetName()); + // LOGF(info, "\033[1;33m hist->GetTitle() = %s, res.fResultsPro[afo]->GetName() = %s\033[0m", hist->GetTitle(), res.fResultsPro[afo]->GetName()); // LOGF(fatal, "in function \033[1;31m%s at line %d : mismatch in lower bin boundaries \n from title = %f , local = %f\033[0m", __FUNCTION__, __LINE__, TString(oa->At(nEntries - 5)->GetName()).Atof(), min); // } // delete oa; // yes, otherwise it's a memory leak @@ -15063,20 +15163,20 @@ THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runN delete baseList; // release back the memory if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return sparseHistClone; -} // THnSparseF* GetSparseHistogramWithWeights(const char* filePath, const char* runNumber, const char* whichCategory, const char* whichDimensions) +} // THnSparseF* getSparseHistogramWithWeights(const char* filePath, const char* runNumber, const char* whichCategory, const char* whichDimensions) //============================================================ -TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNumber) +TH1D* getHistogramWithCentralityWeights(const char* filePath, const char* runNumber) { // Get and return histogram with centrality weights from an external file. - // TBI 20241118 Shall I merge this function with GetHistogramWithWeights(...) as there is a bit of code bloat? + // TBI 20241118 Shall I merge this function with getHistogramWithWeights(...) as there is a bit of code bloat? // TBI 20241021 Strictly speaking, I do not need to pass here 2 arguments, "filePath" and "runNumber", because they are initialized at call from data members. // But since this function is called only once, it's not an important performance loss. But re-think the design here eventually. @@ -15091,7 +15191,7 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum // h) Clone histogram and delete baseList (realising back the memory). if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;33m filePath = %s\033[0m", filePath); LOGF(info, "\033[1;33m runNumber = %s\033[0m", runNumber); LOGF(info, "\033[1;33m fTaskName = %s\033[0m", tc.fTaskName.Data()); @@ -15122,7 +15222,7 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum if (bFileIsInAliEn) { // d) Handle the AliEn case: - TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); if (!alien) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -15139,11 +15239,11 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -15174,11 +15274,11 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -15211,11 +15311,11 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumber)); if (!listWithRuns) { TString runNumberWithLeadingZeroes = "000"; runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number - listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + listWithRuns = reinterpret_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); if (!listWithRuns) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -15224,12 +15324,12 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum } // else { // g) The final touch on histogram with centrality weights: - TString histName = ""; + TString histName; // fetch histogram directly from this list: // Remark: histName must be formated as e.g. "FT0C_multiparticle-correlations-a-b" for default analysis, or "FT0C_multiparticle-correlations-a-b_someCut" - // Isolate short centrality estimator name, e.g. "FT0C" or "V0M" TBI 20250122 move this to utility function, because I have the same code in FancyFormatting() + // Isolate short centrality estimator name, e.g. "FT0C" or "V0M" TBI 20250122 move this to utility function, because I have the same code in fancyFormatting() TString tmp = ec.fsEventCuts[eCentralityEstimator]; // I have to introduce local TString tmp, because ReplaceAll replaces in-place if (tmp.BeginsWith("CentRun2")) { tmp.ReplaceAll("CentRun2", ""); // "CentRun2V0M" => "V0M" @@ -15251,22 +15351,23 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum // if the previous search failed, descend recursively also into the nested lists: if (!hist) { LOGF(info, "\033[1;33m%s at line %d : previous attempt failed, fetching instead recursively hist with name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); - hist = reinterpret_cast(GetObjectFromList(listWithRuns, histName.Data())); + hist = reinterpret_cast(getObjectFromList(listWithRuns, histName.Data())); } if (!hist) { histName = tmp; // yes, for some simple tests I can have only histogram named e.g. 'FT0C' LOGF(info, "\033[1;33m%s at line %d : last attempt, fetching instead hist with trivial name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); - hist = reinterpret_cast(GetObjectFromList(listWithRuns, histName.Data())); + hist = reinterpret_cast(getObjectFromList(listWithRuns, histName.Data())); } if (!hist) { listWithRuns->ls(); LOGF(fatal, "\033[1;31m%s at line %d : couldn't fetch hist with name = %s\033[0m", __FUNCTION__, __LINE__, histName.Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } hist->SetDirectory(0); hist->SetTitle(Form("%s, %s", filePath, runNumber)); // I have to do it here, because only here I have "filePath" av if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } // h) Clone histogram and delete baseList (realising back the memory): @@ -15276,16 +15377,16 @@ TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNum return histClone; -} // TH1D* GetHistogramWithCentralityWeights(const char* filePath, const char* runNumber) +} // TH1D* getHistogramWithCentralityWeights(const char* filePath, const char* runNumber) //============================================================ -TObjArray* GetDefaultObjArrayWithLabels(const char* whichDefaultLabels) +TObjArray* getDefaultObjArrayWithLabels(const char* whichDefaultLabels) { // To speed up testing, I hardwire here some labels and use them directly as they are. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Define TObjArray: @@ -15560,16 +15661,16 @@ TObjArray* GetDefaultObjArrayWithLabels(const char* whichDefaultLabels) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return arr; -} // TObjArray* GetDefaultObjArrayWithLabels(const char* whichDefaultLabels) +} // TObjArray* getDefaultObjArrayWithLabels(const char* whichDefaultLabels) //============================================================ -TObjArray* GetObjArrayWithLabels(const char* filePath) +TObjArray* getObjArrayWithLabels(const char* filePath) { // This function extracts from an external file TObjArray named "labels", and // returns it. External file can be: @@ -15586,7 +15687,7 @@ TObjArray* GetObjArrayWithLabels(const char* filePath) // f) Clean up. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Return value: @@ -15613,7 +15714,7 @@ TObjArray* GetObjArrayWithLabels(const char* filePath) TFile* oaFile = NULL; // file holding TObjArray with all labels if (bFileIsInAliEn) { // c) Handle the AliEn case: - TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); if (!alien) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -15696,7 +15797,7 @@ TObjArray* GetObjArrayWithLabels(const char* filePath) if (tc.fVerbose) { LOGF(info, "\033[1;32m%s => Fetched TObjArray named \"%s\" from file %s\033[0m", __FUNCTION__, oa->GetName(), filePath); - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } // f) Clean up: @@ -15708,11 +15809,11 @@ TObjArray* GetObjArrayWithLabels(const char* filePath) return oa; // "292535" (local) + 294291 (ccdb) -} // TObjArray* GetObjArrayWithLabels(const char *filePath) +} // TObjArray* getObjArrayWithLabels(const char *filePath) //============================================================ -void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) +void getHistogramWithCustomNUA(const char* filePath, EnNUAPDF variable) { // Get and set immediately histogram with custom NUA for specified variable, from an external file. // This structure of an external file is mandatory at the moment: @@ -15722,7 +15823,7 @@ void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) // Remark: Unlike for weights and labels, here I am trying to do everythign in the same function, that's why here return type is void, instead of TH1D*. - // TBI 20240501 there is a bit of code repetition below, add with analogous functions GetHistogramWithWeights(...) and GetObjArrayWithLabels(...) + // TBI 20240501 there is a bit of code repetition below, add with analogous functions getHistogramWithWeights(...) and getObjArrayWithLabels(...) // a) Local objects; // b) Basic protection for arguments; @@ -15734,7 +15835,7 @@ void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) // h) Clone histogram and delete baseList (realising back the memory). if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;32m filePath = %s\033[0m", filePath); LOGF(info, "\033[1;32m variable = %d\033[0m", static_cast(variable)); LOGF(info, "\033[1;32m nua.fCustomNUAPDFHistNames[variable]->Data() = %s\033[0m", nua.fCustomNUAPDFHistNames[variable]->Data()); @@ -15772,7 +15873,7 @@ void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) if (bFileIsInAliEn) { // d) Handle the AliEn case: - TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); if (!alien) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -15829,12 +15930,12 @@ void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - // hist = reinterpret_cast(GetObjectFromList(baseList, nua.fCustomNUAPDFHistNames[variable]->Data())); + // hist = reinterpret_cast(getObjectFromList(baseList, nua.fCustomNUAPDFHistNames[variable]->Data())); } // else { // g) The final touch: - hist = reinterpret_cast(GetObjectFromList(baseList, nua.fCustomNUAPDFHistNames[variable]->Data())); + hist = reinterpret_cast(getObjectFromList(baseList, nua.fCustomNUAPDFHistNames[variable]->Data())); if (!hist) { LOGF(info, "\033[1;31m hist is NULL \033[0m"); LOGF(info, "\033[1;31m variable = %d\033[0m", static_cast(variable)); @@ -15843,7 +15944,7 @@ void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) } // h) Clone histogram and delete baseList (realising back the memory). - // TBI 20250315 here I did it a bit differently than e.g. in GetHistogramWithWeights or in GetSparseHistogramWithWeights . + // TBI 20250315 here I did it a bit differently than e.g. in getHistogramWithWeights or in getSparseHistogramWithWeights . // If it's failing here, redo it in exactly the same way as I did it there. hist->SetDirectory(0); nua.fCustomNUAPDF[variable] = reinterpret_cast(hist->Clone()); @@ -15853,14 +15954,14 @@ void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) // TBI 20240501 if additional cosmetics is needed, it can be implemented here if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void GetHistogramWithCustomNUA(const char* filePath, eNUAPDF variable) +} // void getHistogramWithCustomNUA(const char* filePath, EnNUAPDF variable) //============================================================ -void StoreLabelsInPlaceholder() +void storeLabelsInPlaceholder() { // Storal all Test0 labels in the placeholder. @@ -15870,7 +15971,7 @@ void StoreLabelsInPlaceholder() // d) Finally, store the labels from external source into the placeholder. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Initialize all counters; @@ -15882,12 +15983,12 @@ void StoreLabelsInPlaceholder() // b) Fetch TObjArray with labels from an external file or from local hardwired values: TObjArray* oa = NULL; if (t0.fUseDefaultLabels) { - oa = GetDefaultObjArrayWithLabels(t0.fWhichDefaultLabels.Data()); + oa = getDefaultObjArrayWithLabels(t0.fWhichDefaultLabels.Data()); } else { - LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! There is a large memory blow-up when calling function GetObjArrayWithLabels(...) !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); + LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! There is a large memory blow-up when calling function getObjArrayWithLabels(...) !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); - oa = GetObjArrayWithLabels(t0.fFileWithLabels.Data()); + oa = getObjArrayWithLabels(t0.fFileWithLabels.Data()); // TBI 20250412 There is a large memory penalty in this call, confirmed for "local" and "ccdb" cases (most likely also for "alien", but I didn't test it yet) // a) For the "local" case, it's related to the following minimal reproducer: // TFile *f = new TFile("tmp.root", "read"); // memory blows up by > 50 MB @@ -15942,14 +16043,14 @@ void StoreLabelsInPlaceholder() delete oa; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void StoreLabelsInPlaceholder() +} // void storeLabelsInPlaceholder() //============================================================ -TObject* GetObjectFromList(TList* list, const char* objectName) // Last update: 20210918 +TObject* getObjectFromList(TList* list, const char* objectName) // Last update: 20210918 { // Get TObject pointer from TList, even if it's in some nested TList. Foreseen // to be used to fetch histograms or profiles from files directly. Some ideas @@ -15958,10 +16059,10 @@ TObject* GetObjectFromList(TList* list, const char* objectName) // Last update: // file->Get("hist-name"). // Usage: TH1D *hist = (TH1D*) - // GetObjectFromList("some-valid-TList-pointer","some-object-name"); + // getObjectFromList("some-valid-TList-pointer","some-object-name"); // Example 1: - // GetObjectFromList("some-valid-TList-pointer","some-object-name")->Draw(); + // getObjectFromList("some-valid-TList-pointer","some-object-name")->Draw(); // // yes, for histograms and profiles this is just fine, at least in // interpreted code @@ -15970,7 +16071,7 @@ TObject* GetObjectFromList(TList* list, const char* objectName) // Last update: // b) If I have objects with same name, nested in different TLists, what then? if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Insanity checks: @@ -15997,28 +16098,28 @@ TObject* GetObjectFromList(TList* list, const char* objectName) // Last update: (objectIter = next())) // double round braces are to silent the warnings { if (TString(objectIter->ClassName()).EqualTo("TList")) { - objectFinal = GetObjectFromList(reinterpret_cast(objectIter), objectName); + objectFinal = getObjectFromList(reinterpret_cast(objectIter), objectName); if (objectFinal) return objectFinal; } } // while(objectIter = next()) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return NULL; -} // TObject* GetObjectFromList(TList *list, char *objectName) +} // TObject* getObjectFromList(TList *list, char *objectName) //============================================================ -double Weight(const double& value, eWeights whichWeight) // value, integrated [phi,pt,eta] weight +double particleWeight(const double& value, EnWeights whichWeight) // value, integrated [phi,pt,eta] weight { // Determine particle weight. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;32m value = %f\033[0m", value); LOGF(info, "\033[1;32m variable = %d\033[0m", static_cast(whichWeight)); } @@ -16037,24 +16138,24 @@ double Weight(const double& value, eWeights whichWeight) // value, integrated [p } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return weight; -} // Weight(const double &value, eWeights whichWeight) // value, integrated [phi,pt,eta] weight +} // particleWeight(const double &value, EnWeights whichWeight) // value, integrated [phi,pt,eta] weight //============================================================ -double WeightFromSparse(eDiffWeightCategory dwc) +double weightFromSparse(EnDiffWeightCategory dwc) { // Determine differential multidimensional particle weight using sparse histograms. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;31m dwc = %d\033[0m", static_cast(dwc)); LOGF(info, "\033[1;31m%s at line %d : printing current status of all weights flags\033[0m", __FUNCTION__, __LINE__); - PrintAllWeightsFlags(); + printAllWeightsFlags(); } // if (tc.fVerbose) { // *) Reduce dimensionality if possible, i.e. look up only the dimensions in sparse histogram which were requested in this analysis: @@ -16093,6 +16194,9 @@ double WeightFromSparse(eDiffWeightCategory dwc) if (pw.fUseDiffPtWeights[wPtCentralityAxis]) { pw.fFindBinVector[dwc]->AddAt(ebye.fCentrality, dim++); } + if (pw.fUseDiffPtWeights[wPtVertexZAxis]) { + pw.fFindBinVector[dwc]->AddAt(ebye.fVz, dim++); + } // ... break; } @@ -16108,6 +16212,9 @@ double WeightFromSparse(eDiffWeightCategory dwc) if (pw.fUseDiffEtaWeights[wEtaCentralityAxis]) { pw.fFindBinVector[dwc]->AddAt(ebye.fCentrality, dim++); } + if (pw.fUseDiffEtaWeights[wEtaVertexZAxis]) { + pw.fFindBinVector[dwc]->AddAt(ebye.fVz, dim++); + } // ... break; } @@ -16123,6 +16230,9 @@ double WeightFromSparse(eDiffWeightCategory dwc) if (pw.fUseDiffChargeWeights[wChargeCentralityAxis]) { pw.fFindBinVector[dwc]->AddAt(ebye.fCentrality, dim++); } + if (pw.fUseDiffChargeWeights[wChargeVertexZAxis]) { + pw.fFindBinVector[dwc]->AddAt(ebye.fVz, dim++); + } // ... break; } @@ -16173,88 +16283,88 @@ double WeightFromSparse(eDiffWeightCategory dwc) double weight = pw.fDiffWeightsSparse[dwc]->GetBinContent(bin); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return weight; -} // double WeightFromSparse(eDiffWeightCategory dwc) +} // double weightFromSparse(EnDiffWeightCategory dwc) //============================================================ -double DiffWeight(const double& valueY, const double& valueX, eqvectorKine variableX) +double diffWeight(const double& valueY, const double& valueX, EnqvectorKine variableX) { // !!! OBSOLETE FUNCTION !!! - LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250520, this is an obsolete function, use double WeightFromSparse(...) instead !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); + LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250520, this is an obsolete function, use double weightFromSparse(...) instead !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); // Determine differential particle weight y(x). For the time being, "y = phi" always, but this can be generalized. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Determine first to which bin the 'valueX' corresponds to. // Based on that, I decide from which histogram I fetch weight for y. See MakeWeights.C - // *) Mapping between enums "eqvectorKine" on one side, and enums "eAsFunctionOf" and "eDiffWeights" on the other: - eAsFunctionOf AFO_var = eAsFunctionOf_N; // this local variable determines the enum "eAsFunctionOf" which corresponds to enum "eqvectorKine" - eDiffWeights AFO_diffWeight = eDiffWeights_N; // this local variable determines the enum "eDiffWeights" which corresponds to enum "eqvectorKine" + // *) Mapping between enums "EnqvectorKine" on one side, and enums "EnAsFunctionOf" and "EnDiffWeights" on the other: + EnAsFunctionOf afoVar = eAsFunctionOf_N; // this local variable determines the enum "EnAsFunctionOf" which corresponds to enum "EnqvectorKine" + EnDiffWeights afoDiffWeight = eDiffWeights_N; // this local variable determines the enum "EnDiffWeights" which corresponds to enum "EnqvectorKine" if (variableX == PTq) { - AFO_var = AFO_PT; - AFO_diffWeight = wPHIPT; + afoVar = AFO_PT; + afoDiffWeight = wPHIPT; } else if (variableX == ETAq) { - AFO_var = AFO_ETA; - AFO_diffWeight = wPHIETA; + afoVar = AFO_ETA; + afoDiffWeight = wPHIETA; } // *) Insanity checks on above settings: - if (AFO_var == eAsFunctionOf_N) { - LOGF(fatal, "\033[1;31m%s at line %d : AFO_var == eAsFunctionOf_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); + if (afoVar == eAsFunctionOf_N) { + LOGF(fatal, "\033[1;31m%s at line %d : afoVar == eAsFunctionOf_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); } - if (AFO_diffWeight == eDiffWeights_N) { - LOGF(fatal, "\033[1;31m%s at line %d : AFO_diffWeight == eDiffWeights_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); + if (afoDiffWeight == eDiffWeights_N) { + LOGF(fatal, "\033[1;31m%s at line %d : afoDiffWeight == eDiffWeights_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); } // *) Determine first to which bin the 'valueX' corresponds to. // Based on that, I decide from which histogram I fetch weight for y. See MakeWeights.C - int binX = res.fResultsPro[AFO_var]->FindBin(valueX); + int binX = res.fResultsPro[afoVar]->FindBin(valueX); if (tc.fInsanityCheckForEachParticle) // enable only during debugging, as this check is computationally heavy. { if (binX < 1) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // underflow. this means that I didn't use the same cuts now, and I was using when making the particle weights. Adjust the cuts. } - if (binX > res.fResultsPro[AFO_var]->GetNbinsX()) { + if (binX > res.fResultsPro[afoVar]->GetNbinsX()) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // overflow. this means that I didn't use the same cuts now, and I was using when making the particle weights. Adjust the cuts. } } // if(tc.fInsanityCheckForEachParticle) // *) Finally, determine weight for y(x): - if (!pw.fDiffWeightsHist[AFO_diffWeight][binX - 1]) { + if (!pw.fDiffWeightsHist[afoDiffWeight][binX - 1]) { LOGF(info, "\033[1;32mvalueY = %f\033[0m", valueY); LOGF(info, "\033[1;32mvalueX = %f\033[0m", valueX); LOGF(info, "\033[1;32mvariableX = %d\033[0m", static_cast(variableX)); - LOGF(info, "\033[1;32mAFO_diffWeight = %d\033[0m", static_cast(AFO_diffWeight)); + LOGF(info, "\033[1;32mAFO_diffWeight = %d\033[0m", static_cast(afoDiffWeight)); LOGF(info, "\033[1;32mbinX = %d\033[0m", binX); LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - int bin = pw.fDiffWeightsHist[AFO_diffWeight][binX - 1]->FindBin(valueY); // binX - 1, because I histogram for first bin in X is labeled with "[0]", etc. - if (tc.fInsanityCheckForEachParticle) // enable only during debugging, as this check is computationally heavy. + int bin = pw.fDiffWeightsHist[afoDiffWeight][binX - 1]->FindBin(valueY); // binX - 1, because I histogram for first bin in X is labeled with "[0]", etc. + if (tc.fInsanityCheckForEachParticle) // enable only during debugging, as this check is computationally heavy. { if (bin < 1) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // underflow. this means that I didn't use the same cuts now, and I was using when making the particle weights. Adjust the cuts. } - if (bin > pw.fDiffWeightsHist[AFO_diffWeight][binX - 1]->GetNbinsX()) { + if (bin > pw.fDiffWeightsHist[afoDiffWeight][binX - 1]->GetNbinsX()) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); // overflow. this means that I didn't use the same cuts now, and I was using when making the particle weights. Adjust the cuts. } } // if(tc.fInsanityCheckForEachParticle) - double diffWeight = pw.fDiffWeightsHist[AFO_diffWeight][binX - 1]->GetBinContent(bin); + double diffWeight = pw.fDiffWeightsHist[afoDiffWeight][binX - 1]->GetBinContent(bin); if (tc.fInsanityCheckForEachParticle) // enable only during debugging, as this check is computationally heavy. { if (diffWeight < 0.) { // or <= 0 ? TBI 20240324 rethink @@ -16263,16 +16373,16 @@ double DiffWeight(const double& valueY, const double& valueX, eqvectorKine varia } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return diffWeight; -} // DiffWeight(const double &valueY, const double &valueX, eqvectorKine variableX) +} // diffWeight(const double &valueY, const double &valueX, EnqvectorKine variableX) //============================================================ -void GetParticleWeights() +void getParticleWeights() { // Get the particle weights. Call this function only once. @@ -16289,35 +16399,36 @@ void GetParticleWeights() // f) Differential charge weights using sparse histograms. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Integrated weights: // integrated phi weights: if (pw.fUseWeights[wPHI]) { - TH1D* phiWeights = GetHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "phi"); + TH1D* phiWeights = getHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "phi"); if (!phiWeights) { LOGF(fatal, "in function \033[1;31m%s at line %d, phiWeights is NULL. Check the external file %s with particle weights\033[0m", __FUNCTION__, __LINE__, pw.fFileWithWeights.Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } - SetWeightsHist(phiWeights, wPHI); + setWeightsHist(phiWeights, wPHI); } // integrated pt weights: if (pw.fUseWeights[wPT]) { - TH1D* ptWeights = GetHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "pt"); + TH1D* ptWeights = getHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "pt"); if (!ptWeights) { LOGF(fatal, "\033[1;31m%s at line %d : ptWeights is NULL. Check the external file %s with particle weights\033[0m", __FUNCTION__, __LINE__, pw.fFileWithWeights.Data()); } - SetWeightsHist(ptWeights, wPT); + setWeightsHist(ptWeights, wPT); } // integrated eta weights: if (pw.fUseWeights[wETA]) { - TH1D* etaWeights = GetHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "eta"); + TH1D* etaWeights = getHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "eta"); if (!etaWeights) { LOGF(fatal, "\033[1;31m%s at line %d : etaWeights is NULL. Check the external file %s with particle weights\033[0m", __FUNCTION__, __LINE__, pw.fFileWithWeights.Data()); } - SetWeightsHist(etaWeights, wETA); + setWeightsHist(etaWeights, wETA); } // b) Differential weights: @@ -16340,13 +16451,14 @@ void GetParticleWeights() } // *) okay, this pt bin is within pt phase-space window, defined by pt cut: - phiptWeights = GetHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "phipt", b); + phiptWeights = getHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "phipt", b); if (!phiptWeights) { LOGF(fatal, "\033[1;31m%s at line %d : phiptWeights is NULL. Check the external file %s with particle weights\033[0m", __FUNCTION__, __LINE__, pw.fFileWithWeights.Data()); + exit(__LINE__); // TBI 20260714 temporarily here to silent MegaLinter } // *) okay, just use this histogram with weights: - SetDiffWeightsHist(phiptWeights, wPHIPT, b); + setDiffWeightsHist(phiptWeights, wPHIPT, b); } } // if (pw.fUseDiffWeights[wPHIPT]) { @@ -16369,13 +16481,13 @@ void GetParticleWeights() } // *) okay, this eta bin is within eta phase-space window, defined by eta cut: - phietaWeights = GetHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "phieta", b); + phietaWeights = getHistogramWithWeights(pw.fFileWithWeights.Data(), tc.fRunNumber.Data(), "phieta", b); if (!phietaWeights) { LOGF(fatal, "\033[1;31m%s at line %d : phietaWeights is NULL. Check the external file %s with particle weights\033[0m", __FUNCTION__, __LINE__, pw.fFileWithWeights.Data()); } // *) okay, just use this histogram with weights: - SetDiffWeightsHist(phietaWeights, wPHIETA, b); + setDiffWeightsHist(phietaWeights, wPHIETA, b); } // for(int b=0; bGetAt(i1); double dW1 = nl.ftaNestedLoops[1]->GetAt(i1); - if (1 == order) { + if (eOrder_1 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1); weight = dW1; profile->Fill(0.5, value, weight); @@ -16863,7 +16985,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi2 = nl.ftaNestedLoops[0]->GetAt(i2); double dW2 = nl.ftaNestedLoops[1]->GetAt(i2); - if (2 == order) { + if (eOrder_2 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2); weight = dW1 * dW2; profile->Fill(0.5, value, weight); @@ -16875,7 +16997,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi3 = nl.ftaNestedLoops[0]->GetAt(i3); double dW3 = nl.ftaNestedLoops[1]->GetAt(i3); - if (3 == order) { + if (eOrder_3 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3); weight = dW1 * dW2 * dW3; profile->Fill(0.5, value, weight); @@ -16887,7 +17009,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi4 = nl.ftaNestedLoops[0]->GetAt(i4); double dW4 = nl.ftaNestedLoops[1]->GetAt(i4); - if (4 == order) { + if (eOrder_4 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4); weight = dW1 * dW2 * dW3 * dW4; profile->Fill(0.5, value, weight); @@ -16899,7 +17021,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi5 = nl.ftaNestedLoops[0]->GetAt(i5); double dW5 = nl.ftaNestedLoops[1]->GetAt(i5); - if (5 == order) { + if (eOrder_5 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5); weight = dW1 * dW2 * dW3 * dW4 * dW5; profile->Fill(0.5, value, weight); @@ -16911,7 +17033,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi6 = nl.ftaNestedLoops[0]->GetAt(i6); double dW6 = nl.ftaNestedLoops[1]->GetAt(i6); - if (6 == order) { + if (eOrder_6 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6; profile->Fill(0.5, value, weight); @@ -16923,7 +17045,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi7 = nl.ftaNestedLoops[0]->GetAt(i7); double dW7 = nl.ftaNestedLoops[1]->GetAt(i7); - if (7 == order) { + if (eOrder_7 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7; profile->Fill(0.5, value, weight); @@ -16935,7 +17057,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi8 = nl.ftaNestedLoops[0]->GetAt(i8); double dW8 = nl.ftaNestedLoops[1]->GetAt(i8); - if (8 == order) { + if (eOrder_8 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8; profile->Fill(0.5, value, weight); @@ -16947,7 +17069,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi9 = nl.ftaNestedLoops[0]->GetAt(i9); double dW9 = nl.ftaNestedLoops[1]->GetAt(i9); - if (9 == order) { + if (eOrder_9 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9; profile->Fill(0.5, value, weight); @@ -16959,7 +17081,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi10 = nl.ftaNestedLoops[0]->GetAt(i10); double dW10 = nl.ftaNestedLoops[1]->GetAt(i10); - if (10 == order) { + if (eOrder_10 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10; profile->Fill(0.5, value, weight); @@ -16971,7 +17093,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi11 = nl.ftaNestedLoops[0]->GetAt(i11); double dW11 = nl.ftaNestedLoops[1]->GetAt(i11); - if (11 == order) { + if (eOrder_11 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10 + harmonics->GetAt(10) * dPhi11); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10 * dW11; profile->Fill(0.5, value, weight); @@ -16983,7 +17105,7 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) } double dPhi12 = nl.ftaNestedLoops[0]->GetAt(i12); double dW12 = nl.ftaNestedLoops[1]->GetAt(i12); - if (12 == order) { + if (eOrder_12 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10 + harmonics->GetAt(10) * dPhi11 + harmonics->GetAt(11) * dPhi12); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10 * dW11 * dW12; profile->Fill(0.5, value, weight); @@ -17010,19 +17132,19 @@ double CalculateCustomNestedLoops(TArrayI* harmonics) delete profile; profile = NULL; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return finalValue; -} // double CalculateCustomNestedLoops(TArrayI *harmonics) +} // double calculateCustomNestedLoops(TArrayI *harmonics) //============================================================ -double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_variable, int bin) +double calculateKineCustomNestedLoops(TArrayI* harmonics, EnAsFunctionOf AFO_variable, int bin) { // !!! OBSOLETE FUNCTION !!! - LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250529, this is an obsolete function, use double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarChoice, int bin) instead !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); + LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250529, this is an obsolete function, use double calculateKineCustomNestedLoops(TArrayI* harmonics, EnqvectorKine kineVarChoice, int bin) instead !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); // For the specified harmonics, kine variable, and bin, get the correlation from nested loops. // Order of correlator is the number of harmonics, i.e. the number of elements in an array. @@ -17032,7 +17154,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari // c) Return value. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if (!harmonics) { @@ -17040,7 +17162,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } // *) ... - eqvectorKine qvKine = eqvectorKine_N; // which component of q-vector + EnqvectorKine qvKine = eqvectorKine_N; // which component of q-vector TString kineVarName = ""; switch (AFO_variable) { case AFO_PT: { @@ -17082,7 +17204,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } } - // 'qvKine' is enum eqvectorKine: + // 'qvKine' is enum EnqvectorKine: if (!res.fResultsPro[AFO_variable]) { LOGF(fatal, "\033[1;31m%s at line %d : AFO_variable = %d, bin = %d \033[0m", __FUNCTION__, __LINE__, static_cast(AFO_variable), bin); } @@ -17111,7 +17233,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari for (int i1 = 0; i1 < nParticles; i1++) { double dPhi1 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i1); double dW1 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i1); - if (1 == order) { + if (eOrder_1 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1); weight = dW1; profile->Fill(0.5, value, weight); @@ -17123,7 +17245,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi2 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i2); double dW2 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i2); - if (2 == order) { + if (eOrder_2 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2); weight = dW1 * dW2; profile->Fill(0.5, value, weight); @@ -17135,7 +17257,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi3 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i3); double dW3 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i3); - if (3 == order) { + if (eOrder_3 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3); weight = dW1 * dW2 * dW3; profile->Fill(0.5, value, weight); @@ -17147,7 +17269,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi4 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i4); double dW4 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i4); - if (4 == order) { + if (eOrder_4 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4); weight = dW1 * dW2 * dW3 * dW4; profile->Fill(0.5, value, weight); @@ -17159,7 +17281,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi5 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i5); double dW5 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i5); - if (5 == order) { + if (eOrder_5 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5); weight = dW1 * dW2 * dW3 * dW4 * dW5; profile->Fill(0.5, value, weight); @@ -17171,7 +17293,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi6 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i6); double dW6 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i6); - if (6 == order) { + if (eOrder_6 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6; profile->Fill(0.5, value, weight); @@ -17183,7 +17305,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi7 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i7); double dW7 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i7); - if (7 == order) { + if (eOrder_7 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7; profile->Fill(0.5, value, weight); @@ -17195,7 +17317,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi8 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i8); double dW8 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i8); - if (8 == order) { + if (eOrder_8 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8; profile->Fill(0.5, value, weight); @@ -17207,7 +17329,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi9 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i9); double dW9 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i9); - if (9 == order) { + if (eOrder_9 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9; profile->Fill(0.5, value, weight); @@ -17219,7 +17341,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi10 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i10); double dW10 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i10); - if (10 == order) { + if (eOrder_10 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10; profile->Fill(0.5, value, weight); @@ -17231,7 +17353,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi11 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i11); double dW11 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i11); - if (11 == order) { + if (eOrder_11 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10 + harmonics->GetAt(10) * dPhi11); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10 * dW11; profile->Fill(0.5, value, weight); @@ -17243,7 +17365,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari } double dPhi12 = nl.ftaNestedLoopsKine[qvKine][bin][0]->GetAt(i12); double dW12 = nl.ftaNestedLoopsKine[qvKine][bin][1]->GetAt(i12); - if (12 == order) { + if (eOrder_12 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10 + harmonics->GetAt(10) * dPhi11 + harmonics->GetAt(11) * dPhi12); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10 * dW11 * dW12; profile->Fill(0.5, value, weight); @@ -17270,15 +17392,15 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eAsFunctionOf AFO_vari delete profile; profile = NULL; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return finalValue; -} // double CalculateKineCustomNestedLoops(TArrayI *harmonics, eAsFunctionOf AFO_variable, int bin) +} // double calculateKineCustomNestedLoops(TArrayI *harmonics, EnAsFunctionOf AFO_variable, int bin) //============================================================ -double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarChoice, int bin) +double calculateKineCustomNestedLoops(TArrayI* harmonics, EnqvectorKine kineVarChoice, int bin) { // For the specified harmonics, kine variable, and bin, get the correlation from nested loops. // Order of correlator is the number of harmonics, i.e. the number of elements in an array. @@ -17288,7 +17410,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh // c) Return value. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if (!harmonics) { @@ -17308,7 +17430,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh // It doesn't hurt to do here insanity check on the number of particles, in case I have already calculated it independently when calculating diff. q-vectors: if (qv.fCalculateqvectorsKineAny && qv.fqvectorEntries[kineVarChoice][bin] > 0) { // TBI 20250602 I think this chained condition is ok, but re-think nevertheless if (!(nParticles == qv.fqvectorEntries[kineVarChoice][bin])) { - LOGF(fatal, "\033[1;31m%s at line %d : nParticles = %d, qv.fqvectorEntries[kineVarChoice][bin] = %d, kineVarChoices = %d (%s), bin = %d\033[0m", __FUNCTION__, __LINE__, nParticles, qv.fqvectorEntries[kineVarChoice][bin], static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), bin); + LOGF(fatal, "\033[1;31m%s at line %d : nParticles = %d, qv.fqvectorEntries[kineVarChoice][bin] = %d, kineVarChoices = %d (%s), bin = %d\033[0m", __FUNCTION__, __LINE__, nParticles, qv.fqvectorEntries[kineVarChoice][bin], static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), bin); } } @@ -17334,7 +17456,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh for (int i1 = 0; i1 < nParticles; i1++) { double dPhi1 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i1); double dW1 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i1); - if (1 == order) { + if (eOrder_1 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1); weight = dW1; profile->Fill(0.5, value, weight); @@ -17346,7 +17468,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi2 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i2); double dW2 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i2); - if (2 == order) { + if (eOrder_2 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2); weight = dW1 * dW2; profile->Fill(0.5, value, weight); @@ -17358,7 +17480,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi3 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i3); double dW3 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i3); - if (3 == order) { + if (eOrder_3 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3); weight = dW1 * dW2 * dW3; profile->Fill(0.5, value, weight); @@ -17370,7 +17492,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi4 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i4); double dW4 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i4); - if (4 == order) { + if (eOrder_4 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4); weight = dW1 * dW2 * dW3 * dW4; profile->Fill(0.5, value, weight); @@ -17382,7 +17504,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi5 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i5); double dW5 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i5); - if (5 == order) { + if (eOrder_5 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5); weight = dW1 * dW2 * dW3 * dW4 * dW5; profile->Fill(0.5, value, weight); @@ -17394,7 +17516,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi6 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i6); double dW6 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i6); - if (6 == order) { + if (eOrder_6 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6; profile->Fill(0.5, value, weight); @@ -17406,7 +17528,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi7 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i7); double dW7 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i7); - if (7 == order) { + if (eOrder_7 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7; profile->Fill(0.5, value, weight); @@ -17418,7 +17540,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi8 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i8); double dW8 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i8); - if (8 == order) { + if (eOrder_8 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8; profile->Fill(0.5, value, weight); @@ -17430,7 +17552,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi9 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i9); double dW9 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i9); - if (9 == order) { + if (eOrder_9 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9; profile->Fill(0.5, value, weight); @@ -17442,7 +17564,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi10 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i10); double dW10 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i10); - if (10 == order) { + if (eOrder_10 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10; profile->Fill(0.5, value, weight); @@ -17454,7 +17576,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi11 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i11); double dW11 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i11); - if (11 == order) { + if (eOrder_11 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10 + harmonics->GetAt(10) * dPhi11); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10 * dW11; profile->Fill(0.5, value, weight); @@ -17466,7 +17588,7 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh } double dPhi12 = nl.ftaNestedLoopsKine[kineVarChoice][bin][0]->GetAt(i12); double dW12 = nl.ftaNestedLoopsKine[kineVarChoice][bin][1]->GetAt(i12); - if (12 == order) { + if (eOrder_12 == order) { value = std::cos(harmonics->GetAt(0) * dPhi1 + harmonics->GetAt(1) * dPhi2 + harmonics->GetAt(2) * dPhi3 + harmonics->GetAt(3) * dPhi4 + harmonics->GetAt(4) * dPhi5 + harmonics->GetAt(5) * dPhi6 + harmonics->GetAt(6) * dPhi7 + harmonics->GetAt(7) * dPhi8 + harmonics->GetAt(8) * dPhi9 + harmonics->GetAt(9) * dPhi10 + harmonics->GetAt(10) * dPhi11 + harmonics->GetAt(11) * dPhi12); weight = dW1 * dW2 * dW3 * dW4 * dW5 * dW6 * dW7 * dW8 * dW9 * dW10 * dW11 * dW12; profile->Fill(0.5, value, weight); @@ -17493,20 +17615,20 @@ double CalculateKineCustomNestedLoops(TArrayI* harmonics, eqvectorKine kineVarCh delete profile; profile = NULL; if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return finalValue; -} // double CalculateKineCustomNestedLoops(TArrayI *harmonics, eAsFunctionOf AFO_variable, int bin) +} // double calculateKineCustomNestedLoops(TArrayI *harmonics, EnAsFunctionOf AFO_variable, int bin) //============================================================ -void DetermineMultiplicity() +void determineMultiplicity() { // Determine multiplicity for "vs. mult" results. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if (ec.fsEventCuts[eMultiplicityEstimator].EqualTo("SelectedTracks", TString::kIgnoreCase)) { @@ -17518,15 +17640,15 @@ void DetermineMultiplicity() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void DetermineMultiplicity() +} // void determineMultiplicity() //============================================================ -template -void DetermineReferenceMultiplicity(T const& collision) +template +void determineReferenceMultiplicity(T const& collision) { // Determine collision reference multiplicity. @@ -17538,7 +17660,7 @@ void DetermineReferenceMultiplicity(T const& collision) // f) Print reference multiplicity for the audience... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Determine reference multiplicity for real Run 3 data: @@ -17607,15 +17729,15 @@ void DetermineReferenceMultiplicity(T const& collision) // f) Print centrality for the audience...: if (tc.fVerbose) { LOGF(info, "\033[1;32m ebye.fReferenceMultiplicity = %f\033[0m", ebye.fReferenceMultiplicity); - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void DetermineReferenceMultiplicity(T const& collision) +} // template void determineReferenceMultiplicity(T const& collision) //============================================================ -template -void DetermineCentrality(T const& collision) +template +void determineCentrality(T const& collision) { // Determine collision centrality. // For simulated data, I determine ebye.ImpactParameter here as well. @@ -17632,7 +17754,7 @@ void DetermineCentrality(T const& collision) // h) Print centrality for the audience... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) For real data, determine centrality from default centrality estimator: @@ -17669,10 +17791,10 @@ void DetermineCentrality(T const& collision) if constexpr (rs == eRecAndSim) { // *) Impact parameter: - ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before DetermineSimulatedCentrality(); + ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before determineSimulatedCentrality(); // *) Centrality for simulated data in Run 3: - ebye.fCentralitySim = DetermineSimulatedCentrality(); + ebye.fCentralitySim = determineSimulatedCentrality(); // ... @@ -17682,8 +17804,8 @@ void DetermineCentrality(T const& collision) // b) For simulated data, determine centrality directly from impact parameter + store impact parameter: if constexpr (rs == eSim) { - ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before DetermineSimulatedCentrality(); - ebye.fCentrality = DetermineSimulatedCentrality(); // yes, I use here ebye.fCentrality, not ebye.fCentralitySim + ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before determineSimulatedCentrality(); + ebye.fCentrality = determineSimulatedCentrality(); // yes, I use here ebye.fCentrality, not ebye.fCentralitySim } // c) Same as a), just for converted Run 2 data: @@ -17708,10 +17830,10 @@ void DetermineCentrality(T const& collision) if constexpr (rs == eRecAndSim_Run2) { // *) Impact parameter: - ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before DetermineSimulatedCentrality(); + ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before determineSimulatedCentrality(); // *) Centrality for simulated data in Run 3: - ebye.fCentralitySim = DetermineSimulatedCentrality(); + ebye.fCentralitySim = determineSimulatedCentrality(); // ... @@ -17720,8 +17842,8 @@ void DetermineCentrality(T const& collision) // d) Same as b), just for converted Run 2 data: if constexpr (rs == eSim_Run2) { - ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before DetermineSimulatedCentrality(); - ebye.fCentrality = DetermineSimulatedCentrality(); // yes, I use here ebye.fCentrality, not ebye.fCentralitySim + ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before determineSimulatedCentrality(); + ebye.fCentrality = determineSimulatedCentrality(); // yes, I use here ebye.fCentrality, not ebye.fCentralitySim } // e) Same as a), just for converted Run 1 data: @@ -17739,10 +17861,10 @@ void DetermineCentrality(T const& collision) if constexpr (rs == eRecAndSim_Run1) { // *) Impact parameter: - ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before DetermineSimulatedCentrality(); + ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before determineSimulatedCentrality(); // *) Centrality for simulated data in Run 3: - ebye.fCentralitySim = DetermineSimulatedCentrality(); + ebye.fCentralitySim = determineSimulatedCentrality(); // ... @@ -17751,8 +17873,8 @@ void DetermineCentrality(T const& collision) // f) Same as b), just for converted Run 1 data: if constexpr (rs == eSim_Run1) { - ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before DetermineSimulatedCentrality(); - ebye.fCentrality = DetermineSimulatedCentrality(); // yes, I use here ebye.fCentrality, not ebye.fCentralitySim + ebye.fImpactParameter = collision.mcCollision().impactParameter(); // has to be called before determineSimulatedCentrality(); + ebye.fCentrality = determineSimulatedCentrality(); // yes, I use here ebye.fCentrality, not ebye.fCentralitySim } // g) Test case: @@ -17764,17 +17886,17 @@ void DetermineCentrality(T const& collision) if (tc.fVerbose) { LOGF(info, "\033[1;32m ebye.fCentrality = %f\033[0m", ebye.fCentrality); LOGF(info, "\033[1;32m ebye.fCentralitySim = %f\033[0m", ebye.fCentralitySim); - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void DetermineCentrality(T const& collision) +} // template void determineCentrality(T const& collision) //============================================================ -float DetermineSimulatedCentrality() +float determineSimulatedCentrality() { // Determine centrality at generated/simulated level, just using impact parameter. - // This is a helper function for DetermineCentrality(), to reduce the code bloat there. I do not anticipate calling this function anywhere alse at the moment. + // This is a helper function for determineCentrality(), to reduce the code bloat there. I do not anticipate calling this function anywhere alse at the moment. // Algorithm: // 1. Ideally, I simply fetch this centrality from the table HepMCHeavyIons via getter .centrality(); @@ -17782,7 +17904,7 @@ float DetermineSimulatedCentrality() // 3. From process switches I can see whether I am processing Run 3, Run 2 or Run 1 data, for collision system I support at the moment only Pb+Pb. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } float lSimulatedCentrality = -1.; @@ -17806,10 +17928,10 @@ float DetermineSimulatedCentrality() if (tc.fProcess[eProcessRecSim] || tc.fProcess[eSim]) { // Pb+Pb in Run 3: lSigmaInel = 7.71; // interpolation, see Slide 3 in DDC presentation https://indico.cern.ch/event/1326916/ - } else if (tc.fProcess[eProcessRecSim_Run2] || tc.fProcess[eSim_Run2]) { + } else if (tc.fProcess[eProcessRecSimRun2] || tc.fProcess[eSim_Run2]) { // Pb+Pb in Run 2: lSigmaInel = 7.67; // for 5.02 TeV, see Slide 3 in DDC presentation https://indico.cern.ch/event/1326916/ + Run 2 paper https://arxiv.org/abs/2204.10148 - } else if (tc.fProcess[eProcessRecSim_Run1] || tc.fProcess[eSim_Run1]) { + } else if (tc.fProcess[eProcessRecSimRun1] || tc.fProcess[eSim_Run1]) { // Pb+Pb in Run 1: lSigmaInel = 7.55; // see Slide 3 in DDC presentation https://indico.cern.ch/event/1326916/ + Run 1 multiplicity paper https://arxiv.org/abs/1301.4361 } else { @@ -17819,7 +17941,7 @@ float DetermineSimulatedCentrality() // okay, I have SigmaInel for this collision system and energy, calculate centrality: float b = ebye.fImpactParameter; if (b < 0.) { - LOGF(warning, "\033[1;31m%s at line %d : b < 0. => did you forget to calculate ebye.fImpactParameter before calling DetermineSimulatedCentrality() ? Or you are processing Monte Carlo dataset where IP was not stored, i.e. it's set to -999 (e.g. in LHC21i6a) ? Setting lSimulatedCentrality = -1. and simply continuing... \033[0m", __FUNCTION__, __LINE__); + LOGF(warning, "\033[1;31m%s at line %d : b < 0. => did you forget to calculate ebye.fImpactParameter before calling determineSimulatedCentrality() ? Or you are processing Monte Carlo dataset where IP was not stored, i.e. it's set to -999 (e.g. in LHC21i6a) ? Setting lSimulatedCentrality = -1. and simply continuing... \033[0m", __FUNCTION__, __LINE__); lSimulatedCentrality = -1.; } else { lSimulatedCentrality = o2::constants::math::PI * std::pow(b, 2.) / lSigmaInel; // finally, calculate true simulated centrality directly from impact parameter @@ -17829,17 +17951,17 @@ float DetermineSimulatedCentrality() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return lSimulatedCentrality; -} // float DetermineSimulatedCentrality() +} // float determineSimulatedCentrality() //============================================================ -template -void DetermineOccupancy(T const& collision) +template +void determineOccupancy(T const& collision) { // Determine collision occupancy. @@ -17848,7 +17970,7 @@ void DetermineOccupancy(T const& collision) // c) Print occupancy for the audience... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Determine occupancy from default occupancy estimator, only for eRec and eRecAndSim: @@ -17879,15 +18001,15 @@ void DetermineOccupancy(T const& collision) // c) Print occupancy for the audience...: if (tc.fVerbose) { LOGF(info, "\033[1;32m ebye.fOccupancy = %f\033[0m", ebye.fOccupancy); - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void DetermineOccupancy(T const& collision) +} // template void determineOccupancy(T const& collision) //============================================================ -template -void DetermineInteractionRateAndCurrentRunDuration(T1 const& collision, T2 const&) +template +void determineInteractionRateAndCurrentRunDuration(T1 const& collision, T2 const&) { // Determine interaction rate and current run duration in Run 3. @@ -17900,10 +18022,10 @@ void DetermineInteractionRateAndCurrentRunDuration(T1 const& collision, T2 const // c) Print interaction rate and current run duration for the audience... if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } - if constexpr (rs == eRec || rs == eQA) { // TBI 20250112 check still eRecSim mode here + if constexpr (rs == eRec || rs == eQA) { // TBI 20250112 check still EnRecSim mode here auto bc = collision.template foundBC_as(); // I have the same code snippet at other places, keep in sync. // a1) Determine interaction rate only for eRec: @@ -17939,39 +18061,39 @@ void DetermineInteractionRateAndCurrentRunDuration(T1 const& collision, T2 const // c) Print interaction rate and run duration for the audience...: if (tc.fVerbose) { LOGF(info, "\033[1;32m ebye.fInteractionRate = %f kHz\033[0m", ebye.fInteractionRate); - if (qa.fBookQAEventHistograms2D[eCurrentRunDuration_vs_InteractionRate]) { // TBI 20241127 do I check this flag, or pointer, like in FillEventHistograms(...) ? + if (qa.fBookQAEventHistograms2D[eCurrentRunDuration_vs_InteractionRate]) { // TBI 20241127 do I check this flag, or pointer, like in fillEventHistograms(...) ? LOGF(info, "\033[1;32m ebye.fCurrentRunDuration = %f s (in seconds after SOR)\033[0m", ebye.fCurrentRunDuration); } - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void DetermineInteractionRateAndCurrentRunDuration(T1 const& collision, T2 const&) +} // template void determineInteractionRateAndCurrentRunDuration(T1 const& collision, T2 const&) //============================================================ -template -void DetermineVertexZ(T const& collision) +template +void determineVertexZ(T const& collision) { // Determine vetex z position. // TBI 20250108 I could use ebye.fVz determined here to fill event histograms, but it's not a big deal to fetch it there also via collision.posZ() if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } ebye.fVz = collision.posZ(); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void DetermineVertexZ(T const& collision) +} // void determineVertexZ(T const& collision) //============================================================ -template -void DetermineQAThingies(T1 const& collision, T2 const&) +template +void determineQAThingies(T1 const& collision, T2 const&) { // Remark: I implement ideally here only the getters for which the subscription to additional non-standard tables was needed for QA purposes. // Support only for Run 3 data is provided, because in the "processQA" switch the starting point are tables used in "processRec", and I join to them @@ -17982,7 +18104,7 @@ void DetermineQAThingies(T1 const& collision, T2 const&) // *) Print something for the audience... . if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if constexpr (rs == eQA) { @@ -18006,14 +18128,14 @@ void DetermineQAThingies(T1 const& collision, T2 const&) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } // if (tc.fVerbose) { -} // template void DetermineQAThingies(T1 const& collision, T2 const&) +} // template void determineQAThingies(T1 const& collision, T2 const&) //============================================================ -void ProcessHepMCHeavyIons(aod::HepMCHeavyIon const& hep) +void processHepMCHeavyIons(aod::HepMCHeavyIon const& hep) { // Process extra MC info from HepMCHeavyIons only in this function. // See documentation at https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo @@ -18027,22 +18149,24 @@ void ProcessHepMCHeavyIons(aod::HepMCHeavyIon const& hep) // *) Centrality at generated level: ebye.fCentralitySim = hep.centrality(); - if (ebye.fCentralitySim < 0. || ebye.fCentralitySim > 100.) { + float lCentMin = 0.; + float lCentMax = 100.; + if (ebye.fCentralitySim < lCentMin || ebye.fCentralitySim > lCentMax) { LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! ebye.fCentralitySim = %f, this info is still not stored in the table HepMCHeavyIons !!!! WARNING !!!! \033[0m\n", __FUNCTION__, __LINE__, ebye.fCentralitySim); } // ... TBI 20240429 as soon as HepMCHeavyIons data is stored filled in Monte Carlo datasets, ctd. here -} // void ProcessHepMCHeavyIons(aod::HepMCHeavyIon const& hep) +} // void processHepMCHeavyIons(aod::HepMCHeavyIon const& hep) //============================================================ -void DetermineEventCounters() +void determineEventCounters() { // Determine all event counters. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // Remark: For "RecSim", the total number of events is taken from eRec. @@ -18056,19 +18180,19 @@ void DetermineEventCounters() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void DetermineEventCounters() +} // void determineEventCounters() //============================================================ -void RandomIndices(int nTracks) +void randomIndices(int nTracks) { // Randomize indices using Fisher-Yates algorithm. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } if (nTracks < 1) { @@ -18089,36 +18213,36 @@ void RandomIndices(int nTracks) } // end of for(int i=nTracks-1;i>=1;i--) if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void RandomIndices(int nTracks) +} // void randomIndices(int nTracks) //============================================================ -template -void BanishmentLoopOverParticles(T const& tracks) +template +void banishmentLoopOverParticles(T const& tracks) { // This is the quick banishment loop over particles, as a support for eSelectedTracks cut (used through eMultiplicity, see comments for ebye.fMultiplicity). // This is particularly relevant to get all efficiency corrections right. - // The underlying problem is that particle histograms got filled before eSelectedTracks could be applied in Steer. + // The underlying problem is that particle histograms got filled before eSelectedTracks could be applied in steer. // Therefore, particle histograms got filled even for events which were rejected by eSelectedTracks cut. // In this loop, for those few specific events (typically low-multiplicity outliers), particle histograms are re-filled again with weight -1, - // which in effect cancels the previos fill in the MainLoopOverParticles. + // which in effect cancels the previos fill in the mainLoopOverParticles. - // Remark: I have to use here all additional checks, like ValidTrack, as in the MainLoopOverParticles. + // Remark: I have to use here all additional checks, like validTrack, as in the mainLoopOverParticles. // Therefore, it's important to have local variable lSelectedTracks, so that I can cross-compare // at the end with central data member ebye.fSelectedTracks if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) If random access of tracks from collection is requested, use Fisher-Yates algorithm to generate random indices: // Remark: It is very important that I use exactly the same random sequence from FY already generated in the MainLoop if (tc.fUseFisherYates) { if (!tc.fRandomIndices) { - LOGF(fatal, "\033[1;31m%s at line %d : I have to use here exactly the same random sequence from FY already generated in the MainLoopOverParticles, but it's not available \033[0m", __FUNCTION__, __LINE__); + LOGF(fatal, "\033[1;31m%s at line %d : I have to use here exactly the same random sequence from FY already generated in the mainLoopOverParticles, but it's not available \033[0m", __FUNCTION__, __LINE__); } } @@ -18138,7 +18262,7 @@ void BanishmentLoopOverParticles(T const& tracks) } // *) Skip track objects which are not valid tracks (e.g. Run 2 and 1 tracklets, etc.): - if (!ValidTrack(track)) { + if (!validTrack(track)) { continue; } @@ -18147,17 +18271,17 @@ void BanishmentLoopOverParticles(T const& tracks) // If I start to banish here also particle histograms BEFORE particle cuts, then see if I have to do it also for event histograms BEFORE cuts. // Event histograms AFTER cuts are not affected. // if (ph.fFillParticleHistograms || ph.fFillParticleHistograms2D) { - // FillParticleHistograms(track, eBefore, -1); + // fillParticleHistograms(track, eBefore, -1); // } // *) Particle cuts: - if (!ParticleCuts(track, eCut)) { // Main call for particle cuts. + if (!particleCuts(track, eCut)) { // Main call for particle cuts. continue; // not return!! } // *) Banish particle histograms after particle cuts: if (ph.fFillParticleHistograms || ph.fFillParticleHistograms2D || qa.fFillQAParticleHistograms2D) { - FillParticleHistograms(track, eAfter, -1); // with negative weight -1, I effectively remove the previous fill for this track + fillParticleHistograms(track, eAfter, -1); // with negative weight -1, I effectively remove the previous fill for this track } // *) Increase the local selected particle counter: @@ -18180,14 +18304,14 @@ void BanishmentLoopOverParticles(T const& tracks) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void BanishmentLoopOverParticles(T const& tracks) { +} // template void banishmentLoopOverParticles(T const& tracks) { //============================================================ -void PrintCutCounterContent() +void printCutCounterContent() { // Prints on the screen content of fEventCutCounterHist[][] (all which were booked). @@ -18195,7 +18319,7 @@ void PrintCutCounterContent() // b) Print or die. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Insanity checks: @@ -18204,7 +18328,7 @@ void PrintCutCounterContent() } // b) Print or die: - for (int rs = 0; rs < 2; rs++) // reco/sim + for (int rs = 0; rs < grsN; rs++) // reco/sim { for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter { @@ -18219,57 +18343,57 @@ void PrintCutCounterContent() LOGF(info, "bin = %d => %s : %d", bin, ec.fEventCutCounterHist[rs][cc]->GetXaxis()->GetBinLabel(bin), static_cast(ec.fEventCutCounterHist[rs][cc]->GetBinContent(bin))); } } // for (int cc = 0; cc < eCutCounter_N; cc++) // enum eCutCounter - } // for (int rs = 0; rs < 2; rs++) // reco/sim + } // for (int rs = 0; rs < grsN; rs++) // reco/sim if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void PrintCutCounterContent() +} // void printCutCounterContent() //============================================================ -void Trace(const char* functionName, int lineNumber) +void trace(const char* functionName, int lineNumber) { // A simple utility wrapper. Use only during debugging, sprinkle calls to this function here and there, as follows - // Trace(__FUNCTION__, __LINE__); + // trace(__FUNCTION__, __LINE__); LOGF(info, "\033[1;32m%s .... line %d\033[0m", functionName, lineNumber); -} // void Trace(const char* functionName, int lineNumber) +} // void trace(const char* functionName, int lineNumber) //============================================================ -void Exit() +void myExit() // TBI 20260713 rename { // A simple utility wrapper. Used only during debugging. - // Use directly as: Exit(); + // Use directly as: myExit(); // Line number, function name, formatting, etc, are determinad automatically. LOGF(info, "\n\n\n\n\n\n\n\n\n\n"); exit(1); -} // void Exit() +} // void myExit() //============================================================ -void StartFunction(const char* functionName) +void startFunction(const char* functionName) { // A simple utility wrapper, used when tc.fVerbose = true. It merely ensures uniform formatting of notification when the function starts. LOGF(info, "\033[1;32mStart %s\033[0m", functionName); // prints in green -} // void StartFunction(const char* functionName) +} // void startFunction(const char* functionName) //============================================================ -void ExitFunction(const char* functionName) +void exitFunction(const char* functionName) { // A simple utility wrapper, used when tc.fVerbose = true. It merely ensures uniform formatting of notification when the function exits. LOGF(info, "\033[1;32mExit %s\033[0m", functionName); // prints in green -} // void ExitFunction(const char* functionName) +} // void exitFunction(const char* functionName) //============================================================ @@ -18289,17 +18413,17 @@ void printEnvironment() //============================================================ -void BailOut(bool finalBailout = false) +void bailOut(bool finalBailout = false) { // Use only locally - bail out if maximum number of events was reached, and dump all results by that point in a local ROOT file. // If fSequentialBailout > 0, bail out is performed each fSequentialBailout events, each time in a new local ROOT file. // For sequential bailout, the naming scheme of ROOT files is AnalysisResultsBailOut_eh.fEventCounter[eProcessed].root . - // If ROOT file with the same name already exists, BailOut is not performed, since the argument is that + // If ROOT file with the same name already exists, bailOut is not performed, since the argument is that // it's pointless to perform Bailout for same eh.fEventCounter[eProcessed], even if eh.fEventCounter[eTotal] changed. // Only if finalBailout = true, I will overwrite the existing file with the same name. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Local variables: TBI 20240130 shall I promote 'em to data members + add support for configurables? @@ -18312,7 +18436,7 @@ void BailOut(bool finalBailout = false) // basically, at 1st call "AnalysisResultsBailOut.root" => "AnalysisResultsBailOut_1*eh.fEventCounter[eProcessed].root", // at 2nd call "AnalysisResultsBailOut.root" => "AnalysisResultsBailOut_2*eh.fEventCounter[eProcessed].root", etc. if (!finalBailout && !gSystem->AccessPathName(sBailOutFile.Data(), kFileExists)) { // only for finalBailout = true, I will overwrite the existing file with the same name. - LOGF(info, "\033[1;33m\nsBailOutFile = %s already exits, that means that eh.fEventCounter[eProcessed] is the same as in the previous call of BailOut.\nJust skipping and waiting more events to pass selection criteria... \033[0m", sBailOutFile.Data()); + LOGF(info, "\033[1;33m\nsBailOutFile = %s already exits, that means that eh.fEventCounter[eProcessed] is the same as in the previous call of bailOut.\nJust skipping and waiting more events to pass selection criteria... \033[0m", sBailOutFile.Data()); return; } } @@ -18357,7 +18481,7 @@ void BailOut(bool finalBailout = false) f->Close(); if (tc.fVerbose && !(tc.fSequentialBailout > 0)) { // then it will be called only once, for the only and permanent bailout - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } // *) Hasta la vista: @@ -18366,27 +18490,27 @@ void BailOut(bool finalBailout = false) } else { LOGF(info, "\033[1;32mBailed out sequentially in function %s at line %d\n The output file is: %s\n\n\033[0m", __FUNCTION__, __LINE__, sBailOutFile.Data()); if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } } -} // void BailOut(bool finalBailout = false) +} // void bailOut(bool finalBailout = false) //============================================================ -void FillQvector(const double& dPhi, const double& dPt, const double& dEta) +void fillQvector(const double& dPhi, const double& dPt, const double& dEta) { // !!! OBSOLETE FUNCTION (as of 20250714) !!! - LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250714, this is an obsolete function, use FillQvectorFromSparse(...) instead (yes, use it also when particles weights are NOT needed) !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); + LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250714, this is an obsolete function, use fillQvectorFromSparse(...) instead (yes, use it also when particles weights are NOT needed) !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); return; // Fill integrated Q-vector. - // Example usage: this->FillQvector(dPhi, dPt, dEta); + // Example usage: this->fillQvector(dPhi, dPt, dEta); if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;32m dPhi = %f\033[0m", dPhi); LOGF(info, "\033[1;32m dPt = %f\033[0m", dPt); LOGF(info, "\033[1;32m dEta = %f\033[0m", dEta); @@ -18399,7 +18523,7 @@ void FillQvector(const double& dPhi, const double& dPt, const double& dEta) double wToPowerP = 1.; // weight raised to power p if (pw.fUseWeights[wPHI]) { - wPhi = Weight(dPhi, wPHI); + wPhi = particleWeight(dPhi, wPHI); if (!(wPhi > 0.)) { LOGF(error, "\033[1;33m%s wPhi is not positive\033[0m", __FUNCTION__); LOGF(fatal, "dPhi = %f\nwPhi = %f", dPhi, wPhi); @@ -18407,7 +18531,7 @@ void FillQvector(const double& dPhi, const double& dPt, const double& dEta) } // if(pw.fUseWeights[wPHI]) if (pw.fUseWeights[wPT]) { - wPt = Weight(dPt, wPT); // corresponding pt weight + wPt = particleWeight(dPt, wPT); // corresponding pt weight if (!(wPt > 0.)) { LOGF(error, "\033[1;33m%s wPt is not positive\033[0m", __FUNCTION__); LOGF(fatal, "dPt = %f\nwPt = %f", dPt, wPt); @@ -18415,7 +18539,7 @@ void FillQvector(const double& dPhi, const double& dPt, const double& dEta) } // if(pw.fUseWeights[wPT]) if (pw.fUseWeights[wETA]) { - wEta = Weight(dEta, wETA); // corresponding eta weight + wEta = particleWeight(dEta, wETA); // corresponding eta weight if (!(wEta > 0.)) { LOGF(error, "\033[1;33m%s wEta is not positive\033[0m", __FUNCTION__); LOGF(fatal, "dEta = %f\nwEta = %f", dEta, wEta); @@ -18428,10 +18552,10 @@ void FillQvector(const double& dPhi, const double& dPt, const double& dEta) if (pw.fUseWeights[wPHI] || pw.fUseWeights[wPT] || pw.fUseWeights[wETA]) { wToPowerP = std::pow(wPhi * wPt * wEta, wp); qv.fQvector[h][wp] += TComplex(wToPowerP * std::cos(h * dPhi), wToPowerP * std::sin(h * dPhi)); // Q-vector with weights - // Remark: If ever I will re-use this code, see how I implemented it in void FillQvectorFromSparse() + // Remark: If ever I will re-use this code, see how I implemented it in void fillQvectorFromSparse() } else { qv.fQvector[h][wp] += TComplex(std::cos(h * dPhi), std::sin(h * dPhi)); // bare Q-vector without weights - // Remark: If ever I will re-use this code, see how I implemented it in void FillQvectorFromSparse() + // Remark: If ever I will re-use this code, see how I implemented it in void fillQvectorFromSparse() } } // for(int wp=0;wp 0.)) { LOGF(error, "\033[1;33m%s at line %d : wPhi is not positive\033[0m", __FUNCTION__, __LINE__); LOGF(error, "pbyp.fPhi = %f", pbyp.fPhi); @@ -18523,7 +18647,7 @@ void FillQvectorFromSparse() // *) Multidimensional pt weights: if (pw.fUseDiffPtWeights[wPtPtAxis]) { // yes, 0th axis serves as a common boolean for this category - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); if (!(wPt > 0.)) { LOGF(error, "\033[1;33m%s at line %d : wPt is not positive\033[0m", __FUNCTION__, __LINE__); LOGF(error, "pbyp.fPt = %f", pbyp.fPt); @@ -18536,13 +18660,16 @@ void FillQvectorFromSparse() if (pw.fUseDiffPtWeights[wPtCentralityAxis]) { LOGF(fatal, "ebye.fCentrality = %f", ebye.fCentrality); } + if (pw.fUseDiffPtWeights[wPtVertexZAxis]) { + LOGF(fatal, "ebye.fVz = %f", ebye.fVz); + } LOGF(fatal, "Multidimensional weight for enabled dimensions is wPt = %f", wPt); } } // if(pw.fUseDiffPtWeights[wPtPtAxis]) // *) Multidimensional eta weights: if (pw.fUseDiffEtaWeights[wEtaEtaAxis]) { // yes, 0th axis serves as a common boolean for this category - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); if (!(wEta > 0.)) { LOGF(error, "\033[1;33m%s at line %d : wEta is not positive\033[0m", __FUNCTION__, __LINE__); LOGF(error, "pbyp.fEta = %f", pbyp.fEta); @@ -18555,13 +18682,16 @@ void FillQvectorFromSparse() if (pw.fUseDiffEtaWeights[wEtaCentralityAxis]) { LOGF(fatal, "ebye.fCentrality = %f", ebye.fCentrality); } + if (pw.fUseDiffEtaWeights[wEtaVertexZAxis]) { + LOGF(fatal, "ebye.fVz = %f", ebye.fVz); + } LOGF(fatal, "Multidimensional weight for enabled dimensions is wEta = %f", wEta); } } // if(pw.fUseDiffEtaWeights[wEtaEtaAxis]) // *) Multidimensional charge weights: if (pw.fUseDiffChargeWeights[wChargeChargeAxis]) { // yes, 0th axis serves as a common boolean for this category - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); if (!(wCharge > 0.)) { LOGF(error, "\033[1;33m%s at line %d : wCharge is not positive\033[0m", __FUNCTION__, __LINE__); LOGF(error, "pbyp.fCharge = %f", pbyp.fCharge); @@ -18574,6 +18704,9 @@ void FillQvectorFromSparse() if (pw.fUseDiffChargeWeights[wChargeCentralityAxis]) { LOGF(fatal, "ebye.fCentrality = %f", ebye.fCentrality); } + if (pw.fUseDiffChargeWeights[wChargeVertexZAxis]) { + LOGF(fatal, "ebye.fVz = %f", ebye.fVz); + } LOGF(fatal, "Multidimensional weight for enabled dimensions is wCharge = %f", wCharge); } } // if(pw.fUseDiffChargeWeights[wChargeChargeAxis]) @@ -18631,56 +18764,56 @@ void FillQvectorFromSparse() } // if(es.fCalculateEtaSeparations) { if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void FillQvectorFromSparse() +} // void fillQvectorFromSparse() //============================================================ -void Fillqvector(const double& dPhi, const double& kineVarValue, eqvectorKine kineVarChoice, const double& dEta = 0.) +void fillqvector(const double& dPhi, const double& kineVarValue, EnqvectorKine kineVarChoice, const double& dEta = 0.) { // !!! OBSOLETE FUNCTION (as of 20250527) !!! - LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250527, this is an obsolete function, use Fillqvectors() instead (yes, plural!) + its helper function(s) !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); + LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250527, this is an obsolete function, use fillqvectors() instead (yes, plural!) + its helper function(s) !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); return; // Fill differential q-vector, in generic kinematic variable. Here "kine" originally meant vs. pt or vs. eta, now it's general. - // Example usage #1: this->Fillqvector(dPhi, dPt, PTq); // differential q-vectors without using eta separations - // Example usage #2: this->Fillqvector(dPhi, dPt, PTq, dEta); // differential q-vectors with using eta separations (I need dEta of particle to decide whether particle is added to qa or qb) + // Example usage #1: this->fillqvector(dPhi, dPt, PTq); // differential q-vectors without using eta separations + // Example usage #2: this->fillqvector(dPhi, dPt, PTq, dEta); // differential q-vectors with using eta separations (I need dEta of particle to decide whether particle is added to qa or qb) // Remark: As of 20250527, this function is obsolete, and it's superseeded by more general functions: - // a) void FillqvectorNdim(...) (without particle weights) + // a) void fillqvectorNdim(...) (without particle weights) // b) void FillqvectorNdimFromSparse(...) (with particle weights) - // Remark: As of 20251027, FillqvectorNdim(...) and FillqvectorNdimFromSparse(...) are also obsolete, use instead Fillqvectors() (yes, plural!) + its helper function(s) + // Remark: As of 20251027, fillqvectorNdim(...) and FillqvectorNdimFromSparse(...) are also obsolete, use instead fillqvectors() (yes, plural!) + its helper function(s) if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } - // *) Mapping between enum's "eqvectorKine" on one side, and "eAsFunctionOf", "eWeights" and "eDiffWeights" on the other: + // *) Mapping between enum's "EnqvectorKine" on one side, and "EnAsFunctionOf", "EnWeights" and "EnDiffWeights" on the other: // TBI 20240212 I could promote this also to a member function, if I need it elsewhere. Or I could use TExMap? - eAsFunctionOf AFO_var = eAsFunctionOf_N; // this local variable determines the enum "eAsFunctionOf" which corresponds to enum "eqvectorKine" - eWeights AFO_weight = eWeights_N; // this local variable determines the enum "eWeights" which corresponds to enum "eqvectorKine" - eDiffWeights AFO_diffWeight = eDiffWeights_N; // this local variable determines the enum "eDiffWeights" which corresponds to enum "eqvectorKine" + EnAsFunctionOf afoVar = eAsFunctionOf_N; // this local variable determines the enum "EnAsFunctionOf" which corresponds to enum "EnqvectorKine" + EnWeights afoWeight = eWeights_N; // this local variable determines the enum "EnWeights" which corresponds to enum "EnqvectorKine" + EnDiffWeights afoDiffWeight = eDiffWeights_N; // this local variable determines the enum "EnDiffWeights" which corresponds to enum "EnqvectorKine" switch (kineVarChoice) { case PTq: { - AFO_var = AFO_PT; - AFO_weight = wPT; - AFO_diffWeight = wPHIPT; // TBI 20250215 this is now obsolete, see the comment in enum + afoVar = AFO_PT; + afoWeight = wPT; + afoDiffWeight = wPHIPT; // TBI 20250215 this is now obsolete, see the comment in enum break; } case ETAq: { - AFO_var = AFO_ETA; - AFO_weight = wETA; - AFO_diffWeight = wPHIETA; // TBI 20250215 this is now obsolete, see the comment in enum + afoVar = AFO_ETA; + afoWeight = wETA; + afoDiffWeight = wPHIETA; // TBI 20250215 this is now obsolete, see the comment in enum break; } case CHARGEq: { - AFO_var = AFO_CHARGE; - AFO_weight = wCHARGE; - AFO_diffWeight = wPHICHARGE; // TBI 20250215 this is now obsolete, see the comment in enum + afoVar = AFO_CHARGE; + afoWeight = wCHARGE; + afoDiffWeight = wPHICHARGE; // TBI 20250215 this is now obsolete, see the comment in enum break; } default: { @@ -18690,21 +18823,21 @@ void Fillqvector(const double& dPhi, const double& kineVarValue, eqvectorKine ki } // switch(kineVarChoice) // *) Insanity checks on above settings: - if (AFO_var == eAsFunctionOf_N) { - LOGF(fatal, "\033[1;31m%s at line %d : AFO_var == eAsFunctionOf_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); + if (afoVar == eAsFunctionOf_N) { + LOGF(fatal, "\033[1;31m%s at line %d : afoVar == eAsFunctionOf_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); } - if (AFO_weight == eWeights_N) { - LOGF(fatal, "\033[1;31m%s at line %d : AFO_weight == eWeights_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); + if (afoWeight == eWeights_N) { + LOGF(fatal, "\033[1;31m%s at line %d : afoWeight == eWeights_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); } - if (AFO_diffWeight == eDiffWeights_N) { - LOGF(fatal, "\033[1;31m%s at line %d : AFO_diffWeight == eDiffWeights_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); + if (afoDiffWeight == eDiffWeights_N) { + LOGF(fatal, "\033[1;31m%s at line %d : afoDiffWeight == eDiffWeights_N => add some more entries to the case statement \033[0m", __FUNCTION__, __LINE__); } // *) Get the desired bin number: int bin = -1; - if (res.fResultsPro[AFO_var]) { - bin = res.fResultsPro[AFO_var]->FindBin(kineVarValue); // this 'bin' starts from 1, i.e. this is genuine histogram bin - if (0 >= bin || res.fResultsPro[AFO_var]->GetNbinsX() < bin) { // either underflow or overflow is hit, meaning that histogram is booked in narrower range than cuts + if (res.fResultsPro[afoVar]) { + bin = res.fResultsPro[afoVar]->FindBin(kineVarValue); // this 'bin' starts from 1, i.e. this is genuine histogram bin + if (0 >= bin || res.fResultsPro[afoVar]->GetNbinsX() < bin) { // either underflow or overflow is hit, meaning that histogram is booked in narrower range than cuts LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice = %d, bin = %d, kineVarValue = %f \033[0m", __FUNCTION__, __LINE__, static_cast(kineVarChoice), bin, kineVarValue); } } @@ -18712,29 +18845,29 @@ void Fillqvector(const double& dPhi, const double& kineVarValue, eqvectorKine ki // *) Get all integrated kinematic weights: double wToPowerP = 1.; // weight raised to power p double kineVarWeight = 1.; // e.g. this can be integrated pT or eta weight - if (pw.fUseWeights[AFO_weight]) { - kineVarWeight = Weight(kineVarValue, AFO_weight); // corresponding e.g. pt or eta weight + if (pw.fUseWeights[afoWeight]) { + kineVarWeight = particleWeight(kineVarValue, afoWeight); // corresponding e.g. pt or eta weight if (!(kineVarWeight > 0.)) { LOGF(fatal, "\033[1;31m%s at line %d : kineVarWeight is not positive \033[0m", __FUNCTION__, __LINE__); // TBI 20240212 or could I just skip this particle? } - } // if(fUseWeights[AFO_weight]) { + } // if(fUseWeights[afoWeight]) { // *) Get all differential phi-weights for this kinematic variable: // Remark: special treatment is justified for phi-weights, because q-vector is defined in terms of phi-weights. double diffPhiWeightsForThisKineVar = 1.; - if (pw.fUseDiffWeights[AFO_diffWeight]) { - diffPhiWeightsForThisKineVar = DiffWeight(dPhi, kineVarValue, kineVarChoice); // corresponding differential phi weight as a function of e.g. pt or eta + if (pw.fUseDiffWeights[afoDiffWeight]) { + diffPhiWeightsForThisKineVar = diffWeight(dPhi, kineVarValue, kineVarChoice); // corresponding differential phi weight as a function of e.g. pt or eta if (!(diffPhiWeightsForThisKineVar > 0.)) { LOGF(fatal, "\033[1;31m%s at line %d : diffPhiWeightsForThisKineVar is not positive \033[0m", __FUNCTION__, __LINE__); // TBI 20240212 or could I just skip this particle? } - } // if(pw.fUseDiffWeights[AFO_diffWeight]) { + } // if(pw.fUseDiffWeights[afoDiffWeight]) { // *) Finally, fill differential q-vector in that bin: for (int h = 0; h < gMaxHarmonic * gMaxCorrelator + 1; h++) { for (int wp = 0; wp < gMaxCorrelator + 1; wp++) { // weight power - if (pw.fUseWeights[AFO_weight] || pw.fUseDiffWeights[AFO_diffWeight]) { + if (pw.fUseWeights[afoWeight] || pw.fUseDiffWeights[afoDiffWeight]) { // TBI 20240212 supported at the moment: e.g. q-vector vs pt can be weighted only with diff. phi(pt) and integrated pt weights. // It cannot be weighted in addition with eta weights, since in any case I anticipate I will do always 1-D analysis, by integrating out all other dependencies wToPowerP = std::pow(diffPhiWeightsForThisKineVar * kineVarWeight, wp); @@ -18755,10 +18888,10 @@ void Fillqvector(const double& dPhi, const double& kineVarValue, eqvectorKine ki qv.fqvectorEntries[kineVarChoice][bin - 1]++; // count number of particles in this pt bin in this event // *) Usage of eta separations in differential correlations: - if (es.fCalculateEtaSeparations && es.fCalculateEtaSeparationsAsFunctionOf[AFO_var]) { // yes, I can decouple this one from if (qv.fCalculateQvectors) + if (es.fCalculateEtaSeparations && es.fCalculateEtaSeparationsAsFunctionOf[afoVar]) { // yes, I can decouple this one from if (qv.fCalculateQvectors) - if (AFO_var == AFO_ETA) { - LOGF(fatal, "\033[1;31m%s at line %d : AFO_var == AFO_ETA . This doesn't make any sense in this context. \033[0m", __FUNCTION__, __LINE__); + if (afoVar == AFO_ETA) { + LOGF(fatal, "\033[1;31m%s at line %d : afoVar == AFO_ETA . This doesn't make any sense in this context. \033[0m", __FUNCTION__, __LINE__); } if (dEta < 0.) { @@ -18793,37 +18926,37 @@ void Fillqvector(const double& dPhi, const double& kineVarValue, eqvectorKine ki } // if(es.fCalculateEtaSeparations) { if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void Fillqvector(const double& dPhi, const double& kineVarValue, eqvectorKine kineVarChoice) +} // void fillqvector(const double& dPhi, const double& kineVarValue, EnqvectorKine kineVarChoice) //============================================================ -eAsFunctionOf AfoKineMap1D(eqvectorKine kineVarChoice) +EnAsFunctionOf afoKineMap1D(EnqvectorKine kineVarChoice) { - // Simple utility function to map for the 1-dimensional case eqvectorKine into eAsFunctionOf. + // Simple utility function to map for the 1-dimensional case EnqvectorKine into EnAsFunctionOf. if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } - eAsFunctionOf AFO_var = eAsFunctionOf_N; + EnAsFunctionOf afoVar = eAsFunctionOf_N; switch (kineVarChoice) { case PTq: { - AFO_var = AFO_PT; + afoVar = AFO_PT; break; } case ETAq: { - AFO_var = AFO_ETA; + afoVar = AFO_ETA; break; } case CHARGEq: { - AFO_var = AFO_CHARGE; + afoVar = AFO_CHARGE; break; } @@ -18837,39 +18970,39 @@ eAsFunctionOf AfoKineMap1D(eqvectorKine kineVarChoice) } // switch(kineVarChoice) if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } - return AFO_var; + return afoVar; -} // eAsFunctionOf AfoKineMap1D(eqvectorKine kineVarChoice) +} // EnAsFunctionOf afoKineMap1D(EnqvectorKine kineVarChoice) //============================================================ -eAsFunctionOf2D AfoKineMap2D(eqvectorKine kineVarChoice) +EnAsFunctionOf2D afoKineMap2D(EnqvectorKine kineVarChoice) { - // Simple utility function to map for the 2-dimensional case eqvectorKine into eAsFunctionOf2D. + // Simple utility function to map for the 2-dimensional case EnqvectorKine into EnAsFunctionOf2D. if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } - eAsFunctionOf2D AFO_var = eAsFunctionOf2D_N; + EnAsFunctionOf2D afoVar = eAsFunctionOf2D_N; switch (kineVarChoice) { case PT_ETAq: { - AFO_var = AFO_PT_ETA; + afoVar = AFO_PT_ETA; break; } case PT_CHARGEq: { - AFO_var = AFO_PT_CHARGE; + afoVar = AFO_PT_CHARGE; break; } case ETA_CHARGEq: { - AFO_var = AFO_ETA_CHARGE; + afoVar = AFO_ETA_CHARGE; break; } @@ -18883,29 +19016,29 @@ eAsFunctionOf2D AfoKineMap2D(eqvectorKine kineVarChoice) } // switch(kineVarChoice) if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } - return AFO_var; + return afoVar; -} // eAsFunctionOf2D AfoKineMap2D(eqvectorKine kineVarChoice) +} // EnAsFunctionOf2D afoKineMap2D(EnqvectorKine kineVarChoice) //============================================================ -eAsFunctionOf3D AfoKineMap3D(eqvectorKine kineVarChoice) +EnAsFunctionOf3D afoKineMap3D(EnqvectorKine kineVarChoice) { - // Simple utility function to map for the 3-dimensional case eqvectorKine into eAsFunctionOf3D. + // Simple utility function to map for the 3-dimensional case EnqvectorKine into EnAsFunctionOf3D. if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } - eAsFunctionOf3D AFO_var = eAsFunctionOf3D_N; + EnAsFunctionOf3D afoVar = eAsFunctionOf3D_N; switch (kineVarChoice) { case PT_ETA_CHARGEq: { - AFO_var = AFO_PT_ETA_CHARGE; + afoVar = AFO_PT_ETA_CHARGE; break; } @@ -18919,23 +19052,23 @@ eAsFunctionOf3D AfoKineMap3D(eqvectorKine kineVarChoice) } // switch(kineVarChoice) if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } - return AFO_var; + return afoVar; -} // eAsFunctionOf3D AfoKineMap3D(eqvectorKine kineVarChoice) +} // EnAsFunctionOf3D afoKineMap3D(EnqvectorKine kineVarChoice) //============================================================ -TString StringKineMap(eqvectorKine kineVarChoice) +TString stringKineMap(EnqvectorKine kineVarChoice) { - // Simple utility function to map eqvectorKine into string. + // Simple utility function to map EnqvectorKine into string. - // Example: StringKineMap(PTq).Data() => prints "vs. pt" + // Example: stringKineMap(PTq).Data() => prints "vs. pt" if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } TString s = ""; @@ -18987,20 +19120,20 @@ TString StringKineMap(eqvectorKine kineVarChoice) } // switch(kineVarChoice) if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } return s; -} // TString StringKineMap(eqvectorKine kineVarChoice) +} // TString stringKineMap(EnqvectorKine kineVarChoice) //============================================================ -void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvectorKine kineVarChoice, const double& dEta = 0.) +void fillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, EnqvectorKine kineVarChoice, const double& dEta = 0.) { // !!! OBSOLETE FUNCTION (as of 20250717) !!! - LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250717, this is an obsolete function, use Fillqvectors(...) and its helper function FillqvectorFromSparse(...) instead !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); + LOGF(info, "\033[1;33m%s at line %d: !!!! WARNING !!!! As of 20250717, this is an obsolete function, use fillqvectors(...) and its helper function fillqvectorFromSparse(...) instead !!!! WARNING !!!! \033[0m", __FUNCTION__, __LINE__); return; @@ -19017,22 +19150,22 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect // Example - the standard 1D case: // double kineArr[1] = {dPt}; - // this->FillqvectorNdim(dPhi, kineArr, 1, PTq); // differential q-vector vs. pt + // this->fillqvectorNdim(dPhi, kineArr, 1, PTq); // differential q-vector vs. pt // Example - the 2D case: // double kineArr[2] = {dPt, dEta}; - // this->FillqvectorNdim(dPhi, kineArr, 2, PT_ETAq); // differential q-vector vs. (pt, eta) + // this->fillqvectorNdim(dPhi, kineArr, 2, PT_ETAq); // differential q-vector vs. (pt, eta) // Example - the 3D case: // double kineArr[3] = {dPt, dEta, dCharge}; - // this->FillqvectorNdim(dPhi, kineArr, 3, PT_ETA_CHARGEq); // differential q-vector vs. (pt, eta, charge) + // this->fillqvectorNdim(dPhi, kineArr, 3, PT_ETA_CHARGEq); // differential q-vector vs. (pt, eta, charge) // Example - the 1D case, pt dependence with eta separations: // double kineArr[1] = {dPt}; - // this->FillqvectorNdim(dPhi, kineArr, 1, PTq, dEta); // differential q-vectors with using eta separations (I need dEta of particle to decide whether particle is added to qa or qb) + // this->fillqvectorNdim(dPhi, kineArr, 1, PTq, dEta); // differential q-vectors with using eta separations (I need dEta of particle to decide whether particle is added to qa or qb) if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // This is the linearized global bin, the 2nd index of fqvector[...][gMaxNoBinsKine][...][...], it shall work transparently for 1D, 2D, 3D, etc... @@ -19042,39 +19175,39 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect switch (Ndim) { case 1: { - eAsFunctionOf AFO_var = AfoKineMap1D(kineVarChoice); - if (res.fResultsPro[AFO_var]) { - bin = res.fResultsPro[AFO_var]->FindBin(kineVarValues[0]); // this is linearized 'global bin', for 1D it's the same as ordinary bin + EnAsFunctionOf afoVar = afoKineMap1D(kineVarChoice); + if (res.fResultsPro[afoVar]) { + bin = res.fResultsPro[afoVar]->FindBin(kineVarValues[0]); // this is linearized 'global bin', for 1D it's the same as ordinary bin // TBI 20250528 check if the test below is computationally heavy. If so, add the flag tc.fInsanityCheckForEachParticle here. - if (res.fResultsPro[AFO_var]->IsBinUnderflow(bin) || res.fResultsPro[AFO_var]->IsBinOverflow(bin)) { - LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice = %d (%s), kineVarValues[0] = %f is in bin = %d, which is either underflow or overflow.\033[0m", __FUNCTION__, __LINE__, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), kineVarValues[0], bin); + if (res.fResultsPro[afoVar]->IsBinUnderflow(bin) || res.fResultsPro[afoVar]->IsBinOverflow(bin)) { + LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice = %d (%s), kineVarValues[0] = %f is in bin = %d, which is either underflow or overflow.\033[0m", __FUNCTION__, __LINE__, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), kineVarValues[0], bin); } } break; } case 2: { - eAsFunctionOf2D AFO_var = AfoKineMap2D(kineVarChoice); - if (res.fResultsPro2D[AFO_var]) { - bin = res.fResultsPro2D[AFO_var]->FindBin(kineVarValues[0], kineVarValues[1]); // this is linearized 'global bin' + EnAsFunctionOf2D afoVar = afoKineMap2D(kineVarChoice); + if (res.fResultsPro2D[afoVar]) { + bin = res.fResultsPro2D[afoVar]->FindBin(kineVarValues[0], kineVarValues[1]); // this is linearized 'global bin' // TBI 20250528 check if the test below is computationally heavy. If so, add the flag tc.fInsanityCheckForEachParticle here. - if (res.fResultsPro2D[AFO_var]->IsBinUnderflow(bin) || res.fResultsPro2D[AFO_var]->IsBinOverflow(bin)) { - LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice = %d (%s), kineVarValues[0] = %f, kineVarValues[1] = %f is in global bin = %d, which is either underflow or overflow.\033[0m", __FUNCTION__, __LINE__, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), kineVarValues[0], kineVarValues[1], bin); + if (res.fResultsPro2D[afoVar]->IsBinUnderflow(bin) || res.fResultsPro2D[afoVar]->IsBinOverflow(bin)) { + LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice = %d (%s), kineVarValues[0] = %f, kineVarValues[1] = %f is in global bin = %d, which is either underflow or overflow.\033[0m", __FUNCTION__, __LINE__, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), kineVarValues[0], kineVarValues[1], bin); } } break; } case 3: { - eAsFunctionOf3D AFO_var = AfoKineMap3D(kineVarChoice); - if (res.fResultsPro3D[AFO_var]) { - bin = res.fResultsPro3D[AFO_var]->FindBin(kineVarValues[0], kineVarValues[1], kineVarValues[2]); // this is linearized 'global bin' + EnAsFunctionOf3D afoVar = afoKineMap3D(kineVarChoice); + if (res.fResultsPro3D[afoVar]) { + bin = res.fResultsPro3D[afoVar]->FindBin(kineVarValues[0], kineVarValues[1], kineVarValues[2]); // this is linearized 'global bin' // TBI 20250528 check if the test below is computationally heavy. If so, add the flag tc.fInsanityCheckForEachParticle here. - if (res.fResultsPro3D[AFO_var]->IsBinUnderflow(bin) || res.fResultsPro3D[AFO_var]->IsBinOverflow(bin)) { - LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice = %d (%s), kineVarValues[0] = %f, kineVarValues[1] = %f, kineVarValues[2] = %f is in global bin = %d, which is either underflow or overflow.\033[0m", __FUNCTION__, __LINE__, static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data(), kineVarValues[0], kineVarValues[1], kineVarValues[2], bin); + if (res.fResultsPro3D[afoVar]->IsBinUnderflow(bin) || res.fResultsPro3D[afoVar]->IsBinOverflow(bin)) { + LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice = %d (%s), kineVarValues[0] = %f, kineVarValues[1] = %f, kineVarValues[2] = %f is in global bin = %d, which is either underflow or overflow.\033[0m", __FUNCTION__, __LINE__, static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data(), kineVarValues[0], kineVarValues[1], kineVarValues[2], bin); } } break; @@ -19093,7 +19226,7 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect return; // TBI 20250716 remove eventually, here is just a protection against using the code below // Particle weights from sparse histograms: - // Remark: Keep in sync with corresponding implementation in FillQvectorFromSparse + // Remark: Keep in sync with corresponding implementation in fillQvectorFromSparse double wPhi = 1.; // differential multidimensional phi weight, its dimensions are defined via enum eDiffPhiWeights double wPt = 1.; // differential multidimensional pt weight, its dimensions are defined via enum eDiffPtWeights double wEta = 1.; // differential multidimensional eta weight, its dimensions are defined via enum eDiffEtaWeights @@ -19103,31 +19236,31 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect if (pw.fUseDiffPhiWeights[wPhiPhiAxis]) { // yes, 0th axis serves as a common boolean for this category switch (kineVarChoice) { case PTq: { - // wPhi = WeightFromSparse(dPhi, kineVarValues[0], 0., 0., eDWPhi); + // wPhi = weightFromSparse(dPhi, kineVarValues[0], 0., 0., eDWPhi); break; } case ETAq: { - // wPhi = WeightFromSparse(dPhi, 0., kineVarValues[0], 0., eDWPhi); + // wPhi = weightFromSparse(dPhi, 0., kineVarValues[0], 0., eDWPhi); break; } case CHARGEq: { - // wPhi = WeightFromSparse(dPhi, 0., 0., kineVarValues[0], eDWPhi); + // wPhi = weightFromSparse(dPhi, 0., 0., kineVarValues[0], eDWPhi); break; } case PT_ETAq: { - // wPhi = WeightFromSparse(dPhi, kineVarValues[0], kineVarValues[1], 0., eDWPhi); + // wPhi = weightFromSparse(dPhi, kineVarValues[0], kineVarValues[1], 0., eDWPhi); break; } case PT_CHARGEq: { - // wPhi = WeightFromSparse(dPhi, kineVarValues[0], 0., kineVarValues[1], eDWPhi); + // wPhi = weightFromSparse(dPhi, kineVarValues[0], 0., kineVarValues[1], eDWPhi); break; } case ETA_CHARGEq: { - // wPhi = WeightFromSparse(dPhi, 0., kineVarValues[0], kineVarValues[1], eDWPhi); + // wPhi = weightFromSparse(dPhi, 0., kineVarValues[0], kineVarValues[1], eDWPhi); break; } case PT_ETA_CHARGEq: { - // wPhi = WeightFromSparse(dPhi, kineVarValues[0], kineVarValues[1], kineVarValues[2], eDWPhi); + // wPhi = weightFromSparse(dPhi, kineVarValues[0], kineVarValues[1], kineVarValues[2], eDWPhi); break; } @@ -19139,7 +19272,7 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect } } - // last argument is enum eDiffWeightCategory. Event quantities, e.g. centrality and vz, I do not need to pass, because + // last argument is enum EnDiffWeightCategory. Event quantities, e.g. centrality and vz, I do not need to pass, because // for them I have ebye data members if (!(wPhi > 0.)) { LOGF(error, "\033[1;33m%s wPhi is not positive\033[0m", __FUNCTION__); @@ -19152,11 +19285,11 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect if (pw.fUseDiffPtWeights[wPtPtAxis]) { // yes, 0th axis serves as a common boolean for this category switch (kineVarChoice) { case PTq: { - // wPt = WeightFromSparse(dPhi, kineVarValues[0], 0., 0., eDWPt); + // wPt = weightFromSparse(dPhi, kineVarValues[0], 0., 0., eDWPt); break; } case CHARGEq: { - // wPt = WeightFromSparse(dPhi, 0., 0., kineVarValues[0], eDWPt); + // wPt = weightFromSparse(dPhi, 0., 0., kineVarValues[0], eDWPt); break; } @@ -19179,11 +19312,11 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect if (pw.fUseDiffEtaWeights[wEtaEtaAxis]) { // yes, 0th axis serves as a common boolean for this category switch (kineVarChoice) { case ETAq: { - // wEta = WeightFromSparse(dPhi, 0., kineVarValues[0], 0., eDWEta); + // wEta = weightFromSparse(dPhi, 0., kineVarValues[0], 0., eDWEta); break; } case CHARGEq: { - // wEta = WeightFromSparse(dPhi, 0., 0., kineVarValues[0], eDWEta); + // wEta = weightFromSparse(dPhi, 0., 0., kineVarValues[0], eDWEta); break; } @@ -19227,7 +19360,7 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect if (es.fCalculateEtaSeparations && qv.fCalculateqvectorsKineEtaSeparations[kineVarChoice]) { // yes, I have decoupled this one from if (qv.fCalculateQvectors) if (kineVarChoice == ETAq || kineVarChoice == PT_ETAq || kineVarChoice == ETA_CHARGEq || kineVarChoice == PT_ETA_CHARGEq) { - LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice == %s . This doesn't make any sense in this context => eta separations cannot be used for differential vectors vs. eta (either 1D or 2D or 3D case). \033[0m", __FUNCTION__, __LINE__, StringKineMap(kineVarChoice).Data()); + LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice == %s . This doesn't make any sense in this context => eta separations cannot be used for differential vectors vs. eta (either 1D or 2D or 3D case). \033[0m", __FUNCTION__, __LINE__, stringKineMap(kineVarChoice).Data()); } if (dEta < 0.) { @@ -19260,21 +19393,21 @@ void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvect } // if(es.fCalculateEtaSeparations) if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void FillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, eqvectorKine kineVarChoice, const double& dEta = 0.) +} // void fillqvectorNdim(const double& dPhi, double* kineVarValues, int Ndim, EnqvectorKine kineVarChoice, const double& dEta = 0.) //============================================================ -void Fillqvectors() +void fillqvectors() { - // In this function, I fill all requested differential q-vectors, by calling for each requested case a helper function FillqvectorFromSparse(...). + // In this function, I fill all requested differential q-vectors, by calling for each requested case a helper function fillqvectorFromSparse(...). // :fqv if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); LOGF(info, "\033[1;32m pbyp.fPhi = %f\033[0m", pbyp.fPhi); LOGF(info, "\033[1;32m pbyp.fPt = %f\033[0m", pbyp.fPt); LOGF(info, "\033[1;32m pbyp.fEta = %f\033[0m", pbyp.fEta); @@ -19306,23 +19439,23 @@ void Fillqvectors() // ****) determine all supported particle weights: // w_phi(pt): if (pw.fUseDiffPhiWeights[wPhiPtAxis]) { - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } // w_pt(pt): if (pw.fUseDiffPtWeights[wPtPtAxis]) { - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } // w_eta(pt): if (pw.fUseDiffEtaWeights[wEtaPtAxis]) { - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } // w_charge(pt): if (pw.fUseDiffChargeWeights[wChargePtAxis]) { - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } // ****) finally, fill: - FillqvectorFromSparse(PTq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pT) filled for global bin to which this pT corresponds + fillqvectorFromSparse(PTq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pT) filled for global bin to which this pT corresponds } // if(tc.fCalculateAsFunctionOf[AFO_PT]) @@ -19335,23 +19468,23 @@ void Fillqvectors() // ****) determine all supported particle weights: // w_phi(eta): if (pw.fUseDiffPhiWeights[wPhiEtaAxis]) { - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } // w_pt(eta): if (pw.fUseDiffPtWeights[wPtEtaAxis]) { - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } // w_eta(eta): if (pw.fUseDiffEtaWeights[wEtaEtaAxis]) { - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } // w_charge(eta): if (pw.fUseDiffChargeWeights[wChargeEtaAxis]) { - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } // ****) finally, fill: - FillqvectorFromSparse(ETAq, bin, wPhi * wPt * wEta * wCharge); // weighted q(eta) filled for global bin to which this eta corresponds + fillqvectorFromSparse(ETAq, bin, wPhi * wPt * wEta * wCharge); // weighted q(eta) filled for global bin to which this eta corresponds } // if (mupa.fCalculateCorrelationsAsFunctionOf[AFO_ETA] || t0.fCalculateTest0AsFunctionOf[AFO_ETA]) @@ -19364,23 +19497,23 @@ void Fillqvectors() // ****) determine all supported particle weights: // w_phi(charge): if (pw.fUseDiffPhiWeights[wPhiChargeAxis]) { - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } // w_pt(charge): if (pw.fUseDiffPtWeights[wPtChargeAxis]) { - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } // w_eta(charge): if (pw.fUseDiffEtaWeights[wEtaChargeAxis]) { - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } // w_charge(charge): if (pw.fUseDiffChargeWeights[wChargeChargeAxis]) { - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } // ****) finally, fill: - FillqvectorFromSparse(CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(charge) filled for global bin to which this charge corresponds + fillqvectorFromSparse(CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(charge) filled for global bin to which this charge corresponds } // if (mupa.fCalculateCorrelationsAsFunctionOf[AFO_CHARGE] || t0.fCalculateTest0AsFunctionOf[AFO_CHARGE]) @@ -19396,26 +19529,26 @@ void Fillqvectors() // ****) determine all supported particle weights: // w_phi(pt,eta): if (pw.fUseDiffPhiWeights[wPhiPtAxis] && pw.fUseDiffPhiWeights[wPhiEtaAxis]) { - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } // w_pt(pt,eta): if (pw.fUseDiffPtWeights[wPtPtAxis] && pw.fUseDiffPtWeights[wPtEtaAxis]) { - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } // w_eta(pt,eta): if (pw.fUseDiffEtaWeights[wEtaPtAxis] && pw.fUseDiffEtaWeights[wEtaEtaAxis]) { - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } // w_charge(pt,eta): if (pw.fUseDiffChargeWeights[wChargePtAxis] && pw.fUseDiffChargeWeights[wChargeEtaAxis]) { - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } // ****) finally, fill: - FillqvectorFromSparse(PT_ETAq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pt,eta) filled for global bin to which this (pt,eta) corresponds + fillqvectorFromSparse(PT_ETAq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pt,eta) filled for global bin to which this (pt,eta) corresponds } // if (t0.fCalculate2DTest0AsFunctionOf[AFO_PT_ETA] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_ETA]) @@ -19428,23 +19561,23 @@ void Fillqvectors() // ****) determine all supported particle weights: // w_phi(pt,charge): if (pw.fUseDiffPhiWeights[wPhiPtAxis] && pw.fUseDiffPhiWeights[wPhiChargeAxis]) { - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } // w_pt(pt,charge): if (pw.fUseDiffPtWeights[wPtPtAxis] && pw.fUseDiffPtWeights[wPtChargeAxis]) { - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } // w_eta(pt,charge): if (pw.fUseDiffEtaWeights[wEtaPtAxis] && pw.fUseDiffEtaWeights[wEtaChargeAxis]) { - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } // w_charge(pt,charge): if (pw.fUseDiffChargeWeights[wChargePtAxis] && pw.fUseDiffChargeWeights[wChargeChargeAxis]) { - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } // ****) finally, fill: - FillqvectorFromSparse(PT_CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pt,charge) filled for global bin to which this (pt,charge) corresponds + fillqvectorFromSparse(PT_CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pt,charge) filled for global bin to which this (pt,charge) corresponds } // if (t0.fCalculate2DTest0AsFunctionOf[AFO_PT_CHARGE] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_PT_CHARGE]) @@ -19457,23 +19590,23 @@ void Fillqvectors() // ****) determine all supported particle weights: // w_phi(eta,charge): if (pw.fUseDiffPhiWeights[wPhiEtaAxis] && pw.fUseDiffPhiWeights[wPhiChargeAxis]) { - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } // w_pt(eta,charge): if (pw.fUseDiffPtWeights[wPtEtaAxis] && pw.fUseDiffPtWeights[wPtChargeAxis]) { - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } // w_eta(eta,charge): if (pw.fUseDiffEtaWeights[wEtaEtaAxis] && pw.fUseDiffEtaWeights[wEtaChargeAxis]) { - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } // w_charge(eta,charge): if (pw.fUseDiffChargeWeights[wChargeEtaAxis] && pw.fUseDiffChargeWeights[wChargeChargeAxis]) { - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } // ****) finally, fill: - FillqvectorFromSparse(ETA_CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(eta,charge) filled in global bin to which this (eta,charge) corresponds + fillqvectorFromSparse(ETA_CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(eta,charge) filled in global bin to which this (eta,charge) corresponds } // if (t0.fCalculate2DTest0AsFunctionOf[AFO_ETA_CHARGE] || t0.fCalculate3DTest0AsFunctionOf[AFO_CENTRALITY_ETA_CHARGE]) @@ -19490,40 +19623,40 @@ void Fillqvectors() // ****) determine all supported particle weights: // w_phi(pt,eta,charge): if (pw.fUseDiffPhiWeights[wPhiPtAxis] && pw.fUseDiffPhiWeights[wPhiEtaAxis] && pw.fUseDiffPhiWeights[wPhiChargeAxis]) { - wPhi = WeightFromSparse(eDWPhi); + wPhi = weightFromSparse(eDWPhi); } // w_pt(pt,eta,charge): if (pw.fUseDiffPtWeights[wPtPtAxis] && pw.fUseDiffPtWeights[wPtEtaAxis] && pw.fUseDiffPtWeights[wPtChargeAxis]) { - wPt = WeightFromSparse(eDWPt); + wPt = weightFromSparse(eDWPt); } // w_eta(pt,eta,charge): if (pw.fUseDiffEtaWeights[wEtaPtAxis] && pw.fUseDiffEtaWeights[wEtaEtaAxis] && pw.fUseDiffEtaWeights[wEtaChargeAxis]) { - wEta = WeightFromSparse(eDWEta); + wEta = weightFromSparse(eDWEta); } // w_charge(pt,eta,charge): if (pw.fUseDiffChargeWeights[wChargePtAxis] && pw.fUseDiffChargeWeights[wChargeEtaAxis] && pw.fUseDiffChargeWeights[wChargeChargeAxis]) { - wCharge = WeightFromSparse(eDWCharge); + wCharge = weightFromSparse(eDWCharge); } // ****) finally, fill: - FillqvectorFromSparse(PT_ETA_CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pt,eta,charge) filled in global bin to which this (pt,eta,charge) corresponds + fillqvectorFromSparse(PT_ETA_CHARGEq, bin, wPhi * wPt * wEta * wCharge); // weighted q(pt,eta,charge) filled in global bin to which this (pt,eta,charge) corresponds } if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void Fillqvectors() +} // void fillqvectors() //============================================================ -void FillqvectorFromSparse(const eqvectorKine& kineVarChoice, const int& bin, const double& dWeight) +void fillqvectorFromSparse(const EnqvectorKine& kineVarChoice, const int& bin, const double& dWeight) { // TBI 20260211 add a comment + document few example use cases if (tc.fVerboseForEachParticle) { - StartFunction(__FUNCTION__); - LOGF(info, "\033[1;32m kineVarChoice = %d (%s)\033[0m", static_cast(kineVarChoice), StringKineMap(kineVarChoice).Data()); + startFunction(__FUNCTION__); + LOGF(info, "\033[1;32m kineVarChoice = %d (%s)\033[0m", static_cast(kineVarChoice), stringKineMap(kineVarChoice).Data()); LOGF(info, "\033[1;32m bin = %d\033[0m", bin); LOGF(info, "\033[1;32m dWeight = %f\033[0m", dWeight); } @@ -19555,7 +19688,7 @@ void FillqvectorFromSparse(const eqvectorKine& kineVarChoice, const int& bin, co if (es.fCalculateEtaSeparations && qv.fCalculateqvectorsKineEtaSeparations[kineVarChoice]) { // yes, I have decoupled this one from if (qv.fCalculateQvectors) if (kineVarChoice == ETAq || kineVarChoice == PT_ETAq || kineVarChoice == ETA_CHARGEq || kineVarChoice == PT_ETA_CHARGEq) { - LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice == %s . This doesn't make any sense in this context => eta separations cannot be used for differential vectors vs. eta (either 1D or 2D or 3D case). \033[0m", __FUNCTION__, __LINE__, StringKineMap(kineVarChoice).Data()); + LOGF(fatal, "\033[1;31m%s at line %d : kineVarChoice == %s . This doesn't make any sense in this context => eta separations cannot be used for differential vectors vs. eta (either 1D or 2D or 3D case). \033[0m", __FUNCTION__, __LINE__, stringKineMap(kineVarChoice).Data()); } if (pbyp.fEta < 0.) { @@ -19588,40 +19721,40 @@ void FillqvectorFromSparse(const eqvectorKine& kineVarChoice, const int& bin, co } // if(es.fCalculateEtaSeparations) if (tc.fVerboseForEachParticle) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void FillqvectorFromSparse(eqvectorKine kineVarChoice, const int& bin, const double& dWeight) +} // void fillqvectorFromSparse(EnqvectorKine kineVarChoice, const int& bin, const double& dWeight) //============================================================ -void CalculateEverything() +void calculateEverything() { // Calculate everything for selected events and particles. // Remark: Data members for Q-vectors (both integrated and differential), containers for nested loops, etc., must all be filled when this function is called. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) Calculate multiparticle correlations (standard, isotropic, same harmonic): if (qv.fCalculateQvectors && mupa.fCalculateCorrelations) { - this->CalculateCorrelations(); + this->calculateCorrelations(); } // *) Calculate differential ("kine") multiparticle correlations: // Remark: vs. pt, vs. eta, etc., are all calculated here if (qv.fCalculateQvectors && mupa.fCalculateCorrelationsAsFunctionOf[AFO_PT]) { - this->CalculateKineCorrelations(AFO_PT); + this->calculateKineCorrelations(AFO_PT); } if (qv.fCalculateQvectors && mupa.fCalculateCorrelationsAsFunctionOf[AFO_ETA]) { - this->CalculateKineCorrelations(AFO_ETA); + this->calculateKineCorrelations(AFO_ETA); } // *) Calculate Test0: TBI 20240110 name convention // Remark: integrated, vs. M and vs. centrality are all calculated here if (qv.fCalculateQvectors && t0.fCalculateTest0) { - this->CalculateTest0(); + this->calculateTest0(); } // *) Calculate kine Test0: @@ -19629,71 +19762,71 @@ void CalculateEverything() // **) 1D kine: // ***) cases for which 1D vs. pt calculus is needed: if (qv.fCalculateQvectors && qv.fCalculateqvectorsKine[PTq]) { // TBI 20250601 do I really need here qv.fCalculateQvectors - this->CalculateKineTest0Ndim(PTq, 1); + this->calculateKineTest0Ndim(PTq, 1); } // ***) cases for which 1D vs. eta calculus is needed: if (qv.fCalculateQvectors && qv.fCalculateqvectorsKine[ETAq]) { // TBI 20250601 do I really need here qv.fCalculateQvectors - this->CalculateKineTest0Ndim(ETAq, 1); + this->calculateKineTest0Ndim(ETAq, 1); } // ***) cases for which 1D vs. charge calculus is needed: if (qv.fCalculateQvectors && qv.fCalculateqvectorsKine[CHARGEq]) { // TBI 20250601 do I really need here qv.fCalculateQvectors - this->CalculateKineTest0Ndim(CHARGEq, 1); + this->calculateKineTest0Ndim(CHARGEq, 1); } // ... // **) 2D kine: // ***) cases for which 2D vs. (pt,eta) calculus is needed: if (qv.fCalculateQvectors && qv.fCalculateqvectorsKine[PT_ETAq]) { // TBI 20250601 do I really need here qv.fCalculateQvectors - this->CalculateKineTest0Ndim(PT_ETAq, 2); + this->calculateKineTest0Ndim(PT_ETAq, 2); } // ***) cases for which 2D vs. (pt,charge) calculus is needed: if (qv.fCalculateQvectors && qv.fCalculateqvectorsKine[PT_CHARGEq]) { // TBI 20250601 do I really need here qv.fCalculateQvectors - this->CalculateKineTest0Ndim(PT_CHARGEq, 2); + this->calculateKineTest0Ndim(PT_CHARGEq, 2); } // ***) cases for which 2D vs. (eta,charge) calculus is needed: if (qv.fCalculateQvectors && qv.fCalculateqvectorsKine[ETA_CHARGEq]) { // TBI 20250601 do I really need here qv.fCalculateQvectors - this->CalculateKineTest0Ndim(ETA_CHARGEq, 2); + this->calculateKineTest0Ndim(ETA_CHARGEq, 2); } // ... // **) 3D kine: // ***) cases for which 3D vs. (pt,eta,charge) calculus is needed: if (qv.fCalculateQvectors && qv.fCalculateqvectorsKine[PT_ETA_CHARGEq]) { // TBI 20250601 do I really need here qv.fCalculateQvectors - this->CalculateKineTest0Ndim(PT_ETA_CHARGEq, 3); + this->calculateKineTest0Ndim(PT_ETA_CHARGEq, 3); } // ... // *) Calculate nested loops: if (nl.fCalculateNestedLoops) { - this->CalculateNestedLoops(); + this->calculateNestedLoops(); if (mupa.fCalculateCorrelations) { // I do not have option here for Test0, because in Test0 I cross-check either e-by-e with CustomNestedLoops or // for all events with IV + fRescaleWithTheoreticalInput = true - this->ComparisonNestedLoopsVsCorrelations(); // I call it here, so comparison is performed cumulatively after each event. The final printout corresponds to all events. + this->comparisonNestedLoopsVsCorrelations(); // I call it here, so comparison is performed cumulatively after each event. The final printout corresponds to all events. } } // *) Calculate correlations with eta separations: if (es.fCalculateEtaSeparations) { - this->CalculateEtaSeparations(); + this->calculateEtaSeparations(); if (es.fCalculateEtaSeparationsAsFunctionOf[AFO_PT]) { - this->CalculateKineEtaSeparationsNdim(PTq, 1); + this->calculateKineEtaSeparationsNdim(PTq, 1); } if (es.fCalculateEtaSeparationsAsFunctionOf[AFO_CHARGE]) { - this->CalculateKineEtaSeparationsNdim(CHARGEq, 1); + this->calculateKineEtaSeparationsNdim(CHARGEq, 1); } } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void CalculateEverything() +} // void calculateEverything() //============================================================ -void SkipThisRun() +void skipThisRun() { // Skip or not the current run from furher processing. @@ -19701,7 +19834,7 @@ void SkipThisRun() // b) Do the check. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // a) Insanity checks: if (tc.fRunNumber.EqualTo("")) { @@ -19720,15 +19853,15 @@ void SkipThisRun() } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // void SkipThisRun() +} // void skipThisRun() //============================================================ -template -void MainLoopOverParticles(T const& tracks) +template +void mainLoopOverParticles(T const& tracks) { // This is the main loop over particles, in which Q-vectors (both integrated and differential) and particle histograms are filled, particle cuts applied, etc. @@ -19740,7 +19873,7 @@ void MainLoopOverParticles(T const& tracks) // *) To process only simulated Run 3, use processSim(...), i.e. set field "processSim": "true" in json config // Remark #2: - // *) To process Run 1 and Run 2 converted data, use in the same spirit e.g. processRec_Run2(...), i.e. set field "processRec_Run2": "true" in json config + // *) To process Run 1 and Run 2 converted data, use in the same spirit e.g. processRecRun2(...), i.e. set field "processRecRun2": "true" in json config // Remark #3: // *) There is also processTest(...), to process data with minimum subscription to the tables. To use it, set field "processTest": "true" in json config @@ -19749,10 +19882,10 @@ void MainLoopOverParticles(T const& tracks) // *) There is also processQA(...), to process data with maximum subscription to the tables (use for Run 3 only). To use it, set field "processQA: "true" in json config // Remark #5: - // *) Switch ProcessHepMChi(...) amounts at the moment to calling one dedicated function + calling Steer for "RecSim", so no special care is needed here for that switch. + // *) Switch ProcessHepMChi(...) amounts at the moment to calling one dedicated function + calling steer for "RecSim", so no special care is needed here for that switch. if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); } // *) If random access of tracks from collection is requested, use Fisher-Yates algorithm to generate random indices: @@ -19760,7 +19893,7 @@ void MainLoopOverParticles(T const& tracks) if (tc.fRandomIndices) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } - this->RandomIndices(tracks.size()); + this->randomIndices(tracks.size()); if (!tc.fRandomIndices) { LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); } @@ -19786,57 +19919,57 @@ void MainLoopOverParticles(T const& tracks) } // *) Skip track objects which are not valid tracks (e.g. Run 2 and 1 tracklets, etc.): - if (!ValidTrack(track)) { + if (!validTrack(track)) { continue; } // *) Fill particle histograms before particle cuts: if (ph.fFillParticleHistograms || ph.fFillParticleHistograms2D || qa.fFillQAParticleHistograms2D) { - FillParticleHistograms(track, eBefore); + fillParticleHistograms(track, eBefore); } // *) Particle cuts counters (use only during QA, as this is computationally heavy): if (pc.fUseParticleCutCounterAbsolute || pc.fUseParticleCutCounterSequential) { - ParticleCutsCounters(track); + particleCutsCounters(track); } // *) Particle cuts: - if (!ParticleCuts(track, eCut)) { // Main call for event cuts. + if (!particleCuts(track, eCut)) { // Main call for event cuts. continue; // not return!! } // *) Fill particle histograms after particle cuts: if (ph.fFillParticleHistograms || ph.fFillParticleHistograms2D || qa.fFillQAParticleHistograms2D) { - FillParticleHistograms(track, eAfter); + fillParticleHistograms(track, eAfter); } // *) Intitialize global (yes, as of 20250718, I promoted 'em to data members, to gain efficiency) kinematic variables: - // Remark: for "eRecSim" processing, kinematics is taken from "reconstructed". + // Remark: for "EnRecSim" processing, kinematics is taken from "reconstructed". pbyp.fPhi = track.phi(); pbyp.fPt = track.pt(); pbyp.fEta = track.eta(); pbyp.fCharge = track.sign(); - // Remark: Keep in sync all calls and flags below with the ones in InternalValidation(). + // Remark: Keep in sync all calls and flags below with the ones in internalValidation(). // *) Integrated Q-vectors: if (qv.fCalculateQvectors || es.fCalculateEtaSeparations) { // This is now the new approach, with sparse histograms: // **) particle arguments are passed by reference // **) event observables (centrality, vertex z, ...), I do not need to pass as arguments, as I have data members for them (ebye.fCentrality, ebye.Vz, ...) - // **) I decide within FillQvectorFromSparse(...) whether and which weights are used. So yes, I use this one, despite its name, even when weights are NOT used + // **) I decide within fillQvectorFromSparse(...) whether and which weights are used. So yes, I use this one, despite its name, even when weights are NOT used // (there is no real performance penalty) - // **) Legacy function FillQvector(...) is obsolete as of 20250714, since I can get both integrated and differential weights from sparse histograms - this->FillQvectorFromSparse(); + // **) Legacy function fillQvector(...) is obsolete as of 20250714, since I can get both integrated and differential weights from sparse histograms + this->fillQvectorFromSparse(); } - // *) Differential q-vectors (keep in sync with the code in InternalValidation()): + // *) Differential q-vectors (keep in sync with the code in internalValidation()): // TBI 20260210 I need here a flag if this calculus is needed at all - this->Fillqvectors(); // within this function, i call FillqvectorFromSparse(...), for each differential q-vector separately + this->fillqvectors(); // within this function, i call fillqvectorFromSparse(...), for each differential q-vector separately - // *) Fill nested loops containers (integrated => I fill kine containers for nested loops in FillqvectorNdim(...)): + // *) Fill nested loops containers (integrated => I fill kine containers for nested loops in fillqvectorNdim(...)): if (nl.fCalculateNestedLoops || nl.fCalculateCustomNestedLoops) { - this->FillNestedLoopsContainers(ebye.fSelectedTracks); // all 4 arguments are passed by reference => not true any longer, I have now 4 pbyp data members + this->fillNestedLoopsContainers(ebye.fSelectedTracks); // all 4 arguments are passed by reference => not true any longer, I have now 4 pbyp data members } // *) Counter of selected tracks in the current event: @@ -19865,14 +19998,14 @@ void MainLoopOverParticles(T const& tracks) } if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } -} // template void MainLoopOverParticles(T const& tracks) { +} // template void mainLoopOverParticles(T const& tracks) { //============================================================ -template -void Steer(T1 const& collision, T2 const& bcs, T3 const& tracks) +template +void steer(T1 const& collision, T2 const& bcs, T3 const& tracks) { // This is the only function to be called in processRec(...), processRecSim(...), and processSim(...). // All analysis workflow is defined step-by-step here, via dedicated function calls. @@ -19881,43 +20014,40 @@ void Steer(T1 const& collision, T2 const& bcs, T3 const& tracks) // :ss if (tc.fVerbose) { - StartFunction(__FUNCTION__); + startFunction(__FUNCTION__); + // *) Print environment: + printEnvironment(); } // memStatus: ~50K (without differential q-vectors and eta separations) - // *) Print environment: - if (tc.fVerbose) { - printEnvironment(); - } - // *) Dry run: if (tc.fDryRun) { - EventCounterForDryRun(eFill); - EventCounterForDryRun(ePrint); - Preprocess(collision, bcs); // yes, so that e.g. I can only test if the particle and centrality weights were correctly fetched from external file and initialized locally into data members + eventCounterForDryRun(eFill); + eventCounterForDryRun(ePrint); + preprocess(collision, bcs); // yes, so that e.g. I can only test if the particle and centrality weights were correctly fetched from external file and initialized locally into data members return; } // memStatus: ~50K (without differential q-vectors and eta separations) // *) Reset event-by-event quantities: TBI 20240430 I do not need this call also here really, but it doesn't hurt either... - ResetEventByEventQuantities(); + resetEventByEventQuantities(); // *) Only do internal validation for all implemented correlators against the theoretical values: if (iv.fUseInternalValidation) { - InternalValidation(); + internalValidation(); return; } // *) Global timestamp: if (tc.fUseStopwatch) { - LOGF(info, "\033[1;32m=> Global timer: Steer begins ... %.6f\033[0m", tc.fTimer[eGlobal]->RealTime()); + LOGF(info, "\033[1;32m=> Global timer: steer begins ... %.6f\033[0m", tc.fTimer[eGlobal]->RealTime()); tc.fTimer[eGlobal]->Continue(); // yes } // *) Do all thingies before starting to process data from this collision (e.g. cut on number of events (both total and selected), fetch the run number, etc.): - Preprocess(collision, bcs); + preprocess(collision, bcs); // *) It was explicitly requested, skip this particular run: if (tc.fSkipRun) { @@ -19926,115 +20056,112 @@ void Steer(T1 const& collision, T2 const& bcs, T3 const& tracks) } // *) Determine collision reference multiplicity: - DetermineReferenceMultiplicity(collision); + determineReferenceMultiplicity(collision); // *) Determine collision centrality: // Remark: I determine also IP here. - DetermineCentrality(collision); + determineCentrality(collision); // *) Determine collision occupancy: - DetermineOccupancy(collision); + determineOccupancy(collision); // *) Determine collision interaction rate and current run duration: - DetermineInteractionRateAndCurrentRunDuration(collision, bcs); // TBI 20250414 temporary commented out, because of memory blow-up + determineInteractionRateAndCurrentRunDuration(collision, bcs); // TBI 20250414 temporary commented out, because of memory blow-up // *) Determine vertex z position: - DetermineVertexZ(collision); + determineVertexZ(collision); // memStatus: ~50K (without differential q-vectors and eta separations) // *) Determine additional QA thingies: if (qa.fFillQAEventHistograms2D || qa.fFillQAParticleHistograms2D || qa.fFillQAParticleEventHistograms2D || qa.fFillQACorrelationsVsHistograms2D || qa.fFillQACorrelationsVsInteractionRateVsProfiles2D) { // Remark: I implement ideally here only the getters for which the subscription to additional non-standard tables was needed for QA purposes. - DetermineQAThingies(collision, bcs); + determineQAThingies(collision, bcs); } // *) Fill event histograms before event cuts: if (eh.fFillEventHistograms || qa.fFillQAEventHistograms2D || qa.fFillQAParticleEventHistograms2D) { // Remark: I do not check above the flag fFillQACorrelationsVsHistograms2D, because as a part of QA I calculate <2> only after cuts in any case - FillEventHistograms(collision, tracks, eBefore); + fillEventHistograms(collision, tracks, eBefore); } // *) Print info on the current event number (total, before cuts): if (tc.fVerboseEventCounter) { - PrintEventCounter(eBefore); + printEventCounter(eBefore); } // *) Event cuts counters (use only during QA, as this is computationally heavy): if (ec.fUseEventCutCounterAbsolute || ec.fUseEventCutCounterSequential) { - EventCutsCounters(collision, tracks); + eventCutsCounters(collision, tracks); } // *) Event cuts: - if (!EventCuts(collision, tracks, eCut)) { // Main call for event cuts + if (!eventCuts(collision, tracks, eCut)) { // Main call for event cuts return; } // memStatus: ~50K (without differential q-vectors and eta separations) // *) Main loop over particles: - MainLoopOverParticles(tracks); // memStatus: so here I invest ~20K, as of 20250530 + mainLoopOverParticles(tracks); // memStatus: so here I invest ~20K, as of 20250530 // memStatus: ~70K (without differential q-vectors and eta separations) // *) Determine multiplicity of this event, for all "vs. mult" results: - DetermineMultiplicity(); + determineMultiplicity(); // *) Remaining event cuts which can be applied only after the loop over particles is performed: - if (!RemainingEventCuts()) { - // yes, I need to remove particles from ParticleHistograms, which were filled in the MainLoopOverParticles also for events which didn't survive RemainingEventCuts - BanishmentLoopOverParticles(tracks); - ResetEventByEventQuantities(); + if (!remainingEventCuts()) { + // yes, I need to remove particles from ParticleHistograms, which were filled in the mainLoopOverParticles also for events which didn't survive remainingEventCuts + banishmentLoopOverParticles(tracks); + resetEventByEventQuantities(); return; } // *) Fill event histograms after event AND particle cuts: if (eh.fFillEventHistograms || qa.fFillQAEventHistograms2D || qa.fFillQAParticleEventHistograms2D || qa.fFillQACorrelationsVsHistograms2D) { - FillEventHistograms(collision, tracks, eAfter); + fillEventHistograms(collision, tracks, eAfter); } // *) Fill subevent multiplicities: // Remark: I can call this one only after Qa and Qb vectors are filled, and after all particle and event cuts: if (es.fCalculateEtaSeparations) { - FillSubeventMultiplicities(); + fillSubeventMultiplicities(); } // *) Calculate everything for selected events and particles: - CalculateEverything(); + calculateEverything(); // memStatus: ~72K (without differential q-vectors and eta separations) // *) Reset event-by-event quantities: - ResetEventByEventQuantities(); + resetEventByEventQuantities(); // *) QA: if (qa.fCheckUnderflowAndOverflow) { // TBI 20240507 introduce eventualy common function QA(), within which I will call all specific QA functions - CheckUnderflowAndOverflow(); + checkUnderflowAndOverflow(); } // *) Print info on the current event number after cuts: if (tc.fVerboseEventCounter) { - PrintEventCounter(eAfter); + printEventCounter(eAfter); } // *) Per request, print content of event cut counters: if (ec.fPrintCutCounterContent) { - PrintCutCounterContent(); + printCutCounterContent(); } // *) Global timestamp: if (tc.fUseStopwatch) { - LOGF(info, "\033[1;32m=> Global timer: Steer ends ... %.6f\033[0m\n", tc.fTimer[eGlobal]->RealTime()); + LOGF(info, "\033[1;32m=> Global timer: steer ends ... %.6f\033[0m\n", tc.fTimer[eGlobal]->RealTime()); tc.fTimer[eGlobal]->Continue(); // yes } - // *) Print environment: if (tc.fVerbose) { + // *) Print environment: printEnvironment(); - } - - if (tc.fVerbose) { - ExitFunction(__FUNCTION__); + exitFunction(__FUNCTION__); } // memStatus (summary): Status at 20260218 (wf-13.sh + file 2023/LHC23zzh/544116/apass5/0140/o2_ctf_run00544116_orbit0034437888_tf0000000601_epn103/008/AO2D.root) @@ -20059,6 +20186,6 @@ void Steer(T1 const& collision, T2 const& bcs, T3 const& tracks) // ~110K (all object declaration + 1D + 2D + 3D kine objects (diff. q-vectors in fine kine bins) + all calculus and 1D histograms filled, standard labels) // ~125K (all object declaration + 1D + 2D + 3D kine objects (diff. q-vectors in fine kine bins) + all calculus and 1D histograms filled, Set_0 labels) -} // template void Steer(T1 const* collision, T2 const* tracks) +} // template void steer(T1 const* collision, T2 const* tracks) #endif // PWGCF_MULTIPARTICLECORRELATIONS_CORE_MUPA_MEMBERFUNCTIONS_H_ diff --git a/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt index 05bb4edb850..32c916c2275 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/MultiparticleCorrelations/Tasks/CMakeLists.txt @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. o2physics_add_dpl_workflow(multiparticle-correlations-ab - SOURCES multiparticle-correlations-ab.cxx + SOURCES multiparticleCorrelationsAb.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) @@ -23,3 +23,23 @@ o2physics_add_dpl_workflow(three-particle-correlations SOURCES threeParticleCorrelations.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(multiharmonic-correlations + SOURCES multiharmonicCorrelations.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(multiparticle-cumulants + SOURCES multiparticleCumulants.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(multiparticle-correlations-mei + SOURCES multiparticleCorrelationsMei.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(multiparticle-correlations-dm + SOURCES multiparticleCorrelationsDm.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiharmonicCorrelations.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiharmonicCorrelations.cxx new file mode 100644 index 00000000000..6768b8dd5bc --- /dev/null +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiharmonicCorrelations.cxx @@ -0,0 +1,988 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" // needed for aod::TracksDCA table + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Definitions of join tables for Run 3 analysis: +using EventSelection = soa::Join; +using CollisionRec = soa::Join::iterator; // use in json "isMC": "true" for "event-selection-task" +using CollisionRecSim = soa::Join::iterator; +using CollisionSim = aod::McCollision; +using TracksRec = soa::Join; +using TrackRec = soa::Join::iterator; +using TracksRecSim = soa::Join; // + use in json "isMC" : "true" +using TrackRecSim = soa::Join::iterator; +using TracksSim = aod::McParticles; +using TrackSim = aod::McParticles::iterator; + +using namespace std; + +// *) Define enums: +enum eRecSim { eRec = 0, + eSim, + eRecAndSim }; + +enum eProcess { + eProcessRec = 0, // Run 3, only reconstructed + eProcessRecSim, // Run 3, both reconstructed and simulated + eProcessSim, // Run 3, only simulated + eProcess_N +}; + +enum eEventHistograms { + eVertexZ = 0, + ePt, + eEventHistograms_N +}; + +enum eCut { + eBefore = 0, + eAfter, + eCut_N +}; + +// *) Main task: +struct MultiharmonicCorrelations { // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root + Service ccdb; + + // *) Base TList to hold all output objects: + TString sBaseListName = "Default list name"; + OutputObj fBaseList{sBaseListName.Data(), + OutputObjHandlingPolicy::AnalysisObject, + OutputObjSourceType::OutputObjSource}; + + // *) Define configurables: + Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; // example for built-in type (float, string, etc.) + Configurable> cfPtBins{"cfPtBins", {1000, 0., 8.}, "nPtBins, ptMin, ptMax"}; // example for an array + Configurable> cfPhiBins{"cfPhiBins", {360, 0., o2::constants::math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfCentrBins{"cfCentrBins", {80, 0., 80.}, "nCentrBins, centrMin, centrMax"}; + Configurable> cfXBins{"cfXBins", {1000, -0.04, -0.01}, "nXBins, xMin, xMax"}; + Configurable> cfYBins{"cfYBins", {1000, -0.01, 0.006}, "nYBins, yMin, yMax"}; + Configurable> cfZBins{"cfZBins", {1000, -20., 20.}, "nZBins, zMin, zMax"}; + Configurable> cfMultBins{"cfMultBins", {50, 0, 2e4}, "nMultBins, multMin, multMax"}; + Configurable> cfMselBins{"cfMselBins", {50, 0, 1e3}, "nMselBins, mselMin, mselMax"}; + Configurable> cfTPCnclsBins{"cfTPCnclsBins", {100, 0., 200.}, "ntpcnclsBins, tpnclsMin, tpcnclsMax"}; + Configurable> cfDCAxyBins{"cfDCAxyBins", {1000, -0.5, 0.5}, "ndcaxyBins, dcaxyMin, dcaxyMax"}; + Configurable> cfDCAzBins{"cfDCAzBins", {1000, -3., 3.}, "ndcazBins, dcazMin, dcazMax"}; + Configurable> cfNcontrBins{"cfNcontrBins", {100, 0., 10000.}, "nNContrBins, NContrMin, NContrMax"}; + + Configurable cfCent{"cfCent", "FT0C", "centrality estimator"}; + Configurable cfMult{"cfMult", "TPC", "multiplicity"}; + Configurable cfQA{"cfQA", true, "quality assurance"}; + Configurable cfInitsim{"cfInitsim", false, "init histograms of sim"}; + Configurable cfUseWeights{"cfUseWeights", true, "use weights"}; + Configurable cfToyModel{"cfToyModel", true, "phi-distribution from toy model"}; + Configurable cfNest{"cfNest", true, "nested loops"}; + Configurable cfTechcuts{"cfTechcuts", true, "technical cuts"}; + + Configurable> cfVertexZ{"cfVertexZ", {-10, 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz < max"}; + Configurable> cfPt{"cfPt", {0.2, 5.0}, "transverse momentum range"}; + Configurable> cfEta{"cfEta", {-0.8, 0.8}, "eta range"}; + Configurable> cfDCAxy{"cfDCAxy", {-0.5, 0.5}, "dca xy range"}; + Configurable> cfDCAz{"cfDCAz", {-0.2, 0.2}, "dca z range"}; + Configurable cftpcNClsFoundmin{"cftpcNClsFoundmin", 70., "tpcNClsFoundmin"}; + Configurable cftpcChi2NClmax{"cftpcChi2NClmax", 4., "tpcChi2NClmax"}; + + Configurable> cfRuns{"cfRuns", {544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124}, "List of run numbers to analyze"}; + Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/p/pengchon/weightsfile06", "path to external ROOT file which holds all particle weights"}; + + // *) Define and initialize all data members to be called in the main process* functions: + // **) Task configuration: + struct TaskConfiguration { + bool fProcess[eProcess_N] = {false}; // Set what to process. See enum eProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. + bool fDryRun = false; // book all histos and run without filling and calculating anything + } tc; // you have to prepend "tc." for all objects name in this group later in the code + + // **) Particle histograms: + struct ParticleHistograms { + TList* fParticleHistogramsList = NULL; //! vecphi; + std::vector vecwei; + TProfile* nestedLoops[maxHarmonic] = {NULL}; + TProfile* pv2_nest = NULL; + TProfile* pv3_nest = NULL; + TProfile* pv4_nest = NULL; + } cor; + + struct PhiHist { + TList* fPhiHistList = NULL; + std::unordered_map histMap; + } phih; + + struct WeightsHist { + TList* fWeightsHistList = NULL; + std::unordered_map weightsmap; + } wh; + + TObject* GetObjectFromList(TList* list, const char* objectName) + { + // Get TObject pointer from TList, even if it's in some nested TList. Foreseen + // to be used to fetch histograms or profiles from files directly. + // Some ideas taken from TCollection::ls() + // If you have added histograms directly to files (without TList's), then you can fetch them directly with + // file->Get("hist-name"). + + // Usage: TH1D *hist = (TH1D*) GetObjectFromList("some-valid-TList-pointer","some-object-name"); + + // Insanity checks: + if (!list) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (!objectName) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (0 == list->GetEntries()) { + return NULL; + } + + // The object is in the current base list: + TObject* objectFinal = list->FindObject(objectName); // the final object I am after + if (objectFinal) { + return objectFinal; + } + + // Otherwise, search for the object recursively in the nested lists: + TObject* objectIter; // iterator object in the loop below + TIter next(list); + while ((objectIter = next())) // double round braces are to silence the warnings + { + if (TString(objectIter->ClassName()).EqualTo("TList")) { + objectFinal = GetObjectFromList(reinterpret_cast(objectIter), objectName); + if (objectFinal) + return objectFinal; + } + } // while(objectIter = next()) + + return NULL; + + } // TObject* GetObjectFromList(TList *list, char *objectName) + + TH1F* GetHistogramWithWeights(const char* filePath, const char* runNumber) + { + // *) Return value: + TH1F* hist = NULL; + TList* baseList = NULL; // base top-level list in the TFile, e.g. named "ccdb_object" + TList* listWithRuns = NULL; // nested list with run-wise TList's holding run-specific weights + + // *) Determine from filePath if the file is on a local machine, or in home dir AliEn, or in CCDB: + // Algorithm: If filePath begins with "/alice/cern.ch/" then it's in the home dir AliEn; + // If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. Therefore, files in AliEn and CCDB must be specified with abs path; + // for local files both abs and relative paths are just fine. + bool bFileIsInAliEn = false; + bool bFileIsInCCDB = false; + + string pathstr = filePath; + const string pathalien = "/alice/cern.ch/"; + const string pathccdb = "/alice-ccdb.cern.ch/"; + if (pathstr.starts_with(pathalien)) { + bFileIsInAliEn = true; + } else if (pathstr.starts_with(pathccdb)) { + bFileIsInCCDB = true; + } + LOGF(info, "bFileIsInCCDB= %d", bFileIsInCCDB); + + if (bFileIsInAliEn) { + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); // do not forget to add #include to the preamble of your analysis task + if (!alien) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + TFile* weightsFile = TFile::Open(Form("alien://%s", filePath), "READ"); // yes, ROOT can open a file transparently, even if it's sitting in AliEn, with this specific syntax + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + weightsFile->GetObject("ccdb_object", baseList); + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + // Finally, from the top-level TList, get the desired nested TList => the technical problem here is that it can be nested at any level, + // for thare there is a helper utility function GetObjectFromList(...) , see its implementation further below + listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + } else if (bFileIsInCCDB) { + // File you want to access is in your home dir in CCDB: + // Remember that here I do not access the file; instead, I directly access the object in that file. + ccdb->setURL("http://alice-ccdb.cern.ch"); // to be able to use "ccdb" this object in your analysis task, see 4b/ below + baseList = reinterpret_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) + + } else { + // this is the local case: + // Check if the external ROOT file exists at the specified path: + if (gSystem->AccessPathName(filePath, kFileExists)) { + LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath,kFileExists)), filePath = %s \033[0m", filePath); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + TFile* weightsFile = TFile::Open(filePath, "READ"); + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + weightsFile->GetObject("ccdb_object", baseList); + + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = reinterpret_cast(GetObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + // baseList->ls(); + LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__ /*, tc.fRunNumber.Data()*/); + } + } + + } // end of else + + // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: + listWithRuns->ls(); + + hist = reinterpret_cast(listWithRuns->FindObject("histWithEfficiencyCorrections")); + if (!hist) { + LOGF(fatal, "no histweight"); + } + + // Once you have a valid pointer to "hist", add these technical lines: + hist->SetDirectory(0); // remove ownerhip from an external file + TH1F* histClone = reinterpret_cast(hist->Clone()); // yes, I have to clone here + + return histClone; + + } // end of TH1F* GetHistogramWithWeights(const char* filePath, const char* runNumber) { + + // ... + + // *) Define all member functions to be called in the main process* functions: + template + bool EventCuts(T1 const& collision) + { + vector vertexZ = cfVertexZ.value; + float vertexZmin = static_cast(vertexZ[0]); + float vertexZmax = static_cast(vertexZ[1]); + float posZ = collision.posZ(); + float NContrcut = 2.; + if (posZ < vertexZmin || posZ > vertexZmax || (!collision.sel8()) || collision.numContrib() < NContrcut) + return false; + if (cfTechcuts) { + if (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard) || !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard) || !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC) || !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) || !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) || !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) + return false; + } + return true; + } + + template + bool ParticleCuts(T const& track) + { + vector Pt = cfPt.value; + float ptcutmin = static_cast(Pt[0]); + float ptcutmax = static_cast(Pt[1]); + float pt = track.pt(); + vector Eta = cfEta.value; + float etacutmin = static_cast(Eta[0]); + float etacutmax = static_cast(Eta[1]); + float eta = track.eta(); + vector dcaXY = cfDCAxy.value; + float dcaxycutmin = static_cast(dcaXY[0]); + float dcaxycutmax = static_cast(dcaXY[1]); + float dcaxy = track.dcaXY(); + vector dcaZ = cfDCAz.value; + float dcazcutmin = static_cast(dcaZ[0]); + float dcazcutmax = static_cast(dcaZ[1]); + float dcaz = track.dcaZ(); + if (pt < ptcutmin || pt > ptcutmax || eta < etacutmin || eta > etacutmax || dcaxy < dcaxycutmin || dcaxy > dcaxycutmax || dcaz < dcazcutmin || dcaz > dcazcutmax || (!track.isPrimaryTrack()) || (!track.isPVContributor()) || track.tpcNClsFound() < cftpcNClsFoundmin.value || track.tpcChi2NCl() > cftpcChi2NClmax) + return false; + return true; + } + + TComplex Q(int n, int p) + { + // Using the fact that Q{-n,p} = Q{n,p}^*. + if (n >= 0) { + return cor.Qvector[n][p]; + } + return TComplex::Conjugate(cor.Qvector[-n][p]); + } + + TComplex Two(Int_t n1, Int_t n2) + { + // Generic two-particle correlation . + TComplex two = Q(n1, 1) * Q(n2, 1) - Q(n1 + n2, 2); + return two; + + } // TComplex Two(Int_t n1, Int_t n2) + TComplex Four(Int_t n1, Int_t n2, Int_t n3, Int_t n4) + { // Generic four-particle correlation . + TComplex four = Q(n1, 1) * Q(n2, 1) * Q(n3, 1) * Q(n4, 1) - Q(n1 + n2, 2) * Q(n3, 1) * Q(n4, 1) - Q(n2, 1) * Q(n1 + n3, 2) * Q(n4, 1) - Q(n1, 1) * Q(n2 + n3, 2) * Q(n4, 1) + 2. * Q(n1 + n2 + n3, 3) * Q(n4, 1) - Q(n2, 1) * Q(n3, 1) * Q(n1 + n4, 2) + Q(n2 + n3, 2) * Q(n1 + n4, 2) - Q(n1, 1) * Q(n3, 1) * Q(n2 + n4, 2) + Q(n1 + n3, 2) * Q(n2 + n4, 2) + 2. * Q(n3, 1) * Q(n1 + n2 + n4, 3) - Q(n1, 1) * Q(n2, 1) * Q(n3 + n4, 2) + Q(n1 + n2, 2) * Q(n3 + n4, 2) + 2. * Q(n2, 1) * Q(n1 + n3 + n4, 3) + 2. * Q(n1, 1) * Q(n2 + n3 + n4, 3) - 6. * Q(n1 + n2 + n3 + n4, 4); + return four; + } // TComplex Four(Int_t n1, Int_t n2, Int_t n3, Int_t n4) + + static double pdf(const double* x, const double* par) + { + double y = 1; + int harm = 6; + for (int i = 0; i < harm; i = i + 1) { + y = y + 2 * (0.04 + (i + 1.) * 0.01) * TMath::Cos((i + 1) * (x[0] - par[0])); + } + return y; + } + + template + void Steer(T1 const& collision, T2 const& tracks) + { + // Dry run: + if (tc.fDryRun) { + return; + } + // Print current run number: + int currentRun = collision.bc().runNumber(); + auto it = phih.histMap.find(currentRun); + auto histweight = wh.weightsmap.find(currentRun); + float centr = 0, M = 0., msel = 0.; + // TF1* f = new TF1("f", pdf, 0, TMath::TwoPi(), 1); + TF1* f = new TF1("f", + "1 +" + "2 * (0.05) * cos(1 * (x - [0])) +" + "2 * (0.06) * cos(2 * (x - [0])) +" + "2 * (0.07) * cos(3 * (x - [0])) +" + "2 * (0.08) * cos(4 * (x - [0])) +" + "2 * (0.09) * cos(5 * (x - [0])) +" + "2 * (0.10) * cos(6 * (x - [0]))", + 0, TMath::TwoPi()); + f->SetParameters(0.); + + if constexpr (rs == eRec || rs == eRecAndSim) { + if (cfCent.value == "FT0C") + centr = collision.centFT0C(); + else if (cfCent.value == "FT0M") + centr = collision.centFT0M(); + else if (cfCent.value == "FT0A") + centr = collision.centFT0A(); + + if (cfMult.value == "TPC") + M = collision.multTPC(); + else if (cfMult.value == "FV0M") + M = collision.multFV0M(); + else if (cfMult.value == "FT0C") + M = collision.multFT0C(); + else if (cfMult.value == "FT0M") + M = collision.multFT0M(); + else if (cfMult.value == "NTracksPV") + M = collision.multNTracksPV(); + + event.fHistX[eBefore]->Fill(collision.posX()); + event.fHistY[eBefore]->Fill(collision.posY()); + event.fHistZ[eBefore]->Fill(collision.posZ()); + event.fHistCentr[eBefore]->Fill(centr); + event.fHistMult[eBefore]->Fill(M); + event.fHistNContr[eBefore]->Fill(collision.numContrib()); + + event.fEventHistograms[eVertexZ][eRec][0]->Fill(collision.posZ()); + + // *) Event cuts: + float centrcut = 80.; + if (!EventCuts(collision) || centr > centrcut) { // Main call for event cuts + return; + } + + event.fHistX[eAfter]->Fill(collision.posX()); + event.fHistY[eAfter]->Fill(collision.posY()); + event.fHistZ[eAfter]->Fill(collision.posZ()); + event.fHistCentr[eAfter]->Fill(centr); + event.fHistMult[eAfter]->Fill(M); + event.fHistNContr[eAfter]->Fill(collision.numContrib()); + + event.fEventHistograms[eVertexZ][eRec][1]->Fill(collision.posZ()); + qa.fQAM_NC->Fill(M, collision.numContrib()); + + if constexpr (rs == eRecAndSim) { + auto mccollision = collision.mcCollision(); + float b = mccollision.impactParameter(); + float centrsim = o2::constants::math::PI * b * b / 7.71; + event.fHistX[eSim]->Fill(mccollision.posX()); + event.fHistY[eSim]->Fill(mccollision.posY()); + event.fHistZ[eSim]->Fill(mccollision.posZ()); + event.fEventHistograms[eVertexZ][eSim][1]->Fill(mccollision.posZ()); + event.fHistCentr[eSim]->Fill(centrsim); + qa.fQA->Fill(centrsim, centr); + centr = centrsim; + } + } + + // before loop over particles + float phi = 0, weight = 1.; + vector().swap(cor.vecphi); + vector().swap(cor.vecwei); + for (int ih = 0; ih < maxHarmonic; ih++) { + for (int ip = 0; ip < maxPower; ip++) { + cor.Qvector[ih][ip] = TComplex(0., 0.); + } + } + + /* + for (int ih = 0; ih < maxHarmonic; ih++) { + for (int ip = 0; ip < maxPower; ip++) { + LOGF(info, "Qvector[%d][%d]=%f, ", ih, ip, cor.Qvector[ih][ip].Rho2()); + } + LOGF(info, "\n"); + } + LOGF(info, "\n\n"); + */ + + // Main loop over particles: + for (const auto& track : tracks) { + + // Fill reconstructed ...: + float ptrec = 0., ptsim = 0.; + if constexpr (rs == eRec || rs == eRecAndSim) { + // Fill track pt distribution: + + pc.fHistPt[eBefore]->Fill(track.pt()); + pc.fHistPhi[eBefore]->Fill(track.phi()); + pc.fHistCharge[eBefore]->Fill(track.sign()); + pc.fHistTPCncls[eBefore]->Fill(track.tpcNClsFindable()); + pc.fHistTracksdcaXY[eBefore]->Fill(track.dcaXY()); + pc.fHistTracksdcaZ[eBefore]->Fill(track.dcaZ()); + + event.fEventHistograms[ePt][eRec][0]->Fill(track.pt()); + ptrec = track.pt(); + + // *) Particle cuts: + if (!ParticleCuts(track)) { // Main call for particle cuts. + continue; // not return!! + } + pc.fHistPt[eAfter]->Fill(track.pt()); + pc.fHistPhi[eAfter]->Fill(track.phi()); + pc.fHistCharge[eAfter]->Fill(track.sign()); + pc.fHistTPCncls[eAfter]->Fill(track.tpcNClsFindable()); + pc.fHistTracksdcaXY[eAfter]->Fill(track.dcaXY()); + pc.fHistTracksdcaZ[eAfter]->Fill(track.dcaZ()); + + event.fEventHistograms[ePt][eRec][1]->Fill(ptrec); + + phi = track.phi(); + if (cfToyModel) { + phi = f->GetRandom(); + } + cor.vecphi.push_back(phi); + if (it != phih.histMap.end()) { + it->second->Fill(phi); + } + + if (cfUseWeights) { + if (histweight != wh.weightsmap.end() && histweight->second) { + weight = histweight->second->GetBinContent(histweight->second->FindBin(phi)); + } else { + LOG(warning) << "No weights found for run " << currentRun << ", using weight=1"; + weight = 1; + } + } else { + weight = 1; + } + cor.vecwei.push_back(weight); + + // ... and corresponding MC truth simulated: + // See https://github.com/AliceO2Group/O2Physics/blob/master/Tutorials/src/mcHistograms.cxx + // See https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo + if constexpr (rs == eRecAndSim) { + if (!track.has_mcParticle()) { + LOGF(warning, " No MC particle for this track, skip..."); + return; + } + auto mcparticle = track.mcParticle(); // corresponding MC truth simulated particle + pc.fHistPt[eSim]->Fill(mcparticle.pt()); + event.fEventHistograms[ePt][eSim][0]->Fill(mcparticle.pt()); + ptsim = mcparticle.pt(); + event.fEventHistograms[ePt][eSim][0]->Fill(ptsim); + phi = mcparticle.phi(); + pc.fHistPhi[eSim]->Fill(mcparticle.phi()); + } // end of if constexpr (rs == eRecAndSim) + + } // if constexpr (rs == eRec || rs == eRecAndSim) + + // analysis in the loop over particle + msel = msel + 1; + for (int ih = 0; ih < maxHarmonic; ih++) { + for (int ip = 0; ip < maxPower; ip++) { + cor.Qvector[ih][ip] += TComplex(std::pow(weight, ip) * TMath::Cos(ih * phi), std::pow(weight, ip) * TMath::Sin(ih * phi)); + } + } + } // end of for (auto track: tracks) + event.fHistMsel[eAfter]->Fill(msel); + + if (cfNest) { + float phi1 = 0., phi2 = 0., weight1 = 1., weight2 = 2.; + for (Int_t c = 0; c < maxHarmonic; c++) { + delete cor.nestedLoops[c]; + cor.nestedLoops[c] = new TProfile("", "", 1, 0., 1.); + cor.nestedLoops[c]->Sumw2(); + } + for (int i1 = 0; i1 < static_cast(cor.vecphi.size()); i1++) { // nested loop of particles + phi1 = cor.vecphi[i1]; + weight1 = cor.vecwei[i1]; + for (int i2 = 0; i2 < static_cast(cor.vecphi.size()); i2++) { + if (i2 == i1) { + continue; + } + phi2 = cor.vecphi[i2]; + weight2 = cor.vecwei[i2]; + cor.nestedLoops[0]->Fill(0.5, TMath::Cos(2 * phi1 - 2 * phi2), weight1 * weight2); + cor.nestedLoops[1]->Fill(0.5, TMath::Cos(3 * phi1 - 3 * phi2), weight1 * weight2); + cor.nestedLoops[2]->Fill(0.5, TMath::Cos(4 * phi1 - 4 * phi2), weight1 * weight2); + } + } // end of two nested loop + } + + // calculate correlations + float Mmin = 4.; + if (msel < Mmin) + return; + float wTwo = Two(0, 0).Re(); + float wFour = Four(0, 0, 0, 0).Re(); + float four32 = Four(3, 2, -3, -2).Re() / wFour; + float four42 = Four(4, 2, -4, -2).Re() / wFour; + float v22 = Two(2, -2).Re() / wTwo; + float v32 = Two(3, -3).Re() / wTwo; + float v42 = Two(4, -4).Re() / wTwo; + if (std::isnan(v22) || std::isnan(v32) || std::isnan(v42) || std::isnan(four32) || std::isnan(four42)) { + LOGF(info, "\033[1;31m%s std::isnan(v22) || std::isnan(v32) || std::isnan(v42) || std::isnan(four32) || std::isnan(four42)\033[0m", __FUNCTION__); + LOGF(error, "v22 = %f\nv32 = %f\nv42 = %f\nfour32=%f\nv42 = %f\n", v22, v32, v42, four32, four42); + return; + } + + cor.pv22_centr->Fill(centr, v22, wTwo); + cor.pv32_centr->Fill(centr, v32, wTwo); + cor.pv42_centr->Fill(centr, v42, wTwo); + cor.pfour32_centr->Fill(centr, four32, wFour); + cor.pfour42_centr->Fill(centr, four42, wFour); + + if (cfNest) { + cor.pv2_nest->Fill(centr, cor.nestedLoops[0]->GetBinContent(1), wTwo); + cor.pv3_nest->Fill(centr, cor.nestedLoops[1]->GetBinContent(1), wTwo); + cor.pv4_nest->Fill(centr, cor.nestedLoops[2]->GetBinContent(1), wTwo); + + LOGF(info, "v22=%f, v22_nest=%f", v22, cor.nestedLoops[0]->GetBinContent(1)); + } + } // end of template void Steer(T1 const& collision, T2 const& tracks) + + // *) Initialize and book all objects: + void init(InitContext&) + { + + // ... code to book and initialize all analysis objects ... + + // *) Set automatically what to process, from an implicit variable "doprocessSomeProcessName" within a PROCESS_SWITCH clause: + tc.fProcess[eProcessRec] = doprocessRec; + tc.fProcess[eProcessRecSim] = doprocessRecSim; + tc.fProcess[eProcessSim] = doprocessSim; + + // *) Configure your task using configurables in the json file: + tc.fDryRun = cfDryRun; + + // *) Book base list: + TList* temp = new TList(); + temp->SetOwner(true); + fBaseList.setObject(temp); + + // *) Book and nest all other TLists: + pc.fParticleHistogramsList = new TList(); + pc.fParticleHistogramsList->SetName("ParticleHistograms"); + pc.fParticleHistogramsList->SetOwner(true); + fBaseList->Add(pc.fParticleHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + event.fEventHistogramsList = new TList(); + event.fEventHistogramsList->SetName("EventHistograms"); + event.fEventHistogramsList->SetOwner(true); + fBaseList->Add(event.fEventHistogramsList); + + qa.fQAList = new TList(); + qa.fQAList->SetName("QAHistograms"); + qa.fQAList->SetOwner(true); + fBaseList->Add(qa.fQAList); + + cor.fCorrelationVariablesList = new TList(); + cor.fCorrelationVariablesList->SetName("CorrelationVariables"); + cor.fCorrelationVariablesList->SetOwner(true); + fBaseList->Add(cor.fCorrelationVariablesList); + + phih.fPhiHistList = new TList(); + phih.fPhiHistList->SetName("PhiHistograms"); + phih.fPhiHistList->SetOwner(true); + fBaseList->Add(phih.fPhiHistList); + + wh.fWeightsHistList = new TList(); + wh.fWeightsHistList->SetName("WeightsHistograms"); + wh.fWeightsHistList->SetOwner(true); + fBaseList->Add(wh.fWeightsHistList); + + // *) Book pt distribution with binning defined through configurables in the json file: + vector l_pt_bins = cfPtBins.value; // define local array and initialize it from an array set in the configurables + vector l_phi_bins = cfPhiBins.value; + vector l_centr_bins = cfCentrBins.value; + vector l_x_bins = cfXBins.value; + vector l_y_bins = cfYBins.value; + vector l_z_bins = cfZBins.value; + vector l_mult_bins = cfMultBins.value; + vector l_msel_bins = cfMselBins.value; + vector l_tpcncls_bins = cfTPCnclsBins.value; + vector l_dcaxy_bins = cfDCAxyBins.value; + vector l_dcaz_bins = cfDCAzBins.value; + vector l_ncontr_bins = cfNcontrBins.value; + vector targetRuns = cfRuns.value; + int nBins = static_cast(l_pt_bins[0]); + int nBinsphi = static_cast(l_phi_bins[0]); + int nBinscentr = static_cast(l_centr_bins[0]); + int nBinsx = static_cast(l_x_bins[0]); + int nBinsy = static_cast(l_y_bins[0]); + int nBinsz = static_cast(l_z_bins[0]); + int nBinsmult = static_cast(l_mult_bins[0]); + int nBinsmsel = static_cast(l_msel_bins[0]); + int nBinscharge = 2; + int nBinstpcncls = static_cast(l_tpcncls_bins[0]); + int nBinsdcaxy = static_cast(l_dcaxy_bins[0]); + int nBinsdcaz = static_cast(l_dcaz_bins[0]); + int nBinsncontr = static_cast(l_ncontr_bins[0]); + + float min = l_pt_bins[1]; + float max = l_pt_bins[2]; + float minphi = l_phi_bins[1]; + float maxphi = l_phi_bins[2]; + float mincentr = l_centr_bins[1]; + float maxcentr = l_centr_bins[2]; + float minx = l_x_bins[1]; + float maxx = l_x_bins[2]; + float miny = l_y_bins[1]; + float maxy = l_y_bins[2]; + float minz = l_z_bins[1]; + float maxz = l_z_bins[2]; + float minmult = l_mult_bins[1]; + float maxmult = l_mult_bins[2]; + float minmsel = l_msel_bins[1]; + float maxmsel = l_msel_bins[2]; + float mincharge = -2.; + float maxcharge = 2.; + float mintpcncls = l_tpcncls_bins[1]; + float maxtpcncls = l_tpcncls_bins[2]; + float mindcaxy = l_dcaxy_bins[1]; + float maxdcaxy = l_dcaxy_bins[2]; + float mindcaz = l_dcaz_bins[1]; + float maxdcaz = l_dcaz_bins[2]; + float minncontr = l_ncontr_bins[1]; + float maxncontr = l_ncontr_bins[2]; + + const char* cevent[] = {"vertexZ", "Pt"}; + const char* cpro[] = {"rec", "sim"}; + const char* ccut[] = {"before", "after"}; + for (int i = 0; i < eEventHistograms_N; i++) { + for (int j = 0; j < eRecAndSim; j++) { + for (int k = 0; k < eCut_N; k++) { + TString histname = Form("fEventHistograms[%s][%s][%s]", cevent[i], cpro[j], ccut[k]); + TString histtitle = Form("%s distribution for %s, %s cut", cevent[i], cpro[j], ccut[k]); + if (i == 0) + event.fEventHistograms[i][j][k] = new TH1F(histname, histtitle, nBinsz, minz, maxz); + if (i == 1) + event.fEventHistograms[i][j][k] = new TH1F(histname, histtitle, nBins, min, max); + event.fEventHistograms[i][j][k]->GetXaxis()->SetTitle(Form("%s", cevent[i])); + // event.fEventHistogramsList->Add(event.fEventHistograms[i][j][k]); + } + } + } + + for (int icut = 0; icut < eCut_N; icut++) { + pc.fHistPt[icut] = new TH1F(Form("fHistPt[%s]", ccut[icut]), Form("pt distribution %s cut for reconstructed particles", ccut[icut]), nBins, min, max); + pc.fHistPhi[icut] = new TH1F(Form("fHistPhi[%s]", ccut[icut]), Form("phi distribution %s cut for reconstructed particles", ccut[icut]), nBinsphi, minphi, maxphi); + pc.fHistCharge[icut] = new TH1F(Form("fHistCharge[%s]", ccut[icut]), Form("charge distribution %s cut for reconstructed particles", ccut[icut]), nBinscharge, mincharge, maxcharge); + pc.fHistTPCncls[icut] = new TH1F(Form("fHistTPCncls[%s]", ccut[icut]), Form("tpcncls distribution %s cut for reconstructed particles", ccut[icut]), nBinstpcncls, mintpcncls, maxtpcncls); + pc.fHistTracksdcaXY[icut] = new TH1F(Form("fHistTracksdcaXY[%s]", ccut[icut]), Form("dcaxy distribution %s cut for reconstructed particles", ccut[icut]), nBinsdcaxy, mindcaxy, maxdcaxy); + pc.fHistTracksdcaZ[icut] = new TH1F(Form("fHistTracksdcaZ[%s]", ccut[icut]), Form("dcaz distribution %s cut for reconstructed particles", ccut[icut]), nBinsdcaz, mindcaz, maxdcaz); + pc.fHistPt[icut]->GetXaxis()->SetTitle("p_{T}"); + pc.fHistPhi[icut]->GetXaxis()->SetTitle("phi"); + pc.fHistCharge[icut]->GetXaxis()->SetTitle("charge"); + pc.fHistTPCncls[icut]->GetXaxis()->SetTitle("TPCNClsFindable"); + pc.fHistTracksdcaXY[icut]->GetXaxis()->SetTitle("DCA XY"); + pc.fHistTracksdcaZ[icut]->GetXaxis()->SetTitle("DCA Z"); + pc.fParticleHistogramsList->Add(pc.fHistPt[icut]); + pc.fParticleHistogramsList->Add(pc.fHistPhi[icut]); + pc.fParticleHistogramsList->Add(pc.fHistCharge[icut]); + pc.fParticleHistogramsList->Add(pc.fHistTPCncls[icut]); + pc.fParticleHistogramsList->Add(pc.fHistTracksdcaXY[icut]); + pc.fParticleHistogramsList->Add(pc.fHistTracksdcaZ[icut]); + + // init eventhist + event.fHistCentr[icut] = new TH1F(Form("fHistCentr[%s]", ccut[icut]), Form("centrality distribution %s cut for reconstructed particles", ccut[icut]), nBinscentr, mincentr, maxcentr); + event.fHistX[icut] = new TH1F(Form("fHistX[%s]", ccut[icut]), Form("posX distribution %s cut for reconstructed particles", ccut[icut]), nBinsx, minx, maxx); + event.fHistY[icut] = new TH1F(Form("fHistY[%s]", ccut[icut]), Form("posY distribution %s cut for reconstructed particles", ccut[icut]), nBinsy, miny, maxy); + event.fHistZ[icut] = new TH1F(Form("fHistZ[%s]", ccut[icut]), Form("posZ distribution %s cut for reconstructed particles", ccut[icut]), nBinsz, minz, maxz); + event.fHistMult[icut] = new TH1I(Form("fHistMult[%s]", ccut[icut]), Form("mult distribution %s cut for reconstructed particles", ccut[icut]), nBinsmult, minmult, maxmult); + event.fHistMsel[icut] = new TH1F(Form("fHistMsel[%s]", ccut[icut]), Form("selected tracks %s cut", ccut[icut]), nBinsmsel, minmsel, maxmsel); + event.fHistNContr[icut] = new TH1I(Form("fHistNContr[%s]", ccut[icut]), Form("NContr distribution %s cut", ccut[icut]), nBinsncontr, minncontr, maxncontr); + event.fHistCentr[icut]->GetXaxis()->SetTitle(Form("centrality, %s", cfCent.value.c_str())); + event.fHistX[icut]->GetXaxis()->SetTitle("x"); + event.fHistY[icut]->GetXaxis()->SetTitle("y"); + event.fHistZ[icut]->GetXaxis()->SetTitle("z"); + event.fHistMult[icut]->GetXaxis()->SetTitle(Form("multiplicity, %s", cfMult.value.c_str())); + event.fHistMsel[icut]->GetXaxis()->SetTitle("selected tracks"); + event.fHistNContr[icut]->GetXaxis()->SetTitle("numContrib"); + event.fEventHistogramsList->Add(event.fHistCentr[icut]); + event.fEventHistogramsList->Add(event.fHistX[icut]); + event.fEventHistogramsList->Add(event.fHistY[icut]); + event.fEventHistogramsList->Add(event.fHistZ[icut]); + event.fEventHistogramsList->Add(event.fHistMult[icut]); + event.fEventHistogramsList->Add(event.fHistMsel[icut]); + event.fEventHistogramsList->Add(event.fHistNContr[icut]); + } + + // init of sim histograms + if (cfInitsim) { + pc.fHistPt[eSim] = new TH1F("fHistPt[eSim]", "pt distribution for simulated particles", nBins, min, max); + pc.fHistPhi[eSim] = new TH1F("fHistPhi[eSim]", "phi distribution for simulated particles", nBinsphi, minphi, maxphi); + pc.fHistCharge[eSim] = new TH1F("fHistCharge[eSim]", "charge distribution for simulated particles", nBinscharge, mincharge, maxcharge); + pc.fHistTPCncls[eSim] = new TH1F("fHistTPCncls[eSim]", "tpcncls distribution for simulated particles", nBinstpcncls, minphi, maxtpcncls); + pc.fHistTracksdcaXY[eSim] = new TH1F("fHistTracksdcaXY[eSim]", "dcaxy distribution for simulated particles", nBinsdcaxy, mindcaxy, maxdcaxy); + pc.fHistTracksdcaZ[eSim] = new TH1F("fHistTracksdcaZ[eSim]", "dcaz distribution for simulated particles", nBinsdcaz, mindcaz, maxdcaz); + pc.fHistPt[eSim]->GetXaxis()->SetTitle("p_{T}"); + pc.fHistPhi[eSim]->GetXaxis()->SetTitle("phi"); + pc.fHistCharge[eSim]->GetXaxis()->SetTitle("charge"); + pc.fHistTPCncls[eSim]->GetXaxis()->SetTitle("TPCNClsFindable"); + pc.fHistTracksdcaXY[eSim]->GetXaxis()->SetTitle("DCA XY"); + pc.fHistTracksdcaZ[eSim]->GetXaxis()->SetTitle("DCA Z"); + pc.fParticleHistogramsList->Add(pc.fHistPt[eSim]); + pc.fParticleHistogramsList->Add(pc.fHistPhi[eSim]); + pc.fParticleHistogramsList->Add(pc.fHistCharge[eSim]); + pc.fParticleHistogramsList->Add(pc.fHistTPCncls[eSim]); + pc.fParticleHistogramsList->Add(pc.fHistTracksdcaXY[eSim]); + pc.fParticleHistogramsList->Add(pc.fHistTracksdcaZ[eSim]); + + event.fHistCentr[eSim] = new TH1F("fHistCentr[eSim]", "centrality distribution for simulated particles", nBinscentr, mincentr, maxcentr); + event.fHistX[eSim] = new TH1F("fHistX[eSim]", "posX distribution for simulated particles", nBinsx, minx, maxx); + event.fHistY[eSim] = new TH1F("fHistY[eSim]", "posY distribution for simulated particles", nBinsy, miny, maxy); + event.fHistZ[eSim] = new TH1F("fHistZ[eSim]", "posZ distribution for simulated particles", nBinsz, minz, maxz); + event.fHistMult[eSim] = new TH1I("fHistMult[eSim]", "mult distribution for simulated particles", nBinsmult, minmult, maxmult); + event.fHistCentr[eSim]->GetXaxis()->SetTitle("centrality"); + event.fHistX[eSim]->GetXaxis()->SetTitle("x"); + event.fHistY[eSim]->GetXaxis()->SetTitle("y"); + event.fHistZ[eSim]->GetXaxis()->SetTitle("z"); + event.fHistMult[eSim]->GetXaxis()->SetTitle("multiplicity"); + event.fEventHistogramsList->Add(event.fHistCentr[eSim]); + event.fEventHistogramsList->Add(event.fHistX[eSim]); + event.fEventHistogramsList->Add(event.fHistY[eSim]); + event.fEventHistogramsList->Add(event.fHistZ[eSim]); + event.fEventHistogramsList->Add(event.fHistMult[eSim]); + } + + // loading weights + for (const int& run : targetRuns) { + std::string runStr = std::to_string(run); + TH1F* histweights = GetHistogramWithWeights(cfFileWithWeights.value.c_str(), runStr.c_str()); + if (!histweights) { + LOG(fatal) << "Failed to load weights for run " << run; + return; + } + histweights->SetName(Form("histWithEfficiencyCorrections_%d", run)); + wh.fWeightsHistList->Add(histweights); + wh.weightsmap[run] = histweights; + } + + qa.fQA = new TH2F("QA_centr", "quality assurance of centrality", nBinscentr, mincentr, maxcentr, nBinscentr, mincentr, maxcentr); + qa.fQAM_NC = new TH2F("QAM_NC", "quality assurance of mult vs. NContributors", nBinsmult, minmult, maxmult, nBinsncontr, minncontr, maxncontr); + if (cfQA) { + if (cfInitsim) + qa.fQAList->Add(qa.fQA); + qa.fQAList->Add(qa.fQAM_NC); + } + + // float quantiles[10] = {0, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8}; + float quantiles[10] = {0, 5, 10, 20, 30, 40, 50, 60, 70, 80}; + cor.pv22_centr = new TProfile("pv22", "profile of v_{2}^{2}", 9, quantiles); + cor.pv32_centr = new TProfile("pv32", "profile of v_{3}^{2}", 9, quantiles); + cor.pv42_centr = new TProfile("pv42", "profile of v_{4}^{2}", 9, quantiles); + cor.pfour32_centr = new TProfile("pfour32", "profile of v_{2}^{2}*v_{3}^{2}", 9, quantiles); + cor.pfour42_centr = new TProfile("pfour42", "profile of v_{2}^{2}*v_{4}^{2}", 9, quantiles); + cor.pv2_nest = new TProfile("pv2_nest", "profile of v_{2} from nest", 9, quantiles); + cor.pv3_nest = new TProfile("pv3_nest", "profile of v_{3} from nest", 9, quantiles); + cor.pv4_nest = new TProfile("pv4_nest", "profile of v_{4} from nest", 9, quantiles); + cor.pv22_centr->GetYaxis()->SetTitle("v_{2}^{2}"); + cor.pv32_centr->GetYaxis()->SetTitle("v_{3}^{2}"); + cor.pv42_centr->GetYaxis()->SetTitle("v_{4}^{2}"); + cor.pv22_centr->GetXaxis()->SetTitle("centrality"); + cor.pv32_centr->GetXaxis()->SetTitle("centrality"); + cor.pv42_centr->GetXaxis()->SetTitle("centrality"); + cor.pfour32_centr->GetYaxis()->SetTitle("v_{2}^{2}v_{3}^{2}"); + cor.pfour42_centr->GetYaxis()->SetTitle("v_{2}^{2}v_{4}^{2}"); + cor.pfour32_centr->GetXaxis()->SetTitle("centrality"); + cor.pfour42_centr->GetXaxis()->SetTitle("centrality"); + cor.pv2_nest->GetYaxis()->SetTitle("v_{2}"); + cor.pv3_nest->GetYaxis()->SetTitle("v_{3}"); + cor.pv4_nest->GetYaxis()->SetTitle("v_{4}"); + cor.pv2_nest->GetXaxis()->SetTitle("centrality"); + cor.pv3_nest->GetXaxis()->SetTitle("centrality"); + cor.pv4_nest->GetXaxis()->SetTitle("centrality"); + cor.fCorrelationVariablesList->Add(cor.pv22_centr); + cor.fCorrelationVariablesList->Add(cor.pv32_centr); + cor.fCorrelationVariablesList->Add(cor.pv42_centr); + cor.fCorrelationVariablesList->Add(cor.pfour32_centr); + cor.fCorrelationVariablesList->Add(cor.pfour42_centr); + if (cfNest) { + cor.fCorrelationVariablesList->Add(cor.pv2_nest); + cor.fCorrelationVariablesList->Add(cor.pv3_nest); + cor.fCorrelationVariablesList->Add(cor.pv4_nest); + } + + // init of phi hist for different runs + for (const int& run : targetRuns) { + std::string histName = "hphi_run_" + std::to_string(run); + std::string histTitle = "Phi dis for Run " + std::to_string(run); + + TH1F* h = new TH1F(histName.c_str(), histTitle.c_str(), nBinsphi, minphi, maxphi); + phih.fPhiHistList->Add(h); + phih.histMap[run] = h; + } + + } // end of void init(InitContext&) { + + // A) Process only reconstructed data: + void processRec(CollisionRec const& collision, aod::BCs const&, TracksRec const& tracks) + { + // ... + + // *) Steer all analysis steps: + Steer(collision, tracks); + } + PROCESS_SWITCH(MultiharmonicCorrelations, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version + + // ------------------------------------------- + + // B) Process both reconstructed and corresponding MC truth simulated data: + void processRecSim(CollisionRecSim const& collision, aod::BCs const&, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) + { + Steer(collision, tracks); + } + PROCESS_SWITCH(MultiharmonicCorrelations, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + + // ------------------------------------------- + + // C) Process only simulated data: + void processSim(CollisionSim const& /*collision*/, aod::BCs const&, TracksSim const& /*tracks*/) + { + // Steer(collision, tracks); // TBI 20241105 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... + } + PROCESS_SWITCH(MultiharmonicCorrelations, processSim, "process only simulated data", false); + +}; // struct MultiharmonicCorrelations { + +// *) The final touch: +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} // WorkflowSpec... diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx index 284259d9988..411c1ec8426 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ar.cxx @@ -86,7 +86,7 @@ enum CorEventDep { kCENDEP, kLAST_CorEventDep }; -const std::string CorEventDepNames[kLAST_CorEventDep] = { +const std::array CorEventDepNames{ "[kINTEGRATED]", "[kMULDEP]", "[kCENDEP]"}; @@ -101,7 +101,7 @@ enum CorTrackDep { kETADEP, kLAST_CorTrackDep }; -const std::string CorTrackDepNames[kLAST_CorTrackDep] = { +const std::array CorTrackDepNames{ "[kPTDEP]", "[kETADEP]"}; std::vector> CorTrackDepDefaultBins = { @@ -121,7 +121,7 @@ enum EventVariable { kMULTPC, kLAST_EventVariable }; -static constexpr std::string_view EventVariableNames[kLAST_EventVariable] = { +static constexpr std::array EventVariableNames{ "EventVariable_VertexX", "EventVariable_VertexY", "EventVariable_VertexZ", @@ -201,10 +201,10 @@ std::vector> TrackVariableDefaultCuts = { {0., 6., 1.}}; // kITSCLUSTERS // common info string for all configurables -const std::string BinningSuffix = std::string("-Binning"); -const std::string CutSuffix = std::string("-Cut"); -const std::string CorrelatorHistPrefix = std::string("CorDepBinning_"); -const std::vector CutInfo = {std::string("Low"), std::string("High"), std::string("Option")}; +static constexpr std::string BinningSuffix{"-Binning"}; +static constexpr std::string CutSuffix{"-Cut"}; +static constexpr std::string CorrelatorHistPrefix{"CorDepBinning_"}; +static const std::vector cutInfo{"Low", "High", "Option"}; const int MaxHarmonic = 10; const int MaxPower = 10; @@ -216,7 +216,7 @@ inline float abs(float vx, float vy, float vz) } // generice function for checking if the value of a variable passes a cut -inline bool SurviveCut(LabeledArray ConfigValue, float Value) +inline bool SurviveCut(const LabeledArray& ConfigValue, float Value) { uint32_t row = 0; bool flag = true; @@ -259,7 +259,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVX{ std::string(AR::EventVariableNames[AR::kVX]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVX).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVX).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVY{ std::string(AR::EventVariableNames[AR::kVY]) + AR::BinningSuffix, @@ -267,7 +267,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVY{ std::string(AR::EventVariableNames[AR::kVY]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVY).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVY).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVZ{ std::string(AR::EventVariableNames[AR::kVZ]) + AR::BinningSuffix, @@ -275,7 +275,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVZ{ std::string(AR::EventVariableNames[AR::kVZ]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVZ).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVZ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisVABS{ std::string(AR::EventVariableNames[AR::kVABS]) + AR::BinningSuffix, @@ -283,7 +283,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutVABS{ std::string(AR::EventVariableNames[AR::kVABS]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kVABS).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kVABS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisCEN{ std::string(AR::EventVariableNames[AR::kCEN]) + AR::BinningSuffix, @@ -291,7 +291,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutCEN{ std::string(AR::EventVariableNames[AR::kCEN]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kCEN).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kCEN).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULQ{ std::string(AR::EventVariableNames[AR::kMULQ]) + AR::BinningSuffix, @@ -299,7 +299,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULQ{ std::string(AR::EventVariableNames[AR::kMULQ]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULQ).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULQ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULW{ std::string(AR::EventVariableNames[AR::kMULW]) + AR::BinningSuffix, @@ -307,7 +307,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULW{ std::string(AR::EventVariableNames[AR::kMULW]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULW).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULW).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULNC{ std::string(AR::EventVariableNames[AR::kMULNC]) + AR::BinningSuffix, @@ -315,7 +315,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULNC{ std::string(AR::EventVariableNames[AR::kMULNC]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULNC).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULNC).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgEventAxisMULTPC{ std::string(AR::EventVariableNames[AR::kMULTPC]) + AR::BinningSuffix, @@ -323,7 +323,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgEventCutMULTPC{ std::string(AR::EventVariableNames[AR::kMULTPC]) + AR::CutSuffix, - {AR::EventVariableDefaultCuts.at(AR::kMULTPC).data(), 3, AR::CutInfo}, + {AR::EventVariableDefaultCuts.at(AR::kMULTPC).data(), 3, AR::cutInfo}, ""}; std::vector cfgEventBinning = { cfgEventAxisVX, @@ -343,7 +343,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutPT{ std::string(AR::TrackVariableNames[AR::kPT]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kPT).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kPT).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisPHI{ std::string(AR::TrackVariableNames[AR::kPHI]) + AR::BinningSuffix, @@ -351,7 +351,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutPHI{ std::string(AR::TrackVariableNames[AR::kPHI]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kPHI).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kPHI).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisETA{ std::string(AR::TrackVariableNames[AR::kETA]) + AR::BinningSuffix, @@ -359,7 +359,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutETA{ std::string(AR::TrackVariableNames[AR::kETA]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kETA).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kETA).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisCHARGE{ std::string(AR::TrackVariableNames[AR::kCHARGE]) + AR::BinningSuffix, @@ -367,7 +367,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutCHARGE{ std::string(AR::TrackVariableNames[AR::kCHARGE]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kCHARGE).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kCHARGE).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisDCAZ{ std::string(AR::TrackVariableNames[AR::kDCAZ]) + AR::BinningSuffix, @@ -375,7 +375,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutDCAZ{ std::string(AR::TrackVariableNames[AR::kDCAZ]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kDCAZ).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kDCAZ).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisDCAXY{ std::string(AR::TrackVariableNames[AR::kDCAXY]) + AR::BinningSuffix, @@ -383,7 +383,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutDCAXY{ std::string(AR::TrackVariableNames[AR::kDCAXY]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kDCAXY).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kDCAXY).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCLUSTERS{ std::string(AR::TrackVariableNames[AR::kTPCCLUSTERS]) + AR::BinningSuffix, @@ -391,7 +391,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCLUSTERS{ std::string(AR::TrackVariableNames[AR::kTPCCLUSTERS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCLUSTERS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCLUSTERS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCROSSEDROWS{ std::string(AR::TrackVariableNames[AR::kTPCCROSSEDROWS]) + AR::BinningSuffix, @@ -399,7 +399,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCROSSEDROWS{ std::string(AR::TrackVariableNames[AR::kTPCCROSSEDROWS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCROSSEDROWS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCROSSEDROWS).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisTPCCHI2{ std::string(AR::TrackVariableNames[AR::kTPCCHI2]) + AR::BinningSuffix, @@ -407,7 +407,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutTPCCHI2{ std::string(AR::TrackVariableNames[AR::kTPCCHI2]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kTPCCHI2).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kTPCCHI2).data(), 3, AR::cutInfo}, ""}; ConfigurableAxis cfgTrackAxisITSCLUSTERS{ std::string(AR::TrackVariableNames[AR::kITSCLUSTERS]) + AR::BinningSuffix, @@ -415,7 +415,7 @@ struct MultiParticleCorrelationsARTask { ""}; Configurable> cfgTrackCutITSCLUSTERS{ std::string(AR::TrackVariableNames[AR::kITSCLUSTERS]) + AR::CutSuffix, - {AR::TrackVariableDefaultCuts.at(AR::kITSCLUSTERS).data(), 3, AR::CutInfo}, + {AR::TrackVariableDefaultCuts.at(AR::kITSCLUSTERS).data(), 3, AR::cutInfo}, ""}; std::vector cfgTrackBinning = { cfgTrackAxisPT, @@ -616,7 +616,7 @@ struct MultiParticleCorrelationsARTask { for (auto SC : cfgSC.value) { Correlators = MapSCToCor(SC); fMapScToCor.insert({SC, Correlators}); - for (auto cor : Correlators) { + for (const auto& cor : Correlators) { if (std::find(fCorrelators.begin(), fCorrelators.end(), cor) != fCorrelators.end()) { continue; @@ -749,7 +749,7 @@ struct MultiParticleCorrelationsARTask { // function for checking if collision survives event cuts template - bool SurviveEventCuts(CollisionObject collision, TrackObject tracks) + bool SurviveEventCuts(const CollisionObject& collision, const TrackObject& tracks) { // Check if event survives event cuts, where we can get the values for the variables immediately @@ -780,7 +780,7 @@ struct MultiParticleCorrelationsARTask { // function for checking if track survices trach cuts template - bool SurviveTrackCuts(TrackObject track) + bool SurviveTrackCuts(const TrackObject& track) { // if all SurviveCut return true, the function will return true // if at least one fails, it will return false @@ -797,7 +797,7 @@ struct MultiParticleCorrelationsARTask { } template - void FillAzimuthalAngle(TrackObject track) + void FillAzimuthalAngle(const TrackObject& track) { double angle = track.phi(); double weight = GetWeight(track); @@ -817,7 +817,7 @@ struct MultiParticleCorrelationsARTask { }; template - double GetWeight(TrackObject /*track*/) + double GetWeight(const TrackObject& /*track*/) { // for efficiency corrections, tbi return 1.; diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ab.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsAb.cxx similarity index 76% rename from PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ab.cxx rename to PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsAb.cxx index fe3e3d0b2a7..575ada9aed2 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/multiparticle-correlations-ab.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsAb.cxx @@ -9,11 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file multiparticle-correlations-ab.cxx -/// \brief ... TBI 20250425 +/// \file multiparticleCorrelationsAb.cxx +/// \brief Task to calculate multiparticle correlations and related observables /// \author Ante.Bilandzic@cern.ch -// O2: +// *) O2 include headers and namespaces: #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/TriggerAliases.h" @@ -34,7 +34,7 @@ using namespace o2; using namespace o2::framework; // *) Run 3: -using BCs_Run3 = soa::Join; // TBI 20241126 under testing +using BCsRun3 = soa::Join; // TBI 20241126 under testing // Remark 1: I have already timestamp in workflow, due to track-propagation. // Remark 2: For consistency with notation below, drop _Run3 and instead use _Run2 and _Run1 TBI 20250401 not sure any longer what I wanted to say here... @@ -55,31 +55,31 @@ using TracksSim = aod::McParticles; using TrackSim = aod::McParticles::iterator; // *) Run 2: -using EventSelection_Run2 = soa::Join; // TBI 20240517 do not subscribe to CentRun2CL0s and CentRun2CL1s => see enum -using CollisionRec_Run2 = soa::Join::iterator; // use in json "isRun2MC" : "true" for "event-selection-task" -using CollisionRecSim_Run2 = soa::Join::iterator; +using EventSelectionRun2 = soa::Join; // TBI 20240517 do not subscribe to CentRun2CL0s and CentRun2CL1s => see enum +using CollisionRecRun2 = soa::Join::iterator; // use in json "isRun2MC" : "true" for "event-selection-task" +using CollisionRecSimRun2 = soa::Join::iterator; // Remark: For tracks, I can use everything same as in Run 3 // *) Run 1: // TBI 20240205 Since centrality calibration is not available in converted Run 1 data, I cannot treat it for the time being in the same way as converted Run 2. // Once calibration is available, just use Run 2 above both for Run 2 and Run 1 -using EventSelection_Run1 = soa::Join; // TBI 20240205 no calibration for centrality in converted LHC10h and LHC11h, at the time of writing -using CollisionRec_Run1 = soa::Join::iterator; -using CollisionRecSim_Run1 = soa::Join::iterator; +using EventSelectionRun1 = soa::Join; // TBI 20240205 no calibration for centrality in converted LHC10h and LHC11h, at the time of writing +using CollisionRecRun1 = soa::Join::iterator; +using CollisionRecSimRun1 = soa::Join::iterator; // Remark: For tracks, I can use everything same as in Run 3 // *) QA: // Remark: This is Run 3 "Rec" + subscription to additional few tables (otherwise unnecessary in my analysis, e.g. some specific detector tables), used only for QA purposes. // Therefore, I start all definitions from what I have defined for Run 3 "Rec", and on top of it join these additional tables for QA. -using BCs_QA = soa::Join; +using BCsQA = soa::Join; // *) BcSels => bc.has_foundFT0(), etc. // *) Run3MatchedToBCSparse => bc.has_zdc(), etc. TBI 20250401 at the moment, I do not use this one -using Collision_QA = CollisionRec; // if I would need additional tables for QA, just join 'em here with CollisionRec -using TracksRec_QA = TracksRec; // if I would need additional tables for QA, just join 'em here with TracksRec +using CollisionQA = CollisionRec; // if I would need additional tables for QA, just join 'em here with CollisionRec +using TracksRecQA = TracksRec; // if I would need additional tables for QA, just join 'em here with TracksRec -// *) ROOT: +// *) ROOT include headers and namespaces: #include -#include +#include // o2-linter: disable=pdg/database (using until o2::framework::O2DatabasePDG lazy initialization is provided) #include #include #include @@ -99,6 +99,7 @@ using TracksRec_QA = TracksRec; // if I would need additional tables for QA, #include #include + using namespace std; // *) Enums: @@ -108,7 +109,7 @@ using namespace std; #include "PWGCF/MultiparticleCorrelations/Core/MuPa-GlobalConstants.h" // *) Main task: -struct MultiparticleCorrelationsAB // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root +struct MultiparticleCorrelationsAb // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root { // *) CCDB: @@ -122,12 +123,15 @@ struct MultiparticleCorrelationsAB // this name is used in lower-case format to // Service pdg; // *) Configurables (cuts): +// TBI 20260519 this code will eventually be inlined here from a temporary external header, when the major development is over #include "PWGCF/MultiparticleCorrelations/Core/MuPa-Configurables.h" // *) Data members: +// TBI 20260519 this code will eventually be inlined here from a temporary external header, when the major development is over #include "PWGCF/MultiparticleCorrelations/Core/MuPa-DataMembers.h" // *) Member functions: +// TBI 20260519 this code will eventually be inlined here from a temporary external header, when the major development is over #include "PWGCF/MultiparticleCorrelations/Core/MuPa-MemberFunctions.h" // ------------------------------------------- @@ -148,7 +152,7 @@ struct MultiparticleCorrelationsAB // this name is used in lower-case format to bool oldHistAddStatus = TH1::AddDirectoryStatus(); TH1::AddDirectory(kFALSE); - // *) Print environment (here I always print it, in Steer(...) only if verbose is set to true): + // *) Print environment (here I always print it, in steer(...) only if verbose is set to true): printEnvironment(); // *) Default configuration, booking, binning and cuts: @@ -232,86 +236,86 @@ struct MultiparticleCorrelationsAB // this name is used in lower-case format to // ------------------------------------------- // A) Process only reconstructed data: - void processRec(CollisionRec const& collision, BCs_Run3 const& bcs, TracksRec const& tracks) + void processRec(CollisionRec const& collision, BCsRun3 const& bcs, TracksRec const& tracks) { // Remark: Do not use here LOGF(fatal, ...) or LOGF(info, ...), because their stdout/stderr is suppressed. Use them in regular member functions instead. // *) Steer all analysis steps: - Steer(collision, bcs, tracks); + steer(collision, bcs, tracks); } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that I have default running version + PROCESS_SWITCH(MultiparticleCorrelationsAb, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that I have default running version // ------------------------------------------- // B) Process both reconstructed and corresponding MC truth simulated data: void processRecSim(CollisionRecSim const& collision, aod::BCs const& bcs, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) { - Steer(collision, bcs, tracks); + steer(collision, bcs, tracks); } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); // ------------------------------------------- // C) Process only simulated data: void processSim(CollisionSim const& /*collision*/, aod::BCs const& /*bcs*/, TracksSim const& /*tracks*/) { - // Steer(collision, bcs, tracks); // TBI 20240517 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... + // steer(collision, bcs, tracks); // TBI 20240517 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processSim, "process only simulated data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processSim, "process only simulated data", false); // ------------------------------------------- // D) Process only converted reconstructed Run 2 data: - void processRec_Run2(CollisionRec_Run2 const& collision, aod::BCs const& bcs, TracksRec const& tracks) + void processRecRun2(CollisionRecRun2 const& collision, aod::BCs const& bcs, TracksRec const& tracks) { - Steer(collision, bcs, tracks); + steer(collision, bcs, tracks); } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processRec_Run2, "process only converted reconstructed Run 2 data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processRecRun2, "process only converted reconstructed Run 2 data", false); // ------------------------------------------- // E) Process both converted reconstructed and corresponding MC truth simulated Run 2 data: - void processRecSim_Run2(CollisionRecSim_Run2 const& collision, aod::BCs const& bcs, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) + void processRecSimRun2(CollisionRecSimRun2 const& collision, aod::BCs const& bcs, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) { - Steer(collision, bcs, tracks); + steer(collision, bcs, tracks); } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processRecSim_Run2, "process both converted reconstructed and simulated Run 2 data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processRecSimRun2, "process both converted reconstructed and simulated Run 2 data", false); // ------------------------------------------- // F) Process only converted simulated Run 2 data: - void processSim_Run2(CollisionSim const& /*collision*/) // TBI 20240517 extend this subscription eventually + void processSimRun2(CollisionSim const& /*collision*/) // TBI 20240517 extend this subscription eventually { - // Steer(collision, tracks); // TBI 20240517 not ready yet, but I do not really need this one urgently, since RecSim_Run2 is working, and I need that one for efficiencies... + // steer(collision, tracks); // TBI 20240517 not ready yet, but I do not really need this one urgently, since RecSim_Run2 is working, and I need that one for efficiencies... } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processSim_Run2, "process only converted simulated Run 2 data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processSimRun2, "process only converted simulated Run 2 data", false); // ------------------------------------------- // G) Process only converted reconstructed Run 1 data: - void processRec_Run1(CollisionRec_Run1 const& collision, aod::BCs const& bcs, TracksRec const& tracks) + void processRecRun1(CollisionRecRun1 const& collision, aod::BCs const& bcs, TracksRec const& tracks) { - Steer(collision, bcs, tracks); + steer(collision, bcs, tracks); } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processRec_Run1, "process only converted reconstructed Run 1 data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processRecRun1, "process only converted reconstructed Run 1 data", false); // ------------------------------------------- // H) Process both converted reconstructed and corresponding MC truth simulated Run 1 data; - void processRecSim_Run1(CollisionRecSim_Run1 const& /*collision*/, aod::BCs const& /*bcs*/, TracksRecSim const& /*tracks*/, aod::McParticles const&, aod::McCollisions const&) + void processRecSimRun1(CollisionRecSimRun1 const& /*collision*/, aod::BCs const& /*bcs*/, TracksRecSim const& /*tracks*/, aod::McParticles const&, aod::McCollisions const&) { - // Steer(collision, bcs, tracks); // TBI 20240517 not ready yet, but for benchmarking in any case I need only "Rec" + // steer(collision, bcs, tracks); // TBI 20240517 not ready yet, but for benchmarking in any case I need only "Rec" } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processRecSim_Run1, "process both converted reconstructed and simulated Run 1 data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processRecSimRun1, "process both converted reconstructed and simulated Run 1 data", false); // ------------------------------------------- // I) Process only converted simulated Run 1 data. - void processSim_Run1(aod::Collision const&) // TBI 20240424 not ready yet, just a dummy to version to get later "doprocess..." variable. + void processSimRun1(aod::Collision const&) // TBI 20240424 not ready yet, just a dummy to version to get later "doprocess..." variable. { - // Steer(collision, tracks); // TBI 20240517 not ready yet, but for benchmarking in any case I need only "Rec" + // steer(collision, tracks); // TBI 20240517 not ready yet, but for benchmarking in any case I need only "Rec" } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processSim_Run1, "process only converted simulated Run 1 data", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processSimRun1, "process only converted simulated Run 1 data", false); // ------------------------------------------- @@ -320,32 +324,32 @@ struct MultiparticleCorrelationsAB // this name is used in lower-case format to // I have to remove "| o2-analysis-centrality-table $JsonFile \" from workflow (yes, remove, not comment out!) void processTest(aod::Collision const& collision, aod::BCs const& bcs, aod::Tracks const& tracks) { - Steer(collision, bcs, tracks); + steer(collision, bcs, tracks); } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processTest, "test processing", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processTest, "test processing", false); // ------------------------------------------- // K) Process data with more than necessary subscriptions to the tables, only for QA purposes: // Remark 1: This is basically the main "processRec" switch, merely enhanced with subscription to few more tables (e.g. detector specific), only for QA purposes. // Remark 2: Ideally, i use the same workflow for "processRec" and "processQA", but most likely at some point I will have to establish separate workflow for "processQA" - void processQA(Collision_QA const& collision, BCs_QA const& bcs, TracksRec_QA const& tracks, aod::FT0s const&) + void processQA(CollisionQA const& collision, BCsQA const& bcs, TracksRecQA const& tracks, aod::FT0s const&) { // Summary for additional tables subscribed to directly here: // *) FT0s => bc.foundFT0().sumAmpC(), etc. - Steer(collision, bcs, tracks); + steer(collision, bcs, tracks); } - PROCESS_SWITCH(MultiparticleCorrelationsAB, processQA, "QA processing", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processQA, "QA processing", false); // ------------------------------------------- // L) Process extra Monte Carlo info the from table HepMCHeavyIons: - // Remark 1: Under testing, merge eventually this process switch with processRecSim, processRecSim_Run2, and processRecSim_Run1 above; + // Remark 1: Under testing, merge eventually this process switch with processRecSim, processRecSimRun2, and processRecSimRun1 above; // This switch does everything the same as processRecSim (so it works only for Run 3 at the moment), except extra info is processed from table HepMCHeavyIons with dedicated function call - // ProcessHepMCHeavyIons(hepMChi). I use this dedicated function, in order not to modify call to Steer(...) by adding the fourth argument. - // TBI 20250429 see if I can circumvent this with templates (i can NOT join HepMCHeavyIons and McParticles), in order to keep call to Steer(...) as simple as it is now + // ProcessHepMCHeavyIons(hepMChi). I use this dedicated function, in order not to modify call to steer(...) by adding the fourth argument. + // TBI 20250429 see if I can circumvent this with templates (i can NOT join HepMCHeavyIons and McParticles), in order to keep call to steer(...) as simple as it is now // Remark 2: In MC LHC24g3 and LHC24e2c, for HepMCHeavyIons only impact parameter is filled; - // As soon as HepMCHeavyIons is correctly filled in MC productions, merge this switch with processRecSim, processRecSim_Run2, and processRecSim_Run1 above. + // As soon as HepMCHeavyIons is correctly filled in MC productions, merge this switch with processRecSim, processRecSimRun2, and processRecSimRun1 above. // Most notably, I will need hep.centrality() (centrality at generated level), and hep.sigmaInelNN() void processHepMChi(CollisionRecSim const& collision, aod::BCs const& bcs, TracksRecSim const& tracks, aod::HepMCHeavyIons const& hepMChi, aod::McParticles const&, aod::McCollisions const&) { @@ -367,16 +371,16 @@ struct MultiparticleCorrelationsAB // this name is used in lower-case format to } // *) Okay, extract all extra info from HepMCHeavyIons: - ProcessHepMCHeavyIons(hep); + processHepMCHeavyIons(hep); - // *) Call the Steer(...) - // TBI 20250429 For the time being, only Run 3 call for Steer(...) is supported. When generalizing to Run 2 and Run 1 process switches, perhaps the better strategy is - // just to inject ProcessHepMCHeavyIons(hep); , and keep call to Steer(...) as it is now? - Steer(collision, bcs, tracks); // TBI 20250429 remember that I have hardwired here eRecAndSim, so this now works only for Run 3 + // *) Call the steer(...) + // TBI 20250429 For the time being, only Run 3 call for steer(...) is supported. When generalizing to Run 2 and Run 1 process switches, perhaps the better strategy is + // just to inject ProcessHepMCHeavyIons(hep); , and keep call to steer(...) as it is now? + steer(collision, bcs, tracks); // TBI 20250429 remember that I have hardwired here eRecAndSim, so this now works only for Run 3 } // void processHepMChi( ... ) - PROCESS_SWITCH(MultiparticleCorrelationsAB, processHepMChi, "HepMCHeavyIons processing", false); + PROCESS_SWITCH(MultiparticleCorrelationsAb, processHepMChi, "HepMCHeavyIons processing", false); // ------------------------------------------- @@ -384,7 +388,7 @@ struct MultiparticleCorrelationsAB // this name is used in lower-case format to // ------------------------------------------- -}; // struct MultiparticleCorrelationsAB +}; // struct MultiparticleCorrelationsAb // ------------------------------------------- @@ -392,6 +396,6 @@ struct MultiparticleCorrelationsAB // this name is used in lower-case format to WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), }; } // WorkflowSpec... diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx new file mode 100644 index 00000000000..e7c0c6a2753 --- /dev/null +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsDm.cxx @@ -0,0 +1,867 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multiparticleCorrelationsDm.cxx +/// \brief Task to calculate multiparticle correlations and related observables +/// \author molnard@tcd.ie + +// O2: + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" // needed for aod::TracksDCA table + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// *) ROOT and C++ include's: + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Definitions of join tables for Run 3 analysis: +using EventSelection = soa::Join; +using CollisionRec = soa::Join::iterator; // use in json "isMC": "true" for "event-selection-task" +using CollisionRecSim = soa::Join::iterator; +using CollisionSim = aod::McCollision; +using TracksRec = soa::Join; +using TrackRec = soa::Join::iterator; +using TracksRecSim = soa::Join; // + use in json "isMC" : "true" +using TrackRecSim = soa::Join::iterator; +using TracksSim = aod::McParticles; +using TrackSim = aod::McParticles::iterator; + +// *) Define enums: +enum ERecSim { ERec = 0, + ESim, + ERecAndSim }; + +enum EProcess { + EProcessRec = 0, // Run 3, only reconstructed + EProcessRecSim, // Run 3, both reconstructed and simulated + EProcessSim, // Run 3, only simulated + EProcess_N +}; + +enum ECentralityEstimator { + ECentFT0C = 0, // default + ECentFT0M, + ECentFT0A, + ECentralityEstimator_N +}; + +enum EReferenceMultiplicityEstimator { + ERefMultTPC = 0, + ERefMultFT0C, + ERefMultFT0M, + EReferenceMultiplicityEstimator_N +}; + +enum EEventHistograms { // this is for cuts + EMultiplicity = 0, + EReferenceMultiplicity, + ECentrality, + EImpactParameter, // only simulated + EVertexX, + EVertexY, + EVertexZ, + EEventHistograms_N +}; + +enum EParticleHistograms { // this is for cuts + EPt = 0, + EPhi, + EParticleHistograms_N +}; + +enum EBeforeAfter { + EBefore = 0, + EAfter, + EBeforeAfter_N +}; + +// *) Main task: +struct MultiparticleCorrelationsDm { // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root + + // *) Base TList to hold all output objects: + TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function + OutputObj fBaseList{sBaseListName.Data(), + OutputObjHandlingPolicy::AnalysisObject, + OutputObjSourceType::OutputObjSource}; + + // *) CCDB: + // Service ccdb; // support for offline callibration data base, not needed for the time being... + // to be able to use ccdb object in the analysis task + Service ccdb{}; + + // *) Define configurables: + + Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; + Configurable cfFillQA{"cfFillQA", false, "book and fill QA histograms (only for development and validation)"}; // switch for QA histograms + Configurable cfUseVertexZCut{"cfUseVertexZCut", true, "apply the vertex Z event cut"}; + Configurable cfUsePtCut{"cfUsePtCut", true, "apply the pt particle cut"}; + + Configurable cfCentralityEstimator{"cfCentralityEstimator", "FT0C", "centrality estimator: FT0C, FT0M, FT0A"}; // centrality estimator with valid values: FT0C (default) + Configurable cfReferenceMultiplicityEstimator{"cfReferenceMultiplicityEstimator", "FT0C", "reference multiplicity estimator: TPC, FT0C, FT0M"}; // ref. multiplicity estimator with valid values: TPC (default) FT0C Ft0M + + // sigma_inel in fm^2 (1 b = 100 fm^2): Pb+Pb at 5.36 TeV = 7.71 b = 771 fm^2 (PH2278 Lecture 2, slide 33), change for other system or energy + Configurable cfSigmaInel{"cfSigmaInel", 771., "sigma_inel in fm^2 (1 b = 100 fm^2), Pb+Pb 5.36 TeV = 771"}; + + Configurable> cfPtBins{"cfPtBins", {1000, 0., 100.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPhiBins{"cfPhiBins", {360, 0., o2::constants::math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfMultiplicityBins{"cfMultiplicityBins", {5000, 0., 5000.}, "nMultBins, multMin, multMax"}; + Configurable> cfRefmultiplicityBins{"cfRefmultiplicityBins", {5000, 0., 5000.}, "nRefMultBins, refMultMin, refMultMax"}; // reference multiplicity + Configurable> cfImpactparameterBins{"cfImpactparameterBins", {200, 0., 20.}, "nBBins, bMin, bMax (fm)"}; // impact parameter + Configurable> cfCentralityBins{"cfCentralityBins", {100, 0., 100.}, "nCentBins, centMin, centMax"}; + Configurable> cfVertexXBins{"cfVertexXBins", {200, -1., 1.}, "nvxBins, vxMin, vxMax"}; + Configurable> cfVertexYBins{"cfVertexYBins", {200, -1, 1.}, "nvyBins, vyMin, vyMax"}; + Configurable> cfVertexZBins{"cfVertexZBins", {300, -15., 15.}, "nvzBins, vzMin, vzMax"}; + Configurable> cfVertexZ{"cfVertexZ", {-10., 10.}, "{min, max} [cm]"}; + Configurable> cfPt{"cfPt", {0.2, 5.0}, "{min, max} [GeV]"}; + + // for weights (efficiency corrections) + Configurable cfUseWeights{"cfUseWeights", false, "load weights from the file in cfFileWithWeights"}; + Configurable cfFileWithWeights{"cfFileWithWeights", "", "path to external ROOT file with weights; on Hyperloop only CCDB paths (/alice-ccdb.cern.ch/...) work"}; + + Configurable cfVerbose{"cfVerbose", false, "print \run number, centrality and vertex for each collision"}; + + // *) Define and initialize all data members to be called in the main process* functions: + // **) Task configuration: + struct TaskConfiguration { + std::array fProcess{}; // see enum EProcess + bool fDryRun = false; // book all histos and run without filling and calculating anything + bool fFillQA = false; // book and fill QA histos + bool fUseVertexZCut = true; + bool fUsePtCut = true; + + int fCentralityEstimator = ECentFT0C; + int fReferenceMultiplicityEstimator = ERefMultTPC; + + float fSigmaInel = 771.; // sigma inel [fm^2] + std::array fVertexZ = {-10., 10.}; // {min, max} [cm] + std::array fPt = {0.2, 5.0}; // {min, max} [GeV] + bool fWeightsLoaded = false; // weights are loaded once, at the first collision + bool fUseWeights = false; // load weights from external file + bool fVerbose = false; // print the values / collision + + } tc; // you have to prepend "tc." for all objects name in this group later in the code + + // **) Particle histograms: + struct ParticleHistograms { + TList* fParticleHistogramsList = nullptr; //!, 2>, EParticleHistograms_N> fParticleHistograms{}; // [type][rec, sim][before, after] + + } pc; // you have to prepend "pc." for all objects name in this group later in the code + + // **) Event histograms: + struct EventHistograms { + TList* fEventHistogramsList = nullptr; + + std::array, 2>, EEventHistograms_N> fEventHistograms{}; // [type][rec, sim][before, after] + + } ec; // you have to prepend "ec." for all objects name in this group later in the code + + // **) QA histograms (booked and filled only if cfFillQA is true): + + struct QAHistograms { + TList* fQAHistogramsList = nullptr; + TH2F* fHistCentralityRecVsSim = nullptr; // x = centrality sim (from impact parameter), y = centrality rec + } qa; // you have to prepend "qa." for all objects name in this group later in the code + + // **) Histogram with weights from an external file + TH1F* histWeights = nullptr; + + // *) Define all member functions to be called in the main process* functions: + + // returns centrality from the estimator selected in json + template + float getCentrality(T const& collision) + { + switch (tc.fCentralityEstimator) { + case ECentFT0C: + return collision.centFT0C(); + case ECentFT0M: + return collision.centFT0M(); + case ECentFT0A: + return collision.centFT0A(); + default: + LOGF(fatal, "Unknown centrality estimator"); + return -1.; + } + } + + // returns reference multiplicity from the estimator selected in json + template + float getReferenceMultiplicity(T const& collision) + { + switch (tc.fReferenceMultiplicityEstimator) { + case ERefMultTPC: + return collision.multTPC(); + case ERefMultFT0C: + return collision.multFT0C(); + case ERefMultFT0M: + return collision.multFT0M(); + default: + LOGF(fatal, "Unknown reference multiplicity estimator"); + return -1.; + } + } + + // centrality from impact parameter: slide 33 PH2278 Lecture 2 + float centralityFromImpactParameter(float b) { return 100. * o2::constants::math::PI * b * b / tc.fSigmaInel; } + + // ______________________ weights _________________________ + + TObject* getObjectFromList(TList* list, const char* objectName) + { + // Last update: 20261018 + + // Get TObject pointer from TList, even if it's in some nested TList. + // Foreseen to be used to fetch histograms or profiles from files directly. + + // Some ideas taken from TCollection::ls() + + // If you have added histograms directly to files (without TList's), then you can fetch them directly with + // file->Get("hist-name"). + + // Example usage: TH1D *hist = (TH1D*) getObjectFromList("some-valid-TList-pointer","some-object-name"); + + // Insanity checks: + if (!list) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (!objectName) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (0 == list->GetEntries()) { + return nullptr; + } + + // The object is in the current base list: + TObject* objectFinal = list->FindObject(objectName); // the final object I am after + if (objectFinal) { + return objectFinal; + } + + // Otherwise, search for the object recursively in the nested lists: + TObject* objectIter = nullptr; // iterator object in the loop below + TIter next(list); + while ((objectIter = next())) // double round braces are to silence the warnings + { + if (TString(objectIter->ClassName()).EqualTo("TList")) { + objectFinal = getObjectFromList(dynamic_cast(objectIter), objectName); + if (objectFinal) { + return objectFinal; + } + } + } // while(objectIter = next()) + + return nullptr; + + } // TObject* getObjectFromList(...) + + TH1F* getHistogramWithWeights(const char* filePath, const char* runNumber) + { + // Last update: 20261018 + + // *) Return value: + TH1F* hist = nullptr; + TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" + TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights + + // *) Determine from filePath if the file is on a local machine, or in home dir AliEn, or in CCDB: + + // Algorithm: a) If filePath begins with "/alice/cern.ch/" then it's in the home dir AliEn; + // b) If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. Therefore, files in AliEn and CCDB must be specified with abs path; + // c) For local files both absolute and relative paths are just fine. + + bool bFileIsInAliEn = false; + bool bFileIsInCCDB = false; + + if (TString(filePath).BeginsWith("/alice/cern.ch/")) { + bFileIsInAliEn = true; // file is in AliEn + } else if (TString(filePath).BeginsWith("/alice-ccdb.cern.ch/")) { + bFileIsInCCDB = true; // file is in CCDB + } + + if (bFileIsInAliEn) { + + // File you want to access is in your home directory in AliEn. + // In this branch, there is a special code to support file access in that case. + + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); + // add #include for it to work + + if (!alien) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + TFile* weightsFile = TFile::Open(Form("alien://%s", filePath), "READ"); // yes, ROOT can open a file transparently, even if it's sitting in AliEn, with this specific syntax + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + weightsFile->GetObject("ccdb_object", baseList); + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + // Finally, from the top-level TList "ccdb_object", get the desired nested TList. + // The technical problem here is that it can be nested at any level, + // to overcome that there is a helper utility function TObject* getObjectFromList(...), + // whose implementation is discussed and provided separately. + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for AliEn case, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } else if (bFileIsInCCDB) { + + // File you want to access is in your home directory in CCDB. + // In this branch, there is a special code to support file access in that case. + + // Remember that here I do not access the file; instead, I directly access the object in that file. + // My home dir in CCDB: https://alice-ccdb.cern.ch/browse/Users/a/abilandz/ => adapt for your case + ccdb->setURL("http://alice-ccdb.cern.ch"); // to be able to use the "ccdb" object in your analysis task + baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); + + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for CCDB, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } else { + + // File you want to access is on your local computer. + // In this branch, there is a special code to support file access in that case. + + // Check if the external ROOT file exists at the specified path: + if (gSystem->AccessPathName(filePath, kFileExists)) { + LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath,kFileExists)), filePath = %s \033[0m", filePath); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + TFile* weightsFile = TFile::Open(filePath, "READ"); + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + weightsFile->GetObject("ccdb_object", baseList); + + if (!baseList) { + // weightsFile->ls(); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + // baseList->ls(); + LOGF(fatal, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); // changed runNumber instead of tc.fRunNumber.Data() + } + } + + // OK, we got the desired run-specific TList with efficiency corrections for local case, + // after that we can use the common code for all 3 cases (local, AliEn, CCDB), + // that common code is further below. + + } // end of else + + // The common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: + // listWithRuns->ls(); + + // get pointer to "hist" from "listWithRuns" + hist = dynamic_cast(getObjectFromList(listWithRuns, "histWithEfficiencyCorrections_1")); + if (!hist) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + hist->SetDirectory(nullptr); // remove ownerhip from an external file + TH1F* histClone = dynamic_cast(hist->Clone()); // yes, I have to clone here + + if (!bFileIsInCCDB) { // not for CCDB, there the CCDB manager owns the object and deletes it itself + delete baseList; // release back the memory + } + + return histClone; + + } // end of TH1F* getHistogramWithWeights(...) + + //__ EVENT CUTS __ + template + bool eventCuts(T1 const& collision) + { + + // *) Vertex Z cut + if (tc.fUseVertexZCut) { + if (!(tc.fVertexZ[0] <= collision.posZ() && collision.posZ() < tc.fVertexZ[1])) { + return false; // if at least one event cut is not satisfied + } + } + return true; // all event cuts passed + } + + //__ PARTICLE CUTS __ + template + bool particleCuts(T const& track) + { + + // *) pt cut + if (tc.fUsePtCut) { + if (!(tc.fPt[0] <= track.pt() && track.pt() < tc.fPt[1])) { + return false; // if at least one event cut is not satisfied + } + } + + return true; // all particle cuts passed + } + + //__ Fill all event histograms __ #ba = EBefore or EAfter + template + void fillEventHistograms(T1 const& collision, T2 const& tracks, EBeforeAfter ba) + { + + if constexpr (rs == ERec || rs == ERecAndSim) { + + float centRec = getCentrality(collision); + + ec.fEventHistograms[EMultiplicity][ERec][ba]->Fill(tracks.size()); // defined as total number of available particles + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->Fill(getReferenceMultiplicity(collision)); + ec.fEventHistograms[ECentrality][ERec][ba]->Fill(centRec); + ec.fEventHistograms[EVertexX][ERec][ba]->Fill(collision.posX()); + ec.fEventHistograms[EVertexY][ERec][ba]->Fill(collision.posY()); + ec.fEventHistograms[EVertexZ][ERec][ba]->Fill(collision.posZ()); + + if constexpr (rs == ERecAndSim) { + if (!collision.has_mcCollision()) { + LOGF(debug, " No MC collision for this collision, skip simulated part..."); + return; + } + auto mcCollision = collision.mcCollision(); + + ec.fEventHistograms[EMultiplicity][ESim][ba]->Fill(tracks.size()); + ec.fEventHistograms[EVertexX][ESim][ba]->Fill(mcCollision.posX()); + ec.fEventHistograms[EVertexY][ESim][ba]->Fill(mcCollision.posY()); + ec.fEventHistograms[EVertexZ][ESim][ba]->Fill(mcCollision.posZ()); + + // impact parameter and centrality calculated from MC + float b = mcCollision.impactParameter(); + if (b > 0.) { // only if the impact parameter is stored in the MC + float centSim = centralityFromImpactParameter(b); + ec.fEventHistograms[EImpactParameter][ESim][ba]->Fill(b); + ec.fEventHistograms[ECentrality][ESim][ba]->Fill(centSim); + + // QA centrality sim vs centrality rec after the cuts + if (tc.fFillQA && ba == EAfter) { + qa.fHistCentralityRecVsSim->Fill(centSim, centRec); + } // if (tc.fFillQA && ba == EAfter) + + } // if (b > 0.) + + } // if constexpr (rs == ERecAndSim) + + } // if constexpr (rs == ERec || rs == ERecAndSim) + + } // fillEventHistograms + + template + void steer(T1 const& collision, T2 const& tracks) + { + + // Dry run: + if (tc.fDryRun) { + return; + } + + // Load the weights once, at the first collision, because the run number is only known here + if (tc.fUseWeights && !tc.fWeightsLoaded) { + histWeights = getHistogramWithWeights(cfFileWithWeights.value.c_str(), Form("%d", collision.bc().runNumber())); + histWeights->SetTitle(cfFileWithWeights.value.c_str()); // book-keeping: title = file path + // value takes the text as std::string and c_str makes it into a constant char* + + fBaseList->Add(histWeights); // so it is saved in AnalysisResults.root + tc.fWeightsLoaded = true; + } + + // Print info for each collision, only if switched on in the config: + if (tc.fVerbose) { + LOGF(info, "Run number: %d", collision.bc().runNumber()); + LOGF(info, "Centrality: %f", getCentrality(collision)); + LOGF(info, "Vertex X position: %f", collision.posX()); + } + + // Fill event histograms before event cuts + fillEventHistograms(collision, tracks, EBefore); + + // Event cuts: leave this collision if it doesn't pass + if (!eventCuts(collision)) { + return; + } + + // Main loop over particles: + auto track = tracks.iteratorAt(0); // set the type and scope from one instance + for (int64_t i = 0; i < tracks.size(); i++) { + + track = tracks.iteratorAt(i); + + // Print track azimuthal angle: + // LOGF(info, "Track azimuthal angle: %f", track.phi()); + + // Fill reconstructed ...: + if constexpr (rs == ERec || rs == ERecAndSim) { + // Fill tracks before cuts: + + pc.fParticleHistograms[EPt][ERec][EBefore]->Fill(track.pt()); + pc.fParticleHistograms[EPhi][ERec][EBefore]->Fill(track.phi()); + + // ... and corresponding MC truth simulated (before cuts): + // See https://github.com/AliceO2Group/O2Physics/blob/master/Tutorials/src/mcHistograms.cxx + // See https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo + + if constexpr (rs == ERecAndSim) { + if (track.has_mcParticle()) { + auto mcparticle = track.mcParticle(); // corresponding MC truth simulated particle + pc.fParticleHistograms[EPt][ESim][EBefore]->Fill(mcparticle.pt()); + pc.fParticleHistograms[EPhi][ESim][EBefore]->Fill(mcparticle.phi()); + } else { + LOGF(debug, " No MC particle for this track..."); + } + } + + // Particle cuts: skip this track, not the whole event + if (!particleCuts(track)) { + continue; + } + + // Fill tracks after cuts: + + pc.fParticleHistograms[EPt][ERec][EAfter]->Fill(track.pt()); + pc.fParticleHistograms[EPhi][ERec][EAfter]->Fill(track.phi()); + + if constexpr (rs == ERecAndSim) { + if (!track.has_mcParticle()) { + continue; + } + + auto mcparticle = track.mcParticle(); // corresponding MC truth simulated particles + pc.fParticleHistograms[EPt][ESim][EAfter]->Fill(mcparticle.pt()); + pc.fParticleHistograms[EPhi][ESim][EAfter]->Fill(mcparticle.phi()); + + } // end of if constexpr (rs == ERecAndSim) { + + } // if constexpr (rs == ERec || rs == ERecAndSim) { + + } // end of for (int64_t i = 0; i < tracks.size(); i++) { + + // Fill event histograms after event and particle cuts + fillEventHistograms(collision, tracks, EAfter); + + } // end of template void steer(T1 const& collision, T2 const& tracks) { + + // *) Initialize and book all objects: + void init(InitContext&) + { + + // ... code to book and initialize all analysis objects ... + + // *) Set automatically what to process, from an implicit variable "doprocessSomeProcessName" within a PROCESS_SWITCH clause: + tc.fProcess[EProcessRec] = doprocessRec; + tc.fProcess[EProcessRecSim] = doprocessRecSim; + tc.fProcess[EProcessSim] = doprocessSim; + + // *) Configure your task using configurables in the json file: + tc.fDryRun = cfDryRun; + tc.fFillQA = cfFillQA; + tc.fVerbose = cfVerbose; + tc.fSigmaInel = cfSigmaInel; + + tc.fUseWeights = cfUseWeights; + if (tc.fUseWeights && cfFileWithWeights.value.empty()) { + LOGF(fatal, "cfUseWeights is true, but cfFileWithWeights is empty"); + } + + // vertex Z cut copy the swtich and range + tc.fUseVertexZCut = cfUseVertexZCut; + std::vector lVzCut = cfVertexZ.value; + + tc.fVertexZ[0] = lVzCut[0]; + tc.fVertexZ[1] = lVzCut[1]; + + // pt cut copy switch and range + tc.fUsePtCut = cfUsePtCut; + std::vector lPtCut = cfPt.value; + + tc.fPt[0] = lPtCut[0]; + tc.fPt[1] = lPtCut[1]; + + // translate the centrality estimator string into the enum + if (cfCentralityEstimator.value == "FT0C") { + tc.fCentralityEstimator = ECentFT0C; + } else if (cfCentralityEstimator.value == "FT0M") { + tc.fCentralityEstimator = ECentFT0M; + } else if (cfCentralityEstimator.value == "FT0A") { + tc.fCentralityEstimator = ECentFT0A; + } else { + LOGF(fatal, "Unknown centrality estimator in json: %s", cfCentralityEstimator.value.c_str()); + } + + // same for the reference multiplicity estimator + if (cfReferenceMultiplicityEstimator.value == "TPC") { + tc.fReferenceMultiplicityEstimator = ERefMultTPC; + } else if (cfReferenceMultiplicityEstimator.value == "FT0C") { + tc.fReferenceMultiplicityEstimator = ERefMultFT0C; + } else if (cfReferenceMultiplicityEstimator.value == "FT0M") { + tc.fReferenceMultiplicityEstimator = ERefMultFT0M; + } else { + LOGF(fatal, "Unknown reference multiplicity estimator in json: %s", cfReferenceMultiplicityEstimator.value.c_str()); + } + + // *) Book base list: + TList* temp = new TList(); + temp->SetOwner(true); + fBaseList.setObject(temp); + + // *) Book and nest all other TLists: + pc.fParticleHistogramsList = new TList(); + pc.fParticleHistogramsList->SetName("ParticleHistograms"); + pc.fParticleHistogramsList->SetOwner(true); + fBaseList->Add(pc.fParticleHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + ec.fEventHistogramsList = new TList(); + ec.fEventHistogramsList->SetName("EventHistograms"); + ec.fEventHistogramsList->SetOwner(true); + fBaseList->Add(ec.fEventHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + if (tc.fFillQA) { // only if QA switched on + qa.fQAHistogramsList = new TList(); + qa.fQAHistogramsList->SetName("QA"); + qa.fQAHistogramsList->SetOwner(true); + fBaseList->Add(qa.fQAHistogramsList); + } + + // *) Book (pt distribution and azimuthal-angle histograms) + event quantities with binning defined through configurables in the json file: + std::vector lPtBins = cfPtBins.value; // define local array and initialize it from an array set in the configurables + int nBins = static_cast(lPtBins[0]); + float min = lPtBins[1]; + float max = lPtBins[2]; + + std::vector lPhiBins = cfPhiBins.value; // azimuthal angle + int nPhiBins = static_cast(lPhiBins[0]); + float phiMin = lPhiBins[1]; + float phiMax = lPhiBins[2]; + + std::vector lMultBins = cfMultiplicityBins.value; // multiplicity + int nMultBins = static_cast(lMultBins[0]); + float multMin = lMultBins[1]; + float multMax = lMultBins[2]; + + std::vector lRefmultBins = cfRefmultiplicityBins.value; // reference multiplicity + int nRefMultBins = static_cast(lRefmultBins[0]); + float refMultMin = lRefmultBins[1]; + float refMultMax = lRefmultBins[2]; + + std::vector lCentBins = cfCentralityBins.value; // centrality + int nCentBins = static_cast(lCentBins[0]); + float centMin = lCentBins[1]; + float centMax = lCentBins[2]; + + std::vector lBBins = cfImpactparameterBins.value; // impact parameter + int nBBins = static_cast(lBBins[0]); + float bMin = lBBins[1]; + float bMax = lBBins[2]; + + std::vector lVxBins = cfVertexXBins.value; // vertex X + int nvxBins = static_cast(lVxBins[0]); + float vxMin = lVxBins[1]; + float vxMax = lVxBins[2]; + + std::vector lVyBins = cfVertexYBins.value; // vertex y + int nvyBins = static_cast(lVyBins[0]); + float vyMin = lVyBins[1]; + float vyMax = lVyBins[2]; + + std::vector lVzBins = cfVertexZBins.value; // vertex z + int nvzBins = static_cast(lVzBins[0]); + float vzMin = lVzBins[1]; + float vzMax = lVzBins[2]; + + // histogram booked before and after cuts + std::array baNames = {"before", "after"}; + // book rec histograms only if a process with reconstructed data runs, sim histograms only if one with simulated data runs + const bool bookRec = doprocessRec || doprocessRecSim; + const bool bookSim = doprocessRecSim || doprocessSim; + for (int ba = 0; ba < EBeforeAfter_N; ba++) { + + if (bookRec) { + // par + pc.fParticleHistograms[EPt][ERec][ba] = new TH1F(Form("fHistPt[ERec][%s]", baNames[ba]), Form("pt distribution for reconstructed particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ERec][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ERec][ba]); + + pc.fParticleHistograms[EPhi][ERec][ba] = new TH1F(Form("fHistPhi[ERec][%s]", baNames[ba]), Form("Azimuthal-angle distribution for reconstructed particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ERec][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ERec][ba]); + // ev + ec.fEventHistograms[EMultiplicity][ERec][ba] = new TH1F(Form("fHistMultiplicity[ERec][%s]", baNames[ba]), Form("Multiplicity for reconstructed particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ERec][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ERec][ba]); + + // reference multiplicity (only reconstructed) + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba] = new TH1F(Form("fHistReferenceMultiplicity[ERec][%s]", baNames[ba]), Form("Reference multiplicity, reconstructed (%s cuts)", baNames[ba]), nRefMultBins, refMultMin, refMultMax); + ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]->GetXaxis()->SetTitle("reference multiplicity"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EReferenceMultiplicity][ERec][ba]); + + ec.fEventHistograms[ECentrality][ERec][ba] = new TH1F(Form("fHistCentrality[ERec][%s]", baNames[ba]), Form("Centrality for reconstructed particles (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ERec][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ERec][ba]); + + ec.fEventHistograms[EVertexX][ERec][ba] = new TH1F(Form("fHistVertexX[ERec][%s]", baNames[ba]), Form("Vertex X for reconstructed particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ERec][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ERec][ba]); + + ec.fEventHistograms[EVertexY][ERec][ba] = new TH1F(Form("fHistVertexY[ERec][%s]", baNames[ba]), Form("Vertex Y for reconstructed particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ERec][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ERec][ba]); + + ec.fEventHistograms[EVertexZ][ERec][ba] = new TH1F(Form("fHistVertexZ[ERec][%s]", baNames[ba]), Form("Vertex Z for reconstructed particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ERec][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ERec][ba]); + } + if (bookSim) { + pc.fParticleHistograms[EPt][ESim][ba] = new TH1F(Form("fHistPt[ESim][%s]", baNames[ba]), Form("pt distribution for simulated particles (%s cuts)", baNames[ba]), nBins, min, max); + pc.fParticleHistograms[EPt][ESim][ba]->GetXaxis()->SetTitle("p_{T}"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPt][ESim][ba]); + + pc.fParticleHistograms[EPhi][ESim][ba] = new TH1F(Form("fHistPhi[ESim][%s]", baNames[ba]), Form("Azimuthal-angle distribution for simulated particles (%s cuts)", baNames[ba]), nPhiBins, phiMin, phiMax); + pc.fParticleHistograms[EPhi][ESim][ba]->GetXaxis()->SetTitle("#varphi (rad)"); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[EPhi][ESim][ba]); + // ev + ec.fEventHistograms[EMultiplicity][ESim][ba] = new TH1F(Form("fHistMultiplicity[ESim][%s]", baNames[ba]), Form("Multiplicity for simulated particles (%s cuts)", baNames[ba]), nMultBins, multMin, multMax); + ec.fEventHistograms[EMultiplicity][ESim][ba]->GetXaxis()->SetTitle("total number of available particles"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EMultiplicity][ESim][ba]); + // sim from impact parameter + ec.fEventHistograms[ECentrality][ESim][ba] = new TH1F(Form("fHistCentrality[ESim][%s]", baNames[ba]), Form("Centrality for simulated particles, from impact parameter (%s cuts)", baNames[ba]), nCentBins, centMin, centMax); + ec.fEventHistograms[ECentrality][ESim][ba]->GetXaxis()->SetTitle("centrality (%)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[ECentrality][ESim][ba]); + // impact parameter, only simulated + ec.fEventHistograms[EImpactParameter][ESim][ba] = new TH1F(Form("fHistImpactParameter[ESim][%s]", baNames[ba]), Form("Impact parameter, simulated (%s cuts)", baNames[ba]), nBBins, bMin, bMax); + ec.fEventHistograms[EImpactParameter][ESim][ba]->GetXaxis()->SetTitle("b (fm)"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EImpactParameter][ESim][ba]); + + ec.fEventHistograms[EVertexX][ESim][ba] = new TH1F(Form("fHistVertexX[ESim][%s]", baNames[ba]), Form("Vertex X for simulated particles (%s cuts)", baNames[ba]), nvxBins, vxMin, vxMax); + ec.fEventHistograms[EVertexX][ESim][ba]->GetXaxis()->SetTitle("vertex X"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexX][ESim][ba]); + + ec.fEventHistograms[EVertexY][ESim][ba] = new TH1F(Form("fHistVertexY[ESim][%s]", baNames[ba]), Form("Vertex Y for simulated particles (%s cuts)", baNames[ba]), nvyBins, vyMin, vyMax); + ec.fEventHistograms[EVertexY][ESim][ba]->GetXaxis()->SetTitle("vertex Y"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexY][ESim][ba]); + + ec.fEventHistograms[EVertexZ][ESim][ba] = new TH1F(Form("fHistVertexZ[ESim][%s]", baNames[ba]), Form("Vertex Z for simulated particles (%s cuts)", baNames[ba]), nvzBins, vzMin, vzMax); + ec.fEventHistograms[EVertexZ][ESim][ba]->GetXaxis()->SetTitle("vertex Z"); + ec.fEventHistogramsList->Add(ec.fEventHistograms[EVertexZ][ESim][ba]); + } + } // end of for (int ba = 0; ba < 2; ba++) + + // QA histograms, booked only if cfFillQA is true + if (tc.fFillQA) { + qa.fHistCentralityRecVsSim = new TH2F("fHistCentralityRecVsSim", "Centrality: reconstructed vs. simulated", nCentBins, centMin, centMax, nCentBins, centMin, centMax); + qa.fHistCentralityRecVsSim->GetXaxis()->SetTitle("centrality simulated (%)"); + qa.fHistCentralityRecVsSim->GetYaxis()->SetTitle("centrality reconstructed (%)"); + qa.fQAHistogramsList->Add(qa.fHistCentralityRecVsSim); + } + + } // end of void init(InitContext&) { + + // A) Process only reconstructed data: + void processRec(CollisionRec const& collision, aod::BCs const&, TracksRec const& tracks) + { + // ... + + // *) steer all analysis steps: + steer(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCorrelationsDm, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version + + // ------------------------------------------- + + // B) Process both reconstructed and corresponding MC truth simulated data: + void processRecSim(CollisionRecSim const& collision, aod::BCs const&, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) + { + steer(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCorrelationsDm, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + + // ------------------------------------------- + + // C) Process only simulated data: + void processSim(CollisionSim const& /*collision*/, aod::BCs const&, TracksSim const& /*tracks*/) + { + // steer(collision, tracks); // TBI 20241105 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... + } + PROCESS_SWITCH(MultiparticleCorrelationsDm, processSim, "process only simulated data", false); + +}; // struct MultiparticleCorrelationsDm { + +// *) The final touch: +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} // WorkflowSpec... diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx new file mode 100644 index 00000000000..4d4ce5047f1 --- /dev/null +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCorrelationsMei.cxx @@ -0,0 +1,1528 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multiparticleCorrelationsMei.cxx +/// \brief Multiparticle correlation in O2 Framework +/// \author yuanjun.mei@cern.ch + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" // needed for aod::TracksDCA table + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants; + +// Definitions of join tables for Run 3 analysis: +using EventSelection = soa::Join; +using CollisionRec = soa::Join::iterator; // use in json "isMC": "true" for "event-selection-task" +using CollisionRecSim = soa::Join::iterator; +using CollisionSim = aod::McCollision; +using TracksRec = soa::Join; +using TrackRec = soa::Join::iterator; +using TracksRecSim = soa::Join; // + use in json "isMC" : "true" +using TrackRecSim = soa::Join::iterator; +using TracksSim = aod::McParticles; +using TrackSim = aod::McParticles::iterator; + +// *) Define enums: +enum ERecSim { + eRec = 0, + eSim, + eRecAndSim, + eRecSim_N +}; + +enum EProcess { + eProcessRec = 0, + eProcessRecSim, + eProcessSim, + eProcess_N +}; + +enum ERealMC { + eReal = 0, + eMC +}; + +enum ECentralityEstimator { + eFT0C = 0, + eFT0M, + eFV0A, + eNTPV, + eCentralityEstimator_N +}; + +enum EOccupancyEstimator { + eOccuTrack = 0, + eOccuFT0C, + eOccupancyEstimator_N +}; + +enum EMultiplicityTables { + eMultTPC = 0, + eMultFV0M, + eMultFT0C, + eMultFT0M, + eMultNTracksPV, + eMultiplicityTables_N +}; + +enum EEventCuts { + eSel8Cut = 0, + eIsGoodITSLayersAll, + eNoCollInTimeRangeStandard, + eNoCollInRofStandard, + eIsGoodZvtxFT0vsPV, + eNoHighMultCollInPrevRof, + eNoSameBunchPileUp, + eIsVertexITSTPC, + eCentCut, + eVertexZCut, + eNContribCut, + eOccupancyCut, + eCorrCentCut, + eMultNContribCut, + EEventCuts_N +}; + +enum EParticleCuts { + ePtCut = 0, + eEtaCut, + eChargeCut, + eTPCNumClusterCut, + eDCAxyCut, + eDCAzCut, + EParticleCuts_N +}; + +enum EBeforeAfterCuts { + eBefore = 0, + eAfter, + eBeforeAfterCuts_N +}; + +enum EUpperLowerBounds { + eUpper = 0, + eLower, + eUpperLowerBounds_N +}; + +enum EEventHistograms { + eHistCentrality = 0, + eHistMult, + eHistRefMult, + eHistVertexX, + eHistVertexY, + eHistVertexZ, + eHistImpactParameter, + eHistNumContrib, + eHistOccupancy, + eEventHistograms_N +}; + +enum EParticleHistograms { + eHistPt = 0, + eHistPhi, + eHistEta, + eHistCharge, + eParticleHistograms_N +}; + +enum EWeightsHistograms { + eWPhiRec = 0, + eWPhiSim, + eWPt, + eWeightsHistograms_N +}; + +enum EMiscHistograms { + eRunNumber = 0, + eMiscHistograms_N +}; + +enum EObservables { + eV22 = 0, + eV22Gap, + eV24, + eObservables_N +}; + +enum EIntValHarmonics { + eIV2 = 0, + eIV3, + eIV4, + eIntValHarmonics_N +}; + +// *) Lists of names: +static constexpr std::array CentralityEstimatorNames = {"FT0C", "FT0M", "FV0A", "NTPV"}; +static constexpr std::array MultiplicityTablesNames = {"multTPC", "multFV0M", "multFT0C", "multFT0M", "multNTracksPV"}; +static constexpr std::array OccupancyEstimatorNames = {"track", "FT0C"}; + +static constexpr std::array ParticleHistTitles = {"p_{T}", "#varphi", "#eta", "Particle charge"}; +static constexpr std::array ParticleHistNames = {"eHistPt", "eHistPhi", "eHistEta", "eHistCharge"}; + +static constexpr std::array EventHistTitles = {"Centrality", "Multiplicity (trak.size())", "Reference Multiplicity", "Vertex X", "Vertex Y", "Vertex Z", "Impact Parameter", "Number of Contributor", "Occupancy"}; +static constexpr std::array EventHistNames = {"eHistCentrality", "eHistMult", "eHistRefMult", "eHistVertexX", "eHistVertexY", "eHistVertexZ", "eHistImpactParameter", "eHistNumContrib", "eHistOccupancy"}; + +static constexpr std::array WeightsNames = {"eWPhiRec", "eWPhiSim", "eWPt"}; + +static constexpr std::array CutsNames = {"eBefore", "eAfter"}; +static constexpr std::array CutsTitles = {"before", "after"}; + +static constexpr std::array ObservablesNames = {"eV22", "eV22Gap", "eV24"}; +static constexpr std::array ObservablesTitles = {"v_{2}^{2}", "v_{2}^{2} with eta gap", "v_{2}^{4}"}; + +static constexpr std::array IntValNames = {"eIV2", "eIV3", "eIV4"}; +static constexpr std::array IntValTitles = {"v_{2}^{2}", "v_{3}^{2}", "v_{4}^{2}"}; + +// *) Main task: +struct MultiparticleCorrelationsMei // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root +{ + // *) Base TList to hold all output objects: + TString sBaseListName = "Default list name"; // yes, I declare it separately, because I need it also later in BailOut() function + OutputObj fBaseList{sBaseListName.Data(), OutputObjHandlingPolicy::AnalysisObject, OutputObjSourceType::OutputObjSource}; + + // *) CCDB: + Service ccdb{}; // support for offline callibration data base + Service pdg{}; + + // *) Define configurables: + Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; + Configurable cfCentralityEstimator{"cfCentralityEstimator", 0, "centrality estimator: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; + Configurable cfMultiplicityTables{"cfMultiplicityTables", 2, "multiplicity tables: 0=multTPC, 1=multFV0M, 2=multFT0C, 3=multFT0M, 4=multNTracksPV"}; + Configurable cfOccupancyEstimator{"cfOccupancyEstimator", 0, "occupancy estimator: 0=trackOccupancyInTimeRange, 1=ft0cOccupancyInTimeRange"}; + + // *) External root files + Configurable cfExternalFileSwitch{"cfExternalFileSwitch", false, "choose to include external root files or not"}; + Configurable cfFileWithWeights{"cfFileWithWeights", "/alice-ccdb.cern.ch/Users/m/mei/thesis-", "path to external ROOT file which holds all particle weights"}; + + // *) Binnings + Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "Centrality bins: nCentBins, centMin, centMax"}; + Configurable> cfMultBins{"cfMultBins", {300, 0., 30000.}, "Multiplicity bins: nMultBins, multMin, multMax"}; + Configurable> cfRefMultBins{"cfRefMultBins", {400, 0., 25000.}, "Reference mult bins: nMultBins, multMin, multMax"}; + Configurable> cfNumContribBins{"cfNumContribBins", {400, 0., 7000.}, "Number of contributors bins: nBinsContrib, contribMin, contribMax"}; + Configurable> cfVxBins{"cfVxBins", {500, -0.04, 0.04}, "Vertex X hist: nVxBins, vxMin, vxMax"}; + Configurable> cfVyBins{"cfVyBins", {500, -0.015, 0.015}, "Vertex Y hist: nVyBins, vyMin, vyMax"}; + Configurable> cfVzBins{"cfVzBins", {500, -20., 20.}, "Vertex Z hist: nVzBins, vzMin, vzMax"}; + Configurable> cfIpBins{"cfIpBins", {100, 0., 20.}, "Impact parameters hist (MC only): nIPBins, ipMin, ipMax"}; + Configurable> cfOccuBins{"cfOccuBins", {300, 0., 10000.}, "Occupancy hist (Rec only): nOccuBins, occuMin, occuMax"}; + + Configurable> cfPtBins{"cfPtBins", {4000, 0., 10.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfPhiBins{"cfPhiBins", {180, 0., math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfEtaBins{"cfEtaBins", {800, -3., 3.}, "nEtaBins, etaMin, etaMax"}; + + Configurable> cfQASelTrackBins{"cfQASelTrackBins", {200, 0., 15000., 400, 0., 6000.}, "bins for hist of selected tracks of QA: {nQASelTrackBinsBefore, minBefore, MaxBefore, nQASelTrackBinsAfter, minAfter, MaxAfter}"}; + + // *) Cuts + Configurable cfMasterCutSwitch{"cfMasterCutSwitch", true, "switch on or off all cuts"}; + + // event level cuts + Configurable> cfEventCutSwitch{"cfEventCutSwitch", {"1Sel8", "1IsGoodITSLayersAll", "1NoCollInTimeRangeStandard", "1NoCollInRofStandard", "1IsGoodZvtxFT0vsPV", "1NoHighMultCollInPrevRof", "1NoSameBunchPileUp", "1IsVertexITSTPC", "1Cent", "1VertexZ", "1NContrib", "1Occupancy", "1CorrCent", "1MultNContribCut"}, "event cuts switch, on and off by the first number before name"}; + Configurable> cfVertexZCutRange{"cfVertexZCutRange", {-10., 10.}, "vertex z position range: {min, max}[cm], with convention: min <= Vz <= max"}; + Configurable> cfCentCutRange{"cfCentCutRange", {0., 80.}, "centrality range: {min, max}, with convention: min <= cent <= max"}; + Configurable cfNContribCutInf{"cfNContribCutInf", 2, "Cuts on number of tracks used for the vertex, only numContrib > cfNContribCutInf survives"}; + Configurable cfOccupancyCutSup{"cfOccupancyCutSup", 6000, "Cuts on number of tracks used for the vertex, only occupancy < cfOccupancyCutSup survives"}; + Configurable> cfCorrCent{"cfCorrCent", {0, 2}, "choose the pair of centrality estimators for cut: 0=FT0C, 1=FT0M, 2=FV0A, 3=NTPV"}; + Configurable cfCorrCentCutRatio{"cfCorrCentCutRatio", 0.5, "correlation cut on centrality, only 2*|cent1 - cent2|/(cent1 + cent2) < cfCorrCentCutRatio survives"}; + Configurable> cfMultNContribBound{"cfMultNContribBound", {"0.160 * x + 153.563", "0.095 * x + -157.476"}, "geometric cuts on correlation of mult vs. nContrib switch, convention {upper bound, lower bound}"}; + + // particle level cuts + Configurable> cfParticleCutSwitch{"cfParticleCutSwitch", {"1Pt", "1Eta", "1Charge", "1TPCNumCluster", "1DCAxy", "1DCAz"}, "particle cuts switch, on and off by the first number before name"}; + Configurable> cfPtCutRange{"cfPtCutRange", {0.2, 5.}, "pt cut range: {min, max}[GeV/c], with convention: min <= pt <= max"}; + Configurable> cfEtaCutRange{"cfEtaCutRange", {-0.8, 0.8}, "eta cut range: {min, max}, with convention: min <= eta <= max"}; + Configurable> cfTPCNumClusterCutRange{"cfTPCNumClusterCutRange", {70, 160}, "range of cut on the number of clusters associated with a reconstructed track: {min, max}, with convention: min <= TPCNcls <= max"}; + Configurable> cfDCAxyCutRange{"cfDCAxyCutRange", {-3.2, 3.2}, "range of cut on signed transverse distance of closest approach between the reconstructed track and the primary interaction vertex in x-y plane: {min, max}, with convention: min <= dcaXY <= max"}; + Configurable> cfDCAzCutRange{"cfDCAzCutRange", {-2.4, 2.4}, "range of cut on signed transverse distance of closest approach between the reconstructed track and the primary interaction vertex in z direction: {min, max}, with convention: min <= dcaZ <= max"}; + + // *) Misc + Configurable cfEtaGap{"cfEtaGap", 1.0, "eta gap for two particle correlation"}; + Configurable cfSigmaInel{"cfSigmaInel", 7.71, "inelastic cross section in mb"}; + Configurable cfQualityAssuranceSwitch{"cfQualityAssuranceSwitch", false, "quality assurance switch"}; + Configurable cfRunMessageSwitch{"cfRunMessageSwitch", false, "run message switch"}; + Configurable cfInternalValidationSwitch{"cfInternalValidationSwitch", false, "internal validation switch"}; + Configurable> cfIVHarmonics{"cfIVHarmonics", {0.06, 0.07, 0.08}, "harmonics for internal validation: {v_2, v_3, v_4, ...}"}; + Configurable cfRNGSeed{"cfRNGSeed", 0, "seed for random number generator"}; + Configurable> cfQASelTrackCentRange{"cfQASelTrackCentRange", {60., 70.}, "centrality range for hist of selected tracks for QA"}; + + // *) Define and initialize all data members to be called in the main process* functions: + // **) Task configuration: + struct TaskConfiguration { + float fSigmaInel = 7.71; + float fEtaGap = 1.0; + float fCorrCentCutRatio = 0.5; + + std::vector fEventCutSwitch; + std::vector fVertexZCutRange = {-10., 10.}; + std::vector fCentCutRange = {0., 80.}; + std::vector fCorrCent = {0, 2}; + std::vector fMultNContribBound; + std::vector fParticleCutSwitch; + std::vector fPtCutRange = {0.2, 5.}; + std::vector fEtaCutRange = {-0.8, 0.8}; + std::vector fTPCNumClusterCutRange = {70, 160}; + std::vector fDCAxyCutRange = {-3.2, 3.2}; + std::vector fDCAzCutRange = {-2.4, 2.4}; + + std::vector fIVHarmonics; + std::vector fQASelTrackCentRange; + + std::string fFileWithWeights = "/alice-ccdb.cern.ch/Users/m/mei/thesis-"; + + int fCentralityEstimator = 0; + int fMultiplicityTables = 0; + int fOccupancyEstimator = 0; + int fNContribCutInf = 2; + int fOccupancyCutSup = 6000; + int fRNGSeed = 0; + + bool fDryRun = false; // book all histos and run without filling and calculating anything + bool fExternalFileSwitch = false; + bool fMasterCutSwitch = true; + bool fQualityAssuranceSwitch = false; + bool fRunMessageSwitch = false; + bool fInternalValidationSwitch = false; + + std::array fProcess{false}; // Set what to process. See enum EProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. + } tc; + + struct ParticleHistograms { + TList* fParticleHistList = nullptr; + std::array, 2>, eParticleHistograms_N> fParticleHist{}; + } pc; + + struct EventHistograms { + TList* fEventHistList = nullptr; + std::array, 2>, eEventHistograms_N> fEventHist{}; //! [ type - see enum EEventHistograms ][reco,sim][before, after event cuts] + } ec; + + struct ExternalHistograms { + TList* fExternalHistogramsList = nullptr; + std::array fWeights{}; + } ex; + + struct Observables { + TList* fObservablesList = nullptr; + std::array, eObservables_N> fProfCorr{}; //! [type][reco,sim] + } obs; + + struct QualityAssurance { + TList* fQualityAssuranceList = nullptr; + std::array fHistCentralityRecSim{}; + std::array fHistMultNContrib{}; + std::array fHistCorrCent{}; + std::array fHistSelectedTrack{}; + } qa; + + struct MiscHistograms { + TList* fMiscHistList = nullptr; + TProfile* fMiscHistRunNumber = nullptr; + std::array fMiscEventCutsCounter{}; //! [reco,sim] + std::array fMiscParticleCutsCounter{}; //! [reco,sim] + } misc; + + struct GeometricCuts { // eUpperLowerBounds_N + std::array fMultNContribBounds{}; + } geoCuts; + + struct InternalValidation { + TRandom3* fRandGen = nullptr; + TF1* fIntValFun = nullptr; + TList* fIntValList = nullptr; + std::array fProfIntVal{}; + } intval; + + struct RunTimeInfo { + bool isFirstCollision = true; // this is used to ensure that weights hist are booked once and only once, otherwise, a crash + int collisionCounter = 0; + int thisRunNumber = 0; + static constexpr int RunMessagePeriod = 100; + } rti; + + // *) functions and templates + std::vector> initQVectorsTable(int maxCorrelator, const std::vector& harmonic) + { + int sum = 0; + for (const int& x : harmonic) { + sum += std::abs(x); + } + const int maxHarmonic = sum + 1; // rows + const int maxPower = maxCorrelator + 1; // cols + + return std::vector>(maxHarmonic, std::vector(maxPower, TComplex(0., 0.))); + } + + template + void updateQVectorsTable(std::vector>& QVectorsTable, T1 const& phi, T1 const& weight) + { + for (std::size_t h = 0; h < QVectorsTable.size(); ++h) { + for (std::size_t p = 0; p < QVectorsTable[h].size(); ++p) { + const auto wp = std::pow(weight, static_cast(p)); + QVectorsTable[h][p] += TComplex(wp * std::cos(h * phi), wp * std::sin(h * phi)); + } + } + } + + template + void updateVecForGap(std::vector const& harmonic, std::vector& qVecAB, std::vector& weightAB, T1 const& phi, T1 const& eta, T1 const& weight) + { + if (eta < -tc.fEtaGap / 2.f) { + qVecAB[0] += TComplex(weight * std::cos(harmonic[0] * phi), weight * std::sin(harmonic[0] * phi)); + weightAB[0] += weight; + } + if (eta > tc.fEtaGap / 2.f) { + qVecAB[1] += TComplex(weight * std::cos(harmonic[1] * phi), weight * std::sin(harmonic[1] * phi)); + weightAB[1] += weight; + } + } + + std::vector two(const std::vector>& QVectorsTable, const std::vector& harmonic) + { + int n1 = harmonic[0], n2 = harmonic[1]; + auto q = [&](int n, int p) -> TComplex { + if (n >= 0) { + return QVectorsTable[n][p]; + } + return TComplex::Conjugate(QVectorsTable[-n][p]); + }; + auto corr = [&](int n1, int n2) -> TComplex { + return q(n1, 1) * q(n2, 1) - q(n1 + n2, 2); + }; + TComplex n = corr(n1, n2); + TComplex d = corr(0, 0); + return {n, d}; + } + + std::vector recursion(int m, const std::vector>& qVector, std::vector& harmonic, int mult = 1, int skip = 0) + { + // Calculate multi-particle correlators by using recursion (an improved faster version) originally developed by + // Kristjan Gulbrandsen (gulbrand@nbi.dk). + + auto q = [&](int n, int p) -> TComplex { + if (n >= 0) { + return qVector[n][p]; + } + return TComplex::Conjugate(qVector[-n][p]); + }; + std::vector c = {q(harmonic[m - 1], mult), q(0, mult)}; + if ((m - 1) == 0) { + return c; + } + std::vector temp = recursion(m - 1, qVector, harmonic); + c[0] *= temp[0]; + c[1] *= temp[1]; + if ((m - 1) == skip) { + return c; + } + int counter1 = 0; + int hhold = harmonic[counter1]; + harmonic[counter1] = harmonic[m - 2]; + harmonic[m - 2] = hhold + harmonic[m - 1]; + std::vector c2 = recursion(m - 1, qVector, harmonic, mult + 1, m - 2); + int counter2 = m - 3; + while (counter2 >= skip) { + harmonic[m - 2] = harmonic[counter1]; + harmonic[counter1] = hhold; + ++counter1; + hhold = harmonic[counter1]; + harmonic[counter1] = harmonic[m - 2]; + harmonic[m - 2] = hhold + harmonic[m - 1]; + temp = recursion(m - 1, qVector, harmonic, mult + 1, counter2); + c2[0] += temp[0]; + c2[1] += temp[1]; + --counter2; + } + harmonic[m - 2] = harmonic[counter1]; + harmonic[counter1] = hhold; + if (mult == 1) { + return {c[0] - c2[0], c[1] - c2[1]}; + } + return {c[0] - static_cast(mult) * c2[0], c[1] - static_cast(mult) * c2[1]}; + } + + bool noneZeroDenom(std::vector resultMultCorr) + { + return resultMultCorr[1].Re() != 0; + } + + TObject* getObjectFromList(TList* list, const char* objectName) + { + // Get TObject pointer from TList, even if it's in some nested TList. Foreseen to be used to fetch histograms or profiles from files directly. + // Some ideas taken from TCollection::ls() + // If you have added histograms directly to files (without TList's), then you can fetch them directly with + // file->Get("hist-name"). + + // Usage: TH1F *hist = (TH1F*) getObjectFromList("some-valid-TList-pointer","some-object-name"); + + // Insanity checks: + if (!list) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (!objectName) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (0 == list->GetEntries()) { + return nullptr; + } + + // The object is in the current base list: + TObject* objectFinal = list->FindObject(objectName); // the final object I am after + if (objectFinal) { + return objectFinal; + } + + // Otherwise, search for the object recursively in the nested lists: + TObject* objectIter = nullptr; // iterator object in the loop below + TIter next(list); + while ((objectIter = next())) // double round braces are to silence the warnings + { + if (auto* subList = dynamic_cast(objectIter)) { + objectFinal = getObjectFromList(subList, objectName); + if (objectFinal) { + return objectFinal; + } + } + } // while(objectIter = next()) + + return nullptr; + } // TObject* getObjectFromList(TList *list, char *objectName) + + template + void getHistogramWithWeights(const char* filePath, const char* runNumber) + { + // *) Return value: + std::array histW{}; + TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" + TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights + + // *) Determine from filePath if the file is on a local machine, or in home dir AliEn, or in CCDB: + // Algorithm: If filePath begins with "/alice/cern.ch/" then it's in the home dir AliEn; If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. Therefore, files in AliEn and CCDB must be specified with abs path; for local files both abs and relative paths are just fine. + bool bFileIsInAliEn = false; + bool bFileIsInCCDB = false; + + std::string path(filePath); + if (path.starts_with("/alice/cern.ch/")) { + bFileIsInAliEn = true; + } else if (path.starts_with("/alice-ccdb.cern.ch/")) { + bFileIsInCCDB = true; + } + + if (bFileIsInAliEn) { + // File you want to access is in your home dir in AliEn: + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); // do not forget to add #include to the preamble of your analysis task + if (!alien) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + TFile* weightsFile = TFile::Open(Form("alien://%s", filePath), "READ"); // yes, ROOT can open a file transparently, even if it's sitting in AliEn, with this specific syntax + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + weightsFile->GetObject("ccdb_object", baseList); + } else if (bFileIsInCCDB) { + // File you want to access is in your home dir in CCDB: Remember that here I do not access the file; instead, I directly access the object in that file. + ccdb->setURL("https://alice-ccdb.cern.ch"); + baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); + } else { + // this is the local case: + // Check if the external ROOT file exists at the specified path: + if (gSystem->AccessPathName(filePath, kFileExists)) { + LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath,kFileExists)), filePath = %s \033[0m", filePath); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + TFile* weightsFile = TFile::Open(filePath, "READ"); + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m can't open file", __FUNCTION__, __LINE__); + } + weightsFile->GetObject("ccdb_object", baseList); + } + + // Here comes the common code for all three cases, where from "listWithRuns" you fetch the desired histogram with efficiency corrections: + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d for case %s\033[0m", __FUNCTION__, __LINE__, filePath); + } + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(warning, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current run number = %s\033[0m", __FUNCTION__, __LINE__, runNumber); + return; + } + } + + for (int i = 0; i < eWeightsHistograms_N; ++i) { + if constexpr (rs == eRec) { + if (i == eWPhiSim) { + continue; + } + } + histW[i] = dynamic_cast(listWithRuns->FindObject(Form("[%s]", WeightsNames[i]))); + if (!histW[i]) { + LOGF(warning, "%s: histogram '%s' not found in run list with run number %s", __FUNCTION__, WeightsNames[i], runNumber); + continue; + } + histW[i]->SetDirectory(nullptr); + ex.fWeights[i] = dynamic_cast(histW[i]->Clone()); + if (!ex.fWeights[i]) { + LOGF(warning, "%s: failed to clone histogram '%s' with run number %s", __FUNCTION__, WeightsNames[i], runNumber); + continue; + } + ex.fWeights[i]->SetStats(false); + ex.fWeights[i]->SetTitle(filePath); + ex.fWeights[i]->SetDirectory(nullptr); + ex.fExternalHistogramsList->Add(ex.fWeights[i]); + } + } // end of void getHistogramWithWeights(const char* filePath, const char* runNumber) + + // templates + template + float chooseCent(T1 const& collision, int const& whichEstimator) + { + switch (whichEstimator) { + case eFT0C: + return static_cast(collision.centFT0C()); + case eFT0M: + return static_cast(collision.centFT0M()); + case eFV0A: + return static_cast(collision.centFV0A()); + case eNTPV: + return static_cast(collision.centNTPV()); + default: + LOG(warning) << "Unknown centrality estimator. Using FT0C as default."; + return static_cast(collision.centFT0C()); + } + } + + template + float chooseMult(T1 const& collision, int const& whichTable) + { + switch (whichTable) { + case eMultTPC: + return static_cast(collision.multTPC()); + case eMultFV0M: + return static_cast(collision.multFV0M()); + case eMultFT0C: + return static_cast(collision.multFT0C()); + case eMultFT0M: + return static_cast(collision.multFT0M()); + case eMultNTracksPV: + return static_cast(collision.multNTracksPV()); + default: + LOG(warning) << "Unknown multiplicity. Using multTPC as default."; + return static_cast(collision.multTPC()); + } + } + + template + float chooseOccu(T1 const& collision, int const& whichEstimator) + { + switch (whichEstimator) { + case eOccuTrack: + return static_cast(collision.trackOccupancyInTimeRange()); + case eOccuFT0C: + return static_cast(collision.ft0cOccupancyInTimeRange()); + default: + LOG(warning) << "Unknown occupancy estimator. Using track as default."; + return static_cast(collision.trackOccupancyInTimeRange()); + } + } + + template + bool eventCuts(T1 const& collision) + { + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!collision.has_mcCollision()) { + return false; + } + } + bool doNotPassCut = false; + for (int i = 0; i < EEventCuts_N; ++i) { + switch (i) { + default: + doNotPassCut = false; + break; + case eSel8Cut: + doNotPassCut = tc.fEventCutSwitch[eSel8Cut].starts_with("1") && !collision.sel8(); + break; + case eNoCollInTimeRangeStandard: + doNotPassCut = tc.fEventCutSwitch[eNoCollInTimeRangeStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard); + break; + case eNoCollInRofStandard: + doNotPassCut = tc.fEventCutSwitch[eNoCollInRofStandard].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard); + break; + case eNoSameBunchPileUp: + doNotPassCut = tc.fEventCutSwitch[eNoSameBunchPileUp].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + break; + case eIsVertexITSTPC: + doNotPassCut = tc.fEventCutSwitch[eIsVertexITSTPC].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC); + break; + case eIsGoodITSLayersAll: + doNotPassCut = tc.fEventCutSwitch[eIsGoodITSLayersAll].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); + break; + case eIsGoodZvtxFT0vsPV: + doNotPassCut = tc.fEventCutSwitch[eIsGoodZvtxFT0vsPV].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); + break; + case eNoHighMultCollInPrevRof: + doNotPassCut = tc.fEventCutSwitch[eNoHighMultCollInPrevRof].starts_with("1") && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof); + break; + case eNContribCut: + doNotPassCut = tc.fEventCutSwitch[eNContribCut].starts_with("1") && (collision.numContrib() < tc.fNContribCutInf); + break; + case eOccupancyCut: { + auto thisOccu = chooseOccu(collision, tc.fOccupancyEstimator); + doNotPassCut = tc.fEventCutSwitch[eOccupancyCut].starts_with("1") && (thisOccu > tc.fOccupancyCutSup); + break; + } + case eVertexZCut: + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + doNotPassCut = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.posZ() > tc.fVertexZCutRange[1] || collision.posZ() < tc.fVertexZCutRange[0]); + } + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + doNotPassCut = tc.fEventCutSwitch[eVertexZCut].starts_with("1") && (collision.mcCollision().posZ() > tc.fVertexZCutRange[1] || collision.mcCollision().posZ() < tc.fVertexZCutRange[0]); + } + break; + case eCentCut: + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + doNotPassCut = tc.fEventCutSwitch[eCentCut].starts_with("1") && (thisCent > tc.fCentCutRange[1] || thisCent < tc.fCentCutRange[0]); + } + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + auto impactParameter = collision.mcCollision().impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + doNotPassCut = tc.fEventCutSwitch[eCentCut].starts_with("1") && (centralityMC > tc.fCentCutRange[1] || centralityMC < tc.fCentCutRange[0]); + } + break; + case eCorrCentCut: { + auto cent1 = chooseCent(collision, tc.fCorrCent[0]); + auto cent2 = chooseCent(collision, tc.fCorrCent[1]); + doNotPassCut = tc.fEventCutSwitch[eCorrCentCut].starts_with("1") && (2 * std::fabsf(cent1 - cent2) / (cent1 + cent2) > tc.fCorrCentCutRatio); + break; + } + case eMultNContribCut: { + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + auto thisNContrib = collision.numContrib(); + auto upper = geoCuts.fMultNContribBounds[eUpper]->Eval(thisRefMult); + auto lower = geoCuts.fMultNContribBounds[eLower]->Eval(thisRefMult); + doNotPassCut = tc.fEventCutSwitch[eMultNContribCut].starts_with("1") && (thisNContrib < lower || thisNContrib > upper); + break; + } + } // end of switch (i) { + + if (doNotPassCut) { + if constexpr (rm == eReal) { + misc.fMiscEventCutsCounter[eRec]->Fill(i + 0.5, 1); + } + if constexpr (rm == eMC) { + misc.fMiscEventCutsCounter[eSim]->Fill(i + 0.5, 1); + } + return false; + } + } // end of for (int i = 0; i < EEventCuts_N; ++i) { + + return true; + } // end of bool eventCuts(T1 const& collision) + + template + void eventHistFill(T1 const& collision, T2 const& tracks) + { + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { // Fill reconstructed-level event histograms + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + auto thisOccu = chooseOccu(collision, tc.fOccupancyEstimator); + int multiplicityRec = static_cast(tracks.size()); + if constexpr (cuts == eBefore) { + ec.fEventHist[eHistMult][eRec][eBefore]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eBefore]->Fill(thisCent); + ec.fEventHist[eHistRefMult][eRec][eBefore]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eBefore]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eBefore]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eBefore]->Fill(collision.posZ()); + ec.fEventHist[eHistNumContrib][eRec][eBefore]->Fill(collision.numContrib()); + ec.fEventHist[eHistOccupancy][eRec][eBefore]->Fill(thisOccu); + } + if constexpr (cuts == eAfter) { + ec.fEventHist[eHistMult][eRec][eAfter]->Fill(multiplicityRec); + ec.fEventHist[eHistCentrality][eRec][eAfter]->Fill(thisCent); + ec.fEventHist[eHistRefMult][eRec][eAfter]->Fill(thisRefMult); + ec.fEventHist[eHistVertexX][eRec][eAfter]->Fill(collision.posX()); + ec.fEventHist[eHistVertexY][eRec][eAfter]->Fill(collision.posY()); + ec.fEventHist[eHistVertexZ][eRec][eAfter]->Fill(collision.posZ()); + ec.fEventHist[eHistNumContrib][eRec][eAfter]->Fill(collision.numContrib()); + ec.fEventHist[eHistOccupancy][eRec][eAfter]->Fill(thisOccu); + } + } + + // Fill MC simulated-level event histograms if both reconstructed and simulated data are processed + if constexpr ((rs == eRecAndSim || rs == eSim) && rm == eMC) { + if (!collision.has_mcCollision()) { + return; + } + auto thisMCCollision = collision.mcCollision(); + int multiplicitySim = static_cast(tracks.size()); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; // centrality for sim derived from impact parameter + if constexpr (cuts == eBefore) { + ec.fEventHist[eHistMult][eSim][eBefore]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eBefore]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eBefore]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eBefore]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eBefore]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eBefore]->Fill(thisMCCollision.posZ()); + } + if constexpr (cuts == eAfter) { + ec.fEventHist[eHistMult][eSim][eAfter]->Fill(multiplicitySim); + ec.fEventHist[eHistCentrality][eSim][eAfter]->Fill(centralityMC); + ec.fEventHist[eHistImpactParameter][eSim][eAfter]->Fill(impactParameter); + ec.fEventHist[eHistVertexX][eSim][eAfter]->Fill(thisMCCollision.posX()); + ec.fEventHist[eHistVertexY][eSim][eAfter]->Fill(thisMCCollision.posY()); + ec.fEventHist[eHistVertexZ][eSim][eAfter]->Fill(thisMCCollision.posZ()); + } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { + } // end of void eventHistFill(T1 const& collision, T2 const& tracks) + + template + bool particleCuts(T const& track) + { + bool doNotPassCut = false; + for (int i = 0; i < EParticleCuts_N; ++i) { + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + switch (i) { + default: + doNotPassCut = false; + break; + case ePtCut: + doNotPassCut = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (track.pt() < tc.fPtCutRange[0] || track.pt() > tc.fPtCutRange[1]); + break; + case eEtaCut: + doNotPassCut = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (track.eta() < tc.fEtaCutRange[0] || track.eta() > tc.fEtaCutRange[1]); + break; + case eChargeCut: + doNotPassCut = tc.fParticleCutSwitch[eChargeCut].starts_with("1") && (track.sign() != 1 && track.sign() != -1); + break; + case eTPCNumClusterCut: + doNotPassCut = tc.fParticleCutSwitch[eTPCNumClusterCut].starts_with("1") && (track.tpcNClsFound() < tc.fTPCNumClusterCutRange[0] || track.tpcNClsFound() > tc.fTPCNumClusterCutRange[1]); + break; + case eDCAxyCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAxyCut].starts_with("1") && (track.dcaXY() < tc.fDCAxyCutRange[0] || track.dcaXY() > tc.fDCAxyCutRange[1]); + break; + case eDCAzCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAzCut].starts_with("1") && (track.dcaZ() < tc.fDCAzCutRange[0] || track.dcaZ() > tc.fDCAzCutRange[1]); + break; + } + } // end of if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!track.has_mcParticle()) { + return false; + } + auto thisMCParticle = track.mcParticle(); + switch (i) { + default: + doNotPassCut = false; + break; + case ePtCut: + doNotPassCut = tc.fParticleCutSwitch[ePtCut].starts_with("1") && (thisMCParticle.pt() < tc.fPtCutRange[0] || thisMCParticle.pt() > tc.fPtCutRange[1]); + break; + case eEtaCut: + doNotPassCut = tc.fParticleCutSwitch[eEtaCut].starts_with("1") && (thisMCParticle.eta() < tc.fEtaCutRange[0] || thisMCParticle.eta() > tc.fEtaCutRange[1]); + break; + case eChargeCut: + if (tc.fParticleCutSwitch[eChargeCut].starts_with("1")) { + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + if (particlePDG) { + const int chargeMCUnit = 3; + auto chargeMC = particlePDG->Charge() / chargeMCUnit; + if (chargeMC != 1 && chargeMC != -1) { + doNotPassCut = true; + } + } else { + doNotPassCut = true; + } + } // end of if (tc.fParticleCutSwitch[eChargeCut]) { + break; + case eTPCNumClusterCut: + doNotPassCut = tc.fParticleCutSwitch[eTPCNumClusterCut].starts_with("1") && (track.tpcNClsFound() < tc.fTPCNumClusterCutRange[0] || track.tpcNClsFound() > tc.fTPCNumClusterCutRange[1]); + break; + case eDCAxyCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAxyCut].starts_with("1") && (track.dcaXY() < tc.fDCAxyCutRange[0] || track.dcaXY() > tc.fDCAxyCutRange[1]); + break; + case eDCAzCut: + doNotPassCut = tc.fParticleCutSwitch[eDCAzCut].starts_with("1") && (track.dcaZ() < tc.fDCAzCutRange[0] || track.dcaZ() > tc.fDCAzCutRange[1]); + break; + } + } // end of if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + + if (doNotPassCut) { + if constexpr (rm == eReal) { + misc.fMiscParticleCutsCounter[eRec]->Fill(i + 0.5, 1); + } + if constexpr (rm == eMC) { + misc.fMiscParticleCutsCounter[eSim]->Fill(i + 0.5, 1); + } + return false; + } + } // end of for (int i = 0; i < EParticleCuts_N; ++i) { + + return true; + } // end of bool particleCuts(T const& track) + + template + void particleHistFill(T1 const& track) + { + if constexpr ((rs == eRec || rs == eRecAndSim) && rm == eReal) { + if constexpr (cuts == eBefore) { + pc.fParticleHist[eHistPt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eBefore]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eBefore]->Fill(track.sign()); + } + if constexpr (cuts == eAfter) { + pc.fParticleHist[eHistPt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHist[eHistPhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHist[eHistEta][eRec][eAfter]->Fill(track.eta()); + pc.fParticleHist[eHistCharge][eRec][eAfter]->Fill(track.sign()); + } + } + + if constexpr ((rs == eSim || rs == eRecAndSim) && rm == eMC) { + if (!track.has_mcParticle()) { + return; + } + auto thisMCParticle = track.mcParticle(); + TParticlePDG* particlePDG = pdg->GetParticle(thisMCParticle.pdgCode()); + float chargeMC = 0.f; + const int chargeMCUnit = 3; + if (particlePDG) { + chargeMC = particlePDG->Charge(); + chargeMC /= chargeMCUnit; + } + if constexpr (cuts == eBefore) { + pc.fParticleHist[eHistPt][eSim][eBefore]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eBefore]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eBefore]->Fill(thisMCParticle.eta()); + if (particlePDG) { + pc.fParticleHist[eHistCharge][eSim][eBefore]->Fill(chargeMC); + } + } + if constexpr (cuts == eAfter) { + pc.fParticleHist[eHistPt][eSim][eAfter]->Fill(thisMCParticle.pt()); + pc.fParticleHist[eHistPhi][eSim][eAfter]->Fill(thisMCParticle.phi()); + pc.fParticleHist[eHistEta][eSim][eAfter]->Fill(thisMCParticle.eta()); + if (particlePDG) { + pc.fParticleHist[eHistCharge][eSim][eAfter]->Fill(chargeMC); + } + } + } // end of if constexpr (rs == eRecAndSim && rm == eMC) { + } // end of void particleHistFill(T1 const& track) + + template + void qaFill(T1 const& collision, T2 const& tracks) + { + auto thisCent = chooseCent(collision, tc.fCentralityEstimator); + auto thisRefMult = chooseMult(collision, tc.fMultiplicityTables); + if constexpr (rm == eReal) { + auto cent1 = chooseCent(collision, tc.fCorrCent[0]); + auto cent2 = chooseCent(collision, tc.fCorrCent[1]); + if constexpr (cuts == eBefore) { + qa.fHistMultNContrib[eBefore]->Fill(thisRefMult, collision.numContrib()); + qa.fHistCorrCent[eBefore]->Fill(cent1, cent2); + if (thisCent > tc.fQASelTrackCentRange[0] && thisCent < tc.fQASelTrackCentRange[1]) { + qa.fHistSelectedTrack[eBefore]->Fill(tracks.size()); + } + } + if constexpr (cuts == eAfter) { + qa.fHistMultNContrib[eAfter]->Fill(thisRefMult, collision.numContrib()); + qa.fHistCorrCent[eAfter]->Fill(cent1, cent2); + } + } + + if constexpr (rs == eRecAndSim && rm == eMC) { + if (!collision.has_mcCollision()) { + return; + } + auto thisMCCollision = collision.mcCollision(); + auto impactParameter = thisMCCollision.impactParameter(); + auto centralityMC = math::PI * impactParameter * impactParameter / tc.fSigmaInel; + if constexpr (cuts == eBefore) { + qa.fHistCentralityRecSim[eBefore]->Fill(thisCent, centralityMC); + } + if constexpr (cuts == eAfter) { + qa.fHistCentralityRecSim[eAfter]->Fill(thisCent, centralityMC); + } + } + } + + // *) Define all member functions to be called in the main process* functions: + template + void steer(T1 const& collision, T2 const& tracks) + { + // Dry run: + if (tc.fDryRun) { + return; + } + auto thisCollCentReal = chooseCent(collision, tc.fCentralityEstimator); + + if (rti.isFirstCollision) { + rti.thisRunNumber = collision.bc().runNumber(); + misc.fMiscHistRunNumber->SetTitle(Form("%d", rti.thisRunNumber)); // Get run number + misc.fMiscHistRunNumber->Fill(0.5, rti.thisRunNumber); + if (tc.fExternalFileSwitch) { + getHistogramWithWeights(tc.fFileWithWeights.c_str(), Form("%d", rti.thisRunNumber)); + } + } + + if (tc.fRunMessageSwitch) { + if (rti.collisionCounter % rti.RunMessagePeriod == 0) { + LOGF(info, "Successfully running, run number is %d", rti.thisRunNumber); + rti.collisionCounter = 0; + } + ++rti.collisionCounter; + } + + // Fill Quality Assurance before cuts + if (tc.fQualityAssuranceSwitch) { + qaFill(collision, tracks); + qaFill(collision, tracks); + } + + // Fill Event Hist before cuts + eventHistFill(collision, tracks); + eventHistFill(collision, tracks); + + bool passEventCutsReal = eventCuts(collision); + // bool passEventCutsMC = eventCuts(collision); // this is currently useless + + // Fill event hist and qa hist after cuts + if (tc.fMasterCutSwitch && passEventCutsReal) { + eventHistFill(collision, tracks); + eventHistFill(collision, tracks); + if (tc.fQualityAssuranceSwitch) { + qaFill(collision, tracks); + qaFill(collision, tracks); + } + } + + std::vector harmonic22 = {-2, 2}; + std::vector harmonic24 = {-2, -2, 2, 2}; + + auto qVecTableReal = initQVectorsTable(4, harmonic24); + auto qVecTableMC = initQVectorsTable(4, harmonic24); + auto qVecTableIV = initQVectorsTable(4, harmonic24); + + std::vector qVecABReal(2, TComplex(0., 0.)); + std::vector qVecABMC(2, TComplex(0., 0.)); + std::vector weightABReal(2, 0.f); + std::vector weightABMC(2, 0.f); + + if (tc.fInternalValidationSwitch) { + double psi = intval.fRandGen->Uniform(0, math::TwoPI); + intval.fIntValFun->SetParameter(0, psi); + } + + // Main loop over particles: + int nPassedTrack = 0; + for (auto const& track : tracks) { + float thisPhiRec = track.phi(); + float thisPt = track.pt(); + + if (tc.fInternalValidationSwitch && passEventCutsReal) { + const float thisPhiIV = intval.fIntValFun->GetRandom(); + updateQVectorsTable(qVecTableIV, thisPhiIV, 1.f); + } + + std::array thisPhiAndPt = {thisPhiRec, 0.f, thisPt}; // {thisPhiRec, thisPhiSim, thisPt} + std::array thisWeights = {1.f, 1.f, 1.f}; // {wPhiRec, wPhiSim, wPt} + + particleHistFill(track); + if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { + particleHistFill(track); + ++nPassedTrack; + if (tc.fExternalFileSwitch) { + for (int k = 0; k < eWeightsHistograms_N; ++k) { + if (k != eWPhiSim && ex.fWeights[k]) { + thisWeights[k] = ex.fWeights[k]->GetBinContent(ex.fWeights[k]->FindBin(thisPhiAndPt[k])); + } + } + } + + float thisWeightReal = thisWeights[eWPhiRec] * thisWeights[eWPt]; + updateQVectorsTable(qVecTableReal, thisPhiRec, thisWeightReal); + auto thisEtaRec = track.eta(); + updateVecForGap(harmonic22, qVecABReal, weightABReal, thisPhiRec, thisEtaRec, thisWeightReal); + } + + if constexpr (rs == eRecAndSim) { + particleHistFill(track); + if (tc.fMasterCutSwitch && passEventCutsReal && particleCuts(track)) { + particleHistFill(track); + + auto thisPhiSim = track.mcParticle().phi(); + auto thisEtaSim = track.mcParticle().eta(); + thisPhiAndPt[eWPhiSim] = thisPhiSim; + if (tc.fExternalFileSwitch && ex.fWeights[eWPhiSim]) { + thisWeights[eWPhiSim] = ex.fWeights[eWPhiSim]->GetBinContent(ex.fWeights[eWPhiSim]->FindBin(thisPhiSim)); + } + + float thisWeightSim = thisWeights[eWPhiSim] * thisWeights[eWPt]; + updateQVectorsTable(qVecTableMC, thisPhiSim, thisWeightSim); + updateVecForGap(harmonic22, qVecABMC, weightABMC, thisPhiSim, thisEtaSim, thisWeightSim); + } + } // end of if constexpr (rs == eRecAndSim) { + } // end of for (auto const& track : tracks) { + if (tc.fMasterCutSwitch && passEventCutsReal && tc.fQualityAssuranceSwitch) { + qa.fHistSelectedTrack[eAfter]->Fill(nPassedTrack); + } + + if (tc.fMasterCutSwitch && passEventCutsReal) { + std::vector result22(2, TComplex(0., 0.)); + std::vector result24(2, TComplex(0., 0.)); + std::vector result22Gap(2, 0.f); + result22 = two(qVecTableReal, harmonic22); + if (noneZeroDenom(result22)) { + obs.fProfCorr[eV22][eRec]->Fill(thisCollCentReal, (result22[0] / result22[1].Re()).Re()); + } + result24 = recursion(4, qVecTableReal, harmonic24); + if (noneZeroDenom(result24)) { + obs.fProfCorr[eV24][eRec]->Fill(thisCollCentReal, (result24[0] / result24[1].Re()).Re()); + } + + result22Gap = {static_cast((qVecABReal[0] * qVecABReal[1]).Re()), weightABReal[0] * weightABReal[1]}; + if (result22Gap[1] != 0) { + obs.fProfCorr[eV22Gap][eRec]->Fill(thisCollCentReal, result22Gap[0] / result22Gap[1]); + } + + if constexpr (rs == eSim || rs == eRecAndSim) { + auto impactParam = collision.mcCollision().impactParameter(); + auto thisCollCentMC = math::PI * impactParam * impactParam / tc.fSigmaInel; + + result22 = two(qVecTableMC, harmonic22); + if (noneZeroDenom(result22)) { + obs.fProfCorr[eV22][eSim]->Fill(thisCollCentMC, (result22[0] / result22[1].Re()).Re()); + } + result24 = recursion(4, qVecTableMC, harmonic24); + if (noneZeroDenom(result24)) { + obs.fProfCorr[eV24][eSim]->Fill(thisCollCentMC, (result24[0] / result24[1].Re()).Re()); + } + + result22Gap = {static_cast((qVecABMC[0] * qVecABMC[1]).Re()), weightABMC[0] * weightABMC[1]}; + if (result22Gap[1] != 0) { + obs.fProfCorr[eV22Gap][eSim]->Fill(thisCollCentMC, result22Gap[0] / result22Gap[1]); + } + } // end of if constexpr (rs == eRecAndSim) { + + if (tc.fInternalValidationSwitch) { + std::vector> harmonicIV = {{-2, 2}, {-3, 3}, {-4, 4}}; + for (int iv = 0; iv < eIntValHarmonics_N; ++iv) { + std::vector result = two(qVecTableIV, harmonicIV[iv]); + if (noneZeroDenom(result)) { + intval.fProfIntVal[iv]->Fill(thisCollCentReal, (result[0] / result[1].Re()).Re()); + } + } + } // end of if (tc.fInternalValidationSwitch) { + } // end of if (tc.fMasterCutSwitch) { + + rti.isFirstCollision = false; // Now the first collision ends + } // end of template void steer(T1 const& collision, T2 const& tracks) { + + // *) Initialize and book all objects: + void init(InitContext&) + { + // *) Set automatically what to process, from an implicit variable "doprocessSomEProcessName" within a PROCESS_SWITCH clause: + tc.fProcess[eProcessRec] = doprocessRec; + tc.fProcess[eProcessRecSim] = doprocessRecSim; + tc.fProcess[eProcessSim] = doprocessSim; + + // *) Set runtime info + rti.isFirstCollision = true; + rti.collisionCounter = 0; + rti.thisRunNumber = 0; + + // *) Set colors + std::vector fillColor(eBeforeAfterCuts_N); + fillColor[eBefore] = kRed - 10; + fillColor[eAfter] = kGreen - 10; + + std::vector lineColor(eBeforeAfterCuts_N); + lineColor[eBefore] = kRed; + lineColor[eAfter] = kGreen; + + // *) Configure your task using configurables in the json file: + tc.fDryRun = cfDryRun; + tc.fCentralityEstimator = cfCentralityEstimator; + tc.fMultiplicityTables = cfMultiplicityTables; + tc.fOccupancyEstimator = cfOccupancyEstimator; + + tc.fExternalFileSwitch = cfExternalFileSwitch; + tc.fFileWithWeights = cfFileWithWeights; + + tc.fMasterCutSwitch = cfMasterCutSwitch; + + tc.fEventCutSwitch = cfEventCutSwitch.value; + tc.fVertexZCutRange = cfVertexZCutRange.value; + tc.fCentCutRange = cfCentCutRange.value; + tc.fNContribCutInf = cfNContribCutInf; + tc.fOccupancyCutSup = cfOccupancyCutSup; + tc.fCorrCent = cfCorrCent.value; + tc.fCorrCentCutRatio = cfCorrCentCutRatio; + tc.fMultNContribBound = cfMultNContribBound.value; + + tc.fParticleCutSwitch = cfParticleCutSwitch.value; + tc.fPtCutRange = cfPtCutRange.value; + tc.fEtaCutRange = cfEtaCutRange.value; + tc.fTPCNumClusterCutRange = cfTPCNumClusterCutRange.value; + tc.fDCAxyCutRange = cfDCAxyCutRange.value; + tc.fDCAzCutRange = cfDCAzCutRange.value; + + tc.fEtaGap = cfEtaGap; + tc.fSigmaInel = cfSigmaInel; + tc.fRNGSeed = cfRNGSeed; + tc.fQualityAssuranceSwitch = cfQualityAssuranceSwitch; + tc.fRunMessageSwitch = cfRunMessageSwitch; + tc.fInternalValidationSwitch = cfInternalValidationSwitch; + tc.fIVHarmonics = cfIVHarmonics.value; + tc.fQASelTrackCentRange = cfQASelTrackCentRange.value; + + // *) Book base list: + auto* temp = new TList(); + temp->SetOwner(true); + fBaseList.setObject(temp); + + // *) Book and nest all other TLists: + // *) Book External Hist List + if (tc.fExternalFileSwitch) { + ex.fExternalHistogramsList = new TList(); + ex.fExternalHistogramsList->SetName("ExternalHistograms"); + ex.fExternalHistogramsList->SetOwner(true); + fBaseList->Add(ex.fExternalHistogramsList); + } + + // *) Book MiscHistograms TLists: + misc.fMiscHistList = new TList(); + misc.fMiscHistList->SetName("MiscHistograms"); + misc.fMiscHistList->SetOwner(true); + fBaseList->Add(misc.fMiscHistList); + + misc.fMiscHistRunNumber = new TProfile("[eRunNumber]", "to be set", 1, 0, 1); + misc.fMiscHistRunNumber->SetStats(false); + misc.fMiscHistRunNumber->GetXaxis()->SetBinLabel(1, "RunNumber"); + misc.fMiscHistList->Add(misc.fMiscHistRunNumber); + + if (doprocessRec || doprocessRecSim) { + misc.fMiscEventCutsCounter[eRec] = new TH1D("[eEventCutsCounter][eRec]", "Event Level Cuts Counter for Reconstructed Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); + misc.fMiscEventCutsCounter[eRec]->SetStats(false); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, tc.fEventCutSwitch[i - 1].c_str()); + } + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eRec]); + + misc.fMiscParticleCutsCounter[eRec] = new TH1D("[eParticleCutsCounter][eRec]", "Particle Level Cuts Counter for Reconstructed Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscParticleCutsCounter[eRec]->SetStats(false); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCutsCounter[eRec]->GetXaxis()->SetBinLabel(i, tc.fParticleCutSwitch[i - 1].c_str()); + } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eRec]); + } + + if (doprocessSim || doprocessRecSim) { + misc.fMiscEventCutsCounter[eSim] = new TH1D("[eEventCutsCounter][eSim]", "Event Level Cuts Counter for Simulated Data (Sequential)", EEventCuts_N, 0, EEventCuts_N); + misc.fMiscEventCutsCounter[eSim]->SetStats(false); + for (int i = 1; i <= EEventCuts_N; ++i) { + misc.fMiscEventCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, tc.fEventCutSwitch[i - 1].c_str()); + } + misc.fMiscHistList->Add(misc.fMiscEventCutsCounter[eSim]); + + misc.fMiscParticleCutsCounter[eSim] = new TH1D("[eParticleCutsCounter][eSim]", "Particle Level Cuts Counter for Simulated Data (Sequential)", EParticleCuts_N, 0, EParticleCuts_N); + misc.fMiscParticleCutsCounter[eSim]->SetStats(false); + for (int i = 1; i <= EParticleCuts_N; ++i) { + misc.fMiscParticleCutsCounter[eSim]->GetXaxis()->SetBinLabel(i, tc.fParticleCutSwitch[i - 1].c_str()); + } + misc.fMiscHistList->Add(misc.fMiscParticleCutsCounter[eSim]); + } + + // *) Book particle TLists: + pc.fParticleHistList = new TList(); + pc.fParticleHistList->SetName("ParticleHistograms"); + pc.fParticleHistList->SetOwner(true); + fBaseList->Add(pc.fParticleHistList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + std::vector> lParticleHistBins(eParticleHistograms_N); + + lParticleHistBins[eHistPt] = cfPtBins.value; + lParticleHistBins[eHistPhi] = cfPhiBins.value; + lParticleHistBins[eHistEta] = cfEtaBins.value; + lParticleHistBins[eHistCharge] = {5, -2.5, 2.5}; + + if (doprocessRec || doprocessRecSim) { + for (int i = 0; i < eParticleHistograms_N; ++i) { + const auto& bins = lParticleHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + pc.fParticleHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s Distribution for Reconstructed Particles %s Cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); + pc.fParticleHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + pc.fParticleHistList->Add(pc.fParticleHist[i][eRec][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } + } // end of if (doprocessRec || doprocessRecSim) { + + if (doprocessSim || doprocessRecSim) { + for (int i = 0; i < eParticleHistograms_N; ++i) { + const auto& bins = lParticleHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + pc.fParticleHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", ParticleHistNames[i], CutsNames[ba]), Form("%s Distribution for Simulated Particles %s Cuts", ParticleHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + pc.fParticleHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", ParticleHistTitles[i])); + pc.fParticleHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + pc.fParticleHistList->Add(pc.fParticleHist[i][eSim][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } + } // end of if (doprocessSim || doprocessRecSim) { + + // *) Book event-level histograms + ec.fEventHistList = new TList(); + ec.fEventHistList->SetName("EventHistograms"); + ec.fEventHistList->SetOwner(true); + fBaseList->Add(ec.fEventHistList); + + std::vector> lEventHistBins(eEventHistograms_N); + + lEventHistBins[eHistCentrality] = cfCentBins.value; + lEventHistBins[eHistMult] = cfMultBins.value; + lEventHistBins[eHistRefMult] = cfRefMultBins.value; + lEventHistBins[eHistVertexX] = cfVxBins.value; + lEventHistBins[eHistVertexY] = cfVyBins.value; + lEventHistBins[eHistVertexZ] = cfVzBins.value; + lEventHistBins[eHistImpactParameter] = cfIpBins.value; + lEventHistBins[eHistNumContrib] = cfNumContribBins.value; + lEventHistBins[eHistOccupancy] = cfOccuBins.value; + + if (doprocessRec || doprocessRecSim) { + for (int i = 0; i < eEventHistograms_N; ++i) { + if (i == eHistImpactParameter) { // no impact parameter for Rec + continue; + } + const auto& bins = lEventHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + ec.fEventHist[i][eRec][ba] = new TH1F(Form("[%s][eRec][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (Reconstructed) %s Cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eRec][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); + ec.fEventHist[i][eRec][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + ec.fEventHistList->Add(ec.fEventHist[i][eRec][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } // end of for (int i = 0; i < eEventHistograms_N; ++i) { + ec.fEventHist[eHistCentrality][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[tc.fCentralityEstimator])); + ec.fEventHist[eHistCentrality][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistCentrality], CentralityEstimatorNames[tc.fCentralityEstimator])); + ec.fEventHist[eHistRefMult][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[tc.fMultiplicityTables])); + ec.fEventHist[eHistRefMult][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], MultiplicityTablesNames[tc.fMultiplicityTables])); + ec.fEventHist[eHistOccupancy][eRec][eBefore]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], OccupancyEstimatorNames[tc.fOccupancyEstimator])); + ec.fEventHist[eHistOccupancy][eRec][eAfter]->GetXaxis()->SetTitle(Form("%s (%s)", EventHistTitles[eHistRefMult], OccupancyEstimatorNames[tc.fOccupancyEstimator])); + } // end of if (doprocessRec || doprocessRecSim) { + + if (doprocessSim || doprocessRecSim) { + for (int i = 0; i < eEventHistograms_N; ++i) { + if (i == eHistRefMult || i == eHistNumContrib || i == eHistOccupancy) { // no RefMult for Sim + continue; + } + const auto& bins = lEventHistBins[i]; + for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + if (!tc.fMasterCutSwitch && ba == eAfter) { + continue; + } + ec.fEventHist[i][eSim][ba] = new TH1F(Form("[%s][eSim][%s]", EventHistNames[i], CutsNames[ba]), Form("%s (Simulated) %s Cuts", EventHistTitles[i], CutsTitles[ba]), static_cast(bins[0]), bins[1], bins[2]); + ec.fEventHist[i][eSim][ba]->GetXaxis()->SetTitle(Form("%s", EventHistTitles[i])); + ec.fEventHist[i][eSim][ba]->SetColors(lineColor[ba], -1, fillColor[ba]); + ec.fEventHistList->Add(ec.fEventHist[i][eSim][ba]); + } // end of for (int ba = 0; ba < eBeforeAfterCuts_N; ++ba) { + } // end of for (int i = 0; i < eEventHistograms_N; ++i) { + ec.fEventHist[eHistCentrality][eSim][eBefore]->GetXaxis()->SetTitle(Form("%s (calculated from I.P.)", EventHistTitles[eHistCentrality])); + ec.fEventHist[eHistCentrality][eSim][eAfter]->GetXaxis()->SetTitle(Form("%s (calculated from I.P.)", EventHistTitles[eHistCentrality])); + } // end of if (doprocessSim || doprocessRecSim) { + + // *) Book observales TLists: + obs.fObservablesList = new TList(); + obs.fObservablesList->SetName("Observables"); + obs.fObservablesList->SetOwner(true); + fBaseList->Add(obs.fObservablesList); + + const std::vector defaultCentBoundaries = {0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f}; + const int nDefaultCentBins = defaultCentBoundaries.size() - 1; + + if (doprocessRec || doprocessRecSim) { + for (int i = 0; i < eObservables_N; ++i) { + obs.fProfCorr[i][eRec] = new TProfile(Form("[%s][eRec]", ObservablesNames[i]), Form("%s for Reconstructed Data after Cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eRec]->SetStats(false); + obs.fProfCorr[i][eRec]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCentralityEstimator])); + obs.fProfCorr[i][eRec]->GetYaxis()->SetTitle(ObservablesTitles[i]); + obs.fProfCorr[i][eRec]->Sumw2(); + obs.fObservablesList->Add(obs.fProfCorr[i][eRec]); + } + } + + if (doprocessSim || doprocessRecSim) { + for (int i = 0; i < eObservables_N; ++i) { + obs.fProfCorr[i][eSim] = new TProfile(Form("[%s][eSim]", ObservablesNames[i]), Form("%s for Simulated Data after Cuts", ObservablesTitles[i]), nDefaultCentBins, defaultCentBoundaries.data()); + obs.fProfCorr[i][eSim]->SetStats(false); + obs.fProfCorr[i][eSim]->GetXaxis()->SetTitle("Centrality (calculated from I.P.)"); + obs.fProfCorr[i][eSim]->GetYaxis()->SetTitle(ObservablesTitles[i]); + obs.fProfCorr[i][eSim]->Sumw2(); + obs.fObservablesList->Add(obs.fProfCorr[i][eSim]); + } + } + + // *) Book and QA TLists: + if (tc.fQualityAssuranceSwitch) { + qa.fQualityAssuranceList = new TList(); + qa.fQualityAssuranceList->SetName("QualityAssurance"); + qa.fQualityAssuranceList->SetOwner(true); + fBaseList->Add(qa.fQualityAssuranceList); + + if (doprocessRec || doprocessRecSim) { + const auto& nContribBins = lEventHistBins[eHistNumContrib]; + const auto& refMultBins = lEventHistBins[eHistRefMult]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistMultNContrib[i] = new TH2F(Form("fHistMultNContrib[%s]", CutsNames[i]), Form("refMult vs. nContributors %s Cuts", CutsNames[i]), static_cast(refMultBins[0]), refMultBins[1], refMultBins[2], static_cast(nContribBins[0]), nContribBins[1], nContribBins[2]); + qa.fHistMultNContrib[i]->GetXaxis()->SetTitle(Form("Reference Multiplicity (%s)", MultiplicityTablesNames[tc.fMultiplicityTables])); + qa.fHistMultNContrib[i]->GetYaxis()->SetTitle("Number of contributors"); + qa.fHistMultNContrib[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistMultNContrib[i]); + } + + const auto& centBins = lEventHistBins[eHistCentrality]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistCorrCent[i] = new TH2F(Form("fHistCorrCent[%s]", CutsNames[i]), Form("Centrality (%s) vs. Centrality (%s) %s Cuts", CentralityEstimatorNames[tc.fCorrCent[0]], CentralityEstimatorNames[tc.fCorrCent[1]], CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); + qa.fHistCorrCent[i]->GetXaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCorrCent[0]])); + qa.fHistCorrCent[i]->GetYaxis()->SetTitle(Form("Centrality (%s)", CentralityEstimatorNames[tc.fCorrCent[1]])); + qa.fHistCorrCent[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistCorrCent[i]); + } + + std::vector lQASelTrackBins = cfQASelTrackBins.value; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistSelectedTrack[i] = new TH1F(Form("fHistSelectedTrack[%s]", CutsNames[i]), Form("Selected Tracks in %.1f-%.1f%% %s cuts", tc.fQASelTrackCentRange[0], tc.fQASelTrackCentRange[1], CutsNames[i]), static_cast(lQASelTrackBins[0 + i * 3]), lQASelTrackBins[1 + i * 3], lQASelTrackBins[2 + i * 3]); + qa.fHistSelectedTrack[i]->GetXaxis()->SetTitle("Number of selected tracks"); + qa.fHistSelectedTrack[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistSelectedTrack[i]); + } + } // end of if (doprocessRec || doprocessRecSim) { + + if (doprocessRecSim) { + const auto& centBins = lEventHistBins[eHistCentrality]; + for (int i = 0; i < eBeforeAfterCuts_N; ++i) { + qa.fHistCentralityRecSim[i] = new TH2F(Form("fHistCentralityRecSim[%s]", CutsNames[i]), Form("Centrality Rec vs Sim %s Cuts", CutsNames[i]), static_cast(centBins[0]), centBins[1], centBins[2], static_cast(centBins[0]), centBins[1], centBins[2]); + qa.fHistCentralityRecSim[i]->GetXaxis()->SetTitle("Centrality (reconstructed)"); + qa.fHistCentralityRecSim[i]->GetYaxis()->SetTitle("Centrality (simulated)"); + qa.fHistCentralityRecSim[i]->SetStats(false); + qa.fQualityAssuranceList->Add(qa.fHistCentralityRecSim[i]); + } + } // end of if (doprocessRecSim) { + } // end of if (tc.fQualityAssuranceSwitch) { + + // *) Book InternalValidation TLists: + if (tc.fInternalValidationSwitch) { + intval.fRandGen = new TRandom3(tc.fRNGSeed); + intval.fIntValFun = new TF1("fIntValFun", "1 + 2*0.04*cos(1*(x - [0])) + 2*[1]*cos(2*(x - [0])) + 2*[2]*cos(3*(x - [0])) + 2*[3]*cos(4*(x - [0]))", 0., math::TwoPI); + for (std::size_t i = 1; i <= tc.fIVHarmonics.size(); ++i) { + intval.fIntValFun->SetParameter(i, tc.fIVHarmonics[i - 1]); + } + + intval.fIntValList = new TList(); + intval.fIntValList->SetName("InternalValidation"); + intval.fIntValList->SetOwner(true); + fBaseList->Add(intval.fIntValList); + + for (int i = 0; i < eIntValHarmonics_N; ++i) { + intval.fProfIntVal[i] = new TProfile(Form("[%s]", IntValNames[i]), Form("Internal Validation for %s with True Value %f", IntValTitles[i], std::pow(tc.fIVHarmonics[i], 2)), nDefaultCentBins, defaultCentBoundaries.data()); + intval.fProfIntVal[i]->SetStats(false); + intval.fProfIntVal[i]->GetXaxis()->SetTitle("Centrality (calculated from I.P.)"); + intval.fProfIntVal[i]->GetYaxis()->SetTitle(IntValTitles[i]); + intval.fProfIntVal[i]->Sumw2(); + intval.fIntValList->Add(intval.fProfIntVal[i]); + } + } + + geoCuts.fMultNContribBounds[eUpper] = new TF1("fMultNContribBounds[eUpper]", tc.fMultNContribBound[eUpper].c_str(), lEventHistBins[eHistRefMult][1], lEventHistBins[eHistRefMult][2]); + geoCuts.fMultNContribBounds[eLower] = new TF1("fMultNContribBounds[eLower]", tc.fMultNContribBound[eLower].c_str(), lEventHistBins[eHistRefMult][1], lEventHistBins[eHistRefMult][2]); + } // end of void init(InitContext&) { + + // A) Process only reconstructed data: + void processRec(CollisionRec const& collision, aod::BCs const&, TracksRec const& tracks) + { + // *) steer all analysis steps: + steer(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCorrelationsMei, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version + + // B) Process both reconstructed and corresponding MC truth simulated data: + void processRecSim(CollisionRecSim const& collision, aod::BCs const&, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) + { + steer(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCorrelationsMei, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + + // C) Process only simulated data: + void processSim(CollisionSim const& /*collision*/, aod::BCs const&, TracksSim const& /*tracks*/) + { + // steer(collision, tracks); // TBI 20241105 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... + } + PROCESS_SWITCH(MultiparticleCorrelationsMei, processSim, "process only simulated data", false); + +}; // struct MultiparticleCorrelationsMei { + +// *) The final touch: +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} // WorkflowSpec... diff --git a/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx new file mode 100644 index 00000000000..0a27a5ef7b4 --- /dev/null +++ b/PWGCF/MultiparticleCorrelations/Tasks/multiparticleCumulants.cxx @@ -0,0 +1,2308 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file multiparticleCumulants.cxx +/// \brief Task for producing multiparticle cumulants +/// \author Pei-Ying Kuan, TU München, pei-ying.kuan@cern.ch + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Definitions of join tables for Run 3 analysis: +using EventSelection = soa::Join; +using CollisionRec = soa::Join::iterator; +using CollisionRecSim = soa::Join::iterator; +using CollisionSim = aod::McCollision; +using TracksRec = soa::Join; +using TrackRec = soa::Join::iterator; +using TracksRecSim = soa::Join; // + use in json "isMC" : "true" +using TrackRecSim = soa::Join::iterator; +using TracksSim = aod::McParticles; +using TrackSim = aod::McParticles::iterator; + +using namespace std; + +// *) Define enums: +enum EnRlMc { + eRl = 0, + eMc +}; + +enum EnRecSim { + eRec = 0, + eSim, + eRecAndSim +}; + +enum EnProcess { + eProcessRec = 0, // Run 3, only reconstructed + eProcessRecSim, // Run 3, both reconstructed and simulated + eProcessSim, // Run 3, only simulated + eProcess_N +}; + +enum EnEventHistograms { + eCent, + eMult, + eVertexX, + eVertexY, + eVertexZ, + eNumContrib, + eNumTrack, + eEventHistograms_N +}; + +static constexpr std::array EventHistNames = { + "Centrality", + "Multiplicity", + "VertexX", + "VertexY", + "VertexZ", + "NumContrib", + "NumTrack"}; + +enum EnParticleHistograms { + ePt, + eEta, + ePhi, + eDCAXY, + eDCAZ, + eParticleHistograms_N +}; + +static constexpr std::array ParticleHistNames = { + "Pt", + "Eta", + "Phi", + "DCAXY", + "DCAZ"}; + +enum EnQAHistograms { + eQACent, + eQAMultNumContrib, + eQAHistograms_N +}; + +enum EnCorrHistograms { + eCorrCent, + eCorrMult, + eCorrNumContribMult, + eCorrHistograms_N +}; + +static constexpr std::array CorrHistNames = { + "Centrality", + "Multiplicity", + "NContributionMultiplicity"}; + +enum EnCorrBound { + eCentABBound, + eCentACBound, + eCentBCBound, + eMultABBound, + eMultACBound, + eMultBCBound, + eNumContribMultBound, + eCorrBound_N +}; + +enum EnCentEstm { + eCentFT0C, + eCentFT0M, + eCentFV0A, + eCentEstm_N +}; + +static constexpr std::array CentEstmNames = { + "FT0C", + "FT0M", + "FV0A"}; + +enum EnMultEstm { + eMultFT0C, + eMultFT0M, + eMultFV0A, + eMultEstm_N +}; + +static constexpr std::array MultEstmNames = { + "FT0C", + "FT0M", + "FV0A"}; + +enum EnEstmCorr { + eFT0CFT0M, + eFT0CFV0A, + eFT0MFV0A, + eEstmCorr_N +}; + +static constexpr std::array EstmCorrName = { + "FT0CFT0M", + "FT0CFV0A", + "FT0MFV0A"}; + +enum EnBeforeAfter { + eBefore, + eAfter, + eBeforeAfter_N +}; + +static constexpr std::array BeforeAfterNames = { + "before", + "after", + "Before", + "After"}; + +static constexpr int NumHarmonics = 3; // n = 2, 3, 4 +static constexpr int NumTwoPCorrBins = NumHarmonics + 1; // <<2>>_n = , , , , in this case 3 +static constexpr int NumThreePCorrBins = 3; // <<3>>_{n1, n2, n3}, (n1, n2, n3) = (2, -1, -1), (3, -2, -1), (4, -2, -2), in this case 3 +static constexpr int NumFourPCorrBins = (NumHarmonics * (NumHarmonics + 1)) / 2; // <<4>>_{n1, n1, n2, n2} = , , , , , , in this case 6 +static constexpr int NumSixPCorrBins = (NumHarmonics * (NumHarmonics - 1) * (NumHarmonics - 2)) / 6; // <<6>>_{n1, n1, n2, n2, n3, n3} = , in this case 1 +static constexpr std::array, NumThreePCorrBins> ThreePHarmonicIndex{{{2, 1, 1}, {3, 2, 1}, {4, 2, 2}}}; +static constexpr std::array, NumFourPCorrBins> FourPHarmonicIndex{{{2, 2}, {2, 3}, {2, 4}, {3, 3}, {3, 4}, {4, 4}}}; +static constexpr int NumEtaGap = 3; + +// *) Main task: +struct MultiparticleCumulants { // this name is used in lower-case format to name the TDirectoryFile in AnalysisResults.root + + // *) Base TList to hold all output objects: + TString sBaseListName = "Default list name"; + OutputObj fBaseList{sBaseListName.Data(), OutputObjHandlingPolicy::AnalysisObject, OutputObjSourceType::OutputObjSource}; + + // *) Service: + Service ccdb{}; // support for offline callibration data base + Service pdg{}; + + // *) Define configurables: + Configurable cfDryRun{"cfDryRun", false, "book all histos and run without filling and calculating anything"}; + Configurable cfCentEstm{"cfCentEstm", "FT0M", "centrality estimator: FT0C, FT0M, FV0A"}; + Configurable cfMultEstm{"cfMultEstm", "FV0A", "multiplicity estimator: FT0C, FT0M, FV0A"}; + + Configurable cfQASwitch{"cfQASwitch", true, "quality assurance switch"}; + Configurable cfWeightSwitch{"cfWeightSwitch", true, "weight switch"}; + Configurable cfPrintSwitch{"cfPrintSwitch", true, "printing result switch"}; + + // *) Event cut switches + Configurable cfGeneralCutSwitch{"cfGeneralCutSwitch", true, "NoCollInTimeRangeStandard, NoCollInRofStandard, NoSameBunchPileup, IsVertexITSTPC, IsGoodITSLayersAll, IsGoodZvtxFT0vsPV, NoHighMultCollInPrevRof"}; + Configurable cfVertexZCutSwitch{"cfVertexZCutSwitch", true, "vertex z cut switch"}; + Configurable cfSel8CutSwitch{"cfSel8CutSwitch", true, "Sel8 cut switch"}; + Configurable cfCentCutSwitch{"cfCentCutSwitch", true, "centrality cut switch"}; + Configurable cfNumContribCutSwitch{"cfNumContribCutSwitch", true, "NContribution cut switch"}; + Configurable cfNumTrackCutSwitch{"cfNumTrackCutSwitch", true, "NTrack cut switch"}; + Configurable cfOccupancyCutSwitch{"cfOccupancyCutSwitch", true, "occupancy cut switch"}; + Configurable cfCentCorrCutSwitch{"cfCentCorrCutSwitch", true, "centrality correlation outlier cut switch"}; + Configurable cfMultCorrCutSwitch{"cfMultCorrCutSwitch", true, "multiplicity correlation outlier cut switch"}; + Configurable cfNumContribMultCorrCutSwitch{"cfNumContribMultCorrCutSwitch", true, "NContribution vs. multiplicity correlation outlier cut switch"}; + + // *) Particle cut switches + Configurable cfPtCutSwitch{"cfPtCutSwitch", true, "Pt cut switch"}; + Configurable cfEtaCutSwitch{"cfEtaCutSwitch", true, "Eta cut switch"}; + Configurable cfSignCutSwitch{"cfSignCutSwitch", true, "Charge cut switch"}; + Configurable cfTpcNClsFoundCutSwitch{"cfTpcNClsFoundCutSwitch", true, ""}; + Configurable cfDCAXYCutSwitch{"cfDCAXYCutSwitch", true, ""}; + Configurable cfDCAZCutSwitch{"cfDCAZCutSwitch", true, ""}; + Configurable cfHasTpcItsCutSwitch{"cfHasTpcItsCutSwitch", false, ""}; + + // *) Event cut + Configurable> cfVertexZCut{"cfVertexZCut", {-10., 10.}, "vertex z position range: {min, max}[cm]"}; + Configurable> cfCentCut{"cfCentCut", {10., 20.}, "centrality range: {min, max}[%]"}; + Configurable> cfNumContribCut{"cfNumContribCut", {0, 3000.}, "NContribution range: {min, max}"}; + Configurable> cfNumTrackCut{"cfNumTrackCut", {0, 3000.}, "NTrack range: {min, max}"}; + Configurable> cfOccupancyCut{"cfOccupancyCut", {0, 6000.}, "Occupancy range: {min, max}"}; + Configurable> cfCentABCorrCut{"cfCentABCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; + Configurable> cfCentACCorrCut{"cfCentACCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; + Configurable> cfCentBCCorrCut{"cfCentBCCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M centrality 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; + Configurable> cfMultABCorrCut{"cfMultABCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FT0M multiplicities 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; + Configurable> cfMultACCorrCut{"cfMultACCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0C vs. FV0A multiplicities 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; + Configurable> cfMultBCCorrCut{"cfMultBCCorrCut", {1.4, 300., 1.4, 300.}, "parameters of limits in FT0M vs. FV0A multiplicities 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; + Configurable> cfNumContribMultCorrCut{"cfNumContribMultCorrCut", {0.035, 100., 0.024, -600.}, "parameters of limits in NContribution vs. multiplicity 2D histograms, cx+d < y < ax+b: {a,b,c,d}"}; + + // *) Particle cut + Configurable> cfPtCut{"cfPtCut", {0.2, 5.0}, "Pt range: {min, max}[GeV], with convention: min < Pt < max"}; + Configurable> cfEtaCut{"cfEtaCut", {-0.8, 0.8}, "Eta range: {min, max}, with convention: min < Eta < max"}; + Configurable> cfSignCut{"cfSignCut", {1, 0, 1}, "sign of charge, 1 to keep and 0 to discard, {negative, neutral, positive}"}; + Configurable> cfTpcNClsFoundCut{"cfTpcNClsFoundCut", {70., 160.}, "range of found TPC clusters for this track geometry: {min, max}"}; + Configurable> cfDCAXYCut{"cfDCAXYCut", {-3.2, 3.2}, "range of distance-of-closest-approach (DCA) of the extrapolated track to the primary position in XY-direction: {min, max}[cm]"}; + Configurable> cfDCAZCut{"cfDCAZCut", {-2.4, 2.4}, "range of distance-of-closest-approach (DCA) of the extrapolated track to the primary position in Z-direction: {min, max}[cm]"}; + Configurable> cfHasTpcItsCut{"cfHasTpcItsCut", {1, 1}, "1 to keep tracks that have {TPC, ITS} information"}; + Configurable> cfEtaGap{"cfEtaGap", {0.4, 0.8, 1.0}, "|dEta| > gap"}; + + // *) Others + Configurable cfFileWithWeights{"cfFileWithWeights", "/scratch3/go52dab/O2tutorial/tutorial3-6/weights.root", "path to external ROOT file which holds all particle weights in O2 format"}; + + // *) Bins + Configurable> cfPtBins{"cfPtBins", {1000, 0., 100.}, "nPtBins, ptMin, ptMax"}; + Configurable> cfEtaBins{"cfEtaBins", {1000, -1., 1.}, "nEtaBins, EtaMin, EtaMax"}; + Configurable> cfPhiBins{"cfPhiBins", {1000, 0., o2::constants::math::TwoPI}, "nPhiBins, phiMin, phiMax"}; + Configurable> cfDCAXYBins{"cfDCAXYBins", {1000, -5., 5.}, "nDCAXYBins, DCAXYMin, DCAXYMax"}; + Configurable> cfDCAZBins{"cfDCAZBins", {1000, -5., 5.}, "nDCAZBins, DCAZMin, DCAZMax"}; + + Configurable> cfCentBins{"cfCentBins", {100, 0., 100.}, "nCenBins, cenMin, cenMax"}; + Configurable> cfFT0CMultBins{"cfFT0CMultBins", {100, 0., 60000.}, "nMultBins, MultMin, MultMax"}; + Configurable> cfFT0MMultBins{"cfFT0MMultBins", {100, 0., 250000.}, "nMultBins, MultMin, MultMax"}; + Configurable> cfFV0AMultBins{"cfFV0AMultBins", {100, 0., 200000.}, "nMultBins, MultMin, MultMax"}; + Configurable> cfVerXBins{"cfVerXBins", {100, -0.05, 0.05}, "nVerXBins, VerXMin, VerXMax"}; + Configurable> cfVerYBins{"cfVerYBins", {100, -0.05, 0.05}, "nVerYBins, VerYMin, VerYMax"}; + Configurable> cfVerZBins{"cfVerZBins", {100, -50., 50.}, "nVerZBins, VerZMin, VerZMax"}; + Configurable> cfNumContribBins{"cfNumContribBins", {100, 0., 5000.}, "nNumContribBins, NumContribMin, NumContribMax"}; + Configurable> cfNumTrackBins{"cfNumTrackBins", {100, 0., 5000.}, "nNumTrackBins, NumTrackMin, NumTrackMax"}; + + Configurable> cfTwoParticleCorrBins{"cfTwoParticleCorrBins", {100, 0., 2e-3}, "nTwoParticleCorrBins, TwoParticleCorrMin, TwoParticleCorrMax"}; + Configurable> cfThreeParticleCorrBins{"cfThreeParticleCorrBins", {100, 0., 2e-4}, "nThreeParticleCorrBins, ThreeParticleCorrMin, ThreeParticleCorrMax"}; + Configurable> cfFourParticleCorrBins{"cfFourParticleCorrBins", {100, 0., 1e-7}, "nFourParticleCorrBins, FourParticleCorrMin, FourParticleCorrMax"}; + Configurable> cfSixParticleCorrBins{"cfSixParticleCorrBins", {100, 0., 1e-11}, "nSixParticleCorrBins, SixParticleCorrMin, SixParticleCorrMax"}; + Configurable> cfNumTrackTotalBins{"cfNumTrackTotalBins", {100, 0, 3000}, "nNumTrackTotalBins, NumTrackTotalMin, NumTrackTotalMax"}; + + // *) Define and initialize all data members to be called in the main process* functions: + // **) Task configuration: + struct TaskConfiguration { + std::array fProcess = {{false}}; // Set what to process. See enum EnProcess for full description. Set via implicit variables within a PROCESS_SWITCH clause. + bool fDryRun = false; + + std::string fCentEstm = "FT0M"; + std::string fMultEstm = "FV0A"; + + bool fPrintSwitch = true; + + bool fGeneralCutSwitch = true; + bool fVertexZCutSwitch = true; + bool fSel8CutSwitch = true; + bool fCentCutSwitch = true; + bool fNumContribCutSwitch = true; + bool fNumTrackCutSwitch = true; + bool fOccupancyCutSwitch = true; + bool fCentCorrCutSwitch = true; + bool fMultCorrCutSwitch = true; + bool fNumContribMultCorrCutSwitch = true; + + bool fPtCutSwitch = true; + bool fEtaCutSwitch = true; + bool fSignCutSwitch = true; + bool fTpcNClsFoundCutSwitch = true; + bool fDCAXYCutSwitch = true; + bool fDCAZCutSwitch = true; + bool fHasTpcItsCutSwitch = true; + + std::vector fVertexZCut = {-10., 10.}; + std::vector fCentCut = {10., 20.}; + std::vector fNumContribCut = {0, 3000.}; + std::vector fNumTrackCut = {0, 350.}; + std::vector fOccupancyCut = {0, 6000.}; + std::vector>> fCentAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; + std::vector>> fMultAllCorrCut = {{{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}, {{0., 0., 0., 0.}, {0., 0., 0., 0.}, {0., 0., 0., 0.}}}; + std::vector fNumContribMultCorrCut = {0.024, -600., 0.035, 100.}; + std::vector fPtCut = {0.2, 5.0}; + std::vector fEtaCut = {-0.8, 0.8}; + std::vector fSignCut = {1, 0, 1}; + std::vector fTpcNClsFoundCut = {70., 160.}; + std::vector fDCAXYCut = {-3.2, 3.2}; + std::vector fDCAZCut = {-2.4, 2.4}; + std::vector fHasTpcItsCut = {1, 1}; + std::vector fEtaGap = {0.4, 0.8, 1.0}; + + std::vector fPtBins = {1000, 0., 100.}; + std::vector fEtaBins = {100, -1., 1.}; + std::vector fPhiBins = {1000, 0., o2::constants::math::TwoPI}; + std::vector fDCAXYBins = {100, -5., 5.}; + std::vector fDCAZBins = {100, -5., 5.}; + + std::vector fCentBins = {100, 0., 100.}; + std::vector fMultBins = {100, 0, 5000}; + std::vector fFT0CMultBins = {100, 0., 60000.}; + std::vector fFT0MMultBins = {100, 0., 250000.}; + std::vector fFV0AMultBins = {100, 0., 200000.}; + + std::vector fVerXBins = {100, -0.05, 0.05}; + std::vector fVerYBins = {100, -0.05, 0.05}; + std::vector fVerZBins = {100, -50., 50.}; + std::vector fNumContribBins = {100, 0, 5000}; + std::vector fNumTrackBins = {100, 0, 5000}; + std::vector fTwoParticleCorrBins = {100, 0, 2e-3}; + std::vector fThreeParticleCorrBins = {100, 0., 2e-4}; + std::vector fFourParticleCorrBins = {100, 0, 1e-7}; + std::vector fSixParticleCorrBins = {100, 0, 1e-11}; + std::vector fNumTrackTotalBins = {100, 0, 3000}; + + std::string fFileWithWeights = "/scratch3/go52dab/O2tutorial/analysis_code/weights.root"; + + } tc; + + struct ParticleHistograms { + TList* fParticleHistogramsList = nullptr; + std::array, eBeforeAfter_N>, eParticleHistograms_N> fParticleHistograms{}; // [type][before/after cut][rec/sim] + } pc; + + struct EventHistograms { + TList* fEventHistogramsList = nullptr; + std::array, eBeforeAfter_N>, eEventHistograms_N> fEventHistograms{}; // [type][before/after cut][rec/sim] + } ev; + + struct QAHistograms { + bool fQASwitch = kTRUE; + TList* fQAHistogramsList = nullptr; + std::array, eQAHistograms_N> fQAHistograms{}; // [type][before/after cut] + } qa; + + struct CorrHistograms { + TList* fCorrHistogramsList = nullptr; + std::array, eMultEstm_N>, eMultEstm_N>, eCorrHistograms_N - 1> fCorrHistograms{}; // [mult/cent][type][type][before/after cut] + std::array, eCorrBound_N> fCorrBounds{}; // [centAB/centAC/centBC/multAB/multAC/multBC/NumContrib vs. mult][upper/lower] + } cr; + + struct WeightHistograms { + bool fWeightSwitch = kTRUE; + TList* fWeightHistogramsList = nullptr; + // Fill phi/pt histograms with that run number: + std::map fPtRealByRunMap; // MC rec (too lazy to change name) data pt histograms, valid if processMonteCarlo + std::map fPtMCByRunMap; // MC sim (too lazy to change name) data pt histograms, valid if processMonteCarlo + std::map fPhiByRunMap; // Phi histograms, valid if processRealData + // Make weight histograms locally. Upload weight histograms to CCDB: + std::vector fWeightHistograms; // Get all weight histograms with that run number + std::map fPhiWeightHistogramsMap; // Get phi weight histograms + std::map fPtWeightHistogramsMap; // Get pt weight histograms + // Null weight histograms. Use them when no weight histograms found in the given run number: + TH1F* fDummyPhiWeightHistogram = nullptr; + TH1F* fDummyPtWeightHistogram = nullptr; + } wt; + + struct MultiparticleCorrelationCalculation { + int h1 = 0; + int h2 = 0; + int h3 = 0; + int h4 = 0; + int h5 = 0; + int h6 = 0; + int h7 = 0; + int h8 = 0; + // Book Q-vector components: + static constexpr int MaxCorrelator = 6; // <>, in this case 6 + static constexpr int MaxHarmonic = ((MaxCorrelator * (MaxCorrelator + 1)) / 2 - 1) * 2 + 1; // (2 + 3 + ... + n) * 2 + 1, in this case need (2 + 3 + 4) * 2 + 1 = 19 at least for SC(2,3,4) + static constexpr int MaxPower = MaxCorrelator + 1; + + std::array, MaxHarmonic> fQvectorBefore; + std::array, MaxHarmonic> fQvectorAfter; + std::array, MaxHarmonic>, NumEtaGap> fQvectorBeforeA; + std::array, MaxHarmonic>, NumEtaGap> fQvectorBeforeB; + std::array, MaxHarmonic>, NumEtaGap> fQvectorAfterA; + std::array, MaxHarmonic>, NumEtaGap> fQvectorAfterB; + } mcc; + + struct MultiparticleCorrelationProfile { + TList* fMultiparticleCorrelationProfilesList = nullptr; + TList* fMultiparticleCorrelationBackupList = nullptr; + std::map fMultiparticleCorrelationByRunMap; + std::array fTwoParticleCorrelationProfiles{}; // [cut] + std::array fThreeParticleCorrelationProfiles{}; + std::array fFourParticleCorrelationProfiles{}; + std::array fSixParticleCorrelationProfiles{}; + std::array, NumEtaGap> fTwoParticleCorrelationGapProfiles{}; + std::array, eBeforeAfter_N>, eBeforeAfter_N> fTwoParticleCorrelationHistograms{}; // [cut][event weight][n] + std::array, eBeforeAfter_N>, eBeforeAfter_N> fThreeParticleCorrelationHistograms{}; + std::array, eBeforeAfter_N>, eBeforeAfter_N> fFourParticleCorrelationHistograms{}; + std::array, eBeforeAfter_N>, eBeforeAfter_N> fSixParticleCorrelationHistograms{}; + std::array, NumEtaGap> fTwoParticleCorrelationGapHistograms{}; + std::array fTwoParticleCorrelationNumTrackProfiles{}; + std::array fThreeParticleCorrelationNumTrackProfiles{}; + std::array fFourParticleCorrelationNumTrackProfiles{}; + } mc; + + struct EventByEventQuantities { + int fRunNumber = 0; + float fReferenceMultiplicity = 0.; + std::array fMultiplicityMaxEbye{}; + float fCentrality = 0.; + float fCentralitySim = 0.; + float fImpactParameter = 0.; + float fNumContrib = 0.; + std::array, eBeforeAfter_N> fTwoParticleCorrelationEbye{}; // <<2>> [before, after][, ,, ] + std::array, eBeforeAfter_N> fTwoParticleCorrelationMinEbye{}; + std::array, eBeforeAfter_N> fTwoParticleCorrelationMaxEbye{}; + std::array, eBeforeAfter_N> fFourParticleCorrelationEbye{}; // <<4>> [before, after][, , , , , ] + std::array, eBeforeAfter_N> fFourParticleCorrelationMinEbye{}; + std::array, eBeforeAfter_N> fFourParticleCorrelationMaxEbye{}; + std::array, eBeforeAfter_N> fThreeParticleCorrelationEbye{}; // <<3>> [before, after][] + std::array, eBeforeAfter_N> fThreeParticleCorrelationMinEbye{}; + std::array, eBeforeAfter_N> fThreeParticleCorrelationMaxEbye{}; + std::array, eBeforeAfter_N> fSixParticleCorrelationEbye{}; // <<6>> [before, after][] + std::array, eBeforeAfter_N> fSixParticleCorrelationMinEbye{}; + std::array, eBeforeAfter_N> fSixParticleCorrelationMaxEbye{}; + } ebye; + + template + bool ctEventCuts(T1 const& collision, T2 const& rlCollisionCentAll, T3 const& rlCollisionMultAll, T4 const& rlCollisionNumContrib) + { + bool pass = true; + + bool bGeneralCut = true; + bool bVertexZCut = true; + bool bSel8Cut = true; + bool bCentCut = true; + bool bNumContribCut = true; + bool bNumOccupancyCut = true; + bool bCentCorrCut = true; + bool bMultCorrCut = true; + bool bNumContribMultCorrCut = true; + + // *) For rec event and sim event + bVertexZCut = collision.posZ() < tc.fVertexZCut[1] && + collision.posZ() > tc.fVertexZCut[0]; + + // *) For rec event only + if constexpr (rs == eRec) { + // *) General cuts + bGeneralCut = collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard) && + collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard) && + collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup) && + collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC) && + collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && + collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && + collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof); + // *) Sel8Cut + bSel8Cut = collision.sel8(); + // *) CentCut + bCentCut = ebye.fCentrality < tc.fCentCut[1] && + ebye.fCentrality > tc.fCentCut[0]; + // *) NumContribCut + bNumContribCut = ebye.fNumContrib < tc.fNumContribCut[1] && + ebye.fNumContrib > tc.fNumContribCut[0]; + // *) NumOccupancyCut + bNumOccupancyCut = collision.trackOccupancyInTimeRange() < tc.fOccupancyCut[1] && + collision.trackOccupancyInTimeRange() > tc.fOccupancyCut[0]; + // *) CentCorrCut + float iCent = 0.; + float jCent = 0.; + int nCentComb = 0; + for (int i = 0; i < eCentEstm_N; i++) { + iCent = rlCollisionCentAll[i]; + for (int j = i + 1; j < eCentEstm_N; j++) { + jCent = rlCollisionCentAll[j]; + float upper = cr.fCorrBounds[eCentABBound + nCentComb][0]->Eval(iCent); + float lower = cr.fCorrBounds[eCentABBound + nCentComb][1]->Eval(iCent); + bCentCorrCut &= jCent >= lower && jCent <= upper; + nCentComb += 1; + } + } + // *) MultCorrCut + float iMult = 0.; + float jMult = 0.; + int nMultComb = 0; + for (int i = 0; i < eMultEstm_N; i++) { + iMult = rlCollisionMultAll[i]; + for (int j = i + 1; j < eMultEstm_N; j++) { + jMult = rlCollisionMultAll[j]; + float upper = cr.fCorrBounds[eMultABBound + nMultComb][0]->Eval(iMult); + float lower = cr.fCorrBounds[eMultABBound + nMultComb][1]->Eval(iMult); + bMultCorrCut &= jMult >= lower && jMult <= upper; + nMultComb += 1; + } + } + // *) NumContribMultCorrCut + float upper = cr.fCorrBounds[eNumContribMultBound][0]->Eval(ebye.fReferenceMultiplicity); + float lower = cr.fCorrBounds[eNumContribMultBound][1]->Eval(ebye.fReferenceMultiplicity); + bNumContribMultCorrCut = rlCollisionNumContrib >= lower && rlCollisionNumContrib <= upper; + } + + // *) For sim event only + if constexpr (rs == eSim) { + // *) CentCut + bCentCut = ebye.fCentralitySim < tc.fCentCut[1] && + ebye.fCentralitySim > tc.fCentCut[0]; + } + + // *) Combine all switches + if (tc.fGeneralCutSwitch) { + pass &= bGeneralCut; + } + if (tc.fVertexZCutSwitch) { + pass &= bVertexZCut; + } + if (tc.fSel8CutSwitch) { + pass &= bSel8Cut; + } + if (tc.fCentCutSwitch) { + pass &= bCentCut; + } + if (tc.fNumContribCutSwitch) { + pass &= bNumContribCut; + } + if (tc.fOccupancyCutSwitch) { + pass &= bNumOccupancyCut; + } + if (tc.fCentCorrCutSwitch) { + pass &= bCentCorrCut; + } + if (tc.fMultCorrCutSwitch) { + pass &= bMultCorrCut; + } + if (tc.fNumContribMultCorrCutSwitch) { + pass &= bNumContribMultCorrCut; + } + + return pass; + } + + template + bool ctParticleCuts(T1 const& track) + { + bool pass = true; + + bool bPtCut = true; + bool bEtaCut = true; + bool bSignCut = true; + bool bTpcNClsFoundCut = true; + bool bDCAXYCut = true; + bool bDCAZCut = true; + bool bHasTpcCut = true; + bool bHasItsCut = true; + + // *) For rec event and sim event + bPtCut = track.pt() < tc.fPtCut[1] && track.pt() > tc.fPtCut[0]; + bEtaCut = track.eta() < tc.fEtaCut[1] && track.eta() > tc.fEtaCut[0]; + + // *) For rec event only + if constexpr (rs == eRec) { + bSignCut = (track.sign() == -1 && tc.fSignCut[0]) || + (track.sign() == 0 && tc.fSignCut[1]) || + (track.sign() == 1 && tc.fSignCut[2]); + bTpcNClsFoundCut = track.tpcNClsFound() < tc.fTpcNClsFoundCut[1] && + track.tpcNClsFound() > tc.fTpcNClsFoundCut[0]; + bDCAXYCut = track.dcaXY() < tc.fDCAXYCut[1] && + track.dcaXY() > tc.fDCAXYCut[0]; + bDCAZCut = track.dcaZ() < tc.fDCAZCut[1] && + track.dcaZ() > tc.fDCAZCut[0]; + bHasTpcCut = track.hasTPC() && tc.fHasTpcItsCut[0]; + bHasItsCut = track.hasITS() && tc.fHasTpcItsCut[1]; + } + + // *) For sim event only + if constexpr (rs == eSim) { + TParticlePDG* particle = pdg->GetParticle(track.pdgCode()); + if (!particle) { + // LOGF(warning, "PDG code %d not found", track.pdgCode()); + bSignCut = false; + } else { + // LOGF(info, "PDG code %d found", track.pdgCode()); + float charge = particle->Charge(); + bSignCut = (charge < 0 && tc.fSignCut[0]) || + (charge == 0 && tc.fSignCut[1]) || + (charge > 0 && tc.fSignCut[2]); + } + } + + if (tc.fPtCutSwitch) { + pass &= bPtCut; + } + if (tc.fEtaCutSwitch) { + pass &= bEtaCut; + } + if (tc.fSignCutSwitch) { + pass &= bSignCut; + } + if (tc.fTpcNClsFoundCutSwitch) { + pass &= bTpcNClsFoundCut; + } + if (tc.fDCAXYCutSwitch) { + pass &= bDCAXYCut; + } + if (tc.fDCAZCutSwitch) { + pass &= bDCAZCut; + } + if (tc.fHasTpcItsCutSwitch) { + pass &= bHasTpcCut; + pass &= bHasItsCut; + } + + return pass; + } + + TComplex mccQ(int n, int p, EnBeforeAfter eba) + { + // Using the fact that Q{-n,p} = Q{n,p}^*. + if (eba == eBefore) { + // Q-vector before cut: + if (n >= 0) { + return mcc.fQvectorBefore[n][p]; + } + return TComplex::Conjugate(mcc.fQvectorBefore[-n][p]); + } + if (n >= 0) { + return mcc.fQvectorAfter[n][p]; + } + return TComplex::Conjugate(mcc.fQvectorAfter[-n][p]); + } + + // TComplex mccFour(int n1, int n2, EnBeforeAfter eba) + // { + // return mccQ(n1, 1, eba) * mccQ(n2, 1, eba) - mccQ(n1 + n2, 2, eba); + // } + + template + TComplex mccRecursion(int n, std::array harmonic, EnBeforeAfter eba, int mult = 1, int skip = 0) + { + // Calculate multi-particle correlators by using recursion (an improved faster version) originally developed by Kristjan Gulbrandsen (gulbrand@nbi.dk). + + int nm1 = n - 1; + TComplex c(mccQ(harmonic[nm1], mult, eba)); + if (nm1 == 0) { + return c; + } + c *= mccRecursion(nm1, harmonic, eba); + if (nm1 == skip) { + return c; + } + + int multp1 = mult + 1; + int nm2 = n - 2; + int counter1 = 0; + int hhold = harmonic[counter1]; + harmonic[counter1] = harmonic[nm2]; + harmonic[nm2] = hhold + harmonic[nm1]; + TComplex c2(mccRecursion(nm1, harmonic, eba, multp1, nm2)); + int counter2 = n - 3; + while (counter2 >= skip) { + harmonic[nm2] = harmonic[counter1]; + harmonic[counter1] = hhold; + ++counter1; + hhold = harmonic[counter1]; + harmonic[counter1] = harmonic[nm2]; + harmonic[nm2] = hhold + harmonic[nm1]; + c2 += mccRecursion(nm1, harmonic, eba, multp1, counter2); + --counter2; + } + harmonic[nm2] = harmonic[counter1]; + harmonic[counter1] = hhold; + + if (mult == 1) { + return c - c2; + } + return c - static_cast(mult) * c2; + } + + TObject* getObjectFromList(TList* list, const char* objectName) + { + // Get TObject pointer from TList, even if it's in some nested TList. + // Foreseen to be used to fetch histograms or profiles from files directly. + // Some ideas taken from TCollection::ls() + // If you have added histograms directly to files (without TList's), then you can fetch them directly with file->Get("hist-name"). + + // Usage: TH1D* hist = (TH1D*) + // getObjectFromList("some-valid-TList-pointer","some-object-name"); + + // Insanity checks: + if (!list) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (!objectName) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + if (0 == list->GetEntries()) { + return nullptr; + } + + // The object is in the current base list: + TObject* objectFinal = list->FindObject(objectName); // the final object I am after + if (objectFinal) { + return objectFinal; + } + + // Otherwise, search for the object recursively in the nested lists: + TObject* objectIter = nullptr; // iterator object in the loop below + TIter next(list); + while ((objectIter = next())) // double round braces are to silence the warnings + { + if (TString(objectIter->ClassName()).EqualTo("TList")) { + objectFinal = getObjectFromList(dynamic_cast(objectIter), objectName); + if (objectFinal) { + return objectFinal; + } + } + } // while(objectIter = next()) + + return nullptr; + + } // TObject* getObjectFromList(TList* list, char* objectName) + + std::vector getHistogramsWithWeights(const char* filePath, const char* runNumber) + { + // a) Return value: + std::vector histograms; + TList* baseList = nullptr; // base top-level list in the TFile, e.g. named "ccdb_object" + TList* listWithRuns = nullptr; // nested list with run-wise TList's holding run-specific weights + + // c) Determine from filePath if the file in on a local machine, or in home dir AliEn, or in CCDB: + // Algorithm: + // If filePath begins with "/alice/data/CCDB/" then it's in home dir AliEn. + // If filePath begins with "/alice-ccdb.cern.ch/" then it's in CCDB. + // Therefore, files in AliEn and CCDB must be specified with abs path, for local files both abs and relative paths are just fine. + bool bFileIsInAliEn = false; + bool bFileIsInCCDB = false; + + TString filePathStr(filePath); + + if (filePathStr(0, 7) == "/alice/") { + bFileIsInAliEn = true; + } else if (filePathStr(0, 20) == "/alice-ccdb.cern.ch/") { + bFileIsInCCDB = true; + } + + if (bFileIsInAliEn) { + // File you want to access is in your home dir in AliEn: + const TGrid* alien = TGrid::Connect("alien", gSystem->Getenv("USER"), "", ""); // do not forget to add #include to the preamble of your analysis task + if (!alien) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + TFile* weightsFile = TFile::Open(Form("alien://%s", filePath), "READ"); // yes, ROOT can open a file transparently, even if it's sitting in AliEn, with this specific syntax + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + weightsFile->GetObject("ccdb_object", baseList); + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + // Finally, from the top-level TList, get the desired nested TList => the technical problem here is that it can be nested at any level, + // for thare there is a helper utility function GetObjectFromList(...) , see its implementation further below + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(warning, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current runnumber = %s\033[0m", __FUNCTION__, __LINE__, runNumber); + histograms = {nullptr}; + return histograms; + } + } + } else if (bFileIsInCCDB) { + // File you want to access is in your home dir in CCDB: + // Remember that here I do not access the file; instead, I directly access the object in that file. + // My home dir in CCDB: https://alice-ccdb.cern.ch/browse/Users/a/abilandz/ => adapt for your case + ccdb->setURL("https://alice-ccdb.cern.ch"); // to be able to use "ccdb" this object in your analysis task, see 4b/ below + baseList = dynamic_cast(ccdb->get(TString(filePath).ReplaceAll("/alice-ccdb.cern.ch/", "").Data())); + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(warning, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current runnumber = %s\033[0m", __FUNCTION__, __LINE__, runNumber); + histograms = {nullptr}; + return histograms; + } + } + + // OK, we got the desired TList with efficiency corrections, after that we can use the common code for all 3 cases (local, AliEn, CCDB, that common code is below) + } else { + // this is the local case, please handle this one now: + // Check if the external ROOT file exists at specified path: + + if (gSystem->AccessPathName(filePath, kFileExists)) { + LOGF(info, "\033[1;33m if(gSystem->AccessPathName(filePath, kFileExists)), filePath = %s \033[0m", filePath); + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + TFile* weightsFile = TFile::Open(filePath, "READ"); + if (!weightsFile) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + weightsFile->GetObject("ccdb_object", baseList); + + if (!baseList) { + LOGF(fatal, "\033[1;31m%s at line %d\033[0m", __FUNCTION__, __LINE__); + } + + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumber)); + if (!listWithRuns) { + TString runNumberWithLeadingZeroes = "000"; + runNumberWithLeadingZeroes += runNumber; // another try, with "000" prepended to run number + listWithRuns = dynamic_cast(getObjectFromList(baseList, runNumberWithLeadingZeroes.Data())); + if (!listWithRuns) { + LOGF(warning, "\033[1;31m%s at line %d : this crash can happen if in the output file there is no list with weights for the current runnumber = %s\033[0m", __FUNCTION__, __LINE__, runNumber); + histograms = {nullptr}; + return histograms; + } + } + } + + TIter next(listWithRuns); + TObject* object = nullptr; + + while (true) { + + object = next(); + if (!object) { + break; + } + + auto* hist = dynamic_cast(object); + if (!hist) { + continue; + } + + hist->SetDirectory(nullptr); + auto* histClone = dynamic_cast(hist->Clone()); + if (!histClone) { + LOGF(warning, "Failed to clone histogram %s", hist->GetName()); + histograms = {nullptr}; + return histograms; + } + histClone->SetTitle(Form("%s:%s", filePath, histClone->GetName())); + histograms.push_back(histClone); + } + + return histograms; + } + + //* ) Define all member functions to be called in the main process* functions: + template + void runLoop(T1 const& collision, T2 const& tracks) + { + + // Dry run: + if (tc.fDryRun) { + return; + } + + // Book Q-vector arrays: + for (int h = 0; h < mcc.MaxHarmonic; h++) { + for (int p = 0; p < mcc.MaxPower; p++) { + mcc.fQvectorBefore[h][p] = TComplex(0., 0.); + mcc.fQvectorAfter[h][p] = TComplex(0., 0.); + + for (int n = 0; n < NumEtaGap; n++) { + mcc.fQvectorBeforeA[n][h][p] = TComplex(0., 0.); + mcc.fQvectorBeforeB[n][h][p] = TComplex(0., 0.); + mcc.fQvectorAfterA[n][h][p] = TComplex(0., 0.); + mcc.fQvectorAfterB[n][h][p] = TComplex(0., 0.); + } + } + } + + // Get run number: + ebye.fRunNumber = collision.bc().runNumber(); + std::string stringRunNumber = std::to_string(ebye.fRunNumber); + + // Book phi histogram with this run number: + if (!wt.fPhiByRunMap.contains(ebye.fRunNumber)) { + auto* hPhi = new TH1F(Form("hPhi_run%d", ebye.fRunNumber), Form("phi distribution for run %d", ebye.fRunNumber), static_cast(tc.fPhiBins[0]), tc.fPhiBins[1], tc.fPhiBins[2]); + hPhi->SetDirectory(nullptr); + wt.fPhiByRunMap.try_emplace(ebye.fRunNumber, hPhi); + wt.fWeightHistogramsList->Add(wt.fPhiByRunMap[ebye.fRunNumber]); + } + + // Book pt MC rec histogram with this run number: + if (!wt.fPtRealByRunMap.contains(ebye.fRunNumber)) { + auto* hPtReal = new TH1F(Form("hPtReal_run%d", ebye.fRunNumber), Form("pt MC rec distribution for run %d", ebye.fRunNumber), static_cast(tc.fPtBins[0]), tc.fPtBins[1], tc.fPtBins[2]); + hPtReal->SetDirectory(nullptr); + wt.fPtRealByRunMap.try_emplace(ebye.fRunNumber, hPtReal); + wt.fWeightHistogramsList->Add(wt.fPtRealByRunMap[ebye.fRunNumber]); + } + + // Book pt MC sim histogram with this run number: + if (!wt.fPtMCByRunMap.contains(ebye.fRunNumber)) { + auto* hPtMC = new TH1F(Form("hPtMC_run%d", ebye.fRunNumber), Form("pt MC sim distribution for run %d", ebye.fRunNumber), static_cast(tc.fPtBins[0]), tc.fPtBins[1], tc.fPtBins[2]); + hPtMC->SetDirectory(nullptr); + wt.fPtMCByRunMap.try_emplace(ebye.fRunNumber, hPtMC); + wt.fWeightHistogramsList->Add(wt.fPtMCByRunMap[ebye.fRunNumber]); + } + + // Get phi and pt weight histogram with this run number: + if (wt.fWeightSwitch && !wt.fPhiWeightHistogramsMap.contains(ebye.fRunNumber)) { + + TH1F* phiWeightHist = dynamic_cast(wt.fDummyPhiWeightHistogram->Clone(Form("wPhi_run%d", ebye.fRunNumber))); + TH1F* ptWeightHist = dynamic_cast(wt.fDummyPtWeightHistogram->Clone(Form("wPt_run%d", ebye.fRunNumber))); + + wt.fWeightHistograms = getHistogramsWithWeights(tc.fFileWithWeights.c_str(), stringRunNumber.c_str()); + + for (auto const& hist : wt.fWeightHistograms) { + if (!hist) { + LOGF(warning, "Fail to loop weight histograms"); + continue; + } + TString histName = hist->GetName(); + if (histName.BeginsWith("wPhi")) { + delete phiWeightHist; + phiWeightHist = dynamic_cast(hist->Clone(Form("wPhi_run%d", ebye.fRunNumber))); + break; + } + } + + for (auto const& hist : wt.fWeightHistograms) { + if (!hist) { + LOGF(warning, "Fail to loop weight histograms"); + continue; + } + TString histName = hist->GetName(); + if (histName.BeginsWith("wPt")) { + delete ptWeightHist; + ptWeightHist = dynamic_cast(hist->Clone(Form("wPt_run%d", ebye.fRunNumber))); + break; + } + } + + phiWeightHist->SetDirectory(nullptr); + wt.fPhiWeightHistogramsMap.try_emplace(ebye.fRunNumber, phiWeightHist); + wt.fWeightHistogramsList->Add(wt.fPhiWeightHistogramsMap[ebye.fRunNumber]); + + ptWeightHist->SetDirectory(nullptr); + wt.fPtWeightHistogramsMap.try_emplace(ebye.fRunNumber, ptWeightHist); + wt.fWeightHistogramsList->Add(wt.fPtWeightHistogramsMap[ebye.fRunNumber]); + } + + // Multiparticle correlation list with this run number: + if (!mc.fMultiparticleCorrelationByRunMap.contains(ebye.fRunNumber)) { + mc.fMultiparticleCorrelationByRunMap.try_emplace(ebye.fRunNumber, new TList()); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->SetName(Form("mcc_run%d", ebye.fRunNumber)); + mc.fMultiparticleCorrelationProfilesList->Add(mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]); + + mc.fMultiparticleCorrelationBackupList = nullptr; + mc.fMultiparticleCorrelationBackupList = new TList(); + mc.fMultiparticleCorrelationBackupList->SetName("backup"); + mc.fMultiparticleCorrelationBackupList->SetOwner(kTRUE); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fMultiparticleCorrelationBackupList); + + for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut + mc.fTwoParticleCorrelationProfiles[i] = nullptr; + mc.fThreeParticleCorrelationProfiles[i] = nullptr; + mc.fFourParticleCorrelationProfiles[i] = nullptr; + mc.fSixParticleCorrelationProfiles[i] = nullptr; + for (int k = 0; k < NumEtaGap; k++) { + mc.fTwoParticleCorrelationGapProfiles[k][i] = nullptr; + } + mc.fTwoParticleCorrelationNumTrackProfiles[i] = nullptr; + mc.fThreeParticleCorrelationNumTrackProfiles[i] = nullptr; + mc.fFourParticleCorrelationNumTrackProfiles[i] = nullptr; + + for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight + mc.fTwoParticleCorrelationHistograms[i][j].fill(nullptr); + mc.fThreeParticleCorrelationHistograms[i][j].fill(nullptr); + mc.fFourParticleCorrelationHistograms[i][j].fill(nullptr); + mc.fSixParticleCorrelationHistograms[i][j].fill(nullptr); + } + } + + // Define 2p profiles and histograms: + for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut + mc.fTwoParticleCorrelationProfiles[i] = new TProfile(Form("prof2%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1.); + mc.fTwoParticleCorrelationProfiles[i]->Sumw2(); + mc.fTwoParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof2NumTrack%sCut", BeforeAfterNames[i + 2]), Form("2-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumTwoPCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fTwoParticleCorrelationNumTrackProfiles[i]->Sumw2(); + for (int n = 0; n < NumEtaGap; n++) { + mc.fTwoParticleCorrelationGapProfiles[n][i] = new TProfile(Form("prof2%sCutGapped%.1f", BeforeAfterNames[i + 2], tc.fEtaGap[n]), Form("2-p correlation %s cut, %.1f gapped", BeforeAfterNames[i], tc.fEtaGap[n]), NumTwoPCorrBins, 0., 1.); + mc.fTwoParticleCorrelationGapProfiles[n][i]->Sumw2(); + } + + for (int k = 0; k < NumTwoPCorrBins; k++) { // v1, v2, v3, v4 + mc.fTwoParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); + mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fTwoParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } + for (int n = 0; n < NumEtaGap; n++) { + mc.fTwoParticleCorrelationGapProfiles[n][i]->GetXaxis()->SetBinLabel(k + 1, Form("", k + 1)); + if (static_cast(i)) { + mc.fTwoParticleCorrelationGapHistograms[n][k] = new TH1D(Form("hist2v%dAfterCutWithGap%.1f", k + 1, tc.fEtaGap[n]), Form("2-p correlation v%d^2 after cut with gap %.1f", k + 1, tc.fEtaGap[n]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); + mc.fTwoParticleCorrelationGapHistograms[n][k]->Sumw2(); + mc.fTwoParticleCorrelationGapHistograms[n][k]->SetOption("HIST"); + } + } + + for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight + mc.fTwoParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist2v%d%sCut%sWeight", k + 1, BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("2-p correlation v%d^2 %s cut %s weight", k + 1, BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fTwoParticleCorrBins[0]), tc.fTwoParticleCorrBins[1], tc.fTwoParticleCorrBins[2]); + if (static_cast(j)) { + mc.fTwoParticleCorrelationHistograms[i][j][k]->Sumw2(); + mc.fTwoParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); + } + mc.fMultiparticleCorrelationBackupList->Add(mc.fTwoParticleCorrelationHistograms[i][j][k]); + } + if (static_cast(i)) { + for (int n = 0; n < NumEtaGap; n++) { + mc.fMultiparticleCorrelationBackupList->Add(mc.fTwoParticleCorrelationGapHistograms[n][k]); + } + } + } + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationNumTrackProfiles[i]); + for (int n = 0; n < NumEtaGap; n++) { + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fTwoParticleCorrelationGapProfiles[n][i]); + } + } + + // Define 3p profiles and histograms: + for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut + mc.fThreeParticleCorrelationProfiles[i] = new TProfile(Form("prof3%sCut", BeforeAfterNames[i + 2]), Form("3-p correlation %s cut", BeforeAfterNames[i]), NumThreePCorrBins, 0., 1.); + mc.fThreeParticleCorrelationProfiles[i]->Sumw2(); + mc.fThreeParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof3NumTrack%sCut", BeforeAfterNames[i + 2]), Form("3-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumThreePCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fThreeParticleCorrelationNumTrackProfiles[i]->Sumw2(); + + for (int k = 0; k < NumThreePCorrBins; k++) { + mc.fThreeParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2])); + mc.fThreeParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2])); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fThreeParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } + + for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight + mc.fThreeParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist3v%dv%dv%d%sCut%sWeight", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2], BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("3-p correlation v%dv%dv%d %s cut %s weight", ThreePHarmonicIndex[k][0], ThreePHarmonicIndex[k][1], ThreePHarmonicIndex[k][2], BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fThreeParticleCorrBins[0]), tc.fThreeParticleCorrBins[1], tc.fThreeParticleCorrBins[2]); + if (static_cast(j)) { + mc.fThreeParticleCorrelationHistograms[i][j][k]->Sumw2(); + mc.fThreeParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); + } + mc.fMultiparticleCorrelationBackupList->Add(mc.fThreeParticleCorrelationHistograms[i][j][k]); + } + } + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fThreeParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fThreeParticleCorrelationNumTrackProfiles[i]); + } + + // Define 4p profiles and histograms: + for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut + mc.fFourParticleCorrelationProfiles[i] = new TProfile(Form("prof4%sCut", BeforeAfterNames[i + 2]), Form("4-p correlation %s cut", BeforeAfterNames[i]), NumFourPCorrBins, 0., 1.); + mc.fFourParticleCorrelationProfiles[i]->Sumw2(); + mc.fFourParticleCorrelationNumTrackProfiles[i] = new TProfile2D(Form("prof4NumTrack%sCut", BeforeAfterNames[i + 2]), Form("4-p correlation vs. nTracks %s cut", BeforeAfterNames[i]), NumFourPCorrBins, 0., 1., tc.fNumTrackTotalBins[0], tc.fNumTrackTotalBins[1], tc.fNumTrackTotalBins[2]); + // mc.fFourParticleCorrelationNumTrackProfiles[i]->Sumw2(); + + for (int k = 0; k < NumFourPCorrBins; k++) { // v2v2, v2v3, v2v4, v3v3, v3v4, v4v4 + mc.fFourParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1])); + mc.fFourParticleCorrelationNumTrackProfiles[i]->GetXaxis()->SetBinLabel(k + 1, Form("", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1])); + for (int m = 0; m < tc.fNumTrackTotalBins[0]; m++) { + int width = (tc.fNumTrackTotalBins[2] - tc.fNumTrackTotalBins[1]) / tc.fNumTrackTotalBins[0]; + int lbound = m * width; + int ubound = (m + 1) * width; + mc.fFourParticleCorrelationNumTrackProfiles[i]->GetYaxis()->SetBinLabel(m + 1, Form("%d-%d", lbound, ubound)); + } + + for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight + mc.fFourParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist4v%dv%d%sCut%sWeight", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1], BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("4-p correlation v%d^2v%d^2 %s cut %s weight", FourPHarmonicIndex[k][0], FourPHarmonicIndex[k][1], BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fFourParticleCorrBins[0]), tc.fFourParticleCorrBins[1], tc.fFourParticleCorrBins[2]); + if (static_cast(j)) { + mc.fFourParticleCorrelationHistograms[i][j][k]->Sumw2(); + mc.fFourParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); + } + mc.fMultiparticleCorrelationBackupList->Add(mc.fFourParticleCorrelationHistograms[i][j][k]); + } + } + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fFourParticleCorrelationProfiles[i]); + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fFourParticleCorrelationNumTrackProfiles[i]); + } + + // Define 6p profiles and histograms: + for (int i = 0; i < eBeforeAfter_N; i++) { // before/after cut + mc.fSixParticleCorrelationProfiles[i] = new TProfile(Form("prof6%sCut", BeforeAfterNames[i + 2]), Form("6-p correlation %s cut", BeforeAfterNames[i]), NumSixPCorrBins, 0., 1.); + mc.fSixParticleCorrelationProfiles[i]->Sumw2(); + + for (int k = 0; k < NumSixPCorrBins; k++) { // v2v3v4 + mc.fSixParticleCorrelationProfiles[i]->GetXaxis()->SetBinLabel(k + 1, ""); + + for (int j = 0; j < eBeforeAfter_N; j++) { // before/after weight + mc.fSixParticleCorrelationHistograms[i][j][k] = new TH1D(Form("hist6v2v3v4%sCut%sWeight", BeforeAfterNames[i + 2], BeforeAfterNames[j + 2]), Form("6-p correlation v2^2v3^2v4^2 %s cut %s weight", BeforeAfterNames[i], BeforeAfterNames[j]), static_cast(tc.fSixParticleCorrBins[0]), tc.fSixParticleCorrBins[1], tc.fSixParticleCorrBins[2]); + if (static_cast(j)) { + mc.fSixParticleCorrelationHistograms[i][j][k]->Sumw2(); + mc.fSixParticleCorrelationHistograms[i][j][k]->SetOption("HIST"); + } + mc.fMultiparticleCorrelationBackupList->Add(mc.fSixParticleCorrelationHistograms[i][j][k]); + } + } + mc.fMultiparticleCorrelationByRunMap[ebye.fRunNumber]->Add(mc.fSixParticleCorrelationProfiles[i]); + } + } + + // Real data centrality: + std::array rlCollisionCentAll = { + collision.centFT0C(), + collision.centFT0M(), + collision.centFV0A()}; + + float rlCollisionCent = 0.; + + for (int i = 0; i < eCentEstm_N; i++) { + if (tc.fPrintSwitch) { + LOGF(info, "%s Centrality: %f", CentEstmNames[i], + rlCollisionCentAll[i]); + } + if (tc.fCentEstm == CentEstmNames[i]) { + rlCollisionCent = rlCollisionCentAll[i]; + } + } + + // Real data multiplicity: + std::array rlCollisionMultAll = { + static_cast(collision.multFT0C()), + static_cast(collision.multFT0M()), + static_cast(collision.multFV0A())}; + float rlCollisionMult = 0.; + + for (int i = 0; i < eMultEstm_N; i++) { + if (tc.fPrintSwitch) { + LOGF(info, "%s Multiplicity: %f", MultEstmNames[i], rlCollisionMultAll[i]); + } + if (tc.fMultEstm == MultEstmNames[i]) { + rlCollisionMult = rlCollisionMultAll[i]; + } + if (rlCollisionMultAll[i] >= ebye.fMultiplicityMaxEbye[i]) { + ebye.fMultiplicityMaxEbye[i] = rlCollisionMultAll[i]; + if (tc.fPrintSwitch) { + LOGF(info, "max %s multiplicity so far = %f", MultEstmNames[i], ebye.fMultiplicityMaxEbye[i]); + } + } + } + + // Real data nContrib: + float rlCollisionNumContrib = 0.; + rlCollisionNumContrib = static_cast(collision.numContrib()); + + // Event-by-event quantity: + ebye.fCentrality = rlCollisionCent; + ebye.fReferenceMultiplicity = rlCollisionMult; + ebye.fNumContrib = rlCollisionNumContrib; + + // Print... + if (tc.fPrintSwitch) { + + LOGF(info, "Run number: %d", ebye.fRunNumber); + + LOGF(info, "Centrality: %f", rlCollisionCent); + LOGF(info, "Multiplicity: %f", static_cast(rlCollisionMult)); + + LOGF(info, "Vertex X position: %f", collision.posX()); + LOGF(info, "Vertex Y position: %f", collision.posY()); + LOGF(info, "Vertex Z position: %f", collision.posZ()); + + LOGF(info, "NContributors: %f", static_cast(rlCollisionNumContrib)); + } + + // If Rec or RecAndSim: + if constexpr (rs == eRec || rs == eRecAndSim) { + + // Fill real event histograms before cut: + ev.fEventHistograms[eCent][eRec][eBefore]->Fill(rlCollisionCent); + ev.fEventHistograms[eMult][eRec][eBefore]->Fill(rlCollisionMult); + ev.fEventHistograms[eVertexX][eRec][eBefore]->Fill(collision.posX()); + ev.fEventHistograms[eVertexY][eRec][eBefore]->Fill(collision.posY()); + ev.fEventHistograms[eVertexZ][eRec][eBefore]->Fill(collision.posZ()); + ev.fEventHistograms[eNumContrib][eRec][eBefore]->Fill(rlCollisionNumContrib); + + // Fill centrality correlation histograms before cut: + for (int i = 0; i < eCentEstm_N; i++) { + for (int j = i + 1; j < eCentEstm_N; j++) { + auto* h = cr.fCorrHistograms[eCorrCent][i][j][eBefore]; + if (!h) { + LOGF(fatal, "Missing histogram cr.fCorrHistograms[eCorrCent][%d][%d][eBefore]", i, j); + } + if (rlCollisionCentAll[i] >= 0. && rlCollisionCentAll[j] >= 0.) { + h->Fill(rlCollisionCentAll[i], rlCollisionCentAll[j]); + } + } + } + + // Fill multiplicity correlation histograms before cut: + for (int i = 0; i < eMultEstm_N; i++) { + for (int j = i + 1; j < eMultEstm_N; j++) { + auto* h = cr.fCorrHistograms[eCorrMult][i][j][eBefore]; + if (!h) { + LOGF(fatal, "Missing histogram cr.fCorrHistograms[eCorrMult][%d][%d][eBefore]", i, j); + } + if (rlCollisionMultAll[i] >= 0. && rlCollisionMultAll[j] >= 0.) { + h->Fill(rlCollisionMultAll[i], rlCollisionMultAll[j]); + } + } + } + + // Fill NContrib vs. multiplicity QA histogram before cut: + if (qa.fQASwitch) { + qa.fQAHistograms[eQAMultNumContrib][eBefore]->Fill(rlCollisionMult, rlCollisionNumContrib); + } + + // If RecAndSim: + if constexpr (rs == eRecAndSim) { + + if (!collision.has_mcCollision()) { + if (tc.fPrintSwitch) { + LOGF(warning, "No MC collision for this collision, skip..."); + } + } else { + // Define MC collision: + auto mccollision = collision.mcCollision(); + + // Define MC centrality: + float mcCollisionCent = 0.; + double mcCollisionCentDouble = 0.; + double b = mccollision.impactParameter() * 1e-15; // convert fm to m + double xs = 7.71e-28; // convert barn to m^2 + mcCollisionCentDouble = o2::constants::math::PI * b * b / xs * 100; + mcCollisionCent = static_cast(mcCollisionCentDouble); + + // Event-by-event quantity: + ebye.fCentralitySim = mcCollisionCent; + ebye.fImpactParameter = b; + + if (tc.fPrintSwitch) { + LOGF(info, "mc impact param (fm): %f", mccollision.impactParameter()); + LOGF(info, "mc centrality: %f", mcCollisionCent); + } + + // Fill MC event histograms before cut: + ev.fEventHistograms[eCent][eSim][eBefore]->Fill(mcCollisionCent); + ev.fEventHistograms[eVertexX][eSim][eBefore]->Fill(mccollision.posX()); + ev.fEventHistograms[eVertexY][eSim][eBefore]->Fill(mccollision.posY()); + ev.fEventHistograms[eVertexZ][eSim][eBefore]->Fill(mccollision.posZ()); + + // Fill MC event histograms after cut: + if (ctEventCuts(mccollision, rlCollisionCentAll, rlCollisionMultAll, rlCollisionNumContrib)) { + ev.fEventHistograms[eCent][eSim][eAfter]->Fill(mcCollisionCent); + ev.fEventHistograms[eVertexX][eSim][eAfter]->Fill(mccollision.posX()); + ev.fEventHistograms[eVertexY][eSim][eAfter]->Fill(mccollision.posY()); + ev.fEventHistograms[eVertexZ][eSim][eAfter]->Fill(mccollision.posZ()); + } + + // Fill centrality QA histogram before/after cut: + if (qa.fQASwitch) { + qa.fQAHistograms[eQACent][eBefore]->Fill(rlCollisionCent, mcCollisionCent); + if (ctEventCuts(collision, rlCollisionCentAll, rlCollisionMultAll, rlCollisionNumContrib) && + ctEventCuts(mccollision, rlCollisionCentAll, rlCollisionMultAll, rlCollisionNumContrib)) { + qa.fQAHistograms[eQACent][eAfter]->Fill(rlCollisionCent, mcCollisionCent); + } + } + } + } + + // Fill real event histograms after cut + if (ctEventCuts(collision, rlCollisionCentAll, rlCollisionMultAll, rlCollisionNumContrib)) { + ev.fEventHistograms[eCent][eRec][eAfter]->Fill(rlCollisionCent); + ev.fEventHistograms[eMult][eRec][eAfter]->Fill(rlCollisionMult); + ev.fEventHistograms[eVertexX][eRec][eAfter]->Fill(collision.posX()); + ev.fEventHistograms[eVertexY][eRec][eAfter]->Fill(collision.posY()); + ev.fEventHistograms[eVertexZ][eRec][eAfter]->Fill(collision.posZ()); + ev.fEventHistograms[eNumContrib][eRec][eAfter]->Fill(rlCollisionNumContrib); + + // Fill centrality correlation histograms after cut: + for (int i = 0; i < eCentEstm_N; i++) { + for (int j = i + 1; j < eCentEstm_N; j++) { + auto* h = cr.fCorrHistograms[eCorrCent][i][j][eAfter]; + if (!h) { + LOGF(fatal, "Missing histogram cr.fCorrHistograms[eCorrCent][%d][%d][eAfter]", i, j); + } + h->Fill(rlCollisionCentAll[i], rlCollisionCentAll[j]); + } + } + + // Fill multiplicity correlation histograms after cut: + for (int i = 0; i < eMultEstm_N; i++) { + for (int j = i + 1; j < eMultEstm_N; j++) { + auto* h = cr.fCorrHistograms[eCorrMult][i][j][eAfter]; + if (!h) { + LOGF(fatal, "Missing histogram cr.fCorrHistograms[eCorrMult][%d][%d][eAfter]", i, j); + } + h->Fill(rlCollisionMultAll[i], rlCollisionMultAll[j]); + } + } + + // Fill NContrib vs. multiplicity QA histogram after cut: + if (qa.fQASwitch) { + qa.fQAHistograms[eQAMultNumContrib][eAfter]->Fill(rlCollisionMult, rlCollisionNumContrib); + } + + } else { + // Fail the event cut, skip this collision: + return; + } + } + + int nTracksBefore = tracks.size(); + int nTracksAfter = 0; + std::vector nTracksBeforeA = {0, 0, 0}; + std::vector nTracksBeforeB = {0, 0, 0}; + std::vector nTracksAfterA = {0, 0, 0}; + std::vector nTracksAfterB = {0, 0, 0}; + + // Calculate Q-vectors for available angles and weights: + double dEta = 0.; + double dPhi = 0.; // particle angle + double dPt = 0.; + double wPhi = 1.; // particle weight + double wPt = 1.; + double wPhiToPowerP = 1.; // particle weight raised to power p. wPhi is actually wPt*wPhi but I'm too lazy to change the name. + + // Main loop over particles: + for (auto const& track : tracks) { + // LOGF(info, "Track azimuthal angle: %f", track.phi()); + // LOGF(info, "Transverse momentum: %f", track.pt()); + + if constexpr (rs == eRec || rs == eRecAndSim) { + + // Fill track histograms before cut: + pc.fParticleHistograms[ePt][eRec][eBefore]->Fill(track.pt()); + pc.fParticleHistograms[eEta][eRec][eBefore]->Fill(track.eta()); + pc.fParticleHistograms[ePhi][eRec][eBefore]->Fill(track.phi()); + pc.fParticleHistograms[eDCAXY][eRec][eBefore]->Fill(track.dcaXY()); + pc.fParticleHistograms[eDCAZ][eRec][eBefore]->Fill(track.dcaZ()); + + // Calculating Q-vector before cut: + dEta = track.eta(); + dPhi = track.phi(); + dPt = track.pt(); + if (wt.fWeightSwitch) { + if (!wt.fPhiWeightHistogramsMap.contains(ebye.fRunNumber) || !wt.fPhiWeightHistogramsMap.at(ebye.fRunNumber)) { + LOGF(fatal, "Missing Phi weight histogram for run %d", ebye.fRunNumber); + } + if (!wt.fPtWeightHistogramsMap.contains(ebye.fRunNumber) || !wt.fPtWeightHistogramsMap.at(ebye.fRunNumber)) { + LOGF(fatal, "Missing Pt weight histogram for run %d", ebye.fRunNumber); + } + + auto* histPhi = wt.fPhiWeightHistogramsMap.at(ebye.fRunNumber); + auto* histPt = wt.fPtWeightHistogramsMap.at(ebye.fRunNumber); + wPhi = histPhi->GetBinContent(histPhi->GetXaxis()->FindBin(dPhi)); + wPt = histPt->GetBinContent(histPt->GetXaxis()->FindBin(dPt)); + + wPhi *= wPt; + } + + for (int h = 0; h < mcc.MaxHarmonic; h++) { + for (int p = 0; p < mcc.MaxPower; p++) { + if (wt.fWeightSwitch) { + wPhiToPowerP = std::pow(wPhi, p); + } + mcc.fQvectorBefore[h][p] += TComplex(wPhiToPowerP * std::cos(h * dPhi), wPhiToPowerP * std::sin(h * dPhi)); + for (int n = 0; n < NumEtaGap; n++) { + if (dEta < -tc.fEtaGap[n] / 2.) { + mcc.fQvectorBeforeA[n][h][p] += TComplex(wPhiToPowerP * std::cos(h * dPhi), wPhiToPowerP * std::sin(h * dPhi)); + } else if (dEta > tc.fEtaGap[n] / 2.) { + mcc.fQvectorBeforeB[n][h][p] += TComplex(wPhiToPowerP * std::cos(h * dPhi), wPhiToPowerP * std::sin(h * dPhi)); + } + } + } + } + + for (int n = 0; n < NumEtaGap; n++) { + if (dEta < -tc.fEtaGap[n] / 2.) { + nTracksBeforeA[n] += 1; + } else if (dEta > tc.fEtaGap[n] / 2.) { + nTracksBeforeB[n] += 1; + } + } + + if (ctParticleCuts(track)) { + + // Fill phi (real rec), pt (MC rec) histogram with this run number: + wt.fPhiByRunMap.at(ebye.fRunNumber)->Fill(track.phi()); + wt.fPtRealByRunMap.at(ebye.fRunNumber)->Fill(track.pt()); + + // Fill particle histograms after cut: + pc.fParticleHistograms[ePt][eRec][eAfter]->Fill(track.pt()); + pc.fParticleHistograms[eEta][eRec][eAfter]->Fill(track.eta()); + pc.fParticleHistograms[ePhi][eRec][eAfter]->Fill(track.phi()); + pc.fParticleHistograms[eDCAXY][eRec][eAfter]->Fill(track.dcaXY()); + pc.fParticleHistograms[eDCAZ][eRec][eAfter]->Fill(track.dcaZ()); + + // Calculating Q-vector after cut: + for (int h = 0; h < mcc.MaxHarmonic; h++) { + for (int p = 0; p < mcc.MaxPower; p++) { + if (wt.fWeightSwitch) { + wPhiToPowerP = std::pow(wPhi, p); + } + mcc.fQvectorAfter[h][p] += TComplex(wPhiToPowerP * std::cos(h * dPhi), wPhiToPowerP * std::sin(h * dPhi)); + + for (int n = 0; n < NumEtaGap; n++) { + if (dEta < -tc.fEtaGap[n] / 2.) { + mcc.fQvectorAfterA[n][h][p] += TComplex(wPhiToPowerP * std::cos(h * dPhi), wPhiToPowerP * std::sin(h * dPhi)); + } else if (dEta > tc.fEtaGap[n] / 2.) { + mcc.fQvectorAfterB[n][h][p] += TComplex(wPhiToPowerP * std::cos(h * dPhi), wPhiToPowerP * std::sin(h * dPhi)); + } + } + } + } + + nTracksAfter += 1; + + for (int n = 0; n < NumEtaGap; n++) { + if (dEta < -tc.fEtaGap[n] / 2.) { + nTracksAfterA[n] += 1; + } else if (dEta > tc.fEtaGap[n] / 2.) { + nTracksAfterB[n] += 1; + } + } + } + // ... + + // ... and corresponding MC truth simulated: + // See + // https://github.com/AliceO2Group/O2Physics/blob/master/Tutorials/src/mcHistograms.cxx + // See + // https://aliceo2group.github.io/analysis-framework/docs/datamodel/ao2dTables.html#montecarlo + if constexpr (rs == eRecAndSim) { + if (!track.has_mcParticle()) { + if (tc.fPrintSwitch) { + LOGF(warning, " No MC particle for this track, skip..."); + } + } else { + // Corresponding MC truth simulated particle + auto mcparticle = track.mcParticle(); + + // Fill MC particle histograms before cut: + pc.fParticleHistograms[ePt][eSim][eBefore]->Fill(mcparticle.pt()); + pc.fParticleHistograms[eEta][eSim][eBefore]->Fill(mcparticle.eta()); + pc.fParticleHistograms[ePhi][eSim][eBefore]->Fill(mcparticle.phi()); + + if (ctParticleCuts(mcparticle)) { + // Fill pt (MC sim) histogram with this run number: + wt.fPtMCByRunMap.at(ebye.fRunNumber)->Fill(mcparticle.pt()); + // Fill MC particle histograms after cut: + pc.fParticleHistograms[ePt][eSim][eAfter]->Fill(mcparticle.pt()); + pc.fParticleHistograms[eEta][eSim][eAfter]->Fill(mcparticle.eta()); + pc.fParticleHistograms[ePhi][eSim][eAfter]->Fill(mcparticle.phi()); + } + } + } // end of if constexpr (rs == eRecAndSim) { + } // if constexpr (rs == eRec || rs == eRecAndSim) { + } // end of for (int64_t i = 0; i < tracks.size(); i++) { + + // Fill real event histograms (NumTrack) before cut: + ev.fEventHistograms[eNumTrack][eRec][eBefore]->Fill(nTracksBefore); + ev.fEventHistograms[eNumTrack][eRec][eAfter]->Fill(nTracksAfter); + + bool bNumTrackCut = true; + if (tc.fNumTrackCutSwitch) { + bNumTrackCut = nTracksAfter > tc.fNumTrackCut[0] && nTracksAfter < tc.fNumTrackCut[1]; + } + + // 2-p correlations: + for (int i = 0; i < NumTwoPCorrBins; i++) { + int k = 2; + mcc.h1 = -(i + 1); + mcc.h2 = i + 1; + std::array harmonicsTwoNum = {mcc.h1, mcc.h2}; + std::array harmonicsTwoDen = {0, 0}; + + // Before cut: + TComplex twoRecursionBefore = mccRecursion(2, harmonicsTwoNum, eBefore) / mccRecursion(2, harmonicsTwoDen, eBefore).Re(); + double wTwoRecursionBefore = mccRecursion(2, harmonicsTwoDen, eBefore).Re(); + ebye.fTwoParticleCorrelationEbye[eBefore][i] = twoRecursionBefore.Re(); + + if (nTracksBefore > k - 1 && wTwoRecursionBefore > 0.) { + // Fill vn^2 profile and histogram: + mc.fTwoParticleCorrelationProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); + mc.fTwoParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i]); + mc.fTwoParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eBefore][i], wTwoRecursionBefore); + mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fTwoParticleCorrelationEbye[eBefore][i]); + + // Find max and min: + if (ebye.fTwoParticleCorrelationEbye[eBefore][i] > ebye.fTwoParticleCorrelationMaxEbye[eBefore][i]) { + ebye.fTwoParticleCorrelationMaxEbye[eBefore][i] = ebye.fTwoParticleCorrelationEbye[eBefore][i]; + LOGF(info, "max v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eBefore][i]); + } + if (ebye.fTwoParticleCorrelationEbye[eBefore][i] < ebye.fTwoParticleCorrelationMinEbye[eBefore][i]) { + ebye.fTwoParticleCorrelationMinEbye[eBefore][i] = ebye.fTwoParticleCorrelationEbye[eBefore][i]; + LOGF(info, "min v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eBefore][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wTwoRecursionBefore = %e", rlCollisionCent, nTracksBefore, wTwoRecursionBefore); + } + } + + // After cut: + TComplex twoRecursionAfter = mccRecursion(2, harmonicsTwoNum, eAfter) / mccRecursion(2, harmonicsTwoDen, eAfter).Re(); + double wTwoRecursionAfter = mccRecursion(2, harmonicsTwoDen, eAfter).Re(); + ebye.fTwoParticleCorrelationEbye[eAfter][i] = twoRecursionAfter.Re(); + + if (nTracksAfter > k - 1 && wTwoRecursionAfter > 0. && bNumTrackCut) { + // Fill vn^2 profile and histogram: + mc.fTwoParticleCorrelationProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); + mc.fTwoParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i]); + mc.fTwoParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fTwoParticleCorrelationEbye[eAfter][i], wTwoRecursionAfter); + mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fTwoParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fTwoParticleCorrelationEbye[eAfter][i]); + + // Find max and min: + if (ebye.fTwoParticleCorrelationEbye[eAfter][i] > ebye.fTwoParticleCorrelationMaxEbye[eAfter][i]) { + ebye.fTwoParticleCorrelationMaxEbye[eAfter][i] = ebye.fTwoParticleCorrelationEbye[eAfter][i]; + LOGF(info, "max v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eAfter][i]); + } + if (ebye.fTwoParticleCorrelationEbye[eAfter][i] < ebye.fTwoParticleCorrelationMinEbye[eAfter][i]) { + ebye.fTwoParticleCorrelationMinEbye[eAfter][i] = ebye.fTwoParticleCorrelationEbye[eAfter][i]; + LOGF(info, "min v%d^2 so far = %e", i + 1, ebye.fTwoParticleCorrelationEbye[eAfter][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wTwoRecursionAfter = %e", rlCollisionCent, nTracksAfter, wTwoRecursionAfter); + } + } + + for (int n = 0; n < NumEtaGap; n++) { + + // Before cut, with gap: + TComplex qvba = mcc.fQvectorBeforeA[n][mcc.h2][1]; + TComplex qvbb = mcc.fQvectorBeforeB[n][mcc.h2][1]; + double wba = mcc.fQvectorBeforeA[n][0][1].Re(); + double wbb = mcc.fQvectorBeforeB[n][0][1].Re(); + double gapb = 0.; + if (wba * wbb > 0) { // nTracksBeforeA[n] * nTracksBeforeB[n] > 0 + gapb = (qvba * TComplex::Conjugate(qvbb)).Re() / (wba * wbb); + mc.fTwoParticleCorrelationGapProfiles[n][eBefore]->Fill(mc.fTwoParticleCorrelationGapProfiles[n][eBefore]->GetXaxis()->GetBinCenter(i + 1), gapb, wba * wbb); + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "etagap=%f, cent=%f, nTracksBeforeA = %d, nTracksBeforeB = %d", tc.fEtaGap[n], rlCollisionCent, nTracksBeforeA[n], nTracksBeforeB[n]); + } + } + + // After cut, with gap: + TComplex qvaa = mcc.fQvectorAfterA[n][mcc.h2][1]; + TComplex qvab = mcc.fQvectorAfterB[n][mcc.h2][1]; + double waa = mcc.fQvectorAfterA[n][0][1].Re(); + double wab = mcc.fQvectorAfterB[n][0][1].Re(); + double gapa = 0.; + if (waa * wab > 0) { + gapa = (qvaa * TComplex::Conjugate(qvab)).Re() / (waa * wab); + mc.fTwoParticleCorrelationGapProfiles[n][eAfter]->Fill(mc.fTwoParticleCorrelationGapProfiles[n][eAfter]->GetXaxis()->GetBinCenter(i + 1), gapa, waa * wab); + mc.fTwoParticleCorrelationGapHistograms[n][i]->Fill(gapa, waa * wab); + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "etagap=%f, cent=%f, nTracksAfterA = %d, nTracksAfterB = %d", tc.fEtaGap[n], rlCollisionCent, nTracksAfterA[n], nTracksAfterB[n]); + } + } + } + } + + // 3-p correlations: + for (int i = 0; i < NumThreePCorrBins; i++) { + int k = 3; + mcc.h1 = ThreePHarmonicIndex[i][0]; + mcc.h2 = -ThreePHarmonicIndex[i][1]; + mcc.h3 = -ThreePHarmonicIndex[i][2]; + std::array harmonicsThreeNum = {mcc.h1, mcc.h2, mcc.h3}; + std::array harmonicsThreeDen = {0, 0, 0}; + + // Before cut: + TComplex threeRecursionBefore = mccRecursion(3, harmonicsThreeNum, eBefore) / mccRecursion(3, harmonicsThreeDen, eBefore).Re(); + double wThreeRecursionBefore = mccRecursion(3, harmonicsThreeDen, eBefore).Re(); + ebye.fThreeParticleCorrelationEbye[eBefore][i] = threeRecursionBefore.Re(); + + if (nTracksBefore > k - 1 && wThreeRecursionBefore > 0.) { + // Fill vn1vn2vn3 profile and histogram: + mc.fThreeParticleCorrelationProfiles[eBefore]->Fill(mc.fThreeParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fThreeParticleCorrelationEbye[eBefore][i], wThreeRecursionBefore); + mc.fThreeParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fThreeParticleCorrelationEbye[eBefore][i]); + mc.fThreeParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fThreeParticleCorrelationEbye[eBefore][i], wThreeRecursionBefore); + mc.fThreeParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fThreeParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + + // Find max and min: + if (ebye.fThreeParticleCorrelationEbye[eBefore][i] > ebye.fThreeParticleCorrelationMaxEbye[eBefore][i]) { + ebye.fThreeParticleCorrelationMaxEbye[eBefore][i] = ebye.fThreeParticleCorrelationEbye[eBefore][i]; + LOGF(info, "max v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + } + if (ebye.fThreeParticleCorrelationEbye[eBefore][i] < ebye.fThreeParticleCorrelationMinEbye[eBefore][i]) { + ebye.fThreeParticleCorrelationMinEbye[eBefore][i] = ebye.fThreeParticleCorrelationEbye[eBefore][i]; + LOGF(info, "min v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eBefore][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wThreeRecursionBefore = %e", rlCollisionCent, nTracksBefore, wThreeRecursionBefore); + } + } + + // After cut: + TComplex threeRecursionAfter = mccRecursion(3, harmonicsThreeNum, eAfter) / mccRecursion(3, harmonicsThreeDen, eAfter).Re(); + double wThreeRecursionAfter = mccRecursion(3, harmonicsThreeDen, eAfter).Re(); + ebye.fThreeParticleCorrelationEbye[eAfter][i] = threeRecursionAfter.Re(); + + if (nTracksAfter > k - 1 && wThreeRecursionAfter > 0. && bNumTrackCut) { + // Fill vn1vn2vn3 profile and histogram: + mc.fThreeParticleCorrelationProfiles[eAfter]->Fill(mc.fThreeParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fThreeParticleCorrelationEbye[eAfter][i], wThreeRecursionAfter); + mc.fThreeParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fThreeParticleCorrelationEbye[eAfter][i]); + mc.fThreeParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fThreeParticleCorrelationEbye[eAfter][i], wThreeRecursionAfter); + mc.fThreeParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fThreeParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + + // Find max and min: + if (ebye.fThreeParticleCorrelationEbye[eAfter][i] > ebye.fThreeParticleCorrelationMaxEbye[eAfter][i]) { + ebye.fThreeParticleCorrelationMaxEbye[eAfter][i] = ebye.fThreeParticleCorrelationEbye[eAfter][i]; + LOGF(info, "max v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + } + if (ebye.fThreeParticleCorrelationEbye[eAfter][i] < ebye.fThreeParticleCorrelationMinEbye[eAfter][i]) { + ebye.fThreeParticleCorrelationMinEbye[eAfter][i] = ebye.fThreeParticleCorrelationEbye[eAfter][i]; + LOGF(info, "min v%dv%dv%d so far = %e", mcc.h1, mcc.h2, mcc.h3, ebye.fThreeParticleCorrelationEbye[eAfter][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wThreeRecursionAfter = %e", rlCollisionCent, nTracksAfter, wThreeRecursionAfter); + } + } + } + + // 4-p correlations: + for (int i = 0; i < NumFourPCorrBins; i++) { + int k = 4; + mcc.h1 = -FourPHarmonicIndex[i][0]; + mcc.h2 = -FourPHarmonicIndex[i][1]; + mcc.h3 = FourPHarmonicIndex[i][0]; + mcc.h4 = FourPHarmonicIndex[i][1]; + std::array harmonicsFourNum = {mcc.h1, mcc.h2, mcc.h3, mcc.h4}; + std::array harmonicsFourDen = {0, 0, 0, 0}; + + // Before cut: + TComplex fourRecursionBefore = mccRecursion(4, harmonicsFourNum, eBefore) / mccRecursion(4, harmonicsFourDen, eBefore).Re(); + double wFourRecursionBefore = mccRecursion(4, harmonicsFourDen, eBefore).Re(); + ebye.fFourParticleCorrelationEbye[eBefore][i] = fourRecursionBefore.Re(); + + if (nTracksBefore > k - 1 && wFourRecursionBefore > 0.) { + // Fill vn^2vm^2 profile and histogram: + mc.fFourParticleCorrelationProfiles[eBefore]->Fill(mc.fFourParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eBefore][i], wFourRecursionBefore); + mc.fFourParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fFourParticleCorrelationEbye[eBefore][i]); + mc.fFourParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fFourParticleCorrelationEbye[eBefore][i], wFourRecursionBefore); + mc.fFourParticleCorrelationNumTrackProfiles[eBefore]->Fill(mc.fFourParticleCorrelationNumTrackProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), nTracksBefore, ebye.fFourParticleCorrelationEbye[eBefore][i]); + + // Find max and min: + if (ebye.fFourParticleCorrelationEbye[eBefore][i] > ebye.fFourParticleCorrelationMaxEbye[eBefore][i]) { + ebye.fFourParticleCorrelationMaxEbye[eBefore][i] = ebye.fFourParticleCorrelationEbye[eBefore][i]; + LOGF(info, "max v%d^2v%d^2 so far = %e", mcc.h3, mcc.h4, ebye.fFourParticleCorrelationEbye[eBefore][i]); + } + if (ebye.fFourParticleCorrelationEbye[eBefore][i] < ebye.fFourParticleCorrelationMinEbye[eBefore][i]) { + ebye.fFourParticleCorrelationMinEbye[eBefore][i] = ebye.fFourParticleCorrelationEbye[eBefore][i]; + LOGF(info, "min v%d^2v%d^2 so far = %e", mcc.h3, mcc.h4, ebye.fFourParticleCorrelationEbye[eBefore][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wFourRecursionBefore = %e", rlCollisionCent, nTracksBefore, wFourRecursionBefore); + } + } + + // After cut: + TComplex fourRecursionAfter = mccRecursion(4, harmonicsFourNum, eAfter) / mccRecursion(4, harmonicsFourDen, eAfter).Re(); + double wFourRecursionAfter = mccRecursion(4, harmonicsFourDen, eAfter).Re(); + ebye.fFourParticleCorrelationEbye[eAfter][i] = fourRecursionAfter.Re(); + + if (nTracksAfter > k - 1 && wFourRecursionAfter > 0. && bNumTrackCut) { + // Fill vn^2vm^2 profile and histogram: + mc.fFourParticleCorrelationProfiles[eAfter]->Fill(mc.fFourParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); + mc.fFourParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fFourParticleCorrelationEbye[eAfter][i]); + mc.fFourParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fFourParticleCorrelationEbye[eAfter][i], wFourRecursionAfter); + mc.fFourParticleCorrelationNumTrackProfiles[eAfter]->Fill(mc.fFourParticleCorrelationNumTrackProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), nTracksAfter, ebye.fFourParticleCorrelationEbye[eAfter][i]); + + // Find max and min: + if (ebye.fFourParticleCorrelationEbye[eAfter][i] > ebye.fFourParticleCorrelationMaxEbye[eAfter][i]) { + ebye.fFourParticleCorrelationMaxEbye[eAfter][i] = ebye.fFourParticleCorrelationEbye[eAfter][i]; + LOGF(info, "max v%d^2v%d^2 so far = %e", mcc.h3, mcc.h4, ebye.fFourParticleCorrelationEbye[eAfter][i]); + } + if (ebye.fFourParticleCorrelationEbye[eAfter][i] < ebye.fFourParticleCorrelationMinEbye[eAfter][i]) { + ebye.fFourParticleCorrelationMinEbye[eAfter][i] = ebye.fFourParticleCorrelationEbye[eAfter][i]; + LOGF(info, "min v%d^2v%d^2 so far = %e", mcc.h3, mcc.h4, ebye.fFourParticleCorrelationEbye[eAfter][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wFourRecursionAfter = %e", rlCollisionCent, nTracksAfter, wFourRecursionAfter); + } + } + } + + // 6-p correlations: + for (int i = 0; i < NumSixPCorrBins; i++) { + int k = 6; + mcc.h1 = -2; + mcc.h2 = -3; + mcc.h3 = -4; + mcc.h4 = 2; + mcc.h5 = 3; + mcc.h6 = 4; + std::array harmonicsSixNum = {mcc.h1, mcc.h2, mcc.h3, mcc.h4, mcc.h5, mcc.h6}; + std::array harmonicsSixDen = {0, 0, 0, 0, 0, 0}; + + // Before cut: + TComplex sixRecursionBefore = mccRecursion(6, harmonicsSixNum, eBefore) / mccRecursion(6, harmonicsSixDen, eBefore).Re(); + double wSixRecursionBefore = mccRecursion(6, harmonicsSixDen, eBefore).Re(); + ebye.fSixParticleCorrelationEbye[eBefore][i] = sixRecursionBefore.Re(); + + if (nTracksBefore > k - 1 && wSixRecursionBefore > 0.) { + // Fill v2^2v3^2v4^2 profile and histogram: + mc.fSixParticleCorrelationProfiles[eBefore]->Fill(mc.fSixParticleCorrelationProfiles[eBefore]->GetXaxis()->GetBinCenter(i + 1), ebye.fSixParticleCorrelationEbye[eBefore][i], wSixRecursionBefore); + mc.fSixParticleCorrelationHistograms[eBefore][eBefore][i]->Fill(ebye.fSixParticleCorrelationEbye[eBefore][i]); + mc.fSixParticleCorrelationHistograms[eBefore][eAfter][i]->Fill(ebye.fSixParticleCorrelationEbye[eBefore][i], wSixRecursionBefore); + + // Find max and min: + if (ebye.fSixParticleCorrelationEbye[eBefore][i] > ebye.fSixParticleCorrelationMaxEbye[eBefore][i]) { + ebye.fSixParticleCorrelationMaxEbye[eBefore][i] = ebye.fSixParticleCorrelationEbye[eBefore][i]; + LOGF(info, "max v%d^2v%d^2v%d^2 so far = %e", mcc.h4, mcc.h5, mcc.h6, ebye.fSixParticleCorrelationEbye[eBefore][i]); + } + if (ebye.fSixParticleCorrelationEbye[eBefore][i] < ebye.fSixParticleCorrelationMinEbye[eBefore][i]) { + ebye.fSixParticleCorrelationMinEbye[eBefore][i] = ebye.fSixParticleCorrelationEbye[eBefore][i]; + LOGF(info, "min v%d^2v%d^2v%d^2 so far = %e", mcc.h4, mcc.h5, mcc.h6, ebye.fSixParticleCorrelationEbye[eBefore][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksBefore = %d, wSixRecursionBefore = %e", rlCollisionCent, nTracksBefore, wSixRecursionBefore); + } + } + + // After cut: + TComplex sixRecursionAfter = mccRecursion(6, harmonicsSixNum, eAfter) / mccRecursion(6, harmonicsSixDen, eAfter).Re(); + double wSixRecursionAfter = mccRecursion(6, harmonicsSixDen, eAfter).Re(); + ebye.fSixParticleCorrelationEbye[eAfter][i] = sixRecursionAfter.Re(); + + if (nTracksAfter > k - 1 && wSixRecursionAfter > 0. && bNumTrackCut) { + // Fill v2^2v3^2v4^2 profile and histogram: + mc.fSixParticleCorrelationProfiles[eAfter]->Fill(mc.fSixParticleCorrelationProfiles[eAfter]->GetXaxis()->GetBinCenter(i + 1), ebye.fSixParticleCorrelationEbye[eAfter][i], wSixRecursionAfter); + mc.fSixParticleCorrelationHistograms[eAfter][eBefore][i]->Fill(ebye.fSixParticleCorrelationEbye[eAfter][i]); + mc.fSixParticleCorrelationHistograms[eAfter][eAfter][i]->Fill(ebye.fSixParticleCorrelationEbye[eAfter][i], wSixRecursionAfter); + + // Find max and min: + if (ebye.fSixParticleCorrelationEbye[eAfter][i] > ebye.fSixParticleCorrelationMaxEbye[eAfter][i]) { + ebye.fSixParticleCorrelationMaxEbye[eAfter][i] = ebye.fSixParticleCorrelationEbye[eAfter][i]; + LOGF(info, "max v%d^2v%d^2v%d^2 so far = %e", mcc.h4, mcc.h5, mcc.h6, ebye.fSixParticleCorrelationEbye[eAfter][i]); + } + if (ebye.fSixParticleCorrelationEbye[eAfter][i] < ebye.fSixParticleCorrelationMinEbye[eAfter][i]) { + ebye.fSixParticleCorrelationMinEbye[eAfter][i] = ebye.fSixParticleCorrelationEbye[eAfter][i]; + LOGF(info, "min v%d^2v%d^2v%d^2 so far = %e", mcc.h4, mcc.h5, mcc.h6, ebye.fSixParticleCorrelationEbye[eAfter][i]); + } + } else { + if (tc.fPrintSwitch) { + LOGF(warning, "cent=%f, nTracksAfter = %d, wSixRecursionAfter = %e", rlCollisionCent, nTracksAfter, wSixRecursionAfter); + } + } + } + } + + void bookParticleHistograms(int histType, const std::vector>& lPcBins) + { + const auto& lPtBins = lPcBins[histType]; // define local array and initialize it from an array set in the configurables + int nBinsPt = static_cast(lPtBins[0]); + float minPt = lPtBins[1]; + float maxPt = lPtBins[2]; + + for (int ba = 0; ba < eBeforeAfter_N; ba++) { + + std::string nameRec = Form("fHist%s[eRec][%s cut]", ParticleHistNames[histType], BeforeAfterNames[ba]); + std::string nameSim = Form("fHist%s[eSim][%s cut]", ParticleHistNames[histType], BeforeAfterNames[ba]); + std::string nameRecfull = Form("%s distribution for reconstructed particles", ParticleHistNames[histType]); + std::string nameSimfull = Form("%s distribution for simulated particles", ParticleHistNames[histType]); + + if (doprocessRec || doprocessRecSim) { + pc.fParticleHistograms[histType][eRec][ba] = new TH1F(nameRec.c_str(), nameRecfull.c_str(), nBinsPt, minPt, maxPt); + pc.fParticleHistograms[histType][eRec][ba]->GetXaxis()->SetTitle(ParticleHistNames[histType]); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[histType][eRec][ba]); + } + + if (doprocessSim || doprocessRecSim) { + pc.fParticleHistograms[histType][eSim][ba] = new TH1F(nameSim.c_str(), nameSimfull.c_str(), nBinsPt, minPt, maxPt); + pc.fParticleHistograms[histType][eSim][ba]->GetXaxis()->SetTitle(ParticleHistNames[histType]); + pc.fParticleHistogramsList->Add(pc.fParticleHistograms[histType][eSim][ba]); + } + } + } + + void bookEventHistograms(int histType, const std::vector>& lEvBins) + { + const auto& lCentBins = lEvBins[histType]; // define local array and initialize it from an array set in the configurables + int nBinsCent = static_cast(lCentBins[0]); + float minCent = lCentBins[1]; + float maxCent = lCentBins[2]; + + for (int ba = 0; ba < eBeforeAfter_N; ba++) { + + std::string nameRec = Form("fHist%s[eRec][%s cut]", EventHistNames[histType], BeforeAfterNames[ba]); + std::string nameSim = Form("fHist%s[eSim][%s cut]", EventHistNames[histType], BeforeAfterNames[ba]); + std::string nameRecfull; + std::string nameSimfull; + + if (histType == eCent) { + nameRecfull = Form("%s %s distribution for reconstructed events", tc.fCentEstm.c_str(), EventHistNames[histType]); + nameSimfull = Form("%s %s distribution for simulated events", tc.fCentEstm.c_str(), EventHistNames[histType]); + } else if (histType == eMult) { + nameRecfull = Form("%s %s distribution for reconstructed events", tc.fMultEstm.c_str(), EventHistNames[histType]); + nameSimfull = Form("%s %s distribution for simulated events", tc.fMultEstm.c_str(), EventHistNames[histType]); + } else { + nameRecfull = Form("%s distribution for reconstructed events", EventHistNames[histType]); + nameSimfull = Form("%s distribution for simulated events", EventHistNames[histType]); + } + + if (doprocessRec || doprocessRecSim) { + ev.fEventHistograms[histType][eRec][ba] = new TH1F(nameRec.c_str(), nameRecfull.c_str(), nBinsCent, minCent, maxCent); + ev.fEventHistograms[histType][eRec][ba]->GetXaxis()->SetTitle(EventHistNames[histType]); + ev.fEventHistogramsList->Add(ev.fEventHistograms[histType][eRec][ba]); + } + + if (doprocessSim || doprocessRecSim) { + if (histType != eNumContrib && histType != eMult) { + ev.fEventHistograms[histType][eSim][ba] = new TH1F(nameSim.c_str(), nameSimfull.c_str(), nBinsCent, minCent, maxCent); + ev.fEventHistograms[histType][eSim][ba]->GetXaxis()->SetTitle(EventHistNames[histType]); + ev.fEventHistogramsList->Add(ev.fEventHistograms[histType][eSim][ba]); + } // No nContrib, multiplicity and nTrack for processSim + } + } + } + + void bookQAHistograms(int histType, const std::vector>& lQABins) + { + int nBinsCentX = 0; + float minCentX = 0.; + float maxCentX = 0.; + int nBinsCentY = 0; + float minCentY = 0.; + float maxCentY = 0.; + int nBinsCent = 0; + float minCent = 0.; + float maxCent = 0.; + + if (histType == eMult) { + const auto& lCentBinsX = lQABins[1]; // MultBins + nBinsCentX = static_cast(lCentBinsX[0]); + minCentX = lCentBinsX[1]; + maxCentX = lCentBinsX[2]; + const auto& lCentBinsY = lQABins[2]; // nContribBins + nBinsCentY = static_cast(lCentBinsY[0]); + minCentY = lCentBinsY[1]; + maxCentY = lCentBinsY[2]; + } else { + const auto& lCentBins = lQABins[histType]; + nBinsCent = static_cast(lCentBins[0]); + minCent = lCentBins[1]; + maxCent = lCentBins[2]; + } + + for (int ba = 0; ba < eBeforeAfter_N; ba++) { + std::string name = Form("fHist%s[%s cut]", EventHistNames[histType], BeforeAfterNames[ba]); + std::string namefull; + + if (histType == eCent) { + namefull = Form("Quality assurance of %s %s", tc.fCentEstm.c_str(), EventHistNames[histType]); + } else if (histType == eMult) { + namefull = Form("Quality assurance of %s %s vs. NContributors", tc.fMultEstm.c_str(), EventHistNames[histType]); + } else { + namefull = Form("Quality assurance of %s", EventHistNames[histType]); + } + + if (histType == eMult) { + qa.fQAHistograms[histType][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsCentX, minCentX, maxCentX, nBinsCentY, minCentY, maxCentY); + qa.fQAHistograms[histType][ba]->GetYaxis()->SetTitle("NContributors"); + qa.fQAHistograms[histType][ba]->GetXaxis()->SetTitle("Reference multiplicity"); + } else { + qa.fQAHistograms[histType][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsCent, minCent, maxCent, nBinsCent, minCent, maxCent); + qa.fQAHistograms[histType][ba]->GetYaxis()->SetTitle(Form("Simulated %s", EventHistNames[histType])); + qa.fQAHistograms[histType][ba]->GetXaxis()->SetTitle(Form("Reconstructed %s", EventHistNames[histType])); + } + qa.fQAHistogramsList->Add(qa.fQAHistograms[histType][ba]); + } + } + + void bookCorrHistograms(int histType, const std::vector>& lCrBins) + { + // (mult)(cent)(NContribMult) offset + int boundOffset = histType == eCorrCent ? eCentABBound : histType == eCorrMult ? eMultABBound + : eNumContribMultBound; + // (mult)(cent)(NContribMult) book functions: + if (histType == eCorrCent || histType == eCorrMult) { + for (int i = 0; i < eEstmCorr_N; i++) { + int indexBound = boundOffset + i; + cr.fCorrBounds[indexBound][0] = new TF1(Form("fCorrUpperBound%s%s", EstmCorrName[i], CorrHistNames[histType]), "[0] * x + [1]"); + cr.fCorrBounds[indexBound][1] = new TF1(Form("fCorrLowerBound%s%s", EstmCorrName[i], CorrHistNames[histType]), "[0] * x + [1]"); + cr.fCorrBounds[indexBound][0]->SetTitle(Form("%s %s upper bound", EstmCorrName[i], CorrHistNames[histType])); + cr.fCorrBounds[indexBound][1]->SetTitle(Form("%s %s lower bound", EstmCorrName[i], CorrHistNames[histType])); + } + } else if (histType == eCorrNumContribMult) { + int indexBound = boundOffset; + cr.fCorrBounds[indexBound][0] = new TF1(Form("fCorrUpperBound%s", CorrHistNames[histType]), "[0] * x + [1]"); + cr.fCorrBounds[indexBound][1] = new TF1(Form("fCorrLowerBound%s", CorrHistNames[histType]), "[0] * x + [1]"); + cr.fCorrBounds[indexBound][0]->SetTitle(Form("%s upper bound", CorrHistNames[histType])); + cr.fCorrBounds[indexBound][1]->SetTitle(Form("%s lower bound", CorrHistNames[histType])); + } + + int nBinsXCent = 0; + float minXCent = 0.; + float maxXCent = 0.; + int nBinsYCent = 0; + float minYCent = 0.; + float maxYCent = 0.; + int nBinsXMult = 0; + float minXMult = 0.; + float maxXMult = 0.; + int nBinsYMult = 0; + float minYMult = 0.; + float maxYMult = 0.; + // int nBinsNumContrib = 0; + // float minNumContrib = 0.; + float maxNumContrib = 0.; + + // (NContribMult) set functions and add to list: + if (histType == eCorrNumContribMult) { + + int indexBins = histType + static_cast(eMultEstm_N); + + // (NContribMult) get bins: + const auto& lMultXBins = lCrBins[indexBins]; + // nBinsXMult = static_cast(lMultXBins[0]); + minXMult = lMultXBins[1]; + maxXMult = lMultXBins[2]; + const auto& lNumContribBins = lCrBins[indexBins - 1]; + // nBinsNumContrib = static_cast(lNumContribBins[0]); + // minNumContrib = lNumContribBins[1]; + maxNumContrib = lNumContribBins[2]; + + // (NContribMult) set functions and add to list: + int indexBound = boundOffset; + cr.fCorrBounds[indexBound][0]->SetRange(minXMult, maxXMult); + cr.fCorrBounds[indexBound][1]->SetRange(minXMult, maxXMult); + cr.fCorrBounds[indexBound][0]->SetMinimum(0); + cr.fCorrBounds[indexBound][1]->SetMinimum(0); + cr.fCorrBounds[indexBound][0]->SetMaximum(maxNumContrib); + cr.fCorrBounds[indexBound][1]->SetMaximum(maxNumContrib); + cr.fCorrBounds[indexBound][0]->SetParameters(tc.fNumContribMultCorrCut[0], tc.fNumContribMultCorrCut[1]); + cr.fCorrBounds[indexBound][1]->SetParameters(tc.fNumContribMultCorrCut[2], tc.fNumContribMultCorrCut[3]); + cr.fCorrHistogramsList->Add(cr.fCorrBounds[indexBound][0]); + cr.fCorrHistogramsList->Add(cr.fCorrBounds[indexBound][1]); + } + + // (mult)(cent) Book histograms, set functions and add to list: + if (histType != eCorrNumContribMult) { + for (int ba = 0; ba < eBeforeAfter_N; ba++) { + std::string name; + std::string namefull = Form("%s correlation %s cut", CorrHistNames[histType], BeforeAfterNames[ba]); + + int nEstm = 0; + if (histType == eCorrCent) { + nEstm = eCentEstm_N; + } else if (histType == eCorrMult) { + nEstm = eMultEstm_N; + } + + int nComb = 0; // count combination AB, AC, BC + + for (int i = 0; i < nEstm; i++) { + + std::string titleX; + if (histType == eCorrCent) { + titleX = Form("%s %s", CentEstmNames[i], CorrHistNames[histType]); + + } else if (histType == eCorrMult) { + titleX = Form("%s %s", MultEstmNames[i], CorrHistNames[histType]); + } + + for (int j = i + 1; j < nEstm; j++) { + + std::string titleY; + if (histType == eCorrCent) { + name = Form("fHist%s[%s vs. %s][%s cut]", CorrHistNames[histType], CentEstmNames[i], CentEstmNames[j], BeforeAfterNames[ba]); + titleY = Form("%s %s", CentEstmNames[j], CorrHistNames[histType]); + + // (cent) get bins: + const auto& lCentXBins = lCrBins[0]; + nBinsXCent = static_cast(lCentXBins[0]); + minXCent = lCentXBins[1]; + maxXCent = lCentXBins[2]; + const auto& lCentYBins = lCrBins[0]; + nBinsYCent = static_cast(lCentYBins[0]); + minYCent = lCentYBins[1]; + maxYCent = lCentYBins[2]; + + // (cent) set functions and add to list: + if (static_cast(ba)) { + int indexBound = boundOffset + nComb; + cr.fCorrBounds[indexBound][0]->SetRange(minXCent, maxXCent); + cr.fCorrBounds[indexBound][1]->SetRange(minXCent, maxXCent); + cr.fCorrBounds[indexBound][0]->SetParameters(tc.fCentAllCorrCut[i][j][0], tc.fCentAllCorrCut[i][j][1]); + cr.fCorrBounds[indexBound][1]->SetParameters(tc.fCentAllCorrCut[i][j][2], tc.fCentAllCorrCut[i][j][3]); + cr.fCorrBounds[indexBound][0]->SetMinimum(0); + cr.fCorrBounds[indexBound][1]->SetMinimum(0); + cr.fCorrBounds[indexBound][0]->SetMaximum(maxYCent); + cr.fCorrBounds[indexBound][1]->SetMaximum(maxYCent); + cr.fCorrHistogramsList->Add(cr.fCorrBounds[indexBound][0]); + cr.fCorrHistogramsList->Add(cr.fCorrBounds[indexBound][1]); + } + + // (cent) book corr 2D histogram: + cr.fCorrHistograms[histType][i][j][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsXCent, minXCent, maxXCent, nBinsYCent, minYCent, maxYCent); + + } else if (histType == eCorrMult) { + name = Form("fHist%s[%s vs. %s][%s cut]", CorrHistNames[histType], MultEstmNames[i], MultEstmNames[j], BeforeAfterNames[ba]); + titleY = Form("%s %s", MultEstmNames[j], CorrHistNames[histType]); + + // (mult) get bins: + const auto& lMultXBins = lCrBins[i + 1]; + nBinsXMult = static_cast(lMultXBins[0]); + minXMult = lMultXBins[1]; + maxXMult = lMultXBins[2]; + const auto& lMultYBins = lCrBins[j + 1]; + nBinsYMult = static_cast(lMultYBins[0]); + minYMult = lMultYBins[1]; + maxYMult = lMultYBins[2]; + + // (mult) set functions and add to list: + if (static_cast(ba)) { + int indexBound = boundOffset + nComb; + cr.fCorrBounds[indexBound][0]->SetRange(minXMult, maxXMult); + cr.fCorrBounds[indexBound][1]->SetRange(minXMult, maxXMult); + cr.fCorrBounds[indexBound][0]->SetParameters(tc.fMultAllCorrCut[i][j][0], tc.fMultAllCorrCut[i][j][1]); + cr.fCorrBounds[indexBound][1]->SetParameters(tc.fMultAllCorrCut[i][j][2], tc.fMultAllCorrCut[i][j][3]); + cr.fCorrBounds[indexBound][0]->SetMinimum(0); + cr.fCorrBounds[indexBound][1]->SetMinimum(0); + cr.fCorrBounds[indexBound][0]->SetMaximum(maxYMult); + cr.fCorrBounds[indexBound][1]->SetMaximum(maxYMult); + cr.fCorrHistogramsList->Add(cr.fCorrBounds[indexBound][0]); + cr.fCorrHistogramsList->Add(cr.fCorrBounds[indexBound][1]); + } + + // (mult) book corr 2D histogram: + cr.fCorrHistograms[histType][i][j][ba] = new TH2F(name.c_str(), namefull.c_str(), nBinsXMult, minXMult, maxXMult, nBinsYMult, minYMult, maxYMult); + } + + // (cent)(mult) set corr histogram + cr.fCorrHistograms[histType][i][j][ba]->GetYaxis()->SetTitle(titleY.c_str()); + cr.fCorrHistograms[histType][i][j][ba]->GetXaxis()->SetTitle(titleX.c_str()); + cr.fCorrHistogramsList->Add(cr.fCorrHistograms[histType][i][j][ba]); + + nComb += 1; + } + } + } + } + } + + // *) Initialize and book all objects: + void init(InitContext&) + { + + // ... code to book and initialize all analysis objects ... + + // *) Set automatically what to process, from an implicit variable + // "doprocessSomeProcessName" within a PROCESS_SWITCH clause: + tc.fProcess[eProcessRec] = doprocessRec; + tc.fProcess[eProcessRecSim] = doprocessRecSim; + tc.fProcess[eProcessSim] = doprocessSim; + + // *) Configure your task using configurables in the json file: + tc.fDryRun = cfDryRun; + tc.fCentEstm = cfCentEstm; + tc.fMultEstm = cfMultEstm; + + tc.fGeneralCutSwitch = cfGeneralCutSwitch; + tc.fVertexZCutSwitch = cfVertexZCutSwitch; + tc.fSel8CutSwitch = cfSel8CutSwitch; + tc.fCentCutSwitch = cfCentCutSwitch; + tc.fNumContribCutSwitch = cfNumContribCutSwitch; + tc.fNumTrackCutSwitch = cfNumTrackCutSwitch; + tc.fOccupancyCutSwitch = cfOccupancyCutSwitch; + tc.fCentCorrCutSwitch = cfCentCorrCutSwitch; + tc.fMultCorrCutSwitch = cfMultCorrCutSwitch; + tc.fNumContribMultCorrCutSwitch = cfNumContribMultCorrCutSwitch; + + tc.fPtCutSwitch = cfPtCutSwitch; + tc.fEtaCutSwitch = cfEtaCutSwitch; + tc.fSignCutSwitch = cfSignCutSwitch; + tc.fTpcNClsFoundCutSwitch = cfTpcNClsFoundCutSwitch; + tc.fDCAXYCutSwitch = cfDCAXYCutSwitch; + tc.fDCAZCutSwitch = cfDCAZCutSwitch; + tc.fHasTpcItsCutSwitch = cfHasTpcItsCutSwitch; + + tc.fVertexZCut = cfVertexZCut; + tc.fCentCut = cfCentCut; + tc.fNumContribCut = cfNumContribCut; + tc.fNumTrackCut = cfNumTrackCut; + tc.fOccupancyCut = cfOccupancyCut; + tc.fCentAllCorrCut[0][1] = cfCentABCorrCut; + tc.fCentAllCorrCut[0][2] = cfCentACCorrCut; + tc.fCentAllCorrCut[1][2] = cfCentBCCorrCut; + tc.fMultAllCorrCut[0][1] = cfMultABCorrCut; + tc.fMultAllCorrCut[0][2] = cfMultACCorrCut; + tc.fMultAllCorrCut[1][2] = cfMultBCCorrCut; + tc.fNumContribMultCorrCut = cfNumContribMultCorrCut; + + tc.fPtCut = cfPtCut; + tc.fEtaCut = cfEtaCut; + tc.fSignCut = cfSignCut; + tc.fTpcNClsFoundCut = cfTpcNClsFoundCut; + tc.fDCAXYCut = cfDCAXYCut; + tc.fDCAZCut = cfDCAZCut; + tc.fHasTpcItsCut = cfHasTpcItsCut; + tc.fEtaGap = cfEtaGap; + + tc.fPtBins = cfPtBins; + tc.fEtaBins = cfEtaBins; + tc.fPhiBins = cfPhiBins; + tc.fDCAXYBins = cfDCAXYBins; + tc.fDCAZBins = cfDCAZBins; + + tc.fCentBins = cfCentBins; + tc.fFT0CMultBins = cfFT0CMultBins; + tc.fFT0MMultBins = cfFT0MMultBins; + tc.fFV0AMultBins = cfFV0AMultBins; + + tc.fVerXBins = cfVerXBins; + tc.fVerYBins = cfVerYBins; + tc.fVerZBins = cfVerZBins; + tc.fNumContribBins = cfNumContribBins; + tc.fNumTrackBins = cfNumTrackBins; + tc.fTwoParticleCorrBins = cfTwoParticleCorrBins; + tc.fThreeParticleCorrBins = cfThreeParticleCorrBins; + tc.fFourParticleCorrBins = cfFourParticleCorrBins; + tc.fSixParticleCorrBins = cfSixParticleCorrBins; + tc.fNumTrackTotalBins = cfNumTrackTotalBins; + + tc.fPrintSwitch = cfPrintSwitch; + tc.fFileWithWeights = cfFileWithWeights; + + qa.fQASwitch = cfQASwitch; + wt.fWeightSwitch = cfWeightSwitch; + + if (tc.fMultEstm == "FT0C") { + tc.fMultBins = tc.fFT0CMultBins; + } else if (tc.fMultEstm == "FT0M") { + tc.fMultBins = tc.fFT0MMultBins; + } else if (tc.fMultEstm == "FV0A") { + tc.fMultBins = tc.fFV0AMultBins; + } else { + LOGF(fatal, "Invalid multiplicity estimator: %s", tc.fMultEstm.c_str()); + } + + // *) Book base list: + auto* temp = new TList(); + temp->SetOwner(kTRUE); + fBaseList.setObject(temp); + + // *) Book and nest all other TLists: + pc.fParticleHistogramsList = new TList(); + pc.fParticleHistogramsList->SetName("ParticleHistograms"); + pc.fParticleHistogramsList->SetOwner(kTRUE); + fBaseList->Add(pc.fParticleHistogramsList); // any nested TList in the base TList appears as a subdir in the output ROOT file + + ev.fEventHistogramsList = new TList(); + ev.fEventHistogramsList->SetName("EventHistograms"); + ev.fEventHistogramsList->SetOwner(kTRUE); + fBaseList->Add(ev.fEventHistogramsList); + + qa.fQAHistogramsList = new TList(); + qa.fQAHistogramsList->SetName("QualityAssuranceHistograms"); + qa.fQAHistogramsList->SetOwner(kTRUE); + fBaseList->Add(qa.fQAHistogramsList); + + wt.fWeightHistogramsList = new TList(); + wt.fWeightHistogramsList->SetName("WeightHistograms"); + wt.fWeightHistogramsList->SetOwner(kTRUE); + fBaseList->Add(wt.fWeightHistogramsList); + + cr.fCorrHistogramsList = new TList(); + cr.fCorrHistogramsList->SetName("CorrelationHistograms"); + cr.fCorrHistogramsList->SetOwner(kTRUE); + fBaseList->Add(cr.fCorrHistogramsList); + + mc.fMultiparticleCorrelationProfilesList = new TList(); + mc.fMultiparticleCorrelationProfilesList->SetName("MultiparticleCorrelationProfiles"); + mc.fMultiparticleCorrelationProfilesList->SetOwner(kTRUE); + fBaseList->Add(mc.fMultiparticleCorrelationProfilesList); + + std::vector> lPcBins = {tc.fPtBins, tc.fEtaBins, tc.fPhiBins, tc.fDCAXYBins, tc.fDCAZBins}; + std::vector> lEvBins = {tc.fCentBins, tc.fMultBins, tc.fVerXBins, tc.fVerYBins, tc.fVerZBins, tc.fNumContribBins, tc.fNumTrackBins}; + std::vector> lQABins = {tc.fCentBins, tc.fMultBins, tc.fNumContribBins}; + std::vector> lCrBins = {tc.fCentBins, tc.fFT0CMultBins, tc.fFT0MMultBins, tc.fFV0AMultBins, tc.fNumContribBins, tc.fMultBins}; + + for (int i = 0; i < eParticleHistograms_N; i++) { + bookParticleHistograms(i, lPcBins); + } + for (int i = 0; i < eEventHistograms_N; i++) { + bookEventHistograms(i, lEvBins); + } + for (int i = 0; i < eQAHistograms_N; i++) { + bookQAHistograms(i, lQABins); + } + for (int i = 0; i < eCorrHistograms_N; i++) { + bookCorrHistograms(i, lCrBins); + } + + wt.fDummyPhiWeightHistogram = new TH1F("fDummyPhiWeightHistogram", "Dummy phi weight histogram", static_cast(tc.fPhiBins[0]), tc.fPhiBins[1], tc.fPhiBins[2]); + for (int i = 1; i <= wt.fDummyPhiWeightHistogram->GetNbinsX(); i++) { + wt.fDummyPhiWeightHistogram->SetBinContent(i, 1.); + } + wt.fDummyPtWeightHistogram = new TH1F("fDummyPtWeightHistogram", "Dummy pt weight histogram", static_cast(tc.fPtBins[0]), tc.fPtBins[1], tc.fPtBins[2]); + for (int i = 1; i <= wt.fDummyPtWeightHistogram->GetNbinsX(); i++) { + wt.fDummyPtWeightHistogram->SetBinContent(i, 1.); + } + + } // end of void init(InitContext&) { + + // A) Process only reconstructed data: + void processRec(CollisionRec const& collision, aod::BCs const&, TracksRec const& tracks) + { + // ... + // *) Steer all analysis steps: + runLoop(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCumulants, processRec, "process only reconstructed data", true); // yes, keep always one process switch "true", so that there is default running version + + // ------------------------------------------- + + // B) Process both reconstructed and corresponding MC truth simulated data: + void processRecSim(CollisionRecSim const& collision, aod::BCs const&, TracksRecSim const& tracks, aod::McParticles const&, aod::McCollisions const&) + { + runLoop(collision, tracks); + } + PROCESS_SWITCH(MultiparticleCumulants, processRecSim, "process both reconstructed and corresponding MC truth simulated data", false); + + // ------------------------------------------- + + // C) Process only simulated data: + void processSim(CollisionSim const& /*collision*/, aod::BCs const&, TracksSim const& /*tracks*/) + { + // runLoop(collision, tracks); // TBI 20241105 not ready yet, but I do not really need this one urgently, since RecSim is working, and I need that one for efficiencies... + } + PROCESS_SWITCH(MultiparticleCumulants, processSim, "process only simulated data", false); + +}; // struct MultiparticleCumulants { + +// *) The final touch: +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} // WorkflowSpec... diff --git a/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx b/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx index b6aab98d17e..b78325f9d01 100644 --- a/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx +++ b/PWGCF/MultiparticleCorrelations/Tasks/threeParticleCorrelations.cxx @@ -82,14 +82,14 @@ struct ThreeParticleCorrelations { std::string prefix = "EventSelection"; Configurable zvtxMax{"zvtxMax", 10.0, "Maximum collision Z-vertex position (cm)"}; Configurable occupMin{"occupMin", 0, "Minimum collision occupancy"}; - Configurable occupMax{"occupMax", 15000, "Maximum collision occupancy"}; + Configurable occupMax{"occupMax", 500, "Maximum collision occupancy"}; Configurable useOccupCut{"useOccupCut", true, "Use the kNoCollInTimeRangeStandard cut"}; } evSelGroup; // V0 filter parameters struct : ConfigurableGroup { std::string prefix = "V0Selection"; - Configurable v0PtMin{"v0PtMin", 0.6, "Minimum V0 transverse momentum"}; + Configurable v0PtMin{"v0PtMin", 1.6, "Minimum V0 transverse momentum"}; Configurable v0PtMax{"v0PtMax", 12.0, "Maximum V0 transverse momentum"}; Configurable tpcNCrossedRows{"tpcNCrossedRows", 70.0, "Minimum number of TPC crossed rows"}; Configurable decayR{"decayR", 1.2, "Minimum V0 decay radius (cm)"}; @@ -182,8 +182,8 @@ struct ThreeParticleCorrelations { using BinningType = ColumnBinningPolicy; using BinningTypeMC = ColumnBinningPolicy; - BinningType collBinning{{confCentBins, confZvtxBins}, true}; - BinningTypeMC collBinningMC{{confCentBins, confZvtxBins}, true}; + BinningType collBinning{{confCentBins, confZvtxBins}}; + BinningTypeMC collBinningMC{{confCentBins, confZvtxBins}}; Pair pairData{collBinning, 5, -1, &cache}; SameKindPair pairMC{collBinningMC, 5, -1, &cache}; @@ -191,7 +191,7 @@ struct ThreeParticleCorrelations { struct : ConfigurableGroup { std::string prefix = "processSwitchBoard"; Configurable confBfieldSwitch{"confBfieldSwitch", 0, "Switch for the detector magnetic field (1 if Pos, -1 if Neg, 0 if both)"}; - Configurable confRatioCorrectionSwitch{"confRatioCorrectionSwitch", false, "Switch for correcting the negative spectra back to the positive spectra"}; + Configurable confRatioCorrectionSwitch{"confRatioCorrectionSwitch", true, "Switch for correcting the negative spectra back to the positive spectra"}; Configurable confFakeV0Switch{"confFakeV0Switch", false, "Switch for the fakeV0Filter function"}; Configurable confRDSwitch{"confRDSwitch", true, "Switch for the radialDistanceFilter function"}; } switchGroup; diff --git a/PWGCF/TableProducer/CMakeLists.txt b/PWGCF/TableProducer/CMakeLists.txt index 6438d45a5fe..7d893f3a156 100644 --- a/PWGCF/TableProducer/CMakeLists.txt +++ b/PWGCF/TableProducer/CMakeLists.txt @@ -11,7 +11,7 @@ o2physics_add_dpl_workflow(filter-correlations SOURCES filterCorrelations.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-correlations-2prong diff --git a/PWGCF/TableProducer/dptDptFilter.cxx b/PWGCF/TableProducer/dptDptFilter.cxx index baff1b96e2d..592070588c7 100644 --- a/PWGCF/TableProducer/dptDptFilter.cxx +++ b/PWGCF/TableProducer/dptDptFilter.cxx @@ -72,8 +72,8 @@ namespace o2::analysis::dptdptfilter { using DptDptFullTracks = soa::Join; using DptDptFullTracksAmbiguous = soa::Join; -using DptDptTracksPID = soa::Join; -using DptDptTracksFullPID = soa::Join; +using DptDptTracksPID = soa::Join; +using DptDptTracksFullPID = soa::Join; using DptDptFullTracksPID = soa::Join; using DptDptFullTracksPIDAmbiguous = soa::Join; using DptDptFullTracksFullPID = soa::Join; @@ -1160,7 +1160,7 @@ struct DptDptFilterTracks { Configurable cfgTrackType{"cfgTrackType", 4, "Type of selected tracks: 0 = no selection;1 = Run2 global tracks FB96;3 = Run3 tracks;4 = Run3 tracks MM sel;5 = Run2 TPC only tracks;7 = Run 3 TPC only tracks;30-33 = any/two on 3 ITS,any/all in 7 ITS;40-43 same as 30-33 w tighter DCAxy;50-53 w tighter pT DCAz. Default 4"}; Configurable cfgOnlyInOneSide{"cfgOnlyInOneSide", false, "select tracks that don't cross the TPC central membrane. Default false"}; Configurable cfgTraceDCAOutliers{"cfgTraceDCAOutliers", {false, 0.0, 0.0}, "Track the generator level DCAxy outliers: false/true, low dcaxy, up dcaxy. Default {false,0.0,0.0}"}; - Configurable cfgTraceOutOfSpeciesParticles{"cfgTraceOutOfSpeciesParticles", false, "Track the particles which are not e,mu,pi,K,p: false/true. Default false"}; + Configurable cfgTraceOutOfSpeciesParticles{"cfgTraceOutOfSpeciesParticles", false, "Track the particles which are not e,mu,pi,K,p,d: false/true. Default false"}; Configurable cfgRecoIdMethod{"cfgRecoIdMethod", 0, "Method for identifying reconstructed tracks: 0 No PID, 1 PID, 2 mcparticle, 3 mcparticle only primaries, 4 mcparticle only sec, 5 mcparicle only sec from decays, 6 mcparticle only sec from material. Default 0"}; Configurable cfgTuneTrackSelection{"cfgTuneTrackSelection", {}, "Track selection: {useit: true/false, tpccls-useit, tpcxrws-useit, tpcxrfc-useit, tpcshcls-useit, dcaxy-useit, dcaz-useit}. Default {false,0.70,false,0.8,false,0.4,false,2.4,false,3.2,false}"}; Configurable cfgPionPIDSelection{"cfgPionPIDSelection", @@ -1178,6 +1178,9 @@ struct DptDptFilterTracks { Configurable cfgMuonPIDSelection{"cfgMuonPIDSelection", {}, "PID criteria for muons"}; + Configurable cfgDeuteronPIDSelection{"cfgDeuteronPIDSelection", + {}, + "PID criteria for deuterons"}; OutputObj fOutput{"DptDptFilterTracksInfo", OutputObjHandlingPolicy::AnalysisObject}; Service fPDG; @@ -1280,6 +1283,7 @@ struct DptDptFilterTracks { insertInPIDselector(cfgPionPIDSelection, 2); insertInPIDselector(cfgKaonPIDSelection, 3); insertInPIDselector(cfgProtonPIDSelection, 4); + insertInPIDselector(cfgDeuteronPIDSelection, 5); if ((fDataType == kData) || (fDataType == kDataNoEvtSel) || (fDataType == kMC)) { /* create the reconstructed data histograms */ @@ -1481,7 +1485,7 @@ struct DptDptFilterTracks { /* the debug info output file if required */ if (cfgOutDebugInfo) { debugstream.open("tracings.csv"); - debugstream << "p,piw,pt,hastof,dEdx,beta,tpcnEl,tpcnMu,tpcnPi,tpcnKa,tpcnPr,tofnEl,tofnMu,tofnPi,tofnKa,tofnPr,tpcnElSft,tpcnMuSft,tpcnPiSft,tpcnKaSft,tpcnPrSft,tofnElSft,tofnMuSft,tofnPiSft,tofnKaSft,tofnPrSft,idcode,pid,pid2,truepid,phprim,process\n"; + debugstream << "p,piw,pt,hastof,dEdx,beta,tpcnEl,tpcnMu,tpcnPi,tpcnKa,tpcnPr,tpcnDe,tofnEl,tofnMu,tofnPi,tofnKa,tofnPr,tofnDe,tpcnElSft,tpcnMuSft,tpcnPiSft,tpcnKaSft,tpcnPrSft,tpcnDeSft,tofnElSft,tofnMuSft,tofnPiSft,tofnKaSft,tofnPrSft,tofnDeSft,idcode,pid,pid2,truepid,phprim,process\n"; debugstream.close(); } } diff --git a/PWGCF/TableProducer/dptDptFilter.h b/PWGCF/TableProducer/dptDptFilter.h index 3d85ab3a79d..879b8092209 100644 --- a/PWGCF/TableProducer/dptDptFilter.h +++ b/PWGCF/TableProducer/dptDptFilter.h @@ -60,6 +60,7 @@ #include #include #include +#include #include namespace o2 @@ -553,6 +554,10 @@ int tracktype = 1; std::vector trackFilters = {}; // the vector of track selectors struct DptDptTrackSelection { + /* disable copying and assignment explicitly */ + DptDptTrackSelection(const DptDptTrackSelection&) = delete; + DptDptTrackSelection& operator=(const DptDptTrackSelection&) = delete; + DptDptTrackSelection(TrackSelection* stdTs, TList* outputList, const char* name) : stdTrackSelection(stdTs) { passedHistogram = new TH1F(name, name, ptbins, ptlow, ptup); @@ -560,7 +565,7 @@ struct DptDptTrackSelection { } DptDptTrackSelection(TrackSelection* stdTs, std::function ptDepCut, TList* outputList, const char* name) : stdTrackSelection(stdTs), - maxDcazPtDep(ptDepCut) + maxDcazPtDep(std::move(ptDepCut)) { passedHistogram = new TH1F(name, name, ptbins, ptlow, ptup); outputList->Add(passedHistogram); @@ -577,7 +582,7 @@ struct DptDptTrackSelection { } void setMaxDcazPtDep(std::function ptDepCut) { - maxDcazPtDep = ptDepCut; + maxDcazPtDep = std::move(ptDepCut); } void setRequirePvContributor(bool pvc = true) { @@ -835,7 +840,7 @@ float particleMaxDCAxy = 999.9f; float particleMaxDCAZ = 999.9f; bool traceCollId0 = false; -inline TList* getCCDBInput(auto& ccdb, const char* ccdbpath, const char* ccdbdate, bool periodInPath = false, const std::string& suffix = "") +inline TList* getCCDBInput(const auto& ccdb, const char* ccdbpath, const char* ccdbdate, bool periodInPath = false, const std::string& suffix = "") { std::tm cfgtm = {}; std::stringstream ss(ccdbdate); @@ -1106,7 +1111,7 @@ template inline bool triggerSelection(CollisionObject const&) { LOGF(fatal, "Trigger selection not implemented for this kind of collisions"); - return false; + return true; } /// \brief Trigger selection for reconstructed collision tables without centrality/multiplicity @@ -1210,7 +1215,7 @@ inline float extractMultiplicity(CollisionObject const& collision, CentMultEstim /// \brief Centrality/multiplicity percentile template requires(o2::aod::HasRun2Centrality) -float getCentMultPercentile(CollisionObject collision) +float getCentMultPercentile(const CollisionObject& collision) { switch (fCentMultEstimator) { case CentMultV0M: @@ -1226,7 +1231,7 @@ float getCentMultPercentile(CollisionObject collision) template requires(o2::aod::HasCentrality) -float getCentMultPercentile(CollisionObject collision) +float getCentMultPercentile(const CollisionObject& collision) { switch (fCentMultEstimator) { case CentMultFV0A: @@ -1246,9 +1251,9 @@ float getCentMultPercentile(CollisionObject collision) /// \brief Centrality selection when there is centrality/multiplicity information template -inline bool centralitySelectionMult(CollisionObject collision, float& centmult) +inline bool centralitySelectionMult(const CollisionObject& collision, float& centmult) { - float mult = getCentMultPercentile(collision); + float mult = getCentMultPercentile(std::move(collision)); if (mult < ValidPercentileUpLimit && ValidPercentileLowLimit < mult) { centmult = mult; collisionFlags.set(CollSelCENTRALITYBIT); @@ -1279,7 +1284,7 @@ template inline bool centralitySelection(CollisionObject const&, float&) { LOGF(fatal, "Centrality selection not implemented for this kind of collisions"); - return false; + return true; } /// \brief Centrality selection for reconstructed and detector level collision tables with centrality/multiplicity information @@ -1365,7 +1370,7 @@ inline bool isCollisionNotExcluded() /// \brief select on the collision occupancy /// \return true if collison passes the occupancy cut false otherwise template -inline bool selectOnOccupancy(CollisionObject collision) +inline bool selectOnOccupancy(const CollisionObject& collision) { switch (fOccupancyEstimation) { case OccupancyNOOCC: @@ -1395,7 +1400,7 @@ template inline bool occupancySelection(CollisionObject const&) { LOGF(fatal, "Occupancy selection not implemented for this kind of collisions"); - return false; + return true; } /// \brief Occupancy selection for reconstructed and detector level collision tables with centrality/multiplicity information @@ -1560,7 +1565,7 @@ struct TpcExcludeTrack { switch (method) { case kNOEXCLUSION: { return false; - } break; + } case kSTATIC: { int phiBinIx = getPhiBinIx(track); /* bins multiple of four have got sector border */ @@ -1569,7 +1574,7 @@ struct TpcExcludeTrack { } else { return true; } - } break; + } case kDYNAMIC: { float phiInTpcSector = std::fmod(track.phi(), TpcPhiSectorWidth); if (track.sign() > 0) { @@ -1577,13 +1582,13 @@ struct TpcExcludeTrack { } else { return (phiInTpcSector < negativeUpCut->Eval(track.pt())) && (negativeLowCut->Eval(track.pt()) < phiInTpcSector); } - } break; + } default: return false; } } - void setCuts(std::string pLowCut, std::string pUpCut, std::string nLowCut, std::string nUpCut) + void setCuts(const std::string& pLowCut, const std::string& pUpCut, const std::string& nLowCut, const std::string& nUpCut) { LOGF(info, "Setting the TPC exclusion cuts: pLow=%s, pUp=%s, nLow=%s, nUp=%s", pLowCut, pUpCut, nLowCut, nUpCut); positiveLowCut = new TF1("posLowCut", pLowCut.c_str(), ptlow, ptup); @@ -1732,12 +1737,12 @@ inline bool acceptParticle(ParticleObject& particle, MCCollisionObject const&) ////////////////////////////////////////////////////////////////////////////////// struct PIDSpeciesSelection { - const std::vector pdgcodes = {kElectron, kMuonMinus, kPiPlus, kKPlus, kProton}; - const std::vector spnames = {"e", "mu", "pi", "ka", "p"}; - const std::vector sptitles = {"e", "#mu", "#pi", "K", "p"}; - const std::vector spfnames = {"E", "Mu", "Pi", "Ka", "Pr"}; - const std::vector spadjnames = {"Electron", "Muon", "Pion", "Kaon", "Proton"}; - const std::vector spmasses = {o2::constants::physics::MassElectron, o2::constants::physics::MassMuon, o2::constants::physics::MassPionCharged, o2::constants::physics::MassKaonCharged, o2::constants::physics::MassProton}; + const std::vector pdgcodes = {kElectron, kMuonMinus, kPiPlus, kKPlus, kProton, o2::constants::physics::kDeuteron}; + const std::vector spnames = {"e", "mu", "pi", "ka", "p", "d"}; + const std::vector sptitles = {"e", "#mu", "#pi", "K", "p", "d"}; + const std::vector spfnames = {"E", "Mu", "Pi", "Ka", "Pr", "De"}; + const std::vector spadjnames = {"Electron", "Muon", "Pion", "Kaon", "Proton", "Deuteron"}; + const std::vector spmasses = {o2::constants::physics::MassElectron, o2::constants::physics::MassMuon, o2::constants::physics::MassPionCharged, o2::constants::physics::MassKaonCharged, o2::constants::physics::MassProton, o2::constants::physics::MassDeuteron}; const std::vector chadjnames = {"P", "M"}; const char* hadname = "h"; const char* hadtitle = "h"; @@ -1786,10 +1791,10 @@ struct PIDSpeciesSelection { auto last = config[config.size() - 1]; uint8_t lastsp = species[config.size() - 1]; LOGF(info, "Inserted species %d with", lastsp); - LOGF(info, " minTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMinNSigmasTPC[0], last->mMinNSigmasTPC[1], last->mMinNSigmasTPC[2], last->mMinNSigmasTPC[3], last->mMinNSigmasTPC[4]); - LOGF(info, " maxTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMaxNSigmasTPC[0], last->mMaxNSigmasTPC[1], last->mMaxNSigmasTPC[2], last->mMaxNSigmasTPC[3], last->mMaxNSigmasTPC[4]); - LOGF(info, " minTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMinNSigmasTOF[0], last->mMinNSigmasTOF[1], last->mMinNSigmasTOF[2], last->mMinNSigmasTOF[3], last->mMinNSigmasTOF[4]); - LOGF(info, " maxTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMaxNSigmasTOF[0], last->mMaxNSigmasTOF[1], last->mMaxNSigmasTOF[2], last->mMaxNSigmasTOF[3], last->mMaxNSigmasTOF[4]); + LOGF(info, " minTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMinNSigmasTPC[0], last->mMinNSigmasTPC[1], last->mMinNSigmasTPC[2], last->mMinNSigmasTPC[3], last->mMinNSigmasTPC[4], last->mMinNSigmasTPC[5]); + LOGF(info, " maxTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMaxNSigmasTPC[0], last->mMaxNSigmasTPC[1], last->mMaxNSigmasTPC[2], last->mMaxNSigmasTPC[3], last->mMaxNSigmasTPC[4], last->mMaxNSigmasTPC[5]); + LOGF(info, " minTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMinNSigmasTOF[0], last->mMinNSigmasTOF[1], last->mMinNSigmasTOF[2], last->mMinNSigmasTOF[3], last->mMinNSigmasTOF[4], last->mMinNSigmasTOF[5]); + LOGF(info, " maxTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMaxNSigmasTOF[0], last->mMaxNSigmasTOF[1], last->mMaxNSigmasTOF[2], last->mMaxNSigmasTOF[3], last->mMaxNSigmasTOF[4], last->mMaxNSigmasTOF[5]); LOGF(info, " %.1f < pT < %.1f", last->mPtMin, last->mPtMax); } void addExcludedSpecies(uint8_t sp, const o2::analysis::TrackSelectionPIDCfg* incfg) @@ -1801,18 +1806,18 @@ struct PIDSpeciesSelection { uint8_t lastsp = speciesexclude[configexclude.size() - 1]; LOGF(info, "Inserted species %d for exclusion with", lastsp); - LOGF(info, " minTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMinNSigmasTPC[0], last->mMinNSigmasTPC[1], last->mMinNSigmasTPC[2], last->mMinNSigmasTPC[3], last->mMinNSigmasTPC[4]); - LOGF(info, " maxTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMaxNSigmasTPC[0], last->mMaxNSigmasTPC[1], last->mMaxNSigmasTPC[2], last->mMaxNSigmasTPC[3], last->mMaxNSigmasTPC[4]); - LOGF(info, " minTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMinNSigmasTOF[0], last->mMinNSigmasTOF[1], last->mMinNSigmasTOF[2], last->mMinNSigmasTOF[3], last->mMinNSigmasTOF[4]); - LOGF(info, " maxTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f", last->mMaxNSigmasTOF[0], last->mMaxNSigmasTOF[1], last->mMaxNSigmasTOF[2], last->mMaxNSigmasTOF[3], last->mMaxNSigmasTOF[4]); + LOGF(info, " minTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMinNSigmasTPC[0], last->mMinNSigmasTPC[1], last->mMinNSigmasTPC[2], last->mMinNSigmasTPC[3], last->mMinNSigmasTPC[4], last->mMinNSigmasTPC[5]); + LOGF(info, " maxTPC nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMaxNSigmasTPC[0], last->mMaxNSigmasTPC[1], last->mMaxNSigmasTPC[2], last->mMaxNSigmasTPC[3], last->mMaxNSigmasTPC[4], last->mMaxNSigmasTPC[5]); + LOGF(info, " minTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMinNSigmasTOF[0], last->mMinNSigmasTOF[1], last->mMinNSigmasTOF[2], last->mMinNSigmasTOF[3], last->mMinNSigmasTOF[4], last->mMinNSigmasTOF[5]); + LOGF(info, " maxTOF nsigmas: el: %.2f, mu: %.2f, pi: %.2f, ka: %.2f, pr: %.2f, de: %.2f", last->mMaxNSigmasTOF[0], last->mMaxNSigmasTOF[1], last->mMaxNSigmasTOF[2], last->mMaxNSigmasTOF[3], last->mMaxNSigmasTOF[4], last->mMaxNSigmasTOF[5]); LOGF(info, " %.1f < pT < %.1f", last->mPtMin, last->mPtMax); } template int8_t whichSpecies(TrackObject const& track) { TString debuginfo; - std::vector tpcnsigmas = {track.tpcNSigmaEl(), track.tpcNSigmaMu(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; - std::vector tofnsigmas = {track.tofNSigmaEl(), track.tofNSigmaMu(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + std::vector tpcnsigmas = {track.tpcNSigmaEl(), track.tpcNSigmaMu(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), track.tpcNSigmaDe()}; + std::vector tofnsigmas = {track.tofNSigmaEl(), track.tofNSigmaMu(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), track.tofNSigmaDe()}; auto outmomentumdebug = [&]() { if constexpr (outdebug != 0) { @@ -1835,14 +1840,14 @@ struct PIDSpeciesSelection { /* out debug if needed */ outnsigmasdebug(); - auto closeTo = [](auto& values, auto& mindet, auto& maxdet, uint8_t sp) { + auto closeTo = [](const auto& values, const auto& mindet, const auto& maxdet, uint8_t sp) { if (mindet[sp] <= values[sp] && values[sp] < maxdet[sp]) { return true; } else { return false; } }; - auto awayFrom = [&](auto& values, auto& mindet, auto& maxdet, uint8_t sp) { + auto awayFrom = [&](const auto& values, const auto& mindet, const auto& maxdet, uint8_t sp) { for (size_t ix = 0; ix < pdgcodes.size(); ix++) { if (ix != sp) { if (mindet[ix] <= values[ix] && values[ix] < maxdet[ix]) { @@ -1888,7 +1893,7 @@ struct PIDSpeciesSelection { } return true; }; - auto aboveThreshold = [&](auto& config) { + auto aboveThreshold = [&](const auto& config) { return ((config->mPThreshold > 0.0) && (config->mPThreshold < track.p())); }; auto isA = [&](auto& config, uint8_t sp) { diff --git a/PWGCF/TableProducer/filter2Prong.cxx b/PWGCF/TableProducer/filter2Prong.cxx index da28c853e3b..e33fc8cae64 100644 --- a/PWGCF/TableProducer/filter2Prong.cxx +++ b/PWGCF/TableProducer/filter2Prong.cxx @@ -19,7 +19,9 @@ #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -29,7 +31,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -44,10 +49,12 @@ #include #include #include +#include #include #include #include #include +#include #include using namespace o2; @@ -64,6 +71,8 @@ enum LambdaPid { kLambda = 0, struct Filter2Prong { SliceCache cache; + Preslice perCollisionCFTrackRefs = aod::track::collisionId; + Preslice perCollisionCFCollRefs = aod::track::collisionId; O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 0, "Verbosity level (0 = major, 1 = per collision)") O2_DEFINE_CONFIGURABLE(cfgYMax, float, -1.0f, "Maximum candidate rapidity") @@ -170,7 +179,6 @@ struct Filter2Prong { ConfigurableAxis axisVertexMix{"axisVertexMix", {7, -7, 7}, "vertex axis for phi event mixing"}; ConfigurableAxis axisMultiplicityMix{"axisMultiplicityMix", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity axis for phi event mixing"}; - HfHelper hfHelper; Produces output2ProngTracks; Produces output2ProngTrackmls; @@ -237,7 +245,7 @@ struct Filter2Prong { // look-up the collision id if ((c.hfflag() & (1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) == 0) continue; - if (cfgYMax >= 0.0f && std::abs(hfHelper.yD0(c)) > cfgYMax) + if (cfgYMax >= 0.0f && std::abs(HfHelper::yD0(c)) > cfgYMax) continue; if constexpr (std::experimental::is_detected::value) { if (std::abs(c.flagMcMatchRec()) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) @@ -246,7 +254,7 @@ struct Filter2Prong { if (c.isSelD0() > 0) { output2ProngTracks(cfcollisions.begin().globalIndex(), - prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0ToPiK(c), aod::cf2prongtrack::D0ToPiK); + prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), HfHelper::invMassD0ToPiK(c), aod::cf2prongtrack::D0ToPiK); if constexpr (std::experimental::is_detected::value) { mlvecd.clear(); for (const float val : c.mlProbD0()) @@ -260,7 +268,7 @@ struct Filter2Prong { if (c.isSelD0bar() > 0) { output2ProngTracks(cfcollisions.begin().globalIndex(), - prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), hfHelper.invMassD0barToKPi(c), + prongCFId[0], prongCFId[1], c.pt(), c.eta(), c.phi(), HfHelper::invMassD0barToKPi(c), c.isSelD0() > 0 ? aod::cf2prongtrack::D0barToKPi : aod::cf2prongtrack::D0barToKPiExclusive); if constexpr (std::experimental::is_detected::value) { mlvecd.clear(); @@ -799,23 +807,51 @@ struct Filter2Prong { PROCESS_SWITCH(Filter2Prong, processDataPhiV0, "Process data Phi and V0 candidates with invariant mass method", false); using DerivedCollisions = soa::Join; - void processDataPhiMixed(DerivedCollisions const& collisions, aod::CFTrackRefs const& cftracks) + void processDataPhiMixed(DerivedCollisions const& collisions, Filter2Prong::PIDTrack const& /*tracksP*/, aod::CFCollRefs const& cfcollrefs, aod::CFTrackRefs const& cftracks) { + struct MixedPhiCandidate { + int64_t cfCollisionId; + int64_t cfTrackProng0Id; + int64_t cfTrackProng1Id; + float pt; + float eta; + float phi; + float invMass; + uint8_t decay; + }; + + if (cfcollrefs.size() <= 0 || cftracks.size() <= 0) { + return; + } + auto getMultiplicity = [](auto const& col) { return col.multiplicity(); }; + using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertexMix, axisMultiplicityMix}, true}; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertexMix, axisMultiplicityMix}}; auto tracksTuple = std::make_tuple(cftracks, cftracks); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; using TB = std::tuple_element - 1, decltype(tracksTuple)>::type; Pair pairs{configurableBinningDerived, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + std::unordered_map collToCF; + collToCF.reserve(cfcollrefs.size()); + for (const auto& cfcollref : cfcollrefs) { + collToCF.emplace(cfcollref.collisionId(), cfcollref.globalIndex()); + } + + std::vector mixedPhiCandidates; o2::aod::ITSResponse itsResponse; for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; + auto cfColl1 = collToCF.find(collision1.globalIndex()); + if (cfColl1 == collToCF.end() || collToCF.find(collision2.globalIndex()) == collToCF.end()) { + continue; + } + if (!(collision1.sel8() && collision1.selection_bit(aod::evsel::kNoSameBunchPileup) && collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && @@ -882,16 +918,40 @@ struct Filter2Prong { } float phi = RecoDecay::constrainAngle(s.Phi(), 0.0f); - - output2ProngTracks(collision1.globalIndex(), - cftrack1.globalIndex(), cftrack2.globalIndex(), - s.pt(), s.eta(), phi, s.M(), - aod::cf2prongtrack::PhiToKKPID3Mixed); + mixedPhiCandidates.push_back({cfColl1->second, + cftrack1.globalIndex(), + cftrack2.globalIndex(), + static_cast(s.pt()), + static_cast(s.eta()), + phi, + static_cast(s.M()), + aod::cf2prongtrack::PhiToKKPID3Mixed}); } } } } } + + std::sort(mixedPhiCandidates.begin(), mixedPhiCandidates.end(), [](const auto& lhs, const auto& rhs) { + if (lhs.cfCollisionId != rhs.cfCollisionId) { + return lhs.cfCollisionId < rhs.cfCollisionId; + } + if (lhs.cfTrackProng0Id != rhs.cfTrackProng0Id) { + return lhs.cfTrackProng0Id < rhs.cfTrackProng0Id; + } + return lhs.cfTrackProng1Id < rhs.cfTrackProng1Id; + }); + + for (const auto& candidate : mixedPhiCandidates) { + output2ProngTracks(candidate.cfCollisionId, + candidate.cfTrackProng0Id, + candidate.cfTrackProng1Id, + candidate.pt, + candidate.eta, + candidate.phi, + candidate.invMass, + candidate.decay); + } } PROCESS_SWITCH(Filter2Prong, processDataPhiMixed, "Process mixed-event phi candidates using O2 framework", false); diff --git a/PWGCF/TableProducer/filterCorrelations.cxx b/PWGCF/TableProducer/filterCorrelations.cxx index bdc404ea7f0..7b91bbfe308 100644 --- a/PWGCF/TableProducer/filterCorrelations.cxx +++ b/PWGCF/TableProducer/filterCorrelations.cxx @@ -9,10 +9,13 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// o2-linter: disable=name/workflow-file (file contains several table-producer tasks) #include "PWGCF/DataModel/CorrelationsDerived.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" +#include "Common/Core/TableHelper.h" +#include "Common/Core/Zorro.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -21,6 +24,8 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include +#include #include #include #include @@ -35,29 +40,38 @@ #include #include +#include #include +#include #include #include #include +#include +#include +#include #include #include // required for is_detected +#include +#include +#include #include #include -#include - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::math_utils::detail; -#define FLOAT_PRECISION 0xFFFFFFF0 -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +constexpr std::uint32_t kFloatPrecision = 0xFFFFFFF0u; +// clang-format off +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME { #NAME, (DEFAULT), (HELP) } // NOLINT(bugprone-macro-parentheses) +// clang-format on struct FilterCF { - Service pdg; + Service pdg{}; + Service ccdb{}; enum TrackSelectionCuts1 : uint8_t { kTrackSelected = BIT(0), @@ -73,36 +87,40 @@ struct FilterCF { }; // Configuration - O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 7.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgCutPt, float, 0.5f, "Minimal pT for tracks") - O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") - O2_DEFINE_CONFIGURABLE(cfgCutMCPt, float, 0.5f, "Minimal pT for particles") - O2_DEFINE_CONFIGURABLE(cfgCutMCEta, float, 0.8f, "Eta range for particles") - O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)") - O2_DEFINE_CONFIGURABLE(cfgTrigger, int, 7, "Trigger choice: (0 = none, 7 = sel7, 8 = sel8, 9 = sel8 + kNoSameBunchPileup + kIsGoodZvtxFT0vsPV, 10 = sel8 before April, 2024, 11 = sel8 for MC, 12 = sel8 with low occupancy cut, 13 = sel8 + kNoSameBunchPileup + kIsGoodITSLayersAll -- for OO/NeNe) ") - O2_DEFINE_CONFIGURABLE(cfgMinOcc, int, 0, "minimum occupancy selection") - O2_DEFINE_CONFIGURABLE(cfgMaxOcc, int, 3000, "maximum occupancy selection") - O2_DEFINE_CONFIGURABLE(cfgCollisionFlags, uint16_t, aod::collision::CollisionFlagsRun2::Run2VertexerTracks, "Request collision flags if non-zero (0 = off, 1 = Run2VertexerTracks)") - O2_DEFINE_CONFIGURABLE(cfgTransientTables, bool, false, "Output transient tables for collision and track IDs to enable successive filtering tasks") - O2_DEFINE_CONFIGURABLE(cfgTrackSelection, int, 0, "Type of track selection (0 = Run 2/3 without systematics | 1 = Run 3 with systematics | 2 = Run 3 with proton pid selection)") - O2_DEFINE_CONFIGURABLE(cfgMinMultiplicity, float, -1, "Minimum multiplicity considered for filtering (if value positive)") - O2_DEFINE_CONFIGURABLE(cfgMcSpecialPDGs, std::vector, {}, "Special MC PDG codes to include in the MC primary particle output (additional to charged particles). Empty = charged particles only.") // needed for some neutral particles - O2_DEFINE_CONFIGURABLE(nsigmaCutTPCProton, float, 3, "proton nsigma TPC") - O2_DEFINE_CONFIGURABLE(nsigmaCutTOFProton, float, 3, "proton nsigma TOF") - O2_DEFINE_CONFIGURABLE(ITSProtonselection, bool, false, "flag for ITS proton nsigma selection") - O2_DEFINE_CONFIGURABLE(nsigmaCutITSProton, float, 3, "proton nsigma ITS") - O2_DEFINE_CONFIGURABLE(dcaxymax, float, 999.f, "maximum dcaxy of tracks") - O2_DEFINE_CONFIGURABLE(dcazmax, float, 999.f, "maximum dcaz of tracks") - O2_DEFINE_CONFIGURABLE(enablePtDepDCAxy, bool, false, "Enable pT-dependent DCAxy cut: |DCAxy| < a + b/pT") - O2_DEFINE_CONFIGURABLE(dcaXyConst, float, 0.004f, "Constant term 'a' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm)") - O2_DEFINE_CONFIGURABLE(dcaXySlope, float, 0.013f, "Slope term 'b' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm x GeV/c)") - O2_DEFINE_CONFIGURABLE(itsnclusters, int, 5, "minimum number of ITS clusters for tracks") - O2_DEFINE_CONFIGURABLE(tpcncrossedrows, int, 80, "minimum number of TPC crossed rows for tracks") - O2_DEFINE_CONFIGURABLE(tpcnclusters, int, 50, "minimum number of TPC clusters found") - O2_DEFINE_CONFIGURABLE(chi2pertpccluster, float, 2.5, "maximum Chi2 / cluster for the TPC track segment") - O2_DEFINE_CONFIGURABLE(chi2peritscluster, float, 36, "maximum Chi2 / cluster for the ITS track segment") + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 7.0f, "Accepted z-vertex range"); + O2_DEFINE_CONFIGURABLE(cfgCutPt, float, 0.5f, "Minimal pT for tracks"); + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks"); + O2_DEFINE_CONFIGURABLE(cfgCutMCPt, float, 0.5f, "Minimal pT for particles"); + O2_DEFINE_CONFIGURABLE(cfgCutMCEta, float, 0.8f, "Eta range for particles"); + O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)"); + O2_DEFINE_CONFIGURABLE(cfgTrigger, int, 7, "Trigger choice: (0 = none, 7 = sel7, 8 = sel8, 9 = sel8 + kNoSameBunchPileup + kIsGoodZvtxFT0vsPV, 10 = sel8 before April, 2024, 11 = sel8 for MC, 12 = sel8 with low occupancy cut, 13 = sel8 + kNoSameBunchPileup + kIsGoodITSLayersAll -- for OO/NeNe, 14 = fHighTrackMult (pp), 15 = fHighMultFv0 (pp), 16 = fHighTrackMult || fHighMultFv0 (pp))"); + O2_DEFINE_CONFIGURABLE(cfgMinOcc, int, 0, "minimum occupancy selection"); + O2_DEFINE_CONFIGURABLE(cfgMaxOcc, int, 3000, "maximum occupancy selection"); + O2_DEFINE_CONFIGURABLE(cfgCollisionFlags, uint16_t, aod::collision::CollisionFlagsRun2::Run2VertexerTracks, "Request collision flags if non-zero (0 = off, 1 = Run2VertexerTracks)"); + O2_DEFINE_CONFIGURABLE(cfgTransientTables, bool, false, "Output transient tables for collision and track IDs to enable successive filtering tasks"); + O2_DEFINE_CONFIGURABLE(cfgTrackSelection, int, 0, "Type of track selection (0 = Run 2/3 without systematics | 1 = Run 3 with systematics | 2 = Run 3 with proton pid selection)"); + O2_DEFINE_CONFIGURABLE(cfgMinMultiplicity, float, -1, "Minimum multiplicity considered for filtering (if value positive)"); + O2_DEFINE_CONFIGURABLE(cfgMcSpecialPDGs, std::vector, std::vector{}, "Special MC PDG codes to include in the MC primary particle output (additional to charged particles). Empty = charged particles only."); // needed for some neutral particles + O2_DEFINE_CONFIGURABLE(nsigmaCutTPCProton, float, 3, "proton nsigma TPC"); + O2_DEFINE_CONFIGURABLE(nsigmaCutTOFProton, float, 3, "proton nsigma TOF"); + O2_DEFINE_CONFIGURABLE(ITSProtonselection, bool, false, "flag for ITS proton nsigma selection"); + O2_DEFINE_CONFIGURABLE(nsigmaCutITSProton, float, 3, "proton nsigma ITS"); + O2_DEFINE_CONFIGURABLE(dcaxymax, float, 999.f, "maximum dcaxy of tracks"); + O2_DEFINE_CONFIGURABLE(dcazmax, float, 999.f, "maximum dcaz of tracks"); + O2_DEFINE_CONFIGURABLE(enablePtDepDCAxy, bool, false, "Enable pT-dependent DCAxy cut: |DCAxy| < a + b/pT"); + O2_DEFINE_CONFIGURABLE(dcaXyConst, float, 0.004f, "Constant term 'a' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm)"); + O2_DEFINE_CONFIGURABLE(dcaXySlope, float, 0.013f, "Slope term 'b' for pT-dependent DCAxy cut: |DCAxy| < a + b/pT (cm x GeV/c)"); + O2_DEFINE_CONFIGURABLE(itsnclusters, int, 5, "minimum number of ITS clusters for tracks"); + O2_DEFINE_CONFIGURABLE(tpcncrossedrows, int, 80, "minimum number of TPC crossed rows for tracks"); + O2_DEFINE_CONFIGURABLE(tpcnclusters, int, 50, "minimum number of TPC clusters found"); + O2_DEFINE_CONFIGURABLE(chi2pertpccluster, float, 2.5, "maximum Chi2 / cluster for the TPC track segment"); + O2_DEFINE_CONFIGURABLE(chi2peritscluster, float, 36, "maximum Chi2 / cluster for the ITS track segment"); O2_DEFINE_CONFIGURABLE(cfgEstimatorBitMask, uint16_t, 0, "BitMask for multiplicity estimators to be included in the CFMultSet tables."); + O2_DEFINE_CONFIGURABLE(cfgEfficiencyMultiplicity, std::string, "", "Multiplicity efficiency (RecoAll / MC): CCDB path or local ROOT file with a 4D ccdb_object (eta, pT, multiplicity, z-vtx); empty disables CFCollisionsExtra output"); + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = CCDB efficiency, 1 = local ROOT efficiency"); + O2_DEFINE_CONFIGURABLE(cfgMultiplicityTrackBitMask, uint16_t, 0, "Required track-type bits for corrected multiplicity; match cfgTrackBitMask used to produce the efficiency (0 = all stored tracks)"); + // Filters and input definitions Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter collisionVertexTypeFilter = (cfgCollisionFlags == 0) || ((aod::collision::flags & cfgCollisionFlags) == cfgCollisionFlags); @@ -114,17 +132,22 @@ struct FilterCF { Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < cfgCutVertex; OutputObj yields{TH3F("yields", "centrality vs pT vs eta", 100, 0, 100, 40, 0, 20, 100, -2, 2)}; - OutputObj etaphi{TH3F("etaphi", "centrality vs eta vs phi", 100, 0, 100, 100, -2, 2, 200, 0, 2 * M_PI)}; + OutputObj etaphi{TH3F("etaphi", "centrality vs eta vs phi", 100, 0, 100, 100, -2, 2, 200, 0, o2::constants::math::TwoPI)}; HistogramRegistry registrytrackQA{"TrackQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + Zorro zorro; + int zorroRun = 0; + Produces outputCollisions; + Produces outputCollisionsExtra; Produces outputTracks; Produces outputMcCollisionLabels; Produces outputTrackLabels; Produces outputMcCollisions; + Produces outputMcCollisionExtras; Produces outputMcParticles; Produces outputCollRefs; @@ -132,14 +155,56 @@ struct FilterCF { Produces outputMcParticleRefs; Produces outputMultSets; - std::vector multiplicities{}; + std::vector multiplicities; + + // Own local histograms independently of their input file. CCDB owns its objects. + std::unique_ptr localMultiplicityEfficiency; + THn* mEfficiency = nullptr; + static constexpr int MultiplicityEfficiencyDimensions = 4; // persistent caches std::vector mcReconstructedCache; std::vector mcParticleLabelsCache; - void init(InitContext&) + void init(InitContext& initContext) { + if (!cfgEfficiencyMultiplicity.value.empty()) { + bool processTracksEnabled = false; + if (!o2::common::core::getTaskOptionValue(initContext, "multiplicity-selector", "processTracks", processTracksEnabled, false)) { + LOGF(fatal, "Could not determine whether MultiplicitySelector::processTracks is enabled"); + } + if (!processTracksEnabled) { + LOGF(fatal, "Efficiency-corrected multiplicity requires MultiplicitySelector::processTracks"); + } + if (cfgLocalEfficiency != 0 && cfgLocalEfficiency != 1) { + LOGF(fatal, "cfgLocalEfficiency must be 0 (CCDB) or 1 (local ROOT file)"); + } + if (cfgMultiplicityTrackBitMask > std::numeric_limits::max()) { + LOGF(fatal, "cfgMultiplicityTrackBitMask must fit the 8-bit track type"); + } + if (cfgLocalEfficiency == 1) { + std::unique_ptr file(TFile::Open(cfgEfficiencyMultiplicity.value.c_str(), "READ")); + if (!file) { + LOGF(fatal, "Could not open multiplicity efficiency file %s", cfgEfficiencyMultiplicity.value.c_str()); + return; + } + if (file->IsZombie()) { + LOGF(fatal, "Multiplicity efficiency file %s is invalid", cfgEfficiencyMultiplicity.value.c_str()); + return; + } + auto* efficiency = dynamic_cast(file->Get("ccdb_object")); + if (!efficiency || efficiency->GetNdimensions() != MultiplicityEfficiencyDimensions) { + LOGF(fatal, "Multiplicity efficiency from %s must be a 4D THn with axes (eta, pT, multiplicity, z-vtx)", cfgEfficiencyMultiplicity.value.c_str()); + return; + } + localMultiplicityEfficiency.reset(dynamic_cast(efficiency->Clone())); + } + } + if ((cfgTrigger >= 14 && cfgTrigger <= 16) || (!cfgEfficiencyMultiplicity.value.empty() && cfgLocalEfficiency == 0)) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } if (doprocessTrackQA) { registrytrackQA.add("zvtx", "Z Vertex position; posz (cm); Events", HistType::kTH1F, {{100, -12, 12}}); registrytrackQA.add("eta", "eta distribution; eta; arb. units", HistType::kTH1F, {{100, -2, 2}}); @@ -155,33 +220,60 @@ struct FilterCF { } template - bool keepCollision(TCollision& collision) + bool keepCollision(const TCollision& collision) { bool isMultSelected = false; - if (collision.multiplicity() >= cfgMinMultiplicity) + if (collision.multiplicity() >= cfgMinMultiplicity) { isMultSelected = true; - + } if (cfgTrigger == 0) { return true; - } else if (cfgTrigger == 7) { + } + if (cfgTrigger == 7) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.alias_bit(kINT7) && collision.sel7(); - } else if (cfgTrigger == 8) { + } + if (cfgTrigger == 8) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.sel8(); - } else if (cfgTrigger == 9) { // relevant only for Pb-Pb + } + if (cfgTrigger == 9) { // relevant only for Pb-Pb; o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); - } else if (cfgTrigger == 10) { // TVX trigger only (sel8 selection before April, 2024) + } + if (cfgTrigger == 10) { // TVX trigger only (sel8 selection before April, 2024); o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.selection_bit(aod::evsel::kIsTriggerTVX); - } else if (cfgTrigger == 11) { // sel8 selection for MC + } + if (cfgTrigger == 11) { // sel8 selection for MC; o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.selection_bit(aod::evsel::kIsTriggerTVX) && collision.selection_bit(aod::evsel::kNoTimeFrameBorder); - } else if (cfgTrigger == 12) { // relevant only for Pb-Pb with occupancy cuts and rejection of the collisions which have other events nearby + } + if (cfgTrigger == 12) { // relevant only for Pb-Pb with occupancy cuts and rejection of nearby collisions; o2-linter: disable=magic-number (documented legacy trigger-selection code) int occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy >= cfgMinOcc && occupancy < cfgMaxOcc) + if (occupancy >= cfgMinOcc && occupancy < cfgMaxOcc) { return isMultSelected && collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) && collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); - else - return false; - } else if (cfgTrigger == 13) { // relevant for pO/OO/NeNe --recommended by Physics Board on 27.01.2026 + } + return false; + } + if (cfgTrigger == 13) { // relevant for pO/OO/NeNe, recommended by Physics Board on 27.01.2026; o2-linter: disable=magic-number (documented legacy trigger-selection code) return isMultSelected && collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); } + + // Zorro-based trigger selection + if (cfgTrigger >= 14 && cfgTrigger <= 16) { // o2-linter: disable=magic-number (documented legacy trigger-selection code) + auto bc = collision.template bc_as(); + if (zorroRun != bc.runNumber()) { + std::string zorroMask; + if (cfgTrigger == 14) { // High-multiplicity pp trigger based on tracks (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighTrackMult"; + } + if (cfgTrigger == 15) { // High-multiplicity pp trigger based on FV0 amplitude (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighMultFv0"; + } + if (cfgTrigger == 16) { // High-multiplicity pp trigger based on tracks or FV0 amplitude (PWGMM/multFilter.cxx); o2-linter: disable=magic-number (documented legacy trigger-selection code) + zorroMask = "fHighTrackMult,fHighMultFv0"; + } + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask); + zorroRun = bc.runNumber(); + } + return isMultSelected && zorro.isSelected(bc.globalBC(), 0); // Set BC threshold to zero to not admit nearby low-multiplicity collisions + } return false; } @@ -192,18 +284,18 @@ struct FilterCF { { o2::aod::ITSResponse itsResponse; - if (ITSProtonselection && candidate.pt() <= 0.6 && !(itsResponse.nSigmaITS(candidate) > nsigmaCutITSProton)) { + if (ITSProtonselection && candidate.pt() <= 0.6 && !(itsResponse.nSigmaITS(candidate) > nsigmaCutITSProton)) { // o2-linter: disable=magic-number (established proton PID momentum boundary) return false; } - if (ITSProtonselection && candidate.pt() > 0.6 && candidate.pt() <= 0.8 && !(itsResponse.nSigmaITS(candidate) > nsigmaCutITSProton)) { + if (ITSProtonselection && candidate.pt() > 0.6 && candidate.pt() <= 0.8 && !(itsResponse.nSigmaITS(candidate) > nsigmaCutITSProton)) { // o2-linter: disable=magic-number (established proton PID momentum boundaries) return false; } if (candidate.hasTOF()) { - if (candidate.pt() < 0.7 && std::abs(candidate.tpcNSigmaPr()) < nsigmaCutTPCProton) { + if (candidate.pt() < 0.7 && std::abs(candidate.tpcNSigmaPr()) < nsigmaCutTPCProton) { // o2-linter: disable=magic-number (established proton PID momentum boundary) return true; } - if (candidate.p() >= 0.7 && std::abs(candidate.tpcNSigmaPr()) < nsigmaCutTPCProton && std::abs(candidate.tofNSigmaPr()) < nsigmaCutTOFProton) { + if (candidate.p() >= 0.7 && std::abs(candidate.tpcNSigmaPr()) < nsigmaCutTPCProton && std::abs(candidate.tofNSigmaPr()) < nsigmaCutTOFProton) { // o2-linter: disable=magic-number (established proton PID momentum boundary) return true; } } else { @@ -232,11 +324,13 @@ struct FilterCF { if (cfgTrackSelection == 0) { if (track.isGlobalTrack()) { return 1; - } else if (track.isGlobalTrackSDD()) { + } + if (track.isGlobalTrackSDD()) { return 2; } return 0; - } else if (cfgTrackSelection == 1) { + } + if (cfgTrackSelection == 1) { uint8_t trackType = 0; if (track.isGlobalTrack()) { trackType |= kTrackSelected; @@ -257,7 +351,8 @@ struct FilterCF { } } return trackType; - } else if (cfgTrackSelection == 2) { + } + if (cfgTrackSelection == 2) { // o2-linter: disable=magic-number (documented track-selection mode) uint8_t trackType = 0; if constexpr (HasProtonPID::value) { if (track.isGlobalTrack() && (track.itsNCls() >= itsnclusters) && (track.tpcNClsCrossedRows() >= tpcncrossedrows) && selectionPIDProton(track)) { @@ -279,9 +374,72 @@ struct FilterCF { return dcaXyConst + dcaXySlope / pt; // a + b/pT } + THn* loadMultiplicityEfficiency(uint64_t timestamp) + { + if (cfgLocalEfficiency == 1) { + return localMultiplicityEfficiency.get(); + } + if (!mEfficiency || !ccdb->isCachedObjectValid(cfgEfficiencyMultiplicity.value, timestamp)) { + mEfficiency = ccdb->getForTimeStamp>(cfgEfficiencyMultiplicity.value, timestamp); + } + if (!mEfficiency || mEfficiency->GetNdimensions() != MultiplicityEfficiencyDimensions) { + LOGF(fatal, "Multiplicity efficiency from %s must be a 4D THn with axes (eta, pT, multiplicity, z-vtx)", cfgEfficiencyMultiplicity.value.c_str()); + } + return mEfficiency; + } + + template + float getCorrectedMultiplicity(const TCollision& collision, const TTracks& tracks, uint64_t timestamp) + { + auto* efficiency = loadMultiplicityEfficiency(timestamp); + double correctedMultiplicity = 0.; + size_t skippedTracks = 0; + for (const auto& track : tracks) { + // Match the tracks written by the corresponding data/MC producer path. + if (!isTrackSelected(track, true)) { + continue; + } + // The map contains RecoAll / MC, not inverse-efficiency weights. + // Keep the original estimator as the map coordinate, including for centrality. + const std::array values{track.eta(), track.pt(), collision.multiplicity(), collision.posZ()}; + const double eff = efficiency->GetBinContent(efficiency->GetBin(values.data())); + if (!std::isfinite(eff) || eff <= 0.) { + ++skippedTracks; + continue; + } + correctedMultiplicity += 1. / eff; + } + if (cfgVerbosity > 0 && skippedTracks > 0) { + LOGF(warning, "Skipped %zu tracks with invalid efficiency while correcting collision %lld", skippedTracks, static_cast(collision.globalIndex())); + } + if (!std::isfinite(correctedMultiplicity) || correctedMultiplicity > std::numeric_limits::max()) { + LOGF(fatal, "Corrected multiplicity cannot be represented as a float: %g", correctedMultiplicity); + } + return static_cast(correctedMultiplicity); + } + + template + bool isTrackSelected(const TTrack& track, bool checkTrackBitMask = false) + { + const float maxDCAxy = getMaxDCAxy(track.pt()); + if (std::abs(track.dcaXY()) > maxDCAxy || std::abs(track.dcaZ()) > dcazmax) { + return false; + } + if (checkTrackBitMask) { + const auto mask = static_cast(cfgMultiplicityTrackBitMask.value); + if (mask != 0 && (getTrackType(track) & mask) != mask) { + return false; + } + } + return true; + } + template using HasMultTables = decltype(std::declval().multNTracksPV()); + template + using HasNMPI = decltype(std::declval().nMPI()); + /// \brief Templetized process data for a given collision and its associated tracks /// \param collision The collision object containing information about the collision /// \param tracks The collection of tracks associated with the collision @@ -298,33 +456,42 @@ struct FilterCF { auto bc = collision.template bc_as(); outputCollisions(bc.runNumber(), collision.posZ(), collision.multiplicity(), bc.timestamp()); + if (!cfgEfficiencyMultiplicity.value.empty()) { + outputCollisionsExtra(getCorrectedMultiplicity(collision, tracks, bc.timestamp())); + } if constexpr (std::experimental::is_detected::value) { multiplicities.clear(); - if (cfgEstimatorBitMask & aod::cfmultset::CentFT0C) + if (cfgEstimatorBitMask & aod::cfmultset::CentFT0C) { multiplicities.push_back(collision.centFT0C()); - if (cfgEstimatorBitMask & aod::cfmultset::MultFV0A) + } + if (cfgEstimatorBitMask & aod::cfmultset::MultFV0A) { multiplicities.push_back(collision.multFV0A()); - if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksPV) + } + if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksPV) { multiplicities.push_back(collision.multNTracksPV()); - if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksGlobal) + } + if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksGlobal) { multiplicities.push_back(collision.multNTracksGlobal()); - if (cfgEstimatorBitMask & aod::cfmultset::CentFT0M) + } + if (cfgEstimatorBitMask & aod::cfmultset::CentFT0M) { multiplicities.push_back(collision.centFT0M()); + } outputMultSets(multiplicities); } - if (cfgTransientTables) + if (cfgTransientTables) { outputCollRefs(collision.globalIndex()); - for (auto& track : tracks) { - float maxDCAxy = getMaxDCAxy(track.pt()); - if ((std::abs(track.dcaXY()) > maxDCAxy) || (std::abs(track.dcaZ()) > dcazmax)) { + } + for (const auto& track : tracks) { + if (!isTrackSelected(track)) { continue; } outputTracks(outputCollisions.lastIndex(), track.pt(), track.eta(), track.phi(), track.sign(), getTrackType(track)); - if (cfgTransientTables) + if (cfgTransientTables) { outputTrackRefs(collision.globalIndex(), track.globalIndex()); + } yields->Fill(collision.multiplicity(), track.pt(), track.eta()); etaphi->Fill(collision.multiplicity(), track.eta(), track.phi()); @@ -349,7 +516,7 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processDataMults, "Process data with multiplicity sets", false); - void processTrackQA(soa::Filtered>::iterator const& collision, soa::Filtered> const& tracks) + void processTrackQA(soa::Filtered>::iterator const& collision, aod::BCsWithTimestamps const&, soa::Filtered> const& tracks) { if (!keepCollision(collision)) { return; @@ -359,8 +526,7 @@ struct FilterCF { if (!track.isGlobalTrack()) { continue; // trackQA for global tracks only } - float maxDCAxy = getMaxDCAxy(track.pt()); - if ((std::abs(track.dcaXY()) > maxDCAxy) || (std::abs(track.dcaZ()) > dcazmax)) { + if (!isTrackSelected(track)) { continue; } registrytrackQA.fill(HIST("eta"), track.eta()); @@ -370,10 +536,12 @@ struct FilterCF { registrytrackQA.fill(HIST("tpcxrows"), track.tpcNClsCrossedRows()); registrytrackQA.fill(HIST("tpcnclst"), track.tpcNClsFound()); registrytrackQA.fill(HIST("itsnclst"), track.itsNCls()); - if (track.tpcNClsFound() > 0) + if (track.tpcNClsFound() > 0) { registrytrackQA.fill(HIST("chi2tpc"), track.tpcChi2NCl()); - if (track.itsNCls() > 0) + } + if (track.itsNCls() > 0) { registrytrackQA.fill(HIST("chi2its"), track.itsChi2NCl()); + } } } PROCESS_SWITCH(FilterCF, processTrackQA, "Process track QA", false); @@ -385,8 +553,8 @@ struct FilterCF { /// event selections /// \param tracks The collection of tracks, filtered by selection criteria /// \param bcs The collection of bunch crossings with timestamps - template - void processMCT(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, + template + void processMCT(MCs const& mcCollisions, aod::McParticles const& allParticles, C1 const& allCollisions, T1 const& tracks, aod::BCsWithTimestamps const&) @@ -400,8 +568,11 @@ struct FilterCF { mcParticleLabelsCache.push_back(-1); } + std::vector bestRecoCollisionIndices(mcCollisions.size(), -1); + std::vector bestRecoCollisionNContrib(mcCollisions.size(), -1); + // PASS 1 on collisions: check which particles are kept - for (auto& collision : allCollisions) { + for (const auto& collision : allCollisions) { auto groupedTracks = tracks.sliceBy(perCollision, collision.globalIndex()); if (cfgVerbosity > 0) { LOGF(info, "processMC: Tracks for collision %d: %d | Vertex: %.1f (%d) | INT7: %d", collision.globalIndex(), groupedTracks.size(), collision.posZ(), collision.flags(), collision.sel7()); @@ -411,14 +582,20 @@ struct FilterCF { continue; } - for (auto& track : groupedTracks) { + const auto mcCollisionId = collision.mcCollisionId(); + if (mcCollisionId >= 0 && mcCollisionId < static_cast(bestRecoCollisionIndices.size()) && collision.numContrib() > bestRecoCollisionNContrib[mcCollisionId]) { + bestRecoCollisionNContrib[mcCollisionId] = collision.numContrib(); + bestRecoCollisionIndices[mcCollisionId] = collision.globalIndex(); + } + + for (const auto& track : groupedTracks) { if (track.has_mcParticle()) { mcReconstructedCache[track.mcParticleId()] = true; } } } - for (auto& mcCollision : mcCollisions) { + for (const auto& mcCollision : mcCollisions) { auto particles = allParticles.sliceBy(perMcCollision, mcCollision.globalIndex()); if (cfgVerbosity > 0) { @@ -427,11 +604,11 @@ struct FilterCF { // Store selected MC particles and MC collisions int multiplicity = 0; - for (auto& particle : particles) { + for (const auto& particle : particles) { int8_t sign = 0; TParticlePDG* pdgparticle = pdg->GetParticle(particle.pdgCode()); if (pdgparticle != nullptr) { - sign = (pdgparticle->Charge() > 0) ? 1.0 : ((pdgparticle->Charge() < 0) ? -1.0 : 0.0); + sign = (pdgparticle->Charge() > 0) ? 1 : ((pdgparticle->Charge() < 0) ? -1 : 0); } bool special = !cfgMcSpecialPDGs->empty() && std::find(cfgMcSpecialPDGs->begin(), cfgMcSpecialPDGs->end(), particle.pdgCode()) != cfgMcSpecialPDGs->end(); @@ -449,20 +626,24 @@ struct FilterCF { } // NOTE using "outputMcCollisions.lastIndex()+1" here to allow filling of outputMcCollisions *after* the loop - outputMcParticles(outputMcCollisions.lastIndex() + 1, truncateFloatFraction(particle.pt(), FLOAT_PRECISION), truncateFloatFraction(particle.eta(), FLOAT_PRECISION), - truncateFloatFraction(particle.phi(), FLOAT_PRECISION), sign, particle.pdgCode(), flags); - if (cfgTransientTables) + outputMcParticles(outputMcCollisions.lastIndex() + 1, truncateFloatFraction(particle.pt(), kFloatPrecision), truncateFloatFraction(particle.eta(), kFloatPrecision), + truncateFloatFraction(particle.phi(), kFloatPrecision), sign, particle.pdgCode(), flags); + if (cfgTransientTables) { outputMcParticleRefs(outputMcCollisions.lastIndex() + 1, particle.globalIndex()); + } // relabeling array mcParticleLabelsCache[particle.globalIndex()] = outputMcParticles.lastIndex(); } } outputMcCollisions(mcCollision.posZ(), multiplicity); + if constexpr (std::experimental::is_detected::value) { + outputMcCollisionExtras(mcCollision.nMPI()); + } } // PASS 2 on collisions: store collisions and tracks - for (auto& collision : allCollisions) { + for (const auto& collision : allCollisions) { auto groupedTracks = tracks.sliceBy(perCollision, collision.globalIndex()); if (cfgVerbosity > 0) { LOGF(info, "processMC: Tracks for collision %d: %d | Vertex: %.1f (%d) | INT7: %d", collision.globalIndex(), groupedTracks.size(), collision.posZ(), collision.flags(), collision.sel7()); @@ -475,27 +656,39 @@ struct FilterCF { auto bc = collision.template bc_as(); // NOTE works only when we store all MC collisions (as we do here) outputCollisions(bc.runNumber(), collision.posZ(), collision.multiplicity(), bc.timestamp()); - outputMcCollisionLabels(collision.mcCollisionId()); + if (!cfgEfficiencyMultiplicity.value.empty()) { + outputCollisionsExtra(getCorrectedMultiplicity(collision, groupedTracks, bc.timestamp())); + } + + const auto mcCollisionId = collision.mcCollisionId(); + const bool bestRecoCollision = mcCollisionId >= 0 && mcCollisionId < static_cast(bestRecoCollisionIndices.size()) && bestRecoCollisionIndices[mcCollisionId] == collision.globalIndex(); + outputMcCollisionLabels(mcCollisionId, bestRecoCollision); if constexpr (std::experimental::is_detected::value) { multiplicities.clear(); - if (cfgEstimatorBitMask & aod::cfmultset::CentFT0C) + if (cfgEstimatorBitMask & aod::cfmultset::CentFT0C) { multiplicities.push_back(collision.centFT0C()); - if (cfgEstimatorBitMask & aod::cfmultset::MultFV0A) + } + if (cfgEstimatorBitMask & aod::cfmultset::MultFV0A) { multiplicities.push_back(collision.multFV0A()); - if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksPV) + } + if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksPV) { multiplicities.push_back(collision.multNTracksPV()); - if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksGlobal) + } + if (cfgEstimatorBitMask & aod::cfmultset::MultNTracksGlobal) { multiplicities.push_back(collision.multNTracksGlobal()); - if (cfgEstimatorBitMask & aod::cfmultset::CentFT0M) + } + if (cfgEstimatorBitMask & aod::cfmultset::CentFT0M) { multiplicities.push_back(collision.centFT0M()); + } outputMultSets(multiplicities); } - if (cfgTransientTables) + if (cfgTransientTables) { outputCollRefs(collision.globalIndex()); + } - for (auto& track : groupedTracks) { + for (const auto& track : groupedTracks) { int mcParticleId = track.mcParticleId(); if (mcParticleId >= 0) { mcParticleId = mcParticleLabelsCache[track.mcParticleId()]; @@ -505,8 +698,9 @@ struct FilterCF { } outputTracks(outputCollisions.lastIndex(), truncateFloatFraction(track.pt()), truncateFloatFraction(track.eta()), truncateFloatFraction(track.phi()), track.sign(), getTrackType(track)); outputTrackLabels(mcParticleId); - if (cfgTransientTables) + if (cfgTransientTables) { outputTrackRefs(collision.globalIndex(), track.globalIndex()); + } yields->Fill(collision.multiplicity(), track.pt(), track.eta()); etaphi->Fill(collision.multiplicity(), track.eta(), track.phi()); @@ -517,15 +711,25 @@ struct FilterCF { // NOTE not filtering collisions here because in that case there can be tracks referring to MC particles which are not part of the selected MC collisions Preslice perMcCollision = aod::mcparticle::mcCollisionId; Preslice perCollision = aod::track::collisionId; + using McCollisionsWithHepMC = soa::Join; void processMC(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, - soa::Filtered> const& tracks, + soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) { processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); } PROCESS_SWITCH(FilterCF, processMC, "Process MC", false); + void processMCWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCWithHepMC, "Process MC with HepMC cross-section (nMPI)", false); + // NOTE not filtering collisions here because in that case there can be tracks referring to MC particles which are not part of the selected MC collisions void processMCPid(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, @@ -536,9 +740,18 @@ struct FilterCF { } PROCESS_SWITCH(FilterCF, processMCPid, "Process MC with PID", false); + void processMCPidWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCPidWithHepMC, "Process MC with PID and HepMC cross-section (nMPI)", false); + void processMCMults(aod::McCollisions const& mcCollisions, aod::McParticles const& allParticles, soa::Join const& allCollisions, - soa::Filtered> const& tracks, + soa::Filtered> const& tracks, aod::BCsWithTimestamps const& bcs) { processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); @@ -546,31 +759,60 @@ struct FilterCF { PROCESS_SWITCH(FilterCF, processMCMults, "Process MC with multiplicity sets", false); - void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& particles) + void processMCMultsWithHepMC(McCollisionsWithHepMC const& mcCollisions, aod::McParticles const& allParticles, + soa::Join const& allCollisions, + soa::Filtered> const& tracks, + aod::BCsWithTimestamps const& bcs) + { + processMCT(mcCollisions, allParticles, allCollisions, tracks, bcs); + } + PROCESS_SWITCH(FilterCF, processMCMultsWithHepMC, "Process MC with multiplicity sets and HepMC cross-section (nMPI)", false); + + /// \brief Templated MCGen processing; stores nMPI only when the MC collision carries the HepMC cross-section table + template + void processMCGenT(const TMC& mcCollision, aod::McParticles const& particles) { float multiplicity = 0.0f; - for (auto& particle : particles) { - if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) > cfgCutMCEta || particle.pt() < cfgCutMCPt) + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || std::abs(particle.eta()) > cfgCutMCEta || particle.pt() < cfgCutMCPt) { continue; + } int8_t sign = 0; - if (TParticlePDG* pdgparticle = pdg->GetParticle(particle.pdgCode())) - if ((sign = pdgparticle->Charge()) != 0) + if (TParticlePDG* pdgparticle = pdg->GetParticle(particle.pdgCode())) { + sign = static_cast(pdgparticle->Charge()); + if (sign != 0) { multiplicity += 1.0f; - outputMcParticles(outputMcCollisions.lastIndex() + 1, truncateFloatFraction(particle.pt(), FLOAT_PRECISION), - truncateFloatFraction(particle.eta(), FLOAT_PRECISION), - truncateFloatFraction(particle.phi(), FLOAT_PRECISION), + } + } + outputMcParticles(outputMcCollisions.lastIndex() + 1, truncateFloatFraction(particle.pt(), kFloatPrecision), + truncateFloatFraction(particle.eta(), kFloatPrecision), + truncateFloatFraction(particle.phi(), kFloatPrecision), sign, particle.pdgCode(), particle.flags()); } outputMcCollisions(mcCollision.posZ(), multiplicity); + if constexpr (std::experimental::is_detected::value) { + outputMcCollisionExtras(mcCollision.nMPI()); + } + } + + void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& particles) + { + processMCGenT(mcCollision, particles); } PROCESS_SWITCH(FilterCF, processMCGen, "Process MCGen", false); + + void processMCGenWithHepMC(McCollisionsWithHepMC::iterator const& mcCollision, aod::McParticles const& particles) + { + processMCGenT(mcCollision, particles); + } + PROCESS_SWITCH(FilterCF, processMCGenWithHepMC, "Process MCGen with HepMC cross-section (nMPI)", false); }; struct MultiplicitySelector { Produces output; - O2_DEFINE_CONFIGURABLE(cfgCutPt, float, 0.5f, "Minimal pT for tracks") - O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPt, float, 0.5f, "Minimal pT for tracks"); + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks"); Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPt); Filter trackSelection = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true); @@ -619,7 +861,7 @@ struct MultiplicitySelector { void processFT0M(aod::CentFT0Ms const& centralities) { - for (auto& c : centralities) { + for (const auto& c : centralities) { output(c.centFT0M()); } } @@ -627,7 +869,7 @@ struct MultiplicitySelector { void processFT0C(aod::CentFT0Cs const& centralities) { - for (auto& c : centralities) { + for (const auto& c : centralities) { output(c.centFT0C()); } } @@ -635,7 +877,7 @@ struct MultiplicitySelector { void processFT0CVariant1(aod::CentFT0CVariant1s const& centralities) { - for (auto& c : centralities) { + for (const auto& c : centralities) { output(c.centFT0CVariant1()); } } @@ -643,7 +885,7 @@ struct MultiplicitySelector { void processFT0CVariant2(aod::CentFT0CVariant2s const& centralities) { - for (auto& c : centralities) { + for (const auto& c : centralities) { output(c.centFT0CVariant2()); } } @@ -651,7 +893,7 @@ struct MultiplicitySelector { void processFT0A(aod::CentFT0As const& centralities) { - for (auto& c : centralities) { + for (const auto& c : centralities) { output(c.centFT0A()); } } @@ -659,7 +901,7 @@ struct MultiplicitySelector { void processCentNGlobal(aod::CentNGlobals const& centralities) { - for (auto& c : centralities) { + for (const auto& c : centralities) { output(c.centNGlobal()); } } @@ -667,7 +909,7 @@ struct MultiplicitySelector { void processRun2V0M(aod::CentRun2V0Ms const& centralities) { - for (auto& c : centralities) { + for (const auto& c : centralities) { output(c.centRun2V0M()); } } diff --git a/PWGCF/Tasks/correlations.cxx b/PWGCF/Tasks/correlations.cxx index 279765c3ae1..0a5c1d4ed6b 100644 --- a/PWGCF/Tasks/correlations.cxx +++ b/PWGCF/Tasks/correlations.cxx @@ -13,6 +13,8 @@ /// \brief task for the correlation calculations with CF-filtered tracks for O2 analysis /// \author Jan Fiete Grosse-Oetringhaus , Jasper Parkkila +// o2-linter: disable=name/workflow-file (preserve the established workflow filename and executable name) + #include "PWGCF/Core/CorrelationContainer.h" #include "PWGCF/Core/PairCuts.h" #include "PWGCF/DataModel/CorrelationsDerived.h" @@ -26,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +48,6 @@ #include #include #include -#include #include #include @@ -59,9 +61,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -70,7 +74,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace constants::math; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; // NOLINT(bugprone-macro-parentheses) // NOTE This is a nice idea but will again make it impossible to use subwagon configurations... // namespace o2::aod @@ -83,7 +87,7 @@ using namespace constants::math; // cfcorreff::Correction); // } // namespace o2::aod -static constexpr float kCfgPairCutDefaults[1][5] = {{-1, -1, -1, -1, -1}}; +static constexpr std::array, 1> kCfgPairCutDefaults = {{{-1, -1, -1, -1, -1}}}; // o2-linter: disable=name/constexpr-constant (preserve the established identifier) struct CorrelationTask { SliceCache cache; @@ -102,19 +106,23 @@ struct CorrelationTask { O2_DEFINE_CONFIGURABLE(cfgTwoTrackCut, float, -1, "Two track cut: -1 = off; >0 otherwise distance value (suggested: 0.02)"); O2_DEFINE_CONFIGURABLE(cfgTwoTrackCutMinRadius, float, 0.8f, "Two track cut: radius in m from which two track cuts are applied"); O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = OFF and 1 = ON for local efficiency"); - O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "choice to drop step RECO if efficiency correction is used") + O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "choice to drop step RECO if efficiency correction is used"); O2_DEFINE_CONFIGURABLE(cfgCentBinsForMC, int, 0, "0 = OFF and 1 = ON for data like multiplicity/centrality bins for MC steps"); + O2_DEFINE_CONFIGURABLE(cfgRequireRecoCollision, int, 1, "0 = all generated collisions; 1 = only generated collisions with exactly 1 reconstructed collision; 2 = select reconstructed collision with largest number of contributors per generated collision") O2_DEFINE_CONFIGURABLE(cfgTrackBitMask, uint16_t, 0, "BitMask for track selection systematics; refer to the enum TrackSelectionCuts in filtering task"); O2_DEFINE_CONFIGURABLE(cfgMultCorrelationsMask, uint16_t, 0, "Selection bitmask for the multiplicity correlations. This should match the filter selection cfgEstimatorBitMask.") O2_DEFINE_CONFIGURABLE(cfgMultCutFormula, std::string, "", "Multiplicity correlations cut formula. A result greater than zero results in accepted event. Parameters: [cFT0C] FT0C centrality, [mFV0A] V0A multiplicity, [mGlob] global track multiplicity, [mPV] PV track multiplicity, [cFT0M] FT0M centrality") // Suggested values: Photon: 0.004; K0 and Lambda: 0.005 - Configurable> cfgPairCut{"cfgPairCut", {kCfgPairCutDefaults[0], 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; + Configurable> cfgPairCut{"cfgPairCut", {kCfgPairCutDefaults.front().data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path to efficiency object for trigger particles") O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path to efficiency object for associated particles") + O2_DEFINE_CONFIGURABLE(cfgUseRun3MagField, bool, false, "false: Run 2 GRPObject; true: Run 3 GRPMagField") + O2_DEFINE_CONFIGURABLE(cfgMultWeight, std::string, "", "Multiplicity/Centrality weight") O2_DEFINE_CONFIGURABLE(cfgNoMixedEvents, int, 5, "Number of mixed events per event") + O2_DEFINE_CONFIGURABLE(cfgRejectMixedPhiProngEvents, bool, true, "Reject associated hadrons from either mixed-phi prong event") O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)") @@ -126,6 +134,10 @@ struct CorrelationTask { O2_DEFINE_CONFIGURABLE(cfgPtDepMLbkg, std::vector, {}, "pT interval for ML training") O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLbkgSel, std::vector, {}, "Bkg ML selection") O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLpromptSel, std::vector, {}, "Prompt ML selection") + O2_DEFINE_CONFIGURABLE(cfgPtCentDepMLnonpromptSel, std::vector, {}, "Non-prompt ML selection (maximum cut on the non-prompt score)") + O2_DEFINE_CONFIGURABLE(cfgMcClassSel, int, -1, "MC class for 2-prong efficiency: -1=auto(promptSel empty?all:prompt), 0=all, 1=prompt, 2=non-prompt") + + std::unordered_map cfTrackToCFCollision; ConfigurableAxis axisVertex{"axisVertex", {7, -7, 7}, "vertex axis for histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; @@ -169,19 +181,22 @@ struct CorrelationTask { std::vector p2indexCache; std::unique_ptr multCutFormula; - std::array multCutFormulaParamIndex; + std::array multCutFormulaParamIndex{}; struct Config { bool mPairCuts = false; THn* mEfficiencyTrigger = nullptr; THn* mEfficiencyAssociated = nullptr; + TH1* mMultWeight = nullptr; bool efficiencyLoaded = false; } cfg; HistogramRegistry registry{"registry"}; PairCuts mPairCuts; - Service ccdb; + Service ccdb{}; + int mCachedRunNumber{-1}; // cached run number for magnetic field -- to avoid re-fetching the magnetic field for the same run, assuming that the magnetic field remains the same for the same run + int mCachedMagField{0}; // cached magnetic field --reduces number of calls to the CCDB using AodCollisions = soa::Filtered>; using AodTracks = soa::Filtered>; @@ -189,15 +204,35 @@ struct CorrelationTask { using DerivedCollisions = soa::Filtered; using DerivedTracks = soa::Filtered; + enum RecoCollisionSelection { + AllGeneratedCollisions = 0, + RequireOneRecoCollision, + RequireBestRecoCollision + }; + void init(o2::framework::InitContext&) { + if (cfgRequireRecoCollision < 0 || cfgRequireRecoCollision > RecoCollisionSelection::RequireBestRecoCollision) { + LOGF(fatal, "Unsupported cfgRequireRecoCollision=%d; use 0 (no reco. collision required), 1 (exactly one reco. collision required), 2 (at least one. reco, and best reco. collision selected)", cfgRequireRecoCollision.value); + } if (doprocessSame2ProngDerivedML || doprocessSame2Prong2ProngML || doprocessMixed2ProngDerivedML || doprocessMixed2Prong2ProngML || doprocessMCEfficiency2ProngML || doprocessMCReflection2ProngML) { - if (cfgPtDepMLbkg->empty() || cfgPtCentDepMLbkgSel->empty()) + if (cfgPtDepMLbkg->empty() || cfgPtCentDepMLbkgSel->empty()) { LOGF(fatal, "cfgPtDepMLbkg or cfgPtCentDepMLbkgSel can not be empty when ML 2-prong selections are used."); - if (cfgPtDepMLbkg->size() != cfgPtCentDepMLbkgSel->size()) + } + if (cfgPtDepMLbkg->size() != cfgPtCentDepMLbkgSel->size()) { LOGF(fatal, "cfgPtDepMLbkg and cfgPtCentDepMLbkgSel must be same size."); - if (!cfgPtCentDepMLpromptSel->empty() && cfgPtCentDepMLpromptSel->size() != cfgPtDepMLbkg->size()) + } + if (!cfgPtCentDepMLpromptSel->empty() && cfgPtCentDepMLpromptSel->size() != cfgPtDepMLbkg->size()) { LOGF(fatal, "cfgPtDepMLbkg and cfgPtCentDepMLpromptSel must be same size."); + } + if (!cfgPtCentDepMLnonpromptSel->empty() && cfgPtCentDepMLnonpromptSel->size() != cfgPtDepMLbkg->size()) + LOGF(fatal, "cfgPtDepMLbkg and cfgPtCentDepMLnonpromptSel must be same size."); + if (cfgPtCentDepMLpromptSel->size() > 0) + LOGF(info, "Prompt selection scores in use."); + else + LOGF(info, "Using inclusive selections."); + if (cfgPtCentDepMLnonpromptSel->size() > 0) + LOGF(info, "Non-prompt selection scores in use."); } registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{100, 0, 100, "/multiplicity/centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"}}}); registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{100, 0, 100, "multiplicity/centrality"}, {100, -2, 2, "#eta"}, {200, 0, o2::constants::math::TwoPI, "#varphi"}}}); @@ -207,6 +242,7 @@ struct CorrelationTask { const AxisSpec& a = AxisSpec(axisInvMass); AxisSpec axisSpecMass = {1000, a.binEdges[0], a.binEdges[a.getNbins()]}; registry.add("invMass", "2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}}); + registry.add("invMassCorrected", "2-prong invariant mass with efficiency correction (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisPtTrigger, axisMultiplicity, axisVertex}}); if (doprocessSame2Prong2Prong || doprocessSame2Prong2ProngML) { registry.add("invMassTwoPart", "2D 2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisMultiplicity}}); registry.add("invMassTwoPartDPhi", "2D 2-prong invariant mass (GeV/c^2)", {HistType::kTHnSparseF, {axisSpecMass, axisSpecMass, axisPtTrigger, axisPtAssoc, axisDeltaPhi}}); @@ -220,19 +256,25 @@ struct CorrelationTask { registry.add("invMassReflected", "2-prong invariant mass (GeV/c^2)", {HistType::kTH3F, {axisSpecMass, axisPtTrigger, axisMultiplicity}}); } if (doprocessSameDerivedMultSet) { - if (cfgMultCorrelationsMask == 0) + if (cfgMultCorrelationsMask == 0) { LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when MultSet process functions are in use."); + } std::vector multAxes; - if (cfgMultCorrelationsMask & aod::cfmultset::CentFT0C) + if (cfgMultCorrelationsMask & aod::cfmultset::CentFT0C) { multAxes.emplace_back(axisMultCorrCent, "FT0C centrality"); - if (cfgMultCorrelationsMask & aod::cfmultset::MultFV0A) + } + if (cfgMultCorrelationsMask & aod::cfmultset::MultFV0A) { multAxes.emplace_back(axisMultCorrV0, "V0A multiplicity"); - if (cfgMultCorrelationsMask & aod::cfmultset::MultNTracksPV) + } + if (cfgMultCorrelationsMask & aod::cfmultset::MultNTracksPV) { multAxes.emplace_back(axisMultCorrMult, "Nch PV"); - if (cfgMultCorrelationsMask & aod::cfmultset::MultNTracksGlobal) + } + if (cfgMultCorrelationsMask & aod::cfmultset::MultNTracksGlobal) { multAxes.emplace_back(axisMultCorrMult, "Nch Global"); - if (cfgMultCorrelationsMask & aod::cfmultset::CentFT0M) + } + if (cfgMultCorrelationsMask & aod::cfmultset::CentFT0M) { multAxes.emplace_back(axisMultCorrCent, "FT0M centrality"); + } registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); } registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{1000, 0, 100, "/multiplicity/centrality"}}}); @@ -298,10 +340,12 @@ struct CorrelationTask { userAxis.emplace_back(axisInvMass, "m (GeV/c^2)"); userMixingAxis.emplace_back(axisInvMass, "m (GeV/c^2)"); } - if (doprocessSame2Prong2Prong || doprocessSame2Prong2ProngML) + if (doprocessSame2Prong2Prong || doprocessSame2Prong2ProngML) { userAxis.emplace_back(axisInvMass, "m (GeV/c^2)"); - if (doprocessMixed2Prong2Prong || doprocessMixed2Prong2ProngML) + } + if (doprocessMixed2Prong2Prong || doprocessMixed2Prong2ProngML) { userMixingAxis.emplace_back(axisInvMass, "m (GeV/c^2)"); + } same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userMixingAxis)); @@ -309,12 +353,14 @@ struct CorrelationTask { same->setTrackEtaCut(cfgCutEta); mixed->setTrackEtaCut(cfgCutEta); - if (!cfgEfficiencyAssociated.value.empty()) + if (!cfgEfficiencyAssociated.value.empty()) { efficiencyAssociatedCache.reserve(512); + } if (doprocessMCEfficiency2Prong || doprocessMCEfficiency2ProngML || doprocessMCReflection2ProngML) { p2indexCache.reserve(16); - if (cfgMcTriggerPDGs->empty()) + if (cfgMcTriggerPDGs->empty()) { LOGF(fatal, "At least one PDG code in {} is to be selected to process 2-prong efficiency.", cfgMcTriggerPDGs.name); + } } // o2-ccdb-upload -p Users/jgrosseo/correlations/LHC15o -f /tmp/correction_2011_global.root -k correction @@ -327,21 +373,32 @@ struct CorrelationTask { ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time } - int getMagneticField(uint64_t timestamp) + int getMagneticField(int runNumber, uint64_t timestamp) { - // TODO done only once (and not per run). Will be replaced by CCDBConfigurable - static o2::parameters::GRPObject* grpo = nullptr; - // static o2::parameters::GRPMagField* grpo = nullptr; - if (grpo == nullptr) { - grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); - // grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + static constexpr const char* kGRPPathRun2 = "GLO/GRP/GRP"; // fixed path for now, can be made into a config + static constexpr const char* kGRPPathRun3 = "GLO/Config/GRPMagField"; // fixed path for now, can be made into a config + + if (runNumber == mCachedRunNumber) { + return mCachedMagField; + } + + if (cfgUseRun3MagField) { + auto* grpmag = ccdb->getForTimeStamp(kGRPPathRun3, timestamp); + if (grpmag == nullptr) { + LOGF(fatal, "Run 3 GRPMagField not found at %s for run %d timestamp %llu", kGRPPathRun3, runNumber, timestamp); + } + mCachedMagField = grpmag->getNominalL3Field(); + } else { + auto* grpo = ccdb->getForTimeStamp(kGRPPathRun2, timestamp); if (grpo == nullptr) { - LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); - return 0; + LOGF(fatal, "Run 2 GRPObject not found at %s for run %d timestamp %llu", kGRPPathRun2, runNumber, timestamp); } - LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + mCachedMagField = grpo->getNominalL3Field(); } - return grpo->getNominalL3Field(); + + mCachedRunNumber = runNumber; + LOGF(info, "Run %d: magnetic field %d kG", runNumber, mCachedMagField); + return mCachedMagField; } template @@ -352,12 +409,16 @@ struct CorrelationTask { return (track.decay() != aod::cf2prongtrack::D0ToPiK || (track.mlProbD0()[0] < cfgPtCentDepMLbkgSel->at(idx) && // not background (cfgPtCentDepMLpromptSel->empty() || - track.mlProbD0()[1] > cfgPtCentDepMLpromptSel->at(idx)))) && // prompt + track.mlProbD0()[1] > cfgPtCentDepMLpromptSel->at(idx)) && // prompt + (cfgPtCentDepMLnonpromptSel->empty() || + track.mlProbD0()[2] < cfgPtCentDepMLnonpromptSel->at(idx)))) && // not non-prompt ((track.decay() != aod::cf2prongtrack::D0barToKPi && track.decay() != aod::cf2prongtrack::D0barToKPiExclusive) || (track.mlProbD0bar()[0] < cfgPtCentDepMLbkgSel->at(idx) && // not background (cfgPtCentDepMLpromptSel->empty() || - track.mlProbD0bar()[1] > cfgPtCentDepMLpromptSel->at(idx)))); // prompt + track.mlProbD0bar()[1] > cfgPtCentDepMLpromptSel->at(idx)) && // prompt + (cfgPtCentDepMLnonpromptSel->empty() || + track.mlProbD0bar()[2] < cfgPtCentDepMLnonpromptSel->at(idx)))); // not non-prompt } template @@ -368,8 +429,9 @@ struct CorrelationTask { { registry.fill(HIST("multiplicity"), multiplicity); if constexpr (std::experimental::is_detected::value) { - if (std::popcount(cfgMultCorrelationsMask.value) != static_cast(collision.multiplicities().size())) + if (std::popcount(cfgMultCorrelationsMask.value) != static_cast(collision.multiplicities().size())) { LOGF(fatal, "Multiplicity selections (cfgMultCorrelationsMask = 0x%x) do not match the size of the table column (%ld). The histogram filling relies on the preservation of order.", cfgMultCorrelationsMask.value, collision.multiplicities().size()); + } // need to convert to vec of doubles since THnSparse has no way to fill vec of floats directly std::vector v(collision.multiplicities().begin(), collision.multiplicities().end()); registry.get(HIST("multCorrelations")).get()->Fill(v.data()); @@ -384,27 +446,41 @@ struct CorrelationTask { using HasInvMass = decltype(std::declval().invMass()); template using HasPDGCode = decltype(std::declval().pdgCode()); + template + using HasTrackType = decltype(std::declval().trackType()); template void fillQA(const TCollision& collision, float multiplicity, float posZ, const TTracks1& tracks1, const TTracks2& tracks2) { for (const auto& track1 : tracks1) { if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { - if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track1.decay()))) == 0u) + if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track1.decay()))) == 0u) { continue; + } if constexpr (std::experimental::is_detected::value) { - if (!passMLScore(track1)) + if (!passMLScore(track1)) { continue; + } + } + float triggerWeight = 1.0f; + if (cfg.mMultWeight) { + triggerWeight *= cfg.mMultWeight->GetBinContent(cfg.mMultWeight->FindBin(multiplicity)); + } + if (cfg.mEfficiencyTrigger) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, collision.posZ()); } registry.fill(HIST("invMass"), track1.invMass(), track1.pt(), multiplicity, posZ); + registry.fill(HIST("invMassCorrected"), track1.invMass(), track1.pt(), multiplicity, posZ, triggerWeight); for (const auto& track2 : tracks2) { if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { if (doprocessSame2Prong2Prong || doprocessMixed2Prong2Prong || doprocessSame2Prong2ProngML || doprocessMixed2Prong2ProngML) { - if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track1.decay()))) == 0u) + if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track1.decay()))) == 0u) { continue; + } if constexpr (std::experimental::is_detected::value) { - if (!passMLScore(track2)) + if (!passMLScore(track2)) { continue; + } } if constexpr (std::experimental::is_detected::value) { @@ -433,12 +509,14 @@ struct CorrelationTask { } } // no shared prong for two mothers - if (cfgCorrelationMethod == 1 && track1.decay() != track2.decay()) + if (cfgCorrelationMethod == 1 && track1.decay() != track2.decay()) { continue; - if (cfgCorrelationMethod == 2 && track1.decay() == track2.decay()) + } + if (cfgCorrelationMethod == 2 && track1.decay() == track2.decay()) { // o2-linter: disable=magic-number (value is the established ddbar correlation-method mode) continue; + } registry.fill(HIST("invMassTwoPart"), track1.invMass(), track2.invMass(), track1.pt(), track2.pt(), multiplicity); - registry.fill(HIST("invMassTwoPartDPhi"), track1.invMass(), track2.invMass(), track1.pt(), track2.pt(), TVector2::Phi_0_2pi(track1.phi() - track2.phi() + TMath::Pi() / 2.0) - TMath::Pi() / 2.0); + registry.fill(HIST("invMassTwoPartDPhi"), track1.invMass(), track2.invMass(), track1.pt(), track2.pt(), TVector2::Phi_0_2pi(track1.phi() - track2.phi() + PIHalf) - PIHalf); if (std::abs(track1.phi() - track2.phi()) < constants::math::PI * 0.5) { registry.fill(HIST("invMassTwoPartDEta"), track1.invMass(), track2.invMass(), track1.pt(), track2.pt(), track1.eta() - track2.eta()); } @@ -447,8 +525,9 @@ struct CorrelationTask { } } if constexpr (std::experimental::is_detected::value) { - if (!cfgMcTriggerPDGs->empty() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track1.pdgCode()) == cfgMcTriggerPDGs->end()) + if (!cfgMcTriggerPDGs->empty() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track1.pdgCode()) == cfgMcTriggerPDGs->end()) { continue; + } } registry.fill(HIST("yieldsTrigger"), multiplicity, track1.pt(), track1.eta()); registry.fill(HIST("etaphiTrigger"), multiplicity, track1.eta(), track1.phi()); @@ -457,7 +536,7 @@ struct CorrelationTask { } template - bool fillCollisionAOD(TTarget target, TCollision collision, float multiplicity) + bool fillCollisionAOD(TTarget target, const TCollision& collision, float multiplicity) { target->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); @@ -511,15 +590,19 @@ struct CorrelationTask { using HasPartDaugh0Id = decltype(std::declval().cfParticleDaugh0Id()); template using HasPartDaugh1Id = decltype(std::declval().cfParticleDaugh1Id()); + template + using HasCFCollisionId = decltype(std::declval().cfCollisionId()); template bool passOutlier(CollType const& collision) { - if (cfgMultCutFormula.value.empty()) + if (cfgMultCutFormula.value.empty()) { return true; + } for (uint i = 0; i < aod::cfmultset::NMultiplicityEstimators; ++i) { - if ((cfgMultCorrelationsMask.value & (1u << i)) == 0 || multCutFormulaParamIndex[i] == ~0u) + if ((cfgMultCorrelationsMask.value & (1u << i)) == 0 || multCutFormulaParamIndex[i] == ~0u) { continue; + } auto estIndex = std::popcount(cfgMultCorrelationsMask.value & ((1u << i) - 1)); multCutFormula->SetParameter(multCutFormulaParamIndex[i], collision.multiplicities()[estIndex]); } @@ -529,8 +612,9 @@ struct CorrelationTask { template std::tuple getV0Rapidity(const T& track) { - if constexpr (!std::experimental::is_detected::value) + if constexpr (!std::experimental::is_detected::value) { return {false, 0.0f}; // no decay type, return dummy rapidity + } const auto decayType = track.decay(); float mass = 0.f; @@ -554,7 +638,7 @@ struct CorrelationTask { const float p2 = px * px + py * py + pz * pz; - const float E = std::sqrt(p2 + mass * mass); + const float E = std::sqrt(p2 + mass * mass); // o2-linter: disable=name/function-variable (E is the conventional symbol for particle energy) return {true, 0.5f * std::log((E + pz) / (E - pz))}; } @@ -572,6 +656,9 @@ struct CorrelationTask { } } + static std::set> duplicates; // workaround for old derived datasets involving 2prongs when cfgDecayParticleMask = 0 + duplicates.clear(); + for (const auto& track1 : tracks1) { // LOGF(info, "Track %f | %f | %f %d %d", track1.eta(), track1.phi(), track1.pt(), track1.isGlobalTrack(), track1.isGlobalTrackSDD()); @@ -581,62 +668,81 @@ struct CorrelationTask { } } + if constexpr (std::experimental::is_detected::value) { + if (cfgDecayParticleMask == 0) { + auto a = std::make_tuple(track1.cfTrackProng0Id(), track1.cfTrackProng1Id()); // workaround allows for use of D0bar in old derived datasets + if (duplicates.contains(a)) + continue; + duplicates.insert(a); + } + } + // sign check and PDG code special cases if constexpr (std::experimental::is_detected::value) { // If the MC trigger particle is on the trigger PDG code list, we will accept them regardless of their charge. if (!cfgMcTriggerPDGs->empty()) { - if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track1.pdgCode()) == cfgMcTriggerPDGs->end()) + if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track1.pdgCode()) == cfgMcTriggerPDGs->end()) { continue; + } } else { // otherwise check the sign against the configuration if (cfgTriggerCharge != 0) { - if (cfgTriggerCharge * track1.sign() < 0) + if (cfgTriggerCharge * track1.sign() < 0) { continue; + } } else if (track1.sign() == 0) { continue; // reject neutral MC particles } } } else if constexpr (std::experimental::is_detected::value) { // Check reco objects that have the sign attribute. There are no neutrals to deal with. - if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) { continue; + } } if constexpr (std::experimental::is_detected::value) { - if (((track1.mcDecay() != aod::cf2prongtrack::D0ToPiK) && (track1.mcDecay() != aod::cf2prongtrack::D0barToKPiExclusive)) || (!cfgPtCentDepMLpromptSel->empty() && (track1.decay() & aod::cf2prongmcpart::Prompt) == 0)) + if (((track1.mcDecay() != aod::cf2prongtrack::D0ToPiK) && (track1.mcDecay() != aod::cf2prongtrack::D0barToKPiExclusive)) || (!cfgPtCentDepMLpromptSel->empty() && (track1.decay() & aod::cf2prongmcpart::Prompt) == 0)) { continue; + } } else if constexpr (std::experimental::is_detected::value) { if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track1.decay()))) == 0u) { continue; // skip particles that do not match the decay mask } if (cfgV0RapidityMax > 0) { auto [t, y] = getV0Rapidity(track1); - if (t && std::abs(y) > cfgV0RapidityMax) + if (t && std::abs(y) > cfgV0RapidityMax) { continue; // V0s are not allowed to be outside the rapidity range + } registry.fill(HIST("yvspt"), y, track1.pt()); } } if constexpr (std::experimental::is_detected::value) { - if (track1.cfParticleDaugh0Id() < 0 && track1.cfParticleDaugh1Id() < 0) + if (track1.cfParticleDaugh0Id() < 0 && track1.cfParticleDaugh1Id() < 0) { continue; // these we could not match + } } if constexpr (std::experimental::is_detected::value) { - if (!passMLScore(track1)) + if (!passMLScore(track1)) { continue; + } } // ML selection float triggerWeight = eventWeight; if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mMultWeight) { + triggerWeight *= cfg.mMultWeight->GetBinContent(cfg.mMultWeight->FindBin(multiplicity)); + } if (cfg.mEfficiencyTrigger) { triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ); } } if (cfgMassAxis) { - if constexpr (std::experimental::is_detected::value) + if constexpr (std::experimental::is_detected::value) { target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, track1.invMass(), triggerWeight); - else if constexpr (std::experimental::is_detected::value) { + } else if constexpr (std::experimental::is_detected::value) { // TParticlePDG *p = pdg->GetParticle(track1.pdgCode()); // target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, p->Mass(), triggerWeight); target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, 1.8, triggerWeight); @@ -654,27 +760,55 @@ struct CorrelationTask { continue; } } + + if ((&target == &mixed) && doprocessMixed2ProngDerivedMixedPhi && cfgRejectMixedPhiProngEvents) { + if constexpr (std::experimental::is_detected::value && + std::experimental::is_detected::value && + std::experimental::is_detected::value && + std::experimental::is_detected::value) { + + if (track1.decay() == aod::cf2prongtrack::PhiToKKPID3Mixed) { + auto pr0 = cfTrackToCFCollision.find(track1.cfTrackProng0Id()); + auto pr1 = cfTrackToCFCollision.find(track1.cfTrackProng1Id()); + + if (pr0 != cfTrackToCFCollision.end() && + pr1 != cfTrackToCFCollision.end()) { + const auto assocColl = track2.cfCollisionId(); + + if (assocColl == pr0->second || assocColl == pr1->second) { + continue; + } + } + } + } + } + if constexpr (std::experimental::is_detected::value) { // skip those that are specifically chosen to be triggers - if (!cfgMcTriggerPDGs->empty() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track2.pdgCode()) != cfgMcTriggerPDGs->end()) + if (!cfgMcTriggerPDGs->empty() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track2.pdgCode()) != cfgMcTriggerPDGs->end()) { continue; // TODO: fix cases like MC D0-D0 + } } // Daughter track and particle checks if constexpr (std::experimental::is_detected::value) { - if (track2.globalIndex() == track1.cfTrackProng0Id()) // do not correlate daughter tracks of the same event + if (track2.globalIndex() == track1.cfTrackProng0Id()) { // do not correlate daughter tracks of the same event continue; + } } if constexpr (std::experimental::is_detected::value) { - if (track2.globalIndex() == track1.cfTrackProng1Id()) // do not correlate daughter tracks of the same event + if (track2.globalIndex() == track1.cfTrackProng1Id()) { // do not correlate daughter tracks of the same event continue; + } } if constexpr (std::experimental::is_detected::value) { - if (track2.globalIndex() == track1.cfParticleDaugh0Id()) // do not correlate daughter particles of the same event + if (track2.globalIndex() == track1.cfParticleDaugh0Id()) { // do not correlate daughter particles of the same event continue; + } } if constexpr (std::experimental::is_detected::value) { - if (track2.globalIndex() == track1.cfParticleDaugh1Id()) // do not correlate daughter particles of the same event + if (track2.globalIndex() == track1.cfParticleDaugh1Id()) { // do not correlate daughter particles of the same event continue; + } } if constexpr (step <= CorrelationContainer::kCFStepTracked && !std::experimental::is_detected::value) { @@ -685,8 +819,10 @@ struct CorrelationTask { // If decay attributes are found for the second track/particle, we assume 2p-2p correlation if constexpr (std::experimental::is_detected::value) { - if ((((track2.mcDecay()) != aod::cf2prongtrack::D0ToPiK) && ((track2.mcDecay()) != aod::cf2prongtrack::D0barToKPiExclusive)) || (!cfgPtCentDepMLpromptSel->empty() && (track2.decay() & aod::cf2prongmcpart::Prompt) == 0)) + if ((((track2.mcDecay()) != aod::cf2prongtrack::D0ToPiK) && ((track2.mcDecay()) != aod::cf2prongtrack::D0barToKPiExclusive)) || (!cfgPtCentDepMLpromptSel->empty() && (track2.decay() & aod::cf2prongmcpart::Prompt) == 0)) { + continue; + } } else if constexpr (std::experimental::is_detected::value) { if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track2.decay()))) == 0u) { continue; // skip particles that do not match the decay mask @@ -702,10 +838,12 @@ struct CorrelationTask { } if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { - if (cfgCorrelationMethod == 1 && track1.decay() != track2.decay()) + if (cfgCorrelationMethod == 1 && track1.decay() != track2.decay()) { continue; - if (cfgCorrelationMethod == 2 && track1.decay() == track2.decay()) + } + if (cfgCorrelationMethod == 2 && track1.decay() == track2.decay()) { // o2-linter: disable=magic-number (value is the established ddbar correlation-method mode) continue; + } } if constexpr (std::experimental::is_detected::value) { @@ -742,8 +880,9 @@ struct CorrelationTask { if constexpr (std::experimental::is_detected::value) { // TODO: support for MC D0-D0 case if (cfgAssociatedCharge != 0) { - if (cfgAssociatedCharge * track2.sign() < 0) + if (cfgAssociatedCharge * track2.sign() < 0) { continue; + } } else if (track2.sign() == 0) { // mc particles come in neutrals, need to check explicitly continue; } @@ -778,21 +917,22 @@ struct CorrelationTask { float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); if constexpr (std::experimental::is_detected::value) { - if (!passMLScore(track2)) + if (!passMLScore(track2)) { continue; + } } // ML selection // last param is the weight - if (cfgMassAxis && (doprocessSame2Prong2Prong || doprocessMixed2Prong2Prong || doprocessSame2Prong2ProngML || doprocessMixed2Prong2ProngML) && !(doprocessSame2ProngDerived || doprocessSame2ProngDerivedML || doprocessMixed2ProngDerived || doprocessMixed2ProngDerivedML)) { - if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) + if (cfgMassAxis && (doprocessSame2Prong2Prong || doprocessMixed2Prong2Prong || doprocessSame2Prong2ProngML || doprocessMixed2Prong2ProngML) && !(doprocessSame2ProngDerived || doprocessSame2ProngDerivedML || doprocessMixed2ProngDerived || doprocessMixed2ProngDerivedML || doprocessMixed2ProngDerivedMixedPhi)) { + if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { target->getPairHist()->Fill(step, track1.eta() - track2.eta(), track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, track2.invMass(), track1.invMass(), associatedWeight); - else + } else { LOGF(fatal, "Can not fill mass axis without invMass column. \n no mass for two particles"); + } } else if (cfgMassAxis) { - if constexpr (std::experimental::is_detected::value) + if constexpr (std::experimental::is_detected::value) { target->getPairHist()->Fill(step, track1.eta() - track2.eta(), track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, track1.invMass(), associatedWeight); - else if constexpr (std::experimental::is_detected::value) { - // TParticlePDG *p = pdg->GetParticle(track1.pdgCode()); //TODO: get the mass for the PDG properly + } else if constexpr (std::experimental::is_detected::value) { target->getPairHist()->Fill(step, track1.eta() - track2.eta(), track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, 1.8, associatedWeight); // p->Mass() } else { LOGF(fatal, "Can not fill mass axis without invMass column. Disable cfgMassAxis."); @@ -819,7 +959,7 @@ struct CorrelationTask { if (cfg.mEfficiencyTrigger == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyTrigger.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for trigger particles from %s (%p)", cfgEfficiencyTrigger.value.c_str(), (void*)cfg.mEfficiencyTrigger); + LOGF(info, "Loaded efficiency histogram for trigger particles from %s (%p)", cfgEfficiencyTrigger.value.c_str(), static_cast(cfg.mEfficiencyTrigger)); } if (cfgEfficiencyAssociated.value.empty() == false) { if (cfgLocalEfficiency > 0) { @@ -831,7 +971,12 @@ struct CorrelationTask { if (cfg.mEfficiencyAssociated == nullptr) { LOGF(fatal, "Could not load efficiency histogram for associated particles from %s", cfgEfficiencyAssociated.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), (void*)cfg.mEfficiencyAssociated); + LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), static_cast(cfg.mEfficiencyAssociated)); + } + if (cfgMultWeight.value.empty() == false) { + TFile* pf = TFile::Open(cfgMultWeight.value.c_str(), "READ"); + cfg.mMultWeight = reinterpret_cast(pf->Get("hWeight")); + LOGF(info, "Loaded multiplicity weight histogram for associated particles from %s", cfgMultWeight.value.c_str()); } cfg.efficiencyLoaded = true; } @@ -866,7 +1011,7 @@ struct CorrelationTask { } registry.fill(HIST("eventcount_same"), -2); fillQA(collision, multiplicity, tracks); - fillCorrelations(same, tracks, tracks, multiplicity, collision.posZ(), getMagneticField(bc.timestamp()), 1.0f); + fillCorrelations(same, tracks, tracks, multiplicity, collision.posZ(), getMagneticField(bc.runNumber(), bc.timestamp()), 1.0f); } PROCESS_SWITCH(CorrelationTask, processSameAOD, "Process same event on AOD", true); @@ -874,7 +1019,7 @@ struct CorrelationTask { void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), collision.multiplicity()); } @@ -883,16 +1028,17 @@ struct CorrelationTask { const auto multiplicity = collision.multiplicity(); int field = 0; if (cfgTwoTrackCut > 0) { - field = getMagneticField(collision.timestamp()); + field = getMagneticField(collision.runNumber(), collision.timestamp()); } int bin = configurableBinningDerived.getBin({collision.posZ(), collision.multiplicity()}); registry.fill(HIST("eventcount_same"), bin); registry.fill(HIST("trackcount_same"), bin, tracks1.size()); - if constexpr (std::experimental::is_detected::value) + if constexpr (std::experimental::is_detected::value) { fillQA(collision, multiplicity, collision.posZ(), tracks1, tracks2); - else + } else { fillQA(collision, multiplicity, tracks1); + } const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); const bool fillReco = !(cfgDropStepRECO && hasEfficiency); @@ -901,6 +1047,7 @@ struct CorrelationTask { same->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f); } + if (hasEfficiency) { same->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f); @@ -915,8 +1062,9 @@ struct CorrelationTask { void processSameDerivedMultSet(soa::Filtered>::iterator const& collision, soa::Filtered const& tracks) { - if (!passOutlier(collision)) + if (!passOutlier(collision)) { return; + } processSameDerivedT(collision, tracks, tracks); } PROCESS_SWITCH(CorrelationTask, processSameDerivedMultSet, "Process same event on derived data with multiplicity sets", false); @@ -951,7 +1099,7 @@ struct CorrelationTask { // NOTE legacy function for O2 integration tests. Full version needs derived data // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 - BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{configurableBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -983,7 +1131,7 @@ struct CorrelationTask { // LOGF(info, "Tracks: %d and %d entries", tracks1.size(), tracks2.size()); - fillCorrelations(mixed, tracks1, tracks2, collision1.centRun2V0M(), collision1.posZ(), getMagneticField(bc.timestamp()), 1.0f / it.currentWindowNeighbours()); + fillCorrelations(mixed, tracks1, tracks2, collision1.centRun2V0M(), collision1.posZ(), getMagneticField(bc.runNumber(), bc.timestamp()), 1.0f / it.currentWindowNeighbours()); } } PROCESS_SWITCH(CorrelationTask, processMixedAOD, "Process mixed events on AOD", false); @@ -994,8 +1142,9 @@ struct CorrelationTask { auto getMultiplicity = [this](auto& col) { if constexpr (std::experimental::is_detected::value) { - if (!passOutlier(col)) + if (!passOutlier(col)) { return -1.0f; + } } else { (void)this; // fix compile error on unused 'this' capture } @@ -1003,7 +1152,7 @@ struct CorrelationTask { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1017,7 +1166,7 @@ struct CorrelationTask { float eventWeight = 1.0f / it.currentWindowNeighbours(); int field = 0; if (cfgTwoTrackCut > 0) { - field = getMagneticField(collision1.timestamp()); + field = getMagneticField(collision1.runNumber(), collision1.timestamp()); } if (cfgVerbosity > 0) { @@ -1071,6 +1220,21 @@ struct CorrelationTask { processMixedDerivedT(collisions, p2tracks, tracks); } PROCESS_SWITCH(CorrelationTask, processMixed2ProngDerived, "Process mixed events on derived data", false); + void processMixed2ProngDerivedMixedPhi(DerivedCollisions const& collisions, + DerivedTracks const& tracks, + soa::Filtered const& p2tracks) + { + cfTrackToCFCollision.clear(); + cfTrackToCFCollision.reserve(tracks.size()); + + for (const auto& trk : tracks) { + cfTrackToCFCollision.emplace(trk.globalIndex(), trk.cfCollisionId()); + } + + processMixedDerivedT(collisions, p2tracks, tracks); + } + + PROCESS_SWITCH(CorrelationTask, processMixed2ProngDerivedMixedPhi, "Process mixed events for mixed-phi triggers", false); void processMixed2ProngDerivedML(DerivedCollisions const& collisions, DerivedTracks const& tracks, soa::Filtered> const& p2tracks) { @@ -1154,10 +1318,11 @@ struct CorrelationTask { case -2212: return 2; } - if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), pdgCode) != cfgMcTriggerPDGs->end()) + if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), pdgCode) != cfgMcTriggerPDGs->end()) { return 4; // NOTE - if changed, the number in processMCEfficiency2Prong needs to be changed too since we skip the getSpecies call - else // The efficiency histogram is hardcoded to contain 5 species. Anything special will have the 4th slot. + } else { // The efficiency histogram is hardcoded to contain 5 species. Anything special will have the 4th slot. return 3; + } } // NOTE SmallGroups includes soa::Filtered always @@ -1168,13 +1333,30 @@ struct CorrelationTask { LOGF(info, "MC collision at vtx-z = %f with %d mc particles and %d reconstructed collisions", mcCollision.posZ(), mcParticles.size(), collisions.size()); } + // Select reconstructed collisions as specified by cfgRequireRecoCollision auto multiplicity = mcCollision.multiplicity(); - if (cfgCentBinsForMC > 0) { + if (cfgRequireRecoCollision > 0) { if (collisions.size() == 0) { return; } - for (const auto& collision : collisions) { - multiplicity = collision.multiplicity(); + if (cfgRequireRecoCollision == RecoCollisionSelection::RequireOneRecoCollision) { // cfgRequireRecoCollision == 1 + if (collisions.size() != 1) { + return; + } + multiplicity = collisions.begin().multiplicity(); + } + if (cfgRequireRecoCollision == RecoCollisionSelection::RequireBestRecoCollision) { // cfgRequireRecoCollision == 2 + bool foundBestCollision = false; + for (const auto& collision : collisions) { + if (collision.bestRecoCollision()) { + multiplicity = collision.multiplicity(); + foundBestCollision = true; + break; + } + } + if (!foundBestCollision) { + return; + } } } // Primaries @@ -1183,7 +1365,11 @@ struct CorrelationTask { same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), multiplicity, mcCollision.posZ()); } } + const bool useBestCollision = cfgRequireRecoCollision == RecoCollisionSelection::AllGeneratedCollisions || cfgRequireRecoCollision == RecoCollisionSelection::RequireBestRecoCollision; // For cfgRequireRecoCollision == 0 still need to reject split vertices for (const auto& collision : collisions) { + if (useBestCollision && !collision.bestRecoCollision()) { + continue; + } auto groupedTracks = tracks.sliceBy(perCollision, collision.globalIndex()); if (cfgVerbosity > 0) { LOGF(info, " Reconstructed collision at vtx-z = %f", collision.posZ()); @@ -1191,12 +1377,14 @@ struct CorrelationTask { } for (const auto& track : groupedTracks) { - if (cfgTrackBitMask > 0 && (track.trackType() & (uint8_t)cfgTrackBitMask) != (uint8_t)cfgTrackBitMask) + if (cfgTrackBitMask > 0 && (track.trackType() & (uint8_t)cfgTrackBitMask) != (uint8_t)cfgTrackBitMask) { continue; + } if (track.has_cfMCParticle()) { const auto& mcParticle = track.cfMCParticle(); - if ((doprocessMCEfficiency2Prong || doprocessMCEfficiency2ProngML) && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) + if ((doprocessMCEfficiency2Prong || doprocessMCEfficiency2ProngML) && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) { continue; // properly booked by the 2Prong efficiency function, ignore here + } if (mcParticle.isPhysicalPrimary()) { same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), multiplicity, mcCollision.posZ()); } @@ -1212,7 +1400,7 @@ struct CorrelationTask { PROCESS_SWITCH(CorrelationTask, processMCEfficiency, "MC: Extract efficiencies", false); template - void processMCEfficiency2ProngT(soa::Filtered::iterator const& mcCollision, soa::Join const& mcParticles, soa::SmallGroups const& collisions, aod::CFTracksWithLabel const&, p2type const& p2tracks, Preslice& perCollision2Prong) + void processMCEfficiency2ProngT(soa::Filtered::iterator const& mcCollision, soa::Join const& mcParticles, soa::SmallGroups const& collisions, aod::CFTracksWithLabel const&, p2type const& p2tracks, Preslice& collision2ProngPreslice) { auto multiplicity = mcCollision.multiplicity(); if (cfgCentBinsForMC > 0) { @@ -1225,70 +1413,96 @@ struct CorrelationTask { } // Primaries p2indexCache.clear(); + std::set signalMC; // mc global index cache + std::set reflectionMC; // mc global index cache + // Resolve the MC class to use for the efficiency denominator/numerator. + // cfgMcClassSel: -1=auto (promptSel empty -> all, else prompt), 0=all, 1=prompt, 2=non-prompt. + int mcClass = cfgMcClassSel.value; + if (mcClass < 0) { + mcClass = cfgPtCentDepMLpromptSel->empty() ? 0 : 1; + } + auto mcClassSel = [&](const auto& mp) -> bool { + bool isP = (mp.decay() & aod::cf2prongmcpart::Prompt) != 0; + bool isNP = (mp.decay() & aod::cf2prongmcpart::NonPrompt) != 0; + return (mcClass == 0) || (mcClass == 1 && isP) || (mcClass == 2 && isNP); + }; for (const auto& mcParticle : mcParticles) { if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) { - if ((mcParticle.mcDecay() != aod::cf2prongtrack::D0ToPiK) && (mcParticle.mcDecay() != aod::cf2prongtrack::D0barToKPiExclusive)) + if ((mcParticle.mcDecay() != aod::cf2prongtrack::D0ToPiK) && (mcParticle.mcDecay() != aod::cf2prongtrack::D0barToKPiExclusive)) { continue; // wrong decay channel - if (mcParticle.cfParticleDaugh0Id() < 0 && mcParticle.cfParticleDaugh1Id() < 0) + } + if (mcParticle.cfParticleDaugh0Id() < 0 && mcParticle.cfParticleDaugh1Id() < 0) { continue; // daughters not found + } if constexpr (!reflectionSpec) { - if (cfgPtCentDepMLpromptSel->empty() || (mcParticle.decay() & aod::cf2prongmcpart::Prompt) != 0) + if (mcClassSel(mcParticle)) same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 4, multiplicity, mcCollision.posZ()); } p2indexCache.push_back(mcParticle.globalIndex()); } } for (const auto& collision : collisions) { - auto grouped2ProngTracks = p2tracks.sliceBy(perCollision2Prong, collision.globalIndex()); + auto grouped2ProngTracks = p2tracks.sliceBy(collision2ProngPreslice, collision.globalIndex()); for (const auto& p2track : grouped2ProngTracks) { if constexpr (!reflectionSpec) { - if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(p2track.decay()))) == 0u) + if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(p2track.decay()))) == 0u) { continue; + } } // Check if the mc particles of the prongs are found. if constexpr (std::experimental::is_detected::value) { - if (!passMLScore(p2track)) + if (!passMLScore(p2track)) { continue; + } } - if constexpr (!reflectionSpec) + if constexpr (!reflectionSpec) { same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, p2track.eta(), p2track.pt(), 4, multiplicity, mcCollision.posZ()); + } auto fillMC2p = [&](const aod::CFTracksWithLabel::iterator& p) -> bool { - if (!p.has_cfMCParticle()) + if (!p.has_cfMCParticle()) { return false; + } auto m = std::find_if(p2indexCache.begin(), p2indexCache.end(), [&](const auto& t) -> bool { const auto& mcParticle = mcParticles.iteratorAt(t - mcParticles.begin().globalIndex()); return (p.cfMCParticleId() == mcParticle.cfParticleDaugh0Id() || p.cfMCParticleId() == mcParticle.cfParticleDaugh1Id()); }); - if (m == p2indexCache.end()) + if (m == p2indexCache.end()) { return false; + } const auto& mcParticle = mcParticles.iteratorAt(*m - mcParticles.begin().globalIndex()); + // check MC class and return true if not the target class (wont be a fake) + if (!mcClassSel(mcParticle)) + return true; // a valid candidate but not the target class. Return true so we won't register a fake. if constexpr (!reflectionSpec) { - if (!cfgPtCentDepMLpromptSel->empty() && (mcParticle.decay() & aod::cf2prongmcpart::Prompt) == 0) - return true; // a valid candidate but not a prompt same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, mcParticle.eta(), mcParticle.pt(), 4, multiplicity, mcCollision.posZ()); } else { - if ((mcParticle.mcDecay() == aod::cf2prongtrack::D0barToKPiExclusive && (p2track.decay() == aod::cf2prongtrack::D0barToKPiExclusive || p2track.decay() == aod::cf2prongtrack::D0barToKPi)) || - (mcParticle.mcDecay() == aod::cf2prongtrack::D0ToPiK && p2track.decay() == aod::cf2prongtrack::D0ToPiK)) + if (!signalMC.contains(mcParticle.globalIndex()) && ((mcParticle.mcDecay() == aod::cf2prongtrack::D0barToKPiExclusive && (p2track.decay() == aod::cf2prongtrack::D0barToKPiExclusive || p2track.decay() == aod::cf2prongtrack::D0barToKPi)) || (mcParticle.mcDecay() == aod::cf2prongtrack::D0ToPiK && p2track.decay() == aod::cf2prongtrack::D0ToPiK))) { registry.fill(HIST("invMassSignal"), p2track.invMass(), p2track.pt(), multiplicity); - else // one particle may be filled into both histograms through duplicates + signalMC.insert(mcParticle.globalIndex()); + } else if (!reflectionMC.contains(mcParticle.globalIndex()) && !signalMC.contains(mcParticle.globalIndex()) && ((mcParticle.mcDecay() == aod::cf2prongtrack::D0barToKPiExclusive && p2track.decay() == aod::cf2prongtrack::D0ToPiK) || (mcParticle.mcDecay() == aod::cf2prongtrack::D0ToPiK && (p2track.decay() == aod::cf2prongtrack::D0barToKPi || p2track.decay() == aod::cf2prongtrack::D0barToKPiExclusive)))) { registry.fill(HIST("invMassReflected"), p2track.invMass(), p2track.pt(), multiplicity); + reflectionMC.insert(mcParticle.globalIndex()); + } } return true; }; if (p2track.has_cfTrackProng0()) { // - if (const auto& p0 = p2track.template cfTrackProng0_as(); fillMC2p(p0)) + if (const auto& p0 = p2track.template cfTrackProng0_as(); fillMC2p(p0)) { continue; + } } if (p2track.has_cfTrackProng1()) { - if (const auto& p1 = p2track.template cfTrackProng1_as(); fillMC2p(p1)) + if (const auto& p1 = p2track.template cfTrackProng1_as(); fillMC2p(p1)) { continue; + } } // fake track - if constexpr (!reflectionSpec) + if constexpr (!reflectionSpec) { same->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, p2track.eta(), p2track.pt(), 4, multiplicity, mcCollision.posZ()); + } } } } @@ -1331,10 +1545,11 @@ struct CorrelationTask { } if (!(doprocessSameDerived || doprocessSameDerivedMultSet || doprocessSame2ProngDerived || doprocessSame2ProngDerivedML || doprocessSame2Prong2Prong || doprocessSame2Prong2ProngML)) { - if constexpr (std::experimental::is_detected::value) + if constexpr (std::experimental::is_detected::value) { fillQA(mcCollision, multiplicity, mcCollision.posZ(), mcParticles1, mcParticles2); - else + } else { fillQA(mcCollision, multiplicity, mcParticles1); + } } same->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); @@ -1381,16 +1596,18 @@ struct CorrelationTask { bool useMCMultiplicity = (cfgCentBinsForMC == 0); auto getMultiplicity = [&collisions, &useMCMultiplicity, this](auto& col) { - if (useMCMultiplicity) + if (useMCMultiplicity) { return col.multiplicity(); + } auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, col.globalIndex()); - if (groupedCollisions.size() == 0) + if (groupedCollisions.size() == 0) { return -1.0f; + } return groupedCollisions.begin().multiplicity(); }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); diff --git a/PWGCF/Tasks/dptDptCorrelations.cxx b/PWGCF/Tasks/dptDptCorrelations.cxx index 0868ab02905..9f7713c221c 100644 --- a/PWGCF/Tasks/dptDptCorrelations.cxx +++ b/PWGCF/Tasks/dptDptCorrelations.cxx @@ -112,7 +112,7 @@ struct DptDptCorrelations { // The DptDptCorrelationsAnalysisTask output objects //============================================================================================ /* histograms */ - TH1F* fhVertexZA; //! fhN1VsPt{nch, nullptr}; //! fhN1VsPtEta{nch, nullptr}; //! fhN1VsEtaPhi{nch, nullptr}; //! kVERYSMALLETA) { + if (track1.eta() < -VERYSMALLETA || track1.eta() > VERYSMALLETA) { float expTmp = std::exp(-track1.eta()); tantheta1 = 2.0 * expTmp / (1.0 - expTmp * expTmp); } - float tantheta2 = kLARGETANTHETA; - if (track2.eta() < -kVERYSMALLETA || track2.eta() > kVERYSMALLETA) { + float tantheta2 = LARGETANTHETA; + if (track2.eta() < -VERYSMALLETA || track2.eta() > VERYSMALLETA) { float expTmp = std::exp(-track2.eta()); tantheta2 = 2.0 * expTmp / (1.0 - expTmp * expTmp); } @@ -352,7 +352,7 @@ struct DptDptCorrelations { ccdbstored = true; } - void storePtAverages(std::vector ptavgs) + void storePtAverages(const std::vector& ptavgs) { LOGF(info, "Stored pT average for %d track ids", ptavgs.size()); for (uint i = 0; i < ptavgs.size(); ++i) { @@ -905,9 +905,9 @@ struct DptDptCorrelations { float* fCentMultMax = nullptr; /* the data collecting engine instances */ - DataCollectingEngine** dataCE; - DataCollectingEngine** dataCEsmall; - DataCollectingEngine** dataCEME; + DataCollectingEngine** dataCE = nullptr; + DataCollectingEngine** dataCEsmall = nullptr; + DataCollectingEngine** dataCEME = nullptr; /* the input file structure from CCDB */ TList* ccdblst = nullptr; @@ -918,8 +918,8 @@ struct DptDptCorrelations { std::string cfgCCDBSuffix{""}; /* pair conversion suppression defaults */ - static constexpr float kCfgPairCutDefaults[1][5] = {{-1, -1, -1, -1, -1}}; - Configurable> cfgPairCut{"cfgPairCut", {kCfgPairCutDefaults[0], 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Conversion suppressions"}; + static constexpr float KCfgPairCutDefaults[1][5] = {{-1, -1, -1, -1, -1}}; + Configurable> cfgPairCut{"cfgPairCut", {KCfgPairCutDefaults[0], 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Conversion suppressions"}; /* two tracks cut */ Configurable cfgTwoTrackCut{"cfgTwoTrackCut", -1, "Two-tracks cut: -1 = off; >0 otherwise distance value (suggested: 0.02"}; Configurable cfgTwoTrackCutMinRadius{"cfgTwoTrackCutMinRadius", 0.8f, "Two-tracks cut: radius in m from which two-tracks cut is applied"}; @@ -1020,10 +1020,9 @@ struct DptDptCorrelations { { /* self configure the desired species */ o2::analysis::dptdptfilter::PIDSpeciesSelection pidselector; - std::vector cfgnames = {"cfgElectronPIDSelection", "cfgMuonPIDSelection", "cfgPionPIDSelection", "cfgKaonPIDSelection", "cfgProtonPIDSelection"}; - std::vector spids = {0, 1, 2, 3, 4}; + std::vector cfgnames = {"cfgElectronPIDSelection", "cfgMuonPIDSelection", "cfgPionPIDSelection", "cfgKaonPIDSelection", "cfgProtonPIDSelection", "cfgDeuteronPIDSelection"}; for (uint i = 0; i < cfgnames.size(); ++i) { - auto includeIt = [&pidselector, &initContext](int spid, auto name) { + auto includeIt = [&pidselector, &initContext](int spid, const auto& name) { bool mUseIt = false; bool mExcludeIt = false; if (getTaskOptionValue(initContext, "dpt-dpt-filter-tracks", TString::Format("%s.mUseIt", name.c_str()).Data(), mUseIt, false) && @@ -1036,7 +1035,7 @@ struct DptDptCorrelations { } } }; - includeIt(spids[i], cfgnames[i]); + includeIt(i, cfgnames[i]); } uint nspecies = pidselector.getNSpecies(); if (nspecies == 0) { @@ -1097,7 +1096,7 @@ struct DptDptCorrelations { } for (int i = 0; i < ncmranges; ++i) { - auto initializeCEInstance = [&fGlobalOutputList](auto dce, auto name, bool im, bool corr) { + auto initializeCEInstance = [&fGlobalOutputList](auto dce, const auto& name, bool im, bool corr) { /* crete the output list for the passed centrality/multiplicity range */ TList* fOutputList = new TList(); fOutputList->SetName(name); @@ -1187,7 +1186,7 @@ struct DptDptCorrelations { /// \brief Get the data collecting engine index corresponding to the passed collision template - int getDCEindex(FilteredCollision collision) + int getDCEindex(const FilteredCollision& collision) { int ixDCE = -1; float cm = collision.centmult(); @@ -1224,8 +1223,8 @@ struct DptDptCorrelations { { using namespace correlationstask; - static constexpr std::string_view kStrDim[] = {"", "", "2D", "3D", "4D"}; - return kStrDim[nNoOfDimensions].data(); + static constexpr std::string_view KStrDim[] = {"", "", "2D", "3D", "4D"}; + return KStrDim[nNoOfDimensions].data(); } template @@ -1249,14 +1248,14 @@ struct DptDptCorrelations { return dataCE[ixDCE]->isCCDBstored(); } }; - auto storePtAverages = [&](auto& ptavgs) { + auto storePtAverages = [&](const auto& ptavgs) { if (cfgSmallDCE.value) { dataCEsmall[ixDCE]->storePtAverages(ptavgs); } else { dataCE[ixDCE]->storePtAverages(ptavgs); } }; - auto storeTrackCorrections = [&](auto& corrs) { + auto storeTrackCorrections = [&](const auto& corrs) { if (cfgSmallDCE.value) { dataCEsmall[ixDCE]->storeTrackCorrections(corrs); } else { @@ -1490,8 +1489,8 @@ struct DptDptCorrelations { std::vector multBinsEdges{VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0, 100.1f}; SliceCache cache; using BinningZVtxMultRec = ColumnBinningPolicy; - BinningZVtxMultRec bindingOnVtxAndMultRec{{vtxBinsEdges, multBinsEdges}, true}; // true is for 'ignore overflows' (true by default) - static constexpr int kNoOfLoggingCombinations = 10; + BinningZVtxMultRec bindingOnVtxAndMultRec{{vtxBinsEdges, multBinsEdges}}; + static constexpr int KNoOfLoggingCombinations = 10; void processRecLevelMixed(soa::Filtered const& collisions, aod::BCsWithTimestamps const&, soa::Filtered const& tracks) { @@ -1501,7 +1500,7 @@ struct DptDptCorrelations { LOGF(DPTDPTLOGCOLLISIONS, "Received %d collisions", collisions.size()); int logcomb = 0; for (auto const& [collision1, tracks1, collision2, tracks2] : pairreco) { - if (logcomb < kNoOfLoggingCombinations) { + if (logcomb < KNoOfLoggingCombinations) { LOGF(DPTDPTLOGCOLLISIONS, "Received collision pair: %ld (%f, %f): %s, %ld (%f, %f): %s", collision1.globalIndex(), collision1.posZ(), collision1.centmult(), collision1.collisionaccepted() ? "accepted" : "not accepted", collision2.globalIndex(), collision2.posZ(), collision2.centmult(), collision2.collisionaccepted() ? "accepted" : "not accepted"); @@ -1536,7 +1535,7 @@ struct DptDptCorrelations { LOGF(DPTDPTLOGCOLLISIONS, "Received %d collisions", collisions.size()); int logcomb = 0; for (auto const& [collision1, tracks1, collision2, tracks2] : pairreco) { - if (logcomb < kNoOfLoggingCombinations) { + if (logcomb < KNoOfLoggingCombinations) { LOGF(DPTDPTLOGCOLLISIONS, "Received collision pair: %ld (%f, %f): %s, %ld (%f, %f): %s", collision1.globalIndex(), @@ -1570,7 +1569,7 @@ struct DptDptCorrelations { PROCESS_SWITCH(DptDptCorrelations, processRecLevelMixedNotStored, "Process reco level mixed events correlations for not stored derived data", false); using BinningZVtxMultGen = ColumnBinningPolicy; - BinningZVtxMultGen bindingOnVtxAndMultGen{{vtxBinsEdges, multBinsEdges}, true}; // true is for 'ignore overflows' (true by default) + BinningZVtxMultGen bindingOnVtxAndMultGen{{vtxBinsEdges, multBinsEdges}}; void processGenLevelMixed(soa::Filtered const& collisions, soa::Filtered const& tracks) { @@ -1580,10 +1579,11 @@ struct DptDptCorrelations { LOGF(DPTDPTLOGCOLLISIONS, "Received %d generated collisions", collisions.size()); int logcomb = 0; for (auto const& [collision1, tracks1, collision2, tracks2] : pairgen) { - if (logcomb < kNoOfLoggingCombinations) { + if (logcomb < KNoOfLoggingCombinations) { LOGF(DPTDPTLOGCOLLISIONS, "Received generated collision pair: %ld (%f, %f): %s, %ld (%f, %f): %s", collision1.globalIndex(), collision1.posZ(), collision1.centmult(), collision1.collisionaccepted() ? "accepted" : "not accepted", collision2.globalIndex(), collision2.posZ(), collision2.centmult(), collision2.collisionaccepted() ? "accepted" : "not accepted"); + logcomb++; } if (!collision1.collisionaccepted() || !collision2.collisionaccepted()) { LOGF(error, "Received collision pair: %ld (%f, %f): %s, %ld (%f, %f): %s", @@ -1611,7 +1611,7 @@ struct DptDptCorrelations { LOGF(DPTDPTLOGCOLLISIONS, "Received %d generated collisions", collisions.size()); int logcomb = 0; for (auto const& [collision1, tracks1, collision2, tracks2] : pairgen) { - if (logcomb < kNoOfLoggingCombinations) { + if (logcomb < KNoOfLoggingCombinations) { LOGF(DPTDPTLOGCOLLISIONS, "Received generated collision pair: %ld (%f, %f): %s, %ld (%f, %f): %s", collision1.globalIndex(), @@ -1622,6 +1622,7 @@ struct DptDptCorrelations { collision2.posZ(), collision2.centmult(), collision2.collisionaccepted() ? "accepted" : "not accepted"); + logcomb++; } if (!collision1.collisionaccepted() || !collision2.collisionaccepted()) { LOGF(error, diff --git a/PWGCF/Tutorial/CFTutorialTask0.cxx b/PWGCF/Tutorial/CFTutorialTask0.cxx index 35d4763dca4..c1acd7899f7 100644 --- a/PWGCF/Tutorial/CFTutorialTask0.cxx +++ b/PWGCF/Tutorial/CFTutorialTask0.cxx @@ -60,7 +60,7 @@ struct CFTutorialTask0 { } histos.fill(HIST("hZvtx_after_sel"), coll.posZ()); - for (auto track : inputTracks) { // Loop over tracks + for (const auto& track : inputTracks) { // Loop over tracks histos.fill(HIST("hP"), track.p()); histos.fill(HIST("hPt"), track.pt()); } diff --git a/PWGCF/Tutorial/CFTutorialTask1.cxx b/PWGCF/Tutorial/CFTutorialTask1.cxx index e4d30fe5f12..403e4518ed0 100644 --- a/PWGCF/Tutorial/CFTutorialTask1.cxx +++ b/PWGCF/Tutorial/CFTutorialTask1.cxx @@ -78,7 +78,7 @@ struct CFTutorialTask1 { } histos.fill(HIST("hZvtx_after_sel"), coll.posZ()); - for (auto track : inputTracks) { // Loop over tracks + for (const auto& track : inputTracks) { // Loop over tracks if (fabs(track.eta()) > 0.8) { continue; } diff --git a/PWGCF/Tutorial/CFTutorialTask2.cxx b/PWGCF/Tutorial/CFTutorialTask2.cxx index c8ba5cca9ff..c4dc0de477a 100644 --- a/PWGCF/Tutorial/CFTutorialTask2.cxx +++ b/PWGCF/Tutorial/CFTutorialTask2.cxx @@ -91,7 +91,7 @@ struct CFTutorialTask2 { { histos.fill(HIST("hZvtx"), coll.posZ()); - for (auto track : inputTracks) { + for (const auto& track : inputTracks) { if (fabs(track.tpcNSigmaPr()) > ConfMinNSigmaTPCCut) { // TPCNSigmaPr is a dynamic column and it is not compatible with Filters continue; } diff --git a/PWGCF/Tutorial/CFTutorialTask3.cxx b/PWGCF/Tutorial/CFTutorialTask3.cxx index 4ece0be89e1..d1a9d200e22 100644 --- a/PWGCF/Tutorial/CFTutorialTask3.cxx +++ b/PWGCF/Tutorial/CFTutorialTask3.cxx @@ -100,7 +100,7 @@ struct CFTutorialTask3 { auto groupNegative = negative->sliceByCached(aod::track::collisionId, coll.globalIndex(), cache); histos.fill(HIST("hZvtx"), coll.posZ()); - for (auto track : groupPositive) { + for (const auto& track : groupPositive) { histos.fill(HIST("hChargePos"), track.sign()); histos.fill(HIST("hP"), track.p()); histos.fill(HIST("hPt"), track.pt()); @@ -108,7 +108,7 @@ struct CFTutorialTask3 { histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi()); } - for (auto track : groupNegative) { + for (const auto& track : groupNegative) { histos.fill(HIST("hChargeNeg"), track.sign()); histos.fill(HIST("hP"), track.p()); histos.fill(HIST("hPt"), track.pt()); diff --git a/PWGCF/Tutorial/CFTutorialTask4.cxx b/PWGCF/Tutorial/CFTutorialTask4.cxx index 22056bb0957..b523c49770d 100644 --- a/PWGCF/Tutorial/CFTutorialTask4.cxx +++ b/PWGCF/Tutorial/CFTutorialTask4.cxx @@ -106,7 +106,7 @@ struct CFTutorialTask4 { auto groupNegative = negative->sliceByCached(aod::track::collisionId, coll.globalIndex(), cache); histos.fill(HIST("hZvtx"), coll.posZ()); - for (auto track : groupPositive) { + for (const auto& track : groupPositive) { histos.fill(HIST("hChargePos"), track.sign()); histos.fill(HIST("hP"), track.p()); histos.fill(HIST("hPt"), track.pt()); @@ -114,7 +114,7 @@ struct CFTutorialTask4 { histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi()); } - for (auto track : groupNegative) { + for (const auto& track : groupNegative) { histos.fill(HIST("hChargeNeg"), track.sign()); histos.fill(HIST("hP"), track.p()); histos.fill(HIST("hPt"), track.pt()); diff --git a/PWGCF/Tutorial/CFTutorialTask5.cxx b/PWGCF/Tutorial/CFTutorialTask5.cxx index a3d087ef42f..6e5d78cdf1c 100644 --- a/PWGCF/Tutorial/CFTutorialTask5.cxx +++ b/PWGCF/Tutorial/CFTutorialTask5.cxx @@ -116,7 +116,7 @@ struct CFTutorialTask5 { auto groupNegative = negative->sliceByCached(aod::track::collisionId, coll.globalIndex(), cache); histos.fill(HIST("hZvtx"), coll.posZ()); - for (auto track : groupPositive) { + for (const auto& track : groupPositive) { histos.fill(HIST("hChargePos"), track.sign()); histos.fill(HIST("hP"), track.p()); histos.fill(HIST("hPt"), track.pt()); @@ -124,7 +124,7 @@ struct CFTutorialTask5 { histos.fill(HIST("hNsigmaTPCP"), track.p(), track.tpcNSigmaPi()); } - for (auto track : groupNegative) { + for (const auto& track : groupNegative) { histos.fill(HIST("hChargeNeg"), track.sign()); histos.fill(HIST("hP"), track.p()); histos.fill(HIST("hPt"), track.pt()); @@ -150,7 +150,7 @@ struct CFTutorialTask5 { void processMixed(MyFilteredCollisions const& colls, MyFilteredTracks const&) { - BinningType colBinning{{ConfVtxBins, ConfMultBins}, true}; + BinningType colBinning{{ConfVtxBins, ConfMultBins}}; for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, colls, colls)) { auto groupPositive = positive->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); auto groupNegative = negative->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); @@ -175,7 +175,7 @@ struct CFTutorialTask5 { void processMixedEventInterface(MyFilteredCollisions& colls, MyFilteredTracks& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{ConfVtxBins, ConfMultBins}, true}; + BinningType colBinning{{ConfVtxBins, ConfMultBins}}; SameKindPair pair{colBinning, 5, -1, colls, tracksTuple, &cache}; for (auto& [c1, tracks1, c2, tracks2] : pair) { Partition groupPositive = aod::track::signed1Pt > ConfChargeCut; diff --git a/PWGCF/TwoParticleCorrelations/Core/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Core/CMakeLists.txt index 557f6dbb674..a8b429afd05 100644 --- a/PWGCF/TwoParticleCorrelations/Core/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Core/CMakeLists.txt @@ -16,6 +16,7 @@ o2physics_add_library(TwoPartCorrCore PIDSelectionFilterAndAnalysis.cxx EventSelectionFilterAndAnalysis.cxx FilterAndAnalysisFramework.cxx + DihadronContainer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore) o2physics_target_root_dictionary(TwoPartCorrCore @@ -25,4 +26,5 @@ o2physics_target_root_dictionary(TwoPartCorrCore PIDSelectionFilterAndAnalysis.h EventSelectionFilterAndAnalysis.h FilterAndAnalysisFramework.h + DihadronContainer.h LINKDEF TwoPartCorrLinkDef.h) diff --git a/PWGCF/TwoParticleCorrelations/Core/DihadronContainer.cxx b/PWGCF/TwoParticleCorrelations/Core/DihadronContainer.cxx new file mode 100644 index 00000000000..572fd3b25e2 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Core/DihadronContainer.cxx @@ -0,0 +1,201 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DihadronContainer.h +/// \brief minimum encapsulated container for di-hadron correlations +/// \author Zhiyong Lu (zhiyong.lu@cern.ch) +/// \since July/2026 + +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +ClassImp(DihadronContainer); + +DihadronContainer::DihadronContainer() : mCorrHist(nullptr), nCorrStep(1) +{ + // Default constructor +} + +DihadronContainer::DihadronContainer(const char* name, const char* objTitle, + const std::vector& correlationAxis, + const uint8_t nStep) : TNamed(name, objTitle), + mCorrHist(nullptr), + nCorrStep(nStep) +{ + + if (strlen(name) == 0) { + return; + } + + int64_t bins = 1; + LOGF(info, "Creating DihadronContainer:"); + for (UInt_t iAxis = 0; iAxis < correlationAxis.size(); iAxis++) { + LOGF(info, "correlationAxis[%d].getNbins() = %d", iAxis, correlationAxis[iAxis].getNbins()); + bins *= correlationAxis[iAxis].getNbins(); + } + LOGF(info, "DihadronContainer with %ld bins in the correlation histogram (approx. %ld-%ld MB of memory)", bins, bins * 4 / 1024 / 1024, bins * 8 / 1024 / 1024); + + mCorrHist = HistFactory::createHist({"mCorrHist", "d^{2}N_{ch}/d#varphid#eta", {HistType::kStepTHnF, correlationAxis, nStep}}).release(); +} + +//_____________________________________________________________________________ +DihadronContainer::DihadronContainer(const DihadronContainer& c) : TNamed(c), + mCorrHist(nullptr), + nCorrStep(c.nCorrStep) +{ + // DihadronContainer copy constructor + if (c.mCorrHist) + mCorrHist = dynamic_cast(c.mCorrHist->Clone()); + else + mCorrHist = nullptr; +} + +//____________________________________________________________________ +DihadronContainer::~DihadronContainer() +{ + // Destructor + if (mCorrHist) { + delete mCorrHist; + mCorrHist = nullptr; + } +} + +//____________________________________________________________________ +DihadronContainer& DihadronContainer::operator=(const DihadronContainer& c) +{ + // assigment operator + if (this != &c) { + c.Copy(*this); + } + return *this; +} + +//____________________________________________________________________ +void DihadronContainer::Copy(TObject& c) const +{ + // copy function + auto target = dynamic_cast(c); + + if (mCorrHist) { + target.mCorrHist = dynamic_cast(mCorrHist->Clone()); + } + + target.nCorrStep = nCorrStep; +} + +//____________________________________________________________________ +void DihadronContainer::deepCopy(DihadronContainer* from) +{ + // copies the entries of this object's members from the object to this object + // fills using the fill function and thus allows that the objects have different binning + + for (Int_t step = 0; step < mCorrHist->getNSteps(); step++) { + LOGF(info, "Copying step %d", step); + THnBase* target = mCorrHist->getTHn(step); + THnBase* source = from->mCorrHist->getTHn(step); + + target->Reset(); + target->RebinnedAdd(source); + } +} + +//____________________________________________________________________ +Long64_t DihadronContainer::Merge(TCollection* list) +{ + // Merge a list of DihadronContainer objects with this + // Returns the number of merged objects (including this). + + if (!list) { + return 0; + } + + if (list->IsEmpty()) { + return 1; + } + + TIterator* iter = list->MakeIterator(); + TObject* obj = nullptr; + + // collections of objects + const UInt_t kMaxLists = 1; + auto lists = new TList*[kMaxLists]; + + for (UInt_t i = 0; i < kMaxLists; i++) { + lists[i] = new TList; + } + + Int_t count = 0; + while ((obj = iter->Next())) { + + auto entry = dynamic_cast(obj); + if (entry == nullptr) { + continue; + } + + if (entry->mCorrHist) { + lists[0]->Add(entry->mCorrHist); + } + + count++; + } + + if (mCorrHist) { + mCorrHist->Merge(lists[0]); + } + + for (UInt_t i = 0; i < kMaxLists; i++) { + delete lists[i]; + } + + delete[] lists; + + return count + 1; +} + +//____________________________________________________________________ +void DihadronContainer::Reset() +{ + // resets all contained histograms + + for (Int_t step = 0; step < mCorrHist->getNSteps(); step++) { + mCorrHist->getTHn(step)->Reset(); + } +} + +THnBase* DihadronContainer::changeToThn(THnBase* sparse) +{ + // change the object to THn for faster processing + + return THn::CreateHn(Form("%s_thn", sparse->GetName()), sparse->GetTitle(), sparse); +} + +ClassImp(DihadronContainer); diff --git a/PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h b/PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h new file mode 100644 index 00000000000..0ddfe8db4b9 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h @@ -0,0 +1,70 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef PWGCF_TWOPARTICLECORRELATIONS_CORE_DIHADRONCONTAINER_H_ +#define PWGCF_TWOPARTICLECORRELATIONS_CORE_DIHADRONCONTAINER_H_ + +/// \file DihadronContainer.h +/// \brief minimum encapsulated container for di-hadron correlations +/// \author Zhiyong Lu (zhiyong.lu@cern.ch) +/// \since July/2026 + +#include + +#include + +#include +#include + +#include +#include + +class TH1; +class TH1F; +class TH3; +class TH3F; +class TH2F; +class TH1D; +class TH2; +class TH2D; +class TCollection; +class THnSparse; +class THnBase; +class StepTHn; + +class DihadronContainer : public TNamed +{ + public: + DihadronContainer(); + DihadronContainer(const char* name, const char* objTitle, const std::vector& correlationAxis, const uint8_t nStep = 1); + virtual ~DihadronContainer(); // NOLINT(modernize-use-override) + + StepTHn* getCorrHist() { return mCorrHist; } + void setCorrHist(StepTHn* hist) { mCorrHist = hist; } + void printCorrHist(); + + DihadronContainer(const DihadronContainer& c); + DihadronContainer& operator=(const DihadronContainer& corr); + virtual void Copy(TObject& c) const; // NOLINT: Making this override breaks compilation for unknown reason + void deepCopy(DihadronContainer* from); + + virtual Long64_t Merge(TCollection* list); + void Reset(); + THnBase* changeToThn(THnBase* sparse); + + protected: + StepTHn* mCorrHist; // container for n-dimension correlations + uint8_t nCorrStep; + + ClassDef(DihadronContainer, 2) // underlying event histogram container +}; + +#endif // PWGCF_TWOPARTICLECORRELATIONS_CORE_DIHADRONCONTAINER_H_ diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx index cf70ab289db..3181b07d8b5 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.cxx @@ -38,7 +38,7 @@ using namespace o2; using namespace o2::analysis::PWGCF; using namespace boost; -EventSelectionConfigurable::EventSelectionConfigurable(std::vector bfieldsel, +EventSelectionConfigurable::EventSelectionConfigurable(const std::vector& bfieldsel, std::vector multsel, std::vector trigsel, std::vector zvtxsel, @@ -49,7 +49,7 @@ EventSelectionConfigurable::EventSelectionConfigurable(std::vector mZVertexSel(""), mPileUpRejection("") { - auto storeCutString = [](auto& selvector, std::string selname) { + auto storeCutString = [](auto& selvector, const std::string& selname) { if (selvector.size() != 0) { if (selvector.size() == 1) { if (selvector[0].size() != 0) { @@ -61,7 +61,7 @@ EventSelectionConfigurable::EventSelectionConfigurable(std::vector TString scut = selname + "{cwv{"; bool def = true; bool firstvar = true; - for (auto cut : selvector) { + for (const auto& cut : selvector) { if (def) { scut += cut + ':'; def = false; @@ -141,7 +141,7 @@ EventSelectionFilterAndAnalysis::EventSelectionFilterAndAnalysis(const EventSele TString cutString = "eventsel{"; bool first = true; - auto appendCut = [&cutString, &first](std::string str) { + auto appendCut = [&cutString, &first](const std::string& str) { if (str.size() > 0) { if (first) { cutString += str; @@ -474,8 +474,6 @@ void EventSelectionFilterAndAnalysis::StoreArmedMask() } mOptArmedMask.push_back(optMask); optMask = 0UL; - if (mTriggerSelection != nullptr) { - } if (mZVertex != nullptr) { armedBrick(mZVertex); } diff --git a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h index fb7ba6b35f0..28ca101cbf4 100644 --- a/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/EventSelectionFilterAndAnalysis.h @@ -25,6 +25,7 @@ #include #include +#include #include namespace o2 @@ -47,10 +48,10 @@ class EventSelectionConfigurable std::string trigsel = "", std::string zvtxsel = "", std::string pileuprej = "") - : mBFiledSel(bfieldsel), mMultSel{multsel}, mTriggerSel{trigsel}, mZVertexSel{zvtxsel}, mPileUpRejection{pileuprej} + : mBFiledSel(std::move(bfieldsel)), mMultSel{std::move(multsel)}, mTriggerSel{std::move(trigsel)}, mZVertexSel{std::move(zvtxsel)}, mPileUpRejection{std::move(pileuprej)} { } - EventSelectionConfigurable(std::vector bfieldsel, + EventSelectionConfigurable(const std::vector& bfieldsel, std::vector multsel, std::vector trigsel, std::vector zvtxsel, @@ -83,6 +84,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis private: struct ComplexBrickHelper { + virtual ~ComplexBrickHelper() = default; CutBrick* mBrick = nullptr; int mDefaultEstimatorIndex = -1; std::vector mAlternateEstimatorIndex = std::vector{}; @@ -94,15 +96,15 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis void armedBrick(uint64_t&, uint64_t&, uint64_t&, int&); }; struct MultiplicityBrick : public ComplexBrickHelper { - virtual void initialize(); + void initialize() override; std::vector GetMultiplicities(); int GetArmedIndex() { return mBrick->getArmedIndex(); } - virtual bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index); + bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index) override; }; struct PileUpRejBrick : public ComplexBrickHelper { - virtual void initialize(); + void initialize() override; std::vector mIndepVar = std::vector{}; - virtual bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index); + bool Filter(uint64_t& mask, int& bit, CutBrick* brick, int index) override; }; static bool filterBrickValue(uint64_t& mask, int& bit, CutBrick* brick, float value); @@ -110,7 +112,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis template void StoreMultiplicities(CollisionToFilter const&, AssociatedTracks const&); int CalculateMaskLength() override; - virtual void StoreArmedMask() override; + void StoreArmedMask() override; std::vector*> mBFieldSelection; //! the magnetic field selection cuts MultiplicityBrick* mMultiplicityClasses; //! the multiplicity classes cuts @@ -118,7 +120,7 @@ class EventSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis CutBrick* mZVertex; //! the z vertex selection cuts PileUpRejBrick* mPileUpRejection; //! the pile-up rejection criteria - ClassDef(EventSelectionFilterAndAnalysis, 1) + ClassDefOverride(EventSelectionFilterAndAnalysis, 1) }; /// \brief Stores the different multiplicities needed for proper collision filtering @@ -140,7 +142,7 @@ inline void EventSelectionFilterAndAnalysis::StoreMultiplicities(CollisionToFilt /* we require tracks with pT > 0.15 and |eta| < 0.8 and additionally the TPCout flag */ /* for the time being there is not TPCout flag, we just consider hasTPC */ for (auto track : tracks) { - if (track.pt() > 0.15 and abs(track.eta()) < 0.8 and track.hasTPC()) { + if (track.pt() > 0.15 and std::abs(track.eta()) < 0.8 and track.hasTPC()) { ntracksTPCout++; } } @@ -230,8 +232,6 @@ inline uint64_t EventSelectionFilterAndAnalysis::Filter(CollisionToFilter const& bool acc = mMultiplicityClasses->ComplexBrickHelper::Filter(selectedMask, bit); acceptcollision = acceptcollision && acc; } - if (mTriggerSelection != nullptr) { - } if (mZVertex != nullptr) { bool acc = filterBrickValue(selectedMask, bit, mZVertex, col.posZ()); acceptcollision = acceptcollision && acc; diff --git a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.cxx b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.cxx index b35b36dc202..3e1c7419742 100644 --- a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.cxx @@ -59,7 +59,7 @@ PIDSelectionConfigurable::PIDSelectionConfigurable(std::vector pidt mPidBaySel_ka{}, mPidBaySel_pr{} { - auto storeCutString = [](auto& selvector, std::string selname) { + auto storeCutString = [](auto& selvector, const std::string& selname) { if (selvector.size() != 0) { if (selvector.size() == 1) { if (selvector[0].size() != 0) { @@ -71,7 +71,7 @@ PIDSelectionConfigurable::PIDSelectionConfigurable(std::vector pidt TString scut = selname + "{cwv{"; bool def = true; bool firstvar = true; - for (auto cut : selvector) { + for (const auto& cut : selvector) { if (def) { scut += cut + ':'; def = false; @@ -151,7 +151,7 @@ PIDSelectionFilterAndAnalysis::PIDSelectionFilterAndAnalysis(const PIDSelectionC TString cutString = "pidsel{"; bool first = true; - auto appendCut = [&cutString, &first](std::string str) { + auto appendCut = [&cutString, &first](const std::string& str) { if (str.size() > 0) { if (first) { cutString += str; @@ -213,7 +213,7 @@ PIDSelectionFilterAndAnalysis::~PIDSelectionFilterAndAnalysis() int PIDSelectionFilterAndAnalysis::CalculateMaskLength() { int length = 0; - auto addLength = [&](auto bricklst) { + auto addLength = [&](const auto& bricklst) { for (auto brick : bricklst) { if (brick != nullptr) { length += brick->Length(); @@ -261,7 +261,7 @@ void PIDSelectionFilterAndAnalysis::ConstructCutFromString(const TString& cutstr LOGF(fatal, "PIDSelectionFilterAndAnalysis::::ConstructCutFromString. Wrong RE: %s, try pidsel{tpcsel{tpcpi{cwv{rg{-3.0,3.0}:rg{-2.0,2.0},rg{-3.0,5.0}}}}} for instance", cutstr.Data()); } LOGF(info, "Captured %s", m[1].str().c_str()); - auto handleDetectorLevel = [](std::string detector, auto& bricklst, std::string cut) { + auto handleDetectorLevel = [](const std::string& detector, auto& bricklst, const std::string& cut) { std::set allowed = {"lim", "th", "rg", "xrg", "cwv", "mrg"}; TString lev3str = cut; while (lev3str.Length() != 0) { @@ -329,7 +329,7 @@ void PIDSelectionFilterAndAnalysis::StoreArmedMask() mOptArmedMask.clear(); mForcedArmedMask = 0UL; int bit = 0; - auto armedList = [&](auto bricklst) { + auto armedList = [&](const auto& bricklst) { auto armedBrick = [&](auto brick, bool opt = false) { if (brick != nullptr) { std::vector res = brick->IsArmed(); diff --git a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h index 421edf90781..7cf25ff5d6a 100644 --- a/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/PIDSelectionFilterAndAnalysis.h @@ -21,6 +21,7 @@ #include #include +#include #include #undef INCORPORATEBAYESIANPID @@ -53,16 +54,16 @@ class PIDSelectionConfigurable #else PIDSelectionConfigurable(std::string pidtpcel = "", std::string pidtpcmu = "", std::string pidtpcpi = "", std::string pidtpcka = "", std::string pidtpcpr = "", std::string pidtofel = "", std::string pidtofmu = "", std::string pidtofpi = "", std::string pidtofka = "", std::string pidtofpr = "") - : mPidTpcSel_el{pidtpcel}, - mPidTpcSel_mu{pidtpcmu}, - mPidTpcSel_pi{pidtpcpi}, - mPidTpcSel_ka{pidtpcka}, - mPidTpcSel_pr{pidtpcpr}, - mPidTofSel_el{pidtofel}, - mPidTofSel_mu{pidtofmu}, - mPidTofSel_pi{pidtofpi}, - mPidTofSel_ka{pidtofka}, - mPidTofSel_pr{pidtofpr} + : mPidTpcSel_el{std::move(pidtpcel)}, + mPidTpcSel_mu{std::move(pidtpcmu)}, + mPidTpcSel_pi{std::move(pidtpcpi)}, + mPidTpcSel_ka{std::move(pidtpcka)}, + mPidTpcSel_pr{std::move(pidtpcpr)}, + mPidTofSel_el{std::move(pidtofel)}, + mPidTofSel_mu{std::move(pidtofmu)}, + mPidTofSel_pi{std::move(pidtofpi)}, + mPidTofSel_ka{std::move(pidtofka)}, + mPidTofSel_pr{std::move(pidtofpr)} { } PIDSelectionConfigurable(std::vector pidtpcel, std::vector pidtpcmu, std::vector pidtpcpi, std::vector pidtpcka, std::vector pidtpcpr, @@ -97,7 +98,7 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis PIDSelectionFilterAndAnalysis(); PIDSelectionFilterAndAnalysis(const TString&, selmodes); PIDSelectionFilterAndAnalysis(const PIDSelectionConfigurable& pidsel, selmodes mode); - virtual ~PIDSelectionFilterAndAnalysis() override; + ~PIDSelectionFilterAndAnalysis() override; void SetPTOF(float ptof) { mPTOF = ptof; } void SetRequireTOF(bool requiretof = false) { mRequireTOF = requiretof; } @@ -132,8 +133,8 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis private: void ConstructCutFromString(const TString&); - virtual int CalculateMaskLength() override; - virtual void StoreArmedMask() override; + int CalculateMaskLength() override; + void StoreArmedMask() override; float mPTOF = 0.8f; ///< the p threshold for cheking TOF information bool mRequireTOF = false; ///< is TOF required @@ -144,7 +145,7 @@ class PIDSelectionFilterAndAnalysis : public SelectionFilterAndAnalysis std::vector*> mCloseNsigmasTOF; std::vector*> mBayesProbability; - ClassDef(PIDSelectionFilterAndAnalysis, 1); + ClassDefOverride(PIDSelectionFilterAndAnalysis, 1); }; /// \brief Fills the filter cuts mask diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx index ffd29c96571..56563c682ef 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.cxx @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -1188,19 +1189,18 @@ bool TrackSelectionBrick::FulfillsITSHitRequirements(uint8_t itsClusterMap) return true; } -const std::string TrackSelectionBrick::mCutNames[static_cast(TrackSelectionBrick::TrackCuts::kNCuts)] = - { - "TrackType", - "TPCNCls", - "TPCCrossedRowsOverNCls", - "TPCRefit", - "ITSNCls", - "ITSChi2NDF", - "ITSRefit", - "ITSHits", - "GoldenChi2", - "DCAxy", - "DCAz"}; +const std::array(TrackSelectionBrick::TrackCuts::kNCuts)> TrackSelectionBrick::mCutNames{ + "TrackType", + "TPCNCls", + "TPCCrossedRowsOverNCls", + "TPCRefit", + "ITSNCls", + "ITSChi2NDF", + "ITSRefit", + "ITSHits", + "GoldenChi2", + "DCAxy", + "DCAz"}; /// \brief Returns wether the cut brick is incorporated in the selection chain /// \return true if the cut brick is incorporated diff --git a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h index 68d7f9671a4..aa79964d65a 100644 --- a/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h +++ b/PWGCF/TwoParticleCorrelations/Core/SkimmingConfigurableCuts.h @@ -23,6 +23,7 @@ #include +#include #include #include #include @@ -46,7 +47,7 @@ class CutBrick : public TNamed CutBrick(); CutBrick(const char*, const char*); CutBrick(const CutBrick&) = delete; - virtual ~CutBrick() override = default; + ~CutBrick() override = default; CutBrick& operator=(const CutBrick&) = delete; public: @@ -98,7 +99,7 @@ class CutBrick : public TNamed BrickStatus mState = kPASSIVE; BrickMode mMode = kUNSELECTED; - ClassDef(CutBrick, 1); + ClassDefOverride(CutBrick, 1); }; /// \class CutBrickLimit @@ -110,14 +111,14 @@ class CutBrickLimit : public CutBrick public: CutBrickLimit(); CutBrickLimit(const char*, const TValueToFilter&); - CutBrickLimit(const TString&); - virtual ~CutBrickLimit() override = default; + explicit CutBrickLimit(const TString&); + ~CutBrickLimit() override = default; CutBrickLimit(const CutBrickLimit&) = delete; CutBrickLimit& operator=(const CutBrickLimit&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -154,13 +155,13 @@ class CutBrickFnLimit : public CutBrickLimit public: CutBrickFnLimit(); CutBrickFnLimit(const char*, const TF1&); - CutBrickFnLimit(const TString&); - virtual ~CutBrickFnLimit() override = default; + explicit CutBrickFnLimit(const TString&); + ~CutBrickFnLimit() override = default; CutBrickFnLimit(const CutBrickFnLimit&) = delete; CutBrickFnLimit& operator=(const CutBrickFnLimit&) = delete; /// sets the value of the limit according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLimit = TValueToFilter(mFunction.Eval(x)); } @@ -181,14 +182,14 @@ class CutBrickThreshold : public CutBrick public: CutBrickThreshold(); CutBrickThreshold(const char*, const TValueToFilter&); - CutBrickThreshold(const TString&); - virtual ~CutBrickThreshold() override = default; + explicit CutBrickThreshold(const TString&); + ~CutBrickThreshold() override = default; CutBrickThreshold(const CutBrickThreshold&) = delete; CutBrickThreshold& operator=(const CutBrickThreshold&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -225,13 +226,13 @@ class CutBrickFnThreshold : public CutBrickThreshold public: CutBrickFnThreshold(); CutBrickFnThreshold(const char*, const TF1&); - CutBrickFnThreshold(const TString&); - virtual ~CutBrickFnThreshold() override = default; + explicit CutBrickFnThreshold(const TString&); + ~CutBrickFnThreshold() override = default; CutBrickFnThreshold(const CutBrickFnThreshold&) = delete; CutBrickFnThreshold& operator=(const CutBrickFnThreshold&) = delete; /// sets the value of the threshold according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mThreshold = TValueToFilter(mFunction.Eval(x)); } @@ -252,14 +253,14 @@ class CutBrickRange : public CutBrick public: CutBrickRange(); CutBrickRange(const char*, const TValueToFilter&, const TValueToFilter&); - CutBrickRange(const TString&); - virtual ~CutBrickRange() override = default; + explicit CutBrickRange(const TString&); + ~CutBrickRange() override = default; CutBrickRange(const CutBrickRange&) = delete; CutBrickRange& operator=(const CutBrickRange&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -297,13 +298,13 @@ class CutBrickFnRange : public CutBrickRange public: CutBrickFnRange(); CutBrickFnRange(const char*, const TF1&, const TF1&); - CutBrickFnRange(const TString&); - virtual ~CutBrickFnRange() override = default; + explicit CutBrickFnRange(const TString&); + ~CutBrickFnRange() override = default; CutBrickFnRange(const CutBrickFnRange&) = delete; CutBrickFnRange& operator=(const CutBrickFnRange&) = delete; /// sets the value of the limits according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLow = TValueToFilter(mLowFunction.Eval(x)); this->mUp = TValueToFilter(mUpFunction.Eval(x)); @@ -326,14 +327,14 @@ class CutBrickExtToRange : public CutBrick public: CutBrickExtToRange(); CutBrickExtToRange(const char*, const TValueToFilter&, const TValueToFilter&); - CutBrickExtToRange(const TString&); - virtual ~CutBrickExtToRange() override = default; + explicit CutBrickExtToRange(const TString&); + ~CutBrickExtToRange() override = default; CutBrickExtToRange(const CutBrickExtToRange&) = delete; CutBrickExtToRange& operator=(const CutBrickExtToRange&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override { return 1; } + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override { return 1; } private: void ConstructCutFromString(const TString&); @@ -371,13 +372,13 @@ class CutBrickFnExtToRange : public CutBrickExtToRange public: CutBrickFnExtToRange(); CutBrickFnExtToRange(const char*, const TF1&, const TF1&); - CutBrickFnExtToRange(const TString&); - virtual ~CutBrickFnExtToRange() override = default; + explicit CutBrickFnExtToRange(const TString&); + ~CutBrickFnExtToRange() override = default; CutBrickFnExtToRange(const CutBrickFnExtToRange&) = delete; CutBrickFnExtToRange& operator=(const CutBrickFnExtToRange&) = delete; /// sets the value of the limits according the passed variable value - virtual void setIndependentFnVar(float x) override + void setIndependentFnVar(float x) override { this->mLow = TValueToFilter(mLowFunction.Eval(x)); this->mUp = TValueToFilter(mUpFunction.Eval(x)); @@ -402,17 +403,17 @@ class CutBrickSelectorMultipleRanges : public CutBrick public: CutBrickSelectorMultipleRanges(); CutBrickSelectorMultipleRanges(const char*, const std::vector&); - CutBrickSelectorMultipleRanges(const TString&); - virtual ~CutBrickSelectorMultipleRanges() override = default; + explicit CutBrickSelectorMultipleRanges(const TString&); + ~CutBrickSelectorMultipleRanges() override = default; CutBrickSelectorMultipleRanges(const CutBrickSelectorMultipleRanges&) = delete; CutBrickSelectorMultipleRanges& operator=(const CutBrickSelectorMultipleRanges&) = delete; - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; /// Return the length needed to code the brick status /// The length is in brick units. The actual length is implementation dependent /// \returns Brick length in units of bricks - virtual int Length() override { return mActive.size(); } + int Length() override { return mActive.size(); } private: void ConstructCutFromString(const TString&); @@ -432,8 +433,8 @@ class CutWithVariations : public CutBrick /// Default constructor CutWithVariations(); CutWithVariations(const char*, const char*, bool); - CutWithVariations(const TString&); - virtual ~CutWithVariations() override = default; + explicit CutWithVariations(const TString&); + ~CutWithVariations() override = default; CutWithVariations(const CutWithVariations&) = delete; CutWithVariations& operator=(const CutWithVariations&) = delete; @@ -441,10 +442,10 @@ class CutWithVariations : public CutBrick bool AddVariationBrick(CutBrick* brick); TList& getDefaultBricks() { return mDefaultBricks; } TList& getVariantBricks() { return mVariationBricks; } - virtual std::vector IsArmed() override; - virtual std::vector Filter(const TValueToFilter&) override; - virtual int Length() override; - virtual int getArmedIndex() override; + std::vector IsArmed() override; + std::vector Filter(const TValueToFilter&) override; + int Length() override; + int getArmedIndex() override; private: void ConstructCutFromString(const TString&); @@ -452,7 +453,7 @@ class CutWithVariations : public CutBrick bool mAllowSeveralDefaults; ///< true if allows to store several cut default values TList mDefaultBricks; ///< the list with the cut default values bricks TList mVariationBricks; ///< the list with the cut variation values bricks - ClassDef(CutWithVariations, 1); + ClassDefOverride(CutWithVariations, 1); }; /// \class SpecialCutBrick @@ -467,7 +468,7 @@ class SpecialCutBrick : public TNamed SpecialCutBrick(); SpecialCutBrick(const char*, const char*); SpecialCutBrick(const SpecialCutBrick&) = delete; - virtual ~SpecialCutBrick() override = default; + ~SpecialCutBrick() override = default; SpecialCutBrick& operator=(const SpecialCutBrick&) = delete; public: @@ -503,15 +504,15 @@ class SpecialCutBrick : public TNamed BrickStatus mState = kPASSIVE; BrickMode mMode = kUNSELECTED; - ClassDef(SpecialCutBrick, 1); + ClassDefOverride(SpecialCutBrick, 1); }; class TrackSelectionBrick : public SpecialCutBrick { public: TrackSelectionBrick() = default; - TrackSelectionBrick(const TString&); - virtual ~TrackSelectionBrick() override = default; + explicit TrackSelectionBrick(const TString&); + ~TrackSelectionBrick() override = default; enum class TrackCuts : int { kTrackType = 0, @@ -530,9 +531,9 @@ class TrackSelectionBrick : public SpecialCutBrick kNCuts }; - static const std::string mCutNames[static_cast(TrackCuts::kNCuts)]; + static const std::array(TrackSelectionBrick::TrackCuts::kNCuts)> mCutNames; - virtual std::vector IsArmed() override; + std::vector IsArmed() override; template bool Filter(TrackToFilter const& track) { @@ -547,8 +548,8 @@ class TrackSelectionBrick : public SpecialCutBrick ((mRequireTPCRefit) ? (track.flags() & o2::aod::track::TPCrefit) : true) && ((mRequireGoldenChi2) ? (track.flags() & o2::aod::track::GoldenChi2) : true) && FulfillsITSHitRequirements(track.itsClusterMap()) && - (mCheckMaxDcaXY && (abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY))) && - (mCheckMaxDcaZ && (abs(track.dcaZ()) <= mMaxDcaZ))) { + (mCheckMaxDcaXY && (std::abs(track.dcaXY()) <= ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY))) && + (mCheckMaxDcaZ && (std::abs(track.dcaZ()) <= mMaxDcaZ))) { this->mState = this->kACTIVE; return true; } else { @@ -572,9 +573,9 @@ class TrackSelectionBrick : public SpecialCutBrick void SetMaxDcaXY(float maxDcaXY) { mMaxDcaXY = maxDcaXY; } void SetMaxDcaZ(float maxDcaZ) { mMaxDcaZ = maxDcaZ; } - void SetMaxDcaXYPtDep(std::function ptDepCut) { mMaxDcaXYPtDep = ptDepCut; } - void SetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) { mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); } - void SetRequireNoHitsInITSLayers(std::set excludedLayers) { mRequiredITSHits.push_back(std::make_pair(-1, excludedLayers)); } + void SetMaxDcaXYPtDep(std::function ptDepCut) { mMaxDcaXYPtDep = std::move(ptDepCut); } + void SetRequireHitsInITSLayers(int8_t minNRequiredHits, const std::set& requiredLayers) { mRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); } + void SetRequireNoHitsInITSLayers(const std::set& excludedLayers) { mRequiredITSHits.push_back(std::make_pair(-1, excludedLayers)); } void ResetITSRequirements() { mRequiredITSHits.clear(); } void DisableNClustersTPCCheck(bool disable = true) { mCheckNClustersTPC = not disable; } @@ -626,7 +627,7 @@ class TrackSelectionBrick : public SpecialCutBrick // vector of ITS requirements (minNRequiredHits in specific requiredLayers) std::vector>> mRequiredITSHits{}; - ClassDef(TrackSelectionBrick, 1); + ClassDefOverride(TrackSelectionBrick, 1); }; } // namespace PWGCF diff --git a/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.cxx b/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.cxx index 695acf9e762..65b9149c6ca 100644 --- a/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.cxx +++ b/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.cxx @@ -36,7 +36,7 @@ using namespace o2::analysis::PWGCF; using namespace boost; /// \brief Constructor adapted to hyperloop -TrackSelectionConfigurable::TrackSelectionConfigurable(std::vector ttype, +TrackSelectionConfigurable::TrackSelectionConfigurable(const std::vector& ttype, std::vector nclstpc, std::vector nxrtpc, std::vector nclsits, @@ -59,7 +59,7 @@ TrackSelectionConfigurable::TrackSelectionConfigurable(std::vector mPtRange{}, mEtaRange{} { - auto storeCutString = [](auto& selvector, std::string selname) { + auto storeCutString = [](auto& selvector, const std::string& selname) { if (selvector.size() != 0) { if (selvector.size() == 1) { if (selvector[0].size() != 0) { @@ -71,7 +71,7 @@ TrackSelectionConfigurable::TrackSelectionConfigurable(std::vector TString scut = selname + "{cwv{"; bool def = true; bool firstvar = true; - for (auto cut : selvector) { + for (const auto& cut : selvector) { if (def) { scut += cut + ':'; def = false; @@ -191,7 +191,7 @@ TrackSelectionFilterAndAnalysis::TrackSelectionFilterAndAnalysis(const TrackSele cutString += ";"; bool first = true; - auto appendCut = [&cutString, &first](std::string str) { + auto appendCut = [&cutString, &first](const std::string& str) { if (str.size() > 0) { if (first) { cutString += str; diff --git a/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.h b/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.h index 75ad4aed4e1..f1ebbfe9084 100644 --- a/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.h +++ b/PWGCF/TwoParticleCorrelations/Core/TrackSelectionFilterAndAnalysis.h @@ -23,6 +23,7 @@ #include #include +#include #include namespace o2 @@ -51,20 +52,20 @@ class TrackSelectionConfigurable std::string dcaz = "", std::string ptrange = "", std::string etarange = "") - : mTrackTypes{ttype}, - mNClustersTPC{nclstpc}, - mNCrossedRowsTPC{nxrtpc}, - mNClustersITS{nclsits}, - mMaxChi2PerClusterTPC{chi2clustpc}, - mMaxChi2PerClusterITS{chi2clusits}, - mMinNCrossedRowsOverFindableClustersTPC{xrofctpc}, - mMaxDcaXY{dcaxy}, - mMaxDcaZ{dcaz}, - mPtRange{ptrange}, - mEtaRange{etarange} + : mTrackTypes{std::move(ttype)}, + mNClustersTPC{std::move(nclstpc)}, + mNCrossedRowsTPC{std::move(nxrtpc)}, + mNClustersITS{std::move(nclsits)}, + mMaxChi2PerClusterTPC{std::move(chi2clustpc)}, + mMaxChi2PerClusterITS{std::move(chi2clusits)}, + mMinNCrossedRowsOverFindableClustersTPC{std::move(xrofctpc)}, + mMaxDcaXY{std::move(dcaxy)}, + mMaxDcaZ{std::move(dcaz)}, + mPtRange{std::move(ptrange)}, + mEtaRange{std::move(etarange)} { } - TrackSelectionConfigurable(std::vector ttype, + TrackSelectionConfigurable(const std::vector& ttype, std::vector nclstpc, std::vector nxrtpc, std::vector nclsits, diff --git a/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h b/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h index c6ff9c1ac28..3b3bddb5118 100644 --- a/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h +++ b/PWGCF/TwoParticleCorrelations/Core/TwoPartCorrLinkDef.h @@ -8,8 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ -#define PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -50,5 +50,4 @@ #pragma link C++ class o2::analysis::PWGCF::EventSelectionFilterAndAnalysis::PileUpRejBrick + ; #pragma link C++ class o2::analysis::PWGCF::EventSelectionFilterAndAnalysis + ; #pragma link C++ class o2::analysis::PWGCF::FilterAndAnalysisFramework + ; - -#endif // PWGCF_TWOPARTICLECORRELATIONS_CORE_TWOPARTCORRLINKDEF_H_ +#pragma link C++ class DihadronContainer + ; diff --git a/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h new file mode 100644 index 00000000000..8941e813244 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h @@ -0,0 +1,110 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LambdaSpinPolarizationDerived.h +/// +/// \brief Derived tables of Lambda candidates with loose selections, input of lambdaSpinPolarization +/// \author Subhadeep Roy + +#ifndef PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ +#define PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace lambdaloosecollision +{ +DECLARE_SOA_COLUMN(Cent, cent, float); +DECLARE_SOA_COLUMN(Mult, mult, float); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); +} // namespace lambdaloosecollision + +DECLARE_SOA_TABLE(LambdaLooseCols, "AOD", "LAMBDALOOSECOL", + o2::soa::Index<>, + lambdaloosecollision::Cent, + lambdaloosecollision::Mult, + lambdaloosecollision::PosX, + lambdaloosecollision::PosY, + lambdaloosecollision::PosZ, + lambdaloosecollision::TimeStamp); +using LambdaLooseCol = LambdaLooseCols::iterator; + +namespace lambdaloosetrack +{ +DECLARE_SOA_INDEX_COLUMN(LambdaLooseCol, lambdaLooseCol); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Rap, rap, float); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(MassOther, massOther, float); +DECLARE_SOA_COLUMN(PrPx, prPx, float); +DECLARE_SOA_COLUMN(PrPy, prPy, float); +DECLARE_SOA_COLUMN(PrPz, prPz, float); +DECLARE_SOA_COLUMN(PosTrackId, posTrackId, int32_t); +DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int32_t); +DECLARE_SOA_COLUMN(V0Type, v0Type, int8_t); +DECLARE_SOA_COLUMN(CosPA, cosPA, float); +DECLARE_SOA_COLUMN(DcaDau, dcaDau, float); +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); +DECLARE_SOA_COLUMN(CTau, cTau, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); +DECLARE_SOA_COLUMN(PosNSigmaPr, posNSigmaPr, float); +DECLARE_SOA_COLUMN(PosNSigmaPi, posNSigmaPi, float); +DECLARE_SOA_COLUMN(NegNSigmaPr, negNSigmaPr, float); +DECLARE_SOA_COLUMN(NegNSigmaPi, negNSigmaPi, float); +DECLARE_SOA_COLUMN(PosTpcCrossedRows, posTpcCrossedRows, int16_t); +DECLARE_SOA_COLUMN(NegTpcCrossedRows, negTpcCrossedRows, int16_t); +} // namespace lambdaloosetrack + +DECLARE_SOA_TABLE(LambdaLooseTracks, "AOD", "LAMBDALOOSETRKS", + o2::soa::Index<>, + lambdaloosetrack::LambdaLooseColId, + lambdaloosetrack::Px, + lambdaloosetrack::Py, + lambdaloosetrack::Pz, + lambdaloosetrack::Pt, + lambdaloosetrack::Eta, + lambdaloosetrack::Phi, + lambdaloosetrack::Rap, + lambdaloosetrack::Mass, + lambdaloosetrack::MassOther, + lambdaloosetrack::PrPx, + lambdaloosetrack::PrPy, + lambdaloosetrack::PrPz, + lambdaloosetrack::PosTrackId, + lambdaloosetrack::NegTrackId, + lambdaloosetrack::V0Type, + lambdaloosetrack::CosPA, + lambdaloosetrack::DcaDau, + lambdaloosetrack::DcaV0ToPV, + lambdaloosetrack::CTau, + lambdaloosetrack::MK0Short, + lambdaloosetrack::PosNSigmaPr, + lambdaloosetrack::PosNSigmaPi, + lambdaloosetrack::NegNSigmaPr, + lambdaloosetrack::NegNSigmaPi, + lambdaloosetrack::PosTpcCrossedRows, + lambdaloosetrack::NegTpcCrossedRows); +using LambdaLooseTrack = LambdaLooseTracks::iterator; +} // namespace o2::aod + +#endif // PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LAMBDASPINPOLARIZATIONDERIVED_H_ diff --git a/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h b/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h index 69b4f8c1b5d..a6c9e2286ce 100644 --- a/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h +++ b/PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h @@ -18,6 +18,8 @@ #ifndef PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ #define PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ +#include "PWGMM/Mult/DataModel/bestCollisionTable.h" + #include "Common/DataModel/Multiplicity.h" #include @@ -115,7 +117,8 @@ DECLARE_SOA_TABLE(UpcLRCollisions, "AOD", "UPCLRCOLLISION", lrcorrcolltable::Multiplicity, lrcorrcolltable::TotalFT0AmplitudeA, lrcorrcolltable::TotalFT0AmplitudeC, - lrcorrcolltable::TotalFV0AmplitudeA); + lrcorrcolltable::TotalFV0AmplitudeA, + timestamp::Timestamp); using UpcLRCollision = UpcLRCollisions::iterator; DECLARE_SOA_TABLE(UpcSgLRCollisions, "AOD", "UPCSGLRCOLLISION", @@ -140,17 +143,25 @@ namespace lrcorrtrktable { DECLARE_SOA_INDEX_COLUMN(LRCollision, lrCollision); DECLARE_SOA_INDEX_COLUMN(UpcLRCollision, upcLRCollision); -DECLARE_SOA_COLUMN(Pt, pt, float); -DECLARE_SOA_COLUMN(Eta, eta, float); -DECLARE_SOA_COLUMN(Phi, phi, float); -DECLARE_SOA_COLUMN(ChannelID, channelID, int); +DECLARE_SOA_COLUMN(ChannelID, channelID, uint8_t); DECLARE_SOA_COLUMN(Amplitude, amplitude, float); -DECLARE_SOA_COLUMN(GainAmplitude, gainAmplitude, float); DECLARE_SOA_COLUMN(InvMass, invMass, float); DECLARE_SOA_COLUMN(IdPos, idPos, int64_t); DECLARE_SOA_COLUMN(IdNeg, idNeg, int64_t); DECLARE_SOA_COLUMN(TrackType, trackType, uint8_t); DECLARE_SOA_COLUMN(V0Type, v0Type, uint8_t); +DECLARE_SOA_COLUMN(AmbDegree, ambDegree, uint8_t); +DECLARE_SOA_COLUMN(TPCNClsFound, tpcNClsFound, uint8_t); +DECLARE_SOA_COLUMN(TPCNClsCrossedRows, tpcNClsCrossedRows, uint8_t); +DECLARE_SOA_COLUMN(TPCChi2NCl, tpcChi2NCl, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(IsCA, isCA, bool); +DECLARE_SOA_COLUMN(IsReassigned, isReassigned, bool); +DECLARE_SOA_COLUMN(IsTrackFT0Outer, isTrackFT0Outer, bool); + enum TrackPid { kSpCharge, kSpPion, @@ -168,9 +179,13 @@ enum V0TrackPid { DECLARE_SOA_TABLE(LRMidTracks, "AOD", "LRMIDTRACK", o2::soa::Index<>, lrcorrtrktable::LRCollisionId, + lrcorrtrktable::TPCNClsFound, + lrcorrtrktable::TPCNClsCrossedRows, + lrcorrtrktable::TPCChi2NCl, lrcorrtrktable::Pt, lrcorrtrktable::Eta, lrcorrtrktable::Phi, + lrcorrtrktable::DcaZ, lrcorrtrktable::TrackType); using LRMidTrack = LRMidTracks::iterator; @@ -179,9 +194,9 @@ DECLARE_SOA_TABLE(LRFt0aTracks, "AOD", "LRFT0ATRACK", lrcorrtrktable::LRCollisionId, lrcorrtrktable::ChannelID, lrcorrtrktable::Amplitude, - lrcorrtrktable::GainAmplitude, lrcorrtrktable::Eta, - lrcorrtrktable::Phi); + lrcorrtrktable::Phi, + lrcorrtrktable::IsTrackFT0Outer); using LRFt0aTrack = LRFt0aTracks::iterator; DECLARE_SOA_TABLE(LRFt0cTracks, "AOD", "LRFT0CTRACK", @@ -189,9 +204,9 @@ DECLARE_SOA_TABLE(LRFt0cTracks, "AOD", "LRFT0CTRACK", lrcorrtrktable::LRCollisionId, lrcorrtrktable::ChannelID, lrcorrtrktable::Amplitude, - lrcorrtrktable::GainAmplitude, lrcorrtrktable::Eta, - lrcorrtrktable::Phi); + lrcorrtrktable::Phi, + lrcorrtrktable::IsTrackFT0Outer); using LRFt0cTrack = LRFt0cTracks::iterator; DECLARE_SOA_TABLE(LRV0Tracks, "AOD", "LRV0TRACK", @@ -209,25 +224,27 @@ using LRV0Track = LRV0Tracks::iterator; DECLARE_SOA_TABLE(LRMftTracks, "AOD", "LRMFTTRACK", o2::soa::Index<>, lrcorrtrktable::LRCollisionId, + lrcorrtrktable::AmbDegree, lrcorrtrktable::Pt, lrcorrtrktable::Eta, - lrcorrtrktable::Phi); + lrcorrtrktable::Phi, + fwdtrack::NClusters, + fwdtrack::BestDCAXY, + fwdtrack::BestDCAZ, + lrcorrtrktable::IsCA, + lrcorrtrktable::IsReassigned); using LRMftTrack = LRMftTracks::iterator; -DECLARE_SOA_TABLE(LRMftBestTracks, "AOD", "LRMFTBESTTRACK", - o2::soa::Index<>, - lrcorrtrktable::LRCollisionId, - lrcorrtrktable::Pt, - lrcorrtrktable::Eta, - lrcorrtrktable::Phi); -using LRMftBestTrack = LRMftBestTracks::iterator; - DECLARE_SOA_TABLE(UpcLRMidTracks, "AOD", "UPCLRMIDTRACK", o2::soa::Index<>, lrcorrtrktable::UpcLRCollisionId, + lrcorrtrktable::TPCNClsFound, + lrcorrtrktable::TPCNClsCrossedRows, + lrcorrtrktable::TPCChi2NCl, lrcorrtrktable::Pt, lrcorrtrktable::Eta, lrcorrtrktable::Phi, + lrcorrtrktable::DcaZ, lrcorrtrktable::TrackType); using UpcLRMidTrack = UpcLRMidTracks::iterator; @@ -236,9 +253,9 @@ DECLARE_SOA_TABLE(UpcLRFt0aTracks, "AOD", "UPCLRFT0ATRACK", lrcorrtrktable::UpcLRCollisionId, lrcorrtrktable::ChannelID, lrcorrtrktable::Amplitude, - lrcorrtrktable::GainAmplitude, lrcorrtrktable::Eta, - lrcorrtrktable::Phi); + lrcorrtrktable::Phi, + lrcorrtrktable::IsTrackFT0Outer); using UpcLRFt0aTrack = UpcLRFt0aTracks::iterator; DECLARE_SOA_TABLE(UpcLRFt0cTracks, "AOD", "UPCLRFT0CTRACK", @@ -246,9 +263,9 @@ DECLARE_SOA_TABLE(UpcLRFt0cTracks, "AOD", "UPCLRFT0CTRACK", lrcorrtrktable::UpcLRCollisionId, lrcorrtrktable::ChannelID, lrcorrtrktable::Amplitude, - lrcorrtrktable::GainAmplitude, lrcorrtrktable::Eta, - lrcorrtrktable::Phi); + lrcorrtrktable::Phi, + lrcorrtrktable::IsTrackFT0Outer); using UpcLRFt0cTrack = UpcLRFt0cTracks::iterator; DECLARE_SOA_TABLE(UpcLRV0Tracks, "AOD", "UPCLRV0TRACK", @@ -266,18 +283,16 @@ using UpcLRV0Track = UpcLRV0Tracks::iterator; DECLARE_SOA_TABLE(UpcLRMftTracks, "AOD", "UPCLRMFTTRACK", o2::soa::Index<>, lrcorrtrktable::UpcLRCollisionId, + lrcorrtrktable::AmbDegree, lrcorrtrktable::Pt, lrcorrtrktable::Eta, - lrcorrtrktable::Phi); + lrcorrtrktable::Phi, + fwdtrack::NClusters, + fwdtrack::BestDCAXY, + fwdtrack::BestDCAZ, + lrcorrtrktable::IsCA, + lrcorrtrktable::IsReassigned); using UpcLRMftTrack = UpcLRMftTracks::iterator; - -DECLARE_SOA_TABLE(UpcLRMftBestTracks, "AOD", "UPCLRMFTBESTTRACK", - o2::soa::Index<>, - lrcorrtrktable::UpcLRCollisionId, - lrcorrtrktable::Pt, - lrcorrtrktable::Eta, - lrcorrtrktable::Phi); -using UpcLRMftBestTrack = UpcLRMftBestTracks::iterator; } // namespace o2::aod #endif // PWGCF_TWOPARTICLECORRELATIONS_DATAMODEL_LONGRANGEDERIVED_H_ diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt index 0669a351d1e..919a0c205e9 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/TableProducer/CMakeLists.txt @@ -33,3 +33,8 @@ o2physics_add_dpl_workflow(longrange-maker SOURCES longrangeMaker.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::TwoPartCorrCore O2Physics::SGCutParHolder COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(lambda-spin-polarization-loose-producer + SOURCES lambdaSpinPolarizationLooseProducer.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx new file mode 100644 index 00000000000..dd5ed9595b4 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/TableProducer/lambdaSpinPolarizationLooseProducer.cxx @@ -0,0 +1,482 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambdaSpinPolarizationLooseProducer.cxx +/// \brief Lambda candidates with loose selections, written as derived data for lambdaSpinPolarization +/// \author Subhadeep Roy + +#include "PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; + +enum LooseEventCutFlow { kLeAll = 1, + kLeTrigger, + kLeTvx, + kLeTFBorder, + kLeItsRofBorder, + kLeItsTpcVtx, + kLeNoSameBunchPileup, + kLeGoodZvtxFT0vsPV, + kLeGoodItsLayers, + kLeCentrality, + kLeVz, + kLeOneV0, + kLeTwoV0, + kLeWritten, + kNLooseEventCutFlow }; + +enum LooseV0Count { kLvAll = 1, + kLvV0Type, + kLvAmbiguousTracks, + kLvKShortRejected, + kLvKinematics, + kLvTopology, + kLvNoHypothesis, + kLvLambdaRow, + kLvAntiLambdaRow, + kLvBothRows, + kNLooseV0Count }; + +enum LooseHypothesis { kLooseLambda = 0, + kLooseAntiLambda, + kNLooseHypotheses }; + +static constexpr std::size_t NV0ForPair = 2; + +enum LooseCentEstimator { kLooseCentFT0M = 0, + kLooseCentFT0C }; + +struct LambdaSpinPolarizationLooseProducer { + + Produces lambdaLooseCollisionTable; + Produces lambdaLooseTrackTable; + + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator: 0=FT0M, 1=FT0C"}; + Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ (cm)"}; + Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ (cm)"}; + Configurable cMinMult{"cMinMult", 0.0, "Min centrality percentile"}; + Configurable cMaxMult{"cMaxMult", 100.0, "Max centrality percentile"}; + Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A+T0C) Run3"}; + Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "TVX Trigger Selection"}; + Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; + Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; + Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; + Configurable cPileupReject{"cPileupReject", false, "Pileup rejection"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Good ITS Layers All"}; + + Configurable cTrackMinPt{"cTrackMinPt", 0.15, "p_{T} minimum"}; + Configurable cTrackMaxPt{"cTrackMaxPt", 999.0, "p_{T} maximum"}; + Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; + Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 50, "TPC Min Crossed Rows (loosest of the variations)"}; + Configurable cMinTpcCROverCls{"cMinTpcCROverCls", -999, "TPC Min CR/Findable Cls"}; + Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 999, "TPC Max Shared Clusters"}; + Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 999, "Max TPC Chi2/ndf"}; + Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 5.0, "TPC nSigma PID cut (loosest of the variations)"}; + Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, "Remove Ambiguous Tracks"}; + + Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.05, "Min proton DCA to PV (cm)"}; + Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.1, "Min pion DCA to PV (cm)"}; + Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Min DCA between V0 daughters"}; + Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1.4, "Max DCA between V0 daughters"}; + Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Min DCA V0 to PV"}; + Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 0.2, "Max DCA V0 to PV (loosest of the variations)"}; + Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Min V0 decay radius (cm)"}; + Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Max V0 decay radius (cm)"}; + Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Min cTau (cm)"}; + Configurable cMaxV0CTau{"cMaxV0CTau", 36.0, "Max cTau (cm) (loosest of the variations)"}; + Configurable cMinV0CosPA{"cMinV0CosPA", 0.98, "Min V0 cos(PA) (loosest of the variations)"}; + Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.001, "K0s mass rejection window (narrowest of the variations)"}; + Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0s mass rejection flag"}; + + Configurable cMinV0Mass{"cMinV0Mass", 1.09, "V0 Mass Min"}; + Configurable cMaxV0Mass{"cMaxV0Mass", 1.14, "V0 Mass Max"}; + Configurable cMinV0Pt{"cMinV0Pt", 0.6, "Minimum V0 pT"}; + Configurable cMaxV0Pt{"cMaxV0Pt", 3.0, "Maximum V0 pT"}; + Configurable cMaxV0Rap{"cMaxV0Rap", 0.5, "|rap| cut"}; + Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Do Eta Analysis"}; + Configurable cV0TypeSelFlag{"cV0TypeSelFlag", true, "V0 Type Selection Flag"}; + Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; + + Configurable cMinV0PerEvent{"cMinV0PerEvent", 2, "Collisions with fewer V0s passing the loose selection are not written"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + struct LooseCandidate { + float px, py, pz, pt, eta, phi, rap, mass, massOther, prPx, prPy, prPz; + int32_t posTrackId, negTrackId; + int8_t v0Type; + float cosPA, dcaDau, dcaV0ToPV, cTau, mK0Short; + float posNSigmaPr, posNSigmaPi, negNSigmaPr, negNSigmaPi; + int16_t posTpcCrossedRows, negTpcCrossedRows; + }; + std::vector candidates; + + float cent = 0., mult = 0.; + + void init(InitContext const&) + { + if (cMinV0PerEvent.value < 1) { + LOGF(fatal, "cMinV0PerEvent must be at least 1, it is %d", cMinV0PerEvent.value); + } + const AxisSpec axisV0Mass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisV0Pt(100., 0., 10., "p_{T} (GeV/#it{c})"); + const AxisSpec axisNV0(20, -0.5, 19.5, "V0s written per collision"); + + auto hCutFlow = histos.add("Events/hEventCutFlow", "event cut flow;;collisions", kTH1D, {{kNLooseEventCutFlow - 1, 0.5, static_cast(kNLooseEventCutFlow) - 0.5}}); + hCutFlow->GetXaxis()->SetBinLabel(kLeAll, "all"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTrigger, "trigger (sel8)"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTvx, "TVX"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTFBorder, "no TF border"); + hCutFlow->GetXaxis()->SetBinLabel(kLeItsRofBorder, "no ITS ROF border"); + hCutFlow->GetXaxis()->SetBinLabel(kLeItsTpcVtx, "ITS-TPC vertex"); + hCutFlow->GetXaxis()->SetBinLabel(kLeNoSameBunchPileup, "no same-bunch pileup"); + hCutFlow->GetXaxis()->SetBinLabel(kLeGoodZvtxFT0vsPV, "good z_{vtx} FT0 vs PV"); + hCutFlow->GetXaxis()->SetBinLabel(kLeGoodItsLayers, "good ITS layers"); + hCutFlow->GetXaxis()->SetBinLabel(kLeCentrality, "centrality range"); + hCutFlow->GetXaxis()->SetBinLabel(kLeVz, "V_{z} range"); + hCutFlow->GetXaxis()->SetBinLabel(kLeOneV0, "#geq 1 V0 with a loose candidate"); + hCutFlow->GetXaxis()->SetBinLabel(kLeTwoV0, "#geq 2 V0s with a loose candidate"); + hCutFlow->GetXaxis()->SetBinLabel(kLeWritten, "written"); + + auto hV0 = histos.add("Tracks/hV0Count", "V0s;;V0s", kTH1D, {{kNLooseV0Count - 1, 0.5, static_cast(kNLooseV0Count) - 0.5}}); + hV0->GetXaxis()->SetBinLabel(kLvAll, "all"); + hV0->GetXaxis()->SetBinLabel(kLvV0Type, "rejected: V0 type"); + hV0->GetXaxis()->SetBinLabel(kLvAmbiguousTracks, "rejected: ambiguous tracks"); + hV0->GetXaxis()->SetBinLabel(kLvKShortRejected, "rejected: K^{0}_{S} mass"); + hV0->GetXaxis()->SetBinLabel(kLvKinematics, "rejected: p_{T} / y"); + hV0->GetXaxis()->SetBinLabel(kLvTopology, "rejected: topology"); + hV0->GetXaxis()->SetBinLabel(kLvNoHypothesis, "rejected: no hypothesis"); + hV0->GetXaxis()->SetBinLabel(kLvLambdaRow, "#Lambda candidate"); + hV0->GetXaxis()->SetBinLabel(kLvAntiLambdaRow, "#bar{#Lambda} candidate"); + hV0->GetXaxis()->SetBinLabel(kLvBothRows, "both candidates"); + + histos.add("Tracks/hLambdaMassVsPt", "#Lambda candidates", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Tracks/hAntiLambdaMassVsPt", "#bar{#Lambda} candidates", kTH2F, {axisV0Mass, axisV0Pt}); + histos.add("Events/hNV0Written", "written collisions", kTH1F, {axisNV0}); + } + + template + bool selCollision(C const& col) + { + histos.fill(HIST("Events/hEventCutFlow"), kLeAll); + if (cCentEstimator == kLooseCentFT0M) { + cent = col.centFT0M(); + } else if (cCentEstimator == kLooseCentFT0C) { + cent = col.centFT0C(); + } + if (cSel8Trig && !col.sel8()) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTrigger); + + if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTvx); + if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeTFBorder); + if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeItsRofBorder); + if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeItsTpcVtx); + if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeNoSameBunchPileup); + if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeGoodZvtxFT0vsPV); + if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeGoodItsLayers); + + if (cent <= cMinMult || cent >= cMaxMult) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeCentrality); + if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeVz); + + mult = col.multNTracksPV(); + return true; + } + + bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) + { + return pt > ptMin && pt < ptMax && rap < rapMax; + } + + template + bool selTrack(T const& track) + { + if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, cTrackMaxPt, cTrackEtaCut)) { + return false; + } + if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { + return false; + } + if (track.tpcCrossedRowsOverFindableCls() < cMinTpcCROverCls) { + return false; + } + if (track.tpcNClsShared() > cMaxTpcSharedClusters) { + return false; + } + if (track.tpcChi2NCl() > cMaxChi2Tpc) { + return false; + } + return true; + } + + template + bool selDaughterTracks(V const& v0, T const&, LooseHypothesis const& v0Type) + { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + if (!selTrack(posTrack) || !selTrack(negTrack)) { + return false; + } + + float dcaProton = 0., dcaPion = 0.; + if (v0Type == kLooseLambda) { + dcaProton = std::abs(v0.dcapostopv()); + dcaPion = std::abs(v0.dcanegtopv()); + } else if (v0Type == kLooseAntiLambda) { + dcaPion = std::abs(v0.dcapostopv()); + dcaProton = std::abs(v0.dcanegtopv()); + } + if (dcaProton < cMinDcaProtonToPV || dcaPion < cMinDcaPionToPV) { + return false; + } + return true; + } + + template + bool topoCutSelection(C const& col, V const& v0, T const&) + { + if (v0.dcaV0daughters() <= cMinV0DcaDaughters || v0.dcaV0daughters() >= cMaxV0DcaDaughters) { + return false; + } + if (v0.dcav0topv() <= cMinDcaV0ToPV || v0.dcav0topv() >= cMaxDcaV0ToPV) { + return false; + } + if (v0.v0radius() <= cMinV0TransRadius || v0.v0radius() >= cMaxV0TransRadius) { + return false; + } + + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + if (ctau <= cMinV0CTau || ctau >= cMaxV0CTau) { + return false; + } + if (v0.v0cosPA() <= cMinV0CosPA) { + return false; + } + return true; + } + + template + bool selLambdaDauWithTpcPid(T const& postrack, T const& negtrack) + { + float tpcNSigmaPr = 0., tpcNSigmaPi = 0.; + if constexpr (part == kLooseLambda) { + tpcNSigmaPr = postrack.tpcNSigmaPr(); + tpcNSigmaPi = negtrack.tpcNSigmaPi(); + } else { + tpcNSigmaPr = negtrack.tpcNSigmaPr(); + tpcNSigmaPi = postrack.tpcNSigmaPi(); + } + return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + } + + template + bool hasAmbiguousDaughters(V const& v0, T const&) + { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + auto posCC = posTrack.compatibleCollIds(); + auto negCC = negTrack.compatibleCollIds(); + if (posCC.size() > 1 || negCC.size() > 1) { + return true; + } + if ((posCC.size() != 0 && posCC[0] != posTrack.collisionId()) || + (negCC.size() != 0 && negCC[0] != negTrack.collisionId())) { + return true; + } + return false; + } + + using CollisionsRun3 = soa::Join; + using Tracks = soa::Join; + + void processData(CollisionsRun3::iterator const& collision, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s, Tracks const& tracks) + { + if (!selCollision(collision)) { + return; + } + + candidates.clear(); + std::size_t nV0 = 0; + for (auto const& v0 : V0s) { + histos.fill(HIST("Tracks/hV0Count"), kLvAll); + if (cV0TypeSelFlag && v0.v0Type() != cV0TypeSelection) { + histos.fill(HIST("Tracks/hV0Count"), kLvV0Type); + continue; + } + if (cRemoveAmbiguousTracks && hasAmbiguousDaughters(v0, tracks)) { + histos.fill(HIST("Tracks/hV0Count"), kLvAmbiguousTracks); + continue; + } + if (cKshortRejFlag && (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + histos.fill(HIST("Tracks/hV0Count"), kLvKShortRejected); + continue; + } + const float rap = cDoEtaAnalysis ? std::abs(v0.eta()) : std::abs(v0.yLambda()); + if (!kinCutSelection(v0.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + histos.fill(HIST("Tracks/hV0Count"), kLvKinematics); + continue; + } + if (!topoCutSelection(collision, v0, tracks)) { + histos.fill(HIST("Tracks/hV0Count"), kLvTopology); + continue; + } + + auto postrack = v0.posTrack_as(); + auto negtrack = v0.negTrack_as(); + const std::array masses = {v0.mLambda(), v0.mAntiLambda()}; + const std::array pid = {selLambdaDauWithTpcPid(postrack, negtrack), selLambdaDauWithTpcPid(postrack, negtrack)}; + const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassLambda0; + + std::size_t nRows = 0; + for (auto const& hyp : {kLooseLambda, kLooseAntiLambda}) { + const bool inMassWindow = masses[hyp] > cMinV0Mass && masses[hyp] < cMaxV0Mass; + if (!inMassWindow || !pid[hyp]) { + continue; + } + if (!selDaughterTracks(v0, tracks, hyp)) { + continue; + } + const bool isLambda = hyp == kLooseLambda; + candidates.push_back(LooseCandidate{.px = v0.px(), + .py = v0.py(), + .pz = v0.pz(), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .rap = v0.yLambda(), + .mass = masses[hyp], + .massOther = masses[isLambda ? kLooseAntiLambda : kLooseLambda], + .prPx = isLambda ? v0.pxpos() : v0.pxneg(), + .prPy = isLambda ? v0.pypos() : v0.pyneg(), + .prPz = isLambda ? v0.pzpos() : v0.pzneg(), + .posTrackId = static_cast(postrack.index()), + .negTrackId = static_cast(negtrack.index()), + .v0Type = static_cast(hyp), + .cosPA = v0.v0cosPA(), + .dcaDau = v0.dcaV0daughters(), + .dcaV0ToPV = v0.dcav0topv(), + .cTau = ctau, + .mK0Short = v0.mK0Short(), + .posNSigmaPr = postrack.tpcNSigmaPr(), + .posNSigmaPi = postrack.tpcNSigmaPi(), + .negNSigmaPr = negtrack.tpcNSigmaPr(), + .negNSigmaPi = negtrack.tpcNSigmaPi(), + .posTpcCrossedRows = postrack.tpcNClsCrossedRows(), + .negTpcCrossedRows = negtrack.tpcNClsCrossedRows()}); + if (isLambda) { + histos.fill(HIST("Tracks/hV0Count"), kLvLambdaRow); + histos.fill(HIST("Tracks/hLambdaMassVsPt"), masses[hyp], v0.pt()); + } else { + histos.fill(HIST("Tracks/hV0Count"), kLvAntiLambdaRow); + histos.fill(HIST("Tracks/hAntiLambdaMassVsPt"), masses[hyp], v0.pt()); + } + ++nRows; + } + if (nRows == 0) { + histos.fill(HIST("Tracks/hV0Count"), kLvNoHypothesis); + continue; + } + if (nRows == kNLooseHypotheses) { + histos.fill(HIST("Tracks/hV0Count"), kLvBothRows); + } + ++nV0; + } + + if (nV0 >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kLeOneV0); + } + if (nV0 >= NV0ForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kLeTwoV0); + } + if (std::cmp_less(nV0, cMinV0PerEvent.value)) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kLeWritten); + histos.fill(HIST("Events/hNV0Written"), nV0); + + auto bc = collision.bc_as(); + lambdaLooseCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), bc.timestamp()); + for (auto const& c : candidates) { + lambdaLooseTrackTable(lambdaLooseCollisionTable.lastIndex(), c.px, c.py, c.pz, c.pt, c.eta, c.phi, c.rap, c.mass, c.massOther, c.prPx, c.prPy, c.prPz, + c.posTrackId, c.negTrackId, c.v0Type, c.cosPA, c.dcaDau, c.dcaV0ToPV, c.cTau, c.mK0Short, + c.posNSigmaPr, c.posNSigmaPi, c.negNSigmaPr, c.negNSigmaPi, c.posTpcCrossedRows, c.negTpcCrossedRows); + } + } + PROCESS_SWITCH(LambdaSpinPolarizationLooseProducer, processData, "Run 3 data (or MC reconstructed as data)", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx index d17bf1418b2..d9d528c64b0 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/longrangeMaker.cxx @@ -30,6 +30,7 @@ #include "Common/Core/TrackSelectionDefaults.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -38,6 +39,7 @@ #include #include +#include #include #include #include @@ -54,8 +56,11 @@ #include #include +#include + #include +#include #include #include #include @@ -76,10 +81,20 @@ using namespace o2::constants::math; auto static constexpr CintZero = 0; auto static constexpr KminFt0cCell = 96; auto static constexpr TotFt0Channels = 208; -AxisSpec axisEvent{15, 0.5, 15.5, "#Event", "EventAxis"}; +auto static constexpr MinFt0aDeadChannelOuter = 60; +auto static constexpr MinFt0aMirrorChannelOuter = 92; +auto static constexpr MaxFt0aMirrorChannelOuter = 95; +auto static constexpr MinFt0aOuterRing = 32; +auto static constexpr Ft0cDeadChannelInner = 139; +auto static constexpr Ft0cMirrorChannelInner = 115; +auto static constexpr MinFt0cDeadChannelOuter = 176; +auto static constexpr MinFt0cMirrorChannelOuter = 144; +auto static constexpr MaxFt0cMirrorChannelOuter = 147; +auto static constexpr MinFt0cOuterRing = 144; + +AxisSpec axisEvent{20, 0.5, 20.5, "#Event", "EventAxis"}; +AxisSpec axisTrackSel{10, 0.5, 10.5, "#Track", "TrackAxis"}; auto static constexpr KminCharge = 3.0f; -static constexpr std::string_view species[] = {"Pi", "Ka", "Pr"}; -static constexpr std::array speciesIds{kPiPlus, kKPlus, kProton}; enum KindOfV0 { kLambda = 0, @@ -94,6 +109,7 @@ struct LongrangeMaker { } cfgCcdbParam; struct : ConfigurableGroup { + std::string prefix = "EventSelection_group"; Configurable isApplyTrigTvx{"isApplyTrigTvx", false, "Enable Ft0a and Ft0c coincidence"}; Configurable isApplyTfborder{"isApplyTfborder", false, "Enable TF border cut"}; Configurable isApplyItsRofborder{"isApplyItsRofborder", false, "Enable ITS ROF border cut"}; @@ -101,53 +117,70 @@ struct LongrangeMaker { Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", false, "Enable GoodZvtxFT0vsPV cut"}; Configurable isApplyGoodITSLayersAll{"isApplyGoodITSLayersAll", false, "Enable GoodITSLayersAll cut"}; Configurable isApplyExtraCorrCut{"isApplyExtraCorrCut", false, "Enable extra NPVtracks vs FTOC correlation cut"}; - Configurable npvTracksCut{"npvTracksCut", 1.0f, "Apply extra NPVtracks cut"}; - Configurable ft0cCut{"ft0cCut", 1.0f, "Apply extra FT0C cut"}; + Configurable isApplyBestCollIndex{"isApplyBestCollIndex", true, "bestCollIndex"}; Configurable isApplyNoCollInTimeRangeStandard{"isApplyNoCollInTimeRangeStandard", false, "Enable NoCollInTimeRangeStandard cut"}; + Configurable isApplyNoCollInTimeRangeStrict{"isApplyNoCollInTimeRangeStrict", false, "Enable NoCollInTimeRangeStrict cut"}; Configurable isApplyNoCollInRofStandard{"isApplyNoCollInRofStandard", false, "Enable NoCollInRofStandard cut"}; + Configurable isApplyNoCollInRofStrict{"isApplyNoCollInRofStrict", false, "Enable NoCollInRofStrict cut"}; Configurable isApplyNoHighMultCollInPrevRof{"isApplyNoHighMultCollInPrevRof", false, "Enable NoHighMultCollInPrevRof cut"}; Configurable isApplyCentFT0C{"isApplyCentFT0C", false, "Centrality based on FT0C"}; + Configurable isApplyMultNTracksPV{"isApplyMultNTracksPV", false, "Multiplicity based on MultNTracksPV"}; Configurable isApplyCentFV0A{"isApplyCentFV0A", false, "Centrality based on FV0A"}; Configurable isApplyCentFT0M{"isApplyCentFT0M", false, "Centrality based on FT0A + FT0C"}; Configurable isApplyOccuSelection{"isApplyOccuSelection", false, "Enable occupancy selection"}; Configurable cfgOccuCut{"cfgOccuCut", 1000, "Occupancy selection"}; Configurable cfgVtxCut{"cfgVtxCut", 10.0f, "vertex Z selection"}; - Configurable isApplyBestCollIndex{"isApplyBestCollIndex", true, "bestCollIndex"}; + Configurable npvTracksCut{"npvTracksCut", 1.0f, "Apply extra NPVtracks cut"}; + Configurable ft0cCut{"ft0cCut", 1.0f, "Apply extra FT0C cut"}; } cfgevtsel; struct : ConfigurableGroup { + std::string prefix = "MidTrackSelection_group"; Configurable cfgEtaCut{"cfgEtaCut", 0.8f, "Eta range to consider"}; Configurable cfgPtCutMin{"cfgPtCutMin", 0.2f, "minimum accepted track pT"}; Configurable cfgPtCutMax{"cfgPtCutMax", 10.0f, "maximum accepted track pT"}; - Configurable cfgPtCutMult{"cfgPtCutMult", 3.0f, "maximum track pT for multiplicity classification"}; + Configurable cfgPtCutMinForMult{"cfgPtCutMinForMult", 0.2f, "minimum track pT for multiplicity classification"}; + Configurable cfgPtCutMaxForMult{"cfgPtCutMaxForMult", 3.0f, "maximum track pT for multiplicity classification"}; Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.f, "cut on minimum number of TPC crossed rows"}; Configurable minTPCNClsFound{"minTPCNClsFound", 50.f, "cut on minimum value of TPC found clusters"}; - Configurable maxDcaZ{"maxDcaZ", 2.f, "cut on maximum abs value of DCA z"}; + Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "cut on minNCrossedRowsOverFindableClustersTPC"}; Configurable maxChi2PerClusterTPC{"maxChi2PerClusterTPC", 4.f, "cut on maximum value of TPC chi2 per cluster"}; + Configurable maxChi2PerClusterITS{"maxChi2PerClusterITS", 36.f, "cut on maximum value of ITS chi2 per cluster"}; + Configurable maxDcaZ{"maxDcaZ", 2.0f, "cut on maximum abs value of DCA z"}; + Configurable maxDcaXY{"maxDcaXY", 1.0f, "cut on maximum abs value of DCA xy"}; + Configurable applyEffCorr{"applyEffCorr", true, "Enable efficiency correction"}; + Configurable cfgEffccdbPath{"cfgEffccdbPath", "Users/a/abmodak/Efficiency/OO/default", "Browse track eff object from CCDB"}; } cfgtrksel; struct : ConfigurableGroup { - Configurable cfigMftEtaMax{"cfigMftEtaMax", -2.5f, "Maximum MFT eta cut"}; - Configurable cfigMftEtaMin{"cfigMftEtaMin", -3.6f, "Minimum MFT eta cut"}; - Configurable cfigMftDcaxy{"cfigMftDcaxy", 2.0f, "cut on DCA xy for MFT tracks"}; - Configurable cfigMftDcaz{"cfigMftDcaz", 2.0f, "cut on DCA z for MFT tracks"}; - Configurable cfigMftCluster{"cfigMftCluster", 5, "cut on MFT Cluster"}; + std::string prefix = "MftTrackSelection_group"; + Configurable cfgUseChi2Cut{"cfgUseChi2Cut", false, "Use condition on MFT track: chi2/Nclusters"}; Configurable useMftPtCut{"useMftPtCut", true, "Choose to apply MFT track pT cut"}; + Configurable cfgMftCluster{"cfgMftCluster", 5, "cut on MFT Cluster"}; + Configurable cfgMftEtaMax{"cfgMftEtaMax", -2.4f, "Maximum MFT eta cut"}; + Configurable cfgMftEtaMin{"cfgMftEtaMin", -3.6f, "Minimum MFT eta cut"}; + Configurable cfgMftDcaxy{"cfgMftDcaxy", 2.0f, "cut on DCA xy for MFT tracks"}; + Configurable cfgMftDcaz{"cfgMftDcaz", 2.0f, "cut on DCA z for MFT tracks"}; Configurable cfgMftPtCutMin{"cfgMftPtCutMin", 0.f, "minimum accepted MFT track pT"}; Configurable cfgMftPtCutMax{"cfgMftPtCutMax", 10.f, "maximum accepted MFT track pT"}; + Configurable cfgMaxChi2NCl{"cfgMaxChi2NCl", 1.f, "Cut on MFT track: chi2/Nclusters"}; } cfgmfttrksel; struct : ConfigurableGroup { - Configurable cfigFt0aEtaMax{"cfigFt0aEtaMax", 4.9f, "Maximum FT0A eta cut"}; - Configurable cfigFt0aEtaMin{"cfigFt0aEtaMin", 3.5f, "Minimum FT0A eta cut"}; - Configurable cfigFt0cEtaMax{"cfigFt0cEtaMax", -2.1f, "Maximum FT0C eta cut"}; - Configurable cfigFt0cEtaMin{"cfigFt0cEtaMin", -3.3f, "Minimum FT0C eta cut"}; - Configurable cfigVerbosity{"cfigVerbosity", 0, "print statement"}; + std::string prefix = "FitTrackSelection_group"; + Configurable cfgFt0aEtaMax{"cfgFt0aEtaMax", 4.9f, "Maximum FT0A eta cut"}; + Configurable cfgFt0aEtaMin{"cfgFt0aEtaMin", 3.5f, "Minimum FT0A eta cut"}; + Configurable cfgFt0cEtaMax{"cfgFt0cEtaMax", -2.1f, "Maximum FT0C eta cut"}; + Configurable cfgFt0cEtaMin{"cfgFt0cEtaMin", -3.3f, "Minimum FT0C eta cut"}; + Configurable cfgVerbosity{"cfgVerbosity", 0, "print statement"}; Configurable useGainCalib{"useGainCalib", true, "use gain calibration"}; - Configurable ConfGainPath{"ConfGainPath", "Analysis/EventPlane/GainEq/FT0", "Path to gain calibration"}; + Configurable applyMirrorFt0a{"applyMirrorFt0a", true, "consider Mirror 92-95 into 60-63"}; + Configurable applyMirrorFt0c{"applyMirrorFt0c", true, "consider Mirror 144-147 into 176-179 and Mirror 115 into 139"}; + Configurable confGainPath{"confGainPath", "Analysis/EventPlane/GainEq/FT0", "Path to gain calibration"}; } cfgfittrksel; struct : ConfigurableGroup { + std::string prefix = "V0TrackSelection_group"; Configurable minTPCcrossedrows{"minTPCcrossedrows", 70.f, "cut on minimum number of crossed rows in TPC"}; Configurable minTPCcrossedrowsoverfindcls{"minTPCcrossedrowsoverfindcls", 0.8f, "cut on minimum value of the ratio between crossed rows and findable clusters in the TPC"}; Configurable v0etaCut{"v0etaCut", 0.8f, "maximum v0 track pseudorapidity"}; @@ -172,26 +205,51 @@ struct LongrangeMaker { Configurable minV0DcaPiLambda{"minV0DcaPiLambda", 0.2f, "Min V0 pion DCA for Lambda"}; Configurable minV0DcaPr{"minV0DcaPr", 0.07f, "Min V0 proton DCA for Lambda"}; Configurable maxLambdaLifeTime{"maxLambdaLifeTime", 30.0f, "Maximum Lambda lifetime (in cm)"}; + + Configurable rejK0sMassWindow{"rejK0sMassWindow", 0.012f, "K0s mass rejection window for Lambda (12 MeV/c^2)"}; + Configurable cfgQAV0Mask{"cfgQAV0Mask", 7, "Bitmask for V0 QA: 1=K0s, 2=Lambda, 4=ALambda, 7=All"}; } cfgv0trksel; + struct : ConfigurableGroup { + std::string prefix = "ConfigAxis_group"; + ConfigurableAxis axisAmplitude{"axisAmplitude", {5000, 0, 10000}, "FT0 amplitude"}; + ConfigurableAxis axisChannel{"axisChannel", {208, 0, 208}, "FT0 channel"}; + ConfigurableAxis axisMFTAmbDegree{"axisMFTAmbDegree", {50, -0.5, 49.5}, "Track Ambiguity axis"}; + ConfigurableAxis axisEta = {"axisEta", {100, -5, 5}, "eta axis"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "#phi axis"}; + + ConfigurableAxis axisV0Species{"axisV0Species", {3, -0.5, 2.5}, "V0 Species (0=K0s, 1=#Lambda, 2=#bar{#Lambda})"}; + ConfigurableAxis axisAlpha{"axisAlpha", {100, -1.0, 1.0}, "Armenteros #alpha"}; + ConfigurableAxis axisQt{"axisQt", {100, 0.0, 0.42}, "Armenteros q_{T} (GeV/c)"}; + ConfigurableAxis axisV0Pt{"axisV0Pt", {100, 0.0, 10.0}, "V0 p_{T} (GeV/c)"}; + ConfigurableAxis axisMass{"axisMass", {150, 0.4, 1.2}, "Invariant Mass (GeV/c^{2})"}; + ConfigurableAxis axisDcaV0Dau{"axisDcaV0Dau", {100, 0.0, 1.5}, "DCA between V0 Daughters (cm)"}; + ConfigurableAxis axisDcaPosToPv{"axisDcaPosToPv", {100, 0.0, 10.0}, "DCA Pos-Prong to PV (cm)"}; + ConfigurableAxis axisDcaNegToPv{"axisDcaNegToPv", {100, 0.0, 10.0}, "DCA Neg-Prong to PV (cm)"}; + ConfigurableAxis axisDcaV0ToPv{"axisDcaV0ToPv", {100, 0.0, 5.0}, "DCA V0 to PV (cm)"}; + ConfigurableAxis axisCosPA{"axisCosPA", {100, 0.97, 1.0}, "Cosine of Pointing Angle (cos#theta_{PA})"}; + ConfigurableAxis axisRadius{"axisRadius", {100, 0.0, 100.0}, "V0 2D Decay Radius (cm)"}; + ConfigurableAxis axisCtau{"axisCtau", {100, 0.0, 50.0}, "Proper Lifetime c#tau (cm)"}; + + ConfigurableAxis axisPQA{"axisPQA", {100, 0.0, 10.0}, "p (GeV/c)"}; + ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "TPC dE/dx (a.u.)"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 15, 25, 30, 40, 50, 60, 80, 100, 150, 200}, "Multiplicity / Centrality"}; + + ConfigurableAxis axisMultQA{"axisMultQA", {500, -0.5, 499.5}, "multiplicity QA axis"}; + ConfigurableAxis axisCentQA{"axisCentQA", {100, 0., 100.}, "centrality QA axis"}; + } cfgAxis; + Configurable> itsNsigmaPidCut{"itsNsigmaPidCut", std::vector{3, 2.5, 2, -3, -2.5, -2}, "ITS n-sigma cut for pions_posNsigma, kaons_posNsigma, protons_posNsigma, pions_negNsigma, kaons_negNsigma, protons_negNsigma"}; Configurable> tpcNsigmaPidCut{"tpcNsigmaPidCut", std::vector{1.5, 1.5, 1.5, -1.5, -1.5, -1.5}, "TPC n-sigma cut for pions_posNsigma, kaons_posNsigma, protons_posNsigma, pions_negNsigma, kaons_negNsigma, protons_negNsigma"}; Configurable> tofNsigmaPidCut{"tofNsigmaPidCut", std::vector{1.5, 1.5, 1.5, -1.5, -1.5, -1.5}, "TOF n-sigma cut for pions_posNsigma, kaons_posNsigma, protons_posNsigma, pions_negNsigma, kaons_negNsigma, protons_negNsigma"}; Configurable cfgTofPidPtCut{"cfgTofPidPtCut", 0.3f, "Minimum pt to use TOF N-sigma"}; Configurable isUseItsPid{"isUseItsPid", false, "Use ITS PID for particle identification"}; - Configurable isUseCentEst{"isUseCentEst", false, "Centrality based classification"}; - - ConfigurableAxis vtxHistBin{"vtxHistBin", {20, -10, 10}, ""}; - ConfigurableAxis multHistBin{"multHistBin", {100, 0, 100}, ""}; - ConfigurableAxis etaHistBin{"etaHistBin", {20, -1, 1}, ""}; - ConfigurableAxis ptHistBin{"ptHistBin", {10, 0, 10}, ""}; - ConfigurableAxis speciesHistBin{"speciesHistBin", {4, 0.5, 4.5}, ""}; Service ccdb; Service pdg; o2::ccdb::CcdbApi ccdbApi; o2::ft0::Geometry ft0Det; - std::vector* offsetFT0; + std::vector* offsetFT0{nullptr}; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; TrackSelection myTrackFilter; @@ -207,6 +265,10 @@ struct LongrangeMaker { Configurable sgCuts{"sgCuts", {}, "SG event cuts"}; Configurable cfgGapSide{"cfgGapSide", 2, "cut on UPC events"}; + // corrections + TH3D* hTrkEff = nullptr; + bool fLoadTrkEffCorr = false; + void init(InitContext&) { ccdb->setURL(cfgCcdbParam.cfgURL); @@ -230,40 +292,75 @@ struct LongrangeMaker { x->SetBinLabel(6, "ApplyGoodZvtxFT0vsPV"); x->SetBinLabel(7, "ApplyGoodITSLayersAll"); x->SetBinLabel(8, "ApplyExtraCorrCut"); - x->SetBinLabel(9, "ApplyNoCollInTimeRangeStandard"); - x->SetBinLabel(10, "ApplyNoCollInRofStandard"); - x->SetBinLabel(11, "ApplyNoHighMultCollInPrevRof"); - x->SetBinLabel(12, "ApplyOccupancySelection"); - histos.add("hSelectionResult", "hSelectionResult", kTH1I, {{5, -0.5, 4.5}}); + x->SetBinLabel(9, "ApplyNoCollInRofStandard"); + x->SetBinLabel(10, "ApplyNoCollInRofStrict"); + x->SetBinLabel(11, "ApplyNoCollInTimeRangeStandard"); + x->SetBinLabel(12, "ApplyNoCollInTimeRangeStrict"); + x->SetBinLabel(13, "ApplyNoHighMultCollInPrevRof"); + x->SetBinLabel(14, "ApplyOccupancySelection"); + x->SetBinLabel(15, "ZvertexSelection"); - AxisSpec axisVtx = {vtxHistBin, "Vertex", "VtxAxis"}; - AxisSpec axisMult = {multHistBin, "Mult", "MultAxis"}; - AxisSpec axisEta = {etaHistBin, "Eta", "EtaAxis"}; - AxisSpec axisPt = {ptHistBin, "Pt", "PtAxis"}; - AxisSpec axisSpecies = {speciesHistBin, "Species", "SpeciesAxis"}; - - if (doprocessTPCtrackEff || doprocessMFTtrackEff) { - histos.add("hGenMCdndpt", "hGenMCdndpt", kTHnSparseD, {axisVtx, axisMult, axisEta, axisPt, axisSpecies}, false); - histos.add("hRecMCdndpt", "hRecMCdndpt", kTHnSparseD, {axisVtx, axisMult, axisEta, axisPt, axisSpecies}, false); - auto hGenSpecies = histos.get(HIST("hGenMCdndpt")); - auto hRecSpecies = histos.get(HIST("hRecMCdndpt")); - auto* axisGen = hGenSpecies->GetAxis(4); - auto* axisRec = hRecSpecies->GetAxis(4); - for (auto i = 0U; i < speciesIds.size(); ++i) { - axisGen->SetBinLabel(i + 1, species[i].data()); - axisRec->SetBinLabel(i + 1, species[i].data()); - } - axisGen->SetBinLabel(4, "Other"); - axisRec->SetBinLabel(4, "Other"); - } + histos.add("hSelectionResult", "hSelectionResult", kTH1I, {{5, -0.5, 4.5}}); - myTrackFilter = getGlobalTrackSelectionRun3ITSMatch(TrackSelection::GlobalTrackRun3ITSMatching::Run3ITSibAny, TrackSelection::GlobalTrackRun3DCAxyCut::Default); + histos.add("hMftTrkSel", "hMftTrkSel", kTH1D, {axisTrackSel}, false); + auto hMftTrkStat = histos.get(HIST("hMftTrkSel")); + auto* xMftTrk = hMftTrkStat->GetXaxis(); + xMftTrk->SetBinLabel(1, "All"); + xMftTrk->SetBinLabel(2, "Cluster selection"); + xMftTrk->SetBinLabel(3, "Chi2/NCluster selection"); + xMftTrk->SetBinLabel(4, "eta selection"); + xMftTrk->SetBinLabel(5, "pT selection"); + xMftTrk->SetBinLabel(6, "CA track selection"); + + histos.add("hMftBestTrkSel", "hMftBestTrkSel", kTH1D, {axisTrackSel}, false); + auto hMftBestTrkStat = histos.get(HIST("hMftBestTrkSel")); + auto* xMftBestTrk = hMftBestTrkStat->GetXaxis(); + xMftBestTrk->SetBinLabel(1, "All"); + xMftBestTrk->SetBinLabel(2, "BestSelCollID selection"); + xMftBestTrk->SetBinLabel(3, "Orphan track selection"); + xMftBestTrk->SetBinLabel(4, "DCAxy selection"); + xMftBestTrk->SetBinLabel(5, "DCAz selection"); + + histos.add("FT0A_Amp", "FT0A_Amp", kTH1D, {cfgAxis.axisAmplitude}); + histos.add("FT0A_Amp_gaincorrected", "FT0A_Amp_gaincorrected", kTH1D, {cfgAxis.axisAmplitude}); + histos.add("FT0A_Channel_vs_Amp", "FT0A_Channel_vs_Amp", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); + histos.add("FT0A_Channel_vs_Amp_gaincorrected", "FT0A_Channel_vs_Amp_gaincorrected", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); + histos.add("FT0A_Channel_vs_eta", "FT0A_Channel_vs_eta", kTH2D, {cfgAxis.axisEta, cfgAxis.axisChannel}); + histos.add("FT0A_Channel_vs_phi", "FT0A_Channel_vs_phi", kTH2D, {cfgAxis.axisPhi, cfgAxis.axisChannel}); + + histos.add("FT0C_Amp", "FT0C_Amp", kTH1D, {cfgAxis.axisAmplitude}); + histos.add("FT0C_Amp_gaincorrected", "FT0C_Amp_gaincorrected", kTH1D, {cfgAxis.axisAmplitude}); + histos.add("FT0C_Channel_vs_Amp", "FT0C_Channel_vs_Amp", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); + histos.add("FT0C_Channel_vs_Amp_gaincorrected", "FT0C_Channel_vs_Amp_gaincorrected", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); + histos.add("FT0C_Channel_vs_eta", "FT0C_Channel_vs_eta", kTH2D, {cfgAxis.axisEta, cfgAxis.axisChannel}); + histos.add("FT0C_Channel_vs_phi", "FT0C_Channel_vs_phi", kTH2D, {cfgAxis.axisPhi, cfgAxis.axisChannel}); + histos.add("h3DVtxZetaPhi", "", kTH3D, {{20, -10, 10}, {16, -0.8, +0.8}, {100, 0., TwoPI}}); + + histos.add("hArmenteros_before", "Armenteros-Podolanski (Before Cuts);#alpha;q_{T} (GeV/c)", kTH2D, {cfgAxis.axisAlpha, cfgAxis.axisQt}); + histos.add("hArmenteros_after", "Armenteros-Podolanski (After Cuts);V0 Species;#alpha;q_{T} (GeV/c)", kTH3D, {cfgAxis.axisV0Species, cfgAxis.axisAlpha, cfgAxis.axisQt}); + histos.add("hTopoQA", "V0 Topological and Kinematic QA;V0 Species;DCA between V0 Daughters (cm);DCA Pos-Prong to PV (cm);DCA Neg-Prong to PV (cm);DCA V0 to PV (cm);Cosine of Pointing Angle (cos#theta_{PA});V0 2D Decay Radius (cm);Proper Lifetime c#tau (cm);V0 p_{T} (GeV/c);Invariant Mass (GeV/c^{2})", kTHnSparseF, {cfgAxis.axisV0Species, cfgAxis.axisDcaV0Dau, cfgAxis.axisDcaPosToPv, cfgAxis.axisDcaNegToPv, cfgAxis.axisDcaV0ToPv, cfgAxis.axisCosPA, cfgAxis.axisRadius, cfgAxis.axisCtau, cfgAxis.axisV0Pt, cfgAxis.axisMass}); + + // V0 Daughter dE/dx Histograms + histos.add("hTpcdEdx_pos_before", "Pos-prong dE/dx Before PID;V0 Species;Multiplicity;p (GeV/c);TPC dE/dx", kTHnSparseF, {cfgAxis.axisV0Species, cfgAxis.axisMultiplicity, cfgAxis.axisPQA, cfgAxis.axisTpcSignal}); + histos.add("hTpcdEdx_neg_before", "Neg-prong dE/dx Before PID;V0 Species;Multiplicity;p (GeV/c);TPC dE/dx", kTHnSparseF, {cfgAxis.axisV0Species, cfgAxis.axisMultiplicity, cfgAxis.axisPQA, cfgAxis.axisTpcSignal}); + + histos.add("hTpcdEdx_pos_after", "Pos-prong dE/dx After PID;V0 Species;Multiplicity;p (GeV/c);TPC dE/dx", kTHnSparseF, {cfgAxis.axisV0Species, cfgAxis.axisMultiplicity, cfgAxis.axisPQA, cfgAxis.axisTpcSignal}); + histos.add("hTpcdEdx_neg_after", "Neg-prong dE/dx After PID;V0 Species;Multiplicity;p (GeV/c);TPC dE/dx", kTHnSparseF, {cfgAxis.axisV0Species, cfgAxis.axisMultiplicity, cfgAxis.axisPQA, cfgAxis.axisTpcSignal}); + + histos.add("Nch_vs_Centrality", "Nch_vs_Centrality", kTH2D, {cfgAxis.axisCentQA, cfgAxis.axisMultQA}); + histos.add("PVtracks_vs_Centrality", "PVtracks_vs_Centrality", kTH2D, {cfgAxis.axisCentQA, cfgAxis.axisMultQA}); + + myTrackFilter = getGlobalTrackSelectionRun3ITSMatch(TrackSelection::GlobalTrackRun3ITSMatching::Run3ITSibAny, + TrackSelection::GlobalTrackRun3DCAxyCut::Default); myTrackFilter.SetPtRange(cfgtrksel.cfgPtCutMin, cfgtrksel.cfgPtCutMax); myTrackFilter.SetEtaRange(-cfgtrksel.cfgEtaCut, cfgtrksel.cfgEtaCut); myTrackFilter.SetMinNCrossedRowsTPC(cfgtrksel.minNCrossedRowsTPC); + myTrackFilter.SetMinNCrossedRowsOverFindableClustersTPC(cfgtrksel.minNCrossedRowsOverFindableClustersTPC); myTrackFilter.SetMinNClustersTPC(cfgtrksel.minTPCNClsFound); - myTrackFilter.SetMaxDcaZ(cfgtrksel.maxDcaZ); myTrackFilter.SetMaxChi2PerClusterTPC(cfgtrksel.maxChi2PerClusterTPC); + myTrackFilter.SetMaxChi2PerClusterITS(cfgtrksel.maxChi2PerClusterITS); + myTrackFilter.SetMaxDcaZ(cfgtrksel.maxDcaZ); + myTrackFilter.SetMaxDcaXYPtDep([scale = cfgtrksel.maxDcaXY.value](float pt) { return scale * (0.0105f + 0.0350f / std::pow(pt, 1.1f)); }); myTrackFilter.print(); tofNsigmaCut = tofNsigmaPidCut; @@ -285,7 +382,6 @@ struct LongrangeMaker { Produces lrft0atracks; Produces lrft0ctracks; Produces lrmfttracks; - Produces lrmftbesttracks; Produces lrv0tracks; Produces upclrcollision; @@ -296,7 +392,6 @@ struct LongrangeMaker { Produces upclrft0atracks; Produces upclrft0ctracks; Produces upclrmfttracks; - Produces upclrmftbesttracks; Produces upclrv0tracks; Filter fTracksEta = nabs(aod::track::eta) < cfgtrksel.cfgEtaCut; @@ -309,20 +404,20 @@ struct LongrangeMaker { std::vector ft0gainvalues{}; void processData(CollTable::iterator const& col, TrksTable const& tracks, - aod::FT0s const&, MftTrkTable const& mfttracks, - soa::SmallGroups const& besttracks, + aod::FT0s const&, MftTrkTable const&, + soa::SmallGroups const& reassoMftTracks, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) { if (!isEventSelected(col)) { return; } - auto multiplicity = countNTracks(tracks); - auto centrality = selColCent(col); auto bc = col.bc_as(); + loadEffCorrection(bc.timestamp()); + // retrieve FT0 gain info from CCDB ft0gainvalues.clear(); ft0gainvalues = {}; if (cfgfittrksel.useGainCalib) { - const auto ft0GainObj = ccdb->getForTimeStamp>(cfgfittrksel.ConfGainPath, bc.timestamp()); + const auto ft0GainObj = ccdb->getForTimeStamp>(cfgfittrksel.confGainPath, bc.timestamp()); if (!ft0GainObj) { for (auto i{0u}; i < TotFt0Channels; i++) { ft0gainvalues.push_back(1.); @@ -335,6 +430,14 @@ struct LongrangeMaker { ft0gainvalues.push_back(1.); } } + float multiplicity = countNTracks(tracks, col.posZ()); + float centrality = selColCent(col); + histos.fill(HIST("Nch_vs_Centrality"), col.centFT0C(), multiplicity); + histos.fill(HIST("PVtracks_vs_Centrality"), col.centFT0C(), col.multNTracksPV()); + + if (cfgfittrksel.cfgVerbosity > 0) { + LOGF(info, "Event multiplicity = %f | centrality = %f", multiplicity, centrality); + } lrcollision(bc.runNumber(), col.posZ(), multiplicity, centrality, bc.timestamp()); // track loop @@ -344,10 +447,28 @@ struct LongrangeMaker { if (!myTrackFilter.IsSelected(track)) continue; auto pid = getTrackPID(track); - lrmidtracks(lrcollision.lastIndex(), track.pt(), track.eta(), track.phi(), aod::lrcorrtrktable::kSpCharge); + lrmidtracks(lrcollision.lastIndex(), + track.tpcNClsFound(), + track.tpcNClsCrossedRows(), + track.tpcChi2NCl(), + track.pt(), + track.eta(), + track.phi(), + track.dcaZ(), + aod::lrcorrtrktable::kSpCharge); + if (pid != aod::lrcorrtrktable::kSpCharge) { - lrmidtracks(lrcollision.lastIndex(), track.pt(), track.eta(), track.phi(), pid); + lrmidtracks(lrcollision.lastIndex(), + track.tpcNClsFound(), + track.tpcNClsCrossedRows(), + track.tpcChi2NCl(), + track.pt(), + track.eta(), + track.phi(), + track.dcaZ(), + pid); } + histos.fill(HIST("h3DVtxZetaPhi"), col.posZ(), track.eta(), track.phi()); } // ft0 loop @@ -359,10 +480,35 @@ struct LongrangeMaker { auto phi = getPhiFT0(chanelid, 0); auto eta = getEtaFT0(chanelid, 0); auto gainampl = ampl / ft0gainvalues[chanelid]; - if (cfgfittrksel.cfigVerbosity > 0) { + if (cfgfittrksel.cfgVerbosity > 0) { LOGF(info, "FT0A info: Channel = %d | indexchannel = %d | %f | %f", chanelid, iCh, ft0gainvalues[chanelid], ft0gainvalues[iCh]); } - lrft0atracks(lrcollision.lastIndex(), chanelid, ampl, gainampl, eta, phi); + if (eta > cfgfittrksel.cfgFt0aEtaMax || eta < cfgfittrksel.cfgFt0aEtaMin) { + continue; + } + lrft0atracks(lrcollision.lastIndex(), + chanelid, + gainampl, + eta, + phi, + chanelid >= MinFt0aOuterRing); + if (cfgfittrksel.applyMirrorFt0a) { + int mirrorId = getFt0aDeadChannelId(chanelid); + if (mirrorId >= 0) { + lrft0atracks(lrcollision.lastIndex(), + mirrorId, + gainampl, + getEtaFT0(mirrorId, 0), + getPhiFT0(mirrorId, 0), + mirrorId >= MinFt0aOuterRing); + } + } + histos.fill(HIST("FT0A_Amp"), ampl); + histos.fill(HIST("FT0A_Channel_vs_Amp"), chanelid, ampl); + histos.fill(HIST("FT0A_Amp_gaincorrected"), gainampl); + histos.fill(HIST("FT0A_Channel_vs_Amp_gaincorrected"), chanelid, gainampl); + histos.fill(HIST("FT0A_Channel_vs_eta"), eta, chanelid); + histos.fill(HIST("FT0A_Channel_vs_phi"), phi, chanelid); } for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { auto chanelid = ft0.channelC()[iCh] + KminFt0cCell; @@ -370,34 +516,58 @@ struct LongrangeMaker { auto phi = getPhiFT0(chanelid, 1); auto eta = getEtaFT0(chanelid, 1); auto gainampl = ampl / ft0gainvalues[chanelid]; - if (cfgfittrksel.cfigVerbosity > 0) { + if (cfgfittrksel.cfgVerbosity > 0) { LOGF(info, "FT0C info: Channel = %d | indexchannel = %d | %f | %f", chanelid, iCh, ft0gainvalues[chanelid], ft0gainvalues[iCh]); } - lrft0ctracks(lrcollision.lastIndex(), chanelid, ampl, gainampl, eta, phi); + if (eta > cfgfittrksel.cfgFt0cEtaMax || eta < cfgfittrksel.cfgFt0cEtaMin) { + continue; + } + lrft0ctracks(lrcollision.lastIndex(), + chanelid, + gainampl, + eta, + phi, + chanelid >= MinFt0cOuterRing); + if (cfgfittrksel.applyMirrorFt0c) { + int mirrorId = getFt0cDeadChannelId(chanelid); + if (mirrorId >= 0) { + lrft0ctracks(lrcollision.lastIndex(), + mirrorId, + gainampl, + getEtaFT0(mirrorId, 1), + getPhiFT0(mirrorId, 1), + mirrorId >= MinFt0cOuterRing); + } + } + histos.fill(HIST("FT0C_Amp"), ampl); + histos.fill(HIST("FT0C_Channel_vs_Amp"), chanelid, ampl); + histos.fill(HIST("FT0C_Amp_gaincorrected"), gainampl); + histos.fill(HIST("FT0C_Channel_vs_Amp_gaincorrected"), chanelid, gainampl); + histos.fill(HIST("FT0C_Channel_vs_eta"), eta, chanelid); + histos.fill(HIST("FT0C_Channel_vs_phi"), phi, chanelid); } } - // mft loop - for (const auto& track : mfttracks) { - if (!isMftTrackSelected(track)) + // mft tracks + for (const auto& reassoMftTrack : reassoMftTracks) { + if (!isMftBestTrackSelected(reassoMftTrack)) + continue; + auto itrack = reassoMftTrack.template mfttrack_as(); + if (!isMftTrackSelected(itrack)) { continue; - auto phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmfttracks(lrcollision.lastIndex(), track.pt(), track.eta(), phi); - } - - if (besttracks.size() > 0) { - for (const auto& besttrack : besttracks) { - if (!isMftBestTrackSelected(besttrack)) - continue; - auto track = besttrack.mfttrack(); - if (!isMftTrackSelected(track)) { - continue; - } - auto phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmftbesttracks(lrcollision.lastIndex(), track.pt(), track.eta(), phi); } + auto phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + lrmfttracks(lrcollision.lastIndex(), + reassoMftTrack.ambDegree(), + itrack.pt(), + itrack.eta(), + phi, + itrack.nClusters(), + reassoMftTrack.bestDCAXY(), + reassoMftTrack.bestDCAZ(), + itrack.isCA(), + itrack.collisionId() != reassoMftTrack.bestCollisionId()); } // v0 loop @@ -411,8 +581,14 @@ struct LongrangeMaker { // K0short if (isSelectK0s(col, v0)) { // candidate is K0s - lrv0tracks(lrcollision.lastIndex(), posTrack.globalIndex(), negTrack.globalIndex(), - v0.pt(), v0.eta(), v0.phi(), v0.mK0Short(), aod::lrcorrtrktable::kSpK0short); + lrv0tracks(lrcollision.lastIndex(), + posTrack.globalIndex(), + negTrack.globalIndex(), + v0.pt(), + v0.eta(), + v0.phi(), + v0.mK0Short(), + aod::lrcorrtrktable::kSpK0short); } // Lambda and Anti-Lambda @@ -422,13 +598,25 @@ struct LongrangeMaker { // Note: candidate compatible with Lambda and Anti-Lambda hypothesis are counted twice (once for each hypothesis) if (lambdaTag) { // candidate is Lambda massV0 = v0.mLambda(); - lrv0tracks(lrcollision.lastIndex(), posTrack.globalIndex(), negTrack.globalIndex(), - v0.pt(), v0.eta(), v0.phi(), massV0, aod::lrcorrtrktable::kSpLambda); + lrv0tracks(lrcollision.lastIndex(), + posTrack.globalIndex(), + negTrack.globalIndex(), + v0.pt(), + v0.eta(), + v0.phi(), + massV0, + aod::lrcorrtrktable::kSpLambda); } if (antilambdaTag) { // candidate is Anti-lambda massV0 = v0.mAntiLambda(); - lrv0tracks(lrcollision.lastIndex(), posTrack.globalIndex(), negTrack.globalIndex(), - v0.pt(), v0.eta(), v0.phi(), massV0, aod::lrcorrtrktable::kSpALambda); + lrv0tracks(lrcollision.lastIndex(), + posTrack.globalIndex(), + negTrack.globalIndex(), + v0.pt(), + v0.eta(), + v0.phi(), + massV0, + aod::lrcorrtrktable::kSpALambda); } // end of Lambda and Anti-Lambda processing } } // process function @@ -436,8 +624,8 @@ struct LongrangeMaker { void processUpc(CollTable::iterator const& col, BCs const& bcs, TrksTable const& tracks, aod::Zdcs const&, aod::FV0As const& fv0as, aod::FT0s const& ft0s, - aod::FDDs const& fdds, MftTrkTable const& mfttracks, - soa::SmallGroups const& besttracks, + aod::FDDs const& fdds, MftTrkTable const&, + soa::SmallGroups const& reassoMftTracks, aod::V0Datas const& V0s) { if (!isEventSelected(col)) { @@ -449,6 +637,7 @@ struct LongrangeMaker { } auto bc = col.template foundBC_as(); + loadEffCorrection(bc.timestamp()); auto newbc = bc; // obtain slice of compatible BCs auto bcRange = udhelpers::compatibleBCs(col, cfgSgCuts.NDtcoll(), bcs, cfgSgCuts.minNBCs()); @@ -464,8 +653,8 @@ struct LongrangeMaker { upchelpers::FITInfo fitInfo{}; udhelpers::getFITinfo(fitInfo, newbc, bcs, ft0s, fv0as, fdds); - auto multiplicity = countNTracks(tracks); - upclrcollision(bc.globalBC(), bc.runNumber(), col.posZ(), multiplicity, fitInfo.ampFT0A, fitInfo.ampFT0C, fitInfo.timeFV0A); + float multiplicity = countNTracks(tracks, col.posZ()); + upclrcollision(bc.globalBC(), bc.runNumber(), col.posZ(), multiplicity, fitInfo.ampFT0A, fitInfo.ampFT0C, fitInfo.timeFV0A, bc.timestamp()); upcsglrcollision(issgevent); if (newbc.has_zdc()) { auto zdc = newbc.zdc(); @@ -481,9 +670,26 @@ struct LongrangeMaker { if (!myTrackFilter.IsSelected(track)) continue; auto pid = getTrackPID(track); - upclrmidtracks(upclrcollision.lastIndex(), track.pt(), track.eta(), track.phi(), aod::lrcorrtrktable::kSpCharge); + upclrmidtracks(upclrcollision.lastIndex(), + track.tpcNClsFound(), + track.tpcNClsCrossedRows(), + track.tpcChi2NCl(), + track.pt(), + track.eta(), + track.phi(), + track.dcaZ(), + aod::lrcorrtrktable::kSpCharge); + if (pid != aod::lrcorrtrktable::kSpCharge) { - upclrmidtracks(upclrcollision.lastIndex(), track.pt(), track.eta(), track.phi(), pid); + upclrmidtracks(upclrcollision.lastIndex(), + track.tpcNClsFound(), + track.tpcNClsCrossedRows(), + track.tpcChi2NCl(), + track.pt(), + track.eta(), + track.phi(), + track.dcaZ(), + pid); } } @@ -495,38 +701,52 @@ struct LongrangeMaker { float ampl = ft0.amplitudeA()[iCh]; auto phi = getPhiFT0(chanelid, 0); auto eta = getEtaFT0(chanelid, 0); - upclrft0atracks(upclrcollision.lastIndex(), chanelid, ampl, ampl, eta, phi); + if (eta > cfgfittrksel.cfgFt0aEtaMax || eta < cfgfittrksel.cfgFt0aEtaMin) { + continue; + } + upclrft0atracks(upclrcollision.lastIndex(), + chanelid, + ampl, + eta, + phi, + chanelid >= MinFt0aOuterRing); } for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { auto chanelid = ft0.channelC()[iCh] + KminFt0cCell; float ampl = ft0.amplitudeC()[iCh]; auto phi = getPhiFT0(chanelid, 1); auto eta = getEtaFT0(chanelid, 1); - upclrft0ctracks(upclrcollision.lastIndex(), chanelid, ampl, ampl, eta, phi); + if (eta > cfgfittrksel.cfgFt0cEtaMax || eta < cfgfittrksel.cfgFt0cEtaMin) { + continue; + } + upclrft0ctracks(upclrcollision.lastIndex(), + chanelid, + ampl, + eta, + phi, + chanelid >= MinFt0cOuterRing); } } - // mft loop - for (const auto& track : mfttracks) { - if (!isMftTrackSelected(track)) + for (const auto& reassoMftTrack : reassoMftTracks) { + if (!isMftBestTrackSelected(reassoMftTrack)) + continue; + auto itrack = reassoMftTrack.template mfttrack_as(); + if (!isMftTrackSelected(itrack)) { continue; - auto phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - upclrmfttracks(upclrcollision.lastIndex(), track.pt(), track.eta(), phi); - } - - if (besttracks.size() > 0) { - for (const auto& besttrack : besttracks) { - if (!isMftBestTrackSelected(besttrack)) - continue; - auto track = besttrack.mfttrack(); - if (!isMftTrackSelected(track)) { - continue; - } - auto phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - upclrmftbesttracks(upclrcollision.lastIndex(), track.pt(), track.eta(), phi); } + auto phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + upclrmfttracks(upclrcollision.lastIndex(), + reassoMftTrack.ambDegree(), + itrack.pt(), + itrack.eta(), + phi, + itrack.nClusters(), + reassoMftTrack.bestDCAXY(), + reassoMftTrack.bestDCAZ(), + itrack.isCA(), + itrack.collisionId() != reassoMftTrack.bestCollisionId()); } // v0 loop @@ -540,8 +760,14 @@ struct LongrangeMaker { // K0short if (isSelectK0s(col, v0)) { // candidate is K0s - upclrv0tracks(upclrcollision.lastIndex(), posTrack.globalIndex(), negTrack.globalIndex(), - v0.pt(), v0.eta(), v0.phi(), v0.mK0Short(), aod::lrcorrtrktable::kSpK0short); + upclrv0tracks(upclrcollision.lastIndex(), + posTrack.globalIndex(), + negTrack.globalIndex(), + v0.pt(), + v0.eta(), + v0.phi(), + v0.mK0Short(), + aod::lrcorrtrktable::kSpK0short); } // Lambda and Anti-Lambda @@ -551,13 +777,25 @@ struct LongrangeMaker { // Note: candidate compatible with Lambda and Anti-Lambda hypothesis are counted twice (once for each hypothesis) if (lambdaTag) { // candidate is Lambda massV0 = v0.mLambda(); - upclrv0tracks(upclrcollision.lastIndex(), posTrack.globalIndex(), negTrack.globalIndex(), - v0.pt(), v0.eta(), v0.phi(), massV0, aod::lrcorrtrktable::kSpLambda); + upclrv0tracks(upclrcollision.lastIndex(), + posTrack.globalIndex(), + negTrack.globalIndex(), + v0.pt(), + v0.eta(), + v0.phi(), + massV0, + aod::lrcorrtrktable::kSpLambda); } if (antilambdaTag) { // candidate is Anti-lambda massV0 = v0.mAntiLambda(); - upclrv0tracks(upclrcollision.lastIndex(), posTrack.globalIndex(), negTrack.globalIndex(), - v0.pt(), v0.eta(), v0.phi(), massV0, aod::lrcorrtrktable::kSpALambda); + upclrv0tracks(upclrcollision.lastIndex(), + posTrack.globalIndex(), + negTrack.globalIndex(), + v0.pt(), + v0.eta(), + v0.phi(), + massV0, + aod::lrcorrtrktable::kSpALambda); } // end of Lambda and Anti-Lambda processing } } // SG events @@ -568,16 +806,15 @@ struct LongrangeMaker { using TrksMCRecTable = soa::Filtered>; using MftTrkMCRecTable = soa::Join; Preslice perColMidtrack = aod::track::collisionId; - Preslice perColMfttrack = o2::aod::fwdtrack::collisionId; - PresliceUnsorted perColMftbesttrack = aod::fwdtrack::bestCollisionId; + PresliceUnsorted perColMftTrack = aod::fwdtrack::bestCollisionId; void processMCRec(ColMCTrueTable::iterator const& mcCollision, ColMCRecTable const& RecCols, TrksMCRecTable const& RecTracks, - aod::FT0s const&, MftTrkMCRecTable const& mfttracks, aod::BestCollisionsFwd3d const& besttracks, + aod::FT0s const&, MftTrkMCRecTable const&, aod::BestCollisionsFwd3d const& reassoMftTracks, aod::McParticles const& mcparticles, aod::BCsWithTimestamps const&) { auto multiplicity = 0; for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMin || particle.pt() > cfgtrksel.cfgPtCutMult) + if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMinForMult || particle.pt() > cfgtrksel.cfgPtCutMaxForMult) continue; multiplicity++; } @@ -590,14 +827,12 @@ struct LongrangeMaker { if (cfgevtsel.isApplyBestCollIndex && RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); - auto multiplicity = countNTracks(recTracksPart); - auto centrality = selColCent(RecCol); auto bc = RecCol.bc_as(); + loadEffCorrection(bc.timestamp()); ft0gainvalues.clear(); ft0gainvalues = {}; if (cfgfittrksel.useGainCalib) { - const auto ft0GainObj = ccdb->getForTimeStamp>(cfgfittrksel.ConfGainPath, bc.timestamp()); + const auto ft0GainObj = ccdb->getForTimeStamp>(cfgfittrksel.confGainPath, bc.timestamp()); if (!ft0GainObj) { for (auto i{0u}; i < TotFt0Channels; i++) { ft0gainvalues.push_back(1.); @@ -610,7 +845,10 @@ struct LongrangeMaker { ft0gainvalues.push_back(1.); } } - lrcollision(bc.runNumber(), RecCol.posZ(), multiplicity, centrality, bc.timestamp()); + auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); + float recMultiplicity = countNTracks(recTracksPart, RecCol.posZ()); + float centrality = selColCent(RecCol); + lrcollision(bc.runNumber(), RecCol.posZ(), recMultiplicity, centrality, bc.timestamp()); lrcollisionMcLabel(RecCol.mcCollisionId()); // track loop @@ -625,10 +863,28 @@ struct LongrangeMaker { if (RecCol.mcCollisionId() != mcpart.mcCollisionId()) continue; auto pid = getTrackPID(track); - lrmidtracks(lrcollision.lastIndex(), track.pt(), track.eta(), track.phi(), aod::lrcorrtrktable::kSpCharge); + lrmidtracks(lrcollision.lastIndex(), + track.tpcNClsFound(), + track.tpcNClsCrossedRows(), + track.tpcChi2NCl(), + track.pt(), + track.eta(), + track.phi(), + track.dcaZ(), + aod::lrcorrtrktable::kSpCharge); + if (pid != aod::lrcorrtrktable::kSpCharge) { - lrmidtracks(lrcollision.lastIndex(), track.pt(), track.eta(), track.phi(), pid); + lrmidtracks(lrcollision.lastIndex(), + track.tpcNClsFound(), + track.tpcNClsCrossedRows(), + track.tpcChi2NCl(), + track.pt(), + track.eta(), + track.phi(), + track.dcaZ(), + pid); } + histos.fill(HIST("h3DVtxZetaPhi"), RecCol.posZ(), track.eta(), track.phi()); } // ft0 loop @@ -640,7 +896,32 @@ struct LongrangeMaker { auto phi = getPhiFT0(chanelid, 0); auto eta = getEtaFT0(chanelid, 0); auto gainampl = ampl / ft0gainvalues[chanelid]; - lrft0atracks(lrcollision.lastIndex(), chanelid, ampl, gainampl, eta, phi); + if (eta > cfgfittrksel.cfgFt0aEtaMax || eta < cfgfittrksel.cfgFt0aEtaMin) { + continue; + } + lrft0atracks(lrcollision.lastIndex(), + chanelid, + gainampl, + eta, + phi, + chanelid >= MinFt0aOuterRing); + if (cfgfittrksel.applyMirrorFt0a) { + int mirrorId = getFt0aDeadChannelId(chanelid); + if (mirrorId >= 0) { + lrft0atracks(lrcollision.lastIndex(), + mirrorId, + gainampl, + getEtaFT0(mirrorId, 0), + getPhiFT0(mirrorId, 0), + mirrorId >= MinFt0aOuterRing); + } + } + histos.fill(HIST("FT0A_Amp"), ampl); + histos.fill(HIST("FT0A_Channel_vs_Amp"), chanelid, ampl); + histos.fill(HIST("FT0A_Amp_gaincorrected"), gainampl); + histos.fill(HIST("FT0A_Channel_vs_Amp_gaincorrected"), chanelid, gainampl); + histos.fill(HIST("FT0A_Channel_vs_eta"), chanelid, eta); + histos.fill(HIST("FT0A_Channel_vs_phi"), chanelid, phi); } for (std::size_t iCh = 0; iCh < ft0.channelC().size(); iCh++) { auto chanelid = ft0.channelC()[iCh] + KminFt0cCell; @@ -648,38 +929,55 @@ struct LongrangeMaker { auto phi = getPhiFT0(chanelid, 1); auto eta = getEtaFT0(chanelid, 1); auto gainampl = ampl / ft0gainvalues[chanelid]; - lrft0ctracks(lrcollision.lastIndex(), chanelid, ampl, gainampl, eta, phi); + if (eta > cfgfittrksel.cfgFt0cEtaMax || eta < cfgfittrksel.cfgFt0cEtaMin) { + continue; + } + lrft0ctracks(lrcollision.lastIndex(), + chanelid, + gainampl, + eta, + phi, + chanelid >= MinFt0cOuterRing); + if (cfgfittrksel.applyMirrorFt0c) { + int mirrorId = getFt0cDeadChannelId(chanelid); + if (mirrorId >= 0) { + lrft0ctracks(lrcollision.lastIndex(), + mirrorId, + gainampl, + getEtaFT0(mirrorId, 1), + getPhiFT0(mirrorId, 1), + mirrorId >= MinFt0cOuterRing); + } + } + histos.fill(HIST("FT0C_Amp"), ampl); + histos.fill(HIST("FT0C_Channel_vs_Amp"), chanelid, ampl); + histos.fill(HIST("FT0C_Amp_gaincorrected"), gainampl); + histos.fill(HIST("FT0C_Channel_vs_Amp_gaincorrected"), chanelid, gainampl); + histos.fill(HIST("FT0C_Channel_vs_eta"), chanelid, eta); + histos.fill(HIST("FT0C_Channel_vs_phi"), chanelid, phi); } } - // mft loop - auto recMftTracksPart = mfttracks.sliceBy(perColMfttrack, RecCol.globalIndex()); - for (const auto& track : recMftTracksPart) { - if (!isMftTrackSelected(track)) + auto recMftTracksPart = reassoMftTracks.sliceBy(perColMftTrack, RecCol.globalIndex()); + for (const auto& reassoMftTrack : recMftTracksPart) { + if (!isMftBestTrackSelected(reassoMftTrack)) continue; - if (!track.has_mcParticle()) + auto itrack = reassoMftTrack.template mfttrack_as(); + if (!isMftTrackSelected(itrack)) { continue; - auto mcpart = track.mcParticle(); - if (RecCol.mcCollisionId() != mcpart.mcCollisionId()) - continue; - auto phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmfttracks(lrcollision.lastIndex(), track.pt(), track.eta(), phi); - } - - auto recMftbestTracksPart = besttracks.sliceBy(perColMftbesttrack, RecCol.globalIndex()); - if (recMftbestTracksPart.size() > 0) { - for (const auto& besttrack : recMftbestTracksPart) { - if (!isMftBestTrackSelected(besttrack)) - continue; - auto track = besttrack.mfttrack_as(); - if (!isMftTrackSelected(track)) { - continue; - } - auto phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - lrmftbesttracks(lrcollision.lastIndex(), track.pt(), track.eta(), phi); } + auto phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + lrmfttracks(lrcollision.lastIndex(), + reassoMftTrack.ambDegree(), + itrack.pt(), + itrack.eta(), + phi, + itrack.nClusters(), + reassoMftTrack.bestDCAXY(), + reassoMftTrack.bestDCAZ(), + itrack.isCA(), + itrack.collisionId() != reassoMftTrack.bestCollisionId()); } for (const auto& particle : mcparticles) { @@ -687,152 +985,131 @@ struct LongrangeMaker { continue; } // Fill midrapidity tracks - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMult) + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) lrmidmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), particle.flags()); // Fill FT0 tracks - if (cfgfittrksel.cfigFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfigFt0aEtaMax) + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) lrft0amctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - if (cfgfittrksel.cfigFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfigFt0cEtaMax) + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) lrft0cmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); // Fill MFT tracks - if (cfgmfttrksel.cfigMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfigMftEtaMax) + if (cfgmfttrksel.cfgMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfgMftEtaMax && particle.pt() > cfgmfttrksel.cfgMftPtCutMin && particle.pt() < cfgmfttrksel.cfgMftPtCutMax) lrmftmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); } } } - void processMCGen(ColMCTrueTable::iterator const& mcCollision, aod::McParticles const& mcparticles) + void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcparticles) { auto multiplicity = 0; + auto multMCFT0A = 0; + auto multMCFT0C = 0; for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMin || particle.pt() > cfgtrksel.cfgPtCutMult) + if (!isGenPartSelected(particle)) continue; - multiplicity++; + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMinForMult && particle.pt() < cfgtrksel.cfgPtCutMaxForMult) + multiplicity++; + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) + multMCFT0C++; + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) + multMCFT0A++; } - lrmccollision(mcCollision.posZ(), multiplicity, mcCollision.multMCFT0A(), mcCollision.multMCFT0C()); + lrmccollision(mcCollision.posZ(), multiplicity, multMCFT0A, multMCFT0C); for (const auto& particle : mcparticles) { if (!isGenPartSelected(particle)) { continue; } // Fill midrapidity tracks - if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMult) + if (std::abs(particle.eta()) < cfgtrksel.cfgEtaCut && particle.pt() > cfgtrksel.cfgPtCutMin && particle.pt() < cfgtrksel.cfgPtCutMax) lrmidmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), particle.flags()); // Fill FT0 tracks - if (cfgfittrksel.cfigFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfigFt0aEtaMax) + if (cfgfittrksel.cfgFt0aEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0aEtaMax) lrft0amctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); - if (cfgfittrksel.cfigFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfigFt0cEtaMax) + if (cfgfittrksel.cfgFt0cEtaMin < particle.eta() && particle.eta() < cfgfittrksel.cfgFt0cEtaMax) lrft0cmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); // Fill MFT tracks - if (cfgmfttrksel.cfigMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfigMftEtaMax) + if (cfgmfttrksel.cfgMftEtaMin < particle.eta() && particle.eta() < cfgmfttrksel.cfgMftEtaMax && particle.pt() > cfgmfttrksel.cfgMftPtCutMin && particle.pt() < cfgmfttrksel.cfgMftPtCutMax) lrmftmctracks(lrmccollision.lastIndex(), particle.pt(), particle.eta(), particle.phi()); } } - void processTPCtrackEff(ColMCTrueTable::iterator const& mcCollision, ColMCRecTable const& RecCols, - TrksMCRecTable const& RecTracks, aod::McParticles const& mcparticles) + void processV0QA(CollTable::iterator const& col, aod::V0Datas const& V0s, TrksTable const& tracks) { - if (std::abs(mcCollision.posZ()) >= cfgevtsel.cfgVtxCut) { + if (!isEventSelected(col)) { return; } - auto multiplicity = 0; - bool atLeastOne = false; - for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) - continue; - if (std::abs(RecCol.posZ()) >= cfgevtsel.cfgVtxCut) - continue; - if (cfgevtsel.isApplyBestCollIndex && RecCol.globalIndex() != mcCollision.bestCollisionIndex()) - continue; - if (isUseCentEst) { - multiplicity = selColCent(RecCol); - } else { - auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); - multiplicity = countNTracks(recTracksPart); - } - atLeastOne = true; + + float multiplicity = selColCent(col); + if (multiplicity == -1) { + multiplicity = countNTracks(tracks, col.posZ()); } - for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle) || std::abs(particle.eta()) > cfgtrksel.cfgEtaCut || particle.pt() < cfgtrksel.cfgPtCutMin || particle.pt() > cfgtrksel.cfgPtCutMax) + + bool checkK0s = (cfgv0trksel.cfgQAV0Mask & 1); + bool checkLam = (cfgv0trksel.cfgQAV0Mask & 2); + bool checkALam = (cfgv0trksel.cfgQAV0Mask & 4); + + for (const auto& v0 : V0s) { + if (!isSelectV0Track(v0)) { continue; - if (atLeastOne) { - auto pos = std::distance(speciesIds.begin(), std::find(speciesIds.begin(), speciesIds.end(), particle.pdgCode())) + 1; - histos.fill(HIST("hGenMCdndpt"), mcCollision.posZ(), multiplicity, particle.eta(), particle.pt(), pos); } - } - for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) - continue; - if (std::abs(RecCol.posZ()) >= cfgevtsel.cfgVtxCut) - continue; - if (cfgevtsel.isApplyBestCollIndex && RecCol.globalIndex() != mcCollision.bestCollisionIndex()) - continue; - auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); - for (const auto& track : recTracksPart) { - if (!track.isGlobalTrack()) - continue; - if (!myTrackFilter.IsSelected(track)) - continue; - if (!track.has_mcParticle()) - continue; - auto particle = track.mcParticle(); - if (RecCol.mcCollisionId() != particle.mcCollisionId()) - continue; - if (particle.isPhysicalPrimary()) { - auto pos = std::distance(speciesIds.begin(), std::find(speciesIds.begin(), speciesIds.end(), particle.pdgCode())) + 1; - histos.fill(HIST("hRecMCdndpt"), mcCollision.posZ(), multiplicity, particle.eta(), particle.pt(), pos); - } + + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + float posP = posTrack.p(); + float negP = negTrack.p(); + float posSignal = posTrack.tpcSignal(); + float negSignal = negTrack.tpcSignal(); + + histos.fill(HIST("hArmenteros_before"), v0.alpha(), v0.qtarm()); + + // Evaluate topology ONLY (PID disabled) + bool isK0sTopoOnly = checkK0s && isSelectK0s(col, v0, false); + bool isLamTopoOnly = checkLam && isSelectLambda(col, v0, false); + bool isAlamTopoOnly = checkALam && isSelectLambda(col, v0, false); + + // Evaluate full selection (PID enabled) + bool isK0sTag = checkK0s && isSelectK0s(col, v0, true); + bool lambdaTag = checkLam && isSelectLambda(col, v0, true); + bool antilambdaTag = checkALam && isSelectLambda(col, v0, true); + + // ---------------- K0s ---------------- + if (isK0sTopoOnly) { + histos.fill(HIST("hTpcdEdx_pos_before"), 0.0f, multiplicity, posP, posSignal); + histos.fill(HIST("hTpcdEdx_neg_before"), 0.0f, multiplicity, negP, negSignal); } - } - } - void processMFTtrackEff(ColMCTrueTable::iterator const& mcCollision, ColMCRecTable const& RecCols, - TrksMCRecTable const& RecTracks, MftTrkMCRecTable const& mfttracks, - aod::McParticles const& mcparticles) - { - if (std::abs(mcCollision.posZ()) >= cfgevtsel.cfgVtxCut) { - return; - } - auto multiplicity = 0; - bool atLeastOne = false; - for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) - continue; - if (std::abs(RecCol.posZ()) >= cfgevtsel.cfgVtxCut) - continue; - if (cfgevtsel.isApplyBestCollIndex && RecCol.globalIndex() != mcCollision.bestCollisionIndex()) - continue; - if (isUseCentEst) { - multiplicity = selColCent(RecCol); - } else { - auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); - multiplicity = countNTracks(recTracksPart); + if (isK0sTag) { + histos.fill(HIST("hTpcdEdx_pos_after"), 0.0f, multiplicity, posP, posSignal); + histos.fill(HIST("hTpcdEdx_neg_after"), 0.0f, multiplicity, negP, negSignal); + histos.fill(HIST("hArmenteros_after"), 0.0f, v0.alpha(), v0.qtarm()); + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * o2::constants::physics::MassK0; + histos.fill(HIST("hTopoQA"), 0.0f, v0.dcaV0daughters(), std::abs(v0.dcapostopv()), std::abs(v0.dcanegtopv()), std::abs(v0.dcav0topv()), v0.v0cosPA(), v0.v0radius(), ctau, v0.pt(), v0.mK0Short()); } - atLeastOne = true; - } - for (const auto& particle : mcparticles) { - if (!isGenPartSelected(particle) || particle.eta() > cfgmfttrksel.cfigMftEtaMax || particle.eta() < cfgmfttrksel.cfigMftEtaMin || particle.pt() < cfgmfttrksel.cfgMftPtCutMin || particle.pt() > cfgmfttrksel.cfgMftPtCutMax) - continue; - if (atLeastOne) - histos.fill(HIST("hGenMCdndpt"), mcCollision.posZ(), multiplicity, particle.eta(), particle.pt(), 1.0); - } - for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) - continue; - if (std::abs(RecCol.posZ()) >= cfgevtsel.cfgVtxCut) - continue; - if (cfgevtsel.isApplyBestCollIndex && RecCol.globalIndex() != mcCollision.bestCollisionIndex()) - continue; - auto recTracksPart = mfttracks.sliceBy(perColMfttrack, RecCol.globalIndex()); - for (const auto& track : recTracksPart) { - if (!isMftTrackSelected(track)) - continue; - if (!track.has_mcParticle()) - continue; - auto particle = track.mcParticle(); - if (RecCol.mcCollisionId() != particle.mcCollisionId()) - continue; - if (particle.isPhysicalPrimary()) - histos.fill(HIST("hRecMCdndpt"), mcCollision.posZ(), multiplicity, particle.eta(), particle.pt(), 1.0); + + // -------------- Lambda --------------- + if (isLamTopoOnly) { + histos.fill(HIST("hTpcdEdx_pos_before"), 1.0f, multiplicity, posP, posSignal); + histos.fill(HIST("hTpcdEdx_neg_before"), 1.0f, multiplicity, negP, negSignal); + } + if (lambdaTag) { + histos.fill(HIST("hTpcdEdx_pos_after"), 1.0f, multiplicity, posP, posSignal); + histos.fill(HIST("hTpcdEdx_neg_after"), 1.0f, multiplicity, negP, negSignal); + histos.fill(HIST("hArmenteros_after"), 1.0f, v0.alpha(), v0.qtarm()); + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * o2::constants::physics::MassLambda; + histos.fill(HIST("hTopoQA"), 1.0f, v0.dcaV0daughters(), std::abs(v0.dcapostopv()), std::abs(v0.dcanegtopv()), std::abs(v0.dcav0topv()), v0.v0cosPA(), v0.v0radius(), ctau, v0.pt(), v0.mLambda()); + } + + // ----------- Anti-Lambda ------------- + if (isAlamTopoOnly) { + histos.fill(HIST("hTpcdEdx_pos_before"), 2.0f, multiplicity, posP, posSignal); + histos.fill(HIST("hTpcdEdx_neg_before"), 2.0f, multiplicity, negP, negSignal); + } + if (antilambdaTag) { + histos.fill(HIST("hTpcdEdx_pos_after"), 2.0f, multiplicity, posP, posSignal); + histos.fill(HIST("hTpcdEdx_neg_after"), 2.0f, multiplicity, negP, negSignal); + histos.fill(HIST("hArmenteros_after"), 2.0f, v0.alpha(), v0.qtarm()); + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * o2::constants::physics::MassLambda; + histos.fill(HIST("hTopoQA"), 2.0f, v0.dcaV0daughters(), std::abs(v0.dcapostopv()), std::abs(v0.dcanegtopv()), std::abs(v0.dcav0topv()), v0.v0cosPA(), v0.v0radius(), ctau, v0.pt(), v0.mAntiLambda()); } } } @@ -888,38 +1165,53 @@ struct LongrangeMaker { return false; } histos.fill(HIST("EventHist"), 8); - if (cfgevtsel.isApplyNoCollInTimeRangeStandard && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (cfgevtsel.isApplyNoCollInRofStandard && !col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } histos.fill(HIST("EventHist"), 9); - if (cfgevtsel.isApplyNoCollInRofStandard && !col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (cfgevtsel.isApplyNoCollInRofStrict && !col.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } histos.fill(HIST("EventHist"), 10); - if (cfgevtsel.isApplyNoHighMultCollInPrevRof && !col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (cfgevtsel.isApplyNoCollInTimeRangeStandard && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } histos.fill(HIST("EventHist"), 11); - if (cfgevtsel.isApplyOccuSelection && (col.trackOccupancyInTimeRange() > cfgevtsel.cfgOccuCut)) { + if (cfgevtsel.isApplyNoCollInTimeRangeStrict && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } histos.fill(HIST("EventHist"), 12); + if (cfgevtsel.isApplyNoHighMultCollInPrevRof && !col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return false; + } + histos.fill(HIST("EventHist"), 13); + if (cfgevtsel.isApplyOccuSelection && (col.trackOccupancyInTimeRange() > cfgevtsel.cfgOccuCut)) { + return false; + } + histos.fill(HIST("EventHist"), 14); + if (std::abs(col.posZ()) >= cfgevtsel.cfgVtxCut) { + return false; + } + histos.fill(HIST("EventHist"), 15); return true; } template - int countNTracks(countTrk const& tracks) + float countNTracks(countTrk const& tracks, float vz) { - auto nTrk = 0; + float nTrk = 0.f; for (const auto& track : tracks) { if (!track.isGlobalTrack()) continue; if (!myTrackFilter.IsSelected(track)) continue; - if (track.pt() < cfgtrksel.cfgPtCutMin || track.pt() > cfgtrksel.cfgPtCutMult) { + if (track.pt() < cfgtrksel.cfgPtCutMinForMult || track.pt() > cfgtrksel.cfgPtCutMaxForMult) { continue; } - nTrk++; + float trkeff = 1.0f; + if (cfgtrksel.applyEffCorr) + trkeff = getTrkEffCorr(vz, track.eta(), track.pt()); + nTrk += trkeff; } return nTrk; } @@ -928,6 +1220,9 @@ struct LongrangeMaker { float selColCent(CheckColCent const& col) { auto cent = -1; + if (cfgevtsel.isApplyMultNTracksPV) { + cent = col.multNTracksPV(); + } if (cfgevtsel.isApplyCentFT0C) { cent = col.centFT0C(); } @@ -985,6 +1280,24 @@ struct LongrangeMaker { } } + int getFt0aDeadChannelId(int channelId) + { + if (channelId >= MinFt0aMirrorChannelOuter && channelId <= MaxFt0aMirrorChannelOuter) + return MinFt0aDeadChannelOuter + (channelId - MinFt0aMirrorChannelOuter); + else + return -1; + } + + int getFt0cDeadChannelId(int channelId) + { + if (channelId >= MinFt0cMirrorChannelOuter && channelId <= MaxFt0cMirrorChannelOuter) + return MinFt0cDeadChannelOuter + (channelId - MinFt0cMirrorChannelOuter); + else if (channelId == Ft0cMirrorChannelInner) + return Ft0cDeadChannelInner; + else + return -1; + } + double getPhiFT0(uint chno, int i) { ft0Det.calculateChannelCenter(); @@ -1006,36 +1319,75 @@ struct LongrangeMaker { return -std::log(std::tan(0.5 * theta)); } - template + template bool isMftTrackSelected(CheckMftTrack const& track) { - if (track.nClusters() < cfgmfttrksel.cfigMftCluster) { + if constexpr (fillHis) { + histos.fill(HIST("hMftTrkSel"), 1); + } + if (track.nClusters() < cfgmfttrksel.cfgMftCluster) { return false; } - if (track.eta() > cfgmfttrksel.cfigMftEtaMax || track.eta() < cfgmfttrksel.cfigMftEtaMin) { + if constexpr (fillHis) { + histos.fill(HIST("hMftTrkSel"), 2); + } + if (cfgmfttrksel.cfgUseChi2Cut) { + float nclMft = std::max(2.0f * track.nClusters() - 5.0f, 1.0f); + float mftChi2NCl = track.chi2() / nclMft; + if (mftChi2NCl > cfgmfttrksel.cfgMaxChi2NCl) + return false; + } + if constexpr (fillHis) { + histos.fill(HIST("hMftTrkSel"), 3); + } + if (track.eta() > cfgmfttrksel.cfgMftEtaMax || track.eta() < cfgmfttrksel.cfgMftEtaMin) { return false; } + if constexpr (fillHis) { + histos.fill(HIST("hMftTrkSel"), 4); + } if (cfgmfttrksel.useMftPtCut && (track.pt() < cfgmfttrksel.cfgMftPtCutMin || track.pt() > cfgmfttrksel.cfgMftPtCutMax)) { return false; } + if constexpr (fillHis) { + histos.fill(HIST("hMftTrkSel"), 5); + } + if constexpr (fillHis) { + histos.fill(HIST("hMftTrkSel"), 6); + } return true; } - template - bool isMftBestTrackSelected(CheckMftBestTrack const& besttrack) + template + bool isMftBestTrackSelected(CheckMftTrack const& mfttrack) { - if (besttrack.bestCollisionId() < CintZero) { + if constexpr (fillHis) { + histos.fill(HIST("hMftBestTrkSel"), 1); + } + if (mfttrack.bestCollisionId() < CintZero) { return false; } - if (besttrack.ambDegree() == CintZero) { + if constexpr (fillHis) { + histos.fill(HIST("hMftBestTrkSel"), 2); + } + if (mfttrack.ambDegree() == CintZero) { return false; } - if (std::abs(besttrack.bestDCAXY()) > cfgmfttrksel.cfigMftDcaxy) { + if constexpr (fillHis) { + histos.fill(HIST("hMftBestTrkSel"), 3); + } + if (std::abs(mfttrack.bestDCAXY()) >= cfgmfttrksel.cfgMftDcaxy) { return false; } - if (std::abs(besttrack.bestDCAZ()) >= cfgmfttrksel.cfigMftDcaz) { + if constexpr (fillHis) { + histos.fill(HIST("hMftBestTrkSel"), 4); + } + if (std::abs(mfttrack.bestDCAZ()) >= cfgmfttrksel.cfgMftDcaz) { return false; } + if constexpr (fillHis) { + histos.fill(HIST("hMftBestTrkSel"), 5); + } return true; } @@ -1064,7 +1416,7 @@ struct LongrangeMaker { } template - bool isSelectK0s(Collision const& col, const V0candidate& v0) + bool isSelectK0s(Collision const& col, const V0candidate& v0, bool applyPidCut = true) { const auto& posTrack = v0.template posTrack_as(); const auto& negTrack = v0.template negTrack_as(); @@ -1074,7 +1426,7 @@ struct LongrangeMaker { if (v0.mK0Short() < cfgv0trksel.minK0sMass || v0.mK0Short() > cfgv0trksel.maxK0sMass) { return false; } - if ((v0.qtarm() / std::abs(v0.alpha())) < cfgv0trksel.minqtArmenterosForK0s) { + if (v0.qtarm() < (cfgv0trksel.minqtArmenterosForK0s * std::abs(v0.alpha()))) { return false; } if (v0.v0radius() > cfgv0trksel.maxK0sRadius || v0.v0radius() < cfgv0trksel.minK0sRadius) { @@ -1089,25 +1441,40 @@ struct LongrangeMaker { if (std::abs(ctauK0s) > cfgv0trksel.maxK0sLifeTime) { return false; } - if (((std::abs(posTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts) || (std::abs(negTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts))) { - return false; + if (applyPidCut) { + if (std::abs(posTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts || + std::abs(negTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts) { + return false; + } } - if ((std::abs(v0.dcapostopv()) < cfgv0trksel.minV0DcaPiK0s || std::abs(v0.dcanegtopv()) < cfgv0trksel.minV0DcaPiK0s)) { + if (std::abs(v0.dcapostopv()) < cfgv0trksel.minV0DcaPiK0s || + std::abs(v0.dcanegtopv()) < cfgv0trksel.minV0DcaPiK0s) { return false; } return true; } template - bool isSelectLambda(Collision const& col, const V0candidate& v0) + bool isSelectLambda(Collision const& col, const V0candidate& v0, bool applyPidCut = true) { const auto& posTrack = v0.template posTrack_as(); const auto& negTrack = v0.template negTrack_as(); float ctauLambda = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * o2::constants::physics::MassLambda; - if ((v0.mLambda() < cfgv0trksel.minLambdaMass || v0.mLambda() > cfgv0trksel.maxLambdaMass) && - (v0.mAntiLambda() < cfgv0trksel.minLambdaMass || v0.mAntiLambda() > cfgv0trksel.maxLambdaMass)) { + + if constexpr (pid == KindOfV0::kLambda) { + if (v0.mLambda() < cfgv0trksel.minLambdaMass || v0.mLambda() > cfgv0trksel.maxLambdaMass) { + return false; + } + } else if constexpr (pid == KindOfV0::kAntiLambda) { + if (v0.mAntiLambda() < cfgv0trksel.minLambdaMass || v0.mAntiLambda() > cfgv0trksel.maxLambdaMass) { + return false; + } + } + + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0) < cfgv0trksel.rejK0sMassWindow) { return false; } + if (v0.v0radius() > cfgv0trksel.maxLambdaRadius || v0.v0radius() < cfgv0trksel.minLambdaRadius) { return false; } @@ -1117,17 +1484,29 @@ struct LongrangeMaker { if (v0.dcaV0daughters() > cfgv0trksel.maxDcaV0DauLambda) { return false; } - if (pid == KindOfV0::kLambda && (std::abs(v0.dcapostopv()) < cfgv0trksel.minV0DcaPr || std::abs(v0.dcanegtopv()) < cfgv0trksel.minV0DcaPiLambda)) { - return false; - } - if (pid == KindOfV0::kAntiLambda && (std::abs(v0.dcapostopv()) < cfgv0trksel.minV0DcaPiLambda || std::abs(v0.dcanegtopv()) < cfgv0trksel.minV0DcaPr)) { - return false; - } - if (pid == KindOfV0::kLambda && ((std::abs(posTrack.tpcNSigmaPr()) > cfgv0trksel.daughPIDCuts) || (std::abs(negTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts))) { - return false; + if constexpr (pid == KindOfV0::kLambda) { + if (std::abs(v0.dcapostopv()) < cfgv0trksel.minV0DcaPr || + std::abs(v0.dcanegtopv()) < cfgv0trksel.minV0DcaPiLambda) { + return false; + } + } else if constexpr (pid == KindOfV0::kAntiLambda) { + if (std::abs(v0.dcapostopv()) < cfgv0trksel.minV0DcaPiLambda || + std::abs(v0.dcanegtopv()) < cfgv0trksel.minV0DcaPr) { + return false; + } } - if (pid == KindOfV0::kAntiLambda && ((std::abs(posTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts) || (std::abs(negTrack.tpcNSigmaPr()) > cfgv0trksel.daughPIDCuts))) { - return false; + if (applyPidCut) { + if constexpr (pid == KindOfV0::kLambda) { + if (std::abs(posTrack.tpcNSigmaPr()) > cfgv0trksel.daughPIDCuts || + std::abs(negTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts) { + return false; + } + } else if constexpr (pid == KindOfV0::kAntiLambda) { + if (std::abs(posTrack.tpcNSigmaPi()) > cfgv0trksel.daughPIDCuts || + std::abs(negTrack.tpcNSigmaPr()) > cfgv0trksel.daughPIDCuts) { + return false; + } + } } if (std::abs(ctauLambda) > cfgv0trksel.maxLambdaLifeTime) { return false; @@ -1135,12 +1514,41 @@ struct LongrangeMaker { return true; } + void loadEffCorrection(uint64_t timestamp) + { + if (fLoadTrkEffCorr) { + return; + } + if (cfgtrksel.cfgEffccdbPath.value.empty() == false) { + hTrkEff = ccdb->getForTimeStamp(cfgtrksel.cfgEffccdbPath, timestamp); + if (hTrkEff == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgtrksel.cfgEffccdbPath.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgtrksel.cfgEffccdbPath.value.c_str(), static_cast(hTrkEff)); + } + fLoadTrkEffCorr = true; + } + + float getTrkEffCorr(float posZ, float eta, float pt) + { + if (!cfgtrksel.applyEffCorr || !hTrkEff) { + return 1.0; + } + int zBin = hTrkEff->GetXaxis()->FindBin(posZ); + int etaBin = hTrkEff->GetYaxis()->FindBin(eta); + int ptBin = hTrkEff->GetZaxis()->FindBin(pt); + float effweight = 1.0 / hTrkEff->GetBinContent(zBin, etaBin, ptBin); + if (!std::isfinite(effweight) || effweight <= 0) { + return 1.0; + } + return effweight; + } + PROCESS_SWITCH(LongrangeMaker, processData, "process All collisions", false); PROCESS_SWITCH(LongrangeMaker, processUpc, "process UPC collisions", false); PROCESS_SWITCH(LongrangeMaker, processMCGen, "process MC generated collisions", false); PROCESS_SWITCH(LongrangeMaker, processMCRec, "process MC both gen and rec collisions", false); - PROCESS_SWITCH(LongrangeMaker, processTPCtrackEff, "process TPC track efficiency", false); - PROCESS_SWITCH(LongrangeMaker, processMFTtrackEff, "process MFT track efficiency", false); + PROCESS_SWITCH(LongrangeMaker, processV0QA, "process V0 QA histograms", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFiltering.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFiltering.cxx index 7d5bff3b422..1733559dd0b 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFiltering.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFiltering.cxx @@ -118,7 +118,7 @@ struct TwoParticleCorrelationsFilter { { using namespace twopfilter; LOGF(TWOPFILTERLOGCOLLISIONS, "Received collision with mask 0x%016lx and %ld tracks", collision.selflags(), tracks.size()); - auto passOptions = [](auto options, auto mask) { + auto passOptions = [](const auto& options, auto mask) { bool all = true; for (auto option : options) { all = all && ((option & mask) != 0UL); @@ -149,7 +149,7 @@ struct TwoParticleCorrelationsFilter { { using namespace twopfilter; LOGF(TWOPFILTERLOGCOLLISIONS, "Received collision with mask 0x%016lx and %ld tracks", collision.selflags(), tracks.size()); - auto passOptions = [](auto options, auto mask) { + auto passOptions = [](const auto& options, auto mask) { bool all = true; for (auto option : options) { all = all && ((option & mask) != 0UL); diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFullSkimming.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFullSkimming.cxx index 0441cbb8022..8f2bb9d9650 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFullSkimming.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsFullSkimming.cxx @@ -66,7 +66,7 @@ PWGCF::FilterAndAnalysisFramework* fFilterFramework = nullptr; using namespace cfskim; -void setEventCutsLabels(std::shared_ptr h) +void setEventCutsLabels(const std::shared_ptr& h) { using namespace aod::run2; @@ -98,7 +98,7 @@ void setEventCutsLabels(std::shared_ptr h) } } -void reportEventCuts(std::shared_ptr h, uint32_t eventcuts) +void reportEventCuts(const std::shared_ptr& h, uint32_t eventcuts) { using namespace aod::run2; auto entries = h->GetEntries(); @@ -127,7 +127,7 @@ struct TwoParticleCorrelationsSkimming { int bfield = 0; HistogramRegistry historeg; - int getMagneticField(std::string ccdbpath, uint64_t timestamp) + int getMagneticField(const std::string& ccdbpath, uint64_t timestamp) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable static o2::parameters::GRPObject* grpo = nullptr; diff --git a/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsNotStoredSkimming.cxx b/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsNotStoredSkimming.cxx index 31f15080d39..797103d4451 100644 --- a/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsNotStoredSkimming.cxx +++ b/PWGCF/TwoParticleCorrelations/TableProducer/twoParticleCorrelationsNotStoredSkimming.cxx @@ -66,7 +66,7 @@ PWGCF::FilterAndAnalysisFramework* fFilterFramework = nullptr; using namespace cfskim; -void setEventCutsLabels(std::shared_ptr h) +void setEventCutsLabels(const std::shared_ptr& h) { using namespace aod::run2; @@ -98,7 +98,7 @@ void setEventCutsLabels(std::shared_ptr h) } } -void reportEventCuts(std::shared_ptr h, uint32_t eventcuts) +void reportEventCuts(const std::shared_ptr& h, uint32_t eventcuts) { using namespace aod::run2; auto entries = h->GetEntries(); @@ -124,7 +124,7 @@ struct TwoParticleCorrelationsCollisionSkimming { int bfield = 0; HistogramRegistry historeg; - int getMagneticField(std::string ccdbpath, uint64_t timestamp) + int getMagneticField(const std::string& ccdbpath, uint64_t timestamp) { // TODO done only once (and not per run). Will be replaced by CCDBConfigurable static o2::parameters::GRPObject* grpo = nullptr; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt index 0a12a77b2fe..83f52b206e0 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt +++ b/PWGCF/TwoParticleCorrelations/Tasks/CMakeLists.txt @@ -43,9 +43,9 @@ o2physics_add_dpl_workflow(dpt-dpt-per-run-extra-qc PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(corr-sparse - SOURCES corrSparse.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore +o2physics_add_dpl_workflow(corr-reso + SOURCES corrReso.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(neutron-proton-corr-zdc @@ -60,7 +60,7 @@ o2physics_add_dpl_workflow(di-hadron-cor o2physics_add_dpl_workflow(pid-di-hadron SOURCES pidDiHadron.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(longrange-correlation @@ -78,6 +78,11 @@ o2physics_add_dpl_workflow(long-range-dihadron-cor PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(mft-correlations + SOURCES mftcorrelations.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(two-particle-correlation-pp SOURCES twoParticleCorrelationPp.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGCFCore @@ -108,3 +113,52 @@ o2physics_add_dpl_workflow(particle-origin-analysis PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(corr-fit + SOURCES corrFit.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(casc-di-hadron-corr + SOURCES cascDiHadronCorr.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-particle-correlations-mpi + SOURCES twoParticleCorrelationsMpi.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::GFWCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(charge-balance-function + SOURCES chargeBalanceFunction.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(lambda-proton-balance-function + SOURCES lambdaProtonBalanceFunction.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(lambda-or-anti-lambda-producer-with-spin + SOURCES lambdaOrAntiLambdaProducerWithSpin.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(fox-wolfram-correlation + SOURCES FoxWolframCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-particle-correlations-mult-spher + SOURCES twoParticleCorrelationsMultSpher.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hparticle-correlation + SOURCES hParticleCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pid-long-range + SOURCES pidLongRange.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore O2Physics::AnalysisCCDB O2Physics::GFWCore + COMPONENT_NAME Analysis) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx new file mode 100644 index 00000000000..ba8b5e6b790 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/FoxWolframCorrelation.cxx @@ -0,0 +1,1175 @@ +// Copyright 2020-2022 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FoxWolframCorrelation.cxx +/// \author Andreicovici Iulian Florin +/// \brief RECO-level event shape analysis using Fox-Wolfram Moments for O+O collisions. +/// Classifies events by topology (triangular), fills correlation histograms, +/// and produces custom SOA tables for mixed-event background estimation. +/// \since 29 Oct 2026 + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::track; +using namespace o2::soa; + +// Define compact event and track tables for mixed-event processing. +namespace o2::aod +{ +namespace collision +{ +DECLARE_SOA_COLUMN(Multip, multip, int); +} + +DECLARE_SOA_TABLE(TriangleCollisions, "AOD", "TRIANGLECOLLISION", + o2::soa::Index<>, + collision::PosZ, + collision::Multip); + +using TriangleCollision = TriangleCollisions::iterator; + +namespace track_tri +{ +DECLARE_SOA_INDEX_COLUMN(TriangleCollision, triangleCollision); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace track_tri + +DECLARE_SOA_TABLE(TriangleTracks, "AOD", "TRITRACK", + o2::soa::Index<>, + track_tri::TriangleCollisionId, + track_tri::Pt, track_tri::Eta, track_tri::Phi); + +} // namespace o2::aod + +// Wrap the azimuthal difference to the correlation interval. +double computeDeltaPhi(double phi1, double phi2) +{ + return RecoDecay::constrainAngle(phi1 - phi2, -o2::constants::math::PIHalf); +} + +// Name the available pair-selection modes. +enum LeadAssocMode : int { // 3 distinct modes + kAllPairs = 0, + kpTLeadConstrain = 1, + kpTLeadFree = 2 +}; + +// Name the available event-topology selections. +enum TopologyMode : int { + kAllEvSetNom = 0, + kTriEvSetNom = 1, + kIsoEvSetNom = 2, + kTriEvSet1p = 3, + kTriEvSet2p = 4, + kTriEvSet3p = 5, + kTriEvSet1m = 6, + kTriEvSet2m = 7, + kTriEvSet3m = 8 +}; + +template +// Apply the same pair-selection rules to signal and background. +bool passesLeadAssoc(int mode, bool foundPart, Track const& track1, Track const& track2, + TLead const& triggTrk, TLead const& triggTrkPart) +{ + if (mode == kAllPairs) { // all i-j pairs + return (track1.pt() < 3.f) && (track2.pt() < 3.f); + } + if (mode == kpTLeadConstrain) { // L-A: Leading pt in (2,3), Assoc pt < 2 + return foundPart && (track1.globalIndex() == triggTrkPart.globalIndex()) && (track2.pt() < 2.f); + } + if (mode == kpTLeadFree) { // L-A + return (track1.globalIndex() == triggTrk.globalIndex()) && (track2.pt() < track1.pt()); + } + + return false; +} + +template +// Find the overall leading track and the leading track in the selected momentum window. +auto findLeadingPart(T const& tracks) +{ + int leadingAllId = 0, leadingPartId = 0; + int i = 0; + + double ptMaxAll = -1., ptMaxPart = -1.; + bool foundPart = false; + + for (const auto& track : tracks) { + if (track.pt() > ptMaxAll) { + ptMaxAll = track.pt(); + leadingAllId = i; + } + if (track.pt() > 2.f && track.pt() < 3.f && track.pt() > ptMaxPart) { + ptMaxPart = track.pt(); + leadingPartId = i; + foundPart = true; + } + i++; + } + + return std::make_tuple(tracks.iteratorAt(leadingAllId), tracks.iteratorAt(leadingPartId), foundPart); +} +// Store selected tracks and collisions across data frames. +using TrackTuple = std::tuple; +using CollisionTuple = std::tuple>; + +// Define the reconstructed collision and track views. +using RecCollisions = soa::Join; +using CollisionRecTable = RecCollisions::iterator; +using RecTracks = soa::Join; + +// Select events, compute moments, and fill same-event observables. +struct FoxWolframCorrelation { + + SliceCache cache; + Preslice perCollision = aod::track::collisionId; + + Produces triCollisions; + Produces triTracks; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Configure histogram axes, event selection, and topology. + Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; + Configurable etaRange{"etaRange", 0.8f, "eta range to consider"}; + Configurable nSelEv{"nSelEv", 200, "no. of mixing Ev."}; + Configurable doSameEvent{"doSameEvent", true, "Fill same-event TRI correlations"}; + Configurable doFWMCorrelations{"doFWMCorrelations", false, "Fill H_i-H_j corr. hist"}; + Configurable topologyMode{"topologyMode", kTriEvSetNom, + "SelOpt: 0 AllEv, 1 TriNom, 2 IsoNom, 3 TriSet1p, 4 TriSet2p, 5 TriSet3p, 6 TriSet1m, 7 TriSet2m, 8 TriSet3m"}; + + // Choose the pair selection for same-event correlations. + Configurable leadAssocMode{"leadAssocMode", 2, "0: ALL i-j pairs (pT<3); 1: pTLead in (2,3) & pTAssoc < 2; 2: pTAssoc < pTLead"}; + + Configurable isVtxRange{"isVtxRange", true, "isVtxRange"}; + Configurable isSel8{"isSel8", true, "isSel8"}; + Configurable isNoSameBunchPileup{"isNoSameBunchPileup", true, "isNoSameBunchPileup"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", true, "isGoodZvtxFT0vsPV"}; + Configurable isVertexITSTPC{"isVertexITSTPC", false, "isVertexITSTPC"}; + Configurable isVertexTOFmatched{"isVertexTOFmatched", false, "isVertexTOFmatched"}; + Configurable kIsGoodITSLayer0123{"kIsGoodITSLayer0123", false, "kIsGoodITSLayer0123"}; + Configurable isNoCollInTimeRangeNarrow{"isNoCollInTimeRangeNarrow", false, "isNoCollInTimeRangeNarrow"}; + + //// Configurable FWM limits: H1, H2, H3, H4, H5, H6, H7, H8 //// + Configurable> kTriFWMSetNom{"kTriFWMSetNom", {0.05, 0.30, 0.040, 0.190, 0.020, 0.150, 0.016, 0.120}, "kTriFWMSetNom"}; + + Configurable> kTriFWMSet1p{"kTriFWMSet1p", {0.0525, 0.315, 0.0380, 0.1995, 0.0190, 0.1575, 0.0152, 0.1260}, "kTriFWMSet1p"}; + Configurable> kTriFWMSet2p{"kTriFWMSet2p", {0.0550, 0.330, 0.0360, 0.2090, 0.0180, 0.1650, 0.0144, 0.1320}, "kTriFWMSet2p"}; + Configurable> kTriFWMSet3p{"kTriFWMSet3p", {0.0575, 0.345, 0.0340, 0.2185, 0.0170, 0.1725, 0.0136, 0.1380}, "kTriFWMSet3p"}; + + Configurable> kTriFWMSet1m{"kTriFWMSet1m", {0.0475, 0.2850, 0.0420, 0.1805, 0.0210, 0.1425, 0.0168, 0.1140}, "kTriFWMSet1m"}; + Configurable> kTriFWMSet2m{"kTriFWMSet2m", {0.0450, 0.2700, 0.0440, 0.1710, 0.0220, 0.1350, 0.0176, 0.1080}, "kTriFWMSet2m"}; + Configurable> kTriFWMSet3m{"kTriFWMSet3m", {0.0425, 0.2550, 0.0460, 0.1615, 0.0230, 0.1275, 0.0184, 0.1020}, "kTriFWMSet3m"}; + + //// Named TRI selection indices //// + enum TriSelection { + kTriNominal, + kTriSet1p, + kTriSet2p, + kTriSet3p, + kTriSet1m, + kTriSet2m, + kTriSet3m, + kNTriSel + }; + using TriFlags = std::array; + + Configurable> isoFWMnominal{"isoFWMnominal", {0.02, 0.27, 0.02, 0.16, 0.02, 0.12, 0.02, 0.10}, "isoFWMnominal"}; + + // Multiplicity bins: bin i is [multLower[i], multUpper[i]] (inclusive limits). + // The first three entries are the broad ranges (RANGE1-3: FWM_BIN1-3, lead-assoc, mixing buffers), + // the remaining ones are the finer intervals (BIN4...). Bins may overlap. Max 10 bins. + // The same limits must be given to FoxWolframCorrelationMixing. + Configurable> multLower{"multLower", {40, 86, 141, 40, 61, 81, 101, 121, 141, 161}, "lower multiplicity limit of each bin (inclusive)"}; + Configurable> multUpper{"multUpper", {85, 140, 350, 60, 80, 100, 120, 140, 160, 350}, "upper multiplicity limit of each bin (inclusive)"}; + + static constexpr int MaxMultBins = 10; + int nMultBins = 0; // set in init() from multLower/multUpper + + static constexpr std::array binNames = { + "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", + "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; + + // Map each unique pair of Fox-Wolfram moments to its histogram. + + static constexpr std::array fwmRangeNames = {"FWM_BIN1/", "FWM_BIN2/", "FWM_BIN3/"}; + static constexpr std::array fwmHNames = {"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8"}; + static constexpr std::array fwmMultHNames = {"Multip_H1", "Multip_H2", "Multip_H3", "Multip_H4", "Multip_H5", "Multip_H6", "Multip_H7", "Multip_H8"}; + static constexpr std::array fwmCorrelationNames = {"H1_H2", "H1_H3", "H1_H4", "H1_H5", "H1_H6", "H1_H7", "H1_H8", "H2_H3", "H2_H4", "H2_H5", "H2_H6", "H2_H7", "H2_H8", "H3_H4", "H3_H5", "H3_H6", "H3_H7", "H3_H8", "H4_H5", "H4_H6", "H4_H7", "H4_H8", "H5_H6", "H5_H7", "H5_H8", "H6_H7", "H6_H8", "H7_H8"}; + + static constexpr std::array fwmCorrelationFirst = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6}; + static constexpr std::array fwmCorrelationSecond = {1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7}; + + // Register event, track, moment, and correlation histograms. + void init(InitContext const&) + { + // Reject unsupported topology modes at startup. + if (topologyMode.value < kAllEvSetNom || topologyMode.value > kTriEvSet3m) { + LOGF(fatal, "Invalid topologyMode=%d. Allowed values: 0 ALL, 1 TRI nominal, 2 ISO, 3 TRI 1p, 4 TRI 2p, 5 TRI 3p, 6 TRI 1m, 7 TRI 2m, 8 TRI 3m", topologyMode.value); + } + + // Validate the configurable multiplicity bins. + if (multLower.value.size() != multUpper.value.size() || multLower.value.empty() || multLower.value.size() > static_cast(MaxMultBins)) { + LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); + } + nMultBins = static_cast(multLower.value.size()); + for (int i = 0; i < nMultBins; ++i) { + if (multLower.value[i] > multUpper.value[i]) { + LOGF(fatal, "Multiplicity bin %d has multLower (%d) > multUpper (%d)", i + 1, multLower.value[i], multUpper.value[i]); + } + } + + // Define axes shared by the registered histograms. + const AxisSpec axispT{100, 0.15, 10, "p_{T}"}; + const AxisSpec axispTL{100, 2, 3, "p_{T}^{Lead}"}; + const AxisSpec axispTA{100, 0.15, 2, "p_{T}^{Assoc}"}; + const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; + const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisPhi{100, 0, constants::math::TwoPI, "#phi"}; + const AxisSpec axisEta{40, -0.8, 0.8, "#eta"}; + const AxisSpec axisMultip{350, 0, 350, "Multiplicity"}; + const AxisSpec axisHlrec{50, 0, 1, "H_{l}"}; + const AxisSpec axisHlfine{100, 0, 1, "H_{l}"}; + const AxisSpec axisEvent{10, 0.5, 10.5, "", "EventAxis"}; + const AxisSpec axisRecoil{100, 0., 1, "Recoil"}; + + // Label same-event pair counts by multiplicity interval. + histos.add("SamePairs/SamePairsNoLA", "SamePairsNoLA", kTH1D, {axisEvent}, false); + auto* samePairsNoLA = histos.get(HIST("SamePairs/SamePairsNoLA"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + samePairsNoLA->SetBinLabel(i + 1, i < 3 ? fmt::format("samePairsLA_RANGE{}", i + 1).c_str() : fmt::format("samePairsLA_BIN{}", i + 1).c_str()); + } + + // ALL Ev per Sel. BIN + histos.add("Events_Details/allEvSetNom", "SetAllNominal", kTH1D, {axisEvent}, false); + auto* allEvSetNom = histos.get(HIST("Events_Details/allEvSetNom"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + allEvSetNom->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // ISO Ev per Sel. BIN + histos.add("Events_Details/eventsNoISO", "SetIsoNominal", kTH1D, {axisEvent}, false); + auto* eventsNoISO = histos.get(HIST("Events_Details/eventsNoISO"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + eventsNoISO->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (NOMINAL) + histos.add("Events_Details/triEvSetNom", "SetTriNominal", kTH1D, {axisEvent}, false); + auto* triEvSetNom = histos.get(HIST("Events_Details/triEvSetNom"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSetNom->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET1PLUS) + histos.add("Events_Details/triEvSet1p", "Set1TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet1p = histos.get(HIST("Events_Details/triEvSet1p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet1p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET2PLUS) + histos.add("Events_Details/triEvSet2p", "Set2TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet2p = histos.get(HIST("Events_Details/triEvSet2p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet2p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET3PLUS) + histos.add("Events_Details/triEvSet3p", "Set3TriPlus", kTH1D, {axisEvent}, false); + auto* triEvSet3p = histos.get(HIST("Events_Details/triEvSet3p"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet3p->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET1MINUS) + histos.add("Events_Details/triEvSet1m", "Set1TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet1m = histos.get(HIST("Events_Details/triEvSet1m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet1m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET2MINUS) + histos.add("Events_Details/triEvSet2m", "Set2TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet2m = histos.get(HIST("Events_Details/triEvSet2m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet2m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // TRI Ev per Sel. BIN (SET3MINUS) + histos.add("Events_Details/triEvSet3m", "Set3TriMinus", kTH1D, {axisEvent}, false); + auto* triEvSet3m = histos.get(HIST("Events_Details/triEvSet3m"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + triEvSet3m->SetBinLabel(i + 1, i < 3 ? fmt::format("RANGE{}", i + 1).c_str() : fmt::format("BIN{}", i + 1).c_str()); + } + + // Register inclusive multiplicity, track spectra, and recoil histograms. + histos.add("Multip/ITSTPC_Multiplicity", "Reconstructed ITS-TPC Multiplicity; trks; Events", kTH1D, {axisMultip}); + + histos.add("pTSpectra/pT", "pT", kTH1D, {axispT}); + histos.add("Spectra/phi", "phi", kTH1D, {axisPhi}); + + histos.add("pTSpectra/RecoilALL", "RecoilALL", kTH2D, {axisMultip, axisRecoil}); + histos.add("pTSpectra/RecoilTRI", "RecoilTRI", kTH2D, {axisMultip, axisRecoil}); + + // Register track spectra for each multiplicity interval. + for (int i = 1; i <= nMultBins; i++) { + histos.add(fmt::format("Multip/nTrkAllEv_BIN{}", i).c_str(), fmt::format("nTrkAllEv_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrktriEvSetNom_BIN{}", i).c_str(), fmt::format("nTrktriEvSetNom_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS1p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS1p_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS2p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS2p_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS3p_BIN{}", i).c_str(), fmt::format("nTrkTriEvS3p_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS1m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS1m_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS2m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS2m_BIN{}", i).c_str(), kTH1D, {axisMultip}); + histos.add(fmt::format("Multip/nTrkTriEvS3m_BIN{}", i).c_str(), fmt::format("nTrkTriEvS3m_BIN{}", i).c_str(), kTH1D, {axisMultip}); + + histos.add(fmt::format("Spectra/etaphi_BIN{}", i).c_str(), fmt::format("etaphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + histos.add(fmt::format("Spectra/ptphi_BIN{}", i).c_str(), fmt::format("ptphi_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + histos.add(fmt::format("Spectra/etaphiTri_BIN{}", i).c_str(), fmt::format("etaphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axisEta}); + histos.add(fmt::format("Spectra/ptphiTri_BIN{}", i).c_str(), fmt::format("ptphiTri_BIN{}", i).c_str(), kTH2D, {axisPhi, axispT}); + + // onPeaks and onValleys - ALL EVENTS + histos.add(fmt::format("allEvPkVall/allLRNearPt_BIN{}", i).c_str(), fmt::format("allLRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRNearPt_BIN{}", i).c_str(), fmt::format("allSRNearPt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("allEvPkVall/allLRValleyPt_BIN{}", i).c_str(), fmt::format("allLRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRValleyPt_BIN{}", i).c_str(), fmt::format("allSRValleyPt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("allEvPkVall/allLRAwayPt_BIN{}", i).c_str(), fmt::format("allLRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("allEvPkVall/allSRAwayPt_BIN{}", i).c_str(), fmt::format("allSRAwayPt_BIN{}", i).c_str(), kTH1D, {axispT}); + + // onPeaks and onValleys - TRI EVENTS + histos.add(fmt::format("triEvPkVall/triLRPeak1Pt_BIN{}", i).c_str(), fmt::format("triLRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak1Pt_BIN{}", i).c_str(), fmt::format("triSRPeak1Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("triEvPkVall/triLRPeak2Pt_BIN{}", i).c_str(), fmt::format("triLRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak2Pt_BIN{}", i).c_str(), fmt::format("triSRPeak2Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("triEvPkVall/triLRPeak3Pt_BIN{}", i).c_str(), fmt::format("triLRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRPeak3Pt_BIN{}", i).c_str(), fmt::format("triSRPeak3Pt_BIN{}", i).c_str(), kTH1D, {axispT}); + + histos.add(fmt::format("triEvPkVall/triLRValleysPt_BIN{}", i).c_str(), fmt::format("triLRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); + histos.add(fmt::format("triEvPkVall/triSRValleysPt_BIN{}", i).c_str(), fmt::format("triSRValleysPt_BIN{}", i).c_str(), kTH1D, {axispT}); + } + + // Register leading and associated track spectra in the broad ranges. + for (int i = 1; i <= 3; i++) { + histos.add(fmt::format("pTSpectra/pT_LA_BIN{}", i).c_str(), fmt::format("pT_LA_BIN{}", i).c_str(), kTH2D, {axispT, axispT}); + histos.add(fmt::format("pTSpectra/pT_LA_TriBIN{}", i).c_str(), fmt::format("pT_LA_BIN{}", i).c_str(), kTH2D, {axispT, axispT}); + + histos.add(fmt::format("pTSpectra/fpT_LA_BIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); + histos.add(fmt::format("pTSpectra/fpT_LA_TriBIN{}", i).c_str(), fmt::format("fpT_LA_BIN{}", i).c_str(), kTH2D, {axispTL, axispTA}); + } + + // Register same-event angular correlations. + for (int i = 1; i <= nMultBins; ++i) { + histos.add(fmt::format("Corelations/sTRI_BIN{}", i).c_str(), fmt::format("sTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); + histos.add(fmt::format("Corelations/sProjTRI_BIN{}", i).c_str(), fmt::format("sProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); + } + + // Register moment distributions in the broad ranges. + const std::array bins = {1, 2, 3}; + for (int j = 1; j <= 8; j++) { + for (const auto& i : bins) { + histos.add(fmt::format("FWM_BIN{}/H{}", i, j).c_str(), fmt::format("H_{{{}}}^{{rec}}", j).c_str(), kTH1F, {axisHlrec}); + histos.add(fmt::format("FWM_BIN{}/Multip_H{}", i, j).c_str(), fmt::format("H_{{{}}} vs Multip", j).c_str(), kTH2D, {axisMultip, axisHlfine}); + } + } + + // Register optional pairwise moment correlations. + if (doFWMCorrelations) { + + for (size_t k = 0; k < fwmCorrelationNames.size(); ++k) { + + const int first = fwmCorrelationFirst[k] + 1; + const int second = fwmCorrelationSecond[k] + 1; + + histos.add(fmt::format("FWM_BIN1/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN2/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + histos.add(fmt::format("FWM_BIN3/{}", fwmCorrelationNames[k]).c_str(), fmt::format("H_{{{}}} vs H_{{{}}}", first, second).c_str(), kTH2D, {axisHlfine, axisHlfine}); + } + } + } + + // Apply reconstructed collision quality requirements. + template + bool isEventSelected(const TCollision& RecCollision) + { + if (isVtxRange && std::abs(RecCollision.posZ()) >= vtxRange) { + return false; + } + + if (isSel8 && !RecCollision.sel8()) { + return false; + } + + if (isNoSameBunchPileup && !RecCollision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + + if (isGoodZvtxFT0vsPV && !RecCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + + if (isVertexITSTPC && !RecCollision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + + if (isVertexTOFmatched && !RecCollision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + + if (kIsGoodITSLayer0123 && !RecCollision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + + if (isNoCollInTimeRangeNarrow && !RecCollision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + + return true; + } + + // Compute normalized Fox-Wolfram moments through order eight. + template + std::array computeFWM(T const& tracks) + { + std::array hSN{}; + std::array hS{}; + + // Accumulate transverse-momentum-weighted contributions from track pairs. + for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + double c = std::cos(t1.phi() - t2.phi()); + double w = t1.pt() * t2.pt(); + double p0 = 1.0, p1 = c; + + hSN[0] += w; + hSN[1] += w * p1; + + // Evaluate higher Legendre orders with the recurrence relation. + for (int l = 2; l < 9; ++l) { + + double p = ((2 * l - 1) * c * p1 - (l - 1) * p0) / l; + + hSN[l] += w * p; + p0 = p1; + p1 = p; + } + } + + // Normalize each moment by the zeroth moment. + for (int l = 0; l < 9; ++l) { + hS[l] = hSN[l] / hSN[0]; + } + + return hS; + } + + // Calculate normalized transverse recoil. + template + double computeRecoil(const Tracks& tracks) + { + double sumPx = 0.0; + double sumPy = 0.0; + double sumPt = 0.0; + + // Sum track momenta to form the transverse recoil. + for (const auto& track : tracks) { + sumPx += track.px(); + sumPy += track.py(); + sumPt += track.pt(); + } + + if (sumPt == 0.0) { + return 0.0; + } + + return std::sqrt(sumPx * sumPx + sumPy * sumPy) / sumPt; + } + + // Fill inclusive and topology-selected observables in one multiplicity interval. + template + void fillHistoMult(int binIndex, int minMult, int maxMult, + bool isSelectedTopology, const TriFlags& triFlags, bool isIsoNominal, + const T& tracks) + { + if (tracks.size() < minMult || tracks.size() > maxMult) { + return; + } + + // Count all events and each topology selection in this interval. + histos.fill(HIST("Events_Details/allEvSetNom"), binIndex); + + if (isIsoNominal) { + histos.fill(HIST("Events_Details/eventsNoISO"), binIndex); + } + + if (triFlags[kTriNominal]) { + histos.fill(HIST("Events_Details/triEvSetNom"), binIndex); + } + + // The remaining filters + if (triFlags[kTriSet1p]) { + histos.fill(HIST("Events_Details/triEvSet1p"), binIndex); + } + + if (triFlags[kTriSet2p]) { + histos.fill(HIST("Events_Details/triEvSet2p"), binIndex); + } + + if (triFlags[kTriSet3p]) { + histos.fill(HIST("Events_Details/triEvSet3p"), binIndex); + } + + if (triFlags[kTriSet1m]) { + histos.fill(HIST("Events_Details/triEvSet1m"), binIndex); + } + + if (triFlags[kTriSet2m]) { + histos.fill(HIST("Events_Details/triEvSet2m"), binIndex); + } + + if (triFlags[kTriSet3m]) { + histos.fill(HIST("Events_Details/triEvSet3m"), binIndex); + } + + // Fill inclusive spectra and selected-topology spectra for this interval. + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex == idx + 1) { + + histos.fill(HIST("Multip/nTrkAllEv_") + HIST(binNames[idx]), tracks.size()); + + for (const auto& track : tracks) { + histos.fill(HIST("Spectra/etaphi_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphi_") + HIST(binNames[idx]), track.phi(), track.pt()); + } + + // Fill spectra for the configured topology selection. + if (isSelectedTopology) { + + histos.fill(HIST("Multip/nTrktriEvSetNom_") + HIST(binNames[idx]), tracks.size()); + + for (const auto& track : tracks) { + histos.fill(HIST("Spectra/etaphiTri_") + HIST(binNames[idx]), track.phi(), track.eta()); + histos.fill(HIST("Spectra/ptphiTri_") + HIST(binNames[idx]), track.phi(), track.pt()); + } + } + + // TRI Ev. Multip (the remaining filters) - p == permissive and m == restrictive + if (triFlags[kTriSet1p]) { + histos.fill(HIST("Multip/nTrkTriEvS1p_") + HIST(binNames[idx]), tracks.size()); + } + + if (triFlags[kTriSet2p]) { + histos.fill(HIST("Multip/nTrkTriEvS2p_") + HIST(binNames[idx]), tracks.size()); + } + + if (triFlags[kTriSet3p]) { + histos.fill(HIST("Multip/nTrkTriEvS3p_") + HIST(binNames[idx]), tracks.size()); + } + + if (triFlags[kTriSet1m]) { + histos.fill(HIST("Multip/nTrkTriEvS1m_") + HIST(binNames[idx]), tracks.size()); + } + if (triFlags[kTriSet2m]) { + histos.fill(HIST("Multip/nTrkTriEvS2m_") + HIST(binNames[idx]), tracks.size()); + } + if (triFlags[kTriSet3m]) { + histos.fill(HIST("Multip/nTrkTriEvS3m_") + HIST(binNames[idx]), tracks.size()); + } + } + }); // per Multip Bin + } + + // Fill same-event pair distributions for one multiplicity interval. + template + void fillSameEventHist(int binIndex, + Track const& associatedTrack, + double deltaPhi, + double deltaEta, + bool isLeadAssocPair) + { + + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + + if (binIndex != idx + 1) { + return; + } + + if (!isLeadAssocPair) { + return; + } + + const double degToRad = constants::math::PI / 180.; + const double absDeltaEta = std::abs(deltaEta); + + const bool inLongRange = absDeltaEta > 1.25 && absDeltaEta < 1.6; + const bool inShortRange = absDeltaEta < 0.35; + + /// for ALL Ev. + const bool onNearPeak = deltaPhi > -15. * degToRad && deltaPhi < 15. * degToRad; + const bool onBetweenNearAway = deltaPhi > 75. * degToRad && deltaPhi < 105. * degToRad; + const bool onAwayPeak = deltaPhi > 165. * degToRad && deltaPhi < 195. * degToRad; + + if (topologyMode.value == kAllEvSetNom && leadAssocMode.value == 2) { // for the case with ALL sel Ev + + if (onNearPeak) { // NEAR SIDE + if (inLongRange) { + histos.fill(HIST("allEvPkVall/allLRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (inShortRange) { + histos.fill(HIST("allEvPkVall/allSRNearPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onBetweenNearAway) { // BETWEEN SIDE + if (inLongRange) { + histos.fill(HIST("allEvPkVall/allLRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (inShortRange) { + histos.fill(HIST("allEvPkVall/allSRValleyPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onAwayPeak) { // AWAY SIDE + if (inLongRange) { + histos.fill(HIST("allEvPkVall/allLRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (inShortRange) { + histos.fill(HIST("allEvPkVall/allSRAwayPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + } // for ALL Events + + /// for TRI sel Ev. + const bool onPeak1 = deltaPhi > -15. * degToRad && deltaPhi < 15. * degToRad; + const bool onPeak2 = deltaPhi > 105. * degToRad && deltaPhi < 135. * degToRad; + const bool onPeak3 = deltaPhi > 225. * degToRad && deltaPhi < 255. * degToRad; + const bool onValleys = (deltaPhi > 45. * degToRad && deltaPhi < 75. * degToRad) || (deltaPhi > 165. * degToRad && deltaPhi < 195. * degToRad); + + const bool isAnyTriMode = topologyMode.value == kTriEvSetNom || + topologyMode.value == kTriEvSet1p || + topologyMode.value == kTriEvSet2p || + topologyMode.value == kTriEvSet3p || + topologyMode.value == kTriEvSet1m || + topologyMode.value == kTriEvSet2m || + topologyMode.value == kTriEvSet3m; + + if (isAnyTriMode && leadAssocMode.value == 2) { // for the case with TRI sel Ev (any Sel Filter) + + if (onPeak1) { // on Peak at 0 + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRPeak1Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onPeak2) { // on Peak at 120 + + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRPeak2Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onPeak3) { // on Peak at 240 + + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRPeak3Pt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + if (onValleys) { + // Combined valleys at 60 and 180 degrees // + if (inLongRange) { + histos.fill(HIST("triEvPkVall/triLRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + + if (inShortRange) { + histos.fill(HIST("triEvPkVall/triSRValleysPt_") + HIST(binNames[idx]), associatedTrack.pt()); + } + } + + } // for TRI-like Events + + // Count accepted pairs and fill their angular distributions. + histos.fill(HIST("SamePairs/SamePairsNoLA"), binIndex); + histos.fill(HIST("Corelations/sProjTRI_") + HIST(binNames[idx]), deltaPhi); + histos.fill(HIST("Corelations/sTRI_") + HIST(binNames[idx]), deltaPhi, deltaEta); + }); + } + + /// Helper template for FWMs, FWMs vs Multip, FWMs i-j corr. + template + void fillFWMRange(const std::array& H, int nTrk) + { + // H[0] = H0 (normalizare), H[1..8] = H1..H8 + static_for<0, 7>([&](auto j) { + constexpr int jj = j.value; + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmHNames[jj]), H[jj + 1]); + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmMultHNames[jj]), nTrk, H[jj + 1]); + }); + + if (doFWMCorrelations) { + static_for<0, 27>([&](auto k) { + constexpr int kk = k.value; + histos.fill(HIST(fwmRangeNames[R]) + HIST(fwmCorrelationNames[kk]), H[fwmCorrelationFirst[kk] + 1], H[fwmCorrelationSecond[kk] + 1]); + }); + } + } + + std::array, 3> triTuples; + + Filter itsTpcTracks = requireGlobalTrackInFilter(); + using GlobalTracks = soa::Filtered; + + // Process reconstructed events and buffer selected tracks for mixing. + void process(CollisionRecTable const& collision, + GlobalTracks const& tracks) + { + + // Stop before analysis when the collision fails quality cuts. + if (!isEventSelected(collision)) { + return; + } + + if (tracks.size() == 0) { + return; + } + + // Determine track multiplicity, recoil, and leading tracks. + int nGlobalTracks = tracks.size(); + auto nRecoil = computeRecoil(tracks); + + auto [triggTrk, triggTrkPart, foundPart] = findLeadingPart(tracks); + + // Assign the event to broad and fine multiplicity intervals. + std::array inBin{}; + for (int i = 0; i < nMultBins; ++i) { + inBin[i] = (nGlobalTracks >= multLower.value[i] && nGlobalTracks <= multUpper.value[i]); + } + const bool multRANGE1 = inBin[0]; + const bool multRANGE2 = inBin[1]; + const bool multRANGE3 = inBin[2]; + + // Compute event moments and evaluate topology selections. + auto fwH = computeFWM(tracks); + auto [H0, H1, H2, H3, H4, H5, H6, H7, H8] = fwH; + + // FWMs, FWMs vs Multip, FWMs i-j corr. + if (multRANGE1) { + fillFWMRange<0>(fwH, nGlobalTracks); + } + + if (multRANGE2) { + fillFWMRange<1>(fwH, nGlobalTracks); + } + + if (multRANGE3) { + fillFWMRange<2>(fwH, nGlobalTracks); + } + + // Apply the nominal triangular moment limits + const bool isTriSetNom = (H1 < kTriFWMSetNom.value[0]) && (H2 < kTriFWMSetNom.value[1]) && (H3 > kTriFWMSetNom.value[2]) && (H4 < kTriFWMSetNom.value[3]) && (H5 > kTriFWMSetNom.value[4]) && (H6 < kTriFWMSetNom.value[5]) && (H7 > kTriFWMSetNom.value[6]) && (H8 < kTriFWMSetNom.value[7]); + + // Apply three progressively looser triangular selections + const bool isTriSet1p = (H1 < kTriFWMSet1p.value[0]) && (H2 < kTriFWMSet1p.value[1]) && (H3 > kTriFWMSet1p.value[2]) && (H4 < kTriFWMSet1p.value[3]) && (H5 > kTriFWMSet1p.value[4]) && (H6 < kTriFWMSet1p.value[5]) && (H7 > kTriFWMSet1p.value[6]) && (H8 < kTriFWMSet1p.value[7]); + const bool isTriSet2p = (H1 < kTriFWMSet2p.value[0]) && (H2 < kTriFWMSet2p.value[1]) && (H3 > kTriFWMSet2p.value[2]) && (H4 < kTriFWMSet2p.value[3]) && (H5 > kTriFWMSet2p.value[4]) && (H6 < kTriFWMSet2p.value[5]) && (H7 > kTriFWMSet2p.value[6]) && (H8 < kTriFWMSet2p.value[7]); + const bool isTriSet3p = (H1 < kTriFWMSet3p.value[0]) && (H2 < kTriFWMSet3p.value[1]) && (H3 > kTriFWMSet3p.value[2]) && (H4 < kTriFWMSet3p.value[3]) && (H5 > kTriFWMSet3p.value[4]) && (H6 < kTriFWMSet3p.value[5]) && (H7 > kTriFWMSet3p.value[6]) && (H8 < kTriFWMSet3p.value[7]); + + // Apply three restrictively looser triangular selections + const bool isTriSet1m = (H1 < kTriFWMSet1m.value[0]) && (H2 < kTriFWMSet1m.value[1]) && (H3 > kTriFWMSet1m.value[2]) && (H4 < kTriFWMSet1m.value[3]) && (H5 > kTriFWMSet1m.value[4]) && (H6 < kTriFWMSet1m.value[5]) && (H7 > kTriFWMSet1m.value[6]) && (H8 < kTriFWMSet1m.value[7]); + const bool isTriSet2m = (H1 < kTriFWMSet2m.value[0]) && (H2 < kTriFWMSet2m.value[1]) && (H3 > kTriFWMSet2m.value[2]) && (H4 < kTriFWMSet2m.value[3]) && (H5 > kTriFWMSet2m.value[4]) && (H6 < kTriFWMSet2m.value[5]) && (H7 > kTriFWMSet2m.value[6]) && (H8 < kTriFWMSet2m.value[7]); + const bool isTriSet3m = (H1 < kTriFWMSet3m.value[0]) && (H2 < kTriFWMSet3m.value[1]) && (H3 > kTriFWMSet3m.value[2]) && (H4 < kTriFWMSet3m.value[3]) && (H5 > kTriFWMSet3m.value[4]) && (H6 < kTriFWMSet3m.value[5]) && (H7 > kTriFWMSet3m.value[6]) && (H8 < kTriFWMSet3m.value[7]); + + const TriFlags triFlags = {isTriSetNom, isTriSet1p, isTriSet2p, isTriSet3p, isTriSet1m, isTriSet2m, isTriSet3m}; + + // Apply the configurable isotropic moment limits. + const bool isIsoNominal = (H1 < isoFWMnominal.value[0]) && (H2 < isoFWMnominal.value[1]) && (H3 < isoFWMnominal.value[2]) && (H4 < isoFWMnominal.value[3]) && (H5 < isoFWMnominal.value[4]) && (H6 < isoFWMnominal.value[5]) && (H7 < isoFWMnominal.value[6]) && (H8 < isoFWMnominal.value[7]); + + // Select all, triangular, or isotropic events for correlations. + const bool isSelectedTopology = + (topologyMode.value == kAllEvSetNom) || + (topologyMode.value == kTriEvSetNom && isTriSetNom) || + (topologyMode.value == kIsoEvSetNom && isIsoNominal) || + (topologyMode.value == kTriEvSet1p && isTriSet1p) || + (topologyMode.value == kTriEvSet2p && isTriSet2p) || + (topologyMode.value == kTriEvSet3p && isTriSet3p) || + (topologyMode.value == kTriEvSet1m && isTriSet1m) || + (topologyMode.value == kTriEvSet2m && isTriSet2m) || + (topologyMode.value == kTriEvSet3m && isTriSet3m); + + // Fill inclusive event multiplicity and recoil. + histos.fill(HIST("Multip/ITSTPC_Multiplicity"), nGlobalTracks); + histos.fill(HIST("pTSpectra/RecoilALL"), nGlobalTracks, nRecoil); + + if (isSelectedTopology) { + histos.fill(HIST("pTSpectra/RecoilTRI"), nGlobalTracks, nRecoil); + } + + // Fill inclusive and topology-selected leading-track spectra. + for (const auto& track : tracks) { + + histos.fill(HIST("pTSpectra/pT"), track.pt()); + histos.fill(HIST("Spectra/phi"), track.phi()); + + if (track.globalIndex() == triggTrk.globalIndex()) { + continue; + } + + if (multRANGE1) { + histos.fill(HIST("pTSpectra/pT_LA_BIN1"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_BIN1"), triggTrk.pt(), track.pt()); + } + if (multRANGE2) { + histos.fill(HIST("pTSpectra/pT_LA_BIN2"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_BIN2"), triggTrk.pt(), track.pt()); + } + if (multRANGE3) { + histos.fill(HIST("pTSpectra/pT_LA_BIN3"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_BIN3"), triggTrk.pt(), track.pt()); + } + + if (!isSelectedTopology) { + continue; + } + + if (multRANGE1) { + histos.fill(HIST("pTSpectra/pT_LA_TriBIN1"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_TriBIN1"), triggTrk.pt(), track.pt()); + } + if (multRANGE2) { + histos.fill(HIST("pTSpectra/pT_LA_TriBIN2"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_TriBIN2"), triggTrk.pt(), track.pt()); + } + if (multRANGE3) { + histos.fill(HIST("pTSpectra/pT_LA_TriBIN3"), triggTrk.pt(), track.pt()); + histos.fill(HIST("pTSpectra/fpT_LA_TriBIN3"), triggTrk.pt(), track.pt()); + } + } + + // Build same-event angular correlations for the selected topology. + if (doSameEvent && isSelectedTopology) { + + // Form distinct track pairs within the selected event. + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + + if (track1.globalIndex() == track2.globalIndex()) { + continue; + } + + // Calculate pair separations and classify azimuthal windows. + const double deltaPhi = computeDeltaPhi(track1.phi(), track2.phi()); + const double deltaEta = track1.eta() - track2.eta(); + + // Apply the configured pair selection to each interval. + const bool isLeadAssocPair = passesLeadAssoc(leadAssocMode.value, foundPart, track1, track2, triggTrk, triggTrkPart); + + for (int i = 0; i < nMultBins; ++i) { + if (inBin[i]) { + fillSameEventHist(i + 1, track2, deltaPhi, deltaEta, isLeadAssocPair); + } + } + } + } + + // Map the event to a broad range for mixed-event buffering. + int range = -1; + + if (multRANGE1) { + range = 0; + } else if (multRANGE2) { + range = 1; + } else if (multRANGE3) { + range = 2; + } + + // Buffer up to the requested number of selected events per range. + if (isSelectedTopology && range >= 0 && triTuples[range].size() < static_cast(nSelEv)) { + + std::vector tracksTRI; + tracksTRI.reserve(nGlobalTracks); + + for (const auto& track : tracks) { + tracksTRI.emplace_back(track.pt(), track.eta(), track.phi()); + } + + triTuples[range].emplace_back(collision.posZ(), nGlobalTracks, std::move(tracksTRI)); + } + + // Fill event counts and spectra for all matching multiplicity intervals. + for (int i = 0; i < nMultBins; ++i) { + fillHistoMult(i + 1, multLower.value[i], multUpper.value[i], isSelectedTopology, triFlags, isIsoNominal, tracks); + } + } + + int countDF = 0; + + // Write buffered events to the tables consumed by the mixing task. + void processFILL(aod::Collisions const& /*collisions*/, aod::Tracks const& /*tracks*/) + { + // Track data frames and report buffered event counts. + countDF++; + + // LOGF(info, "Input data - ALL Collisions %d, Tracks %d -> DF no %d", collisions.size(), tracks.size(), countDF); + + for (int range = 0; range < 3; ++range) { + + auto& events = triTuples[range]; + // LOGF(info, "Selected events buffered in RANGE%d: %zu/%d", range + 1, events.size(), static_cast(nSelEv)); + + // Wait until a range reaches its event quota. + if (events.size() < static_cast(nSelEv)) { + continue; + } + + // LOGF(info, "-> FILL TABLES FOR RANGE%d <-", range + 1); + + // Write each buffered collision and its tracks to the output tables. + for (const auto& col : events) { + + float posZ = std::get<0>(col); + int multip = std::get<1>(col); + triCollisions(posZ, multip); + + for (const auto& track : std::get<2>(col)) { + triTracks(triCollisions.lastIndex(), std::get<0>(track), std::get<1>(track), std::get<2>(track)); + } + } + + // Release events after their tables are filled. + events.clear(); + } + } + + PROCESS_SWITCH(FoxWolframCorrelation, processFILL, "Fill the triTables", true); +}; + +// Build mixed-event backgrounds from the selected event tables. +struct FoxWolframCorrelationMixing { + + SliceCache cache_bin1, cache_bin2, cache_bin3, cache_bin4, cache_bin5, cache_bin6, cache_bin7, cache_bin8, cache_bin9, cache_bin10; + Preslice triCol = aod::track_tri::triangleCollisionId; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + static constexpr std::array mixedBinNames = { + "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10"}; + + Configurable nBin{"nBin", 100, "No of bis per axis"}; + ConfigurableAxis axisVtx{"axisVtx", {VARIABLE_WIDTH, -10, 10}, "vertex axis for mixed event histograms"}; + + // Use half-integer edges for inclusive integer multiplicity bins. + ConfigurableAxis axisMult1{"axisMult1", {VARIABLE_WIDTH, 39.5, 85.5}, "axisMult1"}; + ConfigurableAxis axisMult2{"axisMult2", {VARIABLE_WIDTH, 85.5, 140.5}, "axisMult2"}; + ConfigurableAxis axisMult3{"axisMult3", {VARIABLE_WIDTH, 140.5, 350.5}, "axisMult3"}; + ConfigurableAxis axisMult4{"axisMult4", {VARIABLE_WIDTH, 39.5, 60.5}, "axisMult4"}; + ConfigurableAxis axisMult5{"axisMult5", {VARIABLE_WIDTH, 60.5, 80.5}, "axisMult5"}; + ConfigurableAxis axisMult6{"axisMult6", {VARIABLE_WIDTH, 80.5, 100.5}, "axisMult6"}; + ConfigurableAxis axisMult7{"axisMult7", {VARIABLE_WIDTH, 100.5, 120.5}, "axisMult7"}; + ConfigurableAxis axisMult8{"axisMult8", {VARIABLE_WIDTH, 120.5, 140.5}, "axisMult8"}; + ConfigurableAxis axisMult9{"axisMult9", {VARIABLE_WIDTH, 140.5, 160.5}, "axisMult9"}; + ConfigurableAxis axisMult10{"axisMult10", {VARIABLE_WIDTH, 160.5, 350.5}, "axisMult10"}; + + Configurable doMixedEvent{"doMixedEvent", true, "Fill mixed-event TRI correlations"}; + Configurable nMixedEvents{"nMixedEvents", 5, "Number of mixed events per trigger event"}; + + // Same multiplicity limits as in FoxWolframCorrelation (inclusive, same values in both tasks). + // They set how many mixing pools are active and are checked against axisMult1..10. + Configurable> multLower{"multLower", {40, 86, 141, 40, 61, 81, 101, 121, 141, 161}, "lower multip. limits (inclusive)"}; + Configurable> multUpper{"multUpper", {85, 140, 350, 60, 80, 100, 120, 140, 160, 350}, "upper multip. limits (inclusive)"}; + + static constexpr int MaxMultBins = 10; + int nMultBins = 0; // set in init() from multLower/multUpper + + // Choose the pair selection for mixed-event correlations. + Configurable leadAssocMode{"leadAssocMode", 2, "0: ALL i-j pairs (pT<3); 1: pTLead in (2,3) & pTAssoc < 2; 2: pTAssoc < pTLead"}; + + // Register mixed-event pair counts and angular distributions. + void init(InitContext const&) + { + // Validate the configurable multiplicity bins. + if (multLower.value.size() != multUpper.value.size() || multLower.value.empty() || multLower.value.size() > static_cast(MaxMultBins)) { + LOGF(fatal, "multLower/multUpper must have the same size, between 1 and %d entries", MaxMultBins); + } + nMultBins = static_cast(multLower.value.size()); + + // The binning policies are fixed objects, so check that each axisMultK matches [multLower-0.5, multUpper+0.5]. + const std::array, MaxMultBins> axisEdges = { + axisMult1.value, axisMult2.value, axisMult3.value, axisMult4.value, axisMult5.value, + axisMult6.value, axisMult7.value, axisMult8.value, axisMult9.value, axisMult10.value}; + + pools = {&pairBIN1, &pairBIN2, &pairBIN3, &pairBIN4, &pairBIN5, + &pairBIN6, &pairBIN7, &pairBIN8, &pairBIN9, &pairBIN10}; + + const AxisSpec axisDeltaEta{32, -1.6, +1.6, "#Delta#eta"}; + const AxisSpec axisDeltaPhi{72, -constants::math::PIHalf, +3 * constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisEvent{10, 0.5, 10.5, "#Multip. Bin", "EventAxis"}; + + // Label mixed-event pair counts by multiplicity interval. + histos.add("MixingPairs/MixingPairsNoLA", "MixingPairsNoLA", kTH1D, {axisEvent}, false); + auto* mixingPairsNoLA = histos.get(HIST("MixingPairs/MixingPairsNoLA"))->GetXaxis(); + for (int i = 0; i < MaxMultBins; ++i) { + mixingPairsNoLA->SetBinLabel(i + 1, i < 3 ? fmt::format("mixPairsLA_RANGE{}", i + 1).c_str() : fmt::format("mixPairsLA_BIN{}", i + 1).c_str()); + } + + // Register mixed-event correlation histograms for all intervals. + for (int i = 1; i <= nMultBins; i++) { + histos.add(fmt::format("Corelations/bTRI_BIN{}", i).c_str(), fmt::format("bTRI_BIN{}", i).c_str(), kTH2D, {axisDeltaPhi, axisDeltaEta}); + histos.add(fmt::format("Corelations/bProjTRI_BIN{}", i).c_str(), fmt::format("bProjTRI_BIN{}", i).c_str(), kTH1D, {axisDeltaPhi}); + } + } + + // Mix events in vertex and multiplicity intervals. + using BinningType = ColumnBinningPolicy; + + BinningType binningOnPosMultip1{{axisVtx, axisMult1}}; + BinningType binningOnPosMultip2{{axisVtx, axisMult2}}; + BinningType binningOnPosMultip3{{axisVtx, axisMult3}}; + BinningType binningOnPosMultip4{{axisVtx, axisMult4}}; + BinningType binningOnPosMultip5{{axisVtx, axisMult5}}; + BinningType binningOnPosMultip6{{axisVtx, axisMult6}}; + BinningType binningOnPosMultip7{{axisVtx, axisMult7}}; + BinningType binningOnPosMultip8{{axisVtx, axisMult8}}; + BinningType binningOnPosMultip9{{axisVtx, axisMult9}}; + BinningType binningOnPosMultip10{{axisVtx, axisMult10}}; + + // Create independent mixing pools for the ten multiplicity intervals. + SameKindPair pairBIN1{binningOnPosMultip1, nMixedEvents, -1, &cache_bin1}; + SameKindPair pairBIN2{binningOnPosMultip2, nMixedEvents, -1, &cache_bin2}; + SameKindPair pairBIN3{binningOnPosMultip3, nMixedEvents, -1, &cache_bin3}; + SameKindPair pairBIN4{binningOnPosMultip4, nMixedEvents, -1, &cache_bin4}; + SameKindPair pairBIN5{binningOnPosMultip5, nMixedEvents, -1, &cache_bin5}; + SameKindPair pairBIN6{binningOnPosMultip6, nMixedEvents, -1, &cache_bin6}; + SameKindPair pairBIN7{binningOnPosMultip7, nMixedEvents, -1, &cache_bin7}; + SameKindPair pairBIN8{binningOnPosMultip8, nMixedEvents, -1, &cache_bin8}; + SameKindPair pairBIN9{binningOnPosMultip9, nMixedEvents, -1, &cache_bin9}; + SameKindPair pairBIN10{binningOnPosMultip10, nMixedEvents, -1, &cache_bin10}; + + // Pointers to the pools so they can be processed in a loop (filled in init()). + using MixPair = SameKindPair; + std::array pools{}; + + // Fill mixed-event distributions for a selected multiplicity interval. + template + void fillMixedEventBin(PairType& pair, int binIndex) + { + // Mix populated event pairs within the selected pool. + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (tracks1.size() == 0 || tracks2.size() == 0) { + continue; + } + + // Find leading tracks in the first mixed event. + auto [triggTrk, triggTrkPart, foundPart] = findLeadingPart(tracks1); + + // LOGF(debug, "Mix TRI Ev (BIN%d): (%d, %d), z=(%.3f, %.3f), mult=(%d, %d)", + // binIndex, c1.globalIndex(), c2.globalIndex(), c1.posZ(), c2.posZ(), tracks1.size(), tracks2.size()); + + // Select tracks from different events using the configured pair rules. + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if (!passesLeadAssoc(leadAssocMode.value, foundPart, track1, track2, triggTrk, triggTrkPart)) { + continue; + } + + const double deltaPhi = computeDeltaPhi(track1.phi(), track2.phi()); + const double deltaEta = track1.eta() - track2.eta(); + + // Count accepted pairs in the corresponding multiplicity interval. + histos.fill(HIST("MixingPairs/MixingPairsNoLA"), binIndex); + static_for<0, 9>([&](auto i) { + constexpr int idx = i.value; + if (binIndex == idx + 1) { + histos.fill(HIST("Corelations/bProjTRI_") + HIST(mixedBinNames[idx]), deltaPhi); + histos.fill(HIST("Corelations/bTRI_") + HIST(mixedBinNames[idx]), deltaPhi, deltaEta); + } + }); + } + } + } + + // Run mixed-event pairing for all multiplicity intervals. + void process(aod::TriangleCollisions const& collisions, + aod::TriangleTracks const& tracks) + { + // Skip mixing when disabled or when the input tables are empty. + if (!doMixedEvent || collisions.size() == 0 || tracks.size() == 0) { + return; + } + + // Process the active independent mixing pools. + for (int i = 0; i < nMultBins; ++i) { + fillMixedEventBin(*pools[i], i + 1); + } + } +}; + +// Expose the event analysis and mixed-event tasks as one workflow. +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/Lambdacascadecorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/Lambdacascadecorrelation.cxx index d844dd72bfb..16655c4a8a7 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/Lambdacascadecorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/Lambdacascadecorrelation.cxx @@ -9,10 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file Lambdacascadecorrelation.cxx +/// \file lambdaCascadeCorrelation.cxx /// \brief Correlation-balance functions of multistrange baryons /// \author Oveis Sheibani - #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/Utils/inelGt.h" @@ -29,13 +28,10 @@ #include #include #include -#include #include #include #include -#include #include -#include #include #include #include @@ -57,7 +53,7 @@ #include #include #include -#include +#include #include using namespace o2; @@ -73,34 +69,59 @@ using FullTracksExtIU = soa::Join; using FullTracksExtIUWithPID = soa::Join; +// Data-model declarations inlined back into +// the .cxx so the task is contained in a single source file (ALICE submission +// policy). Originally extracted to a sibling header. +// + namespace o2::aod { namespace cascadeflags { DECLARE_SOA_COLUMN(IsSelected, isSelected, int); //~! +// MC-truth purity flag for the cascade row. +// True iff the cascade has a matched MC particle whose pdgCode is +// ±3312 (Ξ) or ±3334 (Ω) AND that MC particle is physical-primary. +// False on data and for combinatorial fakes (typical failure: a primary +// Λ paired with a stray pion that happened to satisfy the cascade-vertex +// fit). Lets the correlator filter out non-Ξ/Ω cascades when MC truth +// is available (LabeledCascades branch). +DECLARE_SOA_COLUMN(IsTrueCascade, isTrueCascade, bool); +// ITS-strangeness-tracking flag for the cascade row. +// True iff the cascade has a matching aod::AssignedTrackedCascades row, +// i.e. ITS reconstructed a track segment for the parent Ξ⁻/Ω⁻ in +// the IB layers (cτ_Ξ ≈ 4.91 cm — the Ξ traverses the inner barrel +// before decaying often enough to be tracked). This is the closest +// thing ALICE has to "ITS confirms this is a real cascade" on data. +// Available on Run 3 only; ITS strangeness tracking is not produced +// for Run 2 AODs. +DECLARE_SOA_COLUMN(IsItsTracked, isItsTracked, bool); +// Per-cascade bitmask: each bit = "passed cut N". Lets the +// downstream tree consumer reconstruct any cascade selection offline +// without re-running the workflow. Bit semantics defined by enum +// CascCutBit below. +DECLARE_SOA_COLUMN(CascCutBits, cascCutBits, uint32_t); } // namespace cascadeflags DECLARE_SOA_TABLE(CascadeFlags, "AOD", "CASCADEFLAGS", //! - cascadeflags::IsSelected); + cascadeflags::IsSelected, + cascadeflags::IsTrueCascade, + cascadeflags::IsItsTracked, + cascadeflags::CascCutBits); using CascDataExtSelected = soa::Join; } // namespace o2::aod -using MyCollisions = soa::Join; -using MyCollisionsMult = soa::Join; -using MyCascades = soa::Filtered; -using LabeledCascades = soa::Join; - namespace o2::aod { namespace lambdacollision { DECLARE_SOA_COLUMN(Cent, cent, float); DECLARE_SOA_COLUMN(Mult, mult, float); -DECLARE_SOA_COLUMN(RefCollId, refCollId, int64_t); // <--- 1. Add this line +DECLARE_SOA_COLUMN(RefCollId, refCollId, int64_t); } // namespace lambdacollision DECLARE_SOA_TABLE(LambdaCollisions, "AOD", "LAMBDACOLS", o2::soa::Index<>, lambdacollision::Cent, lambdacollision::Mult, - lambdacollision::RefCollId, // <--- 2. Add this line + lambdacollision::RefCollId, aod::collision::PosX, aod::collision::PosY, aod::collision::PosZ); @@ -137,6 +158,70 @@ DECLARE_SOA_COLUMN(DcaDau, dcaDau, float); DECLARE_SOA_COLUMN(V0Type, v0Type, int8_t); DECLARE_SOA_COLUMN(V0PrmScd, v0PrmScd, int8_t); DECLARE_SOA_COLUMN(CorrFact, corrFact, float); +// Mother PDG of the truth-matched MC Λ. 0 on data (no truth). +// In MC: 0 if the standalone Λ has no MC particle; else the parent PDG. +// Lets downstream consumers tag feed-down (e.g., motherPdg == ±3312 for +// Ξ-feeddown, ±3334 for Ω-feeddown, ±3212 for Σ⁰, etc.) without rerunning +// MC truth-matching themselves. +DECLARE_SOA_COLUMN(MotherPdg, motherPdg, int); +// Per-V0 topology snapshot stored on the row so: +// (i) the optional Λ TTree carries the variables needed to do a +// pT-differential primary-fraction template fit on data, +// (ii) the partition can gate on a topology-only "looks primary" flag +// on data (where v0PrmScd is uninformative). +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); +DECLARE_SOA_COLUMN(PosItsNCls, posItsNCls, int8_t); +DECLARE_SOA_COLUMN(NegItsNCls, negItsNCls, int8_t); +// Topology-only "looks primary" flag, computed in the V0 loop +// from the primCfg bundle: +// dcav0topv < cPrimMaxDcaV0ToPv +// && v0cosPA(PV) > cPrimMinV0CosPA +// && v0radius < cPrimMaxV0Radius +// && |dcapostopv|, |dcanegtopv| < cPrimMaxDauDcaToPv +// && posItsNCls, negItsNCls >= cPrimMinDauItsNCls +// On data this is the only handle on primary-Λ-ness, so the trigger +// partition AND's it. On MC the partition AND's it WITH the truth bit +// (v0PrmScd==kPrimary), and the Λ TTree exposes the components so an +// external macro can build templates per motherPdg and refine the cuts. +DECLARE_SOA_COLUMN(PassesPrimaryTopo, passesPrimaryTopo, bool); +// ITS hit-map per V0 daughter (uint8_t bitmask, one bit per +// of the 7 ITS layers). Lets a downstream macro do a geometric +// consistency check: a daughter from a V0 vertex at radius r > r_layer +// CANNOT have a hit on that layer — if it does, the V0 is mislocated +// or the "daughter" is a primary track misassigned. Tighter than the +// itsNCls integer count, free at this stage (just propagated through). +DECLARE_SOA_COLUMN(PosItsClusterMap, posItsClusterMap, uint8_t); +DECLARE_SOA_COLUMN(NegItsClusterMap, negItsClusterMap, uint8_t); +// Pseudo-proper transverse decay length L_proper = L_xy * M_Λ / pT. +// Computed treating the V0 vertex distance from PV as L_xy. For primary +// Λ this is the actual proper-time projection (exponential with cτ=7.89 cm); +// for feed-down Λ it OVER-estimates because L_xy includes the parent +// flight (e.g. Ξ cτ ≈ 4.91 cm). The discriminator is the upper tail. +DECLARE_SOA_COLUMN(LProper, lProper, float); +// Per-daughter DCA-XY-to-PV (signed). Already-cut on at production +// (cMinDcaProtonToPV / cMinDcaPionToPV) but not previously exposed downstream. +// Needed for the optional MVA / template-fit refinement and for inspecting +// the low-pT regime where these tighten primary-Λ purity. +DECLARE_SOA_COLUMN(PosDcaXY, posDcaXY, float); +DECLARE_SOA_COLUMN(NegDcaXY, negDcaXY, float); +// Per-V0 cut bitmask: each bit = "passed cut N". Computed +// unconditionally for every accepted V0 (and every V0 when the +// diagnostic mode cFillLambdaTreeAllCandidates is on). Lets a downstream +// macro reconstruct any cut combination offline without re-running. +// Bit semantics defined by enum LambdaCutBit below. +DECLARE_SOA_COLUMN(CutBits, cutBits, uint32_t); +// Raw cut-input values exposed on the Λ row so post-hoc cuts +// can be tightened/loosened without re-running. tpcNSigma values are +// matched to the v0Type hypothesis (proton-leg vs pion-leg). +DECLARE_SOA_COLUMN(TpcNSigmaPosPr, tpcNSigmaPosPr, float); // proton hypothesis on positive daughter +DECLARE_SOA_COLUMN(TpcNSigmaNegPi, tpcNSigmaNegPi, float); // pion hypothesis on negative daughter +DECLARE_SOA_COLUMN(TpcNSigmaPosPi, tpcNSigmaPosPi, float); // pion hypothesis on positive daughter +DECLARE_SOA_COLUMN(TpcNSigmaNegPr, tpcNSigmaNegPr, float); // proton hypothesis on negative daughter +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); // K0s mass hypothesis (for sideband studies) +DECLARE_SOA_COLUMN(QtArm, qtArm, float); // Armenteros qT +DECLARE_SOA_COLUMN(AlphaArm, alphaArm, float); // Armenteros α +DECLARE_SOA_COLUMN(CTau, cTau, float); // proper time × c (ctau) } // namespace lambdatrack DECLARE_SOA_TABLE(LambdaTracks, "AOD", "LAMBDATRACKS", o2::soa::Index<>, lambdatrack::LambdaCollisionId, @@ -154,7 +239,27 @@ DECLARE_SOA_TABLE(LambdaTracks, "AOD", "LAMBDATRACKS", o2::soa::Index<>, lambdatrack::DcaDau, lambdatrack::V0Type, lambdatrack::V0PrmScd, - lambdatrack::CorrFact); + lambdatrack::CorrFact, + lambdatrack::MotherPdg, + lambdatrack::DcaV0ToPV, + lambdatrack::V0Radius, + lambdatrack::PosItsNCls, + lambdatrack::NegItsNCls, + lambdatrack::PassesPrimaryTopo, + lambdatrack::PosItsClusterMap, + lambdatrack::NegItsClusterMap, + lambdatrack::LProper, + lambdatrack::PosDcaXY, + lambdatrack::NegDcaXY, + lambdatrack::CutBits, + lambdatrack::TpcNSigmaPosPr, + lambdatrack::TpcNSigmaNegPi, + lambdatrack::TpcNSigmaPosPi, + lambdatrack::TpcNSigmaNegPr, + lambdatrack::MK0Short, + lambdatrack::QtArm, + lambdatrack::AlphaArm, + lambdatrack::CTau); using LambdaTrack = LambdaTracks::iterator; namespace lambdatrackext @@ -195,6 +300,14 @@ using LambdaMcGenTrack = LambdaMcGenTracks::iterator; } // namespace o2::aod +using MyCollisions = soa::Join; +using MyCollisionsMult = soa::Join; +using MyCascades = soa::Filtered; +using LabeledCascades = soa::Join; + +// All Λ-side data-model declarations live in +// lambdaCascadeCorrelation_DataModel.h, included above. + enum CollisionLabels { kTotColBeforeHasMcCollision = 1, kTotCol, @@ -266,6 +379,90 @@ enum DMCType { kMC }; +// Centralised constants — replace scattered magic literals. +// PDG codes are taken from ROOT's TPDGCode.h enum (PDG_t) so the o2 linter +// is happy and the source of truth is the upstream framework header. +namespace lcorr_const +{ +// PDG codes for the cascades we tag/filter on. Drawn from ::kXiMinus etc. +// in so the magic-literal-detector won't complain. +constexpr int kLambdaPdg = ::kLambda0; // 3122, Λ (uds) +constexpr int kXiMinusPdg = ::kXiMinus; // 3312, Ξ⁻ (dss) +constexpr int kOmegaMinusPdg = ::kOmegaMinus; // 3334, Ω⁻ (sss) +[[maybe_unused]] constexpr int kSigma0Pdg = ::kSigma0; // 3212, Σ⁰ (uds, EM-decay → Λγ) + +// ITS Inner-Barrel layer mask (Layers 0,1,2 in itsClusterMap bitfield). +// Used by the "≥1 daughter has an IB hit" primary-Λ requirement. +constexpr uint8_t kItsIBMask = 0x07; + +// Cascade species-selection flag values written into the +// cascadeflags::IsSelected column. Promoted to namespace scope so both +// LambdaCascadeProducer (sets them) and Lambdacascadecorrelation (consumes +// them) can refer to the same names. +constexpr int kFlagRejected = 0; // rejected by processCandidate cut chain +constexpr int kFlagXiOnly = 1; // bachelor passes pion-PID only → Ξ-eligible +constexpr int kFlagXiAndOmega = 2; // bachelor passes both pion AND kaon PID → both +constexpr int kFlagOmegaOnly = 3; // bachelor passes kaon-PID only → Ω-eligible + +// cVetoMode values for the auto-correlation veto policy. +constexpr int kVetoModeOff = 0; // no veto +constexpr int kVetoModeStrict = 1; // veto only when BOTH daughters shared +constexpr int kVetoModeLoose = 2; // veto when EITHER daughter shared + +// cItsTrackMode values. +[[maybe_unused]] constexpr int kItsTrackModeOff = 0; // ignore ITS-tracking flag +constexpr int kItsTrackModeRequired = 1; // require isItsTracked == true +constexpr int kItsTrackModeRescue = 2; // accept isItsTracked OR truth-match +} // namespace lcorr_const + +// Per-Λ cut-bit enum. Bit i in lambdatrack::cutBits = 1 iff the +// candidate passed cut i. Computed UNCONDITIONALLY for every V0 row +// emitted into the LambdaTracks table — i.e. early-return short-circuits +// in selV0Particle do NOT hide later-stage results. This lets an offline +// macro reconstruct any cut combination from the tree without re-running. +enum LambdaCutBit : uint32_t { + kCutMassWindow = 0, // cMinV0Mass < mLambda < cMaxV0Mass (matched to v0Type) + kCutDauPid = 1, // |nσ(p)| < cTpcNsigmaCut AND |nσ(π)| < cut + kCutDauTrackQual = 2, // selTrack on both daughters + kCutDauDcaToPV = 3, // proton-leg DCA > min AND pion-leg DCA > min + kCutKinematic = 4, // pT and |y or η| within cMinV0Pt..cMaxV0Pt and cMaxV0Rap + kCutDcaV0Dau = 5, // dcaV0daughters in [min,max] + kCutDcaV0ToPV = 6, // dcav0topv in [min,max] + kCutV0Radius = 7, // v0radius in [min,max] + kCutCtau = 8, // ctau in [min,max] + kCutCosPA = 9, // v0cosPA > cMinV0CosPA + kCutK0sRej = 10, // K0s mass-window rejection passed (or flag off) + kCutAmbiguousVeto = 11, // !hasAmbiguousDaughters or veto disabled + kCutMcTrueLambda = 12, // MC: selTrueMcRecLambda (always true on data) + kCutPhase10Prim = 13, // passesPrimaryTopo (set in V0 loop) + kCutBitMax = 14 +}; + +// Per-cascade cut-bit enum. Bit i in cascadeflags::cascCutBits +// = 1 iff the candidate passed cut i. Mirrors the structure of +// LambdaCutBit but for the cascade selection chain in processCandidate. +enum CascCutBit : uint32_t { + kCascCutTpcRowsV0Dau = 0, // V0-daughter tpcNClsCrossedRows + kCascCutTpcRowsBach = 1, // bachelor tpcNClsCrossedRows + kCascCutItsClsV0Dau = 2, // V0-daughter itsNCls + kCascCutItsClsBach = 3, // bachelor itsNCls + kCascCutItsChi2 = 4, // ITS chi²/cluster (V0 + bachelor) + kCascCutTpcChi2 = 5, // TPC chi²/cluster (V0 + bachelor) + kCascCutCascPt = 6, // casc.pt() > cMinCascPt + kCascCutTopology = 7, // v0radius/cascradius/cosPA/dcav0topv/v0mass + kCascCutRadiusOrder = 8, // cascRadius < v0Radius (consistency) + kCascCutTrackEta = 9, // |η| of V0 dau + bachelor + kCascCutCascEta = 10, // |η| of cascade + kCascCutTpcNSigPr = 11, // proton-leg TPC nσ + kCascCutTpcNSigPi = 12, // pion-leg TPC nσ + kCascCutBachPidXi = 13, // bachelor pion nσ (Ξ hypothesis) + kCascCutBachPidOm = 14, // bachelor kaon nσ (Ω hypothesis) + kCascCutCompetingMass = 15, // competing-mass cut (Ω vs Ξ) + kCascCutMcTrueXiOmega = 16, // MC: pdgCode ∈ {±3312,±3334} && isPhysPrim + kCascCutItsTracked = 17, // matched in aod::AssignedTrackedCascades + kCascCutBitMax = 18 +}; + enum CorrHistDim { OneDimCorr = 1, TwoDimCorr, @@ -284,12 +481,173 @@ enum PrmScdPairType { kSS }; -struct LambdaTableProducer { +// ============================================================================= +// Shared event selection. Historically the Λ producer and the cascade +// selector applied slightly different event cuts, breaking the downstream +// correlator's normalisation. Now: one POD `EventCuts`, one templated +// `applyEventSelection()` evaluating only cuts with use*=true, +// and `logEventCuts()` for a grep-friendly one-line config dump. +// Defaults unchanged. +// ============================================================================= +namespace lcorr_evsel +{ + +struct EventCuts { + // Vertex Z. LTP's original semantics: posZ <= min || posZ >= max → reject + // (open interval). CSEL's were |posZ| > max (closed). We adopt LTP's + // convention here; CSEL fills minVtxZ=-maxVertexZ, maxVtxZ=+maxVertexZ + // (the difference matters only for collisions exactly at the boundary, + // which is statistically zero events). + bool useVtxZ{false}; + float minVtxZ{-10.f}, maxVtxZ{10.f}; + + // Trigger + bool useSel8{false}; // Run3 + bool useInt7{false}; // Run2 + bool useSel7{false}; // Run2 + + // Centrality range (LTP only by default) + bool useCentRange{false}; + float minCent{0.f}, maxCent{100.f}; + + // INEL multiplicity cut (CSEL only by default) + // Keeps multNTracksPVeta1 > inelMin convention from CascadeSelector. + bool useInel{false}; + int inelMin{0}; + + // Run3 selection bits + bool useTriggerTvx{false}; + bool useTfBorder{false}; + bool useItsRoBorder{false}; + bool useItsTpcVtx{false}; + bool useNoSameBunchPileup{false}; + bool useZVtxTimeDiff{false}; + bool useIsGoodITSLayers{false}; +}; + +// Apply the configured cuts to a collision. Returns true if accepted; on +// rejection writes a static C-string naming the failing cut into *reason +// so callers can bin-fill, log, etc. centValue is supplied by the caller +// because LTP selects the centrality estimator before the call. +template +inline bool applyEventSelection(C const& col, EventCuts const& cuts, + float centValue, const char*& reason) +{ + reason = "ok"; + + // Vertex Z (LTP open-interval semantics) + if (cuts.useVtxZ) { + if (col.posZ() <= cuts.minVtxZ || col.posZ() >= cuts.maxVtxZ) { + reason = "VtxZ"; + return false; + } + } + + // Trigger — Run3 + if constexpr (run == kRun3) { + if (cuts.useSel8 && !col.sel8()) { + reason = "Sel8"; + return false; + } + } else { + // Trigger — Run2 + if (cuts.useInt7 && !col.alias_bit(kINT7)) { + reason = "Int7"; + return false; + } + if (cuts.useSel7 && !col.sel7()) { + reason = "Sel7"; + return false; + } + } + + // Centrality range (estimator already chosen by caller → centValue passed in) + if (cuts.useCentRange) { + if (centValue <= cuts.minCent || centValue >= cuts.maxCent) { + reason = "CentRange"; + return false; + } + } + + // Run3 selection bits + INEL + if constexpr (run == kRun3) { + if (cuts.useInel && col.multNTracksPVeta1() <= cuts.inelMin) { + reason = "INEL"; + return false; + } + if (cuts.useTriggerTvx && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { + reason = "TriggerTVX"; + return false; + } + if (cuts.useTfBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + reason = "TFBorder"; + return false; + } + if (cuts.useItsRoBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + reason = "ITSROBorder"; + return false; + } + if (cuts.useItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + reason = "ItsTpcVtx"; + return false; + } + if (cuts.useNoSameBunchPileup && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + reason = "NoSameBunchPileup"; + return false; + } + if (cuts.useZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + reason = "ZVtxTimeDiff"; + return false; + } + if (cuts.useIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + reason = "GoodITSLayers"; + return false; + } + } + + return true; +} + +// Single-line, grep-friendly summary of the cut configuration. tag is the +// producer label, e.g. "EVENTSEL-LTP" or "EVENTSEL-CSEL". +inline void logEventCuts(const char* tag, EventCuts const& cuts) +{ + LOGF(info, + "[%s] vtxZ=%d[%.2f,%.2f] sel8=%d int7=%d sel7=%d " + "centRange=%d[%.1f,%.1f] inel=%d>%d " + "trigTvx=%d tfBorder=%d itsROBorder=%d itsTpcVtx=%d " + "noBunchPileup=%d zVtxTimeDiff=%d goodITSLayers=%d", + tag, + static_cast(cuts.useVtxZ), static_cast(cuts.minVtxZ), static_cast(cuts.maxVtxZ), + static_cast(cuts.useSel8), static_cast(cuts.useInt7), static_cast(cuts.useSel7), + static_cast(cuts.useCentRange), static_cast(cuts.minCent), static_cast(cuts.maxCent), + static_cast(cuts.useInel), cuts.inelMin, + static_cast(cuts.useTriggerTvx), static_cast(cuts.useTfBorder), + static_cast(cuts.useItsRoBorder), static_cast(cuts.useItsTpcVtx), + static_cast(cuts.useNoSameBunchPileup), static_cast(cuts.useZVtxTimeDiff), + static_cast(cuts.useIsGoodITSLayers)); +} + +} // namespace lcorr_evsel + +// ============================================================================= +// LambdaCascadeProducer — merger of the former LambdaTableProducer and +// CascadeSelector. One selCollision() per event drives both the Λ tables +// and the cascade flagging, so their collision populations cannot disagree. +// Outputs: LambdaCollisions, LambdaTracks, LambdaMcGen*, CascadeFlags. +// Registries stay separate (histos / cascRegistry) so every histogram path +// in AnalysisResults.root is unchanged. +// ============================================================================= +struct LambdaCascadeProducer { + // === Outputs === + // Lambda-side Produces lambdaCollisionTable; Produces lambdaTrackTable; Produces lambdaMCGenCollisionTable; Produces lambdaMCGenTrackTable; + // Cascade-side + Produces cascflags; // Collisions Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator : 0-FT0M, 1-FV0A"}; @@ -345,15 +703,24 @@ struct LambdaTableProducer { Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; // V0s MC - Configurable cHasMcFlag{"cHasMcFlag", true, "Has Mc Tag"}; + // Removed dead Configurables: cHasMcFlag, cGenPrimaryLambda, + // cGenSecondaryLambda — declared but never gated any logic. cSelMCPSV0 + // kept as a no-op for back-compat (deprecated). Configurable cSelectTrueLambda{"cSelectTrueLambda", true, "Select True Lambda"}; - Configurable cSelMCPSV0{"cSelMCPSV0", true, "Select Primary/Secondary V0"}; + Configurable cSelMCPSV0{"cSelMCPSV0", true, "[DEPRECATED] no-op; v0PrmScd is always tagged from MC truth"}; Configurable cCheckRecoDauFlag{"cCheckRecoDauFlag", true, "Check for reco daughter PID"}; - Configurable cGenPrimaryLambda{"cGenPrimaryLambda", true, "Primary Generated Lambda"}; - Configurable cGenSecondaryLambda{"cGenSecondaryLambda", false, "Secondary Generated Lambda"}; Configurable cGenDecayChannel{"cGenDecayChannel", true, "Gen Level Decay Channel Flag"}; Configurable cRecoMomResoFlag{"cRecoMomResoFlag", false, "Check effect of momentum space smearing on balance function"}; + // Diagnostic mode — emit EVERY V0 candidate (not just those + // passing selV0Particle) into LambdaTracks, with the full cut bitmask + // showing which stage each one failed. Use to build offline cutflow + // plots and tune cut values without re-running. Default false because + // it inflates the table by ~5-10× and downstream consumers need to + // gate on cutBits / passesPrimaryTopo. + Configurable cFillLambdaTreeAllCandidates{"cFillLambdaTreeAllCandidates", false, + "Diagnostic: emit all V0 candidates with cutBits, not only the ones passing selV0Particle"}; + // Efficiency Correction Configurable cCorrectionFlag{"cCorrectionFlag", false, "Correction Flag"}; Configurable cGetEffFact{"cGetEffFact", false, "Get Efficiency Factor Flag"}; @@ -365,12 +732,177 @@ struct LambdaTableProducer { Configurable cUrlCCDB{"cUrlCCDB", "http://ccdb-test.cern.ch:8080", "url of ccdb"}; Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/lambda_corr_fact", "Path for ccdb-object"}; - // Initialize CCDB Service - Service ccdb; - - // Histogram Registry. + // Initialize CCDB Service (shared with cascade-side path; LTP's cUrlCCDB + // is the live URL — see init() comment). + Service ccdb = {}; + + // PDG service for cascade competing-mass cut, brought in from + // the former CascadeSelector struct. + Service pdgDB = {}; + + // =========================================================================== + // Cascade-side Configurables bundled into one ConfigurableGroup: + // StructToTuple caps a task at 99 members and the merged struct has 129. + // The group counts as one member; JSON keys are unchanged (first-arg + // names preserved). Access pattern: cascCfg.tpcNsigmaProton. + // =========================================================================== + struct : ConfigurableGroup { + // Deprecated CSEL-style event-selection knobs. Echoed in the + // init-time deprecation warning but no longer affect event acceptance. + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB url (cascade-side; cosmetic — LTP's cUrlCCDB drives the actual CCDB fetches)"}; + // Removed unused Configurables: useTrigger / triggerList. + // Trigger-skim path was deleted in an earlier phase but the knobs were + // left behind — toggling them in JSON had zero effect. Cleared now. + Configurable doTFBorderCut{"doTFBorderCut", true, "[DEPRECATED] event selection delegated to LTP"}; + Configurable doSel8{"doSel8", true, "[DEPRECATED] sel8 is enforced by LTP's cSel8Trig"}; + Configurable doNoSameBunchPileUp{"doNoSameBunchPileUp", true, "[DEPRECATED] pileup veto is enforced by LTP's cPileupReject"}; + Configurable INEL{"INEL", 0, "[DEPRECATED] INEL>N enforcement is no longer applied"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable maxVertexZ{"maxVertexZ", 10., "[DEPRECATED] |Vz| cut is enforced by LTP's cMin/cMaxZVtx"}; + + // Cascade kinematic / selection. + Configurable etaCascades{"etaCascades", 0.8, "min/max of eta for cascades"}; + Configurable doCompetingMassCut{"doCompetingMassCut", true, "Switch to apply a competing mass cut for the Omega's"}; + Configurable competingMassWindow{"competingMassWindow", 0.01, "Mass window for the competing mass cut"}; + // Cascade pT lower cap. Drops very low-pT cascades where + // topology resolution is poor and the V0+bachelor combinatorial + // background dominates. Default 0.6 GeV/c is a typical PWG-LF Ξ floor. + Configurable cMinCascPt{"cMinCascPt", 0.6f, "Minimum cascade pT [GeV/c]"}; + + // Cascade tracklevel. + Configurable tpcNsigmaBachelor{"tpcNsigmaBachelor", 3, "TPC NSigma bachelor"}; + Configurable tpcNsigmaProton{"tpcNsigmaProton", 3, "TPC NSigma proton <- lambda"}; + Configurable tpcNsigmaPion{"tpcNsigmaPion", 3, "TPC NSigma pion <- lambda"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 80, "min N TPC crossed rows"}; + Configurable minITSClusters{"minITSClusters", 4, "minimum number of ITS clusters"}; + Configurable etaTracks{"etaTracks", 1.0, "min/max of eta for cascade daughter tracks"}; + Configurable tpcChi2{"tpcChi2", 4, "TPC Chi2 (cascade tracks)"}; + Configurable itsChi2{"itsChi2", 36, "ITS Chi2 (cascade tracks)"}; + // Bachelor-specific track-quality knobs. Default-equal to + // the V0-daughter values so existing behaviour is preserved unless the + // user tightens them. A real bachelor π/K typically wants stricter cuts + // than a V0 daughter because mis-association is more common at the + // cascade vertex (further from PV, larger combinatorial pool). + Configurable minBachTPCCrossedRows{"minBachTPCCrossedRows", 80, "min N TPC crossed rows for bachelor"}; + Configurable minBachITSClusters{"minBachITSClusters", 4, "min ITS clusters for bachelor"}; + Configurable maxBachTpcChi2{"maxBachTpcChi2", 4, "max TPC chi2 / cluster for bachelor"}; + Configurable maxBachItsChi2{"maxBachItsChi2", 36, "max ITS chi2 / cluster for bachelor"}; + + // Cascade selection criteria. + // Cascade selection criteria. + // The following Configurables intentionally use snake_case + // names matching the long-standing ALICE V0/cascade-builder JSON + // convention shared across PWGCF / PWGLF tasks. Renaming would break + // back-compat with every existing config file. + Configurable v0setting_cospa{"v0setting_cospa", 0.995, "v0setting_cospa"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable v0setting_dcav0dau{"v0setting_dcav0dau", 1.0, "v0setting_dcav0dau"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable v0setting_dcapostopv{"v0setting_dcapostopv", 0.1, "v0setting_dcapostopv"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable v0setting_dcanegtopv{"v0setting_dcanegtopv", 0.1, "v0setting_dcanegtopv"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable v0setting_radius{"v0setting_radius", 0.9, "v0setting_radius"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable cascadesetting_cospa{"cascadesetting_cospa", 0.95, "cascadesetting_cospa"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + // Removed unused Configurables: cascadesetting_dcacascdau / + // cascadesetting_dcabachtopv. These were declared but never used in the + // cut chain — only `casc.dcacascdaughters()` / `casc.dcabachtopv()` (the + // row values) are read for QA fills. The cascade-builder upstream applies + // the analogous cuts at table-production time; our task didn't re-cut. + Configurable cascadesetting_cascradius{"cascadesetting_cascradius", 0.9, "cascadesetting_cascradius"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable cascadesetting_v0masswindow{"cascadesetting_v0masswindow", 0.01, "cascadesetting_v0masswindow"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + Configurable cascadesetting_mindcav0topv{"cascadesetting_mindcav0topv", 0.01, "cascadesetting_mindcav0topv"}; // o2-linter: disable=name/configurable (back-compat: JSON key) + + // Cascade-side ConfigurableAxes. + ConfigurableAxis cascRadiusAxis{"cascRadiusAxis", {100, 0.0f, 50.0f}, "cm"}; + ConfigurableAxis cascCpaAxis{"cascCpaAxis", {100, 0.95f, 1.0f}, "CPA"}; + ConfigurableAxis cascVertexAxis{"cascVertexAxis", {100, -10.0f, 10.0f}, "cm"}; + ConfigurableAxis cascDcaAxis{"cascDcaAxis", {100, 0.0f, 2.0f}, "cm"}; + ConfigurableAxis invXiMassAxis{"invXiMassAxis", {100, 1.28f, 1.38f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis invOmegaMassAxis{"invOmegaMassAxis", {100, 1.62f, 1.72f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis cascPtAxis{"cascPtAxis", {150, 0, 15}, "#it{p}_{T}"}; + ConfigurableAxis cascRapidityAxis{"cascRapidityAxis", {100, -1.f, 1.f}, "y"}; + ConfigurableAxis invLambdaMassAxis{"invLambdaMassAxis", {100, 1.07f, 1.17f}, "Inv. Mass (GeV/c^{2})"}; + } cascCfg; + + // Topology bundle for "primary-Λ-on-data" trigger purity. + // Wrapped in a ConfigurableGroup to (a) keep them under one logical + // section in JSON and (b) avoid blowing the StructToTuple 99-member + // limit of the parent task struct (the same trick we used + // for cascCfg). + // Defaults are tuned for pp/pPb at moderate Λ pT. PbPb may want a + // tighter cMaxV0Radius (large feed-down tail) and a looser + // cMinDauItsNCls (low-pT acceptance loss in central events). + struct : ConfigurableGroup { + // Master switches. + Configurable cPrimEnable{"cPrimEnable", true, "Compute and write the lambdatrack::passesPrimaryTopo flag"}; + Configurable cPrimRequireBothDauItsHits{"cPrimRequireBothDauItsHits", true, "Require BOTH V0 daughters to have >= cPrimMinDauItsNCls ITS hits"}; + // Topological cuts. + Configurable cPrimMaxDcaV0ToPv{"cPrimMaxDcaV0ToPv", 0.10f, "[cm] V0 impact param to PV (primary-Λ knob)"}; + Configurable cPrimMinV0CosPA{"cPrimMinV0CosPA", 0.999f, "Min V0 cosPA(PV); primary Λ → 1 within ~3e-4"}; + Configurable cPrimMaxV0Radius{"cPrimMaxV0Radius", 30.0f, "[cm] Upper bound on V0 transverse decay radius"}; + Configurable cPrimMaxDauDcaToPv{"cPrimMaxDauDcaToPv", 1.0f, "[cm] Soft upper bound on |daughter DCA-XY-to-PV|"}; + Configurable cPrimMinDauItsNCls{"cPrimMinDauItsNCls", 1, "Minimum ITS hits per V0 daughter"}; + // Pseudo-proper-decay-length upper bound. Set to a few cτ + // beyond the primary peak to keep most primary Λ; feed-down's L_proper + // shifts upward by the parent flight, so a cap here cleanly trims the tail. + // Default 25 cm ≈ 3 cτ — tunable per centrality / collision system. + Configurable cPrimMaxLProper{"cPrimMaxLProper", 25.0f, "[cm] Upper bound on L_proper = L_xy * M_Λ / pT (kills feed-down tail)"}; + // Require at least ONE V0 daughter to have a hit on the + // ITS Inner Barrel (Layers 0,1,2 — bits 0,1,2 of itsClusterMap). + // Strong primary tag at low pT: a feed-down Λ at low pT typically + // has its decay vertex outside the IB, so daughters miss those layers. + Configurable cPrimRequireItsIBHit{"cPrimRequireItsIBHit", true, "Require >=1 V0 daughter with an ITS Inner-Barrel hit (Layers 0-2)"}; + // Topology consistency for cascades (here so it lives next to the rest + // of the purity controls; checked in processCandidate). + Configurable cReqCascRadiusLessThanV0Radius{"cReqCascRadiusLessThanV0Radius", true, "Cascade topology consistency: cascRadius < v0Radius"}; + } primCfg; + + // Plain (non-Configurable) AxisSpec — keeping these out of the group so we + // don't depend on ConfigurableGroup supporting non-Configurable members. + // They were never user-tunable in the original CSEL anyway. + AxisSpec cascItsClustersAxis{8, -0.5, 7.5, "number of ITS clusters"}; + AxisSpec cascTpcRowsAxis{160, -0.5, 159.5, "TPC crossed rows"}; + + // Histogram Registry (Lambda-side — preserves all LTP-style paths). HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Cascade-side HistogramRegistry — verbatim port of CSEL's + // initializer-list registry. Histogram paths under this registry are + // identical to what CSEL emitted before, so AnalysisResults.root structure + // is unchanged downstream. + HistogramRegistry cascRegistry{ + "cascRegistry", + { + {"hV0Radius", "hV0Radius", {HistType::kTH3F, {cascCfg.cascRadiusAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hCascRadius", "hCascRadius", {HistType::kTH3F, {cascCfg.cascRadiusAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hV0CosPA", "hV0CosPA", {HistType::kTH3F, {cascCfg.cascCpaAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hCascCosPA", "hCascCosPA", {HistType::kTH3F, {cascCfg.cascCpaAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hDCAPosToPV", "hDCAPosToPV", {HistType::kTH3F, {cascCfg.cascVertexAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hDCANegToPV", "hDCANegToPV", {HistType::kTH3F, {cascCfg.cascVertexAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hDCABachToPV", "hDCABachToPV", {HistType::kTH3F, {cascCfg.cascVertexAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hDCAV0ToPV", "hDCAV0ToPV", {HistType::kTH3F, {cascCfg.cascVertexAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hDCAV0Dau", "hDCAV0Dau", {HistType::kTH3F, {cascCfg.cascDcaAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hDCACascDau", "hDCACascDau", {HistType::kTH3F, {cascCfg.cascDcaAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hLambdaMass", "hLambdaMass", {HistType::kTH3F, {cascCfg.invLambdaMassAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + + {"hMassXiMinus", "hMassXiMinus", {HistType::kTH3F, {cascCfg.invXiMassAxis, cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}}}, + {"hMassXiPlus", "hMassXiPlus", {HistType::kTH3F, {cascCfg.invXiMassAxis, cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}}}, + {"hMassOmegaMinus", "hMassOmegaMinus", {HistType::kTH3F, {cascCfg.invOmegaMassAxis, cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}}}, + {"hMassOmegaPlus", "hMassOmegaPlus", {HistType::kTH3F, {cascCfg.invOmegaMassAxis, cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}}}, + + {"hTPCnCrossedRowsPos", "hTPCnCrossedRowsPos", {HistType::kTH3F, {cascTpcRowsAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hTPCnCrossedRowsNeg", "hTPCnCrossedRowsNeg", {HistType::kTH3F, {cascTpcRowsAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hTPCnCrossedRowsBach", "hTPCnCrossedRowsBach", {HistType::kTH3F, {cascTpcRowsAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hITSnClustersPos", "hITSnClustersPos", {HistType::kTH3F, {cascItsClustersAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hITSnClustersNeg", "hITSnClustersNeg", {HistType::kTH3F, {cascItsClustersAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hITSnClustersBach", "hITSnClustersBach", {HistType::kTH3F, {cascItsClustersAxis, cascCfg.invXiMassAxis, cascCfg.cascPtAxis}}}, + {"hTPCChi2Pos", "hTPCChi2Pos", {HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Pos"}}}}, + {"hTPCChi2Neg", "hTPCChi2Neg", {HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Neg"}}}}, + {"hTPCChi2Bach", "hTPCChi2Bach", {HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Bach"}}}}, + {"hITSChi2Pos", "hITSChi2Pos", {HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Pos"}}}}, + {"hITSChi2Neg", "hITSChi2Neg", {HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Neg"}}}}, + {"hITSChi2Bach", "hITSChi2Bach", {HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Bach"}}}}, + + {"hTriggerQA", "hTriggerQA", {HistType::kTH1F, {{2, -0.5, 1.5, "Trigger y/n"}}}}, + }, + }; + // initialize corr_factor objects std::vector> vCorrFactStrings = {{"hEffVsPtCentLambda", "hEffVsPtCentAntiLambda"}, {"hEffVsPtYCentLambda", "hEffVsPtYCentAntiLambda"}, @@ -391,6 +923,59 @@ struct LambdaTableProducer { ccdb->setURL(cUrlCCDB.value); ccdb->setCaching(true); + // [DEBUG] Echo the active configuration once at startup so the run log + // shows exactly which cuts were applied. Search the log for "[CFG-LTP]". + LOGF(info, + "[CFG-LTP] CentEst=%d Vz=[%.2f,%.2f] Mult=[%.2f,%.2f] | " + "trig: sel8=%d int7=%d sel7=%d tvx=%d tfBorder=%d itsROBorder=%d " + "itsTpcVtx=%d pileup=%d zVtxTimeDiff=%d goodITS=%d | " + "track: pT=[%.3f,%.3f] |eta|<%.2f tpcCR>=%d tpcCR/Findable>=%.2f " + "tpcShared<=%.2f tpcChi2<=%.2f tpcNsigma=%.2f rmAmbig=%d | " + "V0: dcaPr>=%.3f dcaPi>=%.3f dcaDau=[%.3f,%.3f] dcaV0=[%.3f,%.3f] " + "r=[%.2f,%.2f] ctau=[%.2f,%.2f] cosPA>=%.4f K0Rej=%.3f(flag=%d) | " + "V0kin: m=[%.3f,%.3f] pT=[%.2f,%.2f] |y|<%.2f doEta=%d | " + "MC: trueLambda=%d primSec=%d checkDau=%d " + "genDecCh=%d momReso=%d | corr=%d effFlag=%d primFracFlag=%d", + static_cast(cCentEstimator), static_cast(cMinZVtx), static_cast(cMaxZVtx), + static_cast(cMinMult), static_cast(cMaxMult), + static_cast(cSel8Trig), static_cast(cInt7Trig), static_cast(cSel7Trig), + static_cast(cTriggerTvxSel), static_cast(cTFBorder), static_cast(cNoItsROBorder), + static_cast(cItsTpcVtx), static_cast(cPileupReject), static_cast(cZVtxTimeDiff), + static_cast(cIsGoodITSLayers), + static_cast(cTrackMinPt), static_cast(cTrackMaxPt), static_cast(cTrackEtaCut), + static_cast(cMinTpcCrossedRows), static_cast(cMinTpcCROverCls), + static_cast(cMaxTpcSharedClusters), static_cast(cMaxChi2Tpc), + static_cast(cTpcNsigmaCut), static_cast(cRemoveAmbiguousTracks), + static_cast(cMinDcaProtonToPV), static_cast(cMinDcaPionToPV), + static_cast(cMinV0DcaDaughters), static_cast(cMaxV0DcaDaughters), + static_cast(cMinDcaV0ToPV), static_cast(cMaxDcaV0ToPV), + static_cast(cMinV0TransRadius), static_cast(cMaxV0TransRadius), + static_cast(cMinV0CTau), static_cast(cMaxV0CTau), + static_cast(cMinV0CosPA), static_cast(cKshortRejMassWindow), static_cast(cKshortRejFlag), + static_cast(cMinV0Mass), static_cast(cMaxV0Mass), + static_cast(cMinV0Pt), static_cast(cMaxV0Pt), static_cast(cMaxV0Rap), static_cast(cDoEtaAnalysis), + static_cast(cSelectTrueLambda), static_cast(cSelMCPSV0), + static_cast(cCheckRecoDauFlag), + static_cast(cGenDecayChannel), static_cast(cRecoMomResoFlag), + static_cast(cCorrectionFlag), static_cast(cGetEffFact), static_cast(cGetPrimFrac)); + + // Unified event-selection log (compare to [EVENTSEL-CSEL]). + lcorr_evsel::logEventCuts("EVENTSEL-LTP", buildEventCuts()); + + // Primary-Λ topology bundle + diagnostic-mode flag. + LOGF(info, + "[CFG-LCP-prim] enable=%d reqBothDauITS=%d reqItsIB=%d " + "maxDcaV0ToPv=%.4f minV0CosPA=%.6f maxV0Radius=%.2f maxLProper=%.2f " + "maxDauDcaToPv=%.4f minDauItsNCls=%d " + "cascRadius(primCfg.cPrimEnable), static_cast(primCfg.cPrimRequireBothDauItsHits), + static_cast(primCfg.cPrimRequireItsIBHit), + static_cast(primCfg.cPrimMaxDcaV0ToPv), static_cast(primCfg.cPrimMinV0CosPA), + static_cast(primCfg.cPrimMaxV0Radius), static_cast(primCfg.cPrimMaxLProper), + static_cast(primCfg.cPrimMaxDauDcaToPv), static_cast(primCfg.cPrimMinDauItsNCls), + static_cast(primCfg.cReqCascRadiusLessThanV0Radius), + static_cast(cFillLambdaTreeAllCandidates)); + // initialize axis specifications const AxisSpec axisCols(5, 0.5, 5.5, ""); const AxisSpec axisTrks(30, 0.5, 30.5, ""); @@ -531,82 +1116,160 @@ struct LambdaTableProducer { histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtCent, "kPFCorrPtCent"); histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtRapCent, "kPFCorrPtRapCent"); histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNoPFCorr, "kNoPFCorr"); + + // ===== Cascade-side init (verbatim from former CascadeSelector) ===== + // Cascade-side selection-status & event-selection histograms. + auto h = cascRegistry.add("hSelectionStatus", "hSelectionStatus", HistType::kTH1I, {{10, 0, 10, "status"}}); + h->GetXaxis()->SetBinLabel(1, "All"); + h->GetXaxis()->SetBinLabel(2, "nTPC OK"); + h->GetXaxis()->SetBinLabel(3, "nITS OK"); + h->GetXaxis()->SetBinLabel(4, "track Chi2 OK"); + h->GetXaxis()->SetBinLabel(5, "Topo OK"); + h->GetXaxis()->SetBinLabel(6, "Track eta OK"); + h->GetXaxis()->SetBinLabel(7, "Cascade eta OK"); + h->GetXaxis()->SetBinLabel(8, "V0 PID OK"); + h->GetXaxis()->SetBinLabel(9, "Bach PID OK"); + + // Cascade-side hEventSel: bins reinterpreted as + // 0 = "All cascades seen" + // 1 = "Event accepted by LTP -> casc proceeds to candidate cuts" + // 2 = "Event rejected by LTP -> casc auto-flagged 0" + auto hEventSel = cascRegistry.add("hEventSel", "hEventSel", HistType::kTH1I, {{3, 0, 3, "0=all, 1=LTP-accepted, 2=LTP-rejected"}}); + hEventSel->GetXaxis()->SetBinLabel(1, "All"); + hEventSel->GetXaxis()->SetBinLabel(2, "LTP-accepted"); + hEventSel->GetXaxis()->SetBinLabel(3, "LTP-rejected"); + + // Cascade MC reco-matched histograms (created on demand). + if (doprocessMCRecoRun3) { + cascRegistry.add("truerec/hV0Radius", "hV0Radius", HistType::kTH1F, {cascCfg.cascRadiusAxis}); + cascRegistry.add("truerec/hCascRadius", "hCascRadius", HistType::kTH1F, {cascCfg.cascRadiusAxis}); + cascRegistry.add("truerec/hV0CosPA", "hV0CosPA", HistType::kTH1F, {cascCfg.cascCpaAxis}); + cascRegistry.add("truerec/hCascCosPA", "hCascCosPA", HistType::kTH1F, {cascCfg.cascCpaAxis}); + cascRegistry.add("truerec/hDCAPosToPV", "hDCAPosToPV", HistType::kTH1F, {cascCfg.cascVertexAxis}); + cascRegistry.add("truerec/hDCANegToPV", "hDCANegToPV", HistType::kTH1F, {cascCfg.cascVertexAxis}); + cascRegistry.add("truerec/hDCABachToPV", "hDCABachToPV", HistType::kTH1F, {cascCfg.cascVertexAxis}); + cascRegistry.add("truerec/hDCAV0ToPV", "hDCAV0ToPV", HistType::kTH1F, {cascCfg.cascVertexAxis}); + cascRegistry.add("truerec/hDCAV0Dau", "hDCAV0Dau", HistType::kTH1F, {cascCfg.cascDcaAxis}); + cascRegistry.add("truerec/hDCACascDau", "hDCACascDau", HistType::kTH1F, {cascCfg.cascDcaAxis}); + cascRegistry.add("truerec/hLambdaMass", "hLambdaMass", HistType::kTH1F, {cascCfg.invLambdaMassAxis}); + cascRegistry.add("truerec/hTPCnCrossedRowsPos", "hTPCnCrossedRowsPos", HistType::kTH1F, {cascTpcRowsAxis}); + cascRegistry.add("truerec/hTPCnCrossedRowsNeg", "hTPCnCrossedRowsNeg", HistType::kTH1F, {cascTpcRowsAxis}); + cascRegistry.add("truerec/hTPCnCrossedRowsBach", "hTPCnCrossedRowsBach", HistType::kTH1F, {cascTpcRowsAxis}); + cascRegistry.add("truerec/hITSnClustersPos", "hITSnClustersPos", HistType::kTH1F, {cascItsClustersAxis}); + cascRegistry.add("truerec/hITSnClustersNeg", "hITSnClustersNeg", HistType::kTH1F, {cascItsClustersAxis}); + cascRegistry.add("truerec/hITSnClustersBach", "hITSnClustersBach", HistType::kTH1F, {cascItsClustersAxis}); + cascRegistry.add("truerec/hTPCChi2Pos", "hTPCChi2Pos", HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Pos"}}); + cascRegistry.add("truerec/hTPCChi2Neg", "hTPCChi2Neg", HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Neg"}}); + cascRegistry.add("truerec/hTPCChi2Bach", "hTPCChi2Bach", HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Bach"}}); + cascRegistry.add("truerec/hITSChi2Pos", "hITSChi2Pos", HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Pos"}}); + cascRegistry.add("truerec/hITSChi2Neg", "hITSChi2Neg", HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Neg"}}); + cascRegistry.add("truerec/hITSChi2Bach", "hITSChi2Bach", HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Bach"}}); + cascRegistry.add("truerec/hXiMinus", "hXiMinus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("truerec/hXiPlus", "hXiPlus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("truerec/hOmegaMinus", "hOmegaMinus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("truerec/hOmegaPlus", "hOmegaPlus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + } + + // Cascade MC gen-only histograms (created on demand). + if (doprocessCascadeGenMC) { + cascRegistry.add("gen/hXiMinus", "hXiMinus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("gen/hXiPlus", "hXiPlus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("gen/hOmegaMinus", "hOmegaMinus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("gen/hOmegaPlus", "hOmegaPlus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("genwithrec/hXiMinus", "hXiMinus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("genwithrec/hXiPlus", "hXiPlus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("genwithrec/hOmegaMinus", "hOmegaMinus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("genwithrec/hOmegaPlus", "hOmegaPlus", HistType::kTH2F, {cascCfg.cascPtAxis, cascCfg.cascRapidityAxis}); + cascRegistry.add("genwithrec/hNevents", "hNevents", HistType::kTH1F, {{1, 0, 1, "N generated events with reconstructed event"}}); + cascRegistry.add("gen/hNevents", "hNevents", HistType::kTH1F, {{1, 0, 1, "N generated events"}}); + } + + // Loud deprecation note: CSEL-style event-selection knobs no + // longer affect what events are accepted. The single source of truth is + // LTP's selCollision (configured via cSel8Trig, cMin/cMaxZVtx, + // cPileupReject, cTriggerTvxSel, cTFBorder, cNoItsROBorder, etc.). + LOGF(info, + "CSEL-style event-selection configurables are now ignored: " + "doSel8=%d doNoSameBunchPileUp=%d INEL=%d maxVertexZ=%.2f doTFBorderCut=%d. " + "Event acceptance is driven exclusively by LTP's selCollision (see " + "[CFG-LTP] above). To change event cuts, set the corresponding " + "cSel8Trig / cPileupReject / cTriggerTvxSel / etc. on this same task.", + static_cast(cascCfg.doSel8), static_cast(cascCfg.doNoSameBunchPileUp), static_cast(cascCfg.INEL), + static_cast(cascCfg.maxVertexZ), static_cast(cascCfg.doTFBorderCut)); + + // CCDB URL conflict check. + if (std::string(cUrlCCDB.value) != std::string(cascCfg.ccdbUrl.value)) { + LOGF(warning, + "cUrlCCDB and cascCfg.ccdbUrl differ ('%s' vs '%s'). Only " + "cUrlCCDB drives the live CCDB service; cascCfg.ccdbUrl is cosmetic.", + cUrlCCDB.value.c_str(), cascCfg.ccdbUrl.value.c_str()); + } + } + + // Build the EventCuts struct from this producer's configurables. + // Defaults map 1:1 to current behaviour: only the Configurables already + // toggled on are forwarded as `use*=true` to the shared selector. + // Cannot be `const`: Configurable's implicit conversion to T + // is non-const, so reading any Configurable<> in a const-qualified method + // fails. Drop the const qualifier; the function does not modify state anyway. + lcorr_evsel::EventCuts buildEventCuts() + { + lcorr_evsel::EventCuts c; + c.useVtxZ = true; // LTP always applies VtxZ + c.minVtxZ = cMinZVtx; + c.maxVtxZ = cMaxZVtx; + c.useSel8 = cSel8Trig; + c.useInt7 = cInt7Trig; + c.useSel7 = cSel7Trig; + // Centrality range is meaningful for any non-trivial window; + // matches old behaviour exactly (was applied unconditionally). + c.useCentRange = true; + c.minCent = cMinMult; + c.maxCent = cMaxMult; + // INEL is not part of LTP today; leave off. + c.useInel = false; + c.useTriggerTvx = cTriggerTvxSel; + c.useTfBorder = cTFBorder; + c.useItsRoBorder = cNoItsROBorder; + c.useItsTpcVtx = cItsTpcVtx; + c.useNoSameBunchPileup = cPileupReject; + c.useZVtxTimeDiff = cZVtxTimeDiff; + c.useIsGoodITSLayers = cIsGoodITSLayers; + return c; } template bool selCollision(C const& col) { - // VtxZ Selection - if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { - return false; - } - - if constexpr (run == kRun3) { // Run3 Min-Bias Trigger - // select centrality estimator + // LTP-specific side-effects: pick centrality estimator, then + // delegate to the shared selector. After acceptance, set `mult` global. + if constexpr (run == kRun3) { if (cCentEstimator == kCentFT0M) { cent = col.centFT0M(); } else if (cCentEstimator == kCentFV0A) { cent = col.centFV0A(); } - if (cSel8Trig && !col.sel8()) { - return false; - } - } else { // Run2 Min-Bias Trigger + } else { cent = col.centRun2V0M(); - if (cInt7Trig && !col.alias_bit(kINT7)) { - return false; - } - if (cSel7Trig && !col.sel7()) { - return false; - } - } - - if (cent <= cMinMult || cent >= cMaxMult) { // select centrality percentile class - return false; - } - - if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { - return false; - } - - if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return false; - } - - if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return false; - } - - if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { - return false; } - if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + auto cuts = buildEventCuts(); + const char* reason = "ok"; + if (!lcorr_evsel::applyEventSelection(col, cuts, cent, reason)) { + LOGF(debug, "[LTP] reject: %s (Vz=%.3f cent=%.2f)", + reason, static_cast(col.posZ()), static_cast(cent)); return false; } - if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { - return false; - } - - if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { - return false; - } - - // Set Multiplicity + // Multiplicity (LTP-specific) — distinct from CSEL's multNTracksPVeta1. mult = col.multNTracksPV(); - return true; } // Kinematic Selection - bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) + bool kinCutSelection(float const& ptVal, float const& rapVal, float const& ptMin, float const& ptMax, float const& rapMax) { - if (pt <= ptMin || pt >= ptMax || rap >= rapMax) { - return false; - } - - return true; + return (ptVal > ptMin && ptVal < ptMax && rapVal < rapMax); } // Track Selection @@ -753,18 +1416,14 @@ struct LambdaTableProducer { if (!lambdaFlag && !antiLambdaFlag) { // neither Lambda nor Anti-Lambda histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaNotAntiLambda); return false; - } else if (lambdaFlag && antiLambdaFlag) { // check if the track is identified as lambda and anti-lambda both (DISCARD THIS TRACK) + } + if (lambdaFlag && antiLambdaFlag) { // identified as both lambda and anti-lambda (DISCARD THIS TRACK) histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsBothLambdaAntiLambda); return false; } - if (lambdaFlag || antiLambdaFlag) { - return true; - } - - histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaAfterSel); - - return false; + // Exactly one hypothesis survived. + return true; } template @@ -785,14 +1444,14 @@ struct LambdaTableProducer { histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0DauTrackSel); // Apply Kinematic Selection - float rap = 0.; + float rapV0 = 0.; if (!cDoEtaAnalysis) { - rap = std::abs(v0.yLambda()); + rapV0 = std::abs(v0.yLambda()); } else { - rap = std::abs(v0.eta()); + rapV0 = std::abs(v0.eta()); } - if (!kinCutSelection(v0.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + if (!kinCutSelection(v0.pt(), rapV0, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { return false; } @@ -809,6 +1468,79 @@ struct LambdaTableProducer { return true; } + // Per-V0 cut bitmask, computed with NO short-circuit: each bit mirrors a + // stage of the selV0Particle chain, so any cut combination can be replayed + // offline from the tree alone. WARNING: duplicates the cut conditions — + // keep in sync when changing any bound. ~10 comparisons per V0. + template + uint32_t computeLambdaCutBits(C const& col, V const& v0, T const&, + ParticleType v0Type, bool ambVeto) + { + uint32_t bits = 0; + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + + // 0. Mass window (matched to v0Type) + float mass = (v0Type == kLambda) ? v0.mLambda() : v0.mAntiLambda(); + if (mass > cMinV0Mass && mass < cMaxV0Mass) + bits |= (1u << kCutMassWindow); + + // 1. Daughter PID (matched to v0Type) + float nSPosPr = posTrack.tpcNSigmaPr(), nSNegPi = negTrack.tpcNSigmaPi(); + float nSPosPi = posTrack.tpcNSigmaPi(), nSNegPr = negTrack.tpcNSigmaPr(); + bool pidOk = (v0Type == kLambda) + ? (std::abs(nSPosPr) < cTpcNsigmaCut && std::abs(nSNegPi) < cTpcNsigmaCut) + : (std::abs(nSNegPr) < cTpcNsigmaCut && std::abs(nSPosPi) < cTpcNsigmaCut); + if (pidOk) + bits |= (1u << kCutDauPid); + + // 2. Daughter track quality + if (selTrack(posTrack) && selTrack(negTrack)) + bits |= (1u << kCutDauTrackQual); + + // 3. Daughter DCA-to-PV + float dcaProton = (v0Type == kLambda) ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + float dcaPion = (v0Type == kLambda) ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); + if (dcaProton >= cMinDcaProtonToPV && dcaPion >= cMinDcaPionToPV) + bits |= (1u << kCutDauDcaToPV); + + // 4. Kinematic + float rapVal = cDoEtaAnalysis ? std::abs(v0.eta()) : std::abs(v0.yLambda()); + if (kinCutSelection(v0.pt(), rapVal, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) + bits |= (1u << kCutKinematic); + + // 5. dcaV0Daughters window + if (v0.dcaV0daughters() > cMinV0DcaDaughters && v0.dcaV0daughters() < cMaxV0DcaDaughters) + bits |= (1u << kCutDcaV0Dau); + + // 6. dcav0topv window + if (v0.dcav0topv() > cMinDcaV0ToPV && v0.dcav0topv() < cMaxDcaV0ToPV) + bits |= (1u << kCutDcaV0ToPV); + + // 7. v0radius window + if (v0.v0radius() > cMinV0TransRadius && v0.v0radius() < cMaxV0TransRadius) + bits |= (1u << kCutV0Radius); + + // 8. ctau + float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; + if (ctau > cMinV0CTau && ctau < cMaxV0CTau) + bits |= (1u << kCutCtau); + + // 9. cosPA + if (v0.v0cosPA() > cMinV0CosPA) + bits |= (1u << kCutCosPA); + + // 10. K0s rejection (always passes when flag off) + if (!cKshortRejFlag || std::abs(v0.mK0Short() - MassK0Short) > cKshortRejMassWindow) + bits |= (1u << kCutK0sRej); + + // 11. Ambiguous-track veto (passed when veto disabled OR no ambiguity) + if (ambVeto) + bits |= (1u << kCutAmbiguousVeto); + + return bits; + } + template bool hasAmbiguousDaughters(V const& v0, T const&) { @@ -897,7 +1629,7 @@ struct LambdaTableProducer { } // Get from CCDB - auto ccdbObj = ccdb->getForTimeStamp(cPathCCDB.value, -1); + auto ccdbObj = ccdb->getForTimeStamp(cPathCCDB.value, 1); // Check CCDB Object if (!ccdbObj) { @@ -907,52 +1639,89 @@ struct LambdaTableProducer { // initialize efficiency factor and primary fraction values float effCorrFact = 1., primFrac = 1.; - float rap = (cDoEtaAnalysis) ? v0.eta() : v0.yLambda(); + float rapV0 = (cDoEtaAnalysis) ? v0.eta() : v0.yLambda(); // Get Efficiency Factor if (cGetEffFact) { - TObject* objEff = reinterpret_cast(ccdbObj->FindObject(Form("%s", vCorrFactStrings[cCorrFactHist][part].c_str()))); - TH1F* histEff = reinterpret_cast(objEff->Clone()); - if (histEff->GetDimension() == TwoDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtCent); - effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt())); - } else if (histEff->GetDimension() == ThreeDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtRapCent); - effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt(), rap)); + // Guard against nullptr from FindObject — the named + // histogram may be missing from the CCDB TList. Previously the + // subsequent ->Clone() dereferenced nullptr and crashed. + const auto effName = Form("%s", vCorrFactStrings[cCorrFactHist][part].c_str()); + auto* objEff = ccdbObj->FindObject(effName); + if (!objEff) { + LOGF(warning, "[CCDB] Efficiency histogram '%s' not found; using effCorrFact=1.0", effName); + effCorrFact = 1.f; } else { - histos.fill(HIST("Tracks/h1f_tracks_info"), kNoEffCorr); - LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); - effCorrFact = 1.; + auto* histEff = dynamic_cast(objEff->Clone()); + if (histEff->GetDimension() == TwoDimCorr) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtCent); + effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt())); + } else if (histEff->GetDimension() == ThreeDimCorr) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtRapCent); + effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt(), rapV0)); + } else { + histos.fill(HIST("Tracks/h1f_tracks_info"), kNoEffCorr); + LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); + effCorrFact = 1.; + } + delete histEff; } - delete histEff; } // Get Primary Fraction // (The dimension of this could be different than efficiency because of large errors !!!) if (cGetPrimFrac) { - TObject* objPrm = reinterpret_cast(ccdbObj->FindObject(Form("%s", vPrimFracStrings[cPrimFracHist][part].c_str()))); - TH1F* histPrm = reinterpret_cast(objPrm->Clone()); - if (histPrm->GetDimension() == TwoDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kPFCorrPtCent); - primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt())); - } else if (histPrm->GetDimension() == ThreeDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kPFCorrPtRapCent); - primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt(), rap)); + // Same null-guard as the efficiency lookup above. + const auto pfName = Form("%s", vPrimFracStrings[cPrimFracHist][part].c_str()); + auto* objPrm = ccdbObj->FindObject(pfName); + if (!objPrm) { + LOGF(warning, "[CCDB] Primary-fraction histogram '%s' not found; using primFrac=1.0", pfName); + primFrac = 1.f; } else { - histos.fill(HIST("Tracks/h1f_tracks_info"), kNoPFCorr); - LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); - primFrac = 1.; + auto* histPrm = dynamic_cast(objPrm->Clone()); + if (histPrm->GetDimension() == TwoDimCorr) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kPFCorrPtCent); + primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt())); + } else if (histPrm->GetDimension() == ThreeDimCorr) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kPFCorrPtRapCent); + primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt(), rapV0)); + } else { + histos.fill(HIST("Tracks/h1f_tracks_info"), kNoPFCorr); + LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); + primFrac = 1.; + } + delete histPrm; } - delete histPrm; } return primFrac * effCorrFact; } + // Truth-level mother PDG of a (possibly truth-matched) reco V0. + // Returns 0 on data and on MC V0s that have no MC particle / no mother. + // Used to populate aod::lambdatrack::MotherPdg so downstream consumers can + // tag Ξ feed-down (motherPdg == ±3312), Ω feed-down (±3334), Σ⁰ resonance + // feed-down (±3212), etc. without rerunning truth matching. + template + int getLambdaMotherPdg(V const& v0) + { + if (!v0.has_mcParticle()) + return 0; + auto mcpart = v0.template mcParticle_as(); + if (!mcpart.has_mothers()) + return 0; + return mcpart.template mothers_first_as().pdgCode(); + } + template void fillLambdaMothers(V const& v0, T const&) { auto mcpart = v0.template mcParticle_as(); + // Defensive — a "secondary" V0 should always have ≥1 mother + // by definition, but some AOD generators occasionally strip mother links. + // Without this guard, lambdaMothers[0] dereferences an empty range → UB. + if (!mcpart.has_mothers()) + return; auto lambdaMothers = mcpart.template mothers_as(); histos.fill(HIST("Tracks/h2f_lambda_mothers_pdg"), lambdaMothers[0].pdgCode(), v0.pt()); } @@ -960,7 +1729,7 @@ struct LambdaTableProducer { template void fillLambdaQAHistos(C const& col, V const& v0, T const&) { - static constexpr std::string_view SubDir[] = {"QA/Lambda/", "QA/AntiLambda/"}; + static constexpr std::array SubDir = {"QA/Lambda/", "QA/AntiLambda/"}; // daugthers auto postrack = v0.template posTrack_as(); @@ -1008,15 +1777,15 @@ struct LambdaTableProducer { // Fill Lambda Kinematic Histograms template - void fillKinematicHists(float const& pt, float const& eta, float const& y, float const& phi) + void fillKinematicHists(float const& ptVal, float const& etaVal, float const& y, float const& phiVal) { - static constexpr std::string_view SubDirRG[] = {"McRec/", "McGen/"}; - static constexpr std::string_view SubDirPart[] = {"Lambda/", "AntiLambda/"}; + static constexpr std::array SubDirRG = {"McRec/", "McGen/"}; + static constexpr std::array SubDirPart = {"Lambda/", "AntiLambda/"}; - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPt"), pt); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), eta); + histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPt"), ptVal); + histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), etaVal); histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hRap"), y); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), phi); + histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), phiVal); } // Reconstructed Level Tables @@ -1084,21 +1853,31 @@ struct LambdaTableProducer { rap = v0.yLambda(); phi = v0.phi(); + // Mother PDG: 0 on data, parent PDG on MC. + int motherPdg = 0; + // do MC analysis if constexpr (dmc == kMC) { histos.fill(HIST("Tracks/h2f_tracks_pid_before_sel"), v0.mcParticle().pdgCode(), v0.pt()); - // Get Primary/Secondary Lambda - if (cSelMCPSV0) { - v0PrmScdType = isPrimaryV0(v0); - } + // Always tag primary/secondary from MC truth on MC reco. + // Previously gated by cSelMCPSV0 — when disabled, v0PrmScdType + // stayed kPrimary for ALL candidates, which silently broke the + // downstream goodPrimaryLambda partition (it became equivalent + // to goodLambda, accepting all feed-down Λs). + // cSelMCPSV0 is kept as a no-op Configurable for JSON back-compat; + // the producer now unconditionally annotates v0PrmScd. Users who + // want "treat all as primary" should toggle the correlator-side + // cUsePrimaryLambdasOnly instead. + v0PrmScdType = isPrimaryV0(v0); // check for true Lambda/Anti-Lambda if (cSelectTrueLambda && !selTrueMcRecLambda(v0, tracks)) { continue; } - // get mothers information + // get mothers information (also populated as table column below) + motherPdg = getLambdaMotherPdg(v0); if (v0PrmScdType == kSecondary) { fillLambdaMothers(v0, tracks); } @@ -1136,10 +1915,101 @@ struct LambdaTableProducer { fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } - // Fill Lambda/AntiLambda Table + // Per-V0 topology snapshot — written on every Λ row so + // (i) the optional Λ TTree carries the variables for a downstream + // template fit, (ii) the partition can gate on passesPrimaryTopo + // on data without re-resolving daughter tracks at correlator time. + auto posTrk = v0.template posTrack_as(); + auto negTrk = v0.template negTrack_as(); + float dcaV0ToPV_v = v0.dcav0topv(); // V0 line vs PV + float v0Radius_v = v0.v0radius(); // transverse decay radius + auto posItsNCls_v = static_cast(posTrk.itsNCls()); + auto negItsNCls_v = static_cast(negTrk.itsNCls()); + // ITS hit-map per daughter (uint8_t bitmask, bit i = layer i). + auto posItsClusterMap_v = static_cast(posTrk.itsClusterMap()); + auto negItsClusterMap_v = static_cast(negTrk.itsClusterMap()); + // Per-daughter signed DCA-XY-to-PV (V0Datas already exposes + // these as helices propagated to the PV). + float posDcaXY_v = v0.dcapostopv(); + float negDcaXY_v = v0.dcanegtopv(); + // Pseudo-proper transverse decay length L_proper. + // Guard against pT==0 to avoid div-by-zero for pathological rows. + float lProper_v = (v0.pt() > 0.f) + ? (v0Radius_v * static_cast(MassLambda0) / v0.pt()) + : 0.f; + + // Raw cut-input variables for offline re-cutting. + float tpcNSigmaPosPr_v = posTrk.tpcNSigmaPr(); + float tpcNSigmaNegPi_v = negTrk.tpcNSigmaPi(); + float tpcNSigmaPosPi_v = posTrk.tpcNSigmaPi(); + float tpcNSigmaNegPr_v = negTrk.tpcNSigmaPr(); + float mK0Short_v = v0.mK0Short(); + float qtArm_v = v0.qtarm(); + float alphaArm_v = v0.alpha(); + float cTau_v = v0.distovertotmom(collision.posX(), + collision.posY(), + collision.posZ()) * + MassLambda0; + // When the producer-side gate is disabled, default to + // `true` so the column means "no opinion / everything passes". The + // previous default of `false` silently emptied the partition when the + // correlator's cPrimaryRequireTopo was flipped on without enabling + // cPrimEnable here — a confusing zero-pair failure mode. + bool passesPrim = !primCfg.cPrimEnable; + if (primCfg.cPrimEnable) { + bool topoOk = + std::abs(dcaV0ToPV_v) < primCfg.cPrimMaxDcaV0ToPv && + v0.v0cosPA() > primCfg.cPrimMinV0CosPA && + v0Radius_v < primCfg.cPrimMaxV0Radius && + std::abs(posDcaXY_v) < primCfg.cPrimMaxDauDcaToPv && + std::abs(negDcaXY_v) < primCfg.cPrimMaxDauDcaToPv && + lProper_v < primCfg.cPrimMaxLProper; + bool itsOk = primCfg.cPrimRequireBothDauItsHits + ? (posItsNCls_v >= primCfg.cPrimMinDauItsNCls && negItsNCls_v >= primCfg.cPrimMinDauItsNCls) + : (posItsNCls_v >= primCfg.cPrimMinDauItsNCls || negItsNCls_v >= primCfg.cPrimMinDauItsNCls); + // ITS-IB requirement (bits 0,1,2 = Layers 0,1,2). + // OR'd across both daughters: at least ONE daughter must have an + // IB hit. Optional via the Configurable. + bool itsIBOk = !primCfg.cPrimRequireItsIBHit || (((posItsClusterMap_v | negItsClusterMap_v) & lcorr_const::kItsIBMask) != 0); + passesPrim = topoOk && itsOk && itsIBOk; + } + + // Per-V0 cut bitmask. Bits 0-11 reflect the standalone-V0 + // selection chain; bit 12 reflects MC truth (always 1 on data; set + // post-hoc on MC reco); bit 13 reflects passesPrimaryTopo. Computed + // inline so the row carries the full diagnostic. + // hasAmbiguousDaughters() requires aod::TrackCompColls which is only + // joined in the Run3 Tracks alias, so guard with `if constexpr`. + bool ambVetoOk = true; + if constexpr (run == kRun3) { + ambVetoOk = !cRemoveAmbiguousTracks || !hasAmbiguousDaughters(v0, tracks); + } + uint32_t cutBits_v = computeLambdaCutBits(collision, v0, tracks, v0Type, ambVetoOk); + // Bit 12 (MC truth) — set independently of cSelectTrueLambda. + // On data: always set (truth is N/A; bit means "no MC reason to drop"). + // On MC reco: set iff selTrueMcRecLambda actually passes. Previously + // this was set unconditionally on the assumption that cSelectTrueLambda + // had already filtered the survivors, but when cSelectTrueLambda=false + // the filter never runs and the bit became unreliable. + bool isTrueMcLam = true; + if constexpr (dmc == kMC) { + isTrueMcLam = selTrueMcRecLambda(v0, tracks); + } + if (isTrueMcLam) + cutBits_v |= (1u << kCutMcTrueLambda); + if (passesPrim) + cutBits_v |= (1u << kCutPhase10Prim); + + // Fill Λ/Λ̄ row — motherPdg · topo+flag · ITS maps · L_proper + dau DCAs · cutBits + raw inputs lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.px(), v0.py(), v0.pz(), - pt, eta, phi, rap, mass, v0.template posTrack_as().index(), v0.template negTrack_as().index(), - v0.v0cosPA(), v0.dcaV0daughters(), (int8_t)v0Type, v0PrmScdType, corr_fact); + pt, eta, phi, rap, mass, posTrk.index(), negTrk.index(), + v0.v0cosPA(), v0.dcaV0daughters(), static_cast(v0Type), v0PrmScdType, corr_fact, motherPdg, + dcaV0ToPV_v, v0Radius_v, posItsNCls_v, negItsNCls_v, passesPrim, + posItsClusterMap_v, negItsClusterMap_v, + lProper_v, posDcaXY_v, negDcaXY_v, + cutBits_v, + tpcNSigmaPosPr_v, tpcNSigmaNegPi_v, tpcNSigmaPosPi_v, tpcNSigmaNegPr_v, + mK0Short_v, qtArm_v, alphaArm_v, cTau_v); } } @@ -1153,7 +2023,7 @@ struct LambdaTableProducer { // initialize track objects ParticleType v0Type = kLambda; PrmScdType v0PrmScdType = kPrimary; - float rap = 0.; + float rapGen = 0.; for (auto const& mcpart : mcParticles) { // check for Lambda first @@ -1174,13 +2044,13 @@ struct LambdaTableProducer { // Decide Eta/Rap if (!cDoEtaAnalysis) { - rap = mcpart.y(); + rapGen = mcpart.y(); } else { - rap = mcpart.eta(); + rapGen = mcpart.eta(); } // Apply Kinematic Acceptance - if (!kinCutSelection(mcpart.pt(), std::abs(rap), cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + if (!kinCutSelection(mcpart.pt(), std::abs(rapGen), cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { continue; } @@ -1247,7 +2117,7 @@ struct LambdaTableProducer { // Fill Lambda McGen Table lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.px(), mcpart.py(), mcpart.pz(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), RecoDecay::m(mcpart.p(), mcpart.e()), - daughterIDs[0], daughterIDs[1], (int8_t)v0Type, -999., -999., v0PrmScdType, 1.); + daughterIDs[0], daughterIDs[1], static_cast(v0Type), -999., -999., v0PrmScdType, 1.); } } @@ -1288,76 +2158,505 @@ struct LambdaTableProducer { SliceCache cache; Preslice> perCollision = aod::v0data::collisionId; - using CollisionsRun3 = soa::Join; - using CollisionsRun2 = soa::Join; - using Tracks = soa::Join; - using TracksRun2 = soa::Join; - using TracksMC = soa::Join; - using TracksMCRun2 = soa::Join; - using McV0Tracks = soa::Join; - - void processDataRun3(CollisionsRun3::iterator const& collision, aod::V0Datas const& V0s, Tracks const& tracks) - { - fillLambdaRecoTables(collision, V0s, tracks); - } - - PROCESS_SWITCH(LambdaTableProducer, processDataRun3, "Process for Run3 DATA", true); - - void processDataRun2(CollisionsRun2::iterator const& collision, aod::V0Datas const& V0s, TracksRun2 const& tracks) - { - fillLambdaRecoTables(collision, V0s, tracks); - } - - PROCESS_SWITCH(LambdaTableProducer, processDataRun2, "Process for Run2 DATA", false); - - void processMCRecoRun3(soa::Join::iterator const& collision, aod::McCollisions const&, - McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const&) + // =========================================================================== + // Cascade-side helpers (verbatim from former CascadeSelector, + // s/registry/cascRegistry/g). + // =========================================================================== + + // Templated on TTracks so the call site can pass whichever + // track type is bound in its process function (Tracks for data, + // TracksMC for MC reco). Avoids hardcoding FullTracksExtIUWithPID, which + // is TracksIU-based and incompatible with our merged Tracks-based binding. + template + void fillMatchedHistos(LabeledCascades::iterator const& rec, int flag, TCollision const& collision) { - // check collision - if (!selCollision(collision)) { + if (flag == 0) return; + if (!rec.has_mcParticle()) + return; + auto gen = rec.mcParticle(); + if (!gen.isPhysicalPrimary()) + return; + int genpdg = gen.pdgCode(); + // flag<3 (== 1 or 2) means Ξ-eligible; flag>1 (== 2 or 3) + // means Ω-eligible. Spelled out with named constants. + const bool xiLike = (flag == lcorr_const::kFlagXiOnly || flag == lcorr_const::kFlagXiAndOmega); + const bool omLike = (flag == lcorr_const::kFlagXiAndOmega || flag == lcorr_const::kFlagOmegaOnly); + if ((xiLike && std::abs(genpdg) == lcorr_const::kXiMinusPdg) || + (omLike && std::abs(genpdg) == lcorr_const::kOmegaMinusPdg)) { + cascRegistry.fill(HIST("truerec/hV0Radius"), rec.v0radius()); + cascRegistry.fill(HIST("truerec/hCascRadius"), rec.cascradius()); + cascRegistry.fill(HIST("truerec/hV0CosPA"), rec.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + cascRegistry.fill(HIST("truerec/hCascCosPA"), rec.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + cascRegistry.fill(HIST("truerec/hDCAPosToPV"), rec.dcapostopv()); + cascRegistry.fill(HIST("truerec/hDCANegToPV"), rec.dcanegtopv()); + cascRegistry.fill(HIST("truerec/hDCABachToPV"), rec.dcabachtopv()); + cascRegistry.fill(HIST("truerec/hDCAV0ToPV"), rec.dcav0topv(collision.posX(), collision.posY(), collision.posZ())); + cascRegistry.fill(HIST("truerec/hDCAV0Dau"), rec.dcaV0daughters()); + cascRegistry.fill(HIST("truerec/hDCACascDau"), rec.dcacascdaughters()); + cascRegistry.fill(HIST("truerec/hLambdaMass"), rec.mLambda()); + cascRegistry.fill(HIST("truerec/hITSnClustersPos"), rec.template posTrack_as().itsNCls()); + cascRegistry.fill(HIST("truerec/hITSnClustersNeg"), rec.template negTrack_as().itsNCls()); + cascRegistry.fill(HIST("truerec/hITSnClustersBach"), rec.template bachelor_as().itsNCls()); + cascRegistry.fill(HIST("truerec/hTPCnCrossedRowsPos"), rec.template posTrack_as().tpcNClsCrossedRows()); + cascRegistry.fill(HIST("truerec/hTPCnCrossedRowsNeg"), rec.template negTrack_as().tpcNClsCrossedRows()); + cascRegistry.fill(HIST("truerec/hTPCnCrossedRowsBach"), rec.template bachelor_as().tpcNClsCrossedRows()); + cascRegistry.fill(HIST("truerec/hITSChi2Pos"), rec.template posTrack_as().itsChi2NCl()); + cascRegistry.fill(HIST("truerec/hITSChi2Neg"), rec.template negTrack_as().itsChi2NCl()); + cascRegistry.fill(HIST("truerec/hITSChi2Bach"), rec.template bachelor_as().itsChi2NCl()); + cascRegistry.fill(HIST("truerec/hTPCChi2Pos"), rec.template posTrack_as().tpcChi2NCl()); + cascRegistry.fill(HIST("truerec/hTPCChi2Neg"), rec.template negTrack_as().tpcChi2NCl()); + cascRegistry.fill(HIST("truerec/hTPCChi2Bach"), rec.template bachelor_as().tpcChi2NCl()); + switch (genpdg) { + case lcorr_const::kXiMinusPdg: + cascRegistry.fill(HIST("truerec/hXiMinus"), rec.pt(), rec.yXi()); + break; + case -lcorr_const::kXiMinusPdg: + cascRegistry.fill(HIST("truerec/hXiPlus"), rec.pt(), rec.yXi()); + break; + case lcorr_const::kOmegaMinusPdg: + cascRegistry.fill(HIST("truerec/hOmegaMinus"), rec.pt(), rec.yOmega()); + break; + case -lcorr_const::kOmegaMinusPdg: + cascRegistry.fill(HIST("truerec/hOmegaPlus"), rec.pt(), rec.yOmega()); + break; + default: + break; + } } - fillLambdaRecoTables(collision, V0s, tracks); } - PROCESS_SWITCH(LambdaTableProducer, processMCRecoRun3, "Process for Run3 McReco DATA", false); - - void processMCRecoRun2(soa::Join::iterator const& collision, aod::McCollisions const&, - McV0Tracks const& V0s, TracksMCRun2 const& tracks, aod::McParticles const&) + // Templated on TTracks so the call site picks the bound track + // type (Tracks for data, TracksMC for MC reco). Eliminates the framework's + // multi-track-table index resolution conflict. + template + int processCandidate(TCascade const& casc, TCollision const& collision) { - // check collision - if (!selCollision(collision)) { - return; - } - fillLambdaRecoTables(collision, V0s, tracks); - } + auto bachTrack = casc.template bachelor_as(); + auto posTrack = casc.template posTrack_as(); + auto negTrack = casc.template negTrack_as(); + + cascRegistry.fill(HIST("hV0Radius"), casc.v0radius(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hCascRadius"), casc.cascradius(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hV0CosPA"), casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hCascCosPA"), casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hDCAPosToPV"), casc.dcapostopv(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hDCANegToPV"), casc.dcanegtopv(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hDCABachToPV"), casc.dcabachtopv(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hDCAV0ToPV"), casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ()), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hDCAV0Dau"), casc.dcaV0daughters(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hDCACascDau"), casc.dcacascdaughters(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hLambdaMass"), casc.mLambda(), casc.mXi(), casc.pt()); + + cascRegistry.fill(HIST("hITSnClustersPos"), posTrack.itsNCls(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hITSnClustersNeg"), negTrack.itsNCls(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hITSnClustersBach"), bachTrack.itsNCls(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hTPCnCrossedRowsPos"), posTrack.tpcNClsCrossedRows(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hTPCnCrossedRowsNeg"), negTrack.tpcNClsCrossedRows(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hTPCnCrossedRowsBach"), bachTrack.tpcNClsCrossedRows(), casc.mXi(), casc.pt()); + cascRegistry.fill(HIST("hITSChi2Pos"), posTrack.itsChi2NCl()); + cascRegistry.fill(HIST("hITSChi2Neg"), negTrack.itsChi2NCl()); + cascRegistry.fill(HIST("hITSChi2Bach"), bachTrack.itsChi2NCl()); + cascRegistry.fill(HIST("hTPCChi2Pos"), posTrack.tpcChi2NCl()); + cascRegistry.fill(HIST("hTPCChi2Neg"), negTrack.tpcChi2NCl()); + cascRegistry.fill(HIST("hTPCChi2Bach"), bachTrack.tpcChi2NCl()); + + cascRegistry.fill(HIST("hSelectionStatus"), 0); + + // Cascade pT lower cap — drop very-low-pT cascades early. + if (casc.pt() < cascCfg.cMinCascPt) + return 0; - PROCESS_SWITCH(LambdaTableProducer, processMCRecoRun2, "Process for Run2 McReco DATA", false); + // Split V0-daughter and bachelor-track quality so the + // bachelor can be tightened independently. Default values match the + // V0-daughter values, so existing behaviour is preserved. + if (posTrack.tpcNClsCrossedRows() < cascCfg.minTPCCrossedRows || + negTrack.tpcNClsCrossedRows() < cascCfg.minTPCCrossedRows) + return 0; + if (bachTrack.tpcNClsCrossedRows() < cascCfg.minBachTPCCrossedRows) + return 0; + cascRegistry.fill(HIST("hSelectionStatus"), 1); - void processMCRun3(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - McV0Tracks const& V0s, TracksMC const& tracks, - aod::McParticles const& mcParticles) - { - analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); - } + if (posTrack.itsNCls() < cascCfg.minITSClusters || negTrack.itsNCls() < cascCfg.minITSClusters) + return 0; + if (bachTrack.itsNCls() < cascCfg.minBachITSClusters) + return 0; + cascRegistry.fill(HIST("hSelectionStatus"), 2); - PROCESS_SWITCH(LambdaTableProducer, processMCRun3, "Process for Run3 MC RecoGen", false); + if (posTrack.itsChi2NCl() > cascCfg.itsChi2 || negTrack.itsChi2NCl() > cascCfg.itsChi2) + return 0; + if (bachTrack.itsChi2NCl() > cascCfg.maxBachItsChi2) + return 0; + if (posTrack.tpcChi2NCl() > cascCfg.tpcChi2 || negTrack.tpcChi2NCl() > cascCfg.tpcChi2) + return 0; + if (bachTrack.tpcChi2NCl() > cascCfg.maxBachTpcChi2) + return 0; + cascRegistry.fill(HIST("hSelectionStatus"), 3); - void processMCRun2(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - McV0Tracks const& V0s, TracksMCRun2 const& tracks, - aod::McParticles const& mcParticles) - { - analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); - } + double pvx = collision.posX(); + double pvy = collision.posY(); + double pvz = collision.posZ(); + if (casc.v0radius() < cascCfg.v0setting_radius || + casc.cascradius() < cascCfg.cascadesetting_cascradius || + casc.v0cosPA(pvx, pvy, pvz) < cascCfg.v0setting_cospa || + casc.casccosPA(pvx, pvy, pvz) < cascCfg.cascadesetting_cospa || + casc.dcav0topv(pvx, pvy, pvz) < cascCfg.cascadesetting_mindcav0topv || + std::abs(casc.mLambda() - MassLambda0) > cascCfg.cascadesetting_v0masswindow) + return 0; + cascRegistry.fill(HIST("hSelectionStatus"), 4); + + // Cascade-topology consistency: a real Ξ⁻/Ω⁻ decays first + // (cascRadius), then its Λ-daughter flies further out and decays + // (v0Radius). Reject candidates where this ordering is violated — + // those are typically primary Λ paired with a stray bachelor whose + // joint fit landed at a "cascade vertex" downstream of the V0 + // vertex (geometrically inconsistent with Ξ→Λπ kinematics). + if (primCfg.cReqCascRadiusLessThanV0Radius && casc.cascradius() >= casc.v0radius()) + return 0; - PROCESS_SWITCH(LambdaTableProducer, processMCRun2, "Process for Run2 MC RecoGen", false); + if (std::abs(posTrack.eta()) > cascCfg.etaTracks || std::abs(negTrack.eta()) > cascCfg.etaTracks || std::abs(bachTrack.eta()) > cascCfg.etaTracks) + return 0; + cascRegistry.fill(HIST("hSelectionStatus"), 5); - // Gen-only: fills LambdaMcGenCollisions + LambdaMcGenTracks without touching reco tables. - // Use alongside processMCRecoRun3 (which fills reco tables only). - void processMCGenOnlyRun3(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, + if (std::abs(casc.eta()) > cascCfg.etaCascades) + return 0; + cascRegistry.fill(HIST("hSelectionStatus"), 6); + + if (casc.sign() < 0) { + if (std::abs(posTrack.tpcNSigmaPr()) > cascCfg.tpcNsigmaProton) + return 0; + if (std::abs(negTrack.tpcNSigmaPi()) > cascCfg.tpcNsigmaPion) + return 0; + } else { + if (std::abs(negTrack.tpcNSigmaPr()) > cascCfg.tpcNsigmaProton) + return 0; + if (std::abs(posTrack.tpcNSigmaPi()) > cascCfg.tpcNsigmaPion) + return 0; + } + cascRegistry.fill(HIST("hSelectionStatus"), 7); + + int flag = 0; + if (std::abs(bachTrack.tpcNSigmaPi()) < cascCfg.tpcNsigmaBachelor) + flag = 1; + if (std::abs(bachTrack.tpcNSigmaKa()) < cascCfg.tpcNsigmaBachelor && (!cascCfg.doCompetingMassCut || std::abs(o2::constants::physics::MassXiMinus - casc.mXi()) > cascCfg.competingMassWindow)) + flag = 3 - flag; + + switch (flag) { + case 1: + cascRegistry.fill(HIST("hSelectionStatus"), 8); + if (casc.sign() < 0) { + cascRegistry.fill(HIST("hMassXiMinus"), casc.mXi(), casc.pt(), casc.yXi()); + } else { + cascRegistry.fill(HIST("hMassXiPlus"), casc.mXi(), casc.pt(), casc.yXi()); + } + break; + case 2: + cascRegistry.fill(HIST("hSelectionStatus"), 8); + if (casc.sign() < 0) { + cascRegistry.fill(HIST("hMassXiMinus"), casc.mXi(), casc.pt(), casc.yXi()); + cascRegistry.fill(HIST("hMassOmegaMinus"), casc.mOmega(), casc.pt(), casc.yOmega()); + } else { + cascRegistry.fill(HIST("hMassXiPlus"), casc.mXi(), casc.pt(), casc.yXi()); + cascRegistry.fill(HIST("hMassOmegaPlus"), casc.mOmega(), casc.pt(), casc.yOmega()); + } + break; + case 3: + cascRegistry.fill(HIST("hSelectionStatus"), 8); + if (casc.sign() < 0) { + cascRegistry.fill(HIST("hMassOmegaMinus"), casc.mOmega(), casc.pt(), casc.yOmega()); + } else { + cascRegistry.fill(HIST("hMassOmegaPlus"), casc.mOmega(), casc.pt(), casc.yOmega()); + } + break; + default: + break; + } + + return flag; + } + + // Compute the per-cascade cut bitmask UNCONDITIONALLY. Mirrors + // every stage of processCandidate but doesn't short-circuit, so the bits + // capture the cut state of EVERY cascade (even rejected ones). Cheap: + // ~15 comparisons per cascade. + template + uint32_t computeCascadeCutBits(TCascade const& casc, TCollision const& collision) + { + uint32_t bits = 0; + auto bachTrack = casc.template bachelor_as(); + auto posTrack = casc.template posTrack_as(); + auto negTrack = casc.template negTrack_as(); + + if (posTrack.tpcNClsCrossedRows() >= cascCfg.minTPCCrossedRows && + negTrack.tpcNClsCrossedRows() >= cascCfg.minTPCCrossedRows) + bits |= (1u << kCascCutTpcRowsV0Dau); + if (bachTrack.tpcNClsCrossedRows() >= cascCfg.minBachTPCCrossedRows) + bits |= (1u << kCascCutTpcRowsBach); + + if (posTrack.itsNCls() >= cascCfg.minITSClusters && + negTrack.itsNCls() >= cascCfg.minITSClusters) + bits |= (1u << kCascCutItsClsV0Dau); + if (bachTrack.itsNCls() >= cascCfg.minBachITSClusters) + bits |= (1u << kCascCutItsClsBach); + + if (posTrack.itsChi2NCl() <= cascCfg.itsChi2 && + negTrack.itsChi2NCl() <= cascCfg.itsChi2 && + bachTrack.itsChi2NCl() <= cascCfg.maxBachItsChi2) + bits |= (1u << kCascCutItsChi2); + + if (posTrack.tpcChi2NCl() <= cascCfg.tpcChi2 && + negTrack.tpcChi2NCl() <= cascCfg.tpcChi2 && + bachTrack.tpcChi2NCl() <= cascCfg.maxBachTpcChi2) + bits |= (1u << kCascCutTpcChi2); + + if (casc.pt() >= cascCfg.cMinCascPt) + bits |= (1u << kCascCutCascPt); + + double pvx = collision.posX(), pvy = collision.posY(), pvz = collision.posZ(); + bool topoOk = (casc.v0radius() >= cascCfg.v0setting_radius && + casc.cascradius() >= cascCfg.cascadesetting_cascradius && + casc.v0cosPA(pvx, pvy, pvz) >= cascCfg.v0setting_cospa && + casc.casccosPA(pvx, pvy, pvz) >= cascCfg.cascadesetting_cospa && + casc.dcav0topv(pvx, pvy, pvz) >= cascCfg.cascadesetting_mindcav0topv && + std::abs(casc.mLambda() - MassLambda0) <= cascCfg.cascadesetting_v0masswindow); + if (topoOk) + bits |= (1u << kCascCutTopology); + + if (casc.cascradius() < casc.v0radius()) + bits |= (1u << kCascCutRadiusOrder); + + if (std::abs(posTrack.eta()) <= cascCfg.etaTracks && + std::abs(negTrack.eta()) <= cascCfg.etaTracks && + std::abs(bachTrack.eta()) <= cascCfg.etaTracks) + bits |= (1u << kCascCutTrackEta); + + if (std::abs(casc.eta()) <= cascCfg.etaCascades) + bits |= (1u << kCascCutCascEta); + + if (casc.sign() < 0) { + if (std::abs(posTrack.tpcNSigmaPr()) <= cascCfg.tpcNsigmaProton) + bits |= (1u << kCascCutTpcNSigPr); + if (std::abs(negTrack.tpcNSigmaPi()) <= cascCfg.tpcNsigmaPion) + bits |= (1u << kCascCutTpcNSigPi); + } else { + if (std::abs(negTrack.tpcNSigmaPr()) <= cascCfg.tpcNsigmaProton) + bits |= (1u << kCascCutTpcNSigPr); + if (std::abs(posTrack.tpcNSigmaPi()) <= cascCfg.tpcNsigmaPion) + bits |= (1u << kCascCutTpcNSigPi); + } + + if (std::abs(bachTrack.tpcNSigmaPi()) < cascCfg.tpcNsigmaBachelor) + bits |= (1u << kCascCutBachPidXi); + if (std::abs(bachTrack.tpcNSigmaKa()) < cascCfg.tpcNsigmaBachelor) + bits |= (1u << kCascCutBachPidOm); + + if (!cascCfg.doCompetingMassCut || + std::abs(o2::constants::physics::MassXiMinus - casc.mXi()) > cascCfg.competingMassWindow) + bits |= (1u << kCascCutCompetingMass); + + return bits; + } + + // Helper for the per-collision cascade-flag loop. Emits one row + // into cascflags per cascade in this collision (preserving the joinability + // invariant with aod::CascDataExt). When eventOk=false, all flags are 0. + // When eventOk=true and applyMcMatch=true (MC reco path), additionally + // calls fillMatchedHistos to populate truerec/* histograms. + // TTracks added so the call site forwards the bound + // track type to processCandidate / fillMatchedHistos. + template + void cascadeFlagLoop(TCascades const& Cascades, TCollision const& collision, bool eventOk, + std::unordered_set const& itsTrackedCascIds) + { + cascRegistry.fill(HIST("hEventSel"), eventOk ? 1 : 2); + for (auto const& casc : Cascades) { + cascRegistry.fill(HIST("hEventSel"), 0); + if (!eventOk) { + cascflags(0, false, false, 0u); + continue; + } + int flag = processCandidate(casc, collision); + // MC-truth purity flag. + bool isTrueCasc = false; + if constexpr (ApplyMcMatch) { + if (flag != 0 && casc.has_mcParticle()) { + auto gen = casc.mcParticle(); + int absPdg = std::abs(gen.pdgCode()); + if ((absPdg == lcorr_const::kXiMinusPdg || absPdg == lcorr_const::kOmegaMinusPdg) && + gen.isPhysicalPrimary()) { + isTrueCasc = true; + } + } + } + // ITS-tracking flag. + bool isItsTracked = (itsTrackedCascIds.find(casc.cascadeId()) != itsTrackedCascIds.end()); + + // Per-cascade cut bitmask. Computed unconditionally — if + // the cascade was rejected by processCandidate (flag==0) we still + // record which stages it passed/failed. Bits 0-15 from + // computeCascadeCutBits; bits 16-17 set here from the truth / + // ITS-tracking lookups above. + uint32_t cascCutBits = computeCascadeCutBits(casc, collision); + if (isTrueCasc) + cascCutBits |= (1u << kCascCutMcTrueXiOmega); + if (isItsTracked) + cascCutBits |= (1u << kCascCutItsTracked); + + cascflags(flag, isTrueCasc, isItsTracked, cascCutBits); + if constexpr (ApplyMcMatch) { + fillMatchedHistos(casc, flag, collision); + } + } + } + + using CollisionsRun3 = soa::Join; + using CollisionsRun2 = soa::Join; + // aod::pidTPCKa added so the merged process function can + // bind ONE track type and have it serve both V0 daughter cuts (Pi/Pr) and + // cascade bachelor cuts (Ka). Without this, the merged process function + // had to declare two track types and the framework's "last one wins" rule + // mis-routed V0 index resolution to the wrong table. + using Tracks = soa::Join; + using TracksRun2 = soa::Join; + using TracksMC = soa::Join; + using TracksMCRun2 = soa::Join; + using McV0Tracks = soa::Join; + + // processDataRun3 ABSORBS the former CascadeSelector::processRecData. + // Single event-selection call drives both the V0-side LambdaCollections row + // emission (inside fillLambdaRecoTables) and the Cascade-side flag emission + // (inside cascadeFlagLoop). Impossible for the two halves to disagree. + // Drop the explicit FullTracksExtIUWithPID arg: with one + // bound track type (Tracks) the framework no longer mis-resolves V0 vs + // cascade index targets. Cascade helpers see the same Tracks join via + // the TTracks template parameter. + void processDataRun3(CollisionsRun3::iterator const& collision, + aod::V0Datas const& V0s, Tracks const& tracks, + aod::CascDataExt const& Cascades, + aod::BCsWithTimestamps const&) + { + bool eventOk = selCollision(collision); + // Empty ITS-tracked set — variant without strangeness tracking. + // Use processDataRun3WithItsTracking when the AOD actually contains + // O2tracasccoll (aod::AssignedTrackedCascades). Many derived AODs drop it. + static const std::unordered_set emptySet; + cascadeFlagLoop(Cascades, collision, eventOk, emptySet); + if (!eventOk) { + return; // Lambda-side: don't emit a LambdaCollections row. + } + fillLambdaRecoTables(collision, V0s, tracks); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processDataRun3, "Process for Run3 DATA (Lambda + Cascade, no ITS strangeness tracking)", true); + + // ITS-tracking-aware data variant. Same as processDataRun3 but + // also consumes aod::AssignedTrackedCascades; turn this on (and turn + // processDataRun3 off) only when the AOD actually contains the + // O2tracasccoll tree. Otherwise the AOD reader will fail at startup + // with "Couldn't get TTree O2tracasccoll". + void processDataRun3WithItsTracking(CollisionsRun3::iterator const& collision, + aod::V0Datas const& V0s, Tracks const& tracks, + aod::CascDataExt const& Cascades, + aod::AssignedTrackedCascades const& trackedCascades, + aod::BCsWithTimestamps const&) + { + bool eventOk = selCollision(collision); + std::unordered_set itsTrackedCascIds; + for (auto const& tc : trackedCascades) + itsTrackedCascIds.insert(tc.cascadeId()); + cascadeFlagLoop(Cascades, collision, eventOk, itsTrackedCascIds); + if (!eventOk) { + return; + } + fillLambdaRecoTables(collision, V0s, tracks); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processDataRun3WithItsTracking, "Run3 DATA + ITS strangeness tracking (requires O2tracasccoll in AOD)", false); + + void processDataRun2(CollisionsRun2::iterator const& collision, aod::V0Datas const& V0s, TracksRun2 const& tracks) + { + fillLambdaRecoTables(collision, V0s, tracks); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processDataRun2, "Process for Run2 DATA (Lambda only — no cascade in Run2)", false); + + // processMCRecoRun3 ABSORBS the former CascadeSelector::processRecMC. + // Same pattern as processDataRun3 plus truth-matching on the cascade side + // (fillMatchedHistos populates truerec/* histograms). + void processMCRecoRun3(soa::Join::iterator const& collision, aod::McCollisions const&, + McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const&, + LabeledCascades const& Cascades, + aod::BCsWithTimestamps const&) + { + bool eventOk = selCollision(collision); + // No ITS-tracking variant. Toggle to processMCRecoRun3WithItsTracking + // when the AOD has O2tracasccoll. + static const std::unordered_set emptySet; + cascadeFlagLoop(Cascades, collision, eventOk, emptySet); + if (!eventOk) { + return; + } + fillLambdaRecoTables(collision, V0s, tracks); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processMCRecoRun3, "Run3 MC reco (Lambda + Cascade with truth match, no ITS strangeness tracking)", false); + + // ITS-tracking-aware MC reco variant. + void processMCRecoRun3WithItsTracking(soa::Join::iterator const& collision, aod::McCollisions const&, + McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const&, + LabeledCascades const& Cascades, + aod::AssignedTrackedCascades const& trackedCascades, + aod::BCsWithTimestamps const&) + { + bool eventOk = selCollision(collision); + std::unordered_set itsTrackedCascIds; + for (auto const& tc : trackedCascades) + itsTrackedCascIds.insert(tc.cascadeId()); + cascadeFlagLoop(Cascades, collision, eventOk, itsTrackedCascIds); + if (!eventOk) { + return; + } + fillLambdaRecoTables(collision, V0s, tracks); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processMCRecoRun3WithItsTracking, "Run3 MC reco + ITS strangeness tracking (requires O2tracasccoll in AOD)", false); + + void processMCRecoRun2(soa::Join::iterator const& collision, aod::McCollisions const&, + McV0Tracks const& V0s, TracksMCRun2 const& tracks, aod::McParticles const&) + { + // check collision + if (!selCollision(collision)) { + return; + } + fillLambdaRecoTables(collision, V0s, tracks); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processMCRecoRun2, "Process for Run2 MC reco (Lambda only)", false); + + void processMCRun3(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + McV0Tracks const& V0s, TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processMCRun3, "Process for Run3 MC RecoGen", false); + + void processMCRun2(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + McV0Tracks const& V0s, TracksMCRun2 const& tracks, + aod::McParticles const& mcParticles) + { + analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); + } + + PROCESS_SWITCH(LambdaCascadeProducer, processMCRun2, "Process for Run2 MC RecoGen", false); + + // Gen-only: fills LambdaMcGenCollisions + LambdaMcGenTracks without touching reco tables. + // Use alongside processMCRecoRun3 (which fills reco tables only). + void processMCGenOnlyRun3(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) { // Try to obtain centrality from a matched reco collision @@ -1373,7 +2672,96 @@ struct LambdaTableProducer { fillLambdaMcGenTables(mcCollision, mcParticles); } - PROCESS_SWITCH(LambdaTableProducer, processMCGenOnlyRun3, "Gen-only Run3 (no reco tables)", false); + PROCESS_SWITCH(LambdaCascadeProducer, processMCGenOnlyRun3, "Gen-only Run3 (no reco tables)", false); + + // =========================================================================== + // Cascade MC truth-only path (former CascadeSelector::processGenMC): + // self-contained MC-truth event selection on McCollisions/McParticles, + // used as a closure/efficiency check. + // + // DO NOT add `private:` here: a non-public data member breaks the + // struct's aggregate-ness and StructToTuple dispatch mis-routes. + // =========================================================================== + + // Use CollisionsRun3 (which carries CentFT0Ms etc.) instead of + // the old CSEL MyCollisions Join — selCollision needs centFT0M(). + void processCascadeGenMC(aod::McCollision const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& mcParticles) + { + // MC-truth event-level selection (mirror of CSEL::processGenMC). + if (cascCfg.INEL >= 0 && !pwglf::isINELgtNmc(mcParticles, cascCfg.INEL, pdgDB)) + return; + if (std::abs(mcCollision.posZ()) > cascCfg.maxVertexZ) + return; + + cascRegistry.fill(HIST("gen/hNevents"), 0); + + for (auto const& mcPart : mcParticles) { + if (!mcPart.isPhysicalPrimary()) + continue; + if (std::abs(mcPart.eta()) > cascCfg.etaCascades) + continue; + switch (mcPart.pdgCode()) { + case lcorr_const::kXiMinusPdg: + cascRegistry.fill(HIST("gen/hXiMinus"), mcPart.pt(), mcPart.y()); + break; + case -lcorr_const::kXiMinusPdg: + cascRegistry.fill(HIST("gen/hXiPlus"), mcPart.pt(), mcPart.y()); + break; + case lcorr_const::kOmegaMinusPdg: + cascRegistry.fill(HIST("gen/hOmegaMinus"), mcPart.pt(), mcPart.y()); + break; + case -lcorr_const::kOmegaMinusPdg: + cascRegistry.fill(HIST("gen/hOmegaPlus"), mcPart.pt(), mcPart.y()); + break; + default: + break; + } + } + + // Now check whether at least one matched reco collision passes the + // SAME event selection used for the data path (selCollision via the + // shared lcorr_evsel framework). This guarantees the gen-with-rec + // sample uses the same definition as the data path. + if (collisions.size() < 1) { + return; + } + bool evSel = false; + for (auto const& collision : collisions) { + if (selCollision(collision)) { + evSel = true; + break; + } + } + if (!evSel) + return; + + cascRegistry.fill(HIST("genwithrec/hNevents"), 0); + for (auto const& mcPart : mcParticles) { + if (!mcPart.isPhysicalPrimary()) + continue; + if (std::abs(mcPart.eta()) > cascCfg.etaCascades) + continue; + switch (mcPart.pdgCode()) { + case lcorr_const::kXiMinusPdg: + cascRegistry.fill(HIST("genwithrec/hXiMinus"), mcPart.pt(), mcPart.y()); + break; + case -lcorr_const::kXiMinusPdg: + cascRegistry.fill(HIST("genwithrec/hXiPlus"), mcPart.pt(), mcPart.y()); + break; + case lcorr_const::kOmegaMinusPdg: + cascRegistry.fill(HIST("genwithrec/hOmegaMinus"), mcPart.pt(), mcPart.y()); + break; + case -lcorr_const::kOmegaMinusPdg: + cascRegistry.fill(HIST("genwithrec/hOmegaPlus"), mcPart.pt(), mcPart.y()); + break; + default: + break; + } + } + } + PROCESS_SWITCH(LambdaCascadeProducer, processCascadeGenMC, "Process Cascade MC gen (truth-only closure)", false); }; struct LambdaTracksExtProducer { @@ -1420,12 +2808,39 @@ struct LambdaTracksExtProducer { histos.add("Reco/h1f_antilambda_dcadau", "DCA_{p#pi} at V0 Decay Vertex", kTH1F, {axisDcaDau}); histos.addClone("Reco/", "SharingDau/"); + + // Flag-preference order is implicit (first matching else-if wins). + // Warn loudly if the user enabled more than one strategy at once. + int nStrategies = (cAcceptAllLambda ? 1 : 0) + (cRejAllLambdaShaDau ? 1 : 0) + + (cSelLambdaMassPdg ? 1 : 0) + (cSelLambdaTScore ? 1 : 0); + if (nStrategies == 0) { + LOGF(warning, + "[LambdaTracksExtProducer] No true-Lambda selection strategy enabled. " + "trueLambdaFlag will always be false and no Lambdas will pass the " + "downstream goodLambda partition."); + } else if (nStrategies > 1) { + LOGF(warning, + "[LambdaTracksExtProducer] %d true-Lambda strategies enabled at once " + "(cAcceptAllLambda=%d cRejAllLambdaShaDau=%d cSelLambdaMassPdg=%d " + "cSelLambdaTScore=%d). Selection uses else-if precedence in this order; " + "later strategies will be ignored. Pick exactly one.", + nStrategies, + static_cast(cAcceptAllLambda), static_cast(cRejAllLambdaShaDau), + static_cast(cSelLambdaMassPdg), static_cast(cSelLambdaTScore)); + } else { + LOGF(info, + "[LambdaTracksExtProducer] true-Lambda strategy: %s", + cAcceptAllLambda ? "cAcceptAllLambda" + : cRejAllLambdaShaDau ? "cRejAllLambdaShaDau" + : cSelLambdaMassPdg ? "cSelLambdaMassPdg" + : "cSelLambdaTScore"); + } } template void fillHistos(T const& track) { - static constexpr std::string_view SubDir[] = {"Reco/", "SharingDau/"}; + static constexpr std::array SubDir = {"Reco/", "SharingDau/"}; if (track.v0Type() == kLambda) { histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_invmass"), track.mass()); @@ -1468,13 +2883,21 @@ struct LambdaTracksExtProducer { vSharedDauLambdaIndex.push_back(track.index()); lambdaSharingDauFlag = true; - // Fill DEta-DPhi Histogram - if ((lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) || (lambda.v0Type() == kAntiLambda && track.v0Type() == kLambda)) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } else if (lambda.v0Type() == kLambda && track.v0Type() == kLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + // Fill the per-pair sharing-daughter Δη-Δφ histos + // ONCE per unordered pair. The outer/inner loops iterate every + // ordered pair, so without this guard each unique (A,B) shared + // pair would fill twice (with opposite Δη/Δφ sign on the second + // pass). Keeping only track.index() > lambda.index() picks the + // canonical ordering. + if (track.index() > lambda.index()) { + // Fill DEta-DPhi Histogram + if ((lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) || (lambda.v0Type() == kAntiLambda && track.v0Type() == kLambda)) { + histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kLambda && track.v0Type() == kLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } } // decision based on mass closest to PdgMass of Lambda @@ -1497,15 +2920,11 @@ struct LambdaTracksExtProducer { fillHistos(lambda); } - if (cAcceptAllLambda) { // Accept all lambda - trueLambdaFlag = true; - } else if (cRejAllLambdaShaDau && !lambdaSharingDauFlag) { // Reject all lambda sharing daughter - trueLambdaFlag = true; - } else if (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) { // Select lambda closest to pdg mass - trueLambdaFlag = true; - } else if (cSelLambdaTScore && lambdaMinTScoreFlag) { // Select lambda based on t-score - trueLambdaFlag = true; - } + // Selection policy: accept all / reject sharers / best mass / best t-score. + trueLambdaFlag = cAcceptAllLambda || + (cRejAllLambdaShaDau && !lambdaSharingDauFlag) || + (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) || + (cSelLambdaTScore && lambdaMinTScoreFlag); // Multiplicity of selected lambda if (trueLambdaFlag) { @@ -1539,1409 +2958,479 @@ struct LambdaTracksExtProducer { } }; -struct LambdaR2Correlation { - // Global Configurables - Configurable cNPtBins{"cNPtBins", 34, "N pT Bins"}; - Configurable cMinPt{"cMinPt", 0.8, "pT Min"}; - Configurable cMaxPt{"cMaxPt", 4.2, "pT Max"}; - Configurable cNRapBins{"cNRapBins", 20, "N Rapidity Bins"}; - Configurable cMinRap{"cMinRap", -0.5, "Minimum Rapidity"}; - Configurable cMaxRap{"cMaxRap", 0.5, "Maximum Rapidity"}; - Configurable cNPhiBins{"cNPhiBins", 36, "N Phi Bins"}; - Configurable cAnaSecondaries{"cAnaSecondaries", false, "Analysze Secondaries"}; - Configurable cAnaPairs{"cAnaPairs", false, "Analyze Pairs Flag"}; - Configurable cAnaSecondaryPairs{"cAnaSecondaryPairs", false, "Analyze Secondary Pairs Flag"}; - - // Eta/Rap Analysis - Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Eta/Rap Analysis Flag"}; - - // Centrality Axis - ConfigurableAxis cMultBins{"cMultBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 30.0f, 50.f, 80.0f, 100.f}, "Variable Mult-Bins"}; - - // Histogram Registry. - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - // Initialize global variables - float nrapbins = 0.; - float kminrap = 0.; - float kmaxrap = 0.; - float nphibins = 0.; - float kminphi = 0.; - float kmaxphi = TwoPI; - float rapbinwidth = 0.; - float phibinwidth = 0.; - float q = 0., e = 0., qinv = 0.; - float cent = 0.; - - void init(InitContext const&) - { - // Set Density Histogram Attributes - nrapbins = static_cast(cNRapBins); - kminrap = static_cast(cMinRap); - kmaxrap = static_cast(cMaxRap); - nphibins = static_cast(cNPhiBins); +// ============================================================================= +// Removed dead structs (LambdaR2Correlation, CascadeSelector, +// CascadeCorrelations; ~1370 lines) — see git history to revive. +// ============================================================================= - rapbinwidth = (kmaxrap - kminrap) / nrapbins; - phibinwidth = (kmaxphi - kminphi) / nphibins; +// TTree branch structs + connectors inlined +// back into the .cxx (ALICE submission policy: one source file). Plain POD, +// no O2 framework types — only ROOT TTree is used. +namespace lxicorr +{ +struct CascBranches { + float pt{}, rap{}, mass{}; + int sign{}; + float cascCosPA{}, v0CosPA{}; + float cascRadius{}, v0Radius{}; + float dcaV0Dau{}, dcaCascDau{}; + float dcaV0ToPV{}, dcaPosToPV{}, dcaNegToPV{}, dcaBachToPV{}; + float cent{}, pvZ{}; + // MC truth-matching (filled only in MC mode; default = no match) + int pdgCode{0}; // PDG of matched McParticle (0 = no match = background) + bool isPhysPrim{false}; + // Per-cascade cut bitmask + bachelor PID inputs for offline + // re-cutting. cascCutBits semantics defined by enum CascCutBit below. + unsigned int cascCutBits{0}; + float bachTpcNSigmaPi{0.f}; + float bachTpcNSigmaKa{0.f}; + int bachItsNCls{0}; + int bachTpcNClsCrossedRows{0}; + float mLambdaInside{0.f}; // V0 mass-hypothesis as seen by the cascade fit +}; - int knrapphibins = static_cast(cNRapBins) * static_cast(cNPhiBins); - float kminrapphi = 0.; - float kmaxrapphi = knrapphibins; +// Holds branch data for both Xi and Omega — one raw pointer covers both. +// Gen-level branches — kinematics + truth only, no topology +struct GenBranches { + float pt{}, rap{}, cent{}, pvZ{}; + int pdgCode{}; + bool isPhysPrim{}; +}; - const AxisSpec axisCheck(1, 0, 1, ""); - const AxisSpec axisPosZ(220, -11, 11, "V_{z} (cm)"); - const AxisSpec axisCent(cMultBins, "FT0M (%)"); - const AxisSpec axisChMult(200, 0, 200, "N_{ch}"); - const AxisSpec axisMult(10, 0, 10, "N_{#Lambda}"); - const AxisSpec axisMass(100, 1.06, 1.16, "M_{#Lambda} (GeV/#it{c}^{2})"); - const AxisSpec axisPt(cNPtBins, cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); - const AxisSpec axisEta(cNRapBins, cMinRap, cMaxRap, "#eta"); - const AxisSpec axisRap(cNRapBins, cMinRap, cMaxRap, "y"); - const AxisSpec axisPhi(cNPhiBins, 0., TwoPI, "#varphi (rad)"); - const AxisSpec axisRapPhi(knrapphibins, kminrapphi, kmaxrapphi, "y #varphi"); - const AxisSpec axisQinv(100, 0, 10, "q_{inv} (GeV/#it{c})"); +// Λ TTree branches. Both reco-level (with topo + truth fields) +// and gen-level. The motherPdg field lets the user filter feed-down sources +// post-hoc (e.g., motherPdg==±3312 → Ξ-feeddown). isPhysPrim duplicates the +// primary/secondary tag from lambdatrack::V0PrmScd for convenience. +struct LambdaBranches { + float pt{}, eta{}, rap{}, phi{}, mass{}; + float cosPA{}, dcaDau{}; + int v0Type{}; // 0=Λ, 1=Λ̄ (matches enum ParticleType) + int v0PrmScd{}; // 0=primary, 1=secondary (only meaningful in MC) + bool trueLambdaFlag{}; + float corrFact{}; + float cent{}, pvZ{}; + // MC-only + int pdgCode{0}; + bool isPhysPrim{false}; + int motherPdg{0}; + // Topology snapshot — the inputs of a downstream + // primary-fraction template fit. Per-V0, filled on every row. + float dcaV0ToPV{0.f}; + float v0Radius{0.f}; + int posItsNCls{0}; + int negItsNCls{0}; + bool passesPrimaryTopo{false}; + // Per-daughter 7-bit ITS hit-map for geometric consistency + // checks downstream (vetoes against impossible inner-layer hits when + // the V0 vertex is outside that layer). + unsigned int posItsClusterMap{0}; + unsigned int negItsClusterMap{0}; + // Pseudo-proper decay length and per-daughter DCA-XY. + float lProper{0.f}; + float posDcaXY{0.f}; + float negDcaXY{0.f}; + // Per-V0 cut bitmask + raw cut inputs for offline re-cutting. + unsigned int cutBits{0}; + float tpcNSigmaPosPr{0.f}; // proton hypothesis on positive daughter + float tpcNSigmaNegPi{0.f}; // pion hypothesis on negative daughter + float tpcNSigmaPosPi{0.f}; // pion hypothesis on positive daughter (anti-Λ side) + float tpcNSigmaNegPr{0.f}; // proton hypothesis on negative daughter (anti-Λ side) + float mK0Short{0.f}; + float qtArm{0.f}; + float alphaArm{0.f}; + float cTau{0.f}; +}; - // Create Histograms. - // Event - histos.add("Event/Reco/h1f_collision_posz", "V_{Z} Distribution", kTH1F, {axisPosZ}); - histos.add("Event/Reco/h1f_ft0m_mult_percentile", "FT0M (%)", kTH1F, {axisCent}); - histos.add("Event/Reco/h2f_Mult_vs_Centrality", "N_{ch} vs FT0M(%)", kTH2F, {axisCent, axisChMult}); - histos.add("Event/Reco/h2f_lambda_mult", "#Lambda - Multiplicity", kTH2F, {axisCent, axisMult}); - histos.add("Event/Reco/h2f_antilambda_mult", "#bar{#Lambda} - Multiplicity", kTH2F, {axisCent, axisMult}); +struct LambdaGenBranches { + float pt{}, eta{}, rap{}, phi{}; + int v0Type{}; + int v0PrmScd{}; + float cent{}, pvZ{}; +}; - // Efficiency Histograms - // Single Particle Efficiencies - histos.add("Reco/Primary/Efficiency/h2f_n1_centpt_LaP", "#rho_{1}^{#Lambda}", kTH2F, {axisCent, axisPt}); - histos.add("Reco/Primary/Efficiency/h2f_n1_centpt_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH2F, {axisCent, axisPt}); - histos.add("Reco/Primary/Efficiency/h3f_n1_centpteta_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPt, axisEta}); - histos.add("Reco/Primary/Efficiency/h3f_n1_centpteta_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPt, axisEta}); - histos.add("Reco/Primary/Efficiency/h3f_n1_centptrap_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPt, axisRap}); - histos.add("Reco/Primary/Efficiency/h3f_n1_centptrap_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPt, axisRap}); +// Single pointer covers all six branch sets — 1 StructToTuple slot +struct BranchPair { + CascBranches xi{}; + CascBranches om{}; + GenBranches xiGen{}; + GenBranches omGen{}; + LambdaBranches lam{}; + LambdaGenBranches lamGen{}; +}; - // Single and Two Particle Densities - // 1D Histograms - histos.add("Reco/Primary/h3f_n1_centmasspt_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisMass, axisPt}); - histos.add("Reco/Primary/h3f_n1_centmasspt_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisMass, axisPt}); - histos.add("Reco/Primary/h2f_n1_pt_LaP", "#rho_{1}^{#Lambda}", kTH2F, {axisCent, axisPt}); - histos.add("Reco/Primary/h2f_n1_pt_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH2F, {axisCent, axisPt}); - histos.add("Reco/Primary/h2f_n1_eta_LaP", "#rho_{1}^{#Lambda}", kTH2F, {axisCent, axisEta}); - histos.add("Reco/Primary/h2f_n1_eta_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH2F, {axisCent, axisEta}); - histos.add("Reco/Primary/h2f_n1_rap_LaP", "#rho_{1}^{#Lambda}", kTH2F, {axisCent, axisRap}); - histos.add("Reco/Primary/h2f_n1_rap_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH2F, {axisCent, axisRap}); - histos.add("Reco/Primary/h2f_n1_phi_LaP", "#rho_{1}^{#Lambda}", kTH2F, {axisCent, axisPhi}); - histos.add("Reco/Primary/h2f_n1_phi_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH2F, {axisCent, axisPhi}); +inline void connectBranches(TTree* t, CascBranches* b) +{ + t->Branch("pt", &b->pt); + t->Branch("rap", &b->rap); + t->Branch("mass", &b->mass); + t->Branch("sign", &b->sign); + t->Branch("cascCosPA", &b->cascCosPA); + t->Branch("v0CosPA", &b->v0CosPA); + t->Branch("cascRadius", &b->cascRadius); + t->Branch("v0Radius", &b->v0Radius); + t->Branch("dcaV0Dau", &b->dcaV0Dau); + t->Branch("dcaCascDau", &b->dcaCascDau); + t->Branch("dcaV0ToPV", &b->dcaV0ToPV); + t->Branch("dcaPosToPV", &b->dcaPosToPV); + t->Branch("dcaNegToPV", &b->dcaNegToPV); + t->Branch("dcaBachToPV", &b->dcaBachToPV); + t->Branch("cent", &b->cent); + t->Branch("pvZ", &b->pvZ); + t->Branch("pdgCode", &b->pdgCode); + t->Branch("isPhysPrim", &b->isPhysPrim); + // Per-cascade cut bitmask + bachelor PID + V0-inside mass. + t->Branch("cascCutBits", &b->cascCutBits); + t->Branch("bachTpcNSigmaPi", &b->bachTpcNSigmaPi); + t->Branch("bachTpcNSigmaKa", &b->bachTpcNSigmaKa); + t->Branch("bachItsNCls", &b->bachItsNCls); + t->Branch("bachTpcNClsCrossedRows", &b->bachTpcNClsCrossedRows); + t->Branch("mLambdaInside", &b->mLambdaInside); +} - // rho1 for R2 RapPhi - histos.add("Reco/Primary/h3f_n1_rapphi_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisRap, axisPhi}); - histos.add("Reco/Primary/h3f_n1_rapphi_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisRap, axisPhi}); +inline void connectGenBranches(TTree* t, GenBranches* b) +{ + t->Branch("pt", &b->pt); + t->Branch("rap", &b->rap); + t->Branch("pdgCode", &b->pdgCode); + t->Branch("isPhysPrim", &b->isPhysPrim); + t->Branch("cent", &b->cent); + t->Branch("pvZ", &b->pvZ); +} - // rho1 for Q_{inv} - histos.add("Reco/Primary/h3f_n1_pteta_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPt, axisEta}); - histos.add("Reco/Primary/h3f_n1_pteta_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPt, axisEta}); +// Λ-side connectors. +inline void connectLambdaBranches(TTree* t, LambdaBranches* b) +{ + t->Branch("pt", &b->pt); + t->Branch("eta", &b->eta); + t->Branch("rap", &b->rap); + t->Branch("phi", &b->phi); + t->Branch("mass", &b->mass); + t->Branch("cosPA", &b->cosPA); + t->Branch("dcaDau", &b->dcaDau); + t->Branch("v0Type", &b->v0Type); + t->Branch("v0PrmScd", &b->v0PrmScd); + t->Branch("trueLambdaFlag", &b->trueLambdaFlag); + t->Branch("corrFact", &b->corrFact); + t->Branch("cent", &b->cent); + t->Branch("pvZ", &b->pvZ); + t->Branch("pdgCode", &b->pdgCode); + t->Branch("isPhysPrim", &b->isPhysPrim); + t->Branch("motherPdg", &b->motherPdg); + // Topology snapshot for primary-fraction template fits on data. + t->Branch("dcaV0ToPV", &b->dcaV0ToPV); + t->Branch("v0Radius", &b->v0Radius); + t->Branch("posItsNCls", &b->posItsNCls); + t->Branch("negItsNCls", &b->negItsNCls); + t->Branch("passesPrimaryTopo", &b->passesPrimaryTopo); + // ITS hit-map (7 bits used). Stored as unsigned int for + // ROOT-friendly typing on the read side. + t->Branch("posItsClusterMap", &b->posItsClusterMap); + t->Branch("negItsClusterMap", &b->negItsClusterMap); + // L_proper (cm) and per-daughter signed DCA-XY-to-PV (cm). + t->Branch("lProper", &b->lProper); + t->Branch("posDcaXY", &b->posDcaXY); + t->Branch("negDcaXY", &b->negDcaXY); + // Per-V0 cut bitmask + raw cut inputs. + t->Branch("cutBits", &b->cutBits); + t->Branch("tpcNSigmaPosPr", &b->tpcNSigmaPosPr); + t->Branch("tpcNSigmaNegPi", &b->tpcNSigmaNegPi); + t->Branch("tpcNSigmaPosPi", &b->tpcNSigmaPosPi); + t->Branch("tpcNSigmaNegPr", &b->tpcNSigmaNegPr); + t->Branch("mK0Short", &b->mK0Short); + t->Branch("qtArm", &b->qtArm); + t->Branch("alphaArm", &b->alphaArm); + t->Branch("cTau", &b->cTau); +} - // Clone Singles Primary/Secondary Histogram - if (cAnaSecondaries) { - histos.addClone("Reco/Primary/", "Reco/Secondary/"); - } +inline void connectLambdaGenBranches(TTree* t, LambdaGenBranches* b) +{ + t->Branch("pt", &b->pt); + t->Branch("eta", &b->eta); + t->Branch("rap", &b->rap); + t->Branch("phi", &b->phi); + t->Branch("v0Type", &b->v0Type); + t->Branch("v0PrmScd", &b->v0PrmScd); + t->Branch("cent", &b->cent); + t->Branch("pvZ", &b->pvZ); +} +} // namespace lxicorr - if (cAnaPairs) { - // rho2 for numerator of R2 - histos.add("Reco/PP/h3f_n2_raprap_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH3F, {axisCent, axisRap, axisRap}); - histos.add("Reco/PP/h3f_n2_raprap_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH3F, {axisCent, axisRap, axisRap}); - histos.add("Reco/PP/h3f_n2_raprap_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH3F, {axisCent, axisRap, axisRap}); - histos.add("Reco/PP/h3f_n2_phiphi_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH3F, {axisCent, axisPhi, axisPhi}); - histos.add("Reco/PP/h3f_n2_phiphi_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH3F, {axisCent, axisPhi, axisPhi}); - histos.add("Reco/PP/h3f_n2_phiphi_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH3F, {axisCent, axisPhi, axisPhi}); +struct Lambdacascadecorrelation { - // rho2 for R2 Rap1Phi1Rap2Phi2 - histos.add("Reco/PP/h3f_n2_rapphi_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); - histos.add("Reco/PP/h3f_n2_rapphi_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); - histos.add("Reco/PP/h3f_n2_rapphi_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + // --- Configurables --- + Configurable maxY{"maxY", 0.5, "Max |y| for Lambda, Xi and Omega"}; + Configurable useEff{"useEff", false, "Apply Lambda efficiency correction"}; + // Cascade efficiency weighting (mirror of useEff for Lambdas). + // When true, the pair loops include a cascade-side weight via + // getCascadeEfficiency<>(...). The default helper returns 1.0 — wire + // CCDB loading into it when you have an efficiency map (TODO comment in + // body). + Configurable cUseCascEff{"cUseCascEff", false, "Apply cascade efficiency correction"}; + // Omega bachelor kaon PID cut (kept separate from Xi pion cut for physics correctness) + Configurable tpcNsigmaBachKaon{"tpcNsigmaBachKaon", 3.0f, "TPC NSigma bachelor kaon (Omega selection)"}; + // FT0M centrality bins — same variable-width defaults as LambdaR2Correlation + ConfigurableAxis centAxis{"centAxis", {VARIABLE_WIDTH, 0.0f, 10.0f, 30.0f, 50.0f, 80.0f, 100.0f}, "FT0M centrality (%)"}; + Configurable saveCascTree{"saveCascTree", false, "Save TTree of cascade topological variables into AnalysisResults.root"}; - // rho2 for R2 Qinv - histos.add("Reco/PP/h2f_n2_qinv_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH2F, {axisCent, axisQinv}); - histos.add("Reco/PP/h2f_n2_qinv_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH2F, {axisCent, axisQinv}); - histos.add("Reco/PP/h2f_n2_qinv_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH2F, {axisCent, axisQinv}); + // Use only Λ flagged kPrimary in the standalone Λ list. In MC + // this filters truly-primary Λ via mcParticle.isPhysicalPrimary() (set by + // LambdaCascadeProducer when cSelMCPSV0=true). On data, every Λ is + // unconditionally tagged kPrimary, so this toggle is effectively a no-op + // — combine with a tighter cMaxDcaV0ToPV in lambda-cascade-producer for + // topological enrichment of the primary fraction. + Configurable cUsePrimaryLambdasOnly{"cUsePrimaryLambdasOnly", true, + "Restrict the trigger Λ list to v0PrmScd==kPrimary (MC-meaningful)"}; + + // Save a per-Λ TTree of the standalone V0 candidates. Useful for + // offline cut tuning, primary-fraction template fits, and feed-down + // post-tagging via the motherPdg branch. + Configurable saveLambdaTree{"saveLambdaTree", false, + "Save TTree of standalone-Λ candidates into AnalysisResults.root"}; + + // Auto-correlation veto (replaces bool cVetoSharedDau; legacy key still + // parsed but ignored): + // 0 = off — keep all pairs (systematics only). + // 1 = strict — drop if BOTH daughters match the cascade V0 (default). + // 2 = loose — drop if EITHER daughter matches. + Configurable cVetoMode{"cVetoMode", 1, + "Auto-correlation veto: 0=off, 1=strict (both daughters shared), 2=loose (any daughter)"}; + Configurable cVetoSharedDau{"cVetoSharedDau", true, + "[DEPRECATED] use cVetoMode instead. Value is ignored at runtime."}; + + // Histogram-axis bundle. ConfigurableGroup makes this one + // StructToTuple slot while still exposing every axis as JSON-tunable + // (cDphiAxis.values, etc.). Lets users adjust correlation-function + // resolution without recompiling — change a JSON value, rerun. + struct : ConfigurableGroup { + ConfigurableAxis cDphiAxis{"cDphiAxis", {72, -PIHalf, 3 * PIHalf}, "Δφ"}; + ConfigurableAxis cDyAxis{"cDyAxis", {40, -2.0f, 2.0f}, "Δy"}; + ConfigurableAxis cPtAxisPair{"cPtAxisPair", {100, 0, 10}, "pT axis on single+pair histos"}; + ConfigurableAxis cMassLamAxis{"cMassLamAxis", {100, 1.09, 1.14}, "Λ inv-mass"}; + ConfigurableAxis cMassXiAxis{"cMassXiAxis", {100, 1.28, 1.36}, "Ξ inv-mass"}; + ConfigurableAxis cMassOmAxis{"cMassOmAxis", {100, 1.62, 1.72}, "Ω inv-mass"}; + ConfigurableAxis cRapAxis{"cRapAxis", {100, -1.0f, 1.0f}, "rapidity"}; + // φ axis on single-particle (y, φ) histograms — needed + // to build ρ₁(y, φ) and convolve into ρ₁⊗ρ₁ in (Δy, Δφ). + ConfigurableAxis cPhiAxis{"cPhiAxis", {72, 0.f, TwoPI}, "φ (rad)"}; + } histAxes; - // Clone Pairs Histograms - if (cAnaSecondaryPairs) { - histos.addClone("Reco/PP/", "Reco/PS/"); - histos.addClone("Reco/PP/", "Reco/SP/"); - histos.addClone("Reco/PP/", "Reco/SS/"); - } - } - - // MCGen - if (doprocessMCGen) { - histos.addClone("Event/Reco/", "Event/McGen/"); - histos.addClone("Reco/", "McGen/"); - } - } - - template - void fillPairHistos(U& p1, U& p2) - { - static constexpr std::string_view SubDirRecGen[] = {"Reco/", "McGen/"}; - static constexpr std::string_view SubDirPrmScd[] = {"PP/", "PS/", "SP/", "SS/"}; - static constexpr std::string_view SubDirHist[] = {"LaP_LaM", "LaP_LaP", "LaM_LaM"}; - - float rap1 = (cDoEtaAnalysis) ? p1.eta() : p1.rap(); - float rap2 = (cDoEtaAnalysis) ? p2.eta() : p2.rap(); - - int rapbin1 = static_cast((rap1 - kminrap) / rapbinwidth); - int rapbin2 = static_cast((rap2 - kminrap) / rapbinwidth); - - int phibin1 = static_cast(p1.phi() / phibinwidth); - int phibin2 = static_cast(p2.phi() / phibinwidth); - - float corfac = p1.corrFact() * p2.corrFact(); - - // fill rho2 histograms - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h3f_n2_raprap_") + HIST(SubDirHist[part_pair]), cent, rap1, rap2, corfac); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h3f_n2_phiphi_") + HIST(SubDirHist[part_pair]), cent, p1.phi(), p2.phi(), corfac); - - if (rapbin1 >= 0 && rapbin2 >= 0 && phibin1 >= 0 && phibin2 >= 0 && rapbin1 < nrapbins && rapbin2 < nrapbins && phibin1 < nphibins && phibin2 < nphibins) { - - int rapphix = rapbin1 * nphibins + phibin1; - int rapphiy = rapbin2 * nphibins + phibin2; - - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h3f_n2_rapphi_") + HIST(SubDirHist[part_pair]), cent, rapphix + 0.5, rapphiy + 0.5, corfac); - } - - // qinv histograms - q = RecoDecay::p((p1.px() - p2.px()), (p1.py() - p2.py()), (p1.pz() - p2.pz())); - e = RecoDecay::e(p1.px(), p1.py(), p1.pz(), MassLambda0) - RecoDecay::e(p2.px(), p2.py(), p2.pz(), MassLambda0); - qinv = std::sqrt(-RecoDecay::m2(q, e)); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h2f_n2_qinv_") + HIST(SubDirHist[part_pair]), cent, qinv, corfac); - } - - template - void analyzeSingles(T const& tracks) - { - static constexpr std::string_view SubDirRecGen[] = {"Reco/", "McGen/"}; - static constexpr std::string_view SubDirPrmScd[] = {"Primary/", "Secondary/"}; - static constexpr std::string_view SubDirHist[] = {"LaP", "LaM"}; - - int ntrk = 0; - - for (auto const& track : tracks) { - // count tracks - ++ntrk; - - // Efficiency Plots - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("Efficiency/h2f_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("Efficiency/h3f_n1_centpteta_") + HIST(SubDirHist[part]), cent, track.pt(), track.eta()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("Efficiency/h3f_n1_centptrap_") + HIST(SubDirHist[part]), cent, track.pt(), track.rap()); - - // QA Plots - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h3f_n1_centmasspt_") + HIST(SubDirHist[part]), cent, track.mass(), track.pt()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h2f_n1_pt_") + HIST(SubDirHist[part]), cent, track.pt(), track.corrFact()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h2f_n1_eta_") + HIST(SubDirHist[part]), cent, track.eta(), track.corrFact()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h2f_n1_phi_") + HIST(SubDirHist[part]), cent, track.phi(), track.corrFact()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h2f_n1_rap_") + HIST(SubDirHist[part]), cent, track.rap(), track.corrFact()); - - // Rho1 for N1RapPhi - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h3f_n1_rapphi_") + HIST(SubDirHist[part]), cent, track.rap(), track.phi(), track.corrFact()); - - // Rho1 for Q_{inv} Bkg Estimation - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h3f_n1_pteta_") + HIST(SubDirHist[part]), cent, track.pt(), track.eta(), track.corrFact()); - } - - // fill multiplicity histograms - if (ntrk != 0) { - if (part == kLambda) { - histos.fill(HIST("Event/") + HIST(SubDirRecGen[rec_gen]) + HIST("h2f_lambda_mult"), cent, ntrk); - } else { - histos.fill(HIST("Event/") + HIST(SubDirRecGen[rec_gen]) + HIST("h2f_antilambda_mult"), cent, ntrk); - } - } - } - - template - void analyzePairs(T const& trks_1, T const& trks_2) - { - for (auto const& trk_1 : trks_1) { - for (auto const& trk_2 : trks_2) { - // check for same index for Lambda-Lambda / AntiLambda-AntiLambda - if (samelambda && ((trk_1.index() == trk_2.index()))) { - continue; - } - fillPairHistos(trk_1, trk_2); - } - } - } - - using LambdaCollisions = aod::LambdaCollisions; - using LambdaTracks = soa::Join; - - // using MyCascades = aod::CascDataExt; // ← NOT CascDatas. NEVER CascDatas. - using MyCascades = soa::Filtered; - - SliceCache cache; - Partition partPrimLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partPrimAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partSecdLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); - Partition partSecdAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); - - void processDataReco(LambdaCollisions::iterator const& collision, LambdaTracks const&) - { - histos.fill(HIST("Event/Reco/h1f_collision_posz"), collision.posZ()); - histos.fill(HIST("Event/Reco/h1f_ft0m_mult_percentile"), collision.cent()); - histos.fill(HIST("Event/Reco/h2f_Mult_vs_Centrality"), collision.cent(), collision.mult()); - - cent = collision.cent(); - - auto lambdaPrimTracks = partPrimLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto antiLambdaPrimTracks = partPrimAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto lambdaSecdTracks = partSecdLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto antiLambdaSecdTracks = partSecdAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - - analyzeSingles(lambdaPrimTracks); - analyzeSingles(antiLambdaPrimTracks); - - if (cAnaSecondaries) { - analyzeSingles(lambdaSecdTracks); - analyzeSingles(antiLambdaSecdTracks); - } - - if (cAnaPairs) { - // Primary Pairs Only - analyzePairs(lambdaPrimTracks, antiLambdaPrimTracks); - analyzePairs(lambdaPrimTracks, lambdaPrimTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaPrimTracks); - - // Secondary Pairs - if (cAnaSecondaryPairs) { - analyzePairs(lambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaPrimTracks, lambdaSecdTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, lambdaPrimTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, lambdaSecdTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaSecdTracks); - } - } - } - - PROCESS_SWITCH(LambdaR2Correlation, processDataReco, "Process for Data and MCReco", true); - - using LambdaMcGenCollisions = aod::LambdaMcGenCollisions; - using LambdaMcGenTracks = aod::LambdaMcGenTracks; - - SliceCache cachemc; - Partition partMcPrimLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partMcPrimAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partMcSecdLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); - Partition partMcSecdAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); + // --- Outputs --- + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Direct OutputObj members → framework registers and writes these into + // AnalysisResults.root under the "CascadeTrees" folder automatically. + // setObject() is called in init() when saveCascTree is true. + OutputObj treeXi{"XiCandidates", OutputObjHandlingPolicy::AnalysisObject}; + OutputObj treeOmega{"OmegaCandidates", OutputObjHandlingPolicy::AnalysisObject}; + OutputObj treeXiGen{"XiCandidatesGen", OutputObjHandlingPolicy::AnalysisObject}; + OutputObj treeOmegaGen{"OmegaCandidatesGen", OutputObjHandlingPolicy::AnalysisObject}; + // Λ trees (reco + gen). + OutputObj treeLambda{"LambdaCandidates", OutputObjHandlingPolicy::AnalysisObject}; + OutputObj treeLambdaGen{"LambdaCandidatesGen", OutputObjHandlingPolicy::AnalysisObject}; + // Single raw pointer covers all six branch sets — 1 StructToTuple slot + lxicorr::BranchPair* bp{nullptr}; - void processMCGen(LambdaMcGenCollisions::iterator const& mcgencol, LambdaMcGenTracks const&) + // Last-seen collision index for the Λ-side singles + tree fill. + // analyzeSinglesLambda() is invoked from BOTH processMCRecoXi() and + // processMCRecoOmega() (and the equivalent data process functions). When + // multiple of those flags are on simultaneously — the default for full + // Λ-Ξ + Λ-Ω runs — every Λ would otherwise be filled into Singles/* and + // LambdaCandidates twice (or more). The guard below makes the per-event + // Λ fill idempotent: only the first call per collisionId runs the body, + // subsequent calls from the same event return early. + // -1 marks "no event seen yet" (legal collisionId is >= 0). + int64_t mLastLamSinglesCollIdx{-1}; + + // Idempotence guards for Event/hEventCount. + // Previously a single mLastEventCountCollIdx — but reco process functions + // pass LambdaCollisions::globalIndex() and gen ones pass + // LambdaMcGenCollisions::globalIndex(), which live in different ID spaces. + // A single guard double-counted events when both reco and gen process + // functions were active. Split into two so each "side" of the workflow + // counts unique events independently. Reco counter is the canonical one + // and is the one filled into the histogram. + int64_t mLastEventCountRecoCollIdx{-1}; + int64_t mLastEventCountGenCollIdx{-1}; + + // Helper: fill the per-event counter exactly once per reco collision. + // Reco-side guard. The histogram itself is filled here so + // hEventCount reports unique reco collisions (the canonical event count + // used everywhere downstream). + void fillEventCountOnce(int64_t collIdx) { - histos.fill(HIST("Event/McGen/h1f_collision_posz"), mcgencol.posZ()); - histos.fill(HIST("Event/McGen/h1f_ft0m_mult_percentile"), mcgencol.cent()); - histos.fill(HIST("Event/McGen/h2f_Mult_vs_Centrality"), mcgencol.cent(), mcgencol.mult()); - - cent = mcgencol.cent(); - - auto lambdaPrimTracks = partMcPrimLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); - auto antiLambdaPrimTracks = partMcPrimAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); - auto lambdaSecdTracks = partMcSecdLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); - auto antiLambdaSecdTracks = partMcSecdAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); - - analyzeSingles(lambdaPrimTracks); - analyzeSingles(antiLambdaPrimTracks); - - if (cAnaSecondaries) { - analyzeSingles(lambdaSecdTracks); - analyzeSingles(antiLambdaSecdTracks); - } - - if (cAnaPairs) { - // Primary Pairs Only - analyzePairs(lambdaPrimTracks, antiLambdaPrimTracks); - analyzePairs(lambdaPrimTracks, lambdaPrimTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaPrimTracks); - - // Secondary Pairs - if (cAnaSecondaryPairs) { - analyzePairs(lambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaPrimTracks, lambdaSecdTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, lambdaPrimTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, lambdaSecdTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaSecdTracks); - } + if (collIdx == mLastEventCountRecoCollIdx) { + return; } + mLastEventCountRecoCollIdx = collIdx; + histos.fill(HIST("Event/hEventCount"), 0.5); } - PROCESS_SWITCH(LambdaR2Correlation, processMCGen, "Process for MC Generated", false); -}; - -struct CascadeSelector { - - Service ccdb; - Service pdgDB; - - Produces cascflags; - - // Configurables - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; - Configurable useTrigger{"useTrigger", false, "Use trigger selection on skimmed data"}; - Configurable triggerList{"triggerList", "fDoubleXi, fDoubleOmega, fOmegaXi", "List of triggers used to select events"}; - Configurable doTFBorderCut{"doTFBorderCut", true, "Switch to apply TimeframeBorderCut event selection"}; - Configurable doSel8{"doSel8", true, "Switch to apply sel8 event selection"}; - Configurable doNoSameBunchPileUp{"doNoSameBunchPileUp", true, "Switch to apply NoSameBunchPileUp event selection"}; - Configurable INEL{"INEL", 0, "Number of charged tracks within |eta| < 1 has to be greater than value"}; - Configurable maxVertexZ{"maxVertexZ", 10., "Maximum value of z coordinate of PV"}; - Configurable etaCascades{"etaCascades", 0.8, "min/max of eta for cascades"}; - Configurable doCompetingMassCut{"doCompetingMassCut", true, "Switch to apply a competing mass cut for the Omega's"}; - Configurable competingMassWindow{"competingMassWindow", 0.01, "Mass window for the competing mass cut"}; - - // Tracklevel - Configurable tpcNsigmaBachelor{"tpcNsigmaBachelor", 3, "TPC NSigma bachelor"}; - Configurable tpcNsigmaProton{"tpcNsigmaProton", 3, "TPC NSigma proton <- lambda"}; - Configurable tpcNsigmaPion{"tpcNsigmaPion", 3, "TPC NSigma pion <- lambda"}; - Configurable minTPCCrossedRows{"minTPCCrossedRows", 80, "min N TPC crossed rows"}; // TODO: finetune! 80 > 159/2, so no split tracks? - Configurable minITSClusters{"minITSClusters", 4, "minimum number of ITS clusters"}; - Configurable etaTracks{"etaTracks", 1.0, "min/max of eta for tracks"}; - Configurable tpcChi2{"tpcChi2", 4, "TPC Chi2"}; - Configurable itsChi2{"itsChi2", 36, "ITS Chi2"}; - - // Selection criteria - compatible with core wagon autodetect - copied from cascadeanalysis.cxx - //*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* - Configurable v0setting_cospa{"v0setting_cospa", 0.995, "v0setting_cospa"}; - Configurable v0setting_dcav0dau{"v0setting_dcav0dau", 1.0, "v0setting_dcav0dau"}; - Configurable v0setting_dcapostopv{"v0setting_dcapostopv", 0.1, "v0setting_dcapostopv"}; - Configurable v0setting_dcanegtopv{"v0setting_dcanegtopv", 0.1, "v0setting_dcanegtopv"}; - Configurable v0setting_radius{"v0setting_radius", 0.9, "v0setting_radius"}; - Configurable cascadesetting_cospa{"cascadesetting_cospa", 0.95, "cascadesetting_cospa"}; - Configurable cascadesetting_dcacascdau{"cascadesetting_dcacascdau", 1.0, "cascadesetting_dcacascdau"}; - Configurable cascadesetting_dcabachtopv{"cascadesetting_dcabachtopv", 0.05, "cascadesetting_dcabachtopv"}; - Configurable cascadesetting_cascradius{"cascadesetting_cascradius", 0.9, "cascadesetting_cascradius"}; - Configurable cascadesetting_v0masswindow{"cascadesetting_v0masswindow", 0.01, "cascadesetting_v0masswindow"}; - Configurable cascadesetting_mindcav0topv{"cascadesetting_mindcav0topv", 0.01, "cascadesetting_mindcav0topv"}; - //*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* - - // TODO: variables as function of Omega mass, only do Xi for now - ConfigurableAxis radiusAxis = {"radiusAxis", {100, 0.0f, 50.0f}, "cm"}; - ConfigurableAxis cpaAxis = {"cpaAxis", {100, 0.95f, 1.0f}, "CPA"}; - ConfigurableAxis vertexAxis = {"vertexAxis", {100, -10.0f, 10.0f}, "cm"}; - ConfigurableAxis dcaAxis = {"dcaAxis", {100, 0.0f, 2.0f}, "cm"}; - ConfigurableAxis invXiMassAxis = {"invXiMassAxis", {100, 1.28f, 1.38f}, "Inv. Mass (GeV/c^{2})"}; - ConfigurableAxis invOmegaMassAxis = {"invOmegaMassAxis", {100, 1.62f, 1.72f}, "Inv. Mass (GeV/c^{2})"}; - ConfigurableAxis ptAxis = {"ptAxis", {150, 0, 15}, "#it{p}_{T}"}; - ConfigurableAxis rapidityAxis{"rapidityAxis", {100, -1.f, 1.f}, "y"}; - ConfigurableAxis invLambdaMassAxis{"invLambdaMassAxis", {100, 1.07f, 1.17f}, "Inv. Mass (GeV/c^{2})"}; - AxisSpec itsClustersAxis{8, -0.5, 7.5, "number of ITS clusters"}; - AxisSpec tpcRowsAxis{160, -0.5, 159.5, "TPC crossed rows"}; - HistogramRegistry registry{ - "registry", - { - // basic selection variables - {"hV0Radius", "hV0Radius", {HistType::kTH3F, {radiusAxis, invXiMassAxis, ptAxis}}}, - {"hCascRadius", "hCascRadius", {HistType::kTH3F, {radiusAxis, invXiMassAxis, ptAxis}}}, - {"hV0CosPA", "hV0CosPA", {HistType::kTH3F, {cpaAxis, invXiMassAxis, ptAxis}}}, - {"hCascCosPA", "hCascCosPA", {HistType::kTH3F, {cpaAxis, invXiMassAxis, ptAxis}}}, - {"hDCAPosToPV", "hDCAPosToPV", {HistType::kTH3F, {vertexAxis, invXiMassAxis, ptAxis}}}, - {"hDCANegToPV", "hDCANegToPV", {HistType::kTH3F, {vertexAxis, invXiMassAxis, ptAxis}}}, - {"hDCABachToPV", "hDCABachToPV", {HistType::kTH3F, {vertexAxis, invXiMassAxis, ptAxis}}}, - {"hDCAV0ToPV", "hDCAV0ToPV", {HistType::kTH3F, {vertexAxis, invXiMassAxis, ptAxis}}}, - {"hDCAV0Dau", "hDCAV0Dau", {HistType::kTH3F, {dcaAxis, invXiMassAxis, ptAxis}}}, - {"hDCACascDau", "hDCACascDau", {HistType::kTH3F, {dcaAxis, invXiMassAxis, ptAxis}}}, - {"hLambdaMass", "hLambdaMass", {HistType::kTH3F, {invLambdaMassAxis, invXiMassAxis, ptAxis}}}, - - {"hMassXiMinus", "hMassXiMinus", {HistType::kTH3F, {invXiMassAxis, ptAxis, rapidityAxis}}}, - {"hMassXiPlus", "hMassXiPlus", {HistType::kTH3F, {invXiMassAxis, ptAxis, rapidityAxis}}}, - {"hMassOmegaMinus", "hMassOmegaMinus", {HistType::kTH3F, {invOmegaMassAxis, ptAxis, rapidityAxis}}}, - {"hMassOmegaPlus", "hMassOmegaPlus", {HistType::kTH3F, {invOmegaMassAxis, ptAxis, rapidityAxis}}}, - - // // invariant mass per cut, start with Xi - // {"hMassXi0", "Xi inv mass before selections", {HistType::kTH2F, {invMassAxis, ptAxis}}}, - // {"hMassXi1", "Xi inv mass after TPCnCrossedRows cut", {HistType::kTH2F, {invMassAxis, ptAxis}}}, - // {"hMassXi2", "Xi inv mass after ITSnClusters cut", {HistType::kTH2F, {invMassAxis, ptAxis}}}, - // {"hMassXi3", "Xi inv mass after topo cuts", {HistType::kTH2F, {invMassAxis, ptAxis}}}, - // {"hMassXi4", "Xi inv mass after V0 daughters PID cut", {HistType::kTH2F, {invMassAxis, ptAxis}}}, - // {"hMassXi5", "Xi inv mass after bachelor PID cut", {HistType::kTH2F, {invMassAxis, ptAxis}}}, - - // ITS & TPC clusters, with Xi inv mass - {"hTPCnCrossedRowsPos", "hTPCnCrossedRowsPos", {HistType::kTH3F, {tpcRowsAxis, invXiMassAxis, ptAxis}}}, - {"hTPCnCrossedRowsNeg", "hTPCnCrossedRowsNeg", {HistType::kTH3F, {tpcRowsAxis, invXiMassAxis, ptAxis}}}, - {"hTPCnCrossedRowsBach", "hTPCnCrossedRowsBach", {HistType::kTH3F, {tpcRowsAxis, invXiMassAxis, ptAxis}}}, - {"hITSnClustersPos", "hITSnClustersPos", {HistType::kTH3F, {itsClustersAxis, invXiMassAxis, ptAxis}}}, - {"hITSnClustersNeg", "hITSnClustersNeg", {HistType::kTH3F, {itsClustersAxis, invXiMassAxis, ptAxis}}}, - {"hITSnClustersBach", "hITSnClustersBach", {HistType::kTH3F, {itsClustersAxis, invXiMassAxis, ptAxis}}}, - {"hTPCChi2Pos", "hTPCChi2Pos", {HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Pos"}}}}, - {"hTPCChi2Neg", "hTPCChi2Neg", {HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Neg"}}}}, - {"hTPCChi2Bach", "hTPCChi2Bach", {HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Bach"}}}}, - {"hITSChi2Pos", "hITSChi2Pos", {HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Pos"}}}}, - {"hITSChi2Neg", "hITSChi2Neg", {HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Neg"}}}}, - {"hITSChi2Bach", "hITSChi2Bach", {HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Bach"}}}}, - - {"hTriggerQA", "hTriggerQA", {HistType::kTH1F, {{2, -0.5, 1.5, "Trigger y/n"}}}}, - }, - }; - - // Keep track of which selections the candidates pass - void init(InitContext const&) + // Gen-side guard. Only tracks "have we visited this gen + // collision yet" for downstream fills that need it (currently nothing + // calls this; reserved). Does NOT fill hEventCount — that is the reco + // counter's job. + bool seenGenCollOnce(int64_t mcgenCollIdx) { - ccdb->setURL(ccdbUrl); - ccdb->setCaching(true); - - auto h = registry.add("hSelectionStatus", "hSelectionStatus", HistType::kTH1I, {{10, 0, 10, "status"}}); - h->GetXaxis()->SetBinLabel(1, "All"); - h->GetXaxis()->SetBinLabel(2, "nTPC OK"); - h->GetXaxis()->SetBinLabel(3, "nITS OK"); - h->GetXaxis()->SetBinLabel(4, "track Chi2 OK"); - h->GetXaxis()->SetBinLabel(5, "Topo OK"); - h->GetXaxis()->SetBinLabel(6, "Track eta OK"); - h->GetXaxis()->SetBinLabel(7, "Cascade eta OK"); - h->GetXaxis()->SetBinLabel(8, "V0 PID OK"); - h->GetXaxis()->SetBinLabel(9, "Bach PID OK"); - - auto hEventSel = registry.add("hEventSel", "hEventSel", HistType::kTH1I, {{10, 0, 10, "selection criteria"}}); - hEventSel->GetXaxis()->SetBinLabel(1, "All"); - hEventSel->GetXaxis()->SetBinLabel(2, "sel8"); - hEventSel->GetXaxis()->SetBinLabel(3, "INEL0"); - hEventSel->GetXaxis()->SetBinLabel(4, "V_z"); - hEventSel->GetXaxis()->SetBinLabel(5, "NoSameBunchPileUp"); - hEventSel->GetXaxis()->SetBinLabel(6, "Selected events"); - - if (doprocessRecMC) { - // only create the rec matched to gen histograms if relevant - registry.add("truerec/hV0Radius", "hV0Radius", HistType::kTH1F, {radiusAxis}); - registry.add("truerec/hCascRadius", "hCascRadius", HistType::kTH1F, {radiusAxis}); - registry.add("truerec/hV0CosPA", "hV0CosPA", HistType::kTH1F, {cpaAxis}); - registry.add("truerec/hCascCosPA", "hCascCosPA", HistType::kTH1F, {cpaAxis}); - registry.add("truerec/hDCAPosToPV", "hDCAPosToPV", HistType::kTH1F, {vertexAxis}); - registry.add("truerec/hDCANegToPV", "hDCANegToPV", HistType::kTH1F, {vertexAxis}); - registry.add("truerec/hDCABachToPV", "hDCABachToPV", HistType::kTH1F, {vertexAxis}); - registry.add("truerec/hDCAV0ToPV", "hDCAV0ToPV", HistType::kTH1F, {vertexAxis}); - registry.add("truerec/hDCAV0Dau", "hDCAV0Dau", HistType::kTH1F, {dcaAxis}); - registry.add("truerec/hDCACascDau", "hDCACascDau", HistType::kTH1F, {dcaAxis}); - registry.add("truerec/hLambdaMass", "hLambdaMass", HistType::kTH1F, {invLambdaMassAxis}); - registry.add("truerec/hTPCnCrossedRowsPos", "hTPCnCrossedRowsPos", HistType::kTH1F, {tpcRowsAxis}); - registry.add("truerec/hTPCnCrossedRowsNeg", "hTPCnCrossedRowsNeg", HistType::kTH1F, {tpcRowsAxis}); - registry.add("truerec/hTPCnCrossedRowsBach", "hTPCnCrossedRowsBach", HistType::kTH1F, {tpcRowsAxis}); - registry.add("truerec/hITSnClustersPos", "hITSnClustersPos", HistType::kTH1F, {itsClustersAxis}); - registry.add("truerec/hITSnClustersNeg", "hITSnClustersNeg", HistType::kTH1F, {itsClustersAxis}); - registry.add("truerec/hITSnClustersBach", "hITSnClustersBach", HistType::kTH1F, {itsClustersAxis}); - registry.add("truerec/hTPCChi2Pos", "hTPCChi2Pos", HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Pos"}}); - registry.add("truerec/hTPCChi2Neg", "hTPCChi2Neg", HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Neg"}}); - registry.add("truerec/hTPCChi2Bach", "hTPCChi2Bach", HistType::kTH1F, {{100, 0, 10, "TPC Chi2 Bach"}}); - registry.add("truerec/hITSChi2Pos", "hITSChi2Pos", HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Pos"}}); - registry.add("truerec/hITSChi2Neg", "hITSChi2Neg", HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Neg"}}); - registry.add("truerec/hITSChi2Bach", "hITSChi2Bach", HistType::kTH1F, {{100, 0, 100, "ITS Chi2 Bach"}}); - registry.add("truerec/hXiMinus", "hXiMinus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("truerec/hXiPlus", "hXiPlus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("truerec/hOmegaMinus", "hOmegaMinus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("truerec/hOmegaPlus", "hOmegaPlus", HistType::kTH2F, {ptAxis, rapidityAxis}); - } - - if (doprocessGenMC) { - // only create the MC gen histograms if relevant - registry.add("gen/hXiMinus", "hXiMinus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("gen/hXiPlus", "hXiPlus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("gen/hOmegaMinus", "hOmegaMinus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("gen/hOmegaPlus", "hOmegaPlus", HistType::kTH2F, {ptAxis, rapidityAxis}); - - registry.add("genwithrec/hXiMinus", "hXiMinus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("genwithrec/hXiPlus", "hXiPlus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("genwithrec/hOmegaMinus", "hOmegaMinus", HistType::kTH2F, {ptAxis, rapidityAxis}); - registry.add("genwithrec/hOmegaPlus", "hOmegaPlus", HistType::kTH2F, {ptAxis, rapidityAxis}); - - registry.add("genwithrec/hNevents", "hNevents", HistType::kTH1F, {{1, 0, 1, "N generated events with reconstructed event"}}); - registry.add("gen/hNevents", "hNevents", HistType::kTH1F, {{1, 0, 1, "N generated events"}}); - } - } - - template - bool eventSelection(TCollision const& collision, bool fillHistos) - { - - if (fillHistos) - registry.fill(HIST("hEventSel"), 0); - if (doSel8 && !collision.sel8()) { - if (fillHistos) - registry.fill(HIST("hEventSel"), 1); - return false; - } else if (collision.multNTracksPVeta1() <= INEL) { - if (fillHistos) - registry.fill(HIST("hEventSel"), 2); - return false; - } else if (std::abs(collision.posZ()) > maxVertexZ) { - if (fillHistos) - registry.fill(HIST("hEventSel"), 3); - return false; - } else if (doNoSameBunchPileUp && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { - if (fillHistos) - registry.fill(HIST("hEventSel"), 4); + if (mcgenCollIdx == mLastEventCountGenCollIdx) { return false; } - // passes all selections - if (fillHistos) - registry.fill(HIST("hEventSel"), 5); + mLastEventCountGenCollIdx = mcgenCollIdx; return true; } - template - void fillMatchedHistos(LabeledCascades::iterator rec, int flag, TCollision collision) - { - if (flag == 0) - return; - if (!rec.has_mcParticle()) - return; - auto gen = rec.mcParticle(); - if (!gen.isPhysicalPrimary()) - return; - int genpdg = gen.pdgCode(); - if ((flag < 3 && std::abs(genpdg) == 3312) || (flag > 1 && std::abs(genpdg) == 3334)) { - // if casc is consistent with Xi and has matched gen Xi OR cand is consistent with Omega and has matched gen omega - // have to do this in case we reco true Xi with only Omega hypothesis (or vice versa) (very unlikely) - registry.fill(HIST("truerec/hV0Radius"), rec.v0radius()); - registry.fill(HIST("truerec/hCascRadius"), rec.cascradius()); - registry.fill(HIST("truerec/hV0CosPA"), rec.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); - registry.fill(HIST("truerec/hCascCosPA"), rec.casccosPA(collision.posX(), collision.posY(), collision.posZ())); - registry.fill(HIST("truerec/hDCAPosToPV"), rec.dcapostopv()); - registry.fill(HIST("truerec/hDCANegToPV"), rec.dcanegtopv()); - registry.fill(HIST("truerec/hDCABachToPV"), rec.dcabachtopv()); - registry.fill(HIST("truerec/hDCAV0ToPV"), rec.dcav0topv(collision.posX(), collision.posY(), collision.posZ())); - registry.fill(HIST("truerec/hDCAV0Dau"), rec.dcaV0daughters()); - registry.fill(HIST("truerec/hDCACascDau"), rec.dcacascdaughters()); - registry.fill(HIST("truerec/hLambdaMass"), rec.mLambda()); - registry.fill(HIST("truerec/hITSnClustersPos"), rec.posTrack_as().itsNCls()); - registry.fill(HIST("truerec/hITSnClustersNeg"), rec.negTrack_as().itsNCls()); - registry.fill(HIST("truerec/hITSnClustersBach"), rec.bachelor_as().itsNCls()); - registry.fill(HIST("truerec/hTPCnCrossedRowsPos"), rec.posTrack_as().tpcNClsCrossedRows()); - registry.fill(HIST("truerec/hTPCnCrossedRowsNeg"), rec.negTrack_as().tpcNClsCrossedRows()); - registry.fill(HIST("truerec/hTPCnCrossedRowsBach"), rec.bachelor_as().tpcNClsCrossedRows()); - registry.fill(HIST("truerec/hITSChi2Pos"), rec.posTrack_as().itsChi2NCl()); - registry.fill(HIST("truerec/hITSChi2Neg"), rec.negTrack_as().itsChi2NCl()); - registry.fill(HIST("truerec/hITSChi2Bach"), rec.bachelor_as().itsChi2NCl()); - registry.fill(HIST("truerec/hTPCChi2Pos"), rec.posTrack_as().tpcChi2NCl()); - registry.fill(HIST("truerec/hTPCChi2Neg"), rec.negTrack_as().tpcChi2NCl()); - registry.fill(HIST("truerec/hTPCChi2Bach"), rec.bachelor_as().tpcChi2NCl()); - switch (genpdg) { // is matched so we can use genpdg - case 3312: - registry.fill(HIST("truerec/hXiMinus"), rec.pt(), rec.yXi()); - break; - case -3312: - registry.fill(HIST("truerec/hXiPlus"), rec.pt(), rec.yXi()); - break; - case 3334: - registry.fill(HIST("truerec/hOmegaMinus"), rec.pt(), rec.yOmega()); - break; - case -3334: - registry.fill(HIST("truerec/hOmegaPlus"), rec.pt(), rec.yOmega()); - break; - } - } - } - - template - int processCandidate(TCascade const& casc, TCollision const& collision) - { - // these are the tracks: - auto bachTrack = casc.template bachelor_as(); - auto posTrack = casc.template posTrack_as(); - auto negTrack = casc.template negTrack_as(); - - // topo variables before cuts: - registry.fill(HIST("hV0Radius"), casc.v0radius(), casc.mXi(), casc.pt()); - registry.fill(HIST("hCascRadius"), casc.cascradius(), casc.mXi(), casc.pt()); - registry.fill(HIST("hV0CosPA"), casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()), casc.mXi(), casc.pt()); - registry.fill(HIST("hCascCosPA"), casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()), casc.mXi(), casc.pt()); - registry.fill(HIST("hDCAPosToPV"), casc.dcapostopv(), casc.mXi(), casc.pt()); - registry.fill(HIST("hDCANegToPV"), casc.dcanegtopv(), casc.mXi(), casc.pt()); - registry.fill(HIST("hDCABachToPV"), casc.dcabachtopv(), casc.mXi(), casc.pt()); - registry.fill(HIST("hDCAV0ToPV"), casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ()), casc.mXi(), casc.pt()); - registry.fill(HIST("hDCAV0Dau"), casc.dcaV0daughters(), casc.mXi(), casc.pt()); - registry.fill(HIST("hDCACascDau"), casc.dcacascdaughters(), casc.mXi(), casc.pt()); - registry.fill(HIST("hLambdaMass"), casc.mLambda(), casc.mXi(), casc.pt()); - - registry.fill(HIST("hITSnClustersPos"), posTrack.itsNCls(), casc.mXi(), casc.pt()); - registry.fill(HIST("hITSnClustersNeg"), negTrack.itsNCls(), casc.mXi(), casc.pt()); - registry.fill(HIST("hITSnClustersBach"), bachTrack.itsNCls(), casc.mXi(), casc.pt()); - registry.fill(HIST("hTPCnCrossedRowsPos"), posTrack.tpcNClsCrossedRows(), casc.mXi(), casc.pt()); - registry.fill(HIST("hTPCnCrossedRowsNeg"), negTrack.tpcNClsCrossedRows(), casc.mXi(), casc.pt()); - registry.fill(HIST("hTPCnCrossedRowsBach"), bachTrack.tpcNClsCrossedRows(), casc.mXi(), casc.pt()); - registry.fill(HIST("hITSChi2Pos"), posTrack.itsChi2NCl()); - registry.fill(HIST("hITSChi2Neg"), negTrack.itsChi2NCl()); - registry.fill(HIST("hITSChi2Bach"), bachTrack.itsChi2NCl()); - registry.fill(HIST("hTPCChi2Pos"), posTrack.tpcChi2NCl()); - registry.fill(HIST("hTPCChi2Neg"), negTrack.tpcChi2NCl()); - registry.fill(HIST("hTPCChi2Bach"), bachTrack.tpcChi2NCl()); - - registry.fill(HIST("hSelectionStatus"), 0); // all the cascade before selections - // registry.fill(HIST("hMassXi0"), casc.mXi(), casc.pt()); - - // TPC N crossed rows todo: check if minTPCCrossedRows > 50 - if (posTrack.tpcNClsCrossedRows() < minTPCCrossedRows || negTrack.tpcNClsCrossedRows() < minTPCCrossedRows || bachTrack.tpcNClsCrossedRows() < minTPCCrossedRows) - return 0; - - registry.fill(HIST("hSelectionStatus"), 1); // passes nTPC crossed rows - // registry.fill(HIST("hMassXi1"), casc.mXi(), casc.pt()); - - // ITS N clusters todo: check if minITSClusters > 0 - if (posTrack.itsNCls() < minITSClusters || negTrack.itsNCls() < minITSClusters || bachTrack.itsNCls() < minITSClusters) - return 0; - - registry.fill(HIST("hSelectionStatus"), 2); // passes nITS clusters - // registry.fill(HIST("hMassXi2"), casc.mXi(), casc.pt()); - - // Chi2 cuts - if (posTrack.itsChi2NCl() > itsChi2 || negTrack.itsChi2NCl() > itsChi2 || bachTrack.itsChi2NCl() > itsChi2) - return 0; - if (posTrack.tpcChi2NCl() > tpcChi2 || negTrack.tpcChi2NCl() > tpcChi2 || bachTrack.tpcChi2NCl() > tpcChi2) - return 0; - - registry.fill(HIST("hSelectionStatus"), 3); // passes Chi2 cuts - - //// TOPO CUTS //// TODO: improve! - double pvx = collision.posX(); - double pvy = collision.posY(); - double pvz = collision.posZ(); - if (casc.v0radius() < v0setting_radius || - casc.cascradius() < cascadesetting_cascradius || - casc.v0cosPA(pvx, pvy, pvz) < v0setting_cospa || - casc.casccosPA(pvx, pvy, pvz) < cascadesetting_cospa || - casc.dcav0topv(pvx, pvy, pvz) < cascadesetting_mindcav0topv || - std::abs(casc.mLambda() - 1.115683) > cascadesetting_v0masswindow) - return 0; // It failed at least one topo selection - - registry.fill(HIST("hSelectionStatus"), 4); // passes topo - // registry.fill(HIST("hMassXi3"), casc.mXi(), casc.pt()); - - if (std::abs(posTrack.eta()) > etaTracks || std::abs(negTrack.eta()) > etaTracks || std::abs(bachTrack.eta()) > etaTracks) - return 0; - - registry.fill(HIST("hSelectionStatus"), 5); // passes track eta - - if (std::abs(casc.eta()) > etaCascades) - return 0; - - registry.fill(HIST("hSelectionStatus"), 6); // passes candidate eta - - // TODO: TOF (for pT > 2 GeV per track?) - - //// TPC PID //// - // Lambda check - if (casc.sign() < 0) { - // Proton check: - if (std::abs(posTrack.tpcNSigmaPr()) > tpcNsigmaProton) - return 0; - // Pion check: - if (std::abs(negTrack.tpcNSigmaPi()) > tpcNsigmaPion) - return 0; - } else { - // Proton check: - if (std::abs(negTrack.tpcNSigmaPr()) > tpcNsigmaProton) - return 0; - // Pion check: - if (std::abs(posTrack.tpcNSigmaPi()) > tpcNsigmaPion) - return 0; - } - registry.fill(HIST("hSelectionStatus"), 7); // passes V0 daughters PID - // registry.fill(HIST("hMassXi4"), casc.mXi(), casc.pt()); - - // setting selection flag based on bachelor PID (and competing mass cut for omega's) - int flag = 0; - if (std::abs(bachTrack.tpcNSigmaPi()) < tpcNsigmaBachelor) - flag = 1; - if (std::abs(bachTrack.tpcNSigmaKa()) < tpcNsigmaBachelor && (!doCompetingMassCut || std::abs(pdgDB->Mass(3312) - casc.mXi()) > competingMassWindow)) - flag = 3 - flag; // 3 if only consistent with omega, 2 if consistent with both - - switch (flag) { - case 1: // only Xi - registry.fill(HIST("hSelectionStatus"), 8); // passes bach PID - if (casc.sign() < 0) { - registry.fill(HIST("hMassXiMinus"), casc.mXi(), casc.pt(), casc.yXi()); - } else { - registry.fill(HIST("hMassXiPlus"), casc.mXi(), casc.pt(), casc.yXi()); - } - break; - case 2: // Xi or Omega - registry.fill(HIST("hSelectionStatus"), 8); // passes bach PID - if (casc.sign() < 0) { - registry.fill(HIST("hMassXiMinus"), casc.mXi(), casc.pt(), casc.yXi()); - registry.fill(HIST("hMassOmegaMinus"), casc.mOmega(), casc.pt(), casc.yOmega()); - } else { - registry.fill(HIST("hMassXiPlus"), casc.mXi(), casc.pt(), casc.yXi()); - registry.fill(HIST("hMassOmegaPlus"), casc.mOmega(), casc.pt(), casc.yOmega()); - } - break; - case 3: // only Omega - registry.fill(HIST("hSelectionStatus"), 8); // passes bach PID - if (casc.sign() < 0) { - registry.fill(HIST("hMassOmegaMinus"), casc.mOmega(), casc.pt(), casc.yOmega()); - } else { - registry.fill(HIST("hMassOmegaPlus"), casc.mOmega(), casc.pt(), casc.yOmega()); - } - break; - } - - return flag; - - } // processCandidate - - void processGenMC(aod::McCollision const& mcCollision, soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) - { - // evsel - if (INEL >= 0 && !pwglf::isINELgtNmc(mcParticles, INEL, pdgDB)) - return; - if (std::abs(mcCollision.posZ()) > maxVertexZ) - return; - - registry.fill(HIST("gen/hNevents"), 0); - - for (auto const& mcPart : mcParticles) { - if (!mcPart.isPhysicalPrimary()) - continue; - if (std::abs(mcPart.eta()) > etaCascades) - continue; - - switch (mcPart.pdgCode()) { - case 3312: - registry.fill(HIST("gen/hXiMinus"), mcPart.pt(), mcPart.y()); - break; - case -3312: - registry.fill(HIST("gen/hXiPlus"), mcPart.pt(), mcPart.y()); - break; - case 3334: - registry.fill(HIST("gen/hOmegaMinus"), mcPart.pt(), mcPart.y()); - break; - case -3334: - registry.fill(HIST("gen/hOmegaPlus"), mcPart.pt(), mcPart.y()); - break; - } - } - - // Do the same thing, but now making sure there is at least one matched reconstructed event: - if (collisions.size() < 1) { - return; - } else { - bool evSel = false; // will be true if at least one rec. collision passes evsel - for (auto const& collision : collisions) { - // can be more than 1 rec. collisions due to event splitting - evSel = eventSelection(collision, false); - if (evSel) // exit loop if we find 1 rec. event that passes evsel - break; - } - if (evSel) { - // N gen events with a reconstructed event - registry.fill(HIST("genwithrec/hNevents"), 0); - - for (auto const& mcPart : mcParticles) { - if (!mcPart.isPhysicalPrimary()) - continue; - if (std::abs(mcPart.eta()) > etaCascades) - continue; - - switch (mcPart.pdgCode()) { - case 3312: - registry.fill(HIST("genwithrec/hXiMinus"), mcPart.pt(), mcPart.y()); - break; - case -3312: - registry.fill(HIST("genwithrec/hXiPlus"), mcPart.pt(), mcPart.y()); - break; - case 3334: - registry.fill(HIST("genwithrec/hOmegaMinus"), mcPart.pt(), mcPart.y()); - break; - case -3334: - registry.fill(HIST("genwithrec/hOmegaPlus"), mcPart.pt(), mcPart.y()); - break; - } - } - } - } - } // processGen - - // wrappers for data/MC processes on reco level - void processRecData(MyCollisions::iterator const& collision, aod::CascDataExt const& Cascades, FullTracksExtIUWithPID const&, aod::BCsWithTimestamps const&) - { - bool evSel = eventSelection(collision, true); - // do not skip the collision if event selection fails - this will lead to the cascadeFlag table having less entries than the Cascade table, and therefor not joinable. - for (auto const& casc : Cascades) { - if (!evSel) { - cascflags(0); - continue; - } - int flag = processCandidate(casc, collision); - cascflags(flag); - } - } - - void processRecMC(MyCollisions::iterator const& collision, LabeledCascades const& Cascades, FullTracksExtIUWithPID const&, aod::BCsWithTimestamps const&, aod::McParticles const&) + // Count cascades eligible for a given species in THIS event. + // The slice from CascDataExt/LabeledCascades contains both Ξ and Ω + // candidates; without species filtering, hLamXi and hLamOm both report + // total cascades (Ξ+Ω) → identical numbers. flag semantics defined as + // named constants below. + // Applies the same |y| + int countSpeciesEligible(TCascades const& cascades, TFlagsRow const& flagsStart) { - bool evSel = eventSelection(collision, true); - // do not skip the collision if event selection fails - this will lead to the cascadeFlag table having less entries than the Cascade table, and therefor not joinable. - for (auto const& casc : Cascades) { - if (!evSel) { - cascflags(0); + int n = 0; + for (const auto& c : cascades) { + auto fr = flagsStart + c.globalIndex(); + int f = fr.isSelected(); + if (f == kFlagRejected) { continue; } - int flag = processCandidate(casc, collision); - cascflags(flag); - // do mc matching here - fillMatchedHistos(casc, flag, collision); // if sign < 0 then pdg > 0 - } - } - - PROCESS_SWITCH(CascadeSelector, processRecData, "Process rec data", true); - PROCESS_SWITCH(CascadeSelector, processRecMC, "Process rec MC", false); - PROCESS_SWITCH(CascadeSelector, processGenMC, "Process gen MC", false); -}; // struct - -struct CascadeCorrelations { - Service ccdb; - - // Configurables - Configurable maxRapidity{"maxRapidity", 0.5, "|y| < maxRapidity"}; - Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; - Configurable nMixedEvents{"nMixedEvents", 10, "Number of events to be mixed"}; - Configurable doEfficiencyCorrection{"doEfficiencyCorrection", true, "flag to do efficiency corrections"}; - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; - Configurable useTrigger{"useTrigger", false, "Use trigger selection on skimmed data"}; - Configurable triggerList{"triggerList", "fDoubleXi, fDoubleOmega, fOmegaXi", "List of triggers used to select events"}; - Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "Users/r/rspijker/test/EffTest", "Path of the efficiency corrections"}; - Configurable doTFBorderCut{"doTFBorderCut", true, "Switch to apply TimeframeBorderCut event selection"}; - Configurable doSel8{"doSel8", true, "Switch to apply sel8 event selection"}; - - ConfigurableAxis radiusAxis = {"radiusAxis", {100, 0.0f, 50.0f}, "cm"}; - ConfigurableAxis cpaAxis = {"cpaAxis", {100, 0.95f, 1.0f}, "CPA"}; - ConfigurableAxis invMassAxis = {"invMassAxis", {1000, 1.0f, 2.0f}, "Inv. Mass (GeV/c^{2})"}; - ConfigurableAxis deltaPhiAxis = {"deltaPhiAxis", {180, -PIHalf, 3 * PIHalf}, "#Delta#varphi"}; // 180 is divisible by 18 (tpc sectors) and 20 (run 2 binning) - ConfigurableAxis ptAxis = {"ptAxis", {150, 0, 15}, "#it{p}_{T}"}; - ConfigurableAxis vertexAxis = {"vertexAxis", {200, -10.0f, 10.0f}, "cm"}; - ConfigurableAxis dcaAxis = {"dcaAxis", {100, 0.0f, 2.0f}, "cm"}; - ConfigurableAxis multiplicityAxis{"multiplicityAxis", {100, 0, 100}, "Multiplicity (centFT0M?)"}; - ConfigurableAxis invLambdaMassAxis{"invLambdaMassAxis", {100, 1.07f, 1.17f}, "Inv. Mass (GeV/c^{2})"}; - AxisSpec signAxis{3, -1.5, 1.5, "sign of cascade"}; - AxisSpec deltaYAxis{40, -2.f, 2.f, "#Delta y"}; - AxisSpec rapidityAxis{100, -1.f, 1.f, "y"}; - AxisSpec selectionFlagAxis{4, -0.5f, 3.5f, "Selection flag of casc candidate"}; - AxisSpec itsClustersAxis{8, -0.5, 7.5, "number of ITS clusters"}; - AxisSpec tpcRowsAxis{160, -0.5, 159.5, "TPC crossed rows"}; - - // initialize efficiency maps - TH1D* hEffXiMin; - TH1D* hEffXiPlus; - TH1D* hEffOmegaMin; - TH1D* hEffOmegaPlus; - - // used in MC closure test - Service pdgDB; - o2::pwglf::ParticleCounter mCounter; - - void init(InitContext const&) - { - ccdb->setURL(ccdbUrl); - ccdb->setCaching(true); - if (doEfficiencyCorrection) { - TList* effList = ccdb->getForTimeStamp(efficiencyCCDBPath, 1); - if (!effList) { - LOGF(fatal, "null ptr in efficiency list!"); - } - hEffXiMin = static_cast(effList->FindObject("hXiMinEff")); - hEffXiPlus = static_cast(effList->FindObject("hXiPlusEff")); - hEffOmegaMin = static_cast(effList->FindObject("hOmegaMinEff")); - hEffOmegaPlus = static_cast(effList->FindObject("hOmegaPlusEff")); - } - - mCounter.mPdgDatabase = pdgDB.service; - mCounter.mSelectPrimaries = true; - } - - double getEfficiency(TH1* h, double pT, double y = 0) - { - // This function returns 1 / eff - double eff = h->GetBinContent(h->FindFixBin(pT, y)); - if (eff == 0) - return 0; - else - return 1. / eff; - } - - bool autoCorrelation(std::array triggerTracks, std::array assocTracks) - { - // function that loops over 2 arrays of track indices, checking for common elements - for (int triggerTrack : triggerTracks) { - for (int assocTrack : assocTracks) { - if (triggerTrack == assocTrack) - return true; - } - } - return false; - } - - HistogramRegistry registry{ - "registry", - { - // inv mass - {"hMassXiMinus", "hMassXiMinus", {HistType::kTH3F, {{200, 1.24, 1.44, "Inv. Mass (GeV/c^{2})"}, ptAxis, rapidityAxis}}}, - {"hMassXiPlus", "hMassXiPlus", {HistType::kTH3F, {{200, 1.24, 1.44, "Inv. Mass (GeV/c^{2})"}, ptAxis, rapidityAxis}}}, - {"hMassOmegaMinus", "hMassOmegaMinus", {HistType::kTH3F, {{200, 1.6, 1.8, "Inv. Mass (GeV/c^{2})"}, ptAxis, rapidityAxis}}}, - {"hMassOmegaPlus", "hMassOmegaPlus", {HistType::kTH3F, {{200, 1.6, 1.8, "Inv. Mass (GeV/c^{2})"}, ptAxis, rapidityAxis}}}, - // efficiency corrected inv mass - {"hMassXiEffCorrected", "hMassXiEffCorrected", {HistType::kTHnSparseF, {invMassAxis, signAxis, ptAxis, rapidityAxis, vertexAxis, multiplicityAxis}}, true}, - {"hMassOmegaEffCorrected", "hMassOmegaEffCorrected", {HistType::kTHnSparseF, {invMassAxis, signAxis, ptAxis, rapidityAxis, vertexAxis, multiplicityAxis}}, true}, - - // trigger QA - {"hTriggerQA", "hTriggerQA", {HistType::kTH1F, {{2, -0.5, 1.5, "Trigger y/n"}}}}, - - // basic selection variables (after cuts) - {"hV0Radius", "hV0Radius", {HistType::kTH1F, {radiusAxis}}}, - {"hCascRadius", "hCascRadius", {HistType::kTH1F, {radiusAxis}}}, - {"hV0CosPA", "hV0CosPA", {HistType::kTH1F, {cpaAxis}}}, - {"hCascCosPA", "hCascCosPA", {HistType::kTH1F, {cpaAxis}}}, - {"hDCAPosToPV", "hDCAPosToPV", {HistType::kTH1F, {vertexAxis}}}, - {"hDCANegToPV", "hDCANegToPV", {HistType::kTH1F, {vertexAxis}}}, - {"hDCABachToPV", "hDCABachToPV", {HistType::kTH1F, {vertexAxis}}}, - {"hDCAV0ToPV", "hDCAV0ToPV", {HistType::kTH1F, {vertexAxis}}}, - {"hDCAV0Dau", "hDCAV0Dau", {HistType::kTH1F, {dcaAxis}}}, - {"hDCACascDau", "hDCACascDau", {HistType::kTH1F, {dcaAxis}}}, - {"hLambdaMass", "hLambdaMass", {HistType::kTH1F, {invLambdaMassAxis}}}, - {"hTPCnCrossedRowsPos", "hTPCnCrossedRowsPos", {HistType::kTH1F, {tpcRowsAxis}}}, - {"hTPCnCrossedRowsNeg", "hTPCnCrossedRowsNeg", {HistType::kTH1F, {tpcRowsAxis}}}, - {"hTPCnCrossedRowsBach", "hTPCnCrossedRowsBach", {HistType::kTH1F, {tpcRowsAxis}}}, - {"hITSnClustersPos", "hITSnClustersPos", {HistType::kTH1F, {itsClustersAxis}}}, - {"hITSnClustersNeg", "hITSnClustersNeg", {HistType::kTH1F, {itsClustersAxis}}}, - {"hITSnClustersBach", "hITSnClustersBach", {HistType::kTH1F, {itsClustersAxis}}}, - - {"hSelectionFlag", "hSelectionFlag", {HistType::kTH1I, {selectionFlagAxis}}}, - {"hAutoCorrelation", "hAutoCorrelation", {HistType::kTH1I, {{4, -0.5f, 3.5f, "Types of SS autocorrelation"}}}}, - {"hAutoCorrelationOS", "hAutoCorrelationOS", {HistType::kTH1I, {{2, -1.f, 1.f, "Charge of OS autocorrelated track"}}}}, - {"hPhi", "hPhi", {HistType::kTH1F, {{180, 0, TwoPI, "#varphi"}}}}, - {"hEta", "hEta", {HistType::kTH1F, {{100, -2, 2, "#eta"}}}}, - {"hRapidityXi", "hRapidityXi", {HistType::kTH1F, {rapidityAxis}}}, - {"hRapidityOmega", "hRapidityOmega", {HistType::kTH1F, {rapidityAxis}}}, - - // correlation histos - {"hDeltaPhiSS", "hDeltaPhiSS", {HistType::kTH1F, {deltaPhiAxis}}}, - {"hDeltaPhiOS", "hDeltaPhiOS", {HistType::kTH1F, {deltaPhiAxis}}}, - - {"hXiXi", "hXiXi", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, signAxis, signAxis, ptAxis, ptAxis, invMassAxis, invMassAxis, vertexAxis, multiplicityAxis}}, true}, - {"hXiOm", "hXiOm", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, signAxis, signAxis, ptAxis, ptAxis, invMassAxis, invMassAxis, vertexAxis, multiplicityAxis}}, true}, - {"hOmOm", "hOmOm", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, signAxis, signAxis, ptAxis, ptAxis, invMassAxis, invMassAxis, vertexAxis, multiplicityAxis}}, true}, - - // Mixed events - {"MixedEvents/hMEVz1", "hMEVz1", {HistType::kTH1F, {vertexAxis}}}, - {"MixedEvents/hMEVz2", "hMEVz2", {HistType::kTH1F, {vertexAxis}}}, - {"MixedEvents/hMEDeltaPhiSS", "hMEDeltaPhiSS", {HistType::kTH1F, {deltaPhiAxis}}}, - {"MixedEvents/hMEDeltaPhiOS", "hMEDeltaPhiOS", {HistType::kTH1F, {deltaPhiAxis}}}, - {"MixedEvents/hMEQA", "hMEQA", {HistType::kTH1I, {{2, 0, 2, "QA for exceptions in ME (this histogram should have 0 entries!)"}}}}, - {"MixedEvents/hMEAutoCorrelation", "hMEAutoCorrelation", {HistType::kTH1I, {{4, -0.5f, 3.5f, "Types of SS autocorrelation"}}}}, - {"MixedEvents/hMEAutoCorrelationOS", "hMEAutoCorrelationOS", {HistType::kTH1I, {{2, -1.f, 1.f, "Charge of OS autocorrelated track"}}}}, - - {"MixedEvents/hMEXiXi", "hMEXiXi", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, signAxis, signAxis, ptAxis, ptAxis, invMassAxis, invMassAxis, vertexAxis, multiplicityAxis}}, true}, - {"MixedEvents/hMEXiOm", "hMEXiOm", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, signAxis, signAxis, ptAxis, ptAxis, invMassAxis, invMassAxis, vertexAxis, multiplicityAxis}}, true}, - {"MixedEvents/hMEOmOm", "hMEOmOm", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, signAxis, signAxis, ptAxis, ptAxis, invMassAxis, invMassAxis, vertexAxis, multiplicityAxis}}, true}, - - // MC closure - {"MC/hMCPlusMinus", "hMCPlusMinus", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, ptAxis, ptAxis, vertexAxis, multiplicityAxis}}, true}, - {"MC/hMCPlusPlus", "hMCPlusPlus", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, ptAxis, ptAxis, vertexAxis, multiplicityAxis}}, true}, - {"MC/hMCMinusPlus", "hMCMinusPlus", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, ptAxis, ptAxis, vertexAxis, multiplicityAxis}}, true}, - {"MC/hMCMinusMinus", "hMCMinusMinus", {HistType::kTHnSparseF, {deltaPhiAxis, deltaYAxis, ptAxis, ptAxis, vertexAxis, multiplicityAxis}}, true}, - - {"MC/hGenMultNoReco", "hGenMultNoReco", {HistType::kTH1I, {{100, 0, 100, "Number of generated charged primaries"}}}}, - {"MC/hGenMultOneReco", "hGenMultOneReco", {HistType::kTH1I, {{100, 0, 100, "Number of generated charged primaries"}}}}, - {"MC/hSplitEvents", "hSplitEvents", {HistType::kTH1I, {{10, 0, 10, "Number of rec. events per gen event"}}}}, - - // debug - {"MC/hPhi", "hPhi", {HistType::kTH1F, {{180, 0, TwoPI}}}}, - {"MC/hEta", "hEta", {HistType::kTH1F, {{100, -2, 2}}}}, - {"MC/hRapidity", "hRapidity", {HistType::kTH1F, {{100, -2, 2}}}}, - }, - }; - - // cascade filter - Filter cascadeSelector = aod::cascadeflags::isSelected > 0; - - // Warning: it is not possible to use this axis as configurable due to a bug - however, default values are sensible. - SliceCache cache; - ConfigurableAxis axisVtxZ{"axisVtxZ", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - // ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100, 1000}, "Mixing bins - multiplicity"}; - // using BinningType = ColumnBinningPolicy; - // BinningType colBinning{{axisVtxZ, axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. - using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVtxZ}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. - - void processSameEvent(MyCollisionsMult::iterator const& collision, MyCascades const& Cascades, FullTracksExtIU const&, aod::BCsWithTimestamps const&) - { - - double weight; - // Some QA on the cascades - for (auto const& casc : Cascades) { - if (casc.isSelected() <= 2) { // not exclusively an Omega --> consistent with Xi or both - if (casc.sign() < 0) { - registry.fill(HIST("hMassXiMinus"), casc.mXi(), casc.pt(), casc.yXi()); - weight = getEfficiency(hEffXiMin, casc.pt(), casc.yXi()); - } else { - registry.fill(HIST("hMassXiPlus"), casc.mXi(), casc.pt(), casc.yXi()); - weight = getEfficiency(hEffXiPlus, casc.pt(), casc.yXi()); - } - // LOGF(info, "casc pt %f, weight %f", casc.pt(), weight); - registry.fill(HIST("hMassXiEffCorrected"), casc.mXi(), casc.sign(), casc.pt(), casc.yXi(), collision.posZ(), collision.centFT0M(), weight); - registry.fill(HIST("hRapidityXi"), casc.yXi()); - } - if (casc.isSelected() >= 2) { // consistent with Omega or both - if (casc.sign() < 0) { - registry.fill(HIST("hMassOmegaMinus"), casc.mOmega(), casc.pt(), casc.yOmega()); - weight = getEfficiency(hEffOmegaMin, casc.pt(), casc.yOmega()); - } else { - registry.fill(HIST("hMassOmegaPlus"), casc.mOmega(), casc.pt(), casc.yOmega()); - weight = getEfficiency(hEffOmegaPlus, casc.pt(), casc.yOmega()); + if constexpr (IsOmega) { + if ((f == kFlagXiAndOmega || f == kFlagOmegaOnly) && std::abs(c.yOmega()) <= maxY) { + ++n; } - registry.fill(HIST("hMassOmegaEffCorrected"), casc.mOmega(), casc.sign(), casc.pt(), casc.yOmega(), collision.posZ(), collision.centFT0M(), weight); - registry.fill(HIST("hRapidityOmega"), casc.yOmega()); - } - registry.fill(HIST("hV0Radius"), casc.v0radius()); - registry.fill(HIST("hCascRadius"), casc.cascradius()); - registry.fill(HIST("hV0CosPA"), casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); - registry.fill(HIST("hCascCosPA"), casc.casccosPA(collision.posX(), collision.posY(), collision.posZ())); - registry.fill(HIST("hDCAPosToPV"), casc.dcapostopv()); - registry.fill(HIST("hDCANegToPV"), casc.dcanegtopv()); - registry.fill(HIST("hDCABachToPV"), casc.dcabachtopv()); - registry.fill(HIST("hDCAV0ToPV"), casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ())); - registry.fill(HIST("hDCAV0Dau"), casc.dcaV0daughters()); - registry.fill(HIST("hDCACascDau"), casc.dcacascdaughters()); - registry.fill(HIST("hLambdaMass"), casc.mLambda()); - registry.fill(HIST("hITSnClustersPos"), casc.posTrack_as().itsNCls()); - registry.fill(HIST("hITSnClustersNeg"), casc.negTrack_as().itsNCls()); - registry.fill(HIST("hITSnClustersBach"), casc.bachelor_as().itsNCls()); - registry.fill(HIST("hTPCnCrossedRowsPos"), casc.posTrack_as().tpcNClsCrossedRows()); - registry.fill(HIST("hTPCnCrossedRowsNeg"), casc.negTrack_as().tpcNClsCrossedRows()); - registry.fill(HIST("hTPCnCrossedRowsBach"), casc.bachelor_as().tpcNClsCrossedRows()); - - registry.fill(HIST("hSelectionFlag"), casc.isSelected()); - registry.fill(HIST("hPhi"), casc.phi()); - registry.fill(HIST("hEta"), casc.eta()); - } // casc loop - - for (auto& [c0, c1] : combinations(Cascades, Cascades)) { // combinations automatically applies strictly upper in case of 2 identical tables - // Define the trigger as the particle with the highest pT. As we can't swap the cascade tables themselves, we swap the addresses and later dereference them - auto* triggerAddress = &c0; - auto* assocAddress = &c1; - if (assocAddress->pt() > triggerAddress->pt()) { - std::swap(triggerAddress, assocAddress); - } - auto trigger = *triggerAddress; - auto assoc = *assocAddress; - - // autocorrelation check - std::array triggerTracks = {trigger.posTrackId(), trigger.negTrackId(), trigger.bachelorId()}; - std::array assocTracks = {assoc.posTrackId(), assoc.negTrackId(), assoc.bachelorId()}; - if (autoCorrelation(triggerTracks, assocTracks)) - continue; - - // calculate angular correlations - double dphi = RecoDecay::constrainAngle(trigger.phi() - assoc.phi(), -PIHalf); - - double invMassXiTrigg = trigger.mXi(); - double invMassOmTrigg = trigger.mOmega(); - double invMassXiAssoc = assoc.mXi(); - double invMassOmAssoc = assoc.mOmega(); - - double weightTrigg = 1.; - double weightAssoc = 1.; - - if (trigger.isSelected() <= 2 && std::abs(trigger.yXi()) < maxRapidity) { // trigger Xi - if (doEfficiencyCorrection) - weightTrigg = trigger.sign() < 0 ? getEfficiency(hEffXiMin, trigger.pt()) : getEfficiency(hEffXiPlus, trigger.pt()); - if (assoc.isSelected() <= 2 && std::abs(assoc.yXi()) < maxRapidity) { // assoc Xi - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffXiMin, assoc.pt()) : getEfficiency(hEffXiPlus, assoc.pt()); - registry.fill(HIST("hXiXi"), dphi, trigger.yXi() - assoc.yXi(), trigger.sign(), assoc.sign(), trigger.pt(), assoc.pt(), invMassXiTrigg, invMassXiAssoc, collision.posZ(), collision.centFT0M(), weightTrigg * weightAssoc); - } - if (assoc.isSelected() >= 2 && std::abs(assoc.yOmega()) < maxRapidity) { // assoc Omega - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffOmegaMin, assoc.pt()) : getEfficiency(hEffOmegaPlus, assoc.pt()); - registry.fill(HIST("hXiOm"), dphi, trigger.yXi() - assoc.yOmega(), trigger.sign(), assoc.sign(), trigger.pt(), assoc.pt(), invMassXiTrigg, invMassOmAssoc, collision.posZ(), collision.centFT0M(), weightTrigg * weightAssoc); - } - } - if (trigger.isSelected() >= 2 && std::abs(trigger.yOmega()) < maxRapidity) { // trigger Omega - if (doEfficiencyCorrection) - weightTrigg = trigger.sign() < 0 ? getEfficiency(hEffOmegaMin, trigger.pt()) : getEfficiency(hEffOmegaPlus, trigger.pt()); - if (assoc.isSelected() <= 2 && std::abs(assoc.yXi()) < maxRapidity) { // assoc Xi - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffXiMin, assoc.pt()) : getEfficiency(hEffXiPlus, assoc.pt()); - // if Omega-Xi, fill the Xi-Omega histogram (flip the trigger/assoc and dphy,dy signs) - registry.fill(HIST("hXiOm"), RecoDecay::constrainAngle(assoc.phi() - trigger.phi(), -PIHalf), -(trigger.yOmega() - assoc.yXi()), assoc.sign(), trigger.sign(), assoc.pt(), trigger.pt(), invMassXiAssoc, invMassOmTrigg, collision.posZ(), collision.centFT0M(), weightTrigg * weightAssoc); - } - if (assoc.isSelected() >= 2 && std::abs(assoc.yOmega()) < maxRapidity) { // assoc Omega - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffOmegaMin, assoc.pt()) : getEfficiency(hEffOmegaPlus, assoc.pt()); - registry.fill(HIST("hOmOm"), dphi, trigger.yOmega() - assoc.yOmega(), trigger.sign(), assoc.sign(), trigger.pt(), assoc.pt(), invMassOmTrigg, invMassOmAssoc, collision.posZ(), collision.centFT0M(), weightTrigg * weightAssoc); - } - } - - // QA plots - if (trigger.sign() * assoc.sign() < 0) { - registry.fill(HIST("hDeltaPhiOS"), dphi); } else { - registry.fill(HIST("hDeltaPhiSS"), dphi); - } - } // correlations - } // process same event - - void processMixedEvent(MyCollisionsMult const& collisions, MyCascades const& Cascades, FullTracksExtIU const&) - { - auto cascadesTuple = std::make_tuple(Cascades); - SameKindPair pair{colBinning, nMixedEvents, -1, collisions, cascadesTuple, &cache}; - - for (auto const& [col1, cascades1, col2, cascades2] : pair) { - if (!col1.sel8() || !col2.sel8()) - continue; - if (std::abs(col1.posZ()) > zVertexCut || std::abs(col2.posZ()) > zVertexCut) - continue; - if (col1.globalIndex() == col2.globalIndex()) { - registry.fill(HIST("hMEQA"), 0.5); - continue; - } - registry.fill(HIST("MixedEvents/hMEVz1"), col1.posZ()); - registry.fill(HIST("MixedEvents/hMEVz2"), col2.posZ()); - - for (auto& [casc1, casc2] : combinations(CombinationsFullIndexPolicy(cascades1, cascades2))) { - // specify FullIndexPolicy since the cascades are from different collisions - auto* triggerAddress = &casc1; - auto* assocAddress = &casc2; - if (assocAddress->pt() > triggerAddress->pt()) { - std::swap(triggerAddress, assocAddress); - } - auto trigger = *triggerAddress; - auto assoc = *assocAddress; - - if (trigger.collisionId() == assoc.collisionId()) { - registry.fill(HIST("hMEQA"), 1.5); - continue; - } - - std::array triggerTracks = {trigger.posTrackId(), trigger.negTrackId(), trigger.bachelorId()}; - std::array assocTracks = {assoc.posTrackId(), assoc.negTrackId(), assoc.bachelorId()}; - if (autoCorrelation(triggerTracks, assocTracks)) - continue; - - double dphi = RecoDecay::constrainAngle(trigger.phi() - assoc.phi(), -PIHalf); - - double invMassXiTrigg = trigger.mXi(); - double invMassOmTrigg = trigger.mOmega(); - double invMassXiAssoc = assoc.mXi(); - double invMassOmAssoc = assoc.mOmega(); - - double weightTrigg = 1.; - double weightAssoc = 1.; - - if (trigger.isSelected() <= 2 && std::abs(trigger.yXi()) < maxRapidity) { // trigger Xi - if (doEfficiencyCorrection) - weightTrigg = trigger.sign() < 0 ? getEfficiency(hEffXiMin, trigger.pt()) : getEfficiency(hEffXiPlus, trigger.pt()); - if (assoc.isSelected() <= 2 && std::abs(assoc.yXi()) < maxRapidity) { // assoc Xi - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffXiMin, assoc.pt()) : getEfficiency(hEffXiPlus, assoc.pt()); - registry.fill(HIST("MixedEvents/hMEXiXi"), dphi, trigger.yXi() - assoc.yXi(), trigger.sign(), assoc.sign(), trigger.pt(), assoc.pt(), invMassXiTrigg, invMassXiAssoc, col1.posZ(), col1.centFT0M(), weightTrigg * weightAssoc); - } - if (assoc.isSelected() >= 2 && std::abs(assoc.yOmega()) < maxRapidity) { // assoc Omega - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffOmegaMin, assoc.pt()) : getEfficiency(hEffOmegaPlus, assoc.pt()); - registry.fill(HIST("MixedEvents/hMEXiOm"), dphi, trigger.yXi() - assoc.yOmega(), trigger.sign(), assoc.sign(), trigger.pt(), assoc.pt(), invMassXiTrigg, invMassOmAssoc, col1.posZ(), col1.centFT0M(), weightTrigg * weightAssoc); - } - } - if (trigger.isSelected() >= 2 && std::abs(trigger.yOmega()) < maxRapidity) { // trigger Omega - if (doEfficiencyCorrection) - weightTrigg = trigger.sign() < 0 ? getEfficiency(hEffOmegaMin, trigger.pt()) : getEfficiency(hEffOmegaPlus, trigger.pt()); - if (assoc.isSelected() <= 2 && std::abs(assoc.yXi()) < maxRapidity) { // assoc Xi - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffXiMin, assoc.pt()) : getEfficiency(hEffXiPlus, assoc.pt()); - // if Omega-Xi, fill the Xi-Omega histogram (flip the trigger/assoc and dphy,dy signs) - registry.fill(HIST("MixedEvents/hMEXiOm"), RecoDecay::constrainAngle(assoc.phi() - trigger.phi(), -PIHalf), -(trigger.yOmega() - assoc.yXi()), assoc.sign(), trigger.sign(), assoc.pt(), trigger.pt(), invMassXiAssoc, invMassOmTrigg, col1.posZ(), col1.centFT0M(), weightTrigg * weightAssoc); - } - if (assoc.isSelected() >= 2 && std::abs(assoc.yOmega()) < maxRapidity) { // assoc Omega - if (doEfficiencyCorrection) - weightAssoc = assoc.sign() < 0 ? getEfficiency(hEffOmegaMin, assoc.pt()) : getEfficiency(hEffOmegaPlus, assoc.pt()); - registry.fill(HIST("MixedEvents/hMEOmOm"), dphi, trigger.yOmega() - assoc.yOmega(), trigger.sign(), assoc.sign(), trigger.pt(), assoc.pt(), invMassOmTrigg, invMassOmAssoc, col1.posZ(), col1.centFT0M(), weightTrigg * weightAssoc); - } - } - - // QA plots - if (trigger.sign() * assoc.sign() < 0) { - registry.fill(HIST("MixedEvents/hMEDeltaPhiOS"), dphi); - } else { - registry.fill(HIST("MixedEvents/hMEDeltaPhiSS"), dphi); - } - } // correlations - } // collisions - } // process mixed events - - Configurable etaGenCascades{"etaGenCascades", 0.8, "min/max of eta for generated cascades"}; - Filter genCascadesFilter = nabs(aod::mcparticle::pdgCode) == 3312; - - void processMC(aod::McCollision const&, soa::SmallGroups> const& collisions, soa::Filtered const& genCascades, aod::McParticles const& mcParticles) - { - // Let's do some logic on matched reconstructed collisions - if there less or more than one, fill some QA and skip the rest - double FT0mult = -1; // non-sensible default value just in case - double vtxz = -999.; // non-sensible default value just in case - if (collisions.size() < 1) { - registry.fill(HIST("MC/hSplitEvents"), 0); - registry.fill(HIST("MC/hGenMultNoReco"), mCounter.countFT0A(mcParticles) + mCounter.countFT0C(mcParticles)); - return; - } else if (collisions.size() == 1) { - registry.fill(HIST("MC/hSplitEvents"), 1); - registry.fill(HIST("MC/hGenMultOneReco"), mCounter.countFT0A(mcParticles) + mCounter.countFT0C(mcParticles)); - for (auto const& collision : collisions) { // not really a loop, as there is only one collision - FT0mult = collision.centFT0M(); - vtxz = collision.posZ(); - } - } else if (collisions.size() > 1) { - registry.fill(HIST("MC/hSplitEvents"), collisions.size()); - return; - } - - // QA - for (auto& casc : genCascades) { - if (!casc.isPhysicalPrimary()) - continue; - registry.fill(HIST("MC/hPhi"), casc.phi()); - registry.fill(HIST("MC/hEta"), casc.eta()); - registry.fill(HIST("MC/hRapidity"), casc.y()); - } - - for (auto& [c0, c1] : combinations(genCascades, genCascades)) { // combinations automatically applies strictly upper in case of 2 identical tables - // Define the trigger as the particle with the highest pT. As we can't swap the cascade tables themselves, we swap the addresses and later dereference them - auto* triggerAddress = &c0; - auto* assocAddress = &c1; - if (assocAddress->pt() > triggerAddress->pt()) { - std::swap(triggerAddress, assocAddress); - } - auto trigger = *triggerAddress; - auto assoc = *assocAddress; - - if (!trigger.isPhysicalPrimary() || !assoc.isPhysicalPrimary()) - continue; // require the cascades to be primaries - if (std::abs(trigger.eta()) > etaGenCascades) - continue; // only apply eta cut to trigger - trigger normalization still valid without introducing 2-particle-acceptance effects - - double dphi = RecoDecay::constrainAngle(trigger.phi() - assoc.phi(), -PIHalf); - - if (trigger.pdgCode() < 0) { // anti-trigg --> Plus - if (assoc.pdgCode() < 0) { // anti-assoc --> Plus - registry.fill(HIST("MC/hMCPlusPlus"), dphi, trigger.y() - assoc.y(), trigger.pt(), assoc.pt(), vtxz, FT0mult); - } else { // assoc --> Minus - registry.fill(HIST("MC/hMCPlusMinus"), dphi, trigger.y() - assoc.y(), trigger.pt(), assoc.pt(), vtxz, FT0mult); - } - } else { // trig --> Minus - if (assoc.pdgCode() < 0) { // anti-assoc --> Plus - registry.fill(HIST("MC/hMCMinusPlus"), dphi, trigger.y() - assoc.y(), trigger.pt(), assoc.pt(), vtxz, FT0mult); - } else { - registry.fill(HIST("MC/hMCMinusMinus"), dphi, trigger.y() - assoc.y(), trigger.pt(), assoc.pt(), vtxz, FT0mult); + if ((f == kFlagXiOnly || f == kFlagXiAndOmega) && std::abs(c.yXi()) <= maxY) { + ++n; } } } + return n; } - PROCESS_SWITCH(CascadeCorrelations, processSameEvent, "Process same events", true); - PROCESS_SWITCH(CascadeCorrelations, processMixedEvent, "Process mixed events", true); - PROCESS_SWITCH(CascadeCorrelations, processMC, "Process MC", false); - -}; // struct - -// Branch data structs for TTree filling — plain POD, no O2 framework types. -// Stored via a single raw pointer (1 StructToTuple slot) from the task struct. -namespace lxicorr -{ -struct CascBranches { - float pt, rap, mass; - int sign; - float cascCosPA, v0CosPA; - float cascRadius, v0Radius; - float dcaV0Dau, dcaCascDau; - float dcaV0ToPV, dcaPosToPV, dcaNegToPV, dcaBachToPV; - float cent, pvZ; - // MC truth-matching (filled only in MC mode; default = no match) - int pdgCode{0}; // PDG of matched McParticle (0 = no match = background) - bool isPhysPrim{false}; -}; - -// Holds branch data for both Xi and Omega — one raw pointer covers both. -// Gen-level branches — kinematics + truth only, no topology -struct GenBranches { - float pt, rap, cent, pvZ; - int pdgCode; - bool isPhysPrim; -}; - -// Single pointer covers all four branch sets — 1 StructToTuple slot -struct BranchPair { - CascBranches xi{}; - CascBranches om{}; - GenBranches xiGen{}; - GenBranches omGen{}; -}; - -inline void connectBranches(TTree* t, CascBranches* b) -{ - t->Branch("pt", &b->pt); - t->Branch("rap", &b->rap); - t->Branch("mass", &b->mass); - t->Branch("sign", &b->sign); - t->Branch("cascCosPA", &b->cascCosPA); - t->Branch("v0CosPA", &b->v0CosPA); - t->Branch("cascRadius", &b->cascRadius); - t->Branch("v0Radius", &b->v0Radius); - t->Branch("dcaV0Dau", &b->dcaV0Dau); - t->Branch("dcaCascDau", &b->dcaCascDau); - t->Branch("dcaV0ToPV", &b->dcaV0ToPV); - t->Branch("dcaPosToPV", &b->dcaPosToPV); - t->Branch("dcaNegToPV", &b->dcaNegToPV); - t->Branch("dcaBachToPV", &b->dcaBachToPV); - t->Branch("cent", &b->cent); - t->Branch("pvZ", &b->pvZ); - t->Branch("pdgCode", &b->pdgCode); - t->Branch("isPhysPrim", &b->isPhysPrim); -} - -inline void connectGenBranches(TTree* t, GenBranches* b) -{ - t->Branch("pt", &b->pt); - t->Branch("rap", &b->rap); - t->Branch("pdgCode", &b->pdgCode); - t->Branch("isPhysPrim", &b->isPhysPrim); - t->Branch("cent", &b->cent); - t->Branch("pvZ", &b->pvZ); -} -} // namespace lxicorr - -struct LambdaXiCorrelation { - - // --- Configurables --- - Configurable maxY{"maxY", 0.5, "Max |y| for Lambda, Xi and Omega"}; - Configurable useEff{"useEff", false, "Apply Lambda efficiency correction"}; - // Omega bachelor kaon PID cut (kept separate from Xi pion cut for physics correctness) - Configurable tpcNsigmaBachKaon{"tpcNsigmaBachKaon", 3.0f, "TPC NSigma bachelor kaon (Omega selection)"}; - // FT0M centrality bins — same variable-width defaults as LambdaR2Correlation - ConfigurableAxis centAxis{"centAxis", {VARIABLE_WIDTH, 0.0f, 10.0f, 30.0f, 50.0f, 80.0f, 100.0f}, "FT0M centrality (%)"}; - Configurable saveCascTree{"saveCascTree", false, "Save TTree of cascade topological variables into AnalysisResults.root"}; - - // --- Outputs --- - HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Direct OutputObj members → framework registers and writes these into - // AnalysisResults.root under the "CascadeTrees" folder automatically. - // setObject() is called in init() when saveCascTree is true. - OutputObj treeXi{"XiCandidates", OutputObjHandlingPolicy::AnalysisObject}; - OutputObj treeOmega{"OmegaCandidates", OutputObjHandlingPolicy::AnalysisObject}; - OutputObj treeXiGen{"XiCandidatesGen", OutputObjHandlingPolicy::AnalysisObject}; - OutputObj treeOmegaGen{"OmegaCandidatesGen", OutputObjHandlingPolicy::AnalysisObject}; - // Single raw pointer covers all four branch sets — 1 StructToTuple slot - lxicorr::BranchPair* bp{nullptr}; - // --- Data Slicing Definitions --- using GoodLambdas = soa::Join; Partition goodLambda = aod::lambdatrackext::trueLambdaFlag == true; + // Primary-only Λ partition (MC-meaningful; on data v0PrmScd is + // always kPrimary so the partition coincides with goodLambda). + Partition goodPrimaryLambda = + aod::lambdatrackext::trueLambdaFlag == true && + aod::lambdatrack::v0PrmScd == static_cast(kPrimary); + + // Topology-only primary-Λ partition. Combine with the truth + // partition to get the strictest trigger list: AND of (trueLambdaFlag, + // v0PrmScd==kPrimary, passesPrimaryTopo). On data the v0PrmScd term is + // identically true so the partition reduces to (trueLambdaFlag && + // passesPrimaryTopo) — i.e., topology alone, which is the only handle + // available without truth. + Partition goodPureLambda = + aod::lambdatrackext::trueLambdaFlag == true && + aod::lambdatrack::v0PrmScd == static_cast(kPrimary) && + aod::lambdatrack::passesPrimaryTopo == true; + + // Pick which "primary Λ" partition the trigger uses. + // true → goodPureLambda (truth AND topology; recommended for data) + // false → goodPrimaryLambda (truth-only; use for MC closure tests) + Configurable cPrimaryRequireTopo{"cPrimaryRequireTopo", true, + "Require passesPrimaryTopo on the trigger Λ list (data-friendly tightening)"}; + + // MC-only filter: reject cascades whose CascadeFlags::IsTrueCascade + // is false (i.e., the cascade was not truth-matched to a physical-primary + // Ξ⁻/Ω⁻). On data this flag is always false (no truth available), so the + // filter is automatically disabled in the data process functions — it only + // gates analyzeSinglesXi/Omega and analyzePairs/OmegaPairs when called via + // . Use this for closure/purity studies; turn off when measuring + // the residual combinatorial contamination of your topological selection. + Configurable cRequireTrueCascade{"cRequireTrueCascade", true, + "MC: drop cascade candidates not flagged IsTrueCascade==true"}; + + // ITS strangeness-tracking gating policy (A/B-compare without recompiling): + // 0 = OFF — ignore IsItsTracked (default behaviour). + // 1 = REQUIRE — accept only IsItsTracked cascades (high purity, low stats). + // 2 = RESCUE — keep purity-gate failures IF IsItsTracked confirms them. + // 3 = QA-ONLY — no gating; QA histos only. + // QA histograms are filled in every mode. + Configurable cItsTrackMode{"cItsTrackMode", 0, + "ITS strangeness-tracking gate: 0=off, 1=require, 2=rescue, 3=qa-only"}; + + // Trigger-pT-differential pair histograms (off by default to + // avoid bloating output for the centrality-only analyses). + Configurable cFillPtDifferentialPairs{"cFillPtDifferentialPairs", false, + "Fill PairsPt//hPtDeltaPhiDeltaY (3D pT_Λ × Δφ × Δy)"}; + + // Pair-type selectors. Each toggle gates BOTH the histogram + // booking and the fill, so disabled pair types cost zero memory and + // zero CPU. Defaults: the standard Λ-Ξ / Λ-Ω physics + Λ-Λ on; the + // same-species cascade pairs and the Ξ-Ω cross-species pairs are off + // until you ask for them. + struct : ConfigurableGroup { + Configurable cFillLamXi{"cFillLamXi", true, "Λ–Ξ pairs (4 sign combos)"}; + Configurable cFillLamOm{"cFillLamOm", true, "Λ–Ω pairs (4 sign combos)"}; + Configurable cFillLamLam{"cFillLamLam", true, "Λ–Λ same-event pairs (3 sign combos)"}; + Configurable cFillXiXi{"cFillXiXi", false, "Ξ–Ξ same-event pairs (3 sign combos)"}; + Configurable cFillOmOm{"cFillOmOm", false, "Ω–Ω same-event pairs (3 sign combos)"}; + Configurable cFillXiOm{"cFillXiOm", false, "Ξ–Ω cross-species pairs (4 sign combos)"}; + } pairCfg; + + // Per-event yields. One toggle, one ConfigurableAxis bundle. + // Each species gets 5 histograms under Yields//: + // hNPerEvent — multiplicity distribution (TH1F) → ⟨N_β⟩ = Mean + // hMeanPtPerEvent — per-event ⟨pT⟩ distribution (TH1F) → ⟨pT⟩ = Mean + // hMeanNvsCent — TProfile ⟨N_β⟩ vs centrality + // hMeanPtVsCent — TProfile ⟨pT⟩ vs centrality + // hNvsPt2D — (N, ⟨pT⟩) per event (TH2F) (correlation) + // Filled exclusively by the dedicated processYields function so that the + // count is correct regardless of which pair-process functions are on. + struct : ConfigurableGroup { + Configurable cFillEventYields{"cFillEventYields", true, "Per-event multiplicity + ⟨pT⟩ histograms for each species"}; + ConfigurableAxis cYieldNAxis{"cYieldNAxis", {50, 0., 50.}, "N per event"}; + ConfigurableAxis cYieldPtAxis{"cYieldPtAxis", {100, 0., 10.}, "⟨pT⟩ per event (GeV/c)"}; + } yieldCfg; Preslice lambdasPerCollision = aod::lambdatrack::lambdaCollisionId; Preslice cascadesPerCollision = aod::cascdata::collisionId; Preslice labeledCascPerCollision = aod::cascdata::collisionId; + + // Single source of truth for the trigger-Λ partition pick. + // Replaces a 4-line ternary that was duplicated verbatim in each of the + // four process functions (processXi/Omega/MCRecoXi/MCRecoOmega). The + // partitions all slice to the same SOA type, so a single auto-returning + // helper is sufficient. + template + auto pickLambdaPartition(TColl const& lambdacoll) + { + if (!cUsePrimaryLambdasOnly) + return goodLambda->sliceBy(lambdasPerCollision, lambdacoll.globalIndex()); + return cPrimaryRequireTopo + ? goodPureLambda->sliceBy(lambdasPerCollision, lambdacoll.globalIndex()) + : goodPrimaryLambda->sliceBy(lambdasPerCollision, lambdacoll.globalIndex()); + } + + // Per-cascade purity gate: combines truth (MC) + ITS-tracking + // mode logic that was duplicated in 4 analyze functions. Returns true if + // the cascade should be KEPT for filling. IsMC template param compiles + // away the truth check on the data path. + template + bool passesCascadePurityGate(TFlagsRow const& fr) + { + bool truthOk = true; + if constexpr (IsMC) { + truthOk = !cRequireTrueCascade || fr.isTrueCascade(); + } + bool itsRescue = (cItsTrackMode == lcorr_const::kItsTrackModeRescue && fr.isItsTracked()); + if (!truthOk && !itsRescue) + return false; + if (cItsTrackMode == lcorr_const::kItsTrackModeRequired && !fr.isItsTracked()) + return false; + return true; + } // Gen-level lambda slicing: use HashBy (unsorted-safe) via o2::framework::expressions // Simple approach: iterate all and match by collision index inline (no cache needed) @@ -2952,6 +3441,19 @@ struct LambdaXiCorrelation { // so |pdg|==3312 AND |pdg|==3334 would always be false.) Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; + // Cascade efficiency weight: returns 1/eff for the pair weight + // (w = wLam * wCasc). Default returns 1.0 (no correction); load a CCDB + // efficiency map here when available. kKind: 0 = Xi, 1 = Omega. + // Usage: getCascadeEfficiency<0>(casc.sign(), casc.pt(), xiY). + template + float getCascadeEfficiency(int /*sign*/, float /*pt*/, float /*rap*/) + { + if (!cUseCascEff) + return 1.0f; + // TODO(oveis): load from CCDB and compute weight when an efficiency map exists. + return 1.0f; + } + // --- R2 Calculation Helper --- // R2 = (N_events * Pair_Yield) / (Single_Yield_1 * Single_Yield_2) - 1 static TH2* calculateR2(TH2* hPairs, TH1* hSinglesTrig, TH1* hSinglesAssoc, double nEvents) @@ -2959,7 +3461,7 @@ struct LambdaXiCorrelation { if (!hPairs || !hSinglesTrig || !hSinglesAssoc || nEvents <= 0) return nullptr; - TH2* hR2 = reinterpret_cast(hPairs->Clone(Form("%s_R2", hPairs->GetName()))); + auto* hR2 = dynamic_cast(hPairs->Clone(Form("%s_R2", hPairs->GetName()))); hR2->Reset(); double nS1 = hSinglesTrig->Integral(); @@ -2981,21 +3483,120 @@ struct LambdaXiCorrelation { void init(InitContext const&) { + // [DEBUG] Echo the active configuration at startup. Search log for "[CFG-LXi]". + LOGF(info, + "[CFG-LXi] |y|<=%.2f useEff=%d useCascEff=%d tpcNsigmaBachKaon=%.2f " + "vetoMode=%d primOnly=%d primReqTopo=%d reqTrueCasc=%d itsMode=%d " + "saveCascTree=%d saveLambdaTree=%d " + "pairs[LamXi=%d LamOm=%d LamLam=%d XiXi=%d OmOm=%d XiOm=%d] yields=%d " + "(processXi=%d processOmega=%d processMCRecoXi=%d processMCRecoOmega=%d " + "processAllPairs=%d processYields=%d " + "processMCGenXi=%d processMCGenOmega=%d)", + static_cast(maxY), static_cast(useEff), static_cast(cUseCascEff), static_cast(tpcNsigmaBachKaon), + static_cast(cVetoMode), static_cast(cUsePrimaryLambdasOnly), + static_cast(cPrimaryRequireTopo), static_cast(cRequireTrueCascade), static_cast(cItsTrackMode), + static_cast(saveCascTree), static_cast(saveLambdaTree), + static_cast(pairCfg.cFillLamXi), static_cast(pairCfg.cFillLamOm), static_cast(pairCfg.cFillLamLam), + static_cast(pairCfg.cFillXiXi), static_cast(pairCfg.cFillOmOm), static_cast(pairCfg.cFillXiOm), + static_cast(yieldCfg.cFillEventYields), + static_cast(doprocessXi), static_cast(doprocessOmega), + static_cast(doprocessMCRecoXi), static_cast(doprocessMCRecoOmega), + static_cast(doprocessAllPairs), static_cast(doprocessYields), + static_cast(doprocessMCGenXi), static_cast(doprocessMCGenOmega)); + + // Three-way veto policy. Loud warning when the user disables + // it or picks the loose mode for a systematic study. + if (cVetoMode == lcorr_const::kVetoModeOff) { + LOGF(warning, + "[LXi] cVetoMode=0 (off): pairs whose Λ shares V0 daughter tracks " + "with the cascade's V0 will be KEPT. This produces a fake self-Λ " + "peak around (Δφ≈0, Δy≈0). Use only for systematic studies."); + } else if (cVetoMode == lcorr_const::kVetoModeLoose) { + LOGF(info, + "[LXi] cVetoMode=2 (loose): pairs vetoed when EITHER daughter is " + "shared with the cascade's V0. Stricter than mode 1 — use to " + "estimate the impact of partial-share contamination."); + } else if (cVetoMode != lcorr_const::kVetoModeStrict) { + LOGF(warning, + "[LXi] cVetoMode=%d is not one of {0,1,2}; treating as 0 (off).", + static_cast(cVetoMode)); + } + // Legacy cVetoSharedDau is parsed for back-compat but not used. + // If the user set it to false on the assumption it'd disable the veto, + // remind them to migrate. + if (!cVetoSharedDau) { + LOGF(warning, + "[LXi] cVetoSharedDau=false is DEPRECATED and ignored at runtime. " + "To disable the veto, set cVetoMode=0 instead. Current cVetoMode=%d.", + static_cast(cVetoMode)); + } + + // Data-side primary tag is not informative. + if (doprocessXi || doprocessOmega) { + LOGF(warning, + "[LXi] Running on DATA process functions: the Λ row's v0PrmScd " + "field is unconditionally kPrimary regardless of physics origin " + "(see LambdaCascadeProducer::fillLambdaRecoTables). Treat any " + "primary/secondary partitioning as meaningful only in MC."); + // Loud reminder when the user wires primary-only on data. + if (cUsePrimaryLambdasOnly) { + LOGF(warning, + "[LXi]cUsePrimaryLambdasOnly=true on data: the partition " + "filters on v0PrmScd==kPrimary, but on data EVERY Λ has that tag. " + "The trigger pool is therefore identical to the all-Λ pool. To " + "actually enrich primaries on data, tighten the topological cuts " + "in lambda-cascade-producer (cMaxDcaV0ToPV ~ 0.3-0.5 cm, " + "cMinV0CosPA ~ 0.999) — those are not yet aggressive in your JSON."); + } + } + + // Warnings for known foot-guns until the deeper fixes land. + if (doprocessXi && doprocessOmega) { + LOGF(warning, + "[LXi] Both processXi and processOmega are enabled. Until the " + "single-pass refactor, Lambda singles will be filled " + "TWICE per event. Disable one of them, or disable one."); + } + // Efficiency map sanity (P9): numerator and denominator come from + // different process functions; warn if only one side is active. + if (doprocessMCRecoXi && !doprocessMCGenXi) { + LOGF(warning, + "[LXi] processMCRecoXi is on but processMCGenXi is off; the Xi " + "efficiency Gen denominator will be empty."); + } + if (doprocessMCGenXi && !doprocessMCRecoXi) { + LOGF(warning, + "[LXi] processMCGenXi is on but processMCRecoXi is off; the Xi " + "efficiency Reco numerator will be empty."); + } + if (doprocessMCRecoOmega && !doprocessMCGenOmega) { + LOGF(warning, + "[LXi] processMCRecoOmega is on but processMCGenOmega is off; the " + "Omega efficiency Gen denominator will be empty."); + } + if (doprocessMCGenOmega && !doprocessMCRecoOmega) { + LOGF(warning, + "[LXi] processMCGenOmega is on but processMCRecoOmega is off; the " + "Omega efficiency Reco numerator will be empty."); + } + // --- 1. Axis Definitions --- - const AxisSpec dphi{72, -PIHalf, 3 * PIHalf, "#Delta#varphi"}; - const AxisSpec dy{40, -2.0f, 2.0f, "#Delta y"}; + // Pair / single axes now come from the histAxes ConfigurableGroup + // — change binning via JSON, no recompile. + const AxisSpec dphi{histAxes.cDphiAxis, "#Delta#varphi"}; + const AxisSpec dy{histAxes.cDyAxis, "#Delta y"}; const AxisSpec cent{centAxis, "FT0M (%)"}; - const AxisSpec pt{100, 0, 10, "p_{T} (GeV/c)"}; - const AxisSpec rap{100, -1.0, 1.0, "y"}; - const AxisSpec massLam{100, 1.09, 1.14, "M_{p#pi} (GeV/c^{2})"}; - const AxisSpec massXi{100, 1.28, 1.36, "M_{#Lambda#pi} (GeV/c^{2})"}; - const AxisSpec massOm{100, 1.62, 1.72, "M_{#LambdaK} (GeV/c^{2})"}; + const AxisSpec pt{histAxes.cPtAxisPair, "p_{T} (GeV/c)"}; + const AxisSpec rap{histAxes.cRapAxis, "y"}; + const AxisSpec massLam{histAxes.cMassLamAxis, "M_{p#pi} (GeV/c^{2})"}; + const AxisSpec massXi{histAxes.cMassXiAxis, "M_{#Lambda#pi} (GeV/c^{2})"}; + const AxisSpec massOm{histAxes.cMassOmAxis, "M_{#LambdaK} (GeV/c^{2})"}; const AxisSpec radius{100, 0, 100, "Radius (cm)"}; const AxisSpec cpa{100, 0.9, 1.0, "Cos(PA)"}; const AxisSpec dca{100, 0.0, 5.0, "DCA (cm)"}; const AxisSpec pvDca{100, -10.0, 10.0, "DCA to PV (cm)"}; - // Fixed AxisSpec (not ConfigurableAxis) so bin edges cannot be misconfigured at runtime - const AxisSpec tpcRows{160, -0.5, 159.5, "TPC crossed rows"}; + // tpcRows AxisSpec removed alongside the unfilled QA/Casc/hTPCRows + // bookings; will return when the analyser carries tracks. // --- 2. Histograms --- histos.add("Event/hEventCount", "Event Counter", kTH1F, {{1, 0, 1, "Count"}}); @@ -3007,6 +3608,11 @@ struct LambdaXiCorrelation { histos.add("Singles/Lambda/hPtVsMass", "Lambda p_{T} vs Mass", kTH2F, {massLam, pt}); histos.add("Singles/AntiLambda/hPtVsMass", "AntiLambda p_{T} vs Mass", kTH2F, {massLam, pt}); + // 2D (y, φ) singles — input to ρ₁⊗ρ₁ for the offline R₂ recipe. + const AxisSpec phi{histAxes.cPhiAxis, "φ (rad)"}; + histos.add("Singles/Lambda/hYPhi", "Lambda (y, φ)", kTH2F, {rap, phi}); + histos.add("Singles/AntiLambda/hYPhi", "AntiLambda (y, φ)", kTH2F, {rap, phi}); + // Singles: Xi & QA histos.add("QA/Xi/hRadius", "Xi Radius", kTH1F, {radius}); histos.add("QA/Xi/hCosPA", "Xi CosPA", kTH1F, {cpa}); @@ -3017,16 +3623,20 @@ struct LambdaXiCorrelation { histos.add("QA/Xi/hDCANegToPV", "DCA Neg to PV", kTH1F, {pvDca}); histos.add("QA/Xi/hDCABachToPV", "DCA Bach to PV", kTH1F, {pvDca}); - // Cascade track QA — TPC crossed rows per daughter (fixed AxisSpec, immune to config override) - histos.add("QA/Casc/hTPCRowsPos", "TPC crossed rows (pos)", kTH1F, {tpcRows}); - histos.add("QA/Casc/hTPCRowsNeg", "TPC crossed rows (neg)", kTH1F, {tpcRows}); - histos.add("QA/Casc/hTPCRowsBach", "TPC crossed rows (bach)", kTH1F, {tpcRows}); + // QA/Casc/hTPCRows{Pos,Neg,Bach} were booked here but never filled. + // To fill them properly we need track access (FullTracksExtIUWithPID) + // inside the analyser, which processXi does not currently take. They are + // re-introduced once the analyser carries the tracks template. histos.add("Singles/XiMinus/hPtVsMass", "Xi^{-} p_{T} vs Mass", kTH2F, {massXi, pt}); histos.add("Singles/XiPlus/hPtVsMass", "Xi^{+} p_{T} vs Mass", kTH2F, {massXi, pt}); histos.add("Singles/XiMinus/hRap", "Xi^{-} Rapidity", kTH1F, {rap}); histos.add("Singles/XiPlus/hRap", "Xi^{+} Rapidity", kTH1F, {rap}); + // 2D (y, φ) singles for Ξ. + histos.add("Singles/XiMinus/hYPhi", "Xi^{-} (y, φ)", kTH2F, {rap, phi}); + histos.add("Singles/XiPlus/hYPhi", "Xi^{+} (y, φ)", kTH2F, {rap, phi}); + // --- Omega QA --- histos.add("QA/Om/hRadius", "Omega Radius", kTH1F, {radius}); histos.add("QA/Om/hCosPA", "Omega CosPA", kTH1F, {cpa}); @@ -3043,17 +3653,116 @@ struct LambdaXiCorrelation { histos.add("Singles/OmegaMinus/hRap", "Omega^{-} Rapidity", kTH1F, {rap}); histos.add("Singles/OmegaPlus/hRap", "Omega^{+} Rapidity", kTH1F, {rap}); - // Pairs: Xi (R2 inputs) - histos.add("Pairs/Lam_XiM/hDeltaPhiDeltaY", "L-Xi-", kTH3F, {cent, dphi, dy}); - histos.add("Pairs/Lam_XiP/hDeltaPhiDeltaY", "L-Xi+", kTH3F, {cent, dphi, dy}); - histos.add("Pairs/AntiLam_XiM/hDeltaPhiDeltaY", "AL-Xi-", kTH3F, {cent, dphi, dy}); - histos.add("Pairs/AntiLam_XiP/hDeltaPhiDeltaY", "AL-Xi+", kTH3F, {cent, dphi, dy}); + // 2D (y, φ) singles for Ω. + histos.add("Singles/OmegaMinus/hYPhi", "Omega^{-} (y, φ)", kTH2F, {rap, phi}); + histos.add("Singles/OmegaPlus/hYPhi", "Omega^{+} (y, φ)", kTH2F, {rap, phi}); + + // All pair-type bookings gated by pairCfg toggles. + // Pairs: Λ–Ξ (R2 inputs) + if (pairCfg.cFillLamXi) { + histos.add("Pairs/Lam_XiM/hDeltaPhiDeltaY", "L-Xi-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/Lam_XiP/hDeltaPhiDeltaY", "L-Xi+", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/AntiLam_XiM/hDeltaPhiDeltaY", "AL-Xi-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/AntiLam_XiP/hDeltaPhiDeltaY", "AL-Xi+", kTH3F, {cent, dphi, dy}); + } + + // Pairs: Λ–Ω (R2 inputs) + if (pairCfg.cFillLamOm) { + histos.add("Pairs/Lam_OmM/hDeltaPhiDeltaY", "L-Om-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/Lam_OmP/hDeltaPhiDeltaY", "L-Om+", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/AntiLam_OmM/hDeltaPhiDeltaY", "AL-Om-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/AntiLam_OmP/hDeltaPhiDeltaY", "AL-Om+", kTH3F, {cent, dphi, dy}); + } + + // Pairs: Λ–Λ same-event (3 sign combos) + if (pairCfg.cFillLamLam) { + histos.add("Pairs/Lam_Lam/hDeltaPhiDeltaY", "L-L", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/Lam_AntiLam/hDeltaPhiDeltaY", "L-AL", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/AntiLam_AntiLam/hDeltaPhiDeltaY", "AL-AL", kTH3F, {cent, dphi, dy}); + } + + // Pairs: Ξ–Ξ same-event (3 sign combos) + if (pairCfg.cFillXiXi) { + histos.add("Pairs/XiM_XiM/hDeltaPhiDeltaY", "Xi--Xi-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/XiM_XiP/hDeltaPhiDeltaY", "Xi--Xi+", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/XiP_XiP/hDeltaPhiDeltaY", "Xi+-Xi+", kTH3F, {cent, dphi, dy}); + } + + // Pairs: Ω–Ω same-event (3 sign combos) + if (pairCfg.cFillOmOm) { + histos.add("Pairs/OmM_OmM/hDeltaPhiDeltaY", "Om--Om-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/OmM_OmP/hDeltaPhiDeltaY", "Om--Om+", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/OmP_OmP/hDeltaPhiDeltaY", "Om+-Om+", kTH3F, {cent, dphi, dy}); + } + + // Pairs: Ξ–Ω cross-species (4 sign combos) + if (pairCfg.cFillXiOm) { + histos.add("Pairs/XiM_OmM/hDeltaPhiDeltaY", "Xi--Om-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/XiM_OmP/hDeltaPhiDeltaY", "Xi--Om+", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/XiP_OmM/hDeltaPhiDeltaY", "Xi+-Om-", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/XiP_OmP/hDeltaPhiDeltaY", "Xi+-Om+", kTH3F, {cent, dphi, dy}); + } + + // Like-sign / opposite-sign baryon-number combined pair + // histograms. The four sign combos above split into: + // LS_LamXi (BB or B̄B̄) = Lam_XiM + AntiLam_XiP + // OS_LamXi (BB̄ or B̄B) = Lam_XiP + AntiLam_XiM + // (same for Λ-Ω) + // Filled in the pair loop alongside the four-way split, so users can + // do baryon-number correlation studies without offline summing. + histos.add("Pairs/LS_LamXi/hDeltaPhiDeltaY", "LS Λ-Ξ", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/OS_LamXi/hDeltaPhiDeltaY", "OS Λ-Ξ", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/LS_LamOm/hDeltaPhiDeltaY", "LS Λ-Ω", kTH3F, {cent, dphi, dy}); + histos.add("Pairs/OS_LamOm/hDeltaPhiDeltaY", "OS Λ-Ω", kTH3F, {cent, dphi, dy}); + + // Per-trigger-pT pair histograms (3D: pT_Λ × Δφ × Δy). + // Centrality is integrated here to keep the storage manageable. Fill + // gated by cFillPtDifferentialPairs so users opt in only when needed. + if (cFillPtDifferentialPairs) { + histos.add("PairsPt/Lam_XiM/hPtDeltaPhiDeltaY", "L-Xi- (pT_L, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + histos.add("PairsPt/Lam_XiP/hPtDeltaPhiDeltaY", "L-Xi+ (pT_L, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + histos.add("PairsPt/AntiLam_XiM/hPtDeltaPhiDeltaY", "AL-Xi- (pT_AL, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + histos.add("PairsPt/AntiLam_XiP/hPtDeltaPhiDeltaY", "AL-Xi+ (pT_AL, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + histos.add("PairsPt/Lam_OmM/hPtDeltaPhiDeltaY", "L-Om- (pT_L, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + histos.add("PairsPt/Lam_OmP/hPtDeltaPhiDeltaY", "L-Om+ (pT_L, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + histos.add("PairsPt/AntiLam_OmM/hPtDeltaPhiDeltaY", "AL-Om- (pT_AL, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + histos.add("PairsPt/AntiLam_OmP/hPtDeltaPhiDeltaY", "AL-Om+ (pT_AL, Δφ, Δy)", kTH3F, {pt, dphi, dy}); + } - // Pairs: Omega (R2 inputs) - histos.add("Pairs/Lam_OmM/hDeltaPhiDeltaY", "L-Om-", kTH3F, {cent, dphi, dy}); - histos.add("Pairs/Lam_OmP/hDeltaPhiDeltaY", "L-Om+", kTH3F, {cent, dphi, dy}); - histos.add("Pairs/AntiLam_OmM/hDeltaPhiDeltaY", "AL-Om-", kTH3F, {cent, dphi, dy}); - histos.add("Pairs/AntiLam_OmP/hDeltaPhiDeltaY", "AL-Om+", kTH3F, {cent, dphi, dy}); + // Auto-correlation bookkeeping: per-event distributions of total + // pair count vs vetoed (shared-daughter) pair count. Compare the two + // to estimate the bias of running without the veto. + const AxisSpec axisPairCount{200, 0., 200., "pairs/event"}; + histos.add("QA/AutoCorr/hXiPairsTotal", "Λ-Ξ total pairs/event", kTH1F, {axisPairCount}); + histos.add("QA/AutoCorr/hXiPairsVetoed", "Λ-Ξ vetoed pairs/event", kTH1F, {axisPairCount}); + histos.add("QA/AutoCorr/hOmPairsTotal", "Λ-Ω total pairs/event", kTH1F, {axisPairCount}); + histos.add("QA/AutoCorr/hOmPairsVetoed", "Λ-Ω vetoed pairs/event", kTH1F, {axisPairCount}); + + // ITS strangeness-tracking diagnostics. Filled regardless + // of cItsTrackMode so the comparison is always available downstream. + // X axis: 0 = topology-passing cascade, 1 = also IsItsTracked. + // Reading: bin 1 / bin 0 = ITS-tracking efficiency × "real Ξ" purity. + const AxisSpec axisItsTrackBin{2, -0.5, 1.5, "ITS-tracked"}; + const AxisSpec axisCascPt{50, 0, 10, "p_{T} (GeV/c)"}; + histos.add("QA/ItsTrack/hXiTotalVsTracked", "Ξ topology-pass vs ITS-tracked", kTH2F, {axisItsTrackBin, axisCascPt}); + histos.add("QA/ItsTrack/hOmegaTotalVsTracked", "Ω topology-pass vs ITS-tracked", kTH2F, {axisItsTrackBin, axisCascPt}); + + // Per-event yield histograms — one block per species, + // 5 histos each: hNPerEvent, hMeanPtPerEvent, hMeanNvsCent, + // hMeanPtVsCent, hNvsPt2D. Filled exclusively by processYields so + // counts are correct regardless of which pair-process functions run. + if (yieldCfg.cFillEventYields) { + const AxisSpec axisYieldN{yieldCfg.cYieldNAxis, "N per event"}; + const AxisSpec axisYieldPt{yieldCfg.cYieldPtAxis, "⟨p_{T}⟩ per event (GeV/c)"}; + const std::array species{"Lambda", "AntiLambda", "XiMinus", "XiPlus", "OmegaMinus", "OmegaPlus"}; + for (const auto& sp : species) { + histos.add(Form("Yields/%s/hNPerEvent", sp), Form("%s N/event", sp), kTH1F, {axisYieldN}); + histos.add(Form("Yields/%s/hMeanPtPerEvent", sp), Form("%s ⟨pT⟩/event", sp), kTH1F, {axisYieldPt}); + histos.add(Form("Yields/%s/hMeanNvsCent", sp), Form("%s ⟨N⟩ vs cent", sp), kTProfile, {cent}); + histos.add(Form("Yields/%s/hMeanPtVsCent", sp), Form("%s ⟨pT⟩ vs cent", sp), kTProfile, {cent}); + histos.add(Form("Yields/%s/hNvsPt2D", sp), Form("%s (N, ⟨pT⟩)", sp), kTH2F, {axisYieldN, axisYieldPt}); + } + } // --- MC Gen-level histograms (mirror reco under McGen/) --- // Singles: gen Lambda @@ -3068,6 +3777,19 @@ struct LambdaXiCorrelation { histos.add("McGen/Singles/OmegaMinus/hPtVsRap", "Gen #Omega^{-} p_{T} vs y", kTH2F, {rap, pt}); histos.add("McGen/Singles/OmegaPlus/hPtVsRap", "Gen #Omega^{+} p_{T} vs y", kTH2F, {rap, pt}); + // Closure-test inputs: 2D (y, φ) singles for the ρ₁⊗ρ₁ + // convolution. compute_r2.C consumes these via the --gen switch. + // Mirrors the reco-side Singles/.../hYPhi histograms used in production. + histos.add("McGen/Singles/Lambda/hYPhi", "Gen #Lambda (y, #varphi)", kTH2F, {rap, phi}); + histos.add("McGen/Singles/AntiLambda/hYPhi", "Gen #bar{#Lambda} (y, #varphi)", kTH2F, {rap, phi}); + histos.add("McGen/Singles/XiMinus/hYPhi", "Gen #Xi^{-} (y, #varphi)", kTH2F, {rap, phi}); + histos.add("McGen/Singles/XiPlus/hYPhi", "Gen #Xi^{+} (y, #varphi)", kTH2F, {rap, phi}); + histos.add("McGen/Singles/OmegaMinus/hYPhi", "Gen #Omega^{-} (y, #varphi)", kTH2F, {rap, phi}); + histos.add("McGen/Singles/OmegaPlus/hYPhi", "Gen #Omega^{+} (y, #varphi)", kTH2F, {rap, phi}); + + // Gen event counter (canonical denominator for gen-level R₂). + histos.add("McGen/Event/hEventCount", "Gen Event Counter", kTH1F, {{1, 0, 1, "Count"}}); + // Pairs: gen Lam-Xi histos.add("McGen/Pairs/Lam_XiM/hDeltaPhiDeltaY", "Gen L-Xi-", kTH3F, {cent, dphi, dy}); histos.add("McGen/Pairs/Lam_XiP/hDeltaPhiDeltaY", "Gen L-Xi+", kTH3F, {cent, dphi, dy}); @@ -3080,9 +3802,24 @@ struct LambdaXiCorrelation { histos.add("McGen/Pairs/AntiLam_OmM/hDeltaPhiDeltaY", "Gen AL-Om-", kTH3F, {cent, dphi, dy}); histos.add("McGen/Pairs/AntiLam_OmP/hDeltaPhiDeltaY", "Gen AL-Om+", kTH3F, {cent, dphi, dy}); + // Per-event coincidence counters: how often does a given + // collision contain BOTH a primary-Λ trigger AND a reco cascade? This + // is the dominant statistical bottleneck on small AODs (the pair loop + // only fires when both species coexist), so we instrument it directly. + // Read-off: + // bin(0,0) → events with neither species + // row > 0, col 0 → events with only Λ + // row 0, col > 0 → events with only cascade + // row > 0, col > 0→ events that contribute pair candidates + const AxisSpec axisCoincLam{31, -0.5, 30.5, "N_{primary Λ}"}; + const AxisSpec axisCoincXi{16, -0.5, 15.5, "N_{Ξ}"}; + const AxisSpec axisCoincOm{16, -0.5, 15.5, "N_{Ω}"}; + histos.add("Yields/Coincidence/hLamXi", "events: (N_Λ, N_Ξ)", kTH2F, {axisCoincLam, axisCoincXi}); + histos.add("Yields/Coincidence/hLamOm", "events: (N_Λ, N_Ω)", kTH2F, {axisCoincLam, axisCoincOm}); + // --- 3. Efficiency map production histograms --- // Axis definitions for efficiency maps (coarser binning to avoid empty bins) - const AxisSpec effCent{cent}; + const AxisSpec& effCent = cent; const AxisSpec effPt{50, 0, 10, "p_{T} (GeV/c)"}; const AxisSpec effRap{20, -1.0, 1.0, "y"}; @@ -3097,11 +3834,13 @@ struct LambdaXiCorrelation { histos.add("Eff/Reco/OmegaMinus/hCentPtRap", "Reco #Omega^{-}", kTH3F, {effCent, effPt, effRap}); histos.add("Eff/Reco/OmegaPlus/hCentPtRap", "Reco #Omega^{+}", kTH3F, {effCent, effPt, effRap}); - // TTree setup — only allocates when saveCascTree is enabled. - // OutputObj are direct task members, so the framework automatically - // writes them into AnalysisResults.root under their own folder. - if (saveCascTree) { + // TTree setup — only allocates when at least one of saveCascTree or + // saveLambdaTree is enabled. OutputObj are direct task members, + // so the framework automatically writes them into AnalysisResults.root. + if (saveCascTree || saveLambdaTree) { bp = new lxicorr::BranchPair{}; + } + if (saveCascTree) { treeXi.setObject(new TTree("XiCandidates", "Xi topological variables")); treeOmega.setObject(new TTree("OmegaCandidates", "Omega topological variables")); lxicorr::connectBranches(treeXi.object.get(), &bp->xi); @@ -3111,26 +3850,109 @@ struct LambdaXiCorrelation { lxicorr::connectGenBranches(treeXiGen.object.get(), &bp->xiGen); lxicorr::connectGenBranches(treeOmegaGen.object.get(), &bp->omGen); } + // Λ TTrees. + if (saveLambdaTree) { + treeLambda.setObject(new TTree("LambdaCandidates", "Λ standalone-V0 topo + truth")); + treeLambdaGen.setObject(new TTree("LambdaCandidatesGen", "Λ gen-level kinematics")); + lxicorr::connectLambdaBranches(treeLambda.object.get(), &bp->lam); + lxicorr::connectLambdaGenBranches(treeLambdaGen.object.get(), &bp->lamGen); + } } // --- Analysis Functions --- - template - void analyzeSinglesLambda(T const& tracks) + // Optional: a per-event PV used as the centVal/pvZ source for the + // Λ tree. We get these from the LambdaCollision the caller passes in. + template + void analyzeSinglesLambda(T const& tracks, C const& lambdacoll) { + // Idempotence guard — see mLastLamSinglesCollIdx declaration. + // Returns early on the 2nd+ call within the same event so that running + // both processMCRecoXi and processMCRecoOmega doesn't double-fill the + // Λ singles histograms or LambdaCandidates tree. + const int64_t thisCollIdx = lambdacoll.globalIndex(); + if (thisCollIdx == mLastLamSinglesCollIdx) { + return; + } + mLastLamSinglesCollIdx = thisCollIdx; + for (const auto& track : tracks) { if (std::abs(track.rap()) > maxY) continue; float w = useEff ? track.corrFact() : 1.0f; - bool isAnti = (track.v0Type() == 1); + // Use the typed enum instead of magic literal "1". + bool isAnti = (track.v0Type() == static_cast(kAntiLambda)); + // φ wrapped to [0, 2π) for ρ₁(y, φ) accumulation. + float phiWrapped = RecoDecay::constrainAngle(track.phi(), 0.f); if (!isAnti) { histos.fill(HIST("Singles/Lambda/hPt"), track.pt(), w); histos.fill(HIST("Singles/Lambda/hPtVsMass"), track.mass(), track.pt(), w); + histos.fill(HIST("Singles/Lambda/hYPhi"), track.rap(), phiWrapped, w); } else { histos.fill(HIST("Singles/AntiLambda/hPt"), track.pt(), w); histos.fill(HIST("Singles/AntiLambda/hPtVsMass"), track.mass(), track.pt(), w); + histos.fill(HIST("Singles/AntiLambda/hYPhi"), track.rap(), phiWrapped, w); + } + + // Λ TTree fill — must AND with saveLambdaTree because bp + // is shared with the cascade trees. With saveCascTree=true & saveLambdaTree=false + // bp is allocated but treeLambda is NEVER setObject-ed; reaching Fill() + // dereferences a null TTree and segfaults. + if (bp && saveLambdaTree) { + auto& b = bp->lam; + b.pt = track.pt(); + b.eta = track.eta(); + b.rap = track.rap(); + b.phi = track.phi(); + b.mass = track.mass(); + b.cosPA = track.cosPA(); + b.dcaDau = track.dcaDau(); + b.v0Type = static_cast(track.v0Type()); + b.v0PrmScd = static_cast(track.v0PrmScd()); + b.trueLambdaFlag = track.trueLambdaFlag(); + b.corrFact = track.corrFact(); + b.cent = lambdacoll.cent(); + b.pvZ = lambdacoll.posZ(); + b.motherPdg = track.motherPdg(); + // Populate pdgCode from the truth tag + v0Type so downstream + // selectors of the form `abs(pdgCode)==3122` work uniformly with the + // cascade trees. On data trueLambdaFlag falls back to topology, which + // makes pdgCode best-effort but never wrong: it is non-zero only when + // we have a real Λ-tagged candidate. + if (track.trueLambdaFlag()) { + b.pdgCode = (track.v0Type() == static_cast(kLambda)) + ? lcorr_const::kLambdaPdg + : -lcorr_const::kLambdaPdg; + } else { + b.pdgCode = 0; + } + b.isPhysPrim = (track.v0PrmScd() == static_cast(kPrimary)); + // Topology snapshot — primary-fraction template-fit inputs. + b.dcaV0ToPV = track.dcaV0ToPV(); + b.v0Radius = track.v0Radius(); + b.posItsNCls = static_cast(track.posItsNCls()); + b.negItsNCls = static_cast(track.negItsNCls()); + b.passesPrimaryTopo = track.passesPrimaryTopo(); + // ITS hit-map per daughter for downstream geometric checks. + b.posItsClusterMap = static_cast(track.posItsClusterMap()); + b.negItsClusterMap = static_cast(track.negItsClusterMap()); + // L_proper + per-daughter DCAs. + b.lProper = track.lProper(); + b.posDcaXY = track.posDcaXY(); + b.negDcaXY = track.negDcaXY(); + // cut bitmask + raw cut inputs. + b.cutBits = static_cast(track.cutBits()); + b.tpcNSigmaPosPr = track.tpcNSigmaPosPr(); + b.tpcNSigmaNegPi = track.tpcNSigmaNegPi(); + b.tpcNSigmaPosPi = track.tpcNSigmaPosPi(); + b.tpcNSigmaNegPr = track.tpcNSigmaNegPr(); + b.mK0Short = track.mK0Short(); + b.qtArm = track.qtArm(); + b.alphaArm = track.alphaArm(); + b.cTau = track.cTau(); + treeLambda->Fill(); } } } @@ -3139,10 +3961,29 @@ struct LambdaXiCorrelation { void analyzeSinglesXi(T const& cascades, F const& flagsStart, float pvX, float pvY, float pvZ, float centVal) { for (const auto& casc : cascades) { - if ((flagsStart + casc.globalIndex()).isSelected() == 0) + auto fr = flagsStart + casc.globalIndex(); + const int f = fr.isSelected(); + // Species filter — only Ξ-flagged cascades enter the Ξ + // singles / tree fill. Previously, the loop accepted ANY non-rejected + // cascade (flag 1/2/3), which meant flag==3 (Ω-only) candidates were + // silently classified as Ξ and their mass/rapidity projected under the + // wrong mass hypothesis polluted XiCandidates. + if (f != kFlagXiOnly && f != kFlagXiAndOmega) + continue; + + // QA: count topology-passers and the ITS-tracked subset. + // Filled BEFORE any further gating so the ratio is meaningful. + histos.fill(HIST("QA/ItsTrack/hXiTotalVsTracked"), 0., casc.pt()); + if (fr.isItsTracked()) + histos.fill(HIST("QA/ItsTrack/hXiTotalVsTracked"), 1., casc.pt()); + + // Centralised purity gate (truth + ITS-tracking modes 1/2). + if (!passesCascadePurityGate(fr)) continue; - float xiY = RecoDecay::y(std::array{casc.px(), casc.py(), casc.pz()}, MassXi0); + // Cascade row's own rapidity uses the correct charged-Xi mass. + // Avoid recomputing with MassXi0 (the neutral-cascade mass). + float xiY = casc.yXi(); if (std::abs(xiY) > maxY) continue; @@ -3156,20 +3997,24 @@ struct LambdaXiCorrelation { histos.fill(HIST("QA/Xi/hDCANegToPV"), casc.dcanegtopv()); histos.fill(HIST("QA/Xi/hDCABachToPV"), casc.dcabachtopv()); + // φ wrapped to [0, 2π) for ρ₁(y, φ) accumulation. + float xiPhiWrapped = RecoDecay::constrainAngle(casc.phi(), 0.f); if (casc.sign() < 0) { histos.fill(HIST("Singles/XiMinus/hPtVsMass"), casc.mXi(), casc.pt()); histos.fill(HIST("Singles/XiMinus/hRap"), xiY); + histos.fill(HIST("Singles/XiMinus/hYPhi"), xiY, xiPhiWrapped); } else { histos.fill(HIST("Singles/XiPlus/hPtVsMass"), casc.mXi(), casc.pt()); histos.fill(HIST("Singles/XiPlus/hRap"), xiY); + histos.fill(HIST("Singles/XiPlus/hYPhi"), xiY, xiPhiWrapped); } // MC: fill efficiency numerator for truth-matched primary Xi if constexpr (IsMC) { if (casc.has_mcParticle()) { auto mcpart = casc.mcParticle(); - if (std::abs(mcpart.pdgCode()) == 3312 && mcpart.isPhysicalPrimary()) { - if (mcpart.pdgCode() == 3312) + if (std::abs(mcpart.pdgCode()) == lcorr_const::kXiMinusPdg && mcpart.isPhysicalPrimary()) { + if (mcpart.pdgCode() == lcorr_const::kXiMinusPdg) histos.fill(HIST("Eff/Reco/XiMinus/hCentPtRap"), centVal, casc.pt(), xiY); else histos.fill(HIST("Eff/Reco/XiPlus/hCentPtRap"), centVal, casc.pt(), xiY); @@ -3177,7 +4022,9 @@ struct LambdaXiCorrelation { } } - if (bp) { + // Same gating issue as the Λ tree: bp is shared between + // cascade and Λ trees, so guard the Xi fill on saveCascTree explicitly. + if (bp && saveCascTree) { auto& b = bp->xi; b.pt = casc.pt(); b.rap = xiY; @@ -3209,6 +4056,14 @@ struct LambdaXiCorrelation { b.pdgCode = 0; b.isPhysPrim = false; } + // Per-cascade cut bitmask + bachelor PID inputs. + b.cascCutBits = static_cast(fr.cascCutBits()); + auto bachTrk = casc.template bachelor_as(); + b.bachTpcNSigmaPi = bachTrk.tpcNSigmaPi(); + b.bachTpcNSigmaKa = bachTrk.tpcNSigmaKa(); + b.bachItsNCls = static_cast(bachTrk.itsNCls()); + b.bachTpcNClsCrossedRows = static_cast(bachTrk.tpcNClsCrossedRows()); + b.mLambdaInside = casc.mLambda(); treeXi->Fill(); } } @@ -3219,10 +4074,27 @@ struct LambdaXiCorrelation { void analyzeSinglesOmega(T const& cascades, F const& flagsStart, float pvX, float pvY, float pvZ, float centVal) { for (const auto& casc : cascades) { - if ((flagsStart + casc.globalIndex()).isSelected() == 0) + auto fr = flagsStart + casc.globalIndex(); + const int f = fr.isSelected(); + // Species filter — only Ω-flagged cascades enter the Ω + // singles / tree fill. Previously, the loop accepted any non-rejected + // cascade, so flag==1 (Ξ-only) candidates leaked in. The kaon nσ cut + // below filtered most of them, but a defensive flag gate is cheaper + // and cleaner. + if (f != kFlagOmegaOnly && f != kFlagXiAndOmega) continue; - float omY = RecoDecay::y(std::array{casc.px(), casc.py(), casc.pz()}, MassOmegaMinus); + // QA: total Ω topology-passers and the ITS-tracked subset. + histos.fill(HIST("QA/ItsTrack/hOmegaTotalVsTracked"), 0., casc.pt()); + if (fr.isItsTracked()) + histos.fill(HIST("QA/ItsTrack/hOmegaTotalVsTracked"), 1., casc.pt()); + + // Centralised purity gate. + if (!passesCascadePurityGate(fr)) + continue; + + // Cascade row's own rapidity (correct charged-Omega mass). + float omY = casc.yOmega(); if (std::abs(omY) > maxY) continue; @@ -3241,20 +4113,24 @@ struct LambdaXiCorrelation { histos.fill(HIST("QA/Om/hDCANegToPV"), casc.dcanegtopv()); histos.fill(HIST("QA/Om/hDCABachToPV"), casc.dcabachtopv()); + // φ wrapped to [0, 2π). + float omPhiWrapped = RecoDecay::constrainAngle(casc.phi(), 0.f); if (casc.sign() < 0) { histos.fill(HIST("Singles/OmegaMinus/hPtVsMass"), casc.mOmega(), casc.pt()); histos.fill(HIST("Singles/OmegaMinus/hRap"), omY); + histos.fill(HIST("Singles/OmegaMinus/hYPhi"), omY, omPhiWrapped); } else { histos.fill(HIST("Singles/OmegaPlus/hPtVsMass"), casc.mOmega(), casc.pt()); histos.fill(HIST("Singles/OmegaPlus/hRap"), omY); + histos.fill(HIST("Singles/OmegaPlus/hYPhi"), omY, omPhiWrapped); } // MC: fill efficiency numerator for truth-matched primary Omega if constexpr (IsMC) { if (casc.has_mcParticle()) { auto mcpart = casc.mcParticle(); - if (std::abs(mcpart.pdgCode()) == 3334 && mcpart.isPhysicalPrimary()) { - if (mcpart.pdgCode() == 3334) + if (std::abs(mcpart.pdgCode()) == lcorr_const::kOmegaMinusPdg && mcpart.isPhysicalPrimary()) { + if (mcpart.pdgCode() == lcorr_const::kOmegaMinusPdg) histos.fill(HIST("Eff/Reco/OmegaMinus/hCentPtRap"), centVal, casc.pt(), omY); else histos.fill(HIST("Eff/Reco/OmegaPlus/hCentPtRap"), centVal, casc.pt(), omY); @@ -3262,7 +4138,8 @@ struct LambdaXiCorrelation { } } - if (bp) { + // Gate on saveCascTree (treeOmega is only setObject-ed when on). + if (bp && saveCascTree) { auto& b = bp->om; b.pt = casc.pt(); b.rap = omY; @@ -3294,92 +4171,239 @@ struct LambdaXiCorrelation { b.pdgCode = 0; b.isPhysPrim = false; } + // Per-cascade cut bitmask + bachelor PID inputs. + // These were assigned on the Xi tree path but forgotten on Ω, so + // OmegaCandidates branches cascCutBits / bach* / mLambdaInside + // were silently always 0. Now mirrors the Xi-side write block. + b.cascCutBits = static_cast(fr.cascCutBits()); + b.bachTpcNSigmaPi = bachTrack.tpcNSigmaPi(); + b.bachTpcNSigmaKa = bachTrack.tpcNSigmaKa(); + b.bachItsNCls = static_cast(bachTrack.itsNCls()); + b.bachTpcNClsCrossedRows = static_cast(bachTrack.tpcNClsCrossedRows()); + b.mLambdaInside = casc.mLambda(); treeOmega->Fill(); } } } - template + template void analyzePairs(L const& lambdas, C const& cascades, F const& flagsStart, float centVal) { + // Per-event auto-correlation bookkeeping for Λ-Ξ. + int nXiPairsTotal = 0; + int nXiPairsVetoed = 0; + for (const auto& lam : lambdas) { if (std::abs(lam.rap()) > maxY) continue; float wLam = useEff ? lam.corrFact() : 1.0f; - bool isAntiLam = (lam.v0Type() == 1); + // Use the typed enum instead of magic literal "1". + bool isAntiLam = (lam.v0Type() == static_cast(kAntiLambda)); for (const auto& casc : cascades) { - if ((flagsStart + casc.globalIndex()).isSelected() == 0) + auto fr = flagsStart + casc.globalIndex(); + const int f = fr.isSelected(); + // Species filter — only Ξ-flagged cascades pair as Ξ. + if (f != kFlagXiOnly && f != kFlagXiAndOmega) continue; - float xiY = RecoDecay::y(std::array{casc.px(), casc.py(), casc.pz()}, MassXi0); + // Centralised purity gate. + if (!passesCascadePurityGate(fr)) + continue; + + // Use the cascade row's own Xi rapidity (correct mass). + float xiY = casc.yXi(); if (std::abs(xiY) > maxY) continue; + ++nXiPairsTotal; + + // Three-way auto-correlation veto driven by cVetoMode: + // 1 (strict, default) — both pos AND neg daughters shared + // 2 (loose) — either pos OR neg daughter shared + // 0 (off) — keep all pairs (use only for systematic studies) + const bool posMatch = (lam.posTrackId() == casc.posTrackId()); + const bool negMatch = (lam.negTrackId() == casc.negTrackId()); + const bool veto = + (cVetoMode == lcorr_const::kVetoModeStrict && posMatch && negMatch) || + (cVetoMode == lcorr_const::kVetoModeLoose && (posMatch || negMatch)); + if (veto) { + ++nXiPairsVetoed; + continue; + } + + // Per-cascade efficiency weight (scaffold). Default helper + // returns 1.0; fill in CCDB loading inside getCascadeEfficiency<> + // when you have an efficiency map. + float wCascXi = getCascadeEfficiency<0 /*Xi*/>(casc.sign(), casc.pt(), xiY); + float wPair = wLam * wCascXi; + float dphi = RecoDecay::constrainAngle(casc.phi() - lam.phi(), -PIHalf); float dy = xiY - lam.rap(); bool isXiPlus = (casc.sign() > 0); - if (!isAntiLam && !isXiPlus) - histos.fill(HIST("Pairs/Lam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); - else if (!isAntiLam && isXiPlus) - histos.fill(HIST("Pairs/Lam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); - else if (isAntiLam && !isXiPlus) - histos.fill(HIST("Pairs/AntiLam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); - else if (isAntiLam && isXiPlus) - histos.fill(HIST("Pairs/AntiLam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); + // Baryon-number labels: + // Λ:B=+1, Λ̄:B=-1, Ξ⁻:B=+1 (sign<0), Ξ⁺:B=-1 (sign>0) + // LS = same baryon number, OS = opposite. + bool isLS = (isAntiLam == isXiPlus); // (Λ̄, Ξ⁺) ↔ (Λ, Ξ⁻) both LS + + if (pairCfg.cFillLamXi) { + if (!isAntiLam && !isXiPlus) + histos.fill(HIST("Pairs/Lam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (!isAntiLam && isXiPlus) + histos.fill(HIST("Pairs/Lam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (isAntiLam && !isXiPlus) + histos.fill(HIST("Pairs/AntiLam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (isAntiLam && isXiPlus) + histos.fill(HIST("Pairs/AntiLam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + } + + // Baryon-LS / OS combined fills. + if (isLS) + histos.fill(HIST("Pairs/LS_LamXi/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else + histos.fill(HIST("Pairs/OS_LamXi/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + + // pT-differential pair fill (opt-in via Configurable). + if (cFillPtDifferentialPairs) { + float ptL = lam.pt(); + if (!isAntiLam && !isXiPlus) + histos.fill(HIST("PairsPt/Lam_XiM/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + else if (!isAntiLam && isXiPlus) + histos.fill(HIST("PairsPt/Lam_XiP/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + else if (isAntiLam && !isXiPlus) + histos.fill(HIST("PairsPt/AntiLam_XiM/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + else if (isAntiLam && isXiPlus) + histos.fill(HIST("PairsPt/AntiLam_XiP/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + } } } + + // Per-event auto-correlation summary. + histos.fill(HIST("QA/AutoCorr/hXiPairsTotal"), nXiPairsTotal); + histos.fill(HIST("QA/AutoCorr/hXiPairsVetoed"), nXiPairsVetoed); + if (nXiPairsVetoed > 0) { + LOGF(debug, + "[LXi] Λ-Ξ veto: %d / %d pairs (%.1f%%) dropped as same-Λ in event", + nXiPairsVetoed, nXiPairsTotal, + nXiPairsTotal > 0 ? 100.0f * nXiPairsVetoed / nXiPairsTotal : 0.0f); + } } // Omega pair loop: same structure as Xi pairs but uses Omega mass for rapidity + kaon PID - template + template void analyzeOmegaPairs(L const& lambdas, C const& cascades, F const& flagsStart, float centVal) { + // Per-event auto-correlation bookkeeping for Λ-Ω. + int nOmPairsTotal = 0; + int nOmPairsVetoed = 0; + for (const auto& lam : lambdas) { if (std::abs(lam.rap()) > maxY) continue; float wLam = useEff ? lam.corrFact() : 1.0f; - bool isAntiLam = (lam.v0Type() == 1); + // Use the typed enum instead of magic literal "1". + bool isAntiLam = (lam.v0Type() == static_cast(kAntiLambda)); for (const auto& casc : cascades) { - if ((flagsStart + casc.globalIndex()).isSelected() == 0) + auto fr = flagsStart + casc.globalIndex(); + const int f = fr.isSelected(); + // Species filter — only Ω-flagged cascades pair as Ω. + if (f != kFlagOmegaOnly && f != kFlagXiAndOmega) + continue; + + // Centralised purity gate. + if (!passesCascadePurityGate(fr)) continue; auto bachTrack = casc.template bachelor_as(); if (std::abs(bachTrack.tpcNSigmaKa()) > tpcNsigmaBachKaon) continue; - float omY = RecoDecay::y(std::array{casc.px(), casc.py(), casc.pz()}, MassOmegaMinus); + // Use the cascade row's own Omega rapidity (correct mass). + float omY = casc.yOmega(); if (std::abs(omY) > maxY) continue; + ++nOmPairsTotal; + + // cVetoMode (see processXi pair loop for full doc). + const bool posMatch = (lam.posTrackId() == casc.posTrackId()); + const bool negMatch = (lam.negTrackId() == casc.negTrackId()); + const bool veto = + (cVetoMode == lcorr_const::kVetoModeStrict && posMatch && negMatch) || + (cVetoMode == lcorr_const::kVetoModeLoose && (posMatch || negMatch)); + if (veto) { + ++nOmPairsVetoed; + continue; + } + + // Cascade efficiency weight (Ω flavour). + float wCascOm = getCascadeEfficiency<1 /*Omega*/>(casc.sign(), casc.pt(), omY); + float wPair = wLam * wCascOm; + float dphi = RecoDecay::constrainAngle(casc.phi() - lam.phi(), -PIHalf); float dy = omY - lam.rap(); bool isOmPlus = (casc.sign() > 0); + bool isLS = (isAntiLam == isOmPlus); + + if (pairCfg.cFillLamOm) { + if (!isAntiLam && !isOmPlus) + histos.fill(HIST("Pairs/Lam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (!isAntiLam && isOmPlus) + histos.fill(HIST("Pairs/Lam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (isAntiLam && !isOmPlus) + histos.fill(HIST("Pairs/AntiLam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (isAntiLam && isOmPlus) + histos.fill(HIST("Pairs/AntiLam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + } - if (!isAntiLam && !isOmPlus) - histos.fill(HIST("Pairs/Lam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); - else if (!isAntiLam && isOmPlus) - histos.fill(HIST("Pairs/Lam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); - else if (isAntiLam && !isOmPlus) - histos.fill(HIST("Pairs/AntiLam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); - else if (isAntiLam && isOmPlus) - histos.fill(HIST("Pairs/AntiLam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wLam); + // Baryon-LS / OS combined fills. + if (isLS) + histos.fill(HIST("Pairs/LS_LamOm/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else + histos.fill(HIST("Pairs/OS_LamOm/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + + // pT-differential pair fill (opt-in). + if (cFillPtDifferentialPairs) { + float ptL = lam.pt(); + if (!isAntiLam && !isOmPlus) + histos.fill(HIST("PairsPt/Lam_OmM/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + else if (!isAntiLam && isOmPlus) + histos.fill(HIST("PairsPt/Lam_OmP/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + else if (isAntiLam && !isOmPlus) + histos.fill(HIST("PairsPt/AntiLam_OmM/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + else if (isAntiLam && isOmPlus) + histos.fill(HIST("PairsPt/AntiLam_OmP/hPtDeltaPhiDeltaY"), ptL, dphi, dy, wPair); + } } } + + // Per-event auto-correlation summary. + histos.fill(HIST("QA/AutoCorr/hOmPairsTotal"), nOmPairsTotal); + histos.fill(HIST("QA/AutoCorr/hOmPairsVetoed"), nOmPairsVetoed); + if (nOmPairsVetoed > 0) { + LOGF(debug, + "[LXi] Λ-Ω veto: %d / %d pairs (%.1f%%) dropped as same-Λ in event", + nOmPairsVetoed, nOmPairsTotal, + nOmPairsTotal > 0 ? 100.0f * nOmPairsVetoed / nOmPairsTotal : 0.0f); + } } void processXi(LambdaCollisionsExt::iterator const& lambdacoll, GoodLambdas const& /*lambdas*/, aod::CascDataExt const& cascades, - aod::CascadeFlags const& cascflags) + aod::CascadeFlags const& cascflags, + FullTracksExtIUWithPID const& /*tracks*/) // needed for bachelor_as<> { - histos.fill(HIST("Event/hEventCount"), 0.5); + // Event count is now owned by processYields (always-on by + // default). Removed from here to avoid N× inflation under DPL's + // batch-by-process-function execution. - auto lambdasInThisEvent = goodLambda->sliceBy(lambdasPerCollision, lambdacoll.globalIndex()); + // Trigger-Λ partition choice extracted into pickLambdaPartition(). + auto lambdasInThisEvent = pickLambdaPartition(lambdacoll); const int64_t refCollisionIndex = lambdacoll.refCollId(); auto cascadesInThisEvent = cascades.sliceBy(cascadesPerCollision, refCollisionIndex); @@ -3389,12 +4413,23 @@ struct LambdaXiCorrelation { auto flagsStart = cascflags.begin(); - analyzeSinglesLambda(lambdasInThisEvent); + // Λ singles fill is owned by processYields (single canonical + // owner across the workflow — neither processXi nor processMCRecoXi + // calls it anymore to avoid duplicate fills under any process-switch + // combination). float centVal = lambdacoll.cent(); analyzeSinglesXi(cascadesInThisEvent, flagsStart, pvX, pvY, pvZ, centVal); analyzePairs(lambdasInThisEvent, cascadesInThisEvent, flagsStart, centVal); + // Per-event coincidence: how many primary-Λ + // vs how many Ξ candidates share THIS collision. Now species-filtered via + // countSpeciesEligible<>() so hLamXi reflects Ξ-only multiplicity, not + // the combined Ξ+Ω cascade slice. Underflow row/col still reveals + // "events with only one species". + histos.fill(HIST("Yields/Coincidence/hLamXi"), + lambdasInThisEvent.size(), + countSpeciesEligible(cascadesInThisEvent, flagsStart)); } - PROCESS_SWITCH(LambdaXiCorrelation, processXi, "Λ–Ξ correlation", true); + PROCESS_SWITCH(Lambdacascadecorrelation, processXi, "Λ–Ξ correlation", true); void processOmega(LambdaCollisionsExt::iterator const& lambdacoll, GoodLambdas const& /*lambdas*/, @@ -3402,9 +4437,11 @@ struct LambdaXiCorrelation { aod::CascadeFlags const& cascflags, FullTracksExtIUWithPID const& /*tracks*/) { - histos.fill(HIST("Event/hEventCount"), 0.5); + // Event count is now owned by processYields (always-on by + // default). Removed from here to avoid N× inflation under DPL's + // batch-by-process-function execution. - auto lambdasInThisEvent = goodLambda->sliceBy(lambdasPerCollision, lambdacoll.globalIndex()); + auto lambdasInThisEvent = pickLambdaPartition(lambdacoll); const int64_t refCollisionIndex = lambdacoll.refCollId(); auto cascadesInThisEvent = cascades.sliceBy(cascadesPerCollision, refCollisionIndex); @@ -3414,12 +4451,17 @@ struct LambdaXiCorrelation { auto flagsStart = cascflags.begin(); - analyzeSinglesLambda(lambdasInThisEvent); + // Λ singles fill is owned by processXi (data) / processMCRecoXi (MC). + // Skipped here to prevent double-fill when both Ξ and Ω process functions are on. float centVal = lambdacoll.cent(); analyzeSinglesOmega(cascadesInThisEvent, flagsStart, pvX, pvY, pvZ, centVal); analyzeOmegaPairs(lambdasInThisEvent, cascadesInThisEvent, flagsStart, centVal); + // Per-event coincidence — Ω-eligible only. + histos.fill(HIST("Yields/Coincidence/hLamOm"), + lambdasInThisEvent.size(), + countSpeciesEligible(cascadesInThisEvent, flagsStart)); } - PROCESS_SWITCH(LambdaXiCorrelation, processOmega, "Λ–Ω correlation", false); + PROCESS_SWITCH(Lambdacascadecorrelation, processOmega, "Λ–Ω correlation", false); // --------------------------------------------------------------------------- // MC Reco-level with truth matching: Λ–Ξ @@ -3429,11 +4471,14 @@ struct LambdaXiCorrelation { GoodLambdas const& /*lambdas*/, LabeledCascades const& cascades, aod::CascadeFlags const& cascflags, + FullTracksExtIUWithPID const& /*tracks*/, // for bachelor_as<> aod::McParticles const& /*mcparts*/) { - histos.fill(HIST("Event/hEventCount"), 0.5); + // Event count is now owned by processYields (always-on by + // default). Removed from here to avoid N× inflation under DPL's + // batch-by-process-function execution. - auto lambdasInThisEvent = goodLambda->sliceBy(lambdasPerCollision, lambdacoll.globalIndex()); + auto lambdasInThisEvent = pickLambdaPartition(lambdacoll); const int64_t refCollisionIndex = lambdacoll.refCollId(); auto cascadesInThisEvent = cascades.sliceBy(labeledCascPerCollision, refCollisionIndex); @@ -3443,12 +4488,16 @@ struct LambdaXiCorrelation { auto flagsStart = cascflags.begin(); - analyzeSinglesLambda(lambdasInThisEvent); + // Λ singles owned by processYields. float centVal = lambdacoll.cent(); analyzeSinglesXi(cascadesInThisEvent, flagsStart, pvX, pvY, pvZ, centVal); - analyzePairs(lambdasInThisEvent, cascadesInThisEvent, flagsStart, centVal); + analyzePairs(lambdasInThisEvent, cascadesInThisEvent, flagsStart, centVal); + // Per-event coincidence — Ξ-eligible only. + histos.fill(HIST("Yields/Coincidence/hLamXi"), + lambdasInThisEvent.size(), + countSpeciesEligible(cascadesInThisEvent, flagsStart)); } - PROCESS_SWITCH(LambdaXiCorrelation, processMCRecoXi, "MC reco Λ–Ξ (truth-tagged tree)", false); + PROCESS_SWITCH(Lambdacascadecorrelation, processMCRecoXi, "MC reco Λ–Ξ (truth-tagged tree)", false); // --------------------------------------------------------------------------- // MC Reco-level with truth matching: Λ–Ω @@ -3461,9 +4510,11 @@ struct LambdaXiCorrelation { FullTracksExtIUWithPID const& /*tracks*/, aod::McParticles const& /*mcparts*/) { - histos.fill(HIST("Event/hEventCount"), 0.5); + // Event count is now owned by processYields (always-on by + // default). Removed from here to avoid N× inflation under DPL's + // batch-by-process-function execution. - auto lambdasInThisEvent = goodLambda->sliceBy(lambdasPerCollision, lambdacoll.globalIndex()); + auto lambdasInThisEvent = pickLambdaPartition(lambdacoll); const int64_t refCollisionIndex = lambdacoll.refCollId(); auto cascadesInThisEvent = cascades.sliceBy(labeledCascPerCollision, refCollisionIndex); @@ -3473,12 +4524,349 @@ struct LambdaXiCorrelation { auto flagsStart = cascflags.begin(); - analyzeSinglesLambda(lambdasInThisEvent); + // Λ singles owned by processMCRecoXi. Skipped to avoid double-fill. float centVal = lambdacoll.cent(); analyzeSinglesOmega(cascadesInThisEvent, flagsStart, pvX, pvY, pvZ, centVal); - analyzeOmegaPairs(lambdasInThisEvent, cascadesInThisEvent, flagsStart, centVal); + analyzeOmegaPairs(lambdasInThisEvent, cascadesInThisEvent, flagsStart, centVal); + // Per-event coincidence — Ω-eligible only. + histos.fill(HIST("Yields/Coincidence/hLamOm"), + lambdasInThisEvent.size(), + countSpeciesEligible(cascadesInThisEvent, flagsStart)); + } + PROCESS_SWITCH(Lambdacascadecorrelation, processMCRecoOmega, "MC reco Λ–Ω (truth-tagged tree)", false); + + // =========================================================================== + // Same-species and cross-species pair loops (Λ-Λ, Ξ-Ξ, Ω-Ω, Ξ-Ω) + // =========================================================================== + + // Λ-Λ pair loop: distinct V0s within the trigger partition. The auto- + // correlation veto reuses the existing cVetoMode policy via shared + // posTrackId/negTrackId. Filled into Pairs/Lam_Lam, Lam_AntiLam, + // AntiLam_AntiLam. + template + void analyzeLambdaLambdaPairs(L const& lambdas, float centVal) + { + if (!pairCfg.cFillLamLam) + return; + for (auto i = lambdas.begin(); i != lambdas.end(); ++i) { + const auto& lam1 = *i; + if (std::abs(lam1.rap()) > maxY) + continue; + bool isAnti1 = (lam1.v0Type() == static_cast(kAntiLambda)); + float w1 = useEff ? lam1.corrFact() : 1.0f; + auto j = i; + for (++j; j != lambdas.end(); ++j) { + const auto& lam2 = *j; + if (std::abs(lam2.rap()) > maxY) + continue; + // Auto-correlation veto: drop pairs that share daughters. + const bool posMatch = (lam1.posTrackId() == lam2.posTrackId()); + const bool negMatch = (lam1.negTrackId() == lam2.negTrackId()); + const bool veto = (cVetoMode == lcorr_const::kVetoModeStrict && posMatch && negMatch) || + (cVetoMode == lcorr_const::kVetoModeLoose && (posMatch || negMatch)); + if (veto) + continue; + + bool isAnti2 = (lam2.v0Type() == static_cast(kAntiLambda)); + float w2 = useEff ? lam2.corrFact() : 1.0f; + float wPair = w1 * w2; + float dphi = RecoDecay::constrainAngle(lam2.phi() - lam1.phi(), -PIHalf); + float dy = lam2.rap() - lam1.rap(); + + if (!isAnti1 && !isAnti2) + histos.fill(HIST("Pairs/Lam_Lam/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (isAnti1 && isAnti2) + histos.fill(HIST("Pairs/AntiLam_AntiLam/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else + histos.fill(HIST("Pairs/Lam_AntiLam/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + } + } + } + + // Ξ-Ξ pair loop: distinct cascades passing the Ξ-flag (flag 1 or 2). + // Sign split into XiM-XiM, XiP-XiP, XiM-XiP. Veto policy: shared bachelor. + template + void analyzeXiXiPairs(C const& cascades, F const& flagsStart, float centVal) + { + if (!pairCfg.cFillXiXi) + return; + for (auto i = cascades.begin(); i != cascades.end(); ++i) { + const auto& c1 = *i; + auto fr1 = flagsStart + c1.globalIndex(); + // Named flag constants (was: magic 1/2). + const int f1 = fr1.isSelected(); + if (f1 != kFlagXiOnly && f1 != kFlagXiAndOmega) + continue; + if (!passesCascadePurityGate(fr1)) + continue; + if (std::abs(c1.yXi()) > maxY) + continue; + auto j = i; + for (++j; j != cascades.end(); ++j) { + const auto& c2 = *j; + auto fr2 = flagsStart + c2.globalIndex(); + const int f2 = fr2.isSelected(); + if (f2 != kFlagXiOnly && f2 != kFlagXiAndOmega) + continue; + if (!passesCascadePurityGate(fr2)) + continue; + if (std::abs(c2.yXi()) > maxY) + continue; + float w1 = getCascadeEfficiency<0>(c1.sign(), c1.pt(), c1.yXi()); + float w2 = getCascadeEfficiency<0>(c2.sign(), c2.pt(), c2.yXi()); + float wPair = w1 * w2; + float dphi = RecoDecay::constrainAngle(c2.phi() - c1.phi(), -PIHalf); + float dy = c2.yXi() - c1.yXi(); + bool m1 = (c1.sign() < 0), m2 = (c2.sign() < 0); + if (m1 && m2) + histos.fill(HIST("Pairs/XiM_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (!m1 && !m2) + histos.fill(HIST("Pairs/XiP_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else + histos.fill(HIST("Pairs/XiM_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + } + } + } + + // Ω-Ω pair loop. Same as Ξ-Ξ but on flag 2/3 (Ω-eligible) and uses yOmega. + // Bachelor must satisfy the kaon-PID cut, which is enforced inside the + // cascade producer's processCandidate (flag=2 or 3 means Ω passed PID). + template + void analyzeOmegaOmegaPairs(C const& cascades, F const& flagsStart, float centVal) + { + if (!pairCfg.cFillOmOm) + return; + for (auto i = cascades.begin(); i != cascades.end(); ++i) { + const auto& c1 = *i; + auto fr1 = flagsStart + c1.globalIndex(); + int f1 = fr1.isSelected(); + // Named flag constants (was: magic 2/3). + if (f1 != kFlagXiAndOmega && f1 != kFlagOmegaOnly) + continue; + if (!passesCascadePurityGate(fr1)) + continue; + if (std::abs(c1.yOmega()) > maxY) + continue; + auto j = i; + for (++j; j != cascades.end(); ++j) { + const auto& c2 = *j; + auto fr2 = flagsStart + c2.globalIndex(); + int f2 = fr2.isSelected(); + if (f2 != kFlagXiAndOmega && f2 != kFlagOmegaOnly) + continue; + if (!passesCascadePurityGate(fr2)) + continue; + if (std::abs(c2.yOmega()) > maxY) + continue; + float w1 = getCascadeEfficiency<1>(c1.sign(), c1.pt(), c1.yOmega()); + float w2 = getCascadeEfficiency<1>(c2.sign(), c2.pt(), c2.yOmega()); + float wPair = w1 * w2; + float dphi = RecoDecay::constrainAngle(c2.phi() - c1.phi(), -PIHalf); + float dy = c2.yOmega() - c1.yOmega(); + bool m1 = (c1.sign() < 0), m2 = (c2.sign() < 0); + if (m1 && m2) + histos.fill(HIST("Pairs/OmM_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (!m1 && !m2) + histos.fill(HIST("Pairs/OmP_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else + histos.fill(HIST("Pairs/OmM_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + } + } + } + + // Ξ-Ω cross-species pair loop. flag 1 → Ξ-only, flag 3 → Ω-only, + // flag 2 → both (we treat as either). Generates 4 sign combos. + template + void analyzeXiOmegaPairs(C const& cascades, F const& flagsStart, float centVal) + { + if (!pairCfg.cFillXiOm) + return; + for (const auto& c1 : cascades) { + auto fr1 = flagsStart + c1.globalIndex(); + const int f1 = fr1.isSelected(); + if (f1 == kFlagRejected) + continue; + if (!passesCascadePurityGate(fr1)) + continue; + // Treat c1 as Ξ if it passes Ξ-eligibility AND |yXi| maxY) + continue; + for (const auto& c2 : cascades) { + if (c1.globalIndex() == c2.globalIndex()) + continue; + auto fr2 = flagsStart + c2.globalIndex(); + const int f2 = fr2.isSelected(); + if (!passesCascadePurityGate(fr2)) + continue; + // Treat c2 as Ω if it passes Ω-eligibility AND |yΩ| maxY) + continue; + float w1 = getCascadeEfficiency<0>(c1.sign(), c1.pt(), c1.yXi()); + float w2 = getCascadeEfficiency<1>(c2.sign(), c2.pt(), c2.yOmega()); + float wPair = w1 * w2; + float dphi = RecoDecay::constrainAngle(c2.phi() - c1.phi(), -PIHalf); + float dy = c2.yOmega() - c1.yXi(); + bool xiM = (c1.sign() < 0), omM = (c2.sign() < 0); + if (xiM && omM) + histos.fill(HIST("Pairs/XiM_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (xiM && !omM) + histos.fill(HIST("Pairs/XiM_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else if (!xiM && omM) + histos.fill(HIST("Pairs/XiP_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + else + histos.fill(HIST("Pairs/XiP_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy, wPair); + } + } + } + + // --------------------------------------------------------------------------- + // Pair driver. Single process function that runs ALL the + // same/cross-species pair loops on demand. Use this WITHOUT processXi / + // processOmega when you want full coverage; use the older Λ-X functions + // alone if you only need the Λ-trigger correlations. + // --------------------------------------------------------------------------- + void processAllPairs(LambdaCollisionsExt::iterator const& lambdacoll, + GoodLambdas const& /*lambdas*/, + aod::CascDataExt const& cascades, + aod::CascadeFlags const& cascflags, + FullTracksExtIUWithPID const& /*tracks*/) + { + // Event count is now owned by processYields (always-on by + // default). Removed from here to avoid N× inflation under DPL's + // batch-by-process-function execution. + auto lambdasInThisEvent = pickLambdaPartition(lambdacoll); + const int64_t refCollisionIndex = lambdacoll.refCollId(); + auto cascadesInThisEvent = cascades.sliceBy(cascadesPerCollision, refCollisionIndex); + float centVal = lambdacoll.cent(); + auto flagsStart = cascflags.begin(); + analyzeLambdaLambdaPairs(lambdasInThisEvent, centVal); + analyzeXiXiPairs(cascadesInThisEvent, flagsStart, centVal); + analyzeOmegaOmegaPairs(cascadesInThisEvent, flagsStart, centVal); + analyzeXiOmegaPairs(cascadesInThisEvent, flagsStart, centVal); + } + PROCESS_SWITCH(Lambdacascadecorrelation, processAllPairs, "All same-/cross-species pair loops (Λ-Λ, Ξ-Ξ, Ω-Ω, Ξ-Ω)", false); + + // --------------------------------------------------------------------------- + // processYields — fills the per-event multiplicity + ⟨pT⟩ + // histograms ONCE per event for every species, regardless of which pair- + // process functions are also enabled. Default ON. + // --------------------------------------------------------------------------- + void processYields(LambdaCollisionsExt::iterator const& lambdacoll, + GoodLambdas const& /*lambdas*/, + aod::CascDataExt const& cascades, + aod::CascadeFlags const& cascflags) + { + // processYields is the canonical owner of Event/hEventCount. + // Default-on, fires exactly once per LambdaCollision iterator → unique + // event count regardless of which other process functions are active. + fillEventCountOnce(lambdacoll.globalIndex()); + auto lambdasInThisEvent = pickLambdaPartition(lambdacoll); + // processYields is ALSO the canonical owner of the Λ + // singles histograms + LambdaCandidates tree fill. Previously these + // were in processXi / processMCRecoXi but DPL appears to fire them + // double under some process-switch combinations. + analyzeSinglesLambda(lambdasInThisEvent, lambdacoll); + if (!yieldCfg.cFillEventYields) + return; + const int64_t refCollisionIndex = lambdacoll.refCollId(); + auto cascadesInThisEvent = cascades.sliceBy(cascadesPerCollision, refCollisionIndex); + auto flagsStart = cascflags.begin(); + float centVal = lambdacoll.cent(); + + // Per-event accumulators. + int nLam = 0, nALam = 0, nXiM = 0, nXiP = 0, nOmM = 0, nOmP = 0; + double sLam = 0, sALam = 0, sXiM = 0, sXiP = 0, sOmM = 0, sOmP = 0; + + for (const auto& l : lambdasInThisEvent) { + if (std::abs(l.rap()) > maxY) + continue; + if (l.v0Type() == static_cast(kLambda)) { + ++nLam; + sLam += l.pt(); + } else { + ++nALam; + sALam += l.pt(); + } + } + for (const auto& c : cascadesInThisEvent) { + auto fr = flagsStart + c.globalIndex(); + int f = fr.isSelected(); + if (f == 0) + continue; + // Ξ-eligible (flag 1 or 2) + if ((f == kFlagXiOnly || f == kFlagXiAndOmega) && std::abs(c.yXi()) <= maxY) { + if (c.sign() < 0) { + ++nXiM; + sXiM += c.pt(); + } else { + ++nXiP; + sXiP += c.pt(); + } + } + // Ω-eligible (flag 2 or 3) + if ((f == kFlagXiAndOmega || f == kFlagOmegaOnly) && std::abs(c.yOmega()) <= maxY) { + if (c.sign() < 0) { + ++nOmM; + sOmM += c.pt(); + } else { + ++nOmP; + sOmP += c.pt(); + } + } + } + + auto fillSpecies = [&](const auto& nKey, const auto& mptKey, + const auto& mNvsCKey, const auto& mPtvsCKey, + const auto& nptKey, + int n, double sumPt) { + histos.fill(nKey, n); + histos.fill(mNvsCKey, centVal, n); + if (n > 0) { + double mpt = sumPt / n; + histos.fill(mptKey, mpt); + histos.fill(mPtvsCKey, centVal, mpt); + histos.fill(nptKey, n, mpt); + } + }; + + fillSpecies(HIST("Yields/Lambda/hNPerEvent"), + HIST("Yields/Lambda/hMeanPtPerEvent"), + HIST("Yields/Lambda/hMeanNvsCent"), + HIST("Yields/Lambda/hMeanPtVsCent"), + HIST("Yields/Lambda/hNvsPt2D"), + nLam, sLam); + fillSpecies(HIST("Yields/AntiLambda/hNPerEvent"), + HIST("Yields/AntiLambda/hMeanPtPerEvent"), + HIST("Yields/AntiLambda/hMeanNvsCent"), + HIST("Yields/AntiLambda/hMeanPtVsCent"), + HIST("Yields/AntiLambda/hNvsPt2D"), + nALam, sALam); + fillSpecies(HIST("Yields/XiMinus/hNPerEvent"), + HIST("Yields/XiMinus/hMeanPtPerEvent"), + HIST("Yields/XiMinus/hMeanNvsCent"), + HIST("Yields/XiMinus/hMeanPtVsCent"), + HIST("Yields/XiMinus/hNvsPt2D"), + nXiM, sXiM); + fillSpecies(HIST("Yields/XiPlus/hNPerEvent"), + HIST("Yields/XiPlus/hMeanPtPerEvent"), + HIST("Yields/XiPlus/hMeanNvsCent"), + HIST("Yields/XiPlus/hMeanPtVsCent"), + HIST("Yields/XiPlus/hNvsPt2D"), + nXiP, sXiP); + fillSpecies(HIST("Yields/OmegaMinus/hNPerEvent"), + HIST("Yields/OmegaMinus/hMeanPtPerEvent"), + HIST("Yields/OmegaMinus/hMeanNvsCent"), + HIST("Yields/OmegaMinus/hMeanPtVsCent"), + HIST("Yields/OmegaMinus/hNvsPt2D"), + nOmM, sOmM); + fillSpecies(HIST("Yields/OmegaPlus/hNPerEvent"), + HIST("Yields/OmegaPlus/hMeanPtPerEvent"), + HIST("Yields/OmegaPlus/hMeanNvsCent"), + HIST("Yields/OmegaPlus/hMeanPtVsCent"), + HIST("Yields/OmegaPlus/hNvsPt2D"), + nOmP, sOmP); } - PROCESS_SWITCH(LambdaXiCorrelation, processMCRecoOmega, "MC reco Λ–Ω (truth-tagged tree)", false); + PROCESS_SWITCH(Lambdacascadecorrelation, processYields, "Per-event multiplicity + ⟨pT⟩ for each species", true); // --------------------------------------------------------------------------- // MC Gen-level: Λ–Ξ truth correlation (closure test) @@ -3487,59 +4875,107 @@ struct LambdaXiCorrelation { // McParticles are sliced per McCollision using the stored refMcCollId. // No reconstruction, no PID cuts — pure truth-level R2. // --------------------------------------------------------------------------- + // Single-pass MC gen Λ-Ξ: classify Lambdas and Xis once, then + // do a single pair loop over the classified vectors. The previous version + // scanned `genLambdasAll` four times (Lambda singles, AntiLambda singles, + // Lam-pairs, AntiLam-pairs) and `genXis` three times. With this rewrite + // each table is scanned exactly once. void processMCGenXi(aod::LambdaMcGenCollisions::iterator const& mcgencol, aod::LambdaMcGenTracks const& genLambdasAll, aod::McParticles const& allMcParts) { int32_t thisColId = mcgencol.globalIndex(); - histos.fill(HIST("Event/hEventCount"), 0.5); + // gen process functions do NOT fill Event/hEventCount — + // that's the reco counter's job. seenGenCollOnce is reserved for any + // future gen-side idempotence needs. + (void)seenGenCollOnce(static_cast(thisColId)); + // Gen event counter (R₂ closure-test denominator). Owned + // by processMCGenXi (canonical gen "first owner"). + histos.fill(HIST("McGen/Event/hEventCount"), 0.5); float centVal = mcgencol.cent(); - // Slice McParticles to only those belonging to this McCollision int64_t mcCollId = mcgencol.refMcCollId(); auto genXis = allMcParts.sliceBy(mcParticlesPerMcCollision, mcCollId); - // Fill gen Lambda singles — filter by collision ID inline - for (const auto& lam : genLambdasAll) { - if (lam.lambdaMcGenCollisionId() != thisColId) - continue; - if (lam.v0Type() != (int8_t)kLambda || lam.v0PrmScd() != (int8_t)kPrimary) - continue; - if (std::abs(lam.rap()) > maxY) - continue; - histos.fill(HIST("McGen/Singles/Lambda/hPt"), lam.pt()); - } + // processMCGenXi owns the gen-Λ singles + tree fill. + // processMCGenOmega skips Λ fills (only builds local vectors). This is + // the ownership model — a per-event guard would not work because DPL + // batches by process function, not by event. + + // Pre-classified caches. Reserved sizes are heuristic for typical + // Pb-Pb central event populations. + struct LamLite { + float pt, rap, phi; + }; + struct XiLite { + float pt, rap, phi; + bool isPlus; + }; + std::vector goodLam, goodAntiLam; + std::vector goodXi; + goodLam.reserve(32); + goodAntiLam.reserve(32); + goodXi.reserve(16); + + // Single pass over Lambdas: fill singles, classify into Lam/AntiLam. for (const auto& lam : genLambdasAll) { if (lam.lambdaMcGenCollisionId() != thisColId) continue; - if (lam.v0Type() != (int8_t)kAntiLambda || lam.v0PrmScd() != (int8_t)kPrimary) + if (lam.v0PrmScd() != static_cast(kPrimary)) continue; if (std::abs(lam.rap()) > maxY) continue; - histos.fill(HIST("McGen/Singles/AntiLambda/hPt"), lam.pt()); + LamLite l{.pt = static_cast(lam.pt()), .rap = static_cast(lam.rap()), .phi = static_cast(lam.phi())}; + // Wrap φ to [0, 2π) to match the reco-side convention. + float phiWrapped = RecoDecay::constrainAngle(lam.phi(), 0.f); + if (lam.v0Type() == static_cast(kLambda)) { + histos.fill(HIST("McGen/Singles/Lambda/hPt"), lam.pt()); + histos.fill(HIST("McGen/Singles/Lambda/hYPhi"), lam.rap(), phiWrapped); + goodLam.push_back(l); + } else if (lam.v0Type() == static_cast(kAntiLambda)) { + histos.fill(HIST("McGen/Singles/AntiLambda/hPt"), lam.pt()); + histos.fill(HIST("McGen/Singles/AntiLambda/hYPhi"), lam.rap(), phiWrapped); + goodAntiLam.push_back(l); + } + + // Gen-Λ TTree fill. Owned by processMCGenXi. + if (bp && saveLambdaTree) { + auto& g = bp->lamGen; + g.pt = lam.pt(); + g.eta = lam.eta(); + g.rap = lam.rap(); + g.phi = lam.phi(); + g.v0Type = static_cast(lam.v0Type()); + g.v0PrmScd = static_cast(lam.v0PrmScd()); + g.cent = centVal; + g.pvZ = mcgencol.posZ(); + treeLambdaGen->Fill(); + } } - // Fill gen Xi singles (inline PDG filter) + // Single pass over Xis: fill singles, eff denominator, optional tree, classify. for (const auto& xi : genXis) { - if (std::abs(xi.pdgCode()) != 3312) - continue; // inline PDG filter + if (std::abs(xi.pdgCode()) != lcorr_const::kXiMinusPdg) + continue; if (!xi.isPhysicalPrimary()) continue; - float xiY = RecoDecay::y(std::array{xi.px(), xi.py(), xi.pz()}, MassXi0); + float xiY = RecoDecay::y(std::array{xi.px(), xi.py(), xi.pz()}, MassXiMinus); if (std::abs(xiY) > maxY) continue; - if (xi.pdgCode() == 3312) + bool isPlus = (xi.pdgCode() == -lcorr_const::kXiMinusPdg); + // φ for closure-test (y, φ) singles. + float xiPhiWrapped = RecoDecay::constrainAngle(xi.phi(), 0.f); + if (!isPlus) { histos.fill(HIST("McGen/Singles/XiMinus/hPtVsRap"), xiY, xi.pt()); - else if (xi.pdgCode() == -3312) - histos.fill(HIST("McGen/Singles/XiPlus/hPtVsRap"), xiY, xi.pt()); - - // Efficiency denominator: all physical-primary gen Xi in acceptance - if (xi.pdgCode() == 3312) + histos.fill(HIST("McGen/Singles/XiMinus/hYPhi"), xiY, xiPhiWrapped); histos.fill(HIST("Eff/Gen/XiMinus/hCentPtRap"), centVal, xi.pt(), xiY); - else + } else { + histos.fill(HIST("McGen/Singles/XiPlus/hPtVsRap"), xiY, xi.pt()); + histos.fill(HIST("McGen/Singles/XiPlus/hYPhi"), xiY, xiPhiWrapped); histos.fill(HIST("Eff/Gen/XiPlus/hCentPtRap"), centVal, xi.pt(), xiY); - - if (bp) { + } + // treeXiGen is only setObject-ed when saveCascTree=true. + if (bp && saveCascTree) { auto& b = bp->xiGen; b.pt = xi.pt(); b.rap = xiY; @@ -3549,103 +4985,109 @@ struct LambdaXiCorrelation { b.pvZ = mcgencol.posZ(); treeXiGen->Fill(); } + goodXi.push_back({static_cast(xi.pt()), xiY, static_cast(xi.phi()), isPlus}); } - // Fill gen Lam-Xi pairs - auto fillGenXiPairs = [&](bool isAntiLam) { - int8_t wantType = isAntiLam ? (int8_t)kAntiLambda : (int8_t)kLambda; - for (const auto& lam : genLambdasAll) { - if (lam.lambdaMcGenCollisionId() != thisColId) - continue; - if (lam.v0Type() != wantType || lam.v0PrmScd() != (int8_t)kPrimary) - continue; - if (std::abs(lam.rap()) > maxY) - continue; - for (const auto& xi : genXis) { - if (std::abs(xi.pdgCode()) != 3312) - continue; // inline PDG filter - if (!xi.isPhysicalPrimary()) - continue; - float xiY = RecoDecay::y(std::array{xi.px(), xi.py(), xi.pz()}, MassXi0); - if (std::abs(xiY) > maxY) - continue; - float dphi = RecoDecay::constrainAngle(xi.phi() - lam.phi(), -PIHalf); - float dy = xiY - lam.rap(); - bool isXiPlus = (xi.pdgCode() == -3312); - if (!isAntiLam && !isXiPlus) - histos.fill(HIST("McGen/Pairs/Lam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy); - else if (!isAntiLam && isXiPlus) - histos.fill(HIST("McGen/Pairs/Lam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy); - else if (isAntiLam && !isXiPlus) - histos.fill(HIST("McGen/Pairs/AntiLam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy); - else - histos.fill(HIST("McGen/Pairs/AntiLam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy); - } + // Pair loop over the classified caches. One pass over Lam × Xi, one over AntiLam × Xi. + for (const auto& l : goodLam) { + for (const auto& x : goodXi) { + float dphi = RecoDecay::constrainAngle(x.phi - l.phi, -PIHalf); + float dy = x.rap - l.rap; + if (!x.isPlus) + histos.fill(HIST("McGen/Pairs/Lam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy); + else + histos.fill(HIST("McGen/Pairs/Lam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy); } - }; - fillGenXiPairs(false); - fillGenXiPairs(true); + } + for (const auto& l : goodAntiLam) { + for (const auto& x : goodXi) { + float dphi = RecoDecay::constrainAngle(x.phi - l.phi, -PIHalf); + float dy = x.rap - l.rap; + if (!x.isPlus) + histos.fill(HIST("McGen/Pairs/AntiLam_XiM/hDeltaPhiDeltaY"), centVal, dphi, dy); + else + histos.fill(HIST("McGen/Pairs/AntiLam_XiP/hDeltaPhiDeltaY"), centVal, dphi, dy); + } + } } - PROCESS_SWITCH(LambdaXiCorrelation, processMCGenXi, "MC gen-level Λ–Ξ closure", false); + PROCESS_SWITCH(Lambdacascadecorrelation, processMCGenXi, "MC gen-level Λ–Ξ closure", false); // --------------------------------------------------------------------------- // MC Gen-level: Λ–Ω truth correlation (closure test) // Same structure as processMCGenXi but uses |PDG| == 3334 (Omega). // No kaon PID cut needed — truth level. // --------------------------------------------------------------------------- + // Single-pass MC gen Λ-Ω, structured identically to processMCGenXi. void processMCGenOmega(aod::LambdaMcGenCollisions::iterator const& mcgencol, aod::LambdaMcGenTracks const& genLambdasAll, aod::McParticles const& allMcParts) { int32_t thisColId = mcgencol.globalIndex(); - histos.fill(HIST("Event/hEventCount"), 0.5); + // gen process functions do NOT fill Event/hEventCount — + // that's the reco counter's job. seenGenCollOnce is reserved for any + // future gen-side idempotence needs. + (void)seenGenCollOnce(static_cast(thisColId)); float centVal = mcgencol.cent(); - // Slice McParticles to only those belonging to this McCollision int64_t mcCollId = mcgencol.refMcCollId(); auto genOmegas = allMcParts.sliceBy(mcParticlesPerMcCollision, mcCollId); - // Fill gen Lambda singles — filter by collision ID inline - for (const auto& lam : genLambdasAll) { - if (lam.lambdaMcGenCollisionId() != thisColId) - continue; - if (lam.v0Type() != (int8_t)kLambda || lam.v0PrmScd() != (int8_t)kPrimary) - continue; - if (std::abs(lam.rap()) > maxY) - continue; - histos.fill(HIST("McGen/Singles/Lambda/hPt"), lam.pt()); - } + // Gen-Λ singles + tree are owned by processMCGenXi (DPL + // batches by process function, so a per-event guard would fail). Here + // we only need to BUILD the goodLam/goodAntiLam vectors for the local + // Λ-Ω pair loop — no histogram or tree side effects. + struct LamLite { + float pt, rap, phi; + }; + struct OmLite { + float pt, rap, phi; + bool isPlus; + }; + std::vector goodLam, goodAntiLam; + std::vector goodOm; + goodLam.reserve(32); + goodAntiLam.reserve(32); + goodOm.reserve(8); + + // Single pass over Lambdas — vectors only, no fills. for (const auto& lam : genLambdasAll) { if (lam.lambdaMcGenCollisionId() != thisColId) continue; - if (lam.v0Type() != (int8_t)kAntiLambda || lam.v0PrmScd() != (int8_t)kPrimary) + if (lam.v0PrmScd() != static_cast(kPrimary)) continue; if (std::abs(lam.rap()) > maxY) continue; - histos.fill(HIST("McGen/Singles/AntiLambda/hPt"), lam.pt()); + LamLite l{.pt = static_cast(lam.pt()), .rap = static_cast(lam.rap()), .phi = static_cast(lam.phi())}; + if (lam.v0Type() == static_cast(kLambda)) { + goodLam.push_back(l); + } else if (lam.v0Type() == static_cast(kAntiLambda)) { + goodAntiLam.push_back(l); + } } - // Fill gen Omega singles (inline PDG filter) + // Single pass over Omegas. for (const auto& om : genOmegas) { - if (std::abs(om.pdgCode()) != 3334) - continue; // inline PDG filter + if (std::abs(om.pdgCode()) != lcorr_const::kOmegaMinusPdg) + continue; if (!om.isPhysicalPrimary()) continue; float omY = RecoDecay::y(std::array{om.px(), om.py(), om.pz()}, MassOmegaMinus); if (std::abs(omY) > maxY) continue; - if (om.pdgCode() == 3334) + bool isPlus = (om.pdgCode() == -lcorr_const::kOmegaMinusPdg); + // φ for closure-test (y, φ) singles. + float omPhiWrapped = RecoDecay::constrainAngle(om.phi(), 0.f); + if (!isPlus) { histos.fill(HIST("McGen/Singles/OmegaMinus/hPtVsRap"), omY, om.pt()); - else if (om.pdgCode() == -3334) - histos.fill(HIST("McGen/Singles/OmegaPlus/hPtVsRap"), omY, om.pt()); - - // Efficiency denominator: all physical-primary gen Omega in acceptance - if (om.pdgCode() == 3334) + histos.fill(HIST("McGen/Singles/OmegaMinus/hYPhi"), omY, omPhiWrapped); histos.fill(HIST("Eff/Gen/OmegaMinus/hCentPtRap"), centVal, om.pt(), omY); - else + } else { + histos.fill(HIST("McGen/Singles/OmegaPlus/hPtVsRap"), omY, om.pt()); + histos.fill(HIST("McGen/Singles/OmegaPlus/hYPhi"), omY, omPhiWrapped); histos.fill(HIST("Eff/Gen/OmegaPlus/hCentPtRap"), centVal, om.pt(), omY); - - if (bp) { + } + // treeOmegaGen is only setObject-ed when saveCascTree=true. + if (bp && saveCascTree) { auto& b = bp->omGen; b.pt = om.pt(); b.rap = omY; @@ -3655,56 +5097,45 @@ struct LambdaXiCorrelation { b.pvZ = mcgencol.posZ(); treeOmegaGen->Fill(); } + goodOm.push_back({static_cast(om.pt()), omY, static_cast(om.phi()), isPlus}); } - // Fill gen Lam-Omega pairs - auto fillGenOmPairs = [&](bool isAntiLam) { - int8_t wantType = isAntiLam ? (int8_t)kAntiLambda : (int8_t)kLambda; - for (const auto& lam : genLambdasAll) { - if (lam.lambdaMcGenCollisionId() != thisColId) - continue; - if (lam.v0Type() != wantType || lam.v0PrmScd() != (int8_t)kPrimary) - continue; - if (std::abs(lam.rap()) > maxY) - continue; - for (const auto& om : genOmegas) { - if (std::abs(om.pdgCode()) != 3334) - continue; // inline PDG filter - if (!om.isPhysicalPrimary()) - continue; - float omY = RecoDecay::y(std::array{om.px(), om.py(), om.pz()}, MassOmegaMinus); - if (std::abs(omY) > maxY) - continue; - float dphi = RecoDecay::constrainAngle(om.phi() - lam.phi(), -PIHalf); - float dy = omY - lam.rap(); - bool isOmPlus = (om.pdgCode() == -3334); - if (!isAntiLam && !isOmPlus) - histos.fill(HIST("McGen/Pairs/Lam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy); - else if (!isAntiLam && isOmPlus) - histos.fill(HIST("McGen/Pairs/Lam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy); - else if (isAntiLam && !isOmPlus) - histos.fill(HIST("McGen/Pairs/AntiLam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy); - else - histos.fill(HIST("McGen/Pairs/AntiLam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy); - } + // Pair loop over the classified caches. + for (const auto& l : goodLam) { + for (const auto& o : goodOm) { + float dphi = RecoDecay::constrainAngle(o.phi - l.phi, -PIHalf); + float dy = o.rap - l.rap; + if (!o.isPlus) + histos.fill(HIST("McGen/Pairs/Lam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy); + else + histos.fill(HIST("McGen/Pairs/Lam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy); } - }; - fillGenOmPairs(false); - fillGenOmPairs(true); + } + for (const auto& l : goodAntiLam) { + for (const auto& o : goodOm) { + float dphi = RecoDecay::constrainAngle(o.phi - l.phi, -PIHalf); + float dy = o.rap - l.rap; + if (!o.isPlus) + histos.fill(HIST("McGen/Pairs/AntiLam_OmM/hDeltaPhiDeltaY"), centVal, dphi, dy); + else + histos.fill(HIST("McGen/Pairs/AntiLam_OmP/hDeltaPhiDeltaY"), centVal, dphi, dy); + } + } } - PROCESS_SWITCH(LambdaXiCorrelation, processMCGenOmega, "MC gen-level Λ–Ω closure", false); + PROCESS_SWITCH(Lambdacascadecorrelation, processMCGenOmega, "MC gen-level Λ–Ω closure", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), + // LambdaCascadeProducer absorbs the former LambdaTableProducer + // and CascadeSelector — single event selection, single source of truth. + // LambdaR2Correlation, CascadeSelector (#if 0 block), and + // CascadeCorrelations were removed entirely from this file. + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), - // adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - // adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc) + adaptAnalysisTask(cfgc) }; } diff --git a/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx new file mode 100644 index 00000000000..79f8efb4720 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/cascDiHadronCorr.cxx @@ -0,0 +1,2012 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file cascDiHadronCorr.cxx +/// \brief This task is to caculate cascades di-hadron correlation +/// \author Fuchun Cui(fcui@cern.ch) +/// \since jun/17/2026 + +#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace constants::math; + +// define the filtered collisions and tracks +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; + +struct CascDiHadronCorr { + Service ccdb; + + O2_DEFINE_CONFIGURABLE(cfgCutVtxZ, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") + O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") + O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, false, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 0.0f, "Minimum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 100.0f, "Maximum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 1, "Minimum number of events to mix") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyCasc, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgVerbosity, bool, false, "Verbose output") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, false, "enable trigger pT < associated pT cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, true, "enable trigger pT < associated pT cut in mixed event") + O2_DEFINE_CONFIGURABLE(cfgUseCFStepAll, bool, true, "Filling kCFStepAll") + O2_DEFINE_CONFIGURABLE(cfgSoloPtTrack, bool, false, "Skip trigger tracks that are alone in their pT bin for same process") + O2_DEFINE_CONFIGURABLE(cfgSingleSoloPtTrack, bool, false, "Skip associated tracks that are alone in their pT bin for same process, works only if cfgSoloPtTrack is enabled") + O2_DEFINE_CONFIGURABLE(cfgNSigmapid, std::vector, (std::vector{3, 3, 3}), "tpc NSigma for Pion Proton Kaon") + O2_DEFINE_CONFIGURABLE(cfgOutputXi, bool, true, "Output Xi-charged correlation") + O2_DEFINE_CONFIGURABLE(cfgOutputOmega, bool, false, "Output Omega-charged correlation") + struct : ConfigurableGroup { + std::string prefix = "cfgEvSel"; + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, false, "rejects collisions which are associated with the same found-by-T0 bunch crossing") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersAll, bool, true, "cut time intervals with dead ITS staves") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") + } cfgEvSel; + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") + Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") + Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") + Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + std::vector multT0CCutPars; + std::vector multPVT0CCutPars; + std::vector multGlobalPVCutPars; + std::vector multMultV0ACutPars; + TF1* fMultPVT0CCutLow = nullptr; + TF1* fMultPVT0CCutHigh = nullptr; + TF1* fMultT0CCutLow = nullptr; + TF1* fMultT0CCutHigh = nullptr; + TF1* fMultGlobalPVCutLow = nullptr; + TF1* fMultGlobalPVCutHigh = nullptr; + TF1* fMultMultV0ACutLow = nullptr; + TF1* fMultMultV0ACutHigh = nullptr; + TF1* fT0AV0AMean = nullptr; + TF1* fT0AV0ASigma = nullptr; + } cfgFuncParas; + struct : ConfigurableGroup { + std::string prefix = "cascBuilderOpts"; + // topological cut for cascade + O2_DEFINE_CONFIGURABLE(cfgcasc_radius, float, 0.5f, "minimum decay radius") + O2_DEFINE_CONFIGURABLE(cfgcasc_casccospa, float, 0.99f, "minimum cosine of pointing angle") + O2_DEFINE_CONFIGURABLE(cfgcasc_v0cospa, float, 0.99f, "minimum cosine of pointing angle") + O2_DEFINE_CONFIGURABLE(cfgcasc_dcav0topv, float, 0.01f, "minimum daughter DCA to PV") + O2_DEFINE_CONFIGURABLE(cfgcasc_dcabachtopv, float, 0.01f, "minimum bachelor DCA to PV") + O2_DEFINE_CONFIGURABLE(cfgcasc_dcaLapitopv, float, 0.1f, "minimum pion from casc->Lambda->Pi DCA to PV") + O2_DEFINE_CONFIGURABLE(cfgcasc_dcaLaprtopv, float, 0.1f, "minimum proton from casc->Lambda->Pr DCA to PV") + O2_DEFINE_CONFIGURABLE(cfgcasc_dcacascdau, float, 0.3f, "maximum DCA among cascade daughters") + O2_DEFINE_CONFIGURABLE(cfgcasc_dcav0dau, float, 1.0f, "maximum DCA among V0 daughters") + O2_DEFINE_CONFIGURABLE(cfgcasc_mlambdawindow, float, 0.04f, "Invariant mass window of lambda") + O2_DEFINE_CONFIGURABLE(cfgcasc_compmassrej, float, 0.008f, "competing mass rejection of cascades") + } cascBuilderOpts; + struct : ConfigurableGroup { + std::string prefix = "trkQualityOpts"; + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtDauMin, float, 0.2f, "Minimal pT for daughter tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtDauMax, float, 10.0f, "Maximal pT for daughter tracks") + // track quality selections for daughter track + O2_DEFINE_CONFIGURABLE(cfgMaxITSNCls, int, 10, "check maximum number of ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgMinITSNCls, int, -1, "check minimum number of ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgTPCNCls, int, 0, "check minimum number of TPC hits") + O2_DEFINE_CONFIGURABLE(cfgTPCCrossedRows, int, 0, "check minimum number of TPC crossed rows") + } trkQualityOpts; + + SliceCache cache; + + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity axis for histograms"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for histograms"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {48, -2.4, 2.4}, "delta eta axis for histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt associated axis for histograms"}; + ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; + ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; + ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; + ConfigurableAxis axisInvMass{"axisInvMass", {VARIABLE_WIDTH, 1.7, 1.75, 1.8, 1.85, 1.9, 1.95, 2.0, 5.0}, "invariant mass axis for histograms"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for efficiency histograms"}; + + // make the filters and cuts. + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVtxZ); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + using FilteredCollisions = soa::Filtered>; + using FilteredTracks = soa::Filtered>; + using FilteredTracksWithMCLabels = soa::Filtered>; + using TracksPID = soa::Join; + using DaughterTracks = soa::Join; + + // Filter for MCParticle + Filter particleFilter = (nabs(aod::mcparticle::eta) < cfgCutEta) && (aod::mcparticle::pt > cfgCutPtMin) && (aod::mcparticle::pt < cfgCutPtMax); + using FilteredMcParticles = soa::Filtered; + + // Filter for MCcollisions + Filter mccollisionFilter = nabs(aod::mccollision::posZ) < cfgCutVtxZ; + using FilteredMcCollisions = soa::Filtered; + + using SmallGroupMcCollisions = soa::SmallGroups>; + + Preslice perCollision = aod::cascdata::collisionId; + PresliceUnsorted collisionPerMCCollision = aod::mccollisionlabel::mcCollisionId; + + // Corrections + TH3D* mEfficiency = nullptr; + TH3D* mEfficiencyCasc = nullptr; + TH1D* mCentralityWeight = nullptr; + bool correctionsLoaded = false; + + // Define the outputs + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + HistogramRegistry registry{"registry"}; + + // define global variables + TRandom3* gRandom = new TRandom3(); + enum CentEstimators { + kCentFT0C = 0, + kCentFT0CVariant1, + kCentFT0M, + kCentFV0A, + // Count the total number of enum + kCount_CentEstimators + }; + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; + + // buffer for mixevents + struct ValidCollision { + struct ValidParticle { + float eta; + float phi; + float pt; + }; + float pvz = 0; + float mult = 0; + std::vector assocParticles; + void addValidParticle(float eta, float phi, float pt) + { + ValidParticle particle{eta, phi, pt}; + assocParticles.push_back(particle); + } + }; + using ValidCollisions = std::vector>; + ValidCollisions validCollisions; + + double masslow = 0; + double massup = 0; + + // persistent caches + std::vector efficiencyAssociatedCache; + + std::vector cfgNSigma; + + void init(InitContext&) + { + if (cfgCentTableUnavailable && !cfgSelCollByNch) { + LOGF(fatal, "Centrality table is unavailable, cannot select collisions by centrality"); + } + const AxisSpec axisPhi{72, 0.0, constants::math::TwoPI, "#varphi"}; + const AxisSpec axisEta{40, -1., 1., "#eta"}; + o2::framework::AxisSpec axismass = axisInvMass; + int nMasssBinEdges = axismass.binEdges.size(); + masslow = axismass.binEdges[0]; + massup = axismass.binEdges[nMasssBinEdges - 1]; + cfgNSigma = cfgNSigmapid; + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + LOGF(info, "Starting init"); + + // Event Counter + if (doprocessSame && cfgEvSel.cfgUseAdditionalEventCut.value) { + registry.add("hEventCountSpecific", "Number of Event;; Count", {HistType::kTH1D, {{12, 0, 12}}}); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(1, "after sel8"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(2, "kNoSameBunchPileup"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(3, "kNoITSROFrameBorder"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(5, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(6, "kNoCollInTimeRangeStandard"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(7, "kIsGoodITSLayersAll"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(8, "kNoCollInRofStandard"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(9, "kNoHighMultCollInPrevRof"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(10, "occupancy"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(11, "MultCorrelation"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(12, "cfgEvSelV0AT0ACut"); + } + + if (cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) { + cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; + cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; + cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; + cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; + cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); + cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); + + cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); + cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); + + cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); + cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); + + cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); + cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); + + cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); + cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); + } + + std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); + // Make histograms to check the distributions after cuts + if (doprocessSame) { + registry.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); + registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); + registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); + registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); + registry.add("pT", "pT", {HistType::kTH1D, {axisPtTrigger}}); + registry.add("pTCorrected", "pTCorrected", {HistType::kTH1D, {axisPtTrigger}}); + registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); + registry.add("Nch_used", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event + registry.add("Centrality", hCentTitle.c_str(), {HistType::kTH1D, {{100, 0, 100}}}); + registry.add("CentralityWeighted", hCentTitle.c_str(), {HistType::kTH1D, {{100, 0, 100}}}); + registry.add("Centrality_used", hCentTitle.c_str(), {HistType::kTH1D, {{100, 0, 100}}}); // histogram to see how many events are in the same and mixed event + registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + registry.add("zVtx_used", "zVtx_used", {HistType::kTH1D, {axisVertex}}); + registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + } + if (cfgSoloPtTrack && doprocessSame) { + registry.add("Nch_final_pt", "pT", {HistType::kTH1D, {axisPtTrigger}}); + registry.add("Solo_tracks_trigger", "pT", {HistType::kTH1D, {axisPtTrigger}}); + if (!cfgSingleSoloPtTrack) { + registry.add("Solo_tracks_assoc", "pT", {HistType::kTH1D, {axisPtAssoc}}); + } + } + if (cfgOutputXi || cfgOutputOmega) { + if (!cfgCentTableUnavailable) + registry.add("Invmass", "", {HistType::kTHnSparseF, {{axisPtTrigger, axisInvMass, axisEta, axisCentrality}}}); + } + + registry.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + if (doprocessMCSame && doprocessOntheflySame) { + LOGF(fatal, "Full simulation and on-the-fly processing of same event not supported"); + } + if (doprocessMCMixed && doprocessOntheflyMixed) { + LOGF(fatal, "Full simulation and on-the-fly processing of mixed event not supported"); + } + if (doprocessMCSame || doprocessMCSameCasc) { + registry.add("MCTrue/MCeventcount", "MCeventcount", {HistType::kTH1F, {{5, 0, 5, "bin"}}}); // histogram to see how many events are in the same and mixed event + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(2, "same all"); + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(3, "same reco"); + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(4, "mixed all"); + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(5, "mixed reco"); + registry.add("MCTrue/MCCentrality", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); + registry.add("MCTrue/MCNch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); + registry.add("MCTrue/MCzVtx", "MCzVtx", {HistType::kTH1D, {axisVertex}}); + registry.add("MCTrue/MCPhi", "MCPhi", {HistType::kTH1D, {axisPhi}}); + registry.add("MCTrue/MCEta", "MCEta", {HistType::kTH1D, {axisEta}}); + registry.add("MCTrue/MCpT", "MCpT", {HistType::kTH1D, {axisPtTrigger}}); + registry.add("MCTrue/MCTrig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("MCTrue/MCdeltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + registry.add("MCTrue/MCdeltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); + } + if (doprocessMCEfficiency) { + registry.add("MCEffeventcount", "bin", {HistType::kTH1F, {{5, 0, 5, "bin"}}}); + registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(2, "MC"); + registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(3, "Reco Primary"); + registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(4, "Reco All"); + registry.get(HIST("MCEffeventcount"))->GetXaxis()->SetBinLabel(5, "Fake"); + registry.add("MCGen", "MCGen", {HistType::kTH1D, {axisPtEfficiency}}); + registry.add("MCRec", "MCRec", {HistType::kTH1D, {axisPtEfficiency}}); + registry.add("MCRecPri", "MCRecPri", {HistType::kTH1D, {axisPtEfficiency}}); + } + + LOGF(info, "Initializing correlation container"); + std::vector corrAxis = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEta, "#Delta#eta"}}; + std::vector effAxis = { + {axisEtaEfficiency, "#eta"}, + {axisPtEfficiency, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + }; + std::vector userAxis; + if (cfgOutputXi || cfgOutputOmega) + userAxis.emplace_back(axisInvMass, "m (GeV/c^2)"); + + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + + o2::framework::AxisSpec axisMult = axisMultiplicity; + o2::framework::AxisSpec axisVtx = axisVertex; + int nMultBins = axisMult.binEdges.size() - 1; + int nVtxBins = axisVtx.binEdges.size() - 1; + validCollisions.resize(nMultBins * nVtxBins); + + LOGF(info, "End of init"); + } + + int getMagneticField(uint64_t timestamp) + { + // Get the magnetic field + static o2::parameters::GRPMagField* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("/GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + } + return grpo->getNominalL3Field(); + } + + template + float getCentrality(TCollision const& collision) + { + float cent; + switch (cfgCentEstimator) { + case kCentFT0C: + cent = collision.centFT0C(); + break; + case kCentFT0CVariant1: + cent = collision.centFT0CVariant1(); + break; + case kCentFT0M: + cent = collision.centFT0M(); + break; + case kCentFV0A: + cent = collision.centFV0A(); + break; + default: + cent = collision.centFT0C(); + } + return cent; + } + + template + bool trackSelected(const TTrack& track) + { + if (std::abs(track.eta()) > cfgCutEta) { + return false; + } + if (std::abs(track.pt()) < cfgCutPtMin || std::abs(track.pt()) > cfgCutPtMax) { + return false; + } + if ((track.tpcNClsFound() < cfgCutTPCclu) || (track.tpcNClsCrossedRows() < cfgCutTPCCrossedRows) || (track.itsNCls() < cfgCutITSclu)) { + return false; + } + return true; + } + + template + bool cascSelected(const TTrackCasc& casc, float posX, float posY, float posZ) + { + if (std::abs(casc.eta()) > cfgCutEta) { + return false; + } + if (std::abs(casc.pt()) < cfgCutPtMin || std::abs(casc.pt()) > cfgCutPtMax) { + return false; + } + + auto bachelor = casc.template bachelor_as(); + auto posdau = casc.template posTrack_as(); + auto negdau = casc.template negTrack_as(); + + if (bachelor.pt() < trkQualityOpts.cfgCutPtDauMin.value || bachelor.pt() > trkQualityOpts.cfgCutPtDauMax.value) + return false; + if (posdau.pt() < trkQualityOpts.cfgCutPtDauMin.value || posdau.pt() > trkQualityOpts.cfgCutPtDauMax.value) + return false; + if (negdau.pt() < trkQualityOpts.cfgCutPtDauMin.value || negdau.pt() > trkQualityOpts.cfgCutPtDauMax.value) + return false; + if (std::fabs(bachelor.eta()) > trkQualityOpts.cfgCutEta.value) + return false; + if (std::fabs(posdau.eta()) > trkQualityOpts.cfgCutEta.value) + return false; + if (std::fabs(negdau.eta()) > trkQualityOpts.cfgCutEta.value) + return false; + + // track quality check + if (bachelor.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) + return false; + if (posdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) + return false; + if (negdau.itsNCls() <= trkQualityOpts.cfgMinITSNCls.value) + return false; + if (bachelor.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + return false; + if (posdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + return false; + if (negdau.itsNCls() >= trkQualityOpts.cfgMaxITSNCls.value) + return false; + + if (bachelor.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) + return false; + if (posdau.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) + return false; + if (negdau.tpcNClsFound() <= trkQualityOpts.cfgTPCNCls.value) + return false; + if (bachelor.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) + return false; + if (posdau.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) + return false; + if (negdau.tpcNClsCrossedRows() <= trkQualityOpts.cfgTPCCrossedRows.value) + return false; + + if (cfgOutputXi) { + if (casc.mXi() > massup || casc.mXi() < masslow) + return false; + if (casc.sign() < 0) { + if (std::fabs(bachelor.tpcNSigmaPi()) > cfgNSigma[0]) + return false; + if (std::fabs(posdau.tpcNSigmaPr()) > cfgNSigma[1]) + return false; + if (std::fabs(negdau.tpcNSigmaPi()) > cfgNSigma[0]) + return false; + if (std::fabs(casc.dcapostopv()) < cascBuilderOpts.cfgcasc_dcaLaprtopv.value) + return false; + if (std::fabs(casc.dcanegtopv()) < cascBuilderOpts.cfgcasc_dcaLapitopv.value) + return false; + } else if (casc.sign() > 0) { + if (std::fabs(bachelor.tpcNSigmaPi()) > cfgNSigma[0]) + return false; + if (std::fabs(negdau.tpcNSigmaPr()) > cfgNSigma[1]) + return false; + if (std::fabs(posdau.tpcNSigmaPi()) > cfgNSigma[0]) + return false; + if (std::fabs(casc.dcanegtopv()) < cascBuilderOpts.cfgcasc_dcaLaprtopv.value) + return false; + if (std::fabs(casc.dcapostopv()) < cascBuilderOpts.cfgcasc_dcaLapitopv.value) + return false; + } + if (casc.cascradius() < cascBuilderOpts.cfgcasc_radius.value) + return false; + if (casc.casccosPA(posX, posY, posZ) < cascBuilderOpts.cfgcasc_casccospa.value) + return false; + if (casc.v0cosPA(posX, posY, posZ) < cascBuilderOpts.cfgcasc_v0cospa.value) + return false; + if (std::fabs(casc.dcav0topv(posX, posY, posZ)) < cascBuilderOpts.cfgcasc_dcav0topv.value) + return false; + if (std::fabs(casc.dcabachtopv()) < cascBuilderOpts.cfgcasc_dcabachtopv.value) + return false; + if (casc.dcacascdaughters() > cascBuilderOpts.cfgcasc_dcacascdau.value) + return false; + if (casc.dcaV0daughters() > cascBuilderOpts.cfgcasc_dcav0dau.value) + return false; + if (std::fabs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascBuilderOpts.cfgcasc_mlambdawindow.value) + return false; + } + if (cfgOutputOmega) { + if (casc.mOmega() > massup || casc.mOmega() < masslow) + return false; + if (casc.sign() < 0) { + if (std::fabs(bachelor.tpcNSigmaKa()) > cfgNSigma[2]) + return false; + if (std::fabs(posdau.tpcNSigmaPr()) > cfgNSigma[1]) + return false; + if (std::fabs(negdau.tpcNSigmaPi()) > cfgNSigma[0]) + return false; + if (std::fabs(casc.dcapostopv()) < cascBuilderOpts.cfgcasc_dcaLaprtopv.value) + return false; + if (std::fabs(casc.dcanegtopv()) < cascBuilderOpts.cfgcasc_dcaLapitopv.value) + return false; + } else if (casc.sign() > 0) { + if (std::fabs(bachelor.tpcNSigmaKa()) > cfgNSigma[2]) + return false; + if (std::fabs(negdau.tpcNSigmaPr()) > cfgNSigma[1]) + return false; + if (std::fabs(posdau.tpcNSigmaPi()) > cfgNSigma[0]) + return false; + if (std::fabs(casc.dcanegtopv()) < cascBuilderOpts.cfgcasc_dcaLaprtopv.value) + return false; + if (std::fabs(casc.dcapostopv()) < cascBuilderOpts.cfgcasc_dcaLapitopv.value) + return false; + } + if (casc.cascradius() < cascBuilderOpts.cfgcasc_radius.value) + return false; + if (casc.casccosPA(posX, posY, posZ) < cascBuilderOpts.cfgcasc_casccospa.value) + return false; + if (casc.v0cosPA(posX, posY, posZ) < cascBuilderOpts.cfgcasc_v0cospa.value) + return false; + if (std::fabs(casc.dcav0topv(posX, posY, posZ)) < cascBuilderOpts.cfgcasc_dcav0topv.value) + return false; + if (std::fabs(casc.dcabachtopv()) < cascBuilderOpts.cfgcasc_dcabachtopv.value) + return false; + if (casc.dcacascdaughters() > cascBuilderOpts.cfgcasc_dcacascdau.value) + return false; + if (casc.dcaV0daughters() > cascBuilderOpts.cfgcasc_dcav0dau.value) + return false; + if (std::fabs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascBuilderOpts.cfgcasc_mlambdawindow.value) + return false; + } + return true; + } + + template + bool genTrackSelected(const TTrack& track) + { + if (!track.isPhysicalPrimary()) { + return false; + } + if (!track.producedByGenerator()) { + return false; + } + if (std::abs(track.eta()) > cfgCutEta) { + return false; + } + if (std::abs(track.pt()) < cfgCutPtMin || std::abs(track.pt()) > cfgCutPtMax) { + return false; + } + return true; + } + + void loadCorrection(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (cfgEfficiency.value.empty() == false) { + if (cfgLocalEfficiency > 0) { + TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); + mEfficiency = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); + } else { + mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + } + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiency.value.c_str()); + } + if (cfgEfficiencyCasc.value.empty() == false) { + if (cfgLocalEfficiency > 0) { + TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyCasc.value.c_str(), "READ"); + mEfficiencyCasc = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); + } else { + mEfficiencyCasc = ccdb->getForTimeStamp(cfgEfficiencyCasc, timestamp); + } + if (mEfficiencyCasc == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyCasc.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgEfficiencyCasc.value.c_str()); + } + if (cfgCentralityWeight.value.empty() == false) { + mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + if (mCentralityWeight == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s", cfgCentralityWeight.value.c_str()); + } + correctionsLoaded = true; + } + + bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ, bool isCh = true) + { + if (isCh) { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int zBin = mEfficiency->GetZaxis()->FindBin(posZ); + eff = mEfficiency->GetBinContent(etaBin, ptBin, zBin); + } else { + eff = 1.0; + } + if (eff == 0) + return false; + weight_nue = 1. / eff; + return true; + } else { + float eff = 1.; + if (mEfficiencyCasc) { + int etaBin = mEfficiencyCasc->GetXaxis()->FindBin(eta); + int ptBin = mEfficiencyCasc->GetYaxis()->FindBin(pt); + int zBin = mEfficiencyCasc->GetZaxis()->FindBin(posZ); + eff = mEfficiencyCasc->GetBinContent(etaBin, ptBin, zBin); + } else { + eff = 1.0; + } + if (eff == 0) + return false; + weight_nue = 1. / eff; + return true; + } + } + + bool getCentralityWeight(float& weightCent, const float centrality) + { + float weight = 1.; + if (mCentralityWeight) + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + else + weight = 1.0; + if (weight == 0) + return false; + weightCent = weight; + return true; + } + + // fill multiple histograms + template + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + { + float weff1 = 1; + float vtxz = collision.posZ(); + for (auto const& track1 : tracks) { + if (!trackSelected(track1)) + continue; + if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), vtxz)) + continue; + registry.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + registry.fill(HIST("Eta"), track1.eta()); + registry.fill(HIST("EtaCorrected"), track1.eta(), weff1); + registry.fill(HIST("pT"), track1.pt()); + registry.fill(HIST("pTCorrected"), track1.pt(), weff1); + } + } + + template + float getDPhiStar(TTrack const& track1, TTrackAssoc const& track2, float radius, int magField) + { + float charge1 = track1.sign(); + float charge2 = track2.sign(); + + float phi1 = track1.phi(); + float phi2 = track2.phi(); + + float pt1 = track1.pt(); + float pt2 = track2.pt(); + + int fbSign = (magField > 0) ? 1 : -1; + + float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); + + if (dPhiStar > constants::math::PI) + dPhiStar = constants::math::TwoPI - dPhiStar; + if (dPhiStar < -constants::math::PI) + dPhiStar = -constants::math::TwoPI - dPhiStar; + + return dPhiStar; + } + + template + void fillCorrelations(const TTracks& tracks1, const TCollision& currentCollision, float posZ, int bin, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms (use buffer, only for mixevent) + { + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + if (currentCollision.assocParticles.size() == 0) + return; + // loop over all validCollisions in buffer + for (const auto& collision : validCollisions[bin]) { + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + // Cache efficiency for particles (too many FindBin lookups) + if (mEfficiency) { + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(collision.assocParticles.size()); + for (const auto& track2 : collision.assocParticles) { + float weff = 1.; + getEfficiencyCorrection(weff, track2.eta, track2.pt, posZ); + efficiencyAssociatedCache.push_back(weff); + } + } + + // loop over all tracks + for (const auto& track1 : tracks1) { + if (!trackSelected(track1)) + continue; + if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) + continue; + + int index = 0; + for (const auto& track2 : collision.assocParticles) { + if (mEfficiency) { + associatedWeight = efficiencyAssociatedCache[index]; + index++; + } + if (cfgUsePtOrder && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt) + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi, -PIHalf); + float deltaEta = track1.eta() - track2.eta; + + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + void fillCorrelationsCasc(const TTracks& tracks1, const TCollision& currentCollision, float posX, float posY, float posZ, int bin, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms (use buffer, only for mixevent) + { + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + if (currentCollision.assocParticles.size() == 0) + return; + // loop over all validCollisions in buffer + for (const auto& collision : validCollisions[bin]) { + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + // Cache efficiency for particles (too many FindBin lookups) + if (mEfficiency) { + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(collision.assocParticles.size()); + for (const auto& track2 : collision.assocParticles) { + float weff = 1.; + getEfficiencyCorrection(weff, track2.eta, track2.pt, posZ); + efficiencyAssociatedCache.push_back(weff); + } + } + + // loop over all tracks + for (const auto& track1 : tracks1) { + if (!cascSelected(track1, posX, posY, posZ)) + continue; + if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ, false)) + continue; + + int index = 0; + for (const auto& track2 : collision.assocParticles) { + if (mEfficiency) { + associatedWeight = efficiencyAssociatedCache[index]; + index++; + } + if (cfgUsePtOrder && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt) + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi, -PIHalf); + float deltaEta = track1.eta() - track2.eta; + + if (cfgOutputXi) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt, deltaPhi, deltaEta, track1.mXi(), eventWeight * triggerWeight * associatedWeight); + if (cfgOutputOmega) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt, deltaPhi, deltaEta, track1.mOmega(), eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + // Cache efficiency for particles (too many FindBin lookups) + if (mEfficiency) { + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(tracks2.size()); + for (const auto& track2 : tracks2) { + float weff = 1.; + getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } + + if (!cfgCentTableUnavailable) + registry.fill(HIST("Centrality_used"), cent); + registry.fill(HIST("Nch_used"), tracks1.size()); + + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!trackSelected(track1)) + continue; + if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) + continue; + if (system == SameEvent) { + registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + } + + for (auto const& track2 : tracks2) { + + if (!trackSelected(track2)) + continue; + if (mEfficiency) { + associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; + } + + if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + if (std::abs(deltaEta) < cfgCutMerging) { + + double dPhiStarHigh = getDPhiStar(track1, track2, cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(track1, track2, cfgRadiusLow, magneticField); + + const double kLimit = 3.0 * cfgCutMerging; + + bool bIsBelow = false; + + if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { + for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { + double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); + if (std::abs(dPhiStar) < kLimit) { + bIsBelow = true; + break; + } + } + if (bIsBelow) + continue; + } + } + + // fill the right sparse and histograms + if (system == SameEvent) { + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + void fillCorrelationsCasc(const TTracks& tracks1, const TTracksAssoc& tracks2, float posX, float posY, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + // Cache efficiency for particles (too many FindBin lookups) + if (mEfficiency) { + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(tracks2.size()); + for (const auto& track2 : tracks2) { + float weff = 1.; + getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } + + if (system == SameEvent) { + if (!cfgCentTableUnavailable) + registry.fill(HIST("Centrality_used"), cent); + registry.fill(HIST("Nch_used"), tracks1.size()); + } + + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!cascSelected(track1, posX, posY, posZ)) + continue; + if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ, false)) + continue; + if (system == SameEvent) { + registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + if (!cfgCentTableUnavailable && cfgOutputXi) + registry.fill(HIST("Invmass"), track1.pt(), track1.mXi(), track1.eta(), cent, eventWeight * triggerWeight); + if (!cfgCentTableUnavailable && cfgOutputOmega) + registry.fill(HIST("Invmass"), track1.pt(), track1.mOmega(), track1.eta(), cent, eventWeight * triggerWeight); + } + + auto bachelor = track1.template bachelor_as(); + auto posdau = track1.template posTrack_as(); + auto negdau = track1.template negTrack_as(); + + for (auto const& track2 : tracks2) { + if (!trackSelected(track2)) + continue; + if (mEfficiency) { + associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; + } + + if (!cfgUsePtOrder && bachelor.globalIndex() == track2.globalIndex()) { + continue; // For pt-differential correlations, skip if the trigger bachelor and associate are the same track + } + if (!cfgUsePtOrder && posdau.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger posdau and associate are the same track + if (!cfgUsePtOrder && negdau.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger negdau and associate are the same track + if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + if (std::abs(deltaEta) < cfgCutMerging) { + + double dPhiStarHigh = getDPhiStar(track1, track2, cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(track1, track2, cfgRadiusLow, magneticField); + + const double kLimit = 3.0 * cfgCutMerging; + + bool bIsBelow = false; + + if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { + for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { + double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); + if (std::abs(dPhiStar) < kLimit) { + bIsBelow = true; + break; + } + } + if (bIsBelow) + continue; + } + } + + // fill the right sparse and histograms + if (system == SameEvent) { + registry.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputXi) + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, track1.mXi(), eventWeight * triggerWeight * associatedWeight); + if (cfgOutputOmega) + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, track1.mOmega(), eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + registry.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputXi) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, track1.mXi(), eventWeight * triggerWeight * associatedWeight); + if (cfgOutputOmega) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, track1.mOmega(), eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + void fillCorrelationsExcludeSoloTracks(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + std::vector tracksSkipIndices; + std::vector tracks2SkipIndices; + + findBiasedTracks(tracks1, tracksSkipIndices, posZ); + if (!cfgSingleSoloPtTrack) { // only look for the solo pt tracks if we want to skip both + findBiasedTracks(tracks2, tracks2SkipIndices, posZ); + } + + // Cache efficiency for particles (too many FindBin lookups) + if (mEfficiency) { + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(tracks2.size()); + for (const auto& track2 : tracks2) { + float weff = 1.; + getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } + + if (!cfgCentTableUnavailable) + registry.fill(HIST("Centrality_used"), cent); + registry.fill(HIST("Nch_used"), tracks1.size()); + + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!trackSelected(track1)) + continue; + if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) + continue; + + registry.fill(HIST("Nch_final_pt"), track1.pt()); + + if (std::find(tracksSkipIndices.begin(), tracksSkipIndices.end(), track1.globalIndex()) != tracksSkipIndices.end()) { + registry.fill(HIST("Solo_tracks_trigger"), track1.pt()); + continue; // Skip the track1 if it is alone in pt bin + } + registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + + for (auto const& track2 : tracks2) { + + if (!trackSelected(track2)) + continue; + if (mEfficiency) { + associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; + } + if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + if (cfgUsePtOrder && track1.pt() <= track2.pt()) + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + if (!cfgSingleSoloPtTrack) { // avoid skipping the second track if we only want one + if (std::find(tracks2SkipIndices.begin(), tracks2SkipIndices.end(), track2.globalIndex()) != tracks2SkipIndices.end()) { + registry.fill(HIST("Solo_tracks_assoc"), track2.pt()); + continue; // Skip the track2 if it is alone in pt bin + } + } + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + if (std::abs(deltaEta) < cfgCutMerging) { + + double dPhiStarHigh = getDPhiStar(track1, track2, cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(track1, track2, cfgRadiusLow, magneticField); + + const double kLimit = 3.0 * cfgCutMerging; + + bool bIsBelow = false; + + if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { + for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { + double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); + if (std::abs(dPhiStar) < kLimit) { + bIsBelow = true; + break; + } + } + if (bIsBelow) + continue; + } + } + + // fill the right sparse and histograms + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + + template + void fillMCCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track1.isPhysicalPrimary()) + continue; + if (doprocessOntheflySame && !genTrackSelected(track1)) + continue; + + if (system == SameEvent && (doprocessMCSame || doprocessOntheflySame)) + registry.fill(HIST("MCTrue/MCTrig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + + for (auto const& track2 : tracks2) { + + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track2.isPhysicalPrimary()) + continue; + if (doprocessOntheflyMixed && !genTrackSelected(track2)) + continue; + + if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + // fill the right sparse and histograms + if (system == SameEvent) { + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (doprocessMCSame || doprocessOntheflySame) + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (doprocessMCMixed || doprocessOntheflyMixed) + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + void fillMCCorrelationsCasc(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + double fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track1.isPhysicalPrimary()) + continue; + + if (!((cfgOutputXi && getSpecies(track1.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(track1.pdgCode()) == getSpecies(PDG_t::kOmegaMinus)))) + continue; + + if (system == SameEvent && (doprocessMCSameCasc)) + registry.fill(HIST("MCTrue/MCTrig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + + for (auto const& track2 : tracks2) { + + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track2.isPhysicalPrimary()) + continue; + + if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + // fill the right sparse and histograms + if (system == SameEvent) { + if (doprocessMCSameCasc || doprocessOntheflySame) + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputXi) + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassXiMinus, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputOmega) + same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassOmegaMinus, eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + if (doprocessMCMixedCasc || doprocessOntheflyMixed) + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputXi) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassXiMinus, eventWeight * triggerWeight * associatedWeight); + if (cfgOutputOmega) + mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, o2::constants::physics::MassOmegaMinus, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const bool fillCounter) + { + registry.fill(HIST("hEventCountSpecific"), 0.5); + if (cfgEvSel.cfgEvSelkNoSameBunchPileup.value && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkNoSameBunchPileup.value) + registry.fill(HIST("hEventCountSpecific"), 1.5); + if (cfgEvSel.cfgEvSelkNoITSROFrameBorder.value && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkNoITSROFrameBorder.value) + registry.fill(HIST("hEventCountSpecific"), 2.5); + if (cfgEvSel.cfgEvSelkNoTimeFrameBorder.value && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkNoTimeFrameBorder.value) + registry.fill(HIST("hEventCountSpecific"), 3.5); + if (cfgEvSel.cfgEvSelkIsGoodZvtxFT0vsPV.value && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkIsGoodZvtxFT0vsPV.value) + registry.fill(HIST("hEventCountSpecific"), 4.5); + if (cfgEvSel.cfgEvSelkNoCollInTimeRangeStandard.value && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // no collisions in specified time range + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkNoCollInTimeRangeStandard.value) + registry.fill(HIST("hEventCountSpecific"), 5.5); + if (cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + // from Jan 9 2025 AOT meeting + // cut time intervals with dead ITS staves + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value) + registry.fill(HIST("hEventCountSpecific"), 6.5); + if (cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // no other collisions in this Readout Frame with per-collision multiplicity above threshold + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkIsGoodITSLayersAll.value) + registry.fill(HIST("hEventCountSpecific"), 7.5); + if (cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // veto an event if FT0C amplitude in previous ITS ROF is above threshold + return 0; + } + if (fillCounter && cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) + registry.fill(HIST("hEventCountSpecific"), 8.5); + auto occupancy = collision.trackOccupancyInTimeRange(); + if (cfgEvSel.cfgEvSelOccupancy.value && (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh)) + return 0; + if (fillCounter && cfgEvSel.cfgEvSelOccupancy.value) + registry.fill(HIST("hEventCountSpecific"), 9.5); + + auto multNTracksPV = collision.multNTracksPV(); + if (cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled && !cfgCentTableUnavailable) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(centrality)) + return 0; + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(centrality)) + return 0; + } + if (cfgFuncParas.cfgMultT0CCutEnabled && !cfgCentTableUnavailable) { + if (multTrk < cfgFuncParas.fMultT0CCutLow->Eval(centrality)) + return 0; + if (multTrk > cfgFuncParas.fMultT0CCutHigh->Eval(centrality)) + return 0; + } + if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { + if (multTrk < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) + return 0; + if (multTrk > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) + return 0; + } + if (cfgFuncParas.cfgMultMultV0ACutEnabled) { + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(multTrk)) + return 0; + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(multTrk)) + return 0; + } + } + if (fillCounter && cfgEvSel.cfgEvSelkNoHighMultCollInPrevRof.value) + registry.fill(HIST("hEventCountSpecific"), 10.5); + + // V0A T0A 5 sigma cut + float sigma = 5.0; + if (cfgEvSel.cfgEvSelV0AT0ACut.value && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) + return 0; + if (fillCounter && cfgEvSel.cfgEvSelV0AT0ACut.value) + registry.fill(HIST("hEventCountSpecific"), 11.5); + + return 1; + } + + void findBiasedTracks(FilteredTracks const& tracks, std::vector& tracksSkipIndices, float posZ) + { + // Find the tracks that are alone in their pT bin + tracksSkipIndices.clear(); + static const std::vector& ptBins = axisPtTrigger.value; + static const size_t nPtBins = ptBins.size() - 1; + + std::vector numberOfTracksInBin(nPtBins, 0); + std::vector firstTrackIndex(nPtBins, -1); + + float triggerWeight = 1.0f; + + // Count tracks per bin and remember the first track id in each bin + for (const auto& track : tracks) { + if (!trackSelected(track)) + continue; + if (!getEfficiencyCorrection(triggerWeight, track.eta(), track.pt(), posZ)) + continue; + double pt = track.pt(); + auto binEdgeIt = std::upper_bound(ptBins.begin(), ptBins.end(), pt); + if (binEdgeIt == ptBins.begin() || binEdgeIt == ptBins.end()) + continue; // skip pt bins out of range + + size_t binIndex = static_cast(std::distance(ptBins.begin(), binEdgeIt) - 1); + + if (numberOfTracksInBin[binIndex] == 0) { + firstTrackIndex[binIndex] = track.globalIndex(); + } + ++numberOfTracksInBin[binIndex]; + } + + // Collect track ids for bins that have exactly one track + for (size_t i = 0; i < nPtBins; ++i) { + if (numberOfTracksInBin[i] == 1) { + tracksSkipIndices.push_back(firstTrackIndex[i]); + } + } + } + + void processSame(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::CascDatas const& cascades, aod::BCsWithTimestamps const&, DaughterTracks const&) + { + if (!collision.sel8()) + return; + auto bc = collision.bc_as(); + float cent = -1.; + float weightCent = 1.0f; + if (!cfgCentTableUnavailable) { + cent = getCentrality(collision); + } + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, true)) + return; + loadCorrection(bc.timestamp()); + if (!cfgCentTableUnavailable) { + getCentralityWeight(weightCent, cent); + registry.fill(HIST("Centrality"), cent); + registry.fill(HIST("CentralityWeighted"), cent, weightCent); + } + registry.fill(HIST("Nch"), tracks.size()); + registry.fill(HIST("zVtx"), collision.posZ()); + + if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + return; + } + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + return; + } + + registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + fillYield(collision, tracks); + + same->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); + if (!cfgSoloPtTrack) { + if (!cfgOutputOmega && !cfgOutputXi) + fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + else + fillCorrelationsCasc(cascades, tracks, collision.posX(), collision.posY(), collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + } else { + fillCorrelationsExcludeSoloTracks(tracks, tracks, collision.posZ(), getMagneticField(bc.timestamp()), cent, weightCent); + } + } + PROCESS_SWITCH(CascDiHadronCorr, processSame, "Process same event", true); + + // the process for filling the mixed events + void processMixed(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + if (!collision1.sel8() || !collision2.sel8()) + continue; + + if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + continue; + + if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + continue; + + float cent1 = -1; + float cent2 = -1; + if (!cfgCentTableUnavailable) { + cent1 = getCentrality(collision1); + cent2 = getCentrality(collision2); + } + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision1, tracks1.size(), cent1, false)) + continue; + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision2, tracks2.size(), cent2, false)) + continue; + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + continue; + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + continue; + + registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto bc = collision1.bc_as(); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + if (cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + if (!cfgCentTableUnavailable) + getCentralityWeight(weightCent, cent1); + + if (!cfgOutputOmega && !cfgOutputXi) + fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + } + } + + PROCESS_SWITCH(CascDiHadronCorr, processMixed, "Process mixed events", false); + + void processMixedCasc(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::CascDatas const& cascades, aod::BCsWithTimestamps const&, DaughterTracks const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(cascades, tracks); + Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + if (!collision1.sel8() || !collision2.sel8()) + continue; + + if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + continue; + + if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + continue; + + float cent1 = -1; + float cent2 = -1; + if (!cfgCentTableUnavailable) { + cent1 = getCentrality(collision1); + cent2 = getCentrality(collision2); + } + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision1, tracks1.size(), cent1, false)) + continue; + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision2, tracks2.size(), cent2, false)) + continue; + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + continue; + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + continue; + + registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto bc = collision1.bc_as(); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + if (cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + if (!cfgCentTableUnavailable) + getCentralityWeight(weightCent, cent1); + + fillCorrelationsCasc(tracks1, tracks2, collision1.posX(), collision1.posY(), collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + } + } + PROCESS_SWITCH(CascDiHadronCorr, processMixedCasc, "Process mixed events", true); + + // the process for filling the mixed events by buffer + void processMixedBuffer(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks) + { + ValidCollision currentCollision; + int nBinsMult = 0; + int binMult = 0; + int nBinsVtxZ = 0; + int binVtxZ = 0; + currentCollision.pvz = collision.posZ(); + currentCollision.mult = tracks.size(); + + nBinsMult = registry.get(HIST("Nch"))->GetNbinsX(); + binMult = registry.get(HIST("Nch"))->GetXaxis()->FindBin(tracks.size()) - 1; + nBinsVtxZ = registry.get(HIST("zVtx"))->GetNbinsX(); + binVtxZ = registry.get(HIST("zVtx"))->GetXaxis()->FindBin(collision.posZ()) - 1; + if (binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + return; + int bin = binMult * nBinsVtxZ + binVtxZ; + + if (!collision.sel8()) + return; + + if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) + return; + + float cent = -1; + if (!cfgCentTableUnavailable) { + cent = getCentrality(collision); + } + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, false)) + return; + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) + return; + + float weightCent = 1.0f; + if (!cfgCentTableUnavailable) + getCentralityWeight(weightCent, cent); + + for (const auto& track : tracks) { + if (!trackSelected(track)) + continue; + currentCollision.addValidParticle(track.eta(), track.phi(), track.pt()); + } + + fillCorrelations(tracks, currentCollision, collision.posZ(), bin, weightCent); + if (validCollisions[bin].size() >= static_cast(cfgMixEventNumMin)) { + validCollisions[bin].erase(validCollisions[bin].begin()); + } + validCollisions[bin].push_back(currentCollision); + } + + PROCESS_SWITCH(CascDiHadronCorr, processMixedBuffer, "Process mixed events", false); + + void processMixedBufferCasc(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::CascDatas const& cascades, DaughterTracks const&) + { + ValidCollision currentCollision; + int nBinsMult = 0; + int binMult = 0; + int nBinsVtxZ = 0; + int binVtxZ = 0; + currentCollision.pvz = collision.posZ(); + currentCollision.mult = tracks.size(); + + nBinsMult = registry.get(HIST("Nch"))->GetNbinsX(); + binMult = registry.get(HIST("Nch"))->GetXaxis()->FindBin(tracks.size()) - 1; + nBinsVtxZ = registry.get(HIST("zVtx"))->GetNbinsX(); + binVtxZ = registry.get(HIST("zVtx"))->GetXaxis()->FindBin(collision.posZ()) - 1; + if (binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + return; + int bin = binMult * nBinsVtxZ + binVtxZ; + + if (!collision.sel8()) + return; + + if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) + return; + + float cent = -1; + if (!cfgCentTableUnavailable) { + cent = getCentrality(collision); + } + if (cfgEvSel.cfgUseAdditionalEventCut.value && !eventSelected(collision, tracks.size(), cent, false)) + return; + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) + return; + + float weightCent = 1.0f; + if (!cfgCentTableUnavailable) + getCentralityWeight(weightCent, cent); + + for (const auto& track : tracks) { + if (!trackSelected(track)) + continue; + currentCollision.addValidParticle(track.eta(), track.phi(), track.pt()); + } + + fillCorrelationsCasc(cascades, currentCollision, collision.posX(), collision.posY(), collision.posZ(), bin, weightCent); + if (validCollisions[bin].size() >= static_cast(cfgMixEventNumMin)) { + validCollisions[bin].erase(validCollisions[bin].begin()); + } + validCollisions[bin].push_back(currentCollision); + } + PROCESS_SWITCH(CascDiHadronCorr, processMixedBufferCasc, "Process mixed events", true); + + int getSpecies(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kXiMinus: // Xi + return 1; + case PDG_t::kOmegaMinus: // Omega + return 2; + default: // NOTE. The efficiency histogram is hardcoded to contain 4 species. Anything special will have the last slot. + return 3; + } + } + + void processMCEfficiency(FilteredMcCollisions::iterator const& mcCollision, soa::SmallGroups> const& collisions, soa::Join const& Cascades, FilteredMcParticles const& mcParticles, DaughterTracks const&) + { + registry.fill(HIST("MCEffeventcount"), 0.5); + if (cfgSelCollByNch && (mcParticles.size() < cfgCutMultMin || mcParticles.size() >= cfgCutMultMax)) { + return; + } + // Primaries + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary()) { + if ((cfgOutputXi && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kOmegaMinus))) { + registry.fill(HIST("MCEffeventcount"), 1.5); + registry.fill(HIST("MCGen"), mcParticle.pt()); + same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), 0., mcCollision.posZ()); + } + } + } + for (const auto& collision : collisions) { + auto groupedCascades = Cascades.sliceBy(perCollision, collision.globalIndex()); + if (cfgVerbosity) { + LOGF(info, " Reconstructed collision at vtx-z = %f", collision.posZ()); + LOGF(info, " which has %d tracks", groupedCascades.size()); + } + + for (const auto& casc : groupedCascades) { + if (!cascSelected(casc, collision.posX(), collision.posY(), collision.posZ())) + continue; + if (casc.has_mcParticle()) { + auto mcParticle = casc.mcParticle_as(); + if (mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("MCEffeventcount"), 2.5); + if ((cfgOutputXi && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kOmegaMinus))) + registry.fill(HIST("MCRecPri"), mcParticle.pt()); + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, mcParticle.eta(), mcParticle.pt(), getSpecies(mcParticle.pdgCode()), 0., mcCollision.posZ()); + } + registry.fill(HIST("MCEffeventcount"), 3.5); + if ((cfgOutputXi && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kXiMinus)) || (cfgOutputOmega && getSpecies(mcParticle.pdgCode()) == getSpecies(PDG_t::kOmegaMinus))) + registry.fill(HIST("MCRec"), mcParticle.pt()); + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, mcParticle.eta(), mcParticle.pt(), (cfgOutputXi * getSpecies(PDG_t::kXiMinus) + cfgOutputOmega * getSpecies(PDG_t::kOmegaMinus)), 0., mcCollision.posZ()); + } else { + // fake casc + registry.fill(HIST("MCEffeventcount"), 4.5); + same->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, casc.eta(), casc.pt(), 0, 0., mcCollision.posZ()); + } + } + } + } + PROCESS_SWITCH(CascDiHadronCorr, processMCEfficiency, "MC: Extract efficiencies", false); + + void processMCSame(FilteredMcCollisions::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + if (cfgVerbosity) { + LOGF(info, "processMCSame. MC collision: %d, particles: %d, collisions: %d", mcCollision.globalIndex(), mcParticles.size(), collisions.size()); + } + + float cent = -1; + if (!cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + } + + if (cfgSelCollByNch && (mcParticles.size() < cfgCutMultMin || mcParticles.size() >= cfgCutMultMax)) { + return; + } + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + if (!cfgCentTableUnavailable) + registry.fill(HIST("MCTrue/MCCentrality"), cent); + registry.fill(HIST("MCTrue/MCNch"), mcParticles.size()); + registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), mcParticle.phi()); + registry.fill(HIST("MCTrue/MCEta"), mcParticle.eta()); + registry.fill(HIST("MCTrue/MCpT"), mcParticle.pt()); + } + } + + if (cfgUseCFStepAll) { + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepAll); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + + if (collisions.size() == 0) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), 2.5); + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepTrackedOnlyPrim); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + PROCESS_SWITCH(CascDiHadronCorr, processMCSame, "Process MC same event", false); + + void processMCSameCasc(FilteredMcCollisions::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + if (cfgVerbosity) { + LOGF(info, "processMCSame. MC collision: %d, particles: %d, collisions: %d", mcCollision.globalIndex(), mcParticles.size(), collisions.size()); + } + + float cent = -1; + if (!cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + } + + if (cfgSelCollByNch && (mcParticles.size() < cfgCutMultMin || mcParticles.size() >= cfgCutMultMax)) { + return; + } + if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + if (!cfgCentTableUnavailable) + registry.fill(HIST("MCTrue/MCCentrality"), cent); + registry.fill(HIST("MCTrue/MCNch"), mcParticles.size()); + registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), mcParticle.phi()); + registry.fill(HIST("MCTrue/MCEta"), mcParticle.eta()); + registry.fill(HIST("MCTrue/MCpT"), mcParticle.pt()); + } + } + + if (cfgUseCFStepAll) { + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepAll); + fillMCCorrelationsCasc(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + + if (collisions.size() == 0) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), 2.5); + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepTrackedOnlyPrim); + fillMCCorrelationsCasc(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + PROCESS_SWITCH(CascDiHadronCorr, processMCSameCasc, "Process MC same event", false); + + void processMCMixed(FilteredMcCollisions const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + continue; + + if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + continue; + + auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + if (cfgVerbosity > 0) { + LOGF(info, "Found %d related collisions", groupedCollisions.size()); + } + float cent = -1; + if (!cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions) { + cent = getCentrality(collision); + } + } + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && groupedCollisions.size() != 0 && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) + continue; + + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + float eventWeight = 1.0f; + if (cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + + if (cfgUseCFStepAll) + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + + if (groupedCollisions.size() == 0) { + continue; + } + + registry.fill(HIST("MCTrue/MCeventcount"), 4.5); + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + } + } + PROCESS_SWITCH(CascDiHadronCorr, processMCMixed, "Process MC mixed events", false); + + void processMCMixedCasc(FilteredMcCollisions const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + continue; + + if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + continue; + + auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + if (cfgVerbosity > 0) { + LOGF(info, "Found %d related collisions", groupedCollisions.size()); + } + float cent = -1; + if (!cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions) { + cent = getCentrality(collision); + } + } + + if (!cfgSelCollByNch && !cfgCentTableUnavailable && groupedCollisions.size() != 0 && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) + continue; + + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + float eventWeight = 1.0f; + if (cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + + if (cfgUseCFStepAll) + fillMCCorrelationsCasc(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + + if (groupedCollisions.size() == 0) { + continue; + } + + registry.fill(HIST("MCTrue/MCeventcount"), 4.5); + fillMCCorrelationsCasc(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + } + } + PROCESS_SWITCH(CascDiHadronCorr, processMCMixedCasc, "Process MC mixed events", false); + + void processOntheflySame(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) + { + if (cfgVerbosity) { + LOGF(info, "processOntheflySame. MC collision: %d, particles: %d", mcCollision.globalIndex(), mcParticles.size()); + } + + if (cfgSelCollByNch && (mcParticles.size() < cfgCutMultMin || mcParticles.size() >= cfgCutMultMax)) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + registry.fill(HIST("MCTrue/MCNch"), mcParticles.size()); + registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), mcParticle.phi()); + registry.fill(HIST("MCTrue/MCEta"), mcParticle.eta()); + registry.fill(HIST("MCTrue/MCpT"), mcParticle.pt()); + } + } + + if (cfgUseCFStepAll) { + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepAll); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + + same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepTrackedOnlyPrim); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f); + } + PROCESS_SWITCH(CascDiHadronCorr, processOntheflySame, "Process on-the-fly same event", false); + + void processOntheflyMixed(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) + { + auto getTracksSize = [&mcParticles, this](aod::McCollisions::iterator const& mcCollision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + continue; + + if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + continue; + + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + float eventWeight = 1.0f; + if (cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + + if (cfgUseCFStepAll) + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight); + } + } + PROCESS_SWITCH(CascDiHadronCorr, processOntheflyMixed, "Process on-the-fly mixed events", false); +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/chargeBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/chargeBalanceFunction.cxx new file mode 100644 index 00000000000..98cd608aa39 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/chargeBalanceFunction.cxx @@ -0,0 +1,804 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file chargeBalanceFunction.cxx +/// \brief R2 P2 and BF of charged hadrons. +/// \author Yash Patley + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; +using namespace o2::constants::math; + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; +using namespace o2::constants::math; + +enum ParticlePairType { + kPM = 0, + kPP, + kMM +}; + +enum RecGenType { + kRec = 0, + kGen +}; + +enum DMCType { + kData = 0, + kMC +}; + +struct ChargeBalanceFunction { + // Collisions + Configurable cZVtxCut{"cZVtxCut", 10.0, "Min VtxZ cut"}; + Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; + Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; + Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; + Configurable cMinOccupancy{"cMinOccupancy", 0, "Minimum FT0C Occupancy"}; + Configurable cMaxOccupancy{"cMaxOccupancy", 1e6, "Maximum FT0C Occupancy"}; + + // Tracks + Configurable cTrackNPtBins{"cTrackNPtBins", 20, "N pT bins"}; + Configurable cTrackMinPt{"cTrackMinPt", 0.2, "p_{T} minimum"}; + Configurable cTrackMaxPt{"cTrackMaxPt", 2.0, "p_{T} maximum"}; + Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; + Configurable cTrackGlobal{"cTrackGlobal", true, "Global Track"}; + Configurable cTrackDcaXYCut{"cTrackDcaXYCut", 0.1, "DcaXY Cut"}; + Configurable cTrackDcaZCut{"cTrackDcaZCut", 1., "DcaZ Cut"}; + Configurable cTpcElRejCutMin{"cTpcElRejCutMin", -3., "Electron Rejection Cut Minimum"}; + Configurable cTpcElRejCutMax{"cTpcElRejCutMax", 5., "Electron Rejection Cut Maximum"}; + Configurable cTpcRejCut{"cTpcRejCut", 3, "TPC Rej Cut"}; + + // Correlation + Configurable cNEtaBins{"cNEtaBins", 16, "N Eta Bins"}; + Configurable cNPhiBins{"cNPhiBins", 36, "N Phi Bins"}; + + // Femtoscopic correction + Configurable cApplyFemtoSel{"cApplyFemtoSel", false, "Femto qinv selection"}; + Configurable cFemtoCut{"cFemtoCut", 0.1, "Kaon--Lambda Femto qinv cut"}; + + // Two-track cuts + Configurable cApplyTwoTrackCut{"cApplyTwoTrackCut", false, "Flag for two track cut"}; + Configurable cDEtaCut{"cDEtaCut", 0.02, "DEta cut"}; + Configurable cDPhiStarCut{"cDPhiStarCut", 0.02, "DPhiStar cut"}; + + // Efficiency Correction + Configurable cGetCorrectionFlag{"cGetCorrectionFlag", false, "Apply correction flag"}; + Configurable cGetNuaCorrectionFlag{"cGetNuaCorrectionFlag", false, "Apply NUA correction flag"}; + Configurable cDoEffNuaCorrection{"cDoEffNuaCorrection", false, "Do NUE x NUA correction"}; + + // CCDB + Configurable cUrlCCDB{"cUrlCCDB", "http://alice-ccdb.cern.ch", "ALICE CCDB URL"}; + Configurable cPathCCDBRecoEff{"cPathCCDBRecoEff", "Users/y/ypatley/CBF/Test/RecoEfficiency", "Path for ccdb-object for reco efficiency"}; + Configurable cPathCCDBNuaCorr{"cPathCCDBNuaCorr", "Users/y/ypatley/CBF/Test/Nua", "Path for ccdb-object for NUA correction"}; + Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + + // Configurable Axis + ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Variable Centrality Bins"}; + ConfigurableAxis cPosZBins{"cPosZBins", {VARIABLE_WIDTH, -10.f, -8.0f, -6.0f, -4.0f, -2.0f, 0.f, 2.0f, 4.0f, 6.0f, 8.0f, 10.f}, "Variable Vz Bins"}; + + // Ccdb service + Service ccdb{}; + o2::parameters::GRPMagField* grpo = nullptr; + + // Pdg service + Service pdg{}; + + // Histogram Registry. + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Initialize global variables + float nrapbins = 0.; + float kminrap = 0.; + float kmaxrap = 0.; + float nphibins = 0.; + float kminphi = 0.; + float kmaxphi = TwoPI; + float rapbinwidth = 0.; + float phibinwidth = 0.; + float q = 0., e = 0., qinv = 0.; + float posz = 0., cent = 0., mult = 0.; + float magField = 0.; + std::array vTpcRadii = {0.8, 0.10, 0.12, 0.14, 0.16, 0.18, 0.20, 0.22, 0.24}; + + // Efficiency object + TList* ccdbObjRecoEff = nullptr; + TList* ccdbObjNuaCorr = nullptr; + struct CorrHist { + THnSparseF* hRecEffP = nullptr; + THnSparseF* hRecEffM = nullptr; + TH3F* hNuaP = nullptr; + TH3F* hNuaM = nullptr; + } corrHist; + int nHistDim = 0; + + void init(InitContext const&) + { + // Set CCDB url + ccdb->setURL(cUrlCCDB.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(nolaterthan.value); + + // Set Density Histogram Attributes + nrapbins = static_cast(cNEtaBins); + kminrap = static_cast(-cTrackEtaCut); + kmaxrap = static_cast(cTrackEtaCut); + nphibins = static_cast(cNPhiBins); + + rapbinwidth = (kmaxrap - kminrap) / nrapbins; + phibinwidth = (kmaxphi - kminphi) / nphibins; + + int knrapphibins = static_cast(cNEtaBins) * static_cast(cNPhiBins); + float kminrapphi = 0.; + float kmaxrapphi = knrapphibins; + + // Initialize axis specifications + const AxisSpec axisCheck(1, 0, 1, ""); + const AxisSpec axisChMult(200, 0, 200, "N_{ch}"); + const AxisSpec axisVz(220, -11, 11, "V_{z} (cm)"); + + const AxisSpec axisCent(cCentBins, "FT0C (%)"); + const AxisSpec axisPosZ(cPosZBins, "V_{z} (cm)"); + + const AxisSpec axisDEta(320, -1.6, 1.6, "#Delta#eta"); + const AxisSpec axisDPhi(640, -PIHalf, 3. * PIHalf, "#Delta#varphi"); + + const AxisSpec axisTrackDcaXY{60, -0.15, 0.15, "DCA_{XY}"}; + const AxisSpec axisTrackDcaZ{230, -1.15, 1.15, "DCA_{Z}"}; + const AxisSpec axisdEdx(360, 20, 200, "#frac{dE}{dx}"); + + const AxisSpec axisTrackPt(cTrackNPtBins, cTrackMinPt, cTrackMaxPt, "p_{T} (GeV/#it{c})"); + const AxisSpec axisTrackEta(cNEtaBins, -cTrackEtaCut, cTrackEtaCut, "#eta"); + const AxisSpec axisTrackPhi(cNPhiBins, 0., TwoPI, "#varphi (rad)"); + + const AxisSpec axisRapPhi(knrapphibins, kminrapphi, kmaxrapphi, "#eta#varphi"); + + // Create Histograms + // Event histograms + histos.add("Event/h1f_collision_cent", "FT0C(%)", kTH1F, {axisCent}); + histos.add("Event/h1f_collision_Vz", "V_{z}-distribution", kTH1F, {axisVz}); + + // Track QA + histos.add("TrackQA/hPtDcaXY", "DCA_{XY} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaXY}); + histos.add("TrackQA/hPtDcaZ", "DCA_{Z} vs p_{T}", kTH2F, {axisTrackPt, axisTrackDcaZ}); + histos.add("TrackQA/hTrackTPCdEdX", "hTrackTPCdEdX", kTH2F, {axisTrackPt, axisdEdx}); + + // Two track cut + histos.add("QA/TwoTrackCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/TwoTrackCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/FemtoCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/FemtoCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + + // Efficiency Histograms + // Single Particle Efficiencies + histos.add("Reco/Efficiency/h1f_n1_pt_P", "#rho_{1}^{#plus}", kTH1F, {axisTrackPt}); + histos.add("Reco/Efficiency/h1f_n1_pt_M", "#rho_{1}^{#minus}", kTH1F, {axisTrackPt}); + histos.add("Reco/Efficiency/h4f_n1_centvzptrap_P", "#rho_{1}^{#plus}", kTHnSparseF, {axisCent, axisPosZ, axisTrackPt, axisTrackEta}); + histos.add("Reco/Efficiency/h4f_n1_centvzptrap_M", "#rho_{1}^{#minus}", kTHnSparseF, {axisCent, axisPosZ, axisTrackPt, axisTrackEta}); + + // NUA phi + histos.add("Reco/NUA/h3f_n1_vzrapphi_P", "#rho_{1}^{#plus}", kTH3F, {axisPosZ, axisTrackEta, axisTrackPhi}); + histos.add("Reco/NUA/h3f_n1_vzrapphi_M", "#rho_{1}^{#minus}", kTH3F, {axisPosZ, axisTrackEta, axisTrackPhi}); + + // Correction checks + histos.add("Reco/h1f_n1_pt_P", "#rho_{1}^{#plus}", kTH1F, {axisTrackPt}); + histos.add("Reco/h1f_n1_pt_M", "#rho_{1}^{#minus}", kTH1F, {axisTrackPt}); + histos.add("Reco/h1f_n1_rap_P", "#rho_{1}^{#plus}", kTH1F, {axisTrackEta}); + histos.add("Reco/h1f_n1_rap_M", "#rho_{1}^{#minus}", kTH1F, {axisTrackEta}); + histos.add("Reco/h1f_n1_phi_P", "#rho_{1}^{#plus}", kTH1F, {axisTrackPhi}); + histos.add("Reco/h1f_n1_phi_M", "#rho_{1}^{#minus}", kTH1F, {axisTrackPhi}); + + // Single and Two Particle Densities + // Rho1 for R2 RapPhi + histos.add("Reco/h3f_n1_rapphi_P", "#rho_{1}^{#plus}", kTH3F, {axisCent, axisTrackEta, axisTrackPhi}); + histos.add("Reco/h3f_n1_rapphi_M", "#rho_{1}^{#minus}", kTH3F, {axisCent, axisTrackEta, axisTrackPhi}); + + // Rho1 for P2 pT + histos.add("Reco/h2f_n1_pt_P", "#rho_{1}^{#plus}", kTH2F, {axisCent, axisTrackPt}); + histos.add("Reco/h2f_n1_pt_M", "#rho_{1}^{#minus}", kTH2F, {axisCent, axisTrackPt}); + + // Rho1 for P2 RapPhi + histos.add("Reco/h3f_pt_rapphi_P", "#rho_{1}^{#plus}", kTH3F, {axisCent, axisTrackEta, axisTrackPhi}); + histos.add("Reco/h3f_pt_rapphi_M", "#rho_{1}^{#minus}", kTH3F, {axisCent, axisTrackEta, axisTrackPhi}); + + // Rho2 for R2 RapPhi + histos.add("Reco/h3f_n2_rapphi_PM", "#rho_{2}^{#plus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_n2_rapphi_PP", "#rho_{2}^{#plus#plus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_n2_rapphi_MM", "#rho_{2}^{#minus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + + // Rho2 for P2 RapPhi + histos.add("Reco/h3f_ptpt_rapphi_PM", "#rho_{2}^{#plus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_ptpt_rapphi_PP", "#rho_{2}^{#plus#plus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_ptpt_rapphi_MM", "#rho_{2}^{#minus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_npt_rapphi_PM", "#rho_{2}^{#plus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_npt_rapphi_PP", "#rho_{2}^{#plus#plus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_npt_rapphi_MM", "#rho_{2}^{#minus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_ptn_rapphi_PM", "#rho_{2}^{#plus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_ptn_rapphi_PP", "#rho_{2}^{#plus#plus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_ptn_rapphi_MM", "#rho_{2}^{#minus#minus}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + + histos.addClone("Reco/", "McGen/"); + + // MC Generated Histograms + if (doprocessMCRecoGen) { + // McGen Histos + histos.add("McGen/h1f_collision_recgen", "# of Reco Collision Associated to One Mc Generator Collision", kTH1F, {axisChMult}); + histos.add("McGen/h2f_collision_posZ", "V_{z}-distribution", kTH2F, {axisVz, axisVz}); + histos.add("McGen/h2f_collision_cent", "FT0M Centrality", kTH2F, {axisCent, axisCent}); + } + + // Load correction factor + if (cGetCorrectionFlag) { + // Set CCDB url + ccdb->setURL(cUrlCCDB.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(nolaterthan.value); + + // Get CCDB object + ccdbObjRecoEff = ccdb->getForTimeStamp(cPathCCDBRecoEff.value, nolaterthan.value); + ccdbObjNuaCorr = ccdb->getForTimeStamp(cPathCCDBNuaCorr.value, nolaterthan.value); + + // Load reco eff corrections + loadRecoEfficiencyHistograms(); + } + } + + // Load reco efficiency histograms + void loadRecoEfficiencyHistograms() + { + // Efficiency correction histograms + corrHist.hRecEffP = dynamic_cast(ccdbObjRecoEff->FindObject("h_RecEff_P")); + corrHist.hRecEffM = dynamic_cast(ccdbObjRecoEff->FindObject("h_RecEff_M")); + if (!corrHist.hRecEffP || !corrHist.hRecEffM) { + LOGF(fatal, "CCDB efficiency object doesn't exist !"); + } + nHistDim = corrHist.hRecEffP->GetNdimensions(); + LOGF(info, "Efficiency correction histogram dimensions: %d", nHistDim); + + // Nua correction histograms + corrHist.hNuaP = dynamic_cast(ccdbObjNuaCorr->FindObject("h_Nua_P")); + corrHist.hNuaM = dynamic_cast(ccdbObjNuaCorr->FindObject("h_Nua_M")); + if (!corrHist.hNuaP || !corrHist.hNuaM) { + LOGF(fatal, "CCDB NUA object doesn't exist !"); + } + } + + // Get magnetic field + float getMagneticField(int64_t const& timestamp) + { + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 1; + } + } + auto field = std::lround(5.f * grpo->getL3Current() / 30000.f); + return 0.1 * field; + } + + template + bool selCollision(C const& col) + { + posz = col.posZ(); + if (std::abs(posz) >= cZVtxCut) { // VtxZ selection + return false; + } + + if (cSel8Trig && !col.sel8()) { // Sel8 selection + return false; + } + + cent = col.centFT0C(); + if (cent <= cMinCent || cent >= cMaxCent) { // Centrality selection + return false; + } + + if (col.ft0cOccupancyInTimeRange() < cMinOccupancy || col.ft0cOccupancyInTimeRange() > cMaxOccupancy) { // Occupancy cut + return false; + } + + if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { // Pile-up rejection + return false; + } + + if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { // ZvtxFT0 vs PV + return false; + } + + if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { // All ITS layer active + return false; + } + + return true; + } + + // Track Selection + template + bool selectTrack(T const& track) + { + // Kinematic selection + if (track.pt() <= cTrackMinPt || track.pt() >= cTrackMaxPt || std::abs(track.eta()) >= cTrackEtaCut) { + return false; + } + + // Global track selection + if (cTrackGlobal && !track.isGlobalTrackWoDCA()) { + return false; + } + + // DCA selection + if (std::abs(track.dcaXY()) >= cTrackDcaXYCut || std::abs(track.dcaZ()) >= cTrackDcaZCut) { + return false; + } + + // Electron rejection + if (std::abs(track.tpcNSigmaPi()) > cTpcRejCut && std::abs(track.tpcNSigmaKa()) > cTpcRejCut && std::abs(track.tpcNSigmaPr()) > cTpcRejCut && track.tpcNSigmaEl() > cTpcElRejCutMin && track.tpcNSigmaEl() < cTpcElRejCutMax) { + return false; + } + + // All selection passed + return true; + } + + template + float getEffCorrectionFactor(T const& track, S const& sign) + { + if (!cGetCorrectionFlag) { + return 1.; + } + + if constexpr (rec_gen == kGen) { + return 1.; + } + + std::array binarray{}; + std::array v = {cent, posz, track.pt(), track.eta()}; + if (sign > 0) { + for (int i = 0; i < nHistDim; ++i) { + binarray[i] = corrHist.hRecEffP->GetAxis(i)->FindBin(v[i]); + } + return corrHist.hRecEffP->GetBinContent(corrHist.hRecEffP->GetBin(binarray.data())); + } + + if (sign < 0) { + for (int i = 0; i < nHistDim; ++i) { + binarray[i] = corrHist.hRecEffM->GetAxis(i)->FindBin(v[i]); + } + return corrHist.hRecEffM->GetBinContent(corrHist.hRecEffM->GetBin(binarray.data())); + } + + return 1.; + } + + template + float getNuaCorrectionFactor(T const& track, S const& sign) + { + if (!cGetNuaCorrectionFlag) { + return 1.; + } + + if constexpr (rec_gen == kGen) { + return 1.; + } + + if (sign > 0) { + return corrHist.hNuaP->GetBinContent(corrHist.hNuaP->FindBin(posz, track.eta(), track.phi())); + } + + if (sign < 0) { + return corrHist.hNuaM->GetBinContent(corrHist.hNuaM->FindBin(posz, track.eta(), track.phi())); + } + + return 1.; + } + + template + bool isClosePair(T const& p1, T const& p2) + { + // Before + histos.fill(HIST("QA/TwoTrackCut/Before/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); + + // DPhiStar average over TPC + float dphistar = 0., n = 0.; + for (auto const& radii : vTpcRadii) { + float arg1 = 0.15 * magField * radii / p1.pt(); + float arg2 = 0.15 * magField * radii / p2.pt(); + if (std::abs(arg1) < 1.0 && std::abs(arg2) < 1.0) { + dphistar += (p1.phi() - p2.phi() - (p1.sign() * std::abs(std::asin(arg1))) + (p2.sign() * std::abs(std::asin(arg2)))); + ++n; + } + } + + // Nan check + if (n == 0) { + return false; + } + + // DPhistar + dphistar = RecoDecay::constrainAngle(dphistar / n, -PIHalf); + + // DEta + float deta = p1.eta() - p2.eta(); + + // Return flag + bool retFlag = (std::abs(deta) < cDEtaCut && std::abs(dphistar) < cDPhiStarCut); + + // Before + if (!retFlag) { + histos.fill(HIST("QA/TwoTrackCut/After/h2d_n2_detadphi"), deta, RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); + } + + return retFlag; + } + + template + bool isCloseQinv(T const& p1, T const& p2) + { + // Before + histos.fill(HIST("QA/FemtoCut/Before/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); + float dpt = std::abs(p1.pt() - p2.pt()); + bool retFlag = (dpt < cFemtoCut); + + if (!retFlag) { + histos.fill(HIST("QA/FemtoCut/After/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); + } + + return retFlag; + } + + template + void fillPairHist(T const& trk_1, T const& trk_2, S const& sign_1, S const& sign_2) + { + // Check for same index + if (trk_1.index() == trk_2.index()) { + return; + } + + // Close pair rejection + if constexpr (rec_gen == kRec) { + if (trk_1.sign() * trk_2.sign() < 0) { + // Close pair + if (cApplyTwoTrackCut && isClosePair(trk_1, trk_2)) { + return; + } + // Femto selection + if (cApplyFemtoSel && isCloseQinv(trk_1, trk_2)) { + return; + } + } + } + + // Reco/Gen Dir + static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; + + // RapPhi bins + const auto rapbin1 = static_cast((trk_1.eta() - kminrap) / rapbinwidth); + const auto rapbin2 = static_cast((trk_2.eta() - kminrap) / rapbinwidth); + const auto phibin1 = static_cast(trk_1.phi() / phibinwidth); + const auto phibin2 = static_cast(trk_2.phi() / phibinwidth); + + float effcorr = getEffCorrectionFactor(trk_1, sign_1) * getEffCorrectionFactor(trk_2, sign_2); + float nuacorr = getNuaCorrectionFactor(trk_1, sign_1) * getNuaCorrectionFactor(trk_2, sign_2); + float corrfact = effcorr; + + if (cDoEffNuaCorrection) { + corrfact *= nuacorr; + } + + if (rapbin1 >= 0 && rapbin2 >= 0 && phibin1 >= 0 && phibin2 >= 0 && rapbin1 < nrapbins && rapbin2 < nrapbins && phibin1 < nphibins && phibin2 < nphibins) { + + int rapphix = rapbin1 * nphibins + phibin1; + int rapphiy = rapbin2 * nphibins + phibin2; + + if ((sign_1 > 0 && sign_2 < 0) || (sign_1 < 0 && sign_2 > 0)) { + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_PM"), cent, rapphix + 0.5, rapphiy + 0.5, corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_ptpt_rapphi_PM"), cent, rapphix + 0.5, rapphiy + 0.5, trk_1.pt() * trk_2.pt() * corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_npt_rapphi_PM"), cent, rapphix + 0.5, rapphiy + 0.5, trk_1.pt() * corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_ptn_rapphi_PM"), cent, rapphix + 0.5, rapphiy + 0.5, trk_2.pt() * corrfact); + } else { + if (sign_1 > 0 && sign_2 > 0) { + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_PP"), cent, rapphix + 0.5, rapphiy + 0.5, corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_ptpt_rapphi_PP"), cent, rapphix + 0.5, rapphiy + 0.5, trk_1.pt() * trk_2.pt() * corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_npt_rapphi_PP"), cent, rapphix + 0.5, rapphiy + 0.5, trk_1.pt() * corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_ptn_rapphi_PP"), cent, rapphix + 0.5, rapphiy + 0.5, trk_2.pt() * corrfact); + } else if (sign_1 < 0 && sign_2 < 0) { + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_MM"), cent, rapphix + 0.5, rapphiy + 0.5, corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_ptpt_rapphi_MM"), cent, rapphix + 0.5, rapphiy + 0.5, trk_1.pt() * trk_2.pt() * corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_npt_rapphi_MM"), cent, rapphix + 0.5, rapphiy + 0.5, trk_1.pt() * corrfact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_ptn_rapphi_MM"), cent, rapphix + 0.5, rapphiy + 0.5, trk_2.pt() * corrfact); + } + } + } + } + + template + void fillTrackQA(T const& track) + { + histos.fill(HIST("TrackQA/hPtDcaZ"), track.pt(), track.dcaZ()); + histos.fill(HIST("TrackQA/hPtDcaXY"), track.pt(), track.dcaXY()); + histos.fill(HIST("TrackQA/hTrackTPCdEdX"), track.pt(), track.tpcSignal()); + } + + template + void fillSingleHist(T const& track, S const& sign) + { + // Hist array + static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; + + // Correction factor + float effCorr = getEffCorrectionFactor(track, sign); + float nuaCorr = getNuaCorrectionFactor(track, sign); + float corrFact = effCorr; + + if (cDoEffNuaCorrection) { + corrFact *= nuaCorr; + } + + // Histograms + if (sign > 0) { + // Corrections + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h1f_n1_pt_P"), track.pt()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h4f_n1_centvzptrap_P"), cent, posz, track.pt(), track.eta()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("NUA/h3f_n1_vzrapphi_P"), posz, track.eta(), track.phi()); + + // Checks + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h1f_n1_pt_P"), track.pt(), effCorr); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h1f_n1_rap_P"), track.eta(), effCorr); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h1f_n1_phi_P"), track.phi(), nuaCorr); + + // R2 Rho1 (Eta,Phi) + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n1_rapphi_P"), cent, track.eta(), track.phi(), corrFact); + + // P2 Rho1 (Eta,Phi) + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h2f_n1_pt_P"), cent, track.pt(), corrFact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_pt_rapphi_P"), cent, track.eta(), track.phi(), track.pt() * corrFact); + } else if (sign < 0) { + // Corrections + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h1f_n1_pt_M"), track.pt()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h4f_n1_centvzptrap_M"), cent, posz, track.pt(), track.eta()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("NUA/h3f_n1_vzrapphi_M"), posz, track.eta(), track.phi()); + + // Checks + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h1f_n1_pt_M"), track.pt(), corrFact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h1f_n1_rap_M"), track.eta(), corrFact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h1f_n1_phi_M"), track.phi(), nuaCorr); + + // R2 Rho1 (Eta,Phi) + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n1_rapphi_M"), cent, track.eta(), track.phi(), corrFact); + + // P2 Rho1 (Eta,Phi) + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h2f_n1_pt_M"), cent, track.pt(), corrFact); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_pt_rapphi_M"), cent, track.eta(), track.phi(), track.pt() * corrFact); + } + } + + template + void analyzeCollision(B const&, C const& collision, T const& tracks) + { + // Select Collision (Only for Data... McRec has been selected already !!!) + if constexpr (dmc == kData) { + if (!selCollision(collision)) { + return; + } + } + + // Get magnetic field + magField = getMagneticField(collision.template foundBC_as().timestamp()); + + // Fill Event QA + histos.fill(HIST("Event/h1f_collision_cent"), cent); + histos.fill(HIST("Event/h1f_collision_Vz"), collision.posZ()); + + // Loop over tracks + for (auto const& track_1 : tracks) { + // Check for Mc matched particle + if constexpr (dmc == kMC) { + if (!track_1.has_mcParticle()) { + continue; + } + } + + // Select track + if (!selectTrack(track_1)) { + continue; + } + + // Fill QA + fillTrackQA(track_1); + + // Fill singles + fillSingleHist(track_1, track_1.sign()); + + // Pair + for (auto const& track_2 : tracks) { + // Check for Mc matched particle + if constexpr (dmc == kMC) { + if (!track_2.has_mcParticle()) { + continue; + } + } + + // Select track + if (!selectTrack(track_2)) { + continue; + } + + // Fill pair histograms + fillPairHist(track_1, track_2, track_1.sign(), track_2.sign()); + } + } + } + + template + bool selectMcParticle(M const& mcpart) + { + // Check for Primary Charged Particle + if (!mcpart.isPhysicalPrimary()) { + return false; + } + + // Check pdg info + auto pdgCode = mcpart.pdgCode(); + auto pdgInfo = pdg->GetParticle(pdgCode); + if (pdgInfo == nullptr) { // particle with unknown pdg code + return false; + } + + // Remove electron + if (pdgCode == kElectron || pdgCode == kPositron) { + return false; + } + + // Apply kinematic selection + if (mcpart.pt() <= cTrackMinPt || mcpart.pt() >= cTrackMaxPt || std::abs(mcpart.eta()) >= cTrackEtaCut) { + return false; + } + + // All selection passed + return true; + } + + // MC Gen analysis + template + void analyzeMcGenCollision(M const&, P const& mcParticles) + { + // Loop 1 over MC particles + for (auto const& mcpart_1 : mcParticles) { + // Select mc particle + if (!selectMcParticle(mcpart_1)) { + continue; + } + + // Fill single particle densities + fillSingleHist(mcpart_1, pdg->GetParticle(mcpart_1.pdgCode())->Charge() / 3); + + // Loop 2 + for (auto const& mcpart_2 : mcParticles) { + // Select mc particle + if (!selectMcParticle(mcpart_2)) { + continue; + } + + // Fill pair densities + fillPairHist(mcpart_1, mcpart_2, pdg->GetParticle(mcpart_1.pdgCode())->Charge() / 3, pdg->GetParticle(mcpart_2.pdgCode())->Charge() / 3); + } + } + } + + // MC Reco-Gen analysis + template + void analyzeMcRecoGen(M const& mcCollision, C const& collisions, B const& bc, T const& tracks, P const& mcParticles) + { + // Number of Rec Collisions Associated to the McGen Collision + int nRecCols = collisions.size(); + if (nRecCols != 0) { + histos.fill(HIST("McGen/h1f_collision_recgen"), nRecCols); + } + // Do not analyze if more than one reco collision is accociated to one mc gen collision + if (nRecCols != 1) { + return; + } + // Check the reco collision + if (!collisions.begin().has_mcCollision() || !selCollision(collisions.begin()) || collisions.begin().mcCollisionId() != mcCollision.globalIndex()) { + return; + } + histos.fill(HIST("McGen/h2f_collision_posZ"), mcCollision.posZ(), collisions.begin().posZ()); + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collisions.begin().globalIndex()); + analyzeCollision(bc, collisions.begin(), tracksThisCollision); + analyzeMcGenCollision(mcCollision, mcParticles); + } + + // BF, Collision and Track Table + using BCsRun3 = soa::Join; + using Collisions = soa::Join; + using Tracks = soa::Join; + using TracksMC = soa::Join; + + SliceCache cache; + Preslice tracksPerCollision = aod::track::collisionId; + + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(ChargeBalanceFunction, processDummy, "Dummy process", true); + + void processChargedData(Collisions::iterator const& collision, BCsRun3 const& bc, Tracks const& tracks) + { + analyzeCollision(bc, collision, tracks); + } + + PROCESS_SWITCH(ChargeBalanceFunction, processChargedData, "Charged particle BF process", false); + + void processMCRecoGen(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, BCsRun3 const& bc, + TracksMC const& tracks, + aod::McParticles const& mcParticles) + { + analyzeMcRecoGen(mcCollision, collisions, bc, tracks, mcParticles); + } + + PROCESS_SWITCH(ChargeBalanceFunction, processMCRecoGen, "Process for MC RecoGen", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx new file mode 100644 index 00000000000..cc82c070425 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrFit.cxx @@ -0,0 +1,2069 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file corrFit.cxx +/// \brief Ultra long range correlation using forward FIT detectors and TPC, with focus on multiplicity dependence +/// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch) + +#include "PWGCF/Core/CorrelationContainer.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +using namespace constants::math; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) + +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; +} + +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; + +struct CorrFit { + o2::aod::ITSResponse itsResponse; + Service ccdb{}; + + O2_DEFINE_CONFIGURABLE(cfgUseTransverseMomentum, bool, false, "Use transverse momentum for correlation container") + O2_DEFINE_CONFIGURABLE(cfgQaCheck, bool, true, "Enable QA histograms for event selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgUseRefpTCut, bool, false, "Apply upper pT cut on reference tracks") + O2_DEFINE_CONFIGURABLE(cfgRefpTMax, float, 3.0f, "maximum pT for reference tracks if cfgUseRefpTCut is true") + O2_DEFINE_CONFIGURABLE(cfgRefpTMin, float, 0.2f, "minimum pT for reference tracks if cfgUseRefpTCut is true") + + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5, "Chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "DCAz range for tracks") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"(0.0026+0.005/(x^1.01))", "(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + O2_DEFINE_CONFIGURABLE(cfgMergingCut, float, 0.02, "Merging cut on track merge") + O2_DEFINE_CONFIGURABLE(cfgApplyTwoTrackEfficiency, bool, true, "Apply two track efficiency for tpc tpc") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") + Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") + Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") + Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + std::vector multT0CCutPars; + std::vector multPVT0CCutPars; + std::vector multGlobalPVCutPars; + std::vector multMultV0ACutPars; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") + } cfgFuncParas; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 6 = phi, 0 for no PID") + O2_DEFINE_CONFIGURABLE(cfgGetNsigmaQA, bool, false, "Get QA histograms for selection of pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgGetdEdx, bool, false, "Get dEdx histograms for pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + } cfgPIDConfigs; + + Configurable cfgCutFV0{"cfgCutFV0", 50., "FV0A threshold"}; + Configurable cfgCutFT0A{"cfgCutFT0A", 150., "FT0A threshold"}; + Configurable cfgCutFT0C{"cfgCutFT0C", 50., "FT0C threshold"}; + Configurable cfgCutZDC{"cfgCutZDC", 10., "ZDC threshold"}; + + SliceCache cache; + + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for histograms"}; + ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; + ConfigurableAxis axisPtFiner{"axisPtFiner", {98, 0.2, 10.0}, "pt axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; + ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; + ConfigurableAxis axisDeltaEtaFt0aFt0c{"axisDeltaEtaFt0aFt0c", {32, -1.5, 3.0}, "delta eta axis"}; + ConfigurableAxis axisDeltaEtaTpcTpc{"axisDeltaEtaTpcTpc", {32, -1.6, 1.6}, "delta eta axis for TPC-TPC"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt associated axis for histograms"}; + ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; + ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; + ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; + ConfigurableAxis axisParticle{"axisParticle", {4, 0, 4}, "particle axis for correlation container"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for efficiency histograms"}; + ConfigurableAxis axisAmplitudeFt0a{"axisAmplitudeFt0a", {5000, 0, 1000}, "FT0A amplitude"}; + ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; + + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisNsigmaITS{"axisNsigmaITS", {80, -5, 5}, "nsigmaITS axis"}; + ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + ConfigurableAxis axisSigma{"axisSigma", {200, 0, 20}, "sigma axis for TPC"}; + + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; + ConfigurableAxis cfgaxisFITamp{"cfgaxisFITamp", {1000, 0, 5000}, ""}; + AxisSpec axisFit{cfgaxisFITamp, "fit amplitude"}; + AxisSpec axisChID = {220, 0, 220}; + + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); + + using FilteredCollisions = soa::Filtered>; + using FilteredTracks = soa::Filtered>; + + // FT0 geometry + o2::ft0::Geometry ft0Det; + static constexpr uint64_t Ft0IndexA = 96; + std::vector* offsetFT0 = nullptr; + std::vector cstFT0RelGain; + + // Corrections + TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; + TH1D* mCentralityWeight = nullptr; + bool correctionsLoaded = false; + + // Define the outputs + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + OutputObj sameFt0aFt0c{"sameEvent_FT0A_FT0C"}; + OutputObj mixedFt0aFt0c{"mixedEvent_FT0A_FT0C"}; + OutputObj sameTPC{"sameEvent_TPC"}; + OutputObj mixedTPC{"mixedEvent_TPC"}; + + HistogramRegistry registry{"registry"}; + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + + // define global variables + TRandom3 fRandom{0}; + + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; + + enum FITIndex { + IndexFT0A = 0, + IndexFT0C = 1 + }; + + enum PIDIndex { + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, + UsePhi + }; + enum PiKpArrayIndex { + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow + }; + enum DetectorType { + UseTPC = 0, + UseTof, + UseITS + }; + enum Stage { + Before = 0, + After + }; + + enum EventCutTypes { + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts + }; + + enum EventCutType { + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes + }; + + enum CentEstimators { + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + // Count the total number of enum + NumCentEstimators + }; + + RCTFlagsChecker rctChecker{"CBT"}; + + void init(InitContext&) + { + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + LOGF(info, "Starting init"); + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameTPC) { + registry.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); + registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); + registry.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); + registry.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{14, -0.5, 13.5}}}); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + } + + if ((doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameTPC) && cfgQaCheck) { + registry.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + registry.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + } + + // Multiplicity correlation cuts + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { + cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; + cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; + cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; + cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); + } + + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + if (doprocessSameTPC || doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c) { + registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); + registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); + registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); + registry.add("hParticleCounts", "hParticleCounts", {HistType::kTH1D, {axisParticle}}); + registry.add("hParticleSelected", "hParticleSelected", {HistType::kTH2D, {axisParticle, axisPtTrigger}}); + registry.add("pTFiner", "pTFiner", {HistType::kTH1D, {axisPtFiner}}); + registry.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTH1D, {axisPtFiner}}); + registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); + registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0a || doprocessSameTpcFt0c) { + registry.add("FT0Amp", "", {HistType::kTH2F, {axisChID, axisFit}}); + registry.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, axisFit}}); + } + if (cfgPIDConfigs.cfgGetNsigmaQA) { + if (!cfgPIDConfigs.cfgUseItsPID) { + registry.add("TofTpcNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger}}}); + registry.add("TofTpcNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger}}}); + } // TPC-TOF PID QA hists + if (cfgPIDConfigs.cfgUseItsPID) { + registry.add("TofItsNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger}}}); + registry.add("TofItsNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger}}}); + } // ITS-TOF PID QA hists + } // end of PID QA hists + + if (cfgPIDConfigs.cfgGetdEdx) { + registry.add("TpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + registry.add("ExpTpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + registry.add("ExpSigma_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF}}}); + + registry.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + registry.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + registry.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF}}}); + } + + } // end of single particle distribution histograms + + if (cfgQaCheck) { + registry.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + } + + if (doprocessSameTpcFt0a) { + registry.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + registry.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); + registry.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); + registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); + } + if (doprocessSameTpcFt0c) { + registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + registry.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); + registry.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); + registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); + } + if (doprocessSameFt0aFt0c) { + registry.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); + registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); + } + if (doprocessSameTPC) { + registry.add("deltaEta_deltaPhi_same_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcTpc}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcTpc}}); + registry.add("Trig_hist_TPC", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, axisMult}}}); + } + + registry.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + + LOGF(info, "Initializing correlation container"); + + // Initialize Nch-related histograms and containers + + std::vector corrAxisTpcFt0a = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMult, "N_{ch}"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; + std::vector effAxis = { + {axisEtaEfficiency, "#eta"}, + {axisPtEfficiency, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + }; + std::vector userAxis; + + std::vector corrAxisTpcFt0c = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMult, "N_{ch}"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; + + std::vector corrAxisFt0aFt0c = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMult, "N_{ch}"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaFt0aFt0c, "#Delta#eta"}}; + + std::vector corrAxisTPC = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMult, "N_{ch}"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcTpc, "#Delta#eta"}}; // use the same delta eta axis for TPC-TPC correlation + + if (doprocessSameTpcFt0a) { + sameTpcFt0a.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); + mixedTpcFt0a.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); + } + if (doprocessSameTpcFt0c) { + sameTpcFt0c.setObject(new CorrelationContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); + mixedTpcFt0c.setObject(new CorrelationContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); + } + if (doprocessSameFt0aFt0c) { + sameFt0aFt0c.setObject(new CorrelationContainer("sameEvent_FT0A_FT0C", "sameEvent_FT0A_FT0C", corrAxisFt0aFt0c, effAxis, userAxis)); + mixedFt0aFt0c.setObject(new CorrelationContainer("mixedEvent_FT0A_FT0C", "mixedEvent_FT0A_FT0C", corrAxisFt0aFt0c, effAxis, userAxis)); + } + if (doprocessSameTPC) { + sameTPC.setObject(new CorrelationContainer("sameEvent_TPC", "sameEvent_TPC", corrAxisTPC, effAxis, userAxis)); + mixedTPC.setObject(new CorrelationContainer("mixedEvent_TPC", "mixedEvent_TPC", corrAxisTPC, effAxis, userAxis)); + } + + if (!cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSelRCTflags.value); // override initialzation + } + + LOGF(info, "End of init"); + } + + template + double getCentrality(TCollision const& collision) + { + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: + cent = collision.centFT0C(); + break; + case UseCentFT0CVariant1: + cent = collision.centFT0CVariant1(); + break; + case UseCentFT0M: + cent = collision.centFT0M(); + break; + case UseCentFV0A: + cent = collision.centFV0A(); + break; + default: + cent = collision.centFT0C(); + } + return cent; + } + + template + bool eventRct(TCollision const& collision, const bool fillCounter) + { + if (!rctChecker(collision)) { + if (fillCounter) { + registry.fill(HIST("hEventCountRct"), 0.5); + } + + return false; + } + if (fillCounter) { + registry.fill(HIST("hEventCountRct"), 1.5); + } + + return true; + } + + template + bool eventSelected(TCollision const& collision, const int mult, const bool fillCounter) + { + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoTimeFrameBorder); + } + + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoITSROFrameBorder); + } + + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoSameBunchPileup); + } + + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); + } + + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // no collisions in specified time range + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); + } + + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + // from Jan 9 2025 AOT meeting + // cut time intervals with dead ITS staves + return false; + } + + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + registry.fill(HIST("hEventCount"), UseGoodITSLayer0123); + } + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // no other collisions in this Readout Frame with per-collision multiplicity above threshold + return false; + } + + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // veto an event if FT0C amplitude in previous ITS ROF is above threshold + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + registry.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); + } + + auto multNTracksPV = collision.multNTracksPV(); + auto occupancy = collision.trackOccupancyInTimeRange(); + + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + registry.fill(HIST("hEventCount"), UseOccupancy); + } + + if (eventCuts[UseMultCorrCut][EvCut1]) { + float cent = getCentrality(collision); + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } + } + if (cfgFuncParas.cfgMultMultV0ACutEnabled) { + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } + } + } + + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + registry.fill(HIST("hEventCount"), UseMultCorrCut); + } + + // V0A T0A 5 sigma cut + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + registry.fill(HIST("hEventCount"), UseT0AV0ACut); + } + + return true; + } + + template + void eventSelectedIndividually(TCollision const& collision) + { + registry.fill(HIST("hPassedEventSelection"), FilteredEvents); + + if (collision.sel8()) { + registry.fill(HIST("hPassedEventSelection"), AfterSel8); + } + + if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + registry.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + registry.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + registry.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + registry.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + registry.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + registry.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + registry.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + registry.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + registry.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); + } + + auto occupancy = collision.trackOccupancyInTimeRange(); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + registry.fill(HIST("hPassedEventSelection"), UseOccupancy); + } + } + + double getPhiFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + return RecoDecay::phi(chPos.X() + (*offsetFT0)[i].getX(), chPos.Y() + (*offsetFT0)[i].getY()); + } + + double getEtaFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + auto x = chPos.X() + (*offsetFT0)[i].getX(); + auto y = chPos.Y() + (*offsetFT0)[i].getY(); + auto z = chPos.Z() + (*offsetFT0)[i].getZ(); + if (chno >= Ft0IndexA) { + z = -z; + } + auto r = std::sqrt(x * x + y * y); + auto theta = std::atan2(r, z); + return -std::log(std::tan(0.5 * theta)); + } + + void loadAlignParam(uint64_t timestamp) + { + offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); + if (offsetFT0 == nullptr) { + LOGF(fatal, "Could not load FT0/Calib/Align for timestamp %d", timestamp); + } + } + + template + bool trackSelected(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); + } + + template + float getDPhiStar(TTrack const& track1, TTrackAssoc const& track2, float radius, int magField) + { + float charge1 = track1.sign(); + float charge2 = track2.sign(); + + float phi1 = track1.phi(); + float phi2 = track2.phi(); + + float pt1 = track1.pt(); + float pt2 = track2.pt(); + + int fbSign = (magField > 0) ? 1 : -1; + + float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); + + if (dPhiStar > constants::math::PI) { + dPhiStar = constants::math::TwoPI - dPhiStar; + } + if (dPhiStar < -constants::math::PI) { + dPhiStar = -constants::math::TwoPI - dPhiStar; + } + + return dPhiStar; + } + + void loadGain(aod::BCsWithTimestamps::iterator const& bc) + { + cstFT0RelGain.clear(); + std::string fullPath; + + auto timestamp = bc.timestamp(); + constexpr int ChannelsFT0 = 208; + if (cfgCorrLevel == 0) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + fullPath = cfgGainEqPath; + fullPath += "/FT0"; + const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!objft0Gain) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + cstFT0RelGain = *(objft0Gain); + } + } + } + + template + void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) + { + if (fitType == IndexFT0C) { + id = ft0.channelC()[iCh]; + id = id + Ft0IndexA; + ampl = ft0.amplitudeC()[iCh]; + if (system == SameEvent) { + registry.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + registry.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else if (fitType == IndexFT0A) { + id = ft0.channelA()[iCh]; + ampl = ft0.amplitudeA()[iCh]; + if (system == SameEvent) { + registry.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + registry.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else { + LOGF(fatal, "Cor Index %d out of range", fitType); + } + } + + template + int getNsigmaPID(const TTrack& track, bool fillYields) + { + // Computing Nsigma arrays for pion, kaon, and protons + std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; + int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + + std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTof] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTof]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTof] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTof]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTof] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTof]; + + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } + + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + return -1; // more than one particle satisfy the criteria + } + + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseCharged); + } + + if (isPion) { + pid = UsePions; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UsePions); + } + } else if (isKaon) { + pid = UseKaons; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseKaons); + } + } else if (isProton) { + pid = UseProtons; + if (fillYields) { + registry.fill(HIST("hParticleCounts"), UseProtons); + } + } else { + return -1; // no particle satisfies the criteria + } + + return pid; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + } + + void loadCorrection(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (!cfgEfficiency.value.empty()) { + if (cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + } + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(mEfficiency)); + } + if (!cfgEfficiencyNch.value.empty()) { + if (cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + if (mCentralityWeight == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + } + correctionsLoaded = true; + } + + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + + bool getEfficiencyCorrection(float& weight, float pt, float eta, float vertex) + { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency + eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); + } else { + eff = 1.0; + } + if (eff == 0) { + return false; + } + weight = 1. / eff; + return true; + } + + int getMagneticField(uint64_t timestamp) + { + // Get the magnetic field + static o2::parameters::GRPMagField* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("/GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + } + return grpo->getNominalL3Field(); + } + + template + void trackCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram + { + double nTracksCorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { + continue; + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { + continue; + } + + nTracksCorrected += weightNch; + } + multiplicity = nTracksCorrected; + } + + template + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + { + + float weff1 = 1.0; + float zvtx = collision.posZ(); + + for (auto const& track1 : tracks) { + + if (!trackSelected(track1)) { + continue; + } + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, true) != cfgPIDConfigs.cfgPIDParticle) { + continue; // if PID is selected, check if the track has the right PID + } + + if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), zvtx)) { + continue; + } + + registry.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + registry.fill(HIST("Eta"), track1.eta()); + registry.fill(HIST("EtaCorrected"), track1.eta(), weff1); + registry.fill(HIST("pTFiner"), track1.pt()); + registry.fill(HIST("pTFinerCorrected"), track1.pt(), weff1); + registry.fill(HIST("hParticleSelected"), cfgPIDConfigs.cfgPIDParticle.value, track1.pt()); + } + } + + template + void fillNsigmaBeforeCut(const TTrack& track1, int pid) // function to fill the QA before Nsigma selection + { + switch (pid) { + case UsePions: // For Pions + if (!cfgPIDConfigs.cfgUseItsPID) { + if (cfgPIDConfigs.cfgGetNsigmaQA) { + registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + } + if (cfgPIDConfigs.cfgGetdEdx) { + double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); + + registry.fill(HIST("TpcdEdx_ptwise_beforeCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); + registry.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); + registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); + } + } + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { + registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); + } + break; + case UseKaons: // For Kaons + if (!cfgPIDConfigs.cfgUseItsPID) { + if (cfgPIDConfigs.cfgGetNsigmaQA) { + registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); + } + if (cfgPIDConfigs.cfgGetdEdx) { + double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); + + registry.fill(HIST("TpcdEdx_ptwise_beforeCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); + registry.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); + registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); + } + } + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { + registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); + } + break; + + case UseProtons: // For Protons + if (!cfgPIDConfigs.cfgUseItsPID) { + if (cfgPIDConfigs.cfgGetNsigmaQA) { + registry.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); + } + if (cfgPIDConfigs.cfgGetdEdx) { + double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); + + registry.fill(HIST("TpcdEdx_ptwise_beforeCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); + registry.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); + registry.fill(HIST("ExpSigma_ptwise_beforeCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); + } + } + if (cfgPIDConfigs.cfgGetNsigmaQA && cfgPIDConfigs.cfgUseItsPID) { + registry.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + } + break; + default: + LOGF(info, "PID %d not recognized, skipping nSigma QA filling", pid); + break; + } // end of switch + } // end of fillNsigmaBeforeCut + + template + void fillNsigmaAfterCut(const TTrack& track1, int pid) // function to fill the QA after Nsigma selection + { + switch (pid) { + case UsePions: // For Pions + if (!cfgPIDConfigs.cfgUseItsPID) { + if (cfgPIDConfigs.cfgGetdEdx) { + double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); + + registry.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); + registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); + registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); + } + if (cfgPIDConfigs.cfgGetNsigmaQA) { + registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + } + } + if (cfgPIDConfigs.cfgUseItsPID) { + registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); + } + break; + case UseKaons: // For Kaons + if (!cfgPIDConfigs.cfgUseItsPID) { + if (cfgPIDConfigs.cfgGetdEdx) { + double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); + + registry.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); + registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); + registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); + } + if (cfgPIDConfigs.cfgGetNsigmaQA) { + registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); + } + } + if (cfgPIDConfigs.cfgUseItsPID) { + registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); + } + break; + case UseProtons: // For Protons + if (!cfgPIDConfigs.cfgUseItsPID) { + if (cfgPIDConfigs.cfgGetdEdx) { + double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); + + registry.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); + registry.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); + registry.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); + } + if (cfgPIDConfigs.cfgGetNsigmaQA) { + registry.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); + } + } + if (cfgPIDConfigs.cfgUseItsPID && cfgPIDConfigs.cfgGetNsigmaQA) { + registry.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + } + break; + default: + LOGF(info, "PID %d not recognized, skipping nSigma QA filling, breaking", pid); + break; + } // end of switch + } + + template + void fillCorrelationsTPCFT0(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, double multiplicity, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!trackSelected(track1)) { + continue; + } + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { + fillNsigmaBeforeCut(track1, cfgPIDConfigs.cfgPIDParticle); + } + + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) { + continue; // if PID is selected, check if the track has the right PID + } + + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { + fillNsigmaAfterCut(track1, cfgPIDConfigs.cfgPIDParticle); + } + + if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { + continue; + } + + if (system == SameEvent) { + if (corType == IndexFT0C) { + registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); + } else if (corType == IndexFT0A) { + registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); + } + } + + std::size_t channelSize = 0; + if (corType == IndexFT0C) { + channelSize = ft0.channelC().size(); + } else if (corType == IndexFT0A) { + channelSize = ft0.channelA().size(); + } else { + LOGF(fatal, "Cor Index %d out of range", corType); + } + for (std::size_t iCh = 0; iCh < channelSize; iCh++) { + int chanelid = 0; + float ampl = 0.; + getChannel(ft0, iCh, chanelid, ampl, corType, system); + + auto phi = getPhiFT0(chanelid, corType); + auto eta = getEtaFT0(chanelid, corType); + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); + float deltaEta = track1.eta() - eta; + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == IndexFT0A) { + if (cfgQaCheck) { + registry.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + if (cfgQaCheck) { + registry.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } else if (system == MixedEvent) { + if (corType == IndexFT0A) { + if (cfgQaCheck) { + registry.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + if (cfgQaCheck) { + registry.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } + } + } + } + + template + void fillCorrelationsFT0AFT0C(TFT0s const& ft0Col1, TFT0s const& ft0Col2, float posZ, int system, double multiplicity, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); + + float triggerWeight = 1.0f; + std::size_t channelASize = ft0Col1.channelA().size(); + std::size_t channelCSize = ft0Col2.channelC().size(); + // loop over all tracks + for (std::size_t iChA = 0; iChA < channelASize; iChA++) { + + int chanelAid = 0; + float amplA = 0.; + getChannel(ft0Col1, iChA, chanelAid, amplA, IndexFT0A, system); + auto phiA = getPhiFT0(chanelAid, IndexFT0A); + auto etaA = getEtaFT0(chanelAid, IndexFT0A); + + if (system == SameEvent) { + registry.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, 0.5, multiplicity, eventWeight * amplA); + } + + for (std::size_t iChC = 0; iChC < channelCSize; iChC++) { + int chanelCid = 0; + float amplC = 0.; + getChannel(ft0Col2, iChC, chanelCid, amplC, IndexFT0C, system); + auto phiC = getPhiFT0(chanelCid, IndexFT0C); + auto etaC = getEtaFT0(chanelCid, IndexFT0C); + float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); + float deltaEta = etaA - etaC; + + // fill the right sparse and histograms + if (system == SameEvent) { + if (cfgQaCheck) { + registry.fill(HIST("deltaEta_deltaPhi_same_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + sameFt0aFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, 0.5, multiplicity, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } else if (system == MixedEvent) { + if (cfgQaCheck) { + registry.fill(HIST("deltaEta_deltaPhi_mixed_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + mixedFt0aFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, 0.5, multiplicity, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + } + } + } + + template + void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, double multiplicity, int magneticField, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + + auto fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgSampleSize)); + + float triggerWeight = 1.0f; + + float associateWeight = 1.0f; + + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!trackSelected(track1)) { + continue; + } + + // Fill Nsigma QA + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { + fillNsigmaBeforeCut(track1, cfgPIDConfigs.cfgPIDParticle); + } + + if (cfgPIDConfigs.cfgPIDParticle && getNsigmaPID(track1, false) != cfgPIDConfigs.cfgPIDParticle) { + continue; // if PID is selected, check if the track has the right PID + } + + if (cfgPIDConfigs.cfgPIDParticle > UseCharged) { + fillNsigmaAfterCut(track1, cfgPIDConfigs.cfgPIDParticle); + } + + if (!getEfficiencyCorrectionNch(triggerWeight, track1.pt())) { + continue; + } + + if (system == SameEvent) { + registry.fill(HIST("Trig_hist_TPC"), fSampleIndex, posZ, track1.pt(), multiplicity, eventWeight * triggerWeight); + } + + for (auto const& track2 : tracks2) { + + if (!trackSelected(track2)) { + continue; + } + + if (!getEfficiencyCorrectionNch(associateWeight, track2.pt())) { + continue; + } + + if (cfgUseRefpTCut) { + if (track2.pt() > cfgRefpTMax || track2.pt() < cfgRefpTMin) { + continue; + } + } + if (track1.pt() <= track2.pt()) { + continue; // skip if the trigger pt is less than the associate pt + } + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + if (cfgApplyTwoTrackEfficiency && std::abs(deltaEta) < cfgMergingCut) { + + double dPhiStarHigh = getDPhiStar(track1, track2, cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(track1, track2, cfgRadiusLow, magneticField); + + const double kLimit = 3.0 * cfgMergingCut; + + bool bIsBelow = false; + + constexpr double loopIncrement = 0.01; + const int nLoops = static_cast((cfgRadiusHigh - cfgRadiusLow) / loopIncrement); + + if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { + for (int i = 0; i < nLoops; i++) { + double rad = cfgRadiusLow + i * loopIncrement; + double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); + if (std::abs(dPhiStar) < kLimit) { + bIsBelow = true; + break; + } + } + if (bIsBelow) { + continue; + } + } + } + + // fill the right sparse and histograms + if (system == SameEvent) { + + sameTPC->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, triggerWeight * associateWeight); + if (cfgQaCheck) { + registry.fill(HIST("deltaEta_deltaPhi_same_TPC"), deltaPhi, deltaEta, triggerWeight * associateWeight); + } + } else if (system == MixedEvent) { + + mixedTPC->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, triggerWeight * associateWeight); + + if (cfgQaCheck) { + registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC"), deltaPhi, deltaEta, triggerWeight * associateWeight); + } + } + } + } + } + + bool isGoodRun(int runNumber) + { + for (const auto& excludedRun : cfgRunRemoveList.value) { + if (runNumber == excludedRun) { + return false; + } + } + + return true; + } + + void processSameTpcFt0a(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + if (cfgQaCheck) { + eventSelectedIndividually(collision); + } + + if (!collision.sel8()) { + return; + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + + registry.fill(HIST("zVtx"), collision.posZ()); + + registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + fillYield(collision, tracks); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgQaCheck) { + registry.fill(HIST("Nch"), multiplicity); + } + + if (cfgStrictTrackCounter) { + trackCounter(tracks, multiplicity); + } + + if (cfgQaCheck) { + registry.fill(HIST("Nch_corrected"), multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + return; + } + + const auto& ft0 = collision.foundFT0(); + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, IndexFT0A, eventWeight); + } + PROCESS_SWITCH(CorrFit, processSameTpcFt0a, "Process same event for TPC-FT0 correlation", false); + + void processMixedTpcFt0a(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + + auto multiplicity = static_cast(tracks1.size()); + + if (cfgStrictTrackCounter) { + trackCounter(tracks1, multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + continue; + } + + const auto& ft0 = collision2.foundFT0(); + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, IndexFT0A, eventWeight); + } + } + PROCESS_SWITCH(CorrFit, processMixedTpcFt0a, "Process mixed events for TPC-FT0A correlation", false); + + void processSameTpcFt0c(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + if (cfgQaCheck) { + eventSelectedIndividually(collision); + } + + if (!collision.sel8()) { + return; + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { + return; + } + if (!collision.has_foundFT0()) { + return; + } + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + + registry.fill(HIST("zVtx"), collision.posZ()); + + registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + const auto& ft0 = collision.foundFT0(); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgQaCheck) { + registry.fill(HIST("Nch"), multiplicity); + } + + if (cfgStrictTrackCounter) { + trackCounter(tracks, multiplicity); + } + + if (cfgQaCheck) { + registry.fill(HIST("Nch_corrected"), multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + return; + } + + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, multiplicity, IndexFT0C, 1.0f); + } + PROCESS_SWITCH(CorrFit, processSameTpcFt0c, "Process same event for TPC-FT0C correlation", false); + + void processMixedTpcFt0c(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + + const auto& ft0 = collision2.foundFT0(); + auto multiplicity = static_cast(tracks1.size()); + + if (cfgStrictTrackCounter) { + trackCounter(tracks1, multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + continue; + } + + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, multiplicity, IndexFT0C, eventWeight); + } + } + PROCESS_SWITCH(CorrFit, processMixedTpcFt0c, "Process mixed events for TPC-FT0C correlation", false); + + void processSameFt0aFt0c(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + if (cfgQaCheck) { + eventSelectedIndividually(collision); + } + + if (!collision.sel8()) { + return; + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + + const auto& ft0 = collision.foundFT0(); + + fillYield(collision, tracks); + + registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + auto multiplicity = static_cast(tracks.size()); + + if (cfgQaCheck) { + registry.fill(HIST("Nch"), multiplicity); + } + + if (cfgStrictTrackCounter) { + trackCounter(tracks, multiplicity); + } + + if (cfgQaCheck) { + registry.fill(HIST("Nch_corrected"), multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + return; + } + + fillCorrelationsFT0AFT0C(ft0, ft0, collision.posZ(), SameEvent, multiplicity, eventWeight); + } + PROCESS_SWITCH(CorrFit, processSameFt0aFt0c, "Process same event for FT0A-FT0C correlation", true); + + void processMixedFt0aFt0c(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + // should have the same event to TPC-FT0A/C correlations + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + + const auto& ft0Col1 = collision1.foundFT0(); + const auto& ft0Col2 = collision2.foundFT0(); + + auto multiplicity = static_cast(tracks1.size()); + + if (cfgStrictTrackCounter) { + trackCounter(tracks1, multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + continue; + } + + registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + fillCorrelationsFT0AFT0C(ft0Col1, ft0Col2, collision1.posZ(), MixedEvent, multiplicity, eventWeight); + } + } + PROCESS_SWITCH(CorrFit, processMixedFt0aFt0c, "Process mixed events for FT0A-FT0C correlation", true); + + void processSameTPC(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) + { + + if (cfgQaCheck) { + eventSelectedIndividually(collision); + } + if (!eventRct(collision, true)) { + return; + } + + if (!collision.sel8()) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) { + return; + } + + registry.fill(HIST("zVtx"), collision.posZ()); + + registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + loadCorrection(bc.timestamp()); + + fillYield(collision, tracks); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgQaCheck) { + registry.fill(HIST("Nch"), multiplicity); + } + + if (cfgStrictTrackCounter) { + trackCounter(tracks, multiplicity); + } + + if (cfgQaCheck) { + registry.fill(HIST("Nch_corrected"), multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + return; + } + + fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, multiplicity, getMagneticField(bc.timestamp()), 1.0f); + } + PROCESS_SWITCH(CorrFit, processSameTPC, "Process same event for TPC-TPC correlation", false); + + void processMixedTPC(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) { + continue; + } + + registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + + loadCorrection(bc.timestamp()); + + auto multiplicity = static_cast(tracks1.size()); + + if (cfgStrictTrackCounter) { + trackCounter(tracks1, multiplicity); + } + + if (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations) { + continue; + } + + fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, multiplicity, getMagneticField(collision1.bc_as().timestamp()), 1.0f); + } + } + PROCESS_SWITCH(CorrFit, processMixedTPC, "Process mixed events for TPC-TPC correlation", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx new file mode 100644 index 00000000000..f3185668771 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/corrReso.cxx @@ -0,0 +1,1776 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file corrReso.cxx +/// \brief Ultra long range correlation using forward FIT detectors and TPC, with focus on resonances +/// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch) + +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +using namespace constants::math; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) + +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; +} + +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; + +struct CorrReso { + o2::aod::ITSResponse itsResponse; + Service ccdb{}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0A, bool, true, "Switch for doing TPC-FT0A correlations") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0C, bool, true, "Switch for doing TPC-FT0C correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; + } cfgGeneral; + + struct : ConfigurableGroup{ + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object")} cfgEffWeights; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A") + O2_DEFINE_CONFIGURABLE(cfgMinCentForCorrelations, int, 0, "minimum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxCentForCorrelations, int, 20, "maximum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") + Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") + Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") + Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + std::vector multT0CCutPars; + std::vector multPVT0CCutPars; + std::vector multGlobalPVCutPars; + std::vector multMultV0ACutPars; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") + } cfgFuncParas; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") + O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") + O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "4 = kshort, 5 = lambda, 0 for no PID") + Configurable> cfgResoCuts{"cfgResoCuts", {LongArrayFloat.front().data(), 15, 2, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "RapidityMin", "RapidityMax", "ArmPodMinVal", "DCABetDaug", "CompMassRej"}, {"K0", "Lambda"}}, "Labeled array (float) for various cuts on resonances"}; + Configurable> cfgResoSwitches{"cfgResoSwitches", {LongArrayInt.front().data(), 7, 2, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompMassRej"}, {"K0", "Lambda"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgPIDConfigs; + + SliceCache cache; + + // Axes for general QA + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for QA histograms"}; + ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality axis for QA histograms"}; + ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; + ConfigurableAxis axisPtFiner{"axisPtFiner", {98, 0.2, 10.0}, "pt axis for histograms"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; + ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; + ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; + ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; + + // Axes for long-range correlations QA + ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0", {5000, 0, 1000}, "FT0A amplitude"}; + ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; + ConfigurableAxis axisChannelFt0cAxis{"axisChannelFt0cAxis", {96, 0.0, 96.0}, "FT0C channel"}; + + // Axes for PID QA + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); + + using FilteredCollisions = soa::Filtered>; + using FilteredTracks = soa::Filtered>; + using V0TrackCandidate = aod::V0Datas; + + // FT0 geometry + o2::ft0::Geometry ft0Det; + static constexpr uint64_t Ft0IndexA = 96; + std::vector* offsetFT0 = nullptr; + std::vector cstFT0RelGain; + + // Corrections + TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; + TH1D* mCentralityWeight = nullptr; + bool correctionsLoaded = false; + + // Define the outputs + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + + HistogramRegistry histos{"histos"}; + + // For Labelled array value containers + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + std::array, 15> resoCutVals{}; + std::array, 7> resoSwitchVals{}; + + // define global variables + TRandom3 fRandom{0}; + + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; + + enum FITIndex { + IndexFT0A = 0, + IndexFT0C = 1 + }; + + enum PIDIndex { + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, + }; + + enum PiKpArrayIndex { + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow + }; + enum DetectorType { + UseTPC = 0, + UseTOF, + UseITS + }; + + enum EventCutTypes { + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts + }; + + enum EventCutType { + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes + }; + enum ResoArrayIndex { + IndexK0 = 0, + IndexLambda = 1, + IndexPhi = 2, + NumResoParticles = 3 + }; + enum ResoParticleCuts { + CutCosPA = 0, + CutMassMin, + CutMassMax, + CutPosTrackPt, + CutNegTrackPt, + CutDCAPosToPVMin, + CutDCANegToPVMin, + CutLifeTime, + CutRadiusMin, + CutRadiusMax, + CutRapidityMin, + CutRapidityMax, + CutArmPodMinVal, + CutDCABetDaug, + CutCompMassRej, + NumParticleCuts + }; + enum ResoParticleSwitches { + UseCosPA = 0, + UseMassBins, + UseDCABetDaug, + UseProperLifetime, + UseV0Radius, + UseArmPodCut, + UseCompMassRej, + NumParticleSwitches + }; + + enum ResoSelectionFlags { + FillCandidate = 0, + FillDaughterPt, + FillMassCut, + FillRapidityCut, + FillDCAtoPV, + FillDCAxDaughters, + FillV0Radius, + FillCosPA, + FillProperLifetime, + FillArmPodCut, + FillCompetingMass, + FillDaughterTrackSelected, + }; + + enum CentEstimators { + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + // Count the total number of enum + NumCentEstimators + }; + + RCTFlagsChecker rctChecker{"CBT"}; + + void init(InitContext&) + { + // ---------------------------------------------------------------------------------------------------------------------- + // The way the code is initialized, TPC-FT0 correlations are done together + // ---------------------------------------------------------------------------------------------------------------------- + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + LOGF(info, "Starting init"); + + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); + readMatrix(cfgPIDConfigs.cfgResoCuts->getData(), resoCutVals); + readMatrix(cfgPIDConfigs.cfgResoSwitches->getData(), resoSwitchVals); + + if (cfgPIDConfigs.cfgPIDParticle < UseK0) { + LOGF(fatal, "The input number does not correspond to any particle"); + return; + } + + // Creating mass axis depending on particle - 4 = kshort, 5 = lambda, 6 = phi + AxisSpec axisInvMass = {10, 0, 1, "mass"}; + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + const auto bins = resoSwitchVals[UseMassBins][IndexK0]; + const auto min = resoCutVals[CutMassMin][IndexK0]; + const auto max = resoCutVals[CutMassMax][IndexK0]; + + axisInvMass = {bins, min, max, "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; + } + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + const auto bins = resoSwitchVals[UseMassBins][IndexLambda]; + const auto min = resoCutVals[CutMassMin][IndexLambda]; + const auto max = resoCutVals[CutMassMax][IndexLambda]; + + axisInvMass = {bins, min, max, "M_{p#pi} (GeV/c^{2})"}; + } + + // Set the sample axis + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } + + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { // For K0 + histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); + histos.add("hK0Rap", "", {HistType::kTH1D, {axisEta}}); + histos.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); + + histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "K0 candidates"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { // For Lambda + histos.add("PrPlusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PrPlusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_La", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + + histos.add("PrMinusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PiPlusTPC_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTPC}}}); + histos.add("PrMinusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + histos.add("PiPlusTOF_Al", "", {HistType::kTH2D, {{axisPtFiner, axisNsigmaTOF}}}); + + histos.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); + histos.add("hLambdaRap", "", {HistType::kTH1D, {axisEta}}); + histos.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); + + histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "Lambda candidates"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + + histos.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); + histos.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(HaveFT0Cut + 1, "Event has FT0 cut"); + + if (cfgGeneral.cfgQABasic) { + histos.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + } + + // Multiplicity correlation cuts + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { + cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; + cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; + cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; + cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); + } + + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + if (doprocessSameTpcFt0) { + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); + histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); + + if (cfgGeneral.cfgStrictTrackCounter) { + histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + } + if (!cfgEvSel.cfgSelCollByNch) { + histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); + } + + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); + histos.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); + histos.add("pTFiner", "pTFiner", {HistType::kTH1D, {axisPtFiner}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTH1D, {axisPtFiner}}); + } + + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + histos.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } + } + + if (doprocessMixedTpcFt0) { + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + histos.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + histos.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + } + } + + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + + LOGF(info, "Initializing correlation container"); + + // Initialize containers + std::vector corrAxisTpcFt0a = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisInvMass}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; + + std::vector corrAxisTpcFt0c = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisInvMass}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; + + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + sameTpcFt0a.setObject(new DihadronContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a)); + mixedTpcFt0a.setObject(new DihadronContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a)); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + sameTpcFt0c.setObject(new DihadronContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c)); + mixedTpcFt0c.setObject(new DihadronContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c)); + } + + if (!cfgEvSel.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSel.cfgEvSelRCTflags.value); // override initialzation + } + + LOGF(info, "End of init"); + } + + template + double getCentrality(TCollision const& collision) + { + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: + cent = collision.centFT0C(); + break; + case UseCentFT0CVariant1: + cent = collision.centFT0CVariant1(); + break; + case UseCentFT0M: + cent = collision.centFT0M(); + break; + case UseCentFV0A: + cent = collision.centFV0A(); + break; + default: + cent = collision.centFT0C(); + } + return cent; + } + + template + bool eventRct(TCollision const& collision, const bool fillCounter) + { + if (cfgEvSel.cfgEvSelRCTflags.value.empty()) { + return true; + } + if (!rctChecker(collision)) { + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 0.5); + } + + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 1.5); + } + + return true; + } + + template + bool eventSelected(TCollision const& collision, const int mult, const double cent, const bool fillCounter) + { + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } + if (!collision.sel8()) { + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); + } + + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); + } + + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); + } + + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); + } + + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); + } + + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // no collisions in specified time range + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); + } + + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + // from Jan 9 2025 AOT meeting + // cut time intervals with dead ITS staves + return false; + } + + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); + } + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // no other collisions in this Readout Frame with per-collision multiplicity above threshold + return false; + } + + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // veto an event if FT0C amplitude in previous ITS ROF is above threshold + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); + } + + auto multNTracksPV = collision.multNTracksPV(); + auto occupancy = collision.trackOccupancyInTimeRange(); + + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); + } + + if (eventCuts[UseMultCorrCut][EvCut1]) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } + } + if (cfgFuncParas.cfgMultMultV0ACutEnabled) { + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } + } + } + + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); + } + // V0A T0A 5 sigma cut + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); + } + return true; + } + + template + void eventSelectedIndividually(TCollision const& collision) + { + histos.fill(HIST("hPassedEventSelection"), FilteredEvents); + + if (collision.sel8()) { + histos.fill(HIST("hPassedEventSelection"), AfterSel8); + } + + if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + histos.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + histos.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); + } + + auto occupancy = collision.trackOccupancyInTimeRange(); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + histos.fill(HIST("hPassedEventSelection"), UseOccupancy); + } + } + + double getPhiFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + return RecoDecay::phi(chPos.X() + (*offsetFT0)[i].getX(), chPos.Y() + (*offsetFT0)[i].getY()); + } + + double getEtaFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + auto x = chPos.X() + (*offsetFT0)[i].getX(); + auto y = chPos.Y() + (*offsetFT0)[i].getY(); + auto z = chPos.Z() + (*offsetFT0)[i].getZ(); + if (chno >= Ft0IndexA) { + z = -z; + } + auto r = std::sqrt(x * x + y * y); + auto theta = std::atan2(r, z); + return -std::log(std::tan(0.5 * theta)); + } + + void loadAlignParam(uint64_t timestamp) + { + offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); + if (offsetFT0 == nullptr) { + LOGF(fatal, "Could not load FT0/Calib/Align for timestamp %d", timestamp); + } + } + + template + bool trackSelected(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); + } + + void loadGain(aod::BCsWithTimestamps::iterator const& bc) + { + cstFT0RelGain.clear(); + std::string fullPath; + + auto timestamp = bc.timestamp(); + constexpr int ChannelsFT0 = 208; + if (cfgGeneral.cfgCorrLevel == 0) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + fullPath = cfgGeneral.cfgGainEqPath; + fullPath += "/FT0"; + const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!objft0Gain) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + cstFT0RelGain = *(objft0Gain); + } + } + } + + template + void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType) + { + if (fitType == IndexFT0C) { + id = ft0.channelC()[iCh]; + id = id + Ft0IndexA; + ampl = ft0.amplitudeC()[iCh]; + ampl = ampl / cstFT0RelGain[id]; + } else if (fitType == IndexFT0A) { + id = ft0.channelA()[iCh]; + ampl = ft0.amplitudeA()[iCh]; + ampl = ampl / cstFT0RelGain[id]; + } else { + LOGF(fatal, "Cor Index %d out of range", fitType); + } + } + + template + int getNsigmaPID(const TTrack& track) + { + // Computing Nsigma arrays for pion, kaon, and protons + std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; + int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + + std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; + + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } + + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + return -1; // more than one particle satisfy the criteria + } + + if (isPion) { + pid = UsePions; + } else if (isKaon) { + pid = UseKaons; + } else if (isProton) { + pid = UseProtons; + } else { + return -1; // no particle satisfies the criteria + } + + return pid; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + } + + template + bool selectionV0Daughter(TTrack const& track, int pid) + { + if (!(track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])) { + return false; + } + if (!track.hasTPC()) { + return false; + } + if (!(track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts])) { + return false; + } + if (!(track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts])) { + return false; + } + + if (cfgPIDConfigs.cfgUseOnlyTPC) { + if (pid == UsePions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgTpcCut) { + return false; + } + if (pid == UseKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgTpcCut) { + return false; + } + if (pid == UseProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgTpcCut) { + return false; + } + } else { + int partIndex = getNsigmaPID(track); + int pidIndex = partIndex; // 1 = pion, 2 = kaon, 3 = proton + if (pidIndex != pid) { + return false; + } + } + + return true; + } + + void loadCorrection(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); + } + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); + } + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); + if (mCentralityWeight == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + } + correctionsLoaded = true; + } + + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + + bool getEfficiencyCorrection(float& weight, float pt, float eta, float vertex) + { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency + eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); + } else { + eff = 1.0; + } + if (eff == 0) { + return false; + } + weight = 1. / eff; + return true; + } + + bool getCentralityWeight(float& weightCent, const double centrality) + { + float weight = 1.; + if (mCentralityWeight) { + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + } else { + weight = 1.0; + } + if (weight == 0) { + return false; + } + weightCent = weight; + return true; + } + + template + void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram + { + double nTracksCorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { + continue; + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { + continue; + } + nTracksCorrected += weightNch; + } + multiplicity = nTracksCorrected; + } + + template + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + { + + float weff1 = 1.0; + float zvtx = collision.posZ(); + + for (auto const& track1 : tracks) { + + if (!trackSelected(track1)) { + continue; + } + + if (!getEfficiencyCorrection(weff1, track1.pt(), track1.eta(), zvtx)) { + continue; + } + + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + histos.fill(HIST("Eta"), track1.eta()); + histos.fill(HIST("EtaCorrected"), track1.eta(), weff1); + histos.fill(HIST("pTFiner"), track1.pt()); + histos.fill(HIST("pTFinerCorrected"), track1.pt(), weff1); + } + } + + template + bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX, int system, int corType) + { + double mk0 = candidate.mK0Short(); + + // separate the positive and negative V0 daughters + auto postrack = candidate.template posTrack_as(); + auto negtrack = candidate.template negTrack_as(); + + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexK0] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDaughterPt); + } + if (mk0 < resoCutVals[CutMassMin][IndexK0] || mk0 > resoCutVals[CutMassMax][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillMassCut); + } + // Rapidity correction + if (candidate.yK0Short() > resoCutVals[CutRapidityMax][IndexK0] || candidate.yK0Short() < resoCutVals[CutRapidityMin][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillRapidityCut); + } + // DCA cuts for K0short + if (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDCAxDaughters); + } + // v0 radius cuts + if (resoSwitchVals[UseV0Radius][IndexK0] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexK0] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexK0])) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillV0Radius); + } + // cosine pointing angle cuts + if (resoSwitchVals[UseCosPA][IndexK0] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCosPA); + } + // Proper lifetime + float cTauK0 = candidate.distovertotmom(posX, posY, posZ) * massK0Short; + if (resoSwitchVals[UseProperLifetime][IndexK0] && cTauK0 > resoCutVals[CutLifeTime][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillProperLifetime); + } + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][IndexK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexK0]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexK0]) { + if (std::abs(candidate.mLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + if (std::abs(candidate.mAntiLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillCompetingMass); + } + + // Selection on V0 daughters + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UsePions)) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hK0Count"), FillDaughterTrackSelected); + histos.fill(HIST("hK0Phi"), candidate.phi()); + histos.fill(HIST("hK0Rap"), candidate.yK0Short()); + histos.fill(HIST("hK0Eta"), candidate.eta()); + histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); + histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + + return true; + } + + template + bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX, int system, int corType) + { + bool isL = false; // Is lambda candidate + bool isAL = false; // Is anti-lambda candidate + + double mlambda = candidate.mLambda(); + double mantilambda = candidate.mAntiLambda(); + + // separate the positive and negative V0 daughters + auto postrack = candidate.template posTrack_as(); + auto negtrack = candidate.template negTrack_as(); + + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexLambda] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDaughterPt); + } + if (mlambda > resoCutVals[CutMassMin][IndexLambda] && mlambda < resoCutVals[CutMassMax][IndexLambda]) { + isL = true; + } + if (mantilambda > resoCutVals[CutMassMin][IndexLambda] && mantilambda < resoCutVals[CutMassMax][IndexLambda]) { + isAL = true; + } + + if (!isL && !isAL) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillMassCut); + } + + // Rapidity correction + if (candidate.yLambda() > resoCutVals[CutRapidityMax][IndexLambda] || candidate.yLambda() < resoCutVals[CutRapidityMin][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillRapidityCut); + } + + // DCA cuts for lambda and antilambda + if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda])) { + isL = false; + } + if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda])) { + isAL = false; + } + if (!isL && !isAL) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDCAxDaughters); + } + // v0 radius cuts + if (resoSwitchVals[UseV0Radius][IndexLambda] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexLambda] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexLambda])) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillV0Radius); + } + // cosine pointing angle cuts + if (resoSwitchVals[UseCosPA][IndexLambda] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCosPA); + } + // Proper lifetime + float cTauLambda = candidate.distovertotmom(posX, posY, posZ) * massLambda; + if (resoSwitchVals[UseProperLifetime][IndexLambda] && cTauLambda > resoCutVals[CutLifeTime][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillProperLifetime); + } + + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][IndexLambda] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexLambda]) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexLambda]) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < resoCutVals[CutCompMassRej][IndexLambda]) { + return false; + } + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillCompetingMass); + } + + if (isL) { + if (!selectionV0Daughter(postrack, UseProtons) || !selectionV0Daughter(negtrack, UsePions)) { + isL = false; + } + } + if (isAL) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UseProtons)) { + isAL = false; + } + } + if (!isL && !isAL) { + return false; + } + if (system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("hLambdaCount"), FillDaughterTrackSelected); + histos.fill(HIST("hLambdaPhi"), candidate.phi()); + histos.fill(HIST("hLambdaEta"), candidate.eta()); + histos.fill(HIST("hLambdaRap"), candidate.yLambda()); + } + if (isL && system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); + histos.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); + histos.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (isAL && system == SameEvent && corType == IndexFT0A) { + histos.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); + histos.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); + histos.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); + } + + return true; + } + + template + void fillCorrelationsTPCFT0(const TV0Tracks& tracks1, TFT0s const& ft0, float posZ, float posY, float posX, int system, double eventClass, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + double resoMass = -1; + + // 4 = kshort, 5 = lambda, 6 = phi + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + if (!isSelectedK0(track1, posZ, posY, posX, system, corType)) { + continue; // Reject if called for K0 but V0 is not K0 + } + + resoMass = track1.mK0Short(); + } + + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + if (!isSelectedLambda(track1, posZ, posY, posX, system, corType)) { + continue; // Reject if called for Lambda but V0 is not lambda + } + + resoMass = track1.mLambda(); + } + + if (system == SameEvent) { + if (corType == IndexFT0C) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), resoMass, eventWeight * triggerWeight); + } else if (corType == IndexFT0A) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), resoMass, eventWeight * triggerWeight); + } + } + + std::size_t channelSize = 0; + if (corType == IndexFT0C) { + channelSize = ft0.channelC().size(); + } else if (corType == IndexFT0A) { + channelSize = ft0.channelA().size(); + } else { + LOGF(fatal, "Cor Index %d out of range", corType); + } + for (std::size_t iCh = 0; iCh < channelSize; iCh++) { + int chanelid = 0; + float ampl = 0.; + getChannel(ft0, iCh, chanelid, ampl, corType); + + auto phi = getPhiFT0(chanelid, corType); + auto eta = getEtaFT0(chanelid, corType); + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); + float deltaEta = track1.eta() - eta; + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == IndexFT0A) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } else if (system == MixedEvent) { + if (corType == IndexFT0A) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == IndexFT0C) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), resoMass, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } + } + } + } + + bool isGoodRun(int runNumber) + { + for (const auto& excludedRun : cfgGeneral.cfgRunRemoveList.value) { + if (runNumber == excludedRun) { + return false; + } + } + + return true; + } + + double massLambda = o2::constants::physics::MassLambda; // same as MassLambda0 and MassLambda0Bar + double massK0Short = o2::constants::physics::MassK0Short; // same as o2::constants::physics::MassK0 and o2::constants::physics::MassK0Bar + + void processSameTpcFt0(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, V0TrackCandidate const& V0s) + { + if (cfgGeneral.cfgQABasic) { + eventSelectedIndividually(collision); + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + histos.fill(HIST("Centrality"), cent); + if (!cfgEvSel.cfgSelCollByNch) { + if (!getCentralityWeight(weightCent, cent)) { + return; // or continue in a loop + } + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + } + + histos.fill(HIST("zVtx"), collision.posZ()); + + if (cfgGeneral.cfgQABasic) { + fillYield(collision, tracks); + } + + auto multiplicity = static_cast(tracks.size()); + histos.fill(HIST("Nch"), multiplicity); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + histos.fill(HIST("Nch_corrected"), multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { + return; + } + + histos.fill(HIST("eventcount"), SameEvent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + const auto& ft0 = collision.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, eventClass, IndexFT0A, weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(V0s, ft0, collision.posZ(), collision.posY(), collision.posX(), SameEvent, eventClass, IndexFT0C, weightCent); + } + } + PROCESS_SWITCH(CorrReso, processSameTpcFt0, "Process same event for TPC-FT0 correlation", false); + + void processMixedTpcFt0(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&, V0TrackCandidate const& V0s) + { + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + + auto tracksTuple = std::make_tuple(V0s, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, v0s1, collision2, tracks2] = *it; + + if (!collision1.sel8() || !collision2.sel8()) { // Doing this at the top of mixed events loop to reduce computation (This cut is also there inside eventSelected) + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); + + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); + + if (cfgEvSel.cfgSelCollByNch && cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + float eventWeight = 1.0f; + + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + if (!cfgEvSel.cfgSelCollByNch) { + if (!getCentralityWeight(weightCent, cent1)) { + continue; // or continue in a loop + } + } + + const auto& ft0 = collision2.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, eventClass, IndexFT0A, eventWeight * weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(v0s1, ft0, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, eventClass, IndexFT0C, eventWeight * weightCent); + } + } + } + PROCESS_SWITCH(CorrReso, processMixedTpcFt0, "Process mixed events for TPC-FT0 correlations", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/corrSparse.cxx b/PWGCF/TwoParticleCorrelations/Tasks/corrSparse.cxx deleted file mode 100644 index 08a490c2e1d..00000000000 --- a/PWGCF/TwoParticleCorrelations/Tasks/corrSparse.cxx +++ /dev/null @@ -1,2125 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CorrSparse.cxx -/// \brief Provides a sparse with usefull two particle correlation info -/// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch) - -#include "PWGCF/Core/CorrelationContainer.h" -#include "PWGMM/Mult/DataModel/bestCollisionTable.h" - -#include "Common/CCDB/EventSelectionParams.h" -#include "Common/Core/RecoDecay.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace constants::math; - -namespace o2::aod -{ -namespace corrsparse -{ -DECLARE_SOA_COLUMN(Multiplicity, multiplicity, int); -} -DECLARE_SOA_TABLE(Multiplicity, "AOD", "MULTIPLICITY", - corrsparse::Multiplicity); - -} // namespace o2::aod - -// define the filtered collisions and tracks -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; - -// static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {2.1, 2.2, 2.3, -2.1, -2.2, -2.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {3.1, 3.2, 3.3, -3.1, -3.2, -3.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}}; -// static constexpr int LongArrayInt[3][20] = {{1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {3, 3, 3, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}}; - -struct CorrSparse { - Service ccdb; - - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgZVtxCut, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgQaCheck, bool, false, "Fill QA histograms for multiplicity and zVtx for events used in the analysis") - - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgEtaCut, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") - - } cfgTrackCuts; - - struct : ConfigurableGroup{ - - O2_DEFINE_CONFIGURABLE(processFV0, bool, true, "Process FV0 correlations") - O2_DEFINE_CONFIGURABLE(processFT0A, bool, true, "Process FT0A correlations") - O2_DEFINE_CONFIGURABLE(processFT0C, bool, true, "Process FT0C correlations") - O2_DEFINE_CONFIGURABLE(processMFT, bool, true, "Process MFT correlations") - O2_DEFINE_CONFIGURABLE(withGain, bool, false, "Use gain for FT0A and FT0C") - - } cfgDetectorConfig; - - struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgPtCutMinMFT, float, 0.5f, "minimum accepted MFT track pT") - O2_DEFINE_CONFIGURABLE(cfgPtCutMaxMFT, float, 10.0f, "maximum accepted MFT track pT") - O2_DEFINE_CONFIGURABLE(etaMftTrackMin, float, -3.6, "Minimum eta for MFT track") - O2_DEFINE_CONFIGURABLE(etaMftTrackMax, float, -2.5, "Maximum eta for MFT track") - O2_DEFINE_CONFIGURABLE(nClustersMftTrack, int, 5, "Minimum number of clusters for MFT track") - Configurable cutBestCollisionId{"cutBestCollisionId", 0, "cut on the best collision Id used in a filter"}; - Configurable etaMftTrackMaxFilter{"etaMftTrackMaxFilter", -2.0f, "Maximum value for the eta of MFT tracks when used in filter"}; - Configurable etaMftTrackMinFilter{"etaMftTrackMinFilter", -3.9f, "Minimum value for the eta of MFT tracks when used in filter"}; - Configurable mftMaxDCAxy{"mftMaxDCAxy", 2.0f, "Cut on dcaXY for MFT tracks"}; - Configurable mftMaxDCAz{"mftMaxDCAz", 2.0f, "Cut on dcaZ for MFT tracks"}; - } cfgMftConfig; - - struct : ConfigurableGroup{ - O2_DEFINE_CONFIGURABLE(cfgMinMult, int, 0, "Minimum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgMaxMult, int, 10, "Maximum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, false, "rejects collisions which are associated with the same found-by-T0 bunch crossing") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayer0123, bool, true, "cut time intervals with dead ITS layers 0,1,2,3") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersAll, bool, true, "cut time intervals with dead ITS staves") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") - O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") - O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") - O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") - - } cfgEventSelection; - - struct : ConfigurableGroup{ - - O2_DEFINE_CONFIGURABLE(cfgRejectFT0AInside, bool, false, "Rejection of inner ring channels of the FT0A detector") - O2_DEFINE_CONFIGURABLE(cfgRejectFT0AOutside, bool, false, "Rejection of outer ring channels of the FT0A detector") - O2_DEFINE_CONFIGURABLE(cfgRejectFT0CInside, bool, false, "Rejection of inner ring channels of the FT0C detector") - O2_DEFINE_CONFIGURABLE(cfgRejectFT0COutside, bool, false, "Rejection of outer ring channels of the FT0C detector") - O2_DEFINE_CONFIGURABLE(cfgRemapFT0ADeadChannels, bool, false, "If true, remap FT0A channels 60-63 to amplitudes from 92-95 respectively") - O2_DEFINE_CONFIGURABLE(cfgRemapFT0CDeadChannels, bool, false, "If true, remap FT0C channels 177->145, 176->144, 178->146, 179->147, 139->115")} cfgFITConfig; - - O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgMergingCut, float, 0.02, "Merging cut on track merge") - O2_DEFINE_CONFIGURABLE(cfgApplyTwoTrackEfficiency, bool, true, "Apply two track efficiency for tpc tpc") - O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") - O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - - struct : ConfigurableGroup { - O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") - Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") - Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") - Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); - O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") - Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; - std::vector multT0CCutPars; - std::vector multPVT0CCutPars; - std::vector multGlobalPVCutPars; - std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; - } cfgFuncParas; - - Configurable cfgCutFV0{"cfgCutFV0", 50., "FV0A threshold"}; - Configurable cfgCutFT0A{"cfgCutFT0A", 150., "FT0A threshold"}; - Configurable cfgCutFT0C{"cfgCutFT0C", 50., "FT0C threshold"}; - Configurable cfgCutZDC{"cfgCutZDC", 10., "ZDC threshold"}; - - SliceCache cache; - SliceCache cacheNch; - - ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; - ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt axis for histograms"}; - ConfigurableAxis axisAmbiguity{"axisAmbiguity", {100, 0, 100}, "MFT track ambiguity axis for histograms"}; - - ConfigurableAxis axisEtaMft{"axisEtaMFT", {40, -3.6, -2.4}, "eta axis for MFT tracks in histograms"}; - ConfigurableAxis axisEtaFt0a{"axisEtaFT0A", {40, 3.5, 4.9}, "eta axis for FT0A in histograms"}; - ConfigurableAxis axisEtaFt0c{"axisEtaFT0C", {40, -3.3, -2.1}, "eta axis for FT0C in histograms"}; - ConfigurableAxis axisEtaFv0{"axisEtaFV0", {40, 2.2, 5.1}, "eta axis for FV0 in histograms"}; - - ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; - ConfigurableAxis axisDeltaEta{"axisDeltaEta", {48, -1.6, 1.6}, "delta eta axis for histograms"}; - ConfigurableAxis axisDeltaEtaTpcMft{"axisDeltaEtaTPCMFT", {48, 1.6, 4.6}, "delta eta axis for TPC-MFT histograms"}; - ConfigurableAxis axisDeltaEtaTpcFv0{"axisDeltaEtaTPCFV0", {48, -1.7, -5.9}, "delta eta axis for TPC-FV0 histograms"}; - ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTPCFT0A", {48, -5.7, -2.7}, "delta eta axis for TPC-FT0A histograms"}; - ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTPCFT0C", {48, 1.3, 4.1}, "delta eta axis for TPC-FT0C histograms"}; - ConfigurableAxis axisDeltaEtaMftFt0c{"axisDeltaEtaMFTFT0C", {48, -2.0, 0.6}, "delta eta axis for MFT-FT0C histograms"}; - ConfigurableAxis axisDeltaEtaMftFt0a{"axisDeltaEtaMFTFT0A", {48, -8.5, -5.9}, "delta eta axis for MFT-FT0A histograms"}; - ConfigurableAxis axisDeltaEtaMftFv0{"axisDeltaEtaMFTFV0", {48, -8.6, -4.7}, "delta eta axis for MFT-FV0 histograms"}; - ConfigurableAxis axisDeltaEtaFt0s{"axisDeltaEtaFT0S", {48, -8.6, 5.0}, "delta eta axis for FT0S histograms"}; - - ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; - ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt associated axis for histograms"}; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80, 100}, "multiplicity / centrality axis for histograms"}; - ConfigurableAxis vtxMix{"vtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; - ConfigurableAxis multMix{"multMix", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80, 100}, "multiplicity / centrality axis for mixed event histograms"}; - ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; - - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0}, "pt axis for efficiency histograms"}; - - ConfigurableAxis axisAmplitudeFt0a{"axisAmplitudeFt0a", {5000, 0, 1000}, "FT0A amplitude"}; - ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; - - Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; - Configurable cfgCorrLevel{"cfgCorrLevel", 1, "calibration step: 0 = no corr, 1 = gain corr"}; - ConfigurableAxis cfgaxisFITamp{"cfgaxisFITamp", {1000, 0, 5000}, ""}; - AxisSpec axisFit{cfgaxisFITamp, "fit amplitude"}; - AxisSpec axisChID = {220, 0, 220}; - - // make the filters and cuts. - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgZVtxCut); - Filter trackFilter = (nabs(aod::track::eta) < cfgTrackCuts.cfgEtaCut) && (cfgTrackCuts.cfgPtCutMin < aod::track::pt) && (cfgTrackCuts.cfgPtCutMax > aod::track::pt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgTrackCuts.cfgCutChi2prTPCcls) && (aod::track::dcaZ < cfgTrackCuts.cfgCutDCAz); - - Filter mftTrackEtaFilter = ((aod::fwdtrack::eta < cfgMftConfig.etaMftTrackMaxFilter) && (aod::fwdtrack::eta > cfgMftConfig.etaMftTrackMinFilter)); - - // Filters below will be used for uncertainties - Filter mftTrackCollisionIdFilter = (aod::fwdtrack::bestCollisionId >= 0); - Filter mftTrackDcaXYFilter = (nabs(aod::fwdtrack::bestDCAXY) < cfgMftConfig.mftMaxDCAxy); - // Filter mftTrackDcaZFilter = (nabs(aod::fwdtrack::bestDCAZ) < cfgMftConfig.mftMaxDCAz); - - // using AodCollisions = soa::Filtered>; // aod::CentFT0Cs - // using AodTracks = soa::Filtered>; - - using AodCollisions = soa::Filtered>; - using AodTracks = soa::Filtered>; - using FilteredMftTracks = soa::Filtered; - using Reassociated2DMftTracks = aod::BestCollisionsFwd; - - Preslice perColGlobal = aod::track::collisionId; - - // FT0 geometry - o2::ft0::Geometry ft0Det; - o2::fv0::Geometry* fv0Det{}; - static constexpr uint64_t Ft0IndexA = 96; - std::vector* offsetFT0; - std::vector* offsetFV0; - - std::vector cstFT0RelGain{}; - - // Corrections - TH3D* mEfficiency = nullptr; - TH1D* mCentralityWeight = nullptr; - bool correctionsLoaded = false; - - // Define the outputs - - OutputObj same{"sameEvent"}; - OutputObj mixed{"mixedEvent"}; - - HistogramRegistry registry{"registry"}; - - // Define Global variables - - enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseGoodITSLayer0123, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kUseVertexITSTPC, - kUseTVXinTRD, - kNEventCuts - }; - - enum EventType { - SameEvent = 1, - MixedEvent = 3 - }; - - enum FITIndex { - kFT0A = 0, - kFT0C, - kFV0 - }; - - enum DetectorChannels { - kFT0AInnerRingMin = 0, - kFT0AInnerRingMax = 31, - kFT0AOuterRingMin = 32, - kFT0AOuterRingMax = 95, - kFT0CInnerRingMin = 96, - kFT0CInnerRingMax = 143, - kFT0COuterRingMin = 144, - kFT0COuterRingMax = 207 - }; - - std::array, 16> eventCuts; - - void init(InitContext&) - { - - const AxisSpec axisPhi{72, 0.0, constants::math::TwoPI, "#varphi"}; - const AxisSpec axisEta{40, -1., 1., "#eta"}; - const AxisSpec axisEtaFull{90, -4., 5., "#eta"}; - - ccdb->setURL("http://alice-ccdb.cern.ch"); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - - auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - ccdb->setCreatedNotAfter(now); - fv0Det = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); - - LOGF(info, "Starting init"); - - // Event Counter - if ((doprocessSameTpcFIT || doprocessSameTpcMft || doprocessSameTPC || doprocessSameMFTFIT || doprocessSameTpcMftReassociated2D || doprocessSameMftReassociated2DFIT || doprocessSameFT0s) && cfgUseAdditionalEventCut) { - registry.add("hEventCountSpecific", "Number of Event;; Count", {HistType::kTH1D, {{13, 0, 13}}}); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(1, "after sel8"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(2, "kNoSameBunchPileup"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(3, "kNoITSROFrameBorder"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(5, "kIsGoodZvtxFT0vsPV"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(6, "kNoCollInTimeRangeStandard"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(7, "kIsGoodITSLayer0123"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(8, "kIsGoodITSLayersAll"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(9, "kNoCollInRofStandard"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(10, "kNoHighMultCollInPrevRof"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(11, "occupancy"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(12, "MultCorrelation"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(13, "cfgEvSelV0AT0ACut"); - } - - if (doprocessSameTpcMftReassociated2D || doprocessSameMftReassociated2DFIT) { - registry.add("hEventCountMftReassoc", "Number of Events;; Count", {HistType::kTH1D, {{4, 0, 4}}}); - registry.get(HIST("hEventCountMftReassoc"))->GetXaxis()->SetBinLabel(1, "all MFT tracks"); - registry.get(HIST("hEventCountMftReassoc"))->GetXaxis()->SetBinLabel(2, "MFT tracks after selection"); - registry.get(HIST("hEventCountMftReassoc"))->GetXaxis()->SetBinLabel(3, "ambiguous MFT tracks"); - registry.get(HIST("hEventCountMftReassoc"))->GetXaxis()->SetBinLabel(4, "non-ambiguous MFT tracks"); - - registry.add("ReassociatedMftTracks", "Reassociated MFT tracks", {HistType::kTH1D, {{2, 0, 2}}}); - registry.get(HIST("ReassociatedMftTracks"))->GetXaxis()->SetBinLabel(1, "Not Reassociated MFT tracks"); - registry.get(HIST("ReassociatedMftTracks"))->GetXaxis()->SetBinLabel(2, "Reassociated MFT tracks"); - } - - // Make histograms to check the distributions after cuts - if (doprocessSameTpcFIT || doprocessSameTpcMft || doprocessSameTPC || doprocessSameMFTFIT || doprocessSameTpcMftReassociated2D || doprocessSameMftReassociated2DFIT || doprocessSameFT0s) { - registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); - if (doprocessSameMFTFIT) { - registry.add("Eta", "EtaMFT", {HistType::kTH1D, {axisEtaMft}}); - } - if (doprocessSameTpcFIT || doprocessSameTpcMft || doprocessSameTPC || doprocessSameTpcMftReassociated2D) { - registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); - } - registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); - registry.add("pT", "pT", {HistType::kTH1D, {axisPtTrigger}}); - registry.add("pTCorrected", "pTCorrected", {HistType::kTH1D, {axisPtTrigger}}); - registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("Nch_used", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event - registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); - registry.add("zVtx_used", "zVtx_used", {HistType::kTH1D, {axisVertex}}); - - if (doprocessSameTpcFIT || doprocessSameMFTFIT || doprocessSameMftReassociated2DFIT || doprocessSameFT0s) { - registry.add("FT0Amp", "", {HistType::kTH2F, {axisChID, axisFit}}); - registry.add("FV0Amp", "", {HistType::kTH2F, {axisChID, axisFit}}); - registry.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, axisFit}}); - registry.add("EtaPhi", "", {HistType::kTH2F, {axisEtaFull, axisPhi}}); - } - } - - if (doprocessSameTpcFIT) { - - if (cfgDetectorConfig.processFT0A) { - registry.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); - registry.add("Assoc_amp_same", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - if (cfgDetectorConfig.processFT0C) { - registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); - registry.add("Assoc_amp_same", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - if (cfgDetectorConfig.processFV0) { - registry.add("deltaEta_deltaPhi_same_TPC_FV0", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFv0}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_same_TPC_FV0", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFv0}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_FV0", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFv0}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - } - - if (doprocessSameMFTFIT || doprocessSameMftReassociated2DFIT) { - - if (cfgDetectorConfig.processFT0A) { - registry.add("deltaEta_deltaPhi_same_MFT_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaMftFt0a}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_MFT_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaMftFt0a}}); - registry.add("Assoc_amp_same", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - if (cfgDetectorConfig.processFT0C) { - registry.add("deltaEta_deltaPhi_same_MFT_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaMftFt0c}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_MFT_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaMftFt0c}}); - registry.add("Assoc_amp_same", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - if (cfgDetectorConfig.processFV0) { - registry.add("deltaEta_deltaPhi_same_MFT_FV0", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaMftFv0}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_MFT_FV0", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaMftFv0}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - registry.add("Assoc_amp_same", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - registry.add("Assoc_amp_mixed", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); - } - } - - if (doprocessSameTpcMft || doprocessSameTpcMftReassociated2D) { - registry.add("deltaEta_deltaPhi_same_TPC_MFT", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcMft}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_MFT", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcMft}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - - if (doprocessSameTPC) { - registry.add("deltaEta_deltaPhi_same_TPC_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_TPC_TPC", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - - if (doprocessSameFT0s) { - registry.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0s}}); // check to see the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0s}}); - registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - } - - registry.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event - - LOGF(info, "Initializing correlation container"); - - std::vector corrAxisTpcFt0c = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; - - std::vector effAxis = { - {axisEtaEfficiency, "#eta"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisVertexEfficiency, "z-vtx (cm)"}, - }; - - std::vector userAxis; - - // Correlation axis For TPC-FIT - - std::vector corrAxisTpcFt0a = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; - std::vector corrAxisTpcFv0 = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaTpcFv0, "#Delta#eta"}}; - - // correlation axis for TPC-TPC - std::vector corrAxisTpcTpc = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEta, "#Delta#eta"}}; - - // Correlation axis For TPC-MFT - std::vector corrAxisTpcMft = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaTpcMft, "#Delta#eta"}}; - - // Correlation axis For MFT-FIT - std::vector corrAxisMftFt0a = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaMftFt0a, "#Delta#eta"}}; - std::vector corrAxisMftFt0c = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaMftFt0c, "#Delta#eta"}}; - std::vector corrAxisMftFv0 = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaMftFv0, "#Delta#eta"}}; - - std::vector corrAxisFT0s = {{axisSample, "Sample"}, - {axisVertex, "z-vtx (cm)"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEtaFt0s, "#Delta#eta"}}; - - if (doprocessSameTpcFIT) { - if (cfgDetectorConfig.processFT0A) { - same.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); - } - if (cfgDetectorConfig.processFT0C) { - same.setObject(new CorrelationContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); - } - if (cfgDetectorConfig.processFV0) { - same.setObject(new CorrelationContainer("sameEvent_TPC_FV0", "sameEvent_TPC_FV0", corrAxisTpcFv0, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_TPC_FV0", "mixedEvent_TPC_FV0", corrAxisTpcFv0, effAxis, userAxis)); - } - } - - if (doprocessSameMFTFIT) { - if (cfgDetectorConfig.processFT0A) { - same.setObject(new CorrelationContainer("sameEvent_MFT_FT0A", "sameEvent_MFT_FT0A", corrAxisMftFt0a, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_MFT_FT0A", "mixedEvent_MFT_FT0A", corrAxisMftFt0a, effAxis, userAxis)); - } - if (cfgDetectorConfig.processFT0C) { - same.setObject(new CorrelationContainer("sameEvent_MFT_FT0C", "sameEvent_MFT_FT0C", corrAxisMftFt0c, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_MFT_FT0C", "mixedEvent_MFT_FT0C", corrAxisMftFt0c, effAxis, userAxis)); - } - if (cfgDetectorConfig.processFV0) { - same.setObject(new CorrelationContainer("sameEvent_MFT_FV0", "sameEvent_MFT_FV0", corrAxisMftFv0, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_MFT_FV0", "mixedEvent_MFT_FV0", corrAxisMftFv0, effAxis, userAxis)); - } - } - - if (doprocessSameMftReassociated2DFIT) { - if (cfgDetectorConfig.processFT0A) { - same.setObject(new CorrelationContainer("sameEvent_MFT_Reassociated2D_FT0A", "sameEvent_MFT_Reassociated2D_FT0A", corrAxisMftFt0a, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_MFT_Reassociated2D_FT0A", "mixedEvent_MFT_Reassociated2D_FT0A", corrAxisMftFt0a, effAxis, userAxis)); - } - if (cfgDetectorConfig.processFT0C) { - same.setObject(new CorrelationContainer("sameEvent_MFT_Reassociated2D_FT0C", "sameEvent_MFT_Reassociated2D_FT0C", corrAxisMftFt0c, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_MFT_Reassociated2D_FT0C", "mixedEvent_MFT_Reassociated2D_FT0C", corrAxisMftFt0c, effAxis, userAxis)); - } - if (cfgDetectorConfig.processFV0) { - same.setObject(new CorrelationContainer("sameEvent_MFT_Reassociated2D_FV0", "sameEvent_MFT_Reassociated2D_FV0", corrAxisMftFv0, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_MFT_Reassociated2D_FV0", "mixedEvent_MFT_Reassociated2D_FV0", corrAxisMftFv0, effAxis, userAxis)); - } - } - - if (doprocessSameTPC) { - same.setObject(new CorrelationContainer("sameEvent_TPC_TPC", "sameEvent_TPC_TPC", corrAxisTpcTpc, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_TPC_TPC", "mixedEvent_TPC_TPC", corrAxisTpcTpc, effAxis, userAxis)); - } - - if (doprocessSameTpcMft) { - same.setObject(new CorrelationContainer("sameEvent_TPC_MFT", "sameEvent_TPC_MFT", corrAxisTpcMft, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_TPC_MFT", "mixedEvent_TPC_MFT", corrAxisTpcMft, effAxis, userAxis)); - } - - if (doprocessSameTpcMftReassociated2D) { - same.setObject(new CorrelationContainer("sameEvent_TPC_MFT_Reassociated2D", "sameEvent_TPC_MFT_Reassociated2D", corrAxisTpcMft, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_TPC_MFT_Reassociated2D", "mixedEvent_TPC_MFT_Reassociated2D", corrAxisTpcMft, effAxis, userAxis)); - } - - if (doprocessSameFT0s) { - same.setObject(new CorrelationContainer("sameEvent_FT0A_FT0C", "sameEvent_FT0A_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent_FT0A_FT0C", "mixedEvent_FT0A_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); - } - LOGF(info, "End of init"); - } - - TRandom3* gRandom = new TRandom3(); - - template - bool eventSelected(TCollision collision, const int multTrk, const bool fillCounter) - { - registry.fill(HIST("hEventCountSpecific"), 0.5); - if (cfgEventSelection.cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - // rejects collisions which are associated with the same "found-by-T0" bunch crossing - // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkNoSameBunchPileup) - registry.fill(HIST("hEventCountSpecific"), 1.5); - if (cfgEventSelection.cfgEvSelkNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkNoITSROFrameBorder) - registry.fill(HIST("hEventCountSpecific"), 2.5); - if (cfgEventSelection.cfgEvSelkNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkNoTimeFrameBorder) - registry.fill(HIST("hEventCountSpecific"), 3.5); - if (cfgEventSelection.cfgEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference - // use this cut at low multiplicities with caution - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkIsGoodZvtxFT0vsPV) - registry.fill(HIST("hEventCountSpecific"), 4.5); - if (cfgEventSelection.cfgEvSelkNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - // no collisions in specified time range - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkNoCollInTimeRangeStandard) - registry.fill(HIST("hEventCountSpecific"), 5.5); - - if (cfgEventSelection.cfgEvSelkIsGoodITSLayer0123 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { - // from Jan 9 2025 AOT meeting - // cut time intervals with dead ITS staves - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkIsGoodITSLayer0123) - registry.fill(HIST("hEventCountSpecific"), 6.5); - - if (cfgEventSelection.cfgEvSelkIsGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - // from Jan 9 2025 AOT meeting - // cut time intervals with dead ITS staves - return 0; - } - - if (fillCounter && cfgEventSelection.cfgEvSelkIsGoodITSLayersAll) - registry.fill(HIST("hEventCountSpecific"), 7.5); - - if (cfgEventSelection.cfgEvSelkNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { - // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkNoCollInRofStandard) - registry.fill(HIST("hEventCountSpecific"), 8.5); - if (cfgEventSelection.cfgEvSelkNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { - // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; - } - if (fillCounter && cfgEventSelection.cfgEvSelkNoHighMultCollInPrevRof) - registry.fill(HIST("hEventCountSpecific"), 9.5); - auto occupancy = collision.trackOccupancyInTimeRange(); - if (cfgEventSelection.cfgEvSelOccupancy && (occupancy < cfgEventSelection.cfgCutOccupancyLow || occupancy > cfgEventSelection.cfgCutOccupancyHigh)) - return 0; - if (fillCounter && cfgEventSelection.cfgEvSelOccupancy) - registry.fill(HIST("hEventCountSpecific"), 10.5); - - auto multNTracksPV = collision.multNTracksPV(); - - if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { - if (multTrk < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) - return 0; - if (multTrk > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) - return 0; - } - if (cfgFuncParas.cfgMultMultV0ACutEnabled) { - if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(multTrk)) - return 0; - if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(multTrk)) - return 0; - } - - if (fillCounter && cfgEventSelection.cfgEvSelMultCorrelation) - registry.fill(HIST("hEventCountSpecific"), 11.5); - - // V0A T0A 5 sigma cut - float sigma = 5.0; - if (cfgEventSelection.cfgEvSelV0AT0ACut && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) - return 0; - if (fillCounter && cfgEventSelection.cfgEvSelV0AT0ACut) - registry.fill(HIST("hEventCountSpecific"), 12.5); - - return 1; - } - - double getPhiFV0(uint64_t chno) - { - o2::fv0::Point3Dsimple chPos{}; - int const cellsInLeft[] = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; - bool const isChnoInLeft = std::find(std::begin(cellsInLeft), std::end(cellsInLeft), chno) != std::end(cellsInLeft); - - if (isChnoInLeft) { - chPos = fv0Det->getReadoutCenter(chno); - return RecoDecay::phi(chPos.x + (*offsetFV0)[0].getX(), chPos.y + (*offsetFV0)[0].getY()); - } else { - chPos = fv0Det->getReadoutCenter(chno); - return RecoDecay::phi(chPos.x + (*offsetFV0)[1].getX(), chPos.y + (*offsetFV0)[1].getY()); - } - } - - double getPhiFT0(uint64_t chno, int i) - { - // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C - if (i > 1 || i < 0) { - LOGF(fatal, "kFIT Index %d out of range", i); - } - - ft0Det.calculateChannelCenter(); - auto chPos = ft0Det.getChannelCenter(chno); - return RecoDecay::phi(chPos.X() + (*offsetFT0)[i].getX(), chPos.Y() + (*offsetFT0)[i].getY()); - } - - double getEtaFV0(uint64_t chno) - { - - int const cellsInLeft[] = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; - bool const isChnoInLeft = std::find(std::begin(cellsInLeft), std::end(cellsInLeft), chno) != std::end(cellsInLeft); - - o2::fv0::Point3Dsimple chPos{}; - chPos = fv0Det->getReadoutCenter(chno); - - float offsetX, offsetY, offsetZ; - - if (isChnoInLeft) { - offsetX = (*offsetFV0)[0].getX(); - offsetY = (*offsetFV0)[0].getY(); - offsetZ = (*offsetFV0)[0].getZ(); - } else { - offsetX = (*offsetFV0)[1].getX(); - offsetY = (*offsetFV0)[1].getY(); - offsetZ = (*offsetFV0)[1].getZ(); - } - - auto x = chPos.x + offsetX; - auto y = chPos.y + offsetY; - auto z = chPos.z + offsetZ; - - auto r = std::sqrt(x * x + y * y); - auto theta = std::atan2(r, z); - - return -std::log(std::tan(0.5 * theta)); - } - - double getEtaFT0(uint64_t chno, int i) - { - // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C - if (i > 1 || i < 0) { - LOGF(fatal, "kFIT Index %d out of range", i); - } - ft0Det.calculateChannelCenter(); - auto chPos = ft0Det.getChannelCenter(chno); - auto x = chPos.X() + (*offsetFT0)[i].getX(); - auto y = chPos.Y() + (*offsetFT0)[i].getY(); - auto z = chPos.Z() + (*offsetFT0)[i].getZ(); - if (chno >= Ft0IndexA) { - z = -z; - } - auto r = std::sqrt(x * x + y * y); - auto theta = std::atan2(r, z); - return -std::log(std::tan(0.5 * theta)); - } - - template - bool isAcceptedMftTrack(TTrack const& mftTrack) - { - // cut on the eta of MFT tracks - if (mftTrack.eta() < cfgMftConfig.etaMftTrackMin || mftTrack.eta() > cfgMftConfig.etaMftTrackMax) { - return false; - } - - // cut on the number of clusters of the reconstructed MFT track - if (mftTrack.nClusters() < cfgMftConfig.nClustersMftTrack) { - return false; - } - - if (mftTrack.pt() < cfgMftConfig.cfgPtCutMinMFT || mftTrack.pt() > cfgMftConfig.cfgPtCutMaxMFT) { - return false; - } - - return true; - } - - template - bool isAmbiguousMftTrack(TTrack const& mftTrack, bool fillHistogram) - { - if (mftTrack.ambDegree() > 1) { - if (fillHistogram) { - registry.fill(HIST("hEventCountMftReassoc"), 2.5); // fill histogram for events with at least one ambiguous track); - } - return false; - } - registry.fill(HIST("hEventCountMftReassoc"), 3.5); // fill histogram for events without ambiguous tracks - return true; - } - - void loadAlignParam(uint64_t timestamp) - { - offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); - offsetFV0 = ccdb->getForTimeStamp>("FV0/Calib/Align", timestamp); - if (offsetFT0 == nullptr) { - LOGF(fatal, "Could not load FT0/Calib/Align for timestamp %d", timestamp); - } - if (offsetFV0 == nullptr) { - LOGF(fatal, "Could not load FV0/Calib/Align for timestamp %d", timestamp); - } - } - - void loadGain(aod::BCsWithTimestamps::iterator const& bc) - { - cstFT0RelGain.clear(); - cstFT0RelGain = {}; - std::string fullPath; - - auto timestamp = bc.timestamp(); - constexpr int ChannelsFT0 = 208; - if (cfgCorrLevel == 0) { - for (auto i{0u}; i < ChannelsFT0; i++) { - cstFT0RelGain.push_back(1.); - } - } else { - fullPath = cfgGainEqPath; - fullPath += "/FT0"; - const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); - if (!objft0Gain) { - for (auto i{0u}; i < ChannelsFT0; i++) { - cstFT0RelGain.push_back(1.); - } - } else { - cstFT0RelGain = *(objft0Gain); - } - } - } - - template - void getChannelWithGain(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType) - { - if (fitType == kFT0C) { - id = ft0.channelC()[iCh]; - id = id + Ft0IndexA; - ampl = ft0.amplitudeC()[iCh]; - registry.fill(HIST("FT0Amp"), id, ampl); - ampl = ampl / cstFT0RelGain[id]; - registry.fill(HIST("FT0AmpCorrect"), id, ampl); - } else if (fitType == kFT0A) { - id = ft0.channelA()[iCh]; - ampl = ft0.amplitudeA()[iCh]; - registry.fill(HIST("FT0Amp"), id, ampl); - ampl = ampl / cstFT0RelGain[id]; - registry.fill(HIST("FT0AmpCorrect"), id, ampl); - } else { - LOGF(fatal, "Cor Index %d out of range", fitType); - } - } - - void loadCorrection(uint64_t timestamp) - { - if (correctionsLoaded) { - return; - } - if (cfgEfficiency.value.empty() == false) { - if (cfgLocalEfficiency > 0) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); - mEfficiency = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); - } else { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); - } - if (mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); - } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); - } - if (cfgCentralityWeight.value.empty() == false) { - mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); - if (mCentralityWeight == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); - } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); - } - correctionsLoaded = true; - } - - bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ) - { - float eff = 1.; - if (mEfficiency) { - int etaBin = mEfficiency->GetXaxis()->FindBin(eta); - int ptBin = mEfficiency->GetYaxis()->FindBin(pt); - int zBin = mEfficiency->GetZaxis()->FindBin(posZ); - eff = mEfficiency->GetBinContent(etaBin, ptBin, zBin); - } else { - eff = 1.0; - } - if (eff == 0) - return false; - weight_nue = 1. / eff; - return true; - } - - template - void getChannelFT0(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType) - { - if (fitType == kFT0C) { - id = ft0.channelC()[iCh]; - id = id + Ft0IndexA; - ampl = ft0.amplitudeC()[iCh]; - } else if (fitType == kFT0A) { - id = ft0.channelA()[iCh]; - ampl = ft0.amplitudeA()[iCh]; - } else { - LOGF(fatal, "Cor Index %d out of range", fitType); - } - registry.fill(HIST("FT0Amp"), id, ampl); - } - - template - void getChannelFV0(TFT0s const& fv0, std::size_t const& iCh, int& id, float& ampl) - { - id = fv0.channel()[iCh]; - ampl = fv0.amplitude()[iCh]; - registry.fill(HIST("FV0Amp"), id, ampl); - } - - template - bool trackSelected(TTrack track) - { - return ((track.tpcNClsFound() >= cfgTrackCuts.cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgTrackCuts.cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgTrackCuts.cfgCutITSclu)); - } - - int getMagneticField(uint64_t timestamp) - { - // Get the magnetic field - static o2::parameters::GRPMagField* grpo = nullptr; - if (grpo == nullptr) { - grpo = ccdb->getForTimeStamp("/GLO/Config/GRPMagField", timestamp); - if (grpo == nullptr) { - LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); - return 0; - } - LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); - } - return grpo->getNominalL3Field(); - } - - // fill multiple histograms - template - void fillYield(TCollision collision, TTracks tracks) // function to fill the yield and etaphi histograms. - { - registry.fill(HIST("zVtx"), collision.posZ()); - registry.fill(HIST("Nch"), tracks.size()); - - float weff1 = 1.0; - float zvtx = collision.posZ(); - - for (auto const& track1 : tracks) { - - if constexpr (std::is_same_v) { - if (!isAcceptedMftTrack(track1)) { - continue; - } - } else { - if (!trackSelected(track1)) { - continue; - } - if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), zvtx)) { - continue; - } - } - - registry.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); - registry.fill(HIST("Eta"), track1.eta()); - registry.fill(HIST("EtaCorrected"), track1.eta(), weff1); - registry.fill(HIST("pT"), track1.pt()); - registry.fill(HIST("pTCorrected"), track1.pt(), weff1); - } - } - - template - float getDPhiStar(TTrack const& track1, TTrackAssoc const& track2, float radius, int magField) - { - float charge1 = track1.sign(); - float charge2 = track2.sign(); - - float phi1 = track1.phi(); - float phi2 = track2.phi(); - - float pt1 = track1.pt(); - float pt2 = track2.pt(); - - int fbSign = (magField > 0) ? 1 : -1; - - float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); - - if (dPhiStar > constants::math::PI) - dPhiStar = constants::math::TwoPI - dPhiStar; - if (dPhiStar < -constants::math::PI) - dPhiStar = -constants::math::TwoPI - dPhiStar; - - return dPhiStar; - } - - // Correlations for detectors and TPC - - ////////////////////////////// - //////////MFT/TPC-FIT//////// - //////////////////////////// - template - void fillCorrelationsFIT(TTracks tracks1, TTracksAssociated tracks2, FITs const&, float posZ, int system, int corType, float multiplicity) - { - - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); - float triggerWeight = 1.0f; - - // loop over all tracks - if (cfgQaCheck) { - - if (system == SameEvent) { - registry.fill(HIST("Nch_used"), multiplicity); - registry.fill(HIST("zVtx_used"), posZ); - } - } - - for (auto const& track1 : tracks1) { - - if constexpr (std::is_same_v) { - - if (!isAcceptedMftTrack(track1)) { - continue; - } - } else { - if (!trackSelected(track1)) { - continue; - } - - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) - continue; - } - - if (system == SameEvent) { - registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), triggerWeight); - } - - // if using FV0A for correlations / using FV0A as associated particles - if constexpr (std::is_same_v) { - - std::size_t channelSize = tracks2.channel().size(); - for (std::size_t iCh = 0; iCh < channelSize; iCh++) { - int channelID = 0; - float amplitude = 0.; - - getChannelFV0(tracks2, iCh, channelID, amplitude); - - auto phi = getPhiFV0(channelID); - auto eta = getEtaFV0(channelID); - - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); - float deltaEta = track1.eta() - eta; - - if (system == SameEvent) { - if (cfgDetectorConfig.processMFT) { - registry.fill(HIST("deltaEta_deltaPhi_same_MFT_FV0"), deltaPhi, deltaEta, amplitude * triggerWeight); - } else { - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FV0"), deltaPhi, deltaEta, amplitude * triggerWeight); - } - - registry.fill(HIST("Assoc_amp_same"), channelID, amplitude); - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, amplitude * triggerWeight); - - } else if (system == MixedEvent) { - if (cfgDetectorConfig.processMFT) { - registry.fill(HIST("deltaEta_deltaPhi_mixed_MFT_FV0"), deltaPhi, deltaEta, amplitude); - } else { - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FV0"), deltaPhi, deltaEta, amplitude); - } - registry.fill(HIST("Assoc_amp_mixed"), channelID, amplitude); - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, amplitude); - } - } - } - - // if using FT0A and FT0C for correlations / using FT0A and FT0C as associated particles - if constexpr (std::is_same_v) { - - std::size_t channelSize = 0; - if (corType == kFT0C) { - channelSize = tracks2.channelC().size(); - } else if (corType == kFT0A) { - channelSize = tracks2.channelA().size(); - } else { - LOGF(fatal, "Cor Index %d out of range", corType); - } - - for (std::size_t iCh = 0; iCh < channelSize; iCh++) { - int channelID = 0; - float amplitude = 0.; - if (cfgDetectorConfig.withGain) { - getChannelWithGain(tracks2, iCh, channelID, amplitude, corType); - } else { - getChannelFT0(tracks2, iCh, channelID, amplitude, corType); - } - - // reject depending on FT0C/FT0A rings - if (corType == kFT0C) { - if ((cfgFITConfig.cfgRejectFT0CInside && (channelID >= kFT0CInnerRingMin && channelID <= kFT0CInnerRingMax)) || (cfgFITConfig.cfgRejectFT0COutside && (channelID >= kFT0COuterRingMin && channelID <= kFT0COuterRingMax))) - continue; - } - if (corType == kFT0A) { - if ((cfgFITConfig.cfgRejectFT0AInside && (channelID >= kFT0AInnerRingMin && channelID <= kFT0AInnerRingMax)) || (cfgFITConfig.cfgRejectFT0AOutside && (channelID >= kFT0AOuterRingMin && channelID <= kFT0AOuterRingMax))) - continue; - } - - auto phi = getPhiFT0(channelID, corType); - auto eta = getEtaFT0(channelID, corType); - - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); - float deltaEta = track1.eta() - eta; - - if (system == SameEvent) { - if (corType == kFT0A) { - if (cfgDetectorConfig.processMFT) { - registry.fill(HIST("deltaEta_deltaPhi_same_MFT_FT0A"), deltaPhi, deltaEta, amplitude * triggerWeight); - } else { - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, amplitude * triggerWeight); - } - registry.fill(HIST("Assoc_amp_same"), channelID, amplitude); - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, amplitude * triggerWeight); - } - if (corType == kFT0C) { - if (cfgDetectorConfig.processMFT) { - registry.fill(HIST("deltaEta_deltaPhi_same_MFT_FT0C"), deltaPhi, deltaEta, amplitude * triggerWeight); - } else { - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, amplitude * triggerWeight); - } - registry.fill(HIST("Assoc_amp_same"), channelID, amplitude); - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, amplitude * triggerWeight); - } - } else if (system == MixedEvent) { - if (corType == kFT0A) { - if (cfgDetectorConfig.processMFT) { - registry.fill(HIST("deltaEta_deltaPhi_mixed_MFT_FT0A"), deltaPhi, deltaEta, amplitude); - } else { - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, amplitude); - } - registry.fill(HIST("Assoc_amp_mixed"), channelID, amplitude); - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, amplitude); - } - if (corType == kFT0C) { - if (cfgDetectorConfig.processMFT) { - registry.fill(HIST("deltaEta_deltaPhi_mixed_MFT_FT0C"), deltaPhi, deltaEta, amplitude); - } else { - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, amplitude); - } - registry.fill(HIST("Assoc_amp_mixed"), channelID, amplitude); - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, amplitude); - } - } - } - } - } - } - ////////////////////////// - //////////MFT-Reassociated///////// - ////////////////////////// - - template - void fillCorrelationsMftReassociatedFIT(TTracks tracks1, TTracksAssociated tracks2, FITs const&, float posZ, int system, int corType, float multiplicity, bool cutAmbiguousTracks) - { - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); - float triggerWeight = 1.0f; - - // loop over all tracks - if (cfgQaCheck) { - - if (system == SameEvent) { - registry.fill(HIST("Nch_used"), multiplicity); - registry.fill(HIST("zVtx_used"), posZ); - } - } - - for (auto const& track1 : tracks1) { - - auto reassociatedMftTrack = track1.template mfttrack_as(); - - if (!isAcceptedMftTrack(reassociatedMftTrack)) { - continue; - } - - if (isAmbiguousMftTrack(track1, false)) { - if (cutAmbiguousTracks) { - continue; - } - } - - if (system == SameEvent) { - - registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, reassociatedMftTrack.pt(), triggerWeight); - } - - if constexpr (std::is_same_v) { - - std::size_t channelSize = tracks2.channel().size(); - for (std::size_t iCh = 0; iCh < channelSize; iCh++) { - int channelID = 0; - float amplitude = 0.; - - getChannelFV0(tracks2, iCh, channelID, amplitude); - - auto phi = getPhiFV0(channelID); - auto eta = getEtaFV0(channelID); - - float deltaPhi = RecoDecay::constrainAngle(reassociatedMftTrack.phi() - phi, -PIHalf); - float deltaEta = reassociatedMftTrack.eta() - eta; - - if (system == SameEvent) { - registry.fill(HIST("Assoc_amp_same"), channelID, amplitude); - same->getPairHist()->Fill(step, fSampleIndex, posZ, reassociatedMftTrack.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta, amplitude * triggerWeight); - registry.fill(HIST("deltaEta_deltaPhi_same_MFT_FV0"), deltaPhi, deltaEta, amplitude * triggerWeight); - } else if (system == MixedEvent) { - registry.fill(HIST("Assoc_amp_mixed"), channelID, amplitude); - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, reassociatedMftTrack.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta, amplitude); - registry.fill(HIST("deltaEta_deltaPhi_mixed_MFT_FV0"), deltaPhi, deltaEta, amplitude); - } - } - } - - // if using FT0A and FT0C for correlations / using FT0A and FT0C as associated particles - if constexpr (std::is_same_v) { - - std::size_t channelSize = 0; - if (corType == kFT0C) { - channelSize = tracks2.channelC().size(); - } else if (corType == kFT0A) { - channelSize = tracks2.channelA().size(); - } else { - LOGF(fatal, "Cor Index %d out of range", corType); - } - - for (std::size_t iCh = 0; iCh < channelSize; iCh++) { - int channelID = 0; - float amplitude = 0.; - getChannelWithGain(tracks2, iCh, channelID, amplitude, corType); - - // reject depending on FT0C/FT0A rings - if (corType == kFT0C) { - if ((cfgFITConfig.cfgRejectFT0CInside && (channelID >= kFT0CInnerRingMin && channelID <= kFT0CInnerRingMax)) || (cfgFITConfig.cfgRejectFT0COutside && (channelID >= kFT0COuterRingMin && channelID <= kFT0COuterRingMax))) - continue; - } - if (corType == kFT0A) { - if ((cfgFITConfig.cfgRejectFT0AInside && (channelID >= kFT0AInnerRingMin && channelID <= kFT0AInnerRingMax)) || (cfgFITConfig.cfgRejectFT0AOutside && (channelID >= kFT0AOuterRingMin && channelID <= kFT0AOuterRingMax))) - continue; - } - - auto phi = getPhiFT0(channelID, corType); - auto eta = getEtaFT0(channelID, corType); - - float deltaPhi = RecoDecay::constrainAngle(reassociatedMftTrack.phi() - phi, -PIHalf); - float deltaEta = reassociatedMftTrack.eta() - eta; - - if (system == SameEvent) { - if (corType == kFT0A) { - registry.fill(HIST("Assoc_amp_same"), channelID, amplitude); - same->getPairHist()->Fill(step, fSampleIndex, posZ, reassociatedMftTrack.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta, amplitude * triggerWeight); - registry.fill(HIST("deltaEta_deltaPhi_same_MFT_FT0A"), deltaPhi, deltaEta, amplitude * triggerWeight); - } - if (corType == kFT0C) { - registry.fill(HIST("Assoc_amp_same"), channelID, amplitude); - same->getPairHist()->Fill(step, fSampleIndex, posZ, reassociatedMftTrack.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta, amplitude * triggerWeight); - registry.fill(HIST("deltaEta_deltaPhi_same_MFT_FT0C"), deltaPhi, deltaEta, amplitude * triggerWeight); - } - } else if (system == MixedEvent) { - if (corType == kFT0A) { - registry.fill(HIST("Assoc_amp_mixed"), channelID, amplitude); - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, reassociatedMftTrack.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta, amplitude); - registry.fill(HIST("deltaEta_deltaPhi_mixed_MFT_FT0A"), deltaPhi, deltaEta, amplitude); - } - if (corType == kFT0C) { - registry.fill(HIST("Assoc_amp_mixed"), channelID, amplitude); - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, reassociatedMftTrack.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta, amplitude); - registry.fill(HIST("deltaEta_deltaPhi_mixed_MFT_FT0C"), deltaPhi, deltaEta, amplitude); - } - } - } - } - } - } - - template - void fillCorrelationsMftReassociatedTracks(TTracks tracks1, TTracksAssoc tracks2, float multiplicity, float posZ, int system, int magneticField, bool cutAmbiguousTracks) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms - { - - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); - float triggerWeight = 1.0f; - - auto loopCounter = 0; - if (cfgQaCheck) { - - if (system == SameEvent) { - registry.fill(HIST("Nch_used"), multiplicity); - registry.fill(HIST("zVtx_used"), posZ); - } - } - // loop over all tracks - for (auto const& track1 : tracks1) { - - loopCounter++; - - if (!trackSelected(track1)) - continue; - - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) - continue; - - if (system == SameEvent) { - registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), triggerWeight); - } - - for (auto const& track2 : tracks2) { - - if (!cutAmbiguousTracks && system == SameEvent && (loopCounter == 1)) { - registry.fill(HIST("hEventCountMftReassoc"), 0.5); // fill histogram for events with at least one reassociated track); - } - - auto reassociatedMftTrack = track2.template mfttrack_as(); - - if (!isAcceptedMftTrack(reassociatedMftTrack)) { - continue; - } - - if (!cutAmbiguousTracks && system == SameEvent && (loopCounter == 1)) { - registry.fill(HIST("hEventCountMftReassoc"), 1.5); // fill histogram for events with at least one reassociated track after track selection); - } - - if (isAmbiguousMftTrack(track2, (!cutAmbiguousTracks && system == SameEvent && (loopCounter == 1)))) { - - if (SameEvent && (loopCounter == 1)) { - registry.fill(HIST("ReassociatedMftTracks"), 0.5); - } - if (cutAmbiguousTracks) { - continue; - } - } - - if (reassociatedMftTrack.collisionId() != track2.bestCollisionId()) { - if (SameEvent && (loopCounter == 1)) { - registry.fill(HIST("ReassociatedMftTracks"), 1.5); - } - } - - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - reassociatedMftTrack.phi(), -PIHalf); - float deltaEta = track1.eta() - reassociatedMftTrack.eta(); - - if (cfgApplyTwoTrackEfficiency && std::abs(deltaEta) < cfgMergingCut) { - - double dPhiStarHigh = getDPhiStar(track1, reassociatedMftTrack, cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(track1, reassociatedMftTrack, cfgRadiusLow, magneticField); - - const double kLimit = 3.0 * cfgMergingCut; - - bool bIsBelow = false; - - if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(track1, reassociatedMftTrack, rad, magneticField); - if (std::abs(dPhiStar) < kLimit) { - bIsBelow = true; - break; - } - } - if (bIsBelow) - continue; - } - } - - // fill the right sparse and histograms - if (system == SameEvent) { - - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta); - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_MFT"), deltaPhi, deltaEta); - } else if (system == MixedEvent) { - - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), reassociatedMftTrack.pt(), deltaPhi, deltaEta); - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_MFT"), deltaPhi, deltaEta); - } - } - } - } - - /////////////////////////////////////// - //////////TPC-TPC and TPC-MFT///////// - ///////////////////////////////////// - template - void fillCorrelations(TTracks tracks1, TTracksAssoc tracks2, float posZ, float multiplicity, int system, int magneticField) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms - { - - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); - - float triggerWeight = 1.0f; - - if (cfgQaCheck) { - if (system == SameEvent) { - registry.fill(HIST("Nch_used"), multiplicity); - registry.fill(HIST("zVtx_used"), posZ); - } - } - // loop over all tracks - for (auto const& track1 : tracks1) { - - if constexpr (std::is_same_v) { - if (!isAcceptedMftTrack(track1)) { - continue; - } - } else { - if (!trackSelected(track1)) - continue; - - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) - continue; - } - - if (system == SameEvent) { - registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), triggerWeight); - } - - for (auto const& track2 : tracks2) { - - if constexpr (std::is_same_v) { - if (!isAcceptedMftTrack(track2)) { - continue; - } - } else { - if (!trackSelected(track2)) - continue; - - if (track1.pt() <= track2.pt()) - continue; // skip if the trigger pt is less than the associate pt - } - - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); - float deltaEta = track1.eta() - track2.eta(); - - if (cfgApplyTwoTrackEfficiency && std::abs(deltaEta) < cfgMergingCut) { - - double dPhiStarHigh = getDPhiStar(track1, track2, cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(track1, track2, cfgRadiusLow, magneticField); - - const double kLimit = 3.0 * cfgMergingCut; - - bool bIsBelow = false; - - if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); - if (std::abs(dPhiStar) < kLimit) { - bIsBelow = true; - break; - } - } - if (bIsBelow) - continue; - } - } - - // fill the right sparse and histograms - if (system == SameEvent) { - if (cfgDetectorConfig.processMFT) { - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta); - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_MFT"), deltaPhi, deltaEta); - } else { - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta); - registry.fill(HIST("deltaEta_deltaPhi_same_TPC_TPC"), deltaPhi, deltaEta); - } - } else if (system == MixedEvent) { - if (cfgDetectorConfig.processMFT) { - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta); - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_MFT"), deltaPhi, deltaEta); - } else { - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta); - registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_TPC"), deltaPhi, deltaEta); - } - } - } - } - } - - template - void fillCorrelationsFT0s(TFT0s const& ft01, TFT0s const& ft02, float posZ, int multiplicity, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms - { - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); - - if (cfgQaCheck) { - if (system == SameEvent) { - registry.fill(HIST("zVtx_used"), posZ); - registry.fill(HIST("Nch"), multiplicity); - registry.fill(HIST("Nch_used_ft0c"), ft02.channelC().size()); - registry.fill(HIST("Nch_used_ft0a"), ft01.channelA().size()); - } - } - - float triggerWeight = 1.0f; - std::size_t channelASize = ft01.channelA().size(); - std::size_t channelCSize = ft02.channelC().size(); - // loop over all tracks - for (std::size_t iChA = 0; iChA < channelASize; iChA++) { - - int chanelAid = 0; - float amplA = 0.; - if (cfgDetectorConfig.withGain) { - getChannelWithGain(ft01, iChA, chanelAid, amplA, kFT0A); - } else { - getChannelFT0(ft01, iChA, chanelAid, amplA, kFT0A); - } - auto phiA = getPhiFT0(chanelAid, kFT0A); - auto etaA = getEtaFT0(chanelAid, kFT0A); - - if (system == SameEvent) { - registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, 0.5, eventWeight * amplA); - } - - for (std::size_t iChC = 0; iChC < channelCSize; iChC++) { - int chanelCid = 0; - float amplC = 0.; - if (cfgDetectorConfig.withGain) { - getChannelWithGain(ft02, iChC, chanelCid, amplC, kFT0C); - } else { - getChannelFT0(ft02, iChC, chanelCid, amplC, kFT0C); - } - auto phiC = getPhiFT0(chanelCid, kFT0C); - auto etaC = getEtaFT0(chanelCid, kFT0C); - float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); - float deltaEta = etaA - etaC; - - // fill the right sparse and histograms - if (system == SameEvent) { - registry.fill(HIST("deltaEta_deltaPhi_same_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); - same->getPairHist()->Fill(step, fSampleIndex, posZ, 0.5, 0.5, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); - } else if (system == MixedEvent) { - registry.fill(HIST("deltaEta_deltaPhi_mixed_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, 0.5, 0.5, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); - } - } - } - } - ////////////////////////////////////// - ////// Same event processing ///////// - ////////////////////////////////////// - - //////////////////////////////////// - /////////// Fwrd-Bwrd ///////////// - //////////////////////////////////// - - void processSameMFTFIT(AodCollisions::iterator const& collision, AodTracks const& tpctracks, aod::MFTTracks const& mfts, aod::FT0s const& ft0as, aod::FV0As const& fv0as, aod::BCsWithTimestamps const&) - { - - if (!collision.sel8()) - return; - - auto bc = collision.bc_as(); - - if (cfgUseAdditionalEventCut && !eventSelected(collision, tpctracks.size(), true)) - return; - - if (!collision.has_foundFT0()) - return; - loadAlignParam(bc.timestamp()); - if (cfgDetectorConfig.withGain) { - loadGain(bc); - } - - loadCorrection(bc.timestamp()); - - if ((tpctracks.size() < cfgEventSelection.cfgMinMult || tpctracks.size() >= cfgEventSelection.cfgMaxMult)) { - return; - } - if (mfts.size() == 0) { - return; - } - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - fillYield(collision, mfts); - const auto& multiplicity = tpctracks.size(); - - if (cfgDetectorConfig.processFV0) { - if (collision.has_foundFV0()) { - same->fillEvent(mfts.size(), CorrelationContainer::kCFStepReconstructed); - const auto& fv0 = collision.foundFV0(); - fillCorrelationsFIT(mfts, fv0, fv0as, collision.posZ(), SameEvent, kFV0, multiplicity); - } - } - if (cfgDetectorConfig.processFT0C) { - if (collision.has_foundFT0()) { - same->fillEvent(mfts.size(), CorrelationContainer::kCFStepReconstructed); - const auto& ft0 = collision.foundFT0(); - fillCorrelationsFIT(mfts, ft0, ft0as, collision.posZ(), SameEvent, kFT0C, multiplicity); - } - } - if (cfgDetectorConfig.processFT0A) { - if (collision.has_foundFT0()) { - same->fillEvent(mfts.size(), CorrelationContainer::kCFStepReconstructed); - const auto& ft0 = collision.foundFT0(); - fillCorrelationsFIT(mfts, ft0, ft0as, collision.posZ(), SameEvent, kFT0A, multiplicity); - } - } - } - PROCESS_SWITCH(CorrSparse, processSameMFTFIT, "Process same event for MFT-FIT correlation", true); - - void processSameMftReassociated2DFIT(AodCollisions::iterator const& collision, AodTracks const& tpctracks, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&, - aod::FT0s const& ft0as, aod::FV0As const& fv0as, aod::BCsWithTimestamps const&) - { - if (!collision.sel8()) - return; - - auto bc = collision.bc_as(); - - if (cfgUseAdditionalEventCut && !eventSelected(collision, tpctracks.size(), true)) - return; - - if (!collision.has_foundFT0()) - return; - loadAlignParam(bc.timestamp()); - loadGain(bc); - loadCorrection(bc.timestamp()); - - if ((tpctracks.size() < cfgEventSelection.cfgMinMult || tpctracks.size() >= cfgEventSelection.cfgMaxMult)) { - return; - } - if (reassociatedMftTracks.size() == 0) { - return; - } - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - const auto& multiplicity = tpctracks.size(); - - if (cfgDetectorConfig.processFV0) { - if (collision.has_foundFV0()) { - same->fillEvent(reassociatedMftTracks.size(), CorrelationContainer::kCFStepReconstructed); - const auto& fv0 = collision.foundFV0(); - fillCorrelationsMftReassociatedFIT(reassociatedMftTracks, fv0, fv0as, collision.posZ(), SameEvent, kFV0, multiplicity, false); - } - } - if (cfgDetectorConfig.processFT0C) { - if (collision.has_foundFT0()) { - same->fillEvent(reassociatedMftTracks.size(), CorrelationContainer::kCFStepReconstructed); - const auto& ft0 = collision.foundFT0(); - fillCorrelationsMftReassociatedFIT(reassociatedMftTracks, ft0, ft0as, collision.posZ(), SameEvent, kFT0C, multiplicity, false); - } - } - if (cfgDetectorConfig.processFT0A) { - if (collision.has_foundFT0()) { - same->fillEvent(reassociatedMftTracks.size(), CorrelationContainer::kCFStepReconstructed); - const auto& ft0 = collision.foundFT0(); - fillCorrelationsMftReassociatedFIT(reassociatedMftTracks, ft0, ft0as, collision.posZ(), SameEvent, kFT0A, multiplicity, false); - } - } - } - PROCESS_SWITCH(CorrSparse, processSameMftReassociated2DFIT, "Process same event for MFT-FIT correlation with reassociated tracks", false); - - ///////////////////////// - ////////Mid-Mid////////// - //////////////////////// - - void processSameTPC(AodCollisions::iterator const& collision, AodTracks const& tracks, aod::BCsWithTimestamps const&) - { - - auto bc = collision.bc_as(); - - if (!collision.sel8()) - return; - - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) - return; - - loadCorrection(bc.timestamp()); - - fillYield(collision, tracks); - - if (tracks.size() < cfgEventSelection.cfgMinMult || tracks.size() >= cfgEventSelection.cfgMaxMult) { - return; - } - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - - fillCorrelations(tracks, tracks, collision.posZ(), tracks.size(), SameEvent, getMagneticField(bc.timestamp())); - } - PROCESS_SWITCH(CorrSparse, processSameTPC, "Process same event for TPC-TPC correlation", false); - - ///////////////////// - ////////back-Mid-Fwrd////// - ///////////////////// - - void processSameTpcFIT(AodCollisions::iterator const& collision, AodTracks const& tracks, aod::FT0s const& ft0as, aod::FV0As const& fv0as, aod::BCsWithTimestamps const&) - { - if (!collision.sel8()) - return; - auto bc = collision.bc_as(); - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) - return; - if (!collision.has_foundFT0() && !collision.has_foundFV0()) - return; - - registry.fill(HIST("Nch"), tracks.size()); - registry.fill(HIST("zVtx"), collision.posZ()); - - if ((tracks.size() < cfgEventSelection.cfgMinMult || tracks.size() >= cfgEventSelection.cfgMaxMult)) { - return; - } - - loadAlignParam(bc.timestamp()); - if (cfgDetectorConfig.withGain) { - loadGain(bc); - } - - loadCorrection(bc.timestamp()); - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - fillYield(collision, tracks); - - const auto& multiplicity = tracks.size(); - - if (cfgDetectorConfig.processFV0) { - same->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - const auto& fv0 = collision.foundFV0(); - fillCorrelationsFIT(tracks, fv0, fv0as, collision.posZ(), SameEvent, kFV0, multiplicity); - } - if (cfgDetectorConfig.processFT0C) { - same->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - const auto& ft0 = collision.foundFT0(); - fillCorrelationsFIT(tracks, ft0, ft0as, collision.posZ(), SameEvent, kFT0C, multiplicity); - } - if (cfgDetectorConfig.processFT0A) { - same->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - const auto& ft0 = collision.foundFT0(); - fillCorrelationsFIT(tracks, ft0, ft0as, collision.posZ(), SameEvent, kFT0A, multiplicity); - } - } - PROCESS_SWITCH(CorrSparse, processSameTpcFIT, "process for forward or backwards correlations with TPC", false); - - void processSameTpcMft(AodCollisions::iterator const& collision, AodTracks const& tracks, aod::MFTTracks const& mfts, aod::BCsWithTimestamps const&) - { - if (!collision.sel8()) - return; - - auto bc = collision.bc_as(); - - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) - return; - - loadCorrection(bc.timestamp()); - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - - fillYield(collision, tracks); - - if (tracks.size() < cfgEventSelection.cfgMinMult || tracks.size() >= cfgEventSelection.cfgMaxMult) { - return; - } - - fillCorrelations(tracks, mfts, collision.posZ(), tracks.size(), SameEvent, getMagneticField(bc.timestamp())); - } - PROCESS_SWITCH(CorrSparse, processSameTpcMft, "Process same event for TPC-MFT correlation", false); - - void processSameTpcMftReassociated2D(AodCollisions::iterator const& collision, AodTracks const& tracks, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&, - aod::BCsWithTimestamps const&) - { - if (!collision.sel8()) - return; - - auto bc = collision.bc_as(); - - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), true)) - return; - - loadCorrection(bc.timestamp()); - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - - fillYield(collision, tracks); - - if (tracks.size() < cfgEventSelection.cfgMinMult || tracks.size() >= cfgEventSelection.cfgMaxMult) { - return; - } - - fillCorrelationsMftReassociatedTracks(tracks, reassociatedMftTracks, tracks.size(), collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), false); - } - PROCESS_SWITCH(CorrSparse, processSameTpcMftReassociated2D, "Process same event for TPC-MFT correlation with reassociated tracks", false); - - void processSameFT0s(AodCollisions::iterator const& collision, AodTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) - { - if (!collision.sel8()) - return; - - auto bc = collision.bc_as(); - - if (cfgUseAdditionalEventCut && !eventSelected(collision, 0, true)) - return; - - if (!collision.has_foundFT0()) - return; - - loadAlignParam(bc.timestamp()); - if (cfgDetectorConfig.withGain) { - loadGain(bc); - } - loadCorrection(bc.timestamp()); - - if ((tracks.size() < cfgEventSelection.cfgMinMult || tracks.size() >= cfgEventSelection.cfgMaxMult)) { - return; - } - - registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - - const auto& ft0s = collision.foundFT0(); - auto multiplicity = tracks.size(); - - fillCorrelationsFT0s(ft0s, ft0s, collision.posZ(), multiplicity, SameEvent, 1.0f); - } - PROCESS_SWITCH(CorrSparse, processSameFT0s, "Process same event for FT0 correlation", false); - //////////////////////////////////// - ////// Mixed event processing ////// - //////////////////////////////////// - - ///////////////////////////////////////// - ////////////// Fwrd- Bwrd////////////// - //////////////////////////////////////// - - void processMixedMFTFIT(AodCollisions const& collisions, AodTracks const& tpctracks, aod::MFTTracks const& mfts, aod::FT0s const& ft0as, aod::FV0As const& fv0as, aod::BCsWithTimestamps const&) - { - - auto getTracksSize = [&tpctracks, this](AodCollisions::iterator const& collision) { - auto associatedTracks = tpctracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; - }; - - using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; - - auto tracksTuple = std::make_tuple(mfts, mfts); - Pair pair{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - - for (auto it = pair.begin(); it != pair.end(); it++) { - auto& [collision1, tracks1, collision2, tracks2] = *it; - - if (!collision1.sel8() || !collision2.sel8()) - continue; - - if (collision1.globalIndex() == collision2.globalIndex()) { - continue; - } - - auto slicedtracks = tpctracks.sliceBy(perColGlobal, collision1.globalIndex()); - auto multiplicity = slicedtracks.size(); - - if ((multiplicity < cfgEventSelection.cfgMinMult || multiplicity >= cfgEventSelection.cfgMaxMult)) - continue; - - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) - continue; - - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) - continue; - - auto bc = collision1.bc_as(); - loadAlignParam(bc.timestamp()); - - loadCorrection(bc.timestamp()); - - if (cfgDetectorConfig.processFT0A) { - if (!collision1.has_foundFT0() && !collision2.has_foundFT0()) - continue; - - const auto& ft0 = collision2.foundFT0(); - fillCorrelationsFIT(tracks1, ft0, ft0as, collision1.posZ(), MixedEvent, kFT0A, multiplicity); - } - if (cfgDetectorConfig.processFT0C) { - if (!collision1.has_foundFT0() && !collision2.has_foundFT0()) - continue; - - const auto& ft0 = collision2.foundFT0(); - fillCorrelationsFIT(tracks1, ft0, ft0as, collision1.posZ(), MixedEvent, kFT0C, multiplicity); - } - if (cfgDetectorConfig.processFV0) { - if (collision1.has_foundFV0() && collision2.has_foundFV0()) { - const auto& fv0 = collision2.foundFV0(); - fillCorrelationsFIT(tracks1, fv0, fv0as, collision1.posZ(), MixedEvent, kFV0, multiplicity); - } - } - } - } - PROCESS_SWITCH(CorrSparse, processMixedMFTFIT, "Process mixed events for MFT-FIT correlation", true); - - ///////////////////////////// - //////////Mid-Mid/////////// - //////////////////////////// - - void processMixedTpcTpc(AodCollisions const& collisions, AodTracks const& tracks, aod::BCsWithTimestamps const&) - { - - auto getTracksSize = [&tracks, this](AodCollisions::iterator const& collision) { - auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; - }; - - using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; - - auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pair{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - for (auto const& [collision1, tracks1, collision2, tracks2] : pair) { - - if (!collision1.sel8() || !collision2.sel8()) - continue; - - if (collision1.globalIndex() == collision2.globalIndex()) { - continue; - } - - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); - - loadCorrection(bc.timestamp()); - - if ((tracks1.size() < cfgEventSelection.cfgMinMult || tracks1.size() >= cfgEventSelection.cfgMaxMult)) - continue; - - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) - continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) - continue; - - fillCorrelations(tracks1, tracks2, collision1.posZ(), tracks1.size(), MixedEvent, getMagneticField(bc.timestamp())); - } - } - PROCESS_SWITCH(CorrSparse, processMixedTpcTpc, "Process mixed events for TPC-TPC correlation", false); - - ////////////////////////////// - /////// back-Mid-Fwrd /////// - ///////////////////////////// - - void processMixedTpcFIT(AodCollisions const& collisions, AodTracks const& tracks, aod::FV0As const& fv0as, aod::FT0s const& ft0as, aod::BCsWithTimestamps const&) - { - auto getTracksSize = [&tracks, this](AodCollisions::iterator const& collision) { - auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; - }; - - using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; - - auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pair{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - - for (auto it = pair.begin(); it != pair.end(); it++) { - auto& [collision1, tracks1, collision2, tracks2] = *it; - - if (!collision1.sel8() || !collision2.sel8()) - continue; - - if (collision1.globalIndex() == collision2.globalIndex()) { - continue; - } - - if ((tracks1.size() < cfgEventSelection.cfgMinMult || tracks1.size() >= cfgEventSelection.cfgMaxMult)) - continue; - - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) - continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) - continue; - - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) - continue; - - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); - loadAlignParam(bc.timestamp()); - loadCorrection(bc.timestamp()); - - auto multiplicity = tracks1.size(); - - if (cfgDetectorConfig.processFT0A) { - const auto& ft0 = collision2.foundFT0(); - fillCorrelationsFIT(tracks1, ft0, ft0as, collision1.posZ(), MixedEvent, kFT0A, multiplicity); - } - if (cfgDetectorConfig.processFT0C) { - const auto& ft0 = collision2.foundFT0(); - fillCorrelationsFIT(tracks1, ft0, ft0as, collision1.posZ(), MixedEvent, kFT0C, multiplicity); - } - if (cfgDetectorConfig.processFV0) { - const auto& fv0 = collision2.foundFV0(); - fillCorrelationsFIT(tracks1, fv0, fv0as, collision1.posZ(), MixedEvent, kFV0, multiplicity); - } - } - } - PROCESS_SWITCH(CorrSparse, processMixedTpcFIT, "Process mixed events for TPC-FIT correlation", false); - - void processMixedTpcMFT(AodCollisions const& collisions, AodTracks const& tracks, aod::MFTTracks const& MFTtracks, aod::BCsWithTimestamps const&) - { - - auto getTracksSize = [&tracks, this](AodCollisions::iterator const& collision) { - auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; - }; - - using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; - - auto tracksTuple = std::make_tuple(tracks, MFTtracks); - Pair pair{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - for (auto const& [collision1, tracks1, collision2, tracks2] : pair) { - - if (!collision1.sel8() || !collision2.sel8()) - continue; - - if (collision1.globalIndex() == collision2.globalIndex()) { - continue; - } - - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); - - loadCorrection(bc.timestamp()); - - if ((tracks1.size() < cfgEventSelection.cfgMinMult || tracks1.size() >= cfgEventSelection.cfgMaxMult)) - continue; - - fillCorrelations(tracks1, tracks2, collision1.posZ(), tracks1.size(), MixedEvent, getMagneticField(bc.timestamp())); - } - } - PROCESS_SWITCH(CorrSparse, processMixedTpcMFT, "Process mixed events for TPC-MFT correlation", false); - - void processMixedFT0s(AodCollisions const& collisions, AodTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) - { - - auto getTracksSize = [&tracks, this](AodCollisions::iterator const& collision) { - auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; - }; - - using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; - - auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pair{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - - for (auto it = pair.begin(); it != pair.end(); it++) { - auto& [collision1, tracks1, collision2, tracks2] = *it; - - if (!collision1.sel8() || !collision2.sel8()) - continue; - - if (collision1.globalIndex() == collision2.globalIndex()) { - continue; - } - - if ((tracks1.size() < cfgEventSelection.cfgMinMult || tracks1.size() >= cfgEventSelection.cfgMaxMult)) - continue; - - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), false)) - continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), false)) - continue; - - if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) - continue; - - registry.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin - auto bc = collision1.bc_as(); - loadAlignParam(bc.timestamp()); - loadCorrection(bc.timestamp()); - - auto multiplicity = tracks1.size(); - - const auto& ft0_1 = collision1.foundFT0(); - const auto& ft0_2 = collision2.foundFT0(); - fillCorrelationsFT0s(ft0_1, ft0_2, collision1.posZ(), multiplicity, MixedEvent, 1.0f); - } - } - PROCESS_SWITCH(CorrSparse, processMixedFT0s, "Process mixed events for FT0 correlation", false); -}; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; -} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx index 99de5feb14e..61dfa8787c3 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/diHadronCor.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -73,6 +74,9 @@ struct DiHadronCor { O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") + O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, float, -1.0f, "max DCA to vertex xy, pT dependent in terms of N sigma. -1 for default cut") + O2_DEFINE_CONFIGURABLE(cfgDCAxyFunc, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut") + TF1* fPtDepDCAxy = nullptr; O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") @@ -160,7 +164,7 @@ struct DiHadronCor { // make the filters and cuts. Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVtxZ); - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; using FilteredTracksWithMCLabels = soa::Filtered>; @@ -287,6 +291,7 @@ struct DiHadronCor { registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); registry.add("zVtx_used", "zVtx_used", {HistType::kTH1D, {axisVertex}}); registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + registry.add("hDCAxy", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); } if (cfgSoloPtTrack && doprocessSame) { registry.add("Nch_final_pt", "pT", {HistType::kTH1D, {axisPtTrigger}}); @@ -345,6 +350,12 @@ struct DiHadronCor { same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + if (cfgCutDCAxy > 0.) { + fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgDCAxyFunc->c_str()), 0.001, 1000); + fPtDepDCAxy->SetParameter(0, cfgCutDCAxy); + LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", cfgCutDCAxy.value, cfgDCAxyFunc->c_str())); + } + LOGF(info, "End of init"); } @@ -387,13 +398,16 @@ struct DiHadronCor { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { + if (cfgCutDCAxy > 0. && (std::fabs(track.dcaXY()) > fPtDepDCAxy->Eval(track.pt()))) + return false; + return ((track.tpcNClsFound() >= cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgCutITSclu)); } template - bool genTrackSelected(TTrack track) + bool genTrackSelected(const TTrack& track) { if (!track.isPhysicalPrimary()) { return false; @@ -425,14 +439,14 @@ struct DiHadronCor { if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } if (cfgCentralityWeight.value.empty() == false) { mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); } correctionsLoaded = true; } @@ -469,7 +483,7 @@ struct DiHadronCor { // fill multiple histograms template - void fillYield(TCollision collision, TTracks tracks) // function to fill the yield and etaphi histograms. + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. { float weff1 = 1; float vtxz = collision.posZ(); @@ -511,7 +525,7 @@ struct DiHadronCor { } template - void fillCorrelations(TTracks tracks1, TTracksAssoc tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { // Cache efficiency for particles (too many FindBin lookups) if (mEfficiency) { @@ -544,6 +558,7 @@ struct DiHadronCor { if (system == SameEvent) { registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); } + registry.fill(HIST("hDCAxy"), track1.dcaXY(), track1.pt()); for (auto const& track2 : tracks2) { @@ -600,7 +615,7 @@ struct DiHadronCor { } template - void fillCorrelationsExcludeSoloTracks(TTracks tracks1, TTracksAssoc tracks2, float posZ, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelationsExcludeSoloTracks(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { std::vector tracksSkipIndices; std::vector tracks2SkipIndices; @@ -696,7 +711,7 @@ struct DiHadronCor { } template - void fillMCCorrelations(TTracks tracks1, TTracksAssoc tracks2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillMCCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); @@ -744,7 +759,7 @@ struct DiHadronCor { } template - bool eventSelected(TCollision collision, const int multTrk, const float centrality, const bool fillCounter) + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const bool fillCounter) { registry.fill(HIST("hEventCountSpecific"), 0.5); if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -933,7 +948,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1093,7 +1108,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1180,7 +1195,7 @@ struct DiHadronCor { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx b/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx index e8899bbd173..a531df7a4c7 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/dptDptEfficiencyAndQc.cxx @@ -98,8 +98,8 @@ enum BeforeAfter { }; /* the structures for checking the TPC sector borders impact */ -constexpr int kNoOfTpcSectors = 18; -constexpr float kTpcPhiSectorWidth = (constants::math::TwoPI) / kNoOfTpcSectors; +constexpr int KNoOfTpcSectors = 18; +constexpr float KTpcPhiSectorWidth = (constants::math::TwoPI) / KNoOfTpcSectors; /* the configuration of the nsigma axis */ float minNSigma = -4.05f; @@ -404,8 +404,8 @@ struct QADataCollectingEngine { using namespace analysis::dptdptfilter; using namespace o2::aod::track; - constexpr float kFiftyPerCent = 50.0f; - constexpr float kHundredPerCent = 100.0f; + constexpr float KFiftyPerCent = 50.0f; + constexpr float KHundredPerCent = 100.0f; fhPtB[kindOfData]->Fill(track.pt()); fhPtVsEtaB[kindOfData]->Fill(track.eta(), track.pt()); @@ -425,9 +425,9 @@ struct QADataCollectingEngine { bool hastpc = track.hasTPC() && TrackSelectionFlags::checkFlag(track.trackCutFlag(), TrackSelectionTPC); bool hastof = track.hasTOF(); - float phiInTpcSector = std::fmod(track.phi(), kTpcPhiSectorWidth); - float phiShiftedPercentInTpcSector = phiInTpcSector * 100 / kTpcPhiSectorWidth; - phiShiftedPercentInTpcSector = (phiShiftedPercentInTpcSector > kFiftyPerCent) ? (phiShiftedPercentInTpcSector - kHundredPerCent) : phiShiftedPercentInTpcSector; + float phiInTpcSector = std::fmod(track.phi(), KTpcPhiSectorWidth); + float phiShiftedPercentInTpcSector = phiInTpcSector * 100 / KTpcPhiSectorWidth; + phiShiftedPercentInTpcSector = (phiShiftedPercentInTpcSector > KFiftyPerCent) ? (phiShiftedPercentInTpcSector - KHundredPerCent) : phiShiftedPercentInTpcSector; if (track.sign() > 0) { fhPhiVsPtPosB->Fill(track.pt(), phiInTpcSector); fhPerColNchVsPhiVsPtPosB->Fill(track.pt(), phiInTpcSector); @@ -514,7 +514,7 @@ struct QADataCollectingEngine { fhTruePvsP->Fill(track.p(), mcparticle.p()); fhTruePvsInnerP->Fill(track.tpcInnerParam(), mcparticle.p()); - auto fillhisto = [](auto& h, float pt, float eta, bool cond1, bool cond2) { + auto fillmchisto = [](auto& h, float pt, float eta, bool cond1, bool cond2) { if (cond1 && cond2) { h->Fill(eta, pt); } @@ -522,15 +522,15 @@ struct QADataCollectingEngine { std::vector tPt = {track.pt(), mcparticle.pt()}; std::vector tEta = {track.eta(), mcparticle.eta()}; for (uint ix = 0; ix < tPt.size(); ++ix) { - fillhisto(fhPtVsEtaPrimItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isprimary); - fillhisto(fhPtVsEtaPrimItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isprimary); - fillhisto(fhPtVsEtaPrimItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isprimary); - fillhisto(fhPtVsEtaSecItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, issecdecay); - fillhisto(fhPtVsEtaSecItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, issecdecay); - fillhisto(fhPtVsEtaSecItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, issecdecay); - fillhisto(fhPtVsEtaMatItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isfrommaterial); - fillhisto(fhPtVsEtaMatItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isfrommaterial); - fillhisto(fhPtVsEtaMatItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isfrommaterial); + fillmchisto(fhPtVsEtaPrimItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isprimary); + fillmchisto(fhPtVsEtaPrimItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isprimary); + fillmchisto(fhPtVsEtaPrimItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isprimary); + fillmchisto(fhPtVsEtaSecItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, issecdecay); + fillmchisto(fhPtVsEtaSecItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, issecdecay); + fillmchisto(fhPtVsEtaSecItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, issecdecay); + fillmchisto(fhPtVsEtaMatItsA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits, isfrommaterial); + fillmchisto(fhPtVsEtaMatItsTpcA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastpc, isfrommaterial); + fillmchisto(fhPtVsEtaMatItsTpcTofA[ix][track.trackacceptedid()], tPt[ix], tEta[ix], hasits && hastof, isfrommaterial); } } } @@ -601,8 +601,8 @@ struct QAExtraDataCollectingEngine { // The QA output objects //=================================================== /* pairs histograms */ - constexpr static size_t kNoOfOverflowBins = 2; - constexpr static int kBinNotTracked = -1; + constexpr static size_t KNoOfOverflowBins = 2; + constexpr static int KBinNotTracked = -1; std::vector>>> fhPhiPhiA{2, {nsp, {nsp, nullptr}}}; std::vector>>> fhEtaEtaA{2, {nsp, {nsp, nullptr}}}; std::vector>>> fhN2VsDeltaEtaVsDeltaPhi{2, {nsp, {nsp, nullptr}}}; @@ -618,7 +618,7 @@ struct QAExtraDataCollectingEngine { /* the mapping between pT bins of interest and internal representation, and histogram title to keep track of them offline */ /* it is done once for both reco and gen */ - ptOfInterestBinMap = std::vector(static_cast(ptbins + kNoOfOverflowBins), kBinNotTracked); + ptOfInterestBinMap = std::vector(static_cast(ptbins + KNoOfOverflowBins), KBinNotTracked); LOGF(info, "Configuring the pT bins of interest on a map of length %d", ptOfInterestBinMap.size()); for (size_t ix = 0; ix < ptBinsOfInterest.size(); ++ix) { /* remember our internal axis starts in 0.5 value, i.e. its first central value is 1 */ @@ -634,10 +634,10 @@ struct QAExtraDataCollectingEngine { using namespace analysis::dptdptfilter; AxisSpec phiAxis = {phibins, 0.0f, constants::math::TwoPI, "#varphi"}; - AxisSpec phiSectorAxis = {72, 0.0f, kTpcPhiSectorWidth, "#varphi (mod(2#pi/18)) (rad)"}; + AxisSpec phiSectorAxis = {72, 0.0f, KTpcPhiSectorWidth, "#varphi (mod(2#pi/18)) (rad)"}; AxisSpec deltaPhiAxis = {phibins, 0.0f, constants::math::TwoPI, "#Delta#varphi (rad)"}; AxisSpec deltaEtaAxis = {2 * etabins - 1, etalow - etaup, etaup - etalow, "#Delta#eta"}; - AxisSpec deltaPhiInSectorAxis = {144, -kTpcPhiSectorWidth, kTpcPhiSectorWidth, "#Delta#varphi (rad)"}; + AxisSpec deltaPhiInSectorAxis = {144, -KTpcPhiSectorWidth, KTpcPhiSectorWidth, "#Delta#varphi (rad)"}; AxisSpec etaAxis = {etabins, etalow, etaup, "#eta"}; AxisSpec ptOfInterestAxis = {static_cast(ptBinsOfInterest.size()), 0.5f, static_cast(ptBinsOfInterest.size()) + 0.5f, "#it{p}_{T} (GeV/#it{c})"}; @@ -649,7 +649,7 @@ struct QAExtraDataCollectingEngine { std::string hPtRangesOfInterestTitle; bool firstRange = true; for (size_t ix = 0; ix < ptOfInterestBinMap.size(); ++ix) { - if (ptOfInterestBinMap[ix] != kBinNotTracked) { + if (ptOfInterestBinMap[ix] != KBinNotTracked) { TString ptRange = TString::Format("%s%.2f-%.2f", firstRange ? "" : ",", ptAxis.GetBinLowEdge(ix), ptAxis.GetBinUpEdge(ix)); hPtRangesOfInterestTitle += ptRange.Data(); LOGF(info, " Tracking pT bin %d as internal axis value %d", ix, ptOfInterestBinMap[ix]); @@ -689,8 +689,8 @@ struct QAExtraDataCollectingEngine { return ptOfInterestBinMap[ptAxis.FindFixBin(track.pt())]; }; int ptBin1 = binForPt(track1); - if (ptBin1 != kBinNotTracked) { - float inTpcSectorPhi1 = std::fmod(track1.phi(), kTpcPhiSectorWidth); + if (ptBin1 != KBinNotTracked) { + float inTpcSectorPhi1 = std::fmod(track1.phi(), KTpcPhiSectorWidth); for (auto const& track2 : tracks2) { /* checking the same track id condition */ if (track1 == track2) { @@ -698,7 +698,7 @@ struct QAExtraDataCollectingEngine { continue; } int ptBin2 = binForPt(track2); - if (ptBin2 != kBinNotTracked) { + if (ptBin2 != KBinNotTracked) { float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi()); float deltaEta = track1.eta() - track2.eta(); float preWeight = 1 - std::abs(deltaEta) / deltaEtaSpan; @@ -706,9 +706,9 @@ struct QAExtraDataCollectingEngine { fhPhiPhiA[kindOfData][track1.trackacceptedid()][track2.trackacceptedid()]->Fill(track1.phi(), track2.phi(), weight); fhEtaEtaA[kindOfData][track1.trackacceptedid()][track2.trackacceptedid()]->Fill(track1.eta(), track2.eta()); fhN2VsDeltaEtaVsDeltaPhi[kindOfData][track1.trackacceptedid()][track2.trackacceptedid()]->Fill(deltaEta, deltaPhi, weight); - if (static_cast(track1.phi() / kTpcPhiSectorWidth) == static_cast(track2.phi() / kTpcPhiSectorWidth)) { + if (static_cast(track1.phi() / KTpcPhiSectorWidth) == static_cast(track2.phi() / KTpcPhiSectorWidth)) { /* only if, for sure, both tracks are within the same sector */ - float inTpcSectorPhi2 = std::fmod(track2.phi(), kTpcPhiSectorWidth); + float inTpcSectorPhi2 = std::fmod(track2.phi(), KTpcPhiSectorWidth); float inTpcSectorDeltaPhi = inTpcSectorPhi1 - inTpcSectorPhi2; double values[] = {inTpcSectorPhi1, inTpcSectorPhi2, inTpcSectorDeltaPhi, static_cast(ptBin1), static_cast(ptBin2)}; fhInSectorDeltaPhiVsPhiPhiPerPtBinA[kindOfData][track1.trackacceptedid()][track2.trackacceptedid()]->Fill(values, weight); @@ -728,66 +728,66 @@ struct PidDataCollectingEngine { uint nmainsp = static_cast(efficiencyandqatask::mainspnames.size()); uint nallmainsp = static_cast(efficiencyandqatask::allmainspnames.size()); - constexpr static uint kNoOfSteps = 2; /* Before and after track selection */ + constexpr static uint KNoOfSteps = 2; /* Before and after track selection */ /* PID histograms */ /* before and after */ - std::vector> fhTPCdEdxSignalVsP{kNoOfSteps, nullptr}; - std::vector>> fhTPCdEdxSignalDiffVsP{kNoOfSteps, {nmainsp, nullptr}}; - std::vector>> fhTPCnSigmasVsP{kNoOfSteps, {nallmainsp, nullptr}}; - std::vector> fhTOFSignalVsP{kNoOfSteps, nullptr}; - std::vector>> fhTOFSignalDiffVsP{kNoOfSteps, {nmainsp, nullptr}}; - std::vector>> fhTOFnSigmasVsP{kNoOfSteps, {nallmainsp, nullptr}}; - std::vector> fhPvsTOFSqMass{kNoOfSteps, nullptr}; - std::vector>> fhTPCTOFSigmaVsP{kNoOfSteps, {nmainsp, nullptr}}; + std::vector> fhTPCdEdxSignalVsP{KNoOfSteps, nullptr}; + std::vector>> fhTPCdEdxSignalDiffVsP{KNoOfSteps, {nmainsp, nullptr}}; + std::vector>> fhTPCnSigmasVsP{KNoOfSteps, {nallmainsp, nullptr}}; + std::vector> fhTOFSignalVsP{KNoOfSteps, nullptr}; + std::vector>> fhTOFSignalDiffVsP{KNoOfSteps, {nmainsp, nullptr}}; + std::vector>> fhTOFnSigmasVsP{KNoOfSteps, {nallmainsp, nullptr}}; + std::vector> fhPvsTOFSqMass{KNoOfSteps, nullptr}; + std::vector>> fhTPCTOFSigmaVsP{KNoOfSteps, {nmainsp, nullptr}}; template void init(HistogramRegistry& registry, const char* dirname) { using namespace efficiencyandqatask; - const AxisSpec dEdxAxis{200, 0.0, 200.0, "dE/dx (au)"}; + AxisSpec dEdxAxis{200, 0.0, 200.0, "dE/dx (au)"}; AxisSpec pidPAxis{150, 0.1, 5.0, "#it{p} (GeV/#it{c})"}; pidPAxis.makeLogarithmic(); if constexpr (kindOfData == kReco) { /* PID histograms */ std::vector whenname{"Before", "After"}; - constexpr char kWhenPrefix[kNoOfSteps]{'B', 'A'}; + constexpr char KWhenPrefix[KNoOfSteps]{'B', 'A'}; std::vector whentitle{"before", ""}; - for (uint ix = 0; ix < kNoOfSteps; ++ix) { + for (uint ix = 0; ix < KNoOfSteps; ++ix) { fhTPCdEdxSignalVsP[ix] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("tpcSignalVsP%c", kWhenPrefix[ix]), + HNAMESTRING("tpcSignalVsP%c", KWhenPrefix[ix]), HTITLESTRING("TPC dE/dx signal %s", whentitle[ix].c_str()), kTH2F, {pidPAxis, dEdxAxis}); fhTOFSignalVsP[ix] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("tofSignalVsP%c", kWhenPrefix[ix]), + HNAMESTRING("tofSignalVsP%c", KWhenPrefix[ix]), HTITLESTRING("TOF signal %s", whentitle[ix].c_str()), kTH2F, {pidPAxis, {200, 0.0, 1.1, "#beta"}}); fhPvsTOFSqMass[ix] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("tofPvsMassSq%c", kWhenPrefix[ix]), + HNAMESTRING("tofPvsMassSq%c", KWhenPrefix[ix]), HTITLESTRING("Momentum versus #it{m}^{2} %s", whentitle[ix].c_str()), kTH2F, {{140, 0.0, 1.4, "#it{m}^{2} ((GeV/c^{2})^{2})"}, pidPAxis}); for (uint isp = 0; isp < nmainsp; ++isp) { fhTPCdEdxSignalDiffVsP[ix][isp] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("tpcSignalDiffVsP%c_%s", kWhenPrefix[ix], mainspnames[isp].c_str()), + HNAMESTRING("tpcSignalDiffVsP%c_%s", KWhenPrefix[ix], mainspnames[isp].c_str()), HTITLESTRING("TPC dE/dx to the %s line %s", mainsptitles[isp].c_str(), whentitle[ix].c_str()), kTH2F, {pidPAxis, {400, -200.0, 200.0, FORMATSTRING("dE/dx - _{%s}", mainsptitles[isp].c_str())}}); fhTOFSignalDiffVsP[ix][isp] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("tofSignalDiffVsP%c_%s", kWhenPrefix[ix], mainspnames[isp].c_str()), + HNAMESTRING("tofSignalDiffVsP%c_%s", KWhenPrefix[ix], mainspnames[isp].c_str()), HTITLESTRING("#Delta^{TOF_{%s}} %s", mainsptitles[isp].c_str(), whentitle[ix].c_str()), kTH2F, {pidPAxis, {200, -1000.0, 1000.0, FORMATSTRING("t-t_{ev}-t_{exp_{%s}} (ps)", mainsptitles[isp].c_str())}}); fhTPCTOFSigmaVsP[ix][isp] = ADDHISTOGRAM(TH3, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("toftpcNSigmasVsP%c_%s", kWhenPrefix[ix], mainspnames[isp].c_str()), + HNAMESTRING("toftpcNSigmasVsP%c_%s", KWhenPrefix[ix], mainspnames[isp].c_str()), HTITLESTRING("n#sigma to the %s line %s", mainsptitles[isp].c_str(), whentitle[ix].c_str()), kTH3F, {pidPAxis, {noOfNSigmaBins, minNSigma, maxNSigma, FORMATSTRING("n#sigma_{TPC}^{%s}", mainsptitles[isp].c_str())}, {120, -6.0, 6.0, FORMATSTRING("n#sigma_{TOF}^{%s}", mainsptitles[isp].c_str())}}); } for (uint isp = 0; isp < nallmainsp; ++isp) { fhTPCnSigmasVsP[ix][isp] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("tpcNSigmasVsP%c_%s", kWhenPrefix[ix], allmainspnames[isp].c_str()), + HNAMESTRING("tpcNSigmasVsP%c_%s", KWhenPrefix[ix], allmainspnames[isp].c_str()), HTITLESTRING("TPC n#sigma to the %s line %s", allmainsptitles[isp].c_str(), whentitle[ix].c_str()), kTH2F, {pidPAxis, {noOfNSigmaBins, minNSigma, maxNSigma, FORMATSTRING("n#sigma_{TPC}^{%s}", allmainsptitles[isp].c_str())}}); fhTOFnSigmasVsP[ix][isp] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", whenname[ix].c_str()), - HNAMESTRING("tofNSigmasVsP%c_%s", kWhenPrefix[ix], allmainspnames[isp].c_str()), + HNAMESTRING("tofNSigmasVsP%c_%s", KWhenPrefix[ix], allmainspnames[isp].c_str()), HTITLESTRING("TOF n#sigma to the %s line %s", allmainsptitles[isp].c_str(), whentitle[ix].c_str()), kTH2F, {pidPAxis, {noOfNSigmaBins, minNSigma, maxNSigma, FORMATSTRING("n#sigma_{TOF}^{%s}", allmainsptitles[isp].c_str())}}); } @@ -803,7 +803,7 @@ struct PidDataCollectingEngine { } else { ix = 2 * ix; } - for (uint when = 0; when < kNoOfSteps; ++when) { + for (uint when = 0; when < KNoOfSteps; ++when) { fhTPCnSigmasVsP[when][ix]->Fill(tpcmom, o2::aod::pidutils::tpcNSigma(track)); fhTOFnSigmasVsP[when][ix]->Fill(tofmom, o2::aod::pidutils::tofNSigma(track)); if (track.trackacceptedid() < 0) { @@ -821,7 +821,7 @@ struct PidDataCollectingEngine { } else { ix = 2 * ix; } - for (uint when = 0; when < kNoOfSteps; ++when) { + for (uint when = 0; when < KNoOfSteps; ++when) { fhTPCdEdxSignalDiffVsP[when][ix]->Fill(tpcmom, o2::aod::pidutils::tpcExpSignalDiff(track)); fhTOFSignalDiffVsP[when][ix]->Fill(tofmom, o2::aod::pidutils::tofExpSignalDiff(track)); fhTPCTOFSigmaVsP[when][ix]->Fill(tpcmom, o2::aod::pidutils::tpcNSigma(track), o2::aod::pidutils::tofNSigma(track)); @@ -835,7 +835,7 @@ struct PidDataCollectingEngine { template void fillPID(TrackObject const& track, float tpcmom, float tofmom) { - for (uint when = 0; when < kNoOfSteps; ++when) { + for (uint when = 0; when < KNoOfSteps; ++when) { if constexpr (framework::has_type_v) { fhTPCdEdxSignalVsP[when]->Fill(tpcmom, track.mcTunedTPCSignal()); } else { @@ -879,23 +879,20 @@ struct PidExtraDataCollectingEngine { /* only after track selection */ std::vector> fhIdTPCdEdxSignalVsP{nsp, nullptr}; std::vector> fpIdTPCdEdxSignalVsPSigmas{nsp, nullptr}; - std::vector>> fhIdTPCdEdxSignalDiffVsP{nsp, {nmainsp, nullptr}}; std::vector>> fhIdTPCnSigmasVsP{nsp, {nallmainsp, nullptr}}; std::vector> fhIdTOFSignalVsP{nsp, nullptr}; std::vector> fpIdTOFSignalVsPSigmas{nsp, nullptr}; - std::vector>> fhIdTOFSignalDiffVsP{nsp, {nmainsp, nullptr}}; std::vector>> fhIdTOFnSigmasVsP{nsp, {nallmainsp, nullptr}}; - std::vector> fhIdPvsTOFSqMass{nsp, nullptr}; template void init(HistogramRegistry& registry, const char* dirname) { using namespace efficiencyandqatask; - const AxisSpec dEdxAxis{200, 0.0, 200.0, "dE/dx (au)"}; + AxisSpec dEdxAxis{200, 0.0, 200.0, "dE/dx (au)"}; AxisSpec pidPAxis{150, 0.1, 5.0, "#it{p} (GeV/#it{c})"}; pidPAxis.makeLogarithmic(); - constexpr int kEvenOddBase = 2; + constexpr int KEvenOddBase = 2; if constexpr (kindOfData == kReco) { /* PID histograms */ @@ -918,7 +915,7 @@ struct PidExtraDataCollectingEngine { kTProfile2D, {pidPAxis, {200, 0.0, 1.1, "#beta"}}); for (uint imainsp = 0; imainsp < nallmainsp; ++imainsp) { /* only the same charge makes any sense */ - if (isp % kEvenOddBase == imainsp % kEvenOddBase) { + if (isp % KEvenOddBase == imainsp % KEvenOddBase) { fhIdTPCnSigmasVsP[isp][imainsp] = ADDHISTOGRAM(TH2, DIRECTORYSTRING("%s/%s/%s", dirname, "PID", "Selected"), HNAMESTRING("tpcNSigmasVsPSelected_%s_to%s", tnames[isp].c_str(), allmainspnames[imainsp].c_str()), HTITLESTRING("TPC n#sigma for selected %s to the %s line", tnames[isp].c_str(), allmainsptitles[imainsp].c_str()), @@ -1005,10 +1002,10 @@ struct DptDptEfficiencyAndQc { float* fCentMultMax = nullptr; /* the data collecting engine instances */ - QADataCollectingEngine** qaDataCE; - QAExtraDataCollectingEngine** qaExtraDataCE; - PidDataCollectingEngine** pidDataCE; - PidExtraDataCollectingEngine** pidExtraDataCE; + QADataCollectingEngine** qaDataCE = nullptr; + QAExtraDataCollectingEngine** qaExtraDataCE = nullptr; + PidDataCollectingEngine** pidDataCE = nullptr; + PidExtraDataCollectingEngine** pidExtraDataCE = nullptr; /* the histogram registries */ HistogramRegistry registryOne{"registryOne", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -1092,10 +1089,9 @@ struct DptDptEfficiencyAndQc { getTaskOptionValue(initContext, "dpt-dpt-filter", "cfgBinning.mPhibins", phibins, false); /* configuring the involved species */ - std::vector cfgnames = {"cfgElectronPIDSelection", "cfgMuonPIDSelection", "cfgPionPIDSelection", "cfgKaonPIDSelection", "cfgProtonPIDSelection"}; - std::vector spids = {0, 1, 2, 3, 4}; + std::vector cfgnames = {"cfgElectronPIDSelection", "cfgMuonPIDSelection", "cfgPionPIDSelection", "cfgKaonPIDSelection", "cfgProtonPIDSelection", "cfgDeuteronPIDSelection"}; for (uint i = 0; i < cfgnames.size(); ++i) { - auto includeIt = [&initContext](int spid, auto name) { + auto includeIt = [&initContext](int spid, const auto& name) { bool mUseIt = false; bool mExcludeIt = false; if (getTaskOptionValue(initContext, "dpt-dpt-filter-tracks", TString::Format("%s.mUseIt", name.c_str()).Data(), mUseIt, false) && @@ -1108,7 +1104,7 @@ struct DptDptEfficiencyAndQc { } } }; - includeIt(spids[i], cfgnames[i]); + includeIt(i, cfgnames[i]); } uint nspecies = pidselector.getNSpecies(); if (nspecies == 0) { @@ -1180,13 +1176,12 @@ struct DptDptEfficiencyAndQc { if (doPidExtraAnalysis) { pidExtraDataCE = new PidExtraDataCollectingEngine*[ncmranges]; } - std::string recogen; if (ncmranges > registryBank.size()) { LOGF(fatal, "There are more centrality ranges configured than registries in the bank. Please fix it!"); } /* in reverse order for proper order in results file */ for (uint i = 0; i < ncmranges; ++i) { - auto initializeCEInstance = [&](auto dce, auto name, auto& registry, bool reclevel, bool genlevel) { + auto initializeCEInstance = [&](auto dce, const auto& name, auto& registry, bool reclevel, bool genlevel) { /* crete the output list for the passed centrality/multiplicity range */ /* init the data collection instance */ if (reclevel) { @@ -1242,7 +1237,7 @@ struct DptDptEfficiencyAndQc { /// \brief Get the data collecting engine index corresponding to the passed collision template - int getDCEindex(FilteredCollision collision) + int getDCEindex(const FilteredCollision& collision) { if (!useCentrality.value) { return 0; @@ -1435,7 +1430,7 @@ using BCsWithTimestamps = soa::Join; struct CheckTimestamp { o2::ccdb::CcdbApi ccdbApi; - int mRunNumber; + int mRunNumber = 0; uint64_t runsor = 0; uint64_t runeor = 0; std::shared_ptr hTimeStampDiffNegative = nullptr; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx index fc26117acc0..24cf76f210a 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/etaDihadron.cxx @@ -159,7 +159,7 @@ struct EtaDihadron { // make the filters and cuts. Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVtxZ); - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; using FilteredTracksWithMCLabels = soa::Filtered>; @@ -386,13 +386,13 @@ struct EtaDihadron { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { return ((track.tpcNClsFound() >= cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgCutITSclu)); } template - bool genTrackSelected(TTrack track) + bool genTrackSelected(const TTrack& track) { if (!track.isPhysicalPrimary()) { return false; @@ -468,7 +468,7 @@ struct EtaDihadron { // fill multiple histograms template - void fillYield(TCollision collision, TTracks tracks) // function to fill the yield and etaphi histograms. + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. { float weff1 = 1; float vtxz = collision.posZ(); @@ -510,7 +510,7 @@ struct EtaDihadron { } template - void fillCorrelations(TTracks tracks1, TTracksAssoc tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { // Cache efficiency for particles (too many FindBin lookups) if (mEfficiency) { @@ -599,7 +599,7 @@ struct EtaDihadron { } template - void fillCorrelationsExcludeSoloTracks(TTracks tracks1, TTracksAssoc tracks2, float posZ, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelationsExcludeSoloTracks(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { std::vector tracksSkipIndices; std::vector tracks2SkipIndices; @@ -695,7 +695,7 @@ struct EtaDihadron { } template - void fillMCCorrelations(TTracks tracks1, TTracksAssoc tracks2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillMCCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); @@ -743,7 +743,7 @@ struct EtaDihadron { } template - bool eventSelected(TCollision collision, const int multTrk, const float centrality, const bool fillCounter) + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const bool fillCounter) { registry.fill(HIST("hEventCountSpecific"), 0.5); if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -932,7 +932,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1090,7 +1090,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1174,7 +1174,7 @@ struct EtaDihadron { using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx index 04b2a51a18a..9a2251ed1f5 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/flowDecorrelation.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -72,7 +73,9 @@ struct FlowDecorrelation { O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") - O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, float, 7.0f, "max DCA to vertex xy") + O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, float, -1.0f, "max DCA to vertex xy, pT dependent in terms of N sigma. -1 for default cut") + O2_DEFINE_CONFIGURABLE(cfgDCAxyFunc, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut") + TF1* fPtDepDCAxy = nullptr; O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") @@ -181,7 +184,7 @@ struct FlowDecorrelation { AxisSpec axisChID = {220, 0, 220}; // make the filters and cuts. Filter collisionFilter = (nabs(aod::collision::posZ) < cfgGeneralCuts.cfgCutVtxZ); - Filter trackFilter = (aod::track::pt > cfgGeneralCuts.cfgCutPtMin) && (aod::track::pt < cfgGeneralCuts.cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (aod::track::tpcChi2NCl < cfgGeneralCuts.cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgGeneralCuts.cfgCutDCAz) && (nabs(aod::track::dcaXY) < cfgGeneralCuts.cfgCutDCAxy); + Filter trackFilter = (aod::track::pt > cfgGeneralCuts.cfgCutPtMin) && (aod::track::pt < cfgGeneralCuts.cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (aod::track::tpcChi2NCl < cfgGeneralCuts.cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgGeneralCuts.cfgCutDCAz); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; @@ -200,8 +203,8 @@ struct FlowDecorrelation { o2::ft0::Geometry ft0Det; o2::fv0::Geometry* fv0Det{}; static constexpr uint64_t Ft0IndexA = 96; - std::vector* offsetFT0; - std::vector* offsetFV0; + std::vector* offsetFT0{}; + std::vector* offsetFV0{}; std::vector cstFT0RelGain{}; // Corrections @@ -256,9 +259,6 @@ struct FlowDecorrelation { kFT0COuterRingMin = 144, kFT0COuterRingMax = 207 }; - std::array tofNsigmaCut; - std::array itsNsigmaCut; - std::array tpcNsigmaCut; struct Ft0cCConstants { double zFt0cDistance = -83.44; double maxWidth = 12.; @@ -283,7 +283,6 @@ struct FlowDecorrelation { LOGF(fatal, "Centrality table is unavailable, cannot select collisions by centrality"); } const AxisSpec axisPhi{72, 0.0, constants::math::TwoPI, "#varphi"}; - const AxisSpec axisEta{40, -1., 1., "#eta"}; const AxisSpec axisEtaFull{90, -6., 6., "#eta"}; const AxisSpec axisCentrality{20, 0., 100., "cent"}; @@ -370,6 +369,7 @@ struct FlowDecorrelation { if (doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameFt0aFt0c || doprocessSameTpcMft || doprocessSameTpcFv0) { registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); + registry.add("hDCAxy", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH2D, {{200, -1., 1.}, {200, 0, 5}}}); registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); registry.add("Nch_used", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event @@ -494,6 +494,13 @@ struct FlowDecorrelation { same.setObject(new CorrelationContainer("sameEvent_TPC_FV0", "sameEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); mixed.setObject(new CorrelationContainer("mixedEvent_TPC_FV0", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); } + + if (cfgGeneralCuts.cfgCutDCAxy > 0.) { + cfgGeneralCuts.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgGeneralCuts.cfgDCAxyFunc->c_str()), 0.001, 1000); + cfgGeneralCuts.fPtDepDCAxy->SetParameter(0, cfgGeneralCuts.cfgCutDCAxy); + LOGF(info, "DCAxy pt-dependence function: %s", Form("%0.1f * %s", cfgGeneralCuts.cfgCutDCAxy.value, cfgGeneralCuts.cfgDCAxyFunc->c_str())); + } + LOGF(info, "End of init"); } @@ -613,8 +620,11 @@ struct FlowDecorrelation { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { + if (cfgGeneralCuts.cfgCutDCAxy > 0. && (std::fabs(track.dcaXY()) > cfgGeneralCuts.fPtDepDCAxy->Eval(track.pt()))) + return false; + return ((track.tpcNClsFound() >= cfgGeneralCuts.cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgGeneralCuts.cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgGeneralCuts.cfgCutITSclu)); } @@ -671,14 +681,14 @@ struct FlowDecorrelation { if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } if (cfgCentralityWeight.value.empty() == false) { mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); } correctionsLoaded = true; } @@ -715,7 +725,7 @@ struct FlowDecorrelation { // fill multiple histograms template - void fillYield(TCollision collision, TTracks tracks) // function to fill the yield and etaphi histograms. + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. { float weff1 = 1; float vtxz = collision.posZ(); @@ -762,7 +772,7 @@ struct FlowDecorrelation { } template - void fillCorrelationsTPCFIT(TTracks tracks1, TFT0s const& ft0, float posZ, int system, int corType, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelationsTPCFIT(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, int corType, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { if (system == SameEvent) { if (!cfgCentTableUnavailable) @@ -788,6 +798,7 @@ struct FlowDecorrelation { if (cfgDrawEtaPhiDis && corType == kFT0A) registry.fill(HIST("EtaPhi"), track1.eta(), track1.phi(), eventWeight * triggerWeight); } + registry.fill(HIST("hDCAxy"), track1.dcaXY(), track1.pt()); std::size_t channelSize = 0; if (corType == kFT0A) @@ -899,7 +910,7 @@ struct FlowDecorrelation { } template - void fillCorrelationsTPCFV0(TTracks tracks1, TFT0s const& fv0, float posZ, int system, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelationsTPCFV0(const TTracks& tracks1, TFT0s const& fv0, float posZ, int system, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { if (system == SameEvent) { if (!cfgCentTableUnavailable) @@ -920,6 +931,7 @@ struct FlowDecorrelation { if (system == SameEvent) { registry.fill(HIST("Trig_hist_TPC_FV0"), fSampleIndex, posZ, track1.eta(), eventWeight * triggerWeight); } + registry.fill(HIST("hDCAxy"), track1.dcaXY(), track1.pt()); std::size_t channelSize = 0; channelSize = fv0.channel().size(); @@ -952,7 +964,7 @@ struct FlowDecorrelation { } template - bool eventSelected(TCollision collision, const int multTrk, const float centrality, const bool fillCounter) + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const bool fillCounter) { registry.fill(HIST("hEventCountSpecific"), 0.5); if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { @@ -1093,8 +1105,6 @@ struct FlowDecorrelation { if (y < (Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y > (Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { return true; } - } - if (x < (Ft0aLocations.blockFar + Ft0aLocations.offSetX) && x > (Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { if (y > (-Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y < (-Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { return true; } @@ -1104,8 +1114,6 @@ struct FlowDecorrelation { if (y < (Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y > (Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { return true; } - } - if (x > (-Ft0aLocations.blockFar + Ft0aLocations.offSetX) && x < (-Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { if (y > (-Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y < (-Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { return true; } @@ -1115,9 +1123,6 @@ struct FlowDecorrelation { if (y > (-Ft0aLocations.blockFar + Ft0aLocations.offSetY - Ft0aLocations.rectOffSet) && y < (-Ft0aLocations.blockClose + Ft0aLocations.offSetY - Ft0aLocations.rectOffSet)) { return true; } - } - - if (x < (Ft0aLocations.blockClose + Ft0aLocations.offSetX) && x > (-Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { if (y < (Ft0aLocations.blockFar + Ft0aLocations.offSetY + Ft0aLocations.rectOffSet) && y > (Ft0aLocations.blockClose + Ft0aLocations.offSetY + Ft0aLocations.rectOffSet)) { return true; } @@ -1127,13 +1132,11 @@ struct FlowDecorrelation { if (x > (-Ft0aLocations.blockFar + Ft0aLocations.offSetX - Ft0aLocations.rectOffSet) && x < (-Ft0aLocations.blockClose + Ft0aLocations.offSetX - Ft0aLocations.rectOffSet)) { return true; } - } - - if (y < (Ft0aLocations.blockClose + Ft0aLocations.offSetY) && y > (-Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { if (x < (Ft0aLocations.blockFar + Ft0aLocations.offSetX + Ft0aLocations.rectOffSet) && x > (Ft0aLocations.blockClose + Ft0aLocations.offSetX + Ft0aLocations.rectOffSet)) { return true; } } + return false; } @@ -1190,7 +1193,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1295,7 +1298,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1397,7 +1400,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; @@ -1452,7 +1455,7 @@ struct FlowDecorrelation { PROCESS_SWITCH(FlowDecorrelation, processMixedFt0aFt0c, "Process mixed events for FT0A-FT0C correlation", false); template - void fillCorrelationsTPCMFT(TTracks tracks1, TTracksAssoc tracks2, float posZ, int system, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelationsTPCMFT(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { if (system == SameEvent) { if (!cfgCentTableUnavailable) @@ -1474,6 +1477,7 @@ struct FlowDecorrelation { if (system == SameEvent) { registry.fill(HIST("Trig_hist_TPC_MFT"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); } + registry.fill(HIST("hDCAxy"), track1.dcaXY(), track1.pt()); for (auto const& track2 : tracks2) { @@ -1548,7 +1552,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1661,7 +1665,7 @@ struct FlowDecorrelation { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip @@ -1714,7 +1718,7 @@ struct FlowDecorrelation { PROCESS_SWITCH(FlowDecorrelation, processMixedTpcFv0, "Process mixed events for TPC-FV0 correlation", false); template - void fillMCCorrelations(TTracks tracks1, TTracksAssoc tracks2, float posZ, int system, float eventWeight, int corType) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillMCCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float eventWeight, int corType) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); double tpcEtaAcceptance = 0.9; @@ -1784,7 +1788,13 @@ struct FlowDecorrelation { } } - if (cfgSelCollByNch && (mcParticles.size() < cfgGeneralCuts.cfgCutMultMin || mcParticles.size() >= cfgGeneralCuts.cfgCutMultMax)) { + int tpcMult = 0; + for (const auto& track : mcParticles) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult += 1; + } + + if (cfgSelCollByNch && (tpcMult < cfgGeneralCuts.cfgCutMultMin || tpcMult >= cfgGeneralCuts.cfgCutMultMax)) { return; } if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgGeneralCuts.cfgCutCentMin || cent >= cfgGeneralCuts.cfgCutCentMax)) { @@ -1794,7 +1804,7 @@ struct FlowDecorrelation { registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin if (!cfgCentTableUnavailable) registry.fill(HIST("MCTrue/MCCentrality"), cent); - registry.fill(HIST("MCTrue/MCNch"), mcParticles.size()); + registry.fill(HIST("MCTrue/MCNch"), tpcMult); registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); for (const auto& mcParticle : mcParticles) { if (mcParticle.isPhysicalPrimary()) { @@ -1804,12 +1814,12 @@ struct FlowDecorrelation { } } if (cfgMcTrue.cfgUseCFStepAll) { - same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepAll); + same->fillEvent(tpcMult, CorrelationContainer::kCFStepAll); fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0C); } registry.fill(HIST("MCTrue/MCeventcount"), 2.5); - same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepTrackedOnlyPrim); + same->fillEvent(tpcMult, CorrelationContainer::kCFStepTrackedOnlyPrim); fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0C); } PROCESS_SWITCH(FlowDecorrelation, processMcSameTpcFt0c, "Process MC same event", false); @@ -1818,35 +1828,54 @@ struct FlowDecorrelation { { auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; + int tpcMult = 0; + for (const auto& track : associatedTracks) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult += 1; + } + return tpcMult; }; using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (cfgSelCollByNch && (tracks1.size() < cfgGeneralCuts.cfgCutMultMin || tracks1.size() >= cfgGeneralCuts.cfgCutMultMax)) - continue; + int tpcMult1 = 0; + for (const auto& track : tracks1) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult1 += 1; + } + int tpcMult2 = 0; + for (const auto& track : tracks2) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult2 += 1; + } - if (cfgSelCollByNch && (tracks2.size() < cfgGeneralCuts.cfgCutMultMin || tracks2.size() >= cfgGeneralCuts.cfgCutMultMax)) + if (cfgSelCollByNch && (tpcMult1 < cfgGeneralCuts.cfgCutMultMin || tpcMult1 >= cfgGeneralCuts.cfgCutMultMax)) continue; - auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + if (cfgSelCollByNch && (tpcMult2 < cfgGeneralCuts.cfgCutMultMin || tpcMult2 >= cfgGeneralCuts.cfgCutMultMax)) + continue; - float cent = -1; + auto groupedCollisions1 = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMCCollision, collision2.globalIndex()); + float cent1 = -1; + float cent2 = -1; if (!cfgCentTableUnavailable) { - for (const auto& collision : groupedCollisions) { - cent = getCentrality(collision); + for (const auto& collision : groupedCollisions1) { + cent1 = getCentrality(collision); + } + for (const auto& collision : groupedCollisions2) { + cent2 = getCentrality(collision); } } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgGeneralCuts.cfgCutCentMin || cent >= cfgGeneralCuts.cfgCutCentMax)) + if (!cfgSelCollByNch && !cfgCentTableUnavailable && ((cent1 < cfgGeneralCuts.cfgCutCentMin || cent1 >= cfgGeneralCuts.cfgCutCentMax) || (cent2 < cfgGeneralCuts.cfgCutCentMin || cent2 >= cfgGeneralCuts.cfgCutCentMax))) continue; registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin @@ -1874,7 +1903,13 @@ struct FlowDecorrelation { } } - if (cfgSelCollByNch && (mcParticles.size() < cfgGeneralCuts.cfgCutMultMin || mcParticles.size() >= cfgGeneralCuts.cfgCutMultMax)) { + int tpcMult = 0; + for (const auto& track : mcParticles) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult += 1; + } + + if (cfgSelCollByNch && (tpcMult < cfgGeneralCuts.cfgCutMultMin || tpcMult >= cfgGeneralCuts.cfgCutMultMax)) { return; } if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgGeneralCuts.cfgCutCentMin || cent >= cfgGeneralCuts.cfgCutCentMax)) { @@ -1884,7 +1919,7 @@ struct FlowDecorrelation { registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin if (!cfgCentTableUnavailable) registry.fill(HIST("MCTrue/MCCentrality"), cent); - registry.fill(HIST("MCTrue/MCNch"), mcParticles.size()); + registry.fill(HIST("MCTrue/MCNch"), tpcMult); registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); for (const auto& mcParticle : mcParticles) { if (mcParticle.isPhysicalPrimary()) { @@ -1894,12 +1929,12 @@ struct FlowDecorrelation { } } if (cfgMcTrue.cfgUseCFStepAll) { - same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepAll); + same->fillEvent(tpcMult, CorrelationContainer::kCFStepAll); fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0A); } registry.fill(HIST("MCTrue/MCeventcount"), 2.5); - same->fillEvent(mcParticles.size(), CorrelationContainer::kCFStepTrackedOnlyPrim); + same->fillEvent(tpcMult, CorrelationContainer::kCFStepTrackedOnlyPrim); fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0A); } PROCESS_SWITCH(FlowDecorrelation, processMcSameTpcFt0a, "Process MC same event", false); @@ -1908,35 +1943,54 @@ struct FlowDecorrelation { { auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); - auto mult = associatedTracks.size(); - return mult; + int tpcMult = 0; + for (const auto& track : associatedTracks) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult += 1; + } + return tpcMult; }; using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (cfgSelCollByNch && (tracks1.size() < cfgGeneralCuts.cfgCutMultMin || tracks1.size() >= cfgGeneralCuts.cfgCutMultMax)) - continue; + int tpcMult1 = 0; + for (const auto& track : tracks1) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult1 += 1; + } + int tpcMult2 = 0; + for (const auto& track : tracks2) { + if (std::abs(track.eta()) < cfgMcTrue.cfgEtaTpcCut) + tpcMult2 += 1; + } - if (cfgSelCollByNch && (tracks2.size() < cfgGeneralCuts.cfgCutMultMin || tracks2.size() >= cfgGeneralCuts.cfgCutMultMax)) + if (cfgSelCollByNch && (tpcMult1 < cfgGeneralCuts.cfgCutMultMin || tpcMult1 >= cfgGeneralCuts.cfgCutMultMax)) continue; - auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + if (cfgSelCollByNch && (tpcMult2 < cfgGeneralCuts.cfgCutMultMin || tpcMult2 >= cfgGeneralCuts.cfgCutMultMax)) + continue; - float cent = -1; + auto groupedCollisions1 = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMCCollision, collision2.globalIndex()); + float cent1 = -1; + float cent2 = -1; if (!cfgCentTableUnavailable) { - for (const auto& collision : groupedCollisions) { - cent = getCentrality(collision); + for (const auto& collision : groupedCollisions1) { + cent1 = getCentrality(collision); + } + for (const auto& collision : groupedCollisions2) { + cent2 = getCentrality(collision); } } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgGeneralCuts.cfgCutCentMin || cent >= cfgGeneralCuts.cfgCutCentMax)) + if (!cfgSelCollByNch && !cfgCentTableUnavailable && ((cent1 < cfgGeneralCuts.cfgCutCentMin || cent1 >= cfgGeneralCuts.cfgCutCentMax) || (cent2 < cfgGeneralCuts.cfgCutCentMin || cent2 >= cfgGeneralCuts.cfgCutCentMax))) continue; registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin diff --git a/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx new file mode 100644 index 00000000000..032a5e988f9 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/hParticleCorrelation.cxx @@ -0,0 +1,4098 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file hParticleCorrelation.cxx +/// \brief Task for same- and mixed-event azimuthal angular correlations of trigger hadrons with charged hadrons, identified particles, and resonances +/// +/// \author Rahul Verma (rahul.verma@iitb.ac.in) :: Durgesh Bhatt (durgesh.bhatt@cern.ch) :: Sadhana Dash (sadhana@phy.iitb.ac.in) + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; // for constants + +namespace o2::aod +{ +namespace resonancecndt +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Candidate to collision +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, Tracks, "_Pos"); //! Positive track +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, Tracks, "_Neg"); //! Negative track + +// Candidate kinematics +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); + +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); + +// Invariant-mass hypotheses +DECLARE_SOA_COLUMN(MPhi1020, mPhi1020, float); +DECLARE_SOA_COLUMN(MKStar892, mKStar892, float); +DECLARE_SOA_COLUMN(MKStar892Bar, mKStar892Bar, float); +DECLARE_SOA_COLUMN(MLambda1520, mLambda1520, float); +DECLARE_SOA_COLUMN(MLambda1520Bar, mLambda1520Bar, float); + +// Mass-region tags +DECLARE_SOA_COLUMN(Phi1020Tag, phi1020Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892Tag, kStar892Tag, uint8_t); +DECLARE_SOA_COLUMN(KStar892BarTag, kStar892BarTag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520Tag, lambda1520Tag, uint8_t); +DECLARE_SOA_COLUMN(Lambda1520BarTag, lambda1520BarTag, uint8_t); + +} // namespace resonancecndt + +DECLARE_SOA_TABLE(ResonanceCndts, "AOD", "RESONCNDTS", o2::soa::Index<>, + o2::aod::resonancecndt::CollisionId, + o2::aod::resonancecndt::PosTrackId, + o2::aod::resonancecndt::NegTrackId, + + o2::aod::resonancecndt::Pt, + o2::aod::resonancecndt::Eta, + o2::aod::resonancecndt::Phi, + + o2::aod::resonancecndt::Px, + o2::aod::resonancecndt::Py, + o2::aod::resonancecndt::Pz, + + o2::aod::resonancecndt::MPhi1020, + o2::aod::resonancecndt::MKStar892, + o2::aod::resonancecndt::MKStar892Bar, + o2::aod::resonancecndt::MLambda1520, + o2::aod::resonancecndt::MLambda1520Bar, + + o2::aod::resonancecndt::Phi1020Tag, + o2::aod::resonancecndt::KStar892Tag, + o2::aod::resonancecndt::KStar892BarTag, + o2::aod::resonancecndt::Lambda1520Tag, + o2::aod::resonancecndt::Lambda1520BarTag); + +//______________________________________________________________________________ +// Derived correlation tables +//______________________________________________________________________________ + +namespace hpcorrcollision +{ +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Value of the mixing estimator chosen in the same-event task +DECLARE_SOA_COLUMN(CorrMask, corrMask, uint64_t); //! Mixing pools this collision belongs to (CorrPresenceMask bits) +} // namespace hpcorrcollision + +DECLARE_SOA_TABLE(HPCorrColls, "AOD", "HPCORRCOLL", o2::soa::Index<>, + o2::aod::collision::PosZ, + hpcorrcollision::Centrality, + hpcorrcollision::CorrMask); + +//______________________________________________________________________________ +// Trigger tracks +namespace hpcorrtrigger +{ +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep the derived collision-table relation) +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace hpcorrtrigger + +DECLARE_SOA_TABLE(HPCorrTriggers, "AOD", "HPCORRTRIG", o2::soa::Index<>, + hpcorrtrigger::HPCorrCollId, + hpcorrtrigger::Pt, + hpcorrtrigger::Eta, + hpcorrtrigger::Phi); + +//______________________________________________________________________________ +// Associated particles: tracks, identified hadrons and resonance candidates +namespace hpcorrassoc +{ +DECLARE_SOA_INDEX_COLUMN(HPCorrColl, hpCorrColl); // o2-linter: disable=name/o2-column (Keep the derived collision-table relation) +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Tracks: +1 (h+, pi+, K+, p) / -1 (h-, pi-, K-, pbar). Resonances: 0 (species, incl. anti-K*0 / anti-Lambda(1520), is in UsedMask) +DECLARE_SOA_COLUMN(UsedMask, usedMask, uint64_t); //! CorrPresenceMask bits of every correlation this particle was used in: 0-7 tracks (h, pi, K, p x low/high pT), 18-47 resonances (species x pT x mass window) +} // namespace hpcorrassoc + +DECLARE_SOA_TABLE(HPCorrAssocs, "AOD", "HPCORRASSOC", o2::soa::Index<>, + hpcorrassoc::HPCorrCollId, + hpcorrassoc::Pt, + hpcorrassoc::Eta, + hpcorrassoc::Phi, + hpcorrassoc::Sign, + hpcorrassoc::UsedMask); +} // namespace o2::aod + +// Delta phi in [-pi/2, 3pi/2) +float computeDeltaPhi(float particlePhi, float leadPhi) +{ + return RecoDecay::constrainAngle(particlePhi - leadPhi, -o2::constants::math::PIHalf); +} + +template +float computeRapidity(const T& track, const float mass) +{ + const float energy = RecoDecay::e(track.px(), track.py(), track.pz(), mass); + return 0.5f * std::log((energy + track.pz()) / (energy - track.pz())); +} + +template +inline T getCfg(const auto& cfgLabelledArray, int32_t row, int32_t col) +{ + double val = cfgLabelledArray->get(row, col); // Always stored as double + + if constexpr (std::is_same_v) { + return val != 0.0; // will return anything nonzero as true + } else if constexpr (std::is_same_v) { + // cppcheck-suppress suspiciousFloatingPointCast + return static_cast(val); + } else if constexpr (std::is_same_v) { + return val; + } else if constexpr (std::is_same_v) { + return static_cast(val); + } else { + static_assert( + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v, + "getCfg() only supports T = bool, float, double or int"); + } +} + +int binarySearchVector(const int64_t Key, const std::vector& List, int low, int high) // v2 : list by reference +{ + while (low <= high) { + int mid = low + (high - low) / 2; + if (Key == List[mid]) { + return mid; + } + + if (Key > List[mid]) { + low = mid + 1; // If Key is greater, ignore left half, update the low + } else { + high = mid - 1; + } // If Key is smaller, ignore right half, update the high + } + return -1; // Element is not present +} + +// do a fastest search in an sorted array. ==> Binary Search in an array. +template +bool checkTrackInList(const T& track, const std::vector& ParticleList) // v2 : list by reference +{ + return static_cast(binarySearchVector(track.globalIndex(), ParticleList, 0, ParticleList.size() - 1) != -1); +} + +enum CollisionRejectionTag { + kCollAccepted = 0, + + // Basic event selection + kCollRejectSel8, + kCollRejectVertexZ, + kCollRejectTriggerTVX, + + // TF / ROF borders + kCollRejectITSROFrameBorder, + kCollRejectTimeFrameBorder, + + // Vertex quality + kCollRejectVertexITSTPC, + kCollRejectGoodZvtxFT0vsPV, + kCollRejectVertexTOFmatched, + kCollRejectVertexTRDmatched, + kCollRejectGoodITSLayersAll, + + // Pileup / neighbouring collisions + kCollRejectSameBunchPileup, + kCollRejectCollInTimeRangeStandard, + kCollRejectCollInTimeRangeStrict, + kCollRejectCollInTimeRangeNarrow, + kCollRejectCollInRofStandard, + kCollRejectCollInRofStrict, + kCollRejectHighMultCollInPrevRof, + + // INEL classes + kCollRejectINELgt0, + kCollRejectINELgt1, + + // Multiplicity + kCollRejectMultiplicityLow, + kCollRejectMultiplicityHigh, + + // Centrality + kCollRejectCentralityLow, + kCollRejectCentralityHigh, + + // Occupancy + kCollRejectOccupancyLow, + kCollRejectOccupancyHigh, + + // Interaction rate + kCollRejectInteractionRateLow, + kCollRejectInteractionRateHigh, + + // RCT + kCollRejectRCT, + kCollRejectCorrelationRCT, + + kNCollisionRejectionTags +}; + +enum TrackRejectionTag { + kTrackAccepted = 0, + + // Kinematics + kTrackRejectPt, + kTrackRejectEta, + kTrackRejectMomentum, + kTrackRejectCharge, + + // Standard O2 track selections + kTrackRejectQualityTrack, + kTrackRejectQualityTrackITS, + kTrackRejectQualityTrackTPC, + kTrackRejectPrimaryTrack, + kTrackRejectInAcceptanceTrack, + kTrackRejectGlobalTrack, + kTrackRejectGlobalTrackWoDCA, + kTrackRejectGlobalTrackWoPtEta, + kTrackRejectGlobalTrackWoTPCCluster, + kTrackRejectGlobalTrackWoDCATPCCluster, + kTrackRejectPVContributor, + + // Detector presence + kTrackRejectITS, + kTrackRejectTPC, + kTrackRejectTOF, + kTrackRejectTRD, + + // TPC quality + kTrackRejectTPCNClsFound, + kTrackRejectTPCCrossedRows, + kTrackRejectTPCCrossedRowsOverFindable, + kTrackRejectTPCFoundOverFindable, + kTrackRejectTPCFractionShared, + kTrackRejectTPCChi2, + + // ITS quality + kTrackRejectITSNCls, + kTrackRejectITSNClsInnerBarrel, + kTrackRejectITSChi2, + + // DCA + kTrackRejectFixedDCAxy, + kTrackRejectFixedDCAz, + kTrackRejectPtDependentDCAxy, + kTrackRejectPtDependentDCAz, + + kNTrackRejectionTags +}; + +enum QAStageEnum { + kBeforeSelection = 0, + kAfterSelFunction, + kAfterSelection, + kNQAStages +}; + +static constexpr std::array QAStageDire = { + "BeforeSelection/", + "AfterSelFunction/", + "AfterSelection/"}; + +enum EventQABin { + kEventAllFiltered = 0, + kEventPassedSelCollision, + kEventPassedMinFilteredTracks, + kEventPassedMinSelectedTracks, + kNEventQABins +}; + +enum TrackQABin { + kTrackAllFiltered = 0, + kTrackPassedSelectionTrack, + kNTrackQABins +}; + +enum DataFrameQABin { + kDFWithZeroFilteredColls = 0, + kNDataFrameQABins +}; + +enum EventTypeEnum { + kSameEvent = 0, + kMixedEvent, + kNEventTypes +}; + +static constexpr std::array EventTypeDire = { + "SE/", + "ME/"}; + +enum PairTypeEnum { + kHH = 0, + kHId, + kHPhi, + kHKStar, + kHKStarBar, + kHLambda, + kHLambdaBar, + kPhiPhi, + kNPairTypes +}; + +static constexpr std::array PairTypeDire = { + "hh/", + "hIdentified/", + "hPhi/", + "hKStar/", + "hKStarBar/", + "hLambda/", + "hLambdaBar/", + "PhiPhi/"}; + +enum AnalysisTypeEnum { + kQA = 0, + kCorr, + kNAnalysisTypes +}; + +static constexpr std::array AnalysisTypeDire = { + "QA/", + "Corr/"}; + +enum CorrRoleEnum { + kTrigger = 0, + kAssocLowPt, + kAssocHighPt, + kNCorrRoles +}; + +static constexpr std::array CorrRoleDire = { + "Trigger/", + "AssocLowPt/", + "AssocHighPt/"}; + +enum PrimVtxMassRegionTag : uint8_t { + kMassOutside = 0, + kMassPeak = 1, + kMassLSB = 2, + kMassRSB = 3, + kMassNone = 4, + kNMassRegions = 5 +}; + +static constexpr std::array MassRegionDire = { + "Outside/", + "Peak/", + "LSB/", + "RSB/", + ""}; + +enum DauTypeEnum { + kPosDau = 0, + kNegDau, + kNDauTypes +}; + +static constexpr std::array DauTypeDire = { + "PosDau/", + "NegDau/"}; + +// template +// uint8_t getMassRegionTag(T mass, T lsbLow, T lsbUp, T peakLow, T peakUp, T rsbLow, T rsbUp) +uint8_t getMassRegionTag(float mass, float lsbLow, float lsbUp, float peakLow, float peakUp, float rsbLow, float rsbUp) +{ + if (lsbLow < mass && mass < lsbUp) { + return kMassLSB; + } + if (peakLow < mass && mass < peakUp) { + return kMassPeak; + } + if (rsbLow < mass && mass < rsbUp) { + return kMassRSB; + } + return kMassOutside; +} + +enum CorrCountTypeEnum { + kCountHH = 0, + kCountHPi, + kCountHKa, + kCountHPr, + kCountHPhi, + kCountHKStar, + kCountHKStarBar, + kCountHLambda, + kCountHLambdaBar, + kNCorrCountTypes +}; + +enum CorrCountPtEnum { + kCountLowPt = 0, + kCountHighPt, + kNCorrCountPt +}; + +static constexpr int NCorrCountChannels = static_cast(kNCorrCountTypes) * static_cast(kNCorrCountPt); +static constexpr std::array CorrCountTypeName = { + "hh", + "hPi", + "hKa", + "hPr", + "hPhi", + "hKStar", + "hKStarBar", + "hLambda", + "hLambdaBar"}; + +using CorrCountArray = std::array, kNCorrCountTypes>; +enum ResoCorrCountTypeEnum { + kResoCountHPhi = 0, + kResoCountHKStar, + kResoCountHKStarBar, + kResoCountHLambda, + kResoCountHLambdaBar, + kNResoCorrCountTypes +}; + +using ResoRegionCountArray = std::array, kNCorrCountPt>, kNResoCorrCountTypes>; + +struct MixingBinStatus { + uint64_t nCollisions = 0; // nCollisions = total selected collisions entering this mixing bin in this dataframe + CorrCountArray nCorrCollisions{}; // nCorrCollisions[hPhi][LowPt] = how many COLLISIONS in this mixing bin had >=1 hPhi-low correlation +}; + +enum PidEnum { + kPi = 0, + kKa, + kPr, + kEl, + kMu, + kDe, + kNPid +}; + +static constexpr std::array PidTypeDire = { + "Pi/", + "Ka/", + "Pr/", + "El/", + "Mu/", + "De/"}; + +enum CutSettingEnum { + kThrPforTOF = 0, + kIdCutTypeLowP, + kNSigmaTPCLowP, + kNSigmaTOFLowP, + kNSigmaRadLowP, + kIdCutTypeHighP, + kNSigmaTPCHighP, + kNSigmaTOFHighP, + kNSigmaRadHighP, + kDoVetoOthers, + kDoRelativeTPCcheck, + kDoRelativeTOFcheck, + kDoRelativeTPCTOFcheck, + kNCutSettings +}; + +enum VetoSettingEnum { + kDoVetoTPC = 0, + kDoVetoTOF, + kVetoTPC, + kVetoTOF, + kNVetoSettings +}; + +enum IdentificationType { + kTPCidentified = 0, + kTPCTOFidentified, + kUnidentified, + kNIdentificationTypes +}; + +enum TpcTofCutType { + kCircularCut = 0, + kRectangularCut, + kEllipsoidalCut, + kNCutTypes +}; + +enum MixingEstimatorEnum { + kMixCentFT0C = 0, + kMixCentFT0M, + kMixCentFT0A, + kMixCentFV0A, + kNMixingEstimators +}; + +enum CorrPresenceMask : uint64_t { + kMaskHHLowPt = 1ULL << 0, + kMaskHHHighPt = 1ULL << 1, + kMaskHPiLowPt = 1ULL << 2, + kMaskHPiHighPt = 1ULL << 3, + kMaskHKaLowPt = 1ULL << 4, + kMaskHKaHighPt = 1ULL << 5, + kMaskHPrLowPt = 1ULL << 6, + kMaskHPrHighPt = 1ULL << 7, + + kMaskHPhiLowPt = 1ULL << 8, + kMaskHPhiHighPt = 1ULL << 9, + kMaskHKStarLowPt = 1ULL << 10, + kMaskHKStarHighPt = 1ULL << 11, + kMaskHKStarBarLowPt = 1ULL << 12, + kMaskHKStarBarHighPt = 1ULL << 13, + kMaskHLambdaLowPt = 1ULL << 14, + kMaskHLambdaHighPt = 1ULL << 15, + kMaskHLambdaBarLowPt = 1ULL << 16, + kMaskHLambdaBarHighPt = 1ULL << 17, + + kMaskHPhiLowPtPeak = 1ULL << 18, + kMaskHPhiLowPtLSB = 1ULL << 19, + kMaskHPhiLowPtRSB = 1ULL << 20, + kMaskHPhiHighPtPeak = 1ULL << 21, + kMaskHPhiHighPtLSB = 1ULL << 22, + kMaskHPhiHighPtRSB = 1ULL << 23, + + kMaskHKStarLowPtPeak = 1ULL << 24, + kMaskHKStarLowPtLSB = 1ULL << 25, + kMaskHKStarLowPtRSB = 1ULL << 26, + kMaskHKStarHighPtPeak = 1ULL << 27, + kMaskHKStarHighPtLSB = 1ULL << 28, + kMaskHKStarHighPtRSB = 1ULL << 29, + + kMaskHKStarBarLowPtPeak = 1ULL << 30, + kMaskHKStarBarLowPtLSB = 1ULL << 31, + kMaskHKStarBarLowPtRSB = 1ULL << 32, + kMaskHKStarBarHighPtPeak = 1ULL << 33, + kMaskHKStarBarHighPtLSB = 1ULL << 34, + kMaskHKStarBarHighPtRSB = 1ULL << 35, + + kMaskHLambdaLowPtPeak = 1ULL << 36, + kMaskHLambdaLowPtLSB = 1ULL << 37, + kMaskHLambdaLowPtRSB = 1ULL << 38, + kMaskHLambdaHighPtPeak = 1ULL << 39, + kMaskHLambdaHighPtLSB = 1ULL << 40, + kMaskHLambdaHighPtRSB = 1ULL << 41, + + kMaskHLambdaBarLowPtPeak = 1ULL << 42, + kMaskHLambdaBarLowPtLSB = 1ULL << 43, + kMaskHLambdaBarLowPtRSB = 1ULL << 44, + kMaskHLambdaBarHighPtPeak = 1ULL << 45, + kMaskHLambdaBarHighPtLSB = 1ULL << 46, + kMaskHLambdaBarHighPtRSB = 1ULL << 47, + + kMaskAll = (1ULL << 48) - 1ULL +}; + +constexpr uint64_t getCorrMaskBit(int corrType, int ptType) +{ + return 1ULL << (corrType * kNCorrCountPt + ptType); +} + +constexpr uint64_t getResoRegionMaskBit(int resoType, int ptType, int massRegion) +{ + if (massRegion < kMassPeak || massRegion > kMassRSB) { + return 0ULL; + } + return 1ULL << (18 + resoType * 6 + ptType * 3 + (massRegion - kMassPeak)); +} + +// Bits 18-47 (kMaskHPhiLowPtPeak ... kMaskHLambdaBarHighPtRSB): resonance (species x pT x mass-window) pools +static constexpr uint64_t MaskResoRegions = static_cast(kMaskAll) & ~((1ULL << 18) - 1ULL); + +static constexpr std::array, kNPid> DefaultPIDcheckValues = {{ + {0.7, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Pi + {0.5, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Ka + {0.8, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Pr + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // El + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1}, // Mu + {0.4, 0, 3.0, 3.0, 9.0, 0, 3.0, 3.0, 9.0, 0, 1, 0, 1} // De +}}; + +static constexpr std::array, kNPid> DefaultPidVetoValues = {{ + {1, 1, 3.0, 3.0}, // Pi + {1, 1, 3.0, 3.0}, // Ka + {1, 1, 3.0, 3.0}, // Pr + {0, 0, 3.0, 3.0}, // El + {0, 0, 3.0, 3.0}, // Mu + {0, 0, 3.0, 3.0} // De +}}; + +//______________________________________________________________________________________________________________ +// Common functions independent of configurables used across the tasks + +template +bool relativeIdOthersTPC(const T& track) +{ + float distSpecies = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpecies = std::fabs(track.tpcNSigmaPi()); + } else if constexpr (pidMode == kKa) { + distSpecies = std::fabs(track.tpcNSigmaKa()); + } else if constexpr (pidMode == kPr) { + distSpecies = std::fabs(track.tpcNSigmaPr()); + } else { + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // else if constexpr (pidMode == kEl) { + // distSpecies = std::fabs(track.tpcNSigmaEl()); + // } else if constexpr (pidMode == kMu) { + // distSpecies = std::fabs(track.tpcNSigmaMu()); + // } else if constexpr (pidMode == kDe) { + // distSpecies = std::fabs(track.tpcNSigmaDe()); + // } + + return true; + } + + if constexpr (pidMode != kPi) { + if (std::fabs(track.tpcNSigmaPi()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (std::fabs(track.tpcNSigmaKa()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (std::fabs(track.tpcNSigmaPr()) <= distSpecies) { + return false; + } + } + + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) + // if (std::fabs(track.tpcNSigmaEl()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kMu) + // if (std::fabs(track.tpcNSigmaMu()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kDe) + // if (std::fabs(track.tpcNSigmaDe()) <= distSpecies) + // return false; + + return true; +} + +template +bool relativeIdOthersTOF(const T& track) +{ + float distSpecies = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpecies = std::fabs(track.tofNSigmaPi()); + } else if constexpr (pidMode == kKa) { + distSpecies = std::fabs(track.tofNSigmaKa()); + } else if constexpr (pidMode == kPr) { + distSpecies = std::fabs(track.tofNSigmaPr()); + + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // } else if constexpr (pidMode == kEl) { + // distSpecies = std::fabs(track.tofNSigmaEl()); + // } else if constexpr (pidMode == kMu) { + // distSpecies = std::fabs(track.tofNSigmaMu()); + // } else if constexpr (pidMode == kDe) { + // distSpecies = std::fabs(track.tofNSigmaDe()); + } else { + return true; + } + + if constexpr (pidMode != kPi) { + if (std::fabs(track.tofNSigmaPi()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (std::fabs(track.tofNSigmaKa()) <= distSpecies) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (std::fabs(track.tofNSigmaPr()) <= distSpecies) { + return false; + } + } + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) + // if (std::fabs(track.tofNSigmaEl()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kMu) + // if (std::fabs(track.tofNSigmaMu()) <= distSpecies) + // return false; + + // if constexpr (pidMode != kDe) + // if (std::fabs(track.tofNSigmaDe()) <= distSpecies) + // return false; + + return true; +} + +template +bool relativeIdOthersTPCTOF(const T& track) +{ + float distSpeciesSq = 1.e6f; + + if constexpr (pidMode == kPi) { + distSpeciesSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + distSpeciesSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + distSpeciesSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // } else if constexpr (pidMode == kEl) { + // distSpeciesSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + // } else if constexpr (pidMode == kMu) { + // distSpeciesSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + // } else if constexpr (pidMode == kDe) { + // distSpeciesSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + } else { + return true; + } + + if constexpr (pidMode != kPi) { + const float distSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + if (distSq <= distSpeciesSq) { + return false; + } + } + + if constexpr (pidMode != kKa) { + const float distSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + if (distSq <= distSpeciesSq) { + return false; + } + } + + if constexpr (pidMode != kPr) { + const float distSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + if (distSq <= distSpeciesSq) { + return false; + } + } + // FIX ME! : Relative check will be updated in future, currently implemented for pion, kaon and proton only + // if constexpr (pidMode != kEl) { + // const float distSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + // if constexpr (pidMode != kMu) { + // const float distSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + // if constexpr (pidMode != kDe) { + // const float distSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + // if (distSq <= distSpeciesSq) + // return false; + // } + + return true; +} + +template +bool selIdRectangularCut(const T& track, const float& nSigmaTPC, const float& nSigmaTOF) +{ + if constexpr (pidMode == kPi) { + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC && std::fabs(track.tofNSigmaPi()) < nSigmaTOF; + } else if constexpr (pidMode == kKa) { + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC && std::fabs(track.tofNSigmaKa()) < nSigmaTOF; + } else if constexpr (pidMode == kPr) { + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC && std::fabs(track.tofNSigmaPr()) < nSigmaTOF; + } else if constexpr (pidMode == kEl) { + return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC && std::fabs(track.tofNSigmaEl()) < nSigmaTOF; + } else if constexpr (pidMode == kMu) { + return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC && std::fabs(track.tofNSigmaMu()) < nSigmaTOF; + } else if constexpr (pidMode == kDe) { + return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC && std::fabs(track.tofNSigmaDe()) < nSigmaTOF; + } else { + return false; + } +} + +template +bool selIdEllipsoidalCut(const T& track, const float& nSigmaTPC, const float& nSigmaTOF) +{ + if constexpr (pidMode == kPi) { + float tpc = track.tpcNSigmaPi() / nSigmaTPC; + float tof = track.tofNSigmaPi() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kKa) { + float tpc = track.tpcNSigmaKa() / nSigmaTPC; + float tof = track.tofNSigmaKa() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kPr) { + float tpc = track.tpcNSigmaPr() / nSigmaTPC; + float tof = track.tofNSigmaPr() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kEl) { + float tpc = track.tpcNSigmaEl() / nSigmaTPC; + float tof = track.tofNSigmaEl() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kMu) { + float tpc = track.tpcNSigmaMu() / nSigmaTPC; + float tof = track.tofNSigmaMu() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else if constexpr (pidMode == kDe) { + float tpc = track.tpcNSigmaDe() / nSigmaTPC; + float tof = track.tofNSigmaDe() / nSigmaTOF; + return (tpc * tpc + tof * tof) < 1.0f; + } else { + return false; + } +} + +// Circular cut : (tpcNSigma^2 + tofNSigma^2) < nSigmaSquaredRad ; for 3 sigma, nSigmaSquaredRad = 9.0 +template +constexpr bool selIdCircularCut(const T& track, const float& nSigmaSquaredRad) +{ + float tpc = 0.f; + float tof = 0.f; + + if constexpr (pidMode == kPi) { + tpc = track.tpcNSigmaPi(); + tof = track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + tpc = track.tpcNSigmaKa(); + tof = track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + tpc = track.tpcNSigmaPr(); + tof = track.tofNSigmaPr(); + } else if constexpr (pidMode == kEl) { + tpc = track.tpcNSigmaEl(); + tof = track.tofNSigmaEl(); + } else if constexpr (pidMode == kMu) { + tpc = track.tpcNSigmaMu(); + tof = track.tofNSigmaMu(); + } else if constexpr (pidMode == kDe) { + tpc = track.tpcNSigmaDe(); + tof = track.tofNSigmaDe(); + } else { + return false; // unknown pidMode + } + return (tpc * tpc + tof * tof) < nSigmaSquaredRad; +} + +template +bool applyVetoOthersTPC(const T& track, const std::array& doVetoTPC, const std::array& vetoTPC) +{ + if constexpr (pidMode != kPi) { + if (doVetoTPC[kPi] && std::fabs(track.tpcNSigmaPi()) < vetoTPC[kPi]) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (doVetoTPC[kKa] && std::fabs(track.tpcNSigmaKa()) < vetoTPC[kKa]) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (doVetoTPC[kPr] && std::fabs(track.tpcNSigmaPr()) < vetoTPC[kPr]) { + return false; + } + } + + if constexpr (pidMode != kEl) { + if (doVetoTPC[kEl] && std::fabs(track.tpcNSigmaEl()) < vetoTPC[kEl]) { + return false; + } + } + + if constexpr (pidMode != kMu) { + if (doVetoTPC[kMu] && std::fabs(track.tpcNSigmaMu()) < vetoTPC[kMu]) { + return false; + } + } + + if constexpr (pidMode != kDe) { + if (doVetoTPC[kDe] && std::fabs(track.tpcNSigmaDe()) < vetoTPC[kDe]) { + return false; + } + } + + return true; +} + +template +bool applyVetoOthersTOF(const T& track, const std::array& doVetoTOF, const std::array& vetoTOF) +{ + if constexpr (pidMode != kPi) { + if (doVetoTOF[kPi] && std::fabs(track.tofNSigmaPi()) < vetoTOF[kPi]) { + return false; + } + } + + if constexpr (pidMode != kKa) { + if (doVetoTOF[kKa] && std::fabs(track.tofNSigmaKa()) < vetoTOF[kKa]) { + return false; + } + } + + if constexpr (pidMode != kPr) { + if (doVetoTOF[kPr] && std::fabs(track.tofNSigmaPr()) < vetoTOF[kPr]) { + return false; + } + } + + if constexpr (pidMode != kEl) { + if (doVetoTOF[kEl] && std::fabs(track.tofNSigmaEl()) < vetoTOF[kEl]) { + return false; + } + } + + if constexpr (pidMode != kMu) { + if (doVetoTOF[kMu] && std::fabs(track.tofNSigmaMu()) < vetoTOF[kMu]) { + return false; + } + } + + if constexpr (pidMode != kDe) { + if (doVetoTOF[kDe] && std::fabs(track.tofNSigmaDe()) < vetoTOF[kDe]) { + return false; + } + } + + return true; +} + +// Common Helper functions are over. +//__________________________________________________________________________________________________________________________ + +//__________________________________________________________________________________________________________________________ +// Common Histograms booking functions - to be used inside +template +void addBasicTrackQAHistos(H& histReg, const std::string& basePath, const AxisSpec& axisP, const AxisSpec& axisPt, const AxisSpec& axisEta, const AxisSpec& axisPhi, const AxisSpec& axisDcaXY, const AxisSpec& axisDcaZ, const AxisSpec& axisSign) +{ + histReg.add((basePath + "P").c_str(), "p;p (GeV/c);Counts", kTH1F, {axisP}); + histReg.add((basePath + "Pt").c_str(), "p_{T};p_{T} (GeV/c);Counts", kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "#eta;#eta;Counts", kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "#varphi;#varphi;Counts", kTH1F, {axisPhi}); + histReg.add((basePath + "DCAxy").c_str(), "DCA_{xy};DCA_{xy} (cm);Counts", kTH1F, {axisDcaXY}); + histReg.add((basePath + "DCAz").c_str(), "DCA_{z};DCA_{z} (cm);Counts", kTH1F, {axisDcaZ}); + histReg.add((basePath + "Sign").c_str(), "Sign;Sign;Counts", kTH1F, {axisSign}); + histReg.add((basePath + "DCAxyVsPt").c_str(), "DCA_{xy} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {axisPt, axisDcaXY}); + histReg.add((basePath + "DCAzVsPt").c_str(), "DCA_{z} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {axisPt, axisDcaZ}); +} + +template +void addIdentifiedQAHistos(H& histReg, const std::string& basePath, const AxisSpec& axisP, const AxisSpec& axisRapidity, const AxisSpec& axisTPCSignal, const AxisSpec& axisTOFBeta, const AxisSpec& axisTPCNSigma, const AxisSpec& axisTOFNSigma, const AxisSpec& axisIdMethod) +{ + histReg.add((basePath + "Rapidity").c_str(), "Rapidity;y;Counts", HistType::kTH1F, {axisRapidity}); + histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", HistType::kTH2F, {axisP, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", HistType::kTH2F, {axisP, axisTOFBeta}); + histReg.add((basePath + "TPCNSigmaVsP").c_str(), "TPC n#sigma vs p;p (GeV/c);n#sigma_{TPC}", HistType::kTH2F, {axisP, axisTPCNSigma}); + histReg.add((basePath + "TOFNSigmaVsP").c_str(), "TOF n#sigma vs p;p (GeV/c);n#sigma_{TOF}", HistType::kTH2F, {axisP, axisTOFNSigma}); + histReg.add((basePath + "TPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma;n#sigma_{TPC};n#sigma_{TOF}", HistType::kTH2F, {axisTPCNSigma, axisTOFNSigma}); + histReg.add((basePath + "IdMethodVsP").c_str(), "Identification method vs p;p (GeV/c);ID method", HistType::kTH2F, {axisP, axisIdMethod}); +} + +// Common Histograms booking functions are over +//__________________________________________________________________________________________________________________________ + +struct HParticleCorrelation1ResonanceProducer { + Produces resonanceCndts; + HistogramRegistry resonanceQAPlots{"resonanceQAPlots", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + static constexpr float ResonancePidNSigmaMax = 3.5f; + + struct : ConfigurableGroup { + Configurable c0001printDebugMessages{"c0001printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + Configurable c0002requireSel8{"c0002requireSel8", true, "Require sel8 event selection"}; + Configurable c0003cutZvertex{"c0003cutZvertex", 10.0f, "Maximum |z_{vtx}| (cm)"}; + } cfgEvent; + + struct : ConfigurableGroup { + Configurable c0004ptMin{"c0004ptMin", 0.15f, "Minimum track pT (GeV/c)"}; + Configurable c0005ptMax{"c0005ptMax", 100.0f, "Maximum track pT (GeV/c)"}; + Configurable c0006etaMax{"c0006etaMax", 0.8f, "Maximum |eta|"}; + + Configurable c0007useFixedDCAxy{"c0007useFixedDCAxy", false, "Apply fixed DCAxy cut"}; + Configurable c0008dcaXYMax{"c0008dcaXYMax", 0.1f, "Maximum |DCAxy| (cm)"}; + + Configurable c0009useFixedDCAz{"c0009useFixedDCAz", false, "Apply fixed DCAz cut"}; + Configurable c0010dcaZMax{"c0010dcaZMax", 0.2f, "Maximum |DCAz| (cm)"}; + } cfgTrackCuts; + + struct : ConfigurableGroup { + Configurable c0011doPhi1020{"c0011doPhi1020", true, "Build Phi(1020) candidates"}; + Configurable c0012doKStar892{"c0012doKStar892", true, "Build K*(892)0 candidates"}; + Configurable c0013doKStar892Bar{"c0013doKStar892Bar", true, "Build anti-K*(892)0 candidates"}; + Configurable c0014doLambda1520{"c0014doLambda1520", true, "Build Lambda(1520) candidates"}; + Configurable c0015doLambda1520Bar{"c0015doLambda1520Bar", true, "Build anti-Lambda(1520) candidates"}; + } cfgResonances; + + struct : ConfigurableGroup { + Configurable c0016phi1020PeakLow{"c0016phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c0017phi1020PeakUp{"c0017phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c0018phi1020LSBLow{"c0018phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c0019phi1020LSBUp{"c0019phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c0020phi1020RSBLow{"c0020phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c0021phi1020RSBUp{"c0021phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + } cfgPhi1020Mass; + + struct : ConfigurableGroup { + Configurable c0022kstar892PeakLow{"c0022kstar892PeakLow", 0.846f, "K*(892)0 peak lower mass"}; + Configurable c0023kstar892PeakUp{"c0023kstar892PeakUp", 0.946f, "K*(892)0 peak upper mass"}; + Configurable c0024kstar892LSBLow{"c0024kstar892LSBLow", 0.700f, "K*(892)0 LSB lower mass"}; + Configurable c0025kstar892LSBUp{"c0025kstar892LSBUp", 0.780f, "K*(892)0 LSB upper mass"}; + Configurable c0026kstar892RSBLow{"c0026kstar892RSBLow", 1.010f, "K*(892)0 RSB lower mass"}; + Configurable c0027kstar892RSBUp{"c0027kstar892RSBUp", 1.090f, "K*(892)0 RSB upper mass"}; + } cfgKstar892Mass; + + struct : ConfigurableGroup { + Configurable c0028lambda1520PeakLow{"c0028lambda1520PeakLow", 1.500f, "Lambda(1520) peak lower mass"}; + Configurable c0029lambda1520PeakUp{"c0029lambda1520PeakUp", 1.540f, "Lambda(1520) peak upper mass"}; + Configurable c0030lambda1520LSBLow{"c0030lambda1520LSBLow", 1.440f, "Lambda(1520) LSB lower mass"}; + Configurable c0031lambda1520LSBUp{"c0031lambda1520LSBUp", 1.480f, "Lambda(1520) LSB upper mass"}; + Configurable c0032lambda1520RSBLow{"c0032lambda1520RSBLow", 1.560f, "Lambda(1520) RSB lower mass"}; + Configurable c0033lambda1520RSBUp{"c0033lambda1520RSBUp", 1.600f, "Lambda(1520) RSB upper mass"}; + } cfgLambda1520Mass; + + void init(InitContext const&) + { + const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; + + resonanceQAPlots.add("mPhi1020", "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisPhi1020Mass}); + resonanceQAPlots.add("mKStar892", "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisKStar892Mass}); + resonanceQAPlots.add("mKStar892Bar", "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisKStar892Mass}); + resonanceQAPlots.add("mLambda1520", "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {axisLambda1520Mass}); + resonanceQAPlots.add("mLambda1520Bar", "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts", kTH1F, {axisLambda1520Mass}); + } + + template + bool selPion(const T& track) + { + if (std::abs(track.tpcNSigmaPi()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaPi()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + template + bool selKaon(const T& track) + { + if (std::abs(track.tpcNSigmaKa()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaKa()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + template + bool selProton(const T& track) + { + if (std::abs(track.tpcNSigmaPr()) < ResonancePidNSigmaMax) { + return true; + } + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaPr()) < ResonancePidNSigmaMax) { + return true; + } + } + return false; + } + + // Event Filter + Filter eventFilter = (!cfgEvent.c0002requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.c0003cutZvertex); + + // Track Filter + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.c0004ptMin) && (o2::aod::track::pt < cfgTrackCuts.c0005ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.c0006etaMax); + Filter dcaFilter = ((!cfgTrackCuts.c0007useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.c0008dcaXYMax)) && + ((!cfgTrackCuts.c0009useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.c0010dcaZMax)); + using MyFilteredCollisions = soa::Filtered>; + using MyFilteredTracks = soa::Filtered>; + + Preslice tracksPerCollisionPreslice = o2::aod::track::collisionId; + SliceCache cache; + Partition posTracks = aod::track::signed1Pt > 0.0f; + Partition negTracks = aod::track::signed1Pt < 0.0f; + + int dfNumber = 0; + // void processNothing(aod::HPCorrColls const&) + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.c0001printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. + // Keeps the task alive when running purely on derived data. + } + PROCESS_SWITCH(HParticleCorrelation1ResonanceProducer, processNothing, "Dummy process for derived-data analysis", true); + + void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, o2::aod::Origins const& /*Origins*/) + { + dfNumber++; + if (cfgDebug.c0001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = " << collisions.size() << " :: fullTracks = " << fullTracks.size(); + } + + float ePosPi = 0.0f; + float ePosKa = 0.0f; + float ePosPr = 0.0f; + + float eNegPi = 0.0f; + float eNegKa = 0.0f; + float eNegPr = 0.0f; + + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + float px = 0.0f; + float py = 0.0f; + float pz = 0.0f; + float p = 0.0f; + + float mPhi1020 = -1.0f; + float mKStar892 = -1.0f; + float mKStar892Bar = -1.0f; + float mLambda1520 = -1.0f; + float mLambda1520Bar = -1.0f; + + uint8_t phi1020Tag = kMassOutside; + uint8_t kStar892Tag = kMassOutside; + uint8_t kStar892BarTag = kMassOutside; + uint8_t lambda1520Tag = kMassOutside; + uint8_t lambda1520BarTag = kMassOutside; + + bool posIsPi = false, posIsKa = false, posIsPr = false, negIsPi = false, negIsKa = false, negIsPr = false; + bool fillTable = false; + + for (const auto& collision : collisions) { + auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + for (const auto& posTrack : posTracksPerColl) { + posIsPi = selPion(posTrack); + posIsKa = selKaon(posTrack); + posIsPr = selProton(posTrack); + if (!(posIsPi || posIsKa || posIsPr)) { + continue; + } + // Precompute energies for fast speed + ePosPi = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassPiPlus); + ePosKa = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassKPlus); + ePosPr = RecoDecay::e(posTrack.px(), posTrack.py(), posTrack.pz(), MassProton); + for (const auto& negTrack : negTracksPerColl) { + negIsPi = selPion(negTrack); + negIsKa = selKaon(negTrack); + negIsPr = selProton(negTrack); + if (!(negIsPi || negIsKa || negIsPr)) { + continue; + } + + eNegPi = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassPiPlus); + eNegKa = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassKPlus); + eNegPr = RecoDecay::e(negTrack.px(), negTrack.py(), negTrack.pz(), MassProton); + + px = posTrack.px() + negTrack.px(); + py = posTrack.py() + negTrack.py(); + pz = posTrack.pz() + negTrack.pz(); + p = RecoDecay::p(px, py, pz); // definition = std::sqrt(px*px+py*py+pz*pz); + + fillTable = false; + + mPhi1020 = -1.0f; + mKStar892 = -1.0f; + mKStar892Bar = -1.0f; + mLambda1520 = -1.0f; + mLambda1520Bar = -1.0f; + + phi1020Tag = kMassOutside; + kStar892Tag = kMassOutside; + kStar892BarTag = kMassOutside; + lambda1520Tag = kMassOutside; + lambda1520BarTag = kMassOutside; + + // phi(1020) -> K+ + K- + if (cfgResonances.c0011doPhi1020 && posIsKa && negIsKa) { + mPhi1020 = RecoDecay::m(p, ePosKa + eNegKa); + phi1020Tag = getMassRegionTag(mPhi1020, cfgPhi1020Mass.c0018phi1020LSBLow, cfgPhi1020Mass.c0019phi1020LSBUp, cfgPhi1020Mass.c0016phi1020PeakLow, cfgPhi1020Mass.c0017phi1020PeakUp, cfgPhi1020Mass.c0020phi1020RSBLow, cfgPhi1020Mass.c0021phi1020RSBUp); + if (phi1020Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mPhi1020"), mPhi1020); + } + + // K(892)* -> K+ + pi- + if (cfgResonances.c0012doKStar892 && posIsKa && negIsPi) { + mKStar892 = RecoDecay::m(p, ePosKa + eNegPi); + kStar892Tag = getMassRegionTag(mKStar892, cfgKstar892Mass.c0024kstar892LSBLow, cfgKstar892Mass.c0025kstar892LSBUp, cfgKstar892Mass.c0022kstar892PeakLow, cfgKstar892Mass.c0023kstar892PeakUp, cfgKstar892Mass.c0026kstar892RSBLow, cfgKstar892Mass.c0027kstar892RSBUp); + if (kStar892Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mKStar892"), mKStar892); + } + + // K(892)*Bar -> K- + pi+ + if (cfgResonances.c0013doKStar892Bar && posIsPi && negIsKa) { + mKStar892Bar = RecoDecay::m(p, ePosPi + eNegKa); + kStar892BarTag = getMassRegionTag(mKStar892Bar, cfgKstar892Mass.c0024kstar892LSBLow, cfgKstar892Mass.c0025kstar892LSBUp, cfgKstar892Mass.c0022kstar892PeakLow, cfgKstar892Mass.c0023kstar892PeakUp, cfgKstar892Mass.c0026kstar892RSBLow, cfgKstar892Mass.c0027kstar892RSBUp); + if (kStar892BarTag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mKStar892Bar"), mKStar892Bar); + } + + // Λ(1520) -> P+ + Ka- + if (cfgResonances.c0014doLambda1520 && posIsPr && negIsKa) { + mLambda1520 = RecoDecay::m(p, ePosPr + eNegKa); + lambda1520Tag = getMassRegionTag(mLambda1520, cfgLambda1520Mass.c0030lambda1520LSBLow, cfgLambda1520Mass.c0031lambda1520LSBUp, cfgLambda1520Mass.c0028lambda1520PeakLow, cfgLambda1520Mass.c0029lambda1520PeakUp, cfgLambda1520Mass.c0032lambda1520RSBLow, cfgLambda1520Mass.c0033lambda1520RSBUp); + if (lambda1520Tag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mLambda1520"), mLambda1520); + } + + // Λ(1520)Bar -> PBar + Ka+ + if (cfgResonances.c0015doLambda1520Bar && posIsKa && negIsPr) { + mLambda1520Bar = RecoDecay::m(p, ePosKa + eNegPr); + lambda1520BarTag = getMassRegionTag(mLambda1520Bar, cfgLambda1520Mass.c0030lambda1520LSBLow, cfgLambda1520Mass.c0031lambda1520LSBUp, cfgLambda1520Mass.c0028lambda1520PeakLow, cfgLambda1520Mass.c0029lambda1520PeakUp, cfgLambda1520Mass.c0032lambda1520RSBLow, cfgLambda1520Mass.c0033lambda1520RSBUp); + if (lambda1520BarTag != kMassOutside) { + fillTable = true; + } + resonanceQAPlots.fill(HIST("mLambda1520Bar"), mLambda1520Bar); + } + + if (!fillTable) { + continue; + } + pt = RecoDecay::pt(px, py); // definition = std::sqrt(px*px+py*py); + eta = RecoDecay::eta(std::array{px, py, pz}); // definition = 0.5 * std::log((p + pz) / (p - pz + 1e-12)); // 1e-12 protect divide-by-zero + phi = RecoDecay::phi(px, py); // definition = std::atan2(py, px); + + resonanceCndts(collision.globalIndex(), posTrack.globalIndex(), negTrack.globalIndex(), + pt, eta, phi, px, py, pz, + mPhi1020, mKStar892, mKStar892Bar, mLambda1520, mLambda1520Bar, + phi1020Tag, kStar892Tag, kStar892BarTag, lambda1520Tag, lambda1520BarTag); + } // Neg Tracks + } // Pos Tracks + } // collision Loop is over + } + PROCESS_SWITCH(HParticleCorrelation1ResonanceProducer, processSameEvent, "Process Same event", true); +}; + +struct HParticleCorrelation2SameEvent { + + Produces derivedCollisions; + Produces derivedTriggers; + Produces derivedAssocs; + + // Hisogram redistry: + HistogramRegistry seEventQA{"seEventQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry seTrackQA{"seTrackQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hhCorrelation{"hhCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hIdCorrelation{"hIdCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hPhiCorrelation{"hPhiCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarCorrelation{"hKStarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarBarCorrelation{"hKStarBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaCorrelation{"hLambdaCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaBarCorrelation{"hLambdaBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry resonanceMassSpectra{"resonanceMassSpectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixingQA{"mixingQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + struct : ConfigurableGroup { + Configurable c1001printDebugMessages{"c1001printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + // Basic event selection + Configurable c1002cutZvertex{"c1002cutZvertex", 10.f, "Maximum |z_{vtx}| (cm)"}; // Imp : All + Configurable c1003requireSel8{"c1003requireSel8", true, "Require sel8 event selection"}; // Imp : All + Configurable c1004requireTriggerTVX{"c1004requireTriggerTVX", false, "Require kIsTriggerTVX"}; + + Configurable c1005minNFilteredTracks{"c1005minNFilteredTracks", 3, "Minimum number of filtered tracks required per collision"}; + Configurable c1006minNSelectedTracks{"c1006minNSelectedTracks", 3, "Minimum number of selected tracks required per collision"}; + + // Time-frame / ROF border selections + Configurable c1007requireNoITSROFrameBorder{"c1007requireNoITSROFrameBorder", false, "Require kNoITSROFrameBorder"}; + Configurable c1008requireNoTimeFrameBorder{"c1008requireNoTimeFrameBorder", false, "Require kNoTimeFrameBorder"}; + + // Vertex quality + Configurable c1009requireVertexITSTPC{"c1009requireVertexITSTPC", true, "Require kIsVertexITSTPC"}; // Imp : Light Ions + pp + Configurable c1010requireGoodZvtxFT0vsPV{"c1010requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV"}; // Imp : Light Ions + Configurable c1011requireVertexTOFmatched{"c1011requireVertexTOFmatched", false, "Require kIsVertexTOFmatched"}; + Configurable c1012requireVertexTRDmatched{"c1012requireVertexTRDmatched", false, "Require kIsVertexTRDmatched"}; + Configurable c1013requireGoodITSLayersAll{"c1013requireGoodITSLayersAll", false, "Require kIsGoodITSLayersAll"}; // Imp : Light Ions + + // Pileup / neighbouring-collision rejection + Configurable c1014requireNoSameBunchPileup{"c1014requireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; // Imp : Light Ions + pp + Configurable c1015requireNoCollInTimeRangeStandard{"c1015requireNoCollInTimeRangeStandard", false, "Require kNoCollInTimeRangeStandard"}; + Configurable c1016requireNoCollInTimeRangeStrict{"c1016requireNoCollInTimeRangeStrict", false, "Require kNoCollInTimeRangeStrict"}; + Configurable c1017requireNoCollInTimeRangeNarrow{"c1017requireNoCollInTimeRangeNarrow", false, "Require kNoCollInTimeRangeNarrow"}; + Configurable c1018requireNoCollInRofStandard{"c1018requireNoCollInRofStandard", false, "Require kNoCollInRofStandard"}; + Configurable c1019requireNoCollInRofStrict{"c1019requireNoCollInRofStrict", false, "Require kNoCollInRofStrict"}; + Configurable c1020requireNoHighMultCollInPrevRof{"c1020requireNoHighMultCollInPrevRof", false, "Require kNoHighMultCollInPrevRof"}; + + // INEL event classes + Configurable c1021requireINELgt0{"c1021requireINELgt0", false, "Require INEL > 0"}; + Configurable c1022requireINELgt1{"c1022requireINELgt1", false, "Require INEL > 1"}; + + // Multiplicity selection + Configurable c1023useMultiplicitySelection{"c1023useMultiplicitySelection", false, "Apply multiplicity selection"}; + Configurable c1024multiplicityEstimator{"c1024multiplicityEstimator", 0, "0: multNTracksPV, 1: numContrib, 2: multFT0C, 3: multFT0M"}; + Configurable c1025minMultiplicity{"c1025minMultiplicity", 0.f, "Minimum multiplicity"}; + Configurable c1026maxMultiplicity{"c1026maxMultiplicity", 1.e9f, "Maximum multiplicity"}; + + // Centrality selection + Configurable c1027useCentralitySelection{"c1027useCentralitySelection", false, "Apply centrality selection"}; + Configurable c1028centralityEstimator{"c1028centralityEstimator", 0, "0: centFT0C, 1: centFT0M, 2: centFT0A, 3: centFV0A"}; + Configurable c1029minCentrality{"c1029minCentrality", 0.f, "Minimum centrality percentile"}; + Configurable c1030maxCentrality{"c1030maxCentrality", 100.f, "Maximum centrality percentile"}; + + // Occupancy selection + Configurable c1031useOccupancySelection{"c1031useOccupancySelection", false, "Apply event occupancy selection"}; + Configurable c1032useFT0CbasedOccupancy{"c1032useFT0CbasedOccupancy", false, "Use FT0C occupancy instead of track occupancy"}; + Configurable c1033minOccupancy{"c1033minOccupancy", -1.f, "Minimum occupancy"}; + Configurable c1034maxOccupancy{"c1034maxOccupancy", 1.e9f, "Maximum occupancy"}; + + // Interaction-rate selection + Configurable c1035useInteractionRateSelection{"c1035useInteractionRateSelection", false, "Apply interaction-rate selection"}; + Configurable c1036minInteractionRate{"c1036minInteractionRate", -1.f, "Minimum interaction rate"}; + Configurable c1037maxInteractionRate{"c1037maxInteractionRate", 1.e9f, "Maximum interaction rate"}; + + // RCT / detector-quality selection + Configurable c1038requireRCTFlagChecker{"c1038requireRCTFlagChecker", false, "Apply Run Condition Table event-quality selection"}; + Configurable c1039requireCorrelationAnalysisRCTFlagChecker{"c1039requireCorrelationAnalysisRCTFlagChecker", false, "Apply correlation-analysis RCT selection"}; + Configurable c1040rctFlagCheckerLabel{"c1040rctFlagCheckerLabel", "CBT_muon_global", "RCT flag checker label"}; + Configurable c1041rctCheckZDC{"c1041rctCheckZDC", false, "Include ZDC in RCT detector-quality check"}; + Configurable c1042rctTreatLimitedAcceptanceAsBad{"c1042rctTreatLimitedAcceptanceAsBad", false, "Treat limited detector acceptance as bad"}; + } cfgEvent; + + struct : ConfigurableGroup { + // Kinematics + Configurable c1043ptMin{"c1043ptMin", 0.2f, "Minimum track pT (GeV/c)"}; + Configurable c1044ptMax{"c1044ptMax", 1.e10f, "Maximum track pT (GeV/c)"}; + Configurable c1045etaMax{"c1045etaMax", 0.8f, "Maximum |eta|"}; + + // Standard O2 track-selection flags + Configurable c1046requireGlobalTrack{"c1046requireGlobalTrack", false, "Require isGlobalTrack()"}; + Configurable c1047requireGlobalTrackWoDCA{"c1047requireGlobalTrackWoDCA", false, "Require isGlobalTrackWoDCA()"}; + Configurable c1048requirePVContributor{"c1048requirePVContributor", false, "Require track to be a PV contributor"}; + + // Detector matching / presence + Configurable c1049requireITS{"c1049requireITS", false, "Require ITS information"}; + Configurable c1050requireTPC{"c1050requireTPC", false, "Require TPC information"}; + Configurable c1051requireTOF{"c1051requireTOF", false, "Require TOF information"}; + Configurable c1052requireTRD{"c1052requireTRD", false, "Require TRD information"}; + + // TPC quality + Configurable c1053tpcNClsFoundMin{"c1053tpcNClsFoundMin", 0, "Minimum number of found TPC clusters"}; + Configurable c1054tpcNClsCrossedRowsMin{"c1054tpcNClsCrossedRowsMin", 80, "Minimum number of TPC crossed rows"}; + Configurable c1055tpcCrossedRowsOverFindableMin{"c1055tpcCrossedRowsOverFindableMin", 0.f, "Minimum TPC crossed rows / findable clusters"}; + Configurable c1056tpcFoundOverFindableMin{"c1056tpcFoundOverFindableMin", 0.f, "Minimum TPC found / findable clusters"}; + Configurable c1057tpcFractionSharedMax{"c1057tpcFractionSharedMax", 1.f, "Maximum fraction of shared TPC clusters"}; + Configurable c1058tpcChi2NClMin{"c1058tpcChi2NClMin", 0.f, "Minimum TPC chi2 per cluster"}; + Configurable c1059tpcChi2NClMax{"c1059tpcChi2NClMax", 1.e10f, "Maximum TPC chi2 per cluster"}; + + // ITS quality + Configurable c1060itsNClsMin{"c1060itsNClsMin", 0, "Minimum number of ITS clusters"}; + Configurable c1061itsNClsMax{"c1061itsNClsMax", 7, "Maximum number of ITS clusters"}; + Configurable c1062itsNClsInnerBarrelMin{"c1062itsNClsInnerBarrelMin", 0, "Minimum number of ITS inner-barrel clusters"}; + Configurable c1063itsChi2NClMin{"c1063itsChi2NClMin", 0.f, "Minimum ITS chi2 per cluster"}; + Configurable c1064itsChi2NClMax{"c1064itsChi2NClMax", 1.e10f, "Maximum ITS chi2 per cluster"}; + + // Fixed DCA + Configurable c1065useFixedDCAxy{"c1065useFixedDCAxy", true, "Apply fixed |DCAxy| cut"}; + Configurable c1066dcaXYMax{"c1066dcaXYMax", 0.1f, "Maximum fixed |DCAxy| (cm)"}; + Configurable c1067useFixedDCAz{"c1067useFixedDCAz", true, "Apply fixed |DCAz| cut"}; + Configurable c1068dcaZMax{"c1068dcaZMax", 0.2f, "Maximum fixed |DCAz| (cm)"}; + + // pT-dependent DCAxy: + // |DCAxy| < A + B * pT^C + Configurable c1069usePtDependentDCAxy{"c1069usePtDependentDCAxy", false, "Apply pT-dependent DCAxy cut"}; + Configurable c1070dcaXYPtA{"c1070dcaXYPtA", 0.0105f, "A coefficient of pT-dependent DCAxy cut"}; + Configurable c1071dcaXYPtB{"c1071dcaXYPtB", 0.035f, "B coefficient of pT-dependent DCAxy cut"}; + Configurable c1072dcaXYPtC{"c1072dcaXYPtC", 1.1f, "Power C of pT-dependent DCAxy cut"}; + + // pT-dependent DCAz: + // |DCAz| < A + B * pT^C + Configurable c1073usePtDependentDCAz{"c1073usePtDependentDCAz", false, "Apply pT-dependent DCAz cut"}; + Configurable c1074dcaZPtA{"c1074dcaZPtA", 0.1f, "A coefficient of pT-dependent DCAz cut"}; + Configurable c1075dcaZPtB{"c1075dcaZPtB", 0.0f, "B coefficient of pT-dependent DCAz cut"}; + Configurable c1076dcaZPtC{"c1076dcaZPtC", 0.0f, "Power C of pT-dependent DCAz cut"}; + } cfgTrackCuts; + + struct : ConfigurableGroup { + Configurable c1077triggerPtLow{"c1077triggerPtLow", 4.0f, "Minimum pT for trigger tracks"}; + Configurable c1078triggerPtHigh{"c1078triggerPtHigh", 8.0f, "Maximum pT for trigger tracks"}; + Configurable c1079assocPtLowMin{"c1079assocPtLowMin", 0.0f, "Minimum pT for low-pT associated tracks"}; + Configurable c1080assocPtLowMax{"c1080assocPtLowMax", 2.0f, "Maximum pT for low-pT associated tracks"}; + Configurable c1081assocPtHighMin{"c1081assocPtHighMin", 2.0f, "Minimum pT for high-pT associated tracks"}; + Configurable c1082assocPtHighMax{"c1082assocPtHighMax", 4.0f, "Maximum pT for high-pT associated tracks"}; + } cfgPartitions; + + struct : ConfigurableGroup { + Configurable c1083resoRapidityMin{"c1083resoRapidityMin", -0.5f, "Minimum rapidity for resonance candidates"}; + Configurable c1084resoRapidityMax{"c1084resoRapidityMax", 0.5f, "Maximum rapidity for resonance candidates"}; + + Configurable c1085phiPtLowMin{"c1085phiPtLowMin", 0.0f, "Minimum pT for low-pT Phi(1020)"}; + Configurable c1086phiPtLowMax{"c1086phiPtLowMax", 2.0f, "Maximum pT for low-pT Phi(1020)"}; + Configurable c1087phiPtHighMin{"c1087phiPtHighMin", 2.0f, "Minimum pT for high-pT Phi(1020)"}; + Configurable c1088phiPtHighMax{"c1088phiPtHighMax", 4.0f, "Maximum pT for high-pT Phi(1020)"}; + + Configurable c1089kstarPtLowMin{"c1089kstarPtLowMin", 0.0f, "Minimum pT for low-pT K*(892)0"}; + Configurable c1090kstarPtLowMax{"c1090kstarPtLowMax", 2.0f, "Maximum pT for low-pT K*(892)0"}; + Configurable c1091kstarPtHighMin{"c1091kstarPtHighMin", 2.0f, "Minimum pT for high-pT K*(892)0"}; + Configurable c1092kstarPtHighMax{"c1092kstarPtHighMax", 4.0f, "Maximum pT for high-pT K*(892)0"}; + + Configurable c1093kstarBarPtLowMin{"c1093kstarBarPtLowMin", 0.0f, "Minimum pT for low-pT anti-K*(892)0"}; + Configurable c1094kstarBarPtLowMax{"c1094kstarBarPtLowMax", 2.0f, "Maximum pT for low-pT anti-K*(892)0"}; + Configurable c1095kstarBarPtHighMin{"c1095kstarBarPtHighMin", 2.0f, "Minimum pT for high-pT anti-K*(892)0"}; + Configurable c1096kstarBarPtHighMax{"c1096kstarBarPtHighMax", 4.0f, "Maximum pT for high-pT anti-K*(892)0"}; + + Configurable c1097lambda1520PtLowMin{"c1097lambda1520PtLowMin", 0.0f, "Minimum pT for low-pT Lambda(1520)"}; + Configurable c1098lambda1520PtLowMax{"c1098lambda1520PtLowMax", 2.0f, "Maximum pT for low-pT Lambda(1520)"}; + Configurable c1099lambda1520PtHighMin{"c1099lambda1520PtHighMin", 2.0f, "Minimum pT for high-pT Lambda(1520)"}; + Configurable c1100lambda1520PtHighMax{"c1100lambda1520PtHighMax", 4.0f, "Maximum pT for high-pT Lambda(1520)"}; + + Configurable c1101lambda1520BarPtLowMin{"c1101lambda1520BarPtLowMin", 0.0f, "Minimum pT for low-pT anti-Lambda(1520)"}; + Configurable c1102lambda1520BarPtLowMax{"c1102lambda1520BarPtLowMax", 2.0f, "Maximum pT for low-pT anti-Lambda(1520)"}; + Configurable c1103lambda1520BarPtHighMin{"c1103lambda1520BarPtHighMin", 2.0f, "Minimum pT for high-pT anti-Lambda(1520)"}; + Configurable c1104lambda1520BarPtHighMax{"c1104lambda1520BarPtHighMax", 4.0f, "Maximum pT for high-pT anti-Lambda(1520)"}; + } cfgResPartitions; + + struct : ConfigurableGroup { + ConfigurableAxis c1105axisDeltaPhi{"c1105axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c1106axisDeltaEta{"c1106axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + + ConfigurableAxis c1107axisCorrSparseTriggerPt{"c1107axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis c1108axisCorrSparseAssocPt{"c1108axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis c1109axisCorrSparseDeltaPhi{"c1109axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c1110axisCorrSparseDeltaEta{"c1110axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c1111axisCorrCentrality{"c1111axisCorrCentrality", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "Centrality (%) axis of the correlation histograms (estimator chosen by c1138mixingEstimator); keep equal to c2008"}; + } cfgAxis; + + Configurable c1112fillDauQAOnce{"c1112fillDauQAOnce", false, "Fill each resonance daughter in QA only once per event"}; + Configurable c1113rejectResoWithAnyTriggerDaughter{"c1113rejectResoWithAnyTriggerDaughter", false, "Reject resonance if either daughter belongs to the event trigger population"}; + Configurable c1114requireSelectedTriggerForInvariantMass{"c1114requireSelectedTriggerForInvariantMass", true, "Require at least one selected trigger track before filling resonance invariant-mass spectra"}; + + struct : ConfigurableGroup { + Configurable c1115phi1020PeakLow{"c1115phi1020PeakLow", 1.013f, "Phi(1020) peak lower mass"}; + Configurable c1116phi1020PeakUp{"c1116phi1020PeakUp", 1.026f, "Phi(1020) peak upper mass"}; + Configurable c1117phi1020LSBLow{"c1117phi1020LSBLow", 0.995f, "Phi(1020) LSB lower mass"}; + Configurable c1118phi1020LSBUp{"c1118phi1020LSBUp", 1.005f, "Phi(1020) LSB upper mass"}; + Configurable c1119phi1020RSBLow{"c1119phi1020RSBLow", 1.040f, "Phi(1020) RSB lower mass"}; + Configurable c1120phi1020RSBUp{"c1120phi1020RSBUp", 1.060f, "Phi(1020) RSB upper mass"}; + } cfgPhi1020CorrMass; + + struct : ConfigurableGroup { + Configurable c1121kStar892PeakLow{"c1121kStar892PeakLow", 0.846f, "K*(892)0 correlation peak lower mass"}; + Configurable c1122kStar892PeakUp{"c1122kStar892PeakUp", 0.946f, "K*(892)0 correlation peak upper mass"}; + Configurable c1123kStar892LSBLow{"c1123kStar892LSBLow", 0.700f, "K*(892)0 correlation LSB lower mass"}; + Configurable c1124kStar892LSBUp{"c1124kStar892LSBUp", 0.780f, "K*(892)0 correlation LSB upper mass"}; + Configurable c1125kStar892RSBLow{"c1125kStar892RSBLow", 1.010f, "K*(892)0 correlation RSB lower mass"}; + Configurable c1126kStar892RSBUp{"c1126kStar892RSBUp", 1.090f, "K*(892)0 correlation RSB upper mass"}; + } cfgKStar892CorrMass; + + struct : ConfigurableGroup { + Configurable c1127lambda1520PeakLow{"c1127lambda1520PeakLow", 1.500f, "Lambda(1520) correlation peak lower mass"}; + Configurable c1128lambda1520PeakUp{"c1128lambda1520PeakUp", 1.540f, "Lambda(1520) correlation peak upper mass"}; + Configurable c1129lambda1520LSBLow{"c1129lambda1520LSBLow", 1.440f, "Lambda(1520) correlation LSB lower mass"}; + Configurable c1130lambda1520LSBUp{"c1130lambda1520LSBUp", 1.480f, "Lambda(1520) correlation LSB upper mass"}; + Configurable c1131lambda1520RSBLow{"c1131lambda1520RSBLow", 1.560f, "Lambda(1520) correlation RSB lower mass"}; + Configurable c1132lambda1520RSBUp{"c1132lambda1520RSBUp", 1.600f, "Lambda(1520) correlation RSB upper mass"}; + } cfgLambda1520CorrMass; + + struct : ConfigurableGroup { + Configurable c1133pairMask{"c1133pairMask", 0ULL, "Correlation mask to store; 0 = any non-zero correlation"}; + Configurable c1134mixingBin{"c1134mixingBin", -1, "Mixing bin to store; -1 = all valid mixing bins"}; + Configurable c1135requireAllPairBits{"c1135requireAllPairBits", false, "Require all requested pair-mask bits instead of any requested bit"}; + Configurable c1136resetGlobalCountersPerDF{"c1136resetGlobalCountersPerDF", false, "Reset derived-data global counters at the beginning of every dataframe"}; + } cfgDerivedData; + + struct : ConfigurableGroup { + Configurable c1137nEvtMixing{"c1137nEvtMixing", 5, "Number of events to mix"}; + Configurable c1138mixingEstimator{"c1138mixingEstimator", kMixCentFT0C, "Mixing estimator: 0=FT0C, 1=FT0M, 2=FT0A, 3=FV0A"}; + ConfigurableAxis c1139axisVtxMixing{"c1139axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis c1140axisCentMixing{"c1140axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality"}; + ConfigurableAxis c1141axisMixingOccupancy{"c1141axisMixingOccupancy", {101, -0.5, 100.5}, "Number of collisions in mixing pool"}; + } cfgMixing; + + using BinningTypeVtxZFT0C = ColumnBinningPolicy; + using BinningTypeVtxZFT0M = ColumnBinningPolicy; + using BinningTypeVtxZFT0A = ColumnBinningPolicy; + using BinningTypeVtxZFV0A = ColumnBinningPolicy; + + BinningTypeVtxZFT0C colBinningFT0C{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFT0M colBinningFT0M{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFT0A colBinningFT0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + BinningTypeVtxZFV0A colBinningFV0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + + int nMixBins = 0; + + template + int getMixingBin(const C& collision) + { + switch (cfgMixing.c1138mixingEstimator) { + case kMixCentFT0C: + return colBinningFT0C.getBin({collision.posZ(), collision.centFT0C()}); + case kMixCentFT0M: + return colBinningFT0M.getBin({collision.posZ(), collision.centFT0M()}); + case kMixCentFT0A: + return colBinningFT0A.getBin({collision.posZ(), collision.centFT0A()}); + case kMixCentFV0A: + return colBinningFV0A.getBin({collision.posZ(), collision.centFV0A()}); + default: + return -1; + } + } + + template + float getMixingEstimatorValue(const C& collision) + { + switch (cfgMixing.c1138mixingEstimator) { + case kMixCentFT0C: + return collision.centFT0C(); + case kMixCentFT0M: + return collision.centFT0M(); + case kMixCentFT0A: + return collision.centFT0A(); + case kMixCentFV0A: + return collision.centFV0A(); + default: + return -1.0f; + } + } + + struct : ConfigurableGroup { + Configurable> c1142pidConfigSetting{"c1142pidConfigSetting", {DefaultPIDcheckValues[0].data(), kNPid, kNCutSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"ThrPforTOF", "IdCutTypeLowP", "NSigmaTPCLowP", "NSigmaTOFLowP", "NSigmaRadLowP", "IdCutTypeHighP", "NSigmaTPCHighP", "NSigmaTOFHighP", "NSigmaRadHighP", "doVetoOthers", "doRelativeTPCCheck", "doRelativeTOFcheck", "doRelativeTPCTOFcheck"}}, "Cut values for particle identification"}; + Configurable> c1143pidVetoSetting{"c1143pidVetoSetting", {DefaultPidVetoValues[0].data(), kNPid, kNVetoSettings, {"Pi", "Ka", "Pr", "El", "Mu", "De"}, {"doVetoTPC", "doVetoTOF", "vetoTPC", "vetoTOF"}}, "Veto cuts for particle identification"}; + Configurable c1144cfgId07CheckTofBeta{"c1144cfgId07CheckTofBeta", false, "Require beta > 0 for reliable TOF"}; + } cfgIdCut; + + void init(InitContext const&) + { + if (cfgDebug.c1001printDebugMessages) { + LOGF(info, "Starting init"); + } + + if ((static_cast(cfgDerivedData.c1133pairMask) & ~(0xFFULL | MaskResoRegions)) != 0ULL) { + LOG(fatal) << "c1133pairMask = " << static_cast(cfgDerivedData.c1133pairMask) << " contains bits that are not mixing pools; allowed are bits 0-7 (h-h, h-pi, h-K, h-p) and 18-47 (resonance Peak/LSB/RSB)"; + } + // Axes + AxisSpec axisVertexZ = {30, -15., 15., "vrtx_{Z} [cm]"}; + AxisSpec axisCentFT0C = {1200, -10.0, 110.0, "centFT0C(percentile)"}; + AxisSpec axisMult = {150, -1.0, 149.0}; + + const AxisSpec axisEventVertexZ = {60, -15.0f, 15.0f, "z_{vtx} (cm)"}; + const AxisSpec axisEventCentrality = {102, -1.0f, 101.0f, "Centrality (%)"}; + const AxisSpec axisMixingBinStatus = {2, -0.5f, 1.5f, "Mixing-bin status"}; + + const AxisSpec axisP = {200, 0.0f, 10.0f, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPt = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisTPCInnerParam = {200, 0.0f, 10.0f, "#it{p}_{tpcInnerParam} (GeV/#it{c})"}; + const AxisSpec axisTOFExpMom = {200, 0.0f, 10.0f, "#it{p}_{tofExpMom} (GeV/#it{c})"}; + + const AxisSpec axisEta = {100, -5.0f, 5.0f, "#eta"}; + const AxisSpec axisPhi = {110, -1.0f, 10.0f, "#phi (radians)"}; + const AxisSpec axisRapidity = {200, -5.0f, 5.0f, "Rapidity (y)"}; + const AxisSpec axisDcaXY = {100, -5.0f, 5.0f, "dcaXY"}; + const AxisSpec axisDcaZ = {100, -5.0f, 5.0f, "dcaZ"}; + const AxisSpec axisDcaXYwide = {2000, -100.0f, 100.0f, "dcaXY"}; + const AxisSpec axisDcaZwide = {2000, -100.0f, 100.0f, "dcaZ"}; + const AxisSpec axisSign = {10, -5.0f, 5.0f, "track.sign"}; + + const AxisSpec axisTPCSignal = {100, -1.0f, 1000.0f, "tpcSignal"}; + const AxisSpec axisTOFBeta = {40, -2.0f, 2.0f, "tofBeta"}; + + const AxisSpec axisTPCSignalFine = {10010, -1.0f, 1000.0f, "tpcSignal"}; + const AxisSpec axisTOFBetaFine = {400, -2.0f, 2.0f, "tofBeta"}; + + const AxisSpec axisTPCNSigma = {200, -10.0f, 10.0f, "n#sigma_{TPC}"}; + const AxisSpec axisTOFNSigma = {200, -10.0f, 10.0f, "n#sigma_{TOF}"}; + const AxisSpec axisIdMethod = {2, -0.5f, 1.5f, "ID method (0=TPC, 1=TPC+TOF)"}; + + AxisSpec axisDeltaPhiSpec{cfgAxis.c1105axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.c1106axisDeltaEta, "#Delta#eta"}; + + AxisSpec axisCorrSparseTriggerPt{cfgAxis.c1107axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.c1108axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c1109axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.c1110axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrCentrality{cfgAxis.c1111axisCorrCentrality, "Centrality (%)"}; + + const AxisSpec axisPhi1020Mass = {300, 0.98f, 1.08f, "m_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisKStar892Mass = {300, 0.65f, 1.15f, "m_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisLambda1520Mass = {300, 1.40f, 1.65f, "m_{pK} (GeV/#it{c}^{2})"}; + const AxisSpec axisResoMassCent = {102, -1.0f, 101.0f, "Centrality (%)"}; + + AxisSpec axisVtxMixSpec{cfgMixing.c1139axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMixSpec{cfgMixing.c1140axisCentMixing, "Centrality (%)"}; + AxisSpec axisPoolOccupancy{cfgMixing.c1141axisMixingOccupancy, "N eligible collisions"}; + nMixBins = axisVtxMixSpec.getNbins() * axisCentMixSpec.getNbins(); + + // Re-build the binnings now that the configurables are set (as data members they hold the default axes) + colBinningFT0C = BinningTypeVtxZFT0C{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFT0M = BinningTypeVtxZFT0M{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFT0A = BinningTypeVtxZFT0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + colBinningFV0A = BinningTypeVtxZFV0A{{cfgMixing.c1139axisVtxMixing, cfgMixing.c1140axisCentMixing}, true}; + + const AxisSpec axisMixBin{nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; + const AxisSpec axisCorrChannel{NCorrCountChannels, -0.5, static_cast(NCorrCountChannels) - 0.5, "Correlation channel"}; + const AxisSpec axisReadyBins{nMixBins + 1, -0.5, static_cast(nMixBins) + 0.5, "N ready mixing bins"}; + + const int nEventQABins = static_cast(kNEventQABins); + const int nTrackQABins = static_cast(kNTrackQABins); + const int nDataFrameQABins = static_cast(kNDataFrameQABins); + const int nCollisionRejectionBins = static_cast(kNCollisionRejectionTags); + const int nTrackRejectionBins = static_cast(kNTrackRejectionTags); + + auto addEventQAHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "VertexZ").c_str(), "Collision vertex;z_{vtx} (cm);Counts", HistType::kTH1F, {axisEventVertexZ}); + + histReg.add((basePath + "CentFT0C").c_str(), "FT0C centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFT0M").c_str(), "FT0M centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFT0A").c_str(), "FT0A centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "CentFV0A").c_str(), "FV0A centrality;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + + histReg.add((basePath + "VtxZVsCentFT0C").c_str(), "Vertex z vs FT0C centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFT0M").c_str(), "Vertex z vs FT0M centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFT0A").c_str(), "Vertex z vs FT0A centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + histReg.add((basePath + "VtxZVsCentFV0A").c_str(), "Vertex z vs FV0A centrality;z_{vtx} (cm);Centrality (%)", HistType::kTH2F, {axisEventVertexZ, axisEventCentrality}); + + histReg.add((basePath + "CentFT0CVsCentFT0M").c_str(), "FT0C vs FT0M centrality;FT0C (%);FT0M (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0CVsCentFT0A").c_str(), "FT0C vs FT0A centrality;FT0C (%);FT0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0MVsCentFT0A").c_str(), "FT0M vs FT0A centrality;FT0M (%);FT0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + + histReg.add((basePath + "CentFT0CVsCentFV0A").c_str(), "FT0C vs FV0A centrality;FT0C (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0MVsCentFV0A").c_str(), "FT0M vs FV0A centrality;FT0M (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + histReg.add((basePath + "CentFT0AVsCentFV0A").c_str(), "FT0A vs FV0A centrality;FT0A (%);FV0A (%)", HistType::kTH2F, {axisEventCentrality, axisEventCentrality}); + + histReg.add((basePath + "MixingEstimator").c_str(), "Centrality estimator used for mixing;Centrality (%);Counts", HistType::kTH1F, {axisEventCentrality}); + histReg.add((basePath + "VtxZVsMixingEstimator").c_str(), "Physical mixing-bin map;z_{vtx} (cm);Mixing centrality (%)", HistType::kTH2F, {axisVtxMixSpec, axisCentMixSpec}); + histReg.add((basePath + "MixingBin").c_str(), "Mixing-bin index;Mixing bin;Counts", HistType::kTH1F, {axisMixBin}); + histReg.add((basePath + "MixingBinStatus").c_str(), "Mixing-bin validity;Status;Counts", HistType::kTH1F, {axisMixingBinStatus}); + }; + + addEventQAHistos(seEventQA, "SE/Events/BeforeSelection/"); + seEventQA.addClone("SE/Events/BeforeSelection/", "SE/Events/AfterSelFunction/"); + seEventQA.addClone("SE/Events/BeforeSelection/", "SE/Events/AfterSelection/"); + + auto hMixStatusBefore = seEventQA.get(HIST("SE/Events/BeforeSelection/MixingBinStatus")); + hMixStatusBefore->GetXaxis()->SetBinLabel(1, "Invalid"); + hMixStatusBefore->GetXaxis()->SetBinLabel(2, "Valid"); + + auto hMixStatusAfter = seEventQA.get(HIST("SE/Events/AfterSelection/MixingBinStatus")); + hMixStatusAfter->GetXaxis()->SetBinLabel(1, "Invalid"); + hMixStatusAfter->GetXaxis()->SetBinLabel(2, "Valid"); + + seEventQA.add("SE/Events/EventSelection", "Event selection QA", HistType::kTH1F, {{nEventQABins, -0.5, static_cast(nEventQABins) - 0.5}}); + seTrackQA.add("SE/Tracks/TrackSelection", "Track selection QA", HistType::kTH1F, {{nTrackQABins, -0.5, static_cast(nTrackQABins) - 0.5}}); + seEventQA.add("SE/Events/DataFrameQA", "Dataframe QA", HistType::kTH1F, {{nDataFrameQABins, -0.5, static_cast(nDataFrameQABins) - 0.5}}); + seEventQA.add("SE/Events/CollisionRejection", "Collision rejection reason", HistType::kTH1F, {{nCollisionRejectionBins, -0.5, static_cast(nCollisionRejectionBins) - 0.5}}); + seTrackQA.add("SE/Tracks/TrackRejection", "Track rejection reason", HistType::kTH1F, {{nTrackRejectionBins, -0.5, static_cast(nTrackRejectionBins) - 0.5}}); + + auto hEventQA = seEventQA.get(HIST("SE/Events/EventSelection")); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventAllFiltered) + 1, "Filtered collision"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedSelCollision) + 1, "Passed selCollision"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedMinFilteredTracks) + 1, "Passed min filtered tracks"); + hEventQA->GetXaxis()->SetBinLabel(static_cast(kEventPassedMinSelectedTracks) + 1, "Passed min selected tracks"); + + auto hTrackQA = seTrackQA.get(HIST("SE/Tracks/TrackSelection")); + hTrackQA->GetXaxis()->SetBinLabel(static_cast(kTrackAllFiltered) + 1, "Filtered track"); + hTrackQA->GetXaxis()->SetBinLabel(static_cast(kTrackPassedSelectionTrack) + 1, "Passed selectionTrack"); + + auto hDataFrameQA = seEventQA.get(HIST("SE/Events/DataFrameQA")); + hDataFrameQA->GetXaxis()->SetBinLabel(static_cast(kDFWithZeroFilteredColls) + 1, "Zero filtered collisions"); + + auto hCollRej = seEventQA.get(HIST("SE/Events/CollisionRejection")); + hCollRej->GetXaxis()->SetBinLabel(kCollAccepted + 1, "Accepted"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectSel8 + 1, "sel8"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexZ + 1, "Vertex Z"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectTriggerTVX + 1, "TVX"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectITSROFrameBorder + 1, "ITS ROF border"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectTimeFrameBorder + 1, "TF border"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexITSTPC + 1, "ITS-TPC vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectGoodZvtxFT0vsPV + 1, "FT0/PV z"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexTOFmatched + 1, "TOF vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectVertexTRDmatched + 1, "TRD vertex"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectGoodITSLayersAll + 1, "Good ITS layers"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectSameBunchPileup + 1, "Same bunch pileup"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeStandard + 1, "Time range std"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeStrict + 1, "Time range strict"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInTimeRangeNarrow + 1, "Time range narrow"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInRofStandard + 1, "ROF std"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectCollInRofStrict + 1, "ROF strict"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectHighMultCollInPrevRof + 1, "High mult prev ROF"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectINELgt0 + 1, "INEL > 0"); + hCollRej->GetXaxis()->SetBinLabel(kCollRejectINELgt1 + 1, "INEL > 1"); + + auto hTrackRej = seTrackQA.get(HIST("SE/Tracks/TrackRejection")); + + hTrackRej->GetXaxis()->SetBinLabel(kTrackAccepted + 1, "Accepted"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPt + 1, "pT"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectEta + 1, "Eta"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectMomentum + 1, "Momentum"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectCharge + 1, "Charge"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrack + 1, "Quality track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrackITS + 1, "Quality ITS"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectQualityTrackTPC + 1, "Quality TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPrimaryTrack + 1, "Primary track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectInAcceptanceTrack + 1, "Acceptance track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrack + 1, "Global track"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoDCA + 1, "Global wo DCA"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoPtEta + 1, "Global wo pT eta"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoTPCCluster + 1, "Global wo TPC cluster"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectGlobalTrackWoDCATPCCluster + 1, "Global wo DCA/TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPVContributor + 1, "PV contributor"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITS + 1, "No ITS"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPC + 1, "No TPC"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTOF + 1, "No TOF"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTRD + 1, "No TRD"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCNClsFound + 1, "TPC NCls found"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCCrossedRows + 1, "TPC crossed rows"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCCrossedRowsOverFindable + 1, "TPC crossed/findable"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCFoundOverFindable + 1, "TPC found/findable"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCFractionShared + 1, "TPC shared fraction"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectTPCChi2 + 1, "TPC chi2"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSNCls + 1, "ITS NCls"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSNClsInnerBarrel + 1, "ITS IB NCls"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectITSChi2 + 1, "ITS chi2"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectFixedDCAxy + 1, "Fixed DCAxy"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectFixedDCAz + 1, "Fixed DCAz"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPtDependentDCAxy + 1, "pT-dep DCAxy"); + hTrackRej->GetXaxis()->SetBinLabel(kTrackRejectPtDependentDCAz + 1, "pT-dep DCAz"); + + //______________________________________________________________________________ + // Correlation histogram booking helpers + auto addTrackQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + histReg.add((basePath + "TPCSignalVsTPCInnerParam").c_str(), "TPC signal vs p_{TPC inner};p_{TPC inner} (GeV/c);TPC signal", kTH2F, {axisTPCInnerParam, axisTPCSignal}); + histReg.add((basePath + "TPCSignalVsP").c_str(), "TPC signal vs p;p (GeV/c);TPC signal", kTH2F, {axisP, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsTOFExpMom").c_str(), "TOF #beta vs p_{TOF};p_{TOF} (GeV/c);#beta", kTH2F, {axisTOFExpMom, axisTOFBeta}); + histReg.add((basePath + "TOFBetaVsP").c_str(), "TOF #beta vs p;p (GeV/c);#beta", kTH2F, {axisP, axisTOFBeta}); + }; + + auto addFullTrackPIDQAHistos = [axisP, axisTPCNSigma, axisTOFNSigma](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "TPCNSigmaVsP").c_str(), "TPC n#sigma vs p;p (GeV/c);n#sigma_{TPC}", HistType::kTH2F, {axisP, axisTPCNSigma}); + histReg.add((basePath + "TOFNSigmaVsP").c_str(), "TOF n#sigma vs p;p (GeV/c);n#sigma_{TOF}", HistType::kTH2F, {axisP, axisTOFNSigma}); + histReg.add((basePath + "TPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma;n#sigma_{TPC};n#sigma_{TOF}", HistType::kTH2F, {axisTPCNSigma, axisTOFNSigma}); + }; + + auto addFullTrackQAHistos = [&](auto& histReg, const std::string& basePath) { + addTrackQAHistos(histReg, basePath); + + histReg.add((basePath + "TPCInnerParam").c_str(), "TPC inner momentum;p_{TPC inner} (GeV/c);Counts", HistType::kTH1F, {axisTPCInnerParam}); + histReg.add((basePath + "TOFExpMom").c_str(), "TOF expected momentum;p_{TOF} (GeV/c);Counts", HistType::kTH1F, {axisTOFExpMom}); + + histReg.add((basePath + "DCAxyVsP").c_str(), "DCA_{xy} vs p;p (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisP, axisDcaXY}); + histReg.add((basePath + "DCAxyVsTPCInnerParam").c_str(), "DCA_{xy} vs p_{TPC inner};p_{TPC inner} (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisTPCInnerParam, axisDcaXY}); + histReg.add((basePath + "DCAxyVsTOFExpMom").c_str(), "DCA_{xy} vs p_{TOF};p_{TOF} (GeV/c);DCA_{xy} (cm)", HistType::kTH2F, {axisTOFExpMom, axisDcaXY}); + + histReg.add((basePath + "DCAzVsP").c_str(), "DCA_{z} vs p;p (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisP, axisDcaZ}); + histReg.add((basePath + "DCAzVsTPCInnerParam").c_str(), "DCA_{z} vs p_{TPC inner};p_{TPC inner} (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisTPCInnerParam, axisDcaZ}); + histReg.add((basePath + "DCAzVsTOFExpMom").c_str(), "DCA_{z} vs p_{TOF};p_{TOF} (GeV/c);DCA_{z} (cm)", HistType::kTH2F, {axisTOFExpMom, axisDcaZ}); + + histReg.add((basePath + "PVsPt").c_str(), "p vs p_{T};p (GeV/c);p_{T} (GeV/c)", HistType::kTH2F, {axisP, axisPt}); + histReg.add((basePath + "PVsTPCInnerParam").c_str(), "p vs p_{TPC inner};p (GeV/c);p_{TPC inner} (GeV/c)", HistType::kTH2F, {axisP, axisTPCInnerParam}); + histReg.add((basePath + "PVsTOFExpMom").c_str(), "p vs p_{TOF};p (GeV/c);p_{TOF} (GeV/c)", HistType::kTH2F, {axisP, axisTOFExpMom}); + + histReg.add((basePath + "TPCSignalVsTOFExpMom").c_str(), "TPC signal vs p_{TOF};p_{TOF} (GeV/c);TPC signal", HistType::kTH2F, {axisTOFExpMom, axisTPCSignal}); + histReg.add((basePath + "TOFBetaVsTPCInnerParam").c_str(), "TOF #beta vs p_{TPC inner};p_{TPC inner} (GeV/c);#beta", HistType::kTH2F, {axisTPCInnerParam, axisTOFBeta}); + + addFullTrackPIDQAHistos(histReg, basePath + "PID/Pi/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Ka/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Pr/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/El/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/Mu/"); + histReg.addClone(basePath + "PID/Pi/", basePath + "PID/De/"); + }; + addFullTrackQAHistos(seTrackQA, "SE/Tracks/BeforeSelection/"); + seTrackQA.addClone("SE/Tracks/BeforeSelection/", "SE/Tracks/AfterSelection/"); + + auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTH3F, {axisDeltaPhiSpec, axisDeltaEtaSpec, axisCorrCentrality}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta, axisCorrCentrality}); + }; + + auto addResoMassCorrHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis) { + histReg.add((basePath + "DeltaPhiVsInvMass").c_str(), "#Delta#varphi vs invariant mass;Invariant mass (GeV/c^{2});#Delta#varphi;Centrality (%)", HistType::kTH3F, {massAxis, axisDeltaPhiSpec, axisCorrCentrality}); + histReg.add((basePath + "DeltaEtaVsInvMass").c_str(), "#Delta#eta vs invariant mass;Invariant mass (GeV/c^{2});#Delta#eta;Centrality (%)", HistType::kTH3F, {massAxis, axisDeltaEtaSpec, axisCorrCentrality}); + }; + + auto addResoAssocQAHistos = [&](auto& histReg, const std::string& basePath, const AxisSpec& massAxis, const std::string& massHistTitle) { + histReg.add((basePath + "P").c_str(), "Resonance;p (GeV/c);Counts", HistType::kTH1F, {axisP}); + histReg.add((basePath + "Pt").c_str(), "Resonance;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "Resonance;#eta;Counts", HistType::kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "Resonance;#varphi;Counts", HistType::kTH1F, {axisPhi}); + histReg.add((basePath + "Rapidity").c_str(), "Resonance;y;Counts", HistType::kTH1F, {axisRapidity}); + histReg.add((basePath + "InvMass").c_str(), massHistTitle.c_str(), HistType::kTH1F, {massAxis}); + histReg.add((basePath + "InvMassVsPt").c_str(), "Invariant mass vs p_{T};p_{T} (GeV/c);Invariant mass (GeV/c^{2})", HistType::kTH2F, {axisPt, massAxis}); + }; + + auto addResoDauQAHistos = [&](auto& histReg, const std::string& basePath) { + addBasicTrackQAHistos(histReg, basePath, axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, basePath, axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + }; + + // h-h ______________________________________________________________________________ + addTrackQAHistos(hhCorrelation, "SE/hh/QA/Trigger/"); + hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocLowPt/"); + hhCorrelation.addClone("SE/hh/QA/Trigger/", "SE/hh/QA/AssocHighPt/"); + addCorrHistos(hhCorrelation, "SE/hh/Corr/AssocLowPt/"); + hhCorrelation.addClone("SE/hh/Corr/AssocLowPt/", "SE/hh/Corr/AssocHighPt/"); + + // h-Id ______________________________________________________________________________ + auto registerIdentifiedHistograms = [&](auto& histReg, const std::string& pairName) { + const std::string base = "SE/" + pairName + "/"; + + addTrackQAHistos(histReg, base + "QA/Trigger/"); + addBasicTrackQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisPt, axisEta, axisPhi, axisDcaXY, axisDcaZ, axisSign); + addIdentifiedQAHistos(histReg, base + "QA/AssocLowPt/Pi/", axisP, axisRapidity, axisTPCSignal, axisTOFBeta, axisTPCNSigma, axisTOFNSigma, axisIdMethod); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Ka/"); + histReg.addClone(base + "QA/AssocLowPt/Pi/", base + "QA/AssocLowPt/Pr/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + + addCorrHistos(histReg, base + "Corr/AssocLowPt/Pi/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Ka/"); + histReg.addClone(base + "Corr/AssocLowPt/Pi/", base + "Corr/AssocLowPt/Pr/"); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + registerIdentifiedHistograms(hIdCorrelation, "hIdentified"); + + // h-Resonance ______________________________________________________________________________ + auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName, const AxisSpec& massAxis, const std::string& massHistTitle) { + const std::string base = "SE/" + pairName + "/"; + const std::string qaPeak = base + "QA/AssocLowPt/Peak/"; + + addTrackQAHistos(histReg, base + "QA/Trigger/"); + addResoAssocQAHistos(histReg, qaPeak + "Reso/", massAxis, massHistTitle); + addResoDauQAHistos(histReg, qaPeak + "PosDau/"); + histReg.addClone(qaPeak + "PosDau/", qaPeak + "NegDau/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/LSB/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/RSB/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + + addCorrHistos(histReg, base + "Corr/AssocLowPt/Peak/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/LSB/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/RSB/"); + addResoMassCorrHistos(histReg, base + "Corr/AssocLowPt/MassDependent/", massAxis); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + + registerResonanceHistograms(hPhiCorrelation, "hPhi", axisPhi1020Mass, "#phi(1020);m_{K^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarCorrelation, "hKStar", axisKStar892Mass, "K^{*}(892)^{0};m_{K^{+}#pi^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar", axisKStar892Mass, "#bar{K}^{*}(892)^{0};m_{#pi^{+}K^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaCorrelation, "hLambda", axisLambda1520Mass, "#Lambda(1520);m_{pK^{-}} (GeV/#it{c}^{2});Counts"); + registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar", axisLambda1520Mass, "#bar{#Lambda}(1520);m_{K^{+}#bar{p}} (GeV/#it{c}^{2});Counts"); + + auto addInvariantMassSpectra = [&](const std::string& species, const AxisSpec& massAxis) { + const std::string seBase = "SE/InvariantMass/" + species + "/"; + const std::string meBase = "ME/InvariantMass/" + species + "/"; + + resonanceMassSpectra.add((seBase + "US").c_str(), "Unlike-sign;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + resonanceMassSpectra.add((seBase + "LS").c_str(), "Like-sign;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + resonanceMassSpectra.add((meBase + "US").c_str(), "Mixed-event;m_{inv} (GeV/#it{c}^{2});p_{T} (GeV/#it{c});Centrality (%)", kTHnSparseF, {massAxis, axisPt, axisResoMassCent}); + }; + + addInvariantMassSpectra("Phi", axisPhi1020Mass); + addInvariantMassSpectra("KStar", axisKStar892Mass); + addInvariantMassSpectra("KStarBar", axisKStar892Mass); + addInvariantMassSpectra("Lambda1520", axisLambda1520Mass); + addInvariantMassSpectra("Lambda1520Bar", axisLambda1520Mass); + + //______________________________________________________________________________ + // Event-mixing QA + mixingQA.add("Mixing/DataFramesProcessed", "Processed dataframes;Dataframe;Count", HistType::kTH1D, {{1, -0.5, 0.5}}); + mixingQA.add("Mixing/AllCollisionsPerBin", "Selected collisions per mixing bin;Mixing bin;Collisions", HistType::kTH1D, {axisMixBin}); + mixingQA.add("Mixing/AllCollisionsZCent", "Selected collisions in physical mixing pools;z_{vtx} (cm);Centrality (%)", HistType::kTH2D, {axisVtxMixSpec, axisCentMixSpec}); + mixingQA.add("Mixing/EligibleCollisionsPerBin", "Eligible collisions per mixing pool;Mixing bin;Correlation channel", HistType::kTH2D, {axisMixBin, axisCorrChannel}); + mixingQA.add("Mixing/EligibleCollisionsZCentChannel", "Eligible collisions in physical mixing pools;z_{vtx} (cm);Centrality (%);Correlation channel", HistType::kTH3D, {axisVtxMixSpec, axisCentMixSpec, axisCorrChannel}); + mixingQA.add("Mixing/PerDF/AllPoolOccupancy", "Mixing-pool occupancy per dataframe;Mixing bin;N collisions", HistType::kTH2D, {axisMixBin, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/EligiblePoolOccupancy", "Eligible pool occupancy per dataframe;Mixing bin;Correlation channel;N eligible collisions", HistType::kTH3D, {axisMixBin, axisCorrChannel, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/PoolOccupancyDistribution", "Distribution of mixing-pool occupancies;Correlation channel;N eligible collisions", HistType::kTH2D, {axisCorrChannel, axisPoolOccupancy}); + mixingQA.add("Mixing/PerDF/ReadyPoolMap", "Dataframes where pool was ready;Mixing bin;Correlation channel", HistType::kTH2D, {axisMixBin, axisCorrChannel}); + mixingQA.add("Mixing/PerDF/NReadyMixBins", "Number of ready mixing bins per dataframe;Correlation channel;N ready mixing bins", HistType::kTH2D, {axisCorrChannel, axisReadyBins}); + + auto setCorrChannelLabels = [](TAxis* axis) { + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + axis->SetBinLabel(iCorr * kNCorrCountPt + kCountLowPt + 1, (std::string(CorrCountTypeName[iCorr]) + "_LowPt").c_str()); + axis->SetBinLabel(iCorr * kNCorrCountPt + kCountHighPt + 1, (std::string(CorrCountTypeName[iCorr]) + "_HighPt").c_str()); + } + }; + setCorrChannelLabels(mixingQA.get(HIST("Mixing/EligibleCollisionsPerBin"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/EligibleCollisionsZCentChannel"))->GetZaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/EligiblePoolOccupancy"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/PoolOccupancyDistribution"))->GetXaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/ReadyPoolMap"))->GetYaxis()); + setCorrChannelLabels(mixingQA.get(HIST("Mixing/PerDF/NReadyMixBins"))->GetXaxis()); + + mixingQA.add("DerivedData/RowsWritten", "Rows written to the derived tables;Table;Rows", HistType::kTH1D, {{4, -0.5, 3.5}}); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(1, "Collisions"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(2, "Triggers"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(3, "TrackAssocs"); + mixingQA.get(HIST("DerivedData/RowsWritten"))->GetXaxis()->SetBinLabel(4, "ResoAssocs"); + + if (cfgDebug.c1001printDebugMessages) { + LOGF(info, "Finishing init"); + } + } // Init function is over. + + //_______________________________ Particle Identification _______________________________ + // p-dependent identification + // p < ThrPforTOF : TPC+TOF (circular cut) if TOF is reliable, otherwise TPC only + // p >= ThrPforTOF : TPC+TOF (circular cut), TOF is required + // Default cuts are 3 sigma (see DefaultPIDcheckValues). + + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + template + bool vetoIdOthersTPC(const T& track) + { + // Static is only run once, ever. + static const std::array doVetoTPC = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kDoVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kDoVetoTPC)}; + + static const std::array vetoTPC = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kVetoTPC), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kVetoTPC)}; + + return applyVetoOthersTPC(track, doVetoTPC, vetoTPC); + } + + template + bool vetoIdOthersTOF(const T& track) + { + // Only computed once + static const std::array doVetoTOF = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kDoVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kDoVetoTOF)}; + + static const std::array vetoTOF = { + getCfg(cfgIdCut.c1143pidVetoSetting, kPi, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kKa, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kPr, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kEl, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kMu, kVetoTOF), + getCfg(cfgIdCut.c1143pidVetoSetting, kDe, kVetoTOF)}; + + return applyVetoOthersTOF(track, doVetoTOF, vetoTOF); + } + + template + bool vetoIdOthersTPCTOF(const T& track) + { + // If either veto fails, reject + return vetoIdOthersTPC(track) && vetoIdOthersTOF(track); + } + + // Check if TOF is reliable + template + inline bool checkReliableTOF(const T& track) + { + // which check makes the information of TOF relaiable? should track.beta() be checked? e.g.: + if (cfgIdCut.c1144cfgId07CheckTofBeta) { + return (track.hasTOF() && track.beta() > 0.0f); + } + return track.hasTOF(); + } + + template + bool idTPC(const T& track, const float& nSigmaTPC, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + + static const bool doVetoOthers = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPC(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTPCcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTPCcheck); + if (doRelativeTPCcheck) { + if (!relativeIdOthersTPC(track)) { + // If relativeIdOthersTPC = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPC = false; some particle has stronger nSigma compared to it + return false; + } + } + + if constexpr (pidMode == kPi) { + return std::fabs(track.tpcNSigmaPi()) < nSigmaTPC; + } else if constexpr (pidMode == kKa) { + return std::fabs(track.tpcNSigmaKa()) < nSigmaTPC; + } else if constexpr (pidMode == kPr) { + return std::fabs(track.tpcNSigmaPr()) < nSigmaTPC; + } else if constexpr (pidMode == kEl) { + return std::fabs(track.tpcNSigmaEl()) < nSigmaTPC; + } else if constexpr (pidMode == kMu) { + return std::fabs(track.tpcNSigmaMu()) < nSigmaTPC; + } else if constexpr (pidMode == kDe) { + return std::fabs(track.tpcNSigmaDe()) < nSigmaTPC; + } else { + return false; + } + } + + template + bool idTPCTOF(const T& track, const int& pidCutType, const float& nSigmaTPC, const float& nSigmaTOF, const float& nSigmaSquaredRad, float& nSigmaIdDistSq) + { + if constexpr (pidMode == kPi) { + nSigmaIdDistSq = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + } else if constexpr (pidMode == kKa) { + nSigmaIdDistSq = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + } else if constexpr (pidMode == kPr) { + nSigmaIdDistSq = track.tpcNSigmaPr() * track.tpcNSigmaPr() + track.tofNSigmaPr() * track.tofNSigmaPr(); + } else if constexpr (pidMode == kEl) { + nSigmaIdDistSq = track.tpcNSigmaEl() * track.tpcNSigmaEl() + track.tofNSigmaEl() * track.tofNSigmaEl(); + } else if constexpr (pidMode == kMu) { + nSigmaIdDistSq = track.tpcNSigmaMu() * track.tpcNSigmaMu() + track.tofNSigmaMu() * track.tofNSigmaMu(); + } else if constexpr (pidMode == kDe) { + nSigmaIdDistSq = track.tpcNSigmaDe() * track.tpcNSigmaDe() + track.tofNSigmaDe() * track.tofNSigmaDe(); + } else { + nSigmaIdDistSq = 1000000; + } + static const bool doVetoOthers = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoVetoOthers); + if (doVetoOthers) { + if (!vetoIdOthersTPCTOF(track)) { + // If vetoIdOthers = true; it passed all veto checks + // If vetoIdOthers = false; it failed veto check with some other particle + return false; + } + } + + static const bool doRelativeTOFcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTOFcheck); + if (doRelativeTOFcheck) { + if (!relativeIdOthersTOF(track)) { + // If relativeIdOthersTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + static const bool doRelativeTPCTOFcheck = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kDoRelativeTPCTOFcheck); + if (doRelativeTPCTOFcheck) { + if (!relativeIdOthersTPCTOF(track)) { + // If relativeIdOthersTPCTOF = true; particle has stronger nSigma compared to others + // If relativeIdOthersTPCTOF = false; some particle has stronger nSigma compared to it + return false; + } + } + + switch (pidCutType) { + case kCircularCut: + return selIdCircularCut(track, nSigmaSquaredRad); + case kRectangularCut: + return selIdRectangularCut(track, nSigmaTPC, nSigmaTOF); + case kEllipsoidalCut: + return selIdEllipsoidalCut(track, nSigmaTPC, nSigmaTOF); + default: + return false; + } + } + + template + bool selPdependent(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + // Static cache inside function - initialized once on first call + static const auto thrPforTOF = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kThrPforTOF); + static const auto idCutTypeLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kIdCutTypeLowP); + static const auto nSigmaTPCLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTPCLowP); + static const auto nSigmaTOFLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTOFLowP); + static const auto nSigmaRadLowP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaRadLowP); + static const auto idCutTypeHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kIdCutTypeHighP); + static const auto nSigmaTPCHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTPCHighP); + static const auto nSigmaTOFHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaTOFHighP); + static const auto nSigmaRadHighP = getCfg(cfgIdCut.c1142pidConfigSetting, pidMode, kNSigmaRadHighP); + + if (track.p() < thrPforTOF) { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeLowP, nSigmaTPCLowP, nSigmaTOFLowP, nSigmaRadLowP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } else { + if (idTPC(track, nSigmaTPCLowP, nSigmaIdDistSq)) { + IdMethod = kTPCidentified; + return true; + } + } + } else { + if (checkReliableTOF(track)) { + if (idTPCTOF(track, idCutTypeHighP, nSigmaTPCHighP, nSigmaTOFHighP, nSigmaRadHighP, nSigmaIdDistSq)) { + IdMethod = kTPCTOFidentified; + return true; + } + } + } + return false; + } + + //______________________________Identification Functions________________________________________________________________ + // Pion + template + bool selPion(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Kaon + template + bool selKaon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Proton + template + bool selProton(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Electron + template + bool selElectron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Muon + template + bool selMuon(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Deuteron + template + bool selDeuteron(const T& track, int& IdMethod, float& nSigmaIdDistSq) + { + return selPdependent(track, IdMethod, nSigmaIdDistSq); + } + + // Two-argument versions + template + bool selPion(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selPion(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selKaon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selKaon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selProton(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selProton(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selElectron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selElectron(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selMuon(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selMuon(track, IdMethod, nSigmaIdDistSq); + } + + template + bool selDeuteron(const T& track, int& IdMethod) + { + float nSigmaIdDistSq = -1.0f; + return selDeuteron(track, IdMethod, nSigmaIdDistSq); + } + // + + template + CollisionRejectionTag selCollision(T const& collision) + { + // Basic event selection + if (cfgEvent.c1003requireSel8 && !collision.sel8()) { + return kCollRejectSel8; + } + + if (std::abs(collision.posZ()) >= cfgEvent.c1002cutZvertex) { + return kCollRejectVertexZ; + } + + if (cfgEvent.c1004requireTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return kCollRejectTriggerTVX; + } + + // TF / ITS-ROF borders + if (cfgEvent.c1007requireNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return kCollRejectITSROFrameBorder; + } + + if (cfgEvent.c1008requireNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return kCollRejectTimeFrameBorder; + } + + // Vertex quality + if (cfgEvent.c1009requireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return kCollRejectVertexITSTPC; + } + + if (cfgEvent.c1010requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return kCollRejectGoodZvtxFT0vsPV; + } + + if (cfgEvent.c1011requireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return kCollRejectVertexTOFmatched; + } + + if (cfgEvent.c1012requireVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return kCollRejectVertexTRDmatched; + } + + if (cfgEvent.c1013requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return kCollRejectGoodITSLayersAll; + } + + // Pileup / neighbouring-collision rejection + if (cfgEvent.c1014requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return kCollRejectSameBunchPileup; + } + + if (cfgEvent.c1015requireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return kCollRejectCollInTimeRangeStandard; + } + + if (cfgEvent.c1016requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return kCollRejectCollInTimeRangeStrict; + } + + if (cfgEvent.c1017requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return kCollRejectCollInTimeRangeNarrow; + } + + if (cfgEvent.c1018requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return kCollRejectCollInRofStandard; + } + + if (cfgEvent.c1019requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return kCollRejectCollInRofStrict; + } + + if (cfgEvent.c1020requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return kCollRejectHighMultCollInPrevRof; + } + + // INEL classes + if (cfgEvent.c1021requireINELgt0 && !collision.isInelGt0()) { + return kCollRejectINELgt0; + } + + if (cfgEvent.c1022requireINELgt1 && !collision.isInelGt1()) { + return kCollRejectINELgt1; + } + + return kCollAccepted; + } // selCollision is Over. + + template + TrackRejectionTag selectionTrack(T const& track) + { + + // Filter cuts again: resonance daughters are reached through an index into the full track table, + // which does not re-apply the Filter (the resonance producer uses looser cuts). + if (track.pt() <= cfgTrackCuts.c1043ptMin || track.pt() >= cfgTrackCuts.c1044ptMax) { + return kTrackRejectPt; + } + if (std::abs(track.eta()) >= cfgTrackCuts.c1045etaMax) { + return kTrackRejectEta; + } + if (cfgTrackCuts.c1065useFixedDCAxy && std::abs(track.dcaXY()) >= cfgTrackCuts.c1066dcaXYMax) { + return kTrackRejectFixedDCAxy; + } + if (cfgTrackCuts.c1067useFixedDCAz && std::abs(track.dcaZ()) >= cfgTrackCuts.c1068dcaZMax) { + return kTrackRejectFixedDCAz; + } + // Filter cuts are over + + // Standard O2 flags + if (cfgTrackCuts.c1046requireGlobalTrack && !track.isGlobalTrack()) { + return kTrackRejectGlobalTrack; + } + + if (cfgTrackCuts.c1047requireGlobalTrackWoDCA && !track.isGlobalTrackWoDCA()) { + return kTrackRejectGlobalTrackWoDCA; + } + + if (cfgTrackCuts.c1048requirePVContributor && !track.isPVContributor()) { + return kTrackRejectPVContributor; + } + + // Detector presence + if (cfgTrackCuts.c1049requireITS && !track.hasITS()) { + return kTrackRejectITS; + } + + if (cfgTrackCuts.c1050requireTPC && !track.hasTPC()) { + return kTrackRejectTPC; + } + + if (cfgTrackCuts.c1051requireTOF && !track.hasTOF()) { + return kTrackRejectTOF; + } + + if (cfgTrackCuts.c1052requireTRD && !track.hasTRD()) { + return kTrackRejectTRD; + } + + // TPC quality + if (track.tpcNClsFound() < cfgTrackCuts.c1053tpcNClsFoundMin) { + return kTrackRejectTPCNClsFound; + } + + if (track.tpcNClsCrossedRows() < cfgTrackCuts.c1054tpcNClsCrossedRowsMin) { + return kTrackRejectTPCCrossedRows; + } + + if (track.tpcCrossedRowsOverFindableCls() < cfgTrackCuts.c1055tpcCrossedRowsOverFindableMin) { + return kTrackRejectTPCCrossedRowsOverFindable; + } + + if (track.tpcFoundOverFindableCls() < cfgTrackCuts.c1056tpcFoundOverFindableMin) { + return kTrackRejectTPCFoundOverFindable; + } + + if (track.tpcFractionSharedCls() > cfgTrackCuts.c1057tpcFractionSharedMax) { + return kTrackRejectTPCFractionShared; + } + + if (track.tpcChi2NCl() < cfgTrackCuts.c1058tpcChi2NClMin || track.tpcChi2NCl() > cfgTrackCuts.c1059tpcChi2NClMax) { + return kTrackRejectTPCChi2; + } + + // ITS quality + if (track.itsNCls() < cfgTrackCuts.c1060itsNClsMin || track.itsNCls() > cfgTrackCuts.c1061itsNClsMax) { + return kTrackRejectITSNCls; + } + + if (track.itsNClsInnerBarrel() < cfgTrackCuts.c1062itsNClsInnerBarrelMin) { + return kTrackRejectITSNClsInnerBarrel; + } + + if (track.itsChi2NCl() < cfgTrackCuts.c1063itsChi2NClMin || track.itsChi2NCl() > cfgTrackCuts.c1064itsChi2NClMax) { + return kTrackRejectITSChi2; + } + + // pT-dependent DCA + if (cfgTrackCuts.c1069usePtDependentDCAxy) { + const float dcaXYMax = cfgTrackCuts.c1070dcaXYPtA + cfgTrackCuts.c1071dcaXYPtB / std::pow(track.pt(), cfgTrackCuts.c1072dcaXYPtC); + if (std::abs(track.dcaXY()) > dcaXYMax) { + return kTrackRejectPtDependentDCAxy; + } + } + + if (cfgTrackCuts.c1073usePtDependentDCAz) { + const float dcaZMax = cfgTrackCuts.c1074dcaZPtA; + if (std::abs(track.dcaZ()) > dcaZMax) { + return kTrackRejectPtDependentDCAz; + } + } + + return kTrackAccepted; + } + + template + void fillEventQA(const C& collision) + { + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VertexZ"), collision.posZ()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0C"), collision.centFT0C()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0M"), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0A"), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFV0A"), collision.centFV0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0C"), collision.posZ(), collision.centFT0C()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0M"), collision.posZ(), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFT0A"), collision.posZ(), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsCentFV0A"), collision.posZ(), collision.centFV0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFT0M"), collision.centFT0C(), collision.centFT0M()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFT0A"), collision.centFT0C(), collision.centFT0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0MVsCentFT0A"), collision.centFT0M(), collision.centFT0A()); + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0CVsCentFV0A"), collision.centFT0C(), collision.centFV0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0MVsCentFV0A"), collision.centFT0M(), collision.centFV0A()); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("CentFT0AVsCentFV0A"), collision.centFT0A(), collision.centFV0A()); + + const float mixingEstimatorValue = getMixingEstimatorValue(collision); + const int mixingBin = getMixingBin(collision); + const bool validMixingBin = mixingBin >= 0 && mixingBin < nMixBins; + + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingEstimator"), mixingEstimatorValue); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("VtxZVsMixingEstimator"), collision.posZ(), mixingEstimatorValue); + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingBinStatus"), validMixingBin ? 1.0f : 0.0f); + + if (validMixingBin) { + seEventQA.fill(HIST("SE/Events/") + HIST(QAStageDire[qaStage]) + HIST("MixingBin"), mixingBin); + } + } + + template + void fillFullTrackPIDQA(const T& track) + { + float tpcNSigma = 0.0f; + float tofNSigma = 0.0f; + + if constexpr (pidType == kPi) { + tpcNSigma = track.tpcNSigmaPi(); + tofNSigma = track.tofNSigmaPi(); + } else if constexpr (pidType == kKa) { + tpcNSigma = track.tpcNSigmaKa(); + tofNSigma = track.tofNSigmaKa(); + } else if constexpr (pidType == kPr) { + tpcNSigma = track.tpcNSigmaPr(); + tofNSigma = track.tofNSigmaPr(); + } else if constexpr (pidType == kEl) { + tpcNSigma = track.tpcNSigmaEl(); + tofNSigma = track.tofNSigmaEl(); + } else if constexpr (pidType == kMu) { + tpcNSigma = track.tpcNSigmaMu(); + tofNSigma = track.tofNSigmaMu(); + } else if constexpr (pidType == kDe) { + tpcNSigma = track.tpcNSigmaDe(); + tofNSigma = track.tofNSigmaDe(); + } + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PID/") + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); + } + + template + void fillFullTrackQA(const T& track) + { + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("P"), track.p()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Pt"), track.pt()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCInnerParam"), track.tpcInnerParam()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFExpMom"), track.tofExpMom()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Eta"), track.eta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Phi"), track.phi()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxy"), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAz"), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("Sign"), track.sign()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsP"), track.p(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsTPCInnerParam"), track.tpcInnerParam(), track.dcaXY()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAxyVsTOFExpMom"), track.tofExpMom(), track.dcaXY()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsP"), track.p(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsTPCInnerParam"), track.tpcInnerParam(), track.dcaZ()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("DCAzVsTOFExpMom"), track.tofExpMom(), track.dcaZ()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsPt"), track.p(), track.pt()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsTPCInnerParam"), track.p(), track.tpcInnerParam()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("PVsTOFExpMom"), track.p(), track.tofExpMom()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TPCSignalVsTOFExpMom"), track.tofExpMom(), track.tpcSignal()); + + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsTPCInnerParam"), track.tpcInnerParam(), track.beta()); + seTrackQA.fill(HIST("SE/Tracks/") + HIST(QAStageDire[qaStage]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); + + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + fillFullTrackPIDQA(track); + } + + template + void fillCorrParticleQA(H& histReg, const T& track) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsTPCInnerParam"), track.tpcInnerParam(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsTOFExpMom"), track.tofExpMom(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + } + + template + void fillIdentifiedCorrelation(H& histReg, const T& trigger, const A& associate, const int idMethod) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("P"), associate.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassPiPlus)); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassKPlus)); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Rapidity"), computeRapidity(associate, MassProton)); + } + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxy"), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAz"), associate.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAxyVsPt"), associate.pt(), associate.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DCAzVsPt"), associate.pt(), associate.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCSignalVsP"), associate.p(), associate.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFBetaVsP"), associate.p(), associate.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("IdMethodVsP"), associate.p(), idMethod); + + if constexpr (pidType == kPi) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPi(), associate.tofNSigmaPi()); + } else if constexpr (pidType == kKa) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaKa()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaKa(), associate.tofNSigmaKa()); + } else if constexpr (pidType == kPr) { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsP"), associate.p(), associate.tpcNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TOFNSigmaVsP"), associate.p(), associate.tofNSigmaPr()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("TPCNSigmaVsTOFNSigma"), associate.tpcNSigmaPr(), associate.tofNSigmaPr()); + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); + } + + //______________________________________________________________________________ + // Resonance candidate QA + template + void fillResoAssocQA(H& histReg, const A& associate, const float invMass) + { + const float p = std::sqrt(associate.px() * associate.px() + associate.py() * associate.py() + associate.pz() * associate.pz()); + const float energy = std::sqrt(p * p + invMass * invMass); + const float rapidity = 0.5f * std::log((energy + associate.pz()) / (energy - associate.pz())); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("P"), p); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("Rapidity"), rapidity); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMass"), invMass); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/") + HIST("InvMassVsPt"), associate.pt(), invMass); + } + + //______________________________________________________________________________ + // Resonance daughter QA + template + void fillResoDauQA(H& histReg, const T& track, const int idMethod) + { + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("P"), track.p()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Pt"), track.pt()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Eta"), track.eta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Phi"), track.phi()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxy"), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAz"), track.dcaZ()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Sign"), track.sign()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("DCAzVsPt"), track.pt(), track.dcaZ()); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCSignalVsP"), track.p(), track.tpcSignal()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFBetaVsP"), track.p(), track.beta()); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("IdMethodVsP"), track.p(), idMethod); + + float tpcNSigma = 0.0f; + float tofNSigma = 0.0f; + float massHypothesis = 0.0f; + + if constexpr (pairType == kHPhi || (pairType == kHKStar && dauType == kPosDau) || (pairType == kHKStarBar && dauType == kNegDau) || + (pairType == kHLambda && dauType == kNegDau) || (pairType == kHLambdaBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaKa(); + tofNSigma = track.tofNSigmaKa(); + massHypothesis = MassKPlus; + } else if constexpr ((pairType == kHKStar && dauType == kNegDau) || (pairType == kHKStarBar && dauType == kPosDau)) { + tpcNSigma = track.tpcNSigmaPi(); + tofNSigma = track.tofNSigmaPi(); + massHypothesis = MassPiPlus; + } else if constexpr ((pairType == kHLambda && dauType == kPosDau) || (pairType == kHLambdaBar && dauType == kNegDau)) { + tpcNSigma = track.tpcNSigmaPr(); + tofNSigma = track.tofNSigmaPr(); + massHypothesis = MassProton; + } + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("Rapidity"), computeRapidity(track, massHypothesis)); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsP"), track.p(), tpcNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TOFNSigmaVsP"), track.p(), tofNSigma); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST(DauTypeDire[dauType]) + HIST("TPCNSigmaVsTOFNSigma"), tpcNSigma, tofNSigma); + } + + //______________________________________________________________________________ + // Generic h-h correlation + template + void fillTrigAssociateCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[analysisType]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); + } + + //______________________________________________________________________________ + // Resonance correlation + template + void fillResoCorrelation(H& histReg, const T& trigger, const A& associate) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, currentCentrality); + } + + // Resonance correlation as a function of invariant mass + template + void fillResoMassCorrelation(H& histReg, const T& trigger, const A& associate, const float invMass) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaPhiVsInvMass"), invMass, dPhi, currentCentrality); + histReg.fill(HIST(EventTypeDire[eventType]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("MassDependent/") + HIST("DeltaEtaVsInvMass"), invMass, dEta, currentCentrality); + } + + template + void fillResoRegion(H& histReg, const T& trigger, const A& associate, const P& posDauTrack, const N& negDauTrack, const float invMass, const int posDauIdMethod, const int negDauIdMethod) + { + fillResoAssocQA(histReg, associate, invMass); + fillResoDauQA(histReg, posDauTrack, posDauIdMethod); + fillResoDauQA(histReg, negDauTrack, negDauIdMethod); + fillResoCorrelation(histReg, trigger, associate); + } + + template + bool selectResonanceDaughters(const P& posDauTrack, const N& negDauTrack, int& posDauIdMethod, int& negDauIdMethod) + { + // Phi -> K+ K- ----------------------- + // K(892)* -> K+ + pi- ----------------------- + // K(892)*Bar -> K- + pi+ ----------------------- + // Λ(1520) -> P+ + Ka- ----------------------- + // Λ(1520)Bar -> PBar + Ka+ ----------------------- + + posDauIdMethod = kUnidentified; + negDauIdMethod = kUnidentified; + if constexpr (pairType == kHPhi) { + return selKaon(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStar) { + return selKaon(posDauTrack, posDauIdMethod) && selPion(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHKStarBar) { + return selPion(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambda) { + return selProton(posDauTrack, posDauIdMethod) && selKaon(negDauTrack, negDauIdMethod); + } else if constexpr (pairType == kHLambdaBar) { + return selKaon(posDauTrack, posDauIdMethod) && selProton(negDauTrack, negDauIdMethod); + } + return false; + } + + template + uint8_t getResonanceMassRegion(const A& associate, float& invMass) + { + if constexpr (pairType == kHPhi) { + invMass = associate.mPhi1020(); + return getMassRegionTag(invMass, cfgPhi1020CorrMass.c1117phi1020LSBLow, cfgPhi1020CorrMass.c1118phi1020LSBUp, cfgPhi1020CorrMass.c1115phi1020PeakLow, cfgPhi1020CorrMass.c1116phi1020PeakUp, cfgPhi1020CorrMass.c1119phi1020RSBLow, cfgPhi1020CorrMass.c1120phi1020RSBUp); + } else if constexpr (pairType == kHKStar) { + invMass = associate.mKStar892(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1123kStar892LSBLow, cfgKStar892CorrMass.c1124kStar892LSBUp, cfgKStar892CorrMass.c1121kStar892PeakLow, cfgKStar892CorrMass.c1122kStar892PeakUp, cfgKStar892CorrMass.c1125kStar892RSBLow, cfgKStar892CorrMass.c1126kStar892RSBUp); + } else if constexpr (pairType == kHKStarBar) { + invMass = associate.mKStar892Bar(); + return getMassRegionTag(invMass, cfgKStar892CorrMass.c1123kStar892LSBLow, cfgKStar892CorrMass.c1124kStar892LSBUp, cfgKStar892CorrMass.c1121kStar892PeakLow, cfgKStar892CorrMass.c1122kStar892PeakUp, cfgKStar892CorrMass.c1125kStar892RSBLow, cfgKStar892CorrMass.c1126kStar892RSBUp); + } else if constexpr (pairType == kHLambda) { + invMass = associate.mLambda1520(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1129lambda1520LSBLow, cfgLambda1520CorrMass.c1130lambda1520LSBUp, cfgLambda1520CorrMass.c1127lambda1520PeakLow, cfgLambda1520CorrMass.c1128lambda1520PeakUp, cfgLambda1520CorrMass.c1131lambda1520RSBLow, cfgLambda1520CorrMass.c1132lambda1520RSBUp); + } else if constexpr (pairType == kHLambdaBar) { + invMass = associate.mLambda1520Bar(); + return getMassRegionTag(invMass, cfgLambda1520CorrMass.c1129lambda1520LSBLow, cfgLambda1520CorrMass.c1130lambda1520LSBUp, cfgLambda1520CorrMass.c1127lambda1520PeakLow, cfgLambda1520CorrMass.c1128lambda1520PeakUp, cfgLambda1520CorrMass.c1131lambda1520RSBLow, cfgLambda1520CorrMass.c1132lambda1520RSBUp); + } + + invMass = -1.0f; + return kMassOutside; + } + + template + void incrementCorrCount(CorrCountArray& counts) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + + if constexpr (pairType == kHH) { + ++counts[kCountHH][PtType]; + } else if constexpr (pairType == kHId && pidType == kPi) { + ++counts[kCountHPi][PtType]; + } else if constexpr (pairType == kHId && pidType == kKa) { + ++counts[kCountHKa][PtType]; + } else if constexpr (pairType == kHId && pidType == kPr) { + ++counts[kCountHPr][PtType]; + } else if constexpr (pairType == kHPhi) { + ++counts[kCountHPhi][PtType]; + } else if constexpr (pairType == kHKStar) { + ++counts[kCountHKStar][PtType]; + } else if constexpr (pairType == kHKStarBar) { + ++counts[kCountHKStarBar][PtType]; + } else if constexpr (pairType == kHLambda) { + ++counts[kCountHLambda][PtType]; + } else if constexpr (pairType == kHLambdaBar) { + ++counts[kCountHLambdaBar][PtType]; + } + } + + template + void incrementResoRegionCount(ResoRegionCountArray& counts, uint8_t massRegion) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + + if (massRegion != kMassPeak && massRegion != kMassLSB && massRegion != kMassRSB) { + return; + } + + if constexpr (pairType == kHPhi) { + ++counts[kResoCountHPhi][PtType][massRegion]; + } else if constexpr (pairType == kHKStar) { + ++counts[kResoCountHKStar][PtType][massRegion]; + } else if constexpr (pairType == kHKStarBar) { + ++counts[kResoCountHKStarBar][PtType][massRegion]; + } else if constexpr (pairType == kHLambda) { + ++counts[kResoCountHLambda][PtType][massRegion]; + } else if constexpr (pairType == kHLambdaBar) { + ++counts[kResoCountHLambdaBar][PtType][massRegion]; + } + } + + template + float getInvariantMass(const T1& dau1, const T2& dau2) + { + const float p = RecoDecay::p(dau1.px() + dau2.px(), dau1.py() + dau2.py(), dau1.pz() + dau2.pz()); + + if constexpr (pairType == kHPhi) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHKStar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassPiPlus)); + } else if constexpr (pairType == kHKStarBar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassPiPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHLambda) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassProton) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassKPlus)); + } else if constexpr (pairType == kHLambdaBar) { + return RecoDecay::m(p, RecoDecay::e(dau1.px(), dau1.py(), dau1.pz(), MassKPlus) + RecoDecay::e(dau2.px(), dau2.py(), dau2.pz(), MassProton)); + } + + return -1.0f; + } + + template + bool fillInvariantMassPair(const C& collision, const T1& dau1, const T2& dau2) + { + int dau1IdMethod = kUnidentified; + int dau2IdMethod = kUnidentified; + + if (!selectResonanceDaughters(dau1, dau2, dau1IdMethod, dau2IdMethod)) { + return false; + } + + const float mass = getInvariantMass(dau1, dau2); + const float pt = RecoDecay::pt(dau1.px() + dau2.px(), dau1.py() + dau2.py()); + const float pz = dau1.pz() + dau2.pz(); + const float energy = RecoDecay::e(dau1.px() + dau2.px(), dau1.py() + dau2.py(), pz, mass); + const float rapidity = 0.5f * std::log((energy + pz) / (energy - pz)); + if (rapidity < cfgResPartitions.c1083resoRapidityMin || rapidity > cfgResPartitions.c1084resoRapidityMax) { + return false; + } + + float centrality = -1.0f; + switch (cfgEvent.c1028centralityEstimator) { + case 0: + centrality = collision.centFT0C(); + break; + case 1: + centrality = collision.centFT0M(); + break; + case 2: + centrality = collision.centFT0A(); + break; + case 3: + centrality = collision.centFV0A(); + break; + default: + return false; + } + + if constexpr (pairType == kHPhi && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Phi/US"), mass, pt, centrality); + } else if constexpr (pairType == kHPhi && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Phi/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHKStar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHKStar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStar/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHKStarBar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStarBar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHKStarBar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/KStarBar/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHLambda && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520/US"), mass, pt, centrality); + } else if constexpr (pairType == kHLambda && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520/LS"), mass, pt, centrality); + } else if constexpr (pairType == kHLambdaBar && !isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520Bar/US"), mass, pt, centrality); + } else if constexpr (pairType == kHLambdaBar && isLikeSign) { + resonanceMassSpectra.fill(HIST("SE/InvariantMass/Lambda1520Bar/LS"), mass, pt, centrality); + } + + return true; + } + + template + void fillUnlikeSignInvariantMass(const C& collision, const P& posTracksPerColl, const N& negTracksPerColl) + { + for (const auto& posTrack : posTracksPerColl) { + if (selectionTrack(posTrack) != kTrackAccepted) { + continue; + } + + for (const auto& negTrack : negTracksPerColl) { + if (selectionTrack(negTrack) != kTrackAccepted) { + continue; + } + + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + fillInvariantMassPair(collision, posTrack, negTrack); + } + } + } + + template + void fillLikeSignInvariantMass(const C& collision, const T& tracksPerColl) + { + for (const auto& track1 : tracksPerColl) { + if (selectionTrack(track1) != kTrackAccepted) { + continue; + } + + for (const auto& track2 : tracksPerColl) { + if (track2.globalIndex() <= track1.globalIndex()) { + continue; + } + if (selectionTrack(track2) != kTrackAccepted) { + continue; + } + + fillInvariantMassPair(collision, track1, track2); + + if constexpr (positiveSign) { + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + } else { + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + if (!fillInvariantMassPair(collision, track1, track2)) { + fillInvariantMassPair(collision, track2, track1); + } + } + } + } + } + + uint64_t getStoredCorrMask(uint64_t corrMask, int mixingBin) + { + if (mixingBin < 0 || mixingBin >= nMixBins) { + return 0ULL; + } + + if (cfgDerivedData.c1134mixingBin >= 0 && mixingBin != cfgDerivedData.c1134mixingBin) { + return 0ULL; + } + + const uint64_t requestedMask = cfgDerivedData.c1133pairMask; + if (requestedMask == 0ULL) { + return corrMask; + } + + if (cfgDerivedData.c1135requireAllPairBits && (corrMask & requestedMask) != requestedMask) { + return 0ULL; + } + + // Keeping only the requested pools, so tracks / resonances of other channels are not written. + // Resonance pools are the Peak/LSB/RSB bits (kMaskHPhiLowPtPeak ... kMaskHLambdaBarHighPtRSB). + return corrMask & requestedMask; + } + + //______________________________________________________________________________ + // Tracks : bit 0-7 = pools h, pi, K, p x low/high pT (same bit numbers as CorrPresenceMask), + // bit 15 = used as trigger + // Resonances : bit r = CorrPresenceMask pool bit (18 + r), r = species * 6 + pT class * 3 + (mass window - 1) + // Setting a bit again (e.g. for another trigger) changes nothing, so there are no duplicates, + // and no sorting or searching is needed. Re-sized per collision; the vectors keep their capacity. + static constexpr uint16_t TriggerUsedFlag = 1 << 15; + std::vector trackUsedFlags; + std::vector resoUsedFlags; + int64_t firstTrackGI = 0; + int64_t firstResoGI = 0; + float currentCentrality = -1.0f; // mixing-estimator value of the current collision, used as centrality axis of the correlation histograms + + template + void markTrackUsed(const T& track, const uint16_t flag) + { + const int64_t i = track.globalIndex() - firstTrackGI; + if (i >= 0 && i < static_cast(trackUsedFlags.size())) { + trackUsedFlags[i] |= flag; + } + } + + template + void markResoUsed(const R& resonance, const uint32_t flag) + { + const int64_t i = resonance.globalIndex() - firstResoGI; + if (i >= 0 && i < static_cast(resoUsedFlags.size())) { + resoUsedFlags[i] |= flag; + } + } + + // Particle (+1) / antiparticle (-1) / self-conjugate (0) flag from the associate type + static int8_t getResoSign(const int assocType) + { + if (assocType == kCountHKStar || assocType == kCountHLambda) { + return 1; + } + if (assocType == kCountHKStarBar || assocType == kCountHLambdaBar) { + return -1; + } + return 0; // phi(1020) + } + + // 01-Start-obtaining trigger-LowPtAssociates correlation + template + void executeTrigAssocCorrelation(H& histReg, const T& trigger, const A& associates, const auto& triggerGIList, CorrCountArray& corrCountPerColl, ResoRegionCountArray& resoRegionCountsPerColl) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + int associateSelTag = kTrackAccepted; + int assocPosDauSelTag = kTrackAccepted; + int assocNegDauSelTag = kTrackAccepted; + int posDauIdMethod = kUnidentified; + int negDauIdMethod = kUnidentified; + int pionIdMethod = kUnidentified; + int kaonIdMethod = kUnidentified; + int protonIdMethod = kUnidentified; + + for (const auto& associate : associates) { + //__________________________________________________________________________ + // Hadron-hadron and hadron-Identified correlation + if constexpr (pairType == kHH || pairType == kHId) { + associateSelTag = selectionTrack(associate); + if (associateSelTag != kTrackAccepted) { + continue; + } + + if constexpr (pairType == kHH) { + fillCorrParticleQA(histReg, associate); + fillTrigAssociateCorrelation(histReg, trigger, associate); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHH, PtType))); + } + + if constexpr (pairType == kHId) { + pionIdMethod = kUnidentified; + kaonIdMethod = kUnidentified; + protonIdMethod = kUnidentified; + + if (selPion(associate, pionIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, pionIdMethod); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHPi, PtType))); + } + if (selKaon(associate, kaonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, kaonIdMethod); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHKa, PtType))); + } + if (selProton(associate, protonIdMethod)) { + fillIdentifiedCorrelation(histReg, trigger, associate, protonIdMethod); + incrementCorrCount(corrCountPerColl); + markTrackUsed(associate, static_cast(getCorrMaskBit(kCountHPr, PtType))); + } + } + } + + //__________________________________________________________________________ + // Hadron-Resonance correlation + if constexpr (pairType == kHPhi || pairType == kHKStar || pairType == kHKStarBar || pairType == kHLambda || pairType == kHLambdaBar) { + const auto& posDauTrack = associate.template posTrack_as(); + const auto& negDauTrack = associate.template negTrack_as(); + + // Track Selection ------------------------ + assocPosDauSelTag = selectionTrack(posDauTrack); + if (assocPosDauSelTag != kTrackAccepted) { + continue; + } + + assocNegDauSelTag = selectionTrack(negDauTrack); + if (assocNegDauSelTag != kTrackAccepted) { + continue; + } + + if (!selectResonanceDaughters(posDauTrack, negDauTrack, posDauIdMethod, negDauIdMethod)) { + continue; + } + + float invMass = -1.0f; + const uint8_t massRegion = getResonanceMassRegion(associate, invMass); + const float rapidity = computeRapidity(associate, invMass); + if (rapidity < cfgResPartitions.c1083resoRapidityMin || rapidity > cfgResPartitions.c1084resoRapidityMax) { + continue; + } + + // Trigger-resonance daughter overlap -----expensive check ---- + if (c1113rejectResoWithAnyTriggerDaughter) { + // Strict mode: reject the whole resonance if either daughter belongs to the event trigger population. + if (checkTrackInList(posDauTrack, triggerGIList) || checkTrackInList(negDauTrack, triggerGIList)) { + continue; + } + } else { + // Standard pair-wise autocorrelation removal: reject only if the CURRENT trigger is a daughter. + if (trigger.globalIndex() == posDauTrack.globalIndex() || trigger.globalIndex() == negDauTrack.globalIndex()) { + continue; + } + } + + fillResoMassCorrelation(histReg, trigger, associate, invMass); + + if (massRegion == kMassOutside) { + continue; + } + + switch (massRegion) { + case kMassPeak: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + case kMassLSB: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + case kMassRSB: + fillResoRegion(histReg, trigger, associate, posDauTrack, negDauTrack, invMass, posDauIdMethod, negDauIdMethod); + break; + default: + continue; + } + + incrementCorrCount(corrCountPerColl); + incrementResoRegionCount(resoRegionCountsPerColl, massRegion); + markResoUsed(associate, static_cast(getResoRegionMaskBit(pairType - kHPhi, PtType, massRegion) >> 18)); + } + } // associate particle loop is over + } // function definition is over + + template + void executeCorrelation(H& histReg, const T& triggers, const A& associatesLowPt, const B& associatesHighPt, CorrCountArray& corrCountPerColl, ResoRegionCountArray& resoRegionCountsPerColl) + { + // Trigger Selection + // int nTrigger = 0; + int triggerSelTag = kTrackAccepted; + + // Option 1 : Trigger tracks wont be accepted as daughter track of reconstructed resonance. + // Option 2 : + std::vector triggerGIList; + std::vector posDauGIListPeak; + std::vector negDauGIListPeak; + std::vector posDauGIListLSB; + std::vector negDauGIListLSB; + std::vector posDauGIListRSSB; + std::vector negDauGIListRSSB; + + if (c1113rejectResoWithAnyTriggerDaughter) { + for (const auto& trigger : triggers) { + if (selectionTrack(trigger) != kTrackAccepted) { + continue; + } + triggerGIList.push_back(trigger.globalIndex()); + } + } + + for (const auto& trigger : triggers) { + triggerSelTag = selectionTrack(trigger); + if (triggerSelTag != kTrackAccepted) { + continue; + } + // fillTrigQA + fillCorrParticleQA(histReg, trigger); + markTrackUsed(trigger, TriggerUsedFlag); + // ++nTrigger; + + // triggerTrackIndexList.push_back(triggerTrack.globalIndex()); + executeTrigAssocCorrelation(histReg, trigger, associatesLowPt, triggerGIList, corrCountPerColl, resoRegionCountsPerColl); + executeTrigAssocCorrelation(histReg, trigger, associatesHighPt, triggerGIList, corrCountPerColl, resoRegionCountsPerColl); + } // trigger particle loop + } // execute Correlation Function is over + + // Event Filter + Filter eventFilter = (!cfgEvent.c1003requireSel8) || (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cfgEvent.c1002cutZvertex); + + // Track Filter + Filter ptFilter = (o2::aod::track::pt > cfgTrackCuts.c1043ptMin) && (o2::aod::track::pt < cfgTrackCuts.c1044ptMax); + Filter etaFilter = (nabs(o2::aod::track::eta) < cfgTrackCuts.c1045etaMax); + Filter dcaFilter = ((!cfgTrackCuts.c1065useFixedDCAxy) || (nabs(o2::aod::track::dcaXY) < cfgTrackCuts.c1066dcaXYMax)) && + ((!cfgTrackCuts.c1067useFixedDCAz) || (nabs(o2::aod::track::dcaZ) < cfgTrackCuts.c1068dcaZMax)); + using MyFilteredCollisions = soa::Filtered>; // , + // using MyFilteredTracks = soa::Filtered>; + using MyFilteredTracks = soa::Filtered>; + // For manual sliceBy + Preslice tracksPerCollisionPreslice = o2::aod::track::collisionId; + Preslice resoPerCollisionPreslice = aod::resonancecndt::collisionId; // registers RESONCNDTS/fIndexCollisions in the SliceCache + + // // definition of partitions + SliceCache cache; + Partition triggerTracks = cfgPartitions.c1077triggerPtLow < aod::track::pt && aod::track::pt < cfgPartitions.c1078triggerPtHigh; + Partition posTracks = aod::track::signed1Pt > 0.0f; + Partition negTracks = aod::track::signed1Pt < 0.0f; + + Partition assocTracksLowPt = cfgPartitions.c1079assocPtLowMin < aod::track::pt && aod::track::pt < cfgPartitions.c1080assocPtLowMax; + Partition assocTracksHighPt = cfgPartitions.c1081assocPtHighMin < aod::track::pt && aod::track::pt < cfgPartitions.c1082assocPtHighMax; + + Partition assocPhiLowPt = (cfgResPartitions.c1085phiPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1086phiPtLowMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + Partition assocPhiHighPt = (cfgResPartitions.c1087phiPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1088phiPtHighMax) && (aod::resonancecndt::phi1020Tag != static_cast(kMassOutside)); + + Partition assocKStarLowPt = (cfgResPartitions.c1089kstarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1090kstarPtLowMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + Partition assocKStarHighPt = (cfgResPartitions.c1091kstarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1092kstarPtHighMax) && (aod::resonancecndt::kStar892Tag != static_cast(kMassOutside)); + + Partition assocKStarBarLowPt = (cfgResPartitions.c1093kstarBarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1094kstarBarPtLowMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + Partition assocKStarBarHighPt = (cfgResPartitions.c1095kstarBarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1096kstarBarPtHighMax) && (aod::resonancecndt::kStar892BarTag != static_cast(kMassOutside)); + + Partition assocLambda1520LowPt = (cfgResPartitions.c1097lambda1520PtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1098lambda1520PtLowMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + Partition assocLambda1520HighPt = (cfgResPartitions.c1099lambda1520PtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1100lambda1520PtHighMax) && (aod::resonancecndt::lambda1520Tag != static_cast(kMassOutside)); + + Partition assocLambda1520BarLowPt = (cfgResPartitions.c1101lambda1520BarPtLowMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1102lambda1520BarPtLowMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + Partition assocLambda1520BarHighPt = (cfgResPartitions.c1103lambda1520BarPtHighMin < aod::resonancecndt::pt) && (aod::resonancecndt::pt < cfgResPartitions.c1104lambda1520BarPtHighMax) && (aod::resonancecndt::lambda1520BarTag != static_cast(kMassOutside)); + + int dfNumber = 0; + int64_t iCollGlobalCount = -1; + + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.c1001printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. + // Keeps the task alive when running purely on derived data. + } + PROCESS_SWITCH(HParticleCorrelation2SameEvent, processNothing, "Dummy process for derived-data analysis", true); + + void processSameEvent(MyFilteredCollisions const& collisions, MyFilteredTracks const& fullTracks, aod::ResonanceCndts const& resonanceCndts, o2::aod::Origins const& Origins, aod::BCsWithTimestamps const&) + { + dfNumber++; + mixingQA.fill(HIST("Mixing/DataFramesProcessed"), 0.0); + std::vector mixingBinStatusPerDF(nMixBins); + uint64_t dataframeID = 0; + + if (cfgDerivedData.c1136resetGlobalCountersPerDF) { + iCollGlobalCount = -1; + } + + if (Origins.size() == 0) { + dataframeID = std::numeric_limits::max() - static_cast(dfNumber); + LOG(warn) << "Origins table is empty. Using synthetic dataframeID = " << dataframeID; + } else { + dataframeID = Origins.iteratorAt(0).dataframeID(); + } + + if (cfgDebug.c1001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: Origins = " << Origins.size() << " :: dataframeID = " << dataframeID; + } + + assocPhiLowPt.bindExternalIndices(&fullTracks); + assocPhiHighPt.bindExternalIndices(&fullTracks); + assocKStarLowPt.bindExternalIndices(&fullTracks); + assocKStarHighPt.bindExternalIndices(&fullTracks); + assocKStarBarLowPt.bindExternalIndices(&fullTracks); + assocKStarBarHighPt.bindExternalIndices(&fullTracks); + assocLambda1520LowPt.bindExternalIndices(&fullTracks); + assocLambda1520HighPt.bindExternalIndices(&fullTracks); + assocLambda1520BarLowPt.bindExternalIndices(&fullTracks); + assocLambda1520BarHighPt.bindExternalIndices(&fullTracks); + + if (collisions.size() == 0) { + seEventQA.fill(HIST("SE/Events/DataFrameQA"), kDFWithZeroFilteredColls); + + if (cfgDebug.c1001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = 0 :: No filtered collisions found in this dataframe"; + } + } else if (cfgDebug.c1001printDebugMessages) { + auto bc = collisions.iteratorAt(0).bc_as(); + int currentRunNumber = bc.runNumber(); + + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: currentRunNumber = " << currentRunNumber << " :: " << collisions.iteratorAt(0).bc_as().runNumber(); + LOG(info) << "DEBUG :: df_" << dfNumber << " :: SE :: collisions = " << collisions.size() << " :: fullTracks = " << fullTracks.size() << " :: resonanceCndts = " << resonanceCndts.size(); + } + + int nTrack = 0; + CollisionRejectionTag collisionTag = kCollAccepted; + TrackRejectionTag trackTag = kTrackAccepted; + + for (const auto& collision : collisions) { // CollisionLoop-Start + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventAllFiltered)); + fillEventQA(collision); + + collisionTag = selCollision(collision); + seEventQA.fill(HIST("SE/Events/CollisionRejection"), static_cast(collisionTag)); + if (collisionTag != kCollAccepted) { + continue; + } + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedSelCollision)); + fillEventQA(collision); + const auto tracksPerCollision = fullTracks.sliceBy(tracksPerCollisionPreslice, collision.globalIndex()); + if (tracksPerCollision.size() < cfgEvent.c1005minNFilteredTracks) { + continue; + } + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedMinFilteredTracks)); + + nTrack = 0; + for (const auto& track : tracksPerCollision) { + seTrackQA.fill(HIST("SE/Tracks/TrackSelection"), static_cast(kTrackAllFiltered)); + fillFullTrackQA(track); + + trackTag = selectionTrack(track); + seTrackQA.fill(HIST("SE/Tracks/TrackRejection"), static_cast(trackTag)); + + if (trackTag != kTrackAccepted) { + continue; + } + + seTrackQA.fill(HIST("SE/Tracks/TrackSelection"), static_cast(kTrackPassedSelectionTrack)); + fillFullTrackQA(track); + nTrack++; + } // track loop is over. + + if (nTrack < cfgEvent.c1006minNSelectedTracks) { + continue; + } + + const auto resonancesPerCollision = resonanceCndts.sliceBy(resoPerCollisionPreslice, collision.globalIndex()); + trackUsedFlags.clear(); + resoUsedFlags.clear(); + if (tracksPerCollision.size() > 0) { + firstTrackGI = tracksPerCollision.begin().globalIndex(); + trackUsedFlags.assign(tracksPerCollision.iteratorAt(tracksPerCollision.size() - 1).globalIndex() - firstTrackGI + 1, 0); + } + if (resonancesPerCollision.size() > 0) { + firstResoGI = resonancesPerCollision.begin().globalIndex(); + resoUsedFlags.assign(resonancesPerCollision.iteratorAt(resonancesPerCollision.size() - 1).globalIndex() - firstResoGI + 1, 0); + } + + fillEventQA(collision); + CorrCountArray corrCountsPerColl{}; + ResoRegionCountArray resoRegionCountsPerColl{}; + + seEventQA.fill(HIST("SE/Events/EventSelection"), static_cast(kEventPassedMinSelectedTracks)); + + // Get the Partitions + auto triggerTracksPerColl = triggerTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + auto assocTrksPerCollLowPt = assocTracksLowPt->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto assocTrksPerCollHighPt = assocTracksHighPt->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + auto assocPhiPerCollLowPt = assocPhiLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocPhiPerCollHighPt = assocPhiHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocKStarPerCollLowPt = assocKStarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocKStarPerCollHighPt = assocKStarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocKStarBarPerCollLowPt = assocKStarBarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocKStarBarPerCollHighPt = assocKStarBarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocLambdaPerCollLowPt = assocLambda1520LowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocLambdaPerCollHighPt = assocLambda1520HighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + auto assocLambdaBarPerCollLowPt = assocLambda1520BarLowPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + auto assocLambdaBarPerCollHighPt = assocLambda1520BarHighPt->sliceByCached(aod::resonancecndt::collisionId, collision.globalIndex(), cache); + + currentCentrality = getMixingEstimatorValue(collision); + executeCorrelation(hhCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hIdCorrelation, triggerTracksPerColl, assocTrksPerCollLowPt, assocTrksPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hPhiCorrelation, triggerTracksPerColl, assocPhiPerCollLowPt, assocPhiPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hKStarCorrelation, triggerTracksPerColl, assocKStarPerCollLowPt, assocKStarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hKStarBarCorrelation, triggerTracksPerColl, assocKStarBarPerCollLowPt, assocKStarBarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hLambdaCorrelation, triggerTracksPerColl, assocLambdaPerCollLowPt, assocLambdaPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + executeCorrelation(hLambdaBarCorrelation, triggerTracksPerColl, assocLambdaBarPerCollLowPt, assocLambdaBarPerCollHighPt, corrCountsPerColl, resoRegionCountsPerColl); + + // Unlike Sign Signal + // LikeSign Signal + bool hasSelectedTrigger = false; + if (c1114requireSelectedTriggerForInvariantMass) { + for (const auto& trigger : triggerTracksPerColl) { + if (selectionTrack(trigger) == kTrackAccepted) { + hasSelectedTrigger = true; + break; + } + } + } + + if (!c1114requireSelectedTriggerForInvariantMass || hasSelectedTrigger) { + auto posTracksPerColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negTracksPerColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillUnlikeSignInvariantMass(collision, posTracksPerColl, negTracksPerColl); + fillLikeSignInvariantMass(collision, posTracksPerColl); + fillLikeSignInvariantMass(collision, negTracksPerColl); + } + + //______________________________________________________________________________ + // Event-mixing information for this collision + uint64_t corrPresenceMask = 0ULL; + const int mixingBin = getMixingBin(collision); + const float mixingEstimatorValue = getMixingEstimatorValue(collision); + const bool validMixingBin = mixingBin >= 0 && mixingBin < nMixBins; + + if (validMixingBin) { + ++mixingBinStatusPerDF[mixingBin].nCollisions; + mixingQA.fill(HIST("Mixing/AllCollisionsPerBin"), mixingBin); + mixingQA.fill(HIST("Mixing/AllCollisionsZCent"), collision.posZ(), mixingEstimatorValue); + } + + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + if (corrCountsPerColl[iCorr][iPt] == 0) { + continue; + } + + const int corrChannel = iCorr * kNCorrCountPt + iPt; + corrPresenceMask |= getCorrMaskBit(iCorr, iPt); + + if (validMixingBin) { + ++mixingBinStatusPerDF[mixingBin].nCorrCollisions[iCorr][iPt]; + mixingQA.fill(HIST("Mixing/EligibleCollisionsPerBin"), mixingBin, corrChannel); + mixingQA.fill(HIST("Mixing/EligibleCollisionsZCentChannel"), collision.posZ(), mixingEstimatorValue, corrChannel); + } + } + } + + for (int iReso = 0; iReso < kNResoCorrCountTypes; ++iReso) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + for (int iMassRegion = kMassPeak; iMassRegion <= kMassRSB; ++iMassRegion) { + if (resoRegionCountsPerColl[iReso][iPt][iMassRegion] == 0) { + continue; + } + corrPresenceMask |= getResoRegionMaskBit(iReso, iPt, iMassRegion); + } + } + } + + int nTriggersWritten = 0; + int nTrackAssocsWritten = 0; + int nResoAssocsWritten = 0; + //______________________________________________________________________________ + // Derived tables: only the triggers and associates marked as used, and only for the stored pools. + // Every particle is written once; its UsedMask holds all correlations it was used in. + const uint64_t storedMask = getStoredCorrMask(corrPresenceMask, mixingBin); + if (storedMask != 0ULL) { + derivedCollisions(collision.posZ(), mixingEstimatorValue, storedMask); + iCollGlobalCount++; + + // Tracks: triggers, and h / pi / K / p associates (track pools = CorrPresenceMask bits 0-7) + for (const auto& track : tracksPerCollision) { + const int64_t i = track.globalIndex() - firstTrackGI; + if (i < 0 || i >= static_cast(trackUsedFlags.size()) || trackUsedFlags[i] == 0) { + continue; + } + const uint16_t flags = trackUsedFlags[i]; + if ((flags & TriggerUsedFlag) != 0u) { + nTriggersWritten++; + derivedTriggers(iCollGlobalCount, track.pt(), track.eta(), track.phi()); + } + const uint64_t usedMask = static_cast(flags & 0xFFu) & storedMask; + if (usedMask != 0ULL) { + nTrackAssocsWritten++; + derivedAssocs(iCollGlobalCount, track.pt(), track.eta(), track.phi(), static_cast(track.sign() > 0 ? 1 : -1), usedMask); + } + } + + // Resonance candidates (resonance pools = CorrPresenceMask bits 18-47; flag bit r = pool bit 18 + r) + for (const auto& resonance : resonancesPerCollision) { + const int64_t i = resonance.globalIndex() - firstResoGI; + if (i < 0 || i >= static_cast(resoUsedFlags.size()) || resoUsedFlags[i] == 0) { + continue; + } + const uint64_t usedMask = (static_cast(resoUsedFlags[i]) << 18) & storedMask; + if (usedMask != 0ULL) { + nResoAssocsWritten++; + derivedAssocs(iCollGlobalCount, resonance.pt(), resonance.eta(), resonance.phi(), static_cast(0), usedMask); + } + } + + mixingQA.fill(HIST("DerivedData/RowsWritten"), 0.0); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 1.0, nTriggersWritten); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 2.0, nTrackAssocsWritten); + mixingQA.fill(HIST("DerivedData/RowsWritten"), 3.0, nResoAssocsWritten); + } // Derived data creation block is over. + } // collision loop - Over. + + //______________________________________________________________________________ + // Dataframe-level mixing-pool QA + std::array nReadyMixBins{}; + + for (int iMixBin = 0; iMixBin < nMixBins; ++iMixBin) { + const auto& binStatus = mixingBinStatusPerDF[iMixBin]; + mixingQA.fill(HIST("Mixing/PerDF/AllPoolOccupancy"), iMixBin, binStatus.nCollisions); + + for (int iCorr = 0; iCorr < kNCorrCountTypes; ++iCorr) { + for (int iPt = 0; iPt < kNCorrCountPt; ++iPt) { + const int corrChannel = iCorr * kNCorrCountPt + iPt; + const uint64_t nEligibleCollisions = binStatus.nCorrCollisions[iCorr][iPt]; + + mixingQA.fill(HIST("Mixing/PerDF/EligiblePoolOccupancy"), iMixBin, corrChannel, nEligibleCollisions); + mixingQA.fill(HIST("Mixing/PerDF/PoolOccupancyDistribution"), corrChannel, nEligibleCollisions); + + if (nEligibleCollisions >= static_cast(cfgMixing.c1137nEvtMixing)) { + ++nReadyMixBins[corrChannel]; + mixingQA.fill(HIST("Mixing/PerDF/ReadyPoolMap"), iMixBin, corrChannel); + } + } + } + } + + for (int corrChannel = 0; corrChannel < NCorrCountChannels; ++corrChannel) { + mixingQA.fill(HIST("Mixing/PerDF/NReadyMixBins"), corrChannel, nReadyMixBins[corrChannel]); + } + } + PROCESS_SWITCH(HParticleCorrelation2SameEvent, processSameEvent, "Process Same event", true); +}; + +struct HParticleCorrelation3MixedEvent { + + HistogramRegistry hhCorrelation{"hhCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hIdCorrelation{"hIdCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hPhiCorrelation{"hPhiCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarCorrelation{"hKStarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hKStarBarCorrelation{"hKStarBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaCorrelation{"hLambdaCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hLambdaBarCorrelation{"hLambdaBarCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry mixingOperationQA{"mixingOperationQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // Mixing channels: 8 track channels (h-h, h-pi, h-K, h-p x low/high pT) followed by + // 30 resonance channels (5 species x low/high pT x Peak/LSB/RSB) + static constexpr int NMassWindows = 3; + static constexpr int NTrackMixChannels = static_cast(kCountHPr + 1) * static_cast(kNCorrCountPt); + static constexpr int NMixOperationChannels = NTrackMixChannels + static_cast(kNResoCorrCountTypes) * static_cast(kNCorrCountPt) * NMassWindows; + + static constexpr std::array MixOperationChannelName = { + "hh_LowPt", "hh_HighPt", "hPi_LowPt", "hPi_HighPt", "hKa_LowPt", "hKa_HighPt", "hPr_LowPt", "hPr_HighPt", + "hPhi_LowPt_Peak", "hPhi_LowPt_LSB", "hPhi_LowPt_RSB", "hPhi_HighPt_Peak", "hPhi_HighPt_LSB", "hPhi_HighPt_RSB", + "hKStar_LowPt_Peak", "hKStar_LowPt_LSB", "hKStar_LowPt_RSB", "hKStar_HighPt_Peak", "hKStar_HighPt_LSB", "hKStar_HighPt_RSB", + "hKStarBar_LowPt_Peak", "hKStarBar_LowPt_LSB", "hKStarBar_LowPt_RSB", "hKStarBar_HighPt_Peak", "hKStarBar_HighPt_LSB", "hKStarBar_HighPt_RSB", + "hLambda_LowPt_Peak", "hLambda_LowPt_LSB", "hLambda_LowPt_RSB", "hLambda_HighPt_Peak", "hLambda_HighPt_LSB", "hLambda_HighPt_RSB", + "hLambdaBar_LowPt_Peak", "hLambdaBar_LowPt_LSB", "hLambdaBar_LowPt_RSB", "hLambdaBar_HighPt_Peak", "hLambdaBar_HighPt_LSB", "hLambdaBar_HighPt_RSB"}; + + struct : ConfigurableGroup { + Configurable c2001printDebugMessages{"c2001printDebugMessages", false, "Print debug messages"}; + } cfgDebug; + + struct : ConfigurableGroup { + ConfigurableAxis c2002axisDeltaPhi{"c2002axisDeltaPhi", {80, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c2003axisDeltaEta{"c2003axisDeltaEta", {84, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c2004axisCorrSparseTriggerPt{"c2004axisCorrSparseTriggerPt", {8, 4.0f, 8.0f}, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + ConfigurableAxis c2005axisCorrSparseAssocPt{"c2005axisCorrSparseAssocPt", {8, 0.0f, 4.0f}, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + ConfigurableAxis c2006axisCorrSparseDeltaPhi{"c2006axisCorrSparseDeltaPhi", {32, -2.0f, 6.0f}, "#Delta#varphi"}; + ConfigurableAxis c2007axisCorrSparseDeltaEta{"c2007axisCorrSparseDeltaEta", {24, -2.1f, 2.1f}, "#Delta#eta"}; + ConfigurableAxis c2008axisCorrCentrality{"c2008axisCorrCentrality", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "Centrality (%) axis of the correlation histograms (estimator chosen by c1138mixingEstimator)"}; + } cfgAxis; + + // Selections (track cuts, PID, pT classes, mass windows, rapidity, estimator) are applied in the same-event + // task and stored as tags. Only the mixing itself is configured here; keep these axes equal to c1139/c1140. + struct : ConfigurableGroup { + Configurable c2009nEvtMixing{"c2009nEvtMixing", 5, "Number of partner events mixed with each trigger event"}; + ConfigurableAxis c2010axisVtxMixing{"c2010axisVtxMixing", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins - z vertex"}; + ConfigurableAxis c2011axisCentMixing{"c2011axisCentMixing", {VARIABLE_WIDTH, -1.0, 20.0, 50.0, 80.0, 101.0}, "Mixing bins - centrality (estimator chosen by c1138mixingEstimator in the same-event task)"}; + } cfgMixing; + + // Re-built in init() with the configured axes (as a data member it holds the default axes) + using BinningTypeMixing = ColumnBinningPolicy; + BinningTypeMixing colBinning{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + int nMixBins = 0; + + void init(InitContext const&) + { + if (cfgDebug.c2001printDebugMessages) { + LOGF(info, "Starting init"); + } + + const AxisSpec axisPt = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisEta = {100, -5.0f, 5.0f, "#eta"}; + const AxisSpec axisPhi = {110, -1.0f, 10.0f, "#phi (radians)"}; + const AxisSpec axisSign = {3, -1.5f, 1.5f, "Sign (tracks: charge, resonances: 0)"}; + + AxisSpec axisDeltaPhiSpec{cfgAxis.c2002axisDeltaPhi, "#Delta#varphi"}; + AxisSpec axisDeltaEtaSpec{cfgAxis.c2003axisDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrSparseTriggerPt{cfgAxis.c2004axisCorrSparseTriggerPt, "#it{p}_{T}^{trig} (GeV/#it{c})"}; + AxisSpec axisCorrSparseAssocPt{cfgAxis.c2005axisCorrSparseAssocPt, "#it{p}_{T}^{assoc} (GeV/#it{c})"}; + AxisSpec axisCorrSparseDeltaPhi{cfgAxis.c2006axisCorrSparseDeltaPhi, "#Delta#varphi"}; + AxisSpec axisCorrSparseDeltaEta{cfgAxis.c2007axisCorrSparseDeltaEta, "#Delta#eta"}; + AxisSpec axisCorrCentrality{cfgAxis.c2008axisCorrCentrality, "Centrality (%)"}; + + auto addAssocQAHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "Pt").c_str(), "Associate;p_{T} (GeV/c);Counts", HistType::kTH1F, {axisPt}); + histReg.add((basePath + "Eta").c_str(), "Associate;#eta;Counts", HistType::kTH1F, {axisEta}); + histReg.add((basePath + "Phi").c_str(), "Associate;#varphi;Counts", HistType::kTH1F, {axisPhi}); + histReg.add((basePath + "Sign").c_str(), "Associate;Sign;Counts", HistType::kTH1F, {axisSign}); + }; + + auto addCorrHistos = [&](auto& histReg, const std::string& basePath) { + histReg.add((basePath + "DeltaPhi").c_str(), "Trigger-associate;#Delta#varphi;Pairs", HistType::kTH1F, {axisDeltaPhiSpec}); + histReg.add((basePath + "DeltaEta").c_str(), "Trigger-associate;#Delta#eta;Pairs", HistType::kTH1F, {axisDeltaEtaSpec}); + histReg.add((basePath + "DeltaPhiDeltaEta").c_str(), "Trigger-associate;#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTH3F, {axisDeltaPhiSpec, axisDeltaEtaSpec, axisCorrCentrality}); + histReg.add((basePath + "CorrSparse").c_str(), "Correlation sparse;p_{T}^{trig};p_{T}^{assoc};#Delta#varphi;#Delta#eta;Centrality (%)", HistType::kTHnSparseF, {axisCorrSparseTriggerPt, axisCorrSparseAssocPt, axisCorrSparseDeltaPhi, axisCorrSparseDeltaEta, axisCorrCentrality}); + }; + + // Histogram paths mirror the same-event task (SE/... -> ME/...) + // h-h + addAssocQAHistos(hhCorrelation, "ME/hh/QA/AssocLowPt/"); + hhCorrelation.addClone("ME/hh/QA/AssocLowPt/", "ME/hh/QA/AssocHighPt/"); + addCorrHistos(hhCorrelation, "ME/hh/Corr/AssocLowPt/"); + hhCorrelation.addClone("ME/hh/Corr/AssocLowPt/", "ME/hh/Corr/AssocHighPt/"); + + // h-identified + addAssocQAHistos(hIdCorrelation, "ME/hIdentified/QA/AssocLowPt/Pi/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/Pi/", "ME/hIdentified/QA/AssocLowPt/Ka/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/Pi/", "ME/hIdentified/QA/AssocLowPt/Pr/"); + hIdCorrelation.addClone("ME/hIdentified/QA/AssocLowPt/", "ME/hIdentified/QA/AssocHighPt/"); + addCorrHistos(hIdCorrelation, "ME/hIdentified/Corr/AssocLowPt/Pi/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/Pi/", "ME/hIdentified/Corr/AssocLowPt/Ka/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/Pi/", "ME/hIdentified/Corr/AssocLowPt/Pr/"); + hIdCorrelation.addClone("ME/hIdentified/Corr/AssocLowPt/", "ME/hIdentified/Corr/AssocHighPt/"); + + // h-resonance + auto registerResonanceHistograms = [&](auto& histReg, const std::string& pairName) { + const std::string base = "ME/" + pairName + "/"; + addAssocQAHistos(histReg, base + "QA/AssocLowPt/Peak/Reso/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/LSB/"); + histReg.addClone(base + "QA/AssocLowPt/Peak/", base + "QA/AssocLowPt/RSB/"); + histReg.addClone(base + "QA/AssocLowPt/", base + "QA/AssocHighPt/"); + addCorrHistos(histReg, base + "Corr/AssocLowPt/Peak/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/LSB/"); + histReg.addClone(base + "Corr/AssocLowPt/Peak/", base + "Corr/AssocLowPt/RSB/"); + histReg.addClone(base + "Corr/AssocLowPt/", base + "Corr/AssocHighPt/"); + }; + registerResonanceHistograms(hPhiCorrelation, "hPhi"); + registerResonanceHistograms(hKStarCorrelation, "hKStar"); + registerResonanceHistograms(hKStarBarCorrelation, "hKStarBar"); + registerResonanceHistograms(hLambdaCorrelation, "hLambda"); + registerResonanceHistograms(hLambdaBarCorrelation, "hLambdaBar"); + + //______________________________________________________________________________ + // Mixing binning (re-built here with the configured axes) and mixing QA + AxisSpec axisVtxMix{cfgMixing.c2010axisVtxMixing, "z_{vtx} (cm)"}; + AxisSpec axisCentMix{cfgMixing.c2011axisCentMixing, "Centrality (%)"}; + nMixBins = axisVtxMix.getNbins() * axisCentMix.getNbins(); + colBinning = BinningTypeMixing{{cfgMixing.c2010axisVtxMixing, cfgMixing.c2011axisCentMixing}, true}; + + const AxisSpec axisMixBin = {nMixBins, -0.5, static_cast(nMixBins) - 0.5, "Mixing bin"}; + const AxisSpec axisMixChannel = {NMixOperationChannels, -0.5, static_cast(NMixOperationChannels) - 0.5, "Mixing channel"}; + const AxisSpec axisMixPartners = {static_cast(cfgMixing.c2009nEvtMixing) + 1, -0.5, static_cast(cfgMixing.c2009nEvtMixing) + 0.5, "N mixed partner events"}; + const AxisSpec axisCollPerDF = {200, 0.0, 20000.0, "Derived collisions per dataframe"}; + const AxisSpec axisMixVtxZQA = {100, -10.0f, 10.0f, "z_{vtx} (cm)"}; + const AxisSpec axisMixDeltaVtxZQA = {100, -20.0f, 20.0f, "#Delta z_{vtx} (cm)"}; + const AxisSpec axisMixCentralityQA = {102, -1.0f, 101.0f, "Centrality (%)"}; + const AxisSpec axisMixDeltaCentralityQA = {204, -102.0f, 102.0f, "#Delta Centrality (%)"}; + + mixingOperationQA.add("ME/Mixing/DataFramesProcessed", "Processed dataframes;Dataframe;Count", HistType::kTH1D, {{1, -0.5, 0.5}}); + mixingOperationQA.add("ME/Mixing/CollisionsPerDF", "Derived collisions per (merged) dataframe;N collisions;Dataframes", HistType::kTH1D, {axisCollPerDF}); + mixingOperationQA.add("ME/Mixing/CollisionsPerBinChannel", "Collisions in mixing pools;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixBin, axisMixChannel}); + mixingOperationQA.add("ME/Mixing/CollisionPairsPerChannel", "Mixed collision pairs;Mixing channel;Mixed collision pairs", HistType::kTH1D, {axisMixChannel}); + mixingOperationQA.add("ME/Mixing/CollisionPairsPerBinChannel", "Mixed collision pairs;Mixing bin;Mixing channel", HistType::kTH2D, {axisMixBin, axisMixChannel}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEvent", "Mixed partners per trigger event;Mixing channel;N partner events", HistType::kTH2D, {axisMixChannel, axisMixPartners}); + mixingOperationQA.add("ME/Mixing/PartnersPerTriggerEventVsBin", "Mixed partners per trigger event;Mixing bin;Mixing channel;N partner events", HistType::kTH3D, {axisMixBin, axisMixChannel, axisMixPartners}); + mixingOperationQA.add("ME/Mixing/VtxZ1VsVtxZ2", "Mixed-event vertex positions;z_{vtx}^{trig} (cm);z_{vtx}^{assoc} (cm)", HistType::kTH2F, {axisMixVtxZQA, axisMixVtxZQA}); + mixingOperationQA.add("ME/Mixing/DeltaVtxZVsChannel", "Mixed-event #Delta z_{vtx};Mixing channel;z_{vtx}^{trig}-z_{vtx}^{assoc} (cm)", HistType::kTH2F, {axisMixChannel, axisMixDeltaVtxZQA}); + mixingOperationQA.add("ME/Mixing/Centrality1VsCentrality2", "Mixing estimator of mixed events;Estimator^{trig} (%);Estimator^{assoc} (%)", HistType::kTH2F, {axisMixCentralityQA, axisMixCentralityQA}); + mixingOperationQA.add("ME/Mixing/DeltaCentralityVsChannel", "Mixing-estimator difference;Mixing channel;Estimator^{trig}-Estimator^{assoc} (%)", HistType::kTH2F, {axisMixChannel, axisMixDeltaCentralityQA}); + + auto setMixOperationChannelLabels = [](TAxis* axis) { + for (int iChannel = 0; iChannel < NMixOperationChannels; ++iChannel) { + axis->SetBinLabel(iChannel + 1, MixOperationChannelName[iChannel].data()); + } + }; + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionsPerBinChannel"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/CollisionPairsPerBinChannel"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEvent"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"))->GetYaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaVtxZVsChannel"))->GetXaxis()); + setMixOperationChannelLabels(mixingOperationQA.get(HIST("ME/Mixing/DeltaCentralityVsChannel"))->GetXaxis()); + + if (cfgDebug.c2001printDebugMessages) { + LOGF(info, "Finishing init"); + } + } + + SliceCache cache; + Preslice triggersPerCollPreslice = aod::hpcorrtrigger::hpCorrCollId; + Preslice assocsPerCollPreslice = aod::hpcorrassoc::hpCorrCollId; // registers HPCORRASSOC/fIndexHPCorrColls in the SliceCache + + //______________________________________________________________________________ + // Associate partitions: one per mixing channel. Every stored particle carries UsedMask = all correlations + // h-h, h-pi, h-K, h-p + Partition assocHHLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHHLowPt)); + Partition assocHHHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHHHighPt)); + Partition assocHPiLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPiLowPt)); + Partition assocHPiHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPiHighPt)); + Partition assocHKaLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKaLowPt)); + Partition assocHKaHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKaHighPt)); + Partition assocHPrLowPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPrLowPt)); + Partition assocHPrHighPt = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPrHighPt)); + + // h-Phi + Partition assocHPhiLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtPeak)); + Partition assocHPhiLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtLSB)); + Partition assocHPhiLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiLowPtRSB)); + Partition assocHPhiHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtPeak)); + Partition assocHPhiHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtLSB)); + Partition assocHPhiHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHPhiHighPtRSB)); + + // h-KStar + Partition assocHKStarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtPeak)); + Partition assocHKStarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtLSB)); + Partition assocHKStarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarLowPtRSB)); + Partition assocHKStarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtPeak)); + Partition assocHKStarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtLSB)); + Partition assocHKStarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarHighPtRSB)); + + // h-KStarBar + Partition assocHKStarBarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtPeak)); + Partition assocHKStarBarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtLSB)); + Partition assocHKStarBarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarLowPtRSB)); + Partition assocHKStarBarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtPeak)); + Partition assocHKStarBarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtLSB)); + Partition assocHKStarBarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHKStarBarHighPtRSB)); + + // h-Lambda + Partition assocHLambdaLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtPeak)); + Partition assocHLambdaLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtLSB)); + Partition assocHLambdaLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaLowPtRSB)); + Partition assocHLambdaHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtPeak)); + Partition assocHLambdaHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtLSB)); + Partition assocHLambdaHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaHighPtRSB)); + + // h-LambdaBar + Partition assocHLambdaBarLowPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtPeak)); + Partition assocHLambdaBarLowPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtLSB)); + Partition assocHLambdaBarLowPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarLowPtRSB)); + Partition assocHLambdaBarHighPtPeak = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtPeak)); + Partition assocHLambdaBarHighPtLSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtLSB)); + Partition assocHLambdaBarHighPtRSB = ncheckbit(aod::hpcorrassoc::usedMask, static_cast(kMaskHLambdaBarHighPtRSB)); + + //______________________________________________________________________________ + // Collision pools: one per mixing channel. A collision is in the pool of a channel if its CorrMask has the + // channel bit, i.e. it had at least one trigger-associate pair of this channel in the same event. + // h-h, h-pi, h-K, h-p + Partition collHHLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHHLowPt)); + Partition collHHHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHHHighPt)); + Partition collHPiLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPiLowPt)); + Partition collHPiHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPiHighPt)); + Partition collHKaLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKaLowPt)); + Partition collHKaHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKaHighPt)); + Partition collHPrLowPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPrLowPt)); + Partition collHPrHighPt = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPrHighPt)); + + // h-Phi + Partition collHPhiLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtPeak)); + Partition collHPhiLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtLSB)); + Partition collHPhiLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiLowPtRSB)); + Partition collHPhiHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtPeak)); + Partition collHPhiHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtLSB)); + Partition collHPhiHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHPhiHighPtRSB)); + + // h-KStar + Partition collHKStarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtPeak)); + Partition collHKStarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtLSB)); + Partition collHKStarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarLowPtRSB)); + Partition collHKStarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtPeak)); + Partition collHKStarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtLSB)); + Partition collHKStarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarHighPtRSB)); + + // h-KStarBar + Partition collHKStarBarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtPeak)); + Partition collHKStarBarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtLSB)); + Partition collHKStarBarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarLowPtRSB)); + Partition collHKStarBarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtPeak)); + Partition collHKStarBarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtLSB)); + Partition collHKStarBarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHKStarBarHighPtRSB)); + + // h-Lambda + Partition collHLambdaLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtPeak)); + Partition collHLambdaLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtLSB)); + Partition collHLambdaLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaLowPtRSB)); + Partition collHLambdaHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtPeak)); + Partition collHLambdaHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtLSB)); + Partition collHLambdaHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaHighPtRSB)); + + // h-LambdaBar + Partition collHLambdaBarLowPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtPeak)); + Partition collHLambdaBarLowPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtLSB)); + Partition collHLambdaBarLowPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarLowPtRSB)); + Partition collHLambdaBarHighPtPeak = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtPeak)); + Partition collHLambdaBarHighPtLSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtLSB)); + Partition collHLambdaBarHighPtRSB = ncheckbit(aod::hpcorrcollision::corrMask, static_cast(kMaskHLambdaBarHighPtRSB)); + + // Number of mixed partners of every collision as trigger event (QA only, re-used by all channels) + std::vector nPartnersPerColl; + + //______________________________________________________________________________ + // Channel bookkeeping: PairTypeEnum (+ PidEnum for identified hadrons) -> CorrCountTypeEnum, + // which gives the CorrPresenceMask bit and the mixing-QA channel number + template + static constexpr int getCorrType() + { + if constexpr (pairType == kHH) { + return kCountHH; + } else if constexpr (pairType == kHId) { + return pidType == kPi ? kCountHPi : (pidType == kKa ? kCountHKa : kCountHPr); + } else if constexpr (pairType == kHPhi) { + return kCountHPhi; + } else if constexpr (pairType == kHKStar) { + return kCountHKStar; + } else if constexpr (pairType == kHKStarBar) { + return kCountHKStarBar; + } else if constexpr (pairType == kHLambda) { + return kCountHLambda; + } else { + return kCountHLambdaBar; + } + } + + //______________________________________________________________________________ + // Histogram filling for one mixed trigger-associate pair + template + void fillMixedPair(H& histReg, const T& trigger, const A& associate, const float centrality) + { + const float dPhi = computeDeltaPhi(associate.phi(), trigger.phi()); + const float dEta = associate.eta() - trigger.eta(); + + if constexpr (pairType == kHH) { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHH]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } else if constexpr (pairType == kHId) { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[kHId]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(PidTypeDire[pidType]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } else { + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Pt"), associate.pt()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Eta"), associate.eta()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Phi"), associate.phi()); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kQA]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("Reso/Sign"), associate.sign()); + + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhi"), dPhi); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaEta"), dEta); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("DeltaPhiDeltaEta"), dPhi, dEta, centrality); + histReg.fill(HIST(EventTypeDire[kMixedEvent]) + HIST(PairTypeDire[pairType]) + HIST(AnalysisTypeDire[kCorr]) + HIST(CorrRoleDire[roleType]) + HIST(MassRegionDire[massRegion]) + HIST("CorrSparse"), trigger.pt(), associate.pt(), dPhi, dEta, centrality); + } + } + + template + void fillMixingPairQA(const int mixChannel, const int mixBin, const C& collision1, const C& collision2) + { + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerChannel"), mixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/CollisionPairsPerBinChannel"), mixBin, mixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/VtxZ1VsVtxZ2"), collision1.posZ(), collision2.posZ()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaVtxZVsChannel"), mixChannel, collision1.posZ() - collision2.posZ()); + mixingOperationQA.fill(HIST("ME/Mixing/Centrality1VsCentrality2"), collision1.centrality(), collision2.centrality()); + mixingOperationQA.fill(HIST("ME/Mixing/DeltaCentralityVsChannel"), mixChannel, collision1.centrality() - collision2.centrality()); + } + + //______________________________________________________________________________ + // Mix one channel with selfCombinations. + // collisionPool : collisions whose CorrMask has the channel bit (partition on HPCorrColls) + // assocPartition : associates whose UsedMask has the channel bit (partition on HPCorrAssocs) + // selfCombinations groups the pool by mixing bin (z-vertex x centrality) and pairs every collision with the + // next c2009nEvtMixing collisions of the same bin. Each pair comes once, collision1 before collision2: + // the triggers are taken from collision1 and the associates from collision2. + template + void mixChannel(H& histReg, P& collisionPool, A& assocPartition, aod::HPCorrTriggers const& triggers, const int64_t nCollisions) + { + constexpr int PtType = roleType == kAssocLowPt ? kCountLowPt : kCountHighPt; + constexpr int CorrType = getCorrType(); + constexpr bool IsReso = CorrType >= kCountHPhi; + constexpr int MixChannel = IsReso ? NTrackMixChannels + (CorrType - kCountHPhi) * kNCorrCountPt * NMassWindows + PtType * NMassWindows + (massRegion - kMassPeak) + : CorrType * kNCorrCountPt + PtType; + static_assert(MixChannel >= 0 && MixChannel < NMixOperationChannels, "Invalid mixing channel"); + + nPartnersPerColl.assign(nCollisions, 0); + + for (const auto& [collision1, collision2] : selfCombinations(colBinning, cfgMixing.c2009nEvtMixing, -1, collisionPool, collisionPool)) { + const int mixBin1 = colBinning.getBin({collision1.posZ(), collision1.centrality()}); + const int mixBin2 = colBinning.getBin({collision2.posZ(), collision2.centrality()}); + if (mixBin1 != mixBin2) { + LOG(fatal) << "EVENT MIXING ERROR :: collisions from different mixing bins :: " << mixBin1 << " :: " << mixBin2; + } + + nPartnersPerColl[collision1.globalIndex()]++; + fillMixingPairQA(MixChannel, mixBin1, collision1, collision2); + + const auto triggers1 = triggers.sliceBy(triggersPerCollPreslice, collision1.globalIndex()); + const auto associates2 = assocPartition->sliceByCached(aod::hpcorrassoc::hpCorrCollId, collision2.globalIndex(), cache); + + for (const auto& trigger : triggers1) { + for (const auto& associate : associates2) { + fillMixedPair(histReg, trigger, associate, collision1.centrality()); + } + } + } + + // Pool QA: every collision of the pool and the number of partners it was mixed with as trigger event + for (const auto& collision : collisionPool) { + const int mixBin = colBinning.getBin({collision.posZ(), collision.centrality()}); + if (mixBin < 0) { + continue; + } + const int nPartners = nPartnersPerColl[collision.globalIndex()]; + mixingOperationQA.fill(HIST("ME/Mixing/CollisionsPerBinChannel"), mixBin, MixChannel); + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEvent"), MixChannel, nPartners); + mixingOperationQA.fill(HIST("ME/Mixing/PartnersPerTriggerEventVsBin"), mixBin, MixChannel, nPartners); + } + } + + int dfNumber = 0; + void processNothing(aod::Origins const& origins) + { + if (cfgDebug.c2001printDebugMessages) { + LOG(info) << "DEBUG :: Process Nothing :: df_" << dfNumber << " :: origins = " << origins.size(); + } + // Intentionally empty. Keeps the task alive when the mixing is not run. + } + PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processNothing, "Dummy process for analysis", true); + + void processMixEventInDeriveData(aod::HPCorrColls const& collisions, aod::HPCorrTriggers const& triggers, aod::HPCorrAssocs const& assocs) + { + dfNumber++; + const int64_t nCollisions = collisions.size(); + mixingOperationQA.fill(HIST("ME/Mixing/DataFramesProcessed"), 0.0); + mixingOperationQA.fill(HIST("ME/Mixing/CollisionsPerDF"), nCollisions); + + if (cfgDebug.c2001printDebugMessages) { + LOG(info) << "DEBUG :: df_" << dfNumber << " :: collisions = " << nCollisions << " :: triggers = " << triggers.size() << " :: assocs = " << assocs.size(); + } + + // Index sanity check: every trigger / associate must point to a collision of this (merged) dataframe + int64_t nBadIndex = 0; + for (const auto& trigger : triggers) { + if (trigger.hpCorrCollId() < 0 || trigger.hpCorrCollId() >= nCollisions) { + ++nBadIndex; + } + } + for (const auto& assoc : assocs) { + if (assoc.hpCorrCollId() < 0 || assoc.hpCorrCollId() >= nCollisions) { + ++nBadIndex; + } + } + if (nBadIndex > 0) { + LOG(fatal) << "DERIVED DATA ERROR :: df_" << dfNumber << " :: " << nBadIndex << " trigger/associate rows point outside the collision table"; + } + + // h-h + mixChannel(hhCorrelation, collHHLowPt, assocHHLowPt, triggers, nCollisions); + mixChannel(hhCorrelation, collHHHighPt, assocHHHighPt, triggers, nCollisions); + + // h-pi, h-K, h-p + mixChannel(hIdCorrelation, collHPiLowPt, assocHPiLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPiHighPt, assocHPiHighPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHKaLowPt, assocHKaLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHKaHighPt, assocHKaHighPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPrLowPt, assocHPrLowPt, triggers, nCollisions); + mixChannel(hIdCorrelation, collHPrHighPt, assocHPrHighPt, triggers, nCollisions); + + // h-Phi + mixChannel(hPhiCorrelation, collHPhiLowPtPeak, assocHPhiLowPtPeak, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiLowPtLSB, assocHPhiLowPtLSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiLowPtRSB, assocHPhiLowPtRSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtPeak, assocHPhiHighPtPeak, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtLSB, assocHPhiHighPtLSB, triggers, nCollisions); + mixChannel(hPhiCorrelation, collHPhiHighPtRSB, assocHPhiHighPtRSB, triggers, nCollisions); + + // h-KStar + mixChannel(hKStarCorrelation, collHKStarLowPtPeak, assocHKStarLowPtPeak, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarLowPtLSB, assocHKStarLowPtLSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarLowPtRSB, assocHKStarLowPtRSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtPeak, assocHKStarHighPtPeak, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtLSB, assocHKStarHighPtLSB, triggers, nCollisions); + mixChannel(hKStarCorrelation, collHKStarHighPtRSB, assocHKStarHighPtRSB, triggers, nCollisions); + + // h-KStarBar + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtPeak, assocHKStarBarLowPtPeak, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtLSB, assocHKStarBarLowPtLSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarLowPtRSB, assocHKStarBarLowPtRSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtPeak, assocHKStarBarHighPtPeak, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtLSB, assocHKStarBarHighPtLSB, triggers, nCollisions); + mixChannel(hKStarBarCorrelation, collHKStarBarHighPtRSB, assocHKStarBarHighPtRSB, triggers, nCollisions); + + // h-Lambda + mixChannel(hLambdaCorrelation, collHLambdaLowPtPeak, assocHLambdaLowPtPeak, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaLowPtLSB, assocHLambdaLowPtLSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaLowPtRSB, assocHLambdaLowPtRSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtPeak, assocHLambdaHighPtPeak, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtLSB, assocHLambdaHighPtLSB, triggers, nCollisions); + mixChannel(hLambdaCorrelation, collHLambdaHighPtRSB, assocHLambdaHighPtRSB, triggers, nCollisions); + + // h-LambdaBar + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtPeak, assocHLambdaBarLowPtPeak, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtLSB, assocHLambdaBarLowPtLSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarLowPtRSB, assocHLambdaBarLowPtRSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtPeak, assocHLambdaBarHighPtPeak, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtLSB, assocHLambdaBarHighPtLSB, triggers, nCollisions); + mixChannel(hLambdaBarCorrelation, collHLambdaBarHighPtRSB, assocHLambdaBarHighPtRSB, triggers, nCollisions); + } + PROCESS_SWITCH(HParticleCorrelation3MixedEvent, processMixEventInDeriveData, "Mixed-event correlations from the minimal derived data", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask(context), + adaptAnalysisTask(context), + adaptAnalysisTask(context)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx new file mode 100644 index 00000000000..72a7a8cca45 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaOrAntiLambdaProducerWithSpin.cxx @@ -0,0 +1,2538 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambdaOrAntiLambdaProducerWithSpin.cxx +/// \brief Produces Lambda and anti-Lambda candidates and analyzes their spin correlations +/// \author Akash Raj (akash.raj.john.babu@cern.ch) + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +/*void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back( + {"isMC", VariantType::Bool, false, {"Run the Monte Carlo analysis workflow"}}); +}*/ + +#include + +// Defining Lambda & Anti-Lambda Table + +namespace o2::aod +{ +namespace lambdaorantilambda +{ +// Event information +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); +DECLARE_SOA_COLUMN(Sel8, sel8, bool); +DECLARE_SOA_COLUMN(VertexZ, vertexZ, float); + +// false = Lambda hypothesis +// true = anti-Lambda hypothesis +DECLARE_SOA_COLUMN(IsAntiLambdaHypothesis, isAntiLambdaHypothesis, bool); + +// V0 kinematics +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); + +// Hypothesis-dependent invariant mass +DECLARE_SOA_COLUMN(Mass, mass, float); + +// V0 topology +DECLARE_SOA_COLUMN(CosPA, cosPA, float); +DECLARE_SOA_COLUMN(CTau, cTau, float); +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); +DECLARE_SOA_COLUMN(DcaDau, dcaDau, float); +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); + +// Proton for Lambda; antiproton for anti-Lambda +DECLARE_SOA_COLUMN(ProtonPx, protonPx, float); +DECLARE_SOA_COLUMN(ProtonPy, protonPy, float); +DECLARE_SOA_COLUMN(ProtonPz, protonPz, float); +DECLARE_SOA_COLUMN(ProtonPt, protonPt, float); +DECLARE_SOA_COLUMN(ProtonEta, protonEta, float); +DECLARE_SOA_COLUMN(ProtonPhi, protonPhi, float); +DECLARE_SOA_COLUMN(ProtonDcaToPV, protonDcaToPV, float); +DECLARE_SOA_COLUMN(ProtonTrackId, protonTrackId, int64_t); + +// Pion information +DECLARE_SOA_COLUMN(PionPx, pionPx, float); +DECLARE_SOA_COLUMN(PionPy, pionPy, float); +DECLARE_SOA_COLUMN(PionPz, pionPz, float); +DECLARE_SOA_COLUMN(PionPt, pionPt, float); +DECLARE_SOA_COLUMN(PionEta, pionEta, float); +DECLARE_SOA_COLUMN(PionPhi, pionPhi, float); +DECLARE_SOA_COLUMN(PionDcaToPV, pionDcaToPV, float); +DECLARE_SOA_COLUMN(PionTrackId, pionTrackId, int64_t); + +// Original positive/negative daughter indices +DECLARE_SOA_COLUMN(PosTrackId, posTrackId, int64_t); +DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int64_t); + +// Armenteros–Podolanski variables +DECLARE_SOA_COLUMN(Alpha, alpha, float); +DECLARE_SOA_COLUMN(QtArm, qtArm, float); + +// Compatible with the K0Short mass window; not a truth label +DECLARE_SOA_COLUMN(IsKShortCompatible, isKShortCompatible, bool); + +// Positive daughter: TPC momentum, dE/dx signal, and PID +DECLARE_SOA_COLUMN(PosTPCMomentum, posTPCMomentum, float); +DECLARE_SOA_COLUMN(PosTPCSignal, posTPCSignal, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); + +// Negative daughter: TPC momentum, dE/dx signal, and PID +DECLARE_SOA_COLUMN(NegTPCMomentum, negTPCMomentum, float); +DECLARE_SOA_COLUMN(NegTPCSignal, negTPCSignal, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); + +} // namespace lambdaorantilambda + +DECLARE_SOA_TABLE(LambdaOrAntiLambdas, + "AOD", + "LAMORANTILAM", + o2::soa::Index<>, + + lambdaorantilambda::CollisionId, + lambdaorantilambda::Centrality, + lambdaorantilambda::Multiplicity, + lambdaorantilambda::Sel8, + lambdaorantilambda::VertexZ, + + lambdaorantilambda::IsAntiLambdaHypothesis, + + lambdaorantilambda::Px, + lambdaorantilambda::Py, + lambdaorantilambda::Pz, + lambdaorantilambda::Pt, + lambdaorantilambda::Eta, + lambdaorantilambda::Phi, + lambdaorantilambda::Rapidity, + + lambdaorantilambda::Mass, + + lambdaorantilambda::CosPA, + lambdaorantilambda::CTau, + lambdaorantilambda::V0Radius, + lambdaorantilambda::DcaDau, + lambdaorantilambda::DcaV0ToPV, + + lambdaorantilambda::ProtonPx, + lambdaorantilambda::ProtonPy, + lambdaorantilambda::ProtonPz, + lambdaorantilambda::ProtonPt, + lambdaorantilambda::ProtonEta, + lambdaorantilambda::ProtonPhi, + lambdaorantilambda::ProtonDcaToPV, + lambdaorantilambda::ProtonTrackId, + + lambdaorantilambda::PionPx, + lambdaorantilambda::PionPy, + lambdaorantilambda::PionPz, + lambdaorantilambda::PionPt, + lambdaorantilambda::PionEta, + lambdaorantilambda::PionPhi, + lambdaorantilambda::PionDcaToPV, + lambdaorantilambda::PionTrackId, + + lambdaorantilambda::PosTrackId, + lambdaorantilambda::NegTrackId, + + lambdaorantilambda::Alpha, + lambdaorantilambda::QtArm, + + lambdaorantilambda::IsKShortCompatible, + + lambdaorantilambda::PosTPCMomentum, + lambdaorantilambda::PosTPCSignal, + lambdaorantilambda::PosTPCNSigmaPr, + lambdaorantilambda::PosTPCNSigmaPi, + + lambdaorantilambda::NegTPCMomentum, + lambdaorantilambda::NegTPCSignal, + lambdaorantilambda::NegTPCNSigmaPr, + lambdaorantilambda::NegTPCNSigmaPi); + +namespace lambdahyperon +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); + +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); + +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); +DECLARE_SOA_COLUMN(Mass, mass, float); + +// Proton for Lambda; antiproton for anti-Lambda. +DECLARE_SOA_COLUMN(ProtonPx, protonPx, float); +DECLARE_SOA_COLUMN(ProtonPy, protonPy, float); +DECLARE_SOA_COLUMN(ProtonPz, protonPz, float); + +DECLARE_SOA_COLUMN(PosTrackId, posTrackId, int64_t); +DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int64_t); + +DECLARE_SOA_COLUMN(CosPA, cosPA, float); +DECLARE_SOA_COLUMN(DcaDau, dcaDau, float); +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); +DECLARE_SOA_COLUMN(ProtonDcaToPV, protonDcaToPV, float); +DECLARE_SOA_COLUMN(PionDcaToPV, pionDcaToPV, float); +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); +DECLARE_SOA_COLUMN(Alpha, alpha, float); +DECLARE_SOA_COLUMN(QtArm, qtArm, float); + +// Positive daughter +DECLARE_SOA_COLUMN(PosTPCMomentum, posTPCMomentum, float); +DECLARE_SOA_COLUMN(PosTPCSignal, posTPCSignal, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); + +// Negative daughter +DECLARE_SOA_COLUMN(NegTPCMomentum, negTPCMomentum, float); +DECLARE_SOA_COLUMN(NegTPCSignal, negTPCSignal, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); +} // namespace lambdahyperon + +DECLARE_SOA_TABLE(Lambdas, "AOD", "LAMBDAS", o2::soa::Index<>, + lambdahyperon::CollisionId, + lambdahyperon::Centrality, lambdahyperon::Multiplicity, + lambdahyperon::Px, lambdahyperon::Py, lambdahyperon::Pz, + lambdahyperon::Pt, lambdahyperon::Eta, lambdahyperon::Phi, + lambdahyperon::Rapidity, lambdahyperon::Mass, + lambdahyperon::ProtonPx, lambdahyperon::ProtonPy, lambdahyperon::ProtonPz, + lambdahyperon::PosTrackId, lambdahyperon::NegTrackId, + lambdahyperon::CosPA, lambdahyperon::DcaDau, lambdahyperon::DcaV0ToPV, + lambdahyperon::ProtonDcaToPV, lambdahyperon::PionDcaToPV, + lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm, + lambdahyperon::PosTPCMomentum, lambdahyperon::PosTPCSignal, + lambdahyperon::PosTPCNSigmaPr, lambdahyperon::PosTPCNSigmaPi, + lambdahyperon::NegTPCMomentum, lambdahyperon::NegTPCSignal, + lambdahyperon::NegTPCNSigmaPr, lambdahyperon::NegTPCNSigmaPi); + +DECLARE_SOA_TABLE(AntiLambdas, "AOD", "ANTILAMBDAS", o2::soa::Index<>, + lambdahyperon::CollisionId, + lambdahyperon::Centrality, lambdahyperon::Multiplicity, + lambdahyperon::Px, lambdahyperon::Py, lambdahyperon::Pz, + lambdahyperon::Pt, lambdahyperon::Eta, lambdahyperon::Phi, + lambdahyperon::Rapidity, lambdahyperon::Mass, + lambdahyperon::ProtonPx, lambdahyperon::ProtonPy, lambdahyperon::ProtonPz, + lambdahyperon::PosTrackId, lambdahyperon::NegTrackId, + lambdahyperon::CosPA, lambdahyperon::DcaDau, lambdahyperon::DcaV0ToPV, + lambdahyperon::ProtonDcaToPV, lambdahyperon::PionDcaToPV, + lambdahyperon::V0Radius, lambdahyperon::Alpha, lambdahyperon::QtArm, + lambdahyperon::PosTPCMomentum, lambdahyperon::PosTPCSignal, + lambdahyperon::PosTPCNSigmaPr, lambdahyperon::PosTPCNSigmaPi, + lambdahyperon::NegTPCMomentum, lambdahyperon::NegTPCSignal, + lambdahyperon::NegTPCNSigmaPr, lambdahyperon::NegTPCNSigmaPi); + +// ===================================================================== +// MC-reconstructed Lambda/anti-Lambda candidates +// One row per reconstructed V0 candidate +// ===================================================================== + +namespace mcrecolambda +{ +// Reconstructed-event information +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(Sel8, sel8, bool); +DECLARE_SOA_COLUMN(VertexZ, vertexZ, float); + +// MC truth matching +DECLARE_SOA_COLUMN(HasMcMatch, hasMcMatch, bool); +DECLARE_SOA_COLUMN(IsAntiLambda, isAntiLambda, bool); +DECLARE_SOA_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, bool); + +// Generated kinematics of the matched Lambda/anti-Lambda +DECLARE_SOA_COLUMN(GenPx, genPx, float); +DECLARE_SOA_COLUMN(GenPy, genPy, float); +DECLARE_SOA_COLUMN(GenPz, genPz, float); +DECLARE_SOA_COLUMN(GenPt, genPt, float); +DECLARE_SOA_COLUMN(GenEta, genEta, float); +DECLARE_SOA_COLUMN(GenPhi, genPhi, float); +DECLARE_SOA_COLUMN(GenRapidity, genRapidity, float); + +// Reconstructed V0 kinematics +DECLARE_SOA_COLUMN(RecoPx, recoPx, float); +DECLARE_SOA_COLUMN(RecoPy, recoPy, float); +DECLARE_SOA_COLUMN(RecoPz, recoPz, float); +DECLARE_SOA_COLUMN(RecoPt, recoPt, float); +DECLARE_SOA_COLUMN(RecoEta, recoEta, float); +DECLARE_SOA_COLUMN(RecoPhi, recoPhi, float); +DECLARE_SOA_COLUMN(RecoRapidity, recoRapidity, float); + +//|pTReco-pTGen| +DECLARE_SOA_COLUMN(PtResolution, ptResolution, float); +// Reconstructed invariant-mass hypotheses +DECLARE_SOA_COLUMN(MassLambda, massLambda, float); + +// Reconstructed V0 topology +DECLARE_SOA_COLUMN(DcaDaughters, dcaDaughters, float); +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); +DECLARE_SOA_COLUMN(CosPA, cosPA, float); +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); +DECLARE_SOA_COLUMN(CTau, cTau, float); + +// Proton-Or-AntiProton reconstructed daughter +DECLARE_SOA_COLUMN(ProPx, proPx, float); +DECLARE_SOA_COLUMN(ProPy, proPy, float); +DECLARE_SOA_COLUMN(ProPz, proPz, float); +DECLARE_SOA_COLUMN(ProPt, proPt, float); +DECLARE_SOA_COLUMN(ProEta, proEta, float); +DECLARE_SOA_COLUMN(ProPhi, proPhi, float); +DECLARE_SOA_COLUMN(ProDcaToPV, proDcaToPV, float); +DECLARE_SOA_COLUMN(ProTPCCrossedRows, proTPCCrossedRows, int); +DECLARE_SOA_COLUMN(ProTPCNSigma, proTPCNSigma, float); + +// PiePlus-OrPieMinus- reconstructed daughter +DECLARE_SOA_COLUMN(PiePx, piePx, float); +DECLARE_SOA_COLUMN(PiePy, piePy, float); +DECLARE_SOA_COLUMN(PiePz, piePz, float); +DECLARE_SOA_COLUMN(PiePt, piePt, float); +DECLARE_SOA_COLUMN(PieEta, pieEta, float); +DECLARE_SOA_COLUMN(PiePhi, piePhi, float); +DECLARE_SOA_COLUMN(PieDcaToPV, pieDcaToPV, float); +DECLARE_SOA_COLUMN(PieTPCCrossedRows, pieTPCCrossedRows, int); +DECLARE_SOA_COLUMN(PieTPCNSigma, pieTPCNSigma, float); + +// KShort Mass Campatability +DECLARE_SOA_COLUMN(IsKShortCompatible, isKShortCompatible, bool); + +} // namespace mcrecolambda + +DECLARE_SOA_TABLE(McRecoLambdaCandidates, + "AOD", + "MCRECOLAMBDA", + + o2::soa::Index<>, + + mcrecolambda::CollisionId, + mcrecolambda::Sel8, + mcrecolambda::VertexZ, + + mcrecolambda::HasMcMatch, + mcrecolambda::IsAntiLambda, + mcrecolambda::IsPhysicalPrimary, + + mcrecolambda::GenPx, + mcrecolambda::GenPy, + mcrecolambda::GenPz, + mcrecolambda::GenPt, + mcrecolambda::GenEta, + mcrecolambda::GenPhi, + mcrecolambda::GenRapidity, + + mcrecolambda::RecoPx, + mcrecolambda::RecoPy, + mcrecolambda::RecoPz, + mcrecolambda::RecoPt, + mcrecolambda::RecoEta, + mcrecolambda::RecoPhi, + mcrecolambda::RecoRapidity, + + mcrecolambda::PtResolution, + + mcrecolambda::MassLambda, + + mcrecolambda::DcaDaughters, + mcrecolambda::DcaV0ToPV, + mcrecolambda::CosPA, + mcrecolambda::V0Radius, + mcrecolambda::CTau, + + mcrecolambda::ProPx, + mcrecolambda::ProPy, + mcrecolambda::ProPz, + mcrecolambda::ProPt, + mcrecolambda::ProEta, + mcrecolambda::ProPhi, + mcrecolambda::ProDcaToPV, + mcrecolambda::ProTPCCrossedRows, + mcrecolambda::ProTPCNSigma, + + mcrecolambda::PiePx, + mcrecolambda::PiePy, + mcrecolambda::PiePz, + mcrecolambda::PiePt, + mcrecolambda::PieEta, + mcrecolambda::PiePhi, + mcrecolambda::PieDcaToPV, + mcrecolambda::PieTPCCrossedRows, + mcrecolambda::PieTPCNSigma, + mcrecolambda::IsKShortCompatible); + +// ===================================================================== +// MC-generated Lambda/anti-Lambda candidates +// One row per generated particle +// ===================================================================== + +namespace eveselpassmcgenevent +{ +DECLARE_SOA_INDEX_COLUMN(McCollision, mcCollision); +DECLARE_SOA_COLUMN(NRecoCollisions, nRecoCollisions, int); // Total Number of RecoCollions which is am image of McCollions +DECLARE_SOA_COLUMN(NSel8Collisions, nSel8Collisions, int); // Number of RecoCollions which passed Sel8() +} // namespace eveselpassmcgenevent + +DECLARE_SOA_TABLE(EveSelPassMcGenLambdaCandidates, + "AOD", + "MCGENLAMBDA", + + o2::soa::Index<>, + + eveselpassmcgenevent::McCollisionId, + eveselpassmcgenevent::NRecoCollisions, + eveselpassmcgenevent::NSel8Collisions); + +} // namespace o2::aod + +//*********************************************************************************************************** +// UnFilterred Lambda-LambdaBar Structure (Only TPCnsigma and TPC min CorssRows fillters are applied ) +//*********************************************************************************************************** + +struct LambdaOrAntiLambdaProducerWithSpin { + + Produces + lambdaOrAntiLambdaTable; + + // Produces lambdaTable; + // Produces antilambdaTable; + + HistogramRegistry rLambdaOrAntiLambda{"LambdaORAntiLambda", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // HistogramRegistry rLambda{"Lambdas", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // HistogramRegistry rAntiLambda{"AntiLambdas", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum number of TPC crossed rows"}; + Configurable maxTPCNSigma{"maxTPCNSigma", 3.0f, "Maximum absolute TPC PID n-sigma"}; + Configurable kShortRejectionWindow{"kShortRejectionWindow", 0.001f, "K0Short mass rejection half-window (GeV/c2)"}; + + ConfigurableAxis axisInvariantMass{"axisInvariantMass", {100, 1.08f, 1.2f}, "Invariant-mass axis"}; + ConfigurableAxis axisLambdaAntiLambdaPt{"axisLambdaAntiLambdaPt", {200, 0.f, 10.f}, "LambdaAntiLambda transverse-momentum axis"}; + + void init(InitContext const&) + { + + // Event information + rLambdaOrAntiLambda.add("Event/hCentrality", "Centrality;FT0M centrality (%);entries", HistType::kTH1F, {{100, 0.f, 100.f}}); + rLambdaOrAntiLambda.add("Event/hMultiplicity", "Primary-vertex multiplicity;N_{tracks}^{PV};entries", HistType::kTH1F, {{500, 0.f, 500.f}}); + rLambdaOrAntiLambda.add("Candidate/hHypothesis", "Candidate hypothesis;hypothesis;entries", HistType::kTH1F, {{2, -0.5f, 1.5f}}); + + // V0 kinematics + rLambdaOrAntiLambda.add("Candidate/hPx", "V0 p_{x};p_{x} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Candidate/hPy", "V0 p_{y};p_{y} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Candidate/hPz", "V0 p_{z};p_{z} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + // rLambdaOrAntiLambda.add("Candidate/hPt", "V0 p_{T};p_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 10.f}}); + rLambdaOrAntiLambda.add("Candidate/hPt", "V0 p_{T};p_{T} (GeV/c);entries", HistType::kTH1F, {axisLambdaAntiLambdaPt}); + rLambdaOrAntiLambda.add("Candidate/hEta", "V0 pseudorapidity;#eta;entries", HistType::kTH1F, {{100, -2.f, 2.f}}); + rLambdaOrAntiLambda.add("Candidate/hPhi", "V0 azimuth;#varphi;entries", HistType::kTH1F, {{72, 0.f, o2::constants::math::TwoPI}}); + rLambdaOrAntiLambda.add("Candidate/hRapidity", "V0 rapidity;y;entries", HistType::kTH1F, {{100, -2.f, 2.f}}); + rLambdaOrAntiLambda.add("Candidate/hMass", "Reconstructed invariant mass;m_{p#pi} (GeV/c^{2});entries", HistType::kTH1F, {axisInvariantMass}); + rLambdaOrAntiLambda.add("Candidate/hMassVsPt", + "#Lambda invariant mass versus p_{T};" + "p_{T} (GeV/c);" + "m_{#Lambda} (GeV/c^{2})", + HistType::kTH2F, {axisLambdaAntiLambdaPt, axisInvariantMass}); + + // Topology + rLambdaOrAntiLambda.add("Topology/hCosPA", "V0 pointing angle;cos(PA);entries", HistType::kTH1F, {{300, 0.97f, 1.f}}); + rLambdaOrAntiLambda.add("Topology/hCTau", "Lambda proper decay length;c#tau (cm);entries", HistType::kTH1F, {{200, 0.f, 100.f}}); + rLambdaOrAntiLambda.add("Topology/hRadius", "V0 transverse decay radius;r_{xy} (cm);entries", HistType::kTH1F, {{200, 0.f, 200.f}}); + rLambdaOrAntiLambda.add("Topology/hDcaDaughters", "DCA between V0 daughters;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 5.f}}); + rLambdaOrAntiLambda.add("Topology/hDcaV0ToPV", "V0 DCA to PV;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 5.f}}); + + // Proton or antiproton + rLambdaOrAntiLambda.add("Proton/hTPCdEdxVsP", "V0 proton/antiproton TPC signal;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + + rLambdaOrAntiLambda.add("Proton/hPx", "Proton/antiproton p_{x};p_{x} (GeV/c);entries", HistType::kTH1F, {{200, -5.f, 5.f}}); + rLambdaOrAntiLambda.add("Proton/hPy", "Proton/antiproton p_{y};p_{y} (GeV/c);entries", HistType::kTH1F, {{200, -5.f, 5.f}}); + rLambdaOrAntiLambda.add("Proton/hPz", "Proton/antiproton p_{z};p_{z} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Proton/hPt", "Proton/antiproton p_{T};p_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 5.f}}); + rLambdaOrAntiLambda.add("Proton/hEta", "Proton/antiproton pseudorapidity;#eta;entries", HistType::kTH1F, {{100, -2.f, 2.f}}); + rLambdaOrAntiLambda.add("Proton/hPhi", "Proton/antiproton azimuth;#varphi;entries", HistType::kTH1F, {{72, 0.f, o2::constants::math::TwoPI}}); + rLambdaOrAntiLambda.add("Proton/hDcaToPV", "Proton/antiproton DCA to PV;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 10.f}}); + + // Pion + rLambdaOrAntiLambda.add("Pion/hTPCdEdxVsP", "V0 pion TPC signal;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + + rLambdaOrAntiLambda.add("Pion/hPx", "Pion p_{x};p_{x} (GeV/c);entries", HistType::kTH1F, {{200, -5.f, 5.f}}); + rLambdaOrAntiLambda.add("Pion/hPy", "Pion p_{y};p_{y} (GeV/c);entries", HistType::kTH1F, {{200, -5.f, 5.f}}); + rLambdaOrAntiLambda.add("Pion/hPz", "Pion p_{z};p_{z} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Pion/hPt", "Pion p_{T};p_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 5.f}}); + rLambdaOrAntiLambda.add("Pion/hEta", "Pion pseudorapidity;#eta;entries", HistType::kTH1F, {{100, -2.f, 2.f}}); + rLambdaOrAntiLambda.add("Pion/hPhi", "Pion azimuth;#varphi;entries", HistType::kTH1F, {{72, 0.f, o2::constants::math::TwoPI}}); + rLambdaOrAntiLambda.add("Pion/hDcaToPV", "Pion DCA to PV;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 10.f}}); + + // Positive daughter: measured signal and both PID hypotheses + rLambdaOrAntiLambda.add("Positive/hTPCdEdxVsP", "Positive daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rLambdaOrAntiLambda.add("Positive/hTPCNSigmaPrVsP", "Positive daughter, proton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{p}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Positive/hTPCNSigmaPiVsP", "Positive daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + + // Negative daughter: measured signal and both PID hypotheses + rLambdaOrAntiLambda.add("Negative/hTPCdEdxVsP", "Negative daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rLambdaOrAntiLambda.add("Negative/hTPCNSigmaPrVsP", "Negative daughter, antiproton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#bar{p}}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rLambdaOrAntiLambda.add("Negative/hTPCNSigmaPiVsP", "Negative daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + + // Armenteros-Podolanski + rLambdaOrAntiLambda.add("Armenteros/hAlpha", "Armenteros #alpha;#alpha;entries", HistType::kTH1F, {{200, -1.f, 1.f}}); + rLambdaOrAntiLambda.add("Armenteros/hQtArm", "Armenteros q_{T};q_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 0.4f}}); + rLambdaOrAntiLambda.add("Armenteros/hAlphaVsQt", "Armenteros-Podolanski;#alpha;q_{T} (GeV/c)", HistType::kTH2F, {{200, -1.f, 1.f}, {200, 0.f, 0.4f}}); + + auto* hypothesisAxis = rLambdaOrAntiLambda.get(HIST("Candidate/hHypothesis"))->GetXaxis(); + + hypothesisAxis->SetBinLabel(1, "Lambda"); + hypothesisAxis->SetBinLabel(2, "Anti-Lambda"); + } + + using TracksWithPID = soa::Join; + using CollisionsWithActivity = soa::Join; + + void processData(aod::V0Datas const& v0s, + TracksWithPID const&, + CollisionsWithActivity const&) + { + + for (auto const& v0 : v0s) { + auto collision = v0.collision_as(); + auto posTrack = v0.posTrack_as(); + auto negTrack = v0.negTrack_as(); + + if (posTrack.tpcNClsCrossedRows() < minTPCCrossedRows || negTrack.tpcNClsCrossedRows() < minTPCCrossedRows) { + continue; + } + + const bool isKShortCompatible = + std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) <= kShortRejectionWindow.value; + + const bool isLambda = + std::abs(posTrack.tpcNSigmaPr()) < maxTPCNSigma && + std::abs(negTrack.tpcNSigmaPi()) < maxTPCNSigma; + + const bool isAntiLambda = + std::abs(negTrack.tpcNSigmaPr()) < maxTPCNSigma && + std::abs(posTrack.tpcNSigmaPi()) < maxTPCNSigma; + + // Reject candidates compatible with neither or with both hypotheses. + if (isLambda == isAntiLambda) { + continue; + } + + const auto& protonORantiprotonTrack = isLambda ? posTrack : negTrack; + + const auto& pionTrack = isLambda ? negTrack : posTrack; + + const float mass = isLambda ? v0.mLambda() : v0.mAntiLambda(); + + const float protonDcaToPV = isLambda ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + + const float pionDcaToPV = isLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); + + const float cTau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + + lambdaOrAntiLambdaTable( + // Event information + v0.collisionId(), + collision.centFT0M(), + collision.multNTracksPV(), + collision.sel8(), + collision.posZ(), + + // false = Lambda; true = anti-Lambda + isAntiLambda, + + // V0 kinematics + v0.px(), + v0.py(), + v0.pz(), + v0.pt(), + v0.eta(), + v0.phi(), + v0.yLambda(), // Lambda/anti-Lambda rapidity; v0.y() is the decay position. + + // Reconstructed hypothesis-dependent mass + mass, + + // V0 topology + v0.v0cosPA(), + cTau, + v0.v0radius(), + v0.dcaV0daughters(), + v0.dcav0topv(), + + // Proton for Lambda; antiproton for anti-Lambda + protonORantiprotonTrack.px(), + protonORantiprotonTrack.py(), + protonORantiprotonTrack.pz(), + protonORantiprotonTrack.pt(), + protonORantiprotonTrack.eta(), + protonORantiprotonTrack.phi(), + protonDcaToPV, + protonORantiprotonTrack.globalIndex(), + + // Pion + pionTrack.px(), + pionTrack.py(), + pionTrack.pz(), + pionTrack.pt(), + pionTrack.eta(), + pionTrack.phi(), + pionDcaToPV, + pionTrack.globalIndex(), + + // Original positive and negative track IDs + posTrack.globalIndex(), + negTrack.globalIndex(), + + // Armenteros-Podolanski + v0.alpha(), + v0.qtarm(), + + // K0 Mass Comaptablity test + isKShortCompatible, + + // Positive daughter: measured signal and both PID hypotheses + posTrack.tpcInnerParam(), + posTrack.tpcSignal(), + posTrack.tpcNSigmaPr(), + posTrack.tpcNSigmaPi(), + + // Negative daughter: measured signal and both PID hypotheses + negTrack.tpcInnerParam(), + negTrack.tpcSignal(), + negTrack.tpcNSigmaPr(), + negTrack.tpcNSigmaPi()); + + rLambdaOrAntiLambda.fill(HIST("Event/hCentrality"), collision.centFT0M()); + + rLambdaOrAntiLambda.fill(HIST("Event/hMultiplicity"), collision.multNTracksPV()); + + rLambdaOrAntiLambda.fill(HIST("Candidate/hHypothesis"), isAntiLambda ? 1.f : 0.f); + rLambdaOrAntiLambda.fill(HIST("Candidate/hPx"), v0.px()); + rLambdaOrAntiLambda.fill(HIST("Candidate/hPy"), v0.py()); + rLambdaOrAntiLambda.fill(HIST("Candidate/hPz"), v0.pz()); + rLambdaOrAntiLambda.fill(HIST("Candidate/hPt"), v0.pt()); + rLambdaOrAntiLambda.fill(HIST("Candidate/hEta"), v0.eta()); + rLambdaOrAntiLambda.fill(HIST("Candidate/hPhi"), v0.phi()); + rLambdaOrAntiLambda.fill(HIST("Candidate/hRapidity"), v0.yLambda()); + rLambdaOrAntiLambda.fill(HIST("Candidate/hMass"), mass); + rLambdaOrAntiLambda.fill(HIST("Candidate/hMassVsPt"), v0.pt(), mass); + + rLambdaOrAntiLambda.fill(HIST("Topology/hCosPA"), v0.v0cosPA()); + rLambdaOrAntiLambda.fill(HIST("Topology/hCTau"), cTau); + rLambdaOrAntiLambda.fill(HIST("Topology/hRadius"), v0.v0radius()); + rLambdaOrAntiLambda.fill(HIST("Topology/hDcaDaughters"), v0.dcaV0daughters()); + rLambdaOrAntiLambda.fill(HIST("Topology/hDcaV0ToPV"), v0.dcav0topv()); + + rLambdaOrAntiLambda.fill(HIST("Proton/hTPCdEdxVsP"), protonORantiprotonTrack.tpcInnerParam(), protonORantiprotonTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Proton/hPx"), protonORantiprotonTrack.px()); + rLambdaOrAntiLambda.fill(HIST("Proton/hPy"), protonORantiprotonTrack.py()); + rLambdaOrAntiLambda.fill(HIST("Proton/hPz"), protonORantiprotonTrack.pz()); + rLambdaOrAntiLambda.fill(HIST("Proton/hPt"), protonORantiprotonTrack.pt()); + rLambdaOrAntiLambda.fill(HIST("Proton/hEta"), protonORantiprotonTrack.eta()); + rLambdaOrAntiLambda.fill(HIST("Proton/hPhi"), protonORantiprotonTrack.phi()); + rLambdaOrAntiLambda.fill(HIST("Proton/hDcaToPV"), protonDcaToPV); + + rLambdaOrAntiLambda.fill(HIST("Pion/hTPCdEdxVsP"), pionTrack.tpcInnerParam(), pionTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Pion/hPx"), pionTrack.px()); + rLambdaOrAntiLambda.fill(HIST("Pion/hPy"), pionTrack.py()); + rLambdaOrAntiLambda.fill(HIST("Pion/hPz"), pionTrack.pz()); + rLambdaOrAntiLambda.fill(HIST("Pion/hPt"), pionTrack.pt()); + rLambdaOrAntiLambda.fill(HIST("Pion/hEta"), pionTrack.eta()); + rLambdaOrAntiLambda.fill(HIST("Pion/hPhi"), pionTrack.phi()); + rLambdaOrAntiLambda.fill(HIST("Pion/hDcaToPV"), pionDcaToPV); + + rLambdaOrAntiLambda.fill(HIST("Armenteros/hAlpha"), v0.alpha()); + rLambdaOrAntiLambda.fill(HIST("Armenteros/hQtArm"), v0.qtarm()); + rLambdaOrAntiLambda.fill(HIST("Armenteros/hAlphaVsQt"), v0.alpha(), v0.qtarm()); + + // Positive daughter: before PID selection + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCdEdxVsP"), posTrack.tpcInnerParam(), posTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCNSigmaPrVsP"), posTrack.tpcInnerParam(), posTrack.tpcNSigmaPr()); + rLambdaOrAntiLambda.fill(HIST("Positive/hTPCNSigmaPiVsP"), posTrack.tpcInnerParam(), posTrack.tpcNSigmaPi()); + + // Negative daughter: before PID selection + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCdEdxVsP"), negTrack.tpcInnerParam(), negTrack.tpcSignal()); + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCNSigmaPrVsP"), negTrack.tpcInnerParam(), negTrack.tpcNSigmaPr()); + rLambdaOrAntiLambda.fill(HIST("Negative/hTPCNSigmaPiVsP"), negTrack.tpcInnerParam(), negTrack.tpcNSigmaPi()); + } + } + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaOrAntiLambdaProducerWithSpin, processData, "Run candidate production", true); + PROCESS_SWITCH(LambdaOrAntiLambdaProducerWithSpin, processDummy, "Skip candidate production", false); +}; + +//*********************************************************************************************************** +// Filltered Lambda-LambdaBar Structure +//*********************************************************************************************************** +struct LambdaAntiLambdaSelector { + Produces lambdaTable; + Produces antiLambdaTable; + + HistogramRegistry rSelected{"Selected", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + ConfigurableAxis axisInvariantMass{"axisInvariantMass", {100, 1.08f, 1.2f}, "Invariant-mass axis"}; + ConfigurableAxis axisPt{"axisPt", {200, 0.f, 10.f}, "transverse-momentum axis"}; + + // HistogramRegistry rSelectedLambda{"SelectedLambdas", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // HistogramRegistry rSelectedAntiLambda{"SelectedAntiLambdas", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + void addSelectedHistograms(std::string const& prefix) + { + // Event information + rSelected.add((prefix + "/Event/hCentrality").c_str(), "Centrality;FT0M centrality (%);entries", HistType::kTH1F, {{100, 0.f, 100.f}}); + rSelected.add((prefix + "/Event/hMultiplicity").c_str(), "Primary-vertex multiplicity;N_{tracks}^{PV};entries", HistType::kTH1F, {{500, 0.f, 500.f}}); + + // Candidate kinematics + rSelected.add((prefix + "/Candidate/hPx").c_str(), "Candidate p_{x};p_{x} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + rSelected.add((prefix + "/Candidate/hPy").c_str(), "Candidate p_{y};p_{y} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + rSelected.add((prefix + "/Candidate/hPz").c_str(), "Candidate p_{z};p_{z} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + rSelected.add((prefix + "/Candidate/hPt").c_str(), "Candidate p_{T};p_{T} (GeV/c);entries", HistType::kTH1F, {axisPt}); + rSelected.add((prefix + "/Candidate/hEta").c_str(), "Candidate pseudorapidity;#eta;entries", HistType::kTH1F, {{100, -2.f, 2.f}}); + rSelected.add((prefix + "/Candidate/hPhi").c_str(), "Candidate azimuth;#varphi;entries", HistType::kTH1F, {{72, 0.f, 6.283185307f}}); + rSelected.add((prefix + "/Candidate/hRapidity").c_str(), "Candidate rapidity;y;entries", HistType::kTH1F, {{100, -1.f, 1.f}}); + rSelected.add((prefix + "/Candidate/hMass").c_str(), "Candidate invariant mass;m_{p#pi} (GeV/c^{2});entries", HistType::kTH1F, {axisInvariantMass}); + rSelected.add((prefix + "/Candidate/hMassVsPt").c_str(), + "#Candidate invariant mass versus p_{T};" + "p_{T} (GeV/c);" + "m_{#Lambda} (GeV/c^{2})", + HistType::kTH2F, {axisPt, axisInvariantMass}); + + // Proton or antiproton momentum + rSelected.add((prefix + "/Proton/hPx").c_str(), "Proton/antiproton p_{x};p_{x} (GeV/c);entries", HistType::kTH1F, {{200, -5.f, 5.f}}); + rSelected.add((prefix + "/Proton/hPy").c_str(), "Proton/antiproton p_{y};p_{y} (GeV/c);entries", HistType::kTH1F, {{200, -5.f, 5.f}}); + rSelected.add((prefix + "/Proton/hPz").c_str(), "Proton/antiproton p_{z};p_{z} (GeV/c);entries", HistType::kTH1F, {{200, -10.f, 10.f}}); + + // Topology + rSelected.add((prefix + "/Topology/hCosPA").c_str(), "V0 pointing angle;cos(PA);entries", HistType::kTH1F, {{300, 0.97f, 1.f}}); + rSelected.add((prefix + "/Topology/hDcaDaughters").c_str(), "DCA between V0 daughters;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 5.f}}); + rSelected.add((prefix + "/Topology/hDcaV0ToPV").c_str(), "V0 DCA to primary vertex;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 5.f}}); + rSelected.add((prefix + "/Topology/hProtonDcaToPV").c_str(), "Proton/antiproton DCA to PV;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 10.f}}); + rSelected.add((prefix + "/Topology/hPionDcaToPV").c_str(), "Pion DCA to PV;DCA (cm);entries", HistType::kTH1F, {{200, 0.f, 10.f}}); + rSelected.add((prefix + "/Topology/hRadius").c_str(), "V0 transverse decay radius;r_{xy} (cm);entries", HistType::kTH1F, {{200, 0.f, 200.f}}); + + // Armenteros-Podolanski + rSelected.add((prefix + "/Armenteros/hAlpha").c_str(), "Armenteros #alpha;#alpha;entries", HistType::kTH1F, {{200, -1.f, 1.f}}); + rSelected.add((prefix + "/Armenteros/hQtArm").c_str(), "Armenteros q_{T};q_{T} (GeV/c);entries", HistType::kTH1F, {{200, 0.f, 0.4f}}); + rSelected.add((prefix + "/Armenteros/hAlphaVsQt").c_str(), "Armenteros-Podolanski;#alpha;q_{T} (GeV/c)", HistType::kTH2F, {{200, -1.f, 1.f}, {200, 0.f, 0.4f}}); + + // Positive daughter + rSelected.add((prefix + "/Positive/hTPCdEdxVsP").c_str(), "Positive daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rSelected.add((prefix + "/Positive/hTPCNSigmaPrVsP").c_str(), "Positive daughter, proton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{p}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rSelected.add((prefix + "/Positive/hTPCNSigmaPiVsP").c_str(), "Positive daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + + // Negative daughter + rSelected.add((prefix + "/Negative/hTPCdEdxVsP").c_str(), "Negative daughter;p_{TPC} (GeV/c);TPC dE/dx (arb. units)", HistType::kTH2F, {{200, 0.f, 10.f}, {500, 0.f, 1000.f}}); + rSelected.add((prefix + "/Negative/hTPCNSigmaPrVsP").c_str(), "Negative daughter, antiproton hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#bar{p}}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + rSelected.add((prefix + "/Negative/hTPCNSigmaPiVsP").c_str(), "Negative daughter, pion hypothesis;p_{TPC} (GeV/c);TPC n#sigma_{#pi}", HistType::kTH2F, {{200, 0.f, 10.f}, {200, -10.f, 10.f}}); + } + + void init(InitContext const&) + { + addSelectedHistograms("Lambda"); + addSelectedHistograms("AntiLambda"); + } + + template + void fillSelectedHistograms(bool isAntiLambda, + Candidate const& candidate) + { + if (!isAntiLambda) { + rSelected.fill(HIST("Lambda/Event/hCentrality"), candidate.centrality()); + rSelected.fill(HIST("Lambda/Event/hMultiplicity"), candidate.multiplicity()); + + rSelected.fill(HIST("Lambda/Candidate/hPx"), candidate.px()); + rSelected.fill(HIST("Lambda/Candidate/hPy"), candidate.py()); + rSelected.fill(HIST("Lambda/Candidate/hPz"), candidate.pz()); + rSelected.fill(HIST("Lambda/Candidate/hPt"), candidate.pt()); + rSelected.fill(HIST("Lambda/Candidate/hEta"), candidate.eta()); + rSelected.fill(HIST("Lambda/Candidate/hPhi"), candidate.phi()); + rSelected.fill(HIST("Lambda/Candidate/hRapidity"), candidate.rapidity()); + rSelected.fill(HIST("Lambda/Candidate/hMass"), candidate.mass()); + rSelected.fill(HIST("Lambda/Candidate/hMassVsPt"), candidate.pt(), candidate.mass()); + + rSelected.fill(HIST("Lambda/Proton/hPx"), candidate.protonPx()); + rSelected.fill(HIST("Lambda/Proton/hPy"), candidate.protonPy()); + rSelected.fill(HIST("Lambda/Proton/hPz"), candidate.protonPz()); + + rSelected.fill(HIST("Lambda/Topology/hCosPA"), candidate.cosPA()); + rSelected.fill(HIST("Lambda/Topology/hDcaDaughters"), candidate.dcaDau()); + rSelected.fill(HIST("Lambda/Topology/hDcaV0ToPV"), candidate.dcaV0ToPV()); + rSelected.fill(HIST("Lambda/Topology/hProtonDcaToPV"), candidate.protonDcaToPV()); + rSelected.fill(HIST("Lambda/Topology/hPionDcaToPV"), candidate.pionDcaToPV()); + rSelected.fill(HIST("Lambda/Topology/hRadius"), candidate.v0Radius()); + + rSelected.fill(HIST("Lambda/Armenteros/hAlpha"), candidate.alpha()); + rSelected.fill(HIST("Lambda/Armenteros/hQtArm"), candidate.qtArm()); + rSelected.fill(HIST("Lambda/Armenteros/hAlphaVsQt"), candidate.alpha(), candidate.qtArm()); + + rSelected.fill(HIST("Lambda/Positive/hTPCdEdxVsP"), candidate.posTPCMomentum(), candidate.posTPCSignal()); + rSelected.fill(HIST("Lambda/Positive/hTPCNSigmaPrVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPr()); + rSelected.fill(HIST("Lambda/Positive/hTPCNSigmaPiVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPi()); + + rSelected.fill(HIST("Lambda/Negative/hTPCdEdxVsP"), candidate.negTPCMomentum(), candidate.negTPCSignal()); + rSelected.fill(HIST("Lambda/Negative/hTPCNSigmaPrVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPr()); + rSelected.fill(HIST("Lambda/Negative/hTPCNSigmaPiVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPi()); + } else { + rSelected.fill(HIST("AntiLambda/Event/hCentrality"), candidate.centrality()); + rSelected.fill(HIST("AntiLambda/Event/hMultiplicity"), candidate.multiplicity()); + + rSelected.fill(HIST("AntiLambda/Candidate/hPx"), candidate.px()); + rSelected.fill(HIST("AntiLambda/Candidate/hPy"), candidate.py()); + rSelected.fill(HIST("AntiLambda/Candidate/hPz"), candidate.pz()); + rSelected.fill(HIST("AntiLambda/Candidate/hPt"), candidate.pt()); + rSelected.fill(HIST("AntiLambda/Candidate/hEta"), candidate.eta()); + rSelected.fill(HIST("AntiLambda/Candidate/hPhi"), candidate.phi()); + rSelected.fill(HIST("AntiLambda/Candidate/hRapidity"), candidate.rapidity()); + rSelected.fill(HIST("AntiLambda/Candidate/hMass"), candidate.mass()); + rSelected.fill(HIST("AntiLambda/Candidate/hMassVsPt"), candidate.pt(), candidate.mass()); + + rSelected.fill(HIST("AntiLambda/Proton/hPx"), candidate.protonPx()); + rSelected.fill(HIST("AntiLambda/Proton/hPy"), candidate.protonPy()); + rSelected.fill(HIST("AntiLambda/Proton/hPz"), candidate.protonPz()); + + rSelected.fill(HIST("AntiLambda/Topology/hCosPA"), candidate.cosPA()); + rSelected.fill(HIST("AntiLambda/Topology/hDcaDaughters"), candidate.dcaDau()); + rSelected.fill(HIST("AntiLambda/Topology/hDcaV0ToPV"), candidate.dcaV0ToPV()); + rSelected.fill(HIST("AntiLambda/Topology/hProtonDcaToPV"), candidate.protonDcaToPV()); + rSelected.fill(HIST("AntiLambda/Topology/hPionDcaToPV"), candidate.pionDcaToPV()); + rSelected.fill(HIST("AntiLambda/Topology/hRadius"), candidate.v0Radius()); + + rSelected.fill(HIST("AntiLambda/Armenteros/hAlpha"), candidate.alpha()); + rSelected.fill(HIST("AntiLambda/Armenteros/hQtArm"), candidate.qtArm()); + rSelected.fill(HIST("AntiLambda/Armenteros/hAlphaVsQt"), candidate.alpha(), candidate.qtArm()); + + rSelected.fill(HIST("AntiLambda/Positive/hTPCdEdxVsP"), candidate.posTPCMomentum(), candidate.posTPCSignal()); + rSelected.fill(HIST("AntiLambda/Positive/hTPCNSigmaPrVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPr()); + rSelected.fill(HIST("AntiLambda/Positive/hTPCNSigmaPiVsP"), candidate.posTPCMomentum(), candidate.posTPCNSigmaPi()); + + rSelected.fill(HIST("AntiLambda/Negative/hTPCdEdxVsP"), candidate.negTPCMomentum(), candidate.negTPCSignal()); + rSelected.fill(HIST("AntiLambda/Negative/hTPCNSigmaPrVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPr()); + rSelected.fill(HIST("AntiLambda/Negative/hTPCNSigmaPiVsP"), candidate.negTPCMomentum(), candidate.negTPCNSigmaPi()); + } + } + // ================================================================ + // Event selection + // ================================================================ + + Configurable cutZVertex{"cutZVertex", 10.f, "Maximum absolute reconstructed vertex z"}; + + // ================================================================ + // Event selection Filter + // ================================================================ + + Filter selectedLambdaFilter = aod::lambdaorantilambda::sel8 == true && + nabs(aod::lambdaorantilambda::vertexZ) < cutZVertex; + + // ================================================================ + // Daughter-track selection + // ================================================================ + + Configurable minDaughterPt{"minDaughterPt", 0.15f, "Minimum daughter-track pT (GeV/c)"}; + + Configurable maxDaughterEta{"maxDaughterEta", 0.8f, "Maximum absolute daughter-track eta"}; + + // ===================================================================== + // Applying Daughter Filteres + // ===================================================================== + + Filter daughterKinematicFilter = + aod::lambdaorantilambda::protonPt > minDaughterPt&& + + aod::lambdaorantilambda::pionPt > minDaughterPt&& + + nabs(aod::lambdaorantilambda::protonEta) < maxDaughterEta&& + + nabs(aod::lambdaorantilambda::pionEta) < maxDaughterEta; + + // ================================================================ + // V0 topology selection + // ================================================================ + + Configurable maxDcaV0Daughters{"maxDcaV0Daughters", 1.0f, "Maximum DCA between V0 daughters (cm)"}; + + Configurable maxDcaV0ToPV{"maxDcaV0ToPV", 0.1f, "Maximum DCA of V0 to primary vertex (cm)"}; + + Configurable minDcaProtonToPV{"minDcaProtonToPV", 0.02f, "Minimum proton DCA to primary vertex (cm)"}; + + Configurable minDcaPionToPV{"minDcaPionToPV", 0.06f, "Minimum pion DCA to primary vertex (cm)"}; + + Configurable minV0Radius{"minV0Radius", 0.5f, "Minimum transverse V0 decay radius (cm)"}; + + Configurable maxV0CTau{"maxV0CTau", 30.0f, "Maximum Lambda proper decay length c-tau (cm)"}; + + Configurable minV0CosPA{"minV0CosPA", 0.995f, "Minimum cosine of V0 pointing angle"}; + + // ===================================================================== + // Applying V0 topology Filteres + // ===================================================================== + + Filter topologyFilter = + aod::lambdaorantilambda::dcaDau < maxDcaV0Daughters && + + aod::lambdaorantilambda::dcaV0ToPV < maxDcaV0ToPV && + + aod::lambdaorantilambda::protonDcaToPV > minDcaProtonToPV&& + + aod::lambdaorantilambda::pionDcaToPV > minDcaPionToPV&& + + aod::lambdaorantilambda::v0Radius > minV0Radius&& + + aod::lambdaorantilambda::cTau + minV0CosPA; + + // ================================================================ + // Lambda/anti-Lambda invariant-mass selection + // ================================================================ + + // Configurable minLambdaMass{"minLambdaMass", 1.1081f, "Minimum Lambda invariant mass (GeV/c2)"}; + + // Configurable maxLambdaMass{"maxLambdaMass", 1.1231f, "Maximum Lambda invariant mass (GeV/c2)"}; + + // ===================================================================== + // Applying invariant-mass Filteres + // ===================================================================== + + // Filter lambdaInvariantMassFilter = + // aod::lambdaorantilambda::mass > minLambdaMass && + // + // aod::lambdaorantilambda::mass < maxLambdaMass; + + // ================================================================ + // Lambda/anti-Lambda kinematic selection + // ================================================================ + + Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT (GeV/c)"}; + + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT (GeV/c)"}; + + Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; + + // ===================================================================== + // Applying Lambda/anti-Lambda kinematic Filteres + // ===================================================================== + + Filter lambdakinematicFilter = + + aod::lambdaorantilambda::pt > minLambdaPt&& + + aod::lambdaorantilambda::pt < maxLambdaPt&& + + nabs(aod::lambdaorantilambda::rapidity) < maxLambdaRapidity; + // ===================================================================== + // Applying KShort Mass Compatablilty Filtere + // ===================================================================== + + Filter kShortRejectionFilter = + aod::lambdaorantilambda::isKShortCompatible == false; + + // All compatible filters above are combined with logical AND. + using SelectedLambdaOrAntiLambdas = soa::Filtered; + + // ============================================================== + // Lambda and anti-Lambda partitions + // ============================================================== + + Partition + selectedLambdas = aod::lambdaorantilambda::isAntiLambdaHypothesis == false; + + Partition + selectedAntiLambdas = aod::lambdaorantilambda::isAntiLambdaHypothesis == true; + + void processData(SelectedLambdaOrAntiLambdas const&) + { + // ============================================================ + // Fill the final Lambda table + // ============================================================ + + for (auto const& candidate : selectedLambdas) { + + fillSelectedHistograms(false, candidate); // Lambda + + lambdaTable( + candidate.collisionId(), + candidate.centrality(), + candidate.multiplicity(), + + candidate.px(), + candidate.py(), + candidate.pz(), + + candidate.pt(), + candidate.eta(), + candidate.phi(), + + candidate.rapidity(), + candidate.mass(), + + candidate.protonPx(), + candidate.protonPy(), + candidate.protonPz(), + + candidate.posTrackId(), + candidate.negTrackId(), + + candidate.cosPA(), + candidate.dcaDau(), + candidate.dcaV0ToPV(), + + candidate.protonDcaToPV(), + candidate.pionDcaToPV(), + + candidate.v0Radius(), + candidate.alpha(), + candidate.qtArm(), + + candidate.posTPCMomentum(), + candidate.posTPCSignal(), + candidate.posTPCNSigmaPr(), + candidate.posTPCNSigmaPi(), + + candidate.negTPCMomentum(), + candidate.negTPCSignal(), + candidate.negTPCNSigmaPr(), + candidate.negTPCNSigmaPi()); + } + + // ============================================================ + // Fill the final anti-Lambda table + // ============================================================ + + for (auto const& candidate : selectedAntiLambdas) { + + fillSelectedHistograms(true, candidate); // anti-Lambda + + antiLambdaTable( + candidate.collisionId(), + candidate.centrality(), + candidate.multiplicity(), + + candidate.px(), + candidate.py(), + candidate.pz(), + + candidate.pt(), + candidate.eta(), + candidate.phi(), + + candidate.rapidity(), + candidate.mass(), + + candidate.protonPx(), + candidate.protonPy(), + candidate.protonPz(), + + candidate.posTrackId(), + candidate.negTrackId(), + + candidate.cosPA(), + candidate.dcaDau(), + candidate.dcaV0ToPV(), + + candidate.protonDcaToPV(), + candidate.pionDcaToPV(), + + candidate.v0Radius(), + candidate.alpha(), + candidate.qtArm(), + + candidate.posTPCMomentum(), + candidate.posTPCSignal(), + candidate.posTPCNSigmaPr(), + candidate.posTPCNSigmaPi(), + + candidate.negTPCMomentum(), + candidate.negTPCSignal(), + candidate.negTPCNSigmaPr(), + candidate.negTPCNSigmaPi()); + } + } + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaSelector, processData, "Run candidate production", true); + PROCESS_SWITCH(LambdaAntiLambdaSelector, processDummy, "Skip candidate production", false); +}; + +//*********************************************************************************************************** +// MC Reconstructed and MC Generated Lambda-LambdaBar Table Producer Structure +//*********************************************************************************************************** +struct LambdaAntiLambdaMcRecoTableProducer { + Produces mcRecoTable; + + // ================================================================ + // Daughter-track selection + // ================================================================ + + Configurable maxTPCNSigma{"maxTPCNSigma", 3.f, "Maximum TPC PID n-sigma"}; + Configurable kShortRejectionWindow{"kShortRejectionWindow", 0.001f, "K0Short mass rejection half-window (GeV/c2)"}; + + // ================================================================ + // Lambda/anti-Lambda invariant-mass selection + // ================================================================ + + // Configurable minLambdaMass{"minLambdaMass", 1.1081f, "Minimum Lambda invariant mass (GeV/c2)"}; + // Configurable maxLambdaMass{"maxLambdaMass", 1.1231f, "Maximum Lambda invariant mass (GeV/c2)"}; + + // ===================================================================== + // Applying invariant-mass Filteres + // ===================================================================== + + // Filter lambdaInvariantMassFilter = + // aod::v0data::mass > minLambdaMass && + // aod::v0data::mass < maxLambdaMass; + + using DaughterTracks = soa::Join; + using V0CandidatesMC = soa::Join; + // using SelectedV0CandidatesMC = soa::Filtered; + + void processMcReco( + soa::Join::iterator const& collision, + V0CandidatesMC const& V0s, DaughterTracks const&, aod::McParticles const&) + { + + for (const auto& v0 : V0s) { + + auto posTrack = v0.posTrack_as(); + auto negTrack = v0.negTrack_as(); + + const bool isKShortCompatible = + std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) <= kShortRejectionWindow.value; + + const bool isLambda = + std::abs(posTrack.tpcNSigmaPr()) < maxTPCNSigma && + std::abs(negTrack.tpcNSigmaPi()) < maxTPCNSigma; + + const bool isAntiLambda = + std::abs(negTrack.tpcNSigmaPr()) < maxTPCNSigma && + std::abs(posTrack.tpcNSigmaPi()) < maxTPCNSigma; + + if (isLambda == isAntiLambda) { + continue; + } + + // Hypothesis-dependent daughter assignments + const auto& protonTrack = isLambda ? posTrack : negTrack; + const auto& pionTrack = isLambda ? negTrack : posTrack; + + const float protonDcaToPV = isLambda ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + const float pionDcaToPV = isLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); + + // Hypothesis-dependent reconstructed invariant mass + const float reconstructedMass = + isLambda ? v0.mLambda() : v0.mAntiLambda(); + + // --------------------------------------------------------------- + // Default MC values for an unmatched reconstructed V0 + // --------------------------------------------------------------- + + const bool hasMcMatch = v0.has_mcParticle(); + + // --------------------------------------------------------------- + // Generated quantities for a truth-matched reconstructed V0 + // --------------------------------------------------------------- + + if (!hasMcMatch) { + continue; + } + const auto mcGenParticle = v0.mcParticle(); + + const int pdgCode = mcGenParticle.pdgCode(); + + // Require reconstructed PID hypothesis to agree with MC truth. + const bool correctSpecies = + (isLambda && pdgCode == PDG_t::kLambda0) || + (isAntiLambda && pdgCode == PDG_t::kLambda0Bar); + + if (!correctSpecies) { + continue; + } + + const bool isTrueAntiLambda = (pdgCode == PDG_t::kLambda0Bar); + const bool isPhysicalPrimary = mcGenParticle.isPhysicalPrimary(); + + const float genPx = mcGenParticle.px(); + const float genPy = mcGenParticle.py(); + const float genPz = mcGenParticle.pz(); + const float genPt = mcGenParticle.pt(); + const float genEta = mcGenParticle.eta(); + const float genPhi = mcGenParticle.phi(); + const float genRapidity = mcGenParticle.y(); + + // --------------------------------------------------------------- + // Quantities calculated from the reconstructed candidate + // --------------------------------------------------------------- + + const float cTau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + const float ptResolution = std::abs(v0.pt() - genPt); + // --------------------------------------------------------------- + // Fill exactly one row for every reconstructed V0 + // --------------------------------------------------------------- + + mcRecoTable( + // Event + collision.globalIndex(), + collision.sel8(), + collision.posZ(), + + // MC truth matching + hasMcMatch, + isTrueAntiLambda, + isPhysicalPrimary, + + // Generated V0 kinematics + genPx, + genPy, + genPz, + genPt, + genEta, + genPhi, + genRapidity, + + // Reconstructed V0 kinematics + v0.px(), + v0.py(), + v0.pz(), + v0.pt(), + v0.eta(), + v0.phi(), + v0.yLambda(), + + //|pTReco-pTGen| + ptResolution, + // Reconstructed mass hypotheses + reconstructedMass, + + // Reconstructed topology + v0.dcaV0daughters(), + v0.dcav0topv(), + v0.v0cosPA(), + v0.v0radius(), + cTau, + + // Positive daughter + protonTrack.px(), + protonTrack.py(), + protonTrack.pz(), + protonTrack.pt(), + protonTrack.eta(), + protonTrack.phi(), + protonDcaToPV, + static_cast(protonTrack.tpcNClsCrossedRows()), + protonTrack.tpcNSigmaPr(), + + // Negative daughter + pionTrack.px(), + pionTrack.py(), + pionTrack.pz(), + pionTrack.pt(), + pionTrack.eta(), + pionTrack.phi(), + pionDcaToPV, + static_cast(pionTrack.tpcNClsCrossedRows()), + pionTrack.tpcNSigmaPi(), + isKShortCompatible); + } + } + + Configurable cutZVertexMcGen{"cutZVertexMcGen", 10.f, "Maximum reconstructed vertex z for MCGen event selection"}; + + using RecoCollisionsMC = soa::Join; + + Produces mcGenEventSelectedTable; + + void processEveSelPassedMcGenTable(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups const& recoCollisions) + { + int nRecoCollisions = 0; + int nSel8Collisions = 0; + + for (const auto& recoCollision : recoCollisions) { + ++nRecoCollisions; + + if (recoCollision.sel8() && std::abs(recoCollision.posZ()) < cutZVertexMcGen) { + ++nSel8Collisions; + } + } + + mcGenEventSelectedTable( + mcCollision.globalIndex(), + nRecoCollisions, + nSel8Collisions); + } + + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaMcRecoTableProducer, processDummy, "Do nothing on real data", true); + PROCESS_SWITCH(LambdaAntiLambdaMcRecoTableProducer, processMcReco, "Produce MC reconstructed table", false); + PROCESS_SWITCH(LambdaAntiLambdaMcRecoTableProducer, processEveSelPassedMcGenTable, "Produce MC generated table", false); +}; +//*********************************************************************************************************** +// Using Produced Lambda-LambdaBar MC Reconstructed and MC Generated Lambda-LambdaBar For Efficiency Plots +//*********************************************************************************************************** +struct LambdaAntiLambdaEfficiencyPlots { + HistogramRegistry rEfficiencyPlots{"EfficiencyPlotsy", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Defining Plot axis for later purpose + ConfigurableAxis axisInvariantMass{"axisInvariantMass", {100, 1.08f, 1.2f}, "Invariant-mass axis"}; + ConfigurableAxis axisMcRecoPt{"axisMcRecoPt", {200, 0.f, 10.f}, "McReco transverse-momentum axis"}; + ConfigurableAxis axisGenPt{"axisGenPt", {200, 0.f, 10.f}, "McGen transverse-momentum axis"}; + ConfigurableAxis axisDeltaPt{"axisDeltaPt", {200, 0.f, 2.f}, "|pt_{Reco} - pT_{Gen}|"}; + ConfigurableAxis axisMcRecoEta{"axisMcRecoEta", {100, -2.f, 2.f}, "McReco eta axis"}; + ConfigurableAxis axisMcGenEta{"axisMcGenEta", {100, -2.f, 2.f}, "McGen eta axis"}; + + // ========================Filters====================================== + //~~~~~~~~~~~~~~~~~~~~~~~McRecoFillters~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // ================================================================ + // Event selection Filters + // ================================================================ + + Configurable cutZVertex{"cutZVertex", 10.f, "Maximum absolute reconstructed vertex z"}; + + Filter mcRecoEventFilter = aod::mcrecolambda::sel8 == true && + nabs(aod::mcrecolambda::vertexZ) < cutZVertex && + aod::mcrecolambda::isPhysicalPrimary == true; + // ================================================================ + // Topological Selection Filters + // ================================================================ + + Configurable maxDcaV0Daughters{"maxDcaV0Daughters", 1.0f, "Maximum DCA between V0 daughters"}; + Configurable maxDcaV0ToPV{"maxDcaV0ToPV", 0.1f, "Maximum V0 DCA to primary vertex"}; + Configurable minDcaProtonToPV{"minDcaProtonToPV", 0.02f, "Minimum proton DCA to primary vertex"}; + Configurable minDcaPionToPV{"minDcaPionToPV", 0.06f, "Minimum pion DCA to primary vertex"}; + Configurable minV0Radius{"minV0Radius", 0.5f, "Minimum V0 decay radius"}; + Configurable maxV0CTau{"maxV0CTau", 30.0f, "Maximum Lambda c-tau"}; + Configurable minV0CosPA{"minV0CosPA", 0.995f, "Minimum V0 cosine of pointing angle"}; + + Filter mcRecoTopologyFilter = aod::mcrecolambda::dcaDaughters < maxDcaV0Daughters && + nabs(aod::mcrecolambda::dcaV0ToPV) < maxDcaV0ToPV && + aod::mcrecolambda::proDcaToPV > minDcaProtonToPV&& + aod::mcrecolambda::pieDcaToPV > minDcaPionToPV&& + aod::mcrecolambda::v0Radius > minV0Radius&& + aod::mcrecolambda::cTau + minV0CosPA; + + // ================================================================ + // kinematics Filter + // ================================================================ + + Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT"}; + Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; + + Filter mcRecoKinematicFilter = aod::mcrecolambda::recoPt > minLambdaPt&& + aod::mcrecolambda::recoPt < maxLambdaPt&& + nabs(aod::mcrecolambda::recoRapidity) < maxLambdaRapidity; + + // ================================================================ + // Daughter-track selection + // ================================================================ + + Configurable minDaughterPt{"minDaughterPt", 0.15f, "Minimum daughter-track pT"}; + Configurable maxDaughterEta{"maxDaughterEta", 0.8f, "Maximum absolute daughter-track eta"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum TPC crossed rows"}; + + Filter mcRecoDaughterFilter = aod::mcrecolambda::proPt > minDaughterPt&& aod::mcrecolambda::piePt > minDaughterPt&& + nabs(aod::mcrecolambda::proEta) < maxDaughterEta&& nabs(aod::mcrecolambda::pieEta) < maxDaughterEta&& aod::mcrecolambda::proTPCCrossedRows + >= minTPCCrossedRows&& aod::mcrecolambda::pieTPCCrossedRows >= minTPCCrossedRows; + + Configurable minLambdaMass{"minLambdaMass", 1.11f, "Minimum Lambda mass (GeV/c2)"}; + Configurable maxLambdaMass{"maxLambdaMass", 1.12f, "Maximum Lambda mass (GeV/c2)"}; + + Filter mcRecoMassFilter = aod::mcrecolambda::massLambda >= minLambdaMass && aod::mcrecolambda::massLambda <= maxLambdaMass; + + Filter mcRecoKShortRejectionFilter = aod::mcrecolambda::isKShortCompatible == false; + + void addEfficiencyPlotsHistograms(std::string const& particle) + { + const std::string recoDirectory = particle + "/Reco/"; + const std::string genDirectory = particle + "/Gen/"; + + rEfficiencyPlots.add((recoDirectory + "hMcRecoPt").c_str(), "Reconstructed p_{T};p_{T} (GeV/c);Entries", HistType::kTH1F, {axisMcRecoPt}); + rEfficiencyPlots.add((recoDirectory + "hAssoMcGenPt").c_str(), "Reco Accociated Gen p_{T};p_{T} (GeV/c);Entries", HistType::kTH1F, {axisGenPt}); + rEfficiencyPlots.add((recoDirectory + "hMcRecoEta").c_str(), "Reconstructed #eta;#eta;Entries", HistType::kTH1F, {axisMcRecoEta}); + rEfficiencyPlots.add((recoDirectory + "hAssoMcGenEta").c_str(), "Reco Accociated Gen #eta;#eta;Entries", HistType::kTH1F, {axisMcGenEta}); + rEfficiencyPlots.add((recoDirectory + "hPtResolution").c_str(), "|pT_{Reco} - pT_{Gen}|", HistType::kTH1F, {axisDeltaPt}); + rEfficiencyPlots.add((recoDirectory + "hInvariantMass").c_str(), "Reconstructed invariant mass;m_{p#pi} (GeV/c^{2});entries", HistType::kTH1F, {axisInvariantMass}); + + rEfficiencyPlots.add((genDirectory + "hPt").c_str(), "Generated p_{T};p_{T} (GeV/c);Entries", HistType::kTH1F, {axisGenPt}); + rEfficiencyPlots.add((genDirectory + "hEta").c_str(), "Generated #eta;#eta;Entries", HistType::kTH1F, {axisMcGenEta}); + rEfficiencyPlots.add((genDirectory + "PrimarAndNonPrimary").c_str(), "Generated Primary And Non-Primary Genp_{T};Genp_{T} (GeV/c);Entries", HistType::kTH1F, {axisGenPt}); + } + + void init(InitContext const&) + { + addEfficiencyPlotsHistograms("Lambda"); + addEfficiencyPlotsHistograms("AntiLambda"); + } + + using SelectedMcRecoLambdas = soa::Filtered; + + Partition selectedMcRecoLambdas = aod::mcrecolambda::isAntiLambda == false; + Partition selectedMcRecoAntiLambdas = aod::mcrecolambda::isAntiLambda == true; + + void processEfficiencyMcReco(SelectedMcRecoLambdas const&) + { + for (const auto& lambda : selectedMcRecoLambdas) { + rEfficiencyPlots.fill(HIST("Lambda/Reco/hMcRecoPt"), lambda.recoPt()); + rEfficiencyPlots.fill(HIST("Lambda/Reco/hAssoMcGenPt"), lambda.genPt()); + rEfficiencyPlots.fill(HIST("Lambda/Reco/hMcRecoEta"), lambda.recoEta()); + rEfficiencyPlots.fill(HIST("Lambda/Reco/hAssoMcGenEta"), lambda.genEta()); + rEfficiencyPlots.fill(HIST("Lambda/Reco/hPtResolution"), lambda.ptResolution()); + rEfficiencyPlots.fill(HIST("Lambda/Reco/hInvariantMass"), lambda.massLambda()); + } + + for (const auto& antiLambda : selectedMcRecoAntiLambdas) { + rEfficiencyPlots.fill(HIST("AntiLambda/Reco/hMcRecoPt"), antiLambda.recoPt()); + rEfficiencyPlots.fill(HIST("AntiLambda/Reco/hAssoMcGenPt"), antiLambda.genPt()); + rEfficiencyPlots.fill(HIST("AntiLambda/Reco/hMcRecoEta"), antiLambda.recoEta()); + rEfficiencyPlots.fill(HIST("AntiLambda/Reco/hAssoMcGenEta"), antiLambda.genEta()); + rEfficiencyPlots.fill(HIST("AntiLambda/Reco/hPtResolution"), antiLambda.ptResolution()); + rEfficiencyPlots.fill(HIST("AntiLambda/Reco/hInvariantMass"), antiLambda.massLambda()); + } + } + PROCESS_SWITCH(LambdaAntiLambdaEfficiencyPlots, processEfficiencyMcReco, "Fill MC reconstructed efficiency histograms", false); + + // McGen Filters + Filter mcGenEventFilter = aod::eveselpassmcgenevent::nSel8Collisions > 0; + + using SelectedMcGenEvents = soa::Filtered; + Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; + + void processEfficiencyMcGen(SelectedMcGenEvents::iterator const& selectedMcEvent, aod::McParticles const& allMcParticles) + { + // We will slice the particles here later. + const auto particlesFromThisMcCollision = allMcParticles.sliceBy(mcParticlesPerMcCollision, selectedMcEvent.mcCollisionId()); + + // Reconstructed-collision-opportunity weight + const auto weight = static_cast(selectedMcEvent.nSel8Collisions()); + + for (const auto& particle : particlesFromThisMcCollision) { + + // Generated kinematic acceptance + if (particle.pt() <= minLambdaPt || particle.pt() >= maxLambdaPt) { + continue; + } + if (std::abs(particle.y()) >= maxLambdaRapidity) { + continue; + } + // Lambda or anti-Lambda + + if (particle.pdgCode() == PDG_t::kLambda0) { + // Inclusive: primary + non-primary Lambda + rEfficiencyPlots.fill(HIST("Lambda/Gen/PrimarAndNonPrimary"), particle.pt(), weight); + + if (!particle.isPhysicalPrimary()) { + continue; + } + + rEfficiencyPlots.fill(HIST("Lambda/Gen/hPt"), particle.pt(), weight); + rEfficiencyPlots.fill(HIST("Lambda/Gen/hEta"), particle.eta(), weight); + + } else if (particle.pdgCode() == PDG_t::kLambda0Bar) { + // Inclusive: primary + non-primary anti-Lambda + rEfficiencyPlots.fill(HIST("AntiLambda/Gen/PrimarAndNonPrimary"), particle.pt(), weight); + + if (!particle.isPhysicalPrimary()) { + continue; + } + + rEfficiencyPlots.fill(HIST("AntiLambda/Gen/hPt"), particle.pt(), weight); + rEfficiencyPlots.fill(HIST("AntiLambda/Gen/hEta"), particle.eta(), weight); + } + } + } + + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaEfficiencyPlots, processDummy, "Do nothing on real data", true); + PROCESS_SWITCH(LambdaAntiLambdaEfficiencyPlots, processEfficiencyMcGen, "Fill MC-generated efficiency histograms", false); +}; + +struct LambdaAntiLambdaSelectionCutFlow { + HistogramRegistry cutFlowRegistry{"cutFlowRegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Event selection + Configurable cutZVertex{"cutZVertex", 10.f, "Maximum absolute reconstructed vertex z"}; + + // Daughter selection + Configurable minDaughterPt{"minDaughterPt", 0.15f, "Minimum daughter pT"}; + Configurable maxDaughterEta{"maxDaughterEta", 0.8f, "Maximum daughter absolute eta"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum TPC crossed rows"}; + + // Topology + Configurable maxDcaV0Daughters{"maxDcaV0Daughters", 1.f, "Maximum daughter DCA"}; + Configurable maxDcaV0ToPV{"maxDcaV0ToPV", 0.1f, "Maximum V0 DCA to PV"}; + Configurable minDcaProtonToPV{"minDcaProtonToPV", 0.02f, "Minimum proton DCA to PV"}; + Configurable minDcaPionToPV{"minDcaPionToPV", 0.06f, "Minimum pion DCA to PV"}; + Configurable minV0Radius{"minV0Radius", 0.5f, "Minimum V0 radius"}; + Configurable maxV0CTau{"maxV0CTau", 30.f, "Maximum Lambda c-tau"}; + Configurable minV0CosPA{"minV0CosPA", 0.995f, "Minimum cosine of pointing angle"}; + + // Lambda kinematics + Configurable minLambdaPt{"minLambdaPt", 0.5f, "Minimum Lambda pT"}; + Configurable maxLambdaPt{"maxLambdaPt", 5.0f, "Maximum Lambda pT"}; + Configurable maxLambdaRapidity{"maxLambdaRapidity", 0.5f, "Maximum absolute Lambda rapidity"}; + + // Lambda Mass Cuts + Configurable minLambdaMass{"minLambdaMass", 1.11f, "Minimum Lambda mass (GeV/c2)"}; + Configurable maxLambdaMass{"maxLambdaMass", 1.12f, "Maximum Lambda mass (GeV/c2)"}; + + void init(InitContext const&) + { + AxisSpec eventCutAxis{3, -0.5, 2.5, "Event-selection stage"}; + + AxisSpec lambdaCutAxis{10, -0.5, 9.5, "Candidate-selection stage"}; + + cutFlowRegistry.add("Event/hCutFlow", "Event cut flow;Selection;Collisions", HistType::kTH1D, {eventCutAxis}); + cutFlowRegistry.add("Lambda/hCutFlow", "Lambda cut flow;Selection;Candidates", HistType::kTH1D, {lambdaCutAxis}); + cutFlowRegistry.add("AntiLambda/hCutFlow", "Anti-Lambda cut flow;Selection;Candidates", HistType::kTH1D, {lambdaCutAxis}); + + auto eventHistogram = cutFlowRegistry.get(HIST("Event/hCutFlow")); + + eventHistogram->GetXaxis()->SetBinLabel(1, "All collisions"); + eventHistogram->GetXaxis()->SetBinLabel(2, "sel8"); + eventHistogram->GetXaxis()->SetBinLabel(3, "Vertex z"); + + auto lambdaHistogram = cutFlowRegistry.get(HIST("Lambda/hCutFlow")); + + auto antiLambdaHistogram = cutFlowRegistry.get(HIST("AntiLambda/hCutFlow")); + + for (const auto& histogram : {lambdaHistogram, antiLambdaHistogram}) { + histogram->GetXaxis()->SetBinLabel(1, "Truth matched + PID selected"); + histogram->GetXaxis()->SetBinLabel(2, "sel8"); + histogram->GetXaxis()->SetBinLabel(3, "Vertex z"); + histogram->GetXaxis()->SetBinLabel(4, "Physical primary"); + histogram->GetXaxis()->SetBinLabel(5, "Daughter acceptance"); + histogram->GetXaxis()->SetBinLabel(6, "TPC crossed rows"); + histogram->GetXaxis()->SetBinLabel(7, "V0 topology"); + histogram->GetXaxis()->SetBinLabel(8, "Lambda kinematics"); + histogram->GetXaxis()->SetBinLabel(9, "K0Short rejection"); + histogram->GetXaxis()->SetBinLabel(10, "Lambda mass window"); + } + } + + using ReconstructedEvents = soa::Join; + + void processEventCutFlow(ReconstructedEvents::iterator const& collision) + { + cutFlowRegistry.fill(HIST("Event/hCutFlow"), 0.f); + + if (!collision.sel8()) { + return; + } + + cutFlowRegistry.fill(HIST("Event/hCutFlow"), 1.f); + + if (std::abs(collision.posZ()) >= cutZVertex) { + return; + } + + cutFlowRegistry.fill(HIST("Event/hCutFlow"), 2.f); + } + + void processLambdaCutFlow(aod::McRecoLambdaCandidates const& candidates) + { + for (const auto& candidate : candidates) { + const bool isAntiLambda = candidate.isAntiLambda(); + + auto fillStage = [&](float stage) { + if (isAntiLambda) { + cutFlowRegistry.fill(HIST("AntiLambda/hCutFlow"), stage); + } else { + cutFlowRegistry.fill(HIST("Lambda/hCutFlow"), stage); + } + }; + + // PID, truth match and correct species were already + // required by LambdaAntiLambdaMcRecoTableProducer. + fillStage(0.f); + + if (!candidate.sel8()) { + continue; + } + fillStage(1.f); + + if (std::abs(candidate.vertexZ()) >= cutZVertex) { + continue; + } + fillStage(2.f); + + if (!candidate.isPhysicalPrimary()) { + continue; + } + fillStage(3.f); + + if (candidate.proPt() <= minDaughterPt || candidate.piePt() <= minDaughterPt || + std::abs(candidate.proEta()) >= maxDaughterEta || + std::abs(candidate.pieEta()) >= maxDaughterEta) { + continue; + } + fillStage(4.f); + + if (candidate.proTPCCrossedRows() < minTPCCrossedRows || + candidate.pieTPCCrossedRows() < minTPCCrossedRows) { + continue; + } + fillStage(5.f); + + if (candidate.dcaDaughters() >= maxDcaV0Daughters || std::abs(candidate.dcaV0ToPV()) >= maxDcaV0ToPV || + candidate.proDcaToPV() <= minDcaProtonToPV || candidate.pieDcaToPV() <= minDcaPionToPV || + candidate.v0Radius() <= minV0Radius || candidate.cTau() >= maxV0CTau || + candidate.cosPA() <= minV0CosPA) { + continue; + } + fillStage(6.f); + + if (candidate.recoPt() <= minLambdaPt || candidate.recoPt() >= maxLambdaPt || + std::abs(candidate.recoRapidity()) >= maxLambdaRapidity) { + continue; + } + fillStage(7.f); + + if (candidate.isKShortCompatible()) { + continue; + } + fillStage(8.f); + + if (candidate.massLambda() < minLambdaMass.value || + candidate.massLambda() > maxLambdaMass.value) { + continue; + } + fillStage(9.f); + } + } + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaSelectionCutFlow, processDummy, "Do nothing on real data", true); + PROCESS_SWITCH(LambdaAntiLambdaSelectionCutFlow, processEventCutFlow, "Fill reconstructed-event cut flow", false); + PROCESS_SWITCH(LambdaAntiLambdaSelectionCutFlow, processLambdaCutFlow, "Fill MCReco Lambda cut flow", false); +}; + +struct LambdaAntiLambdaPairAnalysis { + // Defining Histogram registry + HistogramRegistry rSpinAnalysis{"SpinPairAnalysis", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // defining Axis for histograms + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {100, -2.f, 2.f}, "#Delta#eta axis"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -o2::constants::math::PI, o2::constants::math::PI}, "#Delta#phi axis"}; + ConfigurableAxis axisDeltaPt{"axisDeltaPt", {400, -10.f, 10.f}, "#Delta#p_{T} axis"}; + + ConfigurableAxis axisPt{"axisPt", {200, 0.f, 10.f}, "transverse-momentum axis"}; + ConfigurableAxis axisEta{"axisEta", {100, -2.f, 2.f}, "#eta axis"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0, o2::constants::math::TwoPI}, "#phi axis"}; + + ConfigurableAxis axisInvariantMass{"axisInvariantMass", {100, 1.08f, 1.2f}, "Invariant-mass axis"}; + ConfigurableAxis axisDeltaMass{"axisDeltaMass", {100, -0.12f, 0.12f}, "Invariant-mass axis"}; + // ConfigurableAxis axisNumberOfPairs{"axisNumberOfPairs", {1001, -0.5f, 1000.5f}, "NumberOfPairs axis"}; + + ConfigurableAxis axisCos{"axisCos", {200, -1.f, 1.f}, "#Cos#theta axis"}; + ConfigurableAxis axisMult{"axisMult", {200, 0.f, 200.f}, "Multiplicity axis"}; + ConfigurableAxis axisDeltaMult{"axisDeltaMult", {400, -200.f, 200.f}, "Delta Multiplicity axis"}; + ConfigurableAxis axisCent{"axisCent", {100, 0.f, 100.f}, "Centrality axis"}; + ConfigurableAxis axisDeltaCent{"axisDeltaCent", {200, -100.f, 100.f}, "Delta Centrality axis"}; + + // Mixed-Event Compatablity variables + Configurable mixedEventMaxDeltaMultiplicity{"mixedEventMaxDeltaMultiplicity", 10.f, "Maximum multiplicity difference between mixed events"}; + Configurable maxMixedEvents{"maxMixedEvents", 5, "Maximum multiplicity-compatible collision combinations with selected candidates in both collisions per dataframe"}; + + // Short-range pairs + Configurable sameEventShortRangePairMaxDeltaRapidity{"sameEventShortRangePairMaxDeltaRapidity", 0.5f, "ShortRangePai Maximum absolute rapidity difference for same-event pairs"}; + Configurable sameEventShortRangePairMaxDeltaPhi{"sameEventShortRangePairMaxDeltaPhi", 1.047f, "ShortRangePai Maximum absolute wrapped phi difference for same-event pairs"}; // Pi/3 =1.047 + + // For JackKnife Method of Error estemation + Configurable jackknife{"jackknife", false, "Enable leave-one-group-out jackknife"}; + Configurable jackknifeExcludedGroup{"jackknifeExcludedGroup", 0, "Group to exclude: 0 to 9"}; + static constexpr int NJackknifeGroups = 10; + + void addHistograms(TString const& candidate1, TString const& candidate2) + { + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Same-Event~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // EventConter histogram + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/EventCount", "Same-event" + candidate1 + candidate2 + + "pairs;" + "SameEvent", + HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/NumberOfPairs", "Same-event pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + // Delta Kinaematics histos + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/DeltaEtaDelatPhi", "Same-event" + candidate1 + candidate2 + + "pairs;" + "#Delta#eta;" + "#Delta#phi", + HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/DeltaPtDelatEta", "Same-event" + candidate1 + candidate2 + + "pairs;" + "#Delta#p_{T};" + "#Delta#eta", + HistType::kTH2F, {axisDeltaPt, axisDeltaEta}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/DeltaPtDelatPhi", "Same-event" + candidate1 + candidate2 + + "pairs;" + "#Delta#p_{T};" + "#Delta#phi", + HistType::kTH2F, {axisDeltaPt, axisDeltaPhi}); + // Invarient mass and Cent histos + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/DeltaMassDeltaCent", "Same-event" + candidate1 + candidate2 + + "pairs;" + "#Delta m_{#pi#p} ;" + "DeltaCent", + HistType::kTH2F, {axisDeltaMass, axisDeltaCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/DeltaMassDeltaPt", "Same-event" + candidate1 + candidate2 + + "pairs;" + "#Delta m_{#pi#p} ;" + "Deltap_{T}", + HistType::kTH2F, {axisDeltaMass, axisDeltaPt}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/DeltaCentDeltaMult", "Same-event" + candidate1 + candidate2 + + "pairs;" + "DeltaCent ;" + "DeltaMult", + HistType::kTH2F, {axisDeltaCent, axisDeltaMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/Mass", "Same-event" + candidate1 + candidate2 + + "pairs;" + "Mass-Lambda ;" + "Mass-AntiLambda", + HistType::kTH2F, {axisInvariantMass, axisInvariantMass}); + + // ================================================================ + // Same-event short-range spin histograms + // |Delta y| < 0.5 and |Delta phi| < pi/3 + // ================================================================ + + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/ShortRange/CosMult", "Same-event short-range " + candidate1 + candidate2 + " pairs;cos#theta;Mult", HistType::kTH2F, {axisCos, axisMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/ShortRange/CosCent", "Same-event short-range " + candidate1 + candidate2 + " pairs;cos#theta;Cent", HistType::kTH2F, {axisCos, axisCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/ShortRange/Cos", "Same-event short-range " + candidate1 + candidate2 + " pairs;cos#theta", HistType::kTH1F, {axisCos}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/ShortRange/NumberOfPairs", "Same-event short-range pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/ShortRange/EventCount", "Events containing at least one short-range pair;Counter;Events", HistType::kTH1F, {{1, -0.5, 0.5}}); + + // ================================================================ + // Same-event long-range spin histograms + // Complement of the short-range selection + // ================================================================ + + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/LongRange/CosMult", "Same-event long-range " + candidate1 + candidate2 + " pairs;cos#theta;Mult", HistType::kTH2F, {axisCos, axisMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/LongRange/CosCent", "Same-event long-range " + candidate1 + candidate2 + " pairs;cos#theta;Cent", HistType::kTH2F, {axisCos, axisCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/LongRange/Cos", "Same-event long-range " + candidate1 + candidate2 + " pairs;cos#theta", HistType::kTH1F, {axisCos}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/LongRange/NumberOfPairs", "Same-event long-range pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hSameEvent/LongRange/EventCount", "Events containing at least one long-range pair;Counter;Events", HistType::kTH1F, {{1, -0.5, 0.5}}); + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Mixed-Event~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // EventConter histogram + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/EventCount", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "MixedEvent", + HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/EventCount", "Mixed-event short-range " + candidate1 + candidate2 + + "pairs;" + "MixedEvent", + HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/EventCount", "Mixed-event long-range " + candidate1 + candidate2 + + "pairs;" + "MixedEvent", + HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/NumberOfPairs", "Mixed-event pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/NumberOfPairs", "Mixed-event short-range pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/NumberOfPairs", "Mixed-event long-range pair count;Counter;Accepted pairs", HistType::kTH1F, {{1, -0.5, 0.5}}); + + // Delta Kinaematics histos + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/DeltaEtaDelatPhi", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "#Delta#eta;" + "#Delta#phi", + HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/DeltaPtDelatEta", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "#Delta#p_{T};" + "#Delta#eta", + HistType::kTH2F, {axisDeltaPt, axisDeltaEta}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/DeltaPtDelatPhi", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "#Delta#p_{T};" + "#Delta#phi", + HistType::kTH2F, {axisDeltaPt, axisDeltaPhi}); + // Invarient mass and Cent histos + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/DeltaMassDeltaCent", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "#Delta m_{#pi#p} ;" + "DeltaCent", + HistType::kTH2F, {axisDeltaMass, axisDeltaCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/DeltaMassDeltaPt", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "#Delta m_{#pi#p} ;" + "Deltap_{T}", + HistType::kTH2F, {axisDeltaMass, axisDeltaPt}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/DeltaCentDeltaMult", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "DeltaCent ;" + "DeltaMult", + HistType::kTH2F, {axisDeltaCent, axisDeltaMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/Mass", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "Mass-Lambda ;" + "Mass-AntiLambda", + HistType::kTH2F, {axisInvariantMass, axisInvariantMass}); + + // Spin histos + // 2d + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/CosMult", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Mult" + + candidate1 + + ";" + "Mult" + + candidate2, + HistType::kTH3F, {axisCos, axisMult, axisMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/CosMult", "Mixed-event short-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Mult" + + candidate1 + + ";" + "Mult" + + candidate2, + HistType::kTH3F, {axisCos, axisMult, axisMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/CosMult", "Mixed-event long-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Mult" + + candidate1 + + ";" + "Mult" + + candidate2, + HistType::kTH3F, {axisCos, axisMult, axisMult}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/CosCent", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Cent" + + candidate1 + + ";" + "Cent" + + candidate2, + HistType::kTH3F, {axisCos, axisCent, axisCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/CosCent", "Mixed-event short-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Cent" + + candidate1 + + ";" + "Cent" + + candidate2, + HistType::kTH3F, {axisCos, axisCent, axisCent}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/CosCent", "Mixed-event long-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta;" + "Cent" + + candidate1 + + ";" + "Cent" + + candidate2, + HistType::kTH3F, {axisCos, axisCent, axisCent}); + // 1d + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/Cos", "Mixed-event" + candidate1 + candidate2 + + "pairs;" + "cos#theta", + HistType::kTH1F, {axisCos}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/ShortRange/Cos", "Mixed-event short-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta", + HistType::kTH1F, {axisCos}); + rSpinAnalysis.add(candidate1 + candidate2 + "/hMixedEvent/LongRange/Cos", "Mixed-event long-range" + candidate1 + candidate2 + + "pairs;" + "cos#theta", + HistType::kTH1F, {axisCos}); + } + + void init(InitContext const&) + { + addHistograms("Lambda", "AntiLambda"); + addHistograms("Lambda", "Lambda"); + addHistograms("AntiLambda", "AntiLambda"); + if (jackknife.value && (jackknifeExcludedGroup.value < 0 || jackknifeExcludedGroup.value >= NJackknifeGroups)) { + LOGF(fatal, "jackknifeExcludedGroup must be between 0 and 9"); + } + } + + ROOT::Math::PxPyPzMVector daughterInParentRestFrame(float parentLambdaPx, float parentLambdaPy, float parentLambdaPz, float parentLambdaMass, float daughterProtonPx, float daughterProtonPy, float daughterProtonPz, float daughterProtonMass) + { + + ROOT::Math::PxPyPzMVector parentFourVector{parentLambdaPx, parentLambdaPy, parentLambdaPz, parentLambdaMass}; + ROOT::Math::PxPyPzMVector daughterFourVector{daughterProtonPx, daughterProtonPy, daughterProtonPz, daughterProtonMass}; + + ROOT::Math::Boost boostToParentRest{parentFourVector.BoostToCM()}; // returns inverse boost requiret make lambda momentum zero i.e rest fram of lambda + const auto protonStar = boostToParentRest(daughterFourVector); // Applies the store inverse boost of the lambda rest fram to the proton + + return protonStar; + } + + float cosTheta(ROOT::Math::PxPyPzMVector const& proton1, + ROOT::Math::PxPyPzMVector const& proton2) + { + const float numerator = proton1.Px() * proton2.Px() + proton1.Py() * proton2.Py() + proton1.Pz() * proton2.Pz(); + const float denominator = proton1.P() * proton2.P(); + + if (denominator == 0.f) { + return -2.f; + } + return numerator / denominator; + } + + template + bool isSameEventLongRange(Candidate1 const& candidate1, Candidate2 const& candidate2) + { + const float deltaPhi = std::abs(std::remainder(candidate1.phi() - candidate2.phi(), o2::constants::math::TwoPI)); + const float deltaRapidity = std::abs(candidate1.rapidity() - candidate2.rapidity()); + + return deltaPhi > sameEventShortRangePairMaxDeltaPhi && + deltaRapidity > sameEventShortRangePairMaxDeltaRapidity; + } + + template + bool isSameEventShortRange(Candidate1 const& candidate1, Candidate2 const& candidate2) + { + const float deltaRapidity = std::abs(candidate1.rapidity() - candidate2.rapidity()); + + const float deltaPhi = std::abs(std::remainder(candidate1.phi() - candidate2.phi(), o2::constants::math::TwoPI)); + + return deltaRapidity < sameEventShortRangePairMaxDeltaRapidity && + deltaPhi < sameEventShortRangePairMaxDeltaPhi; + } + + template + void fillLambdaAntiLambdaSameEvent(LambdaSlice const& lambdas, AntiLambdaSlice const& antiLambdas) + { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/EventCount"), 0); + + bool hasShortRangePair = false; + bool hasLongRangePair = false; + + // Lambda-AntiLambda pairs + for (auto const& [lambda, antiLambda] : combinations(CombinationsFullIndexPolicy(lambdas, antiLambdas))) { + + // Boost the proton into the Lambda rest frame. + const auto protonStar = daughterInParentRestFrame(lambda.px(), lambda.py(), lambda.pz(), lambda.mass(), lambda.protonPx(), lambda.protonPy(), lambda.protonPz(), o2::constants::physics::MassProton); + // Boost the antiproton into the anti-Lambda rest frame. + const auto antiProtonStar = daughterInParentRestFrame(antiLambda.px(), antiLambda.py(), antiLambda.pz(), antiLambda.mass(), antiLambda.protonPx(), antiLambda.protonPy(), antiLambda.protonPz(), o2::constants::physics::MassProton); + + const float cosDeltaThetaStar = cosTheta(protonStar, antiProtonStar); + const float deltaPhi = std::remainder(lambda.phi() - antiLambda.phi(), o2::constants::math::TwoPI); + // Invalid result returned by cosTheta(). + if (cosDeltaThetaStar < -1.f) { + continue; + } + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/DeltaEtaDelatPhi"), lambda.eta() - antiLambda.eta(), deltaPhi); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/DeltaPtDelatEta"), lambda.pt() - antiLambda.pt(), lambda.eta() - antiLambda.eta()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/DeltaPtDelatPhi"), lambda.pt() - antiLambda.pt(), deltaPhi); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/DeltaMassDeltaCent"), lambda.mass() - antiLambda.mass(), lambda.centrality() - antiLambda.centrality()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/DeltaMassDeltaPt"), lambda.mass() - antiLambda.mass(), lambda.pt() - antiLambda.pt()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/DeltaCentDeltaMult"), lambda.centrality() - antiLambda.centrality(), lambda.multiplicity() - antiLambda.multiplicity()); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/Mass"), lambda.mass(), antiLambda.mass()); + + const bool isShortRange = isSameEventShortRange(lambda, antiLambda); + const bool isLongRange = isSameEventLongRange(lambda, antiLambda); + + if (isShortRange) { + hasShortRangePair = true; + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda.centrality()); + } else if (isLongRange) { + hasLongRangePair = true; + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda.centrality()); + } + } + if (hasShortRangePair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/ShortRange/EventCount"), 0); + } + if (hasLongRangePair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hSameEvent/LongRange/EventCount"), 0); + } + } + + template + void fillLambdaLambdaSameEvent(LambdaSlice const& lambdas) + { + + bool hasShortRangePair = false; + bool hasLongRangePair = false; + + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/EventCount"), 0); + + // Lambda-Lamda pairs + for (auto const& [lambda1, lambda2] : combinations(CombinationsStrictlyUpperIndexPolicy(lambdas, lambdas))) { + + // Boost the proton into the Lambda rest frame. + const auto protonStar1 = daughterInParentRestFrame(lambda1.px(), lambda1.py(), lambda1.pz(), lambda1.mass(), lambda1.protonPx(), lambda1.protonPy(), lambda1.protonPz(), o2::constants::physics::MassProton); + // Boost the antiproton into the anti-Lambda rest frame. + const auto protonStar2 = daughterInParentRestFrame(lambda2.px(), lambda2.py(), lambda2.pz(), lambda2.mass(), lambda2.protonPx(), lambda2.protonPy(), lambda2.protonPz(), o2::constants::physics::MassProton); + + const float cosDeltaThetaStar = cosTheta(protonStar1, protonStar2); + const float deltaPhi = std::remainder(lambda1.phi() - lambda2.phi(), o2::constants::math::TwoPI); + // Invalid result returned by cosTheta(). + if (cosDeltaThetaStar < -1.f) { + continue; + } + + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/NumberOfPairs"), 0); + + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/DeltaEtaDelatPhi"), lambda1.eta() - lambda2.eta(), deltaPhi); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/DeltaPtDelatEta"), lambda1.pt() - lambda2.pt(), lambda1.eta() - lambda2.eta()); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/DeltaPtDelatPhi"), lambda1.pt() - lambda2.pt(), deltaPhi); + + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/DeltaMassDeltaCent"), lambda1.mass() - lambda2.mass(), lambda1.centrality() - lambda2.centrality()); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/DeltaMassDeltaPt"), lambda1.mass() - lambda2.mass(), lambda1.pt() - lambda2.pt()); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/DeltaCentDeltaMult"), lambda1.centrality() - lambda2.centrality(), lambda1.multiplicity() - lambda2.multiplicity()); + + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/Mass"), lambda1.mass(), lambda2.mass()); + + const bool isShortRange = isSameEventShortRange(lambda1, lambda2); + const bool isLongRange = isSameEventLongRange(lambda1, lambda2); + + if (isShortRange) { + hasShortRangePair = true; + + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda1.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda1.centrality()); + } else if (isLongRange) { + hasLongRangePair = true; + + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda1.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda1.centrality()); + } + } + if (hasShortRangePair) { + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/ShortRange/EventCount"), 0); + } + if (hasLongRangePair) { + rSpinAnalysis.fill(HIST("LambdaLambda/hSameEvent/LongRange/EventCount"), 0); + } + } + + template + void fillAntiLambdaAntiLambdaSameEvent(AntiLambdaSlice const& antiLambdas) + { + bool hasShortRangePair = false; + bool hasLongRangePair = false; + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/EventCount"), 0); + + // AntiLambda-AntiLambda pairs + for (auto const& [antilambda1, antilambda2] : combinations(CombinationsStrictlyUpperIndexPolicy(antiLambdas, antiLambdas))) { + + // Boost the proton into the Lambda rest frame. + const auto antiProtonStar1 = daughterInParentRestFrame(antilambda1.px(), antilambda1.py(), antilambda1.pz(), antilambda1.mass(), antilambda1.protonPx(), antilambda1.protonPy(), antilambda1.protonPz(), o2::constants::physics::MassProton); + // Boost the antiproton into the anti-Lambda rest frame. + const auto antiProtonStar2 = daughterInParentRestFrame(antilambda2.px(), antilambda2.py(), antilambda2.pz(), antilambda2.mass(), antilambda2.protonPx(), antilambda2.protonPy(), antilambda2.protonPz(), o2::constants::physics::MassProton); + + const float cosDeltaThetaStar = cosTheta(antiProtonStar1, antiProtonStar2); + const float deltaPhi = std::remainder(antilambda1.phi() - antilambda2.phi(), o2::constants::math::TwoPI); + // Invalid result returned by cosTheta(). + if (cosDeltaThetaStar < -1.f) { + continue; + } + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/NumberOfPairs"), 0); + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/DeltaEtaDelatPhi"), antilambda1.eta() - antilambda2.eta(), deltaPhi); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/DeltaPtDelatEta"), antilambda1.pt() - antilambda2.pt(), antilambda1.eta() - antilambda2.eta()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/DeltaPtDelatPhi"), antilambda1.pt() - antilambda2.pt(), deltaPhi); + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/DeltaMassDeltaCent"), antilambda1.mass() - antilambda2.mass(), antilambda1.centrality() - antilambda2.centrality()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/DeltaMassDeltaPt"), antilambda1.mass() - antilambda2.mass(), antilambda1.pt() - antilambda2.pt()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/DeltaCentDeltaMult"), antilambda1.centrality() - antilambda2.centrality(), antilambda1.multiplicity() - antilambda2.multiplicity()); + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/Mass"), antilambda1.mass(), antilambda2.mass()); + + const bool isShortRange = isSameEventShortRange(antilambda1, antilambda2); + const bool isLongRange = isSameEventLongRange(antilambda1, antilambda2); + + if (isShortRange) { + hasShortRangePair = true; + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/CosMult"), cosDeltaThetaStar, antilambda1.multiplicity()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/CosCent"), cosDeltaThetaStar, antilambda1.centrality()); + } else if (isLongRange) { + hasLongRangePair = true; + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/LongRange/CosMult"), cosDeltaThetaStar, antilambda1.multiplicity()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/LongRange/CosCent"), cosDeltaThetaStar, antilambda1.centrality()); + } + } + if (hasShortRangePair) { + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/ShortRange/EventCount"), 0); + } + if (hasLongRangePair) { + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hSameEvent/LongRange/EventCount"), 0); + } + } + + template + void fillLambdaAntiLambdaMixedEvent(LambdaSlice1 const& lambdas, AntiLambdaSlice2 const& antiLambdas) + { + + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + + bool hasAcceptedPair = false; + + // Lambda-AntiLambda pairs + for (auto const& [lambda, antiLambda] : combinations(CombinationsFullIndexPolicy(lambdas, antiLambdas))) { + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(lambda, antiLambda); + const bool isLongRangeMixed = isSameEventLongRange(lambda, antiLambda); + + // Boost the proton into the Lambda rest frame. + const auto protonStar = daughterInParentRestFrame(lambda.px(), lambda.py(), lambda.pz(), lambda.mass(), lambda.protonPx(), lambda.protonPy(), lambda.protonPz(), o2::constants::physics::MassProton); + // Boost the antiproton into the anti-Lambda rest frame. + const auto antiProtonStar = daughterInParentRestFrame(antiLambda.px(), antiLambda.py(), antiLambda.pz(), antiLambda.mass(), antiLambda.protonPx(), antiLambda.protonPy(), antiLambda.protonPz(), o2::constants::physics::MassProton); + + const float cosDeltaThetaStar = cosTheta(protonStar, antiProtonStar); + const float deltaPhi = std::remainder(lambda.phi() - antiLambda.phi(), o2::constants::math::TwoPI); + // Invalid result returned by cosTheta(). + if (cosDeltaThetaStar < -1.f) { + continue; + } + hasAcceptedPair = true; + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/NumberOfPairs"), 0); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaEtaDelatPhi"), lambda.eta() - antiLambda.eta(), deltaPhi); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaPtDelatEta"), lambda.pt() - antiLambda.pt(), lambda.eta() - antiLambda.eta()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaPtDelatPhi"), lambda.pt() - antiLambda.pt(), deltaPhi); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaMassDeltaCent"), lambda.mass() - antiLambda.mass(), lambda.centrality() - antiLambda.centrality()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaMassDeltaPt"), lambda.mass() - antiLambda.mass(), lambda.pt() - antiLambda.pt()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaCentDeltaMult"), lambda.centrality() - antiLambda.centrality(), lambda.multiplicity() - antiLambda.multiplicity()); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/Mass"), lambda.mass(), antiLambda.mass()); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } + } + + if (hasAcceptedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/EventCount"), 0); + } + + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/EventCount"), 0); + } + } + + template + void fillAntiLambdaLambdaMixedEvent(AntiLambdaSlice1 const& antiLambdas, LambdaSlice2 const& lambdas) + { + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + + bool hasAcceptedPair = false; + // Lambda-AntiLambda pairs + for (auto const& [lambda, antiLambda] : combinations(CombinationsFullIndexPolicy(lambdas, antiLambdas))) { + + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(lambda, antiLambda); + const bool isLongRangeMixed = isSameEventLongRange(lambda, antiLambda); + + // Boost the proton into the Lambda rest frame. + const auto protonStar = daughterInParentRestFrame(lambda.px(), lambda.py(), lambda.pz(), lambda.mass(), lambda.protonPx(), lambda.protonPy(), lambda.protonPz(), o2::constants::physics::MassProton); + // Boost the antiproton into the anti-Lambda rest frame. + const auto antiProtonStar = daughterInParentRestFrame(antiLambda.px(), antiLambda.py(), antiLambda.pz(), antiLambda.mass(), antiLambda.protonPx(), antiLambda.protonPy(), antiLambda.protonPz(), o2::constants::physics::MassProton); + + const float cosDeltaThetaStar = cosTheta(protonStar, antiProtonStar); + const float deltaPhi = std::remainder(lambda.phi() - antiLambda.phi(), o2::constants::math::TwoPI); + // Invalid result returned by cosTheta(). + if (cosDeltaThetaStar < -1.f) { + continue; + } + hasAcceptedPair = true; + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/NumberOfPairs"), 0); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaEtaDelatPhi"), lambda.eta() - antiLambda.eta(), deltaPhi); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaPtDelatEta"), lambda.pt() - antiLambda.pt(), lambda.eta() - antiLambda.eta()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaPtDelatPhi"), lambda.pt() - antiLambda.pt(), deltaPhi); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaMassDeltaCent"), lambda.mass() - antiLambda.mass(), lambda.centrality() - antiLambda.centrality()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaMassDeltaPt"), lambda.mass() - antiLambda.mass(), lambda.pt() - antiLambda.pt()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/DeltaCentDeltaMult"), lambda.centrality() - antiLambda.centrality(), lambda.multiplicity() - antiLambda.multiplicity()); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/Mass"), lambda.mass(), antiLambda.mass()); + + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda.multiplicity(), antiLambda.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda.centrality(), antiLambda.centrality()); + } + } + + if (hasAcceptedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/EventCount"), 0); + } + + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaAntiLambda/hMixedEvent/LongRange/EventCount"), 0); + } + } + + template + void fillLambdaLambdaMixedEvent(LambdaSlice1 const& lambdas1, LambdaSlice2 const& lambdas2) + { + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + + bool hasAcceptedPair = false; + // Lambda-AntiLambda pairs + for (auto const& [lambda1, lambda2] : combinations(CombinationsFullIndexPolicy(lambdas1, lambdas2))) { + + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(lambda1, lambda2); + const bool isLongRangeMixed = isSameEventLongRange(lambda1, lambda2); + + // Boost the proton into the Lambda rest frame. + const auto protonStar = daughterInParentRestFrame(lambda1.px(), lambda1.py(), lambda1.pz(), lambda1.mass(), lambda1.protonPx(), lambda1.protonPy(), lambda1.protonPz(), o2::constants::physics::MassProton); + // Boost the antiproton into the anti-Lambda rest frame. + const auto antiProtonStar = daughterInParentRestFrame(lambda2.px(), lambda2.py(), lambda2.pz(), lambda2.mass(), lambda2.protonPx(), lambda2.protonPy(), lambda2.protonPz(), o2::constants::physics::MassProton); + + const float cosDeltaThetaStar = cosTheta(protonStar, antiProtonStar); + const float deltaPhi = std::remainder(lambda1.phi() - lambda2.phi(), o2::constants::math::TwoPI); + // Invalid result returned by cosTheta(). + if (cosDeltaThetaStar < -1.f) { + continue; + } + hasAcceptedPair = true; + + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/NumberOfPairs"), 0); + + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/DeltaEtaDelatPhi"), lambda1.eta() - lambda2.eta(), deltaPhi); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/DeltaPtDelatEta"), lambda1.pt() - lambda2.pt(), lambda1.eta() - lambda2.eta()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/DeltaPtDelatPhi"), lambda1.pt() - lambda2.pt(), deltaPhi); + + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/DeltaMassDeltaCent"), lambda1.mass() - lambda2.mass(), lambda1.centrality() - lambda2.centrality()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/DeltaMassDeltaPt"), lambda1.mass() - lambda2.mass(), lambda1.pt() - lambda2.pt()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/DeltaCentDeltaMult"), lambda1.centrality() - lambda2.centrality(), lambda1.multiplicity() - lambda2.multiplicity()); + + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/Mass"), lambda1.mass(), lambda2.mass()); + + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, lambda1.multiplicity(), lambda2.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, lambda1.centrality(), lambda2.centrality()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, lambda1.multiplicity(), lambda2.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, lambda1.centrality(), lambda2.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, lambda1.multiplicity(), lambda2.multiplicity()); + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, lambda1.centrality(), lambda2.centrality()); + } + } + + if (hasAcceptedPair) { + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/EventCount"), 0); + } + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("LambdaLambda/hMixedEvent/LongRange/EventCount"), 0); + } + } + + template + void fillAntiLambdaAntiLambdaMixedEvent(AntiLambdaSlice1 const& antiLambdas1, AntiLambdaSlice2 const& antiLambdas2) + { + bool hasShortRangeMixedPair = false; + bool hasLongRangeMixedPair = false; + + bool hasAcceptedPair = false; + // Lambda-AntiLambda pairs + for (auto const& [antiLambda1, antiLambda2] : combinations(CombinationsFullIndexPolicy(antiLambdas1, antiLambdas2))) { + + // Evaluate your existing short-range and long-range tests. + const bool isShortRangeMixed = isSameEventShortRange(antiLambda1, antiLambda2); + const bool isLongRangeMixed = isSameEventLongRange(antiLambda1, antiLambda2); + + // Boost the proton into the Lambda rest frame. + const auto protonStar = daughterInParentRestFrame(antiLambda1.px(), antiLambda1.py(), antiLambda1.pz(), antiLambda1.mass(), antiLambda1.protonPx(), antiLambda1.protonPy(), antiLambda1.protonPz(), o2::constants::physics::MassProton); + // Boost the antiproton into the anti-Lambda rest frame. + const auto antiProtonStar = daughterInParentRestFrame(antiLambda2.px(), antiLambda2.py(), antiLambda2.pz(), antiLambda2.mass(), antiLambda2.protonPx(), antiLambda2.protonPy(), antiLambda2.protonPz(), o2::constants::physics::MassProton); + + const float cosDeltaThetaStar = cosTheta(protonStar, antiProtonStar); + const float deltaPhi = std::remainder(antiLambda1.phi() - antiLambda2.phi(), o2::constants::math::TwoPI); + // Invalid result returned by cosTheta(). + if (cosDeltaThetaStar < -1.f) { + continue; + } + hasAcceptedPair = true; + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/NumberOfPairs"), 0); + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/DeltaEtaDelatPhi"), antiLambda1.eta() - antiLambda2.eta(), deltaPhi); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/DeltaPtDelatEta"), antiLambda1.pt() - antiLambda2.pt(), antiLambda1.eta() - antiLambda2.eta()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/DeltaPtDelatPhi"), antiLambda1.pt() - antiLambda2.pt(), deltaPhi); + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/DeltaMassDeltaCent"), antiLambda1.mass() - antiLambda2.mass(), antiLambda1.centrality() - antiLambda2.centrality()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/DeltaMassDeltaPt"), antiLambda1.mass() - antiLambda2.mass(), antiLambda1.pt() - antiLambda2.pt()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/DeltaCentDeltaMult"), antiLambda1.centrality() - antiLambda2.centrality(), antiLambda1.multiplicity() - antiLambda2.multiplicity()); + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/Mass"), antiLambda1.mass(), antiLambda2.mass()); + + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/CosMult"), cosDeltaThetaStar, antiLambda1.multiplicity(), antiLambda2.multiplicity()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/CosCent"), cosDeltaThetaStar, antiLambda1.centrality(), antiLambda2.centrality()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/Cos"), cosDeltaThetaStar); + + if (isShortRangeMixed) { + hasShortRangeMixedPair = true; + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/CosMult"), cosDeltaThetaStar, antiLambda1.multiplicity(), antiLambda2.multiplicity()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/CosCent"), cosDeltaThetaStar, antiLambda1.centrality(), antiLambda2.centrality()); + } else if (isLongRangeMixed) { + hasLongRangeMixedPair = true; + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/NumberOfPairs"), 0); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/Cos"), cosDeltaThetaStar); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/CosMult"), cosDeltaThetaStar, antiLambda1.multiplicity(), antiLambda2.multiplicity()); + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/CosCent"), cosDeltaThetaStar, antiLambda1.centrality(), antiLambda2.centrality()); + } + } + + if (hasAcceptedPair) { + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/EventCount"), 0); + } + if (hasShortRangeMixedPair) { + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/ShortRange/EventCount"), 0); + } + + if (hasLongRangeMixedPair) { + rSpinAnalysis.fill(HIST("AntiLambdaAntiLambda/hMixedEvent/LongRange/EventCount"), 0); + } + } + // ================================================================ + // Mass Filter + // ================================================================ + + Configurable minLambdaMass{"minLambdaMass", 1.11f, "Minimum Lambda mass"}; + Configurable maxLambdaMass{"maxLambdaMass", 1.12f, "Maximum Lambda mass"}; + + Filter lambdaMassFilter = aod::lambdahyperon::mass >= minLambdaMass && + aod::lambdahyperon::mass <= maxLambdaMass; + + using FilteredLambdas = soa::Filtered; + using FilteredAntiLambdas = soa::Filtered; + using CollisionsWithMultiplicity = soa::Join; + + Preslice lambdasPerCollision = aod::lambdahyperon::collisionId; + Preslice antiLambdasPerCollision = aod::lambdahyperon::collisionId; + + void processSameEventData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) + { + static constexpr int MinimumSameSpeciesCandidates = 2; + + for (auto const& collision : collisions) { + if (jackknife.value && collision.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value) { + continue; + } + + const auto lambdasThisCollision = lambdas.sliceBy(lambdasPerCollision, collision.globalIndex()); + const auto antiLambdasThisCollision = antiLambdas.sliceBy(antiLambdasPerCollision, collision.globalIndex()); + + // Lambda–anti-Lambda requires at least one of each. + if (lambdasThisCollision.size() > 0 && antiLambdasThisCollision.size() > 0) { + fillLambdaAntiLambdaSameEvent(lambdasThisCollision, antiLambdasThisCollision); + } + + // Lambda–Lambda requires at least two Lambdas. + if (lambdasThisCollision.size() >= MinimumSameSpeciesCandidates) { + fillLambdaLambdaSameEvent(lambdasThisCollision); + } + + // Anti-Lambda–anti-Lambda requires at least two anti-Lambdas. + if (antiLambdasThisCollision.size() >= MinimumSameSpeciesCandidates) { + fillAntiLambdaAntiLambdaSameEvent(antiLambdasThisCollision); + } + } + } + + void processMixedEventData(CollisionsWithMultiplicity const& collisions, FilteredLambdas const& lambdas, FilteredAntiLambdas const& antiLambdas) + { + int mixedEventCounter = 0; + + for (auto const& [collision1, collision2] : combinations(CombinationsStrictlyUpperIndexPolicy(collisions, collisions))) { + if (jackknife.value && (collision1.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value || collision2.globalIndex() % NJackknifeGroups == jackknifeExcludedGroup.value)) { + continue; + } + + const float deltaMultiplicity = std::abs(static_cast(collision1.multNTracksPV()) - static_cast(collision2.multNTracksPV())); + + if (mixedEventCounter >= maxMixedEvents) { + break; + } + + if (deltaMultiplicity > mixedEventMaxDeltaMultiplicity) { + continue; + } + + const auto lambdas1 = lambdas.sliceBy(lambdasPerCollision, collision1.globalIndex()); + const auto lambdas2 = lambdas.sliceBy(lambdasPerCollision, collision2.globalIndex()); + + const auto antiLambdas1 = antiLambdas.sliceBy(antiLambdasPerCollision, collision1.globalIndex()); + const auto antiLambdas2 = antiLambdas.sliceBy(antiLambdasPerCollision, collision2.globalIndex()); + + // Empty collisions must not consume the mixing allowance. + if ((lambdas1.size() == 0 && antiLambdas1.size() == 0) || + (lambdas2.size() == 0 && antiLambdas2.size() == 0)) { + continue; + } + + // Lambda–anti-Lambda requires at least one of each. + if (lambdas1.size() > 0 && antiLambdas2.size() > 0) { + fillLambdaAntiLambdaMixedEvent(lambdas1, antiLambdas2); + } + if (lambdas2.size() > 0 && antiLambdas1.size() > 0) { + fillAntiLambdaLambdaMixedEvent(antiLambdas1, lambdas2); + } + // Lambda–Lambda requires at least two Lambdas. + if (lambdas1.size() > 0 && lambdas2.size() > 0) { + fillLambdaLambdaMixedEvent(lambdas1, lambdas2); + } + // Anti-Lambda–anti-Lambda requires at least two anti-Lambdas. + if (antiLambdas1.size() > 0 && antiLambdas2.size() > 0) { + fillAntiLambdaAntiLambdaMixedEvent(antiLambdas1, antiLambdas2); + } + + ++mixedEventCounter; + } + } + + void processDummy(aod::Collisions const&) {} + + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processSameEventData, "Run same-event pair analysis", true); + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processMixedEventData, "Run mixed-event pair analysis", false); + PROCESS_SWITCH(LambdaAntiLambdaPairAnalysis, processDummy, "Skip candidate production", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx new file mode 100644 index 00000000000..648464f6973 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaProtonBalanceFunction.cxx @@ -0,0 +1,4235 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambdaProtonBalanceFunction.cxx +/// \brief Single- and two-particle density correlations (rho1, rho2) and R2 correlations for Lambda-proton, proton-proton and Lambda-Lambda pairs (plus antiparticle combinations) to measure the balance function. +/// \author Anoop Poruthiyil + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using MyTracks = soa::Join; +// MC-labelled versions used only by processMC (efficiency maps) +using MyTracksMC = soa::Join; +using MyV0sMC = soa::Join; + +namespace +{ +float displayDeltaPhi(float dPhi) +{ + return RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI / 2.0f); +} +} // namespace + +// Per-centrality-bin histogram set. Defined at file scope (NOT inside the task struct), +// otherwise O2's struct reflection in adaptAnalysisTask fails to compile. +struct CentRho2Set { + // y space + std::shared_ptr Lp_y, LAp_y, ALp_y, ALAp_y, pp_y, pAp_y, App_y, ApAp_y, LL_y, LAL_y, ALL_y, ALAL_y; + // rho1 y + std::shared_ptr rho1_Proton_y, rho1_AntiProton_y, rho1_Lambda_y, rho1_AntiLambda_y; + // event counter + std::shared_ptr hEvents; +}; + +struct LambdaProtonBalanceFunction { + HistogramRegistry registryLambda{"Lambda_invMass", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryRho{"rho1ANDrho2_LP", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryCent{"rho1ANDrho2_LP_CentralityBased", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryOther{"Other_Hists", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryPid{"PID", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryQaDetector{"QA_Detec", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryProtonPidCheck{"ProtonCounts_byPID_Check", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryCorrelationQA{"CorrelationQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // ── Dedicated registry for V0-daughter veto importance QA ───────────── + // Folder: ProtonVetoQA/ + // Contains per-event distributions of N_before, N_after, N_removed, fraction. + HistogramRegistry registryProtonVetoQA{"ProtonVetoQA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // MC efficiency maps (Gen*/Rec* TH3: x=cent, y=pT, z=y), filled only by processMC. Folder: Efficiency/ + HistogramRegistry registryEff{"Efficiency", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // ════════════════════════════════════════════════════════════════════════ + // TaskState — ordinary C++ runtime/implementation state. + // Grouped into ONE nested object so that the number of direct data members of + // this task struct stays well below the O2 StructToTuple / structured-binding + // reflection limit. Contains NO O2 Framework objects (no Configurable, + // ConfigurableAxis, Filter, HistogramRegistry): those must remain top-level. + // ════════════════════════════════════════════════════════════════════════ + struct TaskState { + // pT bins 0.6 → 3.6 in steps of 0.2 (used for per-bin pT-spectra / mass histograms) + std::vector ptEdges; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION A — Invariant mass histograms + // ════════════════════════════════════════════════════════════════════════ + std::vector> hMassLambdaPtBins; + std::vector> hMassAntiLambdaPtBins; + std::shared_ptr hMassLambdaMerged; + std::shared_ptr hMassAntiLambdaMerged; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION C — rho2 rapidity-space histograms, filled with unrolledIndexY(y, phi). + // Naming: hRho2__y_AllPt. + // ════════════════════════════════════════════════════════════════════════ + // Lambda–proton family (y) + std::shared_ptr hRho2_Lp_y_AllPt; + std::shared_ptr hRho2_LAp_y_AllPt; + std::shared_ptr hRho2_ALp_y_AllPt; + std::shared_ptr hRho2_ALAp_y_AllPt; + // Proton–proton family (y) + std::shared_ptr hRho2_pp_y_AllPt; + std::shared_ptr hRho2_pAp_y_AllPt; + std::shared_ptr hRho2_App_y_AllPt; + std::shared_ptr hRho2_ApAp_y_AllPt; + // Lambda–Lambda family (y) + std::shared_ptr hRho2_LL_y_AllPt; + std::shared_ptr hRho2_LAL_y_AllPt; + std::shared_ptr hRho2_ALL_y_AllPt; + std::shared_ptr hRho2_ALAL_y_AllPt; + + // SECTION E — centrality-differential storage (struct CentRho2Set is defined above the task) + std::vector centSets; + std::vector centEdgesLocal; + + // ════════════════════════════════════════════════════════════════════════ + // SECTION D — pT-spectra and pair-count monitoring histograms + // ════════════════════════════════════════════════════════════════════════ + std::vector> hPtPrimProton_Lambda; + std::vector> hPtPrimAntiProton_Lambda; + std::vector> hPtPrimProton_AntiLambda; + std::vector> hPtPrimAntiProton_AntiLambda; + + std::shared_ptr hPtSelectedPrimProton_AllPt; + std::shared_ptr hPtSelectedPrimAntiProton_AllPt; + std::shared_ptr hPtSelLambda_AllPt; + std::shared_ptr hPtSelAntiLambda_AllPt; + + // ── PID-check histograms (ProtonCounts_byPID_Check) ─────────────────── + std::shared_ptr h_TPC; + std::shared_ptr h_TPCandTOF; + std::shared_ptr h_TPCorTOF; + // ───────────────────────────────────────────────────────────────────── + + // ── Run-level accumulators for proton-veto importance QA ───────────── + // These are summed over all processed events and printed in endOfStream(). + // They quantify how much Lambda feed-down was removed from the proton + // sample before constructing future Balance Functions. + int64_t runTotalProtonBeforeVeto = 0; ///< sum of N_before_veto over all events + int64_t runTotalProtonRemovedVeto = 0; ///< sum of N_removed_by_veto over all events + int64_t runTotalAntiPBeforeVeto = 0; ///< antiproton: sum of N_before_veto + int64_t runTotalAntiPRemovedVeto = 0; ///< antiproton: sum of N_removed_by_veto + + // Runtime copies populated from Configurables in init(). + // Not static, not constexpr — values are known only after O2 delivers the config. + int mRhoPhiBins = 0; + int mRhoYBins = 0; + int mRhoUnrolledBinsY = 0; // mRhoYBins * mRhoPhiBins + float mRhoYMin = 0.f; + float mRhoYMax = 0.f; + + // 1/efficiency maps (TH3: x=cent, y=pt, z=y); loaded once in init() when cApplyEffCorr is true + TH3* invEffL = nullptr; + TH3* invEffAL = nullptr; + TH3* invEffP = nullptr; + TH3* invEffAP = nullptr; + }; + + TaskState state; + + Service ccdb{}; + + // ───────────────────────────────────────────────────────────────────── + + // ── Configurables ───────────────────────────────────────────────────── + + Configurable cPPandV0ZVertexCut{"cPPandV0ZVertexCut", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable cUseQinvCut{"cUseQinvCut", false, "Apply q_inv > cQinvCutValue pair cut (false = no q_inv rejection)"}; + Configurable cQinvCutValue{"cQinvCutValue", 0.01f, "q_inv cut value (GeV/c)"}; + Configurable cQinvQABins{"cQinvQABins", 6000, "Number of bins for QA q_inv histograms"}; + Configurable cQinvQAMax{"cQinvQAMax", 6.0f, "Max q_inv for QA histograms (GeV/c)"}; + Configurable cFillCentHists{"cFillCentHists", true, "Fill centrality-differential rho1/rho2 histograms in separate rho1ANDrho2_LP_CentralityBased folder"}; + Configurable cUseFT0C{"cUseFT0C", false, "Use FT0C instead of FT0M as centrality estimator"}; + ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 40.f, 60.f, 80.f}, "Centrality bins (%)"}; + Configurable cMinCentrality{"cMinCentrality", 0.0f, "Reject events with centrality below this value (%)"}; + Configurable cMaxCentrality{"cMaxCentrality", 80.0f, "Reject events with centrality above this value (%)"}; + Configurable cApplyEffCorr{"cApplyEffCorr", false, "Weight rho1/rho2 by 1/efficiency"}; + Configurable cEffFile{"cEffFile", "", "ROOT file with hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton (TH3: x=cent, y=pt, z=y). Used only if cEffCcdbPath is empty"}; + Configurable cEffCcdbPath{"cEffCcdbPath", "", "CCDB path of a TList holding hInvEff_Lambda, hInvEff_AntiLambda, hInvEff_Proton, hInvEff_AntiProton; if non-empty it is used instead of cEffFile"}; + Configurable cCcdbUrl{"cCcdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable cEffCcdbTimestamp{"cEffCcdbTimestamp", 1700000000000, "CCDB timestamp (ms) used to fetch the efficiency maps (must lie inside the object validity)"}; + + // Track quality cuts + Configurable pProtonTPCMinRows{"pProtonTPCMinRows", 70, "Minimum TPC crossed rows"}; + Configurable pProtonTPCMinRowsOverFindable{"pProtonTPCMinRowsOverFindable", 0.8f, "Min TPC crossed rows / findable clusters"}; + Configurable pProtonTPCMaxChi2PerCluster{"pProtonTPCMaxChi2PerCluster", 4.0f, "Max TPC chi2 / Ncls"}; + Configurable pProtonITSMinClusters{"pProtonITSMinClusters", 5, "Minimum ITS clusters"}; + Configurable pProtonITSMaxChi2PerCluster{"pProtonITSMaxChi2PerCluster", 36.0f, "Max ITS chi2 / Ncls"}; + Configurable cPPMinTpcSignal{"cPPMinTpcSignal", 10.0f, "Min raw TPC signal (sanity cut)"}; + // DCA cuts + Configurable pProtonMaxDCAxy{"pProtonMaxDCAxy", 0.02f, "Max |DCAxy| (cm)"}; // tight cut for cleanest primary-proton sample + Configurable pProtonMaxDCAz{"pProtonMaxDCAz", 0.1f, "Max |DCAz| (cm)"}; + + Configurable pProtonTPCTOFSwitchPt{"pProtonTPCTOFSwitchPt", 0.700000238f, "pT threshold (GeV/c) above which TOF is used if available"}; + // Configurable cPPMinTofBeta{"cPPMinTofBeta", 0.4f, "Min TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented + // Configurable cPPMaxTofBeta{"cPPMaxTofBeta", 0.96f, "Max TOF beta for proton selection (TOF branch only)"}; //BETACUTCommented + + // V0 topology cuts + V0 DCA Cuts + Configurable cV0MaxDcaDaughters{"cV0MaxDcaDaughters", 1.0f, "DCA V0 daughters < (cm)"}; + Configurable lambdaV0DaughterProtonMinDCAToPV{"lambdaV0DaughterProtonMinDCAToPV", 0.01f, "DCA proton daughter to PV > (cm)"}; + Configurable lambdaV0DaughterPionMinDCAToPV{"lambdaV0DaughterPionMinDCAToPV", 0.10f, "DCA pion daughter to PV > (cm)"}; + Configurable lambdaV0MinCosPA{"lambdaV0MinCosPA", 0.995, "V0 CosPA >"}; + Configurable lambdaV0MinDecayRadius{"lambdaV0MinDecayRadius", 0.5f, "V0 radius > (cm)"}; + Configurable cV0MaxDcaToPV{"cV0MaxDcaToPV", 999.0f, "DCA V0 to PV < (cm)"}; + + // PID cuts + Configurable lambdaV0DaughterPionTPCNsigma{"lambdaV0DaughterPionTPCNsigma", 2.0f, "lambdaV0DaughterPionTPCNsigma"}; + Configurable pProtonTPCNsigma{"pProtonTPCNsigma", 2.0f, "pProtonTPCNsigma"}; + Configurable lambdaV0DaughterProtonTPCNsigma{"lambdaV0DaughterProtonTPCNsigma", 2.0f, "lambdaV0DaughterProtonTPCNsigma"}; + Configurable pProtonTOFNsigma{"pProtonTOFNsigma", 2.0f, "pProtonTOFNsigma"}; + + // Contaminant veto for primary (anti)protons: reject track if compatible with pi/K/e + Configurable cUseContaminantVeto{"cUseContaminantVeto", true, "Reject primary (anti)protons compatible with pi/K/e hypotheses"}; + Configurable cVetoUseTOF{"cVetoUseTOF", true, "Also apply TOF veto whenever the track has TOF"}; + Configurable pProtonVetoPionNsigma{"pProtonVetoPionNsigma", 3.0f, "Reject if |nSigma_pi| < this"}; + Configurable pProtonVetoKaonNsigma{"pProtonVetoKaonNsigma", 3.0f, "Reject if |nSigma_K| < this"}; + Configurable pProtonVetoElectronNsigma{"pProtonVetoElectronNsigma", 3.0f, "Reject if |nSigma_e| < this"}; + + // // V0 daughter beta cuts (particle-dependent) //BETACUTCommented + // Configurable cV0protonDauMinBeta{"cV0protonDauMinBeta", 0.4f, "Min TOF beta for (anti)proton V0 daughter"}; //BETACUTCommented + // Configurable cV0protonDauMaxBeta{"cV0protonDauMaxBeta", 0.98f, "Max TOF beta for (anti)proton V0 daughter"}; //BETACUTCommented + // Configurable cV0pionDauMinBeta{"cV0pionDauMinBeta", 0.91f, "Min TOF beta for (anti)pion V0 daughter"}; //BETACUTCommented + // ───────────────────────────────────────────────────────────────────── + + // Proton Kinematic Cuts (from strange9) + Configurable pProtonMinPt{"pProtonMinPt", 0.5f, "Min pT for primary protons (GeV/c)"}; + Configurable pProtonMaxPt{"pProtonMaxPt", 3.6f, "Max pT for primary protons (GeV/c)"}; + + Configurable pProtonMaxY{"pProtonMaxY", 0.5f, "Max |y| for primary protons"}; + + // Lambda Kinematic Cuts (from strange9) + Configurable lambdaV0MinPt{"lambdaV0MinPt", 0.60f, "Min pT for Lambdas"}; + Configurable lambdaV0MaxPt{"lambdaV0MaxPt", 3.60f, "Max pT for Lambdas"}; + Configurable lambdaV0MaxY{"lambdaV0MaxY", 0.5f, "Max |y| for Lambdas"}; + + // V0 Mass and Topology Cuts (from strange9) + Configurable lambdaV0MaxCTau{"lambdaV0MaxCTau", 30.0f, "Max V0 ctau (cm)"}; + Configurable lambdaV0KShortRejectMassWindow{"lambdaV0KShortRejectMassWindow", 0.01f, "K0s rejection window"}; + Configurable lambdaV0MassWindow{"lambdaV0MassWindow", 0.007f, "Lambda mass window (|m - mPDG| < this)"}; + + // V0 Type and Daughter Quality Cuts (from strange9) + Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; + Configurable cV0DauMinPt{"cV0DauMinPt", 0.1f, "Daughter pT minimum"}; + Configurable cV0DauMaxEta{"cV0DauMaxEta", 0.8f, "Daughter |eta| cut"}; + Configurable cV0DauMinTpcCrossedRows{"cV0DauMinTpcCrossedRows", 70, "Daughter TPC min crossed rows"}; + // ── Rho1/Rho2 grid — configurable at runtime (Hyperloop-friendly) ──────── + // These replace the former static constexpr k* constants. Default values + // reproduce the original hardcoded grid (y: 10×72). + Configurable cRhoPhiBins{"cRhoPhiBins", 72, "Number of phi bins for rho1/rho2"}; + Configurable cRhoYBins{"cRhoYBins", 10, "Number of y bins for rho1/rho2"}; + Configurable cRhoYMin{"cRhoYMin", -0.5f, "Min y for rho1/rho2"}; + Configurable cRhoYMax{"cRhoYMax", 0.5f, "Max y for rho1/rho2"}; + // ───────────────────────────────────────────────────────────────────────── + + void buildPtBins() + { + state.ptEdges.clear(); + static constexpr int kNPtEdges = 16; // 0.6, 0.8, ..., 3.6 GeV/c (15 bins) + static constexpr float kPtEdgeMin = 0.6f; + static constexpr float kPtEdgeStep = 0.2f; + for (int i = 0; i < kNPtEdges; ++i) { + state.ptEdges.push_back(kPtEdgeMin + kPtEdgeStep * static_cast(i)); + } + } + + int phiBinIndex(float phi) const + { + constexpr float phiMin = 0.0f; + constexpr float phiMax = o2::constants::math::TwoPI; + if (phi < phiMin || phi >= phiMax) { + return -1; + } + const float binWidth = (phiMax - phiMin) / state.mRhoPhiBins; + return static_cast((phi - phiMin) / binWidth); + } + + int yBinIndex(float y) const + { + if (y < state.mRhoYMin || y >= state.mRhoYMax) { + return -1; + } + const float binWidth = (state.mRhoYMax - state.mRhoYMin) / state.mRhoYBins; + return static_cast((y - state.mRhoYMin) / binWidth); + } + + int unrolledIndexY(float y, float phi) const + { + const int iY = yBinIndex(y); + const int iPhi = phiBinIndex(phi); + if (iY < 0 || iPhi < 0) { + return -1; + } + return iY * state.mRhoPhiBins + iPhi; + } + + // Convenience wrappers with particle-specific masses + static constexpr float kMassProton = o2::constants::physics::MassProton; + static constexpr float kMassLambda = o2::constants::physics::MassLambda; + + template + float protonRapidity(T const& track) const + { + return RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, kMassProton); + } + + // k* = |p*| of one particle in the pair rest frame, from four-momenta. + float computeKstar(float px1, float py1, float pz1, float m1, + float px2, float py2, float pz2, float m2) const + { + const float E1 = std::sqrt(px1 * px1 + py1 * py1 + pz1 * pz1 + m1 * m1); + const float E2 = std::sqrt(px2 * px2 + py2 * py2 + pz2 * pz2 + m2 * m2); + const float sx = px1 + px2; + const float sy = py1 + py2; + const float sz = pz1 + pz2; + const float sE = E1 + E2; + const float s = sE * sE - sx * sx - sy * sy - sz * sz; + if (s <= 0.f) { + return 0.f; + } + // k*^2 = [ (s - (m1+m2)^2)(s - (m1-m2)^2) ] / (4 s) + const float a = s - (m1 + m2) * (m1 + m2); + const float b = s - (m1 - m2) * (m1 - m2); + return std::sqrt(std::max(a * b, 0.f) / (4.f * s)); + } + + // ── Femtoscopic q_inv computation ──────────────────────────────────────── + // Returns q_inv = sqrt( -(p1 - p2)^2 ) in natural units (GeV/c). + // E1 and E2 must be computed by the caller using the appropriate mass + // hypothesis for each track: + // - primary proton: E = sqrt(p^2 + kMassProton^2) + // - V0 (Lambda leg): E = sqrt(p^2 + kMassLambda^2) + float computeQinv(float px1, float py1, float pz1, float E1, + float px2, float py2, float pz2, float E2) const + { + const float dE = E1 - E2; + const float dpx = px1 - px2; + const float dpy = py1 - py2; + const float dpz = pz1 - pz2; + const float q2 = dE * dE - dpx * dpx - dpy * dpy - dpz * dpz; + // q2 should be <= 0 for physical pairs; protect sqrt from numerical noise + return std::sqrt(std::max(-q2, 0.0f)); + } + + // true => pair is rejected by the q_inv cut (never true when the cut is switched off) + bool failsQinvCut(float qinv) const + { + return cUseQinvCut.value && (qinv <= cQinvCutValue.value); + } + + // QA-only: true => pair would survive the q_inv cut, regardless of the cUseQinvCut master switch. + // Used for ALL "After_qinvCut" QA histograms (PP, pAp, App, ApAp, LP-family, LL-family) so they always show the mathematical effect of the cut. + // Physics logic must keep using failsQinvCut(). + bool passesQinvQA(float qinv) const { return qinv > cQinvCutValue.value; } + + // Returns the active centrality estimator's percentile for this collision, + // selected at runtime via cUseFT0C (false = FT0M, true = FT0C). + template + float getCentrality(TCollision const& col) const + { + return cUseFT0C.value ? col.centFT0C() : col.centFT0M(); + } + + // Returns index of centrality bin for cent, or -1 if outside all bins. + int centBinIndex(float cent) const + { + for (size_t i = 0; i + 1 < state.centEdgesLocal.size(); ++i) { + if (cent >= state.centEdgesLocal[i] && cent < state.centEdgesLocal[i + 1]) { + return static_cast(i); + } + } + return -1; + } + + // Fill helper: fills h (if valid) at (a,b) only when both indices are in range. + static void fillIdx(std::shared_ptr const& h, int a, int b, float w = 1.f) + { + if (h && a >= 0 && b >= 0) { + h->Fill(a + 0.5f, b + 0.5f, w); + } + } + + // Efficiency weight w = 1/eff from the TH3 map (x=cent, y=pt, z=y). 1 when correction is off. + // (TH3* non-const: TH1::FindBin is a non-const method.) + float getEffWeight(TH3* h, float cent, float pt, float y) const + { + if (!cApplyEffCorr.value || !h) { + return 1.f; + } + const float v = h->GetBinContent(h->FindBin(cent, pt, y)); + return v > 0.f ? v : 0.f; // 0 means: particle is dropped (no efficiency info) + } + // ───────────────────────────────────────────────────────────────────── + + // ── Two-region PID selection for (anti)protons ──────────────────────── + template + bool passesPrimProtonPid(TTrack const& trk) const + { + const float pt = trk.pt(); + const float nsPr = trk.tpcNSigmaPr(); + + if (std::abs(nsPr) >= pProtonTPCNsigma.value) { + return false; + } + if (pt < pProtonTPCTOFSwitchPt.value) { + return true; + } + if (!trk.hasTOF()) { + return false; + } + return std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value; + } + + // Bitmask of contaminant hypotheses that REJECT the track (bit0 = pi, bit1 = K, bit2 = e). + // A hypothesis rejects only if the track is inside its veto window AND closer to it than to the proton. + // Detector: TOF at/above the TPC/TOF switch pT when TOF is available, TPC otherwise. + template + int contaminantVetoMask(TTrack const& trk, bool& usedTOF) const + { + usedTOF = cVetoUseTOF.value && trk.hasTOF() && trk.pt() >= pProtonTPCTOFSwitchPt.value; + const float nsP = std::abs(usedTOF ? trk.tofNSigmaPr() : trk.tpcNSigmaPr()); + const float nsPi = std::abs(usedTOF ? trk.tofNSigmaPi() : trk.tpcNSigmaPi()); + const float nsK = std::abs(usedTOF ? trk.tofNSigmaKa() : trk.tpcNSigmaKa()); + const float nsE = std::abs(usedTOF ? trk.tofNSigmaEl() : trk.tpcNSigmaEl()); + int mask = 0; + if (nsPi < pProtonVetoPionNsigma.value && nsPi < nsP) { + mask |= 1; + } + if (nsK < pProtonVetoKaonNsigma.value && nsK < nsP) { + mask |= 2; + } + if (nsE < pProtonVetoElectronNsigma.value && nsE < nsP) { + mask |= 4; + } + return mask; + } + + // true => track survives the pi/K/e contaminant veto + template + bool passesContaminantVeto(TTrack const& trk) const + { + if (!cUseContaminantVeto.value) { + return true; + } + bool usedTOF = false; + return contaminantVetoMask(trk, usedTOF) == 0; + } + + // 2D cutflow vs pT only (used directly for the veto-reason bins 16-21) + template + void fillCutFlow2D(float stage, TTrack const& trk) + { + if (trk.sign() > 0) { + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton"), trk.pt(), stage); + } else { + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton"), trk.pt(), stage); + } + } + + // 1D hCutFlow + 2D cutflow (sequential stages 1-15) + template + void fillCutFlow(float stage, TTrack const& trk) + { + registryQaDetector.fill(HIST("Selection/TOF_Matching/hCutFlow"), stage); + fillCutFlow2D(stage, trk); + } + // ───────────────────────────────────────────────────────────────────── + + // ══════════════════════════════════════════════════════════════════════════ + // STAGED QA INFRASTRUCTURE — registryQaDetector ("QA_Detec") + // ══════════════════════════════════════════════════════════════════════════ + + /// Canonical analysis stages for (anti)proton QA fills. + enum class ProtonQAStage : int { + RawAfterTrackSel = 0, ///< after track-quality selection (TrackSelection table), before DCA cuts and PID + TpcPid = 1, ///< after DCA + TPC PID cut passes (all momenta) + TpcTofPid = 2, ///< after DCA + full PID: TPC everywhere, plus TOF required only above pProtonTPCTOFSwitchPt + FinalSelected = 3 ///< entered selectedPrimProtons / selectedPrimAntiProtons + }; + + // ── Staged fill helper: Kinematics ─────────────────────────────────────── + // IsProton=true → Proton subfolder ; IsProton=false → AntiProton subfolder + template + void fillProtonKinematics(ProtonQAStage stage, TTrack const& trk) + { + const float p = trk.p(); + const float pt = trk.pt(); + const float eta = trk.eta(); + const float phi = trk.phi(); + const float y = protonRapidity(trk); + if (stage == ProtonQAStage::RawAfterTrackSel) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/01_Kinematics/01_RAW_AfterTrackSel/h1f_p"), p); + registryQaDetector.fill(HIST("Proton/01_Kinematics/01_RAW_AfterTrackSel/h1f_pt"), pt); + registryQaDetector.fill(HIST("Proton/01_Kinematics/01_RAW_AfterTrackSel/h1f_eta"), eta); + registryQaDetector.fill(HIST("Proton/01_Kinematics/01_RAW_AfterTrackSel/h1f_phi"), phi); + registryQaDetector.fill(HIST("Proton/01_Kinematics/01_RAW_AfterTrackSel/h1f_rapidity"), y); + } else { + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/01_RAW_AfterTrackSel/h1f_p"), p); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/01_RAW_AfterTrackSel/h1f_pt"), pt); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/01_RAW_AfterTrackSel/h1f_eta"), eta); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/01_RAW_AfterTrackSel/h1f_phi"), phi); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/01_RAW_AfterTrackSel/h1f_rapidity"), y); + } + } else if (stage == ProtonQAStage::TpcPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/01_Kinematics/02_TPC_PID/h1f_p"), p); + registryQaDetector.fill(HIST("Proton/01_Kinematics/02_TPC_PID/h1f_pt"), pt); + registryQaDetector.fill(HIST("Proton/01_Kinematics/02_TPC_PID/h1f_eta"), eta); + registryQaDetector.fill(HIST("Proton/01_Kinematics/02_TPC_PID/h1f_phi"), phi); + registryQaDetector.fill(HIST("Proton/01_Kinematics/02_TPC_PID/h1f_rapidity"), y); + } else { + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/02_TPC_PID/h1f_p"), p); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/02_TPC_PID/h1f_pt"), pt); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/02_TPC_PID/h1f_eta"), eta); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/02_TPC_PID/h1f_phi"), phi); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/02_TPC_PID/h1f_rapidity"), y); + } + } else if (stage == ProtonQAStage::TpcTofPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/01_Kinematics/03_TPC_TOF_PID/h1f_p"), p); + registryQaDetector.fill(HIST("Proton/01_Kinematics/03_TPC_TOF_PID/h1f_pt"), pt); + registryQaDetector.fill(HIST("Proton/01_Kinematics/03_TPC_TOF_PID/h1f_eta"), eta); + registryQaDetector.fill(HIST("Proton/01_Kinematics/03_TPC_TOF_PID/h1f_phi"), phi); + registryQaDetector.fill(HIST("Proton/01_Kinematics/03_TPC_TOF_PID/h1f_rapidity"), y); + } else { + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/03_TPC_TOF_PID/h1f_p"), p); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/03_TPC_TOF_PID/h1f_pt"), pt); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/03_TPC_TOF_PID/h1f_eta"), eta); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/03_TPC_TOF_PID/h1f_phi"), phi); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/03_TPC_TOF_PID/h1f_rapidity"), y); + } + } else { // FinalSelected + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/01_Kinematics/04_Final_SelectedProton/h1f_p"), p); + registryQaDetector.fill(HIST("Proton/01_Kinematics/04_Final_SelectedProton/h1f_pt"), pt); + registryQaDetector.fill(HIST("Proton/01_Kinematics/04_Final_SelectedProton/h1f_eta"), eta); + registryQaDetector.fill(HIST("Proton/01_Kinematics/04_Final_SelectedProton/h1f_phi"), phi); + registryQaDetector.fill(HIST("Proton/01_Kinematics/04_Final_SelectedProton/h1f_rapidity"), y); + } else { + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/04_Final_SelectedProton/h1f_p"), p); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/04_Final_SelectedProton/h1f_pt"), pt); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/04_Final_SelectedProton/h1f_eta"), eta); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/04_Final_SelectedProton/h1f_phi"), phi); + registryQaDetector.fill(HIST("AntiProton/01_Kinematics/04_Final_SelectedProton/h1f_rapidity"), y); + } + } + } + + // ── Staged fill helper: DetectorQuality ────────────────────────────────── + template + void fillProtonDetectorQuality(ProtonQAStage stage, TTrack const& trk) + { + const float pt = trk.pt(); + const auto tpcCR = static_cast(trk.tpcNClsCrossedRows()); + const float tpcCRoF = trk.tpcCrossedRowsOverFindableCls(); + const float tpcChi2 = trk.tpcChi2NCl(); + const auto itsN = static_cast(trk.itsNCls()); + const float itsChi2 = trk.itsChi2NCl(); + const float dcaXY = trk.dcaXY(); + const float dcaZ = trk.dcaZ(); + if (stage == ProtonQAStage::RawAfterTrackSel) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/01_RAW_AfterTrackSel/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/01_RAW_AfterTrackSel/h2f_dcaZ_vs_pt"), pt, dcaZ); + } else { + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/01_RAW_AfterTrackSel/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/01_RAW_AfterTrackSel/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/01_RAW_AfterTrackSel/h2f_dcaZ_vs_pt"), pt, dcaZ); + } + } else if (stage == ProtonQAStage::TpcPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/02_TPC_PID/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/02_TPC_PID/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/02_TPC_PID/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/02_TPC_PID/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/02_TPC_PID/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/02_TPC_PID/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/02_TPC_PID/h2f_dcaZ_vs_pt"), pt, dcaZ); + } else { + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/02_TPC_PID/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/02_TPC_PID/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/02_TPC_PID/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/02_TPC_PID/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/02_TPC_PID/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/02_TPC_PID/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/02_TPC_PID/h2f_dcaZ_vs_pt"), pt, dcaZ); + } + } else if (stage == ProtonQAStage::TpcTofPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/03_TPC_TOF_PID/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/03_TPC_TOF_PID/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/03_TPC_TOF_PID/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/03_TPC_TOF_PID/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/03_TPC_TOF_PID/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/03_TPC_TOF_PID/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/03_TPC_TOF_PID/h2f_dcaZ_vs_pt"), pt, dcaZ); + } else { + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/03_TPC_TOF_PID/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/03_TPC_TOF_PID/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/03_TPC_TOF_PID/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/03_TPC_TOF_PID/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/03_TPC_TOF_PID/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/03_TPC_TOF_PID/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/03_TPC_TOF_PID/h2f_dcaZ_vs_pt"), pt, dcaZ); + } + } else { // FinalSelected + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/04_Final_SelectedProton/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/04_Final_SelectedProton/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/04_Final_SelectedProton/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/04_Final_SelectedProton/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/04_Final_SelectedProton/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/04_Final_SelectedProton/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("Proton/02_DetectorQuality/04_Final_SelectedProton/h2f_dcaZ_vs_pt"), pt, dcaZ); + } else { + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/04_Final_SelectedProton/h1f_tpcCrossedRows"), tpcCR); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/04_Final_SelectedProton/h1f_tpcCrossedRowsOverFindable"), tpcCRoF); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/04_Final_SelectedProton/h1f_tpcChi2NCl"), tpcChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/04_Final_SelectedProton/h1f_itsNCls"), itsN); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/04_Final_SelectedProton/h1f_itsChi2NCl"), itsChi2); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/04_Final_SelectedProton/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("AntiProton/02_DetectorQuality/04_Final_SelectedProton/h2f_dcaZ_vs_pt"), pt, dcaZ); + } + } + } + + // ── Staged fill helper: TPC_QA ─────────────────────────────────────────── + template + void fillProtonTpcQA(ProtonQAStage stage, TTrack const& trk) + { + const float p = trk.p(); // total momentum (kept for vs_p plots) + const float pTPC = trk.tpcInnerParam(); // TPC inner param = proton PID selection variable + const float pt = trk.pt(); + const float dEdx = trk.tpcSignal(); + const float nsPr = trk.tpcNSigmaPr(); + const float nsPi = trk.tpcNSigmaPi(); + if (stage == ProtonQAStage::RawAfterTrackSel) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } else { + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/01_RAW_AfterTrackSel/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } + } else if (stage == ProtonQAStage::TpcPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/03_TPC_QA/02_TPC_PID/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/02_TPC_PID/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/02_TPC_PID/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } else { + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/02_TPC_PID/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/02_TPC_PID/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/02_TPC_PID/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/02_TPC_PID/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } + } else if (stage == ProtonQAStage::TpcTofPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/03_TPC_QA/03_TPC_TOF_PID/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/03_TPC_TOF_PID/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/03_TPC_TOF_PID/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } else { + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/03_TPC_TOF_PID/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/03_TPC_TOF_PID/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/03_TPC_TOF_PID/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/03_TPC_TOF_PID/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } + } else { // FinalSelected + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/03_TPC_QA/04_Final_SelectedProton/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/04_Final_SelectedProton/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/04_Final_SelectedProton/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("Proton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } else { + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/04_Final_SelectedProton/h2f_dEdx_vs_p"), p, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/04_Final_SelectedProton/h2f_dEdx_vs_pTPC"), pTPC, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/04_Final_SelectedProton/h2f_dEdx_vs_pt"), pt, dEdx); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_p"), p, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsPr); + registryQaDetector.fill(HIST("AntiProton/03_TPC_QA/04_Final_SelectedProton/h2f_nsigmaPi_vs_pt"), pt, nsPi); + } + } + } + + // ── Staged fill helper: TOF_QA (call only when trk.hasTOF() == true) ───── + template + void fillProtonTofQA(ProtonQAStage stage, TTrack const& trk) + { + const float p = trk.p(); // total momentum (kept for vs_p plots) + const float pTPC = trk.tpcInnerParam(); // TPC inner param = proton PID selection variable + const float pTOF = trk.tofExpMom(); // momentum at the TOF radius (TracksExtra::TOFExpMom) + const float pt = trk.pt(); + const float beta = trk.beta(); + const float nsTOF = trk.tofNSigmaPr(); + const float nsTPC = trk.tpcNSigmaPr(); + const float radius = std::sqrt(nsTPC * nsTPC + nsTOF * nsTOF); + if (stage == ProtonQAStage::RawAfterTrackSel) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/01_RAW_AfterTrackSel/h1f_circular_radius"), radius); + } else { + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/01_RAW_AfterTrackSel/h1f_circular_radius"), radius); + } + } else if (stage == ProtonQAStage::TpcPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/02_TPC_PID/h1f_circular_radius"), radius); + } else { + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/02_TPC_PID/h1f_circular_radius"), radius); + } + } else if (stage == ProtonQAStage::TpcTofPid) { + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/03_TPC_TOF_PID/h1f_circular_radius"), radius); + } else { + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/03_TPC_TOF_PID/h1f_circular_radius"), radius); + } + } else { // FinalSelected + if constexpr (IsProton) { + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("Proton/04_TOF_QA/04_Final_SelectedProton/h1f_circular_radius"), radius); + } else { + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pTPC"), pTPC, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_p"), p, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTPC"), pTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pt"), pt, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaPr_vs_pTOF"), pTOF, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h2f_nsigmaTPC_vs_nsigmaTOF"), nsTPC, nsTOF); + registryQaDetector.fill(HIST("AntiProton/04_TOF_QA/04_Final_SelectedProton/h1f_circular_radius"), radius); + } + } + } + enum class LambdaQAStage { RawCandidate, + Final }; + enum class DaughterParent { Lambda, + AntiLambda }; + enum class DaughterType { Proton, + Pion, + AntiProton, + AntiPion }; + enum class DaughterQAStage { Raw, + Final }; + + // ── Staged fill helper: Lambda QA ────────────────────────────────────────── + template + void fillLambdaQA(LambdaQAStage stage, TV0 const& v0, float ctau, float bgctau) + { + const float pt = v0.pt(); + const float eta = v0.eta(); + const float phi = v0.phi(); + const float rap = IsLambda ? v0.rapidity(1) : v0.rapidity(2); + const float cosPA = v0.v0cosPA(); + const float dcaToPV = v0.dcav0topv(); + const float dcaDau = v0.dcaV0daughters(); + + // Need collision position for accurate decay coordinates, but we use the provided bgctau + // and radius for topology, and we can pull alpha and qtarm. + // VS Code: commented out unused variable. + // constexpr float mLambdaPDG = 1.115683f; + // const float ctau = v0.distovertotmom(0, 0, 0) * mLambdaPDG; // simplified for this block, actual filled in process + const float alpha = v0.alpha(); + const float qtarm = v0.qtarm(); + const float r = v0.v0radius(); + + if constexpr (IsLambda) { + if (stage == LambdaQAStage::RawCandidate) { + registryQaDetector.fill(HIST("Lambda/01_Kinematics/01_RawCandidate/h1f_pt"), pt); + registryQaDetector.fill(HIST("Lambda/01_Kinematics/01_RawCandidate/h1f_eta"), eta); + registryQaDetector.fill(HIST("Lambda/01_Kinematics/01_RawCandidate/h1f_phi"), phi); + + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h1f_cospa"), cosPA); + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h1f_cospa_zoom"), cosPA); + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h1f_dcaV0toPV"), dcaToPV); + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h2f_armpod"), alpha, qtarm); + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h1f_radius"), r); + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h1f_dcaDaughters"), dcaDau); + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h1f_ctau"), ctau); + registryQaDetector.fill(HIST("Lambda/02_Topology/01_RawCandidate/h1f_bgctau"), bgctau); + } else { + registryQaDetector.fill(HIST("Lambda/01_Kinematics/02_Final/h1f_pt"), pt); + registryQaDetector.fill(HIST("Lambda/01_Kinematics/02_Final/h1f_eta"), eta); + registryQaDetector.fill(HIST("Lambda/01_Kinematics/02_Final/h1f_phi"), phi); + + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h1f_cospa"), cosPA); + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h1f_cospa_zoom"), cosPA); + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h1f_dcaV0toPV"), dcaToPV); + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h2f_armpod"), alpha, qtarm); + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h1f_radius"), r); + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h1f_dcaDaughters"), dcaDau); + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h1f_ctau"), ctau); + + registryQaDetector.fill(HIST("Lambda/01_Kinematics/02_Final/h1f_rapidity"), rap); + registryQaDetector.fill(HIST("Lambda/02_Topology/02_Final/h1f_bgctau"), bgctau); + } + } else { + if (stage == LambdaQAStage::RawCandidate) { + registryQaDetector.fill(HIST("AntiLambda/01_Kinematics/01_RawCandidate/h1f_pt"), pt); + registryQaDetector.fill(HIST("AntiLambda/01_Kinematics/01_RawCandidate/h1f_eta"), eta); + registryQaDetector.fill(HIST("AntiLambda/01_Kinematics/01_RawCandidate/h1f_phi"), phi); + + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h1f_cospa"), cosPA); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h1f_cospa_zoom"), cosPA); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h1f_dcaV0toPV"), dcaToPV); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h2f_armpod"), alpha, qtarm); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h1f_radius"), r); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h1f_dcaDaughters"), dcaDau); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h1f_ctau"), ctau); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/01_RawCandidate/h1f_bgctau"), bgctau); + } else { + registryQaDetector.fill(HIST("AntiLambda/01_Kinematics/02_Final/h1f_pt"), pt); + registryQaDetector.fill(HIST("AntiLambda/01_Kinematics/02_Final/h1f_eta"), eta); + registryQaDetector.fill(HIST("AntiLambda/01_Kinematics/02_Final/h1f_phi"), phi); + + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h1f_cospa"), cosPA); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h1f_cospa_zoom"), cosPA); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h1f_dcaV0toPV"), dcaToPV); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h2f_armpod"), alpha, qtarm); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h1f_radius"), r); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h1f_dcaDaughters"), dcaDau); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h1f_ctau"), ctau); + + registryQaDetector.fill(HIST("AntiLambda/01_Kinematics/02_Final/h1f_rapidity"), rap); + registryQaDetector.fill(HIST("AntiLambda/02_Topology/02_Final/h1f_bgctau"), bgctau); + } + } + } + + // ── Staged fill helper: Daughter QA ────────────────────────────────────── + template + void fillDaughterQA(DaughterQAStage stage, TTrack const& trk) + { + const float p = trk.p(); + const float pt = trk.pt(); + const float dedx = trk.tpcSignal(); + const float dcaXY = trk.dcaXY(); + const float dcaZ = trk.dcaZ(); + + // Determine nsigmaTPC based on compile-time DaughterType + float nsigmaTPC = 0.0f; + if constexpr (DauType == DaughterType::Proton || DauType == DaughterType::AntiProton) { + nsigmaTPC = trk.tpcNSigmaPr(); + } else { + nsigmaTPC = trk.tpcNSigmaPi(); + } + + if (stage == DaughterQAStage::Raw) { + if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Proton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + const float nsigmaTOF = trk.tofNSigmaPr(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_nsigmaTOF_vs_p"), p, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_nsigmaTOF_vs_pt"), pt, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_nsigmaTOF_vs_pTOF"), pTOF, nsigmaTOF); + } + // ── Item 3: daughter tracking quality (Lambda/Proton/Raw) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────── + } else if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Pion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_beta_vs_pTOF"), pTOF, beta); + } + // ── Item 3: daughter tracking quality (Lambda/Pion/Raw) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────── + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiProton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + const float nsigmaTOF = trk.tofNSigmaPr(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_nsigmaTOF_vs_p"), p, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_nsigmaTOF_vs_pt"), pt, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_nsigmaTOF_vs_pTOF"), pTOF, nsigmaTOF); + } + // ── Item 3: daughter tracking quality (AntiLambda/AntiProton/Raw) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────────────── + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiPion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_beta_vs_pTOF"), pTOF, beta); + } + // ── Item 3: daughter tracking quality (AntiLambda/AntiPion/Raw) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────────── + } + + // ── Item 4: 2D TOF-matching efficiency map for V0 daughters (Raw) ── + if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Proton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/01_Raw/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } else if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Pion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/01_Raw/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiProton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/01_Raw/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiPion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/01_Raw/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } + // ─────────────────────────────────────────────────────────────────── + + } else { + if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Proton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + const float nsigmaTOF = trk.tofNSigmaPr(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_nsigmaTOF_vs_p"), p, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_nsigmaTOF_vs_pt"), pt, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_nsigmaTOF_vs_pTOF"), pTOF, nsigmaTOF); + } + // ── Item 3: daughter tracking quality (Lambda/Proton/Final) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────── + } else if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Pion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_beta_vs_pTOF"), pTOF, beta); + } + // ── Item 3: daughter tracking quality (Lambda/Pion/Final) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────── + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiProton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + const float nsigmaTOF = trk.tofNSigmaPr(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_beta_vs_pTOF"), pTOF, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_nsigmaTOF_vs_p"), p, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_nsigmaTOF_vs_pt"), pt, nsigmaTOF); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_nsigmaTOF_vs_pTOF"), pTOF, nsigmaTOF); + } + // ── Item 3: daughter tracking quality (AntiLambda/AntiProton/Final) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────────────── + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiPion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_dEdx_vs_p"), p, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_dEdx_vs_pt"), pt, dedx); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_nsigmaTPC_vs_p"), p, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_nsigmaTPC_vs_pt"), pt, nsigmaTPC); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_dcaXY_vs_pt"), pt, dcaXY); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_dcaZ_vs_pt"), pt, dcaZ); + if (trk.hasTOF()) { + const float beta = trk.beta(); + const float pTOF = trk.tofExpMom(); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_beta_vs_p"), p, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_beta_vs_pt"), pt, beta); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_beta_vs_pTOF"), pTOF, beta); + } + // ── Item 3: daughter tracking quality (AntiLambda/AntiPion/Final) ── + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h1f_tpcCrossedRows"), static_cast(trk.tpcNClsCrossedRows())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h1f_tpcCrossedRowsOverFindable"), trk.tpcCrossedRowsOverFindableCls()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h1f_tpcChi2NCl"), trk.tpcChi2NCl()); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h1f_itsNCls"), static_cast(trk.itsNCls())); + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h1f_itsChi2NCl"), trk.itsChi2NCl()); + // ────────────────────────────────────────────────────────────── + } + + // ── Item 4: 2D TOF-matching efficiency map for V0 daughters (Final) ── + if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Proton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/01_Proton/02_Final/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } else if constexpr (Parent == DaughterParent::Lambda && DauType == DaughterType::Pion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/01_Lambda/02_Pion/02_Final/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiProton) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/01_AntiProton/02_Final/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } else if constexpr (Parent == DaughterParent::AntiLambda && DauType == DaughterType::AntiPion) { + registryQaDetector.fill(HIST("LambdaDaughters_PID/02_AntiLambda/02_AntiPion/02_Final/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + } + // ───────────────────────────────────────────────────────────────────── + } + } + + // Per-particle cache used by the pair loops (avoids recomputing per pair). + struct PartInfo { + int64_t gid = 0; + float px = 0.f, py = 0.f, pz = 0.f, E = 0.f; + float phi = 0.f, y = 0.f, pt = 0.f; + int idxY = -1; + }; + + template + PartInfo makeProtonInfo(TTrack const& t) const + { + PartInfo p; + p.gid = t.globalIndex(); + p.px = t.px(); + p.py = t.py(); + p.pz = t.pz(); + p.E = std::sqrt(p.px * p.px + p.py * p.py + p.pz * p.pz + kMassProton * kMassProton); + p.phi = t.phi(); + p.pt = t.pt(); + p.y = protonRapidity(t); + p.idxY = unrolledIndexY(p.y, p.phi); + return p; + } + + void init(InitContext const&) + { + // ── Populate rho1/rho2 grid members from Configurables ────────────────── + state.mRhoPhiBins = cRhoPhiBins.value; + state.mRhoYBins = cRhoYBins.value; + state.mRhoYMin = cRhoYMin.value; + state.mRhoYMax = cRhoYMax.value; + state.mRhoUnrolledBinsY = state.mRhoYBins * state.mRhoPhiBins; + // ──────────────────────────────────────────────────────────────────────── + + buildPtBins(); + + // ── Common axis definitions ────────────────────────────────────────── + AxisSpec lambdaMassAxis = {200, 1.08f, 1.15f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec vertexZAxis = {100, -15.f, 15.f, "vrtx_{Z} [cm]"}; + AxisSpec ptAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisP = {200, 0.0f, 6.0f, "p (GeV/c)"}; + // axisPTPC: TPC inner momentum — the variable that drives the proton PID selection logic. + // Used alongside axisP so both total-p and pTPC views are available for every diagnostic plot. + AxisSpec axisPTPC = {200, 0.0f, 6.0f, "p_{TPC} (GeV/c)"}; + // p_TOF = trk.tofExpMom() (TracksExtra::TOFExpMom): momentum evaluated at the TOF radius, + // i.e. the momentum actually used to build the TOF nsigma/beta response. Only valid when hasTOF()==true. + AxisSpec axisPTOF = {200, 0.0f, 6.0f, "p_{TOF} (GeV/c)"}; + AxisSpec axisTPCdEdx = {200, 0.0f, 200.0f, "TPC dE/dx"}; + AxisSpec qaEtaAxis = {40, -0.8f, 0.8f, "#eta"}; // These should be -0.8 to 0.8 only prev it was -1 to 1 + AxisSpec qaPhiAxis = {72, 0.0f, o2::constants::math::TwoPI, "#varphi"}; + AxisSpec nsigmaAxis = {100, -10.0f, 10.0f, "n#sigma"}; + AxisSpec dcaAxis = {100, -1.0f, 1.0f, "DCAxy (cm)"}; + AxisSpec dcaZAxis = {100, -1.0f, 1.0f, "DCAz (cm)"}; + AxisSpec dcaAxisWide = {200, 0.0f, 5.0f, "DCA (cm)"}; + const std::string centLabel = cUseFT0C.value ? "FT0C" : "FT0M"; + const std::string centAxisTitle = centLabel + " Percentile (%)"; + AxisSpec multAxis = {100, 0.0f, 100.0f, centAxisTitle}; + AxisSpec phiAxis = {state.mRhoPhiBins, 0.0f, o2::constants::math::TwoPI, "#varphi"}; + AxisSpec unrolledAxisY = {state.mRhoUnrolledBinsY, 0.0f, static_cast(state.mRhoUnrolledBinsY), "index(y,#varphi)"}; + AxisSpec rapidityAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; + AxisSpec yAxis = {state.mRhoYBins, state.mRhoYMin, state.mRhoYMax, "y"}; + + // pT axis for ProtonCounts_byPID_Check: 0.15 → 6.0, 118 bins of 0.05 width + AxisSpec pidCheckPtAxis = {118, 0.15f, 6.05f, "#it{p}_{T} (GeV/#it{c})"}; + // ───────────────────────────────────────────────────────────────────── + + // ── Other / event-level histograms ─────────────────────────────────── + registryOther.add("hVertexZRec", "hVertexZRec", {HistType::kTH1F, {vertexZAxis}}); + registryOther.add("hFT0MPercentile", ("Centrality percentile (" + centLabel + ")").c_str(), {HistType::kTH1F, {multAxis}}); + // ── MC efficiency maps (processMC): x = centrality (cCentBins), y = pT, z = y (same range/bins as rho1) ── + { + const AxisSpec axCent(cCentBins, "centrality (%)"); + const AxisSpec axPt{60, 0.0, 6.0, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axY{cRhoYBins.value, cRhoYMin.value, cRhoYMax.value, "y"}; + for (const char* name : {"GenLambda", "GenAntiLambda", "GenProton", "GenAntiProton", + "RecLambda", "RecAntiLambda", "RecProton", "RecAntiProton"}) { + registryEff.add(name, name, {HistType::kTH3F, {axCent, axPt, axY}}); + } + } + registryOther.add("hEffWeightProton", "Efficiency weight 1/#epsilon, protons", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); + registryOther.add("hEffWeightLambda", "Efficiency weight 1/#epsilon, #Lambda", {HistType::kTH1F, {{200, 0.0f, 20.0f}}}); + registryOther.add("hEffWeightZeroCount", "Particles with efficiency weight 0 (1=p, 2=#bar{p}, 3=#Lambda, 4=#bar{#Lambda})", {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + registryOther.add("hNSelectedPrimProtons", "Number of selected primary protons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hNSelectedPrimAntiProtons", "Number of selected primary antiprotons per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hNSelLambda", "Number of selected Lambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + registryOther.add("hNSelAntiLambda", "Number of selected AntiLambda candidates per event", {HistType::kTH1F, {{100, 0.0f, 100.0f}}}); + // ── V0-daughter veto QA counters ───────────────────────────────────── + // Proton veto counters + registryOther.add("hNPrimProtonBeforeVeto", "Proton candidates before V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPrimProtonRemovedByVeto", "Proton candidates removed by V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPrimProtonAfterVeto", "Proton candidates after V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + // AntiProton veto counters + registryOther.add("hNPrimAntiProtonBeforeVeto", "AntiProton candidates before V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPrimAntiProtonRemovedByVeto", "AntiProton candidates removed by V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPrimAntiProtonAfterVeto", "AntiProton candidates after V0-daughter veto;N;Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + // ───────────────────────────────────────────────────────────────────── + registryOther.add("hPtSelLambda", "Selected #Lambda p_{T} spectrum (raw counts);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hPtSelAntiLambda", "Selected #bar{#Lambda} p_{T} spectrum (raw counts);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hPtSelectedPrimProton", "Selected primary proton p_{T} spectrum (raw counts, after V0-daughter veto);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hPtSelectedPrimAntiProton", "Selected primary antiproton p_{T} spectrum (raw counts, after V0-daughter veto);#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {ptAxis}}); + registryOther.add("hNPairs_Lambda_PrimProton", "#Lambda-p pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPairs_Lambda_PrimAntiProton", "#Lambda-#bar{p} pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPairs_AntiLambda_PrimProton", "#bar{#Lambda}-p pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryOther.add("hNPairs_AntiLambda_PrimAntiProton", "#bar{#Lambda}-#bar{p} pairs per event (after q_{inv} cut);N_{pairs};Events", {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + + // ── Shared-daughter (union) cleaning diagnostics for Lambda / AntiLambda ── + registryOther.add("h1f_nLambda_beforeClean", "N_{#Lambda} per event before shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nLambda_rejectedClean", "N_{#Lambda} rejected by shared-daughter cleaning per event;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nLambda_afterClean", "N_{#Lambda} per event after shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_beforeClean", "N_{#bar{#Lambda}} per event before shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_rejectedClean", "N_{#bar{#Lambda}} rejected by shared-daughter cleaning per event;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("h1f_nAntiLambda_afterClean", "N_{#bar{#Lambda}} per event after shared-daughter cleaning;N;Events", {HistType::kTH1F, {{100, -0.5f, 99.5f}}}); + registryOther.add("hSharedDauCleaningSums", "Shared-daughter cleaning: sums over events;Quantity;Sum", {HistType::kTH1D, {{9, 0.5f, 9.5f}}}); + { + auto hSD = registryOther.get(HIST("hSharedDauCleaningSums")); + hSD->GetXaxis()->SetBinLabel(1, "Sum N_L raw"); + hSD->GetXaxis()->SetBinLabel(2, "Sum N_L rej"); + hSD->GetXaxis()->SetBinLabel(3, "Sum N_L clean"); + hSD->GetXaxis()->SetBinLabel(4, "Sum N_AL raw"); + hSD->GetXaxis()->SetBinLabel(5, "Sum N_AL rej"); + hSD->GetXaxis()->SetBinLabel(6, "Sum N_AL clean"); + hSD->GetXaxis()->SetBinLabel(7, "Sum N_L*(N_L-1)"); + hSD->GetXaxis()->SetBinLabel(8, "Sum N_AL*(N_AL-1)"); + hSD->GetXaxis()->SetBinLabel(9, "Sum N_L*N_AL"); + } + + // ══════════════════════════════════════════════════════════════════════════ + // NEW STAGED QA_Detec REGISTRATION + // ══════════════════════════════════════════════════════════════════════════ + + // Axes used for the new hierarchy + AxisSpec axisBeta = {110, 0.0f, 1.1f, "TOF #beta"}; + AxisSpec axisPtProton = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisCosPA_zoom = {200, 0.99f, 1.0f, "Cos(PA)"}; + AxisSpec axisDecayR = {100, 0.0f, 50.0f, "cm"}; + AxisSpec axisCtau = {100, 0.0f, 50.0f, "ctau (cm)"}; + AxisSpec axisCutFlow = {15, 0.5f, 15.5f, "Cut stage"}; + + auto addProtonQAForStage = [&](const char* species, const char* stage) { + // Kinematics + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_p", species, stage), "p;p (GeV/c);Counts", {HistType::kTH1F, {axisP}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_pt", species, stage), "p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {axisPtProton}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_eta", species, stage), "#eta;#eta;Counts", {HistType::kTH1F, {qaEtaAxis}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_phi", species, stage), "#phi;#phi;Counts", {HistType::kTH1F, {qaPhiAxis}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_rapidity", species, stage), "y;y;Counts", {HistType::kTH1F, {rapidityAxis}}); + + // DetectorQuality + registryQaDetector.add(Form("%s/02_DetectorQuality/%s/h1f_tpcCrossedRows", species, stage), "TPC Crossed Rows;N_{cr};Counts", {HistType::kTH1F, {{160, 0.0f, 160.0f, "TPC crossed rows"}}}); + registryQaDetector.add(Form("%s/02_DetectorQuality/%s/h1f_tpcCrossedRowsOverFindable", species, stage), "TPC Crossed Rows / Findable;Ratio;Counts", {HistType::kTH1F, {{100, 0.0f, 1.5f, "N_{cr}/N_{findable}"}}}); + registryQaDetector.add(Form("%s/02_DetectorQuality/%s/h1f_tpcChi2NCl", species, stage), "TPC #chi^{2}/NCl;#chi^{2}/NCl;Counts", {HistType::kTH1F, {{100, 0.0f, 10.0f, "TPC #chi^{2}/NCl"}}}); + registryQaDetector.add(Form("%s/02_DetectorQuality/%s/h1f_itsNCls", species, stage), "ITS NCls;N_{cls};Counts", {HistType::kTH1F, {{10, 0.0f, 10.0f, "ITS N_{cls}"}}}); + registryQaDetector.add(Form("%s/02_DetectorQuality/%s/h1f_itsChi2NCl", species, stage), "ITS #chi^{2}/NCl;#chi^{2}/NCl;Counts", {HistType::kTH1F, {{100, 0.0f, 40.0f, "ITS #chi^{2}/NCl"}}}); + registryQaDetector.add(Form("%s/02_DetectorQuality/%s/h2f_dcaXY_vs_pt", species, stage), "DCA_{xy} vs p_{T};p_{T};DCA_{xy}", {HistType::kTH2F, {axisPtProton, dcaAxis}}); + registryQaDetector.add(Form("%s/02_DetectorQuality/%s/h2f_dcaZ_vs_pt", species, stage), "DCA_{z} vs p_{T};p_{T};DCA_{z}", {HistType::kTH2F, {axisPtProton, dcaZAxis}}); + + // TPC_QA — vs_p (total momentum) kept; vs_pTPC (TPC inner param = PID variable) added alongside + registryQaDetector.add(Form("%s/03_TPC_QA/%s/h2f_dEdx_vs_p", species, stage), "TPC dE/dx vs p;p (GeV/c);dE/dx", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + registryQaDetector.add(Form("%s/03_TPC_QA/%s/h2f_dEdx_vs_pTPC", species, stage), "TPC dE/dx vs p_{TPC};p_{TPC} (GeV/c);dE/dx", {HistType::kTH2F, {axisPTPC, axisTPCdEdx}}); + registryQaDetector.add(Form("%s/03_TPC_QA/%s/h2f_dEdx_vs_pt", species, stage), "TPC dE/dx vs p_{T};p_{T} (GeV/c);dE/dx", {HistType::kTH2F, {axisPtProton, axisTPCdEdx}}); + registryQaDetector.add(Form("%s/03_TPC_QA/%s/h2f_nsigmaPr_vs_p", species, stage), "TPC n#sigma_{p} vs p;p (GeV/c);n#sigma", {HistType::kTH2F, {axisP, nsigmaAxis}}); + registryQaDetector.add(Form("%s/03_TPC_QA/%s/h2f_nsigmaPr_vs_pTPC", species, stage), "TPC n#sigma_{p} vs p_{TPC};p_{TPC} (GeV/c);n#sigma", {HistType::kTH2F, {axisPTPC, nsigmaAxis}}); + registryQaDetector.add(Form("%s/03_TPC_QA/%s/h2f_nsigmaPr_vs_pt", species, stage), "TPC n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma", {HistType::kTH2F, {axisPtProton, nsigmaAxis}}); + registryQaDetector.add(Form("%s/03_TPC_QA/%s/h2f_nsigmaPi_vs_pt", species, stage), "TPC n#sigma_{#pi} vs p_{T};p_{T} (GeV/c);n#sigma", {HistType::kTH2F, {axisPtProton, nsigmaAxis}}); + + // TOF_QA — vs_p (total momentum) kept; vs_pTPC (TPC inner param = PID variable) added alongside + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_beta_vs_p", species, stage), "TOF #beta vs p;p (GeV/c);#beta", {HistType::kTH2F, {axisP, axisBeta}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_beta_vs_pTPC", species, stage), "TOF #beta vs p_{TPC};p_{TPC} (GeV/c);#beta", {HistType::kTH2F, {axisPTPC, axisBeta}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_beta_vs_pt", species, stage), "TOF #beta vs p_{T};p_{T} (GeV/c);#beta", {HistType::kTH2F, {axisPtProton, axisBeta}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_beta_vs_pTOF", species, stage), "TOF #beta vs p_{TOF};p_{TOF} (GeV/c);#beta", {HistType::kTH2F, {axisPTOF, axisBeta}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_p", species, stage), "TOF n#sigma_{p} vs p;p (GeV/c);n#sigma", {HistType::kTH2F, {axisP, nsigmaAxis}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pTPC", species, stage), "TOF n#sigma_{p} vs p_{TPC};p_{TPC} (GeV/c);n#sigma", {HistType::kTH2F, {axisPTPC, nsigmaAxis}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pt", species, stage), "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma", {HistType::kTH2F, {axisPtProton, nsigmaAxis}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaPr_vs_pTOF", species, stage), "TOF n#sigma_{p} vs p_{TOF};p_{TOF} (GeV/c);n#sigma", {HistType::kTH2F, {axisPTOF, nsigmaAxis}}); + + // --- 2D TPC vs TOF n-sigma and 1D circular radius (competitive-veto vs circle comparison) --- + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h2f_nsigmaTPC_vs_nsigmaTOF", species, stage), + "TPC vs TOF n#sigma_{p};n#sigma^{TPC}_{p};n#sigma^{TOF}_{p}", + {HistType::kTH2F, {nsigmaAxis, nsigmaAxis}}); + registryQaDetector.add(Form("%s/04_TOF_QA/%s/h1f_circular_radius", species, stage), + "Circular PID Radius;R = #sqrt{(n#sigma^{TPC}_{p})^{2} + (n#sigma^{TOF}_{p})^{2}};Counts", + {HistType::kTH1F, {{100, 0.0f, 10.0f, "R"}}}); + }; + + static constexpr std::array localProtonStages = { + "01_RAW_AfterTrackSel", "02_TPC_PID", "03_TPC_TOF_PID", "04_Final_SelectedProton"}; + for (const auto& species : {"Proton", "AntiProton"}) { + for (const auto& stage : localProtonStages) { + addProtonQAForStage(species, stage); + } + } + + auto addLambdaQAForStage = [&](const char* species, const char* stage) { + // Kinematics + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_pt", species, stage), "V0 p_{T} (RawCandidate = all V0s before any cut, both hypotheses);p_{T} (GeV/c);Counts", {HistType::kTH1F, {axisPtProton}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_eta", species, stage), "V0 #eta (RawCandidate = all V0s before any cut, both hypotheses);#eta;Counts", {HistType::kTH1F, {qaEtaAxis}}); + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_phi", species, stage), "V0 #varphi (RawCandidate = all V0s before any cut, both hypotheses);#varphi;Counts", {HistType::kTH1F, {qaPhiAxis}}); + if (std::string(stage) == "02_Final") { + registryQaDetector.add(Form("%s/01_Kinematics/%s/h1f_rapidity", species, stage), "y;y;Counts", {HistType::kTH1F, {rapidityAxis}}); + } + + // Topology + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_cospa", species, stage), "V0 cos(PA), full range;cos(PA);Counts", {HistType::kTH1F, {{100, 0.9f, 1.0f, "cos(PA)"}}}); + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_cospa_zoom", species, stage), "V0 cos(PA), zoom 0.99-1.0;cos(PA);Counts", {HistType::kTH1F, {axisCosPA_zoom}}); + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_dcaV0toPV", species, stage), "DCA V0 to PV;DCA (cm);Counts", {HistType::kTH1F, {dcaAxisWide}}); + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_ctau", species, stage), "ctau;ctau (cm);Counts", {HistType::kTH1F, {axisCtau}}); + if (std::string(stage) == "01_RawCandidate" || std::string(stage) == "02_Final") { + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_bgctau", species, stage), "bgctau;bgctau (cm);Counts", {HistType::kTH1F, {{100, 0.0f, 200.0f, "bgctau (cm)"}}}); + } + registryQaDetector.add(Form("%s/02_Topology/%s/h2f_armpod", species, stage), "Armenteros-Podolanski;#alpha;q_{T}", {HistType::kTH2F, {{80, -1.0f, 1.0f, "#alpha"}, {40, 0.0f, 0.4f, "q_{T}"}}}); + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_radius", species, stage), "Decay Radius;R (cm);Counts", {HistType::kTH1F, {axisDecayR}}); + registryQaDetector.add(Form("%s/02_Topology/%s/h1f_dcaDaughters", species, stage), "DCA Daughters;DCA (cm);Counts", {HistType::kTH1F, {dcaAxisWide}}); + }; + + for (const auto& species : {"Lambda", "AntiLambda"}) { + registryQaDetector.add(Form("%s/InvariantMass/NoMassCut/h2f_mass_vs_pt", species), "Mass vs p_{T};p_{T};Mass", {HistType::kTH2F, {axisPtProton, lambdaMassAxis}}); + registryQaDetector.add(Form("%s/InvariantMass/MassCut/h2f_mass_vs_pt", species), "Mass vs p_{T};p_{T};Mass", {HistType::kTH2F, {axisPtProton, lambdaMassAxis}}); + // ── Item 2: K0S competing-mass hypothesis QA ─────────────────────── + registryQaDetector.add(Form("%s/InvariantMass/h1f_mK0Short_beforeCut", species), + "m_{K0S} before K0S rejection cut;m_{K0S} (GeV/c^{2});Counts", + {HistType::kTH1F, {{200, 0.40f, 0.60f, "m_{K0S} (GeV/c^{2})"}}}); + registryQaDetector.add(Form("%s/InvariantMass/h1f_mK0Short_afterLambdaSel", species), + "m_{K0S} for V0s passing Lambda/AntiLambda selection;m_{K0S} (GeV/c^{2});Counts", + {HistType::kTH1F, {{200, 0.40f, 0.60f, "m_{K0S} (GeV/c^{2})"}}}); + registryQaDetector.add(Form("%s/InvariantMass/h2f_mLambda_vs_mK0Short", species), + "m_{#Lambda} vs m_{K0S};m_{K0S} (GeV/c^{2});m_{#Lambda} (GeV/c^{2})", + {HistType::kTH2F, {{200, 0.40f, 0.60f, "m_{K0S}"}, lambdaMassAxis}}); + // ── Item 2: mass vs acceptance uniformity ────────────────────────── + registryQaDetector.add(Form("%s/InvariantMass/h2f_mass_vs_eta", species), + "Mass vs #eta;#eta;m (GeV/c^{2})", + {HistType::kTH2F, {qaEtaAxis, lambdaMassAxis}}); + registryQaDetector.add(Form("%s/InvariantMass/h2f_mass_vs_phi", species), + "Mass vs #varphi;#varphi;m (GeV/c^{2})", + {HistType::kTH2F, {qaPhiAxis, lambdaMassAxis}}); + // ────────────────────────────────────────────────────────────────── + for (const auto& stage : {"01_RawCandidate", "02_Final"}) { + addLambdaQAForStage(species, stage); + } + } + + auto addDaughterQA = [&](const char* parent, const char* dauType, const char* stage) { + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_dEdx_vs_p", parent, dauType, stage), "TPC dE/dx vs p;p;dE/dx", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_dEdx_vs_pt", parent, dauType, stage), "TPC dE/dx vs p_{T};p_{T};dE/dx", {HistType::kTH2F, {axisPtProton, axisTPCdEdx}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_nsigmaTPC_vs_p", parent, dauType, stage), "TPC n#sigma vs p;p;n#sigma", {HistType::kTH2F, {axisP, nsigmaAxis}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_nsigmaTPC_vs_pt", parent, dauType, stage), "TPC n#sigma vs p_{T};p_{T};n#sigma", {HistType::kTH2F, {axisPtProton, nsigmaAxis}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_dcaXY_vs_pt", parent, dauType, stage), "DCA_{xy} vs p_{T};p_{T};DCA_{xy} (cm)", {HistType::kTH2F, {axisPtProton, dcaAxis}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_dcaZ_vs_pt", parent, dauType, stage), "DCA_{z} vs p_{T};p_{T};DCA_{z} (cm)", {HistType::kTH2F, {axisPtProton, dcaZAxis}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_beta_vs_p", parent, dauType, stage), "TOF #beta vs p;p;#beta", {HistType::kTH2F, {axisP, axisBeta}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_beta_vs_pt", parent, dauType, stage), "TOF #beta vs p_{T};p_{T};#beta", {HistType::kTH2F, {axisPtProton, axisBeta}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_beta_vs_pTOF", parent, dauType, stage), "TOF #beta vs p_{TOF};p_{TOF};#beta", {HistType::kTH2F, {axisPTOF, axisBeta}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_nsigmaTOF_vs_p", parent, dauType, stage), "TOF n#sigma vs p;p;n#sigma", {HistType::kTH2F, {axisP, nsigmaAxis}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_nsigmaTOF_vs_pt", parent, dauType, stage), "TOF n#sigma vs p_{T};p_{T};n#sigma", {HistType::kTH2F, {axisPtProton, nsigmaAxis}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_nsigmaTOF_vs_pTOF", parent, dauType, stage), "TOF n#sigma vs p_{TOF};p_{TOF};n#sigma", {HistType::kTH2F, {axisPTOF, nsigmaAxis}}); + // ── Item 3: Daughter tracking quality ──────────────────────────────── + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h1f_tpcCrossedRows", parent, dauType, stage), + "TPC Crossed Rows;N_{cr};Counts", {HistType::kTH1F, {{160, 0.0f, 160.0f, "TPC crossed rows"}}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h1f_tpcCrossedRowsOverFindable", parent, dauType, stage), + "TPC Crossed Rows / Findable;Ratio;Counts", {HistType::kTH1F, {{100, 0.0f, 1.5f, "N_{cr}/N_{findable}"}}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h1f_tpcChi2NCl", parent, dauType, stage), + "TPC #chi^{2}/NCl;#chi^{2}/NCl;Counts", {HistType::kTH1F, {{100, 0.0f, 10.0f, "TPC #chi^{2}/NCl"}}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h1f_itsNCls", parent, dauType, stage), + "ITS NCls;N_{cls};Counts", {HistType::kTH1F, {{10, 0.0f, 10.0f, "ITS N_{cls}"}}}); + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h1f_itsChi2NCl", parent, dauType, stage), + "ITS #chi^{2}/NCl;#chi^{2}/NCl;Counts", {HistType::kTH1F, {{100, 0.0f, 40.0f, "ITS #chi^{2}/NCl"}}}); + // ── Item 4: 2D TOF-matching efficiency maps (eta × phi) ────────────── + registryQaDetector.add(Form("LambdaDaughters_PID/%s/%s/%s/h2f_TOFMatchedFractionVsEtaPhi", parent, dauType, stage), + "TOF matched fraction vs (#eta, #varphi);#eta;#varphi;Fraction", {HistType::kTProfile2D, {qaEtaAxis, qaPhiAxis}}); + // ───────────────────────────────────────────────────────────────────── + }; + + for (const auto& stage : {"01_Raw", "02_Final"}) { + addDaughterQA("01_Lambda", "01_Proton", stage); + addDaughterQA("01_Lambda", "02_Pion", stage); + addDaughterQA("02_AntiLambda", "01_AntiProton", stage); + addDaughterQA("02_AntiLambda", "02_AntiPion", stage); + } + + registryQaDetector.add("Selection/TOF_Matching/hTOFMatchedFractionVsPt", "TOF-matched fraction of selected (anti)protons (before V0-daughter veto) vs p_{T};p_{T} (GeV/c);TOF-matched fraction", {HistType::kTProfile, {axisPtProton}}); + // ── Item 4: 2D TOF-matching efficiency maps (eta × phi) ────────────────── + registryQaDetector.add("Selection/TOF_Matching/h2f_TOFMatchedFractionVsEtaPhi", + "TOF-matched fraction of selected (anti)protons (before V0-daughter veto) vs (#eta, #varphi);#eta;#varphi;TOF-matched fraction", + {HistType::kTProfile2D, {qaEtaAxis, qaPhiAxis}}); + // ───────────────────────────────────────────────────────────────────────── + registryQaDetector.add("Selection/TOF_Matching/hCutFlow", "(Anti)proton track cutflow (both charges);Stage;Counts", {HistType::kTH1F, {axisCutFlow}}); + { + auto hCF = registryQaDetector.get(HIST("Selection/TOF_Matching/hCutFlow")); + hCF->GetXaxis()->SetBinLabel(1, "All tracks"); + hCF->GetXaxis()->SetBinLabel(2, "hasTPC"); + hCF->GetXaxis()->SetBinLabel(3, "TPC crossed rows"); + hCF->GetXaxis()->SetBinLabel(4, "TPC rows/findable"); + hCF->GetXaxis()->SetBinLabel(5, "TPC chi2"); + hCF->GetXaxis()->SetBinLabel(6, "ITS Ncls"); + hCF->GetXaxis()->SetBinLabel(7, "ITS chi2"); + hCF->GetXaxis()->SetBinLabel(8, "TPC signal"); + hCF->GetXaxis()->SetBinLabel(9, "p_TPC & pT window"); + hCF->GetXaxis()->SetBinLabel(10, "|y| window"); + hCF->GetXaxis()->SetBinLabel(11, "DCA xy,z"); + hCF->GetXaxis()->SetBinLabel(12, "PID (TPC/TOF)"); + hCF->GetXaxis()->SetBinLabel(13, "pi/K/e veto"); + hCF->GetXaxis()->SetBinLabel(14, "V0-daughter veto"); + hCF->GetXaxis()->SetBinLabel(15, "Selected"); + } + + // ── V0 (Lambda / AntiLambda) cutflow histograms ───────────────────────── + // Bins 1-3 are filled for every V0 under BOTH hypotheses (PID hypothesis not yet known). + // From bin 4 on, each V0 is counted only in the cutflow of the hypothesis chosen by TPC PID. + // Bin 10 = survivors of shared-daughter cleaning (the sample entering rho1 / rho2). Bin 11 is spare. + registryQaDetector.add("Lambda/hCutFlow", "#Lambda candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); + registryQaDetector.add("AntiLambda/hCutFlow", "#bar{#Lambda} candidate cutflow;Stage;Counts", {HistType::kTH1F, {{11, 0.5f, 11.5f}}}); + { + const char* v0CfLabels[10] = {"All V0s", "V0 Type", "Daughter Kinematics", "Unambiguous TPC PID", "Rapidity", + "pT Acceptance", "Topology & PV DCA", "cTau & K0s Rej", "Mass Window", "Shared-Dau Cleaned"}; + auto hCFL = registryQaDetector.get(HIST("Lambda/hCutFlow")); + auto hCFAL = registryQaDetector.get(HIST("AntiLambda/hCutFlow")); + for (int ib = 0; ib < 10; ++ib) { + hCFL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); + hCFAL->GetXaxis()->SetBinLabel(ib + 1, v0CfLabels[ib]); + } + } + + // 2D cutflow vs pT, separate for protons and antiprotons. + // y bins 1-15 = sequential stages (same as hCutFlow); 16-21 = veto REASONS (not sequential, a track can fill several) + registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton", "Proton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); + registryQaDetector.add("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton", "AntiProton cutflow vs p_{T};p_{T} (GeV/c);Stage", {HistType::kTH2F, {axisPtProton, {21, 0.5f, 21.5f}}}); + { + const char* cfLabels2D[21] = {"All tracks", "hasTPC", "TPC crossed rows", "TPC rows/findable", "TPC chi2", "ITS Ncls", "ITS chi2", + "TPC signal", "p_TPC & pT window", "|y| window", "DCA xy,z", "PID (TPC/TOF)", "pi/K/e veto", + "V0-daughter veto", "Selected", "veto: TPC pi", "veto: TPC K", "veto: TPC e", + "veto: TOF pi", "veto: TOF K", "veto: TOF e"}; + auto hCFp = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_Proton")); + auto hCFa = registryQaDetector.get(HIST("Selection/TOF_Matching/h2f_CutFlow_vs_pt_AntiProton")); + for (int ib = 0; ib < 21; ++ib) { + hCFp->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); + hCFa->GetYaxis()->SetBinLabel(ib + 1, cfLabels2D[ib]); + } + } + + // ══════════════════════════════════════════════════════════════════════════ + // EXISTING QA_Detec REGISTRATION (Preserved for backward compatibility) + // ══════════════════════════════════════════════════════════════════════════ + + // -- Event cutflow -- + registryOther.add("hEventCutflow", "Event Cutflow;Step;Counts", + {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + { + auto hEC = registryOther.get(HIST("hEventCutflow")); + hEC->GetXaxis()->SetBinLabel(1, "sel8 + |z|GetXaxis()->SetBinLabel(2, "numContrib >= 1"); + hEC->GetXaxis()->SetBinLabel(3, Form("%s in [%.0f, %.0f]", cUseFT0C.value ? "centFT0C" : "centFT0M", cMinCentrality.value, cMaxCentrality.value)); + hEC->GetXaxis()->SetBinLabel(4, "INEL > 0"); + } + + // ── ProtonVetoQA histograms ─────────────────────────────────────────── + // Purpose: quantify the importance of the V0-daughter veto for the + // primary-proton sample used in rho1(p), rho2(pp), rho2(p pbar). + // Definitions (protons only, sign > 0): + // h1f_nBeforeVeto : N passing all quality+PID+DCA cuts, before veto. + // h1f_nRemovedByVeto : N rejected because globalIndex ∈ v0DaughterTrackIds. + // h1f_nAfterVeto : N entering selectedPrimProtons (= before - removed). + // h1f_fractionRemoved: N_removed / N_before per event (0 when N_before = 0). + // + // All four histograms are filled ONCE PER EVENT. + // Corresponding run-level totals are printed in endOfStream(). + registryProtonVetoQA.add( + "h1f_nBeforeVeto", + "Proton V0-daughter veto QA: N_{before veto} per event;N_{before veto};Events", + {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryProtonVetoQA.add( + "h1f_nRemovedByVeto", + "Proton V0-daughter veto QA: N_{removed by veto} per event;N_{removed};Events", + {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryProtonVetoQA.add( + "h1f_nAfterVeto", + "Proton V0-daughter veto QA: N_{after veto} per event;N_{after veto};Events", + {HistType::kTH1F, {{200, 0.0f, 200.0f}}}); + registryProtonVetoQA.add( + "h1f_fractionRemoved", + "Proton V0-daughter veto QA: fraction_{removed} = N_{removed}/N_{before} per event;fraction_{removed};Events", + {HistType::kTH1F, {{110, 0.0f, 1.1f}}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Item 5: Event-level pileup rejection QA ─────────────────────────── + // hPileupFlags: each bin corresponds to a different event-selection bit. + // Bin 1 = kNoSameBunchPileup, Bin 2 = kIsGoodZvtxFT0vsPV, + // Bin 3 = kIsVertexITSTPC, Bin 4 = kIsVertexTOFmatched, + // Bin 5 = kIsVertexTRDmatched, Bin 6 = kNoSameBunchPileup && kIsGoodZvtxFT0vsPV && kIsVertexITSTPC all set. + registryOther.add("hPileupFlags", + "Event-level pile-up rejection flags;Flag;Events passing", + {HistType::kTH1F, {{6, 0.5f, 6.5f, "Pileup flag"}}}); + { + auto hPU = registryOther.get(HIST("hPileupFlags")); + hPU->GetXaxis()->SetBinLabel(1, "NoSameBunchPileup"); + hPU->GetXaxis()->SetBinLabel(2, "GoodZvtxFT0vsPV"); + hPU->GetXaxis()->SetBinLabel(3, "VertexITSTPC"); + hPU->GetXaxis()->SetBinLabel(4, "VertexTOFmatched"); + hPU->GetXaxis()->SetBinLabel(5, "VertexTRDmatched"); + hPU->GetXaxis()->SetBinLabel(6, "NoSBPU&&GoodZvtx&&ITSTPC"); + } + registryOther.add("h2f_NTracks_vs_Cent", + ("N_{tracks}(|#eta|<0.8, all TracksIU, no quality cut) vs " + centLabel + ";" + centLabel + " (%);N_{tracks}(|#eta|<0.8)").c_str(), + {HistType::kTH2F, {multAxis, {200, 0.0f, 200.0f, "N_{tracks}"}}}); + registryOther.add("h2f_NumContrib_vs_Cent", + ("N_{contribs} vs " + centLabel + " percentile;" + centLabel + " (%);N_{contribs}").c_str(), + {HistType::kTH2F, {multAxis, {200, 0.0f, 200.0f, "N_{contribs}"}}}); + // ───────────────────────────────────────────────────────────────────── + + // ── PID histograms ─────────────────────────────────────────────────── + // registryPid: vs total-p (kept unchanged — intentional total-momentum monitoring) + registryPid.add("Proton/hTPCdEdxVsP_selected", "", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + registryPid.add("Proton/hTPCdEdxVsP_TPConly", "", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + registryPid.add("Proton/hTPCdEdxVsP_TPCTOF", "", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + registryPid.add("AntiProton/hTPCdEdxVsP_selected", "", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + registryPid.add("AntiProton/hTPCdEdxVsP_TPConly", "", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + registryPid.add("AntiProton/hTPCdEdxVsP_TPCTOF", "", {HistType::kTH2F, {axisP, axisTPCdEdx}}); + // registryPid: vs pTPC (TPC inner param = PID selection variable) — added alongside for QA comparison + registryPid.add("Proton/hTPCdEdxVsPTPC_selected", "", {HistType::kTH2F, {axisPTPC, axisTPCdEdx}}); + registryPid.add("Proton/hTPCdEdxVsPTPC_TPConly", "", {HistType::kTH2F, {axisPTPC, axisTPCdEdx}}); + registryPid.add("Proton/hTPCdEdxVsPTPC_TPCTOF", "", {HistType::kTH2F, {axisPTPC, axisTPCdEdx}}); + registryPid.add("AntiProton/hTPCdEdxVsPTPC_selected", "", {HistType::kTH2F, {axisPTPC, axisTPCdEdx}}); + registryPid.add("AntiProton/hTPCdEdxVsPTPC_TPConly", "", {HistType::kTH2F, {axisPTPC, axisTPCdEdx}}); + registryPid.add("AntiProton/hTPCdEdxVsPTPC_TPCTOF", "", {HistType::kTH2F, {axisPTPC, axisTPCdEdx}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Rho histograms ─────────────────────────────────────────────────── + registryRho.add("hEventCounter", "Events", {HistType::kTH1F, {{1, 0.0, 1.0}}}); + // rapidity-based rho1 + registryRho.add("h2_rho1_Proton_y", "#rho_{1}(p) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); + registryRho.add("h2_rho1_AntiProton_y", "#rho_{1}(#bar{p}) in (y,#varphi) (after V0-daughter veto)", {HistType::kTH2F, {yAxis, phiAxis}}); + registryRho.add("h2_rho1_Lambda_y", "#rho_{1}(#Lambda) in (y,#varphi)", {HistType::kTH2F, {yAxis, phiAxis}}); + registryRho.add("h2_rho1_AntiLambda_y", "#rho_{1}(#bar{#Lambda}) in (y,#varphi)", {HistType::kTH2F, {yAxis, phiAxis}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Autocorrelation / feed-down diagnostic histograms ──────────────── + // Purpose: check whether Lambda daughter protons enter the primary-proton + // sample, causing autocorrelation bias in the Lambda-p rho2. + // Folder: CorrelationQA/PrimaryProton_vs_LambdaDaughter/ + // + // h2f_dEta_dPhi : Δη vs Δφ for every (selected Lambda, selected + // primary proton) pair. The primary-proton list is + // ALREADY cleaned by the V0-daughter veto, so the same + // track cannot appear; this checks for residual + // proximity correlation (e.g. undetected feed-down). + // h2f_dEta_dPhi_nonSameTrack : same, restricted to pairs where the primary + // proton track is NOT the Lambda positive daughter. + AxisSpec axisCorDEta = {64, -1.6f, 1.6f, "#Delta#eta"}; + AxisSpec axisCorDPhi = {72, -o2::constants::math::PI, o2::constants::math::PI, "#Delta#varphi"}; + AxisSpec axisCorDPhiDisplay = {72, -o2::constants::math::PI / 2.0f, 3.0f * o2::constants::math::PI / 2.0f, "#Delta#varphi"}; + registryCorrelationQA.add( + "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter Proton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); + registryCorrelationQA.add( + "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_display", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter Proton (display shifted #Delta#varphi);#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); + registryCorrelationQA.add( + "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_nonSameTrack", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter (non-same-track pairs only);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); + registryCorrelationQA.add( + "PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_nonSameTrack_display", + "Primary Proton (after V0-daughter veto) vs #Lambda Daughter (non-same-track pairs only, display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Combo 2: Lambda daughter proton ↔ primary antiproton ───────────── + // Purpose: check whether the Lambda pos-daughter proton (charge +1) could + // be confused with a primary antiproton (charge −1). + // Same-track matching is impossible here (opposite charges). + registryCorrelationQA.add( + "PrimaryAntiProton_vs_LambdaDaughter/h2f_dEta_dPhi", + "Primary AntiProton (after V0-daughter veto) vs #Lambda Daughter Proton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); + registryCorrelationQA.add( + "PrimaryAntiProton_vs_LambdaDaughter/h2f_dEta_dPhi_display", + "Primary AntiProton (after V0-daughter veto) vs #Lambda Daughter Proton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Combo 3: AntiLambda daughter antiproton ↔ primary proton ───────── + // Purpose: check whether the AntiLambda neg-daughter antiproton (charge −1) + // could leak into the primary proton sample (charge +1). + // Same-track matching is impossible here (opposite charges). + registryCorrelationQA.add( + "PrimaryProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi", + "Primary Proton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); + registryCorrelationQA.add( + "PrimaryProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_display", + "Primary Proton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Combo 4: AntiLambda daughter antiproton ↔ primary antiproton ───── + // Purpose: check whether the AntiLambda neg-daughter antiproton (charge −1) + // enters the primary antiproton sample, causing autocorrelation + // bias in the AntiLambda–pbar rho2. Analogous to Combo 1. + // h2f_dEta_dPhi_nonSameTrack : pairs where the primary antiproton is NOT + // the AntiLambda neg-daughter (i.e. genuine different-track pairs). + registryCorrelationQA.add( + "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton;#Delta#eta = #eta_{prim} - #eta_{dau};#Delta#varphi = #varphi_{prim} - #varphi_{dau}", + {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); + registryCorrelationQA.add( + "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_display", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter AntiProton (display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); + registryCorrelationQA.add( + "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_nonSameTrack", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter (non-same-track pairs only);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisCorDEta, axisCorDPhi}}); + registryCorrelationQA.add( + "PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_nonSameTrack_display", + "Primary AntiProton (after V0-daughter veto) vs #bar{#Lambda} Daughter (non-same-track pairs only, display shifted #Delta#varphi);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisCorDEta, axisCorDPhiDisplay}}); + // ───────────────────────────────────────────────────────────────────── + + // ── QA3: RawPairDensity — Δη vs Δφ for same-sign pairs ─────────────── + // Axes: Δη (400 bins, ±2.0) × Δφ (640 bins, [−π/2, 3π/2]) + AxisSpec axisQA3DEta = {400, -2.0f, 2.0f, "#Delta#eta"}; + AxisSpec axisQA3DPhi = {640, -o2::constants::math::PI / 2.0f, 3.0f * o2::constants::math::PI / 2.0f, "#Delta#varphi"}; + registryCorrelationQA.add( + "QA3/RawPairDensity/h2f_dEta_dPhi_PP", + "Raw pair density p-p;#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisQA3DEta, axisQA3DPhi}}); + registryCorrelationQA.add( + "QA3/RawPairDensity/h2f_dEta_dPhi_LP", + "Raw pair density #Lambda-p (all charge combos);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisQA3DEta, axisQA3DPhi}}); + registryCorrelationQA.add( + "QA3/RawPairDensity/h2f_dEta_dPhi_LL", + "Raw pair density #Lambda-#Lambda (all charge combos);#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {axisQA3DEta, axisQA3DPhi}}); + // ───────────────────────────────────────────────────────────────────── + + // ── QA3: PairCleaning — proton-proton only ──────────────────────────── + registryCorrelationQA.add( + "QA3/PairCleaning/h1f_pairCleaning", + "p-p pair cleaning;Step;Counts", + {HistType::kTH1F, {{4, 0.5f, 4.5f}}}); + // Bin labels are set after histogram registration + { + auto h = registryCorrelationQA.get(HIST("QA3/PairCleaning/h1f_pairCleaning")); + h->GetXaxis()->SetBinLabel(1, "All Candidate Pairs"); + h->GetXaxis()->SetBinLabel(2, "Rejected Same Track"); + h->GetXaxis()->SetBinLabel(3, "Rejected q_inv"); + h->GetXaxis()->SetBinLabel(4, "Accepted"); + } + // ───────────────────────────────────────────────────────────────────── + + // ── QA3: SplitTrackQA — proton-proton close pairs ───────────────────── + registryCorrelationQA.add( + "QA3/SplitTrackQA/h2f_dEta_dPhi_ClosePairs", + "Split-track QA: close p-p pairs;#Delta#eta;#Delta#varphi", + {HistType::kTH2F, {{80, -0.02f, 0.02f, "#Delta#eta"}, {80, -0.05f, 0.05f, "#Delta#varphi"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_openingAngle_ClosePairs", + "Split-track QA: opening angle of close p-p pairs;#theta (rad);Counts", + {HistType::kTH1F, {{100, 0.0f, 0.2f}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_deltaPt_ClosePairs", + "Split-track QA: |#Deltap_{T}| of close p-p pairs;|#Deltap_{T}| (GeV/#it{c});Counts", + {HistType::kTH1F, {{100, 0.0f, 1.0f}}}); + // ───────────────────────────────────────────────────────────────────── + + // ── QA3: SplitTrackQA — q_inv and shared-cluster fraction ───────────── + float qinvMax = cQinvQAMax.value; + int qinvBins = cQinvQABins.value; + if (qinvMax / qinvBins < 0.001f) { + qinvBins = static_cast(qinvMax / 0.001f); + } + + // "Before_qinvCut": filled for every accepted pair, prior to the q_inv + // cut (kQinvCut), so they show the complete original q_inv distributions. + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut", + "Split-track QA: q_{inv} for p-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + // "After_qinvCut": filled only for pairs surviving the q_inv cut + // (q_inv > kQinvCut), applied uniformly to all pair types (PP, pAp, App, ApAp, LP-family, LL-family). + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut", + "Split-track QA: q_{inv} for p-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-p/#bar{p} pairs (all four LP combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut", + "Split-track QA: q_{inv} for #Lambda/#bar{#Lambda}-#Lambda/#bar{#Lambda} pairs (all four LL combos), after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + // q_inv Before/After QA for p-pbar, pbar-p and pbar-pbar pairs + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut", + "Split-track QA: q_{inv} for p-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut", + "Split-track QA: q_{inv} for p-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-p pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-p pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, before q_{inv} cut;q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut", + "Split-track QA: q_{inv} for #bar{p}-#bar{p} pairs, after q_{inv} cut (q_{inv} > 0.01 GeV/c);q_{inv} (GeV/c);Counts", + {HistType::kTH1F, {{qinvBins, 0.0f, qinvMax, "q_{inv} (GeV/c)"}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp_BeforeQinvCut", "k* of #Lambda-p pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_Lp", "k* of #Lambda-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp_BeforeQinvCut", "k* of #Lambda-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_LAp", "k* of #Lambda-#bar{p} pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp_BeforeQinvCut", "k* of #bar{#Lambda}-p pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALp", "k* of #bar{#Lambda}-p pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp_BeforeQinvCut", "k* of #bar{#Lambda}-#bar{p} pairs before q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + registryCorrelationQA.add("QA3/Kstar/h1f_kstar_ALAp", "k* of #bar{#Lambda}-#bar{p} pairs after q_{inv} cut;k* (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0f, 2.0f}}}); + + // ── QA3: 3D q_inv vs ΔΦ vs Δη and q_inv vs ΔΦ vs ΔY ───────────────── + // Filled BEFORE the q_inv cut so the full distribution is visible. + // Nomenclature: "Lp" = Lambda(V0) vs primary proton + // "Lp_dau" = Lambda daughter proton vs primary proton + // "LAp"/"LAp_dau" = Lambda (V0/dau) vs primary antiproton + // "ALp"/"ALp_dau" = AntiLambda (V0/dau) vs primary proton + // "ALAp"/"ALAp_dau" = AntiLambda (V0/dau) vs primary antiproton + { + AxisSpec axisQinv3D = {100, 0.0f, 2.0f, "q_{inv} (GeV/c)"}; + AxisSpec axisDPhi3D = {64, -o2::constants::math::PI / 2.0f, 3.0f * o2::constants::math::PI / 2.0f, "#Delta#varphi"}; + AxisSpec axisDEta3D = {40, -2.0f, 2.0f, "#Delta#eta"}; + AxisSpec axisDY3D = {40, -2.0f, 2.0f, "#Deltay"}; + // ── eta space ───────────────────────────────────────────────────── + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda dau p - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #Lambda dau p - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} dau #bar{p} - p;q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau", + "q_{inv} vs #Delta#varphi vs #Delta#eta: #bar{#Lambda} dau #bar{p} - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Delta#eta", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDEta3D}}); + // ── y space ─────────────────────────────────────────────────────── + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda dau p - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #Lambda dau p - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} (V0) - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} dau #bar{p} - p;q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} (V0) - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + registryCorrelationQA.add("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau", + "q_{inv} vs #Delta#varphi vs #Deltay: #bar{#Lambda} dau #bar{p} - #bar{p};q_{inv} (GeV/c);#Delta#varphi;#Deltay", + {HistType::kTH3F, {axisQinv3D, axisDPhi3D, axisDY3D}}); + } + // ───────────────────────────────────────────────────────────────────── + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_sharedClsFraction_t1", + "Split-track QA: TPC shared-cluster fraction, track 1 (p-p: first proton; #Lambda-p: V0 daughter) in close pairs;Fraction;Counts", + {HistType::kTH1F, {{50, 0.0f, 1.0f}}}); + registryCorrelationQA.add( + "QA3/SplitTrackQA/h1f_sharedClsFraction_t2", + "Split-track QA: TPC shared-cluster fraction, track 2 (p-p: second proton; #Lambda-p: primary (anti)proton) in close pairs;Fraction;Counts", + {HistType::kTH1F, {{50, 0.0f, 1.0f}}}); + registryCorrelationQA.add( + "h2_qinv_dPhi_Lp", + "Lambda-Proton pairs;#Delta#varphi;q_{inv} (GeV/c)", + {HistType::kTH2F, {{72, -static_cast(TMath::Pi()), static_cast(TMath::Pi())}, {100, 0.0f, 0.10f}}}); + // ───────────────────────────────────────────────────────────────────── + + // ── ProtonCounts_byPID_Check histograms (protons only, sign-guarded in process) ── + state.h_TPC = + registryProtonPidCheck.add("h_TPC", + "Proton candidates: TPC PID only (no TOF condition);#it{p}_{T} (GeV/#it{c});Counts", + {HistType::kTH1F, {pidCheckPtAxis}}); + state.h_TPCandTOF = + registryProtonPidCheck.add("h_TPCandTOF", + "Proton candidates: TPC AND TOF both required;#it{p}_{T} (GeV/#it{c});Counts", + {HistType::kTH1F, {pidCheckPtAxis}}); + state.h_TPCorTOF = + registryProtonPidCheck.add("h_TPCorTOF", + "Proton candidates: TPC required; if TOF present at any p, TOF n#sigma also required (differs from final selection, which requires TOF only above p_{TPC} switch);#it{p}_{T} (GeV/#it{c});Counts", + {HistType::kTH1F, {pidCheckPtAxis}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Merged / AllPt histograms ─────────────────────────────────── + // ── Rapidity merged rho2 histograms ───────────────────────────────────── + // NOTE: these must use unrolledAxisY (mRhoYBins*mRhoPhiBins bins), + // GetUnrolledIndexY() below produces indices in [0, mRhoUnrolledBinsY), + // so the booked axis must match that range or downstream R2/BF code that + // expects y-space-sized histograms will see a dimension mismatch. + state.hRho2_Lp_y_AllPt = + registryRho.add("h2_rho2_Lp_y_AllPt", "#rho_{2}(#Lambda, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LAp_y_AllPt = + registryRho.add("h2_rho2_LAp_y_AllPt", "#rho_{2}(#Lambda, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALp_y_AllPt = + registryRho.add("h2_rho2_ALp_y_AllPt", "#rho_{2}(#bar{#Lambda}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALAp_y_AllPt = + registryRho.add("h2_rho2_ALAp_y_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_pp_y_AllPt = + registryRho.add("h2_rho2_pp_y_AllPt", "#rho_{2}(p, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_pAp_y_AllPt = + registryRho.add("h2_rho2_pAp_y_AllPt", "#rho_{2}(p, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of p (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_App_y_AllPt = + registryRho.add("h2_rho2_App_y_AllPt", "#rho_{2}(#bar{p}, p) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of p (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ApAp_y_AllPt = + registryRho.add("h2_rho2_ApAp_y_AllPt", "#rho_{2}(#bar{p}, #bar{p}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{p} (trigger);index(y,#varphi) of #bar{p} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LL_y_AllPt = + registryRho.add("h2_rho2_LL_y_AllPt", "#rho_{2}(#Lambda, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_LAL_y_AllPt = + registryRho.add("h2_rho2_LAL_y_AllPt", "#rho_{2}(#Lambda, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #Lambda (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALL_y_AllPt = + registryRho.add("h2_rho2_ALL_y_AllPt", "#rho_{2}(#bar{#Lambda}, #Lambda) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #Lambda (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + state.hRho2_ALAL_y_AllPt = + registryRho.add("h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda}, #bar{#Lambda}) in (y,#varphi) space, q_{inv} > 0.01 GeV/#it{c};index(y,#varphi) of #bar{#Lambda} (trigger);index(y,#varphi) of #bar{#Lambda} (associate)", {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + // ───────────────────────────────────────────────────────────────────── + + state.hPtSelectedPrimProton_AllPt = + registryOther.add("hPtSelectedPrimProton_AllPt", + "Selected primary proton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", + {HistType::kTH1F, {ptAxis}}); + state.hPtSelectedPrimAntiProton_AllPt = + registryOther.add("hPtSelectedPrimAntiProton_AllPt", + "Selected primary antiproton p_{T} spectrum (integrated, 0.5-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", + {HistType::kTH1F, {ptAxis}}); + state.hPtSelLambda_AllPt = + registryOther.add("hPtSelLambda_AllPt", + "Selected #Lambda p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", + {HistType::kTH1F, {ptAxis}}); + state.hPtSelAntiLambda_AllPt = + registryOther.add("hPtSelAntiLambda_AllPt", + "Selected #bar{#Lambda} p_{T} spectrum (integrated, 0.6-3.6 GeV/#it{c});#it{p}_{T} (GeV/#it{c});Counts", + {HistType::kTH1F, {ptAxis}}); + + state.hMassLambdaMerged = + registryLambda.add("hMassLambdaNoMassCut_AllPt", + "#Lambda mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", + {HistType::kTH1F, {lambdaMassAxis}}); + state.hMassAntiLambdaMerged = + registryLambda.add("hMassAntiLambdaNoMassCut_AllPt", + "#bar{#Lambda} mass (all selections except #Lambda mass window), integrated #it{p}_{T} 0.6-3.6 GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", + {HistType::kTH1F, {lambdaMassAxis}}); + // ───────────────────────────────────────────────────────────────────── + + // ── Per-pT-bin histograms (Lambda pT bins 0.6 → 3.6) ───────────────── + state.hMassLambdaPtBins.clear(); + state.hMassAntiLambdaPtBins.clear(); + state.hPtPrimProton_Lambda.clear(); + state.hPtPrimAntiProton_Lambda.clear(); + state.hPtPrimProton_AntiLambda.clear(); + state.hPtPrimAntiProton_AntiLambda.clear(); + + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + const float ptLo = state.ptEdges[i]; + const float ptHi = state.ptEdges[i + 1]; + + state.hPtPrimProton_Lambda.push_back( + registryOther.add( + Form("hPtPrimProton_Lambda_ptBin%02zu", i), + Form("Proton #it{p}_{T} in #Lambda-p pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), + {HistType::kTH1F, {ptAxis}})); + state.hPtPrimAntiProton_Lambda.push_back( + registryOther.add( + Form("hPtPrimAntiProton_Lambda_ptBin%02zu", i), + Form("Antiproton #it{p}_{T} in #Lambda-#bar{p} pairs, #Lambda #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), + {HistType::kTH1F, {ptAxis}})); + state.hPtPrimProton_AntiLambda.push_back( + registryOther.add( + Form("hPtPrimProton_AntiLambda_ptBin%02zu", i), + Form("Proton #it{p}_{T} in #bar{#Lambda}-p pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{p} (GeV/#it{c});Counts", ptLo, ptHi), + {HistType::kTH1F, {ptAxis}})); + state.hPtPrimAntiProton_AntiLambda.push_back( + registryOther.add( + Form("hPtPrimAntiProton_AntiLambda_ptBin%02zu", i), + Form("Antiproton #it{p}_{T} in #bar{#Lambda}-#bar{p} pairs, #bar{#Lambda} #it{p}_{T} in [%.1f, %.1f) GeV/#it{c};#it{p}_{T}^{#bar{p}} (GeV/#it{c});Counts", ptLo, ptHi), + {HistType::kTH1F, {ptAxis}})); + + state.hMassLambdaPtBins.push_back( + registryLambda.add( + Form("hMassLambdaNoMassCut_ptBin%02zu", i), + Form("#Lambda mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), + {HistType::kTH1F, {lambdaMassAxis}})); + state.hMassAntiLambdaPtBins.push_back( + registryLambda.add( + Form("hMassAntiLambdaNoMassCut_ptBin%02zu", i), + Form("#bar{#Lambda} mass (all selections except #Lambda mass window), %.1f #leq #it{p}_{T} < %.1f GeV/#it{c};#it{M}_{inv} [GeV/#it{c}^{2}];Counts", ptLo, ptHi), + {HistType::kTH1F, {lambdaMassAxis}})); + } + // ───────────────────────────────────────────────────────────────────── + + // ══════════════════════════════════════════════════════════════════════ + // Centrality-differential histograms → top-level folder "rho1ANDrho2_LP_CentralityBased" + // ══════════════════════════════════════════════════════════════════════ + state.centSets.clear(); + // Single source of truth: centrality class edges come from the cCentBins ConfigurableAxis. + // Edges are read back from a temporary ROOT histogram built from the AxisSpec. + { + const AxisSpec centCfgAxis(cCentBins, "centrality"); + HistogramRegistry tmpReg{"tmpCentEdges", {}, OutputObjHandlingPolicy::AnalysisObject}; + auto hTmp = tmpReg.add("hTmp", "", {HistType::kTH1F, {centCfgAxis}}); + state.centEdgesLocal.clear(); + const TAxis* ax = hTmp->GetXaxis(); + for (int i = 1; i <= ax->GetNbins(); ++i) { + state.centEdgesLocal.push_back(static_cast(ax->GetBinLowEdge(i))); + } + state.centEdgesLocal.push_back(static_cast(ax->GetBinUpEdge(ax->GetNbins()))); + } + if (state.centEdgesLocal.size() < 2) { + LOGF(fatal, "cCentBins needs at least 2 edges"); + } + for (const float e : state.centEdgesLocal) { + if (e != std::floor(e)) { + LOGF(fatal, "cCentBins edges must be integers (folder names use integer edges)"); + } + } + if (cFillCentHists.value) { + const size_t nCent = state.centEdgesLocal.size() - 1; + state.centSets.resize(nCent); + + for (size_t ic = 0; ic < nCent; ++ic) { + const float cLo = state.centEdgesLocal[ic]; + const float cHi = state.centEdgesLocal[ic + 1]; + const std::string dir = Form("Cent%02d_%02d", static_cast(cLo), static_cast(cHi)); + const std::string ctag = Form("%s %.0f-%.0f%%", cUseFT0C.value ? "FT0C" : "FT0M", cLo, cHi); + auto& S = state.centSets[ic]; + + S.hEvents = registryCent.add((dir + "/hEventCounter").c_str(), + ("Events, " + ctag).c_str(), + {HistType::kTH1F, {{1, 0.0, 1.0}}}); + + // ── rho1, y space (always) ── + S.rho1_Proton_y = registryCent.add((dir + "/h2_rho1_Proton_y").c_str(), ("#rho_{1}(p) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + S.rho1_AntiProton_y = registryCent.add((dir + "/h2_rho1_AntiProton_y").c_str(), ("#rho_{1}(#bar{p}) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + S.rho1_Lambda_y = registryCent.add((dir + "/h2_rho1_Lambda_y").c_str(), ("#rho_{1}(#Lambda) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + S.rho1_AntiLambda_y = registryCent.add((dir + "/h2_rho1_AntiLambda_y").c_str(), ("#rho_{1}(#bar{#Lambda}) in (y,#varphi), " + ctag).c_str(), {HistType::kTH2F, {yAxis, phiAxis}}); + + // ── rho2, y space (always) ── + auto addY = [&](std::shared_ptr& h, const char* name, const char* title) { + h = registryCent.add((dir + "/" + name).c_str(), (std::string(title) + ", " + ctag).c_str(), {HistType::kTH2F, {unrolledAxisY, unrolledAxisY}}); + }; + addY(S.Lp_y, "h2_rho2_Lp_y_AllPt", "#rho_{2}(#Lambda,p) (y,#varphi)"); + addY(S.LAp_y, "h2_rho2_LAp_y_AllPt", "#rho_{2}(#Lambda,#bar{p}) (y,#varphi)"); + addY(S.ALp_y, "h2_rho2_ALp_y_AllPt", "#rho_{2}(#bar{#Lambda},p) (y,#varphi)"); + addY(S.ALAp_y, "h2_rho2_ALAp_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{p}) (y,#varphi)"); + addY(S.pp_y, "h2_rho2_pp_y_AllPt", "#rho_{2}(p,p) (y,#varphi)"); + addY(S.pAp_y, "h2_rho2_pAp_y_AllPt", "#rho_{2}(p,#bar{p}) (y,#varphi)"); + addY(S.App_y, "h2_rho2_App_y_AllPt", "#rho_{2}(#bar{p},p) (y,#varphi)"); + addY(S.ApAp_y, "h2_rho2_ApAp_y_AllPt", "#rho_{2}(#bar{p},#bar{p}) (y,#varphi)"); + addY(S.LL_y, "h2_rho2_LL_y_AllPt", "#rho_{2}(#Lambda,#Lambda) (y,#varphi)"); + addY(S.LAL_y, "h2_rho2_LAL_y_AllPt", "#rho_{2}(#Lambda,#bar{#Lambda}) (y,#varphi)"); + addY(S.ALL_y, "h2_rho2_ALL_y_AllPt", "#rho_{2}(#bar{#Lambda},#Lambda) (y,#varphi)"); + addY(S.ALAL_y, "h2_rho2_ALAL_y_AllPt", "#rho_{2}(#bar{#Lambda},#bar{#Lambda}) (y,#varphi)"); + } + } + + // ── Efficiency correction: load 1/eff maps once ────────────────────── + if (cApplyEffCorr.value) { + if (!cEffCcdbPath.value.empty()) { + // ── load from CCDB (TList of TH3) ── + ccdb->setURL(cCcdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + TList* effList = ccdb->getForTimeStamp(cEffCcdbPath.value, cEffCcdbTimestamp.value); + if (!effList) { + LOGF(fatal, "Cannot get efficiency TList from CCDB path %s (timestamp %" PRId64 ")", cEffCcdbPath.value.c_str(), static_cast(cEffCcdbTimestamp.value)); + } + auto loadMap = [&](const char* name) -> TH3* { + auto* obj = effList->FindObject(name); + if (!obj) { + LOGF(fatal, "Missing efficiency map %s in CCDB object", name); + } + TH3* h = dynamic_cast(obj->Clone()); + if (!h) { + LOGF(fatal, "Object %s in CCDB could not be cast to TH3*", name); + } + h->SetDirectory(nullptr); + return h; + }; + state.invEffL = loadMap("hInvEff_Lambda"); + state.invEffAL = loadMap("hInvEff_AntiLambda"); + state.invEffP = loadMap("hInvEff_Proton"); + state.invEffAP = loadMap("hInvEff_AntiProton"); + } else { + // ── load from a ROOT file (fallback / local tests) ── + TFile* fEff = TFile::Open(cEffFile.value.c_str(), "READ"); + if (!fEff || fEff->IsZombie()) { + LOGF(fatal, "Cannot open efficiency file %s", cEffFile.value.c_str()); + } + auto loadMap = [&](const char* name) -> TH3* { + TH3* h = dynamic_cast(fEff->Get(name)); + if (!h) { + LOGF(fatal, "Missing efficiency map %s", name); + } + h->SetDirectory(nullptr); + return h; + }; + state.invEffL = loadMap("hInvEff_Lambda"); + state.invEffAL = loadMap("hInvEff_AntiLambda"); + state.invEffP = loadMap("hInvEff_Proton"); + state.invEffAP = loadMap("hInvEff_AntiProton"); + fEff->Close(); + } + + // Sumw2 for all weighted fills (rho1, and every rho2 channel: Lp family, pp family, LL family) + registryRho.get(HIST("h2_rho1_Proton_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_AntiProton_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_Lambda_y"))->Sumw2(); + registryRho.get(HIST("h2_rho1_AntiLambda_y"))->Sumw2(); + state.hRho2_Lp_y_AllPt->Sumw2(); + state.hRho2_LAp_y_AllPt->Sumw2(); + state.hRho2_ALp_y_AllPt->Sumw2(); + state.hRho2_ALAp_y_AllPt->Sumw2(); + state.hRho2_pp_y_AllPt->Sumw2(); + state.hRho2_pAp_y_AllPt->Sumw2(); + state.hRho2_App_y_AllPt->Sumw2(); + state.hRho2_ApAp_y_AllPt->Sumw2(); + state.hRho2_LL_y_AllPt->Sumw2(); + state.hRho2_LAL_y_AllPt->Sumw2(); + state.hRho2_ALL_y_AllPt->Sumw2(); + state.hRho2_ALAL_y_AllPt->Sumw2(); + for (auto& S : state.centSets) { + S.rho1_Proton_y->Sumw2(); + S.rho1_AntiProton_y->Sumw2(); + S.rho1_Lambda_y->Sumw2(); + S.rho1_AntiLambda_y->Sumw2(); + S.Lp_y->Sumw2(); + S.LAp_y->Sumw2(); + S.ALp_y->Sumw2(); + S.ALAp_y->Sumw2(); + S.pp_y->Sumw2(); + S.pAp_y->Sumw2(); + S.App_y->Sumw2(); + S.ApAp_y->Sumw2(); + S.LL_y->Sumw2(); + S.LAL_y->Sumw2(); + S.ALL_y->Sumw2(); + S.ALAL_y->Sumw2(); + } + } + } + + Filter eventFilter = (o2::aod::evsel::sel8 == true); + Filter posZFilter = (nabs(o2::aod::collision::posZ) < cPPandV0ZVertexCut); + + // ── V0 topology cuts ────────────────────────────────────────────────── + template + bool passTopoCuts(TV0 const& v0) const + { + if (v0.dcaV0daughters() > cV0MaxDcaDaughters.value) { + return false; + } + if (v0.v0cosPA() < lambdaV0MinCosPA.value) { + return false; + } + if (v0.v0radius() < lambdaV0MinDecayRadius.value) { + return false; + } + if (std::abs(v0.dcav0topv()) > cV0MaxDcaToPV.value) { + return false; + } + return true; + } + // ───────────────────────────────────────────────────────────────────── + + void processData(soa::Filtered>::iterator const& collision, + aod::V0Datas const& V0s, + MyTracks const& tracks) + { + // ── Ordered-pair convention ──────────────────────────────────────────── + // pp : trigger=proton1, assoc=proton2 (self-pairs rejected) + // pAp : trigger=proton, assoc=antiproton + // App : trigger=antiproton,assoc=proton + // ApAp : trigger=antiproton1,assoc=antiproton2 (self-pairs rejected) + // Lp : trigger=Lambda, assoc=proton (daughter-track overlap rejected) + // LAp : trigger=Lambda, assoc=antiproton + // ALp : trigger=AntiLambda,assoc=proton + // ALAp : trigger=AntiLambda,assoc=antiproton + // LL/LAL/ALL/ALAL: both V0s from respective selected lists (self-pairs rejected) + // + // Every channel fills y-space only (unrolledIndexY). + // ───────────────────────────────────────────────────────────────────── + + // ── Event cutflow bin 1 — enters processData (after sel8 + posZ filters) ── + registryOther.fill(HIST("hEventCutflow"), 1.0f); + + if (collision.numContrib() < 1) { + return; + } + // ── Event cutflow bin 2 — after numContrib >= 1 ────────────────────── + registryOther.fill(HIST("hEventCutflow"), 2.0f); + + const float cent = getCentrality(collision); + if (cent < cMinCentrality.value || cent >= cMaxCentrality.value) { + return; + } + // ── Event cutflow bin 3 — after centrality in [cMinCentrality, cMaxCentrality) ── + registryOther.fill(HIST("hEventCutflow"), 3.0f); + + // INEL > 0: require at least one charged track in |eta| < 0.8 + static constexpr float kMaxInelEta = 0.8f; + int nTracksINEL = 0; + for (auto const& trk : tracks) { + if (std::abs(trk.eta()) < kMaxInelEta) { + ++nTracksINEL; + } + } + if (nTracksINEL < 1) { + return; + } + // ── Event cutflow bin 4 — after INEL > 0 ───────────────────────────── + registryOther.fill(HIST("hEventCutflow"), 4.0f); + + registryRho.fill(HIST("hEventCounter"), 0.5); + + // ── Centrality bin for the centrality-differential histograms ───────── + CentRho2Set* cs = nullptr; + if (cFillCentHists.value) { + const int iCent = centBinIndex(cent); + if (iCent >= 0 && iCent < static_cast(state.centSets.size())) { + cs = &state.centSets[iCent]; + cs->hEvents->Fill(0.5); + } + } + + registryOther.fill(HIST("hVertexZRec"), collision.posZ()); + registryOther.fill(HIST("hFT0MPercentile"), cent); + + // ── Item 5: Event-level pileup rejection metrics ────────────────────── + // Fill hPileupFlags: one bin per evsel bit, filled if the bit is set. + if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + registryOther.fill(HIST("hPileupFlags"), 1.0f); + } + if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + registryOther.fill(HIST("hPileupFlags"), 2.0f); + } + if (collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + registryOther.fill(HIST("hPileupFlags"), 3.0f); + } + if (collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + registryOther.fill(HIST("hPileupFlags"), 4.0f); + } + if (collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + registryOther.fill(HIST("hPileupFlags"), 5.0f); + } + if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup) && + collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && + collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + registryOther.fill(HIST("hPileupFlags"), 6.0f); + } + // N_{tracks} and N_{contribs} vs centrality — filled once per event + registryOther.fill(HIST("h2f_NTracks_vs_Cent"), cent, static_cast(nTracksINEL)); + registryOther.fill(HIST("h2f_NumContrib_vs_Cent"), cent, static_cast(collision.numContrib())); + // ───────────────────────────────────────────────────────────────────── + + // ====== Step 1: Initialise containers ====================================== + std::unordered_set v0DaughterTrackIds; + v0DaughterTrackIds.reserve(64); + std::vector selectedPrimProtons; + std::vector selectedPrimAntiProtons; + std::vector selectedLambdas; + std::vector selectedAntiLambdas; + + int nSelLambda = 0; + int nSelAntiLambda = 0; + int nPairs_Lambda_PrimProton = 0; + int nPairs_Lambda_PrimAntiProton = 0; + int nPairs_AntiLambda_PrimProton = 0; + int nPairs_AntiLambda_PrimAntiProton = 0; + + // ── Veto QA event counters (filled once per event) ─────────────────── + int nProtonBeforeVeto = 0; + int nProtonRemovedVeto = 0; + int nAntiPBeforeVeto = 0; + int nAntiPRemovedVeto = 0; + // ───────────────────────────────────────────────────────────────────── + + // ====== Step 2: V0 validation loop (BEFORE track loop) ================= + // Validates each V0, builds the veto set from validated daughters only, + // fills single-particle histograms, and populates selectedLambdas/AntiLambdas. + // Pairing with protons happens in Step 3 (after the track loop). + constexpr float mLambdaPDG = o2::constants::physics::MassLambda; + constexpr float mK0PDG = o2::constants::physics::MassK0Short; + + for (const auto& v0 : V0s) { + const auto& v0PosDauCached = v0.posTrack_as(); + const auto& v0NegDauCached = v0.negTrack_as(); + + // V0 cutflow bin 1: all V0s (both hypotheses) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 1.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 1.0f); + + // ── TRULY RAW QA Fills — before any V0 selection cut ───────────── + // Placed here at the very top of the loop: before v0Type, daughter + // kinematics, TPC PID, rapidity, pT, topology, DCA, CTau, K0S, or + // mass-window cuts. Every V0 in the event is filled. + // We double-fill under both the Lambda hypothesis (pos=proton, neg=pion) + // and the AntiLambda hypothesis (pos=pion, neg=proton) since no PID + // decision has been made yet. This gives the true combinatorial view. + { + const auto& rawPosDau = v0PosDauCached; + const auto& rawNegDau = v0NegDauCached; + const float rawCtau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float rawBgctau = rawCtau * v0.p() / mLambdaPDG; + // Lambda hypothesis (positive daughter assumed proton) + fillLambdaQA(LambdaQAStage::RawCandidate, v0, rawCtau, rawBgctau); + fillDaughterQA(DaughterQAStage::Raw, rawPosDau); + fillDaughterQA(DaughterQAStage::Raw, rawNegDau); + // AntiLambda hypothesis (negative daughter assumed antiproton) + fillLambdaQA(LambdaQAStage::RawCandidate, v0, rawCtau, rawBgctau); + fillDaughterQA(DaughterQAStage::Raw, rawNegDau); + fillDaughterQA(DaughterQAStage::Raw, rawPosDau); + // ── Item 2: K0S competing-mass hypothesis QA — TRULY RAW (before any cut) ── + registryQaDetector.fill(HIST("Lambda/InvariantMass/h1f_mK0Short_beforeCut"), v0.mK0Short()); + registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mLambda_vs_mK0Short"), v0.mK0Short(), v0.mLambda()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h1f_mK0Short_beforeCut"), v0.mK0Short()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mLambda_vs_mK0Short"), v0.mK0Short(), v0.mAntiLambda()); + // ───────────────────────────────────────────────────────────────── + } + // ───────────────────────────────────────────────────────────────── + + // ── V0 type selection (strange9 requires v0Type == 1) ───────────── + if (v0.v0Type() != cV0TypeSelection.value) { + continue; + } + // V0 cutflow bin 2: V0 type (both hypotheses) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 2.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 2.0f); + // ───────────────────────────────────────────────────────────────── + + const auto& v0PosDau = v0PosDauCached; + const auto& v0NegDau = v0NegDauCached; + + // ── Daughter kinematic quality cuts (from strange9) ─────────────── + if (v0PosDau.pt() <= cV0DauMinPt.value || v0NegDau.pt() <= cV0DauMinPt.value || + std::abs(v0PosDau.eta()) >= cV0DauMaxEta.value || std::abs(v0NegDau.eta()) >= cV0DauMaxEta.value || + v0PosDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value || + v0NegDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value) { + continue; + } + // V0 cutflow bin 3: daughter kinematics (both hypotheses) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 3.0f); + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 3.0f); + // ───────────────────────────────────────────────────────────────── + + // ── PID hypothesis ──────────────────────────────────────────────── + const bool v0DauPID_AsLambda = + (std::abs(v0PosDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value) && + (std::abs(v0NegDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value); + const bool v0DauPID_AsAntiLambda = + (std::abs(v0PosDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value) && + (std::abs(v0NegDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value); + + // Reject ambiguous or unidentified V0s + if (v0DauPID_AsLambda == v0DauPID_AsAntiLambda) { + continue; + } + // V0 cutflow bin 4: unambiguous TPC PID — from here on, fill only the chosen hypothesis + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 4.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 4.0f); + } + // ───────────────────────────────────────────────────────────────── + + // ── TPC shared clusters cut on daughter tracks ──────────────────── + // if (v0DauPID_AsLambda) { + // if (v0PosDau.tpcFractionSharedCls() > 0.4f) { continue; } + // if (v0NegDau.tpcFractionSharedCls() > 0.4f) { continue; } + // } + // if (v0DauPID_AsAntiLambda) { + // if (v0PosDau.tpcFractionSharedCls() > 0.4f) { continue; } + // if (v0NegDau.tpcFractionSharedCls() > 0.4f) { continue; } + // } + // ───────────────────────────────────────────────────────────────── + + // ── Rapidity cuts ──────────────────────────────────────────────── // use lambdaV0MaxY configurable + if (v0DauPID_AsLambda && std::abs(v0.rapidity(1)) > lambdaV0MaxY.value) { + continue; + } + if (v0DauPID_AsAntiLambda && std::abs(v0.rapidity(2)) > lambdaV0MaxY.value) { + continue; + } + // V0 cutflow bin 5: rapidity + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 5.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 5.0f); + } + // ───────────────────────────────────────────────────────────────── + + // ── Eta cuts ────────────────────────────────────────────────────── + // ETACUT-DISABLED (temporary): eta cut commented out, keeping rapidity cut only. + // if (v0DauPID_AsLambda && std::abs(v0.eta()) > lambdaV0MaxEta.value) { continue; } + // ETACUT-DISABLED (temporary): eta cut commented out, keeping rapidity cut only. + // if (v0DauPID_AsAntiLambda && std::abs(v0.eta()) > lambdaV0MaxEta.value) { continue; } + // ───────────────────────────────────────────────────────────────── + + // ── pT acceptance ───────────────────────────────────────────────── + if ((v0DauPID_AsLambda || v0DauPID_AsAntiLambda) && + (v0.pt() < lambdaV0MinPt.value || v0.pt() > lambdaV0MaxPt.value)) { + continue; + } + // V0 cutflow bin 6: pT acceptance + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 6.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 6.0f); + } + // ───────────────────────────────────────────────────────────────── + + // ── DCA of daughters to PV ──────────────────────────────────────── + const float dcaProton = v0DauPID_AsLambda ? std::abs(v0.dcapostopv()) + : std::abs(v0.dcanegtopv()); + const float dcaPion = v0DauPID_AsLambda ? std::abs(v0.dcanegtopv()) + : std::abs(v0.dcapostopv()); + + // ── Lifetime (needed for the CTau cut below; Final QA recomputes ctau/bgctau on the cleaned sample) ── + const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + + // ── (Raw QA fills moved to top of V0 loop — see TRULY RAW block above) ─ + + // ── Apply V0 topology cuts ──────────────────────────────────────────── + if (!passTopoCuts(v0)) { + continue; + } + // ───────────────────────────────────────────────────────────────── + + // ── Apply daughter DCA-to-PV cuts ──────────────────────────────────── + if (dcaProton < lambdaV0DaughterProtonMinDCAToPV.value || + dcaPion < lambdaV0DaughterPionMinDCAToPV.value) { + continue; + } + // V0 cutflow bin 7: topology (passTopoCuts) and daughter DCA-to-PV + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 7.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 7.0f); + } + // ───────────────────────────────────────────────────────────────── + + if (ctau > lambdaV0MaxCTau.value) { + continue; + } + + if (std::abs(v0.mK0Short() - mK0PDG) <= lambdaV0KShortRejectMassWindow.value) { + continue; + } + // V0 cutflow bin 8: cTau and K0s rejection + if (v0DauPID_AsLambda) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 8.0f); + } else { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 8.0f); + } + // ───────────────────────────────────────────────────────────────── + + // ── Mass window selection: |m - mPDG| < lambdaV0MassWindow ───────── + const bool selLambda = v0DauPID_AsLambda && (std::abs(v0.mLambda() - mLambdaPDG) < lambdaV0MassWindow.value); + const bool selAntiLambda = v0DauPID_AsAntiLambda && (std::abs(v0.mAntiLambda() - mLambdaPDG) < lambdaV0MassWindow.value); + // ── Item 2: K0S mass fill after K0S rejection + after Lambda/AntiLambda selection ── + if (selLambda) { + registryQaDetector.fill(HIST("Lambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); + } + if (selAntiLambda) { + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h1f_mK0Short_afterLambdaSel"), v0.mK0Short()); + } + // ───────────────────────────────────────────────────────────────── + + // ── pT bin index for per-bin histograms ────────────────────────── + const float pt = v0.pt(); + // VS Code: commented out unused variable. + // const float eta = v0.eta(); + // VS Code: commented out unused variable. + // const float pz = pt * std::sinh(eta); + int bin = -1; + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { + bin = static_cast(i); + break; + } + } + // ───────────────────────────────────────────────────────────────── + + // ── NoMassCut QA fills (PID + topo only) ───────────────────────── + // Note: fillLambdaQA/fillDaughterQA for 01_Raw were moved earlier in the + // loop (before the daughter DCA-to-PV cut) to give a true pre-cut view. + if (v0DauPID_AsLambda) { + if (bin >= 0) { + state.hMassLambdaPtBins[bin]->Fill(v0.mLambda()); + } + state.hMassLambdaMerged->Fill(v0.mLambda()); + registryQaDetector.fill(HIST("Lambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); + // ── Item 2: mass vs eta/phi acceptance uniformity ── + registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mLambda()); + registryQaDetector.fill(HIST("Lambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mLambda()); + // ────────────────────────────────────────────────── + if (selLambda) { + registryQaDetector.fill(HIST("Lambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mLambda()); + } + } + if (v0DauPID_AsAntiLambda) { + if (bin >= 0) { + state.hMassAntiLambdaPtBins[bin]->Fill(v0.mAntiLambda()); + } + state.hMassAntiLambdaMerged->Fill(v0.mAntiLambda()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/NoMassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); + // ── Item 2: mass vs eta/phi acceptance uniformity ── + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_eta"), v0.eta(), v0.mAntiLambda()); + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/h2f_mass_vs_phi"), v0.phi(), v0.mAntiLambda()); + // ────────────────────────────────────────────────── + if (selAntiLambda) { + registryQaDetector.fill(HIST("AntiLambda/InvariantMass/MassCut/h2f_mass_vs_pt"), pt, v0.mAntiLambda()); + } + } + // ───────────────────────────────────────────────────────────────── + + // // ── Daughter beta window veto (after NoMassCut fills, before Final) ─── //BETACUTCommented + // // Raw and NoMassCut histograms above are filled for ALL topology-passing //BETACUTCommented + // // candidates regardless of daughter beta. The veto here prevents V0s with //BETACUTCommented + // // out-of-window daughter beta from reaching selLambda/selAntiLambda, the //BETACUTCommented + // // Final QA fills, and the selectedLambdas / selectedAntiLambdas containers. //BETACUTCommented + // // Consistent with how primary proton selection rejects incidental TOF hits //BETACUTCommented + // // outside [cPPMinTofBeta, cPPMaxTofBeta], but using particle-dependent //BETACUTCommented + // // beta windows for V0 daughters: [cV0protonDauMinBeta, cV0protonDauMaxBeta] //BETACUTCommented + // // for (anti)proton daughters and >= cV0pionDauMinBeta (no upper bound) for //BETACUTCommented + // // (anti)pion daughters. //BETACUTCommented + // // Lambda: pos = proton hypothesis, neg = pion hypothesis. //BETACUTCommented + // // AntiLambda: neg = antiproton hypothesis, pos = antipion hypothesis. //BETACUTCommented + // if (v0DauPID_AsLambda) { //BETACUTCommented + // // Proton daughter: reject if beta is outside [cV0protonDauMinBeta, cV0protonDauMaxBeta] //BETACUTCommented + // if (v0PosDau.hasTOF() && (v0PosDau.beta() < cV0protonDauMinBeta.value || v0PosDau.beta() > cV0protonDauMaxBeta.value)) { continue; } //BETACUTCommented + // // Pion daughter: only reject unphysically low beta; no upper bound (pions saturate near beta≈1) //BETACUTCommented + // if (v0NegDau.hasTOF() && (v0NegDau.beta() < cV0pionDauMinBeta.value)) { continue; } //BETACUTCommented + // } //BETACUTCommented + // if (v0DauPID_AsAntiLambda) { //BETACUTCommented + // // Antiproton daughter: reject if beta is outside [cV0protonDauMinBeta, cV0protonDauMaxBeta] //BETACUTCommented + // if (v0NegDau.hasTOF() && (v0NegDau.beta() < cV0protonDauMinBeta.value || v0NegDau.beta() > cV0protonDauMaxBeta.value)) { continue; } //BETACUTCommented + // // Antipion daughter: only reject unphysically low beta; no upper bound (pions saturate near beta≈1) //BETACUTCommented + // if (v0PosDau.hasTOF() && (v0PosDau.beta() < cV0pionDauMinBeta.value)) { continue; } //BETACUTCommented + // } //BETACUTCommented + // ───────────────────────────────────────────────────────────────── + + // ── Step 3: Selected Lambda — populate veto set + rho1 + pT histos ─ + if (selLambda) { + // V0 cutflow bin 9: mass window (pre-cleaning) + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 9.0f); + selectedLambdas.push_back(v0); + v0DaughterTrackIds.insert(v0.posTrackId()); + v0DaughterTrackIds.insert(v0.negTrackId()); + + // Final QA, rho1 / pT spectra and nSelLambda are filled after shared-daughter cleaning (below). + } + + if (selAntiLambda) { + // V0 cutflow bin 9: mass window (pre-cleaning) + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 9.0f); + selectedAntiLambdas.push_back(v0); + v0DaughterTrackIds.insert(v0.posTrackId()); + v0DaughterTrackIds.insert(v0.negTrackId()); + + // Final QA, rho1 / pT spectra and nSelAntiLambda are filled after shared-daughter cleaning (below). + } + // ───────────────────────────────────────────────────────────────── + } + // ====== End of Step 2+3 V0 validation loop ============================ + + // ====== Shared-daughter cleaning (symmetric, over the Lambda ∪ AntiLambda union) ====== + // Two candidates sharing a positive OR a negative daughter track are BOTH rejected. + // v0DaughterTrackIds is intentionally left untouched (conservative primary veto). + const int nRawLambda = static_cast(selectedLambdas.size()); + const int nRawAntiLambda = static_cast(selectedAntiLambdas.size()); + + std::unordered_map posIdCount; + std::unordered_map negIdCount; + for (const auto& v : selectedLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + for (const auto& v : selectedAntiLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + + std::vector cleanLambdas; + std::vector cleanAntiLambdas; + for (const auto& v : selectedLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanLambdas.push_back(v); + } + } + for (const auto& v : selectedAntiLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanAntiLambdas.push_back(v); + } + } + selectedLambdas = std::move(cleanLambdas); + selectedAntiLambdas = std::move(cleanAntiLambdas); + + // V0 cutflow bin 10: survivors of shared-daughter cleaning + for ([[maybe_unused]] const auto& v : selectedLambdas) { + registryQaDetector.fill(HIST("Lambda/hCutFlow"), 10.0f); + } + for ([[maybe_unused]] const auto& v : selectedAntiLambdas) { + registryQaDetector.fill(HIST("AntiLambda/hCutFlow"), 10.0f); + } + + nSelLambda = static_cast(selectedLambdas.size()); + nSelAntiLambda = static_cast(selectedAntiLambdas.size()); + + // Cleaning diagnostics + registryOther.fill(HIST("h1f_nLambda_beforeClean"), static_cast(nRawLambda)); + registryOther.fill(HIST("h1f_nLambda_rejectedClean"), static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("h1f_nLambda_afterClean"), static_cast(nSelLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_beforeClean"), static_cast(nRawAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_rejectedClean"), static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("h1f_nAntiLambda_afterClean"), static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 1.0f, static_cast(nRawLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 2.0f, static_cast(nRawLambda - nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 3.0f, static_cast(nSelLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 4.0f, static_cast(nRawAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 5.0f, static_cast(nRawAntiLambda - nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 6.0f, static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 7.0f, static_cast(nSelLambda) * static_cast(nSelLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 8.0f, static_cast(nSelAntiLambda) * static_cast(nSelAntiLambda - 1)); + registryOther.fill(HIST("hSharedDauCleaningSums"), 9.0f, static_cast(nSelLambda) * static_cast(nSelAntiLambda)); + + // rho1 and pT spectra from the cleaned sample (same sample as rho2) + for (const auto& v0 : selectedLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.posTrack_as(); + const auto& dauNeg = v0.negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauPos); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + } + const float wL = getEffWeight(state.invEffL, cent, v0.pt(), v0.rapidity(1)); + registryRho.fill(HIST("h2_rho1_Lambda_y"), v0.rapidity(1), v0.phi(), wL); + if (cs) { + cs->rho1_Lambda_y->Fill(v0.rapidity(1), v0.phi(), wL); + } + registryOther.fill(HIST("hEffWeightLambda"), wL); + if (wL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 3.f); + } + registryOther.fill(HIST("hPtSelLambda"), v0.pt()); + state.hPtSelLambda_AllPt->Fill(v0.pt()); + } + for (const auto& v0 : selectedAntiLambdas) { + // Final QA on the cleaned sample (same sample as rho1 / rho2) + { + const float ctauFinal = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + const float bgctauFinal = ctauFinal * v0.p() / mLambdaPDG; + const auto& dauPos = v0.posTrack_as(); + const auto& dauNeg = v0.negTrack_as(); + fillLambdaQA(LambdaQAStage::Final, v0, ctauFinal, bgctauFinal); + fillDaughterQA(DaughterQAStage::Final, dauNeg); + fillDaughterQA(DaughterQAStage::Final, dauPos); + } + const float wAL = getEffWeight(state.invEffAL, cent, v0.pt(), v0.rapidity(2)); + registryRho.fill(HIST("h2_rho1_AntiLambda_y"), v0.rapidity(2), v0.phi(), wAL); + if (cs) { + cs->rho1_AntiLambda_y->Fill(v0.rapidity(2), v0.phi(), wAL); + } + if (wAL == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 4.f); + } + registryOther.fill(HIST("hPtSelAntiLambda"), v0.pt()); + state.hPtSelAntiLambda_AllPt->Fill(v0.pt()); + } + // ====== End of shared-daughter cleaning =============================== + + // ── Veto QA event counters (filled once per event) ─────────────────── + + for (auto const& trk : tracks) { + + // ── RAW Stage QA Fills (truly before any selection — kinematics, detector quality, + // TPC, and TOF all filled here before any cut fires) ─────────────────────────── + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); + fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); + if (trk.hasTPC()) { + fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); + } + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } + } else { + fillProtonKinematics(ProtonQAStage::RawAfterTrackSel, trk); + fillProtonDetectorQuality(ProtonQAStage::RawAfterTrackSel, trk); + if (trk.hasTPC()) { + fillProtonTpcQA(ProtonQAStage::RawAfterTrackSel, trk); + } + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::RawAfterTrackSel, trk); + } + } + // ───────────────────────────────────────────────────────────────── + + // ── Track quality cuts ──────────────────────────────────────────── + fillCutFlow(1.0f, trk); + if (!trk.hasTPC()) { + continue; + } + fillCutFlow(2.0f, trk); + if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { + continue; + } + fillCutFlow(3.0f, trk); + if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { + continue; + } + fillCutFlow(4.0f, trk); + if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { + continue; + } + fillCutFlow(5.0f, trk); + if (trk.itsNCls() < pProtonITSMinClusters.value) { + continue; + } + fillCutFlow(6.0f, trk); + if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { + continue; + } + fillCutFlow(7.0f, trk); + if (trk.tpcSignal() < cPPMinTpcSignal.value) { + continue; + } + fillCutFlow(8.0f, trk); + // ───────────────────────────────────────────────────────────────── + + // ── pT acceptance cut ───────────────────────────────────────────── + if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { + continue; + } + fillCutFlow(9.0f, trk); + // ───────────────────────────────────────────────────────────────── + + // ── Proton kinematic acceptance ─────────────────────────────────── + // ETACUT-DISABLED (temporary): eta cut commented out, keeping rapidity cut only. + // if (std::abs(trk.eta()) > pProtonMaxEta.value) { continue; } + const float y = protonRapidity(trk); + if (std::abs(y) > pProtonMaxY.value) { + continue; + } + fillCutFlow(10.0f, trk); + // ───────────────────────────────────────────────────────────────── + // ── DCA cuts ───────────────────────────────────────────────────── + const bool passDCA = (std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && + (std::abs(trk.dcaZ()) < pProtonMaxDCAz.value); + if (!passDCA) { + continue; + } + fillCutFlow(11.0f, trk); + // ───────────────────────────────────────────────────────────────── + + // FIX-4: ProtonCounts_byPID_Check — protons only (sign > 0 guard added) + if (trk.sign() > 0) { + const float pt = trk.pt(); + const float nsTPC = std::abs(trk.tpcNSigmaPr()); + + if (nsTPC < pProtonTPCNsigma.value) { + state.h_TPC->Fill(pt); + } + if (trk.hasTOF() && nsTPC < pProtonTPCNsigma.value && + std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + state.h_TPCandTOF->Fill(pt); + } + if (nsTPC < pProtonTPCNsigma.value) { + if (!trk.hasTOF() || std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + state.h_TPCorTOF->Fill(pt); + } + } + } + // ───────────────────────────────────────────────────────────────── + + // TPC PID pass QA fill (intermediate stage, before full selection) + if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value) { + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::TpcPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcPid, trk); + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } + } else { + fillProtonKinematics(ProtonQAStage::TpcPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcPid, trk); + if (trk.hasTOF()) { + fillProtonTofQA(ProtonQAStage::TpcPid, trk); + } + } + } + + // TPC+TOF PID pass QA fill (intermediate stage, before full selection) + if (std::abs(trk.tpcNSigmaPr()) < pProtonTPCNsigma.value && trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < pProtonTOFNsigma.value) { + if (trk.sign() > 0) { + fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); + fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + } else { + fillProtonKinematics(ProtonQAStage::TpcTofPid, trk); + fillProtonDetectorQuality(ProtonQAStage::TpcTofPid, trk); + fillProtonTpcQA(ProtonQAStage::TpcTofPid, trk); + fillProtonTofQA(ProtonQAStage::TpcTofPid, trk); + } + } + + // ── Main PID selection ──────────────────────────────────────────── + if (!passesPrimProtonPid(trk)) { + continue; + } + fillCutFlow(12.0f, trk); + + // pi/K/e contaminant veto + if (!passesContaminantVeto(trk)) { + bool vetoUsedTOF = false; + const int vetoMask = contaminantVetoMask(trk, vetoUsedTOF); + for (int ib = 0; ib < 3; ++ib) { + if (vetoMask & (1 << ib)) { + fillCutFlow2D((vetoUsedTOF ? 19.0f : 16.0f) + ib, trk); + } + } + continue; + } + fillCutFlow(13.0f, trk); + + // TOF Matching Fraction check on selected protons + registryQaDetector.fill(HIST("Selection/TOF_Matching/hTOFMatchedFractionVsPt"), trk.pt(), trk.hasTOF() ? 1.0f : 0.0f); + // ── Item 4: 2D TOF-matching efficiency map for primary protons ────── + registryQaDetector.fill(HIST("Selection/TOF_Matching/h2f_TOFMatchedFractionVsEtaPhi"), + trk.eta(), trk.phi(), trk.hasTOF() ? 1.0f : 0.0f); + // ───────────────────────────────────────────────────────────────── + + // ── PID dEdx monitoring ─────────────────────────────────────────── + // vs total-p: unchanged — intentional total-momentum view + // vs pTPC (tpcInnerParam): added alongside for proton-selection QA comparison + if (trk.sign() > 0) { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); + if (trk.hasTOF()) { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + } else { + registryPid.fill(HIST("Proton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("Proton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + } + } else { + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_selected"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_selected"), trk.tpcInnerParam(), trk.tpcSignal()); + if (trk.hasTOF()) { + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPCTOF"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPCTOF"), trk.tpcInnerParam(), trk.tpcSignal()); + } else { + registryPid.fill(HIST("AntiProton/hTPCdEdxVsP_TPConly"), trk.p(), trk.tpcSignal()); + registryPid.fill(HIST("AntiProton/hTPCdEdxVsPTPC_TPConly"), trk.tpcInnerParam(), trk.tpcSignal()); + } + } + // ───────────────────────────────────────────────────────────────── + + // ── V0-daughter veto: reject tracks shared with any V0 daughter ─── + // Count before veto, check veto set, then classify or skip. + // This MUST appear before push_back so that every pp histogram + // (which iterates selectedPrimProtons / selectedPrimAntiProtons) + // automatically uses the cleaned collection. + const bool isV0Daughter = v0DaughterTrackIds.contains(trk.globalIndex()); + if (trk.sign() > 0) { + ++nProtonBeforeVeto; + } else { + ++nAntiPBeforeVeto; + } + if (isV0Daughter) { + // Track is a reconstructed V0 daughter — skip it. + if (trk.sign() > 0) { + ++nProtonRemovedVeto; + } else { + ++nAntiPRemovedVeto; + } + continue; + } + fillCutFlow(14.0f, trk); + // ───────────────────────────────────────────────────────────────── + + // ── Classify into proton / antiproton lists ─────────────────────── + fillCutFlow(15.0f, trk); + // VS Code: commented out unused variables. + // const float pz = trk.pt() * std::sinh(trk.eta()); + // const float eta = trk.eta(); + if (trk.sign() > 0) { + selectedPrimProtons.push_back(trk); + fillProtonKinematics(ProtonQAStage::FinalSelected, trk); + fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); + fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); + // TOF QA only when track was accepted through the TPC+TOF branch: + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { + fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } + + registryOther.fill(HIST("hPtSelectedPrimProton"), trk.pt()); + state.hPtSelectedPrimProton_AllPt->Fill(trk.pt()); + } else { + selectedPrimAntiProtons.push_back(trk); + fillProtonKinematics(ProtonQAStage::FinalSelected, trk); + fillProtonDetectorQuality(ProtonQAStage::FinalSelected, trk); + fillProtonTpcQA(ProtonQAStage::FinalSelected, trk); + // TOF QA only when track was accepted through the TPC+TOF branch: + // pt > pProtonTPCTOFSwitchPt guarantees hasTOF() and nSigmaTOF passed (beta window cut removed). //BETACUTCommented + if (trk.pt() >= pProtonTPCTOFSwitchPt.value) { + fillProtonTofQA(ProtonQAStage::FinalSelected, trk); + } + + registryOther.fill(HIST("hPtSelectedPrimAntiProton"), trk.pt()); + state.hPtSelectedPrimAntiProton_AllPt->Fill(trk.pt()); + } + // ───────────────────────────────────────────────────────────────── + } + + // ── Fill veto QA histograms (once per event, after the track loop) ── + registryOther.fill(HIST("hNPrimProtonBeforeVeto"), static_cast(nProtonBeforeVeto)); + registryOther.fill(HIST("hNPrimProtonRemovedByVeto"), static_cast(nProtonRemovedVeto)); + registryOther.fill(HIST("hNPrimProtonAfterVeto"), static_cast(nProtonBeforeVeto - nProtonRemovedVeto)); + registryOther.fill(HIST("hNPrimAntiProtonBeforeVeto"), static_cast(nAntiPBeforeVeto)); + registryOther.fill(HIST("hNPrimAntiProtonRemovedByVeto"), static_cast(nAntiPRemovedVeto)); + registryOther.fill(HIST("hNPrimAntiProtonAfterVeto"), static_cast(nAntiPBeforeVeto - nAntiPRemovedVeto)); + // ───────────────────────────────────────────────────────────────────── + + // ── ProtonVetoQA: fill four dedicated per-event histograms ─────────── + // These are filled using PROTON (sign > 0) counters only. + // N_before_veto = nProtonBeforeVeto (tracks passing quality+PID+DCA, before veto) + // N_removed = nProtonRemovedVeto (tracks rejected by the V0-daughter veto) + // N_after_veto = nProtonBeforeVeto - nProtonRemovedVeto + // = size of selectedPrimProtons, used by rho1(p), rho2(pp), rho2(p pbar) + // fraction = N_removed / N_before (0 when N_before = 0, avoids divide-by-zero) + { + const int nBefore = nProtonBeforeVeto; + const int nRemoved = nProtonRemovedVeto; + const int nAfter = nBefore - nRemoved; + const float fraction = (nBefore > 0) + ? static_cast(nRemoved) / static_cast(nBefore) + : 0.0f; + registryProtonVetoQA.fill(HIST("h1f_nBeforeVeto"), static_cast(nBefore)); + registryProtonVetoQA.fill(HIST("h1f_nRemovedByVeto"), static_cast(nRemoved)); + registryProtonVetoQA.fill(HIST("h1f_nAfterVeto"), static_cast(nAfter)); + registryProtonVetoQA.fill(HIST("h1f_fractionRemoved"), fraction); + // Accumulate run-level totals for endOfStream() summary. + state.runTotalProtonBeforeVeto += nBefore; + state.runTotalProtonRemovedVeto += nRemoved; + state.runTotalAntiPBeforeVeto += nAntiPBeforeVeto; + state.runTotalAntiPRemovedVeto += nAntiPRemovedVeto; + } + // ───────────────────────────────────────────────────────────────────── + + // ── Single-particle density for protons (y) ────────────────── + for (auto const& primProton : selectedPrimProtons) { + const float yp = protonRapidity(primProton); + const float wP = getEffWeight(state.invEffP, cent, primProton.pt(), yp); + registryRho.fill(HIST("h2_rho1_Proton_y"), yp, primProton.phi(), wP); + if (cs) { + cs->rho1_Proton_y->Fill(yp, primProton.phi(), wP); + } + registryOther.fill(HIST("hEffWeightProton"), wP); + if (wP == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 1.f); + } + } + for (auto const& primAntiProton : selectedPrimAntiProtons) { + const float yap = protonRapidity(primAntiProton); + const float wAP = getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yap); + registryRho.fill(HIST("h2_rho1_AntiProton_y"), yap, primAntiProton.phi(), wAP); + if (cs) { + cs->rho1_AntiProton_y->Fill(yap, primAntiProton.phi(), wAP); + } + if (wAP == 0.f) { + registryOther.fill(HIST("hEffWeightZeroCount"), 2.f); + } + } + // ───────────────────────────────────────────────────────────────────── + + registryOther.fill(HIST("hNSelectedPrimProtons"), static_cast(selectedPrimProtons.size())); + registryOther.fill(HIST("hNSelectedPrimAntiProtons"), static_cast(selectedPrimAntiProtons.size())); + + // ── Per-particle caches for the pair loops ─────────────────────────── + std::vector protonInfo; + std::vector antiProtonInfo; + protonInfo.reserve(selectedPrimProtons.size()); + antiProtonInfo.reserve(selectedPrimAntiProtons.size()); + std::transform(selectedPrimProtons.begin(), selectedPrimProtons.end(), + std::back_inserter(protonInfo), + [this](auto const& t) { return makeProtonInfo(t); }); + std::transform(selectedPrimAntiProtons.begin(), selectedPrimAntiProtons.end(), + std::back_inserter(antiProtonInfo), + [this](auto const& t) { return makeProtonInfo(t); }); + + // ── pp rho2 fills (y) ─────────────────────────────────────────────────── + for (auto const& p1 : selectedPrimProtons) { + const float y1_pp = protonRapidity(p1); + for (auto const& p2 : selectedPrimProtons) { + // QA3/PairCleaning: count all candidate pairs + registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 1); + if (p1.index() == p2.index()) { + // QA3/PairCleaning: rejected as same track + registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 2); + continue; + } // CHANGED from globalIndex() + // QA3/PairCleaning: bins 3 (Rejected q_inv) and 4 (Accepted) are filled at the q_inv cut below + + // QA3/RawPairDensity: fill Δη vs Δφ for every accepted p-p pair + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + const float deta = p1.eta() - p2.eta(); + const float dphi = RecoDecay::constrainAngle((p1.phi() - p2.phi()), -PIHalf); + registryCorrelationQA.fill(HIST("QA3/RawPairDensity/h2f_dEta_dPhi_PP"), deta, dphi); + + // QA3/SplitTrackQA: q_inv for every accepted p-p pair + { + const float p1mag = std::sqrt(p1.px() * p1.px() + p1.py() * p1.py() + p1.pz() * p1.pz()); + const float p2mag = std::sqrt(p2.px() * p2.px() + p2.py() * p2.py() + p2.pz() * p2.pz()); + const float E1 = std::sqrt(p1mag * p1mag + kMassProton * kMassProton); + const float E2 = std::sqrt(p2mag * p2mag + kMassProton * kMassProton); + const float qinvPP = computeQinv(p1.px(), p1.py(), p1.pz(), E1, p2.px(), p2.py(), p2.pz(), E2); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_Before_qinvCut"), qinvPP); + if (passesQinvQA(qinvPP)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_PP_After_qinvCut"), qinvPP); + } + } + + // QA3/SplitTrackQA: flag close pairs for split-track diagnosis + constexpr float cCloseDEta = 0.003f; + constexpr float cCloseDPhi = 0.005f; + if (std::abs(deta) < cCloseDEta && std::abs(dphi) < cCloseDPhi) { + const float dotProd = p1.px() * p2.px() + p1.py() * p2.py() + p1.pz() * p2.pz(); + const float mag1 = std::sqrt(p1.px() * p1.px() + p1.py() * p1.py() + p1.pz() * p1.pz()); + const float mag2 = std::sqrt(p2.px() * p2.px() + p2.py() * p2.py() + p2.pz() * p2.pz()); + const float openingAngle = std::acos(std::clamp(dotProd / (mag1 * mag2), -1.f, 1.f)); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h2f_dEta_dPhi_ClosePairs"), deta, dphi); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_openingAngle_ClosePairs"), openingAngle); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_deltaPt_ClosePairs"), std::abs(p1.pt() - p2.pt())); + // Shared-cluster fraction (only for close pairs) + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t1"), p1.tpcFractionSharedCls()); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t2"), p2.tpcFractionSharedCls()); + } + } + + // q_inv cut (pp) + { + const float qE1 = std::sqrt(p1.px() * p1.px() + p1.py() * p1.py() + p1.pz() * p1.pz() + kMassProton * kMassProton); + const float qE2 = std::sqrt(p2.px() * p2.px() + p2.py() * p2.py() + p2.pz() * p2.pz() + kMassProton * kMassProton); + if (failsQinvCut(computeQinv(p1.px(), p1.py(), p1.pz(), qE1, p2.px(), p2.py(), p2.pz(), qE2))) { + registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 3); + continue; + } + registryCorrelationQA.fill(HIST("QA3/PairCleaning/h1f_pairCleaning"), 4); + } + const float y2_pp = protonRapidity(p2); + const int idxY1 = unrolledIndexY(y1_pp, p1.phi()); + const int idxY2 = unrolledIndexY(y2_pp, p2.phi()); + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffP, cent, p1.pt(), y1_pp) * getEffWeight(state.invEffP, cent, p2.pt(), y2_pp); + if (idxY1 >= 0 && idxY2 >= 0) { + state.hRho2_pp_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->pp_y, idxY1, idxY2, wPair); + } + } + } + for (auto const& a : protonInfo) { + for (auto const& b : antiProtonInfo) { + const float qinvPAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_Before_qinvCut"), qinvPAp); + if (passesQinvQA(qinvPAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_pAp_After_qinvCut"), qinvPAp); + } + if (failsQinvCut(qinvPAp)) { + continue; + } + const float wPair = getEffWeight(state.invEffP, cent, a.pt, a.y) * getEffWeight(state.invEffAP, cent, b.pt, b.y); + if (a.idxY >= 0 && b.idxY >= 0) { + state.hRho2_pAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->pAp_y, a.idxY, b.idxY, wPair); + } + } + } + for (auto const& a : antiProtonInfo) { + for (auto const& b : protonInfo) { + const float qinvApp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_Before_qinvCut"), qinvApp); + if (passesQinvQA(qinvApp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_App_After_qinvCut"), qinvApp); + } + if (failsQinvCut(qinvApp)) { + continue; + } + const float wPair = getEffWeight(state.invEffAP, cent, a.pt, a.y) * getEffWeight(state.invEffP, cent, b.pt, b.y); + if (a.idxY >= 0 && b.idxY >= 0) { + state.hRho2_App_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->App_y, a.idxY, b.idxY, wPair); + } + } + } + for (auto const& a : antiProtonInfo) { + for (auto const& b : antiProtonInfo) { + if (a.gid == b.gid) { + continue; + } + const float qinvApAp = computeQinv(a.px, a.py, a.pz, a.E, b.px, b.py, b.pz, b.E); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_Before_qinvCut"), qinvApAp); + if (passesQinvQA(qinvApAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_ApAp_After_qinvCut"), qinvApAp); + } + if (failsQinvCut(qinvApAp)) { + continue; + } + const float wPair = getEffWeight(state.invEffAP, cent, a.pt, a.y) * getEffWeight(state.invEffAP, cent, b.pt, b.y); + if (a.idxY >= 0 && b.idxY >= 0) { + state.hRho2_ApAp_y_AllPt->Fill(a.idxY + 0.5f, b.idxY + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->ApAp_y, a.idxY, b.idxY, wPair); + } + } + } + // ───────────────────────────────────────────────────────────────────── + + // ====== Step 5: Lambda-proton and AntiLambda-proton pairing ============ + // Both selectedLambdas/AntiLambdas and selectedPrimProtons/AntiProtons are + // now fully populated. Veto set was built from validated daughters only. + + for (const auto& v0 : selectedLambdas) { + const auto& v0PosDau = v0.posTrack_as(); + const float pt = v0.pt(); + int bin = -1; + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { + bin = static_cast(i); + break; + } + } + + for (auto const& primProton : selectedPrimProtons) { + if (primProton.globalIndex() == v0.posTrackId() || primProton.globalIndex() == v0.negTrackId()) { + continue; + } + // QA3/SplitTrackQA: q_inv for every accepted Lambda-proton pair + float qinvLP = 0.0f; + { + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float pprmag = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); + const float Eppr = std::sqrt(pprmag * pprmag + kMassProton * kMassProton); + qinvLP = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primProton.px(), primProton.py(), primProton.pz(), Eppr); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLP); + } + // QA3/SplitTrackQA: q_inv Before and After cut + if (passesQinvQA(qinvLP)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLP); + } + // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primProton and Lambda dau proton-primProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. Lambda (V0) + const float dPhi_V0_p = RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf3D); + const float dEta_V0_p = v0.eta() - primProton.eta(); + const float yLambda = v0.rapidity(1); + const float yPrimP = protonRapidity(primProton); + const float dY_V0_p = yLambda - yPrimP; + // q_inv using Lambda (V0) four-momentum (kMassLambda hypothesis) + const float v0pmag_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float Ev0_3d = std::sqrt(v0pmag_3d * v0pmag_3d + kMassLambda * kMassLambda); + const float pprmag_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Eppr_3d = std::sqrt(pprmag_3d * pprmag_3d + kMassProton * kMassProton); + const float qinvLP_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr_3d); + // q_inv using Lambda daughter proton (kMassProton hypothesis), only for *_dau histograms + const float dauPmag_3d = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float Edau_3d = std::sqrt(dauPmag_3d * dauPmag_3d + kMassProton * kMassProton); + const float qinvLP_dau = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr_3d); + // Angular variables w.r.t. Lambda daughter proton + const float dPhi_dau_p = RecoDecay::constrainAngle((v0PosDau.phi() - primProton.phi()), -PIHalf3D); + const float dEta_dau_p = v0PosDau.eta() - primProton.eta(); + const float yDauP = protonRapidity(v0PosDau); + const float dY_dau_p = yDauP - yPrimP; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp"), qinvLP_V0, dPhi_V0_p, dEta_V0_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_Lp_dau"), qinvLP_dau, dPhi_dau_p, dEta_dau_p); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp"), qinvLP_V0, dPhi_V0_p, dY_V0_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_Lp_dau"), qinvLP_dau, dPhi_dau_p, dY_dau_p); + } + // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + const float deta_lp = v0.eta() - primProton.eta(); + const float dphi_lp = RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf); + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LP"), + deta_lp, dphi_lp); + constexpr float cCloseDEta = 0.003f; + constexpr float cCloseDPhi = 0.005f; + if (std::abs(deta_lp) < cCloseDEta && std::abs(dphi_lp) < cCloseDPhi) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t1"), v0PosDau.tpcFractionSharedCls()); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t2"), primProton.tpcFractionSharedCls()); + } + } + const float yL = v0.rapidity(1); + const float yP = protonRapidity(primProton); + const int idxYL = unrolledIndexY(yL, v0.phi()); + const int idxYP = unrolledIndexY(yP, primProton.phi()); + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_Lp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + // q_inv vs DeltaPhi diagnostic (filled BEFORE applying q_inv rejection) + float dPhi_Lp = primProton.phi() - v0.phi(); + while (dPhi_Lp > TMath::Pi()) { + dPhi_Lp -= 2.0f * TMath::Pi(); + } + while (dPhi_Lp < -TMath::Pi()) { + dPhi_Lp += 2.0f * TMath::Pi(); + } + registryCorrelationQA.fill(HIST("h2_qinv_dPhi_Lp"), dPhi_Lp, qinvLP); + + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + if (failsQinvCut(qinvLP)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_Lp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + if (idxYL >= 0 && idxYP >= 0 && bin >= 0) { + state.hPtPrimProton_Lambda[bin]->Fill(primProton.pt()); + } + const float wPair = getEffWeight(state.invEffL, cent, v0.pt(), yL) * getEffWeight(state.invEffP, cent, primProton.pt(), yP); + if (idxYL >= 0 && idxYP >= 0) { + state.hRho2_Lp_y_AllPt->Fill(idxYL + 0.5f, idxYP + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->Lp_y, idxYL, idxYP, wPair); + } + ++nPairs_Lambda_PrimProton; + } + for (auto const& primAntiProton : selectedPrimAntiProtons) { + if (primAntiProton.globalIndex() == v0.posTrackId() || primAntiProton.globalIndex() == v0.negTrackId()) { + continue; + } + // QA3/SplitTrackQA: q_inv for every accepted Lambda-antiproton pair + float qinvLAp = 0.0f; + { + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float paprmag = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); + const float Epapr = std::sqrt(paprmag * paprmag + kMassProton * kMassProton); + qinvLAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvLAp); + } + // QA3/SplitTrackQA: q_inv Before and After cut + if (passesQinvQA(qinvLAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvLAp); + } + // QA3/QinvVsAngles: 3D fills for Lambda(V0)-primAntiProton and Lambda dau proton-primAntiProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. Lambda (V0) + const float dPhi_V0_ap = RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_V0_ap = v0.eta() - primAntiProton.eta(); + const float yLambdaL = v0.rapidity(1); + const float yPrimAp = protonRapidity(primAntiProton); + const float dY_V0_ap = yLambdaL - yPrimAp; + // q_inv using Lambda (V0) four-momentum (kMassLambda hypothesis) + const float v0pmag_3dL = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float Ev0_3dL = std::sqrt(v0pmag_3dL * v0pmag_3dL + kMassLambda * kMassLambda); + const float paprmag_3dL = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Epapr_3dL = std::sqrt(paprmag_3dL * paprmag_3dL + kMassProton * kMassProton); + const float qinvLAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0_3dL, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr_3dL); + // q_inv using Lambda daughter proton (kMassProton hypothesis), only for *_dau histograms + const float dauPmag_3dL = std::sqrt(v0PosDau.px() * v0PosDau.px() + v0PosDau.py() * v0PosDau.py() + v0PosDau.pz() * v0PosDau.pz()); + const float Edau_3dL = std::sqrt(dauPmag_3dL * dauPmag_3dL + kMassProton * kMassProton); + const float qinvLAp_dau = computeQinv(v0PosDau.px(), v0PosDau.py(), v0PosDau.pz(), Edau_3dL, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr_3dL); + // Angular variables w.r.t. Lambda daughter proton + const float dPhi_dau_ap = RecoDecay::constrainAngle((v0PosDau.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_dau_ap = v0PosDau.eta() - primAntiProton.eta(); + const float yDauPL = protonRapidity(v0PosDau); + const float dY_dau_ap = yDauPL - yPrimAp; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp"), qinvLAp_V0, dPhi_V0_ap, dEta_V0_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_LAp_dau"), qinvLAp_dau, dPhi_dau_ap, dEta_dau_ap); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp"), qinvLAp_V0, dPhi_V0_ap, dY_V0_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_LAp_dau"), qinvLAp_dau, dPhi_dau_ap, dY_dau_ap); + } + // QA3/RawPairDensity: Lambda-antiproton → same LP histogram + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LP"), + v0.eta() - primAntiProton.eta(), + RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf)); + } + const float yL2 = v0.rapidity(1); + const float yAp = protonRapidity(primAntiProton); + const int idxYL2 = unrolledIndexY(yL2, v0.phi()); + const int idxYAp = unrolledIndexY(yAp, primAntiProton.phi()); + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_LAp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + if (failsQinvCut(qinvLAp)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_LAp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + if (idxYL2 >= 0 && idxYAp >= 0 && bin >= 0) { + state.hPtPrimAntiProton_Lambda[bin]->Fill(primAntiProton.pt()); + } + const float wPair = getEffWeight(state.invEffL, cent, v0.pt(), yL2) * getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yAp); + if (idxYL2 >= 0 && idxYAp >= 0) { + state.hRho2_LAp_y_AllPt->Fill(idxYL2 + 0.5f, idxYAp + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->LAp_y, idxYL2, idxYAp, wPair); + } + ++nPairs_Lambda_PrimAntiProton; + } + + // ── Autocorrelation diagnostic: primary proton vs Lambda daughter proton── + // Lambda proton daughter = v0PosDau (positive track, identified as proton + // by |tpcNSigmaPr| < PProtonTPCNsigma, already in scope above). + // Loop over ALL selected primary protons. NOTE: selectedPrimProtons is already + // cleaned by the V0-daughter veto, so the same-track case cannot occur here; + // this only checks for residual proximity correlations (e.g. undetected feed-down). + // Fills inclusive h2f_dEta_dPhi for all pairs and + // h2f_dEta_dPhi_nonSameTrack for every pair in this cleaned sample. + { + const float etaDau = v0PosDau.eta(); + const float phiDau = v0PosDau.phi(); + for (auto const& primProton : selectedPrimProtons) { + const float dEta = primProton.eta() - etaDau; + float dPhi = primProton.phi() - phiDau; + // Wrap Δφ into [-π, +π] + dPhi = RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI); + + // Always fill the inclusive histogram. + registryCorrelationQA.fill( + HIST("PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi"), + dEta, dPhi); + registryCorrelationQA.fill( + HIST("PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_display"), + dEta, displayDeltaPhi(dPhi)); + + // Fill non-same-track histogram when primary proton ≠ Lambda pos-daughter. + if (primProton.globalIndex() != v0.posTrackId()) { + registryCorrelationQA.fill( + HIST("PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_nonSameTrack"), + dEta, dPhi); + registryCorrelationQA.fill( + HIST("PrimaryProton_vs_LambdaDaughter/h2f_dEta_dPhi_nonSameTrack_display"), + dEta, displayDeltaPhi(dPhi)); + } + } + } + // ───────────────────────────────────────────────────────────────── + + // ── Combo 2: Lambda daughter proton ↔ primary antiproton ───────── + // The Lambda pos-daughter (charge +1) CANNOT appear in the primary + // antiproton list (charge −1), so same-track matching is impossible. + // We fill only the inclusive Δη vs Δφ histogram. + { + const float etaDauL = v0PosDau.eta(); + const float phiDauL = v0PosDau.phi(); + for (auto const& primAntiProton : selectedPrimAntiProtons) { + const float dEta2 = primAntiProton.eta() - etaDauL; + float dPhi2 = primAntiProton.phi() - phiDauL; + dPhi2 = RecoDecay::constrainAngle(dPhi2, -o2::constants::math::PI); + registryCorrelationQA.fill( + HIST("PrimaryAntiProton_vs_LambdaDaughter/h2f_dEta_dPhi"), + dEta2, dPhi2); + registryCorrelationQA.fill( + HIST("PrimaryAntiProton_vs_LambdaDaughter/h2f_dEta_dPhi_display"), + dEta2, displayDeltaPhi(dPhi2)); + } + } + // ───────────────────────────────────────────────────────────────── + } + + for (const auto& v0 : selectedAntiLambdas) { + const auto& v0NegDau = v0.negTrack_as(); + const float pt = v0.pt(); + int bin = -1; + for (size_t i = 0; i < state.ptEdges.size() - 1; ++i) { + if (pt >= state.ptEdges[i] && pt < state.ptEdges[i + 1]) { + bin = static_cast(i); + break; + } + } + + for (auto const& primProton : selectedPrimProtons) { + if (primProton.globalIndex() == v0.posTrackId() || primProton.globalIndex() == v0.negTrackId()) { + continue; + } + // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-proton pair + float qinvALp = 0.0f; + { + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float pprmag3 = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); + const float Eppr3 = std::sqrt(pprmag3 * pprmag3 + kMassProton * kMassProton); + qinvALp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primProton.px(), primProton.py(), primProton.pz(), Eppr3); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALp); + } + // QA3/SplitTrackQA: q_inv Before and After cut + if (passesQinvQA(qinvALp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALp); + } + // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primProton and AntiLambda dau antiproton-primProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. AntiLambda (V0) + const float dPhi_AL_p = RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf3D); + const float dEta_AL_p = v0.eta() - primProton.eta(); + const float yAL = v0.rapidity(2); + const float yPrimP3 = protonRapidity(primProton); + const float dY_AL_p = yAL - yPrimP3; + // q_inv using AntiLambda (V0) four-momentum (kMassLambda hypothesis) + const float almag_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float EalL_3d = std::sqrt(almag_3d * almag_3d + kMassLambda * kMassLambda); + const float pprmag3_3d = std::sqrt(primProton.px() * primProton.px() + primProton.py() * primProton.py() + primProton.pz() * primProton.pz()); + const float Eppr3_3d = std::sqrt(pprmag3_3d * pprmag3_3d + kMassProton * kMassProton); + const float qinvALp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr3_3d); + // q_inv using AntiLambda daughter antiproton (kMassProton hypothesis), only for *_dau histograms + const float dauPmagAL_3d = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float EdauAL_3d = std::sqrt(dauPmagAL_3d * dauPmagAL_3d + kMassProton * kMassProton); + const float qinvALp_dau = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), EdauAL_3d, primProton.px(), primProton.py(), primProton.pz(), Eppr3_3d); + // Angular variables w.r.t. AntiLambda daughter antiproton + const float dPhi_dauAL_p = RecoDecay::constrainAngle((v0NegDau.phi() - primProton.phi()), -PIHalf3D); + const float dEta_dauAL_p = v0NegDau.eta() - primProton.eta(); + const float yDauAL = protonRapidity(v0NegDau); + const float dY_dauAL_p = yDauAL - yPrimP3; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp"), qinvALp_V0, dPhi_AL_p, dEta_AL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALp_dau"), qinvALp_dau, dPhi_dauAL_p, dEta_dauAL_p); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp"), qinvALp_V0, dPhi_AL_p, dY_AL_p); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALp_dau"), qinvALp_dau, dPhi_dauAL_p, dY_dauAL_p); + } + // QA3/RawPairDensity: AntiLambda-proton → same LP histogram + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LP"), + v0.eta() - primProton.eta(), + RecoDecay::constrainAngle((v0.phi() - primProton.phi()), -PIHalf)); + } + const float yAL = v0.rapidity(2); + const float yP2 = protonRapidity(primProton); + const int idxYAL = unrolledIndexY(yAL, v0.phi()); + const int idxYP2 = unrolledIndexY(yP2, primProton.phi()); + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + if (failsQinvCut(qinvALp)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primProton.px(), primProton.py(), primProton.pz(), kMassProton)); + if (idxYAL >= 0 && idxYP2 >= 0 && bin >= 0) { + state.hPtPrimProton_AntiLambda[bin]->Fill(primProton.pt()); + } + const float wPair = getEffWeight(state.invEffAL, cent, v0.pt(), yAL) * getEffWeight(state.invEffP, cent, primProton.pt(), yP2); + if (idxYAL >= 0 && idxYP2 >= 0) { + state.hRho2_ALp_y_AllPt->Fill(idxYAL + 0.5f, idxYP2 + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->ALp_y, idxYAL, idxYP2, wPair); + } + ++nPairs_AntiLambda_PrimProton; + } + for (auto const& primAntiProton : selectedPrimAntiProtons) { + if (primAntiProton.globalIndex() == v0.posTrackId() || primAntiProton.globalIndex() == v0.negTrackId()) { + continue; + } + // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-antiproton pair + float qinvALAp = 0.0f; + { + const float v0pmag = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float paprmag2 = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Ev0 = std::sqrt(v0pmag * v0pmag + kMassLambda * kMassLambda); + const float Epapr2 = std::sqrt(paprmag2 * paprmag2 + kMassProton * kMassProton); + qinvALAp = computeQinv(v0.px(), v0.py(), v0.pz(), Ev0, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_Before_qinvCut"), qinvALAp); + } + // QA3/SplitTrackQA: q_inv Before and After cut + if (passesQinvQA(qinvALAp)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LP_After_qinvCut"), qinvALAp); + } + // QA3/QinvVsAngles: 3D fills for AntiLambda(V0)-primAntiProton and AntiLambda dau antiproton-primAntiProton + { + constexpr float PIHalf3D = o2::constants::math::PI / 2.0f; + // Angular variables w.r.t. AntiLambda (V0) + const float dPhi_AL_ap = RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_AL_ap = v0.eta() - primAntiProton.eta(); + const float yAL2 = v0.rapidity(2); + const float yPrimAp2 = protonRapidity(primAntiProton); + const float dY_AL_ap = yAL2 - yPrimAp2; + // q_inv using AntiLambda (V0) four-momentum (kMassLambda hypothesis) + const float almag2_3d = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + const float EalL2_3d = std::sqrt(almag2_3d * almag2_3d + kMassLambda * kMassLambda); + const float paprmag2_3d = std::sqrt(primAntiProton.px() * primAntiProton.px() + primAntiProton.py() * primAntiProton.py() + primAntiProton.pz() * primAntiProton.pz()); + const float Epapr2_3d = std::sqrt(paprmag2_3d * paprmag2_3d + kMassProton * kMassProton); + const float qinvALAp_V0 = computeQinv(v0.px(), v0.py(), v0.pz(), EalL2_3d, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2_3d); + // q_inv using AntiLambda daughter antiproton (kMassProton hypothesis), only for *_dau histograms + const float dauPmagAL2_3d = std::sqrt(v0NegDau.px() * v0NegDau.px() + v0NegDau.py() * v0NegDau.py() + v0NegDau.pz() * v0NegDau.pz()); + const float EdauAL2_3d = std::sqrt(dauPmagAL2_3d * dauPmagAL2_3d + kMassProton * kMassProton); + const float qinvALAp_dau = computeQinv(v0NegDau.px(), v0NegDau.py(), v0NegDau.pz(), EdauAL2_3d, primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), Epapr2_3d); + // Angular variables w.r.t. AntiLambda daughter antiproton + const float dPhi_dauAL_ap = RecoDecay::constrainAngle((v0NegDau.phi() - primAntiProton.phi()), -PIHalf3D); + const float dEta_dauAL_ap = v0NegDau.eta() - primAntiProton.eta(); + const float yDauAL2 = protonRapidity(v0NegDau); + const float dY_dauAL_ap = yDauAL2 - yPrimAp2; + // eta space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp"), qinvALAp_V0, dPhi_AL_ap, dEta_AL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/eta/h3f_qinv_dPhi_dEta_ALAp_dau"), qinvALAp_dau, dPhi_dauAL_ap, dEta_dauAL_ap); + // y space + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp"), qinvALAp_V0, dPhi_AL_ap, dY_AL_ap); + registryCorrelationQA.fill(HIST("QA3/QinvVsAngles/y/h3f_qinv_dPhi_dY_ALAp_dau"), qinvALAp_dau, dPhi_dauAL_ap, dY_dauAL_ap); + } + // QA3/SplitTrackQA: close-pair block for daughter-overlap diagnosed split tracks + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + const float deta_alap = v0.eta() - primAntiProton.eta(); + const float dphi_alap = RecoDecay::constrainAngle((v0.phi() - primAntiProton.phi()), -PIHalf); + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LP"), + deta_alap, dphi_alap); + constexpr float cCloseDEta = 0.003f; + constexpr float cCloseDPhi = 0.005f; + if (std::abs(deta_alap) < cCloseDEta && std::abs(dphi_alap) < cCloseDPhi) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t1"), v0NegDau.tpcFractionSharedCls()); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_sharedClsFraction_t2"), primAntiProton.tpcFractionSharedCls()); + } + } + const float yAL2 = v0.rapidity(2); + const float yAp2 = protonRapidity(primAntiProton); + const int idxYAL2 = unrolledIndexY(yAL2, v0.phi()); + const int idxYAp2 = unrolledIndexY(yAp2, primAntiProton.phi()); + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALAp_BeforeQinvCut"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + // Physics pair correlations (rho2 / pT / pair-count): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + if (failsQinvCut(qinvALAp)) { + continue; + } + registryCorrelationQA.fill(HIST("QA3/Kstar/h1f_kstar_ALAp"), + computeKstar(v0.px(), v0.py(), v0.pz(), kMassLambda, + primAntiProton.px(), primAntiProton.py(), primAntiProton.pz(), kMassProton)); + if (idxYAL2 >= 0 && idxYAp2 >= 0 && bin >= 0) { + state.hPtPrimAntiProton_AntiLambda[bin]->Fill(primAntiProton.pt()); + } + const float wPair = getEffWeight(state.invEffAL, cent, v0.pt(), yAL2) * getEffWeight(state.invEffAP, cent, primAntiProton.pt(), yAp2); + if (idxYAL2 >= 0 && idxYAp2 >= 0) { + state.hRho2_ALAp_y_AllPt->Fill(idxYAL2 + 0.5f, idxYAp2 + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->ALAp_y, idxYAL2, idxYAp2, wPair); + } + ++nPairs_AntiLambda_PrimAntiProton; + } + + // ── Combo 3: AntiLambda daughter antiproton ↔ primary proton ───── + // The AntiLambda neg-daughter (charge −1) CANNOT appear in the primary + // proton list (charge +1), so same-track matching is impossible. + // We fill only the inclusive Δη vs Δφ histogram. + { + const float etaDauAL = v0NegDau.eta(); + const float phiDauAL = v0NegDau.phi(); + for (auto const& primProton : selectedPrimProtons) { + const float dEta3 = primProton.eta() - etaDauAL; + float dPhi3 = primProton.phi() - phiDauAL; + dPhi3 = RecoDecay::constrainAngle(dPhi3, -o2::constants::math::PI); + registryCorrelationQA.fill( + HIST("PrimaryProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi"), + dEta3, dPhi3); + registryCorrelationQA.fill( + HIST("PrimaryProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_display"), + dEta3, displayDeltaPhi(dPhi3)); + } + } + // ───────────────────────────────────────────────────────────────── + + // ── Combo 4: AntiLambda daughter antiproton ↔ primary antiproton ─ + // AntiLambda neg-daughter = v0NegDau (charge −1, identified as + // antiproton). Primary antiproton = selectedPrimAntiProtons (charge −1). + // Fills inclusive h2f_dEta_dPhi for all pairs and h2f_dEta_dPhi_nonSameTrack + // for every pair in this cleaned sample (selectedPrimAntiProtons is already + // veto-cleaned, so the primary antiproton can never be the AntiLambda + // negative daughter; the nonSameTrack histogram will equal the inclusive one). + { + const float etaDauAL4 = v0NegDau.eta(); + const float phiDauAL4 = v0NegDau.phi(); + for (auto const& primAntiProton : selectedPrimAntiProtons) { + const float dEta4 = primAntiProton.eta() - etaDauAL4; + float dPhi4 = primAntiProton.phi() - phiDauAL4; + dPhi4 = RecoDecay::constrainAngle(dPhi4, -o2::constants::math::PI); + + // Always fill the inclusive histogram. + registryCorrelationQA.fill( + HIST("PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi"), + dEta4, dPhi4); + registryCorrelationQA.fill( + HIST("PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_display"), + dEta4, displayDeltaPhi(dPhi4)); + + // Fill non-same-track histogram when primary antiproton ≠ AntiLambda neg-daughter. + if (primAntiProton.globalIndex() != v0.negTrackId()) { + registryCorrelationQA.fill( + HIST("PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_nonSameTrack"), + dEta4, dPhi4); + registryCorrelationQA.fill( + HIST("PrimaryAntiProton_vs_AntiLambdaDaughter/h2f_dEta_dPhi_nonSameTrack_display"), + dEta4, displayDeltaPhi(dPhi4)); + } + } + } + // ───────────────────────────────────────────────────────────────── + } + // ====== End of Step 5 pairing loops =================================== + + // ── Lambda-Lambda rho2 fills ─────────────────────────────────────── + // LL + for (auto const& v1 : selectedLambdas) { + const float yv1L = v1.rapidity(1); + for (auto const& v2 : selectedLambdas) { + if (v1.index() == v2.index()) { + continue; + } + if (v1.posTrackId() == v2.posTrackId()) { + continue; + } + if (v1.posTrackId() == v2.negTrackId()) { + continue; + } + if (v1.negTrackId() == v2.posTrackId()) { + continue; + } + if (v1.negTrackId() == v2.negTrackId()) { + continue; + } + // QA3/RawPairDensity: Lambda-Lambda → h2f_dEta_dPhi_LL + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LL"), + v1.eta() - v2.eta(), + RecoDecay::constrainAngle((v1.phi() - v2.phi()), -PIHalf)); + } + // QA3/SplitTrackQA: q_inv for every accepted Lambda-Lambda pair + { + const float v1pmag = std::sqrt(v1.px() * v1.px() + v1.py() * v1.py() + v1.pz() * v1.pz()); + const float v2pmag = std::sqrt(v2.px() * v2.px() + v2.py() * v2.py() + v2.pz() * v2.pz()); + const float Ev1 = std::sqrt(v1pmag * v1pmag + kMassLambda * kMassLambda); + const float Ev2 = std::sqrt(v2pmag * v2pmag + kMassLambda * kMassLambda); + const float qinvLL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1, v2.px(), v2.py(), v2.pz(), Ev2); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLL); + if (passesQinvQA(qinvLL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLL); + } + if (failsQinvCut(qinvLL)) { + continue; + } + } + const int idxY1 = unrolledIndexY(yv1L, v1.phi()); + const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffL, cent, v1.pt(), yv1L) * getEffWeight(state.invEffL, cent, v2.pt(), v2.rapidity(1)); + if (idxY1 >= 0 && idxY2 >= 0) { + state.hRho2_LL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->LL_y, idxY1, idxY2, wPair); + } + } + } + // LAL + for (auto const& v1 : selectedLambdas) { + const float yv1LAL = v1.rapidity(1); + for (auto const& v2 : selectedAntiLambdas) { + if (v1.posTrackId() == v2.posTrackId()) { + continue; + } + if (v1.posTrackId() == v2.negTrackId()) { + continue; + } + if (v1.negTrackId() == v2.posTrackId()) { + continue; + } + if (v1.negTrackId() == v2.negTrackId()) { + continue; + } + // QA3/RawPairDensity: Lambda-AntiLambda → same h2f_dEta_dPhi_LL + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LL"), + v1.eta() - v2.eta(), + RecoDecay::constrainAngle((v1.phi() - v2.phi()), -PIHalf)); + } + // QA3/SplitTrackQA: q_inv for every accepted Lambda-AntiLambda pair + { + const float v1pmagL = std::sqrt(v1.px() * v1.px() + v1.py() * v1.py() + v1.pz() * v1.pz()); + const float v2pmagL = std::sqrt(v2.px() * v2.px() + v2.py() * v2.py() + v2.pz() * v2.pz()); + const float Ev1L = std::sqrt(v1pmagL * v1pmagL + kMassLambda * kMassLambda); + const float Ev2L = std::sqrt(v2pmagL * v2pmagL + kMassLambda * kMassLambda); + const float qinvLAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1L, v2.px(), v2.py(), v2.pz(), Ev2L); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvLAL); + if (passesQinvQA(qinvLAL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvLAL); + } + if (failsQinvCut(qinvLAL)) { + continue; + } + } + const int idxY1 = unrolledIndexY(yv1LAL, v1.phi()); + const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffL, cent, v1.pt(), yv1LAL) * getEffWeight(state.invEffAL, cent, v2.pt(), v2.rapidity(2)); + if (idxY1 >= 0 && idxY2 >= 0) { + state.hRho2_LAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->LAL_y, idxY1, idxY2, wPair); + } + } + } + // ALL + for (auto const& v1 : selectedAntiLambdas) { + const float yv1ALL = v1.rapidity(2); + for (auto const& v2 : selectedLambdas) { + if (v1.posTrackId() == v2.posTrackId()) { + continue; + } + if (v1.posTrackId() == v2.negTrackId()) { + continue; + } + if (v1.negTrackId() == v2.posTrackId()) { + continue; + } + if (v1.negTrackId() == v2.negTrackId()) { + continue; + } + // QA3/RawPairDensity: AntiLambda-Lambda → same h2f_dEta_dPhi_LL + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LL"), + v1.eta() - v2.eta(), + RecoDecay::constrainAngle((v1.phi() - v2.phi()), -PIHalf)); + } + // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-Lambda pair + { + const float v1pmagAL = std::sqrt(v1.px() * v1.px() + v1.py() * v1.py() + v1.pz() * v1.pz()); + const float v2pmagAL = std::sqrt(v2.px() * v2.px() + v2.py() * v2.py() + v2.pz() * v2.pz()); + const float Ev1AL = std::sqrt(v1pmagAL * v1pmagAL + kMassLambda * kMassLambda); + const float Ev2AL = std::sqrt(v2pmagAL * v2pmagAL + kMassLambda * kMassLambda); + const float qinvALL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL, v2.px(), v2.py(), v2.pz(), Ev2AL); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALL); + if (passesQinvQA(qinvALL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALL); + } + if (failsQinvCut(qinvALL)) { + continue; + } + } + const int idxY1 = unrolledIndexY(yv1ALL, v1.phi()); + const int idxY2 = unrolledIndexY(v2.rapidity(1), v2.phi()); + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffAL, cent, v1.pt(), yv1ALL) * getEffWeight(state.invEffL, cent, v2.pt(), v2.rapidity(1)); + if (idxY1 >= 0 && idxY2 >= 0) { + state.hRho2_ALL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->ALL_y, idxY1, idxY2, wPair); + } + } + } + // ALAL + for (auto const& v1 : selectedAntiLambdas) { + const float yv1ALAL = v1.rapidity(2); + for (auto const& v2 : selectedAntiLambdas) { + if (v1.index() == v2.index()) { + continue; + } + if (v1.posTrackId() == v2.posTrackId()) { + continue; + } + if (v1.posTrackId() == v2.negTrackId()) { + continue; + } + if (v1.negTrackId() == v2.posTrackId()) { + continue; + } + if (v1.negTrackId() == v2.negTrackId()) { + continue; + } + // QA3/RawPairDensity: AntiLambda-AntiLambda → same h2f_dEta_dPhi_LL + { + constexpr float PIHalf = o2::constants::math::PI / 2.0f; + registryCorrelationQA.fill( + HIST("QA3/RawPairDensity/h2f_dEta_dPhi_LL"), + v1.eta() - v2.eta(), + RecoDecay::constrainAngle((v1.phi() - v2.phi()), -PIHalf)); + } + // QA3/SplitTrackQA: q_inv for every accepted AntiLambda-AntiLambda pair + { + const float v1pmagAL2 = std::sqrt(v1.px() * v1.px() + v1.py() * v1.py() + v1.pz() * v1.pz()); + const float v2pmagAL2 = std::sqrt(v2.px() * v2.px() + v2.py() * v2.py() + v2.pz() * v2.pz()); + const float Ev1AL2 = std::sqrt(v1pmagAL2 * v1pmagAL2 + kMassLambda * kMassLambda); + const float Ev2AL2 = std::sqrt(v2pmagAL2 * v2pmagAL2 + kMassLambda * kMassLambda); + const float qinvALAL = computeQinv(v1.px(), v1.py(), v1.pz(), Ev1AL2, v2.px(), v2.py(), v2.pz(), Ev2AL2); + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_Before_qinvCut"), qinvALAL); + if (passesQinvQA(qinvALAL)) { + registryCorrelationQA.fill(HIST("QA3/SplitTrackQA/h1f_qinv_LL_After_qinvCut"), qinvALAL); + } + if (failsQinvCut(qinvALAL)) { + continue; + } + } + const int idxY1 = unrolledIndexY(yv1ALAL, v1.phi()); + const int idxY2 = unrolledIndexY(v2.rapidity(2), v2.phi()); + // Physics pair correlations (rho2): q_inv cut applied (pairs with q_inv <= kQinvCut are rejected above) + const float wPair = getEffWeight(state.invEffAL, cent, v1.pt(), yv1ALAL) * getEffWeight(state.invEffAL, cent, v2.pt(), v2.rapidity(2)); + if (idxY1 >= 0 && idxY2 >= 0) { + state.hRho2_ALAL_y_AllPt->Fill(idxY1 + 0.5f, idxY2 + 0.5f, wPair); + } + if (cs) { + fillIdx(cs->ALAL_y, idxY1, idxY2, wPair); + } + } + } + // ───────────────────────────────────────────────────────────────────── + + registryOther.fill(HIST("hNSelLambda"), static_cast(nSelLambda)); + registryOther.fill(HIST("hNSelAntiLambda"), static_cast(nSelAntiLambda)); + registryOther.fill(HIST("hNPairs_Lambda_PrimProton"), static_cast(nPairs_Lambda_PrimProton)); + registryOther.fill(HIST("hNPairs_Lambda_PrimAntiProton"), static_cast(nPairs_Lambda_PrimAntiProton)); + registryOther.fill(HIST("hNPairs_AntiLambda_PrimProton"), static_cast(nPairs_AntiLambda_PrimProton)); + registryOther.fill(HIST("hNPairs_AntiLambda_PrimAntiProton"), static_cast(nPairs_AntiLambda_PrimAntiProton)); + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processData, "Process for Data", true); + + // ══════════════════════════════════════════════════════════════════════════ + // MC efficiency maps: Gen* from MC particles, Rec* from the reconstructed selection. + // Gen* use MC-truth pT and y (proton mass for p/pbar, Lambda mass for Lambda/AntiLambda). + // Rec* use RECONSTRUCTED pT and y, i.e. the same variables getEffWeight() queries in processData(). + // The loop is driven by MC collisions (exactly one reconstructed collision required) to avoid + // split-vertex double counting of generated particles. + // ══════════════════════════════════════════════════════════════════════════ + Preslice v0sPerCollision = aod::v0data::collisionId; + Preslice tracksPerCollision = aod::track::collisionId; + + template + static float mcRapidity(TMc const& mc, float mass) + { + return RecoDecay::y(std::array{mc.px(), mc.py(), mc.pz()}, mass); + } + // Generator-level acceptance = same pT range and |y| cut as the reco selection of the species. + bool inLambdaAcc(float pt, float y) const + { + return pt > lambdaV0MinPt.value && pt < lambdaV0MaxPt.value && std::abs(y) < lambdaV0MaxY.value; + } + bool inProtonAcc(float pt, float y) const + { + return pt > pProtonMinPt.value && pt < pProtonMaxPt.value && std::abs(y) < pProtonMaxY.value; + } + + void processMC(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + MyV0sMC const& allV0s, + MyTracksMC const& allTracks, + aod::McParticles const& mcParticles) + { + // ── 1) Event association + selection (soa::Filtered does not apply to SmallGroups) ── + // Exactly one reconstructed collision per MC collision: removes split-vertex double counting. + if (collisions.size() != 1) { + return; + } + auto collision = collisions.begin(); + if (!collision.has_mcCollision() || collision.mcCollisionId() != mcCollision.globalIndex()) { + return; + } + // Same event selection as the Filters of processData(): sel8 + |posZ| cut + if (!collision.sel8() || std::abs(collision.posZ()) >= cPPandV0ZVertexCut.value) { + return; + } + // Slice V0s and tracks to this reconstructed collision + auto V0s = allV0s.sliceBy(v0sPerCollision, collision.globalIndex()); + auto tracks = allTracks.sliceBy(tracksPerCollision, collision.globalIndex()); + + if (collision.numContrib() < 1) { + return; + } + const float cent = getCentrality(collision); // FT0C or FT0M according to cUseFT0C + if (cent < cMinCentrality.value || cent >= cMaxCentrality.value) { + return; + } + // INEL > 0 (same as processData()): at least one charged track in |eta| < 0.8 + { + int nTracksINEL = 0; + for (auto const& trk : tracks) { + if (std::abs(trk.eta()) < 0.8f) { + ++nTracksINEL; + } + } + if (nTracksINEL < 1) { + return; + } + } + + // ── 2) GENERATED: physical primaries of this MC collision (mcParticles is auto-sliced to mcCollision; + // NO V0-daughter veto at generator level) ── + for (auto const& mc : mcParticles) { + if (!mc.isPhysicalPrimary()) { + continue; + } + const int pdg = mc.pdgCode(); + if (pdg == 3122 || pdg == -3122) { + if (!mc.has_daughters()) { + continue; + } + const float pt = mc.pt(); + const float y = mcRapidity(mc, kMassLambda); + if (!inLambdaAcc(pt, y)) { + continue; + } + if (pdg == 3122) { + registryEff.fill(HIST("GenLambda"), cent, pt, y); + } else { + registryEff.fill(HIST("GenAntiLambda"), cent, pt, y); + } + } else if (pdg == 2212 || pdg == -2212) { + const float pt = mc.pt(); + const float y = mcRapidity(mc, kMassProton); + if (!inProtonAcc(pt, y)) { + continue; + } + if (pdg == 2212) { + registryEff.fill(HIST("GenProton"), cent, pt, y); + } else { + registryEff.fill(HIST("GenAntiProton"), cent, pt, y); + } + } + } + + // ══════════════════════════════════════════════════════════════════════ + // BLOCK COPIED FROM processData() — V0 (Lambda/AntiLambda) selection. + // The selection is embedded in processData(), so it is duplicated here with all QA fills removed. + // KEEP IN SYNC with processData() (Step 2 V0 loop + shared-daughter cleaning). + // ══════════════════════════════════════════════════════════════════════ + constexpr float mLambdaPDG = o2::constants::physics::MassLambda; + constexpr float mK0PDG = o2::constants::physics::MassK0Short; + std::unordered_set v0DaughterTrackIds; + std::vector selectedLambdas; + std::vector selectedAntiLambdas; + + for (const auto& v0 : V0s) { + const auto& v0PosDau = v0.posTrack_as(); + const auto& v0NegDau = v0.negTrack_as(); + + if (v0.v0Type() != cV0TypeSelection.value) { + continue; + } + if (v0PosDau.pt() <= cV0DauMinPt.value || v0NegDau.pt() <= cV0DauMinPt.value || + std::abs(v0PosDau.eta()) >= cV0DauMaxEta.value || std::abs(v0NegDau.eta()) >= cV0DauMaxEta.value || + v0PosDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value || + v0NegDau.tpcNClsCrossedRows() <= cV0DauMinTpcCrossedRows.value) { + continue; + } + const bool v0DauPID_AsLambda = + (std::abs(v0PosDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value) && + (std::abs(v0NegDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value); + const bool v0DauPID_AsAntiLambda = + (std::abs(v0PosDau.tpcNSigmaPi()) < lambdaV0DaughterPionTPCNsigma.value) && + (std::abs(v0NegDau.tpcNSigmaPr()) < lambdaV0DaughterProtonTPCNsigma.value); + if (v0DauPID_AsLambda == v0DauPID_AsAntiLambda) { + continue; + } + if (v0DauPID_AsLambda && std::abs(v0.rapidity(1)) > lambdaV0MaxY.value) { + continue; + } + if (v0DauPID_AsAntiLambda && std::abs(v0.rapidity(2)) > lambdaV0MaxY.value) { + continue; + } + if (v0.pt() < lambdaV0MinPt.value || v0.pt() > lambdaV0MaxPt.value) { + continue; + } + const float dcaProton = v0DauPID_AsLambda ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + const float dcaPion = v0DauPID_AsLambda ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); + const float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * mLambdaPDG; + if (!passTopoCuts(v0)) { + continue; + } + if (dcaProton < lambdaV0DaughterProtonMinDCAToPV.value || + dcaPion < lambdaV0DaughterPionMinDCAToPV.value) { + continue; + } + if (ctau > lambdaV0MaxCTau.value) { + continue; + } + if (std::abs(v0.mK0Short() - mK0PDG) <= lambdaV0KShortRejectMassWindow.value) { + continue; + } + const bool selLambda = v0DauPID_AsLambda && (std::abs(v0.mLambda() - mLambdaPDG) < lambdaV0MassWindow.value); + const bool selAntiLambda = v0DauPID_AsAntiLambda && (std::abs(v0.mAntiLambda() - mLambdaPDG) < lambdaV0MassWindow.value); + if (selLambda) { + selectedLambdas.push_back(v0); + v0DaughterTrackIds.insert(v0.posTrackId()); + v0DaughterTrackIds.insert(v0.negTrackId()); + } + if (selAntiLambda) { + selectedAntiLambdas.push_back(v0); + v0DaughterTrackIds.insert(v0.posTrackId()); + v0DaughterTrackIds.insert(v0.negTrackId()); + } + } + // Shared-daughter cleaning (same as processData()) + std::unordered_map posIdCount; + std::unordered_map negIdCount; + for (const auto& v : selectedLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + for (const auto& v : selectedAntiLambdas) { + ++posIdCount[v.posTrackId()]; + ++negIdCount[v.negTrackId()]; + } + std::vector cleanLambdas; + std::vector cleanAntiLambdas; + for (const auto& v : selectedLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanLambdas.push_back(v); + } + } + for (const auto& v : selectedAntiLambdas) { + if (posIdCount[v.posTrackId()] == 1 && negIdCount[v.negTrackId()] == 1) { + cleanAntiLambdas.push_back(v); + } + } + // ══════════════ END OF BLOCK COPIED FROM processData() (V0) ══════════════ + + // ── 4) RECONSTRUCTED Lambda / AntiLambda: true primary (anti)Lambda, filled in RECONSTRUCTED (pT, y) ── + for (const auto& v0 : cleanLambdas) { + const auto& v0PosDau = v0.posTrack_as(); + const auto& v0NegDau = v0.negTrack_as(); + if (!v0.has_mcParticle() || !v0PosDau.has_mcParticle() || !v0NegDau.has_mcParticle()) { + continue; + } + const auto mc = v0.mcParticle_as(); + if (!mc.isPhysicalPrimary() || mc.pdgCode() != 3122) { + continue; + } + registryEff.fill(HIST("RecLambda"), cent, v0.pt(), v0.rapidity(1)); + } + for (const auto& v0 : cleanAntiLambdas) { + const auto& v0PosDau = v0.posTrack_as(); + const auto& v0NegDau = v0.negTrack_as(); + if (!v0.has_mcParticle() || !v0PosDau.has_mcParticle() || !v0NegDau.has_mcParticle()) { + continue; + } + const auto mc = v0.mcParticle_as(); + if (!mc.isPhysicalPrimary() || mc.pdgCode() != -3122) { + continue; + } + registryEff.fill(HIST("RecAntiLambda"), cent, v0.pt(), v0.rapidity(2)); + } + + // ══════════════════════════════════════════════════════════════════════ + // BLOCK COPIED FROM processData() — primary (anti)proton selection (track loop, QA fills removed). + // Reuses passesPrimProtonPid() and passesContaminantVeto(); the remaining cuts are embedded + // in processData() and duplicated here. KEEP IN SYNC with processData(). + // ══════════════════════════════════════════════════════════════════════ + for (auto const& trk : tracks) { + if (!trk.hasTPC()) { + continue; + } + if (trk.tpcNClsCrossedRows() < pProtonTPCMinRows.value) { + continue; + } + if (trk.tpcCrossedRowsOverFindableCls() < pProtonTPCMinRowsOverFindable.value) { + continue; + } + if (trk.tpcChi2NCl() > pProtonTPCMaxChi2PerCluster.value) { + continue; + } + if (trk.itsNCls() < pProtonITSMinClusters.value) { + continue; + } + if (trk.itsChi2NCl() > pProtonITSMaxChi2PerCluster.value) { + continue; + } + if (trk.tpcSignal() < cPPMinTpcSignal.value) { + continue; + } + if (trk.pt() < pProtonMinPt.value || trk.pt() > pProtonMaxPt.value) { + continue; + } + if (std::abs(protonRapidity(trk)) > pProtonMaxY.value) { + continue; + } + if (!((std::abs(trk.dcaXY()) < pProtonMaxDCAxy.value) && (std::abs(trk.dcaZ()) < pProtonMaxDCAz.value))) { + continue; + } + if (!passesPrimProtonPid(trk)) { + continue; + } + if (!passesContaminantVeto(trk)) { + continue; + } + // V0-daughter veto (same set as processData(): built from V0s selected before shared-daughter cleaning) + if (v0DaughterTrackIds.contains(trk.globalIndex())) { + continue; + } + // ══════════════ END OF BLOCK COPIED FROM processData() (protons) ══════════════ + + // ── 3) RECONSTRUCTED protons / antiprotons: true primary p / pbar of this MC collision ── + if (!trk.has_mcParticle()) { + continue; + } + const auto mc = trk.mcParticle_as(); + if (!mc.isPhysicalPrimary()) { + continue; + } + // Fill with RECONSTRUCTED kinematics (same variables as getEffWeight in processData()) + const float yReco = protonRapidity(trk); + if (trk.sign() > 0 && mc.pdgCode() == 2212) { + registryEff.fill(HIST("RecProton"), cent, trk.pt(), yReco); + } else if (trk.sign() < 0 && mc.pdgCode() == -2212) { + registryEff.fill(HIST("RecAntiProton"), cent, trk.pt(), yReco); + } + } + } + PROCESS_SWITCH(LambdaProtonBalanceFunction, processMC, "Efficiency from MC", false); + // ───────────────────────────────────────────────────────────────────── + + // ── Run-level summary printed once at the end of the analysis job ───── + // Called automatically by the O2 framework after all events are processed. + // Prints totals accumulated in runTotalProtonBeforeVeto / runTotalProtonRemovedVeto. + void endOfStream(EndOfStreamContext const&) + { + const int64_t totalBefore = state.runTotalProtonBeforeVeto; + const int64_t totalRemoved = state.runTotalProtonRemovedVeto; + const int64_t totalAfter = totalBefore - totalRemoved; + const double fracRemoved = (totalBefore > 0) + ? static_cast(totalRemoved) / static_cast(totalBefore) + : 0.0; + + LOG(info) << ""; + LOG(info) << "================ Proton Veto QA ================"; + LOG(info) << "Total proton candidates before veto : " << totalBefore; + LOG(info) << "Total proton candidates after veto : " << totalAfter; + LOG(info) << "Total removed by veto : " << totalRemoved; + LOG(info) << Form("Fraction removed : %.6f", fracRemoved); + const double fracRemovedAp = (state.runTotalAntiPBeforeVeto > 0) + ? static_cast(state.runTotalAntiPRemovedVeto) / static_cast(state.runTotalAntiPBeforeVeto) + : 0.0; + LOG(info) << "Total antiproton candidates before veto: " << state.runTotalAntiPBeforeVeto; + LOG(info) << "Total antiproton removed by veto : " << state.runTotalAntiPRemovedVeto; + LOG(info) << Form("Antiproton fraction removed : %.6f", fracRemovedAp); + LOG(info) << "==============================================="; + LOG(info) << ""; + } + // ───────────────────────────────────────────────────────────────────── +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx index 5643f73d107..cb608859637 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaR2Correlation.cxx @@ -16,18 +16,19 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/TriggerAliases.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include #include #include +#include #include #include #include @@ -41,13 +42,15 @@ #include #include +#include +#include #include #include #include -#include -#include +#include #include +#include #include #include #include @@ -64,14 +67,14 @@ namespace o2::aod namespace lambdacollision { DECLARE_SOA_COLUMN(Cent, cent, float); -DECLARE_SOA_COLUMN(Mult, mult, float); +DECLARE_SOA_COLUMN(BField, bField, float); } // namespace lambdacollision DECLARE_SOA_TABLE(LambdaCollisions, "AOD", "LAMBDACOLS", o2::soa::Index<>, lambdacollision::Cent, - lambdacollision::Mult, aod::collision::PosX, aod::collision::PosY, - aod::collision::PosZ); + aod::collision::PosZ, + lambdacollision::BField); using LambdaCollision = LambdaCollisions::iterator; namespace lambdamcgencollision @@ -79,7 +82,6 @@ namespace lambdamcgencollision } DECLARE_SOA_TABLE(LambdaMcGenCollisions, "AOD", "LMCGENCOLS", o2::soa::Index<>, lambdacollision::Cent, - lambdacollision::Mult, o2::aod::mccollision::PosX, o2::aod::mccollision::PosY, o2::aod::mccollision::PosZ); @@ -88,38 +90,36 @@ using LambdaMcGenCollision = LambdaMcGenCollisions::iterator; namespace lambdatrack { DECLARE_SOA_INDEX_COLUMN(LambdaCollision, lambdaCollision); -DECLARE_SOA_COLUMN(Px, px, float); -DECLARE_SOA_COLUMN(Py, py, float); -DECLARE_SOA_COLUMN(Pz, pz, float); DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(Rap, rap, float); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); DECLARE_SOA_COLUMN(Mass, mass, float); DECLARE_SOA_COLUMN(PosTrackId, posTrackId, int64_t); DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int64_t); -DECLARE_SOA_COLUMN(CosPA, cosPA, float); -DECLARE_SOA_COLUMN(DcaDau, dcaDau, float); -DECLARE_SOA_COLUMN(V0Type, v0Type, int8_t); -DECLARE_SOA_COLUMN(V0PrmScd, v0PrmScd, int8_t); +DECLARE_SOA_COLUMN(PosTrackKin, posTrackKin, std::vector); +DECLARE_SOA_COLUMN(NegTrackKin, negTrackKin, std::vector); +DECLARE_SOA_COLUMN(PartType, partType, int8_t); DECLARE_SOA_COLUMN(CorrFact, corrFact, float); } // namespace lambdatrack DECLARE_SOA_TABLE(LambdaTracks, "AOD", "LAMBDATRACKS", o2::soa::Index<>, lambdatrack::LambdaCollisionId, - lambdatrack::Px, - lambdatrack::Py, - lambdatrack::Pz, lambdatrack::Pt, lambdatrack::Eta, lambdatrack::Phi, lambdatrack::Rap, + lambdatrack::Px, + lambdatrack::Py, + lambdatrack::Pz, lambdatrack::Mass, lambdatrack::PosTrackId, lambdatrack::NegTrackId, - lambdatrack::CosPA, - lambdatrack::DcaDau, - lambdatrack::V0Type, - lambdatrack::V0PrmScd, + lambdatrack::PosTrackKin, + lambdatrack::NegTrackKin, + lambdatrack::PartType, lambdatrack::CorrFact); using LambdaTrack = LambdaTracks::iterator; @@ -142,20 +142,17 @@ DECLARE_SOA_INDEX_COLUMN(LambdaMcGenCollision, lambdaMcGenCollision); } DECLARE_SOA_TABLE(LambdaMcGenTracks, "AOD", "LMCGENTRACKS", o2::soa::Index<>, lambdamcgentrack::LambdaMcGenCollisionId, - o2::aod::mcparticle::Px, - o2::aod::mcparticle::Py, - o2::aod::mcparticle::Pz, lambdatrack::Pt, lambdatrack::Eta, lambdatrack::Phi, lambdatrack::Rap, + lambdatrack::Px, + lambdatrack::Py, + lambdatrack::Pz, lambdatrack::Mass, lambdatrack::PosTrackId, lambdatrack::NegTrackId, - lambdatrack::V0Type, - lambdatrack::CosPA, - lambdatrack::DcaDau, - lambdatrack::V0PrmScd, + lambdatrack::PartType, lambdatrack::CorrFact); using LambdaMcGenTrack = LambdaMcGenTracks::iterator; } // namespace o2::aod @@ -166,33 +163,21 @@ enum CollisionLabels { kPassSelCol }; -enum TrackLabels { - kTracksBeforeHasMcParticle = 1, +enum LambdaLabels { + kLambdaBeforeHasMcParticle = 1, kAllV0Tracks, - kV0KShortMassRej, - kNotLambdaNotAntiLambda, - kV0IsBothLambdaAntiLambda, - kNotLambdaAfterSel, - kV0IsLambdaOrAntiLambda, - kPassV0DauTrackSel, kPassV0KinCuts, kPassV0TopoSel, + kPassK0ShortMassRej, + kPassV0DauTrackSel, kAllSelPassed, - kPrimaryLambda, - kSecondaryLambda, - kLambdaDauNotMcParticle, - kLambdaNotPrPiMinus, - kAntiLambdaNotAntiPrPiPlus, - kPassTrueLambdaSel, - kEffCorrPtCent, - kEffCorrPtRapCent, - kNoEffCorr, - kPFCorrPtCent, - kPFCorrPtRapCent, - kNoPFCorr, kGenTotAccLambda, kGenLambdaNoDau, - kGenLambdaToPrPi +}; + +enum EffCorrType { + kEffCorrPtCent = 0, + kEffCorrPtRapCent }; enum CentEstType { @@ -200,11 +185,6 @@ enum CentEstType { kCentFT0C }; -enum RunType { - kRun3 = 0, - kRun2 -}; - enum ParticleType { kLambda = 0, kAntiLambda @@ -213,7 +193,7 @@ enum ParticleType { enum ParticlePairType { kLambdaAntiLambda = 0, kLambdaLambda, - kAntiLambdaAntiLambda + kAntiLambdaAntiLambda, }; enum ShareDauLambda { @@ -231,24 +211,6 @@ enum DMCType { kMC }; -enum CorrHistDim { - OneDimCorr = 1, - TwoDimCorr, - ThreeDimCorr -}; - -enum PrmScdType { - kPrimary = 0, - kSecondary -}; - -enum PrmScdPairType { - kPP = 0, - kPS, - kSP, - kSS -}; - struct LambdaTableProducer { // Table Producers Produces lambdaCollisionTable; @@ -257,28 +219,18 @@ struct LambdaTableProducer { Produces lambdaMCGenTrackTable; // Centrality Axis - ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 50.f, 80.0f, 100.f}, "Variable Centrality Bins"}; + ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Variable Centrality Bins"}; // Collisions Configurable cCentEstimator{"cCentEstimator", 1, "Centrality Estimator : 0-FT0M, 1-FT0C"}; - Configurable cMinZVtx{"cMinZVtx", -7.0, "Min VtxZ cut"}; - Configurable cMaxZVtx{"cMaxZVtx", 7.0, "Max VtxZ cut"}; - Configurable cMinCent{"cMinCent", 0., "Minumum Centrality"}; - Configurable cMaxCent{"cMaxCent", 100.0, "Maximum Centrality"}; + Configurable cZVtxCut{"cZVtxCut", 10.0, "Z-Vtx cut"}; Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; - Configurable cInt7Trig{"cInt7Trig", false, "kINT7 MB Trigger"}; - Configurable cSel7Trig{"cSel7Trig", false, "Sel7 (V0A + V0C) Selection Run2"}; - Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "Trigger Time and Vertex Selection"}; - Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; - Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; - Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; - Configurable cPileupReject{"cPileupReject", false, "Pileup rejection"}; - Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; - Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Good ITS Layers All"}; + Configurable cPileupReject{"cPileupReject", true, "Pileup rejection"}; + Configurable cZVtxTimeDiff{"cZVtxTimeDiff", true, "z-vtx time diff selection"}; + Configurable cIsGoodITSLayers{"cIsGoodITSLayers", true, "Good ITS Layers All"}; // Tracks Configurable cTrackMinPt{"cTrackMinPt", 0.1, "p_{T} minimum"}; - Configurable cTrackMaxPt{"cTrackMaxPt", 999.0, "p_{T} maximum"}; Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 70, "TPC Min Crossed Rows"}; Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 3.0, "TPC NSigma Selection Cut"}; @@ -287,78 +239,87 @@ struct LambdaTableProducer { // V0s Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.02, "Minimum Proton DCAr to PV"}; Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.06, "Minimum Pion DCAr to PV"}; - Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Minimum DCA between V0 daughters"}; - Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1., "Maximum DCA between V0 daughters"}; - Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Minimum DCA V0 to PV"}; - Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 999.0, "Maximum DCA V0 to PV"}; + Configurable cMaxDcaV0Daughters{"cMaxDcaV0Daughters", 1., "Maximum DCA between V0 daughters"}; + Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 0.1, "Maximum DCA V0 to PV"}; Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Minimum V0 radius from PV"}; - Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Maximum V0 radius from PV"}; - Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Minimum ctau"}; Configurable cMaxV0CTau{"cMaxV0CTau", 30.0, "Maximum ctau"}; Configurable cMinV0CosPA{"cMinV0CosPA", 0.995, "Minimum V0 CosPA to PV"}; Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.01, "Reject K0Short Candidates"}; - Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0short Mass Rej Flag"}; - - // V0s kinmatic acceptance - Configurable cV0MassWindow{"cV0MassWindow", 0.007, "V0 Mass Window"}; - Configurable cMinV0Pt{"cMinV0Pt", 0.6, "Minimum V0 pT"}; - Configurable cMaxV0Pt{"cMaxV0Pt", 3.6, "Minimum V0 pT"}; - Configurable cMaxV0Rap{"cMaxV0Rap", 0.5, "|rap| cut"}; - Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Do Eta Analysis"}; - Configurable cV0TypeSelFlag{"cV0TypeSelFlag", true, "V0 Type Selection Flag"}; - Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; + Configurable cArmPodSel{"cArmPodSel", 0.2, "Armentros-Podolanski Selection"}; + + // DCA + Configurable cWeakDecayProcess{"cWeakDecayProcess", 4, "Weak decay process tag for DCA analysis"}; - // V0s MC - Configurable cHasMcFlag{"cHasMcFlag", true, "Has Mc Tag"}; - Configurable cSelectTrueLambda{"cSelectTrueLambda", false, "Select True Lambda"}; - Configurable cSelMCPSV0{"cSelMCPSV0", false, "Select Primary/Secondary V0"}; - Configurable cCheckRecoDauFlag{"cCheckRecoDauFlag", true, "Check for reco daughter PID"}; - Configurable cGenDecayChannel{"cGenDecayChannel", true, "Gen Level Decay Channel Flag"}; + // V0s acceptance + Configurable cLambdaMassWindow{"cLambdaMassWindow", 0.007, "Lambda Mass Window"}; + Configurable cLambdaMinPt{"cLambdaMinPt", 0.8, "Minimum Lambda pT"}; + Configurable cLambdaMaxPt{"cLambdaMaxPt", 3.8, "Maximum Lambda pT"}; + Configurable cLambdaRapCut{"cLambdaRapCut", 0.5, "Lambda |rap| cut"}; + Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; // Efficiency Correction - Configurable cCorrectionFlag{"cCorrectionFlag", false, "Correction Flag"}; + Configurable cGetCorrectionFlag{"cGetCorrectionFlag", false, "Apply correction flag"}; Configurable cGetEffFact{"cGetEffFact", false, "Get Efficiency Factor Flag"}; - Configurable cGetPrimFrac{"cGetPrimFrac", false, "Get Primary Fraction Flag"}; - Configurable cGetMatchEff{"cGetMatchEff", false, "Get Matching Efficiency Flag"}; Configurable cCorrFactHist{"cCorrFactHist", 0, "Efficiency Factor Histogram"}; - Configurable cPrimFracHist{"cPrimFracHist", 0, "Primary Fraction Histogram"}; + + // MC + Configurable cSelPrimaryParticle{"cSelPrimaryParticle", false, "Select primary particle"}; + Configurable cSelTrueParticle{"cSelTrueParticle", false, "Select MC PDG matched particle"}; + + // MC Generator + Configurable cFwdMult{"cFwdMult", 500, "Fwd multiplicity axis config"}; + Configurable cFT0AEtaMin{"cFT0AEtaMin", 3.5, "FT0A Eta Min"}; + Configurable cFT0AEtaMax{"cFT0AEtaMax", 4.9, "FT0A Eta Max"}; + Configurable cFT0CEtaMin{"cFT0CEtaMin", -3.3, "FT0C Eta Min"}; + Configurable cFT0CEtaMax{"cFT0CEtaMax", -2.1, "FT0C Eta Max"}; + Configurable cTPCEtaMin{"cTPCEtaMin", -1., "TPC Eta Min"}; + Configurable cTPCEtaMax{"cTPCEtaMax", 1., "TPC Eta Max"}; // CCDB Configurable cUrlCCDB{"cUrlCCDB", "http://alice-ccdb.cern.ch", "ALICE CCDB URL"}; - Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/LHC22oPass7/LambdaCorrectionFactor", "Path for ccdb-object"}; - Configurable cPathCCDBMatchEff{"cPathCCDBMatchEff", "Users/y/ypatley/LHC22oPass7/ITSTPCMatchEff", "Path for ccdb-object for matching efficiency"}; + Configurable cPathCCDBRecoEff{"cPathCCDBRecoEff", "Users/y/ypatley/LBF/PP/RecoEfficiency", "Path for ccdb-object for reco efficiency"}; + Configurable cPathCCDBMcCent{"cPathCCDBMcCent", "Users/y/ypatley/LBF/PP/McCent/MultCent", "Path for ccdb-object for MC Gen multiplicity centrality calibration"}; + Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; // Initialize CCDB Service - Service ccdb; + Service ccdb{}; + o2::parameters::GRPMagField* grpo = nullptr; // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // initialize corr_factor objects - std::vector> vCorrFactStrings = {{"hEffVsPtCentLambda", "hEffVsPtCentAntiLambda"}, - {"hEffVsPtYCentLambda", "hEffVsPtYCentAntiLambda"}, - {"hEffVsPtEtaCentLambda", "hEffVsPtEtaCentAntiLambda"}}; + // Initialize corr_factor objects + std::vector> vCorrFactStrings = {{"hEffVsPtCentLambda", "hEffVsPtCentAntiLambda"}, {"hEffVsPtYCentLambda", "hEffVsPtYCentAntiLambda"}}; - // initialize corr_factor objects - std::vector> vPrimFracStrings = {{"hPrimFracVsPtCentLambda", "hPrimFracVsPtCentAntiLambda"}, - {"hPrimFracVsPtYCentLambda", "hPrimFracVsPtYCentAntiLambda"}, - {"hPrimFracVsPtEtaCentLambda", "hPrimFracVsPtEtaCentAntiLambda"}}; + // Store correction histograms + struct CorrHist { + std::array vPtCentCorrHists{}; + std::array vPtRapCentCorrHists{}; + } corrHist; // Initialize Global Variables - float cent = 0., mult = 0.; + float cent = 0.; + TList* ccdbObjRecoEff = nullptr; + TList* ccdbObjMcCent = nullptr; + TH1F* hMcFwdMult = nullptr; + static constexpr auto SubDir = std::array{"QA/Lambda/", "QA/AntiLambda/"}; void init(InitContext const&) { + // CCDB // Set CCDB url ccdb->setURL(cUrlCCDB.value); ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(nolaterthan.value); - // initialize axis specifications + // Initialize axis specifications const AxisSpec axisCols(5, 0.5, 5.5, ""); - const AxisSpec axisTrks(30, 0.5, 30.5, ""); + const AxisSpec axisTrks(15, 0.5, 15.5, ""); + const AxisSpec axisFwdMult(cFwdMult, 0, cFwdMult, "N_{ch}^{FWD}"); + const AxisSpec axisEffChecks(3, 0, 3, ""); const AxisSpec axisCent(100, 0, 100, "Centrality(%)"); const AxisSpec axisVarCent(cCentBins, "FT0C%"); - const AxisSpec axisPVMults(1000, 0, 1000, "N_{PV}"); const AxisSpec axisMult(10, 0, 10, "N_{#Lambda}"); const AxisSpec axisVz(220, -11, 11, "V_{z} (cm)"); const AxisSpec axisPID(8000, -4000, 4000, "PdgCode"); @@ -369,39 +330,37 @@ struct LambdaTableProducer { const AxisSpec axisV0Eta(48, -1.2, 1.2, "#eta"); const AxisSpec axisV0Phi(36, 0., TwoPI, "#phi (rad)"); - const AxisSpec axisRadius(2000, 0, 200, "r(cm)"); - const AxisSpec axisCosPA(300, 0.97, 1.0, "cos(#theta_{PA})"); - const AxisSpec axisDcaV0PV(1000, 0., 10., "dca (cm)"); + const AxisSpec axisRadius(5000, 0, 5, "r(cm)"); + const AxisSpec axisCosPA(100, 0.99, 1.0, "cos(#theta_{PA})"); + const AxisSpec axisDcaV0PV(100, 0., 0.1, "dca (cm)"); const AxisSpec axisDcaProngPV(5000, -50., 50., "dca (cm)"); const AxisSpec axisDcaDau(75, 0., 1.5, "Daug DCA (#sigma)"); - const AxisSpec axisCTau(2000, 0, 200, "c#tau (cm)"); - const AxisSpec axisGCTau(2000, 0, 200, "#gammac#tau (cm)"); + const AxisSpec axisCTau(80, 0, 40, "c#tau (cm)"); const AxisSpec axisAlpha(40, -1, 1, "#alpha"); const AxisSpec axisQtarm(40, 0, 0.4, "q_{T}"); - const AxisSpec axisITSTPCTrackPt(100, 0, 10, "p_{T} (GeV/#it{c})"); + const AxisSpec axisDcaLambda(100, 0., cMaxDcaV0ToPV, "Dca_{V^{0}}"); + const AxisSpec axisTrackPt(40, 0, 4, "p_{T} (GeV/#it{c})"); const AxisSpec axisTrackDCA(200, -1, 1, "dca_{XY} (cm)"); - const AxisSpec axisMomPID(80, 0, 4, "p (GeV/#it{c})"); - const AxisSpec axisNsigma(401, -10.025, 10.025, {"n#sigma"}); - const AxisSpec axisdEdx(360, 20, 200, "#frac{dE}{dx}"); + const AxisSpec axisMomPID(80, 0, 4, "p_{T} (GeV/#it{c})"); + const AxisSpec axisTrackNsigma(401, -10.025, 10.025, {"n#sigma"}); + const AxisSpec axisTrackdEdx(360, 20, 200, "#frac{dE}{dx}"); // Create Histograms. // Event histograms histos.add("Events/h1f_collisions_info", "# of Collisions", kTH1F, {axisCols}); histos.add("Events/h1f_collision_posZ", "V_{z}-distribution", kTH1F, {axisVz}); - histos.add("Events/h2f_pvmult_vs_cent", "PVMult Vs Cent", kTH2F, {axisCent, axisPVMults}); + histos.add("Events/h1f_collision_cent", "Centrality distribution", kTH1F, {axisCent}); // QA - histos.add("Tracks/h1f_tracks_info", "# of tracks", kTH1F, {axisTrks}); + histos.add("Tracks/h1f_lambda_info", "Lambda selection info", kTH1F, {axisTrks}); + histos.add("Tracks/h1f_effcorr_info", "Efficiency correction info", kTH1F, {axisEffChecks}); histos.add("Tracks/h2f_armpod_before_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); histos.add("Tracks/h2f_armpod_after_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); - histos.add("Tracks/h1f_lambda_pt_vs_invm", "p_{T} vs M_{#Lambda}", kTH2F, {axisV0Mass, axisV0Pt}); - histos.add("Tracks/h1f_antilambda_pt_vs_invm", "p_{T} vs M_{#bar{#Lambda}}", kTH2F, {axisV0Mass, axisV0Pt}); - histos.add("Tracks/h2f_itstrack_centpt", "h2f_itstrack_centpt", kTH2F, {axisVarCent, axisITSTPCTrackPt}); - histos.add("Tracks/h2f_itstpctrack_centpt", "h2f_itstpctrack_centpt", kTH2F, {axisVarCent, axisITSTPCTrackPt}); // QA Lambda + histos.add("QA/Lambda/h3f_centmasspt", "Invariant Mass", kTH3F, {axisCent, axisV0Pt, axisV0Mass}); histos.add("QA/Lambda/h2f_qt_vs_alpha", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); histos.add("QA/Lambda/h1f_dca_V0_daughters", "DCA between V0 daughters", kTH1F, {axisDcaDau}); histos.add("QA/Lambda/h1f_dca_pos_to_PV", "DCA positive prong to PV", kTH1F, {axisDcaProngPV}); @@ -410,45 +369,24 @@ struct LambdaTableProducer { histos.add("QA/Lambda/h1f_V0_cospa", "cos(#theta_{PA})", kTH1F, {axisCosPA}); histos.add("QA/Lambda/h1f_V0_radius", "V_{0} Decay Radius in XY plane", kTH1F, {axisRadius}); histos.add("QA/Lambda/h1f_V0_ctau", "V_{0} c#tau", kTH1F, {axisCTau}); - histos.add("QA/Lambda/h1f_V0_gctau", "V_{0} #gammac#tau", kTH1F, {axisGCTau}); - - histos.add("QA/Lambda/h1f_pos_prong_pt", "Pos-Prong p_{T}", kTH1F, {axisTrackPt}); - histos.add("QA/Lambda/h1f_neg_prong_pt", "Neg-Prong p_{T}", kTH1F, {axisTrackPt}); - histos.add("QA/Lambda/h1f_pos_prong_eta", "Pos-Prong #eta-distribution", kTH1F, {axisV0Eta}); - histos.add("QA/Lambda/h1f_neg_prong_eta", "Neg-Prong #eta-distribution", kTH1F, {axisV0Eta}); - histos.add("QA/Lambda/h1f_pos_prong_phi", "Pos-Prong #phi-distribution", kTH1F, {axisV0Phi}); - histos.add("QA/Lambda/h1f_neg_prong_phi", "Neg-Prong #phi-distribution", kTH1F, {axisV0Phi}); - histos.add("QA/Lambda/h2f_pos_prong_xisqperclustertpc", "#chi^{2}/cluster TPC", kTH2F, {axisTrackPt, {200, 0, 50}}); - histos.add("QA/Lambda/h2f_pos_prong_xisqperclusterits", "#chi^{2}/cluster ITS", kTH2F, {axisTrackPt, {200, 0, 50}}); - histos.add("QA/Lambda/h2f_neg_prong_xisqperclustertpc", "#chi^{2}/cluster TPC", kTH2F, {axisTrackPt, {200, 0, 50}}); - histos.add("QA/Lambda/h2f_neg_prong_xisqperclusterits", "#chi^{2}/cluster ITS", kTH2F, {axisTrackPt, {200, 0, 50}}); - histos.add("QA/Lambda/h2f_pos_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); histos.add("QA/Lambda/h2f_neg_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); - histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC Signal Pos-Prong", kTH2F, {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC Signal Neg-Prong", kTH2F, {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisNsigma}); - - // Kinematic Histograms - histos.add("McRec/Lambda/hPt", "Transverse Momentum", kTH1F, {axisV0Pt}); - histos.add("McRec/Lambda/hEta", "Pseudorapidity", kTH1F, {axisV0Eta}); - histos.add("McRec/Lambda/hRap", "Rapidity", kTH1F, {axisV0Rap}); - histos.add("McRec/Lambda/hPhi", "Azimuthal Angle", kTH1F, {axisV0Phi}); + histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC Signal Pos-Prong", kTH2F, {axisMomPID, axisTrackdEdx}); + histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC Signal Neg-Prong", kTH2F, {axisMomPID, axisTrackdEdx}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisTrackNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisTrackNsigma}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisTrackNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisTrackNsigma}); - // QA Anti-Lambda - histos.addClone("QA/Lambda/", "QA/AntiLambda/"); - histos.addClone("McRec/Lambda/", "McRec/AntiLambda/"); + // Dca analysis histogram + histos.add("QA/Lambda/DCA/h3f_Dca_vs_pT_cent", "DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); // MC Generated Histograms - if (doprocessMCRun3 || doprocessMCRun2 || doprocessMCRecoRun3 || doprocessMCRecoRun2) { - // McReco Histos - histos.add("Tracks/h2f_tracks_pid_before_sel", "PIDs", kTH2F, {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_tracks_pid_after_sel", "PIDs", kTH2F, {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_primary_lambda_mothers_pdg", "PIDs", kTH2F, {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_secondary_lambda_mothers_pdg", "PIDs", kTH2F, {axisPID, axisV0Pt}); + if (doprocessMCRecoGen || doprocessMCReco) { + // DCA analysis + histos.add("QA/Lambda/DCA/h3f_Prm_Dca_vs_pT_cent", "Primary DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h3f_Scd_Dca_vs_pT_cent", "Weak Decay DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); + histos.add("QA/Lambda/DCA/h3f_Mat_Dca_vs_pT_cent", "Material DCA", kTH3F, {axisCent, axisV0Pt, axisDcaLambda}); // McGen Histos histos.add("McGen/h1f_collision_recgen", "# of Reco Collision Associated to One Mc Generator Collision", kTH1F, {axisMult}); @@ -458,504 +396,354 @@ struct LambdaTableProducer { histos.add("McGen/h1f_lambda_daughter_PDG", "PDG Daughters", kTH1F, {axisPID}); histos.add("McGen/h1f_antilambda_daughter_PDG", "PDG Daughters", kTH1F, {axisPID}); - histos.addClone("McRec/", "McGen/"); - - histos.add("McGen/Lambda/Proton/hPt", "Proton p_{T}", kTH1F, {axisTrackPt}); - histos.add("McGen/Lambda/Proton/hEta", "Proton #eta", kTH1F, {axisV0Eta}); - histos.add("McGen/Lambda/Proton/hRap", "Proton y", kTH1F, {axisV0Rap}); - histos.add("McGen/Lambda/Proton/hPhi", "Proton #phi", kTH1F, {axisV0Phi}); - - histos.addClone("McGen/Lambda/Proton/", "McGen/Lambda/Pion/"); - histos.addClone("McGen/Lambda/Proton/", "McGen/AntiLambda/Proton/"); - histos.addClone("McGen/Lambda/Pion/", "McGen/AntiLambda/Pion/"); - // set bin lables specific to MC histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotColBeforeHasMcCollision, "kTotColBeforeHasMcCollision"); histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kTracksBeforeHasMcParticle, "kTracksBeforeHasMcParticle"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPrimaryLambda, "kPrimaryLambda"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kSecondaryLambda, "kSecondaryLambda"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kLambdaDauNotMcParticle, "kLambdaDauNotMcParticle"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kLambdaNotPrPiMinus, "kLambdaNotPrPiMinus"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAntiLambdaNotAntiPrPiPlus, "kAntiLambdaNotAntiPrPiPlus"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassTrueLambdaSel, "kPassTrueLambdaSel"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenTotAccLambda, "kGenTotAccLambda"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenLambdaNoDau, "kGenLambdaNoDau"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenLambdaToPrPi, "kGenLambdaToPrPi"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kLambdaBeforeHasMcParticle, "kLambdaBeforeHasMcParticle"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kGenTotAccLambda, "kGenTotAccLambda"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kGenLambdaNoDau, "kGenLambdaNoDau"); } - // set bin labels + // Clone histograms + // QA Anti-Lambda + histos.addClone("QA/Lambda/", "QA/AntiLambda/"); + + // Set bin labels histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAllV0Tracks, "kAllV0Tracks"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0KShortMassRej, "kV0KShortMassRej"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNotLambdaNotAntiLambda, "kNotLambdaNotAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0IsBothLambdaAntiLambda, "kV0IsBothLambdaAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNotLambdaAfterSel, "kNotLambdaAfterSel"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kV0IsLambdaOrAntiLambda, "kV0IsLambdaOrAntiLambda"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0DauTrackSel, "kPassV0DauTrackSel"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0KinCuts, "kPassV0KinCuts"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPassV0TopoSel, "kPassV0TopoSel"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAllSelPassed, "kAllSelPassed"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kEffCorrPtCent, "kEffCorrPtCent"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kEffCorrPtRapCent, "kEffCorrPtRapCent"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNoEffCorr, "kNoEffCorr"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtCent, "kPFCorrPtCent"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kPFCorrPtRapCent, "kPFCorrPtRapCent"); - histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kNoPFCorr, "kNoPFCorr"); - } + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kAllV0Tracks, "kAllV0Tracks"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassK0ShortMassRej, "kPassK0ShortMassRej"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassV0DauTrackSel, "kPassV0DauTrackSel"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassV0KinCuts, "kPassV0KinCuts"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kPassV0TopoSel, "kPassV0TopoSel"); + histos.get(HIST("Tracks/h1f_lambda_info"))->GetXaxis()->SetBinLabel(LambdaLabels::kAllSelPassed, "kAllSelPassed"); + + // MC generator histograms + if (doprocessMCGen) { + histos.add("Events/h1f_fwdmult", "Fwd multiplicity", kTH1F, {axisFwdMult}); - template - bool selCollision(C const& col) - { - // VtxZ Selection - if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { - return false; + // MC Gen Mult-Cent Calibration + ccdbObjMcCent = ccdb->getForTimeStamp(cPathCCDBMcCent.value, nolaterthan.value); + hMcFwdMult = dynamic_cast(ccdbObjMcCent->FindObject("hFwdMult")); } - if constexpr (run == kRun3) { // Run3 Min-Bias Trigger - // select centrality estimator - if (cCentEstimator == kCentFT0M) { - cent = col.centFT0M(); - } else if (cCentEstimator == kCentFT0C) { - cent = col.centFT0C(); - } - if (cSel8Trig && !col.sel8()) { - return false; - } - } else { // Run2 Min-Bias Trigger - cent = col.centRun2V0M(); - if (cInt7Trig && !col.alias_bit(kINT7)) { - return false; + // Load correction factor + if (cGetCorrectionFlag) { + // Get CCDB object + ccdbObjRecoEff = ccdb->getForTimeStamp(cPathCCDBRecoEff.value, nolaterthan.value); + + // Load reco eff corrections + loadRecoEfficiencyHistograms(); + } + } + + template + void getCorrFactHists(T& vhists, std::vector const& strings) + { + using HistPtr = typename T::value_type; + for (size_t i = 0; i < vhists.size(); ++i) { + auto* obj = ccdbObjRecoEff->FindObject(strings[i].c_str()); + if (!obj) { + LOGF(fatal, "CCDB object %s not found!", strings[i].c_str()); + continue; } - if (cSel7Trig && !col.sel7()) { - return false; + vhists[i] = dynamic_cast(obj->Clone()); + if (!vhists[i]) { + LOGF(fatal, "CCDB object %s could not be cast!", strings[i].c_str()); } } + } - if (cent <= cMinCent || cent >= cMaxCent) { // select centrality percentile class - return false; + // Load reco efficiency histograms + void loadRecoEfficiencyHistograms() + { + if (cCorrFactHist == kEffCorrPtCent) { + getCorrFactHists(corrHist.vPtCentCorrHists, vCorrFactStrings[kEffCorrPtCent]); + } else if (cCorrFactHist == kEffCorrPtRapCent) { + getCorrFactHists(corrHist.vPtRapCentCorrHists, vCorrFactStrings[kEffCorrPtRapCent]); } + } - if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { - return false; + // Get magnetic field + float getMagneticField(int64_t const& timestamp) + { + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 1; + } } + auto field = std::lround(5.f * grpo->getL3Current() / 30000.f); + return 0.1 * field; + } - if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + template + bool selCollision(C const& col) + { + // Vz Selection + if (std::abs(col.posZ()) >= cZVtxCut) { return false; } - if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + // Run 3 Min-Bias Trigger + if (cSel8Trig && !col.sel8()) { return false; } - if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { - return false; + // Centrality estimator + if (cCentEstimator == kCentFT0M) { + cent = col.centFT0M(); + } else if (cCentEstimator == kCentFT0C) { + cent = col.centFT0C(); } + // Pileup rejection if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } + // Zvtx from FT0 time difference if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } + // Hit all layers in ITS if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { return false; } - // Set Multiplicity - mult = col.multTPC(); - return true; } - // Kinematic Selection - bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) - { - if (pt <= ptMin || pt >= ptMax || rap >= rapMax) { - return false; - } - - return true; - } - - // Track Selection - template - bool selTrack(T const& track) - { - if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, cTrackMaxPt, cTrackEtaCut)) { - return false; - } - - if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { - return false; - } - - return true; - } - - // Daughter Track Selection + // Ambiguous track rejection template - bool selDaughterTracks(V const& v0, T const&, ParticleType const& v0Type) + bool hasAmbiguousDaughters(V const& v0, T const&) { auto posTrack = v0.template posTrack_as(); auto negTrack = v0.template negTrack_as(); - if (!selTrack(posTrack) || !selTrack(negTrack)) { - return false; - } + auto posTrackCompCols = posTrack.compatibleCollIds(); + auto negTrackCompCols = negTrack.compatibleCollIds(); - // Apply DCA Selection on Daughter Tracks Based on Lambda/AntiLambda daughters - float dcaProton = 0., dcaPion = 0.; - if (v0Type == kLambda) { - dcaProton = std::abs(v0.dcapostopv()); - dcaPion = std::abs(v0.dcanegtopv()); - } else if (v0Type == kAntiLambda) { - dcaPion = std::abs(v0.dcapostopv()); - dcaProton = std::abs(v0.dcanegtopv()); + // Check if daughter tracks belongs to more than one collision (Ambiguous Tracks) + if (posTrackCompCols.size() > 1 || negTrackCompCols.size() > 1) { + return true; } - if (dcaProton < cMinDcaProtonToPV || dcaPion < cMinDcaPionToPV) { - return false; + // Check if compatible collision index matches the track collision index + if (((posTrackCompCols.size() != 0) && (posTrackCompCols[0] != posTrack.collisionId())) || + ((negTrackCompCols.size() != 0) && (negTrackCompCols[0] != negTrack.collisionId()))) { + return true; } - return true; + // Pass as not ambiguous + return false; } - template - bool topoCutSelection(C const& col, V const& v0, T const&) + template + bool selLambdaDauTracks(V const& v0, T const& postrack, T const& negtrack, ParticleType& partType) { - // DCA - if (v0.dcaV0daughters() <= cMinV0DcaDaughters || v0.dcaV0daughters() >= cMaxV0DcaDaughters) { + // Kinematic selection + if (postrack.pt() <= cTrackMinPt || negtrack.pt() <= cTrackMinPt || std::abs(postrack.eta()) >= cTrackEtaCut || std::abs(negtrack.eta()) >= cTrackEtaCut || postrack.tpcNClsCrossedRows() <= cMinTpcCrossedRows || negtrack.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { return false; } - if (v0.dcav0topv() <= cMinDcaV0ToPV || v0.dcav0topv() >= cMaxDcaV0ToPV) { + // Apply DCA Selection on Daughter Tracks Based on Lambda/AntiLambda daughters + if (partType == kLambda) { + if (std::abs(v0.dcapostopv()) <= cMinDcaProtonToPV || std::abs(v0.dcanegtopv()) <= cMinDcaPionToPV) { + return false; + } + } else if (partType == kAntiLambda) { + if (std::abs(v0.dcapostopv()) <= cMinDcaPionToPV || std::abs(v0.dcanegtopv()) <= cMinDcaProtonToPV) { + return false; + } + } else { return false; } - if (v0.v0radius() <= cMinV0TransRadius || v0.v0radius() >= cMaxV0TransRadius) { - return false; - } + // Daughter track PID [Dau1 = PosTrack, Dau2 = NegTrack] + float tpcNSigmaDau1 = 0., tpcNSigmaDau2 = 0.; - // ctau - float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; - if (ctau <= cMinV0CTau || ctau >= cMaxV0CTau) { + if (partType == kLambda) { + tpcNSigmaDau1 = postrack.tpcNSigmaPr(); + tpcNSigmaDau2 = negtrack.tpcNSigmaPi(); + } else if (partType == kAntiLambda) { + tpcNSigmaDau1 = postrack.tpcNSigmaPi(); + tpcNSigmaDau2 = negtrack.tpcNSigmaPr(); + } else { + LOGF(fatal, "Particle call not Lambda !!!"); return false; } - // cosine of pointing angle - if (v0.v0cosPA() <= cMinV0CosPA) { + if (std::abs(tpcNSigmaDau1) >= cTpcNsigmaCut || std::abs(tpcNSigmaDau2) >= cTpcNsigmaCut) { return false; } - // all selection criterion passed (Return True) return true; } - template - bool selLambdaDauWithTpcPid(T const& postrack, T const& negtrack) - { - bool returnFlag = false; - float tpcNSigmaPr = 0., tpcNSigmaPi = 0.; - - switch (part) { - // postrack = Proton, negtrack = Pion - case kLambda: - tpcNSigmaPr = postrack.tpcNSigmaPr(); - tpcNSigmaPi = negtrack.tpcNSigmaPi(); - break; - - // negtrack = Proton, postrack = Pion - case kAntiLambda: - tpcNSigmaPr = negtrack.tpcNSigmaPr(); - tpcNSigmaPi = postrack.tpcNSigmaPi(); - break; - } - - if (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut) { - returnFlag = true; - } - - return returnFlag; - } - - template - bool selLambdaMassWindow(V const& v0, T const&, ParticleType& v0type) + template + bool selLambda(C const& collision, V const& v0, T const& tracks, ParticleType& partType) { - // Kshort mass rejection hypothesis - if (cKshortRejFlag && (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kV0KShortMassRej); - return false; - } - - // initialize daughter tracks + // Initialize daughter tracks auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - // initialize selection flags - bool lambdaFlag = false, antiLambdaFlag = false; - - // get v0 track as lambda - if ((std::abs(v0.mLambda() - MassLambda0) <= cV0MassWindow) && (selLambdaDauWithTpcPid(postrack, negtrack))) { - lambdaFlag = true; - v0type = kLambda; - } - - // get v0 track as anti-lambda - if (((std::abs(v0.mAntiLambda() - MassLambda0) <= cV0MassWindow)) && (selLambdaDauWithTpcPid(postrack, negtrack))) { - antiLambdaFlag = true; - v0type = kAntiLambda; - } - - if (!lambdaFlag && !antiLambdaFlag) { // neither Lambda nor Anti-Lambda - histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaNotAntiLambda); - return false; - } else if (lambdaFlag && antiLambdaFlag) { // check if the track is identified as lambda and anti-lambda both (DISCARD THIS TRACK) - histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsBothLambdaAntiLambda); + // Kinematic selections + if (v0.pt() <= cLambdaMinPt || v0.pt() >= cLambdaMaxPt || std::abs(v0.yLambda()) >= cLambdaRapCut) { return false; } - if (lambdaFlag || antiLambdaFlag) { - return true; - } - - histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaAfterSel); + histos.fill(HIST("Tracks/h1f_lambda_info"), kPassV0KinCuts); - return false; - } + // Decay length + float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassLambda0; - template - bool selV0Particle(C const& col, V const& v0, T const& tracks, ParticleType& v0Type) - { - // Apply Lambda Mass Hypothesis - if (!selLambdaMassWindow(v0, tracks, v0Type)) { + // Topological selections + if (v0.dcaV0daughters() >= cMaxDcaV0Daughters || v0.dcav0topv() >= cMaxDcaV0ToPV || v0.v0radius() <= cMinV0TransRadius || v0.v0Type() != cV0TypeSelection || ctauLambda >= cMaxV0CTau || v0.v0cosPA() <= cMinV0CosPA) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsLambdaOrAntiLambda); + histos.fill(HIST("Tracks/h1f_lambda_info"), kPassV0TopoSel); - // Apply Daughter Track Selection - if (!selDaughterTracks(v0, tracks, v0Type)) { + // K0s mass rejection + if (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0DauTrackSel); + histos.fill(HIST("Tracks/h1f_lambda_info"), kPassK0ShortMassRej); + + // Select Lambda / AntiLambda based on Armenteros-Podolanski + bool lambdaFlag = false, antiLambdaFlag = false; + float mass = 0.; - // Apply Kinematic Selection - float rap = 0.; - if (!cDoEtaAnalysis) { - rap = std::abs(v0.yLambda()); + if (v0.qtarm() < cArmPodSel * std::abs(v0.alpha()) && v0.alpha() > 0) { + lambdaFlag = true; + partType = kLambda; + mass = v0.mLambda(); } else { - rap = std::abs(v0.eta()); + antiLambdaFlag = true; + partType = kAntiLambda; + mass = v0.mAntiLambda(); } - if (!kinCutSelection(v0.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + // Select Lambda daughters + if (!selLambdaDauTracks(v0, postrack, negtrack, partType)) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0KinCuts); + histos.fill(HIST("Tracks/h1f_lambda_info"), kPassV0DauTrackSel); - // Apply Topological Selection - if (!topoCutSelection(col, v0, tracks)) { + // Remove lambda with ambiguous daughters + if (cRemoveAmbiguousTracks && hasAmbiguousDaughters(v0, tracks)) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0TopoSel); - - // All Selection Criterion Passed - return true; - } - - template - bool hasAmbiguousDaughters(V const& v0, T const&) - { - auto posTrack = v0.template posTrack_as(); - auto negTrack = v0.template negTrack_as(); - - auto posTrackCompCols = posTrack.compatibleCollIds(); - auto negTrackCompCols = negTrack.compatibleCollIds(); - - // Check if daughter tracks belongs to more than one collision (Ambiguous Tracks) - if (posTrackCompCols.size() > 1 || negTrackCompCols.size() > 1) { - return true; + // Fill invariant mass histograms after all selection + if (lambdaFlag) { + histos.fill(HIST("QA/Lambda/h3f_centmasspt"), cent, v0.pt(), mass); + } else { + histos.fill(HIST("QA/AntiLambda/h3f_centmasspt"), cent, v0.pt(), mass); } - // Check if compatible collision index matches the track collision index - if (((posTrackCompCols.size() != 0) && (posTrackCompCols[0] != posTrack.collisionId())) || - ((negTrackCompCols.size() != 0) && (negTrackCompCols[0] != negTrack.collisionId()))) { - return true; + // Mass window selection + if ((lambdaFlag && std::abs(v0.mLambda() - MassLambda0) >= cLambdaMassWindow) || (antiLambdaFlag && std::abs(v0.mAntiLambda() - MassLambda0) >= cLambdaMassWindow)) { + return false; } - // Pass as not ambiguous - return false; + // All Selection Criterion Passed + return true; } - template - PrmScdType isPrimaryV0(V const& v0) + // Correction factors + template + float getCorrectionFactors(V const& v, float const& rap) { - auto mcpart = v0.template mcParticle_as(); + // Initialize efficiency factor + float effCorrFact = 1.; - // check for secondary lambda - if (!mcpart.isPhysicalPrimary()) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kSecondaryLambda); - return kSecondary; + // Get Efficiency Factor + if (cGetEffFact) { + if (cCorrFactHist == kEffCorrPtCent) { + histos.fill(HIST("Tracks/h1f_effcorr_info"), kEffCorrPtCent); + effCorrFact = corrHist.vPtCentCorrHists[part]->GetBinContent(corrHist.vPtCentCorrHists[part]->FindBin(cent, v.pt())); + } else if (cCorrFactHist == kEffCorrPtRapCent) { + histos.fill(HIST("Tracks/h1f_effcorr_info"), kEffCorrPtRapCent); + effCorrFact = corrHist.vPtRapCentCorrHists[part]->GetBinContent(corrHist.vPtRapCentCorrHists[part]->FindBin(cent, v.pt(), rap)); + } else { + LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); + } } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPrimaryLambda); - return kPrimary; + // Return correction factor + return effCorrFact; } - template - bool selTrueMcRecLambda(V const& v0, T const&) + // MC generator multiplicity estimator and MB event selection + template + bool multEstimator(P const& particles, int& fwdMult) { - auto mcpart = v0.template mcParticle_as(); + int multFT0A = 0, multFT0C = 0, multTPC = 0; - // check if Lambda/AntiLambda - if (std::abs(mcpart.pdgCode()) != kLambda0) { - return false; - } - - // Check for daughters - if (cCheckRecoDauFlag) { - auto postrack = v0.template posTrack_as(); - auto negtrack = v0.template negTrack_as(); - - // check if the daughters have corresponding mcparticle - if (!postrack.has_mcParticle() || !negtrack.has_mcParticle()) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kLambdaDauNotMcParticle); - return false; + // Select charged particles + for (auto const& particle : particles) { + // Primary + if (!particle.isPhysicalPrimary()) { + continue; } - auto mcpostrack = postrack.template mcParticle_as(); - auto mcnegtrack = negtrack.template mcParticle_as(); - - if (mcpart.pdgCode() == kLambda0) { - if (mcpostrack.pdgCode() != kProton || mcnegtrack.pdgCode() != kPiMinus) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kLambdaNotPrPiMinus); - return false; - } - } else if (mcpart.pdgCode() == kLambda0Bar) { - if (mcpostrack.pdgCode() != kPiPlus || mcnegtrack.pdgCode() != kProtonBar) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kAntiLambdaNotAntiPrPiPlus); - return false; - } + // FT0C + if (particle.eta() > cFT0CEtaMin && particle.eta() < cFT0CEtaMax) { + ++multFT0C; } - } - - return true; - } - - template - float getCorrectionFactors(V const& v0, T const&) - { - // Check for efficiency correction flag - if (!cCorrectionFlag) { - return 1.; - } - - // Get from CCDB - auto ccdbObj = ccdb->getForTimeStamp(cPathCCDB.value, -1); - // Check CCDB Object - if (!ccdbObj) { - LOGF(warning, "CCDB OBJECT NOT FOUND"); - return 1.; - } - - // initialize efficiency factor and primary fraction values - float effCorrFact = 1., primFrac = 1., matchEffFact = 1.; - float rap = (cDoEtaAnalysis) ? v0.eta() : v0.yLambda(); - - // Get Efficiency Factor - if (cGetEffFact) { - TObject* objEff = reinterpret_cast(ccdbObj->FindObject(Form("%s", vCorrFactStrings[cCorrFactHist][part].c_str()))); - TH1F* histEff = reinterpret_cast(objEff->Clone()); - if (histEff->GetDimension() == TwoDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtCent); - effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt())); - } else if (histEff->GetDimension() == ThreeDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtRapCent); - effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt(), rap)); - } else { - histos.fill(HIST("Tracks/h1f_tracks_info"), kNoEffCorr); - LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); - effCorrFact = 1.; + // FT0A + if (particle.eta() > cFT0AEtaMin && particle.eta() < cFT0AEtaMax) { + ++multFT0A; } - delete histEff; - } - // Get Primary Fraction - // (The dimension of this could be different than efficiency because of large errors !!!) - if (cGetPrimFrac) { - TObject* objPrm = reinterpret_cast(ccdbObj->FindObject(Form("%s", vPrimFracStrings[cPrimFracHist][part].c_str()))); - TH1F* histPrm = reinterpret_cast(objPrm->Clone()); - if (histPrm->GetDimension() == TwoDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kPFCorrPtCent); - primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt())); - } else if (histPrm->GetDimension() == ThreeDimCorr) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kPFCorrPtRapCent); - primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt(), rap)); - } else { - histos.fill(HIST("Tracks/h1f_tracks_info"), kNoPFCorr); - LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); - primFrac = 1.; + // Central barrel (ITS + TPC) + if (particle.eta() > cTPCEtaMin && particle.eta() < cTPCEtaMax) { + ++multTPC; } - delete histPrm; } - // Get Matching Efficiency Correction - if (cGetMatchEff) { - auto posTrack = v0.template posTrack_as(); - auto negTrack = v0.template negTrack_as(); - auto ccdbObjMatchEff = ccdb->getForTimeStamp(cPathCCDBMatchEff.value, -1); - TObject* objMatchEff = reinterpret_cast(ccdbObjMatchEff->FindObject("hITSTPCMatchingEfficiency")); - TH1F* histMatchEff = reinterpret_cast(objMatchEff->Clone()); - float posTrackMatchEff = histMatchEff->GetBinContent(histMatchEff->FindBin(cent, posTrack.pt())); - float negTrackMatchEff = histMatchEff->GetBinContent(histMatchEff->FindBin(cent, negTrack.pt())); - matchEffFact = posTrackMatchEff * negTrackMatchEff; - delete histMatchEff; + int multFT0M = multFT0A + multFT0C; + fwdMult = multFT0M; + + // Minimum bias trigger (1 track in A side, 1 track in C side, 1 track in central barrel) + if (multFT0A > 0 && multFT0C > 0 && multTPC > 0) { + return true; } - return primFrac * effCorrFact * matchEffFact; + return false; } - template - void fillLambdaMothers(V const& v0, T const&, PrmScdType const& ps) + // MC generator centrality + float getMcCentrality(int mult) { - auto mcpart = v0.template mcParticle_as(); - auto lambdaMothers = mcpart.template mothers_as(); - if (ps == kPrimary) { - histos.fill(HIST("Tracks/h2f_primary_lambda_mothers_pdg"), lambdaMothers[0].pdgCode(), v0.pt()); - } else { - histos.fill(HIST("Tracks/h2f_secondary_lambda_mothers_pdg"), lambdaMothers[0].pdgCode(), v0.pt()); - } + float totalArea = hMcFwdMult->Integral(); + float area = 0.; + int nx = hMcFwdMult->GetNbinsX(); + int bin = hMcFwdMult->GetXaxis()->FindBin(mult + 0.5); + for (int i = bin; i <= nx; ++i) { + area += hMcFwdMult->GetBinContent(i); + } + return (area / totalArea) * 100.; } + // Lambda QA template void fillLambdaQAHistos(C const& col, V const& v0, T const&) { - static constexpr std::string_view SubDir[] = {"QA/Lambda/", "QA/AntiLambda/"}; - - // daugthers + // Daugthers auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - float mass = 0.; - if constexpr (part == kLambda) { - mass = v0.mLambda(); - } else { - mass = v0.mAntiLambda(); - } - - // ctau - float e = RecoDecay::e(v0.px(), v0.py(), v0.pz(), mass); - float gamma = e / mass; + // Decay length float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; - float gctau = ctau * gamma; histos.fill(HIST(SubDir[part]) + HIST("h2f_qt_vs_alpha"), v0.alpha(), v0.qtarm()); histos.fill(HIST(SubDir[part]) + HIST("h1f_dca_V0_daughters"), v0.dcaV0daughters()); @@ -965,19 +753,6 @@ struct LambdaTableProducer { histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_cospa"), v0.v0cosPA()); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_radius"), v0.v0radius()); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_ctau"), ctau); - histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_gctau"), gctau); - - histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_pt"), postrack.pt()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_eta"), postrack.eta()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_phi"), postrack.phi()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_xisqperclusterits"), postrack.pt(), postrack.itsChi2NCl()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_xisqperclustertpc"), postrack.pt(), postrack.tpcChi2NCl()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_pt"), negtrack.pt()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_eta"), negtrack.eta()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_phi"), negtrack.phi()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_xisqperclusterits"), negtrack.pt(), negtrack.itsChi2NCl()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_xisqperclustertpc"), negtrack.pt(), negtrack.tpcChi2NCl()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dcaXY_vs_pt"), postrack.pt(), postrack.dcaXY()); histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dcaXY_vs_pt"), negtrack.pt(), negtrack.dcaXY()); histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dEdx_vs_p"), postrack.tpcInnerParam(), postrack.tpcSignal()); @@ -988,49 +763,37 @@ struct LambdaTableProducer { histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pi_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcNSigmaPi()); } - // Fill Lambda Kinematic Histograms - template - void fillKinematicHists(float const& pt, float const& eta, float const& y, float const& phi) + // Dca analysis + template + void getDcaHist(T const& track, float const& dca) { - static constexpr std::string_view SubDirRG[] = {"McRec/", "McGen/"}; - static constexpr std::string_view SubDirPart[] = {"Lambda/", "AntiLambda/"}; + // Fill histograms + histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Dca_vs_pT_cent"), cent, track.pt(), dca); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPt"), pt); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), eta); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hRap"), y); - histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), phi); - } - - template - void getMatchEffHist(T const& tracks) - { - for (auto const& track : tracks) { - if constexpr (dmc == kMC) { // Check corresponding MC particle - if (!track.has_mcParticle()) { - continue; + if constexpr (dmc == kMC) { + auto mcpart = track.mcParticle(); + if (mcpart.isPhysicalPrimary()) { + histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Prm_Dca_vs_pT_cent"), cent, track.pt(), dca); + } else { + if (mcpart.getProcess() == cWeakDecayProcess) { // Weak decay + histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Scd_Dca_vs_pT_cent"), cent, track.pt(), dca); + } else { // Material + histos.fill(HIST(SubDir[part]) + HIST("DCA/h3f_Mat_Dca_vs_pT_cent"), cent, track.pt(), dca); } } - // ITS only track - if (track.pt() > cTrackMinPt && std::abs(track.eta()) < cTrackEtaCut && track.hasITS() && track.isQualityTrackITS()) { - histos.fill(HIST("Tracks/h2f_itstrack_centpt"), cent, track.pt()); - } - // ITS+TPC track - if (track.pt() > cTrackMinPt && std::abs(track.eta()) < cTrackEtaCut && track.hasITS() && track.hasTPC() && track.isQualityTrackITS() && track.isQualityTrackTPC()) { - histos.fill(HIST("Tracks/h2f_itstpctrack_centpt"), cent, track.pt()); - } } } // Reconstructed Level Tables - template - void fillLambdaRecoTables(C const& collision, V const& v0tracks, T const& tracks) + template + void fillLambdaRecoTables(C const& collision, B const&, V const& v0tracks, T const& tracks) { // Total Collisions histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); // Select Collision (Only for Data... McRec has been selected already !!!) if constexpr (dmc == kData) { - if (!selCollision(collision)) { + if (!selCollision(collision)) { return; } } @@ -1038,212 +801,137 @@ struct LambdaTableProducer { // Fill Collision Histograms histos.fill(HIST("Events/h1f_collisions_info"), kPassSelCol); histos.fill(HIST("Events/h1f_collision_posZ"), collision.posZ()); - histos.fill(HIST("Events/h2f_pvmult_vs_cent"), cent, collision.multNTracksPV()); + histos.fill(HIST("Events/h1f_collision_cent"), cent); // Fill Collision Table - lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ()); + float magField = getMagneticField(collision.template foundBC_as().timestamp()); + lambdaCollisionTable(cent, collision.posX(), collision.posY(), collision.posZ(), magField); // initialize v0track objects - ParticleType v0Type = kLambda; - PrmScdType v0PrmScdType = kPrimary; - float mass = 0., corr_fact = 1.; + ParticleType partType = kLambda; + float lambdaMass = 0., lambdaCorrFact = 1.; - // Loop over V0s + // Loop over V0s to select Lambda for (auto const& v0 : v0tracks) { - // daugthers + // Daugthers auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - // check for corresponding MCGen Particle + // Check for corresponding MCGen Particle if constexpr (dmc == kMC) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kTracksBeforeHasMcParticle); + histos.fill(HIST("Tracks/h1f_lambda_info"), kLambdaBeforeHasMcParticle); if (!v0.has_mcParticle() || !postrack.has_mcParticle() || !negtrack.has_mcParticle()) { // check corresponding MC particle continue; } } - histos.fill(HIST("Tracks/h1f_tracks_info"), kAllV0Tracks); + histos.fill(HIST("Tracks/h1f_lambda_info"), kAllV0Tracks); histos.fill(HIST("Tracks/h2f_armpod_before_sel"), v0.alpha(), v0.qtarm()); - // Select V0 Particle as Lambda/AntiLambda - if (!selV0Particle(collision, v0, tracks, v0Type)) { - continue; - } - - // Select V0 Type Selection - if (cV0TypeSelFlag && v0.v0Type() != cV0TypeSelection) { + // Select V0 as Lambda/AntiLambda + if (!selLambda(collision, v0, tracks, partType)) { continue; } // We have v0 as lambda - histos.fill(HIST("Tracks/h1f_tracks_info"), kAllSelPassed); - - // Remove lambda with ambiguous daughters (Only for run3) - if constexpr (run == kRun3) { - if (cRemoveAmbiguousTracks && hasAmbiguousDaughters(v0, tracks)) { - continue; - } - } - - // Get Lambda mass and kinematic variables - mass = (v0Type == kLambda) ? v0.mLambda() : v0.mAntiLambda(); + histos.fill(HIST("Tracks/h1f_lambda_info"), kAllSelPassed); + histos.fill(HIST("Tracks/h2f_armpod_after_sel"), v0.alpha(), v0.qtarm()); - // do MC analysis + // MC matching if constexpr (dmc == kMC) { - histos.fill(HIST("Tracks/h2f_tracks_pid_before_sel"), v0.mcParticle().pdgCode(), v0.pt()); - - // Get Primary/Secondary Lambda - if (cSelMCPSV0) { - v0PrmScdType = isPrimaryV0(v0); + auto mcpart = v0.mcParticle(); + if (cSelPrimaryParticle && !mcpart.isPhysicalPrimary()) { // Primary lambda selection + continue; } - // check for true Lambda/Anti-Lambda - if (cSelectTrueLambda && !selTrueMcRecLambda(v0, tracks)) { + if (cSelTrueParticle && std::abs(mcpart.pdgCode()) != kLambda0) { // True lambda selection continue; } - - // get mothers information - fillLambdaMothers(v0, tracks, v0PrmScdType); - - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassTrueLambdaSel); - histos.fill(HIST("Tracks/h2f_tracks_pid_after_sel"), v0.mcParticle().pdgCode(), v0.pt()); } - histos.fill(HIST("Tracks/h2f_armpod_after_sel"), v0.alpha(), v0.qtarm()); - - // get correction factors - corr_fact = (v0Type == kLambda) ? getCorrectionFactors(v0, tracks) : getCorrectionFactors(v0, tracks); + // Get Lambda mass and correction factor + lambdaMass = (partType == kLambda) ? v0.mLambda() : v0.mAntiLambda(); + if (cGetCorrectionFlag) { + lambdaCorrFact = (partType == kLambda) ? getCorrectionFactors(v0, v0.yLambda()) : getCorrectionFactors(v0, v0.yLambda()); + } - // fill lambda qa - if (v0Type == kLambda) { - histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), mass, v0.pt()); + // Fill lambda qa + if (partType == kLambda) { + getDcaHist(v0, v0.dcav0topv()); fillLambdaQAHistos(collision, v0, tracks); - fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } else { - histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), mass, v0.pt()); + getDcaHist(v0, v0.dcav0topv()); fillLambdaQAHistos(collision, v0, tracks); - fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } + // Daughter kinematics + std::vector posTrackKin = {postrack.pt(), postrack.eta(), postrack.phi()}; + std::vector negTrackKin = {negtrack.pt(), negtrack.eta(), negtrack.phi()}; + // Fill Lambda/AntiLambda Table - lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.px(), v0.py(), v0.pz(), - v0.pt(), v0.eta(), v0.phi(), v0.yLambda(), mass, - v0.template posTrack_as().index(), v0.template negTrack_as().index(), - v0.v0cosPA(), v0.dcaV0daughters(), (int8_t)v0Type, v0PrmScdType, corr_fact); + lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.pt(), v0.eta(), v0.phi(), v0.yLambda(), v0.px(), v0.py(), v0.pz(), lambdaMass, + v0.template posTrack_as().index(), v0.template negTrack_as().index(), posTrackKin, negTrackKin, + (int8_t)partType, lambdaCorrFact); } } // MC Generater Level Tables - template + template void fillLambdaMcGenTables(C const& mcCollision, M const& mcParticles) { // Fill McGen Collision Table - lambdaMCGenCollisionTable(cent, mult, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + lambdaMCGenCollisionTable(cent, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); // initialize track objects - ParticleType v0Type = kLambda; - PrmScdType v0PrmScdType = kPrimary; - float rap = 0.; + ParticleType partType = kLambda; // Loop over MC particles for (auto const& mcpart : mcParticles) { - // check for Lambda first - if (mcpart.pdgCode() == kLambda0) { - v0Type = kLambda; - } else if (mcpart.pdgCode() == kLambda0Bar) { - v0Type = kAntiLambda; + // Check for Primary Lambda/Anti-Lambda + if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0) { + partType = kLambda; + } else if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0Bar) { + partType = kAntiLambda; } else { continue; } - // check for Primary Lambda/AntiLambda - if (mcpart.isPhysicalPrimary()) { - v0PrmScdType = kPrimary; - } else { - v0PrmScdType = kSecondary; - } - - // Decide Eta/Rap - if (!cDoEtaAnalysis) { - rap = mcpart.y(); - } else { - rap = mcpart.eta(); - } - - // Apply Kinematic Acceptance - if (!kinCutSelection(mcpart.pt(), std::abs(rap), cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + // Kinematic selection + if (mcpart.pt() <= cLambdaMinPt || mcpart.pt() >= cLambdaMaxPt || std::abs(mcpart.y()) >= cLambdaRapCut) { continue; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kGenTotAccLambda); + histos.fill(HIST("Tracks/h1f_lambda_info"), kGenTotAccLambda); // get daughter track info and check for decay channel flag if (!mcpart.has_daughters()) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kGenLambdaNoDau); + histos.fill(HIST("Tracks/h1f_lambda_info"), kGenLambdaNoDau); continue; } auto dautracks = mcpart.template daughters_as(); std::vector daughterPDGs, daughterIDs; - std::vector vDauPt, vDauEta, vDauRap, vDauPhi; for (auto const& dautrack : dautracks) { daughterPDGs.push_back(dautrack.pdgCode()); daughterIDs.push_back(dautrack.globalIndex()); - vDauPt.push_back(dautrack.pt()); - vDauEta.push_back(dautrack.eta()); - vDauRap.push_back(dautrack.y()); - vDauPhi.push_back(dautrack.phi()); - } - if (cGenDecayChannel) { // check decay channel - if (v0Type == kLambda) { - if (daughterPDGs[0] != kProton || daughterPDGs[1] != kPiMinus) { - continue; - } - } else if (v0Type == kAntiLambda) { - if (daughterPDGs[0] != kProtonBar || daughterPDGs[1] != kPiPlus) { - continue; - } - } } - histos.fill(HIST("Tracks/h1f_tracks_info"), kGenLambdaToPrPi); - - if (v0Type == kLambda) { + if (partType == kLambda) { histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), daughterPDGs[0]); histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), daughterPDGs[1]); histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), mcpart.pdgCode()); - histos.fill(HIST("McGen/Lambda/Proton/hPt"), vDauPt[0]); - histos.fill(HIST("McGen/Lambda/Proton/hEta"), vDauEta[0]); - histos.fill(HIST("McGen/Lambda/Proton/hRap"), vDauRap[0]); - histos.fill(HIST("McGen/Lambda/Proton/hPhi"), vDauPhi[0]); - histos.fill(HIST("McGen/Lambda/Pion/hPt"), vDauPt[1]); - histos.fill(HIST("McGen/Lambda/Pion/hEta"), vDauEta[1]); - histos.fill(HIST("McGen/Lambda/Pion/hRap"), vDauRap[1]); - histos.fill(HIST("McGen/Lambda/Pion/hPhi"), vDauPhi[1]); - fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } else { histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[0]); histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[1]); histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), mcpart.pdgCode()); - histos.fill(HIST("McGen/AntiLambda/Pion/hPt"), vDauPt[0]); - histos.fill(HIST("McGen/AntiLambda/Pion/hEta"), vDauEta[0]); - histos.fill(HIST("McGen/AntiLambda/Pion/hRap"), vDauRap[0]); - histos.fill(HIST("McGen/AntiLambda/Pion/hPhi"), vDauPhi[0]); - histos.fill(HIST("McGen/AntiLambda/Proton/hPt"), vDauPt[1]); - histos.fill(HIST("McGen/AntiLambda/Proton/hEta"), vDauEta[1]); - histos.fill(HIST("McGen/AntiLambda/Proton/hRap"), vDauRap[1]); - histos.fill(HIST("McGen/AntiLambda/Proton/hPhi"), vDauPhi[1]); - fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } - - // Fill Lambda McGen Table - lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.px(), mcpart.py(), mcpart.pz(), - mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), RecoDecay::m(mcpart.p(), mcpart.e()), - daughterIDs[0], daughterIDs[1], (int8_t)v0Type, -999., -999., v0PrmScdType, 1.); + // Fill table + lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), mcpart.px(), mcpart.py(), mcpart.pz(), RecoDecay::m(mcpart.p(), mcpart.e()), + daughterIDs[0], daughterIDs[1], (int8_t)partType, 1.); } } - template - void analyzeMcRecoGen(M const& mcCollision, C const& collisions, V const& V0s, T const& tracks, P const& mcParticles) + template + void analyzeMcRecoGen(M const& mcCollision, C const& collisions, B const& bc, V const& V0s, T const& tracks, P const& mcParticles) { // Number of Rec Collisions Associated to the McGen Collision int nRecCols = collisions.size(); @@ -1256,127 +944,114 @@ struct LambdaTableProducer { } histos.fill(HIST("McGen/h1f_collisions_info"), kTotCol); // Check the reco collision - if (!collisions.begin().has_mcCollision() || !selCollision(collisions.begin()) || collisions.begin().mcCollisionId() != mcCollision.globalIndex()) { + if (!collisions.begin().has_mcCollision() || !selCollision(collisions.begin()) || collisions.begin().mcCollisionId() != mcCollision.globalIndex()) { return; } histos.fill(HIST("McGen/h1f_collisions_info"), kPassSelCol); histos.fill(HIST("McGen/h2f_collision_posZ"), mcCollision.posZ(), collisions.begin().posZ()); - auto v0Tracks = V0s.sliceBy(perCollision, collisions.begin().globalIndex()); - fillLambdaRecoTables(collisions.begin(), v0Tracks, tracks); - fillLambdaMcGenTables(mcCollision, mcParticles); + auto v0Tracks = V0s.sliceBy(v0sPerCollision, collisions.begin().globalIndex()); + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collisions.begin().globalIndex()); + fillLambdaRecoTables(collisions.begin(), bc, v0Tracks, tracksThisCollision); + fillLambdaMcGenTables(mcCollision, mcParticles); } - // Collision, tracks and V0s - using CollisionsRun3 = soa::Join; - using CollisionsRun2 = soa::Join; - using Tracks = soa::Join; - using TracksRun2 = soa::Join; + // BC, Collision, tracks and V0s + using BCsRun3 = soa::Join; + using Collisions = soa::Join; + using Tracks = soa::Join; using TracksMC = soa::Join; - using TracksMCRun2 = soa::Join; using McV0Tracks = soa::Join; SliceCache cache; - Preslice> perCollision = aod::v0data::collisionId; + Preslice> v0sPerCollision = aod::v0data::collisionId; + Preslice tracksPerCollision = aod::track::collisionId; - void processDummy(CollisionsRun3::iterator const&) {} + void processDummy(Collisions::iterator const&) {} PROCESS_SWITCH(LambdaTableProducer, processDummy, "Dummy Process", true); - void processDataRun3(CollisionsRun3::iterator const& collision, aod::V0Datas const& V0s, Tracks const& tracks) + void processData(Collisions::iterator const& collision, BCsRun3 const& bc, aod::V0Datas const& V0s, Tracks const& tracks) { - fillLambdaRecoTables(collision, V0s, tracks); + fillLambdaRecoTables(collision, bc, V0s, tracks); } - PROCESS_SWITCH(LambdaTableProducer, processDataRun3, "Process for Run3 DATA", false); + PROCESS_SWITCH(LambdaTableProducer, processData, "Process for DATA", false); - void processDataRun2(CollisionsRun2::iterator const& collision, aod::V0Datas const& V0s, TracksRun2 const& tracks) + void processMCReco(soa::Join::iterator const& collision, aod::McCollisions const&, BCsRun3 const& bc, + McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const&) { - fillLambdaRecoTables(collision, V0s, tracks); + fillLambdaRecoTables(collision, bc, V0s, tracks); } - PROCESS_SWITCH(LambdaTableProducer, processDataRun2, "Process for Run2 DATA", false); + PROCESS_SWITCH(LambdaTableProducer, processMCReco, "Process for McReco DATA", false); - void processMatchEffData(CollisionsRun3::iterator const& collision, Tracks const& tracks) + void processMCRecoGen(aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, BCsRun3 const& bc, + McV0Tracks const& V0s, TracksMC const& tracks, + aod::McParticles const& mcParticles) { - // check collision - if (!selCollision(collision)) { - return; - } - // Get Matching Efficiency - getMatchEffHist(tracks); + analyzeMcRecoGen(mcCollision, collisions, bc, V0s, tracks, mcParticles); } - PROCESS_SWITCH(LambdaTableProducer, processMatchEffData, "Process for Matching Efficieny Calculation", false); + PROCESS_SWITCH(LambdaTableProducer, processMCRecoGen, "Process for MC RecoGen", false); - void processMCRecoRun3(soa::Join::iterator const& collision, aod::McCollisions const&, - McV0Tracks const& V0s, TracksMC const& tracks, aod::McParticles const&) + void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) { - // check collision - if (!selCollision(collision)) { + // Multiplicity estimation + int fwdMult = 0; + bool eventTrig = multEstimator(mcParticles, fwdMult); + + // MB event selection + if (!eventTrig) { return; } - fillLambdaRecoTables(collision, V0s, tracks); - } - PROCESS_SWITCH(LambdaTableProducer, processMCRecoRun3, "Process for Run3 McReco DATA", false); + // Fill fwd multiplicity histogram + histos.fill(HIST("Events/h1f_fwdmult"), fwdMult); - void processMCRecoRun2(soa::Join::iterator const& collision, aod::McCollisions const&, - McV0Tracks const& V0s, TracksMCRun2 const& tracks, aod::McParticles const&) - { - // check collision - if (!selCollision(collision)) { - return; - } - fillLambdaRecoTables(collision, V0s, tracks); - } + // Get centrality + cent = getMcCentrality(fwdMult); - PROCESS_SWITCH(LambdaTableProducer, processMCRecoRun2, "Process for Run2 McReco DATA", false); + // Fill McGen Collision Table + lambdaMCGenCollisionTable(cent, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); - void processMatchEffMCReco(soa::Join::iterator const& collision, aod::McCollisions const&, TracksMC const& tracks, aod::McParticles const&) - { - // check collision - if (!selCollision(collision)) { - return; - } - // Get Matching Efficiency - getMatchEffHist(tracks); - } + // initialize track objects + ParticleType partType = kLambda; - PROCESS_SWITCH(LambdaTableProducer, processMatchEffMCReco, "Process for Matching Efficieny Calculation at MC Reconstructed Level", false); + // Loop over MC particles + for (auto const& mcpart : mcParticles) { + // Check for Primary Lambda/Anti-Lambda + if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0) { + partType = kLambda; + } else if (mcpart.isPhysicalPrimary() && mcpart.pdgCode() == kLambda0Bar) { + partType = kAntiLambda; + } else { + continue; + } - void processMCRun3(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - McV0Tracks const& V0s, TracksMC const& tracks, - aod::McParticles const& mcParticles) - { - analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); - } + // Kinematic selection + if (mcpart.pt() <= cLambdaMinPt || mcpart.pt() >= cLambdaMaxPt || std::abs(mcpart.y()) >= cLambdaRapCut) { + continue; + } - PROCESS_SWITCH(LambdaTableProducer, processMCRun3, "Process for Run3 MC RecoGen", false); + histos.fill(HIST("Tracks/h1f_lambda_info"), kGenTotAccLambda); - void processMCRun2(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - McV0Tracks const& V0s, TracksMCRun2 const& tracks, - aod::McParticles const& mcParticles) - { - analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); + // Fill table + lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), mcpart.px(), mcpart.py(), mcpart.pz(), RecoDecay::m(mcpart.p(), mcpart.e()), + -1, -1, (int8_t)partType, 1.); + } } - PROCESS_SWITCH(LambdaTableProducer, processMCRun2, "Process for Run2 MC RecoGen", false); + PROCESS_SWITCH(LambdaTableProducer, processMCGen, "Process for MC Gen", false); }; struct LambdaTracksExtProducer { - + // Tables Produces lambdaTrackExtTable; // Configurables Configurable cAcceptAllLambda{"cAcceptAllLambda", false, "Accept all Lambda"}; Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, "Reject all Lambda sharing daughters"}; - Configurable cSelLambdaMassPdg{"cSelLambdaMassPdg", false, "Select Lambda closest to Pdg Mass"}; - Configurable cSelLambdaTScore{"cSelLambdaTScore", false, "Select Lambda based on t-score"}; - Configurable cA{"cA", 0.6, "a * |lambdaMass - lambdaPdgMass|"}; - Configurable cB{"cB", 0.6, "b * DcaPrPi"}; - Configurable cC{"cC", 0.6, "c * Cos(theta_{PA})"}; // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -1385,9 +1060,6 @@ struct LambdaTracksExtProducer { { // Axis Specifications const AxisSpec axisMult(10, 0, 10); - const AxisSpec axisMass(100, 1.06, 1.16, "Inv Mass (GeV/#it{c}^{2})"); - const AxisSpec axisCPA(100, 0.995, 1.0, "cos(#theta_{PA})"); - const AxisSpec axisDcaDau(75, 0., 1.5, "Daug DCA (#sigma)"); const AxisSpec axisDEta(320, -1.6, 1.6, "#Delta#eta"); const AxisSpec axisDPhi(640, -PIHalf, 3. * PIHalf, "#Delta#varphi"); @@ -1399,32 +1071,6 @@ struct LambdaTracksExtProducer { histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{SharePair}", kTH2D, {axisDEta, axisDPhi}); histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{SharePair}", kTH2D, {axisDEta, axisDPhi}); histos.add("h2d_n2_etaphi_LaM_LaM", "#rho_{2}^{SharePair}", kTH2D, {axisDEta, axisDPhi}); - - // InvMass, DcaDau and CosPA - histos.add("Reco/h1f_lambda_invmass", "M_{p#pi}", kTH1F, {axisMass}); - histos.add("Reco/h1f_lambda_cospa", "cos(#theta_{PA})", kTH1F, {axisCPA}); - histos.add("Reco/h1f_lambda_dcadau", "DCA_{p#pi} at V0 Decay Vertex", kTH1F, {axisDcaDau}); - histos.add("Reco/h1f_antilambda_invmass", "M_{p#pi}", kTH1F, {axisMass}); - histos.add("Reco/h1f_antilambda_cospa", "cos(#theta_{PA})", kTH1F, {axisCPA}); - histos.add("Reco/h1f_antilambda_dcadau", "DCA_{p#pi} at V0 Decay Vertex", kTH1F, {axisDcaDau}); - - histos.addClone("Reco/", "SharingDau/"); - } - - template - void fillHistos(T const& track) - { - static constexpr std::string_view SubDir[] = {"Reco/", "SharingDau/"}; - - if (track.v0Type() == kLambda) { - histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_invmass"), track.mass()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_dcadau"), track.dcaDau()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_cospa"), track.cosPA()); - } else { - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_invmass"), track.mass()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_dcadau"), track.dcaDau()); - histos.fill(HIST(SubDir[sd]) + HIST("h1f_antilambda_cospa"), track.cosPA()); - } } void processDummy(aod::LambdaCollisions::iterator const&) {} @@ -1436,19 +1082,15 @@ struct LambdaTracksExtProducer { int nTotLambda = 0, nTotAntiLambda = 0, nSelLambda = 0, nSelAntiLambda = 0; for (auto const& lambda : tracks) { - bool lambdaMinDeltaMassFlag = true, lambdaMinTScoreFlag = true; bool lambdaSharingDauFlag = false, trueLambdaFlag = false; std::vector vSharedDauLambdaIndex; - float tLambda = 0., tTrack = 0.; - if (lambda.v0Type() == kLambda) { + if (lambda.partType() == kLambda) { ++nTotLambda; - } else if (lambda.v0Type() == kAntiLambda) { + } else if (lambda.partType() == kAntiLambda) { ++nTotAntiLambda; } - tLambda = (cA * std::abs(lambda.mass() - MassLambda0)) + (cB * lambda.dcaDau()) + (cC * std::abs(lambda.cosPA() - 1.)); - for (auto const& track : tracks) { // check lambda index (don't analyze same lambda track !!!) if (lambda.index() == track.index()) { @@ -1461,54 +1103,29 @@ struct LambdaTracksExtProducer { lambdaSharingDauFlag = true; // Fill DEta-DPhi Histogram - if ((lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) || (lambda.v0Type() == kAntiLambda && track.v0Type() == kLambda)) { + if ((lambda.partType() == kLambda && track.partType() == kAntiLambda) || (lambda.partType() == kAntiLambda && track.partType() == kLambda)) { histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } else if (lambda.v0Type() == kLambda && track.v0Type() == kLambda) { + } else if (lambda.partType() == kLambda && track.partType() == kLambda) { histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { + } else if (lambda.partType() == kAntiLambda && track.partType() == kAntiLambda) { histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } - - // decision based on mass closest to PdgMass of Lambda - if (std::abs(lambda.mass() - MassLambda0) > std::abs(track.mass() - MassLambda0)) { - lambdaMinDeltaMassFlag = false; - } - - // decisions based on t-score - tTrack = (cA * std::abs(track.mass() - MassLambda0)) + (cB * track.dcaDau()) + (cC * std::abs(track.cosPA() - 1.)); - if (tLambda > tTrack) { - lambdaMinTScoreFlag = false; - } } } - // fill QA histograms - if (lambdaSharingDauFlag) { - fillHistos(lambda); - } else { - fillHistos(lambda); - } - - if (cAcceptAllLambda) { // Accept all lambda - trueLambdaFlag = true; - } else if (cRejAllLambdaShaDau && !lambdaSharingDauFlag) { // Reject all lambda sharing daughter - trueLambdaFlag = true; - } else if (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) { // Select lambda closest to pdg mass - trueLambdaFlag = true; - } else if (cSelLambdaTScore && lambdaMinTScoreFlag) { // Select lambda based on t-score - trueLambdaFlag = true; - } + // Accept / Reject + trueLambdaFlag = cAcceptAllLambda || (cRejAllLambdaShaDau && !lambdaSharingDauFlag); // Multiplicity of selected lambda if (trueLambdaFlag) { - if (lambda.v0Type() == kLambda) { + if (lambda.partType() == kLambda) { ++nSelLambda; - } else if (lambda.v0Type() == kAntiLambda) { + } else if (lambda.partType() == kAntiLambda) { ++nSelAntiLambda; } } - // fill LambdaTrackExt table + // Fill LambdaTrackExt table lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag); } @@ -1535,22 +1152,31 @@ struct LambdaTracksExtProducer { struct LambdaR2Correlation { // Global Configurables - Configurable cNPtBins{"cNPtBins", 34, "N pT Bins"}; - Configurable cMinPt{"cMinPt", 0.8, "pT Min"}; - Configurable cMaxPt{"cMaxPt", 4.2, "pT Max"}; - Configurable cNRapBins{"cNRapBins", 20, "N Rapidity Bins"}; + Configurable cLambdaNPtBins{"cLambdaNPtBins", 34, "N pT Bins"}; + Configurable cLambdaPtMin{"cLambdaPtMin", 0.7, "Lambda pT Min"}; + Configurable cLambdaPtMax{"cLambdaPtMax", 3.4, "Lambda pT Max"}; + + Configurable cNRapBins{"cNRapBins", 10, "N Rapidity Bins"}; Configurable cMinRap{"cMinRap", -0.5, "Minimum Rapidity"}; Configurable cMaxRap{"cMaxRap", 0.5, "Maximum Rapidity"}; Configurable cNPhiBins{"cNPhiBins", 36, "N Phi Bins"}; - Configurable cAnaSecondaries{"cAnaSecondaries", false, "Analysze Secondaries"}; + + // Analysis flags Configurable cAnaPairs{"cAnaPairs", false, "Analyze Pairs Flag"}; - Configurable cAnaSecondaryPairs{"cAnaSecondaryPairs", false, "Analyze Secondary Pairs Flag"}; + Configurable cAnaEff{"cAnaEff", false, "Analyze efficiency flag"}; + + // Lambda Kaon femtoscopic correction + Configurable cApplyFemtoSel{"cApplyFemtoSel", false, "Femto qinv selection"}; + Configurable cFemtoCut{"cFemtoCut", 0.1, "Femto qinv cut"}; - // Eta/Rap Analysis - Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Eta/Rap Analysis Flag"}; + // Lambda Kaon two-track cuts + Configurable cApplyTwoTrackCut{"cApplyTwoTrackCut", false, "Flag for two track cut"}; + Configurable cDEtaCut{"cDEtaCut", 0.02, "DEta cut"}; + Configurable cDPhiStarCut{"cDPhiStarCut", 0.02, "DPhiStar cut"}; // Centrality Axis - ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 50.f, 80.0f, 100.f}, "Variable Mult-Bins"}; + ConfigurableAxis cCentBins{"cCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Variable Mult-Bins"}; + ConfigurableAxis cVzBins{"cVzBins", {VARIABLE_WIDTH, -10.f, -8.0f, -6.0f, -4.0f, -2.0f, 0.f, 2.0f, 4.0f, 6.0f, 8.0f, 10.f}, "Variable Vz-Bins"}; // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -1565,7 +1191,9 @@ struct LambdaR2Correlation { float rapbinwidth = 0.; float phibinwidth = 0.; float q = 0., e = 0., qinv = 0.; - float cent = 0.; + float cent = 0., posZ = 0.; + float magField = 0.; + std::array tpcRadii = {0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4}; void init(InitContext const&) { @@ -1585,76 +1213,54 @@ struct LambdaR2Correlation { const AxisSpec axisCheck(1, 0, 1, ""); const AxisSpec axisPosZ(220, -11, 11, "V_{z} (cm)"); const AxisSpec axisCent(cCentBins, "FT0C (%)"); - const AxisSpec axisChMult(200, 0, 200, "N_{ch}"); + const AxisSpec axisVz(cVzBins, "V_{z} (cm)"); const AxisSpec axisMult(10, 0, 10, "N_{#Lambda}"); + const AxisSpec axisDEta(320, -1.6, 1.6, "#Delta#eta"); + const AxisSpec axisDPhi(640, -PIHalf, 3. * PIHalf, "#Delta#varphi"); const AxisSpec axisMass(100, 1.06, 1.16, "M_{#Lambda} (GeV/#it{c}^{2})"); - const AxisSpec axisPt(cNPtBins, cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); - const AxisSpec axisEta(cNRapBins, cMinRap, cMaxRap, "#eta"); + const AxisSpec axisPtLambda(cLambdaNPtBins, cLambdaPtMin, cLambdaPtMax, "p_{T} (GeV/#it{c})"); const AxisSpec axisRap(cNRapBins, cMinRap, cMaxRap, "y"); const AxisSpec axisPhi(cNPhiBins, 0., TwoPI, "#varphi (rad)"); const AxisSpec axisRapPhi(knrapphibins, kminrapphi, kmaxrapphi, "y #varphi"); - const AxisSpec axisQinv(100, 0, 10, "q_{inv} (GeV/#it{c})"); // Create Histograms. // Event histos.add("Event/Reco/h1f_collision_posz", "V_{Z} Distribution", kTH1F, {axisPosZ}); histos.add("Event/Reco/h1f_ft0m_mult_percentile", "FT0M (%)", kTH1F, {axisCent}); - histos.add("Event/Reco/h2f_Mult_vs_Centrality", "N_{ch} vs FT0M(%)", kTProfile, {axisCent}); - histos.add("Event/Reco/h2f_lambda_mult", "#Lambda - Multiplicity", kTProfile, {axisCent}); - histos.add("Event/Reco/h2f_antilambda_mult", "#bar{#Lambda} - Multiplicity", kTProfile, {axisCent}); + + // Two track cut + histos.add("QA/TwoTrackCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/TwoTrackCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/FemtoCut/Before/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); + histos.add("QA/FemtoCut/After/h2d_n2_detadphi", "#rho_{2}", kTH2D, {axisDEta, axisDPhi}); // Efficiency Histograms // Single Particle Efficiencies - histos.add("Reco/Primary/Efficiency/h2f_n1_centpt_LaP", "#rho_{1}^{#Lambda}", kTH2F, {axisCent, axisPt}); - histos.add("Reco/Primary/Efficiency/h2f_n1_centpt_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH2F, {axisCent, axisPt}); - histos.add("Reco/Primary/Efficiency/h3f_n1_centptrap_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPt, axisRap}); - histos.add("Reco/Primary/Efficiency/h3f_n1_centptrap_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPt, axisRap}); + if (cAnaEff) { + histos.add("Reco/Efficiency/h2f_n1_centpt_LaP", "#rho_{1}^{#Lambda}", kTH2F, {axisCent, axisPtLambda}); + histos.add("Reco/Efficiency/h2f_n1_centpt_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH2F, {axisCent, axisPtLambda}); + histos.add("Reco/Efficiency/h3f_n1_centptrap_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPtLambda, axisRap}); + histos.add("Reco/Efficiency/h3f_n1_centptrap_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPtLambda, axisRap}); + histos.add("Reco/Efficiency/h4f_n1_centvzptrap_LaP", "#rho_{1}^{#Lambda}", kTHnSparseF, {axisCent, axisVz, axisPtLambda, axisRap}); + histos.add("Reco/Efficiency/h4f_n1_centvzptrap_LaM", "#rho_{1}^{#bar{#Lambda}}", kTHnSparseF, {axisCent, axisVz, axisPtLambda, axisRap}); + } // Single and Two Particle Densities // 1D Histograms - histos.add("Reco/Primary/h3f_n1_centmasspt_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisMass, axisPt}); - histos.add("Reco/Primary/h3f_n1_centmasspt_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisMass, axisPt}); - histos.add("Reco/Primary/h4f_n1_ptrapphi_LaP", "#rho_{1}^{#Lambda}", kTHnSparseF, {axisCent, axisPt, axisRap, axisPhi}); - histos.add("Reco/Primary/h4f_n1_ptrapphi_LaM", "#rho_{1}^{#bar{#Lambda}}", kTHnSparseF, {axisCent, axisPt, axisRap, axisPhi}); + histos.add("Reco/h3f_n1_centptmass_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPtLambda, axisMass}); + histos.add("Reco/h3f_n1_centptmass_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPtLambda, axisMass}); + histos.add("Reco/h3f_n1_centptrap_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPtLambda, axisRap}); + histos.add("Reco/h3f_n1_centptrap_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPtLambda, axisRap}); // rho1 for R2 RapPhi - histos.add("Reco/Primary/h3f_n1_rapphi_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisRap, axisPhi}); - histos.add("Reco/Primary/h3f_n1_rapphi_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisRap, axisPhi}); - - // rho1 for Q_{inv} - histos.add("Reco/Primary/h3f_n1_ptrapphi_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisPt, axisRapPhi}); - histos.add("Reco/Primary/h3f_n1_ptrapphi_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisPt, axisRapPhi}); - - // Clone Singles Primary/Secondary Histogram - if (cAnaSecondaries) { - histos.addClone("Reco/Primary/", "Reco/Secondary/"); - } + histos.add("Reco/h3f_n1_rapphi_LaP", "#rho_{1}^{#Lambda}", kTH3F, {axisCent, axisRap, axisPhi}); + histos.add("Reco/h3f_n1_rapphi_LaM", "#rho_{1}^{#bar{#Lambda}}", kTH3F, {axisCent, axisRap, axisPhi}); if (cAnaPairs) { - // rho2 for numerator of R2 - histos.add("Reco/PP/h3f_n2_raprap_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH3F, {axisCent, axisRap, axisRap}); - histos.add("Reco/PP/h3f_n2_raprap_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH3F, {axisCent, axisRap, axisRap}); - histos.add("Reco/PP/h3f_n2_raprap_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH3F, {axisCent, axisRap, axisRap}); - histos.add("Reco/PP/h3f_n2_phiphi_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH3F, {axisCent, axisPhi, axisPhi}); - histos.add("Reco/PP/h3f_n2_phiphi_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH3F, {axisCent, axisPhi, axisPhi}); - histos.add("Reco/PP/h3f_n2_phiphi_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH3F, {axisCent, axisPhi, axisPhi}); - // rho2 for R2 Rap1Phi1Rap2Phi2 - histos.add("Reco/PP/h3f_n2_rapphi_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); - histos.add("Reco/PP/h3f_n2_rapphi_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); - histos.add("Reco/PP/h3f_n2_rapphi_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); - - // rho2 for R2 Qinv - histos.add("Reco/PP/h3f_n2_qinv_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTHnSparseF, {axisCent, axisPt, axisRapPhi, axisQinv}); - histos.add("Reco/PP/h3f_n2_qinv_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTHnSparseF, {axisCent, axisPt, axisRapPhi, axisQinv}); - histos.add("Reco/PP/h3f_n2_qinv_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTHnSparseF, {axisCent, axisPt, axisRapPhi, axisQinv}); - - // Clone Pairs Histograms - if (cAnaSecondaryPairs) { - histos.addClone("Reco/PP/", "Reco/PS/"); - histos.addClone("Reco/PP/", "Reco/SP/"); - histos.addClone("Reco/PP/", "Reco/SS/"); - } + histos.add("Reco/h3f_n2_rapphi_LaP_LaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_n2_rapphi_LaP_LaP", "#rho_{2}^{#Lambda#Lambda}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); + histos.add("Reco/h3f_n2_rapphi_LaM_LaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTH3F, {axisCent, axisRapPhi, axisRapPhi}); } // MCGen @@ -1664,106 +1270,150 @@ struct LambdaR2Correlation { } } - // Rap-Phi Bin Index - int getRapPhiBin(float const& rap, float const& phi) + template + bool checkClosePair(A const& v1, A const& v2, int charge1, int charge2) + { + // DPhiStar average over different TPC radius + float dphistar = 0., n = 0.; + for (auto const& radius : tpcRadii) { + float arg1 = 0.15 * magField * radius / v1[0]; + float arg2 = 0.15 * magField * radius / v2[0]; + if (std::abs(arg1) < 1.0 && std::abs(arg2) < 1.0) { + dphistar += v1[2] - v2[2] - (charge1 * std::abs(std::asin(arg1))) + (charge2 * std::abs(std::asin(arg2))); + ++n; + } + } + + // Inf check + if (n == 0) { + return false; + } + + // DPhiStar + dphistar = RecoDecay::constrainAngle(dphistar / n, -PIHalf); + + // DEta + float deta = v1[1] - v2[1]; + + // Return + return (std::abs(deta) < cDEtaCut && std::abs(dphistar) < cDPhiStarCut); + } + + template + bool isClosePair(V const& l1, V const& l2) { - int rapbin = static_cast((rap - kminrap) / rapbinwidth); - int phibin = static_cast(phi / phibinwidth); + // Before + histos.fill(HIST("QA/TwoTrackCut/Before/h2d_n2_detadphi"), l1.eta() - l2.eta(), RecoDecay::constrainAngle(l1.phi() - l2.phi(), -PIHalf)); + + // Assign kinematics + std::array posTrackKinLambda1 = {l1.posTrackKin()[0], l1.posTrackKin()[1], l1.posTrackKin()[2]}; + std::array negTrackKinLambda1 = {l1.negTrackKin()[0], l1.negTrackKin()[1], l1.negTrackKin()[2]}; + std::array posTrackKinLambda2 = {l2.posTrackKin()[0], l2.posTrackKin()[1], l2.posTrackKin()[2]}; + std::array negTrackKinLambda2 = {l2.negTrackKin()[0], l2.negTrackKin()[1], l2.negTrackKin()[2]}; - int rapphibin = -99; - if (rapbin >= 0 && phibin >= 0 && rapbin < nrapbins && phibin < nphibins) { - rapphibin = rapbin * nphibins + phibin; - return rapphibin; + // Check close pair + bool retFlag = checkClosePair(posTrackKinLambda1, posTrackKinLambda2, 1, 1) && checkClosePair(negTrackKinLambda1, negTrackKinLambda2, -1, -1); + + // Fill QA + if (!retFlag) { // Pair accept + histos.fill(HIST("QA/TwoTrackCut/After/h2d_n2_detadphi"), l1.eta() - l2.eta(), RecoDecay::constrainAngle(l1.phi() - l2.phi(), -PIHalf)); } - return rapphibin; + // Pair reject + return retFlag; } - template - void fillPairHistos(U& p1, U& p2) + template + bool isCloseQinv(V const& p1, T const& p2) { - static constexpr std::string_view SubDirRecGen[] = {"Reco/", "McGen/"}; - static constexpr std::string_view SubDirPrmScd[] = {"PP/", "PS/", "SP/", "SS/"}; - static constexpr std::string_view SubDirHist[] = {"LaP_LaM", "LaP_LaP", "LaM_LaM"}; + // Before + histos.fill(HIST("QA/FemtoCut/Before/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); + + // Calculate qinv + q = RecoDecay::p((p1.px() - p2.px()), (p1.py() - p2.py()), (p1.pz() - p2.pz())); + e = RecoDecay::e(p1.px(), p1.py(), p1.pz(), MassLambda0) - RecoDecay::e(p2.px(), p2.py(), p2.pz(), MassKaonCharged); + qinv = std::sqrt(-RecoDecay::m2(q, e)); + + // Pair accept + if (qinv > cFemtoCut) { + histos.fill(HIST("QA/FemtoCut/After/h2d_n2_detadphi"), p1.eta() - p2.eta(), RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PIHalf)); + return false; + } - float rap1 = (cDoEtaAnalysis) ? p1.eta() : p1.rap(); - float rap2 = (cDoEtaAnalysis) ? p2.eta() : p2.rap(); + // Pair reject + return true; + } - int rapbin1 = static_cast((rap1 - kminrap) / rapbinwidth); - int rapbin2 = static_cast((rap2 - kminrap) / rapbinwidth); + template + void fillPairHistos(T1& p1, T2& p2) + { + static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; + static constexpr auto SubDirHist = std::array{"LaP_LaM", "LaP_LaP", "LaM_LaM"}; - int phibin1 = static_cast(p1.phi() / phibinwidth); - int phibin2 = static_cast(p2.phi() / phibinwidth); + const auto rapbin1 = static_cast((p1.rap() - kminrap) / rapbinwidth); + const auto rapbin2 = static_cast((p2.rap() - kminrap) / rapbinwidth); - float corfac = p1.corrFact() * p2.corrFact(); + const auto phibin1 = static_cast(p1.phi() / phibinwidth); + const auto phibin2 = static_cast(p2.phi() / phibinwidth); - // fill rho2 histograms - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h3f_n2_raprap_") + HIST(SubDirHist[part_pair]), cent, rap1, rap2, corfac); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h3f_n2_phiphi_") + HIST(SubDirHist[part_pair]), cent, p1.phi(), p2.phi(), corfac); + const float corfac = p1.corrFact() * p2.corrFact(); if (rapbin1 >= 0 && rapbin2 >= 0 && phibin1 >= 0 && phibin2 >= 0 && rapbin1 < nrapbins && rapbin2 < nrapbins && phibin1 < nphibins && phibin2 < nphibins) { - int rapphix = rapbin1 * nphibins + phibin1; - int rapphiy = rapbin2 * nphibins + phibin2; + const int rapphix = rapbin1 * nphibins + phibin1; + const int rapphiy = rapbin2 * nphibins + phibin2; - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h3f_n2_rapphi_") + HIST(SubDirHist[part_pair]), cent, rapphix + 0.5, rapphiy + 0.5, corfac); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n2_rapphi_") + HIST(SubDirHist[part_pair]), cent, rapphix + 0.5, rapphiy + 0.5, corfac); } - - // qinv histograms - q = RecoDecay::p((p1.px() - p2.px()), (p1.py() - p2.py()), (p1.pz() - p2.pz())); - e = RecoDecay::e(p1.px(), p1.py(), p1.pz(), MassLambda0) - RecoDecay::e(p2.px(), p2.py(), p2.pz(), MassLambda0); - qinv = std::sqrt(-RecoDecay::m2(q, e)); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[psp]) + HIST("h3f_n2_qinv_") + HIST(SubDirHist[part_pair]), cent, p1.pt(), getRapPhiBin(p1.rap(), p1.phi()), qinv, corfac); } - template + template void analyzeSingles(T const& tracks) { - static constexpr std::string_view SubDirRecGen[] = {"Reco/", "McGen/"}; - static constexpr std::string_view SubDirPrmScd[] = {"Primary/", "Secondary/"}; - static constexpr std::string_view SubDirHist[] = {"LaP", "LaM"}; - - int ntrk = 0; + static constexpr auto SubDirRecGen = std::array{"Reco/", "McGen/"}; + static constexpr auto SubDirHist = std::array{"LaP", "LaM"}; for (auto const& track : tracks) { - // count tracks - ++ntrk; - // Efficiency Plots - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("Efficiency/h2f_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("Efficiency/h3f_n1_centptrap_") + HIST(SubDirHist[part]), cent, track.pt(), track.rap()); + if (cAnaEff) { + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h2f_n1_centpt_") + HIST(SubDirHist[part]), cent, track.pt()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h3f_n1_centptrap_") + HIST(SubDirHist[part]), cent, track.pt(), track.rap()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("Efficiency/h4f_n1_centvzptrap_") + HIST(SubDirHist[part]), cent, posZ, track.pt(), track.rap()); + } // QA Plots - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h3f_n1_centmasspt_") + HIST(SubDirHist[part]), cent, track.mass(), track.pt()); - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h4f_n1_ptrapphi_") + HIST(SubDirHist[part]), cent, track.pt(), track.rap(), track.phi(), track.corrFact()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n1_centptmass_") + HIST(SubDirHist[part]), cent, track.pt(), track.mass()); + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n1_centptrap_") + HIST(SubDirHist[part]), cent, track.pt(), track.rap(), track.corrFact()); // Rho1 for N1RapPhi - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h3f_n1_rapphi_") + HIST(SubDirHist[part]), cent, track.rap(), track.phi(), track.corrFact()); - - // Rho1 for Q_{inv} - histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST(SubDirPrmScd[pst]) + HIST("h3f_n1_ptrapphi_") + HIST(SubDirHist[part]), cent, track.pt(), getRapPhiBin(track.rap(), track.phi()), track.corrFact()); - } - - // fill multiplicity histograms - if (ntrk != 0) { - if (part == kLambda) { - histos.fill(HIST("Event/") + HIST(SubDirRecGen[rec_gen]) + HIST("h2f_lambda_mult"), cent, ntrk); - } else { - histos.fill(HIST("Event/") + HIST(SubDirRecGen[rec_gen]) + HIST("h2f_antilambda_mult"), cent, ntrk); - } + histos.fill(HIST(SubDirRecGen[rec_gen]) + HIST("h3f_n1_rapphi_") + HIST(SubDirHist[part]), cent, track.rap(), track.phi(), track.corrFact()); } } - template - void analyzePairs(T const& trks_1, T const& trks_2) + template + void analyzePairs(T1 const& trks_1, T2 const& trks_2) { for (auto const& trk_1 : trks_1) { for (auto const& trk_2 : trks_2) { // check for same index for Lambda-Lambda / AntiLambda-AntiLambda - if (samelambda && ((trk_1.index() == trk_2.index()))) { + if (same && ((trk_1.index() == trk_2.index()))) { continue; } - fillPairHistos(trk_1, trk_2); + + // Lambda close pair rejection + if constexpr (rec_gen == kRec) { + // Close pair + if (cApplyTwoTrackCut && isClosePair(trk_1, trk_2)) { + continue; + } + // Femto selection + if (cApplyFemtoSel && isCloseQinv(trk_1, trk_2)) { + continue; + } + } + + // Fill pair histograms + fillPairHistos(trk_1, trk_2); } } } @@ -1772,10 +1422,8 @@ struct LambdaR2Correlation { using LambdaTracks = soa::Join; SliceCache cache; - Partition partPrimLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partPrimAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partSecdLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); - Partition partSecdAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); + Partition partLambdaTracks = (aod::lambdatrack::partType == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true); + Partition partAntiLambdaTracks = (aod::lambdatrack::partType == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true); void processDummy(aod::LambdaCollisions::iterator const&) {} @@ -1785,41 +1433,22 @@ struct LambdaR2Correlation { { histos.fill(HIST("Event/Reco/h1f_collision_posz"), collision.posZ()); histos.fill(HIST("Event/Reco/h1f_ft0m_mult_percentile"), collision.cent()); - histos.fill(HIST("Event/Reco/h2f_Mult_vs_Centrality"), collision.cent(), collision.mult()); + posZ = collision.posZ(); cent = collision.cent(); + magField = collision.bField(); - auto lambdaPrimTracks = partPrimLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto antiLambdaPrimTracks = partPrimAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto lambdaSecdTracks = partSecdLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto antiLambdaSecdTracks = partSecdAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto lambdaTracks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto antiLambdaTracks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - analyzeSingles(lambdaPrimTracks); - analyzeSingles(antiLambdaPrimTracks); - - if (cAnaSecondaries) { - analyzeSingles(lambdaSecdTracks); - analyzeSingles(antiLambdaSecdTracks); - } + analyzeSingles(lambdaTracks); + analyzeSingles(antiLambdaTracks); if (cAnaPairs) { - // Primary Pairs Only - analyzePairs(lambdaPrimTracks, antiLambdaPrimTracks); - analyzePairs(lambdaPrimTracks, lambdaPrimTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaPrimTracks); - - // Secondary Pairs - if (cAnaSecondaryPairs) { - analyzePairs(lambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaPrimTracks, lambdaSecdTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, lambdaPrimTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, lambdaSecdTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaSecdTracks); - } + // Pairs Only + analyzePairs(lambdaTracks, antiLambdaTracks); + analyzePairs(lambdaTracks, lambdaTracks); + analyzePairs(antiLambdaTracks, antiLambdaTracks); } } @@ -1829,50 +1458,27 @@ struct LambdaR2Correlation { using LambdaMcGenTracks = aod::LambdaMcGenTracks; SliceCache cachemc; - Partition partMcPrimLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partMcPrimAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partMcSecdLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); - Partition partMcSecdAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kSecondary); + Partition partMcLambdaTracks = (aod::lambdatrack::partType == (int8_t)kLambda); + Partition partMcAntiLambdaTracks = (aod::lambdatrack::partType == (int8_t)kAntiLambda); void processMCGen(LambdaMcGenCollisions::iterator const& mcgencol, LambdaMcGenTracks const&) { histos.fill(HIST("Event/McGen/h1f_collision_posz"), mcgencol.posZ()); histos.fill(HIST("Event/McGen/h1f_ft0m_mult_percentile"), mcgencol.cent()); - histos.fill(HIST("Event/McGen/h2f_Mult_vs_Centrality"), mcgencol.cent(), mcgencol.mult()); + posZ = mcgencol.posZ(); cent = mcgencol.cent(); - auto lambdaPrimTracks = partMcPrimLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); - auto antiLambdaPrimTracks = partMcPrimAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); - auto lambdaSecdTracks = partMcSecdLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); - auto antiLambdaSecdTracks = partMcSecdAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cachemc); + auto lambdaTracks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cache); + auto antiLambdaTracks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, mcgencol.globalIndex(), cache); - analyzeSingles(lambdaPrimTracks); - analyzeSingles(antiLambdaPrimTracks); - - if (cAnaSecondaries) { - analyzeSingles(lambdaSecdTracks); - analyzeSingles(antiLambdaSecdTracks); - } + analyzeSingles(lambdaTracks); + analyzeSingles(antiLambdaTracks); if (cAnaPairs) { - // Primary Pairs Only - analyzePairs(lambdaPrimTracks, antiLambdaPrimTracks); - analyzePairs(lambdaPrimTracks, lambdaPrimTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaPrimTracks); - - // Secondary Pairs - if (cAnaSecondaryPairs) { - analyzePairs(lambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaPrimTracks, lambdaSecdTracks); - analyzePairs(antiLambdaPrimTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, lambdaPrimTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaPrimTracks); - analyzePairs(lambdaSecdTracks, antiLambdaSecdTracks); - analyzePairs(lambdaSecdTracks, lambdaSecdTracks); - analyzePairs(antiLambdaSecdTracks, antiLambdaSecdTracks); - } + analyzePairs(lambdaTracks, antiLambdaTracks); + analyzePairs(lambdaTracks, lambdaTracks); + analyzePairs(antiLambdaTracks, antiLambdaTracks); } } diff --git a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx index c743e8df4dd..0d0f413c951 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/lambdaSpinPolarization.cxx @@ -11,8 +11,10 @@ /// \file lambdaSpinPolarization.cxx /// \brief Task to study the Lambda spin polarization -/// \author Yash Patley , Subhadeep Roy +///\author Subhadeep Roy +/// \author Yash Patley , +#include "PWGCF/TwoParticleCorrelations/DataModel/LambdaSpinPolarizationDerived.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" @@ -29,11 +31,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include @@ -42,18 +42,27 @@ #include #include +#include #include +#include #include +#include #include #include #include +#include #include +#include #include +#include #include -#include +#include +#include #include #include +#include +#include #include using namespace o2; @@ -69,28 +78,21 @@ namespace lambdacollision DECLARE_SOA_COLUMN(Cent, cent, float); DECLARE_SOA_COLUMN(Mult, mult, float); DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); -// DECALRE_SOA_ } // namespace lambdacollision + DECLARE_SOA_TABLE(LambdaCollisions, "AOD", "LAMBDACOLS", o2::soa::Index<>, - lambdacollision::Cent, - lambdacollision::Mult, - aod::collision::PosX, - aod::collision::PosY, - aod::collision::PosZ, - lambdacollision::TimeStamp - //, lambdacollision::CollisionId -); + lambdacollision::Cent, lambdacollision::Mult, + aod::collision::PosX, aod::collision::PosY, + aod::collision::PosZ, lambdacollision::TimeStamp); using LambdaCollision = LambdaCollisions::iterator; namespace lambdamcgencollision { } DECLARE_SOA_TABLE(LambdaMcGenCollisions, "AOD", "LMCGENCOLS", o2::soa::Index<>, - lambdacollision::Cent, - lambdacollision::Mult, - o2::aod::mccollision::PosX, - o2::aod::mccollision::PosY, - o2::aod::mccollision::PosZ); + lambdacollision::Cent, lambdacollision::Mult, + o2::aod::mccollision::PosX, o2::aod::mccollision::PosY, + o2::aod::mccollision::PosZ, lambdacollision::TimeStamp); using LambdaMcGenCollision = LambdaMcGenCollisions::iterator; namespace lambdatrack @@ -116,40 +118,23 @@ DECLARE_SOA_COLUMN(V0PrmScd, v0PrmScd, int8_t); DECLARE_SOA_COLUMN(CorrFact, corrFact, float); } // namespace lambdatrack DECLARE_SOA_TABLE(LambdaTracks, "AOD", "LAMBDATRACKS", o2::soa::Index<>, - lambdatrack::LambdaCollisionId, - lambdatrack::Px, - lambdatrack::Py, - lambdatrack::Pz, - lambdatrack::Pt, - lambdatrack::Eta, - lambdatrack::Phi, - lambdatrack::Rap, - lambdatrack::Mass, - lambdatrack::PrPx, - lambdatrack::PrPy, - lambdatrack::PrPz, - lambdatrack::PosTrackId, - lambdatrack::NegTrackId, - lambdatrack::CosPA, - lambdatrack::DcaDau, - lambdatrack::V0Type, - lambdatrack::V0PrmScd, - lambdatrack::CorrFact); + lambdatrack::LambdaCollisionId, lambdatrack::Px, + lambdatrack::Py, lambdatrack::Pz, lambdatrack::Pt, + lambdatrack::Eta, lambdatrack::Phi, lambdatrack::Rap, + lambdatrack::Mass, lambdatrack::PrPx, lambdatrack::PrPy, + lambdatrack::PrPz, lambdatrack::PosTrackId, + lambdatrack::NegTrackId, lambdatrack::CosPA, + lambdatrack::DcaDau, lambdatrack::V0Type, + lambdatrack::V0PrmScd, lambdatrack::CorrFact); using LambdaTrack = LambdaTracks::iterator; namespace lambdatrackext { DECLARE_SOA_COLUMN(LambdaSharingDaughter, lambdaSharingDaughter, bool); -DECLARE_SOA_COLUMN(LambdaSharingDauIds, lambdaSharingDauIds, std::vector); -DECLARE_SOA_COLUMN(TrueLambdaFlag, trueLambdaFlag, bool); -DECLARE_SOA_COLUMN(FakeFlag, fakeFlag, bool); } // namespace lambdatrackext -DECLARE_SOA_TABLE(LambdaTracksExt, "AOD", "LAMBDATRACKSEXT", - lambdatrackext::LambdaSharingDaughter, - lambdatrackext::LambdaSharingDauIds, - lambdatrackext::TrueLambdaFlag, - lambdatrackext::FakeFlag); +DECLARE_SOA_TABLE(LambdaTracksExt, "AOD", "LAMBDATRACKSEXT", + lambdatrackext::LambdaSharingDaughter); using LambdaTrackExt = LambdaTracksExt::iterator; namespace lambdamcgentrack @@ -158,24 +143,13 @@ DECLARE_SOA_INDEX_COLUMN(LambdaMcGenCollision, lambdaMcGenCollision); } DECLARE_SOA_TABLE(LambdaMcGenTracks, "AOD", "LMCGENTRACKS", o2::soa::Index<>, lambdamcgentrack::LambdaMcGenCollisionId, - o2::aod::mcparticle::Px, - o2::aod::mcparticle::Py, - o2::aod::mcparticle::Pz, - lambdatrack::Pt, - lambdatrack::Eta, - lambdatrack::Phi, - lambdatrack::Rap, - lambdatrack::Mass, - lambdatrack::PrPx, - lambdatrack::PrPy, - lambdatrack::PrPz, - lambdatrack::PosTrackId, - lambdatrack::NegTrackId, - lambdatrack::V0Type, - lambdatrack::CosPA, - lambdatrack::DcaDau, - lambdatrack::V0PrmScd, - lambdatrack::CorrFact); + o2::aod::mcparticle::Px, o2::aod::mcparticle::Py, + o2::aod::mcparticle::Pz, lambdatrack::Pt, lambdatrack::Eta, + lambdatrack::Phi, lambdatrack::Rap, lambdatrack::Mass, + lambdatrack::PrPx, lambdatrack::PrPy, lambdatrack::PrPz, + lambdatrack::PosTrackId, lambdatrack::NegTrackId, + lambdatrack::V0Type, lambdatrack::CosPA, lambdatrack::DcaDau, + lambdatrack::V0PrmScd, lambdatrack::CorrFact); using LambdaMcGenTrack = LambdaMcGenTracks::iterator; namespace lambdamixeventcollision @@ -183,45 +157,74 @@ namespace lambdamixeventcollision DECLARE_SOA_COLUMN(CollisionIndex, collisionIndex, int); } // namespace lambdamixeventcollision -DECLARE_SOA_TABLE(LambdaMixEventCollisions, "AOD", "LAMBDAMIXCOLS", o2::soa::Index<>, - lambdamixeventcollision::CollisionIndex, - lambdacollision::Cent, - aod::collision::PosZ, +DECLARE_SOA_TABLE(LambdaMixEventCollisions, "AOD", "LAMBDAMIXCOLS", + o2::soa::Index<>, lambdamixeventcollision::CollisionIndex, + lambdacollision::Cent, aod::collision::PosZ, lambdacollision::TimeStamp); - using LambdaMixEventCollision = LambdaMixEventCollisions::iterator; +namespace lambdamixeventmcgencollision +{ +} +DECLARE_SOA_TABLE(LambdaMixEventMcGenCollisions, "AOD", "LAMBDAMIXMGCOLS", + o2::soa::Index<>, lambdamixeventcollision::CollisionIndex, + lambdacollision::Cent, aod::collision::PosZ, + lambdacollision::TimeStamp); +using LambdaMixEventMcGenCollision = LambdaMixEventMcGenCollisions::iterator; + namespace lambdamixeventtracks { -// DECLARE_SOA_INDEX_COLUMN(LambdaMixEventCollision, lambdaMixEventCollision); DECLARE_SOA_COLUMN(LambdaMixEventCollisionIdx, lambdaMixEventCollisionIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTrackIdx, lambdaMixEventTrackIdx, int); DECLARE_SOA_COLUMN(LambdaMixEventTimeStamp, lambdaMixEventTimeStamp, uint64_t); - +DECLARE_SOA_COLUMN(LambdaMixEventPosTrackIdx, lambdaMixEventPosTrackIdx, int); +DECLARE_SOA_COLUMN(LambdaMixEventNegTrackIdx, lambdaMixEventNegTrackIdx, int); } // namespace lambdamixeventtracks -DECLARE_SOA_TABLE(LambdaMixEventTracks, "AOD", "LAMBDAMIXTRKS", o2::soa::Index<>, - // lambdamixeventtracks::LambdaMixEventCollisionId, +DECLARE_SOA_TABLE(LambdaMixEventTracks, "AOD", "LAMBDAMIXTRKS", + o2::soa::Index<>, lambdamixeventtracks::LambdaMixEventCollisionIdx, - lambdamixeventtracks::LambdaMixEventTrackIdx, - lambdatrack::Px, - lambdatrack::Py, - lambdatrack::Pz, - lambdatrack::Mass, - lambdatrack::PrPx, - lambdatrack::PrPy, - lambdatrack::PrPz, + lambdamixeventtracks::LambdaMixEventTrackIdx, lambdatrack::Px, + lambdatrack::Py, lambdatrack::Pz, lambdatrack::Mass, + lambdatrack::PrPx, lambdatrack::PrPy, lambdatrack::PrPz, lambdatrack::V0Type, - lambdamixeventtracks::LambdaMixEventTimeStamp); - + lambdamixeventtracks::LambdaMixEventTimeStamp, + lambdamixeventtracks::LambdaMixEventPosTrackIdx, + lambdamixeventtracks::LambdaMixEventNegTrackIdx); using LambdaMixEventTrack = LambdaMixEventTracks::iterator; + +namespace lambdamixeventmcgentracks +{ +} +DECLARE_SOA_TABLE(LambdaMixEventMcGenTracks, "AOD", "LAMBDAMIXMGTRKS", + o2::soa::Index<>, + lambdamixeventtracks::LambdaMixEventCollisionIdx, + lambdamixeventtracks::LambdaMixEventTrackIdx, lambdatrack::Px, + lambdatrack::Py, lambdatrack::Pz, lambdatrack::Mass, + lambdatrack::PrPx, lambdatrack::PrPy, lambdatrack::PrPz, + lambdatrack::V0Type, + lambdamixeventtracks::LambdaMixEventTimeStamp); +using LambdaMixEventMcGenTrack = LambdaMixEventMcGenTracks::iterator; } // namespace o2::aod -enum CollisionLabels { - kTotColBeforeHasMcCollision = 1, - kTotCol, - kPassSelCol -}; +enum CollisionLabels { kTotColBeforeHasMcCollision = 1, + kTotCol, + kPassSelCol }; + +enum EventCutFlow { kEvAll = 1, + kEvTrigger, + kEvTvx, + kEvTFBorder, + kEvItsRofBorder, + kEvItsTpcVtx, + kEvNoSameBunchPileup, + kEvGoodZvtxFT0vsPV, + kEvGoodItsLayers, + kEvCentrality, + kEvVz, + kEvOneV0, + kEvTwoV0, + kNEventCutFlow }; enum TrackLabels { kTracksBeforeHasMcParticle = 1, @@ -252,68 +255,72 @@ enum TrackLabels { kGenLambdaToPrPi }; -enum CentEstType { - kCentFT0M = 0, - kCentFT0C -}; +enum CentEstType { kCentFT0M = 0, + kCentFT0C }; -enum RunType { - kRun3 = 0, - kRun2 -}; +enum RunType { kRun3 = 0, + kRun2 }; -enum ParticleType { - kLambda = 0, - kAntiLambda, - kFakeLambda, - kFakeAntiLambda -}; +enum ParticleType { kLambda = 0, + kAntiLambda }; -enum ParticlePairType { - // ----------------------------------------------------------------------- - kLambdaAntiLambda = 0, - kAntiLambdaLambda = 1, - kLambdaLambda = 2, - kAntiLambdaAntiLambda = 3, - // ----------------------------------------------------------------------- - kLambdaFakeAntiLambda = 4, - kAntiLambdaFakeLambda = 5, - kLambdaFakeLambda = 6, - kAntiLambdaFakeAntiLambda = 7 -}; +enum ParticlePairType { kLambdaAntiLambda = 0, + kAntiLambdaLambda, + kLambdaLambda, + kAntiLambdaAntiLambda }; -enum ShareDauLambda { - kUniqueLambda = 0, - kLambdaShareDau -}; +enum PairTier { kSameEvent = 0, + kSameEventInMixing, + kMixedEvent, + kMcGenSameEvent, + kMcGenSameEventInMixing, + kMcGenMixedEvent, + kSameEventSharedDau, + kSameEventInMixingSharedDau }; -enum RecGenType { - kRec = 0, - kGen -}; +enum SharedDauRule { kSharedDauMaxSet = 0, + kSharedDauPairVeto, + kSharedDauDropAll }; -enum DMCType { - kData = 0, - kMC -}; +enum PairEventCount { kPeAll = 1, + kPeOneCand, + kPeTwoCand, + kPeUnlikeSign, + kPeLambdaLambda, + kPeAntiLambdaAntiLambda, + kNPairEventCount }; -enum CorrHistDim { - OneDimCorr = 1, - TwoDimCorr, - ThreeDimCorr -}; +enum MEEventStatus { kMEEventSeen = 1, + kMEEventOutsidePools, + kMEEventWarmUp, + kMEEventMixed, + kMEEventDonated, + kNMEEventStatus }; -enum PrmScdType { - kPrimary = 0, - kSecondary -}; +enum ShareDauLambda { kUniqueLambda = 0, + kLambdaShareDau }; -enum PrmScdPairType { - kPP = 0, - kPS, - kSP, - kSS -}; +enum RecGenType { kRec = 0, + kGen }; + +enum DMCType { kData = 0, + kMC }; + +enum CorrHistDim { OneDimCorr = 1, + TwoDimCorr, + ThreeDimCorr }; + +enum PrmScdType { kPrimary = 0, + kSecondary }; + +enum PrmScdPairType { kPP = 0, + kPS, + kSP, + kSS }; + +static constexpr std::size_t NDaughtersTwoBody = 2; + +static constexpr std::size_t NCandidatesForPair = 2; struct LambdaTableProducer { @@ -322,16 +329,15 @@ struct LambdaTableProducer { Produces lambdaMCGenCollisionTable; Produces lambdaMCGenTrackTable; - // Collisions - Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator : 0-FT0M, 1-FT0C"}; - Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ cut"}; - Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ cut"}; - Configurable cMinMult{"cMinMult", 0., "Minumum Multiplicity"}; - Configurable cMaxMult{"cMaxMult", 100.0, "Maximum Multiplicity"}; - Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A + T0C) Selection Run3"}; + Configurable cCentEstimator{"cCentEstimator", 0, "Centrality Estimator: 0=FT0M, 1=FT0C"}; + Configurable cMinZVtx{"cMinZVtx", -10.0, "Min VtxZ (cm)"}; + Configurable cMaxZVtx{"cMaxZVtx", 10.0, "Max VtxZ (cm)"}; + Configurable cMinMult{"cMinMult", 0.0, "Min centrality percentile"}; + Configurable cMaxMult{"cMaxMult", 100.0, "Max centrality percentile"}; + Configurable cSel8Trig{"cSel8Trig", true, "Sel8 (T0A+T0C) Run3"}; Configurable cInt7Trig{"cInt7Trig", false, "kINT7 MB Trigger"}; - Configurable cSel7Trig{"cSel7Trig", false, "Sel7 (V0A + V0C) Selection Run2"}; - Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "Trigger Time and Vertex Selection"}; + Configurable cSel7Trig{"cSel7Trig", false, "Sel7 (V0A+V0C) Run2"}; + Configurable cTriggerTvxSel{"cTriggerTvxSel", false, "TVX Trigger Selection"}; Configurable cTFBorder{"cTFBorder", false, "Timeframe Border Selection"}; Configurable cNoItsROBorder{"cNoItsROBorder", false, "No ITSRO Border Cut"}; Configurable cItsTpcVtx{"cItsTpcVtx", false, "ITS+TPC Vertex Selection"}; @@ -339,97 +345,78 @@ struct LambdaTableProducer { Configurable cZVtxTimeDiff{"cZVtxTimeDiff", false, "z-vtx time diff selection"}; Configurable cIsGoodITSLayers{"cIsGoodITSLayers", false, "Good ITS Layers All"}; - // Tracks Configurable cTrackMinPt{"cTrackMinPt", 0.15, "p_{T} minimum"}; Configurable cTrackMaxPt{"cTrackMaxPt", 999.0, "p_{T} maximum"}; Configurable cTrackEtaCut{"cTrackEtaCut", 0.8, "Pseudorapidity cut"}; Configurable cMinTpcCrossedRows{"cMinTpcCrossedRows", 70, "TPC Min Crossed Rows"}; - Configurable cMinTpcCROverCls{"cMinTpcCROverCls", 0.8, "Tpc Min Crossed Rows Over Findable Clusters"}; - Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 0.4, "Tpc Max Shared Clusters"}; - Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 4, "Max Chi2 Tpc"}; - Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 3.0, "TPC NSigma Selection Cut"}; + Configurable cMinTpcCROverCls{"cMinTpcCROverCls", -999, "TPC Min CR/Findable Cls"}; + Configurable cMaxTpcSharedClusters{"cMaxTpcSharedClusters", 999, "TPC Max Shared Clusters"}; + Configurable cMaxChi2Tpc{"cMaxChi2Tpc", 999, "Max TPC Chi2/ndf"}; + Configurable cTpcNsigmaCut{"cTpcNsigmaCut", 5.0, "TPC nSigma PID cut"}; Configurable cRemoveAmbiguousTracks{"cRemoveAmbiguousTracks", false, "Remove Ambiguous Tracks"}; - // LS (fake) background control variables - Configurable cApplyFakeDcaCuts{"cApplyFakeDcaCuts", true, "Apply DCA-to-PV cuts on LS daughter tracks"}; - Configurable cFakeDaughterMaxDEta{"cFakeDaughterMaxDEta", -1.f, "Max delta eta between LS daughters as topology proxy (-1=off)"}; - Configurable cFakeDaughterMaxOpenAngle{"cFakeDaughterMaxOpenAngle", -1.f, "Max opening angle between LS daughters [rad] (-1=off)"}; - Configurable cAllowDualAssignment{"cAllowDualAssignment", true, "Allow both PID assignments per LS pair"}; - Configurable cMaxFakeLambdaPerEvent{"cMaxFakeLambdaPerEvent", -1, "Max fake Lambda/AntiLambda candidates per event (-1 = no limit)"}; - Configurable cRandomizeFakeSubsample{"cRandomizeFakeSubsample", false, "Randomly subsample fake candidates if over limit"}; - - // V0s - Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.02, "Minimum Proton DCAr to PV"}; - Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.06, "Minimum Pion DCAr to PV"}; - Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Minimum DCA between V0 daughters"}; - Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1., "Maximum DCA between V0 daughters"}; - Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Minimum DCA V0 to PV"}; - Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 999.0, "Maximum DCA V0 to PV"}; - Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Minimum V0 radius from PV"}; - Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Maximum V0 radius from PV"}; - Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Minimum ctau"}; - Configurable cMaxV0CTau{"cMaxV0CTau", 30.0, "Maximum ctau"}; - Configurable cMinV0CosPA{"cMinV0CosPA", 0.995, "Minimum V0 CosPA to PV"}; - Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.01, "Reject K0Short Candidates"}; - Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0short Mass Rej Flag"}; - - // V0s kinmatic acceptance - Configurable cMinV0Mass{"cMinV0Mass", 1.10, "V0 Mass Min"}; - Configurable cMaxV0Mass{"cMaxV0Mass", 1.12, "V0 Mass Min"}; - Configurable cMinV0Pt{"cMinV0Pt", 0.8, "Minimum V0 pT"}; - Configurable cMaxV0Pt{"cMaxV0Pt", 4.2, "Minimum V0 pT"}; + + Configurable cMinDcaProtonToPV{"cMinDcaProtonToPV", 0.05, "Min proton DCA to PV (cm)"}; + Configurable cMinDcaPionToPV{"cMinDcaPionToPV", 0.1, "Min pion DCA to PV (cm)"}; + Configurable cMinV0DcaDaughters{"cMinV0DcaDaughters", 0., "Min DCA between V0 daughters"}; + Configurable cMaxV0DcaDaughters{"cMaxV0DcaDaughters", 1.4, "Max DCA between V0 daughters"}; + Configurable cMinDcaV0ToPV{"cMinDcaV0ToPV", 0.0, "Min DCA V0 to PV"}; + Configurable cMaxDcaV0ToPV{"cMaxDcaV0ToPV", 0.1, "Max DCA V0 to PV"}; + Configurable cMinV0TransRadius{"cMinV0TransRadius", 0.5, "Min V0 decay radius (cm)"}; + Configurable cMaxV0TransRadius{"cMaxV0TransRadius", 999.0, "Max V0 decay radius (cm)"}; + Configurable cMinV0CTau{"cMinV0CTau", 0.0, "Min cTau (cm)"}; + Configurable cMaxV0CTau{"cMaxV0CTau", 30.0, "Max cTau (cm)"}; + Configurable cMinV0CosPA{"cMinV0CosPA", 0.995, "Min V0 cos(PA)"}; + Configurable cKshortRejMassWindow{"cKshortRejMassWindow", 0.001, "K0s mass rejection window"}; + Configurable cKshortRejFlag{"cKshortRejFlag", true, "K0s mass rejection flag"}; + + Configurable cMinV0Mass{"cMinV0Mass", 1.09, "V0 Mass Min"}; + Configurable cMaxV0Mass{"cMaxV0Mass", 1.14, "V0 Mass Max"}; + Configurable cMinV0Pt{"cMinV0Pt", 0.6, "Minimum V0 pT"}; + Configurable cMaxV0Pt{"cMaxV0Pt", 3.0, "Maximum V0 pT"}; Configurable cMaxV0Rap{"cMaxV0Rap", 0.5, "|rap| cut"}; Configurable cDoEtaAnalysis{"cDoEtaAnalysis", false, "Do Eta Analysis"}; - Configurable cV0TypeSelFlag{"cV0TypeSelFlag", false, "V0 Type Selection Flag"}; + Configurable cV0TypeSelFlag{"cV0TypeSelFlag", true, "V0 Type Selection Flag"}; Configurable cV0TypeSelection{"cV0TypeSelection", 1, "V0 Type Selection"}; - // V0s MC Configurable cHasMcFlag{"cHasMcFlag", true, "Has Mc Tag"}; - Configurable cSelectTrueLambda{"cSelectTrueLambda", true, "Select True Lambda"}; - Configurable cSelMCPSV0{"cSelMCPSV0", true, "Select Primary/Secondary V0"}; + Configurable cSelectTrueLambda{"cSelectTrueLambda", false, "Select True Lambda"}; + Configurable cSelMCPSV0{"cSelMCPSV0", false, "Select Primary/Secondary V0"}; Configurable cCheckRecoDauFlag{"cCheckRecoDauFlag", true, "Check for reco daughter PID"}; Configurable cGenPrimaryLambda{"cGenPrimaryLambda", true, "Primary Generated Lambda"}; Configurable cGenSecondaryLambda{"cGenSecondaryLambda", false, "Secondary Generated Lambda"}; - Configurable cGenDecayChannel{"cGenDecayChannel", true, "Gen Level Decay Channel Flag"}; + Configurable cGenDecayChannel{"cGenDecayChannel", false, "Gen Level Decay Channel Flag"}; Configurable cRecoMomResoFlag{"cRecoMomResoFlag", false, "Check effect of momentum space smearing on balance function"}; - // Efficiency Correction Configurable cCorrectionFlag{"cCorrectionFlag", false, "Correction Flag"}; - Configurable cGetEffFact{"cGetEffFact", false, "Get Efficiency Factor Flag"}; + Configurable cGetEffFact{"cGetEffFact", true, "Get Efficiency Factor Flag"}; Configurable cGetPrimFrac{"cGetPrimFrac", false, "Get Primary Fraction Flag"}; Configurable cCorrFactHist{"cCorrFactHist", 0, "Efficiency Factor Histogram"}; Configurable cPrimFracHist{"cPrimFracHist", 0, "Primary Fraction Histogram"}; - // CCDB - Configurable cUrlCCDB{"cUrlCCDB", "http://ccdb-test.cern.ch:8080", "url of ccdb"}; - Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/lambda_corr_fact", "Path for ccdb-object"}; + Configurable cUrlCCDB{"cUrlCCDB", "http://alice-ccdb.cern.ch", "url of ccdb"}; + Configurable cPathCCDB{"cPathCCDB", "Users/y/ypatley/SpinCorr/RecoEfficiency", "Path for ccdb-object"}; - // Initialize CCDB Service - Service ccdb; + Service ccdb{}; - // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // initialize corr_factor objects - std::vector> vCorrFactStrings = {{"hEffVsPtCentLambda", "hEffVsPtCentAntiLambda"}, - {"hEffVsPtYCentLambda", "hEffVsPtYCentAntiLambda"}, - {"hEffVsPtEtaCentLambda", "hEffVsPtEtaCentAntiLambda"}}; + std::vector> vCorrFactStrings = { + {"hEffVsPtCentLambda", "hEffVsPtCentAntiLambda"}, + {"hEffVsPtYCentLambda", "hEffVsPtYCentAntiLambda"}, + {"hEffVsPtEtaCentLambda", "hEffVsPtEtaCentAntiLambda"}}; - // initialize corr_factor objects - std::vector> vPrimFracStrings = {{"hPrimFracVsPtCentLambda", "hPrimFracVsPtCentAntiLambda"}, - {"hPrimFracVsPtYCentLambda", "hPrimFracVsPtYCentAntiLambda"}, - {"hPrimFracVsPtEtaCentLambda", "hPrimFracVsPtEtaCentAntiLambda"}}; + std::vector> vPrimFracStrings = { + {"hPrimFracVsPtCentLambda", "hPrimFracVsPtCentAntiLambda"}, + {"hPrimFracVsPtYCentLambda", "hPrimFracVsPtYCentAntiLambda"}, + {"hPrimFracVsPtEtaCentLambda", "hPrimFracVsPtEtaCentAntiLambda"}}; - // Initialize Global Variables float cent = 0., mult = 0.; - float pt = 0., eta = 0., rap = 0., phi = 0.; void init(InitContext const&) { - // Set CCDB url ccdb->setURL(cUrlCCDB.value); ccdb->setCaching(true); - // initialize axis specifications const AxisSpec axisCols(5, 0.5, 5.5, ""); const AxisSpec axisTrks(30, 0.5, 30.5, ""); const AxisSpec axisCent(100, 0, 100, "FT0M (%)"); @@ -437,13 +424,13 @@ struct LambdaTableProducer { const AxisSpec axisVz(220, -11, 11, "V_{z} (cm)"); const AxisSpec axisPID(8000, -4000, 4000, "PdgCode"); - const AxisSpec axisV0Mass(140, 1.08, 1.15, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisV0Mass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); const AxisSpec axisV0Pt(100., 0., 10., "p_{T} (GeV/#it{c})"); const AxisSpec axisV0Rap(48, -1.2, 1.2, "y"); const AxisSpec axisV0Eta(48, -1.2, 1.2, "#eta"); const AxisSpec axisV0Phi(36, 0., TwoPI, "#phi (rad)"); - const AxisSpec axisRadius(2000, 0, 200, "r(cm)"); + const AxisSpec axisRadius(2000, 0, 200, "r (cm)"); const AxisSpec axisCosPA(300, 0.97, 1.0, "cos(#theta_{PA})"); const AxisSpec axisDcaV0PV(1000, 0., 10., "dca (cm)"); const AxisSpec axisDcaProngPV(5000, -50., 50., "dca (cm)"); @@ -452,79 +439,79 @@ struct LambdaTableProducer { const AxisSpec axisGCTau(2000, 0, 200, "#gammac#tau (cm)"); const AxisSpec axisAlpha(40, -1, 1, "#alpha"); const AxisSpec axisQtarm(40, 0, 0.4, "q_{T}"); - const AxisSpec axisTrackPt(40, 0, 4, "p_{T} (GeV/#it{c})"); const AxisSpec axisTrackDCA(200, -1, 1, "dca_{XY} (cm)"); const AxisSpec axisMomPID(80, 0, 4, "p (GeV/#it{c})"); - const AxisSpec axisNsigma(401, -10.025, 10.025, {"n#sigma"}); + const AxisSpec axisNsigma(401, -10.025, 10.025, "n#sigma"); const AxisSpec axisdEdx(360, 20, 200, "#frac{dE}{dx}"); - // Create Histograms. - // Event histograms histos.add("Events/h1f_collisions_info", "# of Collisions", kTH1F, {axisCols}); histos.add("Events/h1f_collision_posZ", "V_{z}-distribution", kTH1F, {axisVz}); + histos.add("Events/h1f_collision_cent", "Centrality of the selected collisions", kTH1F, {axisCent}); + auto hCutFlow = histos.add("Events/hEventCutFlow", "event cut flow;;collisions", kTH1D, {{kNEventCutFlow - 1, 0.5, static_cast(kNEventCutFlow) - 0.5}}); + hCutFlow->GetXaxis()->SetBinLabel(kEvAll, "all"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTrigger, "trigger (sel8)"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTvx, "TVX"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTFBorder, "no TF border"); + hCutFlow->GetXaxis()->SetBinLabel(kEvItsRofBorder, "no ITS ROF border"); + hCutFlow->GetXaxis()->SetBinLabel(kEvItsTpcVtx, "ITS-TPC vertex"); + hCutFlow->GetXaxis()->SetBinLabel(kEvNoSameBunchPileup, "no same-bunch pileup"); + hCutFlow->GetXaxis()->SetBinLabel(kEvGoodZvtxFT0vsPV, "good z_{vtx} FT0 vs PV"); + hCutFlow->GetXaxis()->SetBinLabel(kEvGoodItsLayers, "good ITS layers"); + hCutFlow->GetXaxis()->SetBinLabel(kEvCentrality, "centrality range"); + hCutFlow->GetXaxis()->SetBinLabel(kEvVz, "V_{z} range"); + hCutFlow->GetXaxis()->SetBinLabel(kEvOneV0, "#geq 1 selected #Lambda/#bar{#Lambda}"); + hCutFlow->GetXaxis()->SetBinLabel(kEvTwoV0, "#geq 2 selected #Lambda/#bar{#Lambda}"); - // QA histos.add("Tracks/h1f_tracks_info", "# of tracks", kTH1F, {axisTrks}); - histos.add("Tracks/h2f_armpod_before_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); - histos.add("Tracks/h2f_armpod_after_sel", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h2f_armpod_before_sel", "Armenteros-Podolanski (before)", kTH2F, {axisAlpha, axisQtarm}); + histos.add("Tracks/h2f_armpod_after_sel", "Armenteros-Podolanski (after)", kTH2F, {axisAlpha, axisQtarm}); histos.add("Tracks/h1f_lambda_pt_vs_invm", "p_{T} vs M_{#Lambda}", kTH2F, {axisV0Mass, axisV0Pt}); histos.add("Tracks/h1f_antilambda_pt_vs_invm", "p_{T} vs M_{#bar{#Lambda}}", kTH2F, {axisV0Mass, axisV0Pt}); - // histogram for LS subsampling fraction (nKept/nTotal per event) - histos.add("Tracks/h1f_fake_subsample_fraction", "LS subsample fraction (nKept/nTotal)", kTH1F, {{110, 0., 1.1, "f_{subsample}"}}); - - // QA Lambda - histos.add("QA/Lambda/h2f_qt_vs_alpha", "Armentros-Podolanski Plot", kTH2F, {axisAlpha, axisQtarm}); - histos.add("QA/Lambda/h1f_dca_V0_daughters", "DCA between V0 daughters", kTH1F, {axisDcaDau}); - histos.add("QA/Lambda/h1f_dca_pos_to_PV", "DCA positive prong to PV", kTH1F, {axisDcaProngPV}); - histos.add("QA/Lambda/h1f_dca_neg_to_PV", "DCA negative prong to PV", kTH1F, {axisDcaProngPV}); + histos.add("QA/Lambda/h2f_qt_vs_alpha", "Armenteros-Podolanski", kTH2F, {axisAlpha, axisQtarm}); + histos.add("QA/Lambda/h1f_dca_V0_daughters", "DCA V0 daughters", kTH1F, {axisDcaDau}); + histos.add("QA/Lambda/h1f_dca_pos_to_PV", "DCA pos-prong to PV", kTH1F, {axisDcaProngPV}); + histos.add("QA/Lambda/h1f_dca_neg_to_PV", "DCA neg-prong to PV", kTH1F, {axisDcaProngPV}); histos.add("QA/Lambda/h1f_dca_V0_to_PV", "DCA V0 to PV", kTH1F, {axisDcaV0PV}); histos.add("QA/Lambda/h1f_V0_cospa", "cos(#theta_{PA})", kTH1F, {axisCosPA}); - histos.add("QA/Lambda/h1f_V0_radius", "V_{0} Decay Radius in XY plane", kTH1F, {axisRadius}); - histos.add("QA/Lambda/h1f_V0_ctau", "V_{0} c#tau", kTH1F, {axisCTau}); - histos.add("QA/Lambda/h1f_V0_gctau", "V_{0} #gammac#tau", kTH1F, {axisGCTau}); - - histos.add("QA/Lambda/h1f_pos_prong_pt", "Pos-Prong p_{T}", kTH1F, {axisTrackPt}); - histos.add("QA/Lambda/h1f_neg_prong_pt", "Neg-Prong p_{T}", kTH1F, {axisTrackPt}); - histos.add("QA/Lambda/h1f_pos_prong_eta", "Pos-Prong #eta-distribution", kTH1F, {axisV0Eta}); - histos.add("QA/Lambda/h1f_neg_prong_eta", "Neg-Prong #eta-distribution", kTH1F, {axisV0Eta}); - histos.add("QA/Lambda/h1f_pos_prong_phi", "Pos-Prong #phi-distribution", kTH1F, {axisV0Phi}); - histos.add("QA/Lambda/h1f_neg_prong_phi", "Neg-Prong #phi-distribution", kTH1F, {axisV0Phi}); - + histos.add("QA/Lambda/h1f_V0_radius", "V0 decay radius", kTH1F, {axisRadius}); + histos.add("QA/Lambda/h1f_V0_ctau", "c#tau", kTH1F, {axisCTau}); + histos.add("QA/Lambda/h1f_V0_gctau", "#gammac#tau", kTH1F, {axisGCTau}); + histos.add("QA/Lambda/h1f_pos_prong_pt", "Pos-prong p_{T}", kTH1F, {axisTrackPt}); + histos.add("QA/Lambda/h1f_neg_prong_pt", "Neg-prong p_{T}", kTH1F, {axisTrackPt}); + histos.add("QA/Lambda/h1f_pos_prong_eta", "Pos-prong #eta", kTH1F, {axisV0Eta}); + histos.add("QA/Lambda/h1f_neg_prong_eta", "Neg-prong #eta", kTH1F, {axisV0Eta}); + histos.add("QA/Lambda/h1f_pos_prong_phi", "Pos-prong #phi", kTH1F, {axisV0Phi}); + histos.add("QA/Lambda/h1f_neg_prong_phi", "Neg-prong #phi", kTH1F, {axisV0Phi}); histos.add("QA/Lambda/h2f_pos_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); histos.add("QA/Lambda/h2f_neg_prong_dcaXY_vs_pt", "DCA vs p_{T}", kTH2F, {axisTrackPt, axisTrackDCA}); - histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC Signal Pos-Prong", kTH2F, {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC Signal Neg-Prong", kTH2F, {axisMomPID, axisdEdx}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Pos Prong", kTH2F, {axisMomPID, axisNsigma}); - histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma Neg Prong", kTH2F, {axisMomPID, axisNsigma}); - - // Kinematic Histograms - histos.add("McRec/Lambda/hPt", "Transverse Momentum", kTH1F, {axisV0Pt}); - histos.add("McRec/Lambda/hEta", "Pseudorapidity", kTH1F, {axisV0Eta}); - histos.add("McRec/Lambda/hRap", "Rapidity", kTH1F, {axisV0Rap}); - histos.add("McRec/Lambda/hPhi", "Azimuthal Angle", kTH1F, {axisV0Phi}); - - // QA Anti-Lambda + histos.add("QA/Lambda/h2f_pos_prong_dEdx_vs_p", "TPC dE/dx pos", kTH2F, {axisMomPID, axisdEdx}); + histos.add("QA/Lambda/h2f_neg_prong_dEdx_vs_p", "TPC dE/dx neg", kTH2F, {axisMomPID, axisdEdx}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma_{p} pos", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pr_vs_p", "TPC n#sigma_{p} neg", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_pos_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma_{#pi} pos", kTH2F, {axisMomPID, axisNsigma}); + histos.add("QA/Lambda/h2f_neg_prong_tpc_nsigma_pi_vs_p", "TPC n#sigma_{#pi} neg", kTH2F, {axisMomPID, axisNsigma}); + + histos.add("McRec/Lambda/hPt", "p_{T}", kTH1F, {axisV0Pt}); + histos.add("McRec/Lambda/hEta", "#eta", kTH1F, {axisV0Eta}); + histos.add("McRec/Lambda/hRap", "y", kTH1F, {axisV0Rap}); + histos.add("McRec/Lambda/hPhi", "#phi", kTH1F, {axisV0Phi}); + histos.addClone("QA/Lambda/", "QA/AntiLambda/"); histos.addClone("McRec/Lambda/", "McRec/AntiLambda/"); - // MC Generated Histograms - if (doprocessMCRun3 || doprocessMCRun2 /*|| doprocessMCRecoRun3 || doprocessMCRecoRun2*/) { - // McReco Histos - histos.add("Tracks/h2f_tracks_pid_before_sel", "PIDs", kTH2F, {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_tracks_pid_after_sel", "PIDs", kTH2F, {axisPID, axisV0Pt}); - histos.add("Tracks/h2f_lambda_mothers_pdg", "PIDs", kTH2F, {axisPID, axisV0Pt}); - - // McGen Histos - histos.add("McGen/h1f_collision_recgen", "# of Reco Collision Associated to One Mc Generator Collision", kTH1F, {axisMult}); - histos.add("McGen/h1f_collisions_info", "# of collisions", kTH1F, {axisCols}); - histos.add("McGen/h2f_collision_posZ", "V_{z}-distribution", kTH2F, {axisVz, axisVz}); - histos.add("McGen/h2f_collision_cent", "FT0M Centrality", kTH2F, {axisCent, axisCent}); - histos.add("McGen/h1f_lambda_daughter_PDG", "PDG Daughters", kTH1F, {axisPID}); - histos.add("McGen/h1f_antilambda_daughter_PDG", "PDG Daughters", kTH1F, {axisPID}); + if (doprocessMCRun3) { + histos.add("Tracks/h2f_tracks_pid_before_sel", "PIDs before sel", kTH2F, {axisPID, axisV0Pt}); + histos.add("Tracks/h2f_tracks_pid_after_sel", "PIDs after sel", kTH2F, {axisPID, axisV0Pt}); + histos.add("Tracks/h2f_lambda_mothers_pdg", "Lambda mothers", kTH2F, {axisPID, axisV0Pt}); + + histos.add("McGen/h1f_collision_recgen", "RecGen collisions", kTH1F, {axisMult}); + histos.add("McGen/h1f_collisions_info", "Collisions info", kTH1F, {axisCols}); + histos.add("McGen/h2f_collision_posZ", "V_{z} rec vs gen", kTH2F, {axisVz, axisVz}); + histos.add("McGen/h2f_collision_cent", "Centrality rec vs gen", kTH2F, {axisCent, axisCent}); + histos.add("McGen/h1f_lambda_daughter_PDG", "Lambda dau PDG", kTH1F, {axisPID}); + histos.add("McGen/h1f_antilambda_daughter_PDG", "AntiLambda dau PDG", kTH1F, {axisPID}); histos.addClone("McRec/", "McGen/"); @@ -532,12 +519,10 @@ struct LambdaTableProducer { histos.add("McGen/Lambda/Proton/hEta", "Proton #eta", kTH1F, {axisV0Eta}); histos.add("McGen/Lambda/Proton/hRap", "Proton y", kTH1F, {axisV0Rap}); histos.add("McGen/Lambda/Proton/hPhi", "Proton #phi", kTH1F, {axisV0Phi}); - histos.addClone("McGen/Lambda/Proton/", "McGen/Lambda/Pion/"); histos.addClone("McGen/Lambda/Proton/", "McGen/AntiLambda/Proton/"); histos.addClone("McGen/Lambda/Pion/", "McGen/AntiLambda/Pion/"); - // set bin lables specific to MC histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotColBeforeHasMcCollision, "kTotColBeforeHasMcCollision"); histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); histos.get(HIST("McGen/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); @@ -553,7 +538,6 @@ struct LambdaTableProducer { histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kGenLambdaToPrPi, "kGenLambdaToPrPi"); } - // set bin labels histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kTotCol, "kTotCol"); histos.get(HIST("Events/h1f_collisions_info"))->GetXaxis()->SetBinLabel(CollisionLabels::kPassSelCol, "kPassSelCol"); histos.get(HIST("Tracks/h1f_tracks_info"))->GetXaxis()->SetBinLabel(TrackLabels::kAllV0Tracks, "kAllV0Tracks"); @@ -577,13 +561,8 @@ struct LambdaTableProducer { template bool selCollision(C const& col) { - // VtxZ Selection - if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { - return false; - } - - if constexpr (run == kRun3) { // Run3 Min-Bias Trigger - // select centrality estimator + histos.fill(HIST("Events/hEventCutFlow"), kEvAll); + if constexpr (run == kRun3) { if (cCentEstimator == kCentFT0M) { cent = col.centFT0M(); } else if (cCentEstimator == kCentFT0C) { @@ -592,7 +571,7 @@ struct LambdaTableProducer { if (cSel8Trig && !col.sel8()) { return false; } - } else { // Run2 Min-Bias Trigger + } else { cent = col.centRun2V0M(); if (cInt7Trig && !col.alias_bit(kINT7)) { return false; @@ -601,94 +580,85 @@ struct LambdaTableProducer { return false; } } - - if (cent <= cMinMult || cent >= cMaxMult) { // select centrality percentile class - return false; - } + histos.fill(HIST("Events/hEventCutFlow"), kEvTrigger); if (cTriggerTvxSel && !col.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } - + histos.fill(HIST("Events/hEventCutFlow"), kEvTvx); if (cTFBorder && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; } - + histos.fill(HIST("Events/hEventCutFlow"), kEvTFBorder); if (cNoItsROBorder && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; } - + histos.fill(HIST("Events/hEventCutFlow"), kEvItsRofBorder); if (cItsTpcVtx && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { return false; } - + histos.fill(HIST("Events/hEventCutFlow"), kEvItsTpcVtx); if (cPileupReject && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } - + histos.fill(HIST("Events/hEventCutFlow"), kEvNoSameBunchPileup); if (cZVtxTimeDiff && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - + histos.fill(HIST("Events/hEventCutFlow"), kEvGoodZvtxFT0vsPV); if (cIsGoodITSLayers && !col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { return false; } + histos.fill(HIST("Events/hEventCutFlow"), kEvGoodItsLayers); - // Set Multiplicity - mult = col.multNTracksPV(); + if (cent <= cMinMult || cent >= cMaxMult) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvCentrality); + if (col.posZ() <= cMinZVtx || col.posZ() >= cMaxZVtx) { + return false; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvVz); + mult = col.multNTracksPV(); return true; } - // Kinematic Selection bool kinCutSelection(float const& pt, float const& rap, float const& ptMin, float const& ptMax, float const& rapMax) { - if (pt <= ptMin || pt >= ptMax || rap >= rapMax) { - return false; - } - - return true; + return pt > ptMin && pt < ptMax && rap < rapMax; } - // Track Selection template bool selTrack(T const& track) { if (!kinCutSelection(track.pt(), std::abs(track.eta()), cTrackMinPt, cTrackMaxPt, cTrackEtaCut)) { return false; } - if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) { return false; } - if (track.tpcCrossedRowsOverFindableCls() < cMinTpcCROverCls) { return false; } - if (track.tpcNClsShared() > cMaxTpcSharedClusters) { return false; } - if (track.tpcChi2NCl() > cMaxChi2Tpc) { return false; } - return true; } - // Daughter Track Selection template bool selDaughterTracks(V const& v0, T const&, ParticleType const& v0Type) { auto posTrack = v0.template posTrack_as(); auto negTrack = v0.template negTrack_as(); - if (!selTrack(posTrack) || !selTrack(negTrack)) { return false; } - // Apply DCA Selection on Daughter Tracks Based on Lambda/AntiLambda daughters float dcaProton = 0., dcaPion = 0.; if (v0Type == kLambda) { dcaProton = std::abs(v0.dcapostopv()); @@ -697,156 +667,106 @@ struct LambdaTableProducer { dcaPion = std::abs(v0.dcapostopv()); dcaProton = std::abs(v0.dcanegtopv()); } - if (dcaProton < cMinDcaProtonToPV || dcaPion < cMinDcaPionToPV) { return false; } - return true; } template bool topoCutSelection(C const& col, V const& v0, T const&) { - // DCA if (v0.dcaV0daughters() <= cMinV0DcaDaughters || v0.dcaV0daughters() >= cMaxV0DcaDaughters) { return false; } - if (v0.dcav0topv() <= cMinDcaV0ToPV || v0.dcav0topv() >= cMaxDcaV0ToPV) { return false; } - if (v0.v0radius() <= cMinV0TransRadius || v0.v0radius() >= cMaxV0TransRadius) { return false; } - // ctau float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; if (ctau <= cMinV0CTau || ctau >= cMaxV0CTau) { return false; } - - // cosine of pointing angle if (v0.v0cosPA() <= cMinV0CosPA) { return false; } - - // all selection criterion passed (Return True) return true; } template bool selLambdaDauWithTpcPid(T const& postrack, T const& negtrack) { - bool returnFlag = false; float tpcNSigmaPr = 0., tpcNSigmaPi = 0.; - - switch (part) { - // postrack = Proton, negtrack = Pion - case kLambda: - tpcNSigmaPr = postrack.tpcNSigmaPr(); - tpcNSigmaPi = negtrack.tpcNSigmaPi(); - break; - - // negtrack = Proton, postrack = Pion - case kAntiLambda: - tpcNSigmaPr = negtrack.tpcNSigmaPr(); - tpcNSigmaPi = postrack.tpcNSigmaPi(); - break; - } - - if (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut) { - returnFlag = true; + if constexpr (part == kLambda) { + tpcNSigmaPr = postrack.tpcNSigmaPr(); + tpcNSigmaPi = negtrack.tpcNSigmaPi(); + } else { + tpcNSigmaPr = negtrack.tpcNSigmaPr(); + tpcNSigmaPi = postrack.tpcNSigmaPi(); } - - return returnFlag; + return (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); } template bool selLambdaMassWindow(V const& v0, T const&, ParticleType& v0type) { - // Kshort mass rejection hypothesis if (cKshortRejFlag && (std::abs(v0.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { histos.fill(HIST("Tracks/h1f_tracks_info"), kV0KShortMassRej); return false; } - // initialize daughter tracks auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - // initialize selection flags bool lambdaFlag = false, antiLambdaFlag = false; - // get v0 track as lambda - if ((v0.mLambda() > cMinV0Mass && v0.mLambda() < cMaxV0Mass) && (selLambdaDauWithTpcPid(postrack, negtrack))) { + if ((v0.mLambda() > cMinV0Mass && v0.mLambda() < cMaxV0Mass) && selLambdaDauWithTpcPid(postrack, negtrack)) { lambdaFlag = true; v0type = kLambda; } - - // get v0 track as anti-lambda - if ((v0.mAntiLambda() > cMinV0Mass && v0.mAntiLambda() < cMaxV0Mass) && (selLambdaDauWithTpcPid(postrack, negtrack))) { + if ((v0.mAntiLambda() > cMinV0Mass && v0.mAntiLambda() < cMaxV0Mass) && selLambdaDauWithTpcPid(postrack, negtrack)) { antiLambdaFlag = true; v0type = kAntiLambda; } - if (!lambdaFlag && !antiLambdaFlag) { // neither Lambda nor Anti-Lambda + if (!lambdaFlag && !antiLambdaFlag) { histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaNotAntiLambda); return false; - } else if (lambdaFlag && antiLambdaFlag) { // check if the track is identified as lambda and anti-lambda both (DISCARD THIS TRACK) + } + if (lambdaFlag && antiLambdaFlag) { histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsBothLambdaAntiLambda); return false; } - - if (lambdaFlag || antiLambdaFlag) { - return true; - } - - histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaAfterSel); - - return false; + return true; } template bool selV0Particle(C const& col, V const& v0, T const& tracks, ParticleType& v0Type) { - // Apply Lambda Mass Hypothesis if (!selLambdaMassWindow(v0, tracks, v0Type)) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsLambdaOrAntiLambda); - // Apply Daughter Track Selection if (!selDaughterTracks(v0, tracks, v0Type)) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0DauTrackSel); - // Apply Kinematic Selection - float rap = 0.; - if (!cDoEtaAnalysis) { - rap = std::abs(v0.yLambda()); - } else { - rap = std::abs(v0.eta()); - } - + float rap = cDoEtaAnalysis ? std::abs(v0.eta()) : std::abs(v0.yLambda()); if (!kinCutSelection(v0.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0KinCuts); - // Apply Topological Selection if (!topoCutSelection(col, v0, tracks)) { return false; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0TopoSel); - // All Selection Criterion Passed return true; } @@ -855,22 +775,15 @@ struct LambdaTableProducer { { auto posTrack = v0.template posTrack_as(); auto negTrack = v0.template negTrack_as(); - - auto posTrackCompCols = posTrack.compatibleCollIds(); - auto negTrackCompCols = negTrack.compatibleCollIds(); - - // Check if daughter tracks belongs to more than one collision (Ambiguous Tracks) - if (posTrackCompCols.size() > 1 || negTrackCompCols.size() > 1) { + auto posCC = posTrack.compatibleCollIds(); + auto negCC = negTrack.compatibleCollIds(); + if (posCC.size() > 1 || negCC.size() > 1) { return true; } - - // Check if compatible collision index matches the track collision index - if (((posTrackCompCols.size() != 0) && (posTrackCompCols[0] != posTrack.collisionId())) || - ((negTrackCompCols.size() != 0) && (negTrackCompCols[0] != negTrack.collisionId()))) { + if ((posCC.size() != 0 && posCC[0] != posTrack.collisionId()) || + (negCC.size() != 0 && negCC[0] != negTrack.collisionId())) { return true; } - - // Pass as not ambiguous return false; } @@ -878,13 +791,10 @@ struct LambdaTableProducer { PrmScdType isPrimaryV0(V const& v0) { auto mcpart = v0.template mcParticle_as(); - - // check for secondary lambda if (!mcpart.isPhysicalPrimary()) { histos.fill(HIST("Tracks/h1f_tracks_info"), kSecondaryLambda); return kSecondary; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPrimaryLambda); return kPrimary; } @@ -893,86 +803,75 @@ struct LambdaTableProducer { bool selTrueMcRecLambda(V const& v0, T const&) { auto mcpart = v0.template mcParticle_as(); - - // check if Lambda/AntiLambda if (std::abs(mcpart.pdgCode()) != kLambda0) { return false; } - // Check for daughters if (cCheckRecoDauFlag) { auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - - // check if the daughters have corresponding mcparticle if (!postrack.has_mcParticle() || !negtrack.has_mcParticle()) { histos.fill(HIST("Tracks/h1f_tracks_info"), kLambdaDauNotMcParticle); return false; } - auto mcpostrack = postrack.template mcParticle_as(); auto mcnegtrack = negtrack.template mcParticle_as(); - if (mcpart.pdgCode() == kLambda0) { - if (mcpostrack.pdgCode() != kProton || mcnegtrack.pdgCode() != kPiMinus) { + if (mcpostrack.pdgCode() != kProton || + mcnegtrack.pdgCode() != kPiMinus) { histos.fill(HIST("Tracks/h1f_tracks_info"), kLambdaNotPrPiMinus); return false; } } else if (mcpart.pdgCode() == kLambda0Bar) { - if (mcpostrack.pdgCode() != kPiPlus || mcnegtrack.pdgCode() != kProtonBar) { - histos.fill(HIST("Tracks/h1f_tracks_info"), kAntiLambdaNotAntiPrPiPlus); + if (mcpostrack.pdgCode() != kPiPlus || + mcnegtrack.pdgCode() != kProtonBar) { + histos.fill(HIST("Tracks/h1f_tracks_info"), + kAntiLambdaNotAntiPrPiPlus); return false; } } } - return true; } template float getCorrectionFactors(V const& v0) { - // Check for efficiency correction flag if (!cCorrectionFlag) { return 1.; } - // Get from CCDB - auto ccdbObj = ccdb->getForTimeStamp(cPathCCDB.value, -1); - - // Check CCDB Object + auto ccdbObj = ccdb->getForTimeStamp(cPathCCDB.value, 1); if (!ccdbObj) { LOGF(warning, "CCDB OBJECT NOT FOUND"); return 1.; } - // initialize efficiency factor and primary fraction values float effCorrFact = 1., primFrac = 1.; float rap = (cDoEtaAnalysis) ? v0.eta() : v0.yLambda(); - // Get Efficiency Factor if (cGetEffFact) { - TObject* objEff = reinterpret_cast(ccdbObj->FindObject(Form("%s", vCorrFactStrings[cCorrFactHist][part].c_str()))); - TH1F* histEff = reinterpret_cast(objEff->Clone()); + auto* objEff = ccdbObj->FindObject( + Form("%s", vCorrFactStrings[cCorrFactHist][part].c_str())); + auto* histEff = dynamic_cast(objEff->Clone()); if (histEff->GetDimension() == TwoDimCorr) { histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtCent); effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt())); } else if (histEff->GetDimension() == ThreeDimCorr) { histos.fill(HIST("Tracks/h1f_tracks_info"), kEffCorrPtRapCent); - effCorrFact = histEff->GetBinContent(histEff->FindBin(cent, v0.pt(), rap)); + effCorrFact = + histEff->GetBinContent(histEff->FindBin(cent, v0.pt(), rap)); } else { histos.fill(HIST("Tracks/h1f_tracks_info"), kNoEffCorr); - LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); + LOGF(warning, "CCDB: not a histogram!"); effCorrFact = 1.; } delete histEff; } - - // Get Primary Fraction - // (The dimension of this could be different than efficiency because of large errors !!!) if (cGetPrimFrac) { - TObject* objPrm = reinterpret_cast(ccdbObj->FindObject(Form("%s", vPrimFracStrings[cPrimFracHist][part].c_str()))); - TH1F* histPrm = reinterpret_cast(objPrm->Clone()); + auto* objPrm = ccdbObj->FindObject( + Form("%s", vPrimFracStrings[cPrimFracHist][part].c_str())); + auto* histPrm = dynamic_cast(objPrm->Clone()); if (histPrm->GetDimension() == TwoDimCorr) { histos.fill(HIST("Tracks/h1f_tracks_info"), kPFCorrPtCent); primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt())); @@ -981,12 +880,11 @@ struct LambdaTableProducer { primFrac = histPrm->GetBinContent(histPrm->FindBin(cent, v0.pt(), rap)); } else { histos.fill(HIST("Tracks/h1f_tracks_info"), kNoPFCorr); - LOGF(warning, "CCDB OBJECT IS NOT A HISTOGRAM !!!"); + LOGF(warning, "CCDB: not a histogram!"); primFrac = 1.; } delete histPrm; } - return primFrac * effCorrFact; } @@ -995,26 +893,18 @@ struct LambdaTableProducer { { auto mcpart = v0.template mcParticle_as(); auto lambdaMothers = mcpart.template mothers_as(); - histos.fill(HIST("Tracks/h2f_lambda_mothers_pdg"), lambdaMothers[0].pdgCode(), v0.pt()); + histos.fill(HIST("Tracks/h2f_lambda_mothers_pdg"), + lambdaMothers[0].pdgCode(), v0.pt()); } template void fillLambdaQAHistos(C const& col, V const& v0, T const&) { - static constexpr std::string_view SubDir[] = {"QA/Lambda/", "QA/AntiLambda/"}; - - // daugthers + static constexpr std::array SubDir = {"QA/Lambda/", "QA/AntiLambda/"}; auto postrack = v0.template posTrack_as(); auto negtrack = v0.template negTrack_as(); - float mass = 0.; + float mass = (part == kLambda) ? v0.mLambda() : v0.mAntiLambda(); - if constexpr (part == kLambda) { - mass = v0.mLambda(); - } else { - mass = v0.mAntiLambda(); - } - - // ctau float e = RecoDecay::e(v0.px(), v0.py(), v0.pz(), mass); float gamma = e / mass; float ctau = v0.distovertotmom(col.posX(), col.posY(), col.posZ()) * MassLambda0; @@ -1029,14 +919,12 @@ struct LambdaTableProducer { histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_radius"), v0.v0radius()); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_ctau"), ctau); histos.fill(HIST(SubDir[part]) + HIST("h1f_V0_gctau"), gctau); - histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_pt"), postrack.pt()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_eta"), postrack.eta()); - histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_phi"), postrack.phi()); histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_pt"), negtrack.pt()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_eta"), postrack.eta()); histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_eta"), negtrack.eta()); + histos.fill(HIST(SubDir[part]) + HIST("h1f_pos_prong_phi"), postrack.phi()); histos.fill(HIST(SubDir[part]) + HIST("h1f_neg_prong_phi"), negtrack.phi()); - histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dcaXY_vs_pt"), postrack.pt(), postrack.dcaXY()); histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_dcaXY_vs_pt"), negtrack.pt(), negtrack.dcaXY()); histos.fill(HIST(SubDir[part]) + HIST("h2f_pos_prong_dEdx_vs_p"), postrack.tpcInnerParam(), postrack.tpcSignal()); @@ -1047,12 +935,11 @@ struct LambdaTableProducer { histos.fill(HIST(SubDir[part]) + HIST("h2f_neg_prong_tpc_nsigma_pi_vs_p"), negtrack.tpcInnerParam(), negtrack.tpcNSigmaPi()); } - // Fill Lambda Kinematic Histograms template void fillKinematicHists(float const& pt, float const& eta, float const& y, float const& phi) { - static constexpr std::string_view SubDirRG[] = {"McRec/", "McGen/"}; - static constexpr std::string_view SubDirPart[] = {"Lambda/", "AntiLambda/"}; + static constexpr std::array SubDirRG = {"McRec/", "McGen/"}; + static constexpr std::array SubDirPart = {"Lambda/", "AntiLambda/"}; histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPt"), pt); histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hEta"), eta); @@ -1060,53 +947,12 @@ struct LambdaTableProducer { histos.fill(HIST(SubDirRG[rg]) + HIST(SubDirPart[part]) + HIST("hPhi"), phi); } - // Basic track quality selection reused for fake-lambda (LS) daughter candidates - template - bool selTrackBasic(T const& track) - { - if (track.pt() <= cTrackMinPt || track.pt() >= cTrackMaxPt) - return false; - if (std::abs(track.eta()) >= cTrackEtaCut) - return false; - if (track.tpcNClsCrossedRows() <= cMinTpcCrossedRows) - return false; - if (track.tpcCrossedRowsOverFindableCls() < cMinTpcCROverCls) - return false; - if (track.tpcNClsShared() > cMaxTpcSharedClusters) - return false; - if (track.tpcChi2NCl() > cMaxChi2Tpc) - return false; - return true; - } - - template - bool selTrackBasicWithDCA(T const& track, bool isProton) - { - if (!selTrackBasic(track)) - return false; - if (cApplyFakeDcaCuts) { - float minDca = isProton ? static_cast(cMinDcaProtonToPV) : static_cast(cMinDcaPionToPV); - if (std::abs(track.dcaXY()) < minDca) - return false; - } - return true; - } - - struct FakeCandidate { - float px, py, pz, pt, eta, phi, rap, mass; - float prPx, prPy, prPz; - int64_t id1, id2; - ParticleType type; - }; - - // Reconstructed Level Tables - template + template void fillLambdaRecoTables(C const& collision, B const& bc, V const& v0tracks, T const& tracks) { - // Total Collisions histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); - // Select Collision (Only for Data... McRec has been selected already !!!) if constexpr (dmc == kData) { if (!selCollision(collision)) { return; @@ -1115,18 +961,18 @@ struct LambdaTableProducer { histos.fill(HIST("Events/h1f_collisions_info"), kPassSelCol); histos.fill(HIST("Events/h1f_collision_posZ"), collision.posZ()); + histos.fill(HIST("Events/h1f_collision_cent"), cent); - // Fill Collision Table lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), bc.timestamp()); - // initialize v0track objects ParticleType v0Type = kLambda; PrmScdType v0PrmScdType = kPrimary; float mass = 0., corr_fact = 1.; float prPx = 0., prPy = 0., prPz = 0.; + float pt = 0., eta = 0., rap = 0., phi = 0.; + std::size_t nWritten = 0; for (auto const& v0 : v0tracks) { - // check for corresponding MCGen Particle if constexpr (dmc == kMC) { histos.fill(HIST("Tracks/h1f_tracks_info"), kTracksBeforeHasMcParticle); if (!v0.has_mcParticle()) { @@ -1137,63 +983,47 @@ struct LambdaTableProducer { histos.fill(HIST("Tracks/h1f_tracks_info"), kAllV0Tracks); histos.fill(HIST("Tracks/h2f_armpod_before_sel"), v0.alpha(), v0.qtarm()); - // Select V0 Particle as Lambda/AntiLambda if (!selV0Particle(collision, v0, tracks, v0Type)) { continue; } - - // Select V0 Type Selection if (cV0TypeSelFlag && v0.v0Type() != cV0TypeSelection) { continue; } - // we have v0 as lambda histos.fill(HIST("Tracks/h1f_tracks_info"), kAllSelPassed); - // Remove lambda with ambiguous daughters (Only for run3) if constexpr (run == kRun3) { if (cRemoveAmbiguousTracks && hasAmbiguousDaughters(v0, tracks)) { continue; } } - // Get Lambda mass and kinematic variables mass = (v0Type == kLambda) ? v0.mLambda() : v0.mAntiLambda(); pt = v0.pt(); eta = v0.eta(); rap = v0.yLambda(); phi = v0.phi(); - // do MC analysis if constexpr (dmc == kMC) { histos.fill(HIST("Tracks/h2f_tracks_pid_before_sel"), v0.mcParticle().pdgCode(), v0.pt()); - - // Get Primary/Secondary Lambda if (cSelMCPSV0) { v0PrmScdType = isPrimaryV0(v0); } - - // check for true Lambda/Anti-Lambda if (cSelectTrueLambda && !selTrueMcRecLambda(v0, tracks)) { continue; } - - // get mothers information if (v0PrmScdType == kSecondary) { fillLambdaMothers(v0, tracks); } - histos.fill(HIST("Tracks/h1f_tracks_info"), kPassTrueLambdaSel); histos.fill(HIST("Tracks/h2f_tracks_pid_after_sel"), v0.mcParticle().pdgCode(), v0.pt()); - if (cRecoMomResoFlag) { auto mc = v0.template mcParticle_as(); pt = mc.pt(); eta = mc.eta(); rap = mc.y(); phi = mc.phi(); - float y = (cDoEtaAnalysis) ? eta : rap; - // apply kinematic selection (On Truth) + float y = cDoEtaAnalysis ? eta : rap; if (!kinCutSelection(pt, std::abs(y), cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { continue; } @@ -1201,162 +1031,55 @@ struct LambdaTableProducer { } histos.fill(HIST("Tracks/h2f_armpod_after_sel"), v0.alpha(), v0.qtarm()); - - // get correction factors corr_fact = (v0Type == kLambda) ? getCorrectionFactors(v0) : getCorrectionFactors(v0); - // fill lambda qa if (v0Type == kLambda) { - // Assign proton Eta Phi - prPx = v0.template posTrack_as().px(); - prPy = v0.template posTrack_as().py(); - prPz = v0.template posTrack_as().pz(); + prPx = v0.pxpos(); + prPy = v0.pypos(); + prPz = v0.pzpos(); histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), mass, v0.pt()); fillLambdaQAHistos(collision, v0, tracks); fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } else { - // Assign proton Eta Phi - prPx = v0.template negTrack_as().px(); - prPy = v0.template negTrack_as().py(); - prPz = v0.template negTrack_as().pz(); + prPx = v0.pxneg(); + prPy = v0.pyneg(); + prPz = v0.pzneg(); histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), mass, v0.pt()); fillLambdaQAHistos(collision, v0, tracks); fillKinematicHists(v0.pt(), v0.eta(), v0.yLambda(), v0.phi()); } - // Fill Lambda/AntiLambda Table - lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.px(), v0.py(), v0.pz(), - pt, eta, phi, rap, mass, prPx, prPy, prPz, - v0.template posTrack_as().index(), v0.template negTrack_as().index(), - v0.v0cosPA(), v0.dcaV0daughters(), (int8_t)v0Type, v0PrmScdType, corr_fact); + lambdaTrackTable(lambdaCollisionTable.lastIndex(), v0.px(), v0.py(), + v0.pz(), pt, eta, phi, rap, mass, prPx, prPy, prPz, + v0.template posTrack_as().index(), + v0.template negTrack_as().index(), + v0.v0cosPA(), + v0.dcaV0daughters(), (int8_t)v0Type, + v0PrmScdType, + corr_fact); + ++nWritten; } - // ------------------------------------------------------------------------- - // Fake Lambda reconstruction for combinatorial background (LS pairs) - // ------------------------------------------------------------------------- - // subsample before writing to the output table. - std::vector fakeCandBuffer; - fakeCandBuffer.reserve(512); - - for (auto const& tr1 : tracks) { - if (!selTrackBasic(tr1)) { - continue; - } - - for (auto const& tr2 : tracks) { - if (tr2.index() <= tr1.index()) { - continue; - } - - if (!selTrackBasic(tr2)) { - continue; - } - - if (tr1.sign() != tr2.sign()) { - continue; - } - - if (cFakeDaughterMaxDEta > 0.f && - std::abs(tr1.eta() - tr2.eta()) > cFakeDaughterMaxDEta) { - continue; - } - if (cFakeDaughterMaxOpenAngle > 0.f) { - float cosOA = (tr1.px() * tr2.px() + tr1.py() * tr2.py() + tr1.pz() * tr2.pz()) / - (std::sqrt(tr1.px() * tr1.px() + tr1.py() * tr1.py() + tr1.pz() * tr1.pz() + 1e-10f) * - std::sqrt(tr2.px() * tr2.px() + tr2.py() * tr2.py() + tr2.pz() * tr2.pz() + 1e-10f)); - if (std::acos(std::clamp(cosOA, -1.f, 1.f)) > cFakeDaughterMaxOpenAngle) { - continue; - } - } - - float fkPx = tr1.px() + tr2.px(); - float fkPy = tr1.py() + tr2.py(); - float fkPz = tr1.pz() + tr2.pz(); - float fkPt = std::sqrt(fkPx * fkPx + fkPy * fkPy); - float fkEta = RecoDecay::eta(std::array{fkPx, fkPy, fkPz}); - float fkPhi = RecoDecay::phi(fkPx, fkPy); - - ParticleType fakeType = (tr1.sign() > 0) ? kFakeLambda : kFakeAntiLambda; - - auto bufferFakeCandidate = [&](float prPxL, float prPyL, float prPzL, float fkMass, int64_t prId, int64_t piId) { - float fkRap = RecoDecay::y(std::array{fkPx, fkPy, fkPz}, fkMass); - float fkRapEta = cDoEtaAnalysis ? std::abs(fkEta) : std::abs(fkRap); - if (!kinCutSelection(fkPt, fkRapEta, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) - return; - fakeCandBuffer.push_back({fkPx, fkPy, fkPz, fkPt, fkEta, fkPhi, fkRap, fkMass, prPxL, prPyL, prPzL, prId, piId, fakeType}); - }; - - // Assignment A: tr1 = proton candidate, tr2 = pion candidate - if (std::abs(tr1.tpcNSigmaPr()) < cTpcNsigmaCut && - std::abs(tr2.tpcNSigmaPi()) < cTpcNsigmaCut && - selTrackBasicWithDCA(tr1, true /*isProton*/) && - selTrackBasicWithDCA(tr2, false /*isPion*/)) { - std::array pPr = {tr1.px(), tr1.py(), tr1.pz()}; - std::array pPi = {tr2.px(), tr2.py(), tr2.pz()}; - float fkMassA = RecoDecay::m(std::array{pPr, pPi}, std::array{MassProton, MassPionCharged}); - if (fkMassA >= cMinV0Mass && fkMassA <= cMaxV0Mass) { - bufferFakeCandidate(tr1.px(), tr1.py(), tr1.pz(), fkMassA, tr1.index(), tr2.index()); - } - } - - // Assignment B: tr2 = proton candidate, tr1 = pion candidate - if (cAllowDualAssignment && - std::abs(tr2.tpcNSigmaPr()) < cTpcNsigmaCut && - std::abs(tr1.tpcNSigmaPi()) < cTpcNsigmaCut && - selTrackBasicWithDCA(tr2, true /*isProton*/) && - selTrackBasicWithDCA(tr1, false /*isPion*/)) { - std::array pPr = {tr2.px(), tr2.py(), tr2.pz()}; - std::array pPi = {tr1.px(), tr1.py(), tr1.pz()}; - float fkMassB = RecoDecay::m(std::array{pPr, pPi}, std::array{MassProton, MassPionCharged}); - if (fkMassB >= cMinV0Mass && fkMassB <= cMaxV0Mass) { - bufferFakeCandidate(tr2.px(), tr2.py(), tr2.pz(), fkMassB, tr2.index(), tr1.index()); - } - } - } // end tr2 loop - } // end tr1 loop - - int nFakeTotal = static_cast(fakeCandBuffer.size()); - if (cMaxFakeLambdaPerEvent > 0 && nFakeTotal > cMaxFakeLambdaPerEvent) { - if (cRandomizeFakeSubsample) { - static thread_local std::mt19937 rng{std::random_device{}()}; - std::shuffle(fakeCandBuffer.begin(), fakeCandBuffer.end(), rng); - } - fakeCandBuffer.resize(static_cast(cMaxFakeLambdaPerEvent)); + if (nWritten >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kEvOneV0); } - int nFakeKept = static_cast(fakeCandBuffer.size()); - if (nFakeTotal > 0) { - histos.fill(HIST("Tracks/h1f_fake_subsample_fraction"), static_cast(nFakeKept) / static_cast(nFakeTotal)); + if (nWritten >= NCandidatesForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kEvTwoV0); } + } - for (auto const& fc : fakeCandBuffer) { - if (fc.type == kFakeLambda) { - histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), fc.mass, fc.pt); - } else { - histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), fc.mass, fc.pt); - } - lambdaTrackTable(lambdaCollisionTable.lastIndex(), - fc.px, fc.py, fc.pz, fc.pt, fc.eta, fc.phi, fc.rap, fc.mass, - fc.prPx, fc.prPy, fc.prPz, - fc.id1, fc.id2, - -999.f, -999.f, (int8_t)fc.type, kPrimary, 1.f); - } - } // end fillLambdaRecoTables - - // MC Generater Level Tables - template - void fillLambdaMcGenTables(C const& mcCollision, M const& mcParticles) + template + void fillLambdaMcGenTables(B const& bc, C const& mcCollision, M const& mcParticles) { - // Fill McGen Collision Table - lambdaMCGenCollisionTable(cent, mult, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + lambdaMCGenCollisionTable(cent, mult, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + bc.timestamp()); - // initialize track objects ParticleType v0Type = kLambda; PrmScdType v0PrmScdType = kPrimary; float rap = 0.; float prPx = 0., prPy = 0., prPz = 0.; for (auto const& mcpart : mcParticles) { - // check for Lambda first if (mcpart.pdgCode() == kLambda0) { v0Type = kLambda; } else if (mcpart.pdgCode() == kLambda0Bar) { @@ -1365,28 +1088,14 @@ struct LambdaTableProducer { continue; } - // check for Primary Lambda/AntiLambda - if (mcpart.isPhysicalPrimary()) { - v0PrmScdType = kPrimary; - } else { - v0PrmScdType = kSecondary; - } - - // Decide Eta/Rap - if (!cDoEtaAnalysis) { - rap = mcpart.y(); - } else { - rap = mcpart.eta(); - } - - // Apply Kinematic Acceptance - if (!kinCutSelection(mcpart.pt(), std::abs(rap), cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + v0PrmScdType = mcpart.isPhysicalPrimary() ? kPrimary : kSecondary; + rap = cDoEtaAnalysis ? mcpart.eta() : mcpart.y(); + if (!kinCutSelection(mcpart.pt(), std::abs(rap), cMinV0Pt, cMaxV0Pt, + cMaxV0Rap)) { continue; } - histos.fill(HIST("Tracks/h1f_tracks_info"), kGenTotAccLambda); - // get daughter track info and check for decay channel flag if (!mcpart.has_daughters()) { histos.fill(HIST("Tracks/h1f_tracks_info"), kGenLambdaNoDau); continue; @@ -1406,85 +1115,97 @@ struct LambdaTableProducer { vDauPy.push_back(dautrack.py()); vDauPz.push_back(dautrack.pz()); } - if (cGenDecayChannel) { // check decay channel - if (v0Type == kLambda) { - if (daughterPDGs[0] != kProton || daughterPDGs[1] != kPiMinus) { - continue; - } - } else if (v0Type == kAntiLambda) { - if (daughterPDGs[0] != kProtonBar || daughterPDGs[1] != kPiPlus) { - continue; - } + + int prIdx = -1, piIdx = -1; + int const prPdg = (v0Type == kLambda) ? kProton : kProtonBar; + int const piPdg = (v0Type == kLambda) ? kPiMinus : kPiPlus; + for (std::size_t i = 0; i < daughterPDGs.size(); ++i) { + if (daughterPDGs[i] == prPdg) { + prIdx = static_cast(i); + } else if (daughterPDGs[i] == piPdg) { + piIdx = static_cast(i); } } - + if (prIdx < 0 || piIdx < 0) { + continue; + } + if (cGenDecayChannel && daughterPDGs.size() != NDaughtersTwoBody) { + continue; + } histos.fill(HIST("Tracks/h1f_tracks_info"), kGenLambdaToPrPi); + prPx = vDauPx[prIdx]; + prPy = vDauPy[prIdx]; + prPz = vDauPz[prIdx]; + + int const posDauIdx = (v0Type == kLambda) ? prIdx : piIdx; + int const negDauIdx = (v0Type == kLambda) ? piIdx : prIdx; + if (v0Type == kLambda) { - // Assign proton p-vec - prPx = vDauPx[0]; - prPy = vDauPy[0]; - prPz = vDauPz[0]; - histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), daughterPDGs[0]); - histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), daughterPDGs[1]); + histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), daughterPDGs[prIdx]); + histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), daughterPDGs[piIdx]); histos.fill(HIST("McGen/h1f_lambda_daughter_PDG"), mcpart.pdgCode()); - histos.fill(HIST("McGen/Lambda/Proton/hPt"), vDauPt[0]); - histos.fill(HIST("McGen/Lambda/Proton/hEta"), vDauEta[0]); - histos.fill(HIST("McGen/Lambda/Proton/hRap"), vDauRap[0]); - histos.fill(HIST("McGen/Lambda/Proton/hPhi"), vDauPhi[0]); - histos.fill(HIST("McGen/Lambda/Pion/hPt"), vDauPt[1]); - histos.fill(HIST("McGen/Lambda/Pion/hEta"), vDauEta[1]); - histos.fill(HIST("McGen/Lambda/Pion/hRap"), vDauRap[1]); - histos.fill(HIST("McGen/Lambda/Pion/hPhi"), vDauPhi[1]); + histos.fill(HIST("McGen/Lambda/Proton/hPt"), vDauPt[prIdx]); + histos.fill(HIST("McGen/Lambda/Proton/hEta"), vDauEta[prIdx]); + histos.fill(HIST("McGen/Lambda/Proton/hRap"), vDauRap[prIdx]); + histos.fill(HIST("McGen/Lambda/Proton/hPhi"), vDauPhi[prIdx]); + histos.fill(HIST("McGen/Lambda/Pion/hPt"), vDauPt[piIdx]); + histos.fill(HIST("McGen/Lambda/Pion/hEta"), vDauEta[piIdx]); + histos.fill(HIST("McGen/Lambda/Pion/hRap"), vDauRap[piIdx]); + histos.fill(HIST("McGen/Lambda/Pion/hPhi"), vDauPhi[piIdx]); fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } else { - // Assign anti-proton p-vec - prPx = vDauPx[1]; - prPy = vDauPy[1]; - prPz = vDauPz[1]; - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[0]); - histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[1]); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[prIdx]); + histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), daughterPDGs[piIdx]); histos.fill(HIST("McGen/h1f_antilambda_daughter_PDG"), mcpart.pdgCode()); - histos.fill(HIST("McGen/AntiLambda/Pion/hPt"), vDauPt[0]); - histos.fill(HIST("McGen/AntiLambda/Pion/hEta"), vDauEta[0]); - histos.fill(HIST("McGen/AntiLambda/Pion/hRap"), vDauRap[0]); - histos.fill(HIST("McGen/AntiLambda/Pion/hPhi"), vDauPhi[0]); - histos.fill(HIST("McGen/AntiLambda/Proton/hPt"), vDauPt[1]); - histos.fill(HIST("McGen/AntiLambda/Proton/hEta"), vDauEta[1]); - histos.fill(HIST("McGen/AntiLambda/Proton/hRap"), vDauRap[1]); - histos.fill(HIST("McGen/AntiLambda/Proton/hPhi"), vDauPhi[1]); + histos.fill(HIST("McGen/AntiLambda/Proton/hPt"), vDauPt[prIdx]); + histos.fill(HIST("McGen/AntiLambda/Proton/hEta"), vDauEta[prIdx]); + histos.fill(HIST("McGen/AntiLambda/Proton/hRap"), vDauRap[prIdx]); + histos.fill(HIST("McGen/AntiLambda/Proton/hPhi"), vDauPhi[prIdx]); + histos.fill(HIST("McGen/AntiLambda/Pion/hPt"), vDauPt[piIdx]); + histos.fill(HIST("McGen/AntiLambda/Pion/hEta"), vDauEta[piIdx]); + histos.fill(HIST("McGen/AntiLambda/Pion/hRap"), vDauRap[piIdx]); + histos.fill(HIST("McGen/AntiLambda/Pion/hPhi"), vDauPhi[piIdx]); fillKinematicHists(mcpart.pt(), mcpart.eta(), mcpart.y(), mcpart.phi()); } - // Fill Lambda McGen Table - lambdaMCGenTrackTable(lambdaMCGenCollisionTable.lastIndex(), mcpart.px(), mcpart.py(), mcpart.pz(), - mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), RecoDecay::m(mcpart.p(), mcpart.e()), prPx, prPy, prPz, - daughterIDs[0], daughterIDs[1], (int8_t)v0Type, -999., -999., v0PrmScdType, 1.); + lambdaMCGenTrackTable( + lambdaMCGenCollisionTable.lastIndex(), mcpart.px(), mcpart.py(), + mcpart.pz(), mcpart.pt(), mcpart.eta(), mcpart.phi(), mcpart.y(), + RecoDecay::m(mcpart.p(), mcpart.e()), prPx, prPy, prPz, + daughterIDs[posDauIdx], daughterIDs[negDauIdx], (int8_t)v0Type, -999., + -999., v0PrmScdType, 1.); } } - template - void analyzeMcRecoGen(M const& mcCollision, C const& collisions, V const& V0s, T const& /*tracks*/, P const& mcParticles) + template + void analyzeMcRecoGen(M const& mcCollision, C const& collisions, B const&, V const& V0s, T const& tracks, P const& mcParticles) { - // Number of Rec Collisions Associated to the McGen Collision int nRecCols = collisions.size(); if (nRecCols != 0) { histos.fill(HIST("McGen/h1f_collision_recgen"), nRecCols); } - // Do not analyze if more than one reco collision is accociated to one mc gen collision if (nRecCols != 1) { return; } + histos.fill(HIST("McGen/h1f_collisions_info"), kTotCol); - // Check the reco collision - if (!collisions.begin().has_mcCollision() || !selCollision(collisions.begin()) || collisions.begin().mcCollisionId() != mcCollision.globalIndex()) { + if (!collisions.begin().has_mcCollision() || + !selCollision(collisions.begin()) || + collisions.begin().mcCollisionId() != mcCollision.globalIndex()) { return; } histos.fill(HIST("McGen/h1f_collisions_info"), kPassSelCol); - histos.fill(HIST("McGen/h2f_collision_posZ"), mcCollision.posZ(), collisions.begin().posZ()); - auto v0Tracks = V0s.sliceBy(perCollision, collisions.begin().globalIndex()); - // fillLambdaRecoTables(collisions.begin(), v0Tracks, tracks); - fillLambdaMcGenTables(mcCollision, mcParticles); + histos.fill(HIST("McGen/h2f_collision_posZ"), mcCollision.posZ(), + collisions.begin().posZ()); + + auto const& recCollision = collisions.begin(); + auto bc = recCollision.template bc_as(); + auto v0sThisCollision = + V0s.sliceBy(perCollision, recCollision.globalIndex()); + + fillLambdaRecoTables(recCollision, bc, v0sThisCollision, tracks); + fillLambdaMcGenTables(bc, mcCollision, mcParticles); } SliceCache cache; @@ -1492,8 +1213,11 @@ struct LambdaTableProducer { using CollisionsRun3 = soa::Join; using CollisionsRun2 = soa::Join; - using Tracks = soa::Join; - using TracksRun2 = soa::Join; + using Tracks = soa::Join; + using TracksRun2 = soa::Join; using TracksMC = soa::Join; using TracksMCRun2 = soa::Join; using McV0Tracks = soa::Join; @@ -1503,82 +1227,160 @@ struct LambdaTableProducer { auto bc = collision.bc_as(); fillLambdaRecoTables(collision, bc, V0s, tracks); } - PROCESS_SWITCH(LambdaTableProducer, processDataRun3, "Process for Run3 DATA", true); - void processMCRun3(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - McV0Tracks const& V0s, TracksMC const& tracks, - aod::McParticles const& mcParticles) + void processMCRun3( + aod::McCollisions::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::BCsWithTimestamps const& bcts, McV0Tracks const& V0s, + TracksMC const& tracks, aod::McParticles const& mcParticles) { - analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); + analyzeMcRecoGen(mcCollision, collisions, bcts, V0s, tracks, mcParticles); } - PROCESS_SWITCH(LambdaTableProducer, processMCRun3, "Process for Run3 MC RecoGen", false); - void processMCRun2(aod::McCollisions::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - McV0Tracks const& V0s, TracksMCRun2 const& tracks, - aod::McParticles const& mcParticles) + template + bool selLooseCandidate(T const& cand) { - analyzeMcRecoGen(mcCollision, collisions, V0s, tracks, mcParticles); - } - - PROCESS_SWITCH(LambdaTableProducer, processMCRun2, "Process for Run2 MC RecoGen", false); -}; + if (cKshortRejFlag && (std::abs(cand.mK0Short() - MassK0Short) <= cKshortRejMassWindow)) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0KShortMassRej); + return false; + } -struct LambdaTracksExtProducer { + auto passMassAndPid = [&](float mass, float tpcNSigmaPr, float tpcNSigmaPi) { + return (mass > cMinV0Mass && mass < cMaxV0Mass) && (std::abs(tpcNSigmaPr) < cTpcNsigmaCut && std::abs(tpcNSigmaPi) < cTpcNsigmaCut); + }; + const bool isLambda = cand.v0Type() == kLambda; + const bool ownFlag = isLambda ? passMassAndPid(cand.mass(), cand.posNSigmaPr(), cand.negNSigmaPi()) : passMassAndPid(cand.mass(), cand.negNSigmaPr(), cand.posNSigmaPi()); + const bool otherFlag = isLambda ? passMassAndPid(cand.massOther(), cand.negNSigmaPr(), cand.posNSigmaPi()) : passMassAndPid(cand.massOther(), cand.posNSigmaPr(), cand.negNSigmaPi()); + if (!ownFlag) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kNotLambdaNotAntiLambda); + return false; + } + if (otherFlag) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsBothLambdaAntiLambda); + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kV0IsLambdaOrAntiLambda); - Produces lambdaTrackExtTable; + if (cand.posTpcCrossedRows() <= cMinTpcCrossedRows || cand.negTpcCrossedRows() <= cMinTpcCrossedRows) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0DauTrackSel); - // Configurables - Configurable cAcceptAllLambda{"cAcceptAllLambda", false, "Accept all Lambda"}; - Configurable cRejAllLambdaShaDau{"cRejAllLambdaShaDau", true, "Reject all Lambda sharing daughters"}; - Configurable cSelLambdaMassPdg{"cSelLambdaMassPdg", false, "Select Lambda closest to Pdg Mass"}; - Configurable cSelLambdaTScore{"cSelLambdaTScore", false, "Select Lambda based on t-score"}; - Configurable cA{"cA", 0.6, "a * |lambdaMass - lambdaPdgMass|"}; - Configurable cB{"cB", 0.6, "b * DcaPrPi"}; - Configurable cC{"cC", 0.6, "c * Cos(theta_{PA})"}; + float rap = cDoEtaAnalysis ? std::abs(cand.eta()) : std::abs(cand.rap()); + if (!kinCutSelection(cand.pt(), rap, cMinV0Pt, cMaxV0Pt, cMaxV0Rap)) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0KinCuts); - // Histogram Registry. - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + if (cand.dcaDau() <= cMinV0DcaDaughters || cand.dcaDau() >= cMaxV0DcaDaughters) { + return false; + } + if (cand.dcaV0ToPV() <= cMinDcaV0ToPV || cand.dcaV0ToPV() >= cMaxDcaV0ToPV) { + return false; + } + if (cand.cTau() <= cMinV0CTau || cand.cTau() >= cMaxV0CTau) { + return false; + } + if (cand.cosPA() <= cMinV0CosPA) { + return false; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kPassV0TopoSel); + return true; + } - void init(InitContext const&) + void processDerivedData(aod::LambdaLooseCols::iterator const& collision, aod::LambdaLooseTracks const& looseTracks) { - // Axis Specifications - const AxisSpec axisMult(10, 0, 10); - const AxisSpec axisMass(100, 1.06, 1.16, "Inv Mass (GeV/#it{c}^{2})"); - const AxisSpec axisCPA(100, 0.995, 1.0, "cos(#theta_{PA})"); - const AxisSpec axisDcaDau(75, 0., 1.5, "Daug DCA (#sigma)"); - const AxisSpec axisDEta(320, -1.6, 1.6, "#Delta#eta"); - const AxisSpec axisDPhi(640, -PIHalf, 3. * PIHalf, "#Delta#varphi"); - - // Histograms Booking - histos.add("h1i_totlambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h1i_totantilambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h1i_lambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h1i_antilambda_mult", "Multiplicity", kTH1I, {axisMult}); - histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{SharePair} #Lambda#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaM_LaP", "#rho_{2}^{SharePair} #bar{#Lambda}#Lambda", kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{SharePair} #Lambda#Lambda", kTH2D, {axisDEta, axisDPhi}); - histos.add("h2d_n2_etaphi_LaM_LaM", "#rho_{2}^{SharePair} #bar{#Lambda}#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); - - // InvMass, DcaDau and CosPA - histos.add("Reco/h1f_lambda_invmass", "M_{p#pi}", kTH1F, {axisMass}); - histos.add("Reco/h1f_lambda_cospa", "cos(#theta_{PA})", kTH1F, {axisCPA}); - histos.add("Reco/h1f_lambda_dcadau", "DCA_{p#pi} at V0 Decay Vertex", kTH1F, {axisDcaDau}); - histos.add("Reco/h1f_antilambda_invmass", "M_{p#pi}", kTH1F, {axisMass}); - histos.add("Reco/h1f_antilambda_cospa", "cos(#theta_{PA})", kTH1F, {axisCPA}); - histos.add("Reco/h1f_antilambda_dcadau", "DCA_{p#pi} at V0 Decay Vertex", kTH1F, {axisDcaDau}); + histos.fill(HIST("Events/h1f_collisions_info"), kTotCol); + cent = collision.cent(); + mult = collision.mult(); + if (cent <= cMinMult || cent >= cMaxMult) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvCentrality); + if (collision.posZ() <= cMinZVtx || collision.posZ() >= cMaxZVtx) { + return; + } + histos.fill(HIST("Events/hEventCutFlow"), kEvVz); + + histos.fill(HIST("Events/h1f_collisions_info"), kPassSelCol); + histos.fill(HIST("Events/h1f_collision_posZ"), collision.posZ()); + histos.fill(HIST("Events/h1f_collision_cent"), cent); + + lambdaCollisionTable(cent, mult, collision.posX(), collision.posY(), collision.posZ(), collision.timeStamp()); + + std::size_t nWritten = 0; + for (auto const& cand : looseTracks) { + histos.fill(HIST("Tracks/h1f_tracks_info"), kAllV0Tracks); + if (!selLooseCandidate(cand)) { + continue; + } + histos.fill(HIST("Tracks/h1f_tracks_info"), kAllSelPassed); + + if (cand.v0Type() == kLambda) { + histos.fill(HIST("Tracks/h1f_lambda_pt_vs_invm"), cand.mass(), cand.pt()); + fillKinematicHists(cand.pt(), cand.eta(), cand.rap(), cand.phi()); + } else { + histos.fill(HIST("Tracks/h1f_antilambda_pt_vs_invm"), cand.mass(), cand.pt()); + fillKinematicHists(cand.pt(), cand.eta(), cand.rap(), cand.phi()); + } + + lambdaTrackTable(lambdaCollisionTable.lastIndex(), cand.px(), cand.py(), + cand.pz(), cand.pt(), cand.eta(), cand.phi(), cand.rap(), cand.mass(), + cand.prPx(), cand.prPy(), cand.prPz(), + static_cast(cand.posTrackId()), + static_cast(cand.negTrackId()), + cand.cosPA(), cand.dcaDau(), cand.v0Type(), + static_cast(kPrimary), 1.f); + ++nWritten; + } + + if (nWritten >= 1) { + histos.fill(HIST("Events/hEventCutFlow"), kEvOneV0); + } + if (nWritten >= NCandidatesForPair) { + histos.fill(HIST("Events/hEventCutFlow"), kEvTwoV0); + } + } + PROCESS_SWITCH(LambdaTableProducer, processDerivedData, "Process the derived data of lambda-spin-polarization-loose-producer", false); +}; +struct LambdaTracksExtProducer { + + Produces lambdaTrackExtTable; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext const&) + { + const AxisSpec axisMult(10, 0, 10); + const AxisSpec axisMass(200, 1.08, 1.18, "M_{p#pi} (GeV/#it{c}^{2})"); + const AxisSpec axisCPA(100, 0.995, 1.0, "cos(#theta_{PA})"); + const AxisSpec axisDcaDau(75, 0., 1.5, "Daughter DCA (#sigma)"); + const AxisSpec axisDEta(320, -1.6, 1.6, "#Delta#eta"); + const AxisSpec axisDPhi(640, -PIHalf, 3. * PIHalf, "#Delta#varphi"); + + histos.add("h1i_totlambda_mult", "Multiplicity", kTH1I, {axisMult}); + histos.add("h1i_totantilambda_mult", "Multiplicity", kTH1I, {axisMult}); + histos.add("h2d_n2_etaphi_LaP_LaM", "#rho_{2}^{Share} #Lambda#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaM_LaP", "#rho_{2}^{Share} #bar{#Lambda}#Lambda", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaP_LaP", "#rho_{2}^{Share} #Lambda#Lambda", kTH2D, {axisDEta, axisDPhi}); + histos.add("h2d_n2_etaphi_LaM_LaM", "#rho_{2}^{Share} #bar{#Lambda}#bar{#Lambda}", kTH2D, {axisDEta, axisDPhi}); + + histos.add("Reco/h1f_lambda_invmass", "M_{#Lambda}", kTH1F, {axisMass}); + histos.add("Reco/h1f_lambda_cospa", "cos(PA)", kTH1F, {axisCPA}); + histos.add("Reco/h1f_lambda_dcadau", "DCA daughters", kTH1F, {axisDcaDau}); + histos.add("Reco/h1f_antilambda_invmass", "M_{#bar{#Lambda}}", kTH1F, {axisMass}); + histos.add("Reco/h1f_antilambda_cospa", "cos(PA)", kTH1F, {axisCPA}); + histos.add("Reco/h1f_antilambda_dcadau", "DCA daughters", kTH1F, {axisDcaDau}); histos.addClone("Reco/", "SharingDau/"); } template void fillHistos(T const& track) { - static constexpr std::string_view SubDir[] = {"Reco/", "SharingDau/"}; - + static constexpr std::array SubDir = {"Reco/", "SharingDau/"}; if (track.v0Type() == kLambda) { histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_invmass"), track.mass()); histos.fill(HIST(SubDir[sd]) + HIST("h1f_lambda_dcadau"), track.dcaDau()); @@ -1592,14 +1394,10 @@ struct LambdaTracksExtProducer { void process(aod::LambdaCollisions::iterator const&, aod::LambdaTracks const& tracks) { - - int nTotLambda = 0, nTotAntiLambda = 0, nSelLambda = 0, nSelAntiLambda = 0; + int nTotLambda = 0, nTotAntiLambda = 0; for (auto const& lambda : tracks) { - bool lambdaMinDeltaMassFlag = true, lambdaMinTScoreFlag = true; - bool lambdaSharingDauFlag = false, trueLambdaFlag = false; - std::vector vSharedDauLambdaIndex; - float tLambda = 0., tTrack = 0.; + bool lambdaSharingDauFlag = false; if (lambda.v0Type() == kLambda) { ++nTotLambda; @@ -1607,220 +1405,500 @@ struct LambdaTracksExtProducer { ++nTotAntiLambda; } - bool const isFake = (lambda.v0Type() == kFakeLambda || lambda.v0Type() == kFakeAntiLambda); - if (!isFake) { - tLambda = (cA * std::abs(lambda.mass() - MassLambda0)) + (cB * lambda.dcaDau()) + (cC * std::abs(lambda.cosPA() - 1.)); - } - for (auto const& track : tracks) { - // check lambda index (don't analyze same lambda track !!!) if (lambda.index() == track.index()) { continue; } - // check if lambda shares daughters with any other track if (lambda.posTrackId() == track.posTrackId() || lambda.negTrackId() == track.negTrackId()) { - vSharedDauLambdaIndex.push_back(track.index()); lambdaSharingDauFlag = true; - if (!isFake) { - if (lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaM_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } else if (lambda.v0Type() == kLambda && track.v0Type() == kLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { - histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); - } - } - - // decision based on mass closest to PdgMass of Lambda - if (std::abs(lambda.mass() - MassLambda0) > std::abs(track.mass() - MassLambda0)) { - lambdaMinDeltaMassFlag = false; + if (lambda.v0Type() == kLambda && track.v0Type() == kAntiLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaP_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaM_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kLambda && track.v0Type() == kLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaP_LaP"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); + } else if (lambda.v0Type() == kAntiLambda && track.v0Type() == kAntiLambda) { + histos.fill(HIST("h2d_n2_etaphi_LaM_LaM"), lambda.eta() - track.eta(), RecoDecay::constrainAngle((lambda.phi() - track.phi()), -PIHalf)); } - - // decisions based on t-score - // Guard: only compute tTrack for real V0 candidates (fakes have sentinel cosPA/dcaDau) - bool const isFakeTrack = (track.v0Type() == kFakeLambda || track.v0Type() == kFakeAntiLambda); - if (!isFake && !isFakeTrack) { - tTrack = (cA * std::abs(track.mass() - MassLambda0)) + (cB * track.dcaDau()) + (cC * std::abs(track.cosPA() - 1.)); - if (tLambda > tTrack) { - lambdaMinTScoreFlag = false; - } - } - } - } - - if (isFake) { - bool fakeAccepted = false; - if (cAcceptAllLambda) { - fakeAccepted = true; - } else if (cRejAllLambdaShaDau && !lambdaSharingDauFlag) { - fakeAccepted = true; - } - if (fakeAccepted) { - if (lambda.v0Type() == kFakeLambda) - ++nSelLambda; - else - ++nSelAntiLambda; } - lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, false /*trueLambdaFlag*/, fakeAccepted /*fakeFlag*/); - continue; } - // fill QA histograms (real V0s only) if (lambdaSharingDauFlag) { fillHistos(lambda); } else { fillHistos(lambda); } - if (cAcceptAllLambda) { // Accept all lambda - trueLambdaFlag = true; - } else if (cRejAllLambdaShaDau && !lambdaSharingDauFlag) { // Reject all lambda sharing daughter - trueLambdaFlag = true; - } else if (cSelLambdaMassPdg && lambdaMinDeltaMassFlag) { // Select lambda closest to pdg mass - trueLambdaFlag = true; - } else if (cSelLambdaTScore && lambdaMinTScoreFlag) { // Select lambda based on t-score - trueLambdaFlag = true; - } - - // Multiplicity of selected lambda - if (trueLambdaFlag) { - if (lambda.v0Type() == kLambda) { - ++nSelLambda; - } else if (lambda.v0Type() == kAntiLambda) { - ++nSelAntiLambda; - } - } - - // fill LambdaTrackExt table (fakeFlag = false for all real V0 candidates) - lambdaTrackExtTable(lambdaSharingDauFlag, vSharedDauLambdaIndex, trueLambdaFlag, false); + lambdaTrackExtTable(lambdaSharingDauFlag); } - // fill multiplicity histograms if (nTotLambda != 0) { histos.fill(HIST("h1i_totlambda_mult"), nTotLambda); } - if (nTotAntiLambda != 0) { histos.fill(HIST("h1i_totantilambda_mult"), nTotAntiLambda); } - - if (nSelLambda != 0) { - histos.fill(HIST("h1i_lambda_mult"), nSelLambda); - } - - if (nSelAntiLambda != 0) { - histos.fill(HIST("h1i_antilambda_mult"), nSelAntiLambda); - } } }; struct LambdaSpinPolarization { - // Table producer Produces lambdaMixEvtCol; Produces lambdaMixEvtTrk; + Produces lambdaMixEvtMGCol; + Produces lambdaMixEvtMGTrk; + + Configurable cMassAccMin{"cMassAccMin", 1.0956f, "Accepted mass min (GeV/c2); candidates outside the window are not paired"}; + Configurable cMassAccMax{"cMassAccMax", 1.1356f, "Accepted mass max (GeV/c2); candidates outside the window are not paired"}; + Configurable cMassHistMin{"cMassHistMin", 1.0806f, "Mass axes min (GeV/c2), below cMassAccMin"}; + Configurable cMassHistMax{"cMassHistMax", 1.1806f, "Mass axes max (GeV/c2), above cMassAccMax"}; + Configurable cNMassBins{"cNMassBins", 200, "Mass axes N bins"}; + ConfigurableAxis axisDeltaR{"axisDeltaR", {VARIABLE_WIDTH, 0.0, 0.4, 0.8, 1.2, 1.8, 2.4, 3.1, 3.5}, "DeltaR bins (last bin: outside the analysis windows)"}; + Configurable cNBinsCosTS{"cNBinsCosTS", 10, "N costheta* bins"}; + Configurable cSharedDauRule{"cSharedDauRule", 0, "Candidates sharing a daughter track: 0 = keep the largest set without shared tracks per cluster (ties: closest to the PDG mass), 1 = veto only the pairs sharing a track, 2 = drop every candidate sharing a track"}; + Configurable cMinCandPerEvent{"cMinCandPerEvent", 2, "Collisions with fewer reconstructed Lambda + AntiLambda candidates are skipped by the pair, mixing and derived-table processes (the event QA counts all collisions)"}; + + Configurable cMEPoolDepth{"cMEPoolDepth", 100000, "Rolling ME pool: depth in donor events per (centrality, Vz) bin"}; + Configurable cMEPoolMinEvents{"cMEPoolMinEvents", 20, "Rolling ME pool: events a pool must hold before its bin is mixed (warm-up)"}; + Configurable cMEPoolMinCand{"cMEPoolMinCand", 2, "Rolling ME pool: only events with at least this many accepted candidates donate"}; + ConfigurableAxis axisCentME{"axisCentME", {VARIABLE_WIDTH, 0, 10, 20, 40, 100}, "ME pool centrality bins"}; + ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -10., -7., -4., -2., 0, 2., 4., 7., 10.}, "ME pool vtxZ bins"}; + + Configurable cMaxDeltaPt{"cMaxDeltaPt", 0.1f, "Kinematic ME: max |deltaPt(SE)-deltaPt(ME)| (GeV/c)"}; + Configurable cMaxDeltaPhi{"cMaxDeltaPhi", 0.02f, "Kinematic ME: max |deltaPhi(SE)-deltaPhi(ME)| (rad)"}; + Configurable cMaxDeltaRap{"cMaxDeltaRap", 0.03f, "Kinematic ME: max |deltaRap(SE)-deltaRap(ME)|"}; + Configurable cFillSEInMixing{"cFillSEInMixing", true, "Kinematic ME: fill every same-event pair the mixing processes, matched or not (SEproc)"}; + + Configurable cFillKinWSpectra{"cFillKinWSpectra", true, "Kinematic ME: fill the leg-2 spectra of SEproc and ME (weight-map input, closure)"}; + Configurable cMinPt{"cMinPt", 0.6f, "Leg spectra and ME pool grid: pT min (GeV/c)"}; + Configurable cMaxPt{"cMaxPt", 3.0f, "Leg spectra and ME pool grid: pT max (GeV/c)"}; + Configurable cMinRap{"cMinRap", -0.5f, "Leg spectra and ME pool grid: rapidity min"}; + Configurable cMaxRap{"cMaxRap", 0.5f, "Leg spectra and ME pool grid: rapidity max"}; + Configurable cNKinWPtBins{"cNKinWPtBins", 0, "Leg spectra: pT bins (0: no wider than cMaxDeltaPt)"}; + Configurable cNKinWRapBins{"cNKinWRapBins", 0, "Leg spectra: rapidity bins (0: no wider than cMaxDeltaRap)"}; + Configurable cNKinWPhiBins{"cNKinWPhiBins", 0, "Leg spectra: phi bins (0: no wider than cMaxDeltaPhi)"}; + Configurable cApplyKinWeights{"cApplyKinWeights", false, "Kinematic ME: weight the leg-2 replacements with the maps from CCDB"}; + Configurable cKinWeightCcdbUrl{"cKinWeightCcdbUrl", "http://alice-ccdb.cern.ch", "Kinematic weights: CCDB url"}; + Configurable cKinWeightCcdbPath{"cKinWeightCcdbPath", "", "Kinematic weights: CCDB path of a TList with hKinW_ (axes of KinW/*/hLeg2_)"}; + Configurable cKinWeightCcdbTimestamp{"cKinWeightCcdbTimestamp", -1, "Kinematic weights: CCDB timestamp (-1: timestamp of the first mixed collision)"}; + + Configurable cFillClosePairQA{"cFillClosePairQA", true, "Close-pair QA of the same-event pairs: SE (processDataReco) and SEproc (processDataRecoMixed)"}; + Configurable cClosePairMassWindow{"cClosePairMassWindow", 0.004f, "Close-pair QA: both legs within |m - m_Lambda| < this (GeV/c2); <= 0: all pairs"}; + ConfigurableAxis axisClosePair{"axisClosePair", {100, -0.15, 0.15}, "Close-pair QA: bins of the daughter Delta eta, Delta y and Delta phi"}; + Configurable cFillSharedDauQA{"cFillSharedDauQA", true, "Shared-daughter QA: SE and SEproc pairs with a candidate sharing a daughter track with another candidate, and the cluster sizes"}; - // Global Configurables - Configurable cNPtBins{"cNPtBins", 30, "N pT Bins"}; - Configurable cMinPt{"cMinPt", 0.5, "pT Min"}; - Configurable cMaxPt{"cMaxPt", 4.5, "pT Max"}; - Configurable cNRapBins{"cNRapBins", 10, "N Rapidity Bins"}; - Configurable cMinRap{"cMinRap", -0.5, "Minimum Rapidity"}; - Configurable cMaxRap{"cMaxRap", 0.5, "Maximum Rapidity"}; - Configurable cNPhiBins{"cNPhiBins", 36, "N Phi Bins"}; - Configurable cNBinsCosTS{"cNBinsCosTS", 20, "N CosTS Bins"}; - Configurable cNBinsDeltaR{"cNBinsDeltaR", 20, "DeltaR Bins"}; - Configurable cInvBoostFlag{"cInvBoostFlag", true, "Inverse Boost Flag"}; - Configurable mixingParameter{"mixingParameter", 5, "How many events are mixed"}; - - Configurable cDoAtlasMethod{"cDoAtlasMethod", false, "Fill pair-boost histograms"}; - Configurable cDoStarMethod{"cDoStarMethod", true, "Fill lab-boost histograms"}; - - Configurable cMEMode{"cMEMode", 0, "ME mode: 0=standard, 1=sequential"}; - - // Centrality Axis - ConfigurableAxis cMultBins{"cMultBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 30.0f, 50.f, 80.0f, 100.f}, "Variable Mult-Bins"}; - ConfigurableAxis axisCentME{"axisCentME", {VARIABLE_WIDTH, 0, 10, 30, 50, 100}, "Mixing bins - centrality (%)"}; - ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -7, -3, 0, 3, 7}, "Mixing bins - z-vertex"}; - - Configurable cMaxDeltaPt{"cMaxDeltaPt", 0.1f, "Max delta pT for kinematic matching (GeV/c)"}; - Configurable cMaxDeltaPhi{"cMaxDeltaPhi", 0.1f, "Max delta phi for kinematic matching (rad)"}; - Configurable cMaxDeltaRap{"cMaxDeltaRap", 0.1f, "Max delta rapidity for kinematic matching"}; - - // Histogram Registry. HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Service ccdb{}; + + uint64_t pairOrderSeed = 0; + std::array, 4> kinWeightMaps{}; + std::vector legAxesRef; + bool kinWeightsLoaded = false; + + struct SharedDauCand { + int64_t globalIdx; + int64_t posId; + int64_t negId; + float dMass; + }; + static constexpr std::size_t MaxExactCluster = 16; + static constexpr int NoSharedTrackIdx = -1; + std::vector sdCands; + std::vector sdConflicted; + std::vector sdDropped; + + struct PoolTrack { + float mPx, mPy, mPz, mPt, mRap, mPhi, mMass; + float mPrPx, mPrPy, mPrPz; + int8_t mSpecies; + [[nodiscard]] float px() const { return mPx; } + [[nodiscard]] float py() const { return mPy; } + [[nodiscard]] float pz() const { return mPz; } + [[nodiscard]] float pt() const { return mPt; } + [[nodiscard]] float rap() const { return mRap; } + [[nodiscard]] float phi() const { return mPhi; } + [[nodiscard]] float mass() const { return mMass; } + [[nodiscard]] float prPx() const { return mPrPx; } + [[nodiscard]] float prPy() const { return mPrPy; } + [[nodiscard]] float prPz() const { return mPrPz; } + [[nodiscard]] int8_t species() const { return mSpecies; } + }; + + template + PoolTrack toPoolTrack(T const& trk) + { + return PoolTrack{trk.px(), trk.py(), trk.pz(), trk.pt(), trk.rap(), trk.phi(), trk.mass(), trk.prPx(), trk.prPy(), trk.prPz(), static_cast(trk.v0Type())}; + } + + class RollingPool + { + public: + struct Grid { + float ptMin = 0.f; + float rapMin = 0.f; + float cellPt = 1.f; + float cellRap = 1.f; + float cellPhi = TwoPI; + int nPt = 1; + int nRap = 1; + int nPhi = 1; + }; + + RollingPool() = default; + RollingPool(Grid const& g, int depth) : grid(g), maxEvents(depth) {} + + [[nodiscard]] std::size_t nEvents() const { return nPerEvent.size(); } + + void push(std::vector const& event) + { + for (auto const& cand : event) { + const int64_t seq = seqBase + static_cast(slots.size()); + slots.push_back(Slot{.cand = cand, .next = NoSeq}); + auto [it, isNew] = cells[cand.species()].try_emplace(cellKey(cand), Cell{.head = seq, .tail = seq}); + if (!isNew) { + slot(it->second.tail).next = seq; + it->second.tail = seq; + } + } + nPerEvent.push_back(static_cast(event.size())); + while (static_cast(nPerEvent.size()) > maxEvents) { + const int n = nPerEvent.front(); + for (int i = 0; i < n; ++i) { + retireOldest(); + } + nPerEvent.pop_front(); + } + } + + template + void findMatches(PoolTrack const& leg, F const& isMatch, std::vector& out) const + { + out.clear(); + int iPt = 0, iRap = 0, iPhi = 0; + cellCoords(leg, iPt, iRap, iPhi); + std::array phiCells{}; + int nPhiCells = 0; + for (int d = -1; d <= 1; ++d) { + const int k = ((iPhi + d) % grid.nPhi + grid.nPhi) % grid.nPhi; + if (std::find(phiCells.begin(), phiCells.begin() + nPhiCells, k) == phiCells.begin() + nPhiCells) { + phiCells[nPhiCells++] = k; + } + } + auto const& speciesCells = cells[leg.species()]; + for (int jPt = std::max(iPt - 1, 0); jPt <= std::min(iPt + 1, grid.nPt - 1); ++jPt) { + for (int jRap = std::max(iRap - 1, 0); jRap <= std::min(iRap + 1, grid.nRap - 1); ++jRap) { + for (int ip = 0; ip < nPhiCells; ++ip) { + const auto it = speciesCells.find(key(jPt, jRap, phiCells[ip])); + if (it == speciesCells.end()) { + continue; + } + for (int64_t seq = it->second.head; seq != NoSeq;) { + Slot const& sl = slot(seq); + if (isMatch(sl.cand)) { + out.push_back(&sl.cand); + } + seq = sl.next; + } + } + } + } + } + + private: + static constexpr int64_t NoSeq = -1; + struct Slot { + PoolTrack cand; + int64_t next; + }; + struct Cell { + int64_t head; + int64_t tail; + }; + + Slot& slot(int64_t seq) { return slots[static_cast(seq - seqBase)]; } + [[nodiscard]] Slot const& slot(int64_t seq) const { return slots[static_cast(seq - seqBase)]; } + + void retireOldest() + { + Slot const& old = slots.front(); + auto& speciesCells = cells[old.cand.species()]; + auto it = speciesCells.find(cellKey(old.cand)); + if (it == speciesCells.end() || it->second.head != seqBase) { + LOGF(fatal, "RollingPool: grid index out of step with the candidate queue"); + return; + } + if (old.next == NoSeq) { + speciesCells.erase(it); + } else { + it->second.head = old.next; + } + slots.pop_front(); + ++seqBase; + } - // Global variables - float cent = 0.; + [[nodiscard]] int64_t key(int iPt, int iRap, int iPhi) const + { + return (static_cast(iPt) * grid.nRap + iRap) * grid.nPhi + iPhi; + } + + void cellCoords(PoolTrack const& c, int& iPt, int& iRap, int& iPhi) const + { + iPt = std::clamp(static_cast(std::floor((c.pt() - grid.ptMin) / grid.cellPt)), 0, grid.nPt - 1); + iRap = std::clamp(static_cast(std::floor((c.rap() - grid.rapMin) / grid.cellRap)), 0, grid.nRap - 1); + iPhi = std::clamp(static_cast(std::floor(RecoDecay::constrainAngle(c.phi(), 0.f) / grid.cellPhi)), 0, grid.nPhi - 1); + } + + [[nodiscard]] int64_t cellKey(PoolTrack const& c) const + { + int iPt = 0, iRap = 0, iPhi = 0; + cellCoords(c, iPt, iRap, iPhi); + return key(iPt, iRap, iPhi); + } + + Grid grid{}; + int maxEvents = 0; + std::deque slots; + std::deque nPerEvent; + int64_t seqBase = 0; + std::array, 2> cells{}; + }; + + std::vector mePools; + std::vector mePoolsGen; + TAxis mePoolAxisCent; + TAxis mePoolAxisVz; void init(InitContext const&) { - const AxisSpec axisCheck(1, 0, 1, ""); - const AxisSpec axisPosZ(220, -7, 7, "V_{z} (cm)"); - const AxisSpec axisCent(cMultBins, "FT0M (%)"); - const AxisSpec axisChMult(200, 0, 200, "N_{ch}"); - const AxisSpec axisMult(10, 0, 10, "N_{#Lambda}"); - const AxisSpec axisMass(100, 1.08, 1.2, "M_{#Lambda} (GeV/#it{c}^{2})"); - const AxisSpec axisPt(cNPtBins, cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); - const AxisSpec axisEta(cNRapBins, cMinRap, cMaxRap, "#eta"); - const AxisSpec axisRap(cNRapBins, cMinRap, cMaxRap, "y"); - const AxisSpec axisPhi(cNPhiBins, 0., TwoPI, "#varphi (rad)"); - const AxisSpec axisDRap(2 * cNRapBins, cMinRap - cMaxRap, cMaxRap - cMinRap, "#Deltay"); - const AxisSpec axisDPhi(cNPhiBins, -PI, PI, "#Delta#varphi"); + const AxisSpec axisM1(cNMassBins, cMassHistMin, cMassHistMax, "M_{1} (GeV/#it{c}^{2})"); + const AxisSpec axisM2(cNMassBins, cMassHistMin, cMassHistMax, "M_{2} (GeV/#it{c}^{2})"); + const AxisSpec axisDR(axisDeltaR, "#DeltaR"); const AxisSpec axisCosTS(cNBinsCosTS, -1, 1, "cos(#theta*)"); - const AxisSpec axisDR(cNBinsDeltaR, 0, 4, "#DeltaR"); + const std::vector pairAxes = {axisM1, axisM2, axisDR, axisCosTS}; - // Pool occupancy - histos.add("QA/ME/hPoolCentVz", "ME pool occupancy;centrality (%);V_{z} (cm)", kTH2F, {axisCentME, axisVtxZME}); + auto nBins = [](int configured, float range, float window) { + if (configured > 0) { + return configured; + } + return window > 0.f ? static_cast(std::ceil(range / window)) : 1; + }; + const AxisSpec axisLegPt(nBins(cNKinWPtBins, cMaxPt - cMinPt, cMaxDeltaPt), cMinPt, cMaxPt, "p_{T} (GeV/#it{c})"); + const AxisSpec axisLegRap(nBins(cNKinWRapBins, cMaxRap - cMinRap, cMaxDeltaRap), cMinRap, cMaxRap, "y"); + const AxisSpec axisLegPhi(nBins(cNKinWPhiBins, TwoPI, cMaxDeltaPhi), 0., TwoPI, "#varphi (rad)"); + const std::vector legAxes = {axisLegPt, axisLegRap, axisLegPhi}; + legAxesRef.clear(); + for (auto const& spec : legAxes) { + const std::vector edges = axisEdges(spec); + legAxesRef.emplace_back(static_cast(edges.size()) - 1, edges.data()); + } + + const bool massAxesContainWindow = cMassHistMin.value < cMassAccMin.value && cMassAccMax.value < cMassHistMax.value; + if (!massAxesContainWindow) { + LOGF(fatal, "The mass axes [%.4f, %.4f] must be wider than the accepted mass window [%.4f, %.4f]", cMassHistMin.value, cMassHistMax.value, cMassAccMin.value, cMassAccMax.value); + } + for (auto const& edge : {cMassAccMin.value, cMassAccMax.value}) { + if (!isOnMassBinEdge(edge)) { + LOGF(warning, "Accepted mass %.5f is not a bin edge of the mass axes [%.4f, %.4f] / %d", edge, cMassHistMin.value, cMassHistMax.value, cNMassBins.value); + } + } + if (cSharedDauRule.value < kSharedDauMaxSet || cSharedDauRule.value > kSharedDauDropAll) { + LOGF(fatal, "cSharedDauRule must be %d (largest set without shared tracks), %d (veto pairs sharing a track) or %d (drop every candidate sharing a track)", static_cast(kSharedDauMaxSet), static_cast(kSharedDauPairVeto), static_cast(kSharedDauDropAll)); + } + if (cMinCandPerEvent.value < 1) { + LOGF(fatal, "cMinCandPerEvent must be at least 1, it is %d", cMinCandPerEvent.value); + } - // Lambda multiplicity vs centrality - histos.add("QA/ME/hLambdaMultVsCent", "ME #Lambda multiplicity;centrality (%);N_{#Lambda}", kTH2F, {axisCentME, {50, 0, 50}}); - histos.add("QA/ME/hAntiLambdaMultVsCent", "ME #bar{#Lambda} multiplicity;centrality (%);N_{#bar{#Lambda}}", kTH2F, {axisCentME, {50, 0, 50}}); + if (doprocessDataRecoMixed || doprocessMcGenMixed) { + const std::vector centEdges = axisEdges(AxisSpec(axisCentME)); + const std::vector vzEdges = axisEdges(AxisSpec(axisVtxZME)); + mePoolAxisCent.Set(static_cast(centEdges.size()) - 1, centEdges.data()); + mePoolAxisVz.Set(static_cast(vzEdges.size()) - 1, vzEdges.data()); - // inv mass vs pt: four separate signal pair types - histos.add("Reco/h2f_n2_mass_LaPLaM", "m_{inv}^{#Lambda} vs m_{inv}^{#bar{#Lambda}}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); - histos.add("Reco/h2f_n2_mass_LaMLaP", "m_{inv}^{#bar{#Lambda}} vs m_{inv}^{#Lambda}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); - histos.add("Reco/h2f_n2_mass_LaPLaP", "m_{inv}^{#Lambda} vs m_{inv}^{#Lambda}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); - histos.add("Reco/h2f_n2_mass_LaMLaM", "m_{inv}^{#bar{#Lambda}} vs m_{inv}^{#bar{#Lambda}}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); + static constexpr std::array PoolSizeSteps = {1, 2, 5}; + static constexpr int64_t PoolSizeDecade = 10; + const int64_t depth = std::max(1, cMEPoolDepth.value); + std::vector poolSizeEdges = {0.}; + for (int64_t decade = 1; decade < depth; decade *= PoolSizeDecade) { + for (auto const& step : PoolSizeSteps) { + if (step * decade < depth) { + poolSizeEdges.push_back(static_cast(step * decade)); + } + } + } + poolSizeEdges.push_back(static_cast(depth)); + const AxisSpec axisPoolSize(poolSizeEdges, "events in the pool"); - histos.add("RecoBkg/h2f_n2_mass_LaPFkLaM", "US-LS: m_{inv}^{#Lambda} vs m_{inv}^{fake#bar{#Lambda}}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); - histos.add("RecoBkg/h2f_n2_mass_LaMLFkLaP", "US-LS: m_{inv}^{#bar{#Lambda}} vs m_{inv}^{fake#Lambda}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); - histos.add("RecoBkg/h2f_n2_mass_LaPFkLaP", "US-LS: m_{inv}^{#Lambda} vs m_{inv}^{fake#Lambda}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); - histos.add("RecoBkg/h2f_n2_mass_LaMLFkLaM", "US-LS: m_{inv}^{#bar{#Lambda}} vs m_{inv}^{fake#bar{#Lambda}}", kTHnSparseF, {axisMass, axisMass, axisPt, axisPt}); + std::vector mixQaDirs; + if (doprocessDataRecoMixed) { + mixQaDirs.emplace_back("QA/ME/"); + } + if (doprocessMcGenMixed) { + mixQaDirs.emplace_back("McGen/QA/ME/"); + } + for (auto const& dir : mixQaDirs) { + histos.add((dir + "hMixedCentVz").c_str(), "events mixed;cent (%);V_{z} (cm)", kTH2F, {axisCentME, axisVtxZME}); + auto hStatus = histos.add((dir + "hEventStatus").c_str(), "rolling-pool mixing;;collisions", kTH1D, {{kNMEEventStatus - 1, 0.5, static_cast(kNMEEventStatus) - 0.5}}); + hStatus->GetXaxis()->SetBinLabel(kMEEventSeen, "seen"); + hStatus->GetXaxis()->SetBinLabel(kMEEventOutsidePools, "outside the pool bins"); + hStatus->GetXaxis()->SetBinLabel(kMEEventWarmUp, "pairs, pool in warm-up"); + hStatus->GetXaxis()->SetBinLabel(kMEEventMixed, "mixed"); + hStatus->GetXaxis()->SetBinLabel(kMEEventDonated, "donated to the pool"); + histos.add((dir + "hPoolEventsAtMixing").c_str(), "pool size when a collision is mixed;events in the pool;collisions", kTH1F, {axisPoolSize}); + histos.add((dir + "hMatchesLeg2").c_str(), "matches per replaced leg 2;N matches;pair type", kTH2F, {{100, -0.5, 99.5}, {4, -0.5, 3.5}}); + histos.add((dir + "hDeltaRShift").c_str(), "ME pairs are filed at their own #DeltaR;#DeltaR_{seed};#DeltaR_{mixed pair} - #DeltaR_{seed}", kTH2F, {{35, 0., 3.5}, {100, -0.2, 0.2}}); + histos.add((dir + "hLambdaMultVsCent").c_str(), "ME #Lambda mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); + histos.add((dir + "hAntiLambdaMultVsCent").c_str(), "ME #bar{#Lambda} mult;cent;N", kTH2D, {axisCentME, {50, 0, 50}}); + } - histos.add("RecoCorr/h2f_n2_dltaR_LaPLaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTHnSparseF, {axisCent, axisDR, axisCosTS}); - histos.add("RecoCorr/h2f_n2_dltaR_LaMLaP", "#rho_{2}^{#bar{#Lambda}#Lambda}", kTHnSparseF, {axisCent, axisDR, axisCosTS}); - histos.add("RecoCorr/h2f_n2_dltaR_LaPLaP", "#rho_{2}^{#Lambda#Lambda}", kTHnSparseF, {axisCent, axisDR, axisCosTS}); - histos.add("RecoCorr/h2f_n2_dltaR_LaMLaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTHnSparseF, {axisCent, axisDR, axisCosTS}); + if (cMaxDeltaPt.value <= 0.f || cMaxDeltaRap.value <= 0.f || cMaxDeltaPhi.value <= 0.f) { + LOGF(fatal, "Rolling ME pool: the matching windows cMaxDeltaPt, cMaxDeltaRap and cMaxDeltaPhi must be positive"); + } + RollingPool::Grid grid; + grid.ptMin = cMinPt.value; + grid.rapMin = cMinRap.value; + grid.cellPt = cMaxDeltaPt.value; + grid.cellRap = cMaxDeltaRap.value; + grid.nPt = std::max(1, static_cast(std::ceil((cMaxPt.value - cMinPt.value) / grid.cellPt))); + grid.nRap = std::max(1, static_cast(std::ceil((cMaxRap.value - cMinRap.value) / grid.cellRap))); + grid.nPhi = std::max(1, static_cast(std::floor(TwoPI / cMaxDeltaPhi.value))); + grid.cellPhi = TwoPI / static_cast(grid.nPhi); + const auto nPools = static_cast(mePoolAxisCent.GetNbins()) * static_cast(mePoolAxisVz.GetNbins()); + if (doprocessDataRecoMixed) { + mePools.assign(nPools, RollingPool(grid, cMEPoolDepth.value)); + } + if (doprocessMcGenMixed) { + mePoolsGen.assign(nPools, RollingPool(grid, cMEPoolDepth.value)); + } + LOGF(info, "Rolling ME pools: %d (centrality) x %d (Vz) bins, depth %d donor events, grid %d x %d x %d (pT, y, phi)", + mePoolAxisCent.GetNbins(), mePoolAxisVz.GetNbins(), cMEPoolDepth.value, grid.nPt, grid.nRap, grid.nPhi); + } + + std::vector evDirs; + if (doprocessDataReco || doprocessDataRecoMixed) { + evDirs.emplace_back(""); + } + if (doprocessMcGen || doprocessMcGenMixed) { + evDirs.emplace_back("McGen/"); + } + const AxisSpec axisCandMass(cNMassBins, cMassHistMin, cMassHistMax, "M_{p#pi} (GeV/#it{c}^{2})"); + for (auto const& dir : evDirs) { + auto hCount = histos.add((dir + "Events/hEventCount").c_str(), "collisions of the pair analysis;;collisions", kTH1D, {{kNPairEventCount - 1, 0.5, static_cast(kNPairEventCount) - 0.5}}); + hCount->GetXaxis()->SetBinLabel(kPeAll, "all"); + hCount->GetXaxis()->SetBinLabel(kPeOneCand, "#geq 1 accepted #Lambda/#bar{#Lambda}"); + hCount->GetXaxis()->SetBinLabel(kPeTwoCand, "#geq 2 accepted (enter pairs)"); + hCount->GetXaxis()->SetBinLabel(kPeUnlikeSign, "#geq 1 #Lambda#bar{#Lambda} pair"); + hCount->GetXaxis()->SetBinLabel(kPeLambdaLambda, "#geq 1 #Lambda#Lambda pair"); + hCount->GetXaxis()->SetBinLabel(kPeAntiLambdaAntiLambda, "#geq 1 #bar{#Lambda}#bar{#Lambda} pair"); + histos.add((dir + "Events/hNCandAccepted").c_str(), "accepted candidates per collision;N_{#Lambda};N_{#bar{#Lambda}}", kTH2D, {{21, -0.5, 20.5}, {21, -0.5, 20.5}}); + histos.add((dir + "Events/hCentPairEvents").c_str(), "collisions with a pair;cent (%);collisions", kTH1D, {{100, 0., 100.}}); + for (auto const& species : {"Lambda/", "AntiLambda/"}) { + const std::string cand = dir + "QA/PairCand/" + species; + histos.add((cand + "hMassVsPt").c_str(), "accepted candidates of collisions with a pair", kTH2F, {axisCandMass, axisLegPt}); + histos.add((cand + "hRapVsPhi").c_str(), "accepted candidates of collisions with a pair", kTH2F, {axisLegRap, axisLegPhi}); + } + } - histos.add("RecoCorr/h2f_n2_ctheta_LaPLaM", "#rho_{2}^{#Lambda#bar{#Lambda}}", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("RecoCorr/h2f_n2_ctheta_LaMLaP", "#rho_{2}^{#bar{#Lambda}#Lambda}", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("RecoCorr/h2f_n2_ctheta_LaPLaP", "#rho_{2}^{#Lambda#Lambda}", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("RecoCorr/h2f_n2_ctheta_LaMLaM", "#rho_{2}^{#bar{#Lambda}#bar{#Lambda}}", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); + static constexpr std::array PairTags = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + for (auto const& tag : PairTags) { + const std::string pair{tag}; + if (doprocessDataReco) { + histos.add(("SE/hPair_" + pair).c_str(), ("SE " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessDataRecoMixed) { + histos.add(("ME/hPair_" + pair).c_str(), ("ME " + pair).c_str(), kTHnSparseD, pairAxes, true); + } + if (doprocessDataRecoMixed && cFillSEInMixing) { + histos.add(("SEproc/hPair_" + pair).c_str(), ("SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessDataRecoMixed && cFillKinWSpectra) { + histos.add(("KinW/SEproc/hLeg2_" + pair).c_str(), ("leg 2 of SEproc " + pair).c_str(), kTHnSparseD, legAxes); + histos.add(("KinW/ME/hLeg2_" + pair).c_str(), ("candidates replacing leg 2 " + pair).c_str(), kTHnSparseD, legAxes, true); + } + if (cFillSharedDauQA && doprocessDataReco) { + histos.add(("QA/SharedDau/SE/hPair_" + pair).c_str(), ("SE pairs with a candidate sharing a daughter " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (cFillSharedDauQA && doprocessDataRecoMixed) { + histos.add(("QA/SharedDau/SEproc/hPair_" + pair).c_str(), ("SEproc pairs with a candidate sharing a daughter " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessMcGen) { + histos.add(("McGen/SE/hPair_" + pair).c_str(), ("MC generated SE " + pair).c_str(), kTHnSparseF, pairAxes); + } + if (doprocessMcGenMixed) { + histos.add(("McGen/ME/hPair_" + pair).c_str(), ("MC generated ME " + pair).c_str(), kTHnSparseD, pairAxes, true); + } + if (doprocessMcGenMixed && cFillSEInMixing) { + histos.add(("McGen/SEproc/hPair_" + pair).c_str(), ("MC generated SE processed by the mixing " + pair).c_str(), kTHnSparseF, pairAxes); + } + } + if (cFillSharedDauQA && (doprocessDataReco || doprocessDataRecoMixed)) { + histos.add("QA/SharedDau/hClusterSize", "clusters of candidates sharing daughter tracks;candidates in the cluster;clusters", kTH1F, {{20, 0.5, 20.5}}); + } - histos.add("RecoCorrBkg/h2f_n2_dltaR_LaPFkLaM", "#rho_{2}^{#Lambda,fake#bar{#Lambda}} (US-LS)", kTHnSparseF, {axisCent, axisDR, axisCosTS}); - histos.add("RecoCorrBkg/h2f_n2_dltaR_LaMLFkLaP", "#rho_{2}^{#bar{#Lambda},fake#Lambda} (US-LS)", kTHnSparseF, {axisCent, axisDR, axisCosTS}); - histos.add("RecoCorrBkg/h2f_n2_dltaR_LaPFkLaP", "#rho_{2}^{#Lambda,fake#Lambda} (US-LS)", kTHnSparseF, {axisCent, axisDR, axisCosTS}); - histos.add("RecoCorrBkg/h2f_n2_dltaR_LaMLFkLaM", "#rho_{2}^{#bar{#Lambda},fake#bar{#Lambda}} (US-LS)", kTHnSparseF, {axisCent, axisDR, axisCosTS}); + if (cFillClosePairQA && (doprocessDataReco || doprocessDataRecoMixed)) { + const AxisSpec axisCPdEta(axisClosePair, "#Delta#eta"); + const AxisSpec axisCPdY(axisClosePair, "#Deltay"); + const AxisSpec axisCPdPhi(axisClosePair, "#Delta#varphi (rad)"); + std::vector> cpDirs; + if (doprocessDataReco) { + cpDirs.emplace_back("QA/ClosePair/", ""); + } + if (doprocessDataRecoMixed) { + cpDirs.emplace_back("QA/ClosePairSEproc/", "SEproc, "); + } + for (auto const& [dir, tag] : cpDirs) { + for (auto const& cls : {"LamLam", "ALamALam", "UnlikeSign"}) { + for (auto const& combo : {"pp", "pipi", "p1pi2", "pi1p2"}) { + const std::string name = dir + cls + "/h_" + combo; + const std::string title = tag + cls + ", daughters " + combo; + histos.add((name + "_dEta").c_str(), title.c_str(), kTH3F, {axisDR, axisCPdEta, axisCPdPhi}); + histos.add((name + "_dY").c_str(), title.c_str(), kTH3F, {axisDR, axisCPdY, axisCPdPhi}); + } + } + } + } - histos.add("RecoCorrBkg/h2f_n2_ctheta_LaPFkLaM", "#rho_{2}^{#Lambda,fake#bar{#Lambda}} (US-LS)", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("RecoCorrBkg/h2f_n2_ctheta_LaMLFkLaP", "#rho_{2}^{#bar{#Lambda},fake#Lambda} (US-LS)", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("RecoCorrBkg/h2f_n2_ctheta_LaPFkLaP", "#rho_{2}^{#Lambda,fake#Lambda} (US-LS)", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); - histos.add("RecoCorrBkg/h2f_n2_ctheta_LaMLFkLaM", "#rho_{2}^{#bar{#Lambda},fake#bar{#Lambda}} (US-LS)", kTHnSparseF, {axisCent, axisDRap, axisDPhi, axisCosTS}); + if (cApplyKinWeights) { + if (cKinWeightCcdbPath.value.empty()) { + LOGF(fatal, "cApplyKinWeights is set but cKinWeightCcdbPath is empty"); + } + ccdb->setURL(cKinWeightCcdbUrl.value); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + } } - void getBoostVector(std::array const& p, std::array& v, bool inverseBoostFlag = true) + bool isAcceptedMass(float m) const + { + return m >= cMassAccMin.value && m < cMassAccMax.value; + } + + static std::vector axisEdges(AxisSpec const& axis) + { + if (!axis.nBins.has_value()) { + return axis.binEdges; + } + const int n = axis.nBins.value(); + std::vector edges(n + 1); + for (int i = 0; i <= n; ++i) { + edges[i] = axis.binEdges[0] + (axis.binEdges[1] - axis.binEdges[0]) * i / n; + } + return edges; + } + + static constexpr double MassEdgeTolerance = 1e-3; + [[nodiscard]] bool isOnMassBinEdge(double m) const + { + const double width = (static_cast(cMassHistMax.value) - cMassHistMin.value) / cNMassBins.value; + const double k = (m - cMassHistMin.value) / width; + return std::abs(k - std::round(k)) < MassEdgeTolerance; + } + + void getBoostVector(std::array const& p, std::array& v) { int n = p.size(); for (int i = 0; i < n - 1; ++i) { - if (inverseBoostFlag) { - v[i] = -p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); - } else { - v[i] = p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); - } + v[i] = -p[i] / RecoDecay::e(p[0], p[1], p[2], p[3]); } } @@ -1828,357 +1906,767 @@ struct LambdaSpinPolarization { { float e = RecoDecay::e(p[0], p[1], p[2], p[3]); float b2 = b[0] * b[0] + b[1] * b[1] + b[2] * b[2]; - float gamma = 1. / std::sqrt(1 - b2); + float gam = 1.f / std::sqrt(1.f - b2); float bp = b[0] * p[0] + b[1] * p[1] + b[2] * p[2]; - float gamma2 = b2 > 0 ? (gamma - 1.) / b2 : 0.; - - p[0] = p[0] + gamma2 * bp * b[0] + gamma * b[0] * e; - p[1] = p[1] + gamma2 * bp * b[1] + gamma * b[1] * e; - p[2] = p[2] + gamma2 * bp * b[2] + gamma * b[2] * e; + float gam2 = (b2 > 0.f) ? (gam - 1.f) / b2 : 0.f; + p[0] = p[0] + gam2 * bp * b[0] + gam * b[0] * e; + p[1] = p[1] + gam2 * bp * b[1] + gam * b[1] * e; + p[2] = p[2] + gam2 * bp * b[2] + gam * b[2] * e; } - template - void fillPairHistos(U& p1, U& p2) + static float cosOpeningAngle(std::array const& a, std::array const& b) { - static constexpr std::string_view SubDirHistUS[] = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; - static constexpr std::string_view SubDirHistBkg[] = {"LaPFkLaM", "LaMLFkLaP", "LaPFkLaP", "LaMLFkLaM"}; + std::array n1 = {a[0], a[1], a[2]}, n2 = {b[0], b[1], b[2]}; + return RecoDecay::dotProd(n1, n2) / + (RecoDecay::sqrtSumOfSquares(n1[0], n1[1], n1[2]) * + RecoDecay::sqrtSumOfSquares(n2[0], n2[1], n2[2])); + } - constexpr bool IsBkg = (part_pair == kLambdaFakeAntiLambda || part_pair == kAntiLambdaFakeLambda || part_pair == kLambdaFakeLambda || part_pair == kAntiLambdaFakeAntiLambda); + float cosThetaStarAtlas(std::array const& l1, std::array const& l2, std::array const& pr1, std::array const& pr2) + { + auto l1a = l1; + auto l2a = l2; + auto pr1a = pr1; + auto pr2a = pr2; + + const float mPair = RecoDecay::m(std::array{std::array{l1a[0], l1a[1], l1a[2]}, std::array{l2a[0], l2a[1], l2a[2]}}, std::array{l1a[3], l2a[3]}); + std::array llpair = {l1a[0] + l2a[0], l1a[1] + l2a[1], l1a[2] + l2a[2], mPair}; + std::array vPair{}; + getBoostVector(llpair, vPair); + boost(l1a, vPair); + boost(l2a, vPair); + boost(pr1a, vPair); + boost(pr2a, vPair); + std::array v1p{}, v2p{}; + getBoostVector(l1a, v1p); + getBoostVector(l2a, v2p); + boost(pr1a, v1p); + boost(pr2a, v2p); + return cosOpeningAngle(pr1a, pr2a); + } - // Fill pair invariant mass histogram - if constexpr (!IsBkg) { - histos.fill(HIST("Reco/h2f_n2_mass_") + HIST(SubDirHistUS[part_pair]), p1.mass(), p2.mass(), p1.pt(), p2.pt()); - } else { - constexpr int BkgIdx = static_cast(part_pair) - 4; - histos.fill(HIST("RecoBkg/h2f_n2_mass_") + HIST(SubDirHistBkg[BkgIdx]), p1.mass(), p2.mass(), p1.pt(), p2.pt()); - } + static float deltaR(PoolTrack const& p1, PoolTrack const& p2) + { + const float drap = p1.rap() - p2.rap(); + const float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); + return std::sqrt(drap * drap + dphi * dphi); + } - float drap = p1.rap() - p2.rap(); - float dphi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -PI); - float dR = std::sqrt(drap * drap + dphi * dphi); + template + void fillPair(PoolTrack const& p1, PoolTrack const& p2, float dR, float w) + { + static constexpr std::array TierDir = {"SE/hPair_", "SEproc/hPair_", "ME/hPair_", + "McGen/SE/hPair_", "McGen/SEproc/hPair_", "McGen/ME/hPair_", + "QA/SharedDau/SE/hPair_", "QA/SharedDau/SEproc/hPair_"}; + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; - std::array l1 = {p1.px(), p1.py(), p1.pz(), MassLambda0}; - std::array l2 = {p2.px(), p2.py(), p2.pz(), MassLambda0}; + std::array l1 = {p1.px(), p1.py(), p1.pz(), p1.mass()}; + std::array l2 = {p2.px(), p2.py(), p2.pz(), p2.mass()}; std::array pr1 = {p1.prPx(), p1.prPy(), p1.prPz(), MassProton}; std::array pr2 = {p2.prPx(), p2.prPy(), p2.prPz(), MassProton}; - if (cDoAtlasMethod) { - std::array l1Atlas = l1; - std::array l2Atlas = l2; - std::array pr1Atlas = pr1; - std::array pr2Atlas = pr2; - - std::array llpair = { - l1Atlas[0] + l2Atlas[0], - l1Atlas[1] + l2Atlas[1], - l1Atlas[2] + l2Atlas[2], - l1Atlas[3] + l2Atlas[3]}; - - std::array vPair; - getBoostVector(llpair, vPair, cInvBoostFlag); - boost(l1Atlas, vPair); - boost(l2Atlas, vPair); - boost(pr1Atlas, vPair); - boost(pr2Atlas, vPair); - - std::array v1Pair, v2Pair; - getBoostVector(l1Atlas, v1Pair, cInvBoostFlag); - getBoostVector(l2Atlas, v2Pair, cInvBoostFlag); - boost(pr1Atlas, v1Pair); - boost(pr2Atlas, v2Pair); - - std::array pr1tvAtlas = {pr1Atlas[0], pr1Atlas[1], pr1Atlas[2]}; - std::array pr2tvAtlas = {pr2Atlas[0], pr2Atlas[1], pr2Atlas[2]}; - float ctheta = - RecoDecay::dotProd(pr1tvAtlas, pr2tvAtlas) / - (RecoDecay::sqrtSumOfSquares(pr1tvAtlas[0], pr1tvAtlas[1], pr1tvAtlas[2]) * - RecoDecay::sqrtSumOfSquares(pr2tvAtlas[0], pr2tvAtlas[1], pr2tvAtlas[2])); - - if constexpr (!IsBkg) { - histos.fill(HIST("RecoCorr/h2f_n2_ctheta_") + HIST(SubDirHistUS[part_pair]), cent, drap, dphi, ctheta); - histos.fill(HIST("RecoCorr/h2f_n2_dltaR_") + HIST(SubDirHistUS[part_pair]), cent, dR, ctheta); - } else { - constexpr int BkgIdx = static_cast(part_pair) - 4; - histos.fill(HIST("RecoCorrBkg/h2f_n2_ctheta_") + HIST(SubDirHistBkg[BkgIdx]), cent, drap, dphi, ctheta); - histos.fill(HIST("RecoCorrBkg/h2f_n2_dltaR_") + HIST(SubDirHistBkg[BkgIdx]), cent, dR, ctheta); - } - } + const float ctheta = cosThetaStarAtlas(l1, l2, pr1, pr2); + histos.fill(HIST(TierDir[tier]) + HIST(PairTag[part_pair]), p1.mass(), p2.mass(), dR, ctheta, w); + } + + template + void fillLeg2Spectrum(PoolTrack const& leg, float w) + { + static constexpr std::array Dir = {"KinW/SEproc/hLeg2_", "KinW/ME/hLeg2_"}; + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + histos.fill(HIST(Dir[IsME]) + HIST(PairTag[part_pair]), leg.pt(), leg.rap(), leg.phi(), w); + } - if (cDoStarMethod) { - std::array pr1Star = pr1; - std::array pr2Star = pr2; + template + void fillDaughterSeparation(std::array const& a, double ma, std::array const& b, double mb, float dR) + { + static_assert(tier == kSameEvent || tier == kSameEventInMixing, "close-pair QA is filled for SE and SEproc pairs only"); + static constexpr std::array TierDir = {"QA/ClosePair/", "QA/ClosePairSEproc/"}; + static constexpr std::array ClsDir = {"LamLam/", "ALamALam/", "UnlikeSign/"}; + static constexpr std::array ComboName = {"h_pp_", "h_pipi_", "h_p1pi2_", "h_pi1p2_"}; + const float dEta = std::asinh(a[2] / std::hypot(a[0], a[1])) - std::asinh(b[2] / std::hypot(b[0], b[1])); + const float dY = RecoDecay::y(a, ma) - RecoDecay::y(b, mb); + const float dPhi = RecoDecay::constrainAngle(std::atan2(a[1], a[0]) - std::atan2(b[1], b[0]), -PI); + histos.fill(HIST(TierDir[tier]) + HIST(ClsDir[Cls]) + HIST(ComboName[Combo]) + HIST("dEta"), dR, dEta, dPhi); + histos.fill(HIST(TierDir[tier]) + HIST(ClsDir[Cls]) + HIST(ComboName[Combo]) + HIST("dY"), dR, dY, dPhi); + } - std::array v1Lab, v2Lab; - getBoostVector(l1, v1Lab, cInvBoostFlag); - getBoostVector(l2, v2Lab, cInvBoostFlag); - boost(pr1Star, v1Lab); - boost(pr2Star, v2Lab); + template + void fillClosePairQA(PoolTrack const& l1, PoolTrack const& l2, float dR) + { + constexpr int Cls = part_pair == kLambdaLambda ? 0 : (part_pair == kAntiLambdaAntiLambda ? 1 : 2); + const std::array pr1 = {l1.prPx(), l1.prPy(), l1.prPz()}; + const std::array pr2 = {l2.prPx(), l2.prPy(), l2.prPz()}; + const std::array pi1 = {l1.px() - l1.prPx(), l1.py() - l1.prPy(), l1.pz() - l1.prPz()}; + const std::array pi2 = {l2.px() - l2.prPx(), l2.py() - l2.prPy(), l2.pz() - l2.prPz()}; + fillDaughterSeparation(pr1, MassProton, pr2, MassProton, dR); + fillDaughterSeparation(pi1, MassPionCharged, pi2, MassPionCharged, dR); + fillDaughterSeparation(pr1, MassProton, pi2, MassPionCharged, dR); + fillDaughterSeparation(pi1, MassPionCharged, pr2, MassProton, dR); + } - std::array pr1tvStar = {pr1Star[0], pr1Star[1], pr1Star[2]}; - std::array pr2tvStar = {pr2Star[0], pr2Star[1], pr2Star[2]}; - float ctheta = - RecoDecay::dotProd(pr1tvStar, pr2tvStar) / - (RecoDecay::sqrtSumOfSquares(pr1tvStar[0], pr1tvStar[1], pr1tvStar[2]) * - RecoDecay::sqrtSumOfSquares(pr2tvStar[0], pr2tvStar[1], pr2tvStar[2])); + bool isClosePairQAMass(float m) const + { + return cClosePairMassWindow.value <= 0.f || std::abs(m - MassLambda0) < cClosePairMassWindow.value; + } - if constexpr (!IsBkg) { - histos.fill(HIST("RecoCorr/h2f_n2_ctheta_") + HIST(SubDirHistUS[part_pair]), cent, drap, dphi, ctheta); - histos.fill(HIST("RecoCorr/h2f_n2_dltaR_") + HIST(SubDirHistUS[part_pair]), cent, dR, ctheta); - } else { - constexpr int BkgIdx = static_cast(part_pair) - 4; - histos.fill(HIST("RecoCorrBkg/h2f_n2_ctheta_") + HIST(SubDirHistBkg[BkgIdx]), cent, drap, dphi, ctheta); - histos.fill(HIST("RecoCorrBkg/h2f_n2_dltaR_") + HIST(SubDirHistBkg[BkgIdx]), cent, dR, ctheta); + bool isKinematicMatch(PoolTrack const& cand, PoolTrack const& leg) const + { + return std::abs(cand.pt() - leg.pt()) < cMaxDeltaPt.value && + std::abs(cand.rap() - leg.rap()) < cMaxDeltaRap.value && + std::abs(RecoDecay::constrainAngle(cand.phi() - leg.phi(), -PI)) < cMaxDeltaPhi.value; + } + + static constexpr std::size_t NLegAxes = 3; + template + float kinWeight(PoolTrack const& cand) const + { + THnBase const* map = kinWeightMaps[part_pair].get(); + if (!map) { + return 1.f; + } + const std::array x = {cand.pt(), cand.rap(), cand.phi()}; + std::array idx{}; + for (std::size_t i = 0; i < NLegAxes; ++i) { + TAxis const* axis = map->GetAxis(i); + idx[i] = axis->FindFixBin(x[i]); + if (idx[i] < 1 || idx[i] > axis->GetNbins()) { + return 1.f; } } + const int64_t bin = map->GetBin(idx.data()); + if (bin < 0) { + return 1.f; + } + const double w = map->GetBinContent(bin); + return w > 0. ? static_cast(w) : 1.f; } - template - void analyzePairsStandard(T const& trks_1, T const& trks_2) + static constexpr double AxisEdgeTolerance = 1e-6; + [[nodiscard]] bool hasLegAxes(THnBase const& map) const { - for (auto const& trk_1 : trks_1) { - for (auto const& trk_2 : trks_2) { - - if constexpr (samelambda) { - if (trk_1.index() == trk_2.index()) - continue; + if (map.GetNdimensions() != static_cast(legAxesRef.size())) { + return false; + } + for (std::size_t i = 0; i < legAxesRef.size(); ++i) { + TAxis const* axis = map.GetAxis(i); + TAxis const& ref = legAxesRef[i]; + if (axis->GetNbins() != ref.GetNbins()) { + return false; + } + for (int b = 1; b <= ref.GetNbins() + 1; ++b) { + if (std::abs(axis->GetBinLowEdge(b) - ref.GetBinLowEdge(b)) > AxisEdgeTolerance) { + return false; } + } + } + return true; + } - fillPairHistos(trk_1, trk_2); + void loadKinWeights(uint64_t collisionTimestamp) + { + static constexpr std::array PairTag = {"LaPLaM", "LaMLaP", "LaPLaP", "LaMLaM"}; + const int64_t ts = cKinWeightCcdbTimestamp.value >= 0 ? cKinWeightCcdbTimestamp.value : static_cast(collisionTimestamp); + auto* list = ccdb->getForTimeStamp(cKinWeightCcdbPath.value, ts); + if (!list) { + LOGP(fatal, "Kinematic weights: no object at {} for timestamp {}", cKinWeightCcdbPath.value, ts); + return; + } + auto const* guard = dynamic_cast(list->FindObject("kinWeightGuardStatus")); + if (guard != nullptr && !TString(guard->GetTitle()).BeginsWith("OK")) { + LOGF(fatal, "Kinematic weights at %s are flagged by their builder: %s", cKinWeightCcdbPath.value.c_str(), guard->GetTitle()); + return; + } + for (std::size_t i = 0; i < PairTag.size(); ++i) { + const std::string name = "hKinW_" + std::string(PairTag[i]); + auto* map = dynamic_cast(list->FindObject(name.c_str())); + if (!map || !hasLegAxes(*map)) { + LOGF(fatal, "Kinematic weights: %s is missing, or its axes differ from KinW/*/hLeg2_%s (pT, y, phi) of this configuration", name.c_str(), std::string(PairTag[i]).c_str()); + return; } + kinWeightMaps[i].reset(dynamic_cast(map->Clone())); } + auto const* form = dynamic_cast(list->FindObject("kinWeightForm")); + kinWeightsLoaded = true; + LOGF(info, "Kinematic weights loaded from %s (form: %s)", cKinWeightCcdbPath.value.c_str(), form ? form->GetTitle() : "not stated"); } - template - void analyzePairsSequential(T const& trks_1, T const& trks_2) + static uint64_t splitMix(uint64_t x) { - std::vector candidates; - candidates.reserve(trks_2.size()); + x += 0x9E3779B97F4A7C15ULL; + x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL; + x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL; + return x ^ (x >> 31); + } - for (auto const& trk : trks_2) { - candidates.push_back(&trk); - } + template + void setPairOrderSeed(C const& col) + { + const uint64_t vz = std::bit_cast(col.posZ()); + pairOrderSeed = splitMix(((vz << 32) | static_cast(col.globalIndex())) ^ (col.timeStamp() * 0xD6E8FEB86659FD93ULL)); + } - if (candidates.empty()) { - return; + template + bool isCountedOrder(T const& trk_1, T const& trk_2) const + { + const int64_t g1 = trk_1.globalIndex(), g2 = trk_2.globalIndex(); + if (g1 == g2) { + return false; } + const bool firstIsLo = g1 < g2; + const uint64_t lo = static_cast(firstIsLo ? g1 : g2); + const uint64_t hi = static_cast(firstIsLo ? g2 : g1); + const bool swapped = splitMix(pairOrderSeed ^ ((lo << 32) | hi)) & 1ULL; + return firstIsLo != swapped; + } - // Randomize candidates - static thread_local std::mt19937 rng(std::random_device{}()); - std::shuffle(candidates.begin(), candidates.end(), rng); + template + static bool sharesDaughter(T1 const& a, T2 const& b) + { + return a.posTrackId() == b.posTrackId() || a.negTrackId() == b.negTrackId(); + } - for (auto const& trk_1 : trks_1) { + [[nodiscard]] bool sharesDaughterCand(std::size_t a, std::size_t b) const + { + return a != b && (sdCands[a].posId == sdCands[b].posId || sdCands[a].negId == sdCands[b].negId); + } - decltype(&*trks_2.begin()) currentPartner = nullptr; + [[nodiscard]] bool isDropped(int64_t globalIdx) const + { + return std::binary_search(sdDropped.begin(), sdDropped.end(), globalIdx); + } + + [[nodiscard]] bool isConflicted(int64_t globalIdx) const + { + return std::binary_search(sdConflicted.begin(), sdConflicted.end(), globalIdx); + } + + template + [[nodiscard]] bool isKept(T const& trk) const + { + return !Reco || !isDropped(trk.globalIndex()); + } + + template + [[nodiscard]] bool isUsable(T const& trk) const + { + return isAcceptedMass(trk.mass()) && isKept(trk); + } - for (auto const& trk_2 : candidates) { + template + [[nodiscard]] bool hasMinCandidates(T const& lTrks, T const& alTrks) const + { + return std::cmp_greater_equal(lTrks.size() + alTrks.size(), cMinCandPerEvent.value); + } - if constexpr (samelambda) { - if (trk_1.index() == trk_2->index()) + void selectLargestFreeSet(std::vector const& members, std::vector& keep) const + { + const std::size_t s = members.size(); + keep.assign(s, 0); + if (s <= MaxExactCluster) { + std::array conf{}; + for (std::size_t a = 0; a < s; ++a) { + for (std::size_t b = 0; b < s; ++b) { + if (sharesDaughterCand(members[a], members[b])) { + conf[a] |= static_cast(1) << b; + } + } + } + uint32_t best = 0; + int bestN = -1; + float bestScore = 0.f; + const uint32_t nMasks = static_cast(1) << s; + for (uint32_t mask = 1; mask < nMasks; ++mask) { + bool isFree = true; + int nIn = 0; + float score = 0.f; + for (std::size_t a = 0; a < s && isFree; ++a) { + if (((mask >> a) & 1U) == 0U) { continue; + } + if ((conf[a] & mask) != 0U) { + isFree = false; + continue; + } + ++nIn; + score += sdCands[members[a]].dMass; } + if (isFree && (nIn > bestN || (nIn == bestN && score < bestScore))) { + best = mask; + bestN = nIn; + bestScore = score; + } + } + for (std::size_t a = 0; a < s; ++a) { + keep[a] = static_cast((best >> a) & 1U); + } + return; + } + std::vector order(s); + for (std::size_t a = 0; a < s; ++a) { + order[a] = a; + } + std::sort(order.begin(), order.end(), [this, &members](std::size_t x, std::size_t y) { + SharedDauCand const& cx = sdCands[members[x]]; + SharedDauCand const& cy = sdCands[members[y]]; + return cx.dMass < cy.dMass || (cx.dMass == cy.dMass && cx.globalIdx < cy.globalIdx); + }); + for (auto const& a : order) { + bool isFree = true; + for (std::size_t b = 0; b < s && isFree; ++b) { + if (keep[b] != 0 && sharesDaughterCand(members[a], members[b])) { + isFree = false; + } + } + if (isFree) { + keep[a] = 1; + } + } + } - // Initialize - if (!currentPartner) { - currentPartner = trk_2; - continue; + template + void resolveSharedDaughters(T const& lTrks, T const& alTrks, bool fillQA) + { + sdCands.clear(); + sdConflicted.clear(); + sdDropped.clear(); + auto collect = [this](auto const& trks) { + for (auto const& trk : trks) { + if (trk.lambdaSharingDaughter()) { + sdCands.push_back(SharedDauCand{trk.globalIndex(), trk.posTrackId(), trk.negTrackId(), static_cast(std::abs(trk.mass() - MassLambda0))}); + } + } + }; + collect(lTrks); + collect(alTrks); + const std::size_t n = sdCands.size(); + if (n == 0) { + return; + } + std::sort(sdCands.begin(), sdCands.end(), [](SharedDauCand const& a, SharedDauCand const& b) { return a.globalIdx < b.globalIdx; }); + std::vector cluster(n, -1); + std::vector members; + std::vector stack; + std::vector keep; + int nClusters = 0; + for (std::size_t seed = 0; seed < n; ++seed) { + if (cluster[seed] >= 0) { + continue; + } + members.clear(); + stack.assign(1, seed); + cluster[seed] = nClusters; + while (!stack.empty()) { + const std::size_t a = stack.back(); + stack.pop_back(); + members.push_back(a); + for (std::size_t b = 0; b < n; ++b) { + if (cluster[b] < 0 && sharesDaughterCand(a, b)) { + cluster[b] = nClusters; + stack.push_back(b); + } + } + } + ++nClusters; + std::sort(members.begin(), members.end()); + if (fillQA && cFillSharedDauQA) { + histos.fill(HIST("QA/SharedDau/hClusterSize"), members.size()); + } + if (members.size() < NCandidatesForPair) { + continue; + } + for (auto const& a : members) { + sdConflicted.push_back(sdCands[a].globalIdx); + } + if (cSharedDauRule.value == kSharedDauPairVeto) { + continue; + } + if (cSharedDauRule.value == kSharedDauDropAll) { + keep.assign(members.size(), 0); + } else { + selectLargestFreeSet(members, keep); + } + for (std::size_t a = 0; a < members.size(); ++a) { + if (keep[a] == 0) { + sdDropped.push_back(sdCands[members[a]].globalIdx); } + } + } + std::sort(sdConflicted.begin(), sdConflicted.end()); + std::sort(sdDropped.begin(), sdDropped.end()); + } - float dPt = std::abs(trk_2->pt() - currentPartner->pt()); - float dPhi = std::abs(RecoDecay::constrainAngle(trk_2->phi() - currentPartner->phi(), -PI)); - float dRap = std::abs(trk_2->rap() - currentPartner->rap()); + template + void analyzePairsSE(T const& trks_1, T const& trks_2) + { + constexpr bool Reco = tier == kSameEvent; + for (auto const& trk_1 : trks_1) { + if (!isUsable(trk_1)) { + continue; + } + const PoolTrack p1 = toPoolTrack(trk_1); - if (dPt < cMaxDeltaPt && dPhi < cMaxDeltaPhi && dRap < cMaxDeltaRap) { - currentPartner = trk_2; + for (auto const& trk_2 : trks_2) { + if (!isCountedOrder(trk_1, trk_2) || !isUsable(trk_2)) { + continue; + } + if constexpr (Reco) { + if (sharesDaughter(trk_1, trk_2)) { + continue; + } + } + const PoolTrack p2 = toPoolTrack(trk_2); + const float dR = deltaR(p1, p2); + fillPair(p1, p2, dR, 1.f); + if constexpr (Reco) { + if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { + fillClosePairQA(p1, p2, dR); + } + if (cFillSharedDauQA && (isConflicted(trk_1.globalIndex()) || isConflicted(trk_2.globalIndex()))) { + fillPair(p1, p2, dR, 1.f); + } } } + } + } - if (currentPartner) { - fillPairHistos(trk_1, *currentPartner); + template + void fillEventQA(float centrality, T const& lTrks, T const& alTrks) + { + static constexpr std::array EvDir = {"Events/", "McGen/Events/"}; + std::size_t nLambda = 0, nAntiLambda = 0; + for (auto const& trk : lTrks) { + if (isUsable(trk)) { + ++nLambda; + } + } + for (auto const& trk : alTrks) { + if (isUsable(trk)) { + ++nAntiLambda; + } + } + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeAll); + histos.fill(HIST(EvDir[Gen]) + HIST("hNCandAccepted"), nLambda, nAntiLambda); + if (nLambda + nAntiLambda >= 1) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeOneCand); + } + if (nLambda + nAntiLambda < NCandidatesForPair) { + return; + } + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeTwoCand); + histos.fill(HIST(EvDir[Gen]) + HIST("hCentPairEvents"), centrality); + if (nLambda >= 1 && nAntiLambda >= 1) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeUnlikeSign); + } + if (nLambda >= NCandidatesForPair) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeLambdaLambda); + } + if (nAntiLambda >= NCandidatesForPair) { + histos.fill(HIST(EvDir[Gen]) + HIST("hEventCount"), kPeAntiLambdaAntiLambda); + } + for (auto const& trk : lTrks) { + if (isUsable(trk)) { + fillPairCandQA(trk); + } + } + for (auto const& trk : alTrks) { + if (isUsable(trk)) { + fillPairCandQA(trk); } } } - template - void analyzePairsME(T const& trks_1, T const& trks_2) + template + void fillPairCandQA(T const& trk) { - switch (cMEMode) { + static constexpr std::array CandDir = {"QA/PairCand/", "McGen/QA/PairCand/"}; + static constexpr std::array Species = {"Lambda/", "AntiLambda/"}; + histos.fill(HIST(CandDir[Gen]) + HIST(Species[part]) + HIST("hMassVsPt"), trk.mass(), trk.pt()); + histos.fill(HIST(CandDir[Gen]) + HIST(Species[part]) + HIST("hRapVsPhi"), trk.rap(), trk.phi()); + } - case 0: // standard - analyzePairsStandard(trks_1, trks_2); - break; + [[nodiscard]] int poolBin(float centrality, float vz) const + { + const int iCent = mePoolAxisCent.FindFixBin(centrality); + const int iVz = mePoolAxisVz.FindFixBin(vz); + if (iCent < 1 || iCent > mePoolAxisCent.GetNbins() || iVz < 1 || iVz > mePoolAxisVz.GetNbins()) { + return -1; + } + return (iCent - 1) * mePoolAxisVz.GetNbins() + (iVz - 1); + } - case 1: // sequential - analyzePairsSequential(trks_1, trks_2); - break; + template + void analyzePairsMEKinematic(T const& se_trks_1, T const& se_trks_2, RollingPool const& pool) + { + static constexpr std::array MixQaDir = {"QA/ME/", "McGen/QA/ME/"}; + constexpr PairTier SeTier = Gen ? kMcGenSameEventInMixing : kSameEventInMixing; + constexpr PairTier MeTier = Gen ? kMcGenMixedEvent : kMixedEvent; + std::vector matches; + for (auto const& trk1 : se_trks_1) { + if (!isUsable(trk1)) { + continue; + } + const PoolTrack p1 = toPoolTrack(trk1); + + for (auto const& trk2 : se_trks_2) { + if (!isCountedOrder(trk1, trk2) || !isUsable(trk2)) { + continue; + } + if constexpr (!Gen) { + if (sharesDaughter(trk1, trk2)) { + continue; + } + } + const PoolTrack p2 = toPoolTrack(trk2); + const float dRSeed = deltaR(p1, p2); - default: - LOGF(fatal, "Invalid cMEMode value!"); + if (cFillSEInMixing) { + fillPair(p1, p2, dRSeed, 1.f); + } + if constexpr (!Gen) { + if (cFillClosePairQA && isClosePairQAMass(p1.mass()) && isClosePairQAMass(p2.mass())) { + fillClosePairQA(p1, p2, dRSeed); + } + if (cFillSharedDauQA && (isConflicted(trk1.globalIndex()) || isConflicted(trk2.globalIndex()))) { + fillPair(p1, p2, dRSeed, 1.f); + } + if (cFillKinWSpectra) { + fillLeg2Spectrum(p2, 1.f); + } + } + + pool.findMatches(p2, [this, &p2](PoolTrack const& cand) { return isKinematicMatch(cand, p2); }, matches); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hMatchesLeg2"), matches.size(), part_pair); + for (auto const& meP2 : matches) { + float kinW = 1.f; + if constexpr (!Gen) { + kinW = kinWeight(*meP2); + } + const float w = kinW / static_cast(matches.size()); + const float dRMixed = deltaR(p1, *meP2); + fillPair(p1, *meP2, dRMixed, w); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hDeltaRShift"), dRSeed, dRMixed - dRSeed, w); + if constexpr (!Gen) { + if (cFillKinWSpectra) { + fillLeg2Spectrum(*meP2, w); + } + } + } + } + } + } + + template + void mixCollision(C const& col, T const& lTrks, T const& alTrks, std::vector& pools, std::vector& eventCands) + { + static constexpr std::array MixQaDir = {"QA/ME/", "McGen/QA/ME/"}; + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventSeen); + const int bin = poolBin(col.cent(), col.posZ()); + if (bin < 0) { + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventOutsidePools); + return; + } + std::size_t nLambda = 0, nAntiLambda = 0; + for (auto const& trk : lTrks) { + if (isKept(trk)) { + ++nLambda; + } + } + for (auto const& trk : alTrks) { + if (isKept(trk)) { + ++nAntiLambda; + } + } + histos.fill(HIST(MixQaDir[Gen]) + HIST("hLambdaMultVsCent"), col.cent(), nLambda); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hAntiLambdaMultVsCent"), col.cent(), nAntiLambda); + + eventCands.clear(); + for (auto const& trk : lTrks) { + if (isUsable(trk)) { + eventCands.push_back(toPoolTrack(trk)); + } + } + for (auto const& trk : alTrks) { + if (isUsable(trk)) { + eventCands.push_back(toPoolTrack(trk)); + } + } + + RollingPool& pool = pools[bin]; + if (eventCands.size() >= NCandidatesForPair) { + if (static_cast(pool.nEvents()) >= cMEPoolMinEvents.value) { + if constexpr (!Gen) { + if (cApplyKinWeights && !kinWeightsLoaded) { + loadKinWeights(col.timeStamp()); + } + } + setPairOrderSeed(col); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventMixed); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hMixedCentVz"), col.cent(), col.posZ()); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hPoolEventsAtMixing"), pool.nEvents()); + analyzePairsMEKinematic(lTrks, alTrks, pool); + analyzePairsMEKinematic(alTrks, lTrks, pool); + analyzePairsMEKinematic(lTrks, lTrks, pool); + analyzePairsMEKinematic(alTrks, alTrks, pool); + } else { + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventWarmUp); + } + } + if (static_cast(eventCands.size()) >= cMEPoolMinCand.value) { + pool.push(eventCands); + histos.fill(HIST(MixQaDir[Gen]) + HIST("hEventStatus"), kMEEventDonated); } } - // Initialize tables using LambdaCollisions = aod::LambdaCollisions; + using LambdaMcGenCollisions = aod::LambdaMcGenCollisions; using LambdaTracks = soa::Join; Preslice perCollisionLambda = aod::lambdatrack::lambdaCollisionId; - SliceCache cache; - // Real V0 partitions: trueLambdaFlag=true (passed sharing-daughter resolution) AND fakeFlag=false - Partition partLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrackext::fakeFlag == false) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - Partition partAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrackext::trueLambdaFlag == true) && (aod::lambdatrackext::fakeFlag == false) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - // LS combinatorial background partitions: - // fakeFlag=true now means: passed the sharing-daughter acceptance logic (Issue 3 fix). - Partition partFakeLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kFakeLambda) && (aod::lambdatrackext::fakeFlag == true); - Partition partFakeAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kFakeAntiLambda) && (aod::lambdatrackext::fakeFlag == true); + Partition partLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - void processDummy(LambdaCollisions::iterator const&) {} - - PROCESS_SWITCH(LambdaSpinPolarization, processDummy, "Dummy process", false); - - void processDataReco(LambdaCollisions::iterator const& collision, LambdaTracks const&) - { - cent = collision.cent(); + Partition partAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - auto lambdaTracks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto antiLambdaTracks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + SliceCache cachemc; - auto fakeLambdaTracks = partFakeLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto fakeAntiLambdaTracks = partFakeAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + Partition partMcLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - analyzePairsStandard(lambdaTracks, antiLambdaTracks); - analyzePairsStandard(antiLambdaTracks, lambdaTracks); - analyzePairsStandard(lambdaTracks, lambdaTracks); - analyzePairsStandard(antiLambdaTracks, antiLambdaTracks); + Partition partMcAntiLambdaTracks = (aod::lambdatrack::v0Type == (int8_t)kAntiLambda) && (aod::lambdatrack::v0PrmScd == (int8_t)kPrimary); - analyzePairsStandard(lambdaTracks, fakeAntiLambdaTracks); - analyzePairsStandard(fakeLambdaTracks, antiLambdaTracks); + void processDummy(LambdaCollisions::iterator const&) {} + PROCESS_SWITCH(LambdaSpinPolarization, processDummy, "Dummy", false); - analyzePairsStandard(antiLambdaTracks, fakeLambdaTracks); - analyzePairsStandard(fakeAntiLambdaTracks, lambdaTracks); + void processDataReco(LambdaCollisions::iterator const& collision, LambdaTracks const&) + { + setPairOrderSeed(collision); + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + resolveSharedDaughters(lTrks, alTrks, !doprocessDataRecoMixed); + if (!doprocessDataRecoMixed) { + fillEventQA(collision.cent(), lTrks, alTrks); + } + if (!hasMinCandidates(lTrks, alTrks)) { + return; + } - analyzePairsStandard(lambdaTracks, fakeLambdaTracks); - analyzePairsStandard(fakeLambdaTracks, lambdaTracks); - analyzePairsStandard(antiLambdaTracks, fakeAntiLambdaTracks); - analyzePairsStandard(fakeAntiLambdaTracks, antiLambdaTracks); + analyzePairsSE(lTrks, alTrks); + analyzePairsSE(alTrks, lTrks); + analyzePairsSE(lTrks, lTrks); + analyzePairsSE(alTrks, alTrks); } + PROCESS_SWITCH(LambdaSpinPolarization, processDataReco, "SE only (data/MCReco)", false); - PROCESS_SWITCH(LambdaSpinPolarization, processDataReco, "Process for Data and MCReco", true); + void processDataRecoMixed(LambdaCollisions const& cols, LambdaTracks const&) + { + std::vector eventCands; + for (auto const& col : cols) { + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col.globalIndex(), cache); + resolveSharedDaughters(lTrks, alTrks, true); + fillEventQA(col.cent(), lTrks, alTrks); + if (!hasMinCandidates(lTrks, alTrks)) { + continue; + } + mixCollision(col, lTrks, alTrks, mePools, eventCands); + } + } + PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixed, "ME: kinematic mixing against rolling pools that cross DataFrames", false); - struct GetMultiplicity { - float operator()(auto const& col) const - { - return col.cent(); + void processMcGen(LambdaMcGenCollisions::iterator const& collision, aod::LambdaMcGenTracks const&) + { + setPairOrderSeed(collision); + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + if (!doprocessMcGenMixed) { + fillEventQA(collision.cent(), lTrks, alTrks); + } + if (!hasMinCandidates(lTrks, alTrks)) { + return; } - }; - using MixedBinning = FlexibleBinningPolicy, o2::aod::collision::PosZ, GetMultiplicity>; - MixedBinning binningOnVtxAndMult{ - {GetMultiplicity{}}, - {axisVtxZME, axisCentME}, - true}; + analyzePairsSE(lTrks, alTrks); + analyzePairsSE(alTrks, lTrks); + analyzePairsSE(lTrks, lTrks); + analyzePairsSE(alTrks, alTrks); + } + PROCESS_SWITCH(LambdaSpinPolarization, processMcGen, "MC generated: SE pairs (McGen/SE), as processDataReco", false); - void processDataRecoMixed(LambdaCollisions const& col, LambdaTracks const&) + void processMcGenMixed(LambdaMcGenCollisions const& cols, aod::LambdaMcGenTracks const&) { - for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { - - if (col1.globalIndex() == col2.globalIndex()) { + std::vector eventCands; + for (auto const& col : cols) { + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, col.globalIndex(), cachemc); + fillEventQA(col.cent(), lTrks, alTrks); + if (!hasMinCandidates(lTrks, alTrks)) { continue; } - - cent = col1.cent(); - - // QA: pool occupancy - histos.fill(HIST("QA/ME/hPoolCentVz"), col1.cent(), col1.posZ()); - - // Lambda slices - auto lambdaTracksCol1 = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col1.globalIndex(), cache); - auto lambdaTracksCol2 = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - - // Anti-lambda slices - auto antiLambdaTracksCol1 = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col1.globalIndex(), cache); - auto antiLambdaTracksCol2 = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - - // Fake lambda slices (LS background) - auto fakeLambdaTracksCol2 = partFakeLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - auto fakeAntiLambdaTracksCol2 = partFakeAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, col2.globalIndex(), cache); - - // QA: multiplicities - histos.fill(HIST("QA/ME/hLambdaMultVsCent"), col1.cent(), lambdaTracksCol1.size()); - histos.fill(HIST("QA/ME/hAntiLambdaMultVsCent"), col1.cent(), antiLambdaTracksCol1.size()); - - analyzePairsME(lambdaTracksCol1, antiLambdaTracksCol2); - analyzePairsME(antiLambdaTracksCol1, lambdaTracksCol2); - analyzePairsME(lambdaTracksCol1, lambdaTracksCol2); - analyzePairsME(antiLambdaTracksCol1, antiLambdaTracksCol2); - - analyzePairsME(lambdaTracksCol1, fakeAntiLambdaTracksCol2); - analyzePairsME(fakeLambdaTracksCol2, antiLambdaTracksCol1); - - analyzePairsME(antiLambdaTracksCol1, fakeLambdaTracksCol2); - analyzePairsME(fakeAntiLambdaTracksCol2, lambdaTracksCol1); - - analyzePairsME(lambdaTracksCol1, fakeLambdaTracksCol2); - analyzePairsME(fakeLambdaTracksCol2, lambdaTracksCol1); - analyzePairsME(antiLambdaTracksCol1, fakeAntiLambdaTracksCol2); - analyzePairsME(fakeAntiLambdaTracksCol2, antiLambdaTracksCol1); + mixCollision(col, lTrks, alTrks, mePoolsGen, eventCands); } } - - PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixed, "Process for Data and MCReco for Mixed events", false); + PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixed, "MC generated: kinematic mixing against rolling pools, as processDataRecoMixed", false); void processDataRecoMixEvent(LambdaCollisions::iterator const& collision, LambdaTracks const&) { - // Slice each particle type for this collision - auto lambdaTracks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto antiLambdaTracks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto fakeLambdaTracks = partFakeLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - auto fakeAntiLambdaTracks = partFakeAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto lTrks = partLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); + auto alTrks = partAntiLambdaTracks->sliceByCached(aod::lambdatrack::lambdaCollisionId, collision.globalIndex(), cache); - // Skip collision if there are no true lambdas of any kind - if (lambdaTracks.size() == 0 && antiLambdaTracks.size() == 0 && fakeLambdaTracks.size() == 0 && fakeAntiLambdaTracks.size() == 0) { + if (!hasMinCandidates(lTrks, alTrks)) { return; } - // Fill collision table once per collision lambdaMixEvtCol(collision.index(), collision.cent(), collision.posZ(), collision.timeStamp()); - for (auto const& track : lambdaTracks) { - lambdaMixEvtTrk(collision.index(), track.globalIndex(), - track.px(), track.py(), track.pz(), track.mass(), - track.prPx(), track.prPy(), track.prPz(), - track.v0Type(), collision.timeStamp()); + auto fillMixEventTrack = [&](auto const& track) { + const bool shared = track.lambdaSharingDaughter(); + lambdaMixEvtTrk(collision.index(), track.globalIndex(), track.px(), + track.py(), track.pz(), track.mass(), track.prPx(), + track.prPy(), track.prPz(), track.v0Type(), + collision.timeStamp(), + shared ? static_cast(track.posTrackId()) : NoSharedTrackIdx, + shared ? static_cast(track.negTrackId()) : NoSharedTrackIdx); + }; + for (auto const& track : lTrks) { + fillMixEventTrack(track); + } + for (auto const& track : alTrks) { + fillMixEventTrack(track); } + } + PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixEvent, "Mix-event table filling", false); - for (auto const& track : antiLambdaTracks) { - lambdaMixEvtTrk(collision.index(), track.globalIndex(), - track.px(), track.py(), track.pz(), track.mass(), - track.prPx(), track.prPy(), track.prPz(), - track.v0Type(), collision.timeStamp()); - } + void processMcGenMixEvent(LambdaMcGenCollisions::iterator const& collision, aod::LambdaMcGenTracks const&) + { + auto lTrks = partMcLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); + auto alTrks = partMcAntiLambdaTracks->sliceByCached(aod::lambdamcgentrack::lambdaMcGenCollisionId, collision.globalIndex(), cachemc); - for (auto const& track : fakeLambdaTracks) { - lambdaMixEvtTrk(collision.index(), track.globalIndex(), - track.px(), track.py(), track.pz(), track.mass(), - track.prPx(), track.prPy(), track.prPz(), - track.v0Type(), collision.timeStamp()); + if (!hasMinCandidates(lTrks, alTrks)) { + return; } - for (auto const& track : fakeAntiLambdaTracks) { - lambdaMixEvtTrk(collision.index(), track.globalIndex(), - track.px(), track.py(), track.pz(), track.mass(), - track.prPx(), track.prPy(), track.prPz(), - track.v0Type(), collision.timeStamp()); + lambdaMixEvtMGCol(collision.globalIndex(), collision.cent(), + collision.posZ(), collision.timeStamp()); + + for (auto const& track : lTrks) { + lambdaMixEvtMGTrk(collision.globalIndex(), track.globalIndex(), + track.px(), track.py(), track.pz(), track.mass(), + track.prPx(), track.prPy(), track.prPz(), + track.v0Type(), collision.timeStamp()); + } + for (auto const& track : alTrks) { + lambdaMixEvtMGTrk(collision.globalIndex(), track.globalIndex(), + track.px(), track.py(), track.pz(), track.mass(), + track.prPx(), track.prPy(), track.prPz(), + track.v0Type(), collision.timeStamp()); } } - - PROCESS_SWITCH(LambdaSpinPolarization, processDataRecoMixEvent, "Process for Data and MCReco Mix Event", false); + PROCESS_SWITCH(LambdaSpinPolarization, processMcGenMixEvent, "Mix-event McGen table filling", false); }; - WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx index e688c05684d..8f66d3bfb27 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longRangeDihadronCor.cxx @@ -84,45 +84,52 @@ static constexpr float LongArrayFloat[3][6] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3} struct LongRangeDihadronCor { Service ccdb; o2::aod::ITSResponse itsResponse; - - O2_DEFINE_CONFIGURABLE(cfgCutVtxZ, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") - O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") - O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") - O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, false, "rejects collisions which are associated with the same found-by-T0 bunch crossing") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersAll, bool, true, "cut time intervals with dead ITS staves") - O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayer0123, bool, false, "cut time intervals with dead ITS staves (layers 0-3 only, for pp)") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") - O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") - O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") - O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") - O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") - O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - O2_DEFINE_CONFIGURABLE(cfgDrawEtaPhiDis, bool, false, "draw eta-phi distribution for detectors in used") + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgCutVtxZ, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum TPC crossed rows") + O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") + O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, float, 7.0f, "max DCA to vertex xy") + O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoSameBunchPileup, bool, false, "rejects collisions which are associated with the same found-by-T0 bunch crossing") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayersAll, bool, true, "cut time intervals with dead ITS staves") + O2_DEFINE_CONFIGURABLE(cfgEvSelkIsGoodITSLayer0123, bool, false, "cut time intervals with dead ITS staves (layers 0-3 only, for pp)") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelMultCorrelation, bool, true, "Multiplicity correlation cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelV0AT0ACut, bool, true, "V0A T0A 5 sigma cut") + O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgDrawEtaPhiDis, bool, false, "draw eta-phi distribution for detectors in used"); + O2_DEFINE_CONFIGURABLE(cfgEtaTpcCut, float, 0.8f, "Eta cut of TPC MC particles") + O2_DEFINE_CONFIGURABLE(cfgMinEtaFt0Cut, float, 0.0f, "Min eta cut of FT0 MC particles") + O2_DEFINE_CONFIGURABLE(cfgMaxEtaFt0Cut, float, 0.0f, "Max eta cut of FT0 MC particles") + O2_DEFINE_CONFIGURABLE(cfgUseFt0Structure, bool, true, "Use the true structure of FT0 in MC-true") + O2_DEFINE_CONFIGURABLE(cfgUseCFStepAll, bool, true, "Use CFStepAll in addition to primry"); + } cfgGeneral; struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -195,7 +202,7 @@ struct LongRangeDihadronCor { ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt associated axis for histograms"}; ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; - ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; + ConfigurableAxis axisSample{"axisSample", {cfgGeneral.cfgSampleSize, 0, cfgGeneral.cfgSampleSize}, "sample axis for histograms"}; ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; @@ -209,11 +216,24 @@ struct LongRangeDihadronCor { AxisSpec axisFit{cfgaxisFITamp, "fit amplitude"}; AxisSpec axisChID = {220, 0, 220}; // make the filters and cuts. - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVtxZ); - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgGeneral.cfgCutVtxZ); + Filter trackFilter = (nabs(aod::track::eta) < cfgGeneral.cfgCutEta) && (aod::track::pt > cfgGeneral.cfgCutPtMin) && (aod::track::pt < cfgGeneral.cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (aod::track::tpcChi2NCl < cfgGeneral.cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgGeneral.cfgCutDCAz); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; + Filter particleFilter = (nabs(aod::mcparticle::eta) < cfgGeneral.cfgEtaTpcCut || ((aod::mcparticle::eta > cfgGeneral.cfgMinEtaFt0Cut) && (aod::mcparticle::eta < cfgGeneral.cfgMaxEtaFt0Cut))) && (aod::mcparticle::pt > cfgGeneral.cfgCutPtMin) && (aod::mcparticle::pt < cfgGeneral.cfgCutPtMax); + using FilteredMcParticles = soa::Filtered; + + // Filter for MCcollisions + Filter mccollisionFilter = nabs(aod::mccollision::posZ) < cfgGeneral.cfgCutVtxZ; + using FilteredMcCollisions = soa::Filtered; + + using SmallGroupMcCollisions = soa::SmallGroups>; + + PresliceUnsorted collisionPerMCCollision = aod::mccollisionlabel::mcCollisionId; + Preslice perCollision = aod::track::collisionId; + Preslice perColMc = o2::aod::mcparticle::mcCollisionId; + // FT0 geometry o2::ft0::Geometry ft0Det; static constexpr uint64_t Ft0IndexA = 96; @@ -301,18 +321,39 @@ struct LongRangeDihadronCor { std::array tofNsigmaCut; std::array itsNsigmaCut; std::array tpcNsigmaCut; + + struct Ft0cCConstants { + double zFt0cDistance = -83.44; + double maxWidth = 12.; + double minWidthX = 18.25; + double minWidhtY = 18.175; + double insideWidthX = 6.825; + double insideWidthY = 6.675; + }; + struct Ft0aCConstants { + double zFt0aDistance = 338.43; + double offSetX = 0.588; + double offSetY = 1.468; + double blockFar = 14.875; + double blockClose = 2.65; + double rectOffSet = 1.325; + }; + Ft0cCConstants Ft0cLocations{}; + Ft0aCConstants Ft0aLocations{}; + int lastRunNumber = -1; std::vector runNumbers; std::map> histAmpCorrectPerRun; // map of TH3 histograms for all runs void init(InitContext&) { - if (cfgCentTableUnavailable && !cfgSelCollByNch) { + if (cfgGeneral.cfgCentTableUnavailable && !cfgGeneral.cfgSelCollByNch) { LOGF(fatal, "Centrality table is unavailable, cannot select collisions by centrality"); } const AxisSpec axisPhi{72, 0.0, constants::math::TwoPI, "#varphi"}; const AxisSpec axisEta{40, -1., 1., "#eta"}; const AxisSpec axisEtaFull{90, -4., 5., "#eta"}; + const AxisSpec axisCentrality{20, 0., 100., "cent"}; ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -344,7 +385,7 @@ struct LongRangeDihadronCor { itsNsigmaCut[kProtonLow] = cfgPIDConfig.nSigmas->getData()[kITS][kProtonLow]; // Event Counter - if ((doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameFt0aFt0c) && cfgUseAdditionalEventCut) { + if ((doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameFt0aFt0c || doprocessSemiMcSameTpcFt0c || doprocessSemiMcSameTpcFt0a) && cfgGeneral.cfgUseAdditionalEventCut) { registry.add("hEventCountSpecific", "Number of Event;; Count", {HistType::kTH1D, {{13, 0, 13}}}); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(1, "after sel8"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(2, "kNoSameBunchPileup"); @@ -358,10 +399,33 @@ struct LongRangeDihadronCor { registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(10, "kNoHighMultCollInPrevRof"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(11, "occupancy"); registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(12, "MultCorrelation"); - registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(13, "cfgEvSelV0AT0ACut"); + registry.get(HIST("hEventCountSpecific"))->GetXaxis()->SetBinLabel(13, "cfgGeneral.cfgEvSelV0AT0ACut"); + } + if (doprocessMcSameTpcFt0c || doprocessMcSameTpcFt0a || doprocessSemiMcSameTpcFt0c || doprocessSemiMcSameTpcFt0a) { + registry.add("MCTrue/MCeventcount", "MCeventcount", {HistType::kTH1F, {{5, 0, 5, "bin"}}}); // histogram to see how many events are in the same and mixed event + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(2, "same all"); + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(3, "same reco"); + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(4, "mixed all"); + registry.get(HIST("MCTrue/MCeventcount"))->GetXaxis()->SetBinLabel(5, "mixed reco"); + registry.add("MCTrue/MCCentrality", "cent", {HistType::kTH1D, {axisCentrality}}); + registry.add("MCTrue/MCNch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); + registry.add("MCTrue/MCzVtx", "MCzVtx", {HistType::kTH1D, {axisVertex}}); + registry.add("MCTrue/MCPhi", "MCPhi", {HistType::kTH1D, {axisPhi}}); + registry.add("MCTrue/MCEta", "MCEta", {HistType::kTH1D, {axisEtaFull}}); + registry.add("MCTrue/MCEtaTrueShape", "MCEta", {HistType::kTH1D, {axisEtaFull}}); + registry.add("MCTrue/MCpT", "MCpT", {HistType::kTH1D, {axisPtEfficiency}}); + registry.add("MCTrue/MCTrig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtEfficiency}}}); + if (doprocessMcSameTpcFt0c || doprocessSemiMcSameTpcFt0c) { + registry.add("MCTrue/MCdeltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + registry.add("MCTrue/MCdeltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + } + if (doprocessMcSameTpcFt0a || doprocessSemiMcSameTpcFt0a) { + registry.add("MCTrue/MCdeltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + registry.add("MCTrue/MCdeltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + } } - if (cfgEvSelMultCorrelation) { + if (cfgGeneral.cfgEvSelMultCorrelation) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; @@ -392,9 +456,9 @@ struct LongRangeDihadronCor { cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } - std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); + std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgGeneral.cfgCentEstimator); // Make histograms to check the distributions after cuts - if (doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameFt0aFt0c) { + if (doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameFt0aFt0c || doprocessSemiMcSameTpcFt0c || doprocessSemiMcSameTpcFt0a) { registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); @@ -409,18 +473,18 @@ struct LongRangeDihadronCor { registry.add("zVtx_used", "zVtx_used", {HistType::kTH1D, {axisVertex}}); registry.add("FT0Amp", "", {HistType::kTH2F, {axisChID, axisFit}}); registry.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, axisFit}}); - if (cfgDrawEtaPhiDis) { + if (cfgGeneral.cfgDrawEtaPhiDis) { registry.add("EtaPhi", "", {HistType::kTH2F, {axisEtaFull, axisPhi}}); } } - if (doprocessSameTpcFt0a) { + if (doprocessSameTpcFt0a || doprocessSemiMcSameTpcFt0a) { registry.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); registry.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); registry.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); } - if (doprocessSameTpcFt0c) { + if (doprocessSameTpcFt0c || doprocessSemiMcSameTpcFt0c) { registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); registry.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0a}}); @@ -461,11 +525,11 @@ struct LongRangeDihadronCor { {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEtaFt0aFt0c, "#Delta#eta"}}; - if (doprocessSameTpcFt0a) { + if (doprocessSameTpcFt0a || doprocessMcSameTpcFt0a || doprocessSemiMcSameTpcFt0a) { sameTpcFt0a.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); mixedTpcFt0a.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); } - if (doprocessSameTpcFt0c) { + if (doprocessSameTpcFt0c || doprocessMcSameTpcFt0c || doprocessSemiMcSameTpcFt0c) { sameTpcFt0c.setObject(new CorrelationContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); mixedTpcFt0c.setObject(new CorrelationContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); } @@ -474,7 +538,7 @@ struct LongRangeDihadronCor { mixedFt0aFt0c.setObject(new CorrelationContainer("mixedEvent_FT0A_FT0C", "mixedEvent_FT0A_FT0C", corrAxisFt0aFt0c, effAxis, userAxis)); } - if (cfgCumulantConfig.gfwOutput && doprocessSameTpcFt0a && doprocessSameTpcFt0c) { + if (cfgCumulantConfig.gfwOutput && (doprocessSameTpcFt0a && doprocessSameTpcFt0c)) { LOGF(fatal, "If you enable gfwOutput, you can only enable processSameTpcFt0a or processSameTpcFt0c, NOT both."); } if (cfgCumulantConfig.gfwOutput) { @@ -587,7 +651,7 @@ struct LongRangeDihadronCor { float getCentrality(TCollision const& collision) { float cent; - switch (cfgCentEstimator) { + switch (cfgGeneral.cfgCentEstimator) { case kCentFT0C: cent = collision.centFT0C(); break; @@ -607,13 +671,13 @@ struct LongRangeDihadronCor { } template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { - return ((track.tpcNClsFound() >= cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgCutITSclu)); + return ((track.tpcNClsFound() >= cfgGeneral.cfgCutTPCclu) && (track.tpcNClsCrossedRows() >= cfgGeneral.cfgCutTPCCrossedRows) && (track.itsNCls() >= cfgGeneral.cfgCutITSclu)); } template - int getNsigmaPID(TTrack track) + int getNsigmaPID(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -703,24 +767,24 @@ struct LongRangeDihadronCor { if (correctionsLoaded) { return; } - if (cfgEfficiency.value.empty() == false) { - if (cfgLocalEfficiency > 0) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); + if (cfgGeneral.cfgEfficiency.value.empty() == false) { + if (cfgGeneral.cfgLocalEfficiency > 0) { + TFile* fEfficiencyTrigger = TFile::Open(cfgGeneral.cfgEfficiency.value.c_str(), "READ"); mEfficiency = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + mEfficiency = ccdb->getForTimeStamp(cfgGeneral.cfgEfficiency, timestamp); } if (mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgGeneral.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgGeneral.cfgEfficiency.value.c_str(), (void*)mEfficiency); } - if (cfgCentralityWeight.value.empty() == false) { - mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + if (cfgGeneral.cfgCentralityWeight.value.empty() == false) { + mCentralityWeight = ccdb->getForTimeStamp(cfgGeneral.cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgGeneral.cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgGeneral.cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); } if (cfgCumulantConfig.gfwOutput && cfgCumulantConfig.gfwAcceptance.value.empty() == false) { mAcceptance = ccdb->getForTimeStamp(cfgCumulantConfig.gfwAcceptance, timestamp); @@ -838,7 +902,7 @@ struct LongRangeDihadronCor { // fill multiple histograms template - void fillYield(TCollision collision, TTracks tracks) // function to fill the yield and etaphi histograms. + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. { float weff1 = 1; float vtxz = collision.posZ(); @@ -906,15 +970,15 @@ struct LongRangeDihadronCor { } template - void fillCorrelationsTPCFT0(TTracks tracks1, TFT0s const& ft0, float posZ, int system, int corType, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + void fillCorrelationsTPCFT0(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, int corType, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { if (system == SameEvent) { - if (!cfgCentTableUnavailable) + if (!cfgGeneral.cfgCentTableUnavailable) registry.fill(HIST("Centrality_used"), cent); registry.fill(HIST("Nch_used"), tracks1.size()); } - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = gRandom->Uniform(0, cfgGeneral.cfgSampleSize); float triggerWeight = 1.0f; // loop over all tracks @@ -932,7 +996,7 @@ struct LongRangeDihadronCor { } else if (corType == kFT0A) { registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); } - if (cfgDrawEtaPhiDis && corType == kFT0A) { + if (cfgGeneral.cfgDrawEtaPhiDis && corType == kFT0A) { registry.fill(HIST("EtaPhi"), track1.eta(), track1.phi(), eventWeight * triggerWeight); } } @@ -962,7 +1026,7 @@ struct LongRangeDihadronCor { auto phi = getPhiFT0(chanelid, corType); auto eta = getEtaFT0(chanelid, corType); - if (cfgDrawEtaPhiDis && system == SameEvent) { + if (cfgGeneral.cfgDrawEtaPhiDis && system == SameEvent) { registry.fill(HIST("EtaPhi"), eta, phi, ampl * eventWeight); if (mirrorChannel) registry.fill(HIST("EtaPhi"), eta, 4 * PIHalf - phi, ampl * eventWeight); @@ -1006,7 +1070,7 @@ struct LongRangeDihadronCor { template void fillCorrelationsFT0AFT0C(TFT0s const& ft0Col1, TFT0s const& ft0Col2, float posZ, int system, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = gRandom->Uniform(0, cfgGeneral.cfgSampleSize); float triggerWeight = 1.0f; std::size_t channelASize = ft0Col1.channelA().size(); @@ -1067,80 +1131,253 @@ struct LongRangeDihadronCor { } } + template + void fillMCCorrelations(const TTracks& tracks1, const TTracksAssoc& tracks2, float posZ, int system, float eventWeight, int corType) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = gRandom->Uniform(0, cfgGeneral.cfgSampleSize); + double tpcEtaAcceptance = 0.8001; + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + // loop over all FT0 tracks + for (auto const& track1 : tracks1) { + + if (corType == kFT0C && !cfgGeneral.cfgUseFt0Structure) { + if (std::abs(track1.eta()) < tpcEtaAcceptance) + continue; + } + if (corType == kFT0C && cfgGeneral.cfgUseFt0Structure && !ft0cCollisionCourse(track1.eta(), track1.phi(), posZ)) { + continue; + } + if (corType == kFT0A && !cfgGeneral.cfgUseFt0Structure) { + if (std::abs(track1.eta()) < tpcEtaAcceptance) + continue; + } + if (corType == kFT0A && cfgGeneral.cfgUseFt0Structure && !ft0aCollisionCourse(track1.eta(), track1.phi(), posZ)) { + continue; + } + + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track1.isPhysicalPrimary()) + continue; + + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && system == SameEvent) + registry.fill(HIST("MCTrue/MCEtaTrueShape"), track1.eta()); + + if (system == SameEvent && (doprocessMcSameTpcFt0c || doprocessMcSameTpcFt0a)) + registry.fill(HIST("MCTrue/MCTrig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + + // loop over all TPC tracks + for (auto const& track2 : tracks2) { + + if (std::abs(track2.eta()) > tpcEtaAcceptance) + continue; + + if (step >= CorrelationContainer::kCFStepTrackedOnlyPrim && !track2.isPhysicalPrimary()) + continue; + + if (track1.globalIndex() == track2.globalIndex()) + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + + float deltaPhi = RecoDecay::constrainAngle(track2.phi() - track1.phi(), -PIHalf); + float deltaEta = track2.eta() - track1.eta(); + + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == kFT0C) { + sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track2.pt(), track1.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + if (corType == kFT0A) { + sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track2.pt(), track1.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } else if (system == MixedEvent) { + if (corType == kFT0C) { + mixedTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track2.pt(), track1.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + if (corType == kFT0A) { + mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track2.pt(), track1.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + registry.fill(HIST("MCTrue/MCdeltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + } + + template + void fillSemiMcCorrelations(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, int corType, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + if (system == SameEvent) { + if (!cfgGeneral.cfgCentTableUnavailable) + registry.fill(HIST("Centrality_used"), cent); + registry.fill(HIST("Nch_used"), tracks1.size()); + } + + int fSampleIndex = gRandom->Uniform(0, cfgGeneral.cfgSampleSize); + double tpcEtaAcceptance = 0.8001; + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (std::abs(track1.eta()) > tpcEtaAcceptance) + continue; + if (!track1.isPhysicalPrimary()) + continue; + + if (system == SameEvent) { + if (corType == kFT0C) { + registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + } else if (corType == kFT0A) { + registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + } + } + + std::size_t channelSize = 0; + if (corType == kFT0C) { + channelSize = ft0.channelC().size(); + } else if (corType == kFT0A) { + channelSize = ft0.channelA().size(); + } else { + LOGF(fatal, "Cor Index %d out of range", corType); + } + + if (channelSize == 0) + continue; + + for (std::size_t iCh = 0; iCh < channelSize; iCh++) { + int chanelid = 0; + float ampl = 0.; + getChannel(ft0, iCh, chanelid, ampl, corType, system); + if (corType == kFT0C) { + if ((cfgFwdConfig.cfgRejectFT0CInside && (chanelid >= kFT0CInnerRingMin && chanelid <= kFT0CInnerRingMax)) || (cfgFwdConfig.cfgRejectFT0COutside && (chanelid >= kFT0COuterRingMin && chanelid <= kFT0COuterRingMax))) + continue; + } else if (corType == kFT0A) { + if ((cfgFwdConfig.cfgRejectFT0AInside && (chanelid >= kFT0AInnerRingMin && chanelid <= kFT0AInnerRingMax)) || (cfgFwdConfig.cfgRejectFT0AOutside && (chanelid >= kFT0AOuterRingMin && chanelid <= kFT0AOuterRingMax))) + continue; + } + bool mirrorChannel = false; + if ((corType == kFT0A && cfgFwdConfig.cfgMirrorFT0ADeadChannels) || (corType == kFT0C && cfgFwdConfig.cfgMirrorFT0CDeadChannels)) + mirrorChannel = isMirrorId(chanelid, corType); + + auto phi = getPhiFT0(chanelid, corType); + auto eta = getEtaFT0(chanelid, corType); + if (cfgGeneral.cfgDrawEtaPhiDis && system == SameEvent) { + registry.fill(HIST("EtaPhi"), eta, phi, ampl * eventWeight); + if (mirrorChannel) + registry.fill(HIST("EtaPhi"), eta, 4 * PIHalf - phi, ampl * eventWeight); + } + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); + float deltaEta = track1.eta() - eta; + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == kFT0A) { + registry.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (mirrorChannel) + sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), RecoDecay::constrainAngle(track1.phi() - phi - 2 * PIHalf, -PIHalf), deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == kFT0C) { + registry.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (mirrorChannel) + sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), RecoDecay::constrainAngle(track1.phi() - phi - 2 * PIHalf, -PIHalf), deltaEta, ampl * eventWeight * triggerWeight); + } + } else if (system == MixedEvent) { + if (corType == kFT0A) { + registry.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (mirrorChannel) + mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), RecoDecay::constrainAngle(track1.phi() - phi - 2 * PIHalf, -PIHalf), deltaEta, ampl * eventWeight * triggerWeight); + } else if (corType == kFT0C) { + registry.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + mixedTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (mirrorChannel) + mixedTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track1.pt(), RecoDecay::constrainAngle(track1.phi() - phi - 2 * PIHalf, -PIHalf), deltaEta, ampl * eventWeight * triggerWeight); + } + } + } + } + } + template - bool eventSelected(TCollision collision, const int multTrk, const float centrality, const bool fillCounter) + bool eventSelected(const TCollision& collision, const int multTrk, const float centrality, const bool fillCounter) { registry.fill(HIST("hEventCountSpecific"), 0.5); - if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + if (cfgGeneral.cfgEvSelkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof return 0; } - if (fillCounter && cfgEvSelkNoSameBunchPileup) + if (fillCounter && cfgGeneral.cfgEvSelkNoSameBunchPileup) registry.fill(HIST("hEventCountSpecific"), 1.5); - if (cfgEvSelkNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + if (cfgGeneral.cfgEvSelkNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { return 0; } - if (fillCounter && cfgEvSelkNoITSROFrameBorder) + if (fillCounter && cfgGeneral.cfgEvSelkNoITSROFrameBorder) registry.fill(HIST("hEventCountSpecific"), 2.5); - if (cfgEvSelkNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + if (cfgGeneral.cfgEvSelkNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { return 0; } - if (fillCounter && cfgEvSelkNoTimeFrameBorder) + if (fillCounter && cfgGeneral.cfgEvSelkNoTimeFrameBorder) registry.fill(HIST("hEventCountSpecific"), 3.5); - if (cfgEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (cfgGeneral.cfgEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution return 0; } - if (fillCounter && cfgEvSelkIsGoodZvtxFT0vsPV) + if (fillCounter && cfgGeneral.cfgEvSelkIsGoodZvtxFT0vsPV) registry.fill(HIST("hEventCountSpecific"), 4.5); - if (cfgEvSelkNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (cfgGeneral.cfgEvSelkNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range return 0; } - if (fillCounter && cfgEvSelkNoCollInTimeRangeStandard) + if (fillCounter && cfgGeneral.cfgEvSelkNoCollInTimeRangeStandard) registry.fill(HIST("hEventCountSpecific"), 5.5); - if (cfgEvSelkIsGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (cfgGeneral.cfgEvSelkIsGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves return 0; } - if (fillCounter && cfgEvSelkIsGoodITSLayersAll) + if (fillCounter && cfgGeneral.cfgEvSelkIsGoodITSLayersAll) registry.fill(HIST("hEventCountSpecific"), 6.5); - if (cfgEvSelkIsGoodITSLayer0123 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + if (cfgGeneral.cfgEvSelkIsGoodITSLayer0123 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { // for pp: cut time intervals with dead ITS staves on layers 0-3 only return 0; } - if (fillCounter && cfgEvSelkIsGoodITSLayer0123) + if (fillCounter && cfgGeneral.cfgEvSelkIsGoodITSLayer0123) registry.fill(HIST("hEventCountSpecific"), 7.5); - if (cfgEvSelkNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (cfgGeneral.cfgEvSelkNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold return 0; } - if (fillCounter && cfgEvSelkNoCollInRofStandard) + if (fillCounter && cfgGeneral.cfgEvSelkNoCollInRofStandard) registry.fill(HIST("hEventCountSpecific"), 8.5); - if (cfgEvSelkNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (cfgGeneral.cfgEvSelkNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold return 0; } - if (fillCounter && cfgEvSelkNoHighMultCollInPrevRof) + if (fillCounter && cfgGeneral.cfgEvSelkNoHighMultCollInPrevRof) registry.fill(HIST("hEventCountSpecific"), 9.5); auto occupancy = collision.trackOccupancyInTimeRange(); - if (cfgEvSelOccupancy && (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh)) + if (cfgGeneral.cfgEvSelOccupancy && (occupancy < cfgGeneral.cfgCutOccupancyLow || occupancy > cfgGeneral.cfgCutOccupancyHigh)) return 0; - if (fillCounter && cfgEvSelOccupancy) + if (fillCounter && cfgGeneral.cfgEvSelOccupancy) registry.fill(HIST("hEventCountSpecific"), 10.5); auto multNTracksPV = collision.multNTracksPV(); - if (cfgEvSelMultCorrelation) { - if (cfgFuncParas.cfgMultPVT0CCutEnabled && !cfgCentTableUnavailable) { + if (cfgGeneral.cfgEvSelMultCorrelation) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled && !cfgGeneral.cfgCentTableUnavailable) { if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(centrality)) return 0; if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(centrality)) return 0; } - if (cfgFuncParas.cfgMultT0CCutEnabled && !cfgCentTableUnavailable) { + if (cfgFuncParas.cfgMultT0CCutEnabled && !cfgGeneral.cfgCentTableUnavailable) { if (multTrk < cfgFuncParas.fMultT0CCutLow->Eval(centrality)) return 0; if (multTrk > cfgFuncParas.fMultT0CCutHigh->Eval(centrality)) @@ -1159,19 +1396,100 @@ struct LongRangeDihadronCor { return 0; } } - if (fillCounter && cfgEvSelMultCorrelation) + if (fillCounter && cfgGeneral.cfgEvSelMultCorrelation) registry.fill(HIST("hEventCountSpecific"), 11.5); // V0A T0A 5 sigma cut float sigma = 5.0; - if (cfgEvSelV0AT0ACut && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) + if (cfgGeneral.cfgEvSelV0AT0ACut && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > sigma * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) return 0; - if (fillCounter && cfgEvSelV0AT0ACut) + if (fillCounter && cfgGeneral.cfgEvSelV0AT0ACut) registry.fill(HIST("hEventCountSpecific"), 12.5); return 1; } + bool ft0cCollisionCourse(float eta, float phi, float vtxZ) + { + double theta = 2 * std::atan(std::exp(-eta)); + double vx = std::sin(theta) * std::cos(phi); // veloctiy component along x + double vy = std::sin(theta) * std::sin(phi); // veloctiy component along y + double vz = std::cos(theta); // veloctiy component along z + + if (vz > 0.) + return false; + + double x = vx * ((Ft0cLocations.zFt0cDistance - vtxZ) / vz); // location of particle on x at FT0C distance from vertex + double y = vy * ((Ft0cLocations.zFt0cDistance - vtxZ) / vz); // location of particle on x at FT0C distance from vertex + + if (std::abs(x) < Ft0cLocations.maxWidth && (std::abs(y) > Ft0cLocations.insideWidthX && std::abs(y) < Ft0cLocations.minWidthX)) + return true; + if (std::abs(y) < Ft0cLocations.maxWidth && (std::abs(x) > Ft0cLocations.insideWidthY && std::abs(x) < Ft0cLocations.minWidhtY)) + return true; + return false; + } + + bool ft0aCollisionCourse(float eta, float phi, float vtxZ) + { + double theta = 2 * std::atan(std::exp(-eta)); + double vx = std::sin(theta) * std::cos(phi); // veloctiy component along x + double vy = std::sin(theta) * std::sin(phi); // veloctiy component along y + double vz = std::cos(theta); // veloctiy component along z + + if (vz < 0.) + return false; + + double x = vx * ((Ft0aLocations.zFt0aDistance - vtxZ) / vz); // location of particle on x at FT0C distance from vertex + double y = vy * ((Ft0aLocations.zFt0aDistance - vtxZ) / vz); // location of particle on x at FT0C distance from vertex + + if (x < (Ft0aLocations.blockFar + Ft0aLocations.offSetX) && x > (Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { + if (y < (Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y > (Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { + return true; + } + } + if (x < (Ft0aLocations.blockFar + Ft0aLocations.offSetX) && x > (Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { + if (y > (-Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y < (-Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { + return true; + } + } + + if (x > (-Ft0aLocations.blockFar + Ft0aLocations.offSetX) && x < (-Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { + if (y < (Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y > (Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { + return true; + } + } + if (x > (-Ft0aLocations.blockFar + Ft0aLocations.offSetX) && x < (-Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { + if (y > (-Ft0aLocations.blockFar + Ft0aLocations.offSetY) && y < (-Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { + return true; + } + } + + if (x < (Ft0aLocations.blockClose + Ft0aLocations.offSetX) && x > (-Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { + if (y > (-Ft0aLocations.blockFar + Ft0aLocations.offSetY - Ft0aLocations.rectOffSet) && y < (-Ft0aLocations.blockClose + Ft0aLocations.offSetY - Ft0aLocations.rectOffSet)) { + return true; + } + } + + if (x < (Ft0aLocations.blockClose + Ft0aLocations.offSetX) && x > (-Ft0aLocations.blockClose + Ft0aLocations.offSetX)) { + if (y < (Ft0aLocations.blockFar + Ft0aLocations.offSetY + Ft0aLocations.rectOffSet) && y > (Ft0aLocations.blockClose + Ft0aLocations.offSetY + Ft0aLocations.rectOffSet)) { + return true; + } + } + + if (y < (Ft0aLocations.blockClose + Ft0aLocations.offSetY) && y > (-Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { + if (x > (-Ft0aLocations.blockFar + Ft0aLocations.offSetX - Ft0aLocations.rectOffSet) && x < (-Ft0aLocations.blockClose + Ft0aLocations.offSetX - Ft0aLocations.rectOffSet)) { + return true; + } + } + + if (y < (Ft0aLocations.blockClose + Ft0aLocations.offSetY) && y > (-Ft0aLocations.blockClose + Ft0aLocations.offSetY)) { + if (x < (Ft0aLocations.blockFar + Ft0aLocations.offSetX + Ft0aLocations.rectOffSet) && x > (Ft0aLocations.blockClose + Ft0aLocations.offSetX + Ft0aLocations.rectOffSet)) { + return true; + } + } + return false; + } + void processSameTpcFt0a(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) { if (!collision.sel8()) @@ -1179,17 +1497,17 @@ struct LongRangeDihadronCor { auto bc = collision.bc_as(); float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { cent = getCentrality(collision); } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) return; if (!collision.has_foundFT0()) return; loadAlignParam(bc.timestamp()); loadGain(bc); loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); registry.fill(HIST("Centrality"), cent); registry.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1231,10 +1549,10 @@ struct LongRangeDihadronCor { } } - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + if (cfgGeneral.cfgSelCollByNch && (tracks.size() < cfgGeneral.cfgCutMultMin || tracks.size() >= cfgGeneral.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent < cfgGeneral.cfgCutCentMin || cent >= cfgGeneral.cfgCutCentMax)) { return; } @@ -1274,36 +1592,36 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; if (!collision1.sel8() || !collision2.sel8()) continue; - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + if (cfgGeneral.cfgSelCollByNch && (tracks1.size() < cfgGeneral.cfgCutMultMin || tracks1.size() >= cfgGeneral.cfgCutMultMax)) continue; - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + if (cfgGeneral.cfgSelCollByNch && (tracks2.size() < cfgGeneral.cfgCutMultMin || tracks2.size() >= cfgGeneral.cfgCutMultMax)) continue; float cent1 = -1; float cent2 = -1; - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { cent1 = getCentrality(collision1); cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) continue; - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent1 < cfgGeneral.cfgCutCentMin || cent1 >= cfgGeneral.cfgCutCentMax)) continue; - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent2 < cfgGeneral.cfgCutCentMin || cent2 >= cfgGeneral.cfgCutCentMax)) continue; if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) @@ -1314,11 +1632,11 @@ struct LongRangeDihadronCor { loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgGeneral.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgGeneral.cfgCentTableUnavailable) getCentralityWeight(weightCent, cent1); const auto& ft0 = collision2.foundFT0(); fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, kFT0A, cent1, eventWeight * weightCent); @@ -1333,17 +1651,17 @@ struct LongRangeDihadronCor { auto bc = collision.bc_as(); float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { cent = getCentrality(collision); } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) return; if (!collision.has_foundFT0()) return; loadAlignParam(bc.timestamp()); loadGain(bc); loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); registry.fill(HIST("Centrality"), cent); registry.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1385,10 +1703,10 @@ struct LongRangeDihadronCor { } } - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + if (cfgGeneral.cfgSelCollByNch && (tracks.size() < cfgGeneral.cfgCutMultMin || tracks.size() >= cfgGeneral.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent < cfgGeneral.cfgCutCentMin || cent >= cfgGeneral.cfgCutCentMax)) { return; } @@ -1428,36 +1746,36 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; if (!collision1.sel8() || !collision2.sel8()) continue; - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + if (cfgGeneral.cfgSelCollByNch && (tracks1.size() < cfgGeneral.cfgCutMultMin || tracks1.size() >= cfgGeneral.cfgCutMultMax)) continue; - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + if (cfgGeneral.cfgSelCollByNch && (tracks2.size() < cfgGeneral.cfgCutMultMin || tracks2.size() >= cfgGeneral.cfgCutMultMax)) continue; float cent1 = -1; float cent2 = -1; - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { cent1 = getCentrality(collision1); cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) continue; - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent1 < cfgGeneral.cfgCutCentMin || cent1 >= cfgGeneral.cfgCutCentMax)) continue; - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent2 < cfgGeneral.cfgCutCentMin || cent2 >= cfgGeneral.cfgCutCentMax)) continue; if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) @@ -1468,11 +1786,11 @@ struct LongRangeDihadronCor { loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgGeneral.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgGeneral.cfgCentTableUnavailable) getCentralityWeight(weightCent, cent1); const auto& ft0 = collision2.foundFT0(); fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, kFT0C, cent1, eventWeight * weightCent); @@ -1487,10 +1805,10 @@ struct LongRangeDihadronCor { auto bc = collision.bc_as(); float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { cent = getCentrality(collision); } - if (cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) return; if (!collision.has_foundFT0()) return; @@ -1499,10 +1817,10 @@ struct LongRangeDihadronCor { loadCorrection(bc.timestamp()); // should have the same event to TPC-FT0A/C correlations - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + if (cfgGeneral.cfgSelCollByNch && (tracks.size() < cfgGeneral.cfgCutMultMin || tracks.size() >= cfgGeneral.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent < cfgGeneral.cfgCutCentMin || cent >= cfgGeneral.cfgCutCentMax)) { return; } @@ -1539,33 +1857,33 @@ struct LongRangeDihadronCor { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; // should have the same event to TPC-FT0A/C correlations if (!collision1.sel8() || !collision2.sel8()) continue; - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) + if (cfgGeneral.cfgSelCollByNch && (tracks1.size() < cfgGeneral.cfgCutMultMin || tracks1.size() >= cfgGeneral.cfgCutMultMax)) continue; - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + if (cfgGeneral.cfgSelCollByNch && (tracks2.size() < cfgGeneral.cfgCutMultMin || tracks2.size() >= cfgGeneral.cfgCutMultMax)) continue; float cent1 = -1; float cent2 = -1; - if (!cfgCentTableUnavailable) { + if (!cfgGeneral.cfgCentTableUnavailable) { cent1 = getCentrality(collision1); cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) continue; - if (cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) continue; - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent1 < cfgGeneral.cfgCutCentMin || cent1 >= cfgGeneral.cfgCutCentMax)) continue; - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent2 < cfgGeneral.cfgCutCentMin || cent2 >= cfgGeneral.cfgCutCentMax)) continue; if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) @@ -1575,11 +1893,11 @@ struct LongRangeDihadronCor { loadAlignParam(bc.timestamp()); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgGeneral.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgGeneral.cfgCentTableUnavailable) getCentralityWeight(weightCent, cent1); const auto& ft0Col1 = collision1.foundFT0(); const auto& ft0Col2 = collision2.foundFT0(); @@ -1587,6 +1905,604 @@ struct LongRangeDihadronCor { } } PROCESS_SWITCH(LongRangeDihadronCor, processMixedFt0aFt0c, "Process mixed events for FT0A-FT0C correlation", false); + + void processMcSameTpcFt0c(FilteredMcCollisions::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + float cent = -1; + if (!cfgGeneral.cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + } + + int tpcMult = 0; + for (const auto& track : mcParticles) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + + if (cfgCumulantConfig.gfwOutput) { + cfgCumulantConfig.fGFW->Clear(); + float lRandom = cfgCumulantConfig.fRndm->Rndm(); + float independent = cent; + if (cfgCumulantConfig.gfwUseNch) + independent = static_cast(tpcMult); + + // fill TPC Q-vector + for (const auto& track : mcParticles) { + if (std::abs(track.eta()) > cfgGeneral.cfgEtaTpcCut) + continue; + if (!track.isPhysicalPrimary()) + continue; + if (std::abs(track.eta()) > cfgGeneral.cfgEtaTpcCut) + continue; + bool withinPtPOI = (0.2 < track.pt()) && (track.pt() < 10.0); // o2-linter: disable=magic-number (within POI pT range) + bool withinPtRef = (0.2 < track.pt()) && (track.pt() < 3.0); // o2-linter: disable=magic-number (within RF pT range) + if (withinPtRef) + cfgCumulantConfig.fGFW->Fill(track.eta(), cfgCumulantConfig.fPtAxis->FindBin(track.pt()) - 1, track.phi(), 1., 1); + if (withinPtPOI) + cfgCumulantConfig.fGFW->Fill(track.eta(), cfgCumulantConfig.fPtAxis->FindBin(track.pt()) - 1, track.phi(), 1., 2); + if (withinPtPOI && withinPtRef) + cfgCumulantConfig.fGFW->Fill(track.eta(), cfgCumulantConfig.fPtAxis->FindBin(track.pt()) - 1, track.phi(), 1., 4); + } + // fill FT0 Q-vector + for (const auto& tackFT : mcParticles) { + if (std::abs(tackFT.eta()) < cfgGeneral.cfgEtaTpcCut) + continue; + + if (cfgGeneral.cfgUseFt0Structure && !ft0cCollisionCourse(tackFT.eta(), tackFT.phi(), mcCollision.posZ())) { + continue; + } + if (!tackFT.isPhysicalPrimary()) + continue; + + cfgCumulantConfig.fGFW->Fill(tackFT.eta(), 1, tackFT.phi(), 1., 1); + } + + // Filling Flow Container + for (uint l_ind = 0; l_ind < cfgCumulantConfig.corrconfigs.size(); l_ind++) { + fillFC(cfgCumulantConfig.corrconfigs.at(l_ind), independent, lRandom); + } + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult < cfgGeneral.cfgCutMultMin || tpcMult >= cfgGeneral.cfgCutMultMax)) { + return; + } + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent < cfgGeneral.cfgCutCentMin || cent >= cfgGeneral.cfgCutCentMax)) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + if (!cfgGeneral.cfgCentTableUnavailable) + registry.fill(HIST("MCTrue/MCCentrality"), cent); + registry.fill(HIST("MCTrue/MCNch"), tpcMult); + registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), mcParticle.phi()); + registry.fill(HIST("MCTrue/MCEta"), mcParticle.eta()); + registry.fill(HIST("MCTrue/MCpT"), mcParticle.pt()); + } + } + if (cfgGeneral.cfgUseCFStepAll) { + sameTpcFt0c->fillEvent(tpcMult, CorrelationContainer::kCFStepAll); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0C); + } + + registry.fill(HIST("MCTrue/MCeventcount"), 2.5); + sameTpcFt0c->fillEvent(tpcMult, CorrelationContainer::kCFStepTrackedOnlyPrim); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0C); + } + PROCESS_SWITCH(LongRangeDihadronCor, processMcSameTpcFt0c, "Process MC same event", false); + + void processMcSameTpcFt0a(FilteredMcCollisions::iterator const& mcCollision, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + float cent = -1; + if (!cfgGeneral.cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + } + + int tpcMult = 0; + for (const auto& track : mcParticles) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + + if (cfgCumulantConfig.gfwOutput) { + cfgCumulantConfig.fGFW->Clear(); + float lRandom = cfgCumulantConfig.fRndm->Rndm(); + float independent = cent; + if (cfgCumulantConfig.gfwUseNch) + independent = static_cast(tpcMult); + + // fill TPC Q-vector + for (const auto& track : mcParticles) { + if (std::abs(track.eta()) > cfgGeneral.cfgEtaTpcCut) + continue; + if (!track.isPhysicalPrimary()) + continue; + if (std::abs(track.eta()) > cfgGeneral.cfgEtaTpcCut) + continue; + bool withinPtPOI = (0.2 < track.pt()) && (track.pt() < 10.0); // o2-linter: disable=magic-number (within POI pT range) + bool withinPtRef = (0.2 < track.pt()) && (track.pt() < 3.0); // o2-linter: disable=magic-number (within RF pT range) + if (withinPtRef) + cfgCumulantConfig.fGFW->Fill(track.eta(), cfgCumulantConfig.fPtAxis->FindBin(track.pt()) - 1, track.phi(), 1., 1); + if (withinPtPOI) + cfgCumulantConfig.fGFW->Fill(track.eta(), cfgCumulantConfig.fPtAxis->FindBin(track.pt()) - 1, track.phi(), 1., 2); + if (withinPtPOI && withinPtRef) + cfgCumulantConfig.fGFW->Fill(track.eta(), cfgCumulantConfig.fPtAxis->FindBin(track.pt()) - 1, track.phi(), 1., 4); + } + // fill FT0 Q-vector + for (const auto& tackFT : mcParticles) { + if (std::abs(tackFT.eta()) < cfgGeneral.cfgEtaTpcCut) + continue; + + if (cfgGeneral.cfgUseFt0Structure && !ft0aCollisionCourse(tackFT.eta(), tackFT.phi(), mcCollision.posZ())) { + continue; + } + if (!tackFT.isPhysicalPrimary()) + continue; + + cfgCumulantConfig.fGFW->Fill(tackFT.eta(), 1, tackFT.phi(), 1., 1); + } + + // Filling Flow Container + for (uint l_ind = 0; l_ind < cfgCumulantConfig.corrconfigs.size(); l_ind++) { + fillFC(cfgCumulantConfig.corrconfigs.at(l_ind), independent, lRandom); + } + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult < cfgGeneral.cfgCutMultMin || tpcMult >= cfgGeneral.cfgCutMultMax)) { + return; + } + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent < cfgGeneral.cfgCutCentMin || cent >= cfgGeneral.cfgCutCentMax)) { + return; + } + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + if (!cfgGeneral.cfgCentTableUnavailable) + registry.fill(HIST("MCTrue/MCCentrality"), cent); + registry.fill(HIST("MCTrue/MCNch"), tpcMult); + registry.fill(HIST("MCTrue/MCzVtx"), mcCollision.posZ()); + for (const auto& mcParticle : mcParticles) { + if (mcParticle.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), mcParticle.phi()); + registry.fill(HIST("MCTrue/MCEta"), mcParticle.eta()); + registry.fill(HIST("MCTrue/MCpT"), mcParticle.pt()); + } + } + if (cfgGeneral.cfgUseCFStepAll) { + sameTpcFt0a->fillEvent(tpcMult, CorrelationContainer::kCFStepAll); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0A); + } + + registry.fill(HIST("MCTrue/MCeventcount"), 2.5); + sameTpcFt0a->fillEvent(tpcMult, CorrelationContainer::kCFStepTrackedOnlyPrim); + fillMCCorrelations(mcParticles, mcParticles, mcCollision.posZ(), SameEvent, 1.0f, kFT0A); + } + PROCESS_SWITCH(LongRangeDihadronCor, processMcSameTpcFt0a, "Process MC same event", false); + + void processMcMixedTpcFt0c(FilteredMcCollisions const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + int tpcMult = 0; + for (const auto& track : associatedTracks) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + return tpcMult; + }; + + using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + int tpcMult1 = 0; + for (const auto& track : tracks1) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult1 += 1; + } + int tpcMult2 = 0; + for (const auto& track : tracks2) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult2 += 1; + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult1 < cfgGeneral.cfgCutMultMin || tpcMult1 >= cfgGeneral.cfgCutMultMax)) + continue; + + if (cfgGeneral.cfgSelCollByNch && (tpcMult2 < cfgGeneral.cfgCutMultMin || tpcMult2 >= cfgGeneral.cfgCutMultMax)) + continue; + + auto groupedCollisions1 = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMCCollision, collision2.globalIndex()); + float cent1 = -1; + float cent2 = -1; + if (!cfgGeneral.cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions1) { + cent1 = getCentrality(collision); + } + for (const auto& collision : groupedCollisions2) { + cent2 = getCentrality(collision); + } + } + + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && ((cent1 < cfgGeneral.cfgCutCentMin || cent1 >= cfgGeneral.cfgCutCentMax) || (cent2 < cfgGeneral.cfgCutCentMin || cent2 >= cfgGeneral.cfgCutCentMax))) + continue; + + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + float eventWeight = 1.0f; + if (cfgGeneral.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + + if (cfgGeneral.cfgUseCFStepAll) { + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight, kFT0C); + } + + registry.fill(HIST("MCTrue/MCeventcount"), 4.5); + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight, kFT0C); + } + } + PROCESS_SWITCH(LongRangeDihadronCor, processMcMixedTpcFt0c, "Process MC mixed events", false); + + void processMcMixedTpcFt0a(FilteredMcCollisions const& mcCollisions, FilteredMcParticles const& mcParticles, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcParticles, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + int tpcMult = 0; + for (const auto& track : associatedTracks) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + return tpcMult; + }; + + using MixedBinning = FlexibleBinningPolicy, o2::aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + int tpcMult1 = 0; + for (const auto& track : tracks1) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult1 += 1; + } + int tpcMult2 = 0; + for (const auto& track : tracks2) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult2 += 1; + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult1 < cfgGeneral.cfgCutMultMin || tpcMult1 >= cfgGeneral.cfgCutMultMax)) + continue; + + if (cfgGeneral.cfgSelCollByNch && (tpcMult2 < cfgGeneral.cfgCutMultMin || tpcMult2 >= cfgGeneral.cfgCutMultMax)) + continue; + + auto groupedCollisions1 = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMCCollision, collision2.globalIndex()); + float cent1 = -1; + float cent2 = -1; + if (!cfgGeneral.cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions1) { + cent1 = getCentrality(collision); + } + for (const auto& collision : groupedCollisions2) { + cent2 = getCentrality(collision); + } + } + + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && ((cent1 < cfgGeneral.cfgCutCentMin || cent1 >= cfgGeneral.cfgCutCentMax) || (cent2 < cfgGeneral.cfgCutCentMin || cent2 >= cfgGeneral.cfgCutCentMax))) + continue; + + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + float eventWeight = 1.0f; + if (cfgGeneral.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + + if (cfgGeneral.cfgUseCFStepAll) { + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight, kFT0A); + } + + registry.fill(HIST("MCTrue/MCeventcount"), 4.5); + fillMCCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, eventWeight, kFT0A); + } + } + PROCESS_SWITCH(LongRangeDihadronCor, processMcMixedTpcFt0a, "Process MC mixed events", false); + + void processSemiMcSameTpcFt0c(FilteredMcParticles const& mcParticles, FilteredCollisions const& collisions, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + for (const auto& collision : collisions) { + auto tracks1 = mcParticles.sliceBy(perColMc, collision.globalIndex()); // TPC MC-truth + + if (!collision.sel8()) + return; + auto bc = collision.bc_as(); + + int tpcMult = 0; + for (const auto& track : tracks1) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + + float cent = -1.; + float weightCent = 1.0f; + if (!cfgGeneral.cfgCentTableUnavailable) { + cent = getCentrality(collision); + } + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision, tpcMult, cent, true)) + return; + if (!collision.has_foundFT0()) + return; + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + if (!cfgGeneral.cfgCentTableUnavailable) { + getCentralityWeight(weightCent, cent); + registry.fill(HIST("Centrality"), cent); + registry.fill(HIST("CentralityWeighted"), cent, weightCent); + } + registry.fill(HIST("Nch"), tpcMult); + registry.fill(HIST("zVtx"), collision.posZ()); + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + if (!cfgGeneral.cfgCentTableUnavailable) + registry.fill(HIST("MCTrue/MCCentrality"), cent); + registry.fill(HIST("MCTrue/MCNch"), tpcMult); + for (const auto& track1 : tracks1) { + if (track1.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), track1.phi()); + registry.fill(HIST("MCTrue/MCEta"), track1.eta()); + registry.fill(HIST("MCTrue/MCpT"), track1.pt()); + } + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult < cfgGeneral.cfgCutMultMin || tpcMult >= cfgGeneral.cfgCutMultMax)) { + return; + } + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent < cfgGeneral.cfgCutCentMin || cent >= cfgGeneral.cfgCutCentMax)) { + return; + } + + registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + sameTpcFt0c->fillEvent(tpcMult, CorrelationContainer::kCFStepReconstructed); + const auto& ft0 = collision.foundFT0(); + fillSemiMcCorrelations(tracks1, ft0, collision.posZ(), SameEvent, kFT0C, cent, weightCent); + } + } + PROCESS_SWITCH(LongRangeDihadronCor, processSemiMcSameTpcFt0c, "Process truth-TPC and recon-FT0C same events", false); + + void processSemiMcSameTpcFt0a(FilteredMcParticles const& mcParticles, FilteredCollisions const& collisions, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + for (const auto& collision : collisions) { + auto tracks1 = mcParticles.sliceBy(perColMc, collision.globalIndex()); // TPC MC-truth + + if (!collision.sel8()) + return; + auto bc = collision.bc_as(); + + int tpcMult = 0; + for (const auto& track : tracks1) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + + float cent = -1.; + float weightCent = 1.0f; + if (!cfgGeneral.cfgCentTableUnavailable) { + cent = getCentrality(collision); + } + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision, tpcMult, cent, true)) + return; + if (!collision.has_foundFT0()) + return; + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + if (!cfgGeneral.cfgCentTableUnavailable) { + getCentralityWeight(weightCent, cent); + registry.fill(HIST("Centrality"), cent); + registry.fill(HIST("CentralityWeighted"), cent, weightCent); + } + registry.fill(HIST("Nch"), tpcMult); + registry.fill(HIST("zVtx"), collision.posZ()); + + registry.fill(HIST("MCTrue/MCeventcount"), SameEvent); // because its same event i put it in the 1 bin + if (!cfgGeneral.cfgCentTableUnavailable) + registry.fill(HIST("MCTrue/MCCentrality"), cent); + registry.fill(HIST("MCTrue/MCNch"), tpcMult); + for (const auto& track1 : tracks1) { + if (track1.isPhysicalPrimary()) { + registry.fill(HIST("MCTrue/MCPhi"), track1.phi()); + registry.fill(HIST("MCTrue/MCEta"), track1.eta()); + registry.fill(HIST("MCTrue/MCpT"), track1.pt()); + } + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult < cfgGeneral.cfgCutMultMin || tpcMult >= cfgGeneral.cfgCutMultMax)) { + return; + } + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent < cfgGeneral.cfgCutCentMin || cent >= cfgGeneral.cfgCutCentMax)) { + return; + } + + registry.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + sameTpcFt0a->fillEvent(tpcMult, CorrelationContainer::kCFStepReconstructed); + const auto& ft0 = collision.foundFT0(); + fillSemiMcCorrelations(tracks1, ft0, collision.posZ(), SameEvent, kFT0A, cent, weightCent); + } + } + PROCESS_SWITCH(LongRangeDihadronCor, processSemiMcSameTpcFt0a, "Process truth-TPC and recon-FT0A same events", false); + + void processSemiMcMixedTpcFt0c(FilteredMcParticles const& mcParticles, FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&mcParticles, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, collision.globalIndex(), this->cache); + int tpcMult = 0; + for (const auto& track : associatedTracks) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + return tpcMult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracksRecon1, collision2, tracksRecon2] = *it; + + auto tracksTruth1 = mcParticles.sliceBy(perColMc, collision1.globalIndex()); // TPC MC-truth + + if (!collision1.sel8() || !collision2.sel8()) + continue; + + float cent1 = -1; + float cent2 = -1; + if (!cfgGeneral.cfgCentTableUnavailable) { + cent1 = getCentrality(collision1); + cent2 = getCentrality(collision2); + } + + int tpcMult = 0; + for (const auto& track : tracksTruth1) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult < cfgGeneral.cfgCutMultMin || tpcMult >= cfgGeneral.cfgCutMultMax)) + continue; + + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision1, tracksRecon1.size(), cent1, false)) + continue; + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision2, tracksRecon2.size(), cent2, false)) + continue; + + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent1 < cfgGeneral.cfgCutCentMin || cent1 >= cfgGeneral.cfgCutCentMax)) + continue; + + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent2 < cfgGeneral.cfgCutCentMin || cent2 >= cfgGeneral.cfgCutCentMax)) + continue; + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + continue; + + auto bc = collision1.bc_as(); + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + if (cfgGeneral.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + registry.fill(HIST("MCTrue/MCeventcount"), 4.5); + float weightCent = 1.0f; + if (!cfgGeneral.cfgCentTableUnavailable) + getCentralityWeight(weightCent, cent1); + const auto& ft0 = collision2.foundFT0(); + + fillSemiMcCorrelations(tracksTruth1, ft0, collision1.posZ(), MixedEvent, kFT0C, cent1, eventWeight * weightCent); + } + } + PROCESS_SWITCH(LongRangeDihadronCor, processSemiMcMixedTpcFt0c, "Process MC mixed events for TPC(truth)-FT0C(recon)", false); + + void processSemiMcMixedTpcFt0a(FilteredMcParticles const& mcParticles, FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&mcParticles, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, collision.globalIndex(), this->cache); + int tpcMult = 0; + for (const auto& track : associatedTracks) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + return tpcMult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracksRecon1, collision2, tracksRecon2] = *it; + + auto tracksTruth1 = mcParticles.sliceBy(perColMc, collision1.globalIndex()); // TPC MC-truth + + if (!collision1.sel8() || !collision2.sel8()) + continue; + + float cent1 = -1; + float cent2 = -1; + if (!cfgGeneral.cfgCentTableUnavailable) { + cent1 = getCentrality(collision1); + cent2 = getCentrality(collision2); + } + + int tpcMult = 0; + for (const auto& track : tracksTruth1) { + if (std::abs(track.eta()) < cfgGeneral.cfgEtaTpcCut) + tpcMult += 1; + } + + if (cfgGeneral.cfgSelCollByNch && (tpcMult < cfgGeneral.cfgCutMultMin || tpcMult >= cfgGeneral.cfgCutMultMax)) + continue; + + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision1, tracksRecon1.size(), cent1, false)) + continue; + if (cfgGeneral.cfgUseAdditionalEventCut && !eventSelected(collision2, tracksRecon2.size(), cent2, false)) + continue; + + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent1 < cfgGeneral.cfgCutCentMin || cent1 >= cfgGeneral.cfgCutCentMax)) + continue; + + if (!cfgGeneral.cfgSelCollByNch && !cfgGeneral.cfgCentTableUnavailable && (cent2 < cfgGeneral.cfgCutCentMin || cent2 >= cfgGeneral.cfgCutCentMax)) + continue; + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) + continue; + + auto bc = collision1.bc_as(); + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + float eventWeight = 1.0f; + if (cfgGeneral.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + registry.fill(HIST("MCTrue/MCeventcount"), MixedEvent); // fill the mixed event in the 3 bin + registry.fill(HIST("MCTrue/MCeventcount"), 4.5); + float weightCent = 1.0f; + if (!cfgGeneral.cfgCentTableUnavailable) + getCentralityWeight(weightCent, cent1); + const auto& ft0 = collision2.foundFT0(); + + fillSemiMcCorrelations(tracksTruth1, ft0, collision1.posZ(), MixedEvent, kFT0A, cent1, eventWeight * weightCent); + } + } + PROCESS_SWITCH(LongRangeDihadronCor, processSemiMcMixedTpcFt0a, "Process MC mixed events for TPC(truth)-FT0A(recon)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx index c646439fd28..40c09df3d8e 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangeCorrelation.cxx @@ -470,7 +470,7 @@ struct LongrangeCorrelation { } template - int getTrackPID(TTrack track) + int getTrackPID(const TTrack& track) { // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; @@ -867,7 +867,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -902,7 +902,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -937,7 +937,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; auto tracksTuple = std::make_tuple(tracks, mfttracks); Pair pairs{binningOnVtxAndMult, mixingParameter, -1, col, tracksTuple, &cache}; for (auto const& [col1, tracks1, col2, tracks2] : pairs) { @@ -967,7 +967,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; @@ -1002,7 +1002,7 @@ struct LongrangeCorrelation { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxZME, axisMultME}}; for (auto const& [col1, col2] : soa::selfCombinations(binningOnVtxAndMult, mixingParameter, -1, col, col)) { if (!isEventSelected(col1) || !isEventSelected(col2)) { continue; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx index b1d57cf4ed6..9745762a695 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/longrangecorrDerived.cxx @@ -19,8 +19,17 @@ #include "PWGCF/TwoParticleCorrelations/DataModel/LongRangeDerived.h" #include "PWGUD/Core/SGSelector.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" - +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include #include #include #include @@ -31,12 +40,17 @@ #include #include #include +#include #include #include +#include +#include +#include + +#include #include #include -#include #include #include #include @@ -50,17 +64,110 @@ using namespace o2::aod::fwdtrack; using namespace o2::aod::evsel; using namespace o2::constants::math; +auto static constexpr KminCharge = 3.0f; +auto static constexpr KPidMaskPion = 2; +auto static constexpr KPidMaskKaon = 4; +auto static constexpr KPidMaskProton = 8; + struct LongrangecorrDerived { SliceCache cache; SGSelector sgSelector; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Configurable cfgNmixedevent{"cfgNmixedevent", 5, "how many events are mixed"}; - Configurable cfgPidMask{"cfgPidMask", 0, "Selection bitmask for the TPC particle"}; - Configurable cfgV0Mask{"cfgV0Mask", 0, "Selection bitmask for the V0 particle"}; - Configurable cfgVtxCut{"cfgVtxCut", 10.0f, "Vertex Z range to consider"}; - Configurable isUseCentEst{"isUseCentEst", false, "Centrality based classification"}; - Configurable isUseDataLikeMult{"isUseDataLikeMult", 0, "Data like mult/cent classification"}; + TrackSelection myTrackFilter; + Service pdg{}; + Service ccdb{}; + + struct : ConfigurableGroup { + Configurable cfgNmixedevent{"cfgNmixedevent", 5, "how many events are mixed"}; + Configurable cfgSampleSize{"cfgSampleSize", 10, "Sample size for bootstrapping"}; + Configurable cfgPidMask{"cfgPidMask", 0, "Selection bitmask for the TPC particle"}; + Configurable cfgV0Mask{"cfgV0Mask", 0, "Selection bitmask for the V0 particle"}; + Configurable cfgVtxCut{"cfgVtxCut", 10.0f, "Vertex Z range to consider"}; + Configurable isUseCentEst{"isUseCentEst", false, "Centrality based classification"}; + Configurable isUseDataLikeMult{"isUseDataLikeMult", 0, "Data like mult/cent classification"}; + Configurable cfgVerbosity{"cfgVerbosity", 0, "print statement"}; + + Configurable cfgEtaCut{"cfgEtaCut", 0.8f, "Eta range to consider"}; + Configurable cfgPtCutMin{"cfgPtCutMin", 0.2f, "minimum accepted track pT"}; + Configurable cfgPtCutMax{"cfgPtCutMax", 10.0f, "maximum accepted track pT"}; + Configurable cfgTpcMinNclsFound{"cfgTpcMinNclsFound", 50.0f, ""}; + Configurable cfgTpcMinNCrossedRows{"cfgTpcMinNCrossedRows", 70.0f, ""}; + Configurable cfgTpcMaxChi2PerCluster{"cfgTpcMaxChi2PerCluster", 4.0f, ""}; + Configurable cfgTpcMaxDcaZ{"cfgTpcMaxDcaZ", 1.0f, ""}; + Configurable applyEffCorr{"applyEffCorr", true, "Enable efficiency correction"}; + Configurable applyAccCorr{"applyAccCorr", false, "Enable NUA correction"}; + Configurable cfgEffccdbPath{"cfgEffccdbPath", "Users/a/abmodak/Efficiency/OO/default", "Browse track eff object from CCDB"}; + Configurable cfgAccccdbPath{"cfgAccccdbPath", "Users/a/abmodak/Acceptance/OO/default", "Browse track eff object from CCDB"}; + + Configurable cfgMftCluster{"cfgMftCluster", 5, "cut on MFT Cluster"}; + Configurable cfgMftDcaxy{"cfgMftDcaxy", 2.0f, "cut on DCA xy for MFT tracks"}; + Configurable cfgMftDcaz{"cfgMftDcaz", 2.0f, "cut on DCA z for MFT tracks"}; + Configurable cfgRejectAmbTrk{"cfgRejectAmbTrk", false, "Condition to reject Ambiguous tracks"}; + Configurable cfgRejectNonAmbTrk{"cfgRejectNonAmbTrk", false, "Condition to reject Non-Ambiguous tracks"}; + Configurable cfgRequireCA{"cfgRequireCA", false, "Use Cellular Automaton track-finding algorithm"}; + Configurable cfgRequireLTF{"cfgRequireLTF", false, "Use LTF track-finding algorithm"}; + Configurable cfgRequireFt0aOuterRing{"cfgRequireFt0aOuterRing", false, "Consider FT0A Outer Ring"}; + Configurable cfgRequireFt0aInnerRing{"cfgRequireFt0aInnerRing", false, "Consider FT0A Inner Ring"}; + Configurable cfgRequireFt0cOuterRing{"cfgRequireFt0cOuterRing", false, "Consider FT0C Outer Ring"}; + Configurable cfgRequireFt0cInnerRing{"cfgRequireFt0cInnerRing", false, "Consider FT0C Inner Ring"}; + + Configurable cfgTofPidPtCut{"cfgTofPidPtCut", 0.4f, "Minimum pt to use TOF N-sigma"}; + Configurable cfgPidNsigmaMax{"cfgPidNsigmaMax", 1.5f, "Maximum n-sigma for PID"}; + Configurable cfgPidNsigmaMin{"cfgPidNsigmaMin", -1.5f, "Minimum n-sigma for PID"}; + Configurable cfgCleanNsigmaTpcMax{"cfgCleanNsigmaTpcMax", 1.5f, "Max TPC n-sigma for clean templates"}; + Configurable cfgCleanNsigmaTpcMin{"cfgCleanNsigmaTpcMin", -1.5f, "Min TPC n-sigma for clean templates"}; + Configurable cfgCleanNsigmaTofMax{"cfgCleanNsigmaTofMax", 1.5f, "Max TOF n-sigma for clean templates"}; + Configurable cfgCleanNsigmaTofMin{"cfgCleanNsigmaTofMin", -1.5f, "Min TOF n-sigma for clean templates"}; + Configurable cfgGetNsigmaQA{"cfgGetNsigmaQA", true, "Get QA histograms for PID selection"}; + Configurable cfgGetdEdx{"cfgGetdEdx", true, "Get dEdx histograms for TPC signal"}; + + } cfgSel; + + struct : ConfigurableGroup { + ConfigurableAxis axisMultQA{"axisMultQA", {500, -0.5, 499.5}, "multiplicity QA axis"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 26, 46, 133, 1000}, "multiplicity axis"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Centrality (%)"}; + + ConfigurableAxis axisPhi{"axisPhi", {96, 0, TwoPI}, "#phi axis"}; + ConfigurableAxis axisEtaTrig{"axisEtaTrig", {40, -1., 1.}, "#eta trig axis"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {50, 0.0, 8.0}, "pt axis for PID QA histograms"}; + + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20, 20}, "vertex axis"}; + ConfigurableAxis axisEtaAssoc{"axisEtaAssoc", {96, 3.5, 4.9}, "#eta assoc axis"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {40, -6, -2}, "delta eta axis for histograms"}; + ConfigurableAxis axisInvMass{"axisInvMass", {VARIABLE_WIDTH, 1.7, 1.75, 1.8, 1.85, 1.9, 1.95, 2.0}, "invariant mass axis"}; + ConfigurableAxis axisInvMassQA{"axisInvMassQA", {20, 0.45, 0.55}, "invariant mass axis for QA"}; + ConfigurableAxis axisAmplitude{"axisAmplitude", {5000, 0, 10000}, "FT0 amplitude"}; + ConfigurableAxis axisChannel{"axisChannel", {208, 0, 208}, "FT0 channel"}; + ConfigurableAxis axisMultME{"axisMultME", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 1000}, "Mixing bins - multiplicity"}; + ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, "Mixing bins - z-vertex"}; + ConfigurableAxis axisSample{"axisSample", {10, 0, 10}, "sample axis for histograms"}; + + ConfigurableAxis axisTPCNClsFound{"axisTPCNClsFound", {200, -0.5, 199.5}, "TPC Cluster axis"}; + ConfigurableAxis axisTPCNClsCrossedRows{"axisTPCNClsCrossedRows", {200, -0.5, 199.5}, "TPC NCrossedRow axis"}; + ConfigurableAxis axisTPCChi2NCl{"axisTPCChi2NCl", {20, 0.0, 20.0}, "TPC Chi2/NCl axis"}; + ConfigurableAxis axisTPCdcaZ{"axisTPCdcaZ", {200, -10.0, 10.0}, "TPC dcaZ axis"}; + + ConfigurableAxis axisMFTAmbDegree{"axisMFTAmbDegree", {50, -0.5, 49.5}, "Track Ambiguity axis"}; + ConfigurableAxis axisMFTNClusters{"axisMFTNClusters", {200, -0.5, 199.5}, "MFT Cluster axis"}; + ConfigurableAxis axisMFTbestDCAXY{"axisMFTbestDCAXY", {200, -10.0, 10.0}, "MFT dcaXY axis"}; + ConfigurableAxis axisMFTbestDCAZ{"axisMFTbestDCAZ", {200, -10.0, 10.0}, "MFT dcaZ axis"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {1, 0.5, 8.0}, "pt axis for efficiency histograms"}; + + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + + ConfigurableAxis axisMcPt{"axisMcPt", {100, 0.2, 10.0}, "p_{T} (GeV/c)"}; + ConfigurableAxis axisMcEta{"axisMcEta", {100, -0.8, 0.8}, "#eta"}; + ConfigurableAxis axisMcPhi{"axisMcPhi", {100, 0., TwoPI}, "#phi"}; + } cfgAxis; Configurable cfgFv0Cut{"cfgFv0Cut", 50.0f, "FV0A threshold"}; Configurable cfgFt0aCut{"cfgFt0aCut", 100.0f, "FT0A threshold"}; @@ -68,35 +175,19 @@ struct LongrangecorrDerived { Configurable cfgZdcCut{"cfgZdcCut", 0.1f, "ZDC threshold"}; Configurable cfgGapSideCut{"cfgGapSideCut", 0, "Gap-side A=0, C=1, AC = 2, No Gap = -1, All events = 3"}; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 10, 15, 25, 50, 60, 1000}, "multiplicity axis"}; - ConfigurableAxis axisPhi{"axisPhi", {96, 0, TwoPI}, "#phi axis"}; - ConfigurableAxis axisEtaTrig{"axisEtaTrig", {40, -1., 1.}, "#eta trig axis"}; - ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; - ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt assoc axis for histograms"}; - ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20, 20}, "vertex axis"}; - ConfigurableAxis axisEtaAssoc{"axisEtaAssoc", {96, 3.5, 4.9}, "#eta assoc axis"}; - ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; - ConfigurableAxis axisDeltaEta{"axisDeltaEta", {40, -6, -2}, "delta eta axis for histograms"}; - ConfigurableAxis axisInvMass{"axisInvMass", {VARIABLE_WIDTH, 1.7, 1.75, 1.8, 1.85, 1.9, 1.95, 2.0}, "invariant mass axis"}; - ConfigurableAxis axisInvMassQA{"axisInvMassQA", {20, 0.45, 0.55}, "invariant mass axis for QA"}; - ConfigurableAxis axisAmplitude{"axisAmplitude", {5000, 0, 10000}, "FT0 amplitude"}; - ConfigurableAxis axisChannel{"axisChannel", {208, 0, 208}, "FT0 channel"}; - ConfigurableAxis axisMultME{"axisMultME", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 1000}, "Mixing bins - multiplicity"}; - ConfigurableAxis axisVtxZME{"axisVtxZME", {VARIABLE_WIDTH, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, "Mixing bins - z-vertex"}; - - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0}, "pt axis for efficiency histograms"}; - OutputObj same{"sameEvent"}; OutputObj mixed{"mixedEvent"}; + // corrections + TH3D* hTrkEff = nullptr; + TH3D* hTrkAcc = nullptr; + bool fLoadTrkEffCorr = false; + using CollsTable = aod::LRCollisions; using TrksTable = aod::LRMidTracks; using MftTrksTable = aod::LRMftTracks; using Ft0aTrksTable = aod::LRFt0aTracks; using Ft0cTrksTable = aod::LRFt0cTracks; - using MftbestTrksTable = aod::LRMftBestTracks; using V0TrksTable = aod::LRV0Tracks; using McCollsTable = aod::LRMcCollisions; @@ -110,19 +201,16 @@ struct LongrangecorrDerived { using MftTrksUpcTable = aod::UpcLRMftTracks; using Ft0aTrksUpcTable = aod::UpcLRFt0aTracks; using Ft0cTrksUpcTable = aod::UpcLRFt0cTracks; - using MftbestTrksUpcTable = aod::UpcLRMftBestTracks; using V0TrksUpcTable = aod::UpcLRV0Tracks; Preslice perColTpc = aod::lrcorrtrktable::lrCollisionId; Preslice perColMft = aod::lrcorrtrktable::lrCollisionId; - Preslice perColMftbest = aod::lrcorrtrktable::lrCollisionId; Preslice perColFt0a = aod::lrcorrtrktable::lrCollisionId; Preslice perColFt0c = aod::lrcorrtrktable::lrCollisionId; Preslice perColV0 = aod::lrcorrtrktable::lrCollisionId; Preslice perUpcColTpc = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColMft = aod::lrcorrtrktable::upcLRCollisionId; - Preslice perUpcColMftbest = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColFt0a = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColFt0c = aod::lrcorrtrktable::upcLRCollisionId; Preslice perUpcColV0 = aod::lrcorrtrktable::upcLRCollisionId; @@ -134,94 +222,275 @@ struct LongrangecorrDerived { void init(InitContext const&) { - std::vector corrAxis = {{axisVtxZ, "z-vtx (cm)"}, - {axisMultiplicity, "multiplicity"}, - {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, - {axisDeltaPhi, "#Delta#varphi (rad)"}, - {axisDeltaEta, "#Delta#eta"}}; - std::vector effAxis = {{axisVertexEfficiency, "z-vtx (cm)"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisEtaEfficiency, "#eta"}}; - std::vector userAxis = {{axisInvMass, "m (GeV/c^2)"}}; - - same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); - - histos.add("hMultiplicity", "hMultiplicity", kTH1D, {axisMultiplicity}); - histos.add("hCentrality", "hCentrality", kTH1D, {axisMultiplicity}); - histos.add("hVertexZ", "hVertexZ", kTH1D, {axisVtxZ}); - - histos.add("hGapSide", "hGapSide", kTH1I, {{5, -0.5, 4.5}}); - histos.add("hTrueGapSide", "hTrueGapSide", kTH1I, {{6, -1.5, 4.5}}); - histos.add("hTrueGapSide_AfterSel", "hTrueGapSide_AfterSel", kTH1I, {{6, -1.5, 4.5}}); - - histos.add("Trig_eta", "Trig_eta", kTH1D, {axisEtaTrig}); - histos.add("Trig_phi", "Trig_phi", kTH1D, {axisPhi}); - histos.add("Trig_etavsphi", "Trig_etavsphi", kTH2D, {axisPhi, axisEtaTrig}); - histos.add("Trig_pt", "Trig_pt", kTH1D, {axisPtTrigger}); - histos.add("Trig_invMass", "Trig_invMass", kTH1D, {axisInvMassQA}); - histos.add("Trig_hist", "Trig_hist", kTHnSparseF, {axisVtxZ, axisMultiplicity, axisPtTrigger, axisInvMass}); - histos.add("Trig_amp", "Trig_amp", kTH1D, {axisAmplitude}); - histos.add("Trig_amp_gaincorrected", "Trig_amp_gaincorrected", kTH1D, {axisAmplitude}); - histos.add("Channel_vs_Trig_amp", "Channel_vs_Trig_amp", kTH2D, {axisChannel, axisAmplitude}); - histos.add("Channel_vs_Trig_amp_gaincorrected", "Channel_vs_Trig_amp_gaincorrected", kTH2D, {axisChannel, axisAmplitude}); - - histos.add("Assoc_eta", "Assoc_eta", kTH1D, {axisEtaAssoc}); - histos.add("Assoc_phi", "Assoc_phi", kTH1D, {axisPhi}); - histos.add("Assoc_etavsphi", "Assoc_etavsphi", kTH2D, {axisPhi, axisEtaAssoc}); - histos.add("Assoc_pt", "Assoc_pt", kTH1D, {axisPtAssoc}); - histos.add("Assoc_amp", "Assoc_amp", kTH1D, {axisAmplitude}); - histos.add("Assoc_amp_gaincorrected", "Assoc_amp_gaincorrected", kTH1D, {axisAmplitude}); - histos.add("Channel_vs_Assoc_amp", "Channel_vs_Assoc_amp", kTH2D, {axisChannel, axisAmplitude}); - histos.add("Channel_vs_Assoc_amp_gaincorrected", "Channel_vs_Assoc_amp_gaincorrected", kTH2D, {axisChannel, axisAmplitude}); - - histos.add("deltaEta_deltaPhi_same", "", kTH2D, {axisDeltaPhi, axisDeltaEta}); - histos.add("deltaEta_deltaPhi_mixed", "", kTH2D, {axisDeltaPhi, axisDeltaEta}); + std::vector corrAxis = {{cfgAxis.axisSample, "Sample"}, + {cfgAxis.axisVtxZ, "z-vtx (cm)"}, + {cfgAxis.axisMultiplicity, "multiplicity"}, + {cfgAxis.axisPtTrigger, "p_{T} (GeV/c)"}, + {cfgAxis.axisDeltaPhi, "#Delta#varphi (rad)"}, + {cfgAxis.axisDeltaEta, "#Delta#eta"}}; + std::vector effAxis = {{cfgAxis.axisVertexEfficiency, "z-vtx (cm)"}, + {cfgAxis.axisPtEfficiency, "p_{T} (GeV/c)"}, + {cfgAxis.axisEtaEfficiency, "#eta"}}; + std::vector userAxis = {{cfgAxis.axisInvMass, "m (GeV/c^2)"}}; + + if (!(doprocessTPCtrackEff)) { + + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + + histos.add("hMultiplicity", "hMultiplicity", kTH1D, {cfgAxis.axisMultQA}); + histos.add("hCentrality", "hCentrality", kTH1D, {cfgAxis.axisMultQA}); + histos.add("hVertexZ", "hVertexZ", kTH1D, {cfgAxis.axisVtxZ}); + + histos.add("hGapSide", "hGapSide", kTH1I, {{5, -0.5, 4.5}}); + histos.add("hTrueGapSide", "hTrueGapSide", kTH1I, {{6, -1.5, 4.5}}); + histos.add("hTrueGapSide_AfterSel", "hTrueGapSide_AfterSel", kTH1I, {{6, -1.5, 4.5}}); + + histos.add("Trig_eta", "Trig_eta", kTH1D, {cfgAxis.axisEtaTrig}); + histos.add("Trig_eta_corrected", "Trig_eta_corrected", kTH1D, {cfgAxis.axisEtaTrig}); + histos.add("Trig_phi", "Trig_phi", kTH1D, {cfgAxis.axisPhi}); + histos.add("Trig_phi_corrected", "Trig_phi_corrected", kTH1D, {cfgAxis.axisPhi}); + histos.add("Trig_etavsphi", "Trig_etavsphi", kTH2D, {cfgAxis.axisPhi, cfgAxis.axisEtaTrig}); + histos.add("Trig_pt", "Trig_pt", kTH1D, {cfgAxis.axisPtTrigger}); + histos.add("Trig_pt_corrected", "Trig_pt_corrected", kTH1D, {cfgAxis.axisPtTrigger}); + histos.add("Trig_invMass", "Trig_invMass", kTH1D, {cfgAxis.axisInvMassQA}); + histos.add("Trig_hist", "Trigger Track Properties;Sample;Vertex Z (cm);Multiplicity;p_{T} (GeV/c);Invariant Mass (GeV/c^{2})", kTHnSparseF, {cfgAxis.axisSample, cfgAxis.axisVtxZ, cfgAxis.axisMultiplicity, cfgAxis.axisPtTrigger, cfgAxis.axisInvMass}); + histos.add("Trig_amp", "Trig_amp", kTH1D, {cfgAxis.axisAmplitude}); + histos.add("Channel_vs_Trig_amp", "Channel_vs_Trig_amp", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); + + histos.add("Assoc_eta", "Assoc_eta", kTH1D, {cfgAxis.axisEtaAssoc}); + histos.add("Assoc_phi", "Assoc_phi", kTH1D, {cfgAxis.axisPhi}); + histos.add("Assoc_etavsphi", "Assoc_etavsphi", kTH2D, {cfgAxis.axisPhi, cfgAxis.axisEtaAssoc}); + histos.add("Assoc_amp", "Assoc_amp", kTH1D, {cfgAxis.axisAmplitude}); + histos.add("Channel_vs_Assoc_amp", "Channel_vs_Assoc_amp", kTH2D, {cfgAxis.axisChannel, cfgAxis.axisAmplitude}); + + histos.add("deltaEta_deltaPhi_same", "deltaEta_deltaPhi_same", kTH2D, {cfgAxis.axisDeltaPhi, cfgAxis.axisDeltaEta}); + histos.add("deltaEta_deltaPhi_mixed", "deltaEta_deltaPhi_mixed", kTH2D, {cfgAxis.axisDeltaPhi, cfgAxis.axisDeltaEta}); + + histos.add("TPCNClsFound", "TPCNClsFound", kTH1D, {cfgAxis.axisTPCNClsFound}); + histos.add("TPCNClsCrossedRows", "TPCNClsCrossedRows", kTH1D, {cfgAxis.axisTPCNClsCrossedRows}); + histos.add("TPCChi2NCl", "TPCChi2NCl", kTH1D, {cfgAxis.axisTPCChi2NCl}); + histos.add("TPCdcaZ", "TPCdcaZ", kTH1D, {cfgAxis.axisTPCdcaZ}); + + histos.add("MFTNClusters", "MFTNClusters", kTH1D, {cfgAxis.axisMFTNClusters}); + histos.add("MFTbestDCAXY", "MFTbestDCAXY", kTH1D, {cfgAxis.axisMFTbestDCAXY}); + histos.add("MFTbestDCAZ", "MFTbestDCAZ", kTH1D, {cfgAxis.axisMFTbestDCAZ}); + + histos.add("ReassignedMFTtrackAmbDegree", "ReassignedMFTtrackAmbDegree", kTH1D, {cfgAxis.axisMFTAmbDegree}); + histos.add("AssignedMFTtrackAmbDegree", "AssignedMFTtrackAmbDegree", kTH1D, {cfgAxis.axisMFTAmbDegree}); + } + + if (doprocessTPCtrackEff) { + histos.add("hGenMCdndpt", "hGenMCdndpt", kTH3D, {cfgAxis.axisVtxZ, cfgAxis.axisEtaEfficiency, cfgAxis.axisPtEfficiency}); + histos.add("hRecMCdndpt", "hRecMCdndpt", kTH3D, {cfgAxis.axisVtxZ, cfgAxis.axisEtaEfficiency, cfgAxis.axisPtEfficiency}); + + if (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton) { + histos.add("hMCGen_PidPtEtaPhi", "MC Gen Target", kTH3D, {cfgAxis.axisMcPt, cfgAxis.axisMcEta, cfgAxis.axisMcPhi}); + histos.add("hMCRec_PidPtEtaPhi", "MC Rec Target", kTH3D, {cfgAxis.axisMcPt, cfgAxis.axisMcEta, cfgAxis.axisMcPhi}); + } + + if (cfgSel.cfgGetdEdx) { + histos.add("MC_TpcdEdx_ptwise", "MC True TPC dE/dx;Particle Species;p_{T} (GeV/c);TPC dE/dx", kTHnSparseD, {{3, 0.5, 3.5}, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal}); + } + } + + myTrackFilter = getGlobalTrackSelectionRun3ITSMatch(TrackSelection::GlobalTrackRun3ITSMatching::Run3ITSibAny, + TrackSelection::GlobalTrackRun3DCAxyCut::Default); + myTrackFilter.SetPtRange(cfgSel.cfgPtCutMin, cfgSel.cfgPtCutMax); + myTrackFilter.SetEtaRange(-cfgSel.cfgEtaCut, cfgSel.cfgEtaCut); + myTrackFilter.SetMinNCrossedRowsTPC(static_cast(cfgSel.cfgTpcMinNCrossedRows)); + myTrackFilter.SetMinNClustersTPC(static_cast(cfgSel.cfgTpcMinNclsFound)); + myTrackFilter.SetMaxChi2PerClusterTPC(cfgSel.cfgTpcMaxChi2PerCluster); + myTrackFilter.SetMaxDcaZ(cfgSel.cfgTpcMaxDcaZ); + myTrackFilter.print(); + + if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + + if (cfgSel.isUseCentEst) { // CENTRALITY MODE + histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Centrality (%);p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + if (cfgSel.cfgGetdEdx) { + histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Centrality (%);p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisCentrality, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + } + } else { // MULTIPLICITY MODE + histos.add("TofTpcNsigma_before", "TPC vs TOF n#sigma Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + histos.add("TofTpcNsigma_after", "TPC vs TOF n#sigma After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);n#sigma_{TPC};n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisNsigmaTPC, cfgAxis.axisNsigmaTOF}); + + if (cfgSel.cfgGetdEdx) { + histos.add("TpcdEdx_ptwise", "TPC dE/dx Before Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + + histos.add("TpcdEdx_ptwise_afterCut", "TPC dE/dx After Cuts;Multiplicity (N_{ch});p_{T} (GeV/c);TPC dE/dx;n#sigma_{TOF}", + kTHnSparseD, {cfgAxis.axisMultiplicity, cfgAxis.axisPtQA, cfgAxis.axisTpcSignal, cfgAxis.axisNsigmaTOF}); + } + } + } + } + + void loadEffCorrection(uint64_t timestamp) + { + if (fLoadTrkEffCorr) { + return; + } + if (cfgSel.cfgEffccdbPath.value.empty() == false) { + hTrkEff = ccdb->getForTimeStamp(cfgSel.cfgEffccdbPath, timestamp); + if (hTrkEff == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgSel.cfgEffccdbPath.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgEffccdbPath.value.c_str(), static_cast(hTrkEff)); + } + + if (cfgSel.cfgAccccdbPath.value.empty() == false) { + hTrkAcc = ccdb->getForTimeStamp(cfgSel.cfgAccccdbPath, timestamp); + if (hTrkAcc == nullptr) { + LOGF(fatal, "Could not load NUA histogram for trigger particles from %s", cfgSel.cfgAccccdbPath.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgSel.cfgAccccdbPath.value.c_str(), static_cast(hTrkAcc)); + } + + fLoadTrkEffCorr = true; + } + + float getTrkEffCorr(float posZ, float eta, float pt) + { + if (!cfgSel.applyEffCorr || !hTrkEff) { + return 1.0; + } + int zBin = hTrkEff->GetXaxis()->FindBin(posZ); + int etaBin = hTrkEff->GetYaxis()->FindBin(eta); + int ptBin = hTrkEff->GetZaxis()->FindBin(pt); + float effweight = 1.0 / hTrkEff->GetBinContent(zBin, etaBin, ptBin); + if (!std::isfinite(effweight) || effweight <= 0) { + return 1.0; + } + return effweight; + } + + float getTrkAccCorr(float posZ, float eta, float phi) + { + if (!cfgSel.applyAccCorr || !hTrkAcc) { + return 1.0; + } + int zBin = hTrkAcc->GetXaxis()->FindBin(posZ); + int etaBin = hTrkAcc->GetYaxis()->FindBin(eta); + int phiBin = hTrkAcc->GetZaxis()->FindBin(phi); + float nua = hTrkAcc->GetBinContent(zBin, etaBin, phiBin); + if (!std::isfinite(nua) || nua <= 0) { + return 1.0; + } + return nua; + } + + template + bool isTrackSelected(TTrack const& track) + { + if constexpr (requires { track.tpcNClsFound(); }) { + if (track.tpcNClsFound() < cfgSel.cfgTpcMinNclsFound) + return false; + if (track.tpcNClsCrossedRows() < cfgSel.cfgTpcMinNCrossedRows) + return false; + if (track.tpcChi2NCl() > cfgSel.cfgTpcMaxChi2PerCluster) + return false; + if (std::abs(track.dcaZ()) > cfgSel.cfgTpcMaxDcaZ) + return false; + return true; + } else if constexpr (requires { track.nClusters(); }) { + if (track.nClusters() < cfgSel.cfgMftCluster) + return false; + if (std::abs(track.bestDCAXY()) >= cfgSel.cfgMftDcaxy) + return false; + if (std::abs(track.bestDCAZ()) >= cfgSel.cfgMftDcaz) + return false; + if (cfgSel.cfgRejectAmbTrk && track.ambDegree() > 1) + return false; + if (cfgSel.cfgRejectNonAmbTrk && track.ambDegree() == 1) + return false; + if (cfgSel.cfgRequireCA && !track.isCA()) + return false; + if (cfgSel.cfgRequireLTF && track.isCA()) + return false; + return true; + } else { + return true; + } } template void fillCollQA(TCollision const& col) { histos.fill(HIST("hMultiplicity"), col.multiplicity()); - if constexpr (std::experimental::is_detected::value) { + if constexpr (requires { col.centrality(); }) { histos.fill(HIST("hCentrality"), col.centrality()); } histos.fill(HIST("hVertexZ"), col.posZ()); } template - void fillTrigTrackQA(TTrack const& track) + void fillTrigTrackQA(TTrack const& track, float trigAmpl, float trkeff, float trkAcc) { histos.fill(HIST("Trig_etavsphi"), track.phi(), track.eta()); histos.fill(HIST("Trig_eta"), track.eta()); histos.fill(HIST("Trig_phi"), track.phi()); - if constexpr (std::experimental::is_detected::value) { - histos.fill(HIST("Trig_amp"), track.amplitude()); - histos.fill(HIST("Channel_vs_Trig_amp"), track.channelID(), track.amplitude()); - histos.fill(HIST("Trig_amp_gaincorrected"), track.gainAmplitude()); - histos.fill(HIST("Channel_vs_Trig_amp_gaincorrected"), track.channelID(), track.gainAmplitude()); + histos.fill(HIST("Trig_eta_corrected"), track.eta(), trkeff); + histos.fill(HIST("Trig_phi_corrected"), track.phi(), trkAcc); + if constexpr (requires { track.channelID(); }) { + histos.fill(HIST("Trig_amp"), trigAmpl); + histos.fill(HIST("Channel_vs_Trig_amp"), track.channelID(), trigAmpl); } else { histos.fill(HIST("Trig_pt"), track.pt()); + histos.fill(HIST("Trig_pt_corrected"), track.pt(), trkeff); } - if constexpr (std::experimental::is_detected::value) { + if constexpr (requires { track.invMass(); }) { histos.fill(HIST("Trig_invMass"), track.invMass()); } + if constexpr (requires { track.tpcNClsFound(); }) { + histos.fill(HIST("TPCNClsFound"), track.tpcNClsFound()); + histos.fill(HIST("TPCNClsCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("TPCChi2NCl"), track.tpcChi2NCl()); + histos.fill(HIST("TPCdcaZ"), track.dcaZ()); + } + if constexpr (requires { track.nClusters(); }) { + histos.fill(HIST("MFTNClusters"), track.nClusters()); + histos.fill(HIST("MFTbestDCAXY"), track.bestDCAXY()); + histos.fill(HIST("MFTbestDCAZ"), track.bestDCAZ()); + if (track.isReassigned()) { + histos.fill(HIST("ReassignedMFTtrackAmbDegree"), track.ambDegree()); + } else { + histos.fill(HIST("AssignedMFTtrackAmbDegree"), track.ambDegree()); + } + } } template - void fillAssocTrackQA(TTrack const& track) + void fillAssocTrackQA(TTrack const& track, float assoAmpl) { histos.fill(HIST("Assoc_etavsphi"), track.phi(), track.eta()); histos.fill(HIST("Assoc_eta"), track.eta()); histos.fill(HIST("Assoc_phi"), track.phi()); - if constexpr (std::experimental::is_detected::value) { - histos.fill(HIST("Assoc_amp"), track.amplitude()); - histos.fill(HIST("Channel_vs_Assoc_amp"), track.channelID(), track.amplitude()); - histos.fill(HIST("Assoc_amp_gaincorrected"), track.gainAmplitude()); - histos.fill(HIST("Channel_vs_Assoc_amp_gaincorrected"), track.channelID(), track.gainAmplitude()); - } else { - histos.fill(HIST("Assoc_pt"), track.pt()); + if constexpr (requires { track.channelID(); }) { + histos.fill(HIST("Assoc_amp"), assoAmpl); + histos.fill(HIST("Channel_vs_Assoc_amp"), track.channelID(), assoAmpl); + } + if constexpr (requires { track.nClusters(); }) { + histos.fill(HIST("MFTNClusters"), track.nClusters()); + histos.fill(HIST("MFTbestDCAXY"), track.bestDCAXY()); + histos.fill(HIST("MFTbestDCAZ"), track.bestDCAZ()); + if (track.isReassigned()) { + histos.fill(HIST("ReassignedMFTtrackAmbDegree"), track.ambDegree()); + } else { + histos.fill(HIST("AssignedMFTtrackAmbDegree"), track.ambDegree()); + } } } @@ -243,67 +512,90 @@ struct LongrangecorrDerived { return true; } - template - using HasTpcTrack = decltype(std::declval().trackType()); - template - using HasV0Track = decltype(std::declval().v0Type()); - template - using HasInvMass = decltype(std::declval().invMass()); - template - using HasUpc = decltype(std::declval().gapSide()); - template - using HasFt0 = decltype(std::declval().channelID()); - template - using HasCent = decltype(std::declval().centrality()); - template void fillCorrHist(TTarget target, TTriggers const& triggers, TAssocs const& assocs, bool mixing, float vz, float multiplicity, float eventWeight) { + int fSampleIndex = static_cast(gRandom->Uniform(0, cfgSel.cfgSampleSize)); for (auto const& triggerTrack : triggers) { auto trigAmpl = 1.0f; - if constexpr (std::experimental::is_detected::value) { - trigAmpl = triggerTrack.gainAmplitude(); - } else { - trigAmpl = 1.0; + auto trkeff = 1.0f; + auto trkAcc = 1.0f; + + if (!isTrackSelected(triggerTrack)) + continue; + + if constexpr (requires { triggerTrack.trackType(); }) { + if (cfgSel.cfgPidMask != 0 && (cfgSel.cfgPidMask & (1u << static_cast(triggerTrack.trackType()))) == 0u) + continue; + } else if constexpr (requires { triggerTrack.v0Type(); }) { + if (cfgSel.cfgV0Mask != 0 && (cfgSel.cfgV0Mask & (1u << static_cast(triggerTrack.v0Type()))) == 0u) + continue; } - if constexpr (std::experimental::is_detected::value) { - if (cfgPidMask != 0 && (cfgPidMask & (1u << static_cast(triggerTrack.trackType()))) == 0u) + + if constexpr (requires { triggerTrack.channelID(); }) { + if (cfgSel.cfgRequireFt0aOuterRing && !triggerTrack.isTrackFT0Outer()) continue; - } else if constexpr (std::experimental::is_detected::value) { - if (cfgV0Mask != 0 && (cfgV0Mask & (1u << static_cast(triggerTrack.v0Type()))) == 0u) + if (cfgSel.cfgRequireFt0aInnerRing && triggerTrack.isTrackFT0Outer()) continue; + trigAmpl = triggerTrack.amplitude(); + } else { + trigAmpl = 1.0; } + + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if constexpr (requires { triggerTrack.trackType(); }) { + trkeff = getTrkEffCorr(vz, triggerTrack.eta(), triggerTrack.pt()); + trkAcc = getTrkAccCorr(vz, triggerTrack.eta(), triggerTrack.phi()); + } else { + trkeff = 1.0; + trkAcc = 1.0; + } + } + + if (cfgSel.cfgVerbosity > 0) { + LOGF(info, "NUE correction factor: %f | NUA correction factor: %f", trkeff, trkAcc); + } + if (!mixing) { - fillTrigTrackQA(triggerTrack); - if constexpr (std::experimental::is_detected::value) { - histos.fill(HIST("Trig_hist"), vz, multiplicity, 1.0, 1.0, eventWeight * trigAmpl); - } else if constexpr (std::experimental::is_detected::value) { - histos.fill(HIST("Trig_hist"), vz, multiplicity, triggerTrack.pt(), triggerTrack.invMass(), eventWeight * trigAmpl); + fillTrigTrackQA(triggerTrack, trigAmpl, trkeff, trkAcc); + if constexpr (requires { triggerTrack.channelID(); }) { + histos.fill(HIST("Trig_hist"), fSampleIndex, vz, multiplicity, 1.0, 1.0, eventWeight * trigAmpl * trkeff * trkAcc); + } else if constexpr (requires { triggerTrack.invMass(); }) { + histos.fill(HIST("Trig_hist"), fSampleIndex, vz, multiplicity, triggerTrack.pt(), triggerTrack.invMass(), eventWeight * trigAmpl * trkeff * trkAcc); } else { - histos.fill(HIST("Trig_hist"), vz, multiplicity, triggerTrack.pt(), 1.0, eventWeight * trigAmpl); + histos.fill(HIST("Trig_hist"), fSampleIndex, vz, multiplicity, triggerTrack.pt(), 1.0, eventWeight * trigAmpl * trkeff * trkAcc); } } for (auto const& assoTrack : assocs) { auto assoAmpl = 1.0f; - if constexpr (std::experimental::is_detected::value) { - assoAmpl = assoTrack.gainAmplitude(); + + if (!isTrackSelected(assoTrack)) + continue; + + if constexpr (requires { assoTrack.channelID(); }) { + if (cfgSel.cfgRequireFt0cOuterRing && !assoTrack.isTrackFT0Outer()) + continue; + if (cfgSel.cfgRequireFt0cInnerRing && assoTrack.isTrackFT0Outer()) + continue; + assoAmpl = assoTrack.amplitude(); } else { assoAmpl = 1.0f; } + float deltaPhi = RecoDecay::constrainAngle(triggerTrack.phi() - assoTrack.phi(), -PIHalf); float deltaEta = triggerTrack.eta() - assoTrack.eta(); if (!mixing) { - fillAssocTrackQA(assoTrack); - histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * trigAmpl * assoAmpl); + fillAssocTrackQA(assoTrack, assoAmpl); + histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * trigAmpl * assoAmpl * trkeff * trkAcc); } else { - histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * trigAmpl * assoAmpl); + histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * trigAmpl * assoAmpl * trkeff * trkAcc); } - if constexpr (std::experimental::is_detected::value) { - target->getPairHist()->Fill(step, vz, multiplicity, 1.0, 1.0, deltaPhi, deltaEta, 1.0, eventWeight * trigAmpl * assoAmpl); - } else if constexpr (std::experimental::is_detected::value) { - target->getPairHist()->Fill(step, vz, multiplicity, triggerTrack.pt(), triggerTrack.pt(), deltaPhi, deltaEta, triggerTrack.invMass(), eventWeight * trigAmpl * assoAmpl); + if constexpr (requires { triggerTrack.channelID(); }) { + target->getPairHist()->Fill(step, fSampleIndex, vz, multiplicity, 1.0, deltaPhi, deltaEta, 1.0, eventWeight * trigAmpl * assoAmpl * trkeff * trkAcc); + } else if constexpr (requires { triggerTrack.invMass(); }) { + target->getPairHist()->Fill(step, fSampleIndex, vz, multiplicity, triggerTrack.pt(), deltaPhi, deltaEta, triggerTrack.invMass(), eventWeight * trigAmpl * assoAmpl * trkeff * trkAcc); } else { - target->getPairHist()->Fill(step, vz, multiplicity, triggerTrack.pt(), triggerTrack.pt(), deltaPhi, deltaEta, 1.0, eventWeight * trigAmpl * assoAmpl); + target->getPairHist()->Fill(step, fSampleIndex, vz, multiplicity, triggerTrack.pt(), deltaPhi, deltaEta, 1.0, eventWeight * trigAmpl * assoAmpl * trkeff * trkAcc); } } // associated tracks } // trigger tracks @@ -312,27 +604,32 @@ struct LongrangecorrDerived { template void processSame(TCollision const& col, TTriggers const& triggers, TAssocs const& assocs) { - if (std::abs(col.posZ()) >= cfgVtxCut) { + if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) { return; } + loadEffCorrection(col.timestamp()); fillCollQA(col); auto multiplicity = 1.0f; - if constexpr (std::experimental::is_detected::value) { - if (isUseCentEst) + if constexpr (requires { col.centrality(); }) { + if (cfgSel.isUseCentEst) multiplicity = col.centrality(); else multiplicity = col.multiplicity(); } else { multiplicity = col.multiplicity(); } - fillCorrHist(same, triggers, assocs, false, col.posZ(), multiplicity, 1.0); + if (cfgSel.applyEffCorr) { + fillCorrHist(same, triggers, assocs, false, col.posZ(), multiplicity, 1.0); + } else { + fillCorrHist(same, triggers, assocs, false, col.posZ(), multiplicity, 1.0); + } } // process same template void processMixed(TCollision const& cols, TrackTypes&&... tracks) { auto getMultiplicity = [this](auto& col) { - if constexpr (std::experimental::is_detected::value) { + if constexpr (requires { col.gapSide(); }) { if (!isUpcEventSelected(col)) { return -1.0f; } @@ -340,8 +637,8 @@ struct LongrangecorrDerived { (void)this; } auto multiplicity = 1.0f; - if constexpr (std::experimental::is_detected::value) { - if (isUseCentEst) + if constexpr (requires { col.centrality(); }) { + if (cfgSel.isUseCentEst) multiplicity = col.centrality(); else multiplicity = col.multiplicity(); @@ -351,35 +648,49 @@ struct LongrangecorrDerived { return multiplicity; }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; - Pair pairs{binningOnVtxAndMult, cfgNmixedevent, -1, cols, tracksTuple, &cache}; + Pair pairs{binningOnVtxAndMult, cfgSel.cfgNmixedevent, -1, cols, tracksTuple, &cache}; for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [col1, tracks1, col2, tracks2] = *it; - if constexpr (std::experimental::is_detected::value) { + if constexpr (requires { col1.gapSide(); } || requires { col2.gapSide(); }) { if (!isUpcEventSelected(col1) || !isUpcEventSelected(col2)) { continue; } } + if (std::abs(col1.posZ()) >= cfgSel.cfgVtxCut || std::abs(col2.posZ()) >= cfgSel.cfgVtxCut) { + continue; + } float eventweight = 1.0f / it.currentWindowNeighbours(); auto multiplicity = getMultiplicity(col1); - fillCorrHist(mixed, tracks1, tracks2, true, col1.posZ(), multiplicity, eventweight); + int bin = binningOnVtxAndMult.getBin(std::tuple(col1.posZ(), multiplicity)); + loadEffCorrection(col1.timestamp()); + + if (cfgSel.cfgVerbosity > 0) { + LOGF(info, "processMixed: Mixed collisions bin: %d pair: [%d, %d] %d (%.3f, %.3f), %d (%.3f, %.3f)", bin, it.isNewWindow(), it.currentWindowNeighbours(), col1.globalIndex(), col1.posZ(), col1.multiplicity(), col2.globalIndex(), col2.posZ(), col2.multiplicity()); + } + + if (cfgSel.applyEffCorr) { + fillCorrHist(mixed, tracks1, tracks2, true, col1.posZ(), multiplicity, eventweight); + } else { + fillCorrHist(mixed, tracks1, tracks2, true, col1.posZ(), multiplicity, eventweight); + } } // pair loop } // process mixed template void processMcSame(McCollsTable::iterator const& mccollision, soa::SmallGroups const& collisions, TTriggers const& triggers, TAssocs const& assocs) { - if (std::abs(mccollision.posZ()) >= cfgVtxCut) { + if (std::abs(mccollision.posZ()) >= cfgSel.cfgVtxCut) { return; } fillCollQA(mccollision); auto multiplicity = mccollision.multiplicity(); - if (isUseDataLikeMult > 0) { + if (cfgSel.isUseDataLikeMult > 0) { for (const auto& collision : collisions) { - if (isUseCentEst) + if (cfgSel.isUseCentEst) multiplicity = collision.centrality(); else multiplicity = collision.multiplicity(); @@ -391,7 +702,7 @@ struct LongrangecorrDerived { template void processMcMixed(McCollsTable const& mccollisions, aod::LRCollisionsWithLabel const& collisions, TrackTypes&&... tracks) { - bool useMCMultiplicity = (isUseDataLikeMult == 0); + bool useMCMultiplicity = (cfgSel.isUseDataLikeMult == 0); auto getMultiplicity = [&collisions, &useMCMultiplicity, this](auto& col) { if (useMCMultiplicity) @@ -399,18 +710,18 @@ struct LongrangecorrDerived { auto groupedCollisions = collisions.sliceByCached(aod::lrcorrcolltable::lrMcCollisionId, col.globalIndex(), this->cache); if (groupedCollisions.size() == 0) return -1.0f; - if (isUseCentEst) + if (cfgSel.isUseCentEst) return groupedCollisions.begin().centrality(); else return groupedCollisions.begin().multiplicity(); }; using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - MixedBinning binningOnVtxAndMult{{getMultiplicity}, {axisVtxZME, axisMultME}, true}; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; - Pair pairs{binningOnVtxAndMult, cfgNmixedevent, -1, mccollisions, tracksTuple, &cache}; + Pair pairs{binningOnVtxAndMult, cfgSel.cfgNmixedevent, -1, mccollisions, tracksTuple, &cache}; for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [col1, tracks1, col2, tracks2] = *it; float eventweight = 1.0f / it.currentWindowNeighbours(); @@ -419,6 +730,38 @@ struct LongrangecorrDerived { } // pair loop } // process MC mixed + template + void processMcGenSame(McCollsTable::iterator const& mccollision, TTriggers const& triggers, TAssocs const& assocs) + { + if (std::abs(mccollision.posZ()) >= cfgSel.cfgVtxCut) { + return; + } + fillCollQA(mccollision); + auto multiplicity = mccollision.multiplicity(); + fillCorrHist(same, triggers, assocs, false, mccollision.posZ(), multiplicity, 1.0); + } // process MC gen same + + template + void processMcGenMixed(McCollsTable const& mccollisions, TrackTypes&&... tracks) + { + auto getMultiplicity = [this](auto& collision) { + (void)this; + return collision.multiplicity(); + }; + using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; + MixedBinning binningOnVtxAndMult{{getMultiplicity}, {cfgAxis.axisVtxZME, cfgAxis.axisMultME}}; + auto tracksTuple = std::make_tuple(std::forward(tracks)...); + using TupleAtrack = std::tuple_element<0, decltype(tracksTuple)>::type; + using TupleBtrack = std::tuple_element - 1, decltype(tracksTuple)>::type; + Pair pairs{binningOnVtxAndMult, cfgSel.cfgNmixedevent, -1, mccollisions, tracksTuple, &cache}; + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [col1, tracks1, col2, tracks2] = *it; + float eventweight = 1.0f / it.currentWindowNeighbours(); + auto multiplicity = getMultiplicity(col1); + fillCorrHist(mixed, tracks1, tracks2, true, col1.posZ(), multiplicity, eventweight); + } // pair loop + } // process MC gen mixed + void processTpcft0aSE(CollsTable::iterator const& col, TrksTable const& tracks, Ft0aTrksTable const& ft0as) { processSame(col, tracks, ft0as); @@ -444,22 +787,17 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0as); } - void processV0mftSE(CollsTable::iterator const& col, V0TrksTable const& tracks, MftTrksTable const& mfts) - { - processSame(col, tracks, mfts); - } - - void processTpcmftbestSE(CollsTable::iterator const& col, TrksTable const& tracks, MftbestTrksTable const& mfts) + void processV0ft0cSE(CollsTable::iterator const& col, V0TrksTable const& tracks, Ft0cTrksTable const& ft0cs) { - processSame(col, tracks, mfts); + processSame(col, tracks, ft0cs); } - void processMftbestft0aSE(CollsTable::iterator const& col, MftbestTrksTable const& mfts, Ft0aTrksTable const& ft0as) + void processV0tpcSE(CollsTable::iterator const& col, V0TrksTable const& v0s, TrksTable const& tracks) { - processSame(col, mfts, ft0as); + processSame(col, v0s, tracks); } - void processV0mftbestSE(CollsTable::iterator const& col, V0TrksTable const& tracks, MftbestTrksTable const& mfts) + void processV0mftSE(CollsTable::iterator const& col, V0TrksTable const& tracks, MftTrksTable const& mfts) { processSame(col, tracks, mfts); } @@ -494,22 +832,17 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0as); } - void processV0mftME(CollsTable const& cols, V0TrksTable const& tracks, MftTrksTable const& mfts) + void processV0ft0cME(CollsTable const& cols, V0TrksTable const& tracks, Ft0cTrksTable const& ft0cs) { - processMixed(cols, tracks, mfts); - } - - void processTpcmftbestME(CollsTable const& cols, TrksTable const& tracks, MftbestTrksTable const& mfts) - { - processMixed(cols, tracks, mfts); + processMixed(cols, tracks, ft0cs); } - void processMftbestft0aME(CollsTable const& cols, MftbestTrksTable const& mfts, Ft0aTrksTable const& ft0as) + void processV0tpcME(CollsTable const& cols, V0TrksTable const& v0s, TrksTable const& tracks) { - processMixed(cols, mfts, ft0as); + processMixed(cols, v0s, tracks); } - void processV0mftbestME(CollsTable const& cols, V0TrksTable const& tracks, MftbestTrksTable const& mfts) + void processV0mftME(CollsTable const& cols, V0TrksTable const& tracks, MftTrksTable const& mfts) { processMixed(cols, tracks, mfts); } @@ -559,31 +892,12 @@ struct LongrangecorrDerived { processSame(col, tracks, ft0as); } - void processUpcV0mftSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) + void processUpcV0ft0cSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, Ft0cTrksUpcTable const& ft0cs) { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, tracks, mfts); - } - - void processUpcTpcmftbestSE(UpcCollsTable::iterator const& col, TrksUpcTable const& tracks, MftbestTrksUpcTable const& mfts) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, tracks, mfts); - } - - void processUpcMftbestft0aSE(UpcCollsTable::iterator const& col, MftbestTrksUpcTable const& mfts, Ft0aTrksUpcTable const& ft0as) - { - if (!isUpcEventSelected(col)) { - return; - } - processSame(col, mfts, ft0as); + processSame(col, tracks, ft0cs); } - void processUpcV0mftbestSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, MftbestTrksUpcTable const& mfts) + void processUpcV0mftSE(UpcCollsTable::iterator const& col, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) { if (!isUpcEventSelected(col)) { return; @@ -616,22 +930,12 @@ struct LongrangecorrDerived { processMixed(cols, tracks, ft0as); } - void processUpcV0mftME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) - { - processMixed(cols, tracks, mfts); - } - - void processUpcTpcmftbestME(UpcCollsTable const& cols, TrksUpcTable const& tracks, MftbestTrksUpcTable const& mfts) + void processUpcV0ft0cME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, Ft0cTrksUpcTable const& ft0cs) { - processMixed(cols, tracks, mfts); - } - - void processUpcMftbestft0aME(UpcCollsTable const& cols, MftbestTrksUpcTable const& mfts, Ft0aTrksUpcTable const& ft0as) - { - processMixed(cols, mfts, ft0as); + processMixed(cols, tracks, ft0cs); } - void processUpcV0mftbestME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, MftbestTrksUpcTable const& mfts) + void processUpcV0mftME(UpcCollsTable const& cols, V0TrksUpcTable const& tracks, MftTrksUpcTable const& mfts) { processMixed(cols, tracks, mfts); } @@ -686,6 +990,306 @@ struct LongrangecorrDerived { processMcMixed(mccollisions, collisions, ft0as, ft0cs); } + void processMcGenTpcft0aSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) + { + processMcGenSame(mccollision, tracks, ft0as); + } + + void processMcGenTpcft0cSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McFt0cTrksTable const& ft0cs) + { + processMcGenSame(mccollision, tracks, ft0cs); + } + + void processMcGenTpcmftSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McMftTrksTable const& mfts) + { + processMcGenSame(mccollision, tracks, mfts); + } + + void processMcGenMftft0aSE(McCollsTable::iterator const& mccollision, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) + { + processMcGenSame(mccollision, mfts, ft0as); + } + + void processMcGenV0ft0aSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) + { + processMcGenSame(mccollision, tracks, ft0as); + } + + void processMcGenV0ft0cSE(McCollsTable::iterator const& mccollision, McTrksTable const& tracks, McFt0cTrksTable const& ft0cs) + { + processMcGenSame(mccollision, tracks, ft0cs); + } + + void processMcGenFt0aft0cSE(McCollsTable::iterator const& mccollision, McFt0aTrksTable const& ft0as, McFt0cTrksTable const& ft0cs) + { + processMcGenSame(mccollision, ft0as, ft0cs); + } + + void processMcGenTpcft0aME(McCollsTable const& mccollisions, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) + { + processMcGenMixed(mccollisions, tracks, ft0as); + } + + void processMcGenTpcft0cME(McCollsTable const& mccollisions, McTrksTable const& tracks, McFt0cTrksTable const& ft0cs) + { + processMcGenMixed(mccollisions, tracks, ft0cs); + } + + void processMcGenTpcmftME(McCollsTable const& mccollisions, McTrksTable const& tracks, McMftTrksTable const& mfts) + { + processMcGenMixed(mccollisions, tracks, mfts); + } + + void processMcGenMftft0aME(McCollsTable const& mccollisions, McMftTrksTable const& mfts, McFt0aTrksTable const& ft0as) + { + processMcGenMixed(mccollisions, mfts, ft0as); + } + + void processMcGenV0ft0aME(McCollsTable const& mccollisions, McTrksTable const& tracks, McFt0aTrksTable const& ft0as) + { + processMcGenMixed(mccollisions, tracks, ft0as); + } + + void processMcGenV0ft0cME(McCollsTable const& mccollisions, McTrksTable const& tracks, McFt0cTrksTable const& ft0cs) + { + processMcGenMixed(mccollisions, tracks, ft0cs); + } + + void processMcGenFt0aft0cME(McCollsTable const& mccollisions, McFt0aTrksTable const& ft0as, McFt0cTrksTable const& ft0cs) + { + processMcGenMixed(mccollisions, ft0as, ft0cs); + } + + using ColMCTrueTable = soa::Join; + using ColMCRecTable = soa::SmallGroups>; + using TrksMCRecTable = soa::Join; + Preslice perColMidtrack = aod::track::collisionId; + + template + bool isEventSelected(CheckCol const& col) + { + if (!col.sel8()) + return false; + if (!col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + return false; + if (!col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + return false; + if (!col.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + return false; + if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) + return false; + return true; + } + + template + bool isGenPartSelected(CheckGenPart const& particle) + { + if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) + return false; + auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle == nullptr) + return false; + if (std::abs(pdgParticle->Charge()) < KminCharge) + return false; + return true; + } + + void processTPCtrackEff(ColMCTrueTable::iterator const& mcCollision, ColMCRecTable const& RecCols, + TrksMCRecTable const& RecTracks, aod::McParticles const& mcparticles) + { + if (std::abs(mcCollision.posZ()) >= cfgSel.cfgVtxCut) { + return; + } + bool atLeastOne = false; + for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) + continue; + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) + continue; + atLeastOne = true; + } + for (const auto& particle : mcparticles) { + if (!isGenPartSelected(particle) || + std::abs(particle.eta()) > cfgSel.cfgEtaCut || + particle.pt() < cfgSel.cfgPtCutMin || + particle.pt() > cfgSel.cfgPtCutMax) + continue; + if (atLeastOne) { + histos.fill(HIST("hGenMCdndpt"), mcCollision.posZ(), particle.eta(), particle.pt()); + + if (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton) { + auto pdgcode = std::abs(particle.pdgCode()); + + bool isTargetGen = (cfgSel.cfgPidMask == KPidMaskPion && pdgcode == PDG_t::kPiPlus) || + (cfgSel.cfgPidMask == KPidMaskKaon && pdgcode == PDG_t::kKPlus) || + (cfgSel.cfgPidMask == KPidMaskProton && pdgcode == PDG_t::kProton); + + if (isTargetGen) { + histos.fill(HIST("hMCGen_PidPtEtaPhi"), particle.pt(), particle.eta(), particle.phi()); + } + } + } + } + + for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) + continue; + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) + continue; + auto recTracksPart = RecTracks.sliceBy(perColMidtrack, RecCol.globalIndex()); + for (const auto& track : recTracksPart) { + if (!track.isGlobalTrack()) + continue; + if (!myTrackFilter.IsSelected(track)) + continue; + if (!track.has_mcParticle()) + continue; + auto particle = track.mcParticle(); + if (RecCol.mcCollisionId() != particle.mcCollisionId()) + continue; + if (particle.isPhysicalPrimary()) { + histos.fill(HIST("hRecMCdndpt"), mcCollision.posZ(), particle.eta(), particle.pt()); + + if (cfgSel.cfgGetdEdx) { + int pdgIdx = 0; + auto absPdg = std::abs(particle.pdgCode()); + + if (absPdg == PDG_t::kPiPlus) + pdgIdx = 1; // o2-linter: disable=pdg/explicit-code (histogram species index) + else if (absPdg == PDG_t::kKPlus) + pdgIdx = 2; // o2-linter: disable=pdg/explicit-code (histogram species index) + else if (absPdg == PDG_t::kProton) + pdgIdx = 3; // o2-linter: disable=pdg/explicit-code (histogram species index) + + if (pdgIdx > 0) { + histos.fill(HIST("MC_TpcdEdx_ptwise"), static_cast(pdgIdx), track.pt(), track.tpcSignal()); + } + } + + bool isTpcPion = (track.tpcNSigmaPi() > cfgSel.cfgPidNsigmaMin && track.tpcNSigmaPi() < cfgSel.cfgPidNsigmaMax); + bool isTpcKaon = (track.tpcNSigmaKa() > cfgSel.cfgPidNsigmaMin && track.tpcNSigmaKa() < cfgSel.cfgPidNsigmaMax); + bool isTpcProton = (track.tpcNSigmaPr() > cfgSel.cfgPidNsigmaMin && track.tpcNSigmaPr() < cfgSel.cfgPidNsigmaMax); + + bool isTofPion = (track.tofNSigmaPi() > cfgSel.cfgPidNsigmaMin && track.tofNSigmaPi() < cfgSel.cfgPidNsigmaMax); + bool isTofKaon = (track.tofNSigmaKa() > cfgSel.cfgPidNsigmaMin && track.tofNSigmaKa() < cfgSel.cfgPidNsigmaMax); + bool isTofProton = (track.tofNSigmaPr() > cfgSel.cfgPidNsigmaMin && track.tofNSigmaPr() < cfgSel.cfgPidNsigmaMax); + + bool isPion = false, isKaon = false, isProton = false; + + if (track.pt() > cfgSel.cfgTofPidPtCut && track.hasTOF()) { + isPion = isTofPion && isTpcPion; + isKaon = isTofKaon && isTpcKaon; + isProton = isTofProton && isTpcProton; + } else if (!(track.pt() > cfgSel.cfgTofPidPtCut && !track.hasTOF())) { + isPion = isTpcPion; + isKaon = isTpcKaon; + isProton = isTpcProton; + } + + if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { + isPion = isKaon = isProton = false; + } + + if (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton) { + + auto pdgcodeRec = std::abs(particle.pdgCode()); + bool isTargetRec = (cfgSel.cfgPidMask == KPidMaskPion && isPion && pdgcodeRec == PDG_t::kPiPlus) || + (cfgSel.cfgPidMask == KPidMaskKaon && isKaon && pdgcodeRec == PDG_t::kKPlus) || + (cfgSel.cfgPidMask == KPidMaskProton && isProton && pdgcodeRec == PDG_t::kProton); + + if (isTargetRec) { + histos.fill(HIST("hMCRec_PidPtEtaPhi"), particle.pt(), particle.eta(), particle.phi()); + } + } + } + } + } + } + + using CollStandardTable = soa::Join; + using TrksStandardTable = soa::Join; + + void processPIDQA(CollsTable const& lrCollisions, TrksStandardTable const& standardTracks) + { + for (auto const& col : lrCollisions) { + + if (std::abs(col.posZ()) >= cfgSel.cfgVtxCut) { + continue; + } + + // EXTRACT CENTRALITY/MULTIPLICITY + float multiplicity = 1.0f; + if constexpr (requires { col.centrality(); }) { + if (cfgSel.isUseCentEst) + multiplicity = col.centrality(); + else + multiplicity = col.multiplicity(); + } else { + multiplicity = col.multiplicity(); + } + + auto tracks = standardTracks.sliceByCached(aod::track::collisionId, col.globalIndex(), this->cache); + + for (auto const& track : tracks) { + if (!track.isGlobalTrack()) + continue; + if (!myTrackFilter.IsSelected(track)) + continue; + + if (!track.hasTOF()) + continue; + + float tpcNsig = 0.0f, tofNsig = 0.0f; + if (cfgSel.cfgPidMask & KPidMaskPion) { + tpcNsig = track.tpcNSigmaPi(); + tofNsig = track.tofNSigmaPi(); + } else if (cfgSel.cfgPidMask & KPidMaskKaon) { + tpcNsig = track.tpcNSigmaKa(); + tofNsig = track.tofNSigmaKa(); + } else if (cfgSel.cfgPidMask & KPidMaskProton) { + tpcNsig = track.tpcNSigmaPr(); + tofNsig = track.tofNSigmaPr(); + } + + // FILL BEFORE CUTS + if (cfgSel.cfgGetNsigmaQA && (cfgSel.cfgPidMask == KPidMaskPion || cfgSel.cfgPidMask == KPidMaskKaon || cfgSel.cfgPidMask == KPidMaskProton)) { + histos.fill(HIST("TofTpcNsigma_before"), multiplicity, track.pt(), tpcNsig, tofNsig); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + } + } + + bool isTpcPion = (track.tpcNSigmaPi() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaPi() < cfgSel.cfgCleanNsigmaTpcMax); + bool isTpcKaon = (track.tpcNSigmaKa() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaKa() < cfgSel.cfgCleanNsigmaTpcMax); + bool isTpcProton = (track.tpcNSigmaPr() > cfgSel.cfgCleanNsigmaTpcMin && track.tpcNSigmaPr() < cfgSel.cfgCleanNsigmaTpcMax); + + bool isTofPion = (track.tofNSigmaPi() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaPi() < cfgSel.cfgCleanNsigmaTofMax); + bool isTofKaon = (track.tofNSigmaKa() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaKa() < cfgSel.cfgCleanNsigmaTofMax); + bool isTofProton = (track.tofNSigmaPr() > cfgSel.cfgCleanNsigmaTofMin && track.tofNSigmaPr() < cfgSel.cfgCleanNsigmaTofMax); + + bool isPion = isTofPion && isTpcPion; + bool isKaon = isTofKaon && isTpcKaon; + bool isProton = isTofProton && isTpcProton; + + if ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton)) { + isPion = isKaon = isProton = false; + } + + bool isTargetParticle = ((cfgSel.cfgPidMask & KPidMaskPion) && isPion) || + ((cfgSel.cfgPidMask & KPidMaskKaon) && isKaon) || + ((cfgSel.cfgPidMask & KPidMaskProton) && isProton); + + // FILL AFTER CUTS + if (cfgSel.cfgGetNsigmaQA && isTargetParticle) { + histos.fill(HIST("TofTpcNsigma_after"), multiplicity, track.pt(), tpcNsig, tofNsig); + if (cfgSel.cfgGetdEdx) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), multiplicity, track.pt(), track.tpcSignal(), tofNsig); + } + } + } + } + } + PROCESS_SWITCH(LongrangecorrDerived, processTpcft0aSE, "same event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0aME, "mixed event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processTpcft0cSE, "same event TPC vs FT0C", false); @@ -696,14 +1300,12 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processMftft0aME, "mixed event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0aSE, "same event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processV0ft0aME, "mixed event V0 vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processV0ft0cSE, "same event V0 vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processV0ft0cME, "mixed event V0 vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processV0tpcSE, "same event V0 vs TPC", false); + PROCESS_SWITCH(LongrangecorrDerived, processV0tpcME, "mixed event V0 vs TPC", false); PROCESS_SWITCH(LongrangecorrDerived, processV0mftSE, "same event V0 vs MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processV0mftME, "mixed event V0 vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processTpcmftbestSE, "same event TPC vs best MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processTpcmftbestME, "mixed event TPC vs best MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processMftbestft0aSE, "same event best MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processMftbestft0aME, "mixed event best MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processV0mftbestSE, "same event V0 vs best MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processV0mftbestME, "mixed event V0 vs best MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processFt0aft0cSE, "same event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processFt0aft0cME, "mixed event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcft0aSE, "same UPC event TPC vs FT0A", false); @@ -716,14 +1318,10 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processUpcMftft0aME, "mixed UPC event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0aSE, "same UPC event V0 vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0aME, "mixed UPC event V0 vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0cSE, "same UPC event V0 vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processUpcV0ft0cME, "mixed UPC event V0 vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftSE, "same UPC event V0 vs MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftME, "mixed UPC event V0 vs MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcmftbestSE, "same UPC event TPC vs best MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcTpcmftbestME, "mixed UPC event TPC vs best MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcMftbestft0aSE, "same UPC event best MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcMftbestft0aME, "mixed UPC event best MFT vs FT0A", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftbestSE, "same UPC event V0 vs best MFT", false); - PROCESS_SWITCH(LongrangecorrDerived, processUpcV0mftbestME, "mixed UPC event V0 vs best MFT", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0aSE, "same MC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0aME, "mixed MC event TPC vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcTpcft0cSE, "same MC event TPC vs FT0C", false); @@ -734,6 +1332,22 @@ struct LongrangecorrDerived { PROCESS_SWITCH(LongrangecorrDerived, processMcMftft0aME, "mixed MC event MFT vs FT0A", false); PROCESS_SWITCH(LongrangecorrDerived, processMcFt0aft0cSE, "same MC event FT0A vs FT0C", false); PROCESS_SWITCH(LongrangecorrDerived, processMcFt0aft0cME, "mixed MC event FT0A vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0aSE, "same MC gen event TPC vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0aME, "mixed MC gen event TPC vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0cSE, "same MC gen event TPC vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcft0cME, "mixed MC gen event TPC vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcmftSE, "same MC gen event TPC vs MFT", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenTpcmftME, "mixed MC gen event TPC vs MFT", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenMftft0aSE, "same MC gen event MFT vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenMftft0aME, "mixed MC gen event MFT vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0aSE, "same MC gen event V0 vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0aME, "mixed MC gen event V0 vs FT0A", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0cSE, "same MC gen event V0 vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenV0ft0cME, "mixed MC gen event V0 vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenFt0aft0cSE, "same MC gen event FT0A vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processMcGenFt0aft0cME, "mixed MC gen event FT0A vs FT0C", false); + PROCESS_SWITCH(LongrangecorrDerived, processTPCtrackEff, "process TPC track efficiency", false); + PROCESS_SWITCH(LongrangecorrDerived, processPIDQA, "process PID QA on standard AODs", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx b/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx new file mode 100644 index 00000000000..e34bd64f3a9 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/mftcorrelations.cxx @@ -0,0 +1,1449 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file mftcorrelations.cxx +/// \brief task for two-particle (hadron-hadron) angular correlations built from MFT tracks + +/// \author Mintu Haldar + +#include "PWGCF/Core/CorrelationContainer.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // Pair<>, SameKindPair<> +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace constants::math; + +// Bitmask describing which multiplicity/centrality estimators feed the "multCorrelations" QA +// histogram and, when set, the outlier-rejection formula. +namespace o2::aod +{ +namespace mftmultset +{ +DECLARE_SOA_COLUMN(Multiplicities, multiplicities, std::vector); //! List of auxiliary multiplicities +enum MultiplicityEstimators : uint16_t { + CentFT0C = 1 << 0, + MultFV0A = 1 << 1, + MultNTracksPV = 1 << 2, + MultNTracksGlobal = 1 << 3, + CentFT0M = 1 << 4 +}; +inline constexpr uint32_t NMultiplicityEstimators = __builtin_ctz(CentFT0M) + 1; +} // namespace mftmultset + +DECLARE_SOA_TABLE(MFTMultSets, "AOD", "MFTMULTSET", mftmultset::Multiplicities); //! Auxilary multiplicity set table +using MFTMultSet = MFTMultSets::iterator; +} // namespace o2::aod + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; + +struct Mftcorrelations { + SliceCache cache; + + // ------------------------------------------------------------------ Configurables + O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") + + // Global acceptance + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackEtaCut, float, 0.8f, "Eta cut for global (central-barrel) tracks used as MFT-Global trigger") + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackPtMin, float, 0.15f, "Minimal pT for global tracks") + O2_DEFINE_CONFIGURABLE(cfgGlobalTrackPtMax, float, 10.0f, "Maximal pT for global tracks") + + // MFT acceptance is roughly -3.6 < eta < -2.5; keep as configurables since it is tag/setup dependent. + O2_DEFINE_CONFIGURABLE(cfgMFTCutEtaMin, float, -3.6f, "Minimal eta for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutEtaMax, float, -2.5f, "Maximal eta for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutPtMin, float, 0.5f, "Minimal pT for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTCutPtMax, float, 20.0f, "Maximal pT for MFT tracks") + O2_DEFINE_CONFIGURABLE(cfgMFTMinNClusters, int, 5, "Minimal number of MFT clusters on track") + + O2_DEFINE_CONFIGURABLE(cfgPtOrder, int, 1, "Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)") + O2_DEFINE_CONFIGURABLE(cfgTriggerCharge, int, 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative") + O2_DEFINE_CONFIGURABLE(cfgAssociatedCharge, int, 0, "Select on charge of associated particle: 0 = all; 1 = positive; -1 = negative") + O2_DEFINE_CONFIGURABLE(cfgPairCharge, int, 0, "Select on charge of the pair: 0 = all; 1 = like sign; -1 = unlike sign") + + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, int, 0, "0 = OFF and 1 = ON: read efficiency THn from a local ROOT file instead of CCDB") + O2_DEFINE_CONFIGURABLE(cfgDropStepRECO, bool, false, "drop step RECO if efficiency correction is used") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for trigger particles") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for associated particles") + // FIX #6d: efficiency for central-barrel (global) tracks, used in MFT-Global (trigger) and Global-Global channels. + O2_DEFINE_CONFIGURABLE(cfgEfficiencyGlobal, std::string, "", "CCDB path (or local file if cfgLocalEfficiency=1) to efficiency object for central-barrel (global) tracks") + + O2_DEFINE_CONFIGURABLE(cfgMultCorrelationsMask, uint16_t, 0, "Bitmask of multiplicity/centrality estimators to fill in multCorrelations QA / use for outlier rejection (see mftmultset enum)") + O2_DEFINE_CONFIGURABLE(cfgMultCutFormula, std::string, "", "Multiplicity outlier-rejection formula. A result > 0 accepts the event. Parameters: [cFT0C], [mFV0A], [mPV], [mGlob], [cFT0M]") + + O2_DEFINE_CONFIGURABLE(cfgNoMixedEvents, int, 5, "Number of mixed events per event") + O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)") + + // ------------------------------------------------------------------ Phi weight + // NOTE: keep OFF (empty file) when the efficiency object already has a phi axis, otherwise phi is corrected twice. + O2_DEFINE_CONFIGURABLE(cfgPhiWeightFile, std::string, "", "Local ROOT file with the phi-weight TH1 (empty = disabled)") + O2_DEFINE_CONFIGURABLE(cfgPhiWeightName, std::string, "", "Name of the phi-weight TH1 object inside cfgPhiWeightFile") + + // ------------------------------------------------------------------ Axes + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {40, -2.2, 2.2}, "delta eta axis for histograms (MFT-MFT so somewhat narrower than TPC-TPC)"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0}, "pt associated axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity / centrality axis for histograms"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -3.6, -2.5}, "eta axis for efficiency histograms (MFT acceptance)"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0}, "pt axis for efficiency histograms"}; + ConfigurableAxis axisPhiEfficiency{"axisPhiEfficiency", {36, 0, TwoPI}, "phi axis for efficiency histograms"}; + + ConfigurableAxis axisEtaEfficiencyGlobal{"axisEtaEfficiencyGlobal", {16, -0.8, 0.8}, "eta axis for efficiency histograms (global tracks)"}; + ConfigurableAxis axisPtEfficiencyGlobal{"axisPtEfficiencyGlobal", {VARIABLE_WIDTH, 0.0, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 20.0}, "pt axis for efficiency histograms (global tracks)"}; + + ConfigurableAxis axisMultCorrCent{"axisMultCorrCent", {100, 0, 100}, "multiplicity correlation axis for centralities"}; + ConfigurableAxis axisMultCorrV0{"axisMultCorrV0", {1000, 0, 100000}, "multiplicity correlation axis for V0 multiplicities"}; + ConfigurableAxis axisMultCorrMult{"axisMultCorrMult", {1000, 0, 1000}, "multiplicity correlation axis for track multiplicities"}; + + // ------------------------------------------------------------------ QA Axes + ConfigurableAxis axisMultQA{"axisMultQA", {100, 0, 100}, "multiplicity/centrality axis for QA histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0, 15.0, 20.0}, "pT axis for QA histograms (yields)"}; + ConfigurableAxis axisEtaQA{"axisEtaQA", {50, -4, -2}, "eta axis for QA histograms"}; + ConfigurableAxis axisPhiQA{"axisPhiQA", {200, 0, TwoPI}, "phi axis for QA histograms"}; + + // Global MFT + ConfigurableAxis axisEtaGlobalQA{"axisEtaGlobalQA", {40, -1, 1}, "eta axis for global-track QA histograms"}; + ConfigurableAxis axisDeltaEtaMftGlobal{"axisDeltaEtaMftGlobal", {60, 1.0, 5.0}, "delta eta axis for MFT-Global correlations (deltaEta = etaGlobal - etaMFT is POSITIVE, roughly 1.7-4.4); override via JSON if needed"}; + // FIX #4: delta-eta axis for the global-global (central-barrel hadron-hadron) channel. + ConfigurableAxis axisDeltaEtaGlobal{"axisDeltaEtaGlobal", {32, -1.6, 1.6}, "delta eta axis for global-global (central-barrel hadron-hadron) correlations"}; + + // ------------------------------------------------------------------ Event selection + Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgCutVertex; + + // ------------------------------------------------------------------ MFT track selection + Filter mftTrackFilter = (aod::fwdtrack::eta > cfgMFTCutEtaMin) && (aod::fwdtrack::eta < cfgMFTCutEtaMax) && + (aod::fwdtrack::pt > cfgMFTCutPtMin) && (aod::fwdtrack::pt < cfgMFTCutPtMax); + + // ------------------------------------------------------------------ Global track selection + Filter globalTrackFilter = (nabs(aod::track::eta) < cfgGlobalTrackEtaCut) && + (aod::track::pt > cfgGlobalTrackPtMin) && (aod::track::pt < cfgGlobalTrackPtMax) && + requireGlobalTrackInFilter(); + + // ------------------------------------------------------------------ Output + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + + OutputObj sameMftGlobal{"sameEventMftGlobal"}; + OutputObj mixedMftGlobal{"mixedEventMftGlobal"}; + + // FIX #4: global-global (central-barrel hadron-hadron) output objects. + OutputObj sameGlobal{"sameEventGlobalGlobal"}; + OutputObj mixedGlobal{"mixedEventGlobalGlobal"}; + + HistogramRegistry registry{"registry"}; + + Service ccdb; + Service pdg; + + struct Config { + THn* mEfficiencyTrigger = nullptr; + THn* mEfficiencyAssociated = nullptr; + THn* mEfficiencyGlobal = nullptr; // FIX #6d + TH1* mPhiWeight = nullptr; + bool efficiencyLoaded = false; + } cfg; + + std::vector efficiencyAssociatedCache; + std::vector phiWeightCache; + std::vector phiWeightCache1; + std::unique_ptr multCutFormula; + std::array multCutFormulaParamIndex{}; + + // Collisions are expected to already carry the joined centrality/multiplicity estimator tables. + using ExtendedCollisions = soa::Join; + using FilteredCollisions = soa::Filtered; + using FilteredMFTTracks = soa::Filtered; + using FilteredGlobalTracks = soa::Filtered>; + + // For MFT MC Efficiency + // NOTE SmallGroups includes soa::Filtered always + using McEffCollisions = soa::SmallGroups>; + using McEffMFTTracks = soa::Filtered>; + + // For Global MC Efficiency + using McEffGlobalTracks = soa::Filtered>; + + void init(o2::framework::InitContext&) + { + registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisPtQA, "p_{T}"}, {axisEtaQA, "#eta"}}}); + registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisEtaQA, "#eta"}, {axisPhiQA, "#varphi"}}}); + // FIX #6b: efficiency-corrected QA histograms (weight = efficiency correction factor) + registry.add("yields_corrected", "efficiency-corrected multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisPtQA, "p_{T}"}, {axisEtaQA, "#eta"}}}); + registry.add("etaphi_corrected", "efficiency-corrected multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultQA, "multiplicity/centrality"}, {axisEtaQA, "#eta"}, {axisPhiQA, "#varphi"}}}); + registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{axisMultQA, "multiplicity/centrality"}}}); + registry.add("nClusters", "MFT track number of clusters", {HistType::kTH1F, {{12, -0.5, 11.5, "n clusters"}}}); + registry.add("multiplicity_raw", "event multiplicity (no cuts)", {HistType::kTH1F, {{axisMultQA, "multiplicity/centrality"}}}); + + // MC Efficiency + registry.add("MCEff/hNRecoColl", "N reco collisions per MC collision", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.add("MCEff/hCutFlow", "1=all MC coll, 2=size==1, 3=sel8", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + + registry.add("MCEffGlobal/hNRecoColl", "N reco collisions per MC collision (global)", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.add("MCEffGlobal/hCutFlow", "1=all MC coll, 2=size==1, 3=sel8", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + + if (cfgMultCorrelationsMask != 0) { + std::vector multAxes; + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + multAxes.emplace_back(axisMultCorrCent, "FT0C centrality"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultFV0A) + multAxes.emplace_back(axisMultCorrV0, "V0A multiplicity"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksPV) + multAxes.emplace_back(axisMultCorrMult, "Nch PV"); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksGlobal) + multAxes.emplace_back(axisMultCorrMult, "Nch Global"); + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + multAxes.emplace_back(axisMultCorrCent, "FT0M centrality"); + registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); + + // FIX #3 + if (!(cfgMultCorrelationsMask & aod::mftmultset::CentFT0C)) { + LOGF(warning, + "cfgMultCorrelationsMask=%d does not include CentFT0C. Mixed-event binning follows " + "getMultiplicity(), so make sure the efficiency objects were produced with the same estimator.", + (int)cfgMultCorrelationsMask); + } + } + + const int maxMixBin = AxisSpec(axisMultiplicity).getNbins() * AxisSpec(axisVertex).getNbins(); + registry.add("eventcount_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}}}); + registry.add("trackcount_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + // FIX #1 + registry.add("eventcount_mftglobal_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_mftglobal_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_mftglobal_same", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + registry.add("trackcount_mftglobal_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + if (!cfgMultCutFormula.value.empty()) { + multCutFormula = std::make_unique("multCutFormula", cfgMultCutFormula.value.c_str()); + std::fill_n(multCutFormulaParamIndex.begin(), std::size(multCutFormulaParamIndex), ~0u); + std::array pars = {"cFT0C", "mFV0A", "mPV", "mGlob", "cFT0M"}; + for (uint32_t i = 0, n = multCutFormula->GetNpar(); i < n; ++i) { + auto m = std::find(pars.begin(), pars.end(), multCutFormula->GetParName(i)); + if (m == pars.end()) { + LOGF(warning, "Unknown parameter in cfgMultCutFormula: %s", multCutFormula->GetParName(i)); + continue; + } + multCutFormulaParamIndex[std::distance(pars.begin(), m)] = i; + } + } + + registry.add("mftmft/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi (MFT-MFT)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEta, "#Delta#eta"}}}); + registry.add("mftmft/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi (MFT-MFT)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEta, "#Delta#eta"}}}); + + registry.add("mftGlobal/SE/Trig_etavsphi", "trigger (global) eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaGlobalQA, "#eta"}}}); + registry.add("mftGlobal/SE/Trig_pt", "trigger (global) pT", {HistType::kTH1F, {{axisPtTrigger, "p_{T}"}}}); + registry.add("mftGlobal/SE/Assoc_etavsphi", "associated (MFT) eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaQA, "#eta"}}}); + registry.add("mftGlobal/SE/Assoc_pt", "associated (MFT) pT", {HistType::kTH1F, {{axisPtAssoc, "p_{T}"}}}); + registry.add("mftGlobal/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaMftGlobal, "#Delta#eta"}}}); + registry.add("mftGlobal/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaMftGlobal, "#Delta#eta"}}}); + + // FIX #4: QA histograms for the global-global channel. + registry.add("eventcount_global_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_global_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_global_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}}}); + registry.add("trackcount_global_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {50, -0.5, 49.5}, {50, -0.5, 49.5}}}); + + registry.add("global/SE/etavsphi", "global track eta vs phi", {HistType::kTH2F, {{axisPhiQA, "#varphi"}, {axisEtaGlobalQA, "#eta"}}}); + registry.add("global/SE/pt", "global track pT", {HistType::kTH1F, {{axisPtQA, "p_{T}"}}}); + registry.add("global/SE/deltaEta_deltaPhi", "same event #Delta#eta-#Delta#varphi (global-global)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaGlobal, "#Delta#eta"}}}); + registry.add("global/ME/deltaEta_deltaPhi", "mixed event #Delta#eta-#Delta#varphi (global-global)", {HistType::kTH2F, {{axisDeltaPhi, "#Delta#varphi"}, {axisDeltaEtaGlobal, "#Delta#eta"}}}); + + std::vector corrAxis = {{axisDeltaEta, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + std::vector effAxis = {{axisEtaEfficiency, "#eta"}, + {axisPtEfficiency, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + {axisPhiEfficiency, "#varphi (rad)"}}; + + std::vector effAxisGlobal = {{axisEtaEfficiencyGlobal, "#eta"}, + {axisPtEfficiencyGlobal, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + {axisPhiEfficiency, "#varphi (rad)"}}; + + std::vector userAxis; // no extra axis needed for hadron-hadron + + std::vector corrAxisMftGlobal = {{axisDeltaEtaMftGlobal, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + + // FIX #4 + std::vector corrAxisGlobal = {{axisDeltaEtaGlobal, "#Delta#eta"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisMultiplicity, "multiplicity / centrality"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisVertex, "z-vtx (cm)"}}; + + sameMftGlobal.setObject(new CorrelationContainer("sameEventMftGlobal", "sameEventMftGlobal", corrAxisMftGlobal, effAxis, userAxis)); + mixedMftGlobal.setObject(new CorrelationContainer("mixedEventMftGlobal", "mixedEventMftGlobal", corrAxisMftGlobal, effAxis, userAxis)); + + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + + same->setTrackEtaCut(cfgMFTCutEtaMax); // upper bound of |eta| cut used internally by CorrelationContainer + + sameGlobal.setObject(new CorrelationContainer("sameEventGlobalGlobal", "sameEventGlobalGlobal", corrAxisGlobal, effAxisGlobal, userAxis)); + mixedGlobal.setObject(new CorrelationContainer("mixedEventGlobalGlobal", "mixedEventGlobalGlobal", corrAxisGlobal, effAxisGlobal, userAxis)); + sameGlobal->setTrackEtaCut(cfgGlobalTrackEtaCut); + + if (!cfgEfficiencyAssociated.value.empty()) + efficiencyAssociatedCache.reserve(512); + + // FIX #5: load the phi weight once at init(). + loadPhiWeight(); + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + // ------------------------------------------------------------------ helpers + + int getMagneticField(uint64_t timestamp) + { + static o2::parameters::GRPObject* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + } + return grpo->getNominalL3Field(); + } + + void loadEfficiency(uint64_t timestamp) + { + if (cfg.efficiencyLoaded) { + return; + } + if (!cfgEfficiencyTrigger.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyTrigger.value.c_str(), "READ"); + cfg.mEfficiencyTrigger = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgEfficiencyTrigger, timestamp); + } + if (cfg.mEfficiencyTrigger == nullptr) { + LOGF(fatal, "Could not load trigger efficiency from %s", cfgEfficiencyTrigger.value.c_str()); + } + LOGF(info, "Loaded MFT trigger efficiency from %s (%p)", cfgEfficiencyTrigger.value.c_str(), static_cast(cfg.mEfficiencyTrigger)); + } + if (!cfgEfficiencyAssociated.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyAssociated.value.c_str(), "READ"); + cfg.mEfficiencyAssociated = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgEfficiencyAssociated, timestamp); + } + if (cfg.mEfficiencyAssociated == nullptr) { + LOGF(fatal, "Could not load associated efficiency from %s", cfgEfficiencyAssociated.value.c_str()); + } + LOGF(info, "Loaded MFT associated efficiency from %s (%p)", cfgEfficiencyAssociated.value.c_str(), static_cast(cfg.mEfficiencyAssociated)); + } + // FIX #6d + if (!cfgEfficiencyGlobal.value.empty()) { + if (cfgLocalEfficiency > 0) { + TFile* f = TFile::Open(cfgEfficiencyGlobal.value.c_str(), "READ"); + cfg.mEfficiencyGlobal = dynamic_cast(f->Get("ccdb_object")); + } else { + cfg.mEfficiencyGlobal = ccdb->getForTimeStamp>(cfgEfficiencyGlobal, timestamp); + } + if (cfg.mEfficiencyGlobal == nullptr) { + LOGF(fatal, "Could not load global-track efficiency from %s", cfgEfficiencyGlobal.value.c_str()); + } + LOGF(info, "Loaded global-track efficiency from %s (%p)", cfgEfficiencyGlobal.value.c_str(), static_cast(cfg.mEfficiencyGlobal)); + } + cfg.efficiencyLoaded = true; + } + + // MFT phi is in [-pi,pi], McParticle/global-track phi in [0,2pi). Use [0,2pi) everywhere. + static float wrapPhi(float phi) + { + return RecoDecay::constrainAngle(phi, 0.f); + } + + // FIX #6a: expected axis order of the efficiency object: (eta, pt, multiplicity, posZ [, phi]). + // - phi is wrapped to [0, 2pi) (MFT track.phi() is in [-pi, pi]) + // - 4-dim and 5-dim objects are supported + // - bins outside the axis range (under/overflow) return 0 = track outside efficiency acceptance + double getEfficiencyCorrection(THn* eff, float eta, float pt, float multiplicity, float posZ, float phi) + { + if (eff == nullptr) { + return 1.0; + } + constexpr int NDimWithoutPhi = 4; // eta, pt, mult, z + constexpr int NDimWithPhi = 5; // eta, pt, mult, z, phi + const int nDim = eff->GetNdimensions(); + if (nDim != NDimWithoutPhi && nDim != NDimWithPhi) { + LOGF(fatal, "Efficiency object has %d dimensions, expected %d (eta,pt,mult,z) or %d (+phi)", nDim, NDimWithoutPhi, NDimWithPhi); + } + const double vals[NDimWithPhi] = {eta, pt, multiplicity, posZ, static_cast(RecoDecay::constrainAngle(phi, 0.f))}; + int bins[NDimWithPhi]; + for (int i = 0; i < nDim; ++i) { + TAxis* ax = eff->GetAxis(i); + bins[i] = ax->FindBin(vals[i]); + if (bins[i] < 1 || bins[i] > ax->GetNbins()) { + return 0.0; + } + } + return eff->GetBinContent(bins); + } + + template + using HasSign = decltype(std::declval().sign()); + template + using HasPdgCode = decltype(std::declval().pdgCode()); + template + using HasIsPhysicalPrimary = decltype(std::declval().isPhysicalPrimary()); + + // Retrieve the multiplicity/centrality estimator selected by cfgMultCorrelationsMask. + // Falls back to FT0C centrality if no mask bit is set. + template + float getMultiplicity(TCollision const& collision) + { + if (cfgMultCorrelationsMask == 0) { + return collision.centFT0C(); + } + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + return collision.centFT0C(); + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + return collision.centFT0M(); + return collision.centFT0C(); + } + + template + bool passOutlier(TCollision const& collision) + { + if (cfgMultCutFormula.value.empty() || multCutFormula == nullptr) + return true; + if (multCutFormulaParamIndex[0] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[0], collision.centFT0C()); + if (multCutFormulaParamIndex[1] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[1], collision.multFV0A()); + if (multCutFormulaParamIndex[2] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[2], collision.multNTracksPV()); + if (multCutFormulaParamIndex[3] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[3], collision.multNTracksPV()); // substitute a "global" estimator if/when available + if (multCutFormulaParamIndex[4] != ~0u) + multCutFormula->SetParameter(multCutFormulaParamIndex[4], collision.centFT0M()); + return multCutFormula->Eval() > 0.0f; + } + + template + void fillMultCorrelations(TCollision const& collision) + { + if (cfgMultCorrelationsMask == 0) + return; + std::vector v; + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0C) + v.push_back(collision.centFT0C()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultFV0A) + v.push_back(collision.multFV0A()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksPV) + v.push_back(collision.multNTracksPV()); + if (cfgMultCorrelationsMask & aod::mftmultset::MultNTracksGlobal) + v.push_back(collision.multNTracksPV()); // substitute a "global" estimator if/when available + if (cfgMultCorrelationsMask & aod::mftmultset::CentFT0M) + v.push_back(collision.centFT0M()); + registry.get(HIST("multCorrelations")).get()->Fill(v.data()); + } + + void loadPhiWeight() + { + if (cfg.mPhiWeight != nullptr || cfgPhiWeightFile.value.empty()) { + return; // already loaded, or feature disabled + } + TFile* f = TFile::Open(cfgPhiWeightFile.value.c_str(), "READ"); + if (!f || f->IsZombie()) { + LOGF(fatal, "Could not open phi weight file %s", cfgPhiWeightFile.value.c_str()); + return; + } + cfg.mPhiWeight = dynamic_cast(f->Get(cfgPhiWeightName.value.c_str())); + if (cfg.mPhiWeight) { + cfg.mPhiWeight->SetDirectory(nullptr); // detach so it survives f->Close() + } else { + LOGF(fatal, "Could not find object %s in %s", cfgPhiWeightName.value.c_str(), cfgPhiWeightFile.value.c_str()); + } + f->Close(); + LOGF(info, "Loaded phi weight '%s' from %s (%p)", cfgPhiWeightName.value.c_str(), cfgPhiWeightFile.value.c_str(), static_cast(cfg.mPhiWeight)); + } + + // Looks up a bin-content-based weight from a 1-D phi map. + // Returns 0 if the histogram is missing so the >0 check upstream skips it cleanly. + float getPhiWeight(TH1* hPhiWeight, float phi) + { + if (!hPhiWeight) { + return 0.f; + } + int binX = hPhiWeight->GetXaxis()->FindBin(wrapPhi(phi)); + return hPhiWeight->GetBinContent(binX); + } + + // ------------------------------------------------------------------ QA + // FIX #6b: "yields"/"etaphi" are RAW. "yields_corrected"/"etaphi_corrected" are weighted with the + // efficiency correction factor (associated-particle efficiency object). + template + void fillQA(TCollision const& collision, float multiplicity, TTracks const& tracks) + { + registry.fill(HIST("multiplicity"), multiplicity); + fillMultCorrelations(collision); + const float posZ = collision.posZ(); + + for (const auto& track : tracks) { + if (track.nClusters() < cfgMFTMinNClusters) { + continue; + } + registry.fill(HIST("yields"), multiplicity, track.pt(), track.eta()); + + float phiWeight = 1.f; + if (cfg.mPhiWeight) { + float phiWgt = getPhiWeight(cfg.mPhiWeight, track.phi()); + phiWeight = (phiWgt > 0) ? 1.f / phiWgt : 1.f; + } + registry.fill(HIST("etaphi"), multiplicity, track.eta(), wrapPhi(track.phi()), phiWeight); + registry.fill(HIST("nClusters"), track.nClusters()); + + float effWeight = 1.f; + if (cfg.mEfficiencyAssociated) { + effWeight = getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ, track.phi()); + } + registry.fill(HIST("yields_corrected"), multiplicity, track.pt(), track.eta(), effWeight); + registry.fill(HIST("etaphi_corrected"), multiplicity, track.eta(), wrapPhi(track.phi()), effWeight); + } + } + + // ------------------------------------------------------------------ event-level acceptance + template + bool acceptCollision(TCollision const& collision) + { + if (!collision.sel8()) { + return false; + } + if (!passOutlier(collision)) { + return false; + } + return true; + } + + // ------------------------------------------------------------------ correlation filling + // Hadron-hadron (MFT-MFT). tracks1 = trigger collection, tracks2 = associated collection. + // Same-event: called as fillCorrelations(..., tracks, tracks, ...) -> self-correlation with index check. + // Mixed-event: called as fillCorrelations(..., tracks1, tracks2, ...) from two different collisions. + + template + void fillCorrelations(TTarget target, TTracks1 const& tracks1, TTracks2 const& tracks2, float multiplicity, bool mixing, float posZ, float eventWeight) + { + // ---------------------------------------------------------------- per-call caches + // Indexed by plain position in the loop (NOT filteredIndex()), and built for BOTH + // collections. This is what makes the mixed event correct: tracks1 and tracks2 have the same + // C++ type in SameKindPair, so a type-based "sameCollection" is true in ME as well. + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + efficiencyAssociatedCache.clear(); + if (cfg.mEfficiencyAssociated) { + efficiencyAssociatedCache.reserve(tracks2.size()); + for (const auto& track : tracks2) { + efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), multiplicity, posZ, track.phi())); + } + } + } + + phiWeightCache.clear(); // associated side (tracks2) + phiWeightCache1.clear(); // trigger side (tracks1) + if (cfg.mPhiWeight) { + phiWeightCache.reserve(tracks2.size()); + for (const auto& track : tracks2) { + phiWeightCache.push_back(getPhiWeight(cfg.mPhiWeight, track.phi())); + } + phiWeightCache1.reserve(tracks1.size()); + for (const auto& track : tracks1) { + phiWeightCache1.push_back(getPhiWeight(cfg.mPhiWeight, track.phi())); + } + } + + // ---------------------------------------------------------------- trigger loop + int i1 = -1; + for (const auto& track1 : tracks1) { + ++i1; // must stay before any 'continue' + if (track1.nClusters() < cfgMFTMinNClusters) { + continue; + } + + if constexpr (std::experimental::is_detected::value) { + if constexpr (std::experimental::is_detected::value) { + if (!track1.isPhysicalPrimary()) + continue; + } + } else if constexpr (std::experimental::is_detected::value) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyTrigger) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), multiplicity, posZ, track1.phi()); + } + } + // phi weight of the trigger: applied BEFORE the trigger histogram is filled, so the + // per-trigger normalisation and the pair weights stay consistent. + if (cfg.mPhiWeight) { + const float w1 = phiWeightCache1[i1]; + if (w1 > 0.f) { + triggerWeight *= 1.f / w1; + } + } + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + + // ---------------------------------------------------------------- associated loop + int i2 = -1; + for (const auto& track2 : tracks2) { + ++i2; // must stay before any 'continue' + + if (!mixing && track1.globalIndex() == track2.globalIndex()) { + continue; // no self-correlation (same event only) + } + if (track2.nClusters() < cfgMFTMinNClusters) { + continue; + } + + if constexpr (std::experimental::is_detected::value) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.sign() < 0) + continue; + } + if constexpr (std::experimental::is_detected::value && + std::experimental::is_detected::value) { + if (cfgPairCharge != 0 && cfgPairCharge * track1.sign() * track2.sign() < 0) + continue; + } + + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + float associatedWeight = triggerWeight; // already contains event, trigger eff, trigger phi weight + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + associatedWeight *= efficiencyAssociatedCache[i2]; + } + } + if (cfg.mPhiWeight) { + const float w2 = phiWeightCache[i2]; + if (w2 > 0.f) { + associatedWeight *= 1.f / w2; + } + } + + const float deltaEta = track1.eta() - track2.eta(); + const float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); + + if (mixing) { + registry.fill(HIST("mftmft/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("mftmft/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + // FIX #2 + FIX #6d: MFT-Global. Trigger = central-barrel track (cfg.mEfficiencyGlobal), + // associated = MFT track (cfg.mEfficiencyAssociated). + template + void fillCorrMftGlobal(TTarget target, TTriggers const& triggers, TMFTs const& mftTracks, bool mixing, float posZ, float multiplicity, float eventWeight) + { + constexpr bool TriggerHasSign = std::experimental::is_detected::value; + constexpr bool AssocHasSign = std::experimental::is_detected::value; + + if (!mixing) { + for (const auto& assocTrack : mftTracks) { + if (assocTrack.nClusters() < cfgMFTMinNClusters) { + continue; + } + registry.fill(HIST("mftGlobal/SE/Assoc_etavsphi"), assocTrack.phi(), assocTrack.eta()); + registry.fill(HIST("mftGlobal/SE/Assoc_pt"), assocTrack.pt()); + } + } + + for (const auto& triggerTrack : triggers) { + if constexpr (TriggerHasSign) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * triggerTrack.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, triggerTrack.eta(), triggerTrack.pt(), multiplicity, posZ, triggerTrack.phi()); + } + } + if (!mixing) { + registry.fill(HIST("mftGlobal/SE/Trig_etavsphi"), triggerTrack.phi(), triggerTrack.eta()); + registry.fill(HIST("mftGlobal/SE/Trig_pt"), triggerTrack.pt()); + } + target->getTriggerHist()->Fill(step, triggerTrack.pt(), multiplicity, posZ, triggerWeight); + + for (const auto& assocTrack : mftTracks) { + if (assocTrack.nClusters() < cfgMFTMinNClusters) { + continue; + } + if constexpr (AssocHasSign) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * assocTrack.sign() < 0) + continue; + } + if constexpr (TriggerHasSign && AssocHasSign) { + if (cfgPairCharge != 0 && cfgPairCharge * triggerTrack.sign() * assocTrack.sign() < 0) + continue; + } + float associatedWeight = triggerWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + associatedWeight *= getEfficiencyCorrection(cfg.mEfficiencyAssociated, assocTrack.eta(), assocTrack.pt(), multiplicity, posZ, assocTrack.phi()); + } + } + + float deltaPhi = RecoDecay::constrainAngle(triggerTrack.phi() - assocTrack.phi(), -o2::constants::math::PIHalf); + float deltaEta = triggerTrack.eta() - assocTrack.eta(); + if (mixing) { + registry.fill(HIST("mftGlobal/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("mftGlobal/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + target->getPairHist()->Fill(step, deltaEta, assocTrack.pt(), triggerTrack.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + + // FIX #4 + FIX #6d: Global-Global (central-barrel hadron-hadron). Both tracks use cfg.mEfficiencyGlobal. + // No nClusters() cut: global tracks are quality-selected via FilteredGlobalTracks. + template + void fillCorrGlobalGlobal(TTarget target, TTracks1 const& tracks1, TTracks2 const& tracks2, bool mixing, float posZ, float multiplicity, float eventWeight) + { + constexpr bool Track1HasSign = std::experimental::is_detected::value; + constexpr bool Track2HasSign = std::experimental::is_detected::value; + + for (const auto& track1 : tracks1) { + if constexpr (Track1HasSign) { + if (cfgTriggerCharge != 0 && cfgTriggerCharge * track1.sign() < 0) + continue; + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, track1.eta(), track1.pt(), multiplicity, posZ, track1.phi()); + } + } + if (!mixing) { + registry.fill(HIST("global/SE/etavsphi"), track1.phi(), track1.eta()); + registry.fill(HIST("global/SE/pt"), track1.pt()); + } + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + + for (const auto& track2 : tracks2) { + if constexpr (std::is_same::value) { + if (track1.globalIndex() == track2.globalIndex()) { + continue; // no self-correlation + } + } + + if constexpr (Track2HasSign) { + if (cfgAssociatedCharge != 0 && cfgAssociatedCharge * track2.sign() < 0) + continue; + } + if constexpr (Track1HasSign && Track2HasSign) { + if (cfgPairCharge != 0 && cfgPairCharge * track1.sign() * track2.sign() < 0) + continue; + } + if (cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + float associatedWeight = triggerWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyGlobal) { + associatedWeight *= getEfficiencyCorrection(cfg.mEfficiencyGlobal, track2.eta(), track2.pt(), multiplicity, posZ, track2.phi()); + } + } + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); + float deltaEta = track1.eta() - track2.eta(); + if (mixing) { + registry.fill(HIST("global/ME/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } else { + registry.fill(HIST("global/SE/deltaEta_deltaPhi"), deltaPhi, deltaEta); + } + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + + // ------------------------------------------------------------------ SAME EVENT (data) + void processSameMFT(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const&, FilteredMFTTracks const& tracks) + { + registry.fill(HIST("multiplicity_raw"), collision.centFT0C()); // unconditional, no cuts + if (!acceptCollision(collision)) { + return; + } + + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using BinningType = ColumnBinningPolicy; + BinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + + if (cfgVerbosity > 0) { + LOGF(info, "processSameMFT: %d MFT tracks | Vertex: %.2f | Mult: %.2f", tracks.size(), collision.posZ(), multiplicity); + } + + registry.fill(HIST("eventcount_same"), bin); + registry.fill(HIST("trackcount_same"), bin, tracks.size()); + fillQA(collision, multiplicity, tracks); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + same->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + same->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelations(same, tracks, tracks, multiplicity, false, collision.posZ(), 1.0f); + } + if (hasEfficiency) { + same->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrelations(same, tracks, tracks, multiplicity, false, collision.posZ(), 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processSameMFT, "Process same event MFT-MFT correlations", true); + + void processMftGlobalSE(FilteredCollisions::iterator const& collision, FilteredGlobalTracks const& globalTracks, FilteredMFTTracks const& mftTracks, aod::BCsWithTimestamps const&) + { + if (!acceptCollision(collision)) { + return; + } + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using MixingBinningType = ColumnBinningPolicy; + MixingBinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + registry.fill(HIST("eventcount_mftglobal_same"), bin); + registry.fill(HIST("trackcount_mftglobal_same"), bin, globalTracks.size(), mftTracks.size()); + + // FIX #6d: trigger = global track, associated = MFT track + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrMftGlobal(sameMftGlobal, globalTracks, mftTracks, false, collision.posZ(), multiplicity, 1.0f); + } + if (hasEfficiency) { + sameMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrMftGlobal(sameMftGlobal, globalTracks, mftTracks, false, collision.posZ(), multiplicity, 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processMftGlobalSE, "Process same event MFT-vs-global-track correlations", false); + + // FIX #4: Same-event global-global (central-barrel hadron-hadron) correlations. + void processGlobalGlobalSE(FilteredCollisions::iterator const& collision, FilteredGlobalTracks const& tracks, aod::BCsWithTimestamps const&) + { + if (!acceptCollision(collision)) { + return; + } + loadEfficiency(collision.template bc_as().timestamp()); + const float multiplicity = getMultiplicity(collision); + + using MixingBinningType = ColumnBinningPolicy; + MixingBinningType binning{{axisVertex, axisMultiplicity}, true}; + int bin = binning.getBin({collision.posZ(), multiplicity}); + + if (cfgVerbosity > 0) { + LOGF(info, "processGlobalGlobalSE: %d global tracks | Vertex: %.2f | Mult: %.2f", tracks.size(), collision.posZ(), multiplicity); + } + + registry.fill(HIST("eventcount_global_same"), bin); + registry.fill(HIST("trackcount_global_same"), bin, tracks.size()); + + // FIX #6d + const bool hasEfficiency = (cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrGlobalGlobal(sameGlobal, tracks, tracks, false, collision.posZ(), multiplicity, 1.0f); + } + if (hasEfficiency) { + sameGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrGlobalGlobal(sameGlobal, tracks, tracks, false, collision.posZ(), multiplicity, 1.0f); + } + } + PROCESS_SWITCH(Mftcorrelations, processGlobalGlobalSE, "Process same event global-global (central-barrel) correlations", false); + + void processSameMFTMultSet(FilteredCollisions::iterator const& collision, aod::BCsWithTimestamps const& bcs, FilteredMFTTracks const& tracks) + { + if (cfgMultCorrelationsMask == 0) { + LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when the MultSet process function is in use."); + } + processSameMFT(collision, bcs, tracks); + } + PROCESS_SWITCH(Mftcorrelations, processSameMFTMultSet, "Process same event MFT-MFT correlations with multi-estimator QA", false); + + // ------------------------------------------------------------------ MIXED EVENT (data) + void processMixedMFT(FilteredCollisions const& collisions, FilteredMFTTracks const& tracks, aod::BCsWithTimestamps const&) + { + // Bin mixed events on the SAME multiplicity/centrality estimator that getMultiplicity() returns + // (driven by cfgMultCorrelationsMask), instead of a hardcoded CentFT0C column. This keeps the + // mixing pool synchronized with whatever estimator is actually filled into the CorrelationContainer, + // and lets you switch estimators (e.g. FT0C -> FT0M) purely from the JSON config. + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using BinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + BinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(tracks); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + + const float multiplicity = getMultiplicity(collision1); + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + if (cfgVerbosity > 0) { + LOGF(info, "processMixedMFT: bin %d | pair (%d,%d) | Vertex %.2f/%.2f | Mult %.2f", bin, collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), multiplicity); + } + + if (it.isNewWindow()) { + loadEfficiency(collision1.template bc_as().timestamp()); + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + } + + registry.fill(HIST("eventcount_mixed"), bin); + registry.fill(HIST("trackcount_mixed"), bin, tracks1.size(), tracks2.size()); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + if (it.isNewWindow() && fillReco) { + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (it.isNewWindow() && hasEfficiency) { + mixed->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + + if (fillReco) { + fillCorrelations(mixed, tracks1, tracks2, multiplicity, true, collision1.posZ(), eventWeight); + } + if (hasEfficiency) { + fillCorrelations(mixed, tracks1, tracks2, multiplicity, true, collision1.posZ(), eventWeight); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMixedMFT, "Process mixed event MFT-MFT correlations", false); + + void processMftGlobalME(FilteredCollisions const& collisions, FilteredGlobalTracks const& globalTracks, FilteredMFTTracks const& mftTracks, aod::BCsWithTimestamps const&) + { + // Same fix as processMixedMFT: bin on getMultiplicity() (driven by cfgMultCorrelationsMask) + // instead of a hardcoded CentFT0C column, so the mixing pool matches whatever estimator is + // actually filled into the CorrelationContainer. + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using MixingBinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + MixingBinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(globalTracks, mftTracks); + Pair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + int pairCounter = 0; + int acceptedPairCounter = 0; + int newWindowCounter = 0; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, globalTracks1, collision2, mftTracks2] = *it; + pairCounter++; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + acceptedPairCounter++; + + const float multiplicity = getMultiplicity(collision1); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + + if (it.isNewWindow()) { + newWindowCounter++; + loadEfficiency(collision1.template bc_as().timestamp()); + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (hasEfficiency) { + mixedMftGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + registry.fill(HIST("eventcount_mftglobal_mixed"), bin); + } + registry.fill(HIST("trackcount_mftglobal_mixed"), bin, globalTracks1.size(), mftTracks2.size()); + + if (fillReco) { + fillCorrMftGlobal(mixedMftGlobal, globalTracks1, mftTracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + if (hasEfficiency) { + fillCorrMftGlobal(mixedMftGlobal, globalTracks1, mftTracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + } + + if (cfgVerbosity > 0) { + LOGF(info, "processMftGlobalME: collisions %d | pairs iterated %d | accepted %d | new windows %d", + collisions.size(), pairCounter, acceptedPairCounter, newWindowCounter); + } + } + PROCESS_SWITCH(Mftcorrelations, processMftGlobalME, "Process mixed event MFT-vs-global-track correlations", false); + + // FIX #4: Mixed-event global-global (central-barrel hadron-hadron) correlations. + void processGlobalGlobalME(FilteredCollisions const& collisions, FilteredGlobalTracks const& tracks, aod::BCsWithTimestamps const&) + { + auto getMult = [this](auto const& c) { return getMultiplicity(c); }; + using MixingBinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMult)>; + MixingBinningType binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + + auto tracksTuple = std::make_tuple(tracks); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!acceptCollision(collision1) || !collision2.sel8()) { + continue; + } + + const float multiplicity = getMultiplicity(collision1); + int bin = binning.getBin(std::tuple(collision1.posZ(), multiplicity)); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + if (cfgVerbosity > 0) { + LOGF(info, "processGlobalGlobalME: bin %d | pair (%d,%d) | Vertex %.2f/%.2f | Mult %.2f", bin, collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), multiplicity); + } + + const bool hasEfficiency = (cfg.mEfficiencyGlobal != nullptr); + const bool fillReco = !(cfgDropStepRECO && hasEfficiency); + + if (it.isNewWindow()) { + loadEfficiency(collision1.template bc_as().timestamp()); + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + if (fillReco) { + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + } + if (hasEfficiency) { + mixedGlobal->fillEvent(multiplicity, CorrelationContainer::kCFStepCorrected); + } + registry.fill(HIST("eventcount_global_mixed"), bin); + } + registry.fill(HIST("trackcount_global_mixed"), bin, tracks1.size(), tracks2.size()); + + if (fillReco) { + fillCorrGlobalGlobal(mixedGlobal, tracks1, tracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + if (hasEfficiency) { + fillCorrGlobalGlobal(mixedGlobal, tracks1, tracks2, true, collision1.posZ(), multiplicity, eventWeight); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processGlobalGlobalME, "Process mixed event global-global (central-barrel) correlations", false); + + void processMixedMFTMultSet(FilteredCollisions const& collisions, FilteredMFTTracks const& tracks, aod::BCsWithTimestamps const& bcs) + { + if (cfgMultCorrelationsMask == 0) { + LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when the MultSet process function is in use."); + } + processMixedMFT(collisions, tracks, bcs); + } + PROCESS_SWITCH(Mftcorrelations, processMixedMFTMultSet, "Process mixed event MFT-MFT correlations with multi-estimator QA", false); + + template + bool isChargedPrimary(TParticle const& p) + { + if (!p.isPhysicalPrimary()) { + return false; + } + auto* entry = pdg->GetParticle(p.pdgCode()); + return entry != nullptr && entry->Charge() != 0; + } + + // ------------------------------------------------------------------ MC efficiency extraction + Preslice perCollisionMFT = aod::fwdtrack::collisionId; + + void processMCEfficiencyMFT(aod::McCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + McEffCollisions const& collisions, + McEffMFTTracks const& tracks) + { + registry.fill(HIST("MCEff/hNRecoColl"), collisions.size()); + registry.fill(HIST("MCEff/hCutFlow"), 1); + if (collisions.size() != 1) { + return; + } + registry.fill(HIST("MCEff/hCutFlow"), 2); + if (cfgVerbosity > 0) { + LOGF(info, "processMCEfficiencyMFT: ENTER | mcCollision=%lld | nMcParticles=%d | nCollisions=%d | nTracks=%d", + mcCollision.globalIndex(), mcParticles.size(), collisions.size(), tracks.size()); + } + float multiplicity = -1; + bool passSel8 = false; + int64_t collGlobalIndex = -1; + for (const auto& collision : collisions) { + if (!collision.sel8()) { + continue; + } + registry.fill(HIST("MCEff/hCutFlow"), 3); + passSel8 = true; + multiplicity = getMultiplicity(collision); // driven by cfgMultCorrelationsMask + collGlobalIndex = collision.globalIndex(); + } + if (!passSel8) { + return; + } + + for (const auto& mcParticle : mcParticles) { + if (!isChargedPrimary(mcParticle) || mcParticle.eta() < cfgMFTCutEtaMin || mcParticle.eta() > cfgMFTCutEtaMax || mcParticle.pt() < cfgMFTCutPtMin || mcParticle.pt() > cfgMFTCutPtMax) { + continue; + } + same->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + + auto groupedTracks = tracks.sliceBy(perCollisionMFT, collGlobalIndex); + + std::unordered_set countedPrimaries; + std::unordered_set countedAll; + + for (const auto& track : groupedTracks) { + if (track.nClusters() < cfgMFTMinNClusters) { + continue; + } + if (track.has_mcParticle()) { + const auto& mcParticle = track.mcParticle(); + const int64_t idx = mcParticle.globalIndex(); + + if (mcParticle.isPhysicalPrimary() && countedPrimaries.insert(idx).second) { + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + if (countedAll.insert(idx).second) { + same->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), wrapPhi(mcParticle.phi())); + } + } else { + // FIX #6e: wrap phi to [0,2pi) like the MC-particle phi used for the other steps + same->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, + track.eta(), track.pt(), 0, multiplicity, mcCollision.posZ(), + RecoDecay::constrainAngle(track.phi(), 0.f)); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMCEfficiencyMFT, "MC: Extract MFT tracking efficiency", false); + + //-------------------------------------------------------------------MC Efficiency Extraction for Global + + Preslice perCollisionGlobal = aod::track::collisionId; + + void processMCEfficiencyGlobal(aod::McCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + McEffCollisions const& collisions, + McEffGlobalTracks const& tracks) + { + registry.fill(HIST("MCEffGlobal/hNRecoColl"), collisions.size()); + registry.fill(HIST("MCEffGlobal/hCutFlow"), 1); + if (collisions.size() != 1) { // exactly one reconstructed collision per MC collision (as in the MFT efficiency) + return; + } + registry.fill(HIST("MCEffGlobal/hCutFlow"), 2); + if (cfgVerbosity > 0) { + LOGF(info, "processMCEfficiencyGlobal: mcCollision=%lld | nMcParticles=%d | nCollisions=%d | nTracks=%d", + mcCollision.globalIndex(), mcParticles.size(), collisions.size(), tracks.size()); + } + + float multiplicity = -1; + bool passSel8 = false; + int64_t collGlobalIndex = -1; + for (const auto& collision : collisions) { + if (!collision.sel8()) { + continue; + } + registry.fill(HIST("MCEffGlobal/hCutFlow"), 3); + passSel8 = true; + multiplicity = getMultiplicity(collision); // same estimator as in data (cfgMultCorrelationsMask) + collGlobalIndex = collision.globalIndex(); + } + if (!passSel8) { + return; + } + + // Generated: charged physical primaries in the global-track acceptance + for (const auto& mcParticle : mcParticles) { + if (!isChargedPrimary(mcParticle) || + std::abs(mcParticle.eta()) >= cfgGlobalTrackEtaCut || + mcParticle.pt() < cfgGlobalTrackPtMin || mcParticle.pt() > cfgGlobalTrackPtMax) { + continue; + } + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::MC, mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + + // Reconstructed: global tracks of the selected reco collision (globalTrackFilter already applied) + auto groupedTracks = tracks.sliceBy(perCollisionGlobal, collGlobalIndex); + + std::unordered_set countedPrimaries; // count each MC particle once (split tracks) + std::unordered_set countedAll; + + for (const auto& track : groupedTracks) { + if (track.has_mcParticle()) { + const auto& mcParticle = track.mcParticle(); + const int64_t idx = mcParticle.globalIndex(); + + if (mcParticle.isPhysicalPrimary() && countedPrimaries.insert(idx).second) { + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoPrimaries, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + if (countedAll.insert(idx).second) { + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::RecoAll, + mcParticle.eta(), mcParticle.pt(), 0, multiplicity, mcCollision.posZ(), mcParticle.phi()); + } + } else { + // fake track + sameGlobal->getTrackHistEfficiency()->Fill(CorrelationContainer::Fake, + track.eta(), track.pt(), 0, multiplicity, mcCollision.posZ(), + RecoDecay::constrainAngle(track.phi(), 0.f)); + } + } + } + PROCESS_SWITCH(Mftcorrelations, processMCEfficiencyGlobal, "MC: Extract central-barrel (global track) tracking efficiency", false); + + // ------------------------------------------------------------------ MC generator-level correlations (truth reference / closure test) + using McRecoCollisions = soa::Join; + + // -1, 0, +1 from the PDG database charge (0 = neutral or unknown code) + template + int getMcCharge(TParticle const& p) + { + auto* entry = pdg->GetParticle(p.pdgCode()); + if (entry == nullptr) { + return 0; + } + const double q = entry->Charge(); + return (q > 0) - (q < 0); + } + + // cheap cuts first, PDG lookup last + template + bool acceptMftTruth(TParticle const& p) + { + return p.isPhysicalPrimary() && p.eta() > cfgMFTCutEtaMin && p.eta() < cfgMFTCutEtaMax && + p.pt() > cfgMFTCutPtMin && p.pt() < cfgMFTCutPtMax && getMcCharge(p) != 0; + } + + template + bool acceptGlobalTruth(TParticle const& p) + { + return p.isPhysicalPrimary() && std::abs(p.eta()) < cfgGlobalTrackEtaCut && + p.pt() > cfgGlobalTrackPtMin && p.pt() < cfgGlobalTrackPtMax && getMcCharge(p) != 0; + } + + template + std::vector collectAccepted(TParticles const& particles, TSelector accept) + { + std::vector idx; + idx.reserve(64); + for (const auto& p : particles) { + if (accept(p)) { + idx.push_back(p.globalIndex()); + } + } + return idx; + } + + template + void fillTruthPairs(TTarget& target, TParticles1 const& particles1, TParticles2 const& particles2, + std::vector const& acc1, std::vector const& acc2, + float multiplicity, float posZ, float weight, bool sameCollection) + { + for (const auto& i1 : acc1) { + const auto& p1 = particles1.iteratorAt(i1 - particles1.begin().globalIndex()); + target->getTriggerHist()->Fill(CorrelationContainer::kCFStepAll, p1.pt(), multiplicity, posZ, weight); + for (const auto& i2 : acc2) { + if (sameCollection && i1 == i2) { + continue; + } + const auto& p2 = particles2.iteratorAt(i2 - particles2.begin().globalIndex()); + if (cfgPtOrder != 0 && p2.pt() >= p1.pt()) { + continue; + } + float deltaPhi = RecoDecay::constrainAngle(p1.phi() - p2.phi(), -o2::constants::math::PIHalf); + target->getPairHist()->Fill(CorrelationContainer::kCFStepAll, p1.eta() - p2.eta(), p2.pt(), p1.pt(), multiplicity, deltaPhi, posZ, weight); + } + } + } + + // ---------------- same event + template + void fillTruthSame(TTarget& target, aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, TSelector accept) + { + if (collisions.size() != 1) { // same reco-collision requirement as processMCEfficiencyMFT + return; + } + auto collision = collisions.begin(); + if (!collision.sel8()) { + return; + } + const float multiplicity = getMultiplicity(collision); + const auto acc = collectAccepted(mcParticles, accept); + + target->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + fillTruthPairs(target, mcParticles, mcParticles, acc, acc, multiplicity, mcCollision.posZ(), 1.0f, true); + } + + void processMCSameMFT(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + fillTruthSame(same, mcCollision, mcParticles, collisions, [this](auto const& p) { return acceptMftTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCSameMFT, "Process MC-truth same event MFT-acceptance correlations (charged primaries)", false); + + void processMCSameGlobal(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + fillTruthSame(sameGlobal, mcCollision, mcParticles, collisions, [this](auto const& p) { return acceptGlobalTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCSameGlobal, "Process MC-truth same event global-acceptance correlations (charged primaries)", false); + + // ---------------- mixed event + PresliceUnsorted perMcCollision = aod::mccollisionlabel::mcCollisionId; + + template + void fillTruthMixed(TTarget& target, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, + McRecoCollisions const& collisions, TSelector accept) + { + // multiplicity of an MC collision = that of its single, sel8-passing reco collision; otherwise -1 (skipped by the binning) + auto getMult = [&collisions, this](auto const& mcCol) { + auto grouped = collisions.sliceBy(perMcCollision, mcCol.globalIndex()); + if (grouped.size() != 1) { + return -1.0f; + } + auto c = grouped.begin(); + if (!c.sel8()) { + return -1.0f; + } + return static_cast(getMultiplicity(c)); + }; + + using BinningTypeMC = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMult)>; + BinningTypeMC binning{{getMult}, {axisVertex, axisMultiplicity}, true}; + auto tuple = std::make_tuple(mcParticles); + SameKindPair pairs{binning, cfgNoMixedEvents, -1, mcCollisions, tuple, &cache}; + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [mcCollision1, particles1, mcCollision2, particles2] = *it; + const float eventWeight = 1.0f / it.currentWindowNeighbours(); + const float multiplicity = getMult(mcCollision1); + + if (it.isNewWindow()) { + target->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + } + const auto acc1 = collectAccepted(particles1, accept); + const auto acc2 = collectAccepted(particles2, accept); // once per pair, not once per trigger + fillTruthPairs(target, particles1, particles2, acc1, acc2, multiplicity, mcCollision1.posZ(), eventWeight, false); + } + } + + void processMCMixedMFT(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, McRecoCollisions const& collisions) + { + fillTruthMixed(mixed, mcCollisions, mcParticles, collisions, [this](auto const& p) { return acceptMftTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCMixedMFT, "Process MC-truth mixed event MFT-acceptance correlations (charged primaries)", false); + + void processMCMixedGlobal(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, McRecoCollisions const& collisions) + { + fillTruthMixed(mixedGlobal, mcCollisions, mcParticles, collisions, [this](auto const& p) { return acceptGlobalTruth(p); }); + } + PROCESS_SWITCH(Mftcorrelations, processMCMixedGlobal, "Process MC-truth mixed event global-acceptance correlations (charged primaries)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/neutronProtonCorrZdc.cxx b/PWGCF/TwoParticleCorrelations/Tasks/neutronProtonCorrZdc.cxx index 30ea2b53197..b7c6e137bd6 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/neutronProtonCorrZdc.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/neutronProtonCorrZdc.cxx @@ -203,7 +203,7 @@ struct NeutronProtonCorrZdc { } template - bool eventSelected(TCollision coll, const float centrality) + bool eventSelected(const TCollision& coll, const float centrality) { if (!coll.sel8()) return 0; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx b/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx index dca0592bd91..640352fff2f 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/nucleibalance.cxx @@ -71,10 +71,11 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace constants::math; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(name, type, defaultValue, help) \ + Configurable name{#name, (defaultValue), (help)}; // NOLINT(bugprone-macro-parentheses) static constexpr float PairCutOff = -1.f; -static constexpr float CfgPairCutDefaults[1][5] = {{PairCutOff, PairCutOff, PairCutOff, PairCutOff, PairCutOff}}; +static constexpr std::array, 1> CfgPairCutDefaults{{{PairCutOff, PairCutOff, PairCutOff, PairCutOff, PairCutOff}}}; struct Nucleibalance { SliceCache cache; @@ -86,13 +87,14 @@ struct Nucleibalance { O2_DEFINE_CONFIGURABLE(cfgCutMCPt, float, 0.5f, "Minimal pT for MC particles (AO2D-MC mode)") O2_DEFINE_CONFIGURABLE(cfgCutMCEta, float, 0.8f, "Eta range for MC particles (AO2D-MC mode)") O2_DEFINE_CONFIGURABLE(cfgUseFT0M, int, 1, "Use FT0M centrality (0-100) as multiplicity axis (1=ON, 0=use Ntracks/multiplicity())") - // Track-quality options (AO2D mode). Default selection corresponds to global tracks. - O2_DEFINE_CONFIGURABLE(cfgTPCNClsMin, int, 70, "Minimum number of TPC clusters (tpcNClsFound) in AO2D mode") - O2_DEFINE_CONFIGURABLE(cfgDcaXYMax, float, 0.1f, "Max |DCA_{xy}| to PV (cm) in AO2D mode") - O2_DEFINE_CONFIGURABLE(cfgDcaZMax, float, 0.2f, "Max |DCA_{z}| to PV (cm) in AO2D mode") - O2_DEFINE_CONFIGURABLE(chi2pertpccluster, float, 2.5f, "Maximum Chi2/cluster for the TPC track segment in AO2D mode") - O2_DEFINE_CONFIGURABLE(chi2peritscluster, float, 36.f, "Maximum Chi2/cluster for the ITS track segment in AO2D mode") - O2_DEFINE_CONFIGURABLE(itsnclusters, int, 5, "Minimum number of ITS clusters in AO2D mode") + // Track-quality options. Default selection corresponds to global tracks. + O2_DEFINE_CONFIGURABLE(cfgTPCNClsMin, int, 70, "Minimum number of TPC clusters (tpcNClsFound)") + O2_DEFINE_CONFIGURABLE(cfgDcaXYMax, float, 0.1f, "Max |DCA_{xy}| to PV (cm)") + O2_DEFINE_CONFIGURABLE(cfgDcaZMax, float, 0.2f, "Max |DCA_{z}| to PV (cm)") + O2_DEFINE_CONFIGURABLE(cfgRequirePrimaryTrack, int, 1, "Require isPrimaryTrack()= true (1=ON, 0=OFF)") + O2_DEFINE_CONFIGURABLE(chi2pertpccluster, float, 2.5f, "Maximum Chi2/cluster for the TPC track segment") + O2_DEFINE_CONFIGURABLE(chi2peritscluster, float, 36.f, "Maximum Chi2/cluster for the ITS track segment") + O2_DEFINE_CONFIGURABLE(itsnclusters, int, 1, "Minimum number of ITS clusters") O2_DEFINE_CONFIGURABLE(cfgPtOrder, int, 1, "Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)"); O2_DEFINE_CONFIGURABLE(cfgTriggerCharge, int, 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative"); @@ -124,7 +126,7 @@ struct Nucleibalance { O2_DEFINE_CONFIGURABLE(cfgAssociatedSpecies, int, 2, "Associated species for BF: 0 = #pi, 1 = K, 2 = p, 3 = d, -1 = all charged tracks"); // Suggested values: Photon: 0.004; K0 and Lambda: 0.005 - Configurable> cfgPairCut{"cfgPairCut", {CfgPairCutDefaults[0], 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; + Configurable> cfgPairCut{"cfgPairCut", {CfgPairCutDefaults[0].data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; O2_DEFINE_CONFIGURABLE(cfgEfficiencyTrigger, std::string, "", "CCDB path to efficiency object for trigger particles") O2_DEFINE_CONFIGURABLE(cfgEfficiencyAssociated, std::string, "", "CCDB path to efficiency object for associated particles") @@ -133,7 +135,7 @@ struct Nucleibalance { O2_DEFINE_CONFIGURABLE(cfgVerbosity, int, 1, "Verbosity level (0 = major, 1 = per collision)") - O2_DEFINE_CONFIGURABLE(cfgMcTriggerPDGs, std::vector, {}, "MC PDG codes to use exclusively as trigger particles and exclude from associated particles. Empty = no selection.") + O2_DEFINE_CONFIGURABLE(cfgMcTriggerPDGs, std::vector, std::vector{}, "MC PDG codes to use exclusively as trigger particles and exclude from associated particles. Empty = no selection.") ConfigurableAxis axisVertex{"axisVertex", {7, -7, 7}, "vertex axis for histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; @@ -171,14 +173,17 @@ struct Nucleibalance { { if (cfgTrigger.value == TriggerNone) { return true; - } else if (cfgTrigger.value == TriggerSel8) { + } + if (cfgTrigger.value == TriggerSel8) { return collision.sel8(); - } else if (cfgTrigger.value == TriggerSel8Quality) { + } + if (cfgTrigger.value == TriggerSel8Quality) { return collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); - } else if (cfgTrigger.value == TriggerSel8OccQuality) { + } + if (cfgTrigger.value == TriggerSel8OccQuality) { const int occupancy = collision.trackOccupancyInTimeRange(); if (occupancy < cfgMinOcc.value || occupancy >= cfgMaxOcc.value) { return false; @@ -188,7 +193,8 @@ struct Nucleibalance { collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) && collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); - } else if (cfgTrigger.value == TriggerSel8NoSbpZvtx) { + } + if (cfgTrigger.value == TriggerSel8NoSbpZvtx) { return collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); @@ -269,7 +275,7 @@ struct Nucleibalance { std::vector efficiencyAssociatedCache; std::unique_ptr multCutFormula; - std::array multCutFormulaParamIndex; + std::array multCutFormulaParamIndex{}; struct Config { bool mPairCuts = false; @@ -281,7 +287,7 @@ struct Nucleibalance { HistogramRegistry registry{"registry"}; PairCuts mPairCuts; - Service ccdb; + Service ccdb{}; // AO2D-based tracks with PID for pi / K / p / d using TracksPID = soa::Join 0 && trk.itsNCls() < itsnclusters.value) { @@ -384,15 +399,20 @@ struct Nucleibalance { } struct SimpleTrack { - float eta; - float phi; - float pt; - int charge; + float eta{}; + float phi{}; + float pt{}; + int charge{}; }; + static SimpleTrack makeSimpleTrack(float eta, float phi, float pt, int charge) + { + return SimpleTrack{.eta = eta, .phi = phi, .pt = pt, .charge = charge}; + } + struct MixEventEntry { - float multiplicity; - float zvtx; + float multiplicity = 0.f; + float zvtx = 0.f; std::vector triggerTracks; std::vector associatedTracks; }; @@ -414,17 +434,22 @@ struct Nucleibalance { registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{100, 0, 100, "multiplicity/centrality"}, {100, -2, 2, "#eta"}, {200, 0, o2::constants::math::TwoPI, "#varphi"}}}); if (doprocessSameDerivedMultSet) { - if (cfgMultCorrelationsMask == 0) + if (cfgMultCorrelationsMask.value == 0u) { LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when MultSet process functions are in use."); + } std::vector multAxes; - if (cfgMultCorrelationsMask & aod::cfmultset::CentFT0C) + if ((cfgMultCorrelationsMask.value & aod::cfmultset::CentFT0C) != 0u) { multAxes.emplace_back(100, 0, 100, "FT0C centrality"); - if (cfgMultCorrelationsMask & aod::cfmultset::MultFV0A) + } + if ((cfgMultCorrelationsMask.value & aod::cfmultset::MultFV0A) != 0u) { multAxes.emplace_back(1000, 0, 100000, "V0A multiplicity"); - if (cfgMultCorrelationsMask & aod::cfmultset::MultNTracksPV) + } + if ((cfgMultCorrelationsMask.value & aod::cfmultset::MultNTracksPV) != 0u) { multAxes.emplace_back(100, 0, 1000, "Nch PV"); - if (cfgMultCorrelationsMask & aod::cfmultset::MultNTracksGlobal) + } + if ((cfgMultCorrelationsMask.value & aod::cfmultset::MultNTracksGlobal) != 0u) { multAxes.emplace_back(100, 0, 1000, "Nch Global"); + } registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); } registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{1000, 0, 100, "/multiplicity/centrality"}}}); @@ -492,8 +517,9 @@ struct Nucleibalance { same->setTrackEtaCut(cfgCutEta); mixed->setTrackEtaCut(cfgCutEta); - if (!cfgEfficiencyAssociated.value.empty()) + if (!cfgEfficiencyAssociated.value.empty()) { efficiencyAssociatedCache.reserve(512); + } // o2-ccdb-upload -p Users/jgrosseo/correlations/LHC15o -f /tmp/correction_2011_global.root -k correction @@ -688,41 +714,41 @@ struct Nucleibalance { if (cfg.efficiencyLoaded) { return; } - if (cfgEfficiencyTrigger.value.empty() == false) { + if (!cfgEfficiencyTrigger.value.empty()) { if (cfgLocalEfficiency > 0) { TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiencyTrigger.value.c_str(), "READ"); - cfg.mEfficiencyTrigger = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); + cfg.mEfficiencyTrigger = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); } else { cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgEfficiencyTrigger, timestamp); } if (cfg.mEfficiencyTrigger == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiencyTrigger.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for trigger particles from %s (%p)", cfgEfficiencyTrigger.value.c_str(), (void*)cfg.mEfficiencyTrigger); + LOGF(info, "Loaded efficiency histogram for trigger particles from %s (%p)", cfgEfficiencyTrigger.value.c_str(), static_cast(cfg.mEfficiencyTrigger)); } - if (cfgEfficiencyAssociated.value.empty() == false) { + if (!cfgEfficiencyAssociated.value.empty()) { if (cfgLocalEfficiency > 0) { TFile* fEfficiencyAssociated = TFile::Open(cfgEfficiencyAssociated.value.c_str(), "READ"); - cfg.mEfficiencyAssociated = reinterpret_cast(fEfficiencyAssociated->Get("ccdb_object")); + cfg.mEfficiencyAssociated = dynamic_cast(fEfficiencyAssociated->Get("ccdb_object")); } else { cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgEfficiencyAssociated, timestamp); } if (cfg.mEfficiencyAssociated == nullptr) { LOGF(fatal, "Could not load efficiency histogram for associated particles from %s", cfgEfficiencyAssociated.value.c_str()); } - LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), (void*)cfg.mEfficiencyAssociated); + LOGF(info, "Loaded efficiency histogram for associated particles from %s (%p)", cfgEfficiencyAssociated.value.c_str(), static_cast(cfg.mEfficiencyAssociated)); } cfg.efficiencyLoaded = true; } double getEfficiencyCorrection(THn* eff, float eta, float pt, float multiplicity, float posZ) { - int effVars[4]; + std::array effVars{}; effVars[0] = eff->GetAxis(0)->FindBin(eta); effVars[1] = eff->GetAxis(1)->FindBin(pt); effVars[2] = eff->GetAxis(2)->FindBin(multiplicity); effVars[3] = eff->GetAxis(3)->FindBin(posZ); - return eff->GetBinContent(effVars); + return eff->GetBinContent(effVars.data()); } template @@ -831,7 +857,7 @@ struct Nucleibalance { const auto multiplicity = eventMultiplicityFT0MOrFallback(collision, collision.multiplicity()); using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; int bin = configurableBinningDerived.getBin({collision.posZ(), multiplicity}); registry.fill(HIST("eventcount_same"), bin); registry.fill(HIST("trackcount_same"), bin, tracks.size()); @@ -853,13 +879,13 @@ struct Nucleibalance { if (passPIDForSpecies(trk, cfgTriggerSpecies.value)) { if (cfgTriggerCharge.value == 0 || trk.sign() == cfgTriggerCharge.value) { - triggerTracks.push_back(SimpleTrack{cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign()}); + triggerTracks.push_back(makeSimpleTrack(cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign())); } } if (passPIDForSpecies(trk, cfgAssociatedSpecies.value)) { if (cfgAssociatedCharge.value == 0 || trk.sign() == cfgAssociatedCharge.value) { - associatedTracks.push_back(SimpleTrack{cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign()}); + associatedTracks.push_back(makeSimpleTrack(cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign())); } } } @@ -942,7 +968,7 @@ struct Nucleibalance { } // Save for multiplicity / QA (keep charge even if neutral) - eventTracks.push_back(SimpleTrack{trk.eta(), trk.phi(), trk.pt(), static_cast(trk.sign())}); + eventTracks.push_back(makeSimpleTrack(trk.eta(), trk.phi(), trk.pt(), static_cast(trk.sign()))); if (trk.sign() == 0) { continue; @@ -951,14 +977,14 @@ struct Nucleibalance { // Trigger selection: PID + charge if (passPIDForSpecies(trk, cfgTriggerSpecies.value)) { if (cfgTriggerCharge.value == 0 || trk.sign() == cfgTriggerCharge.value) { - triggerTracks.push_back(SimpleTrack{trk.eta(), trk.phi(), trk.pt(), trk.sign()}); + triggerTracks.push_back(makeSimpleTrack(trk.eta(), trk.phi(), trk.pt(), trk.sign())); } } // Associated selection: PID + charge if (passPIDForSpecies(trk, cfgAssociatedSpecies.value)) { if (cfgAssociatedCharge.value == 0 || trk.sign() == cfgAssociatedCharge.value) { - associatedTracks.push_back(SimpleTrack{trk.eta(), trk.phi(), trk.pt(), trk.sign()}); + associatedTracks.push_back(makeSimpleTrack(trk.eta(), trk.phi(), trk.pt(), trk.sign())); } } } @@ -1098,7 +1124,7 @@ struct Nucleibalance { continue; } - eventTracks.push_back(SimpleTrack{trk.eta(), trk.phi(), trk.pt(), static_cast(trk.sign())}); + eventTracks.push_back(makeSimpleTrack(trk.eta(), trk.phi(), trk.pt(), static_cast(trk.sign()))); if (trk.sign() == 0) { continue; @@ -1106,12 +1132,12 @@ struct Nucleibalance { if (passPIDForSpecies(trk, cfgTriggerSpecies.value) && (cfgTriggerCharge.value == 0 || trk.sign() == cfgTriggerCharge.value)) { - triggerTracks.push_back(SimpleTrack{trk.eta(), trk.phi(), trk.pt(), trk.sign()}); + triggerTracks.push_back(makeSimpleTrack(trk.eta(), trk.phi(), trk.pt(), trk.sign())); } if (passPIDForSpecies(trk, cfgAssociatedSpecies.value) && (cfgAssociatedCharge.value == 0 || trk.sign() == cfgAssociatedCharge.value)) { - associatedTracks.push_back(SimpleTrack{trk.eta(), trk.phi(), trk.pt(), trk.sign()}); + associatedTracks.push_back(makeSimpleTrack(trk.eta(), trk.phi(), trk.pt(), trk.sign())); } } @@ -1230,7 +1256,7 @@ struct Nucleibalance { void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) { using BinningTypeDerived = ColumnBinningPolicy; - BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{axisVertex, axisMultiplicity}}; if (cfgVerbosity > 0) { LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), collision.multiplicity()); } @@ -1279,7 +1305,7 @@ struct Nucleibalance { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1343,7 +1369,7 @@ struct Nucleibalance { }; using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(std::forward(tracks)...); using TA = std::tuple_element<0, decltype(tracksTuple)>::type; @@ -1385,7 +1411,7 @@ struct Nucleibalance { } if (passPIDForSpecies(trk, cfgTriggerSpecies.value)) { if (cfgTriggerCharge.value == 0 || trk.sign() == cfgTriggerCharge.value) { - triggerTracks.push_back(SimpleTrack{cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign()}); + triggerTracks.push_back(makeSimpleTrack(cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign())); } } } @@ -1398,7 +1424,7 @@ struct Nucleibalance { } if (passPIDForSpecies(trk, cfgAssociatedSpecies.value)) { if (cfgAssociatedCharge.value == 0 || trk.sign() == cfgAssociatedCharge.value) { - associatedTracks.push_back(SimpleTrack{cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign()}); + associatedTracks.push_back(makeSimpleTrack(cftrk.eta(), cftrk.phi(), cftrk.pt(), trk.sign())); } } } @@ -1458,11 +1484,13 @@ struct Nucleibalance { case PdgProton: // proton case -PdgProton: return 2; + default: + break; } - if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), pdgCode) != cfgMcTriggerPDGs->end()) + if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), pdgCode) != cfgMcTriggerPDGs->end()) { return 4; - else - return 3; + } + return 3; } // NOTE SmallGroups includes soa::Filtered always @@ -1563,16 +1591,18 @@ struct Nucleibalance { bool useMCMultiplicity = (cfgCentBinsForMC == 0); auto getMultiplicity = [&collisions, &useMCMultiplicity, this](auto& col) { - if (useMCMultiplicity) + if (useMCMultiplicity) { return col.multiplicity(); + } auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, col.globalIndex()); - if (groupedCollisions.size() == 0) + if (groupedCollisions.size() == 0) { return -1.0f; + } return groupedCollisions.begin().multiplicity(); }; using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; - BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}, true}; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // Strictly upper categorised collisions, for cfgNoMixedEvents combinations per bin, skipping those in entry -1 auto tuple = std::make_tuple(std::forward(particles)...); @@ -1676,7 +1706,10 @@ struct Lambdastarproxy { static constexpr float ProxyMomentumScale = 0.5f; static constexpr float TofBetaMin = 0.01f; static constexpr float TofBetaMax = 1.2f; - static constexpr double Half = 0.5; + // PID strategy values + static constexpr int PidStrategyRectangular = 0; + static constexpr int PidStrategyCircularTPCAndTOF = 1; + static constexpr int PidStrategyNucleiDeuteronTPC = 2; // Basic configuration for event and track selection Configurable lstarCutVertex{"lstarCutVertex", float{CutVertexDefault}, "Accepted z-vertex range (cm)"}; Configurable lstarCutPtMin{"lstarCutPtMin", float{CutPtMinDefault}, "Minimal pT for tracks (GeV/c)"}; @@ -1696,10 +1729,44 @@ struct Lambdastarproxy { Configurable lstarCutNsigmaTOFKaon{"lstarCutNsigmaTOFKaon", float{NsigmaTOFDefault}, "|nSigma^{TOF}_{K}| cut"}; Configurable lstarCutNsigmaTPCDe{"lstarCutNsigmaTPCDe", float{NsigmaTPCDefault}, "|nSigma^{TPC}_{d}| cut"}; Configurable lstarCutNsigmaTOFDe{"lstarCutNsigmaTOFDe", float{NsigmaTOFDefault}, "|nSigma^{TOF}_{d}| cut"}; + Configurable lstarEnableTOFNsigmaCutDe{"lstarEnableTOFNsigmaCutDe", 0, "Enable deuteron-only TOF nSigma cut in PID strategy 2"}; + Configurable lstarProxyUseProtonPIDAsDeuteron{"lstarProxyUseProtonPIDAsDeuteron", 0, "Closure/control test: keep proxy candidates that pass the normal deuteron selection and are also proton-like, then build p_proxy = p_track/2"}; + // Optional deuteron-only TOF auxiliary selections. + // Defaults are OFF, so strategy 2 remains TPC nSigma only for deuterons. + Configurable lstarEnableBetaCutDe{"lstarEnableBetaCutDe", 0, "Enable deuteron-only TOF beta cut using beta() > lstarBetaCutDe"}; + Configurable lstarBetaCutDe{"lstarBetaCutDe", 0.4f, "Minimum TOF beta for deuteron-only beta cut"}; + Configurable lstarEnableExpSignalTOFDe{"lstarEnableExpSignalTOFDe", 0, "Enable deuteron-only TOF expected-signal-difference cut when lstarTOFExpSignalDiffDeMax > 0"}; + Configurable lstarTOFExpSignalDiffDeMax{"lstarTOFExpSignalDiffDeMax", -1.f, "Maximum |tofExpSignalDiffDe| for deuterons; <=0 disables the numeric cut"}; + + // PID strategy for final K/p/d candidate selection. + // 0 = pT-ref dependent rectangular cuts: + // pT < pTref: require |TPC| < TPC cut only + // pT >= pTref and TOF exists: require |TPC| < TPC cut and |TOF| < TOF cut + // pT >= pTref and TOF missing: reject + // 1 = pT-ref dependent circular cut: + // pT < pTref: require |TPC| < TPC cut only + // pT >= pTref and TOF exists: require sqrt(TPC^2 + TOF^2) < circular cut + // pT >= pTref and TOF missing: reject + // 2 = hybrid official-like PID: + // deuterons: require |TPC_d| < TPC cut only + // kaons/protons: use the Lambda(1520)-like pT-ref circular TPC+TOF logic + Configurable lstarPidStrategy{"lstarPidStrategy", int{PidStrategyRectangular}, "PID strategy: 0=pTref rectangular TPC/TOF, 1=pTref circular TPC+TOF, 2=hybrid: K/p circular, d TPC-only"}; + + Configurable lstarPidCircularCutKaon{"lstarPidCircularCutKaon", 2.0f, "Circular PID cut sqrt(nSigmaTPC_K^2+nSigmaTOF_K^2) for kaons"}; + Configurable lstarPidCircularCutPr{"lstarPidCircularCutPr", 2.0f, "Circular PID cut sqrt(nSigmaTPC_p^2+nSigmaTOF_p^2) for protons"}; + Configurable lstarPidCircularCutDe{"lstarPidCircularCutDe", 2.0f, "Circular PID cut sqrt(nSigmaTPC_d^2+nSigmaTOF_d^2) for deuterons"}; + + Configurable lstarPidPtRefKaon{"lstarPidPtRefKaon", 0.5f, "pT reference for kaon PID strategy"}; + Configurable lstarPidPtRefPr{"lstarPidPtRefPr", 0.8f, "pT reference for proton PID strategy"}; + Configurable lstarPidPtRefDe{"lstarPidPtRefDe", 0.8f, "pT reference for deuteron PID strategy"}; // Track quality + Configurable lstarRequireINELgt0{"lstarRequireINELgt0", 1, "Require INEL>0 event selection using isInelGt0() when available; fallback to multNTracksPVeta1()/numContrib()"}; Configurable lstarRequireGlobalTrack{"lstarRequireGlobalTrack", bool{RequireGlobalTrackDefault}, "Require global tracks (default)"}; - Configurable lstarTPCNClsMin{"lstarTPCNClsMin", int{TPCNClsMinDefault}, "Minimum number of TPC clusters (tpcNClsFound)"}; + Configurable lstarRequirePrimaryTrack{"lstarRequirePrimaryTrack", 1, "Require isPrimaryTrack() for Lambda* proxy candidates when the column is available"}; + Configurable lstarOnlyGlobalTrackCuts{"lstarOnlyGlobalTrackCuts", 0, "If enabled, apply only the global-track plus primary-track requirements and skip additional ITS/TPC/DCA/chi2 track-quality cuts"}; + Configurable lstarTPCNClsMin{"lstarTPCNClsMin", int{TPCNClsMinDefault}, "Minimum number of TPC crossed rows (tpcNClsCrossedRows)"}; + Configurable lstarTPCCrossedRowsOverFindableMin{"lstarTPCCrossedRowsOverFindableMin", 0.8f, "Minimum TPC crossed rows over findable clusters"}; Configurable lstarDcaXYMax{"lstarDcaXYMax", float{DcaXYMaxDefault}, "Max |DCA_{xy}| to PV (cm)"}; Configurable lstarDcaZMax{"lstarDcaZMax", float{DcaZMaxDefault}, "Max |DCA_{z}| to PV (cm)"}; Configurable lstarChi2PerTPCCluster{"lstarChi2PerTPCCluster", float{Chi2PerTPCClusterDefault}, "Maximum Chi2/cluster for the TPC track segment"}; @@ -1716,7 +1783,12 @@ struct Lambdastarproxy { Configurable lstarNoMixedEvents{"lstarNoMixedEvents", int{NoMixedEventsDefault}, "Number of previous events kept for mixed-event background"}; Configurable lstarMixZvtxMax{"lstarMixZvtxMax", float{MixZvtxMaxDefault}, "Max |Δzvtx| (cm) for event mixing"}; Configurable lstarMixMultMax{"lstarMixMultMax", float{MixMultMaxDefault}, "Max |Δmult| for event mixing"}; + // Master switch for PID QA histogram filling. + // Inclusive PID-QA histograms are filled before final PID cuts, after event/track-quality cuts. + // Candidate-level nSigma/TOF/DCA histograms are filled after final PID cuts. Configurable lstarEnablePidQA{"lstarEnablePidQA", 0, "Enable PID QA histograms (dE/dx, TOF #beta, proxy invariant-mass QA, etc.): 1 = ON, 0 = OFF"}; + Configurable lstarEnableProxyControls{"lstarEnableProxyControls", 1, "Enable TOF/PID-partitioned proxy spectra and inclusive full-pK maps (requires lstarEnableSparse)"}; + Configurable lstarRequireTOFMatchDe{"lstarRequireTOFMatchDe", 0, "Require a TOF match for every selected deuteron; 0 preserves hybrid unmatched candidates"}; Configurable lstarEnableSparse{"lstarEnableSparse", 1, "Enable THnSparse invariant-mass histograms (#Lambda^{*} pK and proxy); 1 = ON, 0 = OFF"}; Configurable lstarLambdaAbsYMax{"lstarLambdaAbsYMax", 0.5f, "Max |y_{pK}| (or y_{proxy K}) for #Lambda^{*} candidates"}; @@ -1725,8 +1797,13 @@ struct Lambdastarproxy { int charge; int tid; }; + // PID flags describe detector compatibility, not truth-level purity. struct ProxyCand { float px, py, pz; + float pxFull, pyFull, pzFull; + bool protonLike; + bool hasTOF; + bool protonTPCCompatible; int charge; int tid; }; @@ -1739,17 +1816,18 @@ struct Lambdastarproxy { // Helpers for invariant-mass kinematics static float phiFromPxPy(float px, float py) { - return std::atan2(py, px); + return RecoDecay::constrainAngle(std::atan2(py, px), -o2::constants::math::PI); } - static float rapidityFromEPz(double e, double pz) + static float ptFromPxPy(float px, float py) { - const double num = e + pz; - const double den = e - pz; - if (num <= 0.0 || den <= 0.0) { - return 0.f; - } - return static_cast(Half * std::log(num / den)); + return RecoDecay::pt(std::array{px, py}); + } + + static float rapidityFromMomentumAndMass(float px, float py, float pz, double mass) + + { + return RecoDecay::y(std::array{px, py, pz}, mass); } // Mixed-event pool entry for pK / proxy background (AO2D only) @@ -1757,6 +1835,7 @@ struct Lambdastarproxy { float mult = 0.f; float zvtx = 0.f; std::vector kaons; + std::vector protons; std::vector proxies; }; @@ -1766,16 +1845,35 @@ struct Lambdastarproxy { template bool keepCollisionAO2D(TCollision const& collision) const { + // Prefer the official event-selection flag when available. + if (lstarRequireINELgt0.value != 0) { + bool isINELgt0 = true; + + if constexpr (requires { collision.isInelGt0(); }) { + isINELgt0 = collision.isInelGt0(); + } else if constexpr (requires { collision.multNTracksPVeta1(); }) { + isINELgt0 = collision.multNTracksPVeta1() > 0; + } else if constexpr (requires { collision.numContrib(); }) { + isINELgt0 = collision.numContrib() > 0; + } + + if (!isINELgt0) { + return false; + } + } if (lstarCfgTrigger.value == TriggerNone) { return true; - } else if (lstarCfgTrigger.value == TriggerSel8) { + } + if (lstarCfgTrigger.value == TriggerSel8) { return collision.sel8(); - } else if (lstarCfgTrigger.value == TriggerSel8Quality) { + } + if (lstarCfgTrigger.value == TriggerSel8Quality) { return collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); - } else if (lstarCfgTrigger.value == TriggerSel8OccQuality) { + } + if (lstarCfgTrigger.value == TriggerSel8OccQuality) { const int occupancy = collision.trackOccupancyInTimeRange(); if (occupancy < lstarMinOcc.value || occupancy >= lstarMaxOcc.value) { return false; @@ -1785,7 +1883,8 @@ struct Lambdastarproxy { collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) && collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); - } else if (lstarCfgTrigger.value == TriggerSel8NoSbpZvtx) { + } + if (lstarCfgTrigger.value == TriggerSel8NoSbpZvtx) { return collision.sel8() && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); @@ -1879,14 +1978,40 @@ struct Lambdastarproxy { } } + // Always require primary tracks + if (lstarRequirePrimaryTrack.value != 0) { + if constexpr (requires { trk.isPrimaryTrack(); }) { + if (!trk.isPrimaryTrack()) { + return false; + } + } + } + + // Optional official-baseline mode: use only the O2 global-track and primary-track definitions. + // This bypasses the extra explicit ITS/TPC/DCA/chi2 cuts below. + if (lstarOnlyGlobalTrackCuts.value != 0) { + return true; + } + if constexpr (requires { trk.itsNCls(); }) { if (lstarITSNClusters.value > 0 && trk.itsNCls() < lstarITSNClusters.value) { return false; } } - if constexpr (requires { trk.tpcNClsFound(); }) { - if (lstarTPCNClsMin.value > 0 && trk.tpcNClsFound() < lstarTPCNClsMin.value) { + if constexpr (requires { trk.tpcNClsCrossedRows(); }) { + if (lstarTPCNClsMin.value > 0 && trk.tpcNClsCrossedRows() < lstarTPCNClsMin.value) { + return false; + } + } else if constexpr (requires { trk.tpcNClsFindable(); trk.tpcCrossedRowsOverFindableCls(); }) { + if (lstarTPCNClsMin.value > 0 && trk.tpcNClsFindable() * trk.tpcCrossedRowsOverFindableCls() < lstarTPCNClsMin.value) { + return false; + } + } + + if constexpr (requires { trk.tpcCrossedRowsOverFindableCls(); }) { + if (lstarTPCCrossedRowsOverFindableMin.value > 0.f && + trk.tpcCrossedRowsOverFindableCls() < lstarTPCCrossedRowsOverFindableMin.value) { return false; } } @@ -1947,7 +2072,8 @@ struct Lambdastarproxy { { AxisSpec massAxis{200, 1.4, 1.9, "M_{pK} (GeV/c^{2})"}; AxisSpec ptAxis{100, 0., 10., "p_{T} (GeV/c)"}; - AxisSpec nsAxis{100, -10., 10., "n#sigma"}; + AxisSpec nsAxis{500, -50., 50., "n#sigma"}; + AxisSpec tofMatchAxis{2, -0.5, 1.5, "has TOF match"}; AxisSpec pAxis{100, 0., 10., "p (GeV/c)"}; AxisSpec etaAxis{80, -2., 2., "#eta"}; AxisSpec phiAxis{64, 0., o2::constants::math::TwoPI, "#varphi"}; @@ -1957,6 +2083,15 @@ struct Lambdastarproxy { AxisSpec dEdxAxis{400, 0., 200., "TPC dE/dx (arb. units)"}; AxisSpec betaAxis{160, 0., 1.6, "#beta_{TOF}"}; + AxisSpec dcaXYAxis{200, -0.2, 0.2, "DCA_{xy} (cm)"}; + AxisSpec dcaZAxis{200, -0.2, 0.2, "DCA_{z} (cm)"}; + + // Count reconstructed collisions received by the data process, before any + // task event cuts and after all of them, independently of track candidates. + histos.add("hEventSelection", "Proxy event selection;Selection stage;Events", + HistType::kTH1D, {AxisSpec{2, 0.5, 2.5, "Selection stage"}}); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "Before event selection"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "After event selection"); // Invariant-mass spectra histos.add("hInvMassPKUnlike", @@ -1966,56 +2101,156 @@ struct Lambdastarproxy { "pK invariant mass (like-sign);M_{pK} (GeV/c^{2});Counts", HistType::kTH1F, {massAxis}); - // Invariant-mass vs pair pT (use p_{T} of pK system) - histos.add("hInvMassPKUnlikeVsPt", - "pK invariant mass vs p_{T} (unlike-sign);M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);Counts", - HistType::kTH2F, {massAxis, ptAxis}); - histos.add("hInvMassPKLikeVsPt", - "pK invariant mass vs p_{T} (like-sign);M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);Counts", - HistType::kTH2F, {massAxis, ptAxis}); - - // THnSparse for invariant-mass analysis (mass, pT, y, phi) + // THnSparse for invariant-mass analysis (mass, pT, multiplicity/centrality) if (lstarEnableSparse.value != 0) { + + if (lstarEnableProxyControls.value != 0) { + // Axes 3/4/5 partition the SAME inclusive proxy sample in every pair class. + const std::vector controlAxes{ + AxisSpec{400, 1.4, 1.8, "M_{(d/2)K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{(d/2)K} (GeV/c)"}, centAxis, + AxisSpec{2, -0.5, 1.5, "deuteron hasTOF (0/1)"}, + AxisSpec{2, -0.5, 1.5, "passes full proton PID (0/1)"}, + AxisSpec{2, -0.5, 1.5, "passes proton TPC cut only (0/1)"}}; + histos.add("hLambdaStarProxySelectedTrackControlSparse", "Selected deuteron track controls", + HistType::kTHnSparseF, + {AxisSpec{100, 0., 10., "p_{T}^{d,full} (GeV/c)"}, controlAxes[3], controlAxes[4], controlAxes[5]}, true); + auto mapAxes = controlAxes; + // Full-pK rapidity is recorded, not imposed. Include mass flow bins + // when projecting these maps back onto the inclusive proxy sample. + mapAxes.emplace_back(1000, 1.4, 2.4, "M_{pK}^{full} (GeV/c^{2})"); + mapAxes.emplace_back(2, -0.5, 1.5, "passes full-pK rapidity (0/1)"); + for (auto const& pairClass : {"Unlike", "Like", "Mixed"}) { + const std::string spectrumName = std::string("hLambdaStarProxyControl") + pairClass + "Sparse"; + const std::string mapName = std::string("hLambdaStarProxyControlFullPK") + pairClass + "Sparse"; + histos.add(spectrumName.c_str(), "Inclusive proxy acceptance; PID compatibility controls", HistType::kTHnSparseF, controlAxes, true); + histos.add(mapName.c_str(), "Inclusive proxy acceptance; full-pK control map", HistType::kTHnSparseF, mapAxes, true); + } + } + + histos.add( + "hLambdaStarProxyVsFullPKMixedSparse", + "d-selected mixed-event pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + + histos.add( + "hLambdaStarProxyProtonLikeUnlikeSparse", + "d-selected and proton-like OS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + + histos.add( + "hLambdaStarProxyProtonLikeLikeSparse", + "d-selected and proton-like LS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + histos.add("hLambdaStarPKUnlikeSparse", - "#Lambda^{*}(1520) pK unlike-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);y_{pK};#varphi_{pK}", + "#Lambda^{*}(1520) pK unlike-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", HistType::kTHnSparseF, - {AxisSpec{400, 1.3, 1.9, "M_{pK} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, - AxisSpec{60, -1.5, 1.5, "y_{pK}"}, - AxisSpec{64, -3.2, 3.2, "#varphi_{pK}"}, centAxis}); + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); histos.add("hLambdaStarPKLikeSparse", - "#Lambda^{*}(1520) pK like-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);y_{pK};#varphi_{pK}", + "#Lambda^{*}(1520) pK like-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", HistType::kTHnSparseF, - {AxisSpec{400, 1.3, 1.9, "M_{pK} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, - AxisSpec{60, -1.5, 1.5, "y_{pK}"}, - AxisSpec{64, -3.2, 3.2, "#varphi_{pK}"}, centAxis}); + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); histos.add("hLambdaStarPKMixedSparse", - "#Lambda^{*}(1520) pK mixed-event candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);y_{pK};#varphi_{pK}", + "#Lambda^{*}(1520) pK mixed-event candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", HistType::kTHnSparseF, - {AxisSpec{400, 1.3, 1.9, "M_{pK} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, - AxisSpec{60, -1.5, 1.5, "y_{pK}"}, - AxisSpec{64, -3.2, 3.2, "#varphi_{pK}"}, centAxis}); + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); - // THnSparse for deuteron-proxy invariant-mass analysis (mass, pT, y, phi) + // THnSparse for deuteron-proxy invariant-mass analysis (mass, pT, multiplicity/centrality) histos.add("hLambdaStarProxySparse", - "#Lambda^{*}(1520) deuteron-proxy candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);y_{p_{proxy}K};#varphi_{p_{proxy}K}", + "#Lambda^{*}(1520) deuteron-proxy candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + + histos.add("hLambdaStarProxyLikeSparse", + "#Lambda^{*}(1520) deuteron-proxy like-sign candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", HistType::kTHnSparseF, - {AxisSpec{400, 1.3, 1.9, "M_{p_{proxy}K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, - AxisSpec{60, -1.5, 1.5, "y_{p_{proxy}K}"}, - AxisSpec{64, -3.2, 3.2, "#varphi_{p_{proxy}K}"}, centAxis}); + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); histos.add("hLambdaStarProxyMixedSparse", - "#Lambda^{*}(1520) deuteron-proxy mixed-event candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);y_{p_{proxy}K};#varphi_{p_{proxy}K}", + "#Lambda^{*}(1520) deuteron-proxy mixed-event candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", HistType::kTHnSparseF, - {AxisSpec{400, 1.3, 1.9, "M_{p_{proxy}K} (GeV/c^{2})"}, - AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, - AxisSpec{60, -1.5, 1.5, "y_{p_{proxy}K}"}, - AxisSpec{64, -3.2, 3.2, "#varphi_{p_{proxy}K}"}, centAxis}); + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + + histos.add( + "hLambdaStarProxyVsFullPKUnlikeSparse", + "d-selected OS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + + histos.add( + "hLambdaStarProxyVsFullPKLikeSparse", + "d-selected LS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); } // Deuteron-proxy invariant mass (p_{proxy} from d/2 combined with K) @@ -2023,6 +2258,7 @@ struct Lambdastarproxy { "#Lambda^{*} proxy invariant mass from (d/2 + K);M_{p_{proxy}K} (GeV/c^{2});Counts", HistType::kTH1F, {massAxis}); + // Inclusive PID QA before final candidate PID cuts: after track-quality cuts only // TPC dE/dx vs total momentum histos.add("hTPCdEdxVsP", "TPC dE/dx vs p;p (GeV/c);dE/dx (arb. units);Counts", @@ -2033,7 +2269,16 @@ struct Lambdastarproxy { "TOF #beta vs p;p (GeV/c);#beta_{TOF};Counts", HistType::kTH2F, {pAxis, betaAxis}); - // --- Per-species PID QA (tagged) --- + histos.add("hHasTOFVsP", + "TOF matching flag vs p;p (GeV/c);has TOF match;Counts", + HistType::kTH2F, {pAxis, tofMatchAxis}); + + histos.add("hHasTOFVsPt", + "TOF matching flag vs p_{T};p_{T} (GeV/c);has TOF match;Counts", + HistType::kTH2F, {ptAxis, tofMatchAxis}); + + // --- Per-species inclusive PID QA before final candidate PID cuts --- + // Species tagging here uses classifyPidSpecies() and is meant only for QA. histos.add("hTPCdEdxVsP_Pi", "TPC dE/dx vs p (tagged #pi);p (GeV/c);dE/dx (arb. units);Counts", HistType::kTH2F, {pAxis, dEdxAxis}); @@ -2060,6 +2305,36 @@ struct Lambdastarproxy { "TOF #beta vs p (tagged d);p (GeV/c);#beta_{TOF};Counts", HistType::kTH2F, {pAxis, betaAxis}); + // Inclusive tagged nSigma QA before final candidate PID cuts: after track-quality cuts only. + // These are separate from the final-candidate PID QA histograms. + histos.add("hNsigmaTPCPionTaggedVsP", + "TPC n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TPC}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFPionTaggedVsP", + "TOF n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TOF}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCKaonTaggedVsP", + "TPC n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFKaonTaggedVsP", + "TOF n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonTaggedVsP", + "TPC n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonTaggedVsP", + "TOF n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronTaggedVsP", + "TPC n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronTaggedVsP", + "TOF n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + // --- MC QA (AO2D-MC mode) --- histos.add("hMcPrimariesPtEta", "MC charged physical primaries; p_{T} (GeV/c); #eta; Counts", @@ -2122,6 +2397,109 @@ struct Lambdastarproxy { histos.add("hNsigmaTOFKaonVsP", "TOF n#sigma_{K} vs p; p (GeV/c); n#sigma^{TOF}_{K};Counts", HistType::kTH2F, {pAxis, nsAxis}); + + // --- Candidate PID QA after final selected K/p/d PID cuts --- + // These histograms are needed for PID studies in the same pT intervals used by the analysis. + histos.add("hTOFBetaVsPt_K", + "TOF #beta vs p_{T} for selected K;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_P", + "TOF #beta vs p_{T} for selected p;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_D", + "TOF #beta vs p_{T} for selected d;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + + histos.add("hNsigmaTPCKaonVsPt", + "TPC n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFKaonVsPt", + "TOF n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonVsP", + "TPC n#sigma_{p} vs p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsP", + "TOF n#sigma_{p} vs p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTPCProtonVsPt", + "TPC n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsPt", + "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronVsPt", + "TPC n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronVsPt", + "TOF n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add( + "hNsigmaTPCProtonForSelectedDeuteronVsPt", + "TPC n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hNsigmaTOFProtonForSelectedDeuteronVsPt", + "TOF n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hTPCNsigmaDeVsPrForSelectedDeuteron", + "TPC PID overlap for selected d candidates;" + "n#sigma^{TPC}_{d};" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add( + "hTOFNsigmaDeVsPrForSelectedDeuteron", + "TOF PID overlap for selected d candidates;" + "n#sigma^{TOF}_{d};" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add("hTPCvsTOFNsigma_K", + "TPC vs TOF n#sigma for selected K;n#sigma^{TPC}_{K};n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_P", + "TPC vs TOF n#sigma for selected p;n#sigma^{TPC}_{p};n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_D", + "TPC vs TOF n#sigma for selected d;n#sigma^{TPC}_{d};n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + + // --- DCA QA for final selected K/p/d candidates --- + // Filled only when lstarEnablePidQA = 1. + histos.add("hDCAxyVsPt_K", + "DCA_{xy} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_K", + "DCA_{z} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_P", + "DCA_{xy} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_P", + "DCA_{z} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_D", + "DCA_{xy} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_D", + "DCA_{z} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); } // AO2D-MC QA: truth primaries + reco-to-MC matching sanity plots @@ -2241,6 +2619,103 @@ struct Lambdastarproxy { return true; // fallback: if column not present, assume available } + template + bool passOptionalDeuteronTOFExtras(const TTrack& trk) const + { + const bool needTOF = + (lstarRequireTOFMatchDe.value != 0) || + (lstarEnableBetaCutDe.value != 0) || + (lstarEnableExpSignalTOFDe.value != 0 && lstarTOFExpSignalDiffDeMax.value > 0.f); + + if (needTOF) { + if constexpr (requires { trk.hasTOF(); }) { + if (!trk.hasTOF()) { + return false; + } + } else { + return false; + } + } + + if (lstarEnableBetaCutDe.value != 0) { + if constexpr (requires { trk.beta(); }) { + if (trk.beta() <= lstarBetaCutDe.value) { + return false; + } + } else { + return false; + } + } + + if (lstarEnableExpSignalTOFDe.value != 0 && + lstarTOFExpSignalDiffDeMax.value > 0.f) { + if constexpr (requires { trk.tofExpSignalDiffDe(); }) { + if (std::abs(trk.tofExpSignalDiffDe()) > lstarTOFExpSignalDiffDeMax.value) { + return false; + } + } else { + return false; + } + } + + return true; + } + + bool passFinalCandidatePID(float pt, float nsTPC, float nsTOF, bool hasTof, + float tpcCut, float tofCut, float circularCut, + float ptRef, bool isDeuteron = false) const + { + // Strategy 1: analysis-note style circular TPC+TOF cut + if (lstarPidStrategy.value == PidStrategyCircularTPCAndTOF) { + if (pt < ptRef) { + return std::abs(nsTPC) < tpcCut; + } + + if (!hasTof) { + return false; + } + + return std::sqrt(nsTPC * nsTPC + nsTOF * nsTOF) < circularCut; + } + + if (lstarPidStrategy.value == PidStrategyNucleiDeuteronTPC) { + // For deuterons, use TPC nσ as the main hard PID selection. + // If the optional deuteron TOF nσ cut is enabled, apply it only when + // the track has a valid TOF match. Tracks without TOF are kept with + // the TPC-only decision, avoiding an additional TOF-matching efficiency loss. + if (isDeuteron) { + if (std::abs(nsTPC) >= tpcCut) { + return false; + } + if (lstarEnableTOFNsigmaCutDe.value != 0 && hasTof) { + return std::abs(nsTOF) < tofCut; + } + return true; + } + + // For kaons/protons, use the Lambda(1520)-like pT-ref circular TPC+TOF logic. + if (pt < ptRef) { + return std::abs(nsTPC) < tpcCut; + } + + if (!hasTof) { + return false; + } + + return std::sqrt(nsTPC * nsTPC + nsTOF * nsTOF) < circularCut; + } + + // Strategy 0: pT-ref dependent rectangular TPC+TOF cut. + // Below pTref use TPC only; above pTref require TOF and apply both TPC and TOF cuts. + if (pt < ptRef) { + return std::abs(nsTPC) < tpcCut; + } + if (!hasTof) { + return false; + } + return (std::abs(nsTPC) < tpcCut) && (std::abs(nsTOF) < tofCut); + } + // Return: 0=#pi, 1=K, 2=p, 3=d, -1=unclassified template int classifyPidSpecies(const TTrack& trk) @@ -2317,108 +2792,29 @@ struct Lambdastarproxy { static double invariantMass(float px1, float py1, float pz1, double m1, float px2, float py2, float pz2, double m2) { - const double e1 = std::sqrt(m1 * m1 + px1 * px1 + py1 * py1 + pz1 * pz1); - const double e2 = std::sqrt(m2 * m2 + px2 * px2 + py2 * py2 + pz2 * pz2); - const double ex = px1 + px2; - const double ey = py1 + py2; - const double ez = pz1 + pz2; - const double eTot = e1 + e2; - const double p2Tot = ex * ex + ey * ey + ez * ez; - const double m2Tot = eTot * eTot - p2Tot; - return m2Tot > 0. ? std::sqrt(m2Tot) : 0.; + return RecoDecay::m(std::array{std::array{px1, py1, pz1}, + std::array{px2, py2, pz2}}, + std::array{m1, m2}); } - void process(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks) + void process(CollisionsWithEvSel::iterator const& collision, FilteredTracks const& tracks) { + histos.fill(HIST("hEventSelection"), 1.); + // Use unfiltered collisions here so the first bin includes vertex rejects. + // Preserve the strict vertex acceptance of collisionZVtxFilter; the MC QA + // process still uses that framework filter and does not fill this counter. + if (!(std::abs(collision.posZ()) < lstarCutVertex.value)) { + return; + } // Event selection (cfgTrigger) -- AO2D only if (!keepCollisionAO2D(collision)) { return; } + histos.fill(HIST("hEventSelection"), 2.); // physics masses (GeV/c^2) constexpr double MassProton = o2::constants::physics::MassProton; constexpr double MassKaonCharged = o2::constants::physics::MassKaonCharged; - // --- Inclusive PID QA: keep #pi/K/p/d bands in TPC dE/dx and TOF beta plots --- - if (lstarEnablePidQA.value != 0) { - for (auto const& trk : tracks) { - if (trk.pt() < lstarCutPtMin.value || std::abs(trk.eta()) > lstarCutEtaMax.value) { - continue; - } - if (!passTrackQuality(trk)) { - continue; - } - if (trk.sign() == 0) { - continue; - } - // Inclusive PID QA - fillTPCdEdxVsPIfAvailable(trk); - fillTOFBetaVsPIfAvailable(trk); - - // Per-species PID-QA (tagged) histograms - const int sp = classifyPidSpecies(trk); - switch (sp) { - case 0: { // pion - if constexpr (requires { trk.tpcSignal(); }) { - histos.fill(HIST("hTPCdEdxVsP_Pi"), trk.p(), trk.tpcSignal()); - } - if constexpr (requires { trk.beta(); }) { - const bool hasTof = hasTOFMatch(trk); - const float beta = trk.beta(); - if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { - histos.fill(HIST("hTOFBetaVsP_Pi"), trk.p(), beta); - } - } - break; - } - - case 1: { // kaon - if constexpr (requires { trk.tpcSignal(); }) { - histos.fill(HIST("hTPCdEdxVsP_K"), trk.p(), trk.tpcSignal()); - } - if constexpr (requires { trk.beta(); }) { - const bool hasTof = hasTOFMatch(trk); - const float beta = trk.beta(); - if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { - histos.fill(HIST("hTOFBetaVsP_K"), trk.p(), beta); - } - } - break; - } - - case 2: { // proton - if constexpr (requires { trk.tpcSignal(); }) { - histos.fill(HIST("hTPCdEdxVsP_P"), trk.p(), trk.tpcSignal()); - } - if constexpr (requires { trk.beta(); }) { - const bool hasTof = hasTOFMatch(trk); - const float beta = trk.beta(); - if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { - histos.fill(HIST("hTOFBetaVsP_P"), trk.p(), beta); - } - } - break; - } - - case 3: { // deuteron - if constexpr (requires { trk.tpcSignal(); }) { - histos.fill(HIST("hTPCdEdxVsP_D"), trk.p(), trk.tpcSignal()); - } - if constexpr (requires { trk.beta(); }) { - const bool hasTof = hasTOFMatch(trk); - const float beta = trk.beta(); - if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { - histos.fill(HIST("hTOFBetaVsP_D"), trk.p(), beta); - } - } - break; - } - - default: - break; - } - } - } - std::vector kaonCands; std::vector proxyCands; std::vector protonCands; @@ -2428,7 +2824,9 @@ struct Lambdastarproxy { float eventMultFallback = 0.f; // fallback mixing variable: number of selected charged tracks (after quality cuts) - // Inclusive PID QA loop: count all selected charged tracks for fallback multiplicity + // Inclusive track loop before final candidate PID cuts. + // It counts selected charged tracks for fallback multiplicity and when enabled, + // fills inclusive PID QA after track-quality cuts but before final K/p/d PID cuts. for (auto const& trk : tracks) { if (trk.pt() < lstarCutPtMin.value || std::abs(trk.eta()) > lstarCutEtaMax.value) { continue; @@ -2447,10 +2845,18 @@ struct Lambdastarproxy { fillTPCdEdxVsPIfAvailable(trk); fillTOFBetaVsPIfAvailable(trk); + const double pForPidQA = static_cast(trk.pt()) * std::cosh(static_cast(trk.eta())); + const bool hasTofForPidQA = hasTOFMatch(trk); + + histos.fill(HIST("hHasTOFVsP"), pForPidQA, hasTofForPidQA ? 1.0 : 0.0); + histos.fill(HIST("hHasTOFVsPt"), trk.pt(), hasTofForPidQA ? 1.0 : 0.0); + // Per-species PID-QA (tagged) histograms const int sp = classifyPidSpecies(trk); switch (sp) { case 0: { // pion + histos.fill(HIST("hNsigmaTPCPionTaggedVsP"), pForPidQA, trk.tpcNSigmaPi()); + histos.fill(HIST("hNsigmaTOFPionTaggedVsP"), pForPidQA, trk.tofNSigmaPi()); if constexpr (requires { trk.tpcSignal(); }) { histos.fill(HIST("hTPCdEdxVsP_Pi"), trk.p(), trk.tpcSignal()); } @@ -2464,6 +2870,8 @@ struct Lambdastarproxy { break; } case 1: { // kaon + histos.fill(HIST("hNsigmaTPCKaonTaggedVsP"), pForPidQA, trk.tpcNSigmaKa()); + histos.fill(HIST("hNsigmaTOFKaonTaggedVsP"), pForPidQA, trk.tofNSigmaKa()); if constexpr (requires { trk.tpcSignal(); }) { histos.fill(HIST("hTPCdEdxVsP_K"), trk.p(), trk.tpcSignal()); } @@ -2477,6 +2885,8 @@ struct Lambdastarproxy { break; } case 2: { // proton + histos.fill(HIST("hNsigmaTPCProtonTaggedVsP"), pForPidQA, trk.tpcNSigmaPr()); + histos.fill(HIST("hNsigmaTOFProtonTaggedVsP"), pForPidQA, trk.tofNSigmaPr()); if constexpr (requires { trk.tpcSignal(); }) { histos.fill(HIST("hTPCdEdxVsP_P"), trk.p(), trk.tpcSignal()); } @@ -2490,6 +2900,8 @@ struct Lambdastarproxy { break; } case 3: { // deuteron + histos.fill(HIST("hNsigmaTPCDeuteronTaggedVsP"), pForPidQA, trk.tpcNSigmaDe()); + histos.fill(HIST("hNsigmaTOFDeuteronTaggedVsP"), pForPidQA, trk.tofNSigmaDe()); if constexpr (requires { trk.tpcSignal(); }) { histos.fill(HIST("hTPCdEdxVsP_D"), trk.p(), trk.tpcSignal()); } @@ -2522,23 +2934,61 @@ struct Lambdastarproxy { continue; } - // PID for deuteron candidates + // Deuteron kinematics needed before PID because the PID strategy can depend on pT + const float ptD = trkD.pt(); + const float etaD = trkD.eta(); + const float phiD = trkD.phi(); + + // PID for proxy candidates. + // Normal mode: use the standard deuteron PID and build p_proxy = p_d / 2. + // Closure/control mode: select the subset of standard deuteron-proxy candidates + // that are also proton-like. This tests proton contamination inside the deuteron + // selection, not all proton candidates. + const bool useProtonAsProxy = (lstarProxyUseProtonPIDAsDeuteron.value != 0); + const float nsTPCDe = trkD.tpcNSigmaDe(); const float nsTOFDe = trkD.tofNSigmaDe(); - const bool hasTofDe = hasTOFMatch(trkD); - const bool isDeuteron = (std::abs(nsTPCDe) < lstarCutNsigmaTPCDe.value) && - (!hasTofDe || (std::abs(nsTOFDe) < lstarCutNsigmaTOFDe.value)); - if (!isDeuteron) { + // These controls require the actual match flag, never an inferred PID value. + const bool hasTofDe = trkD.hasTOF(); + + if (!passOptionalDeuteronTOFExtras(trkD)) { + continue; + } + + const bool passesDeuteronSelection = passFinalCandidatePID(ptD, nsTPCDe, nsTOFDe, hasTofDe, + lstarCutNsigmaTPCDe.value, lstarCutNsigmaTOFDe.value, + lstarPidCircularCutDe.value, lstarPidPtRefDe.value, + true); + if (!passesDeuteronSelection) { + continue; + } + + const float nsTPCPrAsProxy = + trkD.tpcNSigmaPr(); + + const float nsTOFPrAsProxy = + trkD.tofNSigmaPr(); + + const bool passesProtonSelection = + passFinalCandidatePID( + ptD, + nsTPCPrAsProxy, + nsTOFPrAsProxy, + hasTofDe, + lstarCutNsigmaTPCPr.value, + lstarCutNsigmaTOFPr.value, + lstarPidCircularCutPr.value, + lstarPidPtRefPr.value, + false); + + if (useProtonAsProxy && + !passesProtonSelection) { continue; } - // Deuteron kinematics - const float ptD = trkD.pt(); - const float etaD = trkD.eta(); - const float phiD = trkD.phi(); const double pD = static_cast(ptD) * std::cosh(static_cast(etaD)); - // QA histos for deuteron PID and kinematics + // Candidate QA after final deuteron PID cut if (lstarEnablePidQA.value != 0) { histos.fill(HIST("hDeuteronProxyPt"), ptD); histos.fill(HIST("hDeuteronProxyEta"), etaD); @@ -2547,6 +2997,44 @@ struct Lambdastarproxy { histos.fill(HIST("hNsigmaTOFDeuteron"), nsTOFDe); histos.fill(HIST("hNsigmaTPCDeuteronVsP"), pD, nsTPCDe); histos.fill(HIST("hNsigmaTOFDeuteronVsP"), pD, nsTOFDe); + histos.fill(HIST("hNsigmaTPCDeuteronVsPt"), ptD, nsTPCDe); + histos.fill(HIST("hNsigmaTOFDeuteronVsPt"), ptD, nsTOFDe); + histos.fill( + HIST( + "hNsigmaTPCProtonForSelectedDeuteronVsPt"), + ptD, + nsTPCPrAsProxy); + + histos.fill( + HIST( + "hNsigmaTOFProtonForSelectedDeuteronVsPt"), + ptD, + nsTOFPrAsProxy); + + histos.fill( + HIST( + "hTPCNsigmaDeVsPrForSelectedDeuteron"), + nsTPCDe, + nsTPCPrAsProxy); + + if (hasTofDe) { + histos.fill( + HIST( + "hTOFNsigmaDeVsPrForSelectedDeuteron"), + nsTOFDe, + nsTOFPrAsProxy); + histos.fill(HIST("hTPCvsTOFNsigma_D"), nsTPCDe, nsTOFDe); + } + if constexpr (requires { trkD.beta(); }) { + const float beta = trkD.beta(); + if (hasTofDe && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsPt_D"), ptD, beta); + } + } + if constexpr (requires { trkD.dcaXY(); trkD.dcaZ(); }) { + histos.fill(HIST("hDCAxyVsPt_D"), ptD, trkD.dcaXY()); + histos.fill(HIST("hDCAzVsPt_D"), ptD, trkD.dcaZ()); + } } // build proton-proxy momentum from deuteron: p_p ≈ p_d / 2 @@ -2554,7 +3042,44 @@ struct Lambdastarproxy { const float pyProxy = ProxyMomentumScale * ptD * std::sin(phiD); const float pzProxy = ProxyMomentumScale * ptD * std::sinh(etaD); - proxyCands.push_back(ProxyCand{pxProxy, pyProxy, pzProxy, static_cast(trkD.sign()), static_cast(trkD.globalIndex())}); + // Full measured momentum of the same d-selected track. + // This will later be interpreted under the proton mass hypothesis. + const float pxFull = + ptD * std::cos(phiD); + + const float pyFull = + ptD * std::sin(phiD); + + const float pzFull = + ptD * std::sinh(etaD); + + if (lstarEnableSparse.value != 0 && lstarEnableProxyControls.value != 0) { + histos.fill(HIST("hLambdaStarProxySelectedTrackControlSparse"), ptD, hasTofDe, + passesProtonSelection, std::abs(nsTPCPrAsProxy) < lstarCutNsigmaTPCPr.value); + } + + proxyCands.push_back( + ProxyCand{ + .px = pxProxy, + .py = pyProxy, + .pz = pzProxy, + + .pxFull = pxFull, + .pyFull = pyFull, + .pzFull = pzFull, + + .protonLike = + passesProtonSelection, + .hasTOF = hasTofDe, + .protonTPCCompatible = std::abs(nsTPCPrAsProxy) < lstarCutNsigmaTPCPr.value, + + .charge = + static_cast( + trkD.sign()), + + .tid = + static_cast( + trkD.globalIndex())}); } // Proton candidates (for genuine pK #Lambda^{*} reconstruction) @@ -2569,24 +3094,52 @@ struct Lambdastarproxy { continue; } + const float ptP = trkP.pt(); + const float etaP = trkP.eta(); + const float phiP = trkP.phi(); + const float nsTPCPr = trkP.tpcNSigmaPr(); const float nsTOFPr = trkP.tofNSigmaPr(); const bool hasTofPr = hasTOFMatch(trkP); - const bool isProton = (std::abs(nsTPCPr) < lstarCutNsigmaTPCPr.value) && - (!hasTofPr || (std::abs(nsTOFPr) < lstarCutNsigmaTOFPr.value)); + const bool isProton = passFinalCandidatePID(ptP, + nsTPCPr, + nsTOFPr, + hasTofPr, + lstarCutNsigmaTPCPr.value, + lstarCutNsigmaTOFPr.value, + lstarPidCircularCutPr.value, + lstarPidPtRefPr.value, false); if (!isProton) { continue; } - const float ptP = trkP.pt(); - const float etaP = trkP.eta(); - const float phiP = trkP.phi(); + const double pP = static_cast(ptP) * std::cosh(static_cast(etaP)); + + if (lstarEnablePidQA.value != 0) { + histos.fill(HIST("hNsigmaTPCProtonVsP"), pP, nsTPCPr); + histos.fill(HIST("hNsigmaTOFProtonVsP"), pP, nsTOFPr); + histos.fill(HIST("hNsigmaTPCProtonVsPt"), ptP, nsTPCPr); + histos.fill(HIST("hNsigmaTOFProtonVsPt"), ptP, nsTOFPr); + if (hasTofPr) { + histos.fill(HIST("hTPCvsTOFNsigma_P"), nsTPCPr, nsTOFPr); + } + if constexpr (requires { trkP.beta(); }) { + const float beta = trkP.beta(); + if (hasTofPr && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsPt_P"), ptP, beta); + } + } + if constexpr (requires { trkP.dcaXY(); trkP.dcaZ(); }) { + histos.fill(HIST("hDCAxyVsPt_P"), ptP, trkP.dcaXY()); + histos.fill(HIST("hDCAzVsPt_P"), ptP, trkP.dcaZ()); + } + } const float pxP = ptP * std::cos(phiP); const float pyP = ptP * std::sin(phiP); const float pzP = ptP * std::sinh(etaP); - protonCands.push_back(ProtonCand{pxP, pyP, pzP, static_cast(trkP.sign()), static_cast(trkP.globalIndex())}); + protonCands.push_back(ProtonCand{.px = pxP, .py = pyP, .pz = pzP, .charge = static_cast(trkP.sign()), .tid = static_cast(trkP.globalIndex())}); } // Kaon candidates @@ -2601,20 +3154,27 @@ struct Lambdastarproxy { continue; } + // Kaon kinematics needed before PID because the PID strategy can depend on pT + const float ptK = trkK.pt(); + const float etaK = trkK.eta(); + const float phiK = trkK.phi(); + // PID for kaon candidates const float nsTPCK = trkK.tpcNSigmaKa(); const float nsTOFK = trkK.tofNSigmaKa(); const bool hasTofK = hasTOFMatch(trkK); - const bool isKaon = (std::abs(nsTPCK) < lstarCutNsigmaTPCKaon.value) && - (!hasTofK || (std::abs(nsTOFK) < lstarCutNsigmaTOFKaon.value)); + const bool isKaon = passFinalCandidatePID(ptK, + nsTPCK, + nsTOFK, + hasTofK, + lstarCutNsigmaTPCKaon.value, + lstarCutNsigmaTOFKaon.value, + lstarPidCircularCutKaon.value, + lstarPidPtRefKaon.value, false); if (!isKaon) { continue; } - // Kaon kinematics - const float ptK = trkK.pt(); - const float etaK = trkK.eta(); - const float phiK = trkK.phi(); const double pK = static_cast(ptK) * std::cosh(static_cast(etaK)); // Kaon QA @@ -2626,21 +3186,39 @@ struct Lambdastarproxy { histos.fill(HIST("hNsigmaTOFKaon"), nsTOFK); histos.fill(HIST("hNsigmaTPCKaonVsP"), pK, nsTPCK); histos.fill(HIST("hNsigmaTOFKaonVsP"), pK, nsTOFK); + histos.fill(HIST("hNsigmaTPCKaonVsPt"), ptK, nsTPCK); + histos.fill(HIST("hNsigmaTOFKaonVsPt"), ptK, nsTOFK); + if (hasTofK) { + histos.fill(HIST("hTPCvsTOFNsigma_K"), nsTPCK, nsTOFK); + } + if constexpr (requires { trkK.beta(); }) { + const float beta = trkK.beta(); + if (hasTofK && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsPt_K"), ptK, beta); + } + } + if constexpr (requires { trkK.dcaXY(); trkK.dcaZ(); }) { + histos.fill(HIST("hDCAxyVsPt_K"), ptK, trkK.dcaXY()); + histos.fill(HIST("hDCAzVsPt_K"), ptK, trkK.dcaZ()); + } } const float pxK = ptK * std::cos(phiK); const float pyK = ptK * std::sin(phiK); const float pzK = ptK * std::sinh(etaK); - kaonCands.push_back(KaonCand{pxK, pyK, pzK, static_cast(trkK.sign()), static_cast(trkK.globalIndex())}); + kaonCands.push_back(KaonCand{.px = pxK, .py = pyK, .pz = pzK, .charge = static_cast(trkK.sign()), .tid = static_cast(trkK.globalIndex())}); } - if (proxyCands.empty() || kaonCands.empty()) { - // still update mixing buffer so that later events can mix with this one + if (kaonCands.empty()) { + // Still update mixing buffer so that later events can mix with this one. + // The pK mixed-event background needs stored protons, while the proxy background + // needs stored proxy candidates. Therefore, do not require proxy candidates here. LStarMixEventEntry entry; entry.mult = eventMult; entry.zvtx = collision.posZ(); entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); entry.proxies = std::move(proxyCands); mLStarMixEvents.push_front(std::move(entry)); if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { @@ -2649,103 +3227,414 @@ struct Lambdastarproxy { return; } - // --- SAME-EVENT: genuine pK #Lambda^{*} candidates --- - for (auto const& pr : protonCands) { - for (auto const& k : kaonCands) { - if (pr.tid == k.tid) { - continue; - } - const double mass = invariantMass(pr.px, pr.py, pr.pz, MassProton, - k.px, k.py, k.pz, MassKaonCharged); + const bool hasProtonCandidates = !protonCands.empty(); + const bool hasProxyCandidates = !proxyCands.empty(); - const float pxTot = pr.px + k.px; - const float pyTot = pr.py + k.py; - const float pzTot = pr.pz + k.pz; - const float ptPair = std::sqrt(pxTot * pxTot + pyTot * pyTot); - const float phiPair = phiFromPxPy(pxTot, pyTot); + // --- SAME-EVENT: genuine pK #Lambda^{*} candidates --- + if (hasProtonCandidates) { + for (auto const& pr : protonCands) { + for (auto const& k : kaonCands) { + if (pr.tid == k.tid) { + continue; + } + const double mass = invariantMass(pr.px, pr.py, pr.pz, MassProton, + k.px, k.py, k.pz, MassKaonCharged); - const double eTot = std::sqrt(mass * mass + static_cast(pxTot) * pxTot + - static_cast(pyTot) * pyTot + static_cast(pzTot) * pzTot); - const float yPair = rapidityFromEPz(eTot, pzTot); + const float pxTot = pr.px + k.px; + const float pyTot = pr.py + k.py; + const float pzTot = pr.pz + k.pz; + const float ptPair = ptFromPxPy(pxTot, pyTot); - if (std::abs(yPair) > lstarLambdaAbsYMax.value) { - continue; - } + const float yPair = rapidityFromMomentumAndMass(pxTot, pyTot, pzTot, mass); - const bool unlikeSignPK = (pr.charge * k.charge) < 0; - if (unlikeSignPK) { - histos.fill(HIST("hInvMassPKUnlike"), mass); - histos.fill(HIST("hInvMassPKUnlikeVsPt"), mass, ptPair); - if (lstarEnableSparse.value != 0) { - histos.fill(HIST("hLambdaStarPKUnlikeSparse"), mass, ptPair, yPair, phiPair, eventMult); + if (std::abs(yPair) > lstarLambdaAbsYMax.value) { + continue; } - } else { - histos.fill(HIST("hInvMassPKLike"), mass); - histos.fill(HIST("hInvMassPKLikeVsPt"), mass, ptPair); - if (lstarEnableSparse.value != 0) { - histos.fill(HIST("hLambdaStarPKLikeSparse"), mass, ptPair, yPair, phiPair, eventMult); + + const bool unlikeSignPK = (pr.charge * k.charge) < 0; + if (unlikeSignPK) { + histos.fill(HIST("hInvMassPKUnlike"), mass); + if (lstarEnableSparse.value != 0) { + histos.fill(HIST("hLambdaStarPKUnlikeSparse"), mass, ptPair, eventMult); + } + } else { + histos.fill(HIST("hInvMassPKLike"), mass); + if (lstarEnableSparse.value != 0) { + histos.fill(HIST("hLambdaStarPKLikeSparse"), mass, ptPair, eventMult); + } } } } } // --- SAME-EVENT: proxy (d/2) + K --- - for (auto const& pr : proxyCands) { - for (auto const& k : kaonCands) { - if (pr.tid == k.tid) - continue; // sanity check: should never match, but just in case of bug in candidate-building logic - const double mass = invariantMass(pr.px, pr.py, pr.pz, MassProton, k.px, k.py, k.pz, MassKaonCharged); + if (hasProxyCandidates) { + for (auto const& pr : proxyCands) { + for (auto const& k : kaonCands) { + if (pr.tid == k.tid) { + continue; // sanity check: should never match, but just in case of bug in candidate-building logic + } + const double mass = invariantMass(pr.px, pr.py, pr.pz, MassProton, k.px, k.py, k.pz, MassKaonCharged); + // Same d-selected track, but now use its FULL measured + // momentum and assign the proton mass. + // + // No proton PID requirement is involved. + const double massFullPK = + invariantMass( + pr.pxFull, + pr.pyFull, + pr.pzFull, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTotFullPK = + pr.pxFull + k.px; + + const float pyTotFullPK = + pr.pyFull + k.py; + + const float pzTotFullPK = + pr.pzFull + k.pz; + + const float yFullPK = + rapidityFromMomentumAndMass( + pxTotFullPK, + pyTotFullPK, + pzTotFullPK, + massFullPK); + + const float pxTot = pr.px + k.px; + const float pyTot = pr.py + k.py; + const float pzTot = pr.pz + k.pz; + const float ptPair = ptFromPxPy(pxTot, pyTot); + + const float yProxy = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); - const float pxTot = pr.px + k.px; - const float pyTot = pr.py + k.py; - const float pzTot = pr.pz + k.pz; - const float ptPair = std::sqrt(pxTot * pxTot + pyTot * pyTot); - const float phiPair = phiFromPxPy(pxTot, pyTot); + if (std::abs(yProxy) > + lstarLambdaAbsYMax.value) { + continue; + } - const double eTot = std::sqrt(mass * mass + static_cast(pxTot) * pxTot + static_cast(pyTot) * pyTot + static_cast(pzTot) * pzTot); - const float yPair = rapidityFromEPz(eTot, pzTot); + const bool unlikeSignProxy = + (pr.charge * k.charge) < 0; - // Inclusive invariant-mass spectrum for the #Lambda^{*} proxy (d/2 + K) - histos.fill(HIST("hDeuteronProxyMass"), mass); - if (lstarEnableSparse.value != 0) { - histos.fill(HIST("hLambdaStarProxySparse"), mass, ptPair, yPair, phiPair, eventMult); + // Inclusive invariant-mass spectrum for the #Lambda^{*} proxy (d/2 + K) + histos.fill(HIST("hDeuteronProxyMass"), mass); + if (lstarEnableSparse.value != 0) { + if (lstarEnableProxyControls.value != 0) { + const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; + if (unlikeSignProxy) { + histos.fill(HIST("hLambdaStarProxyControlUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + histos.fill(HIST("hLambdaStarProxyControlFullPKUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } else { + histos.fill(HIST("hLambdaStarProxyControlLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + histos.fill(HIST("hLambdaStarProxyControlFullPKLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } + } + + if (unlikeSignProxy) { + + histos.fill( + HIST( + "hLambdaStarProxySparse"), + mass, + ptPair, + eventMult); + + } else { + + histos.fill( + HIST( + "hLambdaStarProxyLikeSparse"), + mass, + ptPair, + eventMult); + } + if (std::abs(yFullPK) <= + lstarLambdaAbsYMax.value) { + + if (unlikeSignProxy) { + + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKUnlikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + + } else { + + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKLikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } + } + if (pr.protonLike) { + + if (unlikeSignProxy) { + + histos.fill( + HIST("hLambdaStarProxyProtonLikeUnlikeSparse"), + mass, + ptPair, + eventMult); + + } else { + + histos.fill( + HIST("hLambdaStarProxyProtonLikeLikeSparse"), + mass, + ptPair, + eventMult); + } + } + } } } } - // --- MIXED-EVENT: current proxies + previous-event kaons --- + // --- MIXED-EVENT: current kaons + previous-event real protons --- + // This fills the standard pK mixed-event background. for (auto const& prev : mLStarMixEvents) { - if (std::abs(prev.zvtx - collision.posZ()) > lstarMixZvtxMax.value) + + if (std::abs(prev.zvtx - collision.posZ()) > + lstarMixZvtxMax.value) { continue; - if (std::abs(prev.mult - eventMult) > lstarMixMultMax.value) + } + + if (std::abs(prev.mult - eventMult) > + lstarMixMultMax.value) { continue; - if (prev.kaons.empty()) { + } + + if (prev.protons.empty()) { continue; } - for (auto const& pr : proxyCands) { - for (auto const& k : prev.kaons) { - // convention: mix for unlike-sign only (resonance background) + for (auto const& pr : prev.protons) { + + for (auto const& k : kaonCands) { + + // Unlike-sign pK mixed-event background. if ((pr.charge * k.charge) >= 0) { continue; } - if (pr.tid == k.tid) - continue; // sanity check: should never match, but just in case of bug in candidate-building logic - const double mass = invariantMass(pr.px, pr.py, pr.pz, MassProton, k.px, k.py, k.pz, MassKaonCharged); + const double mass = + invariantMass( + pr.px, + pr.py, + pr.pz, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTot = + pr.px + k.px; + + const float pyTot = + pr.py + k.py; + + const float pzTot = + pr.pz + k.pz; + + const float ptPair = + ptFromPxPy( + pxTot, + pyTot); + + const float yPair = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); + + if (std::abs(yPair) > + lstarLambdaAbsYMax.value) { + continue; + } - const float pxTot = pr.px + k.px; - const float pyTot = pr.py + k.py; - const float pzTot = pr.pz + k.pz; - const float ptPair = std::sqrt(pxTot * pxTot + pyTot * pyTot); - const float phiPair = phiFromPxPy(pxTot, pyTot); + if (lstarEnableSparse.value != 0) { - const double eTot = std::sqrt(mass * mass + static_cast(pxTot) * pxTot + static_cast(pyTot) * pyTot + static_cast(pzTot) * pzTot); - const float yPair = rapidityFromEPz(eTot, pzTot); + histos.fill( + HIST( + "hLambdaStarPKMixedSparse"), + mass, + ptPair, + eventMult); + } + } + } + } - // Fill mixed-event THnSparse (proxy only) - if (lstarEnableSparse.value != 0) { - histos.fill(HIST("hLambdaStarProxyMixedSparse"), mass, ptPair, yPair, phiPair, eventMult); + // --- MIXED-EVENT: current proxies + previous-event kaons --- + // This fills the deuteron-proxy mixed-event background. + // + // For every d-selected proxy candidate we calculate: + // + // 1) M_{(d/2)K} + // using p_proxy = p_track / 2 + // + // 2) M_{pK}^{full} + // using the FULL measured momentum of the same d-selected track, + // but assigning the proton mass. + // + // The standard proxy mixed spectrum requires only the proxy rapidity + // acceptance. The 2D proxy-vs-full-pK map additionally requires the + // full-pK hypothesis to satisfy the same rapidity acceptance. + if (hasProxyCandidates) { + + for (auto const& prev : mLStarMixEvents) { + + if (std::abs(prev.zvtx - collision.posZ()) > + lstarMixZvtxMax.value) { + continue; + } + + if (std::abs(prev.mult - eventMult) > + lstarMixMultMax.value) { + continue; + } + + if (prev.kaons.empty()) { + continue; + } + + for (auto const& pr : proxyCands) { + + for (auto const& k : prev.kaons) { + + // Unlike-sign proxy-K mixed-event background. + if ((pr.charge * k.charge) >= 0) { + continue; + } + + // ---------------------------------------------------------- + // Proxy hypothesis: p_proxy = p_d / 2 + // ---------------------------------------------------------- + + const double mass = + invariantMass( + pr.px, + pr.py, + pr.pz, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTot = + pr.px + k.px; + + const float pyTot = + pr.py + k.py; + + const float pzTot = + pr.pz + k.pz; + + const float ptPair = + ptFromPxPy( + pxTot, + pyTot); + + const float yProxy = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); + + // Standard proxy rapidity acceptance. + if (std::abs(yProxy) > + lstarLambdaAbsYMax.value) { + continue; + } + + // ---------------------------------------------------------- + // Full-momentum proton hypothesis for the SAME d-selected + // track. + // ---------------------------------------------------------- + + const double massFullPK = + invariantMass( + pr.pxFull, + pr.pyFull, + pr.pzFull, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTotFullPK = + pr.pxFull + k.px; + + const float pyTotFullPK = + pr.pyFull + k.py; + + const float pzTotFullPK = + pr.pzFull + k.pz; + + const float yFullPK = + rapidityFromMomentumAndMass( + pxTotFullPK, + pyTotFullPK, + pzTotFullPK, + massFullPK); + + if (lstarEnableSparse.value != 0) { + + if (lstarEnableProxyControls.value != 0) { + const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; + histos.fill(HIST("hLambdaStarProxyControlMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + histos.fill(HIST("hLambdaStarProxyControlFullPKMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } + + // Standard mixed-event proxy spectrum. + // + // This requires only the proxy hypothesis to satisfy + // |y| < lstarLambdaAbsYMax. + histos.fill( + HIST( + "hLambdaStarProxyMixedSparse"), + mass, + ptPair, + eventMult); + + // Mixed-event 2D map. + // + // Since this histogram explicitly represents both + // hypotheses, require the full-pK hypothesis to satisfy + // the same configurable rapidity acceptance as well. + if (std::abs(yFullPK) <= + lstarLambdaAbsYMax.value) { + + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKMixedSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } + } } } } @@ -2756,6 +3645,7 @@ struct Lambdastarproxy { entry.mult = eventMult; entry.zvtx = collision.posZ(); entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); entry.proxies = std::move(proxyCands); mLStarMixEvents.push_front(std::move(entry)); diff --git a/PWGCF/TwoParticleCorrelations/Tasks/particleOriginAnalysis.cxx b/PWGCF/TwoParticleCorrelations/Tasks/particleOriginAnalysis.cxx index 1b72e99ae3f..7360e956468 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/particleOriginAnalysis.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/particleOriginAnalysis.cxx @@ -25,7 +25,6 @@ #include "Common/Core/TableHelper.h" -#include #include #include #include @@ -37,7 +36,6 @@ #include #include #include -#include #include #include @@ -177,10 +175,9 @@ struct ParticleOriginAnalysis { /* self configure the desired species */ o2::analysis::dptdptfilter::PIDSpeciesSelection pidselector; - std::vector cfgnames = {"cfgElectronPIDSelection", "cfgMuonPIDSelection", "cfgPionPIDSelection", "cfgKaonPIDSelection", "cfgProtonPIDSelection"}; - std::vector spids = {0, 1, 2, 3, 4}; + std::vector cfgnames = {"cfgElectronPIDSelection", "cfgMuonPIDSelection", "cfgPionPIDSelection", "cfgKaonPIDSelection", "cfgProtonPIDSelection", "cfgDeuteronPIDSelection"}; for (uint i = 0; i < cfgnames.size(); ++i) { - auto includeIt = [&pidselector, &initContext](int spid, auto name) { + auto includeIt = [&pidselector, &initContext](int spid, const auto& name) { bool mUseIt = false; bool mExcludeIt = false; if (getTaskOptionValue(initContext, "dpt-dpt-filter-tracks", TString::Format("%s.mUseIt", name.c_str()).Data(), mUseIt, false) && @@ -193,7 +190,7 @@ struct ParticleOriginAnalysis { } } }; - includeIt(spids[i], cfgnames[i]); + includeIt(i, cfgnames[i]); } uint nspecies = pidselector.getNSpecies(); if (nspecies == 0) { diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx index b3efdcc4480..064272be3d0 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidDiHadron.cxx @@ -14,11 +14,10 @@ /// \author Preet Bhanjan Pati (preet.bhanjan.pati@cern.ch), Zhiyong Lu (zhiyong.lu@cern.ch) /// \since July/29/2025 -#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" -#include "Common/CCDB/TriggerAliases.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -51,14 +50,12 @@ #include #include #include -#include - -#include #include #include #include #include +#include #include #include @@ -68,13 +65,13 @@ using namespace o2::framework::expressions; using namespace constants::math; // define the filtered collisions and tracks -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) template -auto readMatrix(Array2D const& mat, P& array, int rowStart, int rowEnd, int colStart, int colEnd) +auto readMatrix(Array2D const& mat, P& array) { - for (auto i = rowStart; i < rowEnd; ++i) { - for (auto j = colStart; j < colEnd; ++j) { + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { array[i][j] = mat(i, j); } } @@ -82,57 +79,83 @@ auto readMatrix(Array2D const& mat, P& array, int rowStart, int rowEnd, int c return; } -static constexpr float LongArrayFloat[3][20] = {{1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {2.1, 2.2, 2.3, -2.1, -2.2, -2.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}, {3.1, 3.2, 3.3, -3.1, -3.2, -3.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2, 1.3, -1.1, -1.2, -1.3, 1.1, 1.2}}; -static constexpr int LongArrayInt[3][20] = {{1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {2, 2, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}, {3, 3, 3, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1}}; +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; struct PidDiHadron { o2::aod::ITSResponse itsResponse; - Service ccdb; - - O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 10.0f, "maximum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for ref tracks") - O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 3.0f, "Maximal pT for ref tracks") - O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgTpcCluster, float, 50.0f, "minimum TPC clusters") - O2_DEFINE_CONFIGURABLE(cfgTpcCrossRows, float, 70.0f, "minimum TPC crossed rows") - O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.5f, "Minimum pt to use TOF N-sigma") - O2_DEFINE_CONFIGURABLE(cfgTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") - O2_DEFINE_CONFIGURABLE(cfgITScluster, float, 5.0f, "minimum ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMax, int, 2000, "Minimum occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMin, int, 0, "Maximum occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgFakeKaonCut, float, 0.1f, "Maximum difference in measured momentum and TPC inner ring momentum of particle") - O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "max DCA to vertex z") - O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") - O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") - O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") - O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") - O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") - O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") - O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") - O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, false, "Use additional event cut on mult correlations") - O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") - O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") - O2_DEFINE_CONFIGURABLE(cfgVerbosity, bool, false, "Verbose output") - O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, false, "enable trigger pT < associated pT cut") - O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, false, "enable trigger pT < associated pT cut in mixed event") - O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, true, "Use ITS PID for particle identification") - O2_DEFINE_CONFIGURABLE(cfgUseOnlyTPC, bool, true, "Use only TPC PID for daughter selection") - O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 6 = phi, 0 for no PID") - O2_DEFINE_CONFIGURABLE(cfgGetNsigmaQA, bool, true, "Get QA histograms for selection of pions, kaons, and protons") - O2_DEFINE_CONFIGURABLE(cfgGetdEdx, bool, true, "Get dEdx histograms for pions, kaons, and protons") - O2_DEFINE_CONFIGURABLE(cfgUseAntiLambda, bool, true, "Use AntiLambda candidates for analysis") - O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Service ccdb{}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAsscPtMin, float, 0.2f, "Minimal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgAsscPtMax, float, 3.0f, "Maximal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.0, "Merging cut on track merge") + O2_DEFINE_CONFIGURABLE(cfgUseCutMerging, bool, false, "use two track cut"); + O2_DEFINE_CONFIGURABLE(cfgMixEventNumMin, int, 5, "Minimum number of events to mix") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, false, "enable trigger pT < associated pT cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, false, "enable trigger pT < associated pT cut in mixed event") + } cfgGeneral; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); + O2_DEFINE_CONFIGURABLE(cfgUseTrigWeight, bool, false, "enable trigger efficiency correction") + } cfgEffWeights; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMax, int, 2000, "Maximum occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyMin, int, 0, "Minimum occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgCutMultMin, int, 0, "Minimum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgCutMultMax, int, 10, "Maximum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FV0A") + O2_DEFINE_CONFIGURABLE(cfgCentTableUnavailable, bool, false, "if a dataset does not provide centrality information") + O2_DEFINE_CONFIGURABLE(cfgCutCentMin, float, 60.0f, "Minimum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgCutCentMax, float, 80.0f, "Maximum centrality for collision") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event"); + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + O2_DEFINE_CONFIGURABLE(cfgMCTrigIsPhysPrim, bool, false, "Switch for using only physical primary trigger particles in MC correlations"); + O2_DEFINE_CONFIGURABLE(cfgMCAsscIsPhysPrim, bool, false, "Switch for using only physical primary associated particles in MC correlations"); + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQAdEdx, bool, false, "Get dEdx histograms for pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgQANsigma, bool, false, "Get QA histograms for selection of pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgPIDTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgPIDUseIts, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + O2_DEFINE_CONFIGURABLE(cfgPIDOnlyTpcCut, float, 3.0f, "TPC N-sigma cut for pions, kaons, protons") + O2_DEFINE_CONFIGURABLE(cfgPIDUseOnlyTpc, bool, true, "Use only TPC PID for daughter selection") + O2_DEFINE_CONFIGURABLE(cfgPIDParticle, int, 0, "1 = pion, 2 = kaon, 3 = proton, 4 = kshort, 5 = lambda, 0 for no PID") + O2_DEFINE_CONFIGURABLE(cfgPIDUseReso, bool, false, "Switch to turn on resonance analysis") + O2_DEFINE_CONFIGURABLE(cfgPIDNclsPIDCut, int, 70, "Number of clusters used for PID selection") + O2_DEFINE_CONFIGURABLE(cfgPIDUseNclsPID, bool, false, "Switch for using cfgPIDNclsPIDCut") + Configurable> cfgPIDnSigmas{"cfgPIDnSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + Configurable> cfgPIDResoCuts{"cfgPIDResoCuts", {LongArrayFloat.front().data(), 15, 2, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "RapidityMin", "RapidityMax", "ArmPodMinVal", "DCABetDaug", "CompMassRej"}, {"K0", "Lambda"}}, "Labeled array (float) for various cuts on resonances"}; + Configurable> cfgPIDResoSwitches{"cfgPIDResoSwitches", {LongArrayInt.front().data(), 7, 2, {"UseCosPA", "NMassBins", "UseDCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut", "UseCompMassRej"}, {"K0", "Lambda"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgPIDConfigs; struct : ConfigurableGroup { O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); @@ -149,208 +172,229 @@ struct PidDiHadron { O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; - O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"); - O2_DEFINE_CONFIGURABLE(cfgDCAxy, std::string, "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"); std::vector multT0CCutPars; std::vector multPVT0CCutPars; std::vector multGlobalPVCutPars; std::vector multMultV0ACutPars; - TF1* fMultPVT0CCutLow = nullptr; - TF1* fMultPVT0CCutHigh = nullptr; - TF1* fMultT0CCutLow = nullptr; - TF1* fMultT0CCutHigh = nullptr; - TF1* fMultGlobalPVCutLow = nullptr; - TF1* fMultGlobalPVCutHigh = nullptr; - TF1* fMultMultV0ACutLow = nullptr; - TF1* fMultMultV0ACutHigh = nullptr; - TF1* fT0AV0AMean = nullptr; - TF1* fT0AV0ASigma = nullptr; - TF1* fPtDepDCAxy = nullptr; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") } cfgFuncParas; SliceCache cache; + // Axes for general QA ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity axis for histograms"}; - ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for histograms"}; - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {100, 0, 1000}, "multiplicity axis for histograms"}; + ConfigurableAxis axisCentrality{"axisCentrality", {100, 0, 100}, "centrality axis for histograms"}; + ConfigurableAxis axisPt{"axisPt", {98, 0.2, 10.0}, "pt axis for histograms"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; ConfigurableAxis axisDeltaEta{"axisDeltaEta", {48, -2.4, 2.4}, "delta eta axis for histograms"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; - ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt associated axis for histograms"}; + ConfigurableAxis axisParticle{"axisParticle", {4, 0, 4}, "particle axis for correlation container"}; ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; - ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; + + // Axes for PID QA ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; ConfigurableAxis axisNsigmaITS{"axisNsigmaITS", {80, -5, 5}, "nsigmaITS axis"}; ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; ConfigurableAxis axisSigma{"axisSigma", {200, 0, 20}, "sigma axis for TPC"}; - Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt[0], 15, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut", "kIsVertexITSTPC", "kTVXinTRD"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; - Configurable> nSigmas{"nSigmas", {LongArrayFloat[0], 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; - Configurable> resonanceCuts{"resonanceCuts", {LongArrayFloat[0], 12, 3, {"cos_PAs", "massMin", "massMax", "PosTrackPt", "NegTrackPt", "DCAPosToPVMin", "DCANegToPVMin", "Lifetime", "RadiusMin", "RadiusMax", "Rapidity", "ArmPodMinVal"}, {"K0", "Lambda", "Phi"}}, "Labeled array (float) for various cuts on resonances"}; - Configurable> resonanceSwitches{"resonanceSwitches", {LongArrayInt[0], 6, 3, {"UseCosPA", "NMassBins", "DCABetDaug", "UseProperLifetime", "UseV0Radius", "UseArmPodCut"}, {"K0", "Lambda", "Phi"}}, "Labeled array (int) for various cuts on resonances"}; - - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {1, 0, 1}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {1, 0, 1}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {1, 0, 1}, "pt axis for efficiency histograms"}; - // make the filters and cuts. - Filter collisionFilter = (nabs(aod::collision::posZ) < cfgCutVertex); - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtPOIMin) && (aod::track::pt < cfgCutPtPOIMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter mcCollisionFilter = (nabs(aod::mccollision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz); + Filter mcTrackFilter = (nabs(aod::mcparticle::eta) < cfgTrkSel.cfgGlobalEta) && (aod::mcparticle::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::mcparticle::pt < cfgTrkSel.cfgGlobalPtMax); using FilteredCollisions = soa::Filtered>; using FilteredTracks = soa::Filtered>; - using FilteredMcTracks = soa::Filtered>; + using FilteredTracksMcLabels = soa::Filtered>; using V0TrackCandidate = aod::V0Datas; + using FilteredMcCollisions = soa::Filtered; + using FilteredMcTracks = soa::Filtered; + using SmallGroupMcCollisions = soa::SmallGroups>; + + Partition asscTracks = (aod::track::pt > cfgGeneral.cfgAsscPtMin) && (aod::track::pt < cfgGeneral.cfgAsscPtMax); + Partition asscTracksMC = (aod::mcparticle::pt > cfgGeneral.cfgAsscPtMin) && (aod::mcparticle::pt < cfgGeneral.cfgAsscPtMax); - Preslice perCollision = aod::track::collisionId; - Preslice perCollisionV0 = aod::v0::collisionId; + PresliceUnsorted collisionPerMCCollision = aod::mccollisionlabel::mcCollisionId; // Corrections TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; TH1D* mCentralityWeight = nullptr; bool correctionsLoaded = false; // Define the outputs - OutputObj same{"sameEvent"}; - OutputObj mixed{"mixedEvent"}; - OutputObj sameReso{"sameEventReso"}; - OutputObj mixedReso{"mixedEventReso"}; + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + OutputObj sameReso{"sameEventReso"}; + OutputObj mixedReso{"mixedEventReso"}; HistogramRegistry histos{"histos"}; // define global variables - TRandom3* gRandom = new TRandom3(); + TRandom3 fRandom{0}; enum PIDIndex { - kCharged = 0, - kPions, - kKaons, - kProtons, - kK0, - kLambda, - kPhi + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda }; enum PiKpArrayIndex { - iPionUp = 0, - iKaonUp, - iProtonUp, - iPionLow, - iKaonLow, - iProtonLow + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow }; enum ResoArrayIndex { - iK0 = 0, - iLambda = 1, - iPhi = 2, - NResoParticles = 3 + IndexK0 = 0, + IndexLambda, + NumResoParticles }; enum ResoParticleCuts { - kCosPA = 0, - kMassMin, - kMassMax, - kPosTrackPt, - kNegTrackPt, - kDCAPosToPVMin, - kDCANegToPVMin, - kLifeTime, - kRadiusMin, - kRadiusMax, - kRapidity, - kArmPodMinVal, - kNParticleCuts + CutCosPA = 0, + CutMassMin, + CutMassMax, + CutPosTrackPt, + CutNegTrackPt, + CutDCAPosToPVMin, + CutDCANegToPVMin, + CutLifeTime, + CutRadiusMin, + CutRadiusMax, + CutRapidityMin, + CutRapidityMax, + CutArmPodMinVal, + CutDCABetDaug, + CutCompMassRej, + NumParticleCuts }; enum ResoParticleSwitches { - kUseCosPA = 0, - kMassBins, - kDCABetDaug, - kUseProperLifetime, - kUseV0Radius, - kUseArmPodCut, - kNParticleSwitches + UseCosPA = 0, + UseMassBins, + UseDCABetDaug, + UseProperLifetime, + UseV0Radius, + UseArmPodCut, + UseCompMassRej, + NumParticleSwitches }; + + enum ResoSelectionFlags { + FillCandidate = 0, + FillDaughterPt, + FillMassCut, + FillRapidityCut, + FillDCAtoPV, + FillDCAxDaughters, + FillV0Radius, + FillCosPA, + FillProperLifetime, + FillArmPodCut, + FillCompetingMass, + FillDaughterTrackSelected, + }; + enum EventCutTypes { - kFilteredEvents = 0, - kAfterSel8, - kUseNoTimeFrameBorder, - kUseNoITSROFrameBorder, - kUseNoSameBunchPileup, - kUseGoodZvtxFT0vsPV, - kUseNoCollInTimeRangeStandard, - kUseGoodITSLayersAll, - kUseNoCollInRofStandard, - kUseNoHighMultCollInPrevRof, - kUseOccupancy, - kUseMultCorrCut, - kUseT0AV0ACut, - kUseVertexITSTPC, - kUseTVXinTRD, - kNEventCuts + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + NumEventCuts + }; + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes }; enum CentEstimators { - kCentFT0C = 0, - kCentFT0CVariant1, - kCentFT0M, - kCentFV0A, - // Count the total number of enum - kCount_CentEstimators + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + NumCentEstimators }; enum EventType { SameEvent = 1, MixedEvent = 3 }; enum DetectorType { - kTPC = 0, - kTOF, - kITS + UseTPC = 0, + UseTOF, + UseITS }; enum EventCutType { - kEvCut1 = 0, - kNEvCutTypes = 1 + EvCut1 = 0, + NumEvCutTypes = 1 + }; + enum Stage { + Before = 0, + After }; - std::array, 15> eventCuts; - std::array, 12> resoCutVals; - std::array, 7> resoSwitchVals; - std::array tofNsigmaCut; - std::array itsNsigmaCut; - std::array tpcNsigmaCut; - + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + std::array, 15> resoCutVals{}; + std::array, 7> resoSwitchVals{}; // persistent caches std::vector efficiencyAssociatedCache; + std::vector useAssociatedCache; void init(InitContext&) { - readMatrix(resonanceCuts->getData(), resoCutVals, kCosPA, kNParticleCuts, iK0, NResoParticles); - readMatrix(resonanceSwitches->getData(), resoSwitchVals, kUseCosPA, kNParticleSwitches, iK0, NResoParticles); - readMatrix(cfgUseEventCuts->getData(), eventCuts, kFilteredEvents, kNEventCuts, kEvCut1, kNEvCutTypes); - - tpcNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kTPC]; - tpcNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kTPC]; - tpcNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kTPC]; - tpcNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kTPC]; - tpcNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kTPC]; - tpcNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kTPC]; - - tofNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kTOF]; - tofNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kTOF]; - tofNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kTOF]; - tofNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kTOF]; - tofNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kTOF]; - tofNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kTOF]; - - itsNsigmaCut[iPionUp] = nSigmas->getData()[iPionUp][kITS]; - itsNsigmaCut[iKaonUp] = nSigmas->getData()[iKaonUp][kITS]; - itsNsigmaCut[iProtonUp] = nSigmas->getData()[iProtonUp][kITS]; - itsNsigmaCut[iPionLow] = nSigmas->getData()[iPionLow][kITS]; - itsNsigmaCut[iKaonLow] = nSigmas->getData()[iKaonLow][kITS]; - itsNsigmaCut[iProtonLow] = nSigmas->getData()[iProtonLow][kITS]; - - AxisSpec axisK0Mass = {resoSwitchVals[kMassBins][iK0], resoCutVals[kMassMin][iK0], resoCutVals[kMassMax][iK0]}; - AxisSpec axisLambdaMass = {resoSwitchVals[kMassBins][iLambda], resoCutVals[kMassMin][iLambda], resoCutVals[kMassMax][iLambda]}; - AxisSpec axisPhiMass = {resoSwitchVals[kMassBins][iPhi], resoCutVals[kMassMin][iPhi], resoCutVals[kMassMax][iPhi]}; - - if (cfgCentTableUnavailable && !cfgSelCollByNch) { + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.cfgPIDnSigmas->getData(), nSigmaVals); + readMatrix(cfgPIDConfigs.cfgPIDResoCuts->getData(), resoCutVals); + readMatrix(cfgPIDConfigs.cfgPIDResoSwitches->getData(), resoSwitchVals); + + if (cfgEvSel.cfgCentTableUnavailable && !cfgEvSel.cfgSelCollByNch) { LOGF(fatal, "Centrality table is unavailable, cannot select collisions by centrality"); } const AxisSpec axisPhi{72, 0.0, constants::math::TwoPI, "#varphi"}; @@ -363,238 +407,280 @@ struct PidDiHadron { LOGF(info, "Starting init"); - // Creating mass axis depending on particle - 4 = kshort, 5 = lambda, 6 = phi - AxisSpec massAxisReso = {10, 0, 1, "mass"}; - if (cfgPIDParticle == kK0) - massAxisReso = {resoSwitchVals[kMassBins][iK0], resoCutVals[kMassMin][iK0], resoCutVals[kMassMax][iK0], "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; - if (cfgPIDParticle == kLambda) - massAxisReso = {resoSwitchVals[kMassBins][iLambda], resoCutVals[kMassMin][iLambda], resoCutVals[kMassMax][iLambda], "M_{p#pi^{-}} (GeV/c^{2})"}; - if (cfgPIDParticle == kPhi) - massAxisReso = {resoSwitchVals[kMassBins][iPhi], resoCutVals[kMassMin][iPhi], resoCutVals[kMassMax][iPhi], "M_{K^{+}K^{-}} (GeV/c^{2})"}; + // Creating mass axis depending on particle - 4 = kshort, 5 = lambda + AxisSpec axisInvMass = {10, 0, 1, "mass"}; + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + const auto bins = resoSwitchVals[UseMassBins][IndexK0]; + const auto min = resoCutVals[CutMassMin][IndexK0]; + const auto max = resoCutVals[CutMassMax][IndexK0]; + + axisInvMass = {bins, min, max, "M_{#pi^{+}#pi^{-}} (GeV/c^{2})"}; + } + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + const auto bins = resoSwitchVals[UseMassBins][IndexLambda]; + const auto min = resoCutVals[CutMassMin][IndexLambda]; + const auto max = resoCutVals[CutMassMax][IndexLambda]; + + axisInvMass = {bins, min, max, "M_{p#pi} (GeV/c^{2})"}; + } + + // Set the sample axis + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } // Event Counter - if ((doprocessSame || doprocessSameReso || doprocessMC) && cfgUseAdditionalEventCut) { + if ((doprocessSame || doprocessSameReso || doprocessMCPID) && cfgEvSel.cfgUseAdditionalEventCut) { histos.add("hEventCount", "Number of Events;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kFilteredEvents + 1, "Filtered event"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kAfterSel8 + 1, "After sel8"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoSameBunchPileup + 1, "kNoSameBunchPileup"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoCollInRofStandard + 1, "kNoCollInRofStandard"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseOccupancy + 1, "Occupancy Cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseMultCorrCut + 1, "MultCorrelation Cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseT0AV0ACut + 1, "T0AV0A cut"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseVertexITSTPC + 1, "kIsVertexITSTPC"); - histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(kUseTVXinTRD + 1, "kTVXinTRD"); - } - - if (cfgPIDParticle == kK0) { // For K0 - histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + } + + if (cfgPIDConfigs.cfgPIDUseReso && cfgPIDConfigs.cfgPIDParticle == UseK0) { // For K0 + histos.add("PiPlusTPC_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiPlusTOF_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_K0", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); histos.add("hK0Phi", "", {HistType::kTH1D, {axisPhi}}); histos.add("hK0Eta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{11, 0, 11}}}); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(1, "K0 candidates"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(7, "V0radius"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(8, "CosPA"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(10, "ArmenterosPod"); - histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(11, "Daughter track selection"); - } - if (cfgPIDParticle == kLambda) { // For Lambda - histos.add("PrPlusTPC_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PiMinusTPC_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("PrPlusTOF_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("PiMinusTOF_L", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); + histos.add("hK0Count", "Number of K0;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hK0Count"))->GetYaxis()->SetTitle("Counts"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "K0 candidates"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hK0Count"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); + } + if (cfgPIDConfigs.cfgPIDUseReso && cfgPIDConfigs.cfgPIDParticle == UseLambda) { // For Lambda + histos.add("PrPlusTPC_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PiMinusTPC_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTPC}}}); + histos.add("PrPlusTOF_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); + histos.add("PiMinusTOF_L", "", {HistType::kTH2D, {{axisPtTrigger, axisNsigmaTOF}}}); histos.add("hLambdaPhi", "", {HistType::kTH1D, {axisPhi}}); histos.add("hLambdaEta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{10, 0, 10}}}); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(1, "Lambda candidates"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(2, "Daughter pt"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(3, "Mass cut"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(4, "Rapidity cut"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(5, "DCA to PV"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(6, "DCA between daughters"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(7, "V0radius"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(8, "CosPA"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(9, "Proper lifetime"); - histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(10, "Daughter track selection"); - } - if (cfgPIDParticle == kPhi) { // For Phi - histos.add("KaPlusTPC", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("KaMinusTPC", "", {HistType::kTH2D, {{axisPt, axisNsigmaTPC}}}); - histos.add("KaPlusTOF", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("KaMinusTOF", "", {HistType::kTH2D, {{axisPt, axisNsigmaTOF}}}); - histos.add("hPhiPhi", "", {HistType::kTH1D, {axisPhi}}); - histos.add("hPhiEta", "", {HistType::kTH1D, {axisEta}}); - histos.add("hPhiMass_sparse", "", {HistType::kTHnSparseD, {{axisPhiMass, axisPt, axisMultiplicity}}}); - - histos.add("hPhiCount", "Number of Phi;; Count", {HistType::kTH1D, {{5, 0, 5}}}); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(1, "Phi candidates"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(2, "Daughter track selection"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(3, "Fake Kaon"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(4, "CosPA"); - histos.get(HIST("hPhiCount"))->GetXaxis()->SetBinLabel(5, "Rapidity cut"); + histos.add("hLambdaCount", "Number of Lambda;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hLambdaCount"))->GetYaxis()->SetTitle("Counts"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCandidate + 1, "Lambda candidates"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterPt + 1, "Daughter pt"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillMassCut + 1, "Mass cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillRapidityCut + 1, "Rapidity cut"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAtoPV + 1, "DCA to PV"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDCAxDaughters + 1, "DCA between daughters"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillV0Radius + 1, "V0radius"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCosPA + 1, "CosPA"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillProperLifetime + 1, "Proper lifetime"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillArmPodCut + 1, "ArmenterosPod"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillCompetingMass + 1, "CompetingMassRejection"); + histos.get(HIST("hLambdaCount"))->GetXaxis()->SetBinLabel(FillDaughterTrackSelected + 1, "Daughter track selection"); } // Multiplicity correlation cuts - if (eventCuts[kUseMultCorrCut][kEvCut1]) { + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; - cfgFuncParas.fMultPVT0CCutLow = new TF1("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutLow->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); - cfgFuncParas.fMultPVT0CCutHigh = new TF1("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultPVT0CCutHigh->SetParameters(&(cfgFuncParas.multPVT0CCutPars[0])); - - cfgFuncParas.fMultT0CCutLow = new TF1("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutLow->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); - cfgFuncParas.fMultT0CCutHigh = new TF1("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); - cfgFuncParas.fMultT0CCutHigh->SetParameters(&(cfgFuncParas.multT0CCutPars[0])); - - cfgFuncParas.fMultGlobalPVCutLow = new TF1("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutLow->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); - cfgFuncParas.fMultGlobalPVCutHigh = new TF1("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(&(cfgFuncParas.multGlobalPVCutPars[0])); - - cfgFuncParas.fMultMultV0ACutLow = new TF1("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutLow->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); - cfgFuncParas.fMultMultV0ACutHigh = new TF1("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); - cfgFuncParas.fMultMultV0ACutHigh->SetParameters(&(cfgFuncParas.multMultV0ACutPars[0])); - } - if (eventCuts[kUseT0AV0ACut][kEvCut1]) { - cfgFuncParas.fT0AV0AMean = new TF1("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); - cfgFuncParas.fT0AV0ASigma = new TF1("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); } - cfgFuncParas.fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgFuncParas.cfgDCAxy->c_str()), 0.001, 100); - cfgFuncParas.fPtDepDCAxy->SetParameter(0, cfgFuncParas.cfgDCAxyNSigma); - LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", cfgFuncParas.cfgDCAxy->c_str())); + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgEvSel.cfgCentEstimator); - std::string hCentTitle = "Centrality distribution, Estimator " + std::to_string(cfgCentEstimator); // Make histograms to check the distributions after cuts if (doprocessSame || doprocessSameReso) { - histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); - histos.add("Nch_used", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event + histos.add("Nch_corrected", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); histos.add("Centrality", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); histos.add("CentralityWeighted", hCentTitle.c_str(), {HistType::kTH1D, {{100, 0, 100}}}); - histos.add("Centrality_used", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); // histogram to see how many events are in the same and mixed event histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); - histos.add("zVtx_used", "zVtx_used", {HistType::kTH1D, {axisVertex}}); - - if (cfgPIDParticle == kCharged || cfgPIDParticle == kPions || cfgPIDParticle == kKaons || cfgPIDParticle == kProtons) { - histos.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); - histos.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); - histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); - histos.add("pT", "pT", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("pTCorrected", "pTCorrected", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); - - if (cfgGetNsigmaQA) { - if (!cfgUseItsPID) { - histos.add("TofTpcNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPt}}}); - histos.add("TofTpcNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPt}}}); - - if (cfgGetdEdx) { - histos.add("TpcdEdx_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpSigma_ptwise", "", {HistType::kTHnSparseD, {{axisPt, axisSigma, axisNsigmaTOF}}}); - - histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPt, axisSigma, axisNsigmaTOF}}}); - } - } // TPC-TOF PID QA hists - if (cfgUseItsPID) { - histos.add("TofItsNsigma_before", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPt}}}); - histos.add("TofItsNsigma_after", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPt}}}); - } // ITS-TOF PID QA hists - } // end of PID QA hists + histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + } + + if (doprocessSame) { + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); } - if (cfgPIDParticle == kK0 || cfgPIDParticle == kLambda || cfgPIDParticle == kPhi) { - histos.add("Trig_histReso", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger, massAxisReso}}}); + if (cfgPIDConfigs.cfgQANsigma) { + if (!cfgPIDConfigs.cfgPIDUseIts) { // TPC-TOF PID QA hists + histos.add("TofTpcNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofTpcNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } + if (cfgPIDConfigs.cfgPIDUseIts) { // ITS-TOF PID QA hists + histos.add("TofItsNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofItsNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } + } // end of PID nsigma QA hists + if (cfgPIDConfigs.cfgQAdEdx) { + histos.add("TpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + + histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + } // end of PID dEdx QA hists + + // Trigger histogram + histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + + if (doprocessSameMC) { + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); + histos.add("Centrality", hCentTitle.c_str(), {HistType::kTH1D, {axisCentrality}}); + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("deltaEta_deltaPhi_same", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // check to see the delta eta and delta phi distribution + + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPt, axisParticle, axisEventClass}}); } + histos.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); } - if (doprocessMixed || doprocessMixedReso) { + + if (doprocessSameReso) { + if (cfgPIDConfigs.cfgPIDParticle == UseK0 || cfgPIDConfigs.cfgPIDParticle == UseLambda) { + histos.add("Trig_histReso", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisInvMass}}}); + } + } + + if (doprocessMixed || doprocessMixedReso || doprocessMixedMC) { histos.add("deltaEta_deltaPhi_mixed", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); } - if (doprocessMC) { - histos.add("hNsigmaPionTruePositives", "hNsigmaPionTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are pions and selected as pions - histos.add("hNsigmaKaonTruePositives", "hNsigmaKaonTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are kaons and selected as kaons - histos.add("hNsigmaProtonTruePositives", "hNsigmaProtonTruePositives", {HistType::kTH1D, {axisPt}}); // Fraction of particles that are protons and selected as protons - histos.add("hNsigmaPionSelected", "hNsigmaPionSelected", {HistType::kTH1D, {axisPt}}); - histos.add("hNsigmaKaonSelected", "hNsigmaKaonSelected", {HistType::kTH1D, {axisPt}}); - histos.add("hNsigmaProtonSelected", "hNsigmaProtonSelected", {HistType::kTH1D, {axisPt}}); + if (doprocessMCPID) { + histos.add("hNsigmaPionTruePositives", "hNsigmaPionTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are pions and selected as pions + histos.add("hNsigmaKaonTruePositives", "hNsigmaKaonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are kaons and selected as kaons + histos.add("hNsigmaProtonTruePositives", "hNsigmaProtonTruePositives", {HistType::kTH1D, {axisPtTrigger}}); // Fraction of particles that are protons and selected as protons + + histos.add("hNsigmaPionSelected", "hNsigmaPionSelected", {HistType::kTH1D, {axisPtTrigger}}); + histos.add("hNsigmaKaonSelected", "hNsigmaKaonSelected", {HistType::kTH1D, {axisPtTrigger}}); + histos.add("hNsigmaProtonSelected", "hNsigmaProtonSelected", {HistType::kTH1D, {axisPtTrigger}}); - histos.add("hNsigmaPionTrue", "hNsigmaPionTrue", {HistType::kTH1D, {axisPt}}); // All true pions from MC - histos.add("hNsigmaKaonTrue", "hNsigmaKaonTrue", {HistType::kTH1D, {axisPt}}); // All true kaons from MC - histos.add("hNsigmaProtonTrue", "hNsigmaProtonTrue", {HistType::kTH1D, {axisPt}}); // All true protons from MC + histos.add("hNsigmaPionTrue", "hNsigmaPionTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true pions from MC + histos.add("hNsigmaKaonTrue", "hNsigmaKaonTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true kaons from MC + histos.add("hNsigmaProtonTrue", "hNsigmaProtonTrue", {HistType::kTH1D, {axisPtTrigger}}); // All true protons from MC - histos.add("TpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("ExpTpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_pi", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_ka", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("ExpTpcdEdx_ptwise_pr", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pi_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_ka_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_pr_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pi_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_ka_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_pr_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); - histos.add("TpcdEdx_ptwise_all_physprim", "", {HistType::kTHnSparseD, {{axisPt, axisTpcSignal, axisNsigmaTOF}}}); + histos.add("TpcdEdx_ptwise_all_physprim", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF}}}); } histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event LOGF(info, "Initializing correlation container"); - std::vector corrAxis = {{axisSample, "Sample"}, + std::vector corrAxis = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEta, "#Delta#eta"}}; - std::vector corrAxisReso = {{axisSample, "Sample"}, + std::vector corrAxisReso = {{axisSample}, {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, {axisPtTrigger, "p_{T} (GeV/c)"}, - {massAxisReso}, + {axisInvMass}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEta, "#Delta#eta"}}; - std::vector effAxis = { - {axisEtaEfficiency, "#eta"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisVertexEfficiency, "z-vtx (cm)"}, - }; - std::vector userAxis; - if (cfgPIDParticle == kCharged || cfgPIDParticle == kPions || cfgPIDParticle == kKaons || cfgPIDParticle == kProtons) { - same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userAxis)); + if (!cfgPIDConfigs.cfgPIDUseReso) { + same.setObject(new DihadronContainer("sameEvent", "sameEvent", corrAxis)); + mixed.setObject(new DihadronContainer("mixedEvent", "mixedEvent", corrAxis)); } - if (cfgPIDParticle == kK0 || cfgPIDParticle == kLambda || cfgPIDParticle == kPhi) { - sameReso.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisReso, effAxis, userAxis)); - mixedReso.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisReso, effAxis, userAxis)); + if (cfgPIDConfigs.cfgPIDUseReso && (cfgPIDConfigs.cfgPIDParticle == UseK0 || cfgPIDConfigs.cfgPIDParticle == UseLambda)) { + sameReso.setObject(new DihadronContainer("sameEvent", "sameEvent", corrAxisReso)); + mixedReso.setObject(new DihadronContainer("mixedEvent", "mixedEvent", corrAxisReso)); } LOGF(info, "End of init"); } @@ -617,18 +703,18 @@ struct PidDiHadron { template float getCentrality(TCollision const& collision) { - float cent; - switch (cfgCentEstimator) { - case kCentFT0C: + float cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: cent = collision.centFT0C(); break; - case kCentFT0CVariant1: + case UseCentFT0CVariant1: cent = collision.centFT0CVariant1(); break; - case kCentFT0M: + case UseCentFT0M: cent = collision.centFT0M(); break; - case kCentFV0A: + case UseCentFV0A: cent = collision.centFV0A(); break; default: @@ -638,66 +724,119 @@ struct PidDiHadron { } template - bool trackSelected(TTrack track) + bool trackSelected(TTrack const& track) { - if (cfgFuncParas.cfgDCAxyNSigma && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt()))) + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { return false; - return ((track.tpcNClsFound() >= cfgTpcCluster) && (track.tpcNClsCrossedRows() >= cfgTpcCrossRows) && (track.itsNCls() >= cfgITScluster)); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); } template bool selectionV0Daughter(TTrack const& track, int pid) { - if (!(track.itsNCls() > cfgITScluster)) - return 0; - if (!track.hasTPC()) + if (!(track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])) { return false; - if (track.tpcNClsFound() < cfgTpcCluster) + } + if (!track.hasTPC()) { return false; - if (!(track.tpcNClsCrossedRows() > cfgTpcCrossRows)) - return 0; + } + if (!(track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts])) { + return false; + } + if (!(track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts])) { + return false; + } - if (cfgUseOnlyTPC) { - if (pid == kPions && std::abs(track.tpcNSigmaPi()) > cfgTpcCut) + if (cfgPIDConfigs.cfgPIDUseOnlyTpc) { + if (pid == UsePions && std::abs(track.tpcNSigmaPi()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; - if (pid == kKaons && std::abs(track.tpcNSigmaKa()) > cfgTpcCut) + } + if (pid == UseKaons && std::abs(track.tpcNSigmaKa()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; - if (pid == kProtons && std::abs(track.tpcNSigmaPr()) > cfgTpcCut) + } + if (pid == UseProtons && std::abs(track.tpcNSigmaPr()) > cfgPIDConfigs.cfgPIDOnlyTpcCut) { return false; + } } else { int partIndex = getNsigmaPID(track); int pidIndex = partIndex; // 1 = pion, 2 = kaon, 3 = proton - if (pidIndex != pid) + if (pidIndex != pid) { return false; + } } return true; } template - int getNsigmaPID(TTrack track) + int getNsigmaPID(const TTrack& track) { + if (cfgPIDConfigs.cfgPIDUseNclsPID && track.tpcNClsPID() < cfgPIDConfigs.cfgPIDNclsPIDCut) { + return -1; + } + // Computing Nsigma arrays for pion, kaon, and protons std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton - std::array nSigmaToUse = cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS - std::array detectorNsigmaCut = cfgUseItsPID ? itsNsigmaCut : tpcNsigmaCut; // Choose which nSigma to use: TPC or ITS - - bool isPion, isKaon, isProton; - bool isDetectedPion = nSigmaToUse[iPionUp] < detectorNsigmaCut[iPionUp] && nSigmaToUse[iPionUp] > detectorNsigmaCut[iPionLow]; - bool isDetectedKaon = nSigmaToUse[iKaonUp] < detectorNsigmaCut[iKaonUp] && nSigmaToUse[iKaonUp] > detectorNsigmaCut[iKaonLow]; - bool isDetectedProton = nSigmaToUse[iProtonUp] < detectorNsigmaCut[iProtonUp] && nSigmaToUse[iProtonUp] > detectorNsigmaCut[iProtonLow]; - - bool isTofPion = nSigmaTOF[iPionUp] < tofNsigmaCut[iPionUp] && nSigmaTOF[iPionUp] > tofNsigmaCut[iPionLow]; - bool isTofKaon = nSigmaTOF[iKaonUp] < tofNsigmaCut[iKaonUp] && nSigmaTOF[iKaonUp] > tofNsigmaCut[iKaonLow]; - bool isTofProton = nSigmaTOF[iProtonUp] < tofNsigmaCut[iProtonUp] && nSigmaTOF[iProtonUp] > tofNsigmaCut[iProtonLow]; - - if (track.pt() > cfgTofPtCut && !track.hasTOF()) { + std::array nSigmaToUse = cfgPIDConfigs.cfgPIDUseIts ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgPIDUseIts ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; + + if (track.pt() > cfgPIDConfigs.cfgPIDTofPtCut && !track.hasTOF()) { return -1; - } else if (track.pt() > cfgTofPtCut && track.hasTOF()) { + } + if (track.pt() > cfgPIDConfigs.cfgPIDTofPtCut && track.hasTOF()) { isPion = isTofPion && isDetectedPion; isKaon = isTofKaon && isDetectedKaon; isProton = isTofProton && isDetectedProton; @@ -707,16 +846,16 @@ struct PidDiHadron { isProton = isDetectedProton; } - if (cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { return -1; // more than one particle satisfy the criteria } if (isPion) { - pid = kPions; + pid = UsePions; } else if (isKaon) { - pid = kKaons; + pid = UseKaons; } else if (isProton) { - pid = kProtons; + pid = UseProtons; } else { return -1; // no particle satisfies the criteria } @@ -729,29 +868,60 @@ struct PidDiHadron { if (correctionsLoaded) { return; } - if (cfgEfficiency.value.empty() == false) { - if (cfgLocalEfficiency > 0) { - TFile* fEfficiencyTrigger = TFile::Open(cfgEfficiency.value.c_str(), "READ"); - mEfficiency = reinterpret_cast(fEfficiencyTrigger->Get("ccdb_object")); + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + } else { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); } if (mEfficiency == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); - if (cfgCentralityWeight.value.empty() == false) { - mCentralityWeight = ccdb->getForTimeStamp(cfgCentralityWeight, timestamp); + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); if (mCentralityWeight == nullptr) { - LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCentralityWeight.value.c_str()); + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgCentralityWeight.value.c_str(), (void*)mCentralityWeight); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); } correctionsLoaded = true; } + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ) { float eff = 1.; @@ -763,93 +933,135 @@ struct PidDiHadron { } else { eff = 1.0; } - if (eff == 0) + if (eff == 0) { return false; + } weight_nue = 1. / eff; return true; } - bool getCentralityWeight(float& weightCent, const float centrality) + bool getCentralityWeight(float& weightCent, const double centrality) { float weight = 1.; - if (mCentralityWeight) + if (mCentralityWeight) { weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); - else + } else { weight = 1.0; - if (weight == 0) + } + if (weight == 0) { return false; + } weightCent = weight; return true; } - // fill multiple histograms - template - void fillYield(TCollision collision, TTracks tracks) // function to fill the yield and etaphi histograms. + template + void nchCounter(const TTracks& tracks, double& multiplicity) // function to count the number of tracks in the event and fill the histogram { - float weff1 = 1; - float vtxz = collision.posZ(); - for (auto const& track1 : tracks) { - if (!trackSelected(track1)) + double nTracksCorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { continue; - if (cfgPIDParticle && getNsigmaPID(track1) != cfgPIDParticle) - continue; // if PID is selected, check if the track has the right PID - if (!getEfficiencyCorrection(weff1, track1.eta(), track1.pt(), vtxz)) + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { continue; - histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track1.phi(), 0.0)); - histos.fill(HIST("Eta"), track1.eta()); - histos.fill(HIST("EtaCorrected"), track1.eta(), weff1); - histos.fill(HIST("pT"), track1.pt()); - histos.fill(HIST("pTCorrected"), track1.pt(), weff1); + } + nTracksCorrected += weightNch; } + multiplicity = nTracksCorrected; } template - void fillNsigmaAfterCut(TTrack track1, Int_t pid) // function to fill the QA after Nsigma selection + void fillTrackQA(TTrack const& track, int pid, double eventClass, Stage stage, float weff) // function to fill the QA after Nsigma selection { - switch (pid) { - case 1: // For Pions - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); + if (cfgGeneral.cfgQABasic) { + if (stage == Before && pid == UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), track.eta(), UseCharged); + histos.fill(HIST("EtaCorrected"), track.eta(), UseCharged, weff); + histos.fill(HIST("pTFiner"), track.pt(), UseCharged, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), UseCharged, eventClass, weff); + + } else if (stage == After && pid > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), pid); + histos.fill(HIST("Eta"), track.eta(), pid); + histos.fill(HIST("EtaCorrected"), track.eta(), pid, weff); + histos.fill(HIST("pTFiner"), track.pt(), pid, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), pid, eventClass, weff); + } + } + + if (pid > UseCharged && (cfgPIDConfigs.cfgQANsigma || cfgPIDConfigs.cfgQAdEdx)) { + const bool useITS = cfgPIDConfigs.cfgPIDUseIts; + double tpcNSigma = 0.0; + double tpcExpSigma = 0.0; + double tofNSigma = 0.0; + double itsNSigma = 0.0; + switch (pid) { + case UsePions: // For Pions + tofNSigma = track.tofNSigmaPi(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPi(); + tpcExpSigma = track.tpcExpSigmaPi(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); - break; - case 2: // For Kaons - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); + break; + case UseKaons: // For Kaons + tofNSigma = track.tofNSigmaKa(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaKa(); + tpcExpSigma = track.tpcExpSigmaKa(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); - break; - case 3: // For Protons - if (!cfgUseItsPID) { - if (cfgGetdEdx) { - double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); - - histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); - histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); - histos.fill(HIST("ExpSigma_ptwise_afterCut"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); + break; + + case UseProtons: // For Protons + tofNSigma = track.tofNSigmaPr(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPr(); + tpcExpSigma = track.tpcExpSigmaPr(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + default: + return; + } + + if (cfgPIDConfigs.cfgQANsigma) { + if (stage == Before) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_beforeCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_beforeCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } else if (stage == After) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_afterCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_afterCut"), itsNSigma, tofNSigma, track.pt(), pid); } - histos.fill(HIST("TofTpcNsigma_after"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); } - if (cfgUseItsPID) - histos.fill(HIST("TofItsNsigma_after"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); - break; - } // end of switch + } + + if (cfgPIDConfigs.cfgQAdEdx) { + double tpcExpSignal = track.tpcSignal() - (tpcNSigma * tpcExpSigma); + + if (stage == Before) { + histos.fill(HIST("TpcdEdx_ptwise_beforeCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_beforeCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } else if (stage == After) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_afterCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } + } + } // if not a charged particle } float getDPhiStar(float charge1, float charge2, float phi1, float phi2, float pt1, float pt2, float radius, int magField) @@ -858,246 +1070,337 @@ struct PidDiHadron { float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); - if (dPhiStar > constants::math::PI) + if (dPhiStar > constants::math::PI) { dPhiStar = constants::math::TwoPI - dPhiStar; - if (dPhiStar < -constants::math::PI) + } + if (dPhiStar < -constants::math::PI) { dPhiStar = -constants::math::TwoPI - dPhiStar; + } return dPhiStar; } - template - void fillCorrelations(TTracks tracks1, TTracksAssoc tracks2, float posZ, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelations(const TTracks& triggers, const TTracksAssoc& assocs, float posZ, int system, int magneticField, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - // Cache efficiency for particles (too many FindBin lookups) - if (mEfficiency) { - efficiencyAssociatedCache.clear(); - efficiencyAssociatedCache.reserve(tracks2.size()); - for (const auto& track2 : tracks2) { - float weff = 1.; - getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); - efficiencyAssociatedCache.push_back(weff); + const int kNullIndex = -44; + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(assocs.size()); + useAssociatedCache.clear(); + useAssociatedCache.reserve(assocs.size()); + + for (const auto& assc : assocs) { // loading efficiencies for associated particles and simultaneously applying track selection + if (trackSelected(assc)) { + useAssociatedCache.push_back(1); + if (mEfficiency) { + float weff = 1.; + getEfficiencyCorrection(weff, assc.eta(), assc.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } else { + useAssociatedCache.push_back(0); + if (mEfficiency) { + efficiencyAssociatedCache.push_back(kNullIndex); + } } - } - - if (system == SameEvent) { - if (!cfgCentTableUnavailable) - histos.fill(HIST("Centrality_used"), cent); - histos.fill(HIST("Nch_used"), tracks1.size()); - } + } // end of efficieny requirement - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; float associatedWeight = 1.0f; // loop over all tracks - for (auto const& track1 : tracks1) { + for (auto const& trig : triggers) { - if (!trackSelected(track1)) + if (!trackSelected(trig)) { continue; - - // Fill Nsigma QA - if (cfgGetNsigmaQA && !cfgUseItsPID) { - if (cfgPIDParticle == kPions) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPi = track1.tpcSignal() - (track1.tpcNSigmaPi() * track1.tpcExpSigmaPi()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPi()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalPi, track1.tofNSigmaPi()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaPi(), track1.tofNSigmaPi()); - } - } - if (cfgPIDParticle == kKaons) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalKa = track1.tpcSignal() - (track1.tpcNSigmaKa() * track1.tpcExpSigmaKa()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaKa()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalKa, track1.tofNSigmaKa()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaKa(), track1.tofNSigmaKa()); - } - } - if (cfgPIDParticle == kProtons) { - histos.fill(HIST("TofTpcNsigma_before"), track1.tpcNSigmaPr(), track1.tofNSigmaPr(), track1.pt()); - if (cfgGetdEdx) { - double tpcExpSignalPr = track1.tpcSignal() - (track1.tpcNSigmaPr() * track1.tpcExpSigmaPr()); - - histos.fill(HIST("TpcdEdx_ptwise"), track1.pt(), track1.tpcSignal(), track1.tofNSigmaPr()); - histos.fill(HIST("ExpTpcdEdx_ptwise"), track1.pt(), tpcExpSignalPr, track1.tofNSigmaPr()); - histos.fill(HIST("ExpSigma_ptwise"), track1.pt(), track1.tpcExpSigmaPr(), track1.tofNSigmaPr()); - } - } } - if (cfgGetNsigmaQA && cfgUseItsPID) { - if (cfgPIDParticle == kPions) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPi(), track1.pt()); - if (cfgPIDParticle == kKaons) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaKa(), track1.pt()); - if (cfgPIDParticle == kProtons) - histos.fill(HIST("TofItsNsigma_before"), itsResponse.nSigmaITS(track1), track1.tofNSigmaPr(), track1.pt()); + + if (cfgEffWeights.cfgUseTrigWeight && !getEfficiencyCorrection(triggerWeight, trig.eta(), trig.pt(), posZ)) { + continue; } - if (cfgPIDParticle && getNsigmaPID(track1) != cfgPIDParticle) - continue; // if PID is selected, check if the track has the right PID + if (system == SameEvent) { + fillTrackQA(trig, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(trig, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(trig, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(trig, UseProtons, eventClass, Before, triggerWeight); + } - if (cfgGetNsigmaQA) - fillNsigmaAfterCut(track1, cfgPIDParticle); + int pidIndex = getNsigmaPID(trig); - if (!getEfficiencyCorrection(triggerWeight, track1.eta(), track1.pt(), posZ)) - continue; if (system == SameEvent) { - histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, track1.pt(), eventWeight * triggerWeight); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + fillTrackQA(trig, pidIndex, eventClass, After, triggerWeight); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, eventWeight * triggerWeight); + } } - for (auto const& track2 : tracks2) { + for (auto const& assc : assocs) { - if (!trackSelected(track2)) + if (useAssociatedCache[assc.filteredIndex()] == 0) { // rejecting associated tracks continue; - if (mEfficiency) { - associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; } - if (!cfgUsePtOrder && track1.globalIndex() == track2.globalIndex()) + if (mEfficiency && efficiencyAssociatedCache[assc.filteredIndex()] != kNullIndex) { // loading associated efficiency + associatedWeight = efficiencyAssociatedCache[assc.filteredIndex()]; + } + + if (!cfgGeneral.cfgUsePtOrder && trig.globalIndex() == assc.globalIndex()) { continue; // For pt-differential correlations, skip if the trigger and associate are the same track - if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt - if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); - float deltaEta = track1.eta() - track2.eta(); + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); - if (std::abs(deltaEta) < cfgCutMerging) { + if (std::abs(deltaEta) < cfgGeneral.cfgCutMerging && cfgGeneral.cfgUseCutMerging) { - double dPhiStarHigh = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusLow, magneticField); + double dPhiStarHigh = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusLow, magneticField); - const double kLimit = 3.0 * cfgCutMerging; + const double kLimit = 3.0 * cfgGeneral.cfgCutMerging; bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(track1.sign(), track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), rad, magneticField); + const double step = 0.01; + const double low = cfgGeneral.cfgRadiusLow; + const double high = cfgGeneral.cfgRadiusHigh; + const auto nSteps = static_cast(std::ceil((high - low) / step)); + + for (int i = 0; i < nSteps; ++i) { + const double rad = low + i * step; + double dPhiStar = getDPhiStar(trig.sign(), assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } - } + } // End of merging cut condition // fill the right sparse and histograms if (system == SameEvent) { - - same->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } else if (system == MixedEvent) { - - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), track2.pt(), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } } } } - template - void fillCorrelationsReso(TV0Tracks tracks1, TTracksAssoc tracks2, float posZ, float posY, float posX, int system, int magneticField, float cent, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelationsReso(const TV0Tracks& triggers, const TTracksAssoc& assocs, float posZ, float posY, float posX, int system, int magneticField, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { - // Cache efficiency for particles (too many FindBin lookups) - if (mEfficiency) { - efficiencyAssociatedCache.clear(); - efficiencyAssociatedCache.reserve(tracks2.size()); - for (const auto& track2 : tracks2) { - float weff = 1.; - getEfficiencyCorrection(weff, track2.eta(), track2.pt(), posZ); - efficiencyAssociatedCache.push_back(weff); + const int kNullIndex = -44; + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(assocs.size()); + useAssociatedCache.clear(); + useAssociatedCache.reserve(assocs.size()); + + for (const auto& assc : assocs) { // loading efficiencies for associated particles and simultaneously applying track selection + if (trackSelected(assc)) { + useAssociatedCache.push_back(1); + if (mEfficiency) { + float weff = 1.; + getEfficiencyCorrection(weff, assc.eta(), assc.pt(), posZ); + efficiencyAssociatedCache.push_back(weff); + } + } else { + useAssociatedCache.push_back(0); + if (mEfficiency) { + efficiencyAssociatedCache.push_back(kNullIndex); + } } - } + } // end of efficieny requirement - if (system == SameEvent) { - if (!cfgCentTableUnavailable) - histos.fill(HIST("Centrality_used"), cent); - histos.fill(HIST("Nch_used"), tracks2.size()); // Taking Nch from tracks2 since tracks1 are V0s - } - - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); float triggerWeight = 1.0f; float associatedWeight = 1.0f; // loop over all tracks - for (auto const& track1 : tracks1) { + for (auto const& trig : triggers) { double resoMass = -1; - // 4 = kshort, 5 = lambda, 6 = phi - if (cfgPIDParticle == kK0) { - if (!isSelectedK0(track1, posZ, posY, posX)) + // 4 = kshort, 5 = lambda + if (cfgPIDConfigs.cfgPIDParticle == UseK0) { + if (!isSelectedK0(trig, posZ, posY, posX, system)) { continue; // Reject if called for K0 but V0 is not K0 + } - resoMass = track1.mK0Short(); + resoMass = trig.mK0Short(); } - if (cfgPIDParticle == kLambda) { - if (!isSelectedLambda(track1, posZ, posY, posX)) + if (cfgPIDConfigs.cfgPIDParticle == UseLambda) { + if (!isSelectedLambda(trig, posZ, posY, posX, system)) { continue; // Reject if called for Lambda but V0 is not lambda - - resoMass = track1.mLambda(); + } + resoMass = trig.mLambda(); } if (system == SameEvent) { - histos.fill(HIST("Trig_histReso"), fSampleIndex, posZ, track1.pt(), resoMass, eventWeight * triggerWeight); + histos.fill(HIST("Trig_histReso"), fSampleIndex, posZ, eventClass, trig.pt(), resoMass, eventWeight * triggerWeight); } - for (auto const& track2 : tracks2) { + for (auto const& assc : assocs) { - if (!trackSelected(track2)) - continue; - if (track2.pt() < cfgCutPtMin || track2.pt() > cfgCutPtMax) // Select associated particles in the pt range 0.2 - 3.0 GeV/c - continue; - if (mEfficiency) { - associatedWeight = efficiencyAssociatedCache[track2.filteredIndex()]; + if (useAssociatedCache[assc.filteredIndex()] == 0) { + continue; // rejecting associated tracks } - if (cfgUsePtOrder && system == SameEvent && track1.pt() <= track2.pt()) + if (mEfficiency && efficiencyAssociatedCache[assc.filteredIndex()] != kNullIndex) { + associatedWeight = efficiencyAssociatedCache[assc.filteredIndex()]; // loading associated efficiency + } + + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt - if (cfgUsePtOrder && system == MixedEvent && cfgUsePtOrderInMixEvent && track1.pt() <= track2.pt()) + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } - float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); - float deltaEta = track1.eta() - track2.eta(); + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); - if (std::abs(deltaEta) < cfgCutMerging) { + if (std::abs(deltaEta) < cfgGeneral.cfgCutMerging && cfgGeneral.cfgUseCutMerging) { - double dPhiStarHigh = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusHigh, magneticField); - double dPhiStarLow = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), cfgRadiusLow, magneticField); + double dPhiStarHigh = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusHigh, magneticField); + double dPhiStarLow = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), cfgGeneral.cfgRadiusLow, magneticField); - const double kLimit = 3.0 * cfgCutMerging; + const double kLimit = 3.0 * cfgGeneral.cfgCutMerging; bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { - double dPhiStar = getDPhiStar(0, track2.sign(), track1.phi(), track2.phi(), track1.pt(), track2.pt(), rad, magneticField); + const double step = 0.01; + const double low = cfgGeneral.cfgRadiusLow; + const double high = cfgGeneral.cfgRadiusHigh; + const auto nSteps = static_cast(std::ceil((high - low) / step)); + + for (int i = 0; i < nSteps; ++i) { + const double rad = low + i * step; + double dPhiStar = getDPhiStar(0, assc.sign(), trig.phi(), assc.phi(), trig.pt(), assc.pt(), rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } } // fill the right sparse and histograms if (system == SameEvent) { - sameReso->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + sameReso->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } else if (system == MixedEvent) { + mixedReso->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } + } + } + } + + template + void fillCorrelationsMC(const TTracks& triggers, const TTracksAssoc& assocs, float posZ, int system, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms for MC truth + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + float associatedWeight = 1.0f; + + // loop over all tracks + for (auto const& trig : triggers) { + + if (cfgTrkSel.cfgMCTrigIsPhysPrim && !trig.isPhysicalPrimary()) { + continue; + } + + auto getPIDcode = [](int x) { + int pid = -1; + switch (x) { + case PDG_t::kPiPlus: + pid = UsePions; + break; + case PDG_t::kKPlus: + pid = UseKaons; + break; + case PDG_t::kProton: + pid = UseProtons; + break; + default: + return pid; + } // end of switch + return pid; + }; + + int pidIndex = getPIDcode(trig.pdgCode()); + + if (system == SameEvent) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(trig.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), trig.eta(), UseCharged); + histos.fill(HIST("pTFiner"), trig.pt(), UseCharged, eventClass); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(trig.phi(), 0.0), pidIndex); + histos.fill(HIST("Eta"), trig.eta(), pidIndex); + histos.fill(HIST("pTFiner"), trig.pt(), pidIndex, eventClass); + histos.fill(HIST("Trig_hist"), fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, eventWeight * triggerWeight); + } + } + + for (auto const& assc : assocs) { + + if (cfgTrkSel.cfgMCAsscIsPhysPrim && !assc.isPhysicalPrimary()) { + continue; + } + + if (!cfgGeneral.cfgUsePtOrder && trig.globalIndex() == assc.globalIndex()) { + continue; // For pt-differential correlations, skip if the trigger and associate are the same track + } + if (cfgGeneral.cfgUsePtOrder && system == SameEvent && trig.pt() <= assc.pt()) { + continue; // Without pt-differential correlations, skip if the trigger pt is less than the associate pt + } + if (cfgGeneral.cfgUsePtOrder && system == MixedEvent && cfgGeneral.cfgUsePtOrderInMixEvent && trig.pt() <= assc.pt()) { + continue; // For pt-differential correlations in mixed events, skip if the trigger pt is less than the associate pt + } + + float deltaPhi = RecoDecay::constrainAngle(trig.phi() - assc.phi(), -PIHalf); + float deltaEta = trig.eta() - assc.eta(); + + // fill the right sparse and histograms + if (system == SameEvent) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + same->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } else if (system == MixedEvent) { - mixedReso->getPairHist()->Fill(step, fSampleIndex, posZ, track1.pt(), resoMass, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), UseCharged, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + if (pidIndex > UseCharged) { + mixed->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, trig.pt(), pidIndex, deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); + } histos.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, eventWeight * triggerWeight * associatedWeight); } } @@ -1105,144 +1408,165 @@ struct PidDiHadron { } template - bool selectionEvent(TCollision collision, const int mult, const float cent, const bool fillCounter) + bool selectionEvent(TCollision const& collision, const int mult, const double cent, const bool fillCounter) { - if (fillCounter) - histos.fill(HIST("hEventCount"), kFilteredEvents); + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } if (!collision.sel8()) { - return 0; + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); } - if (fillCounter) - histos.fill(HIST("hEventCount"), kAfterSel8); - if (eventCuts[kUseNoTimeFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { - return 0; + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); } - if (fillCounter && eventCuts[kUseNoTimeFrameBorder][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoTimeFrameBorder); - if (eventCuts[kUseNoITSROFrameBorder][kEvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return 0; + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); } - if (fillCounter && eventCuts[kUseNoITSROFrameBorder][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoITSROFrameBorder); - if (eventCuts[kUseNoSameBunchPileup][kEvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); } - if (fillCounter && eventCuts[kUseNoSameBunchPileup][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoSameBunchPileup); - if (eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution - return 0; + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); } - if (fillCounter && eventCuts[kUseGoodZvtxFT0vsPV][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseGoodZvtxFT0vsPV); - if (eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { // no collisions in specified time range - return 0; + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); } - if (fillCounter && eventCuts[kUseNoCollInTimeRangeStandard][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoCollInTimeRangeStandard); - if (eventCuts[kUseGoodITSLayersAll][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { // from Jan 9 2025 AOT meeting // cut time intervals with dead ITS staves - return 0; + return false; + } + + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); } - if (fillCounter && eventCuts[kUseGoodITSLayersAll][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseGoodITSLayersAll); - if (eventCuts[kUseNoCollInRofStandard][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { // no other collisions in this Readout Frame with per-collision multiplicity above threshold - return 0; + return false; } - if (fillCounter && eventCuts[kUseNoCollInRofStandard][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoCollInRofStandard); - if (eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { // veto an event if FT0C amplitude in previous ITS ROF is above threshold - return 0; + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); } - if (fillCounter && eventCuts[kUseNoHighMultCollInPrevRof][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseNoHighMultCollInPrevRof); auto multNTracksPV = collision.multNTracksPV(); auto occupancy = collision.trackOccupancyInTimeRange(); - if (eventCuts[kUseOccupancy][kEvCut1] && (occupancy < cfgCutOccupancyMin || occupancy > cfgCutOccupancyMax)) { - return 0; + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyMin || occupancy > cfgEvSel.cfgCutOccupancyMax)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); } - if (fillCounter && eventCuts[kUseOccupancy][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseOccupancy); - if (eventCuts[kUseMultCorrCut][kEvCut1]) { - if (cfgFuncParas.cfgMultPVT0CCutEnabled && !cfgCentTableUnavailable) { - if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) - return 0; - if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) - return 0; + if (eventCuts[UseMultCorrCut][EvCut1]) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } } - if (cfgFuncParas.cfgMultT0CCutEnabled && !cfgCentTableUnavailable) { - if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) - return 0; - if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) - return 0; + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } } if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { - if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) - return 0; - if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) - return 0; + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } } if (cfgFuncParas.cfgMultMultV0ACutEnabled) { - if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) - return 0; - if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) - return 0; + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } } } - if (fillCounter && eventCuts[kUseMultCorrCut][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseMultCorrCut); + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); + } // V0A T0A 5 sigma cut - if (eventCuts[kUseT0AV0ACut][kEvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) - return 0; - if (fillCounter && eventCuts[kUseT0AV0ACut][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseT0AV0ACut); - - if (eventCuts[kUseVertexITSTPC][kEvCut1] && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return 0; - if (fillCounter && eventCuts[kUseVertexITSTPC][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseVertexITSTPC); - - if (eventCuts[kUseTVXinTRD][kEvCut1] && collision.alias_bit(kTVXinTRD)) { - return 0; + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); } - if (fillCounter && eventCuts[kUseTVXinTRD][kEvCut1]) - histos.fill(HIST("hEventCount"), kUseTVXinTRD); - return 1; + return true; } void processSame(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) { auto bc = collision.bc_as(); - float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) - cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) + float cent = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision); + + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) { return; + } loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); histos.fill(HIST("Centrality"), cent); histos.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1250,24 +1574,32 @@ struct PidDiHadron { histos.fill(HIST("Nch"), tracks.size()); histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + histos.fill(HIST("Nch_corrected"), multiplicity); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - fillYield(collision, tracks); - same->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillCorrelations(tracks, assocs, collision.posZ(), SameEvent, getMagneticField(bc.timestamp()), eventClass, weightCent); } PROCESS_SWITCH(PidDiHadron, processSame, "Process same event", true); // the process for filling the mixed events void processMixed(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::BCsWithTimestamps const&) { - auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); auto mult = associatedTracks.size(); @@ -1276,56 +1608,74 @@ struct PidDiHadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(tracks, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, tracks1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) - continue; + float cent1 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision1); + float cent2 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision2); - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) { continue; - - float cent1 = -1; - float cent2 = -1; - if (!cfgCentTableUnavailable) { - cent1 = getCentrality(collision1); - cent2 = getCentrality(collision2); } - if (cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) { continue; - if (cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { continue; + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { continue; + } + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgEffWeights.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent1); + } - fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); + fillCorrelations(tracks1, assocs, collision1.posZ(), MixedEvent, getMagneticField(bc.timestamp()), eventClass, eventWeight * weightCent); } } PROCESS_SWITCH(PidDiHadron, processMixed, "Process mixed events", true); template - bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedK0(V0 const& candidate, float posZ, float posY, float posX, int system) { double mk0 = candidate.mK0Short(); @@ -1333,58 +1683,103 @@ struct PidDiHadron { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - histos.fill(HIST("hK0Count"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iK0] || negtrack.pt() < resoCutVals[kNegTrackPt][iK0]) + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexK0] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 1.5); - if (mk0 < resoCutVals[kMassMin][iK0] && mk0 > resoCutVals[kMassMax][iK0]) + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDaughterPt); + } + if (mk0 < resoCutVals[CutMassMin][IndexK0] || mk0 > resoCutVals[CutMassMax][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 2.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillMassCut); + } // Rapidity correction - if (candidate.yK0Short() > resoCutVals[kRapidity][iK0]) + if (candidate.yK0Short() > resoCutVals[CutRapidityMax][IndexK0] || candidate.yK0Short() < resoCutVals[CutRapidityMin][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 3.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillRapidityCut); + } // DCA cuts for K0short - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iK0]) + if (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexK0] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 4.5); - if (std::abs(candidate.dcaV0daughters()) > resoSwitchVals[kDCABetDaug][iK0]) + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexK0] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 5.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iK0] && (candidate.v0radius() < resoCutVals[kRadiusMin][iK0] || candidate.v0radius() > resoCutVals[kRadiusMax][iK0])) + if (resoSwitchVals[UseV0Radius][IndexK0] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexK0] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexK0])) { return false; - histos.fill(HIST("hK0Count"), 6.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillV0Radius); + } // cosine pointing angle cuts - if (candidate.v0cosPA() < resoCutVals[kCosPA][iK0]) + if (resoSwitchVals[UseCosPA][IndexK0] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 7.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCosPA); + } // Proper lifetime float cTauK0 = candidate.distovertotmom(posX, posY, posZ) * massK0Short; - if (resoSwitchVals[kUseProperLifetime][iK0] && std::abs(cTauK0) > resoCutVals[kLifeTime][iK0]) + if (resoSwitchVals[UseProperLifetime][IndexK0] && cTauK0 > resoCutVals[CutLifeTime][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 8.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillProperLifetime); + } // ArmenterosPodolanskiCut - if (resoSwitchVals[kUseArmPodCut][iK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[kArmPodMinVal][iK0]) + if (resoSwitchVals[UseArmPodCut][IndexK0] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexK0]) { return false; - histos.fill(HIST("hK0Count"), 9.5); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillArmPodCut); + } + + if (resoSwitchVals[UseCompMassRej][IndexK0]) { + if (std::abs(candidate.mLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + if (std::abs(candidate.mAntiLambda() - o2::constants::physics::MassLambda) < resoCutVals[CutCompMassRej][IndexK0]) { + return false; + } + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillCompetingMass); + } + // Selection on V0 daughters - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kPions)) + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UsePions)) { return false; - histos.fill(HIST("hK0Count"), 10.5); - - histos.fill(HIST("hK0Phi"), candidate.phi()); - histos.fill(HIST("hK0Eta"), candidate.eta()); - histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); - histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); - histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); - histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (system == SameEvent) { + histos.fill(HIST("hK0Count"), FillDaughterTrackSelected); + histos.fill(HIST("hK0Phi"), candidate.phi()); + histos.fill(HIST("hK0Rap"), candidate.yK0Short()); + histos.fill(HIST("hK0Eta"), candidate.eta()); + histos.fill(HIST("PiPlusTPC_K0"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_K0"), postrack.pt(), postrack.tofNSigmaPi()); + histos.fill(HIST("PiMinusTPC_K0"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_K0"), negtrack.pt(), negtrack.tofNSigmaPi()); + } return true; } template - bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX) + bool isSelectedLambda(V0 const& candidate, float posZ, float posY, float posX, int system) { bool isL = false; // Is lambda candidate bool isAL = false; // Is anti-lambda candidate @@ -1396,71 +1791,127 @@ struct PidDiHadron { auto postrack = candidate.template posTrack_as(); auto negtrack = candidate.template negTrack_as(); - histos.fill(HIST("hLambdaCount"), 0.5); - if (postrack.pt() < resoCutVals[kPosTrackPt][iLambda] || negtrack.pt() < resoCutVals[kNegTrackPt][iLambda]) + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCandidate); + } + if (postrack.pt() < resoCutVals[CutPosTrackPt][IndexLambda] || negtrack.pt() < resoCutVals[CutNegTrackPt][IndexLambda]) { return false; - - histos.fill(HIST("hLambdaCount"), 1.5); - if (mlambda > resoCutVals[kMassMin][iLambda] && mlambda < resoCutVals[kMassMax][iLambda]) + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDaughterPt); + } + if (mlambda > resoCutVals[CutMassMin][IndexLambda] && mlambda < resoCutVals[CutMassMax][IndexLambda]) { isL = true; - if (mantilambda > resoCutVals[kMassMin][iLambda] && mantilambda < resoCutVals[kMassMax][iLambda]) + } + if (mantilambda > resoCutVals[CutMassMin][IndexLambda] && mantilambda < resoCutVals[CutMassMax][IndexLambda]) { isAL = true; + } if (!isL && !isAL) { return false; } - histos.fill(HIST("hLambdaCount"), 2.5); + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillMassCut); + } // Rapidity correction - if (candidate.yLambda() > resoCutVals[kRapidity][iLambda]) + if (candidate.yLambda() > resoCutVals[CutRapidityMax][IndexLambda] || candidate.yLambda() < resoCutVals[CutRapidityMin][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 3.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillRapidityCut); + } + // DCA cuts for lambda and antilambda - if (isL) { - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCAPosToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCANegToPVMin][iLambda]) - return false; + if (isL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda])) { + isL = false; } - if (isAL) { - if (std::abs(candidate.dcapostopv()) < resoCutVals[kDCANegToPVMin][iLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[kDCAPosToPVMin][iLambda]) - return false; + if (isAL && (std::abs(candidate.dcapostopv()) < resoCutVals[CutDCANegToPVMin][IndexLambda] || std::abs(candidate.dcanegtopv()) < resoCutVals[CutDCAPosToPVMin][IndexLambda])) { + isAL = false; } - histos.fill(HIST("hLambdaCount"), 4.5); - if (std::abs(candidate.dcaV0daughters()) > resoSwitchVals[kDCABetDaug][iLambda]) + if (!isL && !isAL) { return false; - histos.fill(HIST("hLambdaCount"), 5.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDCAtoPV); + } + if (resoSwitchVals[UseDCABetDaug][IndexLambda] && std::abs(candidate.dcaV0daughters()) > resoCutVals[CutDCABetDaug][IndexLambda]) { + return false; + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDCAxDaughters); + } // v0 radius cuts - if (resoSwitchVals[kUseV0Radius][iLambda] && (candidate.v0radius() < resoCutVals[kRadiusMin][iLambda] || candidate.v0radius() > resoCutVals[kRadiusMax][iLambda])) + if (resoSwitchVals[UseV0Radius][IndexLambda] && (candidate.v0radius() < resoCutVals[CutRadiusMin][IndexLambda] || candidate.v0radius() > resoCutVals[CutRadiusMax][IndexLambda])) { return false; - histos.fill(HIST("hLambdaCount"), 6.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillV0Radius); + } // cosine pointing angle cuts - if (candidate.v0cosPA() < resoCutVals[kCosPA][iLambda]) + if (resoSwitchVals[UseCosPA][IndexLambda] && candidate.v0cosPA() < resoCutVals[CutCosPA][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 7.5); + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCosPA); + } // Proper lifetime float cTauLambda = candidate.distovertotmom(posX, posY, posZ) * massLambda; - if (resoSwitchVals[kUseProperLifetime][iLambda] && cTauLambda > resoCutVals[kLifeTime][iLambda]) + if (resoSwitchVals[UseProperLifetime][IndexLambda] && cTauLambda > resoCutVals[CutLifeTime][IndexLambda]) { return false; - histos.fill(HIST("hLambdaCount"), 8.5); - if (isL) { - if (!selectionV0Daughter(postrack, kProtons) || !selectionV0Daughter(negtrack, kPions)) - return false; } - if (isAL) { - if (!selectionV0Daughter(postrack, kPions) || !selectionV0Daughter(negtrack, kProtons)) - return false; + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillProperLifetime); } - histos.fill(HIST("hLambdaCount"), 9.5); - if (!cfgUseAntiLambda && isAL) { // Reject the track if it is antilambda + // ArmenterosPodolanskiCut + if (resoSwitchVals[UseArmPodCut][IndexLambda] && (candidate.qtarm() / std::abs(candidate.alpha())) < resoCutVals[CutArmPodMinVal][IndexLambda]) { return false; } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillArmPodCut); + } - histos.fill(HIST("hLambdaPhi"), candidate.phi()); - histos.fill(HIST("hLambdaEta"), candidate.eta()); - histos.fill(HIST("PrPlusTPC_L"), postrack.pt(), postrack.tpcNSigmaPr()); - histos.fill(HIST("PrPlusTOF_L"), postrack.pt(), postrack.tofNSigmaPr()); - histos.fill(HIST("PiMinusTPC_L"), negtrack.pt(), negtrack.tpcNSigmaPi()); - histos.fill(HIST("PiMinusTOF_L"), negtrack.pt(), negtrack.tofNSigmaPi()); + if (resoSwitchVals[UseCompMassRej][IndexLambda]) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < resoCutVals[CutCompMassRej][IndexLambda]) { + return false; + } + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillCompetingMass); + } + + if (isL) { + if (!selectionV0Daughter(postrack, UseProtons) || !selectionV0Daughter(negtrack, UsePions)) { + isL = false; + } + } + if (isAL) { + if (!selectionV0Daughter(postrack, UsePions) || !selectionV0Daughter(negtrack, UseProtons)) { + isAL = false; + } + } + if (!isL && !isAL) { + return false; + } + if (system == SameEvent) { + histos.fill(HIST("hLambdaCount"), FillDaughterTrackSelected); + histos.fill(HIST("hLambdaPhi"), candidate.phi()); + histos.fill(HIST("hLambdaEta"), candidate.eta()); + histos.fill(HIST("hLambdaRap"), candidate.yLambda()); + } + if (isL && system == SameEvent) { + histos.fill(HIST("PrPlusTPC_La"), postrack.pt(), postrack.tpcNSigmaPr()); + histos.fill(HIST("PrPlusTOF_La"), postrack.pt(), postrack.tofNSigmaPr()); + histos.fill(HIST("PiMinusTPC_La"), negtrack.pt(), negtrack.tpcNSigmaPi()); + histos.fill(HIST("PiMinusTOF_La"), negtrack.pt(), negtrack.tofNSigmaPi()); + } + if (isAL && system == SameEvent) { + histos.fill(HIST("PrMinusTPC_Al"), negtrack.pt(), negtrack.tpcNSigmaPr()); + histos.fill(HIST("PrMinusTOF_Al"), negtrack.pt(), negtrack.tofNSigmaPr()); + histos.fill(HIST("PiPlusTPC_Al"), postrack.pt(), postrack.tpcNSigmaPi()); + histos.fill(HIST("PiPlusTOF_Al"), postrack.pt(), postrack.tofNSigmaPi()); + } return true; } @@ -1472,15 +1923,15 @@ struct PidDiHadron { void processSameReso(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::BCsWithTimestamps const&, aod::V0Datas const& V0s) { auto bc = collision.bc_as(); - float cent = -1.; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) - cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) + float cent = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision); + + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracks.size(), cent, true)) { return; + } loadCorrection(bc.timestamp()); - if (!cfgCentTableUnavailable) { + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent); histos.fill(HIST("Centrality"), cent); histos.fill(HIST("CentralityWeighted"), cent, weightCent); @@ -1488,17 +1939,28 @@ struct PidDiHadron { histos.fill(HIST("Nch"), tracks.size()); histos.fill(HIST("zVtx"), collision.posZ()); - if (cfgSelCollByNch && (tracks.size() < cfgCutMultMin || tracks.size() >= cfgCutMultMax)) { + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity); + } + + histos.fill(HIST("Nch_corrected"), multiplicity); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin - sameReso->fillEvent(tracks.size(), CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReso(V0s, tracks, collision.posZ(), collision.posY(), collision.posX(), SameEvent, getMagneticField(bc.timestamp()), cent, weightCent); + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + fillCorrelationsReso(V0s, assocs, collision.posZ(), collision.posY(), collision.posX(), SameEvent, getMagneticField(bc.timestamp()), eventClass, weightCent); } PROCESS_SWITCH(PidDiHadron, processSameReso, "Process same event for resonances", true); @@ -1512,106 +1974,231 @@ struct PidDiHadron { using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; auto tracksTuple = std::make_tuple(V0s, tracks); - Pair pairs{binningOnVtxAndMult, cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto it = pairs.begin(); it != pairs.end(); it++) { auto& [collision1, v0s1, collision2, tracks2] = *it; - if (!collision1.sel8() || !collision2.sel8()) + if (!collision1.sel8() || !collision2.sel8()) { continue; + } auto tracks1 = tracks.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), this->cache); - if (cfgSelCollByNch && (tracks1.size() < cfgCutMultMin || tracks1.size() >= cfgCutMultMax)) - continue; - if (cfgSelCollByNch && (tracks2.size() < cfgCutMultMin || tracks2.size() >= cfgCutMultMax)) - continue; + float cent2 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision2); + float cent1 = cfgEvSel.cfgCentTableUnavailable ? -1.f : getCentrality(collision1); - float cent1 = -1; - float cent2 = -1; - if (!cfgCentTableUnavailable) { - cent1 = getCentrality(collision1); - cent2 = getCentrality(collision2); + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) { + continue; } - if (cfgUseAdditionalEventCut && !selectionEvent(collision1, tracks1.size(), cent1, false)) + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) { continue; - if (cfgUseAdditionalEventCut && !selectionEvent(collision2, tracks2.size(), cent2, false)) + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { continue; + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent1 < cfgCutCentMin || cent1 >= cfgCutCentMax)) + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { continue; + } + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1); + + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2); + } + } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent2 < cfgCutCentMin || cent2 >= cfgCutCentMax)) + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } - histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin auto bc = collision1.bc_as(); loadCorrection(bc.timestamp()); float eventWeight = 1.0f; - if (cfgUseEventWeights) { + if (cfgEffWeights.cfgUseEventWeights) { eventWeight = 1.0f / it.currentWindowNeighbours(); } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; float weightCent = 1.0f; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { getCentralityWeight(weightCent, cent1); + } + + auto assocs = asscTracks->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - fillCorrelationsReso(v0s1, tracks2, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, getMagneticField(bc.timestamp()), cent1, eventWeight * weightCent); + fillCorrelationsReso(v0s1, assocs, collision1.posZ(), collision1.posY(), collision1.posX(), MixedEvent, getMagneticField(bc.timestamp()), eventClass, eventWeight * weightCent); } } PROCESS_SWITCH(PidDiHadron, processMixedReso, "Process mixed events", true); - void processMC(FilteredCollisions::iterator const& collision, FilteredMcTracks const& tracksmc, aod::BCsWithTimestamps const&, aod::McParticles const&) + void processSameMC(FilteredMcCollisions::iterator const& mcCollision, FilteredMcTracks const& mcTracks, SmallGroupMcCollisions const& collisions) + { + float cent = -1.f; + if (!cfgEvSel.cfgCentTableUnavailable) { + for (const auto& collision : collisions) { + cent = getCentrality(collision); + } + histos.fill(HIST("Centrality"), cent); + } + auto multiplicity = static_cast(mcTracks.size()); + histos.fill(HIST("Nch"), multiplicity); + histos.fill(HIST("zVtx"), mcCollision.posZ()); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity < cfgEvSel.cfgCutMultMin || multiplicity >= cfgEvSel.cfgCutMultMax)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { + return; + } + histos.fill(HIST("eventcount"), SameEvent); + + float eventWeight = 1.0f; + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + auto mcAssocs = asscTracksMC->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + + fillCorrelationsMC(mcTracks, mcAssocs, mcCollision.posZ(), SameEvent, eventClass, eventWeight); + + } // end of processSameMC + PROCESS_SWITCH(PidDiHadron, processSameMC, "Process Monte Carlo for same event correlations", true); + + void processMixedMC(FilteredMcCollisions const& mcCollisions, FilteredMcTracks const& mcTracks, SmallGroupMcCollisions const& collisions) + { + auto getTracksSize = [&mcTracks, this](FilteredMcCollisions::iterator const& mcCollision) { + auto associatedTracks = mcTracks.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(mcTracks, mcTracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMixEventNumMin, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [mcCollision1, mcTracks1, mcCollision2, mcTracks2] = *it; + + auto groupedCollisions1 = collisions.sliceBy(collisionPerMCCollision, mcCollision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMCCollision, mcCollision2.globalIndex()); + + float cent1 = -1.f; + float cent2 = -1.f; + + if (!cfgEvSel.cfgCentTableUnavailable) { + for (const auto& collision : groupedCollisions1) { + cent1 = getCentrality(collision); + } + for (const auto& collision : groupedCollisions2) { + cent2 = getCentrality(collision); + } + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent1 < cfgEvSel.cfgCutCentMin || cent1 >= cfgEvSel.cfgCutCentMax)) { + continue; + } + + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent2 < cfgEvSel.cfgCutCentMin || cent2 >= cfgEvSel.cfgCutCentMax)) { + continue; + } + + auto multiplicity1 = static_cast(mcTracks1.size()); + auto multiplicity2 = static_cast(mcTracks2.size()); + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 < cfgEvSel.cfgCutMultMin || multiplicity1 >= cfgEvSel.cfgCutMultMax)) { + continue; + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity2 < cfgEvSel.cfgCutMultMin || multiplicity2 >= cfgEvSel.cfgCutMultMax)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); + + float eventWeight = (cfgEffWeights.cfgUseEventWeights) ? (1.0f / it.currentWindowNeighbours()) : 1.0f; + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + auto mcAssocs = asscTracksMC->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision2.globalIndex(), cache); + + fillCorrelationsMC(mcTracks1, mcAssocs, mcCollision1.posZ(), MixedEvent, eventClass, eventWeight); + } + } + PROCESS_SWITCH(PidDiHadron, processMixedMC, "Process Monte Carlo for mixed event correlations", true); + + void processMCPID(FilteredCollisions::iterator const& collision, FilteredTracksMcLabels const& tracksmc, aod::BCsWithTimestamps const&, aod::McParticles const&) { float cent = -1.; - if (!cfgCentTableUnavailable) + if (!cfgEvSel.cfgCentTableUnavailable) { cent = getCentrality(collision); - if (cfgUseAdditionalEventCut && !selectionEvent(collision, tracksmc.size(), cent, true)) + } + if (cfgEvSel.cfgUseAdditionalEventCut && !selectionEvent(collision, tracksmc.size(), cent, true)) { return; + } - if (cfgSelCollByNch && (tracksmc.size() < cfgCutMultMin || tracksmc.size() >= cfgCutMultMax)) { + if (cfgEvSel.cfgSelCollByNch && (tracksmc.size() < cfgEvSel.cfgCutMultMin || tracksmc.size() >= cfgEvSel.cfgCutMultMax)) { return; } - if (!cfgSelCollByNch && !cfgCentTableUnavailable && (cent < cfgCutCentMin || cent >= cfgCutCentMax)) { + if (!cfgEvSel.cfgSelCollByNch && !cfgEvSel.cfgCentTableUnavailable && (cent < cfgEvSel.cfgCutCentMin || cent >= cfgEvSel.cfgCutCentMax)) { return; } // loop over all tracks for (auto const& track : tracksmc) { - if (!trackSelected(track)) + if (!trackSelected(track)) { continue; + } int pidIndex = getNsigmaPID(track); // Fill Counts for selection through Nsigma cuts - if (pidIndex == kPions) + if (pidIndex == UsePions) { histos.fill(HIST("hNsigmaPionSelected"), track.pt()); - if (pidIndex == kKaons) + } + if (pidIndex == UseKaons) { histos.fill(HIST("hNsigmaKaonSelected"), track.pt()); - if (pidIndex == kProtons) + } + if (pidIndex == UseProtons) { histos.fill(HIST("hNsigmaProtonSelected"), track.pt()); + } if (track.mcParticle().isPhysicalPrimary()) { - if (cfgPIDParticle == kPions) + if (cfgPIDConfigs.cfgPIDParticle == UsePions) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - if (cfgPIDParticle == kKaons) + } + if (cfgPIDConfigs.cfgPIDParticle == UseKaons) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - if (cfgPIDParticle == kProtons) + } + if (cfgPIDConfigs.cfgPIDParticle == UseProtons) { histos.fill(HIST("TpcdEdx_ptwise_all_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + } } // Check the PDG code for the particles (MC truth) and match with analysed Nsigma PID if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kPiPlus) { histos.fill(HIST("hNsigmaPionTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_pi"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_pi_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPi()); + } double tpcExpSignalPi = track.tpcSignal() - (track.tpcNSigmaPi() * track.tpcExpSigmaPi()); histos.fill(HIST("ExpTpcdEdx_ptwise_pi"), track.pt(), tpcExpSignalPi, track.tofNSigmaPi()); - if (pidIndex == kPions) { + if (pidIndex == UsePions) { histos.fill(HIST("hNsigmaPionTruePositives"), track.pt()); } } // Pion condition @@ -1619,13 +2206,14 @@ struct PidDiHadron { if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kKPlus) { histos.fill(HIST("hNsigmaKaonTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_ka"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_ka_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaKa()); + } double tpcExpSignalKa = track.tpcSignal() - (track.tpcNSigmaKa() * track.tpcExpSigmaKa()); histos.fill(HIST("ExpTpcdEdx_ptwise_ka"), track.pt(), tpcExpSignalKa, track.tofNSigmaKa()); - if (pidIndex == kKaons) { + if (pidIndex == UseKaons) { histos.fill(HIST("hNsigmaKaonTruePositives"), track.pt()); } } // Kaon condition @@ -1633,19 +2221,20 @@ struct PidDiHadron { if (std::abs(track.mcParticle().pdgCode()) == PDG_t::kProton) { histos.fill(HIST("hNsigmaProtonTrue"), track.pt()); histos.fill(HIST("TpcdEdx_ptwise_pr"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); - if (track.mcParticle().isPhysicalPrimary()) + if (track.mcParticle().isPhysicalPrimary()) { histos.fill(HIST("TpcdEdx_ptwise_pr_physprim"), track.pt(), track.tpcSignal(), track.tofNSigmaPr()); + } double tpcExpSignalPr = track.tpcSignal() - (track.tpcNSigmaPr() * track.tpcExpSigmaPr()); histos.fill(HIST("ExpTpcdEdx_ptwise_pr"), track.pt(), tpcExpSignalPr, track.tofNSigmaPr()); - if (pidIndex == kProtons) { + if (pidIndex == UseProtons) { histos.fill(HIST("hNsigmaProtonTruePositives"), track.pt()); } } // Proton condition } // end of tracks MC loop - } // end of process MC - PROCESS_SWITCH(PidDiHadron, processMC, "Process Monte Carlo", true); + } // end of process MCPID + PROCESS_SWITCH(PidDiHadron, processMCPID, "Process Monte Carlo for PID", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx new file mode 100644 index 00000000000..555d4f8de81 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/pidLongRange.cxx @@ -0,0 +1,1915 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pidLongRange.cxx +/// \brief Identified particles long-range correlations using forward FIT detectors and TPC, with focus on particle +/// \author Thor Jensen (thor.kjaersgaard.jensen@cern.ch), Preet Bhanjan Pati (preet.bhanjan.pati@cern.ch) + +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; +using namespace constants::math; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) + +template +auto readMatrix(Array2D const& mat, P& array) +{ + for (auto i = 0; i < static_cast(mat.rows); ++i) { + for (auto j = 0; j < static_cast(mat.cols); ++j) { + array[i][j] = mat(i, j); + } + } + + return; +} + +static constexpr std::array, 20> LongArrayFloat = {{{{1.1, 2.1, 3.1}}, {{1.2, 2.2, 3.2}}, {{1.3, 2.3, 3.3}}, {{-1.1, -2.1, -3.1}}, {{-1.2, -2.2, -3.2}}, {{-1.3, -2.3, -3.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}, {{1.3, 1.3, 1.3}}, {{-1.1, -1.1, -1.1}}, {{-1.2, -1.2, -1.2}}, {{-1.3, -1.3, -1.3}}, {{1.1, 1.1, 1.1}}, {{1.2, 1.2, 1.2}}}}; +static constexpr std::array, 20> LongArrayInt = {{{{1, 2, 3}}, {{1, 2, 3}}, {{1, 2, 3}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}, {{1, 1, 1}}, {{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 0}}, {{1, 1, 1}}, {{1, 1, 1}}}}; + +struct PidLongRange { + o2::aod::ITSResponse itsResponse; + Service ccdb{}; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgQABasic, bool, true, "Enable QA histograms for event and track selection") + O2_DEFINE_CONFIGURABLE(cfgStrictTrackCounter, bool, false, "Strict track counter for multiplicity correlation cut, counts only tracks that pass all cuts and are used in the correlation") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, int, 10, "Sample size for mixed event") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0A, bool, true, "Switch for doing TPC-FT0A correlations") + O2_DEFINE_CONFIGURABLE(cfgAnalyzeTPCFT0C, bool, true, "Switch for doing TPC-FT0C correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + Configurable> cfgRunRemoveList{"cfgRunRemoveList", std::vector{-1}, "excluded run numbers"}; + Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; + Configurable cfgCorrLevel{"cfgCorrLevel", 0, "calibration step: 0 = no corr, 1 = gain corr"}; + } cfgGeneral; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgEfficiencyNch, std::string, "", "CCDB path to multiplicity dependent efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") + O2_DEFINE_CONFIGURABLE(cfgCentralityWeight, std::string, "", "CCDB path to centrality weight object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgLocalEfficiencyNch, bool, false, "Use local multiplicity dependent efficiency object"); + } cfgEffWeights; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalPtMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgGlobalEta, float, 0.8f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalChi2prTPCcls, float, 2.5f, "Maximum chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgGlobalDCAz, float, 2.0f, "Maximum DCA to vertex z") + Configurable> cfgTrackCutsDCAxy{"cfgTrackCutsDCAxy", std::vector{"[0]*(0.0026+0.005/(x^1.01))", "[0]*(0.0026+0.005/(x^1.01))"}, "Functional form of pt-dependent DCAxy cut"}; + Configurable> cfgTrackCutsDCAz{"cfgTrackCutsDCAz", std::vector{"", ""}, "Functional form of pt-dependent DCAz cut"}; + Configurable> cfgTrackCuts{"cfgTrackCuts", {LongArrayFloat.front().data(), 10, 2, {"PtMin", "PtMax", "EtaCut", "Chi2PrTpcCls", "TpcCluster", "TpcCrossedRows", "ItsCluster", "DCAz", "DCAzNsigma", "DCAxyNsigma"}, {"General", "SelNch"}}, "Labeled array for track selections"}; + } cfgTrkSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgGlobalCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgUseAdditionalEventCut, bool, true, "Use additional event cut on mult correlations") + O2_DEFINE_CONFIGURABLE(cfgMinMultForCorrelations, int, 0, "minimum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxMultForCorrelations, int, 20, "maximum multiplicity for correlations") + O2_DEFINE_CONFIGURABLE(cfgSelCollByNch, bool, true, "Select collisions by Nch or centrality") + O2_DEFINE_CONFIGURABLE(cfgCentEstimator, int, 0, "0:FT0C; 1:FT0CVariant1; 2:FT0M; 3:FT0A") + O2_DEFINE_CONFIGURABLE(cfgMinCentForCorrelations, int, 0, "minimum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgMaxCentForCorrelations, int, 20, "maximum centrality for correlations") + O2_DEFINE_CONFIGURABLE(cfgUseCol2ForMixedEvSel, bool, true, "Use Mult/Cent cut on collision 2 during mixed event"); + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking (CBT)', 'CBT_hadronPID' ") + Configurable> cfgUseEventCuts{"cfgUseEventCuts", {LongArrayInt.front().data(), 14, 1, {"Filtered Events", "Sel8", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kNoCollInTimeRangeStandard", "kIsGoodITSLayersAll", "kIsGoodITSLayer0123", "kNoCollInRofStandard", "kNoHighMultCollInPrevRof", "Occupancy", "Multcorrelation", "T0AV0ACut"}, {"EvCuts"}}, "Labeled array (int) for various cuts on resonances"}; + } cfgEvSel; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgMultCentHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 10.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultCentLowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultT0CCutEnabled, bool, false, "Enable Global multiplicity vs T0C centrality cut") + Configurable> cfgMultT0CCutPars{"cfgMultT0CCutPars", std::vector{143.04, -4.58368, 0.0766055, -0.000727796, 2.86153e-06, 23.3108, -0.36304, 0.00437706, -4.717e-05, 1.98332e-07}, "Global multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultPVT0CCutEnabled, bool, false, "Enable PV multiplicity vs T0C centrality cut") + Configurable> cfgMultPVT0CCutPars{"cfgMultPVT0CCutPars", std::vector{195.357, -6.15194, 0.101313, -0.000955828, 3.74793e-06, 30.0326, -0.43322, 0.00476265, -5.11206e-05, 2.13613e-07}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultPVHighCutFunction, std::string, "[0]+[1]*x + 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultPVLowCutFunction, std::string, "[0]+[1]*x - 5.*([2]+[3]*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultGlobalPVCutEnabled, bool, false, "Enable global multiplicity vs PV multiplicity cut") + Configurable> cfgMultGlobalPVCutPars{"cfgMultGlobalPVCutPars", std::vector{-0.140809, 0.734344, 2.77495, 0.0165935}, "PV multiplicity vs T0C centrality cut parameter values"}; + O2_DEFINE_CONFIGURABLE(cfgMultMultV0AHighCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x + 4.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ALowCutFunction, std::string, "[0] + [1]*x + [2]*x*x + [3]*x*x*x + [4]*x*x*x*x - 3.*([5] + [6]*x + [7]*x*x + [8]*x*x*x + [9]*x*x*x*x)", "Functional for multiplicity correlation cut"); + O2_DEFINE_CONFIGURABLE(cfgMultMultV0ACutEnabled, bool, false, "Enable global multiplicity vs V0A multiplicity cut") + Configurable> cfgMultMultV0ACutPars{"cfgMultMultV0ACutPars", std::vector{534.893, 184.344, 0.423539, -0.00331436, 5.34622e-06, 871.239, 53.3735, -0.203528, 0.000122758, 5.41027e-07}, "Global multiplicity vs V0A multiplicity cut parameter values"}; + std::vector multT0CCutPars; + std::vector multPVT0CCutPars; + std::vector multGlobalPVCutPars; + std::vector multMultV0ACutPars; + std::unique_ptr fMultPVT0CCutLow = nullptr; + std::unique_ptr fMultPVT0CCutHigh = nullptr; + std::unique_ptr fMultT0CCutLow = nullptr; + std::unique_ptr fMultT0CCutHigh = nullptr; + std::unique_ptr fMultGlobalPVCutLow = nullptr; + std::unique_ptr fMultGlobalPVCutHigh = nullptr; + std::unique_ptr fMultMultV0ACutLow = nullptr; + std::unique_ptr fMultMultV0ACutHigh = nullptr; + std::unique_ptr fT0AV0AMean = nullptr; + std::unique_ptr fT0AV0ASigma = nullptr; + std::unique_ptr fPtDepDCAxy = nullptr; + std::unique_ptr fPtDepDCAxyForNch = nullptr; + std::unique_ptr fPtDepDCAz = nullptr; + std::unique_ptr fPtDepDCAzForNch = nullptr; + O2_DEFINE_CONFIGURABLE(cfgV0AT0Acut, int, 5, "V0AT0A cut") + } cfgFuncParas; + + struct : ConfigurableGroup { + O2_DEFINE_CONFIGURABLE(cfgTofPtCut, float, 0.4f, "Minimum pt to use TOF N-sigma") + O2_DEFINE_CONFIGURABLE(cfgUseItsPID, bool, false, "Use ITS PID for particle identification") + O2_DEFINE_CONFIGURABLE(cfgQANsigma, bool, false, "Get QA histograms for selection of pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgQAdEdx, bool, false, "Get dEdx histograms for pions, kaons, and protons") + O2_DEFINE_CONFIGURABLE(cfgPIDUseRejection, bool, true, "True: use exclusion exclusion criteria for PID determination, false: don't use exclusion") + Configurable> nSigmas{"nSigmas", {LongArrayFloat.front().data(), 6, 3, {"UpCut_pi", "UpCut_ka", "UpCut_pr", "LowCut_pi", "LowCut_ka", "LowCut_pr"}, {"TPC", "TOF", "ITS"}}, "Labeled array for n-sigma values for TPC, TOF, ITS for pions, kaons, protons (positive and negative)"}; + } cfgPIDConfigs; + + SliceCache cache; + + // Axes for general QA + ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisMult{"axisMult", {10, 0, 100}, "multiplicity axis for QA histograms"}; + ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality axis for QA histograms"}; + ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; + ConfigurableAxis axisPtFiner{"axisPtFiner", {98, 0.2, 10.0}, "pt axis for histograms"}; + ConfigurableAxis axisDCAz{"axisDCAz", {200, -2, 2}, "DCA_{z} (cm)"}; + ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -1, 1}, "DCA_{xy} (cm)"}; + + // Axes for Correlations + ConfigurableAxis axisMultForCorr{"axisMultForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 50, 100, 150, 200, 300, 500}, "multiplicity axis for correlation histograms"}; + ConfigurableAxis axisCentForCorr{"axisCentForCorr", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for correlation histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; + ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; + ConfigurableAxis axisDeltaEtaFt0aFt0c{"axisDeltaEtaFt0aFt0c", {32, -1.5, 3.0}, "delta eta axis"}; + ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; + ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240, 260}, "multiplicity / centrality axis for mixed event histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.2, 0.5, 1, 1.5, 2, 3, 4, 6, 10}, "pt trigger axis for histograms"}; + ConfigurableAxis axisParticle{"axisParticle", {4, 0, 4}, "particle axis for correlation container"}; + ConfigurableAxis axisTpcNcls{"axisTpcNcls", {500, 0, 500}, "number of TPC clusters used for PID"}; + + // Axes for long-range correlations QA + ConfigurableAxis axisAmplitudeFt0{"axisAmplitudeFt0", {5000, 0, 1000}, "FT0A amplitude"}; + ConfigurableAxis axisChannelFt0aAxis{"axisChannelFt0aAxis", {96, 0.0, 96.0}, "FT0A channel"}; + ConfigurableAxis axisChannelFt0cAxis{"axisChannelFt0cAxis", {96, 0.0, 96.0}, "FT0C channel"}; + ConfigurableAxis cfgFITamp{"cfgFITamp", {1000, 0, 5000}, "FT0 amplitude"}; + + // Axes for PID QA + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {80, -5, 5}, "nsigmaTPC axis"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {80, -5, 5}, "nsigmaTOF axis"}; + ConfigurableAxis axisNsigmaITS{"axisNsigmaITS", {80, -5, 5}, "nsigmaITS axis"}; + ConfigurableAxis axisTpcSignal{"axisTpcSignal", {250, 0, 250}, "dEdx axis for TPC"}; + ConfigurableAxis axisSigma{"axisSigma", {200, 0, 20}, "sigma axis for TPC"}; + + Filter collisionFilter = (nabs(aod::collision::posZ) < cfgEvSel.cfgGlobalCutVertex); + Filter trackFilter = (nabs(aod::track::eta) < cfgTrkSel.cfgGlobalEta) && (aod::track::pt > cfgTrkSel.cfgGlobalPtMin) && (aod::track::pt < cfgTrkSel.cfgGlobalPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (nabs(aod::track::dcaZ) < cfgTrkSel.cfgGlobalDCAz) && (aod::track::tpcChi2NCl < cfgTrkSel.cfgGlobalChi2prTPCcls); + + using FilteredCollisions = soa::Filtered>; + using FilteredTracks = soa::Filtered>; + + // FT0 geometry + o2::ft0::Geometry ft0Det; + static constexpr uint64_t Ft0IndexA = 96; + std::vector* offsetFT0 = nullptr; + std::vector cstFT0RelGain; + + // Corrections + TH3D* mEfficiency = nullptr; + TH1D* mEfficiencyNch = nullptr; + TH1D* mCentralityWeight = nullptr; + bool correctionsLoaded = false; + + // Define the outputs + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + OutputObj sameFt0aFt0c{"sameEvent_FT0A_FT0C"}; + OutputObj mixedFt0aFt0c{"mixedEvent_FT0A_FT0C"}; + + HistogramRegistry histos{"histos"}; + + // For Labelled array value containers + std::array, 10> trackCuts{}; + std::array, 14> eventCuts{}; + std::array, 6> nSigmaVals{}; + + // define global variables + TRandom3 fRandom{0}; + + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; + + enum FITIndex { + IndexFT0A = 0, + IndexFT0C = 1 + }; + + enum PIDIndex { + UseCharged = 0, + UsePions, + UseKaons, + UseProtons, + UseK0, + UseLambda, + UsePhi + }; + enum PiKpArrayIndex { + IndexPionUp = 0, + IndexKaonUp, + IndexProtonUp, + IndexPionLow, + IndexKaonLow, + IndexProtonLow + }; + enum DetectorType { + UseTPC = 0, + UseTOF, + UseITS + }; + enum Stage { + Before = 0, + After + }; + + enum EventCutTypes { + FilteredEvents = 0, + AfterSel8, + UseNoTimeFrameBorder, + UseNoITSROFrameBorder, + UseNoSameBunchPileup, + UseGoodZvtxFT0vsPV, + UseNoCollInTimeRangeStandard, + UseGoodITSLayersAll, + UseGoodITSLayer0123, + UseNoCollInRofStandard, + UseNoHighMultCollInPrevRof, + UseOccupancy, + UseMultCorrCut, + UseT0AV0ACut, + HaveFT0Cut, + NumEventCuts + }; + + enum EventCutType { + EvCut1 = 0, + NumEvCutTypes = 1 + }; + + enum TrackCuts { + TrkCutPtMin = 0, + TrkCutPtMax, + TrkCutEtaCut, + TrkCutChi2PrTpcCls, + TrkCutTpcCluster, + TrkCutTpcCrossedRows, + TrkCutItsCluster, + TrkCutDCAz, + TrkCutDCAzNsigma, + TrkCutDCAxyNsigma + }; + + enum TrackCutGroup { + UseGenTrkCuts = 0, + UseNchSelCuts, + NumTrackCutTypes + }; + + enum CentEstimators { + UseCentFT0C = 0, + UseCentFT0CVariant1, + UseCentFT0M, + UseCentFV0A, + // Count the total number of enum + NumCentEstimators + }; + + RCTFlagsChecker rctChecker{"CBT"}; + + void init(InitContext&) + { + // ---------------------------------------------------------------------------------------------------------------------- + // The way the code is initialized, TPC-FT0 correlations are done together and FT0A-FT0C correlations are done separately + // ---------------------------------------------------------------------------------------------------------------------- + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + LOGF(info, "Starting init"); + + readMatrix(cfgTrkSel.cfgTrackCuts->getData(), trackCuts); + readMatrix(cfgEvSel.cfgUseEventCuts->getData(), eventCuts); + readMatrix(cfgPIDConfigs.nSigmas->getData(), nSigmaVals); + + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) { + histos.add("hEventCountRct", "Number of Event;; Count", {HistType::kTH1D, {{2, 0, 2}}}); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(1, "rct fail"); + histos.get(HIST("hEventCountRct"))->GetXaxis()->SetBinLabel(2, "rct pass"); + histos.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{15, -0.5, 14.5}}}); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseMultCorrCut + 1, "MultCorrelation Cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(UseT0AV0ACut + 1, "T0AV0A cut"); + histos.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(HaveFT0Cut + 1, "Event has FT0 cut"); + } + + if ((doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) && cfgGeneral.cfgQABasic) { + histos.add("hPassedEventSelection", "Number of Event;; Count", {HistType::kTH1D, {{12, -0.5, 11.5}}}); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(FilteredEvents + 1, "Filtered events"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(AfterSel8 + 1, "After sel8"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoTimeFrameBorder + 1, "kNoTimeFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoITSROFrameBorder + 1, "kNoITSROFrameBorder"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoSameBunchPileup + 1, "kNoSameBunchPileup"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodZvtxFT0vsPV + 1, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInTimeRangeStandard + 1, "kNoCollInTimeRangeStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayersAll + 1, "kIsGoodITSLayersAll"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseGoodITSLayer0123 + 1, "kIsGoodITSLayer0123"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoCollInRofStandard + 1, "kNoCollInRofStandard"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseNoHighMultCollInPrevRof + 1, "kNoHighMultCollInPrevRof"); + histos.get(HIST("hPassedEventSelection"))->GetXaxis()->SetBinLabel(UseOccupancy + 1, "Occupancy Cut"); + } + + // Multiplicity correlation cuts + if (eventCuts[UseMultCorrCut][EvCut1] != 0) { + cfgFuncParas.multT0CCutPars = cfgFuncParas.cfgMultT0CCutPars; + cfgFuncParas.multPVT0CCutPars = cfgFuncParas.cfgMultPVT0CCutPars; + cfgFuncParas.multGlobalPVCutPars = cfgFuncParas.cfgMultGlobalPVCutPars; + cfgFuncParas.multMultV0ACutPars = cfgFuncParas.cfgMultMultV0ACutPars; + + cfgFuncParas.fMultPVT0CCutLow = std::make_unique("fMultPVT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutLow->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + cfgFuncParas.fMultPVT0CCutHigh = std::make_unique("fMultPVT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultPVT0CCutHigh->SetParameters(cfgFuncParas.multPVT0CCutPars.data()); + + cfgFuncParas.fMultT0CCutLow = std::make_unique("fMultT0CCutLow", cfgFuncParas.cfgMultCentLowCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutLow->SetParameters(cfgFuncParas.multT0CCutPars.data()); + cfgFuncParas.fMultT0CCutHigh = std::make_unique("fMultT0CCutHigh", cfgFuncParas.cfgMultCentHighCutFunction->c_str(), 0, 100); + cfgFuncParas.fMultT0CCutHigh->SetParameters(cfgFuncParas.multT0CCutPars.data()); + + cfgFuncParas.fMultGlobalPVCutLow = std::make_unique("fMultGlobalPVCutLow", cfgFuncParas.cfgMultMultPVLowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutLow->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + cfgFuncParas.fMultGlobalPVCutHigh = std::make_unique("fMultGlobalPVCutHigh", cfgFuncParas.cfgMultMultPVHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultGlobalPVCutHigh->SetParameters(cfgFuncParas.multGlobalPVCutPars.data()); + + cfgFuncParas.fMultMultV0ACutLow = std::make_unique("fMultMultV0ACutLow", cfgFuncParas.cfgMultMultV0ALowCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutLow->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + cfgFuncParas.fMultMultV0ACutHigh = std::make_unique("fMultMultV0ACutHigh", cfgFuncParas.cfgMultMultV0AHighCutFunction->c_str(), 0, 4000); + cfgFuncParas.fMultMultV0ACutHigh->SetParameters(cfgFuncParas.multMultV0ACutPars.data()); + } + if (eventCuts[UseT0AV0ACut][EvCut1] != 0) { + cfgFuncParas.fT0AV0AMean = std::make_unique("fT0AV0AMean", "[0]+[1]*x", 0, 200000); + cfgFuncParas.fT0AV0AMean->SetParameters(-1601.0581, 9.417652e-01); + cfgFuncParas.fT0AV0ASigma = std::make_unique("fT0AV0ASigma", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 200000); + cfgFuncParas.fT0AV0ASigma->SetParameters(463.4144, 6.796509e-02, -9.097136e-07, 7.971088e-12, -2.600581e-17); + } + + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAxy = std::make_unique("ptDepDCAxy", cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxy->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts]); + LOGF(info, "DCAxy pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAxyForNch = std::make_unique("ptDepDCAxyForNch", cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAxyForNch->SetParameter(0, trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts]); + LOGF(info, "DCAxy pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAxyNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].c_str()); + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + cfgFuncParas.fPtDepDCAz = std::make_unique("ptDepDCAz", cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAz->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts]); + LOGF(info, "DCAz pt-dependence function: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseGenTrkCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].c_str()); + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + cfgFuncParas.fPtDepDCAzForNch = std::make_unique("ptDepDCAzForNch", cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str(), 0.001, 1000); + cfgFuncParas.fPtDepDCAzForNch->SetParameter(0, trackCuts[TrkCutDCAzNsigma][UseNchSelCuts]); + LOGF(info, "DCAz pt-dependence function for Nch: %0.1f * %s", trackCuts[TrkCutDCAzNsigma][UseNchSelCuts], cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].c_str()); + } + + const AxisSpec axisT0C{70, 0, 70000, "N_{ch} (T0C)"}; + const AxisSpec axisT0A{200, 0, 200000, "N_{ch} (T0A)"}; + const AxisSpec axisChi2{100, 0., 10.}; + const AxisSpec axisChID = {220, 0, 220}; + + auto maxSample = static_cast(cfgGeneral.cfgSampleSize); + AxisSpec axisSample{cfgGeneral.cfgSampleSize, 0, maxSample, "Sample"}; + + // Choose if it is Nch selection or Centrality selection + AxisSpec axisEventClass = {axisMultForCorr, "N_{ch}"}; + + if (!cfgEvSel.cfgSelCollByNch) { + axisEventClass = {axisCentForCorr, "Centrality"}; + } + + if (doprocessQA) { + histos.add("h_globalTracks_centT0C_before", "before cut;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_PVTracks_centT0C_before", "before cut;Centrality T0C;mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_globalTracks_PVTracks_before", "before cut;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisMult, axisMult}}); + histos.add("h_globalTracks_multT0A_before", "before cut;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_globalTracks_multV0A_before", "before cut;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_multV0A_multT0A_before", "before cut;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + histos.add("h_multT0C_centT0C_before", "before cut;Centrality T0C;mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + + histos.add("h_globalTracks_centT0C_after", "after cut;Centrality T0C;mulplicity global tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_PVTracks_centT0C_after", "after cut;Centrality T0C;mulplicity PV tracks", {HistType::kTH2D, {axisCent, axisMult}}); + histos.add("h_globalTracks_PVTracks_after", "after cut;mulplicity PV tracks;mulplicity global tracks", {HistType::kTH2D, {axisMult, axisMult}}); + histos.add("h_globalTracks_multT0A_after", "after cut;mulplicity T0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_globalTracks_multV0A_after", "after cut;mulplicity V0A;mulplicity global tracks", {HistType::kTH2D, {axisT0A, axisMult}}); + histos.add("h_multV0A_multT0A_after", "after cut;mulplicity T0A;mulplicity V0A", {HistType::kTH2D, {axisT0A, axisT0A}}); + histos.add("h_multT0C_centT0C_after", "after cut;Centrality T0C;mulplicity T0C", {HistType::kTH2D, {axisCent, axisT0C}}); + histos.add("h_centFT0M_centFT0C", "after cut;Centrality T0C;Centrality T0M", {HistType::kTH2D, {axisCent, axisCent}}); + histos.add("h_centFV0A_centFT0C", "after cut;Centrality T0C;Centrality V0A", {HistType::kTH2D, {axisCent, axisCent}}); + + // Add track cuts table + histos.add("hDCAz_before", "DCAz before cuts; DCAz (cm); Pt", {HistType::kTH2D, {axisDCAz, axisPtFiner}}); + histos.add("hDCAxy_before", "DCAxy before cuts; DCAxy (cm); Pt", {HistType::kTH2D, {axisDCAxy, axisPtFiner}}); + histos.add("hTPCNclsPID_before", "hTPCNclsPID_before", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCNclsFound_before", "hTPCNclsFound_before", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCCrossedRows_before", "hTPCCrossedRows_before", {HistType::kTH1D, {axisTpcNcls}}); + + histos.add("hDCAz_after", "DCAz after cuts; DCAz (cm); Pt", {HistType::kTH3D, {axisDCAz, axisPtFiner, axisParticle}}); + histos.add("hDCAxy_after", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH3D, {axisDCAxy, axisPtFiner, axisParticle}}); + histos.add("hTPCNclsPID_after", "hTPCNclsPID_after", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCNclsFound_after", "hTPCNclsFound_after", {HistType::kTH1D, {axisTpcNcls}}); + histos.add("hTPCCrossedRows_after", "hTPCCrossedRows_after", {HistType::kTH1D, {axisTpcNcls}}); + + histos.add("hChi2prTPCcls", "#chi^{2}/cluster for the TPC track segment", {HistType::kTH1D, {axisChi2}}); + histos.add("hChi2prITScls", "#chi^{2}/cluster for the ITS track", {HistType::kTH1D, {axisChi2}}); + histos.add("hITSNclsFound", "Number of found ITS clusters", {HistType::kTH1D, {axisTpcNcls}}); + } + + if (doprocessSameFt0aFt0c || doprocessSameTpcFt0 || doprocessQA) { + histos.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + histos.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMult}}); + histos.add("Nch_corrected", "N_{ch} corrected", {HistType::kTH1D, {axisMult}}); + histos.add("Centrality", "Centrality", {HistType::kTH1D, {axisCent}}); + histos.add("CentralityWeighted", "Centrality (weighted)", {HistType::kTH1D, {axisCent}}); + } + + if (doprocessSameTpcFt0 || doprocessQA) { + if (cfgGeneral.cfgQABasic) { + histos.add("Phi", "Phi", {HistType::kTH2D, {axisPhi, axisParticle}}); + histos.add("Eta", "Eta", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("EtaCorrected", "EtaCorrected", {HistType::kTH2D, {axisEta, axisParticle}}); + histos.add("pTFiner", "pTFiner", {HistType::kTHnSparseF, {axisPtFiner, axisParticle, axisEventClass}}); + histos.add("pTFinerCorrected", "pTFinerCorrected", {HistType::kTHnSparseF, {axisPtFiner, axisParticle, axisEventClass}}); + } + // PID nSigma histograms + if (cfgPIDConfigs.cfgQANsigma) { + if (!cfgPIDConfigs.cfgUseItsPID) { + histos.add("TofTpcNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofTpcNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaTPC, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } // TPC-TOF PID QA hists + if (cfgPIDConfigs.cfgUseItsPID) { + histos.add("TofItsNsigma_beforeCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + histos.add("TofItsNsigma_afterCut", "", {HistType::kTHnSparseD, {{axisNsigmaITS, axisNsigmaTOF, axisPtTrigger, axisParticle}}}); + } // ITS-TOF PID QA hists + } // end of PID QA hists + + // PID dEdx histograms + if (cfgPIDConfigs.cfgQAdEdx) { + histos.add("TpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_beforeCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + + histos.add("TpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpTpcdEdx_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisTpcSignal, axisNsigmaTOF, axisParticle}}}); + histos.add("ExpSigma_ptwise_afterCut", "", {HistType::kTHnSparseD, {{axisPtTrigger, axisSigma, axisNsigmaTOF, axisParticle}}}); + } + } + if (doprocessSameTpcFt0) { // QA plots are based on TPC tracks, so they are only included in TPC-FT0A process and not in TPC-FT0C process + // TPC-FT0A correlation histograms + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + histos.add("Assoc_amp_same_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Assoc_amp_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisChannelFt0aAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + // TPC-FT0C correlation histograms + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + histos.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + histos.add("Assoc_amp_same_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Assoc_amp_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisChannelFt0cAxis, axisAmplitudeFt0}}); + histos.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass, axisPtTrigger, axisParticle}}}); + } + + histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + } + + if (doprocessSameFt0aFt0c) { + histos.add("Phi_FT0A_FT0C", "Phi_FT0A_FT0C", {HistType::kTH1D, {axisPhi}}); + histos.add("Eta_FT0A_FT0C", "Eta_FT0A_FT0C", {HistType::kTH1D, {axisEta}}); + histos.add("EtaCorrected_FT0A_FT0C", "EtaCorrected_FT0A_FT0C", {HistType::kTH1D, {axisEta}}); + histos.add("pTFiner_FT0A_FT0C", "pTFiner_FT0A_FT0C", {HistType::kTH1D, {axisPtFiner}}); + histos.add("pTFinerCorrected_FT0A_FT0C", "pTFinerCorrected_FT0A_FT0C", {HistType::kTH1D, {axisPtFiner}}); + histos.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); // check to see the delta eta and delta phi distribution + histos.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); + histos.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisEventClass}}}); + + histos.add("FT0Amp", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + histos.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, cfgFITamp}}); + } + + histos.add("eventcount", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + histos.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisMult, axisMult}}); + + LOGF(info, "Initializing correlation container"); + + // Initialize containers + std::vector corrAxisTpcFt0a = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; + + std::vector corrAxisTpcFt0c = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisParticle, "Particle, 0 = charged, 1 = pion, 2 = kaon, 3 = proton"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; + + std::vector corrAxisFt0aFt0c = {{axisSample}, + {axisVertex, "z-vtx (cm)"}, + {axisEventClass}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaFt0aFt0c, "#Delta#eta"}}; + + if (doprocessSameTpcFt0) { + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + sameTpcFt0a.setObject(new DihadronContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a)); + mixedTpcFt0a.setObject(new DihadronContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a)); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + sameTpcFt0c.setObject(new DihadronContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c)); + mixedTpcFt0c.setObject(new DihadronContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c)); + } + } + + if (doprocessSameFt0aFt0c) { + sameFt0aFt0c.setObject(new DihadronContainer("sameEvent_FT0A_FT0C", "sameEvent_FT0A_FT0C", corrAxisFt0aFt0c)); + mixedFt0aFt0c.setObject(new DihadronContainer("mixedEvent_FT0A_FT0C", "mixedEvent_FT0A_FT0C", corrAxisFt0aFt0c)); + } + + if (!cfgEvSel.cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSel.cfgEvSelRCTflags.value); // override initialzation + } + + LOGF(info, "End of init"); + } + + template + double getCentrality(TCollision const& collision) + { + double cent = 0.0; + switch (cfgEvSel.cfgCentEstimator) { + case UseCentFT0C: + cent = collision.centFT0C(); + break; + case UseCentFT0CVariant1: + cent = collision.centFT0CVariant1(); + break; + case UseCentFT0M: + cent = collision.centFT0M(); + break; + case UseCentFV0A: + cent = collision.centFV0A(); + break; + default: + cent = collision.centFT0C(); + } + return cent; + } + + template + bool eventRct(TCollision const& collision, const bool fillCounter) + { + if (!rctChecker(collision)) { + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 0.5); + } + + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCountRct"), 1.5); + } + + return true; + } + + template + bool eventSelected(TCollision const& collision, const int mult, const double cent, const bool fillCounter) + { + if (fillCounter) { + histos.fill(HIST("hEventCount"), FilteredEvents); + } + if (!collision.sel8()) { + return false; + } + if (fillCounter) { + histos.fill(HIST("hEventCount"), AfterSel8); + } + + if (eventCuts[UseNoTimeFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoTimeFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoTimeFrameBorder); + } + + if (eventCuts[UseNoITSROFrameBorder][EvCut1] && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillCounter && eventCuts[UseNoITSROFrameBorder][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoITSROFrameBorder); + } + + if (eventCuts[UseNoSameBunchPileup][EvCut1] && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return false; + } + if (fillCounter && eventCuts[UseNoSameBunchPileup][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoSameBunchPileup); + } + + if (eventCuts[UseGoodZvtxFT0vsPV][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return false; + } + if (fillCounter && eventCuts[UseGoodZvtxFT0vsPV][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodZvtxFT0vsPV); + } + + if (eventCuts[UseNoCollInTimeRangeStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // no collisions in specified time range + return false; + } + + if (fillCounter && eventCuts[UseNoCollInTimeRangeStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInTimeRangeStandard); + } + + if (eventCuts[UseGoodITSLayersAll][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + // from Jan 9 2025 AOT meeting + // cut time intervals with dead ITS staves + return false; + } + + if (fillCounter && eventCuts[UseGoodITSLayersAll][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayersAll); + } + + if (eventCuts[UseGoodITSLayer0123][EvCut1] && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return false; + } + if (fillCounter && eventCuts[UseGoodITSLayer0123][EvCut1]) { + histos.fill(HIST("hEventCount"), UseGoodITSLayer0123); + } + + if (eventCuts[UseNoCollInRofStandard][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // no other collisions in this Readout Frame with per-collision multiplicity above threshold + return false; + } + + if (fillCounter && eventCuts[UseNoCollInRofStandard][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoCollInRofStandard); + } + + if (eventCuts[UseNoHighMultCollInPrevRof][EvCut1] && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // veto an event if FT0C amplitude in previous ITS ROF is above threshold + return false; + } + if (fillCounter && eventCuts[UseNoHighMultCollInPrevRof][EvCut1]) { + histos.fill(HIST("hEventCount"), UseNoHighMultCollInPrevRof); + } + + auto multNTracksPV = collision.multNTracksPV(); + auto occupancy = collision.trackOccupancyInTimeRange(); + + if (eventCuts[UseOccupancy][EvCut1] && (occupancy < cfgEvSel.cfgCutOccupancyLow || occupancy > cfgEvSel.cfgCutOccupancyHigh)) { + return false; + } + if (fillCounter && eventCuts[UseOccupancy][EvCut1]) { + histos.fill(HIST("hEventCount"), UseOccupancy); + } + + if (eventCuts[UseMultCorrCut][EvCut1]) { + if (cfgFuncParas.cfgMultPVT0CCutEnabled) { + if (multNTracksPV < cfgFuncParas.fMultPVT0CCutLow->Eval(cent)) { + return false; + } + if (multNTracksPV > cfgFuncParas.fMultPVT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultT0CCutEnabled) { + if (mult < cfgFuncParas.fMultT0CCutLow->Eval(cent)) { + return false; + } + if (mult > cfgFuncParas.fMultT0CCutHigh->Eval(cent)) { + return false; + } + } + if (cfgFuncParas.cfgMultGlobalPVCutEnabled) { + if (mult < cfgFuncParas.fMultGlobalPVCutLow->Eval(multNTracksPV)) { + return false; + } + if (mult > cfgFuncParas.fMultGlobalPVCutHigh->Eval(multNTracksPV)) { + return false; + } + } + if (cfgFuncParas.cfgMultMultV0ACutEnabled) { + if (collision.multFV0A() < cfgFuncParas.fMultMultV0ACutLow->Eval(mult)) { + return false; + } + if (collision.multFV0A() > cfgFuncParas.fMultMultV0ACutHigh->Eval(mult)) { + return false; + } + } + } + + if (fillCounter && eventCuts[UseMultCorrCut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseMultCorrCut); + } + + // V0A T0A 5 sigma cut + if (eventCuts[UseT0AV0ACut][EvCut1] && (std::fabs(collision.multFV0A() - cfgFuncParas.fT0AV0AMean->Eval(collision.multFT0A())) > cfgFuncParas.cfgV0AT0Acut * cfgFuncParas.fT0AV0ASigma->Eval(collision.multFT0A()))) { + return false; + } + if (fillCounter && eventCuts[UseT0AV0ACut][EvCut1]) { + histos.fill(HIST("hEventCount"), UseT0AV0ACut); + } + + return true; + } + + template + void eventSelectedIndividually(TCollision const& collision) + { + histos.fill(HIST("hPassedEventSelection"), FilteredEvents); + + if (collision.sel8()) { + histos.fill(HIST("hPassedEventSelection"), AfterSel8); + } + + if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoTimeFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + histos.fill(HIST("hPassedEventSelection"), UseNoITSROFrameBorder); + } + + if (collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + histos.fill(HIST("hPassedEventSelection"), UseNoSameBunchPileup); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodZvtxFT0vsPV); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInTimeRangeStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayersAll); + } + + if (collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + histos.fill(HIST("hPassedEventSelection"), UseGoodITSLayer0123); + } + + if (collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + histos.fill(HIST("hPassedEventSelection"), UseNoCollInRofStandard); + } + + if (collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + histos.fill(HIST("hPassedEventSelection"), UseNoHighMultCollInPrevRof); + } + + auto occupancy = collision.trackOccupancyInTimeRange(); + if (eventCuts[UseOccupancy][EvCut1] && (occupancy > cfgEvSel.cfgCutOccupancyLow && occupancy < cfgEvSel.cfgCutOccupancyHigh)) { + histos.fill(HIST("hPassedEventSelection"), UseOccupancy); + } + } + + double getPhiFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + return RecoDecay::phi(chPos.X() + (*offsetFT0)[i].getX(), chPos.Y() + (*offsetFT0)[i].getY()); + } + + double getEtaFT0(uint64_t chno, int i) + { + // offsetFT0[0]: FT0A, offsetFT0[1]: FT0C + if (i > 1 || i < 0) { + LOGF(fatal, "kFIT Index %d out of range", i); + } + ft0Det.calculateChannelCenter(); + auto chPos = ft0Det.getChannelCenter(chno); + auto x = chPos.X() + (*offsetFT0)[i].getX(); + auto y = chPos.Y() + (*offsetFT0)[i].getY(); + auto z = chPos.Z() + (*offsetFT0)[i].getZ(); + if (chno >= Ft0IndexA) { + z = -z; + } + auto r = std::sqrt(x * x + y * y); + auto theta = std::atan2(r, z); + return -std::log(std::tan(0.5 * theta)); + } + + void loadAlignParam(uint64_t timestamp) + { + offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", timestamp); + if (offsetFT0 == nullptr) { + LOGF(fatal, "Could not load FT0/Calib/Align for timestamp %d", timestamp); + } + } + + template + bool trackSelected(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseGenTrkCuts].empty() && std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxy->Eval(track.pt())) { + return false; + } + + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseGenTrkCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAz->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseGenTrkCuts]) { + return false; + } + } + + return ((track.pt() > trackCuts[TrkCutPtMin][UseGenTrkCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseGenTrkCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseGenTrkCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseGenTrkCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseGenTrkCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseGenTrkCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseGenTrkCuts])); + } + + template + bool trackSelectedForNch(TTrack const& track) + { + if (!cfgTrkSel.cfgTrackCutsDCAxy.value[UseNchSelCuts].empty() && (std::fabs(track.dcaXY()) > cfgFuncParas.fPtDepDCAxyForNch->Eval(track.pt()))) { + return false; + } + if (!cfgTrkSel.cfgTrackCutsDCAz.value[UseNchSelCuts].empty()) { + if (std::fabs(track.dcaZ()) > cfgFuncParas.fPtDepDCAzForNch->Eval(track.pt())) { + return false; + } + } else { + if (std::fabs(track.dcaZ()) > trackCuts[TrkCutDCAz][UseNchSelCuts]) { + return false; + } + } + return ((track.pt() > trackCuts[TrkCutPtMin][UseNchSelCuts]) && (track.pt() < trackCuts[TrkCutPtMax][UseNchSelCuts]) && (std::abs(track.eta()) < trackCuts[TrkCutEtaCut][UseNchSelCuts]) && (track.tpcChi2NCl() < trackCuts[TrkCutChi2PrTpcCls][UseNchSelCuts]) && (track.tpcNClsFound() >= trackCuts[TrkCutTpcCluster][UseNchSelCuts]) && (track.tpcNClsCrossedRows() >= trackCuts[TrkCutTpcCrossedRows][UseNchSelCuts]) && (track.itsNCls() >= trackCuts[TrkCutItsCluster][UseNchSelCuts])); + } + + void loadGain(aod::BCsWithTimestamps::iterator const& bc) + { + cstFT0RelGain.clear(); + std::string fullPath; + + auto timestamp = bc.timestamp(); + constexpr int ChannelsFT0 = 208; + if (cfgGeneral.cfgCorrLevel == 0) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + fullPath = cfgGeneral.cfgGainEqPath; + fullPath += "/FT0"; + const auto objft0Gain = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!objft0Gain) { + for (auto i{0u}; i < ChannelsFT0; i++) { + cstFT0RelGain.push_back(1.); + } + } else { + cstFT0RelGain = *(objft0Gain); + } + } + } + + template + void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, float& ampl, int fitType, int system) + { + if (fitType == IndexFT0C) { + id = ft0.channelC()[iCh]; + id = id + Ft0IndexA; + ampl = ft0.amplitudeC()[iCh]; + if (system == SameEvent) { + histos.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + histos.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else if (fitType == IndexFT0A) { + id = ft0.channelA()[iCh]; + ampl = ft0.amplitudeA()[iCh]; + if (system == SameEvent) { + histos.fill(HIST("FT0Amp"), id, ampl); + } + ampl = ampl / cstFT0RelGain[id]; + if (system == SameEvent) { + histos.fill(HIST("FT0AmpCorrect"), id, ampl); + } + } else { + LOGF(fatal, "Cor Index %d out of range", fitType); + } + } + + template + int getNsigmaPID(const TTrack& track) + { + // Computing Nsigma arrays for pion, kaon, and protons + std::array nSigmaTPC = {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + std::array nSigmaTOF = {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + std::array nSigmaITS = {itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track), itsResponse.nSigmaITS(track)}; + int pid = -1; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + + std::array nSigmaToUse = cfgPIDConfigs.cfgUseItsPID ? nSigmaITS : nSigmaTPC; // Choose which nSigma to use: TPC or ITS + int useIndexDetector = UseTPC; + useIndexDetector = cfgPIDConfigs.cfgUseItsPID ? UseITS : UseTPC; // Choose which nSigma to use: TPC or ITS + + bool isPion = false; + bool isKaon = false; + bool isProton = false; + bool isDetectedPion = false; + bool isDetectedKaon = false; + bool isDetectedProton = false; + bool isTofPion = false; + bool isTofKaon = false; + bool isTofProton = false; + + isDetectedPion = nSigmaToUse[IndexPionUp] < nSigmaVals[IndexPionUp][useIndexDetector] && nSigmaToUse[IndexPionUp] > nSigmaVals[IndexPionLow][useIndexDetector]; + isDetectedKaon = nSigmaToUse[IndexKaonUp] < nSigmaVals[IndexKaonUp][useIndexDetector] && nSigmaToUse[IndexKaonUp] > nSigmaVals[IndexKaonLow][useIndexDetector]; + isDetectedProton = nSigmaToUse[IndexProtonUp] < nSigmaVals[IndexProtonUp][useIndexDetector] && nSigmaToUse[IndexProtonUp] > nSigmaVals[IndexProtonLow][useIndexDetector]; + + isTofPion = nSigmaTOF[IndexPionUp] < nSigmaVals[IndexPionUp][UseTOF] && nSigmaTOF[IndexPionUp] > nSigmaVals[IndexPionLow][UseTOF]; + isTofKaon = nSigmaTOF[IndexKaonUp] < nSigmaVals[IndexKaonUp][UseTOF] && nSigmaTOF[IndexKaonUp] > nSigmaVals[IndexKaonLow][UseTOF]; + isTofProton = nSigmaTOF[IndexProtonUp] < nSigmaVals[IndexProtonUp][UseTOF] && nSigmaTOF[IndexProtonUp] > nSigmaVals[IndexProtonLow][UseTOF]; + + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && !track.hasTOF()) { + return -1; + } + if (track.pt() > cfgPIDConfigs.cfgTofPtCut && track.hasTOF()) { + isPion = isTofPion && isDetectedPion; + isKaon = isTofKaon && isDetectedKaon; + isProton = isTofProton && isDetectedProton; + } else { + isPion = isDetectedPion; + isKaon = isDetectedKaon; + isProton = isDetectedProton; + } + + if (cfgPIDConfigs.cfgPIDUseRejection && ((isPion && isKaon) || (isPion && isProton) || (isKaon && isProton))) { + return -1; // more than one particle satisfy the criteria + } + + if (isPion) { + pid = UsePions; + } else if (isKaon) { + pid = UseKaons; + } else if (isProton) { + pid = UseProtons; + } else { + return -1; // no particle satisfies the criteria + } + + return pid; // -1 = not identified, 1 = pion, 2 = kaon, 3 = proton + } + + void loadCorrection(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (!cfgEffWeights.cfgEfficiency.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiency) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiency.value.c_str(), "READ"); + mEfficiency = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiency = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiency, timestamp); + } + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiency.value.c_str(), static_cast(mEfficiency)); + } + if (!cfgEffWeights.cfgEfficiencyNch.value.empty()) { + if (cfgEffWeights.cfgLocalEfficiencyNch) { + TFile* fEfficiencyTrigger = nullptr; + fEfficiencyTrigger = TFile::Open(cfgEffWeights.cfgEfficiencyNch.value.c_str(), "READ"); + mEfficiencyNch = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + + } else { + mEfficiencyNch = ccdb->getForTimeStamp(cfgEffWeights.cfgEfficiencyNch, timestamp); + } + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgEfficiencyNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgEfficiencyNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!cfgEffWeights.cfgCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(cfgEffWeights.cfgCentralityWeight, timestamp); + if (mCentralityWeight == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEffWeights.cfgCentralityWeight.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEffWeights.cfgCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + } + correctionsLoaded = true; + } + + bool getEfficiencyCorrectionNch(float& weightNch, float pt) + { + float effNch = 1.; + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(pt); + effNch = mEfficiencyNch->GetBinContent(ptBin); + } else { + effNch = 1.0; + } + if (effNch == 0) { + return false; + } + weightNch = 1. / effNch; + return true; + } + + bool getEfficiencyCorrection(float& weight, float pt, float eta, float vertex) + { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); // use the eta bin corresponding to eta=0 for the trigger particle efficiency + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int vertexBin = mEfficiency->GetZaxis()->FindBin(vertex); // use the vertex bin corresponding to z=0 for the trigger particle efficiency + eff = mEfficiency->GetBinContent(etaBin, ptBin, vertexBin); + } else { + eff = 1.0; + } + if (eff == 0) { + return false; + } + weight = 1. / eff; + return true; + } + + bool getCentralityWeight(float& weightCent, const double centrality) + { + float weight = 1.; + if (mCentralityWeight) { + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + } else { + weight = 1.0; + } + if (weight == 0) { + return false; + } + weightCent = weight; + return true; + } + + template + void nchCounter(const TTracks& tracks, double& multiplicity, bool fillHistogram) // function to count the number of tracks in the event and fill the histogram + { + double nTracksCorrected = 0; + double nTracksUncorrected = 0; + float weightNch = 1.0f; + for (auto const& track : tracks) { + if (!trackSelectedForNch(track)) { + continue; + } + + if (!getEfficiencyCorrectionNch(weightNch, track.pt())) { + continue; + } + + nTracksUncorrected += 1.0; + nTracksCorrected += weightNch; + } + if (fillHistogram) { + histos.fill(HIST("hTrackCorrection2d"), nTracksUncorrected, nTracksCorrected); + } + multiplicity = nTracksCorrected; + } + + template + void fillYieldFt0aFt0c(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + { + + float weff1 = 1.0; + float zvtx = collision.posZ(); + + for (auto const& track1 : tracks) { + + if (!trackSelected(track1)) { + continue; + } + + if (!getEfficiencyCorrection(weff1, track1.pt(), track1.eta(), zvtx)) { + continue; + } + + histos.fill(HIST("Phi_FT0A_FT0C"), RecoDecay::constrainAngle(track1.phi(), 0.0)); + histos.fill(HIST("Eta_FT0A_FT0C"), track1.eta()); + histos.fill(HIST("EtaCorrected_FT0A_FT0C"), track1.eta(), weff1); + histos.fill(HIST("pTFiner_FT0A_FT0C"), track1.pt()); + histos.fill(HIST("pTFinerCorrected_FT0A_FT0C"), track1.pt(), weff1); + } + } + + template + void fillTrackQA(TTrack const& track, int pid, double eventClass, Stage stage, float weff) // function to fill the QA after Nsigma selection + { + if (cfgGeneral.cfgQABasic) { + if (stage == Before && pid == UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), UseCharged); + histos.fill(HIST("Eta"), track.eta(), UseCharged); + histos.fill(HIST("EtaCorrected"), track.eta(), UseCharged, weff); + histos.fill(HIST("pTFiner"), track.pt(), UseCharged, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), UseCharged, eventClass, weff); + + } else if (stage == After && pid > UseCharged) { + histos.fill(HIST("Phi"), RecoDecay::constrainAngle(track.phi(), 0.0), pid); + histos.fill(HIST("Eta"), track.eta(), pid); + histos.fill(HIST("EtaCorrected"), track.eta(), pid, weff); + histos.fill(HIST("pTFiner"), track.pt(), pid, eventClass); + histos.fill(HIST("pTFinerCorrected"), track.pt(), pid, eventClass, weff); + } + } + + if (pid > UseCharged) { + const bool useITS = cfgPIDConfigs.cfgUseItsPID; + double tpcNSigma = 0.0; + double tpcExpSigma = 0.0; + double tofNSigma = 0.0; + double itsNSigma = 0.0; + switch (pid) { + case UsePions: // For Pions + tofNSigma = track.tofNSigmaPi(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPi(); + tpcExpSigma = track.tpcExpSigmaPi(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + case UseKaons: // For Kaons + tofNSigma = track.tofNSigmaKa(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaKa(); + tpcExpSigma = track.tpcExpSigmaKa(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + + case UseProtons: // For Protons + tofNSigma = track.tofNSigmaPr(); + if (!useITS) { + tpcNSigma = track.tpcNSigmaPr(); + tpcExpSigma = track.tpcExpSigmaPr(); + } else { + itsNSigma = itsResponse.nSigmaITS(track); + } + break; + default: + return; + } + + if (stage == Before) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_beforeCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_beforeCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } else if (stage == After) { + if (!useITS) { + histos.fill(HIST("TofTpcNsigma_afterCut"), tpcNSigma, tofNSigma, track.pt(), pid); + } else { + histos.fill(HIST("TofItsNsigma_afterCut"), itsNSigma, tofNSigma, track.pt(), pid); + } + } + + if (cfgPIDConfigs.cfgQAdEdx && !useITS) { + double tpcExpSignal = track.tpcSignal() - (tpcNSigma * tpcExpSigma); + + if (stage == Before) { + histos.fill(HIST("TpcdEdx_ptwise_beforeCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_beforeCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_beforeCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } else if (stage == After) { + histos.fill(HIST("TpcdEdx_ptwise_afterCut"), track.pt(), track.tpcSignal(), tofNSigma, pid); + histos.fill(HIST("ExpTpcdEdx_ptwise_afterCut"), track.pt(), tpcExpSignal, tofNSigma, pid); + histos.fill(HIST("ExpSigma_ptwise_afterCut"), track.pt(), tpcExpSigma, tofNSigma, pid); + } + } + } // if not a charged particle + } + + template + void fillCorrelationsTPCFT0(const TTracks& tracks1, TFT0s const& ft0, float posZ, int system, double eventClass, int corType, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + + if (!trackSelected(track1)) { + continue; + } + if (!getEfficiencyCorrection(triggerWeight, track1.pt(), track1.eta(), posZ)) { // Keep efficiency string empty to disable, efficiency is not used for tpc-ft0 correlations + continue; + } + + if (system == SameEvent) { + fillTrackQA(track1, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(track1, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(track1, UseProtons, eventClass, Before, triggerWeight); + } + + int pidIndex = getNsigmaPID(track1); + + if (system == SameEvent) { + if (pidIndex > UseCharged) { + fillTrackQA(track1, pidIndex, eventClass, After, triggerWeight); + } + if (corType == IndexFT0C) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, eventWeight * triggerWeight); + } + } else if (corType == IndexFT0A) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + histos.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, eventWeight * triggerWeight); + } + } + } + + std::size_t channelSize = 0; + if (corType == IndexFT0C) { + channelSize = ft0.channelC().size(); + } else if (corType == IndexFT0A) { + channelSize = ft0.channelA().size(); + } else { + LOGF(fatal, "Cor Index %d out of range", corType); + } + for (std::size_t iCh = 0; iCh < channelSize; iCh++) { + int chanelid = 0; + float ampl = 0.; + getChannel(ft0, iCh, chanelid, ampl, corType, system); + + auto phi = getPhiFT0(chanelid, corType); + auto eta = getEtaFT0(chanelid, corType); + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - phi, -PIHalf); + float deltaEta = track1.eta() - eta; + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == IndexFT0A) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + sameTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } else if (corType == IndexFT0C) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_same_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + sameTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } + } else if (system == MixedEvent) { + if (corType == IndexFT0A) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0A"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + mixedTpcFt0a->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } else if (corType == IndexFT0C) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Assoc_amp_mixed_TPC_FT0C"), chanelid, ampl); + histos.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), UseCharged, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + if (pidIndex > UseCharged) { + mixedTpcFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, track1.pt(), pidIndex, deltaPhi, deltaEta, ampl * eventWeight * triggerWeight); + } + } + } + } + } + } + + template + void fillCorrelationsFT0AFT0C(TFT0s const& ft0Col1, TFT0s const& ft0Col2, float posZ, int system, double eventClass, float eventWeight) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + int fSampleIndex = static_cast(fRandom.Uniform(0.0, cfgGeneral.cfgSampleSize)); + + float triggerWeight = 1.0f; + std::size_t channelASize = ft0Col1.channelA().size(); + std::size_t channelCSize = ft0Col2.channelC().size(); + // loop over all tracks + for (std::size_t iChA = 0; iChA < channelASize; iChA++) { + + int chanelAid = 0; + float amplA = 0.; + getChannel(ft0Col1, iChA, chanelAid, amplA, IndexFT0A, system); + auto phiA = getPhiFT0(chanelAid, IndexFT0A); + auto etaA = getEtaFT0(chanelAid, IndexFT0A); + + if (system == SameEvent) { + histos.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, eventClass, eventWeight * amplA); + } + + for (std::size_t iChC = 0; iChC < channelCSize; iChC++) { + int chanelCid = 0; + float amplC = 0.; + getChannel(ft0Col2, iChC, chanelCid, amplC, IndexFT0C, system); + auto phiC = getPhiFT0(chanelCid, IndexFT0C); + auto etaC = getEtaFT0(chanelCid, IndexFT0C); + float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); + float deltaEta = etaA - etaC; + + // fill the right sparse and histograms + if (system == SameEvent) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("deltaEta_deltaPhi_same_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + sameFt0aFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } else if (system == MixedEvent) { + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("deltaEta_deltaPhi_mixed_FT0A_FT0C"), deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + mixedFt0aFt0c->getCorrHist()->Fill(0, fSampleIndex, posZ, eventClass, deltaPhi, deltaEta, amplA * amplC * eventWeight * triggerWeight); + } + } + } + } + + bool isGoodRun(int runNumber) + { + for (const auto& excludedRun : cfgGeneral.cfgRunRemoveList.value) { + if (runNumber == excludedRun) { + return false; + } + } + + return true; + } + + void processSameTpcFt0(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + if (cfgGeneral.cfgQABasic) { + eventSelectedIndividually(collision); + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + if (!getCentralityWeight(weightCent, cent)) { + return; // or continue in a loop + } + + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch"), multiplicity); + } + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity, false); + } + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch_corrected"), multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { + return; + } + + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + const auto& ft0 = collision.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, eventClass, IndexFT0A, weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(tracks, ft0, collision.posZ(), SameEvent, eventClass, IndexFT0C, weightCent); + } + } + PROCESS_SWITCH(PidLongRange, processSameTpcFt0, "Process same event for TPC-FT0 correlation", false); + + void processMixedTpcFt0(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + if (!collision1.sel8() || !collision2.sel8()) { // Doing this at the top of mixed events loop to reduce computation (This cut is also there inside eventSelected) + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1, false); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2, false); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + float eventWeight = 1.0f; + + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + getCentralityWeight(weightCent, cent1); + + const auto& ft0 = collision2.foundFT0(); + if (cfgGeneral.cfgAnalyzeTPCFT0A) { + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, eventClass, IndexFT0A, eventWeight * weightCent); + } + if (cfgGeneral.cfgAnalyzeTPCFT0C) { + fillCorrelationsTPCFT0(tracks1, ft0, collision1.posZ(), MixedEvent, eventClass, IndexFT0C, eventWeight * weightCent); + } + } + } + PROCESS_SWITCH(PidLongRange, processMixedTpcFt0, "Process mixed events for TPC-FT0A correlation", false); + + void processSameFt0aFt0c(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + if (cfgGeneral.cfgQABasic) { + eventSelectedIndividually(collision); + } + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + if (!collision.has_foundFT0()) { + return; + } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + getCentralityWeight(weightCent, cent); + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + fillYieldFt0aFt0c(collision, tracks); + + auto multiplicity = static_cast(tracks.size()); + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch"), multiplicity); + } + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity, false); + } + + if (cfgGeneral.cfgQABasic) { + histos.fill(HIST("Nch_corrected"), multiplicity); + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity > cfgEvSel.cfgMaxMultForCorrelations || multiplicity < cfgEvSel.cfgMinMultForCorrelations)) { + return; + } + if (!cfgEvSel.cfgSelCollByNch && (cent > cfgEvSel.cfgMaxCentForCorrelations || cent < cfgEvSel.cfgMinCentForCorrelations)) { + return; + } + + histos.fill(HIST("eventcount"), SameEvent); // because its same event i put it in the 1 bin + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + const auto& ft0 = collision.foundFT0(); + + fillCorrelationsFT0AFT0C(ft0, ft0, collision.posZ(), SameEvent, eventClass, weightCent); + } + PROCESS_SWITCH(PidLongRange, processSameFt0aFt0c, "Process same event for FT0A-FT0C correlation", true); + + void processMixedFt0aFt0c(FilteredCollisions const& collisions, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + + auto getTracksSize = [&tracks, this](FilteredCollisions::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgGeneral.cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + // should have the same event to TPC-FT0A/C correlations + if (!collision1.sel8() || !collision2.sel8()) { + continue; + } + + if (!eventRct(collision1, false) || !eventRct(collision2, false)) { + continue; + } + + auto cent1 = getCentrality(collision1); + auto cent2 = getCentrality(collision2); + + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision1, tracks1.size(), cent1, false)) { + continue; + } + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision2, tracks2.size(), cent2, false)) { + continue; + } + + if (!(collision1.has_foundFT0() && collision2.has_foundFT0())) { + continue; + } + + auto bc = collision1.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + continue; + } + } + loadAlignParam(bc.timestamp()); + loadCorrection(bc.timestamp()); + + auto multiplicity1 = static_cast(tracks1.size()); + auto multiplicity2 = static_cast(tracks2.size()); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks1, multiplicity1, false); + if (cfgEvSel.cfgUseCol2ForMixedEvSel) { + nchCounter(tracks2, multiplicity2, false); + } + } + + if (cfgEvSel.cfgSelCollByNch && (multiplicity1 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity1 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (multiplicity2 > cfgEvSel.cfgMaxMultForCorrelations || multiplicity2 < cfgEvSel.cfgMinMultForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && (cent1 > cfgEvSel.cfgMaxCentForCorrelations || cent1 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + if (!cfgEvSel.cfgSelCollByNch && cfgEvSel.cfgUseCol2ForMixedEvSel && (cent2 > cfgEvSel.cfgMaxCentForCorrelations || cent2 < cfgEvSel.cfgMinCentForCorrelations)) { + continue; + } + + histos.fill(HIST("eventcount"), MixedEvent); // fill the mixed event in the 3 bin + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity1 : cent1; + + float eventWeight = 1.0f; + + if (cfgEffWeights.cfgUseEventWeights) { + eventWeight = 1.0f / it.currentWindowNeighbours(); + } + float weightCent = 1.0f; + getCentralityWeight(weightCent, cent1); + + const auto& ft0Col1 = collision1.foundFT0(); + const auto& ft0Col2 = collision2.foundFT0(); + + fillCorrelationsFT0AFT0C(ft0Col1, ft0Col2, collision1.posZ(), MixedEvent, eventClass, eventWeight * weightCent); + } + } + PROCESS_SWITCH(PidLongRange, processMixedFt0aFt0c, "Process mixed events for FT0A-FT0C correlation", true); + + void processQA(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks, aod::FT0s const&, aod::BCsWithTimestamps const&) + { + eventSelectedIndividually(collision); + + if (!eventRct(collision, true)) { + return; + } + + auto bc = collision.bc_as(); + int currentRunNumber = bc.runNumber(); + if (!cfgGeneral.cfgRunRemoveList.value.empty()) { + if (!isGoodRun(currentRunNumber)) { // Rejects runs if bad run number + return; + } + } + + histos.fill(HIST("h_globalTracks_centT0C_before"), collision.centFT0C(), tracks.size()); + histos.fill(HIST("h_PVTracks_centT0C_before"), collision.centFT0C(), collision.multNTracksPV()); + histos.fill(HIST("h_globalTracks_PVTracks_before"), collision.multNTracksPV(), tracks.size()); + histos.fill(HIST("h_globalTracks_multT0A_before"), collision.multFT0A(), tracks.size()); + histos.fill(HIST("h_globalTracks_multV0A_before"), collision.multFV0A(), tracks.size()); + histos.fill(HIST("h_multV0A_multT0A_before"), collision.multFT0A(), collision.multFV0A()); + histos.fill(HIST("h_multT0C_centT0C_before"), collision.centFT0C(), collision.multFT0C()); + + auto cent = getCentrality(collision); + if (cfgEvSel.cfgUseAdditionalEventCut && !eventSelected(collision, tracks.size(), cent, true)) { + return; + } + + histos.fill(HIST("h_globalTracks_centT0C_after"), collision.centFT0C(), tracks.size()); + histos.fill(HIST("h_PVTracks_centT0C_after"), collision.centFT0C(), collision.multNTracksPV()); + histos.fill(HIST("h_globalTracks_PVTracks_after"), collision.multNTracksPV(), tracks.size()); + histos.fill(HIST("h_globalTracks_multT0A_after"), collision.multFT0A(), tracks.size()); + histos.fill(HIST("h_globalTracks_multV0A_after"), collision.multFV0A(), tracks.size()); + histos.fill(HIST("h_multV0A_multT0A_after"), collision.multFT0A(), collision.multFV0A()); + histos.fill(HIST("h_multT0C_centT0C_after"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("h_centFT0M_centFT0C"), collision.centFT0C(), collision.centFT0M()); + histos.fill(HIST("h_centFV0A_centFT0C"), collision.centFT0C(), collision.centFV0A()); + + if (!collision.has_foundFT0()) { + return; + } + histos.fill(HIST("hEventCount"), HaveFT0Cut); + + loadAlignParam(bc.timestamp()); + loadGain(bc); + loadCorrection(bc.timestamp()); + float weightCent = 1.0f; + + getCentralityWeight(weightCent, cent); + histos.fill(HIST("Centrality"), cent); + histos.fill(HIST("CentralityWeighted"), cent, weightCent); + + histos.fill(HIST("zVtx"), collision.posZ()); + + auto multiplicity = static_cast(tracks.size()); + + histos.fill(HIST("Nch"), multiplicity); + + if (cfgGeneral.cfgStrictTrackCounter) { + nchCounter(tracks, multiplicity, true); + } + + histos.fill(HIST("Nch_corrected"), multiplicity); + + auto eventClass = (cfgEvSel.cfgSelCollByNch) ? multiplicity : cent; + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track : tracks) { + + histos.fill(HIST("hDCAz_before"), track.dcaZ(), track.pt()); + histos.fill(HIST("hDCAxy_before"), track.dcaXY(), track.pt()); + histos.fill(HIST("hTPCNclsPID_before"), track.tpcNClsPID()); + histos.fill(HIST("hTPCNclsFound_before"), track.tpcNClsFound()); + histos.fill(HIST("hTPCCrossedRows_before"), track.tpcNClsCrossedRows()); + + if (!trackSelected(track)) { + continue; + } + + histos.fill(HIST("hTPCNclsPID_after"), track.tpcNClsPID()); + histos.fill(HIST("hTPCNclsFound_after"), track.tpcNClsFound()); + histos.fill(HIST("hTPCCrossedRows_after"), track.tpcNClsCrossedRows()); + histos.fill(HIST("hChi2prTPCcls"), track.tpcChi2NCl()); + histos.fill(HIST("hChi2prITScls"), track.itsChi2NCl()); + histos.fill(HIST("hITSNclsFound"), track.itsNCls()); + + histos.fill(HIST("hDCAz_after"), track.dcaZ(), track.pt(), UseCharged); + histos.fill(HIST("hDCAxy_after"), track.dcaXY(), track.pt(), UseCharged); + + fillTrackQA(track, UseCharged, eventClass, Before, triggerWeight); + fillTrackQA(track, UsePions, eventClass, Before, triggerWeight); + fillTrackQA(track, UseKaons, eventClass, Before, triggerWeight); + fillTrackQA(track, UseProtons, eventClass, Before, triggerWeight); + + int pidIndex = getNsigmaPID(track); + + if (pidIndex > UseCharged) { + fillTrackQA(track, pidIndex, eventClass, After, triggerWeight); + histos.fill(HIST("hDCAz_after"), track.dcaZ(), track.pt(), pidIndex); + histos.fill(HIST("hDCAxy_after"), track.dcaXY(), track.pt(), pidIndex); + } + + } // end of track loop + } + PROCESS_SWITCH(PidLongRange, processQA, "Process for storing the QA for events and tracks", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/r2p2-4-id.cxx b/PWGCF/TwoParticleCorrelations/Tasks/r2p2-4-id.cxx index 3601f1f7fe4..26d6604a9d7 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/r2p2-4-id.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/r2p2-4-id.cxx @@ -128,7 +128,7 @@ struct FillFlagsTable { void processData(soa::Join const& tracks) { int8_t etabin, phibin, binNpid; - for (auto track : tracks) { + for (const auto& track : tracks) { etabin = (track.eta() + 0.8) * 15; // 15= 24/1.6 phibin = 36 * track.phi() / (2 * constants::math::PI); if ((etabin < 0) || (etabin >= 24) || (phibin < 0) || (phibin >= 36)) { @@ -181,7 +181,7 @@ struct FillFlagsTable { void processMC(soa::Join const& recotracks, aod::McParticles const& gentracks) { int8_t etabin, phibin, binNpid; - for (auto track : recotracks) { + for (const auto& track : recotracks) { if (track.has_mcParticle()) { etabin = (track.eta() + 0.8) * 15; // 15= 24/1.6 phibin = 36 * track.phi() / (2 * constants::math::PI); @@ -257,7 +257,7 @@ struct FillFlagsTable { } ftable(binNpid); } - for (auto track : gentracks) { + for (const auto& track : gentracks) { switch (abs(track.pdgCode())) { case 211: histos.fill(HIST("genptpi"), track.pt()); @@ -399,14 +399,14 @@ struct r2p24id { if ((iftrack2 && ((mult1 < 1) || (mult2 < 1))) || ((!iftrack2) && (mult1 < 2))) // Reject Collisions without sufficient particles return; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { histos.fill(HIST("h1d_n1_phi"), track1.phi()); histos.fill(HIST("h1d_n1_eta"), track1.eta()); histos.fill(HIST("h1d_n1_pt"), track1.pt()); } histos.fill(HIST("h1i_n1_multPM"), mult1 + mult2); - for (auto track1 : tracks1) { + for (const auto& track1 : tracks1) { //---Single Particle Distribution (particle1)---------------------------------------- sign1 = (track1.sign() + 1) / 2; hist.h1d_1p[0][sign1]->Fill(track1.pt(), 1.0 / (2.0 * constants::math::PI * track1.pt())); // h1d_n1_pt*1 @@ -415,7 +415,7 @@ struct r2p24id { //----------------------------------------------------------------------- } if (iftrack2) { - for (auto track2 : tracks2) { + for (const auto& track2 : tracks2) { //---Single Particle Distribution (particle2)---------------------------------------- sign2 = (track2.sign() + 1) / 2; hist.h1d_1p[1][sign2]->Fill(track2.pt(), 1.0 / (2.0 * constants::math::PI * track2.pt())); // h1d_n1_pt*2 @@ -424,12 +424,12 @@ struct r2p24id { //----------------------------------------------------------------------- } } - for (auto track1 : tracks1) { + for (const auto& track1 : tracks1) { sign1 = (track1.sign() + 1) / 2; etabin1 = (track1.eta() + 0.8) * 15; // 15= 24/1.6 phibin1 = 36 * track1.phi() / (2 * constants::math::PI); - for (auto track2 : tracks2) { + for (const auto& track2 : tracks2) { if (track1.index() == track2.index()) continue; diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx index 125fe4db09f..73b8b8eecfc 100644 --- a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationPp.cxx @@ -178,7 +178,7 @@ struct TwoParticleCorrelationPp { using BinningType = ColumnBinningPolicy; template - bool isEventSelected(TCollision collision) + bool isEventSelected(const TCollision& collision) { if (evSel8 && !collision.sel8()) { return false; @@ -269,7 +269,7 @@ struct TwoParticleCorrelationPp { } template - void fillQA(TTracks tracks, float multiplicity) + void fillQA(const TTracks& tracks, float multiplicity) { for (const auto& track : tracks) { histos.fill(HIST("yields"), multiplicity, track.pt(), track.eta()); @@ -286,7 +286,7 @@ struct TwoParticleCorrelationPp { } template - void fillCorrelations(TTarget target, TTracks tracks1, TTracks tracks2, float multiplicity, float posZ, int system) + void fillCorrelations(TTarget target, const TTracks& tracks1, const TTracks& tracks2, float multiplicity, float posZ, int system) { for (const auto& track1 : tracks1) { if (isTrackCut(track1) == false) { @@ -360,7 +360,7 @@ struct TwoParticleCorrelationPp { void processMixed(FullCollisions const& collisions, FullTracks const& tracks) { - BinningType bindingOnVtx{{vtxBinsEdges}, true}; + BinningType bindingOnVtx{{vtxBinsEdges}}; auto tracksTuple = std::make_tuple(tracks); SameKindPairGetBin(deltaeta_ix + 1, deltaphi_ix + 1); } - void storeTrackCorrections(std::vector corrs) + void storeTrackCorrections(const std::vector& corrs) { LOGF(info, "Stored NUA&NUE corrections for %d track species", corrs.size()); fhNuaNue_vsZEtaPhiPt = corrs; ccdbstored = true; } - void storePtAverages(std::vector ptavgs) + void storePtAverages(const std::vector& ptavgs) { LOGF(info, "Stored pT average for %d track species", ptavgs.size()); fhPtAvg_vsEtaPhi = ptavgs; @@ -415,7 +415,7 @@ struct twoParticleCorrelations { } } - void init(TList* fOutputList, std::vector idnames) + void init(TList* fOutputList, const std::vector& idnames) { LOGF(info, "Correlations processing engine::init()"); using namespace twopcorrelations; @@ -425,7 +425,7 @@ struct twoParticleCorrelations { TH1::AddDirectory(kFALSE); /* load the species names */ - for (auto id : idnames) { + for (const auto& id : idnames) { LOGF(info, "Adding particle species %s", id.c_str()); tname.push_back(std::string(id.c_str())); } @@ -787,7 +787,7 @@ struct twoParticleCorrelations { /// \brief Get the data collecting engine index corresponding to the passed collision template - int getDCEindex(FilteredCollision collision) + int getDCEindex(const FilteredCollision& collision) { int ixDCE = -1; float cm = collision.centmult()[fMultiplicityIndex]; @@ -837,7 +837,7 @@ struct twoParticleCorrelations { { using namespace twopcorrelations; LOGF(TWOPCORRLOGCOLLISIONS, "Received collision with mask 0x%016lx and %ld tracks", collision.selflags(), tracks.size()); - auto passOptions = [](auto options, auto mask) { + auto passOptions = [](const auto& options, auto mask) { bool all = true; for (auto option : options) { all = all && ((option & mask) != 0UL); diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx new file mode 100644 index 00000000000..d0f62249d26 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMpi.cxx @@ -0,0 +1,2714 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoParticleCorrelationsMpi.cxx +/// \brief task for the MPI-proxy classification of correlation calculations with CF-filtered tracks for O2 analysis +/// \author Emil Gorm Dahlbæk Nielsen + +#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGCF/Core/PairCuts.h" +#include "PWGCF/DataModel/CorrelationsDerived.h" +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/FlowPtContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWConfig.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" + +#include "Common/CCDB/TriggerAliases.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace constants::math; +using namespace o2::analysis::genericframework; + +static constexpr std::array, 1> CfgPairCutDefaults = {{{-1, -1, -1, -1, -1}}}; +static constexpr std::array, 4> LongArrayDouble = {{{{-0.8, -0.5}}, {{0.5, 0.8}}, {{-2, -2}}, {{-2, -2}}}}; +static constexpr int DefaultAcceptancePhiBins = 72; +static constexpr int DefaultAcceptanceEtaBins = 32; +static constexpr int InvalidRunNumber = -1; +static constexpr int FlowNBootstrap = 10; +static constexpr int MaxPtCorrelationOrder = 4; + +struct TwoParticleCorrelationsMpi { + SliceCache cache; + + // Configuration + struct : ConfigurableGroup { + Configurable cfgCutVertex{"cfgCutVertex", 7.0f, "Accepted z-vertex range"}; + Configurable cfgCutPt{"cfgCutPt", 0.5f, "Minimal pT for tracks"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable> cfgFlowPtRef{"cfgFlowPtRef", {0.5f, 3.0f}, "Minimum and maximum pT for reference flow particles"}; + Configurable> cfgFlowPtPOI{"cfgFlowPtPOI", {0.5f, 10.0f}, "Minimum and maximum pT for particles of interest in flow correlations"}; + Configurable cfgPtOrder{"cfgPtOrder", 1, "Only consider pairs for which pT,1 < pT,2 (0 = OFF, 1 = ON)"}; + Configurable cfgTriggerCharge{"cfgTriggerCharge", 0, "Select on charge of trigger particle: 0 = all; 1 = positive; -1 = negative"}; + Configurable cfgAssociatedCharge{"cfgAssociatedCharge", 0, "Select on charge of associated particle: 0 = all charged; 1 = positive; -1 = negative"}; + Configurable cfgPairCharge{"cfgPairCharge", 0, "Select on charge of particle pair: 0 = all; 1 = like sign; -1 = unlike sign"}; + Configurable cfgCorrelationMethod{"cfgCorrelationMethod", 0, "Correlation method, 0 = all, 1 = dd, 2 = ddbar"}; + Configurable cfgTwoTrackCut{"cfgTwoTrackCut", -1, "Two track cut: -1 = off; >0 otherwise distance value (suggested: 0.02)"}; + Configurable cfgTwoTrackCutMinRadius{"cfgTwoTrackCutMinRadius", 0.8f, "Two track cut: radius in m from which two track cuts are applied"}; + Configurable cfgLocalEfficiency{"cfgLocalEfficiency", 0, "0 = OFF and 1 = ON for local efficiency"}; + Configurable cfgDropStepRECO{"cfgDropStepRECO", false, "choice to drop step RECO if efficiency correction is used"}; + Configurable cfgCentBinsForMC{"cfgCentBinsForMC", 0, "0 = generated multiplicity; 1 = reconstructed multiplicity and all associated collisions"}; + } cfgGeneral; + Configurable cfgTrackBitMask{"cfgTrackBitMask", 0, "BitMask for track selection systematics; refer to the enum TrackSelectionCuts in filtering task"}; + Configurable cfgMultCorrelationsMask{"cfgMultCorrelationsMask", 0, "Selection bitmask for the multiplicity correlations. This should match the filter selection cfgEstimatorBitMask."}; + Configurable cfgMultCutFormula{"cfgMultCutFormula", "", "Multiplicity correlations cut formula. A result greater than zero results in accepted event. Parameters: [cFT0C] FT0C centrality, [mFV0A] V0A multiplicity, [mGlob] global track multiplicity, [mPV] PV track multiplicity, [cFT0M] FT0M centrality"}; + + // Suggested values: Photon: 0.004; K0 and Lambda: 0.005 + Configurable> cfgPairCut{"cfgPairCut", {CfgPairCutDefaults.front().data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; + + struct : ConfigurableGroup { + Configurable cfgEfficiencyTrigger{"cfgEfficiencyTrigger", "", "CCDB path to efficiency object for trigger particles"}; + Configurable cfgEfficiencyAssociated{"cfgEfficiencyAssociated", "", "CCDB path to efficiency object for associated particles"}; + Configurable cfgAcceptance{"cfgAcceptance", "", "CCDB path to the GFW acceptance object"}; + Configurable cfgFillAcceptanceWeights{"cfgFillAcceptanceWeights", false, "Fill acceptance maps instead of physics outputs"}; + Configurable cfgAcceptanceRunByRun{"cfgAcceptanceRunByRun", false, "Fill or load run-by-run acceptance weights"}; + Configurable cfgAcceptancePhiBins{"cfgAcceptancePhiBins", int{DefaultAcceptancePhiBins}, "Number of phi bins in acceptance maps"}; + Configurable cfgAcceptanceEtaBins{"cfgAcceptanceEtaBins", int{DefaultAcceptanceEtaBins}, "Number of eta bins in acceptance maps"}; + } cfgCorrections; + + Configurable cfgNuncSeedsTemplateFile{"cfgNuncSeedsTemplateFile", "", "Local ROOT file containing ensembleYieldTemplates"}; + Configurable cfgNuncSeedsTemplate{"cfgNuncSeedsTemplate", "", "CCDB path to the ensemble-yield template ccdb_object"}; + Configurable cfgMinPairAcceptance{"cfgMinPairAcceptance", 0.05f, "Minimum pair acceptance used by the event seed estimator"}; + Configurable cfgMaxPairAcceptanceWeight{"cfgMaxPairAcceptanceWeight", 20.f, "Maximum allowed inverse pair-acceptance weight"}; + Configurable cfgEventSeedEstimatorMethod{"cfgEventSeedEstimatorMethod", 0, "Event seed estimator: 0 = fixed ensemble pair probabilities, 1 = Gamma-regularized weighted MAP-EM"}; + Configurable cfgEventSeedPriorExposure{"cfgEventSeedPriorExposure", 1.f, "Gamma-prior strength in equivalent event exposures for MAP-EM"}; + Configurable cfgEventSeedEMMaxIterations{"cfgEventSeedEMMaxIterations", 10, "Maximum number of weighted MAP-EM iterations per event"}; + Configurable cfgEventSeedEMTolerance{"cfgEventSeedEMTolerance", 1.e-5f, "Relative component-yield convergence tolerance for MAP-EM"}; + Configurable> cfgEventClassifierPercentileLower{"cfgEventClassifierPercentileLower", {}, "Lower event-classifier percentile boundary in each axisMultiplicity bin; empty keeps the raw classifier user axis"}; + Configurable> cfgEventClassifierPercentileUpper{"cfgEventClassifierPercentileUpper", {}, "Upper event-classifier percentile boundary in each axisMultiplicity bin; empty keeps the raw classifier user axis"}; + + Configurable cfgNumMixedEvents{"cfgNumMixedEvents", 5, "Number of mixed events per event"}; + + Configurable cfgVerbosity{"cfgVerbosity", 1, "Verbosity level (0 = major, 1 = per collision)"}; + + Configurable cfgDecayParticleMask{"cfgDecayParticleMask", 0, "Selection bitmask for the decay particles: 0 = no selection"}; + Configurable cfgV0RapidityMax{"cfgV0RapidityMax", 0.8, "Maximum rapidity for the decay particles (0 = no selection)"}; + Configurable cfgUserAxis{"cfgUserAxis", 0, "Additional user axis: 0 = OFF, 1 = invariant mass, 2 = event seed, 3 = nMPI (MCGen only)"}; + Configurable> cfgMcTriggerPDGs{"cfgMcTriggerPDGs", {}, "MC PDG codes to use exclusively as trigger particles and exclude from associated particles. Empty = no selection."}; + + ConfigurableAxis axisVertex{"axisVertex", {7, -7, 7}, "vertex axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {40, -2, 2}, "delta eta axis for histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, "pt associated axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity / centrality axis for histograms"}; + ConfigurableAxis axisMultiplicityQA{"axisMultiplicityQA", {250, 0, 250}, "fine multiplicity / centrality axis for control histograms"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0}, "pt axis for efficiency histograms"}; + + ConfigurableAxis axisUser{"axisUser", {VARIABLE_WIDTH, 1.7, 1.75, 1.8, 1.85, 1.9, 1.95, 2.0, 5.0}, "additional user axis for histograms"}; + + ConfigurableAxis axisMultCorrCent{"axisMultCorrCent", {100, 0, 100}, "multiplicity correlation axis for centralities"}; + ConfigurableAxis axisMultCorrV0{"axisMultCorrV0", {1000, 0, 100000}, "multiplicity correlation axis for V0 multiplicities"}; + ConfigurableAxis axisMultCorrMult{"axisMultCorrMult", {1000, 0, 1000}, "multiplicity correlation axis for track multiplicities"}; + + Configurable cfgRegions{"cfgRegions", {{"refN", "refP", "refFull"}, {-0.8, 0.4, -0.8}, {-0.4, 0.8, 0.8}, {0, 0, 0}, {1, 1, 1}}, "Configurations for GFW regions"}; + Configurable cfgCorrConfig{"cfgCorrConfig", {{"refP {2} refN {-2}", "refP {3} refN {-3}", "refP {4} refN {-4}", "refFull {2 -2}", "refFull {2 2 -2 -2}"}, {"ChGap22", "ChGap32", "ChGap42", "ChFull22", "ChFull24"}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}}, "Configurations for each correlation to calculate"}; + Configurable> cfgPtPtGaps{"cfgPtPtGaps", {LongArrayDouble.front().data(), 4, 2, {"subevent 1", "subevent 2", "subevent 3", "subevent 4"}, {"etamin", "etamax"}}, "{etamin,etamax} for all ptpt-subevents"}; + struct : ConfigurableGroup { + Configurable cfgUsePtCorrWeights{"cfgUsePtCorrWeights", true, "Use multiplicity-based event weighting for pt-pt correlations"}; + Configurable cfgUseMultiplicityFlowWeights{"cfgUseMultiplicityFlowWeights", true, "Use multiplicity-based event weighting for azimuthal correlations"}; + Configurable cfgUseMultiplicityFractionWeights{"cfgUseMultiplicityFractionWeights", false, "Use multiplicity-based event weighting for the spectral fraction"}; + } cfgEventWeight; + + // This filter is applied to AOD and derived data (column names are identical) + Filter collisionZVtxFilter = nabs(aod::collision::posZ) < cfgGeneral.cfgCutVertex; + // This filter is only applied to AOD + Filter collisionVertexTypeFilter = (aod::collision::flags & static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks)) == static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks); + + // Track filters + Filter trackFilter = (nabs(aod::track::eta) < cfgGeneral.cfgCutEta) && (aod::track::pt > cfgGeneral.cfgCutPt) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)); + Filter cfTrackFilter = (nabs(aod::cftrack::eta) < cfgGeneral.cfgCutEta) && (aod::cftrack::pt > cfgGeneral.cfgCutPt) && ncheckbit(aod::track::trackType, as(cfgTrackBitMask)); + + // MC filters + Filter cfMCCollisionFilter = nabs(aod::mccollision::posZ) < cfgGeneral.cfgCutVertex; + Filter cfMCParticleFilter = (nabs(aod::cfmcparticle::eta) < cfgGeneral.cfgCutEta) && (aod::cfmcparticle::pt > cfgGeneral.cfgCutPt); // && (aod::cfmcparticle::sign != 0); //check the sign manually, some specials may be neutral + Filter mcParticleFilter = (nabs(aod::mcparticle::eta) < cfgGeneral.cfgCutEta) && (aod::mcparticle::pt > cfgGeneral.cfgCutPt); + + // Output definitions + OutputObj same{"sameEvent"}; + OutputObj mixed{"mixedEvent"}; + + // persistent caches + std::vector efficiencyAssociatedCache; + + std::unique_ptr multCutFormula; + std::array multCutFormulaParamIndex{}; + + struct Config { + bool mPairCuts = false; + THn* mEfficiencyTrigger = nullptr; + THn* mEfficiencyAssociated = nullptr; + GFWWeights* mAcceptance = nullptr; + bool efficiencyLoaded = false; + bool acceptanceLoaded = false; + int acceptanceRunNumber = InvalidRunNumber; + } cfg; + + struct YieldTemplate { + int trigBin = -1; + int assocBin = -1; + int multBin = -1; + double nchLow = 0.0; + double nchHigh = 0.0; + double trigPtLow = 0.0; + double trigPtHigh = 0.0; + double assocPtLow = 0.0; + double assocPtHigh = 0.0; + std::array parameters{}; + }; + + struct EventSeedPairObservation { + std::size_t templateIndex = 0; + std::array normalizedShapes{}; // near, away, baseline densities normalized by their fitted yields + double acceptanceWeight = 0.0; + }; + + struct EventSeedEstimate { + int nTriggers = 0; + int nCandidatePairs = 0; + int nPairs = 0; + int nPairsWithoutTemplate = 0; + double nearPairs = 0.0; + double awayPairs = 0.0; + double baselinePairs = 0.0; + double probabilityNearPairs = 0.0; + double probabilityAwayPairs = 0.0; + double probabilityBaselinePairs = 0.0; + double rawNearPairs = 0.0; + double rawAwayPairs = 0.0; + double rawBaselinePairs = 0.0; + int nAcceptanceCorrectedPairs = 0; + int nPairsRejectedByAcceptance = 0; + double sumAcceptanceWeights = 0.0; + std::vector pairObservations; + std::vector> templatePriorCounts; + std::array priorComponentCounts{}; + int emIterations = 0; + bool emConverged = false; + bool usedMapEM = false; + + [[nodiscard]] bool hasTriggers() const { return nTriggers > 0; } + [[nodiscard]] bool hasAcceptanceCorrectedPairs() const { return nAcceptanceCorrectedPairs > 0; } + [[nodiscard]] bool isValid() const { return hasTriggers() && hasAcceptanceCorrectedPairs(); } + [[nodiscard]] double nearYield() const { return hasTriggers() ? nearPairs / nTriggers : 0.0; } + [[nodiscard]] double awayYield() const { return hasTriggers() ? awayPairs / nTriggers : 0.0; } + [[nodiscard]] double probabilityNearYield() const { return hasTriggers() ? probabilityNearPairs / nTriggers : 0.0; } + [[nodiscard]] double probabilityAwayYield() const { return hasTriggers() ? probabilityAwayPairs / nTriggers : 0.0; } + [[nodiscard]] double probabilityNuncSeeds() const + { + const double denominator = 1.0 + probabilityNearYield() + probabilityAwayYield(); + return isValid() && denominator > 0.0 ? nTriggers / denominator : -1.0; + } + [[nodiscard]] double nuncSeeds() const + { + const double denominator = 1.0 + nearYield() + awayYield(); + return isValid() && denominator > 0.0 ? nTriggers / denominator : -1.0; + } + }; + + struct PendingSeedTriggerFill { + float pt; + float multiplicity; + float posZ; + float weight; + }; + + struct PendingSeedPairFill { + float deltaEta; + float assocPt; + float triggerPt; + float multiplicity; + float deltaPhi; + float posZ; + float weight; + }; + + struct DetectorClosureResponseCoordinates { + float trueMultiplicity; + float rawMultiplicity; + float correctedMultiplicity; + }; + + std::vector yieldTemplates; + std::vector> pairAcceptanceMaps; + std::vector> pairAcceptanceEtaVertexMaps; + int pairAcceptanceSchemaVersion = 0; + const TList* loadedCcdbYieldTemplateObject = nullptr; + bool eventSeedEstimatorEnabled = false; + bool eventClassifierPercentileAxisEnabled = false; + + enum CorrelationMethod { + All = 0, + Dd, + Ddbar + }; + enum UserAxisMode { + NoUserAxis = 0, + InvariantMassAxis, + EventSeedAxis, + NMPIAxis + }; + HistogramRegistry registry{"registry"}; + PairCuts mPairCuts; + + // Generic Framework outputs + OutputObj fFC{"FlowContainer"}; + OutputObj fFCpt{"FlowPtContainer"}; + OutputObj fFCGen{"FlowContainer_gen"}; + OutputObj fFCptGen{"FlowPtContainer_gen"}; + std::unique_ptr fGFW{std::make_unique()}; + std::unique_ptr fPtAxis; + std::unique_ptr fRndm{std::make_unique()}; + std::vector mCorrConfigs; + std::shared_ptr mAcceptanceWeights; + std::map> mAcceptanceWeightsByRun; + + Service ccdb{}; + Service pdg{}; + + using AodCollisions = soa::Filtered>; + using AodTracks = soa::Filtered>; + + using DerivedCollisions = soa::Filtered; + using DerivedCollisionsCorrected = soa::Filtered; + using DerivedMCClosureCollisions = soa::Join; + using DerivedCollisionsMultSet = soa::Filtered>; + using DerivedCollisionsMultSetCorrected = soa::Filtered>; + using DerivedTracks = soa::Filtered; + + void init(o2::framework::InitContext&) + { + if (cfgUserAxis < NoUserAxis || cfgUserAxis > NMPIAxis) { + LOGF(fatal, "Unsupported cfgUserAxis=%d; use 0 (off), 1 (invariant mass), 2 (event seed), or 3 (nMPI)", cfgUserAxis.value); + } + if (cfgGeneral.cfgCentBinsForMC < 0 || cfgGeneral.cfgCentBinsForMC > 1) { + LOGF(fatal, "Unsupported cfgCentBinsForMC=%d; use 0 (generated multiplicity), 1 (reconstructed multiplicity and all associated collisions)", cfgGeneral.cfgCentBinsForMC.value); + } + const int enabledDerivedSameProcesses = static_cast(doprocessSameDerived) + static_cast(doprocessSameDerivedCorrected) + static_cast(doprocessSameDerivedMultSet) + static_cast(doprocessSameDerivedMultSetCorrected); + if (enabledDerivedSameProcesses > 1) { + LOGF(fatal, "Only one reconstructed derived same-event process can be enabled"); + } + const int enabledDerivedMixedProcesses = static_cast(doprocessMixedDerived) + static_cast(doprocessMixedDerivedCorrected) + static_cast(doprocessMixedDerivedMultSet) + static_cast(doprocessMixedDerivedMultSetCorrected); + if (enabledDerivedMixedProcesses > 1) { + LOGF(fatal, "Only one reconstructed derived mixed-event process can be enabled"); + } + if ((doprocessMCSameDerived || doprocessMCDetectorClosure) && enabledDerivedSameProcesses > 0) { + LOGF(fatal, "The MC generated/reconstructed same-event processes are mutually exclusive with the reconstructed derived same-event processes because they also fill those outputs"); + } + if (doprocessMCSameDerived && doprocessMCDetectorClosure) { + LOGF(fatal, "processMCSameDerived and processMCDetectorClosure are mutually exclusive because both fill generated and reconstructed same-event outputs"); + } + if (doprocessSameGenMC && (doprocessMCSameDerived || doprocessMCDetectorClosure)) { + LOGF(fatal, "processSameGenMC is mutually exclusive with processMCSameDerived and processMCDetectorClosure because they fill the same generated outputs"); + } + if (doprocessMCDetectorClosure && cfgGeneral.cfgCentBinsForMC != 0) { + LOGF(fatal, "processMCDetectorClosure requires cfgCentBinsForMC=0 so generated and reconstructed outputs are both filled versus true charged multiplicity"); + } + if (doprocessMCDetectorClosure && cfgUserAxis == NMPIAxis) { + LOGF(fatal, "processMCDetectorClosure does not support cfgUserAxis=3 (true N MPI); use the true charged-multiplicity axis for detector closure"); + } + if (doprocessMCDetectorClosure && cfgCorrections.cfgFillAcceptanceWeights) { + LOGF(fatal, "processMCDetectorClosure cannot be used to produce acceptance weights"); + } + if (cfgUserAxis == NMPIAxis && + (!(doprocessSameGenMC || doprocessMCSameDerived) || + doprocessSameAOD || enabledDerivedSameProcesses > 0 || + doprocessMixedAOD || enabledDerivedMixedProcesses > 0 || doprocessMCMixedDerived || + cfgGeneral.cfgCentBinsForMC != 0 || cfgCorrections.cfgFillAcceptanceWeights)) { + LOGF(fatal, "cfgUserAxis=3 supports generated MC same events only: enable processSameGenMC or processMCSameDerived and disable all other same/mixed processes"); + } + if (cfgCorrections.cfgFillAcceptanceWeights && !cfgCorrections.cfgAcceptance.value.empty()) { + LOGF(fatal, "cfgFillAcceptanceWeights and cfgAcceptance are mutually exclusive: produce and apply acceptance weights in separate jobs"); + } + if (cfgCorrections.cfgFillAcceptanceWeights && !(doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected || doprocessMCSameDerived)) { + LOGF(fatal, "cfgFillAcceptanceWeights requires a reconstructed derived same-event process"); + } + if (cfgCorrections.cfgAcceptancePhiBins < 1 || cfgCorrections.cfgAcceptanceEtaBins < 1) { + LOGF(fatal, "Acceptance-map bin counts must be positive"); + } + if (!cfgNuncSeedsTemplateFile.value.empty() && !cfgNuncSeedsTemplate.value.empty()) { + LOGF(fatal, "Configure only one template source: cfgNuncSeedsTemplateFile or cfgNuncSeedsTemplate"); + } + if (cfgEventSeedEstimatorMethod < 0 || cfgEventSeedEstimatorMethod > 1) { + LOGF(fatal, "Unsupported cfgEventSeedEstimatorMethod=%d; use 0 (fixed probabilities) or 1 (weighted MAP-EM)", cfgEventSeedEstimatorMethod.value); + } + if (cfgEventSeedPriorExposure < 0.f || cfgEventSeedEMMaxIterations < 1 || cfgEventSeedEMTolerance <= 0.f) { + LOGF(fatal, "MAP-EM configuration requires non-negative prior exposure, at least one iteration, and positive tolerance"); + } + eventSeedEstimatorEnabled = !cfgNuncSeedsTemplateFile.value.empty() || !cfgNuncSeedsTemplate.value.empty(); + if (cfgUserAxis == EventSeedAxis && !eventSeedEstimatorEnabled) { + LOGF(fatal, "cfgUserAxis=2 requires an event-seed template configured through cfgNuncSeedsTemplateFile or cfgNuncSeedsTemplate"); + } + const bool hasLowerPercentileBoundaries = !cfgEventClassifierPercentileLower->empty(); + const bool hasUpperPercentileBoundaries = !cfgEventClassifierPercentileUpper->empty(); + if (hasLowerPercentileBoundaries != hasUpperPercentileBoundaries) { + LOGF(fatal, "Configure both cfgEventClassifierPercentileLower and cfgEventClassifierPercentileUpper, or leave both empty"); + } + eventClassifierPercentileAxisEnabled = hasLowerPercentileBoundaries; + if (eventClassifierPercentileAxisEnabled) { + const int nMultiplicityBins = AxisSpec(axisMultiplicity).getNbins(); + if (static_cast(cfgEventClassifierPercentileLower->size()) != nMultiplicityBins || + static_cast(cfgEventClassifierPercentileUpper->size()) != nMultiplicityBins) { + LOGF(fatal, "Event-classifier percentile boundary vectors must each contain exactly %d values, one per axisMultiplicity bin", nMultiplicityBins); + } + for (int multBin = 0; multBin < nMultiplicityBins; ++multBin) { + if (!std::isfinite(cfgEventClassifierPercentileLower->at(multBin)) || + !std::isfinite(cfgEventClassifierPercentileUpper->at(multBin)) || + cfgEventClassifierPercentileLower->at(multBin) >= cfgEventClassifierPercentileUpper->at(multBin)) { + LOGF(fatal, "Invalid event-classifier percentile boundaries in multiplicity bin %d: lower=%g upper=%g", + multBin, cfgEventClassifierPercentileLower->at(multBin), cfgEventClassifierPercentileUpper->at(multBin)); + } + } + if (cfgUserAxis == EventSeedAxis || cfgUserAxis == NMPIAxis) { + const std::vector expectedEdges{-1.5, -0.5, 0.5, 1.5, 2.5}; + const auto& configuredEdges = AxisSpec(axisUser).binEdges; + const float threshold = 1.e-6; + if (configuredEdges.size() != expectedEdges.size() || + !std::equal(configuredEdges.begin(), configuredEdges.end(), expectedEdges.begin(), + [&threshold](double lhs, double rhs) { return std::abs(lhs - rhs) < threshold; })) { + LOGF(fatal, "Percentile-class event classifier axis requires axisUser edges {-1.5,-0.5,0.5,1.5,2.5}"); + } + } + } + LOGF(info, "Event seed estimator histogram booking: %s (local template='%s', CCDB template='%s')", + eventSeedEstimatorEnabled ? "enabled" : "disabled", + cfgNuncSeedsTemplateFile.value.c_str(), cfgNuncSeedsTemplate.value.c_str()); + + registry.add("yields", "multiplicity/centrality vs pT vs eta", {HistType::kTH3F, {{axisMultiplicityQA, "/multiplicity/centrality"}, {40, 0, 20, "p_{T}"}, {100, -2, 2, "#eta"}}}); + registry.add("etaphi", "multiplicity/centrality vs eta vs phi", {HistType::kTH3F, {{axisMultiplicityQA, "multiplicity/centrality"}, {100, -2, 2, "#eta"}, {200, 0, o2::constants::math::TwoPI, "#varphi"}}}); + if (doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected) { + if (cfgMultCorrelationsMask == 0) { + LOGF(fatal, "cfgMultCorrelationsMask can not be 0 when MultSet process functions are in use."); + } + std::vector multAxes; + if ((cfgMultCorrelationsMask & aod::cfmultset::CentFT0C) != 0) { + multAxes.emplace_back(axisMultCorrCent, "FT0C centrality"); + } + if ((cfgMultCorrelationsMask & aod::cfmultset::MultFV0A) != 0) { + multAxes.emplace_back(axisMultCorrV0, "V0A multiplicity"); + } + if ((cfgMultCorrelationsMask & aod::cfmultset::MultNTracksPV) != 0) { + multAxes.emplace_back(axisMultCorrMult, "Nch PV"); + } + if ((cfgMultCorrelationsMask & aod::cfmultset::MultNTracksGlobal) != 0) { + multAxes.emplace_back(axisMultCorrMult, "Nch Global"); + } + if ((cfgMultCorrelationsMask & aod::cfmultset::CentFT0M) != 0) { + multAxes.emplace_back(axisMultCorrCent, "FT0M centrality"); + } + registry.add("multCorrelations", "Multiplicity correlations", {HistType::kTHnSparseF, multAxes}); + } + registry.add("multiplicity", "event multiplicity", {HistType::kTH1F, {{axisMultiplicityQA, "/multiplicity/centrality"}}}); + if (doprocessMCDetectorClosure) { + registry.add("detectorClosure/rawMultiplicityVsTrue", "raw reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{raw,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); + registry.add("detectorClosure/correctedMultiplicityVsTrue", "corrected reconstructed versus true charged multiplicity;N_{ch}^{true};N_{ch}^{corr,reco}", {HistType::kTH2F, {axisMultiplicity, axisMultiplicity}}); + registry.add("detectorClosure/triggerResponseRaw", "efficiency-weighted trigger response versus raw reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{raw,reco}"}, {axisPtTrigger, "p_{T,trig}"}}}); + registry.add("detectorClosure/triggerResponseCorrected", "efficiency-weighted trigger response versus corrected reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{corr,reco}"}, {axisPtTrigger, "p_{T,trig}"}}}); + registry.add("detectorClosure/pairResponseRaw", "efficiency-weighted pair response versus raw reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{raw,reco}"}, {axisDeltaEta, "#Delta#eta"}, {axisPtAssoc, "p_{T,assoc}"}, {axisPtTrigger, "p_{T,trig}"}, {axisDeltaPhi, "#Delta#varphi"}}}); + registry.add("detectorClosure/pairResponseCorrected", "efficiency-weighted pair response versus corrected reconstructed multiplicity", {HistType::kTHnSparseF, {{axisMultiplicity, "N_{ch}^{true}"}, {axisMultiplicity, "N_{ch}^{corr,reco}"}, {axisDeltaEta, "#Delta#eta"}, {axisPtAssoc, "p_{T,assoc}"}, {axisPtTrigger, "p_{T,trig}"}, {axisDeltaPhi, "#Delta#varphi"}}}); + registry.add("detectorClosure/profileCorrectedMultiplicityVsTrue", "mean corrected reconstructed multiplicity versus true multiplicity;N_{ch}^{true};#LT N_{ch}^{corr,reco} #GT", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("detectorClosure/profileTrueMultiplicityVsCorrected", "mean true multiplicity versus corrected reconstructed multiplicity;N_{ch}^{corr,reco};#LT N_{ch}^{true} #GT", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("detectorClosure/recoCollisionsPerMCCollision", "accepted reconstructed collisions per MC collision;N_{coll}^{reco};events", {HistType::kTH1F, {{11, -0.5, 10.5}}}); + registry.add("detectorClosure/status", "detector-closure event status;status;events", {HistType::kTH1F, {{3, -0.5, 2.5}}}); + auto* detectorClosureStatus = registry.get(HIST("detectorClosure/status")).get(); + detectorClosureStatus->GetXaxis()->SetBinLabel(1, "no reconstructed collision"); + detectorClosureStatus->GetXaxis()->SetBinLabel(2, "one best collision"); + detectorClosureStatus->GetXaxis()->SetBinLabel(3, "invalid best-collision count"); + } + if (eventSeedEstimatorEnabled) { + registry.add("eventSeedEstimator", "event-level template estimator", {HistType::kTHnSparseF, {{axisMultiplicityQA, "multiplicity"}, {100, -0.5, 99.5, "N_{trig}"}, {200, 0, 20, "Y_{near}"}, {200, 0, 20, "Y_{away}"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); + registry.add("eventSeedPairProbabilities", "summed pair probabilities", {HistType::kTH3F, {{200, 0, 200, "#Sigma P_{baseline}"}, {200, 0, 200, "#Sigma P_{near}"}, {200, 0, 200, "#Sigma P_{away}"}}}); + registry.add("eventSeedEstimatorStatus", "event-estimator status", {HistType::kTH1F, {{5, -0.5, 4.5, "status"}}}); + registry.add("eventSeedTemplateCoverage", "template coverage vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {102, -0.01, 1.01, "matched/candidate pairs"}}}); + registry.add("eventSeedEstimateVsMultiplicity", "event-level seed estimate vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 100, "N_{uncorrelated seeds}"}}}); + registry.add("eventSeedFixedProbabilityVsMultiplicity", "fixed-probability event Seed versus multiplicity;multiplicity;N_{seed}^{probability sum}", {HistType::kTH2F, {axisMultiplicity, {1000, 0, 100}}}); + registry.add("eventNearYieldVsMultiplicity", "event-level near yield vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 20, "Y_{near}"}}}); + registry.add("eventAwayYieldVsMultiplicity", "event-level away yield vs multiplicity", {HistType::kTH2F, {{axisMultiplicityQA, "multiplicity"}, {200, 0, 20, "Y_{away}"}}}); + registry.add("profileEventNTriggers", "mean event trigger count", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventNearPairs", "mean summed near-pair probability", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventAwayPairs", "mean summed away-pair probability", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventBaselinePairs", "mean summed baseline-pair probability", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventRawNearPairs", "mean raw summed near-pair probability", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventRawAwayPairs", "mean raw summed away-pair probability", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventRawBaselinePairs", "mean raw summed baseline-pair probability", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventAcceptanceCoverage", "fraction of template-matched pairs with valid acceptance", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventMeanAcceptanceWeight", "mean inverse acceptance weight for corrected pairs", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("eventSeedAcceptanceWeight", "inverse pair-acceptance weight", {HistType::kTH1F, {{200, 0, 20, "1/A"}}}); + registry.add("profileEventNearYield", "mean event near yield", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventAwayYield", "mean event away yield", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventNuncSeeds", "mean event uncorrelated-seed estimate", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventEstimatorValidity", "fraction of events with a valid seed estimate", {HistType::kTProfile, {axisMultiplicity}}); + if (cfgEventSeedEstimatorMethod == 1) { + registry.add("eventSeedEMIterations", "weighted MAP-EM iterations;iterations;events", {HistType::kTH1F, {{cfgEventSeedEMMaxIterations + 1, -0.5, cfgEventSeedEMMaxIterations + 0.5}}}); + registry.add("profileEventEMConvergence", "weighted MAP-EM convergence fraction", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventEMNearPrior", "mean MAP-EM near-component prior mode", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventEMAwayPrior", "mean MAP-EM away-component prior mode", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("profileEventEMBaselinePrior", "mean MAP-EM baseline-component prior mode", {HistType::kTProfile, {axisMultiplicity}}); + registry.add("eventSeedMAPVsProbability", "MAP-EM versus fixed-probability seed estimate;N_{seed}^{probability sum};N_{seed}^{MAP-EM}", {HistType::kTH2F, {{200, 0, 100}, {200, 0, 100}}}); + registry.add("eventSeedMAPEMVsMultiplicity", "MAP-EM event Seed versus multiplicity;multiplicity;N_{seed}^{MAP-EM}", {HistType::kTH2F, {axisMultiplicity, {1000, 0, 100}}}); + registry.add("profileEventProbabilityNuncSeeds", "mean fixed-probability seed estimate", {HistType::kTProfile, {axisMultiplicity}}); + } + auto* estimatorStatus = registry.get(HIST("eventSeedEstimatorStatus")).get(); + estimatorStatus->GetXaxis()->SetBinLabel(1, "unused"); + estimatorStatus->GetXaxis()->SetBinLabel(2, "no template-covered triggers"); + estimatorStatus->GetXaxis()->SetBinLabel(3, "no template-covered pairs"); + estimatorStatus->GetXaxis()->SetBinLabel(4, "no acceptance-corrected pairs"); + estimatorStatus->GetXaxis()->SetBinLabel(5, "valid estimate"); + registry.add("mcValidation/estimatedSeedsVsTrueNMPI", "template estimator response;N_{MPI}^{true};N_{seed}^{estimated}", {HistType::kTH2F, {{101, -0.5, 100.5}, {202, -0.5, 100.5}}}); + registry.add("mcValidation/estimatedSeedsVsTrueNMPIVsMultiplicity", "event-level template estimator response;multiplicity;N_{MPI}^{true};N_{seed}^{estimated}", {HistType::kTH3F, {axisMultiplicity, {101, -0.5, 100.5}, {202, -0.5, 100.5}}}); + if (cfgEventSeedEstimatorMethod == 1) { + registry.add("mcValidation/probabilityEstimatedSeedsVsTrueNMPI", "fixed-probability estimator response;N_{MPI}^{true};N_{seed}^{probability sum}", {HistType::kTH2F, {{101, -0.5, 100.5}, {202, -0.5, 100.5}}}); + } + registry.add("mcValidation/profileEstimatedSeedsVsTrueNMPI", "mean template estimate;N_{MPI}^{true};#LT N_{seed}^{estimated} #GT", {HistType::kTProfile, {{101, -0.5, 100.5}}}); + registry.add("mcValidation/profileBiasVsTrueNMPI", "mean estimator bias;N_{MPI}^{true};#LT N_{seed}^{estimated} - N_{MPI}^{true} #GT", {HistType::kTProfile, {{101, -0.5, 100.5}}}); + registry.add("mcValidation/relativeResidualVsTrueNMPI", "relative estimator residual;N_{MPI}^{true};(N_{seed}^{estimated} - N_{MPI}^{true}) / N_{MPI}^{true}", {HistType::kTH2F, {{101, -0.5, 100.5}, {240, -3., 3.}}}); + registry.add("mcValidation/trueNMPIVsMultiplicity", "true MPI count versus reconstructed multiplicity;multiplicity;N_{MPI}^{true}", {HistType::kTH2F, {{100, 0., 100.}, {101, -0.5, 100.5}}}); + registry.add("mcValidation/templateCoverageVsTrueNMPI", "template pair coverage versus true MPI count;N_{MPI}^{true};matched / candidate pairs", {HistType::kTH2F, {{101, -0.5, 100.5}, {102, -0.01, 1.01}}}); + registry.add("mcValidation/status", "MC template-estimator validation status;status;events", {HistType::kTH1F, {{7, -0.5, 6.5}}}); + auto* mcValidationStatus = registry.get(HIST("mcValidation/status")).get(); + mcValidationStatus->GetXaxis()->SetBinLabel(1, "no selected reconstructed collision"); + mcValidationStatus->GetXaxis()->SetBinLabel(2, "invalid N MPI"); + mcValidationStatus->GetXaxis()->SetBinLabel(3, "outside template multiplicity"); + mcValidationStatus->GetXaxis()->SetBinLabel(4, "no template-covered triggers"); + mcValidationStatus->GetXaxis()->SetBinLabel(5, "no template-covered pairs"); + mcValidationStatus->GetXaxis()->SetBinLabel(6, "no acceptance-corrected pairs"); + mcValidationStatus->GetXaxis()->SetBinLabel(7, "valid response"); + registry.add("mcValidation/generated/estimatedSeedsVsTrueNMPI", "generated-level template estimator response;N_{MPI}^{true};N_{seed,gen}^{estimated}", {HistType::kTH2F, {{101, -0.5, 100.5}, {202, -0.5, 100.5}}}); + registry.add("mcValidation/generated/estimatedSeedsVsTrueNMPIVsMultiplicity", "generated event-level template estimator response;N_{ch}^{gen};N_{MPI}^{true};N_{seed,gen}^{estimated}", {HistType::kTH3F, {axisMultiplicity, {101, -0.5, 100.5}, {202, -0.5, 100.5}}}); + if (cfgEventSeedEstimatorMethod == 1) { + registry.add("mcValidation/generated/probabilityEstimatedSeedsVsTrueNMPI", "generated-level fixed-probability estimator response;N_{MPI}^{true};N_{seed,gen}^{probability sum}", {HistType::kTH2F, {{101, -0.5, 100.5}, {202, -0.5, 100.5}}}); + } + registry.add("mcValidation/generated/profileEstimatedSeedsVsTrueNMPI", "mean generated-level template estimate;N_{MPI}^{true};#LT N_{seed,gen}^{estimated} #GT", {HistType::kTProfile, {{101, -0.5, 100.5}}}); + registry.add("mcValidation/generated/profileBiasVsTrueNMPI", "mean generated-level estimator bias;N_{MPI}^{true};#LT N_{seed,gen}^{estimated} - N_{MPI}^{true} #GT", {HistType::kTProfile, {{101, -0.5, 100.5}}}); + registry.add("mcValidation/generated/relativeResidualVsTrueNMPI", "generated-level relative estimator residual;N_{MPI}^{true};(N_{seed,gen}^{estimated} - N_{MPI}^{true}) / N_{MPI}^{true}", {HistType::kTH2F, {{101, -0.5, 100.5}, {240, -3., 3.}}}); + registry.add("mcValidation/generated/trueNMPIVsMultiplicity", "true MPI count versus generated charged multiplicity;N_{ch}^{gen};N_{MPI}^{true}", {HistType::kTH2F, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}}); + registry.add("mcValidation/generated/templateCoverageVsTrueNMPI", "generated-level template pair coverage versus true MPI count;N_{MPI}^{true};matched / candidate pairs", {HistType::kTH2F, {{101, -0.5, 100.5}, {102, -0.01, 1.01}}}); + registry.add("mcValidation/generated/status", "generated-level MC template-estimator validation status;status;events", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + auto* generatedValidationStatus = registry.get(HIST("mcValidation/generated/status")).get(); + generatedValidationStatus->GetXaxis()->SetBinLabel(1, "invalid N MPI"); + generatedValidationStatus->GetXaxis()->SetBinLabel(2, "outside template multiplicity"); + generatedValidationStatus->GetXaxis()->SetBinLabel(3, "no template-covered triggers"); + generatedValidationStatus->GetXaxis()->SetBinLabel(4, "no template-covered pairs"); + generatedValidationStatus->GetXaxis()->SetBinLabel(5, "no acceptance-corrected pairs"); + generatedValidationStatus->GetXaxis()->SetBinLabel(6, "valid response"); + } + registry.add("yvspt", "y vs pT", {HistType::kTH2F, {{100, -1, 1, "y"}, {100, 0, 20, "p_{T}"}}}); // y vs pT for all tracks (control histogram) + + const int maxMixBin = AxisSpec(axisMultiplicity).getNbins() * AxisSpec(axisVertex).getNbins(); + // The bin numbers for the control histograms (eventcount_*) come from getBin(...) and are the following: #mult_bin * #number_of_z_bins + #zbin + registry.add("eventcount_same", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("eventcount_mixed", "bin", {HistType::kTH1F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}}}); + registry.add("trackcount_same", "bin", {HistType::kTH2F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {10, -0.5, 9.5}}}); + registry.add("trackcount_mixed", "bin", {HistType::kTH3F, {{maxMixBin + 2, -2.5, -0.5 + maxMixBin, "bin"}, {10, -0.5, 9.5}, {10, -0.5, 9.5}}}); + if (cfgCorrections.cfgFillAcceptanceWeights && !cfgCorrections.cfgAcceptanceRunByRun) { + AxisSpec phiAxis{cfgCorrections.cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; + AxisSpec etaAxis{cfgCorrections.cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; + mAcceptanceWeights = registry.add("phi_eta_vtxz_ref", "Reference-track acceptance;#varphi;#eta;z_{vtx} (cm)", {HistType::kTH3D, {phiAxis, etaAxis, axisVertex}}); + } + + mPairCuts.SetHistogramRegistry(®istry); + + if (cfgPairCut->get("Photon") > 0 || cfgPairCut->get("K0") > 0 || cfgPairCut->get("Lambda") > 0 || cfgPairCut->get("Phi") > 0 || cfgPairCut->get("Rho") > 0) { + mPairCuts.SetPairCut(PairCuts::Photon, cfgPairCut->get("Photon")); + mPairCuts.SetPairCut(PairCuts::K0, cfgPairCut->get("K0")); + mPairCuts.SetPairCut(PairCuts::Lambda, cfgPairCut->get("Lambda")); + mPairCuts.SetPairCut(PairCuts::Phi, cfgPairCut->get("Phi")); + mPairCuts.SetPairCut(PairCuts::Rho, cfgPairCut->get("Rho")); + cfg.mPairCuts = true; + } + + if (cfgGeneral.cfgTwoTrackCut > 0) { + mPairCuts.SetTwoTrackCuts(cfgGeneral.cfgTwoTrackCut, cfgGeneral.cfgTwoTrackCutMinRadius); + } + + // --- OBJECT INIT --- + + if (!cfgMultCutFormula.value.empty()) { + multCutFormula = std::make_unique("multCutFormula", cfgMultCutFormula.value.c_str()); + std::fill_n(multCutFormulaParamIndex.begin(), std::size(multCutFormulaParamIndex), ~0u); + std::array pars = {"cFT0C", "mFV0A", "mPV", "mGlob", "cFT0M"}; // must correspond the order of MultiplicityEstimators + for (uint i = 0, n = multCutFormula->GetNpar(); i < n; ++i) { + auto m = std::find(pars.begin(), pars.end(), multCutFormula->GetParName(i)); + if (m == pars.end()) { + + LOGF(warning, "Unknown parameter in cfgMultCutFormula: %s", multCutFormula->GetParName(i)); + continue; + } + if ((cfgMultCorrelationsMask.value & (1u << i)) == 0) { + LOGF(warning, "The centrality/multiplicity estimator %s is not available to be used in cfgMultCutFormula. Ensure cfgMultCorrelationsMask is correct and matches the CFMultSets in derived data."); + } else { + multCutFormulaParamIndex[std::distance(pars.begin(), m)] = i; + LOGF(info, "Multiplicity cut parameter %s in use.", m->c_str()); + } + } + } + + std::vector corrAxis; + corrAxis.reserve(6); + corrAxis.emplace_back(axisDeltaEta, "#Delta#eta"); + corrAxis.emplace_back(axisPtAssoc, "p_{T} (GeV/c)"); + corrAxis.emplace_back(axisPtTrigger, "p_{T} (GeV/c)"); + corrAxis.emplace_back(axisMultiplicity, "multiplicity / centrality"); + corrAxis.emplace_back(axisDeltaPhi, "#Delta#varphi (rad)"); + corrAxis.emplace_back(axisVertex, "z-vtx (cm)"); + std::vector effAxis = {{axisEtaEfficiency, "#eta"}, + {axisPtEfficiency, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}}; + std::vector userAxis; + std::vector userMixingAxis; + + if (cfgUserAxis == InvariantMassAxis) { + userAxis.emplace_back(axisUser, "m (GeV/c^2)"); + userMixingAxis.emplace_back(axisUser, "m (GeV/c^2)"); + } else if (cfgUserAxis == EventSeedAxis) { + const char* title = eventClassifierPercentileAxisEnabled ? "N_{seed} percentile class" : "N_{seed}"; + userAxis.emplace_back(axisUser, title); + userMixingAxis.emplace_back(axisUser, title); + } else if (cfgUserAxis == NMPIAxis) { + const char* title = eventClassifierPercentileAxisEnabled ? "N_{MPI}^{true} percentile class" : "N_{MPI}^{true}"; + userAxis.emplace_back(axisUser, title); + userMixingAxis.emplace_back(axisUser, title); + } + same.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, userAxis)); + mixed.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, userMixingAxis)); + + same->setTrackEtaCut(cfgGeneral.cfgCutEta); + mixed->setTrackEtaCut(cfgGeneral.cfgCutEta); + + if (!cfgCorrections.cfgEfficiencyAssociated.value.empty()) { + efficiencyAssociatedCache.reserve(512); + } + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time + + loadLocalYieldTemplates(); + + // Generic Framework init + AxisSpec ptAxisSpec = axisPtTrigger; + const auto validateFlowPtRange = [&ptAxisSpec](const std::pair& range, const char* name) { + if (!std::isfinite(range.first) || !std::isfinite(range.second) || range.first >= range.second) { + LOGF(fatal, "%s must define a finite, increasing pT range; received {%g, %g}", name, range.first, range.second); + } + if (range.first < ptAxisSpec.binEdges.front() || range.second > ptAxisSpec.binEdges.back()) { + LOGF(fatal, "%s={%g, %g} must be contained in axisPtTrigger={%g, %g}", + name, range.first, range.second, ptAxisSpec.binEdges.front(), ptAxisSpec.binEdges.back()); + } + }; + validateFlowPtRange(cfgGeneral.cfgFlowPtRef.value, "cfgFlowPtRef"); + validateFlowPtRange(cfgGeneral.cfgFlowPtPOI.value, "cfgFlowPtPOI"); + const int nPtBins = static_cast(ptAxisSpec.binEdges.size()) - 1; + fPtAxis = std::make_unique(nPtBins, ptAxisSpec.binEdges.data()); + if (cfgRegions->GetSize() < 0) { + LOGF(error, "Configuration contains vectors of different size - check the GFWRegions configurable"); + } + for (auto i(0); i < cfgRegions->GetSize(); ++i) { + fGFW->AddRegion(cfgRegions->GetNames()[i], cfgRegions->GetEtaMin()[i], cfgRegions->GetEtaMax()[i], (cfgRegions->GetpTDifs()[i] != 0) ? nPtBins + 1 : 1, cfgRegions->GetBitmasks()[i]); + } + for (auto i = 0; i < cfgCorrConfig->GetSize(); ++i) { + mCorrConfigs.push_back(fGFW->GetCorrelatorConfig(cfgCorrConfig->GetCorrs()[i], cfgCorrConfig->GetHeads()[i], cfgCorrConfig->GetpTDifs()[i] != 0)); + } + if (mCorrConfigs.empty()) { + LOGF(error, "Configuration contains vectors of different size - check the GFWCorrConfig configurable"); + } + fGFW->CreateRegions(); + int nPtPtSubevents = 0; + for (uint32_t i = 0; i < cfgPtPtGaps->rows(); ++i) { + if (cfgPtPtGaps->get(i, uint32_t{0}) < -1. || cfgPtPtGaps->get(i, uint32_t{1}) < -1.) { + continue; + } + ++nPtPtSubevents; + } + + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { + auto recoProfiles = std::make_unique(); + addConfigObjectsToObjArray(recoProfiles.get(), mCorrConfigs, cfgUserAxis == EventSeedAxis && eventClassifierPercentileAxisEnabled); + fFC.setObject(new FlowContainer("FlowContainer")); + fFC->SetXAxis(fPtAxis.get()); + fFC->Initialize(recoProfiles.get(), axisMultiplicity, FlowNBootstrap); + + fFCpt.setObject(new FlowPtContainer("FlowPtContainer")); + fFCpt->setEventWeight(cfgEventWeight.cfgUsePtCorrWeights ? eventweight::TupleWeight : eventweight::UnityWeight); + fFCpt->setUseCentralMoments(true); + fFCpt->setUseGapMethod(true); + fFCpt->initialise(axisMultiplicity, MaxPtCorrelationOrder, cfgCorrConfig.value, FlowNBootstrap); + fFCpt->initialiseSubevent(axisMultiplicity, MaxPtCorrelationOrder, nPtPtSubevents, FlowNBootstrap); + } + if (!cfgCorrections.cfgFillAcceptanceWeights && (doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameGenMC)) { + auto generatedProfiles = std::make_unique(); + addConfigObjectsToObjArray(generatedProfiles.get(), mCorrConfigs, cfgUserAxis == NMPIAxis && eventClassifierPercentileAxisEnabled); + fFCGen.setObject(new FlowContainer("FlowContainer_gen")); + fFCGen->SetXAxis(fPtAxis.get()); + fFCGen->Initialize(generatedProfiles.get(), axisMultiplicity, FlowNBootstrap); + + fFCptGen.setObject(new FlowPtContainer("FlowPtContainer_gen")); + fFCptGen->setEventWeight(cfgEventWeight.cfgUsePtCorrWeights ? eventweight::TupleWeight : eventweight::UnityWeight); + fFCptGen->setUseCentralMoments(true); + fFCptGen->setUseGapMethod(true); + fFCptGen->initialise(axisMultiplicity, MaxPtCorrelationOrder, cfgCorrConfig.value, FlowNBootstrap); + fFCptGen->initialiseSubevent(axisMultiplicity, MaxPtCorrelationOrder, nPtPtSubevents, FlowNBootstrap); + } + } + + enum DataType { + Reco, + Gen + }; + enum GFWRegionMask { + ReferenceMask = 1, + PoiMask = 2, + OverlapMask = 4 + }; + enum SeedClass { + InvalidSeedClass = -1, + LowSeedClass, + MiddleSeedClass, + HighSeedClass, + SeedClassCount + }; + + const char* getSeedClassSuffix(const SeedClass seedClass) const + { + if (cfgUserAxis.value == NMPIAxis) { + switch (seedClass) { + case LowSeedClass: + return "__nMPILow"; + case MiddleSeedClass: + return "__nMPIMiddle"; + case HighSeedClass: + return "__nMPIHigh"; + default: + return ""; + } + } + switch (seedClass) { + case LowSeedClass: + return "__seedLow"; + case MiddleSeedClass: + return "__seedMiddle"; + case HighSeedClass: + return "__seedHigh"; + default: + return ""; + } + } + + std::string getSeedClassProfileName(const std::string& baseName, const SeedClass seedClass) const + { + return baseName + getSeedClassSuffix(seedClass); + } + + void addFlowProfileDefinition(TObjArray* profiles, const std::string& name, const std::string& title, const bool addSeedClasses) + { + profiles->Add(new TNamed(name.c_str(), title.c_str())); + if (!addSeedClasses) { + return; + } + for (int seedClassIndex = LowSeedClass; seedClassIndex < SeedClassCount; ++seedClassIndex) { + const auto seedClass = static_cast(seedClassIndex); + const auto classifiedName = getSeedClassProfileName(name, seedClass); + const auto classifiedTitle = getSeedClassProfileName(title, seedClass); + profiles->Add(new TNamed(classifiedName.c_str(), classifiedTitle.c_str())); + } + } + + void addConfigObjectsToObjArray(TObjArray* profiles, const std::vector& configs, const bool addSeedClasses) + { + profiles->SetOwner(true); + for (const auto& config : configs) { + if (config.pTDif) { + const std::string suffix = "_ptDiff"; + for (auto i = 0; i < fPtAxis->GetNbins(); ++i) { + const std::string index = Form("_pt_%i", i + 1); + const std::string name = config.Head + index; + const std::string title = config.Head + suffix; + addFlowProfileDefinition(profiles, name, title, addSeedClasses); + } + } else { + addFlowProfileDefinition(profiles, config.Head, config.Head, addSeedClasses); + } + } + } + + template + void fillOutputContainers(const float& centMult, const double& randomNumber, const SeedClass seedClass = InvalidSeedClass) + { + auto& flowContainer = (dt == Gen) ? fFCGen : fFC; + auto& flowPtContainer = (dt == Gen) ? fFCptGen : fFCpt; + const auto fillFlowProfile = [&](const std::string& profileName, const double value, const double weight) { + flowContainer->FillProfile(profileName.c_str(), centMult, value, weight, randomNumber); + if (seedClass != InvalidSeedClass) { + const auto classifiedName = getSeedClassProfileName(profileName, seedClass); + flowContainer->FillProfile(classifiedName.c_str(), centMult, value, weight, randomNumber); + } + }; + + flowPtContainer->calculateCorrelations(); + flowPtContainer->calculateSubeventCorrelations(); + flowPtContainer->fillPtProfiles(centMult, randomNumber); + flowPtContainer->fillSubeventPtProfiles(centMult, randomNumber); + flowPtContainer->fillCMProfiles(centMult, randomNumber); + flowPtContainer->fillCMSubeventProfiles(centMult, randomNumber); + + for (std::size_t configIndex = 0; configIndex < mCorrConfigs.size(); ++configIndex) { + if (!mCorrConfigs.at(configIndex).pTDif) { + const auto dnx = fGFW->Calculate(mCorrConfigs.at(configIndex), 0, true).real(); + if (dnx == 0) { + continue; + } + const auto val = fGFW->Calculate(mCorrConfigs.at(configIndex), 0, false).real() / dnx; + if (std::abs(val) < 1) { + fillFlowProfile(mCorrConfigs.at(configIndex).Head, val, cfgEventWeight.cfgUseMultiplicityFlowWeights ? dnx : 1.0); + flowPtContainer->fillVnPtProfiles(centMult, val, dnx, randomNumber, cfgCorrConfig->GetpTCorrMasks()[configIndex]); + } + continue; + } + for (int i = 1; i <= fPtAxis->GetNbins(); i++) { + const auto dnx = fGFW->Calculate(mCorrConfigs.at(configIndex), i - 1, true).real(); + if (dnx == 0) { + continue; + } + const auto val = fGFW->Calculate(mCorrConfigs.at(configIndex), i - 1, false).real() / dnx; + if (std::abs(val) < 1) { + const std::string profileName = Form("%s_pt_%i", mCorrConfigs.at(configIndex).Head.c_str(), i); + fillFlowProfile(profileName, val, cfgEventWeight.cfgUseMultiplicityFlowWeights ? dnx : 1.0); + } + } + } + } + + template + inline void fillPtSums(const TTrack& track, const float& centMult, const double& posZ) + { + auto& flowPtContainer = (dt == Gen) ? fFCptGen : fFCpt; + double weff = 1.; + if constexpr (dt == Reco) { + if (cfg.mEfficiencyTrigger) { + weff = getEfficiencyCorrection(cfg.mEfficiencyTrigger, track.eta(), track.pt(), centMult, posZ); + } + } + if (weff < 0) { + return; + } + + // Fill the nominal sums + if (std::abs(track.eta()) < cfgGeneral.cfgCutEta) { + flowPtContainer->fill(weff, track.pt()); + } + + // Fill the subevent sums + std::size_t index = 0; + for (uint32_t i = 0; i < cfgPtPtGaps->rows(); ++i) { + const auto etamin = cfgPtPtGaps->get(i, uint32_t{0}); + const auto etamax = cfgPtPtGaps->get(i, uint32_t{1}); + if (etamin < -1. || etamax < -1.) { + continue; + } + if (etamin < track.eta() && track.eta() < etamax) { + flowPtContainer->fillSub(weff, track.pt(), index); + } + ++index; + } + } + + template + double getAcceptance(const TTrack& track, const double& posZ) + { + if (!cfg.mAcceptance) { + return 1.; + } + return cfg.mAcceptance->getNUA(track.phi(), track.eta(), posZ); + } + + std::shared_ptr getAcceptanceWeightsForRun(const int runNumber) + { + const auto existing = mAcceptanceWeightsByRun.find(runNumber); + if (existing != mAcceptanceWeightsByRun.end()) { + return existing->second; + } + + AxisSpec phiAxis{cfgCorrections.cfgAcceptancePhiBins, 0., o2::constants::math::TwoPI, "#varphi"}; + AxisSpec etaAxis{cfgCorrections.cfgAcceptanceEtaBins, -cfgGeneral.cfgCutEta.value, cfgGeneral.cfgCutEta.value, "#eta"}; + const std::string histogramName = Form("%d/phi_eta_vtxz_ref", runNumber); + const auto histogram = registry.add(histogramName.c_str(), "Reference-track acceptance;#varphi;#eta;z_{vtx} (cm)", {HistType::kTH3D, {phiAxis, etaAxis, axisVertex}}); + mAcceptanceWeightsByRun.emplace(runNumber, histogram); + return histogram; + } + + template + void fillAcceptanceWeights(const TTrack& track, const double& posZ, const int runNumber) + { + if (track.pt() <= fPtAxis->GetXmin() || track.pt() >= fPtAxis->GetXmax()) { + return; + } + const auto histogram = cfgCorrections.cfgAcceptanceRunByRun ? getAcceptanceWeightsForRun(runNumber) : mAcceptanceWeights; + histogram->Fill(track.phi(), track.eta(), posZ); + } + + template + inline void fillGFW(const TTrack& track, const float& centMult, const double& posZ) + { + const bool withinPtRef = track.pt() > cfgGeneral.cfgFlowPtRef->first && track.pt() < cfgGeneral.cfgFlowPtRef->second; + const bool withinPtPOI = track.pt() > cfgGeneral.cfgFlowPtPOI->first && track.pt() < cfgGeneral.cfgFlowPtPOI->second; + if (!withinPtRef && !withinPtPOI) { + return; + } + double wacc = 1.; + double weff = 1.; + if constexpr (dt == Reco) { + wacc = getAcceptance(track, posZ); + if (cfg.mEfficiencyAssociated) { + weff = getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), centMult, posZ); + } + } + if (weff < 0 || !std::isfinite(wacc) || wacc <= 0.) { + return; + } + const double weight = weff * wacc; + const int ptBin = fPtAxis->FindBin(track.pt()) - 1; + if (withinPtRef) { + fGFW->Fill(track.eta(), ptBin, track.phi(), weight, ReferenceMask); + } + if (withinPtPOI) { + fGFW->Fill(track.eta(), ptBin, track.phi(), weight, PoiMask); + } + if (withinPtRef && withinPtPOI) { + fGFW->Fill(track.eta(), ptBin, track.phi(), weight, OverlapMask); + } + } + + int getMagneticField(uint64_t timestamp) + { + // TODO done only once (and not per run). Will be replaced by CCDBConfigurable + static o2::parameters::GRPObject* grpo = nullptr; + // static o2::parameters::GRPMagField* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/GRP/GRP", timestamp); + // grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + } + return grpo->getNominalL3Field(); + } + + template + using HasMultSet = decltype(std::declval().multiplicities()); + + template + using HasCorrectedMultiplicity = decltype(std::declval().multiplicityCorrected()); + + template + static float getAnalysisMultiplicity(const TCollision& collision) + { + if constexpr (std::experimental::is_detected::value) { + return collision.multiplicityCorrected(); + } + return collision.multiplicity(); + } + + template + void fillQA(const TCollision& collision, float multiplicity, const TTracks& tracks) + { + registry.fill(HIST("multiplicity"), multiplicity); + if constexpr (std::experimental::is_detected::value) { + if (std::popcount(cfgMultCorrelationsMask.value) != static_cast(collision.multiplicities().size())) { + LOGF(fatal, "Multiplicity selections (cfgMultCorrelationsMask = 0x%x) do not match the size of the table column (%ld). The histogram filling relies on the preservation of order.", cfgMultCorrelationsMask.value, collision.multiplicities().size()); + } + // need to convert to vec of doubles since THnSparse has no way to fill vec of floats directly + std::vector v(collision.multiplicities().begin(), collision.multiplicities().end()); + registry.get(HIST("multCorrelations")).get()->Fill(v.data()); + } + for (const auto& track1 : tracks) { + registry.fill(HIST("yields"), multiplicity, track1.pt(), track1.eta()); + registry.fill(HIST("etaphi"), multiplicity, track1.eta(), track1.phi()); + } + } + + template + using HasInvMass = decltype(std::declval().invMass()); + template + using HasPDGCode = decltype(std::declval().pdgCode()); + + template + void fillQA(const TCollision& collision, float multiplicity, float posZ, const TTracks1& tracks1, const TTracks2& tracks2) + { + for (const auto& track1 : tracks1) { + if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { + if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track1.decay()))) == 0u) { + continue; + } + registry.fill(HIST("invMass"), track1.invMass(), track1.pt(), multiplicity, posZ); + } + if constexpr (std::experimental::is_detected::value) { + if (!cfgMcTriggerPDGs->empty() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track1.pdgCode()) == cfgMcTriggerPDGs->end()) { + continue; + } + } + registry.fill(HIST("yieldsTrigger"), multiplicity, track1.pt(), track1.eta()); + registry.fill(HIST("etaphiTrigger"), multiplicity, track1.eta(), track1.phi()); + } + fillQA(collision, multiplicity, tracks2); + } + + template + bool fillContainerEvent(TTarget target, float multiplicity, CorrelationContainer::CFStep step) + { + if (multiplicity < 0.f) { + return false; + } + target->fillEvent(multiplicity, step); + return true; + } + + template + bool fillCollisionAOD(const TTarget& target, const TCollision& collision, const float& multiplicity) + { + if (!fillContainerEvent(target, multiplicity, CorrelationContainer::kCFStepAll)) { + return false; + } + + if (!collision.alias_bit(kINT7) || !collision.sel7()) { + return false; + } + + fillContainerEvent(target, multiplicity, CorrelationContainer::kCFStepReconstructed); + + return true; + } + + template + bool checkObject(TTrack& track) + { + if constexpr (step <= CorrelationContainer::kCFStepAnaTopology) { + // If using MC trigger PDGs, allow ONLY those PDGs to bypass isPhysicalPrimary + if (!cfgMcTriggerPDGs->empty()) { + // track has pdgCode in this compilation branch (you only call checkObject where that is true) + const bool isWantedTrigger = + std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track.pdgCode()) != cfgMcTriggerPDGs->end(); + if (isWantedTrigger) { + return true; // allow phi, K*, etc. even if not physical primary + } + // For everything else keep original definition + return track.isPhysicalPrimary(); + } + return track.isPhysicalPrimary(); + } else if constexpr (step == CorrelationContainer::kCFStepTrackedOnlyPrim) { + return track.isPhysicalPrimary() && (track.flags() & aod::cfmcparticle::kReconstructed); + } else if constexpr (step == CorrelationContainer::kCFStepTracked) { + return (track.flags() & aod::cfmcparticle::kReconstructed); + } + + return true; + } + + template + using HasSign = decltype(std::declval().sign()); + template + using HasDecay = decltype(std::declval().decay()); + template + using HasPartDaugh0Id = decltype(std::declval().cfParticleDaugh0Id()); + template + using HasPartDaugh1Id = decltype(std::declval().cfParticleDaugh1Id()); + + template + int getParticleSign(const TParticle& particle) + { + if constexpr (std::experimental::is_detected::value) { + return particle.sign(); + } else if constexpr (std::experimental::is_detected::value) { + const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle) { + return (pdgParticle->Charge() > 0.0) - (pdgParticle->Charge() < 0.0); + } + } + return 0; + } + + template + bool passOutlier(CollType const& collision) + { + if (cfgMultCutFormula.value.empty()) { + return true; + } + for (uint i = 0; i < aod::cfmultset::NMultiplicityEstimators; ++i) { + if ((cfgMultCorrelationsMask.value & (1u << i)) == 0 || multCutFormulaParamIndex[i] == ~0u) { + continue; + } + auto estIndex = std::popcount(cfgMultCorrelationsMask.value & ((1u << i) - 1)); + multCutFormula->SetParameter(multCutFormulaParamIndex[i], collision.multiplicities()[estIndex]); + } + return multCutFormula->Eval() > 0.0f; + } + + template + std::tuple getV0Rapidity(const T& track) + { + if constexpr (!std::experimental::is_detected::value) { + return {false, 0.0f}; // no decay type, return dummy rapidity + } + const auto decayType = track.decay(); + float mass = 0.f; + + if (decayType == aod::cf2prongtrack::K0stoPiPi) { + mass = o2::constants::physics::MassK0Short; + } else if (decayType == aod::cf2prongtrack::LambdatoPPi || decayType == aod::cf2prongtrack::AntiLambdatoPiP) { + mass = o2::constants::physics::MassLambda; + } else if (decayType == aod::cf2prongtrack::PhiToKKPID1 || decayType == aod::cf2prongtrack::PhiToKKPID2 || decayType == aod::cf2prongtrack::PhiToKKPID3) { + mass = o2::constants::physics::MassPhi; + } else { + return {false, 0.0f}; // unsupported decay type, return dummy rapidity + } + + const float pt = track.pt(); + const float eta = track.eta(); + const float phi = track.phi(); + + const float px = pt * std::cos(phi); + const float py = pt * std::sin(phi); + const float pz = pt * std::sinh(eta); + + const float p2 = px * px + py * py + pz * pz; + + const float E = std::sqrt(p2 + mass * mass); // o2-linter: disable=name/function-variable (E is standard variable name for energy) + return {true, 0.5f * std::log((E + pz) / (E - pz))}; + } + + void loadYieldTemplatesFromTree(TTree* tree, const TNamed* schemaVersion, const TNamed* correlationStep, const std::string& source) + { + if (!tree) { + LOGF(fatal, "Missing ensembleYieldTemplates in %s", source.c_str()); + return; + } + if (schemaVersion == nullptr || TString(schemaVersion->GetTitle()) != "1") { + LOGF(fatal, "Unsupported or missing ensemble-yield template schema version in %s", source.c_str()); + return; + } + if (correlationStep == nullptr || TString(correlationStep->GetTitle()) != "kCFStepReconstructed") { + LOGF(fatal, "Ensemble-yield templates must be derived at kCFStepReconstructed"); + return; + } + + YieldTemplate value; + int fitStatus = -1; + std::array parameters{}; + tree->SetBranchAddress("trigBin", &value.trigBin); + tree->SetBranchAddress("assocBin", &value.assocBin); + tree->SetBranchAddress("multBin", &value.multBin); + tree->SetBranchAddress("nchLow", &value.nchLow); + tree->SetBranchAddress("nchHigh", &value.nchHigh); + tree->SetBranchAddress("trigPtLow", &value.trigPtLow); + tree->SetBranchAddress("trigPtHigh", &value.trigPtHigh); + tree->SetBranchAddress("assocPtLow", &value.assocPtLow); + tree->SetBranchAddress("assocPtHigh", &value.assocPtHigh); + tree->SetBranchAddress("parameters", parameters.data()); + tree->SetBranchAddress("fitStatus", &fitStatus); + + yieldTemplates.clear(); + yieldTemplates.reserve(tree->GetEntries()); + for (int64_t iEntry = 0; iEntry < tree->GetEntries(); ++iEntry) { + tree->GetEntry(iEntry); + if (fitStatus != 0) { + LOGF(warning, "Skipping failed yield template (%d, %d, %d), fit status %d", value.trigBin, value.assocBin, value.multBin, fitStatus); + continue; + } + value.parameters = parameters; + if (value.parameters[2] <= 0.0 || value.parameters[5] <= 0.0 || value.parameters[8] <= 0.0) { + LOGF(warning, "Skipping yield template (%d, %d, %d) with non-positive Gaussian width", value.trigBin, value.assocBin, value.multBin); + continue; + } + const auto intervalMatchesAxis = [](const AxisSpec& axis, double low, double high) { + constexpr double Tolerance = 1e-6; + const auto& edges = axis.binEdges; + for (std::size_t index = 0; index + 1 < edges.size(); ++index) { + if (std::abs(edges[index] - low) < Tolerance && std::abs(edges[index + 1] - high) < Tolerance) { + return true; + } + } + return false; + }; + if (!intervalMatchesAxis(AxisSpec(axisMultiplicity), value.nchLow, value.nchHigh) || + !intervalMatchesAxis(AxisSpec(axisPtTrigger), value.trigPtLow, value.trigPtHigh) || + !intervalMatchesAxis(AxisSpec(axisPtAssoc), value.assocPtLow, value.assocPtHigh)) { + LOGF(fatal, "Template binning does not match the configured axes for template (%d, %d, %d)", value.trigBin, value.assocBin, value.multBin); + tree->ResetBranchAddresses(); + return; + } + yieldTemplates.push_back(value); + } + tree->ResetBranchAddresses(); + + if (yieldTemplates.empty()) { + LOGF(fatal, "No valid ensemble-yield templates were loaded from %s", source.c_str()); + return; + } + LOGF(info, "Loaded %zu ensemble-yield templates from %s", yieldTemplates.size(), source.c_str()); + } + + void loadPairAcceptanceMaps(const std::function& findObject, const std::string& source) + { + const auto* schemaVersion = dynamic_cast(findObject("pairAcceptanceSchemaVersion")); + const auto* normalization = dynamic_cast(findObject("pairAcceptanceNormalization")); + const auto* axes = dynamic_cast(findObject("pairAcceptanceAxes")); + const TString schema = schemaVersion != nullptr ? schemaVersion->GetTitle() : ""; + if (schemaVersion == nullptr || (schema != "2" && schema != "3") || normalization == nullptr || axes == nullptr) { + LOGF(fatal, "Unsupported or missing pair-acceptance metadata in %s", source.c_str()); + return; + } + + const int nMultiplicityBins = AxisSpec(axisMultiplicity).getNbins(); + pairAcceptanceMaps.clear(); + pairAcceptanceEtaVertexMaps.clear(); + pairAcceptanceSchemaVersion = schema.Atoi(); + int multiplicityDependentOnly = 2; + if (pairAcceptanceSchemaVersion == multiplicityDependentOnly) { + pairAcceptanceMaps.resize(nMultiplicityBins); + } else { + pairAcceptanceEtaVertexMaps.resize(nMultiplicityBins); + } + for (int multBin = 0; multBin < nMultiplicityBins; ++multBin) { + if (pairAcceptanceSchemaVersion == multiplicityDependentOnly) { + auto* inputMap = dynamic_cast(findObject(Form("pairAcceptance_mult_%d", multBin))); + auto* clone = inputMap != nullptr ? dynamic_cast(inputMap->Clone(Form("loadedPairAcceptance_mult_%d", multBin))) : nullptr; + if (clone == nullptr) { + LOGF(fatal, "Missing or invalid pairAcceptance_mult_%d in %s", multBin, source.c_str()); + pairAcceptanceMaps.clear(); + return; + } + clone->SetDirectory(nullptr); + pairAcceptanceMaps[multBin].reset(clone); + } else { + auto* inputMap = dynamic_cast(findObject(Form("pairAcceptanceEtaVertex_mult_%d", multBin))); + auto* clone = inputMap != nullptr ? dynamic_cast(inputMap->Clone(Form("loadedPairAcceptanceEtaVertex_mult_%d", multBin))) : nullptr; + if (clone == nullptr) { + LOGF(fatal, "Missing or invalid pairAcceptanceEtaVertex_mult_%d in %s", multBin, source.c_str()); + pairAcceptanceEtaVertexMaps.clear(); + return; + } + clone->SetDirectory(nullptr); + pairAcceptanceEtaVertexMaps[multBin].reset(clone); + } + } + LOGF(info, "Loaded %d schema-%d pair-acceptance maps from %s", nMultiplicityBins, pairAcceptanceSchemaVersion, source.c_str()); + } + + void loadLocalYieldTemplates() + { + if (cfgNuncSeedsTemplateFile.value.empty()) { + return; + } + + std::unique_ptr input(TFile::Open(cfgNuncSeedsTemplateFile.value.c_str(), "READ")); + if (!input || input->IsZombie()) { + LOGF(fatal, "Could not open ensemble-yield template file: %s", cfgNuncSeedsTemplateFile.value.c_str()); + return; + } + + auto* calibration = dynamic_cast(input->Get("ccdb_object")); + auto findObject = [&](const char* name) -> TObject* { + if (auto* object = input->Get(name)) { + return object; + } + return calibration ? calibration->FindObject(name) : nullptr; + }; + loadYieldTemplatesFromTree( + dynamic_cast(findObject("ensembleYieldTemplates")), + dynamic_cast(findObject("ensembleYieldTemplateSchemaVersion")), + dynamic_cast(findObject("ensembleYieldTemplateCorrelationStep")), + cfgNuncSeedsTemplateFile.value); + loadPairAcceptanceMaps(findObject, cfgNuncSeedsTemplateFile.value); + } + + void loadCcdbYieldTemplates(uint64_t timestamp) + { + if (cfgNuncSeedsTemplate.value.empty()) { + return; + } + + auto* calibration = ccdb->getForTimeStamp(cfgNuncSeedsTemplate.value, timestamp); + if (!calibration) { + LOGF(fatal, "Could not load ensemble-yield templates from CCDB path %s at timestamp %llu", cfgNuncSeedsTemplate.value.c_str(), timestamp); + return; + } + if (calibration == loadedCcdbYieldTemplateObject) { + return; + } + + loadYieldTemplatesFromTree( + dynamic_cast(calibration->FindObject("ensembleYieldTemplates")), + dynamic_cast(calibration->FindObject("ensembleYieldTemplateSchemaVersion")), + dynamic_cast(calibration->FindObject("ensembleYieldTemplateCorrelationStep")), + cfgNuncSeedsTemplate.value); + loadPairAcceptanceMaps( + [calibration](const char* name) -> TObject* { return calibration->FindObject(name); }, + cfgNuncSeedsTemplate.value); + loadedCcdbYieldTemplateObject = calibration; + } + + const YieldTemplate* findYieldTemplate(double multiplicity, double trigPt, double assocPt) const + { + for (const auto& yieldTemplate : yieldTemplates) { + if (multiplicity >= yieldTemplate.nchLow && multiplicity < yieldTemplate.nchHigh && + trigPt >= yieldTemplate.trigPtLow && trigPt < yieldTemplate.trigPtHigh && + assocPt >= yieldTemplate.assocPtLow && assocPt < yieldTemplate.assocPtHigh) { + return &yieldTemplate; + } + } + return nullptr; + } + + bool hasTriggerTemplate(double multiplicity, double trigPt) const + { + return std::any_of(yieldTemplates.begin(), yieldTemplates.end(), [multiplicity, trigPt](const auto& yieldTemplate) { + return multiplicity >= yieldTemplate.nchLow && multiplicity < yieldTemplate.nchHigh && + trigPt >= yieldTemplate.trigPtLow && trigPt < yieldTemplate.trigPtHigh; + }); + } + + bool hasMultiplicityTemplate(double multiplicity) const + { + return std::any_of(yieldTemplates.begin(), yieldTemplates.end(), [multiplicity](const auto& yieldTemplate) { + return multiplicity >= yieldTemplate.nchLow && multiplicity < yieldTemplate.nchHigh; + }); + } + + static double evaluateGaussian(double x, const double* parameters) + { + const double pull = (x - parameters[1]) / parameters[2]; + return parameters[0] * std::exp(-0.5 * pull * pull); + } + + static std::array getTemplateComponentIntegrals(const YieldTemplate& yieldTemplate) + { + const auto& p = yieldTemplate.parameters; + const double gaussianIntegralFactor = std::sqrt(o2::constants::math::TwoPI); + return { + std::max(0.0, gaussianIntegralFactor * (p[0] * p[2] + p[3] * p[5])), + std::max(0.0, gaussianIntegralFactor * p[6] * p[8]), + std::max(0.0, o2::constants::math::TwoPI * p[9])}; + } + + void addTriggerPriorExpectations(EventSeedEstimate& estimate, double multiplicity, double trigPt) + { + if (estimate.templatePriorCounts.empty()) { + estimate.templatePriorCounts.resize(yieldTemplates.size()); + } + for (std::size_t templateIndex = 0; templateIndex < yieldTemplates.size(); ++templateIndex) { + const auto& yieldTemplate = yieldTemplates[templateIndex]; + if (multiplicity < yieldTemplate.nchLow || multiplicity >= yieldTemplate.nchHigh || + trigPt < yieldTemplate.trigPtLow || trigPt >= yieldTemplate.trigPtHigh) { + continue; + } + const auto componentIntegrals = getTemplateComponentIntegrals(yieldTemplate); + for (std::size_t component = 0; component < componentIntegrals.size(); ++component) { + // The fit integrals are per-trigger yields. Summing one copy for every + // templated trigger makes the Gamma-prior mode follow the event's actual + // trigger-pT composition without using its observed pair assignments. + estimate.templatePriorCounts[templateIndex][component] += componentIntegrals[component]; + estimate.priorComponentCounts[component] += componentIntegrals[component]; + } + } + } + + int findPairAcceptanceMultiplicityBin(double multiplicity) const + { + const auto& edges = AxisSpec(axisMultiplicity).binEdges; + const auto upper = std::upper_bound(edges.begin(), edges.end(), multiplicity); + return static_cast(std::distance(edges.begin(), upper)) - 1; + } + + double getEventSeedUserAxisValue(double multiplicity, double eventSeed) const + { + if (!eventClassifierPercentileAxisEnabled || eventSeed < 0.0) { + return eventSeed; + } + const int multBin = findPairAcceptanceMultiplicityBin(multiplicity); + if (multBin < 0 || multBin >= static_cast(cfgEventClassifierPercentileLower->size())) { + return -1.0; + } + if (eventSeed < cfgEventClassifierPercentileLower->at(multBin)) { + return 0.0; + } + if (eventSeed >= cfgEventClassifierPercentileUpper->at(multBin)) { + return 2.0; + } + return 1.0; + } + + double getTrueNMPIUserAxisValue(double multiplicity, double nMPI) const + { + if (!eventClassifierPercentileAxisEnabled || nMPI < 0.0) { + return nMPI; + } + const int multBin = findPairAcceptanceMultiplicityBin(multiplicity); + if (multBin < 0 || multBin >= static_cast(cfgEventClassifierPercentileLower->size())) { + return -1.0; + } + if (nMPI < cfgEventClassifierPercentileLower->at(multBin)) { + return 0.0; + } + if (nMPI >= cfgEventClassifierPercentileUpper->at(multBin)) { + return 2.0; + } + return 1.0; + } + + SeedClass getEventSeedClass(const double multiplicity, const double eventSeed) const + { + if (!eventClassifierPercentileAxisEnabled) { + return InvalidSeedClass; + } + const double userAxisValue = getEventSeedUserAxisValue(multiplicity, eventSeed); + if (!std::isfinite(userAxisValue) || + userAxisValue < static_cast(LowSeedClass) || + userAxisValue >= static_cast(SeedClassCount)) { + return InvalidSeedClass; + } + return static_cast(userAxisValue); + } + + SeedClass getTrueNMPIClass(const double multiplicity, const double nMPI) const + { + if (!eventClassifierPercentileAxisEnabled) { + return InvalidSeedClass; + } + const double userAxisValue = getTrueNMPIUserAxisValue(multiplicity, nMPI); + if (!std::isfinite(userAxisValue) || + userAxisValue < static_cast(LowSeedClass) || + userAxisValue >= static_cast(SeedClassCount)) { + return InvalidSeedClass; + } + return static_cast(userAxisValue); + } + + double getPairAcceptance(double multiplicity, double deltaPhi, double deltaEta, double posZ) const + { + const int multBin = findPairAcceptanceMultiplicityBin(multiplicity); + int etaVertexMultiplicityDependentOnly = 3; + if (pairAcceptanceSchemaVersion == etaVertexMultiplicityDependentOnly) { + if (multBin < 0 || multBin >= static_cast(pairAcceptanceEtaVertexMaps.size()) || pairAcceptanceEtaVertexMaps[multBin] == nullptr) { + return 0.0; + } + const auto* map = pairAcceptanceEtaVertexMaps[multBin].get(); + const int etaBin = map->GetXaxis()->FindFixBin(deltaEta); + const int vertexBin = map->GetYaxis()->FindFixBin(posZ); + if (etaBin < 1 || etaBin > map->GetNbinsX() || vertexBin < 1 || vertexBin > map->GetNbinsY()) { + return 0.0; + } + return map->GetBinContent(etaBin, vertexBin); + } + if (multBin < 0 || multBin >= static_cast(pairAcceptanceMaps.size()) || pairAcceptanceMaps[multBin] == nullptr) { + return 0.0; + } + const auto* map = pairAcceptanceMaps[multBin].get(); + const int phiBin = map->GetXaxis()->FindFixBin(deltaPhi); + const int etaBin = map->GetYaxis()->FindFixBin(deltaEta); + const int vertexBin = map->GetZaxis()->FindFixBin(posZ); + if (phiBin < 1 || phiBin > map->GetNbinsX() || etaBin < 1 || etaBin > map->GetNbinsY() || + vertexBin < 1 || vertexBin > map->GetNbinsZ()) { + return 0.0; + } + return map->GetBinContent(phiBin, etaBin, vertexBin); + } + + void addPairProbabilities(EventSeedEstimate& estimate, const YieldTemplate& yieldTemplate, + double multiplicity, double deltaPhi, double deltaEta, double posZ, bool fillAcceptanceQA = true) + { + const auto& parameters = yieldTemplate.parameters; + const double near = std::max(0.0, evaluateGaussian(deltaPhi, parameters.data()) + evaluateGaussian(deltaPhi, parameters.data() + 3)); + const double away = std::max(0.0, evaluateGaussian(deltaPhi, parameters.data() + 6)); + const double baseline = std::max(0.0, parameters[9]); + const double total = baseline + near + away; + if (total <= 0.0) { + return; + } + const double nearProbability = near / total; + const double awayProbability = away / total; + const double baselineProbability = baseline / total; + estimate.rawNearPairs += nearProbability; + estimate.rawAwayPairs += awayProbability; + estimate.rawBaselinePairs += baselineProbability; + ++estimate.nPairs; + + const double acceptance = getPairAcceptance(multiplicity, deltaPhi, deltaEta, posZ); + if (!std::isfinite(acceptance) || acceptance < cfgMinPairAcceptance || acceptance <= 0.0) { + ++estimate.nPairsRejectedByAcceptance; + return; + } + const double acceptanceWeight = 1.0 / acceptance; + if (!std::isfinite(acceptanceWeight) || acceptanceWeight > cfgMaxPairAcceptanceWeight) { + ++estimate.nPairsRejectedByAcceptance; + return; + } + estimate.probabilityNearPairs += acceptanceWeight * nearProbability; + estimate.probabilityAwayPairs += acceptanceWeight * awayProbability; + estimate.probabilityBaselinePairs += acceptanceWeight * baselineProbability; + // Method 0 uses these values directly. Method 1 overwrites them with the + // event-wide MAP-EM component yields after all pairs have been collected. + estimate.nearPairs = estimate.probabilityNearPairs; + estimate.awayPairs = estimate.probabilityAwayPairs; + estimate.baselinePairs = estimate.probabilityBaselinePairs; + + if (cfgEventSeedEstimatorMethod == 1) { + const auto componentIntegrals = getTemplateComponentIntegrals(yieldTemplate); + EventSeedPairObservation observation; + observation.templateIndex = static_cast(&yieldTemplate - yieldTemplates.data()); + observation.normalizedShapes = { + componentIntegrals[0] > 0.0 ? near / componentIntegrals[0] : 0.0, + componentIntegrals[1] > 0.0 ? away / componentIntegrals[1] : 0.0, + componentIntegrals[2] > 0.0 ? baseline / componentIntegrals[2] : 0.0}; + observation.acceptanceWeight = acceptanceWeight; + estimate.pairObservations.push_back(observation); + } + estimate.sumAcceptanceWeights += acceptanceWeight; + ++estimate.nAcceptanceCorrectedPairs; + if (fillAcceptanceQA) { + registry.fill(HIST("eventSeedAcceptanceWeight"), acceptanceWeight); + } + } + + void finalizeEventSeedEstimate(EventSeedEstimate& estimate) + { + if (cfgEventSeedEstimatorMethod == 0 || !estimate.hasAcceptanceCorrectedPairs()) { + return; + } + + estimate.usedMapEM = true; + auto componentYields = estimate.priorComponentCounts; + const double priorExposure = cfgEventSeedPriorExposure; + constexpr double Tiny = 1.e-15; + bool hasPositivePrior = false; + for (const auto& value : componentYields) { + hasPositivePrior = hasPositivePrior || value > 0.0; + } + if (!hasPositivePrior) { + return; + } + + for (int iteration = 0; iteration < cfgEventSeedEMMaxIterations; ++iteration) { + std::array weightedComponentCounts{}; + + for (const auto& observation : estimate.pairObservations) { + if (observation.templateIndex >= estimate.templatePriorCounts.size()) { + continue; + } + const auto& templatePrior = estimate.templatePriorCounts[observation.templateIndex]; + std::array numerators{}; + double denominator = 0.0; + for (std::size_t component = 0; component < numerators.size(); ++component) { + if (estimate.priorComponentCounts[component] <= 0.0) { + continue; + } + // templatePrior/priorComponentCounts is the ensemble-predicted pT-bin + // fraction for this component. At the prior mode, the responsibility + // therefore reduces to the existing fixed-template probability. + const double templateFraction = templatePrior[component] / estimate.priorComponentCounts[component]; + numerators[component] = componentYields[component] * templateFraction * observation.normalizedShapes[component]; + denominator += numerators[component]; + } + if (!std::isfinite(denominator) || denominator <= 0.0) { + continue; + } + for (std::size_t component = 0; component < numerators.size(); ++component) { + weightedComponentCounts[component] += observation.acceptanceWeight * numerators[component] / denominator; + } + } + + std::array updatedYields{}; + double maximumRelativeChange = 0.0; + for (std::size_t component = 0; component < updatedYields.size(); ++component) { + // Gamma(shape=1+s*m, rate=s) has mode m. Combining it with one + // event exposure gives the closed MAP update below. With s=0 this is + // the unregularized weighted EM update. + updatedYields[component] = + (priorExposure * estimate.priorComponentCounts[component] + weightedComponentCounts[component]) / + (priorExposure + 1.0); + const double scale = std::max({std::abs(componentYields[component]), std::abs(updatedYields[component]), Tiny}); + maximumRelativeChange = std::max(maximumRelativeChange, std::abs(updatedYields[component] - componentYields[component]) / scale); + } + componentYields = updatedYields; + estimate.emIterations = iteration + 1; + if (maximumRelativeChange < cfgEventSeedEMTolerance) { + estimate.emConverged = true; + break; + } + } + + estimate.nearPairs = componentYields[0]; + estimate.awayPairs = componentYields[1]; + estimate.baselinePairs = componentYields[2]; + } + + void fillEventSeedEstimatorQA(double multiplicity, const EventSeedEstimate& estimate) + { + if (yieldTemplates.empty()) { + return; + } + if (!hasMultiplicityTemplate(multiplicity)) { + return; + } + registry.fill(HIST("profileEventNTriggers"), multiplicity, estimate.nTriggers); + registry.fill(HIST("profileEventNearPairs"), multiplicity, estimate.nearPairs); + registry.fill(HIST("profileEventAwayPairs"), multiplicity, estimate.awayPairs); + registry.fill(HIST("profileEventBaselinePairs"), multiplicity, estimate.baselinePairs); + registry.fill(HIST("profileEventRawNearPairs"), multiplicity, estimate.rawNearPairs); + registry.fill(HIST("profileEventRawAwayPairs"), multiplicity, estimate.rawAwayPairs); + registry.fill(HIST("profileEventRawBaselinePairs"), multiplicity, estimate.rawBaselinePairs); + const double acceptanceCoverage = estimate.nPairs > 0 ? static_cast(estimate.nAcceptanceCorrectedPairs) / estimate.nPairs : 0.0; + const double meanAcceptanceWeight = estimate.nAcceptanceCorrectedPairs > 0 ? estimate.sumAcceptanceWeights / estimate.nAcceptanceCorrectedPairs : 0.0; + registry.fill(HIST("profileEventAcceptanceCoverage"), multiplicity, acceptanceCoverage); + registry.fill(HIST("profileEventMeanAcceptanceWeight"), multiplicity, meanAcceptanceWeight); + registry.fill(HIST("profileEventEstimatorValidity"), multiplicity, estimate.isValid() ? 1.0 : 0.0); + if (!estimate.hasTriggers()) { + registry.fill(HIST("eventSeedEstimatorStatus"), 1.0); + return; + } + const double coverage = estimate.nCandidatePairs > 0 ? static_cast(estimate.nPairs) / estimate.nCandidatePairs : 0.0; + registry.fill(HIST("eventSeedTemplateCoverage"), multiplicity, coverage); + if (estimate.nPairs == 0) { + registry.fill(HIST("eventSeedEstimatorStatus"), 2.0); + return; + } + if (!estimate.hasAcceptanceCorrectedPairs()) { + registry.fill(HIST("eventSeedEstimatorStatus"), 3.0); + return; + } + registry.fill(HIST("eventSeedEstimatorStatus"), 4.0); + registry.fill(HIST("eventSeedEstimator"), multiplicity, estimate.nTriggers, estimate.nearYield(), estimate.awayYield(), estimate.nuncSeeds()); + registry.fill(HIST("eventSeedPairProbabilities"), estimate.probabilityBaselinePairs, estimate.probabilityNearPairs, estimate.probabilityAwayPairs); + registry.fill(HIST("eventSeedEstimateVsMultiplicity"), multiplicity, estimate.nuncSeeds()); + registry.fill(HIST("eventSeedFixedProbabilityVsMultiplicity"), multiplicity, estimate.probabilityNuncSeeds()); + registry.fill(HIST("eventNearYieldVsMultiplicity"), multiplicity, estimate.nearYield()); + registry.fill(HIST("eventAwayYieldVsMultiplicity"), multiplicity, estimate.awayYield()); + registry.fill(HIST("profileEventNearYield"), multiplicity, estimate.nearYield()); + registry.fill(HIST("profileEventAwayYield"), multiplicity, estimate.awayYield()); + registry.fill(HIST("profileEventNuncSeeds"), multiplicity, estimate.nuncSeeds()); + if (estimate.usedMapEM) { + registry.fill(HIST("profileEventProbabilityNuncSeeds"), multiplicity, estimate.probabilityNuncSeeds()); + registry.fill(HIST("eventSeedEMIterations"), estimate.emIterations); + registry.fill(HIST("profileEventEMConvergence"), multiplicity, estimate.emConverged ? 1.0 : 0.0); + registry.fill(HIST("profileEventEMNearPrior"), multiplicity, estimate.priorComponentCounts[0]); + registry.fill(HIST("profileEventEMAwayPrior"), multiplicity, estimate.priorComponentCounts[1]); + registry.fill(HIST("profileEventEMBaselinePrior"), multiplicity, estimate.priorComponentCounts[2]); + registry.fill(HIST("eventSeedMAPVsProbability"), estimate.probabilityNuncSeeds(), estimate.nuncSeeds()); + registry.fill(HIST("eventSeedMAPEMVsMultiplicity"), multiplicity, estimate.nuncSeeds()); + } + } + + void fillMCValidation(double multiplicity, const EventSeedEstimate& estimate, int trueNMPI) + { + if (!eventSeedEstimatorEnabled || yieldTemplates.empty()) { + return; + } + if (trueNMPI < 0) { + registry.fill(HIST("mcValidation/status"), 1.0); + return; + } + registry.fill(HIST("mcValidation/trueNMPIVsMultiplicity"), multiplicity, trueNMPI); + if (!hasMultiplicityTemplate(multiplicity)) { + registry.fill(HIST("mcValidation/status"), 2.0); + return; + } + if (!estimate.hasTriggers()) { + registry.fill(HIST("mcValidation/status"), 3.0); + return; + } + const double coverage = estimate.nCandidatePairs > 0 ? static_cast(estimate.nPairs) / estimate.nCandidatePairs : 0.0; + registry.fill(HIST("mcValidation/templateCoverageVsTrueNMPI"), trueNMPI, coverage); + if (estimate.nPairs == 0) { + registry.fill(HIST("mcValidation/status"), 4.0); + return; + } + if (!estimate.hasAcceptanceCorrectedPairs()) { + registry.fill(HIST("mcValidation/status"), 5.0); + return; + } + + const double estimatedSeeds = estimate.nuncSeeds(); + const double bias = estimatedSeeds - trueNMPI; + registry.fill(HIST("mcValidation/status"), 6.0); + registry.fill(HIST("mcValidation/estimatedSeedsVsTrueNMPI"), trueNMPI, estimatedSeeds); + registry.fill(HIST("mcValidation/estimatedSeedsVsTrueNMPIVsMultiplicity"), multiplicity, trueNMPI, estimatedSeeds); + if (estimate.usedMapEM) { + registry.fill(HIST("mcValidation/probabilityEstimatedSeedsVsTrueNMPI"), trueNMPI, estimate.probabilityNuncSeeds()); + } + registry.fill(HIST("mcValidation/profileEstimatedSeedsVsTrueNMPI"), trueNMPI, estimatedSeeds); + registry.fill(HIST("mcValidation/profileBiasVsTrueNMPI"), trueNMPI, bias); + if (trueNMPI > 0) { + registry.fill(HIST("mcValidation/relativeResidualVsTrueNMPI"), trueNMPI, bias / trueNMPI); + } + } + + void fillGeneratedMCValidation(double multiplicity, const EventSeedEstimate& estimate, int trueNMPI) + { + if (!eventSeedEstimatorEnabled || yieldTemplates.empty()) { + return; + } + if (trueNMPI < 0) { + registry.fill(HIST("mcValidation/generated/status"), 0.0); + return; + } + registry.fill(HIST("mcValidation/generated/trueNMPIVsMultiplicity"), multiplicity, trueNMPI); + if (!hasMultiplicityTemplate(multiplicity)) { + registry.fill(HIST("mcValidation/generated/status"), 1.0); + return; + } + if (!estimate.hasTriggers()) { + registry.fill(HIST("mcValidation/generated/status"), 2.0); + return; + } + const double coverage = estimate.nCandidatePairs > 0 ? static_cast(estimate.nPairs) / estimate.nCandidatePairs : 0.0; + registry.fill(HIST("mcValidation/generated/templateCoverageVsTrueNMPI"), trueNMPI, coverage); + if (estimate.nPairs == 0) { + registry.fill(HIST("mcValidation/generated/status"), 3.0); + return; + } + if (!estimate.hasAcceptanceCorrectedPairs()) { + registry.fill(HIST("mcValidation/generated/status"), 4.0); + return; + } + + const double estimatedSeeds = estimate.nuncSeeds(); + const double bias = estimatedSeeds - trueNMPI; + registry.fill(HIST("mcValidation/generated/status"), 5.0); + registry.fill(HIST("mcValidation/generated/estimatedSeedsVsTrueNMPI"), trueNMPI, estimatedSeeds); + registry.fill(HIST("mcValidation/generated/estimatedSeedsVsTrueNMPIVsMultiplicity"), multiplicity, trueNMPI, estimatedSeeds); + if (estimate.usedMapEM) { + registry.fill(HIST("mcValidation/generated/probabilityEstimatedSeedsVsTrueNMPI"), trueNMPI, estimate.probabilityNuncSeeds()); + } + registry.fill(HIST("mcValidation/generated/profileEstimatedSeedsVsTrueNMPI"), trueNMPI, estimatedSeeds); + registry.fill(HIST("mcValidation/generated/profileBiasVsTrueNMPI"), trueNMPI, bias); + if (trueNMPI > 0) { + registry.fill(HIST("mcValidation/generated/relativeResidualVsTrueNMPI"), trueNMPI, bias / trueNMPI); + } + } + + template + void flushSeedAxisFills(TTarget target, const std::vector& triggerFills, const std::vector& pairFills, double eventSeed) + { + for (const auto& fill : triggerFills) { + target->getTriggerHist()->Fill(step, fill.pt, fill.multiplicity, fill.posZ, getEventSeedUserAxisValue(fill.multiplicity, eventSeed), fill.weight); + } + for (const auto& fill : pairFills) { + target->getPairHist()->Fill(step, fill.deltaEta, fill.assocPt, fill.triggerPt, fill.multiplicity, fill.deltaPhi, fill.posZ, getEventSeedUserAxisValue(fill.multiplicity, eventSeed), fill.weight); + } + } + + template + double estimateEventSeedWithoutFilling(TTarget target, TTracks& tracks, float multiplicity, float posZ, int magField) + { + EventSeedEstimate estimate; + std::vector discardedTriggerFills; + std::vector discardedPairFills; + discardedTriggerFills.reserve(tracks.size()); + discardedPairFills.reserve(tracks.size() * tracks.size()); + fillCorrelations(std::move(target), tracks, tracks, multiplicity, posZ, magField, 1.0f, &estimate, &discardedTriggerFills, &discardedPairFills, -1.f, false, false); + finalizeEventSeedEstimate(estimate); + return estimate.nuncSeeds(); + } + + template + void fillCorrelations(TTarget target, TTracks1& tracks1, TTracks2& tracks2, float multiplicity, float posZ, int magField, float eventWeight, EventSeedEstimate* seedEstimate = nullptr, std::vector* pendingTriggerFills = nullptr, std::vector* pendingPairFills = nullptr, double userAxisValue = -1.0, bool fillEstimatorAcceptanceQA = true, bool fillLoopQA = true, float efficiencyMultiplicity = -1.f, const DetectorClosureResponseCoordinates* detectorClosureResponse = nullptr) + { + if (multiplicity < 0.f) { + return; + } + const float efficiencyLookupMultiplicity = efficiencyMultiplicity >= 0.f ? efficiencyMultiplicity : multiplicity; + + // Cache efficiency for particles (too many FindBin lookups) + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + efficiencyAssociatedCache.clear(); + efficiencyAssociatedCache.reserve(tracks2.size()); + for (const auto& track : tracks2) { + efficiencyAssociatedCache.push_back(getEfficiencyCorrection(cfg.mEfficiencyAssociated, track.eta(), track.pt(), efficiencyLookupMultiplicity, posZ)); + } + } + } + + for (const auto& track1 : tracks1) { + // LOGF(info, "Track %f | %f | %f %d %d", track1.eta(), track1.phi(), track1.pt(), track1.isGlobalTrack(), track1.isGlobalTrackSDD()); + + if constexpr (step <= CorrelationContainer::kCFStepTracked && !std::experimental::is_detected::value) { + if (!checkObject(track1)) { + continue; + } + } + + // sign check and PDG code special cases + if constexpr (std::experimental::is_detected::value) { + // If the MC trigger particle is on the trigger PDG code list, we will accept them regardless of their charge. + if (!cfgMcTriggerPDGs->empty()) { + if (std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track1.pdgCode()) == cfgMcTriggerPDGs->end()) { + continue; + } + } else { // otherwise check the sign against the configuration + const int sign = getParticleSign(track1); + if (cfgGeneral.cfgTriggerCharge != 0) { + if (cfgGeneral.cfgTriggerCharge * sign < 0) { + continue; + } + } else if (sign == 0) { + continue; // reject neutral MC particles + } + } + } else if constexpr (std::experimental::is_detected::value) { + // Check reco objects that have the sign attribute. There are no neutrals to deal with. + if (cfgGeneral.cfgTriggerCharge != 0 && cfgGeneral.cfgTriggerCharge * track1.sign() < 0) { + continue; + } + } + + if constexpr (std::experimental::is_detected::value) { + if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track1.decay()))) == 0u) { + continue; // skip particles that do not match the decay mask + } + if (cfgV0RapidityMax > 0) { + auto [t, y] = getV0Rapidity(track1); + if (t && std::abs(y) > cfgV0RapidityMax) { + continue; // V0s are not allowed to be outside the rapidity range + } + if (fillLoopQA) { + registry.fill(HIST("yvspt"), y, track1.pt()); + } + } + } + + if constexpr (std::experimental::is_detected::value) { + if (track1.cfParticleDaugh0Id() < 0 && track1.cfParticleDaugh1Id() < 0) { + continue; // these we could not match + } + } + + float triggerWeight = eventWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyTrigger) { + triggerWeight *= getEfficiencyCorrection(cfg.mEfficiencyTrigger, track1.eta(), track1.pt(), efficiencyLookupMultiplicity, posZ); + } + if (detectorClosureResponse) { + registry.fill(HIST("detectorClosure/triggerResponseRaw"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->rawMultiplicity, track1.pt(), triggerWeight); + registry.fill(HIST("detectorClosure/triggerResponseCorrected"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->correctedMultiplicity, track1.pt(), triggerWeight); + } + } + + if (cfgUserAxis == EventSeedAxis) { + if (pendingTriggerFills) { + pendingTriggerFills->push_back({track1.pt(), multiplicity, posZ, triggerWeight}); + } else { + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, userAxisValue, triggerWeight); + } + } else if (cfgUserAxis == NMPIAxis) { + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, userAxisValue, triggerWeight); + } else if (cfgUserAxis == InvariantMassAxis) { + if constexpr (std::experimental::is_detected::value) { + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, track1.invMass(), triggerWeight); + } else if constexpr (std::experimental::is_detected::value) { + // TParticlePDG *p = pdg->GetParticle(track1.pdgCode()); + // target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, p->Mass(), triggerWeight); + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, 1.8, triggerWeight); + } else { + LOGF(fatal, "Can not fill invariant-mass user axis without invMass column. Disable cfgUserAxis or select another mode."); + } + } else { + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + } + + const bool triggerHasTemplate = seedEstimate && hasTriggerTemplate(multiplicity, track1.pt()); + if (triggerHasTemplate) { + ++seedEstimate->nTriggers; + if (cfgEventSeedEstimatorMethod == 1) { + addTriggerPriorExpectations(*seedEstimate, multiplicity, track1.pt()); + } + } + + for (const auto& track2 : tracks2) { + if constexpr (std::is_same::value) { + if (track1.globalIndex() == track2.globalIndex()) { + // LOGF(info, "Track identical: %f | %f | %f || %f | %f | %f", track1.eta(), track1.phi(), track1.pt(), track2.eta(), track2.phi(), track2.pt()); + continue; + } + } + + if constexpr (std::experimental::is_detected::value) { // skip those that are specifically chosen to be triggers + if (!cfgMcTriggerPDGs->empty() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), track2.pdgCode()) != cfgMcTriggerPDGs->end()) { + continue; + } + } + + // Daughter particle checks + if constexpr (std::experimental::is_detected::value) { + if (track2.globalIndex() == track1.cfParticleDaugh0Id()) { // do not correlate daughter particles of the same event + continue; + } + } + if constexpr (std::experimental::is_detected::value) { + if (track2.globalIndex() == track1.cfParticleDaugh1Id()) { // do not correlate daughter particles of the same event + continue; + } + } + + if constexpr (step <= CorrelationContainer::kCFStepTracked && !std::experimental::is_detected::value) { + if (!checkObject(track2)) { + continue; + } + } + + if constexpr (std::experimental::is_detected::value) { + if (cfgDecayParticleMask != 0 && (cfgDecayParticleMask & (1u << static_cast(track2.decay()))) == 0u) { + continue; // skip particles that do not match the decay mask + } + } + + if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { + if (cfgGeneral.cfgCorrelationMethod == CorrelationMethod::Dd && track1.decay() != track2.decay()) { + continue; + } + if (cfgGeneral.cfgCorrelationMethod == CorrelationMethod::Ddbar && track1.decay() == track2.decay()) { + continue; + } + } + + if (cfgGeneral.cfgPtOrder != 0 && track2.pt() >= track1.pt()) { + continue; + } + + if constexpr (std::experimental::is_detected::value || std::experimental::is_detected::value) { + const int associatedSign = getParticleSign(track2); + if (cfgGeneral.cfgAssociatedCharge != 0) { + if (cfgGeneral.cfgAssociatedCharge * associatedSign < 0) { + continue; + } + } else if (associatedSign == 0) { // mc particles come in neutrals, need to check explicitly + continue; + } + } + + if constexpr ((std::experimental::is_detected::value || std::experimental::is_detected::value) && + (std::experimental::is_detected::value || std::experimental::is_detected::value)) { + if (cfgGeneral.cfgPairCharge != 0 && cfgGeneral.cfgPairCharge * getParticleSign(track1) * getParticleSign(track2) < 0) { + continue; + } + } + + if constexpr (std::is_same::value) { + if constexpr (step >= CorrelationContainer::kCFStepReconstructed) { + if constexpr (std::experimental::is_detected::value && std::experimental::is_detected::value) { + if (cfg.mPairCuts && mPairCuts.conversionCuts(track1, track2)) { + continue; + } + if (cfgGeneral.cfgTwoTrackCut > 0 && mPairCuts.twoTrackCut(track1, track2, magField)) { + continue; + } + } + } + } + + float associatedWeight = triggerWeight; + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (cfg.mEfficiencyAssociated) { + associatedWeight *= efficiencyAssociatedCache[track2.filteredIndex()]; + } + } + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PIHalf); + const float deltaEta = track1.eta() - track2.eta(); + + if constexpr (step == CorrelationContainer::kCFStepCorrected) { + if (detectorClosureResponse) { + registry.fill(HIST("detectorClosure/pairResponseRaw"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->rawMultiplicity, deltaEta, track2.pt(), track1.pt(), deltaPhi, associatedWeight); + registry.fill(HIST("detectorClosure/pairResponseCorrected"), detectorClosureResponse->trueMultiplicity, detectorClosureResponse->correctedMultiplicity, deltaEta, track2.pt(), track1.pt(), deltaPhi, associatedWeight); + } + } + + if (triggerHasTemplate) { + ++seedEstimate->nCandidatePairs; + if (const auto* yieldTemplate = findYieldTemplate(multiplicity, track1.pt(), track2.pt())) { + addPairProbabilities(*seedEstimate, *yieldTemplate, multiplicity, deltaPhi, deltaEta, posZ, fillEstimatorAcceptanceQA); + } else { + ++seedEstimate->nPairsWithoutTemplate; + } + } + + // last param is the weight + if (cfgUserAxis == EventSeedAxis) { + if (pendingPairFills) { + pendingPairFills->push_back({deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight}); + } else { + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, userAxisValue, associatedWeight); + } + } else if (cfgUserAxis == NMPIAxis) { + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, userAxisValue, associatedWeight); + } else if (cfgUserAxis == InvariantMassAxis) { + if constexpr (std::experimental::is_detected::value) { + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, track1.invMass(), associatedWeight); + } else if constexpr (std::experimental::is_detected::value) { + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, 1.8, associatedWeight); // p->Mass() + } else { + LOGF(fatal, "Can not fill invariant-mass user axis without invMass column. Disable cfgUserAxis or select another mode."); + } + } else { + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, associatedWeight); + } + } + } + } + + void loadEfficiency(uint64_t timestamp) + { + if (cfg.efficiencyLoaded) { + return; + } + if (!cfgCorrections.cfgEfficiencyTrigger.value.empty()) { + if (cfgGeneral.cfgLocalEfficiency > 0) { + TFile* fEfficiencyTrigger = TFile::Open(cfgCorrections.cfgEfficiencyTrigger.value.c_str(), "READ"); + cfg.mEfficiencyTrigger = dynamic_cast(fEfficiencyTrigger->Get("ccdb_object")); + } else { + cfg.mEfficiencyTrigger = ccdb->getForTimeStamp>(cfgCorrections.cfgEfficiencyTrigger, timestamp); + } + if (cfg.mEfficiencyTrigger == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgCorrections.cfgEfficiencyTrigger.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram for trigger particles from %s", cfgCorrections.cfgEfficiencyTrigger.value.c_str()); + } + if (!cfgCorrections.cfgEfficiencyAssociated.value.empty()) { + if (cfgGeneral.cfgLocalEfficiency > 0) { + TFile* fEfficiencyAssociated = TFile::Open(cfgCorrections.cfgEfficiencyAssociated.value.c_str(), "READ"); + cfg.mEfficiencyAssociated = dynamic_cast(fEfficiencyAssociated->Get("ccdb_object")); + } else { + cfg.mEfficiencyAssociated = ccdb->getForTimeStamp>(cfgCorrections.cfgEfficiencyAssociated, timestamp); + } + if (cfg.mEfficiencyAssociated == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for associated particles from %s", cfgCorrections.cfgEfficiencyAssociated.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram for associated particles from %s", cfgCorrections.cfgEfficiencyAssociated.value.c_str()); + } + cfg.efficiencyLoaded = true; + } + + void loadAcceptance(const uint64_t timestamp, const int runNumber) + { + if (cfgCorrections.cfgFillAcceptanceWeights || cfgCorrections.cfgAcceptance.value.empty()) { + cfg.mAcceptance = nullptr; + cfg.acceptanceLoaded = true; + return; + } + if (cfg.acceptanceLoaded && (!cfgCorrections.cfgAcceptanceRunByRun || cfg.acceptanceRunNumber == runNumber)) { + return; + } + + std::string path = cfgCorrections.cfgAcceptance.value; + if (cfgCorrections.cfgAcceptanceRunByRun) { + if (path.back() != '/') { + path += '/'; + } + path += "RunByRun/"; + } + cfg.mAcceptance = ccdb->getForTimeStamp(path, timestamp); + if (!cfg.mAcceptance || !cfg.mAcceptance->isDataFilled()) { + LOGF(fatal, "Could not load a populated GFW acceptance object from %s for run %d at timestamp %llu", path.c_str(), runNumber, timestamp); + } + cfg.acceptanceLoaded = true; + cfg.acceptanceRunNumber = runNumber; + LOGF(info, "Loaded GFW acceptance object from %s for run %d", path.c_str(), runNumber); + } + + double getEfficiencyCorrection(THn* eff, float eta, float pt, float multiplicity, float posZ) + { + std::array effVars{}; + effVars[0] = eff->GetAxis(0)->FindBin(eta); + effVars[1] = eff->GetAxis(1)->FindBin(pt); + effVars[2] = eff->GetAxis(2)->FindBin(multiplicity); + effVars[3] = eff->GetAxis(3)->FindBin(posZ); + return eff->GetBinContent(effVars.data()); + } + + template + void processSameAODT(TCollision const& collision, TTracks const& tracks, const int* trueNMPI = nullptr) + { + // NOTE legacy function for O2 integration tests. Full version needs derived data + + if (cfgVerbosity > 0) { + LOGF(info, "processSameAOD: Tracks for collision: %d | Vertex: %.1f | INT7: %d | V0M: %.1f", tracks.size(), collision.posZ(), collision.sel7(), collision.centRun2V0M()); + } + + // TODO will go to CCDBConfigurable + auto bc = collision.template bc_as(); + loadEfficiency(bc.timestamp()); + loadCcdbYieldTemplates(bc.timestamp()); + + const auto multiplicity = collision.centRun2V0M(); + + if (!fillCollisionAOD(same, collision, multiplicity)) { + return; + } + registry.fill(HIST("eventcount_same"), -2); + fillQA(collision, multiplicity, tracks); + EventSeedEstimate seedEstimate; + std::vector pendingTriggerFills; + std::vector pendingPairFills; + if (cfgUserAxis == EventSeedAxis) { + pendingTriggerFills.reserve(tracks.size()); + pendingPairFills.reserve(tracks.size() * tracks.size()); + } + fillCorrelations(same, tracks, tracks, multiplicity, collision.posZ(), getMagneticField(bc.timestamp()), 1.0f, &seedEstimate, + cfgUserAxis == EventSeedAxis ? &pendingTriggerFills : nullptr, + cfgUserAxis == EventSeedAxis ? &pendingPairFills : nullptr); + finalizeEventSeedEstimate(seedEstimate); + if (cfgUserAxis == EventSeedAxis) { + flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); + } + fillEventSeedEstimatorQA(multiplicity, seedEstimate); + if (trueNMPI) { + fillMCValidation(multiplicity, seedEstimate, *trueNMPI); + } + } + + // Version with explicit nested loop + void processSameAOD(AodCollisions::iterator const& collision, aod::BCsWithTimestamps const&, AodTracks const& tracks) + { + processSameAODT(collision, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameAOD, "Process same event on AOD", true); + + void processSameGenMC(soa::Filtered::iterator const& mcCollision, + soa::Filtered const& mcParticles, + soa::SmallGroups const& collisions) + { + if (cfgCorrections.cfgFillAcceptanceWeights) { + return; + } + if (cfgUserAxis == NMPIAxis && mcCollision.nMPI() < 0) { + return; + } + if (!cfgNuncSeedsTemplate.value.empty()) { + for (const auto& collision : collisions) { + loadCcdbYieldTemplates(collision.timestamp()); + break; + } + } + + const auto generatedMultiplicity = mcCollision.multiplicity(); + const double trueNMPIUserAxisValue = cfgUserAxis == NMPIAxis + ? getTrueNMPIUserAxisValue(generatedMultiplicity, mcCollision.nMPI()) + : -1.0; + if (cfgUserAxis == NMPIAxis) { + fGFW->Clear(); + fFCptGen->clearVector(); + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + const int sign = getParticleSign(mcParticle); + if (sign == 0 || (cfgGeneral.cfgAssociatedCharge != 0 && cfgGeneral.cfgAssociatedCharge * sign < 0)) { + continue; + } + if (!cfgMcTriggerPDGs->empty() && + std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) { + continue; + } + fillPtSums(mcParticle, generatedMultiplicity, mcCollision.posZ()); + fillGFW(mcParticle, generatedMultiplicity, mcCollision.posZ()); + } + fillOutputContainers(generatedMultiplicity, fRndm->Rndm(), getTrueNMPIClass(generatedMultiplicity, mcCollision.nMPI())); + } + fillContainerEvent(same, generatedMultiplicity, CorrelationContainer::kCFStepAll); + EventSeedEstimate seedEstimate; + std::vector pendingTriggerFills; + std::vector pendingPairFills; + if (cfgUserAxis == EventSeedAxis) { + pendingTriggerFills.reserve(mcParticles.size()); + pendingPairFills.reserve(mcParticles.size() * mcParticles.size()); + } + fillCorrelations(same, mcParticles, mcParticles, generatedMultiplicity, mcCollision.posZ(), 0, 1.0f, &seedEstimate, + cfgUserAxis == EventSeedAxis ? &pendingTriggerFills : nullptr, + cfgUserAxis == EventSeedAxis ? &pendingPairFills : nullptr, + trueNMPIUserAxisValue); + finalizeEventSeedEstimate(seedEstimate); + if (cfgUserAxis == EventSeedAxis) { + flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); + } + fillGeneratedMCValidation(generatedMultiplicity, seedEstimate, mcCollision.nMPI()); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameGenMC, "Process generated MC events from derived data and validate against the stored HepMC N MPI", false); + + template + void processSameDerivedT(CollType const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2, const int* trueNMPI = nullptr, const float* outputMultiplicityOverride = nullptr, const float* efficiencyMultiplicityOverride = nullptr, const DetectorClosureResponseCoordinates* detectorClosureResponse = nullptr) + { + auto getMultiplicity = [](const auto& col) { + return getAnalysisMultiplicity(col); + }; + using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; // underflows and overflows get bin -1 + const float collisionMultiplicity = getMultiplicity(collision); + const float multiplicity = outputMultiplicityOverride ? *outputMultiplicityOverride : collisionMultiplicity; + const float efficiencyMultiplicity = efficiencyMultiplicityOverride ? *efficiencyMultiplicityOverride : collisionMultiplicity; + if (cfgVerbosity > 0) { + LOGF(info, "processSameDerivedT: Tracks for collision: %d/%d | Vertex: %.1f | Multiplicity/Centrality: %.1f", tracks1.size(), tracks2.size(), collision.posZ(), multiplicity); + } + if (cfgCorrections.cfgFillAcceptanceWeights) { + for (const auto& track : tracks2) { + fillAcceptanceWeights(track, collision.posZ(), collision.runNumber()); + } + return; + } + loadEfficiency(collision.timestamp()); + loadAcceptance(collision.timestamp(), collision.runNumber()); + loadCcdbYieldTemplates(collision.timestamp()); + + int field = 0; + if (cfgGeneral.cfgTwoTrackCut > 0) { + field = getMagneticField(collision.timestamp()); + } + + fGFW->Clear(); + fFCpt->clearVector(); + for (const auto& track : tracks2) { + fillPtSums(track, efficiencyMultiplicity, collision.posZ()); + fillGFW(track, efficiencyMultiplicity, collision.posZ()); + } + + int bin = configurableBinningDerived.getBin(std::tuple(collision.posZ(), multiplicity)); + registry.fill(HIST("eventcount_same"), bin); + registry.fill(HIST("trackcount_same"), bin, tracks1.size()); + if constexpr (std::experimental::is_detected::value) { + fillQA(collision, multiplicity, collision.posZ(), tracks1, tracks2); + } else { + fillQA(collision, multiplicity, tracks1); + } + + const bool hasEfficiency = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + const bool fillReco = !(cfgGeneral.cfgDropStepRECO && hasEfficiency); + EventSeedEstimate seedEstimate; + + if (cfgUserAxis == EventSeedAxis) { + std::vector pendingTriggerFills; + std::vector pendingPairFills; + pendingTriggerFills.reserve(tracks1.size()); + pendingPairFills.reserve(tracks1.size() * tracks2.size()); + + if (fillReco) { + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills); + finalizeEventSeedEstimate(seedEstimate); + flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); + } else if (hasEfficiency) { + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate, &pendingTriggerFills, &pendingPairFills, -1.0, true, true, efficiencyMultiplicity, detectorClosureResponse); + finalizeEventSeedEstimate(seedEstimate); + flushSeedAxisFills(same, pendingTriggerFills, pendingPairFills, seedEstimate.nuncSeeds()); + } + + if (fillReco && hasEfficiency) { + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, nullptr, nullptr, nullptr, getEventSeedUserAxisValue(multiplicity, seedEstimate.nuncSeeds()), true, true, efficiencyMultiplicity, detectorClosureResponse); + } + const auto seedClass = getEventSeedClass(multiplicity, seedEstimate.nuncSeeds()); + fillOutputContainers(multiplicity, fRndm->Rndm(), seedClass); + fillEventSeedEstimatorQA(multiplicity, seedEstimate); + if (trueNMPI) { + fillMCValidation(multiplicity, seedEstimate, *trueNMPI); + } + return; + } + + if (fillReco) { + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, &seedEstimate); + } + if (hasEfficiency) { + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepCorrected); + fillCorrelations(same, tracks1, tracks2, multiplicity, collision.posZ(), field, 1.0f, fillReco ? nullptr : &seedEstimate, nullptr, nullptr, -1.0, true, true, efficiencyMultiplicity, detectorClosureResponse); + } + finalizeEventSeedEstimate(seedEstimate); + fillOutputContainers(multiplicity, fRndm->Rndm()); + fillEventSeedEstimatorQA(multiplicity, seedEstimate); + if (trueNMPI) { + fillMCValidation(multiplicity, seedEstimate, *trueNMPI); + } + } + + void processSameDerived(DerivedCollisions::iterator const& collision, DerivedTracks const& tracks) + { + processSameDerivedT(collision, tracks, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameDerived, "Process same event on derived data", false); + + void processSameDerivedCorrected(DerivedCollisionsCorrected::iterator const& collision, DerivedTracks const& tracks) + { + processSameDerivedT(collision, tracks, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameDerivedCorrected, "Process same event on derived data with corrected multiplicity", false); + + void processSameDerivedMultSet(DerivedCollisionsMultSet::iterator const& collision, DerivedTracks const& tracks) + { + if (!passOutlier(collision)) { + return; + } + processSameDerivedT(collision, tracks, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameDerivedMultSet, "Process same event on derived data with multiplicity sets", false); + + void processSameDerivedMultSetCorrected(DerivedCollisionsMultSetCorrected::iterator const& collision, DerivedTracks const& tracks) + { + if (!passOutlier(collision)) { + return; + } + processSameDerivedT(collision, tracks, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processSameDerivedMultSetCorrected, "Process same event on derived data with corrected multiplicity and multiplicity sets", false); + + using BinningTypeAOD = ColumnBinningPolicy; + void processMixedAOD(AodCollisions const& collisions, AodTracks const& tracks, aod::BCsWithTimestamps const&) + { + // NOTE legacy function for O2 integration tests. Full version needs derived data + + // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 + BinningTypeAOD configurableBinning{{axisVertex, axisMultiplicity}}; + auto tracksTuple = std::make_tuple(tracks); + SameKindPair pairs{configurableBinning, cfgNumMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + + int skipID = -1; + double triggerEventSeed = -1.0; + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + int bin = configurableBinning.getBin({collision1.posZ(), collision1.centRun2V0M()}); + if (cfgVerbosity > 0) { + LOGF(info, "processMixedAOD: Mixed collisions bin: %d pair: %d (%.3f, %.3f), %d (%.3f, %.3f)", bin, collision1.globalIndex(), collision1.posZ(), collision1.centRun2V0M(), collision2.globalIndex(), collision2.posZ(), collision2.centRun2V0M()); + } + if (collision1.globalIndex() == skipID) { + continue; + } + + if (it.isNewWindow()) { + skipID = -1; + if (!fillCollisionAOD(mixed, collision1, collision1.centRun2V0M())) { + skipID = collision1.globalIndex(); + continue; + } + if (cfgUserAxis == EventSeedAxis) { + auto bc = collision1.bc_as(); + loadCcdbYieldTemplates(bc.timestamp()); + triggerEventSeed = getEventSeedUserAxisValue(collision1.centRun2V0M(), estimateEventSeedWithoutFilling(mixed, tracks1, collision1.centRun2V0M(), collision1.posZ(), getMagneticField(bc.timestamp()))); + } + } + if (!collision2.alias_bit(kINT7) || !collision2.sel7()) { + continue; + } + + registry.fill(HIST("eventcount_mixed"), bin); + + auto bc = collision1.bc_as(); + + // LOGF(info, "Tracks: %d and %d entries", tracks1.size(), tracks2.size()); + + fillCorrelations(mixed, tracks1, tracks2, collision1.centRun2V0M(), collision1.posZ(), getMagneticField(bc.timestamp()), 1.0f / it.currentWindowNeighbours(), nullptr, nullptr, nullptr, triggerEventSeed); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMixedAOD, "Process mixed events on AOD", false); + + template + void processMixedDerivedT(CollType const& collisions, TrackTypes&&... tracks) + { + auto getMultiplicity = + [this](const auto& col) { + if constexpr (std::experimental::is_detected::value) { + if (!passOutlier(col)) { + return -1.0f; + } + } else { + (void)this; // fix compile error on unused 'this' capture + } + return getAnalysisMultiplicity(col); + }; + + using BinningTypeDerived = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; + BinningTypeDerived configurableBinningDerived{{getMultiplicity}, {axisVertex, axisMultiplicity}}; + // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 + auto tracksTuple = std::make_tuple(std::forward(tracks)...); + using TA = std::tuple_element<0, decltype(tracksTuple)>::type; + using TB = std::tuple_element - 1, decltype(tracksTuple)>::type; + Pair pairs{configurableBinningDerived, cfgNumMixedEvents, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + + double triggerEventSeed = -1.0; + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + float multiplicity = getMultiplicity(collision1); + int bin = configurableBinningDerived.getBin(std::tuple(collision1.posZ(), multiplicity)); + float eventWeight = 1.0f / it.currentWindowNeighbours(); + int field = 0; + if (cfgGeneral.cfgTwoTrackCut > 0) { + field = getMagneticField(collision1.timestamp()); + } + + if (cfgVerbosity > 0) { + LOGF(info, "processMixedDerived: Mixed collisions bin: %d pair: [%d, %d] %d (%.3f, %.3f), %d (%.3f, %.3f)", bin, it.isNewWindow(), it.currentWindowNeighbours(), collision1.globalIndex(), collision1.posZ(), multiplicity, collision2.globalIndex(), collision2.posZ(), getAnalysisMultiplicity(collision2)); + } + + bool hasEfficiencyMixed = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + bool fillRecoMixed = !(cfgGeneral.cfgDropStepRECO && hasEfficiencyMixed); + + if (it.isNewWindow()) { + loadEfficiency(collision1.timestamp()); + hasEfficiencyMixed = (cfg.mEfficiencyAssociated != nullptr || cfg.mEfficiencyTrigger != nullptr); + fillRecoMixed = !(cfgGeneral.cfgDropStepRECO && hasEfficiencyMixed); + + if (cfgUserAxis == EventSeedAxis) { + loadCcdbYieldTemplates(collision1.timestamp()); + if constexpr (std::is_same_v, std::remove_cvref_t>) { + triggerEventSeed = getEventSeedUserAxisValue(multiplicity, estimateEventSeedWithoutFilling(mixed, tracks1, multiplicity, collision1.posZ(), field)); + } else { + LOGF(fatal, "Event-seed user axis for mixed events requires the same trigger and associated track table so the trigger event can be estimated independently"); + } + } + + if (fillRecoMixed) { + fillContainerEvent(mixed, multiplicity, CorrelationContainer::kCFStepReconstructed); + } + } + + // LOGF(info, "Tracks: %d and %d entries", tracks1.size(), tracks2.size()); + + registry.fill(HIST("eventcount_mixed"), bin); + registry.fill(HIST("trackcount_mixed"), bin, tracks1.size(), tracks2.size()); + if (fillRecoMixed) { + fillCorrelations(mixed, tracks1, tracks2, multiplicity, collision1.posZ(), field, eventWeight, nullptr, nullptr, nullptr, triggerEventSeed); + } + + if (hasEfficiencyMixed) { + if (it.isNewWindow()) { + fillContainerEvent(mixed, multiplicity, CorrelationContainer::kCFStepCorrected); + } + fillCorrelations(mixed, tracks1, tracks2, multiplicity, collision1.posZ(), field, eventWeight, nullptr, nullptr, nullptr, triggerEventSeed); + } + } + } + + void processMixedDerived(DerivedCollisions const& collisions, DerivedTracks const& tracks) + { + processMixedDerivedT(collisions, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMixedDerived, "Process mixed events on derived data", false); + + void processMixedDerivedCorrected(DerivedCollisionsCorrected const& collisions, DerivedTracks const& tracks) + { + processMixedDerivedT(collisions, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMixedDerivedCorrected, "Process mixed events on derived data with corrected multiplicity", false); + + void processMixedDerivedMultSet(DerivedCollisionsMultSet const& collisions, DerivedTracks const& tracks) + { + processMixedDerivedT(collisions, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMixedDerivedMultSet, "Process mixed events on derived data with multiplicity sets", false); + + void processMixedDerivedMultSetCorrected(DerivedCollisionsMultSetCorrected const& collisions, DerivedTracks const& tracks) + { + processMixedDerivedT(collisions, tracks); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMixedDerivedMultSetCorrected, "Process mixed events on derived data with corrected multiplicity and multiplicity sets", false); + + template + void processMCSameDerivedT(McCollision const& mcCollision, Particles1 const& mcParticles1, Particles2 const& mcParticles2, Collisions const& collisions) + { + if (cfgCorrections.cfgFillAcceptanceWeights) { + return; + } + if (cfgUserAxis == NMPIAxis && mcCollision.nMPI() < 0) { + return; + } + if (cfgVerbosity > 0) { + LOGF(info, "processMCSameDerivedT. MC collision: %d, particles1: %d, particles2: %d, collisions: %d", mcCollision.globalIndex(), mcParticles1.size(), mcParticles2.size(), collisions.size()); + } + + auto multiplicity = mcCollision.multiplicity(); + if (cfgGeneral.cfgCentBinsForMC > 0) { + if (collisions.size() == 0) { + return; + } + for (const auto& collision : collisions) { + multiplicity = collision.multiplicity(); + } + } + const double trueNMPIUserAxisValue = cfgUserAxis == NMPIAxis + ? getTrueNMPIUserAxisValue(multiplicity, mcCollision.nMPI()) + : -1.0; + + if (!(doprocessMCSameDerived || doprocessMCDetectorClosure || doprocessSameDerived || doprocessSameDerivedCorrected || doprocessSameDerivedMultSet || doprocessSameDerivedMultSetCorrected)) { + if constexpr (std::experimental::is_detected::value) { + fillQA(mcCollision, multiplicity, mcCollision.posZ(), mcParticles1, mcParticles2); + } else { + fillQA(mcCollision, multiplicity, mcParticles1); + } + } + + fGFW->Clear(); + fFCptGen->clearVector(); + for (const auto& mcParticle : mcParticles2) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + const int sign = getParticleSign(mcParticle); + if (sign == 0 || (cfgGeneral.cfgAssociatedCharge != 0 && cfgGeneral.cfgAssociatedCharge * sign < 0)) { + continue; + } + if (!cfgMcTriggerPDGs->empty() && std::find(cfgMcTriggerPDGs->begin(), cfgMcTriggerPDGs->end(), mcParticle.pdgCode()) != cfgMcTriggerPDGs->end()) { + continue; + } + fillPtSums(mcParticle, multiplicity, mcCollision.posZ()); + fillGFW(mcParticle, multiplicity, mcCollision.posZ()); + } + const SeedClass nMPIClass = cfgUserAxis == NMPIAxis ? getTrueNMPIClass(multiplicity, mcCollision.nMPI()) : InvalidSeedClass; + fillOutputContainers(multiplicity, fRndm->Rndm(), nMPIClass); + + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepAll); + fillCorrelations(same, mcParticles1, mcParticles2, multiplicity, mcCollision.posZ(), 0, 1.0f, + nullptr, nullptr, nullptr, trueNMPIUserAxisValue); + + if (collisions.size() == 0) { + return; + } + + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepVertex); + fillCorrelations(same, mcParticles1, mcParticles2, multiplicity, mcCollision.posZ(), 0, 1.0f, + nullptr, nullptr, nullptr, trueNMPIUserAxisValue); + + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepTrackedOnlyPrim); + fillCorrelations(same, mcParticles1, mcParticles2, multiplicity, mcCollision.posZ(), 0, 1.0f, + nullptr, nullptr, nullptr, trueNMPIUserAxisValue); + + fillContainerEvent(same, multiplicity, CorrelationContainer::kCFStepTracked); + fillCorrelations(same, mcParticles1, mcParticles2, multiplicity, mcCollision.posZ(), 0, 1.0f, + nullptr, nullptr, nullptr, trueNMPIUserAxisValue); + + // kCFStepReconstructed and kCFStepCorrected are filled below for every + // reconstructed collision associated with this MC collision. + } + + // NOTE SmallGroups includes soa::Filtered always + Preslice derivedTracksPerCollision = aod::cftrack::cfCollisionId; + void processMCSameDerived(soa::Filtered::iterator const& mcCollision, soa::Filtered const& mcParticles, soa::SmallGroups const& collisions, soa::Filtered const& tracks) // TODO. For mixed no need to check the daughters since the events are different + { + processMCSameDerivedT(mcCollision, mcParticles, mcParticles, collisions); + if (cfgUserAxis == NMPIAxis) { + return; + } + if (eventSeedEstimatorEnabled && collisions.size() == 0) { + registry.fill(HIST("mcValidation/status"), 0.0); + } + const int trueNMPI = mcCollision.nMPI(); + for (const auto& collision : collisions) { + auto collisionTracks = tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex()); + processSameDerivedT(collision, collisionTracks, collisionTracks, &trueNMPI); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCSameDerived, "Process generated and reconstructed MC same events on derived data and validate against HepMC N MPI", false); + + void processMCDetectorClosure(soa::Filtered::iterator const& mcCollision, + soa::Filtered const& mcParticles, + soa::SmallGroups const& collisions, + soa::Filtered const& tracks) + { + registry.fill(HIST("detectorClosure/recoCollisionsPerMCCollision"), static_cast(collisions.size())); + + int bestRecoCollisionCount = 0; + for (const auto& collision : collisions) { + if (collision.bestRecoCollision()) { + ++bestRecoCollisionCount; + } + } + + if (collisions.size() == 0) { + registry.fill(HIST("detectorClosure/status"), 0.0); + } else if (bestRecoCollisionCount == 1) { + registry.fill(HIST("detectorClosure/status"), 1.0); + } else { + registry.fill(HIST("detectorClosure/status"), 2.0); + LOGF(fatal, "processMCDetectorClosure expected exactly one best reconstructed collision for MC collision %d, found %d among %d reconstructed collisions", + mcCollision.globalIndex(), bestRecoCollisionCount, collisions.size()); + } + + // Fill generated steps once. kCFStepVertex is filled only when at least one + // selected reconstructed collision is associated with this MC collision. + processMCSameDerivedT(mcCollision, mcParticles, mcParticles, collisions); + + if (bestRecoCollisionCount == 0) { + return; + } + + const float trueMultiplicity = mcCollision.multiplicity(); + const int trueNMPI = mcCollision.nMPI(); + for (const auto& collision : collisions) { + if (!collision.bestRecoCollision()) { + continue; + } + + const float rawMultiplicity = collision.multiplicity(); + const float correctedMultiplicity = collision.multiplicityCorrected(); + registry.fill(HIST("detectorClosure/rawMultiplicityVsTrue"), trueMultiplicity, rawMultiplicity); + registry.fill(HIST("detectorClosure/correctedMultiplicityVsTrue"), trueMultiplicity, correctedMultiplicity); + registry.fill(HIST("detectorClosure/profileCorrectedMultiplicityVsTrue"), trueMultiplicity, correctedMultiplicity); + registry.fill(HIST("detectorClosure/profileTrueMultiplicityVsCorrected"), correctedMultiplicity, trueMultiplicity); + + auto collisionTracks = tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex()); + const DetectorClosureResponseCoordinates responseCoordinates{trueMultiplicity, rawMultiplicity, correctedMultiplicity}; + // Physics histograms use true multiplicity, while efficiency weights use + // the reconstructed multiplicity coordinate for which the map was made. + processSameDerivedT(collision, collisionTracks, collisionTracks, &trueNMPI, &trueMultiplicity, &rawMultiplicity, &responseCoordinates); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCDetectorClosure, "Fill generated, raw reconstructed, and efficiency-corrected MC correlations versus true charged multiplicity", false); + + PresliceUnsorted collisionPerMCCollision = aod::cfcollision::cfMcCollisionId; + template + void processMCMixedDerivedT(soa::Filtered const& mcCollisions, soa::Filtered const& collisions, ParticleTypes&&... particles) + { + bool useMCMultiplicity = (cfgGeneral.cfgCentBinsForMC == 0); + auto getMultiplicity = + [&collisions, &useMCMultiplicity, this](auto& col) { + if (useMCMultiplicity) { + return col.multiplicity(); + } + auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, col.globalIndex()); + if (groupedCollisions.size() == 0) { + return -1.0f; + } + return groupedCollisions.begin().multiplicity(); + }; + + using BinningTypeMCDerived = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; + BinningTypeMCDerived configurableBinning{{getMultiplicity}, {axisVertex, axisMultiplicity}}; + + // Strictly upper categorised collisions, for cfgNumMixedEvents combinations per bin, skipping those in entry -1 + auto tuple = std::make_tuple(std::forward(particles)...); + using TA = std::tuple_element<0, decltype(tuple)>::type; + using TB = std::tuple_element - 1, decltype(tuple)>::type; + Pair, TA, TB, BinningTypeMCDerived> pairs{configurableBinning, cfgNumMixedEvents, -1, mcCollisions, tuple, &cache}; // -1 is the number of the bin to skip + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + float eventWeight = 1.0f / it.currentWindowNeighbours(); + + float multiplicity = getMultiplicity(collision1); + if (cfgVerbosity > 0) { + int bin = configurableBinning.getBin(std::tuple(collision1.posZ(), multiplicity)); + LOGF(info, "processMCMixedDerived: Mixed collisions bin: %d pair: [%d, %d] %d (%.3f, %.3f), %d (%.3f, %.3f)", bin, it.isNewWindow(), it.currentWindowNeighbours(), collision1.globalIndex(), collision1.posZ(), getMultiplicity(collision1), collision2.globalIndex(), collision2.posZ(), getMultiplicity(collision2)); + } + + // STEP 0 + if (it.isNewWindow()) { + fillContainerEvent(mixed, multiplicity, CorrelationContainer::kCFStepAll); + } + fillCorrelations(mixed, tracks1, tracks2, multiplicity, collision1.posZ(), 0, eventWeight); + // check if collision1 has at least one reconstructed collision + auto groupedCollisions = collisions.sliceBy(collisionPerMCCollision, collision1.globalIndex()); + if (cfgVerbosity > 0) { + LOGF(info, "Found %d related collisions", groupedCollisions.size()); + } + if (groupedCollisions.size() == 0) { + continue; + } + + // STEP 2, 4, 5 + if (it.isNewWindow()) { + fillContainerEvent(mixed, multiplicity, CorrelationContainer::kCFStepVertex); + fillContainerEvent(mixed, multiplicity, CorrelationContainer::kCFStepTrackedOnlyPrim); + fillContainerEvent(mixed, multiplicity, CorrelationContainer::kCFStepTracked); + } + fillCorrelations(mixed, tracks1, tracks2, multiplicity, collision1.posZ(), 0, eventWeight); + fillCorrelations(mixed, tracks1, tracks2, multiplicity, collision1.posZ(), 0, eventWeight); + fillCorrelations(mixed, tracks1, tracks2, multiplicity, collision1.posZ(), 0, eventWeight); + + // NOTE kCFStepReconstructed and kCFStepCorrected are filled in processMixedDerived + // This also means that if a MC collision had several reconstructed vertices (collisions), all of them are filled + } + } + + void processMCMixedDerived(soa::Filtered const& mcCollisions, soa::Filtered const& mcParticles, soa::Filtered const& collisions) + { + processMCMixedDerivedT(mcCollisions, collisions, mcParticles); + } + PROCESS_SWITCH(TwoParticleCorrelationsMpi, processMCMixedDerived, + "Process MC mixed events on derived data", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx new file mode 100644 index 00000000000..8284aa29488 --- /dev/null +++ b/PWGCF/TwoParticleCorrelations/Tasks/twoParticleCorrelationsMultSpher.cxx @@ -0,0 +1,1262 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file twoParticleCorrelationsMultSpher.cxx +/// \brief Two-particle angular correlations in pp collisions at 13 TeV. +/// Structured in parallel with the AliRoot AliMESppColTask analysis. +/// MC truth level: McCollision + McParticles. +/// Includes: multiplicity classes, transverse sphericity, PID (MC: PDG code), +/// event mixing with 4 separate pools per sphericity class, +/// THnSparse for multi-dimensional analysis + TH2D SE/ME for +/// direct normalisation (both per-trigger and per-pair). +/// +/// Pool structure (separate for MC generated and Reco reconstructed data): +/// eventPoolsMC[sphClass][zvtxBin][multBin] +/// eventPoolsReco[sphClass][zvtxBin][multBin] +/// sphClass 0 : S_T in (0.0, 0.3] — jetty +/// sphClass 1 : S_T in (0.3, 0.6] — intermediate +/// sphClass 2 : S_T in (0.6, 1.0] — isotropic +/// sphClass 3 : S_T > 0.0 — all events (no sphericity cut) +/// +/// \author Madalina Tarzila + +#include "PWGHF/Utils/utilsAnalysis.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// ============================================================ +// PID species +// ============================================================ +enum PidSpecies { PidUnidentified = 0, + PidPion, + PidKaon, + PidProton, + NPidSpecies }; +static constexpr std::array PidNames{"unid", "pion", "kaon", "proton"}; + +// ============================================================ +// Sphericity classes +// ============================================================ +static constexpr int NSphClasses = 4; +static constexpr std::array SphMax{0.3, 0.6, 1.0, 1.0}; +static constexpr std::array SphLabels{"jetty", "intermediate", "isotropic", "all"}; + +// Half a bin: puts integer-valued axes (indices, cut-flow steps) at bin centres. +static constexpr double HalfBin = 0.5; + +// ============================================================ +// Cut-flow steps (bin numbers of the evSel_* and track cut-flow histograms) +// ============================================================ +enum EventCut { EventCutRead = 1, + EventCutZvtx, + EventCutMultBin, + EventCutZvtxBin, + EventCutLeadPt, + EventCutSphericity, + EventCutSphClass, + EventCutSeFilled, + NEventCuts = EventCutSeFilled }; +enum TrackCut { TrackCutSeen = 1, + TrackCutGlobal, + TrackCutDcaXY, + TrackCutDcaZ, + TrackCutEta, + TrackCutPt, + NTrackCuts = TrackCutPt }; + +// ============================================================ +struct TwoParticleCorrelationsMultSpher { + + // ---------------------------------------------------------------- + // Configurables + // ---------------------------------------------------------------- + Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histograms [0-10 GeV/c]"}; + Configurable nBinsPt2{"nBinsPt2", 50, "N bins in pT study histograms [0-5 GeV/c]"}; + Configurable mixPoolSize{"mixPoolSize", 200, "Max events per mixing pool cell"}; + Configurable minAssocPt{"minAssocPt", 0.2, "Min pT for track selection (GeV/c)"}; + Configurable minLeadPt{"minLeadPt", 1.0, "Min pT for leading (trigger) particle (GeV/c)"}; + Configurable maxLeadPt{"maxLeadPt", 2.0, "Max pT for leading (trigger) particle (GeV/c)"}; + Configurable minAssocCorr{"minAssocCorr", 1.0, "Min pT for associated particle in correlations (GeV/c)"}; + Configurable maxAssocCorr{"maxAssocCorr", 2.0, "Max pT for associated particle in correlations (GeV/c)"}; + Configurable maxEta{"maxEta", 0.8, "Max |eta| for track selection"}; + Configurable maxZvtx{"maxZvtx", 10.0, "Max |z_vtx| (cm)"}; + // |y| cut applied ONLY to PID-identified tracks (pion/kaon/proton) -- + // independent of maxEta, which applies to all-species tracks (hSE/hME). + // Rapidity is well-defined only for massive/identified particles; at + // pT=0.5 GeV/c, proton y_max~0.48, so |y|<0.5 keeps full acceptance for + // that species. Reference: ALICE 2511.10399 (arXiv, Nov 2025), Sec. 4 -- + // "tracks reconstructed within |eta|<0.8, with the additional rapidity + // cut of |y|<0.5". + Configurable maxRapPID{"maxRapPID", 0.5, "Max |y| for PID-identified tracks (pion/kaon/proton), independent of maxEta"}; + + // Reco-specific configurables + Configurable maxDcaXY{"maxDcaXY", 2.0, "Max DCA XY (cm) for reco tracks"}; + Configurable maxDcaZ{"maxDcaZ", 2.0, "Max DCA Z (cm) for reco tracks"}; + // Combined TPC+TOF PID criterion per ALICE 2511.10399 Sec. 4: two + // different thresholds -- selection at 2 sigma, ambiguity rejection + // at 3 sigma. + Configurable maxNSigmaPID{"maxNSigmaPID", 2.0, "Max combined N_sigma,PID for species selection (ALICE 2511.10399)"}; + Configurable maxNSigmaPIDAmbig{"maxNSigmaPIDAmbig", 3.0, "N_sigma,PID threshold for ambiguity rejection (>1 species below this -> PidUnidentified)"}; + + // Thresholds of the legacy PID gating (independent 3-sigma cuts on TPC + // and TOF, no combined quadrature, no pT-dependent TOF gating), kept so + // that it can be switched on with useLegacyNSigmaGating without removing + // the ALICE 2511.10399 criterion. + Configurable maxNSigmaTPC{"maxNSigmaTPC", 3.0, "[legacy] Max |N_sigma| TPC for PID, used only when useLegacyNSigmaGating=true"}; + Configurable maxNSigmaTOF{"maxNSigmaTOF", 3.0, "[legacy] Max |N_sigma| TOF for PID, used only when useLegacyNSigmaGating=true"}; + + // Three independently toggleable flags to compare the legacy PID + // methodology (independent 3-sigma TPC/TOF cuts, minimum-chi2 species + // choice, no rapidity cut) with the ALICE 2511.10399-based one. Each + // isolates one component, so any combination of the 8 can be run to + // see which change drives a statistics difference. The gating and + // selection defaults reproduce the 2511.10399 behaviour; setting all + // three to the legacy side reproduces the legacy pidFromNSigma. + // + // applyRapidityCutPID defaults to false: the |y|<0.5 cut only discarded + // already-correctly-identified tracks between |y|=0.5 and the |eta|<0.8 + // acceptance, so dropping it raises both yield and purity at the same + // time. Gating and selection stay at the 2511.10399 values, because + // relaxing the gating trades purity for statistics. + Configurable useLegacyNSigmaGating{"useLegacyNSigmaGating", false, "false=pT-gated combined N_sigma,PID (2511.10399); true=independent TPC/TOF 3-sigma AND-gate, TOF always required when available (legacy)"}; + Configurable useLegacyPIDSelection{"useLegacyPIDSelection", false, "false=reject if >1 species below ambiguity threshold; true=pick minimum-chi2 species among those passing the gate, no rejection (legacy)"}; + Service pdg{}; + + Configurable applyRapidityCutPID{"applyRapidityCutPID", false, "false=skip the |y| ZvtxEdges{-10., -5., -2.5, 2.5, 5., 10.}; + static constexpr std::array MultEdges{1, 10, 15, 20, 30, 46, 81}; + + static std::string multLabel(int im) + { + return std::to_string(MultEdges[im]) + "-" + std::to_string(MultEdges[im + 1] - 1); + } + + // ---------------------------------------------------------------- + // Mixing pools + // [sphClass][zvtxBin][multBin] — "all" (existing) + // [pidIdx][sphClass][zvtxBin][multBin] — per PID species + // pidIdx: 0=pion, 1=kaon, 2=proton + // ---------------------------------------------------------------- + static constexpr int NPidCorr = 3; // pion, kaon, proton + static constexpr std::array PidCorrNames{"pion", "kaon", "proton"}; + // PDG codes corresponding to pidIdx + static int pidCorrPDG(int idx) + { + if (idx == 0) { + return PDG_t::kPiPlus; + } + if (idx == 1) { + return PDG_t::kKPlus; + } + return PDG_t::kProton; + } + /// Index of a pion/kaon/proton in the NPidCorr-sized arrays, or -1 for any other species. + static int pidSpeciesToIdx(PidSpecies pid) + { + if (pid < PidPion || pid > PidProton) { + return -1; + } + return pid - PidPion; + } + + using EventPool = std::deque>; + // Pool "all" — separate per MC (generated) and Reco (reconstructed) + std::array, NZvtxBins>, NSphClasses> eventPoolsMC; + std::array, NZvtxBins>, NSphClasses> eventPoolsReco; + // Pool per PID species (like-sign) — separate for MC and Reco + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidMC; + std::array, NZvtxBins>, NSphClasses>, NPidCorr> eventPoolsPidReco; + + // ---------------------------------------------------------------- + // Histogram registry + handles + // ---------------------------------------------------------------- + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // SE/ME TH2D "all": [sphClass][multBin] — MC and Reco separate + std::array, NMultBins>, NSphClasses> hSeMC{}; + std::array, NMultBins>, NSphClasses> hMeMC{}; + std::array, NMultBins>, NSphClasses> hSeReco{}; + std::array, NMultBins>, NSphClasses> hMeReco{}; + + // SE/ME TH2D like-sign per species: [pidIdx][sphClass][multBin] — MC and Reco separate + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidMC{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hSePidReco{}; + std::array, NMultBins>, NSphClasses>, NPidCorr> hMePidReco{}; + + // Trigger counter: axes = mult bin (x) x sphClass (y) — MC and Reco separate + std::shared_ptr hNtrigMC; + std::shared_ptr hNtrigReco; + std::array, NPidCorr> hNtrigPidMC{}; + std::array, NPidCorr> hNtrigPidReco{}; + + // Per-mult sphericity — MC and Reco separate + std::array, NMultBins> hSphMultMC{}; + std::array, NMultBins> hSphMultReco{}; + + // ---------------------------------------------------------------- + // [DEBUG, TEMPORARY] Track-cut counter for processReco diagnostics. + // Bins: 1=tracks seen, 2=passed isGlobalTrackWoDCA, 3=passed DCAxy, + // 4=passed DCAz, 5=passed eta, 6=passed pt (selected). + // Remove once the empty-Reco-histograms issue is understood/fixed. + // ---------------------------------------------------------------- + std::shared_ptr hTrackCutDebugReco; + + // ---------------------------------------------------------------- + // PID confusion matrix (true species from PDG vs. tagged species from + // pidFromNSigma applied to the SAME reconstructed track, per pT bin). + // Quick contamination estimate, reported as a systematic; it does NOT + // correct anything. Filled only by processMCConfusion (needs both MC + // truth and reconstructed nSigma together, unlike + // processMC/processReco which only ever see one or the other). + // ---------------------------------------------------------------- + std::shared_ptr hPidConfusionMC; + + // ---------------------------------------------------------------- + // Helpers + // ---------------------------------------------------------------- + + static PidSpecies pidFromPDG(int pdg) + { + switch (std::abs(pdg)) { + case PDG_t::kPiPlus: + return PidPion; + case PDG_t::kKPlus: + return PidKaon; + case PDG_t::kProton: + return PidProton; + default: + return PidUnidentified; + } + } + + /// PID from TPC(+TOF) nSigma for reconstructed tracks. Two independently + /// toggleable behaviors (see the useLegacyNSigmaGating and + /// useLegacyPIDSelection Configurables), so that the legacy and the + /// ALICE 2511.10399-based methodologies can be compared: + /// + /// legacyGating (gate = "does this species hypothesis pass at all"): + /// false (default): pT>0.5 && TOF available -> combined + /// N_sigma,PID = sqrt(nsTPC^2+nsTOF^2) < maxSel (2 sigma, per ALICE + /// 2511.10399 Sec. 4); else TPC-only. + /// true: legacy style -- |nsTPC| < maxTPC AND (if TOF available) |nsTOF| < maxTOF, + /// independently (no quadrature sum for the gate itself), TOF + /// required whenever available regardless of pT. + /// + /// legacySelection (what to do with hypotheses that pass the gate): + /// false (default): reject the track outright (PidUnidentified) if MORE + /// THAN ONE hypothesis also falls below the looser ambiguity + /// threshold maxAmbig (3 sigma) -- this comparison always uses the + /// combined-quadrature N_sigma,PID, regardless of legacyGating, so + /// the two flags stay independently meaningful. + /// true: legacy style -- among hypotheses passing the gate, + /// pick the one with minimum chi2 = nsTPC^2+nsTOF^2 (or nsTPC^2 + /// alone if no TOF); no rejection for multiple passing hypotheses. + /// + /// The two flags combine independently (e.g. legacy gating + new + /// selection is a valid, meaningful hybrid) -- this is deliberate, so + /// the statistics impact of each component can be isolated rather than + /// only comparing "old vs new" as one indivisible block. + template + PidSpecies pidFromNSigma(const T& track, double maxSel, double maxAmbig, + double maxTPC, double maxTOF, + bool legacyGating, bool legacySelection) const + { + struct Hypo { + PidSpecies pid = PidUnidentified; + double nsTPC = 0.; + double nsTOF = 0.; + }; + const std::array hypos{{{PidPion, track.tpcNSigmaPi(), track.tofNSigmaPi()}, + {PidKaon, track.tpcNSigmaKa(), track.tofNSigmaKa()}, + {PidProton, track.tpcNSigmaPr(), track.tofNSigmaPr()}}}; + + // TOF unavailable -> nSigma returns -999; check with any species + bool hasTOF = (std::fabs(track.tofNSigmaPi()) < MaxValidNSigma); + bool useCombined = (track.pt() > MinPtCombinedPid) && hasTOF; // only feeds the new-style gate + + auto passesGate = [&](const Hypo& h) { + if (legacyGating) { + return (std::fabs(h.nsTPC) < maxTPC) && (!hasTOF || std::fabs(h.nsTOF) < maxTOF); + } + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + return nsPID < maxSel; + }; + + if (legacySelection) { + // Best-fit (min chi2) among hypotheses passing the gate -- no + // ambiguity rejection (legacy style). + PidSpecies best = PidUnidentified; + double bestChi2 = std::numeric_limits::max(); + for (const auto& h : hypos) { + if (!passesGate(h)) { + continue; + } + double chi2 = h.nsTPC * h.nsTPC + (hasTOF ? h.nsTOF * h.nsTOF : 0.0); + if (chi2 < bestChi2) { + bestChi2 = chi2; + best = h.pid; + } + } + return best; + } + + // Ambiguity-rejection selection (current default style). + int nBelowAmbig = 0; + PidSpecies selected = PidUnidentified; + for (const auto& h : hypos) { + if (!passesGate(h)) { + continue; + } + double nsPID = useCombined + ? std::sqrt(h.nsTPC * h.nsTPC + h.nsTOF * h.nsTOF) + : std::fabs(h.nsTPC); + if (nsPID < maxAmbig) { + ++nBelowAmbig; + } + selected = h.pid; + } + if (nBelowAmbig > 1) { + return PidUnidentified; // ambiguous, reject + } + return selected; + } + + /// Returns PDG mass for identified species, 0 for unidentified. + static double pidMass(PidSpecies pid) + { + switch (pid) { + case PidPion: + return o2::constants::physics::MassPiPlus; + case PidKaon: + return o2::constants::physics::MassKPlus; + case PidProton: + return o2::constants::physics::MassProton; + default: + return 0.; + } + } + + // Returns specific sph class (0,1,2) or -1 if unphysical + // Note: class 3 ("all") is handled separately — it is always filled + // when sph > 0, without needing a specific bin lookup. + static int sphClass(double sph) + { + if (sph <= 0.) { + return -1; + } + for (int ic = 0; ic < NSphClasses - 1; ++ic) { + if (sph <= SphMax[ic]) { + return ic; + } + } + return -1; + } + + static double computeSphericity(const std::vector& tracks) + { + if (tracks.size() < MinTracksSphericity) { + return -1.0; + } + double sxx = 0., sxy = 0., syy = 0.; + for (const auto& t : tracks) { + double px = t.pt * std::cos(t.phi); + double py = t.pt * std::sin(t.phi); + sxx += px * px; + sxy += px * py; + syy += py * py; + } + double tr = sxx + syy; + double disc = tr * tr - 4.0 * (sxx * syy - sxy * sxy); + if (disc < 0.) { + disc = 0.; + } + double lambdaMin = (tr - std::sqrt(disc)) / 2.0; + if (tr < Epsilon) { + return -1.0; + } + return std::clamp(2.0 * lambdaMin / tr, 0.0, 1.0); + } + + /// Compute rapidity from pT, eta and particle mass. + /// y = 0.5 * ln((energy + pz) / (energy - pz)) + /// For unidentified particles (mass <= 0), returns eta. + static double computeRapidity(double pt, double eta, double mass) + { + if (mass <= 0.) { + return eta; // massless / unidentified -> y = eta + } + double pz = pt * std::sinh(eta); + double energy = std::sqrt(mass * mass + pt * pt + pz * pz); + if (energy <= std::fabs(pz) + Epsilon) { + return eta; // safety + } + return 0.5 * std::log((energy + pz) / (energy - pz)); + } + + // ---------------------------------------------------------------- + // init + // ---------------------------------------------------------------- + void init(InitContext const&) + { + const AxisSpec axisZvtx{100, -15., 15., "z_{vtx} (cm)"}; + const AxisSpec axisMult{100, 0., 180., "N_{ch}"}; + const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi{60, 0., o2::constants::math::TwoPI, "#phi"}; + const AxisSpec axisDphi{30, -o2::constants::math::PIHalf, o2::constants::math::PI + o2::constants::math::PIHalf, "#Delta#phi"}; + const AxisSpec axisDeta{32, -1.6, 1.6, "#Delta#eta"}; + // Reserved for a possible rapidity-differentiated analysis -- unused + // while the PID correlations run pseudorapidity-only. + [[maybe_unused]] const AxisSpec axisDy{32, -1.6, 1.6, "#Delta y"}; + const AxisSpec axisPt{nBinsPt, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec axisSph{100, 0., 1., "S_{T}"}; + const AxisSpec axisPt2{nBinsPt2, 0., 5., "p_{T} (GeV/c)"}; + + // ---------------------------------------------------------------- + // Common lambda: books a complete set of histograms for one + // "channel" (MC or Reco), with all names suffixed accordingly. + // Takes references to all channel-specific members. + // ---------------------------------------------------------------- + auto bookChannel = [&](const char* suf, + std::array, NMultBins>, NSphClasses>& hSE, + std::array, NMultBins>, NSphClasses>& hME, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hSEpid, + std::array, NMultBins>, NSphClasses>, NPidCorr>& hMEpid, + std::shared_ptr& hNtrig, + std::array, NPidCorr>& hNtrigPID, + std::array, NMultBins>& hSphMult) { + // QA + auto hQA = histos.add(Form("evSel_%s", suf), Form("Event selection (%s)", suf), HistType::kTH1D, {{NEventCuts, HalfBin, static_cast(NEventCuts) + HalfBin}}); + hQA->GetXaxis()->SetBinLabel(EventCutRead, "Events read"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtx, "|z_{vtx}|<10"); + hQA->GetXaxis()->SetBinLabel(EventCutMultBin, "multBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutZvtxBin, "zvtxBin OK"); + hQA->GetXaxis()->SetBinLabel(EventCutLeadPt, "leadPt #in [min,max]"); + hQA->GetXaxis()->SetBinLabel(EventCutSphericity, "S_{T} valid"); + hQA->GetXaxis()->SetBinLabel(EventCutSphClass, "sphClass OK"); + hQA->GetXaxis()->SetBinLabel(EventCutSeFilled, "SE filled"); + + histos.add(Form("Zvertex_%s", suf), Form("z_{vtx} (%s);z_{vtx} (cm);events", suf), kTH1D, {axisZvtx}); + histos.add(Form("multHist_%s", suf), Form("N_{ch} (%s);N_{ch};events", suf), kTH1D, {axisMult}); + histos.add(Form("etaHistogram_%s", suf), Form("#eta (%s);#eta;tracks", suf), kTH1D, {axisEta}); + histos.add(Form("phiHistogram_%s", suf), Form("#phi (%s);#phi;tracks", suf), kTH1D, {axisPhi}); + histos.add(Form("ptHistogram_%s", suf), Form("p_{T} (%s);p_{T} (GeV/c);tracks", suf), kTH1D, {axisPt}); + histos.add(Form("ptLeadHistogram_%s", suf), Form("p_{T} lead (%s);p_{T} (GeV/c);events", suf), kTH1D, {axisPt}); + histos.add(Form("ptAssocHistogram_%s", suf), Form("p_{T} assoc (%s);p_{T} (GeV/c);pairs", suf), kTH1D, {axisPt}); + histos.add(Form("sphericity_%s", suf), Form("S_{T} (inclusive, %s);S_{T};events", suf), kTH1D, {axisSph}); + histos.add(Form("sphericity_beforeCuts_%s", suf), Form("S_{T} (all evts with >=3 tracks, %s);S_{T};events", suf), kTH1D, {axisSph}); + + // Per-mult sphericity + for (int im = 0; im < NMultBins; ++im) { + hSphMult[im] = histos.add( + Form("sphericity_mult%d_%s", im, suf), + Form("S_T [%s] (%s);S_{T};events", multLabel(im).c_str(), suf), + HistType::kTH1D, {axisSph}); + } + + // Trigger counter TH2: x = multBin, y = sphClass + hNtrig = histos.add( + Form("hNtrig%s", suf), + Form("N_{trig} (%s);mult bin;sph class", suf), + HistType::kTH2D, + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrig->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrig->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); + } + + // SE/ME TH2D: [sphClass][multBin] + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSE[ic][im] = histos.add( + Form("hSE%s_sph%d_mult%d", suf, ic, im), + Form("SE [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hME[ic][im] = histos.add( + Form("hME%s_sph%d_mult%d", suf, ic, im), + Form("ME [%s][%s] (%s);#Delta#phi;#Delta#eta", SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + + // ---- SE/ME like-sign per PID species: [pidIdx][sphClass][multBin] --- + // PID correlations use Delta-eta, same as the inclusive hSE/hME + // histograms, NOT Delta-y. The rapidity infrastructure + // (computeRapidity(), the per-track y field, axisDy) is kept for a + // possible rapidity-differentiated analysis and is deliberately + // unused for now. + for (int ip = 0; ip < NPidCorr; ++ip) { + for (int ic = 0; ic < NSphClasses; ++ic) { + for (int im = 0; im < NMultBins; ++im) { + hSEpid[ip][ic][im] = histos.add( + Form("hSE%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("SE %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + hMEpid[ip][ic][im] = histos.add( + Form("hME%s_pid%d_sph%d_mult%d", suf, ip, ic, im), + Form("ME %s--%s [%s][%s] (%s);#Delta#varphi;#Delta#eta", + PidCorrNames[ip], PidCorrNames[ip], SphLabels[ic], multLabel(im).c_str(), suf), + HistType::kTH2D, {axisDphi, axisDeta}); + } + } + // Trigger counter per PID species + hNtrigPID[ip] = histos.add( + Form("hNtrig%s_pid%d", suf, ip), + Form("N_{trig} %s (%s);mult bin;sph class", PidCorrNames[ip], suf), + HistType::kTH2D, + {{NMultBins, -HalfBin, static_cast(NMultBins) - HalfBin}, + {NSphClasses, -HalfBin, static_cast(NSphClasses) - HalfBin}}); + for (int im = 0; im < NMultBins; ++im) { + hNtrigPID[ip]->GetXaxis()->SetBinLabel(im + 1, multLabel(im).c_str()); + } + for (int ic = 0; ic < NSphClasses; ++ic) { + hNtrigPID[ip]->GetYaxis()->SetBinLabel(ic + 1, SphLabels[ic]); + } + } + }; + + // Book histograms for MC (generated) channel + bookChannel("MC", + hSeMC, hMeMC, hSePidMC, hMePidMC, + hNtrigMC, hNtrigPidMC, hSphMultMC); + + // Book histograms for Reco (reconstructed) channel + bookChannel("Reco", + hSeReco, hMeReco, hSePidReco, hMePidReco, + hNtrigReco, hNtrigPidReco, hSphMultReco); + + // [DEBUG, TEMPORARY] track-cut diagnostic counter for processReco + hTrackCutDebugReco = histos.add( + "hTrackCutDebug_Reco", + "Track cut flow (Reco, DEBUG);cut;tracks", + HistType::kTH1D, {{NTrackCuts, HalfBin, static_cast(NTrackCuts) + HalfBin}}); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutSeen, "seen"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutGlobal, "passed isGlobalTrackWoDCA"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaXY, "passed DCAxy"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutDcaZ, "passed DCAz"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutEta, "passed eta"); + hTrackCutDebugReco->GetXaxis()->SetBinLabel(TrackCutPt, "passed pt (selected)"); + + // PID confusion matrix (contamination estimate) -- + // axes: pT, true species (PDG), tagged species (pidFromNSigma). + // Species axis order matches PidSpecies: 0=unid,1=pion,2=kaon,3=proton. + const AxisSpec axisPidSpecies{static_cast(NPidSpecies), -HalfBin, static_cast(NPidSpecies) - HalfBin, "species"}; + hPidConfusionMC = histos.add( + "hPidConfusion_MC", + "True vs. tagged PID species (MC);p_{T} (GeV/c);true species;tagged species", + HistType::kTH3D, {axisPt2, axisPidSpecies, axisPidSpecies}); + for (int ip = 0; ip < static_cast(NPidSpecies); ++ip) { + hPidConfusionMC->GetYaxis()->SetBinLabel(ip + 1, PidNames[ip]); + hPidConfusionMC->GetZaxis()->SetBinLabel(ip + 1, PidNames[ip]); + } + } + + // ---------------------------------------------------------------- + // processMC + // ---------------------------------------------------------------- + void processMC(aod::McCollision const& mcCollision, + aod::McParticles const& mcParticles) + { + histos.fill(HIST("evSel_MC"), EventCutRead); + + const double zvtx = mcCollision.posZ(); + if (std::fabs(zvtx) > maxZvtx) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutZvtx); + histos.fill(HIST("Zvertex_MC"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(MultEdges.back() - 1); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0.; + + for (const auto& p : mcParticles) { + if (!p.isPhysicalPrimary()) { + continue; + } + auto* pdgPtr = pdg->GetParticle(p.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { + continue; + } + if (std::fabs(p.eta()) > maxEta) { + continue; + } + if (p.pt() < minAssocPt) { + continue; + } + + double mass = pdgPtr->Mass(); + double rap = computeRapidity(p.pt(), p.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species + // (pion/kaon/proton) -- the all-species channel (hSE/hME) keeps the + // track regardless, gated only by |eta| < maxEta above. Clean way to + // do this: keep pdg for all-species bookkeeping, but zero it (-> + // PidUnidentified via pidFromPDG) when the rapidity cut fails, so the + // track contributes to hSE/hME but not hSEpid/hMEpid. + int pdgForPID = p.pdgCode(); + if (applyRapidityCutPID && pidFromPDG(pdgForPID) != PidUnidentified && std::fabs(rap) > maxRapPID) { + pdgForPID = 0; + } + + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(p.phi()), static_cast(p.eta()), static_cast(p.pt()), pdgForPID}); + + histos.fill(HIST("etaHistogram_MC"), p.eta()); + histos.fill(HIST("phiHistogram_MC"), p.phi()); + histos.fill(HIST("ptHistogram_MC"), p.pt()); + + if (p.pt() > pTlead) { + pTlead = p.pt(); + phiLead = p.phi(); + etaLead = p.eta(); + leadIdx = idx; + } + } + + const int nch = static_cast(selTracks.size()); + histos.fill(HIST("multHist_MC"), nch); + + const int mBin = o2::analysis::findBin(&MultEdges, nch); + if (mBin < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutMultBin); + + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); + if (zBin < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutZvtxBin); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) { + return; + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutLeadPt); + histos.fill(HIST("ptLeadHistogram_MC"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + // Fill sphericity_beforeCuts here — no additional cuts applied yet + if (sph >= 0.) { + histos.fill(HIST("sphericity_beforeCuts_MC"), sph); + } + if (sph < 0.) { + return; // fewer than 3 tracks + } + histos.fill(HIST("evSel_MC"), EventCutSphericity); + histos.fill(HIST("sphericity_MC"), sph); + hSphMultMC[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); // 0,1,2 or -1 + if (sphCls < 0) { + return; + } + histos.fill(HIST("evSel_MC"), EventCutSphClass); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrigMC->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigMC->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); // "all" + + // ================================================================ + // SAME-EVENT correlations + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) { + continue; + } + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) { + continue; // associated pT < leading pT + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { + continue; + } + + histos.fill(HIST("ptAssocHistogram_MC"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); + + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - assoc.eta; + + // TH2D SE: specific sph class (uses Delta-eta) + hSeMC[sphCls][mBin]->Fill(dphi, deta); + // TH2D SE: "all" class (uses Delta-eta) + hSeMC[NSphClasses - 1][mBin]->Fill(dphi, deta); + + // SE like-sign: fill only if trigger and associated are the same species + // Uses Delta-eta (pseudorapidity-only regime) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSePidMC[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidMC[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) { + histos.fill(HIST("evSel_MC"), EventCutSeFilled); + } + + // ---- hNtrigMC per PID species leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidMC[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + } + + // ================================================================ + // Update pools AFTER SE + // ================================================================ + // Pool "all" — specific sph + { + auto& pool = eventPoolsMC[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + // Pool "all" — all sph + { + auto& poolAll = eventPoolsMC[NSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if (static_cast(poolAll.size()) > mixPoolSize) { + poolAll.pop_front(); + } + } + + // Pool per PID species — contains only the tracks of that species + for (int ip = 0; ip < NPidCorr; ++ip) { + // Build a vector with only the tracks of species ip + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { + pidTracks.push_back(t); + } + } + if (pidTracks.empty()) { + continue; + } + // Specific sph pool + { + auto& pool = eventPoolsPidMC[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + // "all" sph pool + { + auto& pool = eventPoolsPidMC[ip][NSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + } + + // ================================================================ + // MIXED-EVENT "all" + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPoolsMC[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - tr.eta; + hMeMC[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(NSphClasses - 1); + + // ================================================================ + // MIXED-EVENT like-sign per PID species + // Trigger = leading track of species pidLeadIdx + // Associated = tracks of the same species from the PID pool + // Uses Delta-eta (pseudorapidity-only regime) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPidMC[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double detaPID = etaLead - tr.eta; + hMePidMC[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(NSphClasses - 1); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMC, "Process MC truth", true); + + // ================================================================ + // processReco — reconstructed data (Collision + Tracks) + // ================================================================ + using ColReco = soa::Join; + using TrackReco = soa::Join; + + void processReco(ColReco::iterator const& col, + TrackReco const& tracks) + { + histos.fill(HIST("evSel_Reco"), EventCutRead); + + // ---- event selection ---- + if (!col.sel8()) { + return; + } + const double zvtx = col.posZ(); + if (std::fabs(zvtx) > maxZvtx) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutZvtx); + histos.fill(HIST("Zvertex_Reco"), zvtx); + + // ---- one-pass: build selTracks + find leading --------------- + std::vector selTracks; + selTracks.reserve(MultEdges.back() - 1); + int leadIdx = -1; + double pTlead = -1., phiLead = 0., etaLead = 0.; + + for (const auto& track : tracks) { + hTrackCutDebugReco->Fill(TrackCutSeen); // seen + + // Quality selection: global track without DCA (apply DCA separately) + if (!track.isGlobalTrackWoDCA()) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutGlobal); // passed isGlobalTrackWoDCA + + // DCA cuts + if (std::fabs(track.dcaXY()) > maxDcaXY) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutDcaXY); // passed DCAxy + if (std::fabs(track.dcaZ()) > maxDcaZ) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutDcaZ); // passed DCAz + + // Kinematic cuts (same as MC) + if (std::fabs(track.eta()) > maxEta) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutEta); // passed eta + if (track.pt() < minAssocPt) { + continue; + } + hTrackCutDebugReco->Fill(TrackCutPt); // passed pt (selected) + + // PID from nSigma (combined TPC+TOF, ALICE 2511.10399, unless the + // legacy flags below switch to legacy gating/selection) + PidSpecies pid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + double mass = pidMass(pid); + double rap = computeRapidity(track.pt(), track.eta(), mass); + + // |y| < maxRapPID cut applies ONLY to PID-identified species -- see + // matching comment in processMC. Track stays in selTracks either way + // (all-species channel unaffected); only the PID assignment is reset. + if (applyRapidityCutPID && pid != PidUnidentified && std::fabs(rap) > maxRapPID) { + pid = PidUnidentified; + } + + // pdg = 0 for reco (no MC truth); store sign for future like/unlike-sign + int idx = static_cast(selTracks.size()); + selTracks.push_back({static_cast(track.phi()), static_cast(track.eta()), static_cast(track.pt()), 0}); + // Store PID info in pdg field as signed pidIdx for later use: + // sign(track.sign()) * (211 for pion, 321 for kaon, 2212 for proton, 0 for unid) + const int pidIdx = pidSpeciesToIdx(pid); + const int pdgLike = pidIdx >= 0 ? pidCorrPDG(pidIdx) : 0; + selTracks.back().pdg = track.sign() * pdgLike; + + histos.fill(HIST("etaHistogram_Reco"), track.eta()); + histos.fill(HIST("phiHistogram_Reco"), track.phi()); + histos.fill(HIST("ptHistogram_Reco"), track.pt()); + + if (track.pt() > pTlead) { + pTlead = track.pt(); + phiLead = track.phi(); + etaLead = track.eta(); + leadIdx = idx; + } + } + + const int nch = static_cast(selTracks.size()); + histos.fill(HIST("multHist_Reco"), nch); + + const int mBin = o2::analysis::findBin(&MultEdges, nch); + if (mBin < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutMultBin); + + const int zBin = o2::analysis::findBin(&ZvtxEdges, zvtx); + if (zBin < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutZvtxBin); + + // ---- leading particle pT cut [minLeadPt, maxLeadPt] ----------- + if (leadIdx < 0) { + return; + } + if (pTlead < minLeadPt || pTlead > maxLeadPt) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutLeadPt); + histos.fill(HIST("ptLeadHistogram_Reco"), pTlead); + + // ---- sphericity --------------------------------------------- + const double sph = computeSphericity(selTracks); + if (sph >= 0.) { + histos.fill(HIST("sphericity_beforeCuts_Reco"), sph); + } + if (sph < 0.) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutSphericity); + histos.fill(HIST("sphericity_Reco"), sph); + hSphMultReco[mBin]->Fill(sph); + + const int sphCls = sphClass(sph); + if (sphCls < 0) { + return; + } + histos.fill(HIST("evSel_Reco"), EventCutSphClass); + + const PidSpecies pidLead = pidFromPDG(selTracks[leadIdx].pdg); + + // ---- trigger counter ---------------------------------------- + hNtrigReco->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigReco->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + + // ================================================================ + // SAME-EVENT correlations (identical logic to processMC) + // ================================================================ + bool seHasPairs = false; + for (int ia = 0; ia < nch; ++ia) { + if (ia == leadIdx) { + continue; + } + const auto& assoc = selTracks[ia]; + if (assoc.pt >= pTlead) { + continue; + } + if (assoc.pt < minAssocCorr || assoc.pt > maxAssocCorr) { + continue; + } + + histos.fill(HIST("ptAssocHistogram_Reco"), assoc.pt); + + const PidSpecies pidAssoc = pidFromPDG(assoc.pdg); + + const double dphi = RecoDecay::constrainAngle(phiLead - assoc.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - assoc.eta; + + // All-species SE (Delta-eta) + hSeReco[sphCls][mBin]->Fill(dphi, deta); + hSeReco[NSphClasses - 1][mBin]->Fill(dphi, deta); + + // PID SE — same species, Delta-eta (pseudorapidity-only regime) + int pidLeadIdx = pidSpeciesToIdx(pidLead); + int pidAssocIdx = pidSpeciesToIdx(pidAssoc); + if (pidLeadIdx >= 0 && pidLeadIdx == pidAssocIdx) { + hSePidReco[pidLeadIdx][sphCls][mBin]->Fill(dphi, deta); + hSePidReco[pidLeadIdx][NSphClasses - 1][mBin]->Fill(dphi, deta); + } + + seHasPairs = true; + } + if (seHasPairs) { + histos.fill(HIST("evSel_Reco"), EventCutSeFilled); + } + + // ---- hNtrigReco per PID species leading -------------------------- + int pidLeadIdx = pidSpeciesToIdx(pidLead); + if (pidLeadIdx >= 0) { + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast(sphCls)); + hNtrigPidReco[pidLeadIdx]->Fill(static_cast(mBin), static_cast((NSphClasses - 1))); + } + + // ================================================================ + // Update pools AFTER SE (identical to processMC) + // ================================================================ + { + auto& pool = eventPoolsReco[sphCls][zBin][mBin]; + pool.push_back(selTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + { + auto& poolAll = eventPoolsReco[NSphClasses - 1][zBin][mBin]; + poolAll.push_back(selTracks); + if (static_cast(poolAll.size()) > mixPoolSize) { + poolAll.pop_front(); + } + } + + for (int ip = 0; ip < NPidCorr; ++ip) { + std::vector pidTracks; + for (const auto& t : selTracks) { + if (pidSpeciesToIdx(pidFromPDG(t.pdg)) == ip) { + pidTracks.push_back(t); + } + } + if (pidTracks.empty()) { + continue; + } + { + auto& pool = eventPoolsPidReco[ip][sphCls][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + { + auto& pool = eventPoolsPidReco[ip][NSphClasses - 1][zBin][mBin]; + pool.push_back(pidTracks); + if (static_cast(pool.size()) > mixPoolSize) { + pool.pop_front(); + } + } + } + + // ================================================================ + // MIXED-EVENT "all" (Delta-eta) + // ================================================================ + auto doMixing = [&](int poolIdx) { + const auto& pool = eventPoolsReco[poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double deta = etaLead - tr.eta; + hMeReco[poolIdx][mBin]->Fill(dphi, deta); + } + } + }; + + doMixing(sphCls); + doMixing(NSphClasses - 1); + + // ================================================================ + // MIXED-EVENT PID (Delta-eta, pseudorapidity-only regime) + // ================================================================ + if (pidLeadIdx >= 0) { + auto doMixingPID = [&](int poolIdx) { + const auto& pool = eventPoolsPidReco[pidLeadIdx][poolIdx][zBin][mBin]; + const int nEvts = static_cast(pool.size()) - 1; + for (int ie = 0; ie < nEvts; ++ie) { + for (const auto& tr : pool[ie]) { + if (tr.pt >= pTlead) { + continue; + } + if (tr.pt < minAssocCorr || tr.pt > maxAssocCorr) { + continue; + } + const double dphi = RecoDecay::constrainAngle(phiLead - tr.phi, -o2::constants::math::PIHalf); + const double detaPID = etaLead - tr.eta; + hMePidReco[pidLeadIdx][poolIdx][mBin]->Fill(dphi, detaPID); + } + } + }; + doMixingPID(sphCls); + doMixingPID(NSphClasses - 1); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processReco, "Process reconstructed data", false); + + // ---------------------------------------------------------------- + // processMCConfusion -- PID contamination estimate. Fills hPidConfusionMC (pT, true species, tagged + // species) on MC only -- needs BOTH the truth PDG (via McTrackLabels) + // AND the reconstructed track's nSigma values on the SAME track, + // which neither processMC (truth only) nor processReco (no truth + // link) has access to, hence a dedicated process function. Reports + // contamination as a systematic; does NOT correct anything (no + // unfolding is implemented). Off by default, like the other MC-only diagnostics. + // ---------------------------------------------------------------- + using TrackRecoMC = soa::Join; + + void processMCConfusion(ColReco::iterator const& col, + TrackRecoMC const& tracks, + aod::McParticles const&) + { + if (!col.sel8()) { + return; + } + if (std::fabs(col.posZ()) > maxZvtx) { + return; + } + + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + auto mcPart = track.mcParticle(); + if (!mcPart.isPhysicalPrimary()) { + continue; + } + auto* pdgPtr = pdg->GetParticle(mcPart.pdgCode()); + if (!pdgPtr || std::fabs(pdgPtr->Charge()) < MinCharge) { + continue; + } + + // Same track-level cuts as processReco, so the matrix reflects the + // actual analyzed track population, not an unfiltered sample. + if (std::fabs(track.dcaXY()) > maxDcaXY) { + continue; + } + if (std::fabs(track.dcaZ()) > maxDcaZ) { + continue; + } + if (std::fabs(track.eta()) > maxEta) { + continue; + } + if (track.pt() < minAssocPt) { + continue; + } + + PidSpecies truePid = pidFromPDG(mcPart.pdgCode()); + PidSpecies taggedPid = pidFromNSigma(track, maxNSigmaPID, maxNSigmaPIDAmbig, + maxNSigmaTPC, maxNSigmaTOF, + useLegacyNSigmaGating, useLegacyPIDSelection); + + // Same rapidity cut as processReco applies to its "tagged" PID -- + // without this, applyRapidityCutPID=false would silently have zero + // effect on this matrix even though it changes the processReco yields. + if (applyRapidityCutPID && taggedPid != PidUnidentified) { + double tagMass = pidMass(taggedPid); + double tagRap = computeRapidity(track.pt(), track.eta(), tagMass); + if (std::fabs(tagRap) > maxRapPID) { + taggedPid = PidUnidentified; + } + } + + histos.fill(HIST("hPidConfusion_MC"), track.pt(), static_cast(truePid), static_cast(taggedPid)); + } + } + PROCESS_SWITCH(TwoParticleCorrelationsMultSpher, processMCConfusion, "Rapid PID contamination estimate: true (PDG) vs tagged (nSigma) species confusion matrix, MC only", false); + +}; // end struct + +// ---------------------------------------------------------------- +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGDQ/Core/AnalysisCompositeCut.cxx b/PWGDQ/Core/AnalysisCompositeCut.cxx index cbf0f328283..8be1abf6e60 100644 --- a/PWGDQ/Core/AnalysisCompositeCut.cxx +++ b/PWGDQ/Core/AnalysisCompositeCut.cxx @@ -13,17 +13,13 @@ #include "AnalysisCut.h" -#include - #include -ClassImp(AnalysisCompositeCut) - - //____________________________________________________________________________ - AnalysisCompositeCut::AnalysisCompositeCut(bool useAND) : AnalysisCut(), - fOptionUseAND(useAND), - fCutList(), - fCompositeCutList() +//____________________________________________________________________________ +AnalysisCompositeCut::AnalysisCompositeCut(bool useAND) : AnalysisCut(), + fOptionUseAND(useAND), + fCutList(), + fCompositeCutList() { // // default constructor diff --git a/PWGDQ/Core/AnalysisCompositeCut.h b/PWGDQ/Core/AnalysisCompositeCut.h index a6b3f6afda4..a9482c3cb91 100644 --- a/PWGDQ/Core/AnalysisCompositeCut.h +++ b/PWGDQ/Core/AnalysisCompositeCut.h @@ -19,7 +19,6 @@ #include "PWGDQ/Core/AnalysisCut.h" -#include #include #include @@ -28,7 +27,7 @@ class AnalysisCompositeCut : public AnalysisCut { public: - AnalysisCompositeCut(bool useAND = kTRUE); + explicit AnalysisCompositeCut(bool useAND = kTRUE); AnalysisCompositeCut(const char* name, const char* title, bool useAND = kTRUE); AnalysisCompositeCut(const AnalysisCompositeCut& c); AnalysisCompositeCut& operator=(const AnalysisCompositeCut& c); @@ -36,8 +35,8 @@ class AnalysisCompositeCut : public AnalysisCut void AddCut(AnalysisCut* cut) { - if (cut->IsA() == AnalysisCompositeCut::Class()) { - fCompositeCutList.push_back(*(AnalysisCompositeCut*)cut); + if (auto* composite = dynamic_cast(cut)) { + fCompositeCutList.push_back(*composite); } else { fCutList.push_back(*cut); } @@ -52,8 +51,6 @@ class AnalysisCompositeCut : public AnalysisCut bool fOptionUseAND; // true (default): apply AND on all cuts; false: use OR std::vector fCutList; // list of cuts std::vector fCompositeCutList; // list of composite cuts - - ClassDef(AnalysisCompositeCut, 2); }; #endif // PWGDQ_CORE_ANALYSISCOMPOSITECUT_H_ diff --git a/PWGDQ/Core/AnalysisCut.cxx b/PWGDQ/Core/AnalysisCut.cxx index a1dae4ce6b0..63c67d1a94f 100644 --- a/PWGDQ/Core/AnalysisCut.cxx +++ b/PWGDQ/Core/AnalysisCut.cxx @@ -21,8 +21,6 @@ using std::cout; using std::endl; -ClassImp(AnalysisCut); - std::vector AnalysisCut::fgUsedVars = {}; //____________________________________________________________________________ diff --git a/PWGDQ/Core/AnalysisCut.h b/PWGDQ/Core/AnalysisCut.h index 16f2916fe93..bb06c423182 100644 --- a/PWGDQ/Core/AnalysisCut.h +++ b/PWGDQ/Core/AnalysisCut.h @@ -9,9 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// -// Class for analysis cuts applied on the variables defined in the VarManager +/// \author Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +/// \file AnalysisCut.h +/// \brief Class for analysis cuts applied on the variables defined in the VarManager // #ifndef PWGDQ_CORE_ANALYSISCUT_H_ @@ -22,6 +22,8 @@ #include +#include +#include #include //_________________________________________________________________________ @@ -49,29 +51,27 @@ class AnalysisCut : public TNamed void PrintCuts(); struct CutContainer { - short fVar; // variable to be cut upon + int16_t fVar; // variable to be cut upon float fLow; // lower limit for the var float fHigh; // upper limit for the var bool fExclude; // if true, use the selection range for exclusion - short fDepVar; // first (optional) variable on which the cut depends + int16_t fDepVar; // first (optional) variable on which the cut depends float fDepLow; // lower limit for the first dependent var float fDepHigh; // upper limit for the first dependent var bool fDepExclude; // if true, then use the dependent variable range as exclusion - short fDepVar2; // second (optional) variable on which the cut depends + int16_t fDepVar2; // second (optional) variable on which the cut depends float fDep2Low; // lower limit for the second dependent var float fDep2High; // upper limit for the second dependent var bool fDep2Exclude; // if true, then use the dependent variable range as exclusion - TF1* fFuncLow; // function for the lower limit cut - TF1* fFuncHigh; // function for the upper limit cut + std::shared_ptr fFuncLow; // function for the lower limit cut + std::shared_ptr fFuncHigh; // function for the upper limit cut }; protected: std::vector fCuts; - - ClassDef(AnalysisCut, 1); }; //____________________________________________________________________________ @@ -85,7 +85,7 @@ void AnalysisCut::AddCut(int var, T1 cutLow, T2 cutHigh, bool exclude, // CutContainer cut = {}; - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v>) { if (dependentVar < 0) { return; } @@ -95,7 +95,7 @@ void AnalysisCut::AddCut(int var, T1 cutLow, T2 cutHigh, bool exclude, cut.fFuncLow = nullptr; cut.fLow = cutLow; } - if constexpr (std::is_same_v) { + if constexpr (std::is_same_v>) { if (dependentVar < 0) { return; } diff --git a/PWGDQ/Core/CMakeLists.txt b/PWGDQ/Core/CMakeLists.txt index 41ceb661bf9..265d1c76be2 100644 --- a/PWGDQ/Core/CMakeLists.txt +++ b/PWGDQ/Core/CMakeLists.txt @@ -23,16 +23,40 @@ o2physics_add_library(PWGDQCore MCSignal.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2::GlobalTracking O2Physics::AnalysisCore KFParticle::KFParticle O2Physics::MLCore) -o2physics_target_root_dictionary(PWGDQCore - HEADERS AnalysisCut.h - AnalysisCompositeCut.h - VarManager.h - HistogramManager.h - CutsLibrary.h - MixingHandler.h - MixingLibrary.h - HistogramsLibrary.h - MCProng.h - MCSignal.h - MCSignalLibrary.h - LINKDEF PWGDQCoreLinkDef.h) +# Precompiled header shared across PWGDQ. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# VarManager.h alone is ~8000 lines and is parsed by two thirds of the group. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(DQAnalysisPCH OBJECT dqAnalysisPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET DQAnalysisPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(DQAnalysisPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(DQAnalysisPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGDQ/DataModel/ReducedInfoTables.h"]] # 65% + [["PWGDQ/Core/VarManager.h"]] # 65%, ~8000 lines + ) +endif() diff --git a/PWGDQ/Core/CutsLibrary.cxx b/PWGDQ/Core/CutsLibrary.cxx index 5c0a0a67de9..467b4888b0e 100644 --- a/PWGDQ/Core/CutsLibrary.cxx +++ b/PWGDQ/Core/CutsLibrary.cxx @@ -9,8 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// +/// \author Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +/// \file CutsLibrary.cxxx +/// \brief Library with cuts to use + #include "PWGDQ/Core/CutsLibrary.h" #include "AnalysisCompositeCut.h" @@ -29,7 +31,7 @@ #include #include -#include +#include #include #include #include @@ -43,7 +45,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // TODO: Agree on some conventions for the naming // Think of possible customization of the predefined cuts via names - AnalysisCompositeCut* cut = new AnalysisCompositeCut(cutName, cutName); + auto* cut = new AnalysisCompositeCut(cutName, cutName); std::string nameStr = cutName; // /////////////////////////////////////////////// @@ -52,30 +54,30 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // // // see CutsLubrary.h for the description // // /////////////////////////////////////////////// - if (!nameStr.compare("Electron2022")) { + if (nameStr == "Electron2022") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug5")); return cut; } - if (!nameStr.compare("Electron2023")) { + if (nameStr == "Electron2023") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug5_noCorr")); return cut; } - if (!nameStr.compare("Electron2025_1")) { - AnalysisCut* kineCut = new AnalysisCut("kineCut", "kine cut"); + if (nameStr == "Electron2025_1") { + auto* kineCut = new AnalysisCut("kineCut", "kine cut"); kineCut->AddCut(VarManager::kP, 1.0, 1000.0); kineCut->AddCut(VarManager::kEta, -0.9, 0.9); - AnalysisCut* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); + auto* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); qualityCuts->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); qualityCuts->AddCut(VarManager::kTPCchi2, 0.0, 4.0); qualityCuts->AddCut(VarManager::kTPCncls, 70, 161.); qualityCuts->AddCut(VarManager::kTrackDCAz, -0.5, 0.5); - AnalysisCut* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); + auto* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -3.0, 4.0, false, VarManager::kPin, 0.0, 5.0); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -1.5, 4.0, false, VarManager::kPin, 5.0, 1000.0); pidCuts->AddCut(VarManager::kTPCnSigmaPi, 2.5, 999, false, VarManager::kPin, 0.0, 5.0); @@ -87,18 +89,18 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("Electron2025_2")) { - AnalysisCut* kineCut = new AnalysisCut("kineCut", "kine cut"); + if (nameStr == "Electron2025_2") { + auto* kineCut = new AnalysisCut("kineCut", "kine cut"); kineCut->AddCut(VarManager::kP, 1.0, 1000.0); kineCut->AddCut(VarManager::kEta, -0.9, 0.9); - AnalysisCut* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); + auto* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); qualityCuts->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); qualityCuts->AddCut(VarManager::kTPCchi2, 0.0, 4.0); qualityCuts->AddCut(VarManager::kTPCncls, 70, 161.); qualityCuts->AddCut(VarManager::kTrackDCAz, -0.5, 0.5); - AnalysisCut* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); + auto* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -3.0, 4.0, false, VarManager::kPin, 0.0, 5.0); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -2.0, 4.0, false, VarManager::kPin, 5.0, 1000.0); pidCuts->AddCut(VarManager::kTPCnSigmaPi, 2.0, 999, false, VarManager::kPin, 0.0, 5.0); @@ -110,18 +112,18 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("Electron2025_3")) { - AnalysisCut* kineCut = new AnalysisCut("kineCut", "kine cut"); + if (nameStr == "Electron2025_3") { + auto* kineCut = new AnalysisCut("kineCut", "kine cut"); kineCut->AddCut(VarManager::kP, 1.0, 1000.0); kineCut->AddCut(VarManager::kEta, -0.9, 0.9); - AnalysisCut* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); + auto* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); qualityCuts->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); qualityCuts->AddCut(VarManager::kTPCchi2, 0.0, 4.0); qualityCuts->AddCut(VarManager::kTPCncls, 70, 161.); qualityCuts->AddCut(VarManager::kTrackDCAz, -0.5, 0.5); - AnalysisCut* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); + auto* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -2.5, 4.0, false, VarManager::kPin, 0.0, 5.0); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -2.0, 4.0, false, VarManager::kPin, 5.0, 1000.0); pidCuts->AddCut(VarManager::kTPCnSigmaPr, 2.5, 999, false, VarManager::kPin, 0.0, 5.0); @@ -132,18 +134,18 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("Electron2025_4")) { - AnalysisCut* kineCut = new AnalysisCut("kineCut", "kine cut"); + if (nameStr == "Electron2025_4") { + auto* kineCut = new AnalysisCut("kineCut", "kine cut"); kineCut->AddCut(VarManager::kP, 1.0, 1000.0); kineCut->AddCut(VarManager::kEta, -0.9, 0.9); - AnalysisCut* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); + auto* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); qualityCuts->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); qualityCuts->AddCut(VarManager::kTPCchi2, 0.0, 4.0); qualityCuts->AddCut(VarManager::kTPCncls, 70, 161.); qualityCuts->AddCut(VarManager::kTrackDCAz, -0.5, 0.5); - AnalysisCut* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); + auto* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -2.5, 4.0, false, VarManager::kPin, 0.0, 5.0); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -1.5, 4.0, false, VarManager::kPin, 5.0, 1000.0); pidCuts->AddCut(VarManager::kTPCnSigmaPi, 2.5, 999, false, VarManager::kPin, 0.0, 5.0); @@ -155,18 +157,18 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("Electron2025_5")) { - AnalysisCut* kineCut = new AnalysisCut("kineCut", "kine cut"); + if (nameStr == "Electron2025_5") { + auto* kineCut = new AnalysisCut("kineCut", "kine cut"); kineCut->AddCut(VarManager::kP, 1.0, 1000.0); kineCut->AddCut(VarManager::kEta, -0.9, 0.9); - AnalysisCut* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); + auto* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); qualityCuts->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); qualityCuts->AddCut(VarManager::kTPCchi2, 0.0, 4.0); qualityCuts->AddCut(VarManager::kTPCncls, 70, 161.); qualityCuts->AddCut(VarManager::kTrackDCAz, -0.5, 0.5); - AnalysisCut* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); + auto* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -2.25, 4.0, false, VarManager::kPin, 0.0, 5.0); pidCuts->AddCut(VarManager::kTPCnSigmaEl, -1.5, 4.0, false, VarManager::kPin, 5.0, 1000.0); pidCuts->AddCut(VarManager::kTPCnSigmaPi, 2.5, 999, false, VarManager::kPin, 0.0, 5.0); @@ -178,41 +180,69 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("LowMassElectron2023")) { + if (nameStr == "Electron2025_4_ldong") { + auto* kineCut = new AnalysisCut("kineCut", "kine cut"); + kineCut->AddCut(VarManager::kP, 1.0, 1000.0); + kineCut->AddCut(VarManager::kEta, -0.9, 0.9); + + auto* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); + qualityCuts->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); + qualityCuts->AddCut(VarManager::kIsSPDany, 0.5, 1.5); + qualityCuts->AddCut(VarManager::kITSchi2, 0.0, 5.0); + qualityCuts->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + qualityCuts->AddCut(VarManager::kTPCncls, 70, 161.); + qualityCuts->AddCut(VarManager::kTrackDCAz, -0.5, 0.5); + qualityCuts->AddCut(VarManager::kTrackDCAxy, -0.5, 0.5); + + auto* pidCuts = new AnalysisCut("pidCuts", "pid cuts"); + pidCuts->AddCut(VarManager::kTPCnSigmaEl, -2.5, 4.0, false, VarManager::kPin, 0.0, 5.0); + pidCuts->AddCut(VarManager::kTPCnSigmaEl, -1.5, 4.0, false, VarManager::kPin, 5.0, 1000.0); + pidCuts->AddCut(VarManager::kTPCnSigmaPi, 2.7, 999, false, VarManager::kPin, 0.0, 5.0); + pidCuts->AddCut(VarManager::kTPCnSigmaPi, 2.7, 999, false, VarManager::kPin, 5.0, 1000.0); + pidCuts->AddCut(VarManager::kTPCnSigmaPr, 3.0, 999, false, VarManager::kPin, 0.0, 5.0); + pidCuts->AddCut(VarManager::kTPCnSigmaPr, 2.7, 999, false, VarManager::kPin, 5.0, 1000.0); + + cut->AddCut(kineCut); + cut->AddCut(qualityCuts); + cut->AddCut(pidCuts); + return cut; + } + + if (nameStr == "LowMassElectron2023") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("LooseGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("lmee_pp_502TeV_TOFloose_pionrej")); return cut; } - if (!nameStr.compare("MuonLow2022")) { + if (nameStr == "MuonLow2022") { cut->AddCut(GetAnalysisCut("muonLowPt2")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("MuonHigh2022")) { + if (nameStr == "MuonHigh2022") { cut->AddCut(GetAnalysisCut("muonHighPt2")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("MuonLow2023")) { + if (nameStr == "MuonLow2023") { cut->AddCut(GetAnalysisCut("muonLowPt2")); cut->AddCut(GetAnalysisCut("muonQualityCuts10SigmaPDCA")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("MuonHigh2023")) { + if (nameStr == "MuonHigh2023") { cut->AddCut(GetAnalysisCut("muonHighPt6")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("ElectronForEMu")) { + if (nameStr == "ElectronForEMu") { cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaLoose")); return cut; } - if (!nameStr.compare("MuonForEMu")) { + if (nameStr == "MuonForEMu") { cut->AddCut(GetAnalysisCut("muonLowPt5")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); cut->AddCut(GetAnalysisCut("MCHMID")); @@ -222,14 +252,14 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // End of Cuts for CEFP // // /////////////////////////////////////////////// - if (!nameStr.compare("jpsiO2MCdebugCuts")) { + if (nameStr == "jpsiO2MCdebugCuts") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPID1")); return cut; } - if (!nameStr.compare("jpsiBenchmarkCuts")) { + if (nameStr == "jpsiBenchmarkCuts") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityBenchmark")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -237,21 +267,21 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts2")) { + if (nameStr == "jpsiO2MCdebugCuts2") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigma")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts2_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts2_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts2_prefiltered1")) { + if (nameStr == "jpsiO2MCdebugCuts2_prefiltered1") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigma")); @@ -259,41 +289,47 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts3")) { + if (nameStr == "jpsiO2MCdebugCuts3") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaMedium")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts4")) { + if (nameStr == "jpsiO2MCdebugCuts4") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaLoose")); return cut; } - if (!nameStr.compare("electronSelection1_ionut")) { + if (nameStr == "electronSelection1_ionut") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaMedium")); return cut; } - if (!nameStr.compare("jpsiKineDcaQualitynoPID")) { + if (nameStr == "jpsiKineDcaQualitynoPID") { cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); return cut; } - if (!nameStr.compare("electronSelection1_ionut_withTOFPID")) { + if (nameStr == "jpsiKineDcaQualitynoPID2") { + cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); + cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); + cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); + return cut; + } + if (nameStr == "electronSelection1_ionut_withTOFPID") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaMedium_withLargeTOFPID")); return cut; } - if (!nameStr.compare("electronSelection1_idstoreh")) { // same as electronSelection1_ionut, but with kIsSPDAny -> kIsITSibAny + if (nameStr == "electronSelection1_idstoreh") { // same as electronSelection1_ionut, but with kIsSPDAny -> kIsITSibAny cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); @@ -301,7 +337,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("electronSelection1pos_ionut")) { + if (nameStr == "electronSelection1pos_ionut") { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); @@ -309,7 +345,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) cut->AddCut(GetAnalysisCut("electronPIDnsigmaMedium")); return cut; } - if (!nameStr.compare("electronSelection1neg_ionut")) { + if (nameStr == "electronSelection1neg_ionut") { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); @@ -318,7 +354,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("electronSelection2_ionut")) { + if (nameStr == "electronSelection2_ionut") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); @@ -326,7 +362,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) cut->AddCut(GetAnalysisCut("insideTPCsector")); return cut; } - if (!nameStr.compare("electronSelection2pos_ionut")) { + if (nameStr == "electronSelection2pos_ionut") { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); @@ -335,7 +371,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) cut->AddCut(GetAnalysisCut("insideTPCsector")); return cut; } - if (!nameStr.compare("electronSelection2neg_ionut")) { + if (nameStr == "electronSelection2neg_ionut") { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); @@ -345,14 +381,14 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts4_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts4_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug1")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts5")) { + if (nameStr == "jpsiO2MCdebugCuts5") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaVeryLoose")); @@ -360,7 +396,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts6")) { + if (nameStr == "jpsiO2MCdebugCuts6") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaVeryVeryLoose")); @@ -368,7 +404,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts7")) { + if (nameStr == "jpsiO2MCdebugCuts7") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaOpen")); @@ -376,7 +412,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts7_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts7_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug5")); @@ -384,7 +420,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts7_noCorr")) { + if (nameStr == "jpsiO2MCdebugCuts7_noCorr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug5_noCorr")); @@ -392,7 +428,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts7_Corr_2")) { + if (nameStr == "jpsiO2MCdebugCuts7_Corr_2") { cut->AddCut(GetAnalysisCut("jpsiStandardKine2")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug5")); @@ -400,7 +436,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts7_Corr_3")) { + if (nameStr == "jpsiO2MCdebugCuts7_Corr_3") { cut->AddCut(GetAnalysisCut("jpsiStandardKine3")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug5")); @@ -408,28 +444,28 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts8")) { + if (nameStr == "jpsiO2MCdebugCuts8") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPID1shiftUp")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts9")) { + if (nameStr == "jpsiO2MCdebugCuts9") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPID1shiftDown")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts10_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts10_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); // no cut on ITS clusters cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts10_Corr_Amb")) { + if (nameStr == "jpsiO2MCdebugCuts10_Corr_Amb") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); // no cut on ITS clusters cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); @@ -437,133 +473,126 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts11_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts11_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug3")); // cut on 1 ITS cluster cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts12")) { + if (nameStr == "jpsiO2MCdebugCuts12") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); // no cut on ITS clusters cut->AddCut(GetAnalysisCut("electronPIDnsigmaVeryLoose")); // with 3 sigma El TOF return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts_Pdependent_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts_Pdependent_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine4")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("pidCut_lowP_Corr")); - AnalysisCompositeCut* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); + auto* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); pidCut_highP->AddCut(GetAnalysisCut("EleInclusion_highP_Corr")); pidCut_highP->AddCut(GetAnalysisCut("PionExclusion_highP_Corr")); cut->AddCut(pidCut_highP); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts_Pdependent")) { + if (nameStr == "jpsiO2MCdebugCuts_Pdependent") { cut->AddCut(GetAnalysisCut("jpsiStandardKine4")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("pidCut_lowP")); - AnalysisCompositeCut* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); + auto* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); pidCut_highP->AddCut(GetAnalysisCut("EleInclusion_highP")); pidCut_highP->AddCut(GetAnalysisCut("PionExclusion_highP")); cut->AddCut(pidCut_highP); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts_Pdependent2_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts_Pdependent2_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine4")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("pidCut_lowP_Corr")); - AnalysisCompositeCut* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); + auto* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); pidCut_highP->AddCut(GetAnalysisCut("EleInclusion_highP2_Corr")); pidCut_highP->AddCut(GetAnalysisCut("PionExclusion_highP_Corr")); cut->AddCut(pidCut_highP); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts_Pdependent2")) { + if (nameStr == "jpsiO2MCdebugCuts_Pdependent2") { cut->AddCut(GetAnalysisCut("jpsiStandardKine4")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("pidCut_lowP")); - AnalysisCompositeCut* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); + auto* pidCut_highP = new AnalysisCompositeCut("pidCut_highP", "pidCut_highP", kFALSE); pidCut_highP->AddCut(GetAnalysisCut("EleInclusion_highP2")); pidCut_highP->AddCut(GetAnalysisCut("PionExclusion_highP")); cut->AddCut(pidCut_highP); return cut; } - if (!nameStr.compare("JpsiPWGSkimmedCuts1")) { // please do not remove or modify, this is used for the common Skimmed tree production, (Xiaozhi Bai) - cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); - cut->AddCut(GetAnalysisCut("electronTrackQualitySkimmed")); - cut->AddCut(GetAnalysisCut("electronPIDLooseSkimmed")); - return cut; - } - - if (!nameStr.compare("JpsiPWGSkimmedCuts1")) { // please do not remove or modify, this is used for the common Skimmed tree production, (Xiaozhi Bai) + if (nameStr == "JpsiPWGSkimmedCuts1") { // please do not remove or modify, this is used for the common Skimmed tree production, (Xiaozhi Bai) cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); cut->AddCut(GetAnalysisCut("electronTrackQualitySkimmed")); cut->AddCut(GetAnalysisCut("electronPIDLooseSkimmed")); return cut; } - if (!nameStr.compare("JpsiPWGSkimmedCuts2")) { + if (nameStr == "JpsiPWGSkimmedCuts2") { cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); cut->AddCut(GetAnalysisCut("electronTrackQualitySkimmed")); cut->AddCut(GetAnalysisCut("electronPIDLooseSkimmed2")); return cut; } - if (!nameStr.compare("JpsiPWGSkimmedCuts3")) { + if (nameStr == "JpsiPWGSkimmedCuts3") { cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); cut->AddCut(GetAnalysisCut("electronTrackQualitySkimmed2")); cut->AddCut(GetAnalysisCut("electronPIDLooseSkimmed2")); return cut; } - if (!nameStr.compare("JpsiPWGSkimmedCuts4")) { + if (nameStr == "JpsiPWGSkimmedCuts4") { cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); cut->AddCut(GetAnalysisCut("electronTrackQualitySkimmed2")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug9")); // loose cut return cut; } - if (!nameStr.compare("JpsiPWGSkimmedCuts5")) { + if (nameStr == "JpsiPWGSkimmedCuts5") { cut->AddCut(GetAnalysisCut("electronTrackQualitySkimmed3")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug8")); return cut; } - if (!nameStr.compare("pidElectron_ionut")) { + if (nameStr == "pidElectron_ionut") { cut->AddCut(GetAnalysisCut("pidcalib_ele")); cut->AddCut(GetAnalysisCut("jpsiStandardKine3")); return cut; } - if (!nameStr.compare("pidElectron_ionut_posEta")) { + if (nameStr == "pidElectron_ionut_posEta") { cut->AddCut(GetAnalysisCut("pidcalib_ele")); cut->AddCut(GetAnalysisCut("jpsiPIDcalibKine_posEta")); return cut; } - if (!nameStr.compare("pidElectron_ionut_negEta")) { + if (nameStr == "pidElectron_ionut_negEta") { cut->AddCut(GetAnalysisCut("pidcalib_ele")); cut->AddCut(GetAnalysisCut("jpsiPIDcalibKine_negEta")); return cut; } - if (!nameStr.compare("pidPion_ionut")) { + if (nameStr == "pidPion_ionut") { cut->AddCut(GetAnalysisCut("pidcalib_pion")); cut->AddCut(GetAnalysisCut("jpsiStandardKine3")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts13_Corr")) { + if (nameStr == "jpsiO2MCdebugCuts13_Corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); // no cut on ITS clusters cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); @@ -571,14 +600,14 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts14")) { + if (nameStr == "jpsiO2MCdebugCuts14") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaSkewed")); return cut; } - if (!nameStr.compare("jpsiO2MCdebugCuts14andDCA")) { + if (nameStr == "jpsiO2MCdebugCuts14andDCA") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaSkewed")); @@ -586,14 +615,14 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("emu_electronCuts")) { + if (nameStr == "emu_electronCuts") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaSkewed")); return cut; } - if (!nameStr.compare("emu_electronCuts_tof")) { + if (nameStr == "emu_electronCuts_tof") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaSkewed")); @@ -601,14 +630,14 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("emu_electronCuts_tightTPC")) { + if (nameStr == "emu_electronCuts_tightTPC") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaSkewed_2")); return cut; } - if (!nameStr.compare("emu_electronCuts_tof_tightTPC")) { + if (nameStr == "emu_electronCuts_tof_tightTPC") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaSkewed_2")); @@ -616,14 +645,14 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiKineAndQuality")) { + if (nameStr == "jpsiKineAndQuality") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); return cut; } - if (!nameStr.compare("jpsiPID1")) { + if (nameStr == "jpsiPID1") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); // standard kine cuts usually are applied via Filter in the task cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -631,7 +660,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiPID2")) { + if (nameStr == "jpsiPID2") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -639,7 +668,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiPIDnsigma")) { + if (nameStr == "jpsiPIDnsigma") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -647,44 +676,44 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("pionPIDCut1")) { + if (nameStr == "pionPIDCut1") { cut->AddCut(GetAnalysisCut("pionQualityCut1")); cut->AddCut(GetAnalysisCut("pionPIDnsigma")); return cut; } - if (!nameStr.compare("pionPIDCut2")) { + if (nameStr == "pionPIDCut2") { cut->AddCut(GetAnalysisCut("pionQualityCut2")); cut->AddCut(GetAnalysisCut("pionPIDnsigma")); return cut; } - if (!nameStr.compare("PIDCalibElectron")) { + if (nameStr == "PIDCalibElectron") { cut->AddCut(GetAnalysisCut("pidcalib_ele")); return cut; } - if (!nameStr.compare("PIDCalibPion")) { + if (nameStr == "PIDCalibPion") { cut->AddCut(GetAnalysisCut("pidcalib_pion")); return cut; } - if (!nameStr.compare("PIDCalibKaon")) { + if (nameStr == "PIDCalibKaon") { cut->AddCut(GetAnalysisCut("pidcalib_kaon")); return cut; } - if (!nameStr.compare("PIDCalibProton")) { + if (nameStr == "PIDCalibProton") { cut->AddCut(GetAnalysisCut("pidcalib_proton")); return cut; } - if (!nameStr.compare("PIDCalib_basic")) { + if (nameStr == "PIDCalib_basic") { cut->AddCut(GetAnalysisCut("pidbasic")); return cut; } - if (!nameStr.compare("PIDefficiency_wPID")) { + if (nameStr == "PIDefficiency_wPID") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -693,7 +722,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("PIDefficiency_woPID")) { + if (nameStr == "PIDefficiency_woPID") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -701,34 +730,34 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("highPtHadron")) { + if (nameStr == "highPtHadron") { cut->AddCut(GetAnalysisCut("highPtHadron")); return cut; } - if (!nameStr.compare("rho0Cuts")) { + if (nameStr == "rho0Cuts") { cut->AddCut(GetAnalysisCut("rho0Kine")); cut->AddCut(GetAnalysisCut("pionQuality")); cut->AddCut(GetAnalysisCut("pionPIDnsigma")); return cut; } - if (!nameStr.compare("rho0Kine")) { + if (nameStr == "rho0Kine") { cut->AddCut(GetAnalysisCut("rho0Kine")); return cut; } - if (!nameStr.compare("openEtaSel")) { + if (nameStr == "openEtaSel") { cut->AddCut(GetAnalysisCut("openEtaSel")); return cut; } - if (!nameStr.compare("hasTOF")) { + if (nameStr == "hasTOF") { cut->AddCut(GetAnalysisCut("hasTOF")); return cut; } - if (!nameStr.compare("singleGapTrackCuts1")) { + if (nameStr == "singleGapTrackCuts1") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("SPDany")); cut->AddCut(GetAnalysisCut("openEtaSel")); @@ -736,7 +765,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("singleGapTrackCuts2")) { + if (nameStr == "singleGapTrackCuts2") { cut->AddCut(GetAnalysisCut("muonLowPt3")); cut->AddCut(GetAnalysisCut("ITSiball")); cut->AddCut(GetAnalysisCut("openEtaSel")); @@ -744,41 +773,41 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("singleGapTrackCuts3")) { + if (nameStr == "singleGapTrackCuts3") { cut->AddCut(GetAnalysisCut("PIDStandardKine2")); cut->AddCut(GetAnalysisCut("SPDany")); - AnalysisCompositeCut* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); + auto* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); cut_notpc->AddCut(GetAnalysisCut("noTPC")); - AnalysisCompositeCut* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); + auto* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); cut_tpcpid->AddCut(GetAnalysisCut("pionQuality")); - AnalysisCompositeCut* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); + auto* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); cut_OR->AddCut(cut_notpc); cut_OR->AddCut(cut_tpcpid); cut->AddCut(cut_OR); return cut; } - if (!nameStr.compare("singleGapTrackCuts4")) { + if (nameStr == "singleGapTrackCuts4") { cut->AddCut(GetAnalysisCut("PIDStandardKine2")); cut->AddCut(GetAnalysisCut("ITSibany")); - AnalysisCompositeCut* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); + auto* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); cut_notpc->AddCut(GetAnalysisCut("noTPC")); - AnalysisCompositeCut* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); + auto* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); cut_tpcpid->AddCut(GetAnalysisCut("pionQuality")); - AnalysisCompositeCut* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); + auto* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); cut_OR->AddCut(cut_notpc); cut_OR->AddCut(cut_tpcpid); cut->AddCut(cut_OR); return cut; } - if (!nameStr.compare("PIDCalib")) { + if (nameStr == "PIDCalib") { cut->AddCut(GetAnalysisCut("PIDStandardKine")); // standard kine cuts usually are applied via Filter in the task cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -786,50 +815,50 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("NoPID")) { + if (nameStr == "NoPID") { cut->AddCut(GetAnalysisCut("PIDStandardKine2")); // standard kine cuts usually are applied via Filter in the task cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly2")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); return cut; } - if (!nameStr.compare("KineCutOnly")) { + if (nameStr == "KineCutOnly") { cut->AddCut(GetAnalysisCut("PIDStandardKine")); // standard kine cuts usually are applied via Filter in the task return cut; } - if (!nameStr.compare("KineCutOnly2")) { + if (nameStr == "KineCutOnly2") { cut->AddCut(GetAnalysisCut("PIDStandardKine2")); // standard kine cuts usually are applied via Filter in the task return cut; } - if (!nameStr.compare("KineCutOnly3")) { + if (nameStr == "KineCutOnly3") { cut->AddCut(GetAnalysisCut("PIDStandardKine3")); // standard kine cuts usually are applied via Filter in the task return cut; } - if (!nameStr.compare("kaonPID")) { + if (nameStr == "kaonPID") { cut->AddCut(GetAnalysisCut("PIDStandardKine")); // standard kine cuts usually are applied via Filter in the task cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("kaonPIDnsigma")); return cut; } - if (!nameStr.compare("kaonPID2")) { + if (nameStr == "kaonPID2") { cut->AddCut(GetAnalysisCut("PIDStandardKine")); // standard kine cuts usually are applied via Filter in the task cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("kaonPIDnsigma2")); return cut; } - if (!nameStr.compare("kaonPID3")) { + if (nameStr == "kaonPID3") { cut->AddCut(GetAnalysisCut("AssocKine")); // standard kine cuts usually are applied via Filter in the task cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); return cut; } - if (!nameStr.compare("kaonPID3_withDCA")) { // same as kaonPID3 but with cut on DCA and SPDAny->ITSAny + if (nameStr == "kaonPID3_withDCA") { // same as kaonPID3 but with cut on DCA and SPDAny->ITSAny cut->AddCut(GetAnalysisCut("AssocKine")); // standard kine cuts usually are applied via Filter in the task cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug4")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); @@ -837,344 +866,344 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("kaonPID4")) { + if (nameStr == "kaonPID4") { cut->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); return cut; } - if (!nameStr.compare("kaonPID5")) { + if (nameStr == "kaonPID5") { cut->AddCut(GetAnalysisCut("kaonPIDnsigma")); return cut; } - if (!nameStr.compare("kaonPID6")) { + if (nameStr == "kaonPID6") { cut->AddCut(GetAnalysisCut("kaonPIDnsigma700")); return cut; } - if (!nameStr.compare("kaonPIDTPCTOForTPC")) { - AnalysisCompositeCut* cut_tpctof_nSigma = new AnalysisCompositeCut("pid_TPCTOFnSigma", "pid_TPCTOFnSigma", kTRUE); + if (nameStr == "kaonPIDTPCTOForTPC") { + auto* cut_tpctof_nSigma = new AnalysisCompositeCut("pid_TPCTOFnSigma", "pid_TPCTOFnSigma", kTRUE); cut_tpctof_nSigma->AddCut(GetAnalysisCut("hasTOF")); cut_tpctof_nSigma->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut("noTOF")); cut_tpc_nSigma->AddCut(GetAnalysisCut("kaonPIDnsigma")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("kaon_nsigma", "kaon_nsigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("kaon_nsigma", "kaon_nsigma", kFALSE); cut_pid_OR->AddCut(cut_tpctof_nSigma); cut_pid_OR->AddCut(cut_tpc_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare("kaonPIDTPCTOForTPC700")) { - AnalysisCompositeCut* cut_tpctof_nSigma = new AnalysisCompositeCut("pid_TPCTOFnSigma", "pid_TPCTOFnSigma", kTRUE); + if (nameStr == "kaonPIDTPCTOForTPC700") { + auto* cut_tpctof_nSigma = new AnalysisCompositeCut("pid_TPCTOFnSigma", "pid_TPCTOFnSigma", kTRUE); cut_tpctof_nSigma->AddCut(GetAnalysisCut("hasTOF")); cut_tpctof_nSigma->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut("noTOF")); cut_tpc_nSigma->AddCut(GetAnalysisCut("kaonPIDnsigma700")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("kaon_nsigma", "kaon_nsigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("kaon_nsigma", "kaon_nsigma", kFALSE); cut_pid_OR->AddCut(cut_tpctof_nSigma); cut_pid_OR->AddCut(cut_tpc_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare("kaonPosPID4")) { + if (nameStr == "kaonPosPID4") { cut->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("posTrack")); return cut; } - if (!nameStr.compare("kaonPosPID4Pt05")) { + if (nameStr == "kaonPosPID4Pt05") { cut->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("muonLowPt")); return cut; } - if (!nameStr.compare("kaonNegPID4")) { + if (nameStr == "kaonNegPID4") { cut->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("negTrack")); return cut; } - if (!nameStr.compare("kaonNegPID4Pt05")) { + if (nameStr == "kaonNegPID4Pt05") { cut->AddCut(GetAnalysisCut("kaonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("muonLowPt")); return cut; } - if (!nameStr.compare("pionPID")) { + if (nameStr == "pionPID") { cut->AddCut(GetAnalysisCut("pionPID_TPCnTOF")); return cut; } - if (!nameStr.compare("pionPID2")) { + if (nameStr == "pionPID2") { cut->AddCut(GetAnalysisCut("pionPIDnsigma")); return cut; } - if (!nameStr.compare("pionPIDTPCTOForTPC")) { - AnalysisCompositeCut* cut_tpctof_nSigma = new AnalysisCompositeCut("pid_TPCTOFnSigma", "pid_TPCTOFnSigma", kTRUE); + if (nameStr == "pionPIDTPCTOForTPC") { + auto* cut_tpctof_nSigma = new AnalysisCompositeCut("pid_TPCTOFnSigma", "pid_TPCTOFnSigma", kTRUE); cut_tpctof_nSigma->AddCut(GetAnalysisCut("pionPID_TPCnTOF")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut("pionPIDnsigma")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("pion_nsigma", "pion_nsigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("pion_nsigma", "pion_nsigma", kFALSE); cut_pid_OR->AddCut(cut_tpctof_nSigma); cut_pid_OR->AddCut(cut_tpc_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare("pionPosPID")) { + if (nameStr == "pionPosPID") { cut->AddCut(GetAnalysisCut("pionPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("posTrack")); return cut; } - if (!nameStr.compare("pionPosPID2")) { + if (nameStr == "pionPosPID2") { cut->AddCut(GetAnalysisCut("pionPIDnsigma")); cut->AddCut(GetAnalysisCut("posTrack")); return cut; } - if (!nameStr.compare("pionNegPID")) { + if (nameStr == "pionNegPID") { cut->AddCut(GetAnalysisCut("pionPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("negTrack")); return cut; } - if (!nameStr.compare("pionNegPID2")) { + if (nameStr == "pionNegPID2") { cut->AddCut(GetAnalysisCut("pionPIDnsigma")); cut->AddCut(GetAnalysisCut("negTrack")); return cut; } - if (!nameStr.compare("protonPosPID")) { + if (nameStr == "protonPosPID") { cut->AddCut(GetAnalysisCut("protonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("posTrack")); return cut; } - if (!nameStr.compare("protonPosPIDPt05")) { + if (nameStr == "protonPosPIDPt05") { cut->AddCut(GetAnalysisCut("protonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("muonLowPt")); return cut; } - if (!nameStr.compare("protonNegPID")) { + if (nameStr == "protonNegPID") { cut->AddCut(GetAnalysisCut("protonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("negTrack")); return cut; } - if (!nameStr.compare("protonNegPIDPt05")) { + if (nameStr == "protonNegPIDPt05") { cut->AddCut(GetAnalysisCut("protonPID_TPCnTOF")); cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("muonLowPt")); return cut; } - if (!nameStr.compare("protonPIDPV")) { + if (nameStr == "protonPIDPV") { cut->AddCut(GetAnalysisCut("protonPID_TPCnTOF2")); cut->AddCut(GetAnalysisCut("protonPVcut")); return cut; } - if (!nameStr.compare("protonPIDPV2")) { + if (nameStr == "protonPIDPV2") { cut->AddCut(GetAnalysisCut("protonPID_TPCnTOF2")); return cut; } - if (!nameStr.compare("PrimaryTrack_DCAz")) { + if (nameStr == "PrimaryTrack_DCAz") { cut->AddCut(GetAnalysisCut("PrimaryTrack_DCAz")); return cut; } - if (!nameStr.compare("posPrimaryTrack_DCAz")) { + if (nameStr == "posPrimaryTrack_DCAz") { cut->AddCut(GetAnalysisCut("PrimaryTrack_DCAz")); cut->AddCut(GetAnalysisCut("posTrack")); return cut; } - if (!nameStr.compare("negPrimaryTrack_DCAz")) { + if (nameStr == "negPrimaryTrack_DCAz") { cut->AddCut(GetAnalysisCut("PrimaryTrack_DCAz")); cut->AddCut(GetAnalysisCut("negTrack")); return cut; } - if (!nameStr.compare("posStandardPrimaryTrackDCA")) { + if (nameStr == "posStandardPrimaryTrackDCA") { cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCA")); cut->AddCut(GetAnalysisCut("posTrack")); return cut; } - if (!nameStr.compare("negStandardPrimaryTrackDCA")) { + if (nameStr == "negStandardPrimaryTrackDCA") { cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCA")); cut->AddCut(GetAnalysisCut("negTrack")); return cut; } - if (!nameStr.compare("posTrack")) { + if (nameStr == "posTrack") { cut->AddCut(GetAnalysisCut("posTrack")); return cut; } - if (!nameStr.compare("negTrack")) { + if (nameStr == "negTrack") { cut->AddCut(GetAnalysisCut("negTrack")); return cut; } - if (!nameStr.compare("posTrackKaonRej")) { + if (nameStr == "posTrackKaonRej") { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("kaonRejNsigma")); return cut; } - if (!nameStr.compare("negTrackKaonRej")) { + if (nameStr == "negTrackKaonRej") { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("kaonRejNsigma")); return cut; } - if (!nameStr.compare("pTLow05")) { + if (nameStr == "pTLow05") { cut->AddCut(GetAnalysisCut("muonLowPt")); return cut; } - if (!nameStr.compare("pTLow04")) { + if (nameStr == "pTLow04") { cut->AddCut(GetAnalysisCut("pTLow04")); return cut; } - if (!nameStr.compare("pTLow03")) { + if (nameStr == "pTLow03") { cut->AddCut(GetAnalysisCut("pTLow03")); return cut; } - if (!nameStr.compare("pTLow02")) { + if (nameStr == "pTLow02") { cut->AddCut(GetAnalysisCut("pTLow02")); return cut; } - if (!nameStr.compare("pTLow05DCAzHigh03")) { + if (nameStr == "pTLow05DCAzHigh03") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("PrimaryTrack_DCAz")); return cut; } - if (!nameStr.compare("pTLow04DCAzHigh03")) { + if (nameStr == "pTLow04DCAzHigh03") { cut->AddCut(GetAnalysisCut("pTLow04")); cut->AddCut(GetAnalysisCut("PrimaryTrack_DCAz")); return cut; } - if (!nameStr.compare("pTLow03DCAzHigh03")) { + if (nameStr == "pTLow03DCAzHigh03") { cut->AddCut(GetAnalysisCut("pTLow03")); cut->AddCut(GetAnalysisCut("PrimaryTrack_DCAz")); return cut; } // NOTE Below there are several TPC pid cuts used for studies of the Run3 TPC post PID calib. - if (!nameStr.compare("Jpsi_TPCPost_calib_debug1")) { + if (nameStr == "Jpsi_TPCPost_calib_debug1") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug1")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug2")) { + if (nameStr == "Jpsi_TPCPost_calib_debug2") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_noITSCuts_debug2")) { + if (nameStr == "Jpsi_TPCPost_calib_noITSCuts_debug2") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_noITSCuts_debug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug3")) { + if (nameStr == "Jpsi_TPCPost_calib_debug3") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug3")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug4")) { + if (nameStr == "Jpsi_TPCPost_calib_debug4") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug4")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_noITSCuts_debug4")) { + if (nameStr == "Jpsi_TPCPost_calib_noITSCuts_debug4") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_noITSCuts_debug")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug4")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug6")) { + if (nameStr == "Jpsi_TPCPost_calib_debug6") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug2")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug6")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug7")) { + if (nameStr == "Jpsi_TPCPost_calib_debug7") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug2")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug7")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug8")) { + if (nameStr == "Jpsi_TPCPost_calib_debug8") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug5")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug8")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug9")) { + if (nameStr == "Jpsi_TPCPost_calib_debug9") { cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug4")); cut->AddCut(GetAnalysisCut("electronPIDLooseSkimmed3")); return cut; } - if (!nameStr.compare("Jpsi_TPCPost_calib_debug10")) { + if (nameStr == "Jpsi_TPCPost_calib_debug10") { cut->AddCut(GetAnalysisCut("jpsiKineSkimmed")); cut->AddCut(GetAnalysisCut("jpsi_trackCut_debug6")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug10")); return cut; } - if (!nameStr.compare("LMee_TPCPost_calib_debug1")) { + if (nameStr == "LMee_TPCPost_calib_debug1") { cut->AddCut(GetAnalysisCut("lmee_trackCut_debug")); cut->AddCut(GetAnalysisCut("lmee_TPCPID_debug1")); return cut; } - if (!nameStr.compare("ITSalone_prefilter")) { + if (nameStr == "ITSalone_prefilter") { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); return cut; } - if (!nameStr.compare("ITSalonebAny_prefilter")) { + if (nameStr == "ITSalonebAny_prefilter") { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualitybAnyITSOnly")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); return cut; } - if (!nameStr.compare("TPCalone_prefilter")) { + if (nameStr == "TPCalone_prefilter") { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); return cut; } - if (!nameStr.compare("ITSTPC_prefilter")) { + if (nameStr == "ITSTPC_prefilter") { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1182,8 +1211,8 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - for (int iCut = 0; iCut < 10; iCut++) { - if (!nameStr.compare(Form("jpsiEleSel%d_ionut", iCut))) { + for (int iCut = 0; iCut < 10; iCut++) { // o2-linter: disable=magic-number (number of cuts) + if (nameStr == Form("jpsiEleSel%d_ionut", iCut)) { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1191,7 +1220,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("jpsiEleSelTight%d_ionut", iCut))) { + if (nameStr == Form("jpsiEleSelTight%d_ionut", iCut)) { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQualityTight_ionut")); @@ -1202,11 +1231,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // Magnus composite cuts ----------------------------------------------------------------------------------------------------------------- - AnalysisCompositeCut* magnus_PID111 = new AnalysisCompositeCut("magnus_PID111", ""); + auto* magnus_PID111 = new AnalysisCompositeCut("magnus_PID111", ""); magnus_PID111->AddCut(GetAnalysisCut("pidJpsi_magnus_ele1")); magnus_PID111->AddCut(GetAnalysisCut("pidJpsi_magnus_pion1")); magnus_PID111->AddCut(GetAnalysisCut("pidJpsi_magnus_prot1")); - if (!nameStr.compare("MagnussOptimization111")) { + if (nameStr == "MagnussOptimization111") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1214,11 +1243,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID211 = new AnalysisCompositeCut("magnus_PID211", ""); + auto* magnus_PID211 = new AnalysisCompositeCut("magnus_PID211", ""); magnus_PID211->AddCut(GetAnalysisCut("pidJpsi_magnus_ele2")); magnus_PID211->AddCut(GetAnalysisCut("pidJpsi_magnus_pion1")); magnus_PID211->AddCut(GetAnalysisCut("pidJpsi_magnus_prot1")); - if (!nameStr.compare("MagnussOptimization211")) { + if (nameStr == "MagnussOptimization211") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1226,11 +1255,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID311 = new AnalysisCompositeCut("magnus_PID311", ""); + auto* magnus_PID311 = new AnalysisCompositeCut("magnus_PID311", ""); magnus_PID311->AddCut(GetAnalysisCut("pidJpsi_magnus_ele3")); magnus_PID311->AddCut(GetAnalysisCut("pidJpsi_magnus_pion1")); magnus_PID311->AddCut(GetAnalysisCut("pidJpsi_magnus_prot1")); - if (!nameStr.compare("MagnussOptimization311")) { + if (nameStr == "MagnussOptimization311") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1238,11 +1267,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID121 = new AnalysisCompositeCut("magnus_PID121", ""); + auto* magnus_PID121 = new AnalysisCompositeCut("magnus_PID121", ""); magnus_PID121->AddCut(GetAnalysisCut("pidJpsi_magnus_ele1")); magnus_PID121->AddCut(GetAnalysisCut("pidJpsi_magnus_pion2")); magnus_PID121->AddCut(GetAnalysisCut("pidJpsi_magnus_prot1")); - if (!nameStr.compare("MagnussOptimization121")) { + if (nameStr == "MagnussOptimization121") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1250,11 +1279,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID112 = new AnalysisCompositeCut("magnus_PID112", ""); + auto* magnus_PID112 = new AnalysisCompositeCut("magnus_PID112", ""); magnus_PID112->AddCut(GetAnalysisCut("pidJpsi_magnus_ele1")); magnus_PID112->AddCut(GetAnalysisCut("pidJpsi_magnus_pion1")); magnus_PID112->AddCut(GetAnalysisCut("pidJpsi_magnus_prot2")); - if (!nameStr.compare("MagnussOptimization112")) { + if (nameStr == "MagnussOptimization112") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1262,11 +1291,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID122 = new AnalysisCompositeCut("magnus_PID122", ""); + auto* magnus_PID122 = new AnalysisCompositeCut("magnus_PID122", ""); magnus_PID122->AddCut(GetAnalysisCut("pidJpsi_magnus_ele1")); magnus_PID122->AddCut(GetAnalysisCut("pidJpsi_magnus_pion2")); magnus_PID122->AddCut(GetAnalysisCut("pidJpsi_magnus_prot2")); - if (!nameStr.compare("MagnussOptimization122")) { + if (nameStr == "MagnussOptimization122") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1274,11 +1303,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID222 = new AnalysisCompositeCut("magnus_PID222", ""); + auto* magnus_PID222 = new AnalysisCompositeCut("magnus_PID222", ""); magnus_PID222->AddCut(GetAnalysisCut("pidJpsi_magnus_ele2")); magnus_PID222->AddCut(GetAnalysisCut("pidJpsi_magnus_pion2")); magnus_PID222->AddCut(GetAnalysisCut("pidJpsi_magnus_prot2")); - if (!nameStr.compare("MagnussOptimization222")) { + if (nameStr == "MagnussOptimization222") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1286,11 +1315,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID212 = new AnalysisCompositeCut("magnus_PID212", ""); + auto* magnus_PID212 = new AnalysisCompositeCut("magnus_PID212", ""); magnus_PID212->AddCut(GetAnalysisCut("pidJpsi_magnus_ele2")); magnus_PID212->AddCut(GetAnalysisCut("pidJpsi_magnus_pion1")); magnus_PID212->AddCut(GetAnalysisCut("pidJpsi_magnus_prot2")); - if (!nameStr.compare("MagnussOptimization212")) { + if (nameStr == "MagnussOptimization212") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1298,11 +1327,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID221 = new AnalysisCompositeCut("magnus_PID221", ""); + auto* magnus_PID221 = new AnalysisCompositeCut("magnus_PID221", ""); magnus_PID221->AddCut(GetAnalysisCut("pidJpsi_magnus_ele2")); magnus_PID221->AddCut(GetAnalysisCut("pidJpsi_magnus_pion2")); magnus_PID221->AddCut(GetAnalysisCut("pidJpsi_magnus_prot1")); - if (!nameStr.compare("MagnussOptimization221")) { + if (nameStr == "MagnussOptimization221") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1310,11 +1339,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID321 = new AnalysisCompositeCut("magnus_PID321", ""); + auto* magnus_PID321 = new AnalysisCompositeCut("magnus_PID321", ""); magnus_PID321->AddCut(GetAnalysisCut("pidJpsi_magnus_ele3")); magnus_PID321->AddCut(GetAnalysisCut("pidJpsi_magnus_pion2")); magnus_PID321->AddCut(GetAnalysisCut("pidJpsi_magnus_prot1")); - if (!nameStr.compare("MagnussOptimization321")) { + if (nameStr == "MagnussOptimization321") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1322,11 +1351,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID312 = new AnalysisCompositeCut("magnus_PID312", ""); + auto* magnus_PID312 = new AnalysisCompositeCut("magnus_PID312", ""); magnus_PID312->AddCut(GetAnalysisCut("pidJpsi_magnus_ele3")); magnus_PID312->AddCut(GetAnalysisCut("pidJpsi_magnus_pion1")); magnus_PID312->AddCut(GetAnalysisCut("pidJpsi_magnus_prot2")); - if (!nameStr.compare("MagnussOptimization312")) { + if (nameStr == "MagnussOptimization312") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1334,11 +1363,11 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - AnalysisCompositeCut* magnus_PID322 = new AnalysisCompositeCut("magnus_PID322", ""); + auto* magnus_PID322 = new AnalysisCompositeCut("magnus_PID322", ""); magnus_PID322->AddCut(GetAnalysisCut("pidJpsi_magnus_ele1")); magnus_PID322->AddCut(GetAnalysisCut("pidJpsi_magnus_pion2")); magnus_PID322->AddCut(GetAnalysisCut("pidJpsi_magnus_prot2")); - if (!nameStr.compare("MagnussOptimization322")) { + if (nameStr == "MagnussOptimization322") { cut->AddCut(GetAnalysisCut("kineJpsiEle_ionut")); cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); cut->AddCut(GetAnalysisCut("trackQuality_ionut")); @@ -1350,7 +1379,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) //--------------------------------------------------------------- // Cuts for the selection of legs from dalitz decay // - if (!nameStr.compare("DalitzCut1")) { + if (nameStr == "DalitzCut1") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1358,7 +1387,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut2")) { + if (nameStr == "DalitzCut2") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1366,7 +1395,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut2_Corr")) { + if (nameStr == "DalitzCut2_Corr") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1374,7 +1403,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut3")) { + if (nameStr == "DalitzCut3") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1382,7 +1411,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut3_Corr")) { + if (nameStr == "DalitzCut3_Corr") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1390,7 +1419,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut1SPDfirst")) { + if (nameStr == "DalitzCut1SPDfirst") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1399,7 +1428,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut1SPDfirst_Corr")) { + if (nameStr == "DalitzCut1SPDfirst_Corr") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1408,7 +1437,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut2SPDfirst")) { + if (nameStr == "DalitzCut2SPDfirst") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1417,7 +1446,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut2SPDfirst_Corr")) { + if (nameStr == "DalitzCut2SPDfirst_Corr") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1426,7 +1455,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut3SPDfirst")) { + if (nameStr == "DalitzCut3SPDfirst") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1435,7 +1464,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("DalitzCut3SPDfirst_Corr")) { + if (nameStr == "DalitzCut3SPDfirst_Corr") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1444,52 +1473,89 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("Dalitz_WithTOF_SPDfirst")) { + if (nameStr == "Dalitz_WithTOF_SPDfirst") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); cut->AddCut(GetAnalysisCut("SPDfirst")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut("electronPIDPrKaPiRejLoose")); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut("electronPID_TOFnsigma")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare("Dalitz_WithTOF_SPDfirst_Corr")) { + if (nameStr == "Dalitz_WithTOF_SPDfirst_Corr") { cut->AddCut(GetAnalysisCut("dalitzStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); cut->AddCut(GetAnalysisCut("SPDfirst")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut("electronPIDPrKaPiRejLoose_Corr")); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut("electronPID_TOFnsigma_Corr")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - for (int i = 1; i <= 8; i++) { - if (!nameStr.compare(Form("dalitzSelected%d", i))) { + for (int i = 1; i <= 8; i++) { // o2-linter: disable=magic-number (number of cuts) + if (nameStr == Form("dalitzSelected%d", i)) { cut->AddCut(GetAnalysisCut(Form("dalitzLeg%d", i))); return cut; } } - if (!nameStr.compare("jpsiPIDworseRes")) { + if (nameStr == "electronPrimaryTag0") { + // with tight 3 sigma DCA cut for selecting primary electrons + cut->AddCut(GetAnalysisCut("electronPID_TPCnsigma_loose")); // 3 sigma inclusion, 3sigma rejection + cut->AddCut(GetAnalysisCut("electronPrimary_dca3sigma")); + cut->AddCut(GetAnalysisCut("jpsiStandardKine")); + return cut; + } + + if (nameStr == "electronPrimaryTag1") { + // with 7 sigma DCA cut for selecting primary electrons + cut->AddCut(GetAnalysisCut("electronPID_TPCnsigma_loose")); // 3 sigma inclusion, 3sigma rejection + cut->AddCut(GetAnalysisCut("electronPrimary_dca7sigma")); + cut->AddCut(GetAnalysisCut("jpsiStandardKine")); + return cut; + } + + if (nameStr == "electronPrimaryProbe_TPC") { + cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); + cut->AddCut(GetAnalysisCut("lmeeStandardKine")); + return cut; + } + + if (nameStr == "electronPrimaryProbe_ITS") { + cut->AddCut(GetAnalysisCut("electronStandardQualitybAnyITSOnly")); + cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCA")); + cut->AddCut(GetAnalysisCut("lmeeStandardKine")); + return cut; + } + + if (nameStr == "electronPrimaryProbe_ITSTPC") { + cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); + cut->AddCut(GetAnalysisCut("electronStandardQualitybAnyITSOnly")); + cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCA")); + cut->AddCut(GetAnalysisCut("lmeeStandardKine")); + return cut; + } + + if (nameStr == "jpsiPIDworseRes") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -1497,7 +1563,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiPIDshift")) { + if (nameStr == "jpsiPIDshift") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -1505,7 +1571,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiPID1shiftUp")) { + if (nameStr == "jpsiPID1shiftUp") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -1513,54 +1579,82 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsiPID1shiftDown")) { + if (nameStr == "jpsiPID1shiftDown") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQuality")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); cut->AddCut(GetAnalysisCut("electronPID1shiftDown")); return cut; } + // ------------------------------------------------------------------------------------------------- + // + // Q vector contributor cut + // + if (nameStr == "selTPCCentral") { + auto* kineCut = new AnalysisCut("kineCut", "kine cut"); + kineCut->AddCut(VarManager::kEta, -0.8, 0.8); + kineCut->AddCut(VarManager::kPt, 0.15, 5); + + auto* qualityCuts = new AnalysisCut("qualityCuts", "quality cuts"); + qualityCuts->AddCut(VarManager::kTPCchi2, 0., 4.); + qualityCuts->AddCut(VarManager::kTPCnCRoverFindCls, 0.8, 1.); + qualityCuts->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); + qualityCuts->AddCut(VarManager::kITSchi2, 0., 36.); + + auto* dcaCuts = new AnalysisCut("dcaCuts", "DCA cuts"); + std::shared_ptr f1dcaxyHigh = std::make_shared("f1dcaxy", "[0]+[1]/pow(x,[2])", 0., 10.); + f1dcaxyHigh->SetParameters(0.0105, 0.035, 1.1); + std::shared_ptr f1dcaxyLow = std::make_shared("f1dcaxy_low", "[0]+[1]/pow(x,[2])", 0., 10.); + f1dcaxyLow->SetParameters(-0.0105, -0.035, 1.1); + dcaCuts->AddCut(VarManager::kTrackDCAxy, f1dcaxyLow, f1dcaxyHigh); + dcaCuts->AddCut(VarManager::kTrackDCAz, -2., 2.); + + cut->AddCut(kineCut); + cut->AddCut(qualityCuts); + cut->AddCut(dcaCuts); + } + // ------------------------------------------------------------------------------------------------- // // LMee cuts // List of cuts used for low mass dielectron analyses // // Skimming cuts: - if (!nameStr.compare("lmee_skimming")) { + if (nameStr == "lmee_skimming") { cut->AddCut(GetAnalysisCut("lmee_skimming_cuts")); return cut; } // LMee Run2 PID cuts - if (!nameStr.compare("lmeePID_TPChadrejTOFrec")) { + if (nameStr == "lmeePID_TPChadrejTOFrec") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); - AnalysisCompositeCut* cut_tpc_hadrej = new AnalysisCompositeCut("pid_TPChadrej", "pid_TPChadrej", kTRUE); + auto* cut_tpc_hadrej = new AnalysisCompositeCut("pid_TPChadrej", "pid_TPChadrej", kTRUE); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_electron")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_pion_rejection")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_kaon_rejection")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_proton_rejection")); - AnalysisCompositeCut* cut_tof_rec = new AnalysisCompositeCut("pid_tof_rec", "pid_tof_rec", kTRUE); + auto* cut_tof_rec = new AnalysisCompositeCut("pid_tof_rec", "pid_tof_rec", kTRUE); cut_tof_rec->AddCut(GetAnalysisCut("tpc_electron")); cut_tof_rec->AddCut(GetAnalysisCut("tof_electron")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("pid_TPChadrejTOFrec", "pid_TPChadrejTOFrec", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("pid_TPChadrejTOFrec", "pid_TPChadrejTOFrec", kFALSE); cut_pid_OR->AddCut(cut_tpc_hadrej); cut_pid_OR->AddCut(cut_tof_rec); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare("lmeePID_TPChadrej")) { + if (nameStr == "lmeePID_TPChadrej") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); - AnalysisCompositeCut* cut_tpc_hadrej = new AnalysisCompositeCut("pid_TPChadrej", "pid_TPChadrej", kTRUE); + auto* cut_tpc_hadrej = new AnalysisCompositeCut("pid_TPChadrej", "pid_TPChadrej", kTRUE); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_electron")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_pion_rejection")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_kaon_rejection")); @@ -1569,30 +1663,30 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_eNSigmaRun2")) { + if (nameStr == "lmee_eNSigmaRun2") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut("electronPID_TPCnsigma")); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut("electronPID_TOFnsigma")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare("lmeePID_TOFrec")) { + if (nameStr == "lmeePID_TOFrec") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); - AnalysisCompositeCut* cut_tof_rec = new AnalysisCompositeCut("pid_tof_rec", "pid_tof_rec", kTRUE); + auto* cut_tof_rec = new AnalysisCompositeCut("pid_tof_rec", "pid_tof_rec", kTRUE); cut_tof_rec->AddCut(GetAnalysisCut("tpc_electron")); cut_tof_rec->AddCut(GetAnalysisCut("tof_electron")); @@ -1600,28 +1694,28 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_GlobalTrackRun2")) { + if (nameStr == "lmee_GlobalTrackRun2") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); return cut; } - if (!nameStr.compare("lmee_GlobalTrackRun2_lowPt")) { + if (nameStr == "lmee_GlobalTrackRun2_lowPt") { cut->AddCut(GetAnalysisCut("lmeeLowBKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); return cut; } - if (!nameStr.compare("lmee_TPCTrackRun2_lowPt")) { + if (nameStr == "lmee_TPCTrackRun2_lowPt") { cut->AddCut(GetAnalysisCut("lmeeLowBKine")); cut->AddCut(GetAnalysisCut("TightTPCTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); return cut; } - if (!nameStr.compare("lmee_TPCTrackRun2")) { + if (nameStr == "lmee_TPCTrackRun2") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightTPCTrack")); cut->AddCut(GetAnalysisCut("standardPrimaryTrack")); @@ -1630,31 +1724,31 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // LMee Run3 PID cuts - if (!nameStr.compare("lmeePID_TPChadrejTOFrecRun3")) { + if (nameStr == "lmeePID_TPChadrejTOFrecRun3") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_hadrej = new AnalysisCompositeCut("pid_TPChadrej", "pid_TPChadrej", kTRUE); + auto* cut_tpc_hadrej = new AnalysisCompositeCut("pid_TPChadrej", "pid_TPChadrej", kTRUE); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_electron")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_pion_muon_band_rejection")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_pion_rejection_highp")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_kaon_rejection")); cut_tpc_hadrej->AddCut(GetAnalysisCut("tpc_proton_rejection")); - AnalysisCompositeCut* cut_tof_rec = new AnalysisCompositeCut("pid_tof_rec", "pid_tof_rec", kTRUE); + auto* cut_tof_rec = new AnalysisCompositeCut("pid_tof_rec", "pid_tof_rec", kTRUE); cut_tof_rec->AddCut(GetAnalysisCut("tpc_electron")); cut_tof_rec->AddCut(GetAnalysisCut("tof_electron_loose")); cut_tof_rec->AddCut(GetAnalysisCut("tpc_pion_rejection_highp")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("pid_TPChadrejTOFrec", "pid_TPChadrejTOFrec", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("pid_TPChadrejTOFrec", "pid_TPChadrejTOFrec", kFALSE); cut_pid_OR->AddCut(cut_tpc_hadrej); cut_pid_OR->AddCut(cut_tof_rec); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare("lmee_Run3_TPCelectron")) { + if (nameStr == "lmee_Run3_TPCelectron") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); @@ -1662,7 +1756,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_Run3_TOFelectron")) { + if (nameStr == "lmee_Run3_TOFelectron") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); @@ -1670,7 +1764,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_Run3_posTrack_TPCelectron")) { + if (nameStr == "lmee_Run3_posTrack_TPCelectron") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); @@ -1679,7 +1773,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_Run3_posTrack_TOFelectron")) { + if (nameStr == "lmee_Run3_posTrack_TOFelectron") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); @@ -1688,7 +1782,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_Run3_negTrack_TPCelectron")) { + if (nameStr == "lmee_Run3_negTrack_TPCelectron") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); @@ -1697,7 +1791,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_Run3_negTrack_TOFelectron")) { + if (nameStr == "lmee_Run3_negTrack_TOFelectron") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); @@ -1721,21 +1815,21 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // loop to define PID cuts with and without post calibration for (size_t icase = 0; icase < vecTypetrack.size(); icase++) { // Tracking cuts of Pb--Pb analysis - if (!nameStr.compare(Form("lmee%s_PbPb_selection", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee%s_PbPb_selection", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); return cut; } - if (!nameStr.compare(Form("lmee%s_PbPb_selection_pt04", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee%s_PbPb_selection_pt04", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine_pt04")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); return cut; } - if (!nameStr.compare(Form("lmee%s_TrackCuts_Resol", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee%s_TrackCuts_Resol", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("openEtaSel")); // No pt cut and wider eta cut to produce resolution maps cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); @@ -1743,7 +1837,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 4 cuts to separate pos & neg tracks in pos & neg eta range - if (!nameStr.compare(Form("lmee_posTrack_posEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_posTrack_posEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1751,7 +1845,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_negTrack_posEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_negTrack_posEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1759,7 +1853,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_posTrack_negEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_posTrack_negEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1767,7 +1861,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_negTrack_negEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_negTrack_negEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1776,7 +1870,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 2 cuts to separate pos & neg tracks - if (!nameStr.compare(Form("lmee_posTrack_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_posTrack_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("etaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1784,7 +1878,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_negTrack_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_negTrack_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("etaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1793,7 +1887,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 4 cuts to separate pos & neg tracks in pos & neg eta range low B field - if (!nameStr.compare(Form("lmee_lowB_posTrack_posEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_lowB_posTrack_posEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1801,7 +1895,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_lowB_negTrack_posEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_lowB_negTrack_posEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1809,7 +1903,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_lowB_posTrack_negEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_lowB_posTrack_negEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1817,7 +1911,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_lowB_negTrack_negEta_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_lowB_negTrack_negEta_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1826,7 +1920,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 2 cuts to separate pos & neg tracks in low B field - if (!nameStr.compare(Form("lmee_lowB_posTrack_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_lowB_posTrack_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("etaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1834,7 +1928,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_lowB_negTrack_selection%s", vecTypetrack.at(icase).Data()))) { + if (nameStr == Form("lmee_lowB_negTrack_selection%s", vecTypetrack.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("etaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))); @@ -1857,12 +1951,12 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // loop to define PID cuts with and without post calibration for (size_t icase = 0; icase < vecPIDcase.size(); icase++) { - if (!nameStr.compare(Form("lmee_onlyTPCPID%s", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_onlyTPCPID%s", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut(Form("electronPIDOnly%s", vecPIDcase.at(icase).Data()))); return cut; } - if (!nameStr.compare(Form("ITSTPC_TPCPID%s_prefilter", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("ITSTPC_TPCPID%s_prefilter", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1871,45 +1965,45 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("ITS_ifTPC_TPCPID%s_prefilter", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("ITS_ifTPC_TPCPID%s_prefilter", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); + auto* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); cut_notpc->AddCut(GetAnalysisCut("noTPC")); - AnalysisCompositeCut* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); + auto* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); cut_tpcpid->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); cut_tpcpid->AddCut(GetAnalysisCut(Form("electronPIDOnly%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); + auto* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); cut_OR->AddCut(cut_notpc); cut_OR->AddCut(cut_tpcpid); cut->AddCut(cut_OR); return cut; } - if (!nameStr.compare(Form("ITS_ifTPCStandard_TPCPID%s_prefilter", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("ITS_ifTPCStandard_TPCPID%s_prefilter", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityITSOnly")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_notpcstandard = new AnalysisCompositeCut("NoTPCstandard", "NoTPCstandard", kTRUE); + auto* cut_notpcstandard = new AnalysisCompositeCut("NoTPCstandard", "NoTPCstandard", kTRUE); cut_notpcstandard->AddCut(GetAnalysisCut("NoelectronStandardQualityTPCOnly")); - AnalysisCompositeCut* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); + auto* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); cut_tpcpid->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); cut_tpcpid->AddCut(GetAnalysisCut(Form("electronPIDOnly%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); + auto* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); cut_OR->AddCut(cut_notpcstandard); cut_OR->AddCut(cut_tpcpid); cut->AddCut(cut_OR); return cut; } - if (!nameStr.compare(Form("ITSTPCbAny_TPCPID%s_prefilter", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("ITSTPCbAny_TPCPID%s_prefilter", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualitybAnyITSOnly")); cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); @@ -1918,57 +2012,57 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("ITSbAny_ifTPC_TPCPID%s_prefilter", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("ITSbAny_ifTPC_TPCPID%s_prefilter", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualitybAnyITSOnly")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); + auto* cut_notpc = new AnalysisCompositeCut("NoTPC", "NoTPC", kTRUE); cut_notpc->AddCut(GetAnalysisCut("noTPC")); - AnalysisCompositeCut* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); + auto* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); cut_tpcpid->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); cut_tpcpid->AddCut(GetAnalysisCut(Form("electronPIDOnly%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); + auto* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); cut_OR->AddCut(cut_notpc); cut_OR->AddCut(cut_tpcpid); cut->AddCut(cut_OR); return cut; } - if (!nameStr.compare(Form("ITSbAny_ifTPCStandard_TPCPID%s_prefilter", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("ITSbAny_ifTPCStandard_TPCPID%s_prefilter", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeePrefilterKine")); cut->AddCut(GetAnalysisCut("electronStandardQualitybAnyITSOnly")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_notpcstandard = new AnalysisCompositeCut("NoTPCstandard", "NoTPCstandard", kTRUE); + auto* cut_notpcstandard = new AnalysisCompositeCut("NoTPCstandard", "NoTPCstandard", kTRUE); cut_notpcstandard->AddCut(GetAnalysisCut("NoelectronStandardQualityTPCOnly")); - AnalysisCompositeCut* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); + auto* cut_tpcpid = new AnalysisCompositeCut("pid_TPC", "pid_TPC", kTRUE); cut_tpcpid->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly")); cut_tpcpid->AddCut(GetAnalysisCut(Form("electronPIDOnly%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); + auto* cut_OR = new AnalysisCompositeCut("OR", "OR", kFALSE); cut_OR->AddCut(cut_notpcstandard); cut_OR->AddCut(cut_tpcpid); cut->AddCut(cut_OR); return cut; } - for (unsigned int i = 0; i < 30; i++) { - if (!nameStr.compare(Form("ElSelCutVar%s%i", vecPIDcase.at(icase).Data(), i))) { + for (unsigned int i = 0; i < 30; i++) { // o2-linter: disable=magic-number (number of cuts) + if (nameStr == Form("ElSelCutVar%s%i", vecPIDcase.at(icase).Data(), i)) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeCutVarTrackCuts%i", i))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma_cutVar%s%i", vecPIDcase.at(icase).Data(), i))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma_cutVar%s%i", vecPIDcase.at(icase).Data(), i))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -1978,7 +2072,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) for (size_t jcase = 0; jcase < vecTypetrackWithPID.size(); jcase++) { // All previous cut with TightGlobalTrackRun3 - if (!nameStr.compare(Form("ITSTPC%s_TPCPIDalone%s_PbPb", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("ITSTPC%s_TPCPIDalone%s_PbPb", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); @@ -1986,97 +2080,97 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_loose", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_loose", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_loose", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_loose", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_strongHadRej", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_strongHadRej", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongHadRej", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongHadRej", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_TOFreq", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_TOFreq", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); @@ -2084,133 +2178,133 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_Resol", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_Resol", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("openEtaSel")); // No pt cut and wider eta cut to produce resolution maps cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_tightNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_tightNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_tightNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_tightNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine_pt04")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine_pt04")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_eNSigmaRun3%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_eNSigmaRun3%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeLowBKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); // to reject looper using DCAz - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_eNSigmaRun3%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_eNSigmaRun3%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeLowBKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); // to reject looper using DCAz - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_TOFreqRun3%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_TOFreqRun3%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine_pt04")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); @@ -2218,7 +2312,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_TOFreqRun3%s_tightNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_TOFreqRun3%s_tightNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine_pt04")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); @@ -2227,152 +2321,152 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 8 cuts for QC - if (!nameStr.compare(Form("lmee%s_NSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_NSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("pt02Sel")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_NSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_NSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("pt02Sel")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_NSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_NSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("pt04Sel")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_NSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_NSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("pt04Sel")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_posNSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_posNSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_posNSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_posNSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_negNSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_negNSigmaRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_negNSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_negNSigmaRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -2380,7 +2474,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 6 cuts for QC - if (!nameStr.compare(Form("lmee%s_posTOFreqRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_posTOFreqRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); @@ -2389,7 +2483,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_posTOFreqRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_posTOFreqRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); @@ -2398,7 +2492,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_TOFreqRun3_posEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_TOFreqRun3_posEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("pt04Sel")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); @@ -2407,7 +2501,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_TOFreqRun3_negEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_TOFreqRun3_negEta%s_strongNSigEPbPb_rejBadTOF_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("pt04Sel")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); @@ -2416,7 +2510,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_negTOFreqRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_negTOFreqRun3_posEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); @@ -2425,7 +2519,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_negTOFreqRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_negTOFreqRun3_negEta%s_strongNSigEPbPb_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); @@ -2434,42 +2528,42 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_TPC_PID", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_TPC_PID", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); cut->AddCut(cut_tpc_nSigma); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_TOF_PID", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_TOF_PID", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); cut->AddCut(cut_tof_nSigma); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_strongNSigE_DCA05", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_strongNSigE_DCA05", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_DCA05")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -2477,76 +2571,76 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 4 cuts to separate pos & neg tracks in pos & neg eta range applying electron PID - if (!nameStr.compare(Form("lmee%s_posNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_posNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_negNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_negNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_posNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_posNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_negNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_negNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -2554,76 +2648,76 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 4 cuts to separate pos & neg tracks in pos & neg eta range applying electron PID for low B field - if (!nameStr.compare(Form("lmee%s_lowB_posNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_posNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_negNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_negNSigmaRun3_posEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_posNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_posNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_negNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_negNSigmaRun3_negEta%s_strongNSigE", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -2631,76 +2725,76 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // 4 cuts to separate pos & neg tracks in pos & neg eta range applying electron PID for low B field with bad TOF rejection - if (!nameStr.compare(Form("lmee%s_lowB_posNSigmaRun3_posEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_posNSigmaRun3_posEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_negNSigmaRun3_posEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_negNSigmaRun3_posEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("posEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_posNSigmaRun3_negEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_posNSigmaRun3_negEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("posTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_lowB_negNSigmaRun3_negEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_lowB_negNSigmaRun3_negEta%s_strongNSigE_rejBadTOF", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("negTrack")); cut->AddCut(GetAnalysisCut("negEtaSel")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_lowB_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -2708,38 +2802,38 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } // some older cuts - if (!nameStr.compare(Form("lmee%s_pp502TeV_PID%s", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_pp502TeV_PID%s", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TPC%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TOF%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - for (int i = 1; i <= 8; i++) { - if (!nameStr.compare(Form("lmee%s_pp502TeV_PID%s_UsePrefilter%d", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data(), i))) { + for (int i = 1; i <= 8; i++) { // o2-linter: disable=magic-number (number of cuts) + if (nameStr == Form("lmee%s_pp502TeV_PID%s_UsePrefilter%d", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data(), i)) { cut->AddCut(GetAnalysisCut(Form("notDalitzLeg%d", i))); cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TPC%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TOF%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -2747,56 +2841,56 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } } - if (!nameStr.compare(Form("lmee%s_pp502TeV_lowB_PID%s", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_pp502TeV_lowB_PID%s", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeLowBKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("standardPrimaryTrackDCAz")); // DCAz to reject loopers - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_lowB_TPC%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_lowB_TOF%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee%s_eNSigmaRun3%s_pt04", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine_pt04")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - for (int i = 1; i <= 8; i++) { - if (!nameStr.compare(Form("lmee%s_eNSigmaRun3%s_UsePrefilter%d", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data(), i))) { + for (int i = 1; i <= 8; i++) { // o2-linter: disable=magic-number (number of cuts) + if (nameStr == Form("lmee%s_eNSigmaRun3%s_UsePrefilter%d", vecTypetrackWithPID.at(jcase).Data(), vecPIDcase.at(icase).Data(), i)) { cut->AddCut(GetAnalysisCut(Form("notDalitzLeg%d", i))); cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut(Form("lmeeQCTrackCuts%s", vecTypetrackWithPID.at(jcase).Data()))); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); @@ -2805,55 +2899,55 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } } - if (!nameStr.compare(Form("lmee_eNSigmaRun3%s_strongTPC", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_eNSigmaRun3%s_strongTPC", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3_strongTPC")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TPCnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("electronPID_TOFnsigma%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee_skimmingtesta_PID%s", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_skimmingtesta_PID%s", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("LooseGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TPCloose%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TOFloose%s", vecPIDcase.at(icase).Data()))); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); cut->AddCut(cut_pid_OR); return cut; } - if (!nameStr.compare(Form("lmee_skimmingtestb_PID%s", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_skimmingtestb_PID%s", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("LooseGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TPCloosenopkrej%s", vecPIDcase.at(icase).Data()))); cut->AddCut(cut_tpc_nSigma); return cut; } - if (!nameStr.compare(Form("lmee_skimmingtesta_TOF%s", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_skimmingtesta_TOF%s", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("LooseGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); @@ -2861,7 +2955,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_skimmingtesta_TOF_pionrej%s", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_skimmingtesta_TOF_pionrej%s", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("LooseGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); @@ -2869,7 +2963,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_skimmingtesta_TOF_pionrej_noDCA%s", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_skimmingtesta_TOF_pionrej_noDCA%s", vecPIDcase.at(icase).Data())) { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("LooseGlobalTrackRun3")); cut->AddCut(GetAnalysisCut(Form("lmee_pp_502TeV_TOFloose_pionrej%s", vecPIDcase.at(icase).Data()))); @@ -2877,70 +2971,81 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) } } - if (!nameStr.compare("testCut_chic")) { + if (nameStr == "testCut_chic") { cut->AddCut(GetAnalysisCut("jpsiStandardKine5")); cut->AddCut(GetAnalysisCut("electronStandardQualityForO2MCdebug")); cut->AddCut(GetAnalysisCut("electronPIDnsigma")); return cut; } - if (!nameStr.compare("lmee_GlobalTrackRun3")) { + if (nameStr == "lmee_GlobalTrackRun3") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); return cut; } - if (!nameStr.compare("lmee_GlobalTrackRun3_lowPt")) { + if (nameStr == "lmee_GlobalTrackRun3_lowPt") { cut->AddCut(GetAnalysisCut("lmeeLowBKine")); cut->AddCut(GetAnalysisCut("TightGlobalTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); return cut; } - if (!nameStr.compare("lmee_TPCTrackRun3_lowPt")) { + if (nameStr == "lmee_TPCTrackRun3_lowPt") { cut->AddCut(GetAnalysisCut("lmeeLowBKine")); cut->AddCut(GetAnalysisCut("TightTPCTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); return cut; } - if (!nameStr.compare("lmee_TPCTrackRun3")) { + if (nameStr == "lmee_TPCTrackRun3") { cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("TightTPCTrackRun3")); cut->AddCut(GetAnalysisCut("PrimaryTrack_looseDCA")); return cut; } - if (!nameStr.compare("trackCut_compareDQEMframework")) { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ + if (nameStr == "trackCut_compareDQEMframework") { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ cut->AddCut(GetAnalysisCut("lmeeStandardKine")); cut->AddCut(GetAnalysisCut("trackQuality_compareDQEMframework")); cut->AddCut(GetAnalysisCut("trackDCA1cm")); - AnalysisCompositeCut* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); + auto* cut_tpc_nSigma = new AnalysisCompositeCut("pid_TPCnSigma", "pid_TPCnSigma", kTRUE); cut_tpc_nSigma->AddCut(GetAnalysisCut("lmee_commonDQEM_PID_TPC")); - AnalysisCompositeCut* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); + auto* cut_tof_nSigma = new AnalysisCompositeCut("pid_TOFnSigma", "pid_TOFnSigma", kTRUE); cut_tof_nSigma->AddCut(GetAnalysisCut("lmee_commonDQEM_PID_TOF")); - AnalysisCompositeCut* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); + auto* cut_pid_OR = new AnalysisCompositeCut("e_NSigma", "e_NSigma", kFALSE); cut_pid_OR->AddCut(cut_tpc_nSigma); cut_pid_OR->AddCut(cut_tof_nSigma); return cut; } + if (nameStr == "jpsi_debug_TPCTOF3_rejBadTOF") { + cut->AddCut(GetAnalysisCut("jpsiStandardKine5")); + cut->AddCut(GetAnalysisCut("electronStandardQualityTPCOnly3")); + cut->AddCut(GetAnalysisCut("SPDfirst")); + cut->AddCut(GetAnalysisCut("dcaCut1_ionut")); + cut->AddCut(GetAnalysisCut("pidJpsi_TPCpion0")); + cut->AddCut(GetAnalysisCut("pidJpsi_beta")); + cut->AddCut(GetAnalysisCut("pidJpsi_noTOF_prot")); + return cut; + } + // ------------------------------------------------------------------------------------------------- // lmee pair cuts - if (!nameStr.compare("pairPhiV")) { - AnalysisCompositeCut* cut_pairPhiV = new AnalysisCompositeCut("cut_pairPhiV", "cut_pairPhiV", kTRUE); + if (nameStr == "pairPhiV") { + auto* cut_pairPhiV = new AnalysisCompositeCut("cut_pairPhiV", "cut_pairPhiV", kTRUE); cut_pairPhiV->AddCut(GetAnalysisCut("pairLowMass")); cut_pairPhiV->AddCut(GetAnalysisCut("pairPhiV")); cut->AddCut(cut_pairPhiV); return cut; } - if (!nameStr.compare("excludePairPhiV")) { - AnalysisCompositeCut* cut_pairlowPhiV = new AnalysisCompositeCut("cut_pairlowPhiV", "cut_pairlowPhiV", kFALSE); + if (nameStr == "excludePairPhiV") { + auto* cut_pairlowPhiV = new AnalysisCompositeCut("cut_pairlowPhiV", "cut_pairlowPhiV", kFALSE); cut_pairlowPhiV->AddCut(GetAnalysisCut("excludePairLowMass")); cut_pairlowPhiV->AddCut(GetAnalysisCut("excludePairPhiV")); cut->AddCut(cut_pairlowPhiV); @@ -2950,651 +3055,712 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // ------------------------------------------------------------------------------------------------- // Muon cuts - if (!nameStr.compare("muonQualityCutsMatchingOnly")) { + if (nameStr == "muonQualityCutsMatchingOnly") { cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonMinimalCuts")) { + if (nameStr == "muonEtaCut") { + cut->AddCut(GetAnalysisCut("muonEtaCut")); + return cut; + } + + if (nameStr == "muonMinimalCuts") { cut->AddCut(GetAnalysisCut("muonMinimalCuts")); return cut; } - if (!nameStr.compare("muonMinimalCuts10SigmaPDCA")) { + if (nameStr == "muonMinimalCuts10SigmaPDCA") { cut->AddCut(GetAnalysisCut("muonMinimalCuts10SigmaPDCA")); return cut; } - if (!nameStr.compare("muonQualityCuts")) { + if (nameStr == "muonQualityCuts") { cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("matchedQualityCuts")) { + if (nameStr == "matchedQualityCuts") { cut->AddCut(GetAnalysisCut("matchedQualityCuts")); return cut; } - if (!nameStr.compare("matchedQualityCutsMFTeta")) { + if (nameStr == "matchedQualityCutsMFTeta") { cut->AddCut(GetAnalysisCut("matchedQualityCutsMFTeta")); return cut; } - if (!nameStr.compare("muonQualityCuts5SigmaPDCA_Run3")) { + if (nameStr == "muonQualityCuts5SigmaPDCA_Run3") { cut->AddCut(GetAnalysisCut("muonQualityCuts5SigmaPDCA_Run3")); return cut; } - if (!nameStr.compare("muonLowPt5SigmaPDCA_Run3")) { + if (nameStr == "muonLowPt5SigmaPDCA_Run3") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("muonQualityCuts5SigmaPDCA_Run3")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("muonQualityCuts10SigmaPDCA_MCHMID")) { + if (nameStr == "muonQualityCuts10SigmaPDCA_MCHMID") { cut->AddCut(GetAnalysisCut("muonQualityCuts10SigmaPDCA")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("muonLowPt10SigmaPDCA")) { + if (nameStr == "muonLowPt1p5_MchMidmatching") { + cut->AddCut(GetAnalysisCut("muonLowPt1p5")); + cut->AddCut(GetAnalysisCut("matchedMchMid")); + return cut; + } + + if (nameStr == "muonLowPt1p7_MchMidmatching") { + cut->AddCut(GetAnalysisCut("muonLowPt1p7")); + cut->AddCut(GetAnalysisCut("matchedMchMid")); + return cut; + } + + if (nameStr == "muonLowPt6_MchMidmatching") { + cut->AddCut(GetAnalysisCut("muonLowPt6")); + cut->AddCut(GetAnalysisCut("matchedMchMid")); + return cut; + } + + if (nameStr == "muonLowPt10SigmaPDCA") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("muonQualityCuts10SigmaPDCA")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("muonLowPt210SigmaPDCA")) { + if (nameStr == "muonLowPt210SigmaPDCA") { cut->AddCut(GetAnalysisCut("muonLowPt2")); cut->AddCut(GetAnalysisCut("muonQualityCuts10SigmaPDCA")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("muonLowPt510SigmaPDCA")) { + if (nameStr == "muonLowPt510SigmaPDCA") { cut->AddCut(GetAnalysisCut("muonLowPt5")); cut->AddCut(GetAnalysisCut("muonQualityCuts10SigmaPDCA")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("muonLowPt610SigmaPDCA")) { + if (nameStr == "muonLowPt610SigmaPDCA") { cut->AddCut(GetAnalysisCut("muonLowPt6")); cut->AddCut(GetAnalysisCut("muonQualityCuts10SigmaPDCA")); cut->AddCut(GetAnalysisCut("MCHMID")); return cut; } - if (!nameStr.compare("muonLowPt")) { + if (nameStr == "muonLowPt") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonLowPt2")) { + if (nameStr == "muonLowPt2") { cut->AddCut(GetAnalysisCut("muonLowPt2")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonLowPt3")) { + if (nameStr == "muonLowPt3") { cut->AddCut(GetAnalysisCut("muonLowPt3")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonLowPt4")) { + if (nameStr == "muonLowPt4") { cut->AddCut(GetAnalysisCut("muonLowPt4")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonLowPt5")) { + if (nameStr == "muonLowPt5") { cut->AddCut(GetAnalysisCut("muonLowPt5")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonLowPt6")) { + if (nameStr == "muonLowPt6") { cut->AddCut(GetAnalysisCut("muonLowPt6")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonLowPtMatchingOnly")) { + if (nameStr == "muonLowPt1p5") { + cut->AddCut(GetAnalysisCut("muonLowPt1p5")); + cut->AddCut(GetAnalysisCut("muonQualityCuts")); + return cut; + } + + if (nameStr == "muonLowPt1p7") { + cut->AddCut(GetAnalysisCut("muonLowPt1p7")); + cut->AddCut(GetAnalysisCut("muonQualityCuts")); + return cut; + } + + if (nameStr == "muonLowPtMatchingOnly") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonLowPtMatchingOnly2")) { + if (nameStr == "muonLowPtMatchingOnly2") { cut->AddCut(GetAnalysisCut("muonLowPt2")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonLowPtMatchingOnly3")) { + if (nameStr == "muonLowPtMatchingOnly3") { cut->AddCut(GetAnalysisCut("muonLowPt3")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonLowPtMatchingOnly4")) { + if (nameStr == "muonLowPtMatchingOnly4") { cut->AddCut(GetAnalysisCut("muonLowPt4")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonLowPtMatchingOnly5")) { + if (nameStr == "muonLowPtMatchingOnly5") { cut->AddCut(GetAnalysisCut("muonLowPt5")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonLowPtMatchingOnly6")) { + if (nameStr == "muonLowPtMatchingOnly6") { cut->AddCut(GetAnalysisCut("muonLowPt6")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonHighPt")) { + if (nameStr == "muonHighPt") { cut->AddCut(GetAnalysisCut("muonHighPt")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonHighPt2")) { + if (nameStr == "muonHighPt2") { cut->AddCut(GetAnalysisCut("muonHighPt2")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonHighPt3")) { + if (nameStr == "muonHighPt3") { cut->AddCut(GetAnalysisCut("muonHighPt3")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonHighPt4")) { + if (nameStr == "muonHighPt4") { cut->AddCut(GetAnalysisCut("muonHighPt4")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonHighPt5")) { + if (nameStr == "muonHighPt5") { cut->AddCut(GetAnalysisCut("muonHighPt5")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonHighPt6")) { + if (nameStr == "muonHighPt6") { cut->AddCut(GetAnalysisCut("muonHighPt6")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonHighPtMatchingOnly2")) { + if (nameStr == "muonHighPtMatchingOnly2") { cut->AddCut(GetAnalysisCut("muonHighPt2")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonHighPtMatchingOnly3")) { + if (nameStr == "muonHighPtMatchingOnly3") { cut->AddCut(GetAnalysisCut("muonHighPt3")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonTightQualityCutsForTests")) { + if (nameStr == "muonTightQualityCutsForTests") { cut->AddCut(GetAnalysisCut("muonTightQualityCutsForTests")); return cut; } - if (!nameStr.compare("mchTrack")) { + if (nameStr == "mchTrack") { cut->AddCut(GetAnalysisCut("mchTrack")); return cut; } - if (!nameStr.compare("matchedMchMid")) { + if (nameStr == "matchedMchMid") { cut->AddCut(GetAnalysisCut("matchedMchMid")); return cut; } - if (!nameStr.compare("matchedFwd")) { + if (nameStr == "muonMchAndMchMid") { + cut->AddCut(GetAnalysisCut("muonMchAndMchMid")); + return cut; + } + + if (nameStr == "muonStreamCuts") { + cut->AddCut(GetAnalysisCut("muonMchAndMchMid")); + cut->AddCut(GetAnalysisCut("muonMinimalCuts10SigmaPDCA")); + return cut; + } + + if (nameStr == "matchedFwd") { cut->AddCut(GetAnalysisCut("matchedFwd")); return cut; } - if (!nameStr.compare("matchedGlobal")) { + if (nameStr == "matchedGlobal") { cut->AddCut(GetAnalysisCut("matchedGlobal")); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut1")) { + if (nameStr == "Chi2MCHMFTCut1") { cut->AddCut(GetAnalysisCut("Chi2MCHMFTCut1")); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut2")) { + if (nameStr == "Chi2MCHMFTCut2") { cut->AddCut(GetAnalysisCut("Chi2MCHMFTCut2")); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut3")) { + if (nameStr == "Chi2MCHMFTCut3") { cut->AddCut(GetAnalysisCut("Chi2MCHMFTCut3")); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut4")) { + if (nameStr == "Chi2MCHMFTCut4") { cut->AddCut(GetAnalysisCut("Chi2MCHMFTCut4")); return cut; } - if (!nameStr.compare("muonQualityCutsMUONStandalone")) { + if (nameStr == "muonQualityCutsMUONStandalone") { cut->AddCut(GetAnalysisCut("matchedMchMid")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } - if (!nameStr.compare("muonQualityCutsGlobal")) { + if (nameStr == "muonQualityCutsGlobal") { cut->AddCut(GetAnalysisCut("matchedGlobal")); cut->AddCut(GetAnalysisCut("muonQualityCuts")); return cut; } // ----------------------------------------------------------- // Pair cuts - if (!nameStr.compare("pairNoCut")) { + if (nameStr == "pairNoCut") { cut->AddCut(GetAnalysisCut("pairNoCut")); return cut; } - if (!nameStr.compare("pairMassLow1")) { + if (nameStr == "pairMassLow1") { cut->AddCut(GetAnalysisCut("pairMassLow1")); return cut; } - if (!nameStr.compare("pairMassLow2")) { + if (nameStr == "pairMassLow2") { cut->AddCut(GetAnalysisCut("pairMassLow2")); return cut; } - if (!nameStr.compare("pairPtLow3")) { + if (nameStr == "pairPtLow3") { cut->AddCut(GetAnalysisCut("pairPtLow3")); return cut; } - if (!nameStr.compare("pairPtLow4")) { + if (nameStr == "pairPtLow4") { cut->AddCut(GetAnalysisCut("pairPtLow4")); return cut; } - if (!nameStr.compare("pairPtLow5")) { + if (nameStr == "pairPtLow5") { cut->AddCut(GetAnalysisCut("pairPtLow5")); return cut; } - if (!nameStr.compare("pairMassLow3")) { + if (nameStr == "pairMassLow3") { cut->AddCut(GetAnalysisCut("pairMassLow3")); return cut; } - if (!nameStr.compare("pairMassLow4")) { + if (nameStr == "pairMassLow4") { cut->AddCut(GetAnalysisCut("pairMassLow4")); return cut; } - if (!nameStr.compare("pairMassLow5")) { + if (nameStr == "pairMassLow5") { cut->AddCut(GetAnalysisCut("pairMassLow5")); return cut; } - if (!nameStr.compare("pairMassLow6")) { + if (nameStr == "pairMassLow6") { cut->AddCut(GetAnalysisCut("pairMassLow6")); return cut; } - if (!nameStr.compare("pairMassLow7")) { + if (nameStr == "pairMassLow7") { cut->AddCut(GetAnalysisCut("pairMassLow7")); return cut; } - if (!nameStr.compare("pairMassLow8")) { + if (nameStr == "pairMassLow8") { cut->AddCut(GetAnalysisCut("pairMassLow8")); return cut; } - if (!nameStr.compare("pairMassLow9")) { + if (nameStr == "pairMassLow9") { cut->AddCut(GetAnalysisCut("pairMassLow9")); return cut; } - if (!nameStr.compare("pairMassLow10")) { + if (nameStr == "pairMassLow10") { cut->AddCut(GetAnalysisCut("pairMassLow10")); return cut; } - if (!nameStr.compare("pairMassLow11")) { + if (nameStr == "pairMassLow11") { cut->AddCut(GetAnalysisCut("pairMassLow11")); return cut; } - if (!nameStr.compare("pairMassLow12")) { + if (nameStr == "pairMassLow12") { cut->AddCut(GetAnalysisCut("pairMassLow12")); return cut; } - if (!nameStr.compare("pairMass1to2")) { + if (nameStr == "pairMass1to2") { cut->AddCut(GetAnalysisCut("pairMass1to2")); return cut; } - if (!nameStr.compare("pairMassIMR")) { + if (nameStr == "pairMassIMR") { cut->AddCut(GetAnalysisCut("pairMassIMR")); return cut; } - if (!nameStr.compare("pairMass1_5to2_7")) { + if (nameStr == "pairMass1_5to2_7") { cut->AddCut(GetAnalysisCut("pairMass1_5to2_7")); return cut; } - if (!nameStr.compare("pairMass1_3to3_5")) { + if (nameStr == "pairMass1_3to3_5") { cut->AddCut(GetAnalysisCut("pairMass1_3to3_5")); return cut; } - if (!nameStr.compare("pairMass1_3")) { + if (nameStr == "pairMass1_3") { cut->AddCut(GetAnalysisCut("pairMass1_3")); return cut; } - if (!nameStr.compare("pairMass1_5to3_5")) { + if (nameStr == "pairMass1_5to3_5") { cut->AddCut(GetAnalysisCut("pairMass1_5to3_5")); return cut; } - if (!nameStr.compare("pairDalitz1")) { + if (nameStr == "pairDalitz1") { cut->AddCut(GetAnalysisCut("pairDalitz1")); return cut; } - if (!nameStr.compare("pairDalitz1Strong")) { + if (nameStr == "pairDalitz1Strong") { cut->AddCut(GetAnalysisCut("pairDalitz1Strong")); return cut; } - if (!nameStr.compare("pairDalitz2")) { + if (nameStr == "pairDalitz2") { cut->AddCut(GetAnalysisCut("pairDalitz2")); return cut; } - if (!nameStr.compare("pairDalitz3")) { + if (nameStr == "pairDalitz3") { cut->AddCut(GetAnalysisCut("pairDalitz3")); return cut; } - if (!nameStr.compare("paira_prefilter1")) { + if (nameStr == "paira_prefilter1") { cut->AddCut(GetAnalysisCut("paira_prefilter1")); return cut; } - if (!nameStr.compare("paira_prefilter2")) { + if (nameStr == "paira_prefilter2") { cut->AddCut(GetAnalysisCut("paira_prefilter2")); return cut; } - if (!nameStr.compare("paira_prefilter3")) { + if (nameStr == "paira_prefilter3") { cut->AddCut(GetAnalysisCut("paira_prefilter3")); return cut; } - if (!nameStr.compare("paira_prefilter4")) { + if (nameStr == "paira_prefilter4") { cut->AddCut(GetAnalysisCut("paira_prefilter4")); return cut; } - if (!nameStr.compare("paira_prefilter5")) { + if (nameStr == "paira_prefilter5") { cut->AddCut(GetAnalysisCut("paira_prefilter5")); return cut; } - if (!nameStr.compare("paira_prefilter6")) { + if (nameStr == "paira_prefilter6") { cut->AddCut(GetAnalysisCut("paira_prefilter6")); return cut; } - if (!nameStr.compare("paira_prefilter7")) { + if (nameStr == "paira_prefilter7") { cut->AddCut(GetAnalysisCut("paira_prefilter7")); return cut; } - if (!nameStr.compare("pairb_prefilter1")) { + if (nameStr == "pairb_prefilter1") { cut->AddCut(GetAnalysisCut("pairb_prefilter1")); return cut; } - if (!nameStr.compare("pairb_prefilter2")) { + if (nameStr == "pairb_prefilter2") { cut->AddCut(GetAnalysisCut("pairb_prefilter2")); return cut; } - if (!nameStr.compare("pairb_prefilter3")) { + if (nameStr == "pairb_prefilter3") { cut->AddCut(GetAnalysisCut("pairb_prefilter3")); return cut; } - if (!nameStr.compare("pairb_prefilter4")) { + if (nameStr == "pairb_prefilter4") { cut->AddCut(GetAnalysisCut("pairb_prefilter4")); return cut; } - if (!nameStr.compare("pairb_prefilter5")) { + if (nameStr == "pairb_prefilter5") { cut->AddCut(GetAnalysisCut("pairb_prefilter5")); return cut; } - if (!nameStr.compare("pairb_prefilter6")) { + if (nameStr == "pairb_prefilter6") { cut->AddCut(GetAnalysisCut("pairb_prefilter6")); return cut; } - if (!nameStr.compare("pairb_prefilter7")) { + if (nameStr == "pairb_prefilter7") { cut->AddCut(GetAnalysisCut("pairb_prefilter7")); return cut; } - if (!nameStr.compare("pairc_prefilter1")) { + if (nameStr == "pairc_prefilter1") { cut->AddCut(GetAnalysisCut("pairc_prefilter1")); return cut; } - if (!nameStr.compare("pairc_prefilter2")) { + if (nameStr == "pairc_prefilter2") { cut->AddCut(GetAnalysisCut("pairc_prefilter2")); return cut; } - if (!nameStr.compare("pairc_prefilter3")) { + if (nameStr == "pairc_prefilter3") { cut->AddCut(GetAnalysisCut("pairc_prefilter3")); return cut; } - if (!nameStr.compare("pairc_prefilter4")) { + if (nameStr == "pairc_prefilter4") { cut->AddCut(GetAnalysisCut("pairc_prefilter4")); return cut; } - if (!nameStr.compare("pairc_prefilter5")) { + if (nameStr == "pairc_prefilter5") { cut->AddCut(GetAnalysisCut("pairc_prefilter5")); return cut; } - if (!nameStr.compare("pairc_prefilter6")) { + if (nameStr == "pairc_prefilter6") { cut->AddCut(GetAnalysisCut("pairc_prefilter6")); return cut; } - if (!nameStr.compare("pairc_prefilter7")) { + if (nameStr == "pairc_prefilter7") { cut->AddCut(GetAnalysisCut("pairc_prefilter7")); return cut; } - if (!nameStr.compare("paird_prefilter1")) { + if (nameStr == "paird_prefilter1") { cut->AddCut(GetAnalysisCut("paird_prefilter1")); return cut; } - if (!nameStr.compare("paire_prefilter1")) { + if (nameStr == "paire_prefilter1") { cut->AddCut(GetAnalysisCut("paire_prefilter1")); return cut; } - if (!nameStr.compare("pairf_prefilter1")) { + if (nameStr == "pairf_prefilter1") { cut->AddCut(GetAnalysisCut("pairf_prefilter1")); return cut; } - if (!nameStr.compare("pairg_prefilter1")) { + if (nameStr == "pairg_prefilter1") { cut->AddCut(GetAnalysisCut("pairg_prefilter1")); return cut; } - if (!nameStr.compare("pairh_prefilter1")) { + if (nameStr == "pairh_prefilter1") { cut->AddCut(GetAnalysisCut("pairh_prefilter1")); return cut; } - if (!nameStr.compare("pairi_prefilter1")) { + if (nameStr == "pairi_prefilter1") { cut->AddCut(GetAnalysisCut("pairi_prefilter1")); return cut; } - if (!nameStr.compare("pairJpsi")) { + if (nameStr == "pairJpsi") { cut->AddCut(GetAnalysisCut("pairJpsi")); return cut; } - if (!nameStr.compare("pairJpsi2")) { + if (nameStr == "pairJpsi2") { cut->AddCut(GetAnalysisCut("pairJpsi2")); return cut; } - if (!nameStr.compare("pairJpsi3")) { + if (nameStr == "pairJpsi3") { cut->AddCut(GetAnalysisCut("pairJpsi3")); return cut; } - if (!nameStr.compare("pairPsi2S")) { + if (nameStr == "pairPsi2S") { cut->AddCut(GetAnalysisCut("pairPsi2S")); return cut; } - if (!nameStr.compare("pairUpsilon")) { + if (nameStr == "pairUpsilon") { cut->AddCut(GetAnalysisCut("pairUpsilon")); return cut; } - if (!nameStr.compare("pairX3872Cut1")) { + if (nameStr == "pairX3872Cut1") { cut->AddCut(GetAnalysisCut("pairX3872")); return cut; } - if (!nameStr.compare("pairX3872Cut2")) { + if (nameStr == "pairX3872Cut2") { cut->AddCut(GetAnalysisCut("pairX3872_2")); return cut; } - if (!nameStr.compare("pairX3872Cut3")) { + if (nameStr == "pairX3872Cut3") { cut->AddCut(GetAnalysisCut("pairX3872_3")); return cut; } - if (!nameStr.compare("DipionPairCut1")) { + if (!nameStr.compare("pairX3872Minitree")) { + cut->AddCut(GetAnalysisCut("pairX3872_minitree")); + return cut; + } + + if (nameStr == "DipionPairCut1") { cut->AddCut(GetAnalysisCut("DipionMassCut1")); return cut; } - if (!nameStr.compare("DipionPairCut2")) { + if (nameStr == "DipionPairCut2") { cut->AddCut(GetAnalysisCut("DipionMassCut2")); return cut; } - if (!nameStr.compare("pairRapidityForward")) { + if (nameStr == "pairRapidityForward") { cut->AddCut(GetAnalysisCut("pairRapidityForward")); return cut; } - if (!nameStr.compare("pairJpsiLowPt1")) { + if (nameStr == "pairJpsiLowPt1") { cut->AddCut(GetAnalysisCut("pairJpsi")); cut->AddCut(GetAnalysisCut("pairPtLow1")); return cut; } - if (!nameStr.compare("pairJpsiLowPt2")) { + if (nameStr == "pairJpsiLowPt2") { cut->AddCut(GetAnalysisCut("pairJpsi")); cut->AddCut(GetAnalysisCut("pairPtLow2")); return cut; } - if (!nameStr.compare("pairCoherentRho0")) { + if (nameStr == "pairCoherentRho0") { cut->AddCut(GetAnalysisCut("pairPtLow3")); return cut; } - if (!nameStr.compare("pairD0")) { + if (nameStr == "pairD0") { cut->AddCut(GetAnalysisCut("pairD0")); return cut; } - if (!nameStr.compare("pairD0HighPt1")) { + if (nameStr == "pairD0Topology") { + cut->AddCut(GetAnalysisCut("pairD0Topology")); + return cut; + } + + if (nameStr == "pairD0TopologyTight") { + cut->AddCut(GetAnalysisCut("pairD0TopologyTight")); + return cut; + } + + if (nameStr == "pairD0HighPt1") { cut->AddCut(GetAnalysisCut("pairLxyzProjected3sigma")); cut->AddCut(GetAnalysisCut("pairPtLow5")); return cut; } - if (!nameStr.compare("pairD0HighPt2")) { + if (nameStr == "pairD0HighPt2") { cut->AddCut(GetAnalysisCut("pairTauxyzProjected1")); cut->AddCut(GetAnalysisCut("pairPtLow5")); return cut; } - if (!nameStr.compare("pairD0HighPt3")) { + if (nameStr == "pairD0HighPt3") { cut->AddCut(GetAnalysisCut("pairTauxyzProjected1sigma")); cut->AddCut(GetAnalysisCut("pairPtLow5")); return cut; } - if (!nameStr.compare("pairTauxyzProjected1")) { + if (nameStr == "pairTauxyzProjected1") { cut->AddCut(GetAnalysisCut("pairTauxyzProjected1")); return cut; } - if (!nameStr.compare("pairLxyProjected3sigmaLambdacCand")) { + if (nameStr == "pairLxyProjected3sigmaLambdacCand") { cut->AddCut(GetAnalysisCut("pairLxyProjected3sigmaLambdacCand")); return cut; } - if (!nameStr.compare("pairLxyProjected3sigmaDplusCand")) { + if (nameStr == "pairLxyProjected3sigmaDplusCand") { cut->AddCut(GetAnalysisCut("pairLxyProjected3sigmaDplusCand")); return cut; } - if (!nameStr.compare("pairCosPointingPos")) { + if (nameStr == "pairCosPointingPos") { cut->AddCut(GetAnalysisCut("pairCosPointingPos")); return cut; } - if (!nameStr.compare("pairCosPointingNeg90")) { + if (nameStr == "pairCosPointingNeg90") { cut->AddCut(GetAnalysisCut("pairCosPointingNeg90")); return cut; } - if (!nameStr.compare("pairCosPointingNeg85")) { + if (nameStr == "pairCosPointingNeg85") { cut->AddCut(GetAnalysisCut("pairCosPointingNeg85")); return cut; } - if (!nameStr.compare("pairTauxyzProjectedCosPointing1")) { + if (nameStr == "pairTauxyzProjectedCosPointing1") { cut->AddCut(GetAnalysisCut("pairCosPointingNeg")); cut->AddCut(GetAnalysisCut("pairTauxyzProjected1")); return cut; @@ -3605,122 +3771,122 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) // Below are a list of single electron single muon and in order or optimize the trigger // trigger selection cuts - if (!nameStr.compare("jpsiO2TriggerTestCuts_LooseNsigma")) { + if (nameStr == "jpsiO2TriggerTestCuts_LooseNsigma") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaOpen")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_LooseNsigma_corr")) { + if (nameStr == "jpsiO2TriggerTestCuts_LooseNsigma_corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug5")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_MediumNsigma")) { + if (nameStr == "jpsiO2TriggerTestCuts_MediumNsigma") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaOpen")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_MediumNsigma_corr")) { + if (nameStr == "jpsiO2TriggerTestCuts_MediumNsigma_corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug1")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_TightNsigma")) { + if (nameStr == "jpsiO2TriggerTestCuts_TightNsigma") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("electronPIDnsigma")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_TightNsigma_corr")) { + if (nameStr == "jpsiO2TriggerTestCuts_TightNsigma_corr") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_debug2")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_TPCPID1")) { + if (nameStr == "jpsiO2TriggerTestCuts_TPCPID1") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_TriggerTest1")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_TPCPID2")) { + if (nameStr == "jpsiO2TriggerTestCuts_TPCPID2") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_TriggerTest2")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_TPCPID3")) { + if (nameStr == "jpsiO2TriggerTestCuts_TPCPID3") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_TriggerTest3")); return cut; } - if (!nameStr.compare("jpsiO2TriggerTestCuts_TPCPID4")) { + if (nameStr == "jpsiO2TriggerTestCuts_TPCPID4") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronStandardQualityTriggerTest")); cut->AddCut(GetAnalysisCut("jpsi_TPCPID_TriggerTest4")); return cut; } - if (!nameStr.compare("emu_electron_test")) { + if (nameStr == "emu_electron_test") { cut->AddCut(GetAnalysisCut("jpsiStandardKine")); cut->AddCut(GetAnalysisCut("electronTrackQuality_Maolin")); cut->AddCut(GetAnalysisCut("electronPIDnsigmaEMu")); return cut; } - if (!nameStr.compare("muonLooseTriggerTestCuts")) { + if (nameStr == "muonLooseTriggerTestCuts") { cut->AddCut(GetAnalysisCut("muonLooseTriggerTestCuts")); return cut; } - if (!nameStr.compare("muonLooseTriggerTestCuts_LowPt")) { + if (nameStr == "muonLooseTriggerTestCuts_LowPt") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("muonLooseTriggerTestCuts")); return cut; } - if (!nameStr.compare("muonLooseTriggerTestCuts_HighPt2")) { + if (nameStr == "muonLooseTriggerTestCuts_HighPt2") { cut->AddCut(GetAnalysisCut("muonHighPt2")); cut->AddCut(GetAnalysisCut("muonLooseTriggerTestCuts")); return cut; } - if (!nameStr.compare("muonLooseTriggerTestCuts_HighPt3")) { + if (nameStr == "muonLooseTriggerTestCuts_HighPt3") { cut->AddCut(GetAnalysisCut("muonHighPt3")); cut->AddCut(GetAnalysisCut("muonLooseTriggerTestCuts")); return cut; } - if (!nameStr.compare("muonHighPtMatchingOnly2")) { + if (nameStr == "muonHighPtMatchingOnly2") { cut->AddCut(GetAnalysisCut("muonHighPt2")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonHighPtMatchingOnly3")) { + if (nameStr == "muonHighPtMatchingOnly3") { cut->AddCut(GetAnalysisCut("muonHighPt3")); cut->AddCut(GetAnalysisCut("muonQualityCutsMatchingOnly")); return cut; } - if (!nameStr.compare("muonMatchingMFTMCHTriggerTestCuts")) { + if (nameStr == "muonMatchingMFTMCHTriggerTestCuts") { cut->AddCut(GetAnalysisCut("muonMatchingMFTMCHTriggerTestCuts")); return cut; } - if (!nameStr.compare("muonMatchingMFTMCHTriggerTestCuts_LowPt")) { + if (nameStr == "muonMatchingMFTMCHTriggerTestCuts_LowPt") { cut->AddCut(GetAnalysisCut("muonLowPt")); cut->AddCut(GetAnalysisCut("muonMatchingMFTMCHTriggerTestCuts")); return cut; @@ -3729,184 +3895,238 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) //--------------------------------------------------------------- // ALICE 3 studies composite cuts - if (!nameStr.compare("alice3StandardKine")) { + if (nameStr == "alice3StandardKine") { cut->AddCut(GetAnalysisCut("alice3StandardKine")); return cut; } - if (!nameStr.compare("alice3KineSkim")) { + if (nameStr == "alice3KineSkim") { cut->AddCut(GetAnalysisCut("alice3KineSkim")); return cut; } - if (!nameStr.compare("alice3TrackQuality")) { + if (nameStr == "alice3TrackQuality") { cut->AddCut(GetAnalysisCut("alice3TrackQuality")); return cut; } - if (!nameStr.compare("alice3TrackQualityTightDCA")) { + if (nameStr == "alice3TrackQualityTightDCA") { cut->AddCut(GetAnalysisCut("alice3TrackQuality")); return cut; } - if (!nameStr.compare("alice3StandardTrack")) { + if (nameStr == "alice3StandardTrack") { cut->AddCut(GetAnalysisCut("alice3StandardKine")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); return cut; } - if (!nameStr.compare("alice3StandardTrackTOFAcceptance")) { + if (nameStr == "alice3StandardTrackTOFAcceptance") { cut->AddCut(GetAnalysisCut("alice3KineTOFAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); return cut; } - if (!nameStr.compare("alice3StandardTrackRICHAcceptance")) { + if (nameStr == "alice3StandardTrackRICHAcceptance") { cut->AddCut(GetAnalysisCut("alice3KineRICHAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); return cut; } - if (!nameStr.compare("alice3TightDCATrack")) { + if (nameStr == "alice3StandardTrackD0") { + cut->AddCut(GetAnalysisCut("alice3StandardKineD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3TOFEtaD0") { + cut->AddCut(GetAnalysisCut("alice3TOFEta")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3RICHEtaD0") { + cut->AddCut(GetAnalysisCut("alice3RICHEta")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3StandardTrackTOFAcceptanceD0") { + cut->AddCut(GetAnalysisCut("alice3KineTOFAcceptanceD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3StandardTrackRICHAcceptanceD0") { + cut->AddCut(GetAnalysisCut("alice3KineRICHAcceptanceD0")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3TightDCATrack") { cut->AddCut(GetAnalysisCut("alice3StandardKine")); cut->AddCut(GetAnalysisCut("alice3TrackQualityTightDCA")); return cut; } - if (!nameStr.compare("alice3TightDCATrackTOFAcceptance")) { + if (nameStr == "alice3TightDCATrackTOFAcceptance") { cut->AddCut(GetAnalysisCut("alice3KineTOFAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQualityTightDCA")); return cut; } - if (!nameStr.compare("alice3TightDCATrackRICHAcceptance")) { + if (nameStr == "alice3TightDCATrackRICHAcceptance") { cut->AddCut(GetAnalysisCut("alice3KineRICHAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQualityTightDCA")); return cut; } - if (!nameStr.compare("alice3iTOFPIDEl")) { + if (nameStr == "alice3iTOFPIDEl") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); return cut; } - if (!nameStr.compare("alice3iTOFPIDPi")) { + if (nameStr == "alice3iTOFPIDPi") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); return cut; } - if (!nameStr.compare("alice3iTOFPIDKa")) { + if (nameStr == "alice3iTOFPIDKa") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDKa")); return cut; } - if (!nameStr.compare("alice3iTOFPIDPr")) { + if (nameStr == "alice3iTOFPIDPr") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDPr")); return cut; } - if (!nameStr.compare("alice3oTOFPIDEl")) { + if (nameStr == "alice3oTOFPIDEl") { cut->AddCut(GetAnalysisCut("alice3oTOFPIDEl")); return cut; } - if (!nameStr.compare("alice3oTOFPIDPi")) { + if (nameStr == "alice3oTOFPIDPi") { cut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); return cut; } - if (!nameStr.compare("alice3oTOFPIDKa")) { + if (nameStr == "alice3oTOFPIDKa") { cut->AddCut(GetAnalysisCut("alice3oTOFPIDKa")); return cut; } - if (!nameStr.compare("alice3oTOFPIDPr")) { + if (nameStr == "alice3oTOFPIDPr") { cut->AddCut(GetAnalysisCut("alice3oTOFPIDPr")); return cut; } - if (!nameStr.compare("alice3FullTOFPIDEl")) { + if (nameStr == "alice3FullTOFPIDEl") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDEl")); return cut; } - if (!nameStr.compare("alice3FullTOFPIDPi")) { + if (nameStr == "alice3FullTOFPIDPi") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); return cut; } - if (!nameStr.compare("alice3FullTOFPIDKa")) { + if (nameStr == "alice3FullTOFPIDKa") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDKa")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDKa")); return cut; } - if (!nameStr.compare("alice3FullTOFPIDPr")) { + if (nameStr == "alice3FullTOFPIDPr") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDPr")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDPr")); return cut; } - if (!nameStr.compare("alice3RICHPIDEl")) { + if (nameStr == "alice3EitherTOFPIDPi") { + auto* pidCut = new AnalysisCompositeCut( + "alice3EitherTOFPIDPi", + "iTOF OR oTOF pion PID", + kFALSE); + + pidCut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); + pidCut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); + + delete cut; + return pidCut; + } + + if (nameStr == "alice3EitherTOFPIDKa") { + auto* pidCut = new AnalysisCompositeCut( + "alice3EitherTOFPIDKa", + "iTOF OR oTOF kaon PID", + kFALSE); + + pidCut->AddCut(GetAnalysisCut("alice3iTOFPIDKa")); + pidCut->AddCut(GetAnalysisCut("alice3oTOFPIDKa")); + + delete cut; + return pidCut; + } + + if (nameStr == "alice3RICHPIDEl") { cut->AddCut(GetAnalysisCut("alice3RICHPIDEl")); return cut; } - if (!nameStr.compare("alice3RICHPIDPi")) { + if (nameStr == "alice3RICHPIDPi") { cut->AddCut(GetAnalysisCut("alice3RICHPIDPi")); return cut; } - if (!nameStr.compare("alice3RICHPIDKa")) { + if (nameStr == "alice3RICHPIDKa") { cut->AddCut(GetAnalysisCut("alice3RICHPIDKa")); return cut; } - if (!nameStr.compare("alice3RICHPIDPr")) { + if (nameStr == "alice3RICHPIDPr") { cut->AddCut(GetAnalysisCut("alice3RICHPIDPr")); return cut; } - if (!nameStr.compare("alice3RICHTOFPIDEl")) { + if (nameStr == "alice3RICHTOFPIDEl") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDEl")); cut->AddCut(GetAnalysisCut("alice3RICHPIDEl")); return cut; } - if (!nameStr.compare("alice3RICHTOFPIDPi")) { + if (nameStr == "alice3RICHTOFPIDPi") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); cut->AddCut(GetAnalysisCut("alice3RICHPIDPi")); return cut; } - if (!nameStr.compare("alice3RICHTOFPIDKa")) { + if (nameStr == "alice3RICHTOFPIDKa") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDKa")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDKa")); cut->AddCut(GetAnalysisCut("alice3RICHPIDKa")); return cut; } - if (!nameStr.compare("alice3RICHTOFPIDPr")) { + if (nameStr == "alice3RICHTOFPIDPr") { cut->AddCut(GetAnalysisCut("alice3iTOFPIDPr")); cut->AddCut(GetAnalysisCut("alice3oTOFPIDPr")); cut->AddCut(GetAnalysisCut("alice3RICHPIDPr")); return cut; } - if (!nameStr.compare("alice3DielectronPID")) { + if (nameStr == "alice3DielectronPID") { cut->AddCut(GetAnalysisCut("alice3JpsiKine")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); - cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); - cut->AddCut(GetAnalysisCut("alice3oTOFPIDEl")); - cut->AddCut(GetAnalysisCut("alice3RICHPIDEl")); + cut->AddCut(GetAnalysisCut("alice3CharmoniumPID")); return cut; } - if (!nameStr.compare("alice3DielectronPIDTOFOnly")) { + if (nameStr == "alice3DielectronPIDTOFOnly") { cut->AddCut(GetAnalysisCut("alice3JpsiKineTOFAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); @@ -3914,7 +4134,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("alice3DielectronPIDRICHOnly")) { + if (nameStr == "alice3DielectronPIDRICHOnly") { cut->AddCut(GetAnalysisCut("alice3JpsiKineTOFAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); @@ -3922,7 +4142,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("alice3DielectronPIDTOFOnly")) { + if (nameStr == "alice3DielectronPIDTOFOnly") { cut->AddCut(GetAnalysisCut("alice3JpsiKine")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); @@ -3930,7 +4150,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("alice3DielectronPIDTOFAcceptance")) { + if (nameStr == "alice3DielectronPIDTOFAcceptance") { cut->AddCut(GetAnalysisCut("alice3JpsiKineTOFAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); @@ -3939,7 +4159,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } - if (!nameStr.compare("alice3DielectronPIDRICHAcceptance")) { + if (nameStr == "alice3DielectronPIDRICHAcceptance") { cut->AddCut(GetAnalysisCut("alice3JpsiKineRICHAcceptance")); cut->AddCut(GetAnalysisCut("alice3TrackQuality")); cut->AddCut(GetAnalysisCut("alice3iTOFPIDEl")); @@ -3948,6 +4168,38 @@ AnalysisCompositeCut* o2::aod::dqcuts::GetCompositeCut(const char* cutName) return cut; } + if (nameStr == "alice3LambdaCQualityCuts") { + cut->AddCut(GetAnalysisCut("alice3LambdaCKine")); + cut->AddCut(GetAnalysisCut("alice3TrackQuality")); + return cut; + } + + if (nameStr == "alice3TOFPIDPi") { + cut->AddCut(GetAnalysisCut("alice3iTOFPIDPi")); + cut->AddCut(GetAnalysisCut("alice3oTOFPIDPi")); + return cut; + } + + if (nameStr == "alice3TOFPIDKaRej") { + cut->AddCut(GetAnalysisCut("alice3D0KaonTOFPID")); + return cut; + } + + if (nameStr == "alice3TOFPIDPiRej") { + cut->AddCut(GetAnalysisCut("alice3D0PionTOFPID")); + return cut; + } + + if (nameStr == "alice3RICHTOFPIDPiRej") { + cut->AddCut(GetAnalysisCut("alice3D0PionRICHTOFPID")); + return cut; + } + + if (nameStr == "alice3RICHTOFPIDKaRej") { + cut->AddCut(GetAnalysisCut("alice3D0KaonRICHTOFPID")); + return cut; + } + delete cut; LOGF(fatal, Form("Did not find cut %s. Returning nullptr", cutName)); return nullptr; @@ -3958,50 +4210,50 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // // define here cuts which are likely to be used often // - AnalysisCut* cut = new AnalysisCut(cutName, cutName); + auto* cut = new AnalysisCut(cutName, cutName); std::string nameStr = cutName; // --------------------------------------------------------------- // Event cuts - if (!nameStr.compare("noEventCut")) { + if (nameStr == "noEventCut") { return cut; } - if (!nameStr.compare("eventNoTFBorder")) { + if (nameStr == "eventNoTFBorder") { cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventIsTVXTriggered")) { + if (nameStr == "eventIsTVXTriggered") { cut->AddCut(VarManager::kIsTVXTriggered, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventStandard")) { + if (nameStr == "eventStandard") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsINT7, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventStandardNoINT7")) { + if (nameStr == "eventStandardNoINT7") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); return cut; } - if (!nameStr.compare("eventStandardtest")) { + if (nameStr == "eventStandardtest") { cut->AddCut(VarManager::kVtxZ, -30.0, 30.0); return cut; } - if (!nameStr.compare("eventSel8")) { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder + if (nameStr == "eventSel8") { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventStandardSel8")) { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder + if (nameStr == "eventStandardSel8") { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventStandardSel8WithITSROFRecomputedCut")) { + if (nameStr == "eventStandardSel8WithITSROFRecomputedCut") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4009,14 +4261,14 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8NoTFBorder")) { // Redundant w.r.t. eventStandardSel8, to be removed + if (nameStr == "eventStandardSel8NoTFBorder") { // Redundant w.r.t. eventStandardSel8, to be removed cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventStandardSel8NoTFBNoITSROFB")) { // Redundant w.r.t. eventStandardSel8, to be removed + if (nameStr == "eventStandardSel8NoTFBNoITSROFB") { // Redundant w.r.t. eventStandardSel8, to be removed cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4024,7 +4276,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8NoTFBNoITSROFBrecomp")) { + if (nameStr == "eventStandardSel8NoTFBNoITSROFBrecomp") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4032,43 +4284,63 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventSel8NoSameBunch")) { + if (nameStr == "eventSel8NoSameBunch") { cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoSameBunch, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSel8TriggerZNAZNC")) { + if (nameStr == "eventSel8TriggerZNAZNC") { cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsTriggerZNAZNC, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSel8TriggerZNAZNCNoPileUp")) { + if (nameStr == "eventSel8TriggerZNAZNCNoPileUp") { cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsTriggerZNAZNC, 0.5, 1.5); cut->AddCut(VarManager::kIsNoSameBunch, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSel8NoSameBunchGoodZvtx")) { + if (nameStr == "eventSel8NoSameBunchGoodZvtx") { + cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); + cut->AddCut(VarManager::kIsNoSameBunch, 0.5, 1.5); + cut->AddCut(VarManager::kIsGoodZvtxFT0vsPV, 0.5, 1.5); + return cut; + } + + if (nameStr == "eventStandardSel8PbPbQuality") { + cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); + cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); + cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); + cut->AddCut(VarManager::kIsNoITSROFBorder, 0.5, 1.5); + cut->AddCut(VarManager::kIsNoSameBunch, 0.5, 1.5); + cut->AddCut(VarManager::kIsGoodZvtxFT0vsPV, 0.5, 1.5); + return cut; + } + + if (nameStr == "eventStandardSel8NoPileup") { + cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoSameBunch, 0.5, 1.5); cut->AddCut(VarManager::kIsGoodZvtxFT0vsPV, 0.5, 1.5); + cut->AddCut(VarManager::kNoCollInTimeRangeStandard, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventStandardSel8PbPbQuality")) { + if (nameStr == "eventStandardSel8PbPbQualityCent90") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); cut->AddCut(VarManager::kIsNoITSROFBorder, 0.5, 1.5); cut->AddCut(VarManager::kIsNoSameBunch, 0.5, 1.5); cut->AddCut(VarManager::kIsGoodZvtxFT0vsPV, 0.5, 1.5); + cut->AddCut(VarManager::kCentFT0C, 0.0, 90.0); return cut; } - if (!nameStr.compare("eventStandardSel8PbPbQualityGoodITSLayersAll")) { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder + if (nameStr == "eventStandardSel8PbPbQualityGoodITSLayersAll") { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4079,7 +4351,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8PbPbQualityTightTrackOccupancy")) { + if (nameStr == "eventStandardSel8PbPbQualityTightTrackOccupancy") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4091,7 +4363,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8PbPbQualityFirmTrackOccupancy")) { + if (nameStr == "eventStandardSel8PbPbQualityFirmTrackOccupancy") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4104,7 +4376,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8PbPbQualityLooseTrackOccupancy")) { + if (nameStr == "eventStandardSel8PbPbQualityLooseTrackOccupancy") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4117,7 +4389,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8PbPbQualityTightTrackOccupancyCollInTime")) { + if (nameStr == "eventStandardSel8PbPbQualityTightTrackOccupancyCollInTime") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4131,7 +4403,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8PbPbQualityTightTrackOccupancyCollInTime")) { + if (nameStr == "eventStandardSel8PbPbQualityTightTrackOccupancyCollInTime2") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4160,7 +4432,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) 50000.}; for (size_t icase = 0; icase < vecOccupancies.size() - 1; icase++) { - if (!nameStr.compare(Form("eventStandardSel8PbPbQualityTrackOccupancySlice%lu", icase))) { + if (nameStr == Form("eventStandardSel8PbPbQualityTrackOccupancySlice%lu", icase)) { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4175,7 +4447,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } for (size_t icase = 0; icase < vecOccupancies.size() - 1; icase++) { - if (!nameStr.compare(Form("eventStandardSel8PbPbQualityTrackOccupancySlice_0_%lu", icase))) { + if (nameStr == Form("eventStandardSel8PbPbQualityTrackOccupancySlice_0_%lu", icase)) { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4189,7 +4461,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } } - if (!nameStr.compare("eventStandardSel8ppQuality")) { + if (nameStr == "eventStandardSel8ppQuality") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4201,7 +4473,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8ppQualityNoVtxZ")) { + if (nameStr == "eventStandardSel8ppQualityNoVtxZ") { cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); cut->AddCut(VarManager::kIsNoITSROFBorder, 0.5, 1.5); @@ -4212,7 +4484,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8multAnalysis")) { + if (nameStr == "eventStandardSel8multAnalysis") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoTFBorder, 0.5, 1.5); @@ -4222,7 +4494,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8VtxQuality1")) { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder + if (nameStr == "eventStandardSel8VtxQuality1") { // kIsSel8 = kIsTriggerTVX && kNoITSROFrameBorder && kNoTimeFrameBorder cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kIsSel8, 0.5, 1.5); cut->AddCut(VarManager::kIsNoSameBunch, 0.5, 1.5); @@ -4231,15 +4503,15 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventStandardSel8PbPbMultCorr")) { - TF1* fMultPVCutLow = new TF1("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x - 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); + if (nameStr == "eventStandardSel8PbPbMultCorr") { + std::shared_ptr fMultPVCutLow = std::make_shared("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x - 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); fMultPVCutLow->SetParameters(3257.29, -121.848, 1.98492, -0.0172128, 6.47528e-05, 154.756, -1.86072, -0.0274713, 0.000633499, -3.37757e-06); - TF1* fMultPVCutHigh = new TF1("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x + 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); + std::shared_ptr fMultPVCutHigh = std::make_shared("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x + 3.5*([5]+[6]*x+[7]*x*x+[8]*x*x*x+[9]*x*x*x*x)", 0, 100); fMultPVCutHigh->SetParameters(3257.29, -121.848, 1.98492, -0.0172128, 6.47528e-05, 154.756, -1.86072, -0.0274713, 0.000633499, -3.37757e-06); - TF1* fMultCutLow = new TF1("fMultCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x - 2.*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); + std::shared_ptr fMultCutLow = std::make_shared("fMultCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x - 2.*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); fMultCutLow->SetParameters(1654.46, -47.2379, 0.449833, -0.0014125, 150.773, -3.67334, 0.0530503, -0.000614061, 3.15956e-06); - TF1* fMultCutHigh = new TF1("fMultCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x + 3.*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); + std::shared_ptr fMultCutHigh = std::make_shared("fMultCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x + 3.*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); fMultCutHigh->SetParameters(1654.46, -47.2379, 0.449833, -0.0014125, 150.773, -3.67334, 0.0530503, -0.000614061, 3.15956e-06); cut->AddCut(VarManager::kVtxNcontribReal, fMultPVCutLow, fMultPVCutHigh, false, VarManager::kCentFT0C, 0.0, 100.0, false); cut->AddCut(VarManager::kMultA, fMultCutLow, fMultCutHigh, false, VarManager::kCentFT0C, 0.0, 100.0, false); @@ -4250,165 +4522,165 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("eventDimuonStandard")) { + if (nameStr == "eventDimuonStandard") { cut->AddCut(VarManager::kIsMuonUnlikeLowPt7, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventMuonStandard")) { + if (nameStr == "eventMuonStandard") { cut->AddCut(VarManager::kIsMuonSingleLowPt7, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventTPCMultLow")) { + if (nameStr == "eventTPCMultLow") { cut->AddCut(VarManager::kMultTPC, 0, 50); return cut; } - if (!nameStr.compare("eventExclusivePair")) { + if (nameStr == "eventExclusivePair") { cut->AddCut(VarManager::kVtxNcontrib, 2, 2); return cut; } - if (!nameStr.compare("eventVtxNContrib")) { + if (nameStr == "eventVtxNContrib") { cut->AddCut(VarManager::kVtxNcontrib, 0, 10); return cut; } - if (!nameStr.compare("eventTPCMult3")) { + if (nameStr == "eventTPCMult3") { cut->AddCut(VarManager::kMultTPC, 3, 3); return cut; } - if (!nameStr.compare("int7vtxZ5")) { + if (nameStr == "int7vtxZ5") { cut->AddCut(VarManager::kVtxZ, -5.0, 5.0); cut->AddCut(VarManager::kIsINT7, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventDoubleGap")) { + if (nameStr == "eventDoubleGap") { cut->AddCut(VarManager::kIsDoubleGap, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSingleGap")) { + if (nameStr == "eventSingleGap") { cut->AddCut(VarManager::kIsSingleGap, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSingleGapA")) { + if (nameStr == "eventSingleGapA") { cut->AddCut(VarManager::kIsSingleGapA, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSingleGapAZDC")) { + if (nameStr == "eventSingleGapAZDC") { cut->AddCut(VarManager::kIsSingleGapA, 0.5, 1.5); cut->AddCut(VarManager::kEnergyCommonZNA, -1000., 1.); cut->AddCut(VarManager::kEnergyCommonZNC, 1., 1000.); return cut; } - if (!nameStr.compare("eventSingleGapC")) { + if (nameStr == "eventSingleGapC") { cut->AddCut(VarManager::kIsSingleGapC, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSingleGapCZDC")) { + if (nameStr == "eventSingleGapCZDC") { cut->AddCut(VarManager::kIsSingleGapC, 0.5, 1.5); cut->AddCut(VarManager::kEnergyCommonZNC, -1000., 1.); cut->AddCut(VarManager::kEnergyCommonZNA, 1., 1000.); return cut; } - if (!nameStr.compare("eventSingleGapACZDC")) { - AnalysisCompositeCut* cutA = new AnalysisCompositeCut("singleGapAZDC", "singleGapAZDC", kTRUE); + if (nameStr == "eventSingleGapACZDC") { + auto* cutA = new AnalysisCompositeCut("singleGapAZDC", "singleGapAZDC", kTRUE); cutA->AddCut(GetAnalysisCut("eventSingleGapAZDC")); - AnalysisCompositeCut* cutC = new AnalysisCompositeCut("singleGapCZDC", "singleGapCZDC", kTRUE); + auto* cutC = new AnalysisCompositeCut("singleGapCZDC", "singleGapCZDC", kTRUE); cutC->AddCut(GetAnalysisCut("eventSingleGapCZDC")); - AnalysisCompositeCut* cutAorC = new AnalysisCompositeCut("singleGapACZDC", "singleGapACZDC", kFALSE); + auto* cutAorC = new AnalysisCompositeCut("singleGapACZDC", "singleGapACZDC", kFALSE); cutAorC->AddCut(cutA); cutAorC->AddCut(cutC); return cutAorC; } - if (!nameStr.compare("eventUPCMode")) { + if (nameStr == "eventUPCMode") { cut->AddCut(VarManager::kIsITSUPCMode, 0.5, 1.5); return cut; } - if (!nameStr.compare("eventSingleGapACZDC_UPCMode")) { - AnalysisCompositeCut* cutA = new AnalysisCompositeCut("singleGapAZDC", "singleGapAZDC", kTRUE); + if (nameStr == "eventSingleGapACZDC_UPCMode") { + auto* cutA = new AnalysisCompositeCut("singleGapAZDC", "singleGapAZDC", kTRUE); cutA->AddCut(GetAnalysisCut("eventSingleGapAZDC")); cutA->AddCut(GetAnalysisCut("eventUPCMode")); - AnalysisCompositeCut* cutC = new AnalysisCompositeCut("singleGapCZDC", "singleGapCZDC", kTRUE); + auto* cutC = new AnalysisCompositeCut("singleGapCZDC", "singleGapCZDC", kTRUE); cutC->AddCut(GetAnalysisCut("eventSingleGapCZDC")); cutC->AddCut(GetAnalysisCut("eventUPCMode")); - AnalysisCompositeCut* cutAorC = new AnalysisCompositeCut("singleGapACZDC", "singleGapACZDC", kFALSE); + auto* cutAorC = new AnalysisCompositeCut("singleGapACZDC", "singleGapACZDC", kFALSE); cutAorC->AddCut(cutA); cutAorC->AddCut(cutC); return cutAorC; } - if (!nameStr.compare("eventXn0nTime")) { + if (nameStr == "eventXn0nTime") { cut->AddCut(VarManager::kTimeZNA, -2.0, 2.0); cut->AddCut(VarManager::kTimeZNC, -2.0, 2.0, true); return cut; } - if (!nameStr.compare("event0nXnTime")) { + if (nameStr == "event0nXnTime") { cut->AddCut(VarManager::kTimeZNA, -2.0, 2.0, true); cut->AddCut(VarManager::kTimeZNC, -2.0, 2.0); return cut; } - if (!nameStr.compare("event0n0nTime")) { + if (nameStr == "event0n0nTime") { cut->AddCut(VarManager::kTimeZNA, -2.0, 2.0, true); cut->AddCut(VarManager::kTimeZNC, -2.0, 2.0, true); return cut; } - if (!nameStr.compare("eventXnXnTime")) { + if (nameStr == "eventXnXnTime") { cut->AddCut(VarManager::kTimeZNA, -2.0, 2.0); cut->AddCut(VarManager::kTimeZNC, -2.0, 2.0); return cut; } // Event cuts based on centrality - if (!nameStr.compare("eventStandardNoINT7Cent090")) { + if (nameStr == "eventStandardNoINT7Cent090") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kCentFT0C, 0.0, 90.0); return cut; } - if (!nameStr.compare("eventStandardNoINT7Cent7090")) { + if (nameStr == "eventStandardNoINT7Cent7090") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kCentFT0C, 70.0, 90.0); return cut; } - if (!nameStr.compare("eventStandardNoINT7Cent5070")) { + if (nameStr == "eventStandardNoINT7Cent5070") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kCentFT0C, 50.0, 70.0); return cut; } - if (!nameStr.compare("eventStandardNoINT7Cent3050")) { + if (nameStr == "eventStandardNoINT7Cent3050") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kCentFT0C, 30.0, 50.0); return cut; } - if (!nameStr.compare("eventStandardNoINT7Cent1030")) { + if (nameStr == "eventStandardNoINT7Cent1030") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kCentFT0C, 10.0, 30.0); return cut; } - if (!nameStr.compare("eventStandardNoINT7Cent010")) { + if (nameStr == "eventStandardNoINT7Cent010") { cut->AddCut(VarManager::kVtxZ, -10.0, 10.0); cut->AddCut(VarManager::kCentFT0C, 0.0, 10.0); return cut; @@ -4416,174 +4688,174 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // --------------------------------------------------- // Barrel track kine cuts - if (!nameStr.compare("negTrack")) { + if (nameStr == "negTrack") { cut->AddCut(VarManager::kCharge, -99., 0.); return cut; } - if (!nameStr.compare("posTrack")) { + if (nameStr == "posTrack") { cut->AddCut(VarManager::kCharge, 0., 99.); return cut; } - if (!nameStr.compare("posEtaSel")) { + if (nameStr == "posEtaSel") { cut->AddCut(VarManager::kEta, 0., 0.8); return cut; } - if (!nameStr.compare("negEtaSel")) { + if (nameStr == "negEtaSel") { cut->AddCut(VarManager::kEta, -0.8, 0.); return cut; } - if (!nameStr.compare("etaSel")) { + if (nameStr == "etaSel") { cut->AddCut(VarManager::kEta, -0.8, 0.8); return cut; } - if (!nameStr.compare("pt02Sel")) { + if (nameStr == "pt02Sel") { cut->AddCut(VarManager::kPt, 0.2, 20.0); return cut; } - if (!nameStr.compare("pt04Sel")) { + if (nameStr == "pt04Sel") { cut->AddCut(VarManager::kPt, 0.4, 20.0); return cut; } - if (!nameStr.compare("openEtaSel")) { + if (nameStr == "openEtaSel") { cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("insideTPCsector")) { + if (nameStr == "insideTPCsector") { cut->AddCut(VarManager::kTrackIsInsideTPCModule, 0.5, 1.5); return cut; } - if (!nameStr.compare("rho0Kine")) { + if (nameStr == "rho0Kine") { cut->AddCut(VarManager::kPt, 0.1, 1000.0); cut->AddCut(VarManager::kEta, -1.1, 1.1); return cut; } - if (!nameStr.compare("pionQuality")) { + if (nameStr == "pionQuality") { cut->AddCut(VarManager::kTPCncls, 50.0, 1000.); return cut; } - if (!nameStr.compare("primaryVertexContributor")) { + if (nameStr == "primaryVertexContributor") { cut->AddCut(VarManager::kPVContributor, 0.5, 1.5); return cut; } - if (!nameStr.compare("jpsiStandardKine")) { + if (nameStr == "jpsiStandardKine") { cut->AddCut(VarManager::kPt, 1.0, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("jpsiStandardKine2")) { + if (nameStr == "jpsiStandardKine2") { cut->AddCut(VarManager::kPt, 0.9, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("jpsiStandardKine3")) { + if (nameStr == "jpsiStandardKine3") { cut->AddCut(VarManager::kP, 1.2, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("jpsiPIDcalibKine_posEta")) { + if (nameStr == "jpsiPIDcalibKine_posEta") { cut->AddCut(VarManager::kPin, 1.0, 1000.0); cut->AddCut(VarManager::kEta, 0.0, 0.9); return cut; } - if (!nameStr.compare("jpsiPIDcalibKine_negEta")) { + if (nameStr == "jpsiPIDcalibKine_negEta") { cut->AddCut(VarManager::kPin, 1.0, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.0); return cut; } - if (!nameStr.compare("jpsiStandardKine4")) { + if (nameStr == "jpsiStandardKine4") { cut->AddCut(VarManager::kP, 1.5, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("jpsiKineSkimmed")) { - cut->AddCut(VarManager::kPt, 0.7, 1000.0); + if (nameStr == "jpsiStandardKine5") { + cut->AddCut(VarManager::kP, 1.0, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("jpsiKineSkimmed")) { + if (nameStr == "jpsiKineSkimmed") { cut->AddCut(VarManager::kPt, 0.7, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("lmeePrefilterKine")) { + if (nameStr == "lmeePrefilterKine") { cut->AddCut(VarManager::kPt, 0., 20.0); cut->AddCut(VarManager::kEta, -1.2, 1.2); return cut; } - if (!nameStr.compare("lmeeStandardKine")) { + if (nameStr == "lmeeStandardKine") { cut->AddCut(VarManager::kPt, 0.2, 20.0); cut->AddCut(VarManager::kEta, -0.8, 0.8); return cut; } - if (!nameStr.compare("lmeeStandardKine_pt04")) { + if (nameStr == "lmeeStandardKine_pt04") { cut->AddCut(VarManager::kPt, 0.4, 20.0); cut->AddCut(VarManager::kEta, -0.8, 0.8); return cut; } - if (!nameStr.compare("lmeeLowBKine")) { + if (nameStr == "lmeeLowBKine") { cut->AddCut(VarManager::kPt, 0.075, 20.0); cut->AddCut(VarManager::kEta, -0.8, 0.8); return cut; } - if (!nameStr.compare("dalitzStandardKine")) { + if (nameStr == "dalitzStandardKine") { cut->AddCut(VarManager::kPt, 0.15, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("PIDStandardKine")) { + if (nameStr == "PIDStandardKine") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kPt, 1.0, 1000.0); return cut; } - if (!nameStr.compare("PIDStandardKine2")) { + if (nameStr == "PIDStandardKine2") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kPt, 0.1, 1000.0); return cut; } - if (!nameStr.compare("PIDStandardKine3")) { + if (nameStr == "PIDStandardKine3") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kPt, 0.5, 1000.0); return cut; } - if (!nameStr.compare("pTLow04")) { + if (nameStr == "pTLow04") { cut->AddCut(VarManager::kPt, 0.4, 1000.0); return cut; } - if (!nameStr.compare("pTLow03")) { + if (nameStr == "pTLow03") { cut->AddCut(VarManager::kPt, 0.3, 1000.0); return cut; } - if (!nameStr.compare("pTLow02")) { + if (nameStr == "pTLow02") { cut->AddCut(VarManager::kPt, 0.2, 1000.0); return cut; } @@ -4594,17 +4866,17 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // --------------------------------------------------- // MC generated particle acceptance cuts - if (!nameStr.compare("rapidity08")) { + if (nameStr == "rapidity08") { cut->AddCut(VarManager::kMCY, -0.8, 0.8); return cut; } - if (!nameStr.compare("rapidity09")) { + if (nameStr == "rapidity09") { cut->AddCut(VarManager::kMCY, -0.9, 0.9); return cut; } - if (!nameStr.compare("acceptance_pp13600")) { + if (nameStr == "acceptance_pp13600") { cut->AddCut(VarManager::kMCY, -0.8, 0.8); cut->AddCut(VarManager::kMCPt1, 1.0, 1000.0); cut->AddCut(VarManager::kMCPt2, 1.0, 1000.0); @@ -4613,7 +4885,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("acceptance_pp5360")) { + if (nameStr == "acceptance_pp5360") { cut->AddCut(VarManager::kMCY, -0.9, 0.9); cut->AddCut(VarManager::kMCPt1, 1.0, 1000.0); cut->AddCut(VarManager::kMCPt2, 1.0, 1000.0); @@ -4622,12 +4894,30 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } + if (nameStr == "acceptance_PbPb5360") { + cut->AddCut(VarManager::kMCY, -0.9, 0.9); + cut->AddCut(VarManager::kMCP1, 1.0, 1000.0); + cut->AddCut(VarManager::kMCP2, 1.0, 1000.0); + cut->AddCut(VarManager::kMCEta1, -0.9, 0.9); + cut->AddCut(VarManager::kMCEta2, -0.9, 0.9); + return cut; + } + + if (nameStr == "acceptance_PbPb5360_y08") { + cut->AddCut(VarManager::kMCY, -0.8, 0.8); + cut->AddCut(VarManager::kMCP1, 1.0, 1000.0); + cut->AddCut(VarManager::kMCP2, 1.0, 1000.0); + cut->AddCut(VarManager::kMCEta1, -0.8, 0.8); + cut->AddCut(VarManager::kMCEta2, -0.8, 0.8); + return cut; + } + // --------------------------------------------------- // MC generated particle acceptance cuts // Run 2 only - if (!nameStr.compare("highPtHadron")) { + if (nameStr == "highPtHadron") { cut->AddCut(VarManager::kPt, 4.0, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); @@ -4638,7 +4928,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("TightGlobalTrack")) { + if (nameStr == "TightGlobalTrack") { cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); cut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); @@ -4650,7 +4940,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronStandardQuality")) { + if (nameStr == "electronStandardQuality") { cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); cut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); cut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); @@ -4660,7 +4950,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronStandardQualityBenchmark")) { + if (nameStr == "electronStandardQualityBenchmark") { cut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); cut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); @@ -4671,7 +4961,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // Run 2 or run 3 - if (!nameStr.compare("jpsi_trackCut_debug")) { + if (nameStr == "jpsi_trackCut_debug") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 90., 159); @@ -4679,14 +4969,14 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_trackCut_noITSCuts_debug")) { + if (nameStr == "jpsi_trackCut_noITSCuts_debug") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 90., 159); return cut; } - if (!nameStr.compare("jpsi_trackCut_debug2")) { + if (nameStr == "jpsi_trackCut_debug2") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 90., 159); @@ -4697,7 +4987,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_trackCut_debug3")) { + if (nameStr == "jpsi_trackCut_debug3") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 90., 159); @@ -4711,7 +5001,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_trackCut_debug4")) { + if (nameStr == "jpsi_trackCut_debug4") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 90., 159); @@ -4722,7 +5012,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_trackCut_debug5")) { + if (nameStr == "jpsi_trackCut_debug5") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 70., 159); @@ -4730,7 +5020,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_trackCut_debug6")) { + if (nameStr == "jpsi_trackCut_debug6") { cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 120., 159); cut->AddCut(VarManager::kTPCnclsCR, 140., 159); @@ -4739,7 +5029,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_trackCut_debug")) { + if (nameStr == "lmee_trackCut_debug") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 80., 159); @@ -4747,7 +5037,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_skimming_cuts")) { + if (nameStr == "lmee_skimming_cuts") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); @@ -4756,7 +5046,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("trackQuality_compareDQEMframework")) { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ + if (nameStr == "trackQuality_compareDQEMframework") { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kITSncls, 4.5, 7.5); @@ -4767,7 +5057,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if ((!nameStr.compare("TightGlobalTrackRun3")) || (!nameStr.compare("lmeeQCTrackCuts"))) { + if ((nameStr == "TightGlobalTrackRun3") || (nameStr == "lmeeQCTrackCuts")) { cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); @@ -4791,45 +5081,45 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // loop to define PID cuts with and without post calibration for (size_t icase = 1; icase < vecTypetrack.size(); icase++) { - if (!nameStr.compare(Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data()))) { - if (icase == 1) { + if (nameStr == Form("lmeeQCTrackCuts%s", vecTypetrack.at(icase).Data())) { + if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kITSncls, 6.5, 7.5); cut->AddCut(VarManager::kTPCnclsCR, 80.0, 161.); cut->AddCut(VarManager::kTPCncls, 90.0, 170.); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kITSncls, 4.5, 7.5); - } else if (icase == 3) { + } else if (icase == 3) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kITSncls, 4.5, 7.5); cut->AddCut(VarManager::kHasTPC, -0.5, 0.5); - } else if (icase == 4) { + } else if (icase == 4) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCnclsCR, 80.0, 161.); cut->AddCut(VarManager::kTPCncls, 90.0, 170.); - } else if (icase == 5) { + } else if (icase == 5) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCnclsCR, 80.0, 161.); cut->AddCut(VarManager::kTPCncls, 90.0, 170.); cut->AddCut(VarManager::kHasITS, -0.5, 0.5); - } else if (icase == 6) { + } else if (icase == 6) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCnclsCR, 80.0, 161.); cut->AddCut(VarManager::kTPCncls, 90.0, 170.); cut->AddCut(VarManager::kHasTRD, -0.5, 0.5); - } else if (icase == 7) { + } else if (icase == 7) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kITSncls, 4.5, 7.5); cut->AddCut(VarManager::kTPCnclsCR, 80.0, 161.); cut->AddCut(VarManager::kTPCncls, 130.0, 170.); - } else if (icase == 8) { + } else if (icase == 8) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); @@ -4848,7 +5138,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } } - if (!nameStr.compare("LooseGlobalTrackRun3")) { + if (nameStr == "LooseGlobalTrackRun3") { cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kITSchi2, 0.0, 6.0); @@ -4857,7 +5147,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("TightGlobalTrackRun3_strongTPC")) { + if (nameStr == "TightGlobalTrackRun3_strongTPC") { cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); @@ -4867,40 +5157,40 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("TightTPCTrackRun3")) { + if (nameStr == "TightTPCTrackRun3") { cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCnclsCR, 80.0, 161.); return cut; } - if (!nameStr.compare("TightTPCTrack")) { + if (nameStr == "TightTPCTrack") { cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCnclsCR, 80.0, 161.); cut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); return cut; } - if (!nameStr.compare("SPDfirst")) { + if (nameStr == "SPDfirst") { cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); return cut; } - if (!nameStr.compare("noTPC")) { + if (nameStr == "noTPC") { cut->AddCut(VarManager::kHasTPC, -0.5, 0.5); return cut; } - if (!nameStr.compare("SPDany")) { + if (nameStr == "SPDany") { cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); return cut; } - if (!nameStr.compare("ITSiball")) { + if (nameStr == "ITSiball") { cut->AddCut(VarManager::kIsITSibAll, 0.5, 1.5); return cut; } - if (!nameStr.compare("ITSibany")) { + if (nameStr == "ITSibany") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); return cut; } @@ -4913,7 +5203,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) std::vector cutVar_TPCnCls = {80., 100., 80., 90., 90., 80., 80., 80., 80., 90., 100., 100., 80., 80., 80., 80., 100., 90., 100., 90., 90., 100., 100., 80., 100., 90., 90., 100., 90., 90.}; for (unsigned int i = 0; i < cutVar_ITSchi2.size(); i++) { - if (!nameStr.compare(Form("lmeeCutVarTrackCuts%i", i))) { + if (nameStr == Form("lmeeCutVarTrackCuts%i", i)) { cut->AddCut(VarManager::kIsSPDfirst, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, cutVar_ITSchi2.at(i)); cut->AddCut(VarManager::kTPCchi2, 0.0, cutVar_TPCchi2.at(i)); @@ -4924,66 +5214,72 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } } - if (!nameStr.compare("electronStandardQualityForO2MCdebug")) { + if (nameStr == "electronStandardQualityForO2MCdebug") { cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 70, 161.); return cut; } - if (!nameStr.compare("electronStandardQualityForO2MCdebug2")) { + if (nameStr == "electronStandardQualityForO2MCdebug2") { cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 100.0, 161.); return cut; } - if (!nameStr.compare("electronStandardQualityForO2MCdebug3")) { + if (nameStr == "electronStandardQualityForO2MCdebug3") { cut->AddCut(VarManager::kITSncls, 0.5, 10); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 70, 161.); return cut; } - if (!nameStr.compare("electronStandardQualityForO2MCdebug4")) { + if (nameStr == "electronStandardQualityForO2MCdebug4") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 70, 161.); return cut; } - if (!nameStr.compare("electronStandardQualityITSOnly")) { + if (nameStr == "electronStandardQualityITSOnly") { cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kITSncls, 3.5, 7.5); return cut; } - if (!nameStr.compare("electronStandardQualitybAnyITSOnly")) { + if (nameStr == "electronStandardQualitybAnyITSOnly") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kITSncls, 3.5, 7.5); return cut; } - if (!nameStr.compare("electronStandardQualityTPCOnly")) { + if (nameStr == "electronStandardQualityTPCOnly") { cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 70, 161.); return cut; } - if (!nameStr.compare("electronStandardQualityTPCOnly2")) { + if (nameStr == "electronStandardQualityTPCOnly2") { cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); cut->AddCut(VarManager::kTPCncls, 100, 161.); return cut; } - if (!nameStr.compare("NoelectronStandardQualityTPCOnly")) { + if (nameStr == "electronStandardQualityTPCOnly3") { + cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + cut->AddCut(VarManager::kTPCncls, 120, 161.); + return cut; + } + + if (nameStr == "NoelectronStandardQualityTPCOnly") { cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0, true, VarManager::kTPCncls, 70, 161.); return cut; } - if (!nameStr.compare("electronTrackQualitySkimmed")) { + if (nameStr == "electronTrackQualitySkimmed") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kTPCncls, 60, 161); @@ -4992,14 +5288,14 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronTrackQualitySkimmed2")) { + if (nameStr == "electronTrackQualitySkimmed2") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); cut->AddCut(VarManager::kTPCncls, 60, 161); return cut; } - if (!nameStr.compare("electronTrackQualitySkimmed3")) { + if (nameStr == "electronTrackQualitySkimmed3") { cut->AddCut(VarManager::kPt, 1.0, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); @@ -5008,7 +5304,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronTrackQuality_Maolin")) { + if (nameStr == "electronTrackQuality_Maolin") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kITSchi2, 0.0, 15.0); cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); @@ -5019,14 +5315,14 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("pionQualityCut1")) { + if (nameStr == "pionQualityCut1") { cut->AddCut(VarManager::kPt, 0.15, 1000.0); cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kTPCncls, 70, 161); return cut; } - if (!nameStr.compare("pionQualityCut2")) { + if (nameStr == "pionQualityCut2") { cut->AddCut(VarManager::kPt, 0.15, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); @@ -5035,7 +5331,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("protonPVcut")) { + if (nameStr == "protonPVcut") { cut->AddCut(VarManager::kTrackDCAxy, -0.1, 0.1); cut->AddCut(VarManager::kTrackDCAz, -0.15, 0.15); cut->AddCut(VarManager::kPt, 0.4, 3); @@ -5046,31 +5342,69 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("pidbasic")) { + if (nameStr == "pidbasic") { cut->AddCut(VarManager::kEta, -0.9, 0.9); cut->AddCut(VarManager::kTPCncls, 60, 161.); return cut; } - if (!nameStr.compare("standardPrimaryTrack")) { + if (nameStr == "standardPrimaryTrack") { cut->AddCut(VarManager::kTrackDCAxy, -1.0, 1.0); cut->AddCut(VarManager::kTrackDCAz, -3.0, 3.0); return cut; } - if (!nameStr.compare("trackDCA1cm")) { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ + if (nameStr == "trackDCA1cm") { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ cut->AddCut(VarManager::kTrackDCAxy, -1.0, 1.0); cut->AddCut(VarManager::kTrackDCAz, -1.0, 1.0); return cut; } - if (!nameStr.compare("dcaCut1_ionut")) { + if (nameStr == "electronPrimary_dca3sigma" || nameStr == "electronPrimary_dca7sigma") { + std::shared_ptr fDCAxyresLow = std::make_shared("fDCAxyresLow", "[0] + [1] * pow(x, -[2])", 0.1, 1000.); + std::shared_ptr fDCAzresLow = std::make_shared("fDCAzresLow", "[0] + [1] * pow(x, -[2])", 0.1, 1000.); + std::shared_ptr fDCAxyresUp = std::make_shared("fDCAxyresUp", "[0] + [1] * pow(x, -[2])", 0.1, 1000.); + std::shared_ptr fDCAzresUp = std::make_shared("fDCAzresUp", "[0] + [1] * pow(x, -[2])", 0.1, 1000.); + + if (nameStr == "electronPrimary_dca3sigma") { + // DCAxy and DCAz 3 sigma cut. DCA resolution vs pt extracted from fits of Users/m/mfaggin/test/inputsTrackTuner/pp2024/pass1_minBias/vsPhi (used for the track tuner) + // we add in addition a term for the misalignment of the mean of the distribution, which seems to be at most 20 mum for DCAxy and 10 mum for DCAz + fDCAxyresLow->SetParameters(-3 * 8.7e-4 - 20e-4, -3 * 25.4e-4, 0.79); // res is 8.7 + 25.4/pt^0.79 mum + fDCAzresLow->SetParameters(-3 * 9.4e-4 - 10e-4, -3 * 26.5e-4, 0.79); // res is 9.4 + 26.5/pt^0.79 mum + fDCAxyresUp->SetParameters(3 * 8.7e-4 + 20e-4, 3 * 25.4e-4, 0.79); // res is 8.7 + 25.4/pt^0.79 mum + fDCAzresUp->SetParameters(3 * 9.4e-4 + 10e-4, 3 * 26.5e-4, 0.79); // res is 9.4 + 26.5/pt^0.79 mum + cut->AddCut(VarManager::kTrackDCAxy, fDCAxyresLow, fDCAxyresUp, false, VarManager::kPt, 0.2, 1000.); + cut->AddCut(VarManager::kTrackDCAz, fDCAzresLow, fDCAzresUp, false, VarManager::kPt, 0.2, 1000.); + cut->AddCut(VarManager::kIsITSibFirst, 0.5, 1.5); + cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); + cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + cut->AddCut(VarManager::kTPCncls, 100, 161.); + return cut; + } + + if (nameStr == "electronPrimary_dca7sigma") { + // DCAxy and DCAz 7 sigma cut + fDCAxyresLow->SetParameters(-7 * 8.7e-4 - 20e-4, -7 * 25.4e-4, 0.79); + fDCAzresLow->SetParameters(-7 * 9.4e-4 - 10e-4, -7 * 26.5e-4, 0.79); + fDCAxyresUp->SetParameters(7 * 8.7e-4 + 20e-4, 7 * 25.4e-4, 0.79); + fDCAzresUp->SetParameters(7 * 9.4e-4 + 10e-4, 7 * 26.5e-4, 0.79); + cut->AddCut(VarManager::kTrackDCAxy, fDCAxyresLow, fDCAxyresUp, false, VarManager::kPt, 0.2, 1000.); + cut->AddCut(VarManager::kTrackDCAz, fDCAzresLow, fDCAzresUp, false, VarManager::kPt, 0.2, 1000.); + cut->AddCut(VarManager::kIsITSibFirst, 0.5, 1.5); + cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); + cut->AddCut(VarManager::kTPCchi2, 0.0, 4.0); + cut->AddCut(VarManager::kTPCncls, 100, 161.); + return cut; + } + } + + if (nameStr == "dcaCut1_ionut") { cut->AddCut(VarManager::kTrackDCAxy, -0.5, 0.5); cut->AddCut(VarManager::kTrackDCAz, -0.5, 0.5); return cut; } - if (!nameStr.compare("trackQuality_ionut")) { + if (nameStr == "trackQuality_ionut") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kTPCncls, 70, 161); cut->AddCut(VarManager::kITSchi2, 0.0, 5.0); @@ -5078,7 +5412,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("trackQualityTight_ionut")) { + if (nameStr == "trackQualityTight_ionut") { cut->AddCut(VarManager::kIsITSibAny, 0.5, 1.5); cut->AddCut(VarManager::kTPCncls, 100, 161); cut->AddCut(VarManager::kITSchi2, 0.0, 3.0); @@ -5087,13 +5421,13 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("kineJpsiEle_ionut")) { + if (nameStr == "kineJpsiEle_ionut") { cut->AddCut(VarManager::kP, 1.0, 15.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("pidJpsiEle0_ionut")) { + if (nameStr == "pidJpsiEle0_ionut") { cut->AddCut(VarManager::kTPCnSigmaEl, -2.0, 4.0, false, VarManager::kPin, 1.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 4.0, false, VarManager::kPin, 4.0, 150.0); cut->AddCut(VarManager::kTPCnSigmaEl, 98.1, 98.11, false, VarManager::kPin, 0.0, 1.0); @@ -5101,7 +5435,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("pidJpsiEle1_ionut")) { + if (nameStr == "pidJpsiEle1_ionut") { cut->AddCut(VarManager::kTPCnSigmaEl, -1.5, 4.0, false, VarManager::kPin, 1.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 4.0, false, VarManager::kPin, 4.0, 150.0); cut->AddCut(VarManager::kTPCnSigmaEl, 98.1, 98.11, false, VarManager::kPin, 0.0, 1.0); @@ -5109,129 +5443,144 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("pidJpsiEle2_ionut")) { + if (nameStr == "pidJpsiEle2_ionut") { cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, -4.0, 4.0, true); return cut; } - if (!nameStr.compare("pidJpsiEle3_ionut")) { + if (nameStr == "pidJpsiEle3_ionut") { cut->AddCut(VarManager::kTPCnSigmaEl, -0.5, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, -4.0, 4.0, true); return cut; } - if (!nameStr.compare("pidJpsiEle4_ionut")) { + if (nameStr == "pidJpsiEle4_ionut") { cut->AddCut(VarManager::kTPCnSigmaEl, 0.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, -4.0, 4.0, true); return cut; } - if (!nameStr.compare("pidJpsiEle5_ionut")) { + if (nameStr == "pidJpsiEle5_ionut") { cut->AddCut(VarManager::kTPCnSigmaEl, 0.5, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, -4.0, 4.0, true); return cut; } - if (!nameStr.compare("pidJpsiEle6_ionut")) { + if (nameStr == "pidJpsiEle6_ionut") { cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 4.0); cut->AddCut(VarManager::kTOFnSigmaEl, -1.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, -4.0, 4.0, true); return cut; } - if (!nameStr.compare("pidJpsiEle7_ionut")) { + if (nameStr == "pidJpsiEle7_ionut") { cut->AddCut(VarManager::kTOFnSigmaEl, -3.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 4.0); return cut; } - if (!nameStr.compare("pidJpsiEle8_ionut")) { + if (nameStr == "pidJpsiEle8_ionut") { cut->AddCut(VarManager::kTOFnSigmaEl, -3.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaEl, -1.5, 4.0); return cut; } - if (!nameStr.compare("pidJpsiEle9_ionut")) { + if (nameStr == "pidJpsiEle9_ionut") { cut->AddCut(VarManager::kTOFnSigmaEl, -3.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaEl, -2.0, 4.0); return cut; } + if (nameStr == "pidJpsi_TPCpion0") { + cut->AddCut(VarManager::kTPCnSigmaPi, 4.0, 1000.0); + return cut; + } + + if (nameStr == "pidJpsi_noTOF_prot") { + cut->AddCut(VarManager::kTPCnSigmaPr, 3.5, 1000.0, false, VarManager::kHasTOF, -0.5, 0.5); + return cut; + } + + if (nameStr == "pidJpsi_beta") { + cut->AddCut(VarManager::kTOFbeta, 0.98, 1.02, false, VarManager::kHasTOF, 0.5, 1.5); + return cut; + } + // Magnus cuts ---------------------------------------------------------- - if (!nameStr.compare("pidJpsi_magnus_ele1")) { + if (nameStr == "pidJpsi_magnus_ele1") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 4.0); return cut; } - if (!nameStr.compare("pidJpsi_magnus_ele2")) { + if (nameStr == "pidJpsi_magnus_ele2") { cut->AddCut(VarManager::kTPCnSigmaEl, -2.0, 4.0); return cut; } - if (!nameStr.compare("pidJpsi_magnus_ele3")) { + if (nameStr == "pidJpsi_magnus_ele3") { cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 4.0); return cut; } - if (!nameStr.compare("pidJpsi_magnus_prot1")) { + if (nameStr == "pidJpsi_magnus_prot1") { cut->AddCut(VarManager::kTPCnSigmaPr, 3.0, 1000.0); return cut; } - if (!nameStr.compare("pidJpsi_magnus_prot2")) { + if (nameStr == "pidJpsi_magnus_prot2") { cut->AddCut(VarManager::kTPCnSigmaPr, 3.5, 1000.0); return cut; } - if (!nameStr.compare("pidJpsi_magnus_pion1")) { + if (nameStr == "pidJpsi_magnus_pion1") { cut->AddCut(VarManager::kTPCnSigmaPi, 3.0, 1000.0); return cut; } - if (!nameStr.compare("pidJpsi_magnus_pion2")) { + if (nameStr == "pidJpsi_magnus_pion2") { cut->AddCut(VarManager::kTPCnSigmaPi, 3.5, 1000.0); return cut; } // ---------------------------------------------------------------------------------- - if (!nameStr.compare("standardPrimaryTrackDCAz")) { + if (nameStr == "standardPrimaryTrackDCAz") { cut->AddCut(VarManager::kTrackDCAxy, -3.0, 3.0); cut->AddCut(VarManager::kTrackDCAz, -1.0, 1.0); return cut; } - if (!nameStr.compare("standardPrimaryTrackDCA")) { + if (nameStr == "standardPrimaryTrackDCA") { cut->AddCut(VarManager::kTrackDCAxy, -0.1, 0.1); cut->AddCut(VarManager::kTrackDCAz, -0.15, 0.15); return cut; } - if (!nameStr.compare("PrimaryTrack_looseDCA")) { + if (nameStr == "PrimaryTrack_looseDCA") { cut->AddCut(VarManager::kTrackDCAxy, -3.0, 3.0); cut->AddCut(VarManager::kTrackDCAz, -3.0, 3.0); return cut; } - if (!nameStr.compare("tightPrimaryTrack")) { + if (nameStr == "tightPrimaryTrack") { cut->AddCut(VarManager::kTrackDCAsigXY, -3.0, 3.0); cut->AddCut(VarManager::kTrackDCAsigZ, -3.0, 3.0); return cut; } - if (!nameStr.compare("PrimaryTrack_DCA05")) { + if (nameStr == "PrimaryTrack_DCA05") { cut->AddCut(VarManager::kTrackDCAsigXY, -0.5, 0.5); cut->AddCut(VarManager::kTrackDCAsigZ, -0.5, 0.5); return cut; } - if (!nameStr.compare("PrimaryTrack_DCAz")) { + if (nameStr == "PrimaryTrack_DCAz") { cut->AddCut(VarManager::kTrackDCAz, -0.3, 0.3); return cut; } - if (!nameStr.compare("hasTOF")) { + if (nameStr == "hasTOF") { cut->AddCut(VarManager::kHasTOF, 0.5, 1.5); return cut; } - if (!nameStr.compare("noTOF")) { + if (nameStr == "noTOF") { cut->AddCut(VarManager::kHasTOF, -0.5, 0.5); return cut; } @@ -5239,34 +5588,34 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // ----------------------------------------------------- // V0 and Dalitz legs selections - for (int i = 1; i <= 8; i++) { - if (!nameStr.compare(Form("dalitzLeg%d", i))) { - cut->AddCut(VarManager::kIsDalitzLeg + i - 1, 0.5, 1.5); + for (int i = 1; i <= 8; i++) { // o2-linter: disable=magic-number (number of cuts) + if (nameStr == Form("dalitzLeg%d", i)) { + cut->AddCut(VarManager::kIsDalitzLeg + i - 1, -0.5, 0.5, true); return cut; } - if (!nameStr.compare(Form("notDalitzLeg%d", i))) { + if (nameStr == Form("notDalitzLeg%d", i)) { cut->AddCut(VarManager::kIsDalitzLeg + i - 1, -0.5, 0.5); return cut; } } - if (!nameStr.compare("pidcalib_ele")) { - cut->AddCut(VarManager::kIsLegFromGamma, 0.5, 1.5, false); + if (nameStr == "pidcalib_ele") { + cut->AddCut(VarManager::kIsLegFromGamma, -0.5, 0.5, true); return cut; } - if (!nameStr.compare("pidcalib_pion")) { - cut->AddCut(VarManager::kIsLegFromK0S, 0.5, 1.5, false); + if (nameStr == "pidcalib_pion") { + cut->AddCut(VarManager::kIsLegFromK0S, -0.5, 0.5, true); return cut; } - if (!nameStr.compare("pidcalib_proton")) { - cut->AddCut(VarManager::kIsProtonFromLambdaAndAntiLambda, 0.5, 1.5, false); + if (nameStr == "pidcalib_proton") { + cut->AddCut(VarManager::kIsProtonFromLambdaAndAntiLambda, -0.5, 0.5, true); return cut; } - if (!nameStr.compare("pidcalib_kaon")) { + if (nameStr == "pidcalib_kaon") { cut->AddCut(VarManager::kTOFnSigmaKa, -2.0, 2.0); cut->AddCut(VarManager::kTOFnSigmaPi, -2.0, 2.0, true); cut->AddCut(VarManager::kITSncls, 1.5, 7.5); @@ -5275,55 +5624,55 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // ------------------------------------------------ // Barrel PID cuts - if (!nameStr.compare("jpsi_TPCPID_debug1")) { + if (nameStr == "jpsi_TPCPID_debug1") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 2.5, 999); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, 2.5, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug2")) { + if (nameStr == "jpsi_TPCPID_debug2") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 3.0, 999); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, 3.0, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug3")) { + if (nameStr == "jpsi_TPCPID_debug3") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 3.5, 999); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, 3.5, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug4")) { + if (nameStr == "jpsi_TPCPID_debug4") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.0, 999); cut->AddCut(VarManager::kTPCnSigmaPr, 3.0, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug5")) { + if (nameStr == "jpsi_TPCPID_debug5") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -4.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 2.5, 999); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, 2.5, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug5_noCorr")) { + if (nameStr == "jpsi_TPCPID_debug5_noCorr") { cut->AddCut(VarManager::kTPCnSigmaEl, -4.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.5, 999); cut->AddCut(VarManager::kTPCnSigmaPr, 2.5, 999); return cut; } - if (!nameStr.compare("electronPIDLooseSkimmed")) { + if (nameStr == "electronPIDLooseSkimmed") { cut->AddCut(VarManager::kTPCnSigmaEl, -4.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.5, 999); cut->AddCut(VarManager::kTPCnSigmaPr, 2.5, 999); return cut; } - if (!nameStr.compare("electronPIDLooseSkimmed2")) { + if (nameStr == "electronPIDLooseSkimmed2") { cut->AddCut(VarManager::kTPCnSigmaEl, -4.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.5, 999, false, VarManager::kPin, 0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi, 1.5, 999, false, VarManager::kPin, 3.0, 999); @@ -5332,28 +5681,28 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronPIDLooseSkimmed3")) { + if (nameStr == "electronPIDLooseSkimmed3") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.0, 999, false, VarManager::kPin, 0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 3.0, 999, false, VarManager::kPin, 0, 3.0); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug6")) { + if (nameStr == "jpsi_TPCPID_debug6") { cut->AddCut(VarManager::kTPCnSigmaEl, -2.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.0, 999); cut->AddCut(VarManager::kTPCnSigmaPr, 3.0, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug7")) { + if (nameStr == "jpsi_TPCPID_debug7") { cut->AddCut(VarManager::kTPCnSigmaEl, -2.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.5, 999); cut->AddCut(VarManager::kTPCnSigmaPr, 3.5, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug8")) { + if (nameStr == "jpsi_TPCPID_debug8") { cut->AddCut(VarManager::kTPCnSigmaEl, -2.0, 3.0, false, VarManager::kPin, 0.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0, false, VarManager::kPin, 3.0, 999.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.0, 999, false, VarManager::kPin, 0.0, 3.0); @@ -5362,108 +5711,110 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug9")) { + if (nameStr == "jpsi_TPCPID_debug9") { cut->AddCut(VarManager::kTPCnSigmaEl, -2.5, 4.0); cut->AddCut(VarManager::kTPCnSigmaPi, 1.0, 999, false, VarManager::kPin, 3.0, 999.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.0, 999); return cut; } - if (!nameStr.compare("jpsi_TPCPID_debug10")) { + if (nameStr == "jpsi_TPCPID_debug10") { cut->AddCut(VarManager::kTPCnSigmaEl, -1.5, 2.0); cut->AddCut(VarManager::kTPCnSigmaPi, 4.0, 999); cut->AddCut(VarManager::kTPCnSigmaPr, 3.5, 999); return cut; } - if (!nameStr.compare("pidCut_lowP_Corr")) { + if (nameStr == "pidCut_lowP_Corr") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 3.0, false, VarManager::kP, 0.0, 5.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 3.0, 999, false, VarManager::kP, 0.0, 5.0); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, 2.5, 999, false, VarManager::kP, 0.0, 5.0); return cut; } - if (!nameStr.compare("EleInclusion_highP_Corr")) { + if (nameStr == "EleInclusion_highP_Corr") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -1.0, 4.0, false, VarManager::kP, 5.0, 999.0); return cut; } - if (!nameStr.compare("EleInclusion_highP2_Corr")) { + if (nameStr == "EleInclusion_highP2_Corr") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -0.5, 4.0, false, VarManager::kP, 5.0, 999.0); return cut; } - if (!nameStr.compare("PionExclusion_highP_Corr")) { + if (nameStr == "PionExclusion_highP_Corr") { cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 2.0, 999, false, VarManager::kP, 5.0, 999.0); return cut; } - if (!nameStr.compare("pidCut_lowP")) { + if (nameStr == "pidCut_lowP") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.0, 5.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.0, 999, false, VarManager::kP, 0.0, 5.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.5, 999, false, VarManager::kP, 0.0, 5.0); return cut; } - if (!nameStr.compare("EleInclusion_highP")) { + if (nameStr == "EleInclusion_highP") { cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 4.0, false, VarManager::kP, 5.0, 999.0); return cut; } - if (!nameStr.compare("EleInclusion_highP2")) { + if (nameStr == "EleInclusion_highP2") { cut->AddCut(VarManager::kTPCnSigmaEl, -0.5, 4.0, false, VarManager::kP, 5.0, 999.0); return cut; } - if (!nameStr.compare("PionExclusion_highP")) { + if (nameStr == "PionExclusion_highP") { cut->AddCut(VarManager::kTPCnSigmaPi, 2.0, 999, false, VarManager::kP, 5.0, 999.0); return cut; } - if (!nameStr.compare("lmee_TPCPID_debug1")) { + if (nameStr == "lmee_TPCPID_debug1") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -5.0, 5.0); return cut; } - TF1* cutLow1 = new TF1("cutLow1", "pol1", 0., 10.); - if (!nameStr.compare("electronPID1")) { - cutLow1->SetParameters(130., -40.0); - cut->AddCut(VarManager::kTPCsignal, 70., 100.); - cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); - return cut; - } + if (nameStr == "electronPID1" || nameStr == "electronPID1shiftUp" || nameStr == "electronPID1shiftDown" || nameStr == "electronPID2" || nameStr == "electronPID3") { + std::shared_ptr cutLow1 = std::make_shared("cutLow1", "pol1", 0., 10.); + if (nameStr == "electronPID1") { + cutLow1->SetParameters(130., -40.0); + cut->AddCut(VarManager::kTPCsignal, 70., 100.); + cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + return cut; + } - if (!nameStr.compare("electronPID1shiftUp")) { - cut->AddCut(VarManager::kTPCsignal, 70. - 0.85, 100. - 0.85); - cutLow1->SetParameters(130. - 0.85, -40.0); - cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0 - 0.85, false, VarManager::kPin, 0.5, 3.0); - return cut; - } + if (nameStr == "electronPID1shiftUp") { + cut->AddCut(VarManager::kTPCsignal, 70. - 0.85, 100. - 0.85); + cutLow1->SetParameters(130. - 0.85, -40.0); + cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0 - 0.85, false, VarManager::kPin, 0.5, 3.0); + return cut; + } - if (!nameStr.compare("electronPID1shiftDown")) { - cut->AddCut(VarManager::kTPCsignal, 70.0 + 0.85, 100.0 + 0.85); - cutLow1->SetParameters(130. + 0.85, -40.0); - cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0 + 0.85, false, VarManager::kPin, 0.5, 3.0); - return cut; - } + if (nameStr == "electronPID1shiftDown") { + cut->AddCut(VarManager::kTPCsignal, 70.0 + 0.85, 100.0 + 0.85); + cutLow1->SetParameters(130. + 0.85, -40.0); + cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0 + 0.85, false, VarManager::kPin, 0.5, 3.0); + return cut; + } - if (!nameStr.compare("electronPID2")) { - cutLow1->SetParameters(130., -40.0); - cut->AddCut(VarManager::kTPCsignal, 73., 100.); - cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); - return cut; - } + if (nameStr == "electronPID2") { + cutLow1->SetParameters(130., -40.0); + cut->AddCut(VarManager::kTPCsignal, 73., 100.); + cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + return cut; + } - if (!nameStr.compare("electronPID3")) { - cutLow1->SetParameters(130., -40.0); - cut->AddCut(VarManager::kTPCsignal, 60., 110.); - cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); - return cut; + if (nameStr == "electronPID3") { + cutLow1->SetParameters(130., -40.0); + cut->AddCut(VarManager::kTPCsignal, 60., 110.); + cut->AddCut(VarManager::kTPCsignal, cutLow1, 100.0, false, VarManager::kPin, 0.5, 3.0); + return cut; + } } - if (!nameStr.compare("electronPIDnsigma")) { + if (nameStr == "electronPIDnsigma") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 3.0, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.0, 3000.0); return cut; } - if (!nameStr.compare("lmee_commonDQEM_PID_TPC")) { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ + if (nameStr == "lmee_commonDQEM_PID_TPC") { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ cut->AddCut(VarManager::kTPCnSigmaEl, -2.5, 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -1e12, 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5471,7 +5822,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("lmee_commonDQEM_PID_TOF")) { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ + if (nameStr == "lmee_commonDQEM_PID_TOF") { // cut setting to check least common factor between reduced data sets of PWGEM and PWGDQ cut->AddCut(VarManager::kTPCnSigmaEl, -2.5, 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); @@ -5485,27 +5836,27 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // loop to define TPC PID cuts with and without post calibration for (size_t icase = 0; icase < vecPIDcase.size(); icase++) { - if (!nameStr.compare(Form("electronPIDOnly%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPIDOnly%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 3.0); } return cut; } - if (!nameStr.compare(Form("electronPID_TPCnsigma%s_loose", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TPCnsigma%s_loose", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5514,18 +5865,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TPCnsigma%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { // previously known as electronPID_TPCnsigma_tight + if (nameStr == Form("electronPID_TPCnsigma%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // previously known as electronPID_TPCnsigma_tight // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5534,18 +5885,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TPCnsigma%s_strongHadRej", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TPCnsigma%s_strongHadRej", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5554,18 +5905,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5574,20 +5925,20 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_lowB_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_lowB_TPCnsigma%s_strongNSigE", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5597,20 +5948,20 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_lowB_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_lowB_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5622,18 +5973,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TPCnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5644,18 +5995,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TPCnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5666,18 +6017,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TPCnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TPCnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, 0., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, 0., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, 0., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5699,18 +6050,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) std::vector cutVar_TPCnSigmaPr_up = {2., 2., 3., 2., 3., 3., 3., 2., 4., 3., 3., 4., 4., 3., 4., 4., 3., 4., 2., 3., 4., 4., 3., 4., 3., 2., 3., 3., 2., 3}; for (unsigned int i = 0; i < cutVar_TPCnSigmaEl_low.size(); i++) { - if (!nameStr.compare(Form("electronPID_TPCnsigma_cutVar%s%i", vecPIDcase.at(icase).Data(), i))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TPCnsigma_cutVar%s%i", vecPIDcase.at(icase).Data(), i)) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, cutVar_TPCnSigmaEl_low.at(i), cutVar_TPCnSigmaEl_up.at(i), false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, cutVar_TPCnSigmaPi_low.at(i), cutVar_TPCnSigmaPi_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, cutVar_TPCnSigmaKa_low.at(i), cutVar_TPCnSigmaKa_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, cutVar_TPCnSigmaPr_low.at(i), cutVar_TPCnSigmaPr_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, cutVar_TPCnSigmaEl_low.at(i), cutVar_TPCnSigmaEl_up.at(i), false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, cutVar_TPCnSigmaPi_low.at(i), cutVar_TPCnSigmaPi_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, cutVar_TPCnSigmaKa_low.at(i), cutVar_TPCnSigmaKa_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, cutVar_TPCnSigmaPr_low.at(i), cutVar_TPCnSigmaPr_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, cutVar_TPCnSigmaEl_low.at(i), cutVar_TPCnSigmaEl_up.at(i), false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, cutVar_TPCnSigmaPi_low.at(i), cutVar_TPCnSigmaPi_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, cutVar_TPCnSigmaKa_low.at(i), cutVar_TPCnSigmaKa_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); @@ -5720,18 +6071,18 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } } - if (!nameStr.compare(Form("lmee_pp_502TeV_TPC%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_TPC%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5740,20 +6091,20 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_lowB_TPC%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_lowB_TPC%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3.5, 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3.5, 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3.5, 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -3., 3., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5763,7 +6114,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_TPCloosenopkrej%s", vecPIDcase.at(icase).Data()))) { + if (nameStr == Form("lmee_pp_502TeV_TPCloosenopkrej%s", vecPIDcase.at(icase).Data())) { if (icase == 0) { cut->AddCut(VarManager::kTPCnSigmaEl, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 2.5, true, VarManager::kPin, 0.0, 1e+10, false); @@ -5774,29 +6125,29 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_TPCPbPbnopkrej%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_TPCPbPbnopkrej%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); } return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_TPCloose%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_TPCloose%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 2.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -2., 2., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -2., 2., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1) { + } else if (icase == 1) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 2.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -2., 2., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -2., 2., true, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 2) { + } else if (icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 2.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa_Corr, -2., 2., true, VarManager::kPin, 0.0, 1e+10, false); @@ -5806,21 +6157,21 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } } - if (!nameStr.compare("electronPIDnsigmaOpen")) { + if (nameStr == "electronPIDnsigmaOpen") { cut->AddCut(VarManager::kTPCnSigmaEl, -4.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.0, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.0, 3000.0); return cut; } - if (!nameStr.compare("electronPIDnsigmaVeryVeryLoose")) { + if (nameStr == "electronPIDnsigmaVeryVeryLoose") { cut->AddCut(VarManager::kTPCnSigmaEl, -4.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.5, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.0, 3000.0); return cut; } - if (!nameStr.compare("electronPIDnsigmaVeryLoose")) { + if (nameStr == "electronPIDnsigmaVeryLoose") { cut->AddCut(VarManager::kTPCnSigmaEl, -4.0, 4.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.5, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.5, 3000.0); @@ -5828,41 +6179,41 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronPIDnsigmaLoose")) { + if (nameStr == "electronPIDnsigmaLoose") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.5, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.5, 3000.0); return cut; } - if (!nameStr.compare("electronPIDnsigmaMedium")) { + if (nameStr == "electronPIDnsigmaMedium") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.7, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.7, 3000.0); return cut; } - if (!nameStr.compare("electronPIDnsigmaMedium_withLargeTOFPID")) { + if (nameStr == "electronPIDnsigmaMedium_withLargeTOFPID") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 2.7, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 2.7, 3000.0); cut->AddCut(VarManager::kTOFnSigmaEl, -5.0, 5.0); return cut; } - if (!nameStr.compare("electronPIDnsigmaSkewed")) { + if (nameStr == "electronPIDnsigmaSkewed") { cut->AddCut(VarManager::kTPCnSigmaEl, -2.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 3.5, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.5, 3000.0); return cut; } - if (!nameStr.compare("electronPIDnsigmaSkewed_2")) { + if (nameStr == "electronPIDnsigmaSkewed_2") { cut->AddCut(VarManager::kTPCnSigmaEl, -0.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 3.5, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.5, 3000.0); return cut; } - if (!nameStr.compare("electronPIDPrKaPiRej")) { + if (nameStr == "electronPIDPrKaPiRej") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, -3.0, 3.0, true); cut->AddCut(VarManager::kTPCnSigmaPi, -3.0, 3.0, true); @@ -5870,7 +6221,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronPIDPrKaPiRej_Corr")) { + if (nameStr == "electronPIDPrKaPiRej_Corr") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -3.0, 3.0, true); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3.0, 3.0, true); @@ -5878,7 +6229,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronPIDPrKaPiRejLoose")) { + if (nameStr == "electronPIDPrKaPiRejLoose") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, -2.0, 2.0, true); cut->AddCut(VarManager::kTPCnSigmaPi, -3.0, 3.0, true, VarManager::kPin, 0.0, 1.0, false); @@ -5887,7 +6238,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronPIDPrKaPiRejLoose_Corr")) { + if (nameStr == "electronPIDPrKaPiRejLoose_Corr") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr_Corr, -2.0, 2.0, true); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3.0, 3.0, true, VarManager::kPin, 0.0, 1.0, false); @@ -5896,7 +6247,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("electronPIDnsigmaEMu")) { + if (nameStr == "electronPIDnsigmaEMu") { cut->AddCut(VarManager::kTPCnSigmaEl, -1.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 3.5, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.5, 3000.0); @@ -5904,158 +6255,158 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("kaonPIDnsigma")) { + if (nameStr == "kaonPIDnsigma") { cut->AddCut(VarManager::kTPCnSigmaKa, -3.0, 3.0); return cut; } - if (!nameStr.compare("kaonRejNsigma")) { + if (nameStr == "kaonRejNsigma") { cut->AddCut(VarManager::kTPCnSigmaKa, -3.0, 3.0, true); return cut; } - if (!nameStr.compare("kaonPIDnsigma2")) { + if (nameStr == "kaonPIDnsigma2") { cut->AddCut(VarManager::kTPCnSigmaKa, -2.0, 2.0); return cut; } - if (!nameStr.compare("kaonPID_TPCnTOF")) { + if (nameStr == "kaonPID_TPCnTOF") { cut->AddCut(VarManager::kTPCnSigmaKa, -3.0, 3.0); cut->AddCut(VarManager::kTOFnSigmaKa, -3.0, 3.0); return cut; } - if (!nameStr.compare("kaonPIDnsigma700")) { + if (nameStr == "kaonPIDnsigma700") { cut->AddCut(VarManager::kTPCnSigmaKa, -3.0, 3.0); cut->AddCut(VarManager::kPin, 0.0, 0.7); return cut; } - if (!nameStr.compare("AssocKine")) { + if (nameStr == "AssocKine") { cut->AddCut(VarManager::kPt, 1.0, 1000.0); cut->AddCut(VarManager::kEta, -0.9, 0.9); return cut; } - if (!nameStr.compare("electronPIDworseRes")) { + if (nameStr == "electronPIDworseRes") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 3.0 * 0.8, 3000.0); // emulates a 20% degradation in PID resolution cut->AddCut(VarManager::kTPCnSigmaPi, 3.0 * 0.8, 3000.0); // proton and pion rejections are effectively relaxed by 20% return cut; } - if (!nameStr.compare("electronPIDshift")) { + if (nameStr == "electronPIDshift") { cut->AddCut(VarManager::kTPCnSigmaEl, -3.0, 3.0); cut->AddCut(VarManager::kTPCnSigmaPr, 3.0 - 0.2, 3000.0); cut->AddCut(VarManager::kTPCnSigmaPi, 3.0 - 0.2, 3000.0); return cut; } - if (!nameStr.compare("pionPIDnsigma")) { + if (nameStr == "pionPIDnsigma") { cut->AddCut(VarManager::kTPCnSigmaPi, -3.0, 3.0); return cut; } - if (!nameStr.compare("pionPID_TPCnTOF")) { + if (nameStr == "pionPID_TPCnTOF") { cut->AddCut(VarManager::kTPCnSigmaPi, -3.0, 3.0); cut->AddCut(VarManager::kTOFnSigmaPi, -3.0, 3.0); return cut; } - if (!nameStr.compare("protonPID_TPCnTOF")) { + if (nameStr == "protonPID_TPCnTOF") { cut->AddCut(VarManager::kTPCnSigmaPr, -3.0, 3.0); cut->AddCut(VarManager::kTOFnSigmaPr, -3.0, 3.0); return cut; } - if (!nameStr.compare("protonPID_TPCnTOF2")) { + if (nameStr == "protonPID_TPCnTOF2") { cut->AddCut(VarManager::kTPCnSigmaPr, -2.5, 2.5); return cut; } - if (!nameStr.compare("tpc_pion_rejection")) { - TF1* f1maxPi = new TF1("f1maxPi", "[0]+[1]*x", 0, 10); + if (nameStr == "tpc_pion_rejection") { + std::shared_ptr f1maxPi = std::make_shared("f1maxPi", "[0]+[1]*x", 0, 10); f1maxPi->SetParameters(85, -50); cut->AddCut(VarManager::kTPCsignal, 70, f1maxPi, true, VarManager::kPin, 0.0, 0.4, false); return cut; } - if (!nameStr.compare("tpc_pion_band_rejection")) { - TF1* f1minPi = new TF1("f1minPi", "[0]+[1]*log(x)", 0, 10); + if (nameStr == "tpc_pion_band_rejection") { + std::shared_ptr f1minPi = std::make_shared("f1minPi", "[0]+[1]*log(x)", 0, 10); f1minPi->SetParameters(-115, -90); - TF1* f1maxPi = new TF1("f1maxPi", "[0]+[1]*log(x)", 0, 10); + std::shared_ptr f1maxPi = std::make_shared("f1maxPi", "[0]+[1]*log(x)", 0, 10); f1maxPi->SetParameters(-70, -90); cut->AddCut(VarManager::kTPCsignal, f1minPi, f1maxPi, true, VarManager::kPin, 0.05, 0.3, false); return cut; } - if (!nameStr.compare("tpc_pion_muon_band_rejection")) { - TF1* f1minPi = new TF1("f1minPi", "[0]+exp([1]*x+[2])", 0, 10); + if (nameStr == "tpc_pion_muon_band_rejection") { + std::shared_ptr f1minPi = std::make_shared("f1minPi", "[0]+exp([1]*x+[2])", 0, 10); f1minPi->SetParameters(37, -18, 5.5); - TF1* f1maxPi = new TF1("f1maxPi", "[0]+exp([1]*x+[2])", 0, 10); + std::shared_ptr f1maxPi = std::make_shared("f1maxPi", "[0]+exp([1]*x+[2])", 0, 10); f1maxPi->SetParameters(67, -17, 5.9); cut->AddCut(VarManager::kTPCsignal, f1minPi, f1maxPi, true, VarManager::kPin, 0.0, 10, false); return cut; } - if (!nameStr.compare("tpc_pion_rejection_highp")) { - TF1* f1minPi = new TF1("f1minPi", "[0]+[1]*x", 0, 10); + if (nameStr == "tpc_pion_rejection_highp") { + std::shared_ptr f1minPi = std::make_shared("f1minPi", "[0]+[1]*x", 0, 10); f1minPi->SetParameters(65, 4.); cut->AddCut(VarManager::kTPCsignal, f1minPi, 110., false, VarManager::kPin, 0.0, 10, false); return cut; } - if (!nameStr.compare("tpc_kaon_rejection")) { - TF1* f1minKa = new TF1("f1minKa", "[0]+exp([1]*x+[2])", 0, 10); + if (nameStr == "tpc_kaon_rejection") { + std::shared_ptr f1minKa = std::make_shared("f1minKa", "[0]+exp([1]*x+[2])", 0, 10); f1minKa->SetParameters(37, -4, 5.6); - TF1* f1maxKa = new TF1("f1maxKa", "[0]+exp([1]*x+[2])", 0, 10); + std::shared_ptr f1maxKa = std::make_shared("f1maxKa", "[0]+exp([1]*x+[2])", 0, 10); f1maxKa->SetParameters(60, -4.1, 6.); cut->AddCut(VarManager::kTPCsignal, f1minKa, f1maxKa, true, VarManager::kPin, 0.0, 10.0, false); return cut; } - if (!nameStr.compare("tpc_proton_rejection")) { - TF1* f1minPr = new TF1("f1minPr", "[0]+exp([1]*x+[2])", 0, 10); + if (nameStr == "tpc_proton_rejection") { + std::shared_ptr f1minPr = std::make_shared("f1minPr", "[0]+exp([1]*x+[2])", 0, 10); f1minPr->SetParameters(37, -2.6, 6.1); - TF1* f1maxPr = new TF1("f1maxPr", "[0]+exp([1]*x+[2])", 0, 10); + std::shared_ptr f1maxPr = std::make_shared("f1maxPr", "[0]+exp([1]*x+[2])", 0, 10); f1maxPr->SetParameters(60, -2.4, 6.2); cut->AddCut(VarManager::kTPCsignal, f1minPr, f1maxPr, true, VarManager::kPin, 0.0, 10, false); return cut; } - if (!nameStr.compare("tpc_electron")) { + if (nameStr == "tpc_electron") { cut->AddCut(VarManager::kTPCsignal, 60, 110, false, VarManager::kPin, 0.0, 1e+10, false); return cut; } - if (!nameStr.compare("tof_electron")) { + if (nameStr == "tof_electron") { cut->AddCut(VarManager::kTOFbeta, 0.99, 1.01, false, VarManager::kPin, 0.0, 1e+10, false); return cut; } - if (!nameStr.compare("tof_electron_sigma")) { + if (nameStr == "tof_electron_sigma") { cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); return cut; } - if (!nameStr.compare("tof_electron_sigma_2")) { + if (nameStr == "tof_electron_sigma_2") { cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3.); return cut; } - if (!nameStr.compare("tof_electron_loose")) { + if (nameStr == "tof_electron_loose") { cut->AddCut(VarManager::kTOFbeta, 0.95, 1.05, false, VarManager::kPin, 0.0, 1e+10, false); return cut; } // loop to define TOF PID cuts with and without post calibration for (size_t icase = 0; icase < vecPIDcase.size(); icase++) { - if (!nameStr.compare(Form("electronPID_TOFnsigma%s_loose", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFnsigma%s_loose", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6063,12 +6414,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFnsigma%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { // previously known as electronPID_TOFnsigma_tight + if (nameStr == Form("electronPID_TOFnsigma%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // previously known as electronPID_TOFnsigma_tight // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6076,12 +6427,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFnsigma%s_strongHadRej", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFnsigma%s_strongHadRej", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6089,12 +6440,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6111,12 +6462,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) std::vector cutVar_TOFnSigmaEl_up = {4., 2., 4., 2., 4., 3., 2., 3., 3., 3., 4., 3., 2., 3., 4., 3., 3., 3., 4., 4., 2., 2., 2., 3., 3., 3., 2., 3., 2., 4}; for (unsigned int i = 0; i < cutVar_TOFnSigmaEl_low.size(); i++) { - if (!nameStr.compare(Form("electronPID_TOFnsigma_cutVar%s%i", vecPIDcase.at(icase).Data(), i))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFnsigma_cutVar%s%i", vecPIDcase.at(icase).Data(), i)) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, cutVar_TPCnSigmaEl_low.at(i), cutVar_TPCnSigmaEl_up.at(i), false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, cutVar_TPCnSigmaPi_low.at(i), cutVar_TPCnSigmaPi_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, cutVar_TOFnSigmaEl_low.at(i), cutVar_TOFnSigmaEl_up.at(i), false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, cutVar_TPCnSigmaEl_low.at(i), cutVar_TPCnSigmaEl_up.at(i), false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, cutVar_TPCnSigmaPi_low.at(i), cutVar_TPCnSigmaPi_up.at(i), true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, cutVar_TOFnSigmaEl_low.at(i), cutVar_TOFnSigmaEl_up.at(i), false, VarManager::kPin, 0.3, 1e+10, false); @@ -6125,14 +6476,14 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) } } - if (!nameStr.compare(Form("electronPID_TPC_TOFnsigma%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { // previously known as electronPID_TOFnsigma_tight + if (nameStr == Form("electronPID_TPC_TOFnsigma%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // previously known as electronPID_TOFnsigma_tight // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaKa, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); @@ -6142,13 +6493,13 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_lowB_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_lowB_TOFnsigma%s_strongNSigE", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.3, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.25, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.3, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.25, 1e+10, false); @@ -6157,13 +6508,13 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_lowB_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_lowB_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.3, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.25, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.3, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 4., true, VarManager::kPin, 0.25, 1e+10, false); @@ -6174,12 +6525,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFnsigma%s_strongNSigE_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6189,12 +6540,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFnsigma%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6204,12 +6555,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFnsigma%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, 0., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 1., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, 0., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 1., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6219,12 +6570,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFreq%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFreq%s_strongNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -1., 2., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -2., 2., false, VarManager::kPin, 0.0, 1e+10, false); @@ -6232,12 +6583,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("electronPID_TOFreq%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("electronPID_TOFreq%s_tightNSigEPbPb_rejBadTOF", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, 0., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 1., false, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, 0., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 4., true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 1., false, VarManager::kPin, 0.0, 1e+10, false); @@ -6245,12 +6596,12 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_TOF%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_TOF%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.3, 1e+10, false); @@ -6258,13 +6609,13 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_lowB_TOF%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_lowB_TOF%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3., 3., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaMu, -3., 3.5, true, VarManager::kPin, 0.0, 1e+10, false); @@ -6273,13 +6624,13 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_TOFloose%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_TOFloose%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 3., true, VarManager::kPin, 0.0, 4.0, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 2.5, true, VarManager::kPin, 4.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -4., 4., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 3., true, VarManager::kPin, 0.0, 4.0, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 2.5, true, VarManager::kPin, 4.0, 1e+10, false); @@ -6288,13 +6639,13 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare(Form("lmee_pp_502TeV_TOFloose_pionrej%s", vecPIDcase.at(icase).Data()))) { - if (icase == 0) { + if (nameStr == Form("lmee_pp_502TeV_TOFloose_pionrej%s", vecPIDcase.at(icase).Data())) { + if (icase == 0) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 3.5, true, VarManager::kPin, 0.0, 2.0, false); cut->AddCut(VarManager::kTPCnSigmaPi, -99., 2.5, true, VarManager::kPin, 2.0, 1e+10, false); cut->AddCut(VarManager::kTOFnSigmaEl, -4., 4., false, VarManager::kPin, 0.3, 1e+10, false); - } else if (icase == 1 || icase == 2) { + } else if (icase == 1 || icase == 2) { // o2-linter: disable=magic-number (number of cuts) cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -4., 4., false, VarManager::kPin, 0.0, 1e+10, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 3.5, true, VarManager::kPin, 0.0, 2.0, false); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, -99., 2.5, true, VarManager::kPin, 2.0, 1e+10, false); @@ -6306,27 +6657,32 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // ------------------------------------------------------------------------------------------------- // Muon cuts - if (!nameStr.compare("GlobalMuonTrack")) { + if (nameStr == "GlobalMuonTrack") { cut->AddCut(VarManager::kMuonTrackType, -0.5, 0.5); return cut; } - if (!nameStr.compare("MFTMCH")) { + if (nameStr == "MFTMCH") { cut->AddCut(VarManager::kMuonTrackType, 1.5, 2.5); return cut; } - if (!nameStr.compare("MCHMID")) { + if (nameStr == "MCHMID") { cut->AddCut(VarManager::kMuonTrackType, 2.5, 3.5); return cut; } - if (!nameStr.compare("MCHStandalone")) { + if (nameStr == "MCHStandalone") { cut->AddCut(VarManager::kMuonTrackType, 3.5, 4.5); return cut; } - if (!nameStr.compare("muonMinimalCuts")) { + if (nameStr == "muonEtaCut") { + cut->AddCut(VarManager::kEta, -4.0, -2.5); + return cut; + } + + if (nameStr == "muonMinimalCuts") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); cut->AddCut(VarManager::kMuonPDca, 0.0, 594.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6334,7 +6690,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("muonMinimalCuts10SigmaPDCA")) { + if (nameStr == "muonMinimalCuts10SigmaPDCA") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); cut->AddCut(VarManager::kMuonPDca, 0.0, 990.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6342,7 +6698,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("muonQualityCuts")) { + if (nameStr == "muonQualityCuts") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); cut->AddCut(VarManager::kMuonPDca, 0.0, 594.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6352,7 +6708,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("muonQualityCuts5SigmaPDCA_Run3")) { + if (nameStr == "muonQualityCuts5SigmaPDCA_Run3") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); cut->AddCut(VarManager::kMuonPDca, 0.0, 500.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6362,7 +6718,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("muonQualityCuts10SigmaPDCA")) { + if (nameStr == "muonQualityCuts10SigmaPDCA") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); cut->AddCut(VarManager::kMuonPDca, 0.0, 990.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6372,7 +6728,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("matchedQualityCuts")) { + if (nameStr == "matchedQualityCuts") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); cut->AddCut(VarManager::kMuonPDca, 0.0, 594.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6383,7 +6739,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("matchedQualityCutsMFTeta")) { + if (nameStr == "matchedQualityCutsMFTeta") { cut->AddCut(VarManager::kEta, -3.6, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 17.6, 89.5); cut->AddCut(VarManager::kMuonPDca, 0.0, 594.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6394,74 +6750,84 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("muonQualityCutsMatchingOnly")) { + if (nameStr == "muonQualityCutsMatchingOnly") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonChi2, 0.0, 1e6); cut->AddCut(VarManager::kMuonChi2MatchMCHMID, 0.0, 1e6); // matching MCH-MID return cut; } - if (!nameStr.compare("muonLowPt")) { + if (nameStr == "muonLowPt") { cut->AddCut(VarManager::kPt, 0.5, 1000.0); return cut; } - if (!nameStr.compare("muonLowPt2")) { + if (nameStr == "muonLowPt2") { cut->AddCut(VarManager::kPt, 0.7, 1000.0); return cut; } - if (!nameStr.compare("muonLowPt3")) { + if (nameStr == "muonLowPt3") { cut->AddCut(VarManager::kPt, 0.8, 1000.0); return cut; } - if (!nameStr.compare("muonLowPt4")) { + if (nameStr == "muonLowPt4") { cut->AddCut(VarManager::kPt, 0.9, 1000.0); return cut; } - if (!nameStr.compare("muonLowPt5")) { + if (nameStr == "muonLowPt5") { cut->AddCut(VarManager::kPt, 1.0, 1000.0); return cut; } - if (!nameStr.compare("muonLowPt6")) { + if (nameStr == "muonLowPt6") { cut->AddCut(VarManager::kPt, 2.0, 1000.0); return cut; } - if (!nameStr.compare("muonHighPt")) { + if (nameStr == "muonLowPt1p5") { + cut->AddCut(VarManager::kPt, 1.5, 1000.0); + return cut; + } + + if (nameStr == "muonLowPt1p7") { + cut->AddCut(VarManager::kPt, 1.7, 1000.0); + return cut; + } + + if (nameStr == "muonHighPt") { cut->AddCut(VarManager::kPt, 3.0, 1000.0); return cut; } - if (!nameStr.compare("muonHighPt2")) { + if (nameStr == "muonHighPt2") { cut->AddCut(VarManager::kPt, 4.0, 1000.0); return cut; } - if (!nameStr.compare("muonHighPt3")) { + if (nameStr == "muonHighPt3") { cut->AddCut(VarManager::kPt, 6.0, 1000.0); return cut; } - if (!nameStr.compare("muonHighPt4")) { + if (nameStr == "muonHighPt4") { cut->AddCut(VarManager::kPt, 8.0, 1000.0); return cut; } - if (!nameStr.compare("muonHighPt5")) { + if (nameStr == "muonHighPt5") { cut->AddCut(VarManager::kPt, 10.0, 1000.0); return cut; } - if (!nameStr.compare("muonHighPt6")) { + if (nameStr == "muonHighPt6") { cut->AddCut(VarManager::kPt, 20.0, 1000.0); return cut; } - if (!nameStr.compare("muonTightQualityCutsForTests")) { + if (nameStr == "muonTightQualityCutsForTests") { cut->AddCut(VarManager::kEta, -4.0, -2.5); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 20.0, 60.0); cut->AddCut(VarManager::kMuonPDca, 0.0, 594.0, false, VarManager::kMuonRAtAbsorberEnd, 17.6, 26.5); @@ -6470,371 +6836,405 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("mchTrack")) { + if (nameStr == "mchTrack") { cut->AddCut(VarManager::kMuonTrackType, 3.5, 4.5); return cut; } - if (!nameStr.compare("matchedMchMid")) { + if (nameStr == "matchedMchMid") { cut->AddCut(VarManager::kMuonTrackType, 2.5, 3.5); return cut; } - if (!nameStr.compare("matchedFwd")) { + if (nameStr == "muonMchAndMchMid") { + cut->AddCut(VarManager::kMuonTrackType, 2.5, 4.5); // selecting both MCH and MCH-MID tracks + return cut; + } + + if (nameStr == "matchedFwd") { cut->AddCut(VarManager::kMuonTrackType, 1.5, 2.5); return cut; } - if (!nameStr.compare("matchedGlobal")) { + if (nameStr == "matchedGlobal") { cut->AddCut(VarManager::kMuonTrackType, -0.5, 0.5); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut1")) { + if (nameStr == "Chi2MCHMFTCut1") { cut->AddCut(VarManager::kMuonChi2MatchMCHMFT, 0, 30); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut2")) { + if (nameStr == "Chi2MCHMFTCut2") { cut->AddCut(VarManager::kMuonChi2MatchMCHMFT, 0, 40); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut3")) { + if (nameStr == "Chi2MCHMFTCut3") { cut->AddCut(VarManager::kMuonChi2MatchMCHMFT, 0, 50); return cut; } - if (!nameStr.compare("Chi2MCHMFTCut4")) { + if (nameStr == "Chi2MCHMFTCut4") { cut->AddCut(VarManager::kMuonChi2MatchMCHMFT, 0, 60); return cut; } // ----------------------------------------------------------------------------------------------- // Pair cuts - if (!nameStr.compare("pairDalitz1")) { + if (nameStr == "pairDalitz1") { cut->AddCut(VarManager::kMass, 0.0, 0.015, false, VarManager::kPt, 0., 1.); cut->AddCut(VarManager::kMass, 0.0, 0.035, false, VarManager::kPt, 0., 1., true); - TF1* fcutHigh = new TF1("f1", "[0] - [0]/[1]*x", -1.5, 1.5); + std::shared_ptr fcutHigh = std::make_shared("f1", "[0] - [0]/[1]*x", -1.5, 1.5); fcutHigh->SetParameters(0.6, 0.12); - TF1* fcutLow = new TF1("f2", "-[0] + [0]/[1]*x", -1.5, 1.5); + std::shared_ptr fcutLow = std::make_shared("f2", "-[0] + [0]/[1]*x", -1.5, 1.5); fcutLow->SetParameters(0.6, 0.12); cut->AddCut(VarManager::kPsiPair, fcutLow, fcutHigh, true, VarManager::kDeltaPhiPair, 0, 0.12); return cut; } - if (!nameStr.compare("pairDalitz1Strong")) { + if (nameStr == "pairDalitz1Strong") { cut->AddCut(VarManager::kMass, 0.0, 0.015, false, VarManager::kPt, 0., 1.); cut->AddCut(VarManager::kMass, 0.0, 0.035, false, VarManager::kPt, 0., 1., true); cut->AddCut(VarManager::kDeltaPhiPair, -1., 0.); return cut; } - if (!nameStr.compare("pairDalitz2")) { + if (nameStr == "pairDalitz2") { cut->AddCut(VarManager::kMass, 0.0, 0.015, false, VarManager::kPt, 0., 1.); cut->AddCut(VarManager::kMass, 0.0, 0.035, false, VarManager::kPt, 0., 1., true); return cut; } - if (!nameStr.compare("pairDalitz3")) { + if (nameStr == "pairDalitz3") { cut->AddCut(VarManager::kMass, 0.0, 0.15); return cut; } - if (!nameStr.compare("paira_prefilter1")) { + if (nameStr == "paira_prefilter1") { cut->AddCut(VarManager::kMass, 0.0, 0.06); return cut; } - if (!nameStr.compare("paira_prefilter2")) { + if (nameStr == "paira_prefilter2") { cut->AddCut(VarManager::kMass, 0.0, 0.06); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.025); return cut; } - if (!nameStr.compare("paira_prefilter3")) { + if (nameStr == "paira_prefilter3") { cut->AddCut(VarManager::kMass, 0.0, 0.06); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.05); return cut; } - if (!nameStr.compare("paira_prefilter4")) { + if (nameStr == "paira_prefilter4") { cut->AddCut(VarManager::kMass, 0.0, 0.06); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.075); return cut; } - if (!nameStr.compare("paira_prefilter5")) { + if (nameStr == "paira_prefilter5") { cut->AddCut(VarManager::kMass, 0.0, 0.06); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.1); return cut; } - if (!nameStr.compare("paira_prefilter6")) { + if (nameStr == "paira_prefilter6") { cut->AddCut(VarManager::kMass, 0.0, 0.06); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.125); return cut; } - if (!nameStr.compare("paira_prefilter7")) { + if (nameStr == "paira_prefilter7") { cut->AddCut(VarManager::kMass, 0.0, 0.06); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.15); return cut; } - if (!nameStr.compare("pairb_prefilter1")) { + if (nameStr == "pairb_prefilter1") { cut->AddCut(VarManager::kMass, 0.0, 0.05); return cut; } - if (!nameStr.compare("pairb_prefilter2")) { + if (nameStr == "pairb_prefilter2") { cut->AddCut(VarManager::kMass, 0.0, 0.05); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.025); return cut; } - if (!nameStr.compare("pairb_prefilter3")) { + if (nameStr == "pairb_prefilter3") { cut->AddCut(VarManager::kMass, 0.0, 0.05); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.05); return cut; } - if (!nameStr.compare("pairb_prefilter4")) { + if (nameStr == "pairb_prefilter4") { cut->AddCut(VarManager::kMass, 0.0, 0.05); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.075); return cut; } - if (!nameStr.compare("pairb_prefilter5")) { + if (nameStr == "pairb_prefilter5") { cut->AddCut(VarManager::kMass, 0.0, 0.05); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.1); return cut; } - if (!nameStr.compare("pairb_prefilter6")) { + if (nameStr == "pairb_prefilter6") { cut->AddCut(VarManager::kMass, 0.0, 0.05); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.125); return cut; } - if (!nameStr.compare("pairb_prefilter7")) { + if (nameStr == "pairb_prefilter7") { cut->AddCut(VarManager::kMass, 0.0, 0.05); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.15); return cut; } - if (!nameStr.compare("pairc_prefilter1")) { + if (nameStr == "pairc_prefilter1") { cut->AddCut(VarManager::kMass, 0.0, 0.04); return cut; } - if (!nameStr.compare("pairc_prefilter2")) { + if (nameStr == "pairc_prefilter2") { cut->AddCut(VarManager::kMass, 0.0, 0.04); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.025); return cut; } - if (!nameStr.compare("pairc_prefilter3")) { + if (nameStr == "pairc_prefilter3") { cut->AddCut(VarManager::kMass, 0.0, 0.04); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.05); return cut; } - if (!nameStr.compare("pairc_prefilter4")) { + if (nameStr == "pairc_prefilter4") { cut->AddCut(VarManager::kMass, 0.0, 0.04); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.075); return cut; } - if (!nameStr.compare("pairc_prefilter5")) { + if (nameStr == "pairc_prefilter5") { cut->AddCut(VarManager::kMass, 0.0, 0.04); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.1); return cut; } - if (!nameStr.compare("pairc_prefilter6")) { + if (nameStr == "pairc_prefilter6") { cut->AddCut(VarManager::kMass, 0.0, 0.04); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.125); return cut; } - if (!nameStr.compare("pairc_prefilter7")) { + if (nameStr == "pairc_prefilter7") { cut->AddCut(VarManager::kMass, 0.0, 0.04); cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.15); return cut; } - if (!nameStr.compare("paird_prefilter1")) { + if (nameStr == "paird_prefilter1") { cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.025); return cut; } - if (!nameStr.compare("paire_prefilter1")) { + if (nameStr == "paire_prefilter1") { cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.05); return cut; } - if (!nameStr.compare("pairf_prefilter1")) { + if (nameStr == "pairf_prefilter1") { cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.075); return cut; } - if (!nameStr.compare("pairg_prefilter1")) { + if (nameStr == "pairg_prefilter1") { cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.1); return cut; } - if (!nameStr.compare("pairh_prefilter1")) { + if (nameStr == "pairh_prefilter1") { cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.125); return cut; } - if (!nameStr.compare("pairi_prefilter1")) { + if (nameStr == "pairi_prefilter1") { cut->AddCut(VarManager::kOpeningAngle, 0.0, 0.15); return cut; } - if (!nameStr.compare("pairNoCut")) { + if (nameStr == "pairNoCut") { cut->AddCut(VarManager::kMass, 0.0, 1000.0); return cut; } - if (!nameStr.compare("DipionMassCut1")) { + if (nameStr == "DipionMassCut1") { cut->AddCut(VarManager::kMass, 0.5, 1.0); return cut; } - if (!nameStr.compare("DipionMassCut2")) { + if (nameStr == "DipionMassCut2") { cut->AddCut(VarManager::kMass, 0.0, 1.0); return cut; } - if (!nameStr.compare("pairMassLow1")) { + if (nameStr == "pairMassLow1") { cut->AddCut(VarManager::kMass, 1.0, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow2")) { + if (nameStr == "pairMassLow2") { cut->AddCut(VarManager::kMass, 1.5, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow3")) { + if (nameStr == "pairMassLow3") { cut->AddCut(VarManager::kMass, 1.6, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow4")) { + if (nameStr == "pairMassLow4") { cut->AddCut(VarManager::kMass, 1.7, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow5")) { + if (nameStr == "pairMassLow5") { cut->AddCut(VarManager::kMass, 1.8, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow6")) { + if (nameStr == "pairMassLow6") { cut->AddCut(VarManager::kMass, 1.85, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow7")) { + if (nameStr == "pairMassLow7") { cut->AddCut(VarManager::kMass, 1.9, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow8")) { + if (nameStr == "pairMassLow8") { cut->AddCut(VarManager::kMass, 2.0, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow9")) { + if (nameStr == "pairMassLow9") { cut->AddCut(VarManager::kMass, 2.2, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow10")) { + if (nameStr == "pairMassLow10") { cut->AddCut(VarManager::kMass, 2.5, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow11")) { + if (nameStr == "pairMassLow11") { cut->AddCut(VarManager::kMass, 3.0, 1000.0); return cut; } - if (!nameStr.compare("pairMassLow12")) { + if (nameStr == "pairMassLow12") { cut->AddCut(VarManager::kMass, 3.5, 1000.0); return cut; } - if (!nameStr.compare("pairMass1to2")) { + if (nameStr == "pairMass1to2") { cut->AddCut(VarManager::kMass, 1., 2.); return cut; } - if (!nameStr.compare("pairMassIMR")) { + if (nameStr == "pairMassIMR") { cut->AddCut(VarManager::kMass, 1.1, 2.7); return cut; } - if (!nameStr.compare("pairMass1_5to2_7")) { + if (nameStr == "pairMass1_5to2_7") { cut->AddCut(VarManager::kMass, 1.5, 2.7); return cut; } - if (!nameStr.compare("pairMass1_3to3_5")) { + if (nameStr == "pairMass1_3to3_5") { cut->AddCut(VarManager::kMass, 1.3, 3.5); return cut; } - if (!nameStr.compare("pairMass1_3")) { + if (nameStr == "pairMass1_3") { cut->AddCut(VarManager::kMass, 1.3, 1000.0); return cut; } - if (!nameStr.compare("pairMass1_5to3_5")) { + if (nameStr == "pairMass1_5to3_5") { cut->AddCut(VarManager::kMass, 1.5, 3.5); return cut; } - if (!nameStr.compare("pairD0")) { + if (nameStr == "pairD0") { cut->AddCut(VarManager::kMass, 1.814, 1.914); return cut; } - if (!nameStr.compare("pairJpsi")) { + if (nameStr == "pairD0Topology") { + cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.03); + cut->AddCut(VarManager::kD0ImpParProduct, -1000.0, 0.0); + cut->AddCut(VarManager::kDCAxy1, -0.1, 0.1); + cut->AddCut(VarManager::kDCAxy2, -0.1, 0.1); + return cut; + } + + if (nameStr == "pairD0TopologyTight") { + cut->AddCut(VarManager::kCosPointingAngle, 0.8, 1.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1.0, false, VarManager::kPt, 1.0, 4.0); + cut->AddCut(VarManager::kCosPointingAngle, 0.85, 1.0, false, VarManager::kPt, 4.0, 50.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.035, 1000.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kD0DcaDaughters, 0.0, 0.03, false, VarManager::kPt, 1.0, 50.0); + cut->AddCut(VarManager::kD0ImpParProduct, -1000.0, 0.0); + cut->AddCut(VarManager::kDCAxy1, -0.1, 0.1); + cut->AddCut(VarManager::kDCAxy2, -0.1, 0.1); + cut->AddCut(VarManager::kPt1, 0.5, 100.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kPt1, 0.3, 100.0, false, VarManager::kPt, 1.0, 1.5); + cut->AddCut(VarManager::kPt1, 0.4, 100.0, false, VarManager::kPt, 1.5, 2.0); + cut->AddCut(VarManager::kPt1, 0.7, 100.0, false, VarManager::kPt, 2.0, 50.0); + cut->AddCut(VarManager::kPt2, 0.5, 100.0, false, VarManager::kPt, 0.0, 1.0); + cut->AddCut(VarManager::kPt2, 0.3, 100.0, false, VarManager::kPt, 1.0, 1.5); + cut->AddCut(VarManager::kPt2, 0.4, 100.0, false, VarManager::kPt, 1.5, 2.0); + cut->AddCut(VarManager::kPt2, 0.7, 100.0, false, VarManager::kPt, 2.0, 50.0); + return cut; + } + + if (nameStr == "pairJpsi") { cut->AddCut(VarManager::kMass, 2.8, 3.3); return cut; } - if (!nameStr.compare("pairJpsi2")) { + if (nameStr == "pairJpsi2") { cut->AddCut(VarManager::kMass, 2.72, 3.2); return cut; } - if (!nameStr.compare("pairJpsi3")) { + if (nameStr == "pairJpsi3") { cut->AddCut(VarManager::kMass, 2.92, 3.14); return cut; } - if (!nameStr.compare("pairPsi2S")) { + if (nameStr == "pairPsi2S") { cut->AddCut(VarManager::kMass, 3.4, 3.9); return cut; } - if (!nameStr.compare("pairUpsilon")) { + if (nameStr == "pairUpsilon") { cut->AddCut(VarManager::kMass, 8.0, 11.0); return cut; } - if (!nameStr.compare("pairX3872")) { + if (nameStr == "pairX3872") { cut->AddCut(VarManager::kQ, 0.0, 0.3); return cut; } - if (!nameStr.compare("pairX3872_2")) { + if (nameStr == "pairX3872_2") { cut->AddCut(VarManager::kQuadDefaultDileptonMass, 3.0, 5.0); cut->AddCut(VarManager::kQ, 0.0, 0.5); cut->AddCut(VarManager::kDeltaR, 0.0, 5.0); @@ -6842,7 +7242,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("pairX3872_3")) { + if (nameStr == "pairX3872_3") { cut->AddCut(VarManager::kQuadDefaultDileptonMass, 3.0, 5.0); cut->AddCut(VarManager::kQ, 0.0, 0.5); cut->AddCut(VarManager::kDeltaR, 0.0, 5.0); @@ -6850,104 +7250,117 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("pairPtLow1")) { + std::shared_ptr fDeltaR2High = std::make_shared("fDeltaR2High", "[0]+[1]*x", 0.0, 5.0); + fDeltaR2High->SetParameters(3.0, -0.4); + if (!nameStr.compare("pairX3872_minitree")) { + cut->AddCut(VarManager::kRap, -0.8, 0.8); + cut->AddCut(VarManager::kQuadDefaultDileptonMass, 3.0, 5.0); + cut->AddCut(VarManager::kDeltaR2, 0.0, 1.0, false, VarManager::kPt, 5.0, 1000.0); + cut->AddCut(VarManager::kDeltaR2, 0.0, fDeltaR2High, false, VarManager::kPt, 0.0, 5.0); + cut->AddCut(VarManager::kQuadPt, 0.0, 1000.0); + cut->AddCut(VarManager::kVertexingProcCode, 0.5, 2.5); + cut->AddCut(VarManager::kVertexingQuadProcCode, 0.5, 2.5); + return cut; + } + + if (nameStr == "pairPtLow1") { cut->AddCut(VarManager::kPt, 2.0, 1000.0); return cut; } - if (!nameStr.compare("pairPtLow2")) { + if (nameStr == "pairPtLow2") { cut->AddCut(VarManager::kPt, 5.0, 1000.0); return cut; } - if (!nameStr.compare("pairPtLow3")) { + if (nameStr == "pairPtLow3") { cut->AddCut(VarManager::kPt, 0, 0.15); return cut; } - if (!nameStr.compare("pairPtLow4")) { + if (nameStr == "pairPtLow4") { cut->AddCut(VarManager::kPt, 0, 10.0); return cut; } - if (!nameStr.compare("pairPtLow5")) { + if (nameStr == "pairPtLow5") { cut->AddCut(VarManager::kPt, 0.8, 1000.0); return cut; } - if (!nameStr.compare("pairRapidityForward")) { + if (nameStr == "pairRapidityForward") { cut->AddCut(VarManager::kRap, 2.5, 4.0); return cut; } - if (!nameStr.compare("pairDCA")) { + if (nameStr == "pairDCA") { cut->AddCut(VarManager::kQuadDCAabsXY, .0, .50); return cut; } - if (!nameStr.compare("singleDCA")) { + if (nameStr == "singleDCA") { cut->AddCut(VarManager::kTrackDCAsigXY, 0.0, 5.); return cut; } - if (!nameStr.compare("pairPhiV")) { + if (nameStr == "pairPhiV") { cut->AddCut(VarManager::kPairPhiv, 2., 3.2); return cut; } - if (!nameStr.compare("excludePairPhiV")) { + if (nameStr == "excludePairPhiV") { cut->AddCut(VarManager::kPairPhiv, 2., 3.2, true); return cut; } - if (!nameStr.compare("pairLowMass")) { + if (nameStr == "pairLowMass") { cut->AddCut(VarManager::kMass, 0., 0.1); return cut; } - if (!nameStr.compare("excludePairLowMass")) { + if (nameStr == "excludePairLowMass") { cut->AddCut(VarManager::kMass, 0., 0.1, true); return cut; } - if (!nameStr.compare("pairLxyzProjected3sigma")) { + if (nameStr == "pairLxyzProjected3sigma") { cut->AddCut(VarManager::kVertexingLxyzProjected, 0.015, 10.); return cut; } - if (!nameStr.compare("pairTauxyzProjected1")) { + if (nameStr == "pairTauxyzProjected1") { cut->AddCut(VarManager::kVertexingTauxyzProjected, 0.0005, 10.); return cut; } - if (!nameStr.compare("pairTauxyzProjected1sigma")) { + if (nameStr == "pairTauxyzProjected1sigma") { cut->AddCut(VarManager::kVertexingTauxyzProjected, 0.003, 10.); return cut; } - if (!nameStr.compare("pairLxyProjected3sigmaLambdacCand")) { - TF1* f1minLxyProjected = new TF1("f1minLxyProjected", "[0]+[1]*x", 0., 20.); + if (nameStr == "pairLxyProjected3sigmaLambdacCand") { + std::shared_ptr f1minLxyProjected = std::make_shared("f1minLxyProjected", "[0]+[1]*x", 0., 20.); f1minLxyProjected->SetParameters(0.0065, -0.00023); cut->AddCut(VarManager::kVertexingLxyProjected, f1minLxyProjected, 1., false, VarManager::kPt, 0., 20.); return cut; } - if (!nameStr.compare("pairLxyProjected3sigmaDplusCand")) { + if (nameStr == "pairLxyProjected3sigmaDplusCand") { cut->AddCut(VarManager::kVertexingLxyProjected, 0.009, 10.); return cut; } - if (!nameStr.compare("pairCosPointingPos")) { + if (nameStr == "pairCosPointingPos") { cut->AddCut(VarManager::kCosPointingAngle, 0.9, 1000.); return cut; } - if (!nameStr.compare("pairCosPointingNeg90")) { + if (nameStr == "pairCosPointingNeg90") { cut->AddCut(VarManager::kCosPointingAngle, -1000., -0.9); return cut; } - if (!nameStr.compare("pairCosPointingNeg85")) { + if (nameStr == "pairCosPointingNeg85") { cut->AddCut(VarManager::kCosPointingAngle, -1000., -0.85); return cut; } @@ -6957,7 +7370,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) // Below are a list of single electron single muon and pair selection in order or optimize the trigger // trigger selection cuts - if (!nameStr.compare("electronStandardQualityTriggerTest")) { + if (nameStr == "electronStandardQualityTriggerTest") { cut->AddCut(VarManager::kIsSPDany, 0.5, 1.5); cut->AddCut(VarManager::kIsITSrefit, 0.5, 1.5); cut->AddCut(VarManager::kIsTPCrefit, 0.5, 1.5); @@ -6967,7 +7380,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("muonLooseTriggerTestCuts")) { + if (nameStr == "muonLooseTriggerTestCuts") { cut->AddCut(VarManager::kEta, -4.5, -2.0); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 10, 100); cut->AddCut(VarManager::kMuonPDca, 0.0, 1500, false, VarManager::kMuonRAtAbsorberEnd, 10, 26.5); @@ -6977,7 +7390,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("muonMatchingMFTMCHTriggerTestCuts")) { + if (nameStr == "muonMatchingMFTMCHTriggerTestCuts") { cut->AddCut(VarManager::kEta, -4.5, -2.0); cut->AddCut(VarManager::kMuonRAtAbsorberEnd, 10, 100); cut->AddCut(VarManager::kMuonPDca, 0.0, 1500, false, VarManager::kMuonRAtAbsorberEnd, 10, 26.5); @@ -6988,7 +7401,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_TPCPID_TriggerTest1")) { + if (nameStr == "jpsi_TPCPID_TriggerTest1") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 4.0, false, VarManager::kPin, 0, 2.0); cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2.0, 4.0, false, VarManager::kPin, 2.0, 9999.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 3.0, 999, false, VarManager::kPin, 0, 2.0); @@ -6996,7 +7409,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_TPCPID_TriggerTest2")) { + if (nameStr == "jpsi_TPCPID_TriggerTest2") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 4.0, false, VarManager::kPin, 0, 2.0); cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2.0, 4.0, false, VarManager::kPin, 2.0, 9999.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 2.5, 999, false, VarManager::kPin, 0, 2.0); @@ -7004,7 +7417,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_TPCPID_TriggerTest3")) { + if (nameStr == "jpsi_TPCPID_TriggerTest3") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 4.0, false, VarManager::kPin, 0, 3.0); cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2.0, 4.0, false, VarManager::kPin, 3.0, 9999.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 3.0, 999, false, VarManager::kPin, 0, 2.0); @@ -7012,7 +7425,7 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("jpsi_TPCPID_TriggerTest4")) { + if (nameStr == "jpsi_TPCPID_TriggerTest4") { cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -3.0, 4.0, false, VarManager::kPin, 0, 3.0); cut->AddCut(VarManager::kTPCnSigmaEl_Corr, -2.0, 4.0, false, VarManager::kPin, 3.0, 9999.0); cut->AddCut(VarManager::kTPCnSigmaPi_Corr, 2.5, 999, false, VarManager::kPin, 0, 2.0); @@ -7023,57 +7436,90 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) //--------------------------------------------------------------- // ALICE 3 Analysis Cuts - if (!nameStr.compare("alice3KineSkim")) { + if (nameStr == "alice3KineSkim") { cut->AddCut(VarManager::kPt, 0.05, 1000.0); cut->AddCut(VarManager::kEta, -4.0, 4.0); return cut; } - if (!nameStr.compare("alice3StandardKine")) { + if (nameStr == "alice3StandardKine") { cut->AddCut(VarManager::kPt, 0.1, 1000.0); cut->AddCut(VarManager::kEta, -2.5, 2.5); // Total tracker acceptance in v3b geomety return cut; } - if (!nameStr.compare("alice3KineTOFAcceptance")) { + if (nameStr == "alice3KineTOFAcceptance") { cut->AddCut(VarManager::kPt, 0.1, 1000.0); cut->AddCut(VarManager::kEta, -2.0, 2.0); // TOF acceptance in v3b geomety return cut; } - if (!nameStr.compare("alice3KineRICHAcceptance")) { + if (nameStr == "alice3KineRICHAcceptance") { cut->AddCut(VarManager::kPt, 0.1, 1000.0); cut->AddCut(VarManager::kEta, -0.8, 0.8); // RICH acceptance in v3b geomety return cut; } - if (!nameStr.compare("alice3JpsiKine")) { - cut->AddCut(VarManager::kPt, 1.0, 1000.0); + if (nameStr == "alice3StandardKineD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); cut->AddCut(VarManager::kEta, -2.5, 2.5); // Total tracker acceptance in v3b geomety return cut; } - if (!nameStr.compare("alice3JpsiKineTOFAcceptance")) { - cut->AddCut(VarManager::kPt, 1.0, 1000.0); + if (nameStr == "alice3TOFEta") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -2.0, 2.0); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3RICHEta") { + cut->AddCut(VarManager::kEta, -0.8, 0.8); // Total tracker acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3KineTOFAcceptanceD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); cut->AddCut(VarManager::kEta, -2.0, 2.0); // TOF acceptance in v3b geomety return cut; } - if (!nameStr.compare("alice3JpsiKineRICHAcceptance")) { + if (nameStr == "alice3KineRICHAcceptanceD0") { + cut->AddCut(VarManager::kPt, 0.7, 1000.0); + cut->AddCut(VarManager::kEta, -0.8, 0.8); // TOF acceptance in v3b geomety + return cut; + } + + if (nameStr == "alice3LambdaCKine") { + cut->AddCut(VarManager::kPt, 0.2, 1000.0); + cut->AddCut(VarManager::kEta, -2.0, 2.0); + return cut; + } + + if (nameStr == "alice3JpsiKine") { cut->AddCut(VarManager::kPt, 1.0, 1000.0); - cut->AddCut(VarManager::kEta, -0.8, 0.8); // RICH acceptance in v3b geomety + cut->AddCut(VarManager::kEta, -2.5, 2.5); + return cut; + } + + if (nameStr == "alice3JpsiKineTOFAcceptance") { + cut->AddCut(VarManager::kPt, 1.0, 1000.0); + cut->AddCut(VarManager::kEta, -2.0, 2.0); return cut; } - if (!nameStr.compare("alice3TrackQuality")) { + if (nameStr == "alice3JpsiKineRICHAcceptance") { + cut->AddCut(VarManager::kPt, 1.0, 1000.0); + cut->AddCut(VarManager::kEta, -0.8, 0.8); + return cut; + } + + if (nameStr == "alice3TrackQuality") { cut->AddCut(VarManager::kIsReconstructed, 0.5, 1.5); - cut->AddCut(VarManager::kNSiliconHits, 6.0, 12.0); - cut->AddCut(VarManager::kTrackDCAxy, -3.0, 3.0); - cut->AddCut(VarManager::kTrackDCAz, -3.0, 3.0); + cut->AddCut(VarManager::kNSiliconHits, 5.0, 12.0); return cut; } - if (!nameStr.compare("alice3TrackQualityTightDCA")) { + if (nameStr == "alice3TrackQualityTightDCA") { cut->AddCut(VarManager::kIsReconstructed, 0.5, 1.5); cut->AddCut(VarManager::kNSiliconHits, 6.0, 12.0); cut->AddCut(VarManager::kTrackDCAxy, -1.0, 1.0); @@ -7081,92 +7527,145 @@ AnalysisCut* o2::aod::dqcuts::GetAnalysisCut(const char* cutName) return cut; } - if (!nameStr.compare("alice3OTPIDEl")) { + if (nameStr == "alice3OTPIDEl") { cut->AddCut(VarManager::kOTnSigmaEl, -3.0, 3.0); return cut; } - if (!nameStr.compare("alice3OTPIDPi")) { + if (nameStr == "alice3OTPIDPi") { cut->AddCut(VarManager::kOTnSigmaPi, -3.0, 3.0); return cut; } - if (!nameStr.compare("alice3OTPIDKa")) { + if (nameStr == "alice3OTPIDKa") { cut->AddCut(VarManager::kOTnSigmaKa, -3.0, 3.0); return cut; } - if (!nameStr.compare("alice3OTPIDPr")) { + if (nameStr == "alice3OTPIDPr") { cut->AddCut(VarManager::kOTnSigmaPr, -3.0, 3.0); return cut; } - if (!nameStr.compare("alice3iTOFPIDEl")) { - cut->AddCut(VarManager::kInnerTOFnSigmaEl, -3.0, 3.0); + if (nameStr == "alice3iTOFPIDEl") { + cut->AddCut(VarManager::kInnerTOFnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } - if (!nameStr.compare("alice3iTOFPIDPi")) { - cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0); + if (nameStr == "alice3iTOFPIDPi") { + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } - if (!nameStr.compare("alice3iTOFPIDKa")) { - cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0); + if (nameStr == "alice3iTOFPIDKa") { + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } - if (!nameStr.compare("alice3iTOFPIDPr")) { - cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0); + if (nameStr == "alice3iTOFPIDPr") { + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); return cut; } - if (!nameStr.compare("alice3oTOFPIDEl")) { - cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0, 3.0); + if (nameStr == "alice3oTOFPIDEl") { + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } - if (!nameStr.compare("alice3oTOFPIDEl")) { - cut->AddCut(VarManager::kOuterTOFnSigmaEl, -3.0, 3.0); + if (nameStr == "alice3oTOFPIDPi") { + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } - if (!nameStr.compare("alice3oTOFPIDPi")) { - cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0); + if (nameStr == "alice3oTOFPIDKa") { + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } - if (!nameStr.compare("alice3oTOFPIDKa")) { - cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0); + if (nameStr == "alice3oTOFPIDPr") { + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); return cut; } - if (!nameStr.compare("alice3oTOFPIDPr")) { - cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0); + if (nameStr == "alice3D0KaonTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); return cut; } - if (!nameStr.compare("alice3RICHPIDEl")) { - cut->AddCut(VarManager::kRICHnSigmaEl, -3.0, 3.0); - cut->AddCut(VarManager::kHasRICHSigEl, 0.5, 1.5); + if (nameStr == "alice3D0PionTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); return cut; } - if (!nameStr.compare("alice3RICHPIDPi")) { - cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0); - cut->AddCut(VarManager::kHasRICHSigPi, 0.5, 1.5); + if (nameStr == "alice3D0KaonRICHTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0, false, VarManager::kHasRICHSigKa, 0.5, 1.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, true, VarManager::kP, 0.57, 1000.0, false, VarManager::kRICHnSigmaPi, 0.5, 1.5); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0, true, VarManager::kP, 3.8, 1000.0, false, VarManager::kRICHnSigmaPr, 0.5, 1.5); + return cut; + } + + if (nameStr == "alice3D0PionRICHTOFPID") { + cut->AddCut(VarManager::kInnerTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.57, 1000.0, false, VarManager::kHasRICHSigKa, 0.5, 1.5); + + cut->AddCut(VarManager::kInnerTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + cut->AddCut(VarManager::kInnerTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 1.5); + + cut->AddCut(VarManager::kOuterTOFnSigmaKa, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + cut->AddCut(VarManager::kOuterTOFnSigmaPr, -3.0, 3.0, true, VarManager::kP, 0.0, 2.5); + + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, true, VarManager::kP, 2.0, 1000.0, false, VarManager::kRICHnSigmaPi, 0.5, 1.5); + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0, true, VarManager::kP, 3.8, 1000.0, false, VarManager::kRICHnSigmaPr, 0.5, 1.5); + return cut; + } + + if (nameStr == "alice3CharmoniumPID") { + cut->AddCut(VarManager::kOuterTOFnSigmaEl, -2.0, 3.0, false, VarManager::kP, 0.0, 1.2); + cut->AddCut(VarManager::kRICHnSigmaEl, -2.0, 3.0, false, VarManager::kHasRICHSigEl, 0.5, 1.5, false, VarManager::kP, 0.7, 1000.0); + return cut; + } + + if (nameStr == "alice3RICHPIDEl") { + cut->AddCut(VarManager::kRICHnSigmaEl, -3.0, 3.0, false, VarManager::kP, 0.7, 1000.0, false, VarManager::kHasRICHSigEl, 0.5, 1.5); + return cut; + } + + if (nameStr == "alice3RICHPIDPi") { + cut->AddCut(VarManager::kRICHnSigmaPi, -3.0, 3.0, false, VarManager::kP, 0.57, 1000.0, false, VarManager::kHasRICHSigPi, 0.5, 1.5); return cut; } - if (!nameStr.compare("alice3RICHPIDKa")) { - cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0); - cut->AddCut(VarManager::kHasRICHSigKa, 0.5, 1.5); + if (nameStr == "alice3RICHPIDKa") { + cut->AddCut(VarManager::kRICHnSigmaKa, -3.0, 3.0, false, VarManager::kP, 2.0, 1000.0, false, VarManager::kHasRICHSigKa, 0.5, 1.5); return cut; } - if (!nameStr.compare("alice3RICHPIDPr")) { - cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0); - cut->AddCut(VarManager::kHasRICHSigPr, 0.5, 1.5); + if (nameStr == "alice3RICHPIDPr") { + cut->AddCut(VarManager::kRICHnSigmaPr, -3.0, 3.0, false, VarManager::kP, 3.8, 1000.0, false, VarManager::kHasRICHSigPr, 0.5, 1.5); return cut; } @@ -7196,7 +7695,7 @@ std::vector o2::aod::dqcuts::GetCutsFromJSON(const char* json) // Check that the json is parsed correctly rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) return cuts; } @@ -7383,7 +7882,7 @@ AnalysisCut* o2::aod::dqcuts::ParseJSONAnalysisCut(T cut, const char* cutName) } // construct the AnalysisCut object and add the AddCuts - AnalysisCut* retCut = new AnalysisCut(cutName, cut->HasMember("library") ? cut->FindMember("title")->value.GetString() : ""); + auto* retCut = new AnalysisCut(cutName, cut->HasMember("library") ? cut->FindMember("title")->value.GetString() : ""); // loop over all the members for this cut and configure the AddCut objects for (rapidjson::Value::ConstMemberIterator it = cut->MemberBegin(); it != cut->MemberEnd(); it++) { @@ -7425,8 +7924,8 @@ AnalysisCut* o2::aod::dqcuts::ParseJSONAnalysisCut(T cut, const char* cutName) bool depCut2Exclude = (addcut.HasMember("depCut2Exclude") ? addcut.FindMember("depCut2Exclude")->value.GetBool() : false); LOG(info) << "depCut2Exclude " << depCut2Exclude; - TF1* cutLowFunc = nullptr; - TF1* cutHighFunc = nullptr; + std::shared_ptr cutLowFunc = nullptr; + std::shared_ptr cutHighFunc = nullptr; double cutLowNumber = 0.0; double cutHighNumber = 0.0; if (cutLow_isNumber) { @@ -7434,8 +7933,8 @@ AnalysisCut* o2::aod::dqcuts::ParseJSONAnalysisCut(T cut, const char* cutName) LOG(info) << "cutLowNumber " << cutLowNumber; } else { auto& cutLow = addcut.FindMember("cutLow")->value; - cutLowFunc = new TF1(cutLow.FindMember("funcName")->value.GetString(), cutLow.FindMember("funcBody")->value.GetString(), - cutLow.FindMember("xLow")->value.GetDouble(), cutLow.FindMember("xHigh")->value.GetDouble()); + cutLowFunc = std::make_shared(cutLow.FindMember("funcName")->value.GetString(), cutLow.FindMember("funcBody")->value.GetString(), + cutLow.FindMember("xLow")->value.GetDouble(), cutLow.FindMember("xHigh")->value.GetDouble()); LOG(info) << "cutLowFunc " << cutLow.FindMember("funcName")->value.GetString() << ", " << cutLow.FindMember("funcBody")->value.GetString() << ", " << cutLow.FindMember("xLow")->value.GetDouble() << ", " << cutLow.FindMember("xHigh")->value.GetDouble(); } @@ -7444,8 +7943,8 @@ AnalysisCut* o2::aod::dqcuts::ParseJSONAnalysisCut(T cut, const char* cutName) LOG(info) << "cutHighNumber " << cutHighNumber; } else { auto& cutHigh = addcut.FindMember("cutHigh")->value; - cutHighFunc = new TF1(cutHigh.FindMember("funcName")->value.GetString(), cutHigh.FindMember("funcBody")->value.GetString(), - cutHigh.FindMember("xLow")->value.GetDouble(), cutHigh.FindMember("xHigh")->value.GetDouble()); + cutHighFunc = std::make_shared(cutHigh.FindMember("funcName")->value.GetString(), cutHigh.FindMember("funcBody")->value.GetString(), + cutHigh.FindMember("xLow")->value.GetDouble(), cutHigh.FindMember("xHigh")->value.GetDouble()); LOG(info) << "cutHighFunc " << cutHigh.FindMember("funcName")->value.GetString() << ", " << cutHigh.FindMember("funcBody")->value.GetString() << ", " << cutHigh.FindMember("xLow")->value.GetDouble() << ", " << cutHigh.FindMember("xHigh")->value.GetDouble(); } @@ -7514,7 +8013,7 @@ AnalysisCompositeCut* o2::aod::dqcuts::ParseJSONAnalysisCompositeCut(T cut, cons return GetCompositeCut(cut->FindMember("library")->value.GetString()); } - AnalysisCompositeCut* retCut = new AnalysisCompositeCut(cutName, cut->HasMember("library") ? cut->FindMember("title")->value.GetString() : "", cut->FindMember("useAND")->value.GetBool()); + auto* retCut = new AnalysisCompositeCut(cutName, cut->HasMember("library") ? cut->FindMember("title")->value.GetString() : "", cut->FindMember("useAND")->value.GetBool()); // Loop to find AddCut objects for (rapidjson::Value::ConstMemberIterator it = cut->MemberBegin(); it != cut->MemberEnd(); it++) { @@ -7580,7 +8079,7 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro // Check that the json is parsed correctly rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) return {}; } @@ -7642,8 +8141,9 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro for (auto centMember = obj.MemberBegin(); centMember != obj.MemberEnd(); ++centMember) { TString centKey = centMember->name.GetString(); - if (!centKey.Contains("AddCentCut")) + if (!centKey.Contains("AddCentCut")) { continue; + } const auto& centCut = centMember->value; @@ -7657,8 +8157,9 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro for (auto ptMember = centCut.MemberBegin(); ptMember != centCut.MemberEnd(); ++ptMember) { TString ptKey = ptMember->name.GetString(); - if (!ptKey.Contains("AddPtCut")) + if (!ptKey.Contains("AddPtCut")) { continue; + } const auto& ptCut = ptMember->value; @@ -7676,8 +8177,9 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro for (auto mlMember = ptCut.MemberBegin(); mlMember != ptCut.MemberEnd(); ++mlMember) { TString mlKey = mlMember->name.GetString(); - if (!mlKey.Contains("AddMLCut")) + if (!mlKey.Contains("AddMLCut")) { continue; + } const auto& mlcut = mlMember->value; @@ -7696,21 +8198,23 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro } } - if (!cutDirsFilled) { - cutDirsFilled = true; - } + cutDirsFilled = true; centBins.emplace_back(centMin, centMax); ptBins.emplace_back(ptMin, ptMax); cutsMl.push_back(binCuts); labelsFlatBin.push_back(Form("%s_cent%.0f_%.0f_pt%.1f_%.1f", cent.c_str(), centMin, centMax, ptMin, ptMax)); LOG(info) << "Added cut for " << Form("%s_cent%.0f_%.0f_pt%.1f_%.1f", cent.c_str(), centMin, centMax, ptMin, ptMax) << " with cuts: ["; + std::string msg = ""; for (size_t i = 0; i < binCuts.size(); ++i) { - std::cout << binCuts[i]; - if (i != binCuts.size() - 1) - std::cout << ", "; + msg += std::to_string(binCuts[i]); + if (i != binCuts.size() - 1) { + msg += ", "; + } } - std::cout << "] and direction: " << (exclude ? "CutGreater" : "CutSmaller") << std::endl; + msg += "] and direction: "; + msg += (exclude ? "CutGreater" : "CutSmaller"); + LOG(info) << msg; } } @@ -7721,6 +8225,7 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro } std::vector labelsClass; + labelsClass.reserve(cutsMl[0].size()); for (size_t j = 0; j < cutsMl[0].size(); ++j) { labelsClass.push_back(Form("score class %d", static_cast(j))); } @@ -7742,9 +8247,10 @@ o2::aod::dqmlcuts::BdtScoreConfig o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFro binaryCfg.cutsMl = makeLabeledCutsMl(cutsMl, labelsFlatBin, labelsClass); return binaryCfg; + } - // MultiClass - } else if (typeStr == "MultiClass") { + // MultiClass + if (typeStr == "MultiClass") { dqmlcuts::MultiClassBdtScoreConfig multiCfg; multiCfg.inputFeatures = namesInputFeatures; multiCfg.onnxFiles = onnxFileNames; diff --git a/PWGDQ/Core/HistogramManager.cxx b/PWGDQ/Core/HistogramManager.cxx index ae1c4be3022..74fa3d21b8c 100644 --- a/PWGDQ/Core/HistogramManager.cxx +++ b/PWGDQ/Core/HistogramManager.cxx @@ -14,47 +14,42 @@ #include #include +#include #include #include #include #include +#include +#include #include +#include +#include #include +#include +#include +#include +#include +#include #include #include #include +#include +#include #include -#include #include #include +#include #include -using namespace std; - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -ClassImp(HistogramManager); //_______________________________________________________________________________ HistogramManager::HistogramManager() : TNamed("", ""), fMainList(nullptr), fNVars(0), fUsedVars(nullptr), - fVariablesMap(), fUseDefaultVariableNames(false), - fBinsAllocated(0), - fVariableNames(nullptr), - fVariableUnits(nullptr) + fBinsAllocated(0) { // // Constructor @@ -63,27 +58,25 @@ HistogramManager::HistogramManager() : TNamed("", ""), //_______________________________________________________________________________ HistogramManager::HistogramManager(const char* name, const char* title, const int maxNVars) : TNamed(name, title), - fMainList(), + fMainList(new THashList), fNVars(maxNVars), - fUsedVars(), - fVariablesMap(), + fUsedVars(new bool[maxNVars]), + fUseDefaultVariableNames(kFALSE), - fBinsAllocated(0), - fVariableNames(), - fVariableUnits() + fBinsAllocated(0) { // // Constructor // - fMainList = new THashList; + fMainList->SetOwner(kTRUE); fMainList->SetName(name); - fUsedVars = new bool[maxNVars]; + for (int i = 0; i < maxNVars; ++i) { fUsedVars[i] = false; } - fVariableNames = new TString[maxNVars]; - fVariableUnits = new TString[maxNVars]; + fVariableNames.resize(maxNVars); + fVariableUnits.resize(maxNVars); } //_______________________________________________________________________________ @@ -102,9 +95,11 @@ void HistogramManager::SetDefaultVarNames(TString* vars, TString* units) // // Set default variable names // + std::span varNames{vars, static_cast(fNVars)}; + std::span varUnits{units, static_cast(fNVars)}; for (int i = 0; i < fNVars; ++i) { - fVariableNames[i] = vars[i]; - fVariableUnits[i] = units[i]; + fVariableNames[i] = varNames[i]; + fVariableUnits[i] = varUnits[i]; } }; @@ -141,7 +136,7 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // TODO: replace the cout warning messages with LOG (same for all the other functions) // get the list to which the histogram should be added - auto* hList = reinterpret_cast(fMainList->FindObject(histClass)); + auto* hList = dynamic_cast(fMainList->FindObject(histClass)); if (!hList) { LOG(warn) << "HistogramManager::AddHistogram(): Histogram list " << histClass << " not found!"; LOG(warn) << " Histogram not created"; @@ -209,9 +204,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += nXbins + 2; // TODO: possibly make the call of Sumw2() optional for all histograms h->Sumw2(); - if (fVariableNames[varX][0]) { + if (fVariableNames[varX][0] != 0) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), - (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); + ((fVariableUnits[varX][0] != 0) ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); } if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); @@ -231,7 +226,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // if requested, build the profile using the profile widths instead of stat errors // TODO: make this option more transparent to the user ? if (titleStr.Contains("--s--")) { - (reinterpret_cast(h))->BuildOptions(0., 0., "s"); + if (auto* profile = dynamic_cast(h)) { + profile->BuildOptions(0., 0., "s"); + } } } else { if (!isdouble) { @@ -242,9 +239,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) { + if (fVariableNames[varX][0] != 0) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), - (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); + ((fVariableUnits[varX][0] != 0) ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); } if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); @@ -253,13 +250,13 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co MakeAxisLabels(h->GetXaxis(), xLabels); } - if (fVariableNames[varY][0]) { + if (fVariableNames[varY][0] != 0) { h->GetYaxis()->SetTitle(Form("%s %s", fVariableNames[varY].Data(), - (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); + ((fVariableUnits[varY][0] != 0) ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); } - if (fVariableNames[varY][0] && isProfile) { + if ((fVariableNames[varY][0] != 0) && isProfile) { h->GetYaxis()->SetTitle(Form("<%s> %s", fVariableNames[varY].Data(), - (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); + ((fVariableUnits[varY][0] != 0) ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); } if (arr->At(2)) { h->GetYaxis()->SetTitle(arr->At(2)->GetName()); @@ -278,14 +275,18 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); if (titleStr.Contains("--s--")) { - (reinterpret_cast(h))->BuildOptions(0., 0., "s"); + if (auto* profile = dynamic_cast(h)) { + profile->BuildOptions(0., 0., "s"); + } } } else { // TProfile2D h = new TProfile2D(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xmin, xmax, nYbins, ymin, ymax); fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); if (titleStr.Contains("--s--")) { - (reinterpret_cast(h))->BuildOptions(0., 0., "s"); + if (auto* profile = dynamic_cast(h)) { + profile->BuildOptions(0., 0., "s"); + } } } } else { // TH3F @@ -297,9 +298,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) { + if (fVariableNames[varX][0] != 0) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), - (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); + ((fVariableUnits[varX][0] != 0) ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); } if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); @@ -307,9 +308,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); } - if (fVariableNames[varY][0]) { + if (fVariableNames[varY][0] != 0) { h->GetYaxis()->SetTitle(Form("%s %s", fVariableNames[varY].Data(), - (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); + ((fVariableUnits[varY][0] != 0) ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); } if (arr->At(2)) { h->GetYaxis()->SetTitle(arr->At(2)->GetName()); @@ -317,13 +318,13 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co if (yLabels[0] != '\0') { MakeAxisLabels(h->GetYaxis(), yLabels); } - if (fVariableNames[varZ][0]) { + if (fVariableNames[varZ][0] != 0) { h->GetZaxis()->SetTitle(Form("%s %s", fVariableNames[varZ].Data(), - (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); + ((fVariableUnits[varZ][0] != 0) ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); } - if (fVariableNames[varZ][0] && isProfile && varT < 0) { // for TProfile2D + if ((fVariableNames[varZ][0] != 0) && isProfile && varT < 0) { // for TProfile2D h->GetZaxis()->SetTitle(Form("<%s> %s", fVariableNames[varZ].Data(), - (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); + ((fVariableUnits[varZ][0] != 0) ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); } if (arr->At(3)) { h->GetZaxis()->SetTitle(arr->At(3)->GetName()); @@ -334,6 +335,11 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co h->SetDirectory(nullptr); hList->Add(h); break; + + default: + LOG(warn) << "HistogramManager::AddHistogram(): Unsupported dimension " << dimension + << " for histogram " << hname; + return; } // end switch } @@ -350,7 +356,7 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // // get the list to which the histogram should be added - auto* hList = reinterpret_cast(fMainList->FindObject(histClass)); + auto* hList = dynamic_cast(fMainList->FindObject(histClass)); if (!hList) { LOG(warn) << "HistogramManager::AddHistogram(): Histogram list " << histClass << " not found!"; LOG(warn) << " Histogram not created"; @@ -417,9 +423,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co } fBinsAllocated += nXbins + 2; h->Sumw2(); - if (fVariableNames[varX][0]) { + if (fVariableNames[varX][0] != 0) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), - (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); + ((fVariableUnits[varX][0] != 0) ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); } if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); @@ -437,7 +443,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += nXbins + 2; h->Sumw2(); if (titleStr.Contains("--s--")) { - (reinterpret_cast(h))->BuildOptions(0., 0., "s"); + if (auto* profile = dynamic_cast(h)) { + profile->BuildOptions(0., 0., "s"); + } } } else { if (!isdouble) { @@ -448,9 +456,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) { + if (fVariableNames[varX][0] != 0) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), - (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); + ((fVariableUnits[varX][0] != 0) ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); } if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); @@ -458,13 +466,13 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); } - if (fVariableNames[varY][0]) { + if (fVariableNames[varY][0] != 0) { h->GetYaxis()->SetTitle(Form("%s %s", fVariableNames[varY].Data(), - (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); + ((fVariableUnits[varY][0] != 0) ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); } - if (fVariableNames[varY][0] && isProfile) { + if ((fVariableNames[varY][0] != 0) && isProfile) { h->GetYaxis()->SetTitle(Form("<%s> %s", fVariableNames[varY].Data(), - (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); + ((fVariableUnits[varY][0] != 0) ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); } if (arr->At(2)) { @@ -484,14 +492,18 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); if (titleStr.Contains("--s--")) { - (reinterpret_cast(h))->BuildOptions(0., 0., "s"); + if (auto* profile = dynamic_cast(h)) { + profile->BuildOptions(0., 0., "s"); + } } } else { h = new TProfile2D(hname, (arr->At(0) ? arr->At(0)->GetName() : ""), nXbins, xbins, nYbins, ybins); fBinsAllocated += (nXbins + 2) * (nYbins + 2); h->Sumw2(); if (titleStr.Contains("--s--")) { - (reinterpret_cast(h))->BuildOptions(0., 0., "s"); + if (auto* profile = dynamic_cast(h)) { + profile->BuildOptions(0., 0., "s"); + } } } } else { @@ -503,9 +515,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co fBinsAllocated += (nXbins + 2) * (nYbins + 2) * (nZbins + 2); h->Sumw2(); } - if (fVariableNames[varX][0]) { + if (fVariableNames[varX][0] != 0) { h->GetXaxis()->SetTitle(Form("%s %s", fVariableNames[varX].Data(), - (fVariableUnits[varX][0] ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); + ((fVariableUnits[varX][0] != 0) ? Form("(%s)", fVariableUnits[varX].Data()) : ""))); } if (arr->At(1)) { h->GetXaxis()->SetTitle(arr->At(1)->GetName()); @@ -513,9 +525,9 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co if (xLabels[0] != '\0') { MakeAxisLabels(h->GetXaxis(), xLabels); } - if (fVariableNames[varY][0]) { + if (fVariableNames[varY][0] != 0) { h->GetYaxis()->SetTitle(Form("%s %s", fVariableNames[varY].Data(), - (fVariableUnits[varY][0] ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); + ((fVariableUnits[varY][0] != 0) ? Form("(%s)", fVariableUnits[varY].Data()) : ""))); } if (arr->At(2)) { h->GetYaxis()->SetTitle(arr->At(2)->GetName()); @@ -523,13 +535,13 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co if (yLabels[0] != '\0') { MakeAxisLabels(h->GetYaxis(), yLabels); } - if (fVariableNames[varZ][0]) { + if (fVariableNames[varZ][0] != 0) { h->GetZaxis()->SetTitle(Form("%s %s", fVariableNames[varZ].Data(), - (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); + ((fVariableUnits[varZ][0] != 0) ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); } - if (fVariableNames[varZ][0] && isProfile && varT < 0) { // TProfile2D + if ((fVariableNames[varZ][0] != 0) && isProfile && varT < 0) { // TProfile2D h->GetZaxis()->SetTitle(Form("<%s> %s", fVariableNames[varZ].Data(), - (fVariableUnits[varZ][0] ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); + ((fVariableUnits[varZ][0] != 0) ? Form("(%s)", fVariableUnits[varZ].Data()) : ""))); } if (arr->At(3)) { @@ -540,6 +552,11 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co } hList->Add(h); break; + + default: + LOG(warn) << "HistogramManager::AddHistogram(): Unsupported dimension " << dimension + << " for histogram " << hname; + return; } // end switch(dimension) } @@ -553,7 +570,7 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // // get the list to which the histogram should be added - auto* hList = reinterpret_cast(fMainList->FindObject(histClass)); + auto* hList = dynamic_cast(fMainList->FindObject(histClass)); if (!hList) { LOG(warn) << "HistogramManager::AddHistogram(): Histogram list " << histClass << " not found!"; LOG(warn) << " Histogram not created"; @@ -609,34 +626,43 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co } h->Sumw2(); + std::span axisLabels{}; + if (axLabels) { + axisLabels = std::span{axLabels, static_cast(nDimensions)}; + } + // configure the THn histogram and count the allocated bins for (int idim = 0; idim < nDimensions; ++idim) { nbins *= (nBins[idim] + 2); TAxis* axis = h->GetAxis(idim); - if (fVariableNames[vars[idim]][0]) { + if (fVariableNames[vars[idim]][0] != 0) { axis->SetTitle(Form("%s %s", fVariableNames[vars[idim]].Data(), - (fVariableUnits[vars[idim]][0] ? Form("(%s)", fVariableUnits[vars[idim]].Data()) : ""))); + ((fVariableUnits[vars[idim]][0] != 0) ? Form("(%s)", fVariableUnits[vars[idim]].Data()) : ""))); } if (arr->At(1 + idim)) { axis->SetTitle(arr->At(1 + idim)->GetName()); } - if (axLabels && !axLabels[idim].IsNull()) { - MakeAxisLabels(axis, axLabels[idim].Data()); + if (!axisLabels.empty() && !axisLabels[idim].IsNull()) { + MakeAxisLabels(axis, axisLabels[idim].Data()); } fUsedVars[vars[idim]] = kTRUE; } if (!isdouble) { if (useSparse) { - hList->Add(reinterpret_cast(h)); - } else { - hList->Add(reinterpret_cast(h)); + if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); + } + } else if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); } } else { if (useSparse) { - hList->Add(reinterpret_cast(h)); - } else { - hList->Add(reinterpret_cast(h)); + if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); + } + } else if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); } } @@ -653,7 +679,7 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // // get the list to which the histogram should be added - auto* hList = reinterpret_cast(fMainList->FindObject(histClass)); + auto* hList = dynamic_cast(fMainList->FindObject(histClass)); if (!hList) { LOG(warn) << "HistogramManager::AddHistogram(): Histogram list " << histClass << " not found!"; LOG(warn) << " Histogram not created"; @@ -689,10 +715,13 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co auto* xmin = new double[nDimensions]; auto* xmax = new double[nDimensions]; int* nBins = new int[nDimensions]; + std::span limits{binLimits, static_cast(nDimensions)}; for (int idim = 0; idim < nDimensions; ++idim) { - nBins[idim] = binLimits[idim].GetSize() - 1; - xmin[idim] = binLimits[idim][0]; - xmax[idim] = binLimits[idim][nBins[idim]]; + const TArrayD& dimLimits = limits[idim]; + const double* dimBins = dimLimits.GetArray(); + nBins[idim] = dimLimits.GetSize() - 1; + xmin[idim] = dimBins[0]; + xmax[idim] = dimBins[nBins[idim]]; } // initialize the THn with equal spaced bins @@ -713,51 +742,60 @@ void HistogramManager::AddHistogram(const char* histClass, const char* hname, co // rebin the axes according to the user requested binning for (int idim = 0; idim < nDimensions; ++idim) { TAxis* axis = h->GetAxis(idim); - axis->Set(nBins[idim], binLimits[idim].GetArray()); + axis->Set(nBins[idim], limits[idim].GetArray()); } h->Sumw2(); + std::span axisLabels{}; + if (axLabels) { + axisLabels = std::span{axLabels, static_cast(nDimensions)}; + } + uint32_t bins = 1; for (int idim = 0; idim < nDimensions; ++idim) { bins *= (nBins[idim] + 2); TAxis* axis = h->GetAxis(idim); - if (fVariableNames[vars[idim]][0]) { + if (fVariableNames[vars[idim]][0] != 0) { axis->SetTitle(Form("%s %s", fVariableNames[vars[idim]].Data(), - (fVariableUnits[vars[idim]][0] ? Form("(%s)", fVariableUnits[vars[idim]].Data()) : ""))); + ((fVariableUnits[vars[idim]][0] != 0) ? Form("(%s)", fVariableUnits[vars[idim]].Data()) : ""))); } if (arr->At(1 + idim)) { axis->SetTitle(arr->At(1 + idim)->GetName()); } - if (axLabels && !axLabels[idim].IsNull()) { - MakeAxisLabels(axis, axLabels[idim].Data()); + if (!axisLabels.empty() && !axisLabels[idim].IsNull()) { + MakeAxisLabels(axis, axisLabels[idim].Data()); } fUsedVars[vars[idim]] = kTRUE; } if (!isdouble) { if (useSparse) { - hList->Add(reinterpret_cast(h)); - } else { - hList->Add(reinterpret_cast(h)); + if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); + } + } else if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); } } else { if (useSparse) { - hList->Add(reinterpret_cast(h)); - } else { - hList->Add(reinterpret_cast(h)); + if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); + } + } else if (auto* hn = dynamic_cast(h)) { + hList->Add(hn); } } fBinsAllocated += bins; } //__________________________________________________________________ -void HistogramManager::FillHistClass(const char* className, Float_t* values) +void HistogramManager::FillHistClass(const char* className, float* values) { // // fill a class of histograms // // get the needed histogram list - auto* hList = reinterpret_cast(fMainList->FindObject(className)); + auto* hList = dynamic_cast(fMainList->FindObject(className)); if (!hList) { // TODO: add some meaningfull error message /*LOG(warn) << "HistogramManager::FillHistClass(): Histogram list " << className << " not found!"; @@ -771,14 +809,13 @@ void HistogramManager::FillHistClass(const char* className, Float_t* values) TIter next(hList); TObject* h = nullptr; - bool isProfile; - bool isTHn; + bool isProfile = false; + bool isTHn = false; int dimension = 0; - bool isSparse = kFALSE; bool isFillLabelx = kFALSE; // TODO: At the moment, maximum 20 dimensions are foreseen for the THn histograms. We should make this more dynamic // But maybe its better to have it like to avoid dynamically allocating this array in the histogram loop - double fillValues[20] = {0.0}; + std::array fillValues{}; int varX = -1, varY = -1, varZ = -1, varT = -1, varW = -1; // loop over the histogram and std::list @@ -786,12 +823,14 @@ void HistogramManager::FillHistClass(const char* className, Float_t* values) for (auto varIter = varList.begin(); varIter != varList.end(); varIter++) { h = next(); // get the histogram // decode information from the vector of indices - isProfile = ((*varIter)[0] == 1 ? true : false); - isTHn = ((*varIter)[1] > 0 ? true : false); + isProfile = ((*varIter)[0] == 1); + isTHn = ((*varIter)[1] > 0); if (isTHn) { dimension = (*varIter)[1]; + } else if (auto* h1 = dynamic_cast(h)) { + dimension = h1->GetDimension(); } else { - dimension = (reinterpret_cast(h))->GetDimension(); + continue; } // get the various variable indices @@ -805,77 +844,101 @@ void HistogramManager::FillHistClass(const char* className, Float_t* values) varY = (*varIter)[4]; varZ = (*varIter)[5]; varT = (*varIter)[6]; - isFillLabelx = ((*varIter)[7] == 1 ? true : false); + isFillLabelx = ((*varIter)[7] == 1); } if (!isTHn) { switch (dimension) { case 1: if (isProfile) { + auto* profile = dynamic_cast(h); + if (!profile) { + break; + } if (varW > kNothing) { if (isFillLabelx) { - (reinterpret_cast(h))->Fill(Form("%d", static_cast(values[varX])), values[varY], values[varW]); + profile->Fill(Form("%d", static_cast(values[varX])), values[varY], values[varW]); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varW]); + profile->Fill(values[varX], values[varY], values[varW]); } } else { if (isFillLabelx) { - (reinterpret_cast(h))->Fill(Form("%d", static_cast(values[varX])), values[varY]); + profile->Fill(Form("%d", static_cast(values[varX])), values[varY]); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varY]); + profile->Fill(values[varX], values[varY]); } } } else { + auto* h1 = dynamic_cast(h); + if (!h1) { + break; + } if (varW > kNothing) { if (isFillLabelx) { - (reinterpret_cast(h))->Fill(Form("%d", static_cast(values[varX])), values[varW]); + h1->Fill(Form("%d", static_cast(values[varX])), values[varW]); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varW]); + h1->Fill(values[varX], values[varW]); } } else { if (isFillLabelx) { - (reinterpret_cast(h))->Fill(Form("%d", static_cast(values[varX])), 1.); + h1->Fill(Form("%d", static_cast(values[varX])), 1.); } else { - (reinterpret_cast(h))->Fill(values[varX]); + h1->Fill(values[varX]); } } } break; case 2: if (isProfile) { + auto* profile = dynamic_cast(h); + if (!profile) { + break; + } if (varW > kNothing) { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varZ], values[varW]); + profile->Fill(values[varX], values[varY], values[varZ], values[varW]); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varZ]); + profile->Fill(values[varX], values[varY], values[varZ]); } } else { + auto* h2 = dynamic_cast(h); + if (!h2) { + break; + } if (varW > kNothing) { if (isFillLabelx) { - (reinterpret_cast(h))->Fill(Form("%d", static_cast(values[varX])), values[varY], values[varW]); + h2->Fill(Form("%d", static_cast(values[varX])), values[varY], values[varW]); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varW]); + h2->Fill(values[varX], values[varY], values[varW]); } } else { if (isFillLabelx) { - (reinterpret_cast(h))->Fill(Form("%d", static_cast(values[varX])), values[varY], 1.); + h2->Fill(Form("%d", static_cast(values[varX])), values[varY], 1.); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varY]); + h2->Fill(values[varX], values[varY]); } } } break; case 3: if (isProfile) { + auto* profile = dynamic_cast(h); + if (!profile) { + break; + } if (varW > kNothing) { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varZ], values[varT], values[varW]); + profile->Fill(values[varX], values[varY], values[varZ], values[varT], values[varW]); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varZ], values[varT]); + profile->Fill(values[varX], values[varY], values[varZ], values[varT]); } } else { + auto* h3 = dynamic_cast(h); + if (!h3) { + break; + } if (varW > kNothing) { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varZ], values[varW]); + h3->Fill(values[varX], values[varY], values[varZ], values[varW]); } else { - (reinterpret_cast(h))->Fill(values[varX], values[varY], values[varZ]); + h3->Fill(values[varX], values[varY], values[varZ]); } } break; @@ -886,16 +949,16 @@ void HistogramManager::FillHistClass(const char* className, Float_t* values) // end if(!isTHn) } else { if (varW > kNothing) { - if (isSparse) { - (reinterpret_cast(h))->Fill(fillValues, values[varW]); - } else { - (reinterpret_cast(h))->Fill(fillValues, values[varW]); + if (auto* hn = dynamic_cast(h)) { + hn->Fill(fillValues.data(), values[varW]); + } else if (auto* hn = dynamic_cast(h)) { + hn->Fill(fillValues.data(), values[varW]); } } else { - if (isSparse) { - (reinterpret_cast(h))->Fill(fillValues); - } else { - (reinterpret_cast(h))->Fill(fillValues); + if (auto* hn = dynamic_cast(h)) { + hn->Fill(fillValues.data()); + } else if (auto* hn = dynamic_cast(h)) { + hn->Fill(fillValues.data()); } } } // end else @@ -924,14 +987,17 @@ void HistogramManager::Print(Option_t*) const // // Print the defined histograms // - cout << "###################################################################" << endl; - cout << "HistogramManager:: " << fMainList->GetName() << endl; + LOG(info) << "###################################################################"; + LOG(info) << "HistogramManager:: " << fMainList->GetName(); for (int i = 0; i < fMainList->GetEntries(); ++i) { - auto* list = reinterpret_cast(fMainList->At(i)); - cout << "************** List " << list->GetName() << endl; - for (int j = 0; j < list->GetEntries(); ++j) { - TObject* obj = list->At(j); - cout << obj->GetName() << ": " << obj->IsA()->GetName() << endl; + auto* histList = dynamic_cast(fMainList->At(i)); + if (!histList) { + continue; + } + LOG(info) << "************** List " << histList->GetName(); + for (int j = 0; j < histList->GetEntries(); ++j) { + TObject* obj = histList->At(j); + LOG(info) << obj->GetName() << ": " << obj->IsA()->GetName(); } } } diff --git a/PWGDQ/Core/HistogramManager.h b/PWGDQ/Core/HistogramManager.h index 94aa3ecec00..dacf5ad0411 100644 --- a/PWGDQ/Core/HistogramManager.h +++ b/PWGDQ/Core/HistogramManager.h @@ -46,9 +46,7 @@ class HistogramManager : public TNamed void SetMainHistogramList(THashList* list) { - if (fMainList) { - delete fMainList; - } + delete fMainList; fMainList = list; } @@ -91,11 +89,11 @@ class HistogramManager : public TNamed void SetUseDefaultVariableNames(bool flag) { fUseDefaultVariableNames = flag; } void SetDefaultVarNames(TString* vars, TString* units); - const bool* GetUsedVars() const { return fUsedVars; } + [[nodiscard]] const bool* GetUsedVars() const { return fUsedVars; } THashList* GetMainHistogramList() { return fMainList; } // get a histogram list - uint64_t GetAllocatedBins() const { return fBinsAllocated; } + [[nodiscard]] uint64_t GetAllocatedBins() const { return fBinsAllocated; } void Print(Option_t*) const override; private: @@ -108,15 +106,13 @@ class HistogramManager : public TNamed // various bool fUseDefaultVariableNames; //! toggle the usage of default variable names and units uint64_t fBinsAllocated; //! number of allocated bins - TString* fVariableNames; //! variable names - TString* fVariableUnits; //! variable units + std::vector fVariableNames; //! variable names + std::vector fVariableUnits; //! variable units void MakeAxisLabels(TAxis* ax, const char* labels); HistogramManager& operator=(const HistogramManager& c); HistogramManager(const HistogramManager& c); - - ClassDef(HistogramManager, 2) }; #endif // PWGDQ_CORE_HISTOGRAMMANAGER_H_ diff --git a/PWGDQ/Core/HistogramsLibrary.cxx b/PWGDQ/Core/HistogramsLibrary.cxx index 6e7f7ba31d5..d4e5e3eb2a8 100644 --- a/PWGDQ/Core/HistogramsLibrary.cxx +++ b/PWGDQ/Core/HistogramsLibrary.cxx @@ -28,7 +28,6 @@ #include -#include #include #include #include @@ -55,7 +54,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "TF_NMuons", "Number of muons per TF", false, 1000, 0.0, 5000.0, VarManager::kTFNMuons); hm->AddHistogram(histClass, "TF_NMFTs", "Number of MFT tracks per TF", false, 1000, 0.0, 200000.0, VarManager::kTFNMFTs); } - if (!groupStr.CompareTo("event")) { + if (groupStr.CompareTo("event") == 0) { if (!subGroupStr.Contains("generator") && !subGroupStr.Contains("pairing")) { hm->AddHistogram(histClass, "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); hm->AddHistogram(histClass, "VtxZ_Run", "Vtx Z", true, 1, -0.5, 0.5, VarManager::kRunNo, 60, -15.0, 15.0, VarManager::kVtxZ, 1, 0, 1, VarManager::kNothing, "", "", "", VarManager::kNothing, VarManager::kNothing, false, true); @@ -111,6 +110,17 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "CentFT0C_MultTPC", "CentFT0C vs MultTPC", false, 100, 0., 100., VarManager::kCentFT0C, 100, 0., 50000., VarManager::kMultTPC); hm->AddHistogram(histClass, "CentFT0C_Run", "Cent FT0C", true, 1, -0.5, 0.5, VarManager::kRunNo, 100, 0., 100., VarManager::kCentFT0C, 1, 0, 1, VarManager::kNothing, "", "", "", VarManager::kNothing, VarManager::kNothing, false, true); } + if (subGroupStr.Contains("alice3multcent")) { + hm->AddHistogram(histClass, "Cent", "Cent", false, 100, 0.0, 100.0, VarManager::kCent); + hm->AddHistogram(histClass, "MultPV", "MultPV", false, 100, 0.0, 10000.0, VarManager::kMultPV); + hm->AddHistogram(histClass, "MultPVeta1", "MultPVeta1", false, 100, 0.0, 10000.0, VarManager::kMultPVeta1); + hm->AddHistogram(histClass, "MultPVetaHalf", "MultPVetaHalf", false, 100, 0.0, 10000.0, VarManager::kMultPVetaHalf); + hm->AddHistogram(histClass, "MultGlobal", "MultGlobal", false, 100, 0.0, 10000.0, VarManager::kMultGlobalTracks); + hm->AddHistogram(histClass, "MultGlobalPV", "MultGlobalPV", false, 100, 0.0, 10000.0, VarManager::kMultGlobalTracksPV); + } + if (subGroupStr.Contains("randomplane")) { + hm->AddHistogram(histClass, "random plane angle", "random Psi", false, 100, -TMath::Pi() / 2, TMath::Pi() / 2, VarManager::kRandomPsi2); + } if (subGroupStr.Contains("mult")) { if (subGroupStr.Contains("pp")) { hm->AddHistogram(histClass, "MultTPC", "MultTPC", false, 250, 0.0, 500.0, VarManager::kMultTPC); @@ -246,6 +256,8 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "MCVtxZ_VtxZ", "Vtx Z (MC vs rec)", false, 75, -15.0, 15.0, VarManager::kVtxZ, 75, -15.0, 15.0, VarManager::kMCVtxZ); hm->AddHistogram(histClass, "MCVtxZ", "Vtx Z (MC)", false, 75, -15.0, 15.0, VarManager::kMCVtxZ); hm->AddHistogram(histClass, "MCImpPar_CentVZERO", "MC impact param vs CentVZERO", false, 50, 0.0, 100.0, VarManager::kCentVZERO, 20, 0.0, 20.0, VarManager::kMCEventImpParam); + hm->AddHistogram(histClass, "MCEventPlaneAngle", "MC event plane angle", false, 100, -TMath::Pi(), TMath::Pi(), VarManager::kMCEventPlaneAngle); + hm->AddHistogram(histClass, "kMCEventCentrFT0C_CentFT0C", "MC Centrality FT0C vs Centrality FT0C", false, 100, 0.0, 100.0, VarManager::kCentFT0C, 100, 0.0, 100.0, VarManager::kMCEventCentrFT0C); } if (subGroupStr.Contains("generator")) { hm->AddHistogram(histClass, "MCVtxX", "Vtx X", false, 1000, -0.5, 0.5, VarManager::kMCVtxX); @@ -270,20 +282,32 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Q2Y0B_CentFT0C", "", false, 100, 0.0, 100.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kQ2Y0B); hm->AddHistogram(histClass, "Q2X0C_CentFT0C", "", false, 100, 0.0, 100.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kQ2X0C); hm->AddHistogram(histClass, "Q2Y0C_CentFT0C", "", false, 100, 0.0, 100.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kQ2Y0C); + hm->AddHistogram(histClass, "Q2YYAB_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2YYAB); + hm->AddHistogram(histClass, "Q2XXAB_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2XXAB); + hm->AddHistogram(histClass, "Q2XYAB_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2XYAB); + hm->AddHistogram(histClass, "Q2YXAB_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2YXAB); + hm->AddHistogram(histClass, "Q2YYAC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2YYAC); + hm->AddHistogram(histClass, "Q2XXAC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2XXAC); + hm->AddHistogram(histClass, "Q2XYAC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2XYAC); + hm->AddHistogram(histClass, "Q2YXAC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2YXAC); + hm->AddHistogram(histClass, "Q2YYBC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2YYBC); + hm->AddHistogram(histClass, "Q2XXBC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2XXBC); + hm->AddHistogram(histClass, "Q2XYBC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2XYBC); + hm->AddHistogram(histClass, "Q2YXBC_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -1.0, 1.0, VarManager::kQ2YXBC); hm->AddHistogram(histClass, "Psi2A_CentFT0C", "", false, 100, 0.0, 100.0, VarManager::kCentFT0C, 100, -2.0, 2.0, VarManager::kPsi2A); hm->AddHistogram(histClass, "Psi2B_CentFT0C", "", false, 100, 0.0, 100.0, VarManager::kCentFT0C, 100, -2.0, 2.0, VarManager::kPsi2B); hm->AddHistogram(histClass, "Psi2C_CentFT0C", "", false, 100, 0.0, 100.0, VarManager::kCentFT0C, 100, -2.0, 2.0, VarManager::kPsi2C); } if (subGroupStr.Contains("all-qvector")) { - int varZNA[3] = {VarManager::kQ1ZNAX, VarManager::kQ1ZNAY, VarManager::kCentFT0C}; - int varZNC[3] = {VarManager::kQ1ZNCX, VarManager::kQ1ZNCY, VarManager::kCentFT0C}; + std::array varZNA = {VarManager::kQ1ZNAX, VarManager::kQ1ZNAY, VarManager::kCentFT0C}; + std::array varZNC = {VarManager::kQ1ZNCX, VarManager::kQ1ZNCY, VarManager::kCentFT0C}; - int bins[3] = {500, 500, 18}; - double minBins[3] = {-10, -10, 0}; - double maxBins[3] = {10, 10, 90}; - hm->AddHistogram(histClass, "Q1ZNAX_Q1ZNAY_CentFT0C", "", 3, varZNA, bins, minBins, maxBins, 0, -1, kTRUE); - hm->AddHistogram(histClass, "Q1ZNCX_Q1ZNCY_CentFT0C", "", 3, varZNC, bins, minBins, maxBins, 0, -1, kTRUE); + std::array bins = {500, 500, 18}; + std::array minBins = {-10, -10, 0}; + std::array maxBins = {10, 10, 90}; + hm->AddHistogram(histClass, "Q1ZNAX_Q1ZNAY_CentFT0C", "", 3, varZNA.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); + hm->AddHistogram(histClass, "Q1ZNCX_Q1ZNCY_CentFT0C", "", 3, varZNC.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); hm->AddHistogram(histClass, "IntercalibZNA_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -50.0, 50.0, VarManager::KIntercalibZNA); hm->AddHistogram(histClass, "IntercalibZNC_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -50.0, 50.0, VarManager::KIntercalibZNC); @@ -414,19 +438,19 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "R2SP_TPCFT0A_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_AB); hm->AddHistogram(histClass, "R2SP_TPCFT0C_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_AC); hm->AddHistogram(histClass, "R2SP_FT0AFT0C_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_BC); - hm->AddHistogram(histClass, "R2SP_FT0CTPCPOS_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0CTPCPOS); - hm->AddHistogram(histClass, "R2SP_FT0CTPCNEG_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0CTPCNEG); - hm->AddHistogram(histClass, "R2SP_FT0ATPCPOS_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0ATPCPOS); - hm->AddHistogram(histClass, "R2SP_FT0ATPCNEG_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0ATPCNEG); - hm->AddHistogram(histClass, "R3SP_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR3SP); + hm->AddHistogram(histClass, "R2SP_FT0CTPCPOS_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0CTPCPOS); + hm->AddHistogram(histClass, "R2SP_FT0CTPCNEG_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0CTPCNEG); + hm->AddHistogram(histClass, "R2SP_FT0ATPCPOS_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0ATPCPOS); + hm->AddHistogram(histClass, "R2SP_FT0ATPCNEG_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2SP_FT0ATPCNEG); + hm->AddHistogram(histClass, "R3SP_CentFT0C", "", true, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR3SP); hm->AddHistogram(histClass, "R2EP_TPCFT0A_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_AB); hm->AddHistogram(histClass, "R2EP_TPCFT0C_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_AC); hm->AddHistogram(histClass, "R2EP_FT0CFT0A_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_BC); - hm->AddHistogram(histClass, "R2EP_FT0CTPCPOS_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0CTPCPOS); - hm->AddHistogram(histClass, "R2EP_FT0CTPCNEG_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0CTPCNEG); - hm->AddHistogram(histClass, "R2EP_FT0ATPCPOS_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0ATPCPOS); - hm->AddHistogram(histClass, "R2EP_FT0ATPCNEG_CentFT0C", "", false, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0ATPCNEG); - hm->AddHistogram(histClass, "R3EP_CentFT0C", "", true, 18, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR3EP); + hm->AddHistogram(histClass, "R2EP_FT0CTPCPOS_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0CTPCPOS); + hm->AddHistogram(histClass, "R2EP_FT0CTPCNEG_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0CTPCNEG); + hm->AddHistogram(histClass, "R2EP_FT0ATPCPOS_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0ATPCPOS); + hm->AddHistogram(histClass, "R2EP_FT0ATPCNEG_CentFT0C", "", false, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR2EP_FT0ATPCNEG); + hm->AddHistogram(histClass, "R3EP_CentFT0C", "", true, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, -10.0, 10.0, VarManager::kR3EP); } if (subGroupStr.Contains("reso-profile")) { hm->AddHistogram(histClass, "Profile_R2SP_TPCFT0A_CentFT0C", "Profile_R2SP_TPCFT0A_CentFT0C", true, 90, 0.0, 90.0, VarManager::kCentFT0C, 500, 0.0, 5.0, VarManager::kR2SP_AB, 0, 0, 0, -1, "", "", "", -1, VarManager::kWR2SP_AB); @@ -611,7 +635,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } // end "event" - if (!groupStr.CompareTo("two-collisions")) { + if (groupStr.CompareTo("two-collisions") == 0) { hm->AddHistogram(histClass, "DeltaZ", "z_{1} - z_{2}", false, 400, -20., 20., VarManager::kTwoEvDeltaZ); hm->AddHistogram(histClass, "DeltaZ_Z1", "z_{1} - z_{2} vs z_{1}", false, 24, -12., 12., VarManager::kTwoEvPosZ1, 300, -15., 15., VarManager::kTwoEvDeltaZ); hm->AddHistogram(histClass, "DeltaR", "r_{1} - r_{2}", false, 200, -0.1, 0.1, VarManager::kTwoEvDeltaR); @@ -622,7 +646,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "NContrib1vs2", "n.contrib 1 vs 2", false, 100, 0.0, 100.0, VarManager::kTwoEvPVcontrib1, 100, 0.0, 100.0, VarManager::kTwoEvPVcontrib2); } - if (!groupStr.CompareTo("track")) { + if (groupStr.CompareTo("track") == 0) { hm->AddHistogram(histClass, "Pt", "p_{T} distribution", false, 2000, 0.0, 20.0, VarManager::kPt); hm->AddHistogram(histClass, "Eta", "#eta distribution", false, 500, -5.0, 5.0, VarManager::kEta); hm->AddHistogram(histClass, "Phi", "#varphi distribution", false, 500, -2. * o2::constants::math::PI, 2. * o2::constants::math::PI, VarManager::kPhi); @@ -727,32 +751,36 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h if (subGroupStr.Contains("tpcpid")) { if (subGroupStr.Contains("tpcpid_fine")) { // fine binning for pIN: steps in 10 MeV/c from 0 to 1 GeV/c and 100 MeV/c up to 10 GeV/c - double pIN_bins[281]; - for (int i = 0; i <= 200; i++) + std::array pIN_bins{}; + for (int i = 0; i <= 200; i++) { pIN_bins[i] = 0.01 * i; - for (int i = 1; i <= 80; i++) + } + for (int i = 1; i <= 80; i++) { pIN_bins[200 + i] = 2. + 0.1 * i; - int nbins_pIN = sizeof(pIN_bins) / sizeof(*pIN_bins) - 1; + } + int nbins_pIN = static_cast(pIN_bins.size()) - 1; - double TPCdEdx_bins[201]; - for (int i = 0; i <= 200; i++) + std::array TPCdEdx_bins{}; + for (int i = 0; i <= 200; i++) { TPCdEdx_bins[i] = i; - int nbins_TPCdEdx = sizeof(TPCdEdx_bins) / sizeof(*TPCdEdx_bins) - 1; + } + int nbins_TPCdEdx = static_cast(TPCdEdx_bins.size()) - 1; - double nSigma_bins[101]; - for (int i = 0; i <= 100; i++) + std::array nSigma_bins{}; + for (int i = 0; i <= 100; i++) { nSigma_bins[i] = -5. + 0.1 * i; - int nbins_nSigma = sizeof(nSigma_bins) / sizeof(*nSigma_bins) - 1; + } + int nbins_nSigma = static_cast(nSigma_bins.size()) - 1; - hm->AddHistogram(histClass, "TPCdedx_pIN", "TPC dE/dx vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_TPCdEdx, TPCdEdx_bins, VarManager::kTPCsignal); - hm->AddHistogram(histClass, "TPCnSigEle_pIN", "TPC n-#sigma(e) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTPCnSigmaEl); - hm->AddHistogram(histClass, "TPCnSigPi_pIN", "TPC n-#sigma(#pi) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTPCnSigmaPi); - hm->AddHistogram(histClass, "TPCnSigKa_pIN", "TPC n-#sigma(K) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTPCnSigmaKa); - hm->AddHistogram(histClass, "TPCnSigPr_pIN", "TPC n-#sigma(p) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTPCnSigmaPr); + hm->AddHistogram(histClass, "TPCdedx_pIN", "TPC dE/dx vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_TPCdEdx, TPCdEdx_bins.data(), VarManager::kTPCsignal); + hm->AddHistogram(histClass, "TPCnSigEle_pIN", "TPC n-#sigma(e) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTPCnSigmaEl); + hm->AddHistogram(histClass, "TPCnSigPi_pIN", "TPC n-#sigma(#pi) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTPCnSigmaPi); + hm->AddHistogram(histClass, "TPCnSigKa_pIN", "TPC n-#sigma(K) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTPCnSigmaKa); + hm->AddHistogram(histClass, "TPCnSigPr_pIN", "TPC n-#sigma(p) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTPCnSigmaPr); if (subGroupStr.Contains("tpcpid_fine_corr")) { - hm->AddHistogram(histClass, "TPCnSigEl_Corr_pIN", "TPC n-#sigma(e) Corr. vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTPCnSigmaEl_Corr); - hm->AddHistogram(histClass, "TPCnSigPi_Corr_pIN", "TPC n-#sigma(#pi) Corr. vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTPCnSigmaPi_Corr); - hm->AddHistogram(histClass, "TPCnSigPr_Corr_pIN", "TPC n-#sigma(p) Corr. vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTPCnSigmaPr_Corr); + hm->AddHistogram(histClass, "TPCnSigEl_Corr_pIN", "TPC n-#sigma(e) Corr. vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTPCnSigmaEl_Corr); + hm->AddHistogram(histClass, "TPCnSigPi_Corr_pIN", "TPC n-#sigma(#pi) Corr. vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTPCnSigmaPi_Corr); + hm->AddHistogram(histClass, "TPCnSigPr_Corr_pIN", "TPC n-#sigma(p) Corr. vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTPCnSigmaPr_Corr); } } else { hm->AddHistogram(histClass, "TPCdedx_pIN", "TPC dE/dx vs pIN", false, 100, 0.0, 20.0, VarManager::kPin, 150, 0.0, 150., VarManager::kTPCsignal); @@ -801,86 +829,92 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h if (subGroupStr.Contains("postcalib")) { const int kNvarsPID = 4; const int kTPCnsigmaNbins = 70; - double tpcNsigmaBinLims[kTPCnsigmaNbins + 1]; - for (int i = 0; i <= kTPCnsigmaNbins; ++i) + std::array tpcNsigmaBinLims{}; + for (int i = 0; i <= kTPCnsigmaNbins; ++i) { tpcNsigmaBinLims[i] = -7.0 + 0.2 * i; + } const int kPinEleNbins = 20; - double pinEleBinLims[kPinEleNbins + 1] = {0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0, 12.0, 16.0, 20.0}; + std::array pinEleBinLims = {0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 8.0, 10.0, 12.0, 16.0, 20.0}; const int kEtaNbins = 9; - double etaBinLimsI[kEtaNbins + 1] = {-0.9, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.7, 0.9}; + std::array etaBinLimsI = {-0.9, -0.7, -0.5, -0.3, -0.1, 0.1, 0.3, 0.5, 0.7, 0.9}; const int kTPCnClusterbins = 16; - double tpcNclusterBinLims[kTPCnClusterbins + 1]; - for (int i = 0; i <= kTPCnClusterbins; ++i) + std::array tpcNclusterBinLims{}; + for (int i = 0; i <= kTPCnClusterbins; ++i) { tpcNclusterBinLims[i] = 10 * i; + } - TArrayD nSigBinLimits[kNvarsPID]; - nSigBinLimits[0] = TArrayD(kTPCnsigmaNbins + 1, tpcNsigmaBinLims); - nSigBinLimits[1] = TArrayD(kTPCnClusterbins + 1, tpcNclusterBinLims); - nSigBinLimits[2] = TArrayD(kPinEleNbins + 1, pinEleBinLims); - nSigBinLimits[3] = TArrayD(kEtaNbins + 1, etaBinLimsI); + std::array nSigBinLimits; + nSigBinLimits[0] = TArrayD(kTPCnsigmaNbins + 1, tpcNsigmaBinLims.data()); + nSigBinLimits[1] = TArrayD(kTPCnClusterbins + 1, tpcNclusterBinLims.data()); + nSigBinLimits[2] = TArrayD(kPinEleNbins + 1, pinEleBinLims.data()); + nSigBinLimits[3] = TArrayD(kEtaNbins + 1, etaBinLimsI.data()); const int kPinKaNbins = 15; - double pinKaBinLims[kPinKaNbins + 1] = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0}; + std::array pinKaBinLims = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 2.0, 3.0, 4.0, 5.0, 10.0}; - TArrayD nSigBinLimitsKa[kNvarsPID]; - nSigBinLimitsKa[0] = TArrayD(kTPCnsigmaNbins + 1, tpcNsigmaBinLims); - nSigBinLimitsKa[1] = TArrayD(kTPCnClusterbins + 1, tpcNclusterBinLims); - nSigBinLimitsKa[2] = TArrayD(kPinKaNbins + 1, pinKaBinLims); - nSigBinLimitsKa[3] = TArrayD(kEtaNbins + 1, etaBinLimsI); + std::array nSigBinLimitsKa; + nSigBinLimitsKa[0] = TArrayD(kTPCnsigmaNbins + 1, tpcNsigmaBinLims.data()); + nSigBinLimitsKa[1] = TArrayD(kTPCnClusterbins + 1, tpcNclusterBinLims.data()); + nSigBinLimitsKa[2] = TArrayD(kPinKaNbins + 1, pinKaBinLims.data()); + nSigBinLimitsKa[3] = TArrayD(kEtaNbins + 1, etaBinLimsI.data()); if (subGroupStr.Contains("electron")) { - int varsPIDnSigEle[kNvarsPID] = {VarManager::kTPCnSigmaEl, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - int varsPIDnSigEle_Corr[kNvarsPID] = {VarManager::kTPCnSigmaEl_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - hm->AddHistogram(histClass, "nSigmaTPCelectron", "TPC n_{#sigma}(e) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigEle, nSigBinLimits); - hm->AddHistogram(histClass, "nSigmaTPCelectron_Corr", "TPC n_{#sigma}^{Corr}(e) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigEle_Corr, nSigBinLimits); + std::array varsPIDnSigEle = {VarManager::kTPCnSigmaEl, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + std::array varsPIDnSigEle_Corr = {VarManager::kTPCnSigmaEl_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + hm->AddHistogram(histClass, "nSigmaTPCelectron", "TPC n_{#sigma}(e) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigEle.data(), nSigBinLimits.data()); + hm->AddHistogram(histClass, "nSigmaTPCelectron_Corr", "TPC n_{#sigma}^{Corr}(e) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigEle_Corr.data(), nSigBinLimits.data()); } if (subGroupStr.Contains("pion")) { - int varsPIDnSigPion[kNvarsPID] = {VarManager::kTPCnSigmaPi, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - int varsPIDnSigPion_Corr[kNvarsPID] = {VarManager::kTPCnSigmaPi_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - hm->AddHistogram(histClass, "nSigmaTPCpion", "TPC n_{#sigma}(pion) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigPion, nSigBinLimits); - hm->AddHistogram(histClass, "nSigmaTPCpion_Corr", "TPC n_{#sigma}^{Corr}(pion) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigPion_Corr, nSigBinLimits); + std::array varsPIDnSigPion = {VarManager::kTPCnSigmaPi, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + std::array varsPIDnSigPion_Corr = {VarManager::kTPCnSigmaPi_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + hm->AddHistogram(histClass, "nSigmaTPCpion", "TPC n_{#sigma}(pion) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigPion.data(), nSigBinLimits.data()); + hm->AddHistogram(histClass, "nSigmaTPCpion_Corr", "TPC n_{#sigma}^{Corr}(pion) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigPion_Corr.data(), nSigBinLimits.data()); } if (subGroupStr.Contains("kaon")) { - int varsPIDnSigKaon[kNvarsPID] = {VarManager::kTPCnSigmaKa, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - int varsPIDnSigKaon_Corr[kNvarsPID] = {VarManager::kTPCnSigmaKa_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - hm->AddHistogram(histClass, "nSigmaTPCkaon", "TPC n_{#sigma}(kaon) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigKaon, nSigBinLimitsKa); - hm->AddHistogram(histClass, "nSigmaTPCkaon_Corr", "TPC n_{#sigma}^{Corr}(kaon) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigKaon_Corr, nSigBinLimitsKa); + std::array varsPIDnSigKaon = {VarManager::kTPCnSigmaKa, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + std::array varsPIDnSigKaon_Corr = {VarManager::kTPCnSigmaKa_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + hm->AddHistogram(histClass, "nSigmaTPCkaon", "TPC n_{#sigma}(kaon) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigKaon.data(), nSigBinLimitsKa.data()); + hm->AddHistogram(histClass, "nSigmaTPCkaon_Corr", "TPC n_{#sigma}^{Corr}(kaon) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigKaon_Corr.data(), nSigBinLimitsKa.data()); } if (subGroupStr.Contains("proton")) { - int varsPIDnSigProton[kNvarsPID] = {VarManager::kTPCnSigmaPr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - int varsPIDnSigProton_Corr[kNvarsPID] = {VarManager::kTPCnSigmaPr_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; - hm->AddHistogram(histClass, "nSigmaTPCproton", "TPC n_{#sigma}(proton) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigProton, nSigBinLimits); - hm->AddHistogram(histClass, "nSigmaTPCproton_Corr", "TPC n_{#sigma}^{Corr}(proton) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigProton_Corr, nSigBinLimits); + std::array varsPIDnSigProton = {VarManager::kTPCnSigmaPr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + std::array varsPIDnSigProton_Corr = {VarManager::kTPCnSigmaPr_Corr, VarManager::kTPCncls, VarManager::kPin, VarManager::kEta}; + hm->AddHistogram(histClass, "nSigmaTPCproton", "TPC n_{#sigma}(proton) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigProton.data(), nSigBinLimits.data()); + hm->AddHistogram(histClass, "nSigmaTPCproton_Corr", "TPC n_{#sigma}^{Corr}(proton) Vs normNcluster Vs Pin Vs Eta", kNvarsPID, varsPIDnSigProton_Corr.data(), nSigBinLimits.data()); } } if (subGroupStr.Contains("tofpid")) { if (subGroupStr.Contains("tofpid_fine")) { // fine binning for pIN: steps in 10 MeV/c from 0 to 1 GeV/c and 100 MeV/c up to 10 GeV/c - double pIN_bins[281]; - for (int i = 0; i <= 200; i++) + std::array pIN_bins{}; + for (int i = 0; i <= 200; i++) { pIN_bins[i] = 0.01 * i; - for (int i = 1; i <= 80; i++) + } + for (int i = 1; i <= 80; i++) { pIN_bins[200 + i] = 2. + 0.1 * i; - int nbins_pIN = sizeof(pIN_bins) / sizeof(*pIN_bins) - 1; + } + int nbins_pIN = static_cast(pIN_bins.size()) - 1; - double TOFbeta_bins[241]; - for (int i = 0; i <= 240; i++) + std::array TOFbeta_bins{}; + for (int i = 0; i <= 240; i++) { TOFbeta_bins[i] = 0.005 * i; - int nbins_TOFbeta = sizeof(TOFbeta_bins) / sizeof(*TOFbeta_bins) - 1; + } + int nbins_TOFbeta = static_cast(TOFbeta_bins.size()) - 1; - double nSigma_bins[101]; - for (int i = 0; i <= 100; i++) + std::array nSigma_bins{}; + for (int i = 0; i <= 100; i++) { nSigma_bins[i] = -5. + 0.1 * i; - int nbins_nSigma = sizeof(nSigma_bins) / sizeof(*nSigma_bins) - 1; + } + int nbins_nSigma = static_cast(nSigma_bins.size()) - 1; - hm->AddHistogram(histClass, "TOFbeta_pIN", "TOF #beta vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_TOFbeta, TOFbeta_bins, VarManager::kTOFbeta); - hm->AddHistogram(histClass, "TOFnSigEle_pIN", "TOF n-#sigma(e) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTOFnSigmaEl); - hm->AddHistogram(histClass, "TOFnSigPi_pIN", "TOF n-#sigma(#pi) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTOFnSigmaPi); - hm->AddHistogram(histClass, "TOFnSigKa_pIN", "TOF n-#sigma(K) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTOFnSigmaKa); - hm->AddHistogram(histClass, "TOFnSigPr_pIN", "TOF n-#sigma(p) vs pIN", false, nbins_pIN, pIN_bins, VarManager::kPin, nbins_nSigma, nSigma_bins, VarManager::kTOFnSigmaPr); + hm->AddHistogram(histClass, "TOFbeta_pIN", "TOF #beta vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_TOFbeta, TOFbeta_bins.data(), VarManager::kTOFbeta); + hm->AddHistogram(histClass, "TOFnSigEle_pIN", "TOF n-#sigma(e) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTOFnSigmaEl); + hm->AddHistogram(histClass, "TOFnSigPi_pIN", "TOF n-#sigma(#pi) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTOFnSigmaPi); + hm->AddHistogram(histClass, "TOFnSigKa_pIN", "TOF n-#sigma(K) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTOFnSigmaKa); + hm->AddHistogram(histClass, "TOFnSigPr_pIN", "TOF n-#sigma(p) vs pIN", false, nbins_pIN, pIN_bins.data(), VarManager::kPin, nbins_nSigma, nSigma_bins.data(), VarManager::kTOFnSigmaPr); } else { hm->AddHistogram(histClass, "TOFbeta_pIN", "TOF #beta vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 240, 0.0, 1.2, VarManager::kTOFbeta); hm->AddHistogram(histClass, "TOFnSigEle_pIN", "TOF n-#sigma(e) vs pIN", false, 100, 0.0, 10.0, VarManager::kPin, 100, -5.0, 5.0, VarManager::kTOFnSigmaEl); @@ -892,20 +926,25 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h if (subGroupStr.Contains("pidcorre")) { const int kNvarsPID = 3; const int kNbins_pIN = 169; - double pIN_bins[kNbins_pIN + 1]; - for (int i = 0; i <= 140; i++) + std::array pIN_bins{}; + for (int i = 0; i <= 140; i++) { pIN_bins[i] = 0.01 * i + 0.1; - for (int i = 1; i <= 15; i++) + } + for (int i = 1; i <= 15; i++) { pIN_bins[140 + i] = 1.5 + 0.1 * i; - for (int i = 1; i <= 14; i++) + } + for (int i = 1; i <= 14; i++) { pIN_bins[155 + i] = 3. + 0.5 * i; + } const int kNbins_pINmore = 135; - double pIN_binsmore[kNbins_pINmore + 1]; - for (int i = 0; i <= 120; i++) + std::array pIN_binsmore{}; + for (int i = 0; i <= 120; i++) { pIN_binsmore[i] = 0.01 * i + 0.3; - for (int i = 1; i <= 10; i++) + } + for (int i = 1; i <= 10; i++) { pIN_binsmore[120 + i] = 1.5 + 0.2 * i; + } pIN_binsmore[131] = 4.; pIN_binsmore[132] = 5.; pIN_binsmore[133] = 6.; @@ -913,55 +952,60 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h pIN_binsmore[135] = 10.; const int kNbins_nSigma = 100; - double nSigma_bins[kNbins_nSigma + 1]; - for (int i = 0; i <= kNbins_nSigma; i++) + std::array nSigma_bins{}; + for (int i = 0; i <= kNbins_nSigma; i++) { nSigma_bins[i] = -5. + 0.1 * i; + } const int kNbins_nSigmamore = 50; - double nSigma_binsmore[kNbins_nSigmamore + 1]; - for (int i = 0; i <= kNbins_nSigmamore; i++) + std::array nSigma_binsmore{}; + for (int i = 0; i <= kNbins_nSigmamore; i++) { nSigma_binsmore[i] = -5. + 0.2 * i; + } const int kNbins_nSigmagrob = 24; - double nSigma_binsgrob[kNbins_nSigmagrob + 1]; - for (int i = 0; i <= kNbins_nSigmagrob; i++) + std::array nSigma_binsgrob{}; + for (int i = 0; i <= kNbins_nSigmagrob; i++) { nSigma_binsgrob[i] = -6. + 0.5 * i; + } const int kNbins_TOFbeta = 120; - double TOFbeta_bins[kNbins_TOFbeta + 1]; - for (int i = 0; i <= kNbins_TOFbeta; i++) + std::array TOFbeta_bins{}; + for (int i = 0; i <= kNbins_TOFbeta; i++) { TOFbeta_bins[i] = 0.01 * i; + } const int kNbins_TPCdEdx = 140; - double TPCdEdx_bins[kNbins_TPCdEdx + 1]; - for (int i = 0; i <= kNbins_TPCdEdx; i++) + std::array TPCdEdx_bins{}; + for (int i = 0; i <= kNbins_TPCdEdx; i++) { TPCdEdx_bins[i] = i + 20; + } - TArrayD nSigmaBinLimits[kNvarsPID]; - nSigmaBinLimits[0] = TArrayD(kNbins_pIN + 1, pIN_bins); - nSigmaBinLimits[1] = TArrayD(kNbins_nSigma + 1, nSigma_bins); - nSigmaBinLimits[2] = TArrayD(kNbins_nSigma + 1, nSigma_bins); + std::array nSigmaBinLimits; + nSigmaBinLimits[0] = TArrayD(kNbins_pIN + 1, pIN_bins.data()); + nSigmaBinLimits[1] = TArrayD(kNbins_nSigma + 1, nSigma_bins.data()); + nSigmaBinLimits[2] = TArrayD(kNbins_nSigma + 1, nSigma_bins.data()); - TArrayD nSignalBinLimits[kNvarsPID]; - nSignalBinLimits[0] = TArrayD(kNbins_pIN + 1, pIN_bins); - nSignalBinLimits[1] = TArrayD(kNbins_TPCdEdx + 1, TPCdEdx_bins); - nSignalBinLimits[2] = TArrayD(kNbins_TOFbeta + 1, TOFbeta_bins); + std::array nSignalBinLimits; + nSignalBinLimits[0] = TArrayD(kNbins_pIN + 1, pIN_bins.data()); + nSignalBinLimits[1] = TArrayD(kNbins_TPCdEdx + 1, TPCdEdx_bins.data()); + nSignalBinLimits[2] = TArrayD(kNbins_TOFbeta + 1, TOFbeta_bins.data()); - int varsPIDnSignal[kNvarsPID] = {VarManager::kPin, VarManager::kTPCsignal, VarManager::kTOFbeta}; - hm->AddHistogram(histClass, "nSignalTPCTOF", "", kNvarsPID, varsPIDnSignal, nSignalBinLimits); + std::array varsPIDnSignal = {VarManager::kPin, VarManager::kTPCsignal, VarManager::kTOFbeta}; + hm->AddHistogram(histClass, "nSignalTPCTOF", "", kNvarsPID, varsPIDnSignal.data(), nSignalBinLimits.data()); if (subGroupStr.Contains("more")) { const int kNvarsPIDmore = 4; - TArrayD nSigmaBinLimitsmore[kNvarsPIDmore]; - nSigmaBinLimitsmore[0] = TArrayD(kNbins_pINmore + 1, pIN_binsmore); - nSigmaBinLimitsmore[1] = TArrayD(kNbins_nSigmamore + 1, nSigma_binsmore); - nSigmaBinLimitsmore[2] = TArrayD(kNbins_nSigmagrob + 1, nSigma_binsgrob); - nSigmaBinLimitsmore[3] = TArrayD(kNbins_nSigmamore + 1, nSigma_binsmore); - int varsPIDnSigmamore[kNvarsPIDmore] = {VarManager::kPin, VarManager::kTPCnSigmaEl, VarManager::kTPCnSigmaPi, VarManager::kTOFnSigmaEl}; - hm->AddHistogram(histClass, "nSigmaTPCTOF", "", kNvarsPIDmore, varsPIDnSigmamore, nSigmaBinLimitsmore); + std::array nSigmaBinLimitsmore; + nSigmaBinLimitsmore[0] = TArrayD(kNbins_pINmore + 1, pIN_binsmore.data()); + nSigmaBinLimitsmore[1] = TArrayD(kNbins_nSigmamore + 1, nSigma_binsmore.data()); + nSigmaBinLimitsmore[2] = TArrayD(kNbins_nSigmagrob + 1, nSigma_binsgrob.data()); + nSigmaBinLimitsmore[3] = TArrayD(kNbins_nSigmamore + 1, nSigma_binsmore.data()); + std::array varsPIDnSigmamore = {VarManager::kPin, VarManager::kTPCnSigmaEl, VarManager::kTPCnSigmaPi, VarManager::kTOFnSigmaEl}; + hm->AddHistogram(histClass, "nSigmaTPCTOF", "", kNvarsPIDmore, varsPIDnSigmamore.data(), nSigmaBinLimitsmore.data()); } else { - int varsPIDnSigma[kNvarsPID] = {VarManager::kPin, VarManager::kTPCnSigmaEl, VarManager::kTOFnSigmaEl}; - hm->AddHistogram(histClass, "nSigmaTPCTOF", "", kNvarsPID, varsPIDnSigma, nSigmaBinLimits); + std::array varsPIDnSigma = {VarManager::kPin, VarManager::kTPCnSigmaEl, VarManager::kTOFnSigmaEl}; + hm->AddHistogram(histClass, "nSigmaTPCTOF", "", kNvarsPID, varsPIDnSigma.data(), nSigmaBinLimits.data()); } } if (subGroupStr.Contains("runbyrun")) { @@ -1009,6 +1053,21 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } } + if (subGroupStr.Contains("emcal")) { + hm->AddHistogram(histClass, "EMCalEnergy", "EMCal cluster energy", false, 500, 0.0, 50.0, VarManager::kEMCalEnergy); + hm->AddHistogram(histClass, "EMCalCoreEnergy", "EMCal cluster core energy", false, 500, 0.0, 50.0, VarManager::kEMCalCoreEnergy); + hm->AddHistogram(histClass, "EMCalEta_Phi", "EMCal cluster #eta vs #varphi", false, 100, -1.0, 1.0, VarManager::kEMCalEta, 180, 0.0, TMath::TwoPi(), VarManager::kEMCalPhi); + hm->AddHistogram(histClass, "EMCalM02", "EMCal cluster M02", false, 200, 0.0, 2.0, VarManager::kEMCalM02); + hm->AddHistogram(histClass, "EMCalM20", "EMCal cluster M20", false, 200, 0.0, 2.0, VarManager::kEMCalM20); + hm->AddHistogram(histClass, "EMCalNCells", "EMCal cluster no. cells", false, 50, -0.5, 49.5, VarManager::kEMCalNCells); + hm->AddHistogram(histClass, "EMCalTime", "EMCal cluster time", false, 400, -100.0, 100.0, VarManager::kEMCalTime); + hm->AddHistogram(histClass, "EMCalEnergy_Time", "EMCal cluster energy vs time", false, 100, 0.0, 50.0, VarManager::kEMCalEnergy, 200, -100.0, 100.0, VarManager::kEMCalTime); + if (subGroupStr.Contains("emcalmatch")) { + hm->AddHistogram(histClass, "EMCalEoverP", "EMCal E/p", false, 300, 0.0, 3.0, VarManager::kEMCalEoverP); + hm->AddHistogram(histClass, "Pt_EMCalEoverP", "p_{T} vs EMCal E/p", false, 200, 0.0, 20.0, VarManager::kPt, 300, 0.0, 3.0, VarManager::kEMCalEoverP); + hm->AddHistogram(histClass, "EMCalMatchDeltaEta_DeltaPhi", "EMCal matching #Delta#eta vs #Delta#varphi", false, 100, -0.1, 0.1, VarManager::kEMCalMatchDeltaEta, 100, -0.1, 0.1, VarManager::kEMCalMatchDeltaPhi); + } + } if (subGroupStr.Contains("muon")) { if (!subGroupStr.Contains("ambiguity")) { hm->AddHistogram(histClass, "MuonNClusters", "", false, 100, 0.0, 10.0, VarManager::kMuonNClusters); @@ -1111,7 +1170,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "tpcNSigmaPi_tofNSigmaPi", "", false, 200, -10., 10., VarManager::kTPCnSigmaPi, 200, -10., 10., VarManager::kTOFnSigmaPi); } if (subGroupStr.Contains("singlemucumulant")) { - double PtBinEdges[67]; // 0-30GeV/c + std::array PtBinEdges{}; // 0-30GeV/c for (int i = 0; i < 67; i++) { if (i <= 39) { PtBinEdges[i] = i / 10.; @@ -1120,19 +1179,19 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } - double CentBinEdges[19]; // 0-90% + std::array CentBinEdges{}; // 0-90% for (int i = 0; i < 19; i++) { CentBinEdges[i] = i * 5; } - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2REFsingle", "", true, 66, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2REFbysinglemu, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpsingle); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4REFsingle", "", true, 66, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4REFbysinglemu, "", "", "", VarManager::kNothing, VarManager::kM1111REFoverMpsingle); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2POIsingle", "", true, 66, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2POIsingle, "", "", "", VarManager::kNothing, VarManager::kM01POIoverMpsingle); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4POIsingle", "", true, 66, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4POIsingle, "", "", "", VarManager::kNothing, VarManager::kM0111POIoverMpsingle); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2REFsingle", "", true, 66, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2REFbysinglemu, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpsingle); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4REFsingle", "", true, 66, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4REFbysinglemu, "", "", "", VarManager::kNothing, VarManager::kM1111REFoverMpsingle); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2POIsingle", "", true, 66, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2POIsingle, "", "", "", VarManager::kNothing, VarManager::kM01POIoverMpsingle); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4POIsingle", "", true, 66, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4POIsingle, "", "", "", VarManager::kNothing, VarManager::kM0111POIoverMpsingle); } } - if (!groupStr.CompareTo("mctruth_triple")) { + if (groupStr.CompareTo("mctruth_triple") == 0) { hm->AddHistogram(histClass, "Eta_Pt", "", false, 100, -2.0, 2.0, VarManager::kPairEta, 200, 0.0, 30.0, VarManager::kPairPt); hm->AddHistogram(histClass, "Eta_Pt_lepton1", "", false, 100, -2.0, 2.0, VarManager::kEta1, 200, 0.0, 30.0, VarManager::kPt1); hm->AddHistogram(histClass, "Eta_Pt_lepton2", "", false, 100, -2.0, 2.0, VarManager::kEta2, 200, 0.0, 30.0, VarManager::kPt2); @@ -1152,7 +1211,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "DeltaMass_Jpsi", "", false, 1500, 3, 4.5, (VarManager::kDeltaMass_jpsi)); hm->AddHistogram(histClass, "Rapidity", "", false, 400, -5.0, 5.0, VarManager::kRap); } - if (!groupStr.CompareTo("mctruth_pair")) { + if (groupStr.CompareTo("mctruth_pair") == 0) { hm->AddHistogram(histClass, "Mass_Pt", "", false, 500, 0.0, 15.0, VarManager::kMCMass, 40, 0.0, 20.0, VarManager::kMCPt); hm->AddHistogram(histClass, "Pt", "", false, 200, 0.0, 20.0, VarManager::kMCPt); hm->AddHistogram(histClass, "Pt_Dilepton", "", false, 200, 0.0, 20.0, VarManager::kPairPtDau); @@ -1161,16 +1220,24 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Eta_Pt", "", false, 40, -2.0, 2.0, VarManager::kMCEta, 200, 0.0, 20.0, VarManager::kMCPt); hm->AddHistogram(histClass, "Phi_Eta", "#phi vs #eta distribution", false, 200, -5.0, 5.0, VarManager::kMCEta, 200, -2. * o2::constants::math::PI, 2. * o2::constants::math::PI, VarManager::kMCPhi); if (subGroupStr.Contains("polarization")) { - int varspTHE[4] = {VarManager::kMCPt, VarManager::kMCCosThetaHE, VarManager::kMCPhiHE, VarManager::kMCPhiTildeHE}; - int varspTCS[4] = {VarManager::kMCPt, VarManager::kMCCosThetaCS, VarManager::kMCPhiCS, VarManager::kMCPhiTildeCS}; - int bins[4] = {20, 20, 20, 20}; - double xmin[4] = {0., -1., 0., 0.}; - double xmax[4] = {20., 1., 2. * o2::constants::math::PI, 2. * o2::constants::math::PI}; - hm->AddHistogram(histClass, "Pt_cosThetaHE_phiHE_phiTildeHE", "", 4, varspTHE, bins, xmin, xmax, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Pt_cosThetaCS_phiCS_phiTildeCS", "", 4, varspTCS, bins, xmin, xmax, 0, -1, kFALSE); + if (subGroupStr.Contains("pp")) { + std::array varspTHE = {VarManager::kMCPt, VarManager::kMCCosThetaHE, VarManager::kMCPhiHE, VarManager::kMCPhiTildeHE}; + std::array varspTCS = {VarManager::kMCPt, VarManager::kMCCosThetaCS, VarManager::kMCPhiCS, VarManager::kMCPhiTildeCS}; + std::array bins = {20, 20, 20, 20}; + std::array xmin = {0., -1., 0., 0.}; + std::array xmax = {20., 1., 2. * o2::constants::math::PI, 2. * o2::constants::math::PI}; + hm->AddHistogram(histClass, "Pt_cosThetaHE_phiHE_phiTildeHE", "", 4, varspTHE.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + hm->AddHistogram(histClass, "Pt_cosThetaCS_phiCS_phiTildeCS", "", 4, varspTCS.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (subGroupStr.Contains("pbpb")) { + hm->AddHistogram(histClass, "CosThetaStarRandom", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarRandom); + hm->AddHistogram(histClass, "Pt_Cent_CosThetaStarRandom", "", false, 20, 0.0, 20.0, VarManager::kMCPt, 9, 0., 90., VarManager::kMCEventCentrFT0C, 20, -1.0, 1.0, VarManager::kCosThetaStarRandom); + hm->AddHistogram(histClass, "Cos2ThetaStarRandom_Mass", "", true, 50, 2.0, 4.0, VarManager::kMCMass, 100, -1.0, 1.0, VarManager::kCos2ThetaStarRandom); + hm->AddHistogram(histClass, "CosThetaStar", "", false, 100, -1.0, 1.0, VarManager::kMCCosThetaStar); + } } } - if (!groupStr.CompareTo("mctruth_quad")) { + if (groupStr.CompareTo("mctruth_quad") == 0) { hm->AddHistogram(histClass, "hMass_defaultDileptonMass", "", false, 1000, 3.0, 5.0, VarManager::kQuadDefaultDileptonMass); hm->AddHistogram(histClass, "hPt", "", false, 150, 0.0, 15.0, VarManager::kQuadPt); hm->AddHistogram(histClass, "hMass_defaultDileptonMass_Pt", "", false, 100, 3.0, 5.0, VarManager::kQuadDefaultDileptonMass, 150, 0.0, 15.0, VarManager::kQuadPt); @@ -1182,7 +1249,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "hMCPt_MCRap", "", false, 200, 0.0, 20.0, VarManager::kMCPt, 100, -2.0, 2.0, VarManager::kMCY); hm->AddHistogram(histClass, "hMCPhi", "", false, 100, -TMath::Pi(), TMath::Pi(), VarManager::kMCPhi); } - if (!groupStr.CompareTo("mctruth_track")) { + if (groupStr.CompareTo("mctruth_track") == 0) { hm->AddHistogram(histClass, "PtMC", "MC pT", false, 200, 0.0, 20.0, VarManager::kMCPt); hm->AddHistogram(histClass, "EtaMC", "MC #eta", false, 50, -5.0, 5.0, VarManager::kMCEta); hm->AddHistogram(histClass, "PhiMC", "MC #phi", false, 50, -6.3, 6.3, VarManager::kMCPhi); @@ -1190,13 +1257,13 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "CentFT0CMC", "MC Cent. FT0C", false, 18, 0., 90., VarManager::kMCEventCentrFT0C); hm->AddHistogram(histClass, "PtMC_YMC", "MC pT vs MC y", false, 120, 0.0, 30.0, VarManager::kMCPt, 1000, -5.0, 5.0, VarManager::kMCY); hm->AddHistogram(histClass, "PtMC_YMC_CentFT0CMC", "MC pT vs MC y vs MC Cent. FT0C", false, 300, 0.0, 30.0, VarManager::kMCPt, 1000, -5.0, 5.0, VarManager::kMCY, 20, 0., 100., VarManager::kMCEventCentrFT0C); - hm->AddHistogram(histClass, "EtaMC_PtMC", "", false, 40, -2.0, 2.0, VarManager::kMCEta, 200, 0.0, 20.0, VarManager::kMCPt); + hm->AddHistogram(histClass, "EtaMC_PtMC", "", false, 320, -2.0, 2.0, VarManager::kMCEta, 400, 0.0, 40.0, VarManager::kMCPt); hm->AddHistogram(histClass, "VzMC", "MC vz", false, 100, -15.0, 15.0, VarManager::kMCVz); hm->AddHistogram(histClass, "VzMC_VtxZMC", "MC vz vs MC vtxZ", false, 50, -15.0, 15.0, VarManager::kMCVz, 50, -15.0, 15.0, VarManager::kMCVtxZ); hm->AddHistogram(histClass, "Weight", "", false, 50, 0.0, 5.0, VarManager::kMCParticleWeight); hm->AddHistogram(histClass, "MCImpPar_CentFT0CMC", "MC impact param vs MC Cent. FT0C", false, 20, 0.0, 20.0, VarManager::kMCEventImpParam, 100, 0.0, 100.0, VarManager::kMCEventCentrFT0C); } - if (!groupStr.CompareTo("mctruth_mult")) { + if (groupStr.CompareTo("mctruth_mult") == 0) { hm->AddHistogram(histClass, "PtMC_MultEta05", "MC pT vs mult |#eta| < 0.5", false, 200, 0.0, 20.0, VarManager::kMCPt, 150, 0.0, 150.0, VarManager::kMultMCNParticlesEta05); hm->AddHistogram(histClass, "PtMC_MultEta08", "MC pT vs mult |#eta| < 0.8", false, 200, 0.0, 20.0, VarManager::kMCPt, 150, 0.0, 150.0, VarManager::kMultMCNParticlesEta08); hm->AddHistogram(histClass, "PtMC_MultEta10", "MC pT vs mult |#eta| < 1.0", false, 200, 0.0, 20.0, VarManager::kMCPt, 150, 0.0, 150.0, VarManager::kMultMCNParticlesEta10); @@ -1205,26 +1272,64 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "EtaMC_MultEta10", "MC Eta vs mult |#eta| < 1.0", false, 40, -2.0, 2.0, VarManager::kMCEta, 150, 0.0, 150.0, VarManager::kMultMCNParticlesEta10); } - if (!groupStr.CompareTo("energy-correlator-gen")) { - double coschiBins[26]; + if (groupStr.CompareTo("energy-correlator-gen") == 0) { + std::array coschiBins{}; for (int i = 0; i < 26; i++) { coschiBins[i] = -1.0 + 2.0 * TMath::Power(0.04 * i, 2.0); } - hm->AddHistogram(histClass, "Coschi", "", false, 25, coschiBins, VarManager::kMCCosChi, 0, nullptr, -1, 0, nullptr, -1, "", "", "", -1, VarManager::kMCWeight); + hm->AddHistogram(histClass, "Coschi", "", false, 25, coschiBins.data(), VarManager::kMCCosChi, 0, nullptr, -1, 0, nullptr, -1, "", "", "", -1, VarManager::kMCWeight); } - if (!groupStr.CompareTo("polarization-pseudoproper-gen")) { - int varspTHE[3] = {VarManager::kMCPt, VarManager::kMCCosThetaHE, VarManager::kMCVertexingTauxyProjected}; - int varspTCS[3] = {VarManager::kMCPt, VarManager::kMCCosThetaCS, VarManager::kMCVertexingTauxyProjected}; - int varspTRM[3] = {VarManager::kMCPt, VarManager::kMCCosThetaRM, VarManager::kMCVertexingTauxyProjected}; - int bins[3] = {20, 20, 1000}; - double xmin[3] = {0., -1., -0.5}; - double xmax[3] = {20., 1., 0.5}; - hm->AddHistogram(histClass, "Pt_cosThetaHE_Tauxy", "", 3, varspTHE, bins, xmin, xmax, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Pt_cosThetaCS_Tauxy", "", 3, varspTCS, bins, xmin, xmax, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Pt_cosThetaRM_Tauxy", "", 3, varspTRM, bins, xmin, xmax, 0, -1, kFALSE); + if (groupStr.CompareTo("polarization-pseudoproper-midy-he-gen") == 0) { + std::array varspTHE = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCCosThetaHE, VarManager::kMCVertexingTauxyProjected}; + std::array bins = {50, 20, 20, 1000}; + std::array xmin = {2., 0., -1., -0.5}; + std::array xmax = {4., 20., 1., 0.5}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaHE_Tauxy", "", 4, varspTHE.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); } - if (!groupStr.CompareTo("pair")) { + if (groupStr.CompareTo("polarization-pseudoproper-midy-cs-gen") == 0) { + std::array varspTCS = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCCosThetaCS, VarManager::kMCVertexingTauxyProjected}; + std::array bins = {50, 20, 20, 1000}; + std::array xmin = {2., 0., -1., -0.5}; + std::array xmax = {4., 20., 1., 0.5}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaCS_Tauxy", "", 4, varspTCS.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-pseudoproper-midy-rand-gen") == 0) { + std::array varspTRM = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCCosThetaRM, VarManager::kMCVertexingTauxyProjected}; + std::array bins = {50, 20, 20, 1000}; + std::array xmin = {2., 0., -1., -0.5}; + std::array xmax = {4., 20., 1., 0.5}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaRM_Tauxy", "", 4, varspTRM.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-he-gen") == 0) { + std::array varspTHE = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaHE, VarManager::kMCPhiHE}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varspTHE.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-gjb1-gen") == 0) { + std::array varspTGJB1 = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaGJB1, VarManager::kMCPhiGJB1}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaGJB1", "", 5, varspTGJB1.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-gjb2-gen") == 0) { + std::array varspTGJB2 = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaGJB2, VarManager::kMCPhiGJB2}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaGJB2", "", 5, varspTGJB2.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("polarization-dielectron-pbpb-midy-cs-gen") == 0) { + std::array varspTCS = {VarManager::kMCMass, VarManager::kMCPt, VarManager::kMCEventCentrFT0C, VarManager::kMCCosThetaCS, VarManager::kMCPhiCS}; + std::array bins = {50, 30, 10, 20, 10}; + std::array xmin = {2., 0., 0, -1., 0.0}; + std::array xmax = {4., 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "MC_Dielectron_Mass_Pt_Cent_cosThetaCS", "", 5, varspTCS.data(), bins.data(), xmin.data(), xmax.data(), nullptr, -1, kFALSE); + } + if (groupStr.CompareTo("pair") == 0) { if (subGroupStr.Contains("cepf")) { hm->AddHistogram(histClass, "Mass", "", false, 300, 0.0, 12.0, VarManager::kMass); hm->AddHistogram(histClass, "Mass_Pt", "", false, 300, 0.0, 12.0, VarManager::kMass, 10, 0.0, 20.0, VarManager::kPt); @@ -1244,46 +1349,46 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_MultFDDC", "Mass vs MultFDDC", false, 200, 2.0, 5.0, VarManager::kMass, 1000, 0, 25000.0, VarManager::kMultFDDC); } if (subGroupStr.Contains("mass_mult_pv")) { - double multAnalysisBins[4] = {0.0, 4.0, 8.0, 1000.0}; + std::array multAnalysisBins = {0.0, 4.0, 8.0, 1000.0}; - double multAnalsisMassBins[201] = {0.0}; + std::array multAnalsisMassBins = {0.0}; for (int i = 0; i <= 200; i++) { multAnalsisMassBins[i] = 2.0 + i * 0.015; } - double multAnalysisPVBins[151] = {0.0}; + std::array multAnalysisPVBins = {0.0}; for (int i = 0; i <= 150; i++) { multAnalysisPVBins[i] = i * 1.0; } - double multAnalysisFV0ABins[1001] = {0.0}; + std::array multAnalysisFV0ABins = {0.0}; for (int i = 0; i <= 1000; i++) { multAnalysisFV0ABins[i] = i * 1.0; } - hm->AddHistogram(histClass, "Mass_VtxNcontribReal_Pt", "Mass vs VtxNcontribReal vs Pt", false, 200, multAnalsisMassBins, VarManager::kMass, 150, multAnalysisPVBins, VarManager::kVtxNcontribReal, 3, multAnalysisBins, VarManager::kPt); - hm->AddHistogram(histClass, "Mass_MultFV0A_Pt", "Mass vs MultFV0A vs Pt", false, 200, multAnalsisMassBins, VarManager::kMass, 1000, multAnalysisFV0ABins, VarManager::kMultFV0A, 3, multAnalysisBins, VarManager::kPt); - hm->AddHistogram(histClass, "Mass_MultFT0A_Pt", "Mass vs MultFT0A vs Pt", false, 200, multAnalsisMassBins, VarManager::kMass, 1000, multAnalysisFV0ABins, VarManager::kMultFT0A, 3, multAnalysisBins, VarManager::kPt); - hm->AddHistogram(histClass, "Mass_MultFT0C_Pt", "Mass vs MultFT0C vs Pt", false, 200, multAnalsisMassBins, VarManager::kMass, 1000, multAnalysisFV0ABins, VarManager::kMultFT0C, 3, multAnalysisBins, VarManager::kPt); + hm->AddHistogram(histClass, "Mass_VtxNcontribReal_Pt", "Mass vs VtxNcontribReal vs Pt", false, 200, multAnalsisMassBins.data(), VarManager::kMass, 150, multAnalysisPVBins.data(), VarManager::kVtxNcontribReal, 3, multAnalysisBins.data(), VarManager::kPt); + hm->AddHistogram(histClass, "Mass_MultFV0A_Pt", "Mass vs MultFV0A vs Pt", false, 200, multAnalsisMassBins.data(), VarManager::kMass, 1000, multAnalysisFV0ABins.data(), VarManager::kMultFV0A, 3, multAnalysisBins.data(), VarManager::kPt); + hm->AddHistogram(histClass, "Mass_MultFT0A_Pt", "Mass vs MultFT0A vs Pt", false, 200, multAnalsisMassBins.data(), VarManager::kMass, 1000, multAnalysisFV0ABins.data(), VarManager::kMultFT0A, 3, multAnalysisBins.data(), VarManager::kPt); + hm->AddHistogram(histClass, "Mass_MultFT0C_Pt", "Mass vs MultFT0C vs Pt", false, 200, multAnalsisMassBins.data(), VarManager::kMass, 1000, multAnalysisFV0ABins.data(), VarManager::kMultFT0C, 3, multAnalysisBins.data(), VarManager::kPt); } if (subGroupStr.Contains("barrel")) { hm->AddHistogram(histClass, "Mass", "", false, 500, 0.0, 5.0, VarManager::kMass); hm->AddHistogram(histClass, "Mass_HighRange", "", false, 375, 0.0, 15.0, VarManager::kMass); hm->AddHistogram(histClass, "Pt", "", false, 2000, 0.0, 20., VarManager::kPt); hm->AddHistogram(histClass, "Mass_Pt", "", false, 500, 0.0, 5.0, VarManager::kMass, 40, 0.0, 20.0, VarManager::kPt); - double massBins[76]; + std::array massBins{}; for (int i = 0; i < 76; i++) { massBins[i] = 1.5 + i * 0.04; } - double ptBins[70]; + std::array ptBins{}; for (int i = 0; i <= 50; i++) { ptBins[i] = i * 0.01; } for (int i = 1; i <= 19; i++) { ptBins[50 + i] = 0.5 + i * 0.5; } - hm->AddHistogram(histClass, "Mass_PtFine", "", false, 75, massBins, VarManager::kMass, 69, ptBins, VarManager::kPt); - hm->AddHistogram(histClass, "Eta_Pt", "", false, 40, -2.0, 2.0, VarManager::kEta, 40, 0.0, 20.0, VarManager::kPt); + hm->AddHistogram(histClass, "Mass_PtFine", "", false, 75, massBins.data(), VarManager::kMass, 69, ptBins.data(), VarManager::kPt); + hm->AddHistogram(histClass, "Eta_Pt", "", false, 320, -2.0, 2.0, VarManager::kEta, 400, 0.0, 40.0, VarManager::kPt); hm->AddHistogram(histClass, "Y_Pt", "", false, 40, -2.0, 2.0, VarManager::kRap, 40, 0.0, 20.0, VarManager::kPt); hm->AddHistogram(histClass, "Mass_VtxZ", "", true, 30, -15.0, 15.0, VarManager::kVtxZ, 500, 0.0, 5.0, VarManager::kMass); if (subGroupStr.Contains("energy-correlator")) { @@ -1301,31 +1406,65 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "VtxZ_VtxNcontribReal", "VtxZ vs VtxNcontribReal", false, 240, -12.0, 12.0, VarManager::kVtxZ, 200, 0, 200.0, VarManager::kVtxNcontribReal); } if (subGroupStr.Contains("alice3barrel")) { - double pTBins[21] = { + std::array pTBins = { 0.1, 0.5, 1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.5, 8.0, 10.0, 12.0, 15.0, 20.0, 30.0, 40.0, 60.0, 80.0, 100.0}; - double massBins[501]; + std::array alice3MassBins{}; for (int i = 0; i < 501; i++) { - massBins[i] = 0.01 * (i); + alice3MassBins[i] = 0.01 * (i); } - hm->AddHistogram(histClass, "Mass_PtLong", "", false, 500, massBins, VarManager::kMass, 20, pTBins, VarManager::kPt); + hm->AddHistogram(histClass, "Mass_PtLong", "", false, 500, alice3MassBins.data(), VarManager::kMass, 20, pTBins.data(), VarManager::kPt); + } + if (subGroupStr.Contains("alice3pair")) { + hm->AddHistogram(histClass, "CosinePointingAngle", "cos(#theta_{p})", false, 2000.0, -1.0, 1.0, VarManager::kCosPointingAngle); + hm->AddHistogram(histClass, "DCADaughters", "DCA_{dau}", false, 2000.0, -10.0, 10.0, VarManager::kD0DcaDaughters); + hm->AddHistogram(histClass, "ImpParProduct", "d_{k} #times d_{#pi}", false, 2000.0, -100.0, 100.0, VarManager::kD0ImpParProduct); + hm->AddHistogram(histClass, "DCAxy1", "DCA_{xy, 1}", false, 2000.0, -10.0, 10.0, VarManager::kDCAxy1); + hm->AddHistogram(histClass, "DCAxy2", "DCA_{xy, 2}", false, 2000.0, -10.0, 10.0, VarManager::kDCAxy2); + } + if (subGroupStr.Contains("alice3pbpb")) { + hm->AddHistogram(histClass, "Mass_Cent", "", false, 125, 0.0, 5.0, VarManager::kMass, 20, 0.0, 100.0, VarManager::kCent); + hm->AddHistogram(histClass, "Pt_CentFT0C", "", false, 100, 0.0, 10.0, VarManager::kPt, 20, 0.0, 100.0, VarManager::kCent); + hm->AddHistogram(histClass, "Mass_Pt_CentFT0C", "", false, 75, 1.5, 4.5, VarManager::kMass, 20, 0.0, 10.0, VarManager::kPt, 10, 0.0, 100.0, VarManager::kCent); + } + if (subGroupStr.Contains("alice3mult")) { + hm->AddHistogram(histClass, "Mass_Pt_MultGlobal", "Mass vs pT vs multGlobal", false, 200, 0.0, 5.0, VarManager::kMass, 40, 0.0, 40.0, VarManager::kPt, 100, 0.0, 10000.0, VarManager::kMultGlobalTracks); + hm->AddHistogram(histClass, "Mass_MultGlobal", "Mass vs Global Tracks", false, 200, 0.0, 5.0, VarManager::kMass, 100, 0, 10000.0, VarManager::kMultGlobalTracks); + hm->AddHistogram(histClass, "Mass_MultGlobal_VtxZ", "Mass vs Global Tracks vs VtxZ", false, 200, 2.0, 5.0, VarManager::kMass, 100, 0, 10000.0, VarManager::kMultGlobalTracks, 20, -10.0, 10.0, VarManager::kVtxZ); + hm->AddHistogram(histClass, "VtxZ_MultGlobal", "VtxZ vs VtxNcontribReal", false, 240, -12.0, 12.0, VarManager::kVtxZ, 100, 0, 10000.0, VarManager::kMultGlobalTracks); } if (subGroupStr.Contains("dielectron-polarization-he-pbpb")) { - int varsHEpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int binspT[5] = {100, 30, 10, 10, 10}; - double xminpT[5] = {2., 0., 0, -1., 0.0}; - double xmaxpT[5] = {4.5, 3., 100, 1., 6.28}; - hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varsHEpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsHEpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaHE", "", 5, varsHEpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + } + + if (subGroupStr.Contains("dielectron-polarization-gjb1-pbpb")) { + std::array varsGJB1pbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaGJB1, VarManager::kPhiGJB1}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaGJB1", "", 5, varsGJB1pbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + } + + if (subGroupStr.Contains("dielectron-polarization-gjb2-pbpb")) { + std::array varsGJB2pbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaGJB2, VarManager::kPhiGJB2}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaGJB2", "", 5, varsGJB2pbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dielectron-polarization-cs-pbpb")) { - int varsCSpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int binspT[5] = {100, 30, 10, 10, 10}; - double xminpT[5] = {2.0, 0., 0, -1., 0.0}; - double xmaxpT[5] = {4.5, 3., 100, 1., 6.28}; - hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaCS", "", 5, varsCSpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsCSpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array binspT = {100, 30, 10, 10, 10}; + std::array xminpT = {2.0, 0., 0, -1., 0.0}; + std::array xmaxpT = {4.5, 3., 100, 1., 6.28}; + hm->AddHistogram(histClass, "Dielectron_Mass_Pt_Cent_cosThetaCS", "", 5, varsCSpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("polarization")) { if (subGroupStr.Contains("helicity")) { @@ -1348,12 +1487,47 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } if (subGroupStr.Contains("globalpolarization")) { - hm->AddHistogram(histClass, "CosThetaStarTPC", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarTPC); - hm->AddHistogram(histClass, "CosThetaStarFT0A", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarFT0A); - hm->AddHistogram(histClass, "CosThetaStarFT0C", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarFT0C); + hm->AddHistogram(histClass, "RandomPlaneAngle", "", false, 100, -TMath::Pi() / 2, TMath::Pi() / 2, VarManager::kRandomPsi2); hm->AddHistogram(histClass, "CosThetaStarRandom", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarRandom); - hm->AddHistogram(histClass, "Cos2ThetaStarFT0C_Mass", "", true, 50, 2.0, 4.0, VarManager::kMass, 100, -1.0, 1.0, VarManager::kCos2ThetaStarFT0C); hm->AddHistogram(histClass, "Cos2ThetaStarRandom_Mass", "", true, 50, 2.0, 4.0, VarManager::kMass, 100, -1.0, 1.0, VarManager::kCos2ThetaStarRandom); + if (subGroupStr.Contains("tpc")) { + hm->AddHistogram(histClass, "CosThetaStarTPC", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarTPC); + } + if (subGroupStr.Contains("ft0")) { + hm->AddHistogram(histClass, "CosThetaStarFT0A", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarFT0A); + hm->AddHistogram(histClass, "CosThetaStarFT0C", "", false, 100, -1.0, 1.0, VarManager::kCosThetaStarFT0C); + hm->AddHistogram(histClass, "Cos2ThetaStarFT0C_Mass", "", true, 50, 2.0, 4.0, VarManager::kMass, 100, -1.0, 1.0, VarManager::kCos2ThetaStarFT0C); + } + if (subGroupStr.Contains("mc")) { + hm->AddHistogram(histClass, "EventPlaneAngle", "", false, 100, -TMath::Pi(), TMath::Pi(), VarManager::kMCEventPlaneAngle); + hm->AddHistogram(histClass, "CosThetaStarMC", "", false, 100, -1.0, 1.0, VarManager::kMCCosThetaStar); + } + } + if (subGroupStr.Contains("flow-jpsi-ep")) { + std::array bins_A2 = {50, 20, 20, 9, 200}; + std::array minBins_A2 = {2.0, 0.0, -1., 0.0, -20.0}; + std::array maxBins_A2 = {4.0, 2.0, 1.0, 90.0, 20.0}; + std::array bins_DeltaPhi = {50, 20, 20, 9, 10}; + std::array minBins_DeltaPhi = {2.0, 0.0, -1., 0.0, 0}; + std::array maxBins_DeltaPhi = {4.0, 2.0, 1.0, 90.0, 3.14}; + if (subGroupStr.Contains("tpc")) { + std::array varA2_TPC = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kA2EP_TPC}; + std::array varDeltaPhi_TPC = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kDeltaPhi_TPC}; + hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_A2_TPC", "", 5, varA2_TPC.data(), bins_A2.data(), minBins_A2.data(), maxBins_A2.data(), nullptr, -1, kTRUE); + hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_DeltaPhi_TPC", "", 5, varDeltaPhi_TPC.data(), bins_DeltaPhi.data(), minBins_DeltaPhi.data(), maxBins_DeltaPhi.data(), nullptr, -1, kTRUE); + } + if (subGroupStr.Contains("ft0c")) { + std::array varA2_FT0C = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kA2EP_FT0C}; + std::array varDeltaPhi_FT0C = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kDeltaPhi_FT0C}; + hm->AddHistogram(histClass, "Mass_Pt_centFT0C_A2_FT0C", "", 5, varA2_FT0C.data(), bins_A2.data(), minBins_A2.data(), maxBins_A2.data(), nullptr, -1, kTRUE); + hm->AddHistogram(histClass, "Mass_Pt_centFT0C_DeltaPhi_FT0C", "", 5, varDeltaPhi_FT0C.data(), bins_DeltaPhi.data(), minBins_DeltaPhi.data(), maxBins_DeltaPhi.data(), nullptr, -1, kTRUE); + } + if (subGroupStr.Contains("ft0a")) { + std::array varA2_FT0A = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kA2EP_FT0A}; + std::array varDeltaPhi_FT0A = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kDeltaPhi_FT0A}; + hm->AddHistogram(histClass, "Mass_Pt_centFT0C_A2_FT0A", "", 5, varA2_FT0A.data(), bins_A2.data(), minBins_A2.data(), maxBins_A2.data(), nullptr, -1, kTRUE); + hm->AddHistogram(histClass, "Mass_Pt_centFT0C_DeltaPhi_FT0A", "", 5, varDeltaPhi_FT0A.data(), bins_DeltaPhi.data(), minBins_DeltaPhi.data(), maxBins_DeltaPhi.data(), nullptr, -1, kTRUE); + } } if (subGroupStr.Contains("upsilon")) { hm->AddHistogram(histClass, "MassUpsilon_Pt", "", false, 500, 7.0, 12.0, VarManager::kMass, 400, 0.0, 40.0, VarManager::kPt); @@ -1387,29 +1561,29 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "VtxingChi2PCA", "", false, 100, 0.0, 10.0, VarManager::kVertexingChi2PCA); } if (subGroupStr.Contains("tauxy-midy-pol-he")) { - int varspTHE[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaHE, VarManager::kVertexingTauxyProjectedPoleJPsiMass}; - int binspT[4] = {50, 10, 20, 1000}; - double xminpT[4] = {2., 0., -1., -0.5}; - double xmaxpT[4] = {4., 20., 1., 0.5}; - hm->AddHistogram(histClass, "Tauxy_Mass_Pt_CosthetaHE", "", 4, varspTHE, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varspTHE = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaHE, VarManager::kVertexingTauxyProjectedPoleJPsiMass}; + std::array binspT = {50, 10, 20, 1000}; + std::array xminpT = {2., 0., -1., -0.5}; + std::array xmaxpT = {4., 20., 1., 0.5}; + hm->AddHistogram(histClass, "Tauxy_Mass_Pt_CosthetaHE", "", 4, varspTHE.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("tauxy-midy-pol-rand")) { - int varspTRand[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaRM, VarManager::kVertexingTauxyProjectedPoleJPsiMass}; + std::array varspTRand = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaRM, VarManager::kVertexingTauxyProjectedPoleJPsiMass}; - int binspT[4] = {50, 10, 20, 1000}; - double xminpT[4] = {2., 0., -1., -0.5}; - double xmaxpT[4] = {4., 20., 1., 0.5}; - hm->AddHistogram(histClass, "Tauxy_Mass_Pt_CosthetaRand", "", 4, varspTRand, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array binspT = {50, 10, 20, 1000}; + std::array xminpT = {2., 0., -1., -0.5}; + std::array xmaxpT = {4., 20., 1., 0.5}; + hm->AddHistogram(histClass, "Tauxy_Mass_Pt_CosthetaRand", "", 4, varspTRand.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("tauxy-midy-pol-cs")) { - int varspTCS[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaCS, VarManager::kVertexingTauxyProjectedPoleJPsiMass}; + std::array varspTCS = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaCS, VarManager::kVertexingTauxyProjectedPoleJPsiMass}; - int binspT[4] = {50, 10, 20, 1000}; - double xminpT[4] = {2., 0., -1., -0.5}; - double xmaxpT[4] = {4., 20., 1., 0.5}; - hm->AddHistogram(histClass, "Tauxy_Mass_Pt_CosthetaCS", "", 4, varspTCS, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array binspT = {50, 10, 20, 1000}; + std::array xminpT = {2., 0., -1., -0.5}; + std::array xmaxpT = {4., 20., 1., 0.5}; + hm->AddHistogram(histClass, "Tauxy_Mass_Pt_CosthetaCS", "", 4, varspTCS.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("kalman-filter")) { @@ -1464,24 +1638,25 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h const int kNvarsPair = 4; const int kInvMassNbins = 3; - double InvMassBinLims[kInvMassNbins + 1] = {2.2, 2.6, 3.4, 3.6}; + std::array InvMassBinLims = {2.2, 2.6, 3.4, 3.6}; const int kPtNbins = 10; - double PtBinLims[kPtNbins + 1] = {1., 2., 3., 4., 5., 6., 7., 8., 9., 12., 20.}; + std::array PtBinLims = {1., 2., 3., 4., 5., 6., 7., 8., 9., 12., 20.}; const int kTauNBins = 500; - double TauBinLims[kTauNBins + 1]; - for (int i = 0; i <= kTauNBins; ++i) + std::array TauBinLims{}; + for (int i = 0; i <= kTauNBins; ++i) { TauBinLims[i] = -0.3 + (0.0015 * i); + } - TArrayD nCutsBinLimits[kNvarsPair]; - nCutsBinLimits[0] = TArrayD(kInvMassNbins + 1, InvMassBinLims); - nCutsBinLimits[1] = TArrayD(kPtNbins + 1, PtBinLims); - nCutsBinLimits[2] = TArrayD(kTauNBins + 1, TauBinLims); - nCutsBinLimits[3] = TArrayD(kTauNBins + 1, TauBinLims); + std::array nCutsBinLimits; + nCutsBinLimits[0] = TArrayD(kInvMassNbins + 1, InvMassBinLims.data()); + nCutsBinLimits[1] = TArrayD(kPtNbins + 1, PtBinLims.data()); + nCutsBinLimits[2] = TArrayD(kTauNBins + 1, TauBinLims.data()); + nCutsBinLimits[3] = TArrayD(kTauNBins + 1, TauBinLims.data()); - int varsPair[kNvarsPair] = {VarManager::kMass, VarManager::kPt, VarManager::kVertexingTauzProjected, VarManager::kVertexingTauxyProjected}; - hm->AddHistogram(histClass, "tau_MultiD", "Invariant mass vs. pT vs. eta vs. rapidity vs. Run2 tau", kNvarsPair, varsPair, nCutsBinLimits); + std::array varsPair = {VarManager::kMass, VarManager::kPt, VarManager::kVertexingTauzProjected, VarManager::kVertexingTauxyProjected}; + hm->AddHistogram(histClass, "tau_MultiD", "Invariant mass vs. pT vs. eta vs. rapidity vs. Run2 tau", kNvarsPair, varsPair.data(), nCutsBinLimits.data()); } if (subGroupStr.Contains("flow")) { @@ -1499,18 +1674,18 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_EtaProbe", "mass vs probe eta", false, 750, 0.0, 15.0, VarManager::kMass, 120, 0.0, 30.0, VarManager::kEta2); // Warning: in the tag-and-probe task t2 is always the probe } if (subGroupStr.Contains("dimuon-multi-diff")) { - int varsKine[3] = {VarManager::kMass, VarManager::kPt, VarManager::kRap}; - int binsKine[3] = {250, 120, 60}; - double xminKine[3] = {0.0, 0.0, 2.5}; - double xmaxKine[3] = {5.0, 30.0, 4.0}; - hm->AddHistogram(histClass, "Mass_Pt_Rapidity", "", 3, varsKine, binsKine, xminKine, xmaxKine, 0, -1, kFALSE); + std::array varsKine = {VarManager::kMass, VarManager::kPt, VarManager::kRap}; + std::array binsKine = {250, 120, 60}; + std::array xminKine = {0.0, 0.0, 2.5}; + std::array xmaxKine = {5.0, 30.0, 4.0}; + hm->AddHistogram(histClass, "Mass_Pt_Rapidity", "", 3, varsKine.data(), binsKine.data(), xminKine.data(), xmaxKine.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-high-mass-multi-diff")) { - int varsKine[3] = {VarManager::kMass, VarManager::kPt, VarManager::kRap}; - int binsKine[3] = {250, 120, 60}; - double xminKine[3] = {7.0, 0.0, 2.5}; - double xmaxKine[3] = {12.0, 30.0, 4.0}; - hm->AddHistogram(histClass, "Mass_Pt_Rapidity", "", 3, varsKine, binsKine, xminKine, xmaxKine, 0, -1, kFALSE); + std::array varsKine = {VarManager::kMass, VarManager::kPt, VarManager::kRap}; + std::array binsKine = {250, 120, 60}; + std::array xminKine = {7.0, 0.0, 2.5}; + std::array xmaxKine = {12.0, 30.0, 4.0}; + hm->AddHistogram(histClass, "Mass_Pt_Rapidity", "", 3, varsKine.data(), binsKine.data(), xminKine.data(), xmaxKine.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-centr")) { hm->AddHistogram(histClass, "Mass_CentFT0C", "", false, 750, 0.0, 15.0, VarManager::kMass, 100, 0., 100., VarManager::kCentFT0C); @@ -1531,12 +1706,12 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "U2Q2_CentFT0C_ev2", "mass vs. centrality vs. U2Q2_event2", false, 125, 0.0, 5.0, VarManager::kMass, 9, 0.0, 90.0, VarManager::kCentFT0C, 100, -10.0, 10.0, VarManager::kU2Q2Ev2); } if (subGroupStr.Contains("metest")) { - double MassBinEdges[251]; // 0-5GeV/c2 + std::array MassBinEdges{}; // 0-5GeV/c2 for (int i = 0; i < 251; i++) { MassBinEdges[i] = i * 0.02; } - double PtBinEdges[49]; // 0-20GeV/c + std::array PtBinEdges{}; // 0-20GeV/c for (int i = 0; i < 49; i++) { if (i <= 9) { PtBinEdges[i] = i / 10.; @@ -1545,13 +1720,13 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } - double CentBinEdges[19]; // 0-90% + std::array CentBinEdges{}; // 0-90% for (int i = 0; i < 19; i++) { CentBinEdges[i] = i * 5; } - hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2ME_SP", "Mass_Pt_CentFT0C_V2ME_SP", true, 250, MassBinEdges, VarManager::kMass, 48, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, "", "", "", VarManager::kV2ME_SP, VarManager::kWV2ME_SP); - hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2ME_EP", "Mass_Pt_CentFT0C_V2ME_EP", true, 250, MassBinEdges, VarManager::kMass, 48, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, "", "", "", VarManager::kV2ME_EP, VarManager::kWV2ME_EP); + hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2ME_SP", "Mass_Pt_CentFT0C_V2ME_SP", true, 250, MassBinEdges.data(), VarManager::kMass, 48, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, "", "", "", VarManager::kV2ME_SP, VarManager::kWV2ME_SP); + hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2ME_EP", "Mass_Pt_CentFT0C_V2ME_EP", true, 250, MassBinEdges.data(), VarManager::kMass, 48, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, "", "", "", VarManager::kV2ME_EP, VarManager::kWV2ME_EP); } if (subGroupStr.Contains("cumulantme")) { hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_M11REFoverMpME", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 9, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kM11REFoverMpME); @@ -1566,12 +1741,12 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V24ME", "Mass_Pt_CentFT0C_V24ME", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 90, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kV24ME, VarManager::kWV24ME); } if (subGroupStr.Contains("cumulantme1")) { - double MassBinEdges[251]; // 0-5GeV/c2 + std::array MassBinEdges{}; // 0-5GeV/c2 for (int i = 0; i < 251; i++) { MassBinEdges[i] = i * 0.02; } - double PtBinEdges[67]; // 0-30GeV/c + std::array PtBinEdges{}; // 0-30GeV/c for (int i = 0; i < 67; i++) { if (i <= 39) { PtBinEdges[i] = i / 10.; @@ -1580,12 +1755,12 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } - double CentBinEdges[19]; // 0-90% + std::array CentBinEdges{}; // 0-90% for (int i = 0; i < 19; i++) { CentBinEdges[i] = i * 5; } - hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V22ME", "Mass_Pt_CentFT0C_V22ME", true, 250, MassBinEdges, VarManager::kMass, 66, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, "", "", "", VarManager::kV22ME, VarManager::kWV22ME); - hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V24ME", "Mass_Pt_CentFT0C_V24ME", true, 250, MassBinEdges, VarManager::kMass, 66, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, "", "", "", VarManager::kV24ME, VarManager::kWV24ME); + hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V22ME", "Mass_Pt_CentFT0C_V22ME", true, 250, MassBinEdges.data(), VarManager::kMass, 66, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, "", "", "", VarManager::kV22ME, VarManager::kWV22ME); + hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V24ME", "Mass_Pt_CentFT0C_V24ME", true, 250, MassBinEdges.data(), VarManager::kMass, 66, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, "", "", "", VarManager::kV24ME, VarManager::kWV24ME); } if (subGroupStr.Contains("cumulantme2")) { hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_M11REFoverMpME", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 9, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kM11REFoverMpME); @@ -1598,162 +1773,162 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr4POIME", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 9, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR4POIME, VarManager::kM0111POIoverMpME); } if (subGroupStr.Contains("dimuon-polarization-he")) { - int varspTHE[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int binspT[4] = {100, 40, 20, 20}; - double xminpT[4] = {1., 0., -1., -3.14}; - double xmaxpT[4] = {5., 20., 1., +3.14}; - hm->AddHistogram(histClass, "Mass_Pt_cosThetaHE_phiHE", "", 4, varspTHE, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varspTHE = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array binspT = {100, 40, 20, 20}; + std::array xminpT = {1., 0., -1., -3.14}; + std::array xmaxpT = {5., 20., 1., +3.14}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaHE_phiHE", "", 4, varspTHE.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-cs")) { - int varspTCS[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int binspT[4] = {100, 40, 20, 20}; - double xminpT[4] = {1., 0., -1., -3.14}; - double xmaxpT[4] = {5., 20., 1., +3.14}; - hm->AddHistogram(histClass, "Mass_Pt_cosThetaCS_phiCS", "", 4, varspTCS, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varspTCS = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array binspT = {100, 40, 20, 20}; + std::array xminpT = {1., 0., -1., -3.14}; + std::array xmaxpT = {5., 20., 1., +3.14}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaCS_phiCS", "", 4, varspTCS.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-pp")) { - int varspTPP[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaPP, VarManager::kPhiPP}; - int binspT[4] = {100, 40, 20, 20}; - double xminpT[4] = {1., 0., -1., -3.14}; - double xmaxpT[4] = {5., 20., 1., +3.14}; - hm->AddHistogram(histClass, "Mass_Pt_cosThetaPP_phiPP", "", 4, varspTPP, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varspTPP = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaPP, VarManager::kPhiPP}; + std::array binspT = {100, 40, 20, 20}; + std::array xminpT = {1., 0., -1., -3.14}; + std::array xmaxpT = {5., 20., 1., +3.14}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaPP_phiPP", "", 4, varspTPP.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-lowmass-pp")) { - int varspTPP[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaPP, VarManager::kPhiPP}; - int varsrapPP[4] = {VarManager::kMass, VarManager::kRap, VarManager::kCosThetaPP, VarManager::kPhiPP}; - int binspT[4] = {100, 20, 20, 20}; - int binsy[4] = {100, 10, 20, 20}; - double xminpT[4] = {0.2, 0., -1., -3.14}; - double xmaxpT[4] = {1.5, 20., 1., +3.14}; - double xminy[4] = {0.2, 2.5, -1., -3.14}; - double xmaxy[4] = {1.5, 4.0, 1., +3.14}; - hm->AddHistogram(histClass, "Mass_Pt_cosThetaPP_phiPP", "", 4, varspTPP, binspT, xminpT, xmaxpT, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Mass_y_cosThetaPP_phiPP", "", 4, varsrapPP, binsy, xminy, xmaxy, 0, -1, kFALSE); + std::array varspTPP = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaPP, VarManager::kPhiPP}; + std::array varsrapPP = {VarManager::kMass, VarManager::kRap, VarManager::kCosThetaPP, VarManager::kPhiPP}; + std::array binspT = {100, 20, 20, 20}; + std::array binsy = {100, 10, 20, 20}; + std::array xminpT = {0.2, 0., -1., -3.14}; + std::array xmaxpT = {1.5, 20., 1., +3.14}; + std::array xminy = {0.2, 2.5, -1., -3.14}; + std::array xmaxy = {1.5, 4.0, 1., +3.14}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaPP_phiPP", "", 4, varspTPP.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + hm->AddHistogram(histClass, "Mass_y_cosThetaPP_phiPP", "", 4, varsrapPP.data(), binsy.data(), xminy.data(), xmaxy.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("upsilon-polarization-he")) { - int varspTHE[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int binspT[4] = {100, 20, 20, 20}; - double xminpT[4] = {1., 0., -1., 0.}; - double xmaxpT[4] = {15., 20., 1., 2. * o2::constants::math::PI}; - hm->AddHistogram(histClass, "Mass_Pt_cosThetaHE_phiHE", "", 4, varspTHE, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varspTHE = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array binspT = {100, 20, 20, 20}; + std::array xminpT = {1., 0., -1., 0.}; + std::array xmaxpT = {15., 20., 1., 2. * o2::constants::math::PI}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaHE_phiHE", "", 4, varspTHE.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("upsilon-polarization-cs")) { - int varspTCS[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int binspT[4] = {100, 20, 20, 20}; - double xminpT[4] = {1., 0., -1., 0.}; - double xmaxpT[4] = {15., 20., 1., 2. * o2::constants::math::PI}; - hm->AddHistogram(histClass, "Mass_Pt_cosThetaCS_phiCS", "", 4, varspTCS, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varspTCS = {VarManager::kMass, VarManager::kPt, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array binspT = {100, 20, 20, 20}; + std::array xminpT = {1., 0., -1., 0.}; + std::array xmaxpT = {15., 20., 1., 2. * o2::constants::math::PI}; + hm->AddHistogram(histClass, "Mass_Pt_cosThetaCS_phiCS", "", 4, varspTCS.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-vp")) { - int varspTVP[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCosPhiVP, VarManager::kPhiVP}; - int varsrapVP[4] = {VarManager::kMass, VarManager::kRap, VarManager::kCosPhiVP, VarManager::kPhiVP}; - int binspT[4] = {100, 20, 24, 24}; - int binsy[4] = {100, 10, 24, 24}; - double xminpT[4] = {1., 0., -1., 0.}; - double xmaxpT[4] = {5., 20., 1., +3.14}; - double xminy[4] = {1., 2.5, -1., 0.}; - double xmaxy[4] = {5., 4.0, 1., +3.14}; - hm->AddHistogram(histClass, "Mass_Pt_phiVP", "", 4, varspTVP, binspT, xminpT, xmaxpT, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Mass_y_phiVP", "", 4, varsrapVP, binsy, xminy, xmaxy, 0, -1, kFALSE); + std::array varspTVP = {VarManager::kMass, VarManager::kPt, VarManager::kCosPhiVP, VarManager::kPhiVP}; + std::array varsrapVP = {VarManager::kMass, VarManager::kRap, VarManager::kCosPhiVP, VarManager::kPhiVP}; + std::array binspT = {100, 20, 24, 24}; + std::array binsy = {100, 10, 24, 24}; + std::array xminpT = {1., 0., -1., 0.}; + std::array xmaxpT = {5., 20., 1., +3.14}; + std::array xminy = {1., 2.5, -1., 0.}; + std::array xmaxy = {5., 4.0, 1., +3.14}; + hm->AddHistogram(histClass, "Mass_Pt_phiVP", "", 4, varspTVP.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); + hm->AddHistogram(histClass, "Mass_y_phiVP", "", 4, varsrapVP.data(), binsy.data(), xminy.data(), xmaxy.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-rap")) { - int vars[4] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kRap}; - int binspT[4] = {300, 200, 10, 6}; - double xminpT[4] = {2., 0., 0, 2.5}; - double xmaxpT[4] = {8., 20., 100, 4.0}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_Rap", "", 4, vars, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array vars = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kRap}; + std::array binspT = {300, 200, 10, 6}; + std::array xminpT = {2., 0., 0, 2.5}; + std::array xmaxpT = {8., 20., 100, 4.0}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_Rap", "", 4, vars.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-he-pbpb")) { - int varsHEpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int binspT[5] = {150, 30, 10, 10, 10}; - double xminpT[5] = {2., 0., 0, -1., -3.14}; - double xmaxpT[5] = {5., 3., 100, 1., 3.14}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaHE", "", 5, varsHEpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsHEpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array binspT = {150, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., -3.14}; + std::array xmaxpT = {5., 3., 100, 1., 3.14}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaHE", "", 5, varsHEpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-lowmass-he-pbpb")) { - int varsHEpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int binspT[5] = {200, 30, 10, 10, 10}; - double xminpT[5] = {0.2, 0., 0, -1., -3.14}; - double xmaxpT[5] = {1.2, 3., 100, 1., 3.14}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaHE_lowmass", "", 5, varsHEpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsHEpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array binspT = {200, 30, 10, 10, 10}; + std::array xminpT = {0.2, 0., 0, -1., -3.14}; + std::array xmaxpT = {1.2, 3., 100, 1., 3.14}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaHE_lowmass", "", 5, varsHEpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-cs-pbpb")) { - int varsCSpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int binspT[5] = {150, 30, 10, 10, 10}; - double xminpT[5] = {2., 0., 0, -1., -3.14}; - double xmaxpT[5] = {5., 3., 100, 1., 3.14}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaCS", "", 5, varsCSpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsCSpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array binspT = {150, 30, 10, 10, 10}; + std::array xminpT = {2., 0., 0, -1., -3.14}; + std::array xmaxpT = {5., 3., 100, 1., 3.14}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaCS", "", 5, varsCSpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-lowmass-cs-pbpb")) { - int varsCSpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int binspT[5] = {200, 30, 10, 10, 10}; - double xminpT[5] = {0.2, 0., 0, -1., -3.14}; - double xmaxpT[5] = {1.2, 3., 100, 1., 3.14}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaCS_lowmass", "", 5, varsCSpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsCSpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array binspT = {200, 30, 10, 10, 10}; + std::array xminpT = {0.2, 0., 0, -1., -3.14}; + std::array xmaxpT = {1.2, 3., 100, 1., 3.14}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaCS_lowmass", "", 5, varsCSpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-vp-pbpb")) { - int varsVPpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosPhiVP, VarManager::kPhiVP}; - int binspT[5] = {150, 30, 10, 24, 24}; - double xminpT[5] = {2., 0., 0, -1., 0.}; - double xmaxpT[5] = {5., 3., 100, 1., 3.14}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_phiVP", "", 5, varsVPpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsVPpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosPhiVP, VarManager::kPhiVP}; + std::array binspT = {150, 30, 10, 24, 24}; + std::array xminpT = {2., 0., 0, -1., 0.}; + std::array xmaxpT = {5., 3., 100, 1., 3.14}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_phiVP", "", 5, varsVPpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-polarization-lowmass-vp-pbpb")) { - int varsVPpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosPhiVP, VarManager::kPhiVP}; - int binspT[5] = {200, 30, 10, 24, 24}; - double xminpT[5] = {0.2, 0., 0, -1., 0.}; - double xmaxpT[5] = {1.2, 3., 100, 1., 3.14}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_phiVP_lowmass", "", 5, varsVPpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsVPpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosPhiVP, VarManager::kPhiVP}; + std::array binspT = {200, 30, 10, 24, 24}; + std::array xminpT = {0.2, 0., 0, -1., 0.}; + std::array xmaxpT = {1.2, 3., 100, 1., 3.14}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_phiVP_lowmass", "", 5, varsVPpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-rap-polarization-he-pbpb")) { - int varsHEpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kRap}; - int binspT[5] = {150, 30, 10, 10, 6}; - double xminpT[5] = {2., 0., 0, -1., 2.5}; - double xmaxpT[5] = {5., 3., 100, 1., 4.0}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaHE_Rap", "", 5, varsHEpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsHEpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaHE, VarManager::kRap}; + std::array binspT = {150, 30, 10, 10, 6}; + std::array xminpT = {2., 0., 0, -1., 2.5}; + std::array xmaxpT = {5., 3., 100, 1., 4.0}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaHE_Rap", "", 5, varsHEpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-rap-polarization-cs-pbpb")) { - int varsCSpbpb[5] = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kRap}; - int binspT[5] = {150, 30, 10, 10, 6}; - double xminpT[5] = {2., 0., 0, -1., 2.5}; - double xmaxpT[5] = {5., 3., 100, 1., 4.0}; - hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaCS_Rap", "", 5, varsCSpbpb, binspT, xminpT, xmaxpT, 0, -1, kFALSE); + std::array varsCSpbpb = {VarManager::kMass, VarManager::kPt, VarManager::kCentFT0C, VarManager::kCosThetaCS, VarManager::kRap}; + std::array binspT = {150, 30, 10, 10, 6}; + std::array xminpT = {2., 0., 0, -1., 2.5}; + std::array xmaxpT = {5., 3., 100, 1., 4.0}; + hm->AddHistogram(histClass, "Mass_Pt_Cent_cosThetaCS_Rap", "", 5, varsCSpbpb.data(), binspT.data(), xminpT.data(), xmaxpT.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-midmult-polarization-he")) { - int varsITSTPCMulHE[4] = {VarManager::kMass, VarManager::kMultNTracksITSTPC, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int varsITSMulHE[4] = {VarManager::kMass, VarManager::kMultNTracksHasITS, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int binsMul[4] = {100, 20, 20, 20}; - double xminMul[4] = {1., 0., -1., -3.14}; - double xmaxMul[4] = {5., 120., 1., +3.14}; - hm->AddHistogram(histClass, "Mass_ITSTPCMult_cosThetaHE_phiHE", "", 4, varsITSTPCMulHE, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Mass_ITSMult_cosThetaHE_phiHE", "", 4, varsITSMulHE, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); + std::array varsITSTPCMulHE = {VarManager::kMass, VarManager::kMultNTracksITSTPC, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array varsITSMulHE = {VarManager::kMass, VarManager::kMultNTracksHasITS, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array binsMul = {100, 20, 20, 20}; + std::array xminMul = {1., 0., -1., -3.14}; + std::array xmaxMul = {5., 120., 1., +3.14}; + hm->AddHistogram(histClass, "Mass_ITSTPCMult_cosThetaHE_phiHE", "", 4, varsITSTPCMulHE.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); + hm->AddHistogram(histClass, "Mass_ITSMult_cosThetaHE_phiHE", "", 4, varsITSMulHE.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-midmult-polarization-cs")) { - int varsITSTPCMulCS[4] = {VarManager::kMass, VarManager::kMultNTracksITSTPC, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int varsITSMulCS[4] = {VarManager::kMass, VarManager::kMultNTracksHasITS, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int binsMul[4] = {100, 20, 20, 20}; - double xminMul[4] = {1., 0., -1., -3.14}; - double xmaxMul[4] = {5., 120., 1., +3.14}; - hm->AddHistogram(histClass, "Mass_ITSTPCMult_cosThetaCS_phiCS", "", 4, varsITSTPCMulCS, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Mass_ITSMult_cosThetaCS_phiCS", "", 4, varsITSMulCS, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); + std::array varsITSTPCMulCS = {VarManager::kMass, VarManager::kMultNTracksITSTPC, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array varsITSMulCS = {VarManager::kMass, VarManager::kMultNTracksHasITS, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array binsMul = {100, 20, 20, 20}; + std::array xminMul = {1., 0., -1., -3.14}; + std::array xmaxMul = {5., 120., 1., +3.14}; + hm->AddHistogram(histClass, "Mass_ITSTPCMult_cosThetaCS_phiCS", "", 4, varsITSTPCMulCS.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); + hm->AddHistogram(histClass, "Mass_ITSMult_cosThetaCS_phiCS", "", 4, varsITSMulCS.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-fwdmult-polarization-he")) { - int varsFT0AMulHE[4] = {VarManager::kMass, VarManager::kMultFT0A, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int varsFV0AMulHE[4] = {VarManager::kMass, VarManager::kMultFV0A, VarManager::kCosThetaHE, VarManager::kPhiHE}; - int binsMul[4] = {100, 20, 20, 20}; - double xminMul[4] = {1., 0., -1., -3.14}; - double xmaxMul[4] = {5., 3000., 1., +3.14}; - hm->AddHistogram(histClass, "Mass_FT0AMult_cosThetaHE_phiHE", "", 4, varsFT0AMulHE, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Mass_FV0AMult_cosThetaHE_phiHE", "", 4, varsFV0AMulHE, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); + std::array varsFT0AMulHE = {VarManager::kMass, VarManager::kMultFT0A, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array varsFV0AMulHE = {VarManager::kMass, VarManager::kMultFV0A, VarManager::kCosThetaHE, VarManager::kPhiHE}; + std::array binsMul = {100, 20, 20, 20}; + std::array xminMul = {1., 0., -1., -3.14}; + std::array xmaxMul = {5., 3000., 1., +3.14}; + hm->AddHistogram(histClass, "Mass_FT0AMult_cosThetaHE_phiHE", "", 4, varsFT0AMulHE.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); + hm->AddHistogram(histClass, "Mass_FV0AMult_cosThetaHE_phiHE", "", 4, varsFV0AMulHE.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("dimuon-fwdmult-polarization-cs")) { - int varsFT0AMulCS[4] = {VarManager::kMass, VarManager::kMultFT0A, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int varsFV0AMulCS[4] = {VarManager::kMass, VarManager::kMultFV0A, VarManager::kCosThetaCS, VarManager::kPhiCS}; - int binsMul[4] = {100, 20, 20, 20}; - double xminMul[4] = {1., 0., -1., -3.14}; - double xmaxMul[4] = {5., 3000., 1., +3.14}; - hm->AddHistogram(histClass, "Mass_FT0AMult_cosThetaCS_phiCS", "", 4, varsFT0AMulCS, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); - hm->AddHistogram(histClass, "Mass_FV0AMult_cosThetaCS_phiCS", "", 4, varsFV0AMulCS, binsMul, xminMul, xmaxMul, 0, -1, kFALSE); + std::array varsFT0AMulCS = {VarManager::kMass, VarManager::kMultFT0A, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array varsFV0AMulCS = {VarManager::kMass, VarManager::kMultFV0A, VarManager::kCosThetaCS, VarManager::kPhiCS}; + std::array binsMul = {100, 20, 20, 20}; + std::array xminMul = {1., 0., -1., -3.14}; + std::array xmaxMul = {5., 3000., 1., +3.14}; + hm->AddHistogram(histClass, "Mass_FT0AMult_cosThetaCS_phiCS", "", 4, varsFT0AMulCS.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); + hm->AddHistogram(histClass, "Mass_FV0AMult_cosThetaCS_phiCS", "", 4, varsFV0AMulCS.data(), binsMul.data(), xminMul.data(), xmaxMul.data(), nullptr, -1, kFALSE); } if (subGroupStr.Contains("vertexing-forward")) { hm->AddHistogram(histClass, "Lxyz", "", false, 100, 0.0, 10.0, VarManager::kVertexingLxyz); @@ -1784,31 +1959,41 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_Lxyz", "", false, 250, 0.0, 5.0, VarManager::kMass, 1000, 0.0, 5, VarManager::kVertexingLxyz); hm->AddHistogram(histClass, "Mass_OpeningAngle", "", false, 250, 0.0, 5.0, VarManager::kMass, 800, 0, 0.8, VarManager::kOpeningAngle); } + if (subGroupStr.Contains("flow-pos-neg-dimuon")) { + std::array varV2POS = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU2Q2POS, VarManager::kCos2DeltaPhiPOS}; + std::array varV2NEG = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU2Q2NEG, VarManager::kCos2DeltaPhiNEG}; + + std::array bins = {250, 60, 6, 18, 200, 40}; + std::array minBins = {0.0, 0.0, 2.5, 0.0, -10.0, -2.0}; + std::array maxBins = {5.0, 30.0, 4.0, 90.0, 10.0, 2.0}; + hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V2POS", "", 6, varV2POS.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); + hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V2NEG", "", 6, varV2NEG.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); + } if (subGroupStr.Contains("flow-dimuon-high-mass")) { - int varV2[6] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU2Q2, VarManager::kCos2DeltaPhi}; + std::array varV2 = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU2Q2, VarManager::kCos2DeltaPhi}; - int bins[6] = {50, 30, 6, 18, 200, 40}; - double minBins[6] = {7.0, 0.0, 2.5, 0.0, -10.0, -2.0}; - double maxBins[6] = {12.0, 30.0, 4.0, 90.0, 10.0, 2.0}; - hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V2", "", 6, varV2, bins, minBins, maxBins, 0, -1, kTRUE); + std::array bins = {50, 30, 6, 18, 200, 40}; + std::array minBins = {7.0, 0.0, 2.5, 0.0, -10.0, -2.0}; + std::array maxBins = {12.0, 30.0, 4.0, 90.0, 10.0, 2.0}; + hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V2", "", 6, varV2.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); } if (subGroupStr.Contains("flow-dimuon")) { - int varV2[6] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU2Q2, VarManager::kCos2DeltaPhi}; - // int varV3[6] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU3Q3, VarManager::kCos3DeltaPhi}; // removed temporarily + std::array varV2 = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU2Q2, VarManager::kCos2DeltaPhi}; + // std::array varV3 = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kU3Q3, VarManager::kCos3DeltaPhi}; // removed temporarily - int bins[6] = {250, 60, 6, 18, 200, 40}; - double minBins[6] = {0.0, 0.0, 2.5, 0.0, -10.0, -2.0}; - double maxBins[6] = {5.0, 30.0, 4.0, 90.0, 10.0, 2.0}; - hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V2", "", 6, varV2, bins, minBins, maxBins, 0, -1, kTRUE); - // hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V3", "", 6, varV3, bins, minBins, maxBins, 0, -1, kTRUE); // removed temporarily + std::array bins = {250, 60, 6, 18, 200, 40}; + std::array minBins = {0.0, 0.0, 2.5, 0.0, -10.0, -2.0}; + std::array maxBins = {5.0, 30.0, 4.0, 90.0, 10.0, 2.0}; + hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V2", "", 6, varV2.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); + // hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_V3", "", 6, varV3.data(), bins.data(), minBins.data(), maxBins.data(), 0, -1, kTRUE); // removed temporarily } if (subGroupStr.Contains("flow-ccdb")) { - double MassBinEdges[251]; // 0-5GeV/c2 + std::array MassBinEdges{}; // 0-5GeV/c2 for (int i = 0; i < 251; i++) { MassBinEdges[i] = i * 0.02; } - double PtBinEdges[49]; // 0-20GeV/c + std::array PtBinEdges{}; // 0-20GeV/c for (int i = 0; i < 49; i++) { if (i <= 9) { PtBinEdges[i] = i / 10.; @@ -1817,19 +2002,19 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } - double CentBinEdges[19]; // 0-90% + std::array CentBinEdges{}; // 0-90% for (int i = 0; i < 19; i++) { CentBinEdges[i] = i * 5; } - hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2SPwR", "Mass_Pt_CentFT0C_V2SPwR", true, 250, MassBinEdges, VarManager::kMass, 48, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, "", "", "", VarManager::kV2SP, VarManager::kWV2SP); - hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2EPwR", "Mass_Pt_CentFT0C_V2EPwR", true, 250, MassBinEdges, VarManager::kMass, 48, PtBinEdges, VarManager::kPt, 18, CentBinEdges, VarManager::kCentFT0C, "", "", "", VarManager::kV2EP, VarManager::kWV2EP); + hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2SPwR", "Mass_Pt_CentFT0C_V2SPwR", true, 250, MassBinEdges.data(), VarManager::kMass, 48, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, "", "", "", VarManager::kV2SP, VarManager::kWV2SP); + hm->AddHistogram(histClass, "Mass_Pt_CentFT0C_V2EPwR", "Mass_Pt_CentFT0C_V2EPwR", true, 250, MassBinEdges.data(), VarManager::kMass, 48, PtBinEdges.data(), VarManager::kPt, 18, CentBinEdges.data(), VarManager::kCentFT0C, "", "", "", VarManager::kV2EP, VarManager::kWV2EP); } if (subGroupStr.Contains("cumulant")) { - int var[4] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C}; - int bins[4] = {250, 60, 6, 18}; - double minBins[4] = {0.0, 0.0, 2.5, 0.0}; - double maxBins[4] = {5.0, 30.0, 4.0, 90.0}; + std::array var = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C}; + std::array bins = {250, 60, 6, 18}; + std::array minBins = {0.0, 0.0, 2.5, 0.0}; + std::array maxBins = {5.0, 30.0, 4.0, 90.0}; hm->AddHistogram(histClass, "centrFT0C_M11REFoverMp_ev", "", true, 100, 0.0, 100.0, VarManager::kCentFT0C, 1000, 0.0, 1000000.0, VarManager::kM11REFoverMp); hm->AddHistogram(histClass, "centrFT0C_M1111REFoverMp_ev", "", true, 100, 0.0, 100.0, VarManager::kCentFT0C, 1000, 0.0, 100000000000000.0, VarManager::kM1111REFoverMp); hm->AddHistogram(histClass, "centrFT0C_M11M1111REFoverMp_ev", "", true, 100, 0.0, 100.0, VarManager::kCentFT0C, 1000, 0.0, 10000000000000000.0, VarManager::kM11M1111REFoverMp); @@ -1844,7 +2029,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_M01M0111overMp", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kM01M0111overMp); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_M11M0111overMp", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kM11M0111overMp); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_M11M01REFoverMp", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kM11M01overMp); - hm->AddHistogram(histClass, "Mass_Pt_Rapidity_CentFT0C", "", 4, var, bins, minBins, maxBins, 0, -1, kTRUE); + hm->AddHistogram(histClass, "Mass_Pt_Rapidity_CentFT0C", "", 4, var.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr2REF", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR2REFbydimuons, VarManager::kM11REFoverMp); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr4REF", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR4REFbydimuons, VarManager::kM1111REFoverMp); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr2POI", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR2POI, VarManager::kM01POIoverMp); @@ -1855,11 +2040,11 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr2REFCorr2POI", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR2REFCORR2POI, VarManager::kM11M01overMp); } if (subGroupStr.Contains("cumulant1")) { - int var[4] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C}; - int bins[4] = {250, 60, 6, 18}; - double minBins[4] = {0.0, 0.0, 2.5, 0.0}; - double maxBins[4] = {5.0, 30.0, 4.0, 90.0}; - hm->AddHistogram(histClass, "Mass_Pt_Rapidity_CentFT0C", "", 4, var, bins, minBins, maxBins, 0, -1, kTRUE); + std::array var = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C}; + std::array bins = {250, 60, 6, 18}; + std::array minBins = {0.0, 0.0, 2.5, 0.0}; + std::array maxBins = {5.0, 30.0, 4.0, 90.0}; + hm->AddHistogram(histClass, "Mass_Pt_Rapidity_CentFT0C", "", 4, var.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr2REF", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR2REFbydimuons, VarManager::kM11REFoverMp); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr4REF", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR4REFbydimuons, VarManager::kM1111REFoverMp); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr2POI", "", true, 250, 0.0, 5.0, VarManager::kMass, 60, 0.0, 30.0, VarManager::kPt, 18, 0.0, 90.0, VarManager::kCentFT0C, "", "", "", VarManager::kCORR2POI, VarManager::kM01POIoverMp); @@ -1878,11 +2063,11 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "centrFT0C_Corr2Corr4REF_ev", "", true, 100, 0.0, 100.0, VarManager::kCentFT0C, 250, -1.0, 1.0, VarManager::kCORR2CORR4REF, VarManager::kM11M1111REFoverMp); } if (subGroupStr.Contains("singlecumulant")) { - int var[4] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C}; - int bins[4] = {250, 60, 6, 18}; - double minBins[4] = {0.0, 0.0, 2.5, 0.0}; - double maxBins[4] = {5.0, 30.0, 4.0, 90.0}; - hm->AddHistogram(histClass, "Mass_Pt_Rapidity_CentFT0C", "", 4, var, bins, minBins, maxBins, 0, -1, kTRUE); + std::array var = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C}; + std::array bins = {250, 60, 6, 18}; + std::array minBins = {0.0, 0.0, 2.5, 0.0}; + std::array maxBins = {5.0, 30.0, 4.0, 90.0}; + hm->AddHistogram(histClass, "Mass_Pt_Rapidity_CentFT0C", "", 4, var.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr2REFminus", "", true, 60, 0.0, 30.0, VarManager::kPt2, 18, 0.0, 90.0, VarManager::kCentFT0C, 0, 0.0, 1.0, VarManager::kCORR2REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpminus); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr4REFminus", "", true, 60, 0.0, 30.0, VarManager::kPt2, 18, 0.0, 90.0, VarManager::kCentFT0C, 0, 0.0, 1.0, VarManager::kCORR4REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM1111REFoverMpminus); hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr2REFplus", "", true, 60, 0.0, 30.0, VarManager::kPt1, 18, 0.0, 90.0, VarManager::kCentFT0C, 0, 0.0, 1.0, VarManager::kCORR2REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpplus); @@ -1893,7 +2078,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_Corr4POIplus", "", true, 60, 0.0, 30.0, VarManager::kPt1, 18, 0.0, 90.0, VarManager::kCentFT0C, 0, 0.0, 1.0, VarManager::kCORR4POIplus, "", "", "", VarManager::kNothing, VarManager::kM0111POIoverMpplus); } if (subGroupStr.Contains("singlecumulant2")) { - double PtBinEdges[67]; // 0-30GeV/c + std::array PtBinEdges{}; // 0-30GeV/c for (int i = 0; i < 67; i++) { if (i <= 39) { PtBinEdges[i] = i / 10.; @@ -1901,28 +2086,28 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h PtBinEdges[i] = (i - 40) * 1. + 4.; } } - double CentBinEdges[19]; // 0-90% + std::array CentBinEdges{}; // 0-90% for (int i = 0; i < 19; i++) { CentBinEdges[i] = i * 5; } - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2REFminus", "", true, 66, PtBinEdges, VarManager::kPt2, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpminus); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4REFminus", "", true, 66, PtBinEdges, VarManager::kPt2, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM1111REFoverMpminus); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2REFplus", "", true, 66, PtBinEdges, VarManager::kPt1, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpplus); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4REFplus", "", true, 66, PtBinEdges, VarManager::kPt1, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM1111REFoverMpplus); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2POIminus", "", true, 66, PtBinEdges, VarManager::kPt2, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2POIminus, "", "", "", VarManager::kNothing, VarManager::kM01POIoverMpminus); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4POIminus", "", true, 66, PtBinEdges, VarManager::kPt2, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4POIminus, "", "", "", VarManager::kNothing, VarManager::kM0111POIoverMpminus); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2POIplus", "", true, 66, PtBinEdges, VarManager::kPt1, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2POIplus, "", "", "", VarManager::kNothing, VarManager::kM01POIoverMpplus); - hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4POIplus", "", true, 66, PtBinEdges, VarManager::kPt1, 18, CentBinEdges, VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4POIplus, "", "", "", VarManager::kNothing, VarManager::kM0111POIoverMpplus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2REFminus", "", true, 66, PtBinEdges.data(), VarManager::kPt2, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpminus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4REFminus", "", true, 66, PtBinEdges.data(), VarManager::kPt2, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM1111REFoverMpminus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2REFplus", "", true, 66, PtBinEdges.data(), VarManager::kPt1, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM11REFoverMpplus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4REFplus", "", true, 66, PtBinEdges.data(), VarManager::kPt1, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4REFbydimuons, "", "", "", VarManager::kNothing, VarManager::kM1111REFoverMpplus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2POIminus", "", true, 66, PtBinEdges.data(), VarManager::kPt2, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2POIminus, "", "", "", VarManager::kNothing, VarManager::kM01POIoverMpminus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4POIminus", "", true, 66, PtBinEdges.data(), VarManager::kPt2, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4POIminus, "", "", "", VarManager::kNothing, VarManager::kM0111POIoverMpminus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr2POIplus", "", true, 66, PtBinEdges.data(), VarManager::kPt1, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR2POIplus, "", "", "", VarManager::kNothing, VarManager::kM01POIoverMpplus); + hm->AddHistogram(histClass, "Pt_centrFT0C_Corr4POIplus", "", true, 66, PtBinEdges.data(), VarManager::kPt1, 18, CentBinEdges.data(), VarManager::kCentFT0C, 0, nullptr, VarManager::kCORR4POIplus, "", "", "", VarManager::kNothing, VarManager::kM0111POIoverMpplus); } if (subGroupStr.Contains("res-flow-dimuon")) { - int varV2[6] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kR2SP_AB, VarManager::kR2EP_AB}; - // int varV3[6] = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kR3SP, VarManager::kR3EP}; // removed temporarily + std::array varV2 = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kR2SP_AB, VarManager::kR2EP_AB}; + // std::array varV3 = {VarManager::kMass, VarManager::kPt, VarManager::kRap, VarManager::kCentFT0C, VarManager::kR3SP, VarManager::kR3EP}; // removed temporarily - int bins[6] = {125, 60, 6, 18, 200, 40}; - double minBins[6] = {0.0, 0.0, 2.5, 0.0, -10.0, -2.0}; - double maxBins[6] = {5.0, 30.0, 4.0, 90.0, 10.0, 2.0}; - hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_R2", "", 6, varV2, bins, minBins, maxBins, 0, -1, kTRUE); - // hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_R3", "", 6, varV3, bins, minBins, maxBins, 0, -1, kTRUE); // removed temporarily + std::array bins = {125, 60, 6, 18, 200, 40}; + std::array minBins = {0.0, 0.0, 2.5, 0.0, -10.0, -2.0}; + std::array maxBins = {5.0, 30.0, 4.0, 90.0, 10.0, 2.0}; + hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_R2", "", 6, varV2.data(), bins.data(), minBins.data(), maxBins.data(), nullptr, -1, kTRUE); + // hm->AddHistogram(histClass, "Mass_Pt_centrFT0C_R3", "", 6, varV3.data(), bins.data(), minBins.data(), maxBins.data(), 0, -1, kTRUE); // removed temporarily } if (subGroupStr.Contains("z-boson")) { hm->AddHistogram(histClass, "MassZboson", "", false, 240, 20.0, 140.0, VarManager::kMass); @@ -1950,6 +2135,15 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h if (subGroupStr.Contains("correlation-emu")) { hm->AddHistogram(histClass, "DeltaPhiPair2_DeltaEtaPair2", "", false, 600, -o2::constants::math::PIHalf, 1.5 * o2::constants::math::PI, VarManager::kDeltaPhiPair2, 350, 1.5, 5.0, VarManager::kDeltaEtaPair2); hm->AddHistogram(histClass, "DeltaPhiPair2_Pt", "", false, 600, -o2::constants::math::PIHalf, 1.5 * o2::constants::math::PI, VarManager::kDeltaPhiPair2, 200, 0.0, 20.0, VarManager::kPt); + // 4D correlation map: (Delta phi_pair, Delta eta_pair, p_T^{e}, p_T^{mu}). Per-track pT axes use kPt1/kPt2, + // which FillPair populates for SE and FillPairME (patched in this PR) populates for ME. Stored as THnSparse so + // running with many MC-matched hist classes (track cut x muon cut x signal x QA variant) does not blow the + // 1 GB TBufferFile limit during the final ROOT serialization. + std::array varsEmu4D = {VarManager::kDeltaPhiPair2, VarManager::kDeltaEtaPair2, VarManager::kPt1, VarManager::kPt2}; + std::array binsEmu4D = {60, 35, 20, 20}; + std::array xminEmu4D = {-o2::constants::math::PIHalf, 1.5, 0.0, 0.0}; + std::array xmaxEmu4D = {1.5 * o2::constants::math::PI, 5.0, 20.0, 20.0}; + hm->AddHistogram(histClass, "DeltaPhiPair2_DeltaEtaPair2_PtE_PtMu", "", 4, varsEmu4D.data(), binsEmu4D.data(), xminEmu4D.data(), xmaxEmu4D.data(), nullptr, -1, kTRUE); } if (subGroupStr.Contains("dielectrons")) { if (subGroupStr.Contains("prefilter")) { @@ -1966,31 +2160,36 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h // every 10 MeV from 0 to 0.2 GeV/c2 // every 100 MeV from 0.2 to 1. GeV/c2 // every 500 GeV from 1 to 5 GeV/c2 - double mee_bins[37]; - for (int i = 0; i <= 20; i++) + std::array mee_bins{}; + for (int i = 0; i <= 20; i++) { mee_bins[i] = 0.01 * i; - for (int i = 1; i <= 8; i++) + } + for (int i = 1; i <= 8; i++) { mee_bins[20 + i] = 0.2 + 0.1 * i; - for (int i = 1; i <= 8; i++) + } + for (int i = 1; i <= 8; i++) { mee_bins[28 + i] = 1. + 0.5 * i; - int nbins_mee = sizeof(mee_bins) / sizeof(*mee_bins) - 1; + } + int nbins_mee = static_cast(mee_bins.size()) - 1; // binning for ptee at large scales: // every 0.2 GeV/c from 0 to 10 GeV/c - double ptee_bins[51]; - for (int i = 0; i <= 50; i++) + std::array ptee_bins{}; + for (int i = 0; i <= 50; i++) { ptee_bins[i] = 0.2 * i; - int nbins_ptee = sizeof(ptee_bins) / sizeof(*ptee_bins) - 1; + } + int nbins_ptee = static_cast(ptee_bins.size()) - 1; // binning for phiv: // steps of size pi/100 - double phiv_bins[101]; - for (int i = 0; i <= 100; i++) + std::array phiv_bins{}; + for (int i = 0; i <= 100; i++) { phiv_bins[i] = o2::constants::math::PI / 100. * i; - int nbins_phiv = sizeof(phiv_bins) / sizeof(*phiv_bins) - 1; + } + int nbins_phiv = static_cast(phiv_bins.size()) - 1; // 3D histo - hm->AddHistogram(histClass, "Mass_Pt_PhiV", "", false, nbins_mee, mee_bins, VarManager::kMass, nbins_ptee, ptee_bins, VarManager::kPt, nbins_phiv, phiv_bins, VarManager::kPairPhiv); + hm->AddHistogram(histClass, "Mass_Pt_PhiV", "", false, nbins_mee, mee_bins.data(), VarManager::kMass, nbins_ptee, ptee_bins.data(), VarManager::kPt, nbins_phiv, phiv_bins.data(), VarManager::kPairPhiv); } if (subGroupStr.Contains("meeptee")) { hm->AddHistogram(histClass, "Mass_Pt", "", false, 500, 0.0, 5.0, VarManager::kMass, 100, 0.0, 10.0, VarManager::kPt); @@ -2010,43 +2209,52 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h // every 50 MeV from 1.1 to 2.7 GeV/c2 // every 10 MeV from 2.7 to 3.2 GeV/c2 // every 50 MeV from 3.2 to 12 GeV/c2 - double mee_bins[369]; - for (int i = 0; i <= 110; i++) + std::array mee_bins{}; + for (int i = 0; i <= 110; i++) { mee_bins[i] = 0.01 * i; - for (int i = 1; i <= 32; i++) + } + for (int i = 1; i <= 32; i++) { mee_bins[110 + i] = 1.1 + 0.05 * i; - for (int i = 1; i <= 50; i++) + } + for (int i = 1; i <= 50; i++) { mee_bins[142 + i] = 2.7 + 0.01 * i; - for (int i = 1; i <= 176; i++) + } + for (int i = 1; i <= 176; i++) { mee_bins[192 + i] = 3.2 + 0.05 * i; - int nbins_mee = sizeof(mee_bins) / sizeof(*mee_bins) - 1; + } + int nbins_mee = static_cast(mee_bins.size()) - 1; // binning for ptee at large scales: // every 0.1 GeV/c from 0 to 10 GeV/c // every 0.5 GeV/c from 10 to 30 GeV/c - double ptee_bins[201]; - for (int i = 0; i <= 100; i++) + std::array ptee_bins{}; + for (int i = 0; i <= 100; i++) { ptee_bins[i] = 0.1 * i; - for (int i = 1; i <= 100; i++) + } + for (int i = 1; i <= 100; i++) { ptee_bins[100 + i] = 10 + 0.2 * i; - int nbins_ptee = sizeof(ptee_bins) / sizeof(*ptee_bins) - 1; + } + int nbins_ptee = static_cast(ptee_bins.size()) - 1; // binning for dca at large scales: // every 0.1 sigma from 0 to 5 sigma // every 0.5 sigma from 5 to 10 sigma // every 1.0 sigma from 10 to 40 sigma - double dca_bins[91]; - for (int i = 0; i <= 50; i++) + std::array dca_bins{}; + for (int i = 0; i <= 50; i++) { dca_bins[i] = 0.1 * i; - for (int i = 1; i <= 10; i++) + } + for (int i = 1; i <= 10; i++) { dca_bins[50 + i] = 5 + 0.5 * i; - for (int i = 1; i <= 30; i++) + } + for (int i = 1; i <= 30; i++) { dca_bins[60 + i] = 10 + 1 * i; - int nbins_dca = sizeof(dca_bins) / sizeof(*dca_bins) - 1; + } + int nbins_dca = static_cast(dca_bins.size()) - 1; - hm->AddHistogram(histClass, "Mass_QuadDCAsigXY", "", false, nbins_mee, mee_bins, VarManager::kMass, nbins_dca, dca_bins, VarManager::kQuadDCAsigXY); - hm->AddHistogram(histClass, "Mass_QuadDCAsigZ", "", false, nbins_mee, mee_bins, VarManager::kMass, nbins_dca, dca_bins, VarManager::kQuadDCAsigZ); - hm->AddHistogram(histClass, "Mass_Pt_QuadDCAsigXYZ", "", false, nbins_mee, mee_bins, VarManager::kMass, nbins_ptee, ptee_bins, VarManager::kPt, nbins_dca, dca_bins, VarManager::kQuadDCAsigXYZ); + hm->AddHistogram(histClass, "Mass_QuadDCAsigXY", "", false, nbins_mee, mee_bins.data(), VarManager::kMass, nbins_dca, dca_bins.data(), VarManager::kQuadDCAsigXY); + hm->AddHistogram(histClass, "Mass_QuadDCAsigZ", "", false, nbins_mee, mee_bins.data(), VarManager::kMass, nbins_dca, dca_bins.data(), VarManager::kQuadDCAsigZ); + hm->AddHistogram(histClass, "Mass_Pt_QuadDCAsigXYZ", "", false, nbins_mee, mee_bins.data(), VarManager::kMass, nbins_ptee, ptee_bins.data(), VarManager::kPt, nbins_dca, dca_bins.data(), VarManager::kQuadDCAsigXYZ); } } if (subGroupStr.Contains("opencharm")) { @@ -2078,7 +2286,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } } - if (!groupStr.CompareTo("dilepton-track")) { + if (groupStr.CompareTo("dilepton-track") == 0) { if (subGroupStr.Contains("mixedevent")) { // for mixed event hm->AddHistogram(histClass, "Mass_Pt", "", false, 40, 0.0, 20.0, VarManager::kPairMass, 40, 0.0, 20.0, VarManager::kPairPt); hm->AddHistogram(histClass, "Mass", "", false, 750, 0.0, 30.0, VarManager::kPairMass); @@ -2125,26 +2333,27 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h const int kNvarsTripletCuts = 4; const int kInvMassNbins = 100; - double InvMassBinLims[kInvMassNbins + 1]; - for (int i = 0; i <= kInvMassNbins; ++i) + std::array InvMassBinLims{}; + for (int i = 0; i <= kInvMassNbins; ++i) { InvMassBinLims[i] = 4.0 + 0.02 * i; + } const int kPtNbins = 6; - double PtBinLims[kPtNbins + 1] = {0., 2., 4., 6., 8., 10., 20.}; + std::array PtBinLims = {0., 2., 4., 6., 8., 10., 20.}; const int kCosPointingAngleNbins = 5; - double CosPointingAngleBinLims[kCosPointingAngleNbins + 1] = {0., 0.86, 0.90, 0.94, 0.98, 1.0}; + std::array CosPointingAngleBinLims = {0., 0.86, 0.90, 0.94, 0.98, 1.0}; const int kTauNBins = 6; - double TauBinLims[kTauNBins + 1] = {0., 0.005, 0.01, 0.015, 0.02, 0.025, 0.3}; + std::array TauBinLims = {0., 0.005, 0.01, 0.015, 0.02, 0.025, 0.3}; - TArrayD nCutsBinLimits[kNvarsTripletCuts]; - nCutsBinLimits[0] = TArrayD(kInvMassNbins + 1, InvMassBinLims); - nCutsBinLimits[1] = TArrayD(kPtNbins + 1, PtBinLims); - nCutsBinLimits[2] = TArrayD(kCosPointingAngleNbins + 1, CosPointingAngleBinLims); - nCutsBinLimits[3] = TArrayD(kTauNBins + 1, TauBinLims); + std::array nCutsBinLimits; + nCutsBinLimits[0] = TArrayD(kInvMassNbins + 1, InvMassBinLims.data()); + nCutsBinLimits[1] = TArrayD(kPtNbins + 1, PtBinLims.data()); + nCutsBinLimits[2] = TArrayD(kCosPointingAngleNbins + 1, CosPointingAngleBinLims.data()); + nCutsBinLimits[3] = TArrayD(kTauNBins + 1, TauBinLims.data()); - int varsTripletCuts[kNvarsTripletCuts] = {VarManager::kPairMass, VarManager::kPairPt, VarManager::kCosPointingAngle, VarManager::kVertexingTauxyProjected}; - hm->AddHistogram(histClass, "multidimentional-vertexing", "Invariant mass vs. pT vs. cosine of pointing angle vs. tau", kNvarsTripletCuts, varsTripletCuts, nCutsBinLimits); + std::array varsTripletCuts = {VarManager::kPairMass, VarManager::kPairPt, VarManager::kCosPointingAngle, VarManager::kVertexingTauxyProjected}; + hm->AddHistogram(histClass, "multidimentional-vertexing", "Invariant mass vs. pT vs. cosine of pointing angle vs. tau", kNvarsTripletCuts, varsTripletCuts.data(), nCutsBinLimits.data()); } if (subGroupStr.Contains("correlation")) { hm->AddHistogram(histClass, "DeltaEta_DeltaPhi", "", false, 20, -2.0, 2.0, VarManager::kDeltaEta, 50, -8.0, 8.0, VarManager::kDeltaPhi); @@ -2152,30 +2361,32 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } if (subGroupStr.Contains("dilepton-hadron-array-correlation")) { const int kInvMassBins = 500; - double InvMassBinLims[kInvMassBins + 1]; - for (int i = 0; i <= kInvMassBins; i++) + std::array InvMassBinLims{}; + for (int i = 0; i <= kInvMassBins; i++) { InvMassBinLims[i] = 0 + i * 0.01; + } const int kDelEtaBins = 20; - double DelEtaBinLims[kDelEtaBins + 1]; - for (int i = 0; i <= kDelEtaBins; i++) + std::array DelEtaBinLims{}; + for (int i = 0; i <= kDelEtaBins; i++) { DelEtaBinLims[i] = -2 + i * 0.2; + } const int kDelPhiBins = 26; - double DelPhiBinLims[] = {-1.69647, -1.44513, -1.19381, -0.94248, -0.69115, -0.43982, -0.18850, 0.06283, 0.31416, 0.56549, 0.81681, 1.06814, 1.31947, 1.57080, 1.82212, 2.07345, 2.32478, 2.57611, 2.82743, 3.07876, 3.33009, 3.58142, 3.83274, 4.08407, 4.33540, 4.58673, 4.83806}; + std::array DelPhiBinLims = {-1.69647, -1.44513, -1.19381, -0.94248, -0.69115, -0.43982, -0.18850, 0.06283, 0.31416, 0.56549, 0.81681, 1.06814, 1.31947, 1.57080, 1.82212, 2.07345, 2.32478, 2.57611, 2.82743, 3.07876, 3.33009, 3.58142, 3.83274, 4.08407, 4.33540, 4.58673, 4.83806}; const int kPtBins = 12; - double PtBinLims[kPtBins + 1] = {0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 7.5, 10, 20}; + std::array PtBinLims = {0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 7.5, 10, 20}; - TArrayD nJPsiHadCorr[4]; - nJPsiHadCorr[0] = TArrayD(kInvMassBins + 1, InvMassBinLims); - nJPsiHadCorr[1] = TArrayD(kDelEtaBins + 1, DelEtaBinLims); - nJPsiHadCorr[2] = TArrayD(kDelPhiBins + 1, DelPhiBinLims); - nJPsiHadCorr[3] = TArrayD(kPtBins + 1, PtBinLims); + std::array nJPsiHadCorr; + nJPsiHadCorr[0] = TArrayD(kInvMassBins + 1, InvMassBinLims.data()); + nJPsiHadCorr[1] = TArrayD(kDelEtaBins + 1, DelEtaBinLims.data()); + nJPsiHadCorr[2] = TArrayD(kDelPhiBins + 1, DelPhiBinLims.data()); + nJPsiHadCorr[3] = TArrayD(kPtBins + 1, PtBinLims.data()); - int varsJPsiHadCorr[4] = {VarManager::kPairMassDau, VarManager::kDeltaEta, VarManager::kDeltaPhi, VarManager::kPairPtDau}; - hm->AddHistogram(histClass, "InvMass_DelEta_DelPhi", "", 4, varsJPsiHadCorr, nJPsiHadCorr); // Without efficiency - // hm->AddHistogram(histClass, "InvMass_DelEta_DelPhi", "", 4, varsJPsiHadCorr, nJPsiHadCorr, nullptr, VarManager::kJpsiHadronEff); + std::array varsJPsiHadCorr = {VarManager::kPairMassDau, VarManager::kDeltaEta, VarManager::kDeltaPhi, VarManager::kPairPtDau}; + hm->AddHistogram(histClass, "InvMass_DelEta_DelPhi", "", 4, varsJPsiHadCorr.data(), nJPsiHadCorr.data()); // Without efficiency + // hm->AddHistogram(histClass, "InvMass_DelEta_DelPhi", "", 4, varsJPsiHadCorr.data(), nJPsiHadCorr.data(), nullptr, VarManager::kJpsiHadronEff); } if (subGroupStr.Contains("dilepton-hadron-femto")) { hm->AddHistogram(histClass, "DileptonHadronKstar_DileptonMass", "", false, 150, 0.0, 3.0, VarManager::kDileptonHadronKstar, 100, 1.5, 4.5, VarManager::kPairMassDau); @@ -2184,18 +2395,18 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Delta_Mass_DstarD0region", "", false, 50, 0.14, 0.16, VarManager::kDeltaMass); } if (subGroupStr.Contains("energy-correlator")) { - double coschiBins[26]; + std::array coschiBins{}; for (int i = 0; i < 26; i++) { coschiBins[i] = -1.0 + 2.0 * TMath::Power(0.04 * i, 2.0); } - hm->AddHistogram(histClass, "Coschi_unfolding", "", false, 25, coschiBins, VarManager::kMCCosChi_rec, 25, coschiBins, VarManager::kMCCosChi_gen); - hm->AddHistogram(histClass, "Coschi", "", false, 25, coschiBins, VarManager::kCosChi, 0, nullptr, -1, 0, nullptr, -1, "", "", "", -1, VarManager::kECWeight); + hm->AddHistogram(histClass, "Coschi_unfolding", "", false, 25, coschiBins.data(), VarManager::kMCCosChi_rec, 25, coschiBins.data(), VarManager::kMCCosChi_gen); + hm->AddHistogram(histClass, "Coschi", "", false, 25, coschiBins.data(), VarManager::kCosChi, 0, nullptr, -1, 0, nullptr, -1, "", "", "", -1, VarManager::kECWeight); hm->AddHistogram(histClass, "DeltaEta_DeltaPhi_weight", "", false, 20, -2.0, 2.0, VarManager::kDeltaEta, 50, -2.0, 6.0, VarManager::kDeltaPhi, 0, 0, 0, -1, "", "", "", -1, VarManager::kPtDau); } } - if (!groupStr.CompareTo("dilepton-charmhadron")) { + if (groupStr.CompareTo("dilepton-charmhadron") == 0) { if (subGroupStr.EqualTo("jpsitomumu")) { hm->AddHistogram(histClass, "hMassVsPtJPsi", "", false, 100, 0.f, 50.f, VarManager::kPt, 300, 2.f, 5.f, VarManager::kMass); hm->AddHistogram(histClass, "hRapVsPtJPsi", "", false, 100, 0.f, 50.f, VarManager::kPt, 50, -4.5f, -2.0f, VarManager::kRap); @@ -2224,7 +2435,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "hPhiVsPtVsBdtDmesonWithJPsi", "", false, 100, 0.f, 1.f, VarManager::kBdtCharmHadron, 100, 0.f, 50.f, VarManager::kPtCharmHadron, 180, 0., 2 * constants::math::PI, VarManager::kPhiCharmHadron); } } - if (!groupStr.CompareTo("dilepton-dihadron")) { + if (groupStr.CompareTo("dilepton-dihadron") == 0) { if (subGroupStr.Contains("xtojpsipipi") || subGroupStr.Contains("psi2stojpsipipi")) { hm->AddHistogram(histClass, "hMass_X3872", "", false, 1000, 3.0, 5.0, VarManager::kQuadMass); hm->AddHistogram(histClass, "hMass_defaultDileptonMass_X3872", "", false, 1000, 3.0, 5.0, VarManager::kQuadDefaultDileptonMass); @@ -2290,7 +2501,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "PhiMC", "MC #phi", false, 50, -6.3, 6.3, VarManager::kMCPhi); hm->AddHistogram(histClass, "YMC", "MC y", false, 50, -5.0, 5.0, VarManager::kMCY); hm->AddHistogram(histClass, "PtMC_YMC", "MC pT vs MC y", false, 120, 0.0, 30.0, VarManager::kMCPt, 1000, -5.0, 5.0, VarManager::kMCY); - hm->AddHistogram(histClass, "EtaMC_PtMC", "", false, 40, -2.0, 2.0, VarManager::kMCEta, 200, 0.0, 20.0, VarManager::kMCPt); + hm->AddHistogram(histClass, "EtaMC_PtMC", "", false, 320, -2.0, 2.0, VarManager::kMCEta, 400, 0.0, 40.0, VarManager::kMCPt); hm->AddHistogram(histClass, "VzMC", "MC vz", false, 100, -15.0, 15.0, VarManager::kMCVz); hm->AddHistogram(histClass, "VzMC_VtxZMC", "MC vz vs MC vtxZ", false, 50, -15.0, 15.0, VarManager::kMCVz, 50, -15.0, 15.0, VarManager::kMCVtxZ); hm->AddHistogram(histClass, "LzMC", "", false, 1000, 0.0, 2.0, VarManager::kMCVertexingLz); @@ -2314,7 +2525,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "TauxyMC_Tauxy", "", false, 500, -0.01, 0.01, VarManager::kMCVertexingTauxy, 500, -0.01, 0.01, VarManager::kVertexingTauxy); hm->AddHistogram(histClass, "CosPointingAngleMC", "", false, 100, 0.0, 1.0, VarManager::kMCCosPointingAngle); } - if (!groupStr.CompareTo("dilepton-photon-mass")) { + if (groupStr.CompareTo("dilepton-photon-mass") == 0) { hm->AddHistogram(histClass, "Mass_Dilepton", "", false, 500, 0.0, 5.0, VarManager::kPairMassDau); hm->AddHistogram(histClass, "Mass_Photon", "", false, 500, 0.0, 0.1, VarManager::kMassDau); hm->AddHistogram(histClass, "Mass_Dilepton_Mass_Photon", "", false, 250, 0.0, 5.0, VarManager::kPairMassDau, 250, 0.0, 5.0, VarManager::kMassDau); @@ -2334,7 +2545,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h hm->AddHistogram(histClass, "Eta_Pt_lepton2", "", false, 100, -2.0, 2.0, VarManager::kEta2, 200, 0.0, 20.0, VarManager::kPt); } - if (!groupStr.CompareTo("photon")) { + if (groupStr.CompareTo("photon") == 0) { hm->AddHistogram(histClass, "Pt_Photon", "p_{T} distribution", false, 4500, 0.0, 4.5, VarManager::kPt); hm->AddHistogram(histClass, "Eta", "#eta distribution", false, 500, -5.0, 5.0, VarManager::kEta); hm->AddHistogram(histClass, "Eta_Pt", "", false, 100, -2.0, 2.0, VarManager::kEta, 200, 0.0, 20.0, VarManager::kPt); @@ -2345,7 +2556,7 @@ void o2::aod::dqhistograms::DefineHistograms(HistogramManager* hm, const char* h } // specific group/subgroups for trigger - if (!groupStr.CompareTo("software-trigger")) { + if (groupStr.CompareTo("software-trigger") == 0) { if (subGroupStr.Contains("vtxpp")) { hm->AddHistogram(histClass, "VtxZ", "Vtx Z", false, 60, -15.0, 15.0, VarManager::kVtxZ); hm->AddHistogram(histClass, "VtxX", "Vtx X", false, 200, -0.1, 0.1, VarManager::kVtxX); @@ -2398,7 +2609,7 @@ bool o2::aod::dqhistograms::ValidateJSONHistogram(T hist) LOG(fatal) << "histClass field should be an array of strings, e.g. [class1, class2]"; return false; } - for (auto& v : hist->FindMember("histClass")->value.GetArray()) { + for (const auto& v : hist->FindMember("histClass")->value.GetArray()) { if (!v.IsString()) { LOG(fatal) << "histClass field should be an array of strings, e.g. [class1, class2]"; return false; @@ -2473,11 +2684,10 @@ bool o2::aod::dqhistograms::ValidateJSONHistogram(T hist) if (!hist->HasMember("nBins")) { LOG(fatal) << "Missing nBins field for histogram"; return false; - } else { - if (!hist->FindMember("nBins")->value.IsArray()) { - LOG(fatal) << "nBins field should be an array"; - return false; - } + } + if (!hist->FindMember("nBins")->value.IsArray()) { + LOG(fatal) << "nBins field should be an array"; + return false; } } if (hist->HasMember("axLabels") && !hist->FindMember("axLabels")->value.IsArray()) { @@ -2532,7 +2742,7 @@ bool o2::aod::dqhistograms::ValidateJSONHistogram(T hist) if (!isTHn) { TString varX = hist->FindMember("varX")->value.GetString(); - if (VarManager::fgVarNamesMap.find(varX) == VarManager::fgVarNamesMap.end()) { + if (!VarManager::fgVarNamesMap.contains(varX)) { LOG(fatal) << "Bad varX variable (" << hist->FindMember("varX")->value.GetString() << ") specified for histogram"; return false; } @@ -2554,7 +2764,7 @@ bool o2::aod::dqhistograms::ValidateJSONHistogram(T hist) } } if (isTHn) { - for (auto& v : hist->FindMember("vars")->value.GetArray()) { + for (const auto& v : hist->FindMember("vars")->value.GetArray()) { if (VarManager::fgVarNamesMap.find(v.GetString()) == VarManager::fgVarNamesMap.end()) { LOG(fatal) << "Bad variable in vars (" << v.GetString() << ") specified for histogram"; return false; @@ -2585,7 +2795,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch rapidjson::Document document; rapidjson::ParseResult ok = document.Parse(json); if (!ok) { - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")"; // NOLINT(google-readability-casting) TString str = ""; for (int i = ok.Offset() - 30; i < static_cast(ok.Offset()) + 50; i++) { if ((i >= 0) && (i < static_cast(strlen(json)))) { @@ -2617,7 +2827,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch // create an array of strings to store the different histogram classes std::vector histClasses; - for (auto& v : hist.FindMember("histClass")->value.GetArray()) { + for (const auto& v : hist.FindMember("histClass")->value.GetArray()) { histClasses.push_back(v.GetString()); } const char* title = hist.FindMember("title")->value.GetString(); @@ -2628,7 +2838,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch int* vars = new int[nDimensions]; int iDim = 0; - for (auto& v : hist.FindMember("vars")->value.GetArray()) { + for (const auto& v : hist.FindMember("vars")->value.GetArray()) { LOG(debug) << "iDim " << iDim << ": " << v.GetString(); vars[iDim++] = VarManager::fgVarNamesMap[v.GetString()]; } @@ -2636,45 +2846,46 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch int* nBins = nullptr; double* xmin = nullptr; double* xmax = nullptr; - TArrayD* binLimits = nullptr; + std::vector binLimitsVec; if (isConstantBinning) { nBins = new int[nDimensions]; xmin = new double[nDimensions]; xmax = new double[nDimensions]; - int iDim = 0; - for (auto& v : hist.FindMember("nBins")->value.GetArray()) { + iDim = 0; + for (const auto& v : hist.FindMember("nBins")->value.GetArray()) { nBins[iDim++] = v.GetInt(); LOG(debug) << "nBins " << iDim << ": " << nBins[iDim - 1]; } iDim = 0; - for (auto& v : hist.FindMember("xmin")->value.GetArray()) { + for (const auto& v : hist.FindMember("xmin")->value.GetArray()) { xmin[iDim++] = v.GetDouble(); LOG(debug) << "xmin " << iDim << ": " << xmin[iDim - 1]; } iDim = 0; - for (auto& v : hist.FindMember("xmax")->value.GetArray()) { + for (const auto& v : hist.FindMember("xmax")->value.GetArray()) { xmax[iDim++] = v.GetDouble(); LOG(debug) << "xmax " << iDim << ": " << xmax[iDim - 1]; } } else { - int iDim = 0; - binLimits = new TArrayD[nDimensions]; - for (auto& v : hist.FindMember("binLimits")->value.GetArray()) { - double* lims = new double[v.GetArray().Size()]; + iDim = 0; + binLimitsVec.resize(nDimensions); + for (const auto& v : hist.FindMember("binLimits")->value.GetArray()) { + auto* lims = new double[v.GetArray().Size()]; int iElem = 0; - for (auto& lim : v.GetArray()) { + for (const auto& lim : v.GetArray()) { lims[iElem++] = lim.GetDouble(); } - binLimits[iDim++] = TArrayD(v.GetArray().Size(), lims); + binLimitsVec[iDim++] = TArrayD(v.GetArray().Size(), lims); + delete[] lims; } } - TString* axLabels = nullptr; + std::vector axLabelsVec; if (hist.HasMember("axLabels")) { - axLabels = new TString[hist.FindMember("axLabels")->value.GetArray().Size()]; - int iDim = 0; - for (auto& v : hist.FindMember("axLabels")->value.GetArray()) { - axLabels[iDim++] = v.GetString(); + axLabelsVec.resize(hist.FindMember("axLabels")->value.GetArray().Size()); + iDim = 0; + for (const auto& v : hist.FindMember("axLabels")->value.GetArray()) { + axLabelsVec[iDim++] = v.GetString(); } } @@ -2683,15 +2894,24 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch bool isDouble = (hist.HasMember("isDouble") ? hist.FindMember("isDouble")->value.GetBool() : false); if (isConstantBinning) { - for (auto histClass : histClasses) { - hm->AddHistogram(histClass, histName, title, nDimensions, vars, nBins, xmin, xmax, axLabels, varW, useSparse, isDouble); + for (const auto& histClass : histClasses) { + hm->AddHistogram(histClass, histName, title, nDimensions, vars, nBins, xmin, xmax, + axLabelsVec.empty() ? nullptr : axLabelsVec.data(), varW, useSparse, isDouble); } } else { - for (auto histClass : histClasses) { - hm->AddHistogram(histClass, histName, title, nDimensions, vars, binLimits, axLabels, varW, useSparse, isDouble); + for (const auto& histClass : histClasses) { + hm->AddHistogram(histClass, histName, title, nDimensions, vars, binLimitsVec.data(), + axLabelsVec.empty() ? nullptr : axLabelsVec.data(), varW, useSparse, isDouble); } } + delete[] vars; + if (isConstantBinning) { + delete[] nBins; + delete[] xmin; + delete[] xmax; + } + } else { // TH1, TH2 or TH3 LOG(debug) << "is TH1, TH2 or TH3 "; @@ -2714,7 +2934,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch std::vector xbinsVec; if (hist.HasMember("xbins")) { LOG(debug) << "xbins: "; - for (auto& v : hist.FindMember("xbins")->value.GetArray()) { + for (const auto& v : hist.FindMember("xbins")->value.GetArray()) { xbinsVec.push_back(v.GetDouble()); LOG(debug) << v.GetDouble(); } @@ -2735,7 +2955,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch std::vector ybinsVec; if (hist.HasMember("ybins")) { LOG(debug) << "ybins: "; - for (auto& v : hist.FindMember("ybins")->value.GetArray()) { + for (const auto& v : hist.FindMember("ybins")->value.GetArray()) { ybinsVec.push_back(v.GetDouble()); LOG(debug) << v.GetDouble(); } @@ -2756,7 +2976,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch std::vector zbinsVec; if (hist.HasMember("zbins")) { LOG(debug) << "zbins: "; - for (auto& v : hist.FindMember("zbins")->value.GetArray()) { + for (const auto& v : hist.FindMember("zbins")->value.GetArray()) { zbinsVec.push_back(v.GetDouble()); LOG(debug) << v.GetDouble(); } @@ -2784,7 +3004,7 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch LOG(debug) << "isFillLabelx: " << isFillLabelx; if (isConstantBinning) { - for (auto histClass : histClasses) { + for (const auto& histClass : histClasses) { hm->AddHistogram(histClass, histName, title, isProfile, nXbins, xmin, xmax, VarManager::fgVarNamesMap[varX], nYbins, ymin, ymax, VarManager::fgVarNamesMap[varY], @@ -2793,13 +3013,10 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch VarManager::fgVarNamesMap[varT], VarManager::fgVarNamesMap[varW], isdouble, isFillLabelx); } } else { - int xBinsSize = xbinsVec.size(); - if (xBinsSize != (nXbins + 1)) { + if (static_cast(xbinsVec.size()) != (nXbins + 1)) { LOG(fatal) << "Histogram not properly defined in the JSON file. Wrong x binning for histogram"; continue; } - double* xbins = new double[xbinsVec.size()]; - std::copy(xbinsVec.begin(), xbinsVec.end(), xbins); double* ybins = nullptr; if (isTH2 || isTH3) { @@ -2807,22 +3024,20 @@ void o2::aod::dqhistograms::AddHistogramsFromJSON(HistogramManager* hm, const ch LOG(fatal) << "Histogram not properly defined in the JSON file. Wrong y binning for histogram"; continue; } - ybins = new double[ybinsVec.size()]; - std::copy(ybinsVec.begin(), ybinsVec.end(), ybins); + ybins = ybinsVec.data(); } double* zbins = nullptr; if (isTH3) { - if (static_cast(zbinsVec.size()) != (nZbins + 1)) { + if (static_cast(zbinsVec.size()) != (nZbins + 1)) { LOG(fatal) << "Histogram not properly defined in the JSON file. Wrong z binning for histogram"; continue; } - zbins = new double[zbinsVec.size()]; - std::copy(zbinsVec.begin(), zbinsVec.end(), zbins); + zbins = zbinsVec.data(); } - for (auto histClass : histClasses) { + for (const auto& histClass : histClasses) { hm->AddHistogram(histClass, histName, title, isProfile, - nXbins, xbins, VarManager::fgVarNamesMap[varX], + nXbins, xbinsVec.data(), VarManager::fgVarNamesMap[varX], nYbins, ybins, VarManager::fgVarNamesMap[varY], nZbins, zbins, VarManager::fgVarNamesMap[varZ], xLabels, yLabels, zLabels, diff --git a/PWGDQ/Core/MCProng.cxx b/PWGDQ/Core/MCProng.cxx index 1e50a10fce2..611db4bcd5c 100644 --- a/PWGDQ/Core/MCProng.cxx +++ b/PWGDQ/Core/MCProng.cxx @@ -22,8 +22,6 @@ #include #include -ClassImp(MCProng); - std::map MCProng::fgSourceNames = { {"kNothing", MCProng::kNothing}, {"kPhysicalPrimary", MCProng::kPhysicalPrimary}, @@ -94,19 +92,19 @@ MCProng::MCProng(int n, int m) : fNGenerations(n), } //________________________________________________________________________________________________________________ -MCProng::MCProng(int n, const std::vector pdgs, const std::vector checkBothCharges, const std::vector excludePDG, - const std::vector sourceBits, const std::vector excludeSource, - const std::vector useANDonSourceBitMap, bool checkGenerationsInTime, - const std::vector checkIfPDGInHistory, const std::vector excludePDGInHistory) : fNGenerations(n), - fPDGcodes(pdgs), - fCheckBothCharges(checkBothCharges), - fExcludePDG(excludePDG), - fSourceBits(sourceBits), - fExcludeSource(excludeSource), - fUseANDonSourceBitMap(useANDonSourceBitMap), - fCheckGenerationsInTime(checkGenerationsInTime), - fPDGInHistory(checkIfPDGInHistory), - fExcludePDGInHistory(excludePDGInHistory) {} +MCProng::MCProng(int n, const std::vector& pdgs, const std::vector& checkBothCharges, const std::vector& excludePDG, + const std::vector& sourceBits, const std::vector& excludeSource, + const std::vector& useANDonSourceBitMap, bool checkGenerationsInTime, + const std::vector& checkIfPDGInHistory, const std::vector& excludePDGInHistory) : fNGenerations(n), + fPDGcodes(pdgs), + fCheckBothCharges(checkBothCharges), + fExcludePDG(excludePDG), + fSourceBits(sourceBits), + fExcludeSource(excludeSource), + fUseANDonSourceBitMap(useANDonSourceBitMap), + fCheckGenerationsInTime(checkGenerationsInTime), + fPDGInHistory(checkIfPDGInHistory), + fExcludePDGInHistory(excludePDGInHistory) {} //________________________________________________________________________________________________________________ void MCProng::SetPDGcode(int generation, int code, bool checkBothCharges /*= false*/, bool exclude /*= false*/) diff --git a/PWGDQ/Core/MCProng.h b/PWGDQ/Core/MCProng.h index 4677a995bc7..5db3b8feb1a 100644 --- a/PWGDQ/Core/MCProng.h +++ b/PWGDQ/Core/MCProng.h @@ -89,9 +89,9 @@ class MCProng MCProng(); explicit MCProng(int n); MCProng(int n, int m); - MCProng(int n, std::vector pdgs, std::vector checkBothCharges, std::vector excludePDG, - std::vector sourceBits, std::vector excludeSource, std::vector useANDonSourceBitMap, - bool checkGenerationsInTime = false, std::vector checkIfPDGInHistory = {}, std::vector excludePDGInHistory = {}); + MCProng(int n, const std::vector& pdgs, const std::vector& checkBothCharges, const std::vector& excludePDG, + const std::vector& sourceBits, const std::vector& excludeSource, const std::vector& useANDonSourceBitMap, + bool checkGenerationsInTime = false, const std::vector& checkIfPDGInHistory = {}, const std::vector& excludePDGInHistory = {}); MCProng(const MCProng& c) = default; virtual ~MCProng() = default; @@ -114,7 +114,5 @@ class MCProng bool fCheckGenerationsInTime; std::vector fPDGInHistory; std::vector fExcludePDGInHistory; - - ClassDef(MCProng, 2); }; #endif // PWGDQ_CORE_MCPRONG_H_ diff --git a/PWGDQ/Core/MCSignal.cxx b/PWGDQ/Core/MCSignal.cxx index cb05a02b02c..2068d426a70 100644 --- a/PWGDQ/Core/MCSignal.cxx +++ b/PWGDQ/Core/MCSignal.cxx @@ -19,13 +19,12 @@ #include #include +#include #include using std::cout; using std::endl; -ClassImp(MCSignal); - //________________________________________________________________________________________________ MCSignal::MCSignal() : TNamed("", ""), fProngs({}), @@ -54,28 +53,28 @@ MCSignal::MCSignal(int nProngs, const char* name /*= ""*/, const char* title /*= } //________________________________________________________________________________________________ -MCSignal::MCSignal(const char* name, const char* title, std::vector prongs, std::vector commonAncestors, bool excludeCommonAncestor) : TNamed(name, title), - fProngs(prongs), - fNProngs(prongs.size()), - fCommonAncestorIdxs(commonAncestors), - fExcludeCommonAncestor(excludeCommonAncestor), - fDecayChannelIsExclusive(false), - fDecayChannelIsNotExclusive(false), - fNAncestorDirectProngs(0), - fTempAncestorLabel(-1) +MCSignal::MCSignal(const char* name, const char* title, const std::vector& prongs, std::vector commonAncestors, bool excludeCommonAncestor) : TNamed(name, title), + fProngs(prongs), + fNProngs(prongs.size()), + fCommonAncestorIdxs(std::move(commonAncestors)), + fExcludeCommonAncestor(excludeCommonAncestor), + fDecayChannelIsExclusive(false), + fDecayChannelIsNotExclusive(false), + fNAncestorDirectProngs(0), + fTempAncestorLabel(-1) { } //________________________________________________________________________________________________ void MCSignal::SetProngs(std::vector prongs, std::vector commonAncestors) { - fProngs = prongs; + fProngs = std::move(prongs); fNProngs = fProngs.size(); - fCommonAncestorIdxs = commonAncestors; + fCommonAncestorIdxs = std::move(commonAncestors); } //________________________________________________________________________________________________ -void MCSignal::AddProng(MCProng prong, int8_t commonAncestor) +void MCSignal::AddProng(const MCProng& prong, int8_t commonAncestor) { if (fProngs.size() < fNProngs) { fProngs.push_back(prong); diff --git a/PWGDQ/Core/MCSignal.h b/PWGDQ/Core/MCSignal.h index f13f9657fbd..89f98c27d6f 100644 --- a/PWGDQ/Core/MCSignal.h +++ b/PWGDQ/Core/MCSignal.h @@ -68,12 +68,12 @@ class MCSignal : public TNamed public: MCSignal(); MCSignal(int nProngs, const char* name = "", const char* title = ""); // NOLINT - MCSignal(const char* name, const char* title, std::vector prongs, std::vector commonAncestors, bool excludeCommonAncestor = false); + MCSignal(const char* name, const char* title, const std::vector& prongs, std::vector commonAncestors, bool excludeCommonAncestor = false); MCSignal(const MCSignal& c) = default; ~MCSignal() override = default; void SetProngs(std::vector prongs, std::vector commonAncestors); - void AddProng(MCProng prong, int8_t commonAncestor = -1); + void AddProng(const MCProng& prong, int8_t commonAncestor = -1); void SetDecayChannelIsExclusive(int nProngs, bool option = true) { fDecayChannelIsExclusive = option; @@ -331,25 +331,26 @@ bool MCSignal::CheckProng(int i, bool checkSources, const T& track) break; } } - } /*else { // check generation in time - if (!currentMCParticle.has_daughters()) { - return false; - } - const auto& daughtersSlice = currentMCParticle.template daughters_as

(); - for (auto& d : daughtersSlice) { - if (!fProngs[i].fExcludePDGInHistory[k] && fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { - pdgInHistory.emplace_back(d.pdgCode()); - break; - } - if (fProngs[i].fExcludePDGInHistory[k] && !fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { - return false; - } - ith++; - if (ith > 10) { // need error message. Given pdg code was not found within 10 generations of the particles decay chain. - break; - } - } - }*/ + } + // else { // check generation in time + // if (!currentMCParticle.has_daughters()) { + // return false; + // } + // const auto& daughtersSlice = currentMCParticle.template daughters_as

(); + // for (auto& d : daughtersSlice) { + // if (!fProngs[i].fExcludePDGInHistory[k] && fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { + // pdgInHistory.emplace_back(d.pdgCode()); + // break; + // } + // if (fProngs[i].fExcludePDGInHistory[k] && !fProngs[i].ComparePDG(d.pdgCode(), fProngs[i].fPDGInHistory[k], true, fProngs[i].fExcludePDGInHistory[k])) { + // return false; + // } + // ith++; + // if (ith > 10) { // need error message. Given pdg code was not found within 10 generations of the particles decay chain. + // break; + // } + // } + // } } if (pdgInHistory.size() != nIncludedPDG) { // vector has as many entries as mothers (daughters) defined for prong return false; diff --git a/PWGDQ/Core/MCSignalLibrary.cxx b/PWGDQ/Core/MCSignalLibrary.cxx index 0bc89619d35..918addd1b29 100644 --- a/PWGDQ/Core/MCSignalLibrary.cxx +++ b/PWGDQ/Core/MCSignalLibrary.cxx @@ -37,405 +37,405 @@ using namespace o2::constants::physics; MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) { std::string nameStr = name; - MCSignal* signal; + MCSignal* signal = nullptr; // 1-prong signals - if (!nameStr.compare("alicePrimary")) { + if (nameStr == "alicePrimary") { MCProng prong(1); // 1-generation prong prong.SetSourceBit(0, MCProng::kPhysicalPrimary); // set source to be ALICE primary particles signal = new MCSignal(1, name, "ALICE primaries"); // define a signal with one prong signal->AddProng(prong); // add the previously defined prong to the signal return signal; } - if (!nameStr.compare("electron")) { + if (nameStr == "electron") { MCProng prong(1, {11}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor signal = new MCSignal(name, "Inclusive electrons", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("electronPrimary")) { + if (nameStr == "electronPrimary") { MCProng prong(1, {11}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor prong.SetSourceBit(0, MCProng::kPhysicalPrimary); // set source to be ALICE primary particles signal = new MCSignal(name, "Primary electrons", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("pionPrimary")) { + if (nameStr == "pionPrimary") { MCProng prong(1, {211}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor prong.SetSourceBit(0, MCProng::kPhysicalPrimary); // set source to be ALICE primary particles signal = new MCSignal(name, "Primary pions", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("pionPrimaryFromHc")) { + if (nameStr == "pionPrimaryFromHc") { MCProng prong(2, {211, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); signal = new MCSignal(name, "Primary pions from open charmed hadron decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("pionPrimaryFromHb")) { + if (nameStr == "pionPrimaryFromHb") { MCProng prong(2, {211, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); signal = new MCSignal(name, "Primary pions from open beauty hadron decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("photon")) { + if (nameStr == "photon") { MCProng prong(1, {22}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor signal = new MCSignal(name, "Photon", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("muonPrimary")) { + if (nameStr == "muonPrimary") { MCProng prong(1, {13}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor prong.SetSourceBit(0, MCProng::kPhysicalPrimary); // set source to be ALICE primary particles signal = new MCSignal(name, "Primary Muons", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("kaonFromPhi")) { + if (nameStr == "kaonFromPhi") { MCProng prong(2, {321, 333}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); // define 2-generation prong using the full constructor signal = new MCSignal(name, "Kaons from phi-mesons", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("kaonPrimary")) { + if (nameStr == "kaonPrimary") { MCProng prong(1, {321}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor prong.SetSourceBit(0, MCProng::kPhysicalPrimary); // set source to be ALICE primary particles signal = new MCSignal(name, "Primary Kaons", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("Lambda0Baryon")) { + if (nameStr == "Lambda0Baryon") { MCProng prong(1, {3122}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Lambda0 Baryon", {prong}, {-1}); return signal; } - if (!nameStr.compare("SigmaPlusBaryon")) { + if (nameStr == "SigmaPlusBaryon") { MCProng prong(1, {3222}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "SigmaPlus Baryon", {prong}, {-1}); return signal; } - if (!nameStr.compare("proton")) { + if (nameStr == "proton") { MCProng prong(1, {2212}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "proton", {prong}, {-1}); return signal; } - if (!nameStr.compare("protonPrimary")) { + if (nameStr == "protonPrimary") { MCProng prong(1, {2212}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor prong.SetSourceBit(0, MCProng::kPhysicalPrimary); // set source to be ALICE primary particles signal = new MCSignal(name, "Primary Proton", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("protonFromTransport")) { + if (nameStr == "protonFromTransport") { MCProng prong(1, {2212}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kProducedInTransport); signal = new MCSignal(name, "ProtonFromTransport", {prong}, {-1}); return signal; } - if (!nameStr.compare("protonFromLambda0")) { + if (nameStr == "protonFromLambda0") { MCProng prong(2, {2212, 3122}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Proton from Lambda0 decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("protonFromSigmaPlus")) { + if (nameStr == "protonFromSigmaPlus") { MCProng prong(2, {2212, 3222}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Proton from Sigma+ decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("phiMeson")) { + if (nameStr == "phiMeson") { MCProng prong(1, {333}, {true}, {false}, {0}, {0}, {false}); // define 1-generation prong using the full constructor signal = new MCSignal(name, "phi meson", {prong}, {-1}); // define the signal using the full constructor return signal; } - if (!nameStr.compare("muon")) { + if (nameStr == "muon") { MCProng prong(1, {13}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive muons", {prong}, {-1}); return signal; } - if (!nameStr.compare("electronNOTfromTransport")) { + if (nameStr == "electronNOTfromTransport") { MCProng prong(1); prong.SetPDGcode(0, 11, true); prong.SetSourceBit(0, MCProng::kProducedInTransport, true); // exclude particles produces in transport signal = new MCSignal(name, "Electrons which are not produced during transport in detector", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromNonpromptJpsi")) { + if (nameStr == "eFromNonpromptJpsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); signal = new MCSignal(name, "Electrons from non-prompt jpsi decays with beauty in decay chain", {prong}, {-1}); return signal; } - if (!nameStr.compare("ePrimaryFromPromptJpsi")) { + if (nameStr == "ePrimaryFromPromptJpsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from prompt jpsi decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("ePrimaryFromNonpromptJpsi")) { + if (nameStr == "ePrimaryFromNonpromptJpsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from non-prompt jpsi decays with beauty in decay chain", {prong}, {-1}); return signal; } - if (!nameStr.compare("Jpsi")) { + if (nameStr == "Jpsi") { MCProng prong(1, {443}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive jpsi", {prong}, {-1}); return signal; } - if (!nameStr.compare("Helium3")) { + if (nameStr == "Helium3") { MCProng prong(1, {1000020030}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Helium3", {prong}, {-1}); return signal; } - if (!nameStr.compare("Helium3Primary")) { + if (nameStr == "Helium3Primary") { MCProng prong(1, {1000020030}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Helium3Primary", {prong}, {-1}); return signal; } - if (!nameStr.compare("Helium3FromTransport")) { + if (nameStr == "Helium3FromTransport") { MCProng prong(1, {1000020030}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kProducedInTransport); signal = new MCSignal(name, "Helium3FromTransport", {prong}, {-1}); return signal; } - if (!nameStr.compare("promptJpsi")) { + if (nameStr == "promptJpsi") { MCProng prong(1, {443}, {true}, {false}, {0}, {0}, {false}, false, {503}, {true}); signal = new MCSignal(name, "Prompt jpsi (not from beauty)", {prong}, {-1}); return signal; } - if (!nameStr.compare("nonPromptJpsi")) { + if (nameStr == "nonPromptJpsi") { MCProng prong(1, {443}, {true}, {false}, {0}, {0}, {false}, false, {503}, {false}); signal = new MCSignal(name, "Non-prompt jpsi (from beauty)", {prong}, {-1}); return signal; } - if (!nameStr.compare("nonPromptJpsiFromBeauty")) { + if (nameStr == "nonPromptJpsiFromBeauty") { MCProng prong(2, {503, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); signal = new MCSignal(name, "Non-prompt jpsi directly from beauty", {prong}, {-1}); return signal; } - if (!nameStr.compare("nonPromptJpsiNotDirectlyFromBeauty")) { + if (nameStr == "nonPromptJpsiNotDirectlyFromBeauty") { MCProng prong(2, {443, 503}, {true, true}, {false, true}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); signal = new MCSignal(name, "Non-prompt jpsi from other but with beauty in decay chain", {prong}, {-1}); return signal; } - if (!nameStr.compare("AnythingDecayToJpsi")) { + if (nameStr == "AnythingDecayToJpsi") { MCProng prong(2, {MCProng::kPDGCodeNotAssigned, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); signal = new MCSignal(name, "Decay of anything into J/psi", {prong}, {-1}); return signal; } - if (!nameStr.compare("eeFromNonpromptPsi2S")) { + if (nameStr == "eeFromNonpromptPsi2S") { MCProng prong(2, {11, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); signal = new MCSignal(name, "ee pairs from non-prompt psi2s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromPromptPsi2S")) { + if (nameStr == "eeFromPromptPsi2S") { MCProng prong(2, {11, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); signal = new MCSignal(name, "ee pairs from prompt psi2s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eFromNonpromptPsi2S")) { + if (nameStr == "eFromNonpromptPsi2S") { MCProng prong(2, {11, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); signal = new MCSignal(name, "Electrons from beauty psi2s decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPromptPsi2S")) { + if (nameStr == "eFromPromptPsi2S") { MCProng prong(2, {11, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); signal = new MCSignal(name, "Electrons from prompt psi2s decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("Psi2S")) { + if (nameStr == "Psi2S") { MCProng prong(1, {100443}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive psi2s", {prong}, {-1}); return signal; } - if (!nameStr.compare("nonPromptPsi2S")) { + if (nameStr == "nonPromptPsi2S") { MCProng prong(1, {100443}, {true}, {false}, {0}, {0}, {false}, false, {503}, {false}); signal = new MCSignal(name, "Non-prompt psi2s", {prong}, {-1}); return signal; } - if (!nameStr.compare("promptPsi2S")) { + if (nameStr == "promptPsi2S") { MCProng prong(1, {100443}, {true}, {false}, {0}, {0}, {false}, false, {503}, {true}); signal = new MCSignal(name, "Prompt psi2s (not from beauty)", {prong}, {-1}); return signal; } - if (!nameStr.compare("Chic0")) { + if (nameStr == "Chic0") { MCProng prong(1, {10441}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive Chic0", {prong}, {-1}); return signal; } - if (!nameStr.compare("Chic1")) { + if (nameStr == "Chic1") { MCProng prong(1, {20443}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive Chic1", {prong}, {-1}); return signal; } - if (!nameStr.compare("Chic2")) { + if (nameStr == "Chic2") { MCProng prong(1, {445}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive Chic2", {prong}, {-1}); return signal; } - if (!nameStr.compare("Chic012")) { + if (nameStr == "Chic012") { MCProng prong(1, {904}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive Chic0, Chic1 and Chic2", {prong}, {-1}); return signal; } - if (!nameStr.compare("Upsilon1S")) { + if (nameStr == "Upsilon1S") { MCProng prong(1, {553}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive Upsilon1S", {prong}, {-1}); return signal; } - if (!nameStr.compare("Upsilon2S")) { + if (nameStr == "Upsilon2S") { MCProng prong(1, {100553}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive Upsilon2S", {prong}, {-1}); return signal; } - if (!nameStr.compare("Upsilon3S")) { + if (nameStr == "Upsilon3S") { MCProng prong(1, {200553}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive Upsilon3S", {prong}, {-1}); return signal; } - if (!nameStr.compare("allBeautyHadrons")) { + if (nameStr == "allBeautyHadrons") { MCProng prong(1, {503}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "All beauty hadrons", {prong}, {-1}); return signal; } - if (!nameStr.compare("allBeautyHadronsFS")) { + if (nameStr == "allBeautyHadronsFS") { MCProng prong(1, {503}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kHEPMCFinalState); signal = new MCSignal(name, "All beauty hadrons", {prong}, {-1}); return signal; } - if (!nameStr.compare("allOpenBeautyHadrons")) { + if (nameStr == "allOpenBeautyHadrons") { MCProng prong(1, {502}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "All open beauty hadrons", {prong}, {-1}); return signal; } - if (!nameStr.compare("allOpenBeautyHadronsFS")) { + if (nameStr == "allOpenBeautyHadronsFS") { MCProng prong(1, {502}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kHEPMCFinalState); signal = new MCSignal(name, "All open beauty hadrons", {prong}, {-1}); return signal; } - if (!nameStr.compare("Bc")) { + if (nameStr == "Bc") { MCProng prong(1, {541}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Bc", {prong}, {-1}); return signal; } - if (!nameStr.compare("mumuFromJpsiFromBc")) { + if (nameStr == "mumuFromJpsiFromBc") { MCProng prong(3, {13, 443, 541}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Muon pair from jpsi from Bc decays", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("muFromBc")) { + if (nameStr == "muFromBc") { MCProng prong(2, {13, 541}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Muon from Bc decays", {prong}, {1}); return signal; } - if (!nameStr.compare("mumumuFromBc")) { + if (nameStr == "mumumuFromBc") { MCProng prongMuFromJpsi(3, {13, 443, 541}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongMuFromBc(2, {13, 541}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Trimuon from Bc decays", {prongMuFromJpsi, prongMuFromJpsi, prongMuFromBc}, {2, 2, 1}); return signal; } - if (!nameStr.compare("everythingFromBeauty")) { + if (nameStr == "everythingFromBeauty") { MCProng prong(2, {0, 503}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Everything from beauty", {prong}, {-1}); return signal; } - if (!nameStr.compare("everythingFromBeautyFS")) { + if (nameStr == "everythingFromBeautyFS") { MCProng prong(2, {0, 503}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(1, MCProng::kHEPMCFinalState); signal = new MCSignal(name, "Everything from beauty", {prong}, {-1}); return signal; } - if (!nameStr.compare("everythingFromEverythingFromBeauty")) { + if (nameStr == "everythingFromEverythingFromBeauty") { MCProng prong(3, {0, 0, 503}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Everything from everything from beauty", {prong}, {-1}); return signal; } - if (!nameStr.compare("everythingFromEverythingFromBeautyFS")) { + if (nameStr == "everythingFromEverythingFromBeautyFS") { MCProng prong(3, {0, 0, 503}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(2, MCProng::kHEPMCFinalState); signal = new MCSignal(name, "Everything from everything from beauty", {prong}, {-1}); return signal; } - if (!nameStr.compare("allCharmHadrons")) { + if (nameStr == "allCharmHadrons") { MCProng prong(1, {403}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "All charm hadrons", {prong}, {-1}); return signal; } - if (!nameStr.compare("allOpenCharmHadrons")) { + if (nameStr == "allOpenCharmHadrons") { MCProng prong(1, {402}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "All open charm hadrons", {prong}, {-1}); return signal; } - if (!nameStr.compare("allCharmFromBeauty")) { + if (nameStr == "allCharmFromBeauty") { MCProng prong(2, {403, 503}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "All charm hadrons from beauty", {prong}, {-1}); return signal; } - if (!nameStr.compare("allPromptCharm")) { + if (nameStr == "allPromptCharm") { MCProng prong(2, {403, 503}, {true, true}, {false, true}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "All prompt charm hadrons (not from beauty)", {prong}, {-1}); return signal; } - if (!nameStr.compare("Pi0DecayToe")) { + if (nameStr == "Pi0DecayToe") { MCProng prong(2, {111, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Pi0 decays into an electron", {prong}, {-1}); return signal; } - if (!nameStr.compare("Pi0DecayTog")) { + if (nameStr == "Pi0DecayTog") { MCProng prong(2, {111, 22}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Pi0 decays into an gamma", {prong}, {1}); return signal; } - if (!nameStr.compare("Pi0DecayTogg")) { + if (nameStr == "Pi0DecayTogg") { MCProng prong(2, {111, 22}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Pi0 decays into an gamma, gamma", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("PromptPi0DecayToe")) { + if (nameStr == "PromptPi0DecayToe") { MCProng prong(2, {111, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, true, {403, 503}, {true, true}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Pi0 decays into an electron", {prong}, {-1}); return signal; } - if (!nameStr.compare("Pi0")) { + if (nameStr == "Pi0") { MCProng prong(1, {111}, {true}, {false}, {0}, {0}, {false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles signal = new MCSignal(name, "Pi0", {prong}, {-1}); return signal; } - if (!nameStr.compare("LMeeLFQ")) { + if (nameStr == "LMeeLFQ") { MCProng prong(1, {900}, {true}, {false}, {0}, {0}, {false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles signal = new MCSignal(name, "light flavor mesons + quarkonia", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi,jpsi,psi2s return signal; } - if (!nameStr.compare("LMeeLF")) { + if (nameStr == "LMeeLF") { MCProng prong(1, {901}, {true}, {false}, {0}, {0}, {false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles signal = new MCSignal(name, "ligh flavor mesons", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi return signal; } - if (!nameStr.compare("PromptJpsiDecayToe")) { + if (nameStr == "PromptJpsiDecayToe") { MCProng prong(2, {443, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, true, {503}, {true}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Prompt jpsi (not from beauty) decay to electron", {prong}, {-1}); return signal; } - if (!nameStr.compare("electronFromDs")) { + if (nameStr == "electronFromDs") { MCProng prong(2, {11, 431}, {true, true}, {false, false}, {0, 0}, {0, 0}, {true, true}); signal = new MCSignal(name, "Electrons from Ds decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("dsMeson")) { + if (nameStr == "dsMeson") { MCProng prong(1, {431}, {true}, {false}, {0}, {0}, {true}); signal = new MCSignal(name, "Ds mesons", {prong}, {-1}); return signal; } - if (!nameStr.compare("electronFromPC")) { + if (nameStr == "electronFromPC") { MCProng prong(2, {11, 22}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "electron from a photon conversion", {prong}, {-1}); return signal; } - if (!nameStr.compare("PowhegDYMuon1")) { + if (nameStr == "PowhegDYMuon1") { MCProng prong(1, {13}, {true}, {false}, {0}, {0}, {false}); // selecting muons prong.SetSourceBit(0, MCProng::kIsPowhegDYMuon); // set source to be Muon from POWHEG signal = new MCSignal(name, "POWHEG Muon singles", {prong}, {-1}); // define a signal with 1-prong @@ -443,38 +443,62 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // 2-prong signals - if (!nameStr.compare("dielectron")) { + if (nameStr == "dielectron") { MCProng prong(1, {11}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Electron pair", {prong, prong}, {-1, -1}); return signal; } - if (!nameStr.compare("dimuon")) { + if (nameStr == "dimuon") { MCProng prong(1, {13}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Muon pair", {prong, prong}, {-1, -1}); return signal; } - if (!nameStr.compare("electronMuonPair")) { + if (nameStr == "electronMuonPair") { MCProng electron(1, {11}, {true}, {false}, {0}, {0}, {false}); MCProng muon(1, {13}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Electron-muon pair", {electron, muon}, {-1, -1}); return signal; } - if (!nameStr.compare("dielectronFromPC")) { + if (nameStr == "emuFromOpenHFhadron") { + MCProng electron(2, {11, 902}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + electron.SetSourceBit(0, MCProng::kPhysicalPrimary); + MCProng muon(2, {13, 902}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + muon.SetSourceBit(0, MCProng::kPhysicalPrimary); + signal = new MCSignal(name, "e and mu each from an open charm or beauty hadron decay", {electron, muon}, {-1, -1}); + return signal; + } + if (nameStr == "emuFromOpenCharmHadron") { + MCProng electron(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + electron.SetSourceBit(0, MCProng::kPhysicalPrimary); + MCProng muon(2, {13, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + muon.SetSourceBit(0, MCProng::kPhysicalPrimary); + signal = new MCSignal(name, "e and mu each from an open charm hadron decay", {electron, muon}, {-1, -1}); + return signal; + } + if (nameStr == "emuFromOpenBeautyHadron") { + MCProng electron(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + electron.SetSourceBit(0, MCProng::kPhysicalPrimary); + MCProng muon(2, {13, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + muon.SetSourceBit(0, MCProng::kPhysicalPrimary); + signal = new MCSignal(name, "e and mu each from an open beauty hadron decay", {electron, muon}, {-1, -1}); + return signal; + } + if (nameStr == "dielectronFromPC") { MCProng prong(2, {11, 22}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "dielectron from a photon conversion", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("dielectronFromAllPC")) { + if (nameStr == "dielectronFromAllPC") { MCProng prong(2, {11, 22}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "dielectron from a photon conversion", {prong, prong}, {-1, -1}); return signal; } - if (!nameStr.compare("dielectronPCPi0")) { + if (nameStr == "dielectronPCPi0") { MCProng prong(3, {11, 22, 111}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "dielectron from a photon conversion from a pi0", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("PowhegDYMuon2")) { + if (nameStr == "PowhegDYMuon2") { MCProng prong(1, {13}, {true}, {false}, {0}, {0}, {false}); // selecting muons prong.SetSourceBit(0, MCProng::kIsPowhegDYMuon); // set source to be Muon from POWHEG signal = new MCSignal(name, "POWHEG Muon pair", {prong, prong}, {-1, -1}); // define a signal with 2-prong @@ -483,376 +507,400 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) // LMEE single signals // electron signals with mother X: e from mother X - if (!nameStr.compare("eFromAnything")) { + if (nameStr == "eFromAnything") { MCProng prong(2, {11, MCProng::kPDGCodeNotAssigned}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from any mother", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPhoton")) { + if (nameStr == "eFromPhoton") { MCProng prong(2, {11, 22}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from photon conversion", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPi0")) { + if (nameStr == "eFromPi0") { MCProng prong(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from pi0 decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("ePrimaryFromPromptPi0")) { + if (nameStr == "ePrimaryFromPi0") { + MCProng prong(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + prong.SetSourceBit(0, MCProng::kPhysicalPrimary); + signal = new MCSignal(name, "Electrons from primary pi0 decays", {prong}, {-1}); + return signal; + } + if (nameStr == "eSecondaryFromPi0") { + MCProng prong(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + prong.SetSourceBit(0, MCProng::kPhysicalPrimary, true); + signal = new MCSignal(name, "Electrons from secondary pi0 decays", {prong}, {-1}); + return signal; + } + if (nameStr == "ePrimaryFromPromptPi0") { MCProng prong(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502, 402}, {true, true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from prompt pi0 decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromEta")) { + if (nameStr == "eFromEta") { MCProng prong(2, {11, 221}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from eta decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromEtaPrime")) { + if (nameStr == "ePrimaryFromEta") { + MCProng prong(2, {11, 221}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + prong.SetSourceBit(0, MCProng::kPhysicalPrimary); + signal = new MCSignal(name, "Electrons from primary eta decays", {prong}, {-1}); + return signal; + } + if (nameStr == "eSecondaryFromEta") { + MCProng prong(2, {11, 221}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + prong.SetSourceBit(0, MCProng::kPhysicalPrimary, true); + signal = new MCSignal(name, "Electrons from secondary eta decays", {prong}, {-1}); + return signal; + } + if (nameStr == "eFromEtaPrime") { MCProng prong(2, {11, 331}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from eta' decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromRho")) { + if (nameStr == "eFromRho") { MCProng prong(2, {11, 113}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from rho decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromOmega")) { + if (nameStr == "eFromOmega") { MCProng prong(2, {11, 223}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from omega decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPhi")) { + if (nameStr == "eFromPhi") { MCProng prong(2, {11, 333}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from phi decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromJpsi")) { + if (nameStr == "eFromJpsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from jpsi decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("anythingFromJpsi")) { + if (nameStr == "anythingFromJpsi") { MCProng prong(2, {MCProng::kPDGCodeNotAssigned, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Anything from jpsi decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPromptJpsi")) { + if (nameStr == "eFromPromptJpsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); signal = new MCSignal(name, "Electrons from jpsi decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPsi2S")) { + if (nameStr == "eFromPsi2S") { MCProng prong(2, {11, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from psi2s decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromLMeeLF")) { + if (nameStr == "eFromLMeeLF") { MCProng prong(2, {11, 901}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from LF meson decays", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi mesons return signal; } - if (!nameStr.compare("ePrimaryFromLMeeLF")) { + if (nameStr == "ePrimaryFromLMeeLF") { MCProng prong(2, {11, 901}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles signal = new MCSignal(name, "Electrons from LF meson decays", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi mesons return signal; } - if (!nameStr.compare("eFromLMeeLFQ")) { + if (nameStr == "eFromLMeeLFQ") { MCProng prong(2, {11, 900}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(false); // set direction to check generation in time (true) or back in time (false) signal = new MCSignal(name, "Electrons from LF meson + quarkonia decays", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi,jpsi,psi2s mesons return signal; } - if (!nameStr.compare("ePrimaryFromLMeeLFQ")) { + if (nameStr == "ePrimaryFromLMeeLFQ") { MCProng prong(2, {11, 900}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles prong.SetSignalInTime(false); // set direction to check generation in time (true) or back in time (false) signal = new MCSignal(name, "Electrons from LF meson + quarkonia decays", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi,jpsi,psi2s mesons return signal; } - if (!nameStr.compare("eFromHc")) { + if (nameStr == "eFromHc") { MCProng prong(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open charmed hadron decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromAnyHc")) { + if (nameStr == "eFromAnyHc") { MCProng prong(1, {11}, {true}, {false}, {0}, {0}, {false}, false, {402}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); signal = new MCSignal(name, "Electrons from any open charm hadron decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromD0")) { + if (nameStr == "eFromD0") { MCProng prong(2, {11, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from D0 decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromChargedD")) { + if (nameStr == "eFromChargedD") { MCProng prong(2, {11, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from D+/- decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromDs")) { + if (nameStr == "eFromDs") { MCProng prong(2, {11, Pdg::kDS}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Ds +/- decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromLambdaC")) { + if (nameStr == "eFromLambdaC") { MCProng prong(2, {11, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Lambda_c decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromXiC0")) { + if (nameStr == "eFromXiC0") { MCProng prong(2, {11, Pdg::kXiC0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Xi_c_0 decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromXiCPlus")) { + if (nameStr == "eFromXiCPlus") { MCProng prong(2, {11, Pdg::kXiCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Xi_c_+ decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromXiCPlusPlus")) { + if (nameStr == "eFromXiCPlusPlus") { MCProng prong(2, {11, Pdg::kXiCCPlusPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Xi_c_++ decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromHb")) { + if (nameStr == "eFromHb") { MCProng prong(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open beauty hadron decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromAnyHb")) { + if (nameStr == "eFromAnyHb") { MCProng prong(1, {11}, {true}, {false}, {0}, {0}, {false}, false, {502}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); signal = new MCSignal(name, "Electrons from any open beauty hadron decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromHbc")) { + if (nameStr == "eFromHbc") { MCProng prong(2, {11, 902}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open charm or beauty hadron decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromMc")) { + if (nameStr == "eFromMc") { MCProng prong(2, {11, 401}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open charmed meson decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromMb")) { + if (nameStr == "eFromMb") { MCProng prong(2, {11, 501}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open beauty meson decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromBc")) { + if (nameStr == "eFromBc") { MCProng prong(2, {11, 4001}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open charmed baryon decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromBb")) { + if (nameStr == "eFromBb") { MCProng prong(2, {11, 5001}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open beauty baryon decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPromptHc")) { + if (nameStr == "eFromPromptHc") { MCProng prong(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open charmed hadron decays without beauty in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromHbtoHc")) { + if (nameStr == "eFromHbtoHc") { MCProng prong(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from open charmed hadron decays with b hadron in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromPromptLM")) { + if (nameStr == "eFromPromptLM") { MCProng prong(2, {11, 101}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502, 402}, {true, true}); signal = new MCSignal(name, "Electrons from light mesons without B/D in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromHbtoLM")) { + if (nameStr == "eFromHbtoLM") { MCProng prong(2, {11, 101}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {false}); signal = new MCSignal(name, "Electrons from light mesons with B hadron in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromHctoLM")) { + if (nameStr == "eFromHctoLM") { MCProng prong(2, {11, 101, 402}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {502}, {true}); signal = new MCSignal(name, "Electrons from light mesons from D hadron decays and no B in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromUpsilon1S")) { + if (nameStr == "eFromUpsilon1S") { MCProng prong(2, {11, 553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from Upsilon1S decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromUpsilon2S")) { + if (nameStr == "eFromUpsilon2S") { MCProng prong(2, {11, 100553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from Upsilon2S decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromUpsilon3S")) { + if (nameStr == "eFromUpsilon3S") { MCProng prong(2, {11, 200553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electrons from Upsilon3S decays", {prong}, {-1}); return signal; } // muon signals with mother X: mu from mother X - if (!nameStr.compare("muFromJpsi")) { + if (nameStr == "muFromJpsi") { MCProng prong(2, {13, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "muons from jpsi decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromPsi2S")) { + if (nameStr == "muFromPsi2S") { MCProng prong(2, {13, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "muons from psi2s decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromHb")) { + if (nameStr == "muFromHb") { MCProng prong(2, {13, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "muons from b->mu", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromPromptHc")) { + if (nameStr == "muFromPromptHc") { MCProng prong(2, {13, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); signal = new MCSignal(name, "muons from c->mu, without beauty in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromHbtoHc")) { + if (nameStr == "muFromHbtoHc") { MCProng prong(3, {13, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "muons from b->c->mu", {prong}, {-1}); return signal; } - if (!nameStr.compare("secondaryMuon")) { + if (nameStr == "secondaryMuon") { MCProng prong(1, {13}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kProducedInTransport); signal = new MCSignal(name, "muons produced during transport in detector", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromPromptLM")) { + if (nameStr == "muFromPromptLM") { MCProng prong(2, {13, 101}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502, 402}, {true, true}); signal = new MCSignal(name, "muons from light mesons without B/D in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromHbtoLM")) { + if (nameStr == "muFromHbtoLM") { MCProng prong(2, {13, 101}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {false}); signal = new MCSignal(name, "muons from light mesons with B hadron in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromHctoLM")) { + if (nameStr == "muFromHctoLM") { MCProng prong(2, {13, 101, 402}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {502}, {true}); signal = new MCSignal(name, "muons from light mesons from D hadron decays and no B in decay history", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromUpsilon1S")) { + if (nameStr == "muFromUpsilon1S") { MCProng prong(2, {13, 553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "muons from Upsilon1S decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromUpsilon2S")) { + if (nameStr == "muFromUpsilon2S") { MCProng prong(2, {13, 100553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "muons from Upsilon2S decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("muFromUpsilon3S")) { + if (nameStr == "muFromUpsilon3S") { MCProng prong(2, {13, 200553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "muons from Upsilon3S decays", {prong}, {-1}); return signal; } // Decay signal: Mother to electron: X -> e - if (!nameStr.compare("AnythingToE")) { + if (nameStr == "AnythingToE") { MCProng prong(2, {MCProng::kPDGCodeNotAssigned, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Decay of anything into e", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi,jpsi,psi2s mesons return signal; } - if (!nameStr.compare("LFQdecayToE")) { + if (nameStr == "LFQdecayToE") { MCProng prong(2, {900, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "LF meson + quarkonia decays into e", {prong}, {-1}); // pi0,eta,eta',rho,omega,phi,jpsi,psi2s mesons return signal; } - if (!nameStr.compare("HcToE")) { + if (nameStr == "HcToE") { MCProng prong(2, {402, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open charmed hadron decays into e", {prong}, {-1}); return signal; } - if (!nameStr.compare("HbToE")) { + if (nameStr == "HbToE") { MCProng prong(2, {502, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open beauty hadron decays into e", {prong}, {-1}); return signal; } - if (!nameStr.compare("McToE")) { + if (nameStr == "McToE") { MCProng prong(2, {401, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open charmed meson decays into e", {prong}, {-1}); return signal; } - if (!nameStr.compare("MbToE")) { + if (nameStr == "MbToE") { MCProng prong(2, {501, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open beauty meson decays into e", {prong}, {-1}); return signal; } - if (!nameStr.compare("BcToE")) { + if (nameStr == "BcToE") { MCProng prong(2, {4001, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open charmed baryon decays into e", {prong}, {-1}); return signal; } - if (!nameStr.compare("BbToE")) { + if (nameStr == "BbToE") { MCProng prong(2, {5001, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open beauty baryon decay into e", {prong}, {-1}); return signal; } - if (!nameStr.compare("PromptHcToE")) { + if (nameStr == "PromptHcToE") { MCProng prong(3, {502, 402, 11}, {true, true, true}, {true, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open charmed hadron decays into e", {prong}, {-1}); return signal; } - if (!nameStr.compare("NonPromptHcToE")) { + if (nameStr == "NonPromptHcToE") { MCProng prong(3, {502, 402, 11}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "b hadron decays to open charmed hadron decays to e", {prong}, {-1}); return signal; } - if (!nameStr.compare("HFdecayToE")) { + if (nameStr == "HFdecayToE") { MCProng prong(2, {902, 11}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open charm and beauty to electrons", {prong}, {-1}); return signal; } - if (!nameStr.compare("AnyHFdecayToE")) { + if (nameStr == "AnyHFdecayToE") { MCProng prong(1, {902}, {true}, {false}, {0}, {0}, {false}, true, {11}, {false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary, false); // set source to be ALICE primary particles prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) signal = new MCSignal(name, "Open charm and beauty to electrons", {prong}, {-1}); return signal; } - // if (!nameStr.compare("LFQtoPC")) { + // if (nameStr == "LFQtoPC") { // MCProng prong(3, {900, 22, 11}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); // prong.SetSignalInTime(true); // set direction to check for daughters (true, in time) or for mothers (false, back in time) // signal = new MCSignal(name, "LF meson + quarkonia decays into photon conversion electron", {prong}, {-1}); @@ -861,166 +909,184 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) //_________________________________________________________________________________________________________________________ // LMEE pair signals for LF, same mother - if (!nameStr.compare("eeFromAnything")) { + if (nameStr == "eeDuplicated") { // check whether we have two tracks pointing to the same MC particle + MCProng prong(1, {11}, {true}, {false}, {0}, {0}, {false}); + prong.SetSourceBit(0, MCProng::kPhysicalPrimary); + signal = new MCSignal(name, "duplicated electron", {prong, prong}, {0, 0}); // signal at pair level + return signal; + } + if (nameStr == "eeFromAnything") { MCProng prong(2, {11, MCProng::kPDGCodeNotAssigned}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from any decay", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromPi0")) { + if (nameStr == "eeFromPi0") { MCProng prong(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from pi0 decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eePrimaryFromPromptPi0")) { + if (nameStr == "eeSecondaryFromPi0") { + MCProng prong(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + prong.SetSourceBit(0, MCProng::kPhysicalPrimary, true); + signal = new MCSignal(name, "ee secondary pairs from pi0 decays", {prong, prong}, {1, 1}); // signal at pair level + return signal; + } + if (nameStr == "eePrimaryFromPromptPi0") { MCProng prong(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502, 402}, {true, true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from prompt pi0 decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromEta")) { + if (nameStr == "eeFromEta") { MCProng prong(2, {11, 221}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from eta decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromEtaprime")) { + if (nameStr == "eeSecondaryFromEta") { + MCProng prong(2, {11, 221}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + prong.SetSourceBit(0, MCProng::kPhysicalPrimary, true); + signal = new MCSignal(name, "ee pairs from eta decays", {prong, prong}, {1, 1}); // signal at pair level + return signal; + } + if (nameStr == "eeFromEtaprime") { MCProng prong(2, {11, 331}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from eta' decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromRho")) { + if (nameStr == "eeFromRho") { MCProng prong(2, {11, 113}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from rho decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromOmega")) { + if (nameStr == "eeFromOmega") { MCProng prong(2, {11, 223}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from omega decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromPhi")) { + if (nameStr == "eeFromPhi") { MCProng prong(2, {11, 333}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from phi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromJpsi")) { + if (nameStr == "eeFromJpsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from j/psi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromJpsiExclusive")) { + if (nameStr == "eeFromJpsiExclusive") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from j/psi decays", {prong, prong}, {1, 1}); // signal at pair level signal->SetDecayChannelIsExclusive(2, true); return signal; } - if (!nameStr.compare("eeFromJpsiNotExclusive")) { + if (nameStr == "eeFromJpsiNotExclusive") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from j/psi decays", {prong, prong}, {1, 1}); // signal at pair level signal->SetDecayChannelIsNotExclusive(2, true); return signal; } - if (!nameStr.compare("eePrimaryFromPromptJPsi")) { + if (nameStr == "eePrimaryFromPromptJPsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from prompt j/psi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eePrimaryFromNonPromptJPsi")) { + if (nameStr == "eePrimaryFromNonPromptJPsi") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from non-prompt j/psi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromPhi")) { + if (nameStr == "mumuFromPhi") { MCProng prong(2, {13, 333}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "mumu pairs from phi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromJpsi")) { + if (nameStr == "mumuFromJpsi") { MCProng prong(2, {13, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "mumu pairs from j/psi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromPromptJpsi")) { + if (nameStr == "mumuFromPromptJpsi") { MCProng prong(2, {13, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "mumu pairs from prompt j/psi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromNonPromptJpsi")) { + if (nameStr == "mumuFromNonPromptJpsi") { MCProng prong(2, {13, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "mumu pairs from non-prompt j/psi decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromPsi2S")) { + if (nameStr == "eeFromPsi2S") { MCProng prong(2, {11, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from psi2s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromPsi2S")) { + if (nameStr == "mumuFromPsi2S") { MCProng prong(2, {13, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "mumu pairs from psi2s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromPromptPsi2S")) { + if (nameStr == "mumuFromPromptPsi2S") { MCProng prong(2, {13, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "mumu pairs from prompt psi2s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromNonPromptPsi2S")) { + if (nameStr == "mumuFromNonPromptPsi2S") { MCProng prong(2, {13, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "mumu pairs from non-prompt psi2s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromUpsilon1S")) { + if (nameStr == "mumuFromUpsilon1S") { MCProng prong(2, {13, 553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "mumu pairs from upsilon1s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromUpsilon2S")) { + if (nameStr == "mumuFromUpsilon2S") { MCProng prong(2, {13, 100553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "mumu pairs from upsilon2s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("mumuFromUpsilon3S")) { + if (nameStr == "mumuFromUpsilon3S") { MCProng prong(2, {13, 200553}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "mumu pairs from upsilon3s decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromLMeeLFQ")) { + if (nameStr == "eeFromLMeeLFQ") { MCProng prong(2, {11, 900}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from light flavor meson + quarkonia decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromLMeeLF")) { + if (nameStr == "eeFromLMeeLF") { MCProng prong(2, {11, 901}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from light flavor meson decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromLMeeNoHFLFQ")) { + if (nameStr == "eeFromLMeeNoHFLFQ") { MCProng prong(2, {11, 900}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502, 402}, {true, true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from light flavor meson + quarkonia decays not from open-HF decays", {prong, prong}, {1, 1}); // signal at pair level return signal; } - if (!nameStr.compare("eeFromLMeeNoHFLF")) { + if (nameStr == "eeFromLMeeNoHFLF") { MCProng prong(2, {11, 901}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502, 402}, {true, true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from light flavor meson decays not from open-HF decays", {prong, prong}, {1, 1}); // signal at pair level @@ -1029,7 +1095,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) // LMEE pair signals for HF // D0->e and D0->e - if (!nameStr.compare("eeFromD0")) { + if (nameStr == "eeFromD0") { MCProng prong(2, {kElectron, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from D0 decays, no beauty in history", {prong, prong}, {-1, -1}); @@ -1037,7 +1103,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // D0->e and D0->e - if (!nameStr.compare("eeFromPi0FromD0")) { + if (nameStr == "eeFromPi0FromD0") { MCProng prong(2, {kElectron, kPi0, Pdg::kD0}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from D0 to Pi0 decays, no beauty in history", {prong, prong}, {1, 1}); @@ -1045,7 +1111,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // D+/- -> e and D+/- -> e - if (!nameStr.compare("eeFromChargedD")) { + if (nameStr == "eeFromChargedD") { MCProng prong(2, {kElectron, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from D+/- decays, no beauty in history", {prong, prong}, {-1, -1}); @@ -1053,7 +1119,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // D0 -> e and D+/- -> e - if (!nameStr.compare("eeFromD0andChargedD")) { + if (nameStr == "eeFromD0andChargedD") { MCProng prongD0(2, {kElectron, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongDch(2, {kElectron, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongD0.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1063,7 +1129,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // D+/- -> e and D0 -> e - if (!nameStr.compare("eeFromD0andChargedDBis")) { + if (nameStr == "eeFromD0andChargedDBis") { MCProng prongD0(2, {kElectron, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongDch(2, {kElectron, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongD0.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1073,7 +1139,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // D_s->e and D_s->e - if (!nameStr.compare("eeFromDs")) { + if (nameStr == "eeFromDs") { MCProng prong(2, {kElectron, Pdg::kDS}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from Ds +/- decays, no beauty in history", {prong, prong}, {-1, -1}); @@ -1081,7 +1147,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Lambda_c->e and Lambda_c->e - if (!nameStr.compare("eeFromLambdaC")) { + if (nameStr == "eeFromLambdaC") { MCProng prong(2, {kElectron, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from Lambda_c, no beauty in history", {prong, prong}, {-1, -1}); @@ -1089,7 +1155,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Lambda_c->e and D0->e - if (!nameStr.compare("eeFromLambdaCandD0")) { + if (nameStr == "eeFromLambdaCandD0") { MCProng prongLc(2, {kElectron, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongD0(2, {kElectron, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongLc.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1099,7 +1165,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // D0->e and Lambda_c->e - if (!nameStr.compare("eeFromLambdaCandD0Bis")) { + if (nameStr == "eeFromLambdaCandD0Bis") { MCProng prongLc(2, {kElectron, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongD0(2, {kElectron, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongLc.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1109,7 +1175,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Lambda_c->e and D+/- -> e - if (!nameStr.compare("eeFromLambdaCandChargedD")) { + if (nameStr == "eeFromLambdaCandChargedD") { MCProng prongLc(2, {kElectron, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongDch(2, {kElectron, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongLc.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1119,7 +1185,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // D+/- -> e and Lambda_c->e - if (!nameStr.compare("eeFromLambdaCandChargedDBis")) { + if (nameStr == "eeFromLambdaCandChargedDBis") { MCProng prongLc(2, {kElectron, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongDch(2, {kElectron, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongLc.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1129,7 +1195,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Xic0 ->e and Xic0 ->e - if (!nameStr.compare("eeFromXiC0")) { + if (nameStr == "eeFromXiC0") { MCProng prong(2, {kElectron, Pdg::kXiC0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from Xi_c0, no beauty in history", {prong, prong}, {-1, -1}); @@ -1137,7 +1203,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Xi_c+ ->e and Xi_c+ ->e - if (!nameStr.compare("eeFromXiCPlus")) { + if (nameStr == "eeFromXiCPlus") { MCProng prong(2, {kElectron, Pdg::kXiCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from Xi_c+, no beauty in history", {prong, prong}, {-1, -1}); @@ -1145,7 +1211,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Xi_c0 ->e and Xi_c+ ->e - if (!nameStr.compare("eeFromXiC0andXiCPlus")) { + if (nameStr == "eeFromXiC0andXiCPlus") { MCProng prongXiCPlus(2, {kElectron, Pdg::kXiCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongXiC0(2, {kElectron, Pdg::kXiC0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongXiCPlus.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1155,7 +1221,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Xi_c+ ->e and Xi_c0 ->e - if (!nameStr.compare("eeFromXiC0andXiCPlusBis")) { + if (nameStr == "eeFromXiC0andXiCPlusBis") { MCProng prongXiCPlus(2, {kElectron, Pdg::kXiCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); MCProng prongXiC0(2, {kElectron, Pdg::kXiC0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prongXiCPlus.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1165,7 +1231,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Xi_cc++ ->e and Xi_cc++ ->e - if (!nameStr.compare("eeFromXiCPlusPlus")) { + if (nameStr == "eeFromXiCPlusPlus") { MCProng prong(2, {kElectron, Pdg::kXiCCPlusPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from Xi_cc++, no beauty in history", {prong, prong}, {-1, -1}); @@ -1173,7 +1239,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // c->e and c->e (no check) - if (!nameStr.compare("eeFromCCNoCheck")) { + if (nameStr == "eeFromCCNoCheck") { MCProng prong(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from c->e and c->e without check", {prong, prong}, {-1, -1}); // signal at pair level @@ -1181,7 +1247,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // ee from HF in general - if (!nameStr.compare("eeFromHF")) { + if (nameStr == "eeFromHF") { MCProng prong(2, {11, 902}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from b,c->e and b,c->e without check", {prong, prong}, {-1, -1}); // signal at pair level @@ -1189,7 +1255,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Any c in history but no b -> c -> e - if (!nameStr.compare("eeFromPromptCandPromptC")) { + if (nameStr == "eeFromPromptCandPromptC") { MCProng prong(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); // check if mother pdg code is in history prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs with any charm but no beauty in decay chain", {prong, prong}, {-1, -1}); // signal at pair level @@ -1197,7 +1263,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Any b to any c in history b -> c -> e - if (!nameStr.compare("eeFromAnyBtoCandAnyBtoC")) { + if (nameStr == "eeFromAnyBtoCandAnyBtoC") { MCProng prong(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {false}); // check if mother pdg code is in history prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs with any beauty to charm in decay chain", {prong, prong}, {-1, -1}); // signal at pair level @@ -1205,7 +1271,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->c->e, b->c->e - if (!nameStr.compare("eeFromBtoCandBtoC")) { + if (nameStr == "eeFromBtoCandBtoC") { MCProng prong(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false); // check if mother pdg code is in history prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs with any beauty to charm in decay chain", {prong, prong}, {-1, -1}); // signal at pair level @@ -1216,7 +1282,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) // Looking at such decays: B -> (e) D -> (e)e and bar{B} -> e // Signal allows combinations of ee from the same B meson // + the combination of e fom B and e from bar{B} - if (!nameStr.compare("eeFromBandAnyBtoC")) { + if (nameStr == "eeFromBandAnyBtoC") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {false}); // check if mother pdg code is in history @@ -1226,7 +1292,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // Any b->e and Any b->X->c->e - if (!nameStr.compare("eeFromBandAnyBtoCBis")) { + if (nameStr == "eeFromBandAnyBtoCBis") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {false}); // check if mother pdg code is in history @@ -1236,7 +1302,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->e and b->c->e - if (!nameStr.compare("eeFromBandBtoC")) { + if (nameStr == "eeFromBandBtoC") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false); // check if mother pdg code is in history @@ -1246,7 +1312,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->e and b->c->e - if (!nameStr.compare("eeFromBandBtoCBis")) { + if (nameStr == "eeFromBandBtoCBis") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false); // check if mother pdg code is in history @@ -1257,7 +1323,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) // b->e and b->c->e (same mother/grandmother) // require that the mother is the grandmother of the other electron - if (!nameStr.compare("eeFromBandBtoCsameGM")) { + if (nameStr == "eeFromBandBtoCsameGM") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false); // check if mother pdg code is in history @@ -1268,7 +1334,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) // b->e and b->c->e (same mother/grandmother) // require that the mother is the grandmother of the other electron - if (!nameStr.compare("eeFromBandBtoCsameGMBis")) { + if (nameStr == "eeFromBandBtoCsameGMBis") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false); // check if mother pdg code is in history @@ -1279,7 +1345,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) // b->e and b->c->e (different mother/grandmother) // require that the mother is not the grandmother of the other electron - if (!nameStr.compare("eeFromBandBtoCdiffGM")) { + if (nameStr == "eeFromBandBtoCdiffGM") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false); // check if mother pdg code is in history @@ -1290,7 +1356,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) // b->e and b->c->e (different mother/grandmother) // require that the mother is not the grandmother of the other electron - if (!nameStr.compare("eeFromBandBtoCdiffGMBis")) { + if (nameStr == "eeFromBandBtoCdiffGMBis") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false); // check if mother pdg code is in history @@ -1300,7 +1366,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->e and b->e - if (!nameStr.compare("eeFromBB")) { + if (nameStr == "eeFromBB") { MCProng prong(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from b->e and b->e", {prong, prong}, {-1, -1}); // signal at pair level @@ -1308,7 +1374,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->e and b->e (commonAncestors) - if (!nameStr.compare("eeFromSameB")) { + if (nameStr == "eeFromSameB") { MCProng prong(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "ee pairs from b->e and b->e", {prong, prong}, {1, 1}); // signal at pair level @@ -1316,7 +1382,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->e and c->e no check - if (!nameStr.compare("eeFromBandFromC")) { + if (nameStr == "eeFromBandFromC") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongC(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); @@ -1326,7 +1392,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->e and c->e no check - if (!nameStr.compare("eeFromBandFromCBis")) { + if (nameStr == "eeFromBandFromCBis") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongC(2, {11, 402}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); @@ -1336,7 +1402,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // b->e and b->c->e (single b) - if (!nameStr.compare("eeFromSingleBandBtoC")) { + if (nameStr == "eeFromSingleBandBtoC") { MCProng prongB(2, {11, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prongB.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongBtoC(3, {11, 402, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); @@ -1347,26 +1413,26 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) //_________________________________________________________________________________________________________________________ - if (!nameStr.compare("kaonFromBplus")) { + if (nameStr == "kaonFromBplus") { MCProng prong(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaons from B+ decays", {prong}, {1}); return signal; } - if (!nameStr.compare("kaonFromBplusHistory")) { + if (nameStr == "kaonFromBplusHistory") { MCProng prong(1, {321}, {true}, {false}, {0}, {0}, {false}, false, {521}, {false}); signal = new MCSignal(name, "Kaons from B+ decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("kaonPrimaryFromBplusHistory")) { + if (nameStr == "kaonPrimaryFromBplusHistory") { MCProng prong(1, {321}, {true}, {false}, {0}, {0}, {false}, false, {521}, {false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Kaons from B+ decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("kaonPrimaryFromBplusFS")) { + if (nameStr == "kaonPrimaryFromBplusFS") { MCProng prong(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); prong.SetSourceBit(1, MCProng::kHEPMCFinalState); @@ -1374,57 +1440,57 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("kaonFromAnyBHistory")) { + if (nameStr == "kaonFromAnyBHistory") { MCProng prong(1, {321}, {true}, {false}, {0}, {0}, {false}, false, {503}, {false}); signal = new MCSignal(name, "Kaons from B+ decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("JpsiFromBplus")) { + if (nameStr == "JpsiFromBplus") { MCProng prong(2, {443, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Jpsi from B+ decays", {prong}, {1}); return signal; } - if (!nameStr.compare("eFromJpsiFromBplus")) { + if (nameStr == "eFromJpsiFromBplus") { MCProng prong(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Electrons from Jpsi from B+ decays", {prong}, {1}); return signal; } - if (!nameStr.compare("electronFromJpsiFromBplus")) { + if (nameStr == "electronFromJpsiFromBplus") { MCProng prong(3, {11, 443, 521}, {false, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Electrons from Jpsi from B+ decays", {prong}, {1}); return signal; } - if (!nameStr.compare("positronFromJpsiFromBplus")) { + if (nameStr == "positronFromJpsiFromBplus") { MCProng prong(3, {-11, 443, 521}, {false, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Positrons from Jpsi from B+ decays", {prong}, {1}); return signal; } - if (!nameStr.compare("eeFromJpsiFromBplus")) { + if (nameStr == "eeFromJpsiFromBplus") { MCProng prong(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Electron pair from Jpsi from B+ decays", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("eeKaonFromBplus")) { + if (nameStr == "eeKaonFromBplus") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and electron pair from B+", {pronge, pronge, prongKaon}, {2, 2, 1}); return signal; } - if (!nameStr.compare("eeFromJpsiKaonAny")) { + if (nameStr == "eeFromJpsiKaonAny") { MCProng pronge(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongKaon(1, {321}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Kaon and electron pair", {pronge, pronge, prongKaon}, {-1, -1, -1}); return signal; } - if (!nameStr.compare("eeKaonFromBplusExclusive")) { + if (nameStr == "eeKaonFromBplusExclusive") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and electron pair from B+", {pronge, pronge, prongKaon}, {2, 2, 1}); @@ -1432,7 +1498,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eeKaonFromBplusNotExclusive")) { + if (nameStr == "eeKaonFromBplusNotExclusive") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and electron pair from B+", {pronge, pronge, prongKaon}, {2, 2, 1}); @@ -1441,56 +1507,56 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) } // correlated background - if (!nameStr.compare("eePionFromBplus")) { + if (nameStr == "eePionFromBplus") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPion(2, {211, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Pion and electron pair from B+", {pronge, pronge, prongPion}, {2, 2, 1}); return signal; } - if (!nameStr.compare("eeKaonFromBplusViaEverything")) { + if (nameStr == "eeKaonFromBplusViaEverything") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(3, {321, 0, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Kaon and electron pair from B+ via everything", {pronge, pronge, prongKaon}, {2, 2, 2}); return signal; } - if (!nameStr.compare("eeKaonFromB0")) { + if (nameStr == "eeKaonFromB0") { MCProng pronge(3, {11, 443, 511}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 511}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and electron pair from B0", {pronge, pronge, prongKaon}, {2, 2, 1}); return signal; } - if (!nameStr.compare("eePionFromB0ViaEverything")) { // catching feed-down for B0 + if (nameStr == "eePionFromB0ViaEverything") { // catching feed-down for B0 MCProng pronge(3, {11, 443, 511}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPion(3, {211, 0, 511}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Pion and electron pair from B0", {pronge, pronge, prongPion}, {2, 2, 1}); return signal; } - if (!nameStr.compare("eeKaonFromOpenBeautyMesons")) { + if (nameStr == "eeKaonFromOpenBeautyMesons") { MCProng pronge(3, {11, 443, 501}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 501}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Excited kaon and electron pair from B0", {pronge, pronge, prongKaon}, {2, 2, 2}); return signal; } - if (!nameStr.compare("eeKaonFromOpenBeautyHadrons")) { + if (nameStr == "eeKaonFromOpenBeautyHadrons") { MCProng pronge(3, {11, 443, 502}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 502}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and electron pair from open beauty hadrons", {pronge, pronge, prongKaon}, {2, 2, 1}); return signal; } - if (!nameStr.compare("eeKaonFromLambdaB")) { + if (nameStr == "eeKaonFromLambdaB") { MCProng pronge(3, {11, 443, 5122}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 5122}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and electron pair from lambda B", {pronge, pronge, prongKaon}, {2, 2, 1}); return signal; } - if (!nameStr.compare("eeKaonPion0FromBplus")) { + if (nameStr == "eeKaonPion0FromBplus") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPion(2, {111, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); @@ -1498,7 +1564,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eeKaonEtaFromBplus")) { + if (nameStr == "eeKaonEtaFromBplus") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongEta(2, {221, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); @@ -1506,7 +1572,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eeKaonOmegaFromBplus")) { + if (nameStr == "eeKaonOmegaFromBplus") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongOmega(2, {223, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); @@ -1514,7 +1580,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eeKaonPionFromBplus")) { + if (nameStr == "eeKaonPionFromBplus") { MCProng pronge(3, {11, 443, 521}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongKaon(2, {321, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPion(2, {211, 521}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); @@ -1522,35 +1588,35 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("Bplus")) { + if (nameStr == "Bplus") { MCProng prong(1, {521}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "B+", {prong}, {-1}); return signal; } - if (!nameStr.compare("BplusFS")) { + if (nameStr == "BplusFS") { MCProng prong(1, {521}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kHEPMCFinalState); signal = new MCSignal(name, "B+", {prong}, {-1}); return signal; } - if (!nameStr.compare("beautyPairs")) { + if (nameStr == "beautyPairs") { MCProng prong(1, {503}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal("beautyPairs", "Beauty hadron pair", {prong, prong}, {-1, -1}); return signal; } - if (!nameStr.compare("everythingFromBeautyPairs")) { + if (nameStr == "everythingFromBeautyPairs") { MCProng prong(2, {0, 503}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal("everythingFromBeautyPairs", "Everything from beauty hadrons pair", {prong, prong}, {-1, -1}); return signal; } - if (!nameStr.compare("everythingFromEverythingFromBeautyPairsCM")) { + if (nameStr == "everythingFromEverythingFromBeautyPairsCM") { MCProng prong(3, {0, 0, 503}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal("everythingFromEverythingFromBeautyPairs", "Everything from everything from beauty hadrons pair with common grand-mother", {prong, prong}, {2, 2}); return signal; } - if (!nameStr.compare("everythingFromBeautyANDeverythingFromEverythingFromBeautyPairs")) { + if (nameStr == "everythingFromBeautyANDeverythingFromEverythingFromBeautyPairs") { MCProng prong1(3, {0, 0, 503}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prong2(2, {0, 503}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal("everythingFromBeautyANDeverythingFromEverythingFromBeautyPairs", "Everything beauty and everything from everything from beauty hadrons pair", {prong1, prong2}, {2, 1}); @@ -1559,247 +1625,264 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) //------------------------------------------------------------------------------------ - if (!nameStr.compare("D0")) { + if (nameStr == "D0") { MCProng prong(1, {Pdg::kD0}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "D0", {prong}, {-1}); return signal; } - if (!nameStr.compare("nonPromptD0")) { + if (nameStr == "nonPromptD0") { MCProng prong(2, {Pdg::kD0, 503}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Non-prompt D0", {prong}, {-1}); return signal; } - if (!nameStr.compare("D0FS")) { + if (nameStr == "D0FS") { MCProng prong(1, {Pdg::kD0}, {true}, {false}, {0}, {0}, {false}); prong.SetSourceBit(0, MCProng::kHEPMCFinalState); signal = new MCSignal(name, "D0", {prong}, {-1}); return signal; } - if (!nameStr.compare("KPiFromD0")) { + if (nameStr == "KPiFromD0") { MCProng prongKaon(2, {321, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPion(2, {211, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and pion pair from D0", {prongKaon, prongPion}, {1, 1}); return signal; } - if (!nameStr.compare("KPiFromD0Reflected")) { + if (nameStr == "KPiFromD0Reflected") { MCProng prongFalseKaon(2, {211, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongFalsePion(2, {321, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon and pion pair from D0 with reflected mass assumption", {prongFalseKaon, prongFalsePion}, {1, 1}); return signal; } - if (!nameStr.compare("Dcharged")) { + if (nameStr == "Dcharged") { MCProng prong(1, {Pdg::kDPlus}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "D+/-", {prong}, {-1}); return signal; } - if (!nameStr.compare("Dplus")) { + if (nameStr == "Dplus") { MCProng prong(1, {Pdg::kDPlus}, {false}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "D+", {prong}, {-1}); return signal; } - if (!nameStr.compare("Dminus")) { + if (nameStr == "Dminus") { MCProng prong(1, {-Pdg::kDPlus}, {false}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "D+", {prong}, {-1}); return signal; } - if (!nameStr.compare("KPiPiFromDcharged")) { + if (nameStr == "KPiPiFromDcharged") { MCProng prongKaon(2, {321, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPion(2, {211, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon pion pion triplet from D+/-", {prongKaon, prongPion, prongPion}, {1, 1, 1}); return signal; } - if (!nameStr.compare("KPiPiFromDplus")) { + if (nameStr == "KPiPiFromDplus") { MCProng prongKaon(2, {-321, Pdg::kDPlus}, {false, false}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPion(2, {211, Pdg::kDPlus}, {false, false}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon pion pion triplet from D+", {prongKaon, prongPion, prongPion}, {1, 1, 1}); return signal; } - if (!nameStr.compare("KPiPiFromDminus")) { + if (nameStr == "KPiPiFromDminus") { MCProng prongKaon(2, {321, -Pdg::kDPlus}, {false, false}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPion(2, {-211, -Pdg::kDPlus}, {false, false}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon pion pion triplet from D-", {prongKaon, prongPion, prongPion}, {1, 1, 1}); return signal; } - if (!nameStr.compare("Dstar")) { + if (nameStr == "Dstar") { MCProng prong(1, {Pdg::kDStar}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "D*", {prong}, {-1}); return signal; } - if (!nameStr.compare("DstarPlus")) { + if (nameStr == "DstarPlus") { MCProng prong(1, {Pdg::kDStar}, {false}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "D*+", {prong}, {-1}); return signal; } - if (!nameStr.compare("DstarMinus")) { + if (nameStr == "DstarMinus") { MCProng prong(1, {-Pdg::kDStar}, {false}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "D*-", {prong}, {-1}); return signal; } - if (!nameStr.compare("pionFromDstar")) { + if (nameStr == "pionFromDstar") { MCProng prong(2, {211, Pdg::kDStar}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Pions from D* decays", {prong}, {1}); return signal; } - if (!nameStr.compare("D0FromDstar")) { + if (nameStr == "D0FromDstar") { MCProng prong(2, {Pdg::kD0, Pdg::kDStar}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "D0 from D* decays", {prong}, {1}); return signal; } - if (!nameStr.compare("KFromD0FromDstar")) { + if (nameStr == "KFromD0FromDstar") { MCProng prong(3, {321, Pdg::kD0, Pdg::kDStar}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Kaons from D0 from D* decays", {prong}, {1}); return signal; } - if (!nameStr.compare("PiFromD0FromDstar")) { + if (nameStr == "PiFromD0FromDstar") { MCProng prong(3, {211, Pdg::kD0, Pdg::kDStar}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Pions from D0 from D* decays", {prong}, {1}); return signal; } - if (!nameStr.compare("KPiFromD0FromDstar")) { + if (nameStr == "KPiFromD0FromDstar") { MCProng prongKaon(3, {321, Pdg::kD0, Pdg::kDStar}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPion(3, {321, Pdg::kD0, Pdg::kDStar}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Kaon and pion pair from D0 from D* decay", {prongKaon, prongPion}, {1, 1}); return signal; } - if (!nameStr.compare("KPiPiFromD0FromDstar")) { + if (nameStr == "KPiPiFromD0FromDstar") { MCProng prongKaon(3, {321, Pdg::kD0, Pdg::kDStar}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPionSecondary(3, {211, Pdg::kD0, Pdg::kDStar}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPion(2, {211, Pdg::kDStar}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon pion pion triplet from D*", {prongKaon, prongPionSecondary, prongPion}, {2, 2, 1}); return signal; } - if (!nameStr.compare("KPiPiFromD0FromDstarPlus")) { + if (nameStr == "KPiPiFromD0FromDstarPlus") { MCProng prongKaon(3, {-321, Pdg::kD0, Pdg::kDStar}, {false, false, false}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPionSecondary(3, {211, Pdg::kD0, Pdg::kDStar}, {false, false, false}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPion(2, {211, Pdg::kDStar}, {false, false}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon pion pion triplet from D*+", {prongKaon, prongPionSecondary, prongPion}, {2, 2, 1}); return signal; } - if (!nameStr.compare("KPiPiFromD0FromDstarMinus")) { + if (nameStr == "KPiPiFromD0FromDstarMinus") { MCProng prongKaon(3, {321, Pdg::kD0, Pdg::kDStar}, {false, false, false}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPionSecondary(3, {-211, Pdg::kD0, Pdg::kDStar}, {false, false, false}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPion(2, {-211, Pdg::kDStar}, {false, false}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Kaon pion pion triplet from D*-", {prongKaon, prongPionSecondary, prongPion}, {2, 2, 1}); return signal; } - if (!nameStr.compare("KFromDplus")) { + if (nameStr == "KFromDplus") { MCProng prong(2, {321, Pdg::kDPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {502}, {true}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Kaons from D+/- decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("LambdaC")) { + if (nameStr == "KFromD0") { + MCProng prong(2, {321, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + signal = new MCSignal(name, "Kaons from D0 decays", {prong}, {-1}); + return signal; + } + if (nameStr == "PiFromD0") { + MCProng prong(2, {211, Pdg::kD0}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + signal = new MCSignal(name, "Pions from D0 decays", {prong}, {-1}); + return signal; + } + if (nameStr == "LambdaC") { MCProng prong(1, {Pdg::kLambdaCPlus}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Lambda_c", {prong}, {-1}); return signal; } + if (nameStr == "PrKPiFromLambdaC") { + MCProng prongProton(2, {2212, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + MCProng prongKaon(2, {321, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + MCProng prongPion(2, {211, Pdg::kLambdaCPlus}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); + signal = new MCSignal(name, "Proton kaon pion triplet from Lambda_c+/-", {prongProton, prongKaon, prongPion}, {1, 1, 1}); + return signal; + } //-------------------------------------------------------------------------------- - if (!nameStr.compare("JpsiFromChic0")) { + if (nameStr == "JpsiFromChic0") { MCProng prong(2, {443, 10441}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Jpsi from Chic0 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("eFromJpsiFromChic0")) { + if (nameStr == "eFromJpsiFromChic0") { MCProng prong(3, {11, 443, 10441}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Jpsi from Chic0 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("eeFromJpsiFromChic0")) { + if (nameStr == "eeFromJpsiFromChic0") { MCProng prong(3, {11, 443, 10441}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electron pair from Jpsi from Chic0 decays", {prong, prong}, {2, 2}); return signal; } - if (!nameStr.compare("JpsiFromChic1")) { + if (nameStr == "JpsiFromChic1") { MCProng prong(2, {443, 20443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Jpsi from Chic1 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("eFromJpsiFromChic1")) { + if (nameStr == "eFromJpsiFromChic1") { MCProng prong(3, {11, 443, 20443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Jpsi from Chic1 decays", {prong}, {2}); return signal; } - if (!nameStr.compare("eeFromJpsiFromChic1")) { + if (nameStr == "eeFromJpsiFromChic1") { MCProng prong(3, {11, 443, 20443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electron pair from Jpsi from Chic1 decays", {prong, prong}, {2, 2}); return signal; } - if (!nameStr.compare("JpsiFromChic2")) { + if (nameStr == "JpsiFromChic2") { MCProng prong(2, {443, 445}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Jpsi from Chic2 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("JpsiFromChic2")) { + if (nameStr == "JpsiFromChic") { MCProng prong(2, {443, 904}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Jpsi from Chic0, Chic1 or Chic2 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("eFromJpsiFromChic2")) { + if (nameStr == "eFromJpsiFromChic2") { MCProng prong(3, {11, 443, 445}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electrons from Jpsi from Chic2 decays", {prong}, {2}); return signal; } - if (!nameStr.compare("eeFromJpsiFromChic2")) { + if (nameStr == "eeFromJpsiFromChic2") { MCProng prong(3, {11, 443, 445}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electron pair from Jpsi from Chic2 decays", {prong, prong}, {2, 2}); return signal; } - if (!nameStr.compare("eeFromJpsiFromChic012")) { + if (nameStr == "eeFromJpsiFromChic012") { MCProng prong(3, {11, 443, 904}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Electron pair from Jpsi from Chic0, Chic1 or Chic2 decays", {prong, prong}, {2, 2}); return signal; } - if (!nameStr.compare("PhotonFromChic0")) { + if (nameStr == "PhotonFromChic0") { MCProng prong(2, {22, 10441}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Photon from Chic0 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("PhotonFromChic1")) { + if (nameStr == "PhotonFromChic1") { MCProng prong(2, {22, 20443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Photon from Chic1 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("PhotonFromChic2")) { + if (nameStr == "PhotonFromChic2") { MCProng prong(2, {22, 445}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Photon from Chic2 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("PhotonFromChic012")) { + if (nameStr == "PhotonFromChic012") { MCProng prong(2, {22, 904}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Photon from Chic0, Chic1, and Chic2 decays", {prong}, {-1}); return signal; } - if (!nameStr.compare("PhotonFromPi0")) { + if (nameStr == "PhotonFromPi0") { MCProng prong(2, {22, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); // prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Photon from Pi0 decays", {prong}, {1}); return signal; } - if (!nameStr.compare("PhotonPhotonFromPi0")) { + if (nameStr == "PhotonPhotonFromPi0") { MCProng prong(2, {22, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); prong.SetSourceBit(0, MCProng::kPhysicalPrimary); signal = new MCSignal(name, "Photon Photon from Pi0 decays", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("eePhotonFromChic1")) { + if (nameStr == "eePhotonFromChic1") { MCProng pronge(3, {11, 443, 20443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPhoton(2, {22, 20443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); pronge.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1808,7 +1891,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eePhotonFromChic2")) { + if (nameStr == "eePhotonFromChic2") { MCProng pronge(3, {11, 443, 445}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPhoton(2, {22, 445}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); pronge.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1817,7 +1900,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eePhotonFromChic12")) { + if (nameStr == "eePhotonFromChic12") { MCProng pronge(3, {11, 443, MCProng::kPDGCodeNotAssigned}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPhoton(2, {22, MCProng::kPDGCodeNotAssigned}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); pronge.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1826,7 +1909,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eePhotonFromPi0")) { + if (nameStr == "eePhotonFromPi0") { MCProng pronge(2, {11, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPhoton(2, {22, 111}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); pronge.SetSourceBit(0, MCProng::kPhysicalPrimary); @@ -1837,151 +1920,151 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) //-------------------------------------------------------------------------------- - if (!nameStr.compare("X3872")) { + if (nameStr == "X3872") { MCProng prong(1, {9920443}, {true}, {false}, {0}, {0}, {false}); signal = new MCSignal(name, "Inclusive X(3872)", {prong}, {-1}); return signal; } - if (!nameStr.compare("JpsiFromX3872")) { + if (nameStr == "JpsiFromX3872") { MCProng prong(1, {443}, {true}, {false}, {0}, {0}, {false}, false, {9920443}, {false}); signal = new MCSignal(name, "Jpsi from X3872", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromX3872")) { + if (nameStr == "eFromX3872") { MCProng prong(3, {11, 443, 9920443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Electron from Jpsi from X3872", {prong}, {1}); return signal; } - if (!nameStr.compare("PionFromX3872")) { + if (nameStr == "PionFromX3872") { MCProng prong(1, {211}, {true}, {false}, {0}, {0}, {false}, false, {9920443}, {false}); signal = new MCSignal(name, "Pion from Jpsi from X3872", {prong}, {-1}); return signal; } - if (!nameStr.compare("JpsiFromPsi2S")) { + if (nameStr == "JpsiFromPsi2S") { MCProng prong(1, {443}, {true}, {false}, {0}, {0}, {false}, false, {100443}, {false}); signal = new MCSignal(name, "Jpsi from Psi2S", {prong}, {-1}); return signal; } - if (!nameStr.compare("JpsiFromPromptPsi2S")) { + if (nameStr == "JpsiFromPromptPsi2S") { MCProng prong(2, {443, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); signal = new MCSignal(name, "Jpsi from prompt Psi2S", {prong}, {-1}); return signal; } - if (!nameStr.compare("JpsiFromNonpromptPsi2S")) { + if (nameStr == "JpsiFromNonpromptPsi2S") { MCProng prong(2, {443, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); signal = new MCSignal(name, "Jpsi from non-prompt Psi2S", {prong}, {-1}); return signal; } - if (!nameStr.compare("eFromJpsiFromPsi2S")) { + if (nameStr == "eFromJpsiFromPsi2S") { MCProng prong(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); signal = new MCSignal(name, "Electron from Jpsi from Psi2S", {prong}, {1}); return signal; } - if (!nameStr.compare("eFromJpsiFromPromptPsi2S")) { + if (nameStr == "eFromJpsiFromPromptPsi2S") { MCProng prong(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {503}, {true}); signal = new MCSignal(name, "Electron from Jpsi from prompt Psi2S", {prong}, {1}); return signal; } - if (!nameStr.compare("eFromJpsiFromNonpromptPsi2S")) { + if (nameStr == "eFromJpsiFromNonpromptPsi2S") { MCProng prong(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {503}, {false}); signal = new MCSignal(name, "Electron from Jpsi from non-prompt Psi2S", {prong}, {1}); return signal; } - if (!nameStr.compare("PionFromPsi2S")) { + if (nameStr == "PionFromPsi2S") { MCProng prong(1, {211}, {true}, {false}, {0}, {0}, {false}, false, {100443}, {false}); signal = new MCSignal(name, "Pion from Jpsi from Psi2S", {prong}, {-1}); return signal; } - if (!nameStr.compare("PionFromPromptPsi2S")) { + if (nameStr == "PionFromPromptPsi2S") { MCProng prong(2, {211, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); signal = new MCSignal(name, "Pion from prompt Psi2S", {prong}, {-1}); return signal; } - if (!nameStr.compare("PionFromNonpromptPsi2S")) { + if (nameStr == "PionFromNonpromptPsi2S") { MCProng prong(2, {211, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); signal = new MCSignal(name, "Pion from non-prompt Psi2S", {prong}, {-1}); return signal; } - if (!nameStr.compare("eeFromJpsiFromX3872")) { + if (nameStr == "eeFromJpsiFromX3872") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {9920443}, {false}); signal = new MCSignal(name, "Electron pair from Jpsi from X3872", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("JpsiPiPiFromX3872")) { + if (nameStr == "JpsiPiPiFromX3872") { MCProng prongJpsi(2, {443, 9920443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPi(2, {211, 9920443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Jpsi and pion pair from X3872", {prongJpsi, prongPi, prongPi}, {1, 1, 1}); return signal; } - if (!nameStr.compare("eePiPiFromX3872")) { + if (nameStr == "eePiPiFromX3872") { MCProng pronge(3, {11, 443, 9920443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPi(2, {211, 9920443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electron pair and pion pair from X3872", {pronge, pronge, prongPi, prongPi}, {2, 2, 1, 1}); return signal; } - if (!nameStr.compare("eeFromJpsiFromPsi2S")) { + if (nameStr == "eeFromJpsiFromPsi2S") { MCProng prong(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {100443}, {false}); signal = new MCSignal(name, "Electron pair from Jpsi from Psi2S", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("eeFromJpsiFromPromptPsi2S")) { + if (nameStr == "eeFromJpsiFromPromptPsi2S") { MCProng prong(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {503}, {true}); signal = new MCSignal(name, "Electron pair from Jpsi from prompt Psi2S", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("eeFromJpsiFromNonpromptPsi2S")) { + if (nameStr == "eeFromJpsiFromNonpromptPsi2S") { MCProng prong(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {503}, {false}); signal = new MCSignal(name, "Electron pair from Jpsi from non-prompt Psi2S", {prong, prong}, {1, 1}); return signal; } - if (!nameStr.compare("JpsiPiPiFromPsi2S")) { + if (nameStr == "JpsiPiPiFromPsi2S") { MCProng prongJpsi(2, {443, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); MCProng prongPi(2, {211, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Jpsi and pion pair from Psi2S", {prongJpsi, prongPi, prongPi}, {1, 1, 1}); return signal; } - if (!nameStr.compare("eePiPiFromPsi2S")) { + if (nameStr == "eePiPiFromPsi2S") { MCProng pronge(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); MCProng prongPi(2, {211, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); signal = new MCSignal(name, "Electron pair and pion pair from Psi2S", {pronge, pronge, prongPi, prongPi}, {2, 2, 1, 1}); return signal; } - if (!nameStr.compare("eePiPiFromPromptPsi2S")) { + if (nameStr == "eePiPiFromPromptPsi2S") { MCProng pronge(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {503}, {true}); MCProng prongPi(2, {211, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); signal = new MCSignal(name, "Electron pair and pion pair from prompt Psi2S", {pronge, pronge, prongPi, prongPi}, {2, 2, 1, 1}); return signal; } - if (!nameStr.compare("eePiPiFromNonpromptPsi2S")) { + if (nameStr == "eePiPiFromNonpromptPsi2S") { MCProng pronge(3, {11, 443, 100443}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}, false, {503}, {false}); MCProng prongPi(2, {211, 100443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {false}); signal = new MCSignal(name, "Electron pair and pion pair from non-prompt Psi2S", {pronge, pronge, prongPi, prongPi}, {2, 2, 1, 1}); return signal; } - if (!nameStr.compare("eeFromPromptJpsiAnyPrimary")) { + if (nameStr == "eeFromPromptJpsiAnyPrimary") { MCProng pronge(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}, false, {503}, {true}); pronge.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongPrimary(1); @@ -1990,7 +2073,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eeFromJpsiAnyPrimary")) { + if (nameStr == "eeFromJpsiAnyPrimary") { MCProng pronge(2, {11, 443}, {true, true}, {false, false}, {0, 0}, {0, 0}, {false, false}); pronge.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongPrimary(1); @@ -1999,7 +2082,7 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) return signal; } - if (!nameStr.compare("eeFromNonPromptJpsiAnyPrimary")) { + if (nameStr == "eeFromNonPromptJpsiAnyPrimary") { MCProng pronge(3, {11, 443, 503}, {true, true, true}, {false, false, false}, {0, 0, 0}, {0, 0, 0}, {false, false, false}); pronge.SetSourceBit(0, MCProng::kPhysicalPrimary); MCProng prongPrimary(1); @@ -2007,6 +2090,8 @@ MCSignal* o2::aod::dqmcsignals::GetMCSignal(const char* name) signal = new MCSignal(name, "anyprimary and electron pair from non-prompt jpsi", {pronge, pronge, prongPrimary}, {1, 1, -1}); return signal; } + + LOGF(warn, "Did not find MC signal %s", nameStr); return nullptr; } @@ -2034,7 +2119,7 @@ std::vector o2::aod::dqmcsignals::GetMCSignalsFromJSON(const char* js str += json[i]; } } - LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")" << " **** Parsing error is somewhere here: " << str.Data(); + LOG(fatal) << "JSON parse error: " << rapidjson::GetParseErrorFunc(ok.Code()) << " (" << ok.Offset() << ")" << " **** Parsing error is somewhere here: " << str.Data(); // NOLINT(google-readability-casting) return signals; } @@ -2049,10 +2134,10 @@ std::vector o2::aod::dqmcsignals::GetMCSignalsFromJSON(const char* js if (!ValidateJSONMCSignal(&signal, sigName)) { LOG(fatal) << "MCSignal JSON not properly defined for " << sigName << ". Skipping"; continue; - } else { - LOG(debug) << "MCSignal validated"; } + LOG(debug) << "MCSignal validated"; + // Get the signal title const char* title = (signal.HasMember("title") ? signal.FindMember("title")->value.GetString() : ""); LOG(info) << "Title is: " << title; @@ -2090,7 +2175,7 @@ std::vector o2::aod::dqmcsignals::GetMCSignalsFromJSON(const char* js // Get the common ancestors array std::vector commonAncestors; if (signal.HasMember("commonAncestors")) { - for (auto& v : signal.FindMember("commonAncestors")->value.GetArray()) { + for (const auto& v : signal.FindMember("commonAncestors")->value.GetArray()) { commonAncestors.push_back(v.GetInt()); LOG(debug) << "common ancestor " << v.GetInt(); } @@ -2100,7 +2185,7 @@ std::vector o2::aod::dqmcsignals::GetMCSignalsFromJSON(const char* js } } - if (prongs.size() == 0) { + if (prongs.empty()) { LOG(fatal) << "No prongs were defined for this MCSignal!"; return signals; } @@ -2112,7 +2197,7 @@ std::vector o2::aod::dqmcsignals::GetMCSignalsFromJSON(const char* js } // Create the signal and add it to the output vector - MCSignal* mcSignal = new MCSignal(sigName, title, prongs, commonAncestors, excludeCommonAncestor); + auto* mcSignal = new MCSignal(sigName, title, prongs, commonAncestors, excludeCommonAncestor); LOG(debug) << "MCSignal defined, adding to the output vector"; mcSignal->PrintConfig(); signals.push_back(mcSignal); @@ -2165,13 +2250,13 @@ bool o2::aod::dqmcsignals::ValidateJSONMCProng(T prongJSON, const char* prongNam return false; } std::vector nSourceBits; - for (auto& ii : prongJSON->FindMember("sourceBits")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("sourceBits")->value.GetArray()) { if (!ii.IsArray()) { LOG(fatal) << "The sourceBits field should be an array of arrays of MCProng::Source"; return false; } nSourceBits.push_back(ii.GetArray().Size()); - for (auto& iii : ii.GetArray()) { + for (const auto& iii : ii.GetArray()) { if (MCProng::fgSourceNames.find(iii.GetString()) == MCProng::fgSourceNames.end()) { LOG(fatal) << "Source " << iii.GetString() << " not implemented in MCProng"; return false; @@ -2184,7 +2269,7 @@ bool o2::aod::dqmcsignals::ValidateJSONMCProng(T prongJSON, const char* prongNam return false; } int iElem = 0; - for (auto& ii : prongJSON->FindMember("excludeSource")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("excludeSource")->value.GetArray()) { if (!ii.IsArray()) { LOG(fatal) << "The excludeSource field should be an array of arrays of bool"; return false; @@ -2262,13 +2347,13 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa LOG(debug) << "n: " << n; // Get the array of PDG codes std::vector pdgs; - for (auto& pdg : prongJSON->FindMember("pdgs")->value.GetArray()) { + for (const auto& pdg : prongJSON->FindMember("pdgs")->value.GetArray()) { pdgs.push_back(pdg.GetInt()); LOG(debug) << "pdgs: " << pdg.GetInt(); } // get the array of booleans for check both charges option std::vector checkBothCharges; - for (auto& ii : prongJSON->FindMember("checkBothCharges")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("checkBothCharges")->value.GetArray()) { checkBothCharges.push_back(ii.GetBool()); LOG(debug) << "check both charges " << ii.GetBool(); } @@ -2276,7 +2361,7 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa // get the array of booleans for the excludePDG option, defaults to false std::vector excludePDG; if (prongJSON->HasMember("excludePDG")) { - for (auto& ii : prongJSON->FindMember("excludePDG")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("excludePDG")->value.GetArray()) { excludePDG.push_back(ii.GetBool()); LOG(debug) << "exclude pdg " << ii.GetBool(); } @@ -2289,9 +2374,9 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa // get the source bits, and transform from string to int std::vector> sourceBitsVec; if (prongJSON->HasMember("sourceBits")) { - for (auto& ii : prongJSON->FindMember("sourceBits")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("sourceBits")->value.GetArray()) { std::vector sourceBits; - for (auto& iii : ii.GetArray()) { + for (const auto& iii : ii.GetArray()) { sourceBits.push_back(MCProng::fgSourceNames[iii.GetString()]); LOG(debug) << "source bit " << iii.GetString(); } @@ -2301,9 +2386,9 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa // prepare the exclusion source options if specified std::vector> excludeSourceVec; if (prongJSON->HasMember("excludeSource")) { - for (auto& ii : prongJSON->FindMember("excludeSource")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("excludeSource")->value.GetArray()) { std::vector excludeSource; - for (auto& iii : ii.GetArray()) { + for (const auto& iii : ii.GetArray()) { excludeSource.push_back(iii.GetBool()); LOG(debug) << "exclude source bit " << iii.GetBool(); } @@ -2314,7 +2399,7 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa // prepare the useANDonSourceBitMap vector, defaults to true for each generation std::vector useANDonSourceBitMap; if (prongJSON->HasMember("useANDonSourceBitMap")) { - for (auto& ii : prongJSON->FindMember("useANDonSourceBitMap")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("useANDonSourceBitMap")->value.GetArray()) { useANDonSourceBitMap.push_back(ii.GetBool()); LOG(debug) << "use AND on source map " << ii.GetBool(); } @@ -2329,12 +2414,12 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa int igen = 0; std::vector sBitsVec; std::vector sBitsExcludeVec; - for (auto& itgen : sourceBitsVec) { + for (const auto& itgen : sourceBitsVec) { int is = 0; uint64_t sBits = 0; uint64_t sBitsExclude = 0; auto excludeVec = (hasExclude ? excludeSourceVec[igen] : std::vector{}); - for (auto& s : itgen) { + for (const auto& s : itgen) { bool exclude = (hasExclude ? excludeVec[is] : false); if (s != MCProng::kNothing) { sBits |= (static_cast(1) << s); @@ -2383,7 +2468,7 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa std::vector checkIfPDGInHistory = {}; if (prongJSON->HasMember("checkIfPDGInHistory")) { - for (auto& ii : prongJSON->FindMember("checkIfPDGInHistory")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("checkIfPDGInHistory")->value.GetArray()) { checkIfPDGInHistory.push_back(ii.GetInt()); LOG(debug) << "checkIfPDGInHistory: " << ii.GetInt(); } @@ -2391,15 +2476,15 @@ MCProng* o2::aod::dqmcsignals::ParseJSONMCProng(T prongJSON, const char* prongNa std::vector excludePDGInHistory = {}; if (prongJSON->HasMember("excludePDGInHistory")) { - for (auto& ii : prongJSON->FindMember("excludePDGInHistory")->value.GetArray()) { + for (const auto& ii : prongJSON->FindMember("excludePDGInHistory")->value.GetArray()) { excludePDGInHistory.push_back(ii.GetBool()); LOG(debug) << "excludePDGInHistory: " << ii.GetBool(); } } // Calling the MCProng constructor - MCProng* prong = new MCProng(n, pdgs, checkBothCharges, excludePDG, sBitsVec, sBitsExcludeVec, useANDonSourceBitMap, - checkGenerationsInTime, checkIfPDGInHistory, excludePDGInHistory); + auto* prong = new MCProng(n, pdgs, checkBothCharges, excludePDG, sBitsVec, sBitsExcludeVec, useANDonSourceBitMap, + checkGenerationsInTime, checkIfPDGInHistory, excludePDGInHistory); // Print the configuration prong->Print(); return prong; diff --git a/PWGDQ/Core/MixingHandler.cxx b/PWGDQ/Core/MixingHandler.cxx index 5fd601f67d0..7bdaa62f072 100644 --- a/PWGDQ/Core/MixingHandler.cxx +++ b/PWGDQ/Core/MixingHandler.cxx @@ -13,23 +13,23 @@ #include "PWGDQ/Core/VarManager.h" -#include -#include #include #include -#include +#include +#include +#include #include using namespace std; -ClassImp(MixingHandler); - //_________________________________________________________________________ MixingHandler::MixingHandler() : TNamed(), - fIsInitialized(kFALSE), + fIsInitialized(false), fVariableLimits(), - fVariables() + fVariables(), + fPoolDepth(0), + fPools() { // // default constructor @@ -38,9 +38,11 @@ MixingHandler::MixingHandler() : TNamed(), //_________________________________________________________________________ MixingHandler::MixingHandler(const char* name, const char* title) : TNamed(name, title), - fIsInitialized(kFALSE), + fIsInitialized(false), fVariableLimits(), - fVariables() + fVariables(), + fPoolDepth(0), + fPools() { // // Named constructor @@ -56,29 +58,15 @@ MixingHandler::~MixingHandler() } //_________________________________________________________________________ -void MixingHandler::AddMixingVariable(int var, int nBins, float* binLims) +void MixingHandler::AddMixingVariable(int var, const std::vector& binLims) { - // - // add a mixing variable - // - fVariables.push_back(var); - TArrayF varBins; - varBins.Set(nBins, binLims); - fVariableLimits.push_back(varBins); + fVariables[var] = fVariableLimits.size(); + fVariableLimits.push_back(binLims); + // FillEvent() only fills variables marked as used VarManager::SetUseVariable(var); } -//_________________________________________________________________________ -void MixingHandler::AddMixingVariable(int var, int nBins, std::vector binLims) -{ - - float* bins = new float[nBins]; - for (int i = 0; i < nBins; ++i) { - bins[i] = binLims[i]; - } - AddMixingVariable(var, nBins, bins); -} - +/* //_________________________________________________________________________ int MixingHandler::GetMixingVariable(VarManager::Variables var) { @@ -90,7 +78,9 @@ int MixingHandler::GetMixingVariable(VarManager::Variables var) } return -1; } +*/ +/* //_________________________________________________________________________ std::vector MixingHandler::GetMixingVariableLimits(VarManager::Variables var) { @@ -105,21 +95,21 @@ std::vector MixingHandler::GetMixingVariableLimits(VarManager::Variables } } return binLimits; -} +}*/ //_________________________________________________________________________ void MixingHandler::Init() { - // - // Initialization of pools - // The correct event category will be retrieved using the function FindEventCategory() - // - int size = 1; - for (auto v : fVariableLimits) { - size *= (v.GetSize() - 1); + // loop over all variables and create a mixing pool for each category defined by the binning of the variables + int nCategories = 1; + for (auto& var : fVariables) { + nCategories *= (fVariableLimits[var.second].size() - 1); + } + // add elements in the map for each category (the key is the category and the value is an empty pool) + for (int i = 0; i < nCategories; i++) { + fPools[i] = MixingPool(); } - (void)size; - fIsInitialized = kTRUE; + fIsInitialized = true; } //_________________________________________________________________________ @@ -135,16 +125,24 @@ int MixingHandler::FindEventCategory(float* values) Init(); } + // loop over the variables and find out in which bin the value of the variable for the event is located std::vector bin; - int iVar = 0; - for (auto v = fVariableLimits.begin(); v != fVariableLimits.end(); v++, iVar++) { - int binValue = TMath::BinarySearch((*v).GetSize(), (*v).GetArray(), values[fVariables[iVar]]); - bin.push_back(binValue); - if (bin[iVar] == -1 || bin[iVar] == (*v).GetSize() - 1) { + // number of bins per variable in the iteration order of fVariables (fVariableLimits is in insertion order) + std::vector nBins; + for (auto [var, pos] : fVariables) { + // check that the value is within limits, if not return -1 to exclude the event from mixing + size_t binValue = std::distance(fVariableLimits[pos].begin(), std::upper_bound(fVariableLimits[pos].begin(), fVariableLimits[pos].end(), values[var])); + if (binValue == 0 || binValue == fVariableLimits[pos].size()) { return -1; // all variables must be inside limits } + bin.push_back(binValue - 1); + nBins.push_back(fVariableLimits[pos].size() - 1); } + // Hash the bin values to define a unique category + // The hashing is done such that the original bin values can be retrieved from the category + // For example, for 3 variables with n1, n2, n3 bins respectively, the category for bin values (b1, b2, b3) would be: + // category = b1*(n2*n3) + b2*(n3) + b3 int category = 0; int tempCategory = 1; int iv1 = 0; @@ -156,7 +154,7 @@ int MixingHandler::FindEventCategory(float* values) if (iv2 == iv1) { tempCategory *= bin[iv2]; } else { - tempCategory *= (fVariableLimits[iv2].GetSize() - 1); + tempCategory *= nBins[iv2]; } } category += tempCategory; @@ -174,20 +172,25 @@ int MixingHandler::GetBinFromCategory(VarManager::Variables var, int category) c return -1; } - // Search for the position of the variable "var" in the internal variable list of the handler - int tempVar = 0; - for (auto v = fVariables.begin(); v != fVariables.end(); v++, tempVar++) { - if (*v == var) { - break; + // number of bins and position of var in the iteration order of fVariables, as used by FindEventCategory() + std::vector nBins; + int ivar = -1; + for (auto const& [v, pos] : fVariables) { + if (v == var) { + ivar = static_cast(nBins.size()); } + nBins.push_back(fVariableLimits[pos].size() - 1); + } + if (ivar < 0) { + return -1; } // extract the bin position in variable "var" from the category int norm = 1; - for (int i = fVariables.size() - 1; i > tempVar; --i) { - norm *= (fVariableLimits[i].GetSize() - 1); + for (size_t i = nBins.size() - 1; i > static_cast(ivar); --i) { + norm *= nBins[i]; } int truncatedCategory = category - (category % norm); truncatedCategory /= norm; - return truncatedCategory % (fVariableLimits[tempVar].GetSize() - 1); + return truncatedCategory % nBins[ivar]; } diff --git a/PWGDQ/Core/MixingHandler.h b/PWGDQ/Core/MixingHandler.h index f73d5632895..fe61f167c89 100644 --- a/PWGDQ/Core/MixingHandler.h +++ b/PWGDQ/Core/MixingHandler.h @@ -19,29 +19,186 @@ #include "PWGDQ/Core/VarManager.h" -#include #include #include +#include +#include +#include +#include #include class MixingHandler : public TNamed { public: + // number of track cuts which fit in the 32-bit filtering masks + static constexpr int NMaxCuts = 32; + + // Struct to define track properties relevant for mixing and few utility functions + struct MixingTrack { + float pt; + float eta; + float phi; + uint32_t filteringFlags; + // Clear a bit once the track was used in mixing for that bit for the required pool depth. + void ClearBit(uint32_t mask) { filteringFlags &= ~mask; } + void Print() const + { + std::cout << "pt: " << pt << ", eta: " << eta << ", phi: " << phi << ", filteringFlags: " << filteringFlags << std::endl; + } + }; + + // Struct to define events used in mixing and few utility functions. + // An event is defined as two vectors of tracks (typically the legs of a two-body + // decay or the two-particles in a correlation analysis) + struct MixingEvent { + std::vector tracks1; + std::vector tracks2; + // bit map for active filtering bits of all the tracks + uint32_t filteringMask = 0; + // counters to keep track of how many times the event was used for mixing (for each track cut separately) + std::array counters = {0}; + // add a track to the event and update the filtering mask accordingly + void AddTrack1(const MixingTrack& track) + { + tracks1.push_back(track); + filteringMask |= track.filteringFlags; + } + void AddTrack2(const MixingTrack& track) + { + tracks2.push_back(track); + filteringMask |= track.filteringFlags; + } + // Clear bits in the filtering mask. + void ClearFilteringMask(uint32_t mask) { filteringMask &= ~mask; } + // 1) increment the counters for a given track cut bit mask and if the counters reached the pool depth, + // 2) clear the corresponding bit in the tracks filtering flags to exclude them from further mixing + // 3) for each track, if there are no more active bits in the filtering mask, then remove the track from the event + void IncrementCounters(uint32_t mask, int16_t poolDepth) + { + for (int i = 0; i < 32; i++) { + uint32_t bitMask = (static_cast(1) << i); + if (mask & bitMask) { + counters[i]++; + if (counters[i] >= poolDepth) { + for (auto& track : tracks1) { + track.ClearBit(bitMask); + } + for (auto track = tracks1.begin(); track != tracks1.end();) { + if (track->filteringFlags == 0) { + track = tracks1.erase(track); + } else { + ++track; + } + } + + for (auto& track : tracks2) { + track.ClearBit(bitMask); + } + for (auto track = tracks2.begin(); track != tracks2.end();) { + if (track->filteringFlags == 0) { + track = tracks2.erase(track); + } else { + ++track; + } + } + ClearFilteringMask(bitMask); + } + } + } + } + void Print() const + { + std::cout << "Event filtering mask: "; + for (int i = 0; i < 32; i++) { + if (filteringMask & (1ULL << i)) { + std::cout << "1"; + } else { + std::cout << "0"; + } + } + std::cout << std::endl; + for (int i = 0; i < 32; i++) { + if (filteringMask & (1ULL << i)) { + std::cout << "Counter " << i << ": " << counters[i] << std::endl; + } + } + std::cout << "Tracks 1: " << std::endl; + for (const auto& track : tracks1) { + track.Print(); + } + std::cout << "Tracks 2: " << std::endl; + for (const auto& track : tracks2) { + track.Print(); + } + } + }; + + struct MixingPool { + std::vector events; + + // check which events in the pool are empty (i.e. no active tracks for mixing) and remove them from the pool + void CleanPool() + { + for (auto event = events.begin(); event != events.end();) { + if (event->tracks1.empty() && event->tracks2.empty()) { + event = events.erase(event); + } else { + ++event; + } + } + } + // The function that performs the mixing is called outside this class, but the pool provides the events and tracks to be mixed and takes care of updating the events after mixing + // (e.g. incrementing the counters and removing the tracks that reached the pool depth for a given cut) + void UpdatePool(const MixingEvent& event, int16_t poolDepth) + { + for (auto& event : events) { + event.IncrementCounters(event.filteringMask, poolDepth); + } + CleanPool(); + events.push_back(event); + } + // Same, but the stored events are aged only for the cuts in agingMask. Passing the filtering mask of the + // incoming event ages an event only for the cuts for which a mixed pair was actually produced, so that the + // pool depth is a number of mixed partners and not a number of arrivals. + void UpdatePool(const MixingEvent& event, int16_t poolDepth, uint32_t agingMask) + { + for (auto& poolEvent : events) { + poolEvent.IncrementCounters(agingMask, poolDepth); + } + CleanPool(); + events.push_back(event); + } + // getter for the events in the pool + const std::vector& GetEvents() const { return events; } + + void Print() const + { + std::cout << "Mixing pool with " << events.size() << " events:" << std::endl; + for (const auto& event : events) { + event.Print(); + } + } + }; + MixingHandler(); MixingHandler(const char* name, const char* title); virtual ~MixingHandler(); // setters - void AddMixingVariable(int var, int nBins, float* binLims); - void AddMixingVariable(int var, int nBins, std::vector binLims); + void AddMixingVariable(int var, const std::vector& binLims); + void SetPoolDepth(int16_t depth) { fPoolDepth = depth; } + // remove all pools (e.g. at a run change) + void ClearPools() { fPools.clear(); } // getters - int GetNMixingVariables() const { return fVariables.size(); } - int GetMixingVariable(VarManager::Variables var); // returns the position in the internal varible list of the handler. Useful for checks, mostly - std::vector GetMixingVariableLimits(VarManager::Variables var); + // int GetNMixingVariables() const { return fVariables.size(); } + // int GetMixingVariable(VarManager::Variables var); // returns the position in the internal varible list of the handler. Useful for checks, mostly + // std::vector GetMixingVariableLimits(VarManager::Variables var); + MixingPool& GetPool(int category) { return fPools[category]; } + int16_t GetPoolDepth() const { return fPoolDepth; } void Init(); int FindEventCategory(float* values); @@ -54,10 +211,12 @@ class MixingHandler : public TNamed // User options bool fIsInitialized; // check if the mixing handler is initialized - std::vector fVariableLimits; - std::vector fVariables; + // bin limits for the variables used for mixing, the number of vectors corresponds to the number of variables and the content of each vector corresponds to the bin limits for that variable + std::vector> fVariableLimits; + std::map fVariables; // key: variable, value: position in the internal variable list of the handler (used to map the variables to the values passed to FindEventCategory) - ClassDef(MixingHandler, 1); + int16_t fPoolDepth; // number of events to be kept in each pool + std::map fPools; // key: category, value: pool of events corresponding to that category }; #endif // PWGDQ_CORE_MIXINGHANDLER_H_ diff --git a/PWGDQ/Core/MixingLibrary.cxx b/PWGDQ/Core/MixingLibrary.cxx index 28e453b5479..810f09cb62c 100644 --- a/PWGDQ/Core/MixingLibrary.cxx +++ b/PWGDQ/Core/MixingLibrary.cxx @@ -29,185 +29,193 @@ void o2::aod::dqmixing::SetUpMixing(MixingHandler* mh, const char* mixingVarible) { std::string nameStr = mixingVarible; - if (!nameStr.compare("Centrality1")) { + if (nameStr == "Centrality1") { std::vector fCentLimsHashing = {0.0f, 20.0f, 40.0f, 60.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing.size(), fCentLimsHashing); + mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing); } - if (!nameStr.compare("Centrality2")) { + if (nameStr == "Centrality2") { std::vector fCentLimsHashing = {0.0f, 10.0f, 20.0f, 40.0f, 60.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing.size(), fCentLimsHashing); + mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing); } - if (!nameStr.compare("Centrality3")) { + if (nameStr == "Centrality3") { std::vector fCentLimsHashing = {0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing.size(), fCentLimsHashing); + mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing); } - if (!nameStr.compare("Centrality4")) { + if (nameStr == "Centrality4") { std::vector fCentLimsHashing = {0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing.size(), fCentLimsHashing); + mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing); } - if (!nameStr.compare("Centrality5")) { + if (nameStr == "Centrality5") { std::vector fCentLimsHashing = {0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing.size(), fCentLimsHashing); + mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing); } - if (!nameStr.compare("Centrality6")) { + if (nameStr == "Centrality6") { std::vector fCentLimsHashing = {0.0f, 2.5f, 5.0f, 7.5f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing.size(), fCentLimsHashing); + mh->AddMixingVariable(VarManager::kCentVZERO, fCentLimsHashing); } - if (!nameStr.compare("CentralityFT0C1")) { + if (nameStr == "CentralityFT0C1") { std::vector fCentFT0CLimsHashing = {0.0f, 20.0f, 40.0f, 60.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing.size(), fCentFT0CLimsHashing); + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); } - if (!nameStr.compare("CentralityFT0C2")) { + if (nameStr == "CentralityFT0C2") { std::vector fCentFT0CLimsHashing = {0.0f, 10.0f, 20.0f, 40.0f, 60.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing.size(), fCentFT0CLimsHashing); + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); } - if (!nameStr.compare("CentralityFT0C3")) { + if (nameStr == "CentralityFT0C3") { std::vector fCentFT0CLimsHashing = {0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing.size(), fCentFT0CLimsHashing); + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); } - if (!nameStr.compare("CentralityFT0C4")) { + if (nameStr == "CentralityFT0C4") { std::vector fCentFT0CLimsHashing = {0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing.size(), fCentFT0CLimsHashing); + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); } - if (!nameStr.compare("CentralityFT0C5")) { + if (nameStr == "CentralityFT0C5") { std::vector fCentFT0CLimsHashing = {0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing.size(), fCentFT0CLimsHashing); + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); } - if (!nameStr.compare("CentralityFT0C6")) { + if (nameStr == "CentralityFT0C6") { std::vector fCentFT0CLimsHashing = {0.0f, 2.5f, 5.0f, 7.5f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}; - mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing.size(), fCentFT0CLimsHashing); + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); } - if (!nameStr.compare("Mult1")) { + if (nameStr == "CentralityFT0C7") { + std::vector fCentFT0CLimsHashing = {0.0f, 5.0f, 10.0f, 15.0f, 20.0f, 25.0f, 30.0f, 35.0f, 40.0f, 60.0f, 90.0f}; + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); + } + if (nameStr == "CentralityFT0C8") { + std::vector fCentFT0CLimsHashing = {0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 60.0f, 90.0f}; + mh->AddMixingVariable(VarManager::kCentFT0C, fCentFT0CLimsHashing); + } + if (nameStr == "Mult1") { std::vector fMultLimsHashing = {0.0f, 10.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 120.0f, 160.0f, 350.0f}; - mh->AddMixingVariable(VarManager::kVtxNcontrib, fMultLimsHashing.size(), fMultLimsHashing); + mh->AddMixingVariable(VarManager::kVtxNcontrib, fMultLimsHashing); } - if (!nameStr.compare("Mult2")) { + if (nameStr == "Mult2") { std::vector fMultLimsHashing = {0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 120.0f, 140.0f, 180.0f, 350.0f}; - mh->AddMixingVariable(VarManager::kVtxNcontrib, fMultLimsHashing.size(), fMultLimsHashing); + mh->AddMixingVariable(VarManager::kVtxNcontrib, fMultLimsHashing); } - if (!nameStr.compare("Mult3")) { + if (nameStr == "Mult3") { std::vector fMultLimsHashing = {0.0f, 5.0f, 10.0f, 15.0f, 20.0f, 25.0f, 30.0f, 35.0f, 40.0f, 45.0f, 50.0f, 55.0f, 60.0f, 65.0f, 70.0f, 75.0f, 80.0f, 85.0f, 90.0f, 100.0f, 120.0f, 140.0f, 165.0f, 200.0f, 350.0f}; - mh->AddMixingVariable(VarManager::kVtxNcontrib, fMultLimsHashing.size(), fMultLimsHashing); + mh->AddMixingVariable(VarManager::kVtxNcontrib, fMultLimsHashing); } - if (!nameStr.compare("Vtx1")) { + if (nameStr == "Vtx1") { std::vector fZLimsHashing = {-10.0f, 0.0f, 10.0f}; - mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing.size(), fZLimsHashing); + mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing); } - if (!nameStr.compare("Vtx2")) { + if (nameStr == "Vtx2") { std::vector fZLimsHashing = {-10.0f, -5.0f, 0.0f, 5.0f, 10.0f}; - mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing.size(), fZLimsHashing); + mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing); } - if (!nameStr.compare("Vtx3")) { + if (nameStr == "Vtx3") { std::vector fZLimsHashing = {-10.0f, -7.5f, -5.0f, -2.5f, 0.0f, 2.5f, 5.0f, 7.5f, 10.0f}; - mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing.size(), fZLimsHashing); + mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing); } - if (!nameStr.compare("Vtx4")) { + if (nameStr == "Vtx4") { std::vector fZLimsHashing = {-10.0f, -8.0f, -6.0f, -4.0f, -2.0f, 0.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f}; - mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing.size(), fZLimsHashing); + mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing); } - if (!nameStr.compare("Vtx5")) { + if (nameStr == "Vtx5") { std::vector fZLimsHashing = {-10.0f, -9.0f, -8.0f, -7.0f, -6.0f, -5.0f, -4.0f, -3.0f, -2.0f, -1.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f}; - mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing.size(), fZLimsHashing); + mh->AddMixingVariable(VarManager::kVtxZ, fZLimsHashing); } - if (!nameStr.compare("Occupancy1")) { + if (nameStr == "Occupancy1") { std::vector fOccLimsHashing = {0.0f, 500.0f, 1000.0f, 2000.0f, 3000.0f, 6000.0f, 50000.0f}; - mh->AddMixingVariable(VarManager::kTrackOccupancyInTimeRange, fOccLimsHashing.size(), fOccLimsHashing); + mh->AddMixingVariable(VarManager::kTrackOccupancyInTimeRange, fOccLimsHashing); } - if (!nameStr.compare("Occupancy2")) { + if (nameStr == "Occupancy2") { std::vector fOccLimsHashing = {0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 6000.0f, 50000.0f}; - mh->AddMixingVariable(VarManager::kTrackOccupancyInTimeRange, fOccLimsHashing.size(), fOccLimsHashing); + mh->AddMixingVariable(VarManager::kTrackOccupancyInTimeRange, fOccLimsHashing); } - if (!nameStr.compare("Occupancy3")) { + if (nameStr == "Occupancy3") { std::vector fOccLimsHashing = {0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 4500.0f, 6000.0f, 8000.0f, 10000.0f, 50000.0f}; - mh->AddMixingVariable(VarManager::kTrackOccupancyInTimeRange, fOccLimsHashing.size(), fOccLimsHashing); + mh->AddMixingVariable(VarManager::kTrackOccupancyInTimeRange, fOccLimsHashing); } - if (!nameStr.compare("Psi2A1")) { + if (nameStr == "Psi2A1") { std::vector fPsi2A = {-TMath::Pi() / 2., 0.0f, TMath::Pi() / 2.}; - mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A.size(), fPsi2A); + mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A); } - if (!nameStr.compare("Psi2A2")) { + if (nameStr == "Psi2A2") { std::vector fPsi2A = {-TMath::Pi() / 2., -TMath::Pi() / 4., 0.0f, TMath::Pi() / 4., TMath::Pi() / 2.}; - mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A.size(), fPsi2A); + mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A); } - if (!nameStr.compare("Psi2A3")) { + if (nameStr == "Psi2A3") { std::vector fPsi2A = {-4 * TMath::Pi() / 8., -3 * TMath::Pi() / 8., -2 * TMath::Pi() / 8., -TMath::Pi() / 8., 0.0f, TMath::Pi() / 8., 2 * TMath::Pi() / 8., 3 * TMath::Pi() / 8., 4 * TMath::Pi() / 8.}; - mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A.size(), fPsi2A); + mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A); } - if (!nameStr.compare("Psi2A4")) { + if (nameStr == "Psi2A4") { std::vector fPsi2A = {-8 * TMath::Pi() / 16., -7 * TMath::Pi() / 16., -6 * TMath::Pi() / 16., -5 * TMath::Pi() / 16., -4 * TMath::Pi() / 16., -3 * TMath::Pi() / 16., -2 * TMath::Pi() / 16., -TMath::Pi() / 16., 0.0f, TMath::Pi() / 16., 2 * TMath::Pi() / 16., 3 * TMath::Pi() / 16., 4 * TMath::Pi() / 16., 5 * TMath::Pi() / 16., 6 * TMath::Pi() / 16., 7 * TMath::Pi() / 16., 8 * TMath::Pi() / 16.}; - mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A.size(), fPsi2A); + mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A); } - if (!nameStr.compare("Psi2A5")) { + if (nameStr == "Psi2A5") { std::vector fPsi2A = {-12 * TMath::Pi() / 24., -11 * TMath::Pi() / 24., -10 * TMath::Pi() / 24., -9 * TMath::Pi() / 24., -8 * TMath::Pi() / 24., -7 * TMath::Pi() / 24., -6 * TMath::Pi() / 24., -5 * TMath::Pi() / 24., -4 * TMath::Pi() / 24., -3 * TMath::Pi() / 24., -2 * TMath::Pi() / 24., -TMath::Pi() / 24., 0.0f, TMath::Pi() / 24., 2 * TMath::Pi() / 24., 3 * TMath::Pi() / 24., 4 * TMath::Pi() / 24., 5 * TMath::Pi() / 24., 6 * TMath::Pi() / 24., 7 * TMath::Pi() / 24., 8 * TMath::Pi() / 24., 9 * TMath::Pi() / 24., 10 * TMath::Pi() / 24., 11 * TMath::Pi() / 24., 12 * TMath::Pi() / 24.}; - mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A.size(), fPsi2A); + mh->AddMixingVariable(VarManager::kPsi2A, fPsi2A); } - if (!nameStr.compare("Psi2B1")) { + if (nameStr == "Psi2B1") { std::vector fPsi2B = {-TMath::Pi() / 2., 0.0f, TMath::Pi() / 2.}; - mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B.size(), fPsi2B); + mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B); } - if (!nameStr.compare("Psi2B2")) { + if (nameStr == "Psi2B2") { std::vector fPsi2B = {-TMath::Pi() / 2., -TMath::Pi() / 4., 0.0f, TMath::Pi() / 4., TMath::Pi() / 2.}; - mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B.size(), fPsi2B); + mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B); } - if (!nameStr.compare("Psi2B3")) { + if (nameStr == "Psi2B3") { std::vector fPsi2B = {-4 * TMath::Pi() / 8., -3 * TMath::Pi() / 8., -2 * TMath::Pi() / 8., -TMath::Pi() / 8., 0.0f, TMath::Pi() / 8., 2 * TMath::Pi() / 8., 3 * TMath::Pi() / 8., 4 * TMath::Pi() / 8.}; - mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B.size(), fPsi2B); + mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B); } - if (!nameStr.compare("Psi2B4")) { + if (nameStr == "Psi2B4") { std::vector fPsi2B = {-8 * TMath::Pi() / 16., -7 * TMath::Pi() / 16., -6 * TMath::Pi() / 16., -5 * TMath::Pi() / 16., -4 * TMath::Pi() / 16., -3 * TMath::Pi() / 16., -2 * TMath::Pi() / 16., -TMath::Pi() / 16., 0.0f, TMath::Pi() / 16., 2 * TMath::Pi() / 16., 3 * TMath::Pi() / 16., 4 * TMath::Pi() / 16., 5 * TMath::Pi() / 16., 6 * TMath::Pi() / 16., 7 * TMath::Pi() / 16., 8 * TMath::Pi() / 16.}; - mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B.size(), fPsi2B); + mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B); } - if (!nameStr.compare("Psi2B5")) { + if (nameStr == "Psi2B5") { std::vector fPsi2B = {-12 * TMath::Pi() / 24., -11 * TMath::Pi() / 24., -10 * TMath::Pi() / 24., -9 * TMath::Pi() / 24., -8 * TMath::Pi() / 24., -7 * TMath::Pi() / 24., -6 * TMath::Pi() / 24., -5 * TMath::Pi() / 24., -4 * TMath::Pi() / 24., -3 * TMath::Pi() / 24., -2 * TMath::Pi() / 24., -TMath::Pi() / 24., 0.0f, TMath::Pi() / 24., 2 * TMath::Pi() / 24., 3 * TMath::Pi() / 24., 4 * TMath::Pi() / 24., 5 * TMath::Pi() / 24., 6 * TMath::Pi() / 24., 7 * TMath::Pi() / 24., 8 * TMath::Pi() / 24., 9 * TMath::Pi() / 24., 10 * TMath::Pi() / 24., 11 * TMath::Pi() / 24., 12 * TMath::Pi() / 24.}; - mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B.size(), fPsi2B); + mh->AddMixingVariable(VarManager::kPsi2B, fPsi2B); } - if (!nameStr.compare("Psi2C1")) { + if (nameStr == "Psi2C1") { std::vector fPsi2C = {-TMath::Pi() / 2., 0.0f, TMath::Pi() / 2.}; - mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C.size(), fPsi2C); + mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C); } - if (!nameStr.compare("Psi2C2")) { + if (nameStr == "Psi2C2") { std::vector fPsi2C = {-TMath::Pi() / 2., -TMath::Pi() / 4., 0.0f, TMath::Pi() / 4., TMath::Pi() / 2.}; - mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C.size(), fPsi2C); + mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C); } - if (!nameStr.compare("Psi2C3")) { + if (nameStr == "Psi2C3") { std::vector fPsi2C = {-4 * TMath::Pi() / 8., -3 * TMath::Pi() / 8., -2 * TMath::Pi() / 8., -TMath::Pi() / 8., 0.0f, TMath::Pi() / 8., 2 * TMath::Pi() / 8., 3 * TMath::Pi() / 8., 4 * TMath::Pi() / 8.}; - mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C.size(), fPsi2C); + mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C); } - if (!nameStr.compare("Psi2C4")) { + if (nameStr == "Psi2C4") { std::vector fPsi2C = {-8 * TMath::Pi() / 16., -7 * TMath::Pi() / 16., -6 * TMath::Pi() / 16., -5 * TMath::Pi() / 16., -4 * TMath::Pi() / 16., -3 * TMath::Pi() / 16., -2 * TMath::Pi() / 16., -TMath::Pi() / 16., 0.0f, TMath::Pi() / 16., 2 * TMath::Pi() / 16., 3 * TMath::Pi() / 16., 4 * TMath::Pi() / 16., 5 * TMath::Pi() / 16., 6 * TMath::Pi() / 16., 7 * TMath::Pi() / 16., 8 * TMath::Pi() / 16.}; - mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C.size(), fPsi2C); + mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C); } - if (!nameStr.compare("Psi2C5")) { + if (nameStr == "Psi2C5") { std::vector fPsi2C = {-12 * TMath::Pi() / 24., -11 * TMath::Pi() / 24., -10 * TMath::Pi() / 24., -9 * TMath::Pi() / 24., -8 * TMath::Pi() / 24., -7 * TMath::Pi() / 24., -6 * TMath::Pi() / 24., -5 * TMath::Pi() / 24., -4 * TMath::Pi() / 24., -3 * TMath::Pi() / 24., -2 * TMath::Pi() / 24., -TMath::Pi() / 24., 0.0f, TMath::Pi() / 24., 2 * TMath::Pi() / 24., 3 * TMath::Pi() / 24., 4 * TMath::Pi() / 24., 5 * TMath::Pi() / 24., 6 * TMath::Pi() / 24., 7 * TMath::Pi() / 24., 8 * TMath::Pi() / 24., 9 * TMath::Pi() / 24., 10 * TMath::Pi() / 24., 11 * TMath::Pi() / 24., 12 * TMath::Pi() / 24.}; - mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C.size(), fPsi2C); + mh->AddMixingVariable(VarManager::kPsi2C, fPsi2C); } - if (!nameStr.compare("MedianTimeA1")) { + if (nameStr == "MedianTimeA1") { std::vector fMTLimsHashing = {-100.0f, -40.0f, -20.0f, 20.0f, 40.0f, 100.0f}; - mh->AddMixingVariable(VarManager::kNTPCmedianTimeLongA, fMTLimsHashing.size(), fMTLimsHashing); + mh->AddMixingVariable(VarManager::kNTPCmedianTimeLongA, fMTLimsHashing); } - if (!nameStr.compare("MedianTimeA2")) { + if (nameStr == "MedianTimeA2") { std::vector fMTLimsHashing = {-100.0f, -80.0f, -60.0f, -40.0f, -20.0f, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f}; - mh->AddMixingVariable(VarManager::kNTPCmedianTimeLongA, fMTLimsHashing.size(), fMTLimsHashing); + mh->AddMixingVariable(VarManager::kNTPCmedianTimeLongA, fMTLimsHashing); } - if (!nameStr.compare("MedianTimeA3")) { + if (nameStr == "MedianTimeA3") { std::vector fMTLimsHashing = {-100.0f, -80.0f, -60.0f, -40.0f, -30.0f, -20.0f, -10.0f, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 60.0f, 80.0f, 100.0f}; - mh->AddMixingVariable(VarManager::kNTPCmedianTimeLongA, fMTLimsHashing.size(), fMTLimsHashing); + mh->AddMixingVariable(VarManager::kNTPCmedianTimeLongA, fMTLimsHashing); } - if (!nameStr.compare("PileUpA1")) { + if (nameStr == "PileUpA1") { std::vector fPileUpLimsHashing = {0.0f, 1000.0f, 2000.0f, 6000.0f, 10000.0f, 20000.0f}; - mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing.size(), fPileUpLimsHashing); + mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing); } - if (!nameStr.compare("PileUpA2")) { + if (nameStr == "PileUpA2") { std::vector fPileUpLimsHashing = {0.0f, 1000.0f, 2000.0f, 4000.0f, 6000.0f, 8000.0f, 10000.0f, 20000.0f}; - mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing.size(), fPileUpLimsHashing); + mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing); } - if (!nameStr.compare("PileUpA3")) { + if (nameStr == "PileUpA3") { std::vector fPileUpLimsHashing = {0.0f, 1000.0f, 2000.0f, 3000.0f, 4000.0f, 5000.0f, 6000.0f, 8000.0f, 10000.0f, 20000.0f}; - mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing.size(), fPileUpLimsHashing); + mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing); } - if (!nameStr.compare("PileUpA4")) { + if (nameStr == "PileUpA4") { std::vector fPileUpLimsHashing = {0.0f, 500.0f, 1000.0f, 1500.0f, 2000.0f, 2500.0f, 3000.0f, 3500.0f, 4000.0f, 4500.0f, 5000.0f, 5500.0f, 6000.0f, 8000.0f, 10000.0f, 20000.0f}; - mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing.size(), fPileUpLimsHashing); + mh->AddMixingVariable(VarManager::kNTPCcontribLongA, fPileUpLimsHashing); } } @@ -240,7 +248,7 @@ void o2::aod::dqmixing::SetUpMixingFromJSON(MixingHandler* mh, const char* json) const auto& lims = obj["LimsHashing"]; std::vector limits; limits.reserve(lims.Size()); - for (auto& v : lims.GetArray()) { + for (const auto& v : lims.GetArray()) { limits.push_back(v.GetFloat()); } @@ -250,6 +258,6 @@ void o2::aod::dqmixing::SetUpMixingFromJSON(MixingHandler* mh, const char* json) } // set up mixing variable - mh->AddMixingVariable(VarManager::fgVarNamesMap[varStr], limits.size(), limits); + mh->AddMixingVariable(VarManager::fgVarNamesMap[varStr], limits); } } diff --git a/PWGDQ/Core/MuonMatchingMlResponse.h b/PWGDQ/Core/MuonMatchingMlResponse.h index ee2f419a5f5..584cc10b3d6 100644 --- a/PWGDQ/Core/MuonMatchingMlResponse.h +++ b/PWGDQ/Core/MuonMatchingMlResponse.h @@ -20,6 +20,9 @@ #include +#include +#include +#include #include #include #include @@ -27,104 +30,90 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_MFTMUON_MATCH(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesMFTMuonMatch::FEATURE) \ - } - -// Check if the index of mCachedIndices (index associated to a FEATURE) -// matches the entry in EnumInputFeatures associated to this FEATURE -// if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER=FEATURE from track -#define CHECK_AND_FILL_MUON_TRACK(FEATURE, GETTER) \ - case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ - inputFeature = muon.GETTER(); \ - break; \ - } - -// Check if the index of mCachedIndices (index associated to a FEATURE) -// matches the entry in EnumInputFeatures associated to this FEATURE -// if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER=FEATURE from track -#define CHECK_AND_FILL_MUONGLOB_TRACK(FEATURE, GETTER) \ - case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ - inputFeature = muonglob.GETTER(); \ - break; \ - } - -// Check if the index of mCachedIndices (index associated to a FEATURE) -// matches the entry in EnumInputFeatures associated to this FEATURE -// if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER=FEATURE from track -#define CHECK_AND_FILL_MFT_TRACK(FEATURE, GETTER) \ - case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ - inputFeature = mft.GETTER(); \ - break; \ - } +#define FILL_MAP_MFTMUON_MATCH(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesMFTMuonMatch::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE // if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER=FEATURE from track -#define CHECK_AND_FILL_MUON_COV(FEATURE, GETTER) \ +// by calling the corresponding GETTER expression +#define CHECK_AND_FILL_FEATURE(FEATURE, GETTER) \ case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ - inputFeature = muoncov.GETTER(); \ + inputFeature = (GETTER); \ break; \ } // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE -// if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER=FEATURE from track -#define CHECK_AND_FILL_MFT_COV(FEATURE, GETTER) \ - case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ - inputFeature = mftcov.GETTER(); \ - break; \ +// if so, and if OBJECT.GETTER() is a valid function invocation, +// the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER function +#define CHECK_AND_FILL_FEATURE_OPTIONAL_NO_EXPR(FEATURE, OBJECT, GETTER) \ + case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ + if constexpr (requires(decltype(OBJECT) t) { { t.GETTER() } -> std::convertible_to; }) { \ + inputFeature = (OBJECT.GETTER()); \ + } else { \ + inputFeature = 0; \ + } \ + break; \ } // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE -// if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER1 and GETTER2 from track. -#define CHECK_AND_FILL_MFTMUON_DIFF(FEATURE, GETTER1, GETTER2) \ - case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ - inputFeature = (mft.GETTER2() - muon.GETTER1()); \ - break; \ +// if so, and if OBJECT.FUNC() is a valid function invocation, +// the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER expression +#define CHECK_AND_FILL_FEATURE_OPTIONAL_WITH_EXPR(FEATURE, OBJECT, FUNC, GETTER) \ + case static_cast(InputFeaturesMFTMuonMatch::FEATURE): { \ + if constexpr (requires(decltype(OBJECT) t) { { t.FUNC() } -> std::convertible_to; }) { \ + inputFeature = (GETTER); \ + } else { \ + inputFeature = 0; \ + } \ + break; \ } -// Check if the index of mCachedIndices (index associated to a FEATURE) -// matches the entry in EnumInputFeatures associated to this FEATURE -// if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER=FEATURE from collision -#define CHECK_AND_FILL_MFTMUON_COLLISION(GETTER) \ - case static_cast(InputFeaturesMFTMuonMatch::GETTER): { \ - inputFeature = collision.GETTER(); \ - break; \ - } +#define __EXPAND(x) x +#define __GET_MACRO(_1, _2, _3, _4, name, ...) name +#define CHECK_AND_FILL_FEATURE_OPTIONAL(...) __EXPAND(__GET_MACRO(__VA_ARGS__, CHECK_AND_FILL_FEATURE_OPTIONAL_WITH_EXPR, CHECK_AND_FILL_FEATURE_OPTIONAL_NO_EXPR)(__VA_ARGS__)) namespace o2::analysis { // possible input features for ML enum class InputFeaturesMFTMuonMatch : uint8_t { zMatching, + // MFT track parameters xMFT, yMFT, qOverptMFT, tglMFT, phiMFT, + ptMFT, + etaMFT, + timeMFT, + timeResMFT, + clusterSizesAndTrackFlagsMFT, + trackTypeMFT, dcaXY, dcaZ, chi2MFT, nClustersMFT, + // MCH track parameters xMCH, yMCH, qOverptMCH, tglMCH, phiMCH, + ptMCH, + etaMCH, + timeMCH, + timeResMCH, nClustersMCH, chi2MCH, pdca, Rabs, + // MFT covariances cXXMFT, cXYMFT, cYYMFT, @@ -140,6 +129,7 @@ enum class InputFeaturesMFTMuonMatch : uint8_t { c1PtPhiMFT, c1PtTglMFT, c1Pt21Pt2MFT, + // MCH covariances cXXMCH, cXYMCH, cYYMCH, @@ -155,17 +145,49 @@ enum class InputFeaturesMFTMuonMatch : uint8_t { c1PtPhiMCH, c1PtTglMCH, c1Pt21Pt2MCH, + // track residuals + sameSign, deltaX, deltaY, deltaPhi, + deltaTgl, deltaEta, deltaPt, + deltaR, + deltaDirection, + pullX, + pullY, + pullPhi, + pullTgl, + pullEta, + pullPt, + pullR, + deltaPtRel, + // track residuals (absolute values) + absDeltaX, + absDeltaY, + absDeltaPhi, + absDeltaTgl, + absDeltaEta, + absDeltaPt, + absDeltaR, + absDeltaDirection, + absPullX, + absPullY, + absPullPhi, + absPullTgl, + absPullEta, + absPullPt, + absPullR, + absDeltaPtRel, + // primary vertex parameters posX, posY, posZ, numContrib, trackOccupancyInTimeRange, ft0cOccupancyInTimeRange, + multMFT, multFT0A, multFT0C, multNTracksPV, @@ -177,10 +199,65 @@ enum class InputFeaturesMFTMuonMatch : uint8_t { centFT0M, centFT0A, centFT0C, + // global forward track parameters chi2MCHMFT, - chi2GlobMUON + chi2GlobMUON, + dcaX, + dcaY, + isAmbig }; +template +float getDeltaR(T1 const& mftprop, T2 const& mchprop) +{ + return std::sqrt((mchprop.getX() - mftprop.getX()) * (mchprop.getX() - mftprop.getX()) + (mchprop.getY() - mftprop.getY()) * (mchprop.getY() - mftprop.getY())); +} + +template +float getPullR(T1 const& mftprop, T2 const& mchprop) +{ + double deltaR = getDeltaR(mftprop, mchprop); + double err2X = mftprop.getCovariances()(0, 0) + mchprop.getCovariances()(0, 0); + double err2Y = mftprop.getCovariances()(1, 1) + mchprop.getCovariances()(1, 1); + double errR = std::sqrt(err2X + err2Y); + return deltaR / errR; +} + +template +float getPullPt(T1 const& mftprop, T2 const& mchprop) +{ + double invPtMFT = std::abs(mftprop.getInvQPt()); + double ptMFT = (invPtMFT != 0) ? 1.0 / invPtMFT : 0; + double invPtErr2MFT = mftprop.getCovariances()(4, 4); + double ptErr2MFT = invPtErr2MFT * ptMFT * ptMFT * ptMFT * ptMFT; + + double invPtMCH = std::abs(mchprop.getInvQPt()); + double ptMCH = (invPtMCH != 0) ? 1.0 / invPtMCH : 0; + double invPtErr2MCH = mchprop.getCovariances()(4, 4); + double ptErr2MCH = invPtErr2MCH * ptMCH * ptMCH * ptMCH * ptMCH; + + float delta = ptMCH - ptMFT; + float err = std::sqrt(ptErr2MCH + ptErr2MFT); + return (err > 0 ? delta / err : 0); +} + +template +float getDeltaDirection(T1 const& mftprop, T2 const& mchprop) +{ + double cos2 = std::cos(mchprop.getPhi()) * std::cos(mftprop.getPhi()); + double sin2 = std::sin(mchprop.getPhi()) * std::sin(mftprop.getPhi()); + double tgl2 = mchprop.getTgl() * mftprop.getTgl(); + double cosDelta = (cos2 + sin2 + tgl2) / (std::sqrt(mchprop.getTgl() * mchprop.getTgl() + 1) * std::sqrt(mftprop.getTgl() * mftprop.getTgl() + 1)); + return static_cast(std::acos(std::clamp(cosDelta, -1.0, 1.0))); +} + +float getPull(float mftVal, float mftErr2, float mchVal, float mchErr2) +{ + float delta = mchVal - mftVal; + float err = std::sqrt(mchErr2 + mftErr2); + return (err > 0 ? delta / err : delta); +} + template class MlResponseMFTMuonMatch : public MlResponse { @@ -190,113 +267,135 @@ class MlResponseMFTMuonMatch : public MlResponse /// Default destructor virtual ~MlResponseMFTMuonMatch() = default; - template - float returnFeature(uint8_t idx, T1 const& muon, T2 const& mft, C1 const& muoncov, C2 const& mftcov, U const& collision) - { - float inputFeature = 0.; - switch (idx) { - CHECK_AND_FILL_MFT_TRACK(zMatching, z); - CHECK_AND_FILL_MFT_TRACK(xMFT, x); - CHECK_AND_FILL_MFT_TRACK(yMFT, y); - CHECK_AND_FILL_MFT_TRACK(qOverptMFT, signed1Pt); - CHECK_AND_FILL_MFT_TRACK(tglMFT, tgl); - CHECK_AND_FILL_MFT_TRACK(phiMFT, phi); - CHECK_AND_FILL_MFT_TRACK(chi2MFT, chi2); - CHECK_AND_FILL_MFT_TRACK(nClustersMFT, nClusters); - CHECK_AND_FILL_MUON_TRACK(dcaXY, fwddcaXY); - CHECK_AND_FILL_MUON_TRACK(dcaZ, fwddcaz); - CHECK_AND_FILL_MUON_TRACK(xMCH, x); - CHECK_AND_FILL_MUON_TRACK(yMCH, y); - CHECK_AND_FILL_MUON_TRACK(qOverptMCH, signed1Pt); - CHECK_AND_FILL_MUON_TRACK(tglMCH, tgl); - CHECK_AND_FILL_MUON_TRACK(phiMCH, phi); - CHECK_AND_FILL_MUON_TRACK(nClustersMCH, nClusters); - CHECK_AND_FILL_MUON_TRACK(chi2MCH, chi2); - CHECK_AND_FILL_MUON_TRACK(pdca, pDca); - CHECK_AND_FILL_MFT_COV(cXXMFT, cXX); - CHECK_AND_FILL_MFT_COV(cXYMFT, cXY); - CHECK_AND_FILL_MFT_COV(cYYMFT, cYY); - CHECK_AND_FILL_MFT_COV(cPhiYMFT, cPhiY); - CHECK_AND_FILL_MFT_COV(cPhiXMFT, cPhiX); - CHECK_AND_FILL_MFT_COV(cPhiPhiMFT, cPhiPhi); - CHECK_AND_FILL_MFT_COV(cTglYMFT, cTglY); - CHECK_AND_FILL_MFT_COV(cTglXMFT, cTglX); - CHECK_AND_FILL_MFT_COV(cTglPhiMFT, cTglPhi); - CHECK_AND_FILL_MFT_COV(cTglTglMFT, cTglTgl); - CHECK_AND_FILL_MFT_COV(c1PtYMFT, c1PtY); - CHECK_AND_FILL_MFT_COV(c1PtXMFT, c1PtX); - CHECK_AND_FILL_MFT_COV(c1PtPhiMFT, c1PtPhi); - CHECK_AND_FILL_MFT_COV(c1PtTglMFT, c1PtTgl); - CHECK_AND_FILL_MFT_COV(c1Pt21Pt2MFT, c1Pt21Pt2); - CHECK_AND_FILL_MUON_COV(cXXMCH, cXX); - CHECK_AND_FILL_MUON_COV(cXYMCH, cXY); - CHECK_AND_FILL_MUON_COV(cYYMCH, cYY); - CHECK_AND_FILL_MUON_COV(cPhiYMCH, cPhiY); - CHECK_AND_FILL_MUON_COV(cPhiXMCH, cPhiX); - CHECK_AND_FILL_MUON_COV(cPhiPhiMCH, cPhiPhi); - CHECK_AND_FILL_MUON_COV(cTglYMCH, cTglY); - CHECK_AND_FILL_MUON_COV(cTglXMCH, cTglX); - CHECK_AND_FILL_MUON_COV(cTglPhiMCH, cTglPhi); - CHECK_AND_FILL_MUON_COV(cTglTglMCH, cTglTgl); - CHECK_AND_FILL_MUON_COV(c1PtYMCH, c1PtY); - CHECK_AND_FILL_MUON_COV(c1PtXMCH, c1PtX); - CHECK_AND_FILL_MUON_COV(c1PtPhiMCH, c1PtPhi); - CHECK_AND_FILL_MUON_COV(c1PtTglMCH, c1PtTgl); - CHECK_AND_FILL_MUON_COV(c1Pt21Pt2MCH, c1Pt21Pt2); - CHECK_AND_FILL_MFTMUON_COLLISION(posX); - CHECK_AND_FILL_MFTMUON_COLLISION(posY); - CHECK_AND_FILL_MFTMUON_COLLISION(posZ); - CHECK_AND_FILL_MFTMUON_COLLISION(numContrib); - CHECK_AND_FILL_MFTMUON_COLLISION(trackOccupancyInTimeRange); - CHECK_AND_FILL_MFTMUON_COLLISION(ft0cOccupancyInTimeRange); - CHECK_AND_FILL_MFTMUON_COLLISION(multFT0A); - CHECK_AND_FILL_MFTMUON_COLLISION(multFT0C); - CHECK_AND_FILL_MFTMUON_COLLISION(multNTracksPV); - CHECK_AND_FILL_MFTMUON_COLLISION(multNTracksPVeta1); - CHECK_AND_FILL_MFTMUON_COLLISION(multNTracksPVetaHalf); - CHECK_AND_FILL_MFTMUON_COLLISION(isInelGt0); - CHECK_AND_FILL_MFTMUON_COLLISION(isInelGt1); - CHECK_AND_FILL_MFTMUON_COLLISION(multFT0M); - CHECK_AND_FILL_MFTMUON_COLLISION(centFT0M); - CHECK_AND_FILL_MFTMUON_COLLISION(centFT0A); - CHECK_AND_FILL_MFTMUON_COLLISION(centFT0C); - CHECK_AND_FILL_MUON_TRACK(chi2MCHMFT, chi2MatchMCHMFT); - } - return inputFeature; - } - - template - float returnFeatureGlob(uint8_t idx, T1 const& muonglob, T2 const& muon, T3 const& mft, U const& collision) - { - float inputFeature = 0.; - switch (idx) { - CHECK_AND_FILL_MFT_TRACK(xMFT, getX); - CHECK_AND_FILL_MFT_TRACK(yMFT, getY); - CHECK_AND_FILL_MFT_TRACK(qOverptMFT, getInvQPt); - CHECK_AND_FILL_MFT_TRACK(tglMFT, getTanl); - CHECK_AND_FILL_MFT_TRACK(phiMFT, getPhi); - CHECK_AND_FILL_MFT_TRACK(chi2MFT, getTrackChi2); - CHECK_AND_FILL_MUON_TRACK(xMCH, getX); - CHECK_AND_FILL_MUON_TRACK(yMCH, getY); - CHECK_AND_FILL_MUON_TRACK(qOverptMCH, getInvQPt); - CHECK_AND_FILL_MUON_TRACK(tglMCH, getTanl); - CHECK_AND_FILL_MUON_TRACK(phiMCH, getPhi); - CHECK_AND_FILL_MUON_TRACK(chi2MCH, getTrackChi2); - CHECK_AND_FILL_MUONGLOB_TRACK(chi2MCHMFT, chi2MatchMCHMFT); - CHECK_AND_FILL_MUONGLOB_TRACK(chi2GlobMUON, chi2); - CHECK_AND_FILL_MUONGLOB_TRACK(Rabs, rAtAbsorberEnd); - // Below are dummy files to remove warning of unused parameters - CHECK_AND_FILL_MFTMUON_COLLISION(posZ); - } - return inputFeature; - } - - template - float returnFeatureTest(uint8_t idx, T1 const& muon) + template + float returnFeature(uint8_t idx, T1 const& muon, T2 const& mft, T3 const& mch, T4 const& mftprop, T5 const& mchprop, U const& collision) { float inputFeature = 0.; switch (idx) { - CHECK_AND_FILL_MUON_TRACK(chi2MCHMFT, chi2MatchMCHMFT); + // matching parameters + CHECK_AND_FILL_FEATURE(zMatching, mftprop.getZ()); + // MFT track parameters + CHECK_AND_FILL_FEATURE(xMFT, mftprop.getX()); + CHECK_AND_FILL_FEATURE(yMFT, mftprop.getY()); + CHECK_AND_FILL_FEATURE(qOverptMFT, mftprop.getInvQPt()); + CHECK_AND_FILL_FEATURE(tglMFT, mftprop.getTanl()); + CHECK_AND_FILL_FEATURE(phiMFT, mftprop.getPhi()); + CHECK_AND_FILL_FEATURE(chi2MFT, mft.chi2()); + CHECK_AND_FILL_FEATURE(nClustersMFT, mft.nClusters()); + /*dummy value*/ CHECK_AND_FILL_FEATURE(dcaXY, 0); + /*dummy value*/ CHECK_AND_FILL_FEATURE(dcaZ, 0); + CHECK_AND_FILL_FEATURE(ptMFT, mftprop.getPt()); + CHECK_AND_FILL_FEATURE(etaMFT, mftprop.getEta()); + CHECK_AND_FILL_FEATURE(timeMFT, mft.trackTime()); + CHECK_AND_FILL_FEATURE(timeResMFT, mft.trackTimeRes()); + CHECK_AND_FILL_FEATURE(clusterSizesAndTrackFlagsMFT, mft.mftClusterSizesAndTrackFlags()); + CHECK_AND_FILL_FEATURE(trackTypeMFT, (mft.isCA() ? 1 : 0)); + // MCH track parameters + CHECK_AND_FILL_FEATURE(xMCH, mchprop.getX()); + CHECK_AND_FILL_FEATURE(yMCH, mchprop.getY()); + CHECK_AND_FILL_FEATURE(qOverptMCH, mchprop.getInvQPt()); + CHECK_AND_FILL_FEATURE(tglMCH, mchprop.getTanl()); + CHECK_AND_FILL_FEATURE(phiMCH, mchprop.getPhi()); + CHECK_AND_FILL_FEATURE(ptMCH, mchprop.getPt()); + CHECK_AND_FILL_FEATURE(etaMCH, mchprop.getEta()); + CHECK_AND_FILL_FEATURE(timeMCH, mch.trackTime()); + CHECK_AND_FILL_FEATURE(timeResMCH, mch.trackTimeRes()); + CHECK_AND_FILL_FEATURE(nClustersMCH, mch.nClusters()); + CHECK_AND_FILL_FEATURE(chi2MCH, mch.chi2()); + CHECK_AND_FILL_FEATURE(pdca, muon.pDca()); + CHECK_AND_FILL_FEATURE(Rabs, muon.rAtAbsorberEnd()); + // MFT covariances + CHECK_AND_FILL_FEATURE(cXXMFT, mftprop.getCovariances()(0, 0)); + CHECK_AND_FILL_FEATURE(cXYMFT, mftprop.getCovariances()(0, 1)); + CHECK_AND_FILL_FEATURE(cYYMFT, mftprop.getCovariances()(1, 1)); + CHECK_AND_FILL_FEATURE(cPhiXMFT, mftprop.getCovariances()(0, 2)); + CHECK_AND_FILL_FEATURE(cPhiYMFT, mftprop.getCovariances()(1, 2)); + CHECK_AND_FILL_FEATURE(cPhiPhiMFT, mftprop.getCovariances()(2, 2)); + CHECK_AND_FILL_FEATURE(cTglXMFT, mftprop.getCovariances()(0, 3)); + CHECK_AND_FILL_FEATURE(cTglYMFT, mftprop.getCovariances()(1, 3)); + CHECK_AND_FILL_FEATURE(cTglPhiMFT, mftprop.getCovariances()(2, 3)); + CHECK_AND_FILL_FEATURE(cTglTglMFT, mftprop.getCovariances()(3, 3)); + CHECK_AND_FILL_FEATURE(c1PtXMFT, mftprop.getCovariances()(0, 4)); + CHECK_AND_FILL_FEATURE(c1PtYMFT, mftprop.getCovariances()(1, 4)); + CHECK_AND_FILL_FEATURE(c1PtPhiMFT, mftprop.getCovariances()(2, 4)); + CHECK_AND_FILL_FEATURE(c1PtTglMFT, mftprop.getCovariances()(3, 4)); + CHECK_AND_FILL_FEATURE(c1Pt21Pt2MFT, mftprop.getCovariances()(4, 4)); + // MCH covariances + CHECK_AND_FILL_FEATURE(cXXMCH, mchprop.getCovariances()(0, 0)); + CHECK_AND_FILL_FEATURE(cXYMCH, mchprop.getCovariances()(0, 1)); + CHECK_AND_FILL_FEATURE(cYYMCH, mchprop.getCovariances()(1, 1)); + CHECK_AND_FILL_FEATURE(cPhiXMCH, mchprop.getCovariances()(0, 2)); + CHECK_AND_FILL_FEATURE(cPhiYMCH, mchprop.getCovariances()(1, 2)); + CHECK_AND_FILL_FEATURE(cPhiPhiMCH, mchprop.getCovariances()(2, 2)); + CHECK_AND_FILL_FEATURE(cTglXMCH, mchprop.getCovariances()(0, 3)); + CHECK_AND_FILL_FEATURE(cTglYMCH, mchprop.getCovariances()(1, 3)); + CHECK_AND_FILL_FEATURE(cTglPhiMCH, mchprop.getCovariances()(2, 3)); + CHECK_AND_FILL_FEATURE(cTglTglMCH, mchprop.getCovariances()(3, 3)); + CHECK_AND_FILL_FEATURE(c1PtXMCH, mchprop.getCovariances()(0, 4)); + CHECK_AND_FILL_FEATURE(c1PtYMCH, mchprop.getCovariances()(1, 4)); + CHECK_AND_FILL_FEATURE(c1PtPhiMCH, mchprop.getCovariances()(2, 4)); + CHECK_AND_FILL_FEATURE(c1PtTglMCH, mchprop.getCovariances()(3, 4)); + CHECK_AND_FILL_FEATURE(c1Pt21Pt2MCH, mchprop.getCovariances()(4, 4)); + // Track residuals + CHECK_AND_FILL_FEATURE(sameSign, (mch.sign() == mft.sign()) ? 1 : 0); + CHECK_AND_FILL_FEATURE(deltaX, mchprop.getX() - mftprop.getX()); + CHECK_AND_FILL_FEATURE(deltaY, mchprop.getY() - mftprop.getY()); + CHECK_AND_FILL_FEATURE(deltaPhi, mchprop.getPhi() - mftprop.getPhi()); + CHECK_AND_FILL_FEATURE(deltaTgl, mchprop.getTgl() - mftprop.getTgl()); + CHECK_AND_FILL_FEATURE(deltaEta, mchprop.getEta() - mftprop.getEta()); + CHECK_AND_FILL_FEATURE(deltaPt, mchprop.getPt() - mftprop.getPt()); + CHECK_AND_FILL_FEATURE(deltaR, getDeltaR(mftprop, mchprop)); + CHECK_AND_FILL_FEATURE(deltaDirection, getDeltaDirection(mftprop, mchprop)); + CHECK_AND_FILL_FEATURE(deltaPtRel, (mchprop.getPt() - mftprop.getPt()) / (mchprop.getPt() + mftprop.getPt())); + CHECK_AND_FILL_FEATURE(pullX, getPull(mftprop.getX(), mftprop.getCovariances()(0, 0), mchprop.getX(), mchprop.getCovariances()(0, 0))); + CHECK_AND_FILL_FEATURE(pullY, getPull(mftprop.getY(), mftprop.getCovariances()(1, 1), mchprop.getY(), mchprop.getCovariances()(1, 1))); + CHECK_AND_FILL_FEATURE(pullPhi, getPull(mftprop.getPhi(), mftprop.getCovariances()(2, 2), mchprop.getPhi(), mchprop.getCovariances()(2, 2))); + CHECK_AND_FILL_FEATURE(pullTgl, getPull(mftprop.getTgl(), mftprop.getCovariances()(3, 3), mchprop.getTgl(), mchprop.getCovariances()(3, 3))); + /*dummy value*/ CHECK_AND_FILL_FEATURE(pullEta, 0); + CHECK_AND_FILL_FEATURE(pullPt, getPullPt(mftprop, mchprop)); + CHECK_AND_FILL_FEATURE(pullR, getPullR(mftprop, mchprop)); + // Track residuals (absolute values) + CHECK_AND_FILL_FEATURE(absDeltaX, std::fabs(mchprop.getX() - mftprop.getX())); + CHECK_AND_FILL_FEATURE(absDeltaY, std::fabs(mchprop.getY() - mftprop.getY())); + CHECK_AND_FILL_FEATURE(absDeltaPhi, std::fabs(mchprop.getPhi() - mftprop.getPhi())); + CHECK_AND_FILL_FEATURE(absDeltaTgl, std::fabs(mchprop.getTgl() - mftprop.getTgl())); + CHECK_AND_FILL_FEATURE(absDeltaEta, std::fabs(mchprop.getEta() - mftprop.getEta())); + CHECK_AND_FILL_FEATURE(absDeltaPt, std::fabs(mchprop.getPt() - mftprop.getPt())); + CHECK_AND_FILL_FEATURE(absDeltaR, std::fabs(getDeltaR(mftprop, mchprop))); + CHECK_AND_FILL_FEATURE(absDeltaDirection, std::fabs(getDeltaDirection(mftprop, mchprop))); + CHECK_AND_FILL_FEATURE(absDeltaPtRel, std::fabs((mchprop.getPt() - mftprop.getPt()) / (mchprop.getPt() + mftprop.getPt()))); + CHECK_AND_FILL_FEATURE(absPullX, std::fabs(getPull(mftprop.getX(), mftprop.getCovariances()(0, 0), mchprop.getX(), mchprop.getCovariances()(0, 0)))); + CHECK_AND_FILL_FEATURE(absPullY, std::fabs(getPull(mftprop.getY(), mftprop.getCovariances()(1, 1), mchprop.getY(), mchprop.getCovariances()(1, 1)))); + CHECK_AND_FILL_FEATURE(absPullPhi, std::fabs(getPull(mftprop.getPhi(), mftprop.getCovariances()(2, 2), mchprop.getPhi(), mchprop.getCovariances()(2, 2)))); + CHECK_AND_FILL_FEATURE(absPullTgl, std::fabs(getPull(mftprop.getTgl(), mftprop.getCovariances()(3, 3), mchprop.getTgl(), mchprop.getCovariances()(3, 3)))); + /*dummy value*/ CHECK_AND_FILL_FEATURE(absPullEta, 0); + CHECK_AND_FILL_FEATURE(absPullPt, std::fabs(getPullPt(mftprop, mchprop))); + CHECK_AND_FILL_FEATURE(absPullR, std::fabs(getPullR(mftprop, mchprop))); + // primary vertex parameters + CHECK_AND_FILL_FEATURE(posX, collision.posX()); + CHECK_AND_FILL_FEATURE(posY, collision.posY()); + CHECK_AND_FILL_FEATURE(posZ, collision.posZ()); + CHECK_AND_FILL_FEATURE_OPTIONAL(numContrib, collision, numContrib); + CHECK_AND_FILL_FEATURE_OPTIONAL(trackOccupancyInTimeRange, collision, trackOccupancyInTimeRange); + CHECK_AND_FILL_FEATURE_OPTIONAL(ft0cOccupancyInTimeRange, collision, ft0cOccupancyInTimeRange); + CHECK_AND_FILL_FEATURE_OPTIONAL(multMFT, collision, mftNtracks); + CHECK_AND_FILL_FEATURE_OPTIONAL(multFT0A, collision, multFT0A); + CHECK_AND_FILL_FEATURE_OPTIONAL(multFT0C, collision, multFT0C); + CHECK_AND_FILL_FEATURE_OPTIONAL(multNTracksPV, collision, multNTracksPV); + CHECK_AND_FILL_FEATURE_OPTIONAL(multNTracksPVeta1, collision, multNTracksPVeta1); + CHECK_AND_FILL_FEATURE_OPTIONAL(multNTracksPVetaHalf, collision, multNTracksPVetaHalf); + CHECK_AND_FILL_FEATURE_OPTIONAL(isInelGt0, collision, isInelGt0); + CHECK_AND_FILL_FEATURE_OPTIONAL(isInelGt1, collision, isInelGt1); + CHECK_AND_FILL_FEATURE_OPTIONAL(multFT0M, collision, multFT0M); + CHECK_AND_FILL_FEATURE_OPTIONAL(centFT0M, collision, centFT0M); + CHECK_AND_FILL_FEATURE_OPTIONAL(centFT0A, collision, centFT0A); + CHECK_AND_FILL_FEATURE_OPTIONAL(centFT0C, collision, centFT0C); + // global forward track parameters + CHECK_AND_FILL_FEATURE(chi2MCHMFT, muon.chi2MatchMCHMFT()); + CHECK_AND_FILL_FEATURE(chi2GlobMUON, muon.chi2()); + CHECK_AND_FILL_FEATURE_OPTIONAL(dcaX, muon, fwdDcaX); + CHECK_AND_FILL_FEATURE_OPTIONAL(dcaY, muon, fwdDcaY); + CHECK_AND_FILL_FEATURE_OPTIONAL(isAmbig, muon, compatibleCollIds, (muon.compatibleCollIds().size() == 1) ? 0 : 1); } return inputFeature; } @@ -304,34 +403,12 @@ class MlResponseMFTMuonMatch : public MlResponse /// Method to get the input features vector needed for ML inference /// \param track is the single track, \param collision is the collision /// \return inputFeatures vector - template - std::vector getInputFeatures(T1 const& muon, T2 const& mft, C1 const& muoncov, C2 const& mftcov, U const& collision) - { - std::vector inputFeatures; - for (const auto& idx : MlResponse::mCachedIndices) { - float inputFeature = returnFeature(idx, muon, mft, muoncov, mftcov, collision); - inputFeatures.emplace_back(inputFeature); - } - return inputFeatures; - } - - template - std::vector getInputFeaturesTest(T1 const& muon) - { - std::vector inputFeatures; - for (const auto& idx : MlResponse::mCachedIndices) { - float inputFeature = returnFeatureTest(idx, muon); - inputFeatures.emplace_back(inputFeature); - } - return inputFeatures; - } - - template - std::vector getInputFeaturesGlob(T1 const& muonglob, T2 const& muon, T3 const& mft, U const& collision) + template + std::vector getInputFeatures(T1 const& muon, T2 const& mft, T3 const& mch, T4 const& mftprop, T5 const& mchprop, U const& collision) { std::vector inputFeatures; for (const auto& idx : MlResponse::mCachedIndices) { - float inputFeature = returnFeatureGlob(idx, muonglob, muon, mft, collision); + float inputFeature = returnFeature(idx, muon, mft, mch, mftprop, mchprop, collision); inputFeatures.emplace_back(inputFeature); } return inputFeatures; @@ -361,25 +438,39 @@ class MlResponseMFTMuonMatch : public MlResponse void setAvailableInputFeatures() { MlResponse::mAvailableInputFeatures = { + // matching parameters FILL_MAP_MFTMUON_MATCH(zMatching), + // MFT track parameters FILL_MAP_MFTMUON_MATCH(xMFT), FILL_MAP_MFTMUON_MATCH(yMFT), FILL_MAP_MFTMUON_MATCH(qOverptMFT), FILL_MAP_MFTMUON_MATCH(tglMFT), FILL_MAP_MFTMUON_MATCH(phiMFT), + FILL_MAP_MFTMUON_MATCH(ptMFT), + FILL_MAP_MFTMUON_MATCH(etaMFT), + FILL_MAP_MFTMUON_MATCH(timeMFT), + FILL_MAP_MFTMUON_MATCH(timeResMFT), FILL_MAP_MFTMUON_MATCH(dcaXY), FILL_MAP_MFTMUON_MATCH(dcaZ), FILL_MAP_MFTMUON_MATCH(chi2MFT), + FILL_MAP_MFTMUON_MATCH(clusterSizesAndTrackFlagsMFT), + FILL_MAP_MFTMUON_MATCH(trackTypeMFT), FILL_MAP_MFTMUON_MATCH(nClustersMFT), + // MCH track parameters FILL_MAP_MFTMUON_MATCH(xMCH), FILL_MAP_MFTMUON_MATCH(yMCH), FILL_MAP_MFTMUON_MATCH(qOverptMCH), FILL_MAP_MFTMUON_MATCH(tglMCH), FILL_MAP_MFTMUON_MATCH(phiMCH), FILL_MAP_MFTMUON_MATCH(nClustersMCH), + FILL_MAP_MFTMUON_MATCH(ptMCH), + FILL_MAP_MFTMUON_MATCH(etaMCH), + FILL_MAP_MFTMUON_MATCH(timeMCH), + FILL_MAP_MFTMUON_MATCH(timeResMCH), FILL_MAP_MFTMUON_MATCH(chi2MCH), FILL_MAP_MFTMUON_MATCH(pdca), FILL_MAP_MFTMUON_MATCH(Rabs), + // MFT covariances FILL_MAP_MFTMUON_MATCH(cXXMFT), FILL_MAP_MFTMUON_MATCH(cXYMFT), FILL_MAP_MFTMUON_MATCH(cYYMFT), @@ -395,6 +486,7 @@ class MlResponseMFTMuonMatch : public MlResponse FILL_MAP_MFTMUON_MATCH(c1PtPhiMFT), FILL_MAP_MFTMUON_MATCH(c1PtTglMFT), FILL_MAP_MFTMUON_MATCH(c1Pt21Pt2MFT), + // MCH covariances FILL_MAP_MFTMUON_MATCH(cXXMCH), FILL_MAP_MFTMUON_MATCH(cXYMCH), FILL_MAP_MFTMUON_MATCH(cYYMCH), @@ -410,8 +502,66 @@ class MlResponseMFTMuonMatch : public MlResponse FILL_MAP_MFTMUON_MATCH(c1PtPhiMCH), FILL_MAP_MFTMUON_MATCH(c1PtTglMCH), FILL_MAP_MFTMUON_MATCH(c1Pt21Pt2MCH), + // track residuals + FILL_MAP_MFTMUON_MATCH(sameSign), + FILL_MAP_MFTMUON_MATCH(deltaX), + FILL_MAP_MFTMUON_MATCH(deltaY), + FILL_MAP_MFTMUON_MATCH(deltaPhi), + FILL_MAP_MFTMUON_MATCH(deltaTgl), + FILL_MAP_MFTMUON_MATCH(deltaEta), + FILL_MAP_MFTMUON_MATCH(deltaPt), + FILL_MAP_MFTMUON_MATCH(deltaR), + FILL_MAP_MFTMUON_MATCH(deltaDirection), + FILL_MAP_MFTMUON_MATCH(pullX), + FILL_MAP_MFTMUON_MATCH(pullY), + FILL_MAP_MFTMUON_MATCH(pullPhi), + FILL_MAP_MFTMUON_MATCH(pullTgl), + FILL_MAP_MFTMUON_MATCH(pullEta), + FILL_MAP_MFTMUON_MATCH(pullPt), + FILL_MAP_MFTMUON_MATCH(pullR), + FILL_MAP_MFTMUON_MATCH(deltaPtRel), + // track residuals (absolute values) + FILL_MAP_MFTMUON_MATCH(absDeltaX), + FILL_MAP_MFTMUON_MATCH(absDeltaY), + FILL_MAP_MFTMUON_MATCH(absDeltaPhi), + FILL_MAP_MFTMUON_MATCH(absDeltaTgl), + FILL_MAP_MFTMUON_MATCH(absDeltaEta), + FILL_MAP_MFTMUON_MATCH(absDeltaPt), + FILL_MAP_MFTMUON_MATCH(absDeltaR), + FILL_MAP_MFTMUON_MATCH(absDeltaDirection), + FILL_MAP_MFTMUON_MATCH(absPullX), + FILL_MAP_MFTMUON_MATCH(absPullY), + FILL_MAP_MFTMUON_MATCH(absPullPhi), + FILL_MAP_MFTMUON_MATCH(absPullTgl), + FILL_MAP_MFTMUON_MATCH(absPullEta), + FILL_MAP_MFTMUON_MATCH(absPullPt), + FILL_MAP_MFTMUON_MATCH(absPullR), + FILL_MAP_MFTMUON_MATCH(absDeltaPtRel), + // primary vertex parameters + FILL_MAP_MFTMUON_MATCH(posX), + FILL_MAP_MFTMUON_MATCH(posY), + FILL_MAP_MFTMUON_MATCH(posZ), + FILL_MAP_MFTMUON_MATCH(numContrib), + FILL_MAP_MFTMUON_MATCH(trackOccupancyInTimeRange), + FILL_MAP_MFTMUON_MATCH(ft0cOccupancyInTimeRange), + FILL_MAP_MFTMUON_MATCH(multMFT), + FILL_MAP_MFTMUON_MATCH(multFT0A), + FILL_MAP_MFTMUON_MATCH(multFT0C), + FILL_MAP_MFTMUON_MATCH(multNTracksPV), + FILL_MAP_MFTMUON_MATCH(multNTracksPVeta1), + FILL_MAP_MFTMUON_MATCH(multNTracksPVetaHalf), + FILL_MAP_MFTMUON_MATCH(isInelGt0), + FILL_MAP_MFTMUON_MATCH(isInelGt1), + FILL_MAP_MFTMUON_MATCH(multFT0M), + FILL_MAP_MFTMUON_MATCH(centFT0M), + FILL_MAP_MFTMUON_MATCH(centFT0A), + FILL_MAP_MFTMUON_MATCH(centFT0C), + // global forward track parameters FILL_MAP_MFTMUON_MATCH(chi2MCHMFT), - FILL_MAP_MFTMUON_MATCH(chi2GlobMUON)}; + FILL_MAP_MFTMUON_MATCH(chi2GlobMUON), + FILL_MAP_MFTMUON_MATCH(dcaX), + FILL_MAP_MFTMUON_MATCH(dcaY), + FILL_MAP_MFTMUON_MATCH(isAmbig)}; } uint8_t mCachedIndexBinning; // index correspondance between configurable and available input features diff --git a/PWGDQ/Core/VarManager.cxx b/PWGDQ/Core/VarManager.cxx index 68e65dd9cfe..445dabf000c 100644 --- a/PWGDQ/Core/VarManager.cxx +++ b/PWGDQ/Core/VarManager.cxx @@ -11,9 +11,11 @@ #include "PWGDQ/Core/VarManager.h" +#include "Common/Core/RecoDecay.h" #include "Tools/KFparticle/KFUtilities.h" #include +#include #include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include #include #include @@ -33,9 +36,12 @@ #include #include +#include +#include #include #include #include +#include #include #include #include @@ -43,8 +49,6 @@ using namespace o2::constants::physics; -ClassImp(VarManager); - TString VarManager::fgVariableNames[VarManager::kNVars] = {""}; TString VarManager::fgVariableUnits[VarManager::kNVars] = {""}; std::map VarManager::fgVarNamesMap; @@ -53,7 +57,13 @@ bool VarManager::fgUsedKF = false; bool VarManager::fgPVrecalKF = true; float VarManager::fgMagField = 0.5; float VarManager::fgzMatching = -77.5; +float VarManager::fgxShiftFwd = 0.0; +float VarManager::fgyShiftFwd = 0.0; float VarManager::fgzShiftFwd = 0.0; +bool VarManager::fgUseTopBottomShift = false; +float VarManager::fgxShiftFwdBottom = 0.0; +float VarManager::fgyShiftFwdBottom = 0.0; +float VarManager::fgzShiftFwdBottom = 0.0; float VarManager::fgValues[VarManager::kNVars] = {0.0f}; float VarManager::fgTPCInterSectorBoundary = 1.0; // cm int VarManager::fgITSROFbias = 0; @@ -71,10 +81,14 @@ o2::vertexing::FwdDCAFitterN<2> VarManager::fgFitterTwoProngFwd; o2::vertexing::FwdDCAFitterN<3> VarManager::fgFitterThreeProngFwd; o2::globaltracking::MatchGlobalFwd VarManager::mMatching; std::map VarManager::fgCalibs; -bool VarManager::fgRunTPCPostCalibration[4] = {false, false, false, false}; +std::array VarManager::fgRunTPCPostCalibration = {false, false, false, false}; int VarManager::fgCalibrationType = 0; // 0 - no calibration, 1 - calibration vs (TPCncls,pIN,eta) typically for pp, 2 - calibration vs (eta,nPV,nLong,tLong) typically for PbPb bool VarManager::fgUseInterpolatedCalibration = true; // use interpolated calibration histograms (default: true) - +int VarManager::fgEfficiencyType = 0; // type of efficiency to be applied, default is no efficiency +TObject* VarManager::fgEfficiencyHist = nullptr; // histogram for efficiency +TObject* VarManager::fgPosiPhiMap = nullptr; +TObject* VarManager::fgNegaPhiMap = nullptr; +bool VarManager::fgUsePhiCorrection = false; //__________________________________________________________________ VarManager::VarManager() : TObject() { @@ -138,7 +152,7 @@ void VarManager::ResetValues(int startValue, int endValue, float* values) } //__________________________________________________________________ -void VarManager::SetCollisionSystem(TString system, float energy) +void VarManager::SetCollisionSystem(const TString& system, float energy) { // // Set the collision system and the center of mass energy @@ -191,8 +205,8 @@ void VarManager::SetCollisionSystem(TString system, float energy) // TO Do: add more systems // set the beam 4-momentum vectors - float beamAEnergy = energy / 2.0 * sqrt(NumberOfProtonsA * NumberOfProtonsC / NumberOfProtonsC / NumberOfProtonsA); // GeV - float beamCEnergy = energy / 2.0 * sqrt(NumberOfProtonsC * NumberOfProtonsA / NumberOfProtonsA / NumberOfProtonsC); // GeV + float beamAEnergy = energy / 2.0f * std::sqrt(static_cast(NumberOfProtonsA) * NumberOfProtonsC / NumberOfProtonsC / NumberOfProtonsA); // GeV + float beamCEnergy = energy / 2.0f * std::sqrt(static_cast(NumberOfProtonsC) * NumberOfProtonsA / NumberOfProtonsA / NumberOfProtonsC); // GeV float beamAMomentum = std::sqrt(beamAEnergy * beamAEnergy - NumberOfNucleonsA * NumberOfNucleonsA * MassProton * MassProton); float beamCMomentum = std::sqrt(beamCEnergy * beamCEnergy - NumberOfNucleonsC * NumberOfNucleonsC * MassProton * MassProton); fgBeamA.SetPxPyPzE(0, 0, beamAMomentum, beamAEnergy); @@ -234,7 +248,7 @@ void VarManager::FillTrackDerived(float* values) } //__________________________________________________________________ -float VarManager::calculateCosPA(KFParticle kfp, KFParticle PV) +float VarManager::calculateCosPA(const KFParticle& kfp, const KFParticle& PV) { return cpaFromKF(kfp, PV); } @@ -247,7 +261,8 @@ double VarManager::ComputePIDcalibration(int species, double nSigmaValue) if (fgCalibrationType == 1) { // get the calibration histograms - CalibObjects calibMean, calibSigma; + CalibObjects calibMean = kTPCElectronMean; + CalibObjects calibSigma = kTPCElectronSigma; switch (species) { case 0: calibMean = kTPCElectronMean; @@ -270,8 +285,8 @@ double VarManager::ComputePIDcalibration(int species, double nSigmaValue) return -999.0; // Return zero if species is invalid } - TH3F* calibMeanHist = reinterpret_cast(fgCalibs[calibMean]); - TH3F* calibSigmaHist = reinterpret_cast(fgCalibs[calibSigma]); + TH3F* calibMeanHist = dynamic_cast(fgCalibs[calibMean]); + TH3F* calibSigmaHist = dynamic_cast(fgCalibs[calibSigma]); if (!calibMeanHist || !calibSigmaHist) { LOG(fatal) << "Calibration histograms not found for species: " << species; return -999.0; // Return zero if histograms are not found @@ -293,7 +308,9 @@ double VarManager::ComputePIDcalibration(int species, double nSigmaValue) return (nSigmaValue - mean) / sigma; // Return the calibrated nSigma value } else if (fgCalibrationType == 2) { // get the calibration histograms - CalibObjects calibMean, calibSigma, calibStatus; + CalibObjects calibMean = kTPCElectronMean; + CalibObjects calibSigma = kTPCElectronSigma; + CalibObjects calibStatus = kTPCElectronStatus; switch (species) { case 0: calibMean = kTPCElectronMean; @@ -320,9 +337,9 @@ double VarManager::ComputePIDcalibration(int species, double nSigmaValue) return -999.0; // Return zero if species is invalid } - THnF* calibMeanHist = reinterpret_cast(fgCalibs[calibMean]); - THnF* calibSigmaHist = reinterpret_cast(fgCalibs[calibSigma]); - THnF* calibStatusHist = reinterpret_cast(fgCalibs[calibStatus]); + THnF* calibMeanHist = dynamic_cast(fgCalibs[calibMean]); + THnF* calibSigmaHist = dynamic_cast(fgCalibs[calibSigma]); + THnF* calibStatusHist = dynamic_cast(fgCalibs[calibStatus]); if (!calibMeanHist || !calibSigmaHist || !calibStatusHist) { LOG(fatal) << "Calibration histograms not found for species: " << species; return -999.0; // Return zero if histograms are not found @@ -342,17 +359,18 @@ double VarManager::ComputePIDcalibration(int species, double nSigmaValue) binTlong = (binTlong == 0 ? 1 : binTlong); binTlong = (binTlong > calibMeanHist->GetAxis(3)->GetNbins() ? calibMeanHist->GetAxis(3)->GetNbins() : binTlong); - int bin[4] = {binEta, binNpv, binNlong, binTlong}; - int status = static_cast(calibStatusHist->GetBinContent(bin)); - double mean = calibMeanHist->GetBinContent(bin); - double sigma = calibSigmaHist->GetBinContent(bin); + std::array bin{binEta, binNpv, binNlong, binTlong}; + int status = static_cast(calibStatusHist->GetBinContent(bin.data())); + double mean = calibMeanHist->GetBinContent(bin.data()); + double sigma = calibSigmaHist->GetBinContent(bin.data()); switch (status) { case 0: // good calibration, return the calibrated nSigma value return (nSigmaValue - mean) / sigma; break; case 1: - // calibration not valid, return the original nSigma value + case 4: + // calibration not valid or interpolation failed, return the original nSigma value return nSigmaValue; break; case 2: // calibration constant has poor stat uncertainty, consider the user option for what to do @@ -365,10 +383,6 @@ double VarManager::ComputePIDcalibration(int species, double nSigmaValue) return nSigmaValue; } break; - case 4: - // calibration constants interpolation failed, return the original nSigma value - return nSigmaValue; - break; default: return nSigmaValue; // unknown status, return the original nSigma value break; @@ -380,6 +394,146 @@ double VarManager::ComputePIDcalibration(int species, double nSigmaValue) } } +//__________________________________________________________________ +void VarManager::SetEfficiencyObject(int efficiencyType, TObject* obj) +{ + // check the type of the efficiency object and set it accordingly + if (efficiencyType >= kNEfficiencyTypes || efficiencyType < 0) { + LOG(warning) << "SetEfficiencyObject: unknown efficiency type " << efficiencyType; + return; + } + + // set the efficiency type + fgEfficiencyType = efficiencyType; + // set the efficiency object + fgEfficiencyHist = obj; +} + +void VarManager::FillEfficiency(float* values) +{ + // depending on the efficiency type, we use different types of efficiency histograms and different variables to get the efficiency value + if (!values) { + values = fgValues; + } + + if (fgEfficiencyType == kNone) { + values[kPairEfficiency] = 1.0; // if no efficiency is to be applied, set the efficiency value to 1 + values[kPairWeight] = 1.0; // set the weight to 1 + } else if (fgEfficiencyType == kPairPtCentFT0cCosThetaStarFT0c) { + if (!fgEfficiencyHist) { + LOG(fatal) << "efficiency histogram not set"; + return; + } + TH3F* efficiencyHist = dynamic_cast(fgEfficiencyHist); + // Get the bin indices for the efficiency histogram + int binPt = efficiencyHist->GetXaxis()->FindBin(values[kPt]); + binPt = (binPt == 0 ? 1 : binPt); + binPt = (binPt > efficiencyHist->GetXaxis()->GetNbins() ? efficiencyHist->GetXaxis()->GetNbins() : binPt); + int binCent = efficiencyHist->GetYaxis()->FindBin(values[kCentFT0C]); + binCent = (binCent == 0 ? 1 : binCent); + binCent = (binCent > efficiencyHist->GetYaxis()->GetNbins() ? efficiencyHist->GetYaxis()->GetNbins() : binCent); + int binCosThetaStarFT0c = efficiencyHist->GetZaxis()->FindBin(values[kCosThetaStarFT0C]); + binCosThetaStarFT0c = (binCosThetaStarFT0c == 0 ? 1 : binCosThetaStarFT0c); + binCosThetaStarFT0c = (binCosThetaStarFT0c > efficiencyHist->GetZaxis()->GetNbins() ? efficiencyHist->GetZaxis()->GetNbins() : binCosThetaStarFT0c); + + // get the efficiency value from the histogram + values[kPairEfficiency] = efficiencyHist->GetBinContent(binPt, binCent, binCosThetaStarFT0c); + values[kPairWeight] = 1.0 / (values[kPairEfficiency] > 0 ? values[kPairEfficiency] : 1.0); // set the weight as the inverse of the efficiency, but avoid division by zero + } else if (fgEfficiencyType == kPairPtCentFT0cCosThetaStarRandom) { + if (!fgEfficiencyHist) { + LOG(fatal) << "efficiency histogram not set"; + return; + } + TH3F* efficiencyHist = dynamic_cast(fgEfficiencyHist); + // Get the bin indices for the efficiency histogram + int binPt = efficiencyHist->GetXaxis()->FindBin(values[kPt]); + binPt = (binPt == 0 ? 1 : binPt); + binPt = (binPt > efficiencyHist->GetXaxis()->GetNbins() ? efficiencyHist->GetXaxis()->GetNbins() : binPt); + int binCent = efficiencyHist->GetYaxis()->FindBin(values[kCentFT0C]); + binCent = (binCent == 0 ? 1 : binCent); + binCent = (binCent > efficiencyHist->GetYaxis()->GetNbins() ? efficiencyHist->GetYaxis()->GetNbins() : binCent); + int binCosThetaStarRandom = efficiencyHist->GetZaxis()->FindBin(values[kCosThetaStarRandom]); + binCosThetaStarRandom = (binCosThetaStarRandom == 0 ? 1 : binCosThetaStarRandom); + binCosThetaStarRandom = (binCosThetaStarRandom > efficiencyHist->GetZaxis()->GetNbins() ? efficiencyHist->GetZaxis()->GetNbins() : binCosThetaStarRandom); + + // get the efficiency value from the histogram + values[kPairEfficiency] = efficiencyHist->GetBinContent(binPt, binCent, binCosThetaStarRandom); + values[kPairWeight] = 1.0 / (values[kPairEfficiency] > 0 ? values[kPairEfficiency] : 1.0); // set the weight as the inverse of the efficiency, but avoid division by zero + } else { + LOG(warning) << "FillEfficiency: unknown efficiency type " << fgEfficiencyType << ", using default efficiency = 1"; + values[kPairEfficiency] = 1; + values[kPairWeight] = 1; + } +} + +void VarManager::SetPhiMap(TObject* hposi, TObject* hnega, bool option) +{ + fgPosiPhiMap = hposi; + fgNegaPhiMap = hnega; + fgUsePhiCorrection = option; +} + +double VarManager::SampleRotationPhi(double pt, double eta, int charge) +{ + // each type only alarm once + static bool warnedEmptyPhi = false; + + if (!fgUsePhiCorrection) { + return gRandom->Uniform(0., o2::constants::math::TwoPI); + } else { + + TH3D* hMap = nullptr; + if (charge > 0) { + hMap = dynamic_cast(fgPosiPhiMap); + } else { + hMap = dynamic_cast(fgNegaPhiMap); + } + + if (!hMap) { + LOGF(fatal, "Phi map is not a TH3D"); + } + // TH3 axes: X=pT, Y=phi, Z=eta + int ptBin = hMap->GetXaxis()->FindBin(pt); + int etaBin = hMap->GetZaxis()->FindBin(eta); + + ptBin = std::clamp(ptBin, 1, hMap->GetNbinsX()); + etaBin = std::clamp(etaBin, 1, hMap->GetNbinsZ()); + + TH1D* hPhi = hMap->ProjectionY( + Form("hTRPhi_tmp_charge%d_ptbin%d_etabin%d", + charge, ptBin, etaBin), + ptBin, + ptBin, + etaBin, + etaBin); + + if (!hPhi || hPhi->Integral(1, hPhi->GetNbinsX()) <= 0.) { + if (!warnedEmptyPhi) { + LOGF(warn, + "Empty phi distribution for " + "pt=%f, eta=%f, charge=%d, ptBin=%d, etaBin=%d. " + "Falling back to uniform phi sampling.", + pt, + eta, + charge, + ptBin, + etaBin); + + warnedEmptyPhi = true; + } + + delete hPhi; + + return gRandom->Uniform(0., o2::constants::math::TwoPI); + } + + const double phi = RecoDecay::constrainAngle(hPhi->GetRandom()); + + delete hPhi; + return phi; + } +} + //__________________________________________________________________ std::tuple VarManager::BimodalityCoefficientUnbinned(const std::vector& data) { @@ -392,10 +546,9 @@ std::tuple VarManager::BimodalityCoefficientU float mean = std::accumulate(data.begin(), data.end(), 0.0) / n; float m2 = 0.0, m3 = 0.0, m4 = 0.0; - float diff, diff2; - for (float x : data) { - diff = x - mean; - diff2 = diff * diff; + for (const float& x : data) { + const float diff = x - mean; + const float diff2 = diff * diff; m2 += diff2; m3 += diff2 * diff; m4 += diff2 * diff2; @@ -432,7 +585,7 @@ std::tuple VarManager::BimodalityCoeffic int nBins = static_cast((max - min) / binWidth); std::vector counts(nBins, 0.0); - for (float x : data) { + for (const float& x : data) { if (x < min || x >= max) { continue; // skip out-of-range values } @@ -539,14 +692,13 @@ std::tuple VarManager::BimodalityCoeffic // then compute the second, third, and fourth central moments float m2 = 0.0, m3 = 0.0, m4 = 0.0; - float diff, diff2, binCenter; for (int i = 0; i < nBins; ++i) { if (counts[i] == 0) { continue; // skip empty bins } - binCenter = min + (i + 0.5) * binWidth; - diff = binCenter - mean; - diff2 = diff * diff; + const float binCenter = min + (i + 0.5f) * binWidth; + const float diff = binCenter - mean; + const float diff2 = diff * diff; m2 += counts[i] * diff2; m3 += counts[i] * diff2 * diff; m4 += counts[i] * diff2 * diff2; @@ -672,6 +824,7 @@ void VarManager::SetDefaultVarNames() fgVariableNames[kMCEventTime] = "MC event time"; fgVariableNames[kMCEventWeight] = "MC event weight"; fgVariableNames[kMCEventImpParam] = "MC impact parameter"; + fgVariableNames[kMCEventPlaneAngle] = "MC event plane angle"; fgVariableNames[kMCEventCentrFT0C] = "MC Centrality FT0C"; fgVariableNames[kMultMCNParticlesEta05] = "MC Multiplicity Central Barrel for |eta| < 0.5"; fgVariableNames[kMultMCNParticlesEta08] = "MC Multiplicity Central Barrel for |eta| < 0.8"; @@ -684,6 +837,7 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kMCEventTime] = ""; // TODO: add proper unit fgVariableUnits[kMCEventWeight] = ""; fgVariableUnits[kMCEventImpParam] = "b"; + fgVariableUnits[kMCEventPlaneAngle] = ""; fgVariableUnits[kMCEventCentrFT0C] = "%"; fgVariableUnits[kMultMCNParticlesEta05] = "Multiplicity_eta05"; fgVariableUnits[kMultMCNParticlesEta08] = "Multiplicity_eta08"; @@ -840,6 +994,8 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kDCAzNPeaksTrimmed2] = ""; fgVariableNames[kDCAzNPeaksTrimmed3] = "Number of peaks in binned DCAz distribution (trimmed 3)"; fgVariableUnits[kDCAzNPeaksTrimmed3] = ""; + fgVariableNames[kInteractionRate] = "Interaction rate"; + fgVariableUnits[kInteractionRate] = "kHz"; fgVariableNames[kPt] = "p_{T}"; fgVariableUnits[kPt] = "GeV/c"; fgVariableNames[kPt1] = "p_{T1}"; @@ -886,6 +1042,12 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kTrackTimeRes] = "ns"; fgVariableNames[kTrackTimeResRelative] = "Relative resolution of the track time"; fgVariableUnits[kTrackTimeResRelative] = ""; + fgVariableNames[kTrackAssocDeltaTime] = "Track-collision association #Deltat"; + fgVariableUnits[kTrackAssocDeltaTime] = "ns"; + fgVariableNames[kTrackAssocTimeThreshold] = "Track-collision association time threshold"; + fgVariableUnits[kTrackAssocTimeThreshold] = "ns"; + fgVariableNames[kTrackAssocDeltaTimeNorm] = "|#Deltat| / threshold"; + fgVariableUnits[kTrackAssocDeltaTimeNorm] = ""; fgVariableNames[kDetectorMap] = "DetectorMap"; fgVariableUnits[kDetectorMap] = ""; fgVariableNames[kHasITS] = "HasITS"; @@ -923,12 +1085,60 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kTRDsignal] = ""; fgVariableNames[kTOFbeta] = "TOF #beta"; fgVariableUnits[kTOFbeta] = ""; + fgVariableNames[kEMCalEnergy] = "EMCal cluster energy"; + fgVariableUnits[kEMCalEnergy] = "GeV"; + fgVariableNames[kEMCalCoreEnergy] = "EMCal cluster core energy"; + fgVariableUnits[kEMCalCoreEnergy] = "GeV"; + fgVariableNames[kEMCalRawEnergy] = "EMCal cluster raw energy"; + fgVariableUnits[kEMCalRawEnergy] = "GeV"; + fgVariableNames[kEMCalEta] = "EMCal cluster #eta"; + fgVariableUnits[kEMCalEta] = ""; + fgVariableNames[kEMCalPhi] = "EMCal cluster #varphi"; + fgVariableUnits[kEMCalPhi] = "rad."; + fgVariableNames[kEMCalM02] = "EMCal cluster M02"; + fgVariableUnits[kEMCalM02] = ""; + fgVariableNames[kEMCalM20] = "EMCal cluster M20"; + fgVariableUnits[kEMCalM20] = ""; + fgVariableNames[kEMCalNCells] = "EMCal cluster no. cells"; + fgVariableUnits[kEMCalNCells] = ""; + fgVariableNames[kEMCalTime] = "EMCal cluster time"; + fgVariableUnits[kEMCalTime] = "ns"; + fgVariableNames[kEMCalIsExotic] = "EMCal cluster is exotic"; + fgVariableUnits[kEMCalIsExotic] = ""; + fgVariableNames[kEMCalDistanceToBadChannel] = "EMCal cluster distance to bad channel"; + fgVariableUnits[kEMCalDistanceToBadChannel] = ""; + fgVariableNames[kEMCalNLM] = "EMCal cluster no. local maxima"; + fgVariableUnits[kEMCalNLM] = ""; + fgVariableNames[kEMCalDefinition] = "EMCal cluster definition"; + fgVariableUnits[kEMCalDefinition] = ""; + fgVariableNames[kEMCalEoverP] = "E_{EMCal}/p_{track}"; + fgVariableUnits[kEMCalEoverP] = ""; + fgVariableNames[kEMCalMatchDeltaEta] = "#Delta#eta (track-cluster)"; + fgVariableUnits[kEMCalMatchDeltaEta] = ""; + fgVariableNames[kEMCalMatchDeltaPhi] = "#Delta#varphi (track-cluster)"; + fgVariableUnits[kEMCalMatchDeltaPhi] = ""; fgVariableNames[kTrackLength] = "track length"; fgVariableUnits[kTrackLength] = "cm"; fgVariableNames[kTrackDCAxy] = "DCA_{xy}"; fgVariableUnits[kTrackDCAxy] = "cm"; fgVariableNames[kTrackDCAz] = "DCA_{z}"; fgVariableUnits[kTrackDCAz] = "cm"; + fgVariableNames[kDCAxy1] = "DCA_{xy}"; + fgVariableUnits[kDCAxy1] = "cm"; + fgVariableNames[kDCAz1] = "DCA_{z}"; + fgVariableUnits[kDCAz1] = "cm"; + fgVariableNames[kDCAxy2] = "DCA_{xy}"; + fgVariableUnits[kDCAxy2] = "cm"; + fgVariableNames[kDCAz2] = "DCA_{z}"; + fgVariableUnits[kDCAz2] = "cm"; + fgVariableNames[kDCAxyOverSig1] = "DCA_{xy}/#sigma_{DCA_{xy}}"; + fgVariableUnits[kDCAxyOverSig1] = ""; + fgVariableNames[kDCAzOverSig1] = "DCA_{z}/#sigma_{DCA_{z}}"; + fgVariableUnits[kDCAzOverSig1] = ""; + fgVariableNames[kDCAxyOverSig2] = "DCA_{xy}/#sigma_{DCA_{xy}}"; + fgVariableUnits[kDCAxyOverSig2] = ""; + fgVariableNames[kDCAzOverSig2] = "DCA_{z}/#sigma_{DCA_{z}}"; + fgVariableUnits[kDCAzOverSig2] = ""; fgVariableNames[kTPCnSigmaEl] = "n #sigma_{e}^{TPC}"; fgVariableUnits[kTPCnSigmaEl] = ""; fgVariableNames[kTPCnSigmaEl_Corr] = "n #sigma_{e}^{TPC} Corr."; @@ -941,6 +1151,10 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kTPCnSigmaPi_Corr] = ""; fgVariableNames[kTPCnSigmaKa] = "n #sigma_{K}^{TPC}"; fgVariableUnits[kTPCnSigmaKa] = ""; + fgVariableNames[kTPCnSigmaEl1] = "n #sigma_{el}^{TPC}"; + fgVariableUnits[kTPCnSigmaEl1] = ""; + fgVariableNames[kTPCnSigmaEl2] = "n #sigma_{el}^{TPC}"; + fgVariableUnits[kTPCnSigmaEl2] = ""; fgVariableNames[kTPCnSigmaKa_leg1] = "n #sigma_{K}^{TPC}"; fgVariableUnits[kTPCnSigmaKa_leg1] = ""; fgVariableNames[kTPCnSigmaKa_Corr] = "n #sigma_{K}^{TPC} Corr."; @@ -1067,6 +1281,18 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kMCPhiCS] = "rad"; fgVariableNames[kMCPhiTildeCS] = "MC #tilde{#varphi}_{CS}"; fgVariableUnits[kMCPhiTildeCS] = "rad"; + fgVariableNames[kMCCosThetaGJB1] = "MC cos#it{#theta}_{GJ1}"; + fgVariableUnits[kMCCosThetaGJB1] = ""; + fgVariableNames[kMCPhiGJB1] = "MC #varphi_{GJ1}"; + fgVariableUnits[kMCPhiGJB1] = "rad"; + fgVariableNames[kMCPhiTildeGJB1] = "MC #tilde{#varphi}_{GJ1}"; + fgVariableUnits[kMCPhiTildeGJB1] = "rad"; + fgVariableNames[kMCCosThetaGJB2] = "MC cos#it{#theta}_{GJ2}"; + fgVariableUnits[kMCCosThetaGJB2] = ""; + fgVariableNames[kMCPhiGJB2] = "MC #varphi_{GJ2}"; + fgVariableUnits[kMCPhiGJB2] = "rad"; + fgVariableNames[kMCPhiTildeGJB2] = "MC #tilde{#varphi}_{GJ2}"; + fgVariableUnits[kMCPhiTildeGJB2] = "rad"; fgVariableNames[kMCCosThetaPP] = "MC cos(#theta_{PP})"; fgVariableUnits[kMCCosThetaPP] = ""; fgVariableNames[kMCPhiPP] = "MC #varphi_{PP}"; @@ -1141,6 +1367,10 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kCosPointingAngle] = ""; fgVariableNames[kMCCosPointingAngle] = "MC cos(#theta_{pointing})"; fgVariableUnits[kMCCosPointingAngle] = ""; + fgVariableNames[kD0DcaDaughters] = "DCA of D0 daughters"; + fgVariableUnits[kD0DcaDaughters] = ""; + fgVariableNames[kD0ImpParProduct] = "D0 impact parameter product"; + fgVariableUnits[kD0ImpParProduct] = ""; fgVariableNames[kVertexingPz] = "Pz Pair"; fgVariableUnits[kVertexingPz] = "GeV/c"; fgVariableNames[kVertexingSV] = "Secondary Vertexing z"; @@ -1509,6 +1739,18 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kPhiCS] = "rad."; fgVariableNames[kPhiTildeCS] = "#tilde{#varphi}_{CS}"; fgVariableUnits[kPhiTildeCS] = "rad."; + fgVariableNames[kCosThetaGJB1] = "cos#it{#theta}_{GJ1}"; + fgVariableUnits[kCosThetaGJB1] = ""; + fgVariableNames[kPhiGJB1] = "#varphi_{GJ1}"; + fgVariableUnits[kPhiGJB1] = "rad."; + fgVariableNames[kPhiTildeGJB1] = "#tilde{#varphi}_{GJ1}"; + fgVariableUnits[kPhiTildeGJB1] = "rad."; + fgVariableNames[kCosThetaGJB2] = "cos#it{#theta}_{GJ2}"; + fgVariableUnits[kCosThetaGJB2] = ""; + fgVariableNames[kPhiGJB2] = "#varphi_{GJ2}"; + fgVariableUnits[kPhiGJB2] = "rad."; + fgVariableNames[kPhiTildeGJB2] = "#tilde{#varphi}_{GJ2}"; + fgVariableUnits[kPhiTildeGJB2] = "rad."; fgVariableNames[kCosThetaPP] = "cos#it{#theta}_{PP}"; fgVariableUnits[kCosThetaPP] = ""; fgVariableNames[kPhiPP] = "#varphi_{PP}"; @@ -1531,6 +1773,10 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kCosThetaStarRandom] = ""; fgVariableNames[kCos2ThetaStarRandom] = "cos^{2}#it{#theta}^{*}_{Random}"; fgVariableUnits[kCos2ThetaStarRandom] = ""; + fgVariableNames[kMCCosThetaStar] = "cos#it{#theta}^{*}_{MC}"; + fgVariableUnits[kMCCosThetaStar] = ""; + fgVariableNames[kPairWeight] = "weight"; + fgVariableUnits[kPairWeight] = ""; fgVariableNames[kCosPhiVP] = "cos#it{#varphi}_{VP}"; fgVariableUnits[kCosPhiVP] = ""; fgVariableNames[kPhiVP] = "#varphi_{VP} - #Psi_{2}"; @@ -1647,6 +1893,8 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kAmplitudeFT0A] = "a.u."; fgVariableNames[kAmplitudeFT0C] = "FT0C amplitude"; fgVariableUnits[kAmplitudeFT0C] = "a.u."; + fgVariableNames[kAmplitudeFT0M] = "FT0M amplitude"; + fgVariableUnits[kAmplitudeFT0M] = "a.u."; fgVariableNames[kTimeFT0A] = "FT0A time"; fgVariableUnits[kTimeFT0A] = "ns"; fgVariableNames[kTimeFT0C] = "FT0C time"; @@ -1697,10 +1945,26 @@ void VarManager::SetDefaultVarNames() fgVariableUnits[kBGFDDCpf] = ""; fgVariableNames[kMultDensity] = "dNdeta ALICE3"; fgVariableUnits[kMultDensity] = ""; - fgVariableNames[kMultMCNParticlesEta40] = "Multiplicity_eta40"; - fgVariableUnits[kMultMCNParticlesEta40] = ""; - fgVariableNames[kMultMCNParticlesEta20] = "Multiplicity_eta20"; - fgVariableUnits[kMultMCNParticlesEta20] = ""; + fgVariableNames[kCent] = "Centrality ALICE3"; + fgVariableUnits[kCent] = ""; + fgVariableNames[kMultPV] = "Mult PV ALICE3"; + fgVariableUnits[kMultPV] = ""; + fgVariableNames[kMultPVeta1] = "Mult PV ALICE3 in |eta| < 1"; + fgVariableUnits[kMultPVeta1] = ""; + fgVariableNames[kMultPVetaHalf] = "Mult PV ALICE3 in |eta| < 0.5"; + fgVariableUnits[kMultPVetaHalf] = ""; + fgVariableNames[kMultGlobalTracks] = "Mult global tracks ALICE3"; + fgVariableUnits[kMultGlobalTracks] = ""; + fgVariableNames[kMultGlobalTracksPV] = "Mult global tracks that are PV ALICE3"; + fgVariableUnits[kMultGlobalTracksPV] = ""; + fgVariableNames[kMultMCNParticlesAll] = "Multiplicity MC all"; + fgVariableUnits[kMultMCNParticlesAll] = ""; + fgVariableNames[kMultMCNParticlesEta25] = "Multiplicity MC |eta| < 2.5"; + fgVariableUnits[kMultMCNParticlesEta25] = ""; + fgVariableNames[kMultMCNParticlesEta125] = "Multiplicity MC |eta| < 1.25"; + fgVariableUnits[kMultMCNParticlesEta125] = ""; + fgVariableNames[kMultMCNParticlesEta09] = "Multiplicity MC |eta| < 0.9"; + fgVariableUnits[kMultMCNParticlesEta09] = ""; fgVariableNames[kIsReconstructed] = "is track reconstructed"; fgVariableUnits[kIsReconstructed] = ""; fgVariableNames[kNSiliconHits] = "Number of hits in silicon layers"; @@ -1948,6 +2212,11 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kMCEventTime"] = kMCEventTime; fgVarNamesMap["kMCEventWeight"] = kMCEventWeight; fgVarNamesMap["kMCEventImpParam"] = kMCEventImpParam; + fgVarNamesMap["kMCEventPlaneAngle"] = kMCEventPlaneAngle; + fgVarNamesMap["kMCEventCentrFT0C"] = kMCEventCentrFT0C; + fgVarNamesMap["kMultMCNParticlesEta05"] = kMultMCNParticlesEta05; + fgVarNamesMap["kMultMCNParticlesEta08"] = kMultMCNParticlesEta08; + fgVarNamesMap["kMultMCNParticlesEta10"] = kMultMCNParticlesEta10; fgVarNamesMap["kQ1ZNAX"] = kQ1ZNAX; fgVarNamesMap["kQ1ZNAY"] = kQ1ZNAY; fgVarNamesMap["kQ1ZNCX"] = kQ1ZNCX; @@ -2100,6 +2369,7 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kTwoR2EP1"] = kTwoR2EP1; fgVarNamesMap["kTwoR2EP2"] = kTwoR2EP2; fgVarNamesMap["kNPairsPerEvent"] = kNPairsPerEvent; + fgVarNamesMap["kInteractionRate"] = kInteractionRate; fgVarNamesMap["kNEventWiseVariables"] = kNEventWiseVariables; fgVarNamesMap["kX"] = kX; fgVarNamesMap["kY"] = kY; @@ -2125,18 +2395,33 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kEta1"] = kEta1; fgVarNamesMap["kPhi1"] = kPhi1; fgVarNamesMap["kCharge1"] = kCharge1; + fgVarNamesMap["kDCAxy1"] = kDCAxy1; + fgVarNamesMap["kDCAz1"] = kDCAz1; + fgVarNamesMap["kDCAxyOverSig1"] = kDCAxyOverSig1; + fgVarNamesMap["kDCAzOverSig1"] = kDCAzOverSig1; + fgVarNamesMap["kITSclusterMap1"] = kITSclusterMap1; + fgVarNamesMap["kTPCnSigmaEl1"] = kTPCnSigmaEl1; fgVarNamesMap["kPin_leg1"] = kPin_leg1; fgVarNamesMap["kTPCnSigmaKa_leg1"] = kTPCnSigmaKa_leg1; fgVarNamesMap["kPt2"] = kPt2; fgVarNamesMap["kEta2"] = kEta2; fgVarNamesMap["kPhi2"] = kPhi2; fgVarNamesMap["kCharge2"] = kCharge2; + fgVarNamesMap["kDCAxy2"] = kDCAxy2; + fgVarNamesMap["kDCAz2"] = kDCAz2; + fgVarNamesMap["kDCAxyOverSig2"] = kDCAxyOverSig2; + fgVarNamesMap["kDCAzOverSig2"] = kDCAzOverSig2; + fgVarNamesMap["kITSclusterMap2"] = kITSclusterMap2; + fgVarNamesMap["kTPCnSigmaEl2"] = kTPCnSigmaEl2; fgVarNamesMap["kPin"] = kPin; fgVarNamesMap["kSignedPin"] = kSignedPin; fgVarNamesMap["kTOFExpMom"] = kTOFExpMom; fgVarNamesMap["kTrackTime"] = kTrackTime; fgVarNamesMap["kTrackTimeRes"] = kTrackTimeRes; fgVarNamesMap["kTrackTimeResRelative"] = kTrackTimeResRelative; + fgVarNamesMap["kTrackAssocDeltaTime"] = kTrackAssocDeltaTime; + fgVarNamesMap["kTrackAssocTimeThreshold"] = kTrackAssocTimeThreshold; + fgVarNamesMap["kTrackAssocDeltaTimeNorm"] = kTrackAssocDeltaTimeNorm; fgVarNamesMap["kDetectorMap"] = kDetectorMap; fgVarNamesMap["kHasITS"] = kHasITS; fgVarNamesMap["kHasTRD"] = kHasTRD; @@ -2167,6 +2452,22 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kTRDsignal"] = kTRDsignal; fgVarNamesMap["kTRDPattern"] = kTRDPattern; fgVarNamesMap["kTOFbeta"] = kTOFbeta; + fgVarNamesMap["kEMCalEnergy"] = kEMCalEnergy; + fgVarNamesMap["kEMCalCoreEnergy"] = kEMCalCoreEnergy; + fgVarNamesMap["kEMCalRawEnergy"] = kEMCalRawEnergy; + fgVarNamesMap["kEMCalEta"] = kEMCalEta; + fgVarNamesMap["kEMCalPhi"] = kEMCalPhi; + fgVarNamesMap["kEMCalM02"] = kEMCalM02; + fgVarNamesMap["kEMCalM20"] = kEMCalM20; + fgVarNamesMap["kEMCalNCells"] = kEMCalNCells; + fgVarNamesMap["kEMCalTime"] = kEMCalTime; + fgVarNamesMap["kEMCalIsExotic"] = kEMCalIsExotic; + fgVarNamesMap["kEMCalDistanceToBadChannel"] = kEMCalDistanceToBadChannel; + fgVarNamesMap["kEMCalNLM"] = kEMCalNLM; + fgVarNamesMap["kEMCalDefinition"] = kEMCalDefinition; + fgVarNamesMap["kEMCalEoverP"] = kEMCalEoverP; + fgVarNamesMap["kEMCalMatchDeltaEta"] = kEMCalMatchDeltaEta; + fgVarNamesMap["kEMCalMatchDeltaPhi"] = kEMCalMatchDeltaPhi; fgVarNamesMap["kTrackLength"] = kTrackLength; fgVarNamesMap["kTrackDCAxy"] = kTrackDCAxy; fgVarNamesMap["kTrackDCAxyProng1"] = kTrackDCAxyProng1; @@ -2294,12 +2595,19 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kMCPhi"] = kMCPhi; fgVarNamesMap["kMCEta"] = kMCEta; fgVarNamesMap["kMCY"] = kMCY; + fgVarNamesMap["kMCMass"] = kMCMass; fgVarNamesMap["kMCCosThetaHE"] = kMCCosThetaHE; fgVarNamesMap["kMCPhiHE"] = kMCPhiHE; fgVarNamesMap["kMCPhiTildeHE"] = kMCPhiTildeHE; fgVarNamesMap["kMCCosThetaCS"] = kMCCosThetaCS; fgVarNamesMap["kMCPhiCS"] = kMCPhiCS; fgVarNamesMap["kMCPhiTildeCS"] = kMCPhiTildeCS; + fgVarNamesMap["kMCCosThetaGJB1"] = kMCCosThetaGJB1; + fgVarNamesMap["kMCPhiGJB1"] = kMCPhiGJB1; + fgVarNamesMap["kMCPhiTildeGJB1"] = kMCPhiTildeGJB1; + fgVarNamesMap["kMCCosThetaGJB2"] = kMCCosThetaGJB2; + fgVarNamesMap["kMCPhiGJB2"] = kMCPhiGJB2; + fgVarNamesMap["kMCPhiTildeGJB2"] = kMCPhiTildeGJB2; fgVarNamesMap["kMCCosThetaPP"] = kMCCosThetaPP; fgVarNamesMap["kMCPhiPP"] = kMCPhiPP; fgVarNamesMap["kMCPhiTildePP"] = kMCPhiTildePP; @@ -2346,6 +2654,12 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCosThetaCS"] = kCosThetaCS; fgVarNamesMap["kPhiCS"] = kPhiCS; fgVarNamesMap["kPhiTildeCS"] = kPhiTildeCS; + fgVarNamesMap["kCosThetaGJB1"] = kCosThetaGJB1; + fgVarNamesMap["kPhiGJB1"] = kPhiGJB1; + fgVarNamesMap["kPhiTildeGJB1"] = kPhiTildeGJB1; + fgVarNamesMap["kCosThetaGJB2"] = kCosThetaGJB2; + fgVarNamesMap["kPhiGJB2"] = kPhiGJB2; + fgVarNamesMap["kPhiTildeGJB2"] = kPhiTildeGJB2; fgVarNamesMap["kCosThetaPP"] = kCosThetaPP; fgVarNamesMap["kPhiPP"] = kPhiPP; fgVarNamesMap["kPhiTildePP"] = kPhiTildePP; @@ -2357,6 +2671,8 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCos2ThetaStarFT0C"] = kCos2ThetaStarFT0C; fgVarNamesMap["kCosThetaStarRandom"] = kCosThetaStarRandom; fgVarNamesMap["kCos2ThetaStarRandom"] = kCos2ThetaStarRandom; + fgVarNamesMap["kMCCosThetaStar"] = kMCCosThetaStar; + fgVarNamesMap["kPairWeight"] = kPairWeight; fgVarNamesMap["kCosPhiVP"] = kCosPhiVP; fgVarNamesMap["kPhiVP"] = kPhiVP; fgVarNamesMap["kDeltaPhiPair2"] = kDeltaPhiPair2; @@ -2372,12 +2688,21 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kSignQuadDCAsigXY"] = kSignQuadDCAsigXY; fgVarNamesMap["kCosPointingAngle"] = kCosPointingAngle; fgVarNamesMap["kMCCosPointingAngle"] = kMCCosPointingAngle; + fgVarNamesMap["kD0ImpParProduct"] = kD0ImpParProduct; + fgVarNamesMap["kD0DcaDaughters"] = kD0DcaDaughters; fgVarNamesMap["kImpParXYJpsi"] = kImpParXYJpsi; fgVarNamesMap["kImpParXYK"] = kImpParXYK; fgVarNamesMap["kDCATrackProd"] = kDCATrackProd; fgVarNamesMap["kDCATrackVtxProd"] = kDCATrackVtxProd; fgVarNamesMap["kV2SP"] = kV2SP; fgVarNamesMap["kV2EP"] = kV2EP; + fgVarNamesMap["kV2EP_FT0C"] = kV2EP_FT0C; + fgVarNamesMap["kA2EP_TPC"] = kA2EP_TPC; + fgVarNamesMap["kA2EP_FT0A"] = kA2EP_FT0A; + fgVarNamesMap["kA2EP_FT0C"] = kA2EP_FT0C; + fgVarNamesMap["kA2EPME_TPC"] = kA2EPME_TPC; + fgVarNamesMap["kA2EPME_FT0A"] = kA2EPME_FT0A; + fgVarNamesMap["kA2EPME_FT0C"] = kA2EPME_FT0C; fgVarNamesMap["kWV2SP"] = kWV2SP; fgVarNamesMap["kWV2EP"] = kWV2EP; fgVarNamesMap["kU2Q2"] = kU2Q2; @@ -2460,6 +2785,22 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kCos2DeltaPhiMu1"] = kCos2DeltaPhiMu1; fgVarNamesMap["kCos2DeltaPhiMu2"] = kCos2DeltaPhiMu2; fgVarNamesMap["kCos3DeltaPhi"] = kCos3DeltaPhi; + fgVarNamesMap["kDeltaPhi_TPC"] = kDeltaPhi_TPC; + fgVarNamesMap["kDeltaPhi_FT0A"] = kDeltaPhi_FT0A; + fgVarNamesMap["kDeltaPhi_FT0C"] = kDeltaPhi_FT0C; + fgVarNamesMap["kDeltaPhi_Random"] = kDeltaPhi_Random; + fgVarNamesMap["kDeltaPhi_MC"] = kDeltaPhi_MC; + fgVarNamesMap["kCos2DeltaPhi_TPC"] = kCos2DeltaPhi_TPC; + fgVarNamesMap["kCos2DeltaPhi_FT0A"] = kCos2DeltaPhi_FT0A; + fgVarNamesMap["kCos2DeltaPhi_FT0C"] = kCos2DeltaPhi_FT0C; + fgVarNamesMap["kCos2DeltaPhi_Random"] = kCos2DeltaPhi_Random; + fgVarNamesMap["kCos2DeltaPhi_MC"] = kCos2DeltaPhi_MC; + fgVarNamesMap["kDeltaPhiME_TPC"] = kDeltaPhiME_TPC; + fgVarNamesMap["kDeltaPhiME_FT0A"] = kDeltaPhiME_FT0A; + fgVarNamesMap["kDeltaPhiME_FT0C"] = kDeltaPhiME_FT0C; + fgVarNamesMap["kCos2DeltaPhiME_TPC"] = kCos2DeltaPhiME_TPC; + fgVarNamesMap["kCos2DeltaPhiME_FT0A"] = kCos2DeltaPhiME_FT0A; + fgVarNamesMap["kCos2DeltaPhiME_FT0C"] = kCos2DeltaPhiME_FT0C; fgVarNamesMap["kDeltaPtotTracks"] = kDeltaPtotTracks; fgVarNamesMap["kVertexingLxyOverErr"] = kVertexingLxyOverErr; fgVarNamesMap["kVertexingLzOverErr"] = kVertexingLzOverErr; @@ -2554,6 +2895,9 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kBdtNonprompt"] = kBdtNonprompt; fgVarNamesMap["kAmplitudeFT0A"] = kAmplitudeFT0A; fgVarNamesMap["kAmplitudeFT0C"] = kAmplitudeFT0C; + fgVarNamesMap["kAmplitudeFT0M"] = kAmplitudeFT0M; + fgVarNamesMap["kFT0OrA"] = kFT0OrA; + fgVarNamesMap["kFT0OrC"] = kFT0OrC; fgVarNamesMap["kTimeFT0A"] = kTimeFT0A; fgVarNamesMap["kTimeFT0C"] = kTimeFT0C; fgVarNamesMap["kTriggerMaskFT0"] = kTriggerMaskFT0; @@ -2579,8 +2923,16 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kBBFDDCpf"] = kBBFDDCpf; fgVarNamesMap["kBGFDDCpf"] = kBGFDDCpf; fgVarNamesMap["kMultDensity"] = kMultDensity; - fgVarNamesMap["kMultMCNParticlesEta40"] = kMultMCNParticlesEta40; - fgVarNamesMap["kMultMCNParticlesEta20"] = kMultMCNParticlesEta20; + fgVarNamesMap["kCent"] = kCent; + fgVarNamesMap["kMultPV"] = kMultPV; + fgVarNamesMap["kMultPVeta1"] = kMultPVeta1; + fgVarNamesMap["kMultPVetaHalf"] = kMultPVetaHalf; + fgVarNamesMap["kMultGlobalTracks"] = kMultGlobalTracks; + fgVarNamesMap["kMultGlobalTracksPV"] = kMultGlobalTracksPV; + fgVarNamesMap["kMultMCNParticlesAll"] = kMultMCNParticlesAll; + fgVarNamesMap["kMultMCNParticlesEta25"] = kMultMCNParticlesEta25; + fgVarNamesMap["kMultMCNParticlesEta125"] = kMultMCNParticlesEta125; + fgVarNamesMap["kMultMCNParticlesEta09"] = kMultMCNParticlesEta09; fgVarNamesMap["kIsReconstructed"] = kIsReconstructed; fgVarNamesMap["kNSiliconHits"] = kNSiliconHits; fgVarNamesMap["kNTPCHits"] = kNTPCHits; @@ -2633,3 +2985,64 @@ void VarManager::SetDefaultVarNames() fgVarNamesMap["kInnerTOFnSigmaHe3"] = kInnerTOFnSigmaHe3; fgVarNamesMap["kInnerTOFnSigmaAl"] = kInnerTOFnSigmaAl; } + +//__________________________________________________________________ +bool VarManager::computeBarrelAssocTimeCompat(float trackTime, float trackTimeRes, bool timeResIsRange, bool isPVContributor, + int64_t origBC, float origCollTime, int origNumContrib, + int64_t collBC, float collTime, float collTimeRes, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values) +{ + // + // Mirrors the time-based association of Common/Core/CollisionAssociation.h::runAssocWithTime for central barrel tracks. + // Every arithmetic step is kept identical to the associator (including the double->int64 truncations), + // so that re-applying it on skimmed associations with a smaller timeMargin reproduces a skim produced directly with that margin. + // Only tracks assigned to a collision in the AO2D are handled (this is always the case for tracks written by the DQ table makers). + // + if (!values) { + values = fgValues; + } + values[kTrackAssocDeltaTime] = -9999.f; + values[kTrackAssocTimeThreshold] = 0.f; + values[kTrackAssocDeltaTimeNorm] = 9999.f; + + constexpr double BunchSpacingNS = o2::constants::lhc::LHCBunchSpacingNS; + + // (1) BC pre-window (CollisionAssociation.h: trackBCCache, bcOffsetMax, bcOffsetWindow) + // NOTE: the associator uses the raw trackTime here also for PV contributors + const int64_t bcOffsetMax = static_cast(bcWindowForOneSigma * nSigma + timeMargin / BunchSpacingNS); + const int64_t trackBCCache = static_cast(static_cast(origBC) + static_cast(trackTime) / BunchSpacingNS); + if (std::abs(trackBCCache - collBC) > bcOffsetMax) { + return false; + } + + // (2) PV-contributor handling (usePVAssociation: 0 off, 1 OnlySameBc, 2 SameBcAndLowMult) + constexpr int OnlySameBc = 1; + constexpr int SameBcAndLowMult = 2; + const bool pvMode = (usePVAssociation == OnlySameBc && isPVContributor) || + (usePVAssociation == SameBcAndLowMult && isPVContributor && origNumContrib > maxPvContribLowMult); + float tTrack = trackTime; + float tTrackRes = trackTimeRes; + if (pvMode) { + tTrack = origCollTime; // time of the ORIGINAL collision + tTrackRes = static_cast(BunchSpacingNS); // 1 BC + } + + // (3) time difference and threshold + const int64_t bcOffset = origBC - collBC; + const float collTimeRes2 = collTimeRes * collTimeRes; + const float deltaTime = tTrack - collTime + bcOffset * static_cast(BunchSpacingNS); + float threshold = 0.f; + if (pvMode) { + threshold = tTrackRes; // margin NOT applied + } else if (timeResIsRange) { + threshold = tTrackRes + nSigma * std::sqrt(collTimeRes2) + timeMargin; + } else { + threshold = nSigma * std::sqrt(collTimeRes2 + tTrackRes * tTrackRes) + timeMargin; + } + + values[kTrackAssocDeltaTime] = deltaTime; + values[kTrackAssocTimeThreshold] = threshold; + values[kTrackAssocDeltaTimeNorm] = (threshold > 0.f) ? std::abs(deltaTime) / threshold : 9999.f; + return std::abs(deltaTime) < threshold; +} diff --git a/PWGDQ/Core/VarManager.h b/PWGDQ/Core/VarManager.h index e39a7ae2aaf..7a395a9dcfb 100644 --- a/PWGDQ/Core/VarManager.h +++ b/PWGDQ/Core/VarManager.h @@ -23,6 +23,8 @@ #include "PWGUD/Core/UDHelpers.h" #include "PWGUD/Core/UPCHelpers.h" +// +#include "PWGHF/Core/HfHelper.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" @@ -36,13 +38,14 @@ #include #include #include +#include #include -#include #include #include #include #include #include +#include #include #include #include @@ -69,7 +72,6 @@ #include #include -#include #include #include @@ -82,8 +84,6 @@ #include #include -#include - using SMatrix55 = ROOT::Math::SMatrix>; using SMatrix5 = ROOT::Math::SVector; using Vec3D = ROOT::Math::SVector; @@ -149,7 +149,9 @@ class VarManager : public TObject MuonCovRealign = BIT(26), MFTCov = BIT(27), TrackTOFService = BIT(28), - ParticleMC = BIT(29) + ParticleMC = BIT(29), + MuonDca = BIT(30), + TrackEMCal = BIT(31) // NOTE: last free bit of the uint32 track fill maps; used both for aod::EMCALClusters and ReducedEMCals }; enum PairCandidateType { @@ -176,10 +178,10 @@ class VarManager : public TObject enum BarrelTrackFilteringBits { kIsConversionLeg = 0, // electron from conversions - kIsK0sLeg, // pion from K0s - kIsLambdaLeg, // proton or pion from Lambda - kIsALambdaLeg, // proton or pion from anti-Lambda - kIsOmegaLeg, // kaon from Omega baryon decay + kIsK0sLeg = 1, // pion from K0s + kIsLambdaLeg = 2, // proton or pion from Lambda + kIsALambdaLeg = 3, // proton or pion from anti-Lambda + kIsOmegaLeg = 4, // kaon from Omega baryon decay kDalitzBits = 5, // first bit for Dalitz tagged tracks kBarrelUserCutsBits = 13, // first bit for user track cuts kIsTPCPostcalibrated = 63 // tracks were postcalibrated for the TPC PID @@ -190,7 +192,12 @@ class VarManager : public TObject kMuonIsPropagated = 7 // whether the muon was propagated already }; - public: + enum EMCalClusterFilteringBits { + kEMCalClusterIsMatched = 0, // cluster has at least one matched track (from EMCALMatchedTracks) + kEMCalClusterUserCutsBits = 1 // first bit for the user EMCal cluster selections + }; + + // NOLINTNEXTLINE(readability-enum-initial-value) enum Variables { kNothing = -1, // Run wise variables @@ -336,6 +343,7 @@ class VarManager : public TObject kMCEventWeight, kMCEventImpParam, kMCEventCentrFT0C, + kMCEventPlaneAngle, kMultMCNParticlesEta10, kMultMCNParticlesEta08, kMultMCNParticlesEta05, @@ -380,6 +388,9 @@ class VarManager : public TObject kMultA, // Multiplicity of the sub-event A kMultAPOS, // Multiplicity of the sub-event A kMultANEG, // Multiplicity of the sub-event A + kMultA1, + kMultA2, + kNnorm, kMultB, kMultC, kQ3X0A, // q-vector (e.g. from TPC) with x component (harmonic 3 and power 0), sub-event A @@ -474,6 +485,14 @@ class VarManager : public TObject kQ2X0A2, kQ2Y0A1, kQ2Y0A2, + kQ2X0B1, + kQ2X0B2, + kQ2Y0B1, + kQ2Y0B2, + kQ2X0C1, + kQ2X0C2, + kQ2Y0C1, + kQ2Y0C2, kU2Q2Ev1, kU2Q2Ev2, kCos2DeltaPhiEv1, @@ -486,11 +505,21 @@ class VarManager : public TObject kV2ME_EP, kWV2ME_SP, kWV2ME_EP, - kTwoR2SP1, // Scalar product resolution of event1 for ME technique - kTwoR2SP2, // Scalar product resolution of event2 for ME technique - kTwoR2EP1, // Event plane resolution of event2 for ME technique - kTwoR2EP2, // Event plane resolution of event2 for ME technique - kNPairsPerEvent, // number of pairs per event in same-event or mixed-event pairing + kTwoR2SP1, // Scalar product resolution of event1 for ME technique + kTwoR2SP2, // Scalar product resolution of event2 for ME technique + kTwoR2EP1, // Event plane resolution of event2 for ME technique + kTwoR2EP2, // Event plane resolution of event2 for ME technique + kDeltaPhiME_TPC, // Delta phi of VM to TPC event plane for ME technique + kDeltaPhiME_FT0C, // Delta phi of VM to FT0C event plane for ME technique + kDeltaPhiME_FT0A, // Delta phi of VM to FT0A event plane for ME technique + kCos2DeltaPhiME_TPC, // Cos(2*Delta phi) of VM to TPC event plane for ME technique + kCos2DeltaPhiME_FT0C, // Cos(2*Delta phi) of VM to FT0C event plane for ME technique + kCos2DeltaPhiME_FT0A, // Cos(2*Delta phi) of VM to FT0A event plane for ME technique + kA2EPME_TPC, // A2 of VM to TPC event plane for ME technique + kA2EPME_FT0C, // A2 of VM to FT0C event plane for ME technique + kA2EPME_FT0A, // A2 of VM to FT0A event plane for ME technique + kNPairsPerEvent, // number of pairs per event in same-event or mixed-event pairing + kInteractionRate, // Variables for event mixing with cumulant kV22m, @@ -529,12 +558,24 @@ class VarManager : public TObject kEta1, kPhi1, kCharge1, + kDCAxy1, + kDCAz1, + kDCAxyOverSig1, + kDCAzOverSig1, + kITSclusterMap1, + kTPCnSigmaEl1, kPin_leg1, kTPCnSigmaKa_leg1, kPt2, kEta2, kPhi2, kCharge2, + kDCAxy2, + kDCAz2, + kDCAxyOverSig2, + kDCAzOverSig2, + kITSclusterMap2, + kTPCnSigmaEl2, // Barrel track variables kPin, @@ -543,6 +584,9 @@ class VarManager : public TObject kTrackTime, kTrackTimeRes, kTrackTimeResRelative, + kTrackAssocDeltaTime, // track time - time of the associated collision (BC offset included), ns + kTrackAssocTimeThreshold, // time-compatibility threshold of the track-to-collision associator (margin included), ns + kTrackAssocDeltaTimeNorm, // |kTrackAssocDeltaTime| / kTrackAssocTimeThreshold; < 1 means the association is time compatible kDetectorMap, kHasITS, kHasTRD, @@ -617,6 +661,22 @@ class VarManager : public TObject kIsDalitzLeg, // Up to 8 dalitz selections kBarrelNAssocsInBunch = kIsDalitzLeg + 8, // number of in bunch collision associations kBarrelNAssocsOutOfBunch, // number of out of bunch collision associations + kEMCalEnergy, // EMCal cluster energy + kEMCalCoreEnergy, // EMCal cluster core energy + kEMCalRawEnergy, // EMCal cluster raw energy + kEMCalEta, // EMCal cluster pseudorapidity + kEMCalPhi, // EMCal cluster azimuthal angle + kEMCalM02, // EMCal shower shape long axis + kEMCalM20, // EMCal shower shape short axis + kEMCalNCells, // number of cells in the EMCal cluster + kEMCalTime, // EMCal cluster time + kEMCalIsExotic, // exotic EMCal cluster flag + kEMCalDistanceToBadChannel, // distance of the EMCal cluster to the closest bad channel + kEMCalNLM, // number of local maxima of the EMCal cluster + kEMCalDefinition, // EMCal cluster definition + kEMCalEoverP, // EMCal cluster energy over matched track momentum + kEMCalMatchDeltaEta, // eta residual of the matched EMCal cluster w.r.t. the track + kEMCalMatchDeltaPhi, // phi residual of the matched EMCal cluster w.r.t. the track kNBarrelTrackVariables, // Muon track variables @@ -712,18 +772,27 @@ class VarManager : public TObject // MC pair variables kMCPt1, kMCEta1, + kMCP1, kMCPt2, kMCEta2, + kMCP2, kMCCosThetaHE, kMCPhiHE, kMCPhiTildeHE, kMCCosThetaCS, kMCPhiCS, kMCPhiTildeCS, + kMCCosThetaGJB1, + kMCPhiGJB1, + kMCPhiTildeGJB1, + kMCCosThetaGJB2, + kMCPhiGJB2, + kMCPhiTildeGJB2, kMCCosThetaPP, kMCPhiPP, kMCPhiTildePP, kMCCosThetaRM, + kMCCosThetaStar, // Pair variables kCandidateId, @@ -774,6 +843,12 @@ class VarManager : public TObject kCosThetaCS, kPhiCS, kPhiTildeCS, + kCosThetaGJB1, + kPhiGJB1, + kPhiTildeGJB1, + kCosThetaGJB2, + kPhiGJB2, + kPhiTildeGJB2, kCosThetaPP, kPhiPP, kPhiTildePP, @@ -790,6 +865,24 @@ class VarManager : public TObject kDeltaPhiPair2, kDeltaEtaPair2, kPsiPair, + kDeltaPhi_TPC, + kDeltaPhi_FT0A, + kDeltaPhi_FT0C, + kCos2DeltaPhi_TPC, + kCos2DeltaPhi_FT0A, + kCos2DeltaPhi_FT0C, + kDeltaPhi_Random, + kDeltaPhi_MC, + kCos2DeltaPhi_Random, + kCos2DeltaPhi_MC, + kDeltaPhiPP_Random, + kDeltaPhiPP_MC, + kCos2DeltaPhiPP_Random, + kCos2DeltaPhiPP_MC, + kNullA2, + kInfA2, + kSel1, // if track1 is used in TPC Q vector calculation + kSel2, // if track2 is used in TPC Q vector calculation kDeltaPhiPair, kOpeningAngle, kQuadDCAabsXY, @@ -799,15 +892,24 @@ class VarManager : public TObject kQuadDCAsigXYZ, kSignQuadDCAsigXY, kCosPointingAngle, + kD0ImpParProduct, + kD0DcaDaughters, kImpParXYJpsi, kImpParXYK, kDCATrackProd, kDCATrackVtxProd, kV2SP, - kV2EP, + kV2EP, // Psi2A + kV2EP_FT0C, + kV2EP_FT0A, + kA2EP_TPC, + kA2EP_FT0A, + kA2EP_FT0C, kWV2SP, kWV2EP, kU2Q2, + kU2Q2POS, + kU2Q2NEG, kU3Q3, kQ42XA, kQ42YA, @@ -892,6 +994,8 @@ class VarManager : public TObject kPsi2C, kRandomPsi2, kCos2DeltaPhi, + kCos2DeltaPhiPOS, + kCos2DeltaPhiNEG, kCos2DeltaPhiMu1, // cos(phi - phi1) for muon1 kCos2DeltaPhiMu2, ////cos(phi - phi2) for muon2 kCos3DeltaPhi, @@ -922,6 +1026,8 @@ class VarManager : public TObject kS12, kS13, kS23, + kPairEfficiency, + kPairWeight, kNPairVariables, // Candidate-track correlation variables @@ -973,6 +1079,7 @@ class VarManager : public TObject kDeltaR1, kDeltaR2, kDeltaR, + kVertexingQuadProcCode, // DQ-HF correlation variables kMassCharmHadron, @@ -1002,9 +1109,12 @@ class VarManager : public TObject // FIT detector variables kAmplitudeFT0A, kAmplitudeFT0C, + kAmplitudeFT0M, kTimeFT0A, kTimeFT0C, kTriggerMaskFT0, + kFT0OrA, + kFT0OrC, kAmplitudeFDDA, kAmplitudeFDDC, kTimeFDDA, @@ -1029,8 +1139,16 @@ class VarManager : public TObject // ALICE 3 Variables kMultDensity, - kMultMCNParticlesEta40, - kMultMCNParticlesEta20, + kCent, + kMultPV, + kMultPVeta1, + kMultPVetaHalf, + kMultGlobalTracks, + kMultGlobalTracksPV, + kMultMCNParticlesAll, + kMultMCNParticlesEta25, + kMultMCNParticlesEta125, + kMultMCNParticlesEta09, kIsReconstructed, kNSiliconHits, kNTPCHits, @@ -1107,10 +1225,19 @@ class VarManager : public TObject kNCalibObjects }; + enum EfficiencyType { + kNone = 0, + kPairPtCentFT0cCosThetaStarFT0c, + kPairPtCentFT0cCosThetaStarRandom, + // Add more efficiency types as needed + kNEfficiencyTypes + }; + enum DileptonCharmHadronTypes { kJPsi = 0, kD0ToPiK, - kD0barToKPi + kD0barToKPi, + kDplusToPiKPi }; enum EventFilters { @@ -1149,9 +1276,9 @@ class VarManager : public TObject } SetVariableDependencies(); } - static void SetUseVars(const std::vector usedVars) + static void SetUseVars(const std::vector& usedVars) { - for (auto& var : usedVars) { + for (auto const& var : usedVars) { fgUsedVars[var] = true; } } @@ -1170,7 +1297,7 @@ class VarManager : public TObject } // Setup the collision system - static void SetCollisionSystem(TString system, float energy); + static void SetCollisionSystem(const TString& system, float energy); static void SetCollisionSystem(o2::parameters::GRPLHCIFData* grplhcif); static void SetMagneticField(float magField) @@ -1193,6 +1320,42 @@ class VarManager : public TObject static void SetZShift(float z) { fgzShiftFwd = z; + fgUseTopBottomShift = false; + } + + // Set x, y and z shifts for forward tracks + static void Set3DShift(float x, float y, float z) + { + fgxShiftFwd = x; + fgyShiftFwd = y; + fgzShiftFwd = z; + fgUseTopBottomShift = false; + } + + // Set separate x, y, z shifts for top (y >= 0) and bottom (y < 0) forward tracks + // Top shifts are stored in fgx/y/zShiftFwd; bottom shifts in fgx/y/zShiftFwdBottom + static void SetTopBottom3DShift(float xTop, float yTop, float zTop, float xBottom, float yBottom, float zBottom) + { + fgxShiftFwd = xTop; + fgyShiftFwd = yTop; + fgzShiftFwd = zTop; + fgxShiftFwdBottom = xBottom; + fgyShiftFwdBottom = yBottom; + fgzShiftFwdBottom = zBottom; + fgUseTopBottomShift = true; + } + + static void GetFwdShiftForY(float y, float& xShift, float& yShift, float& zShift) + { + if (fgUseTopBottomShift && y < 0.f) { + xShift = fgxShiftFwdBottom; + yShift = fgyShiftFwdBottom; + zShift = fgzShiftFwdBottom; + } else { + xShift = fgxShiftFwd; + yShift = fgyShiftFwd; + zShift = fgzShiftFwd; + } } // Setup the 2 prong KFParticle @@ -1291,17 +1454,9 @@ class VarManager : public TObject return (1.0 / harm) * TMath::ATan2(qnya, qnxa); }; - static float getDeltaPsiInRange(float psi1, float psi2, float harmonic) + static float getDeltaPsiInRange(float psi1, float psi2, unsigned int harmonic) { - float deltaPsi = psi1 - psi2; - if (std::abs(deltaPsi) > o2::constants::math::PI / harmonic) { - if (deltaPsi > 0.) { - deltaPsi -= o2::constants::math::TwoPI / harmonic; - } else { - deltaPsi += o2::constants::math::TwoPI / harmonic; - } - } - return deltaPsi; + return RecoDecay::constrainAngle(psi1 - psi2, -o2::constants::math::PI / harmonic, harmonic); } template static o2::dataformats::VertexBase RecalculatePrimaryVertex(T const& track0, T const& track1, const T1& collision); @@ -1316,8 +1471,6 @@ class VarManager : public TObject template static o2::track::TrackParCovFwd PropagateFwd(const T& track, const C& cov, float z); template - static void FillMuonPDca(const T& muon, const C& collision, float* values = nullptr); - template static void FillPropagateMuon(const T& muon, const C& collision, float* values = nullptr); template static void FillBC(T const& bc, float* values = nullptr); @@ -1330,19 +1483,37 @@ class VarManager : public TObject template static void FillEventFlowResoFactor(T const& hs_sp, T const& hs_ep, float* values = nullptr); template - static void FillTwoEvents(T const& event1, T const& event2, float* values = nullptr); + static void FillTwoEvents(T const& ev1, T const& ev2, float* values = nullptr); template - static void FillTwoMixEvents(T1 const& event1, T1 const& event2, T2 const& tracks1, T2 const& tracks2, float* values = nullptr); + static void FillTwoMixEvents(T1 const& ev1, T1 const& ev2, T2 const& tracks1, T2 const& tracks2, float* values = nullptr); template static void FillTwoMixEventsFlowResoFactor(T const& hs_sp, T const& hs_ep, float* values = nullptr); template - static void FillTwoMixEventsCumulants(T const& h_v22m, T const& h_v24m, T const& h_v22p, T const& h_v24p, T1 const& t1, T2 const& t2, float* values = nullptr); + static void FillTwoMixEventsCumulants(T const& h_v22ev1, T const& h_v24ev1, T const& h_v22ev2, T const& h_v24ev2, T1 const& t1, T2 const& t2, float* values = nullptr); template static void FillTrack(T const& track, float* values = nullptr); template - static void FillPhoton(T const& photon, float* values = nullptr); + static void FillPhoton(T const& track, float* values = nullptr); + template + static void FillTrackEMCal(T const& cluster, float trackP = -1.0f, float deltaEta = -999.0f, float deltaPhi = -999.0f, float* values = nullptr); template static void FillTrackCollision(T const& track, C const& collision, float* values = nullptr); + // Re-evaluation of the time compatibility of a (barrel track, collision) association, mirroring + // Common/Core/CollisionAssociation.h::runAssocWithTime. Fills kTrackAssocDeltaTime, kTrackAssocTimeThreshold, kTrackAssocDeltaTimeNorm. + // orig* : collision originally assigned to the track in the AO2D (reference of trackTime) + // coll* : collision of the association under test + // The last 5 parameters must match the configuration of track-to-collision-associator used at skimming time + // (timeMargin and nSigma may be smaller in order to tighten the association at analysis level). + static bool computeBarrelAssocTimeCompat(float trackTime, float trackTimeRes, bool timeResIsRange, bool isPVContributor, + int64_t origBC, float origCollTime, int origNumContrib, + int64_t collBC, float collTime, float collTimeRes, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values = nullptr); + // Same as above for DQ skimmed tables: track = ReducedTracks+ReducedTracksBarrel, collision/origCollision = ReducedEvents+ReducedEventsExtended + template + static bool isBarrelAssocTimeCompatible(T const& track, C const& collision, C const& origCollision, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values = nullptr); template static void FillTrackCollisionMC(T1 const& track, T2 const& MotherTrack, C const& collision, float* values = nullptr); template @@ -1361,6 +1532,10 @@ class VarManager : public TObject static void FillGlobalMuonRefitCov(T1 const& muontrack, T2 const& mfttrack, const C& collision, C2 const& mftcov, float* values = nullptr); template static void FillPair(T1 const& t1, T2 const& t2, float* values = nullptr); + template + static void FillPairRotation(T1 const& t1, T2 const& t2, int rotation, float* values = nullptr); + template + static void FillPairRotation_ME(T const& t1, T const& t2, int rotation, float* values = nullptr); template static void FillPairCollision(C const& collision, T1 const& t1, T2 const& t2, float* values = nullptr); template @@ -1369,34 +1544,36 @@ class VarManager : public TObject static void FillTriple(T1 const& t1, T2 const& t2, T3 const& t3, float* values = nullptr, PairCandidateType pairType = kTripleCandidateToEEPhoton); template static void FillPairME(T1 const& t1, T2 const& t2, float* values = nullptr); + template + static void FillPairMEAcrossTFs(T const& t1, T const& t2, float* values = nullptr); template static void FillPairMC(T1 const& t1, T2 const& t2, float* values = nullptr); template static void FillTripleMC(T1 const& t1, T2 const& t2, T3 const& t3, float* values = nullptr); template - static void FillQuadMC(T1 const& t1, T2 const& t2, T2 const& t3, float* values = nullptr); + static void FillQuadMC(T1 const& dilepton, T2 const& track1, T2 const& track2, float* values = nullptr); template - static void FillPairVertexing(C const& collision, T const& t1, T const& t2, bool propToSV = false, float* values = nullptr); + static void FillPairVertexing(C const& collision, T const& t1, T const& t2, float* values = nullptr); template - static void FillPairVertexingRecomputePV(C const& /*collision*/, T const& t1, T const& t2, o2::dataformats::VertexBase pvRefitted, float* values = nullptr); + static void FillPairVertexingRecomputePV(C const& /*collision*/, T const& t1, T const& t2, const o2::dataformats::VertexBase& pvRefitted, float* values = nullptr); template static void FillTripletVertexing(C const& collision, T const& t1, T const& t2, T const& t3, PairCandidateType tripletType, float* values = nullptr); template static void FillDileptonTrackVertexing(C const& collision, T1 const& lepton1, T1 const& lepton2, T1 const& track, float* values); template static void FillDileptonHadron(T1 const& dilepton, T2 const& hadron, float* values = nullptr, float hadronMass = 0.0f); - template - static void FillEnergyCorrelatorTriple(T1 const& lepton1, T2 const& lepton2, T3 const& hadron, float* values = nullptr, float Translow = 1. / 3, float Transhigh = 2. / 3, bool applyFitMass = false, float sidebandMass = 0.0f, float weight = 1.0f); + template + static void FillEnergyCorrelatorTriple(C const& collision, T1 const& lepton1, T2 const& lepton2, T3 const& hadron, float* values = nullptr, float Translow = 1. / 3, float Transhigh = 2. / 3, bool applyFitMass = false, float sidebandMass = 0.0f, float weight = 1.0f); template static void FillEnergyCorrelatorsUnfoldingTriple(T1 const& lepton1, T2 const& lepton2, T3 const& hadron, T4 const& track, T5 const& t1, float* values = nullptr, bool applyFitMass = false, float Effweight_rec = 1.f, float Accweight_gen = 1.f, float Translow = 1. / 3, float Transhigh = 2. / 3); template static void FillDileptonPhoton(T1 const& dilepton, T2 const& photon, float* values = nullptr); template static void FillHadron(T const& hadron, float* values = nullptr, float hadronMass = 0.0f); - template - static void FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper, T& bdtScoreCharmHad, float* values = nullptr); - template - static void FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, H hfHelper, T& bdtScoreCharmHad, float* values = nullptr); + template + static void FillSingleDileptonCharmHadron(Cand const& candidate, T& bdtScoreCharmHad, float* values = nullptr); + template + static void FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, T& bdtScoreCharmHad, float* values = nullptr); template static void FillQVectorFromGFW(C const& collision, A const& compA11, A const& compB11, A const& compC11, A const& compA21, A const& compB21, A const& compC21, A const& compA31, A const& compB31, A const& compC31, A const& compA41, A const& compB41, A const& compC41, A const& compA23, A const& compA42, float S10A = 1.0, float S10B = 1.0, float S10C = 1.0, float S11A = 1.0, float S11B = 1.0, float S11C = 1.0, float S12A = 1.0, float S13A = 1.0, float S14A = 1.0, float S21A = 1.0, float S22A = 1.0, float S31A = 1.0, float S41A = 1.0, float* values = nullptr); template @@ -1425,24 +1602,28 @@ class VarManager : public TObject static void FillTrackAlice3(T const& track, float* values = nullptr); template static void FillResolutions(M const& mcTrack, T const& track, float* values = nullptr); + template + static void FillPairVertexingAlice3(C const& collision, T const& t1, T const& t2, bool propToSV = false, float* values = nullptr); + template + static void FillTripletVertexingALICE3(C const& collision, T const& t1, T const& t2, T const& t3, VarManager::PairCandidateType tripletType, float* values = nullptr); static void SetCalibrationObject(CalibObjects calib, TObject* obj) { fgCalibs[calib] = obj; // Check whether all the needed objects for TPC postcalibration are available - if (fgCalibs.find(kTPCElectronMean) != fgCalibs.end() && fgCalibs.find(kTPCElectronSigma) != fgCalibs.end()) { + if (fgCalibs.contains(kTPCElectronMean) && fgCalibs.contains(kTPCElectronSigma)) { fgRunTPCPostCalibration[0] = true; fgUsedVars[kTPCnSigmaEl_Corr] = true; } - if (fgCalibs.find(kTPCPionMean) != fgCalibs.end() && fgCalibs.find(kTPCPionSigma) != fgCalibs.end()) { + if (fgCalibs.contains(kTPCPionMean) && fgCalibs.contains(kTPCPionSigma)) { fgRunTPCPostCalibration[1] = true; fgUsedVars[kTPCnSigmaPi_Corr] = true; } - if (fgCalibs.find(kTPCKaonMean) != fgCalibs.end() && fgCalibs.find(kTPCKaonSigma) != fgCalibs.end()) { + if (fgCalibs.contains(kTPCKaonMean) && fgCalibs.contains(kTPCKaonSigma)) { fgRunTPCPostCalibration[2] = true; fgUsedVars[kTPCnSigmaKa_Corr] = true; } - if (fgCalibs.find(kTPCProtonMean) != fgCalibs.end() && fgCalibs.find(kTPCProtonSigma) != fgCalibs.end()) { + if (fgCalibs.contains(kTPCProtonMean) && fgCalibs.contains(kTPCProtonSigma)) { fgRunTPCPostCalibration[3] = true; fgUsedVars[kTPCnSigmaPr_Corr] = true; } @@ -1458,14 +1639,17 @@ class VarManager : public TObject } static double ComputePIDcalibration(int species, double nSigmaValue); + static void SetEfficiencyObject(int type, TObject* obj); + static void FillEfficiency(float* values = nullptr); + static void SetPhiMap(TObject* h1, TObject* h2, bool option); + static double SampleRotationPhi(double pT, double eta, int charge); static TObject* GetCalibrationObject(CalibObjects calib) { auto obj = fgCalibs.find(calib); if (obj == fgCalibs.end()) { - return 0x0; - } else { - return obj->second; + return nullptr; } + return obj->second; } static void SetTPCInterSectorBoundary(float boundarySize) { @@ -1485,7 +1669,6 @@ class VarManager : public TObject fgEOR = eor; } - public: VarManager(); ~VarManager() override; @@ -1500,7 +1683,13 @@ class VarManager : public TObject static float fgMagField; static float fgzMatching; + static float fgxShiftFwd; + static float fgyShiftFwd; static float fgzShiftFwd; + static bool fgUseTopBottomShift; + static float fgxShiftFwdBottom; + static float fgyShiftFwdBottom; + static float fgzShiftFwdBottom; static float fgCenterOfMassEnergy; // collision energy static float fgMassofCollidingParticle; // mass of the colliding particle static float fgTPCInterSectorBoundary; // TPC inter-sector border size at the TPC outer radius, in cm @@ -1523,11 +1712,11 @@ class VarManager : public TObject static KFPTrack createKFPFwdTrackFromFwdTrack(const T& muon); template static KFPVertex createKFPVertexFromCollision(const T& collision); - static float calculateCosPA(KFParticle kfp, KFParticle PV); + static float calculateCosPA(const KFParticle& kfp, const KFParticle& PV); template static float calculatePhiV(const T1& t1, const T2& t2); template - static float LorentzTransformJpsihadroncosChi(TString Option, const T1& v1, const T2& v2); + static float LorentzTransformJpsihadroncosChi(const TString& Option, const T1& v1, const T2& v2); static o2::vertexing::DCAFitterN<2> fgFitterTwoProngBarrel; static o2::vertexing::DCAFitterN<3> fgFitterThreeProngBarrel; @@ -1536,15 +1725,20 @@ class VarManager : public TObject static o2::vertexing::FwdDCAFitterN<3> fgFitterThreeProngFwd; static o2::globaltracking::MatchGlobalFwd mMatching; - static std::map fgCalibs; // map of calibration histograms - static bool fgRunTPCPostCalibration[4]; // 0-electron, 1-pion, 2-kaon, 3-proton - static int fgCalibrationType; // 0 - no calibration, 1 - calibration vs (TPCncls,pIN,eta) typically for pp, 2 - calibration vs (eta,nPV,nLong,tLong) typically for PbPb - static bool fgUseInterpolatedCalibration; // use interpolated calibration histograms (default: true) + static std::map fgCalibs; // map of calibration histograms + static std::array fgRunTPCPostCalibration; // 0-electron, 1-pion, 2-kaon, 3-proton + static int fgCalibrationType; // 0 - no calibration, 1 - calibration vs (TPCncls,pIN,eta) typically for pp, 2 - calibration vs (eta,nPV,nLong,tLong) typically for PbPb + static bool fgUseInterpolatedCalibration; // use interpolated calibration histograms (default: true) + + static int fgEfficiencyType; // type of efficiency correction to apply + static TObject* fgEfficiencyHist; // histogram for efficiency correction + + static TObject* fgPosiPhiMap; // phi map to correct track rotation + static TObject* fgNegaPhiMap; + static bool fgUsePhiCorrection; VarManager& operator=(const VarManager& c); VarManager(const VarManager& c); - - ClassDef(VarManager, 6); }; template @@ -1584,21 +1778,18 @@ KFPTrack VarManager::createKFPTrackFromTrack(const T& track) track.cSnpSnp(), track.cTglY(), track.cTglZ(), track.cTglSnp(), track.cTglTgl(), track.c1PtY(), track.c1PtZ(), track.c1PtSnp(), track.c1PtTgl(), track.c1Pt21Pt2()}; o2::track::TrackParametrizationWithError trackparCov{track.x(), track.alpha(), std::move(trackpars), std::move(trackcovs)}; - std::array trkpos_par; - std::array trkmom_par; - std::array trk_cov; + std::array trkpos_par{}; + std::array trkmom_par{}; + std::array trk_cov{}; trackparCov.getXYZGlo(trkpos_par); trackparCov.getPxPyPzGlo(trkmom_par); trackparCov.getCovXYZPxPyPzGlo(trk_cov); - float trkpar_KF[6] = {trkpos_par[0], trkpos_par[1], trkpos_par[2], - trkmom_par[0], trkmom_par[1], trkmom_par[2]}; - float trkcov_KF[21]; - for (int i = 0; i < 21; i++) { - trkcov_KF[i] = trk_cov[i]; - } + std::array trkpar_KF = {trkpos_par[0], trkpos_par[1], trkpos_par[2], + trkmom_par[0], trkmom_par[1], trkmom_par[2]}; + std::array trkcov_KF = trk_cov; KFPTrack kfpTrack; - kfpTrack.SetParameters(trkpar_KF); - kfpTrack.SetCovarianceMatrix(trkcov_KF); + kfpTrack.SetParameters(trkpar_KF.data()); + kfpTrack.SetCovarianceMatrix(trkcov_KF.data()); kfpTrack.SetCharge(track.sign()); kfpTrack.SetNDF(track.tpcNClsFound() - 5); kfpTrack.SetChi2(track.tpcChi2NCl() * track.tpcNClsFound()); @@ -1610,17 +1801,14 @@ KFPTrack VarManager::createKFPFwdTrackFromFwdTrack(const T& muon) { o2::track::TrackParCovFwd trackparCov = FwdToTrackPar(muon, muon); - std::array trk_cov; + std::array trk_cov{}; trackparCov.getCovXYZPxPyPzGlo(trk_cov); - double trkpar_KF[6] = {trackparCov.getX(), trackparCov.getY(), trackparCov.getZ(), - trackparCov.getPx(), trackparCov.getPy(), trackparCov.getPz()}; - float trkcov_KF[21]; - for (int i = 0; i < 21; i++) { - trkcov_KF[i] = trk_cov[i]; - } + std::array trkpar_KF = {trackparCov.getX(), trackparCov.getY(), trackparCov.getZ(), + trackparCov.getPx(), trackparCov.getPy(), trackparCov.getPz()}; + std::array trkcov_KF = trk_cov; KFPTrack kfpTrack; - kfpTrack.SetParameters(trkpar_KF); - kfpTrack.SetCovarianceMatrix(trkcov_KF); + kfpTrack.SetParameters(trkpar_KF.data()); + kfpTrack.SetCovarianceMatrix(trkcov_KF.data()); kfpTrack.SetCharge(muon.sign()); kfpTrack.SetNDF(muon.nClusters() - 5); kfpTrack.SetChi2(muon.chi2()); @@ -1673,21 +1861,21 @@ o2::dataformats::VertexBase VarManager::RecalculatePrimaryVertex(T const& track0 o2::math_utils::Point3D vtxXYZ(kVtx.Parameter(0), kVtx.Parameter(1), kVtx.Parameter(2)); std::array vtxCov{kVtx.Covariance(0), kVtx.Covariance(1), kVtx.Covariance(2), kVtx.Covariance(3), kVtx.Covariance(4), kVtx.Covariance(5)}; - o2::dataformats::VertexBase primaryVertexRec = {std::move(vtxXYZ), std::move(vtxCov)}; + o2::dataformats::VertexBase primaryVertexRec = {vtxXYZ, vtxCov}; return primaryVertexRec; } template o2::dataformats::GlobalFwdTrack VarManager::PropagateMuon(const T& muon, const C& collision, const int endPoint) { - o2::track::TrackParCovFwd fwdtrack = o2::aod::fwdtrackutils::getTrackParCovFwdShift(muon, fgzShiftFwd, muon); + float xShift = 0.f; + float yShift = 0.f; + float zShift = 0.f; + GetFwdShiftForY(muon.y(), xShift, yShift, zShift); + o2::track::TrackParCovFwd fwdtrack = o2::aod::fwdtrackutils::getTrackParCovFwd3DShift(muon, xShift, yShift, zShift, muon); o2::dataformats::GlobalFwdTrack propmuon; if (static_cast(muon.trackType()) > 2) { - o2::dataformats::GlobalFwdTrack track; - track.setParameters(fwdtrack.getParameters()); - track.setZ(fwdtrack.getZ()); - track.setCovariances(fwdtrack.getCovariances()); - auto mchTrack = mMatching.FwdtoMCH(track); + auto mchTrack = mMatching.FwdtoMCH(fwdtrack); if (endPoint == kToVertex) { o2::mch::TrackExtrap::extrapToVertex(mchTrack, collision.posX(), collision.posY(), collision.posZ(), collision.covXX(), collision.covYY()); @@ -1702,17 +1890,12 @@ o2::dataformats::GlobalFwdTrack VarManager::PropagateMuon(const T& muon, const C o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrack, fgzMatching); } - auto proptrack = mMatching.MCHtoFwd(mchTrack); - propmuon.setParameters(proptrack.getParameters()); - propmuon.setZ(proptrack.getZ()); - propmuon.setCovariances(proptrack.getCovariances()); + propmuon = mMatching.MCHtoFwd(mchTrack); } else if (static_cast(muon.trackType()) < 2) { std::array dcaInfOrig{999.f, 999.f, 999.f}; fwdtrack.propagateToDCAhelix(fgMagField, {collision.posX(), collision.posY(), collision.posZ()}, dcaInfOrig); - propmuon.setParameters(fwdtrack.getParameters()); - propmuon.setZ(fwdtrack.getZ()); - propmuon.setCovariances(fwdtrack.getCovariances()); + propmuon = fwdtrack; } return propmuon; } @@ -1725,34 +1908,15 @@ o2::track::TrackParCovFwd VarManager::PropagateFwd(const T& track, const C& cov, return fwdtrack; } -template -void VarManager::FillMuonPDca(const T& muon, const C& collision, float* values) -{ - if (!values) { - values = fgValues; - } - - if constexpr ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0) { - - o2::dataformats::GlobalFwdTrack propmuon = PropagateMuon(muon, collision); - o2::dataformats::GlobalFwdTrack propmuonAtDCA = PropagateMuon(muon, collision, kToDCA); - - float dcaX = (propmuonAtDCA.getX() - collision.posX()); - float dcaY = (propmuonAtDCA.getY() - collision.posY()); - float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - values[kMuonPDca] = muon.p() * dcaXY; - } -} - template void VarManager::FillPropagateMuon(const T& muon, const C& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & ReducedMuonCov) > 0) { - if (muon.filteringFlags() & (uint8_t(1) << VarManager::kMuonIsPropagated)) { // the muon is already propagated, so nothing to do + if (muon.filteringFlags() & (static_cast(1) << VarManager::kMuonIsPropagated)) { // the muon is already propagated, so nothing to do return; } } @@ -1767,21 +1931,7 @@ void VarManager::FillPropagateMuon(const T& muon, const C& collision, float* val values[kTgl] = propmuon.getTgl(); values[kPhi] = propmuon.getPhi(); - // Redo propagation only for muon tracks - // propagation of MFT tracks alredy done in fwdtrack-extention task - if (static_cast(muon.trackType()) > 2) { - o2::dataformats::GlobalFwdTrack propmuonAtDCA = PropagateMuon(muon, collision, kToDCA); - o2::dataformats::GlobalFwdTrack propmuonAtRabs = PropagateMuon(muon, collision, kToRabs); - float dcaX = (propmuonAtDCA.getX() - collision.posX()); - float dcaY = (propmuonAtDCA.getY() - collision.posY()); - values[kMuonDCAx] = dcaX; - values[kMuonDCAy] = dcaY; - double xAbs = propmuonAtRabs.getX(); - double yAbs = propmuonAtRabs.getY(); - values[kMuonRAtAbsorberEnd] = std::sqrt(xAbs * xAbs + yAbs * yAbs); - } - - SMatrix55 cov = propmuon.getCovariances(); + const SMatrix55& cov = propmuon.getCovariances(); values[kMuonCXX] = cov(0, 0); values[kMuonCXY] = cov(1, 0); values[kMuonCYY] = cov(1, 1); @@ -1804,15 +1954,20 @@ template void VarManager::FillGlobalMuonRefit(T1 const& muontrack, T2 const& mfttrack, const C& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0) { - o2::dataformats::GlobalFwdTrack propmuon = PropagateMuon(muontrack, collision); - double px = propmuon.getP() * sin(M_PI / 2 - atan(mfttrack.tgl())) * cos(mfttrack.phi()); - double py = propmuon.getP() * sin(M_PI / 2 - atan(mfttrack.tgl())) * sin(mfttrack.phi()); - double pz = propmuon.getP() * cos(M_PI / 2 - atan(mfttrack.tgl())); + float xShift = 0.f; + float yShift = 0.f; + float zShift = 0.f; + GetFwdShiftForY(mfttrack.y(), xShift, yShift, zShift); + o2::dataformats::GlobalFwdTrack propmuon = PropagateMuon(muontrack, collision, kToVertex); + double px = propmuon.getP() * std::sin(o2::constants::math::PIHalf - std::atan(mfttrack.tgl())) * std::cos(mfttrack.phi()); + double py = propmuon.getP() * std::sin(o2::constants::math::PIHalf - std::atan(mfttrack.tgl())) * std::sin(mfttrack.phi()); + double pz = propmuon.getP() * std::cos(o2::constants::math::PIHalf - std::atan(mfttrack.tgl())); double pt = std::sqrt(std::pow(px, 2) + std::pow(py, 2)); - auto mftprop = o2::aod::fwdtrackutils::getTrackParCovFwdShift(mfttrack, fgzShiftFwd); + auto mftprop = o2::aod::fwdtrackutils::getTrackParCovFwd3DShift(mfttrack, xShift, yShift, zShift); + mftprop.setInvQPt(static_cast(muontrack.sign()) / pt); values[kX] = mftprop.getX(); values[kY] = mftprop.getY(); values[kZ] = mftprop.getZ(); @@ -1821,6 +1976,12 @@ void VarManager::FillGlobalMuonRefit(T1 const& muontrack, T2 const& mfttrack, co values[kPz] = pz; values[kEta] = mftprop.getEta(); values[kPhi] = mftprop.getPhi(); + + // Helix DCA of the refitted global track w.r.t. the associated collision + std::array dca{999., 999., 999.}; + mftprop.propagateToDCAhelix(fgMagField, {collision.posX(), collision.posY(), collision.posZ()}, dca); + values[kMuonDCAx] = static_cast(dca[0]); + values[kMuonDCAy] = static_cast(dca[1]); } } @@ -1828,12 +1989,16 @@ template (fgValues); } if constexpr ((MuonfillMap & MuonCov) > 0) { if constexpr ((MFTfillMap & MFTCov) > 0) { - o2::dataformats::GlobalFwdTrack propmuon = PropagateMuon(muontrack, collision); - auto mft = o2::aod::fwdtrackutils::getTrackParCovFwdShift(mfttrack, fgzShiftFwd, mftcov); + float xShift = 0.f; + float yShift = 0.f; + float zShift = 0.f; + GetFwdShiftForY(mfttrack.y(), xShift, yShift, zShift); + o2::dataformats::GlobalFwdTrack propmuon = PropagateMuon(muontrack, collision, kToVertex); + auto mft = o2::aod::fwdtrackutils::getTrackParCovFwd3DShift(mfttrack, xShift, yShift, zShift, mftcov); o2::dataformats::GlobalFwdTrack globalRefit = o2::aod::fwdtrackutils::refitGlobalMuonCov(propmuon, mft); values[kX] = globalRefit.getX(); @@ -1844,6 +2009,12 @@ void VarManager::FillGlobalMuonRefitCov(T1 const& muontrack, T2 const& mfttrack, values[kPz] = globalRefit.getPz(); values[kEta] = globalRefit.getEta(); values[kPhi] = globalRefit.getPhi(); + + // Helix DCA of the covariance-refitted global track w.r.t. the associated collision + std::array dca{999., 999., 999.}; + globalRefit.propagateToDCAhelix(fgMagField, {collision.posX(), collision.posY(), collision.posZ()}, dca); + values[kMuonDCAx] = static_cast(dca[0]); + values[kMuonDCAy] = static_cast(dca[1]); } } } @@ -1852,7 +2023,7 @@ template void VarManager::FillTimeFrame(T const& tf, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr (T::template contains()) { values[kTFNBCs] = tf.size(); @@ -1878,7 +2049,7 @@ template void VarManager::FillBC(T const& bc, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kRunNo] = bc.runNumber(); values[kBC] = bc.globalBC(); @@ -1891,7 +2062,7 @@ template void VarManager::FillEvent(T const& event, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & CollisionTimestamp) > 0) { @@ -1902,6 +2073,10 @@ void VarManager::FillEvent(T const& event, float* values) values[kCollisionRandom] = gRandom->Rndm(); } + if (fgUsedVars[kRandomPsi2]) { + values[kRandomPsi2] = gRandom->Uniform(-o2::constants::math::PIHalf, o2::constants::math::PIHalf); + } + if constexpr ((fillMap & Collision) > 0) { // TODO: trigger info from the event selection requires a separate flag // so that it can be switched off independently of the rest of Collision variables (e.g. if event selection is not available) @@ -1925,7 +2100,7 @@ void VarManager::FillEvent(T const& event, float* values) values[kIsNoTFBorder] = event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder); } if (fgUsedVars[kIsTriggerZNAZNC]) { - values[kIsTriggerZNAZNC] = event.selection_bit(o2::aod::evsel::kIsBBZNA) && event.selection_bit(o2::aod::evsel::kIsBBZNC); + values[kIsTriggerZNAZNC] = static_cast(event.selection_bit(o2::aod::evsel::kIsBBZNA) && event.selection_bit(o2::aod::evsel::kIsBBZNC)); } if (fgUsedVars[kIsNoSameBunch]) { values[kIsNoSameBunch] = event.selection_bit(o2::aod::evsel::kNoSameBunchPileup); @@ -1940,7 +2115,7 @@ void VarManager::FillEvent(T const& event, float* values) values[kIsVertexTOFmatched] = event.selection_bit(o2::aod::evsel::kIsVertexTOFmatched); } if (fgUsedVars[kIsSel8]) { - values[kIsSel8] = event.selection_bit(o2::aod::evsel::kIsTriggerTVX) && event.selection_bit(o2::aod::evsel::kNoITSROFrameBorder) && event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder); + values[kIsSel8] = static_cast(event.selection_bit(o2::aod::evsel::kIsTriggerTVX) && event.selection_bit(o2::aod::evsel::kNoITSROFrameBorder) && event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)); } if (fgUsedVars[kIsGoodITSLayer3]) { values[kIsGoodITSLayer3] = event.selection_bit(o2::aod::evsel::kIsGoodITSLayer3); @@ -1952,37 +2127,37 @@ void VarManager::FillEvent(T const& event, float* values) values[kIsGoodITSLayersAll] = event.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); } if (fgUsedVars[kIsINT7]) { - values[kIsINT7] = (event.alias_bit(kINT7) > 0); + values[kIsINT7] = static_cast(event.alias_bit(kINT7) > 0); } if (fgUsedVars[kIsEMC7]) { - values[kIsEMC7] = (event.alias_bit(kEMC7) > 0); + values[kIsEMC7] = static_cast(event.alias_bit(kEMC7) > 0); } if (fgUsedVars[kIsINT7inMUON]) { - values[kIsINT7inMUON] = (event.alias_bit(kINT7inMUON) > 0); + values[kIsINT7inMUON] = static_cast(event.alias_bit(kINT7inMUON) > 0); } if (fgUsedVars[kIsMuonSingleLowPt7]) { - values[kIsMuonSingleLowPt7] = (event.alias_bit(kMuonSingleLowPt7) > 0); + values[kIsMuonSingleLowPt7] = static_cast(event.alias_bit(kMuonSingleLowPt7) > 0); } if (fgUsedVars[kIsMuonSingleHighPt7]) { - values[kIsMuonSingleHighPt7] = (event.alias_bit(kMuonSingleHighPt7) > 0); + values[kIsMuonSingleHighPt7] = static_cast(event.alias_bit(kMuonSingleHighPt7) > 0); } if (fgUsedVars[kIsMuonUnlikeLowPt7]) { - values[kIsMuonUnlikeLowPt7] = (event.alias_bit(kMuonUnlikeLowPt7) > 0); + values[kIsMuonUnlikeLowPt7] = static_cast(event.alias_bit(kMuonUnlikeLowPt7) > 0); } if (fgUsedVars[kIsMuonLikeLowPt7]) { - values[kIsMuonLikeLowPt7] = (event.alias_bit(kMuonLikeLowPt7) > 0); + values[kIsMuonLikeLowPt7] = static_cast(event.alias_bit(kMuonLikeLowPt7) > 0); } if (fgUsedVars[kIsCUP8]) { - values[kIsCUP8] = (event.alias_bit(kCUP8) > 0); + values[kIsCUP8] = static_cast(event.alias_bit(kCUP8) > 0); } if (fgUsedVars[kIsCUP9]) { - values[kIsCUP9] = (event.alias_bit(kCUP9) > 0); + values[kIsCUP9] = static_cast(event.alias_bit(kCUP9) > 0); } if (fgUsedVars[kIsMUP10]) { - values[kIsMUP10] = (event.alias_bit(kMUP10) > 0); + values[kIsMUP10] = static_cast(event.alias_bit(kMUP10) > 0); } if (fgUsedVars[kIsMUP11]) { - values[kIsMUP11] = (event.alias_bit(kMUP11) > 0); + values[kIsMUP11] = static_cast(event.alias_bit(kMUP11) > 0); } values[kVtxX] = event.posX(); values[kVtxY] = event.posY(); @@ -2063,14 +2238,14 @@ void VarManager::FillEvent(T const& event, float* values) values[kVtxZ] = event.posZ(); values[kVtxNcontrib] = event.numContrib(); if (fgUsedVars[kIsDoubleGap] || fgUsedVars[kIsSingleGap] || fgUsedVars[kIsSingleGapA] || fgUsedVars[kIsSingleGapC] || fgUsedVars[kIsNoGap]) { - values[kIsDoubleGap] = (event.tag_bit(56 + kDoubleGap) > 0); - values[kIsSingleGapA] = (event.tag_bit(56 + kSingleGapA) > 0); - values[kIsSingleGapC] = (event.tag_bit(56 + kSingleGapC) > 0); - values[kIsSingleGap] = values[kIsSingleGapA] || values[kIsSingleGapC]; - values[kIsNoGap] = !values[kIsDoubleGap] && !values[kIsSingleGap]; + values[kIsDoubleGap] = static_cast(event.tag_bit(56 + kDoubleGap) > 0); + values[kIsSingleGapA] = static_cast(event.tag_bit(56 + kSingleGapA) > 0); + values[kIsSingleGapC] = static_cast(event.tag_bit(56 + kSingleGapC) > 0); + values[kIsSingleGap] = static_cast(values[kIsSingleGapA] != 0.f || values[kIsSingleGapC] != 0.f); + values[kIsNoGap] = static_cast(values[kIsDoubleGap] == 0.f && values[kIsSingleGap] == 0.f); } if (fgUsedVars[kIsITSUPCMode]) { - values[kIsITSUPCMode] = (event.tag_bit(56 + kITSUPCMode) > 0); + values[kIsITSUPCMode] = static_cast(event.tag_bit(56 + kITSUPCMode) > 0); } values[kCollisionTime] = event.collisionTime(); values[kCollisionTimeRes] = event.collisionTimeRes(); @@ -2088,34 +2263,34 @@ void VarManager::FillEvent(T const& event, float* values) values[kIsNoITSROFBorderRecomputed] = bcInITSROF > fgITSROFBorderMarginLow && bcInITSROF < fgITSROFlength - fgITSROFBorderMarginHigh ? 1.0 : 0.0; } if (fgUsedVars[kIsNoITSROFBorder]) { - values[kIsNoITSROFBorder] = (event.selection_bit(o2::aod::evsel::kNoITSROFrameBorder) > 0); + values[kIsNoITSROFBorder] = static_cast(event.selection_bit(o2::aod::evsel::kNoITSROFrameBorder) > 0); } if (fgUsedVars[kIsTVXTriggered]) { - values[kIsTVXTriggered] = (event.selection_bit(o2::aod::evsel::kIsTriggerTVX) > 0); + values[kIsTVXTriggered] = static_cast(event.selection_bit(o2::aod::evsel::kIsTriggerTVX) > 0); } if (fgUsedVars[kIsNoTFBorder]) { - values[kIsNoTFBorder] = (event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) > 0); + values[kIsNoTFBorder] = static_cast(event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) > 0); } if (fgUsedVars[kNoCollInTimeRangeStandard]) { - values[kNoCollInTimeRangeStandard] = (event.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard) > 0); + values[kNoCollInTimeRangeStandard] = static_cast(event.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard) > 0); } if (fgUsedVars[kIsNoSameBunch]) { - values[kIsNoSameBunch] = (event.selection_bit(o2::aod::evsel::kNoSameBunchPileup) > 0); + values[kIsNoSameBunch] = static_cast(event.selection_bit(o2::aod::evsel::kNoSameBunchPileup) > 0); } if (fgUsedVars[kIsTriggerZNAZNC]) { - values[kIsTriggerZNAZNC] = event.selection_bit(o2::aod::evsel::kIsBBZNA) && event.selection_bit(o2::aod::evsel::kIsBBZNC); + values[kIsTriggerZNAZNC] = static_cast(event.selection_bit(o2::aod::evsel::kIsBBZNA) && event.selection_bit(o2::aod::evsel::kIsBBZNC)); } if (fgUsedVars[kIsGoodZvtxFT0vsPV]) { - values[kIsGoodZvtxFT0vsPV] = (event.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) > 0); + values[kIsGoodZvtxFT0vsPV] = static_cast(event.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) > 0); } if (fgUsedVars[kIsVertexITSTPC]) { - values[kIsVertexITSTPC] = (event.selection_bit(o2::aod::evsel::kIsVertexITSTPC) > 0); + values[kIsVertexITSTPC] = static_cast(event.selection_bit(o2::aod::evsel::kIsVertexITSTPC) > 0); } if (fgUsedVars[kIsVertexTOFmatched]) { - values[kIsVertexTOFmatched] = (event.selection_bit(o2::aod::evsel::kIsVertexTOFmatched) > 0); + values[kIsVertexTOFmatched] = static_cast(event.selection_bit(o2::aod::evsel::kIsVertexTOFmatched) > 0); } if (fgUsedVars[kIsSel8]) { - values[kIsSel8] = event.selection_bit(o2::aod::evsel::kIsTriggerTVX) && event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) && event.selection_bit(o2::aod::evsel::kNoITSROFrameBorder); + values[kIsSel8] = static_cast(event.selection_bit(o2::aod::evsel::kIsTriggerTVX) && event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) && event.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)); } if (fgUsedVars[kIsGoodITSLayer3]) { values[kIsGoodITSLayer3] = event.selection_bit(o2::aod::evsel::kIsGoodITSLayer3); @@ -2127,37 +2302,37 @@ void VarManager::FillEvent(T const& event, float* values) values[kIsGoodITSLayersAll] = event.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll); } if (fgUsedVars[kIsINT7]) { - values[kIsINT7] = (event.alias_bit(kINT7) > 0); + values[kIsINT7] = static_cast(event.alias_bit(kINT7) > 0); } if (fgUsedVars[kIsEMC7]) { - values[kIsEMC7] = (event.alias_bit(kEMC7) > 0); + values[kIsEMC7] = static_cast(event.alias_bit(kEMC7) > 0); } if (fgUsedVars[kIsINT7inMUON]) { - values[kIsINT7inMUON] = (event.alias_bit(kINT7inMUON) > 0); + values[kIsINT7inMUON] = static_cast(event.alias_bit(kINT7inMUON) > 0); } if (fgUsedVars[kIsMuonSingleLowPt7]) { - values[kIsMuonSingleLowPt7] = (event.alias_bit(kMuonSingleLowPt7) > 0); + values[kIsMuonSingleLowPt7] = static_cast(event.alias_bit(kMuonSingleLowPt7) > 0); } if (fgUsedVars[kIsMuonSingleHighPt7]) { - values[kIsMuonSingleHighPt7] = (event.alias_bit(kMuonSingleHighPt7) > 0); + values[kIsMuonSingleHighPt7] = static_cast(event.alias_bit(kMuonSingleHighPt7) > 0); } if (fgUsedVars[kIsMuonUnlikeLowPt7]) { - values[kIsMuonUnlikeLowPt7] = (event.alias_bit(kMuonUnlikeLowPt7) > 0); + values[kIsMuonUnlikeLowPt7] = static_cast(event.alias_bit(kMuonUnlikeLowPt7) > 0); } if (fgUsedVars[kIsMuonLikeLowPt7]) { - values[kIsMuonLikeLowPt7] = (event.alias_bit(kMuonLikeLowPt7) > 0); + values[kIsMuonLikeLowPt7] = static_cast(event.alias_bit(kMuonLikeLowPt7) > 0); } if (fgUsedVars[kIsCUP8]) { - values[kIsCUP8] = (event.alias_bit(kCUP8) > 0); + values[kIsCUP8] = static_cast(event.alias_bit(kCUP8) > 0); } if (fgUsedVars[kIsCUP9]) { - values[kIsCUP9] = (event.alias_bit(kCUP9) > 0); + values[kIsCUP9] = static_cast(event.alias_bit(kCUP9) > 0); } if (fgUsedVars[kIsMUP10]) { - values[kIsMUP10] = (event.alias_bit(kMUP10) > 0); + values[kIsMUP10] = static_cast(event.alias_bit(kMUP10) > 0); } if (fgUsedVars[kIsMUP11]) { - values[kIsMUP11] = (event.alias_bit(kMUP11) > 0); + values[kIsMUP11] = static_cast(event.alias_bit(kMUP11) > 0); } } @@ -2208,7 +2383,6 @@ void VarManager::FillEvent(T const& event, float* values) values[VarManager::kPsi2A] = Psi2A; values[VarManager::kPsi2B] = Psi2B; values[VarManager::kPsi2C] = Psi2C; - values[VarManager::kRandomPsi2] = gRandom->Uniform(-o2::constants::math::PIHalf, o2::constants::math::PIHalf); if constexpr ((fillMap & ReducedEventQvectorExtra) > 0) { values[kQ42XA] = event.q42xa(); @@ -2241,8 +2415,10 @@ void VarManager::FillEvent(T const& event, float* values) values[kQ1Y0B] = -999; values[kQ1X0C] = -999; values[kQ1Y0C] = -999; - values[kQ2X0A] = (event.qvecBPosRe() * event.nTrkBPos() + event.qvecBNegRe() * event.nTrkBNeg()) / (event.nTrkBPos() + event.nTrkBNeg()); - values[kQ2Y0A] = (event.qvecBPosIm() * event.nTrkBPos() + event.qvecBNegIm() * event.nTrkBNeg()) / (event.nTrkBPos() + event.nTrkBNeg()); + // values[kQ2X0A] = (event.qvecBPosRe() * event.nTrkBPos() + event.qvecBNegRe() * event.nTrkBNeg()) / (event.nTrkBPos() + event.nTrkBNeg()); + // values[kQ2Y0A] = (event.qvecBPosIm() * event.nTrkBPos() + event.qvecBNegIm() * event.nTrkBNeg()) / (event.nTrkBPos() + event.nTrkBNeg()); + values[kQ2X0A] = event.qvecBAllRe(); + values[kQ2Y0A] = event.qvecBAllIm(); values[kQ2X0APOS] = event.qvecBPosRe(); values[kQ2Y0APOS] = event.qvecBPosIm(); values[kQ2X0ANEG] = event.qvecBNegRe(); @@ -2269,6 +2445,114 @@ void VarManager::FillEvent(T const& event, float* values) values[kQ4X0C] = -999; values[kQ4Y0C] = -999; + values[kQ2YYAB] = values[kQ2Y0A] * values[kQ2Y0B]; + values[kQ2XXAB] = values[kQ2X0A] * values[kQ2X0B]; + values[kQ2XYAB] = values[kQ2X0A] * values[kQ2Y0B]; + values[kQ2YXAB] = values[kQ2Y0A] * values[kQ2X0B]; + values[kQ2YYAC] = values[kQ2Y0A] * values[kQ2Y0C]; + values[kQ2XXAC] = values[kQ2X0A] * values[kQ2X0C]; + values[kQ2XYAC] = values[kQ2X0A] * values[kQ2Y0C]; + values[kQ2YXAC] = values[kQ2Y0A] * values[kQ2X0C]; + values[kQ2YYBC] = values[kQ2Y0B] * values[kQ2Y0C]; + values[kQ2XXBC] = values[kQ2X0B] * values[kQ2X0C]; + values[kQ2XYBC] = values[kQ2X0B] * values[kQ2Y0C]; + values[kQ2YXBC] = values[kQ2Y0B] * values[kQ2X0C]; + + EventPlaneHelper epHelper; + float Psi2A = epHelper.GetEventPlane(values[kQ2X0A], values[kQ2Y0A], 2); + float Psi2APOS = epHelper.GetEventPlane(values[kQ2X0APOS], values[kQ2Y0APOS], 2); + float Psi2ANEG = epHelper.GetEventPlane(values[kQ2X0ANEG], values[kQ2Y0ANEG], 2); + float Psi2B = epHelper.GetEventPlane(values[kQ2X0B], values[kQ2Y0B], 2); + float Psi2C = epHelper.GetEventPlane(values[kQ2X0C], values[kQ2Y0C], 2); + + values[kPsi2A] = Psi2A; + values[kPsi2APOS] = Psi2APOS; + values[kPsi2ANEG] = Psi2ANEG; + values[kPsi2B] = Psi2B; + values[kPsi2C] = Psi2C; + + float R2SP_AB = (values[kQ2X0A] * values[kQ2X0B] + values[kQ2Y0A] * values[kQ2Y0B]); + float R2SP_APOSB = (values[kQ2X0APOS] * values[kQ2X0B] + values[kQ2Y0APOS] * values[kQ2Y0B]); + float R2SP_ANEGB = (values[kQ2X0ANEG] * values[kQ2X0B] + values[kQ2Y0ANEG] * values[kQ2Y0B]); + float R2SP_AC = (values[kQ2X0A] * values[kQ2X0C] + values[kQ2Y0A] * values[kQ2Y0C]); + float R2SP_APOSC = (values[kQ2X0APOS] * values[kQ2X0C] + values[kQ2Y0APOS] * values[kQ2Y0C]); + float R2SP_ANEGC = (values[kQ2X0ANEG] * values[kQ2X0C] + values[kQ2Y0ANEG] * values[kQ2Y0C]); + float R2SP_BC = (values[kQ2X0B] * values[kQ2X0C] + values[kQ2Y0B] * values[kQ2Y0C]); + float R2SP_AB_Im = (values[kQ2Y0A] * values[kQ2X0B] - values[kQ2X0A] * values[kQ2Y0B]); + float R2SP_AC_Im = (values[kQ2Y0A] * values[kQ2X0C] - values[kQ2X0A] * values[kQ2Y0C]); + float R2SP_BC_Im = (values[kQ2Y0B] * values[kQ2X0C] - values[kQ2X0B] * values[kQ2Y0C]); + values[kR2SP_AB] = std::isnan(R2SP_AB) || std::isinf(R2SP_AB) ? 0. : R2SP_AB; + values[kR2SP_FT0CTPCPOS] = std::isnan(R2SP_APOSB) || std::isinf(R2SP_APOSB) ? 0. : R2SP_APOSB; + values[kR2SP_FT0CTPCNEG] = std::isnan(R2SP_ANEGB) || std::isinf(R2SP_ANEGB) ? 0. : R2SP_ANEGB; + values[kWR2SP_AB] = std::isnan(R2SP_AB) || std::isinf(R2SP_AB) ? 0. : 1.0; + values[kR2SP_AC] = std::isnan(R2SP_AC) || std::isinf(R2SP_AC) ? 0. : R2SP_AC; + values[kR2SP_FT0ATPCPOS] = std::isnan(R2SP_APOSC) || std::isinf(R2SP_APOSC) ? 0. : R2SP_APOSC; + values[kR2SP_FT0ATPCNEG] = std::isnan(R2SP_ANEGC) || std::isinf(R2SP_ANEGC) ? 0. : R2SP_ANEGC; + values[kWR2SP_AC] = std::isnan(R2SP_AC) || std::isinf(R2SP_AC) ? 0. : 1.0; + values[kR2SP_BC] = std::isnan(R2SP_BC) || std::isinf(R2SP_BC) ? 0. : R2SP_BC; + values[kWR2SP_BC] = std::isnan(R2SP_BC) || std::isinf(R2SP_BC) ? 0. : 1.0; + values[kR2SP_AB_Im] = std::isnan(R2SP_AB_Im) || std::isinf(R2SP_AB_Im) ? 0. : R2SP_AB_Im; + values[kWR2SP_AB_Im] = std::isnan(R2SP_AB_Im) || std::isinf(R2SP_AB_Im) ? 0. : 1.0; + values[kR2SP_AC_Im] = std::isnan(R2SP_AC_Im) || std::isinf(R2SP_AC_Im) ? 0. : R2SP_AC_Im; + values[kWR2SP_AC_Im] = std::isnan(R2SP_AC_Im) || std::isinf(R2SP_AC_Im) ? 0. : 1.0; + values[kR2SP_BC_Im] = std::isnan(R2SP_BC_Im) || std::isinf(R2SP_BC_Im) ? 0. : R2SP_BC_Im; + values[kWR2SP_BC_Im] = std::isnan(R2SP_BC_Im) || std::isinf(R2SP_BC_Im) ? 0. : 1.0; + + float R2EP_AB = std::isnan(Psi2A) || std::isinf(Psi2A) || std::isnan(Psi2B) || std::isinf(Psi2B) ? 0. : TMath::Cos(2 * (Psi2A - Psi2B)); + float R2EP_AC = std::isnan(Psi2A) || std::isinf(Psi2A) || std::isnan(Psi2C) || std::isinf(Psi2C) ? 0. : TMath::Cos(2 * (Psi2A - Psi2C)); + float R2EP_BC = std::isnan(Psi2B) || std::isinf(Psi2B) || std::isnan(Psi2C) || std::isinf(Psi2C) ? 0. : TMath::Cos(2 * (Psi2B - Psi2C)); + float R2EP_AB_Im = std::isnan(Psi2A) || std::isinf(Psi2A) || std::isnan(Psi2B) || std::isinf(Psi2B) ? 0. : TMath::Sin(2 * (Psi2A - Psi2B)); + float R2EP_AC_Im = std::isnan(Psi2A) || std::isinf(Psi2A) || std::isnan(Psi2C) || std::isinf(Psi2C) ? 0. : TMath::Sin(2 * (Psi2A - Psi2C)); + float R2EP_BC_Im = std::isnan(Psi2B) || std::isinf(Psi2B) || std::isnan(Psi2C) || std::isinf(Psi2C) ? 0. : TMath::Sin(2 * (Psi2B - Psi2C)); + values[kR2EP_AB] = std::isnan(R2EP_AB) || std::isinf(R2EP_AB) ? 0. : R2EP_AB; + values[kWR2EP_AB] = std::isnan(R2EP_AB) || std::isinf(R2EP_AB) ? 0. : 1.0; + values[kR2EP_AC] = std::isnan(R2EP_AC) || std::isinf(R2EP_AC) ? 0. : R2EP_AC; + values[kWR2EP_AC] = std::isnan(R2EP_AC) || std::isinf(R2EP_AC) ? 0. : 1.0; + values[kR2EP_BC] = std::isnan(R2EP_BC) || std::isinf(R2EP_BC) ? 0. : R2EP_BC; + values[kWR2EP_BC] = std::isnan(R2EP_BC) || std::isinf(R2EP_BC) ? 0. : 1.0; + values[kR2EP_AB_Im] = std::isnan(R2EP_AB_Im) || std::isinf(R2EP_AB_Im) ? 0. : R2EP_AB_Im; + values[kWR2EP_AB_Im] = std::isnan(R2EP_AB_Im) || std::isinf(R2EP_AB_Im) ? 0. : 1.0; + values[kR2EP_AC_Im] = std::isnan(R2EP_AC_Im) || std::isinf(R2EP_AC_Im) ? 0. : R2EP_AC_Im; + values[kWR2EP_AC_Im] = std::isnan(R2EP_AC_Im) || std::isinf(R2EP_AC_Im) ? 0. : 1.0; + values[kR2EP_BC_Im] = std::isnan(R2EP_BC_Im) || std::isinf(R2EP_BC_Im) ? 0. : R2EP_BC_Im; + values[kWR2EP_BC_Im] = std::isnan(R2EP_BC_Im) || std::isinf(R2EP_BC_Im) ? 0. : 1.0; + } + + if constexpr ((fillMap & CollisionQvectCentr) > 0) { + values[kQ1X0A] = -999; + values[kQ1Y0A] = -999; + values[kQ1X0B] = -999; + values[kQ1Y0B] = -999; + values[kQ1X0C] = -999; + values[kQ1Y0C] = -999; + values[kQ2X0A] = event.qvecTPCallRe() / event.nTrkTPCall(); + values[kQ2Y0A] = event.qvecTPCallIm() / event.nTrkTPCall(); + values[kQ2X0APOS] = event.qvecTPCposRe(); + values[kQ2Y0APOS] = event.qvecTPCposIm(); + values[kQ2X0ANEG] = event.qvecTPCnegRe(); + values[kQ2Y0ANEG] = event.qvecTPCnegIm(); + values[kQ2X0B] = event.qvecFT0ARe(); + values[kQ2Y0B] = event.qvecFT0AIm(); + values[kQ2X0C] = event.qvecFT0CRe(); + values[kQ2Y0C] = event.qvecFT0CIm(); + values[kMultA] = event.nTrkTPCall(); + values[kMultAPOS] = event.nTrkTPCpos(); + values[kMultANEG] = event.nTrkTPCneg(); + values[kMultB] = event.sumAmplFT0A(); + values[kMultC] = event.sumAmplFT0C(); + values[kQ3X0A] = -999; + values[kQ3Y0A] = -999; + values[kQ3X0B] = -999; + values[kQ3Y0B] = -999; + values[kQ3X0C] = -999; + values[kQ3Y0C] = -999; + values[kQ4X0A] = -999; + values[kQ4Y0A] = -999; + values[kQ4X0B] = -999; + values[kQ4Y0B] = -999; + values[kQ4X0C] = -999; + values[kQ4Y0C] = -999; + EventPlaneHelper epHelper; float Psi2A = epHelper.GetEventPlane(values[kQ2X0A], values[kQ2Y0A], 2); float Psi2APOS = epHelper.GetEventPlane(values[kQ2X0APOS], values[kQ2Y0APOS], 2); @@ -2283,14 +2567,22 @@ void VarManager::FillEvent(T const& event, float* values) values[kPsi2C] = Psi2C; float R2SP_AB = (values[kQ2X0A] * values[kQ2X0B] + values[kQ2Y0A] * values[kQ2Y0B]); + float R2SP_APOSB = (values[kQ2X0APOS] * values[kQ2X0B] + values[kQ2Y0APOS] * values[kQ2Y0B]); + float R2SP_ANEGB = (values[kQ2X0ANEG] * values[kQ2X0B] + values[kQ2Y0ANEG] * values[kQ2Y0B]); float R2SP_AC = (values[kQ2X0A] * values[kQ2X0C] + values[kQ2Y0A] * values[kQ2Y0C]); + float R2SP_APOSC = (values[kQ2X0APOS] * values[kQ2X0C] + values[kQ2Y0APOS] * values[kQ2Y0C]); + float R2SP_ANEGC = (values[kQ2X0ANEG] * values[kQ2X0C] + values[kQ2Y0ANEG] * values[kQ2Y0C]); float R2SP_BC = (values[kQ2X0B] * values[kQ2X0C] + values[kQ2Y0B] * values[kQ2Y0C]); float R2SP_AB_Im = (values[kQ2Y0A] * values[kQ2X0B] - values[kQ2X0A] * values[kQ2Y0B]); float R2SP_AC_Im = (values[kQ2Y0A] * values[kQ2X0C] - values[kQ2X0A] * values[kQ2Y0C]); float R2SP_BC_Im = (values[kQ2Y0B] * values[kQ2X0C] - values[kQ2X0B] * values[kQ2Y0C]); values[kR2SP_AB] = std::isnan(R2SP_AB) || std::isinf(R2SP_AB) ? 0. : R2SP_AB; + values[kR2SP_FT0CTPCPOS] = std::isnan(R2SP_APOSB) || std::isinf(R2SP_APOSB) ? 0. : R2SP_APOSB; + values[kR2SP_FT0CTPCNEG] = std::isnan(R2SP_ANEGB) || std::isinf(R2SP_ANEGB) ? 0. : R2SP_ANEGB; values[kWR2SP_AB] = std::isnan(R2SP_AB) || std::isinf(R2SP_AB) ? 0. : 1.0; values[kR2SP_AC] = std::isnan(R2SP_AC) || std::isinf(R2SP_AC) ? 0. : R2SP_AC; + values[kR2SP_FT0ATPCPOS] = std::isnan(R2SP_APOSC) || std::isinf(R2SP_APOSC) ? 0. : R2SP_APOSC; + values[kR2SP_FT0ATPCNEG] = std::isnan(R2SP_ANEGC) || std::isinf(R2SP_ANEGC) ? 0. : R2SP_ANEGC; values[kWR2SP_AC] = std::isnan(R2SP_AC) || std::isinf(R2SP_AC) ? 0. : 1.0; values[kR2SP_BC] = std::isnan(R2SP_BC) || std::isinf(R2SP_BC) ? 0. : R2SP_BC; values[kWR2SP_BC] = std::isnan(R2SP_BC) || std::isinf(R2SP_BC) ? 0. : 1.0; @@ -2330,7 +2622,8 @@ void VarManager::FillEvent(T const& event, float* values) values[kMCEventTime] = event.t(); values[kMCEventWeight] = event.weight(); values[kMCEventImpParam] = event.impactParameter(); - if constexpr ((fillMap & CollisionCent) > 0) { + values[kMCEventPlaneAngle] = event.eventPlaneAngle(); + if constexpr (requires { event.bestCollisionCentFT0C(); }) { // WARNING: temporary solution, ongoing work to provide proper MC gen. centrality values[kMCEventCentrFT0C] = event.bestCollisionCentFT0C(); values[kMultMCNParticlesEta05] = event.multMCNParticlesEta05(); @@ -2355,12 +2648,12 @@ void VarManager::FillEvent(T const& event, float* values) } if constexpr ((fillMap & EventFilter) > 0 || (fillMap & RapidityGapFilter) > 0) { - values[kIsDoubleGap] = (event.eventFilter() & (static_cast(1) << kDoubleGap)) > 0; - values[kIsSingleGapA] = (event.eventFilter() & (static_cast(1) << kSingleGapA)) > 0; - values[kIsSingleGapC] = (event.eventFilter() & (static_cast(1) << kSingleGapC)) > 0; - values[kIsSingleGap] = values[kIsSingleGapA] || values[kIsSingleGapC]; - values[kIsNoGap] = !values[kIsDoubleGap] && !values[kIsSingleGap]; - values[kIsITSUPCMode] = (event.eventFilter() & (static_cast(1) << kITSUPCMode)) > 0; + values[kIsDoubleGap] = static_cast((event.eventFilter() & (static_cast(1) << kDoubleGap)) > 0); + values[kIsSingleGapA] = static_cast((event.eventFilter() & (static_cast(1) << kSingleGapA)) > 0); + values[kIsSingleGapC] = static_cast((event.eventFilter() & (static_cast(1) << kSingleGapC)) > 0); + values[kIsSingleGap] = static_cast(values[kIsSingleGapA] != 0.f || values[kIsSingleGapC] != 0.f); + values[kIsNoGap] = static_cast(values[kIsDoubleGap] == 0.f && values[kIsSingleGap] == 0.f); + values[kIsITSUPCMode] = static_cast((event.eventFilter() & (static_cast(1) << kITSUPCMode)) > 0); } if constexpr ((fillMap & ReducedZdc) > 0) { @@ -2370,9 +2663,12 @@ void VarManager::FillEvent(T const& event, float* values) if constexpr ((fillMap & ReducedFit) > 0) { values[kAmplitudeFT0A] = event.amplitudeFT0A(); values[kAmplitudeFT0C] = event.amplitudeFT0C(); + values[kAmplitudeFT0M] = values[kAmplitudeFT0A] + values[kAmplitudeFT0C]; values[kTimeFT0A] = event.timeFT0A(); values[kTimeFT0C] = event.timeFT0C(); values[kTriggerMaskFT0] = event.triggerMaskFT0(); + values[kFT0OrA] = TESTBIT(event.triggerMaskFT0(), o2::fit::Triggers::bitA); + values[kFT0OrC] = TESTBIT(event.triggerMaskFT0(), o2::fit::Triggers::bitC); values[kNFiredChannelsFT0A] = event.nFiredChannelsFT0A(); values[kNFiredChannelsFT0C] = event.nFiredChannelsFT0C(); values[kAmplitudeFDDA] = event.amplitudeFDDA(); @@ -2433,7 +2729,7 @@ template void VarManager::FillEventTracks(T const& tracks, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // compute event properties based on DCAz of the tracks @@ -2531,7 +2827,7 @@ void VarManager::FillEventTracks(T const& tracks, float* values) int counter2mm = 0; int counter5mm = 0; int counter10mm = 0; - for (auto& d : dcazValues) { + for (auto const& d : dcazValues) { double absD = std::abs(d); if (absD > 0.01) { counter100um++; @@ -2569,7 +2865,7 @@ template void VarManager::FillEventFlowResoFactor(T const& hs_sp, T const& hs_ep, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (values[kCentFT0C] >= 0.) { @@ -2585,7 +2881,7 @@ template void VarManager::FillTwoMixEventsFlowResoFactor(T const& hs_sp, T const& hs_ep, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (values[kTwoEvCentFT0C1] >= 0.) { @@ -2609,13 +2905,13 @@ template void VarManager::FillTwoMixEventsCumulants(T const& h_v22ev1, T const& h_v24ev1, T const& h_v22ev2, T const& h_v24ev2, T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } - int idx_v22ev1; - int idx_v24ev1; - int idx_v22ev2; - int idx_v24ev2; + int idx_v22ev1 = 0; + int idx_v24ev1 = 0; + int idx_v22ev2 = 0; + int idx_v24ev2 = 0; if (values[kTwoEvCentFT0C1] >= 0.) { if (t1.sign() < 0) { @@ -2655,7 +2951,7 @@ template void VarManager::FillTwoEvents(T const& ev1, T const& ev2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // if constexpr (T::template contains()) { values[kTwoEvPosZ1] = ev1.posZ(); @@ -2675,7 +2971,7 @@ template void VarManager::FillTwoMixEvents(T1 const& ev1, T1 const& ev2, T2 const& /*tracks1*/, T2 const& /*tracks2*/, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kTwoEvPosZ1] = ev1.posZ(); values[kTwoEvPosZ2] = ev2.posZ(); @@ -2686,8 +2982,8 @@ void VarManager::FillTwoMixEvents(T1 const& ev1, T1 const& ev2, T2 const& /*trac /* uint32_t Track1Filter = 0; uint32_t Track2Filter = 0; - for (auto& track1 : tracks1) { Track1Filter = uint32_t(track1.isMuonSelected());} - for (auto& track2 : tracks2) { Track2Filter = uint32_t(track2.isMuonSelected());} + for (auto const& track1 : tracks1) { Track1Filter = uint32_t(track1.isMuonSelected());} + for (auto const& track2 : tracks2) { Track2Filter = uint32_t(track2.isMuonSelected());} */ if constexpr ((fillMap & CollisionCent) > 0 || (fillMap & ReducedEventExtended) > 0) { values[kTwoEvCentFT0C1] = ev1.centFT0C(); @@ -2699,6 +2995,16 @@ void VarManager::FillTwoMixEvents(T1 const& ev1, T1 const& ev2, T2 const& /*trac values[kQ2X0A2] = ev2.q2x0a(); values[kQ2Y0A1] = ev1.q2y0a(); values[kQ2Y0A2] = ev2.q2y0a(); + + values[kQ2X0B1] = ev1.q2x0b(); + values[kQ2X0B2] = ev2.q2x0b(); + values[kQ2Y0B1] = ev1.q2y0b(); + values[kQ2Y0B2] = ev2.q2y0b(); + + values[kQ2X0C1] = ev1.q2x0c(); + values[kQ2X0C2] = ev2.q2x0c(); + values[kQ2Y0C1] = ev1.q2y0c(); + values[kQ2Y0C2] = ev2.q2y0c(); } if constexpr ((fillMap & CollisionQvect) > 0) { // Tobe used for the calculation of u1q1 and u2q2 @@ -2706,6 +3012,16 @@ void VarManager::FillTwoMixEvents(T1 const& ev1, T1 const& ev2, T2 const& /*trac values[kQ2X0A2] = (ev2.qvecBPosRe() * ev2.nTrkBPos() + ev2.qvecBNegRe() * ev2.nTrkBNeg()) / (ev2.nTrkBPos() + ev2.nTrkBNeg()); values[kQ2Y0A1] = (ev1.qvecBPosIm() * ev1.nTrkBPos() + ev1.qvecBNegIm() * ev1.nTrkBNeg()) / (ev1.nTrkBPos() + ev1.nTrkBNeg()); values[kQ2Y0A2] = (ev2.qvecBPosIm() * ev2.nTrkBPos() + ev2.qvecBNegIm() * ev2.nTrkBNeg()) / (ev2.nTrkBPos() + ev2.nTrkBNeg()); + + values[kQ2X0B1] = ev1.qvecFT0ARe(); + values[kQ2X0B2] = ev2.qvecFT0ARe(); + values[kQ2Y0B1] = ev1.qvecFT0AIm(); + values[kQ2Y0B2] = ev2.qvecFT0AIm(); + + values[kQ2X0C1] = ev1.qvecFT0CRe(); + values[kQ2X0C2] = ev2.qvecFT0CRe(); + values[kQ2Y0C1] = ev1.qvecFT0CIm(); + values[kQ2Y0C2] = ev2.qvecFT0CIm(); } } @@ -2713,7 +3029,7 @@ template void VarManager::FillTrack(T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & TrackMFT) > 0) { @@ -2758,23 +3074,17 @@ void VarManager::FillTrack(T const& track, float* values) values[kPhi] = track.phi(); values[kCharge] = track.sign(); if (fgUsedVars[kPhiTPCOuter]) { - values[kPhiTPCOuter] = track.phi() - (track.sign() > 0 ? 1.0 : -1.0) * (TMath::PiOver2() - TMath::ACos(0.22 * fgMagField / track.pt())); - if (values[kPhiTPCOuter] > TMath::TwoPi()) { - values[kPhiTPCOuter] -= TMath::TwoPi(); - } - if (values[kPhiTPCOuter] < 0.0) { - values[kPhiTPCOuter] += TMath::TwoPi(); - } + values[kPhiTPCOuter] = RecoDecay::constrainAngle(track.phi() - (track.sign() > 0 ? 1.0 : -1.0) * (o2::constants::math::PIHalf - TMath::ACos(0.22 * fgMagField / track.pt()))); } if (fgUsedVars[kTrackIsInsideTPCModule]) { - float localSectorPhi = values[kPhiTPCOuter] - TMath::Floor(18.0 * values[kPhiTPCOuter] / TMath::TwoPi()) * (TMath::TwoPi() / 18.0); + float localSectorPhi = values[kPhiTPCOuter] - TMath::Floor(18.0 * values[kPhiTPCOuter] / o2::constants::math::TwoPI) * (o2::constants::math::TwoPI / 18.0); float edge = fgTPCInterSectorBoundary / 2.0 / 246.6; // minimal inter-sector boundary as angle float curvature = 3.0 * 3.33 * track.pt() / fgMagField * (1.0 - TMath::Sin(TMath::ACos(0.22 * fgMagField / track.pt()))); if (curvature / 2.466 > edge) { edge = curvature / 2.466; } double min = edge; - double max = TMath::TwoPi() / 18.0 - edge; + double max = o2::constants::math::TwoPI / 18.0 - edge; values[kTrackIsInsideTPCModule] = (localSectorPhi > min && localSectorPhi < max ? 1.0 : 0.0); } @@ -2789,7 +3099,7 @@ void VarManager::FillTrack(T const& track, float* values) values[kIsLegFromAntiLambda] = track.filteringFlags_bit(VarManager::kIsALambdaLeg); values[kIsLegFromOmega] = track.filteringFlags_bit(VarManager::kIsOmegaLeg); - values[kIsProtonFromLambdaAndAntiLambda] = static_cast((values[kIsLegFromLambda] * track.sign() > 0) || (values[kIsLegFromAntiLambda] * (-track.sign()) > 0)); + values[kIsProtonFromLambdaAndAntiLambda] = static_cast((values[kIsLegFromLambda] * track.sign() > 0) || (values[kIsLegFromAntiLambda] * (-track.sign()) > 0)); for (int i = 0; i < 8; i++) { values[kIsDalitzLeg + i] = track.filteringFlags_bit(VarManager::kDalitzBits + i); @@ -2826,44 +3136,44 @@ void VarManager::FillTrack(T const& track, float* values) values[kPin] = track.tpcInnerParam(); values[kSignedPin] = track.tpcInnerParam() * track.sign(); if (fgUsedVars[kIsITSrefit]) { - values[kIsITSrefit] = (track.flags() & o2::aod::track::ITSrefit) > 0; // NOTE: This is just for Run-2 + values[kIsITSrefit] = static_cast((track.flags() & o2::aod::track::ITSrefit) > 0); // NOTE: This is just for Run-2 } if (fgUsedVars[kTrackTimeResIsRange]) { - values[kTrackTimeResIsRange] = (track.flags() & o2::aod::track::TrackTimeResIsRange) > 0; // NOTE: This is NOT for Run-2 + values[kTrackTimeResIsRange] = static_cast((track.flags() & o2::aod::track::TrackTimeResIsRange) > 0); // NOTE: This is NOT for Run-2 } if (fgUsedVars[kIsTPCrefit]) { - values[kIsTPCrefit] = (track.flags() & o2::aod::track::TPCrefit) > 0; // NOTE: This is just for Run-2 + values[kIsTPCrefit] = static_cast((track.flags() & o2::aod::track::TPCrefit) > 0); // NOTE: This is just for Run-2 } if (fgUsedVars[kPVContributor]) { - values[kPVContributor] = (track.flags() & o2::aod::track::PVContributor) > 0; // NOTE: This is NOT for Run-2 + values[kPVContributor] = static_cast((track.flags() & o2::aod::track::PVContributor) > 0); // NOTE: This is NOT for Run-2 } if (fgUsedVars[kIsGoldenChi2]) { - values[kIsGoldenChi2] = (track.flags() & o2::aod::track::GoldenChi2) > 0; // NOTE: This is just for Run-2 + values[kIsGoldenChi2] = static_cast((track.flags() & o2::aod::track::GoldenChi2) > 0); // NOTE: This is just for Run-2 } if (fgUsedVars[kOrphanTrack]) { - values[kOrphanTrack] = (track.flags() & o2::aod::track::OrphanTrack) > 0; // NOTE: This is NOT for Run-2 + values[kOrphanTrack] = static_cast((track.flags() & o2::aod::track::OrphanTrack) > 0); // NOTE: This is NOT for Run-2 } if (fgUsedVars[kIsSPDfirst]) { - values[kIsSPDfirst] = (track.itsClusterMap() & uint8_t(1)) > 0; + values[kIsSPDfirst] = static_cast((track.itsClusterMap() & static_cast(1)) > 0); } if (fgUsedVars[kIsSPDboth]) { - values[kIsSPDboth] = (track.itsClusterMap() & uint8_t(3)) > 0; + values[kIsSPDboth] = static_cast((track.itsClusterMap() & static_cast(3)) > 0); } if (fgUsedVars[kIsSPDany]) { - values[kIsSPDany] = (track.itsClusterMap() & uint8_t(1)) || (track.itsClusterMap() & uint8_t(2)); + values[kIsSPDany] = static_cast((track.itsClusterMap() & static_cast(1)) || (track.itsClusterMap() & static_cast(2))); } if (fgUsedVars[kITSClusterMap]) { values[kITSClusterMap] = track.itsClusterMap(); } if (fgUsedVars[kIsITSibFirst]) { - values[kIsITSibFirst] = (track.itsClusterMap() & uint8_t(1)) > 0; + values[kIsITSibFirst] = static_cast((track.itsClusterMap() & static_cast(1)) > 0); } if (fgUsedVars[kIsITSibAny]) { - values[kIsITSibAny] = (track.itsClusterMap() & (1 << uint8_t(0))) > 0 || (track.itsClusterMap() & (1 << uint8_t(1))) > 0 || (track.itsClusterMap() & (1 << uint8_t(2))) > 0; + values[kIsITSibAny] = static_cast((track.itsClusterMap() & (1 << static_cast(0))) > 0 || (track.itsClusterMap() & (1 << static_cast(1))) > 0 || (track.itsClusterMap() & (1 << static_cast(2))) > 0); } if (fgUsedVars[kIsITSibAll]) { - values[kIsITSibAll] = (track.itsClusterMap() & (1 << uint8_t(0))) > 0 && (track.itsClusterMap() & (1 << uint8_t(1))) > 0 && (track.itsClusterMap() & (1 << uint8_t(2))) > 0; + values[kIsITSibAll] = static_cast((track.itsClusterMap() & (1 << static_cast(0))) > 0 && (track.itsClusterMap() & (1 << static_cast(1))) > 0 && (track.itsClusterMap() & (1 << static_cast(2))) > 0); } values[kTrackTime] = track.trackTime(); @@ -2912,6 +3222,9 @@ void VarManager::FillTrack(T const& track, float* values) values[kITSncls] += ((track.itsClusterMap() & (1 << i)) ? 1 : 0); } } + if (fgUsedVars[kTPCnCRoverFindCls]) { + values[kTPCnCRoverFindCls] = values[kTPCnclsCR] / values[kTPCncls]; + } values[kTrackDCAxy] = track.dcaXY(); values[kTrackDCAz] = track.dcaZ(); if constexpr ((fillMap & ReducedTrackBarrelCov) > 0) { @@ -2969,18 +3282,18 @@ void VarManager::FillTrack(T const& track, float* values) // Quantities based on the dalitz selections if constexpr ((fillMap & DalitzBits) > 0) { for (int i = 0; i < 8; i++) { - values[kIsDalitzLeg + i] = static_cast(track.dalitzBits() & (uint8_t(1) << i)); + values[kIsDalitzLeg + i] = static_cast(track.dalitzBits() & (static_cast(1) << i)); } } // Quantities based on the V0 selections if constexpr ((fillMap & TrackV0Bits) > 0) { - values[kIsLegFromGamma] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsConversionLeg)); - values[kIsLegFromK0S] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsK0sLeg)); - values[kIsLegFromLambda] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsLambdaLeg)); - values[kIsLegFromAntiLambda] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsALambdaLeg)); - values[kIsLegFromOmega] = static_cast(track.pidbit() & (uint8_t(1) << VarManager::kIsOmegaLeg)); - values[kIsProtonFromLambdaAndAntiLambda] = static_cast((values[kIsLegFromLambda] * track.sign() > 0) || (values[kIsLegFromAntiLambda] * (-track.sign()) > 0)); + values[kIsLegFromGamma] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsConversionLeg)); + values[kIsLegFromK0S] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsK0sLeg)); + values[kIsLegFromLambda] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsLambdaLeg)); + values[kIsLegFromAntiLambda] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsALambdaLeg)); + values[kIsLegFromOmega] = static_cast(track.pidbit() & (static_cast(1) << VarManager::kIsOmegaLeg)); + values[kIsProtonFromLambdaAndAntiLambda] = static_cast((values[kIsLegFromLambda] * track.sign() > 0) || (values[kIsLegFromAntiLambda] * (-track.sign()) > 0)); } // Quantities based on the barrel PID tables @@ -3078,8 +3391,10 @@ void VarManager::FillTrack(T const& track, float* values) values[kMuonChi2MatchMCHMFT] = track.chi2MatchMCHMFT(); values[kMuonMatchScoreMCHMFT] = track.matchScoreMCHMFT(); values[kMuonTrackType] = track.trackType(); - values[kMuonDCAx] = track.fwdDcaX(); - values[kMuonDCAy] = track.fwdDcaY(); + if constexpr ((fillMap & ReducedMuonExtra) > 0) { + values[kMuonDCAx] = track.fwdDcaX(); + values[kMuonDCAy] = track.fwdDcaY(); + } values[kMuonTime] = track.trackTime(); values[kMuonTimeRes] = track.trackTimeRes(); } @@ -3106,7 +3421,11 @@ void VarManager::FillTrack(T const& track, float* values) values[kMuonC1Pt21Pt2] = track.c1Pt21Pt2(); } if constexpr ((fillMap & MuonCov) > 0 || (fillMap & MuonCovRealign) > 0) { - auto muonTrack = o2::aod::fwdtrackutils::getTrackParCovFwdShift(track, fgzShiftFwd, track); + float xShift = 0.f; + float yShift = 0.f; + float zShift = 0.f; + GetFwdShiftForY(track.y(), xShift, yShift, zShift); + auto muonTrack = o2::aod::fwdtrackutils::getTrackParCovFwd3DShift(track, xShift, yShift, zShift, track); auto muonCov = muonTrack.getCovariances(); values[kX] = muonTrack.getX(); values[kY] = muonTrack.getY(); @@ -3145,7 +3464,7 @@ void VarManager::FillTrackCollision(T const& track, C const& collision, float* v { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & ReducedTrackBarrel) > 0 || (fillMap & TrackDCA) > 0) { auto trackPar = getTrackPar(track); @@ -3165,21 +3484,50 @@ void VarManager::FillTrackCollision(T const& track, C const& collision, float* v } } if constexpr ((fillMap & MuonCov) > 0 || (fillMap & MuonCovRealign) > 0 || (fillMap & ReducedMuonCov) > 0) { + float dcaX = 999.f; + float dcaY = 999.f; + if (static_cast(track.trackType()) <= 2) { + // Global / MCH-MID: helix DCA only (for globals, kMuonPDca is filled from the matched MCH in skimMuons) + float xShift = 0.f; + float yShift = 0.f; + float zShift = 0.f; + GetFwdShiftForY(track.y(), xShift, yShift, zShift); + o2::track::TrackParCovFwd fwdtrack = o2::aod::fwdtrackutils::getTrackParCovFwd3DShift(track, xShift, yShift, zShift, track); + std::array dca{999., 999., 999.}; + fwdtrack.propagateToDCAhelix(fgMagField, {collision.posX(), collision.posY(), collision.posZ()}, dca); + dcaX = static_cast(dca[0]); + dcaY = static_cast(dca[1]); + } else { + // MCH standalone: DCA and pDCA from MCH extrapolation + o2::dataformats::GlobalFwdTrack propmuonAtDCA = PropagateMuon(track, collision, kToDCA); + dcaX = propmuonAtDCA.getX() - collision.posX(); + dcaY = propmuonAtDCA.getY() - collision.posY(); + float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); + values[kMuonPDca] = track.p() * dcaXY; + } - o2::dataformats::GlobalFwdTrack propmuonAtDCA = PropagateMuon(track, collision, kToDCA); - - float dcaX = (propmuonAtDCA.getX() - collision.posX()); - float dcaY = (propmuonAtDCA.getY() - collision.posY()); - float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - values[kMuonPDca] = track.p() * dcaXY; + values[kMuonDCAx] = dcaX; + values[kMuonDCAy] = dcaY; } } +template +bool VarManager::isBarrelAssocTimeCompatible(T const& track, C const& collision, C const& origCollision, + float nSigma, float timeMargin, int bcWindowForOneSigma, + int usePVAssociation, int maxPvContribLowMult, float* values) +{ + return computeBarrelAssocTimeCompat(track.trackTime(), track.trackTimeRes(), + (track.flags() & o2::aod::track::TrackTimeResIsRange) > 0, track.isPVContributor(), + static_cast(origCollision.globalBC()), origCollision.collisionTime(), origCollision.numContrib(), + static_cast(collision.globalBC()), collision.collisionTime(), collision.collisionTimeRes(), + nSigma, timeMargin, bcWindowForOneSigma, usePVAssociation, maxPvContribLowMult, values); +} + template void VarManager::FillTrackCollisionMatCorr(T const& track, C const& collision, M const& materialCorr, P const& propagator, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & ReducedTrackBarrel) > 0 || (fillMap & TrackDCA) > 0) { auto trackPar = getTrackPar(track); @@ -3207,7 +3555,7 @@ template void VarManager::FillPhoton(T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Quantities based on the basic table (contains just kine information and filter bits) @@ -3235,11 +3583,40 @@ void VarManager::FillPhoton(T const& track, float* values) } } +template +void VarManager::FillTrackEMCal(T const& cluster, float trackP, float deltaEta, float deltaPhi, float* values) +{ + // Fill the EMCal cluster quantities; the cluster can be either an aod::EMCALCluster (skimming) + // or an aod::ReducedEMCal (analysis on skimmed data), the column schemas are identical. + // trackP: momentum of the matched track, used to compute E/p when called in a track-cluster matching context; + // the default (negative) value leaves E/p at the -999 sentinel, as for tracks without a matched cluster. + if (!values) { + values = static_cast(fgValues); + } + + values[kEMCalEnergy] = cluster.energy(); + values[kEMCalCoreEnergy] = cluster.coreEnergy(); + values[kEMCalRawEnergy] = cluster.rawEnergy(); + values[kEMCalEta] = cluster.eta(); + values[kEMCalPhi] = cluster.phi(); + values[kEMCalM02] = cluster.m02(); + values[kEMCalM20] = cluster.m20(); + values[kEMCalNCells] = cluster.nCells(); + values[kEMCalTime] = cluster.time(); + values[kEMCalIsExotic] = cluster.isExotic(); + values[kEMCalDistanceToBadChannel] = cluster.distanceToBadChannel(); + values[kEMCalNLM] = cluster.nlm(); + values[kEMCalDefinition] = cluster.definition(); + values[kEMCalEoverP] = (trackP > 0.0f ? cluster.energy() / trackP : -999.0f); + values[kEMCalMatchDeltaEta] = deltaEta; + values[kEMCalMatchDeltaPhi] = deltaPhi; +} + template void VarManager::FillTrackMC(const U& mcStack, T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Quantities based on the mc particle table @@ -3273,18 +3650,18 @@ void VarManager::FillTrackCollisionMC(T1 const& track, T2 const& MotherTrack, C { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m = o2::constants::physics::MassBPlus; float pdgLifetime = 1.638e-12; // s - if (std::abs(MotherTrack.pdgCode()) == 511) { + if (std::abs(MotherTrack.pdgCode()) == o2::constants::physics::Pdg::kB0) { m = o2::constants::physics::MassB0; pdgLifetime = 1.517e-12; // s } // Extract the collision primary vertex position using constexpr, since the collision type may be CollisionMC or ReducedMCEvent - double collPos[3] = {0.0, 0.0, 0.0}; + std::array collPos = {0.0, 0.0, 0.0}; if constexpr (fillMap & ObjTypes::CollisionMC) { collPos[0] = collision.posX(); collPos[1] = collision.posY(); @@ -3327,12 +3704,13 @@ void VarManager::FillTrackCollisionMC(T1 const& track, const std::array(fgValues); } float m = o2::constants::physics::MassJPsi; - if (massHyp) + if (massHyp != 0.f) { m = massHyp; + } // displaced vertex is computed with decay product (track) and vertex collPos values[kMCVertexingLxy] = (collPos[0] - track.vx()) * (collPos[0] - track.vx()) + @@ -3357,13 +3735,10 @@ template void VarManager::FillEnergyCorrelatorsMC(T const& track, T1 const& t1, float* values, float Translow, float Transhigh, float Accweight) { // energy correlators - float MassHadron; + float MassHadron = o2::constants::physics::MassPionCharged; if constexpr (pairType == kJpsiHadronMass) { MassHadron = TMath::Sqrt(t1.e() * t1.e() - t1.p() * t1.p()); } - if constexpr (pairType == kJpsiPionMass) { - MassHadron = o2::constants::physics::MassPionCharged; - } ROOT::Math::PtEtaPhiMVector v1(track.pt(), track.eta(), track.phi(), o2::constants::physics::MassJPsi); float deltaphi = RecoDecay::constrainAngle(track.phi() - t1.phi(), -o2::constants::math::PIHalf); float deltaeta = t1.eta() - track.eta(); @@ -3397,7 +3772,7 @@ void VarManager::FillEnergyCorrelatorsMC(T const& track, T1 const& t1, float* va float randomPhi_away = -o2::constants::math::PIHalf; float deltaphitrans = RecoDecay::constrainAngle(track.phi() - t1.phi(), -o2::constants::math::PI); - if ((deltaphitrans > -Transhigh * TMath::Pi() && deltaphitrans < -Translow * TMath::Pi()) || (deltaphitrans > Translow * TMath::Pi() && deltaphitrans < Transhigh * TMath::Pi())) { + if ((deltaphitrans > -Transhigh * o2::constants::math::PI && deltaphitrans < -Translow * o2::constants::math::PI) || (deltaphitrans > Translow * o2::constants::math::PI && deltaphitrans < Transhigh * o2::constants::math::PI)) { randomPhi_trans = gRandom->Uniform(-o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf); randomPhi_toward = gRandom->Uniform(-o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf); randomPhi_away = gRandom->Uniform(-o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf); @@ -3425,7 +3800,7 @@ template void VarManager::FillPairPropagateMuon(T1 const& muon1, T2 const& muon2, const C& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } o2::dataformats::GlobalFwdTrack propmuon1 = PropagateMuon(muon1, collision); o2::dataformats::GlobalFwdTrack propmuon2 = PropagateMuon(muon2, collision); @@ -3450,7 +3825,7 @@ template void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -3487,7 +3862,7 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kPt] = v12.Pt(); values[kEta] = v12.Eta(); // values[kPhi] = v12.Phi(); - values[kPhi] = v12.Phi() > 0 ? v12.Phi() : v12.Phi() + 2. * M_PI; + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); values[kRap] = -v12.Rapidity(); double Ptot1 = TMath::Sqrt(v1.Px() * v1.Px() + v1.Py() * v1.Py() + v1.Pz() * v1.Pz()); double Ptot2 = TMath::Sqrt(v2.Px() * v2.Px() + v2.Py() * v2.Py() + v2.Pz() * v2.Pz()); @@ -3501,14 +3876,7 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kPhi2] = t2.phi(); if (fgUsedVars[kDeltaPhiPair2]) { - double phipair2 = v1.Phi() - v2.Phi(); - if (phipair2 > 3 * TMath::Pi() / 2) { - values[kDeltaPhiPair2] = phipair2 - 2 * TMath::Pi(); - } else if (phipair2 < -TMath::Pi() / 2) { - values[kDeltaPhiPair2] = phipair2 + 2 * TMath::Pi(); - } else { - values[kDeltaPhiPair2] = phipair2; - } + values[kDeltaPhiPair2] = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PIHalf); } if (fgUsedVars[kDeltaEtaPair2]) { @@ -3528,21 +3896,25 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kOpeningAngle] = 0.; } else { double arg = scalar / Ptot12; - if (arg > 1.) + if (arg > 1.) { arg = 1.; - if (arg < -1) + } + if (arg < -1) { arg = -1; + } values[kOpeningAngle] = TMath::ACos(arg); } } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -3556,26 +3928,18 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_HE{(v12.Vect()).Unit()}; ROOT::Math::XYZVectorF yaxis_HE{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_HE{(yaxis_HE.Cross(zaxis_HE)).Unit()}; - if (fgUsedVars[kCosThetaHE]) + if (fgUsedVars[kCosThetaHE]) { values[kCosThetaHE] = zaxis_HE.Dot(v_CM); + } if (fgUsedVars[kPhiHE]) { - values[kPhiHE] = TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM)); - if (values[kPhiHE] < 0) { - values[kPhiHE] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiHE] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM))); } if (fgUsedVars[kPhiTildeHE]) { if (fgUsedVars[kCosThetaHE] && fgUsedVars[kPhiHE]) { if (values[kCosThetaHE] > 0) { - values[kPhiTildeHE] = values[kPhiHE] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildeHE] < 0) { - values[kPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeHE] = RecoDecay::constrainAngle(values[kPhiHE] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildeHE] = values[kPhiHE] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildeHE] < 0) { - values[kPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeHE] = RecoDecay::constrainAngle(values[kPhiHE] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildeHE] = -999; // not computable @@ -3587,26 +3951,18 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_CS{(Beam1_CM - Beam2_CM).Unit()}; ROOT::Math::XYZVectorF yaxis_CS{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_CS{(yaxis_CS.Cross(zaxis_CS)).Unit()}; - if (fgUsedVars[kCosThetaCS]) + if (fgUsedVars[kCosThetaCS]) { values[kCosThetaCS] = zaxis_CS.Dot(v_CM); + } if (fgUsedVars[kPhiCS]) { - values[kPhiCS] = TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM)); - if (values[kPhiCS] < 0) { - values[kPhiCS] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiCS] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM))); } if (fgUsedVars[kPhiTildeCS]) { if (fgUsedVars[kCosThetaCS] && fgUsedVars[kPhiCS]) { if (values[kCosThetaCS] > 0) { - values[kPhiTildeCS] = values[kPhiCS] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildeCS] < 0) { - values[kPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeCS] = RecoDecay::constrainAngle(values[kPhiCS] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildeCS] = values[kPhiCS] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildeCS] < 0) { - values[kPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeCS] = RecoDecay::constrainAngle(values[kPhiCS] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildeCS] = -999; // not computable @@ -3614,6 +3970,70 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -3622,23 +4042,14 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kCosThetaPP] = zaxis_PP.Dot(v_CM) / std::sqrt(zaxis_PP.Mag2()); } if (fgUsedVars[kPhiPP]) { - values[kPhiPP] = TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM)); - if (values[kPhiPP] < 0) { - values[kPhiPP] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiPP] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM))); } if (fgUsedVars[kPhiTildePP]) { if (fgUsedVars[kCosThetaPP] && fgUsedVars[kPhiPP]) { if (values[kCosThetaPP] > 0) { - values[kPhiTildePP] = values[kPhiPP] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildePP] < 0) { - values[kPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildePP] = RecoDecay::constrainAngle(values[kPhiPP] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildePP] = values[kPhiPP] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildePP] < 0) { - values[kPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildePP] = RecoDecay::constrainAngle(values[kPhiPP] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildePP] = -999; // not computable @@ -3648,48 +4059,92 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) if (useRM) { double randomCostheta = gRandom->Uniform(-1., 1.); - double randomPhi = gRandom->Uniform(0., 2. * TMath::Pi()); + double randomPhi = gRandom->Uniform(0., o2::constants::math::TwoPI); ROOT::Math::XYZVectorF zaxis_RM(randomCostheta, std::sqrt(1 - randomCostheta * randomCostheta) * std::cos(randomPhi), std::sqrt(1 - randomCostheta * randomCostheta) * std::sin(randomPhi)); - if (fgUsedVars[kCosThetaRM]) + if (fgUsedVars[kCosThetaRM]) { values[kCosThetaRM] = zaxis_RM.Dot(v_CM); + } } } - if constexpr ((pairType == kDecayToEE) && ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0)) { + if (fgUsedVars[kCosThetaStarRandom]) { + ROOT::Math::Boost boostv12{v12.BoostToCM()}; + ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; + ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; - if (fgUsedVars[kQuadDCAabsXY] || fgUsedVars[kQuadDCAsigXY] || fgUsedVars[kQuadDCAabsZ] || fgUsedVars[kQuadDCAsigZ] || fgUsedVars[kQuadDCAsigXYZ] || fgUsedVars[kSignQuadDCAsigXY]) { - // Quantities based on the barrel tables + // using positive sign convention for the first track + ROOT::Math::XYZVectorF v_CM = (t1.sign() > 0 ? v1_CM : v2_CM); + + // Randomize the event plane angle to check the unpolarized contribution + ROOT::Math::XYZVector zaxisRandom = ROOT::Math::XYZVector(TMath::Cos(values[kRandomPsi2]), TMath::Sin(values[kRandomPsi2]), 0).Unit(); + values[kCosThetaStarRandom] = v_CM.Dot(zaxisRandom); + values[kCos2ThetaStarRandom] = values[kCosThetaStarRandom] * values[kCosThetaStarRandom]; + + // if the truth event plane angle is available, calculate the cos(theta*) with respect to the true event plane angle for comparison + if (fgUsedVars[kMCCosThetaStar] && fgUsedVars[kMCEventPlaneAngle]) { + // truth event plane angle + ROOT::Math::XYZVector zaxisTrue = ROOT::Math::XYZVector(TMath::Cos(values[kMCEventPlaneAngle]), TMath::Sin(values[kMCEventPlaneAngle]), 0).Unit(); + values[kMCCosThetaStar] = v_CM.Dot(zaxisTrue); + } + } + + if constexpr ((pairType == kDecayToEE)) { + if constexpr ((fillMap & ReducedTrackBarrel) > 0) { + values[kITSclusterMap1] = t1.itsClusterMap(); + values[kITSclusterMap2] = t2.itsClusterMap(); + } + + if constexpr ((fillMap & ReducedTrackBarrelPID) > 0) { + values[kTPCnSigmaEl1] = t1.tpcNSigmaEl(); + values[kTPCnSigmaEl2] = t2.tpcNSigmaEl(); + } + + if constexpr (((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0)) { double dca1XY = t1.dcaXY(); double dca2XY = t2.dcaXY(); double dca1Z = t1.dcaZ(); double dca2Z = t2.dcaZ(); + + values[kDCAxy1] = dca1XY; + values[kDCAz1] = dca1Z; + values[kDCAxy2] = dca2XY; + values[kDCAz2] = dca2Z; + double dca1sigXY = dca1XY / std::sqrt(t1.cYY()); double dca2sigXY = dca2XY / std::sqrt(t2.cYY()); double dca1sigZ = dca1Z / std::sqrt(t1.cZZ()); double dca2sigZ = dca2Z / std::sqrt(t2.cZZ()); - values[kQuadDCAabsXY] = std::sqrt((dca1XY * dca1XY + dca2XY * dca2XY) / 2); - values[kQuadDCAsigXY] = std::sqrt((dca1sigXY * dca1sigXY + dca2sigXY * dca2sigXY) / 2); - values[kQuadDCAabsZ] = std::sqrt((dca1Z * dca1Z + dca2Z * dca2Z) / 2); - values[kQuadDCAsigZ] = std::sqrt((dca1sigZ * dca1sigZ + dca2sigZ * dca2sigZ) / 2); - values[kSignQuadDCAsigXY] = t1.sign() * t2.sign() * TMath::Sign(1., dca1sigXY) * TMath::Sign(1., dca2sigXY) * std::sqrt((dca1sigXY * dca1sigXY + dca2sigXY * dca2sigXY) / 2); + values[kDCAxyOverSig1] = dca1sigXY; + values[kDCAzOverSig1] = dca1sigZ; + values[kDCAxyOverSig2] = dca2sigXY; + values[kDCAzOverSig2] = dca2sigZ; + + if (fgUsedVars[kQuadDCAabsXY] || fgUsedVars[kQuadDCAsigXY] || fgUsedVars[kQuadDCAabsZ] || fgUsedVars[kQuadDCAsigZ] || fgUsedVars[kQuadDCAsigXYZ] || fgUsedVars[kSignQuadDCAsigXY]) { + // Quantities based on the barrel tables + values[kQuadDCAabsXY] = std::sqrt((dca1XY * dca1XY + dca2XY * dca2XY) / 2); + values[kQuadDCAsigXY] = std::sqrt((dca1sigXY * dca1sigXY + dca2sigXY * dca2sigXY) / 2); + values[kQuadDCAabsZ] = std::sqrt((dca1Z * dca1Z + dca2Z * dca2Z) / 2); + values[kQuadDCAsigZ] = std::sqrt((dca1sigZ * dca1sigZ + dca2sigZ * dca2sigZ) / 2); + values[kSignQuadDCAsigXY] = t1.sign() * t2.sign() * TMath::Sign(1., dca1sigXY) * TMath::Sign(1., dca2sigXY) * std::sqrt((dca1sigXY * dca1sigXY + dca2sigXY * dca2sigXY) / 2); + + double det1 = t1.cYY() * t1.cZZ() - t1.cZY() * t1.cZY(); + double det2 = t2.cYY() * t2.cZZ() - t2.cZY() * t2.cZY(); + if ((det1 < 0) || (det2 < 0)) { + values[kQuadDCAsigXYZ] = -999; + } else { + double chi2t1 = (dca1XY * dca1XY * t1.cZZ() + dca1Z * dca1Z * t1.cYY() - 2. * dca1XY * dca1Z * t1.cZY()) / det1; + double chi2t2 = (dca2XY * dca2XY * t2.cZZ() + dca2Z * dca2Z * t2.cYY() - 2. * dca2XY * dca2Z * t2.cZY()) / det2; - double det1 = t1.cYY() * t1.cZZ() - t1.cZY() * t1.cZY(); - double det2 = t2.cYY() * t2.cZZ() - t2.cZY() * t2.cZY(); - if ((det1 < 0) || (det2 < 0)) { - values[kQuadDCAsigXYZ] = -999; - } else { - double chi2t1 = (dca1XY * dca1XY * t1.cZZ() + dca1Z * dca1Z * t1.cYY() - 2. * dca1XY * dca1Z * t1.cZY()) / det1; - double chi2t2 = (dca2XY * dca2XY * t2.cZZ() + dca2Z * dca2Z * t2.cYY() - 2. * dca2XY * dca2Z * t2.cZY()) / det2; + double dca1sigXYZ = std::sqrt(std::abs(chi2t1) / 2.); + double dca2sigXYZ = std::sqrt(std::abs(chi2t2) / 2.); - double dca1sigXYZ = std::sqrt(std::abs(chi2t1) / 2.); - double dca2sigXYZ = std::sqrt(std::abs(chi2t2) / 2.); - - values[kQuadDCAsigXYZ] = std::sqrt((dca1sigXYZ * dca1sigXYZ + dca2sigXYZ * dca2sigXYZ) / 2); + values[kQuadDCAsigXYZ] = std::sqrt((dca1sigXYZ * dca1sigXYZ + dca2sigXYZ * dca2sigXYZ) / 2); + } } } } - if constexpr ((pairType == kDecayToMuMu) && ((fillMap & Muon) > 0 || (fillMap & ReducedMuon) > 0)) { + if constexpr ((pairType == kDecayToMuMu) && ((fillMap & ReducedMuonExtra) > 0)) { if (fgUsedVars[kQuadDCAabsXY]) { double dca1X = t1.fwdDcaX(); double dca1Y = t1.fwdDcaY(); @@ -3700,16 +4155,93 @@ void VarManager::FillPair(T1 const& t1, T2 const& t2, float* values) values[kQuadDCAabsXY] = std::sqrt((dca1XY * dca1XY + dca2XY * dca2XY) / 2.); } } + if constexpr ((pairType == kElectronMuon) && ((fillMap & ReducedTrackBarrel) > 0)) { + // DCA of the barrel (electron) leg; not filled in FillPairME since the mixed-event barrel track type has no DCA columns + values[kDCAxy1] = t1.dcaXY(); + values[kDCAz1] = t1.dcaZ(); + } if (fgUsedVars[kPairPhiv]) { values[kPairPhiv] = calculatePhiV(t1, t2); } } +// change_start: rotation pair +template +void VarManager::FillPairRotation(T1 const& t1, T2 const& t2, int rotation, float* values) +{ + if (!values) { + values = static_cast(fgValues); + } + + float m1 = o2::constants::physics::MassElectron; + float m2 = o2::constants::physics::MassElectron; + if constexpr (pairType == kDecayToMuMu) { + m1 = o2::constants::physics::MassMuon; + m2 = o2::constants::physics::MassMuon; + } + + if constexpr (pairType == kDecayToPiPi) { + m1 = o2::constants::physics::MassPionCharged; + m2 = o2::constants::physics::MassPionCharged; + } + + if constexpr (pairType == kDecayToKPi) { + m1 = o2::constants::physics::MassKaonCharged; + m2 = o2::constants::physics::MassPionCharged; + // Make the TPC information of the kaon available for pair histograms + values[kPin_leg1] = t1.tpcInnerParam(); + values[kTPCnSigmaKa_leg1] = t1.tpcNSigmaKa(); + } + + if constexpr (pairType == kElectronMuon) { + m2 = o2::constants::physics::MassMuon; + } + + double rotationphi2 = t2.phi(); + + if (rotation == 1) { + rotationphi2 = t2.phi() + o2::constants::math::PI; + } else if (rotation == 2) { + rotationphi2 = 2 * values[kPsi2A] - t2.phi(); + } else if (rotation == 3) { + rotationphi2 = 2 * values[kPsi2A] - t2.phi() + o2::constants::math::PI; + } + + if (rotationphi2 >= o2::constants::math::TwoPI) { + rotationphi2 -= o2::constants::math::TwoPI; + } else if (rotationphi2 < 0) { + rotationphi2 += o2::constants::math::TwoPI; + } + + values[kCharge] = t1.sign() + t2.sign(); + values[kCharge1] = t1.sign(); + values[kCharge2] = t2.sign(); + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), m1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), rotationphi2, m2); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + // values[kPhi] = v12.Phi(); + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); + values[kRap] = -v12.Rapidity(); + double Ptot1 = TMath::Sqrt(v1.Px() * v1.Px() + v1.Py() * v1.Py() + v1.Pz() * v1.Pz()); + double Ptot2 = TMath::Sqrt(v2.Px() * v2.Px() + v2.Py() * v2.Py() + v2.Pz() * v2.Pz()); + values[kDeltaPtotTracks] = Ptot1 - Ptot2; + + values[kPt1] = t1.pt(); + values[kEta1] = t1.eta(); + values[kPhi1] = t1.phi(); + values[kPt2] = t2.pt(); + values[kEta2] = t2.eta(); + values[kPhi2] = rotationphi2; +} + template void VarManager::FillPairCollision(const C& collision, T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((pairType == kDecayToEE) && ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0)) { @@ -3761,7 +4293,7 @@ template (fgValues); } if constexpr ((pairType == kDecayToEE) && ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0)) { @@ -3820,7 +4352,7 @@ void VarManager::FillTriple(T1 const& t1, T2 const& t2, T3 const& t3, float* val { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (pairType == kTripleCandidateToEEPhoton) { float m1 = o2::constants::physics::MassElectron; @@ -3894,7 +4426,7 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) // Lightweight fill function called from the innermost event mixing loop // if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -3920,18 +4452,19 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) values[kPt] = v12.Pt(); values[kEta] = v12.Eta(); // values[kPhi] = v12.Phi(); - values[kPhi] = v12.Phi() > 0 ? v12.Phi() : v12.Phi() + 2. * M_PI; + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); values[kRap] = -v12.Rapidity(); + // Per-track quantities so ME histograms can use kPt1/kPt2/kEta1/kEta2/kPhi1/kPhi2 just like SE FillPair does. + values[kPt1] = t1.pt(); + values[kEta1] = t1.eta(); + values[kPhi1] = t1.phi(); + values[kPt2] = t2.pt(); + values[kEta2] = t2.eta(); + values[kPhi2] = t2.phi(); + if (fgUsedVars[kDeltaPhiPair2]) { - double phipair2ME = v1.Phi() - v2.Phi(); - if (phipair2ME > 3 * TMath::Pi() / 2) { - values[kDeltaPhiPair2] = phipair2ME - 2 * TMath::Pi(); - } else if (phipair2ME < -TMath::Pi() / 2) { - values[kDeltaPhiPair2] = phipair2ME + 2 * TMath::Pi(); - } else { - values[kDeltaPhiPair2] = phipair2ME; - } + values[kDeltaPhiPair2] = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PIHalf); } if (fgUsedVars[kDeltaEtaPair2]) { @@ -3939,12 +4472,14 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -3958,26 +4493,18 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_HE{(v12.Vect()).Unit()}; ROOT::Math::XYZVectorF yaxis_HE{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_HE{(yaxis_HE.Cross(zaxis_HE)).Unit()}; - if (fgUsedVars[kCosThetaHE]) + if (fgUsedVars[kCosThetaHE]) { values[kCosThetaHE] = zaxis_HE.Dot(v_CM); + } if (fgUsedVars[kPhiHE]) { - values[kPhiHE] = TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM)); - if (values[kPhiHE] < 0) { - values[kPhiHE] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiHE] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM))); } if (fgUsedVars[kPhiTildeHE]) { if (fgUsedVars[kCosThetaHE] && fgUsedVars[kPhiHE]) { if (values[kCosThetaHE] > 0) { - values[kPhiTildeHE] = values[kPhiHE] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildeHE] < 0) { - values[kPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeHE] = RecoDecay::constrainAngle(values[kPhiHE] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildeHE] = values[kPhiHE] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildeHE] < 0) { - values[kPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeHE] = RecoDecay::constrainAngle(values[kPhiHE] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildeHE] = -999; // not computable @@ -3989,26 +4516,18 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_CS{(Beam1_CM - Beam2_CM).Unit()}; ROOT::Math::XYZVectorF yaxis_CS{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_CS{(yaxis_CS.Cross(zaxis_CS)).Unit()}; - if (fgUsedVars[kCosThetaCS]) + if (fgUsedVars[kCosThetaCS]) { values[kCosThetaCS] = zaxis_CS.Dot(v_CM); + } if (fgUsedVars[kPhiCS]) { - values[kPhiCS] = TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM)); - if (values[kPhiCS] < 0) { - values[kPhiCS] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiCS] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM))); } if (fgUsedVars[kPhiTildeCS]) { if (fgUsedVars[kCosThetaCS] && fgUsedVars[kPhiCS]) { if (values[kCosThetaCS] > 0) { - values[kPhiTildeCS] = values[kPhiCS] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildeCS] < 0) { - values[kPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeCS] = RecoDecay::constrainAngle(values[kPhiCS] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildeCS] = values[kPhiCS] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildeCS] < 0) { - values[kPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeCS] = RecoDecay::constrainAngle(values[kPhiCS] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildeCS] = -999; // not computable @@ -4016,6 +4535,70 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -4024,23 +4607,14 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) values[kCosThetaPP] = zaxis_PP.Dot(v_CM) / std::sqrt(zaxis_PP.Mag2()); } if (fgUsedVars[kPhiPP]) { - values[kPhiPP] = TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM)); - if (values[kPhiPP] < 0) { - values[kPhiPP] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiPP] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM))); } if (fgUsedVars[kPhiTildePP]) { if (fgUsedVars[kCosThetaPP] && fgUsedVars[kPhiPP]) { if (values[kCosThetaPP] > 0) { - values[kPhiTildePP] = values[kPhiPP] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildePP] < 0) { - values[kPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildePP] = RecoDecay::constrainAngle(values[kPhiPP] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildePP] = values[kPhiPP] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildePP] < 0) { - values[kPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildePP] = RecoDecay::constrainAngle(values[kPhiPP] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildePP] = -999; // not computable @@ -4050,10 +4624,11 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) if (useRM) { double randomCostheta = gRandom->Uniform(-1., 1.); - double randomPhi = gRandom->Uniform(0., 2. * TMath::Pi()); + double randomPhi = gRandom->Uniform(0., o2::constants::math::TwoPI); ROOT::Math::XYZVectorF zaxis_RM(randomCostheta, std::sqrt(1 - randomCostheta * randomCostheta) * std::cos(randomPhi), std::sqrt(1 - randomCostheta * randomCostheta) * std::sin(randomPhi)); - if (fgUsedVars[kCosThetaRM]) + if (fgUsedVars[kCosThetaRM]) { values[kCosThetaRM] = zaxis_RM.Dot(v_CM); + } } } @@ -4062,6 +4637,10 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) // Compute the scalar product UQ for two muon from different event using Q-vector from A, for second and third harmonic float Psi2A1 = getEventPlane(2, values[kQ2X0A1], values[kQ2Y0A1]); float Psi2A2 = getEventPlane(2, values[kQ2X0A2], values[kQ2Y0A2]); + float Psi2B1 = getEventPlane(2, values[kQ2X0B1], values[kQ2Y0B1]); + float Psi2B2 = getEventPlane(2, values[kQ2X0B2], values[kQ2Y0B2]); + float Psi2C1 = getEventPlane(2, values[kQ2X0C1], values[kQ2Y0C1]); + float Psi2C2 = getEventPlane(2, values[kQ2X0C2], values[kQ2Y0C2]); values[kCos2DeltaPhi] = TMath::Cos(2 * (v12.Phi() - Psi2A1)); // WARNING: using the first event EP values[kCos2DeltaPhiEv1] = TMath::Cos(2 * (v1.Phi() - Psi2A1)); values[kCos2DeltaPhiEv2] = TMath::Cos(2 * (v2.Phi() - Psi2A2)); @@ -4092,6 +4671,40 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) values[kV24ME] = (std::isnan(V22ME) || std::isinf(V22ME) || std::isnan(V24ME) || std::isinf(V24ME)) ? 0. : V24ME; values[kWV24ME] = (std::isnan(V22ME) || std::isinf(V22ME) || std::isnan(V24ME) || std::isinf(V24ME)) ? 0. : 1.0; + // coherent Jpsi A2 + bool useCoherentJpsiA2 = fgUsedVars[kA2EPME_TPC] || fgUsedVars[kA2EPME_FT0A] || fgUsedVars[kA2EPME_FT0C]; + if (useCoherentJpsiA2) { + ROOT::Math::Boost boostv12{v12.BoostToCM()}; + ROOT::Math::PtEtaPhiMVector v_daughter = boostv12(t1.sign() > 0 ? v1 : v2); + float Psi2A = t1.sign() > 0 ? Psi2A1 : Psi2A2; + float Psi2B = t1.sign() > 0 ? Psi2B1 : Psi2B2; + float Psi2C = t1.sign() > 0 ? Psi2C1 : Psi2C2; + float R2EP = t1.sign() > 0 ? values[kTwoR2EP1] : values[kTwoR2EP2]; + + // reaction plane + float phi = RecoDecay::constrainAngle(v_daughter.Phi(), -o2::constants::math::PI); + values[kDeltaPhiME_TPC] = phi > Psi2A ? phi - Psi2A : Psi2A - phi; + values[kDeltaPhiME_TPC] = values[kDeltaPhiME_TPC] > TMath::Pi() ? 2. * TMath::Pi() - values[kDeltaPhiME_TPC] : values[kDeltaPhiME_TPC]; + values[kDeltaPhiME_FT0A] = phi > Psi2B ? phi - Psi2B : Psi2B - phi; + values[kDeltaPhiME_FT0A] = values[kDeltaPhiME_FT0A] > TMath::Pi() ? 2. * TMath::Pi() - values[kDeltaPhiME_FT0A] : values[kDeltaPhiME_FT0A]; + values[kDeltaPhiME_FT0C] = phi > Psi2C ? phi - Psi2C : Psi2C - phi; + values[kDeltaPhiME_FT0C] = values[kDeltaPhiME_FT0C] > TMath::Pi() ? 2. * TMath::Pi() - values[kDeltaPhiME_FT0C] : values[kDeltaPhiME_FT0C]; + // fold delta phMEi to [0, pi/2] + values[kDeltaPhiME_TPC] = values[kDeltaPhiME_TPC] > TMath::Pi() / 2. ? TMath::Pi() - values[kDeltaPhiME_TPC] : values[kDeltaPhiME_TPC]; + values[kDeltaPhiME_FT0A] = values[kDeltaPhiME_FT0A] > TMath::Pi() / 2. ? TMath::Pi() - values[kDeltaPhiME_FT0A] : values[kDeltaPhiME_FT0A]; + values[kDeltaPhiME_FT0C] = values[kDeltaPhiME_FT0C] > TMath::Pi() / 2. ? TMath::Pi() - values[kDeltaPhiME_FT0C] : values[kDeltaPhiME_FT0C]; + values[kCos2DeltaPhiME_TPC] = TMath::Cos(2. * (phi - Psi2A)); + values[kCos2DeltaPhiME_FT0A] = TMath::Cos(2. * (phi - Psi2B)); + values[kCos2DeltaPhiME_FT0C] = TMath::Cos(2. * (phi - Psi2C)); + + float A2_TPC = values[kCos2DeltaPhiME_TPC] / R2EP; + float A2_FT0A = values[kCos2DeltaPhiME_FT0A] / R2EP; + float A2_FT0C = values[kCos2DeltaPhiME_FT0C] / R2EP; + values[kA2EPME_TPC] = std::isnan(A2_TPC) || std::isinf(A2_TPC) ? -999. : A2_TPC; + values[kA2EPME_FT0A] = std::isnan(A2_FT0A) || std::isinf(A2_FT0A) ? -999. : A2_FT0A; + values[kA2EPME_FT0C] = std::isnan(A2_FT0C) || std::isinf(A2_FT0C) ? -999. : A2_FT0C; + } + if constexpr ((fillMap & ReducedEventQvectorExtra) > 0) { std::complex Q21(values[kQ2X0A] * values[kS11A], values[kQ2Y0A] * values[kS11A]); std::complex Q42(values[kQ42XA], values[kQ42YA]); @@ -4127,11 +4740,126 @@ void VarManager::FillPairME(T1 const& t1, T2 const& t2, float* values) } } +template +void VarManager::FillPairRotation_ME(T const& t1, T const& t2, int rotation, float* values) +{ + if (!values) { + values = fgValues; + } + + float m1 = o2::constants::physics::MassElectron; + double rotationphi2 = t2.phi; + + if (rotation == 1) { + rotationphi2 = t2.phi + o2::constants::math::PI; + } else if (rotation == 2) { + rotationphi2 = 2 * values[kPsi2A] - t2.phi; + } else if (rotation == 3) { + rotationphi2 = 2 * values[kPsi2A] - t2.phi + o2::constants::math::PI; + } + + if (rotationphi2 >= o2::constants::math::TwoPI) { + rotationphi2 -= o2::constants::math::TwoPI; + } else if (rotationphi2 < 0) { + rotationphi2 += o2::constants::math::TwoPI; + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt, t1.eta, t1.phi, m1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt, t2.eta, rotationphi2, m1); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + // values[kPhi] = v12.Phi(); + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); + values[kRap] = -v12.Rapidity(); + double Ptot1 = TMath::Sqrt(v1.Px() * v1.Px() + v1.Py() * v1.Py() + v1.Pz() * v1.Pz()); + double Ptot2 = TMath::Sqrt(v2.Px() * v2.Px() + v2.Py() * v2.Py() + v2.Pz() * v2.Pz()); + values[kDeltaPtotTracks] = Ptot1 - Ptot2; + + values[kPt1] = t1.pt; + values[kEta1] = t1.eta; + values[kPhi1] = t1.phi; + values[kPt2] = t2.pt; + values[kEta2] = t2.eta; + values[kPhi2] = rotationphi2; +} + +template +void VarManager::FillPairMEAcrossTFs(T const& t1, T const& t2, float* values) +{ + if (!values) { + values = static_cast(fgValues); + } + + float m1 = o2::constants::physics::MassElectron; + ROOT::Math::PtEtaPhiMVector v1(t1.pt, t1.eta, t1.phi, m1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt, t2.eta, t2.phi, m1); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); + values[kRap] = -v12.Rapidity(); + + if (fgUsedVars[kCosThetaStarRandom] || fgUsedVars[kCosThetaStarFT0C]) { + ROOT::Math::Boost boostv12{v12.BoostToCM()}; + ROOT::Math::XYZVectorF v_CM{(boostv12(v1).Vect()).Unit()}; + + if (fgUsedVars[kCosThetaStarRandom]) { + // Randomize the event plane angle to check the unpolarized contribution + ROOT::Math::XYZVector zaxisRandom = ROOT::Math::XYZVector(TMath::Cos(values[kRandomPsi2]), TMath::Sin(values[kRandomPsi2]), 0).Unit(); + values[kCosThetaStarRandom] = v_CM.Dot(zaxisRandom); + values[kCos2ThetaStarRandom] = values[kCosThetaStarRandom] * values[kCosThetaStarRandom]; + } + + if (fgUsedVars[kCosThetaStarFT0C]) { + // event plane angle from FT0C + ROOT::Math::XYZVector zaxisFT0C = ROOT::Math::XYZVector(TMath::Cos(values[kPsi2C]), TMath::Sin(values[kPsi2C]), 0).Unit(); + values[kCosThetaStarFT0C] = v_CM.Dot(zaxisFT0C); + values[kAbsCosThetaStarFT0C] = std::abs(values[kCosThetaStarFT0C]); + values[kCos2ThetaStarFT0C] = values[kCosThetaStarFT0C] * values[kCosThetaStarFT0C]; + } + } + + bool useCoherentJpsiA2 = fgUsedVars[kA2EP_TPC] || fgUsedVars[kA2EP_FT0A] || fgUsedVars[kA2EP_FT0C]; + if (useCoherentJpsiA2) { + ROOT::Math::Boost boostv12{v12.BoostToCM()}; + ROOT::Math::PtEtaPhiMVector v_daughter = boostv12(v1); + float Psi2A = values[kPsi2A]; + float Psi2B = values[kPsi2B]; + float Psi2C = values[kPsi2C]; + + // reaction plane + float phi = RecoDecay::constrainAngle(v_daughter.Phi(), -o2::constants::math::PI); + values[kDeltaPhi_TPC] = phi > Psi2A ? phi - Psi2A : Psi2A - phi; + values[kDeltaPhi_TPC] = values[kDeltaPhi_TPC] > TMath::Pi() ? 2. * TMath::Pi() - values[kDeltaPhi_TPC] : values[kDeltaPhi_TPC]; + values[kDeltaPhi_FT0A] = phi > Psi2B ? phi - Psi2B : Psi2B - phi; + values[kDeltaPhi_FT0A] = values[kDeltaPhi_FT0A] > TMath::Pi() ? 2. * TMath::Pi() - values[kDeltaPhi_FT0A] : values[kDeltaPhi_FT0A]; + values[kDeltaPhi_FT0C] = phi > Psi2C ? phi - Psi2C : Psi2C - phi; + values[kDeltaPhi_FT0C] = values[kDeltaPhi_FT0C] > TMath::Pi() ? 2. * TMath::Pi() - values[kDeltaPhi_FT0C] : values[kDeltaPhi_FT0C]; + // fold delta phi to [0, pi/2] + values[kDeltaPhi_TPC] = values[kDeltaPhi_TPC] > TMath::Pi() / 2. ? TMath::Pi() - values[kDeltaPhi_TPC] : values[kDeltaPhi_TPC]; + values[kDeltaPhi_FT0A] = values[kDeltaPhi_FT0A] > TMath::Pi() / 2. ? TMath::Pi() - values[kDeltaPhi_FT0A] : values[kDeltaPhi_FT0A]; + values[kDeltaPhi_FT0C] = values[kDeltaPhi_FT0C] > TMath::Pi() / 2. ? TMath::Pi() - values[kDeltaPhi_FT0C] : values[kDeltaPhi_FT0C]; + values[kCos2DeltaPhi_TPC] = TMath::Cos(2. * (phi - Psi2A)); + values[kCos2DeltaPhi_FT0A] = TMath::Cos(2. * (phi - Psi2B)); + values[kCos2DeltaPhi_FT0C] = TMath::Cos(2. * (phi - Psi2C)); + + float A2RP_TPC = values[kCos2DeltaPhi_TPC] / values[kR2EP]; + float A2RP_FT0A = values[kCos2DeltaPhi_FT0A] / values[kR2EP]; + float A2RP_FT0C = values[kCos2DeltaPhi_FT0C] / values[kR2EP]; + values[kA2EP_TPC] = std::isnan(A2RP_TPC) || std::isinf(A2RP_TPC) ? -999. : A2RP_TPC; + values[kA2EP_FT0A] = std::isnan(A2RP_FT0A) || std::isinf(A2RP_FT0A) ? -999. : A2RP_FT0A; + values[kA2EP_FT0C] = std::isnan(A2RP_FT0C) || std::isinf(A2RP_FT0C) ? -999. : A2RP_FT0C; + } +} + template void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -4168,14 +4896,18 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) values[kMCPt2] = t2.pt(); values[kMCEta1] = t1.eta(); values[kMCEta2] = t2.eta(); + values[kMCP1] = t1.p(); + values[kMCP2] = t2.p(); // polarization parameters - bool useHE = fgUsedVars[kMCCosThetaHE] || fgUsedVars[kMCPhiHE]; // helicity frame - bool useCS = fgUsedVars[kMCCosThetaCS] || fgUsedVars[kMCPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kMCCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kMCCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kMCCosThetaHE] || fgUsedVars[kMCPhiHE]; // helicity frame + bool useCS = fgUsedVars[kMCCosThetaCS] || fgUsedVars[kMCPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kMCCosThetaGJB1] || fgUsedVars[kMCPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kMCCosThetaGJB2] || fgUsedVars[kMCPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kMCCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kMCCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -4189,26 +4921,18 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_HE{(v12.Vect()).Unit()}; ROOT::Math::XYZVectorF yaxis_HE{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_HE{(yaxis_HE.Cross(zaxis_HE)).Unit()}; - if (fgUsedVars[kMCCosThetaHE]) + if (fgUsedVars[kMCCosThetaHE]) { values[kMCCosThetaHE] = zaxis_HE.Dot(v_CM); + } if (fgUsedVars[kMCPhiHE]) { - values[kMCPhiHE] = TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM)); - if (values[kMCPhiHE] < 0) { - values[kMCPhiHE] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kMCPhiHE] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM))); } if (fgUsedVars[kMCPhiTildeHE]) { if (fgUsedVars[kMCCosThetaHE] && fgUsedVars[kMCPhiHE]) { if (values[kMCCosThetaHE] > 0) { - values[kMCPhiTildeHE] = values[kMCPhiHE] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kMCPhiTildeHE] < 0) { - values[kMCPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kMCPhiTildeHE] = RecoDecay::constrainAngle(values[kMCPhiHE] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kMCPhiTildeHE] = values[kMCPhiHE] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kMCPhiTildeHE] < 0) { - values[kMCPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kMCPhiTildeHE] = RecoDecay::constrainAngle(values[kMCPhiHE] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kMCPhiTildeHE] = -999; // not computable @@ -4220,26 +4944,18 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_CS{(Beam1_CM - Beam2_CM).Unit()}; ROOT::Math::XYZVectorF yaxis_CS{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_CS{(yaxis_CS.Cross(zaxis_CS)).Unit()}; - if (fgUsedVars[kMCCosThetaCS]) + if (fgUsedVars[kMCCosThetaCS]) { values[kMCCosThetaCS] = zaxis_CS.Dot(v_CM); + } if (fgUsedVars[kMCPhiCS]) { - values[kMCPhiCS] = TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM)); - if (values[kMCPhiCS] < 0) { - values[kMCPhiCS] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kMCPhiCS] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM))); } if (fgUsedVars[kMCPhiTildeCS]) { if (fgUsedVars[kMCCosThetaCS] && fgUsedVars[kMCPhiCS]) { if (values[kMCCosThetaCS] > 0) { - values[kMCPhiTildeCS] = values[kMCPhiCS] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kMCPhiTildeCS] < 0) { - values[kMCPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kMCPhiTildeCS] = RecoDecay::constrainAngle(values[kMCPhiCS] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kMCPhiTildeCS] = values[kMCPhiCS] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kMCPhiTildeCS] < 0) { - values[kMCPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kMCPhiTildeCS] = RecoDecay::constrainAngle(values[kMCPhiCS] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kMCPhiTildeCS] = -999; // not computable @@ -4247,6 +4963,70 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kMCCosThetaGJB1]) { + values[kMCCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kMCPhiGJB1]) { + values[kMCPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kMCPhiTildeGJB1]) { + if (fgUsedVars[kMCCosThetaGJB1] && fgUsedVars[kMCPhiGJB1]) { + if (values[kMCCosThetaGJB1] > 0) { + values[kMCPhiTildeGJB1] = RecoDecay::constrainAngle(values[kMCPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kMCPhiTildeGJB1] = RecoDecay::constrainAngle(values[kMCPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kMCPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kMCCosThetaGJB2]) { + values[kMCCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kMCPhiGJB2]) { + values[kMCPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kMCPhiTildeGJB2]) { + if (fgUsedVars[kMCCosThetaGJB2] && fgUsedVars[kMCPhiGJB2]) { + if (values[kMCCosThetaGJB2] > 0) { + values[kMCPhiTildeGJB2] = RecoDecay::constrainAngle(values[kMCPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kMCPhiTildeGJB2] = RecoDecay::constrainAngle(values[kMCPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kMCPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{v12.Vect().Unit()}; @@ -4255,23 +5035,14 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) values[kMCCosThetaPP] = zaxis_PP.Dot(v_CM); } if (fgUsedVars[kMCPhiPP]) { - values[kMCPhiPP] = TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM)); - if (values[kMCPhiPP] < 0) { - values[kMCPhiPP] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kMCPhiPP] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM))); } if (fgUsedVars[kMCPhiTildePP]) { if (fgUsedVars[kMCCosThetaPP] && fgUsedVars[kMCPhiPP]) { if (values[kMCCosThetaPP] > 0) { - values[kMCPhiTildePP] = values[kMCPhiPP] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kMCPhiTildePP] < 0) { - values[kMCPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kMCPhiTildePP] = RecoDecay::constrainAngle(values[kMCPhiPP] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kMCPhiTildePP] = values[kMCPhiPP] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kMCPhiTildePP] < 0) { - values[kMCPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kMCPhiTildePP] = RecoDecay::constrainAngle(values[kMCPhiPP] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kMCPhiTildePP] = -999; // not computable @@ -4281,19 +5052,55 @@ void VarManager::FillPairMC(T1 const& t1, T2 const& t2, float* values) if (useRM) { double randomCostheta = gRandom->Uniform(-1., 1.); - double randomPhi = gRandom->Uniform(0., 2. * TMath::Pi()); + double randomPhi = gRandom->Uniform(0., o2::constants::math::TwoPI); ROOT::Math::XYZVectorF zaxis_RM(randomCostheta, std::sqrt(1 - randomCostheta * randomCostheta) * std::cos(randomPhi), std::sqrt(1 - randomCostheta * randomCostheta) * std::sin(randomPhi)); - if (fgUsedVars[kMCCosThetaRM]) + if (fgUsedVars[kMCCosThetaRM]) { values[kMCCosThetaRM] = zaxis_RM.Dot(v_CM); + } } } + + if (fgUsedVars[kCosThetaStarRandom] || fgUsedVars[kMCCosThetaStar]) { + ROOT::Math::Boost boostv12{v12.BoostToCM()}; + ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; + ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; + + // using positive sign convention for the first track + ROOT::Math::XYZVectorF v_CM = (t1.pdgCode() > 0 ? v1_CM : v2_CM); + + // Randomize the event plane angle to check the unpolarized contribution + ROOT::Math::XYZVector zaxisRandom = ROOT::Math::XYZVector(TMath::Cos(values[kRandomPsi2]), TMath::Sin(values[kRandomPsi2]), 0).Unit(); + values[kCosThetaStarRandom] = v_CM.Dot(zaxisRandom); + values[kCos2ThetaStarRandom] = values[kCosThetaStarRandom] * values[kCosThetaStarRandom]; + + // truth event plane angle + ROOT::Math::XYZVector zaxisTrue = ROOT::Math::XYZVector(TMath::Cos(values[kMCEventPlaneAngle]), TMath::Sin(values[kMCEventPlaneAngle]), 0).Unit(); + values[kMCCosThetaStar] = v_CM.Dot(zaxisTrue); + } + + if (fgUsedVars[kCos2DeltaPhi_Random] || fgUsedVars[kCos2DeltaPhiPP_Random] || fgUsedVars[kCos2DeltaPhi_MC] || fgUsedVars[kCos2DeltaPhiPP_MC]) { + ROOT::Math::Boost boostv12{v12.BoostToCM()}; + ROOT::Math::PtEtaPhiMVector v_daughter = boostv12(t1.pdgCode() > 0 ? v1 : v2); + + // reaction plane + float phi = RecoDecay::constrainAngle(v_daughter.Phi(), -o2::constants::math::PI); + values[kDeltaPhi_Random] = phi - values[kRandomPsi2]; + values[kDeltaPhi_Random] = values[kDeltaPhi_Random] > o2::constants::math::PI ? o2::constants::math::TwoPI - values[kDeltaPhi_Random] : values[kDeltaPhi_Random]; + values[kDeltaPhi_MC] = phi - values[kMCEventPlaneAngle]; + values[kDeltaPhi_MC] = values[kDeltaPhi_MC] > o2::constants::math::PI ? o2::constants::math::TwoPI - values[kDeltaPhi_MC] : values[kDeltaPhi_MC]; + // fold delta phi into [-pi/2, pi/2] + values[kDeltaPhi_Random] = values[kDeltaPhi_Random] > o2::constants::math::PIHalf ? o2::constants::math::PI - values[kDeltaPhi_Random] : values[kDeltaPhi_Random]; + values[kDeltaPhi_MC] = values[kDeltaPhi_MC] > o2::constants::math::PIHalf ? o2::constants::math::PI - values[kDeltaPhi_MC] : values[kDeltaPhi_MC]; + values[kCos2DeltaPhi_Random] = TMath::Cos(2. * (phi - values[kRandomPsi2])); + values[kCos2DeltaPhi_MC] = TMath::Cos(2. * (phi - values[kMCEventPlaneAngle])); + } } template void VarManager::FillTripleMC(T1 const& t1, T2 const& t2, T3 const& t3, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr (candidateType == kTripleCandidateToEEPhoton) { @@ -4375,7 +5182,7 @@ void VarManager::FillTripleMC(T1 const& t1, T2 const& t2, T3 const& t3, float* v } template -void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, bool propToSV, float* values) +void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, float* values) { // check at compile time that the event and cov matrix have the cov matrix constexpr bool eventHasVtxCov = ((collFillMap & Collision) > 0 || (collFillMap & ReducedEventVtxCov) > 0); @@ -4383,7 +5190,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, constexpr bool muonHasCov = ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; float m2 = o2::constants::physics::MassElectron; @@ -4391,7 +5198,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, m1 = o2::constants::physics::MassKaonCharged; m2 = o2::constants::physics::MassPionCharged; } - if constexpr (pairType == kDecayToMuMu && muonHasCov) { + if constexpr (pairType == kDecayToMuMu) { m1 = o2::constants::physics::MassMuon; m2 = o2::constants::physics::MassMuon; } @@ -4399,7 +5206,7 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), m2); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - values[kUsedKF] = fgUsedKF; + values[kUsedKF] = static_cast(fgUsedKF); if (!fgUsedKF) { int procCode = 0; @@ -4438,6 +5245,9 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kVertexingLxyErr] = -999.; values[kVertexingLxyzErr] = -999.; values[kVertexingLzErr] = -999.; + values[kVertexingLxyOverErr] = -999.; + values[kVertexingLzOverErr] = -999.; + values[kVertexingLxyzOverErr] = -999.; values[kVertexingTauxy] = -999.; values[kVertexingTauz] = -999.; @@ -4456,64 +5266,48 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, std::array covMatrixPCA{}; // get track impact parameters // This modifies track momenta! - o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); - std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; - o2::dataformats::VertexBase primaryVertex = {std::move(vtxXYZ), std::move(vtxCov)}; // auto primaryVertex = getPrimaryVertex(collision); - auto covMatrixPV = primaryVertex.getCov(); + std::array covMatrixPV{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; if constexpr ((pairType == kDecayToEE || pairType == kDecayToKPi) && trackHasCov) { secondaryVertex = fgFitterTwoProngBarrel.getPCACandidate(); // printf("secVtx (first) %f %f %f \n",secondaryVertex[0],secondaryVertex[1],secondaryVertex[2]); covMatrixPCA = fgFitterTwoProngBarrel.calcPCACovMatrixFlat(); - auto chi2PCA = fgFitterTwoProngBarrel.getChi2AtPCACandidate(); - auto trackParVar0 = fgFitterTwoProngBarrel.getTrack(0); - auto trackParVar1 = fgFitterTwoProngBarrel.getTrack(1); - values[kVertexingChi2PCA] = chi2PCA; - v1 = {trackParVar0.getPt(), trackParVar0.getEta(), trackParVar0.getPhi(), m1}; - v2 = {trackParVar1.getPt(), trackParVar1.getEta(), trackParVar1.getPhi(), m2}; + values[kVertexingChi2PCA] = fgFitterTwoProngBarrel.getChi2AtPCACandidate(); + v1 = {fgFitterTwoProngBarrel.getTrack(0).getPt(), fgFitterTwoProngBarrel.getTrack(0).getEta(), fgFitterTwoProngBarrel.getTrack(0).getPhi(), m1}; + v2 = {fgFitterTwoProngBarrel.getTrack(1).getPt(), fgFitterTwoProngBarrel.getTrack(1).getEta(), fgFitterTwoProngBarrel.getTrack(1).getPhi(), m2}; v12 = v1 + v2; - if (fgPVrecalKF) + if (fgPVrecalKF) { primaryVertexNew = RecalculatePrimaryVertex(t1, t2, collision); + } } else if constexpr (pairType == kDecayToMuMu && muonHasCov) { // Get pca candidate from forward DCA fitter secondaryVertex = fgFitterTwoProngFwd.getPCACandidate(); covMatrixPCA = fgFitterTwoProngFwd.calcPCACovMatrixFlat(); - auto chi2PCA = fgFitterTwoProngFwd.getChi2AtPCACandidate(); - auto trackParVar0 = fgFitterTwoProngFwd.getTrack(0); - auto trackParVar1 = fgFitterTwoProngFwd.getTrack(1); - values[kVertexingChi2PCA] = chi2PCA; - v1 = {trackParVar0.getPt(), trackParVar0.getEta(), trackParVar0.getPhi(), m1}; - v2 = {trackParVar1.getPt(), trackParVar1.getEta(), trackParVar1.getPhi(), m2}; + values[kVertexingChi2PCA] = fgFitterTwoProngFwd.getChi2AtPCACandidate(); + v1 = {fgFitterTwoProngFwd.getTrack(0).getPt(), fgFitterTwoProngFwd.getTrack(0).getEta(), fgFitterTwoProngFwd.getTrack(0).getPhi(), m1}; + v2 = {fgFitterTwoProngFwd.getTrack(1).getPt(), fgFitterTwoProngFwd.getTrack(1).getEta(), fgFitterTwoProngFwd.getTrack(1).getPhi(), m2}; v12 = v1 + v2; - - values[kPt1] = trackParVar0.getPt(); - values[kEta1] = trackParVar0.getEta(); - values[kPhi1] = trackParVar0.getPhi(); - - values[kPt2] = trackParVar1.getPt(); - values[kEta2] = trackParVar1.getEta(); - values[kPhi2] = trackParVar1.getPhi(); } - double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); - double theta = std::atan2(secondaryVertex[2] - collision.posZ(), - std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + - (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); + const double dx = secondaryVertex[0] - collision.posX(); + const double dy = secondaryVertex[1] - collision.posY(); + const double dz = secondaryVertex[2] - collision.posZ(); + double phi = std::atan2(dy, dx); + double theta = std::atan2(dz, std::sqrt(dx * dx + dy * dy)); values[kVertexingLxyzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); - values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + - (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); - values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); + values[kVertexingLxy] = dx * dx + dy * dy; + values[kVertexingLz] = dz * dz; values[kVertexingLxyz] = values[kVertexingLxy] + values[kVertexingLz]; values[kVertexingLxy] = std::sqrt(values[kVertexingLxy]); values[kVertexingLz] = std::sqrt(values[kVertexingLz]); values[kVertexingLxyz] = std::sqrt(values[kVertexingLxyz]); - values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauz] = -dz * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); values[kVertexingTauxy] = values[kVertexingLxy] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); values[kVertexingPz] = TMath::Abs(v12.Pz()); @@ -4522,68 +5316,50 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kVertexingTauzErr] = values[kVertexingLzErr] * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); - values[kCosPointingAngle] = ((collision.posX() - secondaryVertex[0]) * v12.Px() + - (collision.posY() - secondaryVertex[1]) * v12.Py() + - (collision.posZ() - secondaryVertex[2]) * v12.Pz()) / + values[kCosPointingAngle] = (dx * v12.Px() + dy * v12.Py() + dz * v12.Pz()) / (v12.P() * values[VarManager::kVertexingLxyz]); // Decay length defined as in Run 2 - values[kVertexingLzProjected] = ((secondaryVertex[2] - collision.posZ()) * v12.Pz()) / TMath::Sqrt(v12.Pz() * v12.Pz()); - values[kVertexingLxyProjected] = ((secondaryVertex[0] - collision.posX()) * v12.Px()) + ((secondaryVertex[1] - collision.posY()) * v12.Py()); + values[kVertexingLzProjected] = (dz * v12.Pz()) / TMath::Sqrt(v12.Pz() * v12.Pz()); + values[kVertexingLxyProjected] = (dx * v12.Px()) + (dy * v12.Py()); values[kVertexingLxyProjected] = values[kVertexingLxyProjected] / TMath::Sqrt((v12.Px() * v12.Px()) + (v12.Py() * v12.Py())); - values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v12.Px()) + ((secondaryVertex[1] - collision.posY()) * v12.Py()) + ((secondaryVertex[2] - collision.posZ()) * v12.Pz()); + values[kVertexingLxyzProjected] = (dx * v12.Px()) + (dy * v12.Py()) + (dz * v12.Pz()); values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v12.Px() * v12.Px()) + (v12.Py() * v12.Py()) + (v12.Pz() * v12.Pz())); - if (fgPVrecalKF) { - values[kVertexingLxyProjectedRecalculatePV] = (secondaryVertex[0] - primaryVertexNew.getX()) * v12.Px() + (secondaryVertex[1] - primaryVertexNew.getY()) * v12.Py(); - values[kVertexingLxyProjectedRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] / v12.Pt(); - } values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * v12.M() / (v12.Pt()); values[kVertexingTauxyProjectedPoleJPsiMass] = values[kVertexingLxyProjected] * o2::constants::physics::MassJPsi / (v12.Pt()); values[kVertexingTauxyProjectedNs] = values[kVertexingTauxyProjected] / o2::constants::physics::LightSpeedCm2NS; - if (fgPVrecalKF) + if (fgPVrecalKF) { + values[kVertexingLxyProjectedRecalculatePV] = (secondaryVertex[0] - primaryVertexNew.getX()) * v12.Px() + (secondaryVertex[1] - primaryVertexNew.getY()) * v12.Py(); + values[kVertexingLxyProjectedRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] / v12.Pt(); values[kVertexingTauxyProjectedPoleJPsiMassRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] * o2::constants::physics::MassJPsi / (v12.Pt()); + } values[kVertexingTauzProjected] = values[kVertexingLzProjected] * v12.M() / TMath::Abs(v12.Pz()); values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * v12.M() / (v12.P()); } - } else { + } else if constexpr (((pairType == kDecayToEE || pairType == kDecayToKPi) && trackHasCov) || ((pairType == kDecayToMuMu) && muonHasCov)) { KFParticle trk0KF; KFParticle trk1KF; KFParticle KFGeoTwoProng; - if constexpr ((pairType == kDecayToEE) && trackHasCov) { - KFPTrack kfpTrack0 = createKFPTrackFromTrack(t1); - trk0KF = KFParticle(kfpTrack0, -11 * t1.sign()); - KFPTrack kfpTrack1 = createKFPTrackFromTrack(t2); - trk1KF = KFParticle(kfpTrack1, -11 * t2.sign()); - - KFGeoTwoProng.SetConstructMethod(2); - KFGeoTwoProng.AddDaughter(trk0KF); - KFGeoTwoProng.AddDaughter(trk1KF); - - } else if constexpr ((pairType == kDecayToMuMu) && muonHasCov) { - KFPTrack kfpTrack0 = createKFPFwdTrackFromFwdTrack(t1); - trk0KF = KFParticle(kfpTrack0, -13 * t1.sign()); - KFPTrack kfpTrack1 = createKFPFwdTrackFromFwdTrack(t2); - trk1KF = KFParticle(kfpTrack1, -13 * t2.sign()); - - KFGeoTwoProng.SetConstructMethod(2); - KFGeoTwoProng.AddDaughter(trk0KF); - KFGeoTwoProng.AddDaughter(trk1KF); + if constexpr (pairType == kDecayToEE) { + trk0KF = KFParticle(createKFPTrackFromTrack(t1), -11 * t1.sign()); + trk1KF = KFParticle(createKFPTrackFromTrack(t2), -11 * t2.sign()); + } else if constexpr (pairType == kDecayToMuMu) { + trk0KF = KFParticle(createKFPFwdTrackFromFwdTrack(t1), -13 * t1.sign()); + trk1KF = KFParticle(createKFPFwdTrackFromFwdTrack(t2), -13 * t2.sign()); + } else if constexpr (pairType == kDecayToKPi) { + trk0KF = KFParticle(createKFPTrackFromTrack(t1), 321 * t1.sign()); + trk1KF = KFParticle(createKFPTrackFromTrack(t2), 211 * t2.sign()); + } + KFGeoTwoProng.SetConstructMethod(2); + KFGeoTwoProng.AddDaughter(trk0KF); + KFGeoTwoProng.AddDaughter(trk1KF); - } else if constexpr ((pairType == kDecayToKPi) && trackHasCov) { - KFPTrack kfpTrack0 = createKFPTrackFromTrack(t1); - trk0KF = KFParticle(kfpTrack0, 321 * t1.sign()); - KFPTrack kfpTrack1 = createKFPTrackFromTrack(t2); - trk1KF = KFParticle(kfpTrack1, 211 * t2.sign()); - - KFGeoTwoProng.SetConstructMethod(2); - KFGeoTwoProng.AddDaughter(trk0KF); - KFGeoTwoProng.AddDaughter(trk1KF); - } if (fgUsedVars[kKFMass]) { float mass = 0., massErr = 0.; - if (!KFGeoTwoProng.GetMass(mass, massErr)) + if (!KFGeoTwoProng.GetMass(mass, massErr)) { values[kKFMass] = mass; - else + } else { values[kKFMass] = -999.; + } } if constexpr (eventHasVtxCov) { @@ -4600,14 +5376,17 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kVertexingLxyErr] = (KFPV.GetCovariance(0) + KFGeoTwoProng.GetCovariance(0)) * dxPair2PV * dxPair2PV + (KFPV.GetCovariance(2) + KFGeoTwoProng.GetCovariance(2)) * dyPair2PV * dyPair2PV + 2 * ((KFPV.GetCovariance(1) + KFGeoTwoProng.GetCovariance(1)) * dxPair2PV * dyPair2PV); values[kVertexingLzErr] = (KFPV.GetCovariance(5) + KFGeoTwoProng.GetCovariance(5)) * dzPair2PV * dzPair2PV; values[kVertexingLxyzErr] = (KFPV.GetCovariance(0) + KFGeoTwoProng.GetCovariance(0)) * dxPair2PV * dxPair2PV + (KFPV.GetCovariance(2) + KFGeoTwoProng.GetCovariance(2)) * dyPair2PV * dyPair2PV + (KFPV.GetCovariance(5) + KFGeoTwoProng.GetCovariance(5)) * dzPair2PV * dzPair2PV + 2 * ((KFPV.GetCovariance(1) + KFGeoTwoProng.GetCovariance(1)) * dxPair2PV * dyPair2PV + (KFPV.GetCovariance(3) + KFGeoTwoProng.GetCovariance(3)) * dxPair2PV * dzPair2PV + (KFPV.GetCovariance(4) + KFGeoTwoProng.GetCovariance(4)) * dyPair2PV * dzPair2PV); - if (fabs(values[kVertexingLxy]) < 1.e-8f) + if (std::fabs(values[kVertexingLxy]) < 1.e-8f) { values[kVertexingLxy] = 1.e-8f; + } values[kVertexingLxyErr] = values[kVertexingLxyErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyErr]) / values[kVertexingLxy]; - if (fabs(values[kVertexingLz]) < 1.e-8f) + if (std::fabs(values[kVertexingLz]) < 1.e-8f) { values[kVertexingLz] = 1.e-8f; + } values[kVertexingLzErr] = values[kVertexingLzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLzErr]) / values[kVertexingLz]; - if (fabs(values[kVertexingLxyz]) < 1.e-8f) + if (std::fabs(values[kVertexingLxyz]) < 1.e-8f) { values[kVertexingLxyz] = 1.e-8f; + } values[kVertexingLxyzErr] = values[kVertexingLxyzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyzErr]) / values[kVertexingLxyz]; values[kVertexingTauxy] = KFGeoTwoProng.GetPseudoProperDecayTime(KFPV, KFGeoTwoProng.GetMass()) / (o2::constants::physics::LightSpeedCm2NS); values[kVertexingTauz] = -1 * dzPair2PV * KFGeoTwoProng.GetMass() / (TMath::Abs(KFGeoTwoProng.GetPz()) * o2::constants::physics::LightSpeedCm2NS); @@ -4639,10 +5418,12 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kVertexingLxyzOverErr] = values[kVertexingLxyz] / values[kVertexingLxyzErr]; } - if (fgUsedVars[kKFChi2OverNDFGeo]) + if (fgUsedVars[kKFChi2OverNDFGeo]) { values[kKFChi2OverNDFGeo] = KFGeoTwoProng.GetChi2() / KFGeoTwoProng.GetNDF(); - if (fgUsedVars[kKFCosPA]) + } + if (fgUsedVars[kKFCosPA]) { values[kKFCosPA] = calculateCosPA(KFGeoTwoProng, KFPV); + } // in principle, they should be in FillTrack if (fgUsedVars[kKFTrack0DCAxyz] || fgUsedVars[kKFTrack1DCAxyz]) { @@ -4653,10 +5434,12 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, values[kKFTrack0DCAxy] = trk0KF.GetDistanceFromVertexXY(KFPV); values[kKFTrack1DCAxy] = trk1KF.GetDistanceFromVertexXY(KFPV); } - if (fgUsedVars[kKFDCAxyzBetweenProngs]) + if (fgUsedVars[kKFDCAxyzBetweenProngs]) { values[kKFDCAxyzBetweenProngs] = trk0KF.GetDistanceFromParticle(trk1KF); - if (fgUsedVars[kKFDCAxyBetweenProngs]) + } + if (fgUsedVars[kKFDCAxyBetweenProngs]) { values[kKFDCAxyBetweenProngs] = trk0KF.GetDistanceFromParticleXY(trk1KF); + } if (fgUsedVars[kKFTracksDCAxyzMax]) { values[kKFTracksDCAxyzMax] = values[kKFTrack0DCAxyz] > values[kKFTrack1DCAxyz] ? values[kKFTrack0DCAxyz] : values[kKFTrack1DCAxyz]; @@ -4687,79 +5470,38 @@ void VarManager::FillPairVertexing(C const& collision, T const& t1, T const& t2, KFGeoTopTwoProngBarrel.SetProductionVertex(KFPV); values[kKFChi2OverNDFGeoTop] = KFGeoTopTwoProngBarrel.GetChi2() / KFGeoTopTwoProngBarrel.GetNDF(); float mass = 0., massErr = 0.; - if (!KFGeoTopTwoProngBarrel.GetMass(mass, massErr)) + if (!KFGeoTopTwoProngBarrel.GetMass(mass, massErr)) { values[kKFMassGeoTop] = mass; - else + } else { values[kKFMassGeoTop] = -999.; - } - if (propToSV) { - if constexpr ((pairType == kDecayToMuMu) && muonHasCov) { - o2::track::TrackParCovFwd pars1 = FwdToTrackPar(t1, t1); - o2::track::TrackParCovFwd pars2 = FwdToTrackPar(t2, t2); - - auto geoMan1 = o2::base::GeometryManager::meanMaterialBudget(t1.x(), t1.y(), t1.z(), KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY(), KFGeoTwoProng.GetZ()); - auto geoMan2 = o2::base::GeometryManager::meanMaterialBudget(t2.x(), t2.y(), t2.z(), KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY(), KFGeoTwoProng.GetZ()); - auto x2x01 = static_cast(geoMan1.meanX2X0); - auto x2x02 = static_cast(geoMan2.meanX2X0); - float B[3]; - float xyz[3] = {0, 0, 0}; - KFGeoTwoProng.GetFieldValue(xyz, B); - // TODO: find better soluton to handle cases where KF outputs negative variances - /*float covXX = 0.1; - float covYY = 0.1; - if (KFGeoTwoProng.GetCovariance(0, 0) > 0) { - covXX = KFGeoTwoProng.GetCovariance(0, 0); - } - if (KFGeoTwoProng.GetCovariance(1, 1) > 0) { - covYY = KFGeoTwoProng.GetCovariance(0, 0); - }*/ - pars1.propagateToVtxhelixWithMCS(KFGeoTwoProng.GetZ(), {KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY()}, {KFGeoTwoProng.GetCovariance(0, 0), KFGeoTwoProng.GetCovariance(1, 1)}, B[2], x2x01); - pars2.propagateToVtxhelixWithMCS(KFGeoTwoProng.GetZ(), {KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY()}, {KFGeoTwoProng.GetCovariance(0, 0), KFGeoTwoProng.GetCovariance(1, 1)}, B[2], x2x02); - v1 = {pars1.getPt(), pars1.getEta(), pars1.getPhi(), m1}; - v2 = {pars2.getPt(), pars2.getEta(), pars2.getPhi(), m2}; - v12 = v1 + v2; - values[kMass] = v12.M(); - values[kPt] = v12.Pt(); - values[kEta] = v12.Eta(); - values[kPhi] = v12.Phi(); - values[kRap] = -v12.Rapidity(); - values[kVertexingTauxy] = KFGeoTwoProng.GetPseudoProperDecayTime(KFPV, v12.M()) / (o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauz] = -1 * dzPair2PV * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauzErr] = values[kVertexingLzErr] * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingPz] = TMath::Abs(v12.Pz()); - values[kVertexingSV] = KFGeoTwoProng.GetZ(); - - values[kPt1] = pars1.getPt(); - values[kEta1] = pars1.getEta(); - values[kPhi1] = pars1.getPhi(); - - values[kPt2] = pars2.getPt(); - values[kEta2] = pars2.getEta(); - values[kPhi2] = pars2.getPhi(); } } - } - } - if (propToSV) { - values[kMass] = v12.M(); - values[kPt] = v12.Pt(); - values[kEta] = v12.Eta(); - // values[kPhi] = v12.Phi(); - values[kPhi] = v12.Phi() > 0 ? v12.Phi() : v12.Phi() + 2. * M_PI; - } else { - values[kPt1] = t1.pt(); - values[kEta1] = t1.eta(); - values[kPhi1] = t1.phi(); - values[kPt2] = t2.pt(); - values[kEta2] = t2.eta(); - values[kPhi2] = t2.phi(); + float sv[3] = {KFGeoTwoProng.GetX(), KFGeoTwoProng.GetY(), KFGeoTwoProng.GetZ()}; + KFParticle trk0AtSV = trk0KF; + KFParticle trk1AtSV = trk1KF; + trk0AtSV.TransportToPoint(sv); + trk1AtSV.TransportToPoint(sv); + + v1 = {trk0AtSV.GetPt(), trk0AtSV.GetEta(), trk0AtSV.GetPhi(), m1}; + v2 = {trk1AtSV.GetPt(), trk1AtSV.GetEta(), trk1AtSV.GetPhi(), m2}; + v12 = v1 + v2; + } } + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); + values[kPt1] = v1.Pt(); + values[kEta1] = v1.Eta(); + values[kPhi1] = v1.Phi(); + values[kPt2] = v2.Pt(); + values[kEta2] = v2.Eta(); + values[kPhi2] = v2.Phi(); } template -void VarManager::FillPairVertexingRecomputePV(C const& /*collision*/, T const& t1, T const& t2, o2::dataformats::VertexBase pvRefitted, float* values) +void VarManager::FillPairVertexingRecomputePV(C const& /*collision*/, T const& t1, T const& t2, const o2::dataformats::VertexBase& pvRefitted, float* values) { // recompute decay lenght variables using updated primary vertex @@ -4769,7 +5511,7 @@ void VarManager::FillPairVertexingRecomputePV(C const& /*collision*/, T const& t constexpr bool muonHasCov = ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -4786,8 +5528,9 @@ void VarManager::FillPairVertexingRecomputePV(C const& /*collision*/, T const& t ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), m2); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - if (fgFitterTwoProngBarrel.getNCandidates() == 0) + if (fgFitterTwoProngBarrel.getNCandidates() == 0) { return; + } Vec3D secondaryVertex; if (!fgUsedKF) { // to be updated when seconday vertex is computed with KF @@ -4829,16 +5572,13 @@ void VarManager::FillTripletVertexing(C const& collision, T const& t1, T const& bool trackHasCov = ((fillMap & ReducedTrackBarrelCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } - float m1, m2, m3; + float m1 = o2::constants::physics::MassKaonCharged; + float m2 = o2::constants::physics::MassPionCharged; + float m3 = o2::constants::physics::MassPionCharged; - if (tripletType == kTripleCandidateToKPiPi) { - m1 = o2::constants::physics::MassKaonCharged; - m2 = o2::constants::physics::MassPionCharged; - m3 = o2::constants::physics::MassPionCharged; - } if (tripletType == kTripleCandidateToPKPi) { m1 = o2::constants::physics::MassProton; m2 = o2::constants::physics::MassKaonCharged; @@ -4849,7 +5589,7 @@ void VarManager::FillTripletVertexing(C const& collision, T const& t1, T const& ROOT::Math::PtEtaPhiMVector v3(t3.pt(), t3.eta(), t3.phi(), m3); ROOT::Math::PtEtaPhiMVector v123 = v1 + v2 + v3; - values[kUsedKF] = fgUsedKF; + values[kUsedKF] = static_cast(fgUsedKF); if (!fgUsedKF) { int procCode = 0; @@ -4909,7 +5649,7 @@ void VarManager::FillTripletVertexing(C const& collision, T const& t1, T const& o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; - o2::dataformats::VertexBase primaryVertex = {std::move(vtxXYZ), std::move(vtxCov)}; + o2::dataformats::VertexBase primaryVertex = {vtxXYZ, vtxCov}; auto covMatrixPV = primaryVertex.getCov(); if (fgUsedVars[kVertexingChi2PCA]) { @@ -4940,9 +5680,9 @@ void VarManager::FillTripletVertexing(C const& collision, T const& t1, T const& values[kVertexingTauzErr] = values[kVertexingLzErr] * v123.M() / (TMath::Abs(v123.Pz()) * o2::constants::physics::LightSpeedCm2NS); values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v123.M() / (v123.Pt() * o2::constants::physics::LightSpeedCm2NS); - values[kCosPointingAngle] = ((collision.posX() - secondaryVertex[0]) * v123.Px() + - (collision.posY() - secondaryVertex[1]) * v123.Py() + - (collision.posZ() - secondaryVertex[2]) * v123.Pz()) / + values[kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v123.Px() + + (secondaryVertex[1] - collision.posY()) * v123.Py() + + (secondaryVertex[2] - collision.posZ()) * v123.Pz()) / (v123.P() * values[VarManager::kVertexingLxyz]); // run 2 definitions: Decay length projected onto the momentum vector of the candidate values[kVertexingLzProjected] = (secondaryVertex[2] - collision.posZ()) * v123.Pz(); @@ -4989,10 +5729,11 @@ void VarManager::FillTripletVertexing(C const& collision, T const& t1, T const& } if (fgUsedVars[kKFMass]) { float mass = 0., massErr = 0.; - if (!KFGeoThreeProng.GetMass(mass, massErr)) + if (!KFGeoThreeProng.GetMass(mass, massErr)) { values[kKFMass] = mass; - else + } else { values[kKFMass] = -999.; + } } if constexpr (eventHasVtxCov) { @@ -5010,14 +5751,17 @@ void VarManager::FillTripletVertexing(C const& collision, T const& t1, T const& values[kVertexingLxyErr] = (KFPV.GetCovariance(0) + KFGeoThreeProng.GetCovariance(0)) * dxTriplet2PV * dxTriplet2PV + (KFPV.GetCovariance(2) + KFGeoThreeProng.GetCovariance(2)) * dyTriplet2PV * dyTriplet2PV + 2 * ((KFPV.GetCovariance(1) + KFGeoThreeProng.GetCovariance(1)) * dxTriplet2PV * dyTriplet2PV); values[kVertexingLzErr] = (KFPV.GetCovariance(5) + KFGeoThreeProng.GetCovariance(5)) * dzTriplet2PV * dzTriplet2PV; values[kVertexingLxyzErr] = (KFPV.GetCovariance(0) + KFGeoThreeProng.GetCovariance(0)) * dxTriplet2PV * dxTriplet2PV + (KFPV.GetCovariance(2) + KFGeoThreeProng.GetCovariance(2)) * dyTriplet2PV * dyTriplet2PV + (KFPV.GetCovariance(5) + KFGeoThreeProng.GetCovariance(5)) * dzTriplet2PV * dzTriplet2PV + 2 * ((KFPV.GetCovariance(1) + KFGeoThreeProng.GetCovariance(1)) * dxTriplet2PV * dyTriplet2PV + (KFPV.GetCovariance(3) + KFGeoThreeProng.GetCovariance(3)) * dxTriplet2PV * dzTriplet2PV + (KFPV.GetCovariance(4) + KFGeoThreeProng.GetCovariance(4)) * dyTriplet2PV * dzTriplet2PV); - if (fabs(values[kVertexingLxy]) < 1.e-8f) + if (std::fabs(values[kVertexingLxy]) < 1.e-8f) { values[kVertexingLxy] = 1.e-8f; + } values[kVertexingLxyErr] = values[kVertexingLxyErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyErr]) / values[kVertexingLxy]; - if (fabs(values[kVertexingLz]) < 1.e-8f) + if (std::fabs(values[kVertexingLz]) < 1.e-8f) { values[kVertexingLz] = 1.e-8f; + } values[kVertexingLzErr] = values[kVertexingLzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLzErr]) / values[kVertexingLz]; - if (fabs(values[kVertexingLxyz]) < 1.e-8f) + if (std::fabs(values[kVertexingLxyz]) < 1.e-8f) { values[kVertexingLxyz] = 1.e-8f; + } values[kVertexingLxyzErr] = values[kVertexingLxyzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyzErr]) / values[kVertexingLxyz]; values[kVertexingTauxy] = KFGeoThreeProng.GetPseudoProperDecayTime(KFPV, KFGeoThreeProng.GetMass()) / (o2::constants::physics::LightSpeedCm2NS); @@ -5051,21 +5795,19 @@ void VarManager::FillDileptonTrackVertexing(C const& collision, T1 const& lepton constexpr bool trackHasCov = ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0); constexpr bool muonHasCov = ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0); if (!values) { - values = fgValues; + values = static_cast(fgValues); } - float mtrack; - float mlepton1, mlepton2; + float mtrack = o2::constants::physics::MassMuon; + float mlepton1 = o2::constants::physics::MassMuon; + float mlepton2 = o2::constants::physics::MassMuon; int procCode = 0; int procCodeJpsi = 0; - values[kUsedKF] = fgUsedKF; + values[kUsedKF] = static_cast(fgUsedKF); if (!fgUsedKF) { if constexpr ((candidateType == kBcToThreeMuons) && muonHasCov) { - mlepton1 = o2::constants::physics::MassMuon; - mlepton2 = o2::constants::physics::MassMuon; - mtrack = o2::constants::physics::MassMuon; o2::track::TrackParCovFwd pars1 = FwdToTrackPar(lepton1, lepton1); o2::track::TrackParCovFwd pars2 = FwdToTrackPar(lepton2, lepton2); @@ -5074,11 +5816,11 @@ void VarManager::FillDileptonTrackVertexing(C const& collision, T1 const& lepton procCode = VarManager::fgFitterThreeProngFwd.process(pars1, pars2, pars3); procCodeJpsi = VarManager::fgFitterTwoProngFwd.process(pars1, pars2); } else if constexpr ((candidateType == kBtoJpsiEEK || candidateType == kDstarToD0KPiPi) && trackHasCov) { - if constexpr ((candidateType == kBtoJpsiEEK) && trackHasCov) { + if constexpr (candidateType == kBtoJpsiEEK) { mlepton1 = o2::constants::physics::MassElectron; mlepton2 = o2::constants::physics::MassElectron; mtrack = o2::constants::physics::MassKaonCharged; - } else if constexpr ((candidateType == kDstarToD0KPiPi) && trackHasCov) { + } else if constexpr (candidateType == kDstarToD0KPiPi) { mlepton1 = o2::constants::physics::MassKaonCharged; mlepton2 = o2::constants::physics::MassPionCharged; mtrack = o2::constants::physics::MassPionCharged; @@ -5148,13 +5890,13 @@ void VarManager::FillDileptonTrackVertexing(C const& collision, T1 const& lepton Vec3D secondaryVertex; if constexpr (eventHasVtxCov) { - std::array covMatrixPCA; + std::array covMatrixPCA{}; o2::dataformats::DCA impactParameter0; o2::dataformats::DCA impactParameter1; o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; - o2::dataformats::VertexBase primaryVertex = {std::move(vtxXYZ), std::move(vtxCov)}; + o2::dataformats::VertexBase primaryVertex = {vtxXYZ, vtxCov}; auto covMatrixPV = primaryVertex.getCov(); if constexpr ((candidateType == kBtoJpsiEEK || candidateType == kDstarToD0KPiPi) && trackHasCov) { @@ -5201,13 +5943,13 @@ void VarManager::FillDileptonTrackVertexing(C const& collision, T1 const& lepton values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v123.M() / (v123.Pt() * o2::constants::physics::LightSpeedCm2NS); if (fgUsedVars[kCosPointingAngle] && fgUsedVars[kVertexingLxyz]) { - values[VarManager::kCosPointingAngle] = ((collision.posX() - secondaryVertex[0]) * v123.Px() + - (collision.posY() - secondaryVertex[1]) * v123.Py() + - (collision.posZ() - secondaryVertex[2]) * v123.Pz()) / + values[VarManager::kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v123.Px() + + (secondaryVertex[1] - collision.posY()) * v123.Py() + + (secondaryVertex[2] - collision.posZ()) * v123.Pz()) / (v123.P() * values[VarManager::kVertexingLxyz]); } // run 2 definitions: Lxy projected onto the momentum vector of the candidate - if (fgUsedVars[kVertexingLxyProjected] || fgUsedVars[kVertexingLxyzProjected] || values[kVertexingTauxyProjected]) { + if (fgUsedVars[kVertexingLxyProjected] || fgUsedVars[kVertexingLxyzProjected] || fgUsedVars[kVertexingTauxyProjected]) { values[kVertexingLzProjected] = (secondaryVertex[2] - collision.posZ()) * v123.Pz(); values[kVertexingLzProjected] = values[kVertexingLzProjected] / TMath::Sqrt(v123.Pz() * v123.Pz()); values[kVertexingLxyProjected] = ((secondaryVertex[0] - collision.posX()) * v123.Px()) + ((secondaryVertex[1] - collision.posY()) * v123.Py()); @@ -5243,15 +5985,17 @@ void VarManager::FillDileptonTrackVertexing(C const& collision, T1 const& lepton } // Quantities between 3rd prong and candidate - if (fgUsedVars[kKFDCAxyzBetweenProngs]) + if (fgUsedVars[kKFDCAxyzBetweenProngs]) { values[kKFDCAxyzBetweenProngs] = KFGeoTwoLeptons.GetDistanceFromParticle(hadronKF); + } KFGeoThreeProng.SetConstructMethod(2); KFGeoThreeProng.AddDaughter(KFGeoTwoLeptons); KFGeoThreeProng.AddDaughter(hadronKF); - if (fgUsedVars[kKFMass]) + if (fgUsedVars[kKFMass]) { values[kKFMass] = KFGeoThreeProng.GetMass(); + } if constexpr (eventHasVtxCov) { KFPVertex kfpVertex = createKFPVertexFromCollision(collision); @@ -5267,26 +6011,32 @@ void VarManager::FillDileptonTrackVertexing(C const& collision, T1 const& lepton values[kVertexingLxyErr] = (KFPV.GetCovariance(0) + KFGeoThreeProng.GetCovariance(0)) * dxTriplet3PV * dxTriplet3PV + (KFPV.GetCovariance(2) + KFGeoThreeProng.GetCovariance(2)) * dyTriplet3PV * dyTriplet3PV + 2 * ((KFPV.GetCovariance(1) + KFGeoThreeProng.GetCovariance(1)) * dxTriplet3PV * dyTriplet3PV); values[kVertexingLzErr] = (KFPV.GetCovariance(5) + KFGeoThreeProng.GetCovariance(5)) * dzTriplet3PV * dzTriplet3PV; values[kVertexingLxyzErr] = (KFPV.GetCovariance(0) + KFGeoThreeProng.GetCovariance(0)) * dxTriplet3PV * dxTriplet3PV + (KFPV.GetCovariance(2) + KFGeoThreeProng.GetCovariance(2)) * dyTriplet3PV * dyTriplet3PV + (KFPV.GetCovariance(5) + KFGeoThreeProng.GetCovariance(5)) * dzTriplet3PV * dzTriplet3PV + 2 * ((KFPV.GetCovariance(1) + KFGeoThreeProng.GetCovariance(1)) * dxTriplet3PV * dyTriplet3PV + (KFPV.GetCovariance(3) + KFGeoThreeProng.GetCovariance(3)) * dxTriplet3PV * dzTriplet3PV + (KFPV.GetCovariance(4) + KFGeoThreeProng.GetCovariance(4)) * dyTriplet3PV * dzTriplet3PV); - if (fabs(values[kVertexingLxy]) < 1.e-8f) + if (std::fabs(values[kVertexingLxy]) < 1.e-8f) { values[kVertexingLxy] = 1.e-8f; + } values[kVertexingLxyErr] = values[kVertexingLxyErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyErr]) / values[kVertexingLxy]; - if (fabs(values[kVertexingLz]) < 1.e-8f) + if (std::fabs(values[kVertexingLz]) < 1.e-8f) { values[kVertexingLz] = 1.e-8f; + } values[kVertexingLzErr] = values[kVertexingLzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLzErr]) / values[kVertexingLz]; - if (fabs(values[kVertexingLxyz]) < 1.e-8f) + if (std::fabs(values[kVertexingLxyz]) < 1.e-8f) { values[kVertexingLxyz] = 1.e-8f; + } values[kVertexingLxyzErr] = values[kVertexingLxyzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyzErr]) / values[kVertexingLxyz]; - if (fgUsedVars[kVertexingTauxy]) + if (fgUsedVars[kVertexingTauxy]) { values[kVertexingTauxy] = KFGeoThreeProng.GetPseudoProperDecayTime(KFPV, KFGeoThreeProng.GetMass()) / (o2::constants::physics::LightSpeedCm2NS); - if (fgUsedVars[kVertexingTauxyErr]) + } + if (fgUsedVars[kVertexingTauxyErr]) { values[kVertexingTauxyErr] = values[kVertexingLxyErr] * KFGeoThreeProng.GetMass() / (KFGeoThreeProng.GetPt() * o2::constants::physics::LightSpeedCm2NS); + } - if (fgUsedVars[kCosPointingAngle]) + if (fgUsedVars[kCosPointingAngle]) { values[VarManager::kCosPointingAngle] = (dxTriplet3PV * KFGeoThreeProng.GetPx() + dyTriplet3PV * KFGeoThreeProng.GetPy() + dzTriplet3PV * KFGeoThreeProng.GetPz()) / (KFGeoThreeProng.GetP() * values[VarManager::kVertexingLxyz]); + } } // end calculate vertex variables // As defined in Run 2 (projected onto momentum) @@ -5309,7 +6059,7 @@ template void VarManager::FillQVectorFromGFW(C const& /*collision*/, A const& compA11, A const& compB11, A const& compC11, A const& compA21, A const& compB21, A const& compC21, A const& compA31, A const& compB31, A const& compC31, A const& compA41, A const& compB41, A const& compC41, A const& compA23, A const& compA42, float S10A, float S10B, float S10C, float S11A, float S11B, float S11C, float S12A, float S13A, float S14A, float S21A, float S22A, float S31A, float S41A, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Fill Qn vectors from generic flow framework for different eta gap A, B, C (n=1,2,3,4) with proper normalisation @@ -5370,7 +6120,7 @@ void VarManager::FillQVectorFromGFW(C const& /*collision*/, A const& compA11, A values[kM11REF] = S21A - S12A; values[kM1111REF] = S41A - 6. * S12A * S21A + 8. * S13A * S11A + 3. * S22A - 6. * S14A; values[kCORR2REF] = (norm(compA21) - S12A) / values[kM11REF]; - values[kCORR4REF] = (pow(norm(compA21), 2) + norm(compA42) - 2. * (compA42 * conj(compA21) * conj(compA21)).real() + 8. * (compA23 * conj(compA21)).real() - 4. * S12A * norm(compA21) - 6. * S14A + 2. * S22A) / values[kM1111REF]; + values[kCORR4REF] = (std::pow(norm(compA21), 2) + norm(compA42) - 2. * (compA42 * conj(compA21) * conj(compA21)).real() + 8. * (compA23 * conj(compA21)).real() - 4. * S12A * norm(compA21) - 6. * S14A + 2. * S22A) / values[kM1111REF]; values[kCORR2REF] = std::isnan(values[kM11REF]) || std::isinf(values[kM11REF]) || std::isnan(values[kCORR2REF]) || std::isinf(values[kCORR2REF]) ? 0 : values[kCORR2REF]; values[kM11REF] = std::isnan(values[kM11REF]) || std::isinf(values[kM11REF]) || std::isnan(values[kCORR2REF]) || std::isinf(values[kCORR2REF]) ? 0 : values[kM11REF]; values[kCORR4REF] = std::isnan(values[kM1111REF]) || std::isinf(values[kM1111REF]) || std::isnan(values[kCORR4REF]) || std::isinf(values[kCORR4REF]) ? 0 : values[kCORR4REF]; @@ -5414,7 +6164,7 @@ template void VarManager::FillQVectorFromCentralFW(C const& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float xQVecFT0a = collision.qvecFT0ARe(); // already normalised @@ -5510,7 +6260,7 @@ template void VarManager::FillSpectatorPlane(C const& collision, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } auto zncEnergy = collision.energySectorZNC(); @@ -5546,9 +6296,9 @@ void VarManager::FillSpectatorPlane(C const& collision, float* values) values[kTimeZPA] = collision.timeZPA(); values[kTimeZPC] = collision.timeZPC(); - constexpr float beamEne = 5.36 * 0.5; - constexpr float x[4] = {-1.75, 1.75, -1.75, 1.75}; - constexpr float y[4] = {-1.75, -1.75, 1.75, 1.75}; + // constexpr float beamEne = 5.36 * 0.5; + constexpr std::array x = {-1.75f, 1.75f, -1.75f, 1.75f}; + constexpr std::array y = {-1.75f, -1.75f, 1.75f, 1.75f}; // constexpr float intcalibZNA[4] = {0.7997028, 0.8453715, 0.7879917, 0.7695486}; // constexpr float intcalibZNC[4] = {0.7631577, 0.8408003, 0.7083920, 0.7731769}; // constexpr float alpha = 0.395; // WARNING: Run 2 coorection, to be checked @@ -5579,9 +6329,9 @@ void VarManager::FillSpectatorPlane(C const& collision, float* values) } if (denZNC != 0.) { - float nSpecnC = zncCommon / beamEne; // WARNING: Run 2 coorection, to be checked - float cZNC = 1.89358 - 0.71262 / (nSpecnC + 0.71789); // WARNING: Run 2 coorection, to be checked - cZNC = 1.; + // float nSpecnC = zncCommon / beamEne; // WARNING: Run 2 correction, to be checked + // float cZNC = 1.89358 - 0.71262 / (nSpecnC + 0.71789); // WARNING: Run 2 correction, to be checked + float cZNC = 1.; values[kQ1ZNCX] = cZNC * numXZNC / denZNC; values[kQ1ZNCY] = cZNC * numYZNC / denZNC; } else { @@ -5589,9 +6339,9 @@ void VarManager::FillSpectatorPlane(C const& collision, float* values) } if (denZNA != 0.) { - float nSpecnA = znaCommon / beamEne; // WARNING: Run 2 coorection, to be checked - float cZNA = 1.89358 - 0.71262 / (nSpecnA + 0.71789); // WARNING: Run 2 coorection, to be checked - cZNA = 1.; + // float nSpecnA = znaCommon / beamEne; // WARNING: Run 2 correction, to be checked + // float cZNA = 1.89358 - 0.71262 / (nSpecnA + 0.71789); // WARNING: Run 2 correction, to be checked + float cZNA = 1.; values[kQ1ZNAX] = cZNA * numXZNA / denZNA; values[kQ1ZNAY] = cZNA * numYZNA / denZNA; } else { @@ -5607,7 +6357,7 @@ void VarManager::FillSpectatorPlane(C const& collision, float* values) values[kQ1ZNACXX] = values[kQ1ZNACYY] = values[kQ1ZNACYX] = values[kQ1ZNACXY] = 999.; } - if (znaCommon != 0 && sumZNA != 0 && zncCommon != 0 && sumZNC) { + if (znaCommon != 0 && sumZNA != 0 && zncCommon != 0 && sumZNC != 0) { values[KIntercalibZNA] = znaCommon - sumZNA; values[KIntercalibZNC] = zncCommon - sumZNC; } @@ -5618,7 +6368,7 @@ void VarManager::FillPairVn(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -5648,6 +6398,8 @@ void VarManager::FillPairVn(T1 const& t1, T2 const& t2, float* values) // Compute the scalar product UQ using Q-vector from A, for second and third harmonic // Dilepton vn could be accessible after dividing this product with the R factor values[kU2Q2] = values[kQ2X0A] * TMath::Cos(2 * v12.Phi()) + values[kQ2Y0A] * TMath::Sin(2 * v12.Phi()); + values[kU2Q2POS] = values[kQ2X0APOS] * TMath::Cos(2 * v12.Phi()) + values[kQ2Y0APOS] * TMath::Sin(2 * v12.Phi()); + values[kU2Q2NEG] = values[kQ2X0ANEG] * TMath::Cos(2 * v12.Phi()) + values[kQ2Y0ANEG] * TMath::Sin(2 * v12.Phi()); values[kU3Q3] = values[kQ3X0A] * TMath::Cos(3 * v12.Phi()) + values[kQ3Y0A] * TMath::Sin(3 * v12.Phi()); values[kR2SP_AB] = (values[kQ2X0A] * values[kQ2X0B] + values[kQ2Y0A] * values[kQ2Y0B]); values[kR2SP_AC] = (values[kQ2X0A] * values[kQ2X0C] + values[kQ2Y0A] * values[kQ2Y0C]); @@ -5655,13 +6407,18 @@ void VarManager::FillPairVn(T1 const& t1, T2 const& t2, float* values) values[kR3SP] = (values[kQ3X0B] * values[kQ3X0C] + values[kQ3Y0B] * values[kQ3Y0C]); float Psi2A = getEventPlane(2, values[kQ2X0A], values[kQ2Y0A]); + float Psi2APOS = getEventPlane(2, values[kQ2X0APOS], values[kQ2Y0APOS]); + float Psi2ANEG = getEventPlane(2, values[kQ2X0ANEG], values[kQ2Y0ANEG]); float Psi3A = getEventPlane(3, values[kQ3X0A], values[kQ3Y0A]); float Psi2B = getEventPlane(2, values[kQ2X0B], values[kQ2Y0B]); float Psi3B = getEventPlane(3, values[kQ3X0B], values[kQ3Y0B]); float Psi2C = getEventPlane(2, values[kQ2X0C], values[kQ2Y0C]); float Psi3C = getEventPlane(3, values[kQ3X0C], values[kQ3Y0C]); values[kCos2DeltaPhi] = TMath::Cos(2 * (v12.Phi() - Psi2A)); + values[kCos2DeltaPhiPOS] = TMath::Cos(2 * (v12.Phi() - Psi2APOS)); + values[kCos2DeltaPhiNEG] = TMath::Cos(2 * (v12.Phi() - Psi2ANEG)); values[kCos3DeltaPhi] = TMath::Cos(3 * (v12.Phi() - Psi3A)); + values[kCos2DeltaPhi_FT0C] = TMath::Cos(2 * (v12.Phi() - Psi2C)); values[kR2EP_AB] = TMath::Cos(2 * (Psi2A - Psi2B)); values[kR2EP_AC] = TMath::Cos(2 * (Psi2A - Psi2C)); values[kR2EP_BC] = TMath::Cos(2 * (Psi2B - Psi2C)); @@ -5669,28 +6426,30 @@ void VarManager::FillPairVn(T1 const& t1, T2 const& t2, float* values) float V2SP = values[kU2Q2] / values[kR2SP]; float V2EP = values[kCos2DeltaPhi] / values[kR2EP]; + float V2EP_FT0C = values[kCos2DeltaPhi_FT0C] / values[kR2EP]; values[kV2SP] = std::isnan(V2SP) || std::isinf(V2SP) ? 0. : V2SP; values[kWV2SP] = std::isnan(V2SP) || std::isinf(V2SP) ? 0. : 1.0; values[kV2EP] = std::isnan(V2EP) || std::isinf(V2EP) ? 0. : V2EP; + values[kV2EP_FT0C] = std::isnan(V2EP_FT0C) || std::isinf(V2EP_FT0C) ? 0. : V2EP_FT0C; values[kWV2EP] = std::isnan(V2EP) || std::isinf(V2EP) ? 0. : 1.0; - if (std::isnan(VarManager::fgValues[VarManager::kU2Q2]) == true) { + if (std::isnan(VarManager::fgValues[VarManager::kU2Q2])) { values[kU2Q2] = -999.; values[kR2SP_AB] = -999.; values[kR2SP_AC] = -999.; values[kR2SP_BC] = -999.; } - if (std::isnan(VarManager::fgValues[VarManager::kU3Q3]) == true) { + if (std::isnan(VarManager::fgValues[VarManager::kU3Q3])) { values[kU3Q3] = -999.; values[kR3SP] = -999.; } - if (std::isnan(VarManager::fgValues[VarManager::kCos2DeltaPhi]) == true) { + if (std::isnan(VarManager::fgValues[VarManager::kCos2DeltaPhi])) { values[kCos2DeltaPhi] = -999.; values[kR2EP_AB] = -999.; values[kR2EP_AC] = -999.; values[kR2EP_BC] = -999.; } - if (std::isnan(VarManager::fgValues[VarManager::kCos3DeltaPhi]) == true) { + if (std::isnan(VarManager::fgValues[VarManager::kCos3DeltaPhi])) { values[kCos3DeltaPhi] = -999.; values[kR3EP] = -999.; } @@ -5715,11 +6474,74 @@ void VarManager::FillPairVn(T1 const& t1, T2 const& t2, float* values) values[kCosThetaStarFT0C] = v_CM.Dot(zaxisFT0C); values[kAbsCosThetaStarFT0C] = std::abs(values[kCosThetaStarFT0C]); values[kCos2ThetaStarFT0C] = values[kCosThetaStarFT0C] * values[kCosThetaStarFT0C]; + } - // Randomize the event plane angle to check the unpolarized contribution - ROOT::Math::XYZVector zaxisRandom = ROOT::Math::XYZVector(TMath::Cos(values[kRandomPsi2]), TMath::Sin(values[kRandomPsi2]), 0).Unit(); - values[kCosThetaStarRandom] = v_CM.Dot(zaxisRandom); - values[kCos2ThetaStarRandom] = values[kCosThetaStarRandom] * values[kCosThetaStarRandom]; + // Coherent Jpsi A2 + bool useCoherentJpsiA2 = fgUsedVars[kA2EP_TPC] || fgUsedVars[kA2EP_FT0A] || fgUsedVars[kA2EP_FT0C]; + if (useCoherentJpsiA2) { + // remove daughter from TPC Q-vector + // TODO: remove based on track cut in qVectorTable + float Q2X0A = values[kQ2X0A] * values[kMultA]; + float Q2Y0A = values[kQ2Y0A] * values[kMultA]; + float nNorm = values[kMultA]; + + // checkTrack(t1); + if (values[kSel1] > 0) { + float qx1 = t1.pt() * TMath::Cos(2. * t1.phi()); + float qy1 = t1.pt() * TMath::Sin(2. * t1.phi()); + Q2X0A = Q2X0A - qx1; + Q2Y0A = Q2Y0A - qy1; + nNorm = nNorm - 1.; + } + // checkTrack(t2); + if (values[kSel2] > 0) { + float qx2 = t2.pt() * TMath::Cos(2. * t2.phi()); + float qy2 = t2.pt() * TMath::Sin(2. * t2.phi()); + Q2X0A = Q2X0A - qx2; + Q2Y0A = Q2Y0A - qy2; + nNorm = nNorm - 1.; + } + values[kNnorm] = nNorm; + if (nNorm <= 0) { + values[kQ2X0A] = -999.; + values[kQ2Y0A] = -999.; + return; + } + + Q2X0A = nNorm > 0 ? Q2X0A / nNorm : NAN; + Q2Y0A = nNorm > 0 ? Q2Y0A / nNorm : NAN; + + float Psi2ATPC = getEventPlane(2, Q2X0A, Q2Y0A); + values[kPsi2A] = Psi2ATPC; + + // pT ~ 0.2, non-relativistic + // ROOT::Math::PtEtaPhiMVector v_daughter = t1.sign() > 0 ? v1 - v2 : v2 - v1; + // boost to Jpsi rest frame, then calculate the angle with respect to the event plane + ROOT::Math::Boost boostv12{v12.BoostToCM()}; + ROOT::Math::PtEtaPhiMVector v_daughter = boostv12(t1.sign() > 0 ? v1 : v2); + + // reaction plane + float phi = v_daughter.Phi() > o2::constants::math::PI ? o2::constants::math::TwoPI - v_daughter.Phi() : v_daughter.Phi(); + values[kDeltaPhi_TPC] = phi > Psi2ATPC ? phi - Psi2ATPC : Psi2ATPC - phi; + values[kDeltaPhi_TPC] = values[kDeltaPhi_TPC] > o2::constants::math::PI ? o2::constants::math::TwoPI - values[kDeltaPhi_TPC] : values[kDeltaPhi_TPC]; + values[kDeltaPhi_FT0A] = phi > Psi2B ? phi - Psi2B : Psi2B - phi; + values[kDeltaPhi_FT0A] = values[kDeltaPhi_FT0A] > o2::constants::math::PI ? o2::constants::math::TwoPI - values[kDeltaPhi_FT0A] : values[kDeltaPhi_FT0A]; + values[kDeltaPhi_FT0C] = phi > Psi2C ? phi - Psi2C : Psi2C - phi; + values[kDeltaPhi_FT0C] = values[kDeltaPhi_FT0C] > o2::constants::math::PI ? o2::constants::math::TwoPI - values[kDeltaPhi_FT0C] : values[kDeltaPhi_FT0C]; + // fold delta phi to [0, pi/2] + values[kDeltaPhi_TPC] = values[kDeltaPhi_TPC] > o2::constants::math::PIHalf ? o2::constants::math::PI - values[kDeltaPhi_TPC] : values[kDeltaPhi_TPC]; + values[kDeltaPhi_FT0A] = values[kDeltaPhi_FT0A] > o2::constants::math::PIHalf ? o2::constants::math::PI - values[kDeltaPhi_FT0A] : values[kDeltaPhi_FT0A]; + values[kDeltaPhi_FT0C] = values[kDeltaPhi_FT0C] > o2::constants::math::PIHalf ? o2::constants::math::PI - values[kDeltaPhi_FT0C] : values[kDeltaPhi_FT0C]; + values[kCos2DeltaPhi_TPC] = TMath::Cos(2. * (phi - Psi2ATPC)); + values[kCos2DeltaPhi_FT0A] = TMath::Cos(2. * (phi - Psi2B)); + values[kCos2DeltaPhi_FT0C] = TMath::Cos(2. * (phi - Psi2C)); + + float A2RP_TPC = values[kCos2DeltaPhi_TPC] / values[kR2EP]; + float A2RP_FT0A = values[kCos2DeltaPhi_FT0A] / values[kR2EP]; + float A2RP_FT0C = values[kCos2DeltaPhi_FT0C] / values[kR2EP]; + values[kA2EP_TPC] = std::isnan(A2RP_TPC) || std::isinf(A2RP_TPC) ? -999. : A2RP_TPC; + values[kA2EP_FT0A] = std::isnan(A2RP_FT0A) || std::isinf(A2RP_FT0A) ? -999. : A2RP_FT0A; + values[kA2EP_FT0C] = std::isnan(A2RP_FT0C) || std::isinf(A2RP_FT0C) ? -999. : A2RP_FT0C; } // kV4, kC4POI, kC4REF etc. @@ -5795,7 +6617,7 @@ template void VarManager::FillZDC(T const& zdc, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kEnergyCommonZNA] = (zdc.energyCommonZNA() > 0) ? zdc.energyCommonZNA() : -1.; @@ -5812,7 +6634,7 @@ template void VarManager::FillDileptonHadron(T1 const& dilepton, T2 const& hadron, float* values, float hadronMass) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (fgUsedVars[kPairMass] || fgUsedVars[kPairPt] || fgUsedVars[kPairEta] || fgUsedVars[kPairPhi] || fgUsedVars[kPairMassDau] || fgUsedVars[kPairPtDau] || fgUsedVars[kDileptonHadronKstar]) { @@ -5831,23 +6653,16 @@ void VarManager::FillDileptonHadron(T1 const& dilepton, T2 const& hadron, float* ROOT::Math::PtEtaPhiMVector v12_Qvect = v1 - v2; double Pinv = v12.M(); double Q1 = (dilepton.mass() * dilepton.mass() - hadronMass * hadronMass) / Pinv; - values[kDileptonHadronKstar] = sqrt(Q1 * Q1 - v12_Qvect.M2()) / 2.0; + values[kDileptonHadronKstar] = std::sqrt(Q1 * Q1 - v12_Qvect.M2()) / 2.0; } if (fgUsedVars[kDeltaPhi]) { - double delta = dilepton.phi() - hadron.phi(); - if (delta > 3.0 / 2.0 * M_PI) { - delta -= 2.0 * M_PI; - } - if (delta < -0.5 * M_PI) { - delta += 2.0 * M_PI; - } - values[kDeltaPhi] = delta; + values[kDeltaPhi] = RecoDecay::constrainAngle(dilepton.phi() - hadron.phi(), -o2::constants::math::PIHalf); } if (fgUsedVars[kDeltaPhiSym]) { double delta = std::abs(dilepton.phi() - hadron.phi()); - if (delta > M_PI) { - delta = 2 * M_PI - delta; + if (delta > o2::constants::math::PI) { + delta = o2::constants::math::TwoPI - delta; } values[kDeltaPhiSym] = delta; } @@ -5856,9 +6671,15 @@ void VarManager::FillDileptonHadron(T1 const& dilepton, T2 const& hadron, float* } } -template -void VarManager::FillEnergyCorrelatorTriple(T1 const& lepton1, T2 const& lepton2, T3 const& hadron, float* values, float Translow, float Transhigh, bool applyFitMass, float sidebandMass, float weight) +template +void VarManager::FillEnergyCorrelatorTriple(C const& collision, T1 const& lepton1, T2 const& lepton2, T3 const& hadron, float* values, float Translow, float Transhigh, bool applyFitMass, float sidebandMass, float weight) { + if (!values) { + values = static_cast(fgValues); + } + if (fgUsedVars[kVertexingTauxyProjectedPoleJPsiMass] || fgUsedVars[kVertexingLxyProjected]) { + FillPairVertexing(collision, lepton1, lepton2, values); + } float m1 = o2::constants::physics::MassElectron; float m2 = o2::constants::physics::MassElectron; @@ -5874,7 +6695,8 @@ void VarManager::FillEnergyCorrelatorTriple(T1 const& lepton1, T2 const& lepton2 dileptonmass = sidebandMass; } - if (fgUsedVars[kCosChi] || fgUsedVars[kECWeight] || fgUsedVars[kCosTheta] || fgUsedVars[kEWeight_before] || fgUsedVars[kPtDau] || fgUsedVars[kEtaDau] || fgUsedVars[kPhiDau] || fgUsedVars[kCosChi_randomPhi_trans] || fgUsedVars[kCosChi_randomPhi_toward] || fgUsedVars[kCosChi_randomPhi_away]) { + if (fgUsedVars[kPairMassDau] || fgUsedVars[kCosChi] || fgUsedVars[kECWeight] || fgUsedVars[kCosTheta] || fgUsedVars[kEWeight_before] || fgUsedVars[kPtDau] || fgUsedVars[kEtaDau] || fgUsedVars[kPhiDau] || fgUsedVars[kCosChi_randomPhi_trans] || fgUsedVars[kCosChi_randomPhi_toward] || fgUsedVars[kCosChi_randomPhi_away]) { + values[kPairMassDau] = dilepton.mass(); values[kdileptonmass] = dileptonmass; ROOT::Math::PtEtaPhiMVector v1(dilepton.pt(), dilepton.eta(), dilepton.phi(), dileptonmass); ROOT::Math::PtEtaPhiMVector v2(hadron.pt(), hadron.eta(), hadron.phi(), o2::constants::physics::MassPionCharged); @@ -5904,7 +6726,7 @@ void VarManager::FillEnergyCorrelatorTriple(T1 const& lepton1, T2 const& lepton2 float randomPhi_toward = -o2::constants::math::PIHalf; float randomPhi_away = -o2::constants::math::PIHalf; - if ((deltaphi > -Transhigh * TMath::Pi() && deltaphi < -Translow * TMath::Pi()) || (deltaphi > Translow * TMath::Pi() && deltaphi < Transhigh * TMath::Pi())) { + if ((deltaphi > -Transhigh * o2::constants::math::PI && deltaphi < -Translow * o2::constants::math::PI) || (deltaphi > Translow * o2::constants::math::PI && deltaphi < Transhigh * o2::constants::math::PI)) { randomPhi_trans = gRandom->Uniform(-o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf); randomPhi_toward = gRandom->Uniform(-o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf); randomPhi_away = gRandom->Uniform(-o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf); @@ -5945,13 +6767,10 @@ void VarManager::FillEnergyCorrelatorsUnfoldingTriple(T1 const& lepton1, T2 cons dileptonmass = dilepton.mass(); } - float MassHadron; + float MassHadron = o2::constants::physics::MassPionCharged; if constexpr (pairType == kJpsiHadronMass) { MassHadron = TMath::Sqrt(t1.e() * t1.e() - t1.p() * t1.p()); } - if constexpr (pairType == kJpsiPionMass) { - MassHadron = o2::constants::physics::MassPionCharged; - } ROOT::Math::PtEtaPhiMVector v1_gen(track.pt(), track.eta(), track.phi(), o2::constants::physics::MassJPsi); ROOT::Math::PtEtaPhiMVector v2_gen(t1.pt(), t1.eta(), t1.phi(), MassHadron); float E_boost_gen = LorentzTransformJpsihadroncosChi("weight_boost", v1_gen, v2_gen); @@ -5971,7 +6790,7 @@ void VarManager::FillEnergyCorrelatorsUnfoldingTriple(T1 const& lepton1, T2 cons values[kMCCosChi_randomPhi_trans_gen] = -999.9f; float deltaphi_rec = RecoDecay::constrainAngle(dilepton.phi() - hadron.phi(), -o2::constants::math::PIHalf); - if ((deltaphi_rec > -Transhigh * TMath::Pi() && deltaphi_rec < -Translow * TMath::Pi()) || (deltaphi_rec > Translow * TMath::Pi() && deltaphi_rec < Transhigh * TMath::Pi())) { + if ((deltaphi_rec > -Transhigh * o2::constants::math::PI && deltaphi_rec < -Translow * o2::constants::math::PI) || (deltaphi_rec > Translow * o2::constants::math::PI && deltaphi_rec < Transhigh * o2::constants::math::PI)) { float randomPhi_trans_rec = gRandom->Uniform(-o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf); ROOT::Math::PtEtaPhiMVector v2_randomPhi_trans_rec(v2_rec.pt(), v2_rec.eta(), randomPhi_trans_rec, o2::constants::physics::MassPionCharged); values[kMCCosChi_randomPhi_trans_rec] = LorentzTransformJpsihadroncosChi("coschi", v1_rec, v2_randomPhi_trans_rec); @@ -5987,7 +6806,7 @@ template void VarManager::FillDileptonPhoton(T1 const& dilepton, T2 const& photon, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (fgUsedVars[kPairMass] || fgUsedVars[kPairPt] || fgUsedVars[kPairEta] || fgUsedVars[kPairPhi]) { ROOT::Math::PtEtaPhiMVector v1(dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.mass()); @@ -6014,7 +6833,7 @@ template void VarManager::FillHadron(T const& hadron, float* values, float hadronMass) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } ROOT::Math::PtEtaPhiMVector vhadron(hadron.pt(), hadron.eta(), hadron.phi(), hadronMass); @@ -6025,11 +6844,11 @@ void VarManager::FillHadron(T const& hadron, float* values, float hadronMass) values[kRap] = vhadron.Rapidity(); } -template -void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper, T& bdtScoreCharmHad, float* values) +template +void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, T& bdtScoreCharmHad, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr (partType == kJPsi) { @@ -6039,45 +6858,52 @@ void VarManager::FillSingleDileptonCharmHadron(Cand const& candidate, H hfHelper values[kRap] = candidate.rap(); } if constexpr (partType == kD0ToPiK) { - values[kMassCharmHadron] = hfHelper.invMassD0ToPiK(candidate); + values[kMassCharmHadron] = HfHelper::invMassD0ToPiK(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yD0(candidate); + values[kRapCharmHadron] = HfHelper::yD0(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } if constexpr (partType == kD0barToKPi) { - values[kMassCharmHadron] = hfHelper.invMassD0barToKPi(candidate); + values[kMassCharmHadron] = HfHelper::invMassD0barToKPi(candidate); values[kPtCharmHadron] = candidate.pt(); values[kPhiCharmHadron] = candidate.phi(); - values[kRapCharmHadron] = hfHelper.yD0(candidate); + values[kRapCharmHadron] = HfHelper::yD0(candidate); + values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); + } + if constexpr (partType == kDplusToPiKPi) { + values[kMassCharmHadron] = HfHelper::invMassDplusToPiKPi(candidate); + values[kPtCharmHadron] = candidate.pt(); + values[kPhiCharmHadron] = candidate.phi(); + values[kRapCharmHadron] = HfHelper::yDplus(candidate); values[kBdtCharmHadron] = static_cast(bdtScoreCharmHad); } } -template -void VarManager::FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, H hfHelper, T& bdtScoreCharmHad, float* values) +template +void VarManager::FillDileptonCharmHadron(DQ const& dilepton, HF const& charmHadron, T& bdtScoreCharmHad, float* values) { - FillSingleDileptonCharmHadron(dilepton, hfHelper, bdtScoreCharmHad, values); - FillSingleDileptonCharmHadron(charmHadron, hfHelper, bdtScoreCharmHad, values); + FillSingleDileptonCharmHadron(dilepton, bdtScoreCharmHad, values); + FillSingleDileptonCharmHadron(charmHadron, bdtScoreCharmHad, values); } template void VarManager::FillDileptonTrackTrack(T1 const& dilepton, T2 const& hadron1, T3 const& hadron2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } - double defaultDileptonMass = 3.096; + double defaultDileptonMass = o2::constants::physics::MassJPsi; double hadronMass1 = o2::constants::physics::MassPionCharged; double hadronMass2 = o2::constants::physics::MassPionCharged; if (candidateType == kXtoJpsiPiPi) { - defaultDileptonMass = 3.096; + defaultDileptonMass = o2::constants::physics::MassJPsi; hadronMass1 = o2::constants::physics::MassPionCharged; hadronMass2 = o2::constants::physics::MassPionCharged; } if (candidateType == kChictoJpsiEE) { - defaultDileptonMass = 3.096; + defaultDileptonMass = o2::constants::physics::MassJPsi; hadronMass1 = o2::constants::physics::MassElectron; hadronMass2 = o2::constants::physics::MassElectron; } @@ -6110,7 +6936,7 @@ void VarManager::FillDileptonTrackTrack(T1 const& dilepton, T2 const& hadron1, T values[kQ] = v123.M() - defaultDileptonMass - v23.M(); values[kDeltaR1] = ROOT::Math::VectorUtil::DeltaR(v1, v2); values[kDeltaR2] = ROOT::Math::VectorUtil::DeltaR(v1, v3); - values[kDeltaR] = sqrt(pow(values[kDeltaR1], 2) + pow(values[kDeltaR2], 2)); + values[kDeltaR] = std::sqrt(std::pow(values[kDeltaR1], 2) + std::pow(values[kDeltaR2], 2)); values[kRap] = v123.Rapidity(); } } @@ -6127,17 +6953,17 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l } if (!values) { - values = fgValues; + values = static_cast(fgValues); } - float mtrack1, mtrack2; - float mlepton1, mlepton2; + float mlepton1 = o2::constants::physics::MassElectron; + float mlepton2 = o2::constants::physics::MassElectron; + float mtrack1 = o2::constants::physics::MassPionCharged; + float mtrack2 = o2::constants::physics::MassPionCharged; + float mMother = 3.686; - if constexpr (candidateType == kXtoJpsiPiPi || candidateType == kPsi2StoJpsiPiPi) { - mlepton1 = o2::constants::physics::MassElectron; - mlepton2 = o2::constants::physics::MassElectron; - mtrack1 = o2::constants::physics::MassPionCharged; - mtrack2 = o2::constants::physics::MassPionCharged; + if constexpr (candidateType == kXtoJpsiPiPi) { + mMother = o2::constants::physics::MassX3872; } ROOT::Math::PtEtaPhiMVector v1(lepton1.pt(), lepton1.eta(), lepton1.phi(), mlepton1); @@ -6149,38 +6975,44 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l int procCodeDilepton = 0; int procCodeDileptonTrackTrack = 0; - values[kUsedKF] = fgUsedKF; + values[kUsedKF] = static_cast(fgUsedKF); if (!fgUsedKF) { // create covariance matrix - std::array lepton1pars = {lepton1.y(), lepton1.z(), lepton1.snp(), lepton1.tgl(), lepton1.signed1Pt()}; - std::array lepton1covs = {lepton1.cYY(), lepton1.cZY(), lepton1.cZZ(), lepton1.cSnpY(), lepton1.cSnpZ(), - lepton1.cSnpSnp(), lepton1.cTglY(), lepton1.cTglZ(), lepton1.cTglSnp(), lepton1.cTglTgl(), - lepton1.c1PtY(), lepton1.c1PtZ(), lepton1.c1PtSnp(), lepton1.c1PtTgl(), lepton1.c1Pt21Pt2()}; - o2::track::TrackParCov pars1{lepton1.x(), lepton1.alpha(), lepton1pars, lepton1covs}; - std::array lepton2pars = {lepton2.y(), lepton2.z(), lepton2.snp(), lepton2.tgl(), lepton2.signed1Pt()}; - std::array lepton2covs = {lepton2.cYY(), lepton2.cZY(), lepton2.cZZ(), lepton2.cSnpY(), lepton2.cSnpZ(), - lepton2.cSnpSnp(), lepton2.cTglY(), lepton2.cTglZ(), lepton2.cTglSnp(), lepton2.cTglTgl(), - lepton2.c1PtY(), lepton2.c1PtZ(), lepton2.c1PtSnp(), lepton2.c1PtTgl(), lepton2.c1Pt21Pt2()}; - o2::track::TrackParCov pars2{lepton2.x(), lepton2.alpha(), lepton2pars, lepton2covs}; - std::array track1pars = {track1.y(), track1.z(), track1.snp(), track1.tgl(), track1.signed1Pt()}; - std::array track1covs = {track1.cYY(), track1.cZY(), track1.cZZ(), track1.cSnpY(), track1.cSnpZ(), - track1.cSnpSnp(), track1.cTglY(), track1.cTglZ(), track1.cTglSnp(), track1.cTglTgl(), - track1.c1PtY(), track1.c1PtZ(), track1.c1PtSnp(), track1.c1PtTgl(), track1.c1Pt21Pt2()}; - o2::track::TrackParCov pars3{track1.x(), track1.alpha(), track1pars, track1covs}; - std::array track2pars = {track2.y(), track2.z(), track2.snp(), track2.tgl(), track2.signed1Pt()}; - std::array track2covs = {track2.cYY(), track2.cZY(), track2.cZZ(), track2.cSnpY(), track2.cSnpZ(), - track2.cSnpSnp(), track2.cTglY(), track2.cTglZ(), track2.cTglSnp(), track2.cTglTgl(), - track2.c1PtY(), track2.c1PtZ(), track2.c1PtSnp(), track2.c1PtTgl(), track2.c1Pt21Pt2()}; - o2::track::TrackParCov pars4{track2.x(), track2.alpha(), track2pars, track2covs}; + o2::track::TrackParCov pars1 = getTrackParCov(lepton1); + o2::track::TrackParCov pars2 = getTrackParCov(lepton2); + o2::track::TrackParCov pars3 = getTrackParCov(track1); + o2::track::TrackParCov pars4 = getTrackParCov(track2); + // std::array lepton1pars = {lepton1.y(), lepton1.z(), lepton1.snp(), lepton1.tgl(), lepton1.signed1Pt()}; + // std::array lepton1covs = {lepton1.cYY(), lepton1.cZY(), lepton1.cZZ(), lepton1.cSnpY(), lepton1.cSnpZ(), + // lepton1.cSnpSnp(), lepton1.cTglY(), lepton1.cTglZ(), lepton1.cTglSnp(), lepton1.cTglTgl(), + // lepton1.c1PtY(), lepton1.c1PtZ(), lepton1.c1PtSnp(), lepton1.c1PtTgl(), lepton1.c1Pt21Pt2()}; + // o2::track::TrackParCov pars1{lepton1.x(), lepton1.alpha(), lepton1pars, lepton1covs}; + // std::array lepton2pars = {lepton2.y(), lepton2.z(), lepton2.snp(), lepton2.tgl(), lepton2.signed1Pt()}; + // std::array lepton2covs = {lepton2.cYY(), lepton2.cZY(), lepton2.cZZ(), lepton2.cSnpY(), lepton2.cSnpZ(), + // lepton2.cSnpSnp(), lepton2.cTglY(), lepton2.cTglZ(), lepton2.cTglSnp(), lepton2.cTglTgl(), + // lepton2.c1PtY(), lepton2.c1PtZ(), lepton2.c1PtSnp(), lepton2.c1PtTgl(), lepton2.c1Pt21Pt2()}; + // o2::track::TrackParCov pars2{lepton2.x(), lepton2.alpha(), lepton2pars, lepton2covs}; + // std::array track1pars = {track1.y(), track1.z(), track1.snp(), track1.tgl(), track1.signed1Pt()}; + // std::array track1covs = {track1.cYY(), track1.cZY(), track1.cZZ(), track1.cSnpY(), track1.cSnpZ(), + // track1.cSnpSnp(), track1.cTglY(), track1.cTglZ(), track1.cTglSnp(), track1.cTglTgl(), + // track1.c1PtY(), track1.c1PtZ(), track1.c1PtSnp(), track1.c1PtTgl(), track1.c1Pt21Pt2()}; + // o2::track::TrackParCov pars3{track1.x(), track1.alpha(), track1pars, track1covs}; + // std::array track2pars = {track2.y(), track2.z(), track2.snp(), track2.tgl(), track2.signed1Pt()}; + // std::array track2covs = {track2.cYY(), track2.cZY(), track2.cZZ(), track2.cSnpY(), track2.cSnpZ(), + // track2.cSnpSnp(), track2.cTglY(), track2.cTglZ(), track2.cTglSnp(), track2.cTglTgl(), + // track2.c1PtY(), track2.c1PtZ(), track2.c1PtSnp(), track2.c1PtTgl(), track2.c1Pt21Pt2()}; + // o2::track::TrackParCov pars4{track2.x(), track2.alpha(), track2pars, track2covs}; procCodeDilepton = VarManager::fgFitterTwoProngBarrel.process(pars1, pars2); // create dilepton track // o2::track::TrackParCov parsDilepton = VarManager::fgFitterTwoProngBarrel.createParentTrackParCov(0); // procCodeDileptonTrackTrack = VarManager::fgFitterThreeProngBarrel.process(parsDilepton, pars3, pars4); procCodeDileptonTrackTrack = VarManager::fgFitterFourProngBarrel.process(pars1, pars2, pars3, pars4); + values[kVertexingProcCode] = procCodeDilepton; + values[kVertexingQuadProcCode] = procCodeDileptonTrackTrack; // fill values - if (procCodeDilepton == 0 && procCodeDileptonTrackTrack == 0) { + if (procCodeDilepton == 0 || procCodeDileptonTrackTrack == 0) { // TODO: set the other variables to appropriate values and return values[kVertexingLxy] = -999.; values[kVertexingLxyz] = -999.; @@ -6199,57 +7031,58 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kVertexingTauxyProjected] = -999.; values[kVertexingTauxyzProjected] = -999.; return; - } else { - Vec3D secondaryVertex; - std::array covMatrixPCA; - secondaryVertex = fgFitterFourProngBarrel.getPCACandidate(); - covMatrixPCA = fgFitterFourProngBarrel.calcPCACovMatrixFlat(); - - o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); - std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; - o2::dataformats::VertexBase primaryVertex = {std::move(vtxXYZ), std::move(vtxCov)}; - auto covMatrixPV = primaryVertex.getCov(); - - double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); - double theta = std::atan2(secondaryVertex[2] - collision.posZ(), - std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + - (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); - - values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + - (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); - values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); - values[kVertexingLxyz] = values[kVertexingLxy] + values[kVertexingLz]; - values[kVertexingLxy] = std::sqrt(values[kVertexingLxy]); - values[kVertexingLz] = std::sqrt(values[kVertexingLz]); - values[kVertexingLxyz] = std::sqrt(values[kVertexingLxyz]); - - values[kVertexingLxyzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); - values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); - values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); + } - values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * v1234.M() / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxy] = values[kVertexingLxy] * v1234.M() / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); - - values[kVertexingTauzErr] = values[kVertexingLzErr] * v1234.M() / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); - values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v1234.M() / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); - - values[kCosPointingAngle] = ((collision.posX() - secondaryVertex[0]) * v1234.Px() + - (collision.posY() - secondaryVertex[1]) * v1234.Py() + - (collision.posZ() - secondaryVertex[2]) * v1234.Pz()) / - (v1234.P() * values[VarManager::kVertexingLxyz]); - // // run 2 definitions: Decay length projected onto the momentum vector of the candidate - values[kVertexingLzProjected] = (secondaryVertex[2] - collision.posZ()) * v1234.Pz(); - values[kVertexingLzProjected] = values[kVertexingLzProjected] / TMath::Sqrt(v1234.Pz() * v1234.Pz()); - values[kVertexingLxyProjected] = ((secondaryVertex[0] - collision.posX()) * v1234.Px()) + ((secondaryVertex[1] - collision.posY()) * v1234.Py()); - values[kVertexingLxyProjected] = values[kVertexingLxyProjected] / TMath::Sqrt((v1234.Px() * v1234.Px()) + (v1234.Py() * v1234.Py())); - values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v1234.Px()) + ((secondaryVertex[1] - collision.posY()) * v1234.Py()) + ((secondaryVertex[2] - collision.posZ()) * v1234.Pz()); - values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v1234.Px() * v1234.Px()) + (v1234.Py() * v1234.Py()) + (v1234.Pz() * v1234.Pz())); - - values[kVertexingTauzProjected] = values[kVertexingLzProjected] * v1234.M() / TMath::Abs(v1234.Pz()); - values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * v1234.M() / (v1234.Pt()); - values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * v1234.M() / (v1234.P()); - } - } else if (fgUsedKF) { + Vec3D secondaryVertex; + std::array covMatrixPCA{}; + secondaryVertex = fgFitterFourProngBarrel.getPCACandidate(); + covMatrixPCA = fgFitterFourProngBarrel.calcPCACovMatrixFlat(); + + o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); + std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; + o2::dataformats::VertexBase primaryVertex = {vtxXYZ, vtxCov}; + auto covMatrixPV = primaryVertex.getCov(); + + double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); + double theta = std::atan2(secondaryVertex[2] - collision.posZ(), + std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + + (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); + + values[kVertexingChi2PCA] = fgFitterFourProngBarrel.getChi2AtPCACandidate(); + values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + + (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); + values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); + values[kVertexingLxyz] = values[kVertexingLxy] + values[kVertexingLz]; + values[kVertexingLxy] = std::sqrt(values[kVertexingLxy]); + values[kVertexingLz] = std::sqrt(values[kVertexingLz]); + values[kVertexingLxyz] = std::sqrt(values[kVertexingLxyz]); + + values[kVertexingLxyzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); + values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); + values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); + + values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * mMother / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxy] = values[kVertexingLxy] * mMother / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); + + values[kVertexingTauzErr] = values[kVertexingLzErr] * mMother / (TMath::Abs(v1234.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxyErr] = values[kVertexingLxyErr] * mMother / (v1234.Pt() * o2::constants::physics::LightSpeedCm2NS); + + values[kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v1234.Px() + + (secondaryVertex[1] - collision.posY()) * v1234.Py() + + (secondaryVertex[2] - collision.posZ()) * v1234.Pz()) / + (v1234.P() * values[VarManager::kVertexingLxyz]); + // // run 2 definitions: Decay length projected onto the momentum vector of the candidate + values[kVertexingLzProjected] = (secondaryVertex[2] - collision.posZ()) * v1234.Pz(); + values[kVertexingLzProjected] = values[kVertexingLzProjected] / TMath::Sqrt(v1234.Pz() * v1234.Pz()); + values[kVertexingLxyProjected] = ((secondaryVertex[0] - collision.posX()) * v1234.Px()) + ((secondaryVertex[1] - collision.posY()) * v1234.Py()); + values[kVertexingLxyProjected] = values[kVertexingLxyProjected] / TMath::Sqrt((v1234.Px() * v1234.Px()) + (v1234.Py() * v1234.Py())); + values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v1234.Px()) + ((secondaryVertex[1] - collision.posY()) * v1234.Py()) + ((secondaryVertex[2] - collision.posZ()) * v1234.Pz()); + values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v1234.Px() * v1234.Px()) + (v1234.Py() * v1234.Py()) + (v1234.Pz() * v1234.Pz())); + + values[kVertexingTauzProjected] = values[kVertexingLzProjected] * mMother / TMath::Abs(v1234.Pz()); + values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * mMother / (v1234.Pt()); + values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * mMother / (v1234.P()); + } else { KFParticle lepton1KF; // lepton1 KFParticle lepton2KF; // lepton2 KFParticle KFGeoTwoLeptons; @@ -6317,10 +7150,11 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l if (fgUsedVars[kKFMass]) { float mass = 0., massErr = 0.; - if (!KFGeoFourProng.GetMass(mass, massErr)) + if (!KFGeoFourProng.GetMass(mass, massErr)) { values[kKFMass] = mass; - else + } else { values[kKFMass] = -999.; + } } KFPVertex kfpVertex = createKFPVertexFromCollision(collision); @@ -6338,14 +7172,17 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kVertexingLzErr] = (KFPV.GetCovariance(5) + KFGeoFourProng.GetCovariance(5)) * dzQuadlet2PV * dzQuadlet2PV; values[kVertexingLxyzErr] = (KFPV.GetCovariance(0) + KFGeoFourProng.GetCovariance(0)) * dxQuadlet2PV * dxQuadlet2PV + (KFPV.GetCovariance(2) + KFGeoFourProng.GetCovariance(2)) * dyQuadlet2PV * dyQuadlet2PV + (KFPV.GetCovariance(5) + KFGeoFourProng.GetCovariance(5)) * dzQuadlet2PV * dzQuadlet2PV + 2 * ((KFPV.GetCovariance(1) + KFGeoFourProng.GetCovariance(1)) * dxQuadlet2PV * dyQuadlet2PV + (KFPV.GetCovariance(3) + KFGeoFourProng.GetCovariance(3)) * dxQuadlet2PV * dzQuadlet2PV + (KFPV.GetCovariance(4) + KFGeoFourProng.GetCovariance(4)) * dyQuadlet2PV * dzQuadlet2PV); - if (fabs(values[kVertexingLxy]) < 1.e-8f) + if (std::fabs(values[kVertexingLxy]) < 1.e-8f) { values[kVertexingLxy] = 1.e-8f; + } values[kVertexingLxyErr] = values[kVertexingLxyErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyErr]) / values[kVertexingLxy]; - if (fabs(values[kVertexingLz]) < 1.e-8f) + if (std::fabs(values[kVertexingLz]) < 1.e-8f) { values[kVertexingLz] = 1.e-8f; + } values[kVertexingLzErr] = values[kVertexingLzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLzErr]) / values[kVertexingLz]; - if (fabs(values[kVertexingLxyz]) < 1.e-8f) + if (std::fabs(values[kVertexingLxyz]) < 1.e-8f) { values[kVertexingLxyz] = 1.e-8f; + } values[kVertexingLxyzErr] = values[kVertexingLxyzErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyzErr]) / values[kVertexingLxyz]; values[kVertexingTauxy] = KFGeoFourProng.GetPseudoProperDecayTime(KFPV, KFGeoFourProng.GetMass()) / (o2::constants::physics::LightSpeedCm2NS); @@ -6370,8 +7207,6 @@ void VarManager::FillDileptonTrackTrackVertexing(C const& collision, T1 const& l values[kVertexingTauxyProjectedNs] = values[kVertexingTauxyProjected] / o2::constants::physics::LightSpeedCm2NS; values[kVertexingTauzProjected] = values[kVertexingLzProjected] * KFGeoFourProng.GetMass() / TMath::Abs(KFGeoFourProng.GetPz()); values[kKFChi2OverNDFGeo] = KFGeoFourProng.GetChi2() / KFGeoFourProng.GetNDF(); - } else { - return; } } @@ -6380,14 +7215,14 @@ template void VarManager::FillQuadMC(T1 const& dilepton, T2 const& track1, T2 const& track2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } - double defaultDileptonMass = 3.096; + double defaultDileptonMass = o2::constants::physics::MassJPsi; double hadronMass1 = o2::constants::physics::MassPionCharged; double hadronMass2 = o2::constants::physics::MassPionCharged; - if (candidateType == kXtoJpsiPiPi) { - defaultDileptonMass = 3.096; + if (candidateType == kXtoJpsiPiPi || candidateType == kPsi2StoJpsiPiPi) { + defaultDileptonMass = o2::constants::physics::MassJPsi; hadronMass1 = o2::constants::physics::MassPionCharged; hadronMass2 = o2::constants::physics::MassPionCharged; } @@ -6405,7 +7240,7 @@ void VarManager::FillQuadMC(T1 const& dilepton, T2 const& track1, T2 const& trac values[kQ] = v123.M() - defaultDileptonMass - v23.M(); values[kDeltaR1] = ROOT::Math::VectorUtil::DeltaR(v1, v2); values[kDeltaR2] = ROOT::Math::VectorUtil::DeltaR(v1, v3); - values[kDeltaR] = sqrt(pow(values[kDeltaR1], 2) + pow(values[kDeltaR2], 2)); + values[kDeltaR] = std::sqrt(std::pow(values[kDeltaR1], 2) + std::pow(values[kDeltaR2], 2)); values[kDitrackMass] = v23.M(); values[kDitrackPt] = v23.Pt(); } @@ -6531,7 +7366,7 @@ template void VarManager::FillBdtScore(T1 const& bdtScore, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if (bdtScore.size() == 1) { @@ -6546,7 +7381,7 @@ void VarManager::FillBdtScore(T1 const& bdtScore, float* values) } //__________________________________________________________________ template -float VarManager::LorentzTransformJpsihadroncosChi(TString Option, T1 const& v1, T2 const& v2) +float VarManager::LorentzTransformJpsihadroncosChi(const TString& Option, T1 const& v1, T2 const& v2) { float value = -999.0f; auto beta_v1 = v1.BoostToCM(); @@ -6567,11 +7402,12 @@ float VarManager::LorentzTransformJpsihadroncosChi(TString Option, T1 const& v1, } return value; } + template void VarManager::FillFIT(T1 const& bc, T2 const& bcs, T3 const& ft0s, T4 const& fv0as, T5 const& fdds, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } // Initialize FIT info structure @@ -6581,9 +7417,12 @@ void VarManager::FillFIT(T1 const& bc, T2 const& bcs, T3 const& ft0s, T4 const& // Fill FT0 information values[kAmplitudeFT0A] = fitInfo.ampFT0A; values[kAmplitudeFT0C] = fitInfo.ampFT0C; + values[kAmplitudeFT0M] = values[kAmplitudeFT0A] + values[kAmplitudeFT0C]; values[kTimeFT0A] = fitInfo.timeFT0A; values[kTimeFT0C] = fitInfo.timeFT0C; values[kTriggerMaskFT0] = static_cast(fitInfo.triggerMaskFT0); + values[kFT0OrA] = TESTBIT(fitInfo.triggerMaskFT0, o2::fit::Triggers::bitA); + values[kFT0OrC] = TESTBIT(fitInfo.triggerMaskFT0, o2::fit::Triggers::bitC); const auto ft0Index = bc.ft0Id(); if (ft0Index < 0 || ft0Index >= ft0s.size()) { values[kNFiredChannelsFT0A] = -1; @@ -6628,7 +7467,7 @@ template void VarManager::FillEventAlice3(T const& event, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & CollisionTimestamp) > 0) { values[kTimestamp] = event.timestamp(); @@ -6648,6 +7487,12 @@ void VarManager::FillEventAlice3(T const& event, float* values) values[kVtxChi2] = event.chi2(); values[kCollisionTime] = event.collisionTime(); values[kCollisionTimeRes] = event.collisionTimeRes(); + values[kCent] = event.centRun2V0M(); + values[kMultPV] = event.multNTracksPV(); + values[kMultPVeta1] = event.multNTracksPVeta1(); + values[kMultPVetaHalf] = event.multNTracksPVetaHalf(); + values[kMultGlobalTracks] = event.multNTracksGlobal(); + values[kMultGlobalTracksPV] = event.multNGlobalTracksPV(); } if constexpr ((fillMap & ReducedEvent) > 0) { @@ -6658,6 +7503,12 @@ void VarManager::FillEventAlice3(T const& event, float* values) values[kVtxNcontrib] = event.numContrib(); values[kCollisionTime] = event.collisionTime(); values[kCollisionTimeRes] = event.collisionTimeRes(); + values[kCent] = event.centRun2V0M(); + values[kMultPV] = event.multNTracksPV(); + values[kMultPVeta1] = event.multNTracksPVeta1(); + values[kMultPVetaHalf] = event.multNTracksPVetaHalf(); + values[kMultGlobalTracks] = event.multNTracksGlobal(); + values[kMultGlobalTracksPV] = event.multNGlobalTracksPV(); } if constexpr ((fillMap & ReducedEventVtxCov) > 0) { values[kVtxCovXX] = event.covXX(); @@ -6678,6 +7529,10 @@ void VarManager::FillEventAlice3(T const& event, float* values) values[kMCEventTime] = event.t(); values[kMCEventWeight] = event.weight(); values[kMCEventImpParam] = event.impactParameter(); + values[kMultMCNParticlesAll] = event.multMC(); + values[kMultMCNParticlesEta25] = event.multMC25(); + values[kMultMCNParticlesEta125] = event.multMC125(); + values[kMultMCNParticlesEta09] = event.multMC09(); } if constexpr ((fillMap & ReducedEventMC) > 0) { @@ -6689,6 +7544,10 @@ void VarManager::FillEventAlice3(T const& event, float* values) values[kMCEventTime] = event.t(); values[kMCEventWeight] = event.weight(); values[kMCEventImpParam] = event.impactParameter(); + values[kMultMCNParticlesAll] = event.multMC(); + values[kMultMCNParticlesEta25] = event.multMC25(); + values[kMultMCNParticlesEta125] = event.multMC125(); + values[kMultMCNParticlesEta09] = event.multMC09(); } } @@ -6696,7 +7555,7 @@ template void VarManager::FillTrackAlice3(T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } if constexpr ((fillMap & Track) > 0 || (fillMap & ReducedTrack) > 0) { @@ -6722,7 +7581,7 @@ void VarManager::FillTrackAlice3(T const& track, float* values) values[kCharge] = track.sign(); if (fgUsedVars[kPVContributor]) { - values[kPVContributor] = (track.flags() & o2::aod::track::PVContributor) > 0; + values[kPVContributor] = static_cast((track.flags() & o2::aod::track::PVContributor) > 0); } if (fgUsedVars[kITSClusterMap]) { @@ -6849,7 +7708,7 @@ template void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } float m1 = o2::constants::physics::MassElectron; @@ -6869,10 +7728,6 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) m2 = o2::constants::physics::MassPionCharged; } - if constexpr (pairType == kElectronMuon) { - m2 = o2::constants::physics::MassMuon; - } - values[kCharge] = t1.sign() + t2.sign(); values[kCharge1] = t1.sign(); values[kCharge2] = t2.sign(); @@ -6882,8 +7737,7 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) values[kMass] = v12.M(); values[kPt] = v12.Pt(); values[kEta] = v12.Eta(); - // values[kPhi] = v12.Phi(); - values[kPhi] = v12.Phi() > 0 ? v12.Phi() : v12.Phi() + 2. * M_PI; + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); values[kRap] = -v12.Rapidity(); double Ptot1 = TMath::Sqrt(v1.Px() * v1.Px() + v1.Py() * v1.Py() + v1.Pz() * v1.Pz()); double Ptot2 = TMath::Sqrt(v2.Px() * v2.Px() + v2.Py() * v2.Py() + v2.Pz() * v2.Pz()); @@ -6897,14 +7751,7 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) values[kPhi2] = t2.phi(); if (fgUsedVars[kDeltaPhiPair2]) { - double phipair2 = v1.Phi() - v2.Phi(); - if (phipair2 > 3 * TMath::Pi() / 2) { - values[kDeltaPhiPair2] = phipair2 - 2 * TMath::Pi(); - } else if (phipair2 < -TMath::Pi() / 2) { - values[kDeltaPhiPair2] = phipair2 + 2 * TMath::Pi(); - } else { - values[kDeltaPhiPair2] = phipair2; - } + values[kDeltaPhiPair2] = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PIHalf); } if (fgUsedVars[kDeltaEtaPair2]) { @@ -6924,21 +7771,25 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) values[kOpeningAngle] = 0.; } else { double arg = scalar / Ptot12; - if (arg > 1.) + if (arg > 1.) { arg = 1.; - if (arg < -1) + } + if (arg < -1) { arg = -1; + } values[kOpeningAngle] = TMath::ACos(arg); } } // polarization parameters - bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame - bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame - bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame - bool useRM = fgUsedVars[kCosThetaRM]; // Random frame - - if (useHE || useCS || usePP || useRM) { + bool useHE = fgUsedVars[kCosThetaHE] || fgUsedVars[kPhiHE]; // helicity frame + bool useCS = fgUsedVars[kCosThetaCS] || fgUsedVars[kPhiCS]; // Collins-Soper frame + bool useGJB1 = fgUsedVars[kCosThetaGJB1] || fgUsedVars[kPhiGJB1]; // Gottfried-Jackson frame, beam 1 (+z) + bool useGJB2 = fgUsedVars[kCosThetaGJB2] || fgUsedVars[kPhiGJB2]; // Gottfried-Jackson frame, beam 2 (-z) + bool usePP = fgUsedVars[kCosThetaPP]; // production plane frame + bool useRM = fgUsedVars[kCosThetaRM]; // Random frame + + if (useHE || useCS || useGJB1 || useGJB2 || usePP || useRM) { ROOT::Math::Boost boostv12{v12.BoostToCM()}; ROOT::Math::XYZVectorF v1_CM{(boostv12(v1).Vect()).Unit()}; ROOT::Math::XYZVectorF v2_CM{(boostv12(v2).Vect()).Unit()}; @@ -6952,26 +7803,18 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_HE{(v12.Vect()).Unit()}; ROOT::Math::XYZVectorF yaxis_HE{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_HE{(yaxis_HE.Cross(zaxis_HE)).Unit()}; - if (fgUsedVars[kCosThetaHE]) + if (fgUsedVars[kCosThetaHE]) { values[kCosThetaHE] = zaxis_HE.Dot(v_CM); + } if (fgUsedVars[kPhiHE]) { - values[kPhiHE] = TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM)); - if (values[kPhiHE] < 0) { - values[kPhiHE] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiHE] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_HE.Dot(v_CM), xaxis_HE.Dot(v_CM))); } if (fgUsedVars[kPhiTildeHE]) { if (fgUsedVars[kCosThetaHE] && fgUsedVars[kPhiHE]) { if (values[kCosThetaHE] > 0) { - values[kPhiTildeHE] = values[kPhiHE] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildeHE] < 0) { - values[kPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeHE] = RecoDecay::constrainAngle(values[kPhiHE] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildeHE] = values[kPhiHE] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildeHE] < 0) { - values[kPhiTildeHE] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeHE] = RecoDecay::constrainAngle(values[kPhiHE] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildeHE] = -999; // not computable @@ -6983,26 +7826,18 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) ROOT::Math::XYZVectorF zaxis_CS{(Beam1_CM - Beam2_CM).Unit()}; ROOT::Math::XYZVectorF yaxis_CS{(Beam1_CM.Cross(Beam2_CM)).Unit()}; ROOT::Math::XYZVectorF xaxis_CS{(yaxis_CS.Cross(zaxis_CS)).Unit()}; - if (fgUsedVars[kCosThetaCS]) + if (fgUsedVars[kCosThetaCS]) { values[kCosThetaCS] = zaxis_CS.Dot(v_CM); + } if (fgUsedVars[kPhiCS]) { - values[kPhiCS] = TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM)); - if (values[kPhiCS] < 0) { - values[kPhiCS] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiCS] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_CS.Dot(v_CM), xaxis_CS.Dot(v_CM))); } if (fgUsedVars[kPhiTildeCS]) { if (fgUsedVars[kCosThetaCS] && fgUsedVars[kPhiCS]) { if (values[kCosThetaCS] > 0) { - values[kPhiTildeCS] = values[kPhiCS] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildeCS] < 0) { - values[kPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeCS] = RecoDecay::constrainAngle(values[kPhiCS] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildeCS] = values[kPhiCS] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildeCS] < 0) { - values[kPhiTildeCS] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildeCS] = RecoDecay::constrainAngle(values[kPhiCS] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildeCS] = -999; // not computable @@ -7010,6 +7845,70 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) } } + if (useGJB1) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB1{Beam1_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB1{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB1{zaxis_GJB1.Cross(motherLab_GJB1)}; + if (yaxis_GJB1.Mag2() > 1.e-6f) { + yaxis_GJB1 = yaxis_GJB1.Unit(); + } else { + yaxis_GJB1 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB1{(yaxis_GJB1.Cross(zaxis_GJB1)).Unit()}; + if (fgUsedVars[kCosThetaGJB1]) { + values[kCosThetaGJB1] = zaxis_GJB1.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB1]) { + values[kPhiGJB1] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB1.Dot(v_CM), xaxis_GJB1.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB1]) { + if (fgUsedVars[kCosThetaGJB1] && fgUsedVars[kPhiGJB1]) { + if (values[kCosThetaGJB1] > 0) { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB1] = RecoDecay::constrainAngle(values[kPhiGJB1] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB1] = -999; // not computable + } + } + } + + if (useGJB2) { + // z-axis: beam direction in the mother rest frame + ROOT::Math::XYZVectorF zaxis_GJB2{Beam2_CM.Unit()}; + // mother momentum in LAB (defines the production plane) + ROOT::Math::XYZVectorF motherLab_GJB2{(ROOT::Math::XYZVectorF(v12.Vect())).Unit()}; + // y-axis: normal to the production plane (fallback to (0,1,0) if degenerate) + ROOT::Math::XYZVectorF yaxis_GJB2{zaxis_GJB2.Cross(motherLab_GJB2)}; + if (yaxis_GJB2.Mag2() > 1.e-6f) { + yaxis_GJB2 = yaxis_GJB2.Unit(); + } else { + yaxis_GJB2 = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + } + ROOT::Math::XYZVectorF xaxis_GJB2{(yaxis_GJB2.Cross(zaxis_GJB2)).Unit()}; + if (fgUsedVars[kCosThetaGJB2]) { + values[kCosThetaGJB2] = zaxis_GJB2.Dot(v_CM); + } + if (fgUsedVars[kPhiGJB2]) { + values[kPhiGJB2] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_GJB2.Dot(v_CM), xaxis_GJB2.Dot(v_CM))); + } + if (fgUsedVars[kPhiTildeGJB2]) { + if (fgUsedVars[kCosThetaGJB2] && fgUsedVars[kPhiGJB2]) { + if (values[kCosThetaGJB2] > 0) { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 + } else { + values[kPhiTildeGJB2] = RecoDecay::constrainAngle(values[kPhiGJB2] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 + } + } else { + values[kPhiTildeGJB2] = -999; // not computable + } + } + } + if (usePP) { ROOT::Math::XYZVector zaxis_PP = ROOT::Math::XYZVector(v12.Py(), -v12.Px(), 0.f); ROOT::Math::XYZVector yaxis_PP{(v12.Vect()).Unit()}; @@ -7018,23 +7917,14 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) values[kCosThetaPP] = zaxis_PP.Dot(v_CM) / std::sqrt(zaxis_PP.Mag2()); } if (fgUsedVars[kPhiPP]) { - values[kPhiPP] = TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM)); - if (values[kPhiPP] < 0) { - values[kPhiPP] += 2 * TMath::Pi(); // ensure phi is in [0, 2pi] - } + values[kPhiPP] = RecoDecay::constrainAngle(TMath::ATan2(yaxis_PP.Dot(v_CM), xaxis_PP.Dot(v_CM))); } if (fgUsedVars[kPhiTildePP]) { if (fgUsedVars[kCosThetaPP] && fgUsedVars[kPhiPP]) { if (values[kCosThetaPP] > 0) { - values[kPhiTildePP] = values[kPhiPP] - 0.25 * TMath::Pi(); // phi_tilde = phi - pi/4 - if (values[kPhiTildePP] < 0) { - values[kPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildePP] = RecoDecay::constrainAngle(values[kPhiPP] - o2::constants::math::PIQuarter); // phi_tilde = phi - pi/4 } else { - values[kPhiTildePP] = values[kPhiPP] - 0.75 * TMath::Pi(); // phi_tilde = phi - 3pi/4 - if (values[kPhiTildePP] < 0) { - values[kPhiTildePP] += 2 * TMath::Pi(); // ensure phi_tilde is in [0, 2pi] - } + values[kPhiTildePP] = RecoDecay::constrainAngle(values[kPhiPP] - 3. * o2::constants::math::PIQuarter); // phi_tilde = phi - 3pi/4 } } else { values[kPhiTildePP] = -999; // not computable @@ -7044,10 +7934,11 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) if (useRM) { double randomCostheta = gRandom->Uniform(-1., 1.); - double randomPhi = gRandom->Uniform(0., 2. * TMath::Pi()); + double randomPhi = gRandom->Uniform(0., o2::constants::math::TwoPI); ROOT::Math::XYZVectorF zaxis_RM(randomCostheta, std::sqrt(1 - randomCostheta * randomCostheta) * std::cos(randomPhi), std::sqrt(1 - randomCostheta * randomCostheta) * std::sin(randomPhi)); - if (fgUsedVars[kCosThetaRM]) + if (fgUsedVars[kCosThetaRM]) { values[kCosThetaRM] = zaxis_RM.Dot(v_CM); + } } } @@ -7085,27 +7976,24 @@ void VarManager::FillPairAlice3(T1 const& t1, T2 const& t2, float* values) } } } - if constexpr ((pairType == kDecayToMuMu) && ((fillMap & Muon) > 0 || (fillMap & ReducedMuon) > 0)) { - if (fgUsedVars[kQuadDCAabsXY]) { - double dca1X = t1.fwdDcaX(); - double dca1Y = t1.fwdDcaY(); - double dca1XY = std::sqrt(dca1X * dca1X + dca1Y * dca1Y); - double dca2X = t2.fwdDcaX(); - double dca2Y = t2.fwdDcaY(); - double dca2XY = std::sqrt(dca2X * dca2X + dca2Y * dca2Y); - values[kQuadDCAabsXY] = std::sqrt((dca1XY * dca1XY + dca2XY * dca2XY) / 2.); - } - } if (fgUsedVars[kPairPhiv]) { values[kPairPhiv] = calculatePhiV(t1, t2); } + + if constexpr (pairType == kDecayToKPi) { + if constexpr ((fillMap & TrackDCA) > 0 || (fillMap & ReducedTrackBarrel) > 0) { + values[kDCAxy1] = t1.dcaXY(); + values[kDCAxy2] = t2.dcaXY(); + values[kD0ImpParProduct] = t1.dcaXY() * t2.dcaXY(); + } + } } template void VarManager::FillResolutions(M const& mcTrack, T const& track, float* values) { if (!values) { - values = fgValues; + values = static_cast(fgValues); } values[kDeltaPt] = track.pt() - mcTrack.pt(); @@ -7114,4 +8002,308 @@ void VarManager::FillResolutions(M const& mcTrack, T const& track, float* values values[kEtaResolution] = track.eta() - mcTrack.eta(); } +template +void VarManager::FillPairVertexingAlice3(C const& collision, T const& t1, T const& t2, bool propToSV, float* values) +{ + // check at compile time that the event and cov matrix have the cov matrix + constexpr bool eventHasVtxCov = ((collFillMap & Collision) > 0 || (collFillMap & ReducedEventVtxCov) > 0); + constexpr bool trackHasCov = ((fillMap & TrackCov) > 0 || (fillMap & ReducedTrackBarrelCov) > 0); + + if (!values) { + values = static_cast(fgValues); + } + float m1 = o2::constants::physics::MassElectron; + float m2 = o2::constants::physics::MassElectron; + if constexpr (pairType == kDecayToKPi) { + m1 = o2::constants::physics::MassKaonCharged; + m2 = o2::constants::physics::MassPionCharged; + } + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), m1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), m2); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + + values[kUsedKF] = static_cast(fgUsedKF); + if (!fgUsedKF) { + int procCode = 0; + + // TODO: use trackUtilities functions to initialize the various matrices to avoid code duplication + // auto pars1 = getTrackParCov(t1); + // auto pars2 = getTrackParCov(t2); + // We need to hide the cov data members from the cases when no cov table is provided + if constexpr ((pairType == kDecayToEE || pairType == kDecayToKPi) && trackHasCov) { + std::array t1pars = {t1.y(), t1.z(), t1.snp(), t1.tgl(), t1.signed1Pt()}; + std::array t1covs = {t1.cYY(), t1.cZY(), t1.cZZ(), t1.cSnpY(), t1.cSnpZ(), + t1.cSnpSnp(), t1.cTglY(), t1.cTglZ(), t1.cTglSnp(), t1.cTglTgl(), + t1.c1PtY(), t1.c1PtZ(), t1.c1PtSnp(), t1.c1PtTgl(), t1.c1Pt21Pt2()}; + o2::track::TrackParCov pars1{t1.x(), t1.alpha(), t1pars, t1covs}; + std::array t2pars = {t2.y(), t2.z(), t2.snp(), t2.tgl(), t2.signed1Pt()}; + std::array t2covs = {t2.cYY(), t2.cZY(), t2.cZZ(), t2.cSnpY(), t2.cSnpZ(), + t2.cSnpSnp(), t2.cTglY(), t2.cTglZ(), t2.cTglSnp(), t2.cTglTgl(), + t2.c1PtY(), t2.c1PtZ(), t2.c1PtSnp(), t2.c1PtTgl(), t2.c1Pt21Pt2()}; + o2::track::TrackParCov pars2{t2.x(), t2.alpha(), t2pars, t2covs}; + procCode = fgFitterTwoProngBarrel.process(pars1, pars2); + } else { + return; + } + + values[kVertexingProcCode] = procCode; + if (procCode == 0) { + // TODO: set the other variables to appropriate values and return + values[kVertexingChi2PCA] = -999.; + values[kVertexingLxy] = -999.; + values[kVertexingLxyz] = -999.; + values[kVertexingLz] = -999.; + values[kVertexingLxyErr] = -999.; + values[kVertexingLxyzErr] = -999.; + values[kVertexingLzErr] = -999.; + + values[kVertexingTauxy] = -999.; + values[kVertexingTauz] = -999.; + values[kVertexingTauxyErr] = -999.; + values[kVertexingTauzErr] = -999.; + values[kVertexingPz] = -999.; + values[kVertexingSV] = -999.; + return; + } + + Vec3D secondaryVertex; + o2::dataformats::VertexBase primaryVertexNew; + + if constexpr (eventHasVtxCov) { + + std::array covMatrixPCA{}; + // get track impact parameters + // This modifies track momenta! + o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); + std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; + o2::dataformats::VertexBase primaryVertex = {vtxXYZ, vtxCov}; + // auto primaryVertex = getPrimaryVertex(collision); + auto covMatrixPV = primaryVertex.getCov(); + + if constexpr ((pairType == kDecayToEE || pairType == kDecayToKPi) && trackHasCov) { + secondaryVertex = fgFitterTwoProngBarrel.getPCACandidate(); + // printf("secVtx (first) %f %f %f \n",secondaryVertex[0],secondaryVertex[1],secondaryVertex[2]); + covMatrixPCA = fgFitterTwoProngBarrel.calcPCACovMatrixFlat(); + auto chi2PCA = fgFitterTwoProngBarrel.getChi2AtPCACandidate(); + auto trackParVar0 = fgFitterTwoProngBarrel.getTrack(0); + auto trackParVar1 = fgFitterTwoProngBarrel.getTrack(1); + values[kVertexingChi2PCA] = chi2PCA; + v1 = {trackParVar0.getPt(), trackParVar0.getEta(), trackParVar0.getPhi(), m1}; + v2 = {trackParVar1.getPt(), trackParVar1.getEta(), trackParVar1.getPhi(), m2}; + v12 = v1 + v2; + + const auto pos1 = trackParVar0.getXYZGlo(); + const auto pos2 = trackParVar1.getXYZGlo(); + + const double dx = pos1.X() - pos2.X(); + const double dy = pos1.Y() - pos2.Y(); + const double dz = pos1.Z() - pos2.Z(); + + values[kD0DcaDaughters] = std::sqrt(dx * dx + dy * dy + dz * dz); + } + + double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); + double theta = std::atan2(secondaryVertex[2] - collision.posZ(), + std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + + (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); + + values[kVertexingLxyzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); + values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); + values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); + + values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + + (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); + values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); + values[kVertexingLxyz] = values[kVertexingLxy] + values[kVertexingLz]; + values[kVertexingLxy] = std::sqrt(values[kVertexingLxy]); + values[kVertexingLz] = std::sqrt(values[kVertexingLz]); + values[kVertexingLxyz] = std::sqrt(values[kVertexingLxyz]); + + values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxy] = values[kVertexingLxy] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); + + values[kVertexingPz] = TMath::Abs(v12.Pz()); + values[kVertexingSV] = secondaryVertex[2]; + + values[kVertexingTauzErr] = values[kVertexingLzErr] * v12.M() / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); + + values[kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v12.Px() + + (secondaryVertex[1] - collision.posY()) * v12.Py() + + (secondaryVertex[2] - collision.posZ()) * v12.Pz()) / + (v12.P() * values[VarManager::kVertexingLxyz]); + // Decay length defined as in Run 2 + values[kVertexingLzProjected] = ((secondaryVertex[2] - collision.posZ()) * v12.Pz()) / TMath::Sqrt(v12.Pz() * v12.Pz()); + values[kVertexingLxyProjected] = ((secondaryVertex[0] - collision.posX()) * v12.Px()) + ((secondaryVertex[1] - collision.posY()) * v12.Py()); + values[kVertexingLxyProjected] = values[kVertexingLxyProjected] / TMath::Sqrt((v12.Px() * v12.Px()) + (v12.Py() * v12.Py())); + values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v12.Px()) + ((secondaryVertex[1] - collision.posY()) * v12.Py()) + ((secondaryVertex[2] - collision.posZ()) * v12.Pz()); + values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v12.Px() * v12.Px()) + (v12.Py() * v12.Py()) + (v12.Pz() * v12.Pz())); + if (fgPVrecalKF) { + values[kVertexingLxyProjectedRecalculatePV] = (secondaryVertex[0] - primaryVertexNew.getX()) * v12.Px() + (secondaryVertex[1] - primaryVertexNew.getY()) * v12.Py(); + values[kVertexingLxyProjectedRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] / v12.Pt(); + } + values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * v12.M() / (v12.Pt()); + values[kVertexingTauxyProjectedPoleJPsiMass] = values[kVertexingLxyProjected] * o2::constants::physics::MassJPsi / (v12.Pt()); + values[kVertexingTauxyProjectedNs] = values[kVertexingTauxyProjected] / o2::constants::physics::LightSpeedCm2NS; + if (fgPVrecalKF) { + values[kVertexingTauxyProjectedPoleJPsiMassRecalculatePV] = values[kVertexingLxyProjectedRecalculatePV] * o2::constants::physics::MassJPsi / (v12.Pt()); + } + values[kVertexingTauzProjected] = values[kVertexingLzProjected] * v12.M() / TMath::Abs(v12.Pz()); + values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * v12.M() / (v12.P()); + } + } + if (propToSV) { + values[kMass] = v12.M(); + values[kPt] = v12.Pt(); + values[kEta] = v12.Eta(); + // values[kPhi] = v12.Phi(); + values[kPhi] = RecoDecay::constrainAngle(v12.Phi()); + } else { + values[kPt1] = t1.pt(); + values[kEta1] = t1.eta(); + values[kPhi1] = t1.phi(); + + values[kPt2] = t2.pt(); + values[kEta2] = t2.eta(); + values[kPhi2] = t2.phi(); + } +} + +template +void VarManager::FillTripletVertexingALICE3(C const& collision, T const& t1, T const& t2, T const& t3, VarManager::PairCandidateType tripletType, float* values) +{ + // TODO: Vertexing error variables + constexpr bool eventHasVtxCov = ((collFillMap & Collision) > 0 || (collFillMap & ReducedEventVtxCov) > 0); + bool trackHasCov = ((fillMap & ReducedTrackBarrelCov) > 0); + + if (!values) { + values = static_cast(fgValues); + } + + float m1 = o2::constants::physics::MassKaonCharged; + float m2 = o2::constants::physics::MassPionCharged; + float m3 = o2::constants::physics::MassPionCharged; + + if (tripletType == kTripleCandidateToKPiPi) { + m1 = o2::constants::physics::MassKaonCharged; + m2 = o2::constants::physics::MassPionCharged; + m3 = o2::constants::physics::MassPionCharged; + } + if (tripletType == kTripleCandidateToPKPi) { + m1 = o2::constants::physics::MassProton; + m2 = o2::constants::physics::MassKaonCharged; + m3 = o2::constants::physics::MassPionCharged; + } + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), m1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), m2); + ROOT::Math::PtEtaPhiMVector v3(t3.pt(), t3.eta(), t3.phi(), m3); + ROOT::Math::PtEtaPhiMVector v123 = v1 + v2 + v3; + + int procCode = 0; + + if (trackHasCov) { + std::array t1pars = {t1.y(), t1.z(), t1.snp(), t1.tgl(), t1.signed1Pt()}; + std::array t1covs = {t1.cYY(), t1.cZY(), t1.cZZ(), t1.cSnpY(), t1.cSnpZ(), + t1.cSnpSnp(), t1.cTglY(), t1.cTglZ(), t1.cTglSnp(), t1.cTglTgl(), + t1.c1PtY(), t1.c1PtZ(), t1.c1PtSnp(), t1.c1PtTgl(), t1.c1Pt21Pt2()}; + o2::track::TrackParCov pars1{t1.x(), t1.alpha(), t1pars, t1covs}; + std::array t2pars = {t2.y(), t2.z(), t2.snp(), t2.tgl(), t2.signed1Pt()}; + std::array t2covs = {t2.cYY(), t2.cZY(), t2.cZZ(), t2.cSnpY(), t2.cSnpZ(), + t2.cSnpSnp(), t2.cTglY(), t2.cTglZ(), t2.cTglSnp(), t2.cTglTgl(), + t2.c1PtY(), t2.c1PtZ(), t2.c1PtSnp(), t2.c1PtTgl(), t2.c1Pt21Pt2()}; + o2::track::TrackParCov pars2{t2.x(), t2.alpha(), t2pars, t2covs}; + std::array t3pars = {t3.y(), t3.z(), t3.snp(), t3.tgl(), t3.signed1Pt()}; + std::array t3covs = {t3.cYY(), t3.cZY(), t3.cZZ(), t3.cSnpY(), t3.cSnpZ(), + t3.cSnpSnp(), t3.cTglY(), t3.cTglZ(), t3.cTglSnp(), t3.cTglTgl(), + t3.c1PtY(), t3.c1PtZ(), t3.c1PtSnp(), t3.c1PtTgl(), t3.c1Pt21Pt2()}; + o2::track::TrackParCov pars3{t3.x(), t3.alpha(), t3pars, t3covs}; + procCode = VarManager::fgFitterThreeProngBarrel.process(pars1, pars2, pars3); + } else { + return; + } + + values[VarManager::kVertexingProcCode] = procCode; + if (procCode == 0) { + // TODO: set the other variables to appropriate values and return + values[kVertexingChi2PCA] = -999.; + values[kVertexingLxy] = -999.; + values[kVertexingLxyz] = -999.; + values[kVertexingLz] = -999.; + values[kVertexingLxyErr] = -999.; + values[kVertexingLxyzErr] = -999.; + values[kVertexingLzErr] = -999.; + + values[kVertexingTauxy] = -999.; + values[kVertexingTauz] = -999.; + values[kVertexingTauxyErr] = -999.; + values[kVertexingTauzErr] = -999.; + + values[kVertexingLzProjected] = -999.; + values[kVertexingLxyProjected] = -999.; + values[kVertexingLxyzProjected] = -999.; + values[kVertexingTauzProjected] = -999.; + values[kVertexingTauxyProjected] = -999.; + values[kVertexingTauxyzProjected] = -999.; + + return; + } + + Vec3D secondaryVertex; + + if constexpr (eventHasVtxCov) { + secondaryVertex = fgFitterThreeProngBarrel.getPCACandidate(); + + std::array covMatrixPCA = fgFitterThreeProngBarrel.calcPCACovMatrixFlat(); + + o2::math_utils::Point3D vtxXYZ(collision.posX(), collision.posY(), collision.posZ()); + std::array vtxCov{collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()}; + o2::dataformats::VertexBase primaryVertex = {vtxXYZ, vtxCov}; + auto covMatrixPV = primaryVertex.getCov(); + + if (fgUsedVars[kVertexingChi2PCA]) { + auto chi2PCA = fgFitterThreeProngBarrel.getChi2AtPCACandidate(); + values[VarManager::kVertexingChi2PCA] = chi2PCA; + } + + double phi = std::atan2(secondaryVertex[1] - collision.posY(), secondaryVertex[0] - collision.posX()); + double theta = std::atan2(secondaryVertex[2] - collision.posZ(), + std::sqrt((secondaryVertex[0] - collision.posX()) * (secondaryVertex[0] - collision.posX()) + + (secondaryVertex[1] - collision.posY()) * (secondaryVertex[1] - collision.posY()))); + + values[kVertexingLxy] = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + + (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); + values[kVertexingLz] = (collision.posZ() - secondaryVertex[2]) * (collision.posZ() - secondaryVertex[2]); + values[kVertexingLxyz] = values[kVertexingLxy] + values[kVertexingLz]; + values[kVertexingLxy] = std::sqrt(values[kVertexingLxy]); + values[kVertexingLz] = std::sqrt(values[kVertexingLz]); + values[kVertexingLxyz] = std::sqrt(values[kVertexingLxyz]); + + values[kVertexingLxyzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); + values[kVertexingLxyErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); + values[kVertexingLzErr] = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, 0, theta) + getRotatedCovMatrixXX(covMatrixPCA, 0, theta)); + + values[kVertexingTauz] = (collision.posZ() - secondaryVertex[2]) * v123.M() / (TMath::Abs(v123.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxy] = values[kVertexingLxy] * v123.M() / (v123.Pt() * o2::constants::physics::LightSpeedCm2NS); + + values[kVertexingTauzErr] = values[kVertexingLzErr] * v123.M() / (TMath::Abs(v123.Pz()) * o2::constants::physics::LightSpeedCm2NS); + values[kVertexingTauxyErr] = values[kVertexingLxyErr] * v123.M() / (v123.Pt() * o2::constants::physics::LightSpeedCm2NS); + + values[kCosPointingAngle] = ((secondaryVertex[0] - collision.posX()) * v123.Px() + + (secondaryVertex[1] - collision.posY()) * v123.Py() + + (secondaryVertex[2] - collision.posZ()) * v123.Pz()) / + (v123.P() * values[VarManager::kVertexingLxyz]); + // run 2 definitions: Decay length projected onto the momentum vector of the candidate + values[kVertexingLzProjected] = (secondaryVertex[2] - collision.posZ()) * v123.Pz(); + values[kVertexingLzProjected] = values[kVertexingLzProjected] / TMath::Sqrt(v123.Pz() * v123.Pz()); + values[kVertexingLxyProjected] = ((secondaryVertex[0] - collision.posX()) * v123.Px()) + ((secondaryVertex[1] - collision.posY()) * v123.Py()); + values[kVertexingLxyProjected] = values[kVertexingLxyProjected] / TMath::Sqrt((v123.Px() * v123.Px()) + (v123.Py() * v123.Py())); + values[kVertexingLxyzProjected] = ((secondaryVertex[0] - collision.posX()) * v123.Px()) + ((secondaryVertex[1] - collision.posY()) * v123.Py()) + ((secondaryVertex[2] - collision.posZ()) * v123.Pz()); + values[kVertexingLxyzProjected] = values[kVertexingLxyzProjected] / TMath::Sqrt((v123.Px() * v123.Px()) + (v123.Py() * v123.Py()) + (v123.Pz() * v123.Pz())); + + values[kVertexingTauzProjected] = values[kVertexingLzProjected] * v123.M() / TMath::Abs(v123.Pz()); + values[kVertexingTauxyProjected] = values[kVertexingLxyProjected] * v123.M() / (v123.Pt()); + values[kVertexingTauxyzProjected] = values[kVertexingLxyzProjected] * v123.M() / (v123.P()); + } +} + #endif // PWGDQ_CORE_VARMANAGER_H_ diff --git a/PWGDQ/Core/dqAnalysisPCH.cxx b/PWGDQ/Core/dqAnalysisPCH.cxx new file mode 100644 index 00000000000..aab5454efc3 --- /dev/null +++ b/PWGDQ/Core/dqAnalysisPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the DQAnalysisPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGDQ/DataModel/ReducedInfoTables.h b/PWGDQ/DataModel/ReducedInfoTables.h index 71a15a1ddbd..eeed55d4886 100644 --- a/PWGDQ/DataModel/ReducedInfoTables.h +++ b/PWGDQ/DataModel/ReducedInfoTables.h @@ -152,6 +152,11 @@ DECLARE_SOA_COLUMN(CORR4REF, corr4ref, float); //! Ref Flow correla DECLARE_SOA_COLUMN(M11REF, m11ref, float); //! Weighted multiplicity of <<2>> for reference flow DECLARE_SOA_COLUMN(M1111REF, m1111ref, float); //! Weighted multiplicity of <<4>> for reference flow DECLARE_SOA_COLUMN(M11REFetagap, m11refetagap, float); //! Weighted multiplicity of <<2>> etagap for reference flow + +DECLARE_SOA_COLUMN(Psi2Random, psi2random, float); //! Event plane angle from random subevent +DECLARE_SOA_COLUMN(Psi2A, psi2a, float); //! Event plane angle from Q-vector A +DECLARE_SOA_COLUMN(Psi2B, psi2b, float); //! Event plane angle from Q-vector B +DECLARE_SOA_COLUMN(Psi2C, psi2c, float); //! Event plane angle from Q-vector C } // namespace reducedevent DECLARE_SOA_TABLE_STAGED(ReducedEvents, "REDUCEDEVENT", //! Main event information table @@ -361,6 +366,39 @@ DECLARE_SOA_TABLE(ReducedFITs, "AOD", "REDUCEDFIT", //! FIT detector information using ReducedFIT = ReducedFITs::iterator; +namespace reducedemcal +{ +// EMCal cluster information (from aod::EMCALClusters, see PWGJE/DataModel/EMCALClusters.h) +DECLARE_SOA_INDEX_COLUMN(ReducedEvent, reducedevent); //! +DECLARE_SOA_BITMAP_COLUMN(FilteringFlags, filteringFlags, 64); //! Bitmap of the cluster selections +DECLARE_SOA_COLUMN(Energy, energy, float); //! cluster energy (GeV) +DECLARE_SOA_COLUMN(CoreEnergy, coreEnergy, float); //! cluster core energy (GeV) +DECLARE_SOA_COLUMN(RawEnergy, rawEnergy, float); //! raw cluster energy (GeV) +DECLARE_SOA_COLUMN(Eta, eta, float); //! cluster pseudorapidity (calculated using vertex) +DECLARE_SOA_COLUMN(Phi, phi, float); //! cluster azimuthal angle (calculated using vertex) +DECLARE_SOA_COLUMN(M02, m02, float); //! shower shape long axis +DECLARE_SOA_COLUMN(M20, m20, float); //! shower shape short axis +DECLARE_SOA_COLUMN(NCells, nCells, int); //! number of cells in cluster +DECLARE_SOA_COLUMN(Time, time, float); //! cluster time (ns) +DECLARE_SOA_COLUMN(IsExotic, isExotic, bool); //! flag to mark cluster as exotic +DECLARE_SOA_COLUMN(DistanceToBadChannel, distanceToBadChannel, float); //! distance to bad channel +DECLARE_SOA_COLUMN(NLM, nlm, int); //! number of local maxima +DECLARE_SOA_COLUMN(Definition, definition, int); //! cluster definition, see EMCALClusterDefinition.h +DECLARE_SOA_COLUMN(IsAmbiguous, isAmbiguous, bool); //! true for clusters from BCs with no or multiple collisions +} // namespace reducedemcal + +// Skimmed EMCal cluster information; standalone table (not joinable to the barrel track tables), +// the matching to barrel tracks is stored as an index column in the ReducedTracksBarrelEMCal table +DECLARE_SOA_TABLE(ReducedEMCals, "AOD", "REDUCEDEMCAL", //! + o2::soa::Index<>, reducedemcal::ReducedEventId, reducedemcal::FilteringFlags, + reducedemcal::Energy, reducedemcal::CoreEnergy, reducedemcal::RawEnergy, + reducedemcal::Eta, reducedemcal::Phi, reducedemcal::M02, reducedemcal::M20, + reducedemcal::NCells, reducedemcal::Time, reducedemcal::IsExotic, + reducedemcal::DistanceToBadChannel, reducedemcal::NLM, reducedemcal::Definition, + reducedemcal::IsAmbiguous); + +using ReducedEMCal = ReducedEMCals::iterator; + namespace reducedtrack { // basic track information @@ -394,6 +432,9 @@ DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! [](float pt, float eta) -> float { return pt * std::sinh(eta); }); DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! [](float pt, float eta) -> float { return pt * std::cosh(eta); }); +DECLARE_SOA_INDEX_COLUMN(ReducedEMCal, matchedEMCalCluster); //! matching index pointing to the ReducedEMCals table, -1 if no matched cluster +DECLARE_SOA_COLUMN(EMCalMatchDeltaEta, emcalMatchDeltaEta, float); //! eta residual of the matched EMCal cluster w.r.t. the track +DECLARE_SOA_COLUMN(EMCalMatchDeltaPhi, emcalMatchDeltaPhi, float); //! phi residual of the matched EMCal cluster w.r.t. the track } // namespace reducedtrack // basic track information @@ -442,11 +483,18 @@ DECLARE_SOA_TABLE(ReducedTracksBarrelPID, "AOD", "RTBARRELPID", //! DECLARE_SOA_TABLE(ReducedTracksBarrelInfo, "AOD", "RTBARRELINFO", reducedtrack::CollisionId, collision::PosX, collision::PosY, collision::PosZ, reducedtrack::TrackId); +// barrel track EMCal matching information (joined with ReducedTracks); +// one entry per skimmed barrel track, the index is -1 for tracks without a matched cluster +DECLARE_SOA_TABLE(ReducedTracksBarrelEMCal, "AOD", "RTBARRELEMCAL", //! + reducedtrack::ReducedEMCalId, + reducedtrack::EMCalMatchDeltaEta, reducedtrack::EMCalMatchDeltaPhi); + using ReducedTrack = ReducedTracks::iterator; using ReducedTrackBarrel = ReducedTracksBarrel::iterator; using ReducedTrackBarrelCov = ReducedTracksBarrelCov::iterator; using ReducedTrackBarrelPID = ReducedTracksBarrelPID::iterator; using ReducedTrackBarrelInfo = ReducedTracksBarrelInfo::iterator; +using ReducedTrackBarrelEMCal = ReducedTracksBarrelEMCal::iterator; namespace reducedtrackMC { @@ -821,6 +869,9 @@ DECLARE_SOA_COLUMN(PairDCAxyz, pairDCAxyz, float); DECLARE_SOA_COLUMN(PairDCAxy, pairDCAxy, float); //! Pair DCAxy to PV from KFParticle DECLARE_SOA_COLUMN(DeviationPairKF, deviationPairKF, float); //! Pair chi2 deviation to PV from KFParticle DECLARE_SOA_COLUMN(DeviationxyPairKF, deviationxyPairKF, float); //! Pair chi2 deviation to PV in XY from KFParticle +DECLARE_SOA_COLUMN(BdtBackground, bdtBackground, float); //! BDT output score for the background class +DECLARE_SOA_COLUMN(BdtPrompt, bdtPrompt, float); //! BDT output score for the prompt class +DECLARE_SOA_COLUMN(BdtNonprompt, bdtNonprompt, float); //! BDT output score for the nonprompt class // DECLARE_SOA_INDEX_COLUMN(ReducedMuon, reducedmuon2); //! DECLARE_SOA_COLUMN(CosThetaHE, costhetaHE, float); //! Cosine in the helicity frame DECLARE_SOA_COLUMN(PhiHE, phiHe, float); //! Phi in the helicity frame @@ -835,6 +886,7 @@ DECLARE_SOA_COLUMN(CosThetaRM, costhetaRM, float); //! Cosine in the DECLARE_SOA_COLUMN(CosThetaStarTPC, costhetaStarTPC, float); //! global polarization, event plane reconstructed from TPC tracks DECLARE_SOA_COLUMN(CosThetaStarFT0A, costhetaStarFT0A, float); //! global polarization, event plane reconstructed from FT0A tracks DECLARE_SOA_COLUMN(CosThetaStarFT0C, costhetaStarFT0C, float); //! global polarization, event plane reconstructed from FT0C tracks +DECLARE_SOA_COLUMN(CosThetaStarRandom, costhetaStarRandom, float); //! global polarization, event plane reconstructed from random tracks DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! [](float pt, float phi) -> float { return pt * std::cos(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! @@ -931,6 +983,12 @@ DECLARE_SOA_TABLE_STAGED(DielectronsAll, "RTDIELECTRONALL", //! reducedpair::Lz, reducedpair::Lxy); +DECLARE_SOA_TABLE_STAGED(DielectronsMls, "RTDIELECTRONML", //! + reducedpair::CentFT0C, + reducedpair::BdtBackground, + reducedpair::BdtPrompt, + reducedpair::BdtNonprompt); + DECLARE_SOA_TABLE(DimuonsAll, "AOD", "RTDIMUONALL", //! collision::PosX, collision::PosY, collision::PosZ, collision::NumContrib, evsel::Selection, reducedpair::EventSelection, @@ -987,11 +1045,15 @@ DECLARE_SOA_TABLE(DileptonsMiniTreeRec, "AOD", "RTDILMTREEREC", //! dilepton_track_index::Pt2, dilepton_track_index::Eta2, dilepton_track_index::Phi2); DECLARE_SOA_TABLE(DileptonsPolarization, "AOD", "RTDILPOLAR", //! + reducedpair::Mass, reducedpair::Pt, reducedpair::Eta, reducedpair::Sign, reducedpair::FilterMap, reducedpair::CosThetaHE, reducedpair::PhiHE, reducedpair::PhiTildeHE, reducedpair::CosThetaCS, reducedpair::PhiCS, reducedpair::PhiTildeCS, reducedpair::CosThetaPP, reducedpair::PhiPP, reducedpair::PhiTildePP, reducedpair::CosThetaRM, - reducedpair::CosThetaStarTPC, reducedpair::CosThetaStarFT0A, reducedpair::CosThetaStarFT0C); + reducedpair::CosThetaStarTPC, reducedpair::CosThetaStarFT0A, reducedpair::CosThetaStarFT0C, reducedpair::CosThetaStarRandom); + +DECLARE_SOA_TABLE(DileptonsEventInfo, "AOD", "RTDILEVENTINFO", //! + cent::CentFT0C, collision::PosZ, collision::NumContrib, reducedevent::Psi2Random, reducedevent::Psi2A, reducedevent::Psi2B, reducedevent::Psi2C); using Dielectron = Dielectrons::iterator; using StoredDielectron = StoredDielectrons::iterator; @@ -1002,11 +1064,13 @@ using DimuonExtra = DimuonsExtra::iterator; using DileptonFlow = DileptonsFlow::iterator; using DileptonInfo = DileptonsInfo::iterator; using DielectronAll = DielectronsAll::iterator; +using DielectronMl = DielectronsMls::iterator; using DimuonAll = DimuonsAll::iterator; using DileptonMiniTree = DileptonsMiniTree::iterator; using DileptonMiniTreeGen = DileptonsMiniTreeGen::iterator; using DileptonMiniTreeRec = DileptonsMiniTreeRec::iterator; using DileptonPolarization = DileptonsPolarization::iterator; +using DileptonEventInfo = DileptonsEventInfo::iterator; // Tables for using analysis-dilepton-track with analysis-asymmetric-pairing DECLARE_SOA_TABLE(Ditracks, "AOD", "RTDITRACK", //! @@ -1134,6 +1198,8 @@ DECLARE_SOA_COLUMN(VertexingLxyProjected, vertexingLxyProjected, float); //! DECLARE_SOA_COLUMN(VertexingLxyzProjected, vertexingLxyzProjected, float); //! DECLARE_SOA_COLUMN(VertexingTauzProjected, vertexingTauzProjected, float); //! DECLARE_SOA_COLUMN(VertexingTauxyProjected, vertexingTauxyProjected, float); //! +DECLARE_SOA_BITMAP_COLUMN(DileptonFilterMap, dileptonFilterMap, 32); //! +DECLARE_SOA_BITMAP_COLUMN(QuadFilterMap, quadFilterMap, 32); //! } // namespace dileptonTrackTrackCandidate DECLARE_SOA_TABLE(DileptonTrackTrackCandidates, "AOD", "RTDQUADPLET", //! @@ -1274,32 +1340,44 @@ DECLARE_SOA_COLUMN(DeltaY, deltaY, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); //! DECLARE_SOA_COLUMN(NumItsClsDmesProng0, numItsClsDmesProng0, int); //! DECLARE_SOA_COLUMN(NumItsClsDmesProng1, numItsClsDmesProng1, int); //! +DECLARE_SOA_COLUMN(NumItsClsDmesProng2, numItsClsDmesProng2, int); //! DECLARE_SOA_COLUMN(NumTpcCrossedRowsDmesProng0, numTpcCrossedRowsDmesProng0, int); //! DECLARE_SOA_COLUMN(NumTpcCrossedRowsDmesProng1, numTpcCrossedRowsDmesProng1, int); //! +DECLARE_SOA_COLUMN(NumTpcCrossedRowsDmesProng2, numTpcCrossedRowsDmesProng2, int); //! DECLARE_SOA_COLUMN(EtaDmesProng0, etaDmesProng0, float); //! DECLARE_SOA_COLUMN(EtaDmesProng1, etaDmesProng1, float); //! +DECLARE_SOA_COLUMN(EtaDmesProng2, etaDmesProng2, float); //! DECLARE_SOA_COLUMN(PtDmesProng0, ptDmesProng0, float); //! DECLARE_SOA_COLUMN(PtDmesProng1, ptDmesProng1, float); //! +DECLARE_SOA_COLUMN(PtDmesProng2, ptDmesProng2, float); //! DECLARE_SOA_COLUMN(MinNumItsClsDmesProng, minNumItsClsDmesProng, int); //! DECLARE_SOA_COLUMN(MinNumTpcCrossedRowsDmesProng, minNumTpcCrossedRowsDmesProng, int); //! DECLARE_SOA_COLUMN(MinAbsEtaDmesProng, minAbsEtaDmesProng, float); //! DECLARE_SOA_COLUMN(MinPtDmesProng, minPtDmesProng, float); //! DECLARE_SOA_COLUMN(NumSigmaTpcPiProng0, numSigmaTpcPiProng0, float); //! DECLARE_SOA_COLUMN(NumSigmaTpcPiProng1, numSigmaTpcPiProng1, float); //! +DECLARE_SOA_COLUMN(NumSigmaTpcPiProng2, numSigmaTpcPiProng2, float); //! DECLARE_SOA_COLUMN(NumSigmaTofPiProng0, numSigmaTofPiProng0, float); //! DECLARE_SOA_COLUMN(NumSigmaTofPiProng1, numSigmaTofPiProng1, float); //! +DECLARE_SOA_COLUMN(NumSigmaTofPiProng2, numSigmaTofPiProng2, float); //! DECLARE_SOA_COLUMN(NumSigmaTpcKaProng0, numSigmaTpcKaProng0, float); //! DECLARE_SOA_COLUMN(NumSigmaTpcKaProng1, numSigmaTpcKaProng1, float); //! +DECLARE_SOA_COLUMN(NumSigmaTpcKaProng2, numSigmaTpcKaProng2, float); //! DECLARE_SOA_COLUMN(NumSigmaTofKaProng0, numSigmaTofKaProng0, float); //! DECLARE_SOA_COLUMN(NumSigmaTofKaProng1, numSigmaTofKaProng1, float); //! +DECLARE_SOA_COLUMN(NumSigmaTofKaProng2, numSigmaTofKaProng2, float); //! DECLARE_SOA_DYNAMIC_COLUMN(NumSigmaTpcTofPiProng0, numSigmaTpcTofPiProng0, //! [](float tpcNSigmaPi0, float tofNSigmaPi0) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaPi0, tofNSigmaPi0); }); DECLARE_SOA_DYNAMIC_COLUMN(NumSigmaTpcTofPiProng1, numSigmaTpcTofPiProng1, //! [](float tpcNSigmaPi1, float tofNSigmaPi1) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaPi1, tofNSigmaPi1); }); +DECLARE_SOA_DYNAMIC_COLUMN(NumSigmaTpcTofPiProng2, numSigmaTpcTofPiProng2, //! + [](float tpcNSigmaPi2, float tofNSigmaPi2) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaPi2, tofNSigmaPi2); }); DECLARE_SOA_DYNAMIC_COLUMN(NumSigmaTpcTofKaProng0, numSigmaTpcTofKaProng0, //! [](float tpcNSigmaKa0, float tofNSigmaKa0) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaKa0, tofNSigmaKa0); }); DECLARE_SOA_DYNAMIC_COLUMN(NumSigmaTpcTofKaProng1, numSigmaTpcTofKaProng1, //! [](float tpcNSigmaKa1, float tofNSigmaKa1) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaKa1, tofNSigmaKa1); }); +DECLARE_SOA_DYNAMIC_COLUMN(NumSigmaTpcTofKaProng2, numSigmaTpcTofKaProng2, //! + [](float tpcNSigmaKa2, float tofNSigmaKa2) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaKa2, tofNSigmaKa2); }); } // namespace jpsidmescorr DECLARE_SOA_TABLE(RedJpDmDileptons, "AOD", "REDJPDMDILEPTON", //! @@ -1354,6 +1432,23 @@ DECLARE_SOA_TABLE(RedJpDmDmDau1s, "AOD", "REDJPDMDMDAU1", //! jpsidmescorr::NumSigmaTpcTofPiProng1, jpsidmescorr::NumSigmaTpcTofKaProng1); +DECLARE_SOA_TABLE(RedJpDmDmDau2s, "AOD", "REDJPDMDMDAU2", //! + jpsidmescorr::PtDmesProng2, + jpsidmescorr::EtaDmesProng2, + jpsidmescorr::NumItsClsDmesProng2, + jpsidmescorr::NumTpcCrossedRowsDmesProng2, + jpsidmescorr::NumSigmaTpcPiProng2, + jpsidmescorr::NumSigmaTofPiProng2, + jpsidmescorr::NumSigmaTpcKaProng2, + jpsidmescorr::NumSigmaTofKaProng2, + jpsidmescorr::NumSigmaTpcTofPiProng2, + jpsidmescorr::NumSigmaTpcTofKaProng2); + +// General table for Dmeson mass +DECLARE_SOA_TABLE(RedJpDmDmesMasss, "AOD", "REDJPDMDMESMASS", //??? + jpsidmescorr::MassDmes); + +// Special table for D0 mass: particle and antiparticle separated DECLARE_SOA_TABLE(RedJpDmD0Masss, "AOD", "REDJPDMD0MASS", //! jpsidmescorr::MassD0, jpsidmescorr::MassD0bar); @@ -1366,6 +1461,11 @@ DECLARE_SOA_TABLE(RedJpDmDmesBdts, "AOD", "REDJPDMDMESBDT", //! jpsidmescorr::BdtPromptMassHypo1, jpsidmescorr::BdtNonpromptMassHypo1); +DECLARE_SOA_TABLE(RedJpDmDplusBdts, "AOD", "REDJPDMDPLUSBDT", + jpsidmescorr::BdtBkgMassHypo0, + jpsidmescorr::BdtPromptMassHypo0, + jpsidmescorr::BdtNonpromptMassHypo0); + DECLARE_SOA_TABLE(RedDleptDmesAll, "AOD", "RTDILPTDMESALL", //! reducedpair::Mass, jpsidmescorr::MassDmes, diff --git a/PWGDQ/Macros/dqFlowAccWeights.C b/PWGDQ/Macros/dqFlowAccWeights.C index d4786089810..8df81ff38e1 100644 --- a/PWGDQ/Macros/dqFlowAccWeights.C +++ b/PWGDQ/Macros/dqFlowAccWeights.C @@ -32,7 +32,7 @@ using namespace o2; using namespace std; -void dqFlowAccWeights(int64_t tmin = 1546300800000, int64_t tmax = 1577833200000, std::string Period = "LHC23zzh_pass2", std::string SubDir = "d-q-event-qvector", std::string FileName = "AnalysisResults.root") +void dqFlowAccWeights(int64_t tmin = 1546300800000, int64_t tmax = 1577833200000, const std::string& Period = "LHC23zzh_pass2", const std::string& SubDir = "d-q-event-qvector", const std::string& FileName = "AnalysisResults.root") { if (tmax < tmin) { LOG(fatal) << "Wrong validity syntax!"; @@ -64,7 +64,7 @@ void dqFlowAccWeights(int64_t tmin = 1546300800000, int64_t tmax = 1577833200000 if (!ccdbHost.empty()) { LOGP(info, "Storing alignment object on {}/{}", ccdbHost, objectPath); o2::ccdb::CcdbApi api; - map metadata; // can be empty + std::map metadata; // can be empty metadata.insert(std::pair{"comment", Form("Acceptance weights for %s", Period.c_str())}); api.init(ccdbHost.c_str()); try { diff --git a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx index c59afbb40b8..931b4c168ff 100644 --- a/PWGDQ/Macros/evalMchTrackingEfficiency.cxx +++ b/PWGDQ/Macros/evalMchTrackingEfficiency.cxx @@ -46,8 +46,8 @@ void evalMchTrackingEfficiency(const char* inFile = "AnalysisResults.root", cons { // read ouput from the taskComputeMchEfficiency TFile* file = TFile::Open(inFile, "read"); - TDirectoryFile* dir = reinterpret_cast(file->Get("task-muon-mch-trk-efficiency")); - THnF* hHitsEtaPtPhi = reinterpret_cast(dir->Get("hHitsEtaPtPhi")); + TDirectoryFile* dir = dynamic_cast(file->Get("task-muon-mch-trk-efficiency")); + THnF* hHitsEtaPtPhi = dynamic_cast(dir->Get("hHitsEtaPtPhi")); // retrieve the eta/pt/phi axis binning configuration const int etaAx = 1, ptAx = 2, phiAx = 3; @@ -274,7 +274,7 @@ double computeEfficiencyPerChamber(THnF* hnf, int iAxis, int iCh, double binLimi hnf->GetAxis(iAxis)->SetRangeUser(binLimits[0], binLimits[1]); // Project onto the Nhits axis - TH1F* htmp = reinterpret_cast(hnf->Projection(0)); + TH1F* htmp = dynamic_cast(hnf->Projection(0)); // FIXME: invalid cast double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; @@ -322,7 +322,7 @@ double computeEfficiencyPerChamber(THnF* hnf, const int iAxis[3], int iCh, doubl } // Project onto the Nhits axis - TH1F* htmp = reinterpret_cast(hnf->Projection(0)); + TH1F* htmp = dynamic_cast(hnf->Projection(0)); // FIXME: invalid cast double NhitPairing[16]; for (int i = 0; i < 16; i++) { NhitPairing[i] = 0; diff --git a/PWGDQ/Macros/fit_library/CB2Pdf.h b/PWGDQ/Macros/fit_library/CB2Pdf.h index 23cb291dcdd..d5b5326813e 100644 --- a/PWGDQ/Macros/fit_library/CB2Pdf.h +++ b/PWGDQ/Macros/fit_library/CB2Pdf.h @@ -25,7 +25,7 @@ class CB2Pdf : public RooAbsPdf { public: - CB2Pdf(){}; + CB2Pdf() {} CB2Pdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/ExpPdf.h b/PWGDQ/Macros/fit_library/ExpPdf.h index 306b0aea210..7495176d942 100644 --- a/PWGDQ/Macros/fit_library/ExpPdf.h +++ b/PWGDQ/Macros/fit_library/ExpPdf.h @@ -25,7 +25,7 @@ class ExpPdf : public RooAbsPdf { public: - ExpPdf(){}; + ExpPdf() {} ExpPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/GausPdf.h b/PWGDQ/Macros/fit_library/GausPdf.h index 80a1e956817..22e07a2d522 100644 --- a/PWGDQ/Macros/fit_library/GausPdf.h +++ b/PWGDQ/Macros/fit_library/GausPdf.h @@ -25,7 +25,7 @@ class GausPdf : public RooAbsPdf { public: - GausPdf(){}; + GausPdf() {} GausPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/Macros/fit_library/Pol4ExpPdf.h b/PWGDQ/Macros/fit_library/Pol4ExpPdf.h index 67202dbe5ac..baca326c1f2 100644 --- a/PWGDQ/Macros/fit_library/Pol4ExpPdf.h +++ b/PWGDQ/Macros/fit_library/Pol4ExpPdf.h @@ -28,7 +28,7 @@ TString nameParameters[] = {"p0", "p1", "p2", "p3", "p4", "p5", "N_bkg", "N_sig" class Pol4ExpPdf : public RooAbsPdf { public: - Pol4ExpPdf(){}; + Pol4ExpPdf() {} Pol4ExpPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _P0, diff --git a/PWGDQ/Macros/fit_library/VWGPdf.h b/PWGDQ/Macros/fit_library/VWGPdf.h index 3ed4f2f81e6..af8c6cba9eb 100644 --- a/PWGDQ/Macros/fit_library/VWGPdf.h +++ b/PWGDQ/Macros/fit_library/VWGPdf.h @@ -25,7 +25,7 @@ class VWGPdf : public RooAbsPdf { public: - VWGPdf(){}; + VWGPdf() {} VWGPdf(const char* name, const char* title, RooAbsReal& _x, RooAbsReal& _A, diff --git a/PWGDQ/TableProducer/CMakeLists.txt b/PWGDQ/TableProducer/CMakeLists.txt index b8fd20d356d..5ba06a37cb5 100644 --- a/PWGDQ/TableProducer/CMakeLists.txt +++ b/PWGDQ/TableProducer/CMakeLists.txt @@ -11,35 +11,42 @@ o2physics_add_dpl_workflow(table-maker SOURCES tableMaker.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-with-assoc SOURCES tableMaker_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-mc SOURCES tableMakerMC.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-mc-with-assoc SOURCES tableMakerMC_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-jpsi-hf SOURCES tableMakerJpsiHf.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-maker-muon-mch-trk-eff SOURCES tableMakerMuonMchTrkEfficiency.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(generated-quarkonia-mc SOURCES generatedQuarkoniaMC.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase O2Physics::PWGDQCore COMPONENT_NAME Analysis) diff --git a/PWGDQ/TableProducer/tableMaker.cxx b/PWGDQ/TableProducer/tableMaker.cxx index f2938705e87..eaa2e9a2bc2 100644 --- a/PWGDQ/TableProducer/tableMaker.cxx +++ b/PWGDQ/TableProducer/tableMaker.cxx @@ -202,7 +202,7 @@ struct TableMaker { struct : ConfigurableGroup { Configurable fConfigCcdbUrl{"useCCDBConfigurations.ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"useCCDBConfigurations.ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; - Configurable fConfigCcdbPathZorro{"useCCDBConfigurations.ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"useCCDBConfigurations.ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; } useCCDBConfigurations; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; @@ -236,7 +236,7 @@ struct TableMaker { int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. // TODO: filter on TPC dedx used temporarily until electron PID will be improved - Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == uint8_t(aod::track::Run2Track), aod::track::trackType == uint8_t(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= fConfigBarrelTrackMaxAbsEta && o2::aod::track::tpcSignal >= configTpcSignal.fConfigMinTpcSignal && o2::aod::track::tpcSignal <= configTpcSignal.fConfigMaxTpcSignal && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; + Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == static_cast(aod::track::Run2Track), aod::track::trackType == static_cast(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= fConfigBarrelTrackMaxAbsEta && o2::aod::track::tpcSignal >= configTpcSignal.fConfigMinTpcSignal && o2::aod::track::tpcSignal <= configTpcSignal.fConfigMaxTpcSignal && o2::aod::track::tpcChi2NCl < 4.0f && o2::aod::track::itsChi2NCl < 36.0f; Filter muonFilter = o2::aod::fwdtrack::pt >= fConfigMuonPtLow; @@ -445,17 +445,17 @@ struct TableMaker { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (useZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(useCCDBConfigurations.fConfigCcdbPathZorro.value); zorro.setBCtolerance(useZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), useZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(3)), reinterpret_cast(fStatsList->At(4))); - bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, reinterpret_cast(fStatsList->At(4))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(3)), dynamic_cast(fStatsList->At(4))); + bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, dynamic_cast(fStatsList->At(4))); if (zorroSel) { tag |= (static_cast(true) << 56); // the same bit is used for this zorro selections from ccdb } @@ -471,10 +471,10 @@ struct TableMaker { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); @@ -534,7 +534,7 @@ struct TableMaker { } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(track); if (fDoDetailedQA) { fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); @@ -547,14 +547,14 @@ struct TableMaker { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -571,8 +571,8 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag = static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } if (fConfigIsOnlyforMaps) { @@ -715,7 +715,7 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } if (!trackTempFilterMap) { // does not pass the cuts @@ -738,8 +738,8 @@ struct TableMaker { } } } - fwdFilteringTag = uint8_t(0); - trackTempFilterMap = uint8_t(0); + fwdFilteringTag = static_cast(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(muon); @@ -759,14 +759,14 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -775,7 +775,7 @@ struct TableMaker { // store the cut decisions trackFilteringTag = trackTempFilterMap; // BIT0-7: user selection cuts if (fPropMuon) { - trackFilteringTag |= (uint8_t(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not + trackFilteringTag |= (static_cast(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not } // update the matching MCH/MFT index @@ -917,17 +917,17 @@ struct TableMaker { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (useZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(useCCDBConfigurations.fConfigCcdbPathZorro.value); zorro.setBCtolerance(useZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), useZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(3)), reinterpret_cast(fStatsList->At(4))); - bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, reinterpret_cast(fStatsList->At(4))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(3)), dynamic_cast(fStatsList->At(4))); + bool zorroSel = zorro.isSelected(bc.globalBC(), useZorro.fBcTolerance, dynamic_cast(fStatsList->At(4))); if (zorroSel) { tag |= (static_cast(true) << 56); // the same bit is used for this zorro selections from ccdb } @@ -943,10 +943,10 @@ struct TableMaker { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); @@ -999,7 +999,7 @@ struct TableMaker { } } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); VarManager::FillTrack(track); if (fDoDetailedQA) { fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); @@ -1012,14 +1012,14 @@ struct TableMaker { int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -1036,8 +1036,8 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT2-6: V0Bits trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } if (fConfigIsOnlyforMaps) { @@ -1133,7 +1133,7 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } if (!trackTempFilterMap) { // does not pass the cuts @@ -1152,7 +1152,7 @@ struct TableMaker { } } trackFilteringTag = static_cast(0); - trackTempFilterMap = uint8_t(0); + trackTempFilterMap = static_cast(0); if constexpr (static_cast(TMuonFillMap & VarManager::ObjTypes::MuonRealign)) { if (static_cast(muon.isRemovable())) { @@ -1179,14 +1179,14 @@ struct TableMaker { int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { if ((*cut).IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("Muons_%s", (*cut).GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", (*cut).GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -1196,7 +1196,7 @@ struct TableMaker { // store the cut decisions trackFilteringTag = trackTempFilterMap; // BIT0-7: user selection cuts if (fPropMuon) { - trackFilteringTag |= (uint8_t(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not + trackFilteringTag |= (static_cast(1) << VarManager::kMuonIsPropagated); // store the info on whether the muon is propagated or not } // update the matching MCH/MFT index @@ -1246,7 +1246,7 @@ struct TableMaker { } // end if constexpr (TMuonFillMap) } // end fullSkimming() - void DefineHistograms(TString histClasses) + void DefineHistograms(const TString& histClasses) { std::unique_ptr objArray(histClasses.Tokenize(";")); for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { @@ -1383,10 +1383,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, tracksMuon, nullptr, nullptr); } @@ -1398,10 +1398,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, tracksMuon, nullptr, nullptr); } @@ -1447,7 +1447,7 @@ struct TableMaker { soa::Filtered const& tracksBarrel) { for (auto const& track : tracksBarrel) { - if (track.dalitzBits() != uint8_t(0)) { + if (track.dalitzBits() != static_cast(0)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } @@ -1460,12 +1460,12 @@ struct TableMaker { { for (auto const& track : tracksBarrel) { // check if this event has a Dalitz candidate - if (track.dalitzBits() != uint8_t(0)) { + if (track.dalitzBits() != static_cast(0)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } // check if this event has a electron candidate from V0 - if (track.pidbit() == uint8_t(1)) { + if (track.pidbit() == static_cast(1)) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); break; } @@ -1478,10 +1478,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1500,10 +1500,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1515,10 +1515,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, tracksBarrel, nullptr, nullptr, nullptr); } @@ -1593,10 +1593,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } @@ -1710,10 +1710,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (bc.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); } // List of process functions removed because they are not using cov matrix @@ -1752,10 +1752,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } @@ -1767,10 +1767,10 @@ struct TableMaker { { for (int i = 0; i < kNaliases; i++) { if (collision.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(kNaliases)); if (collision.eventFilter()) { fullSkimming(collision, bcs, nullptr, tracksMuon, nullptr, nullptr); } diff --git a/PWGDQ/TableProducer/tableMakerJpsiHf.cxx b/PWGDQ/TableProducer/tableMakerJpsiHf.cxx index c5569ae7e14..65f69e394d9 100644 --- a/PWGDQ/TableProducer/tableMakerJpsiHf.cxx +++ b/PWGDQ/TableProducer/tableMakerJpsiHf.cxx @@ -55,6 +55,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::aod; using namespace o2::aod::hf_cand_2prong; +using namespace o2::aod::hf_cand_3prong; // bit maps used for the Fill functions of the VarManager constexpr static uint32_t gkTrackFillMapWithColl = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelPID | VarManager::ObjTypes::ReducedTrackCollInfo; @@ -94,13 +95,13 @@ struct CandidateDilepton { using MyEvents = soa::Join; using MyD0CandidatesSelected = soa::Join; using MyD0CandidatesSelectedWithBdt = soa::Join; +using MyDplusCandidatesSelected = soa::Join; +using MyDplusCandidatesSelectedWithBdt = soa::Join; using TracksWithExtra = soa::Join; using MyBarrelTracksSelectedWithColl = soa::Join; using MyMuonTracksSelectedWithColl = soa::Join; -HfHelper hfHelper; - struct tableMakerJpsiHf { // // This task combines dilepton candidates with a open charm hadron @@ -111,8 +112,11 @@ struct tableMakerJpsiHf { Produces redDmesons; Produces redDmesDau0; Produces redDmesDau1; + Produces redDmesDau2; Produces redDmesBdts; + Produces redDplusBdts; Produces redD0Masses; + Produces redDmesMasses; Produces redDileptons; Produces redCollCounter; @@ -125,17 +129,26 @@ struct tableMakerJpsiHf { Configurable fConfigMuonCuts{"cfgMuonCuts", "muonQualityCuts", "Comma separated list of muon cuts"}; Configurable massDileptonCandMin{"massDileptonCandMin", 1, "minimum dilepton mass"}; Configurable massDileptonCandMax{"massDileptonCandMax", 5, "maximum dilepton mass"}; + // General configurables - Configurable configDebug{"configDebug", true, "If true, fill D0 - J/psi histograms separately"}; + Configurable configDebug{"configDebug", true, "If true, fill D - J/psi histograms separately"}; Configurable storeTableForNorm{"storeTableForNorm", true, "If true, store a table with number of processed collisions for normalisation"}; - Preslice perCollisionDmeson = aod::hf_cand::collisionId; - Preslice perCollisionDmesonWithBdt = aod::hf_cand::collisionId; + enum class DmesonType { + D0, + Dplus + }; + + Preslice perCollisionD0 = aod::hf_cand::collisionId; + Preslice perCollisionD0WithBdt = aod::hf_cand::collisionId; + Preslice perCollisionDplus = aod::hf_cand::collisionId; + Preslice perCollisionDplusWithBdt = aod::hf_cand::collisionId; PresliceUnsorted perCollisionMuons = aod::reducedmuon::collisionId; PresliceUnsorted perCollisionElectrons = aod::reducedtrack::collisionId; SliceCache cache; Filter filterD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= 1 || aod::hf_sel_candidate_d0::isSelD0bar >= 1; + Filter filterDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= 1; // Define histograms manager float* fValuesDileptonCharmHadron{}; @@ -147,7 +160,7 @@ struct tableMakerJpsiHf { void init(o2::framework::InitContext&) { - std::array doprocess{doprocessJspiToMuMuD0, doprocessJspiToMuMuD0WithBdt, doprocessJspiToEED0, doprocessJspiToEED0WithBdt}; + std::array doprocess{doprocessJspiToMuMuD0, doprocessJspiToMuMuD0WithBdt, doprocessJspiToEED0, doprocessJspiToEED0WithBdt, doprocessJspiToMuMuDplus, doprocessJspiToMuMuDplusWithBdt, doprocessJspiToEEDplus, doprocessJspiToEEDplusWithBdt}; if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { LOGP(fatal, "Only one process function should be enabled! Please check your configuration!"); } @@ -161,11 +174,11 @@ struct tableMakerJpsiHf { fHistMan->AddHistClass("JPsi"); fHistMan->AddHistClass("JPsiDmeson"); dqhistograms::DefineHistograms(fHistMan, "Dmeson", "dilepton-charmhadron", "dmeson"); - if (doprocessJspiToMuMuD0 || doprocessJspiToMuMuD0WithBdt) { + if (doprocessJspiToMuMuD0 || doprocessJspiToMuMuD0WithBdt || doprocessJspiToMuMuDplus || doprocessJspiToMuMuDplusWithBdt) { dqhistograms::DefineHistograms(fHistMan, "JPsi", "dilepton-charmhadron", "jpsitomumu"); dqhistograms::DefineHistograms(fHistMan, "JPsiDmeson", "dilepton-charmhadron", "jpsitomumudmeson"); } - if (doprocessJspiToEED0 || doprocessJspiToEED0WithBdt) { + if (doprocessJspiToEED0 || doprocessJspiToEED0WithBdt || doprocessJspiToEEDplus || doprocessJspiToEEDplusWithBdt) { dqhistograms::DefineHistograms(fHistMan, "JPsi", "dilepton-charmhadron", "jpsitoee"); dqhistograms::DefineHistograms(fHistMan, "JPsiDmeson", "dilepton-charmhadron", "jpsitoeedmeson"); } @@ -173,7 +186,7 @@ struct tableMakerJpsiHf { fOutputList.setObject(fHistMan->GetMainHistogramList()); // cut strings - if (doprocessJspiToMuMuD0 || doprocessJspiToMuMuD0WithBdt) { + if (doprocessJspiToMuMuD0 || doprocessJspiToMuMuD0WithBdt || doprocessJspiToMuMuDplus || doprocessJspiToMuMuDplusWithBdt) { TString cutNamesMuon = fConfigMuonCuts.value; if (!cutNamesMuon.IsNull()) { std::unique_ptr objArray(cutNamesMuon.Tokenize(",")); @@ -182,7 +195,7 @@ struct tableMakerJpsiHf { } } } - if (doprocessJspiToEED0 || doprocessJspiToEED0WithBdt) { + if (doprocessJspiToEED0 || doprocessJspiToEED0WithBdt || doprocessJspiToEEDplus || doprocessJspiToEEDplusWithBdt) { TString cutNamesElectron = fConfigTrackCuts.value; if (!cutNamesElectron.IsNull()) { // if track cuts std::unique_ptr objArray(cutNamesElectron.Tokenize(",")); @@ -262,8 +275,8 @@ struct tableMakerJpsiHf { } // Template function to run pair - hadron combinations - // TODO: generalise to all charm-hadron species - template + // Charm-hadrons: D0, Dplus; can be extended to other species + template void runDileptonDmeson(TDqTrack const& leptons, THfTrack const& dmesons, MyEvents::iterator const& collision, TracksWithExtra const&) { VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); @@ -271,12 +284,18 @@ struct tableMakerJpsiHf { bool isCollSel{false}; if (configDebug) { for (auto const& dmeson : dmesons) { - if (!TESTBIT(dmeson.hfflag(), DecayType::D0ToPiK)) { - continue; + if constexpr (TDmeson == DmesonType::D0) { + if (!TESTBIT(dmeson.hfflag(), hf_cand_2prong::DecayType::D0ToPiK)) { + continue; + } + } else if constexpr (TDmeson == DmesonType::Dplus) { + if (!TESTBIT(dmeson.hfflag(), hf_cand_3prong::DecayType::DplusToPiKPi)) { + continue; + } } std::array scores = {999., -999., -999., 999., -999., -999.}; // D0 + D0bar - if constexpr (withBdt) { + if constexpr (withBdt && TDmeson == DmesonType::D0) { if (dmeson.mlProbD0().size() == 3) { for (auto iScore{0u}; iScore < dmeson.mlProbD0().size(); ++iScore) { scores[iScore] = dmeson.mlProbD0()[iScore]; @@ -289,21 +308,43 @@ struct tableMakerJpsiHf { } } - auto rapD0 = hfHelper.yD0(dmeson); + std::array scores_dplus = {999., -999., -999.}; // Dplus + if constexpr (withBdt && TDmeson == DmesonType::Dplus) { + if (dmeson.mlProbDplusToPiKPi().size() == 3) { + for (auto iScore{0u}; iScore < dmeson.mlProbDplusToPiKPi().size(); ++iScore) { + scores_dplus[iScore] = dmeson.mlProbDplusToPiKPi()[iScore]; + } + } + } - if (yCandDmesonMax >= 0. && std::abs(rapD0) > yCandDmesonMax) { - continue; + double rapDmeson = -999.; + if constexpr (TDmeson == DmesonType::D0) { + rapDmeson = HfHelper::yD0(dmeson); + } else if constexpr (TDmeson == DmesonType::Dplus) { + rapDmeson = HfHelper::yDplus(dmeson); } - if (dmeson.isSelD0() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores[0], fValuesDileptonCharmHadron); - fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); - VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + if (yCandDmesonMax >= 0. && std::abs(rapDmeson) > yCandDmesonMax) { + continue; } - if (dmeson.isSelD0bar() >= 1) { - VarManager::FillSingleDileptonCharmHadron(dmeson, hfHelper, scores[3], fValuesDileptonCharmHadron); - fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); - VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + + if constexpr (TDmeson == DmesonType::D0) { + if (dmeson.isSelD0() >= 1) { + VarManager::FillSingleDileptonCharmHadron(dmeson, scores[0], fValuesDileptonCharmHadron); + fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + } + if (dmeson.isSelD0bar() >= 1) { + VarManager::FillSingleDileptonCharmHadron(dmeson, scores[3], fValuesDileptonCharmHadron); + fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + } + } else if constexpr (TDmeson == DmesonType::Dplus) { + if (dmeson.isSelDplusToPiKPi() >= 1) { + VarManager::FillSingleDileptonCharmHadron(dmeson, scores_dplus[0], fValuesDileptonCharmHadron); + fHistMan->FillHistClass("Dmeson", fValuesDileptonCharmHadron); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + } } } } @@ -323,15 +364,21 @@ struct tableMakerJpsiHf { if (configDebug) { float dummyScore{-999.f}; - VarManager::FillSingleDileptonCharmHadron(dilepton, hfHelper, dummyScore, fValuesDileptonCharmHadron); + VarManager::FillSingleDileptonCharmHadron(dilepton, dummyScore, fValuesDileptonCharmHadron); fHistMan->FillHistClass("JPsi", fValuesDileptonCharmHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); } // loop over D mesons for (auto const& dmeson : dmesons) { - if (!TESTBIT(dmeson.hfflag(), DecayType::D0ToPiK)) { - continue; + if constexpr (TDmeson == DmesonType::D0) { + if (!TESTBIT(dmeson.hfflag(), hf_cand_2prong::DecayType::D0ToPiK)) { + continue; + } + } else if constexpr (TDmeson == DmesonType::Dplus) { + if (!TESTBIT(dmeson.hfflag(), hf_cand_3prong::DecayType::DplusToPiKPi)) { + continue; + } } int dmesonIdx = dmeson.globalIndex(); @@ -340,16 +387,29 @@ struct tableMakerJpsiHf { isDmesonFilled = true; } - auto rapD0 = hfHelper.yD0(dmeson); + double rapDmeson = -999.; + if constexpr (TDmeson == DmesonType::D0) { + rapDmeson = HfHelper::yD0(dmeson); + } else if constexpr (TDmeson == DmesonType::Dplus) { + rapDmeson = HfHelper::yDplus(dmeson); + } - if (yCandDmesonMax >= 0. && std::abs(rapD0) > yCandDmesonMax) { + if (yCandDmesonMax >= 0. && std::abs(rapDmeson) > yCandDmesonMax) { continue; } auto massD0 = -1.; auto massD0bar = -1.; + auto massDplus = -1.; + + bool isSelectedDmeson{false}; + if constexpr (TDmeson == DmesonType::D0) { + isSelectedDmeson = dmeson.isSelD0() >= 1 || dmeson.isSelD0bar() >= 1; + } else if constexpr (TDmeson == DmesonType::Dplus) { + isSelectedDmeson = dmeson.isSelDplusToPiKPi() >= 1; + } - if (dmeson.isSelD0() >= 1 || dmeson.isSelD0bar() >= 1) { + if (isSelectedDmeson) { if (!isCollSel) { redCollisions(collision.posX(), collision.posY(), collision.posZ(), collision.numContrib()); isCollSel = true; @@ -370,10 +430,16 @@ struct tableMakerJpsiHf { // one table for each daughter with single track variables redDmesDau0(trackProng0.pt(), trackProng0.eta(), trackProng0.itsNCls(), trackProng0.tpcNClsCrossedRows(), dmeson.nSigTpcPi0(), dmeson.nSigTofPi0(), dmeson.nSigTpcKa0(), dmeson.nSigTofKa0()); redDmesDau1(trackProng1.pt(), trackProng1.eta(), trackProng1.itsNCls(), trackProng1.tpcNClsCrossedRows(), dmeson.nSigTpcPi1(), dmeson.nSigTofPi1(), dmeson.nSigTpcKa1(), dmeson.nSigTofKa1()); + + if constexpr (TDmeson == DmesonType::Dplus) { + auto trackProng2 = dmeson.template prong2_as(); + redDmesDau2(trackProng2.pt(), trackProng2.eta(), trackProng2.itsNCls(), trackProng2.tpcNClsCrossedRows(), dmeson.nSigTpcPi2(), dmeson.nSigTofPi2(), dmeson.nSigTpcKa2(), dmeson.nSigTofKa2()); + } filledDmesonIds.push_back(dmesonIdx); } + std::array scores = {999., -999., -999., 999., -999., -999.}; // D0 + D0bar - if constexpr (withBdt) { + if constexpr (withBdt && TDmeson == DmesonType::D0) { if (!isDmesonFilled) { if (dmeson.mlProbD0().size() == 3) { for (auto iScore{0u}; iScore < dmeson.mlProbD0().size(); ++iScore) { @@ -389,20 +455,44 @@ struct tableMakerJpsiHf { } } - if (dmeson.isSelD0() >= 1) { - massD0 = hfHelper.invMassD0ToPiK(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores[0], fValuesDileptonCharmHadron); - fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); - VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); - } - if (dmeson.isSelD0bar() >= 1) { - massD0bar = hfHelper.invMassD0barToKPi(dmeson); - VarManager::FillDileptonCharmHadron(dilepton, dmeson, hfHelper, scores[3], fValuesDileptonCharmHadron); - fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); - VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + std::array scores_dplus = {999., -999., -999.}; + if constexpr (withBdt && TDmeson == DmesonType::Dplus) { + if (!isDmesonFilled) { + if (dmeson.mlProbDplusToPiKPi().size() == 3) { + for (auto iScore{0u}; iScore < dmeson.mlProbDplusToPiKPi().size(); ++iScore) { + scores_dplus[iScore] = dmeson.mlProbDplusToPiKPi()[iScore]; + } + } + redDplusBdts(scores_dplus[0], scores_dplus[1], scores_dplus[2]); + } } - if (!isDmesonFilled) { - redD0Masses(massD0, massD0bar); + + if constexpr (TDmeson == DmesonType::D0) { + if (dmeson.isSelD0() >= 1) { + massD0 = HfHelper::invMassD0ToPiK(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores[0], fValuesDileptonCharmHadron); + fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + } + if (dmeson.isSelD0bar() >= 1) { + massD0bar = HfHelper::invMassD0barToKPi(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores[3], fValuesDileptonCharmHadron); + fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + } + if (!isDmesonFilled) { + redD0Masses(massD0, massD0bar); + } + } else if constexpr (TDmeson == DmesonType::Dplus) { + if (dmeson.isSelDplusToPiKPi() >= 1) { + massDplus = HfHelper::invMassDplusToPiKPi(dmeson); + VarManager::FillDileptonCharmHadron(dilepton, dmeson, scores_dplus[0], fValuesDileptonCharmHadron); + fHistMan->FillHistClass("JPsiDmeson", fValuesDileptonCharmHadron); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDileptonCharmHadron); + } + if (!isDmesonFilled) { + redDmesMasses(massDplus); + } } } } @@ -416,9 +506,48 @@ struct tableMakerJpsiHf { redCollCounter(collisions.size()); } for (auto const& collision : collisions) { - auto groupedDmesonCandidates = selectedD0Candidates.sliceBy(perCollisionDmeson, collision.globalIndex()); + auto groupedDmesonCandidates = selectedD0Candidates.sliceBy(perCollisionD0, collision.globalIndex()); + auto groupedLeptonCandidates = muonCandidates.sliceBy(perCollisionMuons, collision.globalIndex()); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + } + } + + // process J/psi(->mumu) - D0 adding the BDT output scores to the D0 table + void processJspiToMuMuD0WithBdt(MyEvents const& collisions, MyMuonTracksSelectedWithColl const& muonCandidates, soa::Filtered const& selectedD0Candidates, TracksWithExtra const& barrelTracks) + { + if (storeTableForNorm) { + redCollCounter(collisions.size()); + } + for (auto const& collision : collisions) { + auto groupedDmesonCandidates = selectedD0Candidates.sliceBy(perCollisionD0WithBdt, collision.globalIndex()); + auto groupedLeptonCandidates = muonCandidates.sliceBy(perCollisionMuons, collision.globalIndex()); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + } + } + + // process J/psi(->mumu) - Dplus + void processJspiToMuMuDplus(MyEvents const& collisions, MyMuonTracksSelectedWithColl const& muonCandidates, soa::Filtered const& selectedDplusCandidates, TracksWithExtra const& barrelTracks) + { + if (storeTableForNorm) { + redCollCounter(collisions.size()); + } + for (auto const& collision : collisions) { + auto groupedDmesonCandidates = selectedDplusCandidates.sliceBy(perCollisionDplus, collision.globalIndex()); + auto groupedLeptonCandidates = muonCandidates.sliceBy(perCollisionMuons, collision.globalIndex()); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + } + } + + // process J/psi(->mumu) - Dplus adding the BDT output scores to the D0 table + void processJspiToMuMuDplusWithBdt(MyEvents const& collisions, MyMuonTracksSelectedWithColl const& muonCandidates, soa::Filtered const& selectedDplusCandidates, TracksWithExtra const& barrelTracks) + { + if (storeTableForNorm) { + redCollCounter(collisions.size()); + } + for (auto const& collision : collisions) { + auto groupedDmesonCandidates = selectedDplusCandidates.sliceBy(perCollisionDplusWithBdt, collision.globalIndex()); auto groupedLeptonCandidates = muonCandidates.sliceBy(perCollisionMuons, collision.globalIndex()); - runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); } } @@ -429,42 +558,59 @@ struct tableMakerJpsiHf { redCollCounter(collisions.size()); } for (auto const& collision : collisions) { - auto groupedDmesonCandidates = selectedD0Candidates.sliceBy(perCollisionDmeson, collision.globalIndex()); + auto groupedDmesonCandidates = selectedD0Candidates.sliceBy(perCollisionD0, collision.globalIndex()); auto groupedLeptonCandidates = electronCandidates.sliceBy(perCollisionElectrons, collision.globalIndex()); - runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); } } - // process J/psi(->mumu) - D0 adding the BDT output scores to the D0 table - void processJspiToMuMuD0WithBdt(MyEvents const& collisions, MyMuonTracksSelectedWithColl const& muonCandidates, soa::Filtered const& selectedD0CandidatesWithBdt, TracksWithExtra const& barrelTracks) + // process J/psi(->ee) - D0 adding the BDT output scores to the D0 table + void processJspiToEED0WithBdt(MyEvents const& collisions, MyBarrelTracksSelectedWithColl const& electronCandidates, soa::Filtered const& selectedD0Candidates, TracksWithExtra const& barrelTracks) { if (storeTableForNorm) { redCollCounter(collisions.size()); } for (auto const& collision : collisions) { - auto groupedDmesonCandidates = selectedD0CandidatesWithBdt.sliceBy(perCollisionDmesonWithBdt, collision.globalIndex()); - auto groupedLeptonCandidates = muonCandidates.sliceBy(perCollisionMuons, collision.globalIndex()); - runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + auto groupedDmesonCandidates = selectedD0Candidates.sliceBy(perCollisionD0WithBdt, collision.globalIndex()); + auto groupedLeptonCandidates = electronCandidates.sliceBy(perCollisionElectrons, collision.globalIndex()); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); } } - // process J/psi(->ee) - D0 adding the BDT output scores to the D0 table - void processJspiToEED0WithBdt(MyEvents const& collisions, MyBarrelTracksSelectedWithColl const& electronCandidates, soa::Filtered const& selectedD0CandidatesWithBdt, TracksWithExtra const& barrelTracks) + // process J/psi(->ee) - Dplus adding the BDT output scores to the D0 table + void processJspiToEEDplus(MyEvents const& collisions, MyBarrelTracksSelectedWithColl const& electronCandidates, soa::Filtered const& selectedDplusCandidates, TracksWithExtra const& barrelTracks) + { + if (storeTableForNorm) { + redCollCounter(collisions.size()); + } + for (auto const& collision : collisions) { + auto groupedDmesonCandidates = selectedDplusCandidates.sliceBy(perCollisionDplus, collision.globalIndex()); + auto groupedLeptonCandidates = electronCandidates.sliceBy(perCollisionElectrons, collision.globalIndex()); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + } + } + + // process J/psi(->ee) - Dplus with BDT + void processJspiToEEDplusWithBdt(MyEvents const& collisions, MyBarrelTracksSelectedWithColl const& electronCandidates, soa::Filtered const& selectedDplusCandidates, TracksWithExtra const& barrelTracks) { if (storeTableForNorm) { redCollCounter(collisions.size()); } for (auto const& collision : collisions) { - auto groupedDmesonCandidates = selectedD0CandidatesWithBdt.sliceBy(perCollisionDmesonWithBdt, collision.globalIndex()); auto groupedLeptonCandidates = electronCandidates.sliceBy(perCollisionElectrons, collision.globalIndex()); - runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); + auto groupedDmesonCandidates = selectedDplusCandidates.sliceBy(perCollisionDplusWithBdt, collision.globalIndex()); + runDileptonDmeson(groupedLeptonCandidates, groupedDmesonCandidates, collision, barrelTracks); } } PROCESS_SWITCH(tableMakerJpsiHf, processJspiToMuMuD0, "Process J/psi(->mumu) - D0", false); PROCESS_SWITCH(tableMakerJpsiHf, processJspiToEED0, "Process J/psi(->ee) - D0", false); PROCESS_SWITCH(tableMakerJpsiHf, processJspiToMuMuD0WithBdt, "Process J/psi(->mumu) - D0 with BDT", false); + PROCESS_SWITCH(tableMakerJpsiHf, processJspiToMuMuDplus, "Process J/psi(->mumu) - Dplus", false); + PROCESS_SWITCH(tableMakerJpsiHf, processJspiToMuMuDplusWithBdt, "Process J/psi(->mumu) - Dplus with BDT", false); PROCESS_SWITCH(tableMakerJpsiHf, processJspiToEED0WithBdt, "Process J/psi(->ee) - D0 with BDT", false); + PROCESS_SWITCH(tableMakerJpsiHf, processJspiToEEDplus, "Process J/psi(->ee) - Dplus", false); + PROCESS_SWITCH(tableMakerJpsiHf, processJspiToEEDplusWithBdt, "Process J/psi(->ee) - Dplus with BDT", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGDQ/TableProducer/tableMakerMC.cxx b/PWGDQ/TableProducer/tableMakerMC.cxx index 8a2d93f9f89..05baa8e279d 100644 --- a/PWGDQ/TableProducer/tableMakerMC.cxx +++ b/PWGDQ/TableProducer/tableMakerMC.cxx @@ -187,7 +187,7 @@ struct TableMakerMC { int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. // TODO: filter on TPC dedx used temporarily until electron PID will be improved - Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == uint8_t(aod::track::Run2Track), aod::track::trackType == uint8_t(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= 0.9f; + Filter barrelSelectedTracks = ifnode(fIsRun2.node() == true, aod::track::trackType == static_cast(aod::track::Run2Track), aod::track::trackType == static_cast(aod::track::Track)) && o2::aod::track::pt >= fConfigBarrelTrackPtLow && nabs(o2::aod::track::eta) <= 0.9f; Filter muonFilter = o2::aod::fwdtrack::pt >= fConfigMuonPtLow; @@ -432,10 +432,10 @@ struct TableMakerMC { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (!fEventCut->IsSelected(VarManager::fgValues)) { continue; @@ -448,10 +448,10 @@ struct TableMakerMC { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMult) > 0 && (TEventFillMap & VarManager::ObjTypes::CollisionCent) > 0) { @@ -578,14 +578,14 @@ struct TableMakerMC { int i = 0; for (auto& cut : fTrackCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", cut.GetName()), VarManager::fgValues); // fill the reconstructed truth if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", cut.GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } i++; } @@ -604,7 +604,7 @@ struct TableMakerMC { trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { if (track.pidbit() & (static_cast(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } } @@ -623,7 +623,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -775,7 +775,7 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } i++; } @@ -823,11 +823,11 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", cut.GetName()), VarManager::fgValues); } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } i++; } @@ -847,7 +847,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -978,11 +978,11 @@ struct TableMakerMC { mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(isig)); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); } } // end loop over labels @@ -1067,10 +1067,10 @@ struct TableMakerMC { // fill stats information, before selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(kNaliases)); if (!fEventCut->IsSelected(VarManager::fgValues)) { continue; @@ -1083,10 +1083,10 @@ struct TableMakerMC { // fill stats information, after selections for (int i = 0; i < kNaliases; i++) { if (triggerAliases & (static_cast(1) << i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(kNaliases)); event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMult) > 0 && (TEventFillMap & VarManager::ObjTypes::CollisionCent) > 0) { @@ -1201,14 +1201,14 @@ struct TableMakerMC { int i = 0; for (auto& cut : fTrackCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); if (fConfigQA) { fHistMan->FillHistClass(Form("TrackBarrel_%s", cut.GetName()), VarManager::fgValues); // fill the reconstructed truth if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_TrackBarrel_%s", cut.GetName()), VarManager::fgValues); } } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } i++; } @@ -1227,7 +1227,7 @@ struct TableMakerMC { trackFilteringTag |= (static_cast(track.pidbit()) << 2); for (int iv0 = 0; iv0 < 5; iv0++) { if (track.pidbit() & (static_cast(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } } @@ -1246,7 +1246,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -1334,7 +1334,7 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); } i++; } @@ -1379,12 +1379,12 @@ struct TableMakerMC { int i = 0; for (auto& cut : fMuonCuts) { if (cut.IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + trackTempFilterMap |= (static_cast(1) << i); fHistMan->FillHistClass(Form("Muons_%s", cut.GetName()), VarManager::fgValues); if (fIsAmbiguous && isAmbiguous == 1) { fHistMan->FillHistClass(Form("Ambiguous_Muons_%s", cut.GetName()), VarManager::fgValues); } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } i++; } @@ -1404,7 +1404,7 @@ struct TableMakerMC { if (fDoDetailedQA) { j = 0; for (auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { + if (trackTempFilterMap & (static_cast(1) << j)) { fHistMan->FillHistClass(Form("Muons_%s_%s", cut.GetName(), sig.GetName()), VarManager::fgValues); // fill the reconstructed truth } j++; @@ -1517,11 +1517,11 @@ struct TableMakerMC { mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(isig)); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); } } // end loop over labels @@ -1534,7 +1534,7 @@ struct TableMakerMC { fEventLabels.clear(); } - void DefineHistograms(TString histClasses) + void DefineHistograms(const TString& histClasses) { std::unique_ptr objArray(histClasses.Tokenize(";")); for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { @@ -1576,7 +1576,7 @@ struct TableMakerMC { fStatsList.setObject(new TList()); fStatsList->SetOwner(kTRUE); std::vector eventLabels{"BCs", "Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, (float)kNaliases + 1, -0.5, (float)kNaliases + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, static_cast(kNaliases) + 1, -0.5, static_cast(kNaliases) + 0.5); int ib = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); @@ -1767,10 +1767,10 @@ struct TableMakerMC { { for (int i = 0; i < kNaliases; i++) { if (bc.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(kNaliases)); } PROCESS_SWITCH(TableMakerMC, processFull, "Produce both barrel and muon skims", false); diff --git a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx index 39cee24788c..838d7e79af3 100644 --- a/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -64,9 +65,10 @@ #include +#include +#include #include #include -#include #include #include #include @@ -74,9 +76,6 @@ #include #include -using std::cout; -using std::endl; - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -102,9 +101,9 @@ using MyBarrelTracksWithCov = soa::Join; -using MyMuons = soa::Join; -using MyMuonsWithCov = soa::Join; -using MyMuonsRealignWithCov = soa::Join; +using MyMuons = soa::Join; +using MyMuonsWithCov = soa::Join; +using MyMuonsRealignWithCov = soa::Join; using MyEvents = soa::Join; using MyEventsWithMults = soa::Join; @@ -114,12 +113,14 @@ using MyEventsWithCentAndMults = soa::Join; using MyEventsMcWithMults = soa::Join; +using MyBCs = soa::Join; + // Declare bit maps containing information on the table joins content (used as argument in templated functions) constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision; constexpr static uint32_t gkEventFillMapWithMults = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra; // constexpr static uint32_t gkEventFillMapWithCent = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent; constexpr static uint32_t gkEventFillMapWithCentAndMults = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::CollisionMultExtra; -constexpr static uint32_t gkEventFillMapWithMultsRapidityGapFilter = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::RapidityGapFilter; +constexpr static uint32_t gkEventFillMapWithMultsRapidityGapFilterFit = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::RapidityGapFilter | VarManager::ObjTypes::Fit; constexpr static uint32_t gkEventMcFillMap = VarManager::ObjTypes::CollisionMC; constexpr static uint32_t gkEventMcFillMapWithCent = VarManager::ObjTypes::CollisionMC | VarManager::ObjTypes::CollisionCent; // constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackPID; @@ -134,13 +135,22 @@ constexpr static uint32_t gkMuonRealignFillMapWithCov = VarManager::ObjTypes::Mu constexpr static uint32_t gkMFTFillMap = VarManager::ObjTypes::TrackMFT; constexpr static uint32_t gkMFTCovFillMap = VarManager::ObjTypes::TrackMFT | VarManager::ObjTypes::MFTCov; +namespace dqtablemakermc_helpers +{ +inline float* varValues() { return static_cast(VarManager::fgValues); } +inline TString* varNames() { return static_cast(VarManager::fgVariableNames); } +inline TString* varUnits() { return static_cast(VarManager::fgVariableUnits); } +} // namespace dqtablemakermc_helpers + +/* template void PrintBitMap(TMap map, int nbits) { for (int i = 0; i < nbits; i++) { - cout << ((map & (TMap(1) << i)) > 0 ? "1" : "0"); + LOG(info) << ((map & (TMap(1) << i)) > 0 ? "1" : "0"); } } +*/ struct TableMakerMC { @@ -151,6 +161,7 @@ struct TableMakerMC { Produces eventExtended; Produces eventVtxCov; Produces eventInfo; + Produces fit; Produces multPV; Produces multAll; Produces eventMClabels; @@ -177,7 +188,7 @@ struct TableMakerMC { OutputObj fOutputList{"output"}; OutputObj fStatsList{"Statistics"}; //! skimming statistics - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; Configurable fIsRun2{"cfgIsRun2", false, "Whether we analyze Run-2 or Run-3 data"}; @@ -227,9 +238,6 @@ struct TableMakerMC { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable fGrpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fZShiftPath{"zShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; - Configurable fUseRemoteZShift{"cfgUseRemoteZShift", false, "Enable getting Zshift from ccdb"}; - Configurable fManualZShift{"cfgManualZShift", 0.f, "Manual value for the Zshift for muons."}; Configurable fGrpMagPathRun2{"grpmagPathRun2", "GLO/GRP/GRP", "CCDB path of the GRPObject (Usage for Run 2)"}; Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; } fConfigCCDB; @@ -250,18 +258,18 @@ struct TableMakerMC { Configurable> fModelNames{"cfgModelNames", std::vector{"model.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; } fConfigVariousOptions; - Service fCCDB; + Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; o2::parameters::GRPObject* fGrpMagRun2 = nullptr; // for run 2, we access the GRPObject from GLO/GRP/GRP o2::parameters::GRPMagField* fGrpMag = nullptr; // for run 3, we access GRPMagField from GLO/Config/GRPMagField - AnalysisCompositeCut* fEventCut; //! Event selection cut + AnalysisCompositeCut* fEventCut = nullptr; //! Event selection cut std::vector fTrackCuts; //! Barrel track cuts std::vector fMuonCuts; //! Muon track cuts bool fDoDetailedQA = false; // Bool to set detailed QA true, if QA is set true - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. // list of MCsignal objects std::vector fMCSignals; @@ -280,7 +288,7 @@ struct TableMakerMC { o2::analysis::MlResponseMFTMuonMatch matchingMlResponse; std::vector binsPtMl; - std::array cutValues; + std::array cutValues{}; std::vector cutDirMl; // RCT flag checker @@ -334,7 +342,7 @@ struct TableMakerMC { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablemakermc_helpers::varNames(), dqtablemakermc_helpers::varUnits()); // Only use detailed QA when QA is set true if (fConfigHistOutput.fConfigQA && fConfigHistOutput.fConfigDetailedQA) { @@ -360,7 +368,7 @@ struct TableMakerMC { } // Barrel track histograms after cuts; one directory per cut if (fConfigHistOutput.fConfigQA) { - for (auto& cut : fTrackCuts) { + for (const auto& cut : fTrackCuts) { histClasses += Form("TrackBarrel_%s;", cut->GetName()); } } @@ -373,7 +381,7 @@ struct TableMakerMC { } // Muon track histograms after cuts; one directory per cut if (fConfigHistOutput.fConfigQA) { - for (auto& muonCut : fMuonCuts) { + for (const auto& muonCut : fMuonCuts) { histClasses += Form("Muons_%s;", muonCut->GetName()); } } @@ -386,7 +394,7 @@ struct TableMakerMC { // loop over MC signals and add them to the signals array for (int isig = 0; isig < objArray->GetEntries(); ++isig) { MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objArray->At(isig)->GetName()); - if (sig) { + if (sig != nullptr) { fMCSignals.push_back(sig); } } @@ -395,27 +403,27 @@ struct TableMakerMC { TString addMCSignalsStr = fConfigMCSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { - if (mcIt) { + for (const auto& mcIt : addMCSignals) { + if (mcIt != nullptr) { fMCSignals.push_back(mcIt); } } } - for (auto& mcIt : fMCSignals) { + for (const auto& mcIt : fMCSignals) { if (fConfigHistOutput.fConfigQA) { histClasses += Form("MCTruth_%s;", mcIt->GetName()); } if (fDoDetailedQA) { if (isBarrelEnabled) { // in case of detailed QA, setup histogram directories for each combination of reconstructed track cuts and MC signals - for (auto& cut : fTrackCuts) { + for (const auto& cut : fTrackCuts) { histClasses += Form("TrackBarrel_%s_%s;", cut->GetName(), mcIt->GetName()); } } if (isMuonEnabled) { // in case of detailed QA, setup histogram directories for each combination of reconstructed muon cuts and MC signals - for (auto& cut : fMuonCuts) { + for (const auto& cut : fMuonCuts) { histClasses += Form("Muons_%s_%s;", cut->GetName(), mcIt->GetName()); } } @@ -469,7 +477,7 @@ struct TableMakerMC { TString addEvCutsStr = fConfigCuts.fConfigEventCutsJSON.value; if (addEvCutsStr != "") { std::vector addEvCuts = dqcuts::GetCutsFromJSON(addEvCutsStr.Data()); - for (auto& cutIt : addEvCuts) { + for (const auto& cutIt : addEvCuts) { fEventCut->AddCut(cutIt); } } @@ -486,8 +494,8 @@ struct TableMakerMC { TString addTrackCutsStr = fConfigCuts.fConfigTrackCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + for (const auto& t : addTrackCuts) { + fTrackCuts.push_back(dynamic_cast(t)); } } @@ -503,8 +511,8 @@ struct TableMakerMC { TString addMuonCutsStr = fConfigCuts.fConfigMuonCutsJSON.value; if (addMuonCutsStr != "") { std::vector addMuonCuts = dqcuts::GetCutsFromJSON(addMuonCutsStr.Data()); - for (auto& t : addMuonCuts) { - fMuonCuts.push_back(reinterpret_cast(t)); + for (const auto& t : addMuonCuts) { + fMuonCuts.push_back(dynamic_cast(t)); } } @@ -525,11 +533,11 @@ struct TableMakerMC { VarManager::ResetValues(0, VarManager::kNVars); // Loop over MC collisions - for (auto& mcCollision : mcCollisions) { + for (const auto& mcCollision : mcCollisions) { // Get MC collision information into the VarManager VarManager::FillEvent(mcCollision); // Fill histograms - fHistMan->FillHistClass("Event_MCTruth", VarManager::fgValues); + fHistMan->FillHistClass("Event_MCTruth", dqtablemakermc_helpers::varValues()); // Create the skimmed table entry for this collision eventMC(mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.t(), mcCollision.weight(), mcCollision.impactParameter(), mcCollision.bestCollisionCentFT0C(), @@ -549,14 +557,14 @@ struct TableMakerMC { fLabelsMapReversed.clear(); fMCFlags.clear(); - uint16_t mcflags = static_cast(0); // flags which will hold the decisions for each MC signal + auto mcflags = static_cast(0); // flags which will hold the decisions for each MC signal int trackCounter = 0; - for (auto& mctrack : mcTracks) { + for (const auto& mctrack : mcTracks) { // check all the requested MC signals and fill the decision bit map mcflags = 0; int i = 0; - for (auto& sig : fMCSignals) { + for (const auto& sig : fMCSignals) { bool checked = false; if constexpr (soa::is_soa_filtered_v) { auto mctrack_raw = mcTracks.rawIteratorAt(mctrack.globalIndex()); @@ -573,17 +581,16 @@ struct TableMakerMC { /*if ((std::abs(mctrack.pdgCode())>400 && std::abs(mctrack.pdgCode())<599) || (std::abs(mctrack.pdgCode())>4000 && std::abs(mctrack.pdgCode())<5999) || (mcflags > 0)) { - cout << ">>>>>>>>>>>>>>>>>>>>>>> track idx / pdg / process / status code / HEPMC status / primary : " - << mctrack.globalIndex() << " / " << mctrack.pdgCode() << " / " - << mctrack.getProcess() << " / " << mctrack.getGenStatusCode() << " / " << mctrack.getHepMCStatusCode() << " / " << mctrack.isPhysicalPrimary() << endl; - cout << ">>>>>>>>>>>>>>>>>>>>>>> track bitmap: "; + LOG(info) << ">>>>>>>>>>>>>>>>>>>>>> track idx / pdg / process / status code / HEPMC status / primary : " + << mctrack.globalIndex() << " / " << mctrack.pdgCode() << " / " + << mctrack.getProcess() << " / " << mctrack.getGenStatusCode() << " / " << mctrack.getHepMCStatusCode() << " / " << mctrack.isPhysicalPrimary(); + LOG(info) << ">>>>>>>>>>>>>>>>>>>>>> track bitmap: "; PrintBitMap(mcflags, 16); - cout << endl; if (mctrack.has_mothers()) { - for (auto& m : mctrack.mothersIds()) { + for (const auto& m : mctrack.mothersIds()) { if (m < mcTracks.size()) { // protect against bad mother indices auto aMother = mcTracks.rawIteratorAt(m); - cout << "<<<<<< mother idx / pdg: " << m << " / " << aMother.pdgCode() << endl; + LOG(info) << "<<<<<< mother idx / pdg: " << m << " / " << aMother.pdgCode(); } } } @@ -593,7 +600,7 @@ struct TableMakerMC { if (d < mcTracks.size()) { // protect against bad daughter indices auto aDaughter = mcTracks.rawIteratorAt(d); - cout << "<<<<<< daughter idx / pdg: " << d << " / " << aDaughter.pdgCode() << endl; + LOG(info) << "<<<<<< daughter idx / pdg: " << d << " / " << aDaughter.pdgCode(); } } } @@ -605,7 +612,7 @@ struct TableMakerMC { } // If this MC track was not already added to the map, add it now - if (fLabelsMap.find(mctrack.globalIndex()) == fLabelsMap.end()) { + if (!fLabelsMap.contains(mctrack.globalIndex())) { fLabelsMap[mctrack.globalIndex()] = trackCounter; fLabelsMapReversed[trackCounter] = mctrack.globalIndex(); fMCFlags[mctrack.globalIndex()] = mcflags; @@ -618,8 +625,8 @@ struct TableMakerMC { VarManager::FillEvent(mcCollision); int j = 0; for (auto signal = fMCSignals.begin(); signal != fMCSignals.end(); signal++, j++) { - if (mcflags & (static_cast(1) << j)) { - fHistMan->FillHistClass(Form("MCTruth_%s", (*signal)->GetName()), VarManager::fgValues); + if ((mcflags & (static_cast(1) << j)) != 0) { + fHistMan->FillHistClass(Form("MCTruth_%s", (*signal)->GetName()), dqtablemakermc_helpers::varValues()); } } } @@ -693,8 +700,10 @@ struct TableMakerMC { } } - template - void skimCollisions(TEvents const& collisions, BCsWithTimestamps const& /*bcs*/) + template + void skimCollisions(TEvents const& collisions, TBCs const& bcs, + TFt0s const& ft0s, TFv0as const& fv0as, TFdds const& fdds) { // Skim reconstructed collisions which are selected by the user specified cuts @@ -721,17 +730,17 @@ struct TableMakerMC { // Fill the stats event histogram with the event selection bits for (int i = 0; i < o2::aod::evsel::kNsel; i++) { if (collision.selection_bit(i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(1.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(0)))->Fill(1.0, static_cast(o2::aod::evsel::kNsel)); - auto bc = collision.template bc_as(); + auto bc = collision.template bc_as(); // store the selection decisions - uint64_t tag = static_cast(0); + auto tag = static_cast(0); // store some more information in the tag // if the BC found by event selection does not coincide with the collision.bc(), toggle the first bit - auto bcEvSel = collision.template foundBC_as(); + auto bcEvSel = collision.template foundBC_as(); if (bcEvSel.globalIndex() != bc.globalIndex()) { tag |= (static_cast(1) << 0); } @@ -747,34 +756,47 @@ struct TableMakerMC { if (collision.has_mcCollision()) { auto mcCollision = collision.template mcCollision_as(); VarManager::FillEvent(mcCollision); + // Fill FIT info using newbc pattern for UPC events (similar to ZDC) + if constexpr ((TEventFillMap & VarManager::ObjTypes::Fit) > 0) { + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + if constexpr ((TEventFillMap & VarManager::ObjTypes::RapidityGapFilter) > 0) { + auto newbc = bcs.rawIteratorAt(collision.newBcIndex()); + VarManager::FillFIT(newbc, bcs, ft0s, fv0as, fdds); + } else { + VarManager::FillFIT(bcEvSel, bcs, ft0s, fv0as, fdds); + } + } + } } if (fDoDetailedQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_BeforeCuts", dqtablemakermc_helpers::varValues()); } // fill stats information, before selections for (int i = 0; i < o2::aod::evsel::kNsel; i++) { if (collision.selection_bit(i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(2.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(0)))->Fill(2.0, static_cast(o2::aod::evsel::kNsel)); // Apply the user specified event selection - if (!fEventCut->IsSelected(VarManager::fgValues) || (fConfigRCT.fConfigUseRCT.value && !(rctChecker(collision)))) { + if (!fEventCut->IsSelected(dqtablemakermc_helpers::varValues()) || (fConfigRCT.fConfigUseRCT.value && !(rctChecker(collision)))) { continue; } // fill stats information, after selections for (int i = 0; i < o2::aod::evsel::kNsel; i++) { if (collision.selection_bit(i)) { - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(3.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(0)))->Fill(3.0, static_cast(o2::aod::evsel::kNsel)); // Fill historams after event cuts - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", dqtablemakermc_helpers::varValues()); // create the event tables event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); @@ -801,6 +823,30 @@ struct TableMakerMC { eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); eventMClabels(collision.mcCollisionId(), collision.mcMask()); eventInfo(collision.globalIndex()); + // Fill FIT table if requested + if constexpr ((TEventFillMap & VarManager::ObjTypes::Fit) > 0) { + fit(VarManager::fgValues[VarManager::kAmplitudeFT0A], VarManager::fgValues[VarManager::kAmplitudeFT0C], + VarManager::fgValues[VarManager::kTimeFT0A], VarManager::fgValues[VarManager::kTimeFT0C], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFT0]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0A]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0C]), + VarManager::fgValues[VarManager::kAmplitudeFDDA], VarManager::fgValues[VarManager::kAmplitudeFDDC], + VarManager::fgValues[VarManager::kTimeFDDA], VarManager::fgValues[VarManager::kTimeFDDC], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFDD]), + VarManager::fgValues[VarManager::kAmplitudeFV0A], VarManager::fgValues[VarManager::kTimeFV0A], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFV0A]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFV0A]), + static_cast(VarManager::fgValues[VarManager::kBBFT0Apf]), + static_cast(VarManager::fgValues[VarManager::kBGFT0Apf]), + static_cast(VarManager::fgValues[VarManager::kBBFT0Cpf]), + static_cast(VarManager::fgValues[VarManager::kBGFT0Cpf]), + static_cast(VarManager::fgValues[VarManager::kBBFV0Apf]), + static_cast(VarManager::fgValues[VarManager::kBGFV0Apf]), + static_cast(VarManager::fgValues[VarManager::kBBFDDApf]), + static_cast(VarManager::fgValues[VarManager::kBGFDDApf]), + static_cast(VarManager::fgValues[VarManager::kBBFDDCpf]), + static_cast(VarManager::fgValues[VarManager::kBGFDDCpf])); + } if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMultExtra) > 0) { multPV(collision.multNTracksHasITS(), collision.multNTracksHasTPC(), collision.multNTracksHasTOF(), collision.multNTracksHasTRD(), collision.multNTracksITSOnly(), collision.multNTracksTPCOnly(), collision.multNTracksITSTPC(), @@ -836,9 +882,9 @@ struct TableMakerMC { // so in case of multiple associations, the variables depending on the collision association (e.g. DCA, secondary vertexing, etc) // have to be recomputed at analysis time for each association. - uint64_t trackFilteringTag = static_cast(0); - uint32_t trackTempFilterMap = static_cast(0); - uint16_t mcflags = static_cast(0); + uint64_t trackFilteringTag{0}; + uint32_t trackTempFilterMap{0}; + uint16_t mcflags{0}; int trackCounter = fLabelsMap.size(); // Loop over associations @@ -861,18 +907,18 @@ struct TableMakerMC { VarManager::FillTrackCollision(track, collision); } if (fDoDetailedQA) { - fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", dqtablemakermc_helpers::varValues()); } // apply track cuts and fill histograms int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqtablemakermc_helpers::varValues())) { trackTempFilterMap |= (static_cast(1) << i); if (fConfigHistOutput.fConfigQA) { - fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), dqtablemakermc_helpers::varValues()); } - (reinterpret_cast(fStatsList->At(1)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(1)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -890,8 +936,8 @@ struct TableMakerMC { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag |= static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(1)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } } // end if V0Bits @@ -939,24 +985,24 @@ struct TableMakerMC { VarManager::FillTrackMC(mcTracks, mctrack); mcflags = 0; - int i = 0; // runs over the MC signals - int j = 0; // runs over the track cuts + int isig = 0; // runs over the MC signals + int j = 0; // runs over the track cuts // check all the specified signals and fill histograms for MC truth matched tracks - for (auto& sig : fMCSignals) { + for (const auto& sig : fMCSignals) { if (sig->CheckSignal(true, mctrack)) { - mcflags |= (static_cast(1) << i); + mcflags |= (static_cast(1) << isig); // If detailed QA is on, fill histograms for each MC signal and track cut combination if (fDoDetailedQA) { j = 0; - for (auto& cut : fTrackCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { - fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), VarManager::fgValues); // fill the reconstructed truth + for (const auto& cut : fTrackCuts) { + if (trackTempFilterMap & (static_cast(1) << j)) { + fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", cut->GetName(), sig->GetName()), dqtablemakermc_helpers::varValues()); // fill the reconstructed truth } j++; } } } - i++; + isig++; } // if the MC truth particle corresponding to this reconstructed track is not already written, @@ -979,7 +1025,7 @@ struct TableMakerMC { { // Skim MFT tracks // So far no cuts are applied here - uint16_t mcflags = static_cast(0); + auto mcflags = static_cast(0); int trackCounter = fLabelsMap.size(); for (const auto& assoc : mftAssocs) { @@ -987,7 +1033,7 @@ struct TableMakerMC { if (fConfigHistOutput.fConfigQA) { VarManager::FillTrack(track); - fHistMan->FillHistClass("MftTracks", VarManager::fgValues); + fHistMan->FillHistClass("MftTracks", dqtablemakermc_helpers::varValues()); } // write the MFT track global index in the map for skimming (to make sure we have it just once) @@ -1007,12 +1053,12 @@ struct TableMakerMC { mcflags = 0; int i = 0; // runs over the MC signals // check all the specified signals and fill histograms for MC truth matched tracks - for (auto& sig : fMCSignals) { + for (const auto& sig : fMCSignals) { if (sig->CheckSignal(true, mctrack)) { mcflags |= (static_cast(1) << i); // If detailed QA is on, fill histograms for each MC signal and track cut combination if (fDoDetailedQA) { - fHistMan->FillHistClass(Form("MFTTrack_%s", sig->GetName()), VarManager::fgValues); // fill the reconstructed truth + fHistMan->FillHistClass(Form("MFTTrack_%s", sig->GetName()), dqtablemakermc_helpers::varValues()); // fill the reconstructed truth } } i++; @@ -1041,12 +1087,6 @@ struct TableMakerMC { if (static_cast(muon.trackType()) < 2) { auto muonID = muon.matchMCHTrackId(); auto chi2 = muon.chi2MatchMCHMFT(); - if (fConfigVariousOptions.fUseML.value) { - std::vector output; - std::vector inputML = matchingMlResponse.getInputFeaturesTest(muon); - matchingMlResponse.isSelectedMl(inputML, 0, output); - chi2 = output[0]; - } if (mCandidates.find(muonID) == mCandidates.end()) { mCandidates[muonID] = {chi2, muon.globalIndex()}; } else { @@ -1056,7 +1096,7 @@ struct TableMakerMC { } } } - for (auto& pairCand : mCandidates) { + for (const auto& pairCand : mCandidates) { fBestMatch[pairCand.second.second] = true; } } @@ -1078,7 +1118,7 @@ struct TableMakerMC { muonprop = VarManager::PropagateMuon(muontrack, collision, VarManager::kToMatching); } std::vector output; - std::vector inputML = matchingMlResponse.getInputFeaturesGlob(muon, muonprop, mftprop, collision); + std::vector inputML = matchingMlResponse.getInputFeatures(muon, mfttrack, muontrack, mftprop, muonprop, collision); matchingMlResponse.isSelectedMl(inputML, 0, output); float score = output[0]; if (mCandidates.find(muonID) == mCandidates.end()) { @@ -1090,7 +1130,7 @@ struct TableMakerMC { } } } - for (auto& pairCand : mCandidates) { + for (const auto& pairCand : mCandidates) { fBestMatch[pairCand.second.second] = true; } } @@ -1102,10 +1142,10 @@ struct TableMakerMC { // Loop over the collision-track associations, recompute track properties depending on the collision assigned, and apply track cuts for selection // Muons are written only once, even if they constribute to more than one association, // which means that in the case of multiple associations, the track parameters are wrong and should be computed again at analysis time. - uint8_t trackFilteringTag = static_cast(0); - uint8_t trackTempFilterMap = static_cast(0); + auto trackFilteringTag = static_cast(0); + auto trackTempFilterMap = static_cast(0); fFwdTrackIndexMapReversed.clear(); - uint16_t mcflags = static_cast(0); + auto mcflags = static_cast(0); int trackCounter = fLabelsMap.size(); uint32_t offset = muonBasic.lastIndex(); @@ -1119,8 +1159,8 @@ struct TableMakerMC { } } - trackFilteringTag = uint8_t(0); - trackTempFilterMap = uint8_t(0); + trackFilteringTag = static_cast(0); + trackTempFilterMap = static_cast(0); if constexpr (static_cast(TMuonFillMap & VarManager::ObjTypes::MuonRealign)) { // Check refit flag in case of realigned muons @@ -1132,39 +1172,46 @@ struct TableMakerMC { VarManager::FillTrack(muon); // NOTE: If a muon is associated to multiple collisions, depending on the selections, // it may be accepted for some associations and rejected for other - if (fConfigVariousOptions.fPropMuon) { + if (static_cast(muon.trackType()) > 2 && fConfigVariousOptions.fPropMuon) { VarManager::FillPropagateMuon(muon, collision); } - // recalculte pDca and global muon kinematics - if (static_cast(muon.trackType()) < 2 && fConfigVariousOptions.fRefitGlobalMuon) { + // recalculate pDca / DCA and global muon kinematics + // kMuonPDca is always taken from MCH (standalone or the MCH matched to a global) + if (static_cast(muon.trackType()) <= 2) { auto muontrack = muon.template matchMCHTrack_as(); - if (muontrack.eta() < fConfigVariousOptions.fMuonMatchEtaMin || muontrack.eta() > fConfigVariousOptions.fMuonMatchEtaMax) { - continue; - } - auto mfttrack = muon.template matchMFTTrack_as(); VarManager::FillTrackCollision(muontrack, collision); - if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { - auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); - VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + if (fConfigVariousOptions.fRefitGlobalMuon) { + if (muontrack.eta() < fConfigVariousOptions.fMuonMatchEtaMin || muontrack.eta() > fConfigVariousOptions.fMuonMatchEtaMax) { + continue; + } + auto mfttrack = muon.template matchMFTTrack_as(); + // Helix DCA (kMuonDCAx/y) is filled from the refitted parameters inside FillGlobalMuonRefit(Cov) + if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { + auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); + VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + } else { + VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + } } else { - VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + // Helix DCA of the global track; leaves kMuonPDca from the matched MCH above + VarManager::FillTrackCollision(muon, collision); } } else { VarManager::FillTrackCollision(muon, collision); } if (fDoDetailedQA) { - fHistMan->FillHistClass("Muons_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Muons_BeforeCuts", dqtablemakermc_helpers::varValues()); } // check the cuts and fill histograms for each fulfilled cut int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { - trackTempFilterMap |= (uint8_t(1) << i); + if ((*cut)->IsSelected(dqtablemakermc_helpers::varValues())) { + trackTempFilterMap |= (static_cast(1) << i); if (fConfigHistOutput.fConfigQA) { - fHistMan->FillHistClass(Form("Muons_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("Muons_%s", (*cut)->GetName()), dqtablemakermc_helpers::varValues()); } - (reinterpret_cast(fStatsList->At(2)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(2)))->Fill(static_cast(i)); } } @@ -1192,23 +1239,23 @@ struct TableMakerMC { VarManager::FillTrackMC(mcTracks, mctrack); mcflags = 0; - int i = 0; // runs over the MC signals - int j = 0; // runs over the track cuts + int isig = 0; // runs over the MC signals + int j = 0; // runs over the track cuts // check all the specified signals and fill histograms for MC truth matched tracks - for (auto& sig : fMCSignals) { + for (const auto& sig : fMCSignals) { if (sig->CheckSignal(true, mctrack)) { - mcflags |= (static_cast(1) << i); + mcflags |= (static_cast(1) << isig); if (fDoDetailedQA) { j = 0; - for (auto& cut : fMuonCuts) { - if (trackTempFilterMap & (uint8_t(1) << j)) { - fHistMan->FillHistClass(Form("Muons_%s_%s", cut->GetName(), sig->GetName()), VarManager::fgValues); // fill the reconstructed truth + for (const auto& cut : fMuonCuts) { + if (trackTempFilterMap & (static_cast(1) << j)) { + fHistMan->FillHistClass(Form("Muons_%s_%s", cut->GetName(), sig->GetName()), dqtablemakermc_helpers::varValues()); // fill the reconstructed truth } j++; } } // end if do detailed QA } - i++; + isig++; } // end loop over MC signals // if the MC truth particle corresponding to this reconstructed muon is not already written, @@ -1244,7 +1291,7 @@ struct TableMakerMC { // So all the information which pertains to collision association or MFT associations should not be taken from the skimmed data, but recomputed at analysis time. uint32_t mchIdx = -1; uint32_t mftIdx = -1; - if (muon.trackType() == uint8_t(0) || muon.trackType() == uint8_t(2)) { // MCH-MID (2) or global (0) + if (muon.trackType() == static_cast(0) || muon.trackType() == static_cast(2)) { // MCH-MID (2) or global (0) if (fFwdTrackIndexMap.find(muon.matchMCHTrackId()) != fFwdTrackIndexMap.end()) { mchIdx = fFwdTrackIndexMap[muon.matchMCHTrackId()]; } @@ -1261,21 +1308,28 @@ struct TableMakerMC { } VarManager::FillTrack(muon); - if (fConfigVariousOptions.fPropMuon) { + if (static_cast(muon.trackType()) > 2 && fConfigVariousOptions.fPropMuon) { VarManager::FillPropagateMuon(muon, collision); } - // recalculte pDca and global muon kinematics + // recalculate pDca / DCA and global muon kinematics + // kMuonPDca is always taken from MCH (standalone or the MCH matched to a global) int globalClusters = muon.nClusters(); - if (static_cast(muon.trackType()) < 2 && fConfigVariousOptions.fRefitGlobalMuon) { + if (static_cast(muon.trackType()) <= 2) { auto muontrack = muon.template matchMCHTrack_as(); - auto mfttrack = muon.template matchMFTTrack_as(); - globalClusters += mfttrack.nClusters(); VarManager::FillTrackCollision(muontrack, collision); - if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { - auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); - VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + if (fConfigVariousOptions.fRefitGlobalMuon) { + auto mfttrack = muon.template matchMFTTrack_as(); + globalClusters += mfttrack.nClusters(); + // Helix DCA (kMuonDCAx/y) is filled from the refitted parameters inside FillGlobalMuonRefit(Cov) + if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { + auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); + VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + } else { + VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + } } else { - VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + // Helix DCA of the global track; leaves kMuonPDca from the matched MCH above + VarManager::FillTrackCollision(muon, collision); } } else { VarManager::FillTrackCollision(muon, collision); @@ -1302,16 +1356,17 @@ struct TableMakerMC { } // end loop over selected muons } // end skimMuons - template - void fullSkimming(TEvents const& collisions, BCsWithTimestamps const& bcs, + template + void fullSkimming(TEvents const& collisions, TBCs const& bcs, TTracks const& tracksBarrel, TMuons const& muons, TMFTTracks const& mftTracks, TTrackAssoc const& trackAssocs, TFwdTrackAssoc const& fwdTrackAssocs, TMFTTrackAssoc const& mftAssocs, - MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles, TMFTCov const& mftCovs) + MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles, TMFTCov const& mftCovs, + TFt0s const& ft0s, TFv0as const& fv0as, TFdds const& fdds) { // Check whether the run changed and update CCDB if it did if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { - if (fIsRun2 == true) { + if (fIsRun2) { fGrpMagRun2 = fCCDB->getForTimeStamp(fConfigCCDB.fGrpMagPathRun2, bcs.begin().timestamp()); if (fGrpMagRun2 != nullptr) { o2::base::Propagator::initFieldFromGRP(fGrpMagRun2); @@ -1322,16 +1377,6 @@ struct TableMakerMC { o2::base::Propagator::initFieldFromGRP(fGrpMag); VarManager::SetMagneticField(fGrpMag->getNominalL3Field()); } - if (fConfigCCDB.fUseRemoteZShift) { - auto* fZShift = fCCDB->getForTimeStamp>(fConfigCCDB.fZShiftPath, bcs.begin().timestamp()); - if (fZShift != nullptr && !fZShift->empty()) { - VarManager::SetZShift((*fZShift)[0]); - } else { - LOG(fatal) << "Could not retrieve Z-shift value from CCDB"; - } - } else { - VarManager::SetZShift(fConfigCCDB.fManualZShift.value); - } if (fConfigVariousOptions.fPropMuon) { VarManager::SetupMuonMagField(); } @@ -1355,8 +1400,8 @@ struct TableMakerMC { eventVtxCov.reserve(collisions.size()); eventMClabels.reserve(collisions.size()); eventInfo.reserve(collisions.size()); - skimCollisions(collisions, bcs); - if (fCollIndexMap.size() == 0) { + skimCollisions(collisions, bcs, ft0s, fv0as, fdds); + if (fCollIndexMap.empty()) { return; } @@ -1374,7 +1419,7 @@ struct TableMakerMC { trackBarrel.reserve(tracksBarrel.size()); trackBarrelCov.reserve(tracksBarrel.size()); trackBarrelPID.reserve(tracksBarrel.size()); - trackBarrelAssoc.reserve(tracksBarrel.size()); + trackBarrelAssoc.reserve(trackAssocs.size()); trackBarrelLabels.reserve(tracksBarrel.size()); } @@ -1384,7 +1429,7 @@ struct TableMakerMC { map_mfttrackcovs.clear(); mftTrack.reserve(mftTracks.size()); mftTrackExtra.reserve(mftTracks.size()); - mftAssoc.reserve(mftTracks.size()); + mftAssoc.reserve(mftAssocs.size()); mftLabels.reserve(mftTracks.size()); } @@ -1396,18 +1441,18 @@ struct TableMakerMC { muonBasic.reserve(muons.size()); muonExtra.reserve(muons.size()); muonCov.reserve(muons.size()); - muonAssoc.reserve(muons.size()); + muonAssoc.reserve(fwdTrackAssocs.size()); muonLabels.reserve(muons.size()); } if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { - for (auto& mfttrackConv : mftCovs) { + for (const auto& mfttrackConv : mftCovs) { map_mfttrackcovs[mfttrackConv.matchMFTTrackId()] = mfttrackConv.globalIndex(); } } // loop over selected collisions and select the tracks and fwd tracks to be skimmed - if (fCollIndexMap.size() > 0) { + if (!fCollIndexMap.empty()) { for (auto const& [origIdx, skimIdx] : fCollIndexMap) { auto collision = collisions.rawIteratorAt(origIdx); // group the tracks and muons for this collision @@ -1426,6 +1471,8 @@ struct TableMakerMC { if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { if (fConfigVariousOptions.fUseML.value) { skimBestMuonMatchesML(muons, mftTracks, mftCovs, collision); + } else { + skimBestMuonMatches(muons); } } else { skimBestMuonMatches(muons); @@ -1451,14 +1498,14 @@ struct TableMakerMC { std::vector mothers; if (mctrack.has_mothers()) { - for (auto& m : mctrack.mothersIds()) { + for (const auto& m : mctrack.mothersIds()) { if (m < mcParticles.size()) { // protect against bad mother indices - if (fLabelsMap.find(m) != fLabelsMap.end()) { + if (fLabelsMap.contains(m)) { mothers.push_back(fLabelsMap.find(m)->second); } } else { - cout << "Mother label (" << m << ") exceeds the McParticles size (" << mcParticles.size() << ")" << endl; - cout << " Check the MC generator" << endl; + LOG(warn) << "Mother label (" << m << ") exceeds the McParticles size (" << mcParticles.size() << ")"; + LOG(warn) << "Check the MC generator"; } } } @@ -1470,17 +1517,17 @@ struct TableMakerMC { for (int d = mctrack.daughtersIds()[0]; d <= mctrack.daughtersIds()[1]; ++d) { // TODO: remove this check as soon as issues with MC production are fixed if (d < mcParticles.size()) { // protect against bad daughter indices - if (fLabelsMap.find(d) != fLabelsMap.end()) { + if (fLabelsMap.contains(d)) { daughters.push_back(fLabelsMap.find(d)->second); } } else { - cout << "Daughter label (" << d << ") exceeds the McParticles size (" << mcParticles.size() << ")" << endl; - cout << " Check the MC generator" << endl; + LOG(warn) << "Daughter label (" << d << ") exceeds the McParticles size (" << mcParticles.size() << ")"; + LOG(warn) << "Check the MC generator"; } } } - int daughterRange[2] = {-1, -1}; - if (daughters.size() > 0) { + std::array daughterRange{-1, -1}; + if (!daughters.empty()) { daughterRange[0] = daughters[0]; daughterRange[1] = daughters[daughters.size() - 1]; } @@ -1488,24 +1535,24 @@ struct TableMakerMC { // NOTE: Here we assume that MC collisions are not filtered, so there is no new vs old index map for translation auto mcCollision = mctrack.template mcCollision_as(); trackMC(mcCollision.globalIndex(), mctrack.pdgCode(), mctrack.statusCode(), mctrack.flags(), - mothers, daughterRange, + mothers, daughterRange.data(), mctrack.weight(), mctrack.pt(), mctrack.eta(), mctrack.phi(), mctrack.e(), mctrack.vx(), mctrack.vy(), mctrack.vz(), mctrack.vt(), mcflags); for (unsigned int isig = 0; isig < fMCSignals.size(); isig++) { if (mcflags & (static_cast(1) << isig)) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(isig)); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(isig)); } } if (mcflags == 0) { - (reinterpret_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); + (dynamic_cast(fStatsList->At(3)))->Fill(static_cast(fMCSignals.size())); } } // end loop over labels } - void DefineHistograms(TString histClasses) + void DefineHistograms(const TString& histClasses) { std::unique_ptr objArray(histClasses.Tokenize(";")); - for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { + for (int iclass = 0; iclass < objArray->GetEntries(); ++iclass) { TString classStr = objArray->At(iclass)->GetName(); if (fConfigHistOutput.fConfigQA) { fHistMan->AddHistClass(classStr.Data()); @@ -1546,26 +1593,26 @@ struct TableMakerMC { fStatsList.setObject(new TList()); fStatsList->SetOwner(kTRUE); std::vector eventLabels{"BCs", "Collisions before filtering", "Before cuts", "After cuts"}; - TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, (float)o2::aod::evsel::kNsel + 0.5); + TH2I* histEvents = new TH2I("EventStats", "Event statistics", eventLabels.size(), -0.5, eventLabels.size() - 0.5, o2::aod::evsel::kNsel + 1, -0.5, static_cast(o2::aod::evsel::kNsel) + 0.5); int ib = 1; for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); } - for (int ib = 1; ib <= o2::aod::evsel::kNsel; ib++) { - histEvents->GetYaxis()->SetBinLabel(ib, o2::aod::evsel::selectionLabels[ib - 1]); + for (int iy = 1; iy <= o2::aod::evsel::kNsel; iy++) { + histEvents->GetYaxis()->SetBinLabel(iy, o2::aod::evsel::selectionLabels[iy - 1]); } histEvents->GetYaxis()->SetBinLabel(o2::aod::evsel::kNsel + 1, "Total"); fStatsList->Add(histEvents); // Track statistics: one bin for each track selection and 5 bins for V0 tags (gamma, K0s, Lambda, anti-Lambda, Omega) - TH1I* histTracks = new TH1I("TrackStats", "Track statistics", fTrackCuts.size() + 5.0, -0.5, fTrackCuts.size() - 0.5 + 5.0); + TH1I* histTracks = new TH1I("TrackStats", "Track statistics", fTrackCuts.size() + 5, -0.5, fTrackCuts.size() - 0.5 + 5.0); ib = 1; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, ib++) { histTracks->GetXaxis()->SetBinLabel(ib, (*cut)->GetName()); } - const char* v0TagNames[5] = {"Photon conversion", "K^{0}_{s}", "#Lambda", "#bar{#Lambda}", "#Omega"}; - for (int ib = 0; ib < 5; ib++) { - histTracks->GetXaxis()->SetBinLabel(fTrackCuts.size() + 1 + ib, v0TagNames[ib]); + constexpr std::array v0TagNames = {"Photon conversion", "K^{0}_{s}", "#Lambda", "#bar{#Lambda}", "#Omega"}; + for (int iv0 = 0; iv0 < 5; iv0++) { + histTracks->GetXaxis()->SetBinLabel(fTrackCuts.size() + 1 + iv0, v0TagNames[iv0]); } fStatsList->Add(histTracks); TH1I* histMuons = new TH1I("MuonStats", "Muon statistics", fMuonCuts.size(), -0.5, fMuonCuts.size() - 0.5); @@ -1588,14 +1635,14 @@ struct TableMakerMC { aod::TrackAssoc const& trackAssocs, aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPBarrelOnly(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, MyBarrelTracksWithCov const& tracksBarrel, aod::TrackAssoc const& trackAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPMuonOnlyBasic(MyEvents const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1603,7 +1650,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPMuonOnly(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1611,7 +1658,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPPMuonRefit(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1619,7 +1666,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles, aod::MFTTracksCov const& mftCovs) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, mftCovs); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, mftCovs, nullptr, nullptr, nullptr); } void processPPRealignedMuonOnly(MyEventsWithMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1627,7 +1674,7 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPbPb(MyEventsWithCentAndMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1635,7 +1682,7 @@ struct TableMakerMC { aod::TrackAssoc const& trackAssocs, aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, tracksMuon, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } void processPbPbBarrelOnly(MyEventsWithCentAndMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1643,15 +1690,16 @@ struct TableMakerMC { MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { computeCollMergingTag(collisions, tracksBarrel, presliceBarrelTracksWithCov); - fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } - void processPbPbWithFilterBarrelOnly(MyEventsWithMultsAndRapidityGapFilter const& collisions, aod::BCsWithTimestamps const& bcs, + void processPbPbWithFilterBarrelOnly(MyEventsWithMultsAndRapidityGapFilter const& collisions, MyBCs const& bcs, MyBarrelTracksWithCov const& tracksBarrel, aod::TrackAssoc const& trackAssocs, + aod::FT0s& ft0s, aod::FV0As& fv0as, aod::FDDs& fdds, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { computeCollMergingTag(collisions, tracksBarrel, presliceBarrelTracksWithCov); - fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, mcCollisions, mcParticles, nullptr, ft0s, fv0as, fdds); } void processPbPbMuonOnly(MyEventsWithCentAndMults const& collisions, aod::BCsWithTimestamps const& bcs, @@ -1659,9 +1707,9 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); /*LOGP(info, "---------------------------"); - for (auto& mcCollision : mcCollisions) { + for (const auto& mcCollision : mcCollisions) { LOGP(info, "Gen. FT0C centrality = {}", mcCollision.bestCollisionCentFT0C()); //LOGP(info, "Gen. FT0C centrality = {}", mcCollision.posZ()); } @@ -1683,18 +1731,18 @@ struct TableMakerMC { aod::FwdTrackAssoc const& fwdTrackAssocs, aod::MFTTrackAssoc const& mftAssocs, MyEventsMcWithMults const& mcCollisions, aod::McParticles const& mcParticles) { - fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksMuon, mftTracks, nullptr, fwdTrackAssocs, mftAssocs, mcCollisions, mcParticles, nullptr, nullptr, nullptr, nullptr); } // Process the BCs and store stats for luminosity retrieval ----------------------------------------------------------------------------------- void processOnlyBCs(soa::Join::iterator const& bc) { for (int i = 0; i < o2::aod::evsel::kNsel; i++) { - if (bc.alias_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); + if (static_cast(bc.alias_bit(i)) > 0) { + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(0)))->Fill(0.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(0)))->Fill(0.0, static_cast(o2::aod::evsel::kNsel)); } PROCESS_SWITCH(TableMakerMC, processPP, "Produce both barrel and muon skims, pp settings", false); diff --git a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx index 84d9a42bbbc..92452eb0c29 100644 --- a/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx +++ b/PWGDQ/TableProducer/tableMakerMuonMchTrkEfficiency.cxx @@ -187,7 +187,7 @@ struct tableMakerMuonMchTrkEfficiency { // define axes to be used // and the correspondent histograms - auto vbins = (std::vector)binsMuonPt; + auto vbins = std::vector(binsMuonPt); const AxisSpec axisEta{190, 2.3, 4.2, "|#eta|"}; const AxisSpec axisEtaRed{nEtaBins, etaMuonMin, etaMuonMax, "|#eta|"}; const AxisSpec axisEtaGenRed{nEtaBins, etaMuonMin, etaMuonMax, "|#eta| Gen"}; @@ -246,7 +246,7 @@ struct tableMakerMuonMchTrkEfficiency { /// extrapolate tracks to a given r value (spherical coordinates) /// to mimic the (x,y) position in a given chamber - void extrapolate(TLorentzVector vec, int ich, double& x, double& y) + void extrapolate(const TLorentzVector& vec, int ich, double& x, double& y) { // i = 0..9 double zposCh[10] = {5, 5, 7, 7, 10, 10, 12.5, 12.5, 14.5, 14.5}; double theta = vec.Theta(); @@ -396,7 +396,7 @@ struct tableMakerMuonMchTrkEfficiency { /// Event selection template - void runEventSelection(TEvent event) + void runEventSelection(const TEvent& event) { VarManager::ResetValues(0, VarManager::kNEventWiseVariables); VarManager::FillEvent(event); // extract event information and place it in the fValues array @@ -420,7 +420,6 @@ struct tableMakerMuonMchTrkEfficiency { for (int i = 0; i < ncuts; i++) nselmuons.push_back(0); - rowCandidateBase.reserve(tracksMuon.size()); for (auto& muon : tracksMuon) { VarManager::FillTrack(muon); @@ -486,8 +485,6 @@ struct tableMakerMuonMchTrkEfficiency { for (int i = 0; i < ncuts; i++) nselmuons.push_back(0); - rowCandidateBase.reserve(tracksMuon.size()); - rowCandidateGen.reserve(tracksMuon.size()); for (auto& muon : tracksMuon) { /// /// First compute MC matched quantities using either the DQ skimmed or the Framework data models diff --git a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx index adc962dc783..a49d29cb830 100644 --- a/PWGDQ/TableProducer/tableMaker_withAssoc.cxx +++ b/PWGDQ/TableProducer/tableMaker_withAssoc.cxx @@ -26,6 +26,7 @@ #include "PWGDQ/Core/MuonMatchingMlResponse.h" #include "PWGDQ/Core/VarManager.h" #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "PWGJE/DataModel/EMCALClusters.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" @@ -92,8 +93,6 @@ using namespace o2::framework::expressions; using namespace o2::aod; using namespace o2::aod::rctsel; -Zorro zorro; - // Declaration of various Joins used in the different process functions // TODO: Since DCA depends on which collision the track is associated to, we should remove writing and subscribing to DCA tables, to optimize on CPU / memory using MyBarrelTracks = soa::Join; using MyEventsWithMultsExtra = soa::Join; using MyEventsWithCentAndMultsQvect = soa::Join; -using MyMuons = soa::Join; -using MyMuonsWithCov = soa::Join; -using MyMuonsRealignWithCov = soa::Join; -using MyMuonsColl = soa::Join; -using MyMuonsCollWithCov = soa::Join; +using MyMuons = aod::FwdTracks; +using MyMuonsNoDca = soa::Join; +using MyMuonsWithCov = soa::Join; +using MyMuonsRealignWithCov = soa::Join; +using MyMuonsColl = soa::Join; +using MyMuonsCollWithCov = soa::Join; using MyBCs = soa::Join; using ExtBCs = soa::Join; @@ -151,16 +151,19 @@ constexpr static uint32_t gkEventFillMapWithMultsEventFilterZdc = VarManager::Ob constexpr static uint32_t gkEventFillMapWithMultsRapidityGapFilterZdcFit = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::RapidityGapFilter | VarManager::ObjTypes::Zdc | VarManager::ObjTypes::Fit; // constexpr static uint32_t gkEventFillMapWithCent = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent; constexpr static uint32_t gkEventFillMapWithCentAndMults = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra; +constexpr static uint32_t gkEventFillMapWithCentAndMultsZdc = gkEventFillMapWithCentAndMults | VarManager::ObjTypes::Zdc; constexpr static uint32_t gkEventFillMapWithCentAndMultsQvect = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra | VarManager::ObjTypes::CollisionQvectCentr; +constexpr static uint32_t gkEventFillMapWithCentAndMultsQvectZdc = gkEventFillMapWithCentAndMultsQvect | VarManager::ObjTypes::Zdc; constexpr static uint32_t gkEventFillMapWithMultsExtra = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::CollisionMult | VarManager::ObjTypes::CollisionMultExtra; // constexpr static uint32_t gkEventFillMapWithCentRun2 = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCentRun2; // Unused variable // constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackPID | VarManager::ObjTypes::TrackPIDExtra; constexpr static uint32_t gkTrackFillMapWithCov = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackCov | VarManager::ObjTypes::TrackPID | VarManager::ObjTypes::TrackPIDExtra; +constexpr static uint32_t gkTrackFillMapWithCovWithEMCal = gkTrackFillMapWithCov | VarManager::ObjTypes::TrackEMCal; constexpr static uint32_t gkTrackFillMapWithV0Bits = gkTrackFillMapWithCov | VarManager::ObjTypes::TrackV0Bits; constexpr static uint32_t gkTrackFillMapWithV0BitsNoTOF = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackV0Bits | VarManager::ObjTypes::TrackTPCPID; constexpr static uint32_t gkTrackFillMapNoTOF = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackTPCPID; // constexpr static uint32_t gkTrackFillMapWithDalitzBits = gkTrackFillMap | VarManager::ObjTypes::DalitzBits; -// constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::Muon; +constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::Muon | VarManager::ObjTypes::MuonCov; constexpr static uint32_t gkMuonFillMapWithCov = VarManager::ObjTypes::Muon | VarManager::ObjTypes::MuonCov; constexpr static uint32_t gkMuonRealignFillMapWithCov = VarManager::ObjTypes::MuonRealign | VarManager::ObjTypes::MuonCovRealign; // constexpr static uint32_t gkMuonFillMapWithAmbi = VarManager::ObjTypes::Muon | VarManager::ObjTypes::AmbiMuon; @@ -177,41 +180,54 @@ enum SkimStatsHists { kStatsMuons, kStatsOrphanTracks, kStatsZorroInfo, - kStatsZorroSel + kStatsZorroSel, + kStatsEMCal }; +namespace dqtablemaker_helpers +{ +inline float* varValues() { return static_cast(VarManager::fgValues); } +inline TString* varNames() { return static_cast(VarManager::fgVariableNames); } +inline TString* varUnits() { return static_cast(VarManager::fgVariableUnits); } +} // namespace dqtablemaker_helpers + struct TableMaker { - Produces event; - Produces eventExtended; - Produces eventVtxCov; - Produces eventInfo; - Produces eventQvectorCentr; - Produces eventQvectorCentrExtra; - Produces zdc; - Produces fit; - Produces multPV; - Produces multAll; - Produces mergingTable; - Produces trackBarrelInfo; - Produces trackBasic; - Produces trackBarrel; - Produces trackBarrelCov; - Produces trackBarrelPID; - Produces trackBarrelAssoc; - Produces muonBasic; - Produces muonExtra; - Produces muonCov; - Produces muonInfo; - Produces muonAssoc; - Produces mftTrack; - Produces mftTrackExtra; - Produces mftAssoc; + struct : ProducesGroup { + Produces event; + Produces eventExtended; + Produces eventVtxCov; + Produces eventInfo; + Produces zdc; + Produces fit; + Produces multPV; + Produces multAll; + Produces mergingTable; + Produces trackBarrelInfo; + Produces trackBasic; + Produces trackBarrel; + Produces trackBarrelCov; + Produces trackBarrelPID; + Produces trackBarrelAssoc; + Produces emcal; + Produces trackBarrelEMCal; + Produces muonBasic; + Produces muonExtra; + Produces muonCov; + Produces muonInfo; + Produces muonAssoc; + Produces mftTrack; + Produces mftTrackExtra; + Produces mftAssoc; + // Q-vector related tables, to be filled only if the user selects the corresponding option + Produces eventQvectorCentr; + Produces eventQvectorCentrExtra; + } outTables; OutputObj fOutputList{"output"}; //! the histogram manager output list OutputObj fStatsList{"Statistics"}; //! skimming statistics - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; // Event and track AnalysisCut configurables struct : ConfigurableGroup { @@ -221,6 +237,7 @@ struct TableMaker { Configurable fConfigEventCutsJSON{"cfgEventCutsJSON", "", "Additional event selection in JSON format"}; Configurable fConfigTrackCutsJSON{"cfgBarrelTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; Configurable fConfigMuonCutsJSON{"cfgMuonCutsJSON", "", "Additional list of muon cuts in JSON format"}; + Configurable fConfigEMCalCutsJSON{"cfgEMCalClusterCutsJSON", "", "List of EMCal cluster cuts in JSON format; if empty, all clusters are written"}; } fConfigCuts; // RCT selection @@ -246,6 +263,7 @@ struct TableMaker { Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddMuonHistogram{"cfgAddMuonHistogram", "", "Comma separated list of histograms"}; + Configurable fConfigAddEMCalHistogram{"cfgAddEMCalHistogram", "emcal", "Comma separated list of histogram subgroups for the EMCal cluster classes"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable fConfigIrEstimator{"cfgIrEstimator", "", "Estimator of the interaction rate (pp,OO --> T0VTX, Pb-Pb --> ZNC hadronic), to be used with cfgFillBcStat"}; } fConfigHistOutput; @@ -268,13 +286,12 @@ struct TableMaker { struct : ConfigurableGroup { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; - Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/Chunked/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable fConfigGrpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fZShiftPath{"zShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; - Configurable fUseRemoteZShift{"cfgUseRemoteZShift", false, "Enable getting Zshift from ccdb"}; - Configurable fManualZShift{"cfgManualZShift", 0.f, "Manual value for the Zshift for muons."}; + Configurable fFwdShiftPath{"fwdShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for the shift to apply to forward tracks: 1 (z), 3 (x,y,z), or 10 (x,y,z,slopeX,slopeY for top then bottom; slopes unused)"}; + Configurable fUseRemoteFwdShift{"cfgUseRemoteFwdShift", false, "Enable getting the forward track shift from ccdb"}; Configurable fConfigGrpMagPathRun2{"grpmagPathRun2", "GLO/GRP/GRP", "CCDB path of the GRPObject (Usage for Run 2)"}; } fConfigCCDB; @@ -288,6 +305,11 @@ struct TableMaker { Configurable fConfigSaveElectronSample{"cfgSaveElectronSample", false, "If true, only save electron sample"}; } fConfigPostCalibTPC; + // + struct : ConfigurableGroup { + Configurable fConfigQvectCalibAvailable{"cfgQvectCalibAvailable", false, "If true, qvector calibration objects are available in CCDB for this run"}; + } fConfigQvector; + struct : ConfigurableGroup { // Track related options Configurable fPropTrack{"cfgPropTrack", true, "Propagate tracks to associated collision to recalculate DCA and momentum vector"}; @@ -311,18 +333,19 @@ struct TableMaker { Configurable fExcludeShort{"cfgTPCExcludeShort", true, "Exclude short term from long term occupancy (micro-seconds)"}; } fConfigVariousOptions; - Service fCCDB; + Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; o2::parameters::GRPObject* fGrpMagRun2 = nullptr; // for run 2, we access the GRPObject from GLO/GRP/GRP o2::parameters::GRPMagField* fGrpMag = nullptr; // for run 3, we access GRPMagField from GLO/Config/GRPMagField - AnalysisCompositeCut* fEventCut; //! Event selection cut + AnalysisCompositeCut* fEventCut = nullptr; //! Event selection cut std::vector fTrackCuts; //! Barrel track cuts std::vector fMuonCuts; //! Muon track cuts + std::vector fEMCalCuts; //! EMCal cluster cuts bool fDoDetailedQA = false; // Bool to set detailed QA true, if QA is set true - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. // maps used to store index info; NOTE: std::map are sorted in ascending order by default (needed for track to collision indices) std::map fCollIndexMap; // key: old collision index, value: skimmed collision index @@ -331,13 +354,23 @@ struct TableMaker { std::map fFwdTrackIndexMapReversed; // key: new fwd-track global index, value: fwd-track global index std::map fFwdTrackFilterMap; // key: fwd-track global index, value: fwd-track filter map std::map fMftIndexMap; // key: MFT tracklet global index, value: new MFT tracklet global index + std::map fEmcalIndexMap; // key: EMCal cluster global index, value: skimmed cluster index + + // best (smallest delta-R) matched skimmed EMCal cluster for a barrel track + struct EMCalMatch { + int32_t clusterIdx = -1; + float deltaEta = -999.0f; + float deltaPhi = -999.0f; + float deltaR2 = 999.0f; + }; + std::map fTrackEMCalMatchMap; // key: track global index, value: best matched skimmed EMCal cluster std::map fBestMatch; std::unordered_map map_mfttrackcovs; o2::analysis::MlResponseMFTMuonMatch matchingMlResponse; std::vector binsPtMl; - std::array cutValues; + std::array cutValues{}; std::vector cutDirMl; // RCT flag checker @@ -357,6 +390,8 @@ struct TableMaker { Preslice trackIndicesPerCollision = aod::track_association::collisionId; Preslice fwdtrackIndicesPerCollision = aod::track_association::collisionId; Preslice mfttrackIndicesPerCollision = aod::track_association::collisionId; + Preslice emcalClustersPerCollision = aod::emcalcluster::collisionId; + Preslice emcalMatchedTracksPerCluster = aod::emcalclustercell::emcalclusterId; Preslice preslice = aod::track::collisionId; Partition tracksPos = (((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)) && (aod::track::tgl > static_cast(0.05))); @@ -375,6 +410,7 @@ struct TableMaker { Partition tracksNegWithCov = (((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)) && (aod::track::tgl < static_cast(-0.05))); ctpRateFetcher mRateFetcher; + Zorro zorro; parameters::GRPLHCIFData* mLHCIFdata = nullptr; struct { @@ -445,7 +481,7 @@ struct TableMaker { // Initialize the histogram manager fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablemaker_helpers::varNames(), dqtablemaker_helpers::varUnits()); // Only use detailed QA when QA is set true if (fConfigHistOutput.fConfigQA && fConfigHistOutput.fConfigDetailedQA) { @@ -469,10 +505,12 @@ struct TableMaker { // Check whether we have to define barrel or muon histograms bool enableBarrelHistos = (context.mOptions.get("processPP") || context.mOptions.get("processPPWithFilter") || context.mOptions.get("processPPWithFilterBarrelOnly") || context.mOptions.get("processPPBarrelOnly") || context.mOptions.get("processPbPb") || context.mOptions.get("processPbPbBarrelOnly") || context.mOptions.get("processPbPbBarrelOnlyWithV0Bits") || context.mOptions.get("processPbPbBarrelOnlyWithV0BitsNoTOF")) || - context.mOptions.get("processPbPbWithFilterBarrelOnly") || context.mOptions.get("processPPBarrelOnlyWithV0s") || context.mOptions.get("processPbPbBarrelOnlyNoTOF"); + context.mOptions.get("processPbPbWithFilterBarrelOnly") || context.mOptions.get("processPPBarrelOnlyWithV0s") || context.mOptions.get("processPbPbBarrelOnlyNoTOF") || context.mOptions.get("processPPBarrelOnlyWithEMCal") || context.mOptions.get("processPPWithEMCal"); - bool enableMuonHistos = (context.mOptions.get("processPP") || context.mOptions.get("processPPWithFilter") || context.mOptions.get("processPPWithFilterMuonOnly") || context.mOptions.get("processPPWithFilterMuonMFT") || context.mOptions.get("processPPMuonOnly") || context.mOptions.get("processPPRealignedMuonOnly") || context.mOptions.get("processPPMuonMFT") || context.mOptions.get("processPPMuonMFTWithMultsExtra") || - context.mOptions.get("processPbPb") || context.mOptions.get("processPbPbMuonOnly") || context.mOptions.get("processPbPbMuonOnlyWithQvect") || context.mOptions.get("processPbPbRealignedMuonOnly") || context.mOptions.get("processPbPbMuonMFT")); + bool enableEMCalHistos = (context.mOptions.get("processPPBarrelOnlyWithEMCal") || context.mOptions.get("processPPWithEMCal")); + + bool enableMuonHistos = (context.mOptions.get("processPP") || context.mOptions.get("processPPWithEMCal") || context.mOptions.get("processPPWithFilter") || context.mOptions.get("processPPWithFilterMuonOnly") || context.mOptions.get("processPPWithFilterMuonMFT") || context.mOptions.get("processPPMuonOnly") || context.mOptions.get("processPPRealignedMuonOnly") || context.mOptions.get("processPPMuonMFT") || context.mOptions.get("processPPMuonMFTWithMultsExtra") || context.mOptions.get("processPPMuonRefit") || + context.mOptions.get("processPbPb") || context.mOptions.get("processPbPbMuonOnly") || context.mOptions.get("processPbPbWithFilterMuonOnly") || context.mOptions.get("processPbPbStreamMuonOnly") || context.mOptions.get("processPbPbRealignedMuonOnly") || context.mOptions.get("processPbPbMuonMFT")); if (enableBarrelHistos) { // Barrel track histograms, before selections @@ -481,7 +519,7 @@ struct TableMaker { } if (fConfigHistOutput.fConfigQA) { // Barrel track histograms after selections; one histogram directory for each user specified selection - for (auto& cut : fTrackCuts) { + for (const auto& cut : fTrackCuts) { histClasses += Form("TrackBarrel_%s;", cut->GetName()); } } @@ -492,6 +530,18 @@ struct TableMaker { histClasses += "TrackBarrel_PostCalibProton;"; } } + if (enableEMCalHistos) { + // EMCal cluster histograms, before selections + if (fDoDetailedQA) { + histClasses += "EMCalClusters_BeforeCuts;"; + } + if (fConfigHistOutput.fConfigQA) { + // EMCal cluster histograms after selections; one histogram directory for each user specified selection + for (const auto& cut : fEMCalCuts) { + histClasses += Form("EMCalClusters_%s;", cut->GetName()); + } + } + } if (enableMuonHistos) { // Muon tracks before cuts and MFT tracks if (fDoDetailedQA) { @@ -500,7 +550,7 @@ struct TableMaker { } if (fConfigHistOutput.fConfigQA) { // Muon tracks after selections; one directory per selection - for (auto& muonCut : fMuonCuts) { + for (const auto& muonCut : fMuonCuts) { histClasses += Form("Muons_%s;", muonCut->GetName()); } } @@ -546,7 +596,7 @@ struct TableMaker { TString addEvCutsStr = fConfigCuts.fConfigEventCutsJSON.value; if (addEvCutsStr != "") { std::vector addEvCuts = dqcuts::GetCutsFromJSON(addEvCutsStr.Data()); - for (auto& cutIt : addEvCuts) { + for (const auto& cutIt : addEvCuts) { fEventCut->AddCut(cutIt); } } @@ -563,8 +613,17 @@ struct TableMaker { TString addTrackCutsStr = fConfigCuts.fConfigTrackCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + for (const auto& t : addTrackCuts) { + fTrackCuts.push_back(dynamic_cast(t)); + } + } + + // EMCal cluster cuts, via JSON only (there are no named EMCal cluster cuts in the CutsLibrary yet) + TString addEMCalCutsStr = fConfigCuts.fConfigEMCalCutsJSON.value; + if (addEMCalCutsStr != "") { + std::vector addEMCalCuts = dqcuts::GetCutsFromJSON(addEMCalCutsStr.Data()); + for (const auto& t : addEMCalCuts) { + fEMCalCuts.push_back(dynamic_cast(t)); } } @@ -580,19 +639,19 @@ struct TableMaker { TString addMuonCutsStr = fConfigCuts.fConfigMuonCutsJSON.value; if (addMuonCutsStr != "") { std::vector addMuonCuts = dqcuts::GetCutsFromJSON(addMuonCutsStr.Data()); - for (auto& t : addMuonCuts) { - fMuonCuts.push_back(reinterpret_cast(t)); + for (const auto& t : addMuonCuts) { + fMuonCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill } - void DefineHistograms(TString histClasses) + void DefineHistograms(const TString& histClasses) { // Create histograms via HistogramManager std::unique_ptr objArray(histClasses.Tokenize(";")); - for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { + for (int iclass = 0; iclass < objArray->GetEntries(); ++iclass) { TString classStr = objArray->At(iclass)->GetName(); if (fConfigHistOutput.fConfigQA) { fHistMan->AddHistClass(classStr.Data()); @@ -632,6 +691,13 @@ struct TableMaker { } } + TString histEMCalName = fConfigHistOutput.fConfigAddEMCalHistogram.value; + if (classStr.Contains("EMCalClusters")) { + if (fConfigHistOutput.fConfigQA) { + dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "track", histEMCalName); + } + } + TString histMftName = fConfigHistOutput.fConfigAddMuonHistogram.value; if (classStr.Contains("Mft")) { if (fConfigHistOutput.fConfigDetailedQA) { @@ -656,8 +722,8 @@ struct TableMaker { for (auto label = eventLabels.begin(); label != eventLabels.end(); label++, ib++) { histEvents->GetXaxis()->SetBinLabel(ib, (*label).Data()); } - for (int ib = 1; ib <= o2::aod::evsel::kNsel; ib++) { - histEvents->GetYaxis()->SetBinLabel(ib, o2::aod::evsel::selectionLabels[ib - 1]); + for (int iy = 1; iy <= o2::aod::evsel::kNsel; iy++) { + histEvents->GetYaxis()->SetBinLabel(iy, o2::aod::evsel::selectionLabels[iy - 1]); } histEvents->GetYaxis()->SetBinLabel(o2::aod::evsel::kNsel + 1, "Total"); fStatsList->AddAt(histEvents, kStatsEvent); @@ -671,13 +737,13 @@ struct TableMaker { fStatsList->AddAt(histBcs, kStatsBcs); // Track statistics: one bin for each track selection and 5 bins for V0 tags (gamma, K0s, Lambda, anti-Lambda, Omega) - TH1D* histTracks = new TH1D("TrackStats", "Track statistics", fTrackCuts.size() + 5.0, -0.5, fTrackCuts.size() - 0.5 + 5.0); + TH1D* histTracks = new TH1D("TrackStats", "Track statistics", fTrackCuts.size() + 5, -0.5, fTrackCuts.size() - 0.5 + 5.0); ib = 1; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, ib++) { histTracks->GetXaxis()->SetBinLabel(ib, (*cut)->GetName()); } - const char* v0TagNames[5] = {"Photon conversion", "K^{0}_{s}", "#Lambda", "#bar{#Lambda}", "#Omega"}; - for (ib = 0; ib < 5; ib++) { + constexpr std::array v0TagNames{"Photon conversion", "K^{0}_{s}", "#Lambda", "#bar{#Lambda}", "#Omega"}; + for (ib = 0; ib < static_cast(v0TagNames.size()); ib++) { histTracks->GetXaxis()->SetBinLabel(fTrackCuts.size() + 1 + ib, v0TagNames[ib]); } fStatsList->AddAt(histTracks, kStatsTracks); @@ -699,10 +765,19 @@ struct TableMaker { TH2D* histZorroSel = new TH2D("ZorroSel", "trigger of interested", 1, -0.5, 0.5, 1, -0.5, 0.5); fStatsList->AddAt(histZorroSel, kStatsZorroSel); + + // EMCal cluster statistics: one bin per cluster selection, plus one bin counting all skimmed clusters + TH1D* histEMCal = new TH1D("EMCalClusterStats", "EMCal cluster statistics", fEMCalCuts.size() + 1, -0.5, fEMCalCuts.size() + 0.5); + ib = 1; + for (auto cut = fEMCalCuts.begin(); cut != fEMCalCuts.end(); cut++, ib++) { + histEMCal->GetXaxis()->SetBinLabel(ib, (*cut)->GetName()); + } + histEMCal->GetXaxis()->SetBinLabel(fEMCalCuts.size() + 1, "Skimmed clusters"); + fStatsList->AddAt(histEMCal, kStatsEMCal); } template - void computeOccupancyEstimators(TEvents const& collisions, Partition const& tracksPos, Partition const& tracksNeg, Preslice& preslice, TBCs const&) + void computeOccupancyEstimators(TEvents const& collisions, Partition const& tracksPosPart, Partition const& tracksNegPart, Preslice& presliceTracks, TBCs const&) { // clear the occupancy maps for this time frame @@ -741,7 +816,7 @@ struct TableMaker { oVtxZ[collision.globalIndex()] = collision.posZ(); // if more than one collision per bunch, add that collision to the list for that bunch - if (oBCreversed.find(bc) == oBCreversed.end()) { + if (!oBCreversed.contains(bc)) { std::vector evs = {collision.globalIndex()}; oBCreversed[bc] = evs; } else { @@ -750,8 +825,8 @@ struct TableMaker { } // make a slice for this collision and get the number of tracks - auto thisCollTrackPos = tracksPos.sliceBy(preslice, collision.globalIndex()); - auto thisCollTrackNeg = tracksNeg.sliceBy(preslice, collision.globalIndex()); + auto thisCollTrackPos = tracksPosPart.sliceBy(presliceTracks, collision.globalIndex()); + auto thisCollTrackNeg = tracksNegPart.sliceBy(presliceTracks, collision.globalIndex()); collMultPos[collision.globalIndex()] = thisCollTrackPos.size(); collMultNeg[collision.globalIndex()] = thisCollTrackNeg.size(); } @@ -785,7 +860,7 @@ struct TableMaker { // check if this collision is also within the short time range bool isShort = (thisBC >= pastShortBC && thisBC < futureShortBC); // loop over all collisions in this BC - for (auto& thisColl : colls) { + for (const auto& thisColl : colls) { // skip if this is the same collision if (thisColl == collision) { continue; @@ -831,8 +906,8 @@ struct TableMaker { // iterate over the time maps to obtain the median time fOccup.oMedianTimeLongA[collision] = 0.0; float sumMult = 0.0; - if (oTimeMapLongA.size() > 0) { - for (auto& [dt, mult] : oTimeMapLongA) { + if (!oTimeMapLongA.empty()) { + for (const auto& [dt, mult] : oTimeMapLongA) { sumMult += mult; if (sumMult > fOccup.oContribLongA[collision] / 2.0) { fOccup.oMedianTimeLongA[collision] = dt; @@ -842,8 +917,8 @@ struct TableMaker { } fOccup.oMedianTimeLongC[collision] = 0.0; sumMult = 0.0; - if (oTimeMapLongC.size() > 0) { - for (auto& [dt, mult] : oTimeMapLongC) { + if (!oTimeMapLongC.empty()) { + for (const auto& [dt, mult] : oTimeMapLongC) { sumMult += mult; if (sumMult > fOccup.oContribLongC[collision] / 2.0) { fOccup.oMedianTimeLongC[collision] = dt; @@ -853,8 +928,8 @@ struct TableMaker { } fOccup.oMedianTimeShortA[collision] = 0.0; sumMult = 0.0; - if (oTimeMapShortA.size() > 0) { - for (auto& [dt, mult] : oTimeMapShortA) { + if (!oTimeMapShortA.empty()) { + for (const auto& [dt, mult] : oTimeMapShortA) { sumMult += mult; if (sumMult > fOccup.oContribShortA[collision] / 2.0) { fOccup.oMedianTimeShortA[collision] = dt; @@ -864,8 +939,8 @@ struct TableMaker { } fOccup.oMedianTimeShortC[collision] = 0.0; sumMult = 0.0; - if (oTimeMapShortC.size() > 0) { - for (auto& [dt, mult] : oTimeMapShortC) { + if (!oTimeMapShortC.empty()) { + for (const auto& [dt, mult] : oTimeMapShortC) { sumMult += mult; if (sumMult > fOccup.oContribShortC[collision] / 2.0) { fOccup.oMedianTimeShortC[collision] = dt; @@ -883,12 +958,9 @@ struct TableMaker { auto bfilling = mLHCIFdata->getBunchFilling(); double nbc = bfilling.getFilledBCs().size(); - double tvxRate; - if (fConfigHistOutput.fConfigIrEstimator.value.empty()) { - tvxRate = mRateFetcher.fetch(fCCDB.service, timeStamp, bc.runNumber(), "T0VTX"); - } else { - tvxRate = mRateFetcher.fetch(fCCDB.service, timeStamp, bc.runNumber(), fConfigHistOutput.fConfigIrEstimator.value); - } + const double tvxRate = fConfigHistOutput.fConfigIrEstimator.value.empty() + ? mRateFetcher.fetch(fCCDB.service, timeStamp, bc.runNumber(), "T0VTX") + : mRateFetcher.fetch(fCCDB.service, timeStamp, bc.runNumber(), fConfigHistOutput.fConfigIrEstimator.value); double nTriggersPerFilledBC = tvxRate / nbc / o2::constants::lhc::LHCRevFreq; double mu = -std::log(1 - nTriggersPerFilledBC); @@ -896,7 +968,7 @@ struct TableMaker { } template - void computeCollMergingTag(TEvents const& collisions, TTracks const& tracks, Preslice& preslice) + void computeCollMergingTag(TEvents const& collisions, TTracks const& tracks, Preslice& presliceTracks) { // This function uses the standard track-collision association to compute quantities related to collision merging // clear the maps for this time frame @@ -929,7 +1001,7 @@ struct TableMaker { for (const auto& collision : collisions) { // make a slice for this collision and compute the DCAz based event quantities - auto thisCollTracks = tracks.sliceBy(preslice, collision.globalIndex()); + auto thisCollTracks = tracks.sliceBy(presliceTracks, collision.globalIndex()); VarManager::FillEventTracks(thisCollTracks); // fill the VarManager arrays with the information of the tracks associated to this collision, needed for the cuts and histograms // add the computed variables to the maps with the collision index as key fCollMergingTag.bimodalityCoeffDCAz[collision.globalIndex()] = VarManager::fgValues[VarManager::kDCAzBimodalityCoefficient]; @@ -1002,22 +1074,22 @@ struct TableMaker { bool isTriggerZNA = bc.selection_bit(aod::evsel::kIsBBZNA); bool isTriggerZNC = bc.selection_bit(aod::evsel::kIsBBZNC); - (reinterpret_cast(fStatsList->At(kStatsBcs)))->Fill(0.0, muTVX); + (dynamic_cast(fStatsList->At(kStatsBcs)))->Fill(0.0, muTVX); if (isTvx) { - (reinterpret_cast(fStatsList->At(kStatsBcs)))->Fill(1.0, muTVX); + (dynamic_cast(fStatsList->At(kStatsBcs)))->Fill(1.0, muTVX); if (noBorder) { - (reinterpret_cast(fStatsList->At(kStatsBcs)))->Fill(2.0, muTVX); + (dynamic_cast(fStatsList->At(kStatsBcs)))->Fill(2.0, muTVX); if (isCentral) { - (reinterpret_cast(fStatsList->At(kStatsBcs)))->Fill(3.0, muTVX); + (dynamic_cast(fStatsList->At(kStatsBcs)))->Fill(3.0, muTVX); } if (isSemiCentral) { - (reinterpret_cast(fStatsList->At(kStatsBcs)))->Fill(4.0, muTVX); + (dynamic_cast(fStatsList->At(kStatsBcs)))->Fill(4.0, muTVX); } if (isCentral || isSemiCentral) { - (reinterpret_cast(fStatsList->At(kStatsBcs)))->Fill(5.0, muTVX); + (dynamic_cast(fStatsList->At(kStatsBcs)))->Fill(5.0, muTVX); } if (isTriggerZNA && isTriggerZNC) { - (reinterpret_cast(fStatsList->At(kStatsBcs)))->Fill(6.0, muTVX); + (dynamic_cast(fStatsList->At(kStatsBcs)))->Fill(6.0, muTVX); } } } @@ -1029,10 +1101,10 @@ struct TableMaker { for (int i = 0; i < o2::aod::evsel::kNsel; i++) { if (collision.selection_bit(i)) { - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(1.0, static_cast(i)); + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(1.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(1.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(1.0, static_cast(o2::aod::evsel::kNsel)); // apply the event filter computed by filter-PP if constexpr ((TEventFillMap & VarManager::ObjTypes::EventFilter) > 0) { @@ -1108,28 +1180,28 @@ struct TableMaker { } if (fDoDetailedQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_BeforeCuts", dqtablemaker_helpers::varValues()); } // fill stats information, before selections for (int i = 0; i < o2::aod::evsel::kNsel; i++) { if (collision.selection_bit(i)) { - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(2.0, static_cast(i)); + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(2.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(2.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(2.0, static_cast(o2::aod::evsel::kNsel)); if (fConfigZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(fConfigCCDB.fConfigCcdbPathZorro.value); zorro.setBCtolerance(fConfigZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), fConfigZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(kStatsZorroInfo)), reinterpret_cast(fStatsList->At(kStatsZorroSel))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(kStatsZorroInfo)), dynamic_cast(fStatsList->At(kStatsZorroSel))); - if (!fEventCut->IsSelected(VarManager::fgValues) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(collision))) { + if (!fEventCut->IsSelected(dqtablemaker_helpers::varValues()) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(collision))) { continue; } - bool zorroSel = zorro.isSelected(bc.globalBC(), fConfigZorro.fBcTolerance, reinterpret_cast(fStatsList->At(kStatsZorroSel))); + bool zorroSel = zorro.isSelected(bc.globalBC(), fConfigZorro.fBcTolerance, dynamic_cast(fStatsList->At(kStatsZorroSel))); if (zorroSel) { tag |= (static_cast(true) << 56); // the same bit is used for this zorro selections from ccdb } @@ -1137,7 +1209,7 @@ struct TableMaker { continue; } } else { - if (!fEventCut->IsSelected(VarManager::fgValues) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(collision))) { + if (!fEventCut->IsSelected(dqtablemaker_helpers::varValues()) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(collision))) { continue; } } @@ -1145,15 +1217,15 @@ struct TableMaker { // fill stats information, after selections for (int i = 0; i < o2::aod::evsel::kNsel; i++) { if (collision.selection_bit(i)) { - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(3.0, static_cast(i)); + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(3.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(3.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(3.0, static_cast(o2::aod::evsel::kNsel)); - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", dqtablemaker_helpers::varValues()); // create the event tables - event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); + outTables.event(tag, bc.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), collision.collisionTime(), collision.collisionTimeRes()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMult) > 0) { multFV0C = collision.multFV0C(); multTPC = collision.multTPC(); @@ -1172,87 +1244,88 @@ struct TableMaker { centFT0A = collision.centFT0A(); centFT0M = collision.centFT0M(); } - eventExtended(bc.globalBC(), collision.alias_raw(), collision.selection_raw(), bc.timestamp(), VarManager::fgValues[VarManager::kCentVZERO], - multTPC, multFV0A, multFV0C, multFT0A, multFT0C, multFDDA, multFDDC, multZNA, multZNC, multTracklets, multTracksPV, centFT0C, centFT0A, centFT0M); - eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); - eventInfo(collision.globalIndex()); + outTables.eventExtended(bc.globalBC(), collision.alias_raw(), collision.selection_raw(), bc.timestamp(), VarManager::fgValues[VarManager::kCentVZERO], + multTPC, multFV0A, multFV0C, multFT0A, multFT0C, multFDDA, multFDDC, multZNA, multZNC, multTracklets, multTracksPV, centFT0C, centFT0A, centFT0M); + outTables.eventVtxCov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + outTables.eventInfo(collision.globalIndex()); if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionQvectCentr) > 0) { - eventQvectorCentr(collision.qvecFT0ARe(), collision.qvecFT0AIm(), collision.qvecFT0CRe(), collision.qvecFT0CIm(), collision.qvecFT0MRe(), collision.qvecFT0MIm(), collision.qvecFV0ARe(), collision.qvecFV0AIm(), collision.qvecTPCposRe(), collision.qvecTPCposIm(), collision.qvecTPCnegRe(), collision.qvecTPCnegIm(), - collision.sumAmplFT0A(), collision.sumAmplFT0C(), collision.sumAmplFT0M(), collision.sumAmplFV0A(), collision.nTrkTPCpos(), collision.nTrkTPCneg()); - eventQvectorCentrExtra(collision.qvecTPCallRe(), collision.qvecTPCallIm(), collision.nTrkTPCall()); + if (fConfigQvector.fConfigQvectCalibAvailable) { + outTables.eventQvectorCentr(collision.qvecFT0ARe(), collision.qvecFT0AIm(), collision.qvecFT0CRe(), collision.qvecFT0CIm(), collision.qvecFT0MRe(), collision.qvecFT0MIm(), collision.qvecFV0ARe(), collision.qvecFV0AIm(), collision.qvecTPCposRe(), collision.qvecTPCposIm(), collision.qvecTPCnegRe(), collision.qvecTPCnegIm(), + collision.sumAmplFT0A(), collision.sumAmplFT0C(), collision.sumAmplFT0M(), collision.sumAmplFV0A(), collision.nTrkTPCpos(), collision.nTrkTPCneg()); + outTables.eventQvectorCentrExtra(collision.qvecTPCallRe(), collision.qvecTPCallIm(), collision.nTrkTPCall()); + } } - if constexpr ((TEventFillMap & VarManager::ObjTypes::Zdc) > 0) { if constexpr ((TEventFillMap & VarManager::ObjTypes::RapidityGapFilter) > 0) { // The DQRapidityGapFilter contains the index of the bc we want to get ZDC info from auto newbc = bcs.rawIteratorAt(collision.newBcIndex()); if (newbc.has_zdc()) { auto newbc_zdc = newbc.zdc(); - zdc(newbc_zdc.energyCommonZNA(), newbc_zdc.energyCommonZNC(), newbc_zdc.energyCommonZPA(), newbc_zdc.energyCommonZPC(), - newbc_zdc.timeZNA(), newbc_zdc.timeZNC(), newbc_zdc.timeZPA(), newbc_zdc.timeZPC()); + outTables.zdc(newbc_zdc.energyCommonZNA(), newbc_zdc.energyCommonZNC(), newbc_zdc.energyCommonZPA(), newbc_zdc.energyCommonZPC(), + newbc_zdc.timeZNA(), newbc_zdc.timeZNC(), newbc_zdc.timeZPA(), newbc_zdc.timeZPC()); } else { - zdc(-999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0); + outTables.zdc(-999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0); } } else if (bcEvSel.has_zdc()) { auto bc_zdc = bcEvSel.zdc(); - zdc(bc_zdc.energyCommonZNA(), bc_zdc.energyCommonZNC(), bc_zdc.energyCommonZPA(), bc_zdc.energyCommonZPC(), - bc_zdc.timeZNA(), bc_zdc.timeZNC(), bc_zdc.timeZPA(), bc_zdc.timeZPC()); + outTables.zdc(VarManager::fgValues[VarManager::kEnergyCommonZNA], VarManager::fgValues[VarManager::kEnergyCommonZNC], VarManager::fgValues[VarManager::kEnergyCommonZPA], VarManager::fgValues[VarManager::kEnergyCommonZPC], + bc_zdc.timeZNA(), bc_zdc.timeZNC(), bc_zdc.timeZPA(), bc_zdc.timeZPC()); } else { - zdc(-999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0); + outTables.zdc(-999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0, -999.0); } } // Fill FIT table if requested if constexpr ((TEventFillMap & VarManager::ObjTypes::Fit) > 0) { - fit(VarManager::fgValues[VarManager::kAmplitudeFT0A], VarManager::fgValues[VarManager::kAmplitudeFT0C], - VarManager::fgValues[VarManager::kTimeFT0A], VarManager::fgValues[VarManager::kTimeFT0C], - static_cast(VarManager::fgValues[VarManager::kTriggerMaskFT0]), - static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0A]), - static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0C]), - VarManager::fgValues[VarManager::kAmplitudeFDDA], VarManager::fgValues[VarManager::kAmplitudeFDDC], - VarManager::fgValues[VarManager::kTimeFDDA], VarManager::fgValues[VarManager::kTimeFDDC], - static_cast(VarManager::fgValues[VarManager::kTriggerMaskFDD]), - VarManager::fgValues[VarManager::kAmplitudeFV0A], VarManager::fgValues[VarManager::kTimeFV0A], - static_cast(VarManager::fgValues[VarManager::kTriggerMaskFV0A]), - static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFV0A]), - static_cast(VarManager::fgValues[VarManager::kBBFT0Apf]), - static_cast(VarManager::fgValues[VarManager::kBGFT0Apf]), - static_cast(VarManager::fgValues[VarManager::kBBFT0Cpf]), - static_cast(VarManager::fgValues[VarManager::kBGFT0Cpf]), - static_cast(VarManager::fgValues[VarManager::kBBFV0Apf]), - static_cast(VarManager::fgValues[VarManager::kBGFV0Apf]), - static_cast(VarManager::fgValues[VarManager::kBBFDDApf]), - static_cast(VarManager::fgValues[VarManager::kBGFDDApf]), - static_cast(VarManager::fgValues[VarManager::kBBFDDCpf]), - static_cast(VarManager::fgValues[VarManager::kBGFDDCpf])); + outTables.fit(VarManager::fgValues[VarManager::kAmplitudeFT0A], VarManager::fgValues[VarManager::kAmplitudeFT0C], + VarManager::fgValues[VarManager::kTimeFT0A], VarManager::fgValues[VarManager::kTimeFT0C], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFT0]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0A]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFT0C]), + VarManager::fgValues[VarManager::kAmplitudeFDDA], VarManager::fgValues[VarManager::kAmplitudeFDDC], + VarManager::fgValues[VarManager::kTimeFDDA], VarManager::fgValues[VarManager::kTimeFDDC], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFDD]), + VarManager::fgValues[VarManager::kAmplitudeFV0A], VarManager::fgValues[VarManager::kTimeFV0A], + static_cast(VarManager::fgValues[VarManager::kTriggerMaskFV0A]), + static_cast(VarManager::fgValues[VarManager::kNFiredChannelsFV0A]), + static_cast(VarManager::fgValues[VarManager::kBBFT0Apf]), + static_cast(VarManager::fgValues[VarManager::kBGFT0Apf]), + static_cast(VarManager::fgValues[VarManager::kBBFT0Cpf]), + static_cast(VarManager::fgValues[VarManager::kBGFT0Cpf]), + static_cast(VarManager::fgValues[VarManager::kBBFV0Apf]), + static_cast(VarManager::fgValues[VarManager::kBGFV0Apf]), + static_cast(VarManager::fgValues[VarManager::kBBFDDApf]), + static_cast(VarManager::fgValues[VarManager::kBGFDDApf]), + static_cast(VarManager::fgValues[VarManager::kBBFDDCpf]), + static_cast(VarManager::fgValues[VarManager::kBGFDDCpf])); } if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionMultExtra) > 0) { - multPV(collision.multNTracksHasITS(), collision.multNTracksHasTPC(), collision.multNTracksHasTOF(), collision.multNTracksHasTRD(), - collision.multNTracksITSOnly(), collision.multNTracksTPCOnly(), collision.multNTracksITSTPC(), - collision.multNTracksPVeta1(), collision.multNTracksPVetaHalf(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); - - multAll(collision.multAllTracksTPCOnly(), collision.multAllTracksITSTPC(), - fOccup.oContribLongA[collision.globalIndex()], fOccup.oContribLongC[collision.globalIndex()], - fOccup.oMeanTimeLongA[collision.globalIndex()], fOccup.oMeanTimeLongC[collision.globalIndex()], - fOccup.oMedianTimeLongA[collision.globalIndex()], fOccup.oMedianTimeLongC[collision.globalIndex()], - fOccup.oContribShortA[collision.globalIndex()], fOccup.oContribShortC[collision.globalIndex()], - fOccup.oMeanTimeShortA[collision.globalIndex()], fOccup.oMeanTimeShortC[collision.globalIndex()], - fOccup.oMedianTimeShortA[collision.globalIndex()], fOccup.oMedianTimeShortC[collision.globalIndex()]); - } - mergingTable(fCollMergingTag.bimodalityCoeffDCAz[collision.globalIndex()], fCollMergingTag.bimodalityCoeffDCAzBinned[collision.globalIndex()], - fCollMergingTag.bimodalityCoeffDCAzBinnedTrimmed1[collision.globalIndex()], fCollMergingTag.bimodalityCoeffDCAzBinnedTrimmed2[collision.globalIndex()], fCollMergingTag.bimodalityCoeffDCAzBinnedTrimmed3[collision.globalIndex()], - fCollMergingTag.meanDCAz[collision.globalIndex()], fCollMergingTag.meanDCAzBinnedTrimmed1[collision.globalIndex()], fCollMergingTag.meanDCAzBinnedTrimmed2[collision.globalIndex()], fCollMergingTag.meanDCAzBinnedTrimmed3[collision.globalIndex()], - fCollMergingTag.rmsDCAz[collision.globalIndex()], fCollMergingTag.rmsDCAzBinnedTrimmed1[collision.globalIndex()], fCollMergingTag.rmsDCAzBinnedTrimmed2[collision.globalIndex()], fCollMergingTag.rmsDCAzBinnedTrimmed3[collision.globalIndex()], - fCollMergingTag.skewnessDCAz[collision.globalIndex()], fCollMergingTag.kurtosisDCAz[collision.globalIndex()], - fCollMergingTag.fraction100umDCAz[collision.globalIndex()], fCollMergingTag.fraction200umDCAz[collision.globalIndex()], - fCollMergingTag.fraction500umDCAz[collision.globalIndex()], fCollMergingTag.fraction1mmDCAz[collision.globalIndex()], - fCollMergingTag.fraction2mmDCAz[collision.globalIndex()], fCollMergingTag.fraction5mmDCAz[collision.globalIndex()], - fCollMergingTag.fraction10mmDCAz[collision.globalIndex()], - fCollMergingTag.nPeaksDCAz[collision.globalIndex()], fCollMergingTag.nPeaksDCAzTrimmed1[collision.globalIndex()], - fCollMergingTag.nPeaksDCAzTrimmed2[collision.globalIndex()], fCollMergingTag.nPeaksDCAzTrimmed3[collision.globalIndex()]); + outTables.multPV(collision.multNTracksHasITS(), collision.multNTracksHasTPC(), collision.multNTracksHasTOF(), collision.multNTracksHasTRD(), + collision.multNTracksITSOnly(), collision.multNTracksTPCOnly(), collision.multNTracksITSTPC(), + collision.multNTracksPVeta1(), collision.multNTracksPVetaHalf(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); + + outTables.multAll(collision.multAllTracksTPCOnly(), collision.multAllTracksITSTPC(), + fOccup.oContribLongA[collision.globalIndex()], fOccup.oContribLongC[collision.globalIndex()], + fOccup.oMeanTimeLongA[collision.globalIndex()], fOccup.oMeanTimeLongC[collision.globalIndex()], + fOccup.oMedianTimeLongA[collision.globalIndex()], fOccup.oMedianTimeLongC[collision.globalIndex()], + fOccup.oContribShortA[collision.globalIndex()], fOccup.oContribShortC[collision.globalIndex()], + fOccup.oMeanTimeShortA[collision.globalIndex()], fOccup.oMeanTimeShortC[collision.globalIndex()], + fOccup.oMedianTimeShortA[collision.globalIndex()], fOccup.oMedianTimeShortC[collision.globalIndex()]); + } + outTables.mergingTable(fCollMergingTag.bimodalityCoeffDCAz[collision.globalIndex()], fCollMergingTag.bimodalityCoeffDCAzBinned[collision.globalIndex()], + fCollMergingTag.bimodalityCoeffDCAzBinnedTrimmed1[collision.globalIndex()], fCollMergingTag.bimodalityCoeffDCAzBinnedTrimmed2[collision.globalIndex()], fCollMergingTag.bimodalityCoeffDCAzBinnedTrimmed3[collision.globalIndex()], + fCollMergingTag.meanDCAz[collision.globalIndex()], fCollMergingTag.meanDCAzBinnedTrimmed1[collision.globalIndex()], fCollMergingTag.meanDCAzBinnedTrimmed2[collision.globalIndex()], fCollMergingTag.meanDCAzBinnedTrimmed3[collision.globalIndex()], + fCollMergingTag.rmsDCAz[collision.globalIndex()], fCollMergingTag.rmsDCAzBinnedTrimmed1[collision.globalIndex()], fCollMergingTag.rmsDCAzBinnedTrimmed2[collision.globalIndex()], fCollMergingTag.rmsDCAzBinnedTrimmed3[collision.globalIndex()], + fCollMergingTag.skewnessDCAz[collision.globalIndex()], fCollMergingTag.kurtosisDCAz[collision.globalIndex()], + fCollMergingTag.fraction100umDCAz[collision.globalIndex()], fCollMergingTag.fraction200umDCAz[collision.globalIndex()], + fCollMergingTag.fraction500umDCAz[collision.globalIndex()], fCollMergingTag.fraction1mmDCAz[collision.globalIndex()], + fCollMergingTag.fraction2mmDCAz[collision.globalIndex()], fCollMergingTag.fraction5mmDCAz[collision.globalIndex()], + fCollMergingTag.fraction10mmDCAz[collision.globalIndex()], + fCollMergingTag.nPeaksDCAz[collision.globalIndex()], fCollMergingTag.nPeaksDCAzTrimmed1[collision.globalIndex()], + fCollMergingTag.nPeaksDCAzTrimmed2[collision.globalIndex()], fCollMergingTag.nPeaksDCAzTrimmed3[collision.globalIndex()]); // - fCollIndexMap[collision.globalIndex()] = event.lastIndex(); + fCollIndexMap[collision.globalIndex()] = outTables.event.lastIndex(); } } @@ -1264,8 +1337,8 @@ struct TableMaker { // One can apply here cuts which depend on the association (e.g. DCA), which will discard (hopefully most) wrong associations. // Tracks are written only once, even if they constribute to more than one association - uint64_t trackFilteringTag = static_cast(0); - uint32_t trackTempFilterMap = static_cast(0); + auto trackFilteringTag = static_cast(0); + auto trackTempFilterMap = static_cast(0); // material correction for track propagation // TODO: Do we need a configurable to switch between different material correction options? @@ -1293,20 +1366,20 @@ struct TableMaker { } if (fDoDetailedQA) { - fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", dqtablemaker_helpers::varValues()); } // apply track cuts and fill stats histogram int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, i++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqtablemaker_helpers::varValues())) { trackTempFilterMap |= (static_cast(1) << i); // NOTE: the QA is filled here just for the first occurence of this track. // So if there are histograms of quantities which depend on the collision association, these will not be accurate if (fConfigHistOutput.fConfigQA && (fTrackIndexMap.find(track.globalIndex()) == fTrackIndexMap.end())) { - fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), dqtablemaker_helpers::varValues()); } - (reinterpret_cast(fStatsList->At(kStatsTracks)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(kStatsTracks)))->Fill(static_cast(i)); } } if (!trackTempFilterMap) { @@ -1316,7 +1389,7 @@ struct TableMaker { // If this track is already present in the index map, it means it was already skimmed, // so we just store the association and we skip the track if (fTrackIndexMap.find(track.globalIndex()) != fTrackIndexMap.end()) { - trackBarrelAssoc(fCollIndexMap[collision.globalIndex()], fTrackIndexMap[track.globalIndex()]); + outTables.trackBarrelAssoc(fCollIndexMap[collision.globalIndex()], fTrackIndexMap[track.globalIndex()]); continue; } @@ -1324,23 +1397,23 @@ struct TableMaker { if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackV0Bits)) { // BIT0-4: V0Bits trackFilteringTag |= static_cast(track.pidbit()); for (int iv0 = 0; iv0 < 5; iv0++) { - if (track.pidbit() & (uint8_t(1) << iv0)) { - (reinterpret_cast(fStatsList->At(kStatsTracks)))->Fill(fTrackCuts.size() + static_cast(iv0)); + if (track.pidbit() & (static_cast(1) << iv0)) { + (dynamic_cast(fStatsList->At(kStatsTracks)))->Fill(fTrackCuts.size() + static_cast(iv0)); } } // TODO: this part should be removed since the calibration histogram can be filled as any other histogram if (fConfigPostCalibTPC.fConfigIsOnlyforMaps) { if (trackFilteringTag & (static_cast(1) << VarManager::kIsConversionLeg)) { // for electron - fHistMan->FillHistClass("TrackBarrel_PostCalibElectron", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_PostCalibElectron", dqtablemaker_helpers::varValues()); } if (trackFilteringTag & (static_cast(1) << VarManager::kIsK0sLeg)) { // for pion - fHistMan->FillHistClass("TrackBarrel_PostCalibPion", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_PostCalibPion", dqtablemaker_helpers::varValues()); } if ((static_cast(trackFilteringTag & (static_cast(1) << VarManager::kIsLambdaLeg)) * (track.sign()) > 0)) { // for proton from Lambda - fHistMan->FillHistClass("TrackBarrel_PostCalibProton", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_PostCalibProton", dqtablemaker_helpers::varValues()); } if ((static_cast(trackFilteringTag & (static_cast(1) << VarManager::kIsALambdaLeg)) * (track.sign()) < 0)) { // for proton from AntiLambda - fHistMan->FillHistClass("TrackBarrel_PostCalibProton", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_PostCalibProton", dqtablemaker_helpers::varValues()); } } if (fConfigPostCalibTPC.fConfigSaveElectronSample) { // only save electron sample @@ -1362,9 +1435,9 @@ struct TableMaker { // Calculating the percentage of orphan tracks i.e., tracks which have no collisions associated to it if (!track.has_collision()) { - (reinterpret_cast(fStatsList->At(kStatsOrphanTracks)))->Fill(static_cast(-1)); + (dynamic_cast(fStatsList->At(kStatsOrphanTracks)))->Fill(static_cast(-1)); } else { - (reinterpret_cast(fStatsList->At(kStatsOrphanTracks)))->Fill(0.9); + (dynamic_cast(fStatsList->At(kStatsOrphanTracks)))->Fill(0.9); } // NOTE: The collision ID written in the table is the one of the original collision assigned in the AO2D. @@ -1375,48 +1448,124 @@ struct TableMaker { // NOTE: trackBarrelInfo stores the index of the collision as in AO2D (for use in some cases where the analysis on skims is done // in workflows where the original AO2Ds are also present) - trackBarrelInfo(collision.globalIndex(), collision.posX(), collision.posY(), collision.posZ(), track.globalIndex()); - trackBasic(reducedEventIdx, trackFilteringTag, track.pt(), track.eta(), track.phi(), track.sign(), 0); - trackBarrel(track.x(), track.alpha(), track.y(), track.z(), track.snp(), track.tgl(), track.signed1Pt(), - track.tpcInnerParam(), track.flags(), track.itsClusterMap(), track.itsChi2NCl(), - track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), - track.tpcNClsShared(), track.tpcChi2NCl(), - track.trdChi2(), track.trdPattern(), track.tofChi2(), - track.length(), track.dcaXY(), track.dcaZ(), - track.trackTime(), track.trackTimeRes(), track.tofExpMom(), - track.detectorMap()); + outTables.trackBarrelInfo(collision.globalIndex(), collision.posX(), collision.posY(), collision.posZ(), track.globalIndex()); + outTables.trackBasic(reducedEventIdx, trackFilteringTag, track.pt(), track.eta(), track.phi(), track.sign(), 0); + outTables.trackBarrel(track.x(), track.alpha(), track.y(), track.z(), track.snp(), track.tgl(), track.signed1Pt(), + track.tpcInnerParam(), track.flags(), track.itsClusterMap(), track.itsChi2NCl(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), + track.tpcNClsShared(), track.tpcChi2NCl(), + track.trdChi2(), track.trdPattern(), track.tofChi2(), + track.length(), track.dcaXY(), track.dcaZ(), + track.trackTime(), track.trackTimeRes(), track.tofExpMom(), + track.detectorMap()); if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackCov)) { - trackBarrelCov(track.cYY(), track.cZY(), track.cZZ(), track.cSnpY(), track.cSnpZ(), - track.cSnpSnp(), track.cTglY(), track.cTglZ(), track.cTglSnp(), track.cTglTgl(), - track.c1PtY(), track.c1PtZ(), track.c1PtSnp(), track.c1PtTgl(), track.c1Pt21Pt2()); + outTables.trackBarrelCov(track.cYY(), track.cZY(), track.cZZ(), track.cSnpY(), track.cSnpZ(), + track.cSnpSnp(), track.cTglY(), track.cTglZ(), track.cTglSnp(), track.cTglTgl(), + track.c1PtY(), track.c1PtZ(), track.c1PtSnp(), track.c1PtTgl(), track.c1Pt21Pt2()); } if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackPID)) { float nSigmaEl = (fConfigPostCalibTPC.fConfigComputeTPCpostCalib ? VarManager::fgValues[VarManager::kTPCnSigmaEl_Corr] : track.tpcNSigmaEl()); float nSigmaPi = (fConfigPostCalibTPC.fConfigComputeTPCpostCalib ? VarManager::fgValues[VarManager::kTPCnSigmaPi_Corr] : track.tpcNSigmaPi()); float nSigmaKa = ((fConfigPostCalibTPC.fConfigComputeTPCpostCalib && fConfigPostCalibTPC.fConfigComputeTPCpostCalibKaon) ? VarManager::fgValues[VarManager::kTPCnSigmaKa_Corr] : track.tpcNSigmaKa()); float nSigmaPr = (fConfigPostCalibTPC.fConfigComputeTPCpostCalib ? VarManager::fgValues[VarManager::kTPCnSigmaPr_Corr] : track.tpcNSigmaPr()); - trackBarrelPID(track.tpcSignal(), - nSigmaEl, track.tpcNSigmaMu(), nSigmaPi, nSigmaKa, nSigmaPr, - track.beta(), track.tofNSigmaEl(), track.tofNSigmaMu(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), - track.trdSignal()); + outTables.trackBarrelPID(track.tpcSignal(), + nSigmaEl, track.tpcNSigmaMu(), nSigmaPi, nSigmaKa, nSigmaPr, + track.beta(), track.tofNSigmaEl(), track.tofNSigmaMu(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr(), + track.trdSignal()); } else if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackTPCPID)) { float nSigmaEl = (fConfigPostCalibTPC.fConfigComputeTPCpostCalib ? VarManager::fgValues[VarManager::kTPCnSigmaEl_Corr] : track.tpcNSigmaEl()); float nSigmaPi = (fConfigPostCalibTPC.fConfigComputeTPCpostCalib ? VarManager::fgValues[VarManager::kTPCnSigmaPi_Corr] : track.tpcNSigmaPi()); float nSigmaKa = ((fConfigPostCalibTPC.fConfigComputeTPCpostCalib && fConfigPostCalibTPC.fConfigComputeTPCpostCalibKaon) ? VarManager::fgValues[VarManager::kTPCnSigmaKa_Corr] : track.tpcNSigmaKa()); float nSigmaPr = (fConfigPostCalibTPC.fConfigComputeTPCpostCalib ? VarManager::fgValues[VarManager::kTPCnSigmaPr_Corr] : track.tpcNSigmaPr()); - trackBarrelPID(track.tpcSignal(), - nSigmaEl, -999.0, nSigmaPi, nSigmaKa, nSigmaPr, - -999.0, -999.0, -999.0, -999.0, -999.0, -999.0, - -999.0); + outTables.trackBarrelPID(track.tpcSignal(), + nSigmaEl, -999.0, nSigmaPi, nSigmaKa, nSigmaPr, + -999.0, -999.0, -999.0, -999.0, -999.0, -999.0, + -999.0); + } + + // write the matched EMCal cluster index (or -1 if the track has no matched cluster); + // this table has exactly one entry per skimmed track, so it is joinable to the ReducedTracks tables + if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackEMCal)) { + auto match = fTrackEMCalMatchMap.find(track.globalIndex()); + if (match != fTrackEMCalMatchMap.end()) { + outTables.trackBarrelEMCal(match->second.clusterIdx, match->second.deltaEta, match->second.deltaPhi); + } else { + outTables.trackBarrelEMCal(-1, -999.0f, -999.0f); + } } - fTrackIndexMap[track.globalIndex()] = trackBasic.lastIndex(); + fTrackIndexMap[track.globalIndex()] = outTables.trackBasic.lastIndex(); // write the skimmed collision - track association - trackBarrelAssoc(fCollIndexMap[collision.globalIndex()], fTrackIndexMap[track.globalIndex()]); + outTables.trackBarrelAssoc(fCollIndexMap[collision.globalIndex()], fTrackIndexMap[track.globalIndex()]); } // end loop over associations } // end skimTracks + template + void skimEMCal(TEMCals const& clusters, TEMCalMatches const& matchedTracks) + { + // Skim the EMCal clusters of all the selected collisions and record, for each track matched to a cluster, + // the closest (smallest delta-R) skimmed cluster. This has to run before skimTracks, such that the + // matching indices are available at the time the tracks are written. + // NOTE: only the unique clusters (aod::EMCALClusters) are skimmed here; the ambiguous clusters + // (aod::EMCALAmbiguousClusters, from BCs with none or multiple reconstructed collisions) are not + // associated to a collision and have no track matching, so they are left for a dedicated treatment. + for (auto const& [origIdx, skimIdx] : fCollIndexMap) { + auto groupedClusters = clusters.sliceBy(emcalClustersPerCollision, origIdx); + for (const auto& cluster : groupedClusters) { + VarManager::FillTrackEMCal(cluster); + if (fDoDetailedQA) { + fHistMan->FillHistClass("EMCalClusters_BeforeCuts", dqtablemaker_helpers::varValues()); + } + + // apply the cluster selections and fill the filtering tag and the stats histogram + auto clusterTempFilterMap = static_cast(0); + int i = 0; + for (auto cut = fEMCalCuts.begin(); cut != fEMCalCuts.end(); cut++, i++) { + if ((*cut)->IsSelected(dqtablemaker_helpers::varValues())) { + clusterTempFilterMap |= (static_cast(1) << i); + if (fConfigHistOutput.fConfigQA) { + fHistMan->FillHistClass(Form("EMCalClusters_%s", (*cut)->GetName()), dqtablemaker_helpers::varValues()); + } + (dynamic_cast(fStatsList->At(kStatsEMCal)))->Fill(static_cast(i)); + } + } + // if cluster selections are specified, write only the clusters which fulfill at least one of them + if (!fEMCalCuts.empty() && clusterTempFilterMap == 0) { + continue; + } + (dynamic_cast(fStatsList->At(kStatsEMCal)))->Fill(static_cast(fEMCalCuts.size())); + auto clusterFilteringTag = (static_cast(clusterTempFilterMap) << VarManager::kEMCalClusterUserCutsBits); + + // flag the clusters which have at least one matched track, such that a clean + // "EMCal only" cluster sample (e.g. for photons) can be selected on the skimmed data + auto clusterMatches = matchedTracks.sliceBy(emcalMatchedTracksPerCluster, cluster.globalIndex()); + if (clusterMatches.size() > 0) { + clusterFilteringTag |= (static_cast(1) << VarManager::kEMCalClusterIsMatched); + } + + outTables.emcal(skimIdx, clusterFilteringTag, + cluster.energy(), cluster.coreEnergy(), cluster.rawEnergy(), + cluster.eta(), cluster.phi(), cluster.m02(), cluster.m20(), + cluster.nCells(), cluster.time(), cluster.isExotic(), + cluster.distanceToBadChannel(), cluster.nlm(), cluster.definition(), + false); + fEmcalIndexMap[cluster.globalIndex()] = outTables.emcal.lastIndex(); + + // record the track matches of this cluster; for each track keep only the closest cluster + for (const auto& match : clusterMatches) { + float deltaEta = match.deltaEta(); + float deltaPhi = match.deltaPhi(); + float deltaR2 = deltaEta * deltaEta + deltaPhi * deltaPhi; + auto existing = fTrackEMCalMatchMap.find(match.trackId()); + if (existing == fTrackEMCalMatchMap.end() || deltaR2 < existing->second.deltaR2) { + fTrackEMCalMatchMap[match.trackId()] = EMCalMatch{.clusterIdx = static_cast(outTables.emcal.lastIndex()), .deltaEta = deltaEta, .deltaPhi = deltaPhi, .deltaR2 = deltaR2}; + } + } + } // end loop over clusters + } // end loop over skimmed collisions + } // end skimEMCal + template void skimMFT(TEvent const& collision, TBCs const& /*bcs*/, MFTTracks const& /*mfts*/, MFTTrackAssoc const& mftAssocs) { @@ -1428,19 +1577,19 @@ struct TableMaker { if (fConfigHistOutput.fConfigQA) { VarManager::FillTrack(track); - fHistMan->FillHistClass("MftTracks", VarManager::fgValues); + fHistMan->FillHistClass("MftTracks", dqtablemaker_helpers::varValues()); } // write the MFT track global index in the map for skimming (to make sure we have it just once) - if (fMftIndexMap.find(track.globalIndex()) == fMftIndexMap.end()) { + if (!fMftIndexMap.contains(track.globalIndex())) { uint32_t reducedEventIdx = fCollIndexMap[collision.globalIndex()]; - mftTrack(reducedEventIdx, static_cast(0), track.pt(), track.eta(), track.phi()); + outTables.mftTrack(reducedEventIdx, static_cast(0), track.pt(), track.eta(), track.phi()); // TODO: We are not writing the DCA at the moment, because this depend on the collision association - mftTrackExtra(track.mftClusterSizesAndTrackFlags(), track.sign(), 0.0, 0.0, track.nClusters()); + outTables.mftTrackExtra(track.mftClusterSizesAndTrackFlags(), track.sign(), 0.0, 0.0, track.nClusters()); - fMftIndexMap[track.globalIndex()] = mftTrack.lastIndex(); + fMftIndexMap[track.globalIndex()] = outTables.mftTrack.lastIndex(); } - mftAssoc(fCollIndexMap[collision.globalIndex()], fMftIndexMap[track.globalIndex()]); + outTables.mftAssoc(fCollIndexMap[collision.globalIndex()], fMftIndexMap[track.globalIndex()]); } } @@ -1461,7 +1610,7 @@ struct TableMaker { } } } - for (auto& pairCand : mCandidates) { + for (const auto& pairCand : mCandidates) { fBestMatch[pairCand.second.second] = true; } } @@ -1483,7 +1632,7 @@ struct TableMaker { muonprop = VarManager::PropagateMuon(muontrack, collision, VarManager::kToMatching); } std::vector output; - std::vector inputML = matchingMlResponse.getInputFeaturesGlob(muon, muonprop, mftprop, collision); + std::vector inputML = matchingMlResponse.getInputFeatures(muon, mfttrack, muontrack, mftprop, muonprop, collision); matchingMlResponse.isSelectedMl(inputML, 0, output); float score = output[0]; if (mCandidates.find(muonID) == mCandidates.end()) { @@ -1495,7 +1644,7 @@ struct TableMaker { } } } - for (auto& pairCand : mCandidates) { + for (const auto& pairCand : mCandidates) { fBestMatch[pairCand.second.second] = true; } } @@ -1510,11 +1659,11 @@ struct TableMaker { // TODO: Currently, the TMFTFillMap is not used in this function. Is it needed ? - uint8_t trackFilteringTag = static_cast(0); - uint8_t trackTempFilterMap = static_cast(0); + auto trackFilteringTag = static_cast(0); + auto trackTempFilterMap = static_cast(0); fFwdTrackIndexMapReversed.clear(); - uint32_t offset = muonBasic.lastIndex(); + uint32_t offset = outTables.muonBasic.lastIndex(); uint32_t counter = 0; for (const auto& assoc : muonAssocs) { // get the muon @@ -1539,44 +1688,49 @@ struct TableMaker { // NOTE: Muons are propagated to the current associated collisions. // So if a muon is associated to multiple collisions, depending on the selections, // it may be accepted for some associations and rejected for other - if (fConfigVariousOptions.fPropMuon) { + if (static_cast(muon.trackType()) > 2 && fConfigVariousOptions.fPropMuon) { VarManager::FillPropagateMuon(muon, collision); } - // recalculate pDca and global muon kinematics - if (static_cast(muon.trackType()) < 2 && fConfigVariousOptions.fRefitGlobalMuon) { + // recalculate pDca / DCA and global muon kinematics + // kMuonPDca is always taken from MCH (standalone or the MCH matched to a global) + if (static_cast(muon.trackType()) <= 2) { auto muontrack = muon.template matchMCHTrack_as(); - if (muontrack.eta() < fConfigVariousOptions.fMuonMatchEtaMin || muontrack.eta() > fConfigVariousOptions.fMuonMatchEtaMax) { - continue; - } - auto mfttrack = muon.template matchMFTTrack_as(); VarManager::FillTrackCollision(muontrack, collision); - // NOTE: the MFT track originally associated to the MUON track is currently used in the global muon refit - // Should MUON - MFT time ambiguities be taken into account ? - if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { - auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); - VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + if (fConfigVariousOptions.fRefitGlobalMuon) { + if (muontrack.eta() < fConfigVariousOptions.fMuonMatchEtaMin || muontrack.eta() > fConfigVariousOptions.fMuonMatchEtaMax) { + continue; + } + auto mfttrack = muon.template matchMFTTrack_as(); + // Helix DCA (kMuonDCAx/y) is filled from the refitted parameters inside FillGlobalMuonRefit(Cov) + if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { + auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); + VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + } else { + VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + } } else { - VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + // Helix DCA of the global track; leaves kMuonPDca from the matched MCH above + VarManager::FillTrackCollision(muon, collision); } } else { VarManager::FillTrackCollision(muon, collision); } if (fDoDetailedQA) { - fHistMan->FillHistClass("Muons_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Muons_BeforeCuts", dqtablemaker_helpers::varValues()); } // check the cuts and filters int i = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, i++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqtablemaker_helpers::varValues())) { trackTempFilterMap |= (static_cast(1) << i); // NOTE: the QA is filled here just for the first occurence of this muon, which means the current association // will be skipped from histograms if this muon was already filled in the skimming map. // So if there are histograms of quantities which depend on the collision association, these histograms will not be completely accurate if (fConfigHistOutput.fConfigQA && (fFwdTrackIndexMap.find(muon.globalIndex()) == fFwdTrackIndexMap.end())) { - fHistMan->FillHistClass(Form("Muons_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("Muons_%s", (*cut)->GetName()), dqtablemaker_helpers::varValues()); } - (reinterpret_cast(fStatsList->At(kStatsMuons)))->Fill(static_cast(i)); + (dynamic_cast(fStatsList->At(kStatsMuons)))->Fill(static_cast(i)); } } @@ -1602,7 +1756,7 @@ struct TableMaker { fFwdTrackFilterMap[muon.globalIndex()] |= trackFilteringTag; // make a bitwise OR with previous existing cuts } // write the association table - muonAssoc(fCollIndexMap[collision.globalIndex()], fFwdTrackIndexMap[muon.globalIndex()]); + outTables.muonAssoc(fCollIndexMap[collision.globalIndex()], fFwdTrackIndexMap[muon.globalIndex()]); } // end loop over assocs // Now we have the full index map of selected muons so we can proceed with writing the muon tables @@ -1634,38 +1788,45 @@ struct TableMaker { } VarManager::FillTrack(muon); - if (fConfigVariousOptions.fPropMuon) { + if (static_cast(muon.trackType()) > 2 && fConfigVariousOptions.fPropMuon) { VarManager::FillPropagateMuon(muon, collision); } - // recalculte pDca and global muon kinematics + // recalculate pDca / DCA and global muon kinematics + // kMuonPDca is always taken from MCH (standalone or the MCH matched to a global) int globalClusters = muon.nClusters(); - if (static_cast(muon.trackType()) < 2 && fConfigVariousOptions.fRefitGlobalMuon) { + if (static_cast(muon.trackType()) <= 2) { auto muontrack = muon.template matchMCHTrack_as(); - auto mfttrack = muon.template matchMFTTrack_as(); - globalClusters += mfttrack.nClusters(); VarManager::FillTrackCollision(muontrack, collision); - if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { - auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); - VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + if (fConfigVariousOptions.fRefitGlobalMuon) { + auto mfttrack = muon.template matchMFTTrack_as(); + globalClusters += mfttrack.nClusters(); + // Helix DCA (kMuonDCAx/y) is filled from the refitted parameters inside FillGlobalMuonRefit(Cov) + if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { + auto const& mfttrackcov = mfCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); + VarManager::FillGlobalMuonRefitCov(muontrack, mfttrack, collision, mfttrackcov); + } else { + VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + } } else { - VarManager::FillGlobalMuonRefit(muontrack, mfttrack, collision); + // Helix DCA of the global track; leaves kMuonPDca from the matched MCH above + VarManager::FillTrackCollision(muon, collision); } } else { VarManager::FillTrackCollision(muon, collision); } - muonBasic(reducedEventIdx, mchIdx, mftIdx, fFwdTrackFilterMap[muon.globalIndex()], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], muon.sign(), 0); - muonExtra(globalClusters, VarManager::fgValues[VarManager::kMuonPDca], VarManager::fgValues[VarManager::kMuonRAtAbsorberEnd], - VarManager::fgValues[VarManager::kMuonChi2], muon.chi2MatchMCHMID(), muon.chi2MatchMCHMFT(), - muon.matchScoreMCHMFT(), - muon.mchBitMap(), muon.midBitMap(), - muon.midBoards(), muon.trackType(), VarManager::fgValues[VarManager::kMuonDCAx], VarManager::fgValues[VarManager::kMuonDCAy], - muon.trackTime(), muon.trackTimeRes()); - muonInfo(muon.collisionId(), collision.posX(), collision.posY(), collision.posZ()); + outTables.muonBasic(reducedEventIdx, mchIdx, mftIdx, fFwdTrackFilterMap[muon.globalIndex()], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], muon.sign(), 0); + outTables.muonExtra(globalClusters, VarManager::fgValues[VarManager::kMuonPDca], VarManager::fgValues[VarManager::kMuonRAtAbsorberEnd], + VarManager::fgValues[VarManager::kMuonChi2], muon.chi2MatchMCHMID(), muon.chi2MatchMCHMFT(), + muon.matchScoreMCHMFT(), + muon.mchBitMap(), muon.midBitMap(), + muon.midBoards(), muon.trackType(), VarManager::fgValues[VarManager::kMuonDCAx], VarManager::fgValues[VarManager::kMuonDCAy], + muon.trackTime(), muon.trackTimeRes()); + outTables.muonInfo(muon.collisionId(), collision.posX(), collision.posY(), collision.posZ()); if constexpr (static_cast(TMuonFillMap & VarManager::ObjTypes::MuonCov) || static_cast(TMuonFillMap & VarManager::ObjTypes::MuonCovRealign)) { - muonCov(VarManager::fgValues[VarManager::kX], VarManager::fgValues[VarManager::kY], VarManager::fgValues[VarManager::kZ], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kTgl], muon.sign() / VarManager::fgValues[VarManager::kPt], - VarManager::fgValues[VarManager::kMuonCXX], VarManager::fgValues[VarManager::kMuonCXY], VarManager::fgValues[VarManager::kMuonCYY], VarManager::fgValues[VarManager::kMuonCPhiX], VarManager::fgValues[VarManager::kMuonCPhiY], VarManager::fgValues[VarManager::kMuonCPhiPhi], - VarManager::fgValues[VarManager::kMuonCTglX], VarManager::fgValues[VarManager::kMuonCTglY], VarManager::fgValues[VarManager::kMuonCTglPhi], VarManager::fgValues[VarManager::kMuonCTglTgl], VarManager::fgValues[VarManager::kMuonC1Pt2X], VarManager::fgValues[VarManager::kMuonC1Pt2Y], - VarManager::fgValues[VarManager::kMuonC1Pt2Phi], VarManager::fgValues[VarManager::kMuonC1Pt2Tgl], VarManager::fgValues[VarManager::kMuonC1Pt21Pt2]); + outTables.muonCov(VarManager::fgValues[VarManager::kX], VarManager::fgValues[VarManager::kY], VarManager::fgValues[VarManager::kZ], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kTgl], muon.sign() / VarManager::fgValues[VarManager::kPt], + VarManager::fgValues[VarManager::kMuonCXX], VarManager::fgValues[VarManager::kMuonCXY], VarManager::fgValues[VarManager::kMuonCYY], VarManager::fgValues[VarManager::kMuonCPhiX], VarManager::fgValues[VarManager::kMuonCPhiY], VarManager::fgValues[VarManager::kMuonCPhiPhi], + VarManager::fgValues[VarManager::kMuonCTglX], VarManager::fgValues[VarManager::kMuonCTglY], VarManager::fgValues[VarManager::kMuonCTglPhi], VarManager::fgValues[VarManager::kMuonCTglTgl], VarManager::fgValues[VarManager::kMuonC1Pt2X], VarManager::fgValues[VarManager::kMuonC1Pt2Y], + VarManager::fgValues[VarManager::kMuonC1Pt2Phi], VarManager::fgValues[VarManager::kMuonC1Pt2Tgl], VarManager::fgValues[VarManager::kMuonC1Pt21Pt2]); } } // end loop over selected muons } // end skimMuons @@ -1676,7 +1837,8 @@ struct TableMaker { typename TTrackAssoc, typename TFwdTrackAssoc, typename TMFTTrackAssoc, typename TMFTCov, typename TFt0s, typename TFv0as, typename TFdds> void fullSkimming(TEvents const& collisions, TBCs const& bcs, TZdcs const& zdcs, TTracks const& tracksBarrel, TMuons const& muons, TMFTTracks const& mftTracks, - TTrackAssoc const& trackAssocs, TFwdTrackAssoc const& fwdTrackAssocs, TMFTTrackAssoc const& mftAssocs, TMFTCov const& mftCovs, TFt0s const& ft0s, TFv0as const& fv0as, TFdds const& fdds) + TTrackAssoc const& trackAssocs, TFwdTrackAssoc const& fwdTrackAssocs, TMFTTrackAssoc const& mftAssocs, TMFTCov const& mftCovs, TFt0s const& ft0s, TFv0as const& fv0as, TFdds const& fdds, + aod::EMCALClusters const* emcalClusters = nullptr, aod::EMCALMatchedTracks const* emcalMatchedTracks = nullptr) { if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { @@ -1702,7 +1864,7 @@ struct TableMaker { } VarManager::SetCalibrationType(fConfigPostCalibTPC.fConfigTPCpostCalibType, fConfigPostCalibTPC.fConfigTPCuseInterpolatedCalib); } - if (fIsRun2 == true) { + if (fIsRun2) { fGrpMagRun2 = fCCDB->getForTimeStamp(fConfigCCDB.fConfigGrpMagPathRun2, bcs.begin().timestamp()); if (fGrpMagRun2 != nullptr) { o2::base::Propagator::initFieldFromGRP(fGrpMagRun2); @@ -1713,15 +1875,24 @@ struct TableMaker { o2::base::Propagator::initFieldFromGRP(fGrpMag); VarManager::SetMagneticField(fGrpMag->getNominalL3Field()); } - if (fConfigCCDB.fUseRemoteZShift) { - auto* fZShift = fCCDB->getForTimeStamp>(fConfigCCDB.fZShiftPath, bcs.begin().timestamp()); - if (fZShift != nullptr && !fZShift->empty()) { - VarManager::SetZShift((*fZShift)[0]); + if (fConfigCCDB.fUseRemoteFwdShift) { + auto* fFwdShift = fCCDB->getForTimeStamp>(fConfigCCDB.fFwdShiftPath, bcs.begin().timestamp()); + if (fFwdShift == nullptr || fFwdShift->empty()) { + LOG(fatal) << "Could not retrieve forward track shift values from CCDB"; + } else if (fFwdShift->size() == 1) { + VarManager::SetZShift((*fFwdShift)[0]); + } else if (fFwdShift->size() == 3) { + VarManager::Set3DShift((*fFwdShift)[0], (*fFwdShift)[1], (*fFwdShift)[2]); + } else if (fFwdShift->size() == 10) { + // x_top, y_top, z_top, slopeX_top, slopeY_top, x_bottom, y_bottom, z_bottom, slopeX_bottom, slopeY_bottom + // Slopes are unused for now; shift is selected from track y (top: y >= 0, bottom: y < 0) + VarManager::SetTopBottom3DShift((*fFwdShift)[0], (*fFwdShift)[1], (*fFwdShift)[2], + (*fFwdShift)[5], (*fFwdShift)[6], (*fFwdShift)[7]); + LOG(info) << "Loaded top/bottom forward track shifts from CCDB: top=(" << (*fFwdShift)[0] << ", " << (*fFwdShift)[1] << ", " << (*fFwdShift)[2] + << "), bottom=(" << (*fFwdShift)[5] << ", " << (*fFwdShift)[6] << ", " << (*fFwdShift)[7] << ")"; } else { - LOG(fatal) << "Could not retrieve Z-shift value from CCDB"; + LOG(fatal) << "Unexpected number of shift values from CCDB: " << fFwdShift->size() << ", expected 1 (z), 3 (x, y, z) or 10 (top/bottom x,y,z + slopes)"; } - } else { - VarManager::SetZShift(fConfigCCDB.fManualZShift.value); } if (fConfigHistOutput.fConfigFillBcStat) { mLHCIFdata = fCCDB->getSpecific("GLO/Config/GRPLHCIF", bcs.begin().timestamp()); @@ -1740,46 +1911,56 @@ struct TableMaker { } // end updating the CCDB quantities at change of run // skim collisions - event.reserve(collisions.size()); - eventExtended.reserve(collisions.size()); - eventVtxCov.reserve(collisions.size()); + outTables.event.reserve(collisions.size()); + outTables.eventExtended.reserve(collisions.size()); + outTables.eventVtxCov.reserve(collisions.size()); skimCollisions(collisions, bcs, zdcs, ft0s, fv0as, fdds); - if (fCollIndexMap.size() == 0) { + if (fCollIndexMap.empty()) { return; } if constexpr (static_cast(TTrackFillMap)) { fTrackIndexMap.clear(); - trackBarrelInfo.reserve(tracksBarrel.size()); - trackBasic.reserve(tracksBarrel.size()); - trackBarrel.reserve(tracksBarrel.size()); - trackBarrelCov.reserve(tracksBarrel.size()); - trackBarrelPID.reserve(tracksBarrel.size()); - trackBarrelAssoc.reserve(tracksBarrel.size()); + outTables.trackBarrelInfo.reserve(tracksBarrel.size()); + outTables.trackBasic.reserve(tracksBarrel.size()); + outTables.trackBarrel.reserve(tracksBarrel.size()); + outTables.trackBarrelCov.reserve(tracksBarrel.size()); + outTables.trackBarrelPID.reserve(tracksBarrel.size()); + outTables.trackBarrelAssoc.reserve(trackAssocs.size()); + } + + // skim the EMCal clusters of the selected collisions; this runs before the track skimming + // such that the track to cluster matching indices are available when the tracks are written + if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackEMCal)) { + fEmcalIndexMap.clear(); + fTrackEMCalMatchMap.clear(); + outTables.emcal.reserve(emcalClusters->size()); + outTables.trackBarrelEMCal.reserve(tracksBarrel.size()); + skimEMCal(*emcalClusters, *emcalMatchedTracks); } if constexpr (static_cast(TMFTFillMap)) { fMftIndexMap.clear(); map_mfttrackcovs.clear(); - mftTrack.reserve(mftTracks.size()); - mftTrackExtra.reserve(mftTracks.size()); - mftAssoc.reserve(mftTracks.size()); + outTables.mftTrack.reserve(mftTracks.size()); + outTables.mftTrackExtra.reserve(mftTracks.size()); + outTables.mftAssoc.reserve(mftAssocs.size()); } if constexpr (static_cast(TMuonFillMap)) { fFwdTrackIndexMap.clear(); fFwdTrackFilterMap.clear(); fBestMatch.clear(); - muonBasic.reserve(muons.size()); - muonExtra.reserve(muons.size()); - muonInfo.reserve(muons.size()); - muonCov.reserve(muons.size()); - muonAssoc.reserve(muons.size()); + outTables.muonBasic.reserve(muons.size()); + outTables.muonExtra.reserve(muons.size()); + outTables.muonInfo.reserve(muons.size()); + outTables.muonCov.reserve(muons.size()); + outTables.muonAssoc.reserve(fwdTrackAssocs.size()); } if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { - for (auto& mfttrackConv : mftCovs) { + for (const auto& mfttrackConv : mftCovs) { map_mfttrackcovs[mfttrackConv.matchMFTTrackId()] = mfttrackConv.globalIndex(); } } @@ -1805,6 +1986,8 @@ struct TableMaker { if constexpr (static_cast(TMFTFillMap & VarManager::ObjTypes::MFTCov)) { if (fConfigVariousOptions.fUseML.value) { skimBestMuonMatchesML(muons, mftTracks, mftCovs, collision); + } else { + skimBestMuonMatches(muons); } } else { skimBestMuonMatches(muons); @@ -1823,8 +2006,8 @@ struct TableMaker { } // end loop over skimmed collisions // LOG(info) << "Skims in this TF: " << fCollIndexMap.size() << " collisions; " << trackBasic.lastIndex() << " barrel tracks; " - //<< muonBasic.lastIndex() << " muon tracks; " << mftTrack.lastIndex() << " MFT tracks; "; - // LOG(info) << " " << trackBarrelAssoc.lastIndex() << " barrel assocs; " << muonAssoc.lastIndex() << " muon assocs; " << mftAssoc.lastIndex() << " MFT assoc"; + //<< outTables.muonBasic.lastIndex() << " muon tracks; " << mftTrack.lastIndex() << " MFT tracks; "; + // LOG(info) << " " << trackBarrelAssoc.lastIndex() << " barrel assocs; " << outTables.muonAssoc.lastIndex() << " muon assocs; " << outTables.mftAssoc.lastIndex() << " MFT assoc"; } // produce the full DQ skimmed data model typically for pp/p-Pb or UPC Pb-Pb (no centrality), subscribe to the DQ event filter (filter-pp or filter-PbPb) @@ -1847,8 +2030,19 @@ struct TableMaker { fullSkimming(collisions, bcs, nullptr, tracksBarrel, muons, nullptr, trackAssocs, fwdTrackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr); } + // produce the full (barrel + muon) DQ skimmed data model with EMCal clusters, typically for pp (e.g. for e-mu correlations with EMCal electron ID); + // requires the emcal-correction-task upstream in the workflow (for the EMCALClusters and EMCALMatchedTracks tables) + void processPPWithEMCal(MyEventsWithMultsExtra const& collisions, MyBCs const& bcs, + MyBarrelTracksWithCov const& tracksBarrel, + MyMuonsWithCov const& muons, + aod::EMCALClusters const& emcalClusters, aod::EMCALMatchedTracks const& emcalMatchedTracks, + TrackAssoc const& trackAssocs, FwdTrackAssoc const& fwdTrackAssocs) + { + fullSkimming(collisions, bcs, nullptr, tracksBarrel, muons, nullptr, trackAssocs, fwdTrackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr, &emcalClusters, &emcalMatchedTracks); + } + // produce the barrel-only DQ skimmed data model typically for pp/p-Pb or UPC Pb-Pb (no centrality), subscribe to the DQ event filter (filter-pp or filter-PbPb) - void processPPWithFilterBarrelOnly(MyEventsWithMultsAndFilter const& collisions, MyBCs const& bcs, aod::Zdcs& zdcs, + void processPPWithFilterBarrelOnly(MyEventsWithMultsAndFilter const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, MyBarrelTracksWithCov const& tracksBarrel, TrackAssoc const& trackAssocs) { @@ -1871,13 +2065,23 @@ struct TableMaker { } // produce the barrel-only DQ skimmed data model typically for pp/p-Pb or UPC Pb-Pb (no centrality), meant to run on skimmed data - void processPPBarrelOnly(MyEventsWithMults const& collisions, MyBCs const& bcs, aod::Zdcs& zdcs, + void processPPBarrelOnly(MyEventsWithMults const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, MyBarrelTracksWithCov const& tracksBarrel, TrackAssoc const& trackAssocs) { fullSkimming(collisions, bcs, zdcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); } + // produce the barrel-only DQ skimmed data model with EMCal clusters, typically for pp (no centrality); + // requires the emcal-correction-task upstream in the workflow (for the EMCALClusters and EMCALMatchedTracks tables) + void processPPBarrelOnlyWithEMCal(MyEventsWithMults const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, + MyBarrelTracksWithCov const& tracksBarrel, + aod::EMCALClusters const& emcalClusters, aod::EMCALMatchedTracks const& emcalMatchedTracks, + TrackAssoc const& trackAssocs) + { + fullSkimming(collisions, bcs, zdcs, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &emcalClusters, &emcalMatchedTracks); + } + // produce the barrel-only DQ skimmed barrel data model, with V0 tagged tracks void processPPBarrelOnlyWithV0s(MyEventsWithMults const& collisions, MyBCs const& bcs, MyBarrelTracksWithV0BitsNoTOF const& tracksBarrel, @@ -1917,39 +2121,39 @@ struct TableMaker { } // produce the full DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter - void processPbPb(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, + void processPbPb(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, MyBarrelTracksWithCov const& tracksBarrel, MyMuonsWithCov const& muons, MFTTracks const& mftTracks, TrackAssoc const& trackAssocs, FwdTrackAssoc const& fwdTrackAssocs, - MFTTrackAssoc const& mftAssocs) + MFTTrackAssoc const& mftAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { - fullSkimming(collisions, bcs, nullptr, tracksBarrel, muons, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, nullptr, nullptr, nullptr, nullptr); + fullSkimming(collisions, bcs, zdcs, tracksBarrel, muons, mftTracks, trackAssocs, fwdTrackAssocs, mftAssocs, nullptr, ft0s, fv0as, fdds); } // produce the barrel only DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter - void processPbPbBarrelOnly(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, + void processPbPbBarrelOnly(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, MyBarrelTracksWithCov const& tracksBarrel, - TrackAssoc const& trackAssocs) + TrackAssoc const& trackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { computeOccupancyEstimators(collisions, tracksPosWithCov, tracksNegWithCov, presliceWithCov, bcs); computeCollMergingTag(collisions, tracksBarrel, presliceWithCov); - fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the barrel only DQ skimmed data model typically for Pb-Pb (with centrality), no TOF - void processPbPbBarrelOnlyNoTOF(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, + void processPbPbBarrelOnlyNoTOF(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, MyBarrelTracksWithCovNoTOF const& tracksBarrel, - TrackAssoc const& trackAssocs) + TrackAssoc const& trackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { computeOccupancyEstimators(collisions, tracksPosWithCovNoTOF, tracksNegWithCovNoTOF, presliceWithCovNoTOF, bcs); computeCollMergingTag(collisions, tracksBarrel, presliceWithCovNoTOF); - fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the barrel-only DQ skimmed data model typically for UPC Pb-Pb (no centrality), subscribe to the DQ rapidity gap event filter (filter-PbPb) - void processPbPbWithFilterBarrelOnly(MyEventsWithMultsAndRapidityGapFilter const& collisions, MyBCs const& bcs, aod::Zdcs& zdcs, + void processPbPbWithFilterBarrelOnly(MyEventsWithMultsAndRapidityGapFilter const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, MyBarrelTracksWithCov const& tracksBarrel, - TrackAssoc const& trackAssocs, aod::FT0s& ft0s, aod::FV0As& fv0as, aod::FDDs& fdds) + TrackAssoc const& trackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { computeOccupancyEstimators(collisions, tracksPosWithCov, tracksNegWithCov, presliceWithCov, bcs); computeCollMergingTag(collisions, tracksBarrel, presliceWithCov); @@ -1957,37 +2161,45 @@ struct TableMaker { } // produce the barrel only DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter - void processPbPbBarrelOnlyWithV0Bits(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, + void processPbPbBarrelOnlyWithV0Bits(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, MyBarrelTracksWithV0Bits const& tracksBarrel, - TrackAssoc const& trackAssocs) + TrackAssoc const& trackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { computeOccupancyEstimators(collisions, tracksPos, tracksNeg, preslice, bcs); computeCollMergingTag(collisions, tracksBarrel, preslice); - fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the barrel only DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter - void processPbPbBarrelOnlyWithV0BitsNoTOF(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, + void processPbPbBarrelOnlyWithV0BitsNoTOF(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, MyBarrelTracksWithV0BitsNoTOF const& tracksBarrel, - TrackAssoc const& trackAssocs) + TrackAssoc const& trackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { computeOccupancyEstimators(collisions, tracksPosNoTOF, tracksNegNoTOF, presliceNoTOF, bcs); computeCollMergingTag(collisions, tracksBarrel, presliceNoTOF); - fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + fullSkimming(collisions, bcs, nullptr, tracksBarrel, nullptr, nullptr, trackAssocs, nullptr, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the muon only DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter - void processPbPbMuonOnly(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, - MyMuonsWithCov const& muons, FwdTrackAssoc const& fwdTrackAssocs, aod::FT0s& ft0s, aod::FV0As& fv0as, aod::FDDs& fdds) + void processPbPbMuonOnly(MyEventsWithCentAndMults const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, + MyMuonsWithCov const& muons, FwdTrackAssoc const& fwdTrackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) + { + fullSkimming(collisions, bcs, zdcs, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); + } + + // produce the muon-only DQ skimmed data model typically for UPC Pb-Pb (no centrality), subscribe to the DQ rapidity gap event filter (filter-PbPb) + void processPbPbWithFilterMuonOnly(MyEventsWithMultsAndRapidityGapFilter const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, + MyMuonsWithCov const& muons, FwdTrackAssoc const& fwdTrackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { - fullSkimming(collisions, bcs, nullptr, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); + fullSkimming(collisions, bcs, zdcs, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the muon only DQ skimmed data model typically for Pb-Pb (with centrality and flow), no subscribtion to the DQ event filter - void processPbPbMuonOnlyWithQvect(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, - MyMuonsWithCov const& muons, FwdTrackAssoc const& fwdTrackAssocs, aod::FT0s& ft0s, aod::FV0As& fv0as, aod::FDDs& fdds) + // no DCA table filled by the FwdTracExtension to optimize the memory consumption + void processPbPbStreamMuonOnly(MyEventsWithCentAndMultsQvect const& collisions, MyBCs const& bcs, aod::Zdcs const& zdcs, + MyMuonsNoDca const& muons, FwdTrackAssoc const& fwdTrackAssocs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, aod::FDDs const& fdds) { - fullSkimming(collisions, bcs, nullptr, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); + fullSkimming(collisions, bcs, zdcs, nullptr, muons, nullptr, nullptr, fwdTrackAssocs, nullptr, nullptr, ft0s, fv0as, fdds); } // produce the realigned muon only DQ skimmed data model typically for Pb-Pb (with centrality), no subscribtion to the DQ event filter @@ -2018,24 +2230,27 @@ struct TableMaker { void processOnlyBCs(soa::Join::iterator const& bc) { for (int i = 0; i < o2::aod::evsel::kNsel; i++) { - if (bc.selection_bit(i) > 0) { - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(0.0, static_cast(i)); + if (static_cast(bc.selection_bit(i)) > 0) { + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(0.0, static_cast(i)); } } - (reinterpret_cast(fStatsList->At(kStatsEvent)))->Fill(0.0, static_cast(o2::aod::evsel::kNsel)); + (dynamic_cast(fStatsList->At(kStatsEvent)))->Fill(0.0, static_cast(o2::aod::evsel::kNsel)); } PROCESS_SWITCH(TableMaker, processPP, "Build full DQ skimmed data model for pp/p-Pb w/o event filtering (use Zorro)", false); + PROCESS_SWITCH(TableMaker, processPPWithEMCal, "Build full DQ skimmed data model for pp/p-Pb w/o event filtering, with EMCal clusters", false); PROCESS_SWITCH(TableMaker, processPPWithFilter, "Build full DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb, w/ event filtering", false); PROCESS_SWITCH(TableMaker, processPPWithFilterBarrelOnly, "Build barrel only DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb, w/ event filtering", false); PROCESS_SWITCH(TableMaker, processPPWithFilterMuonOnly, "Build muon only DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb, w/ event filtering", false); PROCESS_SWITCH(TableMaker, processPPWithFilterMuonMFT, "Build muon + mft DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb, w/ event filtering", false); PROCESS_SWITCH(TableMaker, processPPBarrelOnly, "Build barrel only DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb", false); + PROCESS_SWITCH(TableMaker, processPPBarrelOnlyWithEMCal, "Build barrel only DQ skimmed data model with EMCal clusters, typically for pp", false); PROCESS_SWITCH(TableMaker, processPPBarrelOnlyWithV0s, "Build barrel only DQ skimmed data model, pp like, with V0 tagged tracks", false); PROCESS_SWITCH(TableMaker, processPPMuonOnly, "Build muon only DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb", false); PROCESS_SWITCH(TableMaker, processPPRealignedMuonOnly, "Build realigned muon only DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb", false); PROCESS_SWITCH(TableMaker, processPPMuonMFT, "Build muon + mft DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb", false); PROCESS_SWITCH(TableMaker, processPPMuonMFTWithMultsExtra, "Build muon + mft DQ skimmed data model typically for pp/p-Pb and UPC Pb-Pb", false); + PROCESS_SWITCH(TableMaker, processPPMuonRefit, "Build muon + mft DQ skimmed data model with MFT covariances for global muon refit, typically for pp/p-Pb", false); PROCESS_SWITCH(TableMaker, processPbPb, "Build full DQ skimmed data model typically for Pb-Pb, w/o event filtering", false); PROCESS_SWITCH(TableMaker, processPbPbBarrelOnly, "Build barrel only DQ skimmed data model typically for Pb-Pb, w/o event filtering", false); PROCESS_SWITCH(TableMaker, processPbPbBarrelOnlyNoTOF, "Build barrel only DQ skimmed data model typically for Pb-Pb, w/o event filtering, no TOF", false); @@ -2043,13 +2258,14 @@ struct TableMaker { PROCESS_SWITCH(TableMaker, processPbPbBarrelOnlyWithV0Bits, "Build barrel only DQ skimmed data model typically for Pb-Pb, w/ V0 bits, w/o event filtering", false); PROCESS_SWITCH(TableMaker, processPbPbBarrelOnlyWithV0BitsNoTOF, "Build barrel only DQ skimmed data model typically for Pb-Pb, w/ V0 bits, no TOF, w/o event filtering", false); PROCESS_SWITCH(TableMaker, processPbPbMuonOnly, "Build muon only DQ skimmed data model typically for Pb-Pb, w/o event filtering", false); - PROCESS_SWITCH(TableMaker, processPbPbMuonOnlyWithQvect, "Build muon only DQ skimmed data model for Pb-Pb, with event properties and flow", false); + PROCESS_SWITCH(TableMaker, processPbPbWithFilterMuonOnly, "Build muon only DQ skimmed data model typically for UPC Pb-Pb, w/ event filtering", false); + PROCESS_SWITCH(TableMaker, processPbPbStreamMuonOnly, "Build muon only DQ skimmed data model for Pb-Pb, with event properties and flow for streaming", false); PROCESS_SWITCH(TableMaker, processPbPbRealignedMuonOnly, "Build realigned muon only DQ skimmed data model typically for Pb-Pb, w/o event filtering", false); PROCESS_SWITCH(TableMaker, processPbPbMuonMFT, "Build muon + mft DQ skimmed data model typically for Pb-Pb, w/o event filtering", false); PROCESS_SWITCH(TableMaker, processOnlyBCs, "Analyze the BCs to store sampled lumi", false); }; -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar) { // // Define here the histograms for all the classes required in analysis. @@ -2057,7 +2273,7 @@ void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurab // The histogram classes and their components histograms are defined below depending on the name of the histogram class // std::unique_ptr objArray(histClasses.Tokenize(";")); - for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { + for (int iclass = 0; iclass < objArray->GetEntries(); ++iclass) { TString classStr = objArray->At(iclass)->GetName(); histMan->AddHistClass(classStr.Data()); diff --git a/PWGDQ/Tasks/CMakeLists.txt b/PWGDQ/Tasks/CMakeLists.txt index 4bb0cf6d714..59594f03b54 100644 --- a/PWGDQ/Tasks/CMakeLists.txt +++ b/PWGDQ/Tasks/CMakeLists.txt @@ -9,68 +9,138 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +# tableReader_withAssoc.h is 5000 lines of task definitions, and seven +# workflow variants below #include it verbatim -- differing only in which +# adaptAnalysisTask<> entries they list, with identical preprocessor state and +# identical link libraries. Without this it is parsed and instantiated seven +# times, and it is the heaviest thing in the PWG: a cc1plus compiling one of +# these was OOM-killed at ~8 GB RSS. +# +# Skipped under recc, which caches compilations remotely instead. +add_library(TableReaderWithAssocPCH OBJECT tableReaderWithAssocPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET TableReaderWithAssocPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(TableReaderWithAssocPCH PUBLIC O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(TableReaderWithAssocPCH PRIVATE + [["PWGDQ/Tasks/tableReader_withAssoc.h"]]) +endif() + o2physics_add_dpl_workflow(table-reader SOURCES tableReader.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-reader-with-assoc - SOURCES tableReader_withAssoc.cxx + SOURCES tableReader_withAssoc_workflowSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore + REUSE_FROM TableReaderWithAssocPCH + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(table-reader-with-assoc-same-event-pairing-barrel-only + SOURCES tableReader_withAssoc_SameEventPairingBarrelOnly_workflowSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore + REUSE_FROM TableReaderWithAssocPCH + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(table-reader-with-assoc-same-event-pairing-muon-only + SOURCES tableReader_withAssoc_SameEventPairingMuonOnly_workflowSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore + REUSE_FROM TableReaderWithAssocPCH + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(table-reader-with-assoc-same-event-pairing + SOURCES tableReader_withAssoc_SameEventPairing_workflowSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore + REUSE_FROM TableReaderWithAssocPCH + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(table-reader-with-assoc-asymmetric-pairing + SOURCES tableReader_withAssoc_AsymmetricPairing_workflowSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore + REUSE_FROM TableReaderWithAssocPCH + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(table-reader-with-assoc-dilepton-track-pairing + SOURCES tableReader_withAssoc_DileptonTrackPairing_workflowSpec.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore + REUSE_FROM TableReaderWithAssocPCH + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(table-reader-with-assoc-dilepton-track-track-pairing + SOURCES tableReader_withAssoc_DileptonTrackTrackPairing_workflowSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore + REUSE_FROM TableReaderWithAssocPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(table-reader-with-assoc-direct SOURCES tableReader_withAssoc_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore O2Physics::MLCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency SOURCES dqEfficiency.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency-with-assoc SOURCES dqEfficiency_withAssoc.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(efficiency-with-assoc-direct SOURCES dqEfficiency_withAssoc_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::ReconstructionDataFormats O2::DetectorsCommonDataFormats O2::DetectorsVertexing COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(energy-correlator-direct SOURCES dqEnergyCorrelator_direct.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pp SOURCES filterPP.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pp-with-association SOURCES filterPPwithAssociation.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filter-pb-pb SOURCES filterPbPb.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::SGCutParHolder COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0-selector SOURCES v0selector.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DCAFitter O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(dalitz-selection SOURCES DalitzSelection.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow SOURCES dqFlow.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::GFWCore COMPONENT_NAME Analysis) @@ -81,21 +151,25 @@ o2physics_add_dpl_workflow(task-muon-mch-trk-eff o2physics_add_dpl_workflow(task-j-psi-hf SOURCES taskJpsiHf.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-muon-dca SOURCES muonDCA.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlation SOURCES dqCorrelation.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2Physics::GFWCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-mch-align-record SOURCES mchAlignRecord.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -116,11 +190,13 @@ o2physics_add_dpl_workflow(task-mch-align-record o2physics_add_dpl_workflow(task-muon-mid-eff SOURCES MIDefficiency.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::MIDBase + REUSE_FROM DQAnalysisPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MIDBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-fwd-track-pid SOURCES taskFwdTrackPid.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) @@ -131,35 +207,54 @@ o2physics_add_dpl_workflow(quarkonia-to-hyperons o2physics_add_dpl_workflow(model-converter-mult-pv SOURCES ModelConverterMultPv.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(model-converter-event-extended SOURCES ModelConverterEventExtended.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(model-converter-mc-reduced-event SOURCES ModelConverterReducedMCEvents.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tag-and-probe SOURCES TagAndProbe.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(qa-matching SOURCES qaMatching.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mft-mch-matcher SOURCES mftMchMatcher.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(muon-global-alignment SOURCES muonGlobalAlignment.cxx + REUSE_FROM DQAnalysisPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(j-psi-muon-correlations + SOURCES jPsiMuonCorrelations.cxx + REUSE_FROM DQAnalysisPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(global-muon-matcher + SOURCES global-muon-matcher.cxx + REUSE_FROM DQAnalysisPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGDQCore O2::MCHGeometryTransformer COMPONENT_NAME Analysis) diff --git a/PWGDQ/Tasks/DalitzSelection.cxx b/PWGDQ/Tasks/DalitzSelection.cxx index c3768b02918..ee455aae569 100644 --- a/PWGDQ/Tasks/DalitzSelection.cxx +++ b/PWGDQ/Tasks/DalitzSelection.cxx @@ -9,31 +9,42 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// Task to select electrons from dalitz decay -// It can produce track and pair histograms for selected tracks -// It creates a bitmap with selections to be stored during skimming +// +/// \file DalitzSelection.cxx +/// \author Gauthier Legras, glegras@uni-muenster.de, gauthier.legras@cern.ch +/// \brief Task to select electrons from dalitz decay +/// It can produce track and pair histograms for selected tracks +/// It creates a bitmap with selections to be stored during skimming // #include "PWGDQ/Core/AnalysisCompositeCut.h" #include "PWGDQ/Core/AnalysisCut.h" #include "PWGDQ/Core/CutsLibrary.h" #include "PWGDQ/Core/HistogramManager.h" #include "PWGDQ/Core/HistogramsLibrary.h" +#include "PWGDQ/Core/MixingHandler.h" +#include "PWGDQ/Core/MixingLibrary.h" #include "PWGDQ/Core/VarManager.h" #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include -#include +#include #include #include #include #include +#include +#include #include +#include #include #include @@ -49,6 +60,7 @@ #include #include #include +#include #include using namespace o2; @@ -57,294 +69,973 @@ using namespace o2::framework::expressions; using namespace o2::aod; using namespace o2::soa; -using MyEvents = soa::Join; +// using MyEvents = soa::Join +using MyEventsWithCent = soa::Join; -using MyBarrelTracks = soa::Join; -constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::Collision; -constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackPID; +using MyBarrelTracksNoTOF = soa::Join; + +// constexpr static uint32_t EventFillMap = VarManager::ObjTypes::Collision; +constexpr static uint32_t EventFillMapWithCent = VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent; +constexpr static uint32_t TrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackPID; +constexpr static uint32_t TrackFillMapNoTOF = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackTPCPID; -struct dalitzPairing { +struct DalitzSelection { Produces dalitzbits; Preslice perCollision = aod::track::collisionId; + Preslice trackIndicesPerCollision = aod::track_association::collisionId; // Configurables - Configurable fConfigEventCuts{"cfgEventCuts", "eventStandardNoINT7", "Event selection"}; - Configurable fConfigDalitzTrackCuts{"cfgDalitzTrackCuts", "", "Dalitz track selection cuts, separated by a comma"}; - Configurable fConfigDalitzPairCuts{"cfgDalitzPairCuts", "", "Dalitz pair selection cuts"}; - Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; - Configurable fQA{"cfgQA", true, "QA histograms"}; - Configurable fConfigBarrelTrackPINLow{"cfgBarrelLowPIN", 0.1f, "Low pt cut for Dalitz tracks in the barrel"}; - Configurable fConfigEtaCut{"cfgEtaCut", 0.9f, "Eta cut for Dalitz tracks in the barrel"}; - Configurable fConfigTPCNSigLow{"cfgTPCNSigElLow", -3.f, "Low TPCNSigEl cut for Dalitz tracks in the barrel"}; - Configurable fConfigTPCNSigHigh{"cfgTPCNSigElHigh", 3.f, "High TPCNsigEl cut for Dalitz tracks in the barrel"}; - // Configurables for TPC post-calibration maps - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/i/iarsene/Calib/TPCpostCalib", "base path to the ccdb object"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - Configurable fConfigComputeTPCpostCalib{"cfgTPCpostCalib", false, "If true, compute TPC post-calibrated n-sigmas"}; - Configurable fUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - - Service fCCDB; - o2::parameters::GRPMagField* grpmag = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + // cuts + struct : ConfigurableGroup { + Configurable fConfigEventCuts{"cfgEventCuts", "eventStandardNoINT7", "Event selection"}; + Configurable fConfigDalitzTrackCuts{"cfgDalitzTrackCuts", "", "Dalitz track selection cuts for tag, separated by a comma"}; + Configurable fConfigDalitzTrackCutsProbe{"cfgDalitzTrackCutsProbe", "", "Dalitz track selection cuts for probe, separated by a comma (if empty, use same selections as the tag)"}; + Configurable fConfigDalitzPairCuts{"cfgDalitzPairCuts", "", "Dalitz pair selection cuts"}; + Configurable fConfigTrackCutsJSON{"cfgTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; + Configurable fConfigTrackCutsProbeJSON{"cfgTrackCutsProbeJSON", "", "Additional list of barrel track cuts for the probe in JSON format"}; + Configurable fConfigPairCutsJSON{"cfgPairCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; + } fConfigCuts; + + // histograms + struct : ConfigurableGroup { + Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; + Configurable fConfigAddPairHistogram{"cfgAddPairHistogram", "", "Comma separated list of histograms"}; + Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format for tracks"}; + } fConfigHistograms; - Filter filterBarrelTrack = o2::aod::track::tpcInnerParam >= fConfigBarrelTrackPINLow && nabs(o2::aod::track::eta) <= fConfigEtaCut && o2::aod::pidtpc::tpcNSigmaEl <= fConfigTPCNSigHigh && o2::aod::pidtpc::tpcNSigmaEl >= fConfigTPCNSigLow; + // additional options + struct : ConfigurableGroup { + Configurable fConfigEnableLikeSign{"cfgEnableLikeSign", false, "Whether or not also add like-sign pairs (for studying combinatorial background which might contain misID or non-primary electrons)"}; + Configurable fQA{"cfgQA", true, "QA histograms"}; + Configurable fRemoveDoubleCounting{"cfgRemoveDoubleCounting", true, "If a track/pair is selected for several collisions, we still count it only once"}; + // Configurables for TPC post-calibration maps + Configurable fRunEventMixing{"cfgRunEventMixing", false, "Whether or not run event mixing (across TFs)"}; + Configurable fConfigMixingDepth{"cfgMixingDepth", 100, "Number of Events stored for event mixing"}; + Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; + Configurable fConfigMixingVariablesJson{"cfgMixingVarsJSON", "", "Mixing configs in JSON format"}; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/i/iarsene/Calib/TPCpostCalib", "base path to the ccdb object"}; + Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable fConfigComputeTPCpostCalib{"cfgTPCpostCalib", false, "If true, compute TPC post-calibrated n-sigmas"}; + Configurable fConfigShiftTPConly{"cfgShiftTPConly", true, "If true, shift the TPC only tracks in z for DCAz"}; + Configurable fUseRemoteField{"cfgUseRemoteField", true, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; + Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable fConfigBitsToPropagate{"cfgBitsToPropagate", 0, "bits to propagate for further analysis (e.g. MC true analysis): 0 = only probe bits (checking selected signals), 1 = tag and probe bits (checking pairs), 2 = tag and probe bits selected from like-sign (checking LS pairs)"}; + } fConfigOptions; + + Service fCCDB{}; + o2::parameters::GRPMagField* grpmag = nullptr; + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; //! the histogram manager output list OutputObj fStatsList{"Statistics"}; //! skimming statistics - std::map fTrackmap; - std::map fDalitzmap; + std::map fTrackmap; // whether it is selected with symmetric or tag cut + std::map fTrackmapProbe; // whether it is selected with probe cut + std::map fDalitzmap; // whether it is selected as dalitz decay daughter with symmetric or tag cut + std::map fDalitzmapProbe; // whether it is selected as dalitz decay daughter with probe cut + std::map fDalitzmapLS; // whether it is selected as decay daughter of a LS pair + + // maps to remove ambiguities + std::map fDalitzmapAmbiguity; + std::map fDalitzmapProbeAmbiguity; + std::map, uint8_t> fAmbiguousPairs; - AnalysisCompositeCut* fEventCut; + AnalysisCompositeCut* fEventCut = nullptr; std::vector fTrackCuts; + std::vector fTrackCutsProbe; std::vector fPairCuts; - HistogramManager* fHistMan; + bool fIsTagAndProbe = false; // whether we are doing tag and probe, or just symmetric cuts + bool fIsOutputRequested = false; + bool fSkipEvent = false; // speed up by skipping next step of event if no track/pair is selected - void init(o2::framework::InitContext&) + MixingHandler fMixingHandler; + MixingHandler::MixingEvent* fMixingEvent = nullptr; + + HistogramManager* fHistMan = nullptr; + + float fVdrift = -1.; // TPC drift velocity + int64_t fFirstTime = 0; // validity time for the v drift, granularity is smaller than one run + int64_t fLastTime = 0; + + void init(o2::framework::InitContext& context) { + fIsTagAndProbe = false; + VarManager::SetDefaultVarNames(); + // Event cuts fEventCut = new AnalysisCompositeCut(true); - TString eventCutStr = fConfigEventCuts.value; + TString eventCutStr = fConfigCuts.fConfigEventCuts.value; fEventCut->AddCut(dqcuts::GetAnalysisCut(eventCutStr.Data())); // Barrel track cuts - TString cutNamesStr = fConfigDalitzTrackCuts.value; + // Tag cuts or symmetric cuts + TString cutNamesStr = fConfigCuts.fConfigDalitzTrackCuts.value; if (!cutNamesStr.IsNull()) { std::unique_ptr objArray(cutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { fTrackCuts.push_back(*dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } + // extra cuts via JSON + TString addTrackCutsStr = fConfigCuts.fConfigTrackCutsJSON.value; + if (addTrackCutsStr != "") { + std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (const auto& t : addTrackCuts) { + fTrackCuts.push_back(*dynamic_cast(t)); + } + } + + // Probe cuts + TString cutNamesProbeStr = fConfigCuts.fConfigDalitzTrackCutsProbe.value; + TString addTrackCutsProbeStr = fConfigCuts.fConfigTrackCutsProbeJSON.value; + if (!cutNamesProbeStr.IsNull() && (cutNamesProbeStr.CompareTo(cutNamesStr) != 0 || addTrackCutsProbeStr.CompareTo(addTrackCutsStr) != 0)) { + std::unique_ptr objArray(cutNamesProbeStr.Tokenize(",")); + for (int icut = 0; icut < objArray->GetEntries(); ++icut) { + fTrackCutsProbe.push_back(*dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); + } + fIsTagAndProbe = true; + } + // extra cuts via JSON + if (addTrackCutsProbeStr != "" && (cutNamesProbeStr.CompareTo(cutNamesStr) != 0 || addTrackCutsProbeStr.CompareTo(addTrackCutsStr) != 0)) { + std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsProbeStr.Data()); + for (const auto& t : addTrackCuts) { + fTrackCutsProbe.push_back(*dynamic_cast(t)); + } + fIsTagAndProbe = true; + } // Pair cuts - TString cutNamesPairStr = fConfigDalitzPairCuts.value; + TString cutNamesPairStr = fConfigCuts.fConfigDalitzPairCuts.value; if (!cutNamesPairStr.IsNull()) { std::unique_ptr objArray(cutNamesPairStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { fPairCuts.push_back(*dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } + // extra cuts via JSON + TString addPairCutsStr = fConfigCuts.fConfigPairCutsJSON.value; + if (addPairCutsStr != "") { + std::vector addPairCuts = dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); + for (const auto& t : addPairCuts) { + fPairCuts.push_back(*dynamic_cast(t)); + } + } - if (fTrackCuts.size() != fPairCuts.size()) { + if (fTrackCuts.size() != fPairCuts.size() || (fTrackCuts.size() != fTrackCutsProbe.size() && fIsTagAndProbe)) { LOGF(fatal, "YOU SHOULD PROVIDE THE SAME NUMBER OF TRACK AND PAIR CUTS"); } // CCDB configuration - if (fConfigComputeTPCpostCalib) { - fCCDB->setURL(fConfigCcdbUrl.value); + if (fConfigOptions.fConfigComputeTPCpostCalib || fConfigOptions.fUseRemoteField || fConfigOptions.fConfigShiftTPConly) { + fCCDB->setURL(fConfigOptions.fConfigCcdbUrl.value); fCCDB->setCaching(true); fCCDB->setLocalObjectValidityChecking(); // Not later than now objects - fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); + fCCDB->setCreatedNotAfter(fConfigOptions.fConfigNoLaterThan.value); } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill - VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); // Create the histogram class names to be added to the histogram manager TString histClasses = ""; - if (fQA) { + if (fConfigOptions.fQA) { auto trackCut = fTrackCuts.begin(); - for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++) { - histClasses += Form("TrackBarrel_%s_%s;", (*trackCut).GetName(), (*pairCut).GetName()); - histClasses += Form("Pair_%s_%s;", (*trackCut).GetName(), (*pairCut).GetName()); + int iCut = 0; + for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, iCut++) { + if (fIsTagAndProbe) { + histClasses += Form("TrackBarrelTag_%s_%s_%s;", (*trackCut).GetName(), fTrackCutsProbe.at(iCut).GetName(), (*pairCut).GetName()); + histClasses += Form("TrackBarrelProbe_%s_%s_%s;", (*trackCut).GetName(), fTrackCutsProbe.at(iCut).GetName(), (*pairCut).GetName()); + histClasses += Form("Pair_%s_%s_%s;", (*trackCut).GetName(), fTrackCutsProbe.at(iCut).GetName(), (*pairCut).GetName()); + if (fConfigOptions.fConfigEnableLikeSign) { + histClasses += Form("PairLS_%s_%s_%s;", (*trackCut).GetName(), fTrackCutsProbe.at(iCut).GetName(), (*pairCut).GetName()); + } + if (fConfigOptions.fRunEventMixing) { + histClasses += Form("PairME_%s_%s_%s;", (*trackCut).GetName(), fTrackCutsProbe.at(iCut).GetName(), (*pairCut).GetName()); + histClasses += Form("PairMELS_%s_%s_%s;", (*trackCut).GetName(), fTrackCutsProbe.at(iCut).GetName(), (*pairCut).GetName()); + } + } else { + histClasses += Form("TrackBarrel_%s_%s;", (*trackCut).GetName(), (*pairCut).GetName()); + histClasses += Form("Pair_%s_%s;", (*trackCut).GetName(), (*pairCut).GetName()); + if (fConfigOptions.fConfigEnableLikeSign) { + histClasses += Form("PairLS_%s_%s;", (*trackCut).GetName(), (*pairCut).GetName()); + } + if (fConfigOptions.fRunEventMixing) { + histClasses += Form("PairME_%s_%s;", (*trackCut).GetName(), (*pairCut).GetName()); + histClasses += Form("PairMELS_%s_%s;", (*trackCut).GetName(), (*pairCut).GetName()); + } + } } } // Define histograms - std::unique_ptr objArray(histClasses.Tokenize(";")); - for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { - TString classStr = objArray->At(iclass)->GetName(); + std::unique_ptr histArray(histClasses.Tokenize(";")); + for (Int_t iclass = 0; iclass < histArray->GetEntries(); ++iclass) { + TString classStr = histArray->At(iclass)->GetName(); fHistMan->AddHistClass(classStr.Data()); if (classStr.Contains("Event")) { - dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "event", ""); + dqhistograms::DefineHistograms(fHistMan, histArray->At(iclass)->GetName(), "event", ""); } - TString histTrackName = fConfigAddTrackHistogram.value; + + TString histTrackName = fConfigHistograms.fConfigAddTrackHistogram.value; if (classStr.Contains("Track")) { - dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "track", histTrackName); + dqhistograms::DefineHistograms(fHistMan, histArray->At(iclass)->GetName(), "track", histTrackName); + dqhistograms::DefineHistograms(fHistMan, histArray->At(iclass)->GetName(), "software-trigger", histTrackName); } + TString histPairName = fConfigHistograms.fConfigAddPairHistogram.value; if (classStr.Contains("Pair")) { - dqhistograms::DefineHistograms(fHistMan, objArray->At(iclass)->GetName(), "pair", "barreldalitz"); + dqhistograms::DefineHistograms(fHistMan, histArray->At(iclass)->GetName(), "pair", histPairName); } } + // Additional histogram via the JSON configurable + TString addHistsStr = fConfigHistograms.fConfigAddJSONHistograms.value; + if (addHistsStr != "") { + dqhistograms::AddHistogramsFromJSON(fHistMan, addHistsStr.Data()); + } + fStatsList.setObject(new TList()); fStatsList->SetOwner(kTRUE); // Dalitz selection statistics: one bin for each (track,pair) selection - TH1I* histTracks = new TH1I("TrackStats", "Dalitz selection statistics", fPairCuts.size() + 1, -0.5, fPairCuts.size() + 0.5); + TH1I* histTracks = new TH1I("TrackStats", "Dalitz selection statistics", fPairCuts.size() * (1 + static_cast(fIsTagAndProbe)) + 1, -0.5, fPairCuts.size() * (1 + static_cast(fIsTagAndProbe)) + 0.5); histTracks->GetXaxis()->SetBinLabel(1, "Events selected"); auto trackCut = fTrackCuts.begin(); int icut = 1; for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { - histTracks->GetXaxis()->SetBinLabel(icut + 1, Form("%s_%s", (*trackCut).GetName(), (*pairCut).GetName())); + if (fIsTagAndProbe) { + histTracks->GetXaxis()->SetBinLabel(2 * icut, Form("%s_%s_%s tag", (*trackCut).GetName(), fTrackCutsProbe.at(icut - 1).GetName(), (*pairCut).GetName())); + histTracks->GetXaxis()->SetBinLabel(2 * icut + 1, Form("%s_%s_%s probe", (*trackCut).GetName(), fTrackCutsProbe.at(icut - 1).GetName(), (*pairCut).GetName())); + } else { + histTracks->GetXaxis()->SetBinLabel(icut + 1, Form("%s_%s", (*trackCut).GetName(), (*pairCut).GetName())); + } } - if (fQA) { + if (fConfigOptions.fQA) { fStatsList->Add(histTracks); } VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); + + // autodetect whether the dalitz bits are requested + fIsOutputRequested = false; + const auto& workflows = context.services().template get(); + // autodetect this table in other devices + for (o2::framework::DeviceSpec const& device : workflows.devices) { + // Check if this device subscribed to the dalitz table + for (auto const& input : device.inputs) { + if (o2::framework::DataSpecUtils::partialMatch(input.matcher, o2::header::DataOrigin("AOD"))) { + std::string tableName = "DalitzBits"; + if (input.matcher.binding == tableName) { + LOGF(info, "Device %s has subscribed to %s", device.name, "DalitzBits"); + fIsOutputRequested = true; + } + } + } + } + + // setup mixing handler + if (fConfigOptions.fRunEventMixing) { + TString mixVarsString = fConfigOptions.fConfigMixingVariables.value; + TString mixVarsJsonString = fConfigOptions.fConfigMixingVariablesJson.value; + std::unique_ptr objArray(mixVarsString.Tokenize(",")); + if (objArray->GetEntries() > 0 || mixVarsJsonString != "") { + if (objArray->GetEntries() > 0) { + for (int iVar = 0; iVar < objArray->GetEntries(); ++iVar) { + dqmixing::SetUpMixing(&fMixingHandler, objArray->At(iVar)->GetName()); + } + } + } + if (mixVarsJsonString != "") { + dqmixing::SetUpMixingFromJSON(&fMixingHandler, mixVarsJsonString.Data()); + } + fMixingHandler.SetPoolDepth(fConfigOptions.fConfigMixingDepth); + fMixingHandler.Init(); + } } - template - void runTrackSelection(TTracks const& tracksBarrel) + template + void runTrackSelection(TTracks const& tracksBarrel, TEvent const& collision) { - for (auto& track : tracksBarrel) { - uint8_t filterMap = uint8_t(0); - VarManager::FillTrack(track); + fSkipEvent = true; + for (const auto& track1 : tracksBarrel) { + auto filterMap = static_cast(0); + auto filterMapProbe = static_cast(0); + int fullTrackIdx = 0; + if constexpr (isReassoc) { + auto const& fullTrack = track1.template track_as(); + VarManager::FillTrack(fullTrack); + VarManager::FillTrackCollision(fullTrack, collision); + // if the track is TPC only, we shift it in z to be compatible with vertex time + if (!fullTrack.hasITS() && !fullTrack.hasTOF() && !fullTrack.hasTRD() && fVdrift > 0. && fConfigOptions.fConfigShiftTPConly) { + float tTB = 0.; + if (collision.collisionTimeRes() < 0.f || fullTrack.collisionId() < 0) { // use track data + tTB = fullTrack.trackTime(); + } else { + // The TPC track can be associated to a different BC than the one the collision under assumption is; + // we need to calculate the difference and subtract this from the trackTime() + const auto trackBC = fullTrack.template collision_as().template bc_as().globalBC(); + const auto colBC = collision.template foundBC_as().globalBC(); + float sign{1.f}; + uint64_t diffBC{0}; + if (colBC < trackBC) { + sign = -1.f; + diffBC = (trackBC - colBC); + } else { + diffBC = (colBC - trackBC); + } + float diffBCNS = sign * static_cast(diffBC) * static_cast(o2::constants::lhc::LHCBunchSpacingNS); + tTB = collision.collisionTime() + diffBCNS; + } + float dTime = tTB - fullTrack.trackTime(); + float dDrift = dTime * fVdrift; + VarManager::fgValues[VarManager::kTrackDCAz] += ((fullTrack.tgl() < 0.) ? -dDrift : dDrift); + } + fullTrackIdx = fullTrack.globalIndex(); + } else { + VarManager::FillTrack(track1); + fullTrackIdx = track1.globalIndex(); + } int i = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); ++cut, ++i) { - if ((*cut).IsSelected(VarManager::fgValues)) { - filterMap |= (uint8_t(1) << i); + if ((*cut).IsSelected(static_cast(VarManager::fgValues))) { + filterMap |= (static_cast(1) << i); + fSkipEvent = false; + } + } + if (fIsTagAndProbe) { + i = 0; + for (auto cut = fTrackCutsProbe.begin(); cut != fTrackCutsProbe.end(); ++cut, ++i) { + if ((*cut).IsSelected(static_cast(VarManager::fgValues))) { + filterMapProbe |= (static_cast(1) << i); + } } } if (filterMap) { - fTrackmap[track.globalIndex()] = filterMap; + fTrackmap[fullTrackIdx] = filterMap; + } + if (filterMapProbe) { + fTrackmapProbe[fullTrackIdx] = filterMapProbe; + } + + if (fConfigOptions.fRunEventMixing && (filterMap || filterMapProbe)) { + // add the track to event mixing, the track1 array is used for the tag and the track2 array is used for the probe + if constexpr (isReassoc) { + auto const& fullTrack = track1.template track_as(); + if (filterMap) { + // we use the 8th bit to keep track of the track sign + MixingHandler::MixingTrack mixingTrack(fullTrack.pt(), fullTrack.eta(), fullTrack.phi(), fullTrack.sign() > 0 ? (static_cast(filterMap) | (static_cast(1) << 8)) : static_cast(filterMap)); + fMixingEvent->AddTrack1(mixingTrack); + } + if (filterMapProbe) { + // we use the 8th bit to keep track of the track sign + MixingHandler::MixingTrack mixingTrack(fullTrack.pt(), fullTrack.eta(), fullTrack.phi(), fullTrack.sign() > 0 ? (static_cast(filterMapProbe) | (static_cast(1) << 8)) : static_cast(filterMapProbe)); + fMixingEvent->AddTrack2(mixingTrack); + } + } else { + if (filterMap) { + // we use the 8th bit to keep track of the track sign + MixingHandler::MixingTrack mixingTrack(track1.pt(), track1.eta(), track1.phi(), track1.sign() > 0 ? (static_cast(filterMap) | (static_cast(1) << 8)) : static_cast(filterMap)); + fMixingEvent->AddTrack1(mixingTrack); + } + if (filterMapProbe) { + // we use the 8th bit to keep track of the track sign + MixingHandler::MixingTrack mixingTrack(track1.pt(), track1.eta(), track1.phi(), track1.sign() > 0 ? (static_cast(filterMapProbe) | (static_cast(1) << 8)) : static_cast(filterMapProbe)); + fMixingEvent->AddTrack2(mixingTrack); + } + } } } // end loop over tracks } - template - void runDalitzPairing(TTracks const& tracks1, TTracks const& tracks2) + template + void runDalitzPairing(TTracks const& tracks1, TTracks const& tracks2, TEvent const& collision) { - for (auto& [track1, track2] : o2::soa::combinations(CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { - if (track1.sign() * track2.sign() > 0) { - continue; + if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { + fDalitzmapAmbiguity.clear(); + fDalitzmapProbeAmbiguity.clear(); + } + fSkipEvent = true; + + for (const auto& track1 : tracks1) { + int trackIdx1 = 0; + bool isTrack1P = false; + + if constexpr (isReassoc) { + auto const& fullTrack1 = track1.template track_as(); + trackIdx1 = fullTrack1.globalIndex(); + isTrack1P = fullTrack1.sign() > 0; + } else { + trackIdx1 = track1.globalIndex(); + isTrack1P = track1.sign() > 0; } - uint8_t twoTracksFilterMap = fTrackmap[track1.globalIndex()] & fTrackmap[track2.globalIndex()]; - if (!twoTracksFilterMap) + if (!fTrackmap[trackIdx1]) { continue; + } - // pairing - VarManager::FillPair(track1, track2); + for (const auto& track2 : tracks2) { + int trackIdx2 = 0; + bool isLikeSign = false; + if constexpr (isReassoc) { + auto const& fullTrack2 = track2.template track_as(); + trackIdx2 = fullTrack2.globalIndex(); + isLikeSign = (isTrack1P && (fullTrack2.sign() > 0)) || (!isTrack1P && (fullTrack2.sign() < 0)); + } else { + trackIdx2 = track2.globalIndex(); + isLikeSign = (isTrack1P && (track2.sign() > 0)) || (!isTrack1P && (track2.sign() < 0)); + } - // Fill pair selection map and fill pair histogram - int icut = 0; - auto trackCut = fTrackCuts.begin(); - for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { - if (!(twoTracksFilterMap & (uint8_t(1) << icut))) { + if (trackIdx1 == trackIdx2) { continue; } - if ((*pairCut).IsSelected(VarManager::fgValues)) { - fDalitzmap[track1.globalIndex()] |= (uint8_t(1) << icut); - fDalitzmap[track2.globalIndex()] |= (uint8_t(1) << icut); - if (fQA) { - fHistMan->FillHistClass(Form("Pair_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), VarManager::fgValues); - } + if (!fIsTagAndProbe && trackIdx1 >= trackIdx2) { + continue; + } + if (!fConfigOptions.fConfigEnableLikeSign && isLikeSign) { + continue; + } + + uint8_t twoTracksFilterMap = fTrackmap[trackIdx1] & (fIsTagAndProbe ? fTrackmapProbe[trackIdx2] : fTrackmap[trackIdx2]); + if (!twoTracksFilterMap) { + continue; + } + + // pairing + if constexpr (isReassoc) { + auto const& fullTrack1 = track1.template track_as(); + auto const& fullTrack2 = track2.template track_as(); + VarManager::FillPair(fullTrack1, fullTrack2); + } else { + VarManager::FillPair(track1, track2); } + + // Fill pair selection map and fill pair histogram + int icut = 0; + auto trackCut = fTrackCuts.begin(); + for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { + if (!(twoTracksFilterMap & (static_cast(1) << icut))) { + continue; + } + if ((*pairCut).IsSelected(static_cast(VarManager::fgValues))) { + fSkipEvent = false; + bool isPairAlreadySelected = false; + if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { + // if we remove double counting and the pair is already selected, we don't fill the histograms + std::pair iPair(trackIdx1, trackIdx2); + if (fAmbiguousPairs.contains(iPair)) { + if (fAmbiguousPairs[iPair] & (static_cast(1) << icut)) { // if this pair is already stored with this cut + isPairAlreadySelected = true; + } else { + fAmbiguousPairs[iPair] |= static_cast(1) << icut; + } + } else { + fAmbiguousPairs[iPair] = static_cast(1) << icut; + } + } + if (!isLikeSign) { + if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { + // if we want to remove double-counting, we fill separately the map for this event and for previous ones, in order to check if it was previously selected + fDalitzmapAmbiguity[trackIdx1] |= (static_cast(1) << icut); + } else { + fDalitzmap[trackIdx1] |= (static_cast(1) << icut); + } + if (fIsTagAndProbe) { + if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { + fDalitzmapProbeAmbiguity[trackIdx2] |= (static_cast(1) << icut); + } else { + fDalitzmapProbe[trackIdx2] |= (static_cast(1) << icut); + } + if (fConfigOptions.fQA && !isPairAlreadySelected) { + fHistMan->FillHistClass(Form("Pair_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); + } + } else { + if (isReassoc & fConfigOptions.fRemoveDoubleCounting) { + fDalitzmapAmbiguity[trackIdx2] |= (static_cast(1) << icut); + } else { + fDalitzmap[trackIdx2] |= (static_cast(1) << icut); + } + if (fConfigOptions.fQA && !isPairAlreadySelected) { + fHistMan->FillHistClass(Form("Pair_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); + } + } + } else { + if (fConfigOptions.fQA && !isPairAlreadySelected) { + fHistMan->FillHistClass(fIsTagAndProbe ? Form("PairLS_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()) : Form("PairLS_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); + if (fConfigOptions.fConfigBitsToPropagate == 2) { + fDalitzmapLS[trackIdx1] |= (static_cast(1) << icut); + fDalitzmapLS[trackIdx2] |= (static_cast(1) << icut); + } + } + } // end if like-sign + } // end if isSelected + } // end cut loop } } // end of tracksP,N loop // Fill Hists - if (fQA) { - for (auto& track : tracks1) { - uint8_t filterMap = fDalitzmap[track.globalIndex()]; - if (!filterMap) { - continue; + if (fConfigOptions.fQA && !fSkipEvent) { + for (const auto& track : tracks1) { + auto filterMap = static_cast(0); + auto filterMapProbe = static_cast(0); + if constexpr (isReassoc) { + auto const& fullTrack = track.template track_as(); + filterMap = fDalitzmap[fullTrack.globalIndex()]; // cppcheck-suppress redundantInitialization + filterMapProbe = fDalitzmapProbe[fullTrack.globalIndex()]; // cppcheck-suppress redundantInitialization + if (fConfigOptions.fRemoveDoubleCounting) { + // we remove track selections which were already selected before + uint8_t currentFilterMap = fDalitzmapAmbiguity[fullTrack.globalIndex()]; + uint8_t currentFilterMapProbe = fDalitzmapProbeAmbiguity[fullTrack.globalIndex()]; + filterMap = currentFilterMap & ~filterMap; + filterMapProbe = currentFilterMapProbe & ~filterMapProbe; + // Then only we can fill the final map + fDalitzmap[fullTrack.globalIndex()] |= fDalitzmapAmbiguity[fullTrack.globalIndex()]; + fDalitzmapProbe[fullTrack.globalIndex()] |= fDalitzmapProbeAmbiguity[fullTrack.globalIndex()]; + } + + if (!filterMap && !filterMapProbe) { + continue; + } + + VarManager::FillTrack(fullTrack); + // The caveat here is that we only fill the DCA to the first selected collision + VarManager::FillTrackCollision(fullTrack, collision); + // if the track is TPC only, we shift it in z to be compatible with vertex time + if (!fullTrack.hasITS() && !fullTrack.hasTOF() && !fullTrack.hasTRD() && fVdrift > 0. && fConfigOptions.fConfigShiftTPConly) { + float tTB = 0.; + if (collision.collisionTimeRes() < 0.f || fullTrack.collisionId() < 0) { // use track data + tTB = fullTrack.trackTime(); + } else { + // The TPC track can be associated to a different BC than the one the collision under assumption is; + // we need to calculate the difference and subtract this from the trackTime() + const auto trackBC = fullTrack.template collision_as().template bc_as().globalBC(); + const auto colBC = collision.template foundBC_as().globalBC(); + float sign{1.f}; + uint64_t diffBC{0}; + if (colBC < trackBC) { + sign = -1.f; + diffBC = (trackBC - colBC); + } else { + diffBC = (colBC - trackBC); + } + float diffBCNS = sign * static_cast(diffBC) * static_cast(o2::constants::lhc::LHCBunchSpacingNS); + tTB = collision.collisionTime() + diffBCNS; + } + float dTime = tTB - fullTrack.trackTime(); + float dDrift = dTime * fVdrift; + VarManager::fgValues[VarManager::kTrackDCAz] += ((fullTrack.tgl() < 0.) ? -dDrift : dDrift); + } + } else { + filterMap = fDalitzmap[track.globalIndex()]; + filterMapProbe = fDalitzmapProbe[track.globalIndex()]; + + if (!filterMap && !filterMapProbe) { + continue; + } + + VarManager::FillTrack(track); } - VarManager::FillTrack(track); int icut = 0; auto trackCut = fTrackCuts.begin(); for (auto pairCut = fPairCuts.begin(); pairCut != fPairCuts.end(); pairCut++, trackCut++, icut++) { - if (filterMap & (uint8_t(1) << icut)) { - reinterpret_cast(fStatsList->At(0))->Fill(icut + 1); - fHistMan->FillHistClass(Form("TrackBarrel_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), VarManager::fgValues); + if (filterMap & (static_cast(1) << icut)) { + dynamic_cast(fStatsList->At(0))->Fill(fIsTagAndProbe ? 2 * icut + 1 : icut + 1); + fHistMan->FillHistClass(fIsTagAndProbe ? Form("TrackBarrelTag_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()) : Form("TrackBarrel_%s_%s", (*trackCut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); + } + if (filterMapProbe & (static_cast(1) << icut)) { + dynamic_cast(fStatsList->At(0))->Fill(2 * icut + 2); + fHistMan->FillHistClass(Form("TrackBarrelProbe_%s_%s_%s", (*trackCut).GetName(), fTrackCutsProbe.at(icut).GetName(), (*pairCut).GetName()), static_cast(VarManager::fgValues)); } } } } } - void processFullTracks(MyEvents const& collisions, aod::BCsWithTimestamps const&, soa::Filtered const& filteredTracks, MyBarrelTracks const& tracks) + void runMixing() + { + // run the mixing with the events in the pool corresponding to this event + auto& pool = fMixingHandler.GetPool(fMixingHandler.FindEventCategory(static_cast(VarManager::fgValues))); + + // Bit 8 is a special bit in this case (keeping track sign), so we don't want it to be erased, except when everything else is empty + uint32_t bitMask = (static_cast(1) << 8); + fMixingEvent->ClearFilteringMask(bitMask); + + for (auto& poolEvent : pool.events) { // o2-linter: disable=const-ref-in-for-loop (false positive, it cannot be made const since it is modified within the loop) + if ((poolEvent.filteringMask & static_cast(255)) == 0) { + // all other bits have been erased, so we can also mark bit 8 for deletion + poolEvent.filteringMask |= bitMask; + poolEvent.counters[8] = fMixingHandler.GetPoolDepth(); + } + for (auto const& t1 : fMixingEvent->tracks1) { + // tag from event 1 and probe from event 2. If not tag and probe method, all tracks are in tracks1 array + for (auto const& t2 : (fIsTagAndProbe ? poolEvent.tracks2 : poolEvent.tracks1)) { + // check the two-track filter for the mixed pair + auto mixedTwoTrackFilter = static_cast(t1.filteringFlags & t2.filteringFlags & static_cast(255)); // we keep only first 8 bits + if (mixedTwoTrackFilter == 0) { + continue; + } + VarManager::FillPairMEAcrossTFs(t1, t2); + if (fIsTagAndProbe) { + // if we run with tag-and-probe method, we often want quantities as a function of kinematics of the probe, so they need to be filled + // (They are not filled by default in the FillPairMEAcrossTFs) to keep it light + VarManager::fgValues[VarManager::kPt2] = t2.pt; + VarManager::fgValues[VarManager::kEta2] = t2.eta; + VarManager::fgValues[VarManager::kPhi2] = t2.phi; + } + bool isLS = (t1.filteringFlags & (static_cast(1) << 8)) == (t2.filteringFlags & (static_cast(1) << 8)); + for (uint32_t icut = 0; icut < fPairCuts.size(); icut++) { + if (((mixedTwoTrackFilter & (static_cast(1) << icut)) != 0) && fPairCuts.at(icut).IsSelected(static_cast(VarManager::fgValues))) { + if (fIsTagAndProbe) { + if (!isLS) { + fHistMan->FillHistClass(Form("PairME_%s_%s_%s", fTrackCuts.at(icut).GetName(), fTrackCutsProbe.at(icut).GetName(), fPairCuts.at(icut).GetName()), static_cast(VarManager::fgValues)); + } else { + fHistMan->FillHistClass(Form("PairMELS_%s_%s_%s", fTrackCuts.at(icut).GetName(), fTrackCutsProbe.at(icut).GetName(), fPairCuts.at(icut).GetName()), static_cast(VarManager::fgValues)); + } + } else { + if (!isLS) { + fHistMan->FillHistClass(Form("PairME_%s_%s", fTrackCuts.at(icut).GetName(), fPairCuts.at(icut).GetName()), static_cast(VarManager::fgValues)); + } else { + fHistMan->FillHistClass(Form("PairMELS_%s_%s", fTrackCuts.at(icut).GetName(), fPairCuts.at(icut).GetName()), static_cast(VarManager::fgValues)); + } + } + } + } + } + } + if (fIsTagAndProbe) { + for (auto const& t2 : fMixingEvent->tracks2) { + // tag from event 2 and probe from event 1 + for (auto const& t1 : poolEvent.tracks1) { + auto mixedTwoTrackFilter = static_cast(t1.filteringFlags & t2.filteringFlags & static_cast(255)); // we keep only first 8 bits + if (mixedTwoTrackFilter == 0) { + continue; + } + VarManager::FillPairMEAcrossTFs(t1, t2); + if (fIsTagAndProbe) { + // if we run with tag-and-probe method, we often want quantities as a function of kinematics of the probe, so they need to be filled + // (They are not filled by default in the FillPairMEAcrossTFs to keep it light) + VarManager::fgValues[VarManager::kPt2] = t2.pt; + VarManager::fgValues[VarManager::kEta2] = t2.eta; + VarManager::fgValues[VarManager::kPhi2] = t2.phi; + VarManager::fgValues[VarManager::kDeltaEtaPair2] = t1.eta - t2.eta; + VarManager::fgValues[VarManager::kDeltaPhiPair] = t1.phi - t2.phi; + } + bool isLS = (t1.filteringFlags & (static_cast(1) << 8)) == (t2.filteringFlags & (static_cast(1) << 8)); + for (uint32_t icut = 0; icut < fPairCuts.size(); icut++) { + if (((mixedTwoTrackFilter & (static_cast(1) << icut)) != 0) && fPairCuts.at(icut).IsSelected(static_cast(VarManager::fgValues))) { + if (!isLS) { + fHistMan->FillHistClass(Form("PairME_%s_%s_%s", fTrackCuts.at(icut).GetName(), fTrackCutsProbe.at(icut).GetName(), fPairCuts.at(icut).GetName()), static_cast(VarManager::fgValues)); + } else { + fHistMan->FillHistClass(Form("PairMELS_%s_%s_%s", fTrackCuts.at(icut).GetName(), fTrackCutsProbe.at(icut).GetName(), fPairCuts.at(icut).GetName()), static_cast(VarManager::fgValues)); + } + } + } + } // end loop on tracks of event 1 + } // end loop on tracks of event 2 + } // end if tag and probe + } // end loop on events from the pool + // add the current event to the pool + pool.UpdatePool(*fMixingEvent, fMixingHandler.GetPoolDepth()); + } + + void initNewRun(int64_t timestamp) + { + + VarManager::ResetValues(0, VarManager::kNRunWiseVariables); + + // We setup the magnetic field, because the conversion rejection cut might depend on it + float magField = 0.; + if (fConfigOptions.fUseRemoteField.value) { + grpmag = fCCDB->getForTimeStamp(fConfigOptions.grpmagPath, timestamp); + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + } else { + magField = fConfigOptions.fConfigMagField.value; + } + LOGF(info, "setting mag field to %f", magField); + if (magField == 0.) { + LOGF(fatal, "magnetic field not set correctly, please check"); + } + VarManager::SetMagneticField(magField); + + if (fConfigOptions.fConfigComputeTPCpostCalib) { + auto calibList = fCCDB->getForTimeStamp(fConfigOptions.fConfigCcdbPathTPC.value, timestamp); + VarManager::SetCalibrationObject(VarManager::kTPCElectronMean, calibList->FindObject("mean_map_electron")); + VarManager::SetCalibrationObject(VarManager::kTPCElectronSigma, calibList->FindObject("sigma_map_electron")); + VarManager::SetCalibrationObject(VarManager::kTPCPionMean, calibList->FindObject("mean_map_pion")); + VarManager::SetCalibrationObject(VarManager::kTPCPionSigma, calibList->FindObject("sigma_map_pion")); + VarManager::SetCalibrationObject(VarManager::kTPCProtonMean, calibList->FindObject("mean_map_proton")); + VarManager::SetCalibrationObject(VarManager::kTPCProtonSigma, calibList->FindObject("sigma_map_proton")); + } + } + + void initNewDF(int64_t timestamp) + { + if (fConfigOptions.fConfigShiftTPConly && (timestamp < fFirstTime || timestamp > fLastTime)) { + std::map headers; + const auto& vd = fCCDB->getForTimeStamp("TPC/Calib/VDriftTgl", timestamp, &headers); + fVdrift = vd->refVDrift * vd->corrFact * 1e-3; + fFirstTime = stol(headers["Valid-From"]); + fLastTime = stol(headers["Valid-Until"]); + headers.clear(); + LOGF(info, "updated v drift with %f cm/ns, valid from %d to %d, current timestamp: %d", fVdrift, fFirstTime, fLastTime, timestamp); + } + } + + void processFullTracks(MyEventsWithCent const& collisions, aod::BCsWithTimestamps const&, soa::Filtered const& filteredTracks, MyBarrelTracks const& tracks) { const int pairType = VarManager::kDecayToEE; fDalitzmap.clear(); + fDalitzmapProbe.clear(); + fDalitzmapLS.clear(); + + bool initDFDone = false; // some quantities might need to be updated for each dataframe - for (auto& collision : collisions) { + for (const auto& collision : collisions) { fTrackmap.clear(); + fTrackmapProbe.clear(); VarManager::ResetValues(VarManager::kNRunWiseVariables, VarManager::kNBarrelTrackVariables); - VarManager::FillEvent(collision); - bool isEventSelected = fEventCut->IsSelected(VarManager::fgValues); + VarManager::FillEvent(collision); + bool isEventSelected = fEventCut->IsSelected(static_cast(VarManager::fgValues)); if (isEventSelected) { - - reinterpret_cast(fStatsList->At(0))->Fill(0); + dynamic_cast(fStatsList->At(0))->Fill(0); auto bc = collision.template bc_as(); if (fCurrentRun != bc.runNumber()) { - VarManager::ResetValues(0, VarManager::kNRunWiseVariables); - - // We setup the magnetic field, because the conversion rejection cut might depend on it - float magField = 0.; - if (fUseRemoteField.value) { - grpmag = fCCDB->getForTimeStamp(grpmagPath, bc.timestamp()); - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", bc.timestamp()); - } - } else { - magField = fConfigMagField.value; - } - LOGF(info, "setting mag field to %f", magField); - if (magField == 0.) { - LOGF(fatal, "magnetic field not set correctly, please check"); + initNewRun(bc.timestamp()); + fCurrentRun = bc.runNumber(); + } + + if (!initDFDone) { + initNewDF(bc.timestamp()); + initDFDone = true; + } + + if (fConfigOptions.fRunEventMixing) { + fMixingEvent = new MixingHandler::MixingEvent(); + } + + auto groupedFilteredTracks = filteredTracks.sliceBy(perCollision, collision.globalIndex()); + runTrackSelection(groupedFilteredTracks, nullptr); + if (!fSkipEvent) { + runDalitzPairing(groupedFilteredTracks, groupedFilteredTracks, nullptr); + } + + if (fConfigOptions.fRunEventMixing) { + if (!fMixingEvent->tracks1.empty()) { + // we require that there is at least one tag track in the event to avoid having the pool full of events with only probe tracks which become useless for mixing + runMixing(); } - VarManager::SetMagneticField(magField); - - if (fConfigComputeTPCpostCalib) { - auto calibList = fCCDB->getForTimeStamp(fConfigCcdbPathTPC.value, bc.timestamp()); - VarManager::SetCalibrationObject(VarManager::kTPCElectronMean, calibList->FindObject("mean_map_electron")); - VarManager::SetCalibrationObject(VarManager::kTPCElectronSigma, calibList->FindObject("sigma_map_electron")); - VarManager::SetCalibrationObject(VarManager::kTPCPionMean, calibList->FindObject("mean_map_pion")); - VarManager::SetCalibrationObject(VarManager::kTPCPionSigma, calibList->FindObject("sigma_map_pion")); - VarManager::SetCalibrationObject(VarManager::kTPCProtonMean, calibList->FindObject("mean_map_proton")); - VarManager::SetCalibrationObject(VarManager::kTPCProtonSigma, calibList->FindObject("sigma_map_proton")); + delete fMixingEvent; + } + } + } + + if (fIsOutputRequested) { + if (fConfigOptions.fConfigBitsToPropagate == 0) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fIsTagAndProbe ? fDalitzmapProbe[track.globalIndex()] : fDalitzmap[track.globalIndex()]); + } + } + if (fConfigOptions.fConfigBitsToPropagate == 1) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fDalitzmapProbe[track.globalIndex()] | fDalitzmap[track.globalIndex()]); + } + } + if (fConfigOptions.fConfigBitsToPropagate == 2) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fDalitzmapLS[track.globalIndex()]); + } + } + } + } + + void processFullTracksWithAssoc(MyEventsWithCent const& collisions, aod::BCsWithTimestamps const&, MyBarrelTracks const& tracks, TrackAssoc const& trackAssocs) + { + + const int pairType = VarManager::kDecayToEE; + fDalitzmap.clear(); + fDalitzmapProbe.clear(); + fDalitzmapLS.clear(); + fDalitzmapAmbiguity.clear(); + fDalitzmapProbeAmbiguity.clear(); + fAmbiguousPairs.clear(); + + bool initDFDone = false; // some quantities might need to be updated for each dataframe + + for (const auto& collision : collisions) { + fTrackmap.clear(); + fTrackmapProbe.clear(); + VarManager::ResetValues(VarManager::kNRunWiseVariables, VarManager::kNBarrelTrackVariables); + VarManager::FillEvent(collision); + bool isEventSelected = fEventCut->IsSelected(static_cast(VarManager::fgValues)); + + if (isEventSelected) { + + dynamic_cast(fStatsList->At(0))->Fill(0); + + auto bc = collision.template bc_as(); + + if (fCurrentRun != bc.runNumber()) { + initNewRun(bc.timestamp()); + fCurrentRun = bc.runNumber(); + } + + if (!initDFDone) { + initNewDF(bc.timestamp()); + initDFDone = true; + } + + if (fConfigOptions.fRunEventMixing) { + fMixingEvent = new MixingHandler::MixingEvent(); + } + + auto groupedTracksAssoc = trackAssocs.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + runTrackSelection(groupedTracksAssoc, collision); + if (!fSkipEvent) { + runDalitzPairing(groupedTracksAssoc, groupedTracksAssoc, collision); + } + if (fConfigOptions.fRunEventMixing) { + if (!fMixingEvent->tracks1.empty()) { + // we require that there is at least one tag track in the event to avoid having the pool full of events with only probe tracks which are then useless + // In addition, this allows to avoid mixing events which are too close in time, which could contain tracks from the same event due to track-to-collision reassociation + runMixing(); } + delete fMixingEvent; + } + } + } + + if (fIsOutputRequested) { + if (fConfigOptions.fConfigBitsToPropagate == 0) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fIsTagAndProbe ? fDalitzmapProbe[track.globalIndex()] : fDalitzmap[track.globalIndex()]); + } + } + if (fConfigOptions.fConfigBitsToPropagate == 1) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fDalitzmapProbe[track.globalIndex()] | fDalitzmap[track.globalIndex()]); + } + } + if (fConfigOptions.fConfigBitsToPropagate == 2) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fDalitzmapLS[track.globalIndex()]); + } + } + } + } + + void processFullTracksWithAssocNoTOF(MyEventsWithCent const& collisions, aod::BCsWithTimestamps const&, MyBarrelTracksNoTOF const& tracks, TrackAssoc const& trackAssocs) + { + + const int pairType = VarManager::kDecayToEE; + fDalitzmap.clear(); + fDalitzmapProbe.clear(); + fDalitzmapLS.clear(); + fDalitzmapAmbiguity.clear(); + fDalitzmapProbeAmbiguity.clear(); + fAmbiguousPairs.clear(); + + bool initDFDone = false; // some quantities might need to be updated for each dataframe + + for (const auto& collision : collisions) { + fTrackmap.clear(); + fTrackmapProbe.clear(); + VarManager::ResetValues(VarManager::kNRunWiseVariables, VarManager::kNBarrelTrackVariables); + VarManager::FillEvent(collision); + bool isEventSelected = fEventCut->IsSelected(static_cast(VarManager::fgValues)); + + if (isEventSelected) { + + dynamic_cast(fStatsList->At(0))->Fill(0); + + auto bc = collision.template bc_as(); + + if (fCurrentRun != bc.runNumber()) { + initNewRun(bc.timestamp()); fCurrentRun = bc.runNumber(); } - auto groupedFilteredTracks = filteredTracks.sliceBy(perCollision, collision.globalIndex()); - runTrackSelection(groupedFilteredTracks); - runDalitzPairing(groupedFilteredTracks, groupedFilteredTracks); + if (!initDFDone) { + initNewDF(bc.timestamp()); + initDFDone = true; + } + + if (fConfigOptions.fRunEventMixing) { + fMixingEvent = new MixingHandler::MixingEvent(); + } + + auto groupedTracksAssoc = trackAssocs.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + runTrackSelection(groupedTracksAssoc, collision); + if (!fSkipEvent) { + runDalitzPairing(groupedTracksAssoc, groupedTracksAssoc, collision); + } + if (fConfigOptions.fRunEventMixing) { + if (!fMixingEvent->tracks1.empty()) { + // we require that there is at least one tag track in the event to avoid having the pool full of events with only probe tracks which are then useless + // In addition, this allows to avoid mixing events which are too close in time, which could contain tracks from the same event due to track-to-collision reassociation + runMixing(); + } + delete fMixingEvent; + } } } - for (auto& track : tracks) { // Fill dalitz bits - dalitzbits(fDalitzmap[track.globalIndex()]); + if (fIsOutputRequested) { + if (fConfigOptions.fConfigBitsToPropagate == 0) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fIsTagAndProbe ? fDalitzmapProbe[track.globalIndex()] : fDalitzmap[track.globalIndex()]); + } + } + if (fConfigOptions.fConfigBitsToPropagate == 1) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fDalitzmapProbe[track.globalIndex()] | fDalitzmap[track.globalIndex()]); + } + } + if (fConfigOptions.fConfigBitsToPropagate == 2) { + for (const auto& track : tracks) { // Fill dalitz bits + dalitzbits(fDalitzmapLS[track.globalIndex()]); + } + } } } - void processDummy(MyEvents&) + void processDummy(MyEventsWithCent const&) { } - PROCESS_SWITCH(dalitzPairing, processFullTracks, "Run Dalitz selection on AO2D tables", false); - PROCESS_SWITCH(dalitzPairing, processDummy, "Do nothing", false); + PROCESS_SWITCH(DalitzSelection, processFullTracksWithAssocNoTOF, "Run Dalitz selection on AO2D tables with reassociation and without TOF PID", false); + PROCESS_SWITCH(DalitzSelection, processFullTracksWithAssoc, "Run Dalitz selection on AO2D tables with reassociation", false); + PROCESS_SWITCH(DalitzSelection, processFullTracks, "Run Dalitz selection on AO2D tables", false); + PROCESS_SWITCH(DalitzSelection, processDummy, "Do nothing", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGDQ/Tasks/MIDefficiency.cxx b/PWGDQ/Tasks/MIDefficiency.cxx index 26bca045c2b..3e8d103e4f5 100644 --- a/PWGDQ/Tasks/MIDefficiency.cxx +++ b/PWGDQ/Tasks/MIDefficiency.cxx @@ -78,7 +78,7 @@ struct midEfficiency { o2::mid::Mapping mapping; // Filter only for MCH-MID matched tracks - Filter muonTrackType = aod::fwdtrack::trackType == uint8_t(3); + Filter muonTrackType = aod::fwdtrack::trackType == static_cast(3); void init(o2::framework::InitContext const& /*ic*/) { diff --git a/PWGDQ/Tasks/TagAndProbe.cxx b/PWGDQ/Tasks/TagAndProbe.cxx index b637ec64aad..9fec1a9907a 100644 --- a/PWGDQ/Tasks/TagAndProbe.cxx +++ b/PWGDQ/Tasks/TagAndProbe.cxx @@ -79,7 +79,7 @@ constexpr static uint32_t gkEventFillMapWithCov = VarManager::ObjTypes::ReducedE constexpr static uint32_t gkMuonFillMapWithCov = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra | VarManager::ObjTypes::ReducedMuonCov; // Global function used to define needed histogram classes -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups); // defines histograms for all tasks template void PrintBitMap(TMap map, int nbits) @@ -213,7 +213,7 @@ struct AnalysisTagAndProbe { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms // dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill @@ -348,7 +348,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/dqCorrelation.cxx b/PWGDQ/Tasks/dqCorrelation.cxx index 890bdefe46b..30c0bc1a38b 100644 --- a/PWGDQ/Tasks/dqCorrelation.cxx +++ b/PWGDQ/Tasks/dqCorrelation.cxx @@ -286,7 +286,7 @@ struct DqCumulantFlow { float weff = 1.0, wacc = 1.0; if (dileptons.size() > 0) { - for (auto track : tracks) { + for (const auto& track : tracks) { trackGlobalIndexes.push_back(track.globalIndex()); } @@ -316,7 +316,7 @@ struct DqCumulantFlow { } } - for (auto dilepton : dileptons) { + for (const auto& dilepton : dileptons) { registry.fill(HIST("dimuon_mass"), dilepton.mass()); VarManager::FillTrack(dilepton, fValuesDilepton); diff --git a/PWGDQ/Tasks/dqEfficiency.cxx b/PWGDQ/Tasks/dqEfficiency.cxx index 45e84b4c3e4..0495204234c 100644 --- a/PWGDQ/Tasks/dqEfficiency.cxx +++ b/PWGDQ/Tasks/dqEfficiency.cxx @@ -95,7 +95,7 @@ constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::ReducedMuon | Va constexpr static uint32_t gkMuonFillMapWithCov = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra | VarManager::ObjTypes::ReducedMuonCov; constexpr static uint32_t gkParticleMCFillMap = VarManager::ObjTypes::ParticleMC; -void DefineHistograms(HistogramManager* histMan, TString histClasses); +void DefineHistograms(HistogramManager* histMan, const TString& histClasses); struct AnalysisEventSelection { Produces eventSel; @@ -103,8 +103,8 @@ struct AnalysisEventSelection { Configurable fConfigEventCuts{"cfgEventCuts", "eventStandard", "Event selection"}; Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - HistogramManager* fHistMan; - AnalysisCompositeCut* fEventCut; + HistogramManager* fHistMan = nullptr; + AnalysisCompositeCut* fEventCut = nullptr; void init(o2::framework::InitContext& context) { @@ -174,7 +174,7 @@ struct AnalysisTrackSelection { Configurable fConfigMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fMCSignals; // list of signals to be checked std::vector fHistNamesReco; @@ -305,13 +305,13 @@ struct AnalysisTrackSelection { } // fill histograms - for (unsigned int i = 0; i < fMCSignals.size(); i++) { - if (!(mcDecision & (static_cast(1) << i))) { + for (unsigned int iMC = 0; iMC < fMCSignals.size(); iMC++) { + if (!(mcDecision & (static_cast(1) << iMC))) { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { - if (filterMap & (uint8_t(1) << j)) { - fHistMan->FillHistClass(fHistNamesMCMatched[j][i].Data(), VarManager::fgValues); + if (filterMap & (static_cast(1) << j)) { + fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts } // end loop over MC signals @@ -343,7 +343,7 @@ struct AnalysisMuonSelection { Configurable fConfigMCSignals{"cfgMuonMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fMCSignals; // list of signals to be checked std::vector fHistNamesReco; @@ -478,13 +478,13 @@ struct AnalysisMuonSelection { } // fill histograms - for (unsigned int i = 0; i < fMCSignals.size(); i++) { - if (!(mcDecision & (static_cast(1) << i))) { + for (unsigned int iMC = 0; iMC < fMCSignals.size(); iMC++) { + if (!(mcDecision & (static_cast(1) << iMC))) { continue; } for (unsigned int j = 0; j < fTrackCuts.size(); j++) { - if (filterMap & (uint8_t(1) << j)) { - fHistMan->FillHistClass(fHistNamesMCMatched[j][i].Data(), VarManager::fgValues); + if (filterMap & (static_cast(1) << j)) { + fHistMan->FillHistClass(fHistNamesMCMatched[j][iMC].Data(), VarManager::fgValues); } } // end loop over cuts } // end loop over MC signals @@ -516,7 +516,7 @@ struct AnalysisSameEventPairing { float mMagField = 0.0; o2::parameters::GRPMagField* grpmag = nullptr; o2::base::MatLayerCylSet* lut = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; Filter filterEventSelected = aod::dqanalysisflags::isEventSelected == 1; @@ -546,7 +546,7 @@ struct AnalysisSameEventPairing { // TODO: special AnalysisCuts to be prepared in this direction // TODO: cuts on the MC truth information to be added if needed - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector> fBarrelHistNames; std::vector> fBarrelHistNamesMCmatched; std::vector> fMuonHistNames; @@ -795,7 +795,7 @@ struct AnalysisSameEventPairing { VarManager::FillPair(t1, t2); // secondary vertexing is not implemented for e-mu pairs so we need to hide this function from the e-mu analysis for now if constexpr ((TPairType == VarManager::kDecayToEE) || (TPairType == VarManager::kDecayToMuMu)) { - VarManager::FillPairVertexing(event, t1, t2, fPropToPCA, VarManager::fgValues); + VarManager::FillPairVertexing(event, t1, t2, VarManager::fgValues); } // run MC matching for this pair @@ -816,7 +816,7 @@ struct AnalysisSameEventPairing { dileptonFilterMap = twoTrackFilter; dileptonMcDecision = mcDecision; - if (!fConfigSkimSignalOnly || (fConfigSkimSignalOnly && mcDecision > 0)) { + if (!fConfigSkimSignalOnly || mcDecision > 0) { if constexpr (TPairType == VarManager::kDecayToEE) { dielectronList(event, VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), dileptonFilterMap, dileptonMcDecision); if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackCollInfo) > 0) { @@ -876,17 +876,17 @@ struct AnalysisSameEventPairing { // Loop over all fulfilled cuts and fill pair histograms for (unsigned int icut = 0; icut < ncuts; icut++) { - if (twoTrackFilter & (uint8_t(1) << icut)) { + if (twoTrackFilter & (static_cast(1) << icut)) { if (t1.sign() * t2.sign() < 0) { fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); if (fConfigAmbiguousHist && !(t1.isAmbiguous() || t2.isAmbiguous())) { fHistMan->FillHistClass(Form("%s_unambiguous", histNames[icut][0].Data()), VarManager::fgValues); } - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMCmatched[icut][isig].Data(), VarManager::fgValues); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(histNamesMCmatched[icut][iSig].Data(), VarManager::fgValues); if (fConfigAmbiguousHist && !(t1.isAmbiguous() || t2.isAmbiguous())) { - fHistMan->FillHistClass(Form("%s_unambiguous", histNamesMCmatched[icut][isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("%s_unambiguous", histNamesMCmatched[icut][iSig].Data()), VarManager::fgValues); } } } @@ -1071,12 +1071,10 @@ struct AnalysisDileptonTrack { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use two values array to avoid mixing up the quantities - float* fValuesDilepton; - float* fValuesTrack; - HistogramManager* fHistMan; + float* fValuesDilepton = nullptr; + float* fValuesTrack = nullptr; + HistogramManager* fHistMan = nullptr; - std::vector> fMuonHistNames; - std::vector> fMuonHistNamesMCmatched; std::vector fRecMCSignalsNames; std::vector fRecMCSignals; @@ -1085,7 +1083,7 @@ struct AnalysisDileptonTrack { // NOTE: the barrel track filter is shared between the filters for dilepton electron candidates (first n-bits) // and the associated hadrons (n+1 bit) --> see the barrel track selection task // The current condition should be replaced when bitwise operators will become available in Filter expressions - int fNHadronCutBit; + int fNHadronCutBit = 0; void init(o2::framework::InitContext& context) { @@ -1197,12 +1195,12 @@ struct AnalysisDileptonTrack { std::vector trackGlobalIndexes; if (dileptons.size() > 0) { - for (auto track : tracks) { + for (const auto& track : tracks) { trackGlobalIndexes.push_back(track.globalIndex()); // std::cout << track.index() << " " << track.globalIndex() << std::endl; } } - for (auto dilepton : dileptons) { + for (const auto& dilepton : dileptons) { int indexLepton1 = dilepton.index0Id(); int indexLepton2 = dilepton.index1Id(); @@ -1242,9 +1240,9 @@ struct AnalysisDileptonTrack { } } // end loop over MC signals - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonsSelected_matchedMC_%s", fRecMCSignalsNames[isig].Data()), fValuesDilepton); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(Form("DileptonsSelected_matchedMC_%s", fRecMCSignalsNames[iSig].Data()), fValuesDilepton); } } @@ -1267,7 +1265,7 @@ struct AnalysisDileptonTrack { mcDecision = 0; isig = 0; for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { - if constexpr (TTrackFillMap & VarManager::ObjTypes::ReducedTrack || TTrackFillMap & VarManager::ObjTypes::ReducedMuon || TTrackFillMap & VarManager::ObjTypes::ReducedMuon) { // for skimmed DQ model + if constexpr (TTrackFillMap & VarManager::ObjTypes::ReducedTrack || TTrackFillMap & VarManager::ObjTypes::ReducedMuon) { // for skimmed DQ model if ((*sig).CheckSignal(false, lepton1MC, lepton2MC, trackMC)) { mcDecision |= (static_cast(1) << isig); } @@ -1278,9 +1276,9 @@ struct AnalysisDileptonTrack { dileptontrackcandidatesList(mcDecision, fValuesTrack[VarManager::kPairMass], fValuesTrack[VarManager::kPairPt], fValuesTrack[VarManager::kPairEta], fValuesTrack[VarManager::kVertexingTauz], fValuesTrack[VarManager::kVertexingTauxy], fValuesTrack[VarManager::kVertexingLz], fValuesTrack[VarManager::kVertexingLxy]); } - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonTrackInvMass_matchedMC_%s", fRecMCSignalsNames[isig].Data()), fValuesTrack); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(Form("DileptonTrackInvMass_matchedMC_%s", fRecMCSignalsNames[iSig].Data()), fValuesTrack); } } } @@ -1357,7 +1355,7 @@ struct AnalysisDileptonTrackTrack { Configurable fConfigUseDCAVertexing{"cfgUseDCAVertexing", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; Produces DileptonTrackTrackTable; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fRecMCSignalsNames; std::vector fRecMCSignals; @@ -1368,10 +1366,10 @@ struct AnalysisDileptonTrackTrack { Filter dileptonFilter = aod::reducedpair::sign == 0; Filter filterBarrelTrackSelected = aod::dqanalysisflags::isBarrelSelected > 0; - float* fValuesQuadruplet; + float* fValuesQuadruplet = nullptr; std::vector fQuadrupletCutNames; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCuts; TString fTrackCutName1; TString fTrackCutName2; @@ -1504,14 +1502,14 @@ struct AnalysisDileptonTrackTrack { std::vector trackGlobalIndexes; if (dileptons.size() > 0) { - for (auto track : tracks) { + for (const auto& track : tracks) { trackGlobalIndexes.push_back(track.globalIndex()); // std::cout << track.index() << " " << track.globalIndex() << std::endl; } } // loop over dileptons - for (auto dilepton : dileptons) { + for (const auto& dilepton : dileptons) { VarManager::FillTrack(dilepton, fValuesQuadruplet); // apply the dilepton cut @@ -1585,9 +1583,9 @@ struct AnalysisDileptonTrackTrack { mcDecision |= (static_cast(1) << isig); } } - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("MCTruthRecQuad_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignalsNames[isig].Data()), fValuesQuadruplet); + for (unsigned int iSig = 0; iSig < fRecMCSignals.size(); iSig++) { + if (mcDecision & (static_cast(1) << iSig)) { + fHistMan->FillHistClass(Form("MCTruthRecQuad_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignalsNames[iSig].Data()), fValuesQuadruplet); } } } @@ -1665,7 +1663,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx index 608363e12c0..9b759f453e9 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc.cxx @@ -24,10 +24,12 @@ #include "PWGDQ/Core/VarManager.h" #include "PWGDQ/DataModel/ReducedInfoTables.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/TableHelper.h" #include #include +#include #include #include #include @@ -47,6 +49,7 @@ #include #include #include +#include #include #include @@ -59,7 +62,6 @@ #include #include #include -#include #include #include #include @@ -67,10 +69,6 @@ #include #include -using std::cout; -using std::endl; -using std::string; - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -167,16 +165,40 @@ DECLARE_SOA_COLUMN(Etaee, etaee, float); DECLARE_SOA_COLUMN(Rapee, rapee, float); DECLARE_SOA_COLUMN(Phiee, phiee, float); DECLARE_SOA_COLUMN(Ptee, ptee, float); -DECLARE_SOA_COLUMN(Lxyee, lxyee, float); -DECLARE_SOA_COLUMN(LxyeePoleMass, lxyeepolemass, float); +DECLARE_SOA_COLUMN(Lxyee, lxyJPsi2ee, float); +DECLARE_SOA_COLUMN(LxyeePoleMass, lxyJPsi2eePoleMass, float); DECLARE_SOA_COLUMN(Lzee, lzee, float); +DECLARE_SOA_COLUMN(LxyeePoleMassPVrecomputed, lxyeePoleMassPVrecomputed, float); +DECLARE_SOA_COLUMN(Vx, vx, float); +DECLARE_SOA_COLUMN(Vy, vy, float); +DECLARE_SOA_COLUMN(Vz, vz, float); +DECLARE_SOA_COLUMN(DcaXY1, dcaXY1, float); +DECLARE_SOA_COLUMN(DcaZ1, dcaZ1, float); +DECLARE_SOA_COLUMN(DcaXY2, dcaXY2, float); +DECLARE_SOA_COLUMN(DcaZ2, dcaZ2, float); DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); +DECLARE_SOA_COLUMN(PercentileFT0C, percentileFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContribJPsi2ee, float); DECLARE_SOA_COLUMN(AmbiguousInBunchPairs, AmbiguousJpsiPairsInBunch, bool); DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, AmbiguousJpsiPairsOutOfBunch, bool); DECLARE_SOA_COLUMN(Corrassoc, corrassoc, bool); +// Additional columns for prompt-non-prompt JPsi separation (BDT training), identical to tableReader_withAssoc +DECLARE_SOA_COLUMN(PairFilter, pairFilter, uint32_t); //! twoTrackFilter (cut bits + ambiguity bits 28-31) +DECLARE_SOA_COLUMN(McDecision, mcDecision, uint32_t); //! MC signal bits, 0 in data +DECLARE_SOA_COLUMN(TauxyeeErr, tauxyeeErr, float); //! kVertexingTauxyErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(TauzeeErr, tauzeeErr, float); //! kVertexingTauzErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(LxyeeProjected, lxyeeProjected, float); //! kVertexingLxyProjected +DECLARE_SOA_COLUMN(LxyeeOverErr, lxyeeOverErr, float); //! kVertexingLxyOverErr +DECLARE_SOA_COLUMN(Lxyzee, lxyzee, float); //! kVertexingLxyz +DECLARE_SOA_COLUMN(LxyzeeOverErr, lxyzeeOverErr, float); //! kVertexingLxyzOverErr +DECLARE_SOA_COLUMN(Chi2PCAee, chi2PCAee, float); //! kVertexingChi2PCA +DECLARE_SOA_COLUMN(CosPAee, cosPAee, float); //! kCosPointingAngle +DECLARE_SOA_COLUMN(DcaXYOverSig1, dcaXYOverSig1, float); +DECLARE_SOA_COLUMN(DcaZOverSig1, dcaZOverSig1, float); +DECLARE_SOA_COLUMN(DcaXYOverSig2, dcaXYOverSig2, float); +DECLARE_SOA_COLUMN(DcaZOverSig2, dcaZOverSig2, float); DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); // Candidate columns efficiency calculation for prompt-non-prompt JPsi separation @@ -218,7 +240,20 @@ DECLARE_SOA_TABLE(JPsiMuonCandidates, "AOD", "DQJPSIMUONA", dqanalysisflags::MassDileptonCandidate, dqanalysisflags::Ptpair, dqanalysisflags::Etapair, dqanalysisflags::Ptassoc, dqanalysisflags::Etaassoc, dqanalysisflags::Phiassoc, dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::Phileg1, dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::Phileg2, dqanalysisflags::McFlag); -DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); +DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", + dqanalysisflags::PairFilter, dqanalysisflags::McDecision, + dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, + dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, + dqanalysisflags::TauxyeeErr, dqanalysisflags::TauzeeErr, + dqanalysisflags::LxyeeProjected, dqanalysisflags::LxyeeOverErr, dqanalysisflags::Lxyzee, dqanalysisflags::LxyzeeOverErr, + dqanalysisflags::Chi2PCAee, dqanalysisflags::CosPAee, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, + dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::DcaXYOverSig1, dqanalysisflags::DcaZOverSig1, + dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::ITSChi2leg1, + dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::DcaXYOverSig2, dqanalysisflags::DcaZOverSig2, + dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::ITSChi2leg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, + dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0C, dqanalysisflags::MultiplicityNContrib); DECLARE_SOA_TABLE(OniaMCTruth, "AOD", "MCTRUTHONIA", dqanalysisflags::OniaPt, dqanalysisflags::OniaEta, dqanalysisflags::OniaY, dqanalysisflags::OniaPhi, dqanalysisflags::OniaVz, dqanalysisflags::OniaVtxZ, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); } // namespace o2::aod @@ -227,6 +262,7 @@ using MyEvents = soa::Join; using MyEventsVtxCov = soa::Join; using MyEventsVtxCovSelected = soa::Join; +using MyEventsVtxCovSelectedInfo = soa::Join; using MyEventsVtxCovSelectedMultExtra = soa::Join; using MyEventsVtxCovSelectedQvector = soa::Join; using MyEventsQvector = soa::Join; @@ -260,21 +296,29 @@ constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::ReducedMuon | Va constexpr static uint32_t gkMuonFillMapWithCov = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra | VarManager::ObjTypes::ReducedMuonCov; // constexpr static uint32_t gkMuonFillMapWithColl = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra | VarManager::ObjTypes::ReducedMuonCollInfo; +namespace dqefficiency_helpers +{ +inline float* varValues() { return static_cast(VarManager::fgValues); } +inline TString* varNames() { return static_cast(VarManager::fgVariableNames); } +inline TString* varUnits() { return static_cast(VarManager::fgVariableUnits); } +} // namespace dqefficiency_helpers + // Global function used to define needed histogram classes -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups); // defines histograms for all tasks -template -void PrintBitMap(TMap map, int nbits) -{ - for (int i = 0; i < nbits; i++) { - cout << ((map & (TMap(1) << i)) > 0 ? "1" : "0"); - } -} +// template +// void PrintBitMap(TMap map, int nbits) +// { +// for (int i = 0; i < nbits; i++) { +// cout << ((map & (TMap(1) << i)) > 0 ? "1" : "0"); +// } +// } // Analysis task that produces event decisions and the Hash table used in event mixing struct AnalysisEventSelection { Produces eventSel; Produces hash; + Produces JetEvents; OutputObj fOutputList{"output"}; Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; Configurable fConfigEventCuts{"cfgEventCuts", "eventStandard", "Event selection"}; @@ -291,18 +335,28 @@ struct AnalysisEventSelection { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + o2::framework::Configurable cfgUseRCT{"cfgUseRCT", false, "Apply RCT quality selection"}; + o2::framework::Configurable cfgRCTPath{"cfgRCTPath", "RCT/Flags/RunFlags", "CCDB path to the RCT run flags"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "RCT selection label"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "Also check ZDC quality"}; + o2::framework::Configurable cfgRCTPassName{"cfgRCTPassName", "", "Reconstruction pass; leave empty for run-only lookup"}; + HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; - AnalysisCompositeCut* fEventCut; + AnalysisCompositeCut* fEventCut = nullptr; - Service fCCDB; + Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; std::map fSelMap; // key: reduced event global index, value: event selection decision std::map> fBCCollMap; // key: global BC, value: vector of reduced event global indices - std::map fMetadataRCT, fHeader; - int fCurrentRun; + std::map fMetadataRCT, fHeader; + int fCurrentRun = -1; + + o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + std::map rctFlags; + int rctRun = -1; void init(o2::framework::InitContext& context) { @@ -323,7 +377,7 @@ struct AnalysisEventSelection { TString eventCutJSONStr = fConfigEventCutsJSON.value; if (eventCutJSONStr != "") { std::vector jsonCuts = dqcuts::GetCutsFromJSON(eventCutJSONStr.Data()); - for (auto& cutIt : jsonCuts) { + for (auto const& cutIt : jsonCuts) { fEventCut->AddCut(cutIt); } } @@ -333,7 +387,7 @@ struct AnalysisEventSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); DefineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;", fConfigAddEventHistogram.value.data()); if (fConfigCheckSplitCollisions) { DefineHistograms(fHistMan, "OutOfBunchCorrelations;SameBunchCorrelations;", ""); @@ -360,6 +414,61 @@ struct AnalysisEventSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); fCCDBApi.init(fConfigCcdbUrl.value); + + if (cfgUseRCT.value) { + if (cfgRCTLabel.value != "CBT" && cfgRCTLabel.value != "CBT_hadronPID" && cfgRCTLabel.value != "CBT_electronPID" && cfgRCTLabel.value != "CBT_calo" && cfgRCTLabel.value != "CBT_muon" && cfgRCTLabel.value != "CBT_muon_glo") { + LOGF(fatal, "RCT Label \"%s\" is unknown.", cfgRCTLabel.value.c_str()); + } + + rctChecker.init(cfgRCTLabel.value, cfgCheckZDC.value); + + if (!rctChecker.any()) { + LOGF(fatal, "Empty RCT mask: check cfgRCTLabel."); + } + } + } + + void loadRCT(int run, uint64_t timestamp) + { + std::map metadata{{"run", std::to_string(run)}}; + + LOG(info) << "Requesting " << cfgRCTPath.value << " run=" << run << " timestamp=" << timestamp; + + if (!cfgRCTPassName.value.empty()) { + metadata["passName"] = cfgRCTPassName.value; + } + + auto* flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + if (flags == nullptr && !cfgRCTPassName.value.empty()) { + metadata.erase("passName"); + flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + } + + if (flags == nullptr || flags->empty()) { + LOGF(fatal, "Missing or empty RCT flags for run %d.", run); + } + + rctFlags = *flags; + rctRun = run; + } + + bool passesRCT(int run, uint64_t timestamp) + { + if (!cfgUseRCT.value) { + return true; + } + + if (run != rctRun) { + loadRCT(run, timestamp); + } + + auto iterator = rctFlags.upper_bound(timestamp); + if (iterator == rctFlags.begin()) { + return false; + } + + const uint32_t flags = std::prev(iterator)->second; + return (static_cast(flags) & rctChecker.value()) == 0; } template @@ -375,7 +484,7 @@ struct AnalysisEventSelection { fSelMap.clear(); fBCCollMap.clear(); - for (auto& event : events) { + for (auto const& event : events) { // Reset the fValues array and fill event observables VarManager::ResetValues(0, VarManager::kNEventWiseVariables); VarManager::FillEvent(event); @@ -386,11 +495,11 @@ struct AnalysisEventSelection { bool decision = false; // if QA is requested fill histograms before event selections if (fConfigQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); // automatically fill all the histograms in the class Event + fHistMan->FillHistClass("Event_BeforeCuts", dqefficiency_helpers::varValues()); // automatically fill all the histograms in the class Event } - if (fEventCut->IsSelected(VarManager::fgValues)) { + if (fEventCut->IsSelected(dqefficiency_helpers::varValues()) && passesRCT(event.runNumber(), event.timestamp())) { if (fConfigQA) { - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", dqefficiency_helpers::varValues()); } decision = true; } @@ -403,17 +512,17 @@ struct AnalysisEventSelection { evIndices.push_back(event.globalIndex()); } if (fMixHandler != nullptr) { - int hh = fMixHandler->FindEventCategory(VarManager::fgValues); + int hh = fMixHandler->FindEventCategory(dqefficiency_helpers::varValues()); hash(hh); } } - for (auto& event : mcEvents) { + for (auto const& event : mcEvents) { // Reset the fValues array and fill event observables VarManager::ResetValues(0, VarManager::kNEventWiseVariables); VarManager::FillEvent(event); if (fConfigQA) { - fHistMan->FillHistClass("EventsMC", VarManager::fgValues); + fHistMan->FillHistClass("EventsMC", dqefficiency_helpers::varValues()); } } } @@ -442,7 +551,9 @@ struct AnalysisEventSelection { collisionSplittingMap[*ev1It] = true; collisionSplittingMap[*ev2It] = true; } - fHistMan->FillHistClass("SameBunchCorrelations", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("SameBunchCorrelations", dqefficiency_helpers::varValues()); + } } // end second event loop } // end first event loop } // end if BC1 events > 1 @@ -456,10 +567,10 @@ struct AnalysisEventSelection { auto const& bc2Events = bc2It->second; // loop over events in the first BC - for (auto ev1It : bc1Events) { + for (const auto& ev1It : bc1Events) { auto ev1 = events.rawIteratorAt(ev1It); // loop over events in the second BC - for (auto ev2It : bc2Events) { + for (const auto& ev2It : bc2Events) { auto ev2 = events.rawIteratorAt(ev2It); // compute 2-event quantities and mark the candidate split collisions VarManager::FillTwoEvents(ev1, ev2); @@ -467,15 +578,17 @@ struct AnalysisEventSelection { collisionSplittingMap[ev1It] = true; collisionSplittingMap[ev2It] = true; } - fHistMan->FillHistClass("OutOfBunchCorrelations", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("OutOfBunchCorrelations", dqefficiency_helpers::varValues()); + } } } } } // publish the table - uint32_t evSel = static_cast(0); - for (auto& event : events) { + auto evSel = static_cast(0); + for (auto const& event : events) { evSel = 0; if (fSelMap[event.globalIndex()]) { // event passed the user cuts evSel |= (static_cast(1) << 0); @@ -496,13 +609,26 @@ struct AnalysisEventSelection { runEventSelection(events, mcEvents); publishSelections(events); } - - void processDummy(MyEvents&) + void processFillEvents(MyEvents const& events) // Used to forward the event table from tablemaker, typical use for now is jet analysis. + { + for (auto const& event : events) { + JetEvents(event.tag_raw(), + event.runNumber(), + event.posX(), + event.posY(), + event.posZ(), + event.numContrib(), + event.collisionTime(), + event.collisionTimeRes()); + } + } + void processDummy(MyEvents const&) { // do nothing } PROCESS_SWITCH(AnalysisEventSelection, processSkimmed, "Run event selection on DQ skimmed events", false); + PROCESS_SWITCH(AnalysisEventSelection, processFillEvents, "Fill storedReducedEvents table for use in JE framework", false); PROCESS_SWITCH(AnalysisEventSelection, processDummy, "Dummy function", true); }; @@ -519,6 +645,17 @@ struct AnalysisTrackSelection { Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; + // Re-application of the track-to-collision time compatibility on the skimmed associations. + // timeMargin and nSigma must be <= the values used by track-to-collision-associator at skimming time; + // bcWindow, usePVAssociation and maxPvContributors must be identical to those values. + struct : ConfigurableGroup { + Configurable cfgAssocTimeCut{"cfgAssocTimeCut", false, "If true, reject associations failing the time compatibility computed with the parameters below"}; + Configurable cfgAssocTimeMargin{"cfgAssocTimeMargin", 0.f, "time margin (ns); must be <= skimming timeMargin"}; + Configurable cfgAssocNSigma{"cfgAssocNSigma", 4.f, "nSigmaForTimeCompat; must be <= skimming value"}; + Configurable cfgAssocBcWindowForOneSigma{"cfgAssocBcWindowForOneSigma", 60, "bcWindowForOneSigma; must be equal to the skimming value"}; + Configurable cfgAssocUsePVAssociation{"cfgAssocUsePVAssociation", 1, "usePVAssociation; must be equal to the skimming value"}; + Configurable cfgAssocMaxPvContributorsForLowMultReassoc{"cfgAssocMaxPvContributorsForLowMultReassoc", 10, "maxPvContributorsForLowMultReassoc; must be equal to the skimming value"}; + } fConfigAssocTime; Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; @@ -528,15 +665,15 @@ struct AnalysisTrackSelection { Configurable fConfigMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigMCSignalsJSON{"cfgTrackMCsignalsJSON", "", "Additional list of MC signals via JSON"}; - Service fCCDB; + Service fCCDB{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fMCSignals; // list of signals to be checked std::vector fHistNamesReco; std::vector fHistNamesMCMatched; - int fCurrentRun; // current run (needed to detect run changes for loading CCDB parameters) + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) std::map> fNAssocsInBunch; // key: track global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: track global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) @@ -560,8 +697,8 @@ struct AnalysisTrackSelection { TString addTrackCutsStr = fConfigCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + for (auto const& t : addTrackCuts) { + fTrackCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill @@ -582,7 +719,7 @@ struct AnalysisTrackSelection { TString addMCSignalsStr = fConfigMCSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 1) { // NOTE: only 1 prong signals continue; } @@ -593,16 +730,16 @@ struct AnalysisTrackSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); // Configure histogram classes for each track cut; // Add histogram classes for each track cut and for each requested MC signal (reconstructed tracks with MC truth) TString histClasses = "AssocsBarrel_BeforeCuts;"; - for (auto& cut : fTrackCuts) { + for (auto const& cut : fTrackCuts) { TString nameStr = Form("AssocsBarrel_%s", cut->GetName()); fHistNamesReco.push_back(nameStr); histClasses += Form("%s;", nameStr.Data()); - for (auto& sig : fMCSignals) { + for (auto const& sig : fMCSignals) { TString nameStr2 = Form("AssocsCorrectBarrel_%s_%s", cut->GetName(), sig->GetName()); fHistNamesMCMatched.push_back(nameStr2); histClasses += Form("%s;", nameStr2.Data()); @@ -647,7 +784,7 @@ struct AnalysisTrackSelection { VarManager::SetCalibrationObject(VarManager::kTPCProtonSigma, calibList->FindObject("sigma_map_proton")); } - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); + auto grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); if (grpmag != nullptr) { VarManager::SetMagneticField(grpmag->getNominalL3Field()); } else { @@ -661,7 +798,7 @@ struct AnalysisTrackSelection { trackAmbiguities.reserve(tracks.size()); // Loop over associations - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { auto event = assoc.template reducedevent_as(); if (!event.isEventSelected_bit(0)) { trackSel(0); @@ -676,6 +813,20 @@ struct AnalysisTrackSelection { } auto track = assoc.template reducedtrack_as(); + + // Reject incompatible associations while preserving the row alignment of trackSel. + // The track time is referenced to its original collision, not the associated one. + if (fConfigAssocTime.cfgAssocTimeCut) { + auto origEvent = track.template reducedevent_as(); + if (!VarManager::isBarrelAssocTimeCompatible(track, event, origEvent, + fConfigAssocTime.cfgAssocNSigma, fConfigAssocTime.cfgAssocTimeMargin, + fConfigAssocTime.cfgAssocBcWindowForOneSigma, fConfigAssocTime.cfgAssocUsePVAssociation, + fConfigAssocTime.cfgAssocMaxPvContributorsForLowMultReassoc)) { + trackSel(0); + continue; + } + } + VarManager::FillTrack(track); // compute quantities which depend on the associated collision, such as DCA VarManager::FillTrackCollision(track, event); @@ -691,16 +842,16 @@ struct AnalysisTrackSelection { } if (fConfigQA) { - fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", dqefficiency_helpers::varValues()); } int iCut = 0; - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqefficiency_helpers::varValues())) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(fHistNamesReco[iCut], VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesReco[iCut], dqefficiency_helpers::varValues()); } } } // end loop over cuts @@ -718,9 +869,9 @@ struct AnalysisTrackSelection { for (unsigned int icut = 0; icut < fTrackCuts.size(); icut++) { if (filterMap & (static_cast(1) << icut)) { if (isCorrectAssoc) { - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), dqefficiency_helpers::varValues()); } } } // end loop over cuts @@ -735,9 +886,9 @@ struct AnalysisTrackSelection { for (unsigned int j = 0; j < fTrackCuts.size(); j++) { if (filterMap & (static_cast(1) << j)) { if (isCorrectAssoc) { - fHistMan->FillHistClass(fHistNamesMCMatched[j * fMCSignals.size() + 2 * i].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[j * fMCSignals.size() + 2 * i].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(fHistNamesMCMatched[j * fMCSignals.size() + 2 * i + 1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[j * fMCSignals.size() + 2 * i + 1].Data(), dqefficiency_helpers::varValues()); } } } // end loop over cuts @@ -773,7 +924,7 @@ struct AnalysisTrackSelection { // So one could QA these tracks separately if (fConfigPublishAmbiguity) { if (fConfigQA) { - for (auto& [trackIdx, evIndices] : fNAssocsInBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsInBunch) { if (evIndices.size() == 1) { continue; } @@ -781,10 +932,10 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", dqefficiency_helpers::varValues()); } // end loop over in-bunch ambiguous tracks - for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsOutOfBunch) { if (evIndices.size() == 1) { continue; } @@ -792,12 +943,12 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", dqefficiency_helpers::varValues()); } // end loop over out-of-bunch ambiguous tracks } // publish the ambiguity table - for (auto& track : tracks) { + for (auto const& track : tracks) { int8_t nInBunch = 0; if (fNAssocsInBunch.find(track.globalIndex()) != fNAssocsInBunch.end()) { nInBunch = fNAssocsInBunch[track.globalIndex()].size(); @@ -819,7 +970,7 @@ struct AnalysisTrackSelection { { runTrackSelection(assocs, events, tracks, eventsMC, tracksMC); } - void processDummy(MyEvents&) + void processDummy(MyEvents const&) { // do nothing } @@ -842,24 +993,22 @@ struct AnalysisMuonSelection { Configurable fConfigAddMuonHistogram{"cfgAddMuonHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; - Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable fConfigMCSignals{"cfgMuonMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigMCSignalsJSON{"cfgMuonMCsignalsJSON", "", "Additional list of MC signals via JSON"}; - Service fCCDB; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + + Service fCCDB{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fMuonCuts; std::vector fHistNamesReco; std::vector fHistNamesMCMatched; std::vector fMCSignals; // list of signals to be checked - int fCurrentRun; // current run kept to detect run changes and trigger loading params from CCDB + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) std::map> fNAssocsInBunch; // key: track global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: track global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) @@ -883,8 +1032,8 @@ struct AnalysisMuonSelection { TString addCutsStr = fConfigCutsJSON.value; if (addCutsStr != "") { std::vector addCuts = dqcuts::GetCutsFromJSON(addCutsStr.Data()); - for (auto& t : addCuts) { - fMuonCuts.push_back(reinterpret_cast(t)); + for (auto const& t : addCuts) { + fMuonCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill @@ -905,7 +1054,7 @@ struct AnalysisMuonSelection { TString addMCSignalsStr = fConfigMCSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 1) { // NOTE: only 1 prong signals continue; } @@ -916,16 +1065,16 @@ struct AnalysisMuonSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); // Configure histogram classes for each track cut; // Add histogram classes for each track cut and for each requested MC signal (reconstructed tracks with MC truth) TString histClasses = "AssocsMuon_BeforeCuts;"; - for (auto& cut : fMuonCuts) { + for (auto const& cut : fMuonCuts) { TString nameStr = Form("AssocsMuon_%s", cut->GetName()); fHistNamesReco.push_back(nameStr); histClasses += Form("%s;", nameStr.Data()); - for (auto& sig : fMCSignals) { + for (auto const& sig : fMCSignals) { TString nameStr2 = Form("AssocsCorrectMuon_%s_%s", cut->GetName(), sig->GetName()); fHistNamesMCMatched.push_back(nameStr2); histClasses += Form("%s;", nameStr2.Data()); @@ -948,32 +1097,21 @@ struct AnalysisMuonSelection { fCCDB->setCaching(true); fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigGeoPath); - } } template void runMuonSelection(ReducedMuonsAssoc const& assocs, TEvents const& events, TMuons const& muons, ReducedMCEvents const& /*eventsMC*/, ReducedMCTracks const& muonsMC) { + fNAssocsInBunch.clear(); + fNAssocsOutOfBunch.clear(); + if (events.size() > 0 && fCurrentRun != events.begin().runNumber()) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); - if (grpmag != nullptr) { - o2::base::Propagator::initFieldFromGRP(grpmag); - VarManager::SetMagneticField(grpmag->getNominalL3Field()); - } else { - // LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", events.begin().timestamp()); - // If the magnetic field is not found it is configured by had by the user - VarManager::SetMagneticField(fConfigMagField.value); - } fCurrentRun = events.begin().runNumber(); } - fNAssocsInBunch.clear(); - fNAssocsOutOfBunch.clear(); muonSel.reserve(assocs.size()); - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { auto event = assoc.template reducedevent_as(); if (!event.has_reducedMCevent() || !event.isEventSelected_bit(0)) { // condition on reducedMCevent to avoid rec. events with no generated event muonSel(0); @@ -996,16 +1134,16 @@ struct AnalysisMuonSelection { } if (fConfigQA) { - fHistMan->FillHistClass("AssocsMuon_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("AssocsMuon_BeforeCuts", dqefficiency_helpers::varValues()); } int iCut = 0; - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqefficiency_helpers::varValues())) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(fHistNamesReco[iCut].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesReco[iCut].Data(), dqefficiency_helpers::varValues()); } } } // end loop over cuts @@ -1023,7 +1161,7 @@ struct AnalysisMuonSelection { } // compute MC matching decisions - uint32_t mcDecision = static_cast(0); + auto mcDecision = static_cast(0); int isig = 0; for (auto sig = fMCSignals.begin(); sig != fMCSignals.end(); sig++, isig++) { if constexpr ((TMuonFillMap & VarManager::ObjTypes::ReducedMuon) > 0) { @@ -1043,9 +1181,9 @@ struct AnalysisMuonSelection { for (unsigned int j = 0; j < fMuonCuts.size(); j++) { if (filterMap & (static_cast(1) << j)) { if (isCorrectAssoc) { - fHistMan->FillHistClass(fHistNamesMCMatched[j * 2 * fMCSignals.size() + 2 * i].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[j * 2 * fMCSignals.size() + 2 * i].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(fHistNamesMCMatched[j * 2 * fMCSignals.size() + 2 * i + 1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[j * 2 * fMCSignals.size() + 2 * i + 1].Data(), dqefficiency_helpers::varValues()); } } } // end loop over cuts @@ -1077,7 +1215,7 @@ struct AnalysisMuonSelection { // TODO: some tracks can be associated to both collisions that have in bunch pileup and collisions from different bunches // So one could QA these tracks separately if (fConfigPublishAmbiguity) { - for (auto& [trackIdx, evIndices] : fNAssocsInBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsInBunch) { if (evIndices.size() == 1) { continue; } @@ -1085,10 +1223,12 @@ struct AnalysisMuonSelection { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("Muon_AmbiguityInBunch", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("Muon_AmbiguityInBunch", dqefficiency_helpers::varValues()); + } } // end loop over in-bunch ambiguous tracks - for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsOutOfBunch) { if (evIndices.size() == 1) { continue; } @@ -1096,11 +1236,13 @@ struct AnalysisMuonSelection { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("Muon_AmbiguityOutOfBunch", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("Muon_AmbiguityOutOfBunch", dqefficiency_helpers::varValues()); + } } // end loop over out-of-bunch ambiguous tracks // publish the ambiguity table - for (auto& track : muons) { + for (auto const& track : muons) { int8_t nInBunch = 0; if (fNAssocsInBunch.find(track.globalIndex()) != fNAssocsInBunch.end()) { nInBunch = fNAssocsInBunch[track.globalIndex()].size(); @@ -1123,7 +1265,7 @@ struct AnalysisMuonSelection { runMuonSelection(assocs, events, muons, eventsMC, tracksMC); } - void processDummy(MyEvents&) + void processDummy(MyEvents const&) { // do nothing } @@ -1149,9 +1291,9 @@ struct AnalysisPrefilterSelection { Configurable fPropTrack{"cfgPropTrack", false, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; std::map fPrefilterMap; - AnalysisCompositeCut* fPairCut; - uint32_t fPrefilterMask; - int fPrefilterCutBit; + AnalysisCompositeCut* fPairCut = nullptr; + uint32_t fPrefilterMask = 0; + int fPrefilterCutBit = -1; Preslice trackAssocsPerCollision = aod::reducedtrack_association::reducedeventId; @@ -1160,6 +1302,7 @@ struct AnalysisPrefilterSelection { if (context.mOptions.get("processDummy")) { return; } + VarManager::SetDefaultVarNames(); bool runPrefilter = true; // get the list of track cuts to be prefiltered @@ -1186,15 +1329,15 @@ struct AnalysisPrefilterSelection { if (runPrefilter) { // get the list of cuts that were computed in the barrel track-selection task and create a bit mask // to mark just the ones we want to apply a prefilter on - string trackCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", trackCuts, false); + std::string trackCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", trackCuts, false); TString allTrackCutsStr = trackCuts; - // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); + // check also the cuts added via JSON and add them to the std::string of cuts + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); TString addTrackCutsStr = trackCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + for (auto const& t : addTrackCuts) { allTrackCutsStr += Form(",%s", t->GetName()); } } @@ -1227,7 +1370,6 @@ struct AnalysisPrefilterSelection { } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill - VarManager::SetDefaultVarNames(); VarManager::SetupTwoProngDCAFitter(5.0f, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, true); // TODO: get these parameters from Configurables VarManager::SetupTwoProngFwdDCAFitter(5.0f, true, 200.0f, 1.0e-3f, 0.9f, true); @@ -1240,7 +1382,7 @@ struct AnalysisPrefilterSelection { return; } - for (auto& [assoc1, assoc2] : o2::soa::combinations(assocs, assocs)) { + for (auto const& [assoc1, assoc2] : o2::soa::combinations(assocs, assocs)) { auto track1 = assoc1.template reducedtrack_as(); auto track2 = assoc2.template reducedtrack_as(); @@ -1256,7 +1398,7 @@ struct AnalysisPrefilterSelection { bool track1Loose = assoc1.isBarrelSelected_bit(fPrefilterCutBit); bool track2Loose = assoc2.isBarrelSelected_bit(fPrefilterCutBit); - if (!((track1Candidate > 0 && track2Loose) || (track2Candidate > 0 && track1Loose))) { + if ((track1Candidate == 0 || !track2Loose) && (track2Candidate == 0 || !track1Loose)) { continue; } @@ -1266,7 +1408,7 @@ struct AnalysisPrefilterSelection { VarManager::FillPairCollision(event, track1, track2); } // if the pair fullfils the criteria, add an entry into the prefilter map for the two tracks - if (fPairCut->IsSelected(VarManager::fgValues)) { + if (fPairCut->IsSelected(dqefficiency_helpers::varValues())) { if (fPrefilterMap.find(track1.globalIndex()) == fPrefilterMap.end() && track1Candidate > 0) { fPrefilterMap[track1.globalIndex()] = track1Candidate; } @@ -1282,7 +1424,7 @@ struct AnalysisPrefilterSelection { fPrefilterMap.clear(); - for (auto& event : events) { + for (auto const& event : events) { auto groupedAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); if (groupedAssocs.size() > 1) { runPrefilter(event, groupedAssocs, tracks); @@ -1296,11 +1438,11 @@ struct AnalysisPrefilterSelection { prefilter(mymap); } } else { - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { // TODO: just use the index from the assoc (no need to cast the whole track) auto track = assoc.template reducedtrack_as(); mymap = -1; - if (fPrefilterMap.find(track.globalIndex()) != fPrefilterMap.end()) { + if (fPrefilterMap.contains(track.globalIndex())) { // NOTE: publish the bitwise negated bits (~), so there will be zeroes for cuts that failed the prefiltering and 1 everywhere else mymap = ~fPrefilterMap[track.globalIndex()]; prefilter(mymap); @@ -1311,7 +1453,7 @@ struct AnalysisPrefilterSelection { } } - void processDummy(MyEvents&) + void processDummy(MyEvents const&) { // do nothing } @@ -1320,6 +1462,223 @@ struct AnalysisPrefilterSelection { PROCESS_SWITCH(AnalysisPrefilterSelection, processDummy, "Do nothing", true); }; +// Single-track selection on the barrel tracks after the prefilter decisions (which are not visible in the +// AnalysisTrackSelection histograms): fills track-level histograms separately for surviving and vetoed tracks, +// per track cut and per MC signal. +// NOTE: Histograms are filled per association, regardless of whether the association is the correct one. +struct AnalysisPostPrefilterTrackSelection { + OutputObj fOutputList{"output"}; + + Configurable fConfigTrackCuts{"cfgTrackCuts", "", "Comma separated list of track cuts (among those of analysis-track-selection) for which to fill histograms"}; + Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; + Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + Configurable fConfigMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; + Configurable fConfigMCSignalsJSON{"cfgTrackMCsignalsJSON", "", "Additional list of MC signals via JSON"}; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + + Service fCCDB{}; + + HistogramManager* fHistMan = nullptr; + std::vector fMCSignals; + std::vector fCutBits; // bit positions of the requested cuts in the track-selection task cut list + std::vector fHistNamesSurvived; // [icut] + std::vector fHistNamesVetoed; // [icut] + std::vector fHistNamesMCSurvived; // [icut * nSignals + isig] + std::vector fHistNamesMCVetoed; // [icut * nSignals + isig] + + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) + + void init(o2::framework::InitContext& context) + { + if (context.mOptions.get("processDummy")) { + return; + } + VarManager::SetDefaultVarNames(); + + fCurrentRun = 0; + TString trackCutsStr = fConfigTrackCuts.value; + std::unique_ptr objArrayCuts(trackCutsStr.Tokenize(",")); + if (objArrayCuts == nullptr || objArrayCuts->GetEntries() == 0) { + LOG(fatal) << " No track cuts specified! Check the cfgTrackCuts configurable"; + } + // get the full list of cuts computed by the track-selection task and resolve the requested cuts to bit positions + std::string trackCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", trackCuts, false); + TString allTrackCutsStr = trackCuts; + // check also the cuts added via JSON and add them to the string of cuts + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); + TString addTrackCutsStr = trackCuts; + if (addTrackCutsStr != "") { + std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { + allTrackCutsStr += Form(",%s", t->GetName()); + } + } + std::unique_ptr objArrayAllCuts(allTrackCutsStr.Tokenize(",")); + if (objArrayAllCuts == nullptr) { + LOG(fatal) << " Not getting any track cuts from the barrel-track-selection "; + } + std::vector cutNames; + for (int icut = 0; icut < objArrayCuts->GetEntries(); ++icut) { + TString cutName = objArrayCuts->At(icut)->GetName(); + if (objArrayAllCuts->FindObject(cutName.Data()) == nullptr) { + LOG(fatal) << " Track cut " << cutName << " not among the cuts calculated by the track-selection task! "; + } + for (int jcut = 0; jcut < objArrayAllCuts->GetEntries(); ++jcut) { + if (cutName.CompareTo(objArrayAllCuts->At(jcut)->GetName()) == 0) { + fCutBits.push_back(jcut); + } + } + cutNames.push_back(cutName); + } + + // Setting the MC signals + TString configSigNamesStr = fConfigMCSignals.value; + std::unique_ptr sigNamesArray(configSigNamesStr.Tokenize(",")); + for (int isig = 0; isig < sigNamesArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(sigNamesArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() != 1) { // NOTE: only 1 prong signals + continue; + } + fMCSignals.push_back(sig); + } + } + // Add the MCSignals from the JSON config + TString addMCSignalsStr = fConfigMCSignalsJSON.value; + if (addMCSignalsStr != "") { + std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); + for (auto const& mcIt : addMCSignals) { + if (mcIt->GetNProngs() != 1) { // NOTE: only 1 prong signals + continue; + } + fMCSignals.push_back(mcIt); + } + } + + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(kTRUE); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); + + // Configure histogram classes for each track cut, separately for prefilter-surviving and prefilter-vetoed tracks, + // and for each requested MC signal (reconstructed tracks with MC truth) + TString histClasses = ""; + for (auto const& cutName : cutNames) { + TString nameStr = Form("TrackBarrelPostPrefilter_%s", cutName.Data()); + fHistNamesSurvived.push_back(nameStr); + histClasses += Form("%s;", nameStr.Data()); + nameStr = Form("TrackBarrelPrefilterVetoed_%s", cutName.Data()); + fHistNamesVetoed.push_back(nameStr); + histClasses += Form("%s;", nameStr.Data()); + for (auto const& sig : fMCSignals) { + TString nameStr2 = Form("TrackBarrelPostPrefilter_%s_%s", cutName.Data(), sig->GetName()); + fHistNamesMCSurvived.push_back(nameStr2); + histClasses += Form("%s;", nameStr2.Data()); + nameStr2 = Form("TrackBarrelPrefilterVetoed_%s_%s", cutName.Data(), sig->GetName()); + fHistNamesMCVetoed.push_back(nameStr2); + histClasses += Form("%s;", nameStr2.Data()); + } + } + + DefineHistograms(fHistMan, histClasses.Data(), fConfigAddTrackHistogram.value.data()); + dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + fOutputList.setObject(fHistMan->GetMainHistogramList()); + + fCCDB->setURL(fConfigCcdbUrl.value); + fCCDB->setCaching(true); + fCCDB->setLocalObjectValidityChecking(); + } + + template + void runPostPrefilterTrackSelection(soa::Join const& assocs, + TEvents const& events, TTracks const& /*tracks*/, TTracksMC const& tracksMC) + { + // load the magnetic field (needed to recompute the DCA with respect to the associated collision) + if (events.size() > 0 && fCurrentRun != events.begin().runNumber()) { + auto grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); + if (grpmag != nullptr) { + VarManager::SetMagneticField(grpmag->getNominalL3Field()); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", events.begin().timestamp()); + } + fCurrentRun = events.begin().runNumber(); + } + + // Loop over associations + for (auto const& assoc : assocs) { + auto event = assoc.template reducedevent_as(); + if (!event.isEventSelected_bit(0)) { + continue; + } + + // check which of the requested cuts are fulfilled by this association and which of those are prefilter-vetoed + uint32_t selectedMap = 0; + uint32_t vetoMap = 0; + for (std::size_t icut = 0; icut < fCutBits.size(); ++icut) { + if (assoc.isBarrelSelected_bit(fCutBits[icut])) { + selectedMap |= (static_cast(1) << icut); + if (!assoc.isBarrelSelectedPrefilter_bit(fCutBits[icut])) { + vetoMap |= (static_cast(1) << icut); + } + } + } + if (selectedMap == 0) { + continue; + } + + VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); + // fill event information which might be needed in histograms that combine track and event properties + VarManager::FillEvent(event); + if (event.has_reducedMCevent()) { + VarManager::FillEvent(event.reducedMCevent()); + } + + auto track = assoc.template reducedtrack_as(); + VarManager::FillTrack(track); + // compute quantities which depend on the associated collision, such as DCA + VarManager::FillTrackCollision(track, event); + if (track.has_reducedMCTrack()) { + VarManager::FillTrackMC(tracksMC, track.reducedMCTrack()); + } + + for (std::size_t icut = 0; icut < fCutBits.size(); ++icut) { + if (!(selectedMap & (static_cast(1) << icut))) { + continue; + } + bool isVetoed = (vetoMap & (static_cast(1) << icut)) > 0; + fHistMan->FillHistClass((isVetoed ? fHistNamesVetoed[icut] : fHistNamesSurvived[icut]).Data(), dqefficiency_helpers::varValues()); + if (track.has_reducedMCTrack()) { + int isig = 0; + for (auto sig = fMCSignals.begin(); sig != fMCSignals.end(); sig++, isig++) { + if ((*sig)->CheckSignal(true, track.reducedMCTrack())) { + fHistMan->FillHistClass((isVetoed ? fHistNamesMCVetoed : fHistNamesMCSurvived)[icut * fMCSignals.size() + isig].Data(), dqefficiency_helpers::varValues()); + } + } + } + } // end loop over cuts + } // end loop over associations + } + + void processSkimmed(soa::Join const& assocs, MyEventsSelected const& events, MyBarrelTracks const& tracks, ReducedMCEvents const& /*eventsMC*/, ReducedMCTracks const& tracksMC) + { + runPostPrefilterTrackSelection(assocs, events, tracks, tracksMC); + } + void processSkimmedWithCov(soa::Join const& assocs, MyEventsVtxCovSelected const& events, MyBarrelTracksWithCov const& tracks, ReducedMCEvents const& /*eventsMC*/, ReducedMCTracks const& tracksMC) + { + runPostPrefilterTrackSelection(assocs, events, tracks, tracksMC); + } + void processDummy(MyEvents const&) + { + // do nothing + } + + PROCESS_SWITCH(AnalysisPostPrefilterTrackSelection, processSkimmed, "Run barrel track post-prefilter selection on DQ skimmed track associations", false); + PROCESS_SWITCH(AnalysisPostPrefilterTrackSelection, processSkimmedWithCov, "Run barrel track post-prefilter selection on DQ skimmed track associations w/ cov matrix", false); + PROCESS_SWITCH(AnalysisPostPrefilterTrackSelection, processDummy, "Dummy function", true); +}; + // Run the same-event pairing // This task assumes that both legs of the resonance fulfill the same cuts (symmetric decay channel) // Runs combinatorics for barrel-barrel, muon-muon and barrel-muon combinations @@ -1342,7 +1701,7 @@ struct AnalysisSameEventPairing { Produces dileptonPolarList; o2::base::MatLayerCylSet* fLUT = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; @@ -1362,18 +1721,13 @@ struct AnalysisSameEventPairing { Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; } fConfigCCDB; struct : ConfigurableGroup { - Configurable useRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable magField{"cfgMagField", 5.0f, "Manually set magnetic field"}; Configurable flatTables{"cfgFlatTables", false, "Produce a single flat tables with all relevant information of the pairs and single tracks"}; Configurable polarTables{"cfgPolarTables", false, "Produce tables with dilepton polarization information"}; Configurable useKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; Configurable useAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - Configurable propToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; - Configurable corrFullGeo{"cfgCorrFullGeo", false, "Use full geometry to correct for MCS effects in track propagation"}; Configurable noCorr{"cfgNoCorrFwdProp", false, "Do not correct for MCS effects in track propagation"}; Configurable collisionSystem{"syst", "pp", "Collision system, pp or PbPb"}; Configurable centerMassEnergy{"energy", 13600, "Center of mass energy in GeV"}; @@ -1384,6 +1738,8 @@ struct AnalysisSameEventPairing { Configurable genSignalsJSON{"cfgMCGenSignalsJSON", "", "Additional list of MC signals (generated) via JSON"}; Configurable recSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; Configurable recSignalsJSON{"cfgMCRecSignalsJSON", "", "Comma separated list of MC signals (reconstructed) via JSON"}; + Configurable emuRecSignals{"cfgEmuMCRecSignals", "", "Comma separated list of MC signals (reconstructed) for e-mu pairs"}; + Configurable emuRecSignalsJSON{"cfgEmuMCRecSignalsJSON", "", "Additional list of MC signals (reconstructed) for e-mu pairs via JSON"}; Configurable finalStateSignals{"cfgBarrelMCFinalStateSignals", "eFromJpsi", "Comma separated list of MC signals (final state particles)"}; Configurable skimSignalOnly{"cfgSkimSignalOnly", false, "Configurable to select only matched candidates"}; } fConfigMC; @@ -1397,22 +1753,25 @@ struct AnalysisSameEventPairing { // Track related options Configurable fPropTrack{"cfgPropTrack", true, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; - Service fCCDB; + Service fCCDB{}; // PDG database - Service pdgDB; + Service pdgDB{}; // Filter filterEventSelected = aod::dqanalysisflags::isEventSelected & uint32_t(1); Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; // keep histogram class names in maps, so we don't have to buld their names in the pair loops std::map> fTrackHistNames; std::map> fBarrelHistNamesMCmatched; std::map> fMuonHistNames; std::map> fMuonHistNamesMCmatched; + std::map> fTrackMuonHistNames; + std::map> fTrackMuonHistNamesMCmatched; std::vector fRecMCSignals; + std::vector fEmuRecMCSignals; std::vector fGenMCSignals; std::vector fFinalStateMCSignals; @@ -1420,19 +1779,24 @@ struct AnalysisSameEventPairing { std::vector fMCGenAccCuts; bool fUseMCGenAccCut = false; - uint32_t fTrackFilterMask; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream - uint32_t fMuonFilterMask; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream - int fNCutsBarrel; - int fNCutsMuon; - int fNPairCuts; + uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream + uint32_t fMuonFilterMask = 0; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream + int fNCutsBarrel = 0; + int fNCutsMuon = 0; + int fNPairCuts = 0; bool fHasTwoProngGenMCsignals = false; - bool fEnableBarrelHistos; - bool fEnableMuonHistos; + bool fEnableBarrelHistos = false; + bool fEnableMuonHistos = false; + bool fEnableBarrelMuonHistos = false; + bool fEnableBarrelMuonMixingHistos = false; Preslice> trackAssocsPerCollision = aod::reducedtrack_association::reducedeventId; Preslice> muonAssocsPerCollision = aod::reducedtrack_association::reducedeventId; + Configurable fConfigMixingDepth{"cfgMixingDepth", 100, "Number of Events stored for event mixing"}; + NoBinningPolicy hashBin; + void init(o2::framework::InitContext& context) { if (context.mOptions.get("processDummy")) { @@ -1442,7 +1806,9 @@ struct AnalysisSameEventPairing { VarManager::SetDefaultVarNames(); fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed"); - fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed"); + fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlyVertexingSkimmed"); + fEnableBarrelMuonHistos = context.mOptions.get("processElectronMuonSkimmed"); + fEnableBarrelMuonMixingHistos = context.mOptions.get("processMixingElectronMuonSkimmed"); // Keep track of all the histogram class names to avoid composing strings in the pairing loop TString histNames = ""; @@ -1484,7 +1850,7 @@ struct AnalysisSameEventPairing { TString addMCSignalsStr = fConfigMC.recSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 2) { // NOTE: only 2 prong signals continue; } @@ -1492,16 +1858,39 @@ struct AnalysisSameEventPairing { } } + // Setting the MC rec signal names for e-mu pairs (independent list; the pair has leg1=electron, leg2=muon) + TString emuSigNamesStr = fConfigMC.emuRecSignals.value; + std::unique_ptr objEmuRecSigArray(emuSigNamesStr.Tokenize(",")); + for (int isig = 0; isig < objEmuRecSigArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objEmuRecSigArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() != 2) { // NOTE: 2-prong signals required + continue; + } + fEmuRecMCSignals.push_back(sig); + } + } + TString addEmuMCSignalsStr = fConfigMC.emuRecSignalsJSON.value; + if (addEmuMCSignalsStr != "") { + std::vector addEmuMCSignals = dqmcsignals::GetMCSignalsFromJSON(addEmuMCSignalsStr.Data()); + for (auto const& mcIt : addEmuMCSignals) { + if (mcIt->GetNProngs() != 2) { + continue; + } + fEmuRecMCSignals.push_back(mcIt); + } + } + // get the barrel track selection cuts - string tempCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + std::string tempCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); TString tempCutsStr = tempCuts; - // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + // check also the cuts added via JSON and add them to the std::string of cuts + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); TString addTrackCutsStr = tempCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + for (auto const& t : addTrackCuts) { tempCutsStr += Form(",%s", t->GetName()); } } @@ -1533,7 +1922,7 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsBarrelSEPP_ambiguousOutOfBunch_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelSEMM_ambiguousOutOfBunch_%s", objArray->At(icut)->GetName())); } - for (auto& n : names) { + for (auto const& n : names) { histNames += Form("%s;", n.Data()); } fTrackHistNames[icut] = names; @@ -1541,9 +1930,9 @@ struct AnalysisSameEventPairing { // if there are pair cuts specified, assign hist directories for each barrel cut - pair cut combination // NOTE: This could possibly lead to large histogram outputs. It is strongly advised to use pair cuts only // if you know what you are doing. - TString cutNamesStr = fConfigCuts.pair.value; - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + TString pairCutNamesStr = fConfigCuts.pair.value; + if (!pairCutNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts names = { @@ -1574,7 +1963,7 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsBarrelSEPM_ambiguousOutOfBunchCorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); names.push_back(Form("PairsBarrelSEPM_ambiguousOutOfBunchIncorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); } - for (auto& n : names) { + for (auto const& n : names) { histNames += Form("%s;", n.Data()); } fBarrelHistNamesMCmatched.try_emplace(icut * fRecMCSignals.size() + isig, names); @@ -1586,14 +1975,14 @@ struct AnalysisSameEventPairing { } // get the muon track selection cuts - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", tempCuts, false); + getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", tempCuts, false); tempCutsStr = tempCuts; - // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", tempCuts, false); + // check also the cuts added via JSON and add them to the std::string of cuts + getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", tempCuts, false); TString addMuonCutsStr = tempCuts; if (addMuonCutsStr != "") { std::vector addMuonCuts = dqcuts::GetCutsFromJSON(addMuonCutsStr.Data()); - for (auto& t : addMuonCuts) { + for (auto const& t : addMuonCuts) { tempCutsStr += Form(",%s", t->GetName()); } } @@ -1624,15 +2013,15 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsMuonSEPP_ambiguousOutOfBunch_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsMuonSEMM_ambiguousOutOfBunch_%s", objArray->At(icut)->GetName())); } - for (auto& n : names) { + for (auto const& n : names) { histNames += Form("%s;", n.Data()); } fMuonHistNames[icut] = names; // if there are specified pair cuts, assign hist dirs for each muon cut - pair cut combination - TString cutNamesStr = fConfigCuts.pair.value; - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + TString pairCutNamesStr = fConfigCuts.pair.value; + if (!pairCutNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts names = { @@ -1663,7 +2052,7 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsMuonSEPM_ambiguousOutOfBunchCorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); names.push_back(Form("PairsMuonSEPM_ambiguousOutOfBunchIncorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); } - for (auto& n : names) { + for (auto const& n : names) { histNames += Form("%s;", n.Data()); } fMuonHistNamesMCmatched.try_emplace(icut * fRecMCSignals.size() + isig, names); @@ -1674,6 +2063,115 @@ struct AnalysisSameEventPairing { } // end loop over cuts } // end if (muonCutsStr) + // build the hist directories for e-mu pairs (track cut x muon cut [x pair cut]) and for the MC matched variants + if (fEnableBarrelMuonHistos || fEnableBarrelMuonMixingHistos) { + // re-fetch the barrel cut list from upstream (we lost it when the muon block reused tempCuts/tempCutsStr) + std::string tempBarrelCutsStr; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempBarrelCutsStr, false); + TString barrelCutsAll = tempBarrelCutsStr; + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempBarrelCutsStr, false); + TString barrelCutsJSONStr = tempBarrelCutsStr; + if (barrelCutsJSONStr != "") { + std::vector addCuts = dqcuts::GetCutsFromJSON(barrelCutsJSONStr.Data()); + for (auto const& t : addCuts) { + barrelCutsAll += Form(",%s", t->GetName()); + } + } + std::unique_ptr objArrayBarrel(barrelCutsAll.Tokenize(",")); + + std::string tempMuonCutsStr; + getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", tempMuonCutsStr, false); + TString muonCutsAll = tempMuonCutsStr; + getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", tempMuonCutsStr, false); + TString muonCutsJSONStr = tempMuonCutsStr; + if (muonCutsJSONStr != "") { + std::vector addCuts = dqcuts::GetCutsFromJSON(muonCutsJSONStr.Data()); + for (auto const& t : addCuts) { + muonCutsAll += Form(",%s", t->GetName()); + } + } + std::unique_ptr objArrayMuon(muonCutsAll.Tokenize(",")); + + TString pairCutNamesStr = fConfigCuts.pair.value; + std::unique_ptr objArrayPair(pairCutNamesStr.IsNull() ? nullptr : pairCutNamesStr.Tokenize(",")); + int nPairCuts = (objArrayPair && objArrayPair->GetEntries() > 0) ? objArrayPair->GetEntries() : 1; + + for (int iTrack = 0; iTrack < objArrayBarrel->GetEntries(); ++iTrack) { + TString trackCutName = objArrayBarrel->At(iTrack)->GetName(); + if (objArrayTrackCuts == nullptr || objArrayTrackCuts->FindObject(trackCutName.Data()) == nullptr) { + continue; + } + + for (int iMuon = 0; iMuon < objArrayMuon->GetEntries(); ++iMuon) { + TString muonCutName = objArrayMuon->At(iMuon)->GetName(); + if (objArrayMuonCuts == nullptr || objArrayMuonCuts->FindObject(muonCutName.Data()) == nullptr) { + continue; + } + + // base (no pair-cut) entry + std::vector names = { + Form("PairsEleMuSEPM_%s_%s", trackCutName.Data(), muonCutName.Data()), + Form("PairsEleMuSEPP_%s_%s", trackCutName.Data(), muonCutName.Data()), + Form("PairsEleMuSEMM_%s_%s", trackCutName.Data(), muonCutName.Data())}; + histNames += Form("%s;%s;%s;", names[0].Data(), names[1].Data(), names[2].Data()); + if (fEnableBarrelMuonMixingHistos) { + names.push_back(Form("PairsEleMuMEPM_%s_%s", trackCutName.Data(), muonCutName.Data())); + names.push_back(Form("PairsEleMuMEPP_%s_%s", trackCutName.Data(), muonCutName.Data())); + names.push_back(Form("PairsEleMuMEMM_%s_%s", trackCutName.Data(), muonCutName.Data())); + histNames += Form("%s;%s;%s;", names[3].Data(), names[4].Data(), names[5].Data()); + } + // NOTE: the map key encodes (iTrack, iMuon) without a pair-cut axis, so this base entry sits at iTrack*fNCutsMuon + iMuon. + // Pair-cut entries below use a different stride so they do not collide. + fTrackMuonHistNames[iTrack * fNCutsMuon + iMuon] = names; + + // (track cut, muon cut, pair cut) entries + if (objArrayPair && objArrayPair->GetEntries() > 0) { + for (int iPairCut = 0; iPairCut < objArrayPair->GetEntries(); ++iPairCut) { + names = { + Form("PairsEleMuSEPM_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), objArrayPair->At(iPairCut)->GetName()), + Form("PairsEleMuSEPP_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), objArrayPair->At(iPairCut)->GetName()), + Form("PairsEleMuSEMM_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), objArrayPair->At(iPairCut)->GetName())}; + histNames += Form("%s;%s;%s;", names[0].Data(), names[1].Data(), names[2].Data()); + if (fEnableBarrelMuonMixingHistos) { + names.push_back(Form("PairsEleMuMEPM_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), objArrayPair->At(iPairCut)->GetName())); + names.push_back(Form("PairsEleMuMEPP_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), objArrayPair->At(iPairCut)->GetName())); + names.push_back(Form("PairsEleMuMEMM_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), objArrayPair->At(iPairCut)->GetName())); + histNames += Form("%s;%s;%s;", names[3].Data(), names[4].Data(), names[5].Data()); + } + int index = fNCutsBarrel * fNCutsMuon + iTrack * (fNCutsMuon * nPairCuts) + iMuon * nPairCuts + iPairCut; + fTrackMuonHistNames[index] = names; + } + } + + // (track cut, muon cut, MC rec signal) MC-matched entries + if (!fEmuRecMCSignals.empty()) { + for (unsigned int isig = 0; isig < fEmuRecMCSignals.size(); isig++) { + auto sig = fEmuRecMCSignals.at(isig); + names = { + Form("PairsEleMuSEPM_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName()), + Form("PairsEleMuSEPP_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName()), + Form("PairsEleMuSEMM_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())}; + if (fConfigQA) { + names.push_back(Form("PairsEleMuSEPMCorrectAssoc_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + names.push_back(Form("PairsEleMuSEPMIncorrectAssoc_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + names.push_back(Form("PairsEleMuSEPM_ambiguousInBunch_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + names.push_back(Form("PairsEleMuSEPM_ambiguousInBunchCorrectAssoc_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + names.push_back(Form("PairsEleMuSEPM_ambiguousInBunchIncorrectAssoc_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + names.push_back(Form("PairsEleMuSEPM_ambiguousOutOfBunch_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + names.push_back(Form("PairsEleMuSEPM_ambiguousOutOfBunchCorrectAssoc_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + names.push_back(Form("PairsEleMuSEPM_ambiguousOutOfBunchIncorrectAssoc_%s_%s_%s", trackCutName.Data(), muonCutName.Data(), sig->GetName())); + } + for (auto const& n : names) { + histNames += Form("%s;", n.Data()); + } + int index = iTrack * (fNCutsMuon * fEmuRecMCSignals.size()) + iMuon * fEmuRecMCSignals.size() + isig; + fTrackMuonHistNamesMCmatched.try_emplace(index, names); + } + } + } + } + } + // get the mc generated acceptance cuts TString mcgenCutsStr = fConfigCuts.MCgenAcc.value; if (!mcgenCutsStr.IsNull()) { @@ -1702,7 +2200,7 @@ struct AnalysisSameEventPairing { TString addMCSignalsGenStr = fConfigMC.genSignalsJSON.value; if (addMCSignalsGenStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsGenStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() > 2) { // NOTE: only 2 prong signals continue; } @@ -1721,7 +2219,7 @@ struct AnalysisSameEventPairing { } if (isMCGen) { - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->GetNProngs() == 1) { histNames += Form("MCTruthGen_%s;", sig->GetName()); // TODO: Add these names to a std::vector to avoid using Form in the process function histNames += Form("MCTruthGenSel_%s;", sig->GetName()); @@ -1733,7 +2231,7 @@ struct AnalysisSameEventPairing { fHasTwoProngGenMCsignals = true; // for these pair level signals, also add histograms for each MCgenAcc cut if specified if (fUseMCGenAccCut) { - for (auto& cut : fMCGenAccCuts) { + for (auto const& cut : fMCGenAccCuts) { histNames += Form("MCTruthGenPairSel_%s_%s;", sig->GetName(), cut->GetName()); histNames += Form("MCTruthGenPseudoPolPairSel_%s_%s;", sig->GetName(), cut->GetName()); } @@ -1750,10 +2248,6 @@ struct AnalysisSameEventPairing { if (fConfigOptions.noCorr) { VarManager::SetupFwdDCAFitterNoCorr(); - } else if (fConfigOptions.corrFullGeo || (fConfigOptions.useKFVertexing && fConfigOptions.propToPCA)) { - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigCCDB.geoPath); - } } else { fLUT = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigCCDB.lutPath)); VarManager::SetupMatLUTFwdDCAFitter(fLUT); @@ -1761,9 +2255,9 @@ struct AnalysisSameEventPairing { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON @@ -1773,41 +2267,28 @@ struct AnalysisSameEventPairing { void initParamsFromCCDB(uint64_t timestamp, bool withTwoProngFitter = true) { - if (fConfigOptions.useRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations - } + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigOptions.magField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(fConfigOptions.magField.value, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (withTwoProngFitter) { + if (fConfigOptions.useKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables + VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); } + } else { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations } } // Template function to run same event pairing (barrel-barrel, muon-muon, barrel-muon) template - void runSameEventPairing(TEvents const& events, Preslice& preslice, TTrackAssocs const& assocs, TTracks const& /*tracks*/, ReducedMCEvents const& /*mcEvents*/, ReducedMCTracks const& /*mcTracks*/) + void runSameEventPairing(TEvents const& events, Preslice& preslice, TTrackAssocs const& assocs, TTracks const& /*tracks*/, ReducedMCEvents const& /*mcEvents*/, ReducedMCTracks const& mcTracks) { if (events.size() == 0) { LOG(warning) << "No events in this TF, going to the next one ..."; @@ -1829,48 +2310,72 @@ struct AnalysisSameEventPairing { ncuts = fNCutsMuon; } - uint32_t twoTrackFilter = static_cast(0); - int sign1 = 0; - int sign2 = 0; - uint32_t mcDecision = static_cast(0); - bool isCorrectAssoc_leg1 = false; - bool isCorrectAssoc_leg2 = false; - dielectronList.reserve(1); - dimuonList.reserve(1); - dielectronsExtraList.reserve(1); - dimuonsExtraList.reserve(1); - dielectronInfoList.reserve(1); - dileptonInfoList.reserve(1); + auto twoTrackFilter = static_cast(0); + int sign1 = 0; + int sign2 = 0; + auto mcDecision = static_cast(0); + bool isCorrectAssoc_leg1 = false; + bool isCorrectAssoc_leg2 = false; + + // estimate reserved size + int64_t reserveSize = 0; + int64_t reserveSizeGen = mcTracks.size(); + for (auto const& event : events) { + if (event.isEventSelected_bit(0)) { + auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); + size_t nGood = 0; + for (auto const& t : groupedAssocs) { + if constexpr (TPairType == VarManager::kDecayToEE) { + if (t.isBarrelSelected_raw()) { + nGood++; + } + } else if constexpr (TPairType == VarManager::kDecayToMuMu) { + if (t.isMuonSelected_raw()) { + nGood++; + } + } + } + reserveSize += nGood * (nGood - 1) / 2; + } + } + + dielectronList.reserve(reserveSize); + dimuonList.reserve(reserveSize); + dielectronsExtraList.reserve(reserveSize); + dimuonsExtraList.reserve(reserveSize); + dielectronInfoList.reserve(reserveSize); + dileptonInfoList.reserve(reserveSize); if (fConfigOptions.flatTables.value) { - dielectronAllList.reserve(1); - dimuonAllList.reserve(1); + dielectronAllList.reserve(reserveSize); + dimuonAllList.reserve(reserveSize); } if (useMiniTree.fConfigMiniTree) { - dileptonMiniTreeGen.reserve(1); - dileptonMiniTreeRec.reserve(1); + dileptonMiniTreeGen.reserve(reserveSizeGen); + dileptonMiniTreeRec.reserve(reserveSize); } if (fConfigOptions.polarTables.value) { - dileptonPolarList.reserve(1); + dileptonPolarList.reserve(reserveSize); } + constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::ReducedEventQvector) > 0); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelCov) > 0); - for (auto& event : events) { + for (auto const& event : events) { if (!event.has_reducedMCevent() || !event.isEventSelected_bit(0)) { // condition on reducedMCevent to avoid rec. events with no generated event continue; } uint8_t evSel = event.isEventSelected_raw(); // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); - VarManager::FillEvent(event.reducedMCevent(), VarManager::fgValues); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); + VarManager::FillEvent(event.reducedMCevent(), dqefficiency_helpers::varValues()); auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); if (groupedAssocs.size() == 0) { continue; } - for (auto& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { + for (auto const& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { if constexpr (TPairType == VarManager::kDecayToEE) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; @@ -1917,19 +2422,19 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); } - if (!fConfigMC.skimSignalOnly || (fConfigMC.skimSignalOnly && mcDecision > 0)) { + if (!fConfigMC.skimSignalOnly || mcDecision > 0) { dielectronList(event.globalIndex(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), twoTrackFilter, mcDecision); if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackCollInfo) > 0) { - dielectronInfoList(t1.collisionId(), t1.trackId(), t2.trackId()); - dileptonInfoList(t1.collisionId(), event.posX(), event.posY(), event.posZ()); + dielectronInfoList(event.collisionId(), t1.trackId(), t2.trackId()); + dileptonInfoList(event.collisionId(), event.posX(), event.posY(), event.posZ()); } if constexpr (trackHasCov && TTwoProngFitter) { dielectronsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingLzProjected], VarManager::fgValues[VarManager::kVertexingLxyProjected]); @@ -1947,15 +2452,38 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kVertexingLzProjected], VarManager::fgValues[VarManager::kVertexingLxyProjected]); } if (fConfigOptions.polarTables.value && t1.has_reducedMCTrack() && t2.has_reducedMCTrack()) { - dileptonPolarList(VarManager::fgValues[VarManager::kCosThetaHE], VarManager::fgValues[VarManager::kPhiHE], VarManager::fgValues[VarManager::kPhiTildeHE], + dileptonPolarList(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], t1.sign() + t2.sign(), twoTrackFilter, + VarManager::fgValues[VarManager::kCosThetaHE], VarManager::fgValues[VarManager::kPhiHE], VarManager::fgValues[VarManager::kPhiTildeHE], VarManager::fgValues[VarManager::kCosThetaCS], VarManager::fgValues[VarManager::kPhiCS], VarManager::fgValues[VarManager::kPhiTildeCS], VarManager::fgValues[VarManager::kCosThetaPP], VarManager::fgValues[VarManager::kPhiPP], VarManager::fgValues[VarManager::kPhiTildePP], VarManager::fgValues[VarManager::kCosThetaRM], - VarManager::fgValues[VarManager::kCosThetaStarTPC], VarManager::fgValues[VarManager::kCosThetaStarFT0A], VarManager::fgValues[VarManager::kCosThetaStarFT0C]); + VarManager::fgValues[VarManager::kCosThetaStarTPC], VarManager::fgValues[VarManager::kCosThetaStarFT0A], VarManager::fgValues[VarManager::kCosThetaStarFT0C], VarManager::fgValues[VarManager::kCosThetaStarRandom]); } } } } + constexpr bool eventHasVtxCovForTable = ((TEventFillMap & VarManager::ObjTypes::ReducedEventVtxCov) > 0); + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelPID) > 0 && trackHasCov && TTwoProngFitter && eventHasVtxCovForTable) { + // kVertexingProcCode == 0: the fit failed; FillPairVertexing then returns early without updating the + // projected / pseudo-proper / pointing-angle variables, so they would be stale -> skip the pair + if (t1.sign() * t2.sign() < 0 && mcDecision > 0) { + const bool ambiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + const bool ambiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + PromptNonPromptSepTable(twoTrackFilter, mcDecision, + VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingLxyProjected], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], + VarManager::fgValues[VarManager::kVertexingChi2PCA], VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], + t1.pt(), t1.eta(), VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kDCAxyOverSig1], VarManager::fgValues[VarManager::kDCAzOverSig1], + VarManager::fgValues[VarManager::kITSclusterMap1], t1.itsChi2NCl(), + t2.pt(), t2.eta(), VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kDCAxyOverSig2], VarManager::fgValues[VarManager::kDCAzOverSig2], + VarManager::fgValues[VarManager::kITSclusterMap2], t2.itsChi2NCl(), + ambiInBunch, ambiOutOfBunch, isCorrectAssoc_leg1 && isCorrectAssoc_leg2, + VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kVtxNcontribReal]); + } + } } if constexpr (TPairType == VarManager::kDecayToMuMu) { @@ -1965,10 +2493,12 @@ struct AnalysisSameEventPairing { } auto t1 = a1.template reducedmuon_as(); auto t2 = a2.template reducedmuon_as(); - if (t1.matchMCHTrackId() == t2.matchMCHTrackId() && t1.matchMCHTrackId() >= 0) + if (t1.matchMCHTrackId() == t2.matchMCHTrackId() && t1.matchMCHTrackId() >= 0) { continue; - if (t1.matchMFTTrackId() == t2.matchMFTTrackId() && t1.matchMFTTrackId() >= 0) + } + if (t1.matchMFTTrackId() == t2.matchMFTTrackId() && t1.matchMFTTrackId() >= 0) { continue; + } sign1 = t1.sign(); sign2 = t2.sign(); // store the ambiguity number of the two dilepton legs in the last 4 digits of the two-track filter @@ -2006,7 +2536,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -2016,63 +2546,51 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), twoTrackFilter, mcDecision); if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { - dileptonInfoList(t1.collisionId(), event.posX(), event.posY(), event.posZ()); + dileptonInfoList(event.collisionId(), event.posX(), event.posY(), event.posZ()); } - if constexpr (TTwoProngFitter) { - dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); - if (fConfigOptions.flatTables.value && t1.has_reducedMCTrack() && t2.has_reducedMCTrack()) { - dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), - event.selection_raw(), evSel, - event.reducedMCevent().mcPosX(), event.reducedMCevent().mcPosY(), event.reducedMCevent().mcPosZ(), - VarManager::fgValues[VarManager::kMass], - mcDecision, - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], - VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], - VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], - VarManager::fgValues[VarManager::kCosPointingAngle], - VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), - VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), - t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), - t1.mcMask(), t2.mcMask(), - t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), - t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), - t1.chi2(), t2.chi2(), - t1.reducedMCTrack().pt(), t1.reducedMCTrack().eta(), t1.reducedMCTrack().phi(), t1.reducedMCTrack().e(), - t2.reducedMCTrack().pt(), t2.reducedMCTrack().eta(), t2.reducedMCTrack().phi(), t2.reducedMCTrack().e(), - t1.reducedMCTrack().vx(), t1.reducedMCTrack().vy(), t1.reducedMCTrack().vz(), t1.reducedMCTrack().vt(), - t2.reducedMCTrack().vx(), t2.reducedMCTrack().vy(), t2.reducedMCTrack().vz(), t2.reducedMCTrack().vt(), - (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), - isCorrectAssoc_leg1, isCorrectAssoc_leg2, - -999.0, -999.0, -999.0, -999.0, -999.0, - -999.0, -999.0, -999.0, -999.0, -999.0, - -999.0, VarManager::fgValues[VarManager::kMultDimuons], - VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); - } + dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); + if (fConfigOptions.flatTables.value && t1.has_reducedMCTrack() && t2.has_reducedMCTrack()) { + dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), + event.selection_raw(), evSel, + event.reducedMCevent().mcPosX(), event.reducedMCevent().mcPosY(), event.reducedMCevent().mcPosZ(), + VarManager::fgValues[VarManager::kMass], + mcDecision, + VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], + VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], + VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), + VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), + t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), + t1.mcMask(), t2.mcMask(), + t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), + t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), + t1.chi2(), t2.chi2(), + t1.reducedMCTrack().pt(), t1.reducedMCTrack().eta(), t1.reducedMCTrack().phi(), t1.reducedMCTrack().e(), + t2.reducedMCTrack().pt(), t2.reducedMCTrack().eta(), t2.reducedMCTrack().phi(), t2.reducedMCTrack().e(), + t1.reducedMCTrack().vx(), t1.reducedMCTrack().vy(), t1.reducedMCTrack().vz(), t1.reducedMCTrack().vt(), + t2.reducedMCTrack().vx(), t2.reducedMCTrack().vy(), t2.reducedMCTrack().vz(), t2.reducedMCTrack().vt(), + (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), + isCorrectAssoc_leg1, isCorrectAssoc_leg2, + -999.0, -999.0, -999.0, -999.0, -999.0, + -999.0, -999.0, -999.0, -999.0, -999.0, + -999.0, VarManager::fgValues[VarManager::kMultDimuons], + VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); } } - // TODO: the model for the electron-muon combination has to be thought through - /*if constexpr (TPairType == VarManager::kElectronMuon) { - twoTrackFilter = a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isMuonSelected_raw() & fTwoTrackFilterMask; - }*/ - // Fill histograms bool isAmbiInBunch = false; bool isAmbiOutOfBunch = false; - bool isCorrect_pair = false; - if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) - isCorrect_pair = true; - for (int icut = 0; icut < ncuts; icut++) { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); - if (sign1 * sign2 < 0) { // +- pairs - fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); // reconstructed, unmatched - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals + if (sign1 * sign2 < 0) { // +- pairs + fHistMan->FillHistClass(histNames[icut][0].Data(), dqefficiency_helpers::varValues()); // reconstructed, unmatched + for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], isAmbiInBunch, isAmbiOutOfBunch, isCorrect_pair, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), dqefficiency_helpers::varValues()); // matched signal if (useMiniTree.fConfigMiniTree) { if constexpr (TPairType == VarManager::kDecayToMuMu) { twoTrackFilter = a1.isMuonSelected_raw() & a2.isMuonSelected_raw() & fMuonFilterMask; @@ -2107,81 +2625,82 @@ struct AnalysisSameEventPairing { } if (fConfigQA) { if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { // correct track-collision association - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][3].Data(), dqefficiency_helpers::varValues()); } else { // incorrect track-collision association - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][4].Data(), dqefficiency_helpers::varValues()); } if (isAmbiInBunch) { // ambiguous in bunch - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][5].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][5].Data(), dqefficiency_helpers::varValues()); if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][6].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][6].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][7].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][7].Data(), dqefficiency_helpers::varValues()); } } if (isAmbiOutOfBunch) { // ambiguous out of bunch - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][8].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][8].Data(), dqefficiency_helpers::varValues()); if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][9].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][9].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][10].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][10].Data(), dqefficiency_helpers::varValues()); } } } } if (fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3].Data(), dqefficiency_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), dqefficiency_helpers::varValues()); } } } } else { if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(histNames[icut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][1].Data(), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][1].Data(), dqefficiency_helpers::varValues()); } } if (fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4].Data(), dqefficiency_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), dqefficiency_helpers::varValues()); } } } else { // -- pairs - fHistMan->FillHistClass(histNames[icut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][2].Data(), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][2].Data(), dqefficiency_helpers::varValues()); } } if (fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][5].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5].Data(), dqefficiency_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), dqefficiency_helpers::varValues()); } } } } for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!(cut.IsSelected(VarManager::fgValues))) // apply pair cuts + if (!(cut.IsSelected(dqefficiency_helpers::varValues()))) { // apply pair cuts continue; + } if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), dqefficiency_helpers::varValues()); } else { if (sign1 > 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), dqefficiency_helpers::varValues()); } } } // end loop (pair cuts) @@ -2193,18 +2712,20 @@ struct AnalysisSameEventPairing { PresliceUnsorted perReducedMcEvent = aod::reducedtrackMC::reducedMCeventId; - template - void runMCGen(MyEventsVtxCovSelected const& events, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) + template + void runMCGen(TEvents const& events, + ReducedMCEvents const& mcEvents, + ReducedMCTracks const& mcTracks) { uint32_t mcDecision = 0; int isig = 0; // Loop over all MC single particles to fill generator level histograms, disregarding of whether they belong to selected reconstructed events or not - for (auto& mctrack : mcTracks) { - for (auto& sig : fGenMCSignals) { + for (auto const& mctrack : mcTracks) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { VarManager::FillTrackMC(mcTracks, mctrack); - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -2214,31 +2735,36 @@ struct AnalysisSameEventPairing { std::vector FinalStateMcParticleIndices; // Now loop over reconstructed events to select only MC particles belonging to the same MC collision as the reconstructed event - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } if (!event.has_reducedMCevent()) { continue; } + // Fill the event-wise variables (e.g. centrality) of this reconstructed event, otherwise the generated particles and pairs + // get the values left over from the last event processed in runSameEventPairing + VarManager::ResetValues(0, VarManager::kNVars); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); + VarManager::FillEvent(event.reducedMCevent(), dqefficiency_helpers::varValues()); FinalStateMcParticleIndices.clear(); auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedMCeventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); - for (auto& track : groupedMCTracks) { + for (auto const& track : groupedMCTracks) { auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, track_raw)) { if (track.reducedMCeventId() != event.reducedMCeventId()) { // check that the mc track belongs to the same mc collision as the reconstructed event continue; } VarManager::FillTrackMC(mcTracks, track); mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); MCTruthTableEffi(VarManager::fgValues[VarManager::kMCPt], VarManager::fgValues[VarManager::kMCEta], VarManager::fgValues[VarManager::kMCY], VarManager::fgValues[VarManager::kMCPhi], VarManager::fgValues[VarManager::kMCVz], VarManager::fgValues[VarManager::kMCVtxZ], VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if (useMiniTree.fConfigMiniTree) { @@ -2248,7 +2774,7 @@ struct AnalysisSameEventPairing { } isig++; } - for (auto& sig : fFinalStateMCSignals) { + for (auto const& sig : fFinalStateMCSignals) { if (sig->CheckSignal(true, track_raw)) { FinalStateMcParticleIndices.push_back(track.globalIndex()); break; // if one of the final state signals is matched, no need to check the others @@ -2259,19 +2785,19 @@ struct AnalysisSameEventPairing { if (fHasTwoProngGenMCsignals) { // loop over combinations of the selected mc particles to fill generator level pair histograms - for (auto& t1 : FinalStateMcParticleIndices) { + for (auto const& t1 : FinalStateMcParticleIndices) { auto t1_raw = mcTracks.rawIteratorAt(t1); - for (auto& t2 : FinalStateMcParticleIndices) { + for (auto const& t2 : FinalStateMcParticleIndices) { if (t2 <= t1) { continue; // avoid double counting and self-pairing } - // for (auto& [t1, t2] : combinations(groupedMCTracks, groupedMCTracks)) { + // for (auto const& [t1, t2] : combinations(groupedMCTracks, groupedMCTracks)) { // cout << "Processing pair of mcTracks with globalIndices = " << t1.globalIndex() << ", " << t2.globalIndex() << endl; auto t2_raw = mcTracks.rawIteratorAt(t2); mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->GetNProngs() != 2) { // NOTE: 2-prong signals required here continue; } @@ -2291,14 +2817,14 @@ struct AnalysisSameEventPairing { } } // cout << " Filled VarManager for the pair." << endl; - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), VarManager::fgValues); - fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); // Fill also acceptance cut histograms if requested if (fUseMCGenAccCut) { - for (auto& cut : fMCGenAccCuts) { - if (cut->IsSelected(VarManager::fgValues)) { - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s_%s", sig->GetName(), cut->GetName()), VarManager::fgValues); - fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairSel_%s_%s", sig->GetName(), cut->GetName()), VarManager::fgValues); + for (auto const& cut : fMCGenAccCuts) { + if (cut->IsSelected(dqefficiency_helpers::varValues())) { + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s_%s", sig->GetName(), cut->GetName()), dqefficiency_helpers::varValues()); + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairSel_%s_%s", sig->GetName(), cut->GetName()), dqefficiency_helpers::varValues()); } } } @@ -2316,6 +2842,324 @@ struct AnalysisSameEventPairing { // cout << "AnalysisSameEventPairing::runMCGen() completed" << endl; } + // Run same-event e-mu pairing on reconstructed tracks. Logic mirrors runEmuSameEventPairing in tableReader_withAssoc.h + // with MC matching added: for each pair we evaluate the user-supplied fEmuRecMCSignals (2-prong, leg1=e, leg2=µ) + // and fill an MC-matched histogram per (track cut, muon cut, signal) when CheckSignal returns true on the linked + // ReducedMCTracks. Mixed events are handled in runEmuMixedPairing / runEmuSameSideMixing and do not perform MC matching. + template + void runEmuSameEventPairing(TEvents const& events, Preslice& preslice1, TTrackAssocs const& assocs1, TTracks const& /*tracks1*/, Preslice& preslice2, TMuonAssocs const& assocs2, TMuons const& /*tracks2*/, ReducedMCEvents const& /*mcEvents*/, ReducedMCTracks const& /*mcTracks*/) + { + if (events.size() > 0) { + if (fCurrentRun != events.begin().runNumber()) { + initParamsFromCCDB(events.begin().timestamp(), true); + fCurrentRun = events.begin().runNumber(); + } + } + + const auto& histNames = fTrackMuonHistNames; + const auto& histNamesMC = fTrackMuonHistNamesMCmatched; + int nPairCuts = !fPairCuts.empty() ? static_cast(fPairCuts.size()) : 1; + + uint32_t twoTrackFilter = 0; + uint32_t mcDecision = 0; + bool isCorrectAssoc_leg1 = false; + bool isCorrectAssoc_leg2 = false; + int sign1 = 0; + int sign2 = 0; + + constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::ReducedEventQvector) > 0); + constexpr bool eventHasQvectorCentr = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); + + for (auto const& event : events) { + if (!event.has_reducedMCevent() || !event.isEventSelected_bit(0)) { + continue; + } + + VarManager::ResetValues(0, VarManager::kNVars); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); + VarManager::FillEvent(event.reducedMCevent(), dqefficiency_helpers::varValues()); + + auto groupedAssocs1 = assocs1.sliceBy(preslice1, event.globalIndex()); + if (groupedAssocs1.size() == 0) { + continue; + } + auto groupedAssocs2 = assocs2.sliceBy(preslice2, event.globalIndex()); + if (groupedAssocs2.size() == 0) { + continue; + } + + for (auto const& [a1, a2] : o2::soa::combinations(soa::CombinationsFullIndexPolicy(groupedAssocs1, groupedAssocs2))) { + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) { + continue; + } + if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) { + continue; + } + + auto t1 = a1.template reducedtrack_as(); + auto t2 = a2.template reducedmuon_as(); + sign1 = t1.sign(); + sign2 = t2.sign(); + + // build the per-pair filter: a bit is set iff the same bit index is set in both the barrel and muon raw selections + twoTrackFilter = 0; + int minCuts = std::min(fNCutsBarrel, fNCutsMuon); + for (int i = 0; i < minCuts; ++i) { + if ((a1.isBarrelSelected_raw() & (static_cast(1) << i)) && (a2.isMuonSelected_raw() & (static_cast(1) << i))) { + twoTrackFilter |= (static_cast(1) << i); + } + } + // ambiguity bits in the upper 4 slots, matching the convention used by dilepton MC histograms + if (t1.barrelAmbiguityInBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 28); + } + if (t2.muonAmbiguityInBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 29); + } + if (t1.barrelAmbiguityOutOfBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 30); + } + if (t2.muonAmbiguityOutOfBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 31); + } + + // MC matching: pass the legs in fixed order (electron, muon) to satisfy the 2-prong template ordering + mcDecision = 0; + isCorrectAssoc_leg1 = false; + isCorrectAssoc_leg2 = false; + if (t1.has_reducedMCTrack() && t2.has_reducedMCTrack()) { + int isig = 0; + for (auto sig = fEmuRecMCSignals.begin(); sig != fEmuRecMCSignals.end(); sig++, isig++) { + if ((*sig)->CheckSignal(true, t1.reducedMCTrack(), t2.reducedMCTrack())) { + mcDecision |= (static_cast(1) << isig); + } + } + isCorrectAssoc_leg1 = (t1.reducedMCTrack().reducedMCevent() == event.reducedMCevent()); + isCorrectAssoc_leg2 = (t2.reducedMCTrack().reducedMCevent() == event.reducedMCevent()); + } + + VarManager::FillPair(t1, t2); + if (fPropTrack) { + VarManager::FillPairCollision(event, t1, t2); + } + if constexpr (eventHasQvector) { + VarManager::FillPairVn(t1, t2); + } + if constexpr (eventHasQvectorCentr) { + VarManager::FillPairVn(t1, t2); + } + + bool isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + bool isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + bool isCorrectPair = isCorrectAssoc_leg1 && isCorrectAssoc_leg2; + + // fill reco histograms per (track cut, muon cut[, pair cut]) and the MC-matched variants per signal + for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { + if (!(a1.isBarrelSelected_raw() & (static_cast(1) << iTrack))) { + continue; + } + for (int iMuon = 0; iMuon < fNCutsMuon; ++iMuon) { + if (!(a2.isMuonSelected_raw() & (static_cast(1) << iMuon))) { + continue; + } + + // base reco entry (no pair-cut) + auto itHistBase = histNames.find(iTrack * fNCutsMuon + iMuon); + if (itHistBase != histNames.end()) { + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(itHistBase->second[0].Data(), dqefficiency_helpers::varValues()); + } else if (sign1 > 0) { + fHistMan->FillHistClass(itHistBase->second[1].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHistBase->second[2].Data(), dqefficiency_helpers::varValues()); + } + } + + // pair-cut reco entries + for (int iPairCut = 0; iPairCut < nPairCuts; ++iPairCut) { + if (!fPairCuts.empty()) { + AnalysisCompositeCut cut = fPairCuts.at(iPairCut); + if (!cut.IsSelected(dqefficiency_helpers::varValues())) { + continue; + } + } + if (fPairCuts.empty()) { + continue; // already filled above + } + int index = fNCutsBarrel * fNCutsMuon + iTrack * (fNCutsMuon * nPairCuts) + iMuon * nPairCuts + iPairCut; + auto itHist = histNames.find(index); + if (itHist == histNames.end()) { + continue; + } + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(itHist->second[0].Data(), dqefficiency_helpers::varValues()); + } else if (sign1 > 0) { + fHistMan->FillHistClass(itHist->second[1].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHist->second[2].Data(), dqefficiency_helpers::varValues()); + } + } + + // MC-matched entries (only when at least one signal fired and both legs have MC links) + if (mcDecision == 0 || fEmuRecMCSignals.empty()) { + continue; + } + for (unsigned int isig = 0; isig < fEmuRecMCSignals.size(); isig++) { + if (!(mcDecision & (static_cast(1) << isig))) { + continue; + } + int indexMC = iTrack * (fNCutsMuon * fEmuRecMCSignals.size()) + iMuon * fEmuRecMCSignals.size() + isig; + auto itHistMC = histNamesMC.find(indexMC); + if (itHistMC == histNamesMC.end()) { + continue; + } + const auto& mcNames = itHistMC->second; + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(mcNames[0].Data(), dqefficiency_helpers::varValues()); + } else if (sign1 > 0) { + fHistMan->FillHistClass(mcNames[1].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(mcNames[2].Data(), dqefficiency_helpers::varValues()); + } + if (!fConfigQA || mcNames.size() < 11) { + continue; + } + // QA fills (PM only, mirroring the dilepton MC layout: indices 3..10) + if (sign1 * sign2 < 0) { + if (isCorrectPair) { + fHistMan->FillHistClass(mcNames[3].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(mcNames[4].Data(), dqefficiency_helpers::varValues()); + } + if (isAmbiInBunch) { + fHistMan->FillHistClass(mcNames[5].Data(), dqefficiency_helpers::varValues()); + if (isCorrectPair) { + fHistMan->FillHistClass(mcNames[6].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(mcNames[7].Data(), dqefficiency_helpers::varValues()); + } + } + if (isAmbiOutOfBunch) { + fHistMan->FillHistClass(mcNames[8].Data(), dqefficiency_helpers::varValues()); + if (isCorrectPair) { + fHistMan->FillHistClass(mcNames[9].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(mcNames[10].Data(), dqefficiency_helpers::varValues()); + } + } + } + } + } + } + } + } + } + + template + void runEmuMixedPairing(TAssoc1 const& assocs1, TAssoc2 const& assocs2, TTracks1 const& /*tracks1*/, TTracks2 const& /*tracks2*/) + { + const auto& histNames = fTrackMuonHistNames; + int nPairCuts = !fPairCuts.empty() ? static_cast(fPairCuts.size()) : 1; + int sign1 = 0; + int sign2 = 0; + + constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::ReducedEventQvector) > 0); + constexpr bool eventHasQvectorCentr = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); + + for (auto const& a1 : assocs1) { + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) { + continue; + } + for (auto const& a2 : assocs2) { + if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) { + continue; + } + auto t1 = a1.template reducedtrack_as(); + auto t2 = a2.template reducedmuon_as(); + sign1 = t1.sign(); + sign2 = t2.sign(); + + VarManager::FillPairME(t1, t2); + if constexpr (eventHasQvector) { + VarManager::FillPairVn(t1, t2); + } + if constexpr (eventHasQvectorCentr) { + VarManager::FillPairVn(t1, t2); + } + + for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { + if (!(a1.isBarrelSelected_raw() & (static_cast(1) << iTrack))) { + continue; + } + for (int iMuon = 0; iMuon < fNCutsMuon; ++iMuon) { + if (!(a2.isMuonSelected_raw() & (static_cast(1) << iMuon))) { + continue; + } + // base ME entry + auto itHistBase = histNames.find(iTrack * fNCutsMuon + iMuon); + if (itHistBase != histNames.end() && itHistBase->second.size() >= 6) { + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(itHistBase->second[3].Data(), dqefficiency_helpers::varValues()); + } else if (sign1 > 0) { + fHistMan->FillHistClass(itHistBase->second[4].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHistBase->second[5].Data(), dqefficiency_helpers::varValues()); + } + } + for (int iPairCut = 0; iPairCut < nPairCuts; ++iPairCut) { + if (!fPairCuts.empty()) { + AnalysisCompositeCut cut = fPairCuts.at(iPairCut); + if (!cut.IsSelected(dqefficiency_helpers::varValues())) { + continue; + } + } + if (fPairCuts.empty()) { + continue; + } + int index = fNCutsBarrel * fNCutsMuon + iTrack * (fNCutsMuon * nPairCuts) + iMuon * nPairCuts + iPairCut; + auto itHist = histNames.find(index); + if (itHist == histNames.end() || itHist->second.size() < 6) { + continue; + } + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(itHist->second[3].Data(), dqefficiency_helpers::varValues()); + } else if (sign1 > 0) { + fHistMan->FillHistClass(itHist->second[4].Data(), dqefficiency_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHist->second[5].Data(), dqefficiency_helpers::varValues()); + } + } + } + } + } + } + } + + template + void runEmuSameSideMixing(TEvents& events, Preslice& preslice1, TTrackAssocs const& assocs1, TTracks const& tracks1, + Preslice& preslice2, TMuonAssocs const& assocs2, TMuons const& tracks2) + { + events.bindExternalIndices(&assocs1); + events.bindExternalIndices(&assocs2); + int mixingDepth = fConfigMixingDepth.value; + for (auto const& [event1, event2] : selfCombinations(hashBin, mixingDepth, -1, events, events)) { + VarManager::ResetValues(0, VarManager::kNVars); + VarManager::FillEvent(event1, dqefficiency_helpers::varValues()); + + auto groupedAssocs1 = assocs1.sliceBy(preslice1, event1.globalIndex()); + groupedAssocs1.bindExternalIndices(&events); + if (groupedAssocs1.size() == 0) { + continue; + } + auto groupedAssocs2 = assocs2.sliceBy(preslice2, event2.globalIndex()); + groupedAssocs2.bindExternalIndices(&events); + if (groupedAssocs2.size() == 0) { + continue; + } + runEmuMixedPairing(groupedAssocs1, groupedAssocs2, tracks1, tracks2); + } + } + void processAllSkimmed(MyEventsVtxCovSelected const& events, soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons, @@ -2323,11 +3167,6 @@ struct AnalysisSameEventPairing { { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks); runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); - // Feature replaced by processMCGen - /*if (fConfigMC.runMCGenPair) { - runMCGen(mcEvents, mcTracks); - }*/ - // runSameEventPairing(event, tracks, muons); } void processBarrelOnlySkimmed(MyEventsVtxCovSelected const& events, @@ -2338,7 +3177,7 @@ struct AnalysisSameEventPairing { runMCGen(events, mcEvents, mcTracks); } - void processBarrelOnlyWithCollSkimmed(MyEventsVtxCovSelected const& events, + void processBarrelOnlyWithCollSkimmed(MyEventsVtxCovSelectedInfo const& events, soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguitiesWithColl const& barrelTracks, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) { @@ -2349,13 +3188,19 @@ struct AnalysisSameEventPairing { void processMuonOnlySkimmed(MyEventsVtxCovSelected const& events, soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); // Feature replaced by processMCGen /* if (fConfigMC.runMCGenPair) { runMCGen(mcEvents, mcTracks); }*/ } + void processMuonOnlyVertexingSkimmed(MyEventsVtxCovSelected const& events, + soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) + { + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); + } + PresliceUnsorted perReducedMcGenEvent = aod::reducedtrackMC::reducedMCeventId; void processMCGen(soa::Filtered const& events, ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) @@ -2364,32 +3209,32 @@ struct AnalysisSameEventPairing { uint32_t mcDecision = 0; int isig = 0; - for (auto& mctrack : mcTracks) { + for (auto const& mctrack : mcTracks) { VarManager::FillTrackMC(mcTracks, mctrack); // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. // NOTE: However, the working model is that the decisions on MC signals are precomputed during skimming and are stored in the mcReducedFlags member. // TODO: Use the mcReducedFlags to select signals - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } // Fill Generated histograms taking into account selected collisions - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } if (!event.has_reducedMCevent()) { continue; } - VarManager::FillEvent(event, VarManager::fgValues); - VarManager::FillEvent(event.reducedMCevent(), VarManager::fgValues); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); + VarManager::FillEvent(event.reducedMCevent(), dqefficiency_helpers::varValues()); // auto groupedMCTracks = mcTracks.sliceBy(perReducedMcGenEvent, event.reducedMCeventId()); // groupedMCTracks.bindInternalIndicesTo(&mcTracks); - // for (auto& track : groupedMCTracks) { - for (auto& track : mcTracks) { + // for (auto const& track : groupedMCTracks) { + for (auto const& track : mcTracks) { if (track.reducedMCeventId() != event.reducedMCeventId()) { continue; } @@ -2398,10 +3243,10 @@ struct AnalysisSameEventPairing { // auto track_raw = groupedMCTracks.rawIteratorAt(track.globalIndex()); mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, track_raw)) { mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); MCTruthTableEffi(VarManager::fgValues[VarManager::kMCPt], VarManager::fgValues[VarManager::kMCEta], VarManager::fgValues[VarManager::kMCY], VarManager::fgValues[VarManager::kMCPhi], VarManager::fgValues[VarManager::kMCVz], VarManager::fgValues[VarManager::kMCVtxZ], VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if (useMiniTree.fConfigMiniTree) { @@ -2414,24 +3259,24 @@ struct AnalysisSameEventPairing { } } // end loop over reconstructed events if (fHasTwoProngGenMCsignals) { - for (auto& [t1, t2] : combinations(mcTracks, mcTracks)) { + for (auto const& [t1, t2] : combinations(mcTracks, mcTracks)) { auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); if (t1_raw.reducedMCeventId() == t2_raw.reducedMCeventId()) { - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->GetNProngs() != 2) { // NOTE: 2-prong signals required here continue; } if (sig->CheckSignal(true, t1_raw, t2_raw)) { VarManager::FillPairMC(t1, t2); // NOTE: This feature will only work for muons - fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } } } // Fill Generated PAIR histograms taking into account selected collisions - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -2440,7 +3285,7 @@ struct AnalysisSameEventPairing { } if (fHasTwoProngGenMCsignals) { - for (auto& [t1, t2] : combinations(mcTracks, mcTracks)) { + for (auto const& [t1, t2] : combinations(mcTracks, mcTracks)) { if (t1.reducedMCeventId() != event.reducedMCeventId()) { continue; } @@ -2452,14 +3297,14 @@ struct AnalysisSameEventPairing { if (t1_raw.reducedMCeventId() == t2_raw.reducedMCeventId()) { mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->GetNProngs() != 2) { // NOTE: 2-prong signals required here continue; } if (sig->CheckSignal(true, t1_raw, t2_raw)) { mcDecision |= (static_cast(1) << isig); VarManager::FillPairMC(t1, t2); // NOTE: This feature will only work for muons - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); if (useMiniTree.fConfigMiniTree) { // WARNING! To be checked dileptonMiniTreeGen(mcDecision, -999, t1.pt(), t1.eta(), t1.phi(), t2.pt(), t2.eta(), t2.phi()); @@ -2478,19 +3323,19 @@ struct AnalysisSameEventPairing { uint32_t mcDecision = 0; int isig = 0; - for (auto& mctrack : mcTracks) { + for (auto const& mctrack : mcTracks) { VarManager::FillTrackMC(mcTracks, mctrack); // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. // NOTE: However, the working model is that the decisions on MC signals are precomputed during skimming and are stored in the mcReducedFlags member. // TODO: Use the mcReducedFlags to select signals - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } // Fill Generated histograms taking into account selected collisions - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -2498,7 +3343,7 @@ struct AnalysisSameEventPairing { continue; } - for (auto& track : mcTracks) { + for (auto const& track : mcTracks) { if (track.reducedMCeventId() != event.reducedMCeventId()) { continue; } @@ -2506,10 +3351,10 @@ struct AnalysisSameEventPairing { auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, track_raw)) { mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); MCTruthTableEffi(VarManager::fgValues[VarManager::kMCPt], VarManager::fgValues[VarManager::kMCEta], VarManager::fgValues[VarManager::kMCY], VarManager::fgValues[VarManager::kMCPhi], VarManager::fgValues[VarManager::kMCVz], VarManager::fgValues[VarManager::kMCVtxZ], VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if (useMiniTree.fConfigMiniTree) { @@ -2522,23 +3367,23 @@ struct AnalysisSameEventPairing { } } // end loop over reconstructed events if (fHasTwoProngGenMCsignals) { - for (auto& [t1, t2] : combinations(mcTracks, mcTracks)) { + for (auto const& [t1, t2] : combinations(mcTracks, mcTracks)) { auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); if (t1_raw.reducedMCeventId() == t2_raw.reducedMCeventId()) { - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->GetNProngs() != 2) { // NOTE: 2-prong signals required here continue; } if (sig->CheckSignal(true, t1_raw, t2_raw)) { VarManager::FillPairMC(t1, t2); // NOTE: This feature will only work for muons - fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenPair_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } } } - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -2549,20 +3394,20 @@ struct AnalysisSameEventPairing { if (fHasTwoProngGenMCsignals) { auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedMCeventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); - for (auto& [t1, t2] : combinations(groupedMCTracks, groupedMCTracks)) { + for (auto const& [t1, t2] : combinations(groupedMCTracks, groupedMCTracks)) { auto t1_raw = groupedMCTracks.rawIteratorAt(t1.globalIndex()); auto t2_raw = groupedMCTracks.rawIteratorAt(t2.globalIndex()); if (t1_raw.reducedMCeventId() == t2_raw.reducedMCeventId()) { mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->GetNProngs() != 2) { // NOTE: 2-prong signals required here continue; } if (sig->CheckSignal(true, t1_raw, t2_raw)) { mcDecision |= (static_cast(1) << isig); VarManager::FillPairMC(t1, t2); // NOTE: This feature will only work for muons - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); if (useMiniTree.fConfigMiniTree) { // WARNING! To be checked dileptonMiniTreeGen(mcDecision, -999, t1.pt(), t1.eta(), t1.phi(), t2.pt(), t2.eta(), t2.phi()); @@ -2576,7 +3421,22 @@ struct AnalysisSameEventPairing { } } - void processDummy(MyEvents&) + void processElectronMuonSkimmed(MyEventsVtxCovSelected const& events, + soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, + soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons, + ReducedMCEvents const& mcEvents, ReducedMCTracks const& mcTracks) + { + runEmuSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, muonAssocsPerCollision, muonAssocs, muons, mcEvents, mcTracks); + } + + void processMixingElectronMuonSkimmed(soa::Filtered& events, + soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, + soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + { + runEmuSameSideMixing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, muonAssocsPerCollision, muonAssocs, muons); + } + + void processDummy(MyEvents const&) { // do nothing } @@ -2585,6 +3445,9 @@ struct AnalysisSameEventPairing { PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmed, "Run barrel only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithCollSkimmed, "Run barrel only pairing, with skimmed tracks and with collision information", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmed, "Run muon only pairing, with skimmed tracks", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlyVertexingSkimmed, "Run muon only pairing with two-prong vertexing, with skimmed tracks", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processElectronMuonSkimmed, "Run electron-muon pairing, with skimmed tracks/muons", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processMixingElectronMuonSkimmed, "Run electron-muon mixing pairing, with skimmed tracks/muons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMCGenWithGrouping, "Loop over MC particle stack (grouped MCTracks) and fill generator level histograms", false); PROCESS_SWITCH(AnalysisSameEventPairing, processDummy, "Dummy function, enabled only if none of the others are enabled", true); @@ -2597,7 +3460,7 @@ struct AnalysisAsymmetricPairing { Produces ditrackExtraList; o2::base::MatLayerCylSet* fLUT = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. // Output objects OutputObj fOutputList{"output"}; @@ -2621,12 +3484,9 @@ struct AnalysisAsymmetricPairing { Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable fConfigGRPMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Choose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; Configurable fConfigUseAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - Configurable fConfigPropToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; Configurable fConfigLutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; Configurable fConfigMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; @@ -2634,32 +3494,32 @@ struct AnalysisAsymmetricPairing { Configurable fConfigMCRecSignalsJSON{"cfgMCRecSignalsJSON", "", "Additional list of MC signals (reconstructed) via JSON"}; Configurable fConfigMCGenSignalsJSON{"cfgMCGenSignalsJSON", "", "Comma separated list of MC signals (generated) via JSON"}; - Service fCCDB; + Service fCCDB{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fPairCuts; - int fNPairHistPrefixes; + int fNPairHistPrefixes = 0; std::vector fRecMCSignals; std::vector fGenMCSignals; // Filter masks to find legs in BarrelTrackCuts table - uint32_t fLegAFilterMask; - uint32_t fLegBFilterMask; - uint32_t fLegCFilterMask; + uint32_t fLegAFilterMask = 0; + uint32_t fLegBFilterMask = 0; + uint32_t fLegCFilterMask = 0; // Maps tracking which combination of leg cuts the track cuts participate in std::map fConstructedLegAFilterMasksMap; std::map fConstructedLegBFilterMasksMap; std::map fConstructedLegCFilterMasksMap; // Filter map for common track cuts - uint32_t fCommonTrackCutMask; + uint32_t fCommonTrackCutMask = 0; // Map tracking which common track cut the track cuts correspond to std::map fCommonTrackCutFilterMasks; - int fNLegCuts; + int fNLegCuts = 0; int fNPairCuts = 0; - int fNCommonTrackCuts; + int fNCommonTrackCuts = 0; // vectors for cut names and signal names, for easy access when calling FillHistogramList() std::vector fLegCutNames; std::vector fPairCutNames; @@ -2688,7 +3548,7 @@ struct AnalysisAsymmetricPairing { VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); // Get the leg cut filter masks fLegAFilterMask = fConfigLegAFilterMask.value; @@ -2707,8 +3567,8 @@ struct AnalysisAsymmetricPairing { TString addPairCutsStr = fConfigPairCutsJSON.value; if (addPairCutsStr != "") { std::vector addPairCuts = dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); - for (auto& t : addPairCuts) { - fPairCuts.push_back(reinterpret_cast(t)); + for (auto const& t : addPairCuts) { + fPairCuts.push_back(dynamic_cast(t)); cutNamesStr += Form(",%s", t->GetName()); } } @@ -2731,7 +3591,7 @@ struct AnalysisAsymmetricPairing { TString addMCSignalsStr = fConfigMCRecSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 2 && mcIt->GetNProngs() != 3) { LOG(fatal) << "Signal at reconstructed level requested (" << mcIt->GetName() << ") " << "does not have 2 or 3 prongs! Fix it"; } @@ -2746,21 +3606,21 @@ struct AnalysisAsymmetricPairing { } // Get the barrel track selection cuts - string tempCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + std::string tempCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); TString tempCutsStr = tempCuts; - // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + // check also the cuts added via JSON and add them to the std::string of cuts + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); TString addTrackCutsStr = tempCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + for (auto const& t : addTrackCuts) { tempCutsStr += Form(",%s", t->GetName()); } } std::unique_ptr objArray(tempCutsStr.Tokenize(",")); // Get the common leg cuts - int commonCutIdx; + int commonCutIdx = -1; TString commonNamesStr = fConfigCommonTrackCuts.value; if (!commonNamesStr.IsNull()) { // if common track cuts std::unique_ptr objArrayCommon(commonNamesStr.Tokenize(",")); @@ -2798,9 +3658,9 @@ struct AnalysisAsymmetricPairing { } fNLegCuts = objArrayLegs->GetEntries(); std::vector isThreeProng; - int legAIdx; - int legBIdx; - int legCIdx; + int legAIdx = -1; + int legBIdx = -1; + int legCIdx = -1; // Loop over leg defining cuts for (int icut = 0; icut < fNLegCuts; ++icut) { TString legsStr = objArrayLegs->At(icut)->GetName(); @@ -2855,9 +3715,9 @@ struct AnalysisAsymmetricPairing { DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), objArrayCommon->At(iCommonCut)->GetName()), fConfigHistogramSubgroups.value.data()); } - TString cutNamesStr = fConfigPairCuts.value; - if (!cutNamesStr.IsNull()) { // if pair cuts - std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); + TString pairCutNamesStr = fConfigPairCuts.value; + if (!pairCutNamesStr.IsNull()) { // if pair cuts + std::unique_ptr objArrayPair(pairCutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { // loop over pair cuts DefineHistograms(fHistMan, Form("TripletsBarrelSE_%s_%s", legsStr.Data(), objArrayPair->At(iPairCut)->GetName()), fConfigHistogramSubgroups.value.data()); @@ -2988,7 +3848,7 @@ struct AnalysisAsymmetricPairing { addMCSignalsStr = fConfigMCGenSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() == 1) { fGenMCSignals.push_back(mcIt); DefineHistograms(fHistMan, Form("MCTruthGen_%s;", mcIt->GetName()), fConfigHistogramSubgroups.value.data()); // TODO: Add these names to a std::vector to avoid using Form in the process function @@ -3029,40 +3889,24 @@ struct AnalysisAsymmetricPairing { void initParamsFromCCDB(uint64_t timestamp, bool isTriplets) { - if (fConfigUseRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (isTriplets) { + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); } } else { - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables } } } @@ -3083,18 +3927,38 @@ struct AnalysisAsymmetricPairing { int sign1 = 0; int sign2 = 0; uint32_t mcDecision = 0; - ditrackList.reserve(1); - ditrackExtraList.reserve(1); + + // estimate reserved size + int64_t reserveSize = 0; + for (auto const& event : events) { + auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); + auto groupedLegBAssocs = legBCandidateAssocs.sliceBy(preslice, event.globalIndex()); + size_t nGoodA = 0; + size_t nGoodB = 0; + for (auto const& t : groupedLegAAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodA++; + } + } + for (auto const& t : groupedLegBAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodB++; + } + } + reserveSize += nGoodA * nGoodB; + } + ditrackList.reserve(reserveSize); + ditrackExtraList.reserve(reserveSize); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::TrackCov) > 0 || (TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelCov) > 0); - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); if (groupedLegAAssocs.size() == 0) { @@ -3105,7 +3969,7 @@ struct AnalysisAsymmetricPairing { continue; } - for (auto& [a1, a2] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs))) { + for (auto const& [a1, a2] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs))) { uint32_t twoTrackFilter = 0; uint32_t twoTrackCommonFilter = 0; @@ -3141,12 +4005,12 @@ struct AnalysisAsymmetricPairing { bool isReflected = false; std::pair trackIds(t1.globalIndex(), t2.globalIndex()); - if (fPairCount.find(trackIds) != fPairCount.end()) { + if (fPairCount.contains(trackIds)) { // Double counting is possible due to track-collision ambiguity. Skip pairs which were counted before fPairCount[trackIds] += 1; continue; } - if (fPairCount.find(std::pair(trackIds.second, trackIds.first)) != fPairCount.end()) { + if (fPairCount.contains(std::pair(trackIds.second, trackIds.first))) { isReflected = true; } fPairCount[trackIds] += 1; @@ -3162,20 +4026,20 @@ struct AnalysisAsymmetricPairing { } // run MC matching for this pair - int isig = 0; + int iSigMc = 0; mcDecision = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { if (t1.has_reducedMCTrack() && t2.has_reducedMCTrack()) { VarManager::FillPairMC(t1.reducedMCTrack(), t2.reducedMCTrack()); if ((*sig)->CheckSignal(true, t1.reducedMCTrack(), t2.reducedMCTrack())) { - mcDecision |= static_cast(1) << isig; + mcDecision |= static_cast(1) << iSigMc; } } } // end loop over MC signals VarManager::FillPair(t1, t2); if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigPropToPCA); + VarManager::FillPairVertexing(event, t1, t2); } // Fill histograms @@ -3183,50 +4047,50 @@ struct AnalysisAsymmetricPairing { for (int icut = 0; icut < fNLegCuts; icut++) { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbi = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); - if (sign1 * sign2 < 0) { // +- pairs - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s", fLegCutNames[icut].Data()), VarManager::fgValues); // reconstructed, unmatched + if (sign1 * sign2 < 0) { // +- pairs + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); // reconstructed, unmatched if (isAmbi && fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); if (isAmbi && fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } } else { // -- pairs - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); if (isAmbi && fConfigQA) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } if (isReflected && fConfigReflectedHistograms) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } @@ -3235,23 +4099,23 @@ struct AnalysisAsymmetricPairing { for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { if (twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } @@ -3260,28 +4124,29 @@ struct AnalysisAsymmetricPairing { } // end loop (common cuts) int iPairCut = 0; for (auto cut = fPairCuts.begin(); cut != fPairCuts.end(); cut++, iPairCut++) { - if (!((*cut)->IsSelected(VarManager::fgValues))) // apply pair cuts + if (!((*cut)->IsSelected(dqefficiency_helpers::varValues()))) { // apply pair cuts continue; + } pairFilter |= (static_cast(1) << iPairCut); // Histograms with pair cuts if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } @@ -3290,23 +4155,23 @@ struct AnalysisAsymmetricPairing { for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { if (twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); } } for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); } } } @@ -3337,13 +4202,13 @@ struct AnalysisAsymmetricPairing { } } - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, dqefficiency_helpers::varValues()); auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); if (groupedLegAAssocs.size() == 0) { @@ -3360,17 +4225,17 @@ struct AnalysisAsymmetricPairing { // Based on triplet type, make suitable combinations of the partitions if (tripletType == VarManager::kTripleCandidateToPKPi) { - for (auto& [a1, a2, a3] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs, groupedLegCAssocs))) { + for (auto const& [a1, a2, a3] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs, groupedLegCAssocs))) { readTriplet(a1, a2, a3, tracks, event, tripletType); } } else if (tripletType == VarManager::kTripleCandidateToKPiPi) { - for (auto& a1 : groupedLegAAssocs) { - for (auto& [a2, a3] : combinations(groupedLegBAssocs, groupedLegCAssocs)) { + for (auto const& a1 : groupedLegAAssocs) { + for (auto const& [a2, a3] : combinations(groupedLegBAssocs, groupedLegCAssocs)) { readTriplet(a1, a2, a3, tracks, event, tripletType); } } } else { - LOG(fatal) << "Given tripletType not recognized. Don't know how to make combinations!" << endl; + LOG(fatal) << "Given tripletType not recognized. Don't know how to make combinations!"; } } // end event loop } @@ -3444,17 +4309,17 @@ struct AnalysisAsymmetricPairing { } // run MC matching for this triplet - int isig = 0; + int iSigMc = 0; mcDecision = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { if (t1.has_reducedMCTrack() && t2.has_reducedMCTrack() && t3.has_reducedMCTrack()) { if ((*sig)->CheckSignal(true, t1.reducedMCTrack(), t2.reducedMCTrack(), t3.reducedMCTrack())) { - mcDecision |= (static_cast(1) << isig); + mcDecision |= (static_cast(1) << iSigMc); } } } // end loop over MC signals - VarManager::FillTriple(t1, t2, t3, VarManager::fgValues, tripletType); + VarManager::FillTriple(t1, t2, t3, dqefficiency_helpers::varValues(), tripletType); if constexpr (TThreeProngFitter) { VarManager::FillTripletVertexing(event, t1, t2, t3, tripletType); } @@ -3464,43 +4329,44 @@ struct AnalysisAsymmetricPairing { for (int icut = 0; icut < fNLegCuts; icut++) { isAmbi = (threeTrackFilter & (static_cast(1) << 29)) || (threeTrackFilter & (static_cast(1) << 30)) || (threeTrackFilter & (static_cast(1) << 31)); if (threeTrackFilter & (static_cast(1) << icut)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) if (fConfigQA && isAmbi) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_ambiguous_%s", fLegCutNames[icut].Data()), dqefficiency_helpers::varValues()); } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { if (threeTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) } } // end loop (common cuts) int iPairCut = 0; for (auto cut = fPairCuts.begin(); cut != fPairCuts.end(); cut++, iPairCut++) { - if (!((*cut)->IsSelected(VarManager::fgValues))) // apply pair cuts + if (!((*cut)->IsSelected(dqefficiency_helpers::varValues()))) { // apply pair cuts continue; + } // Histograms with pair cuts - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) // Histograms with pair cuts and common track cuts for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { if (threeTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqefficiency_helpers::varValues()); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), VarManager::fgValues); // matched signal + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fRecMCSignalNames[isig].Data()), dqefficiency_helpers::varValues()); // matched signal } } // end loop (MC signals) } @@ -3539,15 +4405,15 @@ struct AnalysisAsymmetricPairing { // loop over mc stack and fill histograms for pure MC truth signals // group all the MC tracks which belong to the MC event corresponding to the current reconstructed event // auto groupedMCTracks = tracksMC.sliceBy(aod::reducedtrackMC::reducedMCeventId, event.reducedMCevent().globalIndex()); - for (auto& mctrack : mcTracks) { + for (auto const& mctrack : mcTracks) { VarManager::FillTrackMC(mcTracks, mctrack); // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. // NOTE: However, the working model is that the decisions on MC signals are precomputed during skimming and are stored in the mcReducedFlags member. // TODO: Use the mcReducedFlags to select signals - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -3558,7 +4424,7 @@ struct AnalysisAsymmetricPairing { void processMCGenWithEventSelection(soa::Filtered const& events, ReducedMCEvents const& /*mcEvents*/, ReducedMCTracks const& mcTracks) { - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -3568,21 +4434,21 @@ struct AnalysisAsymmetricPairing { auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedMCeventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); - for (auto& track : groupedMCTracks) { + for (auto const& track : groupedMCTracks) { VarManager::FillTrackMC(mcTracks, track); auto track_raw = groupedMCTracks.rawIteratorAt(track.globalIndex()); - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, track_raw)) { - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } } // end loop over reconstructed events } - void processDummy(MyEvents&) + void processDummy(MyEvents const&) { // do nothing } @@ -3615,9 +4481,7 @@ struct AnalysisDileptonTrack { Configurable fConfigHistogramSubgroups{"cfgDileptonTrackHistogramsSubgroups", "invmass,vertexing", "Comma separated list of dilepton-track histogram subgroups"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; @@ -3638,13 +4502,13 @@ struct AnalysisDileptonTrack { Configurable fConfigUseCorrectlyAssociatedMC{"cfgUseCorrectlyAssociatedMC", true, "Use only correctly associated track to construct triplet"}; Configurable fConfigMixingDepth{"cfgMixingDepth", 5, "Event mixing pool depth"}; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. - int fNCuts; - int fNLegCuts; - int fNPairCuts; - int fNCommonTrackCuts; + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. + int fNCuts = 0; + int fNLegCuts = 0; + int fNPairCuts = 0; + int fNCommonTrackCuts = 0; std::map fCommonTrackCutMap; - uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons + uint32_t fTrackCutBitMap = 0; // track cut bit mask to be used in the selection of tracks associated with dileptons // vector for single-lepton and track cut names for easy access when calling FillHistogramList() std::vector fTrackCutNames; std::vector fLegCutNames; @@ -3652,7 +4516,7 @@ struct AnalysisDileptonTrack { std::vector fPairCutNames; std::vector fCommonPairCutNames; - Service fCCDB; + Service fCCDB{}; // TODO: The filter expressions seem to always use the default value of configurables, not the values from the actual configuration file Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); @@ -3663,9 +4527,9 @@ struct AnalysisDileptonTrack { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use two values array to avoid mixing up the quantities - float* fValuesDilepton; - float* fValuesHadron; - HistogramManager* fHistMan; + float* fValuesDilepton = nullptr; + float* fValuesHadron = nullptr; + HistogramManager* fHistMan = nullptr; std::vector fRecMCSignals; std::vector fGenMCSignals; @@ -3684,9 +4548,9 @@ struct AnalysisDileptonTrack { if (isDummy) { if (isBarrel || isMuon || isBarrelAsymmetric || isMCGen) { - LOG(fatal) << "Dummy function is enabled even if there are normal process functions running! Fix your config!" << endl; + LOG(fatal) << "Dummy function is enabled even if there are normal process functions running! Fix your config!"; } else { - LOG(info) << "Dummy function is enabled. Skipping the rest of the init function" << endl; + LOG(info) << "Dummy function is enabled. Skipping the rest of the init function"; return; } } @@ -3707,7 +4571,7 @@ struct AnalysisDileptonTrack { VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqefficiency_helpers::varNames(), dqefficiency_helpers::varUnits()); TString sigNamesStr = fConfigMCRecSignals.value; std::unique_ptr objRecSigArray(sigNamesStr.Tokenize(",")); @@ -3729,7 +4593,7 @@ struct AnalysisDileptonTrack { TString addMCSignalsStr = fConfigMCRecSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 3) { LOG(fatal) << "Signal at reconstructed level requested (" << mcIt->GetName() << ") " << "does not have 3 prongs! Fix it"; } @@ -3754,7 +4618,7 @@ struct AnalysisDileptonTrack { addMCSignalsStr = fConfigMCGenSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (auto const& mcIt : addMCSignals) { if (mcIt->GetNProngs() == 1) { fGenMCSignals.push_back(mcIt); } @@ -3767,11 +4631,11 @@ struct AnalysisDileptonTrack { // Get the list of single track and muon cuts computed in the dedicated tasks upstream // We need this to know the order in which they were computed, and also to make sure that in this task we do not ask // for cuts which were not computed (in which case this will trigger a fatal) - string cfgTrackSelection_TrackCuts; + std::string cfgTrackSelection_TrackCuts; if (isBarrel || isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); } else { - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", cfgTrackSelection_TrackCuts, false); + getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", cfgTrackSelection_TrackCuts, false); } TObjArray* cfgTrackSelection_objArrayTrackCuts = nullptr; if (!cfgTrackSelection_TrackCuts.empty()) { @@ -3779,17 +4643,17 @@ struct AnalysisDileptonTrack { } // get also the list of cuts specified via the JSON parameters if (isBarrel || isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); } else { - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", cfgTrackSelection_TrackCuts, false); + getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", cfgTrackSelection_TrackCuts, false); } if (!cfgTrackSelection_TrackCuts.empty()) { if (cfgTrackSelection_objArrayTrackCuts == nullptr) { cfgTrackSelection_objArrayTrackCuts = new TObjArray(); } std::vector addTrackCuts = dqcuts::GetCutsFromJSON(cfgTrackSelection_TrackCuts.data()); - for (auto& t : addTrackCuts) { - TObjString* tempObjStr = new TObjString(t->GetName()); + for (auto const& t : addTrackCuts) { + auto tempObjStr = new TObjString(t->GetName()); cfgTrackSelection_objArrayTrackCuts->Add(tempObjStr); } } @@ -3827,20 +4691,20 @@ struct AnalysisDileptonTrack { // NOTE: The track/muon cuts in analysis-same-event-pairing are used to select electrons/muons to build dielectrons/dimuons // NOTE: The cfgPairCuts in analysis-same-event-pairing are used to apply an additional selection on top of the already produced dileptons // but this is only used for histograms, not for the produced dilepton tables - string cfgPairing_TrackCuts; - string cfgPairing_PairCuts; - string cfgPairing_PairCutsJSON; - string cfgPairing_CommonTrackCuts; + std::string cfgPairing_TrackCuts; + std::string cfgPairing_PairCuts; + std::string cfgPairing_PairCutsJSON; + std::string cfgPairing_CommonTrackCuts; if (isBarrel) { - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgTrackCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + getTaskOptionValue(context, "analysis-same-event-pairing", "cfgTrackCuts", cfgPairing_TrackCuts, false); + getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); } else if (isMuon) { - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgMuonCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + getTaskOptionValue(context, "analysis-same-event-pairing", "cfgMuonCuts", cfgPairing_TrackCuts, false); + getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); } else if (isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgLegCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgCommonTrackCuts", cfgPairing_CommonTrackCuts, false); + getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgLegCuts", cfgPairing_TrackCuts, false); + getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgCommonTrackCuts", cfgPairing_CommonTrackCuts, false); } if (cfgPairing_TrackCuts.empty()) { LOG(fatal) << "There are no dilepton cuts specified in the upstream in the same-event-pairing or asymmetric-pairing"; @@ -3863,11 +4727,11 @@ struct AnalysisDileptonTrack { // Get also the pair cuts specified via the JSON parameters if (isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCutsJSON", cfgPairing_PairCutsJSON, false); + getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCutsJSON", cfgPairing_PairCutsJSON, false); TString addPairCutsStr = cfgPairing_PairCutsJSON; if (addPairCutsStr != "") { std::vector addPairCuts = dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); - for (auto& t : addPairCuts) { + for (auto const& t : addPairCuts) { cfgPairing_PairCuts += Form(",%s", t->GetName()); } } @@ -3902,7 +4766,7 @@ struct AnalysisDileptonTrack { pairLegCutName = fTrackCutNames[icut].Data(); } else { // For asymmetric pairs we access the leg cuts instead - pairLegCutName = static_cast(cfgPairing_objArrayTrackCuts->At(icut))->GetString(); + pairLegCutName = dynamic_cast(cfgPairing_objArrayTrackCuts->At(icut))->GetString(); } fLegCutNames.push_back(pairLegCutName); @@ -3912,12 +4776,12 @@ struct AnalysisDileptonTrack { for (int iCutTrack = 0; iCutTrack < fNCuts; iCutTrack++) { // here we check that this track cut is one of those required to associate with the dileptons - if (!(fTrackCutBitMap & (static_cast(1) << iCutTrack))) { + if ((fTrackCutBitMap & (static_cast(1) << iCutTrack)) == 0) { continue; } DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s", pairLegCutName.Data(), fTrackCutNames[iCutTrack].Data()), fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (auto const& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrackMCMatched_%s_%s_%s", pairLegCutName.Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigHistogramSubgroups.value.data()); } @@ -3926,7 +4790,7 @@ struct AnalysisDileptonTrack { for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { DefineHistograms(fHistMan, Form("DileptonsSelected_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data()), "barrel,vertexing"); DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fTrackCutNames[iCutTrack].Data()), fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (auto const& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrackMCMatched_%s_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigHistogramSubgroups.value.data()); } } @@ -3937,7 +4801,7 @@ struct AnalysisDileptonTrack { for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { DefineHistograms(fHistMan, Form("DileptonsSelected_%s_%s", pairLegCutName.Data(), fPairCutNames[iPairCut].Data()), "barrel,vertexing"); DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s", pairLegCutName.Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data()), fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (auto const& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrackMCMatched_%s_%s_%s_%s", pairLegCutName.Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigHistogramSubgroups.value.data()); } @@ -3946,7 +4810,7 @@ struct AnalysisDileptonTrack { for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { DefineHistograms(fHistMan, Form("DileptonsSelected_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), "barrel,vertexing"); DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data()), fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (auto const& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigHistogramSubgroups.value.data()); } } @@ -3958,24 +4822,24 @@ struct AnalysisDileptonTrack { } // end if (isBarrel || isBarrelAsymmetric || isMuon) if (isMCGen) { - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { DefineHistograms(fHistMan, Form("MCTruthGen_%s", sig->GetName()), ""); DefineHistograms(fHistMan, Form("MCTruthGenSel_%s", sig->GetName()), ""); } - for (auto& sig : fRecMCSignals) { + for (auto const& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("MCTruthGenSelBR_%s", sig->GetName()), ""); DefineHistograms(fHistMan, Form("MCTruthGenSelBRAccepted_%s", sig->GetName()), ""); } } if (isMCGen_energycorrelators) { - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { DefineHistograms(fHistMan, Form("MCTruthEenergyCorrelators_%s", sig->GetName()), ""); } } if (isMCGen_energycorrelatorsME) { - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { DefineHistograms(fHistMan, Form("MCTruthEenergyCorrelatorsME_%s", sig->GetName()), ""); } } @@ -3991,25 +4855,17 @@ struct AnalysisDileptonTrack { // init parameters from CCDB void initParamsFromCCDB(uint64_t timestamp) { - if (fConfigUseRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); + } else { + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables } } @@ -4024,13 +4880,13 @@ struct AnalysisDileptonTrack { VarManager::FillEvent(event, fValuesDilepton); VarManager::FillEvent(event.reducedMCevent(), fValuesDilepton); - uint32_t mcDecision = static_cast(0); - size_t isig = 0; + auto mcDecision = static_cast(0); + size_t iSigMc = 0; bool isCorrectAssoc_lepton1 = false; bool isCorrectAssoc_lepton2 = false; bool isCorrectAssoc_assoc = false; - for (auto dilepton : dileptons) { + for (auto const& dilepton : dileptons) { // get full track info of tracks based on the index auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); auto lepton2 = tracks.rawIteratorAt(dilepton.index1Id()); @@ -4045,8 +4901,9 @@ struct AnalysisDileptonTrack { } // dilepton rap cut float rap = dilepton.rap(); - if (fConfigUseMCRapcut && abs(rap) > fConfigDileptonRapCutAbs) + if (fConfigUseMCRapcut && std::abs(rap) > fConfigDileptonRapCutAbs) { continue; + } // Correct association if (fConfigUseCorrectlyAssociatedMC) { @@ -4087,7 +4944,7 @@ struct AnalysisDileptonTrack { } // loop over track associations - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { VarManager::ResetValues(0, VarManager::kNVars, fValuesHadron); VarManager::ResetValues(0, VarManager::kNVars, fValuesDilepton); @@ -4121,10 +4978,10 @@ struct AnalysisDileptonTrack { VarManager::FillDileptonTrackVertexing(event, lepton1, lepton2, track, fValuesHadron); mcDecision = 0; - isig = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { + iSigMc = 0; + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { if ((*sig)->CheckSignal(true, lepton1MC, lepton2MC, trackMC)) { - mcDecision |= (static_cast(1) << isig); + mcDecision |= (static_cast(1) << iSigMc); } } @@ -4185,10 +5042,10 @@ struct AnalysisDileptonTrack { auto trackMC = track.reducedMCTrack(); mcDecision = 0; - isig = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { + iSigMc = 0; + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { if ((*sig)->CheckSignal(true, lepton1MC, lepton2MC, trackMC)) { - mcDecision |= (static_cast(1) << isig); + mcDecision |= (static_cast(1) << iSigMc); } } } @@ -4212,10 +5069,10 @@ struct AnalysisDileptonTrack { } auto trackMC = track.reducedMCTrack(); mcDecision = 0; - isig = 0; - for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, isig++) { + iSigMc = 0; + for (auto sig = fRecMCSignals.begin(); sig != fRecMCSignals.end(); sig++, iSigMc++) { if ((*sig)->CheckSignal(true, lepton1MC, lepton2MC, trackMC)) { - mcDecision |= (static_cast(1) << isig); + mcDecision |= (static_cast(1) << iSigMc); } } // Fill table for correlation analysis @@ -4302,7 +5159,7 @@ struct AnalysisDileptonTrack { initParamsFromCCDB(events.begin().timestamp()); fCurrentRun = events.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -4325,7 +5182,7 @@ struct AnalysisDileptonTrack { initParamsFromCCDB(events.begin().timestamp()); fCurrentRun = events.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (auto const& event : events) { auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); auto groupedDitracks = ditracks.sliceBy(ditracksPerCollision, event.globalIndex()); runDileptonHadron(event, groupedBarrelAssocs, tracks, groupedDitracks, mcEvents, mcTracks); @@ -4348,7 +5205,7 @@ struct AnalysisDileptonTrack { initParamsFromCCDB(events.begin().timestamp()); fCurrentRun = events.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -4363,40 +5220,40 @@ struct AnalysisDileptonTrack { // loop over mc stack and fill histograms for pure MC truth signals // group all the MC tracks which belong to the MC event corresponding to the current reconstructed event // auto groupedMCTracks = tracksMC.sliceBy(aod::reducedtrackMC::reducedMCeventId, event.reducedMCevent().globalIndex()); - for (auto& mctrack : mcTracks) { - - if ((std::abs(mctrack.pdgCode()) > 400 && std::abs(mctrack.pdgCode()) < 599) || - (std::abs(mctrack.pdgCode()) > 4000 && std::abs(mctrack.pdgCode()) < 5999) || - mctrack.mcReducedFlags() > 0) { - /*cout << ">>>>>>>>>>>>>>>>>>>>>>> track idx / pdg / selections: " << mctrack.globalIndex() << " / " << mctrack.pdgCode() << " / "; - PrintBitMap(mctrack.mcReducedFlags(), 16); - cout << endl; - if (mctrack.has_mothers()) { - for (auto& m : mctrack.mothersIds()) { - if (m < mcTracks.size()) { // protect against bad mother indices - auto aMother = mcTracks.rawIteratorAt(m); - cout << "<<<<<< mother idx / pdg: " << m << " / " << aMother.pdgCode() << endl; - } - } - } - - if (mctrack.has_daughters()) { - for (int d = mctrack.daughtersIds()[0]; d <= mctrack.daughtersIds()[1]; ++d) { - if (d < mcTracks.size()) { // protect against bad daughter indices - auto aDaughter = mcTracks.rawIteratorAt(d); - cout << "<<<<<< daughter idx / pdg: " << d << " / " << aDaughter.pdgCode() << endl; - } - } - }*/ - } + for (auto const& mctrack : mcTracks) { + + // if ((std::abs(mctrack.pdgCode()) > 400 && std::abs(mctrack.pdgCode()) < 599) || + // (std::abs(mctrack.pdgCode()) > 4000 && std::abs(mctrack.pdgCode()) < 5999) || + // mctrack.mcReducedFlags() > 0) { + // /*cout << ">>>>>>>>>>>>>>>>>>>>>>> track idx / pdg / selections: " << mctrack.globalIndex() << " / " << mctrack.pdgCode() << " / "; + // PrintBitMap(mctrack.mcReducedFlags(), 16); + // cout << endl; + // if (mctrack.has_mothers()) { + // for (auto& m : mctrack.mothersIds()) { + // if (m < mcTracks.size()) { // protect against bad mother indices + // auto aMother = mcTracks.rawIteratorAt(m); + // cout << "<<<<<< mother idx / pdg: " << m << " / " << aMother.pdgCode() << endl; + // } + // } + // } + // + // if (mctrack.has_daughters()) { + // for (int d = mctrack.daughtersIds()[0]; d <= mctrack.daughtersIds()[1]; ++d) { + // if (d < mcTracks.size()) { // protect against bad daughter indices + // auto aDaughter = mcTracks.rawIteratorAt(d); + // cout << "<<<<<< daughter idx / pdg: " << d << " / " << aDaughter.pdgCode() << endl; + // } + // } + // }*/ + // } VarManager::FillTrackMC(mcTracks, mctrack); // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. // NOTE: However, the working model is that the decisions on MC signals are precomputed during skimming and are stored in the mcReducedFlags member. // TODO: Use the mcReducedFlags to select signals - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -4407,7 +5264,7 @@ struct AnalysisDileptonTrack { void processMCGenWithEventSelection(soa::Filtered const& events, ReducedMCEvents const& /*mcEvents*/, ReducedMCTracks const& mcTracks) { - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -4417,45 +5274,48 @@ struct AnalysisDileptonTrack { auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedMCeventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); - for (auto& track : groupedMCTracks) { + for (auto const& track : groupedMCTracks) { VarManager::FillTrackMC(mcTracks, track); auto track_raw = groupedMCTracks.rawIteratorAt(track.globalIndex()); - for (auto& sig : fGenMCSignals) { + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, track_raw)) { - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } // make a list of all MC tracks in the MC collision corresponding to the current reconstructed event std::vector mcTrackIndices; - for (auto& t : groupedMCTracks) { + for (auto const& t : groupedMCTracks) { mcTrackIndices.push_back(t.globalIndex()); } // make a three nested for loop over all MC tracks in the vector - for (auto t1 : mcTrackIndices) { - auto track1 = mcTracks.rawIteratorAt(*(&t1)); - for (auto t2 : mcTrackIndices) { - if (t1 == t2) + for (const auto& t1 : mcTrackIndices) { + auto track1 = mcTracks.rawIteratorAt(t1); + for (const auto& t2 : mcTrackIndices) { + if (t1 == t2) { continue; + } // if (t2 < t1) continue; - auto track2 = mcTracks.rawIteratorAt(*(&t2)); - for (auto t3 : mcTrackIndices) { - if (t3 == t1) + auto track2 = mcTracks.rawIteratorAt(t2); + for (const auto& t3 : mcTrackIndices) { + if (t3 == t1) { continue; - if (t3 == t2) + } + if (t3 == t2) { continue; - auto track3 = mcTracks.rawIteratorAt(*(&t3)); + } + auto track3 = mcTracks.rawIteratorAt(t3); - for (auto& sig : fRecMCSignals) { + for (auto const& sig : fRecMCSignals) { if (sig->CheckSignal(true, track1, track2, track3)) { - VarManager::FillTripleMC(track1, track2, track3, VarManager::fgValues); // nb! hardcoded for jpsiK + VarManager::FillTripleMC(track1, track2, track3, dqefficiency_helpers::varValues()); // nb! hardcoded for jpsiK - fHistMan->FillHistClass(Form("MCTruthGenSelBR_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSelBR_%s", sig->GetName()), dqefficiency_helpers::varValues()); // apply kinematic cuts if (track1.pt() < fConfigMCGenDileptonLegPtMin.value || std::abs(track1.eta()) > fConfigMCGenDileptonLegEtaAbs.value) { @@ -4467,7 +5327,7 @@ struct AnalysisDileptonTrack { if (track3.pt() < fConfigMCGenHadronPtMin.value || std::abs(track3.eta()) > fConfigMCGenHadronEtaAbs.value) { continue; } - fHistMan->FillHistClass(Form("MCTruthGenSelBRAccepted_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSelBRAccepted_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -4481,26 +5341,31 @@ struct AnalysisDileptonTrack { { auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.reducedMCeventId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); - for (auto& t1 : groupedMCTracks) { + for (auto const& t1 : groupedMCTracks) { auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); // apply kinematic cuts for signal - if ((t1_raw.pt() < fConfigDileptonLowpTCut || t1_raw.pt() > fConfigDileptonHighpTCut)) + if ((t1_raw.pt() < fConfigDileptonLowpTCut || t1_raw.pt() > fConfigDileptonHighpTCut)) { continue; - if (abs(t1_raw.y()) > fConfigDileptonRapCutAbs) + } + if (std::abs(t1_raw.y()) > fConfigDileptonRapCutAbs) { continue; + } // for the energy correlators - for (auto& t2 : groupedMCTracks) { + for (auto const& t2 : groupedMCTracks) { auto t2_raw = groupedMCTracks.rawIteratorAt(t2.globalIndex()); - if (TMath::Abs(t2_raw.pdgCode()) == 443 || TMath::Abs(t2_raw.pdgCode()) == 11 || TMath::Abs(t2_raw.pdgCode()) == 22) + if (std::abs(t2_raw.pdgCode()) == o2::constants::physics::Pdg::kJPsi || std::abs(t2_raw.pdgCode()) == PDG_t::kElectron || std::abs(t2_raw.pdgCode()) == PDG_t::kGamma) { continue; - if (t2_raw.pt() < fConfigMCGenHadronPtMin.value || std::abs(t2_raw.eta()) > fConfigMCGenHadronEtaAbs.value) + } + if (t2_raw.pt() < fConfigMCGenHadronPtMin.value || std::abs(t2_raw.eta()) > fConfigMCGenHadronEtaAbs.value) { continue; - if (t2_raw.getGenStatusCode() <= 0) + } + if (t2_raw.getGenStatusCode() <= 0) { continue; - VarManager::FillEnergyCorrelatorsMC(t1_raw, t2_raw, VarManager::fgValues); - for (auto& sig : fGenMCSignals) { + } + VarManager::FillEnergyCorrelatorsMC(t1_raw, t2_raw, dqefficiency_helpers::varValues()); + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, t1_raw)) { - fHistMan->FillHistClass(Form("MCTruthEenergyCorrelators_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthEenergyCorrelators_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -4513,7 +5378,7 @@ struct AnalysisDileptonTrack { LOG(warning) << "No events in this TF, going to the next one ..."; return; } - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -4531,7 +5396,7 @@ struct AnalysisDileptonTrack { LOG(warning) << "No events in this TF, going to the next one ..."; return; } - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -4549,26 +5414,31 @@ struct AnalysisDileptonTrack { auto groupedMCTracks2 = mcTracks.sliceBy(perReducedMcEvent, event2.reducedMCeventId()); groupedMCTracks1.bindInternalIndicesTo(&mcTracks); groupedMCTracks2.bindInternalIndicesTo(&mcTracks); - for (auto& t1 : groupedMCTracks1) { + for (auto const& t1 : groupedMCTracks1) { auto t1_raw = mcTracks.rawIteratorAt(t1.globalIndex()); // apply kinematic cuts for signal - if ((t1_raw.pt() < fConfigDileptonLowpTCut || t1_raw.pt() > fConfigDileptonHighpTCut)) + if ((t1_raw.pt() < fConfigDileptonLowpTCut || t1_raw.pt() > fConfigDileptonHighpTCut)) { continue; - if (abs(t1_raw.y()) > fConfigDileptonRapCutAbs) + } + if (std::abs(t1_raw.y()) > fConfigDileptonRapCutAbs) { continue; + } // for the energy correlators - for (auto& t2 : groupedMCTracks2) { + for (auto const& t2 : groupedMCTracks2) { auto t2_raw = groupedMCTracks2.rawIteratorAt(t2.globalIndex()); - if (TMath::Abs(t2_raw.pdgCode()) == 443 || TMath::Abs(t2_raw.pdgCode()) == 11 || TMath::Abs(t2_raw.pdgCode()) == 22) + if (std::abs(t2_raw.pdgCode()) == o2::constants::physics::Pdg::kJPsi || std::abs(t2_raw.pdgCode()) == PDG_t::kElectron || std::abs(t2_raw.pdgCode()) == PDG_t::kGamma) { continue; - if (t2_raw.pt() < fConfigMCGenHadronPtMin.value || std::abs(t2_raw.eta()) > fConfigMCGenHadronEtaAbs.value) + } + if (t2_raw.pt() < fConfigMCGenHadronPtMin.value || std::abs(t2_raw.eta()) > fConfigMCGenHadronEtaAbs.value) { continue; - if (t2_raw.getGenStatusCode() <= 0) + } + if (t2_raw.getGenStatusCode() <= 0) { continue; - VarManager::FillEnergyCorrelatorsMC(t1_raw, t2_raw, VarManager::fgValues); - for (auto& sig : fGenMCSignals) { + } + VarManager::FillEnergyCorrelatorsMC(t1_raw, t2_raw, dqefficiency_helpers::varValues()); + for (auto const& sig : fGenMCSignals) { if (sig->CheckSignal(true, t1_raw)) { - fHistMan->FillHistClass(Form("MCTruthEenergyCorrelatorsME_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthEenergyCorrelatorsME_%s", sig->GetName()), dqefficiency_helpers::varValues()); } } } @@ -4583,7 +5453,7 @@ struct AnalysisDileptonTrack { return; } // loop over two event comibnations - for (auto& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { + for (auto const& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { if (!event1.isEventSelected_bit(0) || !event2.isEventSelected_bit(0)) { continue; } @@ -4602,7 +5472,7 @@ struct AnalysisDileptonTrack { return; } // loop over two event comibnations - for (auto& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { + for (auto const& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { if (!event1.isEventSelected_bit(0) || !event2.isEventSelected_bit(0)) { continue; } @@ -4613,7 +5483,7 @@ struct AnalysisDileptonTrack { } } - void processDummy(MyEvents&) + void processDummy(MyEvents const&) { // do nothing } @@ -4637,12 +5507,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx index 0b704a56616..82d99d1ed75 100644 --- a/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/dqEfficiency_withAssoc_direct.cxx @@ -25,6 +25,7 @@ #include "Common/Core/PID/PIDTOFParamService.h" #include "Common/Core/TableHelper.h" #include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" @@ -53,18 +54,18 @@ #include #include -#include #include #include #include +#include #include +#include #include #include #include #include -#include #include #include #include @@ -72,8 +73,6 @@ #include #include -using std::cout; -using std::endl; using std::string; using namespace o2; @@ -179,6 +178,13 @@ DECLARE_SOA_COLUMN(Lxyee, lxyee, float); DECLARE_SOA_COLUMN(LxyeePoleMass, lxyeepolemass, float); DECLARE_SOA_COLUMN(Lzee, lzee, float); DECLARE_SOA_COLUMN(LxyeePoleMassPVrecomputed, lxyeePoleMassPVrecomputed, float); +DECLARE_SOA_COLUMN(Vx, vx, float); +DECLARE_SOA_COLUMN(Vy, vy, float); +DECLARE_SOA_COLUMN(Vz, vz, float); +DECLARE_SOA_COLUMN(DcaXY1, dcaXY1, float); +DECLARE_SOA_COLUMN(DcaZ1, dcaZ1, float); +DECLARE_SOA_COLUMN(DcaXY2, dcaXY2, float); +DECLARE_SOA_COLUMN(DcaZ2, dcaZ2, float); DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); @@ -199,7 +205,11 @@ DECLARE_SOA_TABLE(EventCuts, "AOD", "DQANAEVCUTS", dqanalysisflags::IsEventSelec DECLARE_SOA_TABLE(BarrelTrackCuts, "AOD", "DQANATRKCUTS", dqanalysisflags::IsBarrelSelected); //! joinable to ReducedTracksAssoc DECLARE_SOA_TABLE(BarrelAmbiguities, "AOD", "DQBARRELAMB", dqanalysisflags::BarrelAmbiguityInBunch, dqanalysisflags::BarrelAmbiguityOutOfBunch); //! joinable to ReducedBarrelTracks DECLARE_SOA_TABLE(Prefilter, "AOD", "DQPREFILTER", dqanalysisflags::IsBarrelSelectedPrefilter); //! joinable to ReducedTracksAssoc -DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); +DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", + dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, + dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::TPCnsigmaElleg1, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::TPCnsigmaElleg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); DECLARE_SOA_TABLE(OniaMCTruth, "AOD", "MCTRUTHONIA", dqanalysisflags::OniaPt, dqanalysisflags::OniaEta, dqanalysisflags::OniaY, dqanalysisflags::OniaPhi, dqanalysisflags::OniaVz, dqanalysisflags::OniaVtxZ, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); /*DECLARE_SOA_TABLE(MuonTrackCuts, "AOD", "DQANAMUONCUTS", dqanalysisflags::IsMuonSelected); //! joinable to ReducedMuonsAssoc @@ -254,6 +264,8 @@ using MyMuonTracksWithCovWithAmbiguities = soa::Join; using MyEvents = soa::Join; using MyEventsSelected = soa::Join; +using MyEventsWithCentAndMults = soa::Join; +using MyEventsSelectedWithCentAndMults = soa::Join; using MyBarrelTracksWithCov = soa::Join; +using MyBarrelTracksWithDalitzBits = soa::Join; using MyBarrelTracksWithCovWithAmbiguities = soa::Join void PrintBitMap(TMap map, int nbits) { + std::string msg; for (int i = 0; i < nbits; i++) { - cout << ((map & (TMap(1) << i)) > 0 ? "1" : "0"); + msg += ((map & (TMap(1) << i)) > 0 ? "1" : "0"); } + LOG(info) << msg; } // Analysis task that produces event decisions and the Hash table used in event mixing @@ -315,18 +335,18 @@ struct AnalysisEventSelection { HistogramManager* fHistMan = nullptr; - AnalysisCompositeCut* fEventCut; + AnalysisCompositeCut* fEventCut = nullptr; - Service fCCDB; + Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; std::map fSelMap; // key: reduced event global index, value: event selection decision std::map> fBCCollMap; // key: global BC, value: vector of reduced event global indices - int fCurrentRun; + int fCurrentRun = 0; void init(o2::framework::InitContext& context) { - cout << "AnalysisEventSelection::init() called" << endl; + LOG(info) << "AnalysisEventSelection::init() called"; if (context.mOptions.get("processDummy")) { return; } @@ -344,7 +364,7 @@ struct AnalysisEventSelection { TString eventCutJSONStr = fConfigEventCutsJSON.value; if (eventCutJSONStr != "") { std::vector jsonCuts = dqcuts::GetCutsFromJSON(eventCutJSONStr.Data()); - for (auto& cutIt : jsonCuts) { + for (const auto& cutIt : jsonCuts) { fEventCut->AddCut(cutIt); } } @@ -354,7 +374,7 @@ struct AnalysisEventSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); DefineHistograms(fHistMan, "TimeFrameStats;Event_BeforeCuts;Event_AfterCuts;", fConfigAddEventHistogram.value.data()); if (fConfigCheckSplitCollisions) { DefineHistograms(fHistMan, "OutOfBunchCorrelations;SameBunchCorrelations;", ""); @@ -371,13 +391,13 @@ struct AnalysisEventSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); fCCDBApi.init(fConfigCcdbUrl.value); - cout << "AnalysisEventSelection::init() completed" << endl; + LOG(info) << "AnalysisEventSelection::init() completed"; } template void runEventSelection(TEvents const& events, BCsWithTimestamps const& bcs, TEventsMC const& mcEvents) { - cout << "AnalysisEventSelection::runEventSelection() called with " << events.size() << " events and " << bcs.size() << " BCs" << endl; + LOG(info) << "AnalysisEventSelection::runEventSelection() called with " << events.size() << " events and " << bcs.size() << " BCs"; if (bcs.size() > 0 && bcs.begin().runNumber() != fCurrentRun) { std::map metadataRCT, header; header = fCCDBApi.retrieveHeaders(Form("RCT/Info/RunInformation/%i", bcs.begin().runNumber()), metadataRCT, -1); @@ -386,21 +406,21 @@ struct AnalysisEventSelection { VarManager::SetSORandEOR(sor, eor); } - cout << "Filling TimeFrame statistics histograms" << endl; + // cout << "Filling TimeFrame statistics histograms" << endl; VarManager::ResetValues(0, VarManager::kNEventWiseVariables); VarManager::FillTimeFrame(bcs); VarManager::FillTimeFrame(events); VarManager::FillTimeFrame(mcEvents); if (fConfigQA) { - fHistMan->FillHistClass("TimeFrameStats", VarManager::fgValues); + fHistMan->FillHistClass("TimeFrameStats", static_cast(VarManager::fgValues)); } fSelMap.clear(); fBCCollMap.clear(); // int iEvent = 0; - cout << "Starting event loop for event selection" << endl; - for (auto& event : events) { + // cout << "Starting event loop for event selection" << endl; + for (const auto& event : events) { auto bc = event.template bc_as(); // check if there is a mismatch between the collision associated BC and the recomputed one in event selection @@ -420,11 +440,11 @@ struct AnalysisEventSelection { bool decision = false; // if QA is requested fill histograms before event selections if (fConfigQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); // automatically fill all the histograms in the class Event + fHistMan->FillHistClass("Event_BeforeCuts", static_cast(VarManager::fgValues)); // automatically fill all the histograms in the class Event } - if (fEventCut->IsSelected(VarManager::fgValues)) { + if (fEventCut->IsSelected(static_cast(VarManager::fgValues))) { if (fConfigQA) { - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", static_cast(VarManager::fgValues)); } decision = true; } @@ -438,22 +458,22 @@ struct AnalysisEventSelection { } } - for (auto& event : mcEvents) { + for (const auto& event : mcEvents) { // Reset the fValues array and fill event observables VarManager::ResetValues(0, VarManager::kNEventWiseVariables); VarManager::FillEvent(event); if (fConfigQA) { - fHistMan->FillHistClass("EventsMC", VarManager::fgValues); + fHistMan->FillHistClass("EventsMC", static_cast(VarManager::fgValues)); } } - cout << "AnalysisEventSelection::runEventSelection() completed" << endl; + // cout << "AnalysisEventSelection::runEventSelection() completed" << endl; } template void publishSelections(TEvents const& events) { - cout << "AnalysisEventSelection::publishSelections() called" << endl; + // cout << "AnalysisEventSelection::publishSelections() called" << endl; std::map collisionSplittingMap; // key: event global index, value: whether pileup event is a possible splitting // Reset the fValues array and fill event observables @@ -471,12 +491,12 @@ struct AnalysisEventSelection { auto ev2 = events.rawIteratorAt(*ev2It); // compute 2-event quantities and mark the candidate split collisions VarManager::FillTwoEvents(ev1, ev2); - if (TMath::Abs(VarManager::fgValues[VarManager::kTwoEvDeltaZ]) < fConfigSplitCollisionsDeltaZ) { // this is a possible collision split + if (std::fabs(VarManager::fgValues[VarManager::kTwoEvDeltaZ]) < fConfigSplitCollisionsDeltaZ) { // this is a possible collision split collisionSplittingMap[*ev1It] = true; collisionSplittingMap[*ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("SameBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("SameBunchCorrelations", static_cast(VarManager::fgValues)); } } // end second event loop } // end first event loop @@ -491,19 +511,19 @@ struct AnalysisEventSelection { auto const& bc2Events = bc2It->second; // loop over events in the first BC - for (auto ev1It : bc1Events) { + for (const auto ev1It : bc1Events) { auto ev1 = events.rawIteratorAt(ev1It); // loop over events in the second BC - for (auto ev2It : bc2Events) { + for (const auto ev2It : bc2Events) { auto ev2 = events.rawIteratorAt(ev2It); // compute 2-event quantities and mark the candidate split collisions VarManager::FillTwoEvents(ev1, ev2); - if (TMath::Abs(VarManager::fgValues[VarManager::kTwoEvDeltaZ]) < fConfigSplitCollisionsDeltaZ) { // this is a possible collision split + if (std::fabs(VarManager::fgValues[VarManager::kTwoEvDeltaZ]) < fConfigSplitCollisionsDeltaZ) { // this is a possible collision split collisionSplittingMap[ev1It] = true; collisionSplittingMap[ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("OutOfBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("OutOfBunchCorrelations", static_cast(VarManager::fgValues)); } } } @@ -511,8 +531,8 @@ struct AnalysisEventSelection { } // publish the table - uint32_t evSel = static_cast(0); - for (auto& event : events) { + auto evSel = static_cast(0); + for (const auto& event : events) { evSel = 0; if (fSelMap[event.globalIndex()]) { // event passed the user cuts evSel |= (static_cast(1) << 0); @@ -527,23 +547,32 @@ struct AnalysisEventSelection { } eventSel(evSel); } - cout << "AnalysisEventSelection::publishSelections() completed" << endl; + // cout << "AnalysisEventSelection::publishSelections() completed" << endl; } void processDirect(MyEvents const& events, BCsWithTimestamps const& bcs, soa::Join const& mcEvents) { - cout << "AnalysisEventSelection::processDirect() called" << endl; + // cout << "AnalysisEventSelection::processDirect() called" << endl; runEventSelection(events, bcs, mcEvents); publishSelections(events); - cout << "AnalysisEventSelection::processDirect() completed" << endl; + // cout << "AnalysisEventSelection::processDirect() completed" << endl; } - void processDummy(aod::Collisions&) + void processPbPbDirect(MyEventsWithCentAndMults const& events, BCsWithTimestamps const& bcs, soa::Join const& mcEvents) + { + // cout << "AnalysisEventSelection::processPbPbDirect() called" << endl; + runEventSelection(events, bcs, mcEvents); + publishSelections(events); + // cout << "AnalysisEventSelection::processPbPbDirect() completed" << endl; + } + + void processDummy(const aod::Collisions&) { // do nothing } PROCESS_SWITCH(AnalysisEventSelection, processDirect, "Run event selection on framework AO2Ds", false); + PROCESS_SWITCH(AnalysisEventSelection, processPbPbDirect, "Run PbPb event selection on framework AO2Ds", false); PROCESS_SWITCH(AnalysisEventSelection, processDummy, "Dummy function", true); }; @@ -567,23 +596,23 @@ struct AnalysisTrackSelection { Configurable fConfigMCSignals{"cfgTrackMCSignals", "", "Comma separated list of MC signals"}; Configurable fConfigMCSignalsJSON{"cfgTrackMCsignalsJSON", "", "Additional list of MC signals via JSON"}; - Service fCCDB; - Service fTofResponse; + Service fCCDB{}; + Service fTofResponse{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fMCSignals; // list of signals to be checked std::vector fHistNamesReco; std::vector fHistNamesMCMatched; - int fCurrentRun; // current run (needed to detect run changes for loading CCDB parameters) + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) std::map> fNAssocsInBunch; // key: track global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: track global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) void init(o2::framework::InitContext& context) { - cout << "AnalysisTrackSelection::init() called" << endl; + LOG(info) << "AnalysisTrackSelection::init() called"; if (context.mOptions.get("processDummy")) { return; } @@ -601,8 +630,8 @@ struct AnalysisTrackSelection { TString addTrackCutsStr = fConfigCutsJSON.value; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + for (const auto& t : addTrackCuts) { + fTrackCuts.push_back(dynamic_cast(t)); } } VarManager::SetUseVars(AnalysisCut::fgUsedVars); // provide the list of required variables so that VarManager knows what to fill @@ -623,7 +652,7 @@ struct AnalysisTrackSelection { TString addMCSignalsStr = fConfigMCSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (const auto& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 1) { // NOTE: only 1 prong signals continue; } @@ -634,16 +663,16 @@ struct AnalysisTrackSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); // Configure histogram classes for each track cut; // Add histogram classes for each track cut and for each requested MC signal (reconstructed tracks with MC truth) TString histClasses = "TimeFrameStats;AssocsBarrel_BeforeCuts;"; - for (auto& cut : fTrackCuts) { + for (const auto& cut : fTrackCuts) { TString nameStr = Form("AssocsBarrel_%s", cut->GetName()); fHistNamesReco.push_back(nameStr); histClasses += Form("%s;", nameStr.Data()); - for (auto& sig : fMCSignals) { + for (const auto& sig : fMCSignals) { TString nameStr2 = Form("AssocsCorrectBarrel_%s_%s", cut->GetName(), sig->GetName()); fHistNamesMCMatched.push_back(nameStr2); histClasses += Form("%s;", nameStr2.Data()); @@ -667,14 +696,17 @@ struct AnalysisTrackSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); - fTofResponse->initSetup(fCCDB, context); - cout << "AnalysisTrackSelection::init() completed" << endl; + if (!context.mOptions.get("processWithDalitzBits")) { + fTofResponse->initSetup(fCCDB, context); + } + + LOG(info) << "AnalysisTrackSelection::init() completed"; } template void runTrackSelection(TrackAssoc const& assocs, BCsWithTimestamps const& bcs, TEvents const& events, TTracks const& tracks, McCollisions const& /*eventsMC*/, McParticles const& tracksMC) { - cout << "AnalysisTrackSelection::runTrackSelection() called with " << events.size() << " events, " << tracks.size() << " tracks and " << assocs.size() << " associations" << endl; + LOG(info) << "AnalysisTrackSelection::runTrackSelection() called with " << events.size() << " events, " << tracks.size() << " tracks and " << assocs.size() << " associations"; // determine if TEvents table contains aod::Collisions // bool hasCollisions = std::is_same::value; @@ -685,11 +717,11 @@ struct AnalysisTrackSelection { VarManager::FillTimeFrame(events); VarManager::FillTimeFrame(tracks); if (fConfigQA) { - fHistMan->FillHistClass("TimeFrameStats", VarManager::fgValues); + fHistMan->FillHistClass("TimeFrameStats", static_cast(VarManager::fgValues)); } - cout << "After filling TimeFrame statistics" << endl; - // TODO: Check if postcalibration needed for MC + // cout << "After filling TimeFrame statistics" << endl; + // TODO: Check if postcalibration needed for MC if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { if (fConfigComputeTPCpostCalib) { auto calibList = fCCDB->getForTimeStamp(fConfigCcdbPathTPC.value, bcs.begin().timestamp()); @@ -701,7 +733,7 @@ struct AnalysisTrackSelection { VarManager::SetCalibrationObject(VarManager::kTPCProtonSigma, calibList->FindObject("sigma_map_proton")); } - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(grpmagPath, bcs.begin().timestamp()); + auto grpmag = fCCDB->getForTimeStamp(grpmagPath, bcs.begin().timestamp()); if (grpmag != nullptr) { VarManager::SetMagneticField(grpmag->getNominalL3Field()); } else { @@ -711,13 +743,13 @@ struct AnalysisTrackSelection { fCurrentRun = bcs.begin().runNumber(); } - cout << "Starting loop over track associations" << endl; + // cout << "Starting loop over track associations" << endl; trackSel.reserve(assocs.size()); trackAmbiguities.reserve(tracks.size()); // Loop over associations - for (auto& assoc : assocs) { + for (const auto& assoc : assocs) { auto event = assoc.template collision_as(); if (!event.isEventSelected_bit(0)) { trackSel(0); @@ -742,9 +774,10 @@ struct AnalysisTrackSelection { VarManager::FillTrack(track); // compute quantities which depend on the associated collision, such as DCA - if (track.collisionId() != event.globalIndex()) + if (track.collisionId() != event.globalIndex()) { VarManager::FillTrackCollision(track, event); - // cout << "Filled track observables for association" << endl; + // cout << "Filled track observables for association" << endl; + } bool isCorrectAssoc = false; if (track.has_mcParticle()) { @@ -758,17 +791,17 @@ struct AnalysisTrackSelection { // cout << "Filled MC observables for association" << endl; if (fConfigQA) { - fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", static_cast(VarManager::fgValues)); } // cout << "Filled AssocsBarrel_BeforeCuts histograms" << endl; int iCut = 0; - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(static_cast(VarManager::fgValues))) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(fHistNamesReco[iCut], VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesReco[iCut], static_cast(VarManager::fgValues)); } } } // end loop over cuts @@ -793,11 +826,11 @@ struct AnalysisTrackSelection { // cout << " Cut matched, filling histograms" << endl; if (isCorrectAssoc) { // cout << " Correct association" << endl; - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data(), static_cast(VarManager::fgValues)); // cout << " Filled histogram dir: " << fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig].Data() << endl; } else { // cout << " Incorrect association" << endl; - fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data(), static_cast(VarManager::fgValues)); // cout << " Filled histogram dir: " << fHistNamesMCMatched[icut * 2 * fMCSignals.size() + 2 * isig + 1].Data() << endl; } } @@ -837,7 +870,7 @@ struct AnalysisTrackSelection { // So one could QA these tracks separately if (fConfigPublishAmbiguity) { if (fConfigQA) { - for (auto& [trackIdx, evIndices] : fNAssocsInBunch) { + for (const auto& [trackIdx, evIndices] : fNAssocsInBunch) { if (evIndices.size() == 1) { continue; } @@ -845,10 +878,10 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", static_cast(VarManager::fgValues)); } // end loop over in-bunch ambiguous tracks - for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { + for (const auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { if (evIndices.size() == 1) { continue; } @@ -856,12 +889,12 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", static_cast(VarManager::fgValues)); } // end loop over out-of-bunch ambiguous tracks } // publish the ambiguity table - for (auto& track : tracks) { + for (const auto& track : tracks) { int8_t nInBunch = 0; if (fNAssocsInBunch.find(track.globalIndex()) != fNAssocsInBunch.end()) { nInBunch = fNAssocsInBunch[track.globalIndex()].size(); @@ -873,32 +906,38 @@ struct AnalysisTrackSelection { trackAmbiguities(nInBunch, nOutOfBunch); } } - cout << "AnalysisTrackSelection::runTrackSelection() completed" << endl; + // cout << "AnalysisTrackSelection::runTrackSelection() completed" << endl; } // end runTrackSelection() void processWithCov(TrackAssoc const& assocs, BCsWithTimestamps const& bcs, MyEventsSelected const& events, MyBarrelTracksWithCov const& tracks, McCollisions const& eventsMC, McParticles const& tracksMC) { - cout << "AnalysisTrackSelection::processWithCov() called" << endl; + // cout << "AnalysisTrackSelection::processWithCov() called" << endl; runTrackSelection(assocs, bcs, events, tracks, eventsMC, tracksMC); - cout << "AnalysisTrackSelection::processWithCov() completed" << endl; + // cout << "AnalysisTrackSelection::processWithCov() completed" << endl; } void processWithCovTOFService(TrackAssoc const& assocs, BCsWithTimestamps const& bcs, MyEventsSelected const& events, MyBarrelTracksWithCovNoTOF const& tracks, McCollisions const& eventsMC, McParticles const& tracksMC) { - cout << "AnalysisTrackSelection::processWithCov() called" << endl; + // cout << "AnalysisTrackSelection::processWithCov() called" << endl; fTofResponse->processSetup(bcs.iteratorAt(0)); auto tracksWithTOFservice = soa::Attach(tracks); runTrackSelection(assocs, bcs, events, tracksWithTOFservice, eventsMC, tracksMC); - cout << "AnalysisTrackSelection::processWithCov() completed" << endl; + // cout << "AnalysisTrackSelection::processWithCov() completed" << endl; } - void processDummy(MyEvents&) + void processWithDalitzBits(TrackAssoc const& assocs, BCsWithTimestamps const& bcs, MyEventsSelected const& events, MyBarrelTracksWithDalitzBits const& tracks, + McCollisions const& eventsMC, McParticles const& tracksMC) + { + runTrackSelection(assocs, bcs, events, tracks, eventsMC, tracksMC); + } + void processDummy(const MyEvents&) { // do nothing } PROCESS_SWITCH(AnalysisTrackSelection, processWithCov, "Run barrel track selection on DQ skimmed tracks w/ cov matrix associations", false); PROCESS_SWITCH(AnalysisTrackSelection, processWithCovTOFService, "Run barrel track selection on DQ skimmed tracks w/ cov matrix associations, with TOF service", false); + PROCESS_SWITCH(AnalysisTrackSelection, processWithDalitzBits, "Run barrel track selection on DQ skimmed tracks w/ dalitz bits obtained from DalitzSelection task", false); PROCESS_SWITCH(AnalysisTrackSelection, processDummy, "Dummy function", true); }; @@ -913,15 +952,15 @@ struct AnalysisPrefilterSelection { Configurable fPropTrack{"cfgPropTrack", false, "Propagate tracks to associated collision to recalculate DCA and momentum vector"}; std::map fPrefilterMap; - AnalysisCompositeCut* fPairCut; - uint32_t fPrefilterMask; - int fPrefilterCutBit; + AnalysisCompositeCut* fPairCut = nullptr; + uint32_t fPrefilterMask = 0; + int fPrefilterCutBit = 0; Preslice trackAssocsPerCollision = aod::track_association::collisionId; void init(o2::framework::InitContext& context) { - cout << "AnalysisPrefilterSelection::init() called" << endl; + LOG(info) << "AnalysisPrefilterSelection::init() called"; if (context.mOptions.get("processDummy")) { return; } @@ -959,7 +998,7 @@ struct AnalysisPrefilterSelection { TString addTrackCutsStr = trackCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + for (const auto& t : addTrackCuts) { allTrackCutsStr += Form(",%s", t->GetName()); } } @@ -996,7 +1035,7 @@ struct AnalysisPrefilterSelection { VarManager::SetupTwoProngDCAFitter(5.0f, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, true); // TODO: get these parameters from Configurables VarManager::SetupTwoProngFwdDCAFitter(5.0f, true, 200.0f, 1.0e-3f, 0.9f, true); - cout << "AnalysisPrefilterSelection::init() completed" << endl; + LOG(info) << "AnalysisPrefilterSelection::init() completed"; } template @@ -1007,7 +1046,7 @@ struct AnalysisPrefilterSelection { return; } - for (auto& [assoc1, assoc2] : o2::soa::combinations(assocs, assocs)) { + for (const auto& [assoc1, assoc2] : o2::soa::combinations(assocs, assocs)) { auto track1 = assoc1.template track_as(); auto track2 = assoc2.template track_as(); @@ -1023,7 +1062,7 @@ struct AnalysisPrefilterSelection { bool track1Loose = assoc1.isBarrelSelected_bit(fPrefilterCutBit); bool track2Loose = assoc2.isBarrelSelected_bit(fPrefilterCutBit); - if (!((track1Candidate > 0 && track2Loose) || (track2Candidate > 0 && track1Loose))) { + if ((track1Candidate == 0 || !track2Loose) && (track2Candidate == 0 || !track1Loose)) { continue; } @@ -1033,7 +1072,7 @@ struct AnalysisPrefilterSelection { VarManager::FillPairCollision(event, track1, track2); } // if the pair fullfils the criteria, add an entry into the prefilter map for the two tracks - if (fPairCut->IsSelected(VarManager::fgValues)) { + if (fPairCut->IsSelected(static_cast(VarManager::fgValues))) { if (fPrefilterMap.find(track1.globalIndex()) == fPrefilterMap.end() && track1Candidate > 0) { fPrefilterMap[track1.globalIndex()] = track1Candidate; } @@ -1045,12 +1084,12 @@ struct AnalysisPrefilterSelection { // cout << "AnalysisPrefilterSelection::runPrefilter() completed for event " << event.globalIndex() << endl; } - void processBarrel(MyEvents const& events, soa::Join const& assocs, MyBarrelTracksWithCov const& tracks) + void processBarrel(MyEvents const& events, soa::Join const& assocs, MyBarrelTracksWithCovNoTOF const& tracks) { - cout << "AnalysisPrefilterSelection::processBarrel() called" << endl; + // cout << "AnalysisPrefilterSelection::processBarrel() called" << endl; fPrefilterMap.clear(); - for (auto& event : events) { + for (const auto& event : events) { auto groupedAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); groupedAssocs.bindInternalIndicesTo(&assocs); @@ -1066,12 +1105,12 @@ struct AnalysisPrefilterSelection { prefilter(mymap); } } else { - for (auto& assoc : assocs) { + for (const auto& assoc : assocs) { // TODO: just use the index from the assoc (no need to cast the whole track) // auto track = assoc.template track_as(); mymap = -1; // if (fPrefilterMap.find(track.globalIndex()) != fPrefilterMap.end()) { - if (fPrefilterMap.find(assoc.trackId()) != fPrefilterMap.end()) { + if (fPrefilterMap.contains(assoc.trackId())) { // NOTE: publish the bitwise negated bits (~), so there will be zeroes for cuts that failed the prefiltering and 1 everywhere else // mymap = ~fPrefilterMap[track.globalIndex()]; mymap = ~fPrefilterMap[assoc.trackId()]; @@ -1081,10 +1120,10 @@ struct AnalysisPrefilterSelection { } } } - cout << "AnalysisPrefilterSelection::processBarrel() completed" << endl; + // cout << "AnalysisPrefilterSelection::processBarrel() completed" << endl; } - void processDummy(MyEvents&) + void processDummy(const MyEvents&) { // do nothing } @@ -1106,7 +1145,7 @@ struct AnalysisSameEventPairing { Produces MCTruthTableEffi; o2::base::MatLayerCylSet* fLUT = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; @@ -1115,6 +1154,14 @@ struct AnalysisSameEventPairing { Configurable muon{"cfgMuonCuts", "", "Comma separated list of muon cuts"}; Configurable pair{"cfgPairCuts", "", "Comma separated list of pair cuts, !!! Use only if you know what you are doing, otherwise leave empty"}; Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; + Configurable fConfigMCtruthQA{"cfgMCtruthQA", false, "If true, fill QA histograms with default"}; + Configurable fConfigPseudoHEQA{"cfgPseudoHEQA", false, "If true, fill QA histograms with pseudo-proper decay length analysis with polarization HE"}; + Configurable fConfigPseudoCSQA{"cfgPseudoCSQA", false, "If true, fill QA histograms with pseudo-proper decay length analysis with polarization CS"}; + Configurable fConfigPseudoRMQA{"cfgPseudoRMQA", false, "If true, fill QA histograms with pseudo-proper decay length analysis with polarization RM"}; + Configurable fConfigTruthPbPbMIDYHE{"cfgTruthPbPbmidyHE", false, "If true, fill QA histograms with dielectron pairs in helicity frame"}; + Configurable fConfigTruthPbPbMIDYCS{"cfgTruthPbPbmidyCS", false, "If true, fill QA histograms with dielectron pairs in collion-soper frame"}; + Configurable fConfigTruthPbPbMIDYGJB1{"cfgTruthPbPbmidyGJB1", false, "If true, fill QA histograms with dielectron pairs in Gottfried-Jackson frame, beam 1 (+z)"}; + Configurable fConfigTruthPbPbMIDYGJB2{"cfgTruthPbPbmidyGJB2", false, "If true, fill QA histograms with dielectron pairs in Gottfried-Jackson frame, beam 2 (-z)"}; Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; Configurable useRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; @@ -1141,7 +1188,7 @@ struct AnalysisSameEventPairing { Configurable recSignals{"cfgMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; Configurable recSignalsJSON{"cfgMCRecSignalsJSON", "", "Comma separated list of MC signals (reconstructed) via JSON"}; Configurable skimSignalOnly{"cfgSkimSignalOnly", false, "Configurable to select only matched candidates"}; - Configurable MCgenAcc{"cfgMCGenAccCut", "", "cut for MC generated particles acceptance"}; + Configurable MCgenAcc{"cfgMCGenAccCuts", "", "Comma separated list of MC generated particles acceptance cuts, !!! Use only if you know what you are doing, otherwise leave empty"}; } fConfigMC; struct : ConfigurableGroup { @@ -1151,11 +1198,11 @@ struct AnalysisSameEventPairing { Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; } fConfigCCDB; - Service fCCDB; + Service fCCDB{}; // PDG database - Service pdgDB; + Service pdgDB{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; // vectors needed for PV recomputation std::vector pvContribGlobIDs; @@ -1165,24 +1212,24 @@ struct AnalysisSameEventPairing { // keep histogram class names in maps, so we don't have to buld their names in the pair loops std::map> fTrackHistNames; std::map> fBarrelHistNamesMCmatched; - std::map> fMuonHistNames; - std::map> fMuonHistNamesMCmatched; + // std::map> fMuonHistNames; + // std::map> fMuonHistNamesMCmatched; std::vector fRecMCSignals; std::vector fGenMCSignals; MCSignal* fEFromJpsiSignal = nullptr; std::vector fPairCuts; - AnalysisCompositeCut fMCGenAccCut; + std::vector fMCGenAccCuts; bool fUseMCGenAccCut = false; - uint32_t fTrackFilterMask; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream - uint32_t fMuonFilterMask; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream - int fNCutsBarrel; - int fNCutsMuon; - int fNPairCuts; + uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream + uint32_t fMuonFilterMask = 0; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream + int fNCutsBarrel = 0; + int fNCutsMuon = 0; + int fNPairCuts = 0; bool fHasTwoProngGenMCsignals = false; - bool fEnableBarrelHistos; + bool fEnableBarrelHistos = false; // bool fEnableMuonHistos; Preslice> trackAssocsPerCollision = aod::track_association::collisionId; @@ -1190,13 +1237,13 @@ struct AnalysisSameEventPairing { void init(o2::framework::InitContext& context) { - cout << "AnalysisSameEventPairing::init() called" << endl; + LOG(info) << "AnalysisSameEventPairing::init() called"; if (context.mOptions.get("processDummy")) { return; } VarManager::SetDefaultVarNames(); - fEnableBarrelHistos = context.mOptions.get("processBarrelOnly"); + fEnableBarrelHistos = context.mOptions.get("processBarrelOnly") || context.mOptions.get("processBarrelPbPbOnly"); // fEnableMuonHistos = context.mOptions.get("processMuonOnlySkimmed"); // Keep track of all the histogram class names to avoid composing strings in the pairing loop @@ -1239,7 +1286,7 @@ struct AnalysisSameEventPairing { TString addMCSignalsStr = fConfigMC.recSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (const auto& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 2) { // NOTE: only 2 prong signals continue; } @@ -1258,17 +1305,20 @@ struct AnalysisSameEventPairing { TString addTrackCutsStr = tempCuts; if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + for (const auto& t : addTrackCuts) { tempCutsStr += Form(",%s", t->GetName()); } } - // get the generator level acceptance cut (just for computing acceptance) - TString mcGenAccCutStr = fConfigMC.MCgenAcc.value; - if (mcGenAccCutStr != "") { - AnalysisCut* cut = dqcuts::GetAnalysisCut(mcGenAccCutStr.Data()); - if (cut != nullptr) { - fMCGenAccCut.AddCut(cut); + // get the mc generated acceptance cuts (for computing acceptance) + TString mcgenCutsStr = fConfigMC.MCgenAcc.value; + if (!mcgenCutsStr.IsNull()) { + std::unique_ptr objArray(mcgenCutsStr.Tokenize(",")); + for (int icut = 0; icut < objArray->GetEntries(); ++icut) { + AnalysisCut* cut = dqcuts::GetAnalysisCut(objArray->At(icut)->GetName()); + if (cut != nullptr) { + fMCGenAccCuts.push_back(cut); + } } fUseMCGenAccCut = true; } @@ -1300,7 +1350,7 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsBarrelSEPP_ambiguousOutOfBunch_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelSEMM_ambiguousOutOfBunch_%s", objArray->At(icut)->GetName())); } - for (auto& n : names) { + for (const auto& n : names) { histNames += Form("%s;", n.Data()); } fTrackHistNames[icut] = names; @@ -1308,7 +1358,7 @@ struct AnalysisSameEventPairing { // if there are pair cuts specified, assign hist directories for each barrel cut - pair cut combination // NOTE: This could possibly lead to large histogram outputs. It is strongly advised to use pair cuts only // if you know what you are doing. - TString cutNamesStr = fConfigOptions.pair.value; + // TString cutNamesStr = fConfigOptions.pair.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); @@ -1341,7 +1391,7 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsBarrelSEPM_ambiguousOutOfBunchCorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); names.push_back(Form("PairsBarrelSEPM_ambiguousOutOfBunchIncorrectAssoc_%s_%s", objArray->At(icut)->GetName(), sig->GetName())); } - for (auto& n : names) { + for (const auto& n : names) { histNames += Form("%s;", n.Data()); } fBarrelHistNamesMCmatched.try_emplace(icut * fRecMCSignals.size() + isig, names); @@ -1458,7 +1508,7 @@ struct AnalysisSameEventPairing { TString addMCSignalsGenStr = fConfigMC.genSignalsJSON.value; if (addMCSignalsGenStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsGenStr.Data()); - for (auto& mcIt : addMCSignals) { + for (const auto& mcIt : addMCSignals) { if (mcIt->GetNProngs() > 2) { // NOTE: only 2 prong signals continue; } @@ -1466,15 +1516,66 @@ struct AnalysisSameEventPairing { } } - for (auto& sig : fGenMCSignals) { + for (const auto& sig : fGenMCSignals) { if (sig->GetNProngs() == 1) { histNames += Form("MCTruthGen_%s;", sig->GetName()); // TODO: Add these names to a std::vector to avoid using Form in the process function histNames += Form("MCTruthGenSel_%s;", sig->GetName()); } else if (sig->GetNProngs() == 2) { - histNames += Form("MCTruthGenPairSel_%s;", sig->GetName()); - histNames += Form("MCTruthGenPseudoPolPairSel_%s;", sig->GetName()); + if (fConfigOptions.fConfigMCtruthQA.value) { + histNames += Form("MCTruthGenPairSel_%s;", sig->GetName()); // after event selection + } + if (fConfigOptions.fConfigPseudoHEQA.value) { + histNames += Form("MCTruthGenPseudoPolPairHESel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigPseudoCSQA.value) { + histNames += Form("MCTruthGenPseudoPolPairCSSel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigPseudoRMQA.value) { + histNames += Form("MCTruthGenPseudoPolPairRMSel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYHE.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairHESel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairCSSel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s;", sig->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s;", sig->GetName()); + } fHasTwoProngGenMCsignals = true; } + // for these pair level signals, also add histograms for each MCgenAcc cut if specified + if (fUseMCGenAccCut) { + for (const auto& cut : fMCGenAccCuts) { + if (fConfigOptions.fConfigMCtruthQA.value) { + histNames += Form("MCTruthGenPairSel_%s_%s;", sig->GetName(), cut->GetName()); // after event selection and MCgenAcc cut + } + if (fConfigOptions.fConfigPseudoHEQA.value) { + histNames += Form("MCTruthGenPseudoPolPairHESel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigPseudoCSQA.value) { + histNames += Form("MCTruthGenPseudoPolPairCSSel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigPseudoRMQA.value) { + histNames += Form("MCTruthGenPseudoPolPairRMSel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYHE.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairHESel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairCSSel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s_%s;", sig->GetName(), cut->GetName()); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + histNames += Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s_%s;", sig->GetName(), cut->GetName()); + } + } + } } fCurrentRun = 0; @@ -1496,23 +1597,23 @@ struct AnalysisSameEventPairing { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigOptions.fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigOptions.fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); - cout << "AnalysisSameEventPairing::init() completed" << endl; + LOG(info) << "AnalysisSameEventPairing::init() completed"; } void initParamsFromCCDB(uint64_t timestamp, bool withTwoProngFitter = true) { - cout << "AnalysisSameEventPairing::initParamsFromCCDB() called for timestamp " << timestamp << endl; + LOG(info) << "AnalysisSameEventPairing::initParamsFromCCDB() called for timestamp " << timestamp; if (fConfigOptions.useRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigCCDB.lutPath)); float magField = 0.0; if (grpmag != nullptr) { @@ -1544,7 +1645,7 @@ struct AnalysisSameEventPairing { VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations } } - cout << "AnalysisSameEventPairing::initParamsFromCCDB() completed" << endl; + LOG(info) << "AnalysisSameEventPairing::initParamsFromCCDB() completed"; } template @@ -1556,15 +1657,18 @@ struct AnalysisSameEventPairing { // int nMyPVContrib = 0; int nMyPVContribOrig = 0; for (auto const& trk : tracks) { // check if it is PV contributor - if (!trk.isPVContributor()) + if (!trk.isPVContributor()) { continue; + } // check if it contributes to the vtx of this collision - if (trk.collisionId() != collision.globalIndex()) + if (trk.collisionId() != collision.globalIndex()) { continue; + } // nMyPVContribOrig++; // --- remove t1 and t2 if they are PV contributors --- - if (trk.globalIndex() == t1.globalIndex() || trk.globalIndex() == t2.globalIndex()) + if (trk.globalIndex() == t1.globalIndex() || trk.globalIndex() == t2.globalIndex()) { continue; + } // add tracks and parameters to the list pvContribGlobIDs.push_back(trk.globalIndex()); pvContribTrackPars.push_back(getTrackParCov(trk)); @@ -1589,8 +1693,9 @@ struct AnalysisSameEventPairing { vertexer.init(); bool PVrefit_doable = vertexer.prepareVertexRefit(pvContribTrackPars, Pvtx); - if (!PVrefit_doable) + if (!PVrefit_doable) { return false; + } // --- do the refit --- pvRefitted = vertexer.refitVertex(vec_useTrk_PVrefit, Pvtx); @@ -1599,10 +1704,10 @@ struct AnalysisSameEventPairing { } // Template function to run same event pairing (barrel-barrel, muon-muon, barrel-muon) - template - void runSameEventPairing(TEvents const& events, BCsWithTimestamps const& bcs, Preslice>& preslice, soa::Join const& assocs, TTracks const& tracks, McCollisions const& /*mcEvents*/, McParticles const& /*mcTracks*/) + template + void runSameEventPairing(TEvents const& events, BCsWithTimestamps const& bcs, Preslice>& preslice, soa::Join const& assocs, TTracks const& tracks, TEventsMC const& mcEvents, McParticles const& mcTracks) { - cout << "AnalysisSameEventPairing::runSameEventPairing() called" << endl; + // cout << "AnalysisSameEventPairing::runSameEventPairing() called" << endl; if (events.size() == 0) { LOG(warning) << "No events in this TF, going to the next one ..."; return; @@ -1623,39 +1728,65 @@ struct AnalysisSameEventPairing { ncuts = fNCutsMuon; }*/ - uint32_t twoTrackFilter = static_cast(0); + auto twoTrackFilter = static_cast(0); int sign1 = 0; int sign2 = 0; - uint32_t mcDecision = static_cast(0); + auto mcDecision = static_cast(0); bool isCorrectAssoc_leg1 = false; bool isCorrectAssoc_leg2 = false; - dielectronList.reserve(1); - // dimuonList.reserve(1); - dielectronsExtraList.reserve(1); - // dimuonsExtraList.reserve(1); - dielectronInfoList.reserve(1); - dileptonInfoList.reserve(1); + + // estimate reserved size + int64_t reserveSize = 0; + int64_t reserveSizeGen = mcTracks.size(); + for (const auto& event : events) { + if (event.isEventSelected_bit(0)) { + auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); + size_t nGood = 0; + for (auto const& t : groupedAssocs) { + if constexpr (TPairType == VarManager::kDecayToEE) { + if (t.isBarrelSelected_raw()) { + nGood++; + } + } + /*else if constexpr (TPairType == VarManager::kDecayToMuMu) { + if (t.isMuonSelected_raw()) { + nGood++; + } + }*/ + } + reserveSize += nGood * (nGood - 1) / 2; + } + } + + dielectronList.reserve(reserveSize); + dielectronsExtraList.reserve(reserveSize); + dielectronInfoList.reserve(reserveSize); + dileptonInfoList.reserve(reserveSize); if (fConfigOptions.flatTables.value) { - dielectronAllList.reserve(1); - // dimuonAllList.reserve(1); + dielectronAllList.reserve(reserveSize); } if (fConfigOptions.fConfigMiniTree) { - dileptonMiniTreeGen.reserve(1); - dileptonMiniTreeRec.reserve(1); + dileptonMiniTreeGen.reserve(reserveSizeGen); + dileptonMiniTreeRec.reserve(reserveSize); } + constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::TrackCov) > 0); - for (auto& event : events) { + for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { continue; } // uint8_t evSel = event.isEventSelected_raw(); // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, static_cast(VarManager::fgValues)); + // if (event.has_mcCollision()) { + // VarManager::FillEvent(event.mcCollision(), static_cast(VarManager::fgValues)); + // } if (event.has_mcCollision()) { - VarManager::FillEvent(event.mcCollision(), VarManager::fgValues); + auto mcEvent = mcEvents.rawIteratorAt(event.mcCollisionId()); + VarManager::FillEvent(mcEvent, static_cast(VarManager::fgValues)); } auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); @@ -1663,7 +1794,7 @@ struct AnalysisSameEventPairing { continue; } - for (auto& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { + for (const auto& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { if constexpr (TPairType == VarManager::kDecayToEE) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; @@ -1700,9 +1831,10 @@ struct AnalysisSameEventPairing { } } } // end loop over MC signals - if (t1.has_mcParticle() && t2.has_mcParticle()) { - isCorrectAssoc_leg1 = (t1.mcParticle().mcCollision() == event.mcCollision()); - isCorrectAssoc_leg2 = (t2.mcParticle().mcCollision() == event.mcCollision()); + if (t1.has_mcParticle() && t2.has_mcParticle() && event.has_mcCollision()) { + auto eventMcCollisionId = event.mcCollisionId(); + isCorrectAssoc_leg1 = (t1.mcParticle().mcCollisionId() == eventMcCollisionId); + isCorrectAssoc_leg2 = (t2.mcParticle().mcCollisionId() == eventMcCollisionId); } VarManager::FillPair(t1, t2); @@ -1710,7 +1842,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); if (fConfigOptions.recomputePV) { VarManager::SetPVrecalculationKF(false); VarManager::ResetValues(VarManager::kVertexingLxyProjectedRecalculatePV, VarManager::kVertexingLxyProjectedRecalculatePV + 1); @@ -1718,15 +1850,16 @@ struct AnalysisSameEventPairing { // cout << "primary vertex (before): x -> " << event.posX() << " y -> " << event.posY() << " z -> " << event.posZ() << endl; o2::dataformats::VertexBase pvRefit; bool ok = refitPVWithPVertexer(event, tracks, t1, t2, pvRefit); - if (ok) + if (ok) { VarManager::FillPairVertexingRecomputePV(event, t1, t2, pvRefit); + } // cout << "primary vertex (after): ok -> " << ok << " x -> " << pvRefit.getX() << " y -> " << pvRefit.getY() << " z -> " << pvRefit.getZ() << endl; } } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); } - if (!fConfigMC.skimSignalOnly || (fConfigMC.skimSignalOnly && mcDecision > 0)) { + if (!fConfigMC.skimSignalOnly || (mcDecision > 0)) { dielectronList(event.globalIndex(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), twoTrackFilter, mcDecision); @@ -1802,7 +1935,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -1849,19 +1982,23 @@ struct AnalysisSameEventPairing { bool isAmbiInBunch = false; bool isAmbiOutOfBunch = false; bool isCorrect_pair = false; - if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) + if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { isCorrect_pair = true; + } for (int icut = 0; icut < ncuts; icut++) { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); - if (sign1 * sign2 < 0) { // +- pairs - fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); // reconstructed, unmatched - for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals + if (sign1 * sign2 < 0) { // +- pairs + fHistMan->FillHistClass(histNames[icut][0].Data(), static_cast(VarManager::fgValues)); // reconstructed, unmatched + for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], isAmbiInBunch, isAmbiOutOfBunch, isCorrect_pair, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), VarManager::fgValues); // matched signal + PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], + isAmbiInBunch, isAmbiOutOfBunch, isCorrect_pair, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][0].Data(), static_cast(VarManager::fgValues)); // matched signal /*if (fConfigOptions.fConfigMiniTree) { if constexpr (TPairType == VarManager::kDecayToMuMu) { twoTrackFilter = a1.isMuonSelected_raw() & a2.isMuonSelected_raw() & fMuonFilterMask; @@ -1896,81 +2033,82 @@ struct AnalysisSameEventPairing { }*/ if (fConfigOptions.fConfigQA) { if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { // correct track-collision association - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][3].Data(), static_cast(VarManager::fgValues)); } else { // incorrect track-collision association - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][4].Data(), static_cast(VarManager::fgValues)); } if (isAmbiInBunch) { // ambiguous in bunch - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][5].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][5].Data(), static_cast(VarManager::fgValues)); if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][6].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][6].Data(), static_cast(VarManager::fgValues)); } else { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][7].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][7].Data(), static_cast(VarManager::fgValues)); } } if (isAmbiOutOfBunch) { // ambiguous out of bunch - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][8].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][8].Data(), static_cast(VarManager::fgValues)); if (isCorrectAssoc_leg1 && isCorrectAssoc_leg2) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][9].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][9].Data(), static_cast(VarManager::fgValues)); } else { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][10].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][10].Data(), static_cast(VarManager::fgValues)); } } } } if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), static_cast(VarManager::fgValues)); } } } } else { if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(histNames[icut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][1].Data(), static_cast(VarManager::fgValues)); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][1].Data(), static_cast(VarManager::fgValues)); } } if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), static_cast(VarManager::fgValues)); } } } else { // -- pairs - fHistMan->FillHistClass(histNames[icut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][2].Data(), static_cast(VarManager::fgValues)); for (unsigned int isig = 0; isig < fRecMCSignals.size(); isig++) { // loop over MC signals if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNamesMC[icut * fRecMCSignals.size() + isig][2].Data(), static_cast(VarManager::fgValues)); } } if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][5].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), static_cast(VarManager::fgValues)); } } } } for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!(cut.IsSelected(VarManager::fgValues))) // apply pair cuts + if (!(cut.IsSelected(static_cast(VarManager::fgValues)))) { // apply pair cuts continue; + } if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), static_cast(VarManager::fgValues)); } else { if (sign1 > 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), static_cast(VarManager::fgValues)); } else { - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), static_cast(VarManager::fgValues)); } } } // end loop (pair cuts) @@ -1979,27 +2117,28 @@ struct AnalysisSameEventPairing { } // end loop over pairs of track associations } // end loop over events - cout << "AnalysisSameEventPairing::runSameEventPairing() completed" << endl; + // cout << "AnalysisSameEventPairing::runSameEventPairing() completed" << endl; } PresliceUnsorted perReducedMcEvent = aod::mcparticle::mcCollisionId; - template - void runMCGen(MyEventsSelected const& events, McCollisions const& mcEvents, McParticles const& mcTracks) + // template + template + void runMCGen(TEvents const& events, TEventsMC const& mcEvents, McParticles const& mcTracks) { - cout << "AnalysisSameEventPairing::runMCGen() called" << endl; + // cout << "AnalysisSameEventPairing::runMCGen() called" << endl; uint32_t mcDecision = 0; int isig = 0; // Loop over all MC single particles to fill generator level histograms, disregarding of whether they belong to selected reconstructed events or not - for (auto& mctrack : mcTracks) { - for (auto& sig : fGenMCSignals) { + for (const auto& mctrack : mcTracks) { + for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { VarManager::FillTrackMC(mcTracks, mctrack); - if (fUseMCGenAccCut && !fMCGenAccCut.IsSelected(VarManager::fgValues)) { - continue; - } - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + // if (fUseMCGenAccCut && !fMCGenAccCut.IsSelected(static_cast(VarManager::fgValues))) { + // continue; + // } + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), static_cast(VarManager::fgValues)); } } } @@ -2009,7 +2148,7 @@ struct AnalysisSameEventPairing { std::vector eFromJpsiMcParticleIndices; // Now loop over reconstructed events to select only MC particles belonging to the same MC collision as the reconstructed event - for (auto& event : events) { + for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -2019,32 +2158,39 @@ struct AnalysisSameEventPairing { eFromJpsiMcParticleIndices.clear(); - auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.mcCollisionId()); + auto mcCollisionGlobalIndex = event.mcCollisionId(); + auto mcEvent = mcEvents.rawIteratorAt(mcCollisionGlobalIndex); + + // fill event information + VarManager::FillEvent(event); + VarManager::FillEvent(mcEvent); + + auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, mcCollisionGlobalIndex); groupedMCTracks.bindInternalIndicesTo(&mcTracks); - for (auto& track : groupedMCTracks) { + for (const auto& track : groupedMCTracks) { auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, track_raw)) { - if (track.mcCollisionId() != event.mcCollisionId()) { // check that the mc track belongs to the same mc collision as the reconstructed event + // check that the mc track belongs to the same mc collision as the reconstructed event + if (track.mcCollisionId() != mcCollisionGlobalIndex) { continue; } VarManager::FillTrackMC(mcTracks, track); - if (fUseMCGenAccCut && !fMCGenAccCut.IsSelected(VarManager::fgValues)) { - // cout << "Applying MC gen acceptance cut." << endl; - continue; - } - + // if (fUseMCGenAccCut && !fMCGenAccCut.IsSelected(static_cast(VarManager::fgValues))) { + // // cout << "Applying MC gen acceptance cut." << endl; + // continue; + // } mcDecision |= (static_cast(1) << isig); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); MCTruthTableEffi(VarManager::fgValues[VarManager::kMCPt], VarManager::fgValues[VarManager::kMCEta], VarManager::fgValues[VarManager::kMCY], VarManager::fgValues[VarManager::kMCPhi], VarManager::fgValues[VarManager::kMCVz], VarManager::fgValues[VarManager::kMCVtxZ], VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if (fConfigOptions.fConfigMiniTree) { - auto mcEvent = mcEvents.rawIteratorAt(track_raw.mcCollisionId()); - dileptonMiniTreeGen(mcDecision, mcEvent.impactParameter(), track_raw.pt(), track_raw.eta(), track_raw.phi(), -999, -999, -999); + auto mcEventFromTrack = mcEvents.rawIteratorAt(track_raw.mcCollisionId()); + dileptonMiniTreeGen(mcDecision, mcEventFromTrack.impactParameter(), track_raw.pt(), track_raw.eta(), track_raw.phi(), -999, -999, -999); } } isig++; @@ -2057,9 +2203,9 @@ struct AnalysisSameEventPairing { if (fHasTwoProngGenMCsignals) { // loop over combinations of the selected mc particles to fill generator level pair histograms - for (auto& t1 : eFromJpsiMcParticleIndices) { + for (const auto& t1 : eFromJpsiMcParticleIndices) { auto t1_raw = mcTracks.rawIteratorAt(t1); - for (auto& t2 : eFromJpsiMcParticleIndices) { + for (const auto& t2 : eFromJpsiMcParticleIndices) { if (t2 <= t1) { continue; // avoid double counting and self-pairing } @@ -2069,7 +2215,7 @@ struct AnalysisSameEventPairing { mcDecision = 0; isig = 0; - for (auto& sig : fGenMCSignals) { + for (const auto& sig : fGenMCSignals) { if (sig->GetNProngs() != 2) { // NOTE: 2-prong signals required here continue; } @@ -2084,18 +2230,67 @@ struct AnalysisSameEventPairing { auto motherMCParticle_t1 = t1_raw.template mothers_first_as(); auto motherMCParticle_t2 = t2_raw.template mothers_first_as(); if (motherMCParticle_t1 == motherMCParticle_t2) { - auto mcEvent = mcEvents.rawIteratorAt(motherMCParticle_t1.mcCollisionId()); - std::array collVtxPos = {mcEvent.posX(), mcEvent.posY(), mcEvent.posZ()}; + auto mcEventFromTrack = mcEvents.rawIteratorAt(motherMCParticle_t1.mcCollisionId()); + std::array collVtxPos = {mcEventFromTrack.posX(), mcEventFromTrack.posY(), mcEventFromTrack.posZ()}; VarManager::FillTrackCollisionMC(motherMCParticle_t1, collVtxPos, pdgDB->Mass(motherMCParticle_t1.pdgCode())); } } + if (fConfigOptions.fConfigMCtruthQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigPseudoHEQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairHESel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigPseudoCSQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairCSSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigPseudoRMQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairRMSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYHE.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairHESel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairCSSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s", sig->GetName()), static_cast(VarManager::fgValues)); + } + if (fUseMCGenAccCut) { - if (!fMCGenAccCut.IsSelected(VarManager::fgValues)) { - continue; + for (const auto& cut : fMCGenAccCuts) { + if (cut->IsSelected(static_cast(VarManager::fgValues))) { + if (fConfigOptions.fConfigMCtruthQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigPseudoHEQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairHESel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigPseudoCSQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairCSSel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigPseudoRMQA.value) { + fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairRMSel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + + if (fConfigOptions.fConfigTruthPbPbMIDYHE.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairHESel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYCS.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairCSSel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB1.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB1Sel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + if (fConfigOptions.fConfigTruthPbPbMIDYGJB2.value) { + fHistMan->FillHistClass(Form("MCTruthGenPoldielectronPbPbPairGJB2Sel_%s_%s", sig->GetName(), cut->GetName()), static_cast(VarManager::fgValues)); + } + } } } - fHistMan->FillHistClass(Form("MCTruthGenPairSel_%s", sig->GetName()), VarManager::fgValues); - fHistMan->FillHistClass(Form("MCTruthGenPseudoPolPairSel_%s", sig->GetName()), VarManager::fgValues); if (fConfigOptions.fConfigMiniTree) { // WARNING! To be checked dileptonMiniTreeGen(mcDecision, -999, t1_raw.pt(), t1_raw.eta(), t1_raw.phi(), t2_raw.pt(), t2_raw.eta(), t2_raw.phi()); @@ -2109,25 +2304,36 @@ struct AnalysisSameEventPairing { } // end loop over reconstructed events - cout << "AnalysisSameEventPairing::runMCGen() completed" << endl; + // cout << "AnalysisSameEventPairing::runMCGen() completed" << endl; } void processBarrelOnly(MyEventsSelected const& events, BCsWithTimestamps const& bcs, soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, McCollisions const& mcEvents, McParticles const& mcTracks) { - cout << "AnalysisSameEventPairing::processBarrelOnly() called" << endl; + // cout << "AnalysisSameEventPairing::processBarrelOnly() called" << endl; runSameEventPairing(events, bcs, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks); - runMCGen(events, mcEvents, mcTracks); - cout << "AnalysisSameEventPairing::processBarrelOnly() completed" << endl; + runMCGen(events, mcEvents, mcTracks); + // cout << "AnalysisSameEventPairing::processBarrelOnly() completed" << endl; } - void processDummy(MyEvents&) + void processBarrelPbPbOnly(MyEventsSelectedWithCentAndMults const& events, BCsWithTimestamps const& bcs, + soa::Join const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, soa::Join const& mcEvents, McParticles const& mcTracks) + { + // cout << "AnalysisSameEventPairing::processBarrelPbPbOnly() called" << endl; + runSameEventPairing(events, bcs, trackAssocsPerCollision, barrelAssocs, barrelTracks, mcEvents, mcTracks); + runMCGen(events, mcEvents, mcTracks); + // cout << "AnalysisSameEventPairing::processBarrelPbPbOnly() completed" << endl; + } + + void processDummy(const MyEvents&) { // do nothing } PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnly, "Run barrel only pairing", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelPbPbOnly, "Run barrel PbPb only pairing", false); PROCESS_SWITCH(AnalysisSameEventPairing, processDummy, "Dummy function, enabled only if none of the others are enabled", true); }; @@ -2178,13 +2384,13 @@ struct AnalysisDileptonTrack { Configurable fConfigMCGenSignalHadronJSON{"cfgMCGenSignalHadronJSON", "", "generator level hadron signal (JSON format), used for MC level combinatorics"}; } fConfigMCOptions; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. - int fNCuts; - int fNLegCuts; - int fNPairCuts; - int fNCommonTrackCuts; + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. + int fNCuts = 0; + int fNLegCuts = 0; + int fNPairCuts = 0; + int fNCommonTrackCuts = 0; std::map fCommonTrackCutMap; - uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons + uint32_t fTrackCutBitMap = 0; // track cut bit mask to be used in the selection of tracks associated with dileptons // vector for single-lepton and track cut names for easy access when calling FillHistogramList() std::vector fTrackCutNames; std::vector fLegCutNames; @@ -2192,7 +2398,7 @@ struct AnalysisDileptonTrack { std::vector fPairCutNames; std::vector fCommonPairCutNames; - Service fCCDB; + Service fCCDB{}; // TODO: The filter expressions seem to always use the default value of configurables, not the values from the actual configuration file Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); @@ -2201,18 +2407,18 @@ struct AnalysisDileptonTrack { // Filter filterMuon = aod::dqanalysisflags::isMuonSelected > static_cast(0); // use two values array to avoid mixing up the quantities - float* fValuesDilepton; - float* fValuesHadron; - HistogramManager* fHistMan; + float* fValuesDilepton = nullptr; + float* fValuesHadron = nullptr; + HistogramManager* fHistMan = nullptr; std::vector fRecMCSignals; std::vector fGenMCSignals; - MCSignal* fDileptonLegSignal; - MCSignal* fHadronSignal; + MCSignal* fDileptonLegSignal = nullptr; + MCSignal* fHadronSignal = nullptr; void init(o2::framework::InitContext& context) { - cout << "AnalysisDileptonTrack::init() called" << endl; + LOG(info) << "AnalysisDileptonTrack::init() called"; bool isBarrel = context.mOptions.get("processBarrel"); // bool isBarrelAsymmetric = context.mOptions.get("processDstarToD0Pi"); // bool isMuon = context.mOptions.get("processMuonSkimmed"); @@ -2220,10 +2426,10 @@ struct AnalysisDileptonTrack { bool isDummy = context.mOptions.get("processDummy"); if (isDummy) { - if (isBarrel || isMCGen /*|| isBarrelAsymmetric*/ /*|| isMuon*/) { - LOG(fatal) << "Dummy function is enabled even if there are normal process functions running! Fix your config!" << endl; + if (isBarrel || isMCGen) { + LOG(fatal) << "Dummy function is enabled even if there are normal process functions running! Fix your config!"; } else { - LOG(info) << "Dummy function is enabled. Skipping the rest of the init function" << endl; + LOG(info) << "Dummy function is enabled. Skipping the rest of the init function"; return; } } @@ -2244,7 +2450,7 @@ struct AnalysisDileptonTrack { VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); TString sigNamesStr = fConfigMCOptions.fConfigMCRecSignals.value; std::unique_ptr objRecSigArray(sigNamesStr.Tokenize(",")); @@ -2266,7 +2472,7 @@ struct AnalysisDileptonTrack { TString addMCSignalsStr = fConfigMCOptions.fConfigMCRecSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (const auto& mcIt : addMCSignals) { if (mcIt->GetNProngs() != 3) { LOG(fatal) << "Signal at reconstructed level requested (" << mcIt->GetName() << ") " << "does not have 3 prongs! Fix it"; } @@ -2306,7 +2512,7 @@ struct AnalysisDileptonTrack { addMCSignalsStr = fConfigMCOptions.fConfigMCGenSignalsJSON.value; if (addMCSignalsStr != "") { std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsStr.Data()); - for (auto& mcIt : addMCSignals) { + for (const auto& mcIt : addMCSignals) { if (mcIt->GetNProngs() == 1) { fGenMCSignals.push_back(mcIt); } @@ -2322,9 +2528,9 @@ struct AnalysisDileptonTrack { string cfgTrackSelection_TrackCuts; if (isBarrel /*|| isBarrelAsymmetric*/) { getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); - } /* else { - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", cfgTrackSelection_TrackCuts, false); - }*/ + } // else { + // getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", cfgTrackSelection_TrackCuts, false); + //} TObjArray* cfgTrackSelection_objArrayTrackCuts = nullptr; if (!cfgTrackSelection_TrackCuts.empty()) { cfgTrackSelection_objArrayTrackCuts = TString(cfgTrackSelection_TrackCuts).Tokenize(","); @@ -2332,16 +2538,16 @@ struct AnalysisDileptonTrack { // get also the list of cuts specified via the JSON parameters if (isBarrel /*|| isBarrelAsymmetric*/) { getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); - } /*else { - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", cfgTrackSelection_TrackCuts, false); - }*/ + } // else { + // getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", cfgTrackSelection_TrackCuts, false); + //} if (!cfgTrackSelection_TrackCuts.empty()) { if (cfgTrackSelection_objArrayTrackCuts == nullptr) { cfgTrackSelection_objArrayTrackCuts = new TObjArray(); } std::vector addTrackCuts = dqcuts::GetCutsFromJSON(cfgTrackSelection_TrackCuts.data()); - for (auto& t : addTrackCuts) { - TObjString* tempObjStr = new TObjString(t->GetName()); + for (const auto& t : addTrackCuts) { + auto tempObjStr = new TObjString(t->GetName()); cfgTrackSelection_objArrayTrackCuts->Add(tempObjStr); } } @@ -2381,20 +2587,20 @@ struct AnalysisDileptonTrack { // but this is only used for histograms, not for the produced dilepton tables string cfgPairing_TrackCuts; string cfgPairing_PairCuts; - string cfgPairing_PairCutsJSON; + // string cfgPairing_PairCutsJSON; string cfgPairing_CommonTrackCuts; if (isBarrel) { getTaskOptionValue(context, "analysis-same-event-pairing", "cfgTrackCuts", cfgPairing_TrackCuts, false); getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); - } /* else if (isMuon) { - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgMuonCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); - }*/ - /* else if (isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgLegCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgCommonTrackCuts", cfgPairing_CommonTrackCuts, false); - }*/ + } // else if (isMuon) { + // getTaskOptionValue(context, "analysis-same-event-pairing", "cfgMuonCuts", cfgPairing_TrackCuts, false); + // getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + //} + // else if (isBarrelAsymmetric) { + // getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgLegCuts", cfgPairing_TrackCuts, false); + // getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + // getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgCommonTrackCuts", cfgPairing_CommonTrackCuts, false); + //} if (cfgPairing_TrackCuts.empty()) { LOG(fatal) << "There are no dilepton cuts specified in the upstream in the same-event-pairing or asymmetric-pairing"; } @@ -2443,21 +2649,21 @@ struct AnalysisDileptonTrack { fNLegCuts = fNCuts; // loop over single lepton cuts - if (isBarrel /*|| isBarrelAsymmetric*/ /* || isMuon*/) { + if (isBarrel) { for (int icut = 0; icut < fNLegCuts; ++icut) { TString pairLegCutName; // here we check that this cut is one of those used for building the dileptons - if (isBarrel /*|| isMuon*/) { - if (!cfgPairing_objArrayTrackCuts->FindObject(fTrackCutNames[icut].Data())) { - continue; - } - pairLegCutName = fTrackCutNames[icut].Data(); - } else { - // For asymmetric pairs we access the leg cuts instead - pairLegCutName = static_cast(cfgPairing_objArrayTrackCuts->At(icut))->GetString(); + // if (isBarrel) { + if (!cfgPairing_objArrayTrackCuts->FindObject(fTrackCutNames[icut].Data())) { + continue; } + pairLegCutName = fTrackCutNames[icut].Data(); + // } else { + // // For asymmetric pairs we access the leg cuts instead + // pairLegCutName = static_cast(cfgPairing_objArrayTrackCuts->At(icut))->GetString(); + // } fLegCutNames.push_back(pairLegCutName); @@ -2468,12 +2674,12 @@ struct AnalysisDileptonTrack { for (int iCutTrack = 0; iCutTrack < fNCuts; iCutTrack++) { // here we check that this track cut is one of those required to associate with the dileptons - if (!(fTrackCutBitMap & (static_cast(1) << iCutTrack))) { + if ((fTrackCutBitMap & (static_cast(1) << iCutTrack)) == 0) { continue; } DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s", pairLegCutName.Data(), fTrackCutNames[iCutTrack].Data()), fConfigOptions.fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (const auto& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrackMCMatched_%s_%s_%s", pairLegCutName.Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigOptions.fConfigHistogramSubgroups.value.data()); } @@ -2482,7 +2688,7 @@ struct AnalysisDileptonTrack { for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { DefineHistograms(fHistMan, Form("DileptonsSelected_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data()), "barrel,vertexing"); DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fTrackCutNames[iCutTrack].Data()), fConfigOptions.fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (const auto& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrackMCMatched_%s_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigOptions.fConfigHistogramSubgroups.value.data()); } } @@ -2493,7 +2699,7 @@ struct AnalysisDileptonTrack { for (int iPairCut = 0; iPairCut < fNPairCuts; ++iPairCut) { DefineHistograms(fHistMan, Form("DileptonsSelected_%s_%s", pairLegCutName.Data(), fPairCutNames[iPairCut].Data()), "barrel,vertexing"); DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s", pairLegCutName.Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data()), fConfigOptions.fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (const auto& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrackMCMatched_%s_%s_%s_%s", pairLegCutName.Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigOptions.fConfigHistogramSubgroups.value.data()); } @@ -2502,7 +2708,7 @@ struct AnalysisDileptonTrack { for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { DefineHistograms(fHistMan, Form("DileptonsSelected_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), "barrel,vertexing"); DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data()), fConfigOptions.fConfigHistogramSubgroups.value.data()); - for (auto& sig : fRecMCSignals) { + for (const auto& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("DileptonTrack_%s_%s_%s_%s_%s", pairLegCutName.Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iCutTrack].Data(), sig->GetName()), fConfigOptions.fConfigHistogramSubgroups.value.data()); } } @@ -2514,11 +2720,11 @@ struct AnalysisDileptonTrack { } // end if (isBarrel || isBarrelAsymmetric || isMuon) if (isMCGen) { - for (auto& sig : fGenMCSignals) { + for (const auto& sig : fGenMCSignals) { DefineHistograms(fHistMan, Form("MCTruthGen_%s", sig->GetName()), ""); DefineHistograms(fHistMan, Form("MCTruthGenSel_%s", sig->GetName()), ""); } - for (auto& sig : fRecMCSignals) { + for (const auto& sig : fRecMCSignals) { DefineHistograms(fHistMan, Form("MCTruthGenSelBR_%s", sig->GetName()), ""); DefineHistograms(fHistMan, Form("MCTruthGenSelBRAccepted_%s", sig->GetName()), ""); } @@ -2530,15 +2736,15 @@ struct AnalysisDileptonTrack { } VarManager::SetUseVars(fHistMan->GetUsedVars()); fOutputList.setObject(fHistMan->GetMainHistogramList()); - cout << "AnalysisDileptonTrack::init() completed" << endl; + LOG(info) << "AnalysisDileptonTrack::init() completed"; } // init parameters from CCDB void initParamsFromCCDB(uint64_t timestamp) { - cout << "AnalysisDileptonTrack::initParamsFromCCDB() called for timestamp=" << timestamp << endl; + LOG(info) << "AnalysisDileptonTrack::initParamsFromCCDB() called for timestamp=" << timestamp; if (fConfigCCDBOptions.fConfigUseRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDBOptions.fConfigGRPmagPath.value, timestamp); + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDBOptions.fConfigGRPmagPath.value, timestamp); float magField = 0.0; if (grpmag != nullptr) { magField = grpmag->getNominalL3Field(); @@ -2557,7 +2763,7 @@ struct AnalysisDileptonTrack { VarManager::SetupThreeProngDCAFitter(fConfigCCDBOptions.fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables } } - cout << "AnalysisDileptonTrack::initParamsFromCCDB() completed" << endl; + LOG(info) << "AnalysisDileptonTrack::initParamsFromCCDB() completed"; } // Template function to run pair - hadron combinations @@ -2573,12 +2779,12 @@ struct AnalysisDileptonTrack { VarManager::FillEvent(event.mcCollision(), fValuesDilepton); } - uint32_t mcDecision = static_cast(0); + auto mcDecision = static_cast(0); size_t isig = 0; auto bc = event.template bc_as(); - for (auto dilepton : dileptons) { + for (const auto& dilepton : dileptons) { // get full track info of tracks based on the index auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); auto lepton2 = tracks.rawIteratorAt(dilepton.index1Id()); @@ -2624,7 +2830,7 @@ struct AnalysisDileptonTrack { } // loop over track associations - for (auto& assoc : assocs) { + for (const auto& assoc : assocs) { VarManager::ResetValues(0, VarManager::kNVars, fValuesHadron); // VarManager::ResetValues(0, VarManager::kNVars, fValuesDilepton); @@ -2784,9 +2990,9 @@ struct AnalysisDileptonTrack { } fHistMan->FillHistClass(Form("DileptonTrack_%s_%s", fLegCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), fValuesHadron); - for (uint32_t isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s", fLegCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isig]->GetName()), fValuesHadron); + for (uint32_t isignal = 0; isignal < fRecMCSignals.size(); isignal++) { + if (mcDecision & (static_cast(1) << isignal)) { + fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s", fLegCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isignal]->GetName()), fValuesHadron); } } @@ -2794,9 +3000,9 @@ struct AnalysisDileptonTrack { for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { if (dilepton.commonFilterMap_bit(fCommonTrackCutMap[iCommonCut])) { fHistMan->FillHistClass(Form("DileptonTrack_%s_%s_%s", fLegCutNames[icut].Data(), fCommonPairCutNames[iCommonCut].Data(), fTrackCutNames[iTrackCut].Data()), fValuesHadron); - for (uint32_t isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonPairCutNames[iCommonCut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isig]->GetName()), fValuesHadron); + for (uint32_t isignal = 0; isignal < fRecMCSignals.size(); isignal++) { + if (mcDecision & (static_cast(1) << isignal)) { + fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonPairCutNames[iCommonCut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isignal]->GetName()), fValuesHadron); } } } @@ -2804,17 +3010,17 @@ struct AnalysisDileptonTrack { for (int iPairCut = 0; iPairCut < fNPairCuts; iPairCut++) { if (dilepton.pairFilterMap_bit(iPairCut)) { fHistMan->FillHistClass(Form("DileptonTrack_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iTrackCut].Data()), fValuesHadron); - for (uint32_t isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isig]->GetName()), fValuesHadron); + for (uint32_t isignal = 0; isignal < fRecMCSignals.size(); isignal++) { + if (mcDecision & (static_cast(1) << isignal)) { + fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isignal]->GetName()), fValuesHadron); } } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { if (dilepton.commonFilterMap_bit(fCommonTrackCutMap[iCommonCut])) { fHistMan->FillHistClass(Form("DileptonTrack_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iTrackCut].Data()), fValuesHadron); - for (uint32_t isig = 0; isig < fRecMCSignals.size(); isig++) { - if (mcDecision & (static_cast(1) << isig)) { - fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isig]->GetName()), fValuesHadron); + for (uint32_t isignal = 0; isignal < fRecMCSignals.size(); isignal++) { + if (mcDecision & (static_cast(1) << isignal)) { + fHistMan->FillHistClass(Form("DileptonTrackMCMatched_%s_%s_%s_%s_%s", fLegCutNames[icut].Data(), fCommonPairCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data(), fTrackCutNames[iTrackCut].Data(), fRecMCSignals[isignal]->GetName()), fValuesHadron); } } } @@ -2837,8 +3043,8 @@ struct AnalysisDileptonTrack { MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons, McCollisions const& mcEvents, McParticles const& mcTracks) { - cout << "AnalysisDileptonTrack::processBarrel() called" << endl; - // set up KF or DCAfitter + // cout << "AnalysisDileptonTrack::processBarrel() called" << endl; + // set up KF or DCAfitter if (events.size() == 0) { return; } @@ -2846,7 +3052,7 @@ struct AnalysisDileptonTrack { initParamsFromCCDB(bcs.begin().timestamp()); fCurrentRun = bcs.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -2856,7 +3062,7 @@ struct AnalysisDileptonTrack { // groupedDielectrons.bindInternalIndicesTo(&dileptons); runDileptonHadron(event, bcs, groupedBarrelAssocs, tracks, groupedDielectrons, mcEvents, mcTracks); } - cout << "AnalysisDileptonTrack::processBarrel() completed" << endl; + // cout << "AnalysisDileptonTrack::processBarrel() completed" << endl; } /* void processDstarToD0Pi(soa::Filtered const& events, BCsWithTimestamps const& bcs, @@ -2911,13 +3117,13 @@ struct AnalysisDileptonTrack { void processMCGen(soa::Filtered const& events, McCollisions const& /*mcEvents*/, McParticles const& mcTracks) { - cout << "AnalysisDileptonTrack::processMCGen() called" << endl; - // first loop over MC particles to fill generator level histograms for one prong MC signals (e.g. the B meson) - for (auto& mctrack : mcTracks) { - for (auto& sig : fGenMCSignals) { + // cout << "AnalysisDileptonTrack::processMCGen() called" << endl; + // first loop over MC particles to fill generator level histograms for one prong MC signals (e.g. the B meson) + for (const auto& mctrack : mcTracks) { + for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, mctrack)) { VarManager::FillTrackMC(mcTracks, mctrack); - fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), static_cast(VarManager::fgValues)); } } } @@ -2927,7 +3133,7 @@ struct AnalysisDileptonTrack { std::vector mcParticleListDileptonLegs; std::vector mcParticleListHadron; - for (auto& event : events) { + for (const auto& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -2939,12 +3145,12 @@ struct AnalysisDileptonTrack { auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.mcCollisionId()); groupedMCTracks.bindInternalIndicesTo(&mcTracks); - for (auto& track : groupedMCTracks) { + for (const auto& track : groupedMCTracks) { auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); - for (auto& sig : fGenMCSignals) { + for (const auto& sig : fGenMCSignals) { if (sig->CheckSignal(true, track_raw)) { VarManager::FillTrackMC(mcTracks, track); - fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSel_%s", sig->GetName()), static_cast(VarManager::fgValues)); } } if (fDileptonLegSignal->CheckSignal(true, track_raw)) { @@ -2957,25 +3163,25 @@ struct AnalysisDileptonTrack { // construct all possible triplets of MC tracks in this MC collision to fill generator level histograms // for three prong MC signals (e.g. B -> J/psi + K) - for (auto& t1 : mcParticleListDileptonLegs) { + for (const auto& t1 : mcParticleListDileptonLegs) { auto t1_raw = mcTracks.rawIteratorAt(t1); - for (auto& t2 : mcParticleListDileptonLegs) { + for (const auto& t2 : mcParticleListDileptonLegs) { if (t2 <= t1) { continue; // avoid double counting and self-pairing } auto t2_raw = mcTracks.rawIteratorAt(t2); - for (auto& t3 : mcParticleListHadron) { + for (const auto& t3 : mcParticleListHadron) { if (t3 == t1 || t3 == t2) { continue; // avoid self-pairing } auto t3_raw = mcTracks.rawIteratorAt(t3); - for (auto& sig : fRecMCSignals) { + for (const auto& sig : fRecMCSignals) { if (sig->CheckSignal(true, t1_raw, t2_raw, t3_raw)) { - VarManager::FillTripleMC(t1_raw, t2_raw, t3_raw, VarManager::fgValues); // nb! hardcoded for jpsiK - fHistMan->FillHistClass(Form("MCTruthGenSelBR_%s", sig->GetName()), VarManager::fgValues); + VarManager::FillTripleMC(t1_raw, t2_raw, t3_raw, static_cast(VarManager::fgValues)); // nb! hardcoded for jpsiK + fHistMan->FillHistClass(Form("MCTruthGenSelBR_%s", sig->GetName()), static_cast(VarManager::fgValues)); // apply kinematic cuts if (t1_raw.pt() < fConfigMCOptions.fConfigMCGenDileptonLegPtMin.value || std::abs(t1_raw.eta()) > fConfigMCOptions.fConfigMCGenDileptonLegEtaAbs.value) { @@ -2987,17 +3193,17 @@ struct AnalysisDileptonTrack { if (t3_raw.pt() < fConfigMCOptions.fConfigMCGenHadronPtMin.value || std::abs(t3_raw.eta()) > fConfigMCOptions.fConfigMCGenHadronEtaAbs.value) { continue; } - fHistMan->FillHistClass(Form("MCTruthGenSelBRAccepted_%s", sig->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("MCTruthGenSelBRAccepted_%s", sig->GetName()), static_cast(VarManager::fgValues)); } } } } } } // end loop over reconstructed events - cout << "AnalysisDileptonTrack::processMCGen() completed" << endl; + // cout << "AnalysisDileptonTrack::processMCGen() completed" << endl; } - void processDummy(MyEvents&) + void processDummy(const MyEvents&) { // do nothing } @@ -3009,6 +3215,551 @@ struct AnalysisDileptonTrack { PROCESS_SWITCH(AnalysisDileptonTrack, processDummy, "Dummy function", true); }; +struct AnalysisDileptonTrackTrack { + OutputObj fOutputList{"output"}; + + Configurable fConfigTrackCut1{"cfgTrackCut1", "pionPIDCut1", "track1 cut"}; // used for select the tracks from SelectedTracks + Configurable fConfigTrackCut2{"cfgTrackCut2", "pionPIDCut2", "track2 cut"}; // used for select the tracks from SelectedTracks + Configurable fConfigDileptonCut{"cfgDileptonCut", "pairJpsi2", "Dilepton cut"}; + Configurable fConfigQuadrupletCuts{"cfgQuadrupletCuts", "pairX3872Cut1", "Comma separated list of Dilepton-Track-Track cut"}; + Configurable fConfigMCRecSignals{"cfgBarrelMCRecSignals", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable fConfigMCGenSignals{"cfgBarrelMCGenSignals", "", "Comma separated list of MC signals (generated)"}; + Configurable fConfigDileptonMCRecSignal{"cfgDileptonMCRecSignal", "", "Comma separated list of MC signals (reconstructed)"}; + Configurable fConfigTrackPtMin{"cfgTrackPtMin", 0.15f, "Minimum pt of tracks to be used in the quadruplet"}; + Configurable fConfigTrackEtaAbs{"cfgTrackEtaAbs", 0.9f, "Maximum absolute eta of tracks to be used in the quadruplet"}; + Configurable fConfigDileptonPtMin{"cfgDileptonPtMin", 1.0f, "Minimum pt of dileptons to be used in the quadruplet"}; + Configurable fConfigDileptonEtaAbs{"cfgDileptonEtaAbs", 0.9f, "Maximum absolute eta of dileptons to be used in the quadruplet"}; + Configurable fConfigAddDileptonHistogram{"cfgAddDileptonHistogram", "barrel", "Comma separated list of histograms"}; + Configurable fConfigAddQuadrupletHistogram{"cfgAddQuadrupletHistogram", "xtojpsipipi", "Comma separated list of histograms"}; + + Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; + Configurable fConfigSetupFourProngFitter{"cfgSetupFourProngFitter", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; + // Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + // Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; + // Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + Configurable fConfigDileptonLxyCut{"cfgDileptonLxyCut", -999.f, "Dilepton Lxy cut for secondary vertex"}; + + struct : ConfigurableGroup { + Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; + Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + } fConfigCCDBOptions; + + Produces DileptonTrackTrackTable; + + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. + // uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons + // cut name setting + TString fTrackCutName1; + TString fTrackCutName2; + std::vector fTrackCutNames; + uint32_t fTrackCutBitMap1 = 0; + uint32_t fTrackCutBitMap2 = 0; + bool fIsSameTrackCut = false; + AnalysisCompositeCut fDileptonCut{}; + std::vector fQuadrupletCutNames; + std::vector fQuadrupletCuts; + + Service fCCDB; + o2::ccdb::CcdbApi fCCDBApi; + + Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); + Filter dileptonFilter = aod::reducedpair::sign == 0 && aod::reducedpair::lxy > fConfigDileptonLxyCut; + Filter filterBarrelTrackSelected = aod::dqanalysisflags::isBarrelSelected > static_cast(0); + + constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map + + float* fValuesQuadruplet = nullptr; + HistogramManager* fHistMan = nullptr; + + // o2::analysis::QuadMlResponse fQuadMlResponse; + // std::vector fOutputMlQuad; + + std::vector fRecMCSignals; + std::vector fGenMCSignals; + + void init(o2::framework::InitContext& context) + { + bool isX3872 = context.mOptions.get("processX3872"); + bool isPsi2S = context.mOptions.get("processPsi2S"); + bool isMCGen = context.mOptions.get("processMCGen") || context.mOptions.get("processMCGenWithEventSelection"); + bool isDummy = context.mOptions.get("processDummy"); + + if (isDummy) { + if (isPsi2S || isX3872 || isMCGen) { + LOG(fatal) << "Dummy function is enabled even if there are normal process functions running! Fix your config!"; + return; + } else { + LOG(info) << "Dummy function is enabled. Skipping the rest of the init function"; + return; + } + } + + fCurrentRun = 0; + + fCCDB->setURL(fConfigCCDBOptions.fConfigCcdbUrl.value); + fCCDB->setCaching(true); + fCCDB->setLocalObjectValidityChecking(); + fCCDB->setCreatedNotAfter(fConfigCCDBOptions.fConfigNoLaterThan.value); + if (!o2::base::GeometryManager::isGeometryLoaded()) { + fCCDB->get(fConfigCCDBOptions.fConfigGeoPath); + } + + fValuesQuadruplet = new float[VarManager::kNVars]; + VarManager::SetDefaultVarNames(); + fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); + fHistMan->SetUseDefaultVariableNames(true); + fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + + TString sigNamesStr = fConfigMCRecSignals.value; + std::unique_ptr objRecSigArray(sigNamesStr.Tokenize(",")); + if (!sigNamesStr.IsNull()) { + for (int isig = 0; isig < objRecSigArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objRecSigArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() != 4) { + LOG(fatal) << "Signal at reconstructed level requested (" << sig->GetName() << ") " << "does not have 4 prongs! Fix it"; + } + fRecMCSignals.push_back(sig); + } else { + LOG(fatal) << "Signal at reconstructed level requested (" << objRecSigArray->At(isig)->GetName() << ") " << "could not be retrieved from the library! -> skipped"; + } + } + } + + TString sigGenNamesStr = fConfigMCGenSignals.value; + std::unique_ptr objGenSigArray(sigGenNamesStr.Tokenize(",")); + for (int isig = 0; isig < objGenSigArray->GetEntries(); ++isig) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objGenSigArray->At(isig)->GetName()); + if (sig) { + if (sig->GetNProngs() == 1) { // NOTE: 1-prong signals required + fGenMCSignals.push_back(sig); + } + } + } + + string cfgTrackSelection_TrackCuts; + getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); + TObjArray* cfgTrackSelection_objArrayTrackCuts = nullptr; + if (!cfgTrackSelection_TrackCuts.empty()) { + cfgTrackSelection_objArrayTrackCuts = TString(cfgTrackSelection_TrackCuts).Tokenize(","); + } + getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); + if (!cfgTrackSelection_TrackCuts.empty()) { + if (cfgTrackSelection_objArrayTrackCuts == nullptr) { + cfgTrackSelection_objArrayTrackCuts = new TObjArray(); + } + std::vector addTrackCuts = dqcuts::GetCutsFromJSON(cfgTrackSelection_TrackCuts.data()); + for (const auto& t : addTrackCuts) { + auto tempObjStr = new TObjString(t->GetName()); + cfgTrackSelection_objArrayTrackCuts->Add(tempObjStr); + } + } + for (Int_t icut = 0; icut < cfgTrackSelection_objArrayTrackCuts->GetEntries(); ++icut) { + TString cutName = cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName(); + fTrackCutNames.push_back(cutName); + } + + fTrackCutName1 = fConfigTrackCut1.value; + fTrackCutName2 = fConfigTrackCut2.value; + if (fTrackCutName1 == fTrackCutName2) { + fIsSameTrackCut = true; + } + if (!cfgTrackSelection_objArrayTrackCuts->FindObject(fTrackCutName1.Data())) { + LOGF(fatal, "Track cut name %s in cfgTrackCut1 is not found in analysis-track-selection:cfgTrackCuts", fTrackCutName1.Data()); + } + if (!cfgTrackSelection_objArrayTrackCuts->FindObject(fTrackCutName2.Data())) { + LOGF(fatal, "Track cut name %s in cfgTrackCut2 is not found in analysis-track-selection:cfgTrackCuts", fTrackCutName2.Data()); + } + LOGP(info, "track cut name 1: {}", fTrackCutName1.Data()); + for (Int_t icut = 0; icut < cfgTrackSelection_objArrayTrackCuts->GetEntries(); ++icut) { + LOGP(info, "Available track cut: {}", cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName()); + TString fTrackSelectionCutName = cfgTrackSelection_objArrayTrackCuts->At(icut)->GetName(); + if (fTrackSelectionCutName.CompareTo(fTrackCutName1) == 0) { + LOGP(info, "Found track cut: {}", fTrackCutName1.Data()); + fTrackCutBitMap1 |= (static_cast(1) << icut); + } + if (fTrackSelectionCutName.CompareTo(fTrackCutName2) == 0) { + LOGP(info, "Found track cut: {}", fTrackCutName2.Data()); + fTrackCutBitMap2 |= (static_cast(1) << icut); + } + } + LOGP(info, "Track cut bitmap 1: {}", fTrackCutBitMap1); + + TString configDileptonCutNamesStr = fConfigDileptonCut.value; + fDileptonCut = *dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + TString configQuadruletCutNamesStr = fConfigQuadrupletCuts.value; + std::unique_ptr objArray(configQuadruletCutNamesStr.Tokenize(",")); + for (Int_t icut = 0; icut < objArray->GetEntries(); ++icut) { + TString cutName = objArray->At(icut)->GetName(); + fQuadrupletCutNames.push_back(cutName); + fQuadrupletCuts.push_back(*dqcuts::GetCompositeCut(cutName.Data())); + } + + if (isPsi2S || isX3872) { + DefineHistograms(fHistMan, Form("Pairs_%s", configDileptonCutNamesStr.Data()), fConfigAddDileptonHistogram.value.data()); + if (!configQuadruletCutNamesStr.IsNull()) { + for (std::size_t icut = 0; icut < fQuadrupletCutNames.size(); ++icut) { + if (fIsSameTrackCut) { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } else { + DefineHistograms(fHistMan, Form("QuadrupletSEPM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + } + DefineHistograms(fHistMan, Form("QuadrupletSEPP_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + DefineHistograms(fHistMan, Form("QuadrupletSEMM_%s", fQuadrupletCutNames[icut].Data()), fConfigAddQuadrupletHistogram.value.data()); + for (auto& sig : fRecMCSignals) { + DefineHistograms(fHistMan, Form("QuadrupletMCMatched_%s_%s", fQuadrupletCutNames[icut].Data(), sig->GetName()), fConfigAddQuadrupletHistogram.value.data()); + // DefineHistograms(fHistMan, Form("QuadrupletMCMatchedAmbiguous_%s_%s", fQuadrupletCutNames[icut].Data(), sig->GetName()), fConfigAddQuadrupletHistogram.value.data()); + } + } + } + } + + if (isMCGen) { + for (auto& sig : fGenMCSignals) { + DefineHistograms(fHistMan, Form("MCTruthGenQuad_%s", sig->GetName()), ""); + DefineHistograms(fHistMan, Form("MCTruthGenQuadAccepted_%s", sig->GetName()), ""); + } + // DefineHistograms(fHistMan, "MCTruthGenQuadAccepted", ""); + } + + VarManager::SetUseVars(fHistMan->GetUsedVars()); + fOutputList.setObject(fHistMan->GetMainHistogramList()); + } + + // init parameters from CCDB + void initParamsFromCCDB(uint64_t timestamp) + { + if (fConfigCCDBOptions.fConfigUseRemoteField.value) { + o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDBOptions.fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); + VarManager::SetupFourProngKFParticle(magField); + } else if (fConfigSetupFourProngFitter.value) { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + } + } else { + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(fConfigCCDBOptions.fConfigMagField.value); + VarManager::SetupFourProngKFParticle(fConfigCCDBOptions.fConfigMagField.value); + } else if (fConfigSetupFourProngFitter.value) { + LOGP(info, "Setting up DCA fitter for two and four prong candidates"); + VarManager::SetupTwoProngDCAFitter(fConfigCCDBOptions.fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(fConfigCCDBOptions.fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + } + } + } + + // Template function to run pair - hadron combinations + template + void runDileptonTrackTrack(TEvent const& event, TBCs const& /*bcs*/, soa::Join const& assocs, TTracks const& tracks, TDileptons const& dileptons, McCollisions const& /*mcEvents*/, McParticles const& /*mcTracks*/) + { + VarManager::ResetValues(0, VarManager::kNVars, fValuesQuadruplet); + VarManager::FillEvent(event, fValuesQuadruplet); + // VarManager::FillEvent(event.reducedMCevent(), fValuesQuadruplet); + if (event.has_mcCollision()) { + VarManager::FillEvent(event.mcCollision(), fValuesQuadruplet); + } + + uint32_t mcDecision = static_cast(0); + // size_t isig = 0; + + for (auto dilepton : dileptons) { + // get full track info of tracks based on the index + + int indexLepton1 = dilepton.index0Id(); + int indexLepton2 = dilepton.index1Id(); + auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); + auto lepton2 = tracks.rawIteratorAt(dilepton.index1Id()); + auto lepton1MC = lepton1.mcParticle(); + auto lepton2MC = lepton2.mcParticle(); + // Check that the dilepton has zero charge + if (dilepton.sign() != 0) { + continue; + } + VarManager::FillTrack(dilepton, fValuesQuadruplet); + + // bool isAmbiguousLepton = (dilepton.filterMap_raw() & (static_cast(1) << 28)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 29)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 30)) || + // (dilepton.filterMap_raw() & (static_cast(1) << 31)); + // // if (isAmbi && isAmbiguousLepton) + // // continue; // skip ambiguous dileptons + // if constexpr ((TTrackFillMap & VarManager::ObjTypes::AmbiTrack) > 0) { + // if (isAmbiguousLepton) { + // // LOGP(info, "Dilepton {} is ambiguous, skipping", dilepton.index0Id()); + // continue; // skip ambiguous dileptons + // } + // } + + // apply the dilepton cut + if (!fDileptonCut.IsSelected(fValuesQuadruplet)) + continue; + + fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); + + // loop over hadrons pairs + for (auto& [a1, a2] : o2::soa::combinations(assocs, assocs)) { + uint32_t trackSelection = 0; + trackSelection = ((a1.isBarrelSelected_raw() & fTrackCutBitMap1) && (a2.isBarrelSelected_raw() & fTrackCutBitMap2)); + // LOGP(info, "trackSelection: {}, a1: {}, a2: {}", trackSelection, a1.isBarrelSelected_raw(), a2.isBarrelSelected_raw()); + if (!trackSelection) { + continue; + } + + // get the track from this association + auto track1 = tracks.rawIteratorAt(a1.trackId()); + auto track2 = tracks.rawIteratorAt(a2.trackId()); + if (track1.sign() < 0 && track2.sign() > 0) { + std::swap(track1, track2); + } + // avoid self combinations + if (track1.globalIndex() == indexLepton1 || track1.globalIndex() == indexLepton2 || track2.globalIndex() == indexLepton1 || track2.globalIndex() == indexLepton2) { + continue; + } + + // fill variables + VarManager::FillDileptonTrackTrack(dilepton, track1, track2, fValuesQuadruplet); + // fValuesQuadruplet[VarManager::kWeight] = 1.0f; + if (fConfigSetupFourProngFitter || fConfigUseKFVertexing) { + // LOGP(info, "Using KF or DCA fitter for secondary vertexing"); + VarManager::FillDileptonTrackTrackVertexing(event, lepton1, lepton2, track1, track2, fValuesQuadruplet); + } + + // if (fConfigML.applyBDT) { + // bool isSelectedBDT = false; + // auto pT = fValuesQuadruplet[VarManager::kQuadPt]; + // if (pT < fConfigML.ptBins.value.front() || pT > fConfigML.ptBins.value.back()) { + // continue; // skip quadruplets outside of the pt range of the BDT models + // } + // // TODO: move some variables into varManager and reduce the number of input parameters + // std::vector inputFeatures = fQuadMlResponse.getInputFeatures(lepton1, lepton2, fValuesQuadruplet); + // isSelectedBDT = fQuadMlResponse.isSelectedMl(inputFeatures, fValuesQuadruplet[VarManager::kQuadPt], fOutputMlQuad); + // if (!isSelectedBDT) + // continue; + // VarManager::FillBdtScore(fOutputMlQuad, fValuesQuadruplet); + // } + + auto track1MC = track1.mcParticle(); + auto track2MC = track2.mcParticle(); + mcDecision = 0; + // size_t isig = 0; + for (size_t iSignal = 0; iSignal < fRecMCSignals.size(); ++iSignal) { + if (fRecMCSignals[iSignal]->CheckSignal(true, lepton1MC, lepton2MC, track1MC, track2MC)) { + mcDecision |= (static_cast(1) << iSignal); + } + } + int iCut = 0; + uint32_t CutDecision = 0; + for (auto cutname = fQuadrupletCutNames.begin(); cutname != fQuadrupletCutNames.end(); cutname++, iCut++) { + // apply dilepton-track-track cut + if (fQuadrupletCuts[iCut].IsSelected(fValuesQuadruplet)) { + CutDecision |= (1 << iCut); + if (fIsSameTrackCut) { + if (track1.sign() * track2.sign() < 0) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + for (uint32_t isig = 0; isig < fRecMCSignals.size(); isig++) { + if (mcDecision & (static_cast(1) << isig)) { + fHistMan->FillHistClass(Form("QuadrupletMCMatched_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignals[isig]->GetName()), fValuesQuadruplet); + // if (isAmbiguousLepton || (track1.barrelAmbiguityInBunch() > 1) || (track2.barrelAmbiguityInBunch() > 1) || + // (track1.barrelAmbiguityOutOfBunch() > 1) || (track2.barrelAmbiguityOutOfBunch() > 1)) { + // fHistMan->FillHistClass(Form("QuadrupletMCMatchedAmbiguous_%s_%s", fQuadrupletCutNames[iCut].Data(), fRecMCSignals[isig]->GetName()), fValuesQuadruplet); + // } + } + } + } + } else { + if ((track1.sign() < 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() > 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } + } + if ((track1.sign() > 0) && (track2.sign() > 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEPP_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } else if ((track1.sign() < 0) && (track2.sign() < 0)) { + fHistMan->FillHistClass(Form("QuadrupletSEMM_%s", fQuadrupletCutNames[iCut].Data()), fValuesQuadruplet); + } + } + } // end loop over quadruplet cuts + + // fill table + if (!CutDecision) + continue; + if (!mcDecision) + continue; + // if (fConfigML.applyBDT) + // continue; // skip filling table when BDT is applied for now + DileptonTrackTrackTable(fValuesQuadruplet[VarManager::kQuadDefaultDileptonMass], fValuesQuadruplet[VarManager::kQuadPt], fValuesQuadruplet[VarManager::kQuadEta], fValuesQuadruplet[VarManager::kQuadPhi], fValuesQuadruplet[VarManager::kRap], + fValuesQuadruplet[VarManager::kQ], fValuesQuadruplet[VarManager::kDeltaR1], fValuesQuadruplet[VarManager::kDeltaR2], fValuesQuadruplet[VarManager::kDeltaR], + dilepton.mass(), dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.sign(), + lepton1.tpcNSigmaEl(), lepton1.tpcNSigmaPi(), lepton1.tpcNSigmaPr(), lepton1.tpcNClsFound(), + lepton2.tpcNSigmaEl(), lepton2.tpcNSigmaPi(), lepton2.tpcNSigmaPr(), lepton2.tpcNClsFound(), + fValuesQuadruplet[VarManager::kDitrackMass], fValuesQuadruplet[VarManager::kDitrackPt], track1.pt(), track2.pt(), track1.eta(), track2.eta(), track1.phi(), track2.phi(), track1.sign(), track2.sign(), track1.tpcNSigmaPi(), track2.tpcNSigmaPi(), track1.tpcNSigmaKa(), track2.tpcNSigmaKa(), track1.tpcNSigmaPr(), track1.tpcNSigmaPr(), track1.tpcNClsFound(), track2.tpcNClsFound(), + fValuesQuadruplet[VarManager::kKFMass], fValuesQuadruplet[VarManager::kVertexingProcCode], fValuesQuadruplet[VarManager::kVertexingChi2PCA], fValuesQuadruplet[VarManager::kCosPointingAngle], fValuesQuadruplet[VarManager::kKFDCAxyzBetweenProngs], fValuesQuadruplet[VarManager::kKFChi2OverNDFGeo], + fValuesQuadruplet[VarManager::kVertexingLz], fValuesQuadruplet[VarManager::kVertexingLxy], fValuesQuadruplet[VarManager::kVertexingLxyz], fValuesQuadruplet[VarManager::kVertexingTauz], fValuesQuadruplet[VarManager::kVertexingTauxy], fValuesQuadruplet[VarManager::kVertexingLzErr], fValuesQuadruplet[VarManager::kVertexingLxyzErr], + fValuesQuadruplet[VarManager::kVertexingTauzErr], fValuesQuadruplet[VarManager::kVertexingLzProjected], fValuesQuadruplet[VarManager::kVertexingLxyProjected], fValuesQuadruplet[VarManager::kVertexingLxyzProjected], fValuesQuadruplet[VarManager::kVertexingTauzProjected], fValuesQuadruplet[VarManager::kVertexingTauxyProjected]); + } // end loop over associations + } // end loop over dileptons + } + + Preslice> trackAssocsPerCollision = aod::track_association::collisionId; + Preslice dielectronsPerCollision = aod::reducedpair::reducedeventId; + // Preslice ditracksPerCollision = aod::reducedpair::reducedeventId; + + void processX3872(soa::Filtered const& events, BCsWithTimestamps const& bcs, + soa::Join const& assocs, + MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons, + McCollisions const& mcEvents, McParticles const& mcTracks) + { + // set up KF or DCAfitter + if (events.size() == 0) { + return; + } + if (fCurrentRun != bcs.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(bcs.begin().timestamp()); + fCurrentRun = bcs.begin().runNumber(); + } // end: runNumber + for (const auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, bcs, groupedBarrelAssocs, tracks, groupedDielectrons, mcEvents, mcTracks); + } + } + + void processPsi2S(soa::Filtered const& events, BCsWithTimestamps const& bcs, + soa::Join const& assocs, + MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons, + McCollisions const& mcEvents, McParticles const& mcTracks) + { + // set up KF or DCAfitter + if (events.size() == 0) { + return; + } + if (fCurrentRun != bcs.begin().runNumber()) { // start: runNumber + initParamsFromCCDB(bcs.begin().timestamp()); + fCurrentRun = bcs.begin().runNumber(); + } // end: runNumber + for (auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); + auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); + runDileptonTrackTrack(event, bcs, groupedBarrelAssocs, tracks, groupedDielectrons, mcEvents, mcTracks); + } + } + + PresliceUnsorted perReducedMcEvent = aod::mcparticle::mcCollisionId; + + void processMCGen(McParticles const& mcTracks) + { + // loop over mc stack and fill histograms for pure MC truth signals + // group all the MC tracks which belong to the MC event corresponding to the current reconstructed event + // auto groupedMCTracks = tracksMC.sliceBy(aod::reducedtrackMC::reducedMCeventId, event.reducedMCevent().globalIndex()); + for (auto& mctrack : mcTracks) { + VarManager::FillTrackMC(mcTracks, mctrack); + // NOTE: Signals are checked here mostly based on the skimmed MC stack, so depending on the requested signal, the stack could be incomplete. + // NOTE: However, the working model is that the decisions on MC signals are precomputed during skimming and are stored in the mcReducedFlags member. + // TODO: Use the mcReducedFlags to select signals + for (auto& sig : fGenMCSignals) { + if (sig->CheckSignal(true, mctrack)) { + int daughterIdFirst = mctrack.daughtersIds()[0]; + int daughterIdEnd = mctrack.daughtersIds()[1]; + int Ndaughters = daughterIdEnd - daughterIdFirst + 1; + if (Ndaughters == 3) { + auto dilepton = mcTracks.rawIteratorAt(daughterIdFirst); + auto track1 = mcTracks.rawIteratorAt(daughterIdFirst + 1); + auto track2 = mcTracks.rawIteratorAt(daughterIdFirst + 2); + // LOGP(info, "PDG of dilepton: {}, track1: {}, track2: {}", dilepton.pdgCode(), track1.pdgCode(), track2.pdgCode()); + VarManager::FillQuadMC(dilepton, track1, track2); + int daughterIdFirst2 = dilepton.daughtersIds()[0]; + int daughterIdEnd2 = dilepton.daughtersIds()[1]; + int Ndaughters2 = daughterIdEnd2 - daughterIdFirst2 + 1; + if (Ndaughters2 == 2) { + auto lepton1 = mcTracks.rawIteratorAt(daughterIdFirst2); + auto lepton2 = mcTracks.rawIteratorAt(daughterIdFirst2 + 1); + VarManager::FillPairMC(lepton1, lepton2); + // LOGP(info, "PDG of lepton1: {}, lepton2: {}", lepton1.pdgCode(), lepton2.pdgCode()); + if (lepton1.pt() > fConfigDileptonPtMin && lepton2.pt() > fConfigDileptonPtMin) { + if (std::abs(lepton1.eta()) < fConfigDileptonEtaAbs && std::abs(lepton2.eta()) < fConfigDileptonEtaAbs) { + if (track1.pt() > fConfigTrackPtMin && track2.pt() > fConfigTrackPtMin) { + if (std::abs(track1.eta()) < fConfigTrackEtaAbs && std::abs(track2.eta()) < fConfigTrackEtaAbs) { + fHistMan->FillHistClass(Form("MCTruthGenQuadAccepted_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } + } + } + fHistMan->FillHistClass(Form("MCTruthGen_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } + + void processMCGenWithEventSelection(soa::Filtered const& events, + McCollisions const& /*mcEvents*/, McParticles const& mcTracks) + { + for (auto& event : events) { + if (!event.isEventSelected_bit(0)) { + continue; + } + if (!event.has_mcCollision()) { + continue; + } + + auto groupedMCTracks = mcTracks.sliceBy(perReducedMcEvent, event.mcCollisionId()); + groupedMCTracks.bindInternalIndicesTo(&mcTracks); + for (auto& track : groupedMCTracks) { + + VarManager::FillTrackMC(mcTracks, track); + + auto track_raw = mcTracks.rawIteratorAt(track.globalIndex()); + for (auto& sig : fGenMCSignals) { + if (sig->CheckSignal(true, track_raw)) { + int daughterIdFirst = track_raw.daughtersIds()[0]; + int daughterIdEnd = track_raw.daughtersIds()[1]; + int Ndaughters = daughterIdEnd - daughterIdFirst + 1; + if (Ndaughters == 3) { + auto dilepton = mcTracks.rawIteratorAt(daughterIdFirst); + auto track1 = mcTracks.rawIteratorAt(daughterIdFirst + 1); + auto track2 = mcTracks.rawIteratorAt(daughterIdFirst + 2); + VarManager::FillQuadMC(dilepton, track1, track2); + } + fHistMan->FillHistClass(Form("MCTruthGenQuad_%s", sig->GetName()), VarManager::fgValues); + } + } + } + } // end loop over events + } + + void processDummy(MyEvents&) + { + // do nothing + } + + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processPsi2S, "Run psi(2S) -> e+ e- + pi+ pi- pairing, using skimmed data", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processX3872, "Run X(3872) -> J/psi + pi+ pi- pairing, using skimmed data", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGen, "Loop over MC particle stack and fill generator level histograms", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processMCGenWithEventSelection, "Loop over MC particle stack and fill generator level histograms with event selection", false); + PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", true); +}; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { // Initialize metadata for TOF response @@ -3018,10 +3769,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) { // // Define here the histograms for all the classes required in analysis. @@ -3082,10 +3834,28 @@ void DefineHistograms(HistogramManager* histMan, TString histClasses, const char if (classStr.Contains("MCTruthGenPair")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_pair", histName); } - if (classStr.Contains("MCTruthGenPseudoPolPair")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-pseudoproper-gen", histName); + if (classStr.Contains("MCTruthGenPseudoPolPairHE")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-pseudoproper-midy-he-gen", histName); + } + if (classStr.Contains("MCTruthGenPseudoPolPairCS")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-pseudoproper-midy-cs-gen", histName); + } + if (classStr.Contains("MCTruthGenPseudoPolPairRM")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-pseudoproper-midy-rand-gen", histName); + } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairHE")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-he-gen", histName); } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairCS")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-cs-gen", histName); + } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairGJB1")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-gjb1-gen", histName); + } + if (classStr.Contains("MCTruthGenPoldielectronPbPbPairGJB2")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "polarization-dielectron-pbpb-midy-gjb2-gen", histName); + } if (classStr.Contains("MCTruthGenSelBR")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_triple"); } else if (classStr.Contains("MCTruthGen")) { @@ -3096,6 +3866,10 @@ void DefineHistograms(HistogramManager* histMan, TString histClasses, const char // dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_track"); // } + if (classStr.Contains("MCTruthGenQuad")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "mctruth_quad", histName); + } + if (classStr.Contains("DileptonsSelected")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", histName); } @@ -3119,5 +3893,9 @@ void DefineHistograms(HistogramManager* histMan, TString histClasses, const char if (classStr.Contains("DileptonHadronCorrelation")) { dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-hadron-correlation"); } + + if (classStr.Contains("Quadruplet")) { + dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-dihadron", histName); + } } // end loop over histogram classes } diff --git a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx index b7b64010f6a..ed8ea9265bb 100644 --- a/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx +++ b/PWGDQ/Tasks/dqEnergyCorrelator_direct.cxx @@ -52,6 +52,7 @@ #include +#include #include #include #include @@ -83,7 +84,7 @@ constexpr static uint32_t gkEventFillMapWithMults = VarManager::ObjTypes::BC | V constexpr static uint32_t gkTrackFillMapWithCov = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackCov | VarManager::ObjTypes::TrackPID; // Forward declarations -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups); // defines histograms for all tasks struct AnalysisEnergyCorrelator { OutputObj fOutputList{"output"}; @@ -129,11 +130,12 @@ struct AnalysisEnergyCorrelator { Configurable fConfigAddDileptonHadronHistogram{"cfgAddDileptonHadronHistogram", "", "Dilepton-hadron histograms"}; Configurable fConfigMCRecSignals{"cfgMCRecDileptonHadronSignals", "", "Comma separated list of MC signals (reconstructed)"}; Configurable fConfigMCGenSignals{"cfgMCGenDileptonHadronSignals", "", "Comma separated list of MC signals (generated)"}; + Configurable fConfigMCGenPairSignals{"cfgMCGenDileptonHadronPairSignals", "", "Comma separated list of MC pair signals (generated)"}; Configurable fConfigMCRecSignalsJSON{"cfgMCRecDileptonHadronSignalsJSON", "", "Additional list of MC signals (reconstructed) via JSON"}; Configurable fConfigMCGenSignalsJSON{"cfgMCGenDileptonHadronSignalsJSON", "", "Comma separated list of MC signals (generated) via JSON"}; + Configurable fConfigMCGenPairSignalsJSON{"cfgMCGenDileptonHadronPairSignalsJSON", "", "Comma separated list of MC pair signals (generated) via JSON"}; Configurable fConfigMCGenHadronEtaAbs{"cfgMCGenHadronEtaAbs", 0.9f, "eta abs range for the hadron"}; Configurable fConfigMCGenHadronPtMin{"cfgMCGenHadronPtMin", 0.1f, "minimum pt for the hadron"}; - Configurable fConfigContainlepton{"cfgContainlepton", false, "If true, require the hadron to contain the lepton in its decay tree for the energy correlator study"}; Configurable fConfigUsePionMass{"cfgUsePionMass", false, "If true, use pion mass for the hadron in the energy correlator study"}; Configurable fConfigApplyEfficiency{"cfgApplyEfficiency", false, "If true, apply efficiency correction for the energy correlator study"}; Configurable fConfigApplyEfficiencyME{"cfgApplyEfficiencyME", false, "If true, apply efficiency correction for the energy correlator study"}; @@ -166,6 +168,7 @@ struct AnalysisEnergyCorrelator { std::vector fRecMCTrackSignals; // MC signals for reconstructed tracks std::vector fRecMCSignals; // MC signals for reconstructed pairs std::vector fGenMCSignals; + std::vector fGenMCPairSignals; std::vector fRecMCTripleSignals; // MC signals for reconstructed triples Service fCCDB; @@ -182,6 +185,7 @@ struct AnalysisEnergyCorrelator { TH2F* hEfficiency_dilepton; TH2F* hEfficiency_hadron; TH1F* hMasswindow; + TH2F* hReweighthadron; void init(o2::framework::InitContext& context) { @@ -191,7 +195,7 @@ struct AnalysisEnergyCorrelator { std::vector multBins = fConfigEventOptions.fConfigMultBins.value; multBins.insert(multBins.begin(), VARIABLE_WIDTH); - fMixingBinning = std::make_unique(std::array, 2>{zBins, multBins}, true); + fMixingBinning = std::make_unique(std::array, 2>{zBins, multBins}); bool isBarrelME = context.mOptions.get("processBarrelMixedEvent"); bool isMCGen_energycorrelators = context.mOptions.get("processMCGenEnergyCorrelators") || context.mOptions.get("processMCGenEnergyCorrelatorsPion"); @@ -234,7 +238,7 @@ struct AnalysisEnergyCorrelator { if (trackCutsJSON != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(trackCutsJSON.Data()); for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); fTrackCutNames.push_back(t->GetName()); trackCutStr += Form(",%s", t->GetName()); } @@ -307,7 +311,7 @@ struct AnalysisEnergyCorrelator { if (hadronCutsJSON != "") { std::vector addHadronCuts = dqcuts::GetCutsFromJSON(hadronCutsJSON.Data()); for (auto& t : addHadronCuts) { - fHadronCuts.push_back(reinterpret_cast(t)); + fHadronCuts.push_back(dynamic_cast(t)); fHadronCutNames.push_back(t->GetName()); hadronCutStr += Form(",%s", t->GetName()); } @@ -336,6 +340,29 @@ struct AnalysisEnergyCorrelator { } } + // Add histogram classes for each specified MCsignal at the generator level + // TODO: create a std::vector of hist classes to be used at Fill time, to avoid using Form in the process function + TString sigpairGenNamesStr = fConfigDileptonHadronOptions.fConfigMCGenPairSignals.value; + std::unique_ptr objGenPairSigArray(sigpairGenNamesStr.Tokenize(",")); + for (int isig = 0; isig < objGenPairSigArray->GetEntries(); isig++) { + MCSignal* sig = o2::aod::dqmcsignals::GetMCSignal(objGenPairSigArray->At(isig)->GetName()); + if (sig) { + fGenMCPairSignals.push_back(sig); + } + } + + // Add the MCSignals from the JSON config + TString addMCSignalsPairGenStr = fConfigDileptonHadronOptions.fConfigMCGenPairSignalsJSON.value; + if (addMCSignalsPairGenStr != "") { + std::vector addMCSignals = dqmcsignals::GetMCSignalsFromJSON(addMCSignalsPairGenStr.Data()); + for (auto& mcIt : addMCSignals) { + if (mcIt->GetNProngs() != 2) { // NOTE: only 2 prong signals + continue; + } + fGenMCPairSignals.push_back(mcIt); + } + } + // TODO: create a std::vector of hist classes to be used at Fill time, to avoid using Form in the process function TString sigRecTrackNamesStr = fConfigTrackOptions.fConfigMCRecTrackSignals.value; std::unique_ptr objRecTrackSigArray(sigRecTrackNamesStr.Tokenize(",")); @@ -439,6 +466,14 @@ struct AnalysisEnergyCorrelator { if (sig->GetNProngs() == 1) { if (isMCGen_energycorrelators) { DefineHistograms(fHistMan, Form("MCTruthGenSel_%s", sig->GetName()), ""); + } + } + } + + for (auto& sig : fGenMCPairSignals) { + LOG(info) << "Defining histograms for pair signal: " << sig->GetNProngs(); + if (sig->GetNProngs() == 2) { + if (isMCGen_energycorrelators) { DefineHistograms(fHistMan, Form("MCTruthEenergyCorrelators_%s", sig->GetName()), ""); DefineHistograms(fHistMan, Form("MCTruthEenergyCorrelators_Pion_%s", sig->GetName()), ""); } @@ -466,18 +501,36 @@ struct AnalysisEnergyCorrelator { if (!listAccs) { LOG(fatal) << "Problem getting TList object with efficiencies!"; } - hEfficiency_dilepton = static_cast(listAccs->FindObject("hEfficiency_dilepton")); - hEfficiency_hadron = static_cast(listAccs->FindObject("hEfficiency_hadron")); - hAcceptance_rec = static_cast(listAccs->FindObject("hAcceptance_rec")); - hAcceptance_gen = static_cast(listAccs->FindObject("hAcceptance_gen")); - hMasswindow = static_cast(listAccs->FindObject("hMasswindow")); - if (!hAcceptance_rec || !hAcceptance_gen || !hEfficiency_dilepton || !hEfficiency_hadron || !hMasswindow) { + hEfficiency_dilepton = dynamic_cast(listAccs->FindObject("hEfficiency_dilepton")); + hEfficiency_hadron = dynamic_cast(listAccs->FindObject("hEfficiency_hadron")); + hAcceptance_rec = dynamic_cast(listAccs->FindObject("hAcceptance_rec")); + hAcceptance_gen = dynamic_cast(listAccs->FindObject("hAcceptance_gen")); + hMasswindow = dynamic_cast(listAccs->FindObject("hMasswindow")); + hReweighthadron = dynamic_cast(listAccs->FindObject("hReweighthadron")); + if (!hAcceptance_rec || !hAcceptance_gen || !hEfficiency_dilepton || !hEfficiency_hadron || !hMasswindow || !hReweighthadron) { LOG(fatal) << "Problem getting histograms from the TList object with efficiencies!"; } } + + float GetSafeInterpolationWeight(TH2* hEff, float x, float y) + { + if (!hEff) + return 1.0; + float minX = hEff->GetXaxis()->GetBinCenter(1); + float maxX = hEff->GetXaxis()->GetBinCenter(hEff->GetXaxis()->GetNbins()); + + float minY = hEff->GetYaxis()->GetBinCenter(1); + float maxY = hEff->GetYaxis()->GetBinCenter(hEff->GetYaxis()->GetNbins()); + + float safeX = std::max(minX, std::min(x, maxX)); + float safeY = std::max(minY, std::min(y, maxY)); + + return hEff->Interpolate(safeX, safeY); + } + template void runDileptonHadron(TTrack1 const& track1, TTrack2 const& track2, int iEleCut, - THadron const& hadron, TEvent const& event, aod::McParticles const& /*mcParticles*/) + THadron const& hadron, TEvent const& dileptonEvent, TEvent const& event, aod::McParticles const& /*mcParticles*/) { // Check that hadron is not one of the dilepton legs @@ -503,21 +556,35 @@ struct AnalysisEnergyCorrelator { float Effweight_rec = 1.0f; float Accweight_gen = 1.0f; if (fConfigDileptonHadronOptions.fConfigApplyEfficiency) { + VarManager::FillPair(track1, track2, VarManager::fgValues); + float dilepton_pt = VarManager::fgValues[VarManager::kPt]; float dilepton_eta = VarManager::fgValues[VarManager::kEta]; float dilepton_phi = VarManager::fgValues[VarManager::kPhi]; + float dilepton_rap = VarManager::fgValues[VarManager::kRap]; float hadron_eta = hadron.eta(); float hadron_phi = hadron.phi(); float deltaphi = RecoDecay::constrainAngle(dilepton_phi - hadron_phi, -0.5 * o2::constants::math::PI); - Effweight_rec = hAcceptance_rec->Interpolate(dilepton_eta - hadron_eta, deltaphi); - Accweight_gen = hAcceptance_gen->Interpolate(dilepton_eta - hadron_eta, deltaphi); - float Effdilepton = hEfficiency_dilepton->Interpolate(VarManager::fgValues[VarManager::kPt], dilepton_eta); - float Masswindow = hMasswindow->Interpolate(VarManager::fgValues[VarManager::kPt]); - float Effhadron = hEfficiency_hadron->Interpolate(hadron.pt(), hadron.eta()); - Accweight_gen = Accweight_gen * Effdilepton * Effhadron; + Effweight_rec = GetSafeInterpolationWeight(hAcceptance_rec, dilepton_eta - hadron_eta, deltaphi); + Accweight_gen = GetSafeInterpolationWeight(hAcceptance_gen, dilepton_eta - hadron_eta, deltaphi); + float Effdilepton = GetSafeInterpolationWeight(hEfficiency_dilepton, dilepton_rap, dilepton_pt); + float Effhadron = GetSafeInterpolationWeight(hEfficiency_hadron, hadron_eta, hadron.pt()); + float Masswindow = hMasswindow->Interpolate(dilepton_pt); + float Reweighthadron = 1.0f; + if (std::abs(hadronMC.pdgCode()) == PDG_t::kPiPlus) { + int bin = hReweighthadron->FindBin(0, hadron.pt()); + Reweighthadron = hReweighthadron->GetBinContent(bin); + } else if (std::abs(hadronMC.pdgCode()) == PDG_t::kProton) { + int bin = hReweighthadron->FindBin(1, hadron.pt()); + Reweighthadron = hReweighthadron->GetBinContent(bin); + } else if (std::abs(hadronMC.pdgCode()) == PDG_t::kKPlus) { + int bin = hReweighthadron->FindBin(2, hadron.pt()); + Reweighthadron = hReweighthadron->GetBinContent(bin); + } + Accweight_gen = Accweight_gen * Effdilepton * Effhadron * Reweighthadron; if (fConfigDileptonHadronOptions.fConfigApplyEfficiencyME) { - Effweight_rec = Effdilepton * Effhadron * Masswindow; // for the moment, apply the efficiency correction also for the mixed event pairs, but this can be changed in case we want to apply it only for the same event pairs + Effweight_rec = Effdilepton * Effhadron * Masswindow * Reweighthadron; // for the moment, apply the efficiency correction also for the mixed event pairs, but this can be changed in case we want to apply it only for the same event pairs } else { - Effweight_rec = Effweight_rec * Effdilepton * Effhadron * Masswindow; // apply acceptance and efficiency correction for the real pairs + Effweight_rec = Effweight_rec * Effdilepton * Effhadron * Masswindow * Reweighthadron; // apply acceptance and efficiency correction for the real pairs } } @@ -528,7 +595,7 @@ struct AnalysisEnergyCorrelator { // Fill dilepton-hadron variables std::vector fTransRange = fConfigDileptonHadronOptions.fConfigTransRange; - VarManager::FillEnergyCorrelatorTriple(track1, track2, hadron, VarManager::fgValues, fTransRange[0], fTransRange[1], fConfigDileptonHadronOptions.fConfigApplyMassEC.value, -1, 1. / Effweight_rec); + VarManager::FillEnergyCorrelatorTriple(dileptonEvent, track1, track2, hadron, VarManager::fgValues, fTransRange[0], fTransRange[1], fConfigDileptonHadronOptions.fConfigApplyMassEC.value, -1, 1. / Effweight_rec); if (fConfigDileptonHadronOptions.fConfigUsePionMass.value) { VarManager::FillEnergyCorrelatorsUnfoldingTriple(track1, track2, hadron, motherParticle, hadronMC, VarManager::fgValues, fConfigDileptonHadronOptions.fConfigApplyMassEC.value, 1. / Effweight_rec, 1. / Accweight_gen, fTransRange[0], fTransRange[1]); } else { @@ -760,7 +827,7 @@ struct AnalysisEnergyCorrelator { // Process dilepton-hadron correlation for each common cut for (size_t iCut = 0; iCut < fTrackCuts.size(); iCut++) { if (twoTrackFilter & (static_cast(1) << iCut)) { - runDileptonHadron(t1, t2, iCut, hadron, event, mcParticles); + runDileptonHadron(t1, t2, iCut, hadron, event, event, mcParticles); } } } // end hadron loop @@ -879,7 +946,7 @@ struct AnalysisEnergyCorrelator { // Process dilepton-hadron correlation for each common cut for (size_t iCut = 0; iCut < fTrackCuts.size(); iCut++) { if (twoTrackFilter & (static_cast(1) << iCut)) { - runDileptonHadron(t1, t2, iCut, hadron, event2, mcParticles); + runDileptonHadron(t1, t2, iCut, hadron, event1, event2, mcParticles); } } } // end hadron loop @@ -916,25 +983,16 @@ struct AnalysisEnergyCorrelator { continue; // for the energy correlators for (auto& t2 : groupedMCTracks2) { - auto t2_raw = groupedMCTracks2.rawIteratorAt(t2.globalIndex()); + auto t2_raw = mcTracks.rawIteratorAt(t2.globalIndex()); if (t2.mcCollisionId() != event2.mcCollisionId()) { // check that the mc track belongs to the same mc collision as the reconstructed event continue; } - if (!t2_raw.isPhysicalPrimary()) { - continue; - } if (t2_raw.has_mothers()) { auto mother_raw = t2_raw.template mothers_first_as(); if (mother_raw.globalIndex() == t1_raw.globalIndex()) { continue; } } - if (fConfigDileptonHadronOptions.fConfigContainlepton && std::abs(t2_raw.pdgCode()) != PDG_t::kPiPlus && std::abs(t2_raw.pdgCode()) != PDG_t::kKPlus && std::abs(t2_raw.pdgCode()) != PDG_t::kProton && std::abs(t2_raw.pdgCode()) != PDG_t::kElectron && std::abs(t2_raw.pdgCode()) != PDG_t::kMuonMinus) { - continue; - } - if (!fConfigDileptonHadronOptions.fConfigContainlepton && std::abs(t2_raw.pdgCode()) != PDG_t::kPiPlus && std::abs(t2_raw.pdgCode()) != PDG_t::kKPlus && std::abs(t2_raw.pdgCode()) != PDG_t::kProton) { - continue; - } if (t2_raw.pt() < fConfigDileptonHadronOptions.fConfigMCGenHadronPtMin.value || std::abs(t2_raw.eta()) > fConfigDileptonHadronOptions.fConfigMCGenHadronEtaAbs.value) { continue; } @@ -946,11 +1004,24 @@ struct AnalysisEnergyCorrelator { float hadron_phi = t2_raw.phi(); float deltaphi = RecoDecay::constrainAngle(dilepton_phi - hadron_phi, -0.5 * o2::constants::math::PI); acceptance = hAcceptance_gen->Interpolate(dilepton_eta - hadron_eta, deltaphi); + float Reweighthadron = 1.0f; + if (std::abs(t2_raw.pdgCode()) == PDG_t::kPiPlus) { + int bin = hReweighthadron->FindBin(0, t2_raw.pt()); + Reweighthadron = hReweighthadron->GetBinContent(bin); + } else if (std::abs(t2_raw.pdgCode()) == PDG_t::kProton) { + int bin = hReweighthadron->FindBin(1, t2_raw.pt()); + Reweighthadron = hReweighthadron->GetBinContent(bin); + } else if (std::abs(t2_raw.pdgCode()) == PDG_t::kKPlus) { + int bin = hReweighthadron->FindBin(2, t2_raw.pt()); + Reweighthadron = hReweighthadron->GetBinContent(bin); + } + acceptance = acceptance * Reweighthadron; } std::vector fTransRange = fConfigDileptonHadronOptions.fConfigTransRange; VarManager::FillEnergyCorrelatorsMC(t1_raw, t2_raw, VarManager::fgValues, fTransRange[0], fTransRange[1], 1. / acceptance); - for (auto& sig : fGenMCSignals) { - if (sig->CheckSignal(true, t1_raw)) { + for (auto& sig : fGenMCPairSignals) { + + if (sig->CheckSignal(true, t1_raw, t2_raw)) { if (!MixedEvent && !PionMass) { fHistMan->FillHistClass(Form("MCTruthEenergyCorrelators_%s", sig->GetName()), VarManager::fgValues); } @@ -1112,7 +1183,7 @@ struct AnalysisEnergyCorrelator { }; // Histogram definitions -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) { std::unique_ptr objArray(histClasses.Tokenize(";")); for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { diff --git a/PWGDQ/Tasks/dqFlow.cxx b/PWGDQ/Tasks/dqFlow.cxx index 7b2232dcaff..31865fcd0d4 100644 --- a/PWGDQ/Tasks/dqFlow.cxx +++ b/PWGDQ/Tasks/dqFlow.cxx @@ -100,7 +100,7 @@ constexpr static uint32_t gkEventFillMapRun3 = VarManager::ObjTypes::BC | VarMan constexpr static uint32_t gkEventFillMapRun3Qvect = VarManager::ObjTypes::BC | VarManager::ObjTypes::Collision | VarManager::ObjTypes::CollisionCent | VarManager::ObjTypes::CollisionQvectCentr; constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackPID; -void DefineHistograms(HistogramManager* histMan, TString histClasses); +void DefineHistograms(HistogramManager* histMan, const TString& histClasses); struct DQEventQvector { @@ -142,7 +142,7 @@ struct DQEventQvector { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100.1}, "multiplicity / centrality axis for histograms"}; // Define the filter for barrel tracks and forward tracks - Filter trackFilter = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true); + Filter trackFilter = (requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true)); Filter fwdFilter = (aod::fwdtrack::eta < -2.45f) && (aod::fwdtrack::eta > -3.6f); // Histograms used for optionnal efficiency and non-uniform acceptance corrections @@ -353,6 +353,53 @@ struct DQEventQvector { // } } + // Templated function instantianed for all of the process functions + template + void runFillQvectorFromCentralFWWithFT0CCumulants(TEvent const& collision, TFT0s const& /*ft0s*/) + { + VarManager::ResetValues(0, VarManager::kNVars); + VarManager::FillEvent(collision); + + float S11C = collision.sumAmplFT0C(); + float S12C = 0.f; + float S21C = S11C * S11C; + + // Compute sum of squares of amplitudes from FT0C for flow analysis + if (collision.has_foundFT0()) { + auto ft0 = collision.foundFT0(); + auto const& ampC = ft0.amplitudeC(); + for (auto amp : ampC) { + if (amp > 0.f) { + S12C += amp * amp; + } + } + } + VarManager::FillQVectorFromCentralFW(collision); + std::complex Q21C(collision.qvecFT0CRe() * S11C, collision.qvecFT0CIm() * S11C); + + // Fill necessary quantities for cumulant calculations with weighted Q-vectors + float M11REF = S21C - S12C; + float CORR2REF = (norm(Q21C) - S12C) / M11REF; + CORR2REF = std::isnan(M11REF) || std::isinf(M11REF) || std::isnan(CORR2REF) || std::isinf(CORR2REF) ? 0 : CORR2REF; + M11REF = std::isnan(M11REF) || std::isinf(M11REF) || std::isnan(CORR2REF) || std::isinf(CORR2REF) ? 0 : M11REF; + + if (fConfigQA) { + fHistMan->FillHistClass("Event_BeforeCuts_centralFW", VarManager::fgValues); + if (fEventCut->IsSelected(VarManager::fgValues)) { + fHistMan->FillHistClass("Event_AfterCuts_centralFW", VarManager::fgValues); + } + } + + if (fEventCut->IsSelected(VarManager::fgValues)) { + // temporarily removed --> this table is filled in the table-maker directly + // eventQvectorCentr(collision.qvecFT0ARe(), collision.qvecFT0AIm(), collision.qvecFT0CRe(), collision.qvecFT0CIm(), collision.qvecFT0MRe(), collision.qvecFT0MIm(), collision.qvecFV0ARe(), collision.qvecFV0AIm(), collision.qvecTPCposRe(), collision.qvecTPCposIm(), collision.qvecTPCnegRe(), collision.qvecTPCnegIm(), + // collision.sumAmplFT0A(), collision.sumAmplFT0C(), collision.sumAmplFT0M(), collision.sumAmplFV0A(), collision.nTrkTPCpos(), collision.nTrkTPCneg()); + // eventQvectorCentrExtra(collision.qvecTPCallRe(), collision.qvecTPCallIm(), collision.nTrkTPCall()); + eventRefFlow(M11REF, -9999, -9999, CORR2REF, -9999, -9999, VarManager::fgValues[VarManager::kCentFT0C]); + eventQvectorExtra(-9999, -9999, -9999, -9999, S11C, S12C, -9999, -9999); + } + } + // Templated function instantianed for all of the process functions template void runFillQvector(TEvent const& collision, MyBcs const&, TTracks const& tracks1, aod::Zdcs const&) @@ -576,6 +623,12 @@ struct DQEventQvector { runFillQvectorFromCentralFW(collisions); } + // Process to fill Q vector using barrel tracks in a reduced event table for barrel/muon tracks flow related analyses Run 3 + void processCentralQvectorWithFT0CCumulants(MyEventsWithCentQvectRun3::iterator const& collision, aod::FT0s const& ft0s) + { + runFillQvectorFromCentralFWWithFT0CCumulants(collision, ft0s); + } + // Process to fill Q vector using forward tracks in a reduced event table for barrel/muon tracks flow related analyses Run 3 void processForwardQvector(MyEventsWithCentRun3::iterator const& collisions, MyBcs const& bcs, soa::Filtered const& tracks, aod::Zdcs const& zdcs) { @@ -592,6 +645,7 @@ struct DQEventQvector { PROCESS_SWITCH(DQEventQvector, processBarrelQvector, "Run q-vector task on barrel tracks for Run3", false); PROCESS_SWITCH(DQEventQvector, processAllQvector, "Run q-vector task on barrel tracks for Run3 and using central q-vector", false); PROCESS_SWITCH(DQEventQvector, processCentralQvector, "Run q-vector task using central q-vector", false); + PROCESS_SWITCH(DQEventQvector, processCentralQvectorWithFT0CCumulants, "Run q-vector task using central q-vector with FT0C cumulants", false); PROCESS_SWITCH(DQEventQvector, processForwardQvector, "Run q-vector task on forward tracks for Run3", false); PROCESS_SWITCH(DQEventQvector, processDummy, "Dummy function", false); }; @@ -602,7 +656,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/filterPP.cxx b/PWGDQ/Tasks/filterPP.cxx index 1bfa487594e..71e59bd6f32 100644 --- a/PWGDQ/Tasks/filterPP.cxx +++ b/PWGDQ/Tasks/filterPP.cxx @@ -106,7 +106,7 @@ constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::BC | VarManager constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackSelection | VarManager::ObjTypes::TrackPID; constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::Muon; -void DefineHistograms(HistogramManager* histMan, TString histClasses); +void DefineHistograms(HistogramManager* histMan, const TString& histClasses); struct DQEventSelectionTask { Produces eventSel; @@ -521,7 +521,7 @@ struct DQFilterPPTask { std::vector objCountersBarrel(fNBarrelCuts, 0); // init all counters to zero // count the number of barrel tracks fulfilling each cut - for (auto track : tracksBarrel) { + for (const auto& track : tracksBarrel) { for (int i = 0; i < fNBarrelCuts; ++i) { if (track.isDQBarrelSelected() & (static_cast(1) << i)) { objCountersBarrel[i] += 1; @@ -585,7 +585,7 @@ struct DQFilterPPTask { std::vector objCountersMuon(fNMuonCuts, 0); // init all counters to zero // count the number of muon tracks fulfilling each selection - for (auto muon : muons) { + for (const auto& muon : muons) { for (int i = 0; i < fNMuonCuts; ++i) { if (muon.isDQMuonSelected() & (static_cast(1) << i)) { objCountersMuon[i] += 1; @@ -701,7 +701,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/filterPPwithAssociation.cxx b/PWGDQ/Tasks/filterPPwithAssociation.cxx index cf7c18ce8f6..a338691417b 100644 --- a/PWGDQ/Tasks/filterPPwithAssociation.cxx +++ b/PWGDQ/Tasks/filterPPwithAssociation.cxx @@ -123,7 +123,7 @@ constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::Track | VarMana constexpr static uint32_t gkTrackFillMapTPCPID = VarManager::ObjTypes::Track | VarManager::ObjTypes::TrackExtra | VarManager::ObjTypes::TrackDCA | VarManager::ObjTypes::TrackTPCPID; constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::Muon | VarManager::ObjTypes::MuonCov; -void DefineHistograms(HistogramManager* histMan, TString histClasses, TString subgroups = ""); +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const TString& subgroups = ""); template void PrintBitMap(TMap map, int nbits) @@ -720,7 +720,7 @@ struct DQFilterPPTask { uint32_t pairFilter = 0; // count the number of barrel tracks fulfilling each cut if constexpr (static_cast(TTrackFillMap)) { - for (auto trackAssoc : barrelAssocs) { + for (const auto& trackAssoc : barrelAssocs) { for (int i = 0; i < fNBarrelCuts; ++i) { if (trackAssoc.isDQBarrelSelected() & (static_cast(1) << i)) { objCountersBarrel[i] += 1; @@ -1227,7 +1227,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, TString subgroups) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const TString& subgroups) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/global-muon-matcher.cxx b/PWGDQ/Tasks/global-muon-matcher.cxx new file mode 100644 index 00000000000..4125dabbd9c --- /dev/null +++ b/PWGDQ/Tasks/global-muon-matcher.cxx @@ -0,0 +1,1903 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file global-muon-matcher.cxx // o2-linter: disable=name/file-cpp,name/workflow-file (legacy workflow executable name) +/// \brief Task for analysis MFT-MCH muon matching +/// \author Andrea Ferrero +/// +#include "PWGDQ/Core/MuonMatchingMlResponse.h" +#include "PWGDQ/Core/VarManager.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/fwdtrackUtilities.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FwdTrackReAlignTables.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod; + +namespace o2::aod +{ +namespace globalmuonmatching +{ +DECLARE_SOA_COLUMN(IsTagged, isTagged, bool); //! Whether the MCH(-MID) track passes tagging cuts +DECLARE_SOA_COLUMN(MatchRanking, matchRanking, int32_t); //! Match candidate ranking (-1 for base MCH entries) +DECLARE_SOA_COLUMN(MixedGroupIndex, mixedGroupIndex, int32_t); //! Mixed-event group index (-1 for same-event candidates) +DECLARE_SOA_COLUMN(DeltaBc, deltaBc, int64_t); //! |ΔBC| between mixed collisions (-1 if same-event) +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); //! |Δφ| between mixed MCH tracks (-1 if same-event) +DECLARE_SOA_COLUMN(DeltaR, deltaR, float); //! ΔR = r2−r1 between mixed MCH tracks (-1 if same-event) +DECLARE_SOA_COLUMN(DeltaAttemptsRel, deltaAttemptsRel, float); //! relative Δ(nMatchAttempts) (-1 if same-event) +DECLARE_SOA_COLUMN(DeltaZ, deltaZ, float); //! |Δvz| between mixed collisions (-1 if same-event) +} // namespace globalmuonmatching + +DECLARE_SOA_TABLE(GmmCandFwdTrkExtras, "AOD", "GMMCANDEXTRA", //! Extra info joinable to FwdTracksReAlign + globalmuonmatching::IsTagged, + globalmuonmatching::MatchRanking, + globalmuonmatching::MixedGroupIndex, + globalmuonmatching::DeltaBc, + globalmuonmatching::DeltaPhi, + globalmuonmatching::DeltaR, + globalmuonmatching::DeltaAttemptsRel, + globalmuonmatching::DeltaZ); +} // namespace o2::aod + +using MyEvents = soa::Join; +using MyMuons = soa::Join; +using MyMFTs = aod::MFTTracks; +using MyMFTCovariances = aod::MFTTracksCov; + +using SMatrix55Sym = o2::track::SMatrix55Sym; +using SMatrix55Std = o2::track::SMatrix55Std; +using SMatrix5 = o2::track::SMatrix5; + +constexpr std::array NDetElemCh = {4, 4, 4, 4, 18, 18, 26, 26, 26, 26}; +constexpr std::array SNDetElemCh = {0, 4, 8, 12, 16, 34, 52, 78, 104, 130, 156}; + +// compute minimum difference between azimuthal angles +static float getDeltaPhi(float phi1, float phi2) +{ + return RecoDecay::constrainAngle(phi1 - phi2, -o2::constants::math::PI); +} + +struct GlobalMuonMatching { + + static constexpr int GlobalTrackTypeMax = 2; + static constexpr int MchMidTrackType = 3; + static constexpr int NMchChambers = 10; + static constexpr int MchDetElemNumberingBase = 100; + static constexpr int NMchDetElems = 156; + static constexpr int MinRemovableTrackClusters = 10; + static constexpr int ThetaAbsBoundaryDeg = 3; + static constexpr double SlopeResolutionZ = 535.; + static constexpr float MatchingPlaneDefaultZ = -77.5; + + struct MatchingCandidate { + int64_t muonTrackId{-1}; + int64_t mftTrackId{-1}; + double matchScore{-1}; + double matchChi2{-1}; + int matchRanking{-1}; + int32_t mixedGroupIndex{-1}; + int64_t deltaBc{-1}; + float deltaPhi{-1.f}; + float deltaR{-1.f}; + float deltaAttemptsRel{-1.f}; + float deltaZ{-1.f}; + }; + + struct MchTrackInfo { + int nMatchAttempts{-1}; + bool isTagged{false}; + // vector of MFT-MCH matching candidates + std::vector matchingCandidates; + // vector of vectors of MFT-MCH matching candidates from mixed events + std::vector> mixedMatchingCandidates; + }; + + //// Variables for selecting tagged muons + struct : ConfigurableGroup { + Configurable cfgMuonTaggingNCrossedMftPlanesLow{"cfgMuonTaggingNCrossedMftPlanesLow", 5, ""}; + Configurable cfgMuonTaggingTrackChi2MchUp{"cfgMuonTaggingTrackChi2MchUp", 5.f, ""}; + Configurable cfgMuonTaggingPMchLow{"cfgMuonTaggingPMchLow", 0.0f, ""}; + Configurable cfgMuonTaggingPtMchLow{"cfgMuonTaggingPtMchLow", 0.7f, ""}; + Configurable cfgMuonTaggingEtaMchLow{"cfgMuonTaggingEtaMchLow", -3.6f, ""}; + Configurable cfgMuonTaggingEtaMchUp{"cfgMuonTaggingEtaMchUp", -2.5f, ""}; + Configurable cfgMuonTaggingRabsLow{"cfgMuonTaggingRabsLow", 17.6f, ""}; + Configurable cfgMuonTaggingRabsUp{"cfgMuonTaggingRabsUp", 89.5f, ""}; + Configurable cfgMuonTaggingPdcaUp{"cfgMuonTaggingPdcaUp", 4.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftFrontLow{"cfgMuonTaggingRadiusAtMftFrontLow", 3.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftFrontUp{"cfgMuonTaggingRadiusAtMftFrontUp", 9.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftBackLow{"cfgMuonTaggingRadiusAtMftBackLow", 5.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftBackUp{"cfgMuonTaggingRadiusAtMftBackUp", 12.f, ""}; + } configMuonTagging; + + //// Variables for MCH realignment + struct : ConfigurableGroup { + Configurable cfgEnableMCHRealign{"cfgEnableMCHRealign", true, "Enable re-alignment of MCH clusters and tracks"}; + Configurable cfgGeoRefPath{"cfgGeoRefPath", "GLO/Config/GeometryAligned", "Path of the reference geometry file"}; + Configurable cfgGeoNewPath{"cfgGeoNewPath", "GLO/Config/GeometryAligned", "Path of the new geometry file"}; + Configurable cfgCcdbNoLaterThanRef{"cfgCcdbNoLaterThanRef", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of reference basis"}; + Configurable cfgCcdbNoLaterThanNew{"cfgCcdbNoLaterThanNew", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of new basis"}; + Configurable cfgChamberResolutionX{"cfgChamberResolutionX", 0.04, "Chamber resolution along X configuration for refit"}; // 0.4cm pp, 0.2cm PbPb + Configurable cfgChamberResolutionY{"cfgChamberResolutionY", 0.04, "Chamber resolution along Y configuration for refit"}; // 0.4cm pp, 0.2cm PbPb + Configurable cfgSigmaCutImprove{"cfgSigmaCutImprove", 6., "Sigma cut for track improvement"}; // 6 for pp, 4 for PbPb + } configMchRealign; + + //// Variables for MFT alignment corrections + struct : ConfigurableGroup { + Configurable cfgEnableMftAlignmentCorrections{"cfgEnableMftAlignmentCorrections", true, "Enable alignment corrections for the MFT tracks"}; + // slope corrections + // Configurable cfgMFTAlignmentCorrXSlopeTop{"cfgMFTAlignmentCorrXSlopeTop", (-0.0006696 - 0.0005621) / 2.f, "MFT X slope correction - top half"}; + // Configurable cfgMFTAlignmentCorrXSlopeBottom{"cfgMFTAlignmentCorrXSlopeBottom", (0.00105 + 0.001007) / 2.f, "MFT X slope correction - bottom half"}; + // Configurable cfgMFTAlignmentCorrYSlopeTop{"cfgMFTAlignmentCorrYSlopeTop", (-0.002299 - 0.002442) / 2.f, "MFT Y slope correction - top half"}; + // Configurable cfgMFTAlignmentCorrYSlopeBottom{"cfgMFTAlignmentCorrYSlopeBottom", (-0.0005339 - 0.0006921) / 2.f, "MFT Y slope correction - bottom half"}; + Configurable cfgMFTAlignmentCorrXSlopeTop{"cfgMFTAlignmentCorrXSlopeTop", 0.f, "MFT X slope correction - top half"}; + Configurable cfgMFTAlignmentCorrXSlopeBottom{"cfgMFTAlignmentCorrXSlopeBottom", 0.f, "MFT X slope correction - bottom half"}; + Configurable cfgMFTAlignmentCorrYSlopeTop{"cfgMFTAlignmentCorrYSlopeTop", 0.f, "MFT Y slope correction - top half"}; + Configurable cfgMFTAlignmentCorrYSlopeBottom{"cfgMFTAlignmentCorrYSlopeBottom", 0.f, "MFT Y slope correction - bottom half"}; + // offset corrections + Configurable cfgMFTAlignmentCorrXOffsetTop{"cfgMFTAlignmentCorrXOffsetTop", 0.f, "MFT X offset correction - top half"}; + Configurable cfgMFTAlignmentCorrXOffsetBottom{"cfgMFTAlignmentCorrXOffsetBottom", 0.f, "MFT X offset correction - bottom half"}; + Configurable cfgMFTAlignmentCorrYOffsetTop{"cfgMFTAlignmentCorrYOffsetTop", 0.f, "MFT Y offset correction - top half"}; + Configurable cfgMFTAlignmentCorrYOffsetBottom{"cfgMFTAlignmentCorrYOffsetBottom", 0.f, "MFT Y offset correction - bottom half"}; + } configMftAlignmentCorrections; + + // Magnetic field position bias + Configurable cfgFieldOriginBiasZ{"cfgFieldOriginBiasZ", 0.0f, "Bias applied to the magnetic field z position"}; + + // Variables for CCDB objects access and retrieval + struct : ConfigurableGroup { + Configurable cfgCcdbUrl{"cfgCcdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgCcdbNoLaterThan{"cfgCcdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable cfgGrpPath{"cfgGrpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable cfgGeoPath{"cfgGeoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable cfgGrpMagPath{"cfgGrpMagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + } configCcdb; + + // Matching strategy for the *custom* matches (production baseline is always computed). + // 0 = chi2 (runChi2Matching), 1 = ML (runMlMatching) + struct : ConfigurableGroup { + Configurable cfgCustomMatchingStrategy{"cfgCustomMatchingStrategy", 0, "0=chi2, 1=ML for custom matches"}; + Configurable cfgIncludeGlobalMuonsInFwdTracks{"cfgIncludeGlobalMuonsInFwdTracks", false, "Include MFT-MCH-MID global muons in GMMCANDTRK table"}; + Configurable cfgMaxCandidatesPerMchTrack{"cfgMaxCandidatesPerMchTrack", -1, "Maximum number of match candidates stored per MCH track (-1: no limit)"}; + Configurable cfgMatchAllTracks{"cfgMatchAllTracks", false, "If true the matching is performed considering all the MFT tracks for which the covariances are available; if false the matching is performed considering only the global forward tracks stored at production"}; + } configMatching; + + struct : ConfigurableGroup { + Configurable cfgMixingDepth{"cfgMixingDepth", -1, "Maximum number of mixed candidate groups per MCH track (-1: no limit)"}; + Configurable cfgMinDeltaBc{"cfgMinDeltaBc", 3564, "Minimum DeltaBc between mixed collisions"}; + Configurable cfgMaxDeltaPhi{"cfgMaxDeltaPhi", o2::constants::math::PI / 10.f, "Maximum DelptaPhi between mixed MCH tracks (rad)"}; + Configurable cfgMaxDeltaR{"cfgMaxDeltaR", 10.f, "Maximum DeltaR between mixed MCH tracks"}; + Configurable cfgMaxDeltaAttempts{"cfgMaxDeltaAttempts", 0.1f, "Maximum relative difference in match attempts"}; + Configurable cfgMaxDeltaZ{"cfgMaxDeltaZ", 1.f, "Maximum deltaZ between mixed collisions"}; + } configEventMixing; + + double mBzAtMftCenter{0}; + + using MatchingFunc = std::function(const o2::track::TrackParCovFwd& mchtrack, const o2::track::TrackParCovFwd& mfttrack)>; + std::map mMatchingFunctionMap; ///< MFT-MCH Matching function + + // Chi2 matching interface (single configurable method) + struct : ConfigurableGroup { + Configurable cfgChi2FunctionLabel{"cfgChi2FunctionLabel", std::string{"ProdAll"}, "Text label identifying the chi2 matching method"}; + Configurable cfgChi2FunctionName{"cfgChi2FunctionName", std::string{"prod"}, "Name of the chi2 matching function"}; + Configurable cfgChi2FunctionMatchingPlaneZ{"cfgChi2FunctionMatchingPlaneZ", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + } configChi2MatchingOptions; + + // ML interface (single configurable model) + struct : ConfigurableGroup { + Configurable cfgMlModelLabel{"cfgMlModelLabel", std::string{""}, "Text label identifying this ML model"}; + Configurable cfgMlModelPathCcdb{"cfgMlModelPathCcdb", "Users/m/mcoquet/MLTest", "Path of model on CCDB"}; + Configurable cfgMlModelName{"cfgMlModelName", "model.onnx", "ONNX file name (if not from CCDB full path)"}; + Configurable> cfgMlInputFeatures{"cfgMlInputFeatures", std::vector{"chi2MCHMFT"}, "Names of ML model input features"}; + Configurable cfgMlModelMatchingPlaneZ{"cfgMlModelMatchingPlaneZ", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + } configMlOptions; + + std::vector binsPtMl; + std::array cutValues{}; + std::vector cutDirMl; + bool hasActiveChi2Matching{false}; + std::string activeChi2FunctionName; + double activeChi2MatchingPlaneZ{0.}; + + bool hasActiveMlMatching{false}; + o2::analysis::MlResponseMFTMuonMatch activeMlResponse; + double activeMlMatchingPlaneZ{0.}; + + int mRunNumber{0}; // needed to detect if the run changed and trigger update of magnetic field + + Service ccdbManager{}; + o2::ccdb::CcdbApi fCCDBApi; + + // vector of all MFT-MCH(-MID) matching candidates associated to the same MCH(-MID) track, + // to be sorted in descending order with respect to the matching score + // the map key is the MCH(-MID) track global index + using MatchingCandidates = std::map>; + + class TrackParExt : public o2::track::TrackParCovFwd + { + public: + TrackParExt() = default; + TrackParExt(const TrackParExt& t) = default; + explicit TrackParExt(o2::track::TrackParCovFwd const& t, int nc = -1, bool r = false) + : TrackParCovFwd(t), nClusters(nc), removable(r) {} + ~TrackParExt() = default; + + TrackParExt& operator=(const TrackParCovFwd& tpf) + { + o2::track::TrackParCovFwd::operator=(tpf); + return *this; + } + TrackParExt& operator=(const TrackParExt& tpe) + { + o2::track::TrackParCovFwd::operator=(tpe); + nClusters = tpe.getNClusters(); + removable = tpe.isRemovable(); + return *this; + } + + void setNClusters(int n) { nClusters = n; } + [[nodiscard]] int getNClusters() const { return nClusters; } + + void setRemovable() { removable = true; } + [[nodiscard]] bool isRemovable() const { return removable; } + + [[nodiscard]] o2::track::TrackParCovFwd asTrackParCovFwd() const + { + return {static_cast(*this)}; + } + + private: + int nClusters{-1}; + bool removable{false}; + }; + + std::unordered_map mMchTrackPars; + std::unordered_map mMftTrackPars; + + std::unordered_map mftTrackCovs; + + Produces gmCandidateFwdTracks; + Produces gmCandidateFwdTracksCov; + Produces gmCandidateFwdTrackExtras; + Produces gmAmbiguousFwdTracksReAlign; + + int32_t mGmmCandFwdTrackRowIndex{0}; + std::unordered_map> mAmbBcSliceByFwdTrackId; + bool mHasLastMchAmbiguousBcSlice{false}; + std::array mLastMchAmbiguousBcSlice{}; + + std::unordered_map mMchTrackInfos; + std::unordered_map> mStoredMatchingCandidates; + std::unordered_map mFwdTrackToGmmCandTrkIndex; + + mch::TrackFitter trackFitter; // Track fitter from MCH tracking library + mch::geo::TransformationCreator transformation; + std::map transformRef; // reference geometry w.r.t track data + std::map transformNew; // new geometry + double mImproveCutChi2{0.}; // Chi2 cut for track improvement. + TGeoManager* geoNew = nullptr; + TGeoManager* geoRef = nullptr; + globaltracking::MatchGlobalFwd mMatching; + + Preslice perMuon = aod::fwdtrkcl::fwdtrackId; + + template + o2::mch::TrackParam fwdToMch(const T& fwdtrack) + { + // Convert Forward Track parameters and covariances matrix to the + // MCH track format. + + // Parameter conversion + const double x2 = fwdtrack.getPhi(); + const double x3 = fwdtrack.getTanl(); + const double x4 = fwdtrack.getInvQPt(); + + const auto sinX2 = std::sin(x2); + const auto cosX2 = std::cos(x2); + + const double alpha1 = cosX2 / x3; + const double alpha3 = sinX2 / x3; + const double alpha4 = x4 / std::sqrt(x3 * x3 + sinX2 * sinX2); + + const auto kNorm = std::sqrt(x3 * x3 + sinX2 * sinX2); + const auto kNorm3 = kNorm * kNorm * kNorm; + + // Covariances matrix conversion + SMatrix55Std jacobian; + SMatrix55Sym covariances; + + covariances(0, 0) = fwdtrack.getCovariances()(0, 0); + covariances(0, 1) = fwdtrack.getCovariances()(0, 1); + covariances(0, 2) = fwdtrack.getCovariances()(0, 2); + covariances(0, 3) = fwdtrack.getCovariances()(0, 3); + covariances(0, 4) = fwdtrack.getCovariances()(0, 4); + + covariances(1, 1) = fwdtrack.getCovariances()(1, 1); + covariances(1, 2) = fwdtrack.getCovariances()(1, 2); + covariances(1, 3) = fwdtrack.getCovariances()(1, 3); + covariances(1, 4) = fwdtrack.getCovariances()(1, 4); + + covariances(2, 2) = fwdtrack.getCovariances()(2, 2); + covariances(2, 3) = fwdtrack.getCovariances()(2, 3); + covariances(2, 4) = fwdtrack.getCovariances()(2, 4); + + covariances(3, 3) = fwdtrack.getCovariances()(3, 3); + covariances(3, 4) = fwdtrack.getCovariances()(3, 4); + + covariances(4, 4) = fwdtrack.getCovariances()(4, 4); + + jacobian(0, 0) = 1; + + jacobian(1, 2) = -sinX2 / x3; + jacobian(1, 3) = -cosX2 / (x3 * x3); + + jacobian(2, 1) = 1; + + jacobian(3, 2) = cosX2 / x3; + jacobian(3, 3) = -sinX2 / (x3 * x3); + + jacobian(4, 2) = -x4 * sinX2 * cosX2 / kNorm3; + jacobian(4, 3) = -x3 * x4 / kNorm3; + jacobian(4, 4) = 1 / kNorm; + // jacobian*covariances*jacobian^T + covariances = ROOT::Math::Similarity(jacobian, covariances); + + std::array cov = {covariances(0, 0), covariances(1, 0), covariances(1, 1), covariances(2, 0), covariances(2, 1), covariances(2, 2), covariances(3, 0), covariances(3, 1), covariances(3, 2), covariances(3, 3), covariances(4, 0), covariances(4, 1), covariances(4, 2), covariances(4, 3), covariances(4, 4)}; + std::array param = {fwdtrack.getX(), alpha1, fwdtrack.getY(), alpha3, alpha4}; + + o2::mch::TrackParam convertedTrack(fwdtrack.getZ(), param.data(), cov.data()); + return {convertedTrack}; + } + + o2::track::TrackParCovFwd mchToFwd(const o2::mch::TrackParam& mchParam) + { + // Convert a MCH Track parameters and covariances matrix to the + // Forward track format. Must be called after propagation though the absorber + + o2::track::TrackParCovFwd convertedTrack; + + // Parameter conversion + const double alpha1 = mchParam.getNonBendingSlope(); + const double alpha3 = mchParam.getBendingSlope(); + const double alpha4 = mchParam.getInverseBendingMomentum(); + + const double x2 = std::atan2(-alpha3, -alpha1); + const double x3 = -1. / std::sqrt(alpha3 * alpha3 + alpha1 * alpha1); + const double x4 = alpha4 * -x3 * std::sqrt(1 + alpha3 * alpha3); + + const auto kNorm = alpha1 * alpha1 + alpha3 * alpha3; + const auto kNorm32 = kNorm * std::sqrt(kNorm); + const auto slopeLen = std::sqrt(alpha3 * alpha3 + 1); + + // Covariances matrix conversion + SMatrix55Std jacobian; + SMatrix55Sym covariances; + + covariances(0, 0) = mchParam.getCovariances()(0, 0); + covariances(0, 1) = mchParam.getCovariances()(0, 1); + covariances(0, 2) = mchParam.getCovariances()(0, 2); + covariances(0, 3) = mchParam.getCovariances()(0, 3); + covariances(0, 4) = mchParam.getCovariances()(0, 4); + + covariances(1, 1) = mchParam.getCovariances()(1, 1); + covariances(1, 2) = mchParam.getCovariances()(1, 2); + covariances(1, 3) = mchParam.getCovariances()(1, 3); + covariances(1, 4) = mchParam.getCovariances()(1, 4); + + covariances(2, 2) = mchParam.getCovariances()(2, 2); + covariances(2, 3) = mchParam.getCovariances()(2, 3); + covariances(2, 4) = mchParam.getCovariances()(2, 4); + + covariances(3, 3) = mchParam.getCovariances()(3, 3); + covariances(3, 4) = mchParam.getCovariances()(3, 4); + + covariances(4, 4) = mchParam.getCovariances()(4, 4); + + jacobian(0, 0) = 1; + + jacobian(1, 2) = 1; + + jacobian(2, 1) = -alpha3 / kNorm; + jacobian(2, 3) = alpha1 / kNorm; + + jacobian(3, 1) = alpha1 / kNorm32; + jacobian(3, 3) = alpha3 / kNorm32; + + jacobian(4, 1) = -alpha1 * alpha4 * slopeLen / kNorm32; + jacobian(4, 3) = alpha3 * alpha4 * (1 / (std::sqrt(kNorm) * slopeLen) - slopeLen / kNorm32); + jacobian(4, 4) = slopeLen / std::sqrt(kNorm); + + // jacobian*covariances*jacobian^T + covariances = ROOT::Math::Similarity(jacobian, covariances); + + // Set output + convertedTrack.setX(mchParam.getNonBendingCoor()); + convertedTrack.setY(mchParam.getBendingCoor()); + convertedTrack.setZ(mchParam.getZ()); + convertedTrack.setPhi(x2); + convertedTrack.setTanl(x3); + convertedTrack.setInvQPt(x4); + convertedTrack.setCharge(mchParam.getCharge()); + convertedTrack.setCovariances(covariances); + + return convertedTrack; + } + + int getDetElemId(int iDetElemNumber) + { + // make sure detector number is valid + if (iDetElemNumber < SNDetElemCh[0] || + iDetElemNumber >= SNDetElemCh[NMchChambers]) { + LOGF(fatal, "Invalid detector element number: %d", iDetElemNumber); + } + /// get det element number from ID + // get chamber and element number in chamber + int iCh = 0; + int iDet = 0; + for (int i = 1; i <= NMchChambers; i++) { + if (iDetElemNumber < SNDetElemCh[i]) { + iCh = i; + iDet = iDetElemNumber - SNDetElemCh[i - 1]; + break; + } + } + + // make sure detector index is valid + if (iCh <= 0 || iCh > NMchChambers || iDet >= NDetElemCh[iCh - 1]) { + LOGF(fatal, "Invalid detector element id: %d", MchDetElemNumberingBase * iCh + iDet); + } + + // add number of detectors up to this chamber + return MchDetElemNumberingBase * iCh + iDet; + } + + bool removeTrack(mch::Track& track) + { + // Refit track with re-aligned clusters + bool shouldRemoveTrack = false; + try { + trackFitter.fit(track, false); + } catch (std::exception const& e) { + shouldRemoveTrack = true; + return shouldRemoveTrack; + } + + auto itStartingParam = std::prev(track.rend()); + + while (true) { + + try { + trackFitter.fit(track, true, false, (itStartingParam == track.rbegin()) ? nullptr : &itStartingParam); + } catch (std::exception const&) { + shouldRemoveTrack = true; + break; + } + + double worstLocalChi2 = -1.0; + + track.tagRemovableClusters(0x1F, false); + + auto itWorstParam = track.end(); + + for (auto itParam = track.begin(); itParam != track.end(); ++itParam) { + if (itParam->getLocalChi2() > worstLocalChi2) { + worstLocalChi2 = itParam->getLocalChi2(); + itWorstParam = itParam; + } + } + + if (worstLocalChi2 < mImproveCutChi2) { + break; + } + + if (!itWorstParam->isRemovable()) { + shouldRemoveTrack = true; + track.removable(); + break; + } + + auto itNextParam = track.removeParamAtCluster(itWorstParam); + auto itNextToNextParam = (itNextParam == track.end()) ? itNextParam : std::next(itNextParam); + itStartingParam = track.rbegin(); + + if (track.getNClusters() < MinRemovableTrackClusters) { + shouldRemoveTrack = true; + break; + } + while (itNextToNextParam != track.end()) { + if (itNextToNextParam->getClusterPtr()->getChamberId() != itNextParam->getClusterPtr()->getChamberId()) { + itStartingParam = std::make_reverse_iterator(++itNextParam); + break; + } + ++itNextToNextParam; + } + } + + if (!shouldRemoveTrack) { + for (auto& param : track) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + param.setParameters(param.getSmoothParameters()); + param.setCovariances(param.getSmoothCovariances()); + } + } + + return shouldRemoveTrack; + } + + template + void initCcdb(BC const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + mRunNumber = bc.runNumber(); + std::map metadata; + auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(fCCDBApi, mRunNumber); + auto ts = soreor.first; + auto grpmag = fCCDBApi.retrieveFromTFileAny(configCcdb.cfgGrpMagPath, metadata, ts); + o2::base::Propagator::initFieldFromGRP(grpmag); + LOGF(info, "Set field for muons"); + VarManager::SetupMuonMagField(); + if (!o2::base::GeometryManager::isGeometryLoaded()) { + ccdbManager->get(configCcdb.cfgGeoPath); + } + mch::TrackExtrap::setField(); + mch::TrackExtrap::useExtrapV2(); + + // Load geometry information from CCDB/local + LOGF(info, "Loading reference aligned geometry from CCDB no later than %d", configMchRealign.cfgCcdbNoLaterThanRef.value); + ccdbManager->setCreatedNotAfter(configMchRealign.cfgCcdbNoLaterThanRef.value); // this timestamp has to be consistent with what has been used in reco + geoRef = ccdbManager->getForTimeStamp(configMchRealign.cfgGeoRefPath, bc.timestamp()); + ccdbManager->clearCache(configMchRealign.cfgGeoRefPath); + if (geoRef != nullptr) { + transformation = mch::geo::transformationFromTGeoManager(*geoRef); + } else { + LOGF(fatal, "Reference aligned geometry object is not available in CCDB at timestamp=%llu", bc.timestamp()); + } + for (int i = 0; i < NMchDetElems; i++) { + int iDEN = getDetElemId(i); + transformRef[iDEN] = transformation(iDEN); + } + + LOGF(info, "Loading new aligned geometry from CCDB no later than %d", configMchRealign.cfgCcdbNoLaterThanNew.value); + ccdbManager->setCreatedNotAfter(configMchRealign.cfgCcdbNoLaterThanNew.value); // make sure this timestamp can be resolved regarding the reference one + geoNew = ccdbManager->getForTimeStamp(configMchRealign.cfgGeoNewPath, bc.timestamp()); + ccdbManager->clearCache(configMchRealign.cfgGeoNewPath); + if (geoNew != nullptr) { + transformation = mch::geo::transformationFromTGeoManager(*geoNew); + } else { + LOGF(fatal, "New aligned geometry object is not available in CCDB at timestamp=%llu", bc.timestamp()); + } + for (int i = 0; i < NMchDetElems; i++) { + int iDEN = getDetElemId(i); + transformNew[iDEN] = transformation(iDEN); + } + + // Init magnetic field for MFT track extrapolation + auto* fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + if (fieldB) { + std::array centerMft{0, 0, -61.4}; // Field at center of MFT + mBzAtMftCenter = fieldB->getBz(centerMft.data()); + // std::cout << "fieldB: " << (void*)fieldB << std::endl; + } + } + + void initMatchingFunctions() + { + using SVector2 = ROOT::Math::SVector; + using SVector4 = ROOT::Math::SVector; + using SVector5 = ROOT::Math::SVector; + + using SMatrix44 = ROOT::Math::SMatrix; + using SMatrix45 = ROOT::Math::SMatrix; + using SMatrix22 = ROOT::Math::SMatrix; + using SMatrix25 = ROOT::Math::SMatrix; + + // Define built-in matching functions + //________________________________________________________________________________ + mMatchingFunctionMap["matchALL"] = [](const o2::track::TrackParCovFwd& mchTrack, const o2::track::TrackParCovFwd& mftTrack) -> std::tuple { + // Match two tracks evaluating all parameters: X,Y, phi, tanl & q/pt + + SMatrix55Sym hK, vK; + SVector5 mK(mftTrack.getX(), mftTrack.getY(), mftTrack.getPhi(), + mftTrack.getTanl(), mftTrack.getInvQPt()), + rKKminus1; + const auto& globalMuonTrackParameters = mchTrack.getParameters(); + const auto& globalMuonTrackCovariances = mchTrack.getCovariances(); + vK(0, 0) = mftTrack.getCovariances()(0, 0); + vK(1, 1) = mftTrack.getCovariances()(1, 1); + vK(2, 2) = mftTrack.getCovariances()(2, 2); + vK(3, 3) = mftTrack.getCovariances()(3, 3); + vK(4, 4) = mftTrack.getCovariances()(4, 4); + hK(0, 0) = 1.0; + hK(1, 1) = 1.0; + hK(2, 2) = 1.0; + hK(3, 3) = 1.0; + hK(4, 4) = 1.0; + + // Covariance of residuals + SMatrix55Std invResCov = (vK + ROOT::Math::Similarity(hK, globalMuonTrackCovariances)); + invResCov.Invert(); + + // Update Parameters + rKKminus1 = mK - hK * globalMuonTrackParameters; // Residuals of prediction + + auto matchChi2Track = ROOT::Math::Similarity(rKKminus1, invResCov); + + // return chi2 and NDF + return {matchChi2Track, 5}; + }; + + //________________________________________________________________________________ + mMatchingFunctionMap["matchXYPhiTanl"] = [](const o2::track::TrackParCovFwd& mchTrack, const o2::track::TrackParCovFwd& mftTrack) -> std::tuple { + // Match two tracks evaluating positions & angles + + SMatrix45 hK; + SMatrix44 vK; + SVector4 mK(mftTrack.getX(), mftTrack.getY(), mftTrack.getPhi(), + mftTrack.getTanl()), + rKKminus1; + const auto& globalMuonTrackParameters = mchTrack.getParameters(); + const auto& globalMuonTrackCovariances = mchTrack.getCovariances(); + vK(0, 0) = mftTrack.getCovariances()(0, 0); + vK(1, 1) = mftTrack.getCovariances()(1, 1); + vK(2, 2) = mftTrack.getCovariances()(2, 2); + vK(3, 3) = mftTrack.getCovariances()(3, 3); + hK(0, 0) = 1.0; + hK(1, 1) = 1.0; + hK(2, 2) = 1.0; + hK(3, 3) = 1.0; + + // Covariance of residuals + SMatrix44 invResCov = (vK + ROOT::Math::Similarity(hK, globalMuonTrackCovariances)); + invResCov.Invert(); + + // Residuals of prediction + rKKminus1 = mK - hK * globalMuonTrackParameters; + + auto matchChi2Track = ROOT::Math::Similarity(rKKminus1, invResCov); + + // return chi2 and NDF + return {matchChi2Track, 4}; + }; + + //________________________________________________________________________________ + mMatchingFunctionMap["matchXY"] = [](const o2::track::TrackParCovFwd& mchTrack, const o2::track::TrackParCovFwd& mftTrack) -> std::tuple { + // Calculate Matching Chi2 - X and Y positions + + SMatrix25 hK; + SMatrix22 vK; + SVector2 mK(mftTrack.getX(), mftTrack.getY()), rKKminus1; + const auto& globalMuonTrackParameters = mchTrack.getParameters(); + const auto& globalMuonTrackCovariances = mchTrack.getCovariances(); + vK(0, 0) = mftTrack.getCovariances()(0, 0); + vK(1, 1) = mftTrack.getCovariances()(1, 1); + hK(0, 0) = 1.0; + hK(1, 1) = 1.0; + + // Covariance of residuals + SMatrix22 invResCov = (vK + ROOT::Math::Similarity(hK, globalMuonTrackCovariances)); + invResCov.Invert(); + + // Residuals of prediction + rKKminus1 = mK - hK * globalMuonTrackParameters; + auto matchChi2Track = ROOT::Math::Similarity(rKKminus1, invResCov); + + // return reduced chi2 + return {matchChi2Track, 2}; + }; + } + + void init(o2::framework::InitContext&) + { + // Load geometry + ccdbManager->setURL(configCcdb.cfgCcdbUrl); + ccdbManager->setCaching(true); + ccdbManager->setLocalObjectValidityChecking(); + fCCDBApi.init(configCcdb.cfgCcdbUrl); + mRunNumber = 0; + + // configure magnetic field position bias + o2::conf::ConfigurableParam::setValue("FieldOriginBias.z", std::to_string(cfgFieldOriginBiasZ.value)); + + // Configuration for track fitter + const auto& trackerParam = mch::TrackerParam::Instance(); + trackFitter.setBendingVertexDispersion(trackerParam.bendingVertexDispersion); + trackFitter.setChamberResolution(configMchRealign.cfgChamberResolutionX.value, configMchRealign.cfgChamberResolutionY.value); + trackFitter.smoothTracks(true); + trackFitter.useChamberResolution(); + mImproveCutChi2 = 2. * configMchRealign.cfgSigmaCutImprove.value * configMchRealign.cfgSigmaCutImprove.value; + + // Reset matching configuration, then populate only what we need. + hasActiveChi2Matching = false; + activeChi2FunctionName.clear(); + activeChi2MatchingPlaneZ = 0.; + + hasActiveMlMatching = false; + activeMlMatchingPlaneZ = 0.; + + if (configMatching.cfgCustomMatchingStrategy.value == 0) { + // Matching functions (custom chi2) + initMatchingFunctions(); + auto label = configChi2MatchingOptions.cfgChi2FunctionLabel.value; + auto funcName = configChi2MatchingOptions.cfgChi2FunctionName.value; + auto matchingPlaneZ = configChi2MatchingOptions.cfgChi2FunctionMatchingPlaneZ.value; + + if (!label.empty() && !funcName.empty()) { + hasActiveChi2Matching = true; + activeChi2FunctionName = funcName; + activeChi2MatchingPlaneZ = matchingPlaneZ; + } + } else { + // Matching ML models (custom ML) + // TODO : for now we use hard coded values since the current models use 1 pT bin + binsPtMl = {-1e-6, 1000.0}; + cutValues = {0.0}; + cutDirMl = {cuts_ml::CutNot}; + LabeledArray mycutsMl(cutValues.data(), 1, 1, std::vector{"pT bin 0"}, std::vector{"score"}); + + auto label = configMlOptions.cfgMlModelLabel.value; + auto modelPath = configMlOptions.cfgMlModelPathCcdb.value; + auto inputFeatures = configMlOptions.cfgMlInputFeatures.value; + auto modelName = configMlOptions.cfgMlModelName.value; + auto matchingPlaneZ = configMlOptions.cfgMlModelMatchingPlaneZ.value; + + if (!label.empty() && !modelPath.empty() && !inputFeatures.empty() && !modelName.empty()) { + activeMlResponse.configure(binsPtMl, mycutsMl, cutDirMl, 1); + activeMlResponse.setModelPathsCCDB(std::vector{modelName}, fCCDBApi, std::vector{modelPath}, configCcdb.cfgCcdbNoLaterThan.value); + activeMlResponse.cacheInputFeaturesIndices(inputFeatures); + activeMlResponse.init(); + + hasActiveMlMatching = true; + activeMlMatchingPlaneZ = matchingPlaneZ; + } + } + } + + template + bool pDcaCut(const T& mchTrack, const C& collision, double nSigmaPDCA) + { + static const double sigmaPDCA23 = 80.; + static const double sigmaPDCA310 = 54.; + static const double relPRes = 0.0004; + static const double slopeRes = 0.0005; + + constexpr double AbsorberEndZ = 505.; + constexpr double RadToDeg = 180. / o2::constants::math::PI; + double thetaAbs = std::atan(mchTrack.rAtAbsorberEnd() / AbsorberEndZ) * RadToDeg; + + // propagate muon track to vertex + auto mchTrackAtVertex = VarManager::PropagateMuon(mchTrack, collision, VarManager::kToVertex); + + // double pUncorr = mchTrack.p(); + double p = mchTrackAtVertex.getP(); + + double pDCA = mchTrack.pDca(); + double sigmaPDCA = (thetaAbs < ThetaAbsBoundaryDeg) ? sigmaPDCA23 : sigmaPDCA310; + double nrp = nSigmaPDCA * relPRes * p; + double pResEffect = sigmaPDCA / (1. - nrp / (1. + nrp)); + double slopeResEffect = SlopeResolutionZ * slopeRes * p; + double sigmaPDCAWithRes = std::sqrt(pResEffect * pResEffect + slopeResEffect * slopeResEffect); + return pDCA <= nSigmaPDCA * sigmaPDCAWithRes; + } + + template + bool isGoodMuon(const T& mchTrack, const C& collision, + double chi2Cut, + double pCut, + double pTCut, + std::array etaCut, + std::array rAbsCut, + double nSigmaPdcaCut) + { + // chi2 cut + if (mchTrack.chi2() > chi2Cut) { + return false; + } + + // momentum cut + if (mchTrack.p() < pCut) { + return false; // skip low-momentum tracks + } + + // transverse momentum cut + if (mchTrack.pt() < pTCut) { + return false; // skip low-momentum tracks + } + + // Eta cut + double eta = mchTrack.eta(); + if ((eta < etaCut[0] || eta > etaCut[1])) { + return false; + } + + // RAbs cut + double rAbs = mchTrack.rAtAbsorberEnd(); + if ((rAbs < rAbsCut[0] || rAbs > rAbsCut[1])) { + return false; + } + + // pDCA cut + return pDcaCut(mchTrack, collision, nSigmaPdcaCut); + } + + void storeFwdTrackCovariance(const SMatrix55Sym& cov) + { + const float sigX = std::sqrt(cov(0, 0)); + const float sigY = std::sqrt(cov(1, 1)); + const float sigPhi = std::sqrt(cov(2, 2)); + const float sigTgl = std::sqrt(cov(3, 3)); + const float sig1Pt = std::sqrt(cov(4, 4)); + const auto rhoXY = static_cast(128.f * cov(0, 1) / (sigX * sigY)); + const auto rhoPhiX = static_cast(128.f * cov(0, 2) / (sigPhi * sigX)); + const auto rhoPhiY = static_cast(128.f * cov(1, 2) / (sigPhi * sigY)); + const auto rhoTglX = static_cast(128.f * cov(0, 3) / (sigTgl * sigX)); + const auto rhoTglY = static_cast(128.f * cov(1, 3) / (sigTgl * sigY)); + const auto rhoTglPhi = static_cast(128.f * cov(2, 3) / (sigTgl * sigPhi)); + const auto rho1PtX = static_cast(128.f * cov(0, 4) / (sig1Pt * sigX)); + const auto rho1PtY = static_cast(128.f * cov(1, 4) / (sig1Pt * sigY)); + const auto rho1PtPhi = static_cast(128.f * cov(2, 4) / (sig1Pt * sigPhi)); + const auto rho1PtTgl = static_cast(128.f * cov(3, 4) / (sig1Pt * sigTgl)); + gmCandidateFwdTracksCov(sigX, sigY, sigPhi, sigTgl, sig1Pt, + rhoXY, rhoPhiY, rhoPhiX, rhoTglX, rhoTglY, rhoTglPhi, rho1PtX, rho1PtY, rho1PtPhi, rho1PtTgl); + } + + bool isMchTrackTagged(int64_t mchTrackIndex) const + { + const auto it = mMchTrackInfos.find(mchTrackIndex); + return it != mMchTrackInfos.end() && it->second.isTagged; + } + + template + void fillBaseGmmCandFwdTrack(TMCH const& track, + TrackParExt const& trackPar, + int32_t gmmMchTrackId, + float chi2MatchMCHMFT, + float matchScoreMCHMFT, + bool isTagged) + { + const auto collisionId = track.collisionId(); + bool hasBcSlice = false; + std::array bcSlice{}; + if (collisionId < 0) { + const auto ambIt = mAmbBcSliceByFwdTrackId.find(track.globalIndex()); + if (ambIt != mAmbBcSliceByFwdTrackId.end()) { + bcSlice = ambIt->second; + hasBcSlice = true; + } + } + + gmCandidateFwdTracks( + collisionId, + track.trackType(), + trackPar.getX(), + trackPar.getY(), + trackPar.getZ(), + trackPar.getPhi(), + trackPar.getTgl(), + trackPar.getInvQPt(), + trackPar.getNClusters(), + track.pDca(), + track.rAtAbsorberEnd(), + trackPar.isRemovable(), + trackPar.getTrackChi2(), + track.chi2MatchMCHMID(), + chi2MatchMCHMFT, + matchScoreMCHMFT, + track.matchMFTTrackId(), + gmmMchTrackId, + track.mchBitMap(), + track.midBitMap(), + track.midBoards(), + track.trackTime(), + track.trackTimeRes()); + + storeFwdTrackCovariance(trackPar.getCovariances()); + gmCandidateFwdTrackExtras(isTagged, -1, -1, -1, -1.f, -1.f, -1.f, -1.f); + if (hasBcSlice) { + gmAmbiguousFwdTracksReAlign(mGmmCandFwdTrackRowIndex, bcSlice.data()); + } + mGmmCandFwdTrackRowIndex += 1; + + mHasLastMchAmbiguousBcSlice = hasBcSlice; + if (hasBcSlice) { + mLastMchAmbiguousBcSlice = bcSlice; + } + } + + template + void fillCandidateFwdTrack(TMCH const& mchTrack, + TrackParExt const& mchPar, + int32_t gmmMchTrackId, + TMFT const& mftTrack, + TrackParExt const& mftPar, + const MatchingCandidate& candidate) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum; + using o2::aod::fwdtrackutils::propagationPoint; + + constexpr auto CandidateTrackType = static_cast(ForwardTrackTypeEnum::GlobalForwardTrack); + + auto propmuonAtMft = fwdToMch(mchPar); + o2::mch::TrackExtrap::extrapToVertex(propmuonAtMft, + mftPar.getX(), + mftPar.getY(), + mftPar.getZ(), + mftPar.getSigma2X(), + mftPar.getSigma2Y()); + + const auto globalMuonRefit = o2::aod::fwdtrackutils::refitGlobalMuonCov(mchToFwd(propmuonAtMft), mftPar); + + const auto nClusters = static_cast(std::min(127, mchPar.getNClusters() + mftPar.getNClusters())); + + const auto chi2 = static_cast(mchTrack.chi2()); + const int32_t collisionId = mchTrack.collisionId(); + bool hasBcSlice = false; + std::array bcSlice{}; + if (collisionId < 0) { + if (mHasLastMchAmbiguousBcSlice) { + bcSlice = mLastMchAmbiguousBcSlice; + hasBcSlice = true; + } else { + const auto ambIt = mAmbBcSliceByFwdTrackId.find(mchTrack.globalIndex()); + if (ambIt != mAmbBcSliceByFwdTrackId.end()) { + bcSlice = ambIt->second; + hasBcSlice = true; + } + } + } + + bool isRemovable = mchPar.isRemovable(); + + gmCandidateFwdTracks( + collisionId, + CandidateTrackType, + globalMuonRefit.getX(), + globalMuonRefit.getY(), + globalMuonRefit.getZ(), + globalMuonRefit.getPhi(), + globalMuonRefit.getTgl(), + globalMuonRefit.getInvQPt(), + nClusters, + mchTrack.pDca(), + mchTrack.rAtAbsorberEnd(), + isRemovable, + chi2, + mchTrack.chi2MatchMCHMID(), + static_cast(candidate.matchChi2), + static_cast(candidate.matchScore), + static_cast(mftTrack.globalIndex()), + gmmMchTrackId, + mchTrack.mchBitMap(), + mchTrack.midBitMap(), + mchTrack.midBoards(), + mchTrack.trackTime(), + mchTrack.trackTimeRes()); + + storeFwdTrackCovariance(globalMuonRefit.getCovariances()); + gmCandidateFwdTrackExtras(isMchTrackTagged(mchTrack.globalIndex()), + candidate.matchRanking, + candidate.mixedGroupIndex, + candidate.deltaBc, + candidate.deltaPhi, + candidate.deltaR, + candidate.deltaAttemptsRel, + candidate.deltaZ); + if (hasBcSlice) { + gmAmbiguousFwdTracksReAlign(mGmmCandFwdTrackRowIndex, bcSlice.data()); + } + mGmmCandFwdTrackRowIndex += 1; + } + + o2::track::TrackParCovFwd propagateToZMch(const o2::track::TrackParCovFwd& muon, const double z) + { + auto mchTrack = fwdToMch(muon); + + float absFront = -90.f; + float absBack = -505.f; + + if (muon.getZ() < absBack && z > absFront) { + // extrapolation through the absorber in the upstream direction + o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrack, z); + } else { + // all other cases + o2::mch::TrackExtrap::extrapToZCov(mchTrack, z); + } + + return mchToFwd(mchTrack); + } + + o2::track::TrackParCovFwd propagateToZMft(const o2::track::TrackParCovFwd& mftTrack, const double z) + { + o2::track::TrackParCovFwd trackExtrap{mftTrack}; + trackExtrap.propagateToZ(z, mBzAtMftCenter); + return trackExtrap; + } + + template + o2::track::TrackParCovFwd propagateToVertexMch(const TMCH& muon, + const C& collision) + { + auto mchTrack = fwdToMch(fwdtrackutils::getTrackParCovFwd(muon, muon)); + o2::mch::TrackExtrap::extrapToVertex(mchTrack, + collision.posX(), + collision.posY(), + collision.posZ(), + collision.covXX(), + collision.covYY()); + return mchToFwd(mchTrack); + } + + // tag muons based on the track quality and the track position at the front and back MFT planes + template + void getTaggedMuons(C const& collisions, + TMUON const& muonTracks, + std::vector& taggedMuons) + { + taggedMuons.clear(); + for (const auto& muonTrack : muonTracks) { + + // only consider MCH-MID matches + if (static_cast(muonTrack.trackType()) != MchMidTrackType) { + continue; + } + + // only select MCH-MID tracks associated to a collision + if (!muonTrack.has_collision()) { + continue; + } + + const auto& collision = collisions.rawIteratorAt(muonTrack.collisionId()); + + // select MCH tracks with strict quality cuts + if (!isGoodMuon(muonTrack, collision, + configMuonTagging.cfgMuonTaggingTrackChi2MchUp, + configMuonTagging.cfgMuonTaggingPMchLow, + configMuonTagging.cfgMuonTaggingPtMchLow, + {configMuonTagging.cfgMuonTaggingEtaMchLow, configMuonTagging.cfgMuonTaggingEtaMchUp}, + {configMuonTagging.cfgMuonTaggingRabsLow, configMuonTagging.cfgMuonTaggingRabsUp}, + configMuonTagging.cfgMuonTaggingPdcaUp)) { + continue; + } + + // propagate MCH track to the vertex + auto mchTrackAtVertex = propagateToVertexMch(muonTrack, collision); + + // propagate the track from the vertex to the first MFT plane + const auto& extrapToMFTfirst = propagateToZMch(mchTrackAtVertex, o2::mft::constants::mft::LayerZCoordinate()[0]); + double rFront = std::sqrt(extrapToMFTfirst.getX() * extrapToMFTfirst.getX() + extrapToMFTfirst.getY() * extrapToMFTfirst.getY()); + if (rFront < configMuonTagging.cfgMuonTaggingRadiusAtMftFrontLow.value || rFront > configMuonTagging.cfgMuonTaggingRadiusAtMftFrontUp.value) { + continue; + } + + // propagate the track from the vertex to the last MFT plane + const auto& extrapToMFTlast = propagateToZMch(mchTrackAtVertex, o2::mft::constants::mft::LayerZCoordinate()[9]); + double rBack = std::sqrt(extrapToMFTlast.getX() * extrapToMFTlast.getX() + extrapToMFTlast.getY() * extrapToMFTlast.getY()); + if (rBack < configMuonTagging.cfgMuonTaggingRadiusAtMftBackLow.value || rBack > configMuonTagging.cfgMuonTaggingRadiusAtMftBackUp.value) { + continue; + } + + int64_t muonTrackIndex = muonTrack.globalIndex(); + taggedMuons.emplace_back(muonTrackIndex); + } + } + + template + bool isMftMchTimeCompatible(EVT const& collisions, + BC const& bcs, + TMUON const& mchTrack, + TMFT const& mftTrack) + { + if (!mchTrack.has_collision() || !mftTrack.has_collision()) { + return false; + } + + const auto& collMch = collisions.rawIteratorAt(mchTrack.collisionId()); + const auto& bcMch = bcs.rawIteratorAt(collMch.bcId()); + const auto& collMft = collisions.rawIteratorAt(mftTrack.collisionId()); + const auto& bcMft = bcs.rawIteratorAt(collMft.bcId()); + + int64_t deltaBc = static_cast(bcMft.globalBC()) - static_cast(bcMch.globalBC()); + double deltaBcNS = o2::constants::lhc::LHCBunchSpacingNS * deltaBc; + double deltaTrackTime = mftTrack.trackTime() - mchTrack.trackTime() + deltaBcNS; + double trackTimeResTot = mftTrack.trackTimeRes() + mchTrack.trackTimeRes(); + + return std::fabs(deltaTrackTime) <= trackTimeResTot; + } + + template + int getMftMchMatchAttempts(EVT const& collisions, + BC const& bcs, + TMUON const& mchTrack, + TMFTS const& mftTracks) + { + if (!mchTrack.has_collision()) { + return 0; + } + const auto& collMch = collisions.rawIteratorAt(mchTrack.collisionId()); + const auto& bcMch = bcs.rawIteratorAt(collMch.bcId()); + + int attempts{0}; + for (const auto& mftTrack : mftTracks) { + if (!mftTrack.has_collision()) { + continue; + } + + const auto& collMft = collisions.rawIteratorAt(mftTrack.collisionId()); + const auto& bcMft = bcs.rawIteratorAt(collMft.bcId()); + + int64_t deltaBc = static_cast(bcMft.globalBC()) - static_cast(bcMch.globalBC()); + double deltaBcNS = o2::constants::lhc::LHCBunchSpacingNS * deltaBc; + double deltaTrackTime = mftTrack.trackTime() - mchTrack.trackTime() + deltaBcNS; + double trackTimeResTot = mftTrack.trackTimeRes() + mchTrack.trackTimeRes(); + + if (std::fabs(deltaTrackTime) > trackTimeResTot) { + continue; + } + attempts += 1; + } + + return attempts; + } + + template + void prepareMatchingCandidates(EVT const& collisions, + BC const& bcs, + TMUON const& muonTracks, + TMFT const& mftTracks, + MyMFTCovariances const& mftCovs) + { + mMftTrackPars.clear(); + mMchTrackPars.clear(); + + LOGF(info, "Filling matching candidate tables"); + + for (const auto& muonTrack : muonTracks) { + if (static_cast(muonTrack.trackType()) <= GlobalTrackTypeMax) { + continue; + } + auto mchTrackIndex = muonTrack.globalIndex(); + + // initialize the MCH track parameters, which will be updated by the realignment if enabled + mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + + mMchTrackInfos.try_emplace(mchTrackIndex, MchTrackInfo{ + .nMatchAttempts = 0, + .matchingCandidates = std::vector(), + .mixedMatchingCandidates = std::vector>()}); + } + + for (const auto& mftTrack : mftTracks) { + auto mftTrackIndex = mftTrack.globalIndex(); + + // initialize the MFT track parameters, which will be updated by the alignment corrections if enabled + if (mftTrackCovs.contains(mftTrackIndex) && !mMftTrackPars.contains(mftTrackIndex)) { + auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrackIndex]); + mMftTrackPars.emplace(mftTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(mftTrack, mftTrackCov), mftTrack.nClusters())); + } + } + + // fill matching candidates table + if (!configMatching.cfgMatchAllTracks.value) { + // collect global MFT-MCH or MFT-MCH-MID tracks and associate them to the corresponding MCH(-MID) track + for (const auto& muonTrack : muonTracks) { + // skip MCH or MCH-MID tracks + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { + continue; + } + + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + int64_t mchTrackIndex = mchTrack.globalIndex(); + auto const& mftTrack = muonTrack.template matchMFTTrack_as(); + int64_t mftTrackIndex = mftTrack.globalIndex(); + + if (!mftTrackCovs.contains(mftTrackIndex)) { + continue; + } + + auto& mchTrackInfo = mMchTrackInfos[mchTrackIndex]; + mchTrackInfo.matchingCandidates.emplace_back(MatchingCandidate{ + .muonTrackId = muonTrack.globalIndex(), + .mftTrackId = mftTrackIndex, + .matchScore = muonTrack.matchScoreMCHMFT(), + .matchChi2 = muonTrack.chi2MatchMCHMFT()}); + } + + // set the number of match attempts for this track + for (auto& mchTrackInfo : mMchTrackInfos) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + const auto& mchTrack = muonTracks.rawIteratorAt(mchTrackInfo.first); + mchTrackInfo.second.nMatchAttempts = getMftMchMatchAttempts(collisions, bcs, mchTrack, mftTracks); + } + } else { + // build matching candidates from all time-compatible MFT-MCH pairs + for (const auto& muonTrack : muonTracks) { + if (static_cast(muonTrack.trackType()) <= GlobalTrackTypeMax) { + continue; + } + auto mchTrackIndex = muonTrack.globalIndex(); + for (const auto& mftTrack : mftTracks) { + if (!isMftMchTimeCompatible(collisions, bcs, muonTrack, mftTrack)) { + continue; + } + if (!mftTrackCovs.contains(mftTrack.globalIndex())) { + continue; + } + + auto& mchTrackInfo = mMchTrackInfos[mchTrackIndex]; + mchTrackInfo.matchingCandidates.emplace_back(MatchingCandidate{ + .mftTrackId = mftTrack.globalIndex()}); + } + } + + // set the number of match attempts for this track + for (auto& mchTrackInfo : mMchTrackInfos) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + mchTrackInfo.second.nMatchAttempts = mchTrackInfo.second.matchingCandidates.size(); + } + } + + std::vector taggedMuons; + getTaggedMuons(collisions, muonTracks, taggedMuons); + for (const auto& mchTrackIndex : taggedMuons) { + auto it = mMchTrackInfos.find(mchTrackIndex); + if (it != mMchTrackInfos.end()) { + it->second.isTagged = true; + } + } + } + + template + void prepareEventMixingMatchingCandidates(EVT const& collisions, + BC const& bcs, + TMUON const& muonTracks) + { + LOGF(info, "Filling mixed matching candidate tables"); + + const int mixingDepth = configEventMixing.cfgMixingDepth.value; + const int64_t minDeltaBc = configEventMixing.cfgMinDeltaBc.value; + const float maxDeltaPhi = configEventMixing.cfgMaxDeltaPhi.value; + const float maxDeltaR = configEventMixing.cfgMaxDeltaR.value; + const float maxDeltaAttemptsRel = configEventMixing.cfgMaxDeltaAttempts.value; + const float maxDeltaZ = configEventMixing.cfgMaxDeltaZ.value; + + for (auto& [mchIndex1, mchTrackInfo1] : mMchTrackInfos) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + const auto& mchTrack1 = muonTracks.rawIteratorAt(mchIndex1); + + if (!mchTrack1.has_collision()) { + continue; + } + + const auto& collision1 = collisions.rawIteratorAt(mchTrack1.collisionId()); + const auto& bc1 = bcs.rawIteratorAt(collision1.bcId()); + + auto phi1 = std::atan2(mchTrack1.y(), mchTrack1.x()); + auto r1 = std::hypot(mchTrack1.x(), mchTrack1.y()); + + auto nAttempts1 = mchTrackInfo1.nMatchAttempts; + + auto vz1 = collision1.posZ(); + + for (const auto& [mchIndex2, mchTrackInfo2] : mMchTrackInfos) { + if (mixingDepth >= 0 && static_cast(mchTrackInfo1.mixedMatchingCandidates.size()) >= mixingDepth) { + break; + } + + const auto& mchTrack2 = muonTracks.rawIteratorAt(mchIndex2); + + if (!mchTrack2.has_collision()) { + continue; + } + + const auto& collision2 = collisions.rawIteratorAt(mchTrack2.collisionId()); + const auto& bc2 = bcs.rawIteratorAt(collision2.bcId()); + + auto deltaBc = std::abs(static_cast(bc2.globalBC()) - static_cast(bc1.globalBC())); + if (deltaBc < minDeltaBc) { + continue; + } + + auto phi2 = std::atan2(mchTrack2.y(), mchTrack2.x()); + auto deltaPhi = std::fabs(getDeltaPhi(phi1, phi2)); + if (deltaPhi > maxDeltaPhi) { + continue; + } + + auto r2 = std::hypot(mchTrack2.x(), mchTrack2.y()); + auto deltaR = r2 - r1; + if (deltaR > maxDeltaR) { + continue; + } + + auto nAttempts2 = mchTrackInfo2.nMatchAttempts; + + float deltaAttempts = nAttempts2 - nAttempts1; + float deltaAttemptsRel = (nAttempts1 > 0) ? deltaAttempts / nAttempts1 : 0; + if (deltaAttemptsRel > maxDeltaAttemptsRel) { + continue; + } + + auto vz2 = collision2.posZ(); + auto deltaZ = std::fabs(vz2 - vz1); + if (deltaZ > maxDeltaZ) { + continue; + } + + // add the candidates of MCH track #2 to the list of mixed candidates of track #1 + mchTrackInfo1.mixedMatchingCandidates.push_back(mchTrackInfo2.matchingCandidates); + // update the muon track index of the mixed candidates to the index of track #1 + // and store the mixing deltas for this group + for (auto& candidate : mchTrackInfo1.mixedMatchingCandidates.back()) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + candidate.muonTrackId = mchIndex1; + candidate.deltaBc = deltaBc; + candidate.deltaPhi = deltaPhi; + candidate.deltaR = deltaR; + candidate.deltaAttemptsRel = deltaAttemptsRel; + candidate.deltaZ = deltaZ; + } + } + } + } + + template + o2::track::TrackParCovFwd transformMft(TMFT& mftTrack, TMFTCOV const& mftTrackCov) + { + auto track = fwdToMch(fwdtrackutils::getTrackParCovFwd(mftTrack, mftTrackCov)); + + double z = track.getZ(); + // double dZ = zMCH - z; + double x = track.getNonBendingCoor(); + double y = track.getBendingCoor(); + double xSlope = track.getNonBendingSlope(); + double ySlope = track.getBendingSlope(); + + double xSlopeCorrection = (y > 0) ? configMftAlignmentCorrections.cfgMFTAlignmentCorrXSlopeTop : configMftAlignmentCorrections.cfgMFTAlignmentCorrXSlopeBottom; + double xCorrection = xSlopeCorrection * z + + ((y > 0) ? configMftAlignmentCorrections.cfgMFTAlignmentCorrXOffsetTop : configMftAlignmentCorrections.cfgMFTAlignmentCorrXOffsetBottom); + double xNew = x + xCorrection; + double xSlopeNew = xSlope + xSlopeCorrection; + + track.setNonBendingCoor(xNew); + track.setNonBendingSlope(xSlopeNew); + + double ySlopeCorrection = (y > 0) ? configMftAlignmentCorrections.cfgMFTAlignmentCorrYSlopeTop : configMftAlignmentCorrections.cfgMFTAlignmentCorrYSlopeBottom; + double yCorrection = ySlopeCorrection * z + + ((y > 0) ? configMftAlignmentCorrections.cfgMFTAlignmentCorrYOffsetTop : configMftAlignmentCorrections.cfgMFTAlignmentCorrYOffsetBottom); + track.setBendingCoor(y + yCorrection); + track.setBendingSlope(ySlope + ySlopeCorrection); + + return mchToFwd(track); + } + + template + void runMftRealignment(TMFTs const& mftTracks, TMFTCOVs const& mftCovs) + { + for (const auto& mftTrack : mftTracks) { + auto mftTrackIndex = mftTrack.globalIndex(); + if (!mftTrackCovs.contains(mftTrackIndex)) { + continue; + } + + auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrackIndex]); + mMftTrackPars[mftTrackIndex] = transformMft(mftTrack, mftTrackCov); + } + } + + template + void runMuonRealignment(TMuons const& muons, TMuonCls const& clusters) + { + // Loop over forward tracks + for (auto const& muon : muons) { + int mchIndex = muon.globalIndex(); + // skip global forward matches + if (muon.trackType() <= GlobalTrackTypeMax) { + continue; + } + + // continue; + + auto mchTrackParIt = mMchTrackPars.find(mchIndex); + if (mchTrackParIt == mMchTrackPars.end()) { + continue; + } + + auto clustersSliced = clusters.sliceBy(perMuon, muon.globalIndex()); // Slice clusters by muon id + mch::Track convertedTrack = mch::Track(); // Temporary variable to store re-aligned clusters + + int clIndex = -1; + // Get re-aligned clusters associated to current track + for (auto const& cluster : clustersSliced) { + clIndex += 1; + + auto* clusterMCH = new mch::Cluster(); + + math_utils::Point3D local; + math_utils::Point3D master; + master.SetXYZ(cluster.x(), cluster.y(), cluster.z()); + + // Transformation from reference geometry frame to new geometry frame + transformRef[cluster.deId()].MasterToLocal(master, local); + transformNew[cluster.deId()].LocalToMaster(local, master); + + clusterMCH->x = master.x(); + clusterMCH->y = master.y(); + clusterMCH->z = master.z(); + + const uint32_t clUid = mch::Cluster::buildUniqueId(static_cast(cluster.deId() / 100) - 1, cluster.deId(), clIndex); + clusterMCH->uid = clUid; + clusterMCH->ex = cluster.isGoodX() ? 0.2 : 10.0; + clusterMCH->ey = cluster.isGoodY() ? 0.2 : 10.0; + + // Add transformed cluster into temporary variable + convertedTrack.createParamAtCluster(*clusterMCH); + // LOGF(debug, "Track %d, cluster DE%d: x:%g y:%g z:%g", muon.globalIndex(), cluster.deId(), cluster.x(), cluster.y(), cluster.z()); + // LOGF(debug, "Track %d, re-aligned cluster DE%d: x:%g y:%g z:%g", muonRealignId, cluster.deId(), clusterMCH->getX(), clusterMCH->getY(), clusterMCH->getZ()); + } + + // Refit the re-aligned track + int removable = 0; + if (convertedTrack.getNClusters() != 0) { + removable = removeTrack(convertedTrack); + } else { + LOGF(fatal, "Muon track %d has no associated clusters.", muon.globalIndex()); + } + + // Get the re-aligned track parameter: track param at the first cluster + mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + + // Convert MCH track to FWD track and store new parameters after realignment + mchTrackParIt->second = mchToFwd(mch::TrackParam(convertedTrack.first())); + mchTrackParIt->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); + mchTrackParIt->second.setNClusters(convertedTrack.getNClusters()); + if (removable) { + mchTrackParIt->second.setRemovable(); + } + } + } + + void runChi2Matching(const std::string& funcName, + float matchingPlaneZ, + MatchingCandidates& newMatchingCandidates, + bool useMixedMatchingCandidates) + { + newMatchingCandidates.clear(); + + std::string funcNameEffective = funcName; + float matchingPlaneZEffective = matchingPlaneZ; + if (funcName == "prod") { + funcNameEffective = "matchALL"; + matchingPlaneZEffective = MatchingPlaneDefaultZ; + } + + if (!mMatchingFunctionMap.contains(funcNameEffective)) { + return; + } + auto matchingFunc = mMatchingFunctionMap.at(funcNameEffective); + + for (const auto& [mchIndex, mchTrackInfo] : mMchTrackInfos) { + // get the tracks parameters, which have been updated by the realignment if enabled + const auto mchTrackParIt = mMchTrackPars.find(mchIndex); + if (mchTrackParIt == mMchTrackPars.end()) { + continue; + } + + auto processGroup = [&](const std::vector& candidatesGroup, int32_t mixedGroupIndex, int64_t mchTrackIndex) { + std::vector groupResults; + groupResults.reserve(candidatesGroup.size()); + + for (const auto& candidate : candidatesGroup) { + auto mftTrackParIt = mMftTrackPars.find(candidate.mftTrackId); + if (mftTrackParIt == mMftTrackPars.end()) { + continue; + } + + auto mftTrackProp = mftTrackParIt->second.asTrackParCovFwd(); + auto mchTrackProp = mchTrackParIt->second.asTrackParCovFwd(); + + if (matchingPlaneZEffective < 0.) { + mftTrackProp = propagateToZMft(mftTrackProp, matchingPlaneZ); + mchTrackProp = propagateToZMch(mchTrackProp, matchingPlaneZ); + } + + auto matchResult = matchingFunc(mchTrackProp, mftTrackProp); + float matchChi2 = std::get<0>(matchResult); + + groupResults.emplace_back(MatchingCandidate{ + .muonTrackId = candidate.muonTrackId, + .mftTrackId = candidate.mftTrackId, + .matchScore = -1, + .matchChi2 = matchChi2}); + } + + auto compareMatchingChi2 = [](const MatchingCandidate& track1, const MatchingCandidate& track2) -> bool { + return (track1.matchChi2 < track2.matchChi2); + }; + std::sort(groupResults.begin(), groupResults.end(), compareMatchingChi2); + + int ranking = 1; + for (auto& matchedCandidate : groupResults) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + matchedCandidate.matchRanking = ranking; + ranking += 1; + } + + const int maxCandidates = configMatching.cfgMaxCandidatesPerMchTrack.value; + + auto& storedCandidates = newMatchingCandidates[mchTrackIndex]; + size_t nStoredThisGroup = 0; + for (auto& result : groupResults) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + if (maxCandidates >= 0 && nStoredThisGroup >= static_cast(maxCandidates)) { + break; + } + result.mixedGroupIndex = useMixedMatchingCandidates ? mixedGroupIndex : -1; + if (useMixedMatchingCandidates && !candidatesGroup.empty()) { + result.deltaBc = candidatesGroup.front().deltaBc; + result.deltaPhi = candidatesGroup.front().deltaPhi; + result.deltaR = candidatesGroup.front().deltaR; + result.deltaAttemptsRel = candidatesGroup.front().deltaAttemptsRel; + result.deltaZ = candidatesGroup.front().deltaZ; + } + storedCandidates.push_back(result); + ++nStoredThisGroup; + } + }; + + if (useMixedMatchingCandidates) { + int32_t groupIdx = 0; + for (const auto& candidatesGroup : mchTrackInfo.mixedMatchingCandidates) { + processGroup(candidatesGroup, groupIdx, mchIndex); + groupIdx += 1; + } + } else { + processGroup(mchTrackInfo.matchingCandidates, -1, mchIndex); + } + } + } + + template + void runMlMatching(C const& collisions, + TMUON const& muonTracks, + TMFT const& mftTracks, + o2::analysis::MlResponseMFTMuonMatch& mlResponse, + float matchingPlaneZ, + MatchingCandidates& newMatchingCandidates, + bool useMixedMatchingCandidates) + { + newMatchingCandidates.clear(); + for (const auto& [mchIndex, mchTrackInfo] : mMchTrackInfos) { + auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); + if (!mchTrack.has_collision()) { + continue; + } + + auto collision = collisions.rawIteratorAt(mchTrack.collisionId()); + + // get the tracks parameters, which have been updated by the realignment if enabled + auto mchTrackParIt = mMchTrackPars.find(mchIndex); + if (mchTrackParIt == mMchTrackPars.end()) { + continue; + } + + auto processGroup = [&](const std::vector& candidatesGroup, int32_t mixedGroupIndex, int64_t mchTrackIndex) { + std::vector groupResults; + groupResults.reserve(candidatesGroup.size()); + + for (const auto& candidate : candidatesGroup) { + auto const& muonTrack = (candidate.muonTrackId >= 0) ? muonTracks.rawIteratorAt(candidate.muonTrackId) : mchTrack; + auto const& mftTrack = mftTracks.rawIteratorAt(candidate.mftTrackId); + auto mftTrackParIt = mMftTrackPars.find(candidate.mftTrackId); + if (mftTrackParIt == mMftTrackPars.end()) { + continue; + } + + auto mftTrackProp = mftTrackParIt->second.asTrackParCovFwd(); + auto mchTrackProp = mchTrackParIt->second.asTrackParCovFwd(); + + if (matchingPlaneZ < 0.) { + mftTrackProp = propagateToZMft(mftTrackProp, matchingPlaneZ); + mchTrackProp = propagateToZMch(mchTrackProp, matchingPlaneZ); + } + + std::vector output; + std::vector inputML = mlResponse.getInputFeatures(muonTrack, mftTrack, mchTrack, mftTrackProp, mchTrackProp, collision); + mlResponse.isSelectedMl(inputML, 0, output); + float matchScore = output[0]; + + groupResults.emplace_back(MatchingCandidate{ + .muonTrackId = candidate.muonTrackId, + .mftTrackId = candidate.mftTrackId, + .matchScore = matchScore, + .matchChi2 = -1}); + } + + auto compareMatchingScore = [](const MatchingCandidate& track1, const MatchingCandidate& track2) -> bool { + return (track1.matchScore > track2.matchScore); + }; + std::sort(groupResults.begin(), groupResults.end(), compareMatchingScore); + + int ranking = 1; + for (auto& matchedCandidate : groupResults) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + matchedCandidate.matchRanking = ranking; + ranking += 1; + } + + const int maxCandidates = configMatching.cfgMaxCandidatesPerMchTrack.value; + + auto& storedCandidates = newMatchingCandidates[mchTrackIndex]; + size_t nStoredThisGroup = 0; + for (auto& result : groupResults) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + if (maxCandidates >= 0 && nStoredThisGroup >= static_cast(maxCandidates)) { + break; + } + result.mixedGroupIndex = useMixedMatchingCandidates ? mixedGroupIndex : -1; + if (useMixedMatchingCandidates && !candidatesGroup.empty()) { + result.deltaBc = candidatesGroup.front().deltaBc; + result.deltaPhi = candidatesGroup.front().deltaPhi; + result.deltaR = candidatesGroup.front().deltaR; + result.deltaAttemptsRel = candidatesGroup.front().deltaAttemptsRel; + result.deltaZ = candidatesGroup.front().deltaZ; + } + storedCandidates.push_back(result); + ++nStoredThisGroup; + } + }; + + if (useMixedMatchingCandidates) { + int32_t groupIdx = 0; + for (const auto& candidatesGroup : mchTrackInfo.mixedMatchingCandidates) { + processGroup(candidatesGroup, groupIdx, mchIndex); + groupIdx += 1; + } + } else { + processGroup(mchTrackInfo.matchingCandidates, -1, mchIndex); + } + } + } + + template + void processMatchingCandidates(C const& collisions, + TMUON const& muonTracks, + TMFT const& mftTracks, + CMFT const& mftCovs, + aod::FwdTrkCls const& clusters, + bool useMixedMatchingCandidates) + { + if (configMchRealign.cfgEnableMCHRealign.value) { + runMuonRealignment(muonTracks, clusters); + } + + if (configMftAlignmentCorrections.cfgEnableMftAlignmentCorrections) { + runMftRealignment(mftTracks, mftCovs); + } + + MatchingCandidates newMatchingCandidates; + if (configMatching.cfgCustomMatchingStrategy.value == 0) { + if (hasActiveChi2Matching) { + runChi2Matching(activeChi2FunctionName, activeChi2MatchingPlaneZ, newMatchingCandidates, useMixedMatchingCandidates); + } + } else { + if (hasActiveMlMatching) { + runMlMatching(collisions, muonTracks, mftTracks, activeMlResponse, activeMlMatchingPlaneZ, newMatchingCandidates, useMixedMatchingCandidates); + } + } + + for (const auto& [mchIndex, candidates] : newMatchingCandidates) { + if (candidates.empty()) { + continue; + } + + mStoredMatchingCandidates[mchIndex] = candidates; + } + } + + int32_t countStoredCandidatesForMchTrack(int64_t mchTrackIndex) const + { + const auto candidateIterator = mStoredMatchingCandidates.find(mchTrackIndex); + if (candidateIterator == mStoredMatchingCandidates.end()) { + return 0; + } + return static_cast(candidateIterator->second.size()); + } + + template + void fillGmmCandidateFwdTracks(TMUON const& muonTracks, + TMFT const& mftTracks, + aod::AmbiguousFwdTracks const& ambFwdTracks) + { + mFwdTrackToGmmCandTrkIndex.clear(); + mGmmCandFwdTrackRowIndex = 0; + mHasLastMchAmbiguousBcSlice = false; + mAmbBcSliceByFwdTrackId.clear(); + for (const auto& ambFwdTrack : ambFwdTracks) { + const auto bcIds = ambFwdTrack.bcIds(); + mAmbBcSliceByFwdTrackId[ambFwdTrack.fwdtrackId()] = {bcIds[0], bcIds[1]}; + } + + // First pass: assign GMMCANDTRK row indices for MCH/MCH-MID base entries so that + // MCHTrackId can be remapped consistently even when global muons appear first in FwdTracks. + int32_t nextGmmCandTrkIndex = 0; + for (const auto& track : muonTracks) { + const int trackType = static_cast(track.trackType()); + if (trackType > GlobalTrackTypeMax) { + mFwdTrackToGmmCandTrkIndex[track.globalIndex()] = nextGmmCandTrkIndex; + nextGmmCandTrkIndex += 1 + countStoredCandidatesForMchTrack(track.globalIndex()); + } else if (configMatching.cfgIncludeGlobalMuonsInFwdTracks.value) { + nextGmmCandTrkIndex += 1; + } + } + + // Second pass: fill GMMCANDTRK/GMMCANDTRKCOV in FwdTracks order. + for (const auto& track : muonTracks) { + const int trackType = static_cast(track.trackType()); + + if (trackType > GlobalTrackTypeMax) { + mHasLastMchAmbiguousBcSlice = false; + const int64_t mchTrackIndex = track.globalIndex(); + const int32_t gmmMchTrackId = mFwdTrackToGmmCandTrkIndex.at(mchTrackIndex); + + const auto candidateIterator = mStoredMatchingCandidates.find(mchTrackIndex); + auto mchTrackParIt = mMchTrackPars.find(mchTrackIndex); + fillBaseGmmCandFwdTrack(track, mchTrackParIt->second, gmmMchTrackId, -1.f, -1.f, + isMchTrackTagged(mchTrackIndex)); + + if (candidateIterator != mStoredMatchingCandidates.end()) { + for (const auto& candidate : candidateIterator->second) { + auto mftTrackParIt = mMftTrackPars.find(candidate.mftTrackId); + if (mftTrackParIt != mMftTrackPars.end()) { + const auto& mftTrack = mftTracks.rawIteratorAt(candidate.mftTrackId); + fillCandidateFwdTrack(track, mchTrackParIt->second, gmmMchTrackId, mftTrack, mftTrackParIt->second, candidate); + } + } + } + } + + if (configMatching.cfgIncludeGlobalMuonsInFwdTracks.value && trackType <= GlobalTrackTypeMax) { + int32_t gmmMchTrackId = -1; + const auto mchIterator = mFwdTrackToGmmCandTrkIndex.find(track.matchMCHTrackId()); + if (mchIterator != mFwdTrackToGmmCandTrkIndex.end()) { + gmmMchTrackId = mchIterator->second; + } + TrackParExt parExt(fwdtrackutils::getTrackParCovFwd(track, track)); + fillBaseGmmCandFwdTrack(track, + parExt, + gmmMchTrackId, + track.chi2MatchMCHMFT(), + track.matchScoreMCHMFT(), + isMchTrackTagged(track.matchMCHTrackId())); + } + } + } + + void runCandidateProcessing(MyEvents const& collisions, + aod::BCsWithTimestamps const& bcs, + MyMuons const& muonTracks, + MyMFTs const& mftTracks, + MyMFTCovariances const& mftCovs, + aod::FwdTrkCls const& clusters, + aod::AmbiguousFwdTracks const& ambFwdTracks, + bool useMixedMatchingCandidates) + { + auto bc = bcs.begin(); + initCcdb(bc); + + LOGF(info, "Filling MFT cov"); + mftTrackCovs.clear(); + for (const auto& mftTrackCov : mftCovs) { + mftTrackCovs[mftTrackCov.matchMFTTrackId()] = mftTrackCov.globalIndex(); + } + + mStoredMatchingCandidates.clear(); + mFwdTrackToGmmCandTrkIndex.clear(); + mMchTrackInfos.clear(); + + LOGF(info, "Preparing candidates"); + prepareMatchingCandidates(collisions, bcs, muonTracks, mftTracks, mftCovs); + if (useMixedMatchingCandidates) { + prepareEventMixingMatchingCandidates(collisions, bcs, muonTracks); + } + + LOGF(info, "Processing candidates"); + processMatchingCandidates(collisions, muonTracks, mftTracks, mftCovs, clusters, useMixedMatchingCandidates); + + LOGF(info, "Filling tables"); + fillGmmCandidateFwdTracks(muonTracks, mftTracks, ambFwdTracks); + } + + void processData(MyEvents const& collisions, + aod::BCsWithTimestamps const& bcs, + MyMuons const& muonTracks, + MyMFTs const& mftTracks, + MyMFTCovariances const& mftCovs, + aod::FwdTrkCls const& clusters, + aod::AmbiguousFwdTracks const& ambFwdTracks) + { + runCandidateProcessing(collisions, bcs, muonTracks, mftTracks, mftCovs, clusters, ambFwdTracks, false); + } + + void processMixedData(MyEvents const& collisions, + aod::BCsWithTimestamps const& bcs, + MyMuons const& muonTracks, + MyMFTs const& mftTracks, + MyMFTCovariances const& mftCovs, + aod::FwdTrkCls const& clusters, + aod::AmbiguousFwdTracks const& ambFwdTracks) + { + runCandidateProcessing(collisions, bcs, muonTracks, mftTracks, mftCovs, clusters, ambFwdTracks, true); + } + + PROCESS_SWITCH(GlobalMuonMatching, processData, "processData", true); + PROCESS_SWITCH(GlobalMuonMatching, processMixedData, "process event-mixed matching candidates", false); +}; + +// Extends the fwdtracksrealign table with expression columns +struct GlobalMuonMatchingSpawner { + Spawns realignFwdTrksCov; + Spawns realignFwdTrks; + void init(InitContext const&) {} +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +}; diff --git a/PWGDQ/Tasks/jPsiMuonCorrelations.cxx b/PWGDQ/Tasks/jPsiMuonCorrelations.cxx new file mode 100644 index 00000000000..f53f4229b9f --- /dev/null +++ b/PWGDQ/Tasks/jPsiMuonCorrelations.cxx @@ -0,0 +1,284 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file jPsiMuonCorrelations.cxx +/// \brief Task to perform correlations between J/Psi candidates and single muon tracks +/// \author Kaare Endrup Iversen Lund University +// +#include "PWGDQ/Core/VarManager.h" +#include "PWGDQ/DataModel/ReducedInfoTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using std::string; + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod; + +// Some definitions +namespace o2::aod +{ + +namespace dqanalysisflags +{ +DECLARE_SOA_BITMAP_COLUMN(IsEventSelected, isEventSelected, 8); //! Event decision +DECLARE_SOA_BITMAP_COLUMN(IsMuonSelected, isMuonSelected, 32); //! Muon track decisions (joinable to ReducedMuonsAssoc) +} // namespace dqanalysisflags + +DECLARE_SOA_TABLE(EventCuts, "AOD", "DQANAEVCUTSA", dqanalysisflags::IsEventSelected); //! joinable to ReducedEvents +DECLARE_SOA_TABLE(MuonTrackCuts, "AOD", "DQANAMUONCUTSA", dqanalysisflags::IsMuonSelected); //! joinable to ReducedMuonsAssoc +} // namespace o2::aod + +// Declarations of various short names +using MyEvents = soa::Join; +using MyEventsSelected = soa::Join; + +// using MyPairCandidatesSelected = soa::Join; +using MyPairCandidatesSelected = soa::Join; +using MyMuonTracks = soa::Join; +using MyMuonAssocsSelected = soa::Join; + +// bit maps used for the Fill functions of the VarManager +constexpr static uint32_t GkEventFillMap = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended; +constexpr static uint32_t GkMuonFillMap = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra; + +// Declare helper function +double getRapidity(const double pT, const double eta); +double getWeight(const double pT, const std::vector& binsPT, const std::vector& efficiency, const double etaMin, const double etaMax); + +struct DqJPsiMuonCorrelations { + + // Configurables for the dilepton signal region + Configurable fConfigDileptonLowMass{"fConfigDileptonLowMass", 2.8, "Low mass cut for the dileptons used in analysis"}; + Configurable fConfigDileptonHighMass{"fConfigDileptonHighMass", 3.4, "High mass cut for the dileptons used in analysis"}; + Configurable fConfigBackgroundLowMass{"fConfigBackgroundLowMass", 2.5, "Low mass cut for the background used in analysis"}; + Configurable fConfigBackgroundHighMass{"fConfigBackgroundHighMass", 3.7, "High mass cut for the background used in analysis"}; + + // Configurables for the dilepton and associated muon cuts + Configurable fConfigDileptonPtMin{"fConfigDileptonPtMin", 1.0, "Minimum pT cut for the dilepton"}; + Configurable fConfigDileptonPtMax{"fConfigDileptonPtMax", 20.0, "Maximum pT cut for the dilepton"}; + Configurable fConfigDileptonEtaMin{"fConfigDileptonEtaMin", -4.0, "Minimum eta cut for the dileptons"}; + Configurable fConfigDileptonEtaMax{"fConfigDileptonEtaMax", -2.5, "Maximum eta cut for the dileptons"}; + Configurable fConfigMuonEtaMin{"fConfigMuonEtaMin", -4.0, "Minimum eta cut for the associated muons"}; + Configurable fConfigMuonEtaMax{"fConfigMuonEtaMax", -2.5, "Maximum eta cut for the associated muons"}; + + // Configurables for histograms + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.0f, 14.0f, 16.0f, 18.0f, 20.0f}, "p_{T} (GeV/c)"}; + ConfigurableAxis axisInvMass{"axisInvMass", {80, 1.0f, 5.0f}, "Invariant Mass (GeV/c^{2})"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {10, -constants::math::PIHalf, 3.0f * constants::math::PIHalf}, "#Delta#phi (rad)"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {10, -2.0f, 2.0f}, "#Delta#eta"}; + + // Configurable for acceptance efficiency correction + Configurable> fConfigBinEffJPsi{"fConfigBinEffJPsi", std::vector{1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, "acceptance efficiency correction factors for each pT bin"}; + Configurable> fConfigBinEffMuon{"fConfigBinEffMuon", std::vector{1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}, "acceptance efficiency correction factors for each pT bin"}; + + // Connect to ccdb + Service ccdb{}; + Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + + // Define the filter for events + Filter eventFilter = aod::dqanalysisflags::isEventSelected == 1; + + // Define the filter for the dileptons + Filter dileptonFilter = (aod::reducedpair::pt > fConfigDileptonPtMin && aod::reducedpair::pt < fConfigDileptonPtMax) && (aod::reducedpair::eta > fConfigDileptonEtaMin && aod::reducedpair::eta < fConfigDileptonEtaMax); + + constexpr static uint32_t FgDimuonsFillMap = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::Pair; // fill map + + // use two values array to avoid mixing up the quantities + std::vector fValuesDilepton; + std::vector fValuesMuon; + + HistogramRegistry registry{"registry"}; + + void init(o2::framework::InitContext&) + { + ccdb->setURL(ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setCreatedNotAfter(ccdbNoLaterThan.value); + + // Assert correct size of the efficiency correction vector + uint8_t nAxisPtBins = axisPt.value.size() - 2; + if (nAxisPtBins != fConfigBinEffJPsi.value.size() || nAxisPtBins != fConfigBinEffMuon.value.size()) { + LOGF(fatal, "Configurables axisPt: %zu must have one more value than fConfigBinEffJPsi: %zu and fConfigBinEffMuon: %zu (excluding 'VARIABLE_WIDTH' entry)", + axisPt.value.size() - 1, fConfigBinEffJPsi.value.size(), fConfigBinEffMuon.value.size()); + } + + fValuesDilepton.resize(VarManager::kNVars, 0.0f); + fValuesMuon.resize(VarManager::kNVars, 0.0f); + VarManager::SetDefaultVarNames(); + + // Define trigger histograms + ConfigurableAxis axisTriggerMass{"axisTriggerMass", {VARIABLE_WIDTH, fConfigBackgroundLowMass, fConfigDileptonLowMass, fConfigDileptonHighMass, fConfigBackgroundHighMass}, "Invariant Mass (GeV/c^{2}) for trigger counting"}; + for (const auto& signDilepton : {"dileptonOS", "dileptonSS"}) { + registry.add(Form("%s/h2dDimuonPtInvVsInvMass", signDilepton), "h2dDimuonPtInvVsInvMass", kTH2D, {axisInvMass, axisPt}); + registry.add(Form("%s/h2dTriggersPtInvVsInvMassRegion", signDilepton), "h2dTriggersPtInvVsInvMassRegion", kTH2D, {axisTriggerMass, axisPt}); + + // Define histograms for the dilepton-muon correlations + registry.add(Form("%s/h2dDimuonMuonDeltaEtaVsMuonPtSignal", signDilepton), "h2dDimuonMuonDeltaEtaVsMuonPtSignal", kTH2D, {axisDeltaEta, axisPt}); + registry.add(Form("%s/h2dDimuonMuonDeltaPhiVsMuonPtSignal", signDilepton), "h2dDimuonMuonDeltaPhiVsMuonPtSignal", kTH2D, {axisDeltaPhi, axisPt}); + registry.add(Form("%s/h2dDimuonMuonDeltaEtaVsMuonPtBackground", signDilepton), "h2dDimuonMuonDeltaEtaVsMuonPtBackground", kTH2D, {axisDeltaEta, axisPt}); + registry.add(Form("%s/h2dDimuonMuonDeltaPhiVsMuonPtBackground", signDilepton), "h2dDimuonMuonDeltaPhiVsMuonPtBackground", kTH2D, {axisDeltaPhi, axisPt}); + } + + // QA histograms + registry.add("hEventPosZMuon", "hEventPosZMuon", kTH1D, {{50, -25, 25}}); + } + + // Template function to run pair - muon combinations + template + void runDileptonMuon(TEvent const& event, TMuonAssocs const& assocs, TMuonTracks const& /*tracks*/, TDileptons const& dileptons) + { + VarManager::ResetValues(0, VarManager::kNVars, fValuesMuon.data()); + VarManager::ResetValues(0, VarManager::kNVars, fValuesDilepton.data()); + VarManager::FillEvent(event, fValuesMuon.data()); + VarManager::FillEvent(event, fValuesDilepton.data()); + + if (!event.isEventSelected_bit(0)) { + return; + } + + if (assocs.size() > 0) { + registry.fill(HIST("hEventPosZMuon"), event.posZ()); + } + + if (dileptons.size() > 0) { + + for (const auto& dilepton : dileptons) { + VarManager::FillTrack(dilepton, fValuesDilepton.data()); + + // Dilepton leg kinematic cuts + if ((dilepton.eta1() < fConfigMuonEtaMin || dilepton.eta1() > fConfigMuonEtaMax) || + (dilepton.pt1() < axisPt.value[1] || dilepton.pt1() > axisPt.value.back()) || + (dilepton.eta2() < fConfigMuonEtaMin || dilepton.eta2() > fConfigMuonEtaMax) || + (dilepton.pt2() < axisPt.value[1] || dilepton.pt2() > axisPt.value.back())) { + continue; + } + + // Fill invariant mass vs pT histogram for the dileptons and for trigger counting + double weightDilepton = getWeight(dilepton.pt(), axisPt.value, fConfigBinEffJPsi.value, fConfigDileptonEtaMin, fConfigDileptonEtaMax); + + if (dilepton.sign() == 0) { + registry.fill(HIST("dileptonOS/h2dDimuonPtInvVsInvMass"), dilepton.mass(), dilepton.pt(), weightDilepton); + registry.fill(HIST("dileptonOS/h2dTriggersPtInvVsInvMassRegion"), dilepton.mass(), dilepton.pt(), weightDilepton); + } else { + registry.fill(HIST("dileptonSS/h2dDimuonPtInvVsInvMass"), dilepton.mass(), dilepton.pt(), weightDilepton); + registry.fill(HIST("dileptonSS/h2dTriggersPtInvVsInvMassRegion"), dilepton.mass(), dilepton.pt(), weightDilepton); + } + + for (const auto& assoc : assocs) { + // Check selection bit + if (!assoc.isMuonSelected_bit(0)) { + continue; + } + + // Skip associated muons that are part of the dilepton candidate + if (dilepton.index0Id() == assoc.reducedmuonId() || dilepton.index1Id() == assoc.reducedmuonId()) { + continue; + } + + // Get muon track information + auto track = assoc.template reducedmuon_as(); + + // Muon kinematic cuts + if ((track.eta() < fConfigMuonEtaMin || track.eta() > fConfigMuonEtaMax) || + (track.pt() < axisPt.value[1] || track.pt() > axisPt.value.back())) { + continue; + } + + // Compute deltaEta and deltaPhi between the dilepton and the associated muon + float deltaEta = dilepton.eta() - track.eta(); + float deltaPhi = RecoDecay::constrainAngle(dilepton.phi() - track.phi(), -constants::math::PIHalf); + + // Fill signal and background histograms based on the dilepton mass + double weightMuon = getWeight(track.pt(), axisPt.value, fConfigBinEffMuon.value, fConfigMuonEtaMin, fConfigMuonEtaMax); + + if (dilepton.mass() > fConfigDileptonLowMass && dilepton.mass() < fConfigDileptonHighMass) { + if (dilepton.sign() == 0) { + registry.fill(HIST("dileptonOS/h2dDimuonMuonDeltaEtaVsMuonPtSignal"), deltaEta, track.pt(), weightDilepton * weightMuon); + registry.fill(HIST("dileptonOS/h2dDimuonMuonDeltaPhiVsMuonPtSignal"), deltaPhi, track.pt(), weightDilepton * weightMuon); + } else { + registry.fill(HIST("dileptonSS/h2dDimuonMuonDeltaEtaVsMuonPtSignal"), deltaEta, track.pt(), weightDilepton * weightMuon); + registry.fill(HIST("dileptonSS/h2dDimuonMuonDeltaPhiVsMuonPtSignal"), deltaPhi, track.pt(), weightDilepton * weightMuon); + } + } else if (dilepton.mass() > fConfigBackgroundLowMass && dilepton.mass() < fConfigBackgroundHighMass) { + if (dilepton.sign() == 0) { + registry.fill(HIST("dileptonOS/h2dDimuonMuonDeltaEtaVsMuonPtBackground"), deltaEta, track.pt(), weightDilepton * weightMuon); + registry.fill(HIST("dileptonOS/h2dDimuonMuonDeltaPhiVsMuonPtBackground"), deltaPhi, track.pt(), weightDilepton * weightMuon); + } else { + registry.fill(HIST("dileptonSS/h2dDimuonMuonDeltaEtaVsMuonPtBackground"), deltaEta, track.pt(), weightDilepton * weightMuon); + registry.fill(HIST("dileptonSS/h2dDimuonMuonDeltaPhiVsMuonPtBackground"), deltaPhi, track.pt(), weightDilepton * weightMuon); + } + } + } + } + } + } + + void processSkimmedDimuon(MyEventsSelected::iterator const& event, MyMuonAssocsSelected const& muonassocs, MyMuonTracks const& muontracks, soa::Filtered const& dileptons) + { + runDileptonMuon(event, muonassocs, muontracks, dileptons); + } + void processDummy(MyEvents const&) + { + } + + PROCESS_SWITCH(DqJPsiMuonCorrelations, processSkimmedDimuon, "Run dilepton-muon pairing, using skimmed data", false); + PROCESS_SWITCH(DqJPsiMuonCorrelations, processDummy, "Dummy function", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} + +double getWeight(const double pT, const std::vector& binsPT, const std::vector& efficiency, const double etaMin, const double etaMax) +{ + + int binEff = -1; + for (size_t b = 0; b < binsPT.size() - 1; ++b) { + // Shift pT index by one to account for the VARIABLE_WIDTH entry in the axis configuration + if (pT >= binsPT[b + 1] && pT < binsPT[b + 2]) { + binEff = b; + break; + } + } + + if (binEff == -1) { + LOGF(warn, "pT value %f is outside the defined pT bins", pT); + return 0.0; + } + return 1.0 / (efficiency[binEff] * (RecoDecayPtEtaPhi::y(pT, etaMax, o2::constants::physics::MassJPsi) - RecoDecayPtEtaPhi::y(pT, etaMin, o2::constants::physics::MassJPsi))); +} diff --git a/PWGDQ/Tasks/mchAlignRecord.cxx b/PWGDQ/Tasks/mchAlignRecord.cxx index e2a9814bd20..8cee87e4245 100644 --- a/PWGDQ/Tasks/mchAlignRecord.cxx +++ b/PWGDQ/Tasks/mchAlignRecord.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,6 @@ #include #include -#include #include #include @@ -113,7 +113,7 @@ struct mchAlignRecordTask { "List of param mask for d.o.f to be fixed"}; } fFixDetElem; - Preslice perMuon = aod::fwdtrkcl::fwdtrackId; + Preslice perMuon = aod::fwdtrkcl::fwdtrackId; void init(InitContext& ic) { diff --git a/PWGDQ/Tasks/mftMchMatcher.cxx b/PWGDQ/Tasks/mftMchMatcher.cxx index 860ded115f8..6149433f586 100644 --- a/PWGDQ/Tasks/mftMchMatcher.cxx +++ b/PWGDQ/Tasks/mftMchMatcher.cxx @@ -11,6 +11,10 @@ // /// \file mftMchMatcher.cxx /// \brief MFT-MCH matching tool for data preparation +/// \author Andrea Ferrero +/// \author Marice Coquet +/// \author Nivendra Ramasubramanian +/// \author Yoshini Bailung #include "PWGDQ/Core/VarManager.h" @@ -22,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -42,12 +47,12 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -77,7 +82,7 @@ using namespace std; using SMatrix55 = ROOT::Math::SMatrix>; using SMatrix55Std = ROOT::Math::SMatrix; -using SMatrix5 = ROOT::Math::SVector; +using SMatrix5 = ROOT::Math::SVector; // o2-linter: disable=root/entity (ROOT object is needed) namespace o2::aod { @@ -142,9 +147,10 @@ DECLARE_SOA_COLUMN(C1PtTglMFT, c1PtTglMFT, float); DECLARE_SOA_COLUMN(Chi2Glob, chi2Glob, float); DECLARE_SOA_COLUMN(Chi2Match, chi2Match, float); DECLARE_SOA_COLUMN(IsAmbig, isAmbig, bool); -DECLARE_SOA_COLUMN(MFTMult, mftMult, int); -DECLARE_SOA_COLUMN(DCAX, dcaX, float); -DECLARE_SOA_COLUMN(DCAY, dcaY, float); +DECLARE_SOA_COLUMN(MFTMult, mftMult, int); // o2-linter: disable=name/o2-column (keep temporarily) +DECLARE_SOA_COLUMN(MatchAttempts, matchAttempts, int); +DECLARE_SOA_COLUMN(DCAX, dcaX, float); // o2-linter: disable=name/o2-column (keep temporarily) +DECLARE_SOA_COLUMN(DCAY, dcaY, float); // o2-linter: disable=name/o2-column (keep temporarily) DECLARE_SOA_COLUMN(McMaskGlob, mcMaskGlob, int); DECLARE_SOA_COLUMN(MatchLabel, matchLabel, int); DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); @@ -207,6 +213,7 @@ DECLARE_SOA_TABLE(FwdMatchMLCandidates, "AOD", "FWDMLCAND", fwdmatchcandidates::DCAY, fwdmatchcandidates::IsAmbig, fwdmatchcandidates::MFTMult, + fwdmatchcandidates::MatchAttempts, fwdmatchcandidates::McMaskMCH, fwdmatchcandidates::McMaskMFT, fwdmatchcandidates::McMaskGlob, @@ -214,35 +221,40 @@ DECLARE_SOA_TABLE(FwdMatchMLCandidates, "AOD", "FWDMLCAND", fwdmatchcandidates::IsSignal); } // namespace o2::aod -struct mftMchMatcher { +struct mftMchMatcher { // o2-linter: disable=name/struct (keep temporarily) + static constexpr int GlobalTrackTypeMax = 2; + static constexpr int ThetaAbsBoundaryDeg = 3; + static constexpr double SlopeResolutionZ = 535.; + Produces fwdMatchMLCandidates; //// Variables for selecting muon tracks - Configurable fPMchLow{"cfgPMchLow", 0.0f, ""}; - Configurable fPtMchLow{"cfgPtMchLow", 0.7f, ""}; - Configurable fEtaMchLow{"cfgEtaMchLow", -4.0f, ""}; - Configurable fEtaMchUp{"cfgEtaMchUp", -2.5f, ""}; - Configurable fRabsLow{"cfgRabsLow", 17.6f, ""}; - Configurable fRabsUp{"cfgRabsUp", 89.5f, ""}; - Configurable fSigmaPdcaUp{"cfgPdcaUp", 6.f, ""}; - Configurable fTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; - Configurable fMatchingChi2MchMidUp{"cfgMatchingChi2MchMidUp", 999.f, ""}; + Configurable cfgPMchLow{"cfgPMchLow", 0.0f, ""}; + Configurable cfgPtMchLow{"cfgPtMchLow", 0.7f, ""}; + Configurable cfgRabsLow{"cfgRabsLow", 17.6f, ""}; + Configurable cfgRabsUp{"cfgRabsUp", 89.5f, ""}; + Configurable cfgPdcaUp{"cfgPdcaUp", 6.f, ""}; + Configurable cfgTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; //// Variables for selecting mft tracks - Configurable fEtaMFTLow{"cfgEtaMFTlow", -3.6f, ""}; - Configurable fEtaMFTUp{"cfgEtaMFTup", -2.5f, ""}; - Configurable fTrackChi2MFTUp{"cfgTrackChi2MFTUp", 10.f, ""}; - Configurable fPtMFTLow{"cfgPtMFTLow", 0.1f, ""}; - - //// Variables for matching configuration - Configurable fMaxCandidates{"cfgMaxCandidates", 0, ""}; - - Configurable fKeepBestMatch{"cfgKeepBestMatch", false, "Keep only the best match global muons in the skimming"}; - Configurable fzMatching{"cfgzMatching", -77.5f, "Plane for MFT-MCH matching"}; - - Configurable fSamplingFraction{"cfgSamplingFraction", 1.f, "Fraction of randomly selected events to be processed"}; + Configurable cfgEtaMFTlow{"cfgEtaMFTlow", -3.6f, ""}; + Configurable cfgEtaMFTup{"cfgEtaMFTup", -2.5f, ""}; + + Configurable cfgKeepBestMatch{"cfgKeepBestMatch", false, "Keep only the best match global muons in the skimming"}; + Configurable cfgzMatching{"cfgzMatching", -77.5f, "Plane for MFT-MCH matching"}; + + Configurable cfgSamplingFraction{"cfgSamplingFraction", 1.f, "Fraction of randomly selected events to be processed"}; + Configurable cfgSamplingFractionTrueLeadingMatches{"cfgSamplingFractionTrueLeadingMatches", 1.f, "Fraction of randomly selected leading true matches to be processed"}; + Configurable cfgSamplingFractionWrongLeadingMatches{"cfgSamplingFractionWrongLeadingMatches", 1.f, "Fraction of randomly selected leading wrong matches to be processed"}; + Configurable cfgSamplingFractionDecayLeadingMatches{"cfgSamplingFractionDecayLeadingMatches", 1.f, "Fraction of randomly selected leading decay matches to be processed"}; + Configurable cfgSamplingFractionFakeLeadingMatches{"cfgSamplingFractionFakeLeadingMatches", 1.f, "Fraction of randomly selected leading fake matches to be processed"}; + Configurable cfgSamplingFractionTrueNonLeadingMatches{"cfgSamplingFractionTrueNonLeadingMatches", 1.f, "Fraction of randomly selected non-leading true matches to be processed"}; + Configurable cfgSamplingFractionWrongNonLeadingMatches{"cfgSamplingFractionWrongNonLeadingMatches", 1.f, "Fraction of randomly selected non-leading wrong matches to be processed"}; + Configurable cfgSamplingFractionDecayNonLeadingMatches{"cfgSamplingFractionDecayNonLeadingMatches", 1.f, "Fraction of randomly selected non-leading decay matches to be processed"}; + Configurable cfgSamplingFractionFakeNonLeadingMatches{"cfgSamplingFractionFakeNonLeadingMatches", 1.f, "Fraction of randomly selected non-leading fake matches to be processed"}; + Configurable cfgSamplingBcOddness{"cfgSamplingBcOddness", -1, "Select only events with even (0) or odd (1) global BCs"}; //// Variables for ccdb - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; @@ -256,14 +268,13 @@ struct mftMchMatcher { kMatchTypeWrongNonLeading = 5, kMatchTypeDecayNonLeading = 6, kMatchTypeFakeNonLeading = 7, - kMatchTypeUndefined + kMatchTypeUndefined = 8 }; - double mBzAtMftCenter{0}; o2::globaltracking::MatchGlobalFwd mExtrap; int mRunNumber{0}; // needed to detect if the run changed and trigger update of magnetic field - Service ccdbManager; + Service ccdbManager{}; o2::ccdb::CcdbApi fCCDBApi; o2::parameters::GRPMagField* fGrpMag = nullptr; @@ -279,6 +290,22 @@ struct mftMchMatcher { HistogramRegistry registry{"registry", {}}; + template + o2::dataformats::GlobalFwdTrack trackToGlobalFwd(const T& track, const C& cov) + { + double chi2 = track.chi2(); + SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); + std::vector v1{cov.cXX(), cov.cXY(), cov.cYY(), cov.cPhiX(), cov.cPhiY(), + cov.cPhiPhi(), cov.cTglX(), cov.cTglY(), cov.cTglPhi(), cov.cTglTgl(), + cov.c1PtX(), cov.c1PtY(), cov.c1PtPhi(), cov.c1PtTgl(), cov.c1Pt21Pt2()}; + SMatrix55 tcovs(v1.begin(), v1.end()); + o2::track::TrackParCovFwd trackparCov{track.z(), tpars, tcovs, chi2}; + o2::dataformats::GlobalFwdTrack fwdtrack; + fwdtrack.setParameters(trackparCov.getParameters()); + fwdtrack.setCovariances(trackparCov.getCovariances()); + return fwdtrack; + } + template bool pDCACut(const T& mchTrack, const C& collision, double nSigmaPDCA) { @@ -287,7 +314,9 @@ struct mftMchMatcher { static const double relPRes = 0.0004; static const double slopeRes = 0.0005; - double thetaAbs = TMath::ATan(mchTrack.rAtAbsorberEnd() / 505.) * TMath::RadToDeg(); + constexpr double AbsorberEndZ = 505.; + constexpr double RadToDeg = 180. / o2::constants::math::PI; + double thetaAbs = std::atan(mchTrack.rAtAbsorberEnd() / AbsorberEndZ) * RadToDeg; // propagate muon track to vertex auto trackConv = VarManager::FwdToTrackPar(mchTrack, mchTrack); @@ -302,20 +331,17 @@ struct mftMchMatcher { double p = mchTrackAtVertex.p(); double pDCA = mchTrack.pDca(); - double sigmaPDCA = (thetaAbs < 3) ? sigmaPDCA23 : sigmaPDCA310; + double sigmaPDCA = (thetaAbs < ThetaAbsBoundaryDeg) ? sigmaPDCA23 : sigmaPDCA310; double nrp = nSigmaPDCA * relPRes * p; double pResEffect = sigmaPDCA / (1. - nrp / (1. + nrp)); - double slopeResEffect = 535. * slopeRes * p; - double sigmaPDCAWithRes = TMath::Sqrt(pResEffect * pResEffect + slopeResEffect * slopeResEffect); - if (pDCA > nSigmaPDCA * sigmaPDCAWithRes) { - return false; - } + double slopeResEffect = SlopeResolutionZ * slopeRes * p; + double sigmaPDCAWithRes = std::sqrt(pResEffect * pResEffect + slopeResEffect * slopeResEffect); - return true; + return (pDCA <= nSigmaPDCA * sigmaPDCAWithRes); } template - bool IsGoodMuon(const T& mchTrack, const C& collision, + bool isGoodMuon(const T& mchTrack, const C& collision, double chi2Cut, double pCut, double pTCut, @@ -324,8 +350,9 @@ struct mftMchMatcher { double nSigmaPdcaCut) { // chi2 cut - if (mchTrack.chi2() > chi2Cut) + if (mchTrack.chi2() > chi2Cut) { return false; + } // momentum cut if (mchTrack.p() < pCut) { @@ -358,14 +385,15 @@ struct mftMchMatcher { } template - bool IsGoodMFT(const T& mftTrack, + bool isGoodMFT(const T& mftTrack, double chi2Cut, double pTCut, std::array etaCut) { // chi2 cut - if (mftTrack.chi2() > chi2Cut) + if (mftTrack.chi2() > chi2Cut) { return false; + } // transverse momentum cut if (mftTrack.pt() < pTCut) { @@ -384,8 +412,9 @@ struct mftMchMatcher { template void initCCDB(BC const& bc) { - if (mRunNumber == bc.runNumber()) + if (mRunNumber == bc.runNumber()) { return; + } fGrpMag = ccdbManager->getForTimeStamp(grpmagPath, bc.timestamp()); @@ -400,10 +429,10 @@ struct mftMchMatcher { void init(o2::framework::InitContext&) { // Load geometry - ccdbManager->setURL(ccdburl); + ccdbManager->setURL(ccdbUrl); ccdbManager->setCaching(true); ccdbManager->setLocalObjectValidityChecking(); - fCCDBApi.init(ccdburl); + fCCDBApi.init(ccdbUrl); mRunNumber = 0; if (!o2::base::GeometryManager::isGeometryLoaded()) { @@ -411,7 +440,7 @@ struct mftMchMatcher { ccdbManager->get(geoPath); } - if (fSamplingFraction < 1.0) { + if (cfgSamplingFraction < 1.0) { std::random_device rd; mGenerator = std::mt19937(rd()); } @@ -430,6 +459,17 @@ struct mftMchMatcher { hMatchType->GetXaxis()->SetBinLabel(7, "decay (non leading)"); hMatchType->GetXaxis()->SetBinLabel(8, "fake (non leading)"); hMatchType->GetXaxis()->SetBinLabel(9, "undefined"); + + auto hMatchTypeAccepted = std::get>(registry.add("matchTypeAccepted", "Match type (accepted)", {HistType::kTH1F, {matchTypeAxis}})); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(1, "true (leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(2, "wrong (leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(3, "decay (leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(4, "fake (leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(5, "true (non leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(6, "wrong (non leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(7, "decay (non leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(8, "fake (non leading)"); + hMatchTypeAccepted->GetXaxis()->SetBinLabel(9, "undefined"); } template @@ -438,7 +478,7 @@ struct mftMchMatcher { fBestMatch.clear(); std::unordered_map> mCandidates; for (const auto& muon : muons) { - if (static_cast(muon.trackType()) < 2) { + if (static_cast(muon.trackType()) < GlobalTrackTypeMax) { auto muonID = muon.matchMCHTrackId(); auto chi2 = muon.chi2MatchMCHMFT(); if (mCandidates.find(muonID) == mCandidates.end()) { @@ -450,7 +490,7 @@ struct mftMchMatcher { } } } - for (auto& pairCand : mCandidates) { + for (const auto& pairCand : mCandidates) { fBestMatch[pairCand.second.second] = true; } } @@ -460,12 +500,10 @@ struct mftMchMatcher { TMFT const& mftTracks, std::vector>& matchablePairs) { - static constexpr int muonPdgCode = 13; - // outer loop on muon tracks for (const auto& muonTrack : muonTracks) { // only consider MCH standalone or MCH-MID matches - if (static_cast(muonTrack.trackType()) <= 2) { + if (static_cast(muonTrack.trackType()) <= GlobalTrackTypeMax) { continue; } @@ -473,7 +511,6 @@ struct mftMchMatcher { if (!muonTrack.has_collision()) { continue; } - auto muonCollisionId = muonTrack.collisionId(); // skip tracks that do not have an associated MC particle if (!muonTrack.has_mcParticle()) { @@ -481,23 +518,11 @@ struct mftMchMatcher { } // get the index associated to the MC particle auto muonMcParticle = muonTrack.mcParticle(); - if (std::abs(muonMcParticle.pdgCode()) != muonPdgCode) { - continue; - } int64_t muonMcTrackIndex = muonMcParticle.globalIndex(); // inner loop on MFT tracks for (const auto& mftTrack : mftTracks) { - // only consider MFT tracks associated to the same collision as the muon track - if (!mftTrack.has_collision()) { - continue; - } - auto mftCollisionId = mftTrack.collisionId(); - if (mftCollisionId != muonCollisionId) { - continue; - } - // skip tracks that do not have an associated MC particle if (!mftTrack.has_mcParticle()) { continue; @@ -517,8 +542,9 @@ struct mftMchMatcher { bool isPairedMuon(int64_t muonTrackId, const std::vector>& matchablePairs) { for (const auto& [id1, id2] : matchablePairs) { - if (muonTrackId == id1) + if (muonTrackId == id1) { return true; + } } return false; } @@ -526,6 +552,9 @@ struct mftMchMatcher { template bool isDecay(TMCH const& mchTrack, TMFTs const& mftTracks) { + if (!mchTrack.has_mcParticle()) { + return false; + } const auto& mchMcParticle = mchTrack.mcParticle(); const auto& mchMotherParticles = mchMcParticle.template mothers_as(); @@ -537,8 +566,9 @@ struct mftMchMatcher { // search for an MFT track that is associated to the MCH mother particle for (const auto& mftTrack : mftTracks) { // skip tracks that do not have an associated MC particle - if (!mftTrack.has_mcParticle()) + if (!mftTrack.has_mcParticle()) { continue; + } if (mftTrack.mcParticle().globalIndex() == mchMotherParticle.globalIndex()) { return true; @@ -557,19 +587,13 @@ struct mftMchMatcher { { MuonMatchType result{kMatchTypeUndefined}; - if (static_cast(muonTrack.trackType()) > 2) { + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { return result; } auto const& mchTrack = muonTrack.template matchMCHTrack_as(); auto const& mftTrack = muonTrack.template matchMFTTrack_as(); - if (!muonTrack.has_mcParticle() || !mftTrack.has_mcParticle()) { - // if either the MCH or the MFT tracks are fakes (not associated to any MC particles) - // we consider the match as fake - return (isBestMatch ? kMatchTypeFakeLeading : kMatchTypeFakeNonLeading); - } - bool isPaired = isPairedMuon(mchTrack.globalIndex(), matchablePairs); std::pair matchPair{mchTrack.globalIndex(), mftTrack.globalIndex()}; bool isTrueMatch = std::find(matchablePairs.begin(), matchablePairs.end(), matchPair) != matchablePairs.end(); @@ -588,25 +612,55 @@ struct mftMchMatcher { return result; } - - void processMC(MyEvents const& collisions, - aod::BCsWithTimestamps const& bcs, - MyMuonsMC const& muonTracks, - MyMFTsMC const& mftTracks, - MyMFTCovariances const& mftCovs, - aod::McParticles const& /*mcParticles*/) + template + int getMftMchMatchAttempts(EVT const& collisions, + BC const& bcs, + TMUON const& mchTrack, + TMFTS const& mftTracks) { - if (bcs.size() > 0) { - auto bc = bcs.begin(); - initCCDB(bc); - VarManager::SetMatchingPlane(fzMatching.value); + if (!mchTrack.has_collision()) { + return 0; } + const auto& collMch = collisions.rawIteratorAt(mchTrack.collisionId()); + const auto& bcMch = bcs.rawIteratorAt(collMch.bcId()); + + int attempts{0}; + for (const auto& mftTrack : mftTracks) { + if (!mftTrack.has_collision()) { + continue; + } + + const auto& collMft = collisions.rawIteratorAt(mftTrack.collisionId()); + const auto& bcMft = bcs.rawIteratorAt(collMft.bcId()); + + int64_t deltaBc = static_cast(bcMft.globalBC()) - static_cast(bcMch.globalBC()); + double deltaBcNS = o2::constants::lhc::LHCBunchSpacingNS * deltaBc; + double deltaTrackTime = mftTrack.trackTime() - mchTrack.trackTime() + deltaBcNS; + double trackTimeResTot = mftTrack.trackTimeRes() + mchTrack.trackTimeRes(); + + if (std::fabs(deltaTrackTime) > trackTimeResTot) { + continue; + } + attempts += 1; + } + + return attempts; + } + + template + void fillTable(TCOLLS const& collisions, + TBCS const& bcs, + TMUONS const& muonTracks, + TMFTS const& mftTracks, + TCOVS const& mftCovs) + { + std::unordered_map matchAttemptsMap; registry.get(HIST("acceptedEvents"))->Fill(0); // reject a randomly selected fraction of events - if (fSamplingFraction < 1.0) { + if (cfgSamplingFraction < 1.0) { double rnd = mDistribution(mGenerator); - if (rnd > fSamplingFraction) { + if (rnd > cfgSamplingFraction) { return; } } @@ -615,16 +669,16 @@ struct mftMchMatcher { fillBestMuonMatches(muonTracks); std::vector> matchablePairs; - fillMatchablePairs(muonTracks, mftTracks, matchablePairs); + if constexpr (isMc) { + fillMatchablePairs(muonTracks, mftTracks, matchablePairs); + } mftCovIndexes.clear(); - for (auto& mftTrackCov : mftCovs) { + for (const auto& mftTrackCov : mftCovs) { mftCovIndexes[mftTrackCov.matchMFTTrackId()] = mftTrackCov.globalIndex(); } - fwdMatchMLCandidates.reserve(muonTracks.size()); - - for (auto muon : muonTracks) { + for (const auto& muon : muonTracks) { // only consider global MFT-MCH-MID matches if (static_cast(muon.trackType()) != 0) { continue; @@ -636,47 +690,111 @@ struct mftMchMatcher { bool isBestMatch = fBestMatch.find(muon.globalIndex()) != fBestMatch.end(); - if (fKeepBestMatch && !isBestMatch) { + if (cfgKeepBestMatch && !isBestMatch) { continue; } const auto& collision = collisions.rawIteratorAt(muon.collisionId()); - auto bc_coll = collision.bc_as(); + auto bcColl = collision.template bc_as(); - auto muontrack = muon.template matchMCHTrack_as(); - auto mfttrack = muon.template matchMFTTrack_as(); + auto muontrack = muon.template matchMCHTrack_as(); + auto mfttrack = muon.template matchMFTTrack_as(); auto const& mfttrackcov = mftCovs.rawIteratorAt(mftCovIndexes[mfttrack.globalIndex()]); - auto muonTime = muontrack.trackTime() + bc_coll.globalBC() * o2::constants::lhc::LHCBunchSpacingNS; - auto mftTime = mfttrack.trackTime() + bc_coll.globalBC() * o2::constants::lhc::LHCBunchSpacingNS; + auto muonTime = muontrack.trackTime() + bcColl.globalBC() * o2::constants::lhc::LHCBunchSpacingNS; + auto mftTime = mfttrack.trackTime() + bcColl.globalBC() * o2::constants::lhc::LHCBunchSpacingNS; o2::track::TrackParCovFwd mftprop = VarManager::FwdToTrackPar(mfttrack, mfttrackcov); - o2::track::TrackParCovFwd muonprop = VarManager::FwdToTrackPar(muontrack, muontrack); - if (fzMatching.value < 0.) { - mftprop = VarManager::PropagateFwd(mfttrack, mfttrackcov, fzMatching.value); + o2::dataformats::GlobalFwdTrack muonprop = trackToGlobalFwd(muontrack, muontrack); + if (cfgzMatching.value < 0.) { + mftprop = VarManager::PropagateFwd(mfttrack, mfttrackcov, cfgzMatching.value); muonprop = VarManager::PropagateMuon(muontrack, collision, VarManager::kToMatching); } auto muonpropCov = muonprop.getCovariances(); auto mftpropCov = mftprop.getCovariances(); - if (!IsGoodMuon(muontrack, collision, fTrackChi2MchUp, fPMchLow, fPtMchLow, {fEtaMFTLow, fEtaMFTUp}, {fRabsLow, fRabsUp}, fSigmaPdcaUp)) { + if (!isGoodMuon(muontrack, collision, cfgTrackChi2MchUp, cfgPMchLow, cfgPtMchLow, {cfgEtaMFTlow, cfgEtaMFTup}, {cfgRabsLow, cfgRabsUp}, cfgPdcaUp)) { continue; } // at this level we consider all the matching candidates, regardless of the MFT tracks quality // MFT track quality cuts should be applied only after having selected the best candidate - // if (!IsGoodMFT(mfttrack, fTrackChi2MFTUp, fPtMFTLow, {fEtaMFTLow, fEtaMFTUp})){ + // if (!isGoodMFT(mfttrack, cfgTrackChi2MFTUp, cfgPtMFTLow, {cfgEtaMFTlow, cfgEtaMFTup})){ // continue; //} - bool IsAmbig = (muon.compatibleCollIds().size() != 1); - int MFTMult = collision.mftNtracks(); + bool isAmbig = (muon.compatibleCollIds().size() != 1); + int mftMult = collision.mftNtracks(); + int matchAttempts = 0; + auto matchAttemptsIt = matchAttemptsMap.find(muontrack.globalIndex()); + if (matchAttemptsIt == matchAttemptsMap.end()) { + matchAttempts = getMftMchMatchAttempts(collisions, bcs, muontrack, mftTracks); + matchAttemptsMap.insert(std::make_pair(static_cast(muontrack.globalIndex()), matchAttempts)); + } else { + matchAttempts = matchAttemptsIt->second; + } - auto matchType = getMatchType(muon, muonTracks, mftTracks, matchablePairs, isBestMatch); + auto matchType = kMatchTypeUndefined; + if constexpr (isMc) { + matchType = getMatchType(muon, muonTracks, mftTracks, matchablePairs, isBestMatch); + } bool isSignal = (matchType == kMatchTypeTrueLeading) || (matchType == kMatchTypeTrueNonLeading); + int mcMaskMuon = 0; + int mcMaskMft = 0; + int ncMaskGlob = 0; + if constexpr (isMc) { + mcMaskMuon = muontrack.mcMask(); + mcMaskMft = mfttrack.mcMask(); + ncMaskGlob = muon.mcMask(); + } + registry.get(HIST("matchType"))->Fill(static_cast(matchType)); + // skipp odd/even BCs if requested + if (cfgSamplingBcOddness.value >= 0 && (static_cast((bcColl.globalBC() % 2)) != cfgSamplingBcOddness.value)) { + continue; + } + + float matchTypeSamplingFraction = 1.0; + switch (matchType) { + case kMatchTypeTrueLeading: + matchTypeSamplingFraction = cfgSamplingFractionTrueLeadingMatches; + break; + case kMatchTypeTrueNonLeading: + matchTypeSamplingFraction = cfgSamplingFractionTrueNonLeadingMatches; + break; + case kMatchTypeWrongLeading: + matchTypeSamplingFraction = cfgSamplingFractionWrongLeadingMatches; + break; + case kMatchTypeWrongNonLeading: + matchTypeSamplingFraction = cfgSamplingFractionWrongNonLeadingMatches; + break; + case kMatchTypeDecayLeading: + matchTypeSamplingFraction = cfgSamplingFractionDecayLeadingMatches; + break; + case kMatchTypeDecayNonLeading: + matchTypeSamplingFraction = cfgSamplingFractionDecayNonLeadingMatches; + break; + case kMatchTypeFakeLeading: + matchTypeSamplingFraction = cfgSamplingFractionFakeLeadingMatches; + break; + case kMatchTypeFakeNonLeading: + matchTypeSamplingFraction = cfgSamplingFractionFakeNonLeadingMatches; + break; + default: + break; + } + + if (matchTypeSamplingFraction < 1.0) { + double rnd = mDistribution(mGenerator); + if (rnd > matchTypeSamplingFraction) { + continue; + } + } + + registry.get(HIST("matchTypeAccepted"))->Fill(static_cast(matchType)); + fwdMatchMLCandidates( muonprop.getX(), muonprop.getY(), @@ -732,21 +850,55 @@ struct mftMchMatcher { muon.chi2MatchMCHMFT(), muon.fwdDcaX(), muon.fwdDcaY(), - IsAmbig, - MFTMult, - muontrack.mcMask(), - mfttrack.mcMask(), - muon.mcMask(), + isAmbig, + mftMult, + matchAttempts, + mcMaskMuon, + mcMaskMft, + ncMaskGlob, static_cast(matchType), isSignal); } } + void processMC(MyEvents const& collisions, + aod::BCsWithTimestamps const& bcs, + MyMuonsMC const& muonTracks, + MyMFTsMC const& mftTracks, + MyMFTCovariances const& mftCovs, + aod::McParticles const& /*mcParticles*/) + { + if (bcs.size() > 0) { + auto bc = bcs.begin(); + initCCDB(bc); + VarManager::SetMatchingPlane(cfgzMatching.value); + } + + fillTable(collisions, bcs, muonTracks, mftTracks, mftCovs); + } + PROCESS_SWITCH(mftMchMatcher, processMC, "process_MC", true); + + void processRD(MyEvents const& collisions, + aod::BCsWithTimestamps const& bcs, + MyMuonsWithCov const& muonTracks, + MyMFTs const& mftTracks, + MyMFTCovariances const& mftCovs) + { + if (bcs.size() > 0) { + auto bc = bcs.begin(); + initCCDB(bc); + VarManager::SetMatchingPlane(cfgzMatching.value); + } + + fillTable(collisions, bcs, muonTracks, mftTracks, mftCovs); + } + + PROCESS_SWITCH(mftMchMatcher, processRD, "process_RD", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; }; diff --git a/PWGDQ/Tasks/muonGlobalAlignment.cxx b/PWGDQ/Tasks/muonGlobalAlignment.cxx index 00bd2d964d7..650aad95359 100644 --- a/PWGDQ/Tasks/muonGlobalAlignment.cxx +++ b/PWGDQ/Tasks/muonGlobalAlignment.cxx @@ -9,21 +9,26 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -/// \file muonDCA.cxx -/// \brief Task to compute and evaluate DCA quantities -/// \author Nicolas Bizé , SUBATECH -// +/// \file muonGlobalAlignment.cxx +/// \brief Analysis of global alignment between MFT and MCH-MID +/// \author Andrea Ferrero , CEA-Saclay #include "PWGDQ/Core/VarManager.h" -// #include "PWGDQ/DataModel/ReducedInfoTables.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/fwdtrackUtilities.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/TrackSelectionTables.h" #include #include +#include +#include +#include +#include #include #include #include @@ -37,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -53,6 +58,10 @@ #include #include #include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include #include #include @@ -63,26 +72,28 @@ #include #include -#include #include #include #include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include +#include #include +#include #include -#include - using namespace o2; using namespace o2::mch; using namespace o2::framework; @@ -90,15 +101,10 @@ using namespace o2::aod; using namespace o2::aod::rctsel; -// using MyReducedMuons = soa::Join; -// using MyReducedEvents = soa::Join; -// using MyReducedEventsVtxCov = soa::Join; - using MyCollisions = aod::Collisions; using MyBCs = soa::Join; using MyEvents = soa::Join; -using MyMuonsWithCov = soa::Join; -// using MyMuonsWithCov = aod::FwdTracks; +using MyMuonsWithCov = soa::Join; using MyMFTs = aod::MFTTracks; using MyMFTCovariances = aod::MFTTracksCov; @@ -111,99 +117,306 @@ using MyMFTCovariance = MyMFTCovariances::iterator; using SMatrix55 = ROOT::Math::SMatrix>; using SMatrix5 = ROOT::Math::SVector; -static o2::globaltracking::MatchGlobalFwd sExtrap; - using o2::dataformats::GlobalFwdTrack; using o2::track::TrackParCovFwd; +namespace muonaligncoll +{ +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); //! Bunch crossing number (globally unique in this run) +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); //! Timestamp of a BC in ms (epoch style) +DECLARE_SOA_COLUMN(CollisionTime, collisionTime, float); //! Collision time in ns relative to BC stored in bc() +DECLARE_SOA_COLUMN(CollisionTimeRes, collisionTimeRes, float); //! Resolution of collision time +} // namespace muonaligncoll + +namespace o2::aod +{ +// Reduced collisions table +DECLARE_SOA_TABLE(MACollisions, "AOD", "MACOLL", //! Time and vertex information of collision + o2::soa::Index<>, muonaligncoll::RunNumber, + muonaligncoll::GlobalBC, muonaligncoll::Timestamp, + collision::PosX, collision::PosY, collision::PosZ, + collision::CovXX, collision::CovYY, collision::CovZZ, + collision::CollisionTime, collision::CollisionTimeRes); +using MACollision = MACollisions::iterator; +} // namespace o2::aod + +namespace muonalignmfttrk +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Collision, collision, int32_t, MACollisions, "MACOLLs", ""); +} // namespace muonalignmfttrk + +namespace o2::aod +{ +// Reduced MFT tracks table +DECLARE_SOA_TABLE(StoredMAMFTTracks, "AOD", "MAMFTTRK", //! On disk version of MFTTracks + o2::soa::Index<>, muonalignmfttrk::CollisionId, + fwdtrack::X, fwdtrack::Y, fwdtrack::Z, fwdtrack::Phi, fwdtrack::Tgl, fwdtrack::Signed1Pt, + fwdtrack::v001::NClusters, fwdtrack::MFTClusterSizesAndTrackFlags, fwdtrack::IsCA, + fwdtrack::Px, + fwdtrack::Py, + fwdtrack::Pz, + fwdtrack::Sign, fwdtrack::Chi2); + +DECLARE_SOA_EXTENDED_TABLE_USER(MAMFTTracks, StoredMAMFTTracks, "MAMFTTRK", //! Additional MFTTracks information (Pt, Eta, P), version 0 + aod::fwdtrack::Pt, + aod::fwdtrack::Eta, + aod::fwdtrack::P); +using MAMFTTrack = MAMFTTracks::iterator; +} // namespace o2::aod + +namespace muonalignfwdtrk +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Collision, collision, int32_t, MACollisions, "MACOLLs", ""); +// Index of matching MCH track for GlobalMuonTracks and GlobalForwardTracks +DECLARE_SOA_SELF_INDEX_COLUMN_FULL(MAMCHTrack, matchMCHTrack, int, "MAFWDTRACKs_MatchMCHTrack"); // o2-linter: disable=name/o2-column (to match naming in official tables) +// ID of matching MFT track for GlobalMuonTracks and GlobalForwardTracks +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(MAMFTTrack, matchMFTTrack, int32_t, MAMFTTracks, "MAMFTTRKs", ""); // o2-linter: disable=name/o2-column (to match naming in official tables) +} // namespace muonalignfwdtrk + +namespace o2::aod +{ +// Reduced forward tracks table +DECLARE_SOA_TABLE(StoredMAFwdTracks, "AOD", "MAFWDTRACK", + o2::soa::Index<>, muonalignfwdtrk::CollisionId, fwdtrack::TrackType, + fwdtrack::X, fwdtrack::Y, fwdtrack::Z, fwdtrack::Phi, fwdtrack::Tgl, + fwdtrack::Signed1Pt, fwdtrack::NClusters, fwdtrack::PDca, fwdtrack::RAtAbsorberEnd, + fwdtrack::Px, + fwdtrack::Py, + fwdtrack::Pz, + fwdtrack::Sign, + fwdtrack::Chi2, fwdtrack::Chi2MatchMCHMFT, + muonalignfwdtrk::MAMFTTrackId, muonalignfwdtrk::MAMCHTrackId); + +DECLARE_SOA_EXTENDED_TABLE_USER(MAFwdTracks, StoredMAFwdTracks, "MAFWDTRACK", //! + aod::fwdtrack::Pt, + aod::fwdtrack::Eta, + aod::fwdtrack::P); + +// Reduced forward track covariances table + +DECLARE_SOA_TABLE(StoredMAFwdTracksCov, "AOD", "MAFWDTRACKSCOV", //! + fwdtrack::SigmaX, fwdtrack::SigmaY, fwdtrack::SigmaPhi, fwdtrack::SigmaTgl, fwdtrack::Sigma1Pt, + fwdtrack::RhoXY, fwdtrack::RhoPhiY, fwdtrack::RhoPhiX, fwdtrack::RhoTglX, fwdtrack::RhoTglY, + fwdtrack::RhoTglPhi, fwdtrack::Rho1PtX, fwdtrack::Rho1PtY, fwdtrack::Rho1PtPhi, fwdtrack::Rho1PtTgl); + +DECLARE_SOA_EXTENDED_TABLE_USER(MAFwdTracksCov, StoredMAFwdTracksCov, "MAFWDTRACKSCOV", //! + aod::fwdtrack::CXX, + aod::fwdtrack::CXY, + aod::fwdtrack::CYY, + aod::fwdtrack::CPhiX, + aod::fwdtrack::CPhiY, + aod::fwdtrack::CPhiPhi, + aod::fwdtrack::CTglX, + aod::fwdtrack::CTglY, + aod::fwdtrack::CTglPhi, + aod::fwdtrack::CTglTgl, + aod::fwdtrack::C1PtX, + aod::fwdtrack::C1PtY, + aod::fwdtrack::C1PtPhi, + aod::fwdtrack::C1PtTgl, + aod::fwdtrack::C1Pt21Pt2); + +using MAFwdTrack = MAFwdTracks::iterator; +using MAFwdTrackCov = MAFwdTracksCov::iterator; +} // namespace o2::aod + +namespace muonaligncl +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(FwdTrack, fwdTrack, int32_t, MAFwdTracks, "MAFwdTracks", ""); +} // namespace muonaligncl + +namespace o2::aod +{ +// Reduced MCH clusters table +DECLARE_SOA_TABLE(MAFwdTrkCls, "AOD", "MAFWDTRKCL", //! Forward Track Cluster information + o2::soa::Index<>, + muonaligncl::FwdTrackId, + fwdtrkcl::X, + fwdtrkcl::Y, + fwdtrkcl::Z, + fwdtrkcl::ClInfo, + fwdtrkcl::DEId, + fwdtrkcl::IsGoodX, + fwdtrkcl::IsGoodY); + +using MAFwdTrkCl = MAFwdTrkCls::iterator; +} // namespace o2::aod + +namespace o2::aod +{ + +namespace ma_track_association +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Collision, collision, int32_t, MACollisions, "MACOLLs", ""); //! Collision index +DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL_CUSTOM(Collision, compatibleColl, int32_t, MACollisions, "MACOLLs", ""); //! Array of collision indices +} // namespace ma_track_association + +DECLARE_SOA_TABLE(MAFwdTrackAssoc, "AOD", "MAFWDTRACKASSOC", //! Table for fwdtrack-to-collision association + ma_track_association::CollisionId, + muonaligncl::FwdTrackId); + +DECLARE_SOA_TABLE(MAFwdTrkCompColls, "AOD", "MAFWDTRKCOMPCOL", //! Table with vectors of collision indices stored per fwdtrack + ma_track_association::CollisionIds, o2::soa::Marker<1>); + +} // namespace o2::aod + namespace o2::aod { +// Compact collision + MFT tracks table for DCA analysis DECLARE_SOA_TABLE(CompactMFTTracks, "AOD", "COMPACTMFT", //! standalone table for studying alignment collision::PosX, collision::PosY, collision::PosZ, fwdtrack::Signed1Pt, fwdtrack::Tgl, fwdtrack::Phi, fwdtrack::FwdDcaX, fwdtrack::FwdDcaY, o2::aod::fwdtrack::CXX, o2::aod::fwdtrack::CYY, o2::aod::fwdtrack::CXY, fwdtrack::NClusters, fwdtrack::Chi2, fwdtrack::X, fwdtrack::Y, fwdtrack::Z); -using CompactMFTTrack = CompactMFTTracks; +using CompactMFTTrack = CompactMFTTracks::iterator; } // namespace o2::aod -struct muonGlobalAlignment { - - Produces mftTable; - Configurable cfgProduceMFTTable{"cfgProduceMFTTable", false, "flag to produce MFTsa table"}; +// Switch to enable/disable the processing of the derived tables +// 0: process standard AO2Ds and produce the derived tables +// 1: process derived AO2Ds and disable the derived tables creation +#define PROCESS_DERIVED_TABLES 0 // NOLINT(cppcoreguidelines-macro-usage) + +struct MuonGlobalAlignment { // o2-linter: disable=name/workflow-file,name/struct (exception) + + static constexpr int GlobalTrackTypeMax = 2; + static constexpr int NMchChambers = 10; + static constexpr int NMchDetElems = 156; + static constexpr int ThetaAbsBoundaryDeg = 3; + static constexpr double SlopeResolutionZ = 535.; + static constexpr double AbsorberBackZ = -505.f; + static constexpr double BransonPlaneZ = -466.f; + +#if (PROCESS_DERIVED_TABLES == 0) + Produces collTable; + Produces fwdTable; + Produces fwdCovTable; + Produces fwdTrackAssoc; + Produces fwdTrkCompColls; + Produces mftTable; + Produces clusTable; + Produces compactMftTable; + Configurable cfgProduceMuonAlignmentTables{"cfgProduceMuonAlignmentTables", false, "flag to produce derived tables for muon alignment"}; + Configurable cfgProduceMuonAlignmentTablesWithCovariances{"cfgProduceMuonAlignmentTablesWithCovariances", true, "flag to include muon track covariances in derived tables for muon alignment"}; + Configurable cfgProduceMFTTable{"cfgProduceMFTTable", false, "flag to produce MFTs table"}; +#endif + + //// Variables for selecting events + struct : ConfigurableGroup { + Configurable fEvSelRequireGoodRCT{"cfgEvSelRequireGoodRCT", false, "Require good detector flags in Run Condition Table"}; + Configurable fEvSelRequireFrameBordersExclusion{"cfgEvSelRequireFrameBordersExclusion", false, "Exclude events in the TF and ITS ROF borders"}; + Configurable fEvSelRequireNoCollInTimeRange{"cfgEvSelRequireNoCollInTimeRange", false, "Exclude events with multiple collisions in time range (standard)"}; + Configurable fEvSelRequireNoSameBunch{"cfgEvSelRequireNoSameBunch", false, "Exclude events with multiple collisions in the same bunch crossing"}; + Configurable fEvSelVtxZMin{"cfgEvSelVtxZMin", -10.0f, "Minimum accepted value for vertex z position"}; + Configurable fEvSelVtxZMax{"cfgEvSelVtxZMax", 10.0f, "Maximum accepted value for vertex z position"}; + } configEvSel; //// Variables for selecting MCH and MFT tracks - Configurable fTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; - Configurable fPtMchLow{"cfgPtMchLow", 0.7f, ""}; - Configurable fEtaMftLow{"cfgEtaMftlow", -3.6f, ""}; - Configurable fEtaMftUp{"cfgEtaMftup", -2.5f, ""}; - Configurable fRabsLow{"cfgRabsLow", 17.6f, ""}; - Configurable fRabsUp{"cfgRabsUp", 89.5f, ""}; - Configurable fSigmaPdcaUp{"cfgPdcaUp", 6.f, ""}; + Configurable cfgTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; + Configurable cfgEtaMchLow{"cfgEtaMchLow", -4.0f, ""}; + Configurable cfgEtaMchUp{"cfgEtaMchUp", -2.5f, ""}; + Configurable cfgEtaMftLow{"cfgEtaMftLow", -3.6f, ""}; + Configurable cfgEtaMftUp{"cfgEtaMftUp", -2.5f, ""}; + Configurable cfgRabsLow{"cfgRabsLow", 17.6f, ""}; + Configurable cfgRabsUp{"cfgRabsUp", 89.5f, ""}; + Configurable fSigmaPdcaUp{"fSigmaPdcaUp", 6.f, ""}; - Configurable fTrackNClustMftLow{"cfgTrackNClustMftLow", 7, ""}; - Configurable fTrackChi2MftUp{"cfgTrackChi2MftUp", 999.f, ""}; + Configurable cfgTrackNClustMftLow{"cfgTrackNClustMftLow", 7, ""}; + Configurable cfgTrackChi2MftUp{"cfgTrackChi2MftUp", 999.f, ""}; - Configurable fMftMchResidualsPLow{"cfgMftMchResidualsPLow", 30.f, ""}; - Configurable fMftMchResidualsPtLow{"cfgMftMchResidualsPtLow", 4.f, ""}; + Configurable cfgMftDcaMatchChi2Up{"cfgMftDcaMatchChi2Up", 50.f, ""}; + Configurable cfgMftMchResidualsMatchChi2Up{"cfgMftMchResidualsMatchChi2Up", 50.f, ""}; + Configurable cfgDimuonMatchChi2Up{"cfgDimuonMatchChi2Up", 50.f, ""}; - Configurable fMftTracksMultiplicityMax{"cfgMftTracksMultiplicityMax", 0, "Maximum number of MFT tracks to be processed per event (zero means no limit)"}; + Configurable cfgMftMchResidualsPLow{"cfgMftMchResidualsPLow", 30.f, ""}; + Configurable cfgMftMchResidualsPtLow{"cfgMftMchResidualsPtLow", 4.f, ""}; - Configurable fVertexZshift{"cfgVertexZshift", 0.0f, "Correction to the vertex z position"}; + Configurable cfgDimuonPLow{"cfgDimuonPLow", 0.f, ""}; + Configurable cfgDimuonPtLow{"cfgDimuonPtLow", 0.7f, ""}; + Configurable cfgDimuonMassLow{"cfgDimuonMassLow", 1.8f, ""}; + + Configurable cfgMftTracksMultiplicityMax{"cfgMftTracksMultiplicityMax", 0, "Maximum number of MFT tracks to be processed per event (zero means no limit)"}; + + // Magnetic field position bias + Configurable cfgFieldOriginBiasZ{"cfgFieldOriginBiasZ", 0.0f, "Bias applied to the magnetic field z position"}; + Configurable cfgDipoleZshift{"cfgDipoleZshift", 0.0f, "Correction to the dipole z position"}; + Configurable cfgVertexZshift{"cfgVertexZshift", 0.0f, "Correction to the vertex z position"}; //// Variables for MFT alignment corrections struct : ConfigurableGroup { - Configurable fEnableMFTAlignmentCorrections{"cfgEnableMFTAlignmentCorrections", false, ""}; + Configurable cfgEnableMFTAlignmentCorrections{"cfgEnableMFTAlignmentCorrections", false, ""}; // slope corrections - Configurable fMFTAlignmentCorrXSlopeTop{"cfgMFTAlignmentCorrXSlopeTop", (-0.0006696 - 0.0005621) / 2.f, "MFT X slope correction - top half"}; - Configurable fMFTAlignmentCorrXSlopeBottom{"cfgMFTAlignmentCorrXSlopeBottom", (0.00105 + 0.001007) / 2.f, "MFT X slope correction - bottom half"}; - Configurable fMFTAlignmentCorrYSlopeTop{"cfgMFTAlignmentCorrYSlopeTop", (-0.002299 - 0.002442) / 2.f, "MFT Y slope correction - top half"}; - Configurable fMFTAlignmentCorrYSlopeBottom{"cfgMFTAlignmentCorrYSlopeBottom", (-0.0005339 - 0.0006921) / 2.f, "MFT Y slope correction - bottom half"}; + Configurable cfgMFTAlignmentCorrXSlopeTop{"cfgMFTAlignmentCorrXSlopeTop", (-0.0006696 - 0.0005621) / 2.f, "MFT X slope correction - top half"}; + Configurable cfgMFTAlignmentCorrXSlopeBottom{"cfgMFTAlignmentCorrXSlopeBottom", (0.00105 + 0.001007) / 2.f, "MFT X slope correction - bottom half"}; + Configurable cfgMFTAlignmentCorrYSlopeTop{"cfgMFTAlignmentCorrYSlopeTop", (-0.002299 - 0.002442) / 2.f, "MFT Y slope correction - top half"}; + Configurable cfgMFTAlignmentCorrYSlopeBottom{"cfgMFTAlignmentCorrYSlopeBottom", (-0.0005339 - 0.0006921) / 2.f, "MFT Y slope correction - bottom half"}; // offset corrections - Configurable fMFTAlignmentCorrXOffsetTop{"cfgMFTAlignmentCorrXOffsetTop", 0.f, "MFT X offset correction - top half"}; - Configurable fMFTAlignmentCorrXOffsetBottom{"cfgMFTAlignmentCorrXOffsetBottom", 0.f, "MFT X offset correction - bottom half"}; - Configurable fMFTAlignmentCorrYOffsetTop{"cfgMFTAlignmentCorrYOffsetTop", 0.f, "MFT Y offset correction - top half"}; - Configurable fMFTAlignmentCorrYOffsetBottom{"cfgMFTAlignmentCorrYOffsetBottom", 0.f, "MFT Y offset correction - bottom half"}; + Configurable cfgMFTAlignmentCorrXOffsetTop{"cfgMFTAlignmentCorrXOffsetTop", 0.f, "MFT X offset correction - top half"}; + Configurable cfgMFTAlignmentCorrXOffsetBottom{"cfgMFTAlignmentCorrXOffsetBottom", 0.f, "MFT X offset correction - bottom half"}; + Configurable cfgMFTAlignmentCorrYOffsetTop{"cfgMFTAlignmentCorrYOffsetTop", 0.f, "MFT Y offset correction - top half"}; + Configurable cfgMFTAlignmentCorrYOffsetBottom{"cfgMFTAlignmentCorrYOffsetBottom", 0.f, "MFT Y offset correction - bottom half"}; } configMFTAlignmentCorrections; //// Variables for re-alignment setup struct : ConfigurableGroup { - Configurable fEnableMCHRealign{"cfgEnableMCHRealign", true, "Enable re-alignment of MCH clusters and tracks"}; - Configurable fChamberResolutionX{"cfgChamberResolutionX", 0.4, "Chamber resolution along X configuration for refit"}; // 0.4cm pp, 0.2cm PbPb - Configurable fChamberResolutionY{"cfgChamberResolutionY", 0.4, "Chamber resolution along Y configuration for refit"}; // 0.4cm pp, 0.2cm PbPb - Configurable fSigmaCutImprove{"cfgSigmaCutImprove", 6., "Sigma cut for track improvement"}; - Configurable fMCHRealignCorrections{"cfgMCHRealignCorrections", "", "MCH DE positions/angles corrections in JSON format"}; + Configurable cfgEnableMCHRefit{"cfgEnableMCHRefit", false, "Enable re-fitting of MCH tracks"}; + Configurable cfgEnableMCHRealign{"cfgEnableMCHRealign", false, "Enable re-alignment of MCH clusters and tracks"}; + Configurable cfgChamberResolutionX{"cfgChamberResolutionX", 0.4, "Chamber resolution along X configuration for refit"}; // 0.4cm pp, 0.2cm PbPb + Configurable cfgChamberResolutionY{"cfgChamberResolutionY", 0.4, "Chamber resolution along Y configuration for refit"}; // 0.4cm pp, 0.2cm PbPb + Configurable cfgSigmaCutImprove{"cfgSigmaCutImprove", 6., "Sigma cut for track improvement"}; + Configurable cfgMCHRealignCorrections{"cfgMCHRealignCorrections", "", "MCH DE positions/angles corrections in JSON format"}; } configRealign; + //// Variables for forward two-prong fitter + struct : ConfigurableGroup { + Configurable cfgFitterTwoProngFwdPropagateToPCA{"cfgFitterTwoProngFwdPropagateToPCA", true, ""}; + Configurable cfgFitterTwoProngFwdMaxR{"cfgFitterTwoProngFwdMaxR", 200.f, ""}; + Configurable cfgFitterTwoProngFwdMinParamChange{"cfgFitterTwoProngFwdMinParamChange", 1.0e-3f, ""}; + Configurable cfgFitterTwoProngFwdMinRelChi2Change{"cfgFitterTwoProngFwdMinRelChi2Change", 0.9f, ""}; + Configurable cfgFitterTwoProngFwdUseAbsDCA{"cfgFitterTwoProngFwdUseAbsDCA", false, ""}; + } configFitterTwoProngFwd; + //// Variables for ccdb struct : ConfigurableGroup { - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - // Configurable geoPathRealign{"geoPathRealign", "Users/j/jcastill/GeometryAlignedFix10Fix15ShiftCh1BNew2", "Path of the geometry file"}; - Configurable geoPathRealign{"geoPathRealign", "Users/j/jcastill/GeometryAlignedLoczzm4pLHC24anap1sR5a", "Path of the geometry file"}; - Configurable nolaterthan{"ccdb-no-later-than-ref", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of reference basis"}; - Configurable nolaterthanRealign{"ccdb-no-later-than-new", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of new basis"}; + Configurable geoPathRealign{"geoPathRealign", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable cfgCcdbNoLaterThanRef{"cfgCcdbNoLaterThanRef", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of reference basis"}; + Configurable cfgCcdbNoLaterThanNew{"cfgCcdbNoLaterThanNew", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object of new basis"}; } configCCDB; - Configurable fRequireGoodRCT{"cfgRequireGoodRCT", true, "Require good detector flags in Run Condition Table"}; - - Configurable fEnableVertexShiftAnalysis{"cfgEnableVertexShiftAnalysis", true, "Enable the analysis of vertex shift"}; - Configurable fEnableMftDcaAnalysis{"cfgEnableMftDcaAnalysis", true, "Enable the analysis of DCA-based MFT alignment"}; - Configurable fEnableMftDcaExtraPlots{"cfgEnableMftDcaExtraPlots", false, "Enable additional plots for the analysis of DCA-based MFT alignment"}; - Configurable fEnableGlobalFwdDcaAnalysis{"cfgEnableGlobalFwdDcaAnalysis", true, "Enable the analysis of DCA-based MFT alignment using global forward tracks"}; - Configurable fEnableMftMchResidualsAnalysis{"cfgEnableMftMchResidualsAnalysis", true, "Enable the analysis of residuals between MFT tracks and MCH clusters"}; - Configurable fEnableMftMchResidualsExtraPlots{"cfgEnableMftMchResidualsExtraPlots", false, "Enable additional plots for the analysis of residuals between MFT tracks and MCH clusters"}; - Configurable fEnableMftMchMatchingAnalysis{"cfgEnableMftMchMatchingAnalysis", false, "Enable the analysis of residuals between MFT and MCH tracks at reference planes"}; - - Configurable fRefPlaneZMFT{"cfgRefPlaneZMFT", o2::mft::constants::mft::LayerZCoordinate()[0], "Reference plane on MFT side"}; - Configurable fRefPlaneZMCH{"cfgRefPlaneZMCH", -526.0, "Reference plane on MCH side"}; + //// Variables for histograms configuration + struct : ConfigurableGroup { + Configurable cfgHistMftDcaVzAxis{"cfgHistMftDcaVzAxis", "", "Vertex z axis configuration for MFT DCA histograms"}; + Configurable cfgHistMftDcaTxAxis{"cfgHistMftDcaTxAxis", "", "Track x axis configuration for MFT DCA histograms"}; + Configurable cfgHistMftDcaTyAxis{"cfgHistMftDcaTyAxis", "", "Track y axis configuration for MFT DCA histograms"}; + Configurable cfgHistMftDcaNclusAxis{"cfgHistMftDcaNclusAxis", "", "Clusters axis configuration for MFT DCA histograms"}; + Configurable cfgHistMftDcaEnableDetailedVzAnalysis{"cfgHistMftDcaEnableDetailedVzAnalysis", false, ""}; + Configurable cfgHistDimuonLxyAxis{"cfgHistDimuonLxyAxis", "", "Di-muon Lxy axis configuration"}; + Configurable cfgHistDimuonLzAxis{"cfgHistDimuonLzAxis", "", "Di-muon Lz axis configuration"}; + Configurable cfgHistDimuonTauxyAxis{"cfgHistDimuonTauxyAxis", "", "Di-muon tauxy axis configuration"}; + Configurable cfgHistDimuonTauzAxis{"cfgHistDimuonTauzAxis", "", "Di-muon tauz axis configuration"}; + } configHistograms; + + Configurable cfgEnableVertexShiftAnalysis{"cfgEnableVertexShiftAnalysis", false, "Enable the analysis of vertex shift"}; + Configurable cfgEnableMftDcaAnalysis{"cfgEnableMftDcaAnalysis", false, "Enable the analysis of DCA-based MFT alignment"}; + Configurable cfgEnableMftDcaExtraPlots{"cfgEnableMftDcaExtraPlots", false, "Enable additional plots for the analysis of DCA-based MFT alignment"}; + Configurable cfgEnableGlobalFwdDcaAnalysis{"cfgEnableGlobalFwdDcaAnalysis", false, "Enable the analysis of DCA-based MFT alignment using global forward tracks"}; + Configurable cfgEnableMftMchResidualsAnalysis{"cfgEnableMftMchResidualsAnalysis", true, "Enable the analysis of residuals between MFT tracks and MCH clusters"}; + Configurable cfgEnableMftMchResidualsExtraPlots{"cfgEnableMftMchResidualsExtraPlots", false, "Enable additional plots for the analysis of residuals between MFT tracks and MCH clusters"}; + Configurable cfgEnableMftMchMatchingAnalysis{"cfgEnableMftMchMatchingAnalysis", false, "Enable the analysis of residuals between MFT and MCH tracks at reference planes"}; + Configurable cfgEnableDimuonAnalysis{"cfgEnableDimuonAnalysis", false, "Enable the analysis of di-muon pairs"}; + + Configurable cfgRefPlaneZMFT{"cfgRefPlaneZMFT", o2::mft::constants::mft::LayerZCoordinate()[0], "Reference plane on MFT side"}; + Configurable cfgRefPlaneZMCH{"cfgRefPlaneZMCH", -526.0, "Reference plane on MCH side"}; int mRunNumber{0}; // needed to detect if the run changed and trigger update of magnetic field - Service ccdbManager; - o2::field::MagneticField* fieldB; + Service ccdbManager{}; + o2::field::MagneticField* fieldB{nullptr}; o2::ccdb::CcdbApi ccdbApi; // Derived version of mch::Track class that handles the associated clusters as internal objects and deletes them in the destructor @@ -222,22 +435,74 @@ struct muonGlobalAlignment { } }; + class TrackParExt : public o2::track::TrackParCovFwd + { + public: + TrackParExt() = default; + TrackParExt(const TrackParExt& t) = default; + explicit TrackParExt(o2::track::TrackParCovFwd const& t, int nc = -1, bool r = false) + : TrackParCovFwd(t), nClusters(nc), removable(r) {} + ~TrackParExt() = default; + + TrackParExt& operator=(const TrackParCovFwd& tpf) + { + o2::track::TrackParCovFwd::operator=(tpf); + return *this; + } + TrackParExt& operator=(const TrackParExt& tpe) + { + o2::track::TrackParCovFwd::operator=(tpe); + nClusters = tpe.getNClusters(); + removable = tpe.isRemovable(); + return *this; + } + + void setNClusters(int n) { nClusters = n; } + [[nodiscard]] int getNClusters() const { return nClusters; } + + void setRemovable() { removable = true; } + [[nodiscard]] bool isRemovable() const { return removable; } + + [[nodiscard]] o2::track::TrackParCovFwd asTrackParCovFwd() const + { + return {static_cast(*this)}; + } + + private: + int nClusters{-1}; + bool removable{false}; + }; + + std::unordered_map mMchTrackPars; + std::unordered_map mMftTrackPars; + std::unordered_map mMchTrackParsNew; + std::unordered_map mMftTrackParsNew; + + using MuonPair = std::pair; + using GlobalMuonPair = std::pair, std::vector>; + geo::TransformationCreator transformation; - std::map transformRef; // reference geometry w.r.t track data - std::map transformNew; // new geometry + std::unordered_map transformRef; // reference geometry w.r.t track data + std::unordered_map transformNew; // new geometry TGeoManager* geoNew = nullptr; TGeoManager* geoRef = nullptr; - TrackFitter trackFitter; // Track fitter from MCH tracking library - double mImproveCutChi2; // Chi2 cut for track improvement. + TrackFitter trackFitter; // Track fitter from MCH tracking library + double mImproveCutChi2{0}; // Chi2 cut for track improvement. + + o2::vertexing::FwdDCAFitterN<2> fgFitterTwoProngFwd; struct AlignmentCorrections { double x{0}; double y{0}; double z{0}; }; - std::map mMchAlignmentCorrections; + std::unordered_map mMchAlignmentCorrections; - Preslice perMuon = aod::fwdtrkcl::fwdtrackId; +#if (PROCESS_DERIVED_TABLES == 0) + Preslice perMuon = aod::fwdtrkcl::fwdtrackId; +#else + Preslice perMuon = muonaligncl::fwdTrackId; +#endif o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT_muon_glo", false, false, true}; @@ -250,10 +515,12 @@ struct muonGlobalAlignment { // vector of all MFT-MCH(-MID) matching candidates associated to the same MCH(-MID) track, // to be sorted in descending order with respect to the matching quality // the map key is the MCH(-MID) track global index - using MatchingCandidates = std::map>; + using MatchingCandidates = std::unordered_map>; struct CollisionInfo { - uint64_t bc{0}; + int runNumber{0}; + uint64_t globalBC{0}; + uint64_t timestamp{0}; // z position of the collision double zVertex{0}; // number of MFT tracks associated to the collision @@ -263,103 +530,44 @@ struct muonGlobalAlignment { // vector of MCH(-MID) track indexes std::vector mchTracks; // matching candidates - std::map> globalMuonTracks; + std::unordered_map> globalMuonTracks; + // number of ambiguous collisions associated to each muon track + std::unordered_map muonTracksCollMult; }; - void InitCollisions(MyEvents const& collisions, - MyBCs const& bcs, - MyMuonsWithCov const& muonTracks, - std::map& collisionInfos) - { - // fill collision information for global muon tracks (MFT-MCH-MID matches) - for (auto muonTrack : muonTracks) { - if (!muonTrack.has_collision()) - continue; - - auto collision = collisions.rawIteratorAt(muonTrack.collisionId()); - - if (fRequireGoodRCT && !rctChecker(collision)) - continue; - - uint64_t collisionIndex = collision.globalIndex(); - - auto bc = bcs.rawIteratorAt(collision.bcId()); - - auto& collisionInfo = collisionInfos[collisionIndex]; - collisionInfo.bc = bc.globalBC(); - collisionInfo.zVertex = collision.posZ(); + // global vector of MCH(-MID) track indexes + std::vector mMchTracks; + // global vector of matching candidates + std::unordered_map> mGlobalMuonTracks; - if (static_cast(muonTrack.trackType()) > 2) { - // standalone MCH or MCH-MID tracks - uint64_t mchTrackIndex = muonTrack.globalIndex(); - collisionInfo.mchTracks.push_back(mchTrackIndex); - } else { - // global muon tracks (MFT-MCH or MFT-MCH-MID) - uint64_t muonTrackIndex = muonTrack.globalIndex(); - auto const& mchTrack = muonTrack.template matchMCHTrack_as(); - uint64_t mchTrackIndex = mchTrack.globalIndex(); - - // check if a vector of global muon candidates is already available for the current MCH index - // if not, initialize a new one and add the current global muon track - // bool globalMuonTrackFound = false; - auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); - if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { - matchingCandidateIterator->second.push_back(muonTrackIndex); - // globalMuonTrackFound = true; - } else { - collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); - } + std::tuple getAxisConfiguration(std::string cfgStr, int nBinsDef, double xMinDef, double xMaxDef) + { + // replace commas and semi-colons by spaces + for (size_t i = 0; i < cfgStr.size(); i++) { + if (cfgStr[i] == ',' || cfgStr[i] == ';') { + cfgStr[i] = ' '; } } - // sort the vectors of matching candidates in ascending order based on the matching chi2 value - auto compareChi2 = [&muonTracks](uint64_t trackIndex1, uint64_t trackIndex2) -> bool { - auto const& track1 = muonTracks.rawIteratorAt(trackIndex1); - auto const& track2 = muonTracks.rawIteratorAt(trackIndex2); - - return (track1.chi2MatchMCHMFT() < track2.chi2MatchMCHMFT()); - }; - - for (auto& [collisionIndex, collisionInfo] : collisionInfos) { - for (auto& [mchIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { - std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareChi2); - } + std::tuple result{nBinsDef, xMinDef, xMaxDef}; + std::stringstream cfg(cfgStr); + try { + cfg >> std::get<0>(result); + cfg >> std::get<1>(result); + cfg >> std::get<2>(result); + } catch (const std::exception& e) { + return result; } - } - - void InitCollisions(MyEvents const& collisions, - MyBCs const& bcs, - MyMuonsWithCov const& muonTracks, - MyMFTs const& mftTracks, - std::map& collisionInfos) - { - InitCollisions(collisions, bcs, muonTracks, collisionInfos); - - // fill collision information for MFT standalone tracks - for (auto mftTrack : mftTracks) { - if (!mftTrack.has_collision()) - continue; - - auto collision = collisions.rawIteratorAt(mftTrack.collisionId()); - uint64_t collisionIndex = collision.globalIndex(); - - auto bc = bcs.rawIteratorAt(collision.bcId()); - - uint64_t mftTrackIndex = mftTrack.globalIndex(); - - auto& collisionInfo = collisionInfos[collisionIndex]; - collisionInfo.bc = bc.globalBC(); - collisionInfo.zVertex = collision.posZ(); - collisionInfo.mftTracks.push_back(mftTrackIndex); - } + return result; } template void initCCDB(BC const& bc) { - if (mRunNumber == bc.runNumber()) + if (mRunNumber == bc.runNumber()) { return; + } mRunNumber = bc.runNumber(); ccdbManager->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); @@ -369,32 +577,34 @@ struct muonGlobalAlignment { base::Propagator::initFieldFromGRP(grpmag); TrackExtrap::setField(); TrackExtrap::useExtrapV2(); - fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT - double centerMFT[3] = {0, 0, -61.4}; // or use middle point between Vtx and MFT? - mBzAtMftCenter = fieldB->getBz(centerMFT); + fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); // for MFT + std::array centerMFT{0, 0, -61.4}; // or use middle point between Vtx and MFT? + mBzAtMftCenter = fieldB->getBz(centerMFT.data()); + + fgFitterTwoProngFwd.setBz(grpmag->getNominalL3Field()); } else { LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", bc.timestamp()); } // Load geometry information from CCDB/local - LOGF(info, "Loading reference aligned geometry from CCDB no later than %d", configCCDB.nolaterthan.value); - ccdbManager->setCreatedNotAfter(configCCDB.nolaterthan); // this timestamp has to be consistent with what has been used in reco + LOGF(info, "Loading reference aligned geometry from CCDB no later than %d", configCCDB.cfgCcdbNoLaterThanRef.value); + ccdbManager->setCreatedNotAfter(configCCDB.cfgCcdbNoLaterThanRef); // this timestamp has to be consistent with what has been used in reco geoRef = ccdbManager->getForTimeStamp(configCCDB.geoPath, bc.timestamp()); ccdbManager->clearCache(configCCDB.geoPath); - if (configRealign.fEnableMCHRealign && fEnableMftMchResidualsAnalysis) { + if (configRealign.cfgEnableMCHRealign && (cfgEnableMftMchResidualsAnalysis)) { if (geoRef != nullptr) { transformation = geo::transformationFromTGeoManager(*geoRef); } else { LOGF(fatal, "Reference aligned geometry object is not available in CCDB at timestamp=%llu", bc.timestamp()); } - for (int i = 0; i < 156; i++) { + for (int i = 0; i < NMchDetElems; i++) { int iDEN = GetDetElemId(i); transformRef[iDEN] = transformation(iDEN); } - LOGF(info, "Loading new aligned geometry from CCDB no later than %d", configCCDB.nolaterthanRealign.value); - ccdbManager->setCreatedNotAfter(configCCDB.nolaterthanRealign); // make sure this timestamp can be resolved regarding the reference one + LOGF(info, "Loading new aligned geometry from CCDB no later than %d", configCCDB.cfgCcdbNoLaterThanNew.value); + ccdbManager->setCreatedNotAfter(configCCDB.cfgCcdbNoLaterThanNew); // make sure this timestamp can be resolved regarding the reference one geoNew = ccdbManager->getForTimeStamp(configCCDB.geoPathRealign, bc.timestamp()); ccdbManager->clearCache(configCCDB.geoPathRealign); if (geoNew != nullptr) { @@ -402,7 +612,7 @@ struct muonGlobalAlignment { } else { LOGF(fatal, "New aligned geometry object is not available in CCDB at timestamp=%llu", bc.timestamp()); } - for (int i = 0; i < 156; i++) { + for (int i = 0; i < NMchDetElems; i++) { int iDEN = GetDetElemId(i); transformNew[iDEN] = transformation(iDEN); } @@ -412,29 +622,30 @@ struct muonGlobalAlignment { void init(o2::framework::InitContext&) { // Load geometry - ccdbManager->setURL(configCCDB.ccdburl); + ccdbManager->setURL(configCCDB.ccdbUrl); ccdbManager->setCaching(true); ccdbManager->setLocalObjectValidityChecking(); - ccdbApi.init(configCCDB.ccdburl); + ccdbApi.init(configCCDB.ccdbUrl); mRunNumber = 0; - if (!o2::base::GeometryManager::isGeometryLoaded()) { - LOGF(info, "Load geometry from CCDB"); - ccdbManager->get(configCCDB.geoPath); - } + // configure magnetic field position bias + o2::conf::ConfigurableParam::setValue("FieldOriginBias.z", std::to_string(cfgFieldOriginBiasZ.value)); // Configuration for track fitter const auto& trackerParam = TrackerParam::Instance(); trackFitter.setBendingVertexDispersion(trackerParam.bendingVertexDispersion); - trackFitter.setChamberResolution(configRealign.fChamberResolutionX, configRealign.fChamberResolutionY); + trackFitter.setChamberResolution(configRealign.cfgChamberResolutionX, configRealign.cfgChamberResolutionY); trackFitter.smoothTracks(true); trackFitter.useChamberResolution(); - mImproveCutChi2 = 2. * configRealign.fSigmaCutImprove * configRealign.fSigmaCutImprove; + mImproveCutChi2 = 2. * configRealign.cfgSigmaCutImprove * configRealign.cfgSigmaCutImprove; + + // use the Runge-Kutta extrapolation v2 + TrackExtrap::useExtrapV2(); // Fill table of MCH alignment corrections rapidjson::Document document; // Check that the json is parsed correctly - rapidjson::ParseResult jsonOk = document.Parse(configRealign.fMCHRealignCorrections.value.c_str()); + rapidjson::ParseResult jsonOk = document.Parse(configRealign.cfgMCHRealignCorrections.value.c_str()); if (jsonOk) { for (rapidjson::Value::ConstMemberIterator it = document.MemberBegin(); it != document.MemberEnd(); it++) { LOG(info) << "DE" << it->name.GetString() << " alignment corrections:"; @@ -442,23 +653,35 @@ struct muonGlobalAlignment { LOG(info) << " y: " << it->value["y"].GetDouble(); LOG(info) << " z: " << it->value["z"].GetDouble(); - mMchAlignmentCorrections[std::stoi(it->name.GetString())] = { - it->value["x"].GetDouble(), - it->value["y"].GetDouble(), - it->value["z"].GetDouble()}; + mMchAlignmentCorrections[std::stoi(it->name.GetString())] = AlignmentCorrections{ + .x = it->value["x"].GetDouble(), + .y = it->value["y"].GetDouble(), + .z = it->value["z"].GetDouble()}; } } else { - LOG(error) << "JSON parse error: " << rapidjson::GetParseErrorFunc(jsonOk.Code()) << " (" << jsonOk.Offset() << ")"; + LOG(error) << "JSON parse error: " << rapidjson::GetParseErrorFunc(jsonOk.Code()) << " (" << jsonOk.Offset() << ")"; // NOLINT(google-readability-casting) } + // two-prong track fitter setup + fgFitterTwoProngFwd.setPropagateToPCA(configFitterTwoProngFwd.cfgFitterTwoProngFwdPropagateToPCA.value); + fgFitterTwoProngFwd.setMaxR(configFitterTwoProngFwd.cfgFitterTwoProngFwdMaxR.value); + fgFitterTwoProngFwd.setMinParamChange(configFitterTwoProngFwd.cfgFitterTwoProngFwdMinParamChange.value); + fgFitterTwoProngFwd.setMinRelChi2Change(configFitterTwoProngFwd.cfgFitterTwoProngFwdMinRelChi2Change.value); + fgFitterTwoProngFwd.setUseAbsDCA(configFitterTwoProngFwd.cfgFitterTwoProngFwdUseAbsDCA.value); + float mftLadderWidth = 1.7; AxisSpec dcaxMFTAxis = {400, -0.5, 0.5, "DCA_{x} (cm)"}; AxisSpec dcayMFTAxis = {400, -0.5, 0.5, "DCA_{y} (cm)"}; AxisSpec dcaxMCHAxis = {400, -10.0, 10.0, "DCA_{x} (cm)"}; AxisSpec dcayMCHAxis = {400, -10.0, 10.0, "DCA_{y} (cm)"}; - AxisSpec dcazAxis = {20, -10.0, 10.0, "v_{z} (cm)"}; - AxisSpec txAxis = {30 * 4, -mftLadderWidth * 15.f / 2.f, mftLadderWidth * 15.f / 2.f, "track_{x} (cm)"}; - AxisSpec tyAxis = {24 * 4, -12.f, 12.f, "track_{y} (cm)"}; + auto dcaVzAxisConfig = getAxisConfiguration(configHistograms.cfgHistMftDcaVzAxis, 20, -10., 10.); + AxisSpec dcaVzAxis = {std::get<0>(dcaVzAxisConfig), std::get<1>(dcaVzAxisConfig), std::get<2>(dcaVzAxisConfig), "v_{z} (cm)"}; + auto txAxisConfig = getAxisConfiguration(configHistograms.cfgHistMftDcaTxAxis, 30 * 4, -mftLadderWidth * 15.f / 2.f, mftLadderWidth * 15.f / 2.f); + AxisSpec txAxis = {std::get<0>(txAxisConfig), std::get<1>(txAxisConfig), std::get<2>(txAxisConfig), "track_{x} (cm)"}; + auto tyAxisConfig = getAxisConfiguration(configHistograms.cfgHistMftDcaTyAxis, 24 * 4, -12.f, 12.f); + AxisSpec tyAxis = {std::get<0>(tyAxisConfig), std::get<1>(tyAxisConfig), std::get<2>(tyAxisConfig), "track_{y} (cm)"}; + AxisSpec txCoarseAxis = {2, -mftLadderWidth * 15.f / 2.f, mftLadderWidth * 15.f / 2.f, "track_{x} (cm)"}; + AxisSpec tyCoarseAxis = {2, -12.f, 12.f, "track_{y} (cm)"}; AxisSpec txFineAxis = {1500, -15.f, 15.f, "track_{x} (cm)"}; AxisSpec tyFineAxis = {1500, -15.f, 15.f, "track_{y} (cm)"}; AxisSpec vxAxis = {400, -0.5, 0.5, "vtx_{x} (cm)"}; @@ -479,12 +702,12 @@ struct muonGlobalAlignment { registry.add("vertex_y_vs_x", std::format("Vertex y vs. x").c_str(), {HistType::kTH2F, {vxAxis, vyAxis}}); registry.add("vertex_z", std::format("Vertex z").c_str(), {HistType::kTH1F, {vzAxis}}); - if (fEnableVertexShiftAnalysis || fEnableMftDcaAnalysis) { + if (cfgEnableVertexShiftAnalysis || cfgEnableMftDcaAnalysis) { registry.add("DCA/MFT/nTracksMFT", std::format("Number of MFT tracks per collision").c_str(), {HistType::kTH1F, {{100, 0, 1000, "# of MFT tracks"}}}); registry.add("DCA/MFT/DCA_y_vs_x", std::format("DCA y vs. x").c_str(), {HistType::kTH2F, {dcaxMFTAxis, dcayMFTAxis}}); } - if (fEnableVertexShiftAnalysis) { + if (cfgEnableVertexShiftAnalysis) { registry.add("DCA/MFT/DCA_x_vs_phi_vs_zshift", std::format("DCA(x) vs. #phi vs. z shift").c_str(), {HistType::kTH3F, {zshiftAxis, phiAxis, dcaxMFTAxis}}); registry.add("DCA/MFT/DCA_y_vs_phi_vs_zshift", std::format("DCA(y) vs. #phi vs. z shift").c_str(), {HistType::kTH3F, {zshiftAxis, phiAxis, dcayMFTAxis}}); @@ -494,13 +717,24 @@ struct muonGlobalAlignment { registry.add("DCA/MFT/DCA_y_vs_slopey_vs_zshift", std::format("DCA(y) vs. y slope vs. z shift").c_str(), {HistType::kTH3F, {zshiftAxis, syAxis, dcayMFTAxis}}); } - if (fEnableMftDcaAnalysis) { - registry.add("DCA/MFT/DCA_x", "DCA(x) vs. vz, tx, ty, nclus", - HistType::kTHnSparseF, {dcaxMFTAxis, dcazAxis, txAxis, tyAxis, nMftClustersAxis}); - registry.add("DCA/MFT/DCA_y", "DCA(y) vs. vz, tx, ty, nclus", - HistType::kTHnSparseF, {dcayMFTAxis, dcazAxis, txAxis, tyAxis, nMftClustersAxis}); + if (cfgEnableMftDcaAnalysis) { + if (configHistograms.cfgHistMftDcaEnableDetailedVzAnalysis) { + registry.add("DCA/MFT/DCA_x", "DCA(x) vs. vz, tx, ty, nclus", + HistType::kTHnSparseF, {dcaxMFTAxis, dcaVzAxis, txAxis, tyAxis, nMftClustersAxis}); + registry.add("DCA/MFT/DCA_y", "DCA(y) vs. vz, tx, ty, nclus", + HistType::kTHnSparseF, {dcayMFTAxis, dcaVzAxis, txAxis, tyAxis, nMftClustersAxis}); + } else { + registry.add("DCA/MFT/DCA_x", "DCA(x) vs. vz, tx, ty, nclus", + HistType::kTHnSparseF, {dcaxMFTAxis, {1, -10., 10., "v_{z} (cm)"}, txAxis, tyAxis, nMftClustersAxis}); + registry.add("DCA/MFT/DCA_y", "DCA(y) vs. vz, tx, ty, nclus", + HistType::kTHnSparseF, {dcayMFTAxis, {1, -10., 10., "v_{z} (cm)"}, txAxis, tyAxis, nMftClustersAxis}); + registry.add("DCA/MFT/DCAxVsVz", "DCA(x) vs. vz", + HistType::kTHnSparseF, {dcaxMFTAxis, dcaVzAxis, txCoarseAxis, tyCoarseAxis, nMftClustersAxis}); + registry.add("DCA/MFT/DCAyVsVz", "DCA(y) vs. vz", + HistType::kTHnSparseF, {dcayMFTAxis, dcaVzAxis, txCoarseAxis, tyCoarseAxis, nMftClustersAxis}); + } - if (fEnableMftDcaExtraPlots) { + if (cfgEnableMftDcaExtraPlots) { registry.add("DCA/MFT/layers", "Layers vs. tx, ty, nclus", HistType::kTHnSparseF, {mftLayerAxis, txAxis, tyAxis, nMftClustersAxis}); registry.add("DCA/MFT/trackChi2", "Track #chi^{2} vs. tx, ty, nclus, layer", @@ -520,16 +754,16 @@ struct muonGlobalAlignment { } } - if (fEnableGlobalFwdDcaAnalysis) { + if (cfgEnableGlobalFwdDcaAnalysis) { registry.add("DCA/GlobalFwd/DCA_x", "DCA(x) vs. vz, tx, ty, nclus", - HistType::kTHnSparseF, {dcaxMFTAxis, dcazAxis, txAxis, tyAxis, nMftClustersAxis}); + HistType::kTHnSparseF, {dcaxMFTAxis, dcaVzAxis, txAxis, tyAxis, nMftClustersAxis}); registry.add("DCA/GlobalFwd/DCA_y", "DCA(y) vs. vz, tx, ty, nclus", - HistType::kTHnSparseF, {dcayMFTAxis, dcazAxis, txAxis, tyAxis, nMftClustersAxis}); + HistType::kTHnSparseF, {dcayMFTAxis, dcaVzAxis, txAxis, tyAxis, nMftClustersAxis}); } - if (fEnableMftMchResidualsAnalysis) { - AxisSpec dxAxis = {600, -30.0, 30.0, "#Delta x (cm)"}; - AxisSpec dyAxis = {600, -30.0, 30.0, "#Delta y (cm)"}; + if (cfgEnableMftMchResidualsAnalysis) { + AxisSpec dxAxis = {400, -10.0, 10.0, "#Delta x (cm)"}; + AxisSpec dyAxis = {400, -10.0, 10.0, "#Delta y (cm)"}; registry.add("DCA/MCH/DCA_y_vs_x", std::format("DCA y vs. x").c_str(), {HistType::kTH2F, {dcaxMCHAxis, dcayMCHAxis}}); registry.add("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_mom", std::format("DCA(x) vs. p, quadrant, chargeSign").c_str(), {HistType::kTHnSparseF, {{20, 0, 100.0, "p (GeV/c)"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dcaxMCHAxis}}); @@ -543,20 +777,20 @@ struct muonGlobalAlignment { registry.add("residuals/dy_vs_chamber", "Cluster y residual vs. chamber, quadrant, chargeSign", {HistType::kTHnSparseF, {{10, 1, 11, "chamber"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dyAxis}}); - registry.add("residuals/dx_vs_de", "Cluster x residual vs. DE, quadrant, chargeSign, momentum", - {HistType::kTHnSparseF, {dxAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}}}); - registry.add("residuals/dy_vs_de", "Cluster y residual vs. DE, quadrant, chargeSign, momentum", - {HistType::kTHnSparseF, {dyAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}}}); + registry.add("residuals/dx_vs_de", "Cluster x residual vs. DE, quadrant, chargeSign, momentum, xslope", + {HistType::kTHnSparseF, {dxAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}, sxAxis}}); + registry.add("residuals/dy_vs_de", "Cluster y residual vs. DE, quadrant, chargeSign, momentum, yslope", + {HistType::kTHnSparseF, {dyAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}, syAxis}}); registry.add("residuals/dx_vs_chamber_corr", "Cluster x residual vs. chamber, quadrant, chargeSign (with corrections)", {HistType::kTHnSparseF, {{10, 1, 11, "chamber"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dxAxis}}); registry.add("residuals/dy_vs_chamber_corr", "Cluster y residual vs. chamber, quadrant, chargeSign (with corrections)", {HistType::kTHnSparseF, {{10, 1, 11, "chamber"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dyAxis}}); - registry.add("residuals/dx_vs_de_corr", "Cluster x residual vs. DE, quadrant, chargeSign, momentum (with corrections)", - {HistType::kTHnSparseF, {dxAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}}}); - registry.add("residuals/dy_vs_de_corr", "Cluster y residual vs. DE, quadrant, chargeSign, momentum (with corrections)", - {HistType::kTHnSparseF, {dyAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}}}); + registry.add("residuals/dx_vs_de_corr", "Cluster x residual vs. DE, quadrant, chargeSign, momentum, xslope (with corrections)", + {HistType::kTHnSparseF, {dxAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}, sxAxis}}); + registry.add("residuals/dy_vs_de_corr", "Cluster y residual vs. DE, quadrant, chargeSign, momentum, yslope (with corrections)", + {HistType::kTHnSparseF, {dyAxis, {getNumDE(), 0, static_cast(getNumDE()), "DE"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}, syAxis}}); registry.add("residuals/de_alignment_corrections_x", "DE alignment corrections - X coordinate", {HistType::kTH1F, {{getNumDE(), 0, static_cast(getNumDE()), "DE"}}}); @@ -571,16 +805,16 @@ struct muonGlobalAlignment { registry.get(HIST("residuals/de_alignment_corrections_y"))->SetBinError(deIndex + 1, 0.1); } - if (fEnableMftMchResidualsExtraPlots) { - registry.add("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_vz", std::format("DCA(x) vs. vz, quadrant, chargeSign").c_str(), {HistType::kTHnSparseF, {dcazAxis, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dcaxMCHAxis}}); - registry.add("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_vz", std::format("DCA(y) vs. vz, quadrant, chargeSign").c_str(), {HistType::kTHnSparseF, {dcazAxis, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dcayMCHAxis}}); + if (cfgEnableMftMchResidualsExtraPlots) { + registry.add("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_vz", std::format("DCA(x) vs. vz, quadrant, chargeSign").c_str(), {HistType::kTHnSparseF, {dcaVzAxis, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dcaxMCHAxis}}); + registry.add("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_vz", std::format("DCA(y) vs. vz, quadrant, chargeSign").c_str(), {HistType::kTHnSparseF, {dcaVzAxis, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, dcayMCHAxis}}); registry.add("residuals/dphi_at_mft", "Track #Delta#phi at MFT", {HistType::kTHnSparseF, {{200, -0.2f, 0.2f, "#Delta#phi"}, {80, -10.f, 10.f, "track_x (cm)"}, {80, -10.f, 10.f, "track_y (cm)"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}}}); } } - if (fEnableMftMchMatchingAnalysis) { + if (cfgEnableMftMchMatchingAnalysis) { AxisSpec dxAxis = {200, -10.0, 10.0, "#Deltax (cm)"}; AxisSpec dyAxis = {200, -10.0, 10.0, "#Deltay (cm)"}; AxisSpec dsxAxis = {200, -0.1, 0.1, "#Deltaslope(x) (rad)"}; @@ -611,24 +845,87 @@ struct muonGlobalAlignment { registry.add("matching/dphiAtMCH", "Tracks #Delta#phi at MCH reference plane", {HistType::kTHnSparseF, {dphiAxis, {20, -100.0, 100.0, "track x (cm)"}, {20, -100.0, 100.0, "track y (cm)"}, {4, 0, 4, "quadrant"}, {2, 0, 2, "sign"}, {20, 0, 100.0, "p (GeV/c)"}}}); } + + if (cfgEnableDimuonAnalysis) { + AxisSpec invMassAxis = {1500, 0, 15, "M_{#mu^{+}#mu^{-}} (GeV/c^{2})"}; + AxisSpec invMassAxisCoarse = {60, 2, 5, "M_{#mu^{+}#mu^{-}} (GeV/c^{2})"}; + AxisSpec pTAxis = {30, 0, 30, "#mu^{+}#mu^{-} p_{T} (GeV/c)"}; + AxisSpec pAxis = {50, 0, 200, "#mu^{+}#mu^{-} p (GeV/c)"}; + AxisSpec muPosQuadrantAxis = {4, 0, 4, "#mu^{+} quadrant"}; + AxisSpec muNegQuadrantAxis = {4, 0, 4, "#mu^{-} quadrant"}; + AxisSpec angleAxis = {100, 0, 0.5, "#mu^{+}#mu^{-} angle (rad)"}; + AxisSpec angleDiffAxis = {500, -0.05, 0.05, "#mu^{+}#mu^{-} angle difference (rad)"}; + auto lzAxisConfig = getAxisConfiguration(configHistograms.cfgHistDimuonLzAxis, 100, -0.5, 1.5); + AxisSpec lzAxis = {std::get<0>(lzAxisConfig), std::get<1>(lzAxisConfig), std::get<2>(lzAxisConfig), "L_{z}^{#mu#mu} (cm)"}; + auto tauzAxisConfig = getAxisConfiguration(configHistograms.cfgHistDimuonTauzAxis, 200, -10., 10.); + AxisSpec tauzAxis = {std::get<0>(tauzAxisConfig), std::get<1>(tauzAxisConfig), std::get<2>(tauzAxisConfig), "#tau_{z}^{#mu#mu} (cm)"}; + auto lxyAxisConfig = getAxisConfiguration(configHistograms.cfgHistDimuonLxyAxis, 100, 0, 0.2); + AxisSpec lxyAxis = {std::get<0>(lxyAxisConfig), std::get<1>(lxyAxisConfig), std::get<2>(lxyAxisConfig), "L_{xy}^{#mu#mu} (cm)"}; + auto tauxyAxisConfig = getAxisConfiguration(configHistograms.cfgHistDimuonTauxyAxis, 200, 0, 20); + AxisSpec tauxyAxis = {std::get<0>(tauxyAxisConfig), std::get<1>(tauxyAxisConfig), std::get<2>(tauxyAxisConfig), "L_{xy}^{#mu#mu} (cm)"}; + AxisSpec dcaxAxis = {400, -10.0, 10.0, "#mu^{+}#mu^{-} DCA_{x} (cm)"}; + AxisSpec dcayAxis = {400, -10.0, 10.0, "#mu^{+}#mu^{-} DCA_{y} (cm)"}; + AxisSpec dcaMftxAxis = {400, -0.5, 0.5, "#mu^{+}#mu^{-} DCA_{x} (cm)"}; + AxisSpec dcaMftyAxis = {400, -0.5, 0.5, "#mu^{+}#mu^{-} DCA_{y} (cm)"}; + + // di-muon invariant mass distributions + registry.add("dimuon/invariantMass_MuonKine_MuonCuts", "#mu^{+}#mu^{-} invariant mass (muon cuts)", {HistType::kTHnSparseF, {invMassAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/invariantMass_MuonKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} invariant mass (global muon cuts, good matches)", {HistType::kTHnSparseF, {invMassAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} invariant mass (global muon cuts, rescaled MFT, good matches)", {HistType::kTHnSparseF, {invMassAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // difference in mu+mu- opening angle between MCH and global muon tracks + registry.add("dimuon/angle_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} opening angle difference (global muon cuts, good matches)", {HistType::kTHnSparseF, {angleDiffAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // mu+mu- DCA + registry.add("dimuon/dcax_MuonKine_MuonCuts", "#mu^{+}#mu^{-} DCA_{x} (muon cuts)", {HistType::kTHnSparseF, {dcaxAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/dcay_MuonKine_MuonCuts", "#mu^{+}#mu^{-} DCA_{y} (muon cuts)", {HistType::kTHnSparseF, {dcayAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/dcax_MuonKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{x} (global muon cuts, good matches)", {HistType::kTHnSparseF, {dcaxAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/dcay_MuonKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{y} (global muon cuts, good matches)", {HistType::kTHnSparseF, {dcayAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/dcax_ScaledMftKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{x} (global muon cuts, rescaled MFT, good matches)", {HistType::kTHnSparseF, {dcaMftxAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/dcay_ScaledMftKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{y} (global muon cuts, rescaled MFT, good matches)", {HistType::kTHnSparseF, {dcaMftyAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // mu+mu- decay length z + registry.add("dimuon/Lz_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{z} (global muon cuts, good matches)", {HistType::kTHnSparseF, {lzAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/tauz_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{z} (global muon cuts, good matches)", {HistType::kTHnSparseF, {tauzAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // mu+mu- decay length xy + registry.add("dimuon/Lxy_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{xy} (global muon cuts, good matches)", {HistType::kTHnSparseF, {lxyAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/tauxy_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{xy} (global muon cuts, good matches)", {HistType::kTHnSparseF, {tauxyAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + + // di-muon invariant mass distributions (realigned/refitted tracks) + registry.add("dimuon/realign/invariantMass_MuonKine_MuonCuts", "#mu^{+}#mu^{-} invariant mass (muon cuts)", {HistType::kTHnSparseF, {invMassAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/invariantMass_MuonKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} invariant mass (global muon cuts, good matches)", {HistType::kTHnSparseF, {invMassAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} invariant mass (global muon cuts, rescaled MFT, good matches)", {HistType::kTHnSparseF, {invMassAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // difference in mu+mu- opening angle between MCH and global muon tracks (realigned/refitted tracks) + registry.add("dimuon/realign/angle_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} opening angle difference (global muon cuts, good matches)", {HistType::kTHnSparseF, {angleDiffAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // mu+mu- DCA + registry.add("dimuon/realign/dcax_MuonKine_MuonCuts", "#mu^{+}#mu^{-} DCA_{x} (muon cuts)", {HistType::kTHnSparseF, {dcaxAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/dcay_MuonKine_MuonCuts", "#mu^{+}#mu^{-} DCA_{y} (muon cuts)", {HistType::kTHnSparseF, {dcayAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/dcax_MuonKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{x} (global muon cuts, good matches)", {HistType::kTHnSparseF, {dcaxAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/dcay_MuonKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{y} (global muon cuts, good matches)", {HistType::kTHnSparseF, {dcayAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/dcax_ScaledMftKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{x} (global muon cuts, rescaled MFT, good matches)", {HistType::kTHnSparseF, {dcaMftxAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/dcay_ScaledMftKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} DCA_{y} (global muon cuts, rescaled MFT, good matches)", {HistType::kTHnSparseF, {dcaMftyAxis, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // mu+mu- decay length z + registry.add("dimuon/realign/Lz_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{z} (global muon cuts, good matches)", {HistType::kTHnSparseF, {lzAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/tauz_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{z} (global muon cuts, good matches)", {HistType::kTHnSparseF, {tauzAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + // mu+mu- decay length xy + registry.add("dimuon/realign/Lxy_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{xy} (global muon cuts, good matches)", {HistType::kTHnSparseF, {lxyAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + registry.add("dimuon/realign/tauxy_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} L_{xy} (global muon cuts, good matches)", {HistType::kTHnSparseF, {tauxyAxis, invMassAxisCoarse, pAxis, pTAxis, muPosQuadrantAxis, muNegQuadrantAxis}}); + } } int GetDetElemId(int iDetElemNumber) { - const int fgNCh = 10; - const int fgNDetElemCh[fgNCh] = {4, 4, 4, 4, 18, 18, 26, 26, 26, 26}; - const int fgSNDetElemCh[fgNCh + 1] = {0, 4, 8, 12, 16, 34, 52, 78, 104, 130, 156}; + constexpr int fgNCh = 10; + const std::array fgNDetElemCh{4, 4, 4, 4, 18, 18, 26, 26, 26, 26}; + const std::array fgSNDetElemCh{0, 4, 8, 12, 16, 34, 52, 78, 104, 130, 156}; // make sure detector number is valid - if (!(iDetElemNumber >= fgSNDetElemCh[0] && - iDetElemNumber < fgSNDetElemCh[10])) { + if (iDetElemNumber < fgSNDetElemCh[0] || + iDetElemNumber >= fgSNDetElemCh[10]) { LOGF(fatal, "Invalid detector element number: %d", iDetElemNumber); } /// get det element number from ID // get chamber and element number in chamber int iCh = 0; int iDet = 0; - for (int i = 1; i <= 10; i++) { + for (int i = 1; i <= NMchChambers; i++) { if (iDetElemNumber < fgSNDetElemCh[i]) { iCh = i; iDet = iDetElemNumber - fgSNDetElemCh[i - 1]; @@ -637,7 +934,7 @@ struct muonGlobalAlignment { } // make sure detector index is valid - if (!(iCh > 0 && iCh <= 10 && iDet < fgNDetElemCh[iCh - 1])) { + if (iCh <= 0 || iCh > NMchChambers || iDet >= fgNDetElemCh[iCh - 1]) { LOGF(fatal, "Invalid detector element id: %d", 100 * iCh + iDet); } @@ -680,7 +977,7 @@ struct muonGlobalAlignment { { static int nDE = 0; if (nDE <= 0) { - for (int c = 0; c < 10; c++) { + for (int c = 0; c < NMchChambers; c++) { nDE += getNumDEinChamber(c); } } @@ -710,18 +1007,18 @@ struct muonGlobalAlignment { return idx + offset; } - int GetQuadrant(double phi) + int GetQuadrant(float phi) { - if (phi >= 0 && phi < 90) { + if (phi >= 0 && phi < o2::constants::math::PIHalf) { return 0; } - if (phi >= 90 && phi <= 180) { + if (phi >= o2::constants::math::PIHalf && phi <= o2::constants::math::PI) { return 1; } - if (phi >= -180 && phi < -90) { + if (phi >= -o2::constants::math::PI && phi < -o2::constants::math::PIHalf) { return 2; } - if (phi >= -90 && phi < 0) { + if (phi >= -o2::constants::math::PIHalf && phi < 0) { return 3; } return -1; @@ -730,8 +1027,7 @@ struct muonGlobalAlignment { template int GetQuadrant(const T& track) { - double phi = track.phi() * 180 / TMath::Pi(); - return GetQuadrant(phi); + return GetQuadrant(static_cast(track.phi())); } template @@ -744,18 +1040,16 @@ struct muonGlobalAlignment { // MCH track format. // Parameter conversion - double alpha1, alpha3, alpha4, x2, x3, x4; - - x2 = fwdtrack.getPhi(); - x3 = fwdtrack.getTanl(); - x4 = fwdtrack.getInvQPt(); + double x2 = fwdtrack.getPhi(); + double x3 = fwdtrack.getTanl(); + double x4 = fwdtrack.getInvQPt(); auto sinx2 = TMath::Sin(x2); auto cosx2 = TMath::Cos(x2); - alpha1 = cosx2 / x3; - alpha3 = sinx2 / x3; - alpha4 = x4 / TMath::Sqrt(x3 * x3 + sinx2 * sinx2); + double alpha1 = cosx2 / x3; + double alpha3 = sinx2 / x3; + double alpha4 = x4 / TMath::Sqrt(x3 * x3 + sinx2 * sinx2); auto K = TMath::Sqrt(x3 * x3 + sinx2 * sinx2); auto K3 = K * K * K; @@ -800,11 +1094,11 @@ struct muonGlobalAlignment { // jacobian*covariances*jacobian^T covariances = ROOT::Math::Similarity(jacobian, covariances); - double cov[] = {covariances(0, 0), covariances(1, 0), covariances(1, 1), covariances(2, 0), covariances(2, 1), covariances(2, 2), covariances(3, 0), covariances(3, 1), covariances(3, 2), covariances(3, 3), covariances(4, 0), covariances(4, 1), covariances(4, 2), covariances(4, 3), covariances(4, 4)}; - double param[] = {fwdtrack.getX(), alpha1, fwdtrack.getY(), alpha3, alpha4}; + const std::array cov{covariances(0, 0), covariances(1, 0), covariances(1, 1), covariances(2, 0), covariances(2, 1), covariances(2, 2), covariances(3, 0), covariances(3, 1), covariances(3, 2), covariances(3, 3), covariances(4, 0), covariances(4, 1), covariances(4, 2), covariances(4, 3), covariances(4, 4)}; + const std::array param{fwdtrack.getX(), alpha1, fwdtrack.getY(), alpha3, alpha4}; - o2::mch::TrackParam convertedTrack(fwdtrack.getZ(), param, cov); - return o2::mch::TrackParam(convertedTrack); + o2::mch::TrackParam convertedTrack(fwdtrack.getZ(), param.data(), cov.data()); + return {convertedTrack}; } static o2::dataformats::GlobalFwdTrack MCHtoFwd(const o2::mch::TrackParam& mchParam) @@ -818,15 +1112,13 @@ struct muonGlobalAlignment { o2::dataformats::GlobalFwdTrack convertedTrack; // Parameter conversion - double alpha1, alpha3, alpha4, x2, x3, x4; + double alpha1 = mchParam.getNonBendingSlope(); + double alpha3 = mchParam.getBendingSlope(); + double alpha4 = mchParam.getInverseBendingMomentum(); - alpha1 = mchParam.getNonBendingSlope(); - alpha3 = mchParam.getBendingSlope(); - alpha4 = mchParam.getInverseBendingMomentum(); - - x2 = TMath::ATan2(-alpha3, -alpha1); - x3 = -1. / TMath::Sqrt(alpha3 * alpha3 + alpha1 * alpha1); - x4 = alpha4 * -x3 * TMath::Sqrt(1 + alpha3 * alpha3); + double x2 = TMath::ATan2(-alpha3, -alpha1); + double x3 = -1. / TMath::Sqrt(alpha3 * alpha3 + alpha1 * alpha1); + double x4 = alpha4 * -x3 * TMath::Sqrt(1 + alpha3 * alpha3); auto K = alpha1 * alpha1 + alpha3 * alpha3; auto K32 = K * TMath::Sqrt(K); @@ -935,22 +1227,22 @@ struct muonGlobalAlignment { auto itNextToNextParam = (itNextParam == track.end()) ? itNextParam : std::next(itNextParam); itStartingParam = track.rbegin(); - if (track.getNClusters() < 10) { + if (track.getNClusters() < NMchChambers) { removeTrack = true; break; - } else { - while (itNextToNextParam != track.end()) { - if (itNextToNextParam->getClusterPtr()->getChamberId() != itNextParam->getClusterPtr()->getChamberId()) { - itStartingParam = std::make_reverse_iterator(++itNextParam); - break; - } - ++itNextToNextParam; + } + + while (itNextToNextParam != track.end()) { + if (itNextToNextParam->getClusterPtr()->getChamberId() != itNextParam->getClusterPtr()->getChamberId()) { + itStartingParam = std::make_reverse_iterator(++itNextParam); + break; } + ++itNextToNextParam; } } if (!removeTrack) { - for (auto& param : track) { + for (auto& param : track) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) param.setParameters(param.getSmoothParameters()); param.setCovariances(param.getSmoothCovariances()); } @@ -965,12 +1257,14 @@ struct muonGlobalAlignment { int nClustersCut) { // chi2 cut - if (mftTrack.chi2() > chi2Cut) + if (mftTrack.chi2() > chi2Cut) { return false; + } // number of clusters cut - if (mftTrack.nClusters() < nClustersCut) + if (mftTrack.nClusters() < nClustersCut) { return false; + } return true; } @@ -978,7 +1272,7 @@ struct muonGlobalAlignment { template bool IsGoodMFT(const T& mftTrack) { - return IsGoodMFT(mftTrack, fTrackChi2MftUp, fTrackNClustMftLow); + return IsGoodMFT(mftTrack, cfgTrackChi2MftUp, cfgTrackNClustMftLow); } template @@ -998,16 +1292,13 @@ struct muonGlobalAlignment { double p = mchTrackAtVertex.getP(); double pDCA = mchTrack.pDca(); - double sigmaPDCA = (thetaAbs < 3) ? sigmaPDCA23 : sigmaPDCA310; + double sigmaPDCA = (thetaAbs < ThetaAbsBoundaryDeg) ? sigmaPDCA23 : sigmaPDCA310; double nrp = nSigmaPDCA * relPRes * p; double pResEffect = sigmaPDCA / (1. - nrp / (1. + nrp)); - double slopeResEffect = 535. * slopeRes * p; + double slopeResEffect = SlopeResolutionZ * slopeRes * p; double sigmaPDCAWithRes = TMath::Sqrt(pResEffect * pResEffect + slopeResEffect * slopeResEffect); - if (pDCA > nSigmaPDCA * sigmaPDCAWithRes) { - return false; - } - return true; + return (pDCA <= nSigmaPDCA * sigmaPDCAWithRes); } template @@ -1019,36 +1310,55 @@ struct muonGlobalAlignment { std::array rAbsCut, double nSigmaPdcaCut) { - auto const& mchTrack = (static_cast(muonTrack.trackType()) <= 2) ? muonTrack.template matchMCHTrack_as() : muonTrack; + if (static_cast(muonTrack.trackType()) <= GlobalTrackTypeMax) { + LOGF(warning, "IsGoodMuon() called with global forward track"); + } // chi2 cut - if (mchTrack.chi2() > chi2Cut) + if (muonTrack.chi2() > chi2Cut) { return false; + } // momentum cut - if (mchTrack.p() < pCut) { + if (muonTrack.p() < pCut) { return false; // skip low-momentum tracks } // transverse momentum cut - if (mchTrack.pt() < pTCut) { + if (muonTrack.pt() < pTCut) { return false; // skip low-momentum tracks } // Eta cut - double eta = mchTrack.eta(); + double eta = muonTrack.eta(); if ((eta < etaCut[0] || eta > etaCut[1])) { return false; } // RAbs cut - double rAbs = mchTrack.rAtAbsorberEnd(); + double rAbs = muonTrack.rAtAbsorberEnd(); if ((rAbs < rAbsCut[0] || rAbs > rAbsCut[1])) { return false; } // pDCA cut - if (!pDCACut(mchTrack, collision, nSigmaPdcaCut)) { + if (!pDCACut(muonTrack, collision, nSigmaPdcaCut)) { + return false; + } + + return true; + } + + template + bool isGoodGlobalMatching(const TMUON& muon, + double matchChi2Cut) + { + if (static_cast(muon.trackType()) > GlobalTrackTypeMax) { + return false; + } + + // MFT-MCH match chi2 cut + if (muon.chi2MatchMCHMFT() > matchChi2Cut) { return false; } @@ -1056,7 +1366,67 @@ struct muonGlobalAlignment { } template - o2::dataformats::GlobalFwdTrack FwdToTrackPar(const T& track) + o2::track::TrackParCovFwd TrackToParCovFwd(const T& track) + { + double chi2 = track.chi2(); + SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); + std::vector v1{0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0}; + SMatrix55 tcovs(v1.begin(), v1.end()); + return {track.z(), tpars, tcovs, chi2}; + } + + template + o2::track::TrackParCovFwd TrackToParCovFwd(const T& track, const C& cov) + { + double chi2 = track.chi2(); + SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); + std::vector v1{cov.cXX(), cov.cXY(), cov.cYY(), cov.cPhiX(), cov.cPhiY(), + cov.cPhiPhi(), cov.cTglX(), cov.cTglY(), cov.cTglPhi(), cov.cTglTgl(), + cov.c1PtX(), cov.c1PtY(), cov.c1PtPhi(), cov.c1PtTgl(), cov.c1Pt21Pt2()}; + SMatrix55 tcovs(v1.begin(), v1.end()); + return {track.z(), tpars, tcovs, chi2}; + } + + o2::track::TrackParCovFwd MatchedTrackToParCovFwd(const o2::track::TrackParCovFwd& mchTrackPar, + const o2::track::TrackParCovFwd& mftTrackPar) + { + // extrapolation of MCH track to first MFT track point + auto mchTrackAtMFT = FwdtoMCH(mchTrackPar); + o2::mch::TrackExtrap::extrapToVertex(mchTrackAtMFT, + mftTrackPar.getX(), + mftTrackPar.getY(), + mftTrackPar.getZ(), + std::sqrt(mftTrackPar.getSigma2X()), + std::sqrt(mftTrackPar.getSigma2Y())); + + auto fwdTrackRefit = fwdtrackutils::refitGlobalMuonCov(MCHtoFwd(mchTrackAtMFT), mftTrackPar); + + return {fwdTrackRefit.getZ(), fwdTrackRefit.getParameters(), fwdTrackRefit.getCovariances(), fwdTrackRefit.getTrackChi2()}; + } + + template + o2::track::TrackParCovFwd MatchedTrackToParCovFwd(const T& mchTrack, const T& mftTrack, const C& mftCov) + { + auto mftTrackPar = TrackToParCovFwd(mftTrack, mftCov); + + // extrapolation of MCH track to first MFT track point + auto mchTrackAtMFT = FwdtoMCH(TrackToParCovFwd(mchTrack, mchTrack)); + o2::mch::TrackExtrap::extrapToVertex(mchTrackAtMFT, + mftTrackPar.getX(), + mftTrackPar.getY(), + mftTrackPar.getZ(), + std::sqrt(mftTrackPar.getSigma2X()), + std::sqrt(mftTrackPar.getSigma2Y())); + + auto fwdTrackRefit = fwdtrackutils::refitGlobalMuonCov(MCHtoFwd(mchTrackAtMFT), mftTrackPar); + + return {fwdTrackRefit.getZ(), fwdTrackRefit.getParameters(), fwdTrackRefit.getCovariances(), fwdTrackRefit.getTrackChi2()}; + } + + template + o2::dataformats::GlobalFwdTrack TrackToGlobalFwd(const T& track) { double chi2 = track.chi2(); SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); @@ -1072,6 +1442,23 @@ struct muonGlobalAlignment { return fwdtrack; } + template + o2::dataformats::GlobalFwdTrack TrackToGlobalFwd(const T& track, const C& cov) + { + double chi2 = track.chi2(); + SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); + std::vector v1{cov.cXX(), cov.cXY(), cov.cYY(), cov.cPhiX(), cov.cPhiY(), + cov.cPhiPhi(), cov.cTglX(), cov.cTglY(), cov.cTglPhi(), cov.cTglTgl(), + cov.c1PtX(), cov.c1PtY(), cov.c1PtPhi(), cov.c1PtTgl(), cov.c1Pt21Pt2()}; + SMatrix55 tcovs(v1.begin(), v1.end()); + o2::track::TrackParCovFwd trackparCov{track.z(), tpars, tcovs, chi2}; + o2::dataformats::GlobalFwdTrack fwdtrack; + fwdtrack.setParameters(trackparCov.getParameters()); + fwdtrack.setZ(trackparCov.getZ()); + fwdtrack.setCovariances(trackparCov.getCovariances()); + return fwdtrack; + } + void TransformMFTPar(o2::mch::TrackParam& track) { // double zCH10 = -1437.6; @@ -1082,49 +1469,26 @@ struct muonGlobalAlignment { double xSlope = track.getNonBendingSlope(); double ySlope = track.getBendingSlope(); - double xSlopeCorrection = (y > 0) ? configMFTAlignmentCorrections.fMFTAlignmentCorrXSlopeTop : configMFTAlignmentCorrections.fMFTAlignmentCorrXSlopeBottom; + double xSlopeCorrection = (y > 0) ? configMFTAlignmentCorrections.cfgMFTAlignmentCorrXSlopeTop : configMFTAlignmentCorrections.cfgMFTAlignmentCorrXSlopeBottom; double xCorrection = xSlopeCorrection * z + - ((y > 0) ? configMFTAlignmentCorrections.fMFTAlignmentCorrXOffsetTop : configMFTAlignmentCorrections.fMFTAlignmentCorrXOffsetBottom); + ((y > 0) ? configMFTAlignmentCorrections.cfgMFTAlignmentCorrXOffsetTop : configMFTAlignmentCorrections.cfgMFTAlignmentCorrXOffsetBottom); track.setNonBendingCoor(x + xCorrection); track.setNonBendingSlope(xSlope + xSlopeCorrection); - double ySlopeCorrection = (y > 0) ? configMFTAlignmentCorrections.fMFTAlignmentCorrYSlopeTop : configMFTAlignmentCorrections.fMFTAlignmentCorrYSlopeBottom; + double ySlopeCorrection = (y > 0) ? configMFTAlignmentCorrections.cfgMFTAlignmentCorrYSlopeTop : configMFTAlignmentCorrections.cfgMFTAlignmentCorrYSlopeBottom; double yCorrection = ySlopeCorrection * z + - ((y > 0) ? configMFTAlignmentCorrections.fMFTAlignmentCorrYOffsetTop : configMFTAlignmentCorrections.fMFTAlignmentCorrYOffsetBottom); + ((y > 0) ? configMFTAlignmentCorrections.cfgMFTAlignmentCorrYOffsetTop : configMFTAlignmentCorrections.cfgMFTAlignmentCorrYOffsetBottom); track.setBendingCoor(y + yCorrection); track.setBendingSlope(ySlope + ySlopeCorrection); - /* - std::cout << std::format("[TOTO] MFT position: pos={:0.3f},{:0.3f}", x, y) << std::endl; - std::cout << std::format("[TOTO] MFT corrections: pos={:0.3f},{:0.3f} slope={:0.12f},{:0.12f} angle={:0.12f},{:0.12f}", - xCorrection, yCorrection, xSlopeCorrection, ySlopeCorrection, - std::atan2(xSlopeCorrection, 1), std::atan2(ySlopeCorrection, 1)) << std::endl; - */ - } - - void TransformMFT(o2::dataformats::GlobalFwdTrack& track) - { - auto mchTrack = FwdtoMCH(track); - - TransformMFTPar(mchTrack); - - auto transformedTrack = sExtrap.MCHtoFwd(mchTrack); - track.setParameters(transformedTrack.getParameters()); - track.setZ(transformedTrack.getZ()); - track.setCovariances(transformedTrack.getCovariances()); } void TransformMFT(o2::track::TrackParCovFwd& fwdtrack) { - o2::dataformats::GlobalFwdTrack track; - track.setParameters(fwdtrack.getParameters()); - track.setZ(fwdtrack.getZ()); - track.setCovariances(fwdtrack.getCovariances()); - - auto mchTrack = FwdtoMCH(track); + auto mchTrack = FwdtoMCH(fwdtrack); TransformMFTPar(mchTrack); - auto transformedTrack = sExtrap.MCHtoFwd(mchTrack); + auto transformedTrack = MCHtoFwd(mchTrack); fwdtrack.setParameters(transformedTrack.getParameters()); fwdtrack.setZ(transformedTrack.getZ()); fwdtrack.setCovariances(transformedTrack.getCovariances()); @@ -1133,8 +1497,8 @@ struct muonGlobalAlignment { template T UpdateTrackMomentum(const T& track, const double p, int sign) { - double px = p * sin(M_PI / 2 - atan(track.tgl())) * cos(track.phi()); - double py = p * sin(M_PI / 2 - atan(track.tgl())) * sin(track.phi()); + double px = p * std::sin(o2::constants::math::PIHalf - std::atan(track.tgl())) * std::cos(track.phi()); + double py = p * std::sin(o2::constants::math::PIHalf - std::atan(track.tgl())) * std::sin(track.phi()); double pt = std::sqrt(std::pow(px, 2) + std::pow(py, 2)); SMatrix5 tpars = {track.x(), track.y(), track.phi(), track.tgl(), sign / pt}; @@ -1154,8 +1518,8 @@ struct muonGlobalAlignment { template T UpdateTrackMomentum(const T& track, const o2::mch::TrackParam& track4mom) { - double px = track4mom.p() * sin(M_PI / 2 - atan(track.tgl())) * cos(track.phi()); - double py = track4mom.p() * sin(M_PI / 2 - atan(track.tgl())) * sin(track.phi()); + double px = track4mom.p() * std::sin(o2::constants::math::PIHalf - std::atan(track.tgl())) * std::cos(track.phi()); + double py = track4mom.p() * std::sin(o2::constants::math::PIHalf - std::atan(track.tgl())) * std::sin(track.phi()); double pt = std::sqrt(std::pow(px, 2) + std::pow(py, 2)); double sign = track4mom.getCharge(); @@ -1200,6 +1564,15 @@ struct muonGlobalAlignment { if (mchTrack.getZ() < absBack && z > absFront) { // extrapolation through the absorber in the upstream direction o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrack, z); + } else if (z < absBack) { + // extrapolation downstream of the absorber, correct for dipole longitudinal shift if needed + if (cfgDipoleZshift.value != 0) { + mchTrack.setZ(mchTrack.getZ() + cfgDipoleZshift.value); + o2::mch::TrackExtrap::extrapToZCov(mchTrack, z + cfgDipoleZshift.value); + mchTrack.setZ(mchTrack.getZ() - cfgDipoleZshift.value); + } else { + o2::mch::TrackExtrap::extrapToZCov(mchTrack, z); + } } else { // all other cases o2::mch::TrackExtrap::extrapToZCov(mchTrack, z); @@ -1244,10 +1617,39 @@ struct muonGlobalAlignment { return PropagateMCH(track, z); } + o2::dataformats::GlobalFwdTrack PropagateMCHToVertex(const o2::track::TrackParCovFwd& muon, + const double vx, const double vy, const double vz, + const double covVx, const double covVy) + { + auto mchTrack = FwdtoMCH(muon); + + o2::mch::TrackExtrap::extrapToVertex(mchTrack, vx, vy, vz, covVx, covVy); + + auto proptrack = MCHtoFwd(mchTrack); + o2::dataformats::GlobalFwdTrack propmuon; + propmuon.setParameters(proptrack.getParameters()); + propmuon.setZ(proptrack.getZ()); + propmuon.setCovariances(proptrack.getCovariances()); + + return propmuon; + } + + template + o2::dataformats::GlobalFwdTrack PropagateMCHToVertex(const o2::track::TrackParCovFwd& muon, + const C& collision) + { + return PropagateMCHToVertex(muon, + collision.posX(), + collision.posY(), + collision.posZ(), + std::sqrt(collision.covXX()), + std::sqrt(collision.covYY())); + } + template o2::dataformats::GlobalFwdTrack PropagateMFT(const TMFT& mftTrack, float z) { - static double Bz = -10001; + // static double Bz = -10001; double chi2 = mftTrack.chi2(); SMatrix5 tpars = {mftTrack.x(), mftTrack.y(), mftTrack.phi(), mftTrack.tgl(), mftTrack.signed1Pt()}; std::vector v1{0, 0, 0, 0, 0, @@ -1265,12 +1667,12 @@ struct muonGlobalAlignment { // double centerZ[3] = {mftTrack.x() + propVec[0] / 2., // mftTrack.y() + propVec[1] / 2., // mftTrack.z() + propVec[2] / 2.}; - if (Bz < -10000) { - double centerZ[3] = {0, 0, (-45.f - 77.5f) / 2.f}; - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - Bz = field->getBz(centerZ); - } - fwdtrack.propagateToZ(z, Bz); + // if (Bz < -10000) { + // double centerZ[3] = {0, 0, (-45.f - 77.5f) / 2.f}; + // o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + // Bz = field->getBz(centerZ); + //} + fwdtrack.propagateToZ(z, mBzAtMftCenter); propmuon.setParameters(fwdtrack.getParameters()); propmuon.setZ(fwdtrack.getZ()); @@ -1279,27 +1681,12 @@ struct muonGlobalAlignment { return propmuon; } - template - o2::dataformats::GlobalFwdTrack PropagateMFTToDCA(const TMFT& mftTrack, const C& collision, float zshift) + template + o2::dataformats::GlobalFwdTrack PropagateMFTToDCA(o2::track::TrackParCovFwd mftTrackPar, + const C& collision, + float zshift) { - static double Bz = -10001; - double chi2 = mftTrack.chi2(); - double phiCorrDeg = 0; - double phiCorr = phiCorrDeg * TMath::Pi() / 180.f; - double tR = std::hypot(mftTrack.x(), mftTrack.y()); - double tphi = std::atan2(mftTrack.y(), mftTrack.x()); - double tx = std::cos(tphi + phiCorr) * tR; - double ty = std::sin(tphi + phiCorr) * tR; - SMatrix5 tpars = {tx, ty, mftTrack.phi() + phiCorr, mftTrack.tgl(), mftTrack.signed1Pt()}; - std::vector v1{0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0}; - SMatrix55 tcovs(v1.begin(), v1.end()); - o2::track::TrackParCovFwd fwdtrack{mftTrack.z(), tpars, tcovs, chi2}; - if (configMFTAlignmentCorrections.fEnableMFTAlignmentCorrections) { - TransformMFT(fwdtrack); - } - o2::dataformats::GlobalFwdTrack propmuon; + // static double Bz = -10001; // double propVec[3] = {}; // propVec[0] = collision.posX() - mftTrack.x(); @@ -1309,45 +1696,38 @@ struct muonGlobalAlignment { // double centerZ[3] = {mftTrack.x() + propVec[0] / 2., // mftTrack.y() + propVec[1] / 2., // mftTrack.z() + propVec[2] / 2.}; - if (Bz < -10000) { - double centerZ[3] = {0, 0, -45.f / 2.f}; - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - Bz = field->getBz(centerZ); - } - fwdtrack.propagateToZ(collision.posZ() - zshift, Bz); - - propmuon.setParameters(fwdtrack.getParameters()); - propmuon.setZ(fwdtrack.getZ()); - propmuon.setCovariances(fwdtrack.getCovariances()); - - return propmuon; + // if (Bz < -10000) { + // double centerZ[3] = {0, 0, -45.f / 2.f}; + // o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + // Bz = field->getBz(centerZ); + // } + mftTrackPar.propagateToZ(collision.posZ() - zshift, mBzAtMftCenter); + + o2::dataformats::GlobalFwdTrack result; + result.setParameters(mftTrackPar.getParameters()); + result.setZ(mftTrackPar.getZ()); + result.setCovariances(mftTrackPar.getCovariances()); + + return result; } - template - o2::dataformats::GlobalFwdTrack PropagateMFTToDCA(const TMFT& mftTrack, const TMUON& mchTrack, const C& collision, float zshift) + template + o2::dataformats::GlobalFwdTrack PropagateMFTToDCA(o2::track::TrackParCovFwd mftTrackPar, + const o2::track::TrackParCovFwd& mchTrackPar, + const C& collision, + float zshift) { - static double Bz = -10001; - double chi2 = mftTrack.chi2(); - double phiCorrDeg = 0; - double phiCorr = phiCorrDeg * TMath::Pi() / 180.f; - double tR = std::hypot(mftTrack.x(), mftTrack.y()); - double tphi = std::atan2(mftTrack.y(), mftTrack.x()); - double tx = std::cos(tphi + phiCorr) * tR; - double ty = std::sin(tphi + phiCorr) * tR; - SMatrix5 tpars = {tx, ty, mftTrack.phi() + phiCorr, mftTrack.tgl(), mftTrack.signed1Pt()}; - std::vector v1{0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0}; - SMatrix55 tcovs(v1.begin(), v1.end()); - o2::track::TrackParCovFwd fwdtrack{mftTrack.z(), tpars, tcovs, chi2}; - if (configMFTAlignmentCorrections.fEnableMFTAlignmentCorrections) { - TransformMFT(fwdtrack); - } + // static double Bz = -10001; // extrapolation with MCH tools - auto mchTrackAtMFT = FwdtoMCH(FwdToTrackPar(mchTrack)); - o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrackAtMFT, mftTrack.z()); - UpdateTrackMomentum(fwdtrack, mchTrackAtMFT); + auto mchTrackAtMFT = FwdtoMCH(mchTrackPar); + o2::mch::TrackExtrap::extrapToVertex(mchTrackAtMFT, + mftTrackPar.getX(), + mftTrackPar.getY(), + mftTrackPar.getZ(), + std::sqrt(mftTrackPar.getSigma2X()), + std::sqrt(mftTrackPar.getSigma2Y())); + UpdateTrackMomentum(mftTrackPar, mchTrackAtMFT); // double propVec[3] = {}; // propVec[0] = collision.posX() - mftTrack.x(); @@ -1357,76 +1737,803 @@ struct muonGlobalAlignment { // double centerZ[3] = {mftTrack.x() + propVec[0] / 2., // mftTrack.y() + propVec[1] / 2., // mftTrack.z() + propVec[2] / 2.}; - if (Bz < -10000) { - double centerZ[3] = {0, 0, -45.f / 2.f}; - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - Bz = field->getBz(centerZ); - } - fwdtrack.propagateToZ(collision.posZ() - zshift, Bz); - - o2::dataformats::GlobalFwdTrack propmuon; - propmuon.setParameters(fwdtrack.getParameters()); - propmuon.setZ(fwdtrack.getZ()); - propmuon.setCovariances(fwdtrack.getCovariances()); - - return propmuon; + // if (Bz < -10000) { + // double centerZ[3] = {0, 0, -45.f / 2.f}; + // o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + // Bz = field->getBz(centerZ); + // } + mftTrackPar.propagateToZ(collision.posZ() - zshift, mBzAtMftCenter); + + o2::dataformats::GlobalFwdTrack result; + result.setParameters(mftTrackPar.getParameters()); + result.setZ(mftTrackPar.getZ()); + result.setCovariances(mftTrackPar.getCovariances()); + + return result; } - template - o2::dataformats::GlobalFwdTrack PropagateMFTtoMCH(const TMFT& mftTrack, const o2::mch::TrackParam& mchTrackPar, const double z) + o2::dataformats::GlobalFwdTrack PropagateMFTtoMCH(const o2::track::TrackParCovFwd& mftTrackPar, + const o2::mch::TrackParam& mchTrackPar, + const double z) { // extrapolation with MCH tools auto mchTrackAtMFT = mchTrackPar; - o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrackAtMFT, mftTrack.z()); + o2::mch::TrackExtrap::extrapToVertex(mchTrackAtMFT, + mftTrackPar.getX(), + mftTrackPar.getY(), + mftTrackPar.getZ(), + std::sqrt(mftTrackPar.getSigma2X()), + std::sqrt(mftTrackPar.getSigma2Y())); - auto mftTrackPar = FwdToTrackPar(mftTrack); - if (configMFTAlignmentCorrections.fEnableMFTAlignmentCorrections) { - TransformMFT(mftTrackPar); - } auto mftTrackProp = FwdtoMCH(mftTrackPar); UpdateTrackMomentum(mftTrackProp, mchTrackAtMFT); - if (z < -505.f) { - o2::mch::TrackExtrap::extrapToZ(mftTrackProp, -466.f); + if (z < AbsorberBackZ) { + o2::mch::TrackExtrap::extrapToZ(mftTrackProp, BransonPlaneZ); UpdateTrackMomentum(mftTrackProp, mchTrackPar); } - o2::mch::TrackExtrap::extrapToZ(mftTrackProp, z); + + if (cfgDipoleZshift.value != 0) { + // extrapolate to the back of the absorber, taking into account the dipole shift, + // to avoid that the correction bring the track starting point back into the absorber + if (cfgDipoleZshift.value < 0) { + o2::mch::TrackExtrap::extrapToZ(mftTrackProp, AbsorberBackZ); + } else if (cfgDipoleZshift.value > 0) { + o2::mch::TrackExtrap::extrapToZ(mftTrackProp, AbsorberBackZ - cfgDipoleZshift.value); + } + // shift the track starting point + mftTrackProp.setZ(mftTrackProp.getZ() + cfgDipoleZshift.value); + // extrapolate to the final z, corrected for the dipole shift + o2::mch::TrackExtrap::extrapToZ(mftTrackProp, z + cfgDipoleZshift.value); + // remove the shift from the extrapolated track + mftTrackProp.setZ(mftTrackProp.getZ() - cfgDipoleZshift.value); + } else { + o2::mch::TrackExtrap::extrapToZ(mftTrackProp, z); + } return MCHtoFwd(mftTrackProp); } - void FillDCAPlots(MyEvents const& collisions, - MyBCs const& bcs, - MyMuonsWithCov const& muonTracks, - MyMFTs const& mftTracks, - const std::map& collisionInfos) + template + o2::dataformats::GlobalFwdTrack PropagateMFTToVertex(const o2::track::TrackParCovFwd& mftTrackPar, + const o2::track::TrackParCovFwd& mchTrackPar, + const C& collision) { - // outer loop over collisions - for (auto& [collisionIndex, collisionInfo] : collisionInfos) { - auto const& collision = collisions.rawIteratorAt(collisionIndex); - const auto& bc = bcs.rawIteratorAt(collision.bcId()); + // extrapolation with MCH tools + auto mchTrackAtMFT = FwdtoMCH(mchTrackPar); + o2::mch::TrackExtrap::extrapToVertex(mchTrackAtMFT, + mftTrackPar.getX(), + mftTrackPar.getY(), + mftTrackPar.getZ(), + std::sqrt(mftTrackPar.getSigma2X()), + std::sqrt(mftTrackPar.getSigma2Y())); + + auto fwdTrackRefit = fwdtrackutils::refitGlobalMuonCov(MCHtoFwd(mchTrackAtMFT), mftTrackPar); + + // apply vertex shift correction + fwdTrackRefit.setZ(fwdTrackRefit.getZ() + cfgVertexZshift.value); + + auto fwdTrackProp = fwdtrackutils::propagateTrackParCovFwd(fwdTrackRefit, + 0, + collision, + fwdtrackutils::propagationPoint::kToVertex, + cfgRefPlaneZMFT, + mBzAtMftCenter); + + return fwdTrackProp; + } - // remove TF/ROF borders and ambiguous collisions - if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || - !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - continue; + void getMuonPairs(const CollisionInfo& collisionInfo, + std::vector& muonPairs) + { + // outer loop over muon tracks + for (const auto& mchIndex1 : collisionInfo.mchTracks) { + // inner loop over muon tracks + for (const auto& mchIndex2 : collisionInfo.mchTracks) { + // avoid double-counting of muon pairs + if (mchIndex2 <= mchIndex1) { + continue; + } + + muonPairs.emplace_back(mchIndex1, mchIndex2); + } + } + } + + ROOT::Math::PxPyPzMVector getMuMu4Momentum(const o2::dataformats::GlobalFwdTrack& track1, const o2::dataformats::GlobalFwdTrack& track2) + { + ROOT::Math::PxPyPzMVector muon1{ + track1.getPx(), + track1.getPy(), + track1.getPz(), + o2::constants::physics::MassMuon}; + + ROOT::Math::PxPyPzMVector muon2{ + track2.getPx(), + track2.getPy(), + track2.getPz(), + o2::constants::physics::MassMuon}; + + return muon1 + muon2; + } + + double getMuMuAngle(const o2::dataformats::GlobalFwdTrack& track1, const o2::dataformats::GlobalFwdTrack& track2) + { + ROOT::Math::XYZVector muon1{ + track1.getPx(), + track1.getPy(), + track1.getPz()}; + + ROOT::Math::XYZVector muon2{ + track2.getPx(), + track2.getPy(), + track2.getPz()}; + + return std::acos(muon1.Unit().Dot(muon2.Unit())); + } + + double getMuMuInvariantMass(const o2::dataformats::GlobalFwdTrack& track1, const o2::dataformats::GlobalFwdTrack& track2) + { + return getMuMu4Momentum(track1, track2).M(); + } + + template + std::optional> getMuMuDecayLength(const o2::track::TrackParCovFwd& track1, + const o2::track::TrackParCovFwd& track2, + const C& collision) + { + auto progCode = fgFitterTwoProngFwd.process(track1, track2); + + if (progCode == 0) { + return {}; + } + + // Get pca candidate from forward DCA fitter + auto secondaryVertex = fgFitterTwoProngFwd.getPCACandidate(); + + double Lxy = (collision.posX() - secondaryVertex[0]) * (collision.posX() - secondaryVertex[0]) + + (collision.posY() - secondaryVertex[1]) * (collision.posY() - secondaryVertex[1]); + Lxy = std::sqrt(Lxy); + + double Lz = collision.posZ() - secondaryVertex[2]; + + return {{Lxy, Lz}}; + } + + template + bool MchRefitTrack(const TMUON& mchTrack, const TCLUS& clusters, TrackRealigned& convertedTrack, bool applyCorrections) + { + auto mchTrackPar = FwdtoMCH(TrackToGlobalFwd(mchTrack)); + + // loop over attached clusters + int clIndex = -1; + auto clustersSliced = clusters.sliceBy(perMuon, mchTrack.globalIndex()); // Slice clusters by muon id + for (auto const& cluster : clustersSliced) { + clIndex += 1; + + int deId = cluster.deId(); + int chamber = deId / 100 - 1; + if (chamber < 0 || chamber >= NMchChambers) { + continue; + } + + math_utils::Point3D local; + math_utils::Point3D master; + + master.SetXYZ(cluster.x(), cluster.y(), cluster.z()); + + if (configRealign.cfgEnableMCHRealign) { + // Transformation from reference geometry frame to new geometry frame + transformRef[cluster.deId()].MasterToLocal(master, local); + transformNew[cluster.deId()].LocalToMaster(local, master); + } + + // shift the clusters to correct the longitudinal shift of the dipole + if (cfgDipoleZshift.value != 0) { + master.SetZ(master.z() + cfgDipoleZshift.value); + } + + if (applyCorrections) { + auto correctionsIt = mMchAlignmentCorrections.find(cluster.deId()); + if (correctionsIt != mMchAlignmentCorrections.end()) { + const auto& corrections = correctionsIt->second; + master.SetX(master.x() + corrections.x); + master.SetY(master.y() + corrections.y); + master.SetZ(master.z() + corrections.z); + } + } + + // realigned MCH cluster + auto clusterMCH = new mch::Cluster(); + clusterMCH->x = master.x(); + clusterMCH->y = master.y(); + clusterMCH->z = master.z(); + + uint32_t ClUId = mch::Cluster::buildUniqueId(static_cast(cluster.deId() / 100) - 1, cluster.deId(), clIndex); + clusterMCH->uid = ClUId; + clusterMCH->ex = cluster.isGoodX() ? 0.2 : 10.0; + clusterMCH->ey = cluster.isGoodY() ? 0.2 : 10.0; + + // Add transformed cluster into temporary variable + convertedTrack.createParamAtCluster(*clusterMCH); + } + + bool removable{false}; + // Refit the re-aligned track + if (convertedTrack.getNClusters() != 0) { + removable = RemoveTrack(convertedTrack); + } else { + LOGF(fatal, "Muon track %d has no associated clusters.", mchTrack.globalIndex()); + } + + // subtract the longitudinal shift of the dipole from the track z + if (cfgDipoleZshift.value != 0) { + auto& trackParam = *(convertedTrack.begin()); + trackParam.setZ(trackParam.getZ() - cfgDipoleZshift.value); + } + + return !removable; + } + + template + void InitCollisions(COLL const& collisions, + BC const& bcs, + TMUON const& muonTracks, + TCLS const& clusters, + TMFT const& mftTracks, + std::unordered_map& collisionInfos) + { + mMchTrackPars.clear(); + mMchTrackParsNew.clear(); + mMchTracks.clear(); + mGlobalMuonTracks.clear(); + + // fill collision information for global muon tracks (MFT-MCH-MID matches) + for (const auto& muonTrack : muonTracks) { + uint32_t nCompatColl = 0; + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + + auto collision = collisions.rawIteratorAt(collisionId); + const auto& bc = bcs.rawIteratorAt(collision.bcId()); + + if (configEvSel.fEvSelRequireFrameBordersExclusion) { + // remove TF/ROF borders and ambiguous collisions + if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || + !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + continue; + } + } + + // remove events with multiple collisions in one bunch crossing + if (configEvSel.fEvSelRequireNoCollInTimeRange && + !bc.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + continue; + } + + // remove events with multiple collisions in one bunch crossing + if (configEvSel.fEvSelRequireNoSameBunch && + !bc.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } + + // remove events with bad RCT flags + if (configEvSel.fEvSelRequireGoodRCT && !rctChecker(collision)) { + continue; + } + + if (collision.posZ() < configEvSel.fEvSelVtxZMin || + collision.posZ() > configEvSel.fEvSelVtxZMax) { + continue; + } + + nCompatColl += 1; + + uint64_t collisionIndex = collision.globalIndex(); + + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = bc.runNumber(); + collisionInfo.globalBC = bc.globalBC(); + collisionInfo.timestamp = bc.timestamp(); + collisionInfo.zVertex = collision.posZ(); + + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { + // standalone MCH or MCH-MID tracks + uint64_t mchTrackIndex = muonTrack.globalIndex(); + collisionInfo.mchTracks.push_back(mchTrackIndex); + + mMchTracks.push_back(mchTrackIndex); + + // initialize the original MCH track parameters + mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + + // refit MCH track if requested + if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { + TrackRealigned convertedTrack; + bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + + // Get the re-aligned track parameters: track param at the first cluster + mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + + auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); + if (mchTrackParIt.second) { + // the insertion succeeded + mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); + if (!convertedTrackOk) { + mchTrackParIt.first->second.setRemovable(); + } + } + } else { + // initialize the new MCH track parameters with the original ones, without refitting + mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + } + } else { + // global muon tracks (MFT-MCH or MFT-MCH-MID) + uint64_t muonTrackIndex = muonTrack.globalIndex(); + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + uint64_t mchTrackIndex = mchTrack.globalIndex(); + + mGlobalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + + // check if a vector of global muon candidates is already available for the current MCH index + // if not, initialize a new one and add the current global muon track + // bool globalMuonTrackFound = false; + auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); + if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { + matchingCandidateIterator->second.push_back(muonTrackIndex); + // globalMuonTrackFound = true; + } else { + collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } + } + } + + // store the number of compatible collisions for this track in all the corresponding CollisionInfo objects + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + try { + auto& collisionInfo = collisionInfos.at(collisionId); + collisionInfo.muonTracksCollMult[muonTrack.globalIndex()] = nCompatColl; + } catch (const std::exception& e) { + continue; + } + } + } + + // sort the vectors of matching candidates in ascending order based on the matching chi2 value + auto compareChi2 = [&muonTracks](uint64_t trackIndex1, uint64_t trackIndex2) -> bool { + auto const& track1 = muonTracks.rawIteratorAt(trackIndex1); + auto const& track2 = muonTracks.rawIteratorAt(trackIndex2); + + return (track1.chi2MatchMCHMFT() < track2.chi2MatchMCHMFT()); + }; + + for (auto& [collisionIndex, collisionInfo] : collisionInfos) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + for (auto& [mchIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareChi2); + } + } + + mMftTrackPars.clear(); + mMftTrackParsNew.clear(); + + // fill collision information for MFT standalone tracks + for (const auto& mftTrack : mftTracks) { + if (!mftTrack.has_collision()) { + continue; + } + + auto collision = collisions.rawIteratorAt(mftTrack.collisionId()); + uint64_t collisionIndex = collision.globalIndex(); + + auto bc = bcs.rawIteratorAt(collision.bcId()); + + // remove TF/ROF borders and ambiguous collisions + if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || + !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + continue; + } + + if (configEvSel.fEvSelRequireGoodRCT && !rctChecker(collision)) { + continue; + } + + uint64_t mftTrackIndex = mftTrack.globalIndex(); + + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = bc.runNumber(); + collisionInfo.timestamp = bc.timestamp(); + collisionInfo.zVertex = collision.posZ(); + + collisionInfo.mftTracks.push_back(mftTrackIndex); + + // initialize the original MFT track parameters + auto mftTrackFwd = TrackToParCovFwd(mftTrack); + mMftTrackPars.try_emplace(mftTrackIndex, TrackParExt(mftTrackFwd, mftTrack.nClusters())); + + // initialize the corrected MFT track parameters, if requested + if (configMFTAlignmentCorrections.cfgEnableMFTAlignmentCorrections) { + TransformMFT(mftTrackFwd); + mMftTrackParsNew.try_emplace(mftTrackIndex, TrackParExt(mftTrackFwd, mftTrack.nClusters())); + } else { + // initialize the new MFT track parameters with the original ones, without corrections + mMftTrackParsNew.try_emplace(mftTrackIndex, TrackParExt(mftTrackFwd, mftTrack.nClusters())); + } + } + } + + template + void InitCollisionsDerivedTables(COLL const& collisions, + TMUON const& muonTracks, + TCLS const& clusters, + TMFT const& mftTracks, + std::unordered_map& collisionInfos) + { + mMchTrackPars.clear(); + mMchTrackParsNew.clear(); + mMchTracks.clear(); + mGlobalMuonTracks.clear(); + + // fill collision information for global muon tracks (MFT-MCH-MID matches) + for (const auto& muonTrack : muonTracks) { + uint32_t nCompatColl = 0; + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + + auto collision = collisions.rawIteratorAt(collisionId); + uint64_t collisionIndex = collision.globalIndex(); + + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = collision.runNumber(); + collisionInfo.globalBC = collision.globalBC(); + collisionInfo.timestamp = collision.timestamp(); + collisionInfo.zVertex = collision.posZ(); + + nCompatColl += 1; + + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { + // standalone MCH or MCH-MID tracks + uint64_t mchTrackIndex = muonTrack.globalIndex(); + collisionInfo.mchTracks.push_back(mchTrackIndex); + + mMchTracks.push_back(mchTrackIndex); + + // initialize the original MCH track parameters + mMchTrackPars.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + + // refit MCH track if requested + if (configRealign.cfgEnableMCHRefit || configRealign.cfgEnableMCHRealign) { + TrackRealigned convertedTrack; + bool convertedTrackOk = MchRefitTrack(muonTrack, clusters, convertedTrack, !mMchAlignmentCorrections.empty()); + + // Get the re-aligned track parameters: track param at the first cluster + mch::TrackParam trackParam = mch::TrackParam(convertedTrack.first()); + + auto mchTrackParIt = mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(MCHtoFwd(trackParam), convertedTrack.getNClusters())); + if (mchTrackParIt.second) { + // the insertion succeeded + mchTrackParIt.first->second.setTrackChi2(trackParam.getTrackChi2() / convertedTrack.getNDF()); + if (!convertedTrackOk) { + mchTrackParIt.first->second.setRemovable(); + } + } + } else { + // initialize the new MCH track parameters with the original ones, without refitting + mMchTrackParsNew.try_emplace(mchTrackIndex, TrackParExt(fwdtrackutils::getTrackParCovFwd(muonTrack, muonTrack), muonTrack.nClusters())); + } + } else { + // global muon tracks (MFT-MCH or MFT-MCH-MID) + uint64_t muonTrackIndex = muonTrack.globalIndex(); + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + uint64_t mchTrackIndex = mchTrack.globalIndex(); + + mGlobalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + + // check if a vector of global muon candidates is already available for the current MCH index + // if not, initialize a new one and add the current global muon track + // bool globalMuonTrackFound = false; + auto matchingCandidateIterator = collisionInfo.globalMuonTracks.find(mchTrackIndex); + if (matchingCandidateIterator != collisionInfo.globalMuonTracks.end()) { + matchingCandidateIterator->second.push_back(muonTrackIndex); + // globalMuonTrackFound = true; + } else { + collisionInfo.globalMuonTracks[mchTrackIndex].push_back(muonTrackIndex); + } + } + } + + // store the number of compatible collisions for this track in all the corresponding CollisionInfo objects + for (const auto& collisionId : muonTrack.compatibleCollIds()) { + try { + auto& collisionInfo = collisionInfos.at(collisionId); + collisionInfo.muonTracksCollMult[muonTrack.globalIndex()] = nCompatColl; + } catch (const std::exception& e) { + continue; + } + } + } + + // sort the vectors of matching candidates in ascending order based on the matching chi2 value + auto compareChi2 = [&muonTracks](uint64_t trackIndex1, uint64_t trackIndex2) -> bool { + auto const& track1 = muonTracks.rawIteratorAt(trackIndex1); + auto const& track2 = muonTracks.rawIteratorAt(trackIndex2); + + return (track1.chi2MatchMCHMFT() < track2.chi2MatchMCHMFT()); + }; + + for (auto& [collisionIndex, collisionInfo] : collisionInfos) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + for (auto& [mchIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareChi2); + } + } + + mMftTrackPars.clear(); + mMftTrackParsNew.clear(); + + // fill collision information for MFT standalone tracks + for (const auto& mftTrack : mftTracks) { + if (!mftTrack.has_collision()) { + continue; + } + + auto collision = collisions.rawIteratorAt(mftTrack.collisionId()); + uint64_t collisionIndex = collision.globalIndex(); + + uint64_t mftTrackIndex = mftTrack.globalIndex(); + + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.runNumber = collision.runNumber(); + collisionInfo.timestamp = collision.timestamp(); + collisionInfo.zVertex = collision.posZ(); + + collisionInfo.mftTracks.push_back(mftTrackIndex); + + // initialize the original MFT track parameters + auto mftTrackFwd = TrackToParCovFwd(mftTrack); + mMftTrackPars.try_emplace(mftTrackIndex, TrackParExt(mftTrackFwd, mftTrack.nClusters())); + + // initialize the corrected MFT track parameters, if requested + if (configMFTAlignmentCorrections.cfgEnableMFTAlignmentCorrections) { + TransformMFT(mftTrackFwd); + mMftTrackParsNew.try_emplace(mftTrackIndex, TrackParExt(mftTrackFwd, mftTrack.nClusters())); + } else { + // initialize the new MFT track parameters with the original ones, without corrections + mMftTrackParsNew.try_emplace(mftTrackIndex, TrackParExt(mftTrackFwd, mftTrack.nClusters())); + } + } + } + +#if (PROCESS_DERIVED_TABLES == 0) + template + void StoreCollisions(const std::unordered_map& collisionInfos, + COLL const& collisions, + TMUON const& muonTracks, + TCLS const& clusters, + TMFT const& /*mftTracks*/, + aod::FwdTrackAssoc const& fwdTrackAssocs) + { + int32_t remappedCollisionIndex = 0; + int32_t remappedFwdTrkIndex = 0; + int32_t remappedMftTrkIndex = 0; + + // remapping tables for collisions and tracks IDs + std::unordered_map collisionIdRemapping; + std::unordered_map muonTrackIdRemapping; + std::vector muonTrackIdInverseMapping; + + // define vector of vectors to store indices of compatible collisions per track + std::vector>> collsPerTrack; + + for (const auto& [collisionIndex, collisionInfo] : collisionInfos) { + auto const& c = collisions.rawIteratorAt(collisionIndex); + + if (collisionInfo.mchTracks.empty()) { + continue; + } + + collTable(collisionInfo.runNumber, + collisionInfo.globalBC, + collisionInfo.timestamp, + c.posX(), c.posY(), c.posZ(), + c.covXX(), c.covYY(), c.covZZ(), + c.collisionTime(), c.collisionTimeRes()); + collisionIdRemapping[collisionIndex] = remappedCollisionIndex; + + bool hasGoodOppositeSignMuonPair = false; + std::vector muonPairs; + getMuonPairs(collisionInfo, muonPairs); + for (auto [mchIndex1, mchIndex2] : muonPairs) { + + const auto& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); + const auto& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); + int sign1 = muonTrack1.sign(); + int sign2 = muonTrack2.sign(); + + // skip same-sign muon pairs + if ((sign1 * sign2) >= 0) { + continue; + } + + bool isGoodMuon1 = IsGoodMuon(muonTrack1, c, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon2 = IsGoodMuon(muonTrack2, c, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool goodMuonTracks = (isGoodMuon1 && isGoodMuon2); + if (!goodMuonTracks) { + continue; + } + + // get the pre-stored MCH track parameters + const auto mchTrackParIt1 = mMchTrackPars.find(mchIndex1); + if (mchTrackParIt1 == mMchTrackPars.end()) { + continue; + } + const auto mchTrackParIt2 = mMchTrackPars.find(mchIndex2); + if (mchTrackParIt2 == mMchTrackPars.end()) { + continue; + } + + // compute the track parameters at the vertex + const auto& mchTrackPar1 = mchTrackParIt1->second; + auto mchTrackPar1AtVertex = PropagateMCHToVertex(mchTrackPar1, c); + const auto& mchTrackPar2 = mchTrackParIt2->second; + auto mchTrackPar2AtVertex = PropagateMCHToVertex(mchTrackPar2, c); + + // skip opposite-sign muon pairs with invariant mass below the threshold + double mass = getMuMuInvariantMass(mchTrackPar1AtVertex, mchTrackPar2AtVertex); + if (mass < cfgDimuonMassLow) { + continue; + } + + hasGoodOppositeSignMuonPair = true; + break; + } + + // loop over MCH tracks + for (const auto& mchIndex : collisionInfo.mchTracks) { + auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); + + // skip tracks that have already been stored from another compatible collision + if (muonTrackIdRemapping.contains(mchIndex)) { + continue; + } + + // store all MCH tracks when the event contains a muon pair of opposite sign + if (!hasGoodOppositeSignMuonPair) { + // only store good MCH tracks + if (!IsGoodMuon(mchTrack, c, cfgTrackChi2MchUp, cfgMftMchResidualsPLow, cfgMftMchResidualsPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp)) { + continue; + } + } + + fwdTable(remappedCollisionIndex, + mchTrack.trackType(), + mchTrack.x(), mchTrack.y(), mchTrack.z(), + mchTrack.phi(), mchTrack.tgl(), mchTrack.signed1Pt(), + mchTrack.nClusters(), mchTrack.pDca(), mchTrack.rAtAbsorberEnd(), + mchTrack.chi2(), mchTrack.chi2MatchMCHMFT(), + mchTrack.matchMFTTrackId(), mchTrack.matchMCHTrackId()); + + if (cfgProduceMuonAlignmentTablesWithCovariances.value) { + fwdCovTable(mchTrack.sigmaX(), mchTrack.sigmaY(), mchTrack.sigmaPhi(), mchTrack.sigmaTgl(), mchTrack.sigma1Pt(), + mchTrack.rhoXY(), mchTrack.rhoPhiY(), mchTrack.rhoPhiX(), mchTrack.rhoTglX(), mchTrack.rhoTglY(), + mchTrack.rhoTglPhi(), mchTrack.rho1PtX(), mchTrack.rho1PtY(), mchTrack.rho1PtPhi(), mchTrack.rho1PtTgl()); + } + + // loop over attached clusters + auto clustersSliced = clusters.sliceBy(perMuon, mchTrack.globalIndex()); // Slice clusters by muon id + for (auto const& cluster : clustersSliced) { + clusTable(remappedFwdTrkIndex, cluster.x(), cluster.y(), cluster.z(), cluster.clInfo()); + } + + muonTrackIdRemapping[mchTrack.globalIndex()] = remappedFwdTrkIndex; + muonTrackIdInverseMapping.push_back(mchTrack.globalIndex()); + collsPerTrack.emplace_back(std::make_unique>()); + remappedFwdTrkIndex += 1; + } + + // loop over global muon tracks + for (const auto& [muonIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { + auto const& muonTrack = muonTracks.rawIteratorAt(globalTracksVector[0]); + const auto& mchTrack = muonTrack.template matchMCHTrack_as(); + const auto& mftTrack = muonTrack.template matchMFTTrack_as(); + + // skip tracks that have already been stored from another compatible collision + if (muonTrackIdRemapping.contains(muonTrack.globalIndex())) { + continue; + } + + try { + auto remappedMchId = muonTrackIdRemapping.at(mchTrack.globalIndex()); + + mftTable(remappedCollisionIndex, + mftTrack.x(), mftTrack.y(), mftTrack.z(), + mftTrack.phi(), mftTrack.tgl(), mftTrack.signed1Pt(), + mftTrack.mftClusterSizesAndTrackFlags(), mftTrack.chi2()); + + fwdTable(remappedCollisionIndex, + muonTrack.trackType(), + muonTrack.x(), muonTrack.y(), muonTrack.z(), + muonTrack.phi(), muonTrack.tgl(), muonTrack.signed1Pt(), + muonTrack.nClusters(), muonTrack.pDca(), muonTrack.rAtAbsorberEnd(), + muonTrack.chi2(), muonTrack.chi2MatchMCHMFT(), + remappedMftTrkIndex, remappedMchId); + + if (cfgProduceMuonAlignmentTablesWithCovariances.value) { + fwdCovTable(muonTrack.sigmaX(), muonTrack.sigmaY(), muonTrack.sigmaPhi(), muonTrack.sigmaTgl(), muonTrack.sigma1Pt(), + muonTrack.rhoXY(), muonTrack.rhoPhiY(), muonTrack.rhoPhiX(), muonTrack.rhoTglX(), muonTrack.rhoTglY(), + muonTrack.rhoTglPhi(), muonTrack.rho1PtX(), muonTrack.rho1PtY(), muonTrack.rho1PtPhi(), muonTrack.rho1PtTgl()); + } + + muonTrackIdRemapping[muonTrack.globalIndex()] = remappedFwdTrkIndex; + muonTrackIdInverseMapping.push_back(muonTrack.globalIndex()); + collsPerTrack.emplace_back(std::make_unique>()); + remappedFwdTrkIndex += 1; + remappedMftTrkIndex += 1; + } catch (const std::exception& e) { + continue; + } + } + + remappedCollisionIndex += 1; + } + + // store track-to-collision associations + for (const auto& assoc : fwdTrackAssocs) { + const auto collId = assoc.collisionId(); + const auto trackId = assoc.fwdtrackId(); + + try { + auto remappedCollId = collisionIdRemapping.at(collId); + auto remappedMchId = muonTrackIdRemapping.at(trackId); + fwdTrackAssoc(remappedCollId, remappedMchId); + } catch (const std::exception& e) { + continue; + } + } + + // store reverse track-to-collision associations + for (uint32_t remappedTrackId = 0; remappedTrackId < collsPerTrack.size(); remappedTrackId++) { + // step 1: fill vectors of remapped collision IDs for each of the tracks that is store in the derived tables + auto& remappedCollsVec = collsPerTrack[remappedTrackId]; + const auto trackId = muonTrackIdInverseMapping[remappedTrackId]; + const auto& muonTrack = muonTracks.rawIteratorAt(trackId); + for (const auto& collId : muonTrack.compatibleCollIds()) { + try { + auto remappedCollId = collisionIdRemapping.at(collId); + remappedCollsVec->push_back(remappedCollId); + } catch (const std::exception& e) { + continue; + } + } + + // step 2: output derived table + fwdTrkCompColls(*(remappedCollsVec.get())); + } + } +#endif + + template + void FillMftPlots(C const& collisions, + TMUON const& muonTracks, + TMFT const& mftTracks, + const std::unordered_map& collisionInfos) + { + // outer loop over collisions + for (const auto& [collisionIndex, collisionInfo] : collisionInfos) { + auto const& collision = collisions.rawIteratorAt(collisionIndex); registry.get(HIST("vertex_y_vs_x"))->Fill(collision.posX(), collision.posY()); registry.get(HIST("vertex_z"))->Fill(collision.posZ()); - if (fEnableVertexShiftAnalysis || fEnableMftDcaAnalysis) { + if (cfgEnableVertexShiftAnalysis || cfgEnableMftDcaAnalysis) { registry.get(HIST("DCA/MFT/nTracksMFT"))->Fill(collisionInfo.mftTracks.size()); } - if (fEnableVertexShiftAnalysis || fEnableMftDcaAnalysis) { + if (cfgEnableVertexShiftAnalysis || cfgEnableMftDcaAnalysis) { // loop over MFT tracks auto mftTrackIds = collisionInfo.mftTracks; - if (fMftTracksMultiplicityMax > 0 && mftTrackIds.size() > fMftTracksMultiplicityMax) { + if (cfgMftTracksMultiplicityMax > 0 && mftTrackIds.size() > cfgMftTracksMultiplicityMax) { auto rng = std::default_random_engine{}; std::shuffle(std::begin(mftTrackIds), std::end(mftTrackIds), rng); - mftTrackIds.resize(fMftTracksMultiplicityMax); + mftTrackIds.resize(cfgMftTracksMultiplicityMax); } - for (auto mftIndex : mftTrackIds) { + for (const auto& mftIndex : mftTrackIds) { auto const& mftTrack = mftTracks.rawIteratorAt(mftIndex); if (mftTrack.isCA()) { @@ -1434,29 +2541,37 @@ struct muonGlobalAlignment { } bool isGoodMFT = IsGoodMFT(mftTrack, 999.f, 5); - if (!isGoodMFT) + if (!isGoodMFT) { continue; + } + + // get the pre-stored MFT track parameters after corrections + // if MFT corrections are not enabled, the original MFT track parameters are retrieved + const auto mftTrackParIt = mMftTrackParsNew.find(mftIndex); + if (mftTrackParIt == mMftTrackParsNew.end()) { + continue; + } + const auto& mftTrackPar = mftTrackParIt->second; - auto mftTrackAtDCA = PropagateMFTToDCA(mftTrack, collision, fVertexZshift); + auto mftTrackAtDCA = PropagateMFTToDCA(mftTrackPar, collision, cfgVertexZshift); double dcax = mftTrackAtDCA.getX() - collision.posX(); double dcay = mftTrackAtDCA.getY() - collision.posY(); - double phi = mftTrack.phi() * 180 / TMath::Pi(); + double phi = mftTrack.phi() * o2::constants::math::Rad2Deg; int mftNclusters = mftTrack.nClusters(); double chi2NDF = static_cast(mftNclusters) * 2 - 5; const int nMftLayers = 10; - std::array firedLayers; + std::array firedLayers{false}; for (int layer = 0; layer < nMftLayers; layer++) { - if ((mftTrack.mftClusterSizesAndTrackFlags() >> (layer * 6)) & 0x3F) { + if (((mftTrack.mftClusterSizesAndTrackFlags() >> (layer * 6)) & 0x3F) != 0) { firedLayers[layer] = true; } else { firedLayers[layer] = false; } } - if (fEnableMftDcaAnalysis) { - const int nMftLayers = 10; - if (fEnableMftDcaExtraPlots) { + if (cfgEnableMftDcaAnalysis) { + if (cfgEnableMftDcaExtraPlots) { for (int i = 0; i < nMftLayers; i++) { if (firedLayers[i]) { registry.get(HIST("DCA/MFT/trackChi2"))->Fill(mftTrack.chi2() / chi2NDF, mftTrack.x(), mftTrack.y(), mftNclusters, i); @@ -1464,21 +2579,29 @@ struct muonGlobalAlignment { } } - if (mftTrack.chi2() <= fTrackChi2MftUp) { + if (mftTrack.chi2() <= cfgTrackChi2MftUp) { registry.get(HIST("DCA/MFT/DCA_y_vs_x"))->Fill(dcax, dcay); registry.get(HIST("DCA/MFT/DCA_x"))->Fill(dcax, collision.posZ(), mftTrack.x(), mftTrack.y(), mftNclusters); registry.get(HIST("DCA/MFT/DCA_y"))->Fill(dcay, collision.posZ(), mftTrack.x(), mftTrack.y(), mftNclusters); + if (!configHistograms.cfgHistMftDcaEnableDetailedVzAnalysis) { + registry.get(HIST("DCA/MFT/DCAxVsVz"))->Fill(dcax, collision.posZ(), mftTrack.x(), mftTrack.y(), mftNclusters); + registry.get(HIST("DCA/MFT/DCAyVsVz"))->Fill(dcay, collision.posZ(), mftTrack.x(), mftTrack.y(), mftNclusters); + } + +#if (PROCESS_DERIVED_TABLES == 0) if (cfgProduceMFTTable) { - mftTable(collision.posX(), collision.posY(), collision.posZ(), - mftTrack.signed1Pt(), mftTrack.tgl(), mftTrack.phi(), - dcax, dcay, mftTrackAtDCA.getSigma2X(), mftTrackAtDCA.getSigma2Y(), mftTrackAtDCA.getSigmaXY(), - mftNclusters, mftTrack.chi2(), - mftTrack.x(), mftTrack.y(), mftTrack.z()); + compactMftTable(collision.posX(), collision.posY(), collision.posZ(), + mftTrack.signed1Pt(), mftTrack.tgl(), mftTrack.phi(), + dcax, dcay, mftTrackAtDCA.getSigma2X(), mftTrackAtDCA.getSigma2Y(), mftTrackAtDCA.getSigmaXY(), + mftNclusters, mftTrack.chi2(), + mftTrack.x(), mftTrack.y(), mftTrack.z()); } +#endif - if (fEnableMftDcaExtraPlots) { - if (mftNclusters >= 6) { + if (cfgEnableMftDcaExtraPlots) { + static constexpr int nMftClustersMin = 6; + if (mftNclusters >= nMftClustersMin) { for (int i = 0; i < nMftLayers; i++) { auto mftTrackAtLayer = PropagateMFT(mftTrack, o2::mft::constants::mft::LayerZCoordinate()[i]); std::get>(mMftTrackEffDen[i])->Fill(mftTrackAtLayer.getX(), mftTrackAtLayer.getY()); @@ -1497,13 +2620,15 @@ struct muonGlobalAlignment { } } - if (fEnableVertexShiftAnalysis) { - if (mftTrack.chi2() <= fTrackChi2MftUp && std::fabs(collision.posZ()) < 1.f && mftNclusters >= 6) { - float zshift[21] = {// in millimeters - -5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, - 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0}; - for (int zi = 0; zi < 21; zi++) { - auto mftTrackAtDCAshifted = PropagateMFTToDCA(mftTrack, collision, zshift[zi] / 10.f); + if (cfgEnableVertexShiftAnalysis) { + static constexpr int nMftClustersMin = 6; + if (mftTrack.chi2() <= cfgTrackChi2MftUp && std::fabs(collision.posZ()) < 1.f && mftNclusters >= nMftClustersMin) { + static constexpr int nPoints = 21; + const std::array zshift{// in millimeters + -5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, + 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0}; + for (int zi = 0; zi < nPoints; zi++) { + auto mftTrackAtDCAshifted = PropagateMFTToDCA(mftTrackPar, collision, zshift[zi] / 10.f); double dcaxShifted = mftTrackAtDCAshifted.getX() - collision.posX(); double dcayShifted = mftTrackAtDCAshifted.getY() - collision.posY(); registry.get(HIST("DCA/MFT/DCA_x_vs_phi_vs_zshift"))->Fill(zshift[zi], phi, dcaxShifted); @@ -1522,14 +2647,17 @@ struct muonGlobalAlignment { } } - if (fEnableGlobalFwdDcaAnalysis) { + if (cfgEnableGlobalFwdDcaAnalysis) { // loop over global muon tracks - for (auto& [muonIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { + for (const auto& [muonIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { auto const& muonTrack = muonTracks.rawIteratorAt(globalTracksVector[0]); - const auto& mchTrack = muonTrack.template matchMCHTrack_as(); - const auto& mftTrack = muonTrack.template matchMFTTrack_as(); + const auto& mchTrack = muonTrack.template matchMCHTrack_as(); + const auto& mftTrack = muonTrack.template matchMFTTrack_as(); + + auto mchIndex = mchTrack.globalIndex(); + auto mftIndex = mftTrack.globalIndex(); - if (muonTrack.chi2MatchMCHMFT() < 50) { + if (muonTrack.chi2MatchMCHMFT() < cfgMftDcaMatchChi2Up.value) { continue; } @@ -1537,7 +2665,7 @@ struct muonGlobalAlignment { auto const& muonTrack2 = muonTracks.rawIteratorAt(globalTracksVector[1]); double dchi2 = muonTrack2.chi2MatchMCHMFT() - muonTrack.chi2MatchMCHMFT(); - if (dchi2 < 50) { + if (dchi2 < cfgMftDcaMatchChi2Up.value) { continue; } } @@ -1546,21 +2674,37 @@ struct muonGlobalAlignment { continue; } - bool isGoodMFT = IsGoodMFT(mftTrack, fTrackChi2MftUp, 5); + bool isGoodMFT = IsGoodMFT(mftTrack, cfgTrackChi2MftUp, 5); if (!isGoodMFT) { continue; } - bool isGoodMuon = IsGoodMuon(mchTrack, collision, fTrackChi2MchUp, 0.f, fPtMchLow, {fEtaMftLow, fEtaMftUp}, {fRabsLow, fRabsUp}, fSigmaPdcaUp); - if (!isGoodMuon) + bool isGoodMuon = IsGoodMuon(mchTrack, collision, cfgTrackChi2MchUp, cfgMftMchResidualsPLow, cfgMftMchResidualsPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + if (!isGoodMuon) { continue; + } + + // get the pre-stored MFT track parameters after corrections + // if MFT corrections are not enabled, the original MFT track parameters are retrieved + const auto mftTrackParIt = mMftTrackParsNew.find(mftIndex); + if (mftTrackParIt == mMftTrackParsNew.end()) { + continue; + } + const auto& mftTrackPar = mftTrackParIt->second; - auto mftTrackAtDCA = PropagateMFTToDCA(mftTrack, mchTrack, collision, fVertexZshift); + // get the pre-stored MCH track parameters + const auto mchTrackParIt = mMchTrackPars.find(mchIndex); + if (mchTrackParIt == mMchTrackPars.end()) { + continue; + } + const auto& mchTrackPar = mchTrackParIt->second; + + auto mftTrackAtDCA = PropagateMFTToDCA(mftTrackPar, mchTrackPar, collision, cfgVertexZshift); double dcax = mftTrackAtDCA.getX() - collision.posX(); double dcay = mftTrackAtDCA.getY() - collision.posY(); int mftNclusters = mftTrack.nClusters(); - if (mftTrack.chi2() <= fTrackChi2MftUp) { + if (mftTrack.chi2() <= cfgTrackChi2MftUp) { registry.get(HIST("DCA/GlobalFwd/DCA_x"))->Fill(dcax, collision.posZ(), mftTrack.x(), mftTrack.y(), mftNclusters); registry.get(HIST("DCA/GlobalFwd/DCA_y"))->Fill(dcay, collision.posZ(), mftTrack.x(), mftTrack.y(), mftNclusters); } @@ -1569,141 +2713,98 @@ struct muonGlobalAlignment { } } - template - bool MchRealignTrack(const TMUON& mchTrack, const TCLUS& clusters, TrackRealigned& convertedTrack, bool applyCorrections) + template + void FillMchPlots(C const& collisions, + TMUON const& muonTracks, + TCLS const& clusters, + TMFT const& /*mftTracks*/, + const std::unordered_map& collisionInfos) { - // loop over attached clusters - int clIndex = -1; - auto clustersSliced = clusters.sliceBy(perMuon, mchTrack.globalIndex()); // Slice clusters by muon id - for (auto const& cluster : clustersSliced) { - clIndex += 1; - - int deId = cluster.deId(); - int chamber = deId / 100 - 1; - if (chamber < 0 || chamber > 9) { - continue; - } - - math_utils::Point3D local; - math_utils::Point3D master; - - master.SetXYZ(cluster.x(), cluster.y(), cluster.z()); - - if (configRealign.fEnableMCHRealign) { - // Transformation from reference geometry frame to new geometry frame - transformRef[cluster.deId()].MasterToLocal(master, local); - transformNew[cluster.deId()].LocalToMaster(local, master); - } - - if (applyCorrections) { - auto correctionsIt = mMchAlignmentCorrections.find(cluster.deId()); - if (correctionsIt != mMchAlignmentCorrections.end()) { - const auto& corrections = correctionsIt->second; - master.SetX(master.x() + corrections.x); - master.SetY(master.y() + corrections.y); - master.SetZ(master.z() + corrections.z); - } - } - - // realigned MCH cluster - mch::Cluster* clusterMCH = new mch::Cluster(); - clusterMCH->x = master.x(); - clusterMCH->y = master.y(); - clusterMCH->z = master.z(); - - uint32_t ClUId = mch::Cluster::buildUniqueId(static_cast(cluster.deId() / 100) - 1, cluster.deId(), clIndex); - clusterMCH->uid = ClUId; - clusterMCH->ex = cluster.isGoodX() ? 0.2 : 10.0; - clusterMCH->ey = cluster.isGoodY() ? 0.2 : 10.0; - - // Add transformed cluster into temporary variable - convertedTrack.createParamAtCluster(*clusterMCH); - } - - bool removable{false}; - // Refit the re-aligned track - if (convertedTrack.getNClusters() != 0) { - removable = RemoveTrack(convertedTrack); - } else { - LOGF(fatal, "Muon track %d has no associated clusters.", mchTrack.globalIndex()); - } - - return !removable; - } - - void FillResidualsPlots(MyEvents const& collisions, - MyBCs const& bcs, - MyMuonsWithCov const& muonTracks, - aod::FwdTrkCls const& clusters, - const std::map& collisionInfos) - { - if (!fEnableMftMchResidualsAnalysis && !fEnableMftMchMatchingAnalysis) { + if (!cfgEnableMftMchResidualsAnalysis && !cfgEnableMftMchMatchingAnalysis) { return; } // loop over collisions - for (auto& [collisionIndex, collisionInfo] : collisionInfos) { + for (const auto& [collisionIndex, collisionInfo] : collisionInfos) { auto const& collision = collisions.rawIteratorAt(collisionIndex); - const auto& bc = bcs.rawIteratorAt(collision.bcId()); - - // remove TF/ROF borders and ambiguous collisions - if (!bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) || - !bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - continue; // loop over global muon tracks - for (auto& [muonIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { + for (const auto& [muonIndex, globalTracksVector] : collisionInfo.globalMuonTracks) { auto const& muonTrack = muonTracks.rawIteratorAt(globalTracksVector[0]); - const auto& mchTrack = muonTrack.template matchMCHTrack_as(); - const auto& mftTrack = muonTrack.template matchMFTTrack_as(); - int quadrantMch = GetQuadrant(mchTrack); + const auto& mchTrack = muonTrack.template matchMCHTrack_as(); + const auto& mftTrack = muonTrack.template matchMFTTrack_as(); + // int quadrant = GetQuadrant(mchTrack); + int quadrant = GetQuadrant(mftTrack); int posNeg = (mchTrack.sign() >= 0) ? 0 : 1; - bool isGoodMuon = IsGoodMuon(mchTrack, collision, fTrackChi2MchUp, fMftMchResidualsPLow, fMftMchResidualsPtLow, {fEtaMftLow, fEtaMftUp}, {fRabsLow, fRabsUp}, fSigmaPdcaUp); - if (!isGoodMuon) + auto mchIndex = mchTrack.globalIndex(); + auto mftIndex = mftTrack.globalIndex(); + + bool isGoodMuon = IsGoodMuon(mchTrack, collision, cfgTrackChi2MchUp, cfgMftMchResidualsPLow, cfgMftMchResidualsPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + if (!isGoodMuon) { continue; + } - bool isGoodMFT = IsGoodMFT(mftTrack, fTrackChi2MftUp, fTrackNClustMftLow); - if (!isGoodMFT) + bool isGoodMFT = IsGoodMFT(mftTrack, cfgTrackChi2MftUp, cfgTrackNClustMftLow); + if (!isGoodMFT) { continue; + } - // refit MCH track if enabled - TrackRealigned convertedTrack; - bool convertedTrackOk = false; - if (configRealign.fEnableMCHRealign) { - convertedTrackOk = MchRealignTrack(mchTrack, clusters, convertedTrack, false); + // get the pre-stored MFT track parameters + const auto mftTrackParIt = mMftTrackPars.find(mftIndex); + if (mftTrackParIt == mMftTrackPars.end()) { + continue; } + const auto& mftTrackPar = mftTrackParIt->second; - // apply alignment corrections if available - TrackRealigned convertedTrackWithCorr; - bool convertedTrackWithCorrOk = false; - if (!mMchAlignmentCorrections.empty()) { - convertedTrackWithCorrOk = MchRealignTrack(mchTrack, clusters, convertedTrackWithCorr, true); + // get the pre-stored MFT track parameters after corrections + // if MFT corrections are not enabled, the original MFT track parameters are retrieved + const auto mftTrackParNewIt = mMftTrackParsNew.find(mftIndex); + if (mftTrackParNewIt == mMftTrackParsNew.end()) { + continue; } + const auto& mftTrackParNew = mftTrackParNewIt->second; - if (fEnableMftMchResidualsAnalysis) { + // get the pre-stored MCH track parameters + const auto mchTrackParIt = mMchTrackPars.find(mchIndex); + if (mchTrackParIt == mMchTrackPars.end()) { + continue; + } + const auto& mchTrackPar = mchTrackParIt->second; + + // get the pre-stored MCH track parameters after refit + const auto mchTrackParNewIt = mMchTrackParsNew.find(mchIndex); + if (mchTrackParNewIt == mMchTrackParsNew.end()) { + continue; + } + const auto& mchTrackParNew = mchTrackParNewIt->second; + + double matchChi2 = muonTrack.chi2MatchMCHMFT(); + + // Residuals analysis between MFT tracks and MCH clusters + if (cfgEnableMftMchResidualsAnalysis && (matchChi2 <= cfgMftMchResidualsMatchChi2Up.value)) { // loop over attached clusters auto clustersSliced = clusters.sliceBy(perMuon, mchTrack.globalIndex()); // Slice clusters by muon id for (auto const& cluster : clustersSliced) { int deId = cluster.deId(); int chamber = deId / 100 - 1; - if (chamber < 0 || chamber > 9) + if (chamber < 0 || chamber >= NMchChambers) { continue; + } int deIndex = getDEindex(deId); math_utils::Point3D local; - math_utils::Point3D master; - math_utils::Point3D masterWithCorr; + math_utils::Point3D master; // original cluster position + math_utils::Point3D masterRealign; // cluster position after realignment master.SetXYZ(cluster.x(), cluster.y(), cluster.z()); - masterWithCorr.SetXYZ(cluster.x(), cluster.y(), cluster.z()); + masterRealign.SetXYZ(cluster.x(), cluster.y(), cluster.z()); // apply realignment to MCH cluster - if (configRealign.fEnableMCHRealign) { + if (configRealign.cfgEnableMCHRealign) { // Transformation from reference geometry frame to new geometry frame transformRef[cluster.deId()].MasterToLocal(master, local); - transformNew[cluster.deId()].LocalToMaster(local, master); - transformNew[cluster.deId()].LocalToMaster(local, masterWithCorr); + transformNew[cluster.deId()].LocalToMaster(local, masterRealign); } // apply alignment corrections to MCH cluster (if available) @@ -1711,121 +2812,388 @@ struct muonGlobalAlignment { auto correctionsIt = mMchAlignmentCorrections.find(cluster.deId()); if (correctionsIt != mMchAlignmentCorrections.end()) { const auto& corrections = correctionsIt->second; - masterWithCorr.SetX(masterWithCorr.x() + corrections.x); - masterWithCorr.SetY(masterWithCorr.y() + corrections.y); - masterWithCorr.SetZ(masterWithCorr.z() + corrections.z); + masterRealign.SetX(masterRealign.x() + corrections.x); + masterRealign.SetY(masterRealign.y() + corrections.y); + masterRealign.SetZ(masterRealign.z() + corrections.z); } } - // MFT-MCH residuals (MCH cluster is realigned if enabled) - // if the realignment is enabled and successful, the MFT track is extrpolated - // by taking the momentum from the MCH track refitted with the new alignment - if (!configRealign.fEnableMCHRealign || convertedTrackOk) { - auto mftTrackAtCluster = configRealign.fEnableMCHRealign ? PropagateMFTtoMCH(mftTrack, mch::TrackParam(convertedTrack.first()), master.z()) : PropagateMFTtoMCH(mftTrack, FwdtoMCH(FwdToTrackPar(mchTrack)), master.z()); + // MFT-MCH residuals from original alignment + const auto mftTrackAtCluster = PropagateMFTtoMCH(mftTrackPar, FwdtoMCH(mchTrackPar), master.z()); + const auto mftTrackParamAtCluster = FwdtoMCH(mftTrackAtCluster); - std::array xPos{master.x(), mftTrackAtCluster.getX()}; - std::array yPos{master.y(), mftTrackAtCluster.getY()}; + const std::array xPos{master.x(), mftTrackAtCluster.getX()}; + const std::array yPos{master.y(), mftTrackAtCluster.getY()}; - registry.get(HIST("residuals/dx_vs_chamber"))->Fill(chamber + 1, quadrantMch, posNeg, xPos[0] - xPos[1]); - registry.get(HIST("residuals/dy_vs_chamber"))->Fill(chamber + 1, quadrantMch, posNeg, yPos[0] - yPos[1]); + registry.get(HIST("residuals/dx_vs_chamber"))->Fill(chamber + 1, quadrant, posNeg, xPos[0] - xPos[1]); + registry.get(HIST("residuals/dy_vs_chamber"))->Fill(chamber + 1, quadrant, posNeg, yPos[0] - yPos[1]); - registry.get(HIST("residuals/dx_vs_de"))->Fill(xPos[0] - xPos[1], deIndex, quadrantMch, posNeg, mchTrack.p()); - registry.get(HIST("residuals/dy_vs_de"))->Fill(yPos[0] - yPos[1], deIndex, quadrantMch, posNeg, mchTrack.p()); - } + registry.get(HIST("residuals/dx_vs_de"))->Fill(xPos[0] - xPos[1], deIndex, quadrant, posNeg, mchTrack.p(), mftTrackParamAtCluster.getNonBendingSlope()); + registry.get(HIST("residuals/dy_vs_de"))->Fill(yPos[0] - yPos[1], deIndex, quadrant, posNeg, mchTrack.p(), mftTrackParamAtCluster.getBendingSlope()); // MFT-MCH residuals with realigned and/or corrected MCH clusters // if the alignment corrections are available and the refitting is successful, the MFT track is extrpolated // by taking the momentum from the MCH track refitted with the alignment corrections and the new // alignment (if realignment is enabled) - if (convertedTrackWithCorrOk) { - auto mftTrackAtClusterWithCorr = PropagateMFTtoMCH(mftTrack, mch::TrackParam(convertedTrackWithCorr.first()), masterWithCorr.z()); + if (!mchTrackParNew.isRemovable()) { + const auto mftTrackAtClusterWithCorr = PropagateMFTtoMCH(mftTrackParNew, FwdtoMCH(mchTrackParNew), masterRealign.z()); + const auto mftTrackParamAtClusterWithCorr = FwdtoMCH(mftTrackAtClusterWithCorr); - std::array xPos{masterWithCorr.x(), mftTrackAtClusterWithCorr.getX()}; - std::array yPos{masterWithCorr.y(), mftTrackAtClusterWithCorr.getY()}; + const std::array xPosWithCorr{masterRealign.x(), mftTrackAtClusterWithCorr.getX()}; + const std::array yPosWithCorr{masterRealign.y(), mftTrackAtClusterWithCorr.getY()}; - registry.get(HIST("residuals/dx_vs_chamber_corr"))->Fill(chamber + 1, quadrantMch, posNeg, xPos[0] - xPos[1]); - registry.get(HIST("residuals/dy_vs_chamber_corr"))->Fill(chamber + 1, quadrantMch, posNeg, yPos[0] - yPos[1]); + registry.get(HIST("residuals/dx_vs_chamber_corr"))->Fill(chamber + 1, quadrant, posNeg, xPosWithCorr[0] - xPosWithCorr[1]); + registry.get(HIST("residuals/dy_vs_chamber_corr"))->Fill(chamber + 1, quadrant, posNeg, yPosWithCorr[0] - yPosWithCorr[1]); - registry.get(HIST("residuals/dx_vs_de_corr"))->Fill(xPos[0] - xPos[1], deIndex, quadrantMch, posNeg, mchTrack.p()); - registry.get(HIST("residuals/dy_vs_de_corr"))->Fill(yPos[0] - yPos[1], deIndex, quadrantMch, posNeg, mchTrack.p()); + registry.get(HIST("residuals/dx_vs_de_corr"))->Fill(xPosWithCorr[0] - xPosWithCorr[1], deIndex, quadrant, posNeg, mchTrack.p(), mftTrackParamAtClusterWithCorr.getNonBendingSlope()); + registry.get(HIST("residuals/dy_vs_de_corr"))->Fill(yPosWithCorr[0] - yPosWithCorr[1], deIndex, quadrant, posNeg, mchTrack.p(), mftTrackParamAtClusterWithCorr.getBendingSlope()); } } - if (!configRealign.fEnableMCHRealign || convertedTrackOk) { - auto mchTrackAtDCA = configRealign.fEnableMCHRealign ? PropagateMCHRealigned(convertedTrack, collision.posZ()) : PropagateMCH(mchTrack, collision.posZ()); - auto dcax = mchTrackAtDCA.getX() - collision.posX(); - auto dcay = mchTrackAtDCA.getY() - collision.posY(); - - registry.get(HIST("DCA/MCH/DCA_y_vs_x"))->Fill(dcax, dcay); - registry.get(HIST("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_mom"))->Fill(mchTrack.p(), quadrantMch, posNeg, dcax); - registry.get(HIST("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_mom"))->Fill(mchTrack.p(), quadrantMch, posNeg, dcay); - - if (fEnableMftMchResidualsExtraPlots) { - registry.get(HIST("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_vz"))->Fill(collision.posZ(), quadrantMch, posNeg, dcax); - registry.get(HIST("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_vz"))->Fill(collision.posZ(), quadrantMch, posNeg, dcay); - auto mchTrackAtMFT = configRealign.fEnableMCHRealign ? PropagateMCHRealigned(convertedTrack, mftTrack.z()) : PropagateMCH(mchTrack, mftTrack.z()); - double deltaPhi = mchTrackAtMFT.getPhi() - mftTrack.phi(); - registry.get(HIST("residuals/dphi_at_mft"))->Fill(deltaPhi, mftTrack.x(), mftTrack.y(), posNeg, mchTrackAtMFT.getP()); - } + const auto mchTrackAtDCA = PropagateMCHParam(FwdtoMCH(mchTrackPar), collision.posZ()); + const auto dcax = mchTrackAtDCA.getX() - collision.posX(); + const auto dcay = mchTrackAtDCA.getY() - collision.posY(); + + registry.get(HIST("DCA/MCH/DCA_y_vs_x"))->Fill(dcax, dcay); + registry.get(HIST("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_mom"))->Fill(mchTrackPar.getP(), quadrant, posNeg, dcax); + registry.get(HIST("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_mom"))->Fill(mchTrackPar.getP(), quadrant, posNeg, dcay); + + if (cfgEnableMftMchResidualsExtraPlots) { + registry.get(HIST("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_vz"))->Fill(collision.posZ(), quadrant, posNeg, dcax); + registry.get(HIST("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_vz"))->Fill(collision.posZ(), quadrant, posNeg, dcay); + const auto mchTrackAtMFT = PropagateMCHParam(FwdtoMCH(mchTrackPar), mftTrack.z()); + const double deltaPhi = mchTrackAtMFT.getPhi() - mftTrack.phi(); + registry.get(HIST("residuals/dphi_at_mft"))->Fill(deltaPhi, mftTrack.x(), mftTrack.y(), posNeg, mchTrackAtMFT.getP()); } - if (convertedTrackWithCorrOk) { - auto mchTrackAtDCA = PropagateMCHRealigned(convertedTrackWithCorr, collision.posZ()); - auto dcax = mchTrackAtDCA.getX() - collision.posX(); - auto dcay = mchTrackAtDCA.getY() - collision.posY(); + if (!mchTrackParNew.isRemovable()) { + const auto mchTrackAtDCAWithCorr = PropagateMCHParam(FwdtoMCH(mchTrackParNew), collision.posZ()); + const auto dcaxWithCorr = mchTrackAtDCAWithCorr.getX() - collision.posX(); + const auto dcayWithCorr = mchTrackAtDCAWithCorr.getY() - collision.posY(); - registry.get(HIST("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_mom_corr"))->Fill(mchTrack.p(), quadrantMch, posNeg, dcax); - registry.get(HIST("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_mom_corr"))->Fill(mchTrack.p(), quadrantMch, posNeg, dcay); + registry.get(HIST("DCA/MCH/DCA_x_vs_sign_vs_quadrant_vs_mom_corr"))->Fill(mchTrackParNew.getP(), quadrant, posNeg, dcaxWithCorr); + registry.get(HIST("DCA/MCH/DCA_y_vs_sign_vs_quadrant_vs_mom_corr"))->Fill(mchTrackParNew.getP(), quadrant, posNeg, dcayWithCorr); } } // MFT-MCH track residuals analysis - if (fEnableMftMchMatchingAnalysis && convertedTrackWithCorrOk) { - double refPlaneZ[2] = {fRefPlaneZMFT, fRefPlaneZMCH}; - - std::shared_ptr dxPlots[2]{registry.get(HIST("matching/dxAtMFT")), registry.get(HIST("matching/dxAtMCH"))}; - std::shared_ptr dyPlots[2]{registry.get(HIST("matching/dyAtMFT")), registry.get(HIST("matching/dyAtMCH"))}; - std::shared_ptr dsxPlots[2]{registry.get(HIST("matching/dsxAtMFT")), registry.get(HIST("matching/dsxAtMCH"))}; - std::shared_ptr dsyPlots[2]{registry.get(HIST("matching/dsyAtMFT")), registry.get(HIST("matching/dsyAtMCH"))}; - std::shared_ptr dphiPlots[2]{registry.get(HIST("matching/dphiAtMFT")), registry.get(HIST("matching/dphiAtMCH"))}; - - for (int iRefPlane = 0; iRefPlane < 2; iRefPlane++) { - const auto mftTrackAtRefPlane = configRealign.fEnableMCHRealign ? PropagateMFTtoMCH(mftTrack, mch::TrackParam(convertedTrackWithCorr.first()), refPlaneZ[iRefPlane]) : PropagateMFTtoMCH(mftTrack, FwdtoMCH(FwdToTrackPar(mchTrack)), refPlaneZ[iRefPlane]); - const auto mchTrackAtRefPlane = configRealign.fEnableMCHRealign ? PropagateMCHRealigned(convertedTrackWithCorr, refPlaneZ[iRefPlane]) : PropagateMCH(mchTrack, refPlaneZ[iRefPlane]); + if (cfgEnableMftMchMatchingAnalysis && !mchTrackParNew.isRemovable() && (matchChi2 <= cfgMftMchResidualsMatchChi2Up.value)) { + static constexpr int nRefPlanes = 2; + const std::array refPlaneZ{cfgRefPlaneZMFT, cfgRefPlaneZMCH}; + + std::array, 2> dxPlots{registry.get(HIST("matching/dxAtMFT")), registry.get(HIST("matching/dxAtMCH"))}; + std::array, 2> dyPlots{registry.get(HIST("matching/dyAtMFT")), registry.get(HIST("matching/dyAtMCH"))}; + std::array, 2> dsxPlots{registry.get(HIST("matching/dsxAtMFT")), registry.get(HIST("matching/dsxAtMCH"))}; + std::array, 2> dsyPlots{registry.get(HIST("matching/dsyAtMFT")), registry.get(HIST("matching/dsyAtMCH"))}; + std::array, 2> dphiPlots{registry.get(HIST("matching/dphiAtMFT")), registry.get(HIST("matching/dphiAtMCH"))}; + + for (int iRefPlane = 0; iRefPlane < nRefPlanes; iRefPlane++) { + const auto mftTrackAtRefPlane = PropagateMFTtoMCH(mftTrackParNew, FwdtoMCH(mchTrackParNew), refPlaneZ[iRefPlane]); + const auto mchTrackAtRefPlane = PropagateMCHParam(FwdtoMCH(mchTrackParNew), refPlaneZ[iRefPlane]); const auto& refTrackAtRefPlane = (iRefPlane == 0) ? mftTrackAtRefPlane : mchTrackAtRefPlane; auto dx = mchTrackAtRefPlane.getX() - mftTrackAtRefPlane.getX(); - dxPlots[iRefPlane]->Fill(dx, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrantMch, posNeg, mchTrack.p()); + dxPlots[iRefPlane]->Fill(dx, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrant, posNeg, mchTrackParNew.getP()); auto dy = mchTrackAtRefPlane.getY() - mftTrackAtRefPlane.getY(); - dyPlots[iRefPlane]->Fill(dy, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrantMch, posNeg, mchTrack.p()); + dyPlots[iRefPlane]->Fill(dy, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrant, posNeg, mchTrackParNew.getP()); - auto mftParamAtRefPlane = FwdtoMCH(mftTrackAtRefPlane); - auto mchParamAtRefPlane = FwdtoMCH(mchTrackAtRefPlane); + const auto mftParamAtRefPlane = FwdtoMCH(mftTrackAtRefPlane); + const auto mchParamAtRefPlane = FwdtoMCH(mchTrackAtRefPlane); auto dsx = mchParamAtRefPlane.getNonBendingSlope() - mftParamAtRefPlane.getNonBendingSlope(); - dsxPlots[iRefPlane]->Fill(dsx, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrantMch, posNeg, mchTrack.p()); + dsxPlots[iRefPlane]->Fill(dsx, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrant, posNeg, mchTrackParNew.getP()); auto dsy = mchParamAtRefPlane.getBendingSlope() - mftParamAtRefPlane.getBendingSlope(); - dsyPlots[iRefPlane]->Fill(dsy, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrantMch, posNeg, mchTrack.p()); + dsyPlots[iRefPlane]->Fill(dsy, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrant, posNeg, mchTrackParNew.getP()); - auto dphi = mchTrackAtRefPlane.getPhi() - mftTrackAtRefPlane.getPhi(); - if (dphi < -TMath::Pi()) { - dphi += TMath::Pi() * 2.0; - } else if (dphi > TMath::Pi()) { - dphi -= TMath::Pi() * 2.0; - } - dphiPlots[iRefPlane]->Fill(dphi, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrantMch, posNeg, mchTrack.p()); + auto dphi = RecoDecay::constrainAngle(mchTrackAtRefPlane.getPhi() - mftTrackAtRefPlane.getPhi(), -o2::constants::math::PI); + dphiPlots[iRefPlane]->Fill(dphi, refTrackAtRefPlane.getX(), refTrackAtRefPlane.getY(), quadrant, posNeg, mchTrackParNew.getP()); } } } } } + template + inline void fillDimuonInvmassPlot(const T1& trackPar1, + const T2& trackPar2, + const T3& trackPar1AtVertex, + const T4& trackPar2AtVertex, + HistConfigType histConfig) + { + auto mumu4mom = getMuMu4Momentum(trackPar1AtVertex, trackPar2AtVertex); + double p = mumu4mom.P(); + double pT = mumu4mom.Pt(); + double mass = mumu4mom.M(); + int quadrant1 = GetQuadrant(static_cast(std::atan2(trackPar1.getY(), trackPar1.getX()))); + int quadrant2 = GetQuadrant(static_cast(std::atan2(trackPar2.getY(), trackPar2.getX()))); + + registry.get(histConfig)->Fill(mass, p, pT, quadrant1, quadrant2); + } + + template + inline void fillDimuonDcaPlots(const T1& trackPar1, + const T2& trackPar2, + const T3& trackPar1AtVertex, + const T4& trackPar2AtVertex, + const T5& trackPar1AtDca, + const T6& trackPar2AtDca, + HistConfigType1 histConfigX, + HistConfigType2 histConfigY) + { + auto mumu4mom = getMuMu4Momentum(trackPar1AtVertex, trackPar2AtVertex); + double p = mumu4mom.P(); + double pT = mumu4mom.Pt(); + double dcax = trackPar1AtDca.getX() - trackPar2AtDca.getX(); + double dcay = trackPar1AtDca.getY() - trackPar2AtDca.getY(); + int quadrant1 = GetQuadrant(static_cast(std::atan2(trackPar1.getY(), trackPar1.getX()))); + int quadrant2 = GetQuadrant(static_cast(std::atan2(trackPar2.getY(), trackPar2.getX()))); + + registry.get(histConfigX)->Fill(dcax, p, pT, quadrant1, quadrant2); + registry.get(histConfigY)->Fill(dcay, p, pT, quadrant1, quadrant2); + } + + template + inline void fillDimuonAnglePlot(const T1& trackPar1, + const T2& trackPar2, + const T3& trackPar1AtVertex, + const T4& trackPar2AtVertex, + double mchAngle, + double fwdAngle, + HistConfigType histConfig) + { + auto mumu4mom = getMuMu4Momentum(trackPar1AtVertex, trackPar2AtVertex); + double p = mumu4mom.P(); + double pT = mumu4mom.Pt(); + double dAngle = mchAngle - fwdAngle; + double invMass = mumu4mom.M(); + int quadrant1 = GetQuadrant(static_cast(std::atan2(trackPar1.getY(), trackPar1.getX()))); + int quadrant2 = GetQuadrant(static_cast(std::atan2(trackPar2.getY(), trackPar2.getX()))); + + registry.get(histConfig)->Fill(dAngle, invMass, p, pT, quadrant1, quadrant2); + } + + template + inline void fillDimuonDecayLengthPlots(const T1& trackPar1, + const T2& trackPar2, + const T3& trackPar1AtVertex, + const T4& trackPar2AtVertex, + const C& collision, + HistConfigType1 histConfigLxy, + HistConfigType2 histConfigLz, + HistConfigType3 histConfigTauxy, + HistConfigType4 histConfigTauz) + { + auto decayLength = getMuMuDecayLength(trackPar1, trackPar2, collision); + if (!decayLength) { + return; + } + + auto v12 = getMuMu4Momentum(trackPar1AtVertex, trackPar2AtVertex); + double p = v12.P(); + double pT = v12.Pt(); + double invMass = v12.M(); + int quadrant1 = GetQuadrant(static_cast(std::atan2(trackPar1.getY(), trackPar1.getX()))); + int quadrant2 = GetQuadrant(static_cast(std::atan2(trackPar2.getY(), trackPar2.getX()))); + + registry.get(histConfigLxy)->Fill(decayLength->first, invMass, p, pT, quadrant1, quadrant2); + registry.get(histConfigLz)->Fill(decayLength->second, invMass, p, pT, quadrant1, quadrant2); + + double tauxy = 299792458.e-7 * 10. * decayLength->first * v12.M() / (v12.Pt() * o2::constants::physics::LightSpeedCm2NS); + double tauz = 299792458.e-7 * 10. * decayLength->second * invMass / (TMath::Abs(v12.Pz()) * o2::constants::physics::LightSpeedCm2NS); + + registry.get(histConfigTauxy)->Fill(tauxy, invMass, p, pT, quadrant1, quadrant2); + registry.get(histConfigTauz)->Fill(tauz, invMass, p, pT, quadrant1, quadrant2); + } + + template + void FillDimuonPlots(C const& collisions, + TMUON const& muonTracks, + const std::unordered_map& collisionInfos) + { + if (!cfgEnableDimuonAnalysis) { + return; + } + + for (const auto& [collisionIndex, collisionInfo] : collisionInfos) { + auto const& collision = collisions.rawIteratorAt(collisionIndex); + + std::vector muonPairs; + getMuonPairs(collisionInfo, muonPairs); + + for (auto [mchIndex1, mchIndex2] : muonPairs) { // o2-linter: disable=const-ref-in-for-loop (values are modified in loop) + + int sign1 = muonTracks.rawIteratorAt(mchIndex1).sign(); + int sign2 = muonTracks.rawIteratorAt(mchIndex2).sign(); + + // only consider opposite-sign pairs + if ((sign1 * sign2) >= 0) { + continue; + } + + // make sure that index #1 corresponds to the positive track + if (sign1 < 0) { + std::swap(mchIndex1, mchIndex2); + } + + auto const& muonTrack1 = muonTracks.rawIteratorAt(mchIndex1); + auto const& muonTrack2 = muonTracks.rawIteratorAt(mchIndex2); + + bool isGoodMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool goodMuonTracks = (isGoodMuon1 && isGoodMuon2); + + if (!goodMuonTracks) { + continue; + } + + // get the pre-stored MCH track parameters + const auto mchTrackParIt1 = mMchTrackPars.find(mchIndex1); + if (mchTrackParIt1 == mMchTrackPars.end()) { + continue; + } + const auto mchTrackParIt2 = mMchTrackPars.find(mchIndex2); + if (mchTrackParIt2 == mMchTrackPars.end()) { + continue; + } + const auto& mchTrackPar1 = mchTrackParIt1->second; + auto mchTrackPar1AtDca = PropagateMCHParam(FwdtoMCH(mchTrackPar1), collision.posZ()); + auto mchTrackPar1AtVertex = PropagateMCHToVertex(mchTrackPar1, collision); + const auto& mchTrackPar2 = mchTrackParIt2->second; + auto mchTrackPar2AtDca = PropagateMCHParam(FwdtoMCH(mchTrackPar2), collision.posZ()); + auto mchTrackPar2AtVertex = PropagateMCHToVertex(mchTrackPar2, collision); + + // get the pre-stored MCH track parameters after refit + const auto mchTrackParNewIt1 = mMchTrackParsNew.find(mchIndex1); + if (mchTrackParNewIt1 == mMchTrackParsNew.end()) { + continue; + } + const auto mchTrackParNewIt2 = mMchTrackParsNew.find(mchIndex2); + if (mchTrackParNewIt2 == mMchTrackParsNew.end()) { + continue; + } + const auto& mchTrackParNew1 = mchTrackParNewIt1->second; + auto mchTrackParNew1AtDca = PropagateMCHParam(FwdtoMCH(mchTrackParNew1), collision.posZ()); + auto mchTrackParNew1AtVertex = PropagateMCHToVertex(mchTrackParNew1, collision); + const auto& mchTrackParNew2 = mchTrackParNewIt2->second; + auto mchTrackParNew2AtDca = PropagateMCHParam(FwdtoMCH(mchTrackParNew2), collision.posZ()); + auto mchTrackParNew2AtVertex = PropagateMCHToVertex(mchTrackParNew2, collision); + + fillDimuonInvmassPlot(mchTrackPar1, mchTrackPar2, mchTrackPar1AtVertex, mchTrackPar2AtVertex, HIST("dimuon/invariantMass_MuonKine_MuonCuts")); + fillDimuonInvmassPlot(mchTrackParNew1, mchTrackParNew2, mchTrackParNew1AtVertex, mchTrackParNew2AtVertex, HIST("dimuon/realign/invariantMass_MuonKine_MuonCuts")); + + fillDimuonDcaPlots(mchTrackPar1, mchTrackPar2, + mchTrackPar1AtVertex, mchTrackPar2AtVertex, + mchTrackPar1AtDca, mchTrackPar2AtDca, + HIST("dimuon/dcax_MuonKine_MuonCuts"), HIST("dimuon/dcay_MuonKine_MuonCuts")); + fillDimuonDcaPlots(mchTrackParNew1, mchTrackParNew2, + mchTrackParNew1AtVertex, mchTrackParNew2AtVertex, + mchTrackParNew1AtDca, mchTrackParNew2AtDca, + HIST("dimuon/realign/dcax_MuonKine_MuonCuts"), HIST("dimuon/realign/dcay_MuonKine_MuonCuts")); + + double mchAngle = getMuMuAngle(mchTrackPar1AtVertex, mchTrackPar2AtVertex); + double mchAngleNew = getMuMuAngle(mchTrackParNew1AtVertex, mchTrackParNew2AtVertex); + + try { + const auto& candidates1 = collisionInfo.globalMuonTracks.at(mchIndex1); + const auto& candidates2 = collisionInfo.globalMuonTracks.at(mchIndex2); + + auto fwdIndex1 = candidates1[0]; + auto fwdIndex2 = candidates2[0]; + + auto const& fwdTrack1 = muonTracks.rawIteratorAt(fwdIndex1); + auto const& fwdTrack2 = muonTracks.rawIteratorAt(fwdIndex2); + + bool isGoodGlobalMuon1 = IsGoodMuon(muonTrack1, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool isGoodGlobalMuon2 = IsGoodMuon(muonTrack2, collision, cfgTrackChi2MchUp, cfgDimuonPLow, cfgDimuonPtLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, fSigmaPdcaUp); + bool goodGlobalMuonTracks = (isGoodGlobalMuon1 && isGoodGlobalMuon2); + + bool isGoodMatch1 = isGoodGlobalMatching(fwdTrack1, cfgDimuonMatchChi2Up.value); + bool isGoodMatch2 = isGoodGlobalMatching(fwdTrack2, cfgDimuonMatchChi2Up.value); + bool goodGlobalMuonMatches = (isGoodMatch1 && isGoodMatch2); + + if (!goodGlobalMuonTracks || !goodGlobalMuonMatches) { + continue; + } + + fillDimuonInvmassPlot(mchTrackPar1, mchTrackPar2, mchTrackPar1AtVertex, mchTrackPar2AtVertex, HIST("dimuon/invariantMass_MuonKine_GlobalMuonCuts_GoodMatches")); + fillDimuonInvmassPlot(mchTrackParNew1, mchTrackParNew2, mchTrackParNew1AtVertex, mchTrackParNew2AtVertex, HIST("dimuon/realign/invariantMass_MuonKine_GlobalMuonCuts_GoodMatches")); + + fillDimuonDcaPlots(mchTrackPar1, mchTrackPar2, + mchTrackPar1AtVertex, mchTrackPar2AtVertex, + mchTrackPar1AtDca, mchTrackPar2AtDca, + HIST("dimuon/dcax_MuonKine_GlobalMuonCuts_GoodMatches"), HIST("dimuon/dcay_MuonKine_GlobalMuonCuts_GoodMatches")); + fillDimuonDcaPlots(mchTrackParNew1, mchTrackParNew2, + mchTrackParNew1AtVertex, mchTrackParNew2AtVertex, + mchTrackParNew1AtDca, mchTrackParNew2AtDca, + HIST("dimuon/realign/dcax_MuonKine_GlobalMuonCuts_GoodMatches"), HIST("dimuon/realign/dcay_MuonKine_GlobalMuonCuts_GoodMatches")); + + auto mftIndex1 = fwdTrack1.matchMFTTrackId(); + auto mftIndex2 = fwdTrack2.matchMFTTrackId(); + + const auto mftTrackPar1 = mMftTrackPars.at(mftIndex1); + auto fwdTrackPar1AtDca = PropagateMFTToDCA(mftTrackPar1, mchTrackPar1, collision, cfgVertexZshift); + auto fwdTrackPar1AtVertex = PropagateMFTToVertex(mftTrackPar1, mchTrackPar1, collision); + const auto mftTrackPar2 = mMftTrackPars.at(mftIndex2); + auto fwdTrackPar2AtDca = PropagateMFTToDCA(mftTrackPar2, mchTrackPar2, collision, cfgVertexZshift); + auto fwdTrackPar2AtVertex = PropagateMFTToVertex(mftTrackPar2, mchTrackPar2, collision); + const auto mftTrackParNew1 = mMftTrackParsNew.at(mftIndex1); + auto fwdTrackParNew1AtDca = PropagateMFTToDCA(mftTrackParNew1, mchTrackParNew1, collision, cfgVertexZshift); + auto fwdTrackParNew1AtVertex = PropagateMFTToVertex(mftTrackParNew1, mchTrackParNew1, collision); + const auto mftTrackParNew2 = mMftTrackParsNew.at(mftIndex2); + auto fwdTrackParNew2AtDca = PropagateMFTToDCA(mftTrackParNew2, mchTrackParNew2, collision, cfgVertexZshift); + auto fwdTrackParNew2AtVertex = PropagateMFTToVertex(mftTrackParNew2, mchTrackParNew2, collision); + + fillDimuonInvmassPlot(mchTrackPar1, mchTrackPar2, fwdTrackPar1AtVertex, fwdTrackPar2AtVertex, HIST("dimuon/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches")); + fillDimuonInvmassPlot(mchTrackParNew1, mchTrackParNew2, fwdTrackParNew1AtVertex, fwdTrackParNew2AtVertex, HIST("dimuon/realign/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches")); + + fillDimuonDcaPlots(mchTrackPar1, mchTrackPar2, + fwdTrackPar1AtVertex, fwdTrackPar2AtVertex, + fwdTrackPar1AtDca, fwdTrackPar2AtDca, + HIST("dimuon/dcax_ScaledMftKine_GlobalMuonCuts_GoodMatches"), HIST("dimuon/dcay_ScaledMftKine_GlobalMuonCuts_GoodMatches")); + fillDimuonDcaPlots(mchTrackParNew1, mchTrackParNew2, + fwdTrackParNew1AtVertex, fwdTrackParNew2AtVertex, + fwdTrackParNew1AtDca, fwdTrackParNew2AtDca, + HIST("dimuon/realign/dcax_ScaledMftKine_GlobalMuonCuts_GoodMatches"), HIST("dimuon/realign/dcay_ScaledMftKine_GlobalMuonCuts_GoodMatches")); + + double fwdAngle = getMuMuAngle(fwdTrackPar1AtVertex, fwdTrackPar2AtVertex); + double fwdAngleNew = getMuMuAngle(fwdTrackParNew1AtVertex, fwdTrackParNew2AtVertex); + + fillDimuonAnglePlot(mchTrackPar1, mchTrackPar2, fwdTrackPar1AtVertex, fwdTrackPar2AtVertex, mchAngle, fwdAngle, HIST("dimuon/angle_GlobalMuonCuts_GoodMatches")); + fillDimuonAnglePlot(mchTrackParNew1, mchTrackParNew2, fwdTrackParNew1AtVertex, fwdTrackParNew2AtVertex, mchAngleNew, fwdAngleNew, HIST("dimuon/realign/angle_GlobalMuonCuts_GoodMatches")); + + fillDimuonDecayLengthPlots(MatchedTrackToParCovFwd(mchTrackPar1, mftTrackPar1), + MatchedTrackToParCovFwd(mchTrackPar2, mftTrackPar2), + fwdTrackPar1AtVertex, + fwdTrackPar2AtVertex, + collision, + HIST("dimuon/Lxy_GlobalMuonCuts_GoodMatches"), + HIST("dimuon/Lz_GlobalMuonCuts_GoodMatches"), + HIST("dimuon/tauxy_GlobalMuonCuts_GoodMatches"), + HIST("dimuon/tauz_GlobalMuonCuts_GoodMatches")); + fillDimuonDecayLengthPlots(MatchedTrackToParCovFwd(mchTrackParNew1, mftTrackParNew1), + MatchedTrackToParCovFwd(mchTrackParNew2, mftTrackParNew2), + fwdTrackParNew1AtVertex, + fwdTrackParNew2AtVertex, + collision, + HIST("dimuon/realign/Lxy_GlobalMuonCuts_GoodMatches"), + HIST("dimuon/realign/Lz_GlobalMuonCuts_GoodMatches"), + HIST("dimuon/realign/tauxy_GlobalMuonCuts_GoodMatches"), + HIST("dimuon/realign/tauz_GlobalMuonCuts_GoodMatches")); + } catch (const std::exception&) { + continue; + } + } + } + } + +#if (PROCESS_DERIVED_TABLES == 0) void processQA(MyEvents const& collisions, MyBCs const& bcs, MyMuonsWithCov const& muonTracks, MyMFTs const& mftTracks, - // MyMFTCovariances const& mftCovariances, - aod::FwdTrkCls const& clusters) + aod::FwdTrkCls const& clusters, + aod::FwdTrackAssoc const& fwdTrackAssocs, + aod::FwdTrkCompColls const& /*fwdTrackCompColls*/) { auto bc = bcs.begin(); if (mRunNumber != bc.runNumber()) { @@ -1835,19 +3203,72 @@ struct muonGlobalAlignment { mRunNumber = bc.runNumber(); } - std::map collisionInfos; - InitCollisions(collisions, bcs, muonTracks, mftTracks, collisionInfos); + std::unordered_map collisionInfos; + InitCollisions(collisions, bcs, muonTracks, clusters, mftTracks, collisionInfos); + + if (cfgProduceMuonAlignmentTables) { + StoreCollisions(collisionInfos, collisions, muonTracks, clusters, mftTracks, fwdTrackAssocs); + } + + FillMftPlots(collisions, muonTracks, mftTracks, collisionInfos); - FillDCAPlots(collisions, bcs, muonTracks, mftTracks, collisionInfos); + FillMchPlots(collisions, muonTracks, clusters, mftTracks, collisionInfos); - FillResidualsPlots(collisions, bcs, muonTracks, clusters, collisionInfos); + FillDimuonPlots(collisions, muonTracks, collisionInfos); } - PROCESS_SWITCH(muonGlobalAlignment, processQA, "process qa", true); + PROCESS_SWITCH(MuonGlobalAlignment, processQA, "processQA", true); + +#else + + void processDerivedTables(o2::aod::MACollisions const& collisions, + soa::Join const& muonTracks, + o2::aod::MAFwdTrkCls const& clusters, + o2::aod::MAMFTTracks const& mftTracks) + { + if (collisions.size() < 1) { + return; + } + const auto& c = collisions.begin(); + if (mRunNumber != c.runNumber()) { + initCCDB(c); + LOGF(info, "Set field for muons"); + VarManager::SetupMuonMagField(); + mRunNumber = c.runNumber(); + } + + std::unordered_map collisionInfos; + InitCollisionsDerivedTables(collisions, muonTracks, clusters, mftTracks, collisionInfos); + + FillMftPlots(collisions, muonTracks, mftTracks, collisionInfos); + + FillMchPlots(collisions, muonTracks, clusters, mftTracks, collisionInfos); + + FillDimuonPlots(collisions, muonTracks, collisionInfos); + } + + PROCESS_SWITCH(MuonGlobalAlignment, processDerivedTables, "processDerivedTables", true); +#endif }; +#if (PROCESS_DERIVED_TABLES == 1) +// Extends the derived tables with expression columns +struct MuonGlobalAlignmentSpawner { + Spawns realignFwdTrks; + Spawns realignFwdTrksCov; + Spawns realignMftTrks; + void init(InitContext const&) {} +}; +#endif + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { +#if (PROCESS_DERIVED_TABLES == 0) + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +#else return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +#endif }; diff --git a/PWGDQ/Tasks/qaMatching.cxx b/PWGDQ/Tasks/qaMatching.cxx index 9611f690500..104b54741d4 100644 --- a/PWGDQ/Tasks/qaMatching.cxx +++ b/PWGDQ/Tasks/qaMatching.cxx @@ -17,12 +17,19 @@ #include "PWGDQ/Core/VarManager.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/fwdtrackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FwdTrackReAlignTables.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" #include "Tools/ML/MlResponse.h" #include #include #include +#include #include #include #include @@ -53,13 +60,13 @@ #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include #include #include #include -#include - -#include +#include +#include #include #include @@ -67,9 +74,8 @@ #include #include #include -#include #include -#include +#include #include #include #include @@ -79,15 +85,123 @@ #include #include -#include - using namespace o2; using namespace o2::framework; using namespace o2::aod; -using MyEvents = soa::Join; +namespace qamatching +{ +DECLARE_SOA_COLUMN(P, p, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(InvQPt, invQPt, float); +DECLARE_SOA_COLUMN(Tanl, tanl, float); +DECLARE_SOA_COLUMN(MatchLabel, matchLabel, int8_t); +DECLARE_SOA_COLUMN(MatchType, matchType, int8_t); +DECLARE_SOA_COLUMN(MatchChi2, matchChi2, float); +DECLARE_SOA_COLUMN(MatchScore, matchScore, float); +DECLARE_SOA_COLUMN(MatchRanking, matchRanking, int32_t); +DECLARE_SOA_COLUMN(MftMultiplicity, mftMultiplicity, int32_t); +DECLARE_SOA_COLUMN(TrackType, trackType, int8_t); +DECLARE_SOA_COLUMN(MftMatchAttempts, mftMatchAttempts, int32_t); +DECLARE_SOA_COLUMN(IsTagged, isTagged, bool); +DECLARE_SOA_COLUMN(XAtVtx, xAtVtx, float); +DECLARE_SOA_COLUMN(YAtVtx, yAtVtx, float); +DECLARE_SOA_COLUMN(ZAtVtx, zAtVtx, float); +DECLARE_SOA_COLUMN(FXAtPlaneZ2, fXAtPlaneZ2, float); +DECLARE_SOA_COLUMN(FYAtPlaneZ2, fYAtPlaneZ2, float); +DECLARE_SOA_COLUMN(FZAtPlaneZ2, fZAtPlaneZ2, float); +DECLARE_SOA_COLUMN(PxAtVtx, pxAtVtx, float); +DECLARE_SOA_COLUMN(PyAtVtx, pyAtVtx, float); +DECLARE_SOA_COLUMN(PzAtVtx, pzAtVtx, float); +DECLARE_SOA_COLUMN(ColX, colX, float); +DECLARE_SOA_COLUMN(ColY, colY, float); +DECLARE_SOA_COLUMN(ColZ, colZ, float); +} // namespace qamatching + +namespace o2::aod +{ +DECLARE_SOA_TABLE(QaMatchingEvents, "AOD", "QAMEVT", + o2::soa::Index<>, + qamatching::MftMultiplicity, + qamatching::ColX, + qamatching::ColY, + qamatching::ColZ); +} // namespace o2::aod + +namespace qamatching +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(ReducedEvent, reducedEvent, int32_t, o2::aod::QaMatchingEvents, "QAMEVTs", ""); +} // namespace qamatching + +namespace o2::aod +{ +DECLARE_SOA_TABLE(QaMatchingMCHTrack, "AOD", "QAMCHTRK", + o2::soa::Index<>, + qamatching::ReducedEventId, + qamatching::TrackType, + qamatching::P, + qamatching::Pt, + qamatching::Eta, + qamatching::Phi, + qamatching::InvQPt, + qamatching::Tanl, + qamatching::MftMatchAttempts, + qamatching::IsTagged, + qamatching::XAtVtx, + qamatching::YAtVtx, + qamatching::ZAtVtx, + qamatching::FXAtPlaneZ2, + qamatching::FYAtPlaneZ2, + qamatching::FZAtPlaneZ2, + qamatching::PxAtVtx, + qamatching::PyAtVtx, + qamatching::PzAtVtx); +} // namespace o2::aod + +namespace qamatching +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(ReducedTrack, reducedTrack, int32_t, o2::aod::QaMatchingMCHTrack, "QAMCHTRKs", ""); +} // namespace qamatching + +namespace o2::aod +{ +DECLARE_SOA_TABLE(QaMatchingCandidates, "AOD", "QAMCAND", + o2::soa::Index<>, + qamatching::ReducedEventId, + qamatching::ReducedTrackId, + qamatching::MatchLabel, + qamatching::P, + qamatching::Pt, + qamatching::Eta, + qamatching::Phi, + qamatching::InvQPt, + qamatching::Tanl, + qamatching::MatchType, + qamatching::MatchChi2, + qamatching::MatchScore, + qamatching::MatchRanking, + qamatching::XAtVtx, + qamatching::YAtVtx, + qamatching::ZAtVtx, + qamatching::FXAtPlaneZ2, + qamatching::FYAtPlaneZ2, + qamatching::FZAtPlaneZ2, + qamatching::PxAtVtx, + qamatching::PyAtVtx, + qamatching::PzAtVtx); +} // namespace o2::aod + +namespace qamatching +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Candidate, candidate, int32_t, o2::aod::QaMatchingCandidates, "QAMCANDs", ""); +} // namespace qamatching + +using MyEvents = soa::Join; using MyMuons = soa::Join; -using MyMuonsMC = soa::Join; +using MyMuonsReAlign = soa::Join; +using MyMuonsMC = soa::Join; using MyMFTs = aod::MFTTracks; using MyMFTCovariances = aod::MFTTracksCov; using MyMFTsMC = soa::Join; @@ -98,20 +212,33 @@ using MyMFT = MyMFTs::iterator; using MyMFTCovariance = MyMFTCovariances::iterator; using SMatrix55 = ROOT::Math::SMatrix>; -using SMatrix5 = ROOT::Math::SVector; +using SMatrix5 = ROOT::Math::SVector; static float chi2ToScore(float chi2, int ndf, float chi2max) { - double p = -TMath::Log10(ROOT::Math::chisquared_cdf_c(chi2, ndf)); - double pnorm = -TMath::Log10(ROOT::Math::chisquared_cdf_c(chi2max, ndf)); + double p = -std::log10(ROOT::Math::chisquared_cdf_c(chi2, ndf)); + double pnorm = -std::log10(ROOT::Math::chisquared_cdf_c(chi2max, ndf)); double result = (1.f / (p / pnorm + 1.f)); return static_cast(result); } -struct qaMatching { +static void setMatchTypeAxisLabels(TAxis* axis) +{ + axis->SetBinLabel(1, "true (leading)"); + axis->SetBinLabel(2, "wrong (leading)"); + axis->SetBinLabel(3, "decay (leading)"); + axis->SetBinLabel(4, "fake (leading)"); + axis->SetBinLabel(5, "true (non leading)"); + axis->SetBinLabel(6, "wrong (non leading)"); + axis->SetBinLabel(7, "decay (non leading)"); + axis->SetBinLabel(8, "fake (non leading)"); + axis->SetBinLabel(9, "unknown"); +} + +struct QaMatching { template - using matrix = std::array, nr>; + using Matrix = std::array, nr>; enum MuonMatchType { kMatchTypeTrueLeading = 0, @@ -122,192 +249,223 @@ struct qaMatching { kMatchTypeWrongNonLeading = 5, kMatchTypeDecayNonLeading = 6, kMatchTypeFakeNonLeading = 7, - kMatchTypeUndefined + kMatchTypeUndefined = 8 }; + static constexpr int GlobalTrackTypeMax = 2; + static constexpr int MchMidTrackType = 3; + static constexpr int MftTrackTypeStandard = 0; + static constexpr int MftTrackTypeCA = 1; + static constexpr int ThetaAbsBoundaryDeg = 3; + static constexpr double SlopeResolutionZ = 535.; + static constexpr int MatchingDegreesOfFreedom = 5; + static constexpr size_t MinCandidatesForDeltaChi2 = 2; + static constexpr float MatchingScoreChi2Max = 50.f; + static constexpr float InvalidDeltaChi2 = -1.f; + static constexpr int ExtrapolationMethodStandard = 0; + static constexpr int ExtrapolationMethodMftFirstPoint = 2; + static constexpr int ExtrapolationMethodVertex = 3; + static constexpr int ExtrapolationMethodMftDca = 4; + static constexpr int DecayRankingDirect = 2; + static constexpr float MatchingPlaneDefaultZ = -77.5; + struct MatchingCandidate { int64_t collisionId{-1}; int64_t globalTrackId{-1}; int64_t muonTrackId{-1}; int64_t mftTrackId{-1}; + int trackType{-1}; + o2::track::TrackParCovFwd mftTrackProp; + o2::track::TrackParCovFwd mchTrackProp; double matchScore{-1}; double matchChi2{-1}; int matchRanking{-1}; double matchScoreProd{-1}; double matchChi2Prod{-1}; int matchRankingProd{-1}; - int mftMchMatchAttempts{0}; MuonMatchType matchType{kMatchTypeUndefined}; }; + Configurable cfgIsMc{"cfgIsMc", true, "Wheter the processed data is from MC simulations"}; + + //// Variables for selecting the collisions + Configurable cfgVtxZLow{"cfgVtxZLow", -10.0f, "Lower limit for the vertex z position"}; + Configurable cfgVtxZUp{"cfgVtxZUp", 10.0f, "Upper limit for the vertex z position"}; + //// Variables for selecting muon tracks - Configurable fPMchLow{"cfgPMchLow", 0.0f, ""}; - Configurable fPtMchLow{"cfgPtMchLow", 0.7f, ""}; - Configurable fEtaMchLow{"cfgEtaMchLow", -4.0f, ""}; - Configurable fEtaMchUp{"cfgEtaMchUp", -2.5f, ""}; - Configurable fRabsLow{"cfgRabsLow", 17.6f, ""}; - Configurable fRabsUp{"cfgRabsUp", 89.5f, ""}; - Configurable fSigmaPdcaUp{"cfgPdcaUp", 6.f, ""}; - Configurable fTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; - Configurable fMatchingChi2MchMidUp{"cfgMatchingChi2MchMidUp", 999.f, ""}; + Configurable cfgPMchLow{"cfgPMchLow", 0.0f, ""}; + Configurable cfgPtMchLow{"cfgPtMchLow", 0.7f, ""}; + Configurable cfgEtaMchLow{"cfgEtaMchLow", -4.0f, ""}; + Configurable cfgEtaMchUp{"cfgEtaMchUp", -2.5f, ""}; + Configurable cfgRabsLow{"cfgRabsLow", 17.6f, ""}; + Configurable cfgRabsUp{"cfgRabsUp", 89.5f, ""}; + Configurable cfgPdcaUp{"cfgPdcaUp", 6.f, ""}; + Configurable cfgTrackChi2MchUp{"cfgTrackChi2MchUp", 5.f, ""}; + Configurable cfgMatchingChi2MchMidUp{"cfgMatchingChi2MchMidUp", 999.f, ""}; //// Variables for selecting mft tracks - Configurable fEtaMftLow{"cfgEtaMftlow", -3.6f, ""}; - Configurable fEtaMftUp{"cfgEtaMftup", -2.5f, ""}; - Configurable fTrackNClustMftLow{"cfgTrackNClustMftLow", 7, ""}; - Configurable fTrackChi2MftUp{"cfgTrackChi2MftUp", 999.f, ""}; + Configurable cfgEtaMftLow{"cfgEtaMftLow", -3.6f, ""}; + Configurable cfgEtaMftUp{"cfgEtaMftUp", -2.5f, ""}; + Configurable cfgTrackNClustMftLow{"cfgTrackNClustMftLow", 7, ""}; + Configurable cfgTrackChi2MftUp{"cfgTrackChi2MftUp", 999.f, ""}; //// Variables for selecting global tracks - Configurable fMatchingChi2ScoreMftMchLow{"cfgMatchingChi2ScoreMftMchLow", chi2ToScore(50.f, 5, 50.f), ""}; + Configurable cfgMatchingChi2ScoreMftMchLow{"cfgMatchingChi2ScoreMftMchLow", chi2ToScore(50.f, 5, 50.f), ""}; //// Variables for selecting tagged muons - Configurable fMuonTaggingNCrossedMftPlanesLow{"cfgMuonTaggingNCrossedMftPlanesLow", 5, ""}; - Configurable fMuonTaggingTrackChi2MchUp{"cfgMuonTaggingTrackChi2MchUp", 5.f, ""}; - Configurable fMuonTaggingPMchLow{"cfgMuonTaggingPMchLow", 0.0f, ""}; - Configurable fMuonTaggingPtMchLow{"cfgMuonTaggingPtMchLow", 0.7f, ""}; - Configurable fMuonTaggingEtaMchLow{"cfgMuonTaggingEtaMchLow", -3.6f, ""}; - Configurable fMuonTaggingEtaMchUp{"cfgMuonTaggingEtaMchUp", -2.5f, ""}; - Configurable fMuonTaggingRabsLow{"cfgMuonTaggingRabsLow", 17.6f, ""}; - Configurable fMuonTaggingRabsUp{"cfgMuonTaggingRabsUp", 89.5f, ""}; - Configurable fMuonTaggingSigmaPdcaUp{"cfgMuonTaggingPdcaUp", 4.f, ""}; - Configurable fMuonTaggingChi2DiffLow{"cfgMuonTaggingChi2DiffLow", 100.f, ""}; + Configurable cfgMuonTaggingNCrossedMftPlanesLow{"cfgMuonTaggingNCrossedMftPlanesLow", 5, ""}; + Configurable cfgMuonTaggingTrackChi2MchUp{"cfgMuonTaggingTrackChi2MchUp", 5.f, ""}; + Configurable cfgMuonTaggingPMchLow{"cfgMuonTaggingPMchLow", 0.0f, ""}; + Configurable cfgMuonTaggingPtMchLow{"cfgMuonTaggingPtMchLow", 0.7f, ""}; + Configurable cfgMuonTaggingEtaMchLow{"cfgMuonTaggingEtaMchLow", -3.6f, ""}; + Configurable cfgMuonTaggingEtaMchUp{"cfgMuonTaggingEtaMchUp", -2.5f, ""}; + Configurable cfgMuonTaggingRabsLow{"cfgMuonTaggingRabsLow", 17.6f, ""}; + Configurable cfgMuonTaggingRabsUp{"cfgMuonTaggingRabsUp", 89.5f, ""}; + Configurable cfgMuonTaggingPdcaUp{"cfgMuonTaggingPdcaUp", 4.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftFrontLow{"cfgMuonTaggingRadiusAtMftFrontLow", 3.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftFrontUp{"cfgMuonTaggingRadiusAtMftFrontUp", 9.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftBackLow{"cfgMuonTaggingRadiusAtMftBackLow", 5.f, ""}; + Configurable cfgMuonTaggingRadiusAtMftBackUp{"cfgMuonTaggingRadiusAtMftBackUp", 12.f, ""}; + Configurable cfgMuonTaggingChi2DiffLow{"cfgMuonTaggingChi2DiffLow", 100.f, ""}; //// Variables for ccdb - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable grpMagPath{"grpMagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; // CCDB connection configurables struct : ConfigurableGroup { - Configurable fConfigCcdbUrl{"ccdb-url-", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than-", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - Configurable fConfigGrpPath{"grpPath-", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable fConfigGeoPath{"geoPath-", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable fConfigGrpMagPath{"grpmagPath-", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - } fConfigCCDB; + Configurable cfgCcdbUrl{"cfgCcdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgCcdbNoLaterThan{"cfgCcdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable cfgGrpPath{"cfgGrpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable cfgGeoPath{"cfgGeoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable cfgGrpmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + } configCcdb; struct : ConfigurableGroup { - Configurable fCreatePdgMomHistograms{"cfgCreatePdgMomHistograms", false, "create matching characteristics plots with particle mom PDG codes"}; - } fConfigQAs; + Configurable cfgCreatePdgMomHistograms{"cfgCreatePdgMomHistograms", false, "create matching characteristics plots with particle mom PDG codes"}; + } configQas; /// Variables for histograms configuration - Configurable fNCandidatesMax{"cfgNCandidatesMax", 5, "Number of matching candidates stored for each muon track"}; - Configurable fMftTrackMultiplicityMax{"cfgMftTrackMultiplicityMax", 1000, "Maximum number of MFT tracks per collision"}; + Configurable cfgNCandidatesMax{"cfgNCandidatesMax", 5, "Number of matching candidates stored for each muon track"}; + Configurable cfgMftTrackMultiplicityMax{"cfgMftTrackMultiplicityMax", 1000, "Maximum number of MFT tracks per collision"}; + + Configurable cfgQaMatchingAodDebug{"cfgQaMatchingAodDebug", 0, "If >0, print AO2D filling debug (0=off, N=max collisions)"}; double mBzAtMftCenter{0}; o2::globaltracking::MatchGlobalFwd mExtrap; - using MatchingFunc_t = std::function(const o2::dataformats::GlobalFwdTrack& mchtrack, const o2::track::TrackParCovFwd& mfttrack)>; - std::map mMatchingFunctionMap; ///< MFT-MCH Matching function + using MatchingFunc = std::function(const o2::dataformats::GlobalFwdTrack& mchtrack, const o2::track::TrackParCovFwd& mfttrack)>; + std::map mMatchingFunctionMap; ///< MFT-MCH Matching function // Chi2 matching interface - static constexpr int sChi2FunctionsNum = 5; + static constexpr int Chi2FunctionsNum = 5; struct : ConfigurableGroup { - Configurable fFunctionLabel_1{"cfgChi2FunctionLabel_1", std::string{"ProdAll"}, "Text label identifying this chi2 matching method"}; - Configurable fFunctionLabel_2{"cfgChi2FunctionLabel_2", std::string{"MatchXYPhiTanlMom"}, "Text label identifying this chi2 matching method"}; - Configurable fFunctionLabel_3{"cfgChi2FunctionLabel_3", std::string{"MatchXYPhiTanl"}, "Text label identifying this chi2 matching method"}; - Configurable fFunctionLabel_4{"cfgChi2FunctionLabel_4", std::string{""}, "Text label identifying this chi2 matching method"}; - Configurable fFunctionLabel_5{"cfgChi2FunctionLabel_5", std::string{""}, "Text label identifying this chi2 matching method"}; - std::array*, sChi2FunctionsNum> fFunctionLabel{ - &fFunctionLabel_1, &fFunctionLabel_2, &fFunctionLabel_3, &fFunctionLabel_4, &fFunctionLabel_5}; - - Configurable fFunctionNames_1{"cfgChi2FunctionNames_1", std::string{"prod"}, "Name of the chi2 matching function"}; - Configurable fFunctionNames_2{"cfgChi2FunctionNames_2", std::string{"matchALL"}, "Name of the chi2 matching function"}; - Configurable fFunctionNames_3{"cfgChi2FunctionNames_3", std::string{"matchXYPhiTanl"}, "Name of the chi2 matching function"}; - Configurable fFunctionNames_4{"cfgChi2FunctionNames_4", std::string{""}, "Name of the chi2 matching function"}; - Configurable fFunctionNames_5{"cfgChi2FunctionNames_5", std::string{""}, "Name of the chi2 matching function"}; - std::array*, sChi2FunctionsNum> fFunctionName{ - &fFunctionNames_1, &fFunctionNames_2, &fFunctionNames_3, &fFunctionNames_4, &fFunctionNames_5}; - - Configurable fMatchingScoreCut_1{"cfgChi2FunctionMatchingScoreCut_1", 0.f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_2{"cfgChi2FunctionMatchingScoreCut_2", 0.5f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_3{"cfgChi2FunctionMatchingScoreCut_3", 0.5f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_4{"cfgChi2FunctionMatchingScoreCut_4", 0.5f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_5{"cfgChi2FunctionMatchingScoreCut_5", 0.5f, "Minimum score value for selecting good matches"}; - std::array*, sChi2FunctionsNum> fMatchingScoreCut{ - &fMatchingScoreCut_1, &fMatchingScoreCut_2, &fMatchingScoreCut_3, &fMatchingScoreCut_4, &fMatchingScoreCut_5}; - - Configurable fMatchingPlaneZ_1{"cfgChi2FunctionMatchingPlaneZ_1", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_2{"cfgChi2FunctionMatchingPlaneZ_2", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_3{"cfgChi2FunctionMatchingPlaneZ_3", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_4{"cfgChi2FunctionMatchingPlaneZ_4", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_5{"cfgChi2FunctionMatchingPlaneZ_5", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; - std::array*, sChi2FunctionsNum> fMatchingPlaneZ{ - &fMatchingPlaneZ_1, &fMatchingPlaneZ_2, &fMatchingPlaneZ_3, &fMatchingPlaneZ_4, &fMatchingPlaneZ_5}; - - Configurable fMatchingExtrapMethod_1{"cfgChi2MatchingExtrapMethod_1", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_2{"cfgChi2MatchingExtrapMethod_2", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_3{"cfgChi2MatchingExtrapMethod_3", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_4{"cfgChi2MatchingExtrapMethod_4", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_5{"cfgChi2MatchingExtrapMethod_5", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - std::array*, sChi2FunctionsNum> fMatchingExtrapMethod{ - &fMatchingExtrapMethod_1, &fMatchingExtrapMethod_2, &fMatchingExtrapMethod_3, &fMatchingExtrapMethod_4, &fMatchingExtrapMethod_5}; - } fConfigChi2MatchingOptions; + Configurable cfgChi2FunctionLabel1{"cfgChi2FunctionLabel1", std::string{"ProdAll"}, "Text label identifying this chi2 matching method"}; + Configurable cfgChi2FunctionLabel2{"cfgChi2FunctionLabel2", std::string{"MatchXYPhiTanlMom"}, "Text label identifying this chi2 matching method"}; + Configurable cfgChi2FunctionLabel3{"cfgChi2FunctionLabel3", std::string{"MatchXYPhiTanl"}, "Text label identifying this chi2 matching method"}; + Configurable cfgChi2FunctionLabel4{"cfgChi2FunctionLabel4", std::string{""}, "Text label identifying this chi2 matching method"}; + Configurable cfgChi2FunctionLabel5{"cfgChi2FunctionLabel5", std::string{""}, "Text label identifying this chi2 matching method"}; + std::array*, Chi2FunctionsNum> functionLabels{ + &cfgChi2FunctionLabel1, &cfgChi2FunctionLabel2, &cfgChi2FunctionLabel3, &cfgChi2FunctionLabel4, &cfgChi2FunctionLabel5}; + + Configurable cfgChi2FunctionName1{"cfgChi2FunctionName1", std::string{"prod"}, "Name of the chi2 matching function"}; + Configurable cfgChi2FunctionName2{"cfgChi2FunctionName2", std::string{"matchALL"}, "Name of the chi2 matching function"}; + Configurable cfgChi2FunctionName3{"cfgChi2FunctionName3", std::string{"matchXYPhiTanl"}, "Name of the chi2 matching function"}; + Configurable cfgChi2FunctionName4{"cfgChi2FunctionName4", std::string{""}, "Name of the chi2 matching function"}; + Configurable cfgChi2FunctionName5{"cfgChi2FunctionName5", std::string{""}, "Name of the chi2 matching function"}; + std::array*, Chi2FunctionsNum> functionNames{ + &cfgChi2FunctionName1, &cfgChi2FunctionName2, &cfgChi2FunctionName3, &cfgChi2FunctionName4, &cfgChi2FunctionName5}; + + Configurable cfgChi2FunctionMatchingScoreCut1{"cfgChi2FunctionMatchingScoreCut1", 0.f, "Minimum score value for selecting good matches"}; + Configurable cfgChi2FunctionMatchingScoreCut2{"cfgChi2FunctionMatchingScoreCut2", 0.5f, "Minimum score value for selecting good matches"}; + Configurable cfgChi2FunctionMatchingScoreCut3{"cfgChi2FunctionMatchingScoreCut3", 0.5f, "Minimum score value for selecting good matches"}; + Configurable cfgChi2FunctionMatchingScoreCut4{"cfgChi2FunctionMatchingScoreCut4", 0.5f, "Minimum score value for selecting good matches"}; + Configurable cfgChi2FunctionMatchingScoreCut5{"cfgChi2FunctionMatchingScoreCut5", 0.5f, "Minimum score value for selecting good matches"}; + std::array*, Chi2FunctionsNum> matchingScoreCuts{ + &cfgChi2FunctionMatchingScoreCut1, &cfgChi2FunctionMatchingScoreCut2, &cfgChi2FunctionMatchingScoreCut3, &cfgChi2FunctionMatchingScoreCut4, &cfgChi2FunctionMatchingScoreCut5}; + + Configurable cfgChi2FunctionMatchingPlaneZ1{"cfgChi2FunctionMatchingPlaneZ1", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + Configurable cfgChi2FunctionMatchingPlaneZ2{"cfgChi2FunctionMatchingPlaneZ2", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + Configurable cfgChi2FunctionMatchingPlaneZ3{"cfgChi2FunctionMatchingPlaneZ3", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + Configurable cfgChi2FunctionMatchingPlaneZ4{"cfgChi2FunctionMatchingPlaneZ4", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + Configurable cfgChi2FunctionMatchingPlaneZ5{"cfgChi2FunctionMatchingPlaneZ5", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + std::array*, Chi2FunctionsNum> matchingPlaneZs{ + &cfgChi2FunctionMatchingPlaneZ1, &cfgChi2FunctionMatchingPlaneZ2, &cfgChi2FunctionMatchingPlaneZ3, &cfgChi2FunctionMatchingPlaneZ4, &cfgChi2FunctionMatchingPlaneZ5}; + + Configurable cfgChi2MatchingExtrapMethod1{"cfgChi2MatchingExtrapMethod1", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgChi2MatchingExtrapMethod2{"cfgChi2MatchingExtrapMethod2", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgChi2MatchingExtrapMethod3{"cfgChi2MatchingExtrapMethod3", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgChi2MatchingExtrapMethod4{"cfgChi2MatchingExtrapMethod4", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgChi2MatchingExtrapMethod5{"cfgChi2MatchingExtrapMethod5", 0, "Method for MCH track extrapolation to maching plane"}; + std::array*, Chi2FunctionsNum> matchingExtrapMethods{ + &cfgChi2MatchingExtrapMethod1, &cfgChi2MatchingExtrapMethod2, &cfgChi2MatchingExtrapMethod3, &cfgChi2MatchingExtrapMethod4, &cfgChi2MatchingExtrapMethod5}; + } configChi2MatchingOptions; // ML interface - static constexpr int sMLModelsNum = 5; + static constexpr int MlModelsNum = 5; struct : ConfigurableGroup { - Configurable fModelLabel_1{"cfgMLModelLabel_1", std::string{""}, "Text label identifying this group of ML models"}; - Configurable fModelLabel_2{"cfgMLModelLabel_2", std::string{""}, "Text label identifying this group of ML models"}; - Configurable fModelLabel_3{"cfgMLModelLabel_3", std::string{""}, "Text label identifying this group of ML models"}; - Configurable fModelLabel_4{"cfgMLModelLabel_4", std::string{""}, "Text label identifying this group of ML models"}; - Configurable fModelLabel_5{"cfgMLModelLabel_5", std::string{""}, "Text label identifying this group of ML models"}; - std::array*, sMLModelsNum> fModelLabel{ - &fModelLabel_1, &fModelLabel_2, &fModelLabel_3, &fModelLabel_4, &fModelLabel_5}; - - Configurable fModelPathCCDB_1{"cfgMLModelPathCCDB_1", "Users/m/mcoquet/MLTest", "Paths of models on CCDB"}; - Configurable fModelPathCCDB_2{"cfgMLModelPathsCCDB_2", std::string{""}, "Paths of models on CCDB"}; - Configurable fModelPathCCDB_3{"cfgMLModelPathsCCDB_3", std::string{""}, "Paths of models on CCDB"}; - Configurable fModelPathCCDB_4{"cfgMLModelPathsCCDB_4", std::string{""}, "Paths of models on CCDB"}; - Configurable fModelPathCCDB_5{"cfgMLModelPathsCCDB_5", std::string{""}, "Paths of models on CCDB"}; - std::array*, sMLModelsNum> fModelPathCCDB{ - &fModelPathCCDB_1, &fModelPathCCDB_2, &fModelPathCCDB_3, &fModelPathCCDB_4, &fModelPathCCDB_5}; - - Configurable fModelName_1{"cfgMLModelName_1", "model.onnx", "ONNX file names for each pT bin (if not from CCDB full path)"}; - Configurable fModelName_2{"cfgMLModelNames_2", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; - Configurable fModelName_3{"cfgMLModelNames_3", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; - Configurable fModelName_4{"cfgMLModelNames_4", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; - Configurable fModelName_5{"cfgMLModelNames_5", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; - std::array*, sMLModelsNum> fModelName{ - &fModelName_1, &fModelName_2, &fModelName_3, &fModelName_4, &fModelName_5}; - - Configurable fInputFeatures_1{"cfgMLInputFeatures_1", "chi2MCHMFT", "Names of ML model input features"}; - Configurable fInputFeatures_2{"cfgMLInputFeatures_2", std::string{""}, "Names of ML model input features"}; - Configurable fInputFeatures_3{"cfgMLInputFeatures_3", std::string{""}, "Names of ML model input features"}; - Configurable fInputFeatures_4{"cfgMLInputFeatures_4", std::string{""}, "Names of ML model input features"}; - Configurable fInputFeatures_5{"cfgMLInputFeatures_5", std::string{""}, "Names of ML model input features"}; - std::array*, sMLModelsNum> fInputFeatures{ - &fInputFeatures_1, &fInputFeatures_2, &fInputFeatures_3, &fInputFeatures_4, &fInputFeatures_5}; - - Configurable fMatchingScoreCut_1{"cfgMLModelMatchingScoreCut_1", 0.f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_2{"cfgMLModelMatchingScoreCut_2", 0.f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_3{"cfgMLModelMatchingScoreCut_3", 0.f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_4{"cfgMLModelMatchingScoreCut_4", 0.f, "Minimum score value for selecting good matches"}; - Configurable fMatchingScoreCut_5{"cfgMLModelMatchingScoreCut_5", 0.f, "Minimum score value for selecting good matches"}; - std::array*, sMLModelsNum> fMatchingScoreCut{ - &fMatchingScoreCut_1, &fMatchingScoreCut_2, &fMatchingScoreCut_3, &fMatchingScoreCut_4, &fMatchingScoreCut_5}; - - Configurable fMatchingPlaneZ_1{"cfgMLModelMatchingPlaneZ_1", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_2{"cfgMLModelMatchingPlaneZ_2", 0.f, "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_3{"cfgMLModelMatchingPlaneZ_3", 0.f, "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_4{"cfgMLModelMatchingPlaneZ_4", 0.f, "Z position of the matching plane"}; - Configurable fMatchingPlaneZ_5{"cfgMLModelMatchingPlaneZ_5", 0.f, "Z position of the matching plane"}; - std::array*, sMLModelsNum> fMatchingPlaneZ{ - &fMatchingPlaneZ_1, &fMatchingPlaneZ_2, &fMatchingPlaneZ_3, &fMatchingPlaneZ_4, &fMatchingPlaneZ_5}; - - Configurable fMatchingExtrapMethod_1{"cfgMLMatchingExtrapMethod_1", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_2{"cfgMLMatchingExtrapMethod_2", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_3{"cfgMLMatchingExtrapMethod_3", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_4{"cfgMLMatchingExtrapMethod_4", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - Configurable fMatchingExtrapMethod_5{"cfgMLMatchingExtrapMethod_5", static_cast(0), "Method for MCH track extrapolation to maching plane"}; - std::array*, sMLModelsNum> fMatchingExtrapMethod{ - &fMatchingExtrapMethod_1, &fMatchingExtrapMethod_2, &fMatchingExtrapMethod_3, &fMatchingExtrapMethod_4, &fMatchingExtrapMethod_5}; - } fConfigMlOptions; + Configurable cfgMlModelLabel1{"cfgMlModelLabel1", std::string{""}, "Text label identifying this group of ML models"}; + Configurable cfgMlModelLabel2{"cfgMlModelLabel2", std::string{""}, "Text label identifying this group of ML models"}; + Configurable cfgMlModelLabel3{"cfgMlModelLabel3", std::string{""}, "Text label identifying this group of ML models"}; + Configurable cfgMlModelLabel4{"cfgMlModelLabel4", std::string{""}, "Text label identifying this group of ML models"}; + Configurable cfgMlModelLabel5{"cfgMlModelLabel5", std::string{""}, "Text label identifying this group of ML models"}; + std::array*, MlModelsNum> modelLabels{ + &cfgMlModelLabel1, &cfgMlModelLabel2, &cfgMlModelLabel3, &cfgMlModelLabel4, &cfgMlModelLabel5}; + + Configurable cfgMlModelPathCcdb1{"cfgMlModelPathCcdb1", "Users/m/mcoquet/MLTest", "Paths of models on CCDB"}; + Configurable cfgMlModelPathCcdb2{"cfgMlModelPathCcdb2", std::string{""}, "Paths of models on CCDB"}; + Configurable cfgMlModelPathCcdb3{"cfgMlModelPathCcdb3", std::string{""}, "Paths of models on CCDB"}; + Configurable cfgMlModelPathCcdb4{"cfgMlModelPathCcdb4", std::string{""}, "Paths of models on CCDB"}; + Configurable cfgMlModelPathCcdb5{"cfgMlModelPathCcdb5", std::string{""}, "Paths of models on CCDB"}; + std::array*, MlModelsNum> modelPathCcds{ + &cfgMlModelPathCcdb1, &cfgMlModelPathCcdb2, &cfgMlModelPathCcdb3, &cfgMlModelPathCcdb4, &cfgMlModelPathCcdb5}; + + Configurable cfgMlModelName1{"cfgMlModelName1", "model.onnx", "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable cfgMlModelName2{"cfgMlModelName2", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable cfgMlModelName3{"cfgMlModelName3", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable cfgMlModelName4{"cfgMlModelName4", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable cfgMlModelName5{"cfgMlModelName5", std::string{""}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + std::array*, MlModelsNum> modelNames{ + &cfgMlModelName1, &cfgMlModelName2, &cfgMlModelName3, &cfgMlModelName4, &cfgMlModelName5}; + + Configurable> cfgMlInputFeatures1{"cfgMlInputFeatures1", std::vector{"chi2MCHMFT"}, "Names of ML model input features"}; + Configurable> cfgMlInputFeatures2{"cfgMlInputFeatures2", std::vector{}, "Names of ML model input features"}; + Configurable> cfgMlInputFeatures3{"cfgMlInputFeatures3", std::vector{}, "Names of ML model input features"}; + Configurable> cfgMlInputFeatures4{"cfgMlInputFeatures4", std::vector{}, "Names of ML model input features"}; + Configurable> cfgMlInputFeatures5{"cfgMlInputFeatures5", std::vector{}, "Names of ML model input features"}; + std::array>*, MlModelsNum> inputFeatures{ + &cfgMlInputFeatures1, &cfgMlInputFeatures2, &cfgMlInputFeatures3, &cfgMlInputFeatures4, &cfgMlInputFeatures5}; + + Configurable cfgMlModelMatchingScoreCut1{"cfgMlModelMatchingScoreCut1", 0.f, "Minimum score value for selecting good matches"}; + Configurable cfgMlModelMatchingScoreCut2{"cfgMlModelMatchingScoreCut2", 0.f, "Minimum score value for selecting good matches"}; + Configurable cfgMlModelMatchingScoreCut3{"cfgMlModelMatchingScoreCut3", 0.f, "Minimum score value for selecting good matches"}; + Configurable cfgMlModelMatchingScoreCut4{"cfgMlModelMatchingScoreCut4", 0.f, "Minimum score value for selecting good matches"}; + Configurable cfgMlModelMatchingScoreCut5{"cfgMlModelMatchingScoreCut5", 0.f, "Minimum score value for selecting good matches"}; + std::array*, MlModelsNum> matchingScoreCuts{ + &cfgMlModelMatchingScoreCut1, &cfgMlModelMatchingScoreCut2, &cfgMlModelMatchingScoreCut3, &cfgMlModelMatchingScoreCut4, &cfgMlModelMatchingScoreCut5}; + + Configurable cfgMlModelMatchingPlaneZ1{"cfgMlModelMatchingPlaneZ1", static_cast(o2::mft::constants::mft::LayerZCoordinate()[9]), "Z position of the matching plane"}; + Configurable cfgMlModelMatchingPlaneZ2{"cfgMlModelMatchingPlaneZ2", 0.f, "Z position of the matching plane"}; + Configurable cfgMlModelMatchingPlaneZ3{"cfgMlModelMatchingPlaneZ3", 0.f, "Z position of the matching plane"}; + Configurable cfgMlModelMatchingPlaneZ4{"cfgMlModelMatchingPlaneZ4", 0.f, "Z position of the matching plane"}; + Configurable cfgMlModelMatchingPlaneZ5{"cfgMlModelMatchingPlaneZ5", 0.f, "Z position of the matching plane"}; + std::array*, MlModelsNum> matchingPlaneZs{ + &cfgMlModelMatchingPlaneZ1, &cfgMlModelMatchingPlaneZ2, &cfgMlModelMatchingPlaneZ3, &cfgMlModelMatchingPlaneZ4, &cfgMlModelMatchingPlaneZ5}; + + Configurable cfgMlMatchingExtrapMethod1{"cfgMlMatchingExtrapMethod1", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgMlMatchingExtrapMethod2{"cfgMlMatchingExtrapMethod2", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgMlMatchingExtrapMethod3{"cfgMlMatchingExtrapMethod3", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgMlMatchingExtrapMethod4{"cfgMlMatchingExtrapMethod4", 0, "Method for MCH track extrapolation to maching plane"}; + Configurable cfgMlMatchingExtrapMethod5{"cfgMlMatchingExtrapMethod5", 0, "Method for MCH track extrapolation to maching plane"}; + std::array*, MlModelsNum> matchingExtrapMethods{ + &cfgMlMatchingExtrapMethod1, &cfgMlMatchingExtrapMethod2, &cfgMlMatchingExtrapMethod3, &cfgMlMatchingExtrapMethod4, &cfgMlMatchingExtrapMethod5}; + } configMlOptions; std::vector binsPtMl; - std::array cutValues; + std::array cutValues{}; std::vector cutDirMl; std::map> matchingMlResponses; std::map matchingChi2Functions; @@ -317,7 +475,7 @@ struct qaMatching { int mRunNumber{0}; // needed to detect if the run changed and trigger update of magnetic field - Service ccdbManager; + Service ccdbManager{}; o2::ccdb::CcdbApi fCCDBApi; o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT_muon_glo", false, false, true}; @@ -327,8 +485,25 @@ struct qaMatching { // the map key is the MCH(-MID) track global index using MatchingCandidates = std::map>; + struct TrackTimeInfo { + // global BC number + int64_t bc{-1}; + // track time relative to the global BC + double time{-1}; + // time resolution + double timeRes{-1}; + }; + + struct MchTrackInfo : public TrackTimeInfo { + int64_t index{-1}; + // vector of MFT tracks that are time-compatible with this MCH track + std::vector compatMftTracks; + }; + struct CollisionInfo { int64_t index{0}; + // internal index of this collision in the derived table + int64_t reducedEventId{0}; uint64_t bc{0}; // z position of the collision double zVertex{0}; @@ -336,8 +511,12 @@ struct qaMatching { int mftTracksMultiplicity{0}; // vector of MFT track indexes std::vector mftTracks; - // vector of MCH(-MID) track indexes - std::vector mchTracks; + // time information for MFT tracks + std::unordered_map mftTimeInfos; + // extra information for MCH(-MID) tracks + std::unordered_map mchTracks; + // mapping between original and reduced MCH track indexes + std::map reducedMchTrackIds; // matching candidates MatchingCandidates matchingCandidates; // vector of MFT-MCH track index pairs belonging to the same MC muon particle @@ -347,13 +526,7 @@ struct qaMatching { }; using CollisionInfos = std::map; - std::unordered_map mftTrackCovs; - - std::vector> fMatchablePairs; - MatchingCandidates fMatchingCandidates; - std::vector fTaggedMuons; - using MuonPair = std::pair, std::pair>; using GlobalMuonPair = std::pair>, std::pair>>; @@ -381,28 +554,29 @@ struct qaMatching { ®istryMatching9}}; HistogramRegistry registryDimuon{"registryDimuon", {}}; - std::unordered_map matchingHistos; - matrix dimuonHistos; + Produces qaMatchingEvents; + Produces qaMatchingMCHTrack; + Produces qaMatchingCandidates; struct EfficiencyPlotter { - o2::framework::HistPtr p_num; - o2::framework::HistPtr p_den; - o2::framework::HistPtr p_pdg_num; - o2::framework::HistPtr p_pdg_den; - o2::framework::HistPtr pt_num; - o2::framework::HistPtr pt_den; - o2::framework::HistPtr pt_pdg_num; - o2::framework::HistPtr pt_pdg_den; - o2::framework::HistPtr phi_num; - o2::framework::HistPtr phi_den; - o2::framework::HistPtr phi_pdg_num; - o2::framework::HistPtr phi_pdg_den; - o2::framework::HistPtr eta_num; - o2::framework::HistPtr eta_den; - o2::framework::HistPtr eta_pdg_num; - o2::framework::HistPtr eta_pdg_den; - - EfficiencyPlotter(std::string path, std::string title, + o2::framework::HistPtr pNum; + o2::framework::HistPtr pDen; + o2::framework::HistPtr pPdgNum; + o2::framework::HistPtr pPdgDen; + o2::framework::HistPtr ptNum; + o2::framework::HistPtr ptDen; + o2::framework::HistPtr ptPdgNum; + o2::framework::HistPtr ptPdgDen; + o2::framework::HistPtr phiNum; + o2::framework::HistPtr phiDen; + o2::framework::HistPtr phiPdgNum; + o2::framework::HistPtr phiPdgDen; + o2::framework::HistPtr etaNum; + o2::framework::HistPtr etaDen; + o2::framework::HistPtr etaPdgNum; + o2::framework::HistPtr etaPdgDen; + + EfficiencyPlotter(const std::string& path, const std::string& title, HistogramRegistry& registry, bool createPdgMomHistograms) { AxisSpec pAxis = {100, 0, 100, "p (GeV/c)"}; @@ -417,116 +591,173 @@ struct qaMatching { // momentum dependence histName = path + "p_num"; histTitle = title + " vs. p - num"; - p_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pAxis}}); + pNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pAxis}}); histName = path + "p_den"; histTitle = title + " vs. p - den"; - p_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pAxis}}); + pDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pAxis}}); if (createPdgMomHistograms) { histName = path + "p_pdg_num"; histTitle = title + " vs. p vs pdg ID - num"; - p_pdg_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pAxis, motherPDGAxis}}); + pPdgNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pAxis, motherPDGAxis}}); histName = path + "p_pdg_den"; histTitle = title + " vs. p vs pdg ID - den"; - p_pdg_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pAxis, motherPDGAxis}}); + pPdgDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pAxis, motherPDGAxis}}); } // pT dependence histName = path + "pt_num"; histTitle = title + " vs. p_{T} - num"; - pt_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pTAxis}}); + ptNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pTAxis}}); histName = path + "pt_den"; histTitle = title + " vs. p_{T} - den"; - pt_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pTAxis}}); + ptDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {pTAxis}}); if (createPdgMomHistograms) { histName = path + "pt_pdg_num"; histTitle = title + " vs. p_{T} vs pdg ID - num"; - pt_pdg_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pTAxis, motherPDGAxis}}); + ptPdgNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pTAxis, motherPDGAxis}}); histName = path + "pt_pdg_den"; histTitle = title + " vs. p_{T} vs pdg ID - den"; - pt_pdg_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pTAxis, motherPDGAxis}}); + ptPdgDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pTAxis, motherPDGAxis}}); } // eta dependence histName = path + "eta_num"; histTitle = title + " vs. #eta - num"; - eta_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {etaAxis}}); + etaNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {etaAxis}}); histName = path + "eta_den"; histTitle = title + " vs. #eta - den"; - eta_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {etaAxis}}); + etaDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {etaAxis}}); if (createPdgMomHistograms) { histName = path + "eta_pdg_num"; histTitle = title + " vs. #eta vs pdg ID - num"; - eta_pdg_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {etaAxis, motherPDGAxis}}); + etaPdgNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {etaAxis, motherPDGAxis}}); histName = path + "eta_pdg_den"; histTitle = title + " vs. #eta vs pdg ID - den"; - eta_pdg_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {etaAxis, motherPDGAxis}}); + etaPdgDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {etaAxis, motherPDGAxis}}); } // phi dependence histName = path + "phi_num"; histTitle = title + " vs. #phi - num"; - phi_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {phiAxis}}); + phiNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {phiAxis}}); histName = path + "phi_den"; histTitle = title + " vs. #phi - den"; - phi_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {phiAxis}}); + phiDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {phiAxis}}); if (createPdgMomHistograms) { histName = path + "phi_pdg_num"; histTitle = title + " vs. #phi vs pdg ID - num"; - phi_pdg_num = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {phiAxis, motherPDGAxis}}); + phiPdgNum = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {phiAxis, motherPDGAxis}}); histName = path + "phi_pdg_den"; histTitle = title + " vs. #phi vs pdg ID - den"; - phi_pdg_den = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {phiAxis, motherPDGAxis}}); + phiPdgDen = registry.add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {phiAxis, motherPDGAxis}}); } } template - void Fill(const T& track, bool passed) + void fill(const T& track, bool passed) { - double phi = track.phi() * 180 / TMath::Pi(); - std::get>(p_den)->Fill(track.p()); - std::get>(pt_den)->Fill(track.pt()); - std::get>(eta_den)->Fill(track.eta()); - std::get>(phi_den)->Fill(phi); + constexpr double RadToDeg = 180. / o2::constants::math::PI; + double phi = track.phi() * RadToDeg; + std::get>(pDen)->Fill(track.p()); + std::get>(ptDen)->Fill(track.pt()); + std::get>(etaDen)->Fill(track.eta()); + std::get>(phiDen)->Fill(phi); if (passed) { - std::get>(p_num)->Fill(track.p()); - std::get>(pt_num)->Fill(track.pt()); - std::get>(eta_num)->Fill(track.eta()); - std::get>(phi_num)->Fill(phi); + std::get>(pNum)->Fill(track.p()); + std::get>(ptNum)->Fill(track.pt()); + std::get>(etaNum)->Fill(track.eta()); + std::get>(phiNum)->Fill(phi); } } // Study the PDG origin of particles and their effect on the purity score template - void Fill(const T& track, int pdgCode, bool passed) + void fill(const T& track, int pdgCode, bool passed) { - double phi = track.phi() * 180 / TMath::Pi(); - std::get>(p_pdg_den)->Fill(track.p(), pdgCode); - std::get>(pt_pdg_den)->Fill(track.pt(), pdgCode); - std::get>(eta_pdg_den)->Fill(track.eta(), pdgCode); - std::get>(phi_pdg_den)->Fill(phi, pdgCode); + constexpr double RadToDeg = 180. / o2::constants::math::PI; + double phi = track.phi() * RadToDeg; + std::get>(pPdgDen)->Fill(track.p(), pdgCode); + std::get>(ptPdgDen)->Fill(track.pt(), pdgCode); + std::get>(etaPdgDen)->Fill(track.eta(), pdgCode); + std::get>(phiPdgDen)->Fill(phi, pdgCode); if (passed) { - std::get>(p_pdg_num)->Fill(track.p(), pdgCode); - std::get>(pt_pdg_num)->Fill(track.pt(), pdgCode); - std::get>(eta_pdg_num)->Fill(track.eta(), pdgCode); - std::get>(phi_pdg_num)->Fill(phi, pdgCode); + std::get>(pPdgNum)->Fill(track.p(), pdgCode); + std::get>(ptPdgNum)->Fill(track.pt(), pdgCode); + std::get>(etaPdgNum)->Fill(track.eta(), pdgCode); + std::get>(phiPdgNum)->Fill(phi, pdgCode); } } }; + struct MatchFeaturesHistos { + o2::framework::HistPtr hDeltaP; + o2::framework::HistPtr hDeltaPt; + o2::framework::HistPtr hDeltaX; + o2::framework::HistPtr hDeltaY; + o2::framework::HistPtr hDeltaPhi; + o2::framework::HistPtr hDeltaTanl; + o2::framework::HistPtr hDeltaEta; + o2::framework::HistPtr hRabs; + o2::framework::HistPtr hPullPt; + o2::framework::HistPtr hPullX; + o2::framework::HistPtr hPullY; + o2::framework::HistPtr hPullPhi; + o2::framework::HistPtr hPullTanl; + + MatchFeaturesHistos(const std::string& path, HistogramRegistry* registry, int numCandidates, double scoreMax) + { + AxisSpec indexAxis = {numCandidates, 0, static_cast(numCandidates), "ranking index"}; + int matchTypeMax = static_cast(kMatchTypeUndefined) + 1; + AxisSpec matchTypeAxis = {matchTypeMax, 0, static_cast(matchTypeMax), "match type"}; + AxisSpec taggedAxis = {2, 0, 2.0, "is tagged"}; + AxisSpec scoreAxis = {100, 0, scoreMax, "match score"}; + AxisSpec scoreGapAxis = {100, 0, scoreMax, "match score gap"}; + AxisSpec logpAxis = {16, -1, 3, "log_{10}(p)"}; + AxisSpec dxAxis = {100, -20, 20, "#Deltax (cm)"}; + AxisSpec dyAxis = {100, -20, 20, "#Deltay (cm)"}; + AxisSpec dpAxis = {100, -50, 50, "#Deltap (GeV/c)"}; + AxisSpec dptAxis = {100, -5, 5, "#Deltap_{T} (GeV/c)"}; + AxisSpec dphiAxis = {100, -2, 2, "#Delta#phi (rad)"}; + AxisSpec dtanlAxis = {100, -20, 20, "#Deltatanl"}; + AxisSpec detaAxis = {100, -2, 2, "#Delta#eta"}; + AxisSpec rabsAxis = {100, 0, 100, "R_{abs}"}; + AxisSpec uxAxis = {100, -10, 10, "pull(x)"}; + AxisSpec uyAxis = {100, -10, 10, "pull(y)"}; + AxisSpec uptAxis = {100, -10, 10, "pull(p_{T})"}; + AxisSpec uphiAxis = {100, -10, 10, "pull(#phi)"}; + AxisSpec utanlAxis = {100, -10, 10, "pull(tanl)"}; + + hDeltaP = registry->add((path + "/deltaP").c_str(), "MFT-MCH #Deltap", {HistType::kTHnSparseF, {dpAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hDeltaPt = registry->add((path + "/deltaPt").c_str(), "MFT-MCH #Deltap_{T}", {HistType::kTHnSparseF, {dptAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hDeltaX = registry->add((path + "/deltaX").c_str(), "MFT-MCH #Deltax", {HistType::kTHnSparseF, {dxAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hDeltaY = registry->add((path + "/deltaY").c_str(), "MFT-MCH #Deltay", {HistType::kTHnSparseF, {dyAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hDeltaPhi = registry->add((path + "/deltaPhi").c_str(), "MFT-MCH #Delta#phi", {HistType::kTHnSparseF, {dphiAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hDeltaTanl = registry->add((path + "/deltaTanl").c_str(), "MFT-MCH #DeltaTanl", {HistType::kTHnSparseF, {dtanlAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hDeltaEta = registry->add((path + "/deltaEta").c_str(), "MFT-MCH #Delta#eta", {HistType::kTHnSparseF, {detaAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hRabs = registry->add((path + "/Rabs").c_str(), "MFT-MCH R_{abs}", {HistType::kTHnSparseF, {rabsAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + + hPullPt = registry->add((path + "/pullPt").c_str(), "MFT-MCH Pull p_{T}", {HistType::kTHnSparseF, {uptAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hPullX = registry->add((path + "/pullX").c_str(), "MFT-MCH Pull x", {HistType::kTHnSparseF, {uxAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hPullY = registry->add((path + "/pullY").c_str(), "MFT-MCH Pull y", {HistType::kTHnSparseF, {uyAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hPullPhi = registry->add((path + "/pullPhi").c_str(), "MFT-MCH Pull #phi", {HistType::kTHnSparseF, {uphiAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + hPullTanl = registry->add((path + "/pullTanl").c_str(), "MFT-MCH Pull Tanl", {HistType::kTHnSparseF, {utanlAxis, logpAxis, scoreAxis, scoreGapAxis, indexAxis, matchTypeAxis, taggedAxis}}); + } + }; + struct MatchRankingHistos { o2::framework::HistPtr hist; o2::framework::HistPtr histVsP; @@ -538,13 +769,14 @@ struct qaMatching { o2::framework::HistPtr histVsDeltaChi2; o2::framework::HistPtr histVsProdRanking; - MatchRankingHistos(std::string histName, std::string histTitle, HistogramRegistry* registry, int mftMultMax, int numCandidates) + MatchRankingHistos(const std::string& histName, const std::string& histTitle, HistogramRegistry* registry, int mftMultMax, int numCandidates) { AxisSpec pAxis = {100, 0, 100, "p (GeV/c)"}; + AxisSpec logpAxis = {32, -1, 3, "log_{10}(p)"}; AxisSpec ptAxis = {100, 0, 10, "p_{T} (GeV/c)"}; AxisSpec dzAxis = {100, -1, 4, "#Deltaz (cm)"}; - AxisSpec trackMultAxis = {static_cast(mftMultMax) / 10, 0, static_cast(mftMultMax), "MFT track mult."}; - AxisSpec matchAttemptsAxis = {static_cast(mftMultMax) / 10, 0, static_cast(mftMultMax), "match attempts"}; + AxisSpec trackMultAxis = {mftMultMax / 10, 0, static_cast(mftMultMax), "MFT track mult."}; + AxisSpec matchAttemptsAxis = {mftMultMax / 10, 0, static_cast(mftMultMax), "match attempts"}; AxisSpec trackTypeAxis = {2, 0, 2, "MFT track type"}; int matchTypeMax = static_cast(kMatchTypeUndefined); AxisSpec matchTypeAxis = {matchTypeMax, 0, static_cast(matchTypeMax), "match type"}; @@ -563,7 +795,7 @@ struct qaMatching { std::get>(histVsMftTrackType)->GetXaxis()->SetBinLabel(1, "Kalman"); std::get>(histVsMftTrackType)->GetXaxis()->SetBinLabel(2, "CA"); histVsDeltaChi2 = registry->add((histName + "VsDeltaChi2").c_str(), (histTitle + " vs. #Delta#chi^{2}").c_str(), {HistType::kTH2F, {dchi2Axis, indexAxis}}); - histVsProdRanking = registry->add((histName + "VsProdRanking").c_str(), (histTitle + " vs. prod ranking").c_str(), {HistType::kTH2F, {indexProdAxis, indexAxis}}); + histVsProdRanking = registry->add((histName + "VsProdRanking").c_str(), (histTitle + " vs. prod ranking").c_str(), {HistType::kTHnSparseF, {indexProdAxis, indexAxis, logpAxis}}); } }; @@ -573,6 +805,8 @@ struct qaMatching { std::unique_ptr fMatchRankingPaired; std::unique_ptr fMatchRankingPairedGoodMCH; + std::unique_ptr fMatchFeaturesGoodMCH; + //- o2::framework::HistPtr fMissedMatches; o2::framework::HistPtr fMissedMatchesGoodMCH; @@ -610,214 +844,160 @@ struct qaMatching { o2::framework::HistPtr fTrueMatchChi2VsProd; //- - EfficiencyPlotter fMatchingPurityPlotter; - EfficiencyPlotter fPairingEfficiencyPlotter; - EfficiencyPlotter fMatchingEfficiencyPlotter; - EfficiencyPlotter fFakeMatchingEfficiencyPlotter; + std::unique_ptr fMatchingPurityPlotter; + std::unique_ptr fPairingEfficiencyPlotter; + std::unique_ptr fMatchingEfficiencyPlotter; + std::unique_ptr fFakeMatchingEfficiencyPlotter; - HistogramRegistry* registry; + HistogramRegistry* registry{nullptr}; - MatchingPlotter(std::string path, + MatchingPlotter(const std::string& path, HistogramRegistry* reg, bool createPdgMomHistograms, int mftMultMax, - int numCandidates) - : fMatchingPurityPlotter(path + "matching-purity/", "Matching purity", *reg, createPdgMomHistograms), - fPairingEfficiencyPlotter(path + "pairing-efficiency/", "Pairing efficiency", *reg, createPdgMomHistograms), - fMatchingEfficiencyPlotter(path + "matching-efficiency/", "Matching efficiency", *reg, createPdgMomHistograms), - fFakeMatchingEfficiencyPlotter(path + "fake-matching-efficiency/", "Fake matching efficiency", *reg, createPdgMomHistograms) + int numCandidates, + bool isMc) + : registry(reg) { - registry = reg; AxisSpec pAxis = {100, 0, 100, "p (GeV/c)"}; AxisSpec ptAxis = {100, 0, 10, "p_{T} (GeV/c)"}; AxisSpec dzAxis = {100, 0, 50, "#Deltaz (cm)"}; AxisSpec indexAxis = {6, 0, 6, "ranking index"}; - - std::string histName = path + "matchRanking"; - std::string histTitle = "True match ranking"; - - fMatchRanking = std::make_unique(path + "matchRanking", "True match ranking", registry, mftMultMax, numCandidates); - fMatchRankingGoodMCH = std::make_unique(path + "matchRankingGoodMCH", "True match ranking (good MCH tracks)", registry, mftMultMax, numCandidates); - fMatchRankingPaired = std::make_unique(path + "matchRankingPaired", "True match ranking (paired MCH tracks)", registry, mftMultMax, numCandidates); - fMatchRankingPairedGoodMCH = std::make_unique(path + "matchRankingPairedGoodMCH", "True match ranking (good paired MCH tracks)", registry, mftMultMax, numCandidates); - - //- - AxisSpec missedMatchAxis = {5, 0, 5, ""}; - histName = path + "missedMatches"; - histTitle = "Missed matches"; - fMissedMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {missedMatchAxis}}); - std::get>(fMissedMatches)->GetXaxis()->SetBinLabel(1, "not paired"); - std::get>(fMissedMatches)->GetXaxis()->SetBinLabel(2, "fake MCH"); - std::get>(fMissedMatches)->GetXaxis()->SetBinLabel(3, "not stored"); - histName = path + "missedMatchesGoodMCH"; - histTitle = "Missed matches - good MCH tracks"; - fMissedMatchesGoodMCH = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {missedMatchAxis}}); - std::get>(fMissedMatchesGoodMCH)->GetXaxis()->SetBinLabel(1, "not paired"); - std::get>(fMissedMatchesGoodMCH)->GetXaxis()->SetBinLabel(2, "fake MCH"); - std::get>(fMissedMatchesGoodMCH)->GetXaxis()->SetBinLabel(3, "not stored"); - - AxisSpec decayRankingAxis = {5, 0, 5, "decay ranking"}; - histName = path + "decayRankingGoodMatches"; - histTitle = "Decay ranking - good matches"; - fDecayRankingGoodMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {decayRankingAxis}}); - histName = path + "decayRankingNonLeadingMatches"; - histTitle = "Decay ranking - non-leading matches"; - fDecayRankingNonLeadingMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {decayRankingAxis}}); - histName = path + "decayRankingMissedMatches"; - histTitle = "Decay ranking - missed matches"; - fDecayRankingMissedMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {decayRankingAxis}}); - - AxisSpec scoreGapAxis = {100, 0, 1, "match score difference"}; - histName = path + "scoreGapLeadingTrueMatches"; - histTitle = "Score gap between leading and subleading matches - good matches"; - fScoreGapLeadingTrueMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {scoreGapAxis}}); - histName = path + "scoreGapNonLeadingTrueMatches"; - histTitle = "Score gap between leading and subleading matches - non-leading matches"; - fScoreGapNonLeadingTrueMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {scoreGapAxis}}); - - //- AxisSpec chi2Axis = {100, 0, 100, "matching #chi^{2}/NDF"}; AxisSpec scoreAxis = {100, 0, 1, "matching score"}; - int matchTypeMax = static_cast(kMatchTypeUndefined); - AxisSpec matchTypeAxis = {matchTypeMax, 0, static_cast(matchTypeMax), "match type"}; - histName = path + "matchType"; - histTitle = "Match type"; - fMatchType = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {matchTypeAxis}}); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchType)->GetXaxis()->SetBinLabel(8, "fake (non leading)"); - histName = path + "matchTypeVsP"; - histTitle = "Match type vs. p"; - fMatchTypeVsP = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pAxis, matchTypeAxis}}); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchTypeVsP)->GetYaxis()->SetBinLabel(8, "fake (non leading)"); - histName = path + "matchTypeVsPt"; - histTitle = "Match type vs. p_{T}"; - fMatchTypeVsPt = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {ptAxis, matchTypeAxis}}); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchTypeVsPt)->GetYaxis()->SetBinLabel(8, "fake (non leading)"); - - histName = path + "matchChi2VsType"; - histTitle = "Match #chi^{2} vs. match type"; - fMatchChi2VsType = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {matchTypeAxis, chi2Axis}}); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchChi2VsType)->GetXaxis()->SetBinLabel(8, "fake (non leading)"); - histName = path + "matchChi2VsTypeVsP"; - histTitle = "Match #chi^{2} vs. match type vs. p"; - fMatchChi2VsTypeVsP = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {pAxis, matchTypeAxis, chi2Axis}}); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchChi2VsTypeVsP)->GetYaxis()->SetBinLabel(8, "fake (non leading)"); - histName = path + "matchChi2VsTypeVsPt"; - histTitle = "Match #chi^{2} vs. match type vs. p_{T}"; - fMatchChi2VsTypeVsPt = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {ptAxis, matchTypeAxis, chi2Axis}}); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()->SetBinLabel(8, "fake (non leading)"); - //- - histName = path + "matchScoreVsType"; - histTitle = "Match score vs. match type"; - fMatchScoreVsType = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {matchTypeAxis, scoreAxis}}); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchScoreVsType)->GetXaxis()->SetBinLabel(8, "fake (non leading)"); - histName = path + "matchScoreVsTypeVsP"; - histTitle = "Match score vs. match type vs. p"; - fMatchScoreVsTypeVsP = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {pAxis, matchTypeAxis, scoreAxis}}); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchScoreVsTypeVsP)->GetYaxis()->SetBinLabel(8, "fake (non leading)"); - histName = path + "matchScoreVsTypeVsPt"; - histTitle = "Match score vs. match type vs. p_{T}"; - fMatchScoreVsTypeVsPt = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {ptAxis, matchTypeAxis, scoreAxis}}); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(1, "true (leading)"); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(2, "wrong (leading)"); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(3, "decay (leading)"); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(4, "fake (leading)"); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(5, "true (non leading)"); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(6, "wrong (non leading)"); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(7, "decay (non leading)"); - std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()->SetBinLabel(8, "fake (non leading)"); + + std::string histName; + std::string histTitle; + + fMatchFeaturesGoodMCH = std::make_unique(path + "matchFeaturesGoodMCH", registry, numCandidates, 100); + + if (isMc) { + fMatchRanking = std::make_unique(path + "matchRanking", "True match ranking", registry, mftMultMax, numCandidates); + fMatchRankingGoodMCH = std::make_unique(path + "matchRankingGoodMCH", "True match ranking (good MCH tracks)", registry, mftMultMax, numCandidates); + fMatchRankingPaired = std::make_unique(path + "matchRankingPaired", "True match ranking (paired MCH tracks)", registry, mftMultMax, numCandidates); + fMatchRankingPairedGoodMCH = std::make_unique(path + "matchRankingPairedGoodMCH", "True match ranking (good paired MCH tracks)", registry, mftMultMax, numCandidates); + + fMatchingPurityPlotter = std::make_unique(path + "matching-purity/", "Matching purity", *reg, createPdgMomHistograms); + fPairingEfficiencyPlotter = std::make_unique(path + "pairing-efficiency/", "Pairing efficiency", *reg, createPdgMomHistograms); + fMatchingEfficiencyPlotter = std::make_unique(path + "matching-efficiency/", "Matching efficiency", *reg, createPdgMomHistograms); + fFakeMatchingEfficiencyPlotter = std::make_unique(path + "fake-matching-efficiency/", "Fake matching efficiency", *reg, createPdgMomHistograms); + + //- + AxisSpec missedMatchAxis = {5, 0, 5, ""}; + histName = path + "missedMatches"; + histTitle = "Missed matches"; + fMissedMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {missedMatchAxis}}); + std::get>(fMissedMatches)->GetXaxis()->SetBinLabel(1, "not paired"); + std::get>(fMissedMatches)->GetXaxis()->SetBinLabel(2, "fake MCH"); + std::get>(fMissedMatches)->GetXaxis()->SetBinLabel(3, "not stored"); + histName = path + "missedMatchesGoodMCH"; + histTitle = "Missed matches - good MCH tracks"; + fMissedMatchesGoodMCH = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {missedMatchAxis}}); + std::get>(fMissedMatchesGoodMCH)->GetXaxis()->SetBinLabel(1, "not paired"); + std::get>(fMissedMatchesGoodMCH)->GetXaxis()->SetBinLabel(2, "fake MCH"); + std::get>(fMissedMatchesGoodMCH)->GetXaxis()->SetBinLabel(3, "not stored"); + + AxisSpec decayRankingAxis = {5, 0, 5, "decay ranking"}; + histName = path + "decayRankingGoodMatches"; + histTitle = "Decay ranking - good matches"; + fDecayRankingGoodMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {decayRankingAxis}}); + histName = path + "decayRankingNonLeadingMatches"; + histTitle = "Decay ranking - non-leading matches"; + fDecayRankingNonLeadingMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {decayRankingAxis}}); + histName = path + "decayRankingMissedMatches"; + histTitle = "Decay ranking - missed matches"; + fDecayRankingMissedMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {decayRankingAxis}}); + + AxisSpec scoreGapAxis = {100, 0, 1, "match score difference"}; + histName = path + "scoreGapLeadingTrueMatches"; + histTitle = "Score gap between leading and subleading matches - good matches"; + fScoreGapLeadingTrueMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {scoreGapAxis}}); + histName = path + "scoreGapNonLeadingTrueMatches"; + histTitle = "Score gap between leading and subleading matches - non-leading matches"; + fScoreGapNonLeadingTrueMatches = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {scoreGapAxis}}); + + //- + int matchTypeMax = static_cast(kMatchTypeUndefined) + 1; + AxisSpec matchTypeAxis = {matchTypeMax, 0, static_cast(matchTypeMax), ""}; + histName = path + "matchType"; + histTitle = "Match type"; + fMatchType = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH1F, {matchTypeAxis}}); + setMatchTypeAxisLabels(std::get>(fMatchType)->GetXaxis()); + histName = path + "matchTypeVsP"; + histTitle = "Match type vs. p"; + fMatchTypeVsP = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {pAxis, matchTypeAxis}}); + setMatchTypeAxisLabels(std::get>(fMatchTypeVsP)->GetYaxis()); + histName = path + "matchTypeVsPt"; + histTitle = "Match type vs. p_{T}"; + fMatchTypeVsPt = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {ptAxis, matchTypeAxis}}); + setMatchTypeAxisLabels(std::get>(fMatchTypeVsPt)->GetYaxis()); + + histName = path + "matchChi2VsType"; + histTitle = "Match #chi^{2} vs. match type"; + fMatchChi2VsType = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {matchTypeAxis, chi2Axis}}); + setMatchTypeAxisLabels(std::get>(fMatchChi2VsType)->GetXaxis()); + histName = path + "matchChi2VsTypeVsP"; + histTitle = "Match #chi^{2} vs. match type vs. p"; + fMatchChi2VsTypeVsP = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {pAxis, matchTypeAxis, chi2Axis}}); + setMatchTypeAxisLabels(std::get>(fMatchChi2VsTypeVsP)->GetYaxis()); + histName = path + "matchChi2VsTypeVsPt"; + histTitle = "Match #chi^{2} vs. match type vs. p_{T}"; + fMatchChi2VsTypeVsPt = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {ptAxis, matchTypeAxis, chi2Axis}}); + setMatchTypeAxisLabels(std::get>(fMatchChi2VsTypeVsPt)->GetYaxis()); + //- + histName = path + "matchScoreVsType"; + histTitle = "Match score vs. match type"; + fMatchScoreVsType = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {matchTypeAxis, scoreAxis}}); + setMatchTypeAxisLabels(std::get>(fMatchScoreVsType)->GetXaxis()); + histName = path + "matchScoreVsTypeVsP"; + histTitle = "Match score vs. match type vs. p"; + fMatchScoreVsTypeVsP = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {pAxis, matchTypeAxis, scoreAxis}}); + setMatchTypeAxisLabels(std::get>(fMatchScoreVsTypeVsP)->GetYaxis()); + histName = path + "matchScoreVsTypeVsPt"; + histTitle = "Match score vs. match type vs. p_{T}"; + fMatchScoreVsTypeVsPt = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH3F, {ptAxis, matchTypeAxis, scoreAxis}}); + setMatchTypeAxisLabels(std::get>(fMatchScoreVsTypeVsPt)->GetYaxis()); + } AxisSpec prodScoreAxis = {100, 0, 1, "matching score (prod)"}; histName = path + "matchScoreVsProd"; histTitle = "Match score vs. production"; fMatchScoreVsProd = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {prodScoreAxis, scoreAxis}}); - histName = path + "trueMatchScoreVsProd"; - histTitle = "Match score vs. production - true match"; - fTrueMatchScoreVsProd = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {prodScoreAxis, scoreAxis}}); + if (isMc) { + histName = path + "trueMatchScoreVsProd"; + histTitle = "Match score vs. production - true match"; + fTrueMatchScoreVsProd = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {prodScoreAxis, scoreAxis}}); + } AxisSpec prodChi2Axis = {100, 0, 100, "matching #chi^{2}/NDF (prod)"}; histName = path + "matchChi2VsProd"; histTitle = "Match #chi^{2} vs. production"; fMatchChi2VsProd = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {prodChi2Axis, chi2Axis}}); - histName = path + "trueMatchChi2VsProd"; - histTitle = "Match #chi^{2} vs. production - true match"; - fTrueMatchChi2VsProd = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {{100, 0, 10, "matching #chi^{2} (prod)"}, {100, 0, 10, "matching #chi^{2}"}}}); + if (isMc) { + histName = path + "trueMatchChi2VsProd"; + histTitle = "Match #chi^{2} vs. production - true match"; + fTrueMatchChi2VsProd = registry->add(histName.c_str(), histTitle.c_str(), {HistType::kTH2F, {{100, 0, 10, "matching #chi^{2} (prod)"}, {100, 0, 10, "matching #chi^{2}"}}}); + } } }; std::unique_ptr fChi2MatchingPlotter; - std::map> fMatchingHistogramRegistries; std::map> fMatchingPlotters; std::unique_ptr fTaggedMuonsMatchingPlotter; - std::unique_ptr fSelectedMuonsMatchingPlotter; CollisionInfos fCollisionInfos; template - void initCCDB(BC const& bc) + void initCcdb(BC const& bc) { - if (mRunNumber == bc.runNumber()) + if (mRunNumber == bc.runNumber()) { return; + } mRunNumber = bc.runNumber(); std::map metadata; auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(fCCDBApi, mRunNumber); auto ts = soreor.first; - auto grpmag = fCCDBApi.retrieveFromTFileAny(grpmagPath, metadata, ts); + auto grpmag = fCCDBApi.retrieveFromTFileAny(grpMagPath, metadata, ts); o2::base::Propagator::initFieldFromGRP(grpmag); LOGF(info, "Set field for muons"); VarManager::SetupMuonMagField(); @@ -825,50 +1005,68 @@ struct qaMatching { ccdbManager->get(geoPath); } o2::mch::TrackExtrap::setField(); - auto* fieldB = static_cast(TGeoGlobalMagField::Instance()->GetField()); + auto* fieldB = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); if (fieldB) { - double centerMFT[3] = {0, 0, -61.4}; // Field at center of MFT - mBzAtMftCenter = fieldB->getBz(centerMFT); + const std::array centerMft{0, 0, -61.4}; // Field at center of MFT + mBzAtMftCenter = fieldB->getBz(centerMft.data()); // std::cout << "fieldB: " << (void*)fieldB << std::endl; } } - void CreateMatchingHistosMC() + void createMatchingHistos() { AxisSpec chi2Axis = {1000, 0, 1000, "chi^{2}"}; AxisSpec chi2AxisSmall = {200, 0, 100, "chi^{2}"}; - AxisSpec pAxis = {1000, 0, 100, "p (GeV/c)"}; - AxisSpec pTAxis = {100, 0, 10, "p_{T} (GeV/c)"}; + AxisSpec pAxis = {50, 0, 100, "p (GeV/c)"}; + AxisSpec pTAxis = {50, 0, 10, "p_{T} (GeV/c)"}; AxisSpec etaAxis = {100, -4, -2, "#eta"}; - AxisSpec phiAxis = {90, -180, 180, "#phi (degrees)"}; - std::string histPath = "matching/MC/"; - - AxisSpec trackPositionXAtMFTAxis = {100, -15, 15, "MFT x (cm)"}; - AxisSpec trackPositionYAtMFTAxis = {100, -15, 15, "MFT y (cm)"}; - registry.add((histPath + "pairedMCHTracksAtMFT").c_str(), "Paired MCH tracks position at MFT end", {HistType::kTH2F, {trackPositionXAtMFTAxis, trackPositionYAtMFTAxis}}); - registry.add((histPath + "pairedMFTTracksAtMFT").c_str(), "Paired MFT tracks position at MFT end", {HistType::kTH2F, {trackPositionXAtMFTAxis, trackPositionYAtMFTAxis}}); - registry.add((histPath + "selectedMCHTracksAtMFT").c_str(), "Selected MCH tracks position at MFT end", {HistType::kTH2F, {trackPositionXAtMFTAxis, trackPositionYAtMFTAxis}}); - registry.add((histPath + "selectedMCHTracksAtMFTTrue").c_str(), "Selected MCH tracks position at MFT end - true", {HistType::kTH2F, {trackPositionXAtMFTAxis, trackPositionYAtMFTAxis}}); - registry.add((histPath + "selectedMCHTracksAtMFTFake").c_str(), "Selected MCH tracks position at MFT end - fake", {HistType::kTH2F, {trackPositionXAtMFTAxis, trackPositionYAtMFTAxis}}); - - fChi2MatchingPlotter = std::make_unique(histPath + "Prod/", ®istryMatching, fConfigQAs.fCreatePdgMomHistograms, fMftTrackMultiplicityMax, fNCandidatesMax); + AxisSpec vzAxis = {20, -10, 10, "vertex_{z} (cm)"}; + AxisSpec rAxis = {60, 0, 15, "R_{track} (cm)"}; + AxisSpec mftNClusAxis = {6, 5, 11, "# of MFT clusters"}; + + std::string histPath = cfgIsMc.value ? "matching/MC/" : "matching/"; + + AxisSpec trackPositionXAtMftAxis = {100, -15, 15, "MFT x (cm)"}; + AxisSpec trackPositionYAtMftAxis = {100, -15, 15, "MFT y (cm)"}; + registry.add((histPath + "taggedMCHTracksAtMFT").c_str(), "Tagged MCH tracks position at MFT end", {HistType::kTH2F, {trackPositionXAtMftAxis, trackPositionYAtMftAxis}}); + if (cfgIsMc.value) { + registry.add((histPath + "pairedMCHTracksAtMFT").c_str(), "Paired MCH tracks position at MFT end", {HistType::kTH2F, {trackPositionXAtMftAxis, trackPositionYAtMftAxis}}); + registry.add((histPath + "pairedMFTTracksAtMFT").c_str(), "Paired MFT tracks position at MFT end", {HistType::kTH2F, {trackPositionXAtMftAxis, trackPositionYAtMftAxis}}); + registry.add((histPath + "taggedMCHTracksAtMFTTrue").c_str(), "Tagged MCH tracks position at MFT end - true", {HistType::kTH2F, {trackPositionXAtMftAxis, trackPositionYAtMftAxis}}); + registry.add((histPath + "taggedMCHTracksAtMFTFake").c_str(), "Tagged MCH tracks position at MFT end - fake", {HistType::kTH2F, {trackPositionXAtMftAxis, trackPositionYAtMftAxis}}); + } + + registry.add((histPath + "muonTracksVsMchKine").c_str(), "Muon tracks vs. MCH kine", {HistType::kTHnSparseF, {etaAxis, pTAxis, vzAxis}}); + registry.add((histPath + "muonTracksVsMchKineAtVertex").c_str(), "Muon tracks vs. MCH kine at vertex", {HistType::kTHnSparseF, {etaAxis, pTAxis, vzAxis}}); + + registry.add((histPath + "muonTracksRadiusAtMftFront").c_str(), "Muon tracks radius at MFT front", {HistType::kTHnSparseF, {rAxis, vzAxis, pAxis}}); + registry.add((histPath + "muonTracksRadiusAtMftBack").c_str(), "Muon tracks radius at MFT back", {HistType::kTHnSparseF, {rAxis, vzAxis, pAxis}}); + + if (cfgIsMc.value) { + registry.add((histPath + "muonTracksPairedVsMchKine").c_str(), "Paired muon tracks vs. MCH kine", {HistType::kTHnSparseF, {etaAxis, pTAxis, vzAxis, mftNClusAxis}}); + registry.add((histPath + "muonTracksPairedVsMchKineAtVertex").c_str(), "Paired muon tracks vs. MCH kine at vertex", {HistType::kTHnSparseF, {etaAxis, pTAxis, vzAxis, mftNClusAxis}}); + + registry.add((histPath + "muonTracksRadiusPairedAtMftFront").c_str(), "Paired muon tracks radius at MFT front", {HistType::kTHnSparseF, {rAxis, vzAxis, pAxis}}); + registry.add((histPath + "muonTracksRadiusPairedAtMftBack").c_str(), "Paired muon tracks radius at MFT back", {HistType::kTHnSparseF, {rAxis, vzAxis, pAxis}}); + } + + fChi2MatchingPlotter = std::make_unique(histPath + "Prod/", ®istryMatching, configQas.cfgCreatePdgMomHistograms, cfgMftTrackMultiplicityMax, cfgNCandidatesMax, cfgIsMc.value); int registryIndex = 0; for (const auto& [label, func] : matchingChi2Functions) { - fMatchingPlotters[label] = std::make_unique(histPath + label + "/", registryMatchingVec[registryIndex], fConfigQAs.fCreatePdgMomHistograms, fMftTrackMultiplicityMax, fNCandidatesMax); + fMatchingPlotters[label] = std::make_unique(histPath + label + "/", registryMatchingVec[registryIndex], configQas.cfgCreatePdgMomHistograms, cfgMftTrackMultiplicityMax, cfgNCandidatesMax, cfgIsMc.value); registryIndex += 1; } for (const auto& [label, response] : matchingMlResponses) { - fMatchingPlotters[label] = std::make_unique(histPath + label + "/", (registryMatchingVec[registryIndex]), fConfigQAs.fCreatePdgMomHistograms, fMftTrackMultiplicityMax, fNCandidatesMax); + fMatchingPlotters[label] = std::make_unique(histPath + label + "/", (registryMatchingVec[registryIndex]), configQas.cfgCreatePdgMomHistograms, cfgMftTrackMultiplicityMax, cfgNCandidatesMax, cfgIsMc.value); registryIndex += 1; } - fTaggedMuonsMatchingPlotter = std::make_unique(histPath + "Tagged/", ®istryMatching, fConfigQAs.fCreatePdgMomHistograms, fMftTrackMultiplicityMax, fNCandidatesMax); - fSelectedMuonsMatchingPlotter = std::make_unique(histPath + "Selected/", ®istryMatching, fConfigQAs.fCreatePdgMomHistograms, fMftTrackMultiplicityMax, fNCandidatesMax); + fTaggedMuonsMatchingPlotter = std::make_unique(histPath + "Tagged/", ®istryMatching, configQas.cfgCreatePdgMomHistograms, cfgMftTrackMultiplicityMax, cfgNCandidatesMax, cfgIsMc.value); } - void CreateDimuonHistos() + void createDimuonHistos() { - AxisSpec invMassAxis = {400, 1, 5, "M_{#mu^{+}#mu^{-}} (GeV/c^{2})"}; + AxisSpec invMassAxis = {500, 0, 5, "M_{#mu^{+}#mu^{-}} (GeV/c^{2})"}; AxisSpec invMassCorrelationAxis = {400, 0, 8, "M_{#mu^{+}#mu^{-}} (GeV/c^{2})"}; AxisSpec invMassAxisFull = {5000, 0, 100, "M_{#mu^{+}#mu^{-}} (GeV/c^{2})"}; int matchTypeCombMax = (static_cast(kMatchTypeTrueNonLeading) - 1) * 10 + static_cast(kMatchTypeTrueNonLeading) - 1; @@ -894,9 +1092,19 @@ struct qaMatching { registryDimuon.add("dimuon/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} invariant mass (global muon cuts, rescaled MFT, good matches)", {HistType::kTH1F, {invMassAxis}}); // MFT-MCH-MID tracks with MFT acceptance cuts vs. muon tracks match type, good matches registryDimuon.add("dimuon/MC/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches_vs_match_type", "#mu^{+}#mu^{-} invariant mass vs. match tye (global muon cuts, rescaled MFT, good matches)", {HistType::kTH2F, {invMassAxis, matchTypeAxis}}); + + // global kinematics as stored in candidates + // MFT-MCH-MID tracks with MFT acceptance cuts + registryDimuon.add("dimuon/invariantMass_GlobalKine_GlobalMuonCuts", "#mu^{+}#mu^{-} invariant mass (global muon cuts, global kine)", {HistType::kTH1F, {invMassAxis}}); + // MCH-MID tracks with MFT acceptance cuts vs. muon tracks match type + registryDimuon.add("dimuon/MC/invariantMass_GlobalKine_GlobalMuonCuts_vs_match_type", "#mu^{+}#mu^{-} invariant mass vs. match tye (global muon cuts, global kine)", {HistType::kTH2F, {invMassAxis, matchTypeAxis}}); + // MFT-MCH-MID tracks with MFT acceptance cuts, good matches + registryDimuon.add("dimuon/invariantMass_GlobalKine_GlobalMuonCuts_GoodMatches", "#mu^{+}#mu^{-} invariant mass (global muon cuts, global kine, good matches)", {HistType::kTH1F, {invMassAxis}}); + // MFT-MCH-MID tracks with MFT acceptance cuts vs. muon tracks match type, good matches + registryDimuon.add("dimuon/MC/invariantMass_GlobalKine_GlobalMuonCuts_GoodMatches_vs_match_type", "#mu^{+}#mu^{-} invariant mass vs. match tye (global muon cuts, global kine, good matches)", {HistType::kTH2F, {invMassAxis, matchTypeAxis}}); } - void InitMatchingFunctions() + void initMatchingFunctions() { using SMatrix55Std = ROOT::Math::SMatrix; using SMatrix55Sym = ROOT::Math::SMatrix>; @@ -915,31 +1123,31 @@ struct qaMatching { mMatchingFunctionMap["matchALL"] = [](const o2::dataformats::GlobalFwdTrack& mchTrack, const o2::track::TrackParCovFwd& mftTrack) -> std::tuple { // Match two tracks evaluating all parameters: X,Y, phi, tanl & q/pt - SMatrix55Sym H_k, V_k; - SVector5 m_k(mftTrack.getX(), mftTrack.getY(), mftTrack.getPhi(), - mftTrack.getTanl(), mftTrack.getInvQPt()), - r_k_kminus1; - SVector5 GlobalMuonTrackParameters = mchTrack.getParameters(); - SMatrix55Sym GlobalMuonTrackCovariances = mchTrack.getCovariances(); - V_k(0, 0) = mftTrack.getCovariances()(0, 0); - V_k(1, 1) = mftTrack.getCovariances()(1, 1); - V_k(2, 2) = mftTrack.getCovariances()(2, 2); - V_k(3, 3) = mftTrack.getCovariances()(3, 3); - V_k(4, 4) = mftTrack.getCovariances()(4, 4); - H_k(0, 0) = 1.0; - H_k(1, 1) = 1.0; - H_k(2, 2) = 1.0; - H_k(3, 3) = 1.0; - H_k(4, 4) = 1.0; + SMatrix55Sym hK, vK; + SVector5 mK(mftTrack.getX(), mftTrack.getY(), mftTrack.getPhi(), + mftTrack.getTanl(), mftTrack.getInvQPt()), + rKKminus1; + const SVector5& globalMuonTrackParameters = mchTrack.getParameters(); + const SMatrix55Sym& globalMuonTrackCovariances = mchTrack.getCovariances(); + vK(0, 0) = mftTrack.getCovariances()(0, 0); + vK(1, 1) = mftTrack.getCovariances()(1, 1); + vK(2, 2) = mftTrack.getCovariances()(2, 2); + vK(3, 3) = mftTrack.getCovariances()(3, 3); + vK(4, 4) = mftTrack.getCovariances()(4, 4); + hK(0, 0) = 1.0; + hK(1, 1) = 1.0; + hK(2, 2) = 1.0; + hK(3, 3) = 1.0; + hK(4, 4) = 1.0; // Covariance of residuals - SMatrix55Std invResCov = (V_k + ROOT::Math::Similarity(H_k, GlobalMuonTrackCovariances)); + SMatrix55Std invResCov = (vK + ROOT::Math::Similarity(hK, globalMuonTrackCovariances)); invResCov.Invert(); // Update Parameters - r_k_kminus1 = m_k - H_k * GlobalMuonTrackParameters; // Residuals of prediction + rKKminus1 = mK - hK * globalMuonTrackParameters; // Residuals of prediction - auto matchChi2Track = ROOT::Math::Similarity(r_k_kminus1, invResCov); + auto matchChi2Track = ROOT::Math::Similarity(rKKminus1, invResCov); // return chi2 and NDF return {matchChi2Track, 5}; @@ -949,30 +1157,30 @@ struct qaMatching { mMatchingFunctionMap["matchXYPhiTanl"] = [](const o2::dataformats::GlobalFwdTrack& mchTrack, const o2::track::TrackParCovFwd& mftTrack) -> std::tuple { // Match two tracks evaluating positions & angles - SMatrix45 H_k; - SMatrix44 V_k; - SVector4 m_k(mftTrack.getX(), mftTrack.getY(), mftTrack.getPhi(), - mftTrack.getTanl()), - r_k_kminus1; - SVector5 GlobalMuonTrackParameters = mchTrack.getParameters(); - SMatrix55Sym GlobalMuonTrackCovariances = mchTrack.getCovariances(); - V_k(0, 0) = mftTrack.getCovariances()(0, 0); - V_k(1, 1) = mftTrack.getCovariances()(1, 1); - V_k(2, 2) = mftTrack.getCovariances()(2, 2); - V_k(3, 3) = mftTrack.getCovariances()(3, 3); - H_k(0, 0) = 1.0; - H_k(1, 1) = 1.0; - H_k(2, 2) = 1.0; - H_k(3, 3) = 1.0; + SMatrix45 hK; + SMatrix44 vK; + SVector4 mK(mftTrack.getX(), mftTrack.getY(), mftTrack.getPhi(), + mftTrack.getTanl()), + rKKminus1; + const SVector5& globalMuonTrackParameters = mchTrack.getParameters(); + const SMatrix55Sym& globalMuonTrackCovariances = mchTrack.getCovariances(); + vK(0, 0) = mftTrack.getCovariances()(0, 0); + vK(1, 1) = mftTrack.getCovariances()(1, 1); + vK(2, 2) = mftTrack.getCovariances()(2, 2); + vK(3, 3) = mftTrack.getCovariances()(3, 3); + hK(0, 0) = 1.0; + hK(1, 1) = 1.0; + hK(2, 2) = 1.0; + hK(3, 3) = 1.0; // Covariance of residuals - SMatrix44 invResCov = (V_k + ROOT::Math::Similarity(H_k, GlobalMuonTrackCovariances)); + SMatrix44 invResCov = (vK + ROOT::Math::Similarity(hK, globalMuonTrackCovariances)); invResCov.Invert(); // Residuals of prediction - r_k_kminus1 = m_k - H_k * GlobalMuonTrackParameters; + rKKminus1 = mK - hK * globalMuonTrackParameters; - auto matchChi2Track = ROOT::Math::Similarity(r_k_kminus1, invResCov); + auto matchChi2Track = ROOT::Math::Similarity(rKKminus1, invResCov); // return chi2 and NDF return {matchChi2Track, 4}; @@ -982,23 +1190,23 @@ struct qaMatching { mMatchingFunctionMap["matchXY"] = [](const o2::dataformats::GlobalFwdTrack& mchTrack, const o2::track::TrackParCovFwd& mftTrack) -> std::tuple { // Calculate Matching Chi2 - X and Y positions - SMatrix25 H_k; - SMatrix22 V_k; - SVector2 m_k(mftTrack.getX(), mftTrack.getY()), r_k_kminus1; - SVector5 GlobalMuonTrackParameters = mchTrack.getParameters(); - SMatrix55Sym GlobalMuonTrackCovariances = mchTrack.getCovariances(); - V_k(0, 0) = mftTrack.getCovariances()(0, 0); - V_k(1, 1) = mftTrack.getCovariances()(1, 1); - H_k(0, 0) = 1.0; - H_k(1, 1) = 1.0; + SMatrix25 hK; + SMatrix22 vK; + SVector2 mK(mftTrack.getX(), mftTrack.getY()), rKKminus1; + const SVector5& globalMuonTrackParameters = mchTrack.getParameters(); + const SMatrix55Sym& globalMuonTrackCovariances = mchTrack.getCovariances(); + vK(0, 0) = mftTrack.getCovariances()(0, 0); + vK(1, 1) = mftTrack.getCovariances()(1, 1); + hK(0, 0) = 1.0; + hK(1, 1) = 1.0; // Covariance of residuals - SMatrix22 invResCov = (V_k + ROOT::Math::Similarity(H_k, GlobalMuonTrackCovariances)); + SMatrix22 invResCov = (vK + ROOT::Math::Similarity(hK, globalMuonTrackCovariances)); invResCov.Invert(); // Residuals of prediction - r_k_kminus1 = m_k - H_k * GlobalMuonTrackParameters; - auto matchChi2Track = ROOT::Math::Similarity(r_k_kminus1, invResCov); + rKKminus1 = mK - hK * globalMuonTrackParameters; + auto matchChi2Track = ROOT::Math::Similarity(rKKminus1, invResCov); // return reduced chi2 return {matchChi2Track, 2}; @@ -1008,10 +1216,10 @@ struct qaMatching { void init(o2::framework::InitContext&) { // Load geometry - ccdbManager->setURL(ccdburl); + ccdbManager->setURL(ccdbUrl); ccdbManager->setCaching(true); ccdbManager->setLocalObjectValidityChecking(); - fCCDBApi.init(ccdburl); + fCCDBApi.init(ccdbUrl); mRunNumber = 0; if (!o2::base::GeometryManager::isGeometryLoaded()) { @@ -1019,23 +1227,27 @@ struct qaMatching { ccdbManager->get(geoPath); } + std::vector matchMethodLabels{"Prod"}; + // Matching functions - InitMatchingFunctions(); - for (size_t funcId = 0; funcId < sChi2FunctionsNum; funcId++) { - auto label = fConfigChi2MatchingOptions.fFunctionLabel[funcId]->value; - auto funcName = fConfigChi2MatchingOptions.fFunctionName[funcId]->value; - auto scoreMin = fConfigChi2MatchingOptions.fMatchingScoreCut[funcId]->value; - auto matchingPlaneZ = fConfigChi2MatchingOptions.fMatchingPlaneZ[funcId]->value; - auto extrapMethod = fConfigChi2MatchingOptions.fMatchingExtrapMethod[funcId]->value; + initMatchingFunctions(); + for (size_t funcId = 0; funcId < Chi2FunctionsNum; funcId++) { + auto label = configChi2MatchingOptions.functionLabels[funcId]->value; + auto funcName = configChi2MatchingOptions.functionNames[funcId]->value; + auto scoreMin = configChi2MatchingOptions.matchingScoreCuts[funcId]->value; + auto matchingPlaneZ = configChi2MatchingOptions.matchingPlaneZs[funcId]->value; + auto extrapMethod = configChi2MatchingOptions.matchingExtrapMethods[funcId]->value; if (label == "" || funcName == "") - break; + continue; matchingChi2Functions[label] = funcName; matchingScoreCuts[label] = scoreMin; matchingPlanesZ[label] = matchingPlaneZ; matchingExtrapMethod[label] = extrapMethod; + + matchMethodLabels.push_back(label); } // Matching ML models @@ -1045,49 +1257,61 @@ struct qaMatching { cutDirMl = {cuts_ml::CutNot}; o2::framework::LabeledArray mycutsMl(cutValues.data(), 1, 1, std::vector{"pT bin 0"}, std::vector{"score"}); - for (size_t modelId = 0; modelId < sMLModelsNum; modelId++) { - auto label = fConfigMlOptions.fModelLabel[modelId]->value; - auto modelPath = fConfigMlOptions.fModelPathCCDB[modelId]->value; - auto inputFeatures = fConfigMlOptions.fInputFeatures[modelId]->value; - auto modelName = fConfigMlOptions.fModelName[modelId]->value; - auto scoreMin = fConfigMlOptions.fMatchingScoreCut[modelId]->value; - auto matchingPlaneZ = fConfigMlOptions.fMatchingPlaneZ[modelId]->value; - auto extrapMethod = fConfigMlOptions.fMatchingExtrapMethod[modelId]->value; + for (size_t modelId = 0; modelId < MlModelsNum; modelId++) { + auto label = configMlOptions.modelLabels[modelId]->value; + auto modelPath = configMlOptions.modelPathCcds[modelId]->value; + auto inputFeatures = configMlOptions.inputFeatures[modelId]->value; + auto modelName = configMlOptions.modelNames[modelId]->value; + auto scoreMin = configMlOptions.matchingScoreCuts[modelId]->value; + auto matchingPlaneZ = configMlOptions.matchingPlaneZs[modelId]->value; + auto extrapMethod = configMlOptions.matchingExtrapMethods[modelId]->value; - if (label == "" || modelPath == "" || inputFeatures == "" || modelName == "") - break; + if (label == "" || modelPath == "" || inputFeatures.empty() || modelName == "") + continue; matchingMlResponses[label].configure(binsPtMl, mycutsMl, cutDirMl, 1); - matchingMlResponses[label].setModelPathsCCDB(std::vector{modelName}, fCCDBApi, std::vector{modelPath}, fConfigCCDB.fConfigNoLaterThan.value); - matchingMlResponses[label].cacheInputFeaturesIndices(std::vector{inputFeatures}); + matchingMlResponses[label].setModelPathsCCDB(std::vector{modelName}, fCCDBApi, std::vector{modelPath}, configCcdb.cfgCcdbNoLaterThan.value); + matchingMlResponses[label].cacheInputFeaturesIndices(inputFeatures); matchingMlResponses[label].init(); matchingScoreCuts[label] = scoreMin; matchingPlanesZ[label] = matchingPlaneZ; matchingExtrapMethod[label] = extrapMethod; + + matchMethodLabels.push_back(label); } int nTrackTypes = static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MCHStandaloneTrack) + 1; - AxisSpec trackTypeAxis = {static_cast(nTrackTypes), 0.0, static_cast(nTrackTypes), "track type"}; + AxisSpec trackTypeAxis = {nTrackTypes, 0.0, static_cast(nTrackTypes), "track type"}; registry.add("nTracksPerType", "Number of tracks per type", {HistType::kTH1F, {trackTypeAxis}}); - AxisSpec tracksMultiplicityAxis = {fMftTrackMultiplicityMax, 0, static_cast(fMftTrackMultiplicityMax), "tracks multiplicity"}; + AxisSpec tracksMultiplicityAxis = {cfgMftTrackMultiplicityMax, 0, static_cast(cfgMftTrackMultiplicityMax), "tracks multiplicity"}; registry.add("tracksMultiplicityMFT", "MFT tracks multiplicity", {HistType::kTH1F, {tracksMultiplicityAxis}}); registry.add("tracksMultiplicityMCH", "MCH tracks multiplicity", {HistType::kTH1F, {tracksMultiplicityAxis}}); - CreateMatchingHistosMC(); - CreateDimuonHistos(); + // Histogram for storing the index and label of the matching methods in the horizontal axis. + // This is needed in order to associate the proper label to the match method index in the derived tables. + AxisSpec matchMethodAxis = {static_cast(matchMethodLabels.size()), 0.0, static_cast(matchMethodLabels.size()), ""}; + auto matchMethodsHist = registry.add("matchMethods", "Match methods", {HistType::kTH1F, {matchMethodAxis}}); + for (size_t iLabel = 0; iLabel < matchMethodLabels.size(); iLabel++) { + std::get>(matchMethodsHist)->GetXaxis()->SetBinLabel(iLabel + 1, matchMethodLabels[iLabel].c_str()); + } + + createMatchingHistos(); + createDimuonHistos(); } template - bool pDCACut(const T& mchTrack, const C& collision, double nSigmaPDCA) + bool pDcaCut(const T& mchTrack, const C& collision, double nSigmaPDCA) { static const double sigmaPDCA23 = 80.; static const double sigmaPDCA310 = 54.; static const double relPRes = 0.0004; static const double slopeRes = 0.0005; - double thetaAbs = TMath::ATan(mchTrack.rAtAbsorberEnd() / 505.) * TMath::RadToDeg(); + constexpr double AbsorberEndZ = 505.; + constexpr double RadToDeg = 180. / o2::constants::math::PI; + double thetaAbs = std::atan(mchTrack.rAtAbsorberEnd() / AbsorberEndZ) * RadToDeg; // propagate muon track to vertex auto mchTrackAtVertex = VarManager::PropagateMuon(mchTrack, collision, VarManager::kToVertex); @@ -1096,11 +1320,11 @@ struct qaMatching { double p = mchTrackAtVertex.getP(); double pDCA = mchTrack.pDca(); - double sigmaPDCA = (thetaAbs < 3) ? sigmaPDCA23 : sigmaPDCA310; + double sigmaPDCA = (thetaAbs < ThetaAbsBoundaryDeg) ? sigmaPDCA23 : sigmaPDCA310; double nrp = nSigmaPDCA * relPRes * p; double pResEffect = sigmaPDCA / (1. - nrp / (1. + nrp)); - double slopeResEffect = 535. * slopeRes * p; - double sigmaPDCAWithRes = TMath::Sqrt(pResEffect * pResEffect + slopeResEffect * slopeResEffect); + double slopeResEffect = SlopeResolutionZ * slopeRes * p; + double sigmaPDCAWithRes = std::sqrt(pResEffect * pResEffect + slopeResEffect * slopeResEffect); if (pDCA > nSigmaPDCA * sigmaPDCAWithRes) { return false; } @@ -1109,7 +1333,7 @@ struct qaMatching { } template - bool IsGoodMuon(const T& mchTrack, const C& collision, + bool isGoodMuon(const T& mchTrack, const C& collision, double chi2Cut, double pCut, double pTCut, @@ -1144,7 +1368,7 @@ struct qaMatching { } // pDCA cut - if (!pDCACut(mchTrack, collision, nSigmaPdcaCut)) { + if (!pDcaCut(mchTrack, collision, nSigmaPdcaCut)) { return false; } @@ -1152,19 +1376,19 @@ struct qaMatching { } template - bool IsGoodMuon(const T& muonTrack, const C& collision) + bool isGoodMuon(const T& muonTrack, const C& collision) { - return IsGoodMuon(muonTrack, collision, fTrackChi2MchUp, fPMchLow, fPtMchLow, {fEtaMchLow, fEtaMchUp}, {fRabsLow, fRabsUp}, fSigmaPdcaUp); + return isGoodMuon(muonTrack, collision, cfgTrackChi2MchUp, cfgPMchLow, cfgPtMchLow, {cfgEtaMchLow, cfgEtaMchUp}, {cfgRabsLow, cfgRabsUp}, cfgPdcaUp); } template - bool IsGoodGlobalMuon(const T& muonTrack, const C& collision) + bool isGoodGlobalMuon(const T& muonTrack, const C& collision) { - return IsGoodMuon(muonTrack, collision, fTrackChi2MchUp, fPMchLow, fPtMchLow, {fEtaMftLow, fEtaMftUp}, {fRabsLow, fRabsUp}, fSigmaPdcaUp); + return isGoodMuon(muonTrack, collision, cfgTrackChi2MchUp, cfgPMchLow, cfgPtMchLow, {cfgEtaMftLow, cfgEtaMftUp}, {cfgRabsLow, cfgRabsUp}, cfgPdcaUp); } template - bool IsGoodMFT(const T& mftTrack, + bool isGoodMft(const T& mftTrack, double chi2Cut, int nClustersCut) { @@ -1183,58 +1407,54 @@ struct qaMatching { } template - bool IsGoodMFT(const T& mftTrack) + bool isGoodMft(const T& mftTrack) { - return IsGoodMFT(mftTrack, fTrackChi2MftUp, fTrackNClustMftLow); + return isGoodMft(mftTrack, cfgTrackChi2MftUp, cfgTrackNClustMftLow); } - template - bool IsGoodGlobalMatching(const TMUON& muonTrack, - double matchingScore, + bool isGoodGlobalMatching(const MatchingCandidate& candidate, double matchingScoreCut) { - if (static_cast(muonTrack.trackType()) > 2) + if (static_cast(candidate.trackType) > GlobalTrackTypeMax) return false; // MFT-MCH matching score cut - if (matchingScore < matchingScoreCut) + if (candidate.matchScore < matchingScoreCut) return false; return true; } - template - bool IsGoodGlobalMatching(const TMUON& muonTrack, double matchingScore) + bool isGoodGlobalMatching(const MatchingCandidate& candidate) { - return IsGoodGlobalMatching(muonTrack, matchingScore, fMatchingChi2ScoreMftMchLow); + return isGoodGlobalMatching(candidate, cfgMatchingChi2ScoreMftMchLow); } - template - bool IsTrueGlobalMatching(const TMUON& muonTrack, const std::vector>& matchablePairs) + bool isTrueGlobalMatching(const MatchingCandidate& candidate, const std::vector>& matchablePairs) { - if (static_cast(muonTrack.trackType()) > 2) + if (candidate.trackType > GlobalTrackTypeMax) return false; - int64_t mchTrackId = static_cast(muonTrack.matchMCHTrackId()); - int64_t mftTrackId = static_cast(muonTrack.matchMFTTrackId()); + int64_t mchTrackId = candidate.muonTrackId; + int64_t mftTrackId = candidate.mftTrackId; std::pair trackIndexes = std::make_pair(mchTrackId, mftTrackId); return (std::find(matchablePairs.begin(), matchablePairs.end(), trackIndexes) != matchablePairs.end()); } - bool IsMatchableMCH(int64_t mchTrackId, const std::vector>& matchablePairs) + bool isMatchableMch(int64_t mchTrackId, const std::vector>& matchablePairs) { - for (auto [id1, id2] : matchablePairs) { + for (const auto& [id1, id2] : matchablePairs) { if (mchTrackId == id1) return true; } return false; } - std::optional> GetMatchablePairForMCH(int64_t mchTrackId, const std::vector>& matchablePairs) + std::optional> getMatchablePairForMch(int64_t mchTrackId, const std::vector>& matchablePairs) { - for (auto pair : matchablePairs) { + for (const auto& pair : matchablePairs) { if (mchTrackId == pair.first) return pair; } @@ -1242,7 +1462,7 @@ struct qaMatching { } template - o2::dataformats::GlobalFwdTrack FwdToTrackPar(const T& track) + o2::dataformats::GlobalFwdTrack fwdToTrackPar(const T& track) { double chi2 = track.chi2(); SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); @@ -1259,7 +1479,7 @@ struct qaMatching { } template - o2::dataformats::GlobalFwdTrack FwdToTrackPar(const T& track, const C& cov) + o2::dataformats::GlobalFwdTrack fwdToTrackPar(const T& track, const C& cov) { double chi2 = track.chi2(); SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); @@ -1275,7 +1495,7 @@ struct qaMatching { return fwdtrack; } - o2::dataformats::GlobalFwdTrack PropagateToZMCH(const o2::dataformats::GlobalFwdTrack& muon, const double z) + o2::dataformats::GlobalFwdTrack propagateToZMch(const o2::dataformats::GlobalFwdTrack& muon, const double z) { auto mchTrack = mExtrap.FwdtoMCH(muon); @@ -1300,7 +1520,7 @@ struct qaMatching { } template - o2::dataformats::GlobalFwdTrack PropagateToZMCH(const T& muon, const double z) + o2::dataformats::GlobalFwdTrack propagateToZMch(const T& muon, const double z) { double chi2 = muon.chi2(); SMatrix5 tpars(muon.x(), muon.y(), muon.phi(), muon.tgl(), muon.signed1Pt()); @@ -1314,10 +1534,10 @@ struct qaMatching { track.setZ(fwdtrack.getZ()); track.setCovariances(fwdtrack.getCovariances()); - return PropagateToZMCH(track, z); + return propagateToZMch(track, z); } - o2::dataformats::GlobalFwdTrack PropagateToZMFT(const o2::dataformats::GlobalFwdTrack& mftTrack, const double z) + o2::dataformats::GlobalFwdTrack propagateToZMft(const o2::dataformats::GlobalFwdTrack& mftTrack, const double z) { o2::dataformats::GlobalFwdTrack fwdtrack{mftTrack}; fwdtrack.propagateToZ(z, mBzAtMftCenter); @@ -1325,10 +1545,10 @@ struct qaMatching { } template - o2::dataformats::GlobalFwdTrack PropagateToZMFT(const TMFT& mftTrack, const CMFT& mftCov, const double z) + o2::dataformats::GlobalFwdTrack propagateToZMft(const TMFT& mftTrack, const CMFT& mftCov, const double z) { - o2::dataformats::GlobalFwdTrack fwdtrack = FwdToTrackPar(mftTrack, mftCov); - return PropagateToZMFT(fwdtrack, z); + o2::dataformats::GlobalFwdTrack fwdtrack = fwdToTrackPar(mftTrack, mftCov); + return propagateToZMft(fwdtrack, z); } // method 0: standard extrapolation @@ -1336,60 +1556,60 @@ struct qaMatching { // method 2: MCH track extrapolation constrained to the first MFT track point, MFT extrapolation using MCH momentum // method 3: MCH track extrapolation constrained to the collision point, MFT extrapolation using MCH momentum template - o2::dataformats::GlobalFwdTrack PropagateToMatchingPlaneMCH(const TMCH& mchTrack, const TMFT& mftTrack, const CMFT& mftTrackCov, const C& collision, const double z, int method) + o2::dataformats::GlobalFwdTrack propagateToMatchingPlaneMch(const TMCH& mchTrack, const TMFT& mftTrack, const CMFT& mftTrackCov, const C& collision, const double z, int method) { - if (method == 0 || method == 1) { + if (method == ExtrapolationMethodStandard || method == 1) { // simple extrapolation upstream through the absorber - return PropagateToZMCH(mchTrack, z); + return propagateToZMch(mchTrack, z); } - if (method == 2) { + if (method == ExtrapolationMethodMftFirstPoint) { // extrapolation to the first MFT point and then back to the matching plane - auto mftTrackPar = FwdToTrackPar(mftTrack, mftTrackCov); - // std::cout << std::format("[PropagateToMatchingPlaneMCH] extrapolating to MFT: x={:0.3f} y={:0.3f} z={:0.3f}", mftTrackPar.getX(), mftTrackPar.getY(), mftTrackPar.getZ()) << std::endl; - auto mchTrackAtMFT = PropagateToVertexMCH(FwdToTrackPar(mchTrack, mchTrack), + auto mftTrackPar = fwdToTrackPar(mftTrack, mftTrackCov); + // std::cout << std::format("[propagateToMatchingPlaneMch] extrapolating to MFT: x={:0.3f} y={:0.3f} z={:0.3f}", mftTrackPar.getX(), mftTrackPar.getY(), mftTrackPar.getZ()) << std::endl; + auto mchTrackAtMFT = propagateToVertexMch(fwdToTrackPar(mchTrack, mchTrack), mftTrackPar.getX(), mftTrackPar.getY(), mftTrackPar.getZ(), mftTrackPar.getSigma2X(), mftTrackPar.getSigma2Y()); - // std::cout << std::format("[PropagateToMatchingPlaneMCH] extrapolating to z={:0.3f}", z) << std::endl; - return PropagateToZMCH(mchTrackAtMFT, z); + // std::cout << std::format("[propagateToMatchingPlaneMch] extrapolating to z={:0.3f}", z) << std::endl; + return propagateToZMch(mchTrackAtMFT, z); } - if (method == 3) { + if (method == ExtrapolationMethodVertex) { // extrapolation to the vertex and then back to the matching plane auto mchTrackAtVertex = VarManager::PropagateMuon(mchTrack, collision, VarManager::kToVertex); - return PropagateToZMCH(mchTrackAtVertex, z); + return propagateToZMch(mchTrackAtVertex, z); } - if (method == 4) { + if (method == ExtrapolationMethodMftDca) { // extrapolation to the MFT DCA and then back to the matching plane - auto mftTrackDCA = PropagateToZMFT(FwdToTrackPar(mftTrack, mftTrackCov), collision.posZ()); - auto mchTrackAtDCA = PropagateToVertexMCH(FwdToTrackPar(mchTrack, mchTrack), + auto mftTrackDCA = propagateToZMft(fwdToTrackPar(mftTrack, mftTrackCov), collision.posZ()); + auto mchTrackAtDCA = propagateToVertexMch(fwdToTrackPar(mchTrack, mchTrack), mftTrackDCA.getX(), mftTrackDCA.getY(), mftTrackDCA.getZ(), mftTrackDCA.getSigma2X(), mftTrackDCA.getSigma2Y()); - return PropagateToZMCH(mchTrackAtDCA, z); + return propagateToZMch(mchTrackAtDCA, z); } return {}; } template - o2::dataformats::GlobalFwdTrack PropagateToMatchingPlaneMFT(const TMCH& mchTrack, const TMFT& mftTrack, const CMFT& mftTrackCov, const C& collision, const double z, int method) + o2::dataformats::GlobalFwdTrack propagateToMatchingPlaneMft(const TMCH& mchTrack, const TMFT& mftTrack, const CMFT& mftTrackCov, const C& collision, const double z, int method) { - if (method == 0) { + if (method == ExtrapolationMethodStandard) { // extrapolation with MFT tools - return PropagateToZMFT(mftTrack, mftTrackCov, z); + return propagateToZMft(mftTrack, mftTrackCov, z); } if (method > 0) { // extrapolation with MCH tools auto mchTrackAtVertex = VarManager::PropagateMuon(mchTrack, collision, VarManager::kToVertex); double pMCH = mchTrackAtVertex.getP(); - double px = pMCH * sin(M_PI / 2 - atan(mftTrack.tgl())) * cos(mftTrack.phi()); - double py = pMCH * sin(M_PI / 2 - atan(mftTrack.tgl())) * sin(mftTrack.phi()); - double pt = std::sqrt(std::pow(px, 2) + std::pow(py, 2)); + double px = pMCH * std::sin(o2::constants::math::PIHalf - std::atan(mftTrack.tgl())) * std::cos(mftTrack.phi()); + double py = pMCH * std::sin(o2::constants::math::PIHalf - std::atan(mftTrack.tgl())) * std::sin(mftTrack.phi()); + double pt = std::hypot(px, py); double sign = mchTrack.sign(); - o2::dataformats::GlobalFwdTrack track = FwdToTrackPar(mftTrack, mftTrackCov); + o2::dataformats::GlobalFwdTrack track = fwdToTrackPar(mftTrack, mftTrackCov); // update momentum in track parameters and errors auto newCov = track.getCovariances(); @@ -1412,7 +1632,7 @@ struct qaMatching { return {}; } - o2::dataformats::GlobalFwdTrack PropagateToVertexMCH(const o2::dataformats::GlobalFwdTrack& muon, + o2::dataformats::GlobalFwdTrack propagateToVertexMch(const o2::dataformats::GlobalFwdTrack& muon, const double vx, const double vy, const double vz, const double covVx, const double covVy) { @@ -1430,73 +1650,58 @@ struct qaMatching { } template - o2::dataformats::GlobalFwdTrack PropagateToVertexMCH(const TMCH& muon, + o2::dataformats::GlobalFwdTrack propagateToVertexMch(const TMCH& muon, const C& collision) { - return PropagateToVertexMCH(FwdToTrackPar(muon, muon), + return propagateToVertexMch(fwdToTrackPar(muon, muon), collision.posX(), collision.posY(), collision.posZ(), - collision.covXX(), - collision.covYY()); - } - - o2::dataformats::GlobalFwdTrack PropagateToVertexMFT(o2::dataformats::GlobalFwdTrack muon, - const float vx, const float vy, const float vz, - const float covVx, const float covVy) - { - o2::dataformats::GlobalFwdTrack propmuon; - auto geoMan = o2::base::GeometryManager::meanMaterialBudget(muon.getX(), muon.getY(), muon.getZ(), vx, vy, vz); - auto x2x0 = static_cast(geoMan.meanX2X0); - muon.propagateToVtxhelixWithMCS(vz, {vx, vy}, {covVx, covVy}, mBzAtMftCenter, x2x0); - propmuon.setParameters(muon.getParameters()); - propmuon.setZ(muon.getZ()); - propmuon.setCovariances(muon.getCovariances()); - - return propmuon; + std::sqrt(collision.covXX()), + std::sqrt(collision.covYY())); } template - o2::dataformats::GlobalFwdTrack PropagateToVertexMFT(const TMFT& muon, + o2::dataformats::GlobalFwdTrack propagateToVertexMft(const TMFT& muon, const C& collision) { - return PropagateToVertexMFT(FwdToTrackPar(muon), - collision.posX(), - collision.posY(), - collision.posZ(), - collision.covXX(), - collision.covYY()); + auto fwdTrackProp = fwdtrackutils::propagateTrackParCovFwd(fwdToTrackPar(muon), + 0, + collision, + fwdtrackutils::propagationPoint::kToVertex, + 0, + mBzAtMftCenter); + + return fwdTrackProp; } template - o2::dataformats::GlobalFwdTrack PropagateToVertexMFT(const TMFT& muon, + o2::dataformats::GlobalFwdTrack propagateToVertexMft(const TMFT& mftTrack, const TMCH& mchTrack, const C& collision) { // extrapolation with MCH tools - auto mchTrackAtMFT = mExtrap.FwdtoMCH(FwdToTrackPar(mchTrack)); - o2::mch::TrackExtrap::extrapToVertexWithoutBranson(mchTrackAtMFT, muon.z()); - - auto muonTrackProp = mExtrap.FwdtoMCH(FwdToTrackPar(muon)); - - // update global track momentum from the MCH track - double pRatio = muonTrackProp.p() / mchTrackAtMFT.p(); - double newInvBendMom = muonTrackProp.getInverseBendingMomentum() * pRatio; - muonTrackProp.setInverseBendingMomentum(newInvBendMom); - muonTrackProp.setCharge(mchTrackAtMFT.getCharge()); - - o2::mch::TrackExtrap::extrapToVertex(muonTrackProp, - collision.posX(), - collision.posY(), - collision.posZ(), - collision.covXX(), - collision.covYY()); - - return mExtrap.MCHtoFwd(muonTrackProp); + auto mchTrackAtMFT = mExtrap.FwdtoMCH(fwdToTrackPar(mchTrack)); + o2::mch::TrackExtrap::extrapToVertex(mchTrackAtMFT, + mftTrack.x(), + mftTrack.y(), + mftTrack.z(), + 0, 0); + + auto fwdTrackRefit = fwdtrackutils::refitGlobalMuonCov(mExtrap.MCHtoFwd(mchTrackAtMFT), fwdToTrackPar(mftTrack)); + + auto fwdTrackProp = fwdtrackutils::propagateTrackParCovFwd(fwdTrackRefit, + 0, + collision, + fwdtrackutils::propagationPoint::kToVertex, + 0, + mBzAtMftCenter); + + return fwdTrackProp; } template - void GetMotherParticles(MCP const& mcParticle, std::vector>& motherParticlesVec) + void getMotherParticles(MCP const& mcParticle, std::vector>& motherParticlesVec) { const auto& motherParticles = mcParticle.template mothers_as(); if (motherParticles.empty()) { @@ -1505,11 +1710,11 @@ struct qaMatching { const auto& motherParticle = motherParticles[0]; motherParticlesVec.emplace_back(std::make_pair(static_cast(motherParticle.pdgCode()), static_cast(motherParticle.globalIndex()))); - GetMotherParticles(motherParticle, motherParticlesVec); + getMotherParticles(motherParticle, motherParticlesVec); } template - std::vector> GetMotherParticles(T const& track) + std::vector> getMotherParticles(T const& track) { std::vector> result; if (!track.has_mcParticle()) @@ -1518,15 +1723,15 @@ struct qaMatching { const auto& mcParticle = track.mcParticle(); result.emplace_back(std::make_pair(static_cast(mcParticle.pdgCode()), static_cast(mcParticle.globalIndex()))); - GetMotherParticles(mcParticle, result); + getMotherParticles(mcParticle, result); return result; } template - int GetDecayRanking(TMCH const& mchTrack, TMFTs const& mftTracks) + int getDecayRanking(TMCH const& mchTrack, TMFTs const& mftTracks) { - auto mchMotherParticles = GetMotherParticles(mchTrack); + auto mchMotherParticles = getMotherParticles(mchTrack); int decayRanking = 0; // search for an MFT track that is associated to one of the MCH mother particles @@ -1555,14 +1760,14 @@ struct qaMatching { } template - void FillMatchablePairs(CollisionInfo& collisionInfo, + void fillMatchablePairs(CollisionInfo& collisionInfo, TMUON const& muonTracks, TMFT const& mftTracks) { collisionInfo.matchablePairs.clear(); for (const auto& muonTrack : muonTracks) { // only consider MCH standalone or MCH-MID matches - if (static_cast(muonTrack.trackType()) <= 2) + if (static_cast(muonTrack.trackType()) <= GlobalTrackTypeMax) continue; // only consider tracks associated to the current collision @@ -1576,8 +1781,6 @@ struct qaMatching { continue; // get the index associated to the MC particle auto muonMcParticle = muonTrack.mcParticle(); - if (std::abs(muonMcParticle.pdgCode()) != 13) - continue; int64_t muonMcTrackIndex = muonMcParticle.globalIndex(); @@ -1597,9 +1800,7 @@ struct qaMatching { } } - template - int GetTrueMatchIndex(TMUON const& muonTracks, - const std::vector& matchCandidatesVector, + int getTrueMatchIndex(const std::vector& matchCandidatesVector, const std::vector>& matchablePairs) { // find the index of the matching candidate that corresponds to the true match @@ -1607,9 +1808,7 @@ struct qaMatching { // index=0 means no candidate was found that corresponds to the true match int trueMatchIndex = 0; for (size_t i = 0; i < matchCandidatesVector.size(); i++) { - auto const& muonTrack = muonTracks.rawIteratorAt(matchCandidatesVector[i].globalTrackId); - - if (IsTrueGlobalMatching(muonTrack, matchablePairs)) { + if (isTrueGlobalMatching(matchCandidatesVector[i], matchablePairs)) { trueMatchIndex = i + 1; break; } @@ -1618,7 +1817,7 @@ struct qaMatching { } template - bool IsMuon(const TMCH& mchTrack, + bool isMuon(const TMCH& mchTrack, const TMFT& mftTrack) { // skip tracks that do not have an associated MC particle @@ -1633,76 +1832,70 @@ struct qaMatching { if (mchMcParticle.globalIndex() != mftMcParticle.globalIndex()) return false; - if (std::abs(mchMcParticle.pdgCode()) != 13) + if (std::abs(mchMcParticle.pdgCode()) != kMuonMinus) return false; return true; } template - bool IsMuon(const TMUON& muonTrack, + bool isMuon(const TMUON& muonTrack, TMUONS const& /*muonTracks*/, TMFTS const& /*mftTracks*/) { - if (static_cast(muonTrack.trackType()) >= 2) + if (static_cast(muonTrack.trackType()) >= GlobalTrackTypeMax) return false; auto const& mchTrack = muonTrack.template matchMCHTrack_as(); auto const& mftTrack = muonTrack.template matchMFTTrack_as(); - return IsMuon(mchTrack, mftTrack); + return isMuon(mchTrack, mftTrack); } - template - MuonMatchType GetMatchType(const TMUON& muonTrack, + template + MuonMatchType getMatchType(const MatchingCandidate& candidate, TMUONS const& muonTracks, TMFTS const& mftTracks, const std::vector>& matchablePairs, int ranking) { - if (static_cast(muonTrack.trackType()) > 2) + if (candidate.trackType > GlobalTrackTypeMax) return kMatchTypeUndefined; - auto const& mchTrack = muonTrack.template matchMCHTrack_as(); + auto const& mchTrack = muonTracks.rawIteratorAt(candidate.muonTrackId); - bool isPaired = IsMatchableMCH(mchTrack.globalIndex(), matchablePairs); - bool isMuon = IsMuon(muonTrack, muonTracks, mftTracks); - int decayRanking = GetDecayRanking(mchTrack, mftTracks); + bool isPairable = isMatchableMch(candidate.muonTrackId, matchablePairs); + bool isTrueMatch = isTrueGlobalMatching(candidate, matchablePairs); + int decayRanking = getDecayRanking(mchTrack, mftTracks); MuonMatchType result{kMatchTypeUndefined}; - if (isPaired) { - if (isMuon) { + if (isPairable) { + if (isTrueMatch) { result = (ranking == 1) ? kMatchTypeTrueLeading : kMatchTypeTrueNonLeading; } else { result = (ranking == 1) ? kMatchTypeWrongLeading : kMatchTypeWrongNonLeading; } - } else if (decayRanking == 2) { + } else if (decayRanking == DecayRankingDirect) { result = (ranking == 1) ? kMatchTypeDecayLeading : kMatchTypeDecayNonLeading; } else { result = (ranking == 1) ? kMatchTypeFakeLeading : kMatchTypeFakeNonLeading; } - if (result == kMatchTypeUndefined) { - std::cout << std::format("[GetMatchType] isPaired={} isMuon={} decayRanking={} result={}", - isPaired, isMuon, decayRanking, static_cast(result)) - << std::endl; - } - return result; } - // for each MCH standalone track, collect the associated matching candidates + // tag muons based on the track quality and the track position at the front and back MFT planes template - void GetSelectedMuons(const CollisionInfo& collisionInfo, - C const& collisions, - TMUON const& muonTracks, - std::vector& selectedMuons) + void getTaggedMuons(const CollisionInfo& collisionInfo, + C const& collisions, + TMUON const& muonTracks, + std::vector& taggedMuons) { - selectedMuons.clear(); - for (auto muonTrack : muonTracks) { + taggedMuons.clear(); + for (const auto& muonTrack : muonTracks) { // only consider MCH-MID matches - if (static_cast(muonTrack.trackType()) != 3) { + if (static_cast(muonTrack.trackType()) != MchMidTrackType) { continue; } @@ -1715,13 +1908,13 @@ struct qaMatching { const auto& collision = collisions.rawIteratorAt(muonTrack.collisionId()); // select MCH tracks with strict quality cuts - if (!IsGoodMuon(muonTrack, collision, - fMuonTaggingTrackChi2MchUp, - fMuonTaggingPMchLow, - fMuonTaggingPtMchLow, - {fMuonTaggingEtaMchLow, fMuonTaggingEtaMchUp}, - {fMuonTaggingRabsLow, fMuonTaggingRabsUp}, - fMuonTaggingSigmaPdcaUp)) { + if (!isGoodMuon(muonTrack, collision, + cfgMuonTaggingTrackChi2MchUp, + cfgMuonTaggingPMchLow, + cfgMuonTaggingPtMchLow, + {cfgMuonTaggingEtaMchLow, cfgMuonTaggingEtaMchUp}, + {cfgMuonTaggingRabsLow, cfgMuonTaggingRabsUp}, + cfgMuonTaggingPdcaUp)) { continue; } @@ -1729,66 +1922,32 @@ struct qaMatching { auto mchTrackAtVertex = VarManager::PropagateMuon(muonTrack, collision, VarManager::kToVertex); // propagate the track from the vertex to the first MFT plane - const auto& extrapToMFTfirst = PropagateToZMCH(mchTrackAtVertex, o2::mft::constants::mft::LayerZCoordinate()[0]); + const auto& extrapToMFTfirst = propagateToZMch(mchTrackAtVertex, o2::mft::constants::mft::LayerZCoordinate()[0]); double rFront = std::sqrt(extrapToMFTfirst.getX() * extrapToMFTfirst.getX() + extrapToMFTfirst.getY() * extrapToMFTfirst.getY()); - double rMinFront = 3.f; - double rMaxFront = 9.f; - if (rFront < rMinFront || rFront > rMaxFront) + if (rFront < cfgMuonTaggingRadiusAtMftFrontLow.value || rFront > cfgMuonTaggingRadiusAtMftFrontUp.value) continue; // propagate the track from the vertex to the last MFT plane - const auto& extrapToMFTlast = PropagateToZMCH(mchTrackAtVertex, o2::mft::constants::mft::LayerZCoordinate()[9]); + const auto& extrapToMFTlast = propagateToZMch(mchTrackAtVertex, o2::mft::constants::mft::LayerZCoordinate()[9]); double rBack = std::sqrt(extrapToMFTlast.getX() * extrapToMFTlast.getX() + extrapToMFTlast.getY() * extrapToMFTlast.getY()); - double rMinBack = 5.f; - double rMaxBack = 12.f; - if (rBack < rMinBack || rBack > rMaxBack) + if (rBack < cfgMuonTaggingRadiusAtMftBackLow.value || rBack > cfgMuonTaggingRadiusAtMftBackUp.value) continue; int64_t muonTrackIndex = muonTrack.globalIndex(); - selectedMuons.emplace_back(muonTrackIndex); + taggedMuons.emplace_back(muonTrackIndex); } } - // for each MCH standalone track, collect the associated matching candidates - template - void GetTaggedMuons(const CollisionInfo& collisionInfo, - TMUON const& muonTracks, - const std::vector& selectedMuons, - std::vector& taggedMuons) - { - taggedMuons.clear(); - for (auto [mchIndex, globalTracksVector] : collisionInfo.matchingCandidates) { - - // check if the current muon is selected - if (std::find(selectedMuons.begin(), selectedMuons.end(), mchIndex) == selectedMuons.end()) - continue; - - // if there is only one candidate, mark the muon as select - if (globalTracksVector.size() == 1) { - taggedMuons.emplace_back(mchIndex); - continue; - } - - auto const& muonTrack0 = muonTracks.rawIteratorAt(globalTracksVector[0].globalTrackId); - auto const& muonTrack1 = muonTracks.rawIteratorAt(globalTracksVector[1].globalTrackId); - - double chi2diff = muonTrack1.chi2MatchMCHMFT() - muonTrack0.chi2MatchMCHMFT(); - if (chi2diff < fMuonTaggingChi2DiffLow) - continue; - - taggedMuons.emplace_back(mchIndex); - } - } - - void GetMuonPairs(const CollisionInfo& collisionInfo, + void getMuonPairs(const CollisionInfo& collisionInfo, std::vector& muonPairs, std::vector& globalMuonPairs) { // outer loop over muon tracks - for (auto mchIndex1 : collisionInfo.mchTracks) { - + for (const auto& mchTrack1 : collisionInfo.mchTracks) { + auto mchIndex1 = mchTrack1.first; // inner loop over muon tracks - for (auto mchIndex2 : collisionInfo.mchTracks) { + for (const auto& mchTrack2 : collisionInfo.mchTracks) { + auto mchIndex2 = mchTrack2.first; // avoid double-counting of muon pairs if (mchIndex2 <= mchIndex1) continue; @@ -1799,10 +1958,10 @@ struct qaMatching { } // outer loop over global muon tracks - for (auto& [mchIndex1, matchingCandidates1] : collisionInfo.matchingCandidates) { + for (const auto& [mchIndex1, matchingCandidates1] : collisionInfo.matchingCandidates) { // inner loop over global muon tracks - for (auto& [mchIndex2, matchingCandidates2] : collisionInfo.matchingCandidates) { + for (const auto& [mchIndex2, matchingCandidates2] : collisionInfo.matchingCandidates) { // avoid double-counting of muon pairs if (mchIndex2 <= mchIndex1) continue; @@ -1813,7 +1972,7 @@ struct qaMatching { } } - double GetMuMuInvariantMass(const o2::mch::TrackParam& track1, const o2::mch::TrackParam& track2) + double getMuMuInvariantMass(const o2::mch::TrackParam& track1, const o2::mch::TrackParam& track2) { ROOT::Math::PxPyPzMVector muon1{ track1.px(), @@ -1832,7 +1991,7 @@ struct qaMatching { return dimuon.M(); } - double GetMuMuInvariantMass(const o2::dataformats::GlobalFwdTrack& track1, const o2::dataformats::GlobalFwdTrack& track2) + double getMuMuInvariantMass(const o2::dataformats::GlobalFwdTrack& track1, const o2::dataformats::GlobalFwdTrack& track2) { ROOT::Math::PxPyPzMVector muon1{ track1.getPx(), @@ -1851,46 +2010,50 @@ struct qaMatching { return dimuon.M(); } - template - int GetMftMchMatchAttempts(EVT const& collisions, - BC const& bcs, - TMUON const& mchTrack, - TMFTS const& mftTracks) + int getMftMchMatchAttempts(MchTrackInfo& mchTrackInfo, + const std::unordered_map& mftTracksInfos) { - if (!mchTrack.has_collision()) { - return 0; - } - const auto& collMch = collisions.rawIteratorAt(mchTrack.collisionId()); - const auto& bcMch = bcs.rawIteratorAt(collMch.bcId()); - + const auto& bcMch = mchTrackInfo.bc; int attempts{0}; - for (const auto& mftTrack : mftTracks) { - if (!mftTrack.has_collision()) { - continue; - } + for (const auto& [mftTrackIndex, mftTrackInfo] : mftTracksInfos) { + const auto& bcMft = mftTrackInfo.bc; - const auto& collMft = collisions.rawIteratorAt(mftTrack.collisionId()); - const auto& bcMft = bcs.rawIteratorAt(collMft.bcId()); - - int64_t deltaBc = static_cast(bcMft.globalBC()) - static_cast(bcMch.globalBC()); + int64_t deltaBc = bcMft - bcMch; double deltaBcNS = o2::constants::lhc::LHCBunchSpacingNS * deltaBc; - double deltaTrackTime = mftTrack.trackTime() - mchTrack.trackTime() + deltaBcNS; - double trackTimeResTot = mftTrack.trackTimeRes() + mchTrack.trackTimeRes(); + double deltaTrackTime = mftTrackInfo.time - mchTrackInfo.time + deltaBcNS; + double trackTimeResTot = mftTrackInfo.timeRes + mchTrackInfo.timeRes; if (std::fabs(deltaTrackTime) > trackTimeResTot) { continue; } attempts += 1; + mchTrackInfo.compatMftTracks.push_back(mftTrackIndex); } return attempts; } - template - void FillCollisions(EVT const& collisions, + template + void getMatchChi2AndScore(TMUON const& muonTrack, float& matchChi2, float& matchScore) + { + matchChi2 = muonTrack.chi2MatchMCHMFT() / MatchingDegreesOfFreedom; + matchScore = muonTrack.matchScoreMCHMFT(); + if (matchScore >= 0 && matchChi2 < 0) { + // match score from ML-based matching, we compute a chi2-like value from the score + float matchScoreInv = (matchScore > 0) ? 1.0 / matchScore : std::numeric_limits::max(); + matchChi2 = matchScoreInv - 1.f; + } else { + // we assume a standard chi2-based matching, and compute the score value from the chi2 + matchScore = chi2ToScore(muonTrack.chi2MatchMCHMFT(), MatchingDegreesOfFreedom, MatchingScoreChi2Max); + } + } + + template + void fillCollisions(EVT const& collisions, BC const& bcs, TMUON const& muonTracks, TMFT const& mftTracks, + MyMFTCovariances const& mftCovs, CollisionInfos& collisionInfos) { collisionInfos.clear(); @@ -1903,24 +2066,43 @@ struct qaMatching { if (collisionIds.empty()) return; - for (size_t cid = 1; cid < collisionIds.size() - 1; cid++) { + int64_t reducedMchTrackId = 0; + for (size_t cid = 0; cid < collisionIds.size(); cid++) { const auto& collision = collisions.rawIteratorAt(collisionIds[cid]); int64_t collisionIndex = collision.globalIndex(); auto bc = bcs.rawIteratorAt(collision.bcId()); - /*// require a minimum BC gap between the current conllision and the previous/next ones - const auto& collisionPrev = collisions.rawIteratorAt(collisionIds[cid-1]); - const auto& collisionNext = collisions.rawIteratorAt(collisionIds[cid+1]); - auto bcPrev = bcs.rawIteratorAt(collisionPrev.bcId()); - auto bcNext = bcs.rawIteratorAt(collisionNext.bcId()); - int64_t deltaPrev = bc.globalBC() - bcPrev.globalBC(); - int64_t deltaNext = bcNext.globalBC() - bc.globalBC(); - if (deltaPrev < 50 || deltaNext < 50) { + // remove vertices outside the allowed z range + if ((collision.posZ() < cfgVtxZLow.value) || (collision.posZ() > cfgVtxZUp.value)) { continue; - }*/ + } + + // fill collision information for MFT standalone tracks + for (const auto& mftTrack : mftTracks) { + if (!mftTrack.has_collision()) + continue; + + if (collisionIndex != mftTrack.collisionId()) { + continue; + } + + int64_t mftTrackIndex = mftTrack.globalIndex(); + + auto& collisionInfo = collisionInfos[collisionIndex]; + collisionInfo.index = collisionIndex; + collisionInfo.bc = bc.globalBC(); + collisionInfo.zVertex = collision.posZ(); + + collisionInfo.mftTracks.push_back(mftTrackIndex); + + auto& timeInfo = collisionInfo.mftTimeInfos[mftTrackIndex]; + timeInfo.bc = bc.globalBC(); + timeInfo.time = mftTrack.trackTime(); + timeInfo.timeRes = mftTrack.trackTimeRes(); + } // fill collision information for global muon tracks (MFT-MCH-MID matches) - for (auto muonTrack : muonTracks) { + for (const auto& muonTrack : muonTracks) { if (!muonTrack.has_collision()) continue; @@ -1933,24 +2115,46 @@ struct qaMatching { collisionInfo.bc = bc.globalBC(); collisionInfo.zVertex = collision.posZ(); - if (collisionInfo.matchablePairs.empty()) { - FillMatchablePairs(collisionInfo, muonTracks, mftTracks); + if constexpr (isMC) { + if (collisionInfo.matchablePairs.empty()) { + fillMatchablePairs(collisionInfo, muonTracks, mftTracks); + } } - if (static_cast(muonTrack.trackType()) > 2) { + if (static_cast(muonTrack.trackType()) > GlobalTrackTypeMax) { // standalone MCH or MCH-MID tracks int64_t mchTrackIndex = muonTrack.globalIndex(); - collisionInfo.mchTracks.push_back(mchTrackIndex); + auto& mchTrackInfo = collisionInfo.mchTracks[mchTrackIndex]; + mchTrackInfo.index = mchTrackIndex; + mchTrackInfo.bc = bc.globalBC(); + mchTrackInfo.time = muonTrack.trackTime(); + mchTrackInfo.timeRes = muonTrack.trackTimeRes(); + getMftMchMatchAttempts(mchTrackInfo, collisionInfo.mftTimeInfos); + + collisionInfo.reducedMchTrackIds[mchTrackIndex] = reducedMchTrackId; + reducedMchTrackId += 1; } else { // global muon tracks (MFT-MCH or MFT-MCH-MID) int64_t muonTrackIndex = muonTrack.globalIndex(); - double matchChi2 = muonTrack.chi2MatchMCHMFT() / 5.f; - double matchScore = chi2ToScore(muonTrack.chi2MatchMCHMFT(), 5, 50.f); + float matchChi2{-1}; + float matchScore{-1}; + getMatchChi2AndScore(muonTrack, matchChi2, matchScore); + auto const& mchTrack = muonTrack.template matchMCHTrack_as(); int64_t mchTrackIndex = mchTrack.globalIndex(); auto const& mftTrack = muonTrack.template matchMFTTrack_as(); int64_t mftTrackIndex = mftTrack.globalIndex(); + // get MFT track covariances + if (mftTrackCovs.count(mftTrack.globalIndex()) < 1) { + continue; + } + auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrack.globalIndex()]); + + // propagate MCH and MFT tracks to matching plane + auto mchTrackProp = propagateToMatchingPlaneMch(mchTrack, mftTrack, mftTrackCov, collision, MatchingPlaneDefaultZ, 0); + auto mftTrackProp = propagateToMatchingPlaneMft(mchTrack, mftTrack, mftTrackCov, collision, MatchingPlaneDefaultZ, 0); + // check if a vector of global muon candidates is already available for the current MCH index // if not, initialize a new one and add the current global muon track // bool globalMuonTrackFound = false; @@ -1961,13 +2165,15 @@ struct qaMatching { muonTrackIndex, mchTrackIndex, mftTrackIndex, + static_cast(muonTrack.trackType()), + static_cast(mftTrackProp), + static_cast(mchTrackProp), matchScore, matchChi2, -1, matchScore, matchChi2, -1, - 0, kMatchTypeUndefined}); } else { collisionInfo.matchingCandidates[mchTrackIndex].emplace_back(MatchingCandidate{ @@ -1975,74 +2181,154 @@ struct qaMatching { muonTrackIndex, mchTrackIndex, mftTrackIndex, + static_cast(muonTrack.trackType()), + static_cast(mftTrackProp), + static_cast(mchTrackProp), matchScore, matchChi2, -1, matchScore, matchChi2, -1, - 0, kMatchTypeUndefined}); } } } + } - // fill collision information for MFT standalone tracks - for (auto mftTrack : mftTracks) { - if (!mftTrack.has_collision()) - continue; + // sort the vectors of matching candidates in ascending order based on the matching chi2 value + auto compareMatchingChi2 = [](const MatchingCandidate& track1, const MatchingCandidate& track2) -> bool { + return (track1.matchChi2 < track2.matchChi2); + }; - if (collisionIndex != mftTrack.collisionId()) { - continue; + int64_t reducedEventCounter{0}; + for (auto& [collisionIndex, collisionInfo] : collisionInfos) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + // set the internal collision index + collisionInfo.reducedEventId = reducedEventCounter; + reducedEventCounter += 1; + for (auto& [mchIndex, globalTracksVector] : collisionInfo.matchingCandidates) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareMatchingChi2); + + int ranking = 1; + for (auto& candidate : globalTracksVector) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + candidate.matchRanking = ranking; + candidate.matchRankingProd = ranking; + if constexpr (isMC) { + candidate.matchType = getMatchType(candidate, muonTracks, mftTracks, collisionInfo.matchablePairs, ranking); + } else { + candidate.matchType = kMatchTypeUndefined; + } + ranking += 1; } + } + } + } - int64_t mftTrackIndex = mftTrack.globalIndex(); + template + void fillMatchingPlots(C const& collision, + TMUON const& muonTracks, + const std::vector& taggedMuons, + const MatchingCandidates& matchingCandidates, + MatchingPlotter* plotter) + { + // ==================================== + // Matching properties + for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { + if (globalTracksVector.size() < 1) + continue; - auto& collisionInfo = collisionInfos[collisionIndex]; - collisionInfo.index = collisionIndex; - collisionInfo.bc = bc.globalBC(); - collisionInfo.zVertex = collision.posZ(); + // get the standalone MCH track + auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); - collisionInfo.mftTracks.push_back(mftTrackIndex); + // skip global muon tracks that do not pass the MCH and MFT quality cuts + if (!isGoodGlobalMuon(mchTrack, collision)) + continue; + + double logp = std::log10(mchTrack.p()); + + int isTagged = 0; + if (std::find(taggedMuons.begin(), taggedMuons.end(), mchIndex) != taggedMuons.end()) { + isTagged = 1; + } + + float chi2Gap = 0; + if (globalTracksVector.size() > 1) { + // we have at least two candidates, so we can check the score difference + // between the leading and the sub-leading + auto leadingChi2 = globalTracksVector[0].matchChi2; + auto subleadingChi2 = globalTracksVector[1].matchChi2; + chi2Gap = subleadingChi2 - leadingChi2; + } + + for (const auto& candidate : globalTracksVector) { + double dp = candidate.mchTrackProp.getP() - candidate.mftTrackProp.getP(); + double dpt = candidate.mchTrackProp.getPt() - candidate.mftTrackProp.getPt(); + double dx = candidate.mchTrackProp.getX() - candidate.mftTrackProp.getX(); + double dy = candidate.mchTrackProp.getY() - candidate.mftTrackProp.getY(); + double dphi = candidate.mchTrackProp.getPhi() - candidate.mftTrackProp.getPhi(); + double dtanl = candidate.mchTrackProp.getTanl() - candidate.mftTrackProp.getTanl(); + double deta = candidate.mchTrackProp.getEta() - candidate.mftTrackProp.getEta(); + int matchType = static_cast(candidate.matchType); + int ranking = candidate.matchRanking - 1; + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaP)->Fill(dp, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaPt)->Fill(dpt, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaX)->Fill(dx, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaY)->Fill(dy, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaPhi)->Fill(dphi, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaTanl)->Fill(dtanl, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaEta)->Fill(deta, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hRabs)->Fill(mchTrack.rAtAbsorberEnd(), logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + + double ptError = std::sqrt((1. / candidate.mchTrackProp.getSigma2InvQPt()) + (1. / candidate.mftTrackProp.getSigma2InvQPt())); + double pullPt = (ptError > 0) ? dpt / ptError : 1.e6; + double xError = std::sqrt(candidate.mchTrackProp.getSigma2X() + candidate.mftTrackProp.getSigma2X()); + double pullX = (xError > 0) ? dx / xError : 1.e6; + double yError = std::sqrt(candidate.mchTrackProp.getSigma2Y() + candidate.mftTrackProp.getSigma2Y()); + double pullY = (yError > 0) ? dy / yError : 1.e6; + double phiError = std::sqrt(candidate.mchTrackProp.getSigma2Phi() + candidate.mftTrackProp.getSigma2Phi()); + double pullPhi = (phiError > 0) ? dphi / phiError : 1.e6; + double tanlError = std::sqrt(candidate.mchTrackProp.getSigma2Tanl() + candidate.mftTrackProp.getSigma2Tanl()); + double pullTanl = (tanlError > 0) ? dtanl / tanlError : 1.e6; + std::get>(plotter->fMatchFeaturesGoodMCH->hPullPt)->Fill(pullPt, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullX)->Fill(pullX, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullY)->Fill(pullY, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullPhi)->Fill(pullPhi, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullTanl)->Fill(pullTanl, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); } } - // sort the vectors of matching candidates in ascending order based on the matching score value - auto compareMatchingScore = [](const MatchingCandidate& track1, const MatchingCandidate& track2) -> bool { - return (track1.matchScore > track2.matchScore); - }; + // ==================================== + // Matching chi2 and score vs. production values + for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { + if (globalTracksVector.size() < 1) + continue; - for (auto& [collisionIndex, collisionInfo] : collisionInfos) { - for (auto& [mchIndex, globalTracksVector] : collisionInfo.matchingCandidates) { - std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareMatchingScore); + // loop over candidates + for (const auto& candidate : globalTracksVector) { + float matchScore = candidate.matchScore; + float matchChi2 = candidate.matchChi2; - const auto& mchTrack = muonTracks.rawIteratorAt(mchIndex); - auto mftMchMatchAttempts = GetMftMchMatchAttempts(collisions, bcs, mchTrack, mftTracks); - int ranking = 1; - for (auto& candidate : globalTracksVector) { - const auto& muonTrack = muonTracks.rawIteratorAt(candidate.globalTrackId); + float matchChi2Prod = candidate.matchChi2Prod; + float matchScoreProd = candidate.matchScoreProd; - candidate.matchRanking = ranking; - candidate.matchRankingProd = ranking; - candidate.matchType = GetMatchType(muonTrack, muonTracks, mftTracks, collisionInfo.matchablePairs, ranking); - candidate.mftMchMatchAttempts = mftMchMatchAttempts; - ranking += 1; - } + std::get>(plotter->fMatchScoreVsProd)->Fill(matchScoreProd, matchScore); + std::get>(plotter->fMatchChi2VsProd)->Fill(matchChi2Prod, matchChi2); } } } template - void FillMatchingPlotsMC(C const& collision, + void fillMatchingPlotsMc(C const& collision, const CollisionInfo& collisionInfo, TMUON const& muonTracks, TMFT const& mftTracks, + const std::vector& taggedMuons, const MatchingCandidates& matchingCandidates, const MatchingCandidates& matchingCandidatesProd, const std::vector>& matchablePairs, double matchingScoreCut, MatchingPlotter* plotter, - bool verbose = false) + bool /*verbose*/ = false) { int mftTrackMult = collisionInfo.mftTracks.size(); @@ -2051,7 +2337,7 @@ struct qaMatching { for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { // check if the MCH track belongs to a matchable pair - bool isPairedMCH = IsMatchableMCH(static_cast(mchIndex), matchablePairs); + bool isPairedMCH = isMatchableMch(static_cast(mchIndex), matchablePairs); // get the standalone MCH track auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); @@ -2059,24 +2345,26 @@ struct qaMatching { double mchPt = mchTrack.pt(); // MCH track quality flag - bool isGoodMCH = IsGoodGlobalMuon(mchTrack, collision); + bool isGoodMCH = isGoodGlobalMuon(mchTrack, collision); - auto matchablePair = GetMatchablePairForMCH(static_cast(mchIndex), matchablePairs); + auto matchablePair = getMatchablePairForMch(static_cast(mchIndex), matchablePairs); bool hasMatchablePair = matchablePair.has_value(); int decayRanking = 0; int mftTrackType = -1; - float dchi2 = (globalTracksVector.size() >= 2) ? globalTracksVector[1].matchChi2 - globalTracksVector[0].matchChi2 : -1; + float dchi2 = (globalTracksVector.size() >= MinCandidatesForDeltaChi2) ? globalTracksVector[1].matchChi2 - globalTracksVector[0].matchChi2 : InvalidDeltaChi2; if (hasMatchablePair) { auto const& pairedMftTrack = mftTracks.rawIteratorAt(matchablePair.value().second); - mftTrackType = pairedMftTrack.isCA() ? 1 : 0; - decayRanking = GetDecayRanking(mchTrack, mftTracks); + mftTrackType = pairedMftTrack.isCA() ? MftTrackTypeCA : MftTrackTypeStandard; + decayRanking = getDecayRanking(mchTrack, mftTracks); } + double logp = std::log10(mchTrack.p()); // find the index of the matching candidate that corresponds to the true match // index=1 corresponds to the leading candidate // index=0 means no candidate was found that corresponds to the true match - int trueMatchIndex = GetTrueMatchIndex(muonTracks, globalTracksVector, matchablePairs); - int trueMatchIndexProd = GetTrueMatchIndex(muonTracks, matchingCandidatesProd.at(mchIndex), matchablePairs); + int trueMatchIndex = getTrueMatchIndex(globalTracksVector, matchablePairs); + const auto prodCandidatesIt = matchingCandidatesProd.find(mchIndex); + int trueMatchIndexProd = (prodCandidatesIt != matchingCandidatesProd.end()) ? getTrueMatchIndex(prodCandidatesIt->second, matchablePairs) : 0; float mcParticleDz = -1000; if (mchTrack.has_mcParticle()) { @@ -2084,7 +2372,10 @@ struct qaMatching { mcParticleDz = collision.posZ() - mchMcParticle.vz(); } - int matchAttempts = globalTracksVector[0].mftMchMatchAttempts; + int matchAttempts = 0; + if (const auto& mchTrackInfoIt = collisionInfo.mchTracks.find(mchIndex); mchTrackInfoIt != collisionInfo.mchTracks.end()) { + matchAttempts = mchTrackInfoIt->second.compatMftTracks.size(); + } std::get>(plotter->fMatchRanking->hist)->Fill(trueMatchIndex); std::get>(plotter->fMatchRanking->histVsP)->Fill(mchMom, trueMatchIndex); @@ -2093,7 +2384,7 @@ struct qaMatching { std::get>(plotter->fMatchRanking->histVsMftTrackMult)->Fill(mftTrackMult, trueMatchIndex); std::get>(plotter->fMatchRanking->histVsMatchAttempts)->Fill(matchAttempts, trueMatchIndex); std::get>(plotter->fMatchRanking->histVsMftTrackType)->Fill(mftTrackType, trueMatchIndex); - std::get>(plotter->fMatchRanking->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex); + std::get>(plotter->fMatchRanking->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex, logp); if (dchi2 >= 0) std::get>(plotter->fMatchRanking->histVsDeltaChi2)->Fill(dchi2, trueMatchIndex); @@ -2105,7 +2396,7 @@ struct qaMatching { std::get>(plotter->fMatchRankingGoodMCH->histVsMftTrackMult)->Fill(mftTrackMult, trueMatchIndex); std::get>(plotter->fMatchRankingGoodMCH->histVsMatchAttempts)->Fill(matchAttempts, trueMatchIndex); std::get>(plotter->fMatchRankingGoodMCH->histVsMftTrackType)->Fill(mftTrackType, trueMatchIndex); - std::get>(plotter->fMatchRankingGoodMCH->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex); + std::get>(plotter->fMatchRankingGoodMCH->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex, logp); if (dchi2 >= 0) std::get>(plotter->fMatchRankingGoodMCH->histVsDeltaChi2)->Fill(dchi2, trueMatchIndex); } @@ -2118,7 +2409,7 @@ struct qaMatching { std::get>(plotter->fMatchRankingPaired->histVsMftTrackMult)->Fill(mftTrackMult, trueMatchIndex); std::get>(plotter->fMatchRankingPaired->histVsMatchAttempts)->Fill(matchAttempts, trueMatchIndex); std::get>(plotter->fMatchRankingPaired->histVsMftTrackType)->Fill(mftTrackType, trueMatchIndex); - std::get>(plotter->fMatchRankingPaired->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex); + std::get>(plotter->fMatchRankingPaired->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex, logp); if (dchi2 >= 0) std::get>(plotter->fMatchRankingPaired->histVsDeltaChi2)->Fill(dchi2, trueMatchIndex); } @@ -2131,11 +2422,63 @@ struct qaMatching { std::get>(plotter->fMatchRankingPairedGoodMCH->histVsMftTrackMult)->Fill(mftTrackMult, trueMatchIndex); std::get>(plotter->fMatchRankingPairedGoodMCH->histVsMatchAttempts)->Fill(matchAttempts, trueMatchIndex); std::get>(plotter->fMatchRankingPairedGoodMCH->histVsMftTrackType)->Fill(mftTrackType, trueMatchIndex); - std::get>(plotter->fMatchRankingPairedGoodMCH->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex); + std::get>(plotter->fMatchRankingPairedGoodMCH->histVsProdRanking)->Fill(trueMatchIndexProd, trueMatchIndex, logp); if (dchi2 >= 0) std::get>(plotter->fMatchRankingPairedGoodMCH->histVsDeltaChi2)->Fill(dchi2, trueMatchIndex); } + if (isGoodMCH) { + int isTagged = 0; + if (std::find(taggedMuons.begin(), taggedMuons.end(), mchIndex) != taggedMuons.end()) { + isTagged = 1; + } + + float chi2Gap = 0; + if (globalTracksVector.size() > 1) { + // we have at least two candidates, so we can check the score difference + // between the leading and the sub-leading + auto leadingChi2 = globalTracksVector[0].matchChi2; + auto subleadingChi2 = globalTracksVector[1].matchChi2; + chi2Gap = subleadingChi2 - leadingChi2; + } + + for (const auto& candidate : globalTracksVector) { + double dp = candidate.mchTrackProp.getP() - candidate.mftTrackProp.getP(); + double dpt = candidate.mchTrackProp.getPt() - candidate.mftTrackProp.getPt(); + double dx = candidate.mchTrackProp.getX() - candidate.mftTrackProp.getX(); + double dy = candidate.mchTrackProp.getY() - candidate.mftTrackProp.getY(); + double dphi = candidate.mchTrackProp.getPhi() - candidate.mftTrackProp.getPhi(); + double dtanl = candidate.mchTrackProp.getTanl() - candidate.mftTrackProp.getTanl(); + double deta = candidate.mchTrackProp.getEta() - candidate.mftTrackProp.getEta(); + int matchType = static_cast(candidate.matchType); + int ranking = candidate.matchRanking - 1; + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaP)->Fill(dp, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaPt)->Fill(dpt, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaX)->Fill(dx, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaY)->Fill(dy, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaPhi)->Fill(dphi, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaTanl)->Fill(dtanl, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hDeltaEta)->Fill(deta, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hRabs)->Fill(mchTrack.rAtAbsorberEnd(), logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + + double ptError = std::sqrt((1. / candidate.mchTrackProp.getSigma2InvQPt()) + (1. / candidate.mftTrackProp.getSigma2InvQPt())); + double pullPt = (ptError > 0) ? dpt / ptError : 1.e6; + double xError = std::sqrt(candidate.mchTrackProp.getSigma2X() + candidate.mftTrackProp.getSigma2X()); + double pullX = (xError > 0) ? dx / xError : 1.e6; + double yError = std::sqrt(candidate.mchTrackProp.getSigma2Y() + candidate.mftTrackProp.getSigma2Y()); + double pullY = (yError > 0) ? dy / yError : 1.e6; + double phiError = std::sqrt(candidate.mchTrackProp.getSigma2Phi() + candidate.mftTrackProp.getSigma2Phi()); + double pullPhi = (phiError > 0) ? dphi / phiError : 1.e6; + double tanlError = std::sqrt(candidate.mchTrackProp.getSigma2Tanl() + candidate.mftTrackProp.getSigma2Tanl()); + double pullTanl = (tanlError > 0) ? dtanl / tanlError : 1.e6; + std::get>(plotter->fMatchFeaturesGoodMCH->hPullPt)->Fill(pullPt, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullX)->Fill(pullX, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullY)->Fill(pullY, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullPhi)->Fill(pullPhi, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + std::get>(plotter->fMatchFeaturesGoodMCH->hPullTanl)->Fill(pullTanl, logp, candidate.matchChi2, chi2Gap, ranking, matchType, isTagged); + } + } + if (trueMatchIndex == 0) { // missed matches if (!isPairedMCH) { @@ -2189,7 +2532,7 @@ struct qaMatching { // ==================================== // Matching properties - for (auto [mchIndex, globalTracksVector] : matchingCandidates) { + for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { if (globalTracksVector.size() < 1) continue; @@ -2197,7 +2540,7 @@ struct qaMatching { auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); // skip global muon tracks that do not pass the MCH and MFT quality cuts - if (!IsGoodGlobalMuon(mchTrack, collision)) + if (!isGoodGlobalMuon(mchTrack, collision)) continue; double mchMom = mchTrack.p(); @@ -2220,22 +2563,20 @@ struct qaMatching { } } - for (auto [mchIndex, globalTracksVector] : matchingCandidates) { + for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { if (globalTracksVector.size() < 1) continue; - int trueMatchIndex = GetTrueMatchIndex(muonTracks, globalTracksVector, matchablePairs); + int trueMatchIndex = getTrueMatchIndex(globalTracksVector, matchablePairs); // loop over candidates int candidateIndex = 1; for (const auto& candidate : globalTracksVector) { - auto const& muonTrack = muonTracks.rawIteratorAt(candidate.globalTrackId); - float matchScore = candidate.matchScore; float matchChi2 = candidate.matchChi2; - float matchChi2Prod = muonTrack.chi2MatchMCHMFT() / 5.f; - float matchScoreProd = chi2ToScore(muonTrack.chi2MatchMCHMFT(), 5, 50.f); + float matchChi2Prod = candidate.matchChi2Prod; + float matchScoreProd = candidate.matchScoreProd; std::get>(plotter->fMatchScoreVsProd)->Fill(matchScoreProd, matchScore); std::get>(plotter->fMatchChi2VsProd)->Fill(matchChi2Prod, matchChi2); @@ -2251,52 +2592,34 @@ struct qaMatching { // ==================================== // Matching purity - if (verbose) - std::cout << std::format(" Filling matching purity plots with score cut {}", matchingScoreCut) << std::endl; - for (auto [mchIndex, globalTracksVector] : matchingCandidates) { + for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { if (globalTracksVector.size() < 1) continue; - // get the leading matching candidate - auto const& muonTrack = muonTracks.rawIteratorAt(globalTracksVector[0].globalTrackId); - double matchingScore = globalTracksVector[0].matchScore; - // get the standalone MCH and MFT tracks auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); - auto const& mftTrack = muonTrack.template matchMFTTrack_as(); // skip global muon tracks that do not pass the MCH and MFT quality cuts - if (!IsGoodGlobalMuon(mchTrack, collision)) - continue; - if (!IsGoodMFT(mftTrack)) + if (!isGoodGlobalMuon(mchTrack, collision)) continue; // skip candidates that do not pass the matching quality cuts - if (!IsGoodGlobalMatching(muonTrack, matchingScore, matchingScoreCut)) + if (!isGoodGlobalMatching(globalTracksVector[0], matchingScoreCut)) continue; // check if the matching candidate is a true one - bool isTrueMatch = IsTrueGlobalMatching(muonTrack, matchablePairs); + bool isTrueMatch = isTrueGlobalMatching(globalTracksVector[0], matchablePairs); // ---- MC ancestry ---- - auto motherParticles = GetMotherParticles(muonTrack); + auto motherParticles = getMotherParticles(mchTrack); int motherPDG = 0; if (motherParticles.size() > 1) { motherPDG = motherParticles[1].first; } - - if (verbose) { - std::cout << std::format(" MCH track #{} -> Muon track #{}, isTrueMatch={}", mchIndex, globalTracksVector[0].globalTrackId, isTrueMatch) << std::endl; - std::cout << " MC ancestry (pdg): "; - for (auto const& [pdg, idx] : motherParticles) { - std::cout << "(" << pdg << ") "; - } - std::cout << std::endl; - } // fill matching purity plots - plotter->fMatchingPurityPlotter.Fill(mchTrack, isTrueMatch); - if (fConfigQAs.fCreatePdgMomHistograms) { - plotter->fMatchingPurityPlotter.Fill(mchTrack, motherPDG, isTrueMatch); + plotter->fMatchingPurityPlotter->fill(mchTrack, isTrueMatch); + if (configQas.cfgCreatePdgMomHistograms) { + plotter->fMatchingPurityPlotter->fill(mchTrack, motherPDG, isTrueMatch); } } @@ -2304,13 +2627,13 @@ struct qaMatching { // Matching efficiencies // outer loop on matchable pairs - for (auto [matchableMchIndex, matchableMftIndex] : matchablePairs) { + for (const auto& [matchableMchIndex, matchableMftIndex] : matchablePairs) { // get the standalone MCH track auto const& mchTrack = muonTracks.rawIteratorAt(matchableMchIndex); // skip track pairs that do not pass the MCH and MFT quality cuts // we only consider matchable pairs that fulfill the track quality requirements - if (!IsGoodGlobalMuon(mchTrack, collision)) + if (!isGoodGlobalMuon(mchTrack, collision)) continue; bool goodMatchFound = false; @@ -2320,62 +2643,60 @@ struct qaMatching { if (matchingCandidates.count(matchableMchIndex) > 0) { const auto& globalTracksVector = matchingCandidates.at(static_cast(matchableMchIndex)); if (!globalTracksVector.empty()) { - // get the leading matching candidate - auto const& muonTrack = muonTracks.rawIteratorAt(globalTracksVector[0].globalTrackId); - double matchingScore = globalTracksVector[0].matchScore; - - // get the standalone MFT track - auto const& mftTrack = muonTrack.template matchMFTTrack_as(); - auto mftIndex = mftTrack.globalIndex(); + // get the standalone MFT track index + auto mftIndex = globalTracksVector[0].mftTrackId; - goodMatchFound = IsGoodGlobalMatching(muonTrack, matchingScore, matchingScoreCut); + goodMatchFound = isGoodGlobalMatching(globalTracksVector[0], matchingScoreCut); isTrueMatch = (mftIndex == matchableMftIndex); } } + if (cfgQaMatchingAodDebug > 0 && goodMatchFound && isTrueMatch) { + LOGF(info, + "[good&true] mchId=%lld trackType=%d p=%.3f pt=%.3f eta=%.3f phi=%.3f", + static_cast(mchTrack.globalIndex()), + static_cast(mchTrack.trackType()), + mchTrack.p(), + mchTrack.pt(), + mchTrack.eta(), + mchTrack.phi()); + } + // ---- MC ancestry ---- - auto motherParticles = GetMotherParticles(mchTrack); + auto motherParticles = getMotherParticles(mchTrack); int motherPDG = 0; if (motherParticles.size() > 1) { motherPDG = motherParticles[1].first; } - if (verbose) { - std::cout << " MC ancestry (pdg): "; - for (auto const& [pdg, idx] : motherParticles) { - std::cout << "(" << pdg << ") "; - } - std::cout << std::endl; - } - // fill matching efficiency plots - plotter->fPairingEfficiencyPlotter.Fill(mchTrack, goodMatchFound); - if (fConfigQAs.fCreatePdgMomHistograms) { - plotter->fPairingEfficiencyPlotter.Fill(mchTrack, motherPDG, goodMatchFound); + plotter->fPairingEfficiencyPlotter->fill(mchTrack, goodMatchFound); + if (configQas.cfgCreatePdgMomHistograms) { + plotter->fPairingEfficiencyPlotter->fill(mchTrack, motherPDG, goodMatchFound); } - plotter->fMatchingEfficiencyPlotter.Fill(mchTrack, (goodMatchFound && isTrueMatch)); - if (fConfigQAs.fCreatePdgMomHistograms) { - plotter->fMatchingEfficiencyPlotter.Fill(mchTrack, motherPDG, (goodMatchFound && isTrueMatch)); + plotter->fMatchingEfficiencyPlotter->fill(mchTrack, (goodMatchFound && isTrueMatch)); + if (configQas.cfgCreatePdgMomHistograms) { + plotter->fMatchingEfficiencyPlotter->fill(mchTrack, motherPDG, (goodMatchFound && isTrueMatch)); } - plotter->fFakeMatchingEfficiencyPlotter.Fill(mchTrack, (goodMatchFound && !isTrueMatch)); - if (fConfigQAs.fCreatePdgMomHistograms) { - plotter->fFakeMatchingEfficiencyPlotter.Fill(mchTrack, motherPDG, (goodMatchFound && !isTrueMatch)); + plotter->fFakeMatchingEfficiencyPlotter->fill(mchTrack, (goodMatchFound && !isTrueMatch)); + if (configQas.cfgCreatePdgMomHistograms) { + plotter->fFakeMatchingEfficiencyPlotter->fill(mchTrack, motherPDG, (goodMatchFound && !isTrueMatch)); } } } template - void FillDimuonPlotsMC(const CollisionInfo& collisionInfo, + void fillDimuonPlotsMc(const CollisionInfo& collisionInfo, C const& collisions, TMUON const& muonTracks, - TMFT const& /*mftTracks*/) + TMFT const& mftTracks) { std::vector muonPairs; std::vector globalMuonPairs; - GetMuonPairs(collisionInfo, muonPairs, globalMuonPairs); + getMuonPairs(collisionInfo, muonPairs, globalMuonPairs); - for (auto& [muon1, muon2] : muonPairs) { + for (const auto& [muon1, muon2] : muonPairs) { auto const& collision = collisions.rawIteratorAt(muon1.first); auto mchIndex1 = muon1.second; @@ -2389,16 +2710,16 @@ struct qaMatching { if ((sign1 * sign2) >= 0) continue; - bool goodMuonTracks = (IsGoodMuon(muonTrack1, collision) && IsGoodMuon(muonTrack2, collision)); + bool goodMuonTracks = (isGoodMuon(muonTrack1, collision) && isGoodMuon(muonTrack2, collision)); if (goodMuonTracks) { - double mass = GetMuMuInvariantMass(PropagateToVertexMCH(muonTrack1, collision), - PropagateToVertexMCH(muonTrack2, collision)); + double mass = getMuMuInvariantMass(propagateToVertexMch(muonTrack1, collision), + propagateToVertexMch(muonTrack2, collision)); registryDimuon.get(HIST("dimuon/invariantMass_MuonKine_MuonCuts"))->Fill(mass); } } - for (auto& [muon1, muon2] : globalMuonPairs) { + for (const auto& [muon1, muon2] : globalMuonPairs) { auto& candidates1 = muon1.second; auto& candidates2 = muon2.second; @@ -2406,10 +2727,10 @@ struct qaMatching { auto const& muonTrack1 = muonTracks.rawIteratorAt(candidates1[0].globalTrackId); auto const& muonTrack2 = muonTracks.rawIteratorAt(candidates2[0].globalTrackId); - auto matchScore1 = candidates1[0].matchScore; - auto matchScore2 = candidates2[0].matchScore; - auto const& mchTrack1 = muonTrack1.template matchMCHTrack_as(); - auto const& mchTrack2 = muonTrack2.template matchMCHTrack_as(); + auto const& mchTrack1 = muonTracks.rawIteratorAt(candidates1[0].muonTrackId); + auto const& mchTrack2 = muonTracks.rawIteratorAt(candidates2[0].muonTrackId); + auto const& mftTrack1 = mftTracks.rawIteratorAt(candidates1[0].mftTrackId); + auto const& mftTrack2 = mftTracks.rawIteratorAt(candidates2[0].mftTrackId); int sign1 = mchTrack1.sign(); int sign2 = mchTrack2.sign(); @@ -2426,38 +2747,43 @@ struct qaMatching { matchType = candidates2[0].matchType * 10 + candidates1[0].matchType; } - bool goodGlobalMuonTracks = (IsGoodGlobalMuon(mchTrack1, collision) && IsGoodGlobalMuon(mchTrack2, collision)); + bool goodGlobalMuonTracks = (isGoodGlobalMuon(mchTrack1, collision) && isGoodGlobalMuon(mchTrack2, collision)); if (!goodGlobalMuonTracks) { continue; } - bool goodGlobalMuonMatches = (IsGoodGlobalMatching(muonTrack1, matchScore1) && IsGoodGlobalMatching(muonTrack2, matchScore2)); + bool goodGlobalMuonMatches = (isGoodGlobalMatching(candidates1[0]) && isGoodGlobalMatching(candidates2[0])); - double massMCH = GetMuMuInvariantMass(PropagateToVertexMCH(mchTrack1, collision), - PropagateToVertexMCH(mchTrack2, collision)); - double mass = GetMuMuInvariantMass(PropagateToVertexMCH(muonTrack1, collision), - PropagateToVertexMCH(muonTrack2, collision)); + double massMCH = getMuMuInvariantMass(propagateToVertexMch(mchTrack1, collision), + propagateToVertexMch(mchTrack2, collision)); + double massRescaledKine = getMuMuInvariantMass(propagateToVertexMft(mftTrack1, mchTrack1, collision), + propagateToVertexMft(mftTrack2, mchTrack2, collision)); + double massGlobalKine = getMuMuInvariantMass(propagateToVertexMft(muonTrack1, collision), + propagateToVertexMft(muonTrack2, collision)); registryDimuon.get(HIST("dimuon/invariantMass_MuonKine_GlobalMuonCuts"))->Fill(massMCH); - registryDimuon.get(HIST("dimuon/invariantMass_ScaledMftKine_GlobalMuonCuts"))->Fill(mass); + registryDimuon.get(HIST("dimuon/invariantMass_ScaledMftKine_GlobalMuonCuts"))->Fill(massRescaledKine); + registryDimuon.get(HIST("dimuon/invariantMass_GlobalKine_GlobalMuonCuts"))->Fill(massGlobalKine); registryDimuon.get(HIST("dimuon/MC/invariantMass_MuonKine_GlobalMuonCuts_vs_match_type"))->Fill(massMCH, matchType); - registryDimuon.get(HIST("dimuon/MC/invariantMass_ScaledMftKine_GlobalMuonCuts_vs_match_type"))->Fill(mass, matchType); + registryDimuon.get(HIST("dimuon/MC/invariantMass_ScaledMftKine_GlobalMuonCuts_vs_match_type"))->Fill(massRescaledKine, matchType); + registryDimuon.get(HIST("dimuon/MC/invariantMass_GlobalKine_GlobalMuonCuts_vs_match_type"))->Fill(massGlobalKine, matchType); if (goodGlobalMuonMatches) { registryDimuon.get(HIST("dimuon/invariantMass_MuonKine_GlobalMuonCuts_GoodMatches"))->Fill(massMCH); - registryDimuon.get(HIST("dimuon/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches"))->Fill(mass); + registryDimuon.get(HIST("dimuon/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches"))->Fill(massRescaledKine); + registryDimuon.get(HIST("dimuon/invariantMass_GlobalKine_GlobalMuonCuts_GoodMatches"))->Fill(massGlobalKine); registryDimuon.get(HIST("dimuon/MC/invariantMass_MuonKine_GlobalMuonCuts_GoodMatches_vs_match_type"))->Fill(massMCH, matchType); - registryDimuon.get(HIST("dimuon/MC/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches_vs_match_type"))->Fill(mass, matchType); + registryDimuon.get(HIST("dimuon/MC/invariantMass_ScaledMftKine_GlobalMuonCuts_GoodMatches_vs_match_type"))->Fill(massRescaledKine, matchType); + registryDimuon.get(HIST("dimuon/MC/invariantMass_GlobalKine_GlobalMuonCuts_GoodMatches_vs_match_type"))->Fill(massGlobalKine, matchType); } } } - template - void RunChi2Matching(C const& collisions, - BC const& bcs, + template + void runChi2Matching(C const& collisions, TMUON const& muonTracks, TMFT const& mftTracks, CMFT const& mftCovs, - std::string funcName, + const std::string& funcName, float matchingPlaneZ, int extrapMethod, const std::vector>& matchablePairs, @@ -2475,7 +2801,8 @@ struct qaMatching { return; auto matchingFunc = mMatchingFunctionMap.at(funcName); - for (auto& [mchIndex, globalTracksVector] : matchingCandidates) { + for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { + // get the MCH standalone track auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); for (const auto& candidate : globalTracksVector) { @@ -2485,22 +2812,20 @@ struct qaMatching { auto collision = collisions.rawIteratorAt(muonTrack.collisionId()); - // get MCH and MFT standalone tracks - // auto mchTrack = muonTrack.template matchMCHTrack_as(); + // get the MFT standalone track auto const& mftTrack = muonTrack.template matchMFTTrack_as(); if (mftTrackCovs.count(mftTrack.globalIndex()) < 1) { - // std::cout << std::format("Covariance matrix for MFT track #{} not found", mftTrack.globalIndex()) << std::endl; continue; } auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrack.globalIndex()]); // get tracks parameters in O2 format - auto mftTrackProp = FwdToTrackPar(mftTrack, mftTrackCov); - auto mchTrackProp = FwdToTrackPar(mchTrack, mchTrack); + auto mftTrackProp = fwdToTrackPar(mftTrack, mftTrackCov); + auto mchTrackProp = fwdToTrackPar(mchTrack, mchTrack); if (matchingPlaneZ < 0.) { - mftTrackProp = PropagateToMatchingPlaneMFT(mchTrack, mftTrack, mftTrackCov, collision, matchingPlaneZ, extrapMethod); - mchTrackProp = PropagateToMatchingPlaneMCH(mchTrack, mftTrack, mftTrackCov, collision, matchingPlaneZ, extrapMethod); + mftTrackProp = propagateToMatchingPlaneMft(mchTrack, mftTrack, mftTrackCov, collision, matchingPlaneZ, extrapMethod); + mchTrackProp = propagateToMatchingPlaneMch(mchTrack, mftTrack, mftTrackCov, collision, matchingPlaneZ, extrapMethod); } // run the chi2 matching function @@ -2519,12 +2844,14 @@ struct qaMatching { candidate.globalTrackId, mchIndex, mftTrack.globalIndex(), + candidate.trackType, + static_cast(mftTrackProp), + static_cast(mchTrackProp), matchScore, matchChi2, -1, matchScoreProd, matchChi2Prod, - -1, kMatchTypeUndefined}); } else { newMatchingCandidates[mchIndex].emplace_back(MatchingCandidate{ @@ -2532,46 +2859,46 @@ struct qaMatching { candidate.globalTrackId, mchIndex, mftTrack.globalIndex(), + candidate.trackType, + static_cast(mftTrackProp), + static_cast(mchTrackProp), matchScore, matchChi2, -1, matchScoreProd, matchChi2Prod, - -1, kMatchTypeUndefined}); } } } - // sort the vectors of matching candidates in ascending order based on the matching score value - auto compareMatchingScore = [](const MatchingCandidate& track1, const MatchingCandidate& track2) -> bool { - return (track1.matchScore > track2.matchScore); + // sort the vectors of matching candidates in ascending order based on the matching chi2 value + auto compareMatchingChi2 = [](const MatchingCandidate& track1, const MatchingCandidate& track2) -> bool { + return (track1.matchChi2 < track2.matchChi2); }; - for (auto& [mchIndex, globalTracksVector] : newMatchingCandidates) { - std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareMatchingScore); + for (auto& [mchIndex, globalTracksVector] : newMatchingCandidates) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) + std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareMatchingChi2); - const auto& mchTrack = muonTracks.rawIteratorAt(mchIndex); - auto mftMchMatchAttempts = GetMftMchMatchAttempts(collisions, bcs, mchTrack, mftTracks); int ranking = 1; - for (auto& candidate : globalTracksVector) { - const auto& muonTrack = muonTracks.rawIteratorAt(candidate.globalTrackId); - + for (auto& candidate : globalTracksVector) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) candidate.matchRanking = ranking; - candidate.matchType = GetMatchType(muonTrack, muonTracks, mftTracks, matchablePairs, ranking); - candidate.mftMchMatchAttempts = mftMchMatchAttempts; + if constexpr (isMC) { + candidate.matchType = getMatchType(candidate, muonTracks, mftTracks, matchablePairs, ranking); + } else { + candidate.matchType = kMatchTypeUndefined; + } ranking += 1; } } } - template - void RunChi2Matching(C const& collisions, - BC const& bcs, + template + void runChi2Matching(C const& collisions, TMUON const& muonTracks, TMFT const& mftTracks, CMFT const& mftCovs, - std::string label, + const std::string& label, const std::vector>& matchablePairs, const MatchingCandidates& matchingCandidates, MatchingCandidates& newMatchingCandidates) @@ -2596,16 +2923,15 @@ struct qaMatching { auto matchingPlaneZ = matchingPlanesZ[label]; auto extrapMethod = matchingExtrapMethod[label]; - RunChi2Matching(collisions, bcs, muonTracks, mftTracks, mftCovs, funcName, matchingPlaneZ, extrapMethod, matchablePairs, matchingCandidates, newMatchingCandidates); + runChi2Matching(collisions, muonTracks, mftTracks, mftCovs, funcName, matchingPlaneZ, extrapMethod, matchablePairs, matchingCandidates, newMatchingCandidates); } - template - void RunMLMatching(C const& collisions, - BC const& bcs, + template + void runMlMatching(C const& collisions, TMUON const& muonTracks, TMFT const& mftTracks, CMFT const& mftCovs, - std::string label, + const std::string& label, const std::vector>& matchablePairs, const MatchingCandidates& matchingCandidates, MatchingCandidates& newMatchingCandidates) @@ -2616,7 +2942,7 @@ struct qaMatching { return; auto& mlResponse = mlIter->second; - for (auto& [mchIndex, globalTracksVector] : matchingCandidates) { + for (const auto& [mchIndex, globalTracksVector] : matchingCandidates) { auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); for (const auto& candidate : globalTracksVector) { auto const& muonTrack = muonTracks.rawIteratorAt(candidate.globalTrackId); @@ -2628,32 +2954,32 @@ struct qaMatching { // get MFT standalone track auto const& mftTrack = muonTrack.template matchMFTTrack_as(); if (mftTrackCovs.count(mftTrack.globalIndex()) < 1) { - // std::cout << std::format("Covariance matrix for MFT track #{} not found", mftTrack.globalIndex()) << std::endl; continue; } - // std::cout << fmt::format("Getting covariance matrix for MFT track #{} -> {}", mftTrack.globalIndex(), mftTrackCovs[mftTrack.globalIndex()]) << std::endl; auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrack.globalIndex()]); - // std::cout << fmt::format("Covariance matrix for MFT track #{} retrieved", mftTrack.globalIndex()) << std::endl; // get tracks parameters in O2 format - auto mftTrackProp = FwdToTrackPar(mftTrack, mftTrackCov); - auto mchTrackProp = FwdToTrackPar(mchTrack, mchTrack); + auto mftTrackProp = fwdToTrackPar(mftTrack, mftTrackCov); + auto mchTrackProp = fwdToTrackPar(mchTrack, mchTrack); // extrapolate to the matching plane auto matchingPlaneZ = matchingPlanesZ[label]; if (matchingPlaneZ < 0.) { - mftTrackProp = PropagateToZMFT(mftTrackProp, matchingPlaneZ); - mchTrackProp = PropagateToZMCH(mchTrackProp, matchingPlaneZ); + mftTrackProp = propagateToZMft(mftTrackProp, matchingPlaneZ); + mchTrackProp = propagateToZMch(mchTrackProp, matchingPlaneZ); } // run the ML model std::vector output; - std::vector inputML = mlResponse.getInputFeaturesGlob(muonTrack, mchTrackProp, mftTrackProp, collision); + std::vector inputML = mlResponse.getInputFeatures(muonTrack, mftTrack, mchTrack, mftTrackProp, mchTrackProp, collision); mlResponse.isSelectedMl(inputML, 0, output); float matchScore = output[0]; - float matchChi2Prod = muonTrack.chi2MatchMCHMFT() / 5.f; - float matchScoreProd = chi2ToScore(muonTrack.chi2MatchMCHMFT(), 5, 50.f); - // std::cout << std::format("Matching score: {}, Chi2: {}", matchingScore, muonTrack.chi2MatchMCHMFT()) << std::endl; + float matchScoreInv = (matchScore > 0) ? 1.0 / matchScore : std::numeric_limits::max(); + float matchChi2 = matchScoreInv - 1.f; + + float matchChi2Prod{-1}; + float matchScoreProd{-1}; + getMatchChi2AndScore(muonTrack, matchChi2Prod, matchScoreProd); // check if a vector of global muon candidates is already available for the current MCH index // if not, initialize a new one and add the current global muon track @@ -2664,12 +2990,14 @@ struct qaMatching { candidate.globalTrackId, mchIndex, mftTrack.globalIndex(), + candidate.trackType, + static_cast(mftTrackProp), + static_cast(mchTrackProp), matchScore, - -1, + matchChi2, -1, matchScoreProd, matchChi2Prod, - -1, kMatchTypeUndefined}); } else { newMatchingCandidates[mchIndex].emplace_back(MatchingCandidate{ @@ -2677,12 +3005,14 @@ struct qaMatching { candidate.globalTrackId, mchIndex, mftTrack.globalIndex(), + candidate.trackType, + static_cast(mftTrackProp), + static_cast(mchTrackProp), matchScore, - -1, + matchChi2, -1, matchScoreProd, matchChi2Prod, - -1, kMatchTypeUndefined}); } } @@ -2693,107 +3023,294 @@ struct qaMatching { return (track1.matchScore > track2.matchScore); }; - for (auto& [mchIndex, globalTracksVector] : newMatchingCandidates) { + for (auto& [mchIndex, globalTracksVector] : newMatchingCandidates) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) std::sort(globalTracksVector.begin(), globalTracksVector.end(), compareMatchingScore); - const auto& mchTrack = muonTracks.rawIteratorAt(mchIndex); - auto mftMchMatchAttempts = GetMftMchMatchAttempts(collisions, bcs, mchTrack, mftTracks); int ranking = 1; - for (auto& candidate : globalTracksVector) { - const auto& muonTrack = muonTracks.rawIteratorAt(candidate.globalTrackId); - + for (auto& candidate : globalTracksVector) { // o2-linter: disable=const-ref-in-for-loop (object is modified in loop) candidate.matchRanking = ranking; - candidate.matchType = GetMatchType(muonTrack, muonTracks, mftTracks, matchablePairs, ranking); - candidate.mftMchMatchAttempts = mftMchMatchAttempts; + if constexpr (isMC) { + candidate.matchType = getMatchType(candidate, muonTracks, mftTracks, matchablePairs, ranking); + } else { + candidate.matchType = kMatchTypeUndefined; + } ranking += 1; } } } - template - void ProcessCollisionMC(const CollisionInfo& collisionInfo, - C const& collisions, - BC const& bcs, - TMUON const& muonTracks, - TMFT const& mftTracks, - CMFT const& mftCovs) + template + void processCollision(const CollisionInfo& collisionInfo, + C const& collisions, + TMUON const& muonTracks, + TMFT const& mftTracks, + CMFT const& mftCovs) { auto collision = collisions.rawIteratorAt(collisionInfo.index); + std::vector taggedMuons; + getTaggedMuons(collisionInfo, collisions, muonTracks, taggedMuons); + + int debugCounter{0}; + fillQaMatchingAodEventForCollision(collisionInfo, collision, collisionInfo.reducedEventId, debugCounter); + fillQaMatchingMchTracksForCollision(collisionInfo, collision, muonTracks, taggedMuons, collisionInfo.reducedEventId, collisionInfo.reducedMchTrackIds); + registry.get(HIST("tracksMultiplicityMFT"))->Fill(collisionInfo.mftTracks.size()); registry.get(HIST("tracksMultiplicityMCH"))->Fill(collisionInfo.mchTracks.size()); - // Chi2-based matching analysis - FillMatchingPlotsMC(collision, collisionInfo, muonTracks, mftTracks, collisionInfo.matchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, fMatchingChi2ScoreMftMchLow, fChi2MatchingPlotter.get(), false); - for (auto& [label, func] : matchingChi2Functions) { + for (const auto& element : collisionInfo.mchTracks) { + auto mchIndex = element.first; + auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); + auto mchTrackAtVertex = VarManager::PropagateMuon(mchTrack, collision, VarManager::kToVertex); + + float mchEta = mchTrack.eta(); + float mchPt = mchTrack.pt(); + float mchP = mchTrack.p(); + float mchEtaAtVertex = mchTrackAtVertex.getEta(); + float mchPtAtVertex = mchTrackAtVertex.getPt(); + + float zMftFront = o2::mft::constants::mft::LayerZCoordinate()[0]; + float zMftBack = o2::mft::constants::mft::LayerZCoordinate()[9]; + auto mchTrackAtMftFront = propagateToZMch(mchTrackAtVertex, zMftFront); + auto mchTrackAtMftBack = propagateToZMch(mchTrackAtVertex, zMftBack); + + float rAtMftFront = std::hypot(mchTrackAtMftFront.getX(), mchTrackAtMftFront.getY()); + float rAtMftBack = std::hypot(mchTrackAtMftBack.getX(), mchTrackAtMftBack.getY()); + + if (isMC) { + registry.get(HIST("matching/MC/muonTracksVsMchKine"))->Fill(mchEta, mchPt, collision.posZ()); + registry.get(HIST("matching/MC/muonTracksVsMchKineAtVertex"))->Fill(mchEtaAtVertex, mchPtAtVertex, collision.posZ()); + registry.get(HIST("matching/MC/muonTracksRadiusAtMftFront"))->Fill(rAtMftFront, collision.posZ(), mchP); + registry.get(HIST("matching/MC/muonTracksRadiusAtMftBack"))->Fill(rAtMftBack, collision.posZ(), mchP); + } else { + registry.get(HIST("matching/muonTracksVsMchKine"))->Fill(mchEta, mchPt, collision.posZ()); + registry.get(HIST("matching/muonTracksVsMchKineAtVertex"))->Fill(mchEtaAtVertex, mchPtAtVertex, collision.posZ()); + registry.get(HIST("matching/muonTracksRadiusAtMftFront"))->Fill(rAtMftFront, collision.posZ(), mchP); + registry.get(HIST("matching/muonTracksRadiusAtMftBack"))->Fill(rAtMftBack, collision.posZ(), mchP); + } + + if (isMC) { + auto matchablePair = getMatchablePairForMch(mchIndex, collisionInfo.matchablePairs); + if (!matchablePair) { + continue; + } + auto const& mftTrack = mftTracks.rawIteratorAt(matchablePair.value().second); + registry.get(HIST("matching/MC/muonTracksPairedVsMchKine"))->Fill(mchEta, mchPt, collision.posZ(), mftTrack.nClusters()); + registry.get(HIST("matching/MC/muonTracksPairedVsMchKineAtVertex"))->Fill(mchEtaAtVertex, mchPtAtVertex, collision.posZ(), mftTrack.nClusters()); + + registry.get(HIST("matching/MC/muonTracksRadiusPairedAtMftFront"))->Fill(rAtMftFront, collision.posZ(), mchP); + registry.get(HIST("matching/MC/muonTracksRadiusPairedAtMftBack"))->Fill(rAtMftBack, collision.posZ(), mchP); + + if (mftTrackCovs.count(mftTrack.globalIndex()) < 1) { + continue; + } + auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrack.globalIndex()]); + + // extrapolate the tracks to the matching plane + auto mftTrackAtMftBack = propagateToZMft(fwdToTrackPar(mftTrack, mftTrackCov), zMftBack); + + registry.get(HIST("matching/MC/pairedMCHTracksAtMFT"))->Fill(mchTrackAtMftBack.getX(), mchTrackAtMftBack.getY()); + registry.get(HIST("matching/MC/pairedMFTTracksAtMFT"))->Fill(mftTrackAtMftBack.getX(), mftTrackAtMftBack.getY()); + } + } + + int matchingMethodCounter{0}; + + //------------------------------- + // Chi2-based matching from production + fillQaMatchingAodTablesForCollision(collision, muonTracks, mftTracks, collisionInfo.matchingCandidates, matchingMethodCounter, collisionInfo.reducedEventId, collisionInfo.reducedMchTrackIds, mftCovs); + if constexpr (isMC) { + fillMatchingPlotsMc(collision, collisionInfo, muonTracks, mftTracks, taggedMuons, collisionInfo.matchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, cfgMatchingChi2ScoreMftMchLow, fChi2MatchingPlotter.get(), false); + } else { + fillMatchingPlots(collision, muonTracks, taggedMuons, collisionInfo.matchingCandidates, fChi2MatchingPlotter.get()); + } + + //------------------------------- + // Tagged muons + MatchingCandidates taggedMatchingCandidates; + for (const auto& [mchIndex, globalTracksVector] : collisionInfo.matchingCandidates) { + if (std::find(taggedMuons.begin(), taggedMuons.end(), mchIndex) != taggedMuons.end()) { + taggedMatchingCandidates[mchIndex] = globalTracksVector; + } + } + if constexpr (isMC) { + fillMatchingPlotsMc(collision, collisionInfo, muonTracks, mftTracks, taggedMuons, taggedMatchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, cfgMatchingChi2ScoreMftMchLow, fTaggedMuonsMatchingPlotter.get()); + } else { + fillMatchingPlots(collision, muonTracks, taggedMuons, taggedMatchingCandidates, fTaggedMuonsMatchingPlotter.get()); + } + + //------------------------------- + // Custom chi2-based matching methods + for (const auto& [label, func] : matchingChi2Functions) { MatchingCandidates matchingCandidates; - RunChi2Matching(collisions, bcs, muonTracks, mftTracks, mftCovs, label, collisionInfo.matchablePairs, collisionInfo.matchingCandidates, matchingCandidates); + runChi2Matching(collisions, muonTracks, mftTracks, mftCovs, label, collisionInfo.matchablePairs, collisionInfo.matchingCandidates, matchingCandidates); auto* plotter = fMatchingPlotters.at(label).get(); double matchingScoreCut = matchingScoreCuts.at(label); - FillMatchingPlotsMC(collision, collisionInfo, muonTracks, mftTracks, matchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, matchingScoreCut, plotter, false); + matchingMethodCounter += 1; + fillQaMatchingAodTablesForCollision(collision, muonTracks, mftTracks, matchingCandidates, matchingMethodCounter, collisionInfo.reducedEventId, collisionInfo.reducedMchTrackIds, mftCovs); + if constexpr (isMC) { + fillMatchingPlotsMc(collision, collisionInfo, muonTracks, mftTracks, taggedMuons, matchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, matchingScoreCut, plotter, false); + } else { + fillMatchingPlots(collision, muonTracks, taggedMuons, matchingCandidates, plotter); + } } - // ML-based matching analysis - for (auto& [label, mlResponse] : matchingMlResponses) { + //------------------------------- + // Custom ML-based matching methods + for (const auto& [label, mlResponse] : matchingMlResponses) { MatchingCandidates matchingCandidates; - RunMLMatching(collisions, bcs, muonTracks, mftTracks, mftCovs, label, collisionInfo.matchablePairs, collisionInfo.matchingCandidates, matchingCandidates); + runMlMatching(collisions, muonTracks, mftTracks, mftCovs, label, collisionInfo.matchablePairs, collisionInfo.matchingCandidates, matchingCandidates); auto* plotter = fMatchingPlotters.at(label).get(); double matchingScoreCut = matchingScoreCuts.at(label); - FillMatchingPlotsMC(collision, collisionInfo, muonTracks, mftTracks, matchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, matchingScoreCut, plotter); + matchingMethodCounter += 1; + fillQaMatchingAodTablesForCollision(collision, muonTracks, mftTracks, matchingCandidates, matchingMethodCounter, collisionInfo.reducedEventId, collisionInfo.reducedMchTrackIds, mftCovs); + if constexpr (isMC) { + fillMatchingPlotsMc(collision, collisionInfo, muonTracks, mftTracks, taggedMuons, matchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, matchingScoreCut, plotter); + } else { + fillMatchingPlots(collision, muonTracks, taggedMuons, matchingCandidates, plotter); + } } - // Muons tagging - for (auto [mchIndex, mftIndex] : collisionInfo.matchablePairs) { - auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); - if (!mchTrack.has_collision()) - continue; - auto collision = collisions.rawIteratorAt(mchTrack.collisionId()); + //------------------------------- + // Di-muon analysis + fillDimuonPlotsMc(collisionInfo, collisions, muonTracks, mftTracks); + } - auto const& mftTrack = mftTracks.rawIteratorAt(mftIndex); - if (mftTrackCovs.count(mftTrack.globalIndex()) < 1) { + template + void fillQaMatchingAodTablesForCollision(TCOLLISION const& collision, + TMUON const& muonTracks, + TMFT const& mftTracks, + const MatchingCandidates& matchingCandidates, + int8_t matchLabel, + int32_t reducedEventId, + std::map reducedMchTrackIds, + CMFT const& mftCovs) + { + for (const auto& [mchIndex, candidates] : matchingCandidates) { + if (candidates.empty()) { continue; } - auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrack.globalIndex()]); - auto mchTrackAtVertex = VarManager::PropagateMuon(mchTrack, collision, VarManager::kToVertex); + const auto& mchTrack = muonTracks.rawIteratorAt(mchIndex); + if (!isGoodGlobalMuon(mchTrack, collision)) { + continue; + } - // extrapolate to the matching plane - auto z = o2::mft::constants::mft::LayerZCoordinate()[9]; - auto mchTrackProp = PropagateToZMCH(mchTrackAtVertex, z); - auto mftTrackProp = PropagateToZMFT(FwdToTrackPar(mftTrack, mftTrackCov), z); + auto reducedMchTrackIdIt = reducedMchTrackIds.find(mchIndex); + if (reducedMchTrackIdIt == reducedMchTrackIds.end()) { + continue; + } + int64_t reducedMchTrackId = reducedMchTrackIdIt->second; - registry.get(HIST("matching/MC/pairedMCHTracksAtMFT"))->Fill(mchTrackProp.getX(), mchTrackProp.getY()); - registry.get(HIST("matching/MC/pairedMFTTracksAtMFT"))->Fill(mftTrackProp.getX(), mftTrackProp.getY()); + for (const auto& candidate : candidates) { + const auto& candidateTrack = muonTracks.rawIteratorAt(candidate.globalTrackId); + const auto& mftTrack = mftTracks.rawIteratorAt(candidate.mftTrackId); + if (mftTrackCovs.count(mftTrack.globalIndex()) < 1) { + continue; + } + auto const& mftTrackCov = mftCovs.rawIteratorAt(mftTrackCovs[mftTrack.globalIndex()]); + // propagate global forward track to vertex using momentum rescaling method + auto candidateTrackAtVertex = propagateToVertexMft(mftTrack, mchTrack, collision); + auto candidateTrackAtPlaneZ2 = propagateToZMft(mftTrack, mftTrackCov, o2::mft::constants::mft::LayerZCoordinate()[9]); + qaMatchingCandidates( + reducedEventId, + reducedMchTrackId, + matchLabel, + static_cast(candidateTrack.p()), + static_cast(candidateTrack.pt()), + static_cast(candidateTrack.eta()), + static_cast(candidateTrack.phi()), + static_cast(candidateTrack.signed1Pt()), + static_cast(candidateTrack.tgl()), + static_cast(candidate.matchType), + static_cast(candidate.matchChi2), + static_cast(candidate.matchScore), + static_cast(candidate.matchRanking), + static_cast(candidateTrackAtVertex.getX()), + static_cast(candidateTrackAtVertex.getY()), + static_cast(candidateTrackAtVertex.getZ()), + static_cast(candidateTrackAtPlaneZ2.getX()), + static_cast(candidateTrackAtPlaneZ2.getY()), + static_cast(candidateTrackAtPlaneZ2.getZ()), + static_cast(candidateTrackAtVertex.getPx()), + static_cast(candidateTrackAtVertex.getPy()), + static_cast(candidateTrackAtVertex.getPz())); + } } + } - std::vector selectedMuons; - GetSelectedMuons(collisionInfo, collisions, muonTracks, selectedMuons); - - MatchingCandidates selectedMatchingCandidates; - for (auto [mchIndex, globalTracksVector] : collisionInfo.matchingCandidates) { - if (std::find(selectedMuons.begin(), selectedMuons.end(), mchIndex) != selectedMuons.end()) { - selectedMatchingCandidates[mchIndex] = globalTracksVector; - } + template + void fillQaMatchingAodEventForCollision(const CollisionInfo& collisionInfo, + TCOLLISION const& collision, + int32_t reducedEventId, + int& debugCounter) + { + int32_t mftMultiplicity = static_cast(collisionInfo.mftTracks.size()); + qaMatchingEvents( + mftMultiplicity, + static_cast(collision.posX()), + static_cast(collision.posY()), + static_cast(collision.posZ())); + + if (cfgQaMatchingAodDebug > 0 && debugCounter < cfgQaMatchingAodDebug) { + LOGF(info, "[AO2D] reducedEvent=%", reducedEventId); + debugCounter += 1; } - FillMatchingPlotsMC(collision, collisionInfo, muonTracks, mftTracks, selectedMatchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, fMatchingChi2ScoreMftMchLow, fSelectedMuonsMatchingPlotter.get()); + } - std::vector taggedMuons; - GetTaggedMuons(collisionInfo, muonTracks, selectedMuons, taggedMuons); + template + void fillQaMatchingMchTracksForCollision(const CollisionInfo& collisionInfo, + TCOLLISION const& collision, + TMUON const& muonTracks, + const std::vector& taggedMuons, + int32_t reducedEventId, + const std::map& reducedMchTrackIds) + { + std::vector> orderedMchIds; + orderedMchIds.reserve(reducedMchTrackIds.size()); + for (const auto& [mchIndex, reducedMchTrackId] : reducedMchTrackIds) { + orderedMchIds.emplace_back(reducedMchTrackId, mchIndex); + } + std::sort(orderedMchIds.begin(), orderedMchIds.end()); - MatchingCandidates taggedMatchingCandidates; - for (auto [mchIndex, globalTracksVector] : collisionInfo.matchingCandidates) { + for (const auto& [reducedMchTrackId, mchIndex] : orderedMchIds) { + (void)reducedMchTrackId; + auto const& mchTrack = muonTracks.rawIteratorAt(mchIndex); + int mftMchMatchAttempts = 0; + if (const auto& mchTrackInfoIt = collisionInfo.mchTracks.find(mchIndex); mchTrackInfoIt != collisionInfo.mchTracks.end()) { + mftMchMatchAttempts = mchTrackInfoIt->second.compatMftTracks.size(); + } + auto mchTrackAtVertex = VarManager::PropagateMuon(mchTrack, collision, VarManager::kToVertex); + auto mchTrackAtPlaneZ2 = propagateToZMch(mchTrack, o2::mft::constants::mft::LayerZCoordinate()[9]); + bool isTagged = false; if (std::find(taggedMuons.begin(), taggedMuons.end(), mchIndex) != taggedMuons.end()) { - taggedMatchingCandidates[mchIndex] = globalTracksVector; + isTagged = true; } + qaMatchingMCHTrack( + reducedEventId, + static_cast(mchTrack.trackType()), + static_cast(mchTrack.p()), + static_cast(mchTrack.pt()), + static_cast(mchTrack.eta()), + static_cast(mchTrack.phi()), + static_cast(mchTrack.signed1Pt()), + static_cast(mchTrack.tgl()), + static_cast(mftMchMatchAttempts), + isTagged, + static_cast(mchTrackAtVertex.getX()), + static_cast(mchTrackAtVertex.getY()), + static_cast(mchTrackAtVertex.getZ()), + static_cast(mchTrackAtPlaneZ2.getX()), + static_cast(mchTrackAtPlaneZ2.getY()), + static_cast(mchTrackAtPlaneZ2.getZ()), + static_cast(mchTrackAtVertex.getPx()), + static_cast(mchTrackAtVertex.getPy()), + static_cast(mchTrackAtVertex.getPz())); } - FillMatchingPlotsMC(collision, collisionInfo, muonTracks, mftTracks, taggedMatchingCandidates, collisionInfo.matchingCandidates, collisionInfo.matchablePairs, fMatchingChi2ScoreMftMchLow, fTaggedMuonsMatchingPlotter.get()); - - // Di-muon analysis - FillDimuonPlotsMC(collisionInfo, collisions, muonTracks, mftTracks); } void processQAMC(MyEvents const& collisions, @@ -2804,29 +3321,83 @@ struct qaMatching { aod::McParticles const& /*mcParticles*/) { auto bc = bcs.begin(); - initCCDB(bc); + initCcdb(bc); + + for (const auto& muon : muonTracks) { + registry.get(HIST("nTracksPerType"))->Fill(static_cast(muon.trackType())); + } + + mftTrackCovs.clear(); + for (const auto& mftTrackCov : mftCovs) { + mftTrackCovs[mftTrackCov.matchMFTTrackId()] = mftTrackCov.globalIndex(); + } + + fillCollisions(collisions, bcs, muonTracks, mftTracks, mftCovs, fCollisionInfos); + + for (auto const& [collisionIndex, collisionInfo] : fCollisionInfos) { + processCollision(collisionInfo, collisions, muonTracks, mftTracks, mftCovs); + } + } + + PROCESS_SWITCH(QaMatching, processQAMC, "processQAMC", true); + + void processQA(MyEvents const& collisions, + aod::BCsWithTimestamps const& bcs, + MyMuons const& muonTracks, + MyMFTs const& mftTracks, + MyMFTCovariances const& mftCovs) + { + auto bc = bcs.begin(); + initCcdb(bc); - for (auto& muon : muonTracks) { + for (const auto& muon : muonTracks) { registry.get(HIST("nTracksPerType"))->Fill(static_cast(muon.trackType())); } - FillCollisions(collisions, bcs, muonTracks, mftTracks, fCollisionInfos); + mftTrackCovs.clear(); + for (const auto& mftTrackCov : mftCovs) { + mftTrackCovs[mftTrackCov.matchMFTTrackId()] = mftTrackCov.globalIndex(); + } + + fillCollisions(collisions, bcs, muonTracks, mftTracks, mftCovs, fCollisionInfos); + + for (auto const& [collisionIndex, collisionInfo] : fCollisionInfos) { + processCollision(collisionInfo, collisions, muonTracks, mftTracks, mftCovs); + } + } + + PROCESS_SWITCH(QaMatching, processQA, "processQA", false); + + void processQAReAlign(MyEvents const& collisions, + aod::BCsWithTimestamps const& bcs, + MyMuonsReAlign const& muonTracks, + MyMFTs const& mftTracks, + MyMFTCovariances const& mftCovs) + { + auto bc = bcs.begin(); + initCcdb(bc); + + for (const auto& muon : muonTracks) { + registry.get(HIST("nTracksPerType"))->Fill(static_cast(muon.trackType())); + } mftTrackCovs.clear(); - for (auto& mftTrackCov : mftCovs) { + for (const auto& mftTrackCov : mftCovs) { mftTrackCovs[mftTrackCov.matchMFTTrackId()] = mftTrackCov.globalIndex(); } + fillCollisions(collisions, bcs, muonTracks, mftTracks, mftCovs, fCollisionInfos); + for (auto const& [collisionIndex, collisionInfo] : fCollisionInfos) { - ProcessCollisionMC(collisionInfo, collisions, bcs, muonTracks, mftTracks, mftCovs); + processCollision(collisionInfo, collisions, muonTracks, mftTracks, mftCovs); } } - PROCESS_SWITCH(qaMatching, processQAMC, "processQAMC", true); + PROCESS_SWITCH(QaMatching, processQAReAlign, "processQAReAlign", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; }; diff --git a/PWGDQ/Tasks/quarkoniaToHyperons.cxx b/PWGDQ/Tasks/quarkoniaToHyperons.cxx index d4601569e86..84606f59fac 100644 --- a/PWGDQ/Tasks/quarkoniaToHyperons.cxx +++ b/PWGDQ/Tasks/quarkoniaToHyperons.cxx @@ -378,7 +378,7 @@ struct QuarkoniaToHyperons { Service pdgDB; // For manual sliceBy - PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; enum Selection : uint64_t { selCosPA = 0, selRadius, @@ -519,7 +519,7 @@ struct QuarkoniaToHyperons { secondaryMaskSelectionAntiLambda = maskTopological | maskTrackProperties | maskAntiLambdaSpecific; // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1F, {{20, -0.5f, +19.5f}}); + histos.add("hEventSelection", "hEventSelection", kTH1D, {{20, -0.5f, +19.5f}}); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); @@ -536,20 +536,20 @@ struct QuarkoniaToHyperons { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "Below min occup."); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "Above max occup."); - histos.add("hEventCentrality", "hEventCentrality", kTH1F, {{100, 0.0f, +100.0f}}); - histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2F, {axes.axisCentrality, axes.axisNch}); + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {{100, 0.0f, +100.0f}}); + histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {axes.axisCentrality, axes.axisNch}); - histos.add("hEventOccupancy", "hEventOccupancy", kTH1F, {axes.axisOccupancy}); - histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2F, {axes.axisCentrality, axes.axisOccupancy}); + histos.add("hEventOccupancy", "hEventOccupancy", kTH1D, {axes.axisOccupancy}); + histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {axes.axisCentrality, axes.axisOccupancy}); if (!isPP) { - histos.add("hGapSide", "Gap side; Entries", kTH1F, {{5, -0.5, 4.5}}); - histos.add("hSelGapSide", "Selected gap side; Entries", kTH1F, {axes.axisSelGap}); - histos.add("hEventCentralityVsSelGapSide", ";Centrality (%); Selected gap side", kTH2F, {{100, 0.0f, +100.0f}, axes.axisSelGap}); + histos.add("hGapSide", "Gap side; Entries", kTH1D, {{5, -0.5, 4.5}}); + histos.add("hSelGapSide", "Selected gap side; Entries", kTH1D, {axes.axisSelGap}); + histos.add("hEventCentralityVsSelGapSide", ";Centrality (%); Selected gap side", kTH2D, {{100, 0.0f, +100.0f}, axes.axisSelGap}); } // for QA and test purposes - auto hRawCentrality = histos.add("hRawCentrality", "hRawCentrality", kTH1F, {axes.axisRawCentrality}); + auto hRawCentrality = histos.add("hRawCentrality", "hRawCentrality", kTH1D, {axes.axisRawCentrality}); for (int ii = 1; ii < 101; ii++) { float value = 100.5f - static_cast(ii); @@ -558,340 +558,340 @@ struct QuarkoniaToHyperons { // histograms versus mass if (buildK0sK0sPairs) { - histos.add("K0sK0s/h3dMassK0sK0s", "h3dMassK0sK0s", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dMassK0sK0s", "h3dMassK0sK0s", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); if (!isPP) { // Non-UPC info - histos.add("K0sK0s/h3dMassK0sK0sHadronic", "h3dMassK0sK0sHadronic", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dMassK0sK0sHadronic", "h3dMassK0sK0sHadronic", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); // UPC info - histos.add("K0sK0s/h3dMassK0sK0sSGA", "h3dMassK0sK0sSGA", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dMassK0sK0sSGC", "h3dMassK0sK0sSGC", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dMassK0sK0sDG", "h3dMassK0sK0sDG", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dMassK0sK0sSGA", "h3dMassK0sK0sSGA", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dMassK0sK0sSGC", "h3dMassK0sK0sSGC", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dMassK0sK0sDG", "h3dMassK0sK0sDG", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } - histos.add("K0sK0s/h2dNbrOfK0ShortVsCentrality", "h2dNbrOfK0ShortVsCentrality", kTH2F, {axes.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("K0sK0s/h2dNbrOfK0ShortVsCentrality", "h2dNbrOfK0ShortVsCentrality", kTH2D, {axes.axisCentrality, {10, -0.5f, 9.5f}}); // QA plot // Candidates after K0s selections - histos.add("K0sK0s/K0s/hPosDCAToPV", "hPosDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("K0sK0s/K0s/hNegDCAToPV", "hNegDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("K0sK0s/K0s/hDCAV0Daughters", "hDCAV0Daughters", kTH1F, {axes.axisDCAdau}); - histos.add("K0sK0s/K0s/hDCAV0ToPV", "hDCAV0ToPV", kTH1F, {axes.axisDCAV0ToPV}); - histos.add("K0sK0s/K0s/hV0PointingAngle", "hV0PointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("K0sK0s/K0s/hV0Radius", "hV0Radius", kTH1F, {axes.axisRadius}); - histos.add("K0sK0s/K0s/hV0DecayLength", "hDecayLength", kTH1F, {axes.axisProperLifeTime}); - histos.add("K0sK0s/K0s/hV0InvMassWindow", "hInvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("K0sK0s/K0s/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2F, {axes.axisLambdaMass, axes.axisK0Mass}); - histos.add("K0sK0s/K0s/h2dArmenteros", "h2dArmenteros", kTH2F, {axes.axisAPAlpha, axes.axisAPQt}); - histos.add("K0sK0s/K0s/hPosTPCNsigma", "hPosTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("K0sK0s/K0s/hNegTPCNsigma", "hNegTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("K0sK0s/K0s/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("K0sK0s/K0s/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("K0sK0s/K0s/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("K0sK0s/K0s/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("K0sK0s/K0s/hDCAV0Daughters", "hDCAV0Daughters", kTH1D, {axes.axisDCAdau}); + histos.add("K0sK0s/K0s/hDCAV0ToPV", "hDCAV0ToPV", kTH1D, {axes.axisDCAV0ToPV}); + histos.add("K0sK0s/K0s/hV0PointingAngle", "hV0PointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("K0sK0s/K0s/hV0Radius", "hV0Radius", kTH1D, {axes.axisRadius}); + histos.add("K0sK0s/K0s/hV0DecayLength", "hDecayLength", kTH1D, {axes.axisProperLifeTime}); + histos.add("K0sK0s/K0s/hV0InvMassWindow", "hInvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("K0sK0s/K0s/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2D, {axes.axisLambdaMass, axes.axisK0Mass}); + histos.add("K0sK0s/K0s/h2dArmenteros", "h2dArmenteros", kTH2D, {axes.axisAPAlpha, axes.axisAPQt}); + histos.add("K0sK0s/K0s/hPosTPCNsigma", "hPosTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("K0sK0s/K0s/hNegTPCNsigma", "hNegTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("K0sK0s/K0s/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("K0sK0s/K0s/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); if (doMCAssociation) { - histos.add("K0sK0s/h3dInvMassTrueEtaC1S", "h3dInvMassTrueEtaC1S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dInvMassTrueJPsi", "h3dInvMassTrueJPsi", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dInvMassTrueChiC0", "h3dInvMassTrueChiC0", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dInvMassTrueChiC1", "h3dInvMassTrueChiC1", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dInvMassTrueHC", "h3dInvMassTrueHC", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dInvMassTrueChiC2", "h3dInvMassTrueChiC2", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("K0sK0s/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTrueEtaC1S", "h3dInvMassTrueEtaC1S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTrueJPsi", "h3dInvMassTrueJPsi", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTrueChiC0", "h3dInvMassTrueChiC0", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTrueChiC1", "h3dInvMassTrueChiC1", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTrueHC", "h3dInvMassTrueHC", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTrueChiC2", "h3dInvMassTrueChiC2", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("K0sK0s/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } if (doQA) { - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosDCAToPV", "h3dMassK0sK0sVsPosDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegDCAToPV", "h3dMassK0sK0sVsNegDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsDCAV0Daughters", "h3dMassK0sK0sVsDCAV0Daughters", kTH3F, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsDCAV0ToPV", "h3dMassK0sK0sVsDCAV0ToPV", kTH3F, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsV0PointingAngle", "h3dMassK0sK0sVsV0PointingAngle", kTH3F, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsV0Radius", "h3dMassK0sK0sVsV0Radius", kTH3F, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsDecayLength", "h3dMassK0sK0sVsDecayLength", kTH3F, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsInvMassWindow", "h3dMassK0sK0sVsInvMassWindow", kTH3F, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosTPCNsigma", "h3dMassK0sK0sVsPosTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegTPCNsigma", "h3dMassK0sK0sVsNegTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosITSclusters", "h3dMassK0sK0sVsPosITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegITSclusters", "h3dMassK0sK0sVsNegITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosNbrCrossedRows", "h3dMassK0sK0sVsPosNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegNbrCrossedRows", "h3dMassK0sK0sVsNegNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairRadius3D", "h3dMassK0sK0sVsPairRadius3D", kTH3F, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairRadius2D", "h3dMassK0sK0sVsPairRadius2D", kTH3F, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairZ", "h3dMassK0sK0sVsPairZ", kTH3F, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsDCAPair", "h3dMassK0sK0sVsDCAPair", kTH3F, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairCosPA", "h3dMassK0sK0sVsPairCosPA", kTH3F, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairOpAngle", "h3dMassK0sK0sVsPairOpAngle", kTH3F, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairEta", "h3dMassK0sK0sVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairPhi", "h3dMassK0sK0sVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/K0sK0s/h3dDeltaEtaK0sK0sVsPairEta", "h3dDeltaEtaK0sK0sVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); - histos.add("QA/K0sK0s/h3dDeltaPhiK0sK0sVsPairPhi", "h3dDeltaPhiK0sK0sVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosDCAToPV", "h3dMassK0sK0sVsPosDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegDCAToPV", "h3dMassK0sK0sVsNegDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsDCAV0Daughters", "h3dMassK0sK0sVsDCAV0Daughters", kTH3D, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsDCAV0ToPV", "h3dMassK0sK0sVsDCAV0ToPV", kTH3D, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsV0PointingAngle", "h3dMassK0sK0sVsV0PointingAngle", kTH3D, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsV0Radius", "h3dMassK0sK0sVsV0Radius", kTH3D, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsDecayLength", "h3dMassK0sK0sVsDecayLength", kTH3D, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsInvMassWindow", "h3dMassK0sK0sVsInvMassWindow", kTH3D, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosTPCNsigma", "h3dMassK0sK0sVsPosTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegTPCNsigma", "h3dMassK0sK0sVsNegTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosITSclusters", "h3dMassK0sK0sVsPosITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegITSclusters", "h3dMassK0sK0sVsNegITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPosNbrCrossedRows", "h3dMassK0sK0sVsPosNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsNegNbrCrossedRows", "h3dMassK0sK0sVsNegNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairRadius3D", "h3dMassK0sK0sVsPairRadius3D", kTH3D, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairRadius2D", "h3dMassK0sK0sVsPairRadius2D", kTH3D, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairZ", "h3dMassK0sK0sVsPairZ", kTH3D, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsDCAPair", "h3dMassK0sK0sVsDCAPair", kTH3D, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairCosPA", "h3dMassK0sK0sVsPairCosPA", kTH3D, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairOpAngle", "h3dMassK0sK0sVsPairOpAngle", kTH3D, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairEta", "h3dMassK0sK0sVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dMassK0sK0sVsPairPhi", "h3dMassK0sK0sVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/K0sK0s/h3dDeltaEtaK0sK0sVsPairEta", "h3dDeltaEtaK0sK0sVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); + histos.add("QA/K0sK0s/h3dDeltaPhiK0sK0sVsPairPhi", "h3dDeltaPhiK0sK0sVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); } } if (buildLaLaBarPairs) { - histos.add("LaLaBar/h3dMassLaLabar", "h3dMassLaLabar", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dMassLaLabar", "h3dMassLaLabar", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); if (!isPP) { // Non-UPC info - histos.add("LaLaBar/h3dMassLaLabarHadronic", "h3dMassLaLabarHadronic", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dMassLaLabarHadronic", "h3dMassLaLabarHadronic", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); // UPC info - histos.add("LaLaBar/h3dMassLaLabarSGA", "h3dMassLaLabarSGA", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dMassLaLabarSGC", "h3dMassLaLabarSGC", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dMassLaLabarDG", "h3dMassLaLabarDG", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dMassLaLabarSGA", "h3dMassLaLabarSGA", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dMassLaLabarSGC", "h3dMassLaLabarSGC", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dMassLaLabarDG", "h3dMassLaLabarDG", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } - histos.add("LaLaBar/h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2F, {axes.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("LaLaBar/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2F, {axes.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("LaLaBar/h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axes.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("LaLaBar/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axes.axisCentrality, {10, -0.5f, 9.5f}}); // QA plot // Candidates after Lambda selections - histos.add("LaLaBar/Lambda/hPosDCAToPV", "hPosDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("LaLaBar/Lambda/hNegDCAToPV", "hNegDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("LaLaBar/Lambda/hDCAV0Daughters", "hDCAV0Daughters", kTH1F, {axes.axisDCAdau}); - histos.add("LaLaBar/Lambda/hDCAV0ToPV", "hDCAV0ToPV", kTH1F, {axes.axisDCAV0ToPV}); - histos.add("LaLaBar/Lambda/hV0PointingAngle", "hV0PointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("LaLaBar/Lambda/hV0Radius", "hV0Radius", kTH1F, {axes.axisRadius}); - histos.add("LaLaBar/Lambda/hV0DecayLength", "hDecayLength", kTH1F, {axes.axisProperLifeTime}); - histos.add("LaLaBar/Lambda/hV0InvMassWindow", "hInvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("LaLaBar/Lambda/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2F, {axes.axisLambdaMass, axes.axisK0Mass}); - histos.add("LaLaBar/Lambda/hPosTPCNsigma", "hPosTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("LaLaBar/Lambda/hNegTPCNsigma", "hNegTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("LaLaBar/Lambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("LaLaBar/Lambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("LaLaBar/Lambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("LaLaBar/Lambda/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("LaLaBar/Lambda/hDCAV0Daughters", "hDCAV0Daughters", kTH1D, {axes.axisDCAdau}); + histos.add("LaLaBar/Lambda/hDCAV0ToPV", "hDCAV0ToPV", kTH1D, {axes.axisDCAV0ToPV}); + histos.add("LaLaBar/Lambda/hV0PointingAngle", "hV0PointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("LaLaBar/Lambda/hV0Radius", "hV0Radius", kTH1D, {axes.axisRadius}); + histos.add("LaLaBar/Lambda/hV0DecayLength", "hDecayLength", kTH1D, {axes.axisProperLifeTime}); + histos.add("LaLaBar/Lambda/hV0InvMassWindow", "hInvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("LaLaBar/Lambda/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2D, {axes.axisLambdaMass, axes.axisK0Mass}); + histos.add("LaLaBar/Lambda/hPosTPCNsigma", "hPosTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("LaLaBar/Lambda/hNegTPCNsigma", "hNegTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("LaLaBar/Lambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("LaLaBar/Lambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); // Candidates after AntiLambda selections - histos.add("LaLaBar/AntiLambda/hPosDCAToPV", "hPosDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("LaLaBar/AntiLambda/hNegDCAToPV", "hNegDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("LaLaBar/AntiLambda/hDCAV0Daughters", "hDCADaughters", kTH1F, {axes.axisDCAdau}); - histos.add("LaLaBar/AntiLambda/hDCAV0ToPV", "hDCAV0ToPV", kTH1F, {axes.axisDCAV0ToPV}); - histos.add("LaLaBar/AntiLambda/hV0PointingAngle", "hV0PointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("LaLaBar/AntiLambda/hV0Radius", "hV0Radius", kTH1F, {axes.axisRadius}); - histos.add("LaLaBar/AntiLambda/hV0DecayLength", "hDecayLength", kTH1F, {axes.axisProperLifeTime}); - histos.add("LaLaBar/AntiLambda/hV0InvMassWindow", "hInvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("LaLaBar/AntiLambda/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2F, {axes.axisLambdaMass, axes.axisK0Mass}); - histos.add("LaLaBar/AntiLambda/hPosTPCNsigma", "hPosTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("LaLaBar/AntiLambda/hNegTPCNsigma", "hNegTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("LaLaBar/AntiLambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("LaLaBar/AntiLambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("LaLaBar/AntiLambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("LaLaBar/AntiLambda/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("LaLaBar/AntiLambda/hDCAV0Daughters", "hDCADaughters", kTH1D, {axes.axisDCAdau}); + histos.add("LaLaBar/AntiLambda/hDCAV0ToPV", "hDCAV0ToPV", kTH1D, {axes.axisDCAV0ToPV}); + histos.add("LaLaBar/AntiLambda/hV0PointingAngle", "hV0PointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("LaLaBar/AntiLambda/hV0Radius", "hV0Radius", kTH1D, {axes.axisRadius}); + histos.add("LaLaBar/AntiLambda/hV0DecayLength", "hDecayLength", kTH1D, {axes.axisProperLifeTime}); + histos.add("LaLaBar/AntiLambda/hV0InvMassWindow", "hInvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("LaLaBar/AntiLambda/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2D, {axes.axisLambdaMass, axes.axisK0Mass}); + histos.add("LaLaBar/AntiLambda/hPosTPCNsigma", "hPosTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("LaLaBar/AntiLambda/hNegTPCNsigma", "hNegTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("LaLaBar/AntiLambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("LaLaBar/AntiLambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); if (doMCAssociation) { - histos.add("LaLaBar/h3dInvMassTrueEtaC1S", "h3dInvMassTrueEtaC1S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dInvMassTrueJPsi", "h3dInvMassTrueJPsi", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dInvMassTrueChiC0", "h3dInvMassTrueChiC0", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dInvMassTrueChiC1", "h3dInvMassTrueChiC1", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dInvMassTrueHC", "h3dInvMassTrueHC", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dInvMassTrueChiC2", "h3dInvMassTrueChiC2", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("LaLaBar/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTrueEtaC1S", "h3dInvMassTrueEtaC1S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTrueJPsi", "h3dInvMassTrueJPsi", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTrueChiC0", "h3dInvMassTrueChiC0", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTrueChiC1", "h3dInvMassTrueChiC1", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTrueHC", "h3dInvMassTrueHC", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTrueChiC2", "h3dInvMassTrueChiC2", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("LaLaBar/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } if (doQA) { - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosDCAToPV", "h3dMassLaLaBarVsPosDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegDCAToPV", "h3dMassLaLaBarVsNegDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsDCAV0Daughters", "h3dMassLaLaBarVsDCAV0Daughters", kTH3F, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsDCAV0ToPV", "h3dMassLaLaBarVsDCAV0ToPV", kTH3F, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsV0PointingAngle", "h3dMassLaLaBarVsV0PointingAngle", kTH3F, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsV0Radius", "h3dMassLaLaBarVsV0Radius", kTH3F, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsDecayLength", "h3dMassLaLaBarVsDecayLength", kTH3F, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsInvMassWindow", "h3dMassLaLaBarVsInvMassWindow", kTH3F, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosTPCNsigma", "h3dMassLaLaBarVsPosTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegTPCNsigma", "h3dMassLaLaBarVsNegTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosITSclusters", "h3dMassLaLaBarVsPosITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegITSclusters", "h3dMassLaLaBarVsNegITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosNbrCrossedRows", "h3dMassLaLaBarVsPosNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegNbrCrossedRows", "h3dMassLaLaBarVsNegNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairRadius3D", "h3dMassLaLaBarVsPairRadius3D", kTH3F, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairRadius2D", "h3dMassLaLaBarVsPairRadius2D", kTH3F, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairZ", "h3dMassLaLaBarVsPairZ", kTH3F, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsDCAPair", "h3dMassLaLaBarVsDCAPair", kTH3F, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairCosPA", "h3dMassLaLaBarVsPairCosPA", kTH3F, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairOpAngle", "h3dMassLaLaBarVsPairOpAngle", kTH3F, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairEta", "h3dMassLaLaBarVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairPhi", "h3dMassLaLaBarVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/LaLaBar/h3dDeltaEtaLaLaBarVsPairEta", "h3dDeltaEtaLaLaBarVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); - histos.add("QA/LaLaBar/h3dDeltaPhiLaLaBarVsPairPhi", "h3dDeltaPhiLaLaBarVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosDCAToPV", "h3dMassLaLaBarVsPosDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegDCAToPV", "h3dMassLaLaBarVsNegDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsDCAV0Daughters", "h3dMassLaLaBarVsDCAV0Daughters", kTH3D, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsDCAV0ToPV", "h3dMassLaLaBarVsDCAV0ToPV", kTH3D, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsV0PointingAngle", "h3dMassLaLaBarVsV0PointingAngle", kTH3D, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsV0Radius", "h3dMassLaLaBarVsV0Radius", kTH3D, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsDecayLength", "h3dMassLaLaBarVsDecayLength", kTH3D, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsInvMassWindow", "h3dMassLaLaBarVsInvMassWindow", kTH3D, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosTPCNsigma", "h3dMassLaLaBarVsPosTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegTPCNsigma", "h3dMassLaLaBarVsNegTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosITSclusters", "h3dMassLaLaBarVsPosITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegITSclusters", "h3dMassLaLaBarVsNegITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPosNbrCrossedRows", "h3dMassLaLaBarVsPosNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsNegNbrCrossedRows", "h3dMassLaLaBarVsNegNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairRadius3D", "h3dMassLaLaBarVsPairRadius3D", kTH3D, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairRadius2D", "h3dMassLaLaBarVsPairRadius2D", kTH3D, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairZ", "h3dMassLaLaBarVsPairZ", kTH3D, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsDCAPair", "h3dMassLaLaBarVsDCAPair", kTH3D, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairCosPA", "h3dMassLaLaBarVsPairCosPA", kTH3D, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairOpAngle", "h3dMassLaLaBarVsPairOpAngle", kTH3D, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairEta", "h3dMassLaLaBarVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dMassLaLaBarVsPairPhi", "h3dMassLaLaBarVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/LaLaBar/h3dDeltaEtaLaLaBarVsPairEta", "h3dDeltaEtaLaLaBarVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); + histos.add("QA/LaLaBar/h3dDeltaPhiLaLaBarVsPairPhi", "h3dDeltaPhiLaLaBarVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); } } if (buildXiXiBarPairs) { - histos.add("XiXiBar/h3dMassXiXibar", "h3dMassXiXibar", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dMassXiXibar", "h3dMassXiXibar", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); if (!isPP) { // Non-UPC info - histos.add("XiXiBar/h3dMassXiXibarHadronic", "h3dMassXiXibarHadronic", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dMassXiXibarHadronic", "h3dMassXiXibarHadronic", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); // UPC info - histos.add("XiXiBar/h3dMassXiXibarSGA", "h3dMassXiXibarSGA", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dMassXiXibarSGC", "h3dMassXiXibarSGC", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dMassXiXibarDG", "h3dMassXiXibarDG", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dMassXiXibarSGA", "h3dMassXiXibarSGA", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dMassXiXibarSGC", "h3dMassXiXibarSGC", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dMassXiXibarDG", "h3dMassXiXibarDG", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } - histos.add("XiXiBar/h2dNbrOfXiVsCentrality", "h2dNbrOfXiVsCentrality", kTH2F, {axes.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("XiXiBar/h2dNbrOfAntiXiVsCentrality", "h2dNbrOfAntiXiVsCentrality", kTH2F, {axes.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("XiXiBar/h2dNbrOfXiVsCentrality", "h2dNbrOfXiVsCentrality", kTH2D, {axes.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("XiXiBar/h2dNbrOfAntiXiVsCentrality", "h2dNbrOfAntiXiVsCentrality", kTH2D, {axes.axisCentrality, {10, -0.5f, 9.5f}}); // QA plot // Candidates after Xi selections - histos.add("XiXiBar/Xi/hBachDCAToPV", "hBachDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("XiXiBar/Xi/hPosDCAToPV", "hPosDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("XiXiBar/Xi/hNegDCAToPV", "hNegDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("XiXiBar/Xi/hDCACascDaughters", "hDCACascDaughters", kTH1F, {axes.axisDCAdau}); - histos.add("XiXiBar/Xi/hDCAV0Daughters", "hDCAV0Daughters", kTH1F, {axes.axisDCAdau}); - histos.add("XiXiBar/Xi/hDCAV0ToPV", "hDCAV0ToPV", kTH1F, {axes.axisDCAV0ToPV}); - histos.add("XiXiBar/Xi/hV0PointingAngle", "hV0PointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("XiXiBar/Xi/hV0Radius", "hV0Radius", kTH1F, {axes.axisRadius}); - histos.add("XiXiBar/Xi/hCascPointingAngle", "hCascPointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("XiXiBar/Xi/hCascRadius", "hCascRadius", kTH1F, {axes.axisRadius}); - histos.add("XiXiBar/Xi/hCascDecayLength", "hCascDecayLength", kTH1F, {axes.axisProperLifeTime}); - histos.add("XiXiBar/Xi/hV0InvMassWindow", "hV0InvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("XiXiBar/Xi/hCascInvMassWindow", "hCascInvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("XiXiBar/Xi/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2F, {axes.axisXiMass, axes.axisOmegaMass}); - histos.add("XiXiBar/Xi/hBachTPCNsigma", "hBachTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("XiXiBar/Xi/hPosTPCNsigma", "hPosTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("XiXiBar/Xi/hNegTPCNsigma", "hNegTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("XiXiBar/Xi/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("XiXiBar/Xi/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("XiXiBar/Xi/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("XiXiBar/Xi/hBachDCAToPV", "hBachDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("XiXiBar/Xi/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("XiXiBar/Xi/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("XiXiBar/Xi/hDCACascDaughters", "hDCACascDaughters", kTH1D, {axes.axisDCAdau}); + histos.add("XiXiBar/Xi/hDCAV0Daughters", "hDCAV0Daughters", kTH1D, {axes.axisDCAdau}); + histos.add("XiXiBar/Xi/hDCAV0ToPV", "hDCAV0ToPV", kTH1D, {axes.axisDCAV0ToPV}); + histos.add("XiXiBar/Xi/hV0PointingAngle", "hV0PointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("XiXiBar/Xi/hV0Radius", "hV0Radius", kTH1D, {axes.axisRadius}); + histos.add("XiXiBar/Xi/hCascPointingAngle", "hCascPointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("XiXiBar/Xi/hCascRadius", "hCascRadius", kTH1D, {axes.axisRadius}); + histos.add("XiXiBar/Xi/hCascDecayLength", "hCascDecayLength", kTH1D, {axes.axisProperLifeTime}); + histos.add("XiXiBar/Xi/hV0InvMassWindow", "hV0InvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("XiXiBar/Xi/hCascInvMassWindow", "hCascInvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("XiXiBar/Xi/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2D, {axes.axisXiMass, axes.axisOmegaMass}); + histos.add("XiXiBar/Xi/hBachTPCNsigma", "hBachTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("XiXiBar/Xi/hPosTPCNsigma", "hPosTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("XiXiBar/Xi/hNegTPCNsigma", "hNegTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("XiXiBar/Xi/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("XiXiBar/Xi/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("XiXiBar/Xi/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); // Candidates after AntiXi selections - histos.add("XiXiBar/AntiXi/hBachDCAToPV", "hBachDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("XiXiBar/AntiXi/hPosDCAToPV", "hPosDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("XiXiBar/AntiXi/hNegDCAToPV", "hNegDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("XiXiBar/AntiXi/hDCACascDaughters", "hDCACascDaughters", kTH1F, {axes.axisDCAdau}); - histos.add("XiXiBar/AntiXi/hDCAV0Daughters", "hDCAV0Daughters", kTH1F, {axes.axisDCAdau}); - histos.add("XiXiBar/AntiXi/hDCAV0ToPV", "hDCAV0ToPV", kTH1F, {axes.axisDCAV0ToPV}); - histos.add("XiXiBar/AntiXi/hV0PointingAngle", "hV0PointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("XiXiBar/AntiXi/hV0Radius", "hV0Radius", kTH1F, {axes.axisRadius}); - histos.add("XiXiBar/AntiXi/hCascPointingAngle", "hCascPointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("XiXiBar/AntiXi/hCascRadius", "hCascRadius", kTH1F, {axes.axisRadius}); - histos.add("XiXiBar/AntiXi/hCascDecayLength", "hCascDecayLength", kTH1F, {axes.axisProperLifeTime}); - histos.add("XiXiBar/AntiXi/hV0InvMassWindow", "hV0InvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("XiXiBar/AntiXi/hCascInvMassWindow", "hCascInvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("XiXiBar/AntiXi/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2F, {axes.axisXiMass, axes.axisOmegaMass}); - histos.add("XiXiBar/AntiXi/hBachTPCNsigma", "hBachTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("XiXiBar/AntiXi/hPosTPCNsigma", "hPosTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("XiXiBar/AntiXi/hNegTPCNsigma", "hNegTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("XiXiBar/AntiXi/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("XiXiBar/AntiXi/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("XiXiBar/AntiXi/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("XiXiBar/AntiXi/hBachDCAToPV", "hBachDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("XiXiBar/AntiXi/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("XiXiBar/AntiXi/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("XiXiBar/AntiXi/hDCACascDaughters", "hDCACascDaughters", kTH1D, {axes.axisDCAdau}); + histos.add("XiXiBar/AntiXi/hDCAV0Daughters", "hDCAV0Daughters", kTH1D, {axes.axisDCAdau}); + histos.add("XiXiBar/AntiXi/hDCAV0ToPV", "hDCAV0ToPV", kTH1D, {axes.axisDCAV0ToPV}); + histos.add("XiXiBar/AntiXi/hV0PointingAngle", "hV0PointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("XiXiBar/AntiXi/hV0Radius", "hV0Radius", kTH1D, {axes.axisRadius}); + histos.add("XiXiBar/AntiXi/hCascPointingAngle", "hCascPointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("XiXiBar/AntiXi/hCascRadius", "hCascRadius", kTH1D, {axes.axisRadius}); + histos.add("XiXiBar/AntiXi/hCascDecayLength", "hCascDecayLength", kTH1D, {axes.axisProperLifeTime}); + histos.add("XiXiBar/AntiXi/hV0InvMassWindow", "hV0InvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("XiXiBar/AntiXi/hCascInvMassWindow", "hCascInvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("XiXiBar/AntiXi/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2D, {axes.axisXiMass, axes.axisOmegaMass}); + histos.add("XiXiBar/AntiXi/hBachTPCNsigma", "hBachTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("XiXiBar/AntiXi/hPosTPCNsigma", "hPosTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("XiXiBar/AntiXi/hNegTPCNsigma", "hNegTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("XiXiBar/AntiXi/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("XiXiBar/AntiXi/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("XiXiBar/AntiXi/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); if (doMCAssociation) { - histos.add("XiXiBar/h3dInvMassTrueEtaC1S", "h3dInvMassTrueEtaC1S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dInvMassTrueJPsi", "h3dInvMassTrueJPsi", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dInvMassTrueChiC0", "h3dInvMassTrueChiC0", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dInvMassTrueChiC1", "h3dInvMassTrueChiC1", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dInvMassTrueHC", "h3dInvMassTrueHC", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dInvMassTrueChiC2", "h3dInvMassTrueChiC2", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("XiXiBar/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTrueEtaC1S", "h3dInvMassTrueEtaC1S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTrueJPsi", "h3dInvMassTrueJPsi", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTrueChiC0", "h3dInvMassTrueChiC0", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTrueChiC1", "h3dInvMassTrueChiC1", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTrueHC", "h3dInvMassTrueHC", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTrueChiC2", "h3dInvMassTrueChiC2", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("XiXiBar/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } if (doQA) { - histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachDCAToPV", "h3dMassXiXiBarVsBachDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosDCAToPV", "h3dMassXiXiBarVsPosDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegDCAToPV", "h3dMassXiXiBarVsNegDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCACascDaughters", "h3dMassXiXiBarVsDCACascDaughters", kTH3F, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCAV0Daughters", "h3dMassXiXiBarVsDCAV0Daughters", kTH3F, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCAV0ToPV", "h3dMassXiXiBarVsDCAV0ToPV", kTH3F, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsV0PointingAngle", "h3dMassXiXiBarVsV0PointingAngle", kTH3F, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsV0Radius", "h3dMassXiXiBarVsV0Radius", kTH3F, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsCascPointingAngle", "h3dMassXiXiBarVsCascPointingAngle", kTH3F, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsCascRadius", "h3dMassXiXiBarVsCascRadius", kTH3F, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsDecayLength", "h3dMassXiXiBarVsDecayLength", kTH3F, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsV0InvMassWindow", "h3dMassXiXiBarVsV0InvMassWindow", kTH3F, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsCascInvMassWindow", "h3dMassXiXiBarVsCascInvMassWindow", kTH3F, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachTPCNsigma", "h3dMassXiXiBarVsBachTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosTPCNsigma", "h3dMassXiXiBarVsPosTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegTPCNsigma", "h3dMassXiXiBarVsNegTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachITSclusters", "h3dMassXiXiBarVsBachITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosITSclusters", "h3dMassXiXiBarVsPosITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegITSclusters", "h3dMassXiXiBarVsNegITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachNbrCrossedRows", "h3dMassXiXiBarVsBachNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosNbrCrossedRows", "h3dMassXiXiBarVsPosNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegNbrCrossedRows", "h3dMassXiXiBarVsNegNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairRadius3D", "h3dMassXiXiBarVsPairRadius3D", kTH3F, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairRadius2D", "h3dMassXiXiBarVsPairRadius2D", kTH3F, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairZ", "h3dMassXiXiBarVsPairZ", kTH3F, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCAPair", "h3dMassXiXiBarVsDCAPair", kTH3F, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairCosPA", "h3dMassXiXiBarVsPairCosPA", kTH3F, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairOpAngle", "h3dMassXiXiBarVsPairOpAngle", kTH3F, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairEta", "h3dMassXiXiBarVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairPhi", "h3dMassXiXiBarVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/XiXiBar/h3dDeltaEtaXiXiBarVsPairEta", "h3dDeltaEtaXiXiBarVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); - histos.add("QA/XiXiBar/h3dDeltaPhiXiXiBarVsPairPhi", "h3dDeltaPhiXiXiBarVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachDCAToPV", "h3dMassXiXiBarVsBachDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosDCAToPV", "h3dMassXiXiBarVsPosDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegDCAToPV", "h3dMassXiXiBarVsNegDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCACascDaughters", "h3dMassXiXiBarVsDCACascDaughters", kTH3D, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCAV0Daughters", "h3dMassXiXiBarVsDCAV0Daughters", kTH3D, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCAV0ToPV", "h3dMassXiXiBarVsDCAV0ToPV", kTH3D, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsV0PointingAngle", "h3dMassXiXiBarVsV0PointingAngle", kTH3D, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsV0Radius", "h3dMassXiXiBarVsV0Radius", kTH3D, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsCascPointingAngle", "h3dMassXiXiBarVsCascPointingAngle", kTH3D, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsCascRadius", "h3dMassXiXiBarVsCascRadius", kTH3D, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsDecayLength", "h3dMassXiXiBarVsDecayLength", kTH3D, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsV0InvMassWindow", "h3dMassXiXiBarVsV0InvMassWindow", kTH3D, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsCascInvMassWindow", "h3dMassXiXiBarVsCascInvMassWindow", kTH3D, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachTPCNsigma", "h3dMassXiXiBarVsBachTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosTPCNsigma", "h3dMassXiXiBarVsPosTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegTPCNsigma", "h3dMassXiXiBarVsNegTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachITSclusters", "h3dMassXiXiBarVsBachITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosITSclusters", "h3dMassXiXiBarVsPosITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegITSclusters", "h3dMassXiXiBarVsNegITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsBachNbrCrossedRows", "h3dMassXiXiBarVsBachNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPosNbrCrossedRows", "h3dMassXiXiBarVsPosNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsNegNbrCrossedRows", "h3dMassXiXiBarVsNegNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairRadius3D", "h3dMassXiXiBarVsPairRadius3D", kTH3D, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairRadius2D", "h3dMassXiXiBarVsPairRadius2D", kTH3D, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairZ", "h3dMassXiXiBarVsPairZ", kTH3D, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsDCAPair", "h3dMassXiXiBarVsDCAPair", kTH3D, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairCosPA", "h3dMassXiXiBarVsPairCosPA", kTH3D, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairOpAngle", "h3dMassXiXiBarVsPairOpAngle", kTH3D, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairEta", "h3dMassXiXiBarVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dMassXiXiBarVsPairPhi", "h3dMassXiXiBarVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/XiXiBar/h3dDeltaEtaXiXiBarVsPairEta", "h3dDeltaEtaXiXiBarVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); + histos.add("QA/XiXiBar/h3dDeltaPhiXiXiBarVsPairPhi", "h3dDeltaPhiXiXiBarVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); } } if (buildOmOmBarPairs) { - histos.add("OmOmBar/h3dMassOmOmbar", "h3dMassOmOmbar", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("OmOmBar/h3dMassOmOmbar", "h3dMassOmOmbar", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); if (!isPP) { // Non-UPC info - histos.add("OmOmBar/h3dMassOmOmbarHadronic", "h3dMassOmOmbarHadronic", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("OmOmBar/h3dMassOmOmbarHadronic", "h3dMassOmOmbarHadronic", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); // UPC info - histos.add("OmOmBar/h3dMassOmOmbarSGA", "h3dMassOmOmbarSGA", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("OmOmBar/h3dMassOmOmbarSGC", "h3dMassOmOmbarSGC", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("OmOmBar/h3dMassOmOmbarDG", "h3dMassOmOmbarDG", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("OmOmBar/h3dMassOmOmbarSGA", "h3dMassOmOmbarSGA", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("OmOmBar/h3dMassOmOmbarSGC", "h3dMassOmOmbarSGC", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("OmOmBar/h3dMassOmOmbarDG", "h3dMassOmOmbarDG", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } - histos.add("OmOmBar/h2dNbrOfOmegaVsCentrality", "h2dNbrOfOmegaVsCentrality", kTH2F, {axes.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("OmOmBar/h2dNbrOfAntiOmegaVsCentrality", "h2dNbrOfAntiOmegaVsCentrality", kTH2F, {axes.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("OmOmBar/h2dNbrOfOmegaVsCentrality", "h2dNbrOfOmegaVsCentrality", kTH2D, {axes.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("OmOmBar/h2dNbrOfAntiOmegaVsCentrality", "h2dNbrOfAntiOmegaVsCentrality", kTH2D, {axes.axisCentrality, {10, -0.5f, 9.5f}}); // QA plot // Candidates after Omega selections - histos.add("OmOmBar/Omega/hBachDCAToPV", "hBachDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("OmOmBar/Omega/hPosDCAToPV", "hPosDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("OmOmBar/Omega/hNegDCAToPV", "hNegDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("OmOmBar/Omega/hDCACascDaughters", "hDCACascDaughters", kTH1F, {axes.axisDCAdau}); - histos.add("OmOmBar/Omega/hDCAV0Daughters", "hDCAV0Daughters", kTH1F, {axes.axisDCAdau}); - histos.add("OmOmBar/Omega/hDCAV0ToPV", "hDCAV0ToPV", kTH1F, {axes.axisDCAV0ToPV}); - histos.add("OmOmBar/Omega/hV0PointingAngle", "hV0PointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("OmOmBar/Omega/hV0Radius", "hV0Radius", kTH1F, {axes.axisRadius}); - histos.add("OmOmBar/Omega/hCascPointingAngle", "hCascPointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("OmOmBar/Omega/hCascRadius", "hCascRadius", kTH1F, {axes.axisRadius}); - histos.add("OmOmBar/Omega/hCascDecayLength", "hCascDecayLength", kTH1F, {axes.axisProperLifeTime}); - histos.add("OmOmBar/Omega/hV0InvMassWindow", "hV0InvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("OmOmBar/Omega/hCascInvMassWindow", "hCascInvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("OmOmBar/Omega/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2F, {axes.axisXiMass, axes.axisOmegaMass}); - histos.add("OmOmBar/Omega/hBachTPCNsigma", "hBachTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("OmOmBar/Omega/hPosTPCNsigma", "hPosTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("OmOmBar/Omega/hNegTPCNsigma", "hNegTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("OmOmBar/Omega/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("OmOmBar/Omega/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("OmOmBar/Omega/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("OmOmBar/Omega/hBachDCAToPV", "hBachDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("OmOmBar/Omega/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("OmOmBar/Omega/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("OmOmBar/Omega/hDCACascDaughters", "hDCACascDaughters", kTH1D, {axes.axisDCAdau}); + histos.add("OmOmBar/Omega/hDCAV0Daughters", "hDCAV0Daughters", kTH1D, {axes.axisDCAdau}); + histos.add("OmOmBar/Omega/hDCAV0ToPV", "hDCAV0ToPV", kTH1D, {axes.axisDCAV0ToPV}); + histos.add("OmOmBar/Omega/hV0PointingAngle", "hV0PointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("OmOmBar/Omega/hV0Radius", "hV0Radius", kTH1D, {axes.axisRadius}); + histos.add("OmOmBar/Omega/hCascPointingAngle", "hCascPointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("OmOmBar/Omega/hCascRadius", "hCascRadius", kTH1D, {axes.axisRadius}); + histos.add("OmOmBar/Omega/hCascDecayLength", "hCascDecayLength", kTH1D, {axes.axisProperLifeTime}); + histos.add("OmOmBar/Omega/hV0InvMassWindow", "hV0InvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("OmOmBar/Omega/hCascInvMassWindow", "hCascInvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("OmOmBar/Omega/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2D, {axes.axisXiMass, axes.axisOmegaMass}); + histos.add("OmOmBar/Omega/hBachTPCNsigma", "hBachTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("OmOmBar/Omega/hPosTPCNsigma", "hPosTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("OmOmBar/Omega/hNegTPCNsigma", "hNegTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("OmOmBar/Omega/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("OmOmBar/Omega/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("OmOmBar/Omega/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); // Candidates after AntiOmega selections - histos.add("OmOmBar/AntiOmega/hBachDCAToPV", "hBachDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("OmOmBar/AntiOmega/hPosDCAToPV", "hPosDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("OmOmBar/AntiOmega/hNegDCAToPV", "hNegDCAToPV", kTH1F, {axes.axisDCAtoPV}); - histos.add("OmOmBar/AntiOmega/hDCACascDaughters", "hDCACascDaughters", kTH1F, {axes.axisDCAdau}); - histos.add("OmOmBar/AntiOmega/hDCAV0Daughters", "hDCAV0Daughters", kTH1F, {axes.axisDCAdau}); - histos.add("OmOmBar/AntiOmega/hDCAV0ToPV", "hDCAV0ToPV", kTH1F, {axes.axisDCAV0ToPV}); - histos.add("OmOmBar/AntiOmega/hV0PointingAngle", "hV0PointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("OmOmBar/AntiOmega/hV0Radius", "hV0Radius", kTH1F, {axes.axisRadius}); - histos.add("OmOmBar/AntiOmega/hCascPointingAngle", "hCascPointingAngle", kTH1F, {axes.axisPointingAngle}); - histos.add("OmOmBar/AntiOmega/hCascRadius", "hCascRadius", kTH1F, {axes.axisRadius}); - histos.add("OmOmBar/AntiOmega/hCascDecayLength", "hCascDecayLength", kTH1F, {axes.axisProperLifeTime}); - histos.add("OmOmBar/AntiOmega/hV0InvMassWindow", "hV0InvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("OmOmBar/AntiOmega/hCascInvMassWindow", "hCascInvMassWindow", kTH1F, {axes.axisMassWindow}); - histos.add("OmOmBar/AntiOmega/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2F, {axes.axisXiMass, axes.axisOmegaMass}); - histos.add("OmOmBar/AntiOmega/hBachTPCNsigma", "hBachTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("OmOmBar/AntiOmega/hPosTPCNsigma", "hPosTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("OmOmBar/AntiOmega/hNegTPCNsigma", "hNegTPCNsigma", kTH1F, {axes.axisNsigmaTPC}); - histos.add("OmOmBar/AntiOmega/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("OmOmBar/AntiOmega/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); - histos.add("OmOmBar/AntiOmega/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2F, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("OmOmBar/AntiOmega/hBachDCAToPV", "hBachDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("OmOmBar/AntiOmega/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("OmOmBar/AntiOmega/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axes.axisDCAtoPV}); + histos.add("OmOmBar/AntiOmega/hDCACascDaughters", "hDCACascDaughters", kTH1D, {axes.axisDCAdau}); + histos.add("OmOmBar/AntiOmega/hDCAV0Daughters", "hDCAV0Daughters", kTH1D, {axes.axisDCAdau}); + histos.add("OmOmBar/AntiOmega/hDCAV0ToPV", "hDCAV0ToPV", kTH1D, {axes.axisDCAV0ToPV}); + histos.add("OmOmBar/AntiOmega/hV0PointingAngle", "hV0PointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("OmOmBar/AntiOmega/hV0Radius", "hV0Radius", kTH1D, {axes.axisRadius}); + histos.add("OmOmBar/AntiOmega/hCascPointingAngle", "hCascPointingAngle", kTH1D, {axes.axisPointingAngle}); + histos.add("OmOmBar/AntiOmega/hCascRadius", "hCascRadius", kTH1D, {axes.axisRadius}); + histos.add("OmOmBar/AntiOmega/hCascDecayLength", "hCascDecayLength", kTH1D, {axes.axisProperLifeTime}); + histos.add("OmOmBar/AntiOmega/hV0InvMassWindow", "hV0InvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("OmOmBar/AntiOmega/hCascInvMassWindow", "hCascInvMassWindow", kTH1D, {axes.axisMassWindow}); + histos.add("OmOmBar/AntiOmega/h2dCompetingMassRej", "h2dCompetingMassRej", kTH2D, {axes.axisXiMass, axes.axisOmegaMass}); + histos.add("OmOmBar/AntiOmega/hBachTPCNsigma", "hBachTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("OmOmBar/AntiOmega/hPosTPCNsigma", "hPosTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("OmOmBar/AntiOmega/hNegTPCNsigma", "hNegTPCNsigma", kTH1D, {axes.axisNsigmaTPC}); + histos.add("OmOmBar/AntiOmega/h2dBachelorITSvsTPCpts", "h2dBachelorITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("OmOmBar/AntiOmega/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); + histos.add("OmOmBar/AntiOmega/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axes.axisTPCrows, axes.axisITSclus}); if (doMCAssociation) { - histos.add("OmOmBar/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("OmOmBar/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3F, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("OmOmBar/h3dInvMassTrueEtaC2S", "h3dInvMassTrueEtaC2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("OmOmBar/h3dInvMassTruePsi2S", "h3dInvMassTruePsi2S", kTH3D, {axes.axisCentrality, axes.axisPt, axes.axisQuarkoniumMass}); } if (doQA) { - histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachDCAToPV", "h3dMassOmOmBarVsBachDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosDCAToPV", "h3dMassOmOmBarVsPosDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegDCAToPV", "h3dMassOmOmBarVsNegDCAToPV", kTH3F, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCACascDaughters", "h3dMassOmOmBarVsDCACascDaughters", kTH3F, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCAV0Daughters", "h3dMassOmOmBarVsDCAV0Daughters", kTH3F, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCAV0ToPV", "h3dMassOmOmBarVsDCAV0ToPV", kTH3F, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsV0PointingAngle", "h3dMassOmOmBarVsV0PointingAngle", kTH3F, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsV0Radius", "h3dMassOmOmBarVsV0Radius", kTH3F, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsCascPointingAngle", "h3dMassOmOmBarVsCascPointingAngle", kTH3F, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsCascRadius", "h3dMassOmOmBarVsCascRadius", kTH3F, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsDecayLength", "h3dMassOmOmBarVsDecayLength", kTH3F, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsV0InvMassWindow", "h3dMassOmOmBarVsV0InvMassWindow", kTH3F, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsCascInvMassWindow", "h3dMassOmOmBarVsCascInvMassWindow", kTH3F, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachTPCNsigma", "h3dMassOmOmBarVsBachTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosTPCNsigma", "h3dMassOmOmBarVsPosTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegTPCNsigma", "h3dMassOmOmBarVsNegTPCNsigma", kTH3F, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachITSclusters", "h3dMassOmOmBarVsBachITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosITSclusters", "h3dMassOmOmBarVsPosITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegITSclusters", "h3dMassOmOmBarVsNegITSclusters", kTH3F, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachNbrCrossedRows", "h3dMassOmOmBarVsBachNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosNbrCrossedRows", "h3dMassOmOmBarVsPosNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegNbrCrossedRows", "h3dMassOmOmBarVsNegNbrCrossedRows", kTH3F, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairRadius3D", "h3dMassOmOmBarVsPairRadius3D", kTH3F, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairRadius2D", "h3dMassOmOmBarVsPairRadius2D", kTH3F, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairZ", "h3dMassOmOmBarVsPairZ", kTH3F, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCAPair", "h3dMassOmOmBarVsDCAPair", kTH3F, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairCosPA", "h3dMassOmOmBarVsPairCosPA", kTH3F, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairOpAngle", "h3dMassOmOmBarVsPairOpAngle", kTH3F, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairEta", "h3dMassOmOmBarVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairPhi", "h3dMassOmOmBarVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); - histos.add("QA/OmOmBar/h3dDeltaEtaOmOmBarVsPairEta", "h3dDeltaEtaOmOmBarVsPairEta", kTH3F, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); - histos.add("QA/OmOmBar/h3dDeltaPhiOmOmBarVsPairPhi", "h3dDeltaPhiOmOmBarVsPairPhi", kTH3F, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachDCAToPV", "h3dMassOmOmBarVsBachDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosDCAToPV", "h3dMassOmOmBarVsPosDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegDCAToPV", "h3dMassOmOmBarVsNegDCAToPV", kTH3D, {axes.axisDCAtoPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCACascDaughters", "h3dMassOmOmBarVsDCACascDaughters", kTH3D, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCAV0Daughters", "h3dMassOmOmBarVsDCAV0Daughters", kTH3D, {axes.axisDCAdau, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCAV0ToPV", "h3dMassOmOmBarVsDCAV0ToPV", kTH3D, {axes.axisDCAV0ToPV, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsV0PointingAngle", "h3dMassOmOmBarVsV0PointingAngle", kTH3D, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsV0Radius", "h3dMassOmOmBarVsV0Radius", kTH3D, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsCascPointingAngle", "h3dMassOmOmBarVsCascPointingAngle", kTH3D, {axes.axisPointingAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsCascRadius", "h3dMassOmOmBarVsCascRadius", kTH3D, {axes.axisRadius, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsDecayLength", "h3dMassOmOmBarVsDecayLength", kTH3D, {axes.axisProperLifeTime, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsV0InvMassWindow", "h3dMassOmOmBarVsV0InvMassWindow", kTH3D, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsCascInvMassWindow", "h3dMassOmOmBarVsCascInvMassWindow", kTH3D, {axes.axisMassWindow, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachTPCNsigma", "h3dMassOmOmBarVsBachTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosTPCNsigma", "h3dMassOmOmBarVsPosTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegTPCNsigma", "h3dMassOmOmBarVsNegTPCNsigma", kTH3D, {axes.axisNsigmaTPC, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachITSclusters", "h3dMassOmOmBarVsBachITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosITSclusters", "h3dMassOmOmBarVsPosITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegITSclusters", "h3dMassOmOmBarVsNegITSclusters", kTH3D, {axes.axisITSclus, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsBachNbrCrossedRows", "h3dMassOmOmBarVsBachNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPosNbrCrossedRows", "h3dMassOmOmBarVsPosNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsNegNbrCrossedRows", "h3dMassOmOmBarVsNegNbrCrossedRows", kTH3D, {axes.axisTPCrows, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairRadius3D", "h3dMassOmOmBarVsPairRadius3D", kTH3D, {axes.axisHypPairRadius3D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairRadius2D", "h3dMassOmOmBarVsPairRadius2D", kTH3D, {axes.axisHypPairRadius2D, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairZ", "h3dMassOmOmBarVsPairZ", kTH3D, {axes.axisHypPairZ, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsDCAPair", "h3dMassOmOmBarVsDCAPair", kTH3D, {axes.axisDCAHypPair, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairCosPA", "h3dMassOmOmBarVsPairCosPA", kTH3D, {axes.axisHypPairCosPA, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairOpAngle", "h3dMassOmOmBarVsPairOpAngle", kTH3D, {axes.axisHypPairOpAngle, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairEta", "h3dMassOmOmBarVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dMassOmOmBarVsPairPhi", "h3dMassOmOmBarVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisQuarkoniumMass}); + histos.add("QA/OmOmBar/h3dDeltaEtaOmOmBarVsPairEta", "h3dDeltaEtaOmOmBarVsPairEta", kTH3D, {axes.axisHypPairEta, axes.axisPt, axes.axisHypPairEta}); + histos.add("QA/OmOmBar/h3dDeltaPhiOmOmBarVsPairPhi", "h3dDeltaPhiOmOmBarVsPairPhi", kTH3D, {axes.axisHypPairPhi, axes.axisPt, axes.axisHypPairPhi}); } } @@ -935,7 +935,7 @@ struct QuarkoniaToHyperons { histos.print(); } - template // TCollision should be of the type: soa::Join::iterator or so + template // TCollision should be of the type: soa::Join::iterator or so void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber() || (ccdbConfigurations.useCustomRunNumber && mRunNumber == ccdbConfigurations.customRunNumber)) { @@ -1203,7 +1203,7 @@ struct QuarkoniaToHyperons { } template - bool isEventAccepted(TCollision collision, bool fillHists) + bool isEventAccepted(const TCollision& collision, bool fillHists) // check whether the collision passes our collision selections { if (fillHists) @@ -1295,7 +1295,7 @@ struct QuarkoniaToHyperons { } template - void fillEventHistograms(TCollision collision, float& centrality, int& selGapSide) + void fillEventHistograms(const TCollision& collision, float& centrality, int& selGapSide) { if (isPP) { // centrality = collision.centFT0M(); @@ -1337,7 +1337,7 @@ struct QuarkoniaToHyperons { } template - uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) + uint64_t computeReconstructionBitmap(const TV0& v0, const TCollision& collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) // precalculate this information so that a check is one mask operation, not many { uint64_t bitMap = 0; @@ -1492,7 +1492,7 @@ struct QuarkoniaToHyperons { } template - bool isCascadeSelected(TCascade casc, TCollision collision, float rapidity, bool isXi) + bool isCascadeSelected(const TCascade& casc, const TCollision& collision, float rapidity, bool isXi) // precalculate this information so that a check is one mask operation, not many { // @@ -1728,7 +1728,7 @@ struct QuarkoniaToHyperons { } template - uint64_t computeMCAssociation(TV0 v0) + uint64_t computeMCAssociation(const TV0& v0) // precalculate this information so that a check is one mask operation, not many { uint64_t bitMap = 0; @@ -1758,7 +1758,7 @@ struct QuarkoniaToHyperons { } template - void analyseV0Candidate(TV0 v0, float pt, uint64_t selMap, std::vector& selK0ShortIndices, std::vector& selLambdaIndices, std::vector& selAntiLambdaIndices /*, int v0TableOffset*/) + void analyseV0Candidate(const TV0& v0, float pt, uint64_t selMap, std::vector& selK0ShortIndices, std::vector& selLambdaIndices, std::vector& selAntiLambdaIndices /*, int v0TableOffset*/) // precalculate this information so that a check is one mask operation, not many { bool passK0ShortSelections = false; @@ -1773,7 +1773,7 @@ struct QuarkoniaToHyperons { float k0shortScore = -1; if (mlConfigurations.calculateK0ShortScores) { // evaluate machine-learning scores - float* k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); + const std::vector k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); k0shortScore = k0shortProbability[1]; } else { k0shortScore = v0.k0ShortBDTScore(); @@ -1788,7 +1788,7 @@ struct QuarkoniaToHyperons { float lambdaScore = -1; if (mlConfigurations.calculateLambdaScores) { // evaluate machine-learning scores - float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); + const std::vector lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); lambdaScore = lambdaProbability[1]; } else { lambdaScore = v0.lambdaBDTScore(); @@ -1803,7 +1803,7 @@ struct QuarkoniaToHyperons { float antiLambdaScore = -1; if (mlConfigurations.calculateAntiLambdaScores) { // evaluate machine-learning scores - float* antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); + const std::vector antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); antiLambdaScore = antilambdaProbability[1]; } else { antiLambdaScore = v0.antiLambdaBDTScore(); @@ -1825,7 +1825,7 @@ struct QuarkoniaToHyperons { } template - void fillQAplot(TCollision collision, PairTopoInfo pair, THyperon hyperon, THyperon antiHyperon, float pt, float invmass, int type) + void fillQAplot(const TCollision& collision, PairTopoInfo pair, const THyperon& hyperon, const THyperon& antiHyperon, float pt, float invmass, int type) { // fill QA information about hyperon - antihyperon pair if (type == 0) { if constexpr (requires { hyperon.mK0Short(); antiHyperon.mK0Short(); }) { // check if v0 information is available @@ -2152,7 +2152,7 @@ struct QuarkoniaToHyperons { } template - void analyseHyperonPairCandidate(TCollision collision, THyperon hyperon, THyperon antiHyperon, float centrality, uint8_t gapSide, int type) + void analyseHyperonPairCandidate(const TCollision& collision, const THyperon& hyperon, const THyperon& antiHyperon, float centrality, uint8_t gapSide, int type) // fill information related to the quarkonium mother // type = 0 (Lambda), 1 (Xi), 2 (Omega) { @@ -2217,13 +2217,13 @@ struct QuarkoniaToHyperons { if (hyperonMC.pdgCodeMother() == 441 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // EtaC(1S) histos.fill(HIST("K0sK0s/h3dInvMassTrueEtaC1S"), centrality, ptmc, invmass); } - if (hyperonMC.pdgCodeMother() == 443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // J/psi + if (hyperonMC.pdgCodeMother() == o2::constants::physics::kJPsi && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // J/psi histos.fill(HIST("K0sK0s/h3dInvMassTrueJPsi"), centrality, ptmc, invmass); } if (hyperonMC.pdgCodeMother() == 10441 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC0 histos.fill(HIST("K0sK0s/h3dInvMassTrueChiC0"), centrality, ptmc, invmass); } - if (hyperonMC.pdgCodeMother() == 20443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC1 + if (hyperonMC.pdgCodeMother() == o2::constants::physics::kChiC1 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC1 histos.fill(HIST("K0sK0s/h3dInvMassTrueChiC1"), centrality, ptmc, invmass); } if (hyperonMC.pdgCodeMother() == 10443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // hC @@ -2273,13 +2273,13 @@ struct QuarkoniaToHyperons { if (hyperonMC.pdgCodeMother() == 441 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // EtaC(1S) histos.fill(HIST("LaLaBar/h3dInvMassTrueEtaC1S"), centrality, ptmc, invmass); } - if (hyperonMC.pdgCodeMother() == 443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // J/psi + if (hyperonMC.pdgCodeMother() == o2::constants::physics::kJPsi && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // J/psi histos.fill(HIST("LaLaBar/h3dInvMassTrueJPsi"), centrality, ptmc, invmass); } if (hyperonMC.pdgCodeMother() == 10441 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC0 histos.fill(HIST("LaLaBar/h3dInvMassTrueChiC0"), centrality, ptmc, invmass); } - if (hyperonMC.pdgCodeMother() == 20443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC1 + if (hyperonMC.pdgCodeMother() == o2::constants::physics::kChiC1 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC1 histos.fill(HIST("LaLaBar/h3dInvMassTrueChiC1"), centrality, ptmc, invmass); } if (hyperonMC.pdgCodeMother() == 10443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // hC @@ -2329,13 +2329,13 @@ struct QuarkoniaToHyperons { if (hyperonMC.pdgCodeMother() == 441 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // EtaC(1S) histos.fill(HIST("XiXiBar/h3dInvMassTrueEtaC1S"), centrality, ptmc, invmass); } - if (hyperonMC.pdgCodeMother() == 443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // J/psi + if (hyperonMC.pdgCodeMother() == o2::constants::physics::kJPsi && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // J/psi histos.fill(HIST("XiXiBar/h3dInvMassTrueJPsi"), centrality, ptmc, invmass); } if (hyperonMC.pdgCodeMother() == 10441 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC0 histos.fill(HIST("XiXiBar/h3dInvMassTrueChiC0"), centrality, ptmc, invmass); } - if (hyperonMC.pdgCodeMother() == 20443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC1 + if (hyperonMC.pdgCodeMother() == o2::constants::physics::kChiC1 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // ChiC1 histos.fill(HIST("XiXiBar/h3dInvMassTrueChiC1"), centrality, ptmc, invmass); } if (hyperonMC.pdgCodeMother() == 10443 && hyperonMC.pdgCodeMother() == antiHyperonMC.pdgCodeMother()) { // hC @@ -2408,7 +2408,7 @@ struct QuarkoniaToHyperons { // function to check that the hyperon and antihyperon have different daughter tracks template - bool checkTrackIndices(THyperon hyperon, THyperon antiHyperon) + bool checkTrackIndices(const THyperon& hyperon, const THyperon& antiHyperon) { if constexpr (requires { hyperon.template bachTrackExtra_as(); }) { // cascade case: check if bachelor information is available // check that bachelor track from hyperon is different from daughter tracks of antiHyperon @@ -2497,7 +2497,7 @@ struct QuarkoniaToHyperons { // ______________________________________________________ // Real data processing - no MC subscription - void processRealData(soa::Join const& collisions, V0Candidates const& fullV0s, CascadeCandidates const& fullCascades, DauTracks const&) + void processRealData(soa::Join const& collisions, V0Candidates const& fullV0s, CascadeCandidates const& fullCascades, DauTracks const&) { // Custom grouping v0sGrouped.clear(); @@ -2674,7 +2674,7 @@ struct QuarkoniaToHyperons { // ______________________________________________________ // Simulated processing (subscribes to MC information too) - void processMonteCarlo(soa::Join const& collisions, V0MCCandidates const& fullV0s, CascadeMCCandidates const& fullCascades, DauTracks const&, aod::MotherMCParts const&, soa::Join const& /*mccollisions*/, soa::Join const&, soa::Join const&) + void processMonteCarlo(soa::Join const& collisions, V0MCCandidates const& fullV0s, CascadeMCCandidates const& fullCascades, DauTracks const&, aod::MotherMCParts const&, soa::Join const& /*mccollisions*/, soa::Join const&, soa::Join const&) { // Custom grouping v0sGrouped.clear(); diff --git a/PWGDQ/Tasks/tableReader.cxx b/PWGDQ/Tasks/tableReader.cxx index 3c776e2a42d..dc8095e31a7 100644 --- a/PWGDQ/Tasks/tableReader.cxx +++ b/PWGDQ/Tasks/tableReader.cxx @@ -108,15 +108,15 @@ using MyEventsVtxCovSelectedMultExtra = soa::Join; using MyEventsVtxCovQvectorExtraWithRefFlow = soa::Join; using MyEventsVtxCovSelectedQvectorExtraWithRefFlow = soa::Join; -using MyEventsVtxCovSelectedQvectorCentr = soa::Join; +using MyEventsVtxCovSelectedQvectorCentr = soa::Join; using MyEventsQvector = soa::Join; using MyEventsQvectorMultExtra = soa::Join; -using MyEventsQvectorCentr = soa::Join; -using MyEventsQvectorCentrMultExtra = soa::Join; +using MyEventsQvectorCentr = soa::Join; +using MyEventsQvectorCentrMultExtra = soa::Join; using MyEventsQvectorExtra = soa::Join; using MyEventsHashSelectedQvector = soa::Join; using MyEventsHashSelectedQvectorExtra = soa::Join; -using MyEventsHashSelectedQvectorCentr = soa::Join; +using MyEventsHashSelectedQvectorCentr = soa::Join; using MyEventsVtxCovQvectorMultExtraWithRefFlow = soa::Join; using MyBarrelTracks = soa::Join; @@ -158,7 +158,7 @@ constexpr static int pairTypeMuMu = VarManager::kDecayToMuMu; constexpr static int pairTypeEMu = VarManager::kElectronMuon; // Global function used to define needed histogram classes -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar); // defines histograms for all tasks struct AnalysisEventSelection { Produces eventSel; @@ -177,8 +177,8 @@ struct AnalysisEventSelection { HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; - AnalysisCompositeCut* fEventCut; - int fLastRun; + AnalysisCompositeCut* fEventCut = nullptr; + int fLastRun = -1; Service fCCDB; @@ -273,6 +273,10 @@ struct AnalysisEventSelection { if (fMixHandler != nullptr) { int hh = fMixHandler->FindEventCategory(VarManager::fgValues); + // events outside the mixing limits (-1) get a distinct negative hash so that they are not mixed with each other + if (hh < 0) { + hh = -1 - static_cast(event.globalIndex()); + } hash(hh); } } @@ -345,10 +349,12 @@ struct AnalysisTrackSelection { Service fCCDB; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. + + int64_t reserveSize = 0; void init(o2::framework::InitContext& context) { @@ -416,7 +422,8 @@ struct AnalysisTrackSelection { fCurrentRun = event.runNumber(); } - trackSel.reserve(tracks.size()); + reserveSize += tracks.size(); + trackSel.reserve(reserveSize); uint32_t filterMap = 0; bool prefilterSelected = false; int iCut = 0; @@ -472,11 +479,13 @@ struct AnalysisMuonSelection { Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; Configurable fConfigAddMuonHistogram{"cfgAddMuonHistogram", "", "Comma separated list of histograms"}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fMuonCuts; Filter filterEventSelected = aod::dqanalysisflags::isEventSelected == 1; + int64_t reserveSize = 0; + void init(o2::framework::InitContext& context) { if (context.mOptions.get("processDummy")) { @@ -516,7 +525,8 @@ struct AnalysisMuonSelection { VarManager::ResetValues(0, VarManager::kNMuonTrackVariables); VarManager::FillEvent(event); - muonSel.reserve(muons.size()); + reserveSize += muons.size(); + muonSel.reserve(reserveSize); uint32_t filterMap = 0; int iCut = 0; @@ -592,14 +602,14 @@ struct AnalysisPrefilterSelection { Service ccdb; o2::parameters::GRPMagField* grpmag = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. Filter barrelTracksSelectedPrefilter = aod::dqanalysisflags::isBarrelSelectedPrefilter > 0; Partition> barrelTracksSelected = aod::dqanalysisflags::isBarrelSelected > 0; std::map fPrefiltermap; - AnalysisCompositeCut* fPairCut; + AnalysisCompositeCut* fPairCut = nullptr; void init(o2::framework::InitContext& context) { @@ -709,13 +719,13 @@ struct AnalysisEventMixing { TH2D* SingleMuv24m = nullptr; // Single muon v24, loaded from CCDB TH2D* SingleMuv22p = nullptr; // Single antimuon v22, loaded from CCDB TH2D* SingleMuv24p = nullptr; // Single antimuon v24, loaded from CCDB - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. Filter filterEventSelected = aod::dqanalysisflags::isEventSelected == 1; Filter filterTrackSelected = aod::dqanalysisflags::isBarrelSelected > 0; Filter filterMuonTrackSelected = aod::dqanalysisflags::isMuonSelected > 0; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; // NOTE: The bit mask is required to run pairing just based on the desired electron/muon candidate cuts uint32_t fTwoTrackFilterMask = 0; uint32_t fTwoMuonFilterMask = 0; @@ -1026,6 +1036,7 @@ struct AnalysisSameEventPairing { Produces dielectronInfoList; Produces dimuonExtraList; Produces dielectronAllList; + Produces dielectronMlList; Produces dimuonAllList; Produces dileptonFlowList; Produces dileptonInfoList; @@ -1035,7 +1046,7 @@ struct AnalysisSameEventPairing { o2::base::MatLayerCylSet* lut = nullptr; TH1D* ResoFlowSP = nullptr; // Resolution factors for flow analysis, this will be loaded from CCDB TH1D* ResoFlowEP = nullptr; // Resolution factors for flow analysis, this will be loaded from CCDB - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; Configurable fConfigTrackCuts{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; @@ -1086,7 +1097,7 @@ struct AnalysisSameEventPairing { Filter filterMuonTrackSelected = aod::dqanalysisflags::isMuonSelected > 0; Filter prefilter = aod::dqanalysisflags::isPrefilterVetoed == 0; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; o2::analysis::DQMlResponse fDQMlResponse; std::vector fOutputMlPsi2ee = {}; // TODO: check this is needed or not @@ -1102,6 +1113,8 @@ struct AnalysisSameEventPairing { std::vector> fTrackMuonHistNames; std::vector fPairCuts; + int64_t reserveSize = 0; + void init(o2::framework::InitContext& context) { if (context.mOptions.get("processDummy")) { @@ -1204,7 +1217,6 @@ struct AnalysisSameEventPairing { histNames += Form("%s;%s;%s;", names[0].Data(), names[1].Data(), names[2].Data()); fTrackHistNames.push_back(names); - TString cutNamesStr = fConfigPairCuts.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < objArrayPair->GetEntries(); ++iPairCut) { // loop over pair cuts @@ -1242,7 +1254,6 @@ struct AnalysisSameEventPairing { } fMuonHistNames.push_back(names); - TString cutNamesStr = fConfigPairCuts.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < objArrayPair->GetEntries(); ++iPairCut) { // loop over pair cuts @@ -1275,11 +1286,10 @@ struct AnalysisSameEventPairing { histNames += Form("%s;%s;%s;", names[0].Data(), names[1].Data(), names[2].Data()); fTrackMuonHistNames.push_back(names); - TString cutNamesStr = fConfigPairCuts.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); for (int iPairCut = 0; iPairCut < objArrayPair->GetEntries(); ++iPairCut) { // loop over pair cuts - std::vector names = { + names = { Form("PairsEleMuSEPM_%s_%s_%s", objArrayBarrel->At(icut)->GetName(), objArrayMuon->At(icut)->GetName(), objArrayPair->At(iPairCut)->GetName()), Form("PairsEleMuSEPP_%s_%s_%s", objArrayBarrel->At(icut)->GetName(), objArrayMuon->At(icut)->GetName(), objArrayPair->At(iPairCut)->GetName()), Form("PairsEleMuSEMM_%s_%s_%s", objArrayBarrel->At(icut)->GetName(), objArrayMuon->At(icut)->GetName(), objArrayPair->At(iPairCut)->GetName())}; @@ -1298,7 +1308,7 @@ struct AnalysisSameEventPairing { // ccdb->setLocalObjectValidityChecking(); // ccdb->setCreatedNotAfter(nolaterthan.value); - VarManager::SetCollisionSystem((TString)fCollisionSystem, fCenterMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fCollisionSystem), fCenterMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON @@ -1371,19 +1381,26 @@ struct AnalysisSameEventPairing { uint32_t twoTrackFilter = 0; uint32_t dileptonFilterMap = 0; uint32_t dileptonMcDecision = 0; // placeholder, copy of the dqEfficiency.cxx one - dielectronList.reserve(1); - dimuonList.reserve(1); - dielectronExtraList.reserve(1); - dielectronInfoList.reserve(1); - dimuonExtraList.reserve(1); - dileptonInfoList.reserve(1); - dileptonFlowList.reserve(1); + + if constexpr (TPairType != VarManager::kElectronMuon) { + // tracks1 = tracks2 + // since emu does not require any table in this task, reserveSize is updated for same particle only now + reserveSize += tracks1.size() * (tracks1.size() - 1) / 2; + } + dielectronList.reserve(reserveSize); + dimuonList.reserve(reserveSize); + dielectronExtraList.reserve(reserveSize); + dielectronInfoList.reserve(reserveSize); + dimuonExtraList.reserve(reserveSize); + dileptonInfoList.reserve(reserveSize); + dileptonFlowList.reserve(reserveSize); if (fConfigFlatTables.value) { - dielectronAllList.reserve(1); - dimuonAllList.reserve(1); + dielectronAllList.reserve(reserveSize); + dielectronMlList.reserve(reserveSize); + dimuonAllList.reserve(reserveSize); } if (useMiniTree.fConfigMiniTree) { - dileptonMiniTree.reserve(1); + dileptonMiniTree.reserve(reserveSize); } bool isSelectedBDT = false; @@ -1446,7 +1463,7 @@ struct AnalysisSameEventPairing { VarManager::FillPair(t1, t2); if constexpr ((TPairType == pairTypeEE) || (TPairType == pairTypeMuMu)) { // call this just for ee or mumu pairs if constexpr (TTwoProngFitter == true) { - VarManager::FillPairVertexing(event, t1, t2, fPropToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -1466,6 +1483,8 @@ struct AnalysisSameEventPairing { } } if constexpr ((TPairType == pairTypeEE) && (TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelPID) > 0) { + isSelectedBDT = false; + fOutputMlPsi2ee.clear(); if (applyBDT) { std::vector dqInputFeatures = fDQMlResponse.getInputFeatures(t1, t2, VarManager::fgValues); @@ -1497,7 +1516,7 @@ struct AnalysisSameEventPairing { LOG(debug) << "Model index: " << modelIndex << ", pT: " << VarManager::fgValues[VarManager::kPt] << ", centrality (kCentFT0C): " << VarManager::fgValues[VarManager::kCentFT0C]; isSelectedBDT = fDQMlResponse.isSelectedMl(dqInputFeatures, modelIndex, fOutputMlPsi2ee); - VarManager::FillBdtScore(fOutputMlPsi2ee); // TODO: check if this is needed or not + VarManager::FillBdtScore(fOutputMlPsi2ee); } if (applyBDT && !isSelectedBDT) @@ -1512,6 +1531,10 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kKFMass], VarManager::fgValues[VarManager::kKFChi2OverNDFGeo], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], VarManager::fgValues[VarManager::kVertexingLxy], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kKFCosPA], VarManager::fgValues[VarManager::kKFJpsiDCAxyz], VarManager::fgValues[VarManager::kKFJpsiDCAxy], VarManager::fgValues[VarManager::kKFPairDeviationFromPV], VarManager::fgValues[VarManager::kKFPairDeviationxyFromPV], VarManager::fgValues[VarManager::kKFMassGeoTop], VarManager::fgValues[VarManager::kKFChi2OverNDFGeoTop], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingLzProjected], VarManager::fgValues[VarManager::kVertexingLxyProjected]); + dielectronMlList(VarManager::fgValues[VarManager::kCentFT0C], + (applyBDT && fOutputMlPsi2ee.size() > 0) ? VarManager::fgValues[VarManager::kBdtBackground] : -999.f, + (applyBDT && fOutputMlPsi2ee.size() > 1) ? VarManager::fgValues[VarManager::kBdtPrompt] : -999.f, + (applyBDT && fOutputMlPsi2ee.size() > 2) ? VarManager::fgValues[VarManager::kBdtNonprompt] : -999.f); } } if constexpr (TPairType == pairTypeMuMu) { @@ -1850,7 +1873,7 @@ struct AnalysisDileptonHadron { // To be modified/adapted if new requirements appear float mMagField = 0.0; o2::parameters::GRPMagField* grpmag = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = 0; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; // TODO: For now this is only used to determine the position in the filter bit map for the hadron cut @@ -1876,14 +1899,14 @@ struct AnalysisDileptonHadron { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use two values array to avoid mixing up the quantities - float* fValuesDilepton; - float* fValuesHadron; - HistogramManager* fHistMan; + float* fValuesDilepton = nullptr; + float* fValuesHadron = nullptr; + HistogramManager* fHistMan = nullptr; // NOTE: the barrel track filter is shared between the filters for dilepton electron candidates (first n-bits) // and the associated hadrons (n+1 bit) --> see the barrel track selection task // The current condition should be replaced when bitwise operators will become available in Filter expressions - int fNHadronCutBit; + int fNHadronCutBit = 0; NoBinningPolicy hashBin; @@ -1962,13 +1985,13 @@ struct AnalysisDileptonHadron { std::vector trackGlobalIndexes; if (dileptons.size() > 0) { - for (auto track : tracks) { + for (const auto& track : tracks) { trackGlobalIndexes.push_back(track.globalIndex()); // std::cout << track.index() << " " << track.globalIndex() << std::endl; } } // loop once over dileptons for QA purposes - for (auto dilepton : dileptons) { + for (const auto& dilepton : dileptons) { VarManager::FillTrack(dilepton, fValuesDilepton); fHistMan->FillHistClass("DileptonsSelected", fValuesDilepton); @@ -2040,7 +2063,7 @@ struct AnalysisDileptonHadron { auto evTracks = tracks.sliceBy(perEventTracks, event2.globalIndex()); evTracks.bindExternalIndices(&events); - for (auto dilepton : evDileptons) { + for (const auto& dilepton : evDileptons) { for (auto& track : evTracks) { if (!(static_cast(track.isBarrelSelected()) & (static_cast(1) << fNHadronCutBit))) { @@ -2087,14 +2110,14 @@ struct AnalysisDileptonTrackTrack { constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use some values array to avoid mixing up the quantities - float* fValuesQuadruplet; - HistogramManager* fHistMan; + float* fValuesQuadruplet = nullptr; + HistogramManager* fHistMan = nullptr; // cut name setting TString fTrackCutName1; TString fTrackCutName2; bool fIsSameTrackCut = false; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; @@ -2167,14 +2190,14 @@ struct AnalysisDileptonTrackTrack { std::vector trackGlobalIndexes; if (dileptons.size() > 0) { - for (auto track : tracks) { + for (const auto& track : tracks) { trackGlobalIndexes.push_back(track.globalIndex()); // std::cout << track.index() << " " << track.globalIndex() << std::endl; } } // loop over dileptons - for (auto dilepton : dileptons) { + for (const auto& dilepton : dileptons) { VarManager::FillTrack(dilepton, fValuesQuadruplet); // apply the dilepton cut @@ -2293,7 +2316,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/tableReaderWithAssocPCH.cxx b/PWGDQ/Tasks/tableReaderWithAssocPCH.cxx new file mode 100644 index 00000000000..0575bdc8f19 --- /dev/null +++ b/PWGDQ/Tasks/tableReaderWithAssocPCH.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the precompiled header shared by the tableReader_withAssoc +// workflow variants; see TableReaderWithAssocPCH in this directory's +// CMakeLists.txt. It exists only to own the PCH. diff --git a/PWGDQ/Tasks/tableReader_withAssoc.cxx b/PWGDQ/Tasks/tableReader_withAssoc.h similarity index 57% rename from PWGDQ/Tasks/tableReader_withAssoc.cxx rename to PWGDQ/Tasks/tableReader_withAssoc.h index 34dd7fc7314..354e9fe815d 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc.h @@ -9,8 +9,14 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no -// Configurable workflow for running several DQ or other PWG analyses + +/// \file tableReader_withAssoc.h +/// \author iarsene@cern.ch, i.c.arsene@fys.uio.no +/// \brief Configurable workflow for running several DQ or other PWG analyses +/// \note Precompiled into TableReaderWithAssocPCH + +#ifndef PWGDQ_TASKS_TABLEREADER_WITHASSOC_H_ +#define PWGDQ_TASKS_TABLEREADER_WITHASSOC_H_ #include "PWGDQ/Core/AnalysisCompositeCut.h" #include "PWGDQ/Core/AnalysisCut.h" @@ -24,6 +30,8 @@ #include "PWGDQ/DataModel/ReducedInfoTables.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/TableHelper.h" @@ -49,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -65,7 +74,6 @@ #include #include #include -#include #include #include #include @@ -74,16 +82,6 @@ #include #include -using std::cout; -using std::endl; -using std::string; - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace o2::aod; -using namespace o2::common::core; - // Some definitions namespace o2::aod { @@ -94,10 +92,10 @@ DECLARE_SOA_BITMAP_COLUMN(IsEventSelected, isEventSelected, 8); DECLARE_SOA_BITMAP_COLUMN(IsBarrelSelected, isBarrelSelected, 32); //! Barrel track decisions DECLARE_SOA_COLUMN(BarrelAmbiguityInBunch, barrelAmbiguityInBunch, int8_t); //! Barrel track in-bunch ambiguity DECLARE_SOA_COLUMN(BarrelAmbiguityOutOfBunch, barrelAmbiguityOutOfBunch, int8_t); //! Barrel track out of bunch ambiguity -DECLARE_SOA_BITMAP_COLUMN(IsMuonSelected, isMuonSelected, 32); //! Muon track decisions (joinable to ReducedMuonsAssoc) +DECLARE_SOA_BITMAP_COLUMN(IsMuonSelected, isMuonSelected, 32); //! Muon track decisions (joinable to o2::aod::ReducedMuonsAssoc) DECLARE_SOA_COLUMN(MuonAmbiguityInBunch, muonAmbiguityInBunch, int8_t); //! Muon track in-bunch ambiguity DECLARE_SOA_COLUMN(MuonAmbiguityOutOfBunch, muonAmbiguityOutOfBunch, int8_t); //! Muon track out of bunch ambiguity -DECLARE_SOA_BITMAP_COLUMN(IsBarrelSelectedPrefilter, isBarrelSelectedPrefilter, 32); //! Barrel prefilter decisions (joinable to ReducedTracksAssoc) +DECLARE_SOA_BITMAP_COLUMN(IsBarrelSelectedPrefilter, isBarrelSelectedPrefilter, 32); //! Barrel prefilter decisions (joinable to o2::aod::ReducedTracksAssoc) // Bcandidate columns for ML analysis of B->Jpsi+K DECLARE_SOA_COLUMN(RunNumber, runNumber, uint64_t); DECLARE_SOA_COLUMN(EventIdx, eventIdx, uint64_t); @@ -157,18 +155,49 @@ DECLARE_SOA_COLUMN(TPCChi2leg1, tpcChi2leg1, float); DECLARE_SOA_COLUMN(TPCChi2leg2, tpcChi2leg2, float); DECLARE_SOA_BITMAP_COLUMN(IsJpsiFromBSelected, isJpsiFromBSelected, 32); // Candidate columns for prompt-non-prompt JPsi separation -DECLARE_SOA_COLUMN(Massee, massJPsi2ee, float); -DECLARE_SOA_COLUMN(Ptee, ptJPsi2ee, float); +DECLARE_SOA_COLUMN(Massee, massee, float); +DECLARE_SOA_COLUMN(Etaee, etaee, float); +DECLARE_SOA_COLUMN(Rapee, rapee, float); +DECLARE_SOA_COLUMN(Phiee, phiee, float); +DECLARE_SOA_COLUMN(Ptee, ptee, float); DECLARE_SOA_COLUMN(Lxyee, lxyJPsi2ee, float); DECLARE_SOA_COLUMN(LxyeePoleMass, lxyJPsi2eePoleMass, float); DECLARE_SOA_COLUMN(LRecalxyeePoleMass, lRecalxyeePoleMass, float); -DECLARE_SOA_COLUMN(Lzee, lzJPsi2ee, float); +DECLARE_SOA_COLUMN(Lzee, lzee, float); +DECLARE_SOA_COLUMN(LxyeePoleMassPVrecomputed, lxyeePoleMassPVrecomputed, float); +DECLARE_SOA_COLUMN(Vx, vx, float); +DECLARE_SOA_COLUMN(Vy, vy, float); +DECLARE_SOA_COLUMN(Vz, vz, float); +DECLARE_SOA_COLUMN(DcaXY1, dcaXY1, float); +DECLARE_SOA_COLUMN(DcaZ1, dcaZ1, float); +DECLARE_SOA_COLUMN(DcaXY2, dcaXY2, float); +DECLARE_SOA_COLUMN(DcaZ2, dcaZ2, float); DECLARE_SOA_COLUMN(AmbiguousInBunchPairs, AmbiguousJpsiPairsInBunch, bool); DECLARE_SOA_COLUMN(AmbiguousOutOfBunchPairs, AmbiguousJpsiPairsOutOfBunch, bool); DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); +DECLARE_SOA_COLUMN(PercentileFT0C, percentileFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityNContrib, multiplicityNContribJPsi2ee, float); + +// Additional columns for prompt-non-prompt JPsi separation (BDT training) +DECLARE_SOA_COLUMN(PairSign, pairSign, int8_t); //! t1.sign() + t2.sign() +DECLARE_SOA_COLUMN(PairFilter, pairFilter, uint32_t); //! twoTrackFilter (cut bits + ambiguity bits 28-31) +DECLARE_SOA_COLUMN(McDecision, mcDecision, uint32_t); //! MC signal bits, 0 in data +DECLARE_SOA_COLUMN(Corrassoc, corrassoc, bool); //! correct collision association (MC only, true in data) +DECLARE_SOA_COLUMN(TauxyeeErr, tauxyeeErr, float); //! kVertexingTauxyErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(TauzeeErr, tauzeeErr, float); //! kVertexingTauzErr: in ns (not cm) and with the reconstructed mass +DECLARE_SOA_COLUMN(LxyeeProjected, lxyeeProjected, float); //! kVertexingLxyProjected +DECLARE_SOA_COLUMN(LxyeeOverErr, lxyeeOverErr, float); //! kVertexingLxyOverErr +DECLARE_SOA_COLUMN(Lxyzee, lxyzee, float); //! kVertexingLxyz +DECLARE_SOA_COLUMN(LxyzeeOverErr, lxyzeeOverErr, float); //! kVertexingLxyzOverErr +DECLARE_SOA_COLUMN(Chi2PCAee, chi2PCAee, float); //! kVertexingChi2PCA +DECLARE_SOA_COLUMN(CosPAee, cosPAee, float); //! kCosPointingAngle +DECLARE_SOA_COLUMN(DcaXYOverSig1, dcaXYOverSig1, float); +DECLARE_SOA_COLUMN(DcaZOverSig1, dcaZOverSig1, float); +DECLARE_SOA_COLUMN(DcaXYOverSig2, dcaXYOverSig2, float); +DECLARE_SOA_COLUMN(DcaZOverSig2, dcaZOverSig2, float); + // Candidate columns for JPsi/muon correlations DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); @@ -176,11 +205,11 @@ DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); DECLARE_SOA_TABLE(EventCuts, "AOD", "DQANAEVCUTSA", dqanalysisflags::IsEventSelected); //! joinable to ReducedEvents DECLARE_SOA_TABLE(MixingHashes, "AOD", "DQANAMIXHASHA", dqanalysisflags::MixingHash); //! joinable to ReducedEvents -DECLARE_SOA_TABLE(BarrelTrackCuts, "AOD", "DQANATRKCUTSA", dqanalysisflags::IsBarrelSelected); //! joinable to ReducedTracksAssoc +DECLARE_SOA_TABLE(BarrelTrackCuts, "AOD", "DQANATRKCUTSA", dqanalysisflags::IsBarrelSelected); //! joinable to o2::aod::ReducedTracksAssoc DECLARE_SOA_TABLE(BarrelAmbiguities, "AOD", "DQBARRELAMBA", dqanalysisflags::BarrelAmbiguityInBunch, dqanalysisflags::BarrelAmbiguityOutOfBunch); //! joinable to ReducedBarrelTracks -DECLARE_SOA_TABLE(MuonTrackCuts, "AOD", "DQANAMUONCUTSA", dqanalysisflags::IsMuonSelected); //! joinable to ReducedMuonsAssoc +DECLARE_SOA_TABLE(MuonTrackCuts, "AOD", "DQANAMUONCUTSA", dqanalysisflags::IsMuonSelected); //! joinable to o2::aod::ReducedMuonsAssoc DECLARE_SOA_TABLE(MuonAmbiguities, "AOD", "DQMUONAMBA", dqanalysisflags::MuonAmbiguityInBunch, dqanalysisflags::MuonAmbiguityOutOfBunch); //! joinable to ReducedMuonTracks -DECLARE_SOA_TABLE(Prefilter, "AOD", "DQPREFILTERA", dqanalysisflags::IsBarrelSelectedPrefilter); //! joinable to ReducedTracksAssoc +DECLARE_SOA_TABLE(Prefilter, "AOD", "DQPREFILTERA", dqanalysisflags::IsBarrelSelectedPrefilter); //! joinable to o2::aod::ReducedTracksAssoc DECLARE_SOA_TABLE(BmesonCandidates, "AOD", "DQBMESONSA", dqanalysisflags::RunNumber, dqanalysisflags::EventIdx, dqanalysisflags::EventTimestamp, dqanalysisflags::massBcandidate, dqanalysisflags::MassDileptonCandidate, dqanalysisflags::deltamassBcandidate, dqanalysisflags::pTBcandidate, dqanalysisflags::EtaBcandidate, dqanalysisflags::PhiBcandidate, dqanalysisflags::RapBcandidate, @@ -201,45 +230,62 @@ DECLARE_SOA_TABLE(JPsiMuonCandidates, "AOD", "DQJPSIMUONA", dqanalysisflags::DeltaEta, dqanalysisflags::DeltaPhi, dqanalysisflags::MassDileptonCandidate, dqanalysisflags::Ptpair, dqanalysisflags::Etapair, dqanalysisflags::Ptassoc, dqanalysisflags::Etaassoc, dqanalysisflags::Phiassoc, dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::Phileg1, dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::Phileg2); -DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LRecalxyeePoleMass, dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); +DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", + dqanalysisflags::PairFilter, dqanalysisflags::McDecision, + dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, + dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, + dqanalysisflags::TauxyeeErr, dqanalysisflags::TauzeeErr, + dqanalysisflags::LxyeeProjected, dqanalysisflags::LxyeeOverErr, dqanalysisflags::Lxyzee, dqanalysisflags::LxyzeeOverErr, + dqanalysisflags::Chi2PCAee, dqanalysisflags::CosPAee, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, + dqanalysisflags::Ptleg1, dqanalysisflags::Etaleg1, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::DcaXYOverSig1, dqanalysisflags::DcaZOverSig1, + dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::ITSChi2leg1, + dqanalysisflags::Ptleg2, dqanalysisflags::Etaleg2, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::DcaXYOverSig2, dqanalysisflags::DcaZOverSig2, + dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::ITSChi2leg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::Corrassoc, + dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0C, dqanalysisflags::MultiplicityNContrib); } // namespace o2::aod - // Declarations of various short names -using MyEvents = soa::Join; -using MyEventsBasic = soa::Join; -using MyEventsMultExtra = soa::Join; -using MyEventsMultExtraQVector = soa::Join; -using MyEventsZdc = soa::Join; -using MyEventsMultExtraZdc = soa::Join; -using MyEventsMultExtraZdcFit = soa::Join; -using MyEventsSelected = soa::Join; -using MyEventsMultExtraSelected = soa::Join; -using MyEventsVtxCovSelectedMultExtra = soa::Join; -using MyEventsHashSelected = soa::Join; -using MyEventsVtxCov = soa::Join; -using MyEventsVtxCovSelected = soa::Join; -using MyEventsVtxCovSelectedQvector = soa::Join; -using MyEventsVtxCovSelectedQvectorWithHash = soa::Join; -using MyEventsVtxCovZdcFitSelected = soa::Join; -using MyEventsVtxCovZdcFitSelectedMultExtra = soa::Join; -using MyEventsQvector = soa::Join; -using MyEventsHashSelectedQvector = soa::Join; -using MyEventsQvectorCentr = soa::Join; -using MyEventsQvectorCentrSelected = soa::Join; -using MyEventsHashSelectedQvectorCentr = soa::Join; - -using MyBarrelTracks = soa::Join; -using MyBarrelTracksWithAmbiguities = soa::Join; -using MyBarrelTracksWithCov = soa::Join; -using MyBarrelTracksWithCovWithAmbiguities = soa::Join; -using MyBarrelTracksWithCovWithAmbiguitiesWithColl = soa::Join; -using MyDielectronCandidates = soa::Join; -using MyDitrackCandidates = soa::Join; -using MyDimuonCandidates = soa::Join; -using MyMuonTracks = soa::Join; -using MyMuonTracksWithCov = soa::Join; -using MyMuonTracksWithCovWithAmbiguities = soa::Join; -using MyMuonTracksSelectedWithColl = soa::Join; +using MyEvents = o2::soa::Join; +using MyEventsBasic = o2::soa::Join; +using MyEventsMultExtraNoQvector = o2::soa::Join; +using MyEventsMultExtra = o2::soa::Join; +using MyEventsMultExtraQVector = o2::soa::Join; +using MyEventsZdc = o2::soa::Join; +using MyEventsMultExtraZdc = o2::soa::Join; +using MyEventsMultExtraZdcFit = o2::soa::Join; +using MyEventsSelected = o2::soa::Join; +using MyEventsMultExtraSelected = o2::soa::Join; +using MyEventsVtxCovSelectedMultExtra = o2::soa::Join; +using MyEventsHashSelected = o2::soa::Join; +using MyEventsVtxCov = o2::soa::Join; +using MyEventsVtxCovSelected = o2::soa::Join; +using MyEventsVtxCovSelectedInfo = o2::soa::Join; +using MyEventsVtxCovSelectedQvector = o2::soa::Join; +using MyEventsVtxCovSelectedQvectorWithHash = o2::soa::Join; +using MyEventsVtxCovZdcFitSelected = o2::soa::Join; +using MyEventsVtxCovZdcFitSelectedMultExtra = o2::soa::Join; +using MyEventsQvector = o2::soa::Join; +using MyEventsHashSelectedQvector = o2::soa::Join; +using MyEventsQvectorCentr = o2::soa::Join; +using MyEventsQvectorCentrMerge = o2::soa::Join; +using MyEventsQvectorCentrSelected = o2::soa::Join; +using MyEventsHashSelectedQvectorCentr = o2::soa::Join; + +using MyBarrelTracks = o2::soa::Join; +using MyBarrelTracksWithAmbiguities = o2::soa::Join; +using MyBarrelTracksWithCov = o2::soa::Join; +using MyBarrelTracksWithCovWithAmbiguities = o2::soa::Join; +using MyBarrelTracksWithCovWithAmbiguitiesWithColl = o2::soa::Join; +using MyBarrelTracksWithEMCal = o2::soa::Join; +using MyBarrelTracksWithCovWithEMCal = o2::soa::Join; +using MyDielectronCandidates = o2::soa::Join; +using MyDitrackCandidates = o2::soa::Join; +using MyDimuonCandidates = o2::soa::Join; +using MyMuonTracks = o2::soa::Join; +using MyMuonTracksWithCov = o2::soa::Join; +using MyMuonTracksWithCovWithAmbiguities = o2::soa::Join; +using MyMuonTracksSelectedWithColl = o2::soa::Join; // bit maps used for the Fill functions of the VarManager constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended; @@ -250,6 +296,7 @@ constexpr static uint32_t gkEventFillMapWithCovZdcFit = VarManager::ObjTypes::Re constexpr static uint32_t gkEventFillMapWithMultExtra = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended | VarManager::ObjTypes::ReducedEventMultExtra; // New fillmap constexpr static uint32_t gkEventFillMapWithMultExtraWithQVector = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended | VarManager::ObjTypes::ReducedEventMultExtra | VarManager::ObjTypes::CollisionQvect; +// constexpr static uint32_t gkEventFillMapWithMultExtraWithQVector = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended | VarManager::ObjTypes::ReducedEventMultExtra | VarManager::ObjTypes::ReducedEventQvector; constexpr static uint32_t gkEventFillMapWithMultExtraZdc = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended | VarManager::ObjTypes::ReducedEventMultExtra | VarManager::ReducedZdc; constexpr static uint32_t gkEventFillMapWithMultExtraZdcFit = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended | VarManager::ObjTypes::ReducedEventMultExtra | VarManager::ReducedZdc | VarManager::ReducedFit; constexpr static uint32_t gkEventFillMapWithCovZdcFitMultExtra = VarManager::ObjTypes::ReducedEvent | VarManager::ObjTypes::ReducedEventExtended | VarManager::ObjTypes::ReducedEventVtxCov | VarManager::ReducedZdc | VarManager::ReducedFit | VarManager::ReducedEventMultExtra; @@ -259,6 +306,8 @@ constexpr static uint32_t gkEventFillMapWithQvectorCentr = VarManager::ObjTypes: constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelPID; constexpr static uint32_t gkTrackFillMapWithCov = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID; constexpr static uint32_t gkTrackFillMapWithCovWithColl = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID | VarManager::ObjTypes::ReducedTrackCollInfo; +constexpr static uint32_t gkTrackFillMapWithEMCal = gkTrackFillMap | VarManager::ObjTypes::TrackEMCal; +constexpr static uint32_t gkTrackFillMapWithCovWithEMCal = gkTrackFillMapWithCov | VarManager::ObjTypes::TrackEMCal; // constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra; constexpr static uint32_t gkMuonFillMapWithCov = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra | VarManager::ObjTypes::ReducedMuonCov; @@ -268,59 +317,82 @@ constexpr static int pairTypeEE = VarManager::kDecayToEE; constexpr static int pairTypeMuMu = VarManager::kDecayToMuMu; // constexpr static int pairTypeEMu = VarManager::kElectronMuon; +namespace dqtablereader_helpers +{ +inline float* varValues() { return static_cast(VarManager::fgValues); } +inline TString* varNames() { return static_cast(VarManager::fgVariableNames); } +inline TString* varUnits() { return static_cast(VarManager::fgVariableUnits); } +} // namespace dqtablereader_helpers + // Global function used to define needed histogram classes -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups); // defines histograms for all tasks template void PrintBitMap(TMap map, int nbits) { + std::string bits; + bits.reserve(nbits); for (int i = 0; i < nbits; i++) { - cout << ((map & (TMap(1) << i)) > 0 ? "1" : "0"); + bits += ((map & (TMap(1) << i)) > 0 ? '1' : '0'); } + LOG(info) << bits; } // Analysis task that produces event decisions (analysis cut, in bunch pileup and split collision check) and the Hash table used in event mixing struct AnalysisEventSelection { - Produces eventSel; - Produces hash; - OutputObj fOutputList{"output"}; + o2::framework::Produces eventSel; + o2::framework::Produces hash; + o2::framework::Produces JetEvents; + o2::framework::OutputObj fOutputList{"output"}; // TODO: Provide the mixing variables and binning directly via configurables (e.g. vectors of float) - Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; - Configurable fConfigMixingVariablesJson{"cfgMixingVarsJSON", "", "Mixing configs in JSON format"}; - Configurable fConfigEventCuts{"cfgEventCuts", "eventStandard", "Event selection"}; - Configurable fConfigEventCutsJSON{"cfgEventCutsJSON", "", "Additional event cuts specified in JSON format"}; - Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Add event histograms defined via JSON formatting (see HistogramsLibrary)"}; - Configurable fConfigQA{"cfgQA", true, "If true, QA histograms will be created and filled"}; - - Configurable fConfigITSROFrameStartBorderMargin{"cfgITSROFrameStartBorderMargin", -1, "Number of bcs at the start of ITS RO Frame border. Take from CCDB if -1"}; - Configurable fConfigITSROFrameEndBorderMargin{"cfgITSROFrameEndBorderMargin", -1, "Number of bcs at the end of ITS RO Frame border. Take from CCDB if -1"}; - - Configurable fConfigSplitCollisionsDeltaZ{"cfgSplitCollisionsDeltaZ", 1.0, "maximum delta-z (cm) between two collisions to consider them as split candidates"}; - Configurable fConfigSplitCollisionsDeltaBC{"cfgSplitCollisionsDeltaBC", 100, "maximum delta-BC between two collisions to consider them as split candidates; do not apply if value is negative"}; - Configurable fConfigCheckSplitCollisions{"cfgCheckSplitCollisions", false, "If true, run the split collision check and fill histograms"}; - - Configurable fConfigRunZorro{"cfgRunZorro", false, "Enable event selection with zorro [WARNING: under debug, do not enable!]"}; - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + o2::framework::Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; + o2::framework::Configurable fConfigMixingVariablesJson{"cfgMixingVarsJSON", "", "Mixing configs in JSON format"}; + o2::framework::Configurable fConfigEventCuts{"cfgEventCuts", "eventStandard", "Event selection"}; + o2::framework::Configurable fConfigEventCutsJSON{"cfgEventCutsJSON", "", "Additional event cuts specified in JSON format"}; + o2::framework::Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms"}; + o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Add event histograms defined via JSON formatting (see HistogramsLibrary)"}; + o2::framework::Configurable fConfigQA{"cfgQA", true, "If true, QA histograms will be created and filled"}; + + o2::framework::Configurable fConfigITSROFrameStartBorderMargin{"cfgITSROFrameStartBorderMargin", -1, "Number of bcs at the start of ITS RO Frame border. Take from CCDB if -1"}; + o2::framework::Configurable fConfigITSROFrameEndBorderMargin{"cfgITSROFrameEndBorderMargin", -1, "Number of bcs at the end of ITS RO Frame border. Take from CCDB if -1"}; + + o2::framework::Configurable fConfigSplitCollisionsDeltaZ{"cfgSplitCollisionsDeltaZ", 1.0, "maximum delta-z (cm) between two collisions to consider them as split candidates"}; + o2::framework::Configurable fConfigSplitCollisionsDeltaBC{"cfgSplitCollisionsDeltaBC", 100, "maximum delta-BC between two collisions to consider them as split candidates; do not apply if value is negative"}; + o2::framework::Configurable fConfigCheckSplitCollisions{"cfgCheckSplitCollisions", false, "If true, run the split collision check and fill histograms"}; + + o2::framework::Configurable fConfigRunZorro{"cfgRunZorro", false, "Enable event selection with zorro [WARNING: under debug, do not enable!]"}; + o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + o2::framework::Configurable fConfigFetchInteractionRate{"cfgFetchInteractionRate", false, "Fetch event-wise interaction rate from the CCDB"}; + o2::framework::Configurable fConfigIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + + o2::framework::Configurable cfgUseRCT{"cfgUseRCT", false, "Apply RCT quality selection"}; + o2::framework::Configurable cfgRCTPath{"cfgRCTPath", "RCT/Flags/RunFlags", "CCDB path to the RCT run flags"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "RCT selection label"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "Also check ZDC quality"}; + o2::framework::Configurable cfgRCTPassName{"cfgRCTPassName", "", "Reconstruction pass; leave empty for run-only lookup"}; HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; - AnalysisCompositeCut* fEventCut; + AnalysisCompositeCut* fEventCut = nullptr; - Service fCCDB; + o2::framework::Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; + o2::ctpRateFetcher rateFetcher; std::map fSelMap; // key: reduced event global index, value: event selection decision std::map> fBCCollMap; // key: global BC, value: vector of reduced event global indices - int fCurrentRun; + int fCurrentRun = -1; + + o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + std::map rctFlags; + int rctRun = -1; void init(o2::framework::InitContext& context) { - - bool isAnyProcessEnabled = context.mOptions.get("processSkimmed") || context.mOptions.get("processSkimmedBasic") || context.mOptions.get("processSkimmedWithZdc") || context.mOptions.get("processSkimmedWithMultExtra") || context.mOptions.get("processSkimmedWithMultExtraZdc") || context.mOptions.get("processSkimmedWithMultExtraZdcFit") || context.mOptions.get("processSkimmedWithQvectorCentr"); + bool isAnyProcessEnabled = context.mOptions.get("processSkimmed") || context.mOptions.get("processSkimmedBasic") || context.mOptions.get("processSkimmedWithZdc") || context.mOptions.get("processSkimmedWithMultExtra") || context.mOptions.get("processSkimmedWithMultExtraNoQvector") || context.mOptions.get("processSkimmedWithMultExtraZdc") || context.mOptions.get("processSkimmedWithMultExtraZdcFit") || context.mOptions.get("processSkimmedWithQvectorCentr") || context.mOptions.get("processSkimmedWithQvectorCentrMerge"); bool isDummyEnabled = context.mOptions.get("processDummy"); if (isDummyEnabled) { @@ -338,7 +410,7 @@ struct AnalysisEventSelection { fEventCut = new AnalysisCompositeCut(true); TString eventCutStr = fConfigEventCuts.value; if (eventCutStr != "") { - AnalysisCut* cut = dqcuts::GetAnalysisCut(eventCutStr.Data()); + AnalysisCut* cut = o2::aod::dqcuts::GetAnalysisCut(eventCutStr.Data()); if (cut != nullptr) { fEventCut->AddCut(cut); } @@ -346,8 +418,8 @@ struct AnalysisEventSelection { // Additional cuts via JSON TString eventCutJSONStr = fConfigEventCutsJSON.value; if (eventCutJSONStr != "") { - std::vector jsonCuts = dqcuts::GetCutsFromJSON(eventCutJSONStr.Data()); - for (auto& cutIt : jsonCuts) { + std::vector jsonCuts = o2::aod::dqcuts::GetCutsFromJSON(eventCutJSONStr.Data()); + for (auto const& cutIt : jsonCuts) { fEventCut->AddCut(cutIt); } } @@ -357,13 +429,13 @@ struct AnalysisEventSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); DefineHistograms(fHistMan, "Event_BeforeCuts;Event_AfterCuts;", fConfigAddEventHistogram.value.data()); if (fConfigCheckSplitCollisions) { DefineHistograms(fHistMan, "SameBunchCorrelations;OutOfBunchCorrelations;", ""); } // Additional histograms via JSON - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); VarManager::SetUseVars(fHistMan->GetUsedVars()); fOutputList.setObject(fHistMan->GetMainHistogramList()); } @@ -376,11 +448,11 @@ struct AnalysisEventSelection { fMixHandler->Init(); if (objArray->GetEntries() > 0) { for (int iVar = 0; iVar < objArray->GetEntries(); ++iVar) { - dqmixing::SetUpMixing(fMixHandler, objArray->At(iVar)->GetName()); + o2::aod::dqmixing::SetUpMixing(fMixHandler, objArray->At(iVar)->GetName()); } } if (mixVarsJsonString != "") { - dqmixing::SetUpMixingFromJSON(fMixHandler, mixVarsJsonString.Data()); + o2::aod::dqmixing::SetUpMixingFromJSON(fMixHandler, mixVarsJsonString.Data()); } } @@ -390,6 +462,61 @@ struct AnalysisEventSelection { fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); fCCDBApi.init(fConfigCcdbUrl.value); + + if (cfgUseRCT.value) { + if (cfgRCTLabel.value != "CBT" && cfgRCTLabel.value != "CBT_hadronPID" && cfgRCTLabel.value != "CBT_electronPID" && cfgRCTLabel.value != "CBT_calo" && cfgRCTLabel.value != "CBT_muon" && cfgRCTLabel.value != "CBT_muon_glo") { + LOGF(fatal, "RCT Label \"%s\" is unknown.", cfgRCTLabel.value.c_str()); + } + + rctChecker.init(cfgRCTLabel.value, cfgCheckZDC.value); + + if (!rctChecker.any()) { + LOGF(fatal, "Empty RCT mask: check cfgRCTLabel."); + } + } + } + + void loadRCT(int run, uint64_t timestamp) + { + std::map metadata{{"run", std::to_string(run)}}; + + LOG(info) << "Requesting " << cfgRCTPath.value << " run=" << run << " timestamp=" << timestamp; + + if (!cfgRCTPassName.value.empty()) { + metadata["passName"] = cfgRCTPassName.value; + } + + auto* flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + if (flags == nullptr && !cfgRCTPassName.value.empty()) { + metadata.erase("passName"); + flags = fCCDB->getSpecific>(cfgRCTPath.value, timestamp, metadata); + } + + if (flags == nullptr || flags->empty()) { + LOGF(fatal, "Missing or empty RCT flags for run %d.", run); + } + + rctFlags = *flags; + rctRun = run; + } + + bool passesRCT(int run, uint64_t timestamp) + { + if (!cfgUseRCT.value) { + return true; + } + + if (run != rctRun) { + loadRCT(run, timestamp); + } + + auto iterator = rctFlags.upper_bound(timestamp); + if (iterator == rctFlags.begin()) { + return false; + } + + const uint32_t flags = std::prev(iterator)->second; + return (static_cast(flags) & rctChecker.value()) == 0; } template @@ -403,7 +530,7 @@ struct AnalysisEventSelection { VarManager::SetSORandEOR(sor, eor); auto alppar = fCCDB->getForTimeStamp>("ITS/Config/AlpideParam", events.begin().timestamp()); - EventSelectionParams* par = fCCDB->getForTimeStamp("EventSelection/EventSelectionParams", events.begin().timestamp()); + auto par = fCCDB->getForTimeStamp("EventSelection/EventSelectionParams", events.begin().timestamp()); int itsROFrameStartBorderMargin = fConfigITSROFrameStartBorderMargin < 0 ? par->fITSROFrameStartBorderMargin : fConfigITSROFrameStartBorderMargin; int itsROFrameEndBorderMargin = fConfigITSROFrameEndBorderMargin < 0 ? par->fITSROFrameEndBorderMargin : fConfigITSROFrameEndBorderMargin; VarManager::SetITSROFBorderselection(alppar->roFrameBiasInBC, alppar->roFrameLengthInBC, itsROFrameStartBorderMargin, itsROFrameEndBorderMargin); @@ -413,29 +540,33 @@ struct AnalysisEventSelection { fSelMap.clear(); fBCCollMap.clear(); - for (auto& event : events) { + for (auto const& event : events) { // Reset the fValues array and fill event observables VarManager::ResetValues(0, VarManager::kNEventWiseVariables); VarManager::FillEvent(event); + // Get the instantaneous IR from the CCDB + if (fConfigFetchInteractionRate.value) { + VarManager::fgValues[VarManager::kInteractionRate] = rateFetcher.fetch(fCCDB.service, event.timestamp(), fCurrentRun, fConfigIRSource.value, true) / 1000.; // kHz + } bool decision = false; // Fill histograms in the class Event, before cuts if (fConfigQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_BeforeCuts", dqtablereader_helpers::varValues()); } // Apply event cuts and fill histograms after selection - if (fEventCut->IsSelected(VarManager::fgValues)) { + if (fEventCut->IsSelected(dqtablereader_helpers::varValues()) && passesRCT(event.runNumber(), event.timestamp())) { if (fConfigRunZorro) { if (event.tag_bit(56)) { // This is the bit used for the software trigger event selections [TO BE DONE: find a more clear way to use it] if (fConfigQA) { - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", dqtablereader_helpers::varValues()); } decision = true; } } else { if (fConfigQA) { - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", dqtablereader_helpers::varValues()); } decision = true; } @@ -455,7 +586,11 @@ struct AnalysisEventSelection { // create the mixing hash and publish it into the hash table if (fMixHandler != nullptr) { - int hh = fMixHandler->FindEventCategory(VarManager::fgValues); + int hh = fMixHandler->FindEventCategory(dqtablereader_helpers::varValues()); + // events outside the mixing limits (-1) get a distinct negative hash so that they are not mixed with each other + if (hh < 0) { + hh = -1 - static_cast(event.globalIndex()); + } hash(hh); } } @@ -474,7 +609,7 @@ struct AnalysisEventSelection { // loop over the BC map, get the collision vectors and make in-bunch and out of bunch 2-event correlations for (auto bc1It = fBCCollMap.begin(); bc1It != fBCCollMap.end(); ++bc1It) { uint64_t bc1 = bc1It->first; - auto& bc1Events = bc1It->second; + auto const& bc1Events = bc1It->second; // same bunch event correlations, if more than 1 collisions in this bunch if (bc1Events.size() > 1) { @@ -489,7 +624,7 @@ struct AnalysisEventSelection { collisionSplittingMap[*ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("SameBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("SameBunchCorrelations", dqtablereader_helpers::varValues()); } } // end second event loop } // end first event loop @@ -501,13 +636,13 @@ struct AnalysisEventSelection { if ((bc2 > bc1 ? bc2 - bc1 : bc1 - bc2) > fConfigSplitCollisionsDeltaBC) { break; } - auto& bc2Events = bc2It->second; + auto const& bc2Events = bc2It->second; // loop over events in the first BC - for (auto ev1It : bc1Events) { + for (auto const& ev1It : bc1Events) { auto ev1 = events.rawIteratorAt(ev1It); // loop over events in the second BC - for (auto ev2It : bc2Events) { + for (auto const& ev2It : bc2Events) { auto ev2 = events.rawIteratorAt(ev2It); // compute 2-event quantities and mark the candidate split collisions VarManager::FillTwoEvents(ev1, ev2); @@ -516,7 +651,7 @@ struct AnalysisEventSelection { collisionSplittingMap[ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("OutOfBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("OutOfBunchCorrelations", dqtablereader_helpers::varValues()); } } } @@ -525,8 +660,8 @@ struct AnalysisEventSelection { } // publish the table - uint8_t evSel = static_cast(0); - for (auto& event : events) { + auto evSel = static_cast(0); + for (auto const& event : events) { evSel = 0; if (fSelMap[event.globalIndex()]) { // event passed the user cuts evSel |= (static_cast(1) << 0); @@ -562,6 +697,11 @@ struct AnalysisEventSelection { runEventSelection(events); publishSelections(events); } + void processSkimmedWithMultExtraNoQvector(MyEventsMultExtraNoQvector const& events) + { + runEventSelection(events); + publishSelections(events); + } void processSkimmedWithMultExtraZdc(MyEventsMultExtraZdc const& events) { runEventSelection(events); @@ -577,7 +717,25 @@ struct AnalysisEventSelection { runEventSelection(events); publishSelections(events); } - void processDummy(MyEventsBasic&) + void processSkimmedWithQvectorCentrMerge(MyEventsQvectorCentrMerge const& events) + { + runEventSelection(events); + publishSelections(events); + } + void processFillEvents(MyEventsBasic const& events) // Used to forward the event table from tablemaker, typical use for now is jet analysis. + { + for (auto const& event : events) { + JetEvents(event.tag_raw(), + event.runNumber(), + event.posX(), + event.posY(), + event.posZ(), + event.numContrib(), + event.collisionTime(), + event.collisionTimeRes()); + } + } + void processDummy(MyEventsBasic const&) { // do nothing } @@ -586,44 +744,58 @@ struct AnalysisEventSelection { PROCESS_SWITCH(AnalysisEventSelection, processSkimmedBasic, "Run event selection on DQ skimmed events with basic tables", false); PROCESS_SWITCH(AnalysisEventSelection, processSkimmedWithZdc, "Run event selection on DQ skimmed events, with ZDC", false); PROCESS_SWITCH(AnalysisEventSelection, processSkimmedWithMultExtra, "Run event selection on DQ skimmed events, with mult extra", false); + PROCESS_SWITCH(AnalysisEventSelection, processSkimmedWithMultExtraNoQvector, "Run event selection on DQ skimmed events, with mult extra and without Q-vector", false); PROCESS_SWITCH(AnalysisEventSelection, processSkimmedWithMultExtraZdc, "Run event selection on DQ skimmed events, with mult extra and ZDC", false); PROCESS_SWITCH(AnalysisEventSelection, processSkimmedWithMultExtraZdcFit, "Run event selection on DQ skimmed events, with mult extra, ZDC and FIT", false); PROCESS_SWITCH(AnalysisEventSelection, processSkimmedWithQvectorCentr, "Run event selection on DQ skimmed events, with Q-vector", false); + PROCESS_SWITCH(AnalysisEventSelection, processSkimmedWithQvectorCentrMerge, "Run event selection on DQ skimmed events, with Q-vector and ReducedEventsMergingTable", false); + PROCESS_SWITCH(AnalysisEventSelection, processFillEvents, "Fill storedReducedEvents table for use in JE framework", false); PROCESS_SWITCH(AnalysisEventSelection, processDummy, "Dummy function", true); }; -// Produces a table with barrel track decisions (joinable to the ReducedTracksAssociations) +// o2::framework::Produces a table with barrel track decisions (joinable to the ReducedTracksAssociations) // Here one should add all the track cuts needed through the workflow (e.g. cuts for same-even pairing, electron prefiltering, track for dilepton-track correlations) struct AnalysisTrackSelection { - Produces trackSel; - Produces trackAmbiguities; - OutputObj fOutputList{"output"}; - - Configurable fConfigCuts{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; - Configurable fConfigCutsJSON{"cfgBarrelTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; - Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; - - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - - Configurable fConfigComputeTPCpostCalib{"cfgTPCpostCalib", false, "If true, compute TPC post-calibrated n-sigmas"}; - Configurable fConfigTPCpostCalibType{"cfgTPCpostCalibType", 1, "1: (TPCncls,pIN,eta) calibration typically for pp, 2: (eta,nPV,nLong,tLong) calibration typically for PbPb"}; - Configurable fConfigTPCuseInterpolatedCalib{"cfgTPCpostCalibUseInterpolation", true, "If true, use interpolated calibration values (default: true)"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + o2::framework::Produces trackSel; + o2::framework::Produces trackAmbiguities; + o2::framework::OutputObj fOutputList{"output"}; + + o2::framework::Configurable fConfigCuts{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; + o2::framework::Configurable fConfigCutsJSON{"cfgBarrelTrackCutsJSON", "", "Additional list of barrel track cuts in JSON format"}; + o2::framework::Configurable fConfigAddTrackHistogram{"cfgAddTrackHistogram", "", "Comma separated list of histograms"}; + o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + o2::framework::Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; + o2::framework::Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; + // Re-application of the track-to-collision time compatibility on the skimmed associations. + // timeMargin and nSigma must be <= the values used by track-to-collision-associator at skimming time; + // bcWindow, usePVAssociation and maxPvContributors must be identical to those values. + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable cfgAssocTimeCut{"cfgAssocTimeCut", false, "If true, reject associations failing the time compatibility computed with the parameters below"}; + o2::framework::Configurable cfgAssocTimeMargin{"cfgAssocTimeMargin", 0.f, "time margin (ns); must be <= skimming timeMargin"}; + o2::framework::Configurable cfgAssocNSigma{"cfgAssocNSigma", 4.f, "nSigmaForTimeCompat; must be <= skimming value"}; + o2::framework::Configurable cfgAssocBcWindowForOneSigma{"cfgAssocBcWindowForOneSigma", 60, "bcWindowForOneSigma; must be equal to the skimming value"}; + o2::framework::Configurable cfgAssocUsePVAssociation{"cfgAssocUsePVAssociation", 1, "usePVAssociation; must be equal to the skimming value"}; + o2::framework::Configurable cfgAssocMaxPvContributorsForLowMultReassoc{"cfgAssocMaxPvContributorsForLowMultReassoc", 10, "maxPvContributorsForLowMultReassoc; must be equal to the skimming value"}; + } fConfigAssocTime; + + o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable fConfigCcdbPathTPC{"ccdb-path-tpc", "Users/z/zhxiong/TPCPID/PostCalib", "base path to the ccdb object"}; + o2::framework::Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + + o2::framework::Configurable fConfigComputeTPCpostCalib{"cfgTPCpostCalib", false, "If true, compute TPC post-calibrated n-sigmas"}; + o2::framework::Configurable fConfigTPCpostCalibType{"cfgTPCpostCalibType", 1, "1: (TPCncls,pIN,eta) calibration typically for pp, 2: (eta,nPV,nLong,tLong) calibration typically for PbPb"}; + o2::framework::Configurable fConfigTPCuseInterpolatedCalib{"cfgTPCpostCalibUseInterpolation", true, "If true, use interpolated calibration values (default: true)"}; + o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; // Track related options - Configurable fPropTrack{"cfgPropTrack", true, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; + o2::framework::Configurable fPropTrack{"cfgPropTrack", true, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; - Service fCCDB; + o2::framework::Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; - int fCurrentRun; // current run kept to detect run changes and trigger loading params from CCDB + int fCurrentRun = 0; // current run kept to detect run changes and trigger loading params from CCDB std::map> fNAssocsInBunch; // key: track global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: track global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) @@ -640,15 +812,15 @@ struct AnalysisTrackSelection { if (!cutNamesStr.IsNull()) { std::unique_ptr objArray(cutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - fTrackCuts.push_back(dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); + fTrackCuts.push_back(o2::aod::dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } // Extra cuts via JSON TString addTrackCutsStr = fConfigCutsJSON.value; if (addTrackCutsStr != "") { - std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { + fTrackCuts.push_back(dynamic_cast(t)); } } @@ -657,20 +829,20 @@ struct AnalysisTrackSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); // set one histogram directory for each defined track cut TString histDirNames = "TrackBarrel_BeforeCuts;"; - for (auto& cut : fTrackCuts) { + for (auto const& cut : fTrackCuts) { histDirNames += Form("TrackBarrel_%s;", cut->GetName()); } if (fConfigPublishAmbiguity) { histDirNames += "TrackBarrel_AmbiguityInBunch;TrackBarrel_AmbiguityOutOfBunch;"; } - DefineHistograms(fHistMan, histDirNames.Data(), fConfigAddTrackHistogram.value.data()); // define all histograms - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + DefineHistograms(fHistMan, histDirNames.Data(), fConfigAddTrackHistogram.value.data()); // define all histograms + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); } @@ -682,7 +854,7 @@ struct AnalysisTrackSelection { } template - void runTrackSelection(ReducedTracksAssoc const& assocs, TEvents const& events, TTracks const& tracks) + void runTrackSelection(o2::aod::ReducedTracksAssoc const& assocs, TEvents const& events, TTracks const& tracks) { fNAssocsInBunch.clear(); fNAssocsOutOfBunch.clear(); @@ -704,7 +876,7 @@ struct AnalysisTrackSelection { VarManager::SetCalibrationType(fConfigTPCpostCalibType, fConfigTPCuseInterpolatedCalib); } - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); + auto grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); if (grpmag != nullptr) { VarManager::SetMagneticField(grpmag->getNominalL3Field()); } else { @@ -722,10 +894,10 @@ struct AnalysisTrackSelection { trackSel.reserve(assocs.size()); trackAmbiguities.reserve(tracks.size()); - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); int iCut = 0; - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { // if the event from this association is not selected, reject also the association auto event = assoc.template reducedevent_as(); @@ -738,21 +910,43 @@ struct AnalysisTrackSelection { VarManager::FillEvent(event); auto track = assoc.template reducedtrack_as(); + + // Reject incompatible associations while preserving the row alignment of trackSel. + // The track time is referenced to its original collision, not the associated one. + if (fConfigAssocTime.cfgAssocTimeCut) { + auto origEvent = track.template reducedevent_as(); + if (!VarManager::isBarrelAssocTimeCompatible(track, event, origEvent, + fConfigAssocTime.cfgAssocNSigma, fConfigAssocTime.cfgAssocTimeMargin, + fConfigAssocTime.cfgAssocBcWindowForOneSigma, fConfigAssocTime.cfgAssocUsePVAssociation, + fConfigAssocTime.cfgAssocMaxPvContributorsForLowMultReassoc)) { + trackSel(0); + continue; + } + } + filterMap = static_cast(0); VarManager::FillTrack(track); // compute quantities which depend on the associated collision, such as DCA if (fPropTrack) { VarManager::FillTrackCollision(track, event); } + // fill the quantities of the matched EMCal cluster (e.g. E/p); + // tracks without a matched cluster keep the sentinel values set by ResetValues + if constexpr (static_cast(TTrackFillMap & VarManager::ObjTypes::TrackEMCal)) { + if (track.has_matchedEMCalCluster()) { + auto cluster = track.template matchedEMCalCluster_as(); + VarManager::FillTrackEMCal(cluster, track.p(), track.emcalMatchDeltaEta(), track.emcalMatchDeltaPhi()); + } + } if (fConfigQA) { - fHistMan->FillHistClass("TrackBarrel_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_BeforeCuts", dqtablereader_helpers::varValues()); } iCut = 0; for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqtablereader_helpers::varValues())) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TrackBarrel_%s", (*cut)->GetName()), dqtablereader_helpers::varValues()); } } } // end loop over cuts @@ -786,7 +980,7 @@ struct AnalysisTrackSelection { if (fConfigPublishAmbiguity) { // QA the collision-track associations if (fConfigQA) { - for (auto& [trackIdx, evIndices] : fNAssocsInBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsInBunch) { if (evIndices.size() == 1) { continue; } @@ -795,10 +989,10 @@ struct AnalysisTrackSelection { VarManager::FillTrack(track); // Exceptionally, set the VarManager ambiguity number here, to be used in histograms VarManager::fgValues[VarManager::kBarrelNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", dqtablereader_helpers::varValues()); } // end loop over in-bunch ambiguous tracks - for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsOutOfBunch) { if (evIndices.size() == 1) { continue; } @@ -807,12 +1001,12 @@ struct AnalysisTrackSelection { VarManager::FillTrack(track); // Exceptionally, set the VarManager ambiguity number here VarManager::fgValues[VarManager::kBarrelNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", dqtablereader_helpers::varValues()); } // end loop over out-of-bunch ambiguous tracks } // publish the ambiguity table - for (auto& track : tracks) { + for (auto const& track : tracks) { int8_t nInBunch = 0; if (fNAssocsInBunch.find(track.globalIndex()) != fNAssocsInBunch.end()) { nInBunch = fNAssocsInBunch[track.globalIndex()].size(); @@ -827,19 +1021,27 @@ struct AnalysisTrackSelection { } // end runTrackSelection() - void processSkimmed(ReducedTracksAssoc const& assocs, MyEventsSelected const& events, MyBarrelTracks const& tracks) + void processSkimmed(o2::aod::ReducedTracksAssoc const& assocs, MyEventsSelected const& events, MyBarrelTracks const& tracks) { runTrackSelection(assocs, events, tracks); } - void processSkimmedWithMultExtra(ReducedTracksAssoc const& assocs, MyEventsMultExtraSelected const& events, MyBarrelTracks const& tracks) + void processSkimmedWithMultExtra(o2::aod::ReducedTracksAssoc const& assocs, MyEventsMultExtraSelected const& events, MyBarrelTracks const& tracks) { runTrackSelection(assocs, events, tracks); } - void processSkimmedWithCov(ReducedTracksAssoc const& assocs, MyEventsVtxCovSelected const& events, MyBarrelTracksWithCov const& tracks) + void processSkimmedWithCov(o2::aod::ReducedTracksAssoc const& assocs, MyEventsVtxCovSelected const& events, MyBarrelTracksWithCov const& tracks) { runTrackSelection(assocs, events, tracks); } - void processDummy(MyEventsBasic&) + void processSkimmedWithEMCal(o2::aod::ReducedTracksAssoc const& assocs, MyEventsSelected const& events, MyBarrelTracksWithEMCal const& tracks, o2::aod::ReducedEMCals const& /*emcals*/) + { + runTrackSelection(assocs, events, tracks); + } + void processSkimmedWithCovWithEMCal(o2::aod::ReducedTracksAssoc const& assocs, MyEventsVtxCovSelected const& events, MyBarrelTracksWithCovWithEMCal const& tracks, o2::aod::ReducedEMCals const& /*emcals*/) + { + runTrackSelection(assocs, events, tracks); + } + void processDummy(MyEventsBasic const&) { // do nothing } @@ -847,34 +1049,34 @@ struct AnalysisTrackSelection { PROCESS_SWITCH(AnalysisTrackSelection, processSkimmed, "Run barrel track selection on DQ skimmed track associations", false); PROCESS_SWITCH(AnalysisTrackSelection, processSkimmedWithMultExtra, "Run barrel track selection on DQ skimmed track associations, with extra multiplicity tables", false); PROCESS_SWITCH(AnalysisTrackSelection, processSkimmedWithCov, "Run barrel track selection on DQ skimmed tracks w/ cov matrix associations", false); + PROCESS_SWITCH(AnalysisTrackSelection, processSkimmedWithEMCal, "Run barrel track selection on DQ skimmed track associations, with EMCal matching info", false); + PROCESS_SWITCH(AnalysisTrackSelection, processSkimmedWithCovWithEMCal, "Run barrel track selection on DQ skimmed tracks w/ cov matrix associations, with EMCal matching info", false); PROCESS_SWITCH(AnalysisTrackSelection, processDummy, "Dummy function", true); }; -// Produces a table with muon decisions (joinable to the ReducedMuonsAssociations) +// o2::framework::Produces a table with muon decisions (joinable to the ReducedMuonsAssociations) // Here one should add all the track cuts needed through the workflow (e.g. cuts for same-event pairing, track for dilepton-track correlations) struct AnalysisMuonSelection { - Produces muonSel; - Produces muonAmbiguities; - OutputObj fOutputList{"output"}; + o2::framework::Produces muonSel; + o2::framework::Produces muonAmbiguities; + o2::framework::OutputObj fOutputList{"output"}; - Configurable fConfigCuts{"cfgMuonCuts", "muonQualityCuts", "Comma separated list of muon cuts"}; - Configurable fConfigCutsJSON{"cfgMuonCutsJSON", "", "Additional list of muon cuts in JSON format"}; - Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; - Configurable fConfigAddMuonHistogram{"cfgAddMuonHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; + o2::framework::Configurable fConfigCuts{"cfgMuonCuts", "muonQualityCuts", "Comma separated list of muon cuts"}; + o2::framework::Configurable fConfigCutsJSON{"cfgMuonCutsJSON", "", "Additional list of muon cuts in JSON format"}; + o2::framework::Configurable fConfigQA{"cfgQA", false, "If true, fill QA histograms"}; + o2::framework::Configurable fConfigAddMuonHistogram{"cfgAddMuonHistogram", "", "Comma separated list of histograms"}; + o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + o2::framework::Configurable fConfigPublishAmbiguity{"cfgPublishAmbiguity", true, "If true, publish ambiguity table and fill QA histograms"}; - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - Service fCCDB; + o2::framework::Service fCCDB{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fMuonCuts; - int fCurrentRun; // current run kept to detect run changes and trigger loading params from CCDB + int fCurrentRun = 0; // current run kept to detect run changes and trigger loading params from CCDB std::map> fNAssocsInBunch; // key: muon global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: muon global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) @@ -891,15 +1093,15 @@ struct AnalysisMuonSelection { if (!cutNamesStr.IsNull()) { std::unique_ptr objArray(cutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - fMuonCuts.push_back(dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); + fMuonCuts.push_back(o2::aod::dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } // extra cuts from JSON TString addCutsStr = fConfigCutsJSON.value; if (addCutsStr != "") { - std::vector addCuts = dqcuts::GetCutsFromJSON(addCutsStr.Data()); - for (auto& t : addCuts) { - fMuonCuts.push_back(reinterpret_cast(t)); + std::vector addCuts = o2::aod::dqcuts::GetCutsFromJSON(addCutsStr.Data()); + for (auto const& t : addCuts) { + fMuonCuts.push_back(dynamic_cast(t)); } } @@ -908,20 +1110,20 @@ struct AnalysisMuonSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); // set one histogram directory for each defined track cut TString histDirNames = "TrackMuon_BeforeCuts;"; - for (auto& cut : fMuonCuts) { + for (auto const& cut : fMuonCuts) { histDirNames += Form("TrackMuon_%s;", cut->GetName()); } if (fConfigPublishAmbiguity) { histDirNames += "TrackMuon_AmbiguityInBunch;TrackMuon_AmbiguityOutOfBunch;"; } - DefineHistograms(fHistMan, histDirNames.Data(), fConfigAddMuonHistogram.value.data()); // define all histograms - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + DefineHistograms(fHistMan, histDirNames.Data(), fConfigAddMuonHistogram.value.data()); // define all histograms + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); } @@ -929,34 +1131,24 @@ struct AnalysisMuonSelection { fCCDB->setCaching(true); fCCDB->setLocalObjectValidityChecking(); fCCDB->setCreatedNotAfter(fConfigNoLaterThan.value); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigGeoPath); - } } template - void runMuonSelection(ReducedMuonsAssoc const& assocs, TEvents const& events, TMuons const& muons) + void runMuonSelection(o2::aod::ReducedMuonsAssoc const& assocs, TEvents const& events, TMuons const& muons) { fNAssocsInBunch.clear(); fNAssocsOutOfBunch.clear(); if (events.size() > 0 && fCurrentRun != events.begin().runNumber()) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(grpmagPath, events.begin().timestamp()); - if (grpmag != nullptr) { - o2::base::Propagator::initFieldFromGRP(grpmag); - VarManager::SetMagneticField(grpmag->getNominalL3Field()); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", events.begin().timestamp()); - } fCurrentRun = events.begin().runNumber(); } muonSel.reserve(assocs.size()); muonAmbiguities.reserve(muons.size()); - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); int iCut = 0; - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { auto event = assoc.template reducedevent_as(); if (!event.isEventSelected_bit(0)) { muonSel(0); @@ -970,14 +1162,14 @@ struct AnalysisMuonSelection { filterMap = static_cast(0); VarManager::FillTrack(track); if (fConfigQA) { - fHistMan->FillHistClass("TrackMuon_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_BeforeCuts", dqtablereader_helpers::varValues()); } iCut = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(dqtablereader_helpers::varValues())) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(Form("TrackMuon_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TrackMuon_%s", (*cut)->GetName()), dqtablereader_helpers::varValues()); } } } // end loop over cuts @@ -1008,7 +1200,7 @@ struct AnalysisMuonSelection { if (fConfigPublishAmbiguity) { // QA the collision-track associations if (fConfigQA) { - for (auto& [trackIdx, evIndices] : fNAssocsInBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsInBunch) { if (evIndices.size() == 1) { continue; } @@ -1016,10 +1208,10 @@ struct AnalysisMuonSelection { VarManager::ResetValues(0, VarManager::kNMuonTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackMuon_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_AmbiguityInBunch", dqtablereader_helpers::varValues()); } // end loop over in-bunch ambiguous tracks - for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { + for (auto const& [trackIdx, evIndices] : fNAssocsOutOfBunch) { if (evIndices.size() == 1) { continue; } @@ -1027,12 +1219,12 @@ struct AnalysisMuonSelection { VarManager::ResetValues(0, VarManager::kNMuonTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackMuon_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_AmbiguityOutOfBunch", dqtablereader_helpers::varValues()); } // end loop over out-of-bunch ambiguous tracks } // publish the ambiguity table - for (auto& track : muons) { + for (auto const& track : muons) { int8_t nInBunch = 0; if (fNAssocsInBunch.find(track.globalIndex()) != fNAssocsInBunch.end()) { nInBunch = fNAssocsInBunch[track.globalIndex()].size(); @@ -1046,11 +1238,11 @@ struct AnalysisMuonSelection { } } - void processSkimmed(ReducedMuonsAssoc const& assocs, MyEventsVtxCovSelected const& events, MyMuonTracksWithCov const& muons) + void processSkimmed(o2::aod::ReducedMuonsAssoc const& assocs, MyEventsVtxCovSelected const& events, MyMuonTracksWithCov const& muons) { runMuonSelection(assocs, events, muons); } - void processDummy(MyEventsBasic&) + void processDummy(MyEventsBasic const&) { // do nothing } @@ -1064,21 +1256,21 @@ struct AnalysisMuonSelection { // with the sample of tracks to be used in downstream analysis (fConfigTrackCuts). If a pair is found to pass // the pair prefilter cut (cfgPrefilterPairCut), the analysis track is tagged to be removed from analysis. struct AnalysisPrefilterSelection { - Produces prefilter; // joinable with ReducedTracksAssoc + o2::framework::Produces prefilter; // joinable with o2::aod::ReducedTracksAssoc // Configurables - Configurable fConfigPrefilterTrackCut{"cfgPrefilterTrackCut", "", "Prefilter track cut"}; - Configurable fConfigPrefilterPairCut{"cfgPrefilterPairCut", "", "Prefilter pair cut"}; - Configurable fConfigTrackCuts{"cfgTrackCuts", "", "Track cuts for which to run the prefilter"}; + o2::framework::Configurable fConfigPrefilterTrackCut{"cfgPrefilterTrackCut", "", "Prefilter track cut"}; + o2::framework::Configurable fConfigPrefilterPairCut{"cfgPrefilterPairCut", "", "Prefilter pair cut"}; + o2::framework::Configurable fConfigTrackCuts{"cfgTrackCuts", "", "Track cuts for which to run the prefilter"}; // TODO: Add prefilter pair cut via JSON std::map fPrefilterMap; - AnalysisCompositeCut* fPairCut; - uint32_t fPrefilterMask; - int fPrefilterCutBit; + AnalysisCompositeCut* fPairCut = nullptr; + uint32_t fPrefilterMask = 0; + int fPrefilterCutBit = -1; - Preslice trackAssocsPerCollision = aod::reducedtrack_association::reducedeventId; + o2::framework::Preslice trackAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; void init(o2::framework::InitContext& context) { @@ -1112,15 +1304,15 @@ struct AnalysisPrefilterSelection { if (runPrefilter) { // get the list of cuts that were computed in the barrel track-selection task and create a bit mask // to mark just the ones we want to apply a prefilter on - string trackCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", trackCuts, false); + std::string trackCuts; + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", trackCuts, false); TString allTrackCutsStr = trackCuts; // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", trackCuts, false); TString addTrackCutsStr = trackCuts; if (addTrackCutsStr != "") { - std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { allTrackCutsStr += Form(",%s", t->GetName()); } } @@ -1145,7 +1337,7 @@ struct AnalysisPrefilterSelection { fPairCut = new AnalysisCompositeCut(true); TString pairCutStr = fConfigPrefilterPairCut.value; if (!pairCutStr.IsNull()) { - fPairCut = dqcuts::GetCompositeCut(pairCutStr.Data()); + fPairCut = o2::aod::dqcuts::GetCompositeCut(pairCutStr.Data()); } } if (fPrefilterMask == static_cast(0) || fPrefilterCutBit < 0) { @@ -1158,13 +1350,13 @@ struct AnalysisPrefilterSelection { } template - void runPrefilter(soa::Join const& assocs, TTracks const& /*tracks*/) + void runPrefilter(o2::soa::Join const& assocs, TTracks const& /*tracks*/) { if (fPrefilterCutBit < 0 || fPrefilterMask == 0) { return; } - for (auto& [assoc1, assoc2] : o2::soa::combinations(assocs, assocs)) { + for (auto const& [assoc1, assoc2] : o2::soa::combinations(assocs, assocs)) { auto track1 = assoc1.template reducedtrack_as(); auto track2 = assoc2.template reducedtrack_as(); @@ -1181,14 +1373,14 @@ struct AnalysisPrefilterSelection { bool track2Loose = assoc2.isBarrelSelected_bit(fPrefilterCutBit); // Here we check if we should apply the prefilter for this pair - if (!((track1Candidate > 0 && track2Loose) || (track2Candidate > 0 && track1Loose))) { + if ((track1Candidate == 0 || !track2Loose) && (track2Candidate == 0 || !track1Loose)) { continue; } // compute pair quantities VarManager::FillPair(track1, track2); // if the pair fullfils the criteria, add an entry into the prefilter map for the two tracks - if (fPairCut->IsSelected(VarManager::fgValues)) { + if (fPairCut->IsSelected(dqtablereader_helpers::varValues())) { if (fPrefilterMap.find(track1.globalIndex()) == fPrefilterMap.end() && track1Candidate > 0) { fPrefilterMap[track1.globalIndex()] = track1Candidate; } @@ -1199,11 +1391,11 @@ struct AnalysisPrefilterSelection { } // end loop over combinations } - void processBarrelSkimmed(MyEventsBasic const& events, soa::Join const& assocs, MyBarrelTracks const& tracks) + void processBarrelSkimmed(MyEventsBasic const& events, o2::soa::Join const& assocs, MyBarrelTracks const& tracks) { fPrefilterMap.clear(); - for (auto& event : events) { + for (auto const& event : events) { auto groupedAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); if (groupedAssocs.size() > 1) { runPrefilter(groupedAssocs, tracks); @@ -1217,11 +1409,11 @@ struct AnalysisPrefilterSelection { prefilter(mymap); } } else { - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { // TODO: just use the index from the assoc (no need to cast the whole track) auto track = assoc.template reducedtrack_as(); mymap = -1; - if (fPrefilterMap.find(track.globalIndex()) != fPrefilterMap.end()) { + if (fPrefilterMap.contains(track.globalIndex())) { // NOTE: publish the bitwise negated bits (~), so there will be zeroes for cuts that failed the prefiltering and 1 everywhere else mymap = ~fPrefilterMap[track.globalIndex()]; prefilter(mymap); @@ -1232,7 +1424,7 @@ struct AnalysisPrefilterSelection { } } - void processDummy(MyEventsBasic&) + void processDummy(MyEventsBasic const&) { // do nothing } @@ -1248,80 +1440,105 @@ struct AnalysisPrefilterSelection { // The task implements also process functions for running event mixing struct AnalysisSameEventPairing { - Produces dielectronList; - Produces dimuonList; - Produces electronmuonList; - Produces dielectronsExtraList; - Produces dielectronInfoList; - Produces dimuonsExtraList; - Produces dielectronAllList; - Produces dimuonAllList; - Produces dileptonFlowList; - Produces dileptonInfoList; - Produces PromptNonPromptSepTable; - Produces dileptonPolarList; + o2::framework::Produces dielectronList; + o2::framework::Produces dimuonList; + o2::framework::Produces electronmuonList; + o2::framework::Produces dielectronsExtraList; + o2::framework::Produces dielectronInfoList; + o2::framework::Produces dimuonsExtraList; + o2::framework::Produces dielectronAllList; + o2::framework::Produces dielectronMlList; + o2::framework::Produces dimuonAllList; + o2::framework::Produces dileptonFlowList; + o2::framework::Produces dileptonInfoList; + o2::framework::Produces PromptNonPromptSepTable; + o2::framework::Produces dileptonPolarList; + o2::framework::Produces dileptonEventInfoList; + o2::framework::Produces dileptonMiniTree; o2::base::MatLayerCylSet* fLUT = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. - - OutputObj fOutputList{"output"}; - - struct : ConfigurableGroup { - Configurable track{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; - Configurable muon{"cfgMuonCuts", "", "Comma separated list of muon cuts"}; - Configurable pair{"cfgPairCuts", "", "Comma separated list of pair cuts"}; - Configurable event{"cfgRemoveCollSplittingCandidates", false, "If true, remove collision splitting candidates as determined by the event selection task upstream"}; + TH1D* ResoFlowSP = nullptr; + TH1D* ResoFlowEP = nullptr; + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. + + o2::framework::OutputObj fOutputList{"output"}; + + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable track{"cfgTrackCuts", "jpsiO2MCdebugCuts2", "Comma separated list of barrel track cuts"}; + o2::framework::Configurable muon{"cfgMuonCuts", "", "Comma separated list of muon cuts"}; + o2::framework::Configurable pair{"cfgPairCuts", "", "Comma separated list of pair cuts"}; + o2::framework::Configurable qVector{"cfgQvectorTrackCut", "", "Track cut of Q-vector, enable this if you want to remove the auto-correlation in TPC"}; + o2::framework::Configurable event{"cfgRemoveCollSplittingCandidates", false, "If true, remove collision splitting candidates as determined by the event selection task upstream"}; // TODO: Add pair cuts via JSON } fConfigCuts; - Configurable fConfigMixingDepth{"cfgMixingDepth", 100, "Number of Events stored for event mixing"}; - // Configurable fConfigAddEventMixingHistogram{"cfgAddEventMixingHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - Configurable fConfigQA{"cfgQA", true, "If true, fill output histograms"}; - Configurable fConfigAmbiguousMuonHistograms{"cfgAmbiguousMuonHistograms", true, "If true, fill ambiguous histograms"}; - - struct : ConfigurableGroup { - Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable GrpLhcIfPath{"grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + // add event level histograms after remove collision splitting candidates + o2::framework::Configurable fConfigAddEventHistogram{"cfgAddEventHistogram", "", "Comma separated list of histograms,only used after turn on the cfgRemoveCollSplittingCandidates option"}; + o2::framework::Configurable fConfigAddJSONEventHistograms{"cfgAddJSONEventHistograms", "", "Event histograms in JSON format,only used after turn on the cfgRemoveCollSplittingCandidates option"}; + + o2::framework::Configurable fConfigMixingDepth{"cfgMixingDepth", 100, "Number of Events stored for event mixing"}; + o2::framework::Configurable fConfigMixingVariables{"cfgMixingVars", "", "Mixing configs separated by a comma, default no mixing"}; + o2::framework::Configurable fConfigMixingVariablesJson{"cfgMixingVarsJSON", "", "Mixing configs in JSON format"}; + o2::framework::Configurable fConfigRunMixingAcrossTFs{"cfgRunMixingAcrossTFs", false, "If true, run event mixing using the MixingHandler which accumulates events across multiple TFs"}; + // o2::framework::Configurable fConfigAddEventMixingHistogram{"cfgAddEventMixingHistogram", "", "Comma separated list of histograms"}; + o2::framework::Configurable fConfigAddSEPHistogram{"cfgAddSEPHistogram", "", "Comma separated list of histograms"}; + o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + o2::framework::Configurable fConfigQA{"cfgQA", true, "If true, fill output histograms"}; + o2::framework::Configurable fConfigAmbiguousMuonHistograms{"cfgAmbiguousMuonHistograms", true, "If true, fill ambiguous histograms"}; + + // option for TR pair fill + o2::framework::Configurable fConfigTRPairs{"cfgFillTRPairs", false, "If true, fill Track rotation pairs"}; + o2::framework::Configurable fConfigNRotations{"cfgNRotations", 3, "Number of rotations for event plane preserving track rotation method, only 1 or 3 are supported"}; + + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable grpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + o2::framework::Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + o2::framework::Configurable GrpLhcIfPath{"grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + o2::framework::Configurable efficiencyPath{"effHistPath", "Users/z/zhxiong/efficiency", "Path on the CCDB for the efficiency histograms"}; + o2::framework::Configurable flowPath{"flowPath", "Users/y/yiping/FlowResolution", "Path to the flow resolution object"}; + o2::framework::Configurable phiPath{"phiPath", "Users/h/hxiaoyu/TrackPhi/LHC23", "Path to load phi distribution for track rotation"}; } fConfigCCDB; - struct : ConfigurableGroup { - Configurable useRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable magField{"cfgMagField", 5.0f, "Manually set magnetic field"}; - Configurable flatTables{"cfgFlatTables", false, "Produce a single flat tables with all relevant information of the pairs and single tracks"}; - Configurable polarTables{"cfgPolarTables", false, "Produce tables with dilepton polarization information"}; - Configurable useKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; - Configurable useAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - Configurable propToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; - Configurable corrFullGeo{"cfgCorrFullGeo", false, "Use full geometry to correct for MCS effects in track propagation"}; - Configurable noCorr{"cfgNoCorrFwdProp", false, "Do not correct for MCS effects in track propagation"}; - Configurable collisionSystem{"syst", "pp", "Collision system, pp or PbPb"}; - Configurable centerMassEnergy{"energy", 13600, "Center of mass energy in GeV"}; - Configurable propTrack{"cfgPropTrack", true, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; - Configurable useRemoteCollisionInfo{"cfgUseRemoteCollisionInfo", false, "Use remote collision information from CCDB"}; + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable flatTables{"cfgFlatTables", false, "Produce a single flat tables with all relevant information of the pairs and single tracks"}; + o2::framework::Configurable polarTables{"cfgPolarTables", false, "Produce tables with dilepton polarization information"}; + o2::framework::Configurable useKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; + o2::framework::Configurable useAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; + o2::framework::Configurable noCorr{"cfgNoCorrFwdProp", false, "Do not correct for MCS effects in track propagation"}; + o2::framework::Configurable collisionSystem{"syst", "pp", "Collision system, pp or PbPb"}; + o2::framework::Configurable centerMassEnergy{"energy", 13600, "Center of mass energy in GeV"}; + o2::framework::Configurable propTrack{"cfgPropTrack", true, "Propgate tracks to associated collision to recalculate DCA and momentum vector"}; + o2::framework::Configurable useRemoteCollisionInfo{"cfgUseRemoteCollisionInfo", false, "Use remote collision information from CCDB"}; + o2::framework::Configurable useEfficiencyWeighting{"cfgUseEfficiencyWeighting", false, "Apply efficiency weighting to the pairs from CCDB"}; + o2::framework::Configurable efficiencyType{"cfgEfficiencyType", 0, "Type of efficiency to apply from CCDB: 0 no efficiency, 1 pt-cent-costhetastar"}; + o2::framework::Configurable useFlowReso{"cfgUseFlowReso", false, "Use remote flow information from CCDB"}; + o2::framework::Configurable usePhiDistribution{"cfgUsePhiDistribution", false, "Use phi distribution to correct track rotation"}; } fConfigOptions; - struct : ConfigurableGroup { - Configurable applyBDT{"applyBDT", false, "Flag to apply ML selections"}; - Configurable fConfigBdtCutsJSON{"fConfigBdtCutsJSON", "", "Additional list of BDT cuts in JSON format"}; + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable applyBDT{"applyBDT", false, "Flag to apply ML selections"}; + o2::framework::Configurable fConfigBdtCutsJSON{"fConfigBdtCutsJSON", "", "Additional list of BDT cuts in JSON format"}; - Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/j/jseo/ML/PbPbPsi/default/"}, "Paths of models on CCDB"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; - Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + o2::framework::Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/j/jseo/ML/PbPbPsi/default/"}, "Paths of models on CCDB"}; + o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; } fConfigML; + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable fConfigMiniTree{"useMiniTree.cfgMiniTree", false, "Produce a single flat table with minimal information for analysis"}; + o2::framework::Configurable fConfigMiniTreeMinMass{"useMiniTree.cfgMiniTreeMinMass", 2, "Min. mass cut for minitree"}; + o2::framework::Configurable fConfigMiniTreeMaxMass{"useMiniTree.cfgMiniTreeMaxMass", 5, "Max. mass cut for minitree"}; + } useMiniTree; - Service fCCDB; + o2::framework::Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; - Filter filterEventSelected = aod::dqanalysisflags::isEventSelected > static_cast(0); + o2::framework::expressions::Filter filterEventSelected = o2::aod::dqanalysisflags::isEventSelected > static_cast(0); - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; + MixingHandler fMixingHandler; o2::analysis::DQMlResponse fDQMlResponse; - std::vector fOutputMlPsi2ee = {}; // TODO: check this is needed or not + std::vector fOutputMlPsi2ee; // TODO: check this is needed or not // keep histogram class names in maps, so we don't have to buld their names in the pair loops std::map> fTrackHistNames; @@ -1332,32 +1549,35 @@ struct AnalysisSameEventPairing { std::vector fMuonCuts; std::map, uint32_t> fAmbiguousPairs; - uint32_t fTrackFilterMask; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream - uint32_t fMuonFilterMask; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream - int fNCutsBarrel; - int fNCutsMuon; - int fNPairCuts; - int fNPairPerEvent; + uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream + uint32_t fMuonFilterMask = 0; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream + uint32_t fQvectorFilterMask = 0; // mask for the track cuts applied in TPC Q-vector calculation, used to remove auto-correlation in flow analysis + int fNCutsBarrel = 0; + int fNCutsMuon = 0; + int fNPairCuts = 0; + int fNPairPerEvent = 0; + + bool fEnableBarrelMixingHistos = false; + bool fEnableBarrelHistos = false; + bool fEnableMuonHistos = false; + bool fEnableMuonMixingHistos = false; + bool fEnableBarrelMuonHistos = false; + bool fEnableBarrelMuonMixingHistos = false; - bool fEnableBarrelMixingHistos; - bool fEnableBarrelHistos; - bool fEnableMuonHistos; - bool fEnableMuonMixingHistos; - bool fEnableBarrelMuonHistos; - bool fEnableBarrelMuonMixingHistos; + bool fSplitCheckEnabled = false; - NoBinningPolicy hashBin; + o2::framework::NoBinningPolicy hashBin; - Preslice> trackAssocsPerCollision = aod::reducedtrack_association::reducedeventId; - Preslice> trackEmuAssocsPerCollision = aod::reducedtrack_association::reducedeventId; - Preslice> muonAssocsPerCollision = aod::reducedtrack_association::reducedeventId; + o2::framework::Preslice> trackAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; + o2::framework::Preslice> muonAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; void init(o2::framework::InitContext& context) { - fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed") || context.mOptions.get("processBarrelOnlySkimmedNoCov") || context.mOptions.get("processBarrelOnlySkimmedNoCovWithMultExtra") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmedNoCov"); + fEnableBarrelHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processBarrelOnlySkimmed") || context.mOptions.get("processBarrelOnlyWithCollSkimmed") || context.mOptions.get("processBarrelOnlySkimmedNoCov") || context.mOptions.get("processBarrelOnlySkimmedNoCovWithMultExtra") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmedNoCov") || context.mOptions.get("processBarrelOnlyWithQvectorCentrSkimmed"); fEnableBarrelMixingHistos = context.mOptions.get("processMixingAllSkimmed") || context.mOptions.get("processMixingBarrelSkimmed") || context.mOptions.get("processMixingBarrelSkimmedFlow") || context.mOptions.get("processMixingBarrelWithQvectorCentrSkimmedNoCov"); - fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlySkimmedMultExtra") || context.mOptions.get("processMuonOnlySkimmedFlow") || context.mOptions.get("processMixingMuonSkimmed"); - fEnableMuonMixingHistos = context.mOptions.get("processMixingAllSkimmed") || context.mOptions.get("processMixingMuonSkimmed"); + fEnableBarrelMixingHistos |= fConfigRunMixingAcrossTFs; + fEnableMuonHistos = context.mOptions.get("processAllSkimmed") || context.mOptions.get("processMuonOnlySkimmed") || context.mOptions.get("processMuonOnlySkimmedMultExtra") || context.mOptions.get("processMuonOnlySkimmedFlow") || context.mOptions.get("processMuonOnlyVertexingSkimmed"); + fEnableMuonMixingHistos = context.mOptions.get("processMixingAllSkimmed") || context.mOptions.get("processMixingMuonSkimmed") || context.mOptions.get("processMixingMuonSkimmedFlow"); fEnableBarrelMuonHistos = context.mOptions.get("processElectronMuonSkimmed"); fEnableBarrelMuonMixingHistos = context.mOptions.get("processMixingElectronMuonSkimmed"); @@ -1372,15 +1592,25 @@ struct AnalysisSameEventPairing { // Keep track of all the histogram class names to avoid composing strings in the pairing loop TString histNames = ""; std::vector names; + fTrackHistNames.clear(); + fMuonHistNames.clear(); + fTrackMuonHistNames.clear(); + fPairCuts.clear(); fTrackCuts.clear(); fMuonCuts.clear(); + fTrackFilterMask = 0; + fMuonFilterMask = 0; + fNCutsBarrel = 0; + fNCutsMuon = 0; + fNPairCuts = 0; + fNPairPerEvent = 0; // NOTE: Pair cuts are only applied on the histogram output. The produced pair tables do not have these cuts applied TString cutNamesStr = fConfigCuts.pair.value; if (!cutNamesStr.IsNull()) { std::unique_ptr objArray(cutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - fPairCuts.push_back(*dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); + fPairCuts.push_back(*o2::aod::dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } @@ -1396,6 +1626,7 @@ struct AnalysisSameEventPairing { if (!muonCutsStr.IsNull()) { objArrayMuonCuts = muonCutsStr.Tokenize(","); } + TString qVectorCutStr = fConfigCuts.qVector.value; if (fConfigML.applyBDT) { // BDT cuts via JSON @@ -1408,8 +1639,8 @@ struct AnalysisSameEventPairing { auto config = o2::aod::dqmlcuts::GetBdtScoreCutsAndConfigFromJSON(fConfigML.fConfigBdtCutsJSON.value.c_str()); - if (std::holds_alternative(config)) { - auto& cfg = std::get(config); + if (std::holds_alternative(config)) { + auto& cfg = std::get(config); binsMl = cfg.binsMl; nClassesMl = 1; cutsMl = cfg.cutsMl; @@ -1421,7 +1652,7 @@ struct AnalysisSameEventPairing { fDQMlResponse.setCentType(cfg.centType); LOG(info) << "Using BDT cuts for binary classification"; } else { - auto& cfg = std::get(config); + auto& cfg = std::get(config); binsMl = cfg.binsMl; nClassesMl = 3; cutsMl = cfg.cutsMl; @@ -1446,15 +1677,15 @@ struct AnalysisSameEventPairing { } // get the barrel track selection cuts - string tempCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + std::string tempCuts; + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); TString tempCutsStr = tempCuts; // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); TString addTrackCutsStr = tempCuts; if (addTrackCutsStr != "") { - std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { tempCutsStr += Form(",%s", t->GetName()); } } @@ -1463,12 +1694,16 @@ struct AnalysisSameEventPairing { fNCutsBarrel = objArray->GetEntries(); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { TString tempStr = objArray->At(icut)->GetName(); + if (tempStr.CompareTo(qVectorCutStr) == 0) { + fQvectorFilterMask |= (static_cast(1) << icut); + } fTrackCuts.push_back(tempStr); if (objArrayTrackCuts->FindObject(tempStr.Data()) != nullptr) { fTrackFilterMask |= (static_cast(1) << icut); if (fEnableBarrelHistos) { names = { + // change define histname Form("PairsBarrelSEPM_%s", objArray->At(icut)->GetName()), Form("PairsBarrelSEPP_%s", objArray->At(icut)->GetName()), Form("PairsBarrelSEMM_%s", objArray->At(icut)->GetName())}; @@ -1477,15 +1712,20 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsBarrelSEPP_ambiguousextra_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelSEMM_ambiguousextra_%s", objArray->At(icut)->GetName())); histNames += Form("%s;%s;%s;", names[3].Data(), names[4].Data(), names[5].Data()); + if (fConfigTRPairs) { + names.push_back(Form("PairsBarrelTRPM_%s", objArray->At(icut)->GetName())); + names.push_back(Form("PairsBarrelTRPM_ambiguousextra_%s", objArray->At(icut)->GetName())); + names.push_back(Form("PairsBarrelTR_MEPM_%s", objArray->At(icut)->GetName())); + histNames += Form("%s;%s;%s;", names[6].Data(), names[7].Data(), names[8].Data()); + } if (fEnableBarrelMixingHistos) { names.push_back(Form("PairsBarrelMEPM_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelMEPP_%s", objArray->At(icut)->GetName())); names.push_back(Form("PairsBarrelMEMM_%s", objArray->At(icut)->GetName())); - histNames += Form("%s;%s;%s;", names[6].Data(), names[7].Data(), names[8].Data()); + histNames += Form("%s;%s;%s;", names[(fConfigTRPairs ? 9 : 6)].Data(), names[(fConfigTRPairs ? 10 : 7)].Data(), names[(fConfigTRPairs ? 11 : 8)].Data()); } fTrackHistNames[icut] = names; - TString cutNamesStr = fConfigCuts.pair.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); @@ -1504,14 +1744,14 @@ struct AnalysisSameEventPairing { } // get the muon track selection cuts - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", tempCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", tempCuts, false); tempCutsStr = tempCuts; // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", tempCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", tempCuts, false); TString addMuonCutsStr = tempCuts; if (addMuonCutsStr != "") { - std::vector addMuonCuts = dqcuts::GetCutsFromJSON(addMuonCutsStr.Data()); - for (auto& t : addMuonCuts) { + std::vector addMuonCuts = o2::aod::dqcuts::GetCutsFromJSON(addMuonCutsStr.Data()); + for (auto const& t : addMuonCuts) { tempCutsStr += Form(",%s", t->GetName()); } } @@ -1568,7 +1808,6 @@ struct AnalysisSameEventPairing { } fMuonHistNames[icut] = names; - TString cutNamesStr = fConfigCuts.pair.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); @@ -1586,6 +1825,28 @@ struct AnalysisSameEventPairing { } } + if (fConfigRunMixingAcrossTFs) { + if (fNCutsBarrel > MixingHandler::NMaxCuts) { + LOGF(fatal, "Across-TF mixing supports at most %d barrel track-cut bits, got %d", MixingHandler::NMaxCuts, fNCutsBarrel); + } + TString mixVarsString = fConfigMixingVariables.value; + TString mixVarsJsonString = fConfigMixingVariablesJson.value; + std::unique_ptr objArray(mixVarsString.Tokenize(",")); + if (objArray->GetEntries() > 0 || mixVarsJsonString != "") { + // fMixingHandler = new MixingHandler("mixingHandler", "mixing handler"); + if (objArray->GetEntries() > 0) { + for (int iVar = 0; iVar < objArray->GetEntries(); ++iVar) { + o2::aod::dqmixing::SetUpMixing(&fMixingHandler, objArray->At(iVar)->GetName()); + } + } + } + if (mixVarsJsonString != "") { + o2::aod::dqmixing::SetUpMixingFromJSON(&fMixingHandler, mixVarsJsonString.Data()); + } + fMixingHandler.SetPoolDepth(fConfigMixingDepth); + fMixingHandler.Init(); + } + fCurrentRun = 0; fCCDB->setURL(fConfigCCDB.url.value); @@ -1595,10 +1856,6 @@ struct AnalysisSameEventPairing { if (fConfigOptions.noCorr) { VarManager::SetupFwdDCAFitterNoCorr(); - } else if (fConfigOptions.corrFullGeo || (fConfigOptions.useKFVertexing && fConfigOptions.propToPCA)) { - if (!o2::base::GeometryManager::isGeometryLoaded()) { - fCCDB->get(fConfigCCDB.geoPath); - } } else { fLUT = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigCCDB.lutPath)); VarManager::SetupMatLUTFwdDCAFitter(fLUT); @@ -1607,13 +1864,15 @@ struct AnalysisSameEventPairing { if (fEnableBarrelMuonHistos || fEnableBarrelMuonMixingHistos) { for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { TString trackCutName = fTrackCuts[iTrack]; - if (objArrayTrackCuts->FindObject(trackCutName.Data()) == nullptr) + if (objArrayTrackCuts->FindObject(trackCutName.Data()) == nullptr) { continue; + } for (int iMuon = 0; iMuon < fNCutsMuon; ++iMuon) { TString muonCutName = fMuonCuts[iMuon]; - if (objArrayMuonCuts->FindObject(muonCutName.Data()) == nullptr) + if (objArrayMuonCuts->FindObject(muonCutName.Data()) == nullptr) { continue; + } names = { Form("PairsEleMuSEPM_%s_%s", trackCutName.Data(), muonCutName.Data()), @@ -1621,7 +1880,6 @@ struct AnalysisSameEventPairing { Form("PairsEleMuSEMM_%s_%s", trackCutName.Data(), muonCutName.Data())}; histNames += Form("%s;%s;%s;", names[0].Data(), names[1].Data(), names[2].Data()); int index = iTrack * fNCutsMuon + iMuon; - fTrackMuonHistNames[index] = names; if (fEnableBarrelMuonMixingHistos) { names.push_back(Form("PairsEleMuMEPM_%s_%s", trackCutName.Data(), muonCutName.Data())); @@ -1629,8 +1887,8 @@ struct AnalysisSameEventPairing { names.push_back(Form("PairsEleMuMEMM_%s_%s", trackCutName.Data(), muonCutName.Data())); histNames += Form("%s;%s;%s;", names[3].Data(), names[4].Data(), names[5].Data()); } + fTrackMuonHistNames[index] = names; - TString cutNamesStr = fConfigCuts.pair.value; if (!cutNamesStr.IsNull()) { std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); int nPairCuts = objArrayPair->GetEntries(); @@ -1654,20 +1912,26 @@ struct AnalysisSameEventPairing { } } + o2::common::core::getTaskOptionValue(context, "analysis-event-selection", "cfgCheckSplitCollisions", fSplitCheckEnabled, false); + if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy - DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms + fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + if (fConfigCuts.event.value && fSplitCheckEnabled) { + DefineHistograms(fHistMan, "Event_AfterSplitVeto", fConfigAddEventHistogram.value.data()); // define event histograms after removing collision splitting candidates + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONEventHistograms.value.c_str()); + } + DefineHistograms(fHistMan, histNames.Data(), fConfigAddSEPHistogram.value.data()); // define all histograms if (fEnableBarrelHistos) { DefineHistograms(fHistMan, "PairingSEQA", "sameevent-pairing"); // histograms for QA of the pairing } if (fEnableBarrelMixingHistos) { DefineHistograms(fHistMan, "PairingMEQA", "mixedevent-pairing"); // histograms for QA of the pairing } - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); } } @@ -1675,55 +1939,74 @@ struct AnalysisSameEventPairing { void initParamsFromCCDB(uint64_t timestamp, int runNumber, bool withTwoProngFitter = true) { - if (fConfigOptions.useRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations - } + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (withTwoProngFitter) { - if (fConfigOptions.useKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigOptions.magField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables - VarManager::SetupTwoProngFwdDCAFitter(fConfigOptions.magField.value, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (withTwoProngFitter) { + if (fConfigOptions.useKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // TODO: get these parameters from Configurables + VarManager::SetupTwoProngFwdDCAFitter(magField, true, 200.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); } + } else { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations } - std::map metadataRCT, header; + std::map metadataRCT, header; header = fCCDBApi.retrieveHeaders(Form("RCT/Info/RunInformation/%i", runNumber), metadataRCT, -1); uint64_t sor = std::atol(header["SOR"].c_str()); uint64_t eor = std::atol(header["EOR"].c_str()); VarManager::SetSORandEOR(sor, eor); if (fConfigOptions.useRemoteCollisionInfo) { - o2::parameters::GRPLHCIFData* grpo = fCCDB->getForTimeStamp(fConfigCCDB.GrpLhcIfPath, timestamp); + auto grpo = fCCDB->getForTimeStamp(fConfigCCDB.GrpLhcIfPath, timestamp); VarManager::SetCollisionSystem(grpo); } + + if (fConfigOptions.useEfficiencyWeighting) { + auto effList = fCCDB->getForTimeStamp(fConfigCCDB.efficiencyPath.value, timestamp); + VarManager::SetEfficiencyObject(fConfigOptions.efficiencyType.value, effList->FindObject("efficiency")); + } + + if (fConfigOptions.useFlowReso) { + TString PathFlow = fConfigCCDB.flowPath.value; + TString ccdbPathFlowSP = Form("%s/ScalarProduct", PathFlow.Data()); + TString ccdbPathFlowEP = Form("%s/EventPlane", PathFlow.Data()); + ResoFlowSP = fCCDB->getForTimeStamp(ccdbPathFlowSP.Data(), timestamp); + ResoFlowEP = fCCDB->getForTimeStamp(ccdbPathFlowEP.Data(), timestamp); + if (ResoFlowSP == nullptr || ResoFlowEP == nullptr) { + LOGF(fatal, "Flow resolution histograms not available in CCDB at timestamp=%llu", timestamp); + } + } + + if (fConfigOptions.usePhiDistribution) { + TString PathPhi = fConfigCCDB.phiPath.value; + TString ccdbPathPhiPosi = Form("%s/hPtPhiPositive", PathPhi.Data()); + TString ccdbPathPhiNega = Form("%s/hPtPhiNegative", PathPhi.Data()); + auto phiPosi = fCCDB->getForTimeStamp(ccdbPathPhiPosi.Data(), timestamp); + auto phiNega = fCCDB->getForTimeStamp(ccdbPathPhiNega.Data(), timestamp); + if (phiPosi == nullptr || phiNega == nullptr) { + LOGF(fatal, "Phi distribution histograms not available in CCDB at timestamp=%llu", timestamp); + } + VarManager::SetPhiMap(phiPosi, phiNega, fConfigOptions.usePhiDistribution.value); + } } // Template function to run same event pairing (barrel-barrel, muon-muon, barrel-muon) template - void runSameEventPairing(TEvents const& events, Preslice& preslice, TTrackAssocs const& assocs, TTracks const& /*tracks*/) + void runSameEventPairing(TEvents const& events, o2::framework::Preslice& preslice, TTrackAssocs const& assocs, TTracks const& /*tracks*/) { if (events.size() > 0) { // Additional protection to avoid crashing of events.begin().runNumber() if (fCurrentRun != events.begin().runNumber()) { + if (fConfigRunMixingAcrossTFs) { + // do not mix events from different runs + fMixingHandler.ClearPools(); + } initParamsFromCCDB(events.begin().timestamp(), events.begin().runNumber(), TTwoProngFitter); fCurrentRun = events.begin().runNumber(); } @@ -1751,32 +2034,63 @@ struct AnalysisSameEventPairing { histNames = fTrackMuonHistNames; }*/ - uint32_t twoTrackFilter = static_cast(0); - uint32_t dileptonMcDecision = static_cast(0); // placeholder, copy of the dqEfficiency.cxx one + auto twoTrackFilter = static_cast(0); + auto dileptonMcDecision = static_cast(0); // placeholder, copy of the dqEfficiency.cxx one int sign1 = 0; int sign2 = 0; - dielectronList.reserve(1); - dimuonList.reserve(1); - dielectronsExtraList.reserve(1); - dielectronInfoList.reserve(1); - dimuonsExtraList.reserve(1); - dileptonInfoList.reserve(1); - dileptonFlowList.reserve(1); + // Reserve capacity for the output tables to avoid repeated reallocations + // inside the Arrow builders. Unused capacity is virtual address space + // only — pages are not faulted in until written. + // estimate reserved size + int64_t reserveSize = 0; + for (auto const& event : events) { + if (event.isEventSelected_bit(0)) { + auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); + size_t nGood = 0; + for (auto const& t : groupedAssocs) { + if constexpr (TPairType == VarManager::kDecayToEE) { + if (t.isBarrelSelected_raw()) { + nGood++; + } + } else if constexpr (TPairType == VarManager::kDecayToMuMu) { + if (t.isMuonSelected_raw()) { + nGood++; + } + } + } + reserveSize += nGood * (nGood - 1) / 2; + } + } + + dielectronList.reserve(reserveSize); + dimuonList.reserve(reserveSize); + dielectronsExtraList.reserve(reserveSize); + dielectronInfoList.reserve(reserveSize); + dimuonsExtraList.reserve(reserveSize); + dileptonInfoList.reserve(reserveSize); + dileptonFlowList.reserve(reserveSize); if (fConfigOptions.flatTables.value) { - dielectronAllList.reserve(1); - dimuonAllList.reserve(1); + dielectronAllList.reserve(reserveSize); + dielectronMlList.reserve(reserveSize); + dimuonAllList.reserve(reserveSize); } if (fConfigOptions.polarTables.value) { - dileptonPolarList.reserve(1); + dileptonPolarList.reserve(reserveSize); + dileptonEventInfoList.reserve(reserveSize); } + if (useMiniTree.fConfigMiniTree) { + dileptonMiniTree.reserve(reserveSize); + } + fAmbiguousPairs.clear(); constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::ReducedEventQvector) > 0); constexpr bool eventHasQvectorCentr = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::TrackCov) > 0 || (TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelCov) > 0); + // constexpr bool fillFlowReso = eventHasQvector || eventHasQvectorCentr; bool isSelectedBDT = false; fNPairPerEvent = 0; - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -1786,16 +2100,32 @@ struct AnalysisSameEventPairing { uint8_t evSel = event.isEventSelected_raw(); // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - // VarManager::FillEvent(event, VarManager::fgValues); - VarManager::FillEvent(event, VarManager::fgValues); + // VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + + // fill the event histograms + if (fConfigQA && fConfigCuts.event && fSplitCheckEnabled) { + fHistMan->FillHistClass("Event_AfterSplitVeto", dqtablereader_helpers::varValues()); + } auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); if (groupedAssocs.size() == 0) { continue; } + if (fConfigOptions.useFlowReso) { + if (ResoFlowSP == nullptr || ResoFlowEP == nullptr) { + LOGF(fatal, "Flow resolution histograms are not available, cannot fill flow variables!"); + } + VarManager::FillEventFlowResoFactor(ResoFlowSP, ResoFlowEP); + } + int mixingCategory = -1; + if (fConfigRunMixingAcrossTFs) { + mixingCategory = fMixingHandler.FindEventCategory(dqtablereader_helpers::varValues()); + } + bool isFirst = true; - for (auto& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { + for (auto const& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { if constexpr (TPairType == VarManager::kDecayToEE) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; @@ -1823,13 +2153,27 @@ struct AnalysisSameEventPairing { } fNPairPerEvent++; + + // check if t1 or t2 is used in TPC Q vector calculation, so as to remove auto correlations in the flow analysis + VarManager::fgValues[VarManager::kSel1] = -9999.; + VarManager::fgValues[VarManager::kSel2] = -9999.; + if (t1.reducedeventId() == event.globalIndex()) { + if ((a1.isBarrelSelected_raw() & fQvectorFilterMask) > 0) { + VarManager::fgValues[VarManager::kSel1] = 1.; + } + } + if (t2.reducedeventId() == event.globalIndex()) { + if ((a2.isBarrelSelected_raw() & fQvectorFilterMask) > 0) { + VarManager::fgValues[VarManager::kSel2] = 1.; + } + } VarManager::FillPair(t1, t2); // compute quantities which depend on the associated collision, such as DCA if (fConfigOptions.propTrack) { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -1838,71 +2182,106 @@ struct AnalysisSameEventPairing { VarManager::FillPairVn(t1, t2); } + if (fConfigOptions.useEfficiencyWeighting) { + VarManager::FillEfficiency(); + } + dielectronList(event.globalIndex(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), twoTrackFilter, 0); if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackCollInfo) > 0) { - dielectronInfoList(t1.collisionId(), t1.trackId(), t2.trackId()); - dileptonInfoList(t1.collisionId(), event.posX(), event.posY(), event.posZ()); + dielectronInfoList(event.collisionId(), t1.trackId(), t2.trackId()); + dileptonInfoList(event.collisionId(), event.posX(), event.posY(), event.posZ()); } if (fConfigOptions.polarTables.value) { - dileptonPolarList(VarManager::fgValues[VarManager::kCosThetaHE], VarManager::fgValues[VarManager::kPhiHE], VarManager::fgValues[VarManager::kPhiTildeHE], + dileptonPolarList(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], t1.sign() + t2.sign(), twoTrackFilter, + VarManager::fgValues[VarManager::kCosThetaHE], VarManager::fgValues[VarManager::kPhiHE], VarManager::fgValues[VarManager::kPhiTildeHE], VarManager::fgValues[VarManager::kCosThetaCS], VarManager::fgValues[VarManager::kPhiCS], VarManager::fgValues[VarManager::kPhiTildeCS], VarManager::fgValues[VarManager::kCosThetaPP], VarManager::fgValues[VarManager::kPhiPP], VarManager::fgValues[VarManager::kPhiTildePP], VarManager::fgValues[VarManager::kCosThetaRM], - VarManager::fgValues[VarManager::kCosThetaStarTPC], VarManager::fgValues[VarManager::kCosThetaStarFT0A], VarManager::fgValues[VarManager::kCosThetaStarFT0C]); + VarManager::fgValues[VarManager::kCosThetaStarTPC], VarManager::fgValues[VarManager::kCosThetaStarFT0A], VarManager::fgValues[VarManager::kCosThetaStarFT0C], VarManager::fgValues[VarManager::kCosThetaStarRandom]); + dileptonEventInfoList(VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kVtxNcontrib], VarManager::fgValues[VarManager::kRandomPsi2], VarManager::fgValues[VarManager::kPsi2A], VarManager::fgValues[VarManager::kPsi2B], VarManager::fgValues[VarManager::kPsi2C]); } if constexpr (trackHasCov && TTwoProngFitter) { dielectronsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingLzProjected], VarManager::fgValues[VarManager::kVertexingLxyProjected]); - if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelPID) > 0) { - if (fConfigML.applyBDT) { - std::vector dqInputFeatures = fDQMlResponse.getInputFeatures(t1, t2, VarManager::fgValues); - - if (dqInputFeatures.empty()) { - LOG(fatal) << "Input features for ML selection are empty! Please check your configuration."; - return; - } - - int modelIndex = -1; - const auto& binsCent = fDQMlResponse.getBinsCent(); - const auto& binsPt = fDQMlResponse.getBinsPt(); - const std::string& centType = fDQMlResponse.getCentType(); - - if ("kCentFT0C" == centType) { - modelIndex = o2::aod::dqmlcuts::getMlBinIndex(VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kPt], binsCent, binsPt); - } else if ("kCentFT0A" == centType) { - modelIndex = o2::aod::dqmlcuts::getMlBinIndex(VarManager::fgValues[VarManager::kCentFT0A], VarManager::fgValues[VarManager::kPt], binsCent, binsPt); - } else if ("kCentFT0M" == centType) { - modelIndex = o2::aod::dqmlcuts::getMlBinIndex(VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kPt], binsCent, binsPt); - } else { - LOG(fatal) << "Unknown centrality estimation type: " << centType; - return; - } - - if (modelIndex < 0) { - LOG(info) << "Ml index is negative! This means that the centrality/pt is not in the range of the model bins."; - continue; - } + } + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelPID) > 0) { + isSelectedBDT = false; + fOutputMlPsi2ee.clear(); + if (fConfigML.applyBDT) { + std::vector dqInputFeatures = fDQMlResponse.getInputFeatures(t1, t2, dqtablereader_helpers::varValues()); + + if (dqInputFeatures.empty()) { + LOG(fatal) << "Input features for ML selection are empty! Please check your configuration."; + return; + } - LOG(debug) << "Model index: " << modelIndex << ", pT: " << VarManager::fgValues[VarManager::kPt] << ", centrality (kCentFT0C): " << VarManager::fgValues[VarManager::kCentFT0C]; - isSelectedBDT = fDQMlResponse.isSelectedMl(dqInputFeatures, modelIndex, fOutputMlPsi2ee); - VarManager::FillBdtScore(fOutputMlPsi2ee); // TODO: check if this is needed or not + int modelIndex = -1; + const auto& binsCent = fDQMlResponse.getBinsCent(); + const auto& binsPt = fDQMlResponse.getBinsPt(); + const std::string& centType = fDQMlResponse.getCentType(); + + if ("kCentFT0C" == centType) { + modelIndex = o2::aod::dqmlcuts::getMlBinIndex(VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kPt], binsCent, binsPt); + } else if ("kCentFT0A" == centType) { + modelIndex = o2::aod::dqmlcuts::getMlBinIndex(VarManager::fgValues[VarManager::kCentFT0A], VarManager::fgValues[VarManager::kPt], binsCent, binsPt); + } else if ("kCentFT0M" == centType) { + modelIndex = o2::aod::dqmlcuts::getMlBinIndex(VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kPt], binsCent, binsPt); + } else { + LOG(fatal) << "Unknown centrality estimation type: " << centType; + return; } - if (fConfigML.applyBDT && !isSelectedBDT) + if (modelIndex < 0) { + LOG(debug) << "Ml index is negative! This means that the centrality/pt is not in the range of the model bins."; continue; + } + + LOG(debug) << "Model index: " << modelIndex << ", pT: " << VarManager::fgValues[VarManager::kPt] << ", centrality (kCentFT0C): " << VarManager::fgValues[VarManager::kCentFT0C]; + isSelectedBDT = fDQMlResponse.isSelectedMl(dqInputFeatures, modelIndex, fOutputMlPsi2ee); + VarManager::FillBdtScore(fOutputMlPsi2ee); + } + + if (fConfigML.applyBDT && !isSelectedBDT) { + continue; + } - if (fConfigOptions.flatTables.value) { - dielectronAllList(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), twoTrackFilter, dileptonMcDecision, - t1.pt(), t1.eta(), t1.phi(), t1.itsClusterMap(), t1.itsChi2NCl(), t1.tpcNClsCrossedRows(), t1.tpcNClsFound(), t1.tpcChi2NCl(), t1.dcaXY(), t1.dcaZ(), t1.tpcSignal(), t1.tpcNSigmaEl(), t1.tpcNSigmaPi(), t1.tpcNSigmaPr(), t1.beta(), t1.tofNSigmaEl(), t1.tofNSigmaPi(), t1.tofNSigmaPr(), - t2.pt(), t2.eta(), t2.phi(), t2.itsClusterMap(), t2.itsChi2NCl(), t2.tpcNClsCrossedRows(), t2.tpcNClsFound(), t2.tpcChi2NCl(), t2.dcaXY(), t2.dcaZ(), t2.tpcSignal(), t2.tpcNSigmaEl(), t2.tpcNSigmaPi(), t2.tpcNSigmaPr(), t2.beta(), t2.tofNSigmaEl(), t2.tofNSigmaPi(), t2.tofNSigmaPr(), - VarManager::fgValues[VarManager::kKFTrack0DCAxyz], VarManager::fgValues[VarManager::kKFTrack1DCAxyz], VarManager::fgValues[VarManager::kKFDCAxyzBetweenProngs], VarManager::fgValues[VarManager::kKFTrack0DCAxy], VarManager::fgValues[VarManager::kKFTrack1DCAxy], VarManager::fgValues[VarManager::kKFDCAxyBetweenProngs], - VarManager::fgValues[VarManager::kKFTrack0DeviationFromPV], VarManager::fgValues[VarManager::kKFTrack1DeviationFromPV], VarManager::fgValues[VarManager::kKFTrack0DeviationxyFromPV], VarManager::fgValues[VarManager::kKFTrack1DeviationxyFromPV], - VarManager::fgValues[VarManager::kKFMass], VarManager::fgValues[VarManager::kKFChi2OverNDFGeo], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], VarManager::fgValues[VarManager::kVertexingLxy], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kKFCosPA], VarManager::fgValues[VarManager::kKFJpsiDCAxyz], VarManager::fgValues[VarManager::kKFJpsiDCAxy], - VarManager::fgValues[VarManager::kKFPairDeviationFromPV], VarManager::fgValues[VarManager::kKFPairDeviationxyFromPV], - VarManager::fgValues[VarManager::kKFMassGeoTop], - VarManager::fgValues[VarManager::kKFChi2OverNDFGeoTop], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingLzProjected], VarManager::fgValues[VarManager::kVertexingLxyProjected]); + if (fConfigOptions.flatTables.value) { + dielectronAllList(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), twoTrackFilter, dileptonMcDecision, + t1.pt(), t1.eta(), t1.phi(), t1.itsClusterMap(), t1.itsChi2NCl(), t1.tpcNClsCrossedRows(), t1.tpcNClsFound(), t1.tpcChi2NCl(), t1.dcaXY(), t1.dcaZ(), t1.tpcSignal(), t1.tpcNSigmaEl(), t1.tpcNSigmaPi(), t1.tpcNSigmaPr(), t1.beta(), t1.tofNSigmaEl(), t1.tofNSigmaPi(), t1.tofNSigmaPr(), + t2.pt(), t2.eta(), t2.phi(), t2.itsClusterMap(), t2.itsChi2NCl(), t2.tpcNClsCrossedRows(), t2.tpcNClsFound(), t2.tpcChi2NCl(), t2.dcaXY(), t2.dcaZ(), t2.tpcSignal(), t2.tpcNSigmaEl(), t2.tpcNSigmaPi(), t2.tpcNSigmaPr(), t2.beta(), t2.tofNSigmaEl(), t2.tofNSigmaPi(), t2.tofNSigmaPr(), + VarManager::fgValues[VarManager::kKFTrack0DCAxyz], VarManager::fgValues[VarManager::kKFTrack1DCAxyz], VarManager::fgValues[VarManager::kKFDCAxyzBetweenProngs], VarManager::fgValues[VarManager::kKFTrack0DCAxy], VarManager::fgValues[VarManager::kKFTrack1DCAxy], VarManager::fgValues[VarManager::kKFDCAxyBetweenProngs], + VarManager::fgValues[VarManager::kKFTrack0DeviationFromPV], VarManager::fgValues[VarManager::kKFTrack1DeviationFromPV], VarManager::fgValues[VarManager::kKFTrack0DeviationxyFromPV], VarManager::fgValues[VarManager::kKFTrack1DeviationxyFromPV], + VarManager::fgValues[VarManager::kKFMass], VarManager::fgValues[VarManager::kKFChi2OverNDFGeo], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], VarManager::fgValues[VarManager::kVertexingLxy], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kKFCosPA], VarManager::fgValues[VarManager::kKFJpsiDCAxyz], VarManager::fgValues[VarManager::kKFJpsiDCAxy], + VarManager::fgValues[VarManager::kKFPairDeviationFromPV], VarManager::fgValues[VarManager::kKFPairDeviationxyFromPV], + VarManager::fgValues[VarManager::kKFMassGeoTop], + VarManager::fgValues[VarManager::kKFChi2OverNDFGeoTop], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingLzProjected], VarManager::fgValues[VarManager::kVertexingLxyProjected]); + dielectronMlList(VarManager::fgValues[VarManager::kCentFT0C], + (fConfigML.applyBDT && !fOutputMlPsi2ee.empty()) ? VarManager::fgValues[VarManager::kBdtBackground] : -999.f, + (fConfigML.applyBDT && fOutputMlPsi2ee.size() > 1) ? VarManager::fgValues[VarManager::kBdtPrompt] : -999.f, + (fConfigML.applyBDT && fOutputMlPsi2ee.size() > 2) ? VarManager::fgValues[VarManager::kBdtNonprompt] : -999.f); + } + constexpr bool eventHasVtxCovForTable = ((TEventFillMap & VarManager::ObjTypes::ReducedEventVtxCov) > 0); + if constexpr (trackHasCov && TTwoProngFitter && eventHasVtxCovForTable) { + const bool ambiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + const bool ambiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + // kVertexingProcCode == 0: the fit failed; FillPairVertexing then returns early without updating the + // projected / pseudo-proper / pointing-angle variables, so they would be stale -> skip the pair + if (t1.sign() * t2.sign() < 0) { + PromptNonPromptSepTable(twoTrackFilter, dileptonMcDecision, + VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVertexingTauxyErr], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingLxyProjected], VarManager::fgValues[VarManager::kVertexingLxyOverErr], VarManager::fgValues[VarManager::kVertexingLxyz], VarManager::fgValues[VarManager::kVertexingLxyzOverErr], + VarManager::fgValues[VarManager::kVertexingChi2PCA], VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], + t1.pt(), t1.eta(), VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kDCAxyOverSig1], VarManager::fgValues[VarManager::kDCAzOverSig1], + VarManager::fgValues[VarManager::kITSclusterMap1], t1.itsChi2NCl(), + t2.pt(), t2.eta(), VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kDCAxyOverSig2], VarManager::fgValues[VarManager::kDCAzOverSig2], + VarManager::fgValues[VarManager::kITSclusterMap2], t2.itsChi2NCl(), + ambiInBunch, ambiOutOfBunch, true, + VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kVtxNcontribReal]); } } } @@ -1916,10 +2295,12 @@ struct AnalysisSameEventPairing { auto t1 = a1.template reducedmuon_as(); auto t2 = a2.template reducedmuon_as(); - if (t1.matchMCHTrackId() == t2.matchMCHTrackId() && t1.matchMCHTrackId() >= 0) + if (t1.matchMCHTrackId() == t2.matchMCHTrackId() && t1.matchMCHTrackId() >= 0) { continue; - if (t1.matchMFTTrackId() == t2.matchMFTTrackId() && t1.matchMFTTrackId() >= 0) + } + if (t1.matchMFTTrackId() == t2.matchMFTTrackId() && t1.matchMFTTrackId() >= 0) { continue; + } sign1 = t1.sign(); sign2 = t2.sign(); // store the ambiguity number of the two dilepton legs in the last 4 digits of the two-track filter @@ -1942,7 +2323,7 @@ struct AnalysisSameEventPairing { VarManager::FillPairCollision(event, t1, t2); } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); } if constexpr (eventHasQvector) { VarManager::FillPairVn(t1, t2); @@ -1955,52 +2336,51 @@ struct AnalysisSameEventPairing { VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), twoTrackFilter, 0); if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { - dileptonInfoList(t1.collisionId(), event.posX(), event.posY(), event.posZ()); + dileptonInfoList(event.collisionId(), event.posX(), event.posY(), event.posZ()); } - if constexpr (TTwoProngFitter) { - dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); - if (fConfigOptions.flatTables.value) { - dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), - event.selection_raw(), evSel, - -999., -999., -999., - VarManager::fgValues[VarManager::kMass], - false, - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], - VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], - VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], - VarManager::fgValues[VarManager::kCosPointingAngle], - VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), - VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), - t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), - 0., 0., - t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), - t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), - t1.chi2(), t2.chi2(), - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999., -999., - (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), - true, true, - VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kU3Q3], - VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kCentFT0C], - VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kCos3DeltaPhi], - VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], VarManager::fgValues[VarManager::kMultDimuons], - VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); - } - if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { - if constexpr (eventHasQvector == true || eventHasQvectorCentr == true) { - dileptonFlowList(t1.collisionId(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kCentFT0C], - VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), isFirst, - VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kR2SP_AC], VarManager::fgValues[VarManager::kR2SP_BC], - VarManager::fgValues[VarManager::kU3Q3], VarManager::fgValues[VarManager::kR3SP], - VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2EP_AC], VarManager::fgValues[VarManager::kR2EP_BC], - VarManager::fgValues[VarManager::kCos3DeltaPhi], VarManager::fgValues[VarManager::kR3EP], - VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], - VarManager::fgValues[VarManager::kCORR2REF], VarManager::fgValues[VarManager::kCORR4REF], VarManager::fgValues[VarManager::kM11REF], VarManager::fgValues[VarManager::kM1111REF], - VarManager::fgValues[VarManager::kMultDimuons], VarManager::fgValues[VarManager::kMultA]); - } + dimuonsExtraList(t1.globalIndex(), t2.globalIndex(), VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingLz], VarManager::fgValues[VarManager::kVertexingLxy]); + if (fConfigOptions.flatTables.value) { + dimuonAllList(event.posX(), event.posY(), event.posZ(), event.numContrib(), + event.selection_raw(), evSel, + -999., -999., -999., + VarManager::fgValues[VarManager::kMass], + false, + VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), VarManager::fgValues[VarManager::kVertexingChi2PCA], + VarManager::fgValues[VarManager::kVertexingTauz], VarManager::fgValues[VarManager::kVertexingTauzErr], + VarManager::fgValues[VarManager::kVertexingTauxy], VarManager::fgValues[VarManager::kVertexingTauxyErr], + VarManager::fgValues[VarManager::kCosPointingAngle], + VarManager::fgValues[VarManager::kPt1], VarManager::fgValues[VarManager::kEta1], VarManager::fgValues[VarManager::kPhi1], t1.sign(), + VarManager::fgValues[VarManager::kPt2], VarManager::fgValues[VarManager::kEta2], VarManager::fgValues[VarManager::kPhi2], t2.sign(), + t1.fwdDcaX(), t1.fwdDcaY(), t2.fwdDcaX(), t2.fwdDcaY(), + 0., 0., + t1.chi2MatchMCHMID(), t2.chi2MatchMCHMID(), + t1.chi2MatchMCHMFT(), t2.chi2MatchMCHMFT(), + t1.chi2(), t2.chi2(), + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999., -999., + (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)), (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)), + true, true, + VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kU3Q3], + VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kCentFT0C], + VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kCos3DeltaPhi], + VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], VarManager::fgValues[VarManager::kMultDimuons], + VarManager::fgValues[VarManager::kVertexingPz], VarManager::fgValues[VarManager::kVertexingSV]); + } + + if constexpr ((TTrackFillMap & VarManager::ObjTypes::ReducedMuonCollInfo) > 0) { + if constexpr (eventHasQvector || eventHasQvectorCentr) { + dileptonFlowList(event.collisionId(), VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kCentFT0C], + VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kPhi], t1.sign() + t2.sign(), isFirst, + VarManager::fgValues[VarManager::kU2Q2], VarManager::fgValues[VarManager::kR2SP_AB], VarManager::fgValues[VarManager::kR2SP_AC], VarManager::fgValues[VarManager::kR2SP_BC], + VarManager::fgValues[VarManager::kU3Q3], VarManager::fgValues[VarManager::kR3SP], + VarManager::fgValues[VarManager::kCos2DeltaPhi], VarManager::fgValues[VarManager::kR2EP_AB], VarManager::fgValues[VarManager::kR2EP_AC], VarManager::fgValues[VarManager::kR2EP_BC], + VarManager::fgValues[VarManager::kCos3DeltaPhi], VarManager::fgValues[VarManager::kR3EP], + VarManager::fgValues[VarManager::kCORR2POI], VarManager::fgValues[VarManager::kCORR4POI], VarManager::fgValues[VarManager::kM01POI], VarManager::fgValues[VarManager::kM0111POI], + VarManager::fgValues[VarManager::kCORR2REF], VarManager::fgValues[VarManager::kCORR4REF], VarManager::fgValues[VarManager::kM11REF], VarManager::fgValues[VarManager::kM1111REF], + VarManager::fgValues[VarManager::kMultDimuons], VarManager::fgValues[VarManager::kMultA]); } } if (t1.sign() != t2.sign()) { @@ -2020,8 +2400,9 @@ struct AnalysisSameEventPairing { bool isLeg2Ambi = false; bool isAmbiExtra = false; - if (fConfigML.applyBDT && !isSelectedBDT) + if (fConfigML.applyBDT && !isSelectedBDT) { continue; + } for (int icut = 0; icut < ncuts; icut++) { if (twoTrackFilter & (static_cast(1) << icut)) { @@ -2033,7 +2414,7 @@ struct AnalysisSameEventPairing { if constexpr (TPairType == VarManager::kDecayToEE) { if (isLeg1Ambi && isLeg2Ambi) { std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); - if (fAmbiguousPairs.find(iPair) != fAmbiguousPairs.end()) { + if (fAmbiguousPairs.contains(iPair)) { if (fAmbiguousPairs[iPair] & (static_cast(1) << icut)) { // if this pair is already stored with this cut isAmbiExtra = true; } else { @@ -2045,92 +2426,328 @@ struct AnalysisSameEventPairing { } } if (sign1 * sign2 < 0) { - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], isAmbiInBunch, isAmbiOutOfBunch, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); - if (fConfigAmbiguousMuonHistograms) { + if (fConfigQA) { + fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); + } + if (useMiniTree.fConfigMiniTree) { + auto t1 = a1.template reducedmuon_as(); + auto t2 = a2.template reducedmuon_as(); + + // By default (kPt1, kEta1, kPhi1) are for the positive charge + float dileptonMass = VarManager::fgValues[VarManager::kMass]; + if (dileptonMass > useMiniTree.fConfigMiniTreeMinMass && dileptonMass < useMiniTree.fConfigMiniTreeMaxMass) { + // In the miniTree the positive daughter is positioned as first + if (t1.sign() > 0) { + dileptonMiniTree(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kRap], + VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kCos2DeltaPhi], + t1.pt(), t1.eta(), t1.phi(), t2.pt(), t2.eta(), t2.phi()); + } else { + dileptonMiniTree(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kRap], + VarManager::fgValues[VarManager::kCentFT0C], VarManager::fgValues[VarManager::kCos2DeltaPhi], + t2.pt(), t2.eta(), t2.phi(), t1.pt(), t1.eta(), t1.phi()); + } + } + } + if (fConfigQA && fConfigAmbiguousMuonHistograms) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][3 + histIdxOffset].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + histIdxOffset].Data(), dqtablereader_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][3 + histIdxOffset + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + histIdxOffset + 3].Data(), dqtablereader_helpers::varValues()); } if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][3 + histIdxOffset + 6].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + histIdxOffset + 6].Data(), dqtablereader_helpers::varValues()); } } } if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s", fTrackCuts[icut].Data()), VarManager::fgValues); - if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_ambiguousextra_%s", fTrackCuts[icut].Data()), VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass(histNames[icut][0].Data(), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(histNames[icut][3].Data(), dqtablereader_helpers::varValues()); + } } } - } else { + } else if (fConfigQA) { if (sign1 > 0) { if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][1].Data(), dqtablereader_helpers::varValues()); if (fConfigAmbiguousMuonHistograms) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][4 + histIdxOffset].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + histIdxOffset].Data(), dqtablereader_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][4 + histIdxOffset + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + histIdxOffset + 3].Data(), dqtablereader_helpers::varValues()); } if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][4 + histIdxOffset + 6].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + histIdxOffset + 6].Data(), dqtablereader_helpers::varValues()); } } } if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s", fTrackCuts[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][1].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_ambiguousextra_%s", fTrackCuts[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4].Data(), dqtablereader_helpers::varValues()); } } } else { if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][2].Data(), dqtablereader_helpers::varValues()); if (fConfigAmbiguousMuonHistograms) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][5 + histIdxOffset].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + histIdxOffset].Data(), dqtablereader_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][5 + histIdxOffset + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + histIdxOffset + 3].Data(), dqtablereader_helpers::varValues()); } if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][5 + histIdxOffset + 6].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + histIdxOffset + 6].Data(), dqtablereader_helpers::varValues()); } } } if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s", fTrackCuts[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][2].Data(), dqtablereader_helpers::varValues()); if (isAmbiExtra) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_ambiguousextra_%s", fTrackCuts[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5].Data(), dqtablereader_helpers::varValues()); } } } } - for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { - AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!(cut.IsSelected(VarManager::fgValues))) // apply pair cuts - continue; - if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][0].Data(), VarManager::fgValues); - } else { - if (sign1 > 0) { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][1].Data(), VarManager::fgValues); + if (fConfigQA) { + for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { + AnalysisCompositeCut cut = fPairCuts.at(iPairCut); + if (!(cut.IsSelected(dqtablereader_helpers::varValues()))) { // apply pair cuts + continue; + } + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][0].Data(), dqtablereader_helpers::varValues()); } else { - fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][2].Data(), VarManager::fgValues); + if (sign1 > 0) { + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][1].Data(), dqtablereader_helpers::varValues()); + } else { + fHistMan->FillHistClass(histNames[ncuts + icut * ncuts + iPairCut][2].Data(), dqtablereader_helpers::varValues()); + } } - } - } // end loop (pair cuts) + } // end loop (pair cuts) + } } } // end loop (cuts) + + // edit + // rotation 20 times + if (fConfigTRPairs) { + if constexpr (TPairType == VarManager::kDecayToEE) { + twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; + + if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue + continue; + } + + auto t1 = a1.template reducedtrack_as(); + auto t2 = a2.template reducedtrack_as(); + sign1 = t1.sign(); + sign2 = t2.sign(); + if (t1.barrelAmbiguityInBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 28); + } + if (t2.barrelAmbiguityInBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 29); + } + if (t1.barrelAmbiguityOutOfBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 30); + } + if (t2.barrelAmbiguityOutOfBunch() > 1) { + twoTrackFilter |= (static_cast(1) << 31); + } + + for (int icut = 0; icut < ncuts; icut++) { + if (twoTrackFilter & (static_cast(1) << icut)) { + isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); + isUnambiguous = !(isAmbiInBunch || isAmbiOutOfBunch); + isLeg1Ambi = (twoTrackFilter & (static_cast(1) << 28) || (twoTrackFilter & (static_cast(1) << 30))); + isLeg2Ambi = (twoTrackFilter & (static_cast(1) << 29) || (twoTrackFilter & (static_cast(1) << 31))); + if constexpr (TPairType == VarManager::kDecayToEE) { + if (isLeg1Ambi && isLeg2Ambi) { + std::pair iPair(a1.reducedtrackId(), a2.reducedtrackId()); + if (fAmbiguousPairs.contains(iPair)) { + if (fAmbiguousPairs[iPair] & (static_cast(1) << icut)) { // if this pair is already stored with this cut + isAmbiExtra = true; + } else { + fAmbiguousPairs[iPair] |= static_cast(1) << icut; + } + } else { + fAmbiguousPairs[iPair] = static_cast(1) << icut; + } + } + } + if (sign1 * sign2 < 0) { + if (fConfigNRotations.value == 1) { + VarManager::FillPairRotation(t1, t2, fConfigNRotations.value); + if constexpr (TPairType == VarManager::kDecayToEE) { + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } else if (fConfigNRotations.value == 3) { + for (int irot = 1; irot <= fConfigNRotations.value; irot++) { + VarManager::FillPairRotation(t1, t2, irot); + if constexpr (TPairType == VarManager::kDecayToEE) { + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + if (isAmbiExtra) { + fHistMan->FillHistClass(Form("PairsBarrelTRPM_ambiguousextra_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } + } else { + LOGF(fatal, "Unsupported number of rotations: %d, only 1 and 3 are supported", fConfigNRotations.value); + } + } + } + } + } + } + // end } // end loop over pairs of track associations VarManager::fgValues[VarManager::kNPairsPerEvent] = fNPairPerEvent; if (fEnableBarrelHistos && fConfigQA) { - fHistMan->FillHistClass("PairingSEQA", VarManager::fgValues); + fHistMan->FillHistClass("PairingSEQA", dqtablereader_helpers::varValues()); + } + + if (fConfigRunMixingAcrossTFs) { + // run event mixing across TFs + if (mixingCategory < 0) { + continue; + } + // 1) create a MixingEvent and fill it with the relevant tracks + MixingHandler::MixingEvent mixingEvent; + uint32_t trackFilterForMixing = 0; + for (auto const& assoc : groupedAssocs) { + if constexpr (TPairType == VarManager::kDecayToEE) { + trackFilterForMixing = assoc.isBarrelSelected_raw() & assoc.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; + if (!trackFilterForMixing) { + continue; + } + auto t1 = assoc.template reducedtrack_as(); + MixingHandler::MixingTrack mixingTrack(t1.pt(), t1.eta(), t1.phi(), trackFilterForMixing); + if (t1.sign() > 0) { + mixingEvent.AddTrack1(mixingTrack); + } else { + mixingEvent.AddTrack2(mixingTrack); + } + } + } + if (mixingEvent.tracks1.empty() && mixingEvent.tracks2.empty()) { + continue; + } + // 2) run the mixing with the events in the pool corresponding to this event + auto& pool = fMixingHandler.GetPool(mixingCategory); + for (auto const& poolEvent : pool.GetEvents()) { + for (auto const& t1 : mixingEvent.tracks1) { + // run +- pairing + for (auto const& t2 : poolEvent.tracks2) { + // check the two-track filter for the mixed pair + uint32_t mixedTwoTrackFilter = t1.filteringFlags & t2.filteringFlags; + if (!mixedTwoTrackFilter) { + continue; + } + VarManager::FillPairMEAcrossTFs(t1, t2); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + if (fConfigTRPairs) { + // mixing event track should be rotated, so second parameter + if (fConfigNRotations.value != 1 && fConfigNRotations.value != 3) { + LOGF(fatal, "Unsupported number of rotations: %d, only 1 and 3 are supported", fConfigNRotations.value); + } + for (int irot = 1; irot <= fConfigNRotations.value; ++irot) { + VarManager::FillPairRotation_ME(t2, t1, irot); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + fHistMan->FillHistClass(Form("PairsBarrelTR_MEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } + } + // run ++ pairing + for (auto const& t2 : poolEvent.tracks1) { + // check the two-track filter for the mixed pair + uint32_t mixedTwoTrackFilter = t1.filteringFlags & t2.filteringFlags; + if (!mixedTwoTrackFilter) { + continue; + } + VarManager::FillPairMEAcrossTFs(t1, t2); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } + } + for (auto const& t1 : mixingEvent.tracks2) { + // run -+ pairing + for (auto const& t2 : poolEvent.tracks1) { + // check the two-track filter for the mixed pair + uint32_t mixedTwoTrackFilter = t1.filteringFlags & t2.filteringFlags; + if (!mixedTwoTrackFilter) { + continue; + } + VarManager::FillPairMEAcrossTFs(t1, t2); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + if (fConfigTRPairs) { + if (fConfigNRotations.value != 1 && fConfigNRotations.value != 3) { + LOGF(fatal, "Unsupported number of rotations: %d, only 1 and 3 are supported", fConfigNRotations.value); + } + for (int irot = 1; irot <= fConfigNRotations.value; ++irot) { + VarManager::FillPairRotation_ME(t2, t1, irot); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + fHistMan->FillHistClass(Form("PairsBarrelTR_MEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } + } + // run -- pairing + for (auto const& t2 : poolEvent.tracks2) { + // check the two-track filter for the mixed pair + uint32_t mixedTwoTrackFilter = t1.filteringFlags & t2.filteringFlags; + if (!mixedTwoTrackFilter) { + continue; + } + VarManager::FillPairMEAcrossTFs(t1, t2); + for (int icut = 0; icut < ncuts; icut++) { + if (mixedTwoTrackFilter & (static_cast(1) << icut)) { + if (fConfigQA) { + fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } + } + } + } + } + // 3) add the current event to the pool + pool.UpdatePool(mixingEvent, fMixingHandler.GetPoolDepth(), mixingEvent.filteringMask); + // pool.Print(); } } // end loop over events } @@ -2141,9 +2758,9 @@ struct AnalysisSameEventPairing { std::map> histNames = fTrackHistNames; int pairSign = 0; int ncuts = 0; - uint32_t twoTrackFilter = static_cast(0); - for (auto& a1 : assocs1) { - for (auto& a2 : assocs2) { + auto twoTrackFilter = static_cast(0); + for (auto const& a1 : assocs1) { + for (auto const& a2 : assocs2) { if constexpr (TPairType == VarManager::kDecayToEE) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; if (!twoTrackFilter) { // the tracks must have at least one filter bit in common to continue @@ -2169,14 +2786,19 @@ struct AnalysisSameEventPairing { } auto t1 = a1.template reducedmuon_as(); auto t2 = a2.template reducedmuon_as(); - if (t1.matchMCHTrackId() == t2.matchMCHTrackId() && t1.matchMCHTrackId() >= 0) + if (t1.matchMCHTrackId() == t2.matchMCHTrackId() && t1.matchMCHTrackId() >= 0) { continue; - if (t1.matchMFTTrackId() == t2.matchMFTTrackId() && t1.matchMCHTrackId() >= 0) + } + if (t1.matchMFTTrackId() == t2.matchMFTTrackId() && t1.matchMCHTrackId() >= 0) { continue; + } VarManager::FillPairME(t1, t2); if constexpr ((TEventFillMap & VarManager::ObjTypes::ReducedEventQvector) > 0) { VarManager::FillPairVn(t1, t2); } + if constexpr ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0) { + VarManager::FillPairVn(t1, t2); + } pairSign = t1.sign() + t2.sign(); // store the ambiguity number of the two dilepton legs in the last 4 digits of the two-track filter if (t1.muonAmbiguityInBunch() > 1) { @@ -2238,60 +2860,62 @@ struct AnalysisSameEventPairing { isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); isUnambiguous = !((twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)) || (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31))); - if (pairSign == 0) { - if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][3].Data(), VarManager::fgValues); - if (fConfigAmbiguousMuonHistograms) { - if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][15].Data(), VarManager::fgValues); - } - if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][18].Data(), VarManager::fgValues); - } - if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][21].Data(), VarManager::fgValues); - } - } - } - if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), VarManager::fgValues); - } - } else { - if (pairSign > 0) { + if (fConfigQA) { + if (pairSign == 0) { if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3].Data(), dqtablereader_helpers::varValues()); if (fConfigAmbiguousMuonHistograms) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][16].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][15].Data(), dqtablereader_helpers::varValues()); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][19].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][18].Data(), dqtablereader_helpers::varValues()); } if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][22].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][21].Data(), dqtablereader_helpers::varValues()); } } } if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelMEPM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); } } else { - if constexpr (TPairType == VarManager::kDecayToMuMu) { - fHistMan->FillHistClass(histNames[icut][5].Data(), VarManager::fgValues); - if (fConfigAmbiguousMuonHistograms) { - if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][17].Data(), VarManager::fgValues); - } - if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][20].Data(), VarManager::fgValues); + if (pairSign > 0) { + if constexpr (TPairType == VarManager::kDecayToMuMu) { + fHistMan->FillHistClass(histNames[icut][4].Data(), dqtablereader_helpers::varValues()); + if (fConfigAmbiguousMuonHistograms) { + if (isAmbiInBunch) { + fHistMan->FillHistClass(histNames[icut][16].Data(), dqtablereader_helpers::varValues()); + } + if (isAmbiOutOfBunch) { + fHistMan->FillHistClass(histNames[icut][19].Data(), dqtablereader_helpers::varValues()); + } + if (isUnambiguous) { + fHistMan->FillHistClass(histNames[icut][22].Data(), dqtablereader_helpers::varValues()); + } } - if (isUnambiguous) { - fHistMan->FillHistClass(histNames[icut][23].Data(), VarManager::fgValues); + } + if constexpr (TPairType == VarManager::kDecayToEE) { + fHistMan->FillHistClass(Form("PairsBarrelMEPP_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } + } else { + if constexpr (TPairType == VarManager::kDecayToMuMu) { + fHistMan->FillHistClass(histNames[icut][5].Data(), dqtablereader_helpers::varValues()); + if (fConfigAmbiguousMuonHistograms) { + if (isAmbiInBunch) { + fHistMan->FillHistClass(histNames[icut][17].Data(), dqtablereader_helpers::varValues()); + } + if (isAmbiOutOfBunch) { + fHistMan->FillHistClass(histNames[icut][20].Data(), dqtablereader_helpers::varValues()); + } + if (isUnambiguous) { + fHistMan->FillHistClass(histNames[icut][23].Data(), dqtablereader_helpers::varValues()); + } } } - } - if constexpr (TPairType == VarManager::kDecayToEE) { - fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), VarManager::fgValues); + if constexpr (TPairType == VarManager::kDecayToEE) { + fHistMan->FillHistClass(Form("PairsBarrelMEMM_%s", fTrackCuts[icut].Data()), dqtablereader_helpers::varValues()); + } } } } @@ -2302,14 +2926,20 @@ struct AnalysisSameEventPairing { // barrel-barrel and muon-muon event mixing template - void runSameSideMixing(TEvents& events, TAssocs const& assocs, TTracks const& tracks, Preslice& preSlice) + void runSameSideMixing(TEvents& events, TAssocs const& assocs, TTracks const& tracks, o2::framework::Preslice& preSlice) { + if (ResoFlowSP == nullptr || ResoFlowEP == nullptr) { + LOG(info) << "Flow resolution objects not set, flow will not be filled for mixed events"; + if (events.size() > 0) { + initParamsFromCCDB(events.begin().timestamp(), events.begin().runNumber(), false); + } + } events.bindExternalIndices(&assocs); int mixingDepth = fConfigMixingDepth.value; fAmbiguousPairs.clear(); - for (auto& [event1, event2] : selfCombinations(hashBin, mixingDepth, -1, events, events)) { + for (auto const& [event1, event2] : selfCombinations(hashBin, mixingDepth, -1, events, events)) { VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event1, VarManager::fgValues); + VarManager::FillEvent(event1, dqtablereader_helpers::varValues()); auto assocs1 = assocs.sliceBy(preSlice, event1.globalIndex()); assocs1.bindExternalIndices(&events); @@ -2318,16 +2948,20 @@ struct AnalysisSameEventPairing { assocs2.bindExternalIndices(&events); fNPairPerEvent = 0; + VarManager::FillTwoMixEvents(event1, event2, assocs1, assocs2); + if (fConfigOptions.useFlowReso) { + VarManager::FillTwoMixEventsFlowResoFactor(ResoFlowSP, ResoFlowEP); + } runMixedPairing(assocs1, assocs2, tracks, tracks); VarManager::fgValues[VarManager::kNPairsPerEvent] = fNPairPerEvent; if (fEnableBarrelMixingHistos && fConfigQA) { - fHistMan->FillHistClass("PairingMEQA", VarManager::fgValues); + fHistMan->FillHistClass("PairingMEQA", dqtablereader_helpers::varValues()); } } // end event loop } template - void runEmuSameEventPairing(TEvents const& events, Preslice& preslice1, TTrackAssocs const& assocs1, TTracks const& /*tracks1*/, Preslice& preslice2, TMuonAssocs const& assocs2, TMuons const& /*tracks2*/) + void runEmuSameEventPairing(TEvents const& events, o2::framework::Preslice& preslice1, TTrackAssocs const& assocs1, TTracks const& /*tracks1*/, o2::framework::Preslice& preslice2, TMuonAssocs const& assocs2, TMuons const& /*tracks2*/) { if (events.size() > 0) { if (fCurrentRun != events.begin().runNumber()) { @@ -2337,9 +2971,9 @@ struct AnalysisSameEventPairing { } const auto& histNames = fTrackMuonHistNames; - int nPairCuts = (fPairCuts.size() > 0) ? fPairCuts.size() : 1; + int nPairCuts = !fPairCuts.empty() ? static_cast(fPairCuts.size()) : 1; - electronmuonList.reserve(1); + electronmuonList.reserve(assocs1.size()); uint32_t twoTrackFilter = 0; int sign1 = 0; @@ -2348,29 +2982,39 @@ struct AnalysisSameEventPairing { constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::ReducedEventQvector) > 0); constexpr bool eventHasQvectorCentr = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); - for (auto& event : events) { - if (!event.isEventSelected_bit(0)) + for (auto const& event : events) { + if (!event.isEventSelected_bit(0)) { continue; - if (fConfigCuts.event && event.isEventSelected_bit(2)) + } + if (fConfigCuts.event && event.isEventSelected_bit(2)) { continue; + } VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + + // fill the event histograms + if (fConfigQA && fConfigCuts.event && fSplitCheckEnabled) { + fHistMan->FillHistClass("Event_AfterSplitVeto", dqtablereader_helpers::varValues()); + } auto groupedAssocs1 = assocs1.sliceBy(preslice1, event.globalIndex()); - if (groupedAssocs1.size() == 0) + if (groupedAssocs1.size() == 0) { continue; + } auto groupedAssocs2 = assocs2.sliceBy(preslice2, event.globalIndex()); - if (groupedAssocs2.size() == 0) + if (groupedAssocs2.size() == 0) { continue; + } // Custom combination policy - for (auto& [a1, a2] : o2::soa::combinations(soa::CombinationsFullIndexPolicy(groupedAssocs1, groupedAssocs2))) { - if (!(a1.isBarrelSelected_raw() & fTrackFilterMask)) + for (auto const& [a1, a2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(groupedAssocs1, groupedAssocs2))) { + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) { continue; - // if (!a1.isBarrelSelectedPrefilter_raw()) continue; - if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) + } + if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) { continue; + } auto t1 = a1.template reducedtrack_as(); auto t2 = a2.template reducedmuon_as(); @@ -2380,18 +3024,22 @@ struct AnalysisSameEventPairing { twoTrackFilter = 0; int minCuts = std::min(fNCutsBarrel, fNCutsMuon); for (int i = 0; i < minCuts; ++i) { - if ((a1.isBarrelSelected_raw() & (1u << i)) && (a2.isMuonSelected_raw() & (1u << i))) { + if ((a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & (1u << i)) && (a2.isMuonSelected_raw() & (1u << i))) { twoTrackFilter |= (1u << i); } } - if (t1.barrelAmbiguityInBunch() > 1) + if (t1.barrelAmbiguityInBunch() > 1) { twoTrackFilter |= (1u << 28); - if (t1.barrelAmbiguityOutOfBunch() > 1) + } + if (t1.barrelAmbiguityOutOfBunch() > 1) { twoTrackFilter |= (1u << 30); - if (t2.muonAmbiguityInBunch() > 1) + } + if (t2.muonAmbiguityInBunch() > 1) { twoTrackFilter |= (1u << 29); - if (t2.muonAmbiguityOutOfBunch() > 1) + } + if (t2.muonAmbiguityOutOfBunch() > 1) { twoTrackFilter |= (1u << 31); + } VarManager::FillPair(t1, t2); if (fConfigOptions.propTrack) { @@ -2409,30 +3057,36 @@ struct AnalysisSameEventPairing { t1.sign() + t2.sign(), twoTrackFilter, 0); for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { - if (!(a1.isBarrelSelected_raw() & (1u << iTrack))) + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & (1u << iTrack))) { continue; + } for (int iMuon = 0; iMuon < fNCutsMuon; ++iMuon) { - if (!(a2.isMuonSelected_raw() & (1u << iMuon))) + if (!(a2.isMuonSelected_raw() & (1u << iMuon))) { continue; + } for (unsigned int iPairCut = 0; iPairCut < (fPairCuts.empty() ? 1 : fPairCuts.size()); iPairCut++) { if (!fPairCuts.empty()) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!cut.IsSelected(VarManager::fgValues)) + if (!cut.IsSelected(dqtablereader_helpers::varValues())) { continue; + } } int index = iTrack * (fNCutsMuon * nPairCuts) + iMuon * nPairCuts + iPairCut; auto itHist = histNames.find(index); - if (itHist == histNames.end()) + if (itHist == histNames.end()) { continue; - if (sign1 * sign2 < 0) { // Opposite Sign - fHistMan->FillHistClass(itHist->second[0].Data(), VarManager::fgValues); - } else { // Like Sign - if (sign1 > 0) { - fHistMan->FillHistClass(itHist->second[1].Data(), VarManager::fgValues); - } else { - fHistMan->FillHistClass(itHist->second[2].Data(), VarManager::fgValues); + } + if (fConfigQA) { + if (sign1 * sign2 < 0) { // Opposite Sign + fHistMan->FillHistClass(itHist->second[0].Data(), dqtablereader_helpers::varValues()); + } else { // Like Sign + if (sign1 > 0) { + fHistMan->FillHistClass(itHist->second[1].Data(), dqtablereader_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHist->second[2].Data(), dqtablereader_helpers::varValues()); + } } } } // end pair cut loop @@ -2449,15 +3103,15 @@ struct AnalysisSameEventPairing { const auto& histNames = fTrackMuonHistNames; int sign1 = 0; int sign2 = 0; - int nPairCuts = (fPairCuts.size() > 0) ? fPairCuts.size() : 1; + int nPairCuts = !fPairCuts.empty() ? static_cast(fPairCuts.size()) : 1; constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::ReducedEventQvector) > 0); constexpr bool eventHasQvectorCentr = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); - for (auto& a1 : assocs1) { - if (!(a1.isBarrelSelected_raw() & fTrackFilterMask)) { + for (auto const& a1 : assocs1) { + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) { continue; } - for (auto& a2 : assocs2) { + for (auto const& a2 : assocs2) { if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) { continue; } @@ -2476,7 +3130,7 @@ struct AnalysisSameEventPairing { } for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { - if (!(a1.isBarrelSelected_raw() & (1u << iTrack))) { + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & (1u << iTrack))) { continue; } for (int iMuon = 0; iMuon < fNCutsMuon; ++iMuon) { @@ -2486,7 +3140,7 @@ struct AnalysisSameEventPairing { for (int iPairCut = 0; iPairCut < nPairCuts; ++iPairCut) { if (!fPairCuts.empty()) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!cut.IsSelected(VarManager::fgValues)) { + if (!cut.IsSelected(dqtablereader_helpers::varValues())) { continue; } } @@ -2495,13 +3149,15 @@ struct AnalysisSameEventPairing { if (itHist == histNames.end() || itHist->second.size() < 6) { continue; } - if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(itHist->second[3].Data(), VarManager::fgValues); - } else { - if (sign1 > 0) { - fHistMan->FillHistClass(itHist->second[4].Data(), VarManager::fgValues); + if (fConfigQA) { + if (sign1 * sign2 < 0) { + fHistMan->FillHistClass(itHist->second[3].Data(), dqtablereader_helpers::varValues()); } else { - fHistMan->FillHistClass(itHist->second[5].Data(), VarManager::fgValues); + if (sign1 > 0) { + fHistMan->FillHistClass(itHist->second[4].Data(), dqtablereader_helpers::varValues()); + } else { + fHistMan->FillHistClass(itHist->second[5].Data(), dqtablereader_helpers::varValues()); + } } } } // end pair cut loop @@ -2512,15 +3168,15 @@ struct AnalysisSameEventPairing { } template - void runEmuSameSideMixing(TEvents& events, Preslice& preslice1, TTrackAssocs const& assocs1, TTracks const& tracks1, - Preslice& preslice2, TMuonAssocs const& assocs2, TMuons const& tracks2) + void runEmuSameSideMixing(TEvents& events, o2::framework::Preslice& preslice1, TTrackAssocs const& assocs1, TTracks const& tracks1, + o2::framework::Preslice& preslice2, TMuonAssocs const& assocs2, TMuons const& tracks2) { events.bindExternalIndices(&assocs1); events.bindExternalIndices(&assocs2); int mixingDepth = fConfigMixingDepth.value; - for (auto& [event1, event2] : selfCombinations(hashBin, mixingDepth, -1, events, events)) { + for (auto const& [event1, event2] : selfCombinations(hashBin, mixingDepth, -1, events, events)) { VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event1, VarManager::fgValues); + VarManager::FillEvent(event1, dqtablereader_helpers::varValues()); auto groupedAssocs1 = assocs1.sliceBy(preslice1, event1.globalIndex()); groupedAssocs1.bindExternalIndices(&events); @@ -2539,8 +3195,8 @@ struct AnalysisSameEventPairing { } void processAllSkimmed(MyEventsVtxCovSelected const& events, - soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); @@ -2548,118 +3204,131 @@ struct AnalysisSameEventPairing { } void processBarrelOnlySkimmed(MyEventsVtxCovSelected const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } void processBarrelOnlySkimmedFlow(MyEventsVtxCovSelectedQvector const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithAmbiguities const& barrelTracks) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } void processBarrelOnlySkimmedNoCov(MyEventsSelected const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithAmbiguities const& barrelTracks) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } void processBarrelOnlySkimmedNoCovWithMultExtra(MyEventsMultExtraSelected const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithAmbiguities const& barrelTracks) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } - void processBarrelOnlyWithCollSkimmed(MyEventsVtxCovSelected const& events, - soa::Join const& barrelAssocs, + void processBarrelOnlyWithCollSkimmed(MyEventsVtxCovSelectedInfo const& events, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguitiesWithColl const& barrelTracks) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } void processBarrelOnlyWithQvectorCentrSkimmedNoCov(MyEventsQvectorCentrSelected const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithAmbiguities const& barrelTracks) { runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } + void processBarrelOnlyWithQvectorCentrSkimmed(MyEventsQvectorCentrSelected const& events, + o2::soa::Join const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) + { + runSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); + } + void processMuonOnlySkimmed(MyEventsVtxCovSelected const& events, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); } void processMuonOnlySkimmedMultExtra(MyEventsVtxCovSelectedMultExtra const& events, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); } void processMuonOnlySkimmedFlow(MyEventsQvectorCentrSelected const& events, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + { + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + } + + void processMuonOnlyVertexingSkimmed(MyEventsVtxCovSelected const& events, + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); + runSameEventPairing(events, muonAssocsPerCollision, muonAssocs, muons); } void processElectronMuonSkimmed(MyEventsVtxCovSelected const& events, - soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks, + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runEmuSameEventPairing(events, trackEmuAssocsPerCollision, barrelAssocs, barrelTracks, muonAssocsPerCollision, muonAssocs, muons); + runEmuSameEventPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, muonAssocsPerCollision, muonAssocs, muons); } - void processMixingAllSkimmed(soa::Filtered& events, - soa::Join const& trackAssocs, MyBarrelTracksWithCov const& tracks, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + void processMixingAllSkimmed(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Join const& trackAssocs, MyBarrelTracksWithCov const& tracks, + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { runSameSideMixing(events, trackAssocs, tracks, trackAssocsPerCollision); runSameSideMixing(events, muonAssocs, muons, muonAssocsPerCollision); } - void processMixingBarrelSkimmed(soa::Filtered& events, - soa::Join const& trackAssocs, aod::ReducedTracks const& tracks) + void processMixingBarrelSkimmed(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Join const& trackAssocs, o2::aod::ReducedTracks const& tracks) { runSameSideMixing(events, trackAssocs, tracks, trackAssocsPerCollision); } - void processMixingBarrelSkimmedFlow(soa::Filtered& events, - soa::Join const& trackAssocs, aod::ReducedTracks const& tracks) + void processMixingBarrelSkimmedFlow(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Join const& trackAssocs, o2::aod::ReducedTracks const& tracks) { runSameSideMixing(events, trackAssocs, tracks, trackAssocsPerCollision); } - void processMixingBarrelWithQvectorCentrSkimmedNoCov(soa::Filtered& events, - soa::Join const& trackAssocs, MyBarrelTracksWithAmbiguities const& tracks) + void processMixingBarrelWithQvectorCentrSkimmedNoCov(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Join const& trackAssocs, MyBarrelTracksWithAmbiguities const& tracks) { runSameSideMixing(events, trackAssocs, tracks, trackAssocsPerCollision); } - void processMixingMuonSkimmed(soa::Filtered& events, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + void processMixingMuonSkimmed(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { runSameSideMixing(events, muonAssocs, muons, muonAssocsPerCollision); } - void processMixingMuonSkimmedFlow(soa::Filtered& events, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + void processMixingMuonSkimmedFlow(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { runSameSideMixing(events, muonAssocs, muons, muonAssocsPerCollision); } - void processMixingElectronMuonSkimmed(soa::Filtered& events, - soa::Join const& barrelAssocs, aod::ReducedTracks const& barrelTracks, - soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) + void processMixingElectronMuonSkimmed(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Join const& barrelAssocs, o2::aod::ReducedTracks const& barrelTracks, + o2::soa::Join const& muonAssocs, MyMuonTracksWithCovWithAmbiguities const& muons) { - runEmuSameSideMixing(events, trackEmuAssocsPerCollision, barrelAssocs, barrelTracks, muonAssocsPerCollision, muonAssocs, muons); + runEmuSameSideMixing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, muonAssocsPerCollision, muonAssocs, muons); } - void processDummy(MyEventsBasic&) + void processDummy(MyEventsBasic const&) { // do nothing } @@ -2670,10 +3339,12 @@ struct AnalysisSameEventPairing { PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmedNoCov, "Run barrel only pairing (no covariances), with skimmed tracks and with collision information", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmedNoCovWithMultExtra, "Run barrel only pairing (no covariances), with skimmed tracks, with collision information, with MultsExtra", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithQvectorCentrSkimmedNoCov, "Run barrel only pairing (no covariances), with skimmed tracks, with Qvector from central framework", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithQvectorCentrSkimmed, "Run barrel only pairing (no covariances), with skimmed tracks, with collision information, with Qvector from central framework", false); PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlySkimmedFlow, "Run barrel only pairing, with skimmed tracks and with flow", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmed, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedMultExtra, "Run muon only pairing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlySkimmedFlow, "Run muon only pairing, with skimmed tracks and flow", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processMuonOnlyVertexingSkimmed, "Run muon only pairing with two-prong vertexing, with skimmed tracks", false); PROCESS_SWITCH(AnalysisSameEventPairing, processElectronMuonSkimmed, "Run electron-muon pairing, with skimmed tracks/muons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMixingAllSkimmed, "Run all types of mixed pairing, with skimmed tracks/muons", false); PROCESS_SWITCH(AnalysisSameEventPairing, processMixingBarrelSkimmed, "Run barrel type mixing pairing, with skimmed tracks", false); @@ -2688,76 +3359,76 @@ struct AnalysisSameEventPairing { // Run pairing for resonance with legs fulfilling separate cuts (asymmetric decay channel) struct AnalysisAsymmetricPairing { - Produces ditrackList; - Produces ditrackExtraList; + o2::framework::Produces ditrackList; + o2::framework::Produces ditrackExtraList; o2::base::MatLayerCylSet* fLUT = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. // Output objects - OutputObj fOutputList{"output"}; + o2::framework::OutputObj fOutputList{"output"}; // Configurables - Configurable fConfigLegCuts{"cfgLegCuts", "", ":[:],[:[:],...]"}; - Configurable fConfigLegAFilterMask{"cfgLegAFilterMask", 0, "Filter mask corresponding to cuts in track-selection"}; - Configurable fConfigLegBFilterMask{"cfgLegBFilterMask", 0, "Filter mask corresponding to cuts in track-selection"}; - Configurable fConfigLegCFilterMask{"cfgLegCFilterMask", 0, "Filter mask corresponding to cuts in track-selection"}; - Configurable fConfigCommonTrackCuts{"cfgCommonTrackCuts", "", "Comma separated list of cuts to be applied to all legs"}; - Configurable fConfigPairCuts{"cfgPairCuts", "", "Comma separated list of pair cuts"}; - Configurable fConfigPairCutsJSON{"cfgPairCutsJSON", "", "Additional list of pair cuts in JSON format"}; - Configurable fConfigRemoveCollSplittingCandidates{"cfgRemoveCollSplittingCandidates", false, "If true, remove collision splitting candidates as determined by the event selection task upstream"}; - Configurable fConfigSkipAmbiguousIdCombinations{"cfgSkipAmbiguousIdCombinations", true, "Choose whether to skip pairs/triples which pass a stricter combination of cuts, e.g. KKPi triplets for D+ -> KPiPi"}; - - Configurable fConfigHistogramSubgroups{"cfgAsymmetricPairingHistogramsSubgroups", "barrel,vertexing", "Comma separated list of asymmetric-pairing histogram subgroups"}; - Configurable fConfigSameSignHistograms{"cfgSameSignHistograms", false, "Include same sign pair histograms for 2-prong decays"}; - Configurable fConfigAmbiguousHistograms{"cfgAmbiguousHistograms", false, "Include separate histograms for pairs/triplets with ambiguous tracks"}; - Configurable fConfigReflectedHistograms{"cfgReflectedHistograms", false, "Include separate histograms for pairs which are reflections of previously counted pairs"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable fConfigGRPMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Choose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; - - Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; - Configurable fConfigUseAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; - Configurable fConfigPropToPCA{"cfgPropToPCA", false, "Propagate tracks to secondary vertex"}; - Configurable fConfigLutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - - Service fCCDB; - - HistogramManager* fHistMan; + o2::framework::Configurable fConfigLegCuts{"cfgLegCuts", "", ":[:],[:[:],...]"}; + o2::framework::Configurable fConfigLegAFilterMask{"cfgLegAFilterMask", 0, "o2::framework::expressions::Filter mask corresponding to cuts in track-selection"}; + o2::framework::Configurable fConfigLegBFilterMask{"cfgLegBFilterMask", 0, "o2::framework::expressions::Filter mask corresponding to cuts in track-selection"}; + o2::framework::Configurable fConfigLegCFilterMask{"cfgLegCFilterMask", 0, "o2::framework::expressions::Filter mask corresponding to cuts in track-selection"}; + o2::framework::Configurable fConfigCommonTrackCuts{"cfgCommonTrackCuts", "", "Comma separated list of cuts to be applied to all legs"}; + o2::framework::Configurable fConfigPairCuts{"cfgPairCuts", "", "Comma separated list of pair cuts"}; + o2::framework::Configurable fConfigPairCutsJSON{"cfgPairCutsJSON", "", "Additional list of pair cuts in JSON format"}; + o2::framework::Configurable fConfigRemoveCollSplittingCandidates{"cfgRemoveCollSplittingCandidates", false, "If true, remove collision splitting candidates as determined by the event selection task upstream"}; + o2::framework::Configurable fConfigSkipAmbiguousIdCombinations{"cfgSkipAmbiguousIdCombinations", true, "Choose whether to skip pairs/triples which pass a stricter combination of cuts, e.g. KKPi triplets for D+ -> KPiPi"}; + + o2::framework::Configurable fConfigHistogramSubgroups{"cfgAsymmetricPairingHistogramsSubgroups", "barrel,vertexing", "Comma separated list of asymmetric-pairing histogram subgroups"}; + o2::framework::Configurable fConfigSameSignHistograms{"cfgSameSignHistograms", false, "Include same sign pair histograms for 2-prong decays"}; + o2::framework::Configurable fConfigAmbiguousHistograms{"cfgAmbiguousHistograms", false, "Include separate histograms for pairs/triplets with ambiguous tracks"}; + o2::framework::Configurable fConfigReflectedHistograms{"cfgReflectedHistograms", false, "Include separate histograms for pairs which are reflections of previously counted pairs"}; + o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + + o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable fConfigGRPMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + + o2::framework::Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; + o2::framework::Configurable fConfigUseAbsDCA{"cfgUseAbsDCA", false, "Use absolute DCA minimization instead of chi^2 minimization in secondary vertexing"}; + o2::framework::Configurable fConfigLutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + o2::framework::Configurable fConfigFetchInteractionRate{"cfgFetchInteractionRate", false, "Fetch event-wise interaction rate from the CCDB"}; + o2::framework::Configurable fConfigIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + + o2::framework::Service fCCDB{}; + o2::ctpRateFetcher rateFetcher; + + HistogramManager* fHistMan = nullptr; std::vector fPairCuts; - int fNPairHistPrefixes; + int fNPairHistPrefixes = 0; - // Filter masks to find legs in BarrelTrackCuts table - uint32_t fLegAFilterMask; - uint32_t fLegBFilterMask; - uint32_t fLegCFilterMask; + // o2::framework::expressions::Filter masks to find legs in BarrelTrackCuts table + uint32_t fLegAFilterMask = 0; + uint32_t fLegBFilterMask = 0; + uint32_t fLegCFilterMask = 0; // Maps tracking which combination of leg cuts the track cuts participate in std::map fConstructedLegAFilterMasksMap; std::map fConstructedLegBFilterMasksMap; std::map fConstructedLegCFilterMasksMap; - // Filter map for common track cuts - uint32_t fCommonTrackCutMask; + // o2::framework::expressions::Filter map for common track cuts + uint32_t fCommonTrackCutMask = 0; // Map tracking which common track cut the track cuts correspond to std::map fCommonTrackCutFilterMasks; - int fNLegCuts; - int fNPairCuts; - int fNCommonTrackCuts; + int fNLegCuts = 0; + int fNPairCuts = 0; + int fNCommonTrackCuts = 0; // vectors for cut names and signal names, for easy access when calling FillHistogramList() std::vector fLegCutNames; std::vector fPairCutNames; std::vector fCommonCutNames; - Preslice> trackAssocsPerCollision = aod::reducedtrack_association::reducedeventId; + o2::framework::Preslice> trackAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; // Partitions for triplets and asymmetric pairs - Partition> legACandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegAFilterMask) > static_cast(0); - Partition> legBCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegBFilterMask) > static_cast(0); - Partition> legCCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegCFilterMask) > static_cast(0); + o2::framework::Partition> legACandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegAFilterMask) > static_cast(0); + o2::framework::Partition> legBCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegBFilterMask) > static_cast(0); + o2::framework::Partition> legCCandidateAssocs = (o2::aod::dqanalysisflags::isBarrelSelected & fConfigLegCFilterMask) > static_cast(0); // Map to track how many times a pair of tracks has been encountered std::map, int8_t> fPairCount; @@ -2771,7 +3442,7 @@ struct AnalysisAsymmetricPairing { VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); // Get the leg cut filter maps fLegAFilterMask = fConfigLegAFilterMask.value; @@ -2782,15 +3453,15 @@ struct AnalysisAsymmetricPairing { if (!cutNamesStr.IsNull()) { std::unique_ptr objArray(cutNamesStr.Tokenize(",")); for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - fPairCuts.push_back(dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); + fPairCuts.push_back(o2::aod::dqcuts::GetCompositeCut(objArray->At(icut)->GetName())); } } // Extra pair cuts via JSON TString addPairCutsStr = fConfigPairCutsJSON.value; if (addPairCutsStr != "") { - std::vector addPairCuts = dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); - for (auto& t : addPairCuts) { - fPairCuts.push_back(reinterpret_cast(t)); + std::vector addPairCuts = o2::aod::dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); + for (auto const& t : addPairCuts) { + fPairCuts.push_back(dynamic_cast(t)); cutNamesStr += Form(",%s", t->GetName()); } } @@ -2800,21 +3471,21 @@ struct AnalysisAsymmetricPairing { fPairCutNames.push_back(objArrayPairCuts->At(j)->GetName()); } // Get the barrel track selection cuts - string tempCuts; - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); + std::string tempCuts; + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", tempCuts, false); TString tempCutsStr = tempCuts; // check also the cuts added via JSON and add them to the string of cuts - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", tempCuts, false); TString addTrackCutsStr = tempCuts; if (addTrackCutsStr != "") { - std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); - for (auto& t : addTrackCuts) { + std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); + for (auto const& t : addTrackCuts) { tempCutsStr += Form(",%s", t->GetName()); } } std::unique_ptr objArray(tempCutsStr.Tokenize(",")); // Get the common leg cuts - int commonCutIdx; + int commonCutIdx = -1; TString commonNamesStr = fConfigCommonTrackCuts.value; if (!commonNamesStr.IsNull()) { // if common track cuts std::unique_ptr objArrayCommon(commonNamesStr.Tokenize(",")); @@ -2852,9 +3523,9 @@ struct AnalysisAsymmetricPairing { } fNLegCuts = objArrayLegs->GetEntries(); std::vector isThreeProng; - int legAIdx; - int legBIdx; - int legCIdx; + int legAIdx = -1; + int legBIdx = -1; + int legCIdx = -1; // Loop over leg defining cuts for (int icut = 0; icut < fNLegCuts; ++icut) { TString legsStr = objArrayLegs->At(icut)->GetName(); @@ -2989,54 +3660,38 @@ struct AnalysisAsymmetricPairing { fLUT = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigLutPath)); VarManager::SetupMatLUTFwdDCAFitter(fLUT); - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON - VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + VarManager::SetUseVars(fHistMan->GetUsedVars()); // provide the list of required variables so that VarManager knows what to fill fOutputList.setObject(fHistMan->GetMainHistogramList()); } void initParamsFromCCDB(uint64_t timestamp, bool isTriplets) { - if (fConfigUseRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPMagPath, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); + } else { + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (isTriplets) { + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - } else { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); } } else { - if (isTriplets) { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); - } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupTwoProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables - } + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigUseAbsDCA.value); // TODO: get these parameters from Configurables } } } // Template function to run same event pairing with asymmetric pairs (e.g. kaon-pion) template - void runAsymmetricPairing(TEvents const& events, Preslice& preslice, TTrackAssocs const& /*assocs*/, TTracks const& /*tracks*/) + void runAsymmetricPairing(TEvents const& events, o2::framework::Preslice& preslice, TTrackAssocs const& /*assocs*/, TTracks const& /*tracks*/) { fPairCount.clear(); @@ -3049,12 +3704,32 @@ struct AnalysisAsymmetricPairing { int sign1 = 0; int sign2 = 0; - ditrackList.reserve(1); - ditrackExtraList.reserve(1); + + // Reserve capacity for the output tables + int64_t reserveSize = 0; + for (auto const& event : events) { + auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); + auto groupedLegBAssocs = legBCandidateAssocs.sliceBy(preslice, event.globalIndex()); + size_t nGoodA = 0; + size_t nGoodB = 0; + for (auto const& t : groupedLegAAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodA++; + } + } + for (auto const& t : groupedLegBAssocs) { + if (t.isBarrelSelected_raw()) { + nGoodB++; + } + } + reserveSize += nGoodA * nGoodB; + } + ditrackList.reserve(reserveSize); + ditrackExtraList.reserve(reserveSize); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::TrackCov) > 0 || (TTrackFillMap & VarManager::ObjTypes::ReducedTrackBarrelCov) > 0); - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -3063,7 +3738,11 @@ struct AnalysisAsymmetricPairing { } // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, dqtablereader_helpers::varValues()); + // Get the instantaneous IR from the CCDB + if (fConfigFetchInteractionRate.value) { + VarManager::fgValues[VarManager::kInteractionRate] = rateFetcher.fetch(fCCDB.service, event.timestamp(), fCurrentRun, fConfigIRSource.value, true) / 1000.; // kHz + } auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); if (groupedLegAAssocs.size() == 0) { @@ -3074,11 +3753,11 @@ struct AnalysisAsymmetricPairing { continue; } - for (auto& [a1, a2] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs))) { + for (auto const& [a1, a2] : combinations(o2::soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs))) { - uint32_t twoTrackFilter = static_cast(0); - uint32_t twoTrackCommonFilter = static_cast(0); - uint32_t pairFilter = static_cast(0); + auto twoTrackFilter = static_cast(0); + auto twoTrackCommonFilter = static_cast(0); + auto pairFilter = static_cast(0); for (int icut = 0; icut < fNLegCuts; ++icut) { // Find leg pair definitions both candidates participate in if ((a1.isBarrelSelected_raw() & fConstructedLegAFilterMasksMap[icut]) && (a2.isBarrelSelected_raw() & fConstructedLegBFilterMasksMap[icut])) { @@ -3108,12 +3787,12 @@ struct AnalysisAsymmetricPairing { bool isReflected = false; std::pair trackIds(t1.globalIndex(), t2.globalIndex()); - if (fPairCount.find(trackIds) != fPairCount.end()) { + if (fPairCount.contains(trackIds)) { // Double counting is possible due to track-collision ambiguity. Skip pairs which were counted before fPairCount[trackIds] += 1; continue; } - if (fPairCount.find(std::pair(trackIds.second, trackIds.first)) != fPairCount.end()) { + if (fPairCount.contains(std::pair(trackIds.second, trackIds.first))) { isReflected = true; } fPairCount[trackIds] += 1; @@ -3130,7 +3809,7 @@ struct AnalysisAsymmetricPairing { VarManager::FillPair(t1, t2); if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigPropToPCA); + VarManager::FillPairVertexing(event, t1, t2); } // Fill histograms @@ -3139,70 +3818,71 @@ struct AnalysisAsymmetricPairing { if (twoTrackFilter & (static_cast(1) << icut)) { isAmbi = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s", fLegCutNames[icut].Data()), VarManager::fgValues); // reconstructed, unmatched + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); // reconstructed, unmatched if (isAmbi && fConfigAmbiguousHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_ambiguous_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); } if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_reflected_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); } } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); if (isAmbi && fConfigAmbiguousHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_ambiguous_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); } if (isReflected && fConfigReflectedHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_reflected_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); } } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); if (isAmbi && fConfigAmbiguousHistograms.value) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_ambiguous_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); } if (isReflected && fConfigReflectedHistograms) { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_reflected_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); } } } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { if (twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqtablereader_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqtablereader_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqtablereader_helpers::varValues()); } } } } // end loop (common cuts) int iPairCut = 0; for (auto cut = fPairCuts.begin(); cut != fPairCuts.end(); cut++, iPairCut++) { - if (!((*cut)->IsSelected(VarManager::fgValues))) // apply pair cuts + if (!((*cut)->IsSelected(dqtablereader_helpers::varValues()))) { // apply pair cuts continue; + } pairFilter |= (static_cast(1) << iPairCut); // Histograms with pair cuts if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); } } // Histograms with pair cuts and common track cuts for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { if (twoTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); } else if (fConfigSameSignHistograms.value) { if (sign1 > 0) { - fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEPP_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); } else { - fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("PairsBarrelSEMM_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); } } } @@ -3222,7 +3902,7 @@ struct AnalysisAsymmetricPairing { // Template function to run same event triplets (e.g. D+->K-pi+pi+) template - void runThreeProng(TEvents const& events, Preslice& preslice, TTrackAssocs const& /*assocs*/, TTracks const& tracks, VarManager::PairCandidateType tripletType) + void runThreeProng(TEvents const& events, o2::framework::Preslice& preslice, TTrackAssocs const& /*assocs*/, TTracks const& tracks, VarManager::PairCandidateType tripletType) { if (events.size() > 0) { // Additional protection to avoid crashing of events.begin().runNumber() if (fCurrentRun != events.begin().runNumber()) { @@ -3231,7 +3911,7 @@ struct AnalysisAsymmetricPairing { } } - for (auto& event : events) { + for (auto const& event : events) { if (!event.isEventSelected_bit(0)) { continue; } @@ -3240,7 +3920,7 @@ struct AnalysisAsymmetricPairing { } // Reset the fValues array VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, dqtablereader_helpers::varValues()); auto groupedLegAAssocs = legACandidateAssocs.sliceBy(preslice, event.globalIndex()); if (groupedLegAAssocs.size() == 0) { @@ -3257,17 +3937,17 @@ struct AnalysisAsymmetricPairing { // Based on triplet type, make suitable combinations of the partitions if (tripletType == VarManager::kTripleCandidateToPKPi) { - for (auto& [a1, a2, a3] : combinations(soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs, groupedLegCAssocs))) { + for (auto const& [a1, a2, a3] : combinations(o2::soa::CombinationsFullIndexPolicy(groupedLegAAssocs, groupedLegBAssocs, groupedLegCAssocs))) { readTriplet(a1, a2, a3, tracks, event, tripletType); } } else if (tripletType == VarManager::kTripleCandidateToKPiPi) { - for (auto& a1 : groupedLegAAssocs) { - for (auto& [a2, a3] : combinations(groupedLegBAssocs, groupedLegCAssocs)) { + for (auto const& a1 : groupedLegAAssocs) { + for (auto const& [a2, a3] : combinations(groupedLegBAssocs, groupedLegCAssocs)) { readTriplet(a1, a2, a3, tracks, event, tripletType); } } } else { - LOG(fatal) << "Given tripletType not recognized. Don't know how to make combinations!" << endl; + LOG(fatal) << "Given tripletType not recognized. Don't know how to make combinations!"; } } // end event loop } @@ -3276,8 +3956,8 @@ struct AnalysisAsymmetricPairing { template void readTriplet(TTrackAssoc const& a1, TTrackAssoc const& a2, TTrackAssoc const& a3, TTracks const& /*tracks*/, TEvent const& event, VarManager::PairCandidateType tripletType) { - uint32_t threeTrackFilter = static_cast(0); - uint32_t threeTrackCommonFilter = static_cast(0); + auto threeTrackFilter = static_cast(0); + auto threeTrackCommonFilter = static_cast(0); for (int icut = 0; icut < fNLegCuts; ++icut) { // Find out which leg cut combinations the triplet passes if ((a1.isBarrelSelected_raw() & fConstructedLegAFilterMasksMap[icut]) && (a2.isBarrelSelected_raw() & fConstructedLegBFilterMasksMap[icut]) && (a3.isBarrelSelected_raw() & fConstructedLegCFilterMasksMap[icut])) { @@ -3338,7 +4018,7 @@ struct AnalysisAsymmetricPairing { threeTrackFilter |= (static_cast(1) << 31); } - VarManager::FillTriple(t1, t2, t3, VarManager::fgValues, tripletType); + VarManager::FillTriple(t1, t2, t3, dqtablereader_helpers::varValues(), tripletType); if constexpr (TThreeProngFitter) { VarManager::FillTripletVertexing(event, t1, t2, t3, tripletType); } @@ -3346,25 +4026,26 @@ struct AnalysisAsymmetricPairing { // Fill histograms for (int icut = 0; icut < fNLegCuts; icut++) { if (threeTrackFilter & (static_cast(1) << icut)) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); if (fConfigAmbiguousHistograms.value && ((threeTrackFilter & (static_cast(1) << 29)) || (threeTrackFilter & (static_cast(1) << 30)) || (threeTrackFilter & (static_cast(1) << 31)))) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_ambiguous_%s", fLegCutNames[icut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_ambiguous_%s", fLegCutNames[icut].Data()), dqtablereader_helpers::varValues()); } for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; iCommonCut++) { if (threeTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data()), dqtablereader_helpers::varValues()); } } // end loop (common cuts) int iPairCut = 0; for (auto cut = fPairCuts.begin(); cut != fPairCuts.end(); cut++, iPairCut++) { - if (!((*cut)->IsSelected(VarManager::fgValues))) // apply pair cuts + if (!((*cut)->IsSelected(dqtablereader_helpers::varValues()))) { // apply pair cuts continue; + } // Histograms with pair cuts - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s", fLegCutNames[icut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); // Histograms with pair cuts and common track cuts for (int iCommonCut = 0; iCommonCut < fNCommonTrackCuts; ++iCommonCut) { if (threeTrackCommonFilter & fCommonTrackCutFilterMasks[iCommonCut]) { - fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TripletsBarrelSE_%s_%s_%s", fLegCutNames[icut].Data(), fCommonCutNames[iCommonCut].Data(), fPairCutNames[iPairCut].Data()), dqtablereader_helpers::varValues()); } } } // end loop (pair cuts) @@ -3373,41 +4054,41 @@ struct AnalysisAsymmetricPairing { } void processKaonPionSkimmed(MyEventsVtxCovZdcFitSelected const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) { runAsymmetricPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } void processKaonPionSkimmedMultExtra(MyEventsVtxCovZdcFitSelectedMultExtra const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) { runAsymmetricPairing(events, trackAssocsPerCollision, barrelAssocs, barrelTracks); } void processKaonPionPionSkimmed(MyEventsVtxCovZdcFitSelected const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) { runThreeProng(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, VarManager::kTripleCandidateToKPiPi); } void processKaonPionPionSkimmedMultExtra(MyEventsVtxCovZdcFitSelectedMultExtra const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) { runThreeProng(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, VarManager::kTripleCandidateToKPiPi); } void processProtonKaonPionSkimmed(MyEventsVtxCovZdcFitSelected const& events, - soa::Join const& barrelAssocs, + o2::soa::Join const& barrelAssocs, MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) { runThreeProng(events, trackAssocsPerCollision, barrelAssocs, barrelTracks, VarManager::kTripleCandidateToPKPi); } - void processDummy(MyEventsBasic&) + void processDummy(MyEventsBasic const&) { // do nothing } @@ -3424,47 +4105,46 @@ struct AnalysisAsymmetricPairing { // Dileptons produced with all the selection cuts specified in the same-event pairing task are combined with the // tracks passing the fConfigTrackCut cut. The dileptons cuts from the same-event pairing task are auto-detected struct AnalysisDileptonTrack { - Produces BmesonsTable; - Produces DileptonTrackTable; - OutputObj fOutputList{"output"}; - - Configurable fConfigTrackCuts{"cfgTrackCuts", "kaonPID", "Comma separated list of cuts for the track to be correlated with the dileptons"}; - Configurable fConfigDileptonLowMass{"cfgDileptonLowMass", 2.8, "Low mass cut for the dileptons used in analysis"}; - Configurable fConfigDileptonHighMass{"cfgDileptonHighMass", 3.2, "High mass cut for the dileptons used in analysis"}; - Configurable fConfigDileptonLowpTCut{"cfgDileptonLowpTCut", 0.0, "Low pT cut for dileptons used in the triplet vertexing"}; - Configurable fConfigDileptonHighpTCut{"cfgDileptonHighpTCut", 1E5, "High pT cut for dileptons used in the triplet vertexing"}; - Configurable fConfigDileptonRapCutAbs{"cfgDileptonRapCutAbs", 1.0, "Rap cut for dileptons used in the triplet vertexing"}; - Configurable fConfigDileptonLxyCut{"cfgDileptonLxyCut", 0.0, "Lxy cut for dileptons used in the triplet vertexing"}; - Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; - - Configurable fConfigHistogramSubgroups{"cfgDileptonTrackHistogramsSubgroups", "invmass,vertexing", "Comma separated list of dilepton-track histogram subgroups"}; - Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; - Configurable fConfigMixingDepth{"cfgMixingDepth", 5, "Event mixing pool depth"}; - - Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; - - Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; - Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable fConfigUseRapcut{"cfgUseMCRapcut", false, "Use Rap cut for dileptons used in the triplet vertexing"}; - Configurable fConfigEnergycorrelator{"cfgEnergycorrelator", false, "Add some hist for energy correlator study"}; - Configurable fConfigApplyMassEC{"cfgApplyMassEC", false, "Apply fit mass for sideband for the energy correlator study"}; - Configurable> fConfigFitmassEC{"cfgTFitmassEC", std::vector{-0.541438, 2.8, 3.2}, "parameter from the fit fuction and fit range"}; - Configurable> fConfigTransRange{"cfgTransRange", std::vector{0.333333, 0.666667}, "Transverse region for the energy correlstor analysis"}; - - Configurable fConfigApplyEfficiency{"cfgApplyEfficiency", false, "If true, apply efficiency correction for the energy correlator study"}; - Configurable fConfigApplyEfficiencyME{"cfgApplyEfficiencyME", false, "If true, apply efficiency correction for the energy correlator study"}; - Configurable fConfigAccCCDBPath{"AccCCDBPath", "Users/y/yalin/pptest/test2", "Path of the efficiency corrections"}; - - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. - int fNCuts; // number of dilepton leg cuts - int fNLegCuts; - int fNPairCuts; // number of pair cuts - int fNCommonTrackCuts; + o2::framework::Produces BmesonsTable; + o2::framework::Produces DileptonTrackTable; + o2::framework::OutputObj fOutputList{"output"}; + + o2::framework::Configurable fConfigTrackCuts{"cfgTrackCuts", "kaonPID", "Comma separated list of cuts for the track to be correlated with the dileptons"}; + o2::framework::Configurable fConfigDileptonLowMass{"cfgDileptonLowMass", 2.8, "Low mass cut for the dileptons used in analysis"}; + o2::framework::Configurable fConfigDileptonHighMass{"cfgDileptonHighMass", 3.2, "High mass cut for the dileptons used in analysis"}; + o2::framework::Configurable fConfigDileptonLowpTCut{"cfgDileptonLowpTCut", 0.0, "Low pT cut for dileptons used in the triplet vertexing"}; + o2::framework::Configurable fConfigDileptonHighpTCut{"cfgDileptonHighpTCut", 1E5, "High pT cut for dileptons used in the triplet vertexing"}; + o2::framework::Configurable fConfigDileptonRapCutAbs{"cfgDileptonRapCutAbs", 1.0, "Rap cut for dileptons used in the triplet vertexing"}; + o2::framework::Configurable fConfigDileptonLxyCut{"cfgDileptonLxyCut", 0.0, "Lxy cut for dileptons used in the triplet vertexing"}; + o2::framework::Configurable fConfigDileptonTauxyCut{"cfgDileptonTauxyCut", -10000, "Tauxy cut for dileptons used to select the non-prompt Jpsi"}; + o2::framework::Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; + + o2::framework::Configurable fConfigHistogramSubgroups{"cfgDileptonTrackHistogramsSubgroups", "invmass,vertexing", "Comma separated list of dilepton-track histogram subgroups"}; + o2::framework::Configurable fConfigAddJSONHistograms{"cfgAddJSONHistograms", "", "Histograms in JSON format"}; + o2::framework::Configurable fConfigMixingDepth{"cfgMixingDepth", 5, "Event mixing pool depth"}; + + o2::framework::Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + + o2::framework::Configurable fConfigCcdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable fConfigNoLaterThan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + o2::framework::Configurable fConfigGeoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + o2::framework::Configurable fConfigUseRapcut{"cfgUseMCRapcut", false, "Use Rap cut for dileptons used in the triplet vertexing"}; + o2::framework::Configurable fConfigEnergycorrelator{"cfgEnergycorrelator", false, "Add some hist for energy correlator study"}; + o2::framework::Configurable fConfigApplyMassEC{"cfgApplyMassEC", false, "Apply fit mass for sideband for the energy correlator study"}; + o2::framework::Configurable> fConfigFitmassEC{"cfgTFitmassEC", std::vector{-0.541438, 2.8, 3.2}, "parameter from the fit fuction and fit range"}; + o2::framework::Configurable> fConfigTransRange{"cfgTransRange", std::vector{0.333333, 0.666667}, "Transverse region for the energy correlstor analysis"}; + + o2::framework::Configurable fConfigApplyEfficiency{"cfgApplyEfficiency", false, "If true, apply efficiency correction for the energy correlator study"}; + o2::framework::Configurable fConfigApplyEfficiencyME{"cfgApplyEfficiencyME", false, "If true, apply efficiency correction for the energy correlator study"}; + o2::framework::Configurable fConfigAccCCDBPath{"AccCCDBPath", "Users/y/yalin/pptest/test2", "Path of the efficiency corrections"}; + + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. + int fNCuts = 0; // number of dilepton leg cuts + int fNLegCuts = 0; + int fNPairCuts = 0; // number of pair cuts + int fNCommonTrackCuts = 0; std::map fCommonTrackCutMap; - uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons + uint32_t fTrackCutBitMap = 0; // track cut bit mask to be used in the selection of tracks associated with dileptons // vector for single-lepton and track cut names for easy access when calling FillHistogramList() std::vector fTrackCutNames; std::vector fLegCutNames; @@ -3472,30 +4152,30 @@ struct AnalysisDileptonTrack { std::vector fPairCutNames; std::vector fCommonPairCutNames; - Service fCCDB; + o2::framework::Service fCCDB{}; // TODO: The filter expressions seem to always use the default value of configurables, not the values from the actual configuration file - Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); - Filter dileptonFilter = aod::reducedpair::pt > fConfigDileptonLowpTCut&& aod::reducedpair::pt fConfigDileptonLowMass&& aod::reducedpair::mass fConfigDileptonLxyCut; - Filter filterBarrel = aod::dqanalysisflags::isBarrelSelected > static_cast(0); - Filter filterMuon = aod::dqanalysisflags::isMuonSelected > static_cast(0); + o2::framework::expressions::Filter eventFilter = o2::aod::dqanalysisflags::isEventSelected > static_cast(0); + o2::framework::expressions::Filter dileptonFilter = o2::aod::reducedpair::pt > fConfigDileptonLowpTCut&& o2::aod::reducedpair::pt fConfigDileptonLowMass&& o2::aod::reducedpair::mass fConfigDileptonLxyCut&& o2::aod::reducedpair::tauxy > fConfigDileptonTauxyCut; + o2::framework::expressions::Filter filterBarrel = o2::aod::dqanalysisflags::isBarrelSelected > static_cast(0); + o2::framework::expressions::Filter filterMuon = o2::aod::dqanalysisflags::isMuonSelected > static_cast(0); constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use two values array to avoid mixing up the quantities - float* fValuesDilepton; - float* fValuesHadron; - HistogramManager* fHistMan; + float* fValuesDilepton = nullptr; + float* fValuesHadron = nullptr; + HistogramManager* fHistMan = nullptr; - NoBinningPolicy fHashBin; + o2::framework::NoBinningPolicy fHashBin; TF1* fMassBkg = nullptr; - TH2F* hAcceptance_rec; - TH2F* hAcceptance_gen; - TH2F* hEfficiency_dilepton; - TH2F* hEfficiency_hadron; - TH1F* hMasswindow; + TH2F* hAcceptance_rec = nullptr; + TH2F* hAcceptance_gen = nullptr; + TH2F* hEfficiency_dilepton = nullptr; + TH2F* hEfficiency_hadron = nullptr; + TH1F* hMasswindow = nullptr; void init(o2::framework::InitContext& context) { @@ -3520,7 +4200,7 @@ struct AnalysisDileptonTrack { VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); // For each track/muon selection used to produce dileptons, create a separate histogram directory using the // name of the track/muon cut. @@ -3528,11 +4208,11 @@ struct AnalysisDileptonTrack { // Get the list of single track and muon cuts computed in the dedicated tasks upstream // We need this to know the order in which they were computed, and also to make sure that in this task we do not ask // for cuts which were not computed (in which case this will trigger a fatal) - string cfgTrackSelection_TrackCuts; + std::string cfgTrackSelection_TrackCuts; if (isBarrel || isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgTrackCuts", cfgTrackSelection_TrackCuts, false); } else { - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", cfgTrackSelection_TrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCuts", cfgTrackSelection_TrackCuts, false); } TObjArray* cfgTrackSelection_objArrayTrackCuts = nullptr; @@ -3541,17 +4221,17 @@ struct AnalysisDileptonTrack { } // get also the list of cuts specified via the JSON parameters if (isBarrel || isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-track-selection", "cfgBarrelTrackCutsJSON", cfgTrackSelection_TrackCuts, false); } else { - getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", cfgTrackSelection_TrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-muon-selection", "cfgMuonCutsJSON", cfgTrackSelection_TrackCuts, false); } if (!cfgTrackSelection_TrackCuts.empty()) { if (cfgTrackSelection_objArrayTrackCuts == nullptr) { cfgTrackSelection_objArrayTrackCuts = new TObjArray(); } - std::vector addTrackCuts = dqcuts::GetCutsFromJSON(cfgTrackSelection_TrackCuts.data()); - for (auto& t : addTrackCuts) { - TObjString* tempObjStr = new TObjString(t->GetName()); + std::vector addTrackCuts = o2::aod::dqcuts::GetCutsFromJSON(cfgTrackSelection_TrackCuts.data()); + for (auto const& t : addTrackCuts) { + auto tempObjStr = new TObjString(t->GetName()); cfgTrackSelection_objArrayTrackCuts->Add(tempObjStr); } } @@ -3586,20 +4266,20 @@ struct AnalysisDileptonTrack { // NOTE: The track/muon cuts in analysis-same-event-pairing are used to select electrons/muons to build dielectrons/dimuons // NOTE: The cfgPairCuts in analysis-same-event-pairing are used to apply an additional selection on top of the already produced dileptons // but this is only used for histograms, not for the produced dilepton tables - string cfgPairing_TrackCuts; - string cfgPairing_PairCuts; - string cfgPairing_PairCutsJSON; - string cfgPairing_CommonTrackCuts; + std::string cfgPairing_TrackCuts; + std::string cfgPairing_PairCuts; + std::string cfgPairing_PairCutsJSON; + std::string cfgPairing_CommonTrackCuts; if (isBarrel) { - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgTrackCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "cfgTrackCuts", cfgPairing_TrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); } else if (isMuon) { - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgMuonCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "cfgMuonCuts", cfgPairing_TrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-same-event-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); } else if (isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgLegCuts", cfgPairing_TrackCuts, false); - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgCommonTrackCuts", cfgPairing_CommonTrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgLegCuts", cfgPairing_TrackCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCuts", cfgPairing_PairCuts, false); + o2::common::core::getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgCommonTrackCuts", cfgPairing_CommonTrackCuts, false); } if (cfgPairing_TrackCuts.empty()) { LOG(fatal) << "There are no dilepton cuts specified in the upstream in the same-event-pairing or asymmetric-pairing"; @@ -3621,11 +4301,11 @@ struct AnalysisDileptonTrack { } // end if (common cuts) // Get also the pair cuts specified via the JSON parameters if (isBarrelAsymmetric) { - getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCutsJSON", cfgPairing_PairCutsJSON, false); + o2::common::core::getTaskOptionValue(context, "analysis-asymmetric-pairing", "cfgPairCutsJSON", cfgPairing_PairCutsJSON, false); TString addPairCutsStr = cfgPairing_PairCutsJSON; if (addPairCutsStr != "") { - std::vector addPairCuts = dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); - for (auto& t : addPairCuts) { + std::vector addPairCuts = o2::aod::dqcuts::GetCutsFromJSON(addPairCutsStr.Data()); + for (auto const& t : addPairCuts) { cfgPairing_PairCuts += Form(",%s", t->GetName()); } } @@ -3658,7 +4338,7 @@ struct AnalysisDileptonTrack { pairLegCutName = fTrackCutNames[icut].Data(); } else { // For asymmetric pairs we access the leg cuts instead - pairLegCutName = static_cast(cfgPairing_objArrayTrackCuts->At(icut))->GetString(); + pairLegCutName = dynamic_cast(cfgPairing_objArrayTrackCuts->At(icut))->GetString(); } fLegCutNames.push_back(pairLegCutName); @@ -3668,7 +4348,7 @@ struct AnalysisDileptonTrack { for (int iCutTrack = 0; iCutTrack < fNCuts; iCutTrack++) { // here we check that this track cut is one of those required to associate with the dileptons - if (!(fTrackCutBitMap & (static_cast(1) << iCutTrack))) { + if ((fTrackCutBitMap & (static_cast(1) << iCutTrack)) == 0) { continue; } @@ -3708,7 +4388,7 @@ struct AnalysisDileptonTrack { } // end loop over pair leg track cuts } - dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON + o2::aod::dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON VarManager::SetUseVars(fHistMan->GetUsedVars()); fOutputList.setObject(fHistMan->GetMainHistogramList()); @@ -3732,44 +4412,53 @@ struct AnalysisDileptonTrack { // init parameters from CCDB void initParamsFromCCDB(uint64_t timestamp) { - if (fConfigUseRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(magField); - } else { - VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupThreeProngKFParticle(fConfigMagField.value); - } else { - VarManager::SetupThreeProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupThreeProngKFParticle(magField); + } else { + VarManager::SetupThreeProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables } } void initAccFromCCDB(uint64_t timestamp) { - TList* listAccs = fCCDB->getForTimeStamp(fConfigAccCCDBPath, timestamp); + auto listAccs = fCCDB->getForTimeStamp(fConfigAccCCDBPath, timestamp); if (!listAccs) { LOG(fatal) << "Problem getting TList object with efficiencies!"; } - hEfficiency_dilepton = static_cast(listAccs->FindObject("hEfficiency_dilepton")); - hEfficiency_hadron = static_cast(listAccs->FindObject("hEfficiency_hadron")); - hAcceptance_rec = static_cast(listAccs->FindObject("hAcceptance_rec")); - hAcceptance_gen = static_cast(listAccs->FindObject("hAcceptance_gen")); - hMasswindow = static_cast(listAccs->FindObject("hMasswindow")); + hEfficiency_dilepton = dynamic_cast(listAccs->FindObject("hEfficiency_dilepton")); + hEfficiency_hadron = dynamic_cast(listAccs->FindObject("hEfficiency_hadron")); + hAcceptance_rec = dynamic_cast(listAccs->FindObject("hAcceptance_rec")); + hAcceptance_gen = dynamic_cast(listAccs->FindObject("hAcceptance_gen")); + hMasswindow = dynamic_cast(listAccs->FindObject("hMasswindow")); if (!hAcceptance_rec || !hAcceptance_gen || !hEfficiency_dilepton || !hEfficiency_hadron || !hMasswindow) { LOG(fatal) << "Problem getting histograms from the TList object with efficiencies!"; } } + float GetSafeInterpolationWeight(TH2* hEff, float x, float y) + { + if (!hEff) { + return 1.0; + } + float minX = hEff->GetXaxis()->GetBinCenter(1); + float maxX = hEff->GetXaxis()->GetBinCenter(hEff->GetXaxis()->GetNbins()); + + float minY = hEff->GetYaxis()->GetBinCenter(1); + float maxY = hEff->GetYaxis()->GetBinCenter(hEff->GetYaxis()->GetNbins()); + + float safeX = std::max(minX, std::min(x, maxX)); + float safeY = std::max(minY, std::min(y, maxY)); + + return hEff->Interpolate(safeX, safeY); + } + // Template function to run pair - hadron combinations template void runDileptonHadron(TEvent const& event, TTrackAssocs const& assocs, TTracks const& tracks, TDileptons const& dileptons) @@ -3779,7 +4468,7 @@ struct AnalysisDileptonTrack { VarManager::FillEvent(event, fValuesHadron); VarManager::FillEvent(event, fValuesDilepton); - for (auto dilepton : dileptons) { + for (auto const& dilepton : dileptons) { // get full track info of tracks based on the index auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); auto lepton2 = tracks.rawIteratorAt(dilepton.index1Id()); @@ -3789,8 +4478,9 @@ struct AnalysisDileptonTrack { } // dilepton rap cut float rap = dilepton.rap(); - if (fConfigUseRapcut && std::abs(rap) > fConfigDileptonRapCutAbs) + if (fConfigUseRapcut && std::abs(rap) > fConfigDileptonRapCutAbs) { continue; + } VarManager::FillTrack(dilepton, fValuesDilepton); @@ -3825,13 +4515,13 @@ struct AnalysisDileptonTrack { } // end loop over single lepton selections // loop over hadrons - for (auto& assoc : assocs) { + for (auto const& assoc : assocs) { uint32_t trackSelection = 0; if constexpr (TCandidateType == VarManager::kBtoJpsiEEK) { // check the cuts fulfilled by this candidate track; if none just continue trackSelection = (assoc.isBarrelSelected_raw() & fTrackCutBitMap); - if (!trackSelection) { + if (trackSelection == 0) { continue; } @@ -3853,14 +4543,14 @@ struct AnalysisDileptonTrack { float hadron_eta = track.eta(); float hadron_phi = track.phi(); float deltaphi = RecoDecay::constrainAngle(dilepton_phi - hadron_phi, -0.5 * o2::constants::math::PI); - Effweight_rec = hAcceptance_rec->Interpolate(dilepton_eta - hadron_eta, deltaphi); - float Effdilepton = hEfficiency_dilepton->Interpolate(dilepton.pt(), dilepton_eta); + Effweight_rec = GetSafeInterpolationWeight(hAcceptance_rec, dilepton_eta - hadron_eta, deltaphi); + float Effdilepton = GetSafeInterpolationWeight(hEfficiency_dilepton, dilepton.rap(), dilepton.pt()); + float Effhadron = GetSafeInterpolationWeight(hEfficiency_hadron, track.eta(), track.pt()); float Masswindow = hMasswindow->Interpolate(dilepton.pt()); - float Effhadron = hEfficiency_hadron->Interpolate(track.pt(), hadron_eta); Effweight_rec = Effweight_rec * Effdilepton * Effhadron * Masswindow; } std::vector fTransRange = fConfigTransRange; - VarManager::FillEnergyCorrelatorTriple(lepton1, lepton2, track, fValuesHadron, fTransRange[0], fTransRange[1], fConfigApplyMassEC, fMassBkg->GetRandom(), 1. / Effweight_rec); + VarManager::FillEnergyCorrelatorTriple(event, lepton1, lepton2, track, fValuesHadron, fTransRange[0], fTransRange[1], fConfigApplyMassEC, fMassBkg->GetRandom(), 1. / Effweight_rec); // table to be written out for ML analysis BmesonsTable(event.runNumber(), event.globalIndex(), event.timestamp(), fValuesHadron[VarManager::kPairMass], dilepton.mass(), fValuesHadron[VarManager::kDeltaMass], fValuesHadron[VarManager::kPairPt], fValuesHadron[VarManager::kPairEta], fValuesHadron[VarManager::kPairPhi], fValuesHadron[VarManager::kPairRap], @@ -3880,7 +4570,7 @@ struct AnalysisDileptonTrack { } if constexpr (TCandidateType == VarManager::kDstarToD0KPiPi) { trackSelection = (assoc.isBarrelSelected_raw() & fTrackCutBitMap); - if (!trackSelection) { + if (trackSelection == 0) { continue; } auto track = assoc.template reducedtrack_as(); @@ -3896,7 +4586,7 @@ struct AnalysisDileptonTrack { } if constexpr (TCandidateType == VarManager::kBcToThreeMuons) { trackSelection = (assoc.isMuonSelected_raw() & fTrackCutBitMap); - if (!trackSelection) { + if (trackSelection == 0) { continue; } auto track = assoc.template reducedmuon_as(); @@ -3951,13 +4641,13 @@ struct AnalysisDileptonTrack { } } - Preslice trackAssocsPerCollision = aod::reducedtrack_association::reducedeventId; - Preslice dielectronsPerCollision = aod::reducedpair::reducedeventId; - Preslice ditracksPerCollision = aod::reducedpair::reducedeventId; + o2::framework::Preslice trackAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; + o2::framework::Preslice dielectronsPerCollision = o2::aod::reducedpair::reducedeventId; + o2::framework::Preslice ditracksPerCollision = o2::aod::reducedpair::reducedeventId; - void processBarrelSkimmed(soa::Filtered const& events, - soa::Filtered> const& assocs, - MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons) + void processBarrelSkimmed(o2::soa::Filtered const& events, + o2::soa::Filtered> const& assocs, + MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) { // set up KF or DCAfitter if (events.size() == 0) { @@ -3970,16 +4660,16 @@ struct AnalysisDileptonTrack { } fCurrentRun = events.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (auto const& event : events) { auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); runDileptonHadron(event, groupedBarrelAssocs, tracks, groupedDielectrons); } } - void processDstarToD0Pi(soa::Filtered const& events, - soa::Filtered> const& assocs, - MyBarrelTracksWithCov const& tracks, soa::Filtered const& ditracks) + void processDstarToD0Pi(o2::soa::Filtered const& events, + o2::soa::Filtered> const& assocs, + MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& ditracks) { // set up KF or DCAfitter if (events.size() == 0) { @@ -3989,19 +4679,19 @@ struct AnalysisDileptonTrack { initParamsFromCCDB(events.begin().timestamp()); fCurrentRun = events.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (auto const& event : events) { auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); auto groupedDitracks = ditracks.sliceBy(ditracksPerCollision, event.globalIndex()); runDileptonHadron(event, groupedBarrelAssocs, tracks, groupedDitracks); } } - Preslice muonAssocsPerCollision = aod::reducedtrack_association::reducedeventId; - Preslice dimuonsPerCollision = aod::reducedpair::reducedeventId; + o2::framework::Preslice muonAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; + o2::framework::Preslice dimuonsPerCollision = o2::aod::reducedpair::reducedeventId; - void processMuonSkimmed(soa::Filtered const& events, - soa::Filtered> const& assocs, - MyMuonTracksWithCov const& tracks, soa::Filtered const& dileptons) + void processMuonSkimmed(o2::soa::Filtered const& events, + o2::soa::Filtered> const& assocs, + MyMuonTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) { // set up KF or DCAfitter if (events.size() == 0) { @@ -4011,16 +4701,16 @@ struct AnalysisDileptonTrack { initParamsFromCCDB(events.begin().timestamp()); fCurrentRun = events.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (auto const& event : events) { auto groupedMuonAssocs = assocs.sliceBy(muonAssocsPerCollision, event.globalIndex()); auto groupedDimuons = dileptons.sliceBy(dimuonsPerCollision, event.globalIndex()); runDileptonHadron(event, groupedMuonAssocs, tracks, groupedDimuons); } } - void processBarrelMixedEvent(soa::Filtered& events, - soa::Filtered> const& assocs, - MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons) + void processBarrelMixedEvent(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Filtered> const& assocs, + MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) { if (events.size() == 0) { return; @@ -4037,10 +4727,10 @@ struct AnalysisDileptonTrack { events.bindExternalIndices(&assocs); // loop over two event comibnations - for (auto& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { + for (auto const& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { // fill event quantities VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event1, VarManager::fgValues); + VarManager::FillEvent(event1, dqtablereader_helpers::varValues()); // get the dilepton slice for event1 auto evDileptons = dileptons.sliceBy(dielectronsPerCollision, event1.globalIndex()); @@ -4051,11 +4741,11 @@ struct AnalysisDileptonTrack { evAssocs.bindExternalIndices(&events); // loop over associations - for (auto& assoc : evAssocs) { + for (auto const& assoc : evAssocs) { // check that this track fulfills at least one of the specified cuts uint32_t trackSelection = (assoc.isBarrelSelected_raw() & fTrackCutBitMap); - if (!trackSelection) { + if (trackSelection == 0) { continue; } @@ -4063,7 +4753,7 @@ struct AnalysisDileptonTrack { auto track = assoc.template reducedtrack_as(); // loop over dileptons - for (auto dilepton : evDileptons) { + for (auto const& dilepton : evDileptons) { // get full track info of tracks based on the index auto lepton1 = tracks.rawIteratorAt(dilepton.index0Id()); auto lepton2 = tracks.rawIteratorAt(dilepton.index1Id()); @@ -4073,11 +4763,12 @@ struct AnalysisDileptonTrack { } // dilepton rap cut float rap = dilepton.rap(); - if (fConfigUseRapcut && std::abs(rap) > fConfigDileptonRapCutAbs) + if (fConfigUseRapcut && std::abs(rap) > fConfigDileptonRapCutAbs) { continue; + } // compute dilepton - track quantities - VarManager::FillDileptonHadron(dilepton, track, VarManager::fgValues); + VarManager::FillDileptonHadron(dilepton, track, dqtablereader_helpers::varValues()); // for the energy correlator analysis float Effweight_rec = 1.0f; @@ -4087,10 +4778,10 @@ struct AnalysisDileptonTrack { float hadron_eta = track.eta(); float hadron_phi = track.phi(); float deltaphi = RecoDecay::constrainAngle(dilepton_phi - hadron_phi, -0.5 * o2::constants::math::PI); - Effweight_rec = hAcceptance_rec->Interpolate(dilepton_eta - hadron_eta, deltaphi); - float Effdilepton = hEfficiency_dilepton->Interpolate(dilepton.pt(), dilepton_eta); + Effweight_rec = GetSafeInterpolationWeight(hAcceptance_rec, dilepton_eta - hadron_eta, deltaphi); + float Effdilepton = GetSafeInterpolationWeight(hEfficiency_dilepton, dilepton.rap(), dilepton.pt()); + float Effhadron = GetSafeInterpolationWeight(hEfficiency_hadron, track.eta(), track.pt()); float Masswindow = hMasswindow->Interpolate(dilepton.pt()); - float Effhadron = hEfficiency_hadron->Interpolate(track.pt(), hadron_eta); if (fConfigApplyEfficiencyME) { Effweight_rec = Effdilepton * Effhadron * Masswindow; // for the moment, apply the efficiency correction also for the mixed event pairs, but this can be changed in case we want to apply it only for the same event pairs } else { @@ -4098,7 +4789,7 @@ struct AnalysisDileptonTrack { } } std::vector fTransRange = fConfigTransRange; - VarManager::FillEnergyCorrelatorTriple(lepton1, lepton2, track, VarManager::fgValues, fTransRange[0], fTransRange[1], fConfigApplyMassEC, fMassBkg->GetRandom(), 1. / Effweight_rec); + VarManager::FillEnergyCorrelatorTriple(event1, lepton1, lepton2, track, dqtablereader_helpers::varValues(), fTransRange[0], fTransRange[1], fConfigApplyMassEC, fMassBkg->GetRandom(), 1. / Effweight_rec); // loop over dilepton leg cuts and track cuts and fill histograms separately for each combination for (int icut = 0; icut < fNCuts; icut++) { @@ -4106,10 +4797,10 @@ struct AnalysisDileptonTrack { continue; } for (uint32_t iTrackCut = 0; iTrackCut < fTrackCutNames.size(); iTrackCut++) { - if (trackSelection & (static_cast(1) << iTrackCut)) { - fHistMan->FillHistClass(Form("DileptonTrackME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), VarManager::fgValues); + if ((trackSelection & (static_cast(1) << iTrackCut)) != 0) { + fHistMan->FillHistClass(Form("DileptonTrackME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), dqtablereader_helpers::varValues()); if (fConfigEnergycorrelator) { - fHistMan->FillHistClass(Form("DileptonTrackECME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), VarManager::fgValues); + fHistMan->FillHistClass(Form("DileptonTrackECME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), dqtablereader_helpers::varValues()); } } } @@ -4119,9 +4810,9 @@ struct AnalysisDileptonTrack { } // end event loop } - void processMuonMixedEvent(soa::Filtered& events, - soa::Filtered> const& assocs, - MyMuonTracksWithCov const&, soa::Filtered const& dileptons) + void processMuonMixedEvent(o2::soa::Filtered& events, // o2-linter: disable=const-ref-in-process + o2::soa::Filtered> const& assocs, + MyMuonTracksWithCov const&, o2::soa::Filtered const& dileptons) { if (events.size() == 0) { return; @@ -4129,9 +4820,9 @@ struct AnalysisDileptonTrack { events.bindExternalIndices(&dileptons); events.bindExternalIndices(&assocs); - for (auto& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { + for (auto const& [event1, event2] : selfCombinations(fHashBin, fConfigMixingDepth.value, -1, events, events)) { VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event1, VarManager::fgValues); + VarManager::FillEvent(event1, dqtablereader_helpers::varValues()); auto evDileptons = dileptons.sliceBy(dimuonsPerCollision, event1.globalIndex()); evDileptons.bindExternalIndices(&events); @@ -4139,23 +4830,23 @@ struct AnalysisDileptonTrack { auto evAssocs = assocs.sliceBy(muonAssocsPerCollision, event2.globalIndex()); evAssocs.bindExternalIndices(&events); - for (auto& assoc : evAssocs) { + for (auto const& assoc : evAssocs) { uint32_t muonSelection = assoc.isMuonSelected_raw() & fTrackCutBitMap; - if (!muonSelection) { + if (muonSelection == 0) { continue; } auto track = assoc.template reducedmuon_as(); - for (auto dilepton : evDileptons) { - VarManager::FillDileptonHadron(dilepton, track, VarManager::fgValues); + for (auto const& dilepton : evDileptons) { + VarManager::FillDileptonHadron(dilepton, track, dqtablereader_helpers::varValues()); for (int icut = 0; icut < fNCuts; icut++) { if (!dilepton.filterMap_bit(icut)) { continue; } for (uint32_t iTrackCut = 0; iTrackCut < fTrackCutNames.size(); iTrackCut++) { - if (muonSelection & (static_cast(1) << iTrackCut)) { - fHistMan->FillHistClass(Form("DileptonTrackME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), VarManager::fgValues); + if ((muonSelection & (static_cast(1) << iTrackCut)) != 0) { + fHistMan->FillHistClass(Form("DileptonTrackME_%s_%s", fTrackCutNames[icut].Data(), fTrackCutNames[iTrackCut].Data()), dqtablereader_helpers::varValues()); } } } @@ -4164,7 +4855,7 @@ struct AnalysisDileptonTrack { } // end event loop } - void processDummy(MyEventsBasic&) + void processDummy(MyEventsBasic const&) { // do nothing } @@ -4178,44 +4869,42 @@ struct AnalysisDileptonTrack { }; struct AnalysisDileptonTrackTrack { - OutputObj fOutputList{"output"}; + o2::framework::OutputObj fOutputList{"output"}; - Configurable fConfigTrackCut1{"cfgTrackCut1", "pionPIDCut1", "track1 cut"}; // used for select the tracks from SelectedTracks - Configurable fConfigTrackCut2{"cfgTrackCut2", "pionPIDCut2", "track2 cut"}; // used for select the tracks from SelectedTracks - Configurable fConfigDileptonCut{"cfgDiLeptonCut", "pairJpsi2", "Dilepton cut"}; - Configurable fConfigQuadrupletCuts{"cfgQuadrupletCuts", "pairX3872Cut1", "Comma separated list of Dilepton-Track-Track cut"}; - Configurable fConfigAddDileptonHistogram{"cfgAddDileptonHistogram", "barrel", "Comma separated list of histograms"}; - Configurable fConfigAddQuadrupletHistogram{"cfgAddQuadrupletHistogram", "xtojpsipipi", "Comma separated list of histograms"}; + o2::framework::Configurable fConfigTrackCut1{"cfgTrackCut1", "pionPIDCut1", "track1 cut"}; // used for select the tracks from SelectedTracks + o2::framework::Configurable fConfigTrackCut2{"cfgTrackCut2", "pionPIDCut2", "track2 cut"}; // used for select the tracks from SelectedTracks + o2::framework::Configurable fConfigDileptonCut{"cfgDiLeptonCut", "pairJpsi2", "Dilepton cut"}; + o2::framework::Configurable fConfigQuadrupletCuts{"cfgQuadrupletCuts", "pairX3872Cut1", "Comma separated list of Dilepton-Track-Track cut"}; + o2::framework::Configurable fConfigAddDileptonHistogram{"cfgAddDileptonHistogram", "barrel", "Comma separated list of histograms"}; + o2::framework::Configurable fConfigAddQuadrupletHistogram{"cfgAddQuadrupletHistogram", "xtojpsipipi", "Comma separated list of histograms"}; - Configurable fConfigSetupFourProngFitter{"cfgSetupFourProngFitter", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; - Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; - Configurable fConfigUseRemoteField{"cfgUseRemoteField", false, "Chose whether to fetch the magnetic field from ccdb or set it manually"}; - Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable fConfigMagField{"cfgMagField", 5.0f, "Manually set magnetic field"}; + o2::framework::Configurable fConfigSetupFourProngFitter{"cfgSetupFourProngFitter", false, "Use DCA for secondary vertex reconstruction (DCAFitter is used by default)"}; + o2::framework::Configurable fConfigUseKFVertexing{"cfgUseKFVertexing", false, "Use KF Particle for secondary vertex reconstruction (DCAFitter is used by default)"}; + o2::framework::Configurable fConfigGRPmagPath{"cfgGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Produces DileptonTrackTrackTable; + o2::framework::Produces DileptonTrackTrackTable; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. // uint32_t fTrackCutBitMap; // track cut bit mask to be used in the selection of tracks associated with dileptons // cut name setting TString fTrackCutName1; TString fTrackCutName2; bool fIsSameTrackCut = false; - AnalysisCompositeCut fDileptonCut; + AnalysisCompositeCut fDileptonCut{}; std::vector fQuadrupletCutNames; std::vector fQuadrupletCuts; - Service fCCDB; + o2::framework::Service fCCDB{}; - Filter eventFilter = aod::dqanalysisflags::isEventSelected > static_cast(0); - Filter dileptonFilter = aod::reducedpair::sign == 0; - Filter filterBarrelTrackSelected = aod::dqanalysisflags::isBarrelSelected > static_cast(0); + o2::framework::expressions::Filter eventFilter = o2::aod::dqanalysisflags::isEventSelected > static_cast(0); + o2::framework::expressions::Filter dileptonFilter = o2::aod::reducedpair::sign == 0; + o2::framework::expressions::Filter filterBarrelTrackSelected = o2::aod::dqanalysisflags::isBarrelSelected > static_cast(0); constexpr static uint32_t fgDileptonFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::Pair; // fill map // use some values array to avoid mixing up the quantities - float* fValuesQuadruplet; - HistogramManager* fHistMan; + float* fValuesQuadruplet = nullptr; + HistogramManager* fHistMan = nullptr; void init(o2::framework::InitContext& context) { @@ -4231,7 +4920,7 @@ struct AnalysisDileptonTrackTrack { VarManager::SetDefaultVarNames(); fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(dqtablereader_helpers::varNames(), dqtablereader_helpers::varUnits()); // define cuts fTrackCutName1 = fConfigTrackCut1.value; @@ -4240,13 +4929,13 @@ struct AnalysisDileptonTrackTrack { fIsSameTrackCut = true; } TString configDileptonCutNamesStr = fConfigDileptonCut.value; - fDileptonCut = *dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); + fDileptonCut = *o2::aod::dqcuts::GetCompositeCut(configDileptonCutNamesStr.Data()); TString configQuadruletCutNamesStr = fConfigQuadrupletCuts.value; std::unique_ptr objArray(configQuadruletCutNamesStr.Tokenize(",")); for (Int_t icut = 0; icut < objArray->GetEntries(); ++icut) { TString cutName = objArray->At(icut)->GetName(); fQuadrupletCutNames.push_back(cutName); - fQuadrupletCuts.push_back(*dqcuts::GetCompositeCut(cutName.Data())); + fQuadrupletCuts.push_back(*o2::aod::dqcuts::GetCompositeCut(cutName.Data())); } if (!context.mOptions.get("processDummy")) { @@ -4272,30 +4961,19 @@ struct AnalysisDileptonTrackTrack { // init parameters from CCDB void initParamsFromCCDB(uint64_t timestamp) { - if (fConfigUseRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); - float magField = 0.0; - if (grpmag != nullptr) { - magField = grpmag->getNominalL3Field(); - } else { - LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); - } - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(magField); - VarManager::SetupFourProngKFParticle(magField); - } else if (fConfigSetupFourProngFitter.value) { - VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - VarManager::SetupFourProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + auto grpmag = fCCDB->getForTimeStamp(fConfigGRPmagPath.value, timestamp); + float magField = 0.0; + if (grpmag != nullptr) { + magField = grpmag->getNominalL3Field(); } else { - if (fConfigUseKFVertexing.value) { - VarManager::SetupTwoProngKFParticle(fConfigMagField.value); - VarManager::SetupFourProngKFParticle(fConfigMagField.value); - } else if (fConfigSetupFourProngFitter.value) { - LOGP(info, "Setting up DCA fitter for two and four prong candidates"); - VarManager::SetupTwoProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - VarManager::SetupFourProngDCAFitter(fConfigMagField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables - } + LOGF(fatal, "GRP object is not available in CCDB at timestamp=%llu", timestamp); + } + if (fConfigUseKFVertexing.value) { + VarManager::SetupTwoProngKFParticle(magField); + VarManager::SetupFourProngKFParticle(magField); + } else if (fConfigSetupFourProngFitter.value) { + VarManager::SetupTwoProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables + VarManager::SetupFourProngDCAFitter(magField, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, false); // TODO: get these parameters from Configurables } } @@ -4308,7 +4986,7 @@ struct AnalysisDileptonTrackTrack { // int indexOffset = -999; // std::vector trackGlobalIndexes; - for (auto dilepton : dileptons) { + for (auto const& dilepton : dileptons) { // get full track info of tracks based on the index int indexLepton1 = dilepton.index0Id(); @@ -4323,13 +5001,14 @@ struct AnalysisDileptonTrackTrack { // LOGP(info, "is dilepton selected: {}", fDileptonCut.IsSelected(fValuesQuadruplet)); // apply the dilepton cut - if (!fDileptonCut.IsSelected(fValuesQuadruplet)) + if (!fDileptonCut.IsSelected(fValuesQuadruplet)) { continue; + } fHistMan->FillHistClass(Form("Pairs_%s", fDileptonCut.GetName()), fValuesQuadruplet); // loop over hadrons pairs - for (auto& [a1, a2] : o2::soa::combinations(assocs, assocs)) { + for (auto const& [a1, a2] : o2::soa::combinations(assocs, assocs)) { uint32_t trackSelection = 0; if (fIsSameTrackCut) { trackSelection = ((a1.isBarrelSelected_raw() & (static_cast(1) << 1)) || (a2.isBarrelSelected_raw() & (static_cast(1) << 1))); @@ -4337,7 +5016,7 @@ struct AnalysisDileptonTrackTrack { trackSelection = ((a1.isBarrelSelected_raw() & (static_cast(1) << 1)) && (a2.isBarrelSelected_raw() & (static_cast(1) << 2))); } // LOGP(info, "trackSelection: {}, a1: {}, a2: {}", trackSelection, a1.isBarrelSelected_raw(), a2.isBarrelSelected_raw()); - if (!trackSelection) { + if (trackSelection == 0) { continue; } @@ -4382,8 +5061,9 @@ struct AnalysisDileptonTrackTrack { } // loop over dilepton-track-track cuts // fill table - if (!CutDecision) + if (!CutDecision) { continue; + } DileptonTrackTrackTable(fValuesQuadruplet[VarManager::kQuadMass], fValuesQuadruplet[VarManager::kQuadPt], fValuesQuadruplet[VarManager::kQuadEta], fValuesQuadruplet[VarManager::kQuadPhi], fValuesQuadruplet[VarManager::kRap], fValuesQuadruplet[VarManager::kQ], fValuesQuadruplet[VarManager::kDeltaR1], fValuesQuadruplet[VarManager::kDeltaR2], fValuesQuadruplet[VarManager::kDeltaR], dilepton.mass(), dilepton.pt(), dilepton.eta(), dilepton.phi(), dilepton.sign(), @@ -4397,13 +5077,13 @@ struct AnalysisDileptonTrackTrack { } } - Preslice trackAssocsPerCollision = aod::reducedtrack_association::reducedeventId; - Preslice dielectronsPerCollision = aod::reducedpair::reducedeventId; - Preslice ditracksPerCollision = aod::reducedpair::reducedeventId; + o2::framework::Preslice trackAssocsPerCollision = o2::aod::reducedtrack_association::reducedeventId; + o2::framework::Preslice dielectronsPerCollision = o2::aod::reducedpair::reducedeventId; + o2::framework::Preslice ditracksPerCollision = o2::aod::reducedpair::reducedeventId; - void processJpsiPiPi(soa::Filtered const& events, - soa::Filtered> const& assocs, - MyBarrelTracksWithCov const& tracks, soa::Filtered const& dileptons) + void processJpsiPiPi(o2::soa::Filtered const& events, + o2::soa::Filtered> const& assocs, + MyBarrelTracksWithCov const& tracks, o2::soa::Filtered const& dileptons) { if (events.size() == 0) { return; @@ -4412,14 +5092,14 @@ struct AnalysisDileptonTrackTrack { initParamsFromCCDB(events.begin().timestamp()); fCurrentRun = events.begin().runNumber(); } // end: runNumber - for (auto& event : events) { + for (auto const& event : events) { auto groupedBarrelAssocs = assocs.sliceBy(trackAssocsPerCollision, event.globalIndex()); auto groupedDielectrons = dileptons.sliceBy(dielectronsPerCollision, event.globalIndex()); runDileptonTrackTrack(event, groupedBarrelAssocs, tracks, groupedDielectrons); } } - void processDummy(MyEventsBasic&) + void processDummy(MyEventsBasic const&) { // do nothing } @@ -4428,20 +5108,7 @@ struct AnalysisDileptonTrackTrack { PROCESS_SWITCH(AnalysisDileptonTrackTrack, processDummy, "Dummy function", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; -} - -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) { // // Define here the histograms for all the classes required in analysis. @@ -4456,76 +5123,78 @@ void DefineHistograms(HistogramManager* histMan, TString histClasses, const char TString histName = histGroups; // NOTE: The level of detail for histogramming can be controlled via configurables if (classStr.Contains("Event")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "event", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "event", histName); } if (classStr.Contains("SameBunchCorrelations") || classStr.Contains("OutOfBunchCorrelations")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "two-collisions", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "two-collisions", histName); } if (classStr.Contains("Track") && !classStr.Contains("Pairs")) { if (classStr.Contains("Barrel")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", histName); if (classStr.Contains("PIDCalibElectron")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "postcalib_electron"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "postcalib_electron"); } if (classStr.Contains("PIDCalibPion")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "postcalib_pion"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "postcalib_pion"); } if (classStr.Contains("PIDCalibProton")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "postcalib_proton"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "postcalib_proton"); } if (classStr.Contains("Ambiguity")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "ambiguity"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", "ambiguity"); } } if (classStr.Contains("Muon")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", histName); } } if (classStr.Contains("Pairs")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", histName); } if (classStr.Contains("Pairing")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "event", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "event", histName); } if (classStr.Contains("Triplets")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", histName); } if (classStr.Contains("DileptonsSelected")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", "barrel,vertexing"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "pair", "barrel,vertexing"); } if (classStr.Contains("DileptonTrack") && !classStr.Contains("ME")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-track", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-track", histName); } if (classStr.Contains("DileptonTrackME")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-track", "dilepton-hadron-array-correlation"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-track", "dilepton-hadron-array-correlation"); } if (classStr.Contains("DileptonTrackECME")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-track", "energy-correlator"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-track", "energy-correlator"); } if (classStr.Contains("HadronsSelected")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "track", histName); } if (classStr.Contains("DileptonHadronInvMass")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-hadron-mass"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-hadron-mass"); } if (classStr.Contains("DileptonHadronCorrelation")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-hadron-correlation"); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-hadron-correlation"); } if (classStr.Contains("Quadruplet")) { - dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-dihadron", histName); + o2::aod::dqhistograms::DefineHistograms(histMan, objArray->At(iclass)->GetName(), "dilepton-dihadron", histName); } } // end loop over histogram classes } + +#endif // PWGDQ_TASKS_TABLEREADER_WITHASSOC_H_ diff --git a/PWGDQ/Tasks/tableReader_withAssoc_AsymmetricPairing_workflowSpec.cxx b/PWGDQ/Tasks/tableReader_withAssoc_AsymmetricPairing_workflowSpec.cxx new file mode 100644 index 00000000000..62fd0a134a3 --- /dev/null +++ b/PWGDQ/Tasks/tableReader_withAssoc_AsymmetricPairing_workflowSpec.cxx @@ -0,0 +1,30 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// Configurable workflow for running several DQ or other PWG analyses + +#include "PWGDQ/Tasks/tableReader_withAssoc.h" + +#include +#include + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGDQ/Tasks/tableReader_withAssoc_DileptonTrackPairing_workflowSpec.cxx b/PWGDQ/Tasks/tableReader_withAssoc_DileptonTrackPairing_workflowSpec.cxx new file mode 100644 index 00000000000..c76889c25a4 --- /dev/null +++ b/PWGDQ/Tasks/tableReader_withAssoc_DileptonTrackPairing_workflowSpec.cxx @@ -0,0 +1,32 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// Configurable workflow for running several DQ or other PWG analyses + +#include "PWGDQ/Tasks/tableReader_withAssoc.h" + +#include +#include + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGDQ/Tasks/tableReader_withAssoc_DileptonTrackTrackPairing_workflowSpec.cxx b/PWGDQ/Tasks/tableReader_withAssoc_DileptonTrackTrackPairing_workflowSpec.cxx new file mode 100644 index 00000000000..14954aff005 --- /dev/null +++ b/PWGDQ/Tasks/tableReader_withAssoc_DileptonTrackTrackPairing_workflowSpec.cxx @@ -0,0 +1,30 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// Configurable workflow for running several DQ or other PWG analyses + +#include "PWGDQ/Tasks/tableReader_withAssoc.h" + +#include +#include + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairingBarrelOnly_workflowSpec.cxx b/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairingBarrelOnly_workflowSpec.cxx new file mode 100644 index 00000000000..e70f0bfbc56 --- /dev/null +++ b/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairingBarrelOnly_workflowSpec.cxx @@ -0,0 +1,29 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// Configurable workflow for running several DQ or other PWG analyses + +#include "PWGDQ/Tasks/tableReader_withAssoc.h" + +#include +#include + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGEM/Dilepton/TableProducer/dimuonProducer.cxx b/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairingMuonOnly_workflowSpec.cxx similarity index 64% rename from PWGEM/Dilepton/TableProducer/dimuonProducer.cxx rename to PWGDQ/Tasks/tableReader_withAssoc_SameEventPairingMuonOnly_workflowSpec.cxx index ed298d39c39..e18dd6fa7ba 100644 --- a/PWGEM/Dilepton/TableProducer/dimuonProducer.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairingMuonOnly_workflowSpec.cxx @@ -9,13 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// ======================== -// -// This code is for dimuon analyses. -// Please write to: daiki.sekihata@cern.ch +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// Configurable workflow for running several DQ or other PWG analyses -#include "PWGEM/Dilepton/Core/DileptonProducer.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGDQ/Tasks/tableReader_withAssoc.h" #include #include @@ -24,5 +21,8 @@ using namespace o2::framework; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask>(cfgc, TaskName{"dimuon-producer"})}; + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; } diff --git a/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairing_workflowSpec.cxx b/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairing_workflowSpec.cxx new file mode 100644 index 00000000000..6f72e0cf357 --- /dev/null +++ b/PWGDQ/Tasks/tableReader_withAssoc_SameEventPairing_workflowSpec.cxx @@ -0,0 +1,30 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// Configurable workflow for running several DQ or other PWG analyses + +#include "PWGDQ/Tasks/tableReader_withAssoc.h" + +#include +#include + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx index c8002425a33..47a21cedcc9 100644 --- a/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx +++ b/PWGDQ/Tasks/tableReader_withAssoc_direct.cxx @@ -27,11 +27,13 @@ #include "Common/Core/TableHelper.h" #include "Common/Core/Zorro.h" #include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -52,6 +54,7 @@ #include #include +#include #include #include #include @@ -79,8 +82,6 @@ using namespace o2::framework::expressions; using namespace o2::aod; using namespace o2::common::core; -Zorro zorro; - // Some definitions namespace o2::aod { @@ -168,6 +169,13 @@ DECLARE_SOA_COLUMN(Lxyee, lxyee, float); DECLARE_SOA_COLUMN(LxyeePoleMass, lxyeepolemass, float); DECLARE_SOA_COLUMN(Lzee, lzee, float); DECLARE_SOA_COLUMN(LxyeePoleMassPVrecomputed, lxyeePoleMassPVrecomputed, float); +DECLARE_SOA_COLUMN(Vx, vx, float); +DECLARE_SOA_COLUMN(Vy, vy, float); +DECLARE_SOA_COLUMN(Vz, vz, float); +DECLARE_SOA_COLUMN(DcaXY1, dcaXY1, float); +DECLARE_SOA_COLUMN(DcaZ1, dcaZ1, float); +DECLARE_SOA_COLUMN(DcaXY2, dcaXY2, float); +DECLARE_SOA_COLUMN(DcaZ2, dcaZ2, float); DECLARE_SOA_COLUMN(MultiplicityFT0A, multiplicityFT0AJPsi2ee, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0CJPsi2ee, float); DECLARE_SOA_COLUMN(PercentileFT0M, percentileFT0MJPsi2ee, float); @@ -189,10 +197,10 @@ DECLARE_SOA_TABLE(MuonTrackCuts, "AOD", "DQANAMUONCUTS", dqanalysisflags::IsMuon DECLARE_SOA_TABLE(MuonAmbiguities, "AOD", "DQMUONAMB", dqanalysisflags::MuonAmbiguityInBunch, dqanalysisflags::MuonAmbiguityOutOfBunch); //! joinable to FwdTracks DECLARE_SOA_TABLE(JPsieeCandidates, "AOD", "DQPSEUDOPROPER", - dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, - dqanalysisflags::Phiee, dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, - dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, - dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); + dqanalysisflags::Massee, dqanalysisflags::Ptee, dqanalysisflags::Etaee, dqanalysisflags::Rapee, dqanalysisflags::Phiee, + dqanalysisflags::Lxyee, dqanalysisflags::LxyeePoleMass, dqanalysisflags::Lzee, dqanalysisflags::LxyeePoleMassPVrecomputed, + dqanalysisflags::Vx, dqanalysisflags::Vy, dqanalysisflags::Vz, dqanalysisflags::DcaXY1, dqanalysisflags::DcaZ1, dqanalysisflags::ITSClusterMapleg1, dqanalysisflags::TPCnsigmaElleg1, dqanalysisflags::DcaXY2, dqanalysisflags::DcaZ2, dqanalysisflags::ITSClusterMapleg2, dqanalysisflags::TPCnsigmaElleg2, + dqanalysisflags::AmbiguousInBunchPairs, dqanalysisflags::AmbiguousOutOfBunchPairs, dqanalysisflags::MultiplicityFT0A, dqanalysisflags::MultiplicityFT0C, dqanalysisflags::PercentileFT0M, dqanalysisflags::MultiplicityNContrib); DECLARE_SOA_TABLE(BmesonCandidates, "AOD", "DQBMESONS", dqanalysisflags::RunNumber, dqanalysisflags::EventIdx, dqanalysisflags::EventTimestamp, @@ -216,8 +224,11 @@ DECLARE_SOA_TABLE(BmesonCandidates, "AOD", "DQBMESONS", // Using definitions (data-only) using MyEvents = soa::Join; +using MyEventsQvectorCentr = soa::Join; using MyEventsSelected = soa::Join; +using MyEventsQvectorCentrSelected = soa::Join; using MyEventsHashSelected = soa::Join; +using MyEventsHashSelectedQvectorCentr = soa::Join; using MyBarrelTracksWithCov = soa::Join fConfigZorroTrigMask{"cfgZorroTriggerMask", "fDiMuon", "DQ Trigger masks: fSingleE,fLMeeIMR,fLMeeHMR,fDiElectron,fSingleMuLow,fSingleMuHigh,fDiMuon"}; Configurable fConfigRunZorroSel{"cfgRunZorroSel", false, "Select events with trigger mask"}; Configurable fBcTolerance{"cfgBcTolerance", 100, "Number of BCs of margin for software triggers"}; - Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "/Users/m/mpuccio/EventFiltering/OTS/Chunked/", "base path to the ccdb object for zorro"}; + Configurable fConfigCcdbPathZorro{"ccdb-path-zorro", "EventFiltering/Zorro/", "base path to the ccdb object for zorro"}; } fConfigZorro; // RCT selection @@ -310,20 +323,22 @@ struct AnalysisEventSelection { // RCT flag checker o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT"}; + Zorro zorro; + HistogramManager* fHistMan = nullptr; MixingHandler* fMixHandler = nullptr; - AnalysisCompositeCut* fEventCut; + AnalysisCompositeCut* fEventCut = nullptr; o2::parameters::GRPObject* fGrpMagRun2 = nullptr; // for run 2, we access the GRPObject from GLO/GRP/GRP o2::parameters::GRPMagField* fGrpMag = nullptr; // for run 3, we access GRPMagField from GLO/Config/GRPMagField - Service fCCDB; + Service fCCDB{}; o2::ccdb::CcdbApi fCCDBApi; std::map fSelMap; // key: reduced event global index, value: event selection decision std::map> fBCCollMap; // key: global BC, value: vector of reduced event global indices - int fCurrentRun; + int fCurrentRun = -1; void init(o2::framework::InitContext& context) { @@ -355,7 +370,7 @@ struct AnalysisEventSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(true); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); DefineHistograms(fHistMan, "TimeFrameStats;Event_BeforeCuts;Event_AfterCuts;", fConfigAddEventHistogram.value.data()); if (fConfigCheckSplitCollisions) { DefineHistograms(fHistMan, "OutOfBunchCorrelations;SameBunchCorrelations;", ""); @@ -434,7 +449,7 @@ struct AnalysisEventSelection { } VarManager::SetCalibrationType(fConfigPostCalibTPC.fConfigTPCpostCalibType, fConfigPostCalibTPC.fConfigTPCuseInterpolatedCalib); } - if (fIsRun2 == true) { + if (fIsRun2) { fGrpMagRun2 = fCCDB->getForTimeStamp(fConfigCCDB.fConfigGrpMagPathRun2, bcs.begin().timestamp()); if (fGrpMagRun2 != nullptr) { o2::base::Propagator::initFieldFromGRP(fGrpMagRun2); @@ -466,7 +481,7 @@ struct AnalysisEventSelection { VarManager::FillTimeFrame(bcs); VarManager::FillTimeFrame(events); if (fConfigQA) { - fHistMan->FillHistClass("TimeFrameStats", VarManager::fgValues); + fHistMan->FillHistClass("TimeFrameStats", static_cast(VarManager::fgValues)); } fSelMap.clear(); @@ -479,35 +494,45 @@ struct AnalysisEventSelection { VarManager::FillBC(bc); VarManager::FillEvent(event); + // the hash table is joined to the events by row order, so publish one row per event before any event selection + if (fMixHandler != nullptr) { + int hh = fMixHandler->FindEventCategory(static_cast(VarManager::fgValues)); + // events outside the mixing limits (-1) get a distinct negative hash so that they are not mixed with each other + if (hh < 0) { + hh = -1 - static_cast(event.globalIndex()); + } + hash(hh); + } + bool decision = false; if (fConfigQA) { - fHistMan->FillHistClass("Event_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_BeforeCuts", static_cast(VarManager::fgValues)); } if (fConfigZorro.fConfigRunZorro) { zorro.setBaseCCDBPath(fConfigZorro.fConfigCcdbPathZorro.value); zorro.setBCtolerance(fConfigZorro.fBcTolerance); zorro.initCCDB(fCCDB.service, fCurrentRun, bc.timestamp(), fConfigZorro.fConfigZorroTrigMask.value); - zorro.populateExternalHists(fCurrentRun, reinterpret_cast(fStatsList->At(kStatsZorroInfo)), reinterpret_cast(fStatsList->At(kStatsZorroSel))); + zorro.populateExternalHists(fCurrentRun, dynamic_cast(fStatsList->At(kStatsZorroInfo)), dynamic_cast(fStatsList->At(kStatsZorroSel))); - if (!fEventCut->IsSelected(VarManager::fgValues) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { + if (!fEventCut->IsSelected(static_cast(VarManager::fgValues)) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { continue; } - bool zorroSel = zorro.isSelected(bc.globalBC(), fConfigZorro.fBcTolerance, reinterpret_cast(fStatsList->At(kStatsZorroSel))); + bool zorroSel = zorro.isSelected(bc.globalBC(), fConfigZorro.fBcTolerance, dynamic_cast(fStatsList->At(kStatsZorroSel))); if (fConfigZorro.fConfigRunZorroSel && (!zorroSel)) { continue; } } else { - if (!fEventCut->IsSelected(VarManager::fgValues) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { + if (!fEventCut->IsSelected(static_cast(VarManager::fgValues)) || (fConfigRCT.fConfigUseRCT.value && !rctChecker(event))) { continue; } } decision = true; if (fConfigQA) { - fHistMan->FillHistClass("Event_AfterCuts", VarManager::fgValues); + fHistMan->FillHistClass("Event_AfterCuts", static_cast(VarManager::fgValues)); } fSelMap[event.globalIndex()] = decision; @@ -518,10 +543,6 @@ struct AnalysisEventSelection { auto& evIndices = fBCCollMap[bc.globalBC()]; evIndices.push_back(event.globalIndex()); } - if (fMixHandler != nullptr) { - int hh = fMixHandler->FindEventCategory(VarManager::fgValues); - hash(hh); - } } } @@ -551,7 +572,7 @@ struct AnalysisEventSelection { collisionSplittingMap[*ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("SameBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("SameBunchCorrelations", static_cast(VarManager::fgValues)); } } // end second event loop } // end first event loop @@ -578,7 +599,7 @@ struct AnalysisEventSelection { collisionSplittingMap[ev2It] = true; } if (fConfigQA) { - fHistMan->FillHistClass("OutOfBunchCorrelations", VarManager::fgValues); + fHistMan->FillHistClass("OutOfBunchCorrelations", static_cast(VarManager::fgValues)); } } } @@ -586,7 +607,7 @@ struct AnalysisEventSelection { } // publish the table - uint32_t evSel = static_cast(0); + auto evSel = static_cast(0); for (auto& event : events) { evSel = 0; if (fSelMap[event.globalIndex()]) { // event passed the user cuts @@ -610,9 +631,16 @@ struct AnalysisEventSelection { publishSelections(events); } + void processDirectWithQvectorCentr(MyEventsQvectorCentr const& events, BCsWithTimestamps const& bcs) + { + runEventSelection(events, bcs); + publishSelections(events); + } + void processDummy(aod::Collisions&) {} PROCESS_SWITCH(AnalysisEventSelection, processDirect, "Run event selection on framework AO2Ds", false); + PROCESS_SWITCH(AnalysisEventSelection, processDirectWithQvectorCentr, "Run event selection on skimmed data with Q-vector and centrality", false); PROCESS_SWITCH(AnalysisEventSelection, processDummy, "Dummy function", true); }; @@ -634,22 +662,23 @@ struct AnalysisTrackSelection { Configurable fConfigComputeTPCpostCalib{"cfgTPCpostCalib", false, "If true, compute TPC post-calibrated n-sigmas"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Service fCCDB; - Service fTofResponse; + Service fCCDB{}; + Service fTofResponse{}; - HistogramManager* fHistMan; + HistogramManager* fHistMan = nullptr; std::vector fTrackCuts; std::vector fHistNamesReco; - int fCurrentRun; // current run (needed to detect run changes for loading CCDB parameters) + int fCurrentRun = 0; // current run (needed to detect run changes for loading CCDB parameters) std::map> fNAssocsInBunch; // key: track global index, value: vector of global index for events associated in-bunch (events that have in-bunch pileup or splitting) std::map> fNAssocsOutOfBunch; // key: track global index, value: vector of global index for events associated out-of-bunch (events that have no in-bunch pileup) void init(o2::framework::InitContext& context) { - if (context.mOptions.get("processDummy")) + if (context.mOptions.get("processDummy")) { return; + } VarManager::SetDefaultVarNames(); fCurrentRun = 0; @@ -668,7 +697,7 @@ struct AnalysisTrackSelection { if (addTrackCutsStr != "") { std::vector addTrackCuts = dqcuts::GetCutsFromJSON(addTrackCutsStr.Data()); for (auto& t : addTrackCuts) { - fTrackCuts.push_back(reinterpret_cast(t)); + fTrackCuts.push_back(dynamic_cast(t)); } } @@ -678,7 +707,7 @@ struct AnalysisTrackSelection { if (fConfigQA) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); // Configure histogram classes for each track cut; TString histClasses = "TimeFrameStats;AssocsBarrel_BeforeCuts;"; @@ -717,8 +746,9 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNVars); VarManager::FillTimeFrame(events); VarManager::FillTimeFrame(tracks); - if (fConfigQA) - fHistMan->FillHistClass("TimeFrameStats", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("TimeFrameStats", static_cast(VarManager::fgValues)); + } if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { if (fConfigComputeTPCpostCalib) { @@ -731,7 +761,7 @@ struct AnalysisTrackSelection { VarManager::SetCalibrationObject(VarManager::kTPCProtonSigma, calibList->FindObject("sigma_map_proton")); } - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(grpmagPath, bcs.begin().timestamp()); + auto grpmag = fCCDB->getForTimeStamp(grpmagPath, bcs.begin().timestamp()); if (grpmag != nullptr) { VarManager::SetMagneticField(grpmag->getNominalL3Field()); } else { @@ -764,19 +794,21 @@ struct AnalysisTrackSelection { VarManager::FillTrack(track); // compute quantities which depend on the associated collision, such as DCA - if (track.collisionId() != event.globalIndex()) + if (track.collisionId() != event.globalIndex()) { VarManager::FillTrackCollision(track, event); + } - if (fConfigQA) - fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", VarManager::fgValues); + if (fConfigQA) { + fHistMan->FillHistClass("AssocsBarrel_BeforeCuts", static_cast(VarManager::fgValues)); + } int iCut = 0; - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); for (auto cut = fTrackCuts.begin(); cut != fTrackCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(static_cast(VarManager::fgValues))) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(fHistNamesReco[iCut], VarManager::fgValues); + fHistMan->FillHistClass(fHistNamesReco[iCut], static_cast(VarManager::fgValues)); } } } // end loop over cuts @@ -818,7 +850,7 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityInBunch", static_cast(VarManager::fgValues)); } // end loop over in-bunch ambiguous tracks for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { @@ -829,7 +861,7 @@ struct AnalysisTrackSelection { VarManager::ResetValues(0, VarManager::kNBarrelTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kBarrelNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackBarrel_AmbiguityOutOfBunch", static_cast(VarManager::fgValues)); } // end loop over out-of-bunch ambiguous tracks } @@ -850,7 +882,7 @@ struct AnalysisTrackSelection { void processWithCov(TrackAssoc const& assocs, BCsWithTimestamps const& bcs, MyEventsSelected const& events, MyBarrelTracksWithCov const& tracks) { - runTrackSelection(assocs, bcs, events, tracks); + runTrackSelection(assocs, bcs, events, tracks); } void processWithCovTOFService(TrackAssoc const& assocs, BCsWithTimestamps const& bcs, MyEventsSelected const& events, MyBarrelTracksWithCovNoTOF const& tracks) @@ -879,9 +911,9 @@ struct AnalysisPrefilterSelection { Configurable fPropTrack{"cfgPropTrack", false, "Propagate tracks to associated collision to recalculate DCA and momentum vector"}; std::map fPrefilterMap; - AnalysisCompositeCut* fPairCut; - uint32_t fPrefilterMask; - int fPrefilterCutBit; + AnalysisCompositeCut* fPairCut = nullptr; + uint32_t fPrefilterMask = 0; + int fPrefilterCutBit = -1; Preslice trackAssocsPerCollision = aod::track_association::collisionId; @@ -987,7 +1019,7 @@ struct AnalysisPrefilterSelection { bool track1Loose = assoc1.isBarrelSelected_bit(fPrefilterCutBit); bool track2Loose = assoc2.isBarrelSelected_bit(fPrefilterCutBit); - if (!((track1Candidate > 0 && track2Loose) || (track2Candidate > 0 && track1Loose))) { + if ((track1Candidate == 0 || !track2Loose) && (track2Candidate == 0 || !track1Loose)) { continue; } @@ -997,7 +1029,7 @@ struct AnalysisPrefilterSelection { VarManager::FillPairCollision(event, track1, track2); } // if the pair fullfils the criteria, add an entry into the prefilter map for the two tracks - if (fPairCut->IsSelected(VarManager::fgValues)) { + if (fPairCut->IsSelected(static_cast(VarManager::fgValues))) { if (fPrefilterMap.find(track1.globalIndex()) == fPrefilterMap.end() && track1Candidate > 0) { fPrefilterMap[track1.globalIndex()] = track1Candidate; } @@ -1034,7 +1066,7 @@ struct AnalysisPrefilterSelection { // auto track = assoc.template track_as(); mymap = -1; // if (fPrefilterMap.find(track.globalIndex()) != fPrefilterMap.end()) { - if (fPrefilterMap.find(assoc.trackId()) != fPrefilterMap.end()) { + if (fPrefilterMap.contains(assoc.trackId())) { // NOTE: publish the bitwise negated bits (~), so there will be zeroes for cuts that failed the prefiltering and 1 everywhere else // mymap = ~fPrefilterMap[track.globalIndex()]; mymap = ~fPrefilterMap[assoc.trackId()]; @@ -1075,7 +1107,7 @@ struct AnalysisMuonSelection { Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; } fConfigCCDB; - Service fCCDB; + Service fCCDB{}; HistogramManager* fHistMan = nullptr; std::vector fMuonCuts; @@ -1105,7 +1137,7 @@ struct AnalysisMuonSelection { if (addCutsStr != "") { std::vector addCuts = dqcuts::GetCutsFromJSON(addCutsStr.Data()); for (auto& t : addCuts) { - fMuonCuts.push_back(reinterpret_cast(t)); + fMuonCuts.push_back(dynamic_cast(t)); } } @@ -1115,7 +1147,7 @@ struct AnalysisMuonSelection { if (fHistMan == nullptr) { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); TString histDirNames = "TrackMuon_BeforeCuts;"; for (auto& cut : fMuonCuts) { @@ -1149,7 +1181,7 @@ struct AnalysisMuonSelection { fNAssocsOutOfBunch.clear(); if (bcs.size() > 0 && fCurrentRun != bcs.begin().runNumber()) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, bcs.begin().timestamp()); + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, bcs.begin().timestamp()); if (grpmag != nullptr) { o2::base::Propagator::initFieldFromGRP(grpmag); VarManager::SetMagneticField(grpmag->getNominalL3Field()); @@ -1163,7 +1195,7 @@ struct AnalysisMuonSelection { if (fConfigPublishAmbiguity) { muonAmbiguities.reserve(muons.size()); } - uint32_t filterMap = static_cast(0); + auto filterMap = static_cast(0); int iCut = 0; for (auto& assoc : assocs) { @@ -1179,14 +1211,14 @@ struct AnalysisMuonSelection { filterMap = static_cast(0); VarManager::FillTrack(track); if (fConfigQA) { - fHistMan->FillHistClass("TrackMuon_BeforeCuts", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_BeforeCuts", static_cast(VarManager::fgValues)); } iCut = 0; for (auto cut = fMuonCuts.begin(); cut != fMuonCuts.end(); cut++, iCut++) { - if ((*cut)->IsSelected(VarManager::fgValues)) { + if ((*cut)->IsSelected(static_cast(VarManager::fgValues))) { filterMap |= (static_cast(1) << iCut); if (fConfigQA) { - fHistMan->FillHistClass(Form("TrackMuon_%s", (*cut)->GetName()), VarManager::fgValues); + fHistMan->FillHistClass(Form("TrackMuon_%s", (*cut)->GetName()), static_cast(VarManager::fgValues)); } } } @@ -1212,22 +1244,24 @@ struct AnalysisMuonSelection { if (fConfigPublishAmbiguity) { if (fConfigQA) { for (auto& [trackIdx, evIndices] : fNAssocsInBunch) { - if (evIndices.size() <= 1) + if (evIndices.size() <= 1) { continue; + } auto track = muons.rawIteratorAt(trackIdx); VarManager::ResetValues(0, VarManager::kNMuonTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsInBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackMuon_AmbiguityInBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_AmbiguityInBunch", static_cast(VarManager::fgValues)); } for (auto& [trackIdx, evIndices] : fNAssocsOutOfBunch) { - if (evIndices.size() <= 1) + if (evIndices.size() <= 1) { continue; + } auto track = muons.rawIteratorAt(trackIdx); VarManager::ResetValues(0, VarManager::kNMuonTrackVariables); VarManager::FillTrack(track); VarManager::fgValues[VarManager::kMuonNAssocsOutOfBunch] = static_cast(evIndices.size()); - fHistMan->FillHistClass("TrackMuon_AmbiguityOutOfBunch", VarManager::fgValues); + fHistMan->FillHistClass("TrackMuon_AmbiguityOutOfBunch", static_cast(VarManager::fgValues)); } } // publish ambiguity table (one row per FwdTrack) @@ -1268,7 +1302,9 @@ struct AnalysisSameEventPairing { Produces electronmuonList; o2::base::MatLayerCylSet* fLUT = nullptr; - int fCurrentRun; // needed to detect if the run changed and trigger update of calibrations etc. + TH1D* ResoFlowSP = nullptr; + TH1D* ResoFlowEP = nullptr; + int fCurrentRun = -1; // needed to detect if the run changed and trigger update of calibrations etc. OutputObj fOutputList{"output"}; @@ -1302,6 +1338,7 @@ struct AnalysisSameEventPairing { Configurable fConfigMiniTree{"cfgMiniTree", false, "Produce a single flat table with minimal information for analysis"}; Configurable fConfigMiniTreeMinMass{"cfgMiniTreeMinMass", 2, "Min. mass cut for minitree"}; Configurable fConfigMiniTreeMaxMass{"cfgMiniTreeMaxMass", 5, "Max. mass cut for minitree"}; + Configurable useFlowReso{"cfgUseFlowReso", false, "Use remote flow information from CCDB"}; } fConfigOptions; struct : ConfigurableGroup { @@ -1309,9 +1346,10 @@ struct AnalysisSameEventPairing { Configurable grpMagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable flowPath{"flowPath", "Users/y/yiping/FlowResolution", "Path to the flow resolution object"}; } fConfigCCDB; - Service fCCDB; + Service fCCDB{}; // vectors needed for PV recomputation std::vector pvContribGlobIDs; @@ -1320,22 +1358,22 @@ struct AnalysisSameEventPairing { // keep histogram class names in maps, so we don't have to buld their names in the pair loops std::map> fTrackHistNames; - std::map> fMuonHistNames; + // std::map> fMuonHistNames; std::map> fTrackMuonHistNames; // for electron-muon pairs: key = iTrack * fNCutsMuon + iMuon std::vector fPairCuts; - AnalysisCompositeCut fMCGenAccCut; + AnalysisCompositeCut fMCGenAccCut{}; // bool fUseMCGenAccCut = false; - uint32_t fTrackFilterMask; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream - uint32_t fMuonFilterMask; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream - int fNCutsBarrel; - int fNCutsMuon; - int fNPairCuts; + uint32_t fTrackFilterMask = 0; // mask for the track cuts required in this task to be applied on the barrel cuts produced upstream + uint32_t fMuonFilterMask = 0; // mask for the muon cuts required in this task to be applied on the muon cuts produced upstream + int fNCutsBarrel = 0; + int fNCutsMuon = 0; + int fNPairCuts = 0; bool fHasTwoProngGenMCsignals = false; - bool fEnableBarrelHistos; - bool fEnableBarrelMuonHistos; + bool fEnableBarrelHistos = false; + bool fEnableBarrelMuonHistos = false; std::vector fTrackCuts; // barrel cut names, used in EMu histogram filling std::vector fMuonCuts; // muon cut names, used in EMu histogram filling @@ -1351,7 +1389,7 @@ struct AnalysisSameEventPairing { } VarManager::SetDefaultVarNames(); - fEnableBarrelHistos = context.mOptions.get("processBarrelOnly"); + fEnableBarrelHistos = context.mOptions.get("processBarrelOnly") || context.mOptions.get("processBarrelOnlyWithQvectorCentr"); fEnableBarrelMuonHistos = context.mOptions.get("processElectronMuonDirect"); // Keep track of all the histogram class names to avoid composing strings in the pairing loop @@ -1427,7 +1465,6 @@ struct AnalysisSameEventPairing { // if there are pair cuts specified, assign hist directories for each barrel cut - pair cut combination // NOTE: This could possibly lead to large histogram outputs. It is strongly advised to use pair cuts only // if you know what you are doing. - TString cutNamesStr = fConfigOptions.pair.value; if (!cutNamesStr.IsNull()) { // if pair cuts std::unique_ptr objArrayPair(cutNamesStr.Tokenize(",")); fNPairCuts = objArrayPair->GetEntries(); @@ -1474,8 +1511,9 @@ struct AnalysisSameEventPairing { int seqTrackIdx = 0; // sequential index into fTrackCuts (which contains only required cuts) for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { // skip barrel cuts not required in this task - if (!(fTrackFilterMask & (static_cast(1) << iTrack))) + if ((fTrackFilterMask & (static_cast(1) << iTrack)) == 0) { continue; + } TString trackCutName = fTrackCuts[seqTrackIdx]; seqTrackIdx++; std::vector names = { @@ -1511,9 +1549,9 @@ struct AnalysisSameEventPairing { fHistMan = new HistogramManager("analysisHistos", "aa", VarManager::kNVars); fHistMan->SetUseDefaultVariableNames(kTRUE); - fHistMan->SetDefaultVarNames(VarManager::fgVariableNames, VarManager::fgVariableUnits); + fHistMan->SetDefaultVarNames(static_cast(VarManager::fgVariableNames), static_cast(VarManager::fgVariableUnits)); - VarManager::SetCollisionSystem((TString)fConfigOptions.collisionSystem, fConfigOptions.centerMassEnergy); // set collision system and center of mass energy + VarManager::SetCollisionSystem(TString(fConfigOptions.collisionSystem), fConfigOptions.centerMassEnergy); // set collision system and center of mass energy DefineHistograms(fHistMan, histNames.Data(), fConfigOptions.fConfigAddSEPHistogram.value.data()); // define all histograms dqhistograms::AddHistogramsFromJSON(fHistMan, fConfigOptions.fConfigAddJSONHistograms.value.c_str()); // ad-hoc histograms via JSON @@ -1524,7 +1562,7 @@ struct AnalysisSameEventPairing { void initParamsFromCCDB(uint64_t timestamp, bool withTwoProngFitter = true) { if (fConfigOptions.useRemoteField.value) { - o2::parameters::GRPMagField* grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); + auto grpmag = fCCDB->getForTimeStamp(fConfigCCDB.grpMagPath, timestamp); o2::base::MatLayerCylSet* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(fCCDB->get(fConfigCCDB.lutPath)); float magField = 0.0; if (grpmag != nullptr) { @@ -1556,6 +1594,17 @@ struct AnalysisSameEventPairing { VarManager::SetupTwoProngDCAFitter(fConfigOptions.magField.value, true, 200.0f, 4.0f, 1.0e-3f, 0.9f, fConfigOptions.useAbsDCA.value); // needed because take in varmanager Bz from fgFitterTwoProngBarrel for PhiV calculations } } + + if (fConfigOptions.useFlowReso) { + TString PathFlow = fConfigCCDB.flowPath.value; + TString ccdbPathFlowSP = Form("%s/ScalarProduct", PathFlow.Data()); + TString ccdbPathFlowEP = Form("%s/EventPlane", PathFlow.Data()); + ResoFlowSP = fCCDB->getForTimeStamp(ccdbPathFlowSP.Data(), timestamp); + ResoFlowEP = fCCDB->getForTimeStamp(ccdbPathFlowEP.Data(), timestamp); + if (ResoFlowSP == nullptr || ResoFlowEP == nullptr) { + LOGF(fatal, "Flow resolution histograms not available in CCDB at timestamp=%llu", timestamp); + } + } } template @@ -1567,15 +1616,18 @@ struct AnalysisSameEventPairing { // int nMyPVContrib = 0; int nMyPVContribOrig = 0; for (auto const& trk : tracks) { // check if it is PV contributor - if (!trk.isPVContributor()) + if (!trk.isPVContributor()) { continue; + } // check if it contributes to the vtx of this collision - if (trk.collisionId() != collision.globalIndex()) + if (trk.collisionId() != collision.globalIndex()) { continue; + } // nMyPVContribOrig++; // --- remove t1 and t2 if they are PV contributors --- - if (trk.globalIndex() == t1.globalIndex() || trk.globalIndex() == t2.globalIndex()) + if (trk.globalIndex() == t1.globalIndex() || trk.globalIndex() == t2.globalIndex()) { continue; + } // add tracks and parameters to the list pvContribGlobIDs.push_back(trk.globalIndex()); pvContribTrackPars.push_back(getTrackParCov(trk)); @@ -1600,8 +1652,9 @@ struct AnalysisSameEventPairing { vertexer.init(); bool PVrefit_doable = vertexer.prepareVertexRefit(pvContribTrackPars, Pvtx); - if (!PVrefit_doable) + if (!PVrefit_doable) { return false; + } // --- do the refit --- pvRefitted = vertexer.refitVertex(vec_useTrk_PVrefit, Pvtx); @@ -1630,43 +1683,67 @@ struct AnalysisSameEventPairing { int sign1 = 0; int sign2 = 0; - dielectronList.reserve(1); - // dimuonList.reserve(1); - dielectronsExtraList.reserve(1); - // dimuonsExtraList.reserve(1); - dielectronInfoList.reserve(1); - dileptonInfoList.reserve(1); + // estimate reserved size + int64_t reserveSize = 0; + for (auto& event : events) { + if (event.isEventSelected_bit(0)) { + auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); + size_t nGood = 0; + for (auto const& t : groupedAssocs) { + if constexpr (TPairType == VarManager::kDecayToEE) { + if (t.isBarrelSelected_raw()) { + nGood++; + } + } + } + reserveSize += nGood * (nGood - 1) / 2; + } + } + dielectronList.reserve(reserveSize); + dielectronsExtraList.reserve(reserveSize); + dielectronInfoList.reserve(reserveSize); + dileptonInfoList.reserve(reserveSize); if (fConfigOptions.flatTables.value) { - dielectronAllList.reserve(1); - // dimuonAllList.reserve(1); + dielectronAllList.reserve(reserveSize); } - constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); + constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::CollisionQvectCentr) > 0); constexpr bool trackHasCov = ((TTrackFillMap & VarManager::ObjTypes::TrackCov) > 0); + // constexpr bool fillFlowReso = eventHasQvector; for (auto& event : events) { - if (!event.isEventSelected_bit(0)) + if (!event.isEventSelected_bit(0)) { continue; + } if (fConfigOptions.collSplitting && event.isEventSelected_bit(2)) { continue; } VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, static_cast(VarManager::fgValues)); auto groupedAssocs = assocs.sliceBy(preslice, event.globalIndex()); - if (groupedAssocs.size() == 0) + if (groupedAssocs.size() == 0) { continue; + } + + if (fConfigOptions.useFlowReso) { + if (ResoFlowSP == nullptr || ResoFlowEP == nullptr) { + LOGF(fatal, "Flow resolution histograms are not available, cannot fill flow variables!"); + } + VarManager::FillEventFlowResoFactor(ResoFlowSP, ResoFlowEP); + } for (auto& [a1, a2] : o2::soa::combinations(groupedAssocs, groupedAssocs)) { if constexpr (TPairType == VarManager::kDecayToEE) { twoTrackFilter = a1.isBarrelSelected_raw() & a2.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & a2.isBarrelSelectedPrefilter_raw() & fTrackFilterMask; - if (!twoTrackFilter) + if (!twoTrackFilter) { continue; + } auto t1 = a1.template track_as(); auto t2 = a2.template track_as(); @@ -1701,17 +1778,17 @@ struct AnalysisSameEventPairing { } if constexpr (TTwoProngFitter) { - VarManager::FillPairVertexing(event, t1, t2, fConfigOptions.propToPCA); + VarManager::FillPairVertexing(event, t1, t2); o2::dataformats::VertexBase pvRefit; if (fConfigOptions.recomputePV) { VarManager::SetPVrecalculationKF(false); VarManager::ResetValues(VarManager::kVertexingLxyProjectedRecalculatePV, VarManager::kVertexingLxyProjectedRecalculatePV + 1); VarManager::ResetValues(VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV, VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV + 1); // cout << "primary vertex (before): x -> " << event.posX() << " y -> " << event.posY() << " z -> " << event.posZ() << endl; - o2::dataformats::VertexBase pvRefit; bool ok = refitPVWithPVertexer(event, tracks, t1, t2, pvRefit); - if (ok) + if (ok) { VarManager::FillPairVertexingRecomputePV(event, t1, t2, pvRefit); + } // cout << "primary vertex (after): ok -> " << ok << " x -> " << pvRefit.getX() << " y -> " << pvRefit.getY() << " z -> " << pvRefit.getZ() << endl; } } @@ -1735,44 +1812,47 @@ struct AnalysisSameEventPairing { } if constexpr (eventHasQvector) { - VarManager::FillPairVn(t1, t2); + VarManager::FillPairVn(t1, t2); } } // Fill normal histograms - bool isAmbiInBunch = (twoTrackFilter & (1 << 28)) || (twoTrackFilter & (1 << 29)); - bool isAmbiOutOfBunch = (twoTrackFilter & (1 << 30)) || (twoTrackFilter & (1 << 31)); + bool isAmbiInBunch = (twoTrackFilter & (static_cast(1) << 28)) || (twoTrackFilter & (static_cast(1) << 29)); + bool isAmbiOutOfBunch = (twoTrackFilter & (static_cast(1) << 30)) || (twoTrackFilter & (static_cast(1) << 31)); for (int icut = 0; icut < ncuts; icut++) { // loop over cut definitions if (twoTrackFilter & (static_cast(1) << icut)) { if (sign1 * sign2 < 0) { // opposite sign pairs - fHistMan->FillHistClass(histNames[icut][0].Data(), VarManager::fgValues); - PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], isAmbiInBunch, isAmbiOutOfBunch, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); + fHistMan->FillHistClass(histNames[icut][0].Data(), static_cast(VarManager::fgValues)); + PromptNonPromptSepTable(VarManager::fgValues[VarManager::kMass], VarManager::fgValues[VarManager::kPt], VarManager::fgValues[VarManager::kEta], VarManager::fgValues[VarManager::kRap], VarManager::fgValues[VarManager::kPhi], + VarManager::fgValues[VarManager::kVertexingTauxyProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMass], VarManager::fgValues[VarManager::kVertexingTauzProjected], VarManager::fgValues[VarManager::kVertexingTauxyProjectedPoleJPsiMassRecalculatePV], + VarManager::fgValues[VarManager::kVtxX], VarManager::fgValues[VarManager::kVtxY], VarManager::fgValues[VarManager::kVtxZ], VarManager::fgValues[VarManager::kDCAxy1], VarManager::fgValues[VarManager::kDCAz1], VarManager::fgValues[VarManager::kITSclusterMap1], VarManager::fgValues[VarManager::kTPCnSigmaEl1], VarManager::fgValues[VarManager::kDCAxy2], VarManager::fgValues[VarManager::kDCAz2], VarManager::fgValues[VarManager::kITSclusterMap2], VarManager::fgValues[VarManager::kTPCnSigmaEl2], + isAmbiInBunch, isAmbiOutOfBunch, VarManager::fgValues[VarManager::kMultFT0A], VarManager::fgValues[VarManager::kMultFT0C], VarManager::fgValues[VarManager::kCentFT0M], VarManager::fgValues[VarManager::kVtxNcontribReal]); if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][3 + 3].Data(), static_cast(VarManager::fgValues)); } } } else if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(histNames[icut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][1].Data(), static_cast(VarManager::fgValues)); if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][4].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][4 + 3].Data(), static_cast(VarManager::fgValues)); } } } else { // -- pairs - fHistMan->FillHistClass(histNames[icut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][2].Data(), static_cast(VarManager::fgValues)); if (fConfigOptions.fConfigQA) { if (isAmbiInBunch) { - fHistMan->FillHistClass(histNames[icut][5].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5].Data(), static_cast(VarManager::fgValues)); } if (isAmbiOutOfBunch) { - fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[icut][5 + 3].Data(), static_cast(VarManager::fgValues)); } } } @@ -1780,14 +1860,15 @@ struct AnalysisSameEventPairing { // Pair cuts for (unsigned int iPairCut = 0; iPairCut < fPairCuts.size(); iPairCut++) { AnalysisCompositeCut cut = fPairCuts.at(iPairCut); - if (!cut.IsSelected(VarManager::fgValues)) - continue; // apply pair cuts + if (!cut.IsSelected(static_cast(VarManager::fgValues))) { + continue; // apply pair cuts + } if (sign1 * sign2 < 0) { // opposite sign pairs - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][0].Data(), static_cast(VarManager::fgValues)); } else if (sign1 > 0) { // ++ pairs - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][1].Data(), static_cast(VarManager::fgValues)); } else { // -- pairs - fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(histNames[ncuts + icut * fPairCuts.size() + iPairCut][2].Data(), static_cast(VarManager::fgValues)); } } // end loop (pair cuts) } @@ -1823,26 +1904,32 @@ struct AnalysisSameEventPairing { constexpr bool eventHasQvector = ((TEventFillMap & VarManager::ObjTypes::CollisionQvect) > 0); for (auto& event : events) { - if (!event.isEventSelected_bit(0)) + if (!event.isEventSelected_bit(0)) { continue; - if (fConfigOptions.collSplitting && event.isEventSelected_bit(2)) + } + if (fConfigOptions.collSplitting && event.isEventSelected_bit(2)) { continue; + } VarManager::ResetValues(0, VarManager::kNVars); - VarManager::FillEvent(event, VarManager::fgValues); + VarManager::FillEvent(event, static_cast(VarManager::fgValues)); auto groupedAssocs1 = assocs1.sliceBy(preslice1, event.globalIndex()); - if (groupedAssocs1.size() == 0) + if (groupedAssocs1.size() == 0) { continue; + } auto groupedAssocs2 = assocs2.sliceBy(preslice2, event.globalIndex()); - if (groupedAssocs2.size() == 0) + if (groupedAssocs2.size() == 0) { continue; + } for (auto& [a1, a2] : o2::soa::combinations(soa::CombinationsFullIndexPolicy(groupedAssocs1, groupedAssocs2))) { - if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) + if (!(a1.isBarrelSelected_raw() & a1.isBarrelSelectedPrefilter_raw() & fTrackFilterMask)) { continue; - if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) + } + if (!(a2.isMuonSelected_raw() & fMuonFilterMask)) { continue; + } auto t1 = a1.template track_as(); auto t2 = a2.template fwdtrack_as(); @@ -1857,14 +1944,18 @@ struct AnalysisSameEventPairing { } } // store ambiguity flags in bits 28-31 - if (t1.barrelAmbiguityInBunch() > 1) + if (t1.barrelAmbiguityInBunch() > 1) { twoTrackFilter |= (1u << 28); - if (t2.muonAmbiguityInBunch() > 1) + } + if (t2.muonAmbiguityInBunch() > 1) { twoTrackFilter |= (1u << 29); - if (t1.barrelAmbiguityOutOfBunch() > 1) + } + if (t1.barrelAmbiguityOutOfBunch() > 1) { twoTrackFilter |= (1u << 30); - if (t2.muonAmbiguityOutOfBunch() > 1) + } + if (t2.muonAmbiguityOutOfBunch() > 1) { twoTrackFilter |= (1u << 31); + } VarManager::FillPair(t1, t2); if (fConfigOptions.fPropTrack) { @@ -1880,21 +1971,24 @@ struct AnalysisSameEventPairing { t1.sign() + t2.sign(), twoTrackFilter, 0); for (int iTrack = 0; iTrack < fNCutsBarrel; ++iTrack) { - if (!(a1.isBarrelSelected_raw() & (1u << iTrack))) + if (!(a1.isBarrelSelected_raw() & (1u << iTrack))) { continue; + } for (int iMuon = 0; iMuon < fNCutsMuon; ++iMuon) { - if (!(a2.isMuonSelected_raw() & (1u << iMuon))) + if (!(a2.isMuonSelected_raw() & (1u << iMuon))) { continue; + } int index = iTrack * fNCutsMuon + iMuon; auto itHist = histNames.find(index); - if (itHist == histNames.end()) + if (itHist == histNames.end()) { continue; + } if (sign1 * sign2 < 0) { - fHistMan->FillHistClass(itHist->second[0].Data(), VarManager::fgValues); + fHistMan->FillHistClass(itHist->second[0].Data(), static_cast(VarManager::fgValues)); } else if (sign1 > 0) { - fHistMan->FillHistClass(itHist->second[1].Data(), VarManager::fgValues); + fHistMan->FillHistClass(itHist->second[1].Data(), static_cast(VarManager::fgValues)); } else { - fHistMan->FillHistClass(itHist->second[2].Data(), VarManager::fgValues); + fHistMan->FillHistClass(itHist->second[2].Data(), static_cast(VarManager::fgValues)); } } // end muon cut loop } // end barrel cut loop @@ -1924,10 +2018,18 @@ struct AnalysisSameEventPairing { muonAssocsPerCollision, muonAssocs, muons); } + void processBarrelOnlyWithQvectorCentr(MyEventsQvectorCentrSelected const& events, BCsWithTimestamps const& bcs, + soa::Join const& barrelAssocs, + MyBarrelTracksWithCovWithAmbiguities const& barrelTracks) + { + runSameEventPairing(events, bcs, trackAssocsPerCollision, barrelAssocs, barrelTracks); + } + void processDummy(MyEvents&) { /* do nothing */ } PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnly, "Run barrel only pairing", false); PROCESS_SWITCH(AnalysisSameEventPairing, processElectronMuonDirect, "Run electron-muon pairing on AO2D tracks/fwd-tracks", false); + PROCESS_SWITCH(AnalysisSameEventPairing, processBarrelOnlyWithQvectorCentr, "Run barrel only pairing with Q-vector and centrality", false); PROCESS_SWITCH(AnalysisSameEventPairing, processDummy, "Dummy function", true); }; @@ -1944,7 +2046,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) // adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, const char* histGroups) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const char* histGroups) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGDQ/Tasks/tableReader_withAssoc_workflowSpec.cxx b/PWGDQ/Tasks/tableReader_withAssoc_workflowSpec.cxx new file mode 100644 index 00000000000..0097ffe5692 --- /dev/null +++ b/PWGDQ/Tasks/tableReader_withAssoc_workflowSpec.cxx @@ -0,0 +1,33 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no +// Configurable workflow for running several DQ or other PWG analyses + +#include "PWGDQ/Tasks/tableReader_withAssoc.h" + +#include +#include + +using namespace o2::framework; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGDQ/Tasks/taskFwdTrackPid.cxx b/PWGDQ/Tasks/taskFwdTrackPid.cxx index 8f7febbbf28..d79c5c3c9e7 100644 --- a/PWGDQ/Tasks/taskFwdTrackPid.cxx +++ b/PWGDQ/Tasks/taskFwdTrackPid.cxx @@ -56,7 +56,7 @@ constexpr static uint32_t gkEventFillMap = VarManager::ObjTypes::ReducedEvent | constexpr static uint32_t gkMCEventFillMap = VarManager::ObjTypes::ReducedEventMC; constexpr static uint32_t gkMuonFillMap = VarManager::ObjTypes::ReducedMuon | VarManager::ObjTypes::ReducedMuonExtra; -void DefineHistograms(HistogramManager* histMan, TString histClasses); +void DefineHistograms(HistogramManager* histMan, const TString& histClasses); struct taskFwdTrackPid { Produces fwdPidAllList; @@ -131,7 +131,6 @@ struct taskFwdTrackPid { template void runFwdTrackPid(TEvent const& event, Muons const& muons, MftTracks const& mftTracks) { - fwdPidAllList.reserve(1); for (const auto& muon : muons) { if (muon.has_matchMFTTrack() && muon.trackType() == 0 && TMath::Abs(muon.fwdDcaX()) < fConfigMaxDCA && TMath::Abs(muon.fwdDcaY()) < fConfigMaxDCA) { auto mftTrack = muon.template matchMFTTrack_as(); @@ -157,7 +156,6 @@ struct taskFwdTrackPid { template void runFwdTrackPidMC(TEvent const& event, Muons const& muons, MftTracks const& mftTracks, TEventsMC const& /*eventsMC*/, TTracksMC const& /*tracksMC*/) { - fwdPidAllList.reserve(1); for (const auto& muon : muons) { if (muon.has_matchMFTTrack() && muon.trackType() == 0 && TMath::Abs(muon.fwdDcaX()) < fConfigMaxDCA && TMath::Abs(muon.fwdDcaY()) < fConfigMaxDCA) { auto mftTrack = muon.template matchMFTTrack_as(); @@ -243,7 +241,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses) { std::unique_ptr objArray(histClasses.Tokenize(";")); for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { diff --git a/PWGDQ/Tasks/taskJpsiHf.cxx b/PWGDQ/Tasks/taskJpsiHf.cxx index b41950b4056..2c5135e93ba 100644 --- a/PWGDQ/Tasks/taskJpsiHf.cxx +++ b/PWGDQ/Tasks/taskJpsiHf.cxx @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -55,10 +56,16 @@ static const std::vector labelsPt{}; static const std::vector labelsCutsBdt = {"BDT background", "BDT prompt", "BDT nonprompt"}; } // namespace bdtcuts +enum class DmesonType { + D0, + Dplus +}; + // Declarations of various short names using MyRedEvents = aod::RedJpDmColls; using MyRedPairCandidatesSelected = aod::RedJpDmDileptons; using MyRedD0CandidatesSelected = soa::Join; +using MyRedDplusCandidatesSelected = soa::Join; struct taskJPsiHf { // @@ -80,7 +87,8 @@ struct taskJPsiHf { // Preslices for unsorted indexes PresliceUnsorted perCollisionDilepton = aod::jpsidmescorr::redJpDmCollId; - PresliceUnsorted perCollisionDmeson = aod::jpsidmescorr::redJpDmCollId; + PresliceUnsorted perCollisionD0 = aod::jpsidmescorr::redJpDmCollId; + PresliceUnsorted perCollisionDplus = aod::jpsidmescorr::redJpDmCollId; // histogram for normalisation std::shared_ptr hCollisions; @@ -95,7 +103,7 @@ struct taskJPsiHf { // Template function to run pair - hadron combinations // TODO: generalise to all charm-hadron species - template + template void runDileptonDmeson(TEvent const& /*event*/, TDqTrack const& dileptons, THfTrack const& dmesons) { float ptDilepton = -999; @@ -123,29 +131,55 @@ struct taskJPsiHf { continue; } - auto minItsClsDmesDau = (dmeson.numItsClsDmesProng0() < dmeson.numItsClsDmesProng1()) ? dmeson.numItsClsDmesProng0() : dmeson.numItsClsDmesProng1(); - auto minTpcCrossRowsDmesDau = (dmeson.numTpcCrossedRowsDmesProng0() < dmeson.numTpcCrossedRowsDmesProng1()) ? dmeson.numTpcCrossedRowsDmesProng0() : dmeson.numTpcCrossedRowsDmesProng1(); - auto minPtDmesDau = (dmeson.ptDmesProng0() < dmeson.ptDmesProng1()) ? dmeson.ptDmesProng0() : dmeson.ptDmesProng1(); - auto minAbsEtaDmesDau = (std::abs(dmeson.etaDmesProng0()) < std::abs(dmeson.etaDmesProng1())) ? std::abs(dmeson.etaDmesProng0()) : std::abs(dmeson.etaDmesProng1()); - - if (dmeson.massD0() > 0) { - rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); - deltaRap = rapDilepton - rapDmeson; - auto bdtBkg = dmeson.bdtBkgMassHypo0(); - auto bdtPrompt = dmeson.bdtPromptMassHypo0(); - auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); - if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0() > massHfCandMin && dmeson.massD0() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { - redDileptDimesAll(dilepton.mass(), dmeson.massD0(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + int minItsClsDmesDau = 0; + int minTpcCrossRowsDmesDau = 0; + float minPtDmesDau = 0.f; + float minAbsEtaDmesDau = 0.f; + if constexpr (TDmeson == DmesonType::D0) { + minItsClsDmesDau = (dmeson.numItsClsDmesProng0() < dmeson.numItsClsDmesProng1()) ? dmeson.numItsClsDmesProng0() : dmeson.numItsClsDmesProng1(); + minTpcCrossRowsDmesDau = (dmeson.numTpcCrossedRowsDmesProng0() < dmeson.numTpcCrossedRowsDmesProng1()) ? dmeson.numTpcCrossedRowsDmesProng0() : dmeson.numTpcCrossedRowsDmesProng1(); + minPtDmesDau = (dmeson.ptDmesProng0() < dmeson.ptDmesProng1()) ? dmeson.ptDmesProng0() : dmeson.ptDmesProng1(); + minAbsEtaDmesDau = (std::abs(dmeson.etaDmesProng0()) < std::abs(dmeson.etaDmesProng1())) ? std::abs(dmeson.etaDmesProng0()) : std::abs(dmeson.etaDmesProng1()); + } + if constexpr (TDmeson == DmesonType::Dplus) { + minItsClsDmesDau = std::min({dmeson.numItsClsDmesProng0(), dmeson.numItsClsDmesProng1(), dmeson.numItsClsDmesProng2()}); + minTpcCrossRowsDmesDau = std::min({dmeson.numTpcCrossedRowsDmesProng0(), dmeson.numTpcCrossedRowsDmesProng1(), dmeson.numTpcCrossedRowsDmesProng2()}); + minPtDmesDau = std::min({dmeson.ptDmesProng0(), dmeson.ptDmesProng1(), dmeson.ptDmesProng2()}); + minAbsEtaDmesDau = std::min({std::abs(dmeson.etaDmesProng0()), std::abs(dmeson.etaDmesProng1()), std::abs(dmeson.etaDmesProng2())}); + } + + if constexpr (TDmeson == DmesonType::D0) { + if (dmeson.massD0() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo0(); + auto bdtPrompt = dmeson.bdtPromptMassHypo0(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0() > massHfCandMin && dmeson.massD0() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massD0(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } + } + if (dmeson.massD0bar() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo1(); + auto bdtPrompt = dmeson.bdtPromptMassHypo1(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo1(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0bar() > massHfCandMin && dmeson.massD0bar() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massD0bar(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } } } - if (dmeson.massD0bar() > 0) { - rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassD0); - deltaRap = rapDilepton - rapDmeson; - auto bdtBkg = dmeson.bdtBkgMassHypo1(); - auto bdtPrompt = dmeson.bdtPromptMassHypo1(); - auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo1(); - if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massD0bar() > massHfCandMin && dmeson.massD0bar() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { - redDileptDimesAll(dilepton.mass(), dmeson.massD0bar(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + if constexpr (TDmeson == DmesonType::Dplus) { + if (dmeson.massDmes() > 0) { + rapDmeson = RecoDecay::y(std::array{dmeson.px(), dmeson.py(), dmeson.pz()}, constants::physics::MassDPlus); + deltaRap = rapDilepton - rapDmeson; + auto bdtBkg = dmeson.bdtBkgMassHypo0(); + auto bdtPrompt = dmeson.bdtPromptMassHypo0(); + auto bdtNonPrompt = dmeson.bdtNonpromptMassHypo0(); + if ((dilepton.mass() > massDileptonCandMin && dilepton.mass() < massDileptonCandMax) && (dmeson.massDmes() > massHfCandMin && dmeson.massDmes() < massHfCandMax && bdtBkg < cutsDmesBdt->get(ptBinDmesForBdt, "BDT background") && bdtPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT prompt") && bdtNonPrompt > cutsDmesBdt->get(ptBinDmesForBdt, "BDT nonprompt"))) { + redDileptDimesAll(dilepton.mass(), dmeson.massDmes(), ptDilepton, ptDmeson, rapDilepton, rapDmeson, phiDilepton, phiDmeson, deltaRap, deltaPhi, bdtBkg, bdtPrompt, bdtNonPrompt, minItsClsDmesDau, minTpcCrossRowsDmesDau, minPtDmesDau, minAbsEtaDmesDau); + } } } } @@ -158,8 +192,19 @@ struct taskJPsiHf { for (auto& event : events) { hCollisions->Fill(1.f); auto groupedDileptonCandidates = dileptons.sliceBy(perCollisionDilepton, event.index()); - auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionDmeson, event.index()); - runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); + auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionD0, event.index()); + runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); + } + } + + void processRedJspiDplus(MyRedEvents const& events, MyRedPairCandidatesSelected const& dileptons, MyRedDplusCandidatesSelected const& dmesons) + { + // Fill the column of collisions with pairs + for (auto& event : events) { + hCollisions->Fill(1.f); + auto groupedDileptonCandidates = dileptons.sliceBy(perCollisionDilepton, event.index()); + auto groupedDmesonCandidates = dmesons.sliceBy(perCollisionDplus, event.index()); + runDileptonDmeson(event, groupedDileptonCandidates, groupedDmesonCandidates); } } @@ -172,6 +217,7 @@ struct taskJPsiHf { } PROCESS_SWITCH(taskJPsiHf, processRedJspiD0, "Process J/psi - D0", true); + PROCESS_SWITCH(taskJPsiHf, processRedJspiDplus, "Process J/psi - Dplus", true); PROCESS_SWITCH(taskJPsiHf, processNormCounter, "Process normalization counter", true); }; diff --git a/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx b/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx index 999b46bd148..806adcf79e8 100644 --- a/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx +++ b/PWGDQ/Tasks/taskMuonMchTrkEfficiency.cxx @@ -110,7 +110,7 @@ struct taskMuonMchTrkEfficiency { // define axes to be used // and the correspondent histograms - auto vbins = (std::vector)binsMuonPt; + auto vbins = std::vector(binsMuonPt); const AxisSpec axisEta{nEtaBins, etaMuonMin, etaMuonMax, "|#eta|"}; const AxisSpec axisPt{vbins, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec axisPhi{nPhiBins, -3.14, 3.14, "#varphi"}; diff --git a/PWGDQ/Tasks/v0selector.cxx b/PWGDQ/Tasks/v0selector.cxx index f464459adf0..807d3295f55 100644 --- a/PWGDQ/Tasks/v0selector.cxx +++ b/PWGDQ/Tasks/v0selector.cxx @@ -387,8 +387,8 @@ struct v0selector { registry.fill(HIST("hV0Psi"), psipair, mGamma); } if (mGamma < v0max_mee && std::abs(V0.posTrack_as().tpcNSigmaEl()) < cutNsigmaElTPC && std::abs(V0.negTrack_as().tpcNSigmaEl()) < cutNsigmaElTPC && std::abs(psipair) < maxpsipair) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kGamma); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kGamma); + pidmap[V0.posTrackId()] |= (static_cast(1) << kGamma); + pidmap[V0.negTrackId()] |= (static_cast(1) << kGamma); storeV0AddID(V0.globalIndex(), kGamma); if (fillhisto) { registry.fill(HIST("hGammaRxy"), V0.x(), V0.y()); @@ -402,8 +402,8 @@ struct v0selector { registry.fill(HIST("hMassK0SPhi"), V0.phi(), mK0S); } if ((0.48 < mK0S && mK0S < 0.51) && std::abs(V0.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(V0.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kK0S); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kK0S); + pidmap[V0.posTrackId()] |= (static_cast(1) << kK0S); + pidmap[V0.negTrackId()] |= (static_cast(1) << kK0S); storeV0AddID(V0.globalIndex(), kK0S); } } else if (v0id == kLambda) { // L->p + pi- @@ -411,8 +411,8 @@ struct v0selector { registry.fill(HIST("hMassLambda"), V0radius, mLambda); } if (v0id == kLambda && (1.110 < mLambda && mLambda < 1.120) && std::abs(V0.posTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(V0.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kLambda); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kLambda); + pidmap[V0.posTrackId()] |= (static_cast(1) << kLambda); + pidmap[V0.negTrackId()] |= (static_cast(1) << kLambda); storeV0AddID(V0.globalIndex(), kLambda); } } else if (v0id == kAntiLambda) { // Lbar -> pbar + pi+ @@ -420,8 +420,8 @@ struct v0selector { registry.fill(HIST("hMassAntiLambda"), V0radius, mAntiLambda); } if ((1.110 < mAntiLambda && mAntiLambda < 1.120) && std::abs(V0.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(V0.negTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC) { - pidmap[V0.posTrackId()] |= (uint8_t(1) << kAntiLambda); - pidmap[V0.negTrackId()] |= (uint8_t(1) << kAntiLambda); + pidmap[V0.posTrackId()] |= (static_cast(1) << kAntiLambda); + pidmap[V0.negTrackId()] |= (static_cast(1) << kAntiLambda); storeV0AddID(V0.globalIndex(), kAntiLambda); } } @@ -543,7 +543,7 @@ struct v0selector { registry.fill(HIST("hMassOmega"), cascRadius, mOmega); } if (cutMassOmegaLow < mOmega && mOmega < cutMassOmegaHigh && cutMassCascV0Low < mV0Lambda && mV0Lambda < cutMassCascV0High && std::abs(casc.posTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(casc.negTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(casc.bachelor_as().tpcNSigmaKa()) < cutNsigmaKaTPC) { - pidmap[casc.bachelorId()] |= (uint8_t(1) << kOmega); + pidmap[casc.bachelorId()] |= (static_cast(1) << kOmega); storeCascAddID(casc.globalIndex(), kOmega); } } else if (cascid == kAntiOmega && v0id == kAntiLambda) { @@ -551,7 +551,7 @@ struct v0selector { registry.fill(HIST("hMassAntiOmega"), cascRadius, mOmega); } if (cutMassOmegaLow < mOmega && mOmega < cutMassOmegaHigh && cutMassCascV0Low < mV0Lambda && mV0Lambda < cutMassCascV0High && std::abs(casc.posTrack_as().tpcNSigmaPi()) < cutNsigmaPiTPC && std::abs(casc.negTrack_as().tpcNSigmaPr()) < cutNsigmaPrTPC && std::abs(casc.bachelor_as().tpcNSigmaKa()) < cutNsigmaKaTPC) { - pidmap[casc.bachelorId()] |= (uint8_t(1) << kAntiOmega); + pidmap[casc.bachelorId()] |= (static_cast(1) << kAntiOmega); storeCascAddID(casc.globalIndex(), kAntiOmega); } } @@ -762,7 +762,7 @@ struct trackPIDQA { } // end of track loop } // end of process - void DefineHistograms(TString histClasses) + void DefineHistograms(const TString& histClasses) { std::unique_ptr objArray(histClasses.Tokenize(";")); for (Int_t iclass = 0; iclass < objArray->GetEntries(); ++iclass) { diff --git a/PWGEM/Dilepton/CMakeLists.txt b/PWGEM/Dilepton/CMakeLists.txt index 7ff980baa4f..a36a3f0190a 100644 --- a/PWGEM/Dilepton/CMakeLists.txt +++ b/PWGEM/Dilepton/CMakeLists.txt @@ -13,4 +13,4 @@ add_subdirectory(Core) # add_subdirectory(DataModel) add_subdirectory(Tasks) add_subdirectory(TableProducer) - +add_subdirectory(Utils) diff --git a/PWGEM/Dilepton/Core/DielectronCut.cxx b/PWGEM/Dilepton/Core/DielectronCut.cxx index 64d564cf36a..006a0fa5252 100644 --- a/PWGEM/Dilepton/Core/DielectronCut.cxx +++ b/PWGEM/Dilepton/Core/DielectronCut.cxx @@ -17,6 +17,8 @@ #include +#include + #include #include @@ -61,7 +63,7 @@ void DielectronCut::SetPairOpAng(float minOpAng, float maxOpAng) } void DielectronCut::SetMaxMeePhiVDep(std::function phivDepCut, float min_phiv, float max_phiv) { - mMaxMeePhiVDep = phivDepCut; + mMaxMeePhiVDep = std::move(phivDepCut); mMinPhivPair = min_phiv; mMaxPhivPair = max_phiv; LOG(info) << "Dielectron Cut, set max mee phiv dep: " << mMaxMeePhiVDep(2.5); @@ -79,11 +81,11 @@ void DielectronCut::SetMindEtadPhi(bool flag1, bool flag2, float min_deta, float mMinDeltaPhi = min_dphi; LOG(info) << "Dielectron Cut, set apply deta-dphi cut: " << mApplydEtadPhi << " apply deta-dphi* cut: " << mApplydEtadPhiPosition << " min_deta: " << mMinDeltaEta << " min_dphi: " << mMinDeltaPhi; } -void DielectronCut::SetRequireDifferentSides(bool flag) -{ - mRequireDiffSides = flag; - LOG(info) << "Dielectron Cut, require 2 tracks to be from different sides: " << mRequireDiffSides; -} +// void DielectronCut::SetRequireDifferentSides(bool flag) +// { +// mRequireDiffSides = flag; +// LOG(info) << "Dielectron Cut, require 2 tracks to be from different sides: " << mRequireDiffSides; +// } void DielectronCut::SetTrackPtRange(float minPt, float maxPt) { mMinTrackPt = minPt; @@ -163,8 +165,6 @@ void DielectronCut::SetMeanClusterSizeITS(float min, float max) { mMinMeanClusterSizeITS = min; mMaxMeanClusterSizeITS = max; - // mMinP_ITSClusterSize = minP; - // mMaxP_ITSClusterSize = maxP; LOG(info) << "Dielectron Cut, set mean cluster size ITS range: " << mMinMeanClusterSizeITS << " - " << mMaxMeanClusterSizeITS; } void DielectronCut::SetChi2TOF(float min, float max) @@ -174,26 +174,27 @@ void DielectronCut::SetChi2TOF(float min, float max) LOG(info) << "Dielectron Cut, set chi2 TOF range: " << mMinChi2TOF << " - " << mMaxChi2TOF; } -void DielectronCut::SetTrackDca3DRange(float min, float max) +void DielectronCut::SetTrackMaxDcaSigma(float max, uint dcaType) { - mMinDca3D = min; - mMaxDca3D = max; - LOG(info) << "Dielectron Cut, set DCA 3D range in sigma: " << mMinDca3D << " - " << mMaxDca3D; + mMaxDcaSigma = max; + mDCAType = dcaType; + LOG(info) << "Dielectron Cut, set max DCA in sigma: " << mMaxDcaSigma; + LOG(info) << "Dielectron Cut, set DCA type: " << mDCAType; } void DielectronCut::SetTrackMaxDcaXY(float maxDcaXY) { mMaxDcaXY = maxDcaXY; - LOG(info) << "Dielectron Cut, set max DCA xy: " << mMaxDcaXY; + LOG(info) << "Dielectron Cut, set max DCA xy in cm: " << mMaxDcaXY; } void DielectronCut::SetTrackMaxDcaZ(float maxDcaZ) { mMaxDcaZ = maxDcaZ; - LOG(info) << "Dielectron Cut, set max DCA z: " << mMaxDcaZ; + LOG(info) << "Dielectron Cut, set max DCA z in cm: " << mMaxDcaZ; } void DielectronCut::SetTrackMaxDcaXYPtDep(std::function ptDepCut) { - mMaxDcaXYPtDep = ptDepCut; + mMaxDcaXYPtDep = std::move(ptDepCut); LOG(info) << "Dielectron Cut, set max DCA xy pt dep: " << mMaxDcaXYPtDep(1.0); } void DielectronCut::ApplyPhiV(bool flag) @@ -201,11 +202,6 @@ void DielectronCut::ApplyPhiV(bool flag) mApplyPhiV = flag; LOG(info) << "Dielectron Cut, apply phiv cut: " << mApplyPhiV; } -void DielectronCut::ApplyPrefilter(bool flag) -{ - mApplyPF = flag; - LOG(info) << "Dielectron Cut, apply prefilter: " << mApplyPF; -} void DielectronCut::SetPIDScheme(int scheme) { @@ -323,20 +319,6 @@ void DielectronCut::SetTOFNsigmaPrRange(float min, float max) // mMaxITSNsigmaPr = max; // LOG(info) << "Dielectron Cut, set ITS n sigma Pr range: " << mMinITSNsigmaPr << " - " << mMaxITSNsigmaPr; // } -// -// void DielectronCut::SetPRangeForITSNsigmaKa(float min, float max) -// { -// mMinP_ITSNsigmaKa = min; -// mMaxP_ITSNsigmaKa = max; -// LOG(info) << "Dielectron Cut, set p range for ITS n sigma Ka: " << mMinP_ITSNsigmaKa << " - " << mMaxP_ITSNsigmaKa; -// } -// -// void DielectronCut::SetPRangeForITSNsigmaPr(float min, float max) -// { -// mMinP_ITSNsigmaPr = min; -// mMaxP_ITSNsigmaPr = max; -// LOG(info) << "Dielectron Cut, set p range for ITS n sigma Pr: " << mMinP_ITSNsigmaPr << " - " << mMaxP_ITSNsigmaPr; -// } void DielectronCut::SetPinRangeForPionRejectionTPC(float min, float max) { diff --git a/PWGEM/Dilepton/Core/DielectronCut.h b/PWGEM/Dilepton/Core/DielectronCut.h index feba64b0410..8a41649e992 100644 --- a/PWGEM/Dilepton/Core/DielectronCut.h +++ b/PWGEM/Dilepton/Core/DielectronCut.h @@ -29,9 +29,12 @@ #include #include +#include + #include #include +#include #include #include #include @@ -66,14 +69,13 @@ class DielectronCut : public TNamed kTPCFracSharedClusters, kRelDiffPin, kTPCChi2NDF, - kDCA3Dsigma, + kDCAsigma, kDCAxy, kDCAz, kITSNCls, kITSChi2NDF, kITSClusterSize, kTTCA, - kPrefilter, kNCuts }; @@ -89,33 +91,33 @@ class DielectronCut : public TNamed kTPChadrejORTOFreqLowB = 7, }; - template - bool IsSelected(TPair const& pair) const - { - auto t1 = std::get<0>(pair); - auto t2 = std::get<1>(pair); - float bz = std::get<2>(pair); + // template + // bool IsSelected(TPair const& pair) const + // { + // auto t1 = std::get<0>(pair); + // auto t2 = std::get<1>(pair); + // float bz = std::get<2>(pair); - if (!IsSelectedTrack(t1) || !IsSelectedTrack(t2)) { - return false; - } + // if (!IsSelectedTrack(t1) || !IsSelectedTrack(t2)) { + // return false; + // } - if (!IsSelectedPair(t1, t2, bz)) { - return false; - } + // if (!IsSelectedPair(t1, t2, bz)) { + // return false; + // } - return true; - } + // return true; + // } template - bool IsSelectedPair(TTrack1 const& t1, TTrack2 const& t2, const float bz, const float refR) const + bool IsSelectedPair(TTrack1 const& t1, TTrack2 const& t2) const { ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; float dca_ee_3d = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), bz); + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), mBz); float opAng = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz()); if (v12.M() < mMinMee || mMaxMee < v12.M()) { @@ -144,31 +146,30 @@ class DielectronCut : public TNamed return false; } - if (mRequireDiffSides && t1.eta() * t2.eta() > 0.0) { - return false; - } + // if (mRequireDiffSides && t1.eta() * t2.eta() > 0.0) { + // return false; + // } if (mApplydEtadPhi && mApplydEtadPhiPosition) { // applying both cuts is not allowed. return false; } float deta = v1.Eta() - v2.Eta(); - float dphi = v1.Phi() - v2.Phi(); - o2::math_utils::bringToPMPi(dphi); + float dphi = RecoDecay::constrainAngle(v1.Phi(), 0, 1U) - RecoDecay::constrainAngle(v2.Phi(), 0, 1U); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi if (mApplydEtadPhi && std::pow(deta / mMinDeltaEta, 2) + std::pow(dphi / mMinDeltaPhi, 2) < 1.f) { return false; } - float phiPosition1 = t1.phi() + std::asin(t1.sign() * 0.30282 * (bz * 0.1) * refR / (2.f * t1.pt())); - float phiPosition2 = t2.phi() + std::asin(t2.sign() * 0.30282 * (bz * 0.1) * refR / (2.f * t2.pt())); - - phiPosition1 = RecoDecay::constrainAngle(phiPosition1, 0, 1); // 0-2pi - phiPosition2 = RecoDecay::constrainAngle(phiPosition2, 0, 1); // 0-2pi - float dphiPosition = phiPosition1 - phiPosition2; - o2::math_utils::bringToPMPi(dphiPosition); - if (mApplydEtadPhiPosition && std::pow(deta / mMinDeltaEta, 2) + std::pow(dphiPosition / mMinDeltaPhi, 2) < 1.f) { - return false; - } + // float phiPosition1 = t1.phi() + std::asin(t1.sign() * -0.30282 * (mBz * 0.1) * mRefR / (2.f * t1.pt())); + // float phiPosition2 = t2.phi() + std::asin(t2.sign() * -0.30282 * (mBz * 0.1) * mRefR / (2.f * t2.pt())); + // phiPosition1 = RecoDecay::constrainAngle(phiPosition1, 0, 1U); // 0-2pi + // phiPosition2 = RecoDecay::constrainAngle(phiPosition2, 0, 1U); // 0-2pi + // float dphiPosition = phiPosition1 - phiPosition2; + // dphiPosition = RecoDecay::constrainAngle(dphiPosition, -M_PI, 1U); // -pi - +pi + // if (mApplydEtadPhiPosition && std::pow(deta / mMinDeltaEta, 2) + std::pow(dphiPosition / mMinDeltaPhi, 2) < 1.f) { + // return false; + // } return true; } @@ -176,9 +177,9 @@ class DielectronCut : public TNamed template bool IsSelectedTrack(TTrack const& track) const { - if (!track.hasITS() || !track.hasTPC()) { - return false; - } + // if (!track.hasITS() || !track.hasTPC()) { + // return false; + // } if (!dont_require_pteta) { if (!IsSelectedTrack(track, DielectronCuts::kTrackPtRange)) { @@ -195,7 +196,7 @@ class DielectronCut : public TNamed if (!IsSelectedTrack(track, DielectronCuts::kTrackPhiPositionRange)) { return false; } - if (!IsSelectedTrack(track, DielectronCuts::kDCA3Dsigma)) { + if (!IsSelectedTrack(track, DielectronCuts::kDCAsigma)) { return false; } if (!IsSelectedTrack(track, DielectronCuts::kDCAxy)) { @@ -263,9 +264,6 @@ class DielectronCut : public TNamed return false; } } - if (mApplyPF && !IsSelectedTrack(track, DielectronCuts::kPrefilter)) { - return false; - } // PID cuts if (!PassPID(track)) { @@ -336,8 +334,6 @@ class DielectronCut : public TNamed bool is_el_included_TPC = mMinTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < mMaxTPCNsigmaEl; bool is_pi_excluded_TPC = (track.tpcInnerParam() > mMinPinForPionRejectionTPC && track.tpcInnerParam() < mMaxPinForPionRejectionTPC) ? (track.tpcNSigmaPi() < mMinTPCNsigmaPi || mMaxTPCNsigmaPi < track.tpcNSigmaPi()) : true; bool is_el_included_TOF = (mMinTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < mMaxTOFNsigmaEl) && (track.hasTOF() && track.tofChi2() < mMaxChi2TOF); - // bool is_ka_excluded_ITS = (mMinP_ITSNsigmaKa < track.p() && track.p() < mMaxP_ITSNsigmaKa) ? (track.itsNSigmaKa() < mMinITSNsigmaKa || mMaxITSNsigmaKa < track.itsNSigmaKa()) : true; - // bool is_pr_excluded_ITS = (mMinP_ITSNsigmaPr < track.p() && track.p() < mMaxP_ITSNsigmaPr) ? (track.itsNSigmaPr() < mMinITSNsigmaPr || mMaxITSNsigmaPr < track.itsNSigmaPr()) : true; return is_el_included_TPC && is_pi_excluded_TPC && is_el_included_TOF; } @@ -347,8 +343,6 @@ class DielectronCut : public TNamed bool is_el_included_TPC = mMinTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < mMaxTPCNsigmaEl; bool is_pi_excluded_TPC = track.tpcInnerParam() < mMaxPinForPionRejectionTPC ? (track.tpcNSigmaPi() < mMinTPCNsigmaPi || mMaxTPCNsigmaPi < track.tpcNSigmaPi()) : true; bool is_el_included_TOF = (mMinTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < mMaxTOFNsigmaEl) && (track.hasTOF() && track.tofChi2() < mMaxChi2TOF); - // bool is_ka_excluded_ITS = (mMinP_ITSNsigmaKa < track.p() && track.p() < mMaxP_ITSNsigmaKa) ? (track.itsNSigmaKa() < mMinITSNsigmaKa || mMaxITSNsigmaKa < track.itsNSigmaKa()) : true; - // bool is_pr_excluded_ITS = (mMinP_ITSNsigmaPr < track.p() && track.p() < mMaxP_ITSNsigmaPr) ? (track.itsNSigmaPr() < mMinITSNsigmaPr || mMaxITSNsigmaPr < track.itsNSigmaPr()) : true; return is_el_included_TPC && is_pi_excluded_TPC && is_el_included_TOF; } @@ -361,8 +355,6 @@ class DielectronCut : public TNamed bool is_ka_excluded_TPC = track.tpcNSigmaKa() < mMinTPCNsigmaKa || mMaxTPCNsigmaKa < track.tpcNSigmaKa(); bool is_pr_excluded_TPC = track.tpcNSigmaPr() < mMinTPCNsigmaPr || mMaxTPCNsigmaPr < track.tpcNSigmaPr(); bool is_el_included_TOF = track.hasTOF() ? (mMinTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < mMaxTOFNsigmaEl) : true; - // bool is_ka_excluded_ITS = (mMinP_ITSNsigmaKa < track.p() && track.p() < mMaxP_ITSNsigmaKa) ? (track.itsNSigmaKa() < mMinITSNsigmaKa || mMaxITSNsigmaKa < track.itsNSigmaKa()) : true; - // bool is_pr_excluded_ITS = (mMinP_ITSNsigmaPr < track.p() && track.p() < mMaxP_ITSNsigmaPr) ? (track.itsNSigmaPr() < mMinITSNsigmaPr || mMaxITSNsigmaPr < track.itsNSigmaPr()) : true; return is_el_included_TPC && is_pi_excluded_TPC && is_ka_excluded_TPC && is_pr_excluded_TPC && is_el_included_TOF; } @@ -370,8 +362,6 @@ class DielectronCut : public TNamed bool PassTPConly(T const& track) const { bool is_el_included_TPC = mMinTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < mMaxTPCNsigmaEl; - // bool is_ka_excluded_ITS = (mMinP_ITSNsigmaKa < track.p() && track.p() < mMaxP_ITSNsigmaKa) ? (track.itsNSigmaKa() < mMinITSNsigmaKa || mMaxITSNsigmaKa < track.itsNSigmaKa()) : true; - // bool is_pr_excluded_ITS = (mMinP_ITSNsigmaPr < track.p() && track.p() < mMaxP_ITSNsigmaPr) ? (track.itsNSigmaPr() < mMinITSNsigmaPr || mMaxITSNsigmaPr < track.itsNSigmaPr()) : true; return is_el_included_TPC; } @@ -383,8 +373,6 @@ class DielectronCut : public TNamed bool is_pi_excluded_TPC = track.tpcInnerParam() < mMaxPinForPionRejectionTPC ? (track.tpcNSigmaPi() < mMinTPCNsigmaPi || mMaxTPCNsigmaPi < track.tpcNSigmaPi()) : true; bool is_ka_excluded_TPC = track.tpcNSigmaKa() < mMinTPCNsigmaKa || mMaxTPCNsigmaKa < track.tpcNSigmaKa(); bool is_pr_excluded_TPC = track.tpcNSigmaPr() < mMinTPCNsigmaPr || mMaxTPCNsigmaPr < track.tpcNSigmaPr(); - // bool is_ka_excluded_ITS = (mMinP_ITSNsigmaKa < track.p() && track.p() < mMaxP_ITSNsigmaKa) ? (track.itsNSigmaKa() < mMinITSNsigmaKa || mMaxITSNsigmaKa < track.itsNSigmaKa()) : true; - // bool is_pr_excluded_ITS = (mMinP_ITSNsigmaPr < track.p() && track.p() < mMaxP_ITSNsigmaPr) ? (track.itsNSigmaPr() < mMinITSNsigmaPr || mMaxITSNsigmaPr < track.itsNSigmaPr()) : true; return is_el_included_TPC && is_pi_excluded_TPC && is_ka_excluded_TPC && is_pr_excluded_TPC; } @@ -394,8 +382,6 @@ class DielectronCut : public TNamed bool is_el_included_TPC = mMinTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < mMaxTPCNsigmaEl; bool is_pi_excluded_TPC = track.tpcInnerParam() < mMaxPinForPionRejectionTPC ? (track.tpcNSigmaPi() < mMinTPCNsigmaPi || mMaxTPCNsigmaPi < track.tpcNSigmaPi()) : true; bool is_el_included_TOF = track.hasTOF() ? (mMinTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < mMaxTOFNsigmaEl) : true; - // bool is_ka_excluded_ITS = (mMinP_ITSNsigmaKa < track.p() && track.p() < mMaxP_ITSNsigmaKa) ? (track.itsNSigmaKa() < mMinITSNsigmaKa || mMaxITSNsigmaKa < track.itsNSigmaKa()) : true; - // bool is_pr_excluded_ITS = (mMinP_ITSNsigmaPr < track.p() && track.p() < mMaxP_ITSNsigmaPr) ? (track.itsNSigmaPr() < mMinITSNsigmaPr || mMaxITSNsigmaPr < track.itsNSigmaPr()) : true; return is_el_included_TPC && is_pi_excluded_TPC && is_el_included_TOF; } @@ -462,8 +448,8 @@ class DielectronCut : public TNamed case DielectronCuts::kTPCChi2NDF: return mMinChi2PerClusterTPC < track.tpcChi2NCl() && track.tpcChi2NCl() < mMaxChi2PerClusterTPC; - case DielectronCuts::kDCA3Dsigma: - return mMinDca3D < o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(track) && o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(track) < mMaxDca3D; // in sigma for single leg + case DielectronCuts::kDCAsigma: + return (std::array{std::fabs(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(track)), std::fabs(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(track)), std::fabs(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(track))}[mDCAType]) < mMaxDcaSigma; case DielectronCuts::kDCAxy: return std::fabs(track.dcaXY()) < ((mMaxDcaXYPtDep) ? mMaxDcaXYPtDep(track.pt()) : mMaxDcaXY); @@ -483,9 +469,6 @@ class DielectronCut : public TNamed case DielectronCuts::kTTCA: return mEnableTTCA ? true : track.isAssociatedToMPC(); - case DielectronCuts::kPrefilter: - return track.pfb() <= 0; - default: return false; } @@ -500,7 +483,7 @@ class DielectronCut : public TNamed void SetMaxMeePhiVDep(std::function phivDepCut, float min_phiv, float max_phiv); void SelectPhotonConversion(bool flag); void SetMindEtadPhi(bool applydEtadPhi, bool applydEtadPhiPosition, float min_deta, float min_dphi); - void SetRequireDifferentSides(bool flag); + // void SetRequireDifferentSides(bool flag); void SetTrackPtRange(float minPt = 0.f, float maxPt = 1e10f); void SetTrackEtaRange(float minEta = -1e10f, float maxEta = 1e10f); @@ -537,18 +520,14 @@ class DielectronCut : public TNamed // void SetITSNsigmaKaRange(float min, float max); // void SetITSNsigmaPrRange(float min, float max); - // void SetPRangeForITSNsigmaKa(float min, float max); - // void SetPRangeForITSNsigmaPr(float min, float max); - void SetPinRangeForPionRejectionTPC(float min, float max); void RequireITSibAny(bool flag); void RequireITSib1st(bool flag); - void SetTrackDca3DRange(float min, float max); // in sigma - void SetTrackMaxDcaXY(float maxDcaXY); // in cm - void SetTrackMaxDcaZ(float maxDcaZ); // in cm + void SetTrackMaxDcaSigma(float max, uint dcaType); // in sigma + void SetTrackMaxDcaXY(float maxDcaXY); // in cm + void SetTrackMaxDcaZ(float maxDcaZ); // in cm void SetTrackMaxDcaXYPtDep(std::function ptDepCut); - void ApplyPrefilter(bool flag); void ApplyPhiV(bool flag); void IncludeITSsa(bool flag, float maxpt); void EnableTTCA(bool flag); @@ -558,7 +537,7 @@ class DielectronCut : public TNamed // mPIDMlResponse = mlResponse; // } - void SetMLThresholds(const std::vector bins, const std::vector cuts) + void SetMLThresholds(const std::vector& bins, const std::vector& cuts) { if (bins.size() != cuts.size() + 1) { LOG(fatal) << "cuts.size() + 1 mutst be exactly the same as bins.size(). Check your bins and thresholds."; @@ -589,7 +568,7 @@ class DielectronCut : public TNamed float mMinDeltaEta{0.f}; float mMinDeltaPhi{0.f}; float mMinOpAng{0.f}, mMaxOpAng{1e10f}; - bool mRequireDiffSides{false}; // flag to require 2 tracks to be from different sides. (A-C combination). If one wants 2 tracks to be in the same side (A-A or C-C), one can simply use track eta cut. + // bool mRequireDiffSides{false}; // flag to require 2 tracks to be from different sides. (A-C combination). If one wants 2 tracks to be in the same side (A-A or C-C), one can simply use track eta cut. // kinematic cuts float mMinTrackPt{0.f}, mMaxTrackPt{1e10f}; // range in pT @@ -614,15 +593,13 @@ class DielectronCut : public TNamed bool mRequireITSib1st{false}; float mMinChi2TOF{-1e10f}, mMaxChi2TOF{1e10f}; // max tof chi2 per - float mMinDca3D{0.0f}; // min dca in 3D in units of sigma - float mMaxDca3D{1e+10}; // max dca in 3D in units of sigma + uint mDCAType{0}; // 0:3D, 1:XY, 2:Z, else:3D + float mMaxDcaSigma{1e+10}; // max dca in units of sigma float mMaxDcaXY{1.0f}; // max dca in xy plane float mMaxDcaZ{1.0f}; // max dca in z direction std::function mMaxDcaXYPtDep{}; // max dca in xy plane as function of pT bool mApplyPhiV{true}; - bool mApplyPF{false}; float mMinMeanClusterSizeITS{0.0}, mMaxMeanClusterSizeITS{1e10f}; // x cos(lmabda) - // float mMinP_ITSClusterSize{0.0}, mMaxP_ITSClusterSize{0.0}; bool mIncludeITSsa{false}; float mMaxPtITSsa{1e+10}; @@ -649,8 +626,6 @@ class DielectronCut : public TNamed // float mMinITSNsigmaPi{-1e+10}, mMaxITSNsigmaPi{+1e+10}; // float mMinITSNsigmaKa{-1e+10}, mMaxITSNsigmaKa{+1e+10}; // float mMinITSNsigmaPr{-1e+10}, mMaxITSNsigmaPr{+1e+10}; - // float mMinP_ITSNsigmaKa{0.0}, mMaxP_ITSNsigmaKa{0.0}; - // float mMinP_ITSNsigmaPr{0.0}, mMaxP_ITSNsigmaPr{0.0}; // o2::analysis::MlResponseDielectronSingleTrack* mPIDMlResponse{nullptr}; std::vector mMLBins{}; // binning for a feature variable. e.g. tpcInnerParam diff --git a/PWGEM/Dilepton/Core/Dilepton.h b/PWGEM/Dilepton/Core/Dilepton.h index 5bce11ac26f..738634eeb3f 100644 --- a/PWGEM/Dilepton/Core/Dilepton.h +++ b/PWGEM/Dilepton/Core/Dilepton.h @@ -20,6 +20,7 @@ #include "PWGEM/Dilepton/Core/DielectronCut.h" #include "PWGEM/Dilepton/Core/DimuonCut.h" #include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" #include "PWGEM/Dilepton/DataModel/dileptonTables.h" #include "PWGEM/Dilepton/Utils/EMFwdTrack.h" #include "PWGEM/Dilepton/Utils/EMTrack.h" @@ -63,6 +64,7 @@ #include #include #include +#include #include #include #include @@ -89,12 +91,15 @@ using MyCollisions = o2::soa::Join; using MyCollision = MyCollisions::iterator; +using MyCollisionsSWT = o2::soa::Join; +using MyCollisionSWT = MyCollisionsSWT::iterator; + using MyElectrons = o2::soa::Join; using MyElectron = MyElectrons::iterator; using FilteredMyElectrons = o2::soa::Filtered; using FilteredMyElectron = FilteredMyElectrons::iterator; -using MyMuons = o2::soa::Join; +using MyMuons = o2::soa::Join; using MyMuon = MyMuons::iterator; using FilteredMyMuons = o2::soa::Filtered; using FilteredMyMuon = FilteredMyMuons::iterator; @@ -135,7 +140,7 @@ struct Dilepton { o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00}, "mll bins for output histograms"}; o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; - o2::framework::ConfigurableAxis ConfYllBins{"ConYllBins", {1, -1.f, 1.f}, "yll bins for output histograms"}; // pair rapidity + o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {1, -1.f, 1.f}, "yll bins for output histograms"}; // pair rapidity // o2::framework::ConfigurableAxis ConfMmumuBins{"ConfMmumuBins", {o2::framework::VARIABLE_WIDTH, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.10, 4.20, 4.30, 4.40, 4.50, 4.60, 4.70, 4.80, 4.90, 5.00, 5.10, 5.20, 5.30, 5.40, 5.50, 5.60, 5.70, 5.80, 5.90, 6.00, 6.10, 6.20, 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.10, 7.20, 7.30, 7.40, 7.50, 7.60, 7.70, 7.80, 7.90, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.50, 12.00}, "mmumu bins for output histograms"}; // for dimuon. one can copy bins here to hyperloop page. @@ -229,6 +234,7 @@ struct Dilepton { o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; @@ -257,15 +263,15 @@ struct Dilepton { o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; // configuration for PID ML - o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.98, 0.98, 0.9, 0.9, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; - o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + // o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + // o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; + // o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; + // o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; + // o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + // o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; } dielectroncuts; DimuonCut fDimuonCut; @@ -283,9 +289,6 @@ struct Dilepton { o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; - o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; - o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; @@ -305,6 +308,7 @@ struct Dilepton { o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; + o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; @@ -320,6 +324,16 @@ struct Dilepton { o2::framework::Configurable bcMarginForSoftwareTrigger{"bcMarginForSoftwareTrigger", 100, "Number of BCs of margin for software triggers"}; } zorroGroup; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "flowcorrection_group"; + o2::framework::Configurable cfgFillNUAHist{"cfgFillNUAHist", true, "flag to fill the NUA correction hist"}; + o2::framework::Configurable cfgApplyWeightNUA{"cfgApplyWeightNUA", false, "flag to apply q-vector Non-uniform acceptance weighting"}; + o2::framework::Configurable nuaPath{"nuaPath", "Users/o/omassen/Dielectron/NUAWeights/LHC23_PbPb_pass5", "Path to NUA-weights file"}; + o2::framework::ConfigurableAxis ConfNUAPhiBins{"ConfNUAPhiBins", {60, -M_PI, M_PI}, "NUA histogram bins - phi-angle"}; + o2::framework::ConfigurableAxis ConfNUAEtaBins{"ConfNUAEtaBins", {40, -1., +1.}, "NUA histogram bins - eta"}; + o2::framework::ConfigurableAxis ConfNUAZVtxBins{"ConfNUAZVtxBins", {40, -10., 10.}, "NUA histogram bins - z-vtx"}; + } flowcorrectionGroup; + Zorro zorro; int mToIidx = 0; int mTOICounter = 0; @@ -328,8 +342,8 @@ struct Dilepton { o2::aod::rctsel::RCTFlagsChecker rctChecker; // o2::ccdb::CcdbApi ccdbApi; o2::framework::Service ccdb; - int mRunNumber; - float d_bz; + int mRunNumber{0}; + float d_bz{0}; o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; // static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; @@ -352,6 +366,7 @@ struct Dilepton { float beamP1 = 0.f; // beam momentum float beamP2 = 0.f; // beam momentum TH2D* h2sp_resolution = nullptr; + std::vector h3nua_weights; void init(o2::framework::InitContext& /*context*/) { @@ -555,6 +570,16 @@ struct Dilepton { h2sp_resolution = reinterpret_cast(list->FindObject(spresoHistName.value.data())); LOGF(info, "h2sp_resolution.GetBinContent(40, 1) = %f", h2sp_resolution->GetBinContent(40, 1)); } + + if (flowcorrectionGroup.cfgApplyWeightNUA) { + auto list_nua = ccdb->getForTimeStamp(flowcorrectionGroup.nuaPath, collision.timestamp()); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_uls_NUA"))); + LOGF(info, "h3nua_weights.at(0)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(0)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lspp_NUA"))); + LOGF(info, "h3nua_weights.at(1)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(1)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lsmm_NUA"))); + LOGF(info, "h3nua_weights.at(2)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(2)->GetBinContent(5, 5, 5)); + } } ~Dilepton() @@ -645,6 +670,9 @@ struct Dilepton { const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); + } fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); @@ -773,6 +801,7 @@ struct Dilepton { fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, cfgDCAType); fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); @@ -793,14 +822,14 @@ struct Dilepton { if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMl.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMl.value[i]); + binsML.reserve(dielectroncuts.binsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { + binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); } std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMl.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMl.value[i]); + thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { + thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); } fDielectronCut.SetMLThresholds(binsML, thresholdsML); } // end of PID ML @@ -827,6 +856,7 @@ struct Dilepton { fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); + fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); @@ -875,8 +905,24 @@ struct Dilepton { } } + float getNUAweight(const int type_int, const float phi, const float eta, const float zVtx) + { + if (h3nua_weights.at(type_int) == nullptr) { + return 1.f; + } + int binId_phi = h3nua_weights.at(type_int)->GetXaxis()->FindBin(phi); + int binId_eta = h3nua_weights.at(type_int)->GetYaxis()->FindBin(eta); + int binId_zVtx = h3nua_weights.at(type_int)->GetZaxis()->FindBin(zVtx); + float nuaWeight = h3nua_weights.at(type_int)->GetBinContent(binId_phi, binId_eta, binId_zVtx); + + if (nuaWeight == 0 || std::isnan(nuaWeight) || std::isinf(nuaWeight)) { + nuaWeight = 1.f; + } + return nuaWeight; + } + template - bool fillPairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, const std::vector weightvector) + bool fillPairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, const std::vector& weightvector) { if constexpr (ev_id == 0) { if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { @@ -907,7 +953,7 @@ struct Dilepton { } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2, d_bz, 0)) { + if (!cut.IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -955,7 +1001,7 @@ struct Dilepton { if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { float deta = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); float dphi = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); float opAng = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz()); @@ -1049,14 +1095,26 @@ struct Dilepton { if constexpr (ev_id == 0) { // LOGF(info, "collision.centFT0C() = %f, collision.trackOccupancyInTimeRange() = %d, getSPresolution = %f", collision.centFT0C(), collision.trackOccupancyInTimeRange(), getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange())); + if (flowcorrectionGroup.cfgApplyWeightNUA) { + weight *= 1. / getNUAweight(t1.sign() * t2.sign() < 0 ? 0 : (t1.sign() > 0 && t2.sign() > 0 ? 1 : 2), v12.Phi(), v12.Eta(), collision.posZ()); + } float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } } } else if constexpr (ev_id == 1) { if (t1.sign() * t2.sign() < 0) { // ULS @@ -1216,9 +1274,9 @@ struct Dilepton { used_trackIds_per_col.emplace_back(t1.globalIndex()); if (cfgDoMix) { if (t1.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXX(), t1.cXY(), t1.cYY())); + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXX(), t1.cXY(), t1.cYY())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); } } } @@ -1226,9 +1284,9 @@ struct Dilepton { used_trackIds_per_col.emplace_back(t2.globalIndex()); if (cfgDoMix) { if (t2.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXX(), t2.cXY(), t2.cYY())); + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXX(), t2.cXY(), t2.cYY())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); } } } @@ -1242,9 +1300,11 @@ struct Dilepton { o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; using FilteredMyCollisions = o2::soa::Filtered; + using FilteredMyCollisionsSWT = o2::soa::Filtered; o2::framework::SliceCache cache; - o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; + // o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; + o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; o2::framework::expressions::Filter trackFilter_electron = dielectroncuts.cfg_min_pt_track < o2::aod::track::pt && dielectroncuts.cfg_min_eta_track < o2::aod::track::eta && o2::aod::track::eta < dielectroncuts.cfg_max_eta_track && nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz && o2::aod::track::itsChi2NCl < dielectroncuts.cfg_max_chi2its && o2::aod::track::tpcChi2NCl < dielectroncuts.cfg_max_chi2tpc; o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); @@ -1269,13 +1329,10 @@ struct Dilepton { o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); - o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; + // o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; + o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track; o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_muon = ifnode(dimuoncuts.cfg_apply_cuts_from_prefilter_derived.node() && dimuoncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimarymuon::pfbderived >= static_cast(0)); o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); @@ -1349,26 +1406,31 @@ struct Dilepton { } if constexpr (isTriggerAnalysis) { - if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + if (!collision.triggerMask_bit(emswtId)) { continue; } - auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected - auto TOIcounter = zorro.getTOIcounters()[0]; // this has to be called after zorro::isSelected - auto ATcounter = zorro.getATcounters()[mToIidx]; // this has to be called after zorro::isSelected - - if (swt_bitset.test(mToIidx)) { - while (ATcounter > mATCounter) { - mATCounter++; - fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), collision.runNumber()); - } + // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // continue; + // } - while (TOIcounter > mTOICounter) { - fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), collision.runNumber()); - mTOICounter++; // always incremented by 1 in zorro!! - } - // LOGF(info, "collision.globalIndex() = %d, collision.globalBC() = %llu, mTOICounter = %d, mATcounter = %d", collision.globalIndex(), collision.globalBC(), mTOICounter, mATCounter); - } + // auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected + // auto TOIcounter = zorro.getTOIcounters()[0]; // this has to be called after zorro::isSelected + // auto ATcounter = zorro.getATcounters()[mToIidx]; // this has to be called after zorro::isSelected + + // if (swt_bitset.test(mToIidx)) { + // while (ATcounter > mATCounter) { + // mATCounter++; + // fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), collision.runNumber()); + // } + + // while (TOIcounter > mTOICounter) { + // fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), collision.runNumber()); + // mTOICounter++; // always incremented by 1 in zorro!! + // } + // // LOGF(info, "collision.globalIndex() = %d, collision.globalBC() = %llu, mTOICounter = %d, mATcounter = %d", collision.globalIndex(), collision.globalBC(), mTOICounter, mATCounter); + // } } std::vector bootstrapweights = {}; @@ -1472,11 +1534,11 @@ struct Dilepton { std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. // make a vector of selected photons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { @@ -1493,8 +1555,8 @@ struct Dilepton { continue; } - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix @@ -1524,8 +1586,15 @@ struct Dilepton { if (nuls > 0 || nlspp > 0 || nlsmm > 0) { map_mixed_eventId_to_globalBC[key_df_collision] = collision.globalBC(); - emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); - emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_pos = emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_neg = emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + if (evicted_pos != evicted_neg) { + LOGF(fatal, "Positive and negative mixing pools are out of sync"); + } + if (evicted_pos) { + map_mixed_eventId_to_globalBC.erase(*evicted_pos); + } + } // end of if pair exist } // end of collision loop @@ -1552,17 +1621,10 @@ struct Dilepton { if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { return false; } - - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { - // return false; - // } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { - // return false; - // } } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2, d_bz, 0.0)) { + if (!cut.IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -1703,7 +1765,7 @@ struct Dilepton { } PROCESS_SWITCH(Dilepton, processAnalysis, "run dilepton analysis", true); - void processTriggerAnalysis(FilteredMyCollisions const& collisions, Types const&... args) + void processTriggerAnalysis(FilteredMyCollisionsSWT const& collisions, Types const&... args, o2::aod::EMSWTriggerATCounters const& countersAT, o2::aod::EMSWTriggerATOICounters const& countersATOI) { if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { auto electrons = std::get<0>(std::tie(args...)); @@ -1722,6 +1784,19 @@ struct Dilepton { map_weight.clear(); map_best_match_globalmuon.clear(); ndf++; + + // for nomalization + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + for (const auto& counter : countersAT) { + if (counter.isAT_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), mRunNumber); + } + } + for (const auto& counter : countersATOI) { + if (counter.isAToI_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), mRunNumber); + } + } } PROCESS_SWITCH(Dilepton, processTriggerAnalysis, "run dilepton analysis on triggered data", false); diff --git a/PWGEM/Dilepton/Core/DileptonHadronMPC.h b/PWGEM/Dilepton/Core/DileptonHadronMPC.h index 8fd7755e18f..0825c1d2f40 100644 --- a/PWGEM/Dilepton/Core/DileptonHadronMPC.h +++ b/PWGEM/Dilepton/Core/DileptonHadronMPC.h @@ -58,7 +58,6 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include #include #include @@ -81,12 +80,15 @@ using MyCollisions = o2::soa::Join; using MyCollision = MyCollisions::iterator; +using MyCollisionsSWT = o2::soa::Join; +using MyCollisionSWT = MyCollisionsSWT::iterator; + using MyElectrons = o2::soa::Join; using MyElectron = MyElectrons::iterator; using FilteredMyElectrons = o2::soa::Filtered; using FilteredMyElectron = FilteredMyElectrons::iterator; -using MyMuons = o2::soa::Join; +using MyMuons = o2::soa::Join; using MyMuon = MyMuons::iterator; using FilteredMyMuons = o2::soa::Filtered; using FilteredMyMuon = FilteredMyMuons::iterator; @@ -188,7 +190,6 @@ struct DileptonHadronMPC { o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; o2::framework::Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv (constant)"}; o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut at PV"}; - o2::framework::Configurable cfg_apply_detadphiposition{"cfg_apply_detadphiposition", false, "flag to apply deta-dphi elliptic cut at certain radius"}; o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 electrons (elliptic cut)"}; o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.2, "min dphi between 2 electrons (elliptic cut)"}; @@ -204,6 +205,8 @@ struct DileptonHadronMPC { o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_mirror_phi_track{"cfg_mirror_phi_track", false, "mirror the phi cut around Pi, min and max Phi should be in 0-Pi"}; + o2::framework::Configurable cfg_reject_phi_track{"cfg_reject_phi_track", false, "reject the phi interval"}; o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; o2::framework::Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 100, "min ncrossed rows"}; @@ -216,6 +219,8 @@ struct DileptonHadronMPC { o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; o2::framework::Configurable cfgRefR{"cfgRefR", 0.5, "reference R (in m) for extrapolation"}; // https://cds.cern.ch/record/1419204 + o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; + o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif : 4, kPIDML : 5, kTPChadrejORTOFreq_woTOFif : 6]"}; o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; @@ -426,12 +431,12 @@ struct DileptonHadronMPC { if (doprocessTriggerAnalysis) { LOGF(info, "Trigger analysis is enabled. Desired trigger name = %s", zorroGroup.cfg_swt_name.value); - fRegistry.add("NormTrigger/hInspectedTVX", "inspected TVX;run number;N_{TVX}", o2::framework::HistType::kTProfile, {{80000, 520000.5, 600000.5}}, true); - fRegistry.add("NormTrigger/hScalers", "trigger counter before DS;run number;counter", o2::framework::HistType::kTProfile, {{80000, 520000.5, 600000.5}}, true); - fRegistry.add("NormTrigger/hSelections", "trigger counter after DS;run number;counter", o2::framework::HistType::kTProfile, {{80000, 520000.5, 600000.5}}, true); - auto hTriggerCounter = fRegistry.add("NormTrigger/hTriggerCounter", Form("trigger counter of %s;run number;", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH2D, {{80000, 520000.5, 600000.5}, {2, -0.5, 1.5}}, false); - hTriggerCounter->GetYaxis()->SetBinLabel(1, "Analyzed Trigger"); - hTriggerCounter->GetYaxis()->SetBinLabel(2, "Analyzed TOI"); + // fRegistry.add("NormTrigger/hInspectedTVX", "inspected TVX;run number;N_{TVX}", o2::framework::HistType::kTProfile, {{80000, 520000.5, 600000.5}}, true); + // fRegistry.add("NormTrigger/hScalers", "trigger counter before DS;run number;counter", o2::framework::HistType::kTProfile, {{80000, 520000.5, 600000.5}}, true); + // fRegistry.add("NormTrigger/hSelections", "trigger counter after DS;run number;counter", o2::framework::HistType::kTProfile, {{80000, 520000.5, 600000.5}}, true); + // auto hTriggerCounter = fRegistry.add("NormTrigger/hTriggerCounter", Form("trigger counter of %s;run number;", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH2D, {{80000, 520000.5, 600000.5}, {2, -0.5, 1.5}}, false); + // hTriggerCounter->GetYaxis()->SetBinLabel(1, "Analyzed Trigger"); + // hTriggerCounter->GetYaxis()->SetBinLabel(2, "Analyzed TOI"); } } @@ -475,6 +480,7 @@ struct DileptonHadronMPC { LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; } mRunNumber = collision.runNumber(); + fDielectronCut.SetTrackPhiPositionRange(dielectroncuts.cfg_min_phiposition_track, dielectroncuts.cfg_max_phiposition_track, dielectroncuts.cfgRefR, d_bz, dielectroncuts.cfg_mirror_phi_track); // zorro if constexpr (isTriggerAnalysis) { @@ -626,9 +632,9 @@ struct DileptonHadronMPC { fDielectronCut.SetPairDCARange(dielectroncuts.cfg_min_pair_dca3d, dielectroncuts.cfg_max_pair_dca3d); // in sigma fDielectronCut.SetMaxMeePhiVDep([&](float phiv) { return dielectroncuts.cfg_phiv_intercept + phiv * dielectroncuts.cfg_phiv_slope; }, dielectroncuts.cfg_min_phiv, dielectroncuts.cfg_max_phiv); fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); - fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, dielectroncuts.cfg_apply_detadphiposition, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); + fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); fDielectronCut.SetPairOpAng(0.f, 6.3); - fDielectronCut.SetRequireDifferentSides(false); + // fDielectronCut.SetRequireDifferentSides(false); // for track fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); @@ -750,7 +756,7 @@ struct DileptonHadronMPC { } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2, d_bz, dielectroncuts.cfgRefR)) { + if (!cut.IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -820,9 +826,9 @@ struct DileptonHadronMPC { used_trackIds_per_col.emplace_back(t1.globalIndex()); if (cfgDoMix) { if (t1.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXX(), t1.cXY(), t1.cYY())); + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXX(), t1.cXY(), t1.cYY())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); } } } @@ -830,9 +836,9 @@ struct DileptonHadronMPC { used_trackIds_per_col.emplace_back(t2.globalIndex()); if (cfgDoMix) { if (t2.sign() > 0) { - emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXX(), t2.cXY(), t2.cYY())); + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); } else { - emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXX(), t2.cXY(), t2.cYY())); + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); } } } @@ -883,7 +889,7 @@ struct DileptonHadronMPC { } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2, d_bz, dielectroncuts.cfgRefR)) { + if (!cut.IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -972,6 +978,7 @@ struct DileptonHadronMPC { o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; using FilteredMyCollisions = o2::soa::Filtered; + using FilteredMyCollisionsSWT = o2::soa::Filtered; o2::framework::SliceCache cache; o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; @@ -1001,10 +1008,6 @@ struct DileptonHadronMPC { o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track && dimuoncuts.cfg_min_phi_track < o2::aod::fwdtrack::phi && o2::aod::fwdtrack::phi < dimuoncuts.cfg_max_phi_track; o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_muon = ifnode(dimuoncuts.cfg_apply_cuts_from_prefilter_derived.node() && dimuoncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimarymuon::pfbderived >= static_cast(0)); o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); @@ -1050,7 +1053,11 @@ struct DileptonHadronMPC { } if constexpr (isTriggerAnalysis) { - if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // continue; + // } + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + if (!collision.triggerMask_bit(emswtId)) { continue; } } @@ -1171,10 +1178,10 @@ struct DileptonHadronMPC { std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); // make a vector of selected electrons in this collision. - auto selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); - auto selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); - auto collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. // LOGF(info, "selected_posTracks_in_this_event.size() = %d, selected_negTracks_in_this_event.size() = %d, collisionIds_in_mixing_pool.size() = %d", selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), collisionIds_in_mixing_pool.size()); @@ -1194,8 +1201,8 @@ struct DileptonHadronMPC { continue; } - auto posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); - auto negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "posTracks_from_event_pool.size() = %d, negTracks_from_event_pool.size() = %d", posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix @@ -1226,8 +1233,8 @@ struct DileptonHadronMPC { } // end of loop over mixed event pool for lepton-lepton if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonHadronAnalysisType::kAzimuthalCorrelation)) { - auto selected_refTracks_in_this_event = emh_ref->GetTracksPerCollision(key_df_collision); - auto collisionIds_in_mixing_pool_hadron = emh_ref->GetCollisionIdsFromEventPool(key_bin); + const auto& selected_refTracks_in_this_event = emh_ref->GetTracksPerCollision(key_df_collision); + const auto& collisionIds_in_mixing_pool_hadron = emh_ref->GetCollisionIdsFromEventPool(key_bin); // for ULS and hadron mix for (const auto& pos : selected_posTracks_in_this_event) { @@ -1246,7 +1253,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(pos, neg, cut, ref); } @@ -1276,7 +1283,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(pos1, pos2, cut, ref); } @@ -1304,7 +1311,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); for (const auto& ref : refTracks_from_event_pool) { fillDileptonHadron<1>(neg1, neg2, cut, ref); } @@ -1328,7 +1335,7 @@ struct DileptonHadronMPC { continue; } - auto refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); + const auto& refTracks_from_event_pool = emh_ref->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "selected_refTracks_in_this_event.size() = %d, collisionIds_in_mixing_pool_hadron.size() = %d, refTracks_from_event_pool.size() = %d", selected_refTracks_in_this_event.size(), collisionIds_in_mixing_pool_hadron.size(), refTracks_from_event_pool.size()); for (const auto& ref1 : selected_refTracks_in_this_event) { // ref-ref mix for (const auto& ref2 : refTracks_from_event_pool) { @@ -1384,7 +1391,7 @@ struct DileptonHadronMPC { } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2, d_bz, dielectroncuts.cfgRefR)) { + if (!cut.IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -1492,10 +1499,11 @@ struct DileptonHadronMPC { map_weight.clear(); map_best_match_globalmuon.clear(); ndf++; + // correlation analyses does not require normalization by the number of events. } PROCESS_SWITCH(DileptonHadronMPC, processAnalysis, "run dilepton analysis", true); - void processTriggerAnalysis(FilteredMyCollisions const& collisions, FilteredRefTracks const& refTracks, Types const&... args) + void processTriggerAnalysis(FilteredMyCollisionsSWT const& collisions, FilteredRefTracks const& refTracks, Types const&... args) { if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { auto electrons = std::get<0>(std::tie(args...)); diff --git a/PWGEM/Dilepton/Core/DileptonMC.h b/PWGEM/Dilepton/Core/DileptonMC.h index 10a069da32d..219d6c0dec3 100644 --- a/PWGEM/Dilepton/Core/DileptonMC.h +++ b/PWGEM/Dilepton/Core/DileptonMC.h @@ -20,6 +20,7 @@ #include "PWGEM/Dilepton/Core/DielectronCut.h" #include "PWGEM/Dilepton/Core/DimuonCut.h" #include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" #include "PWGEM/Dilepton/DataModel/dileptonTables.h" #include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" #include "PWGEM/Dilepton/Utils/EventHistograms.h" @@ -91,7 +92,7 @@ using MyMCElectron = MyMCElectrons::iterator; using FilteredMyMCElectrons = o2::soa::Filtered; using FilteredMyMCElectron = FilteredMyMCElectrons::iterator; -using MyMCMuons = o2::soa::Join; +using MyMCMuons = o2::soa::Join; using MyMCMuon = MyMCMuons::iterator; using FilteredMyMCMuons = o2::soa::Filtered; using FilteredMyMCMuon = FilteredMyMCMuons::iterator; @@ -229,6 +230,7 @@ struct DileptonMC { o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; @@ -259,8 +261,8 @@ struct DileptonMC { // configuration for PID ML o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.98, 0.98, 0.9, 0.9, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; + o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; @@ -283,9 +285,6 @@ struct DileptonMC { o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; - o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; - o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; @@ -305,14 +304,16 @@ struct DileptonMC { o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; + o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; - o2::framework::Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. - o2::framework::Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. + o2::framework::Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. + o2::framework::Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. + o2::framework::Configurable acceptOnlyXORMatching{"acceptOnlyXORMatching", false, "flag to accept only correct-wrong XOR pairs between MFT and MCH-MID"}; // this is only for MC study, as we don't know correct match in data. } dimuoncuts; o2::aod::rctsel::RCTFlagsChecker rctChecker; @@ -436,9 +437,6 @@ struct DileptonMC { fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptJPsi/"); fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/PromptPsi2S/"); fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/NonPromptPsi2S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon1S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon2S/"); - // fRegistry.addClone("Generated/sm/PromptPi0/", "Generated/sm/Upsilon3S/"); fRegistry.add("Generated/ccbar/c2l_c2l/uls/hs", "generated dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_dphi_ee, axis_deta_ee, axis_cos_theta_pol, axis_phi_pol, axis_quadmom, axis_aco, axis_asym_pt, axis_dphi_e_ee}, true); fRegistry.addClone("Generated/ccbar/c2l_c2l/uls/", "Generated/ccbar/c2l_c2l/lspp/"); @@ -751,6 +749,7 @@ struct DileptonMC { fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, cfgDCAType); fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); @@ -771,14 +770,14 @@ struct DileptonMC { if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMl.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMl.value[i]); + binsML.reserve(dielectroncuts.binsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { + binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); } std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMl.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMl.value[i]); + thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { + thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); } fDielectronCut.SetMLThresholds(binsML, thresholdsML); } // end of PID ML @@ -805,6 +804,7 @@ struct DileptonMC { fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); + fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); @@ -1419,7 +1419,7 @@ struct DileptonMC { return false; } } - if (!cut.IsSelectedPair(t1, t2, d_bz, 0.0)) { + if (!cut.IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -1436,22 +1436,33 @@ struct DileptonMC { // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { // return false; // } + + bool isCorrectMatch1 = t1.emmcparticleId() == t1.emmftmcparticleId(); + bool isCorrectMatch2 = t2.emmcparticleId() == t2.emmftmcparticleId(); + if (dimuoncuts.acceptOnlyCorrectMatch) { - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && t1.emmcparticleId() != t1.emmftmcparticleId()) { + if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !isCorrectMatch1) { return false; } - if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && t2.emmcparticleId() != t2.emmftmcparticleId()) { + if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !isCorrectMatch2) { return false; } } - if (dimuoncuts.acceptOnlyWrongMatch) { // reject correctly matched MFT-MCH-MID for bkg estimation - if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && t1.emmcparticleId() == t1.emmftmcparticleId()) { + if (dimuoncuts.acceptOnlyWrongMatch) { + if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && isCorrectMatch1) { return false; } - if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && t2.emmcparticleId() == t2.emmftmcparticleId()) { + if (t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && isCorrectMatch2) { return false; } } + if (dimuoncuts.acceptOnlyXORMatching) { // this is dummy comment. + if (t1.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && t2.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { + if (!(isCorrectMatch1 ^ isCorrectMatch2)) { + return false; + } + } + } if (!cut.IsSelectedPair(t1, t2)) { return false; @@ -2111,6 +2122,12 @@ struct DileptonMC { if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { continue; } + if (!(eventcuts.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgTrackOccupancyMax)) { + continue; + } + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + continue; + } fRegistry.fill(HIST("MCEvent/after/hZvtx"), mccollision.posZ()); auto posTracks_per_coll = posTracksMC.sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); @@ -2171,7 +2188,7 @@ struct DileptonMC { } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.template IsSelectedPair(t1, t2, d_bz, 0.0)) { + if (!cut.template IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -2530,10 +2547,6 @@ struct DileptonMC { o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type; o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_muon = ifnode(dimuoncuts.cfg_apply_cuts_from_prefilter_derived.node() && dimuoncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimarymuon::pfbderived >= static_cast(0)); o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; diff --git a/PWGEM/Dilepton/Core/DileptonMixingTest.h b/PWGEM/Dilepton/Core/DileptonMixingTest.h new file mode 100644 index 00000000000..692ccdfe82c --- /dev/null +++ b/PWGEM/Dilepton/Core/DileptonMixingTest.h @@ -0,0 +1,1931 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code runs loop over leptons. +// Please write to: daiki.sekihata@cern.ch + +#ifndef PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ +#define PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ + +#include "PWGEM/Dilepton/Core/DielectronCut.h" +#include "PWGEM/Dilepton/Core/DimuonCut.h" +#include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/Dilepton/Utils/EMFwdTrack.h" +#include "PWGEM/Dilepton/Utils/EMTrack.h" +#include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" +#include "PWGEM/Dilepton/Utils/EventHistograms.h" +#include "PWGEM/Dilepton/Utils/EventMixingHandler.h" +#include "PWGEM/Dilepton/Utils/PairUtilities.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using MyCollisions = o2::soa::Join; +using MyCollision = MyCollisions::iterator; + +using MyCollisionsSWT = o2::soa::Join; +using MyCollisionSWT = MyCollisionsSWT::iterator; + +using MyElectrons = o2::soa::Join; +using MyElectron = MyElectrons::iterator; +using FilteredMyElectrons = o2::soa::Filtered; +using FilteredMyElectron = FilteredMyElectrons::iterator; + +using MyMuons = o2::soa::Join; +using MyMuon = MyMuons::iterator; +using FilteredMyMuons = o2::soa::Filtered; +using FilteredMyMuon = FilteredMyMuons::iterator; + +using MyEMH_electron = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMTrack>; +using MyEMH_muon = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMFwdTrack>; + +template +struct Dilepton { + + // Configurables + o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; + o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; + o2::framework::Configurable cfgApplySPresolution{"cfgApplySPresolution", false, "flag to apply resolution correction for flow analysis"}; + o2::framework::Configurable spresoPath{"spresoPath", "Users/d/dsekihat/PWGEM/dilepton/Qvector/resolution/LHC23zzh/pass3/test", "Path to SP resolution file"}; + o2::framework::Configurable spresoHistName{"spresoHistName", "h1_R2_FT0M_BPos_BNeg", "histogram name of SP resolution file"}; + + o2::framework::Configurable cfgAnalysisType{"cfgAnalysisType", static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC), "kQC:0, kUPC:1, kFlowV2SP:2, kFlowV3SP:3, kPolarization:4, kHFll:5, kBootstrapv2:6, kFlowV2EP:7, kFlowV3EP:8"}; + o2::framework::Configurable cfgEP2Estimator_for_Mix{"cfgEP2Estimator_for_Mix", 3, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; + o2::framework::Configurable cfgQvecEstimator{"cfgQvecEstimator", 2, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5, FV0A:6"}; + o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + o2::framework::Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; + o2::framework::Configurable cfgDoMix{"cfgDoMix", true, "flag for event mixing"}; + o2::framework::Configurable ndepth{"ndepth", 1000, "depth for event mixing"}; + o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; + o2::framework::ConfigurableAxis ConfVtxBins{"ConfVtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; + o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {16, -M_PI / 2, +M_PI / 2}, "Mixing bins - event plane angle"}; + o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; + o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; + o2::framework::Configurable cfgUseSignedDCA{"cfgUseSignedDCA", false, "flag to use signs in the DCA calculation"}; + o2::framework::Configurable cfgPolarizationFrame{"cfgPolarizationFrame", 0, "frame of polarization. 0:CS, 1:HX, else:FATAL"}; + + o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00}, "mll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {1, -1.f, 1.f}, "yll bins for output histograms"}; // pair rapidity + + // o2::framework::ConfigurableAxis ConfMmumuBins{"ConfMmumuBins", {o2::framework::VARIABLE_WIDTH, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11,1.12,1.13,1.14,1.15,1.16,1.17,1.18,1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.10, 4.20, 4.30, 4.40, 4.50, 4.60, 4.70, 4.80, 4.90, 5.00, 5.10, 5.20, 5.30, 5.40, 5.50, 5.60, 5.70, 5.80, 5.90, 6.00, 6.10, 6.20, 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.10, 7.20, 7.30, 7.40, 7.50, 7.60, 7.70, 7.80, 7.90, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.50, 12.00}, "mmumu bins for output histograms"}; // for dimuon. one can copy bins here to hyperloop page. + + o2::framework::ConfigurableAxis ConfSPBins{"ConfSPBins", {200, -5, 5}, "SP bins for flow analysis"}; + o2::framework::ConfigurableAxis ConfPolarizationCosThetaBins{"ConfPolarizationCosThetaBins", {20, -1.f, 1.f}, "cos(theta) bins for polarization analysis"}; + o2::framework::ConfigurableAxis ConfPolarizationPhiBins{"ConfPolarizationPhiBins", {1, -M_PI, M_PI}, "phi bins for polarization analysis"}; + o2::framework::ConfigurableAxis ConfPolarizationQuadMomBins{"ConfPolarizationQuadMomBins", {15, -0.5, 1}, "quadrupole moment bins for polarization analysis"}; // quardrupole moment <(3 x cos^2(theta) -1)/2> + + o2::framework::Configurable cfgNumBootstrapSamples{"cfgNumBootstrapSamples", 1, "Number of Bootstrap Samples"}; + + EMEventCut fEMEventCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "eventcut_group"; + o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; + o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; + o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. + o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + o2::framework::Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; + o2::framework::Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; + o2::framework::Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; + o2::framework::Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + o2::framework::Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; + o2::framework::Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "require no collision in time range strict"}; + o2::framework::Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "require no collision in time range standard"}; + o2::framework::Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "require no collision in time range strict"}; + o2::framework::Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "require no HM collision in previous ITS ROF"}; + o2::framework::Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "number of inactive chips on ITS layer 3 are below threshold "}; + o2::framework::Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "number of inactive chips on ITS layers 0-3 are below threshold "}; + o2::framework::Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "number of inactive chips on all ITS layers are below threshold "}; + // for RCT + o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; + o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + + o2::framework::Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; + o2::framework::Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; + o2::framework::Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; + o2::framework::Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; + } eventcuts; + + DielectronCut fDielectronCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "dielectroncut_group"; + o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; + o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; + o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pT"}; + o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pT"}; + o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -0.8, "min pair rapidity"}; + o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.8, "max pair rapidity"}; + o2::framework::Configurable cfg_min_pair_dca3d{"cfg_min_pair_dca3d", 0.0, "min pair dca3d in sigma"}; + o2::framework::Configurable cfg_max_pair_dca3d{"cfg_max_pair_dca3d", 1e+10, "max pair dca3d in sigma"}; + o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", true, "flag to apply phiv cut"}; + o2::framework::Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; + o2::framework::Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; + o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; + o2::framework::Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv (constant)"}; + o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut at PV"}; + o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 electrons (elliptic cut)"}; + o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.2, "min dphi between 2 electrons (elliptic cut)"}; + o2::framework::Configurable cfg_min_opang{"cfg_min_opang", 0.0, "min opening angle"}; + o2::framework::Configurable cfg_max_opang{"cfg_max_opang", 6.4, "max opening angle"}; + // o2::framework::Configurable cfg_require_diff_sides{"cfg_require_diff_sides", false, "flag to require 2 tracks are from different sides."}; + + o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; + o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h + + o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; + o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h + + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; + o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; + o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; + o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; + o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; + o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_mirror_phi_track{"cfg_mirror_phi_track", false, "mirror the phi cut around Pi, min and max Phi should be in 0-Pi"}; + o2::framework::Configurable cfg_reject_phi_track{"cfg_reject_phi_track", false, "reject the phi interval"}; + o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; + o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; + o2::framework::Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 100, "min ncrossed rows"}; + o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; + o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 5.0, "max chi2/NclsITS"}; + o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; + o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; + o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; + o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; + o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; + o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; + o2::framework::Configurable cfg_max_its_cluster_size{"cfg_max_its_cluster_size", 16.f, "max ITS cluster size"}; + // o2::framework::Configurable cfg_min_rel_diff_pin{"cfg_min_rel_diff_pin", -1e+10, "min rel. diff. between pin and ppv"}; + // o2::framework::Configurable cfg_max_rel_diff_pin{"cfg_max_rel_diff_pin", +1e+10, "max rel. diff. between pin and ppv"}; + o2::framework::Configurable cfgRefR{"cfgRefR", 0.50, "ref. radius (m) for calculating phi position"}; // 0.50 +/- 0.06 can be syst. unc. + o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; + o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; + + o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif : 4, kPIDML : 5, kTPChadrejORTOFreq_woTOFif : 6]"}; + o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; + // o2::framework::Configurable cfg_min_TPCNsigmaMu{"cfg_min_TPCNsigmaMu", -0.0, "min. TPC n sigma for muon exclusion"}; + // o2::framework::Configurable cfg_max_TPCNsigmaMu{"cfg_max_TPCNsigmaMu", +0.0, "max. TPC n sigma for muon exclusion"}; + o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3.0, "max. TPC n sigma for pion exclusion"}; + o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3.0, "min. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3.0, "max. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3.0, "min. TPC n sigma for proton exclusion"}; + o2::framework::Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3.0, "max. TPC n sigma for proton exclusion"}; + o2::framework::Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3.0, "min. TOF n sigma for electron inclusion"}; + o2::framework::Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3.0, "max. TOF n sigma for electron inclusion"}; + o2::framework::Configurable cfg_min_pin_pirejTPC{"cfg_min_pin_pirejTPC", 0.f, "min. pin for pion rejection in TPC"}; + o2::framework::Configurable cfg_max_pin_pirejTPC{"cfg_max_pin_pirejTPC", 1e+10, "max. pin for pion rejection in TPC"}; + o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; + + // configuration for PID ML + // o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + // o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; + // o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; + // o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; + // o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + // o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + } dielectroncuts; + + DimuonCut fDimuonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "dimuoncut_group"; + o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; + o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; + o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pt"}; + o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pt"}; + o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -4.0, "min pair rapidity"}; + o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", -2.5, "max pair rapidity"}; + o2::framework::Configurable cfg_min_pair_dcaxy{"cfg_min_pair_dcaxy", 0.0, "min pair dca3d in sigma"}; + o2::framework::Configurable cfg_max_pair_dcaxy{"cfg_max_pair_dcaxy", 1e+10, "max pair dca3d in sigma"}; + o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut"}; + o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; + o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; + + o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; + o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; + o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; + o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; + o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; + o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; + o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; + o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; + o2::framework::Configurable cfg_max_chi2mft{"cfg_max_chi2mft", 1e+6, "max chi2/ndf"}; + // o2::framework::Configurable cfg_max_matching_chi2_mftmch{"cfg_max_matching_chi2_mftmch", 40, "max chi2 for MFT-MCH matching"}; + o2::framework::Configurable cfg_border_pt_for_chi2mchmft{"cfg_border_pt_for_chi2mchmft", 0, "border pt for different max chi2 for MFT-MCH matching"}; + o2::framework::Configurable cfg_max_matching_chi2_mftmch_lowPt{"cfg_max_matching_chi2_mftmch_lowPt", 8, "max chi2 for MFT-MCH matching for low pT"}; + o2::framework::Configurable cfg_max_matching_chi2_mftmch_highPt{"cfg_max_matching_chi2_mftmch_highPt", 40, "max chi2 for MFT-MCH matching for high pT"}; + o2::framework::Configurable cfg_max_matching_chi2_mchmid{"cfg_max_matching_chi2_mchmid", 1e+10, "max chi2 for MCH-MID matching"}; + o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; + o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; + o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; + o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; + o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; + o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; + o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; + } dimuoncuts; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "zorroGroup"; + o2::framework::Configurable cfg_swt_name{"cfg_swt_name", "fLMeeIMR", "desired software trigger name. 1 trigger per 1 task."}; // 1 trigger per 1 task + o2::framework::Configurable ccdbPathSoftwareTrigger{"ccdbPathSoftwareTrigger", "EventFiltering/Zorro/", "ccdb path for ZORRO objects"}; + o2::framework::Configurable bcMarginForSoftwareTrigger{"bcMarginForSoftwareTrigger", 100, "Number of BCs of margin for software triggers"}; + } zorroGroup; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "flowcorrection_group"; + o2::framework::Configurable cfgFillNUAHist{"cfgFillNUAHist", true, "flag to fill the NUA correction hist"}; + o2::framework::Configurable cfgApplyWeightNUA{"cfgApplyWeightNUA", false, "flag to apply q-vector Non-uniform acceptance weighting"}; + o2::framework::Configurable nuaPath{"nuaPath", "Users/o/omassen/Dielectron/NUAWeights/LHC23_PbPb_pass5", "Path to NUA-weights file"}; + o2::framework::ConfigurableAxis ConfNUAPhiBins{"ConfNUAPhiBins", {60, -M_PI, M_PI}, "NUA histogram bins - phi-angle"}; + o2::framework::ConfigurableAxis ConfNUAEtaBins{"ConfNUAEtaBins", {40, -1., +1.}, "NUA histogram bins - eta"}; + o2::framework::ConfigurableAxis ConfNUAZVtxBins{"ConfNUAZVtxBins", {40, -10., 10.}, "NUA histogram bins - z-vtx"}; + } flowcorrectionGroup; + + Zorro zorro; + int mToIidx = 0; + int mTOICounter = 0; + int mATCounter = 0; + + o2::aod::rctsel::RCTFlagsChecker rctChecker; + // o2::ccdb::CcdbApi ccdbApi; + o2::framework::Service ccdb; + int mRunNumber{0}; + float d_bz{0}; + + o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; + // static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; + static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; + + std::mt19937 engine; + std::vector cent_bin_edges; + std::vector zvtx_bin_edges; + std::vector ep_bin_edges; + std::vector occ_bin_edges; + + int nmod = -1; // this is for flow analysis + float leptonM1 = 0.f; + float leptonM2 = 0.f; + + float beamM1 = o2::constants::physics::MassProton; // mass of beam + float beamM2 = o2::constants::physics::MassProton; // mass of beam + float beamE1 = 0.f; // beam energy + float beamE2 = 0.f; // beam energy + float beamP1 = 0.f; // beam momentum + float beamP2 = 0.f; // beam momentum + TH2D* h2sp_resolution = nullptr; + std::vector h3nua_weights; + + void init(o2::framework::InitContext& /*context*/) + { + mRunNumber = 0; + d_bz = 0; + mToIidx = 0; + mTOICounter = 0; + mATCounter = 0; + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); + + if (ConfVtxBins.value[0] == o2::framework::VARIABLE_WIDTH) { + zvtx_bin_edges = std::vector(ConfVtxBins.value.begin(), ConfVtxBins.value.end()); + zvtx_bin_edges.erase(zvtx_bin_edges.begin()); + for (const auto& edge : zvtx_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: zvtx_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfVtxBins.value[0]); + float xmin = static_cast(ConfVtxBins.value[1]); + float xmax = static_cast(ConfVtxBins.value[2]); + zvtx_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + zvtx_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: zvtx_bin_edges[%d] = %f", i, zvtx_bin_edges[i]); + } + } + + if (ConfCentBins.value[0] == o2::framework::VARIABLE_WIDTH) { + cent_bin_edges = std::vector(ConfCentBins.value.begin(), ConfCentBins.value.end()); + cent_bin_edges.erase(cent_bin_edges.begin()); + for (const auto& edge : cent_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: cent_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfCentBins.value[0]); + float xmin = static_cast(ConfCentBins.value[1]); + float xmax = static_cast(ConfCentBins.value[2]); + cent_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + cent_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: cent_bin_edges[%d] = %f", i, cent_bin_edges[i]); + } + } + + if (ConfEPBins.value[0] == o2::framework::VARIABLE_WIDTH) { + ep_bin_edges = std::vector(ConfEPBins.value.begin(), ConfEPBins.value.end()); + ep_bin_edges.erase(ep_bin_edges.begin()); + for (const auto& edge : ep_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: ep_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfEPBins.value[0]); + float xmin = static_cast(ConfEPBins.value[1]); + float xmax = static_cast(ConfEPBins.value[2]); + ep_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + ep_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: ep_bin_edges[%d] = %f", i, ep_bin_edges[i]); + } + } + + LOGF(info, "cfgOccupancyEstimator = %d", cfgOccupancyEstimator.value); + if (ConfOccupancyBins.value[0] == o2::framework::VARIABLE_WIDTH) { + occ_bin_edges = std::vector(ConfOccupancyBins.value.begin(), ConfOccupancyBins.value.end()); + occ_bin_edges.erase(occ_bin_edges.begin()); + for (const auto& edge : occ_bin_edges) { + LOGF(info, "o2::framework::VARIABLE_WIDTH: occ_bin_edges = %f", edge); + } + } else { + int nbins = static_cast(ConfOccupancyBins.value[0]); + float xmin = static_cast(ConfOccupancyBins.value[1]); + float xmax = static_cast(ConfOccupancyBins.value[2]); + occ_bin_edges.resize(nbins + 1); + for (int i = 0; i < nbins + 1; i++) { + occ_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; + LOGF(info, "FIXED_WIDTH: occ_bin_edges[%d] = %f", i, occ_bin_edges[i]); + } + } + + emh_pos = new TEMH(ndepth); + emh_neg = new TEMH(ndepth); + maxBufferedEvents = static_cast(ndepth.value) * (zvtx_bin_edges.size() - 1) * (cent_bin_edges.size() - 1) * (ep_bin_edges.size() - 1) * (occ_bin_edges.size() - 1); + + DefineEMEventCut(); + addhistograms(); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + DefineDielectronCut(); + leptonM1 = o2::constants::physics::MassElectron; + leptonM2 = o2::constants::physics::MassElectron; + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + DefineDimuonCut(); + leptonM1 = o2::constants::physics::MassMuon; + leptonM2 = o2::constants::physics::MassMuon; + } + + fRegistry.add("Pair/mix/hDiffBC", "diff. global BC in mixed event;|BC_{current} - BC_{mixed}|", o2::framework::HistType::kTH1D, {{10001, -0.5, 10000.5}}, true); + + fRegistry.add("Pair/mix/hEventBufferFillLevel", "Event buffer fill level;Number of processed events;Buffered events / maximum buffered events", o2::framework::HistType::kTProfile, {{1500, 0.5, 15000000.5}}, false); + fRegistry.get(HIST("Pair/mix/hEventBufferFillLevel"))->SetCanExtend(TH1::kXaxis); + + if (doprocessTriggerAnalysis) { + LOGF(info, "Trigger analysis is enabled. Desired trigger name = %s", zorroGroup.cfg_swt_name.value.data()); + fRegistry.add("Event/trigger/hInspectedTVX", "inspected TVX;run number;N_{TVX}", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hScaler", "trigger counter before DS;run number;counter", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hSelection", "trigger counter after DS;run number;counter", o2::framework::HistType::kTProfile, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hAnalysedTrigger", Form("analysed trigger %s;run number;counter", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH1D, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + fRegistry.add("Event/trigger/hAnalysedToI", Form("analysed ToI %s;run number;counter", zorroGroup.cfg_swt_name.value.data()), o2::framework::HistType::kTH1D, {{100000, 500000.5, 600000.5}}, true); // extend X range in Run 4/5 + } + + if (doprocessNorm) { + fRegistry.addClone("Event/before/hCollisionCounter", "Event/norm/hCollisionCounter"); + fRegistry.add("Event/norm/hZvtx", "hZvtx;Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{100, -50, +50}}, false); + } + if (doprocessBC) { + auto hTVXCounter = fRegistry.add("BC/hTVXCounter", "TVX counter", o2::framework::HistType::kTH1D, {{6, -0.5f, 5.5f}}); + hTVXCounter->GetXaxis()->SetBinLabel(1, "TVX"); + hTVXCounter->GetXaxis()->SetBinLabel(2, "TVX && NoTFB"); + hTVXCounter->GetXaxis()->SetBinLabel(3, "TVX && NoITSROFB"); + hTVXCounter->GetXaxis()->SetBinLabel(4, "TVX && GoodRCT"); + hTVXCounter->GetXaxis()->SetBinLabel(5, "TVX && NoTFB && NoITSROFB"); + hTVXCounter->GetXaxis()->SetBinLabel(6, "TVX && NoTFB && NoITSROFB && GoodRCT"); + } + } + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + + // In case override, don't proceed, please - no CCDB access required + if (d_bz_input > -990) { + d_bz = d_bz_input; + o2::parameters::GRPMagField grpmag; + if (std::fabs(d_bz) > 1e-5) { + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + } + o2::base::Propagator::initFieldFromGRP(&grpmag); + mRunNumber = collision.runNumber(); + return; + } + + auto run3grp_timestamp = collision.timestamp(); + o2::parameters::GRPObject* grpo = 0x0; + o2::parameters::GRPMagField* grpmag = 0x0; + if (!skipGRPOquery) + grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); + if (grpo) { + o2::base::Propagator::initFieldFromGRP(grpo); + // Fetch magnetic field from ccdb for current collision + d_bz = grpo->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; + } else { + grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + // Fetch magnetic field from ccdb for current collision + d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; + } + + // zorro + if constexpr (isTriggerAnalysis) { + zorro.setCCDBpath(zorroGroup.ccdbPathSoftwareTrigger); + zorro.setBCtolerance(zorroGroup.bcMarginForSoftwareTrigger); // this does nothing. + mToIidx = zorro.initCCDB(ccdb.service, collision.runNumber(), collision.timestamp(), zorroGroup.cfg_swt_name.value)[0]; + zorro.populateHistRegistry(fRegistry, collision.runNumber()); + + uint64_t nInspectedTVX = zorro.getInspectedTVX()->GetBinContent(1); + uint64_t nScalers = zorro.getScalers()->GetBinContent(zorro.getScalers()->GetXaxis()->FindBin(zorroGroup.cfg_swt_name.value.data())); + uint64_t nSelections = zorro.getSelections()->GetBinContent(zorro.getSelections()->GetXaxis()->FindBin(zorroGroup.cfg_swt_name.value.data())); + LOGF(info, "run number %d: total inspected TVX events = %llu, scalers = %llu, selections = %llu", collision.runNumber(), nInspectedTVX, nScalers, nSelections); + + fRegistry.fill(HIST("Event/trigger/hInspectedTVX"), collision.runNumber(), nInspectedTVX); + fRegistry.fill(HIST("Event/trigger/hScaler"), collision.runNumber(), nScalers); + fRegistry.fill(HIST("Event/trigger/hSelection"), collision.runNumber(), nSelections); + } + + mRunNumber = collision.runNumber(); + fDielectronCut.SetTrackPhiPositionRange(dielectroncuts.cfg_min_phiposition_track, dielectroncuts.cfg_max_phiposition_track, dielectroncuts.cfgRefR, d_bz, dielectroncuts.cfg_mirror_phi_track); + + auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", collision.timestamp()); + int beamZ1 = grplhcif->getBeamZ(o2::constants::lhc::BeamC); + int beamZ2 = grplhcif->getBeamZ(o2::constants::lhc::BeamA); + int beamA1 = grplhcif->getBeamA(o2::constants::lhc::BeamC); + int beamA2 = grplhcif->getBeamA(o2::constants::lhc::BeamA); + beamE1 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamC); + beamE2 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamA); + beamM1 = o2::constants::physics::MassProton * beamA1; + beamM2 = o2::constants::physics::MassProton * beamA2; + beamP1 = std::sqrt(std::pow(beamE1, 2) - std::pow(beamM1, 2)); + beamP2 = std::sqrt(std::pow(beamE2, 2) - std::pow(beamM2, 2)); + LOGF(info, "beamZ1 = %d, beamZ2 = %d, beamA1 = %d, beamA2 = %d, beamE1 = %f (GeV), beamE2 = %f (GeV), beamM1 = %f (GeV), beamM2 = %f (GeV), beamP1 = %f (GeV), beamP2 = %f (GeV)", beamZ1, beamZ2, beamA1, beamA2, beamE1, beamE2, beamM1, beamM2, beamP1, beamP2); + + if (cfgApplySPresolution) { + auto list = ccdb->getForTimeStamp(spresoPath, collision.timestamp()); + h2sp_resolution = reinterpret_cast(list->FindObject(spresoHistName.value.data())); + LOGF(info, "h2sp_resolution.GetBinContent(40, 1) = %f", h2sp_resolution->GetBinContent(40, 1)); + } + + if (flowcorrectionGroup.cfgApplyWeightNUA) { + auto list_nua = ccdb->getForTimeStamp(flowcorrectionGroup.nuaPath, collision.timestamp()); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_uls_NUA"))); + LOGF(info, "h3nua_weights.at(0)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(0)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lspp_NUA"))); + LOGF(info, "h3nua_weights.at(1)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(1)->GetBinContent(5, 5, 5)); + h3nua_weights.push_back(reinterpret_cast(list_nua->FindObject("weights_lsmm_NUA"))); + LOGF(info, "h3nua_weights.at(2)->GetBinContent(5, 5, 5) = %f", h3nua_weights.at(2)->GetBinContent(5, 5, 5)); + } + } + + ~Dilepton() + { + delete emh_pos; + emh_pos = 0x0; + delete emh_neg; + emh_neg = 0x0; + + used_trackIds_per_col.clear(); + used_trackIds_per_col.shrink_to_fit(); + map_mixed_eventId_to_globalBC.clear(); + + delete h2sp_resolution; + } + + void addhistograms() + { + const std::string qvec_det_names[7] = {"FT0M", "FT0A", "FT0C", "BTot", "BPos", "BNeg", "FV0A"}; + + std::string mass_axis_title = "m_{ll} (GeV/c^{2})"; + std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; + std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; + std::string pair_y_axis_title = "y_{ll}"; + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + mass_axis_title = "m_{ee} (GeV/c^{2})"; + pair_pt_axis_title = "p_{T,ee} (GeV/c)"; + pair_y_axis_title = "y_{ee}"; + pair_dca_axis_title = "DCA_{ee}^{3D} (#sigma)"; + if (cfgDCAType == 1) { + pair_dca_axis_title = "DCA_{ee}^{XY} (#sigma)"; + } else if (cfgDCAType == 2) { + pair_dca_axis_title = "DCA_{ee}^{Z} (#sigma)"; + } + if (cfgUseSignedDCA) { + pair_dca_axis_title = "Signed " + pair_dca_axis_title; + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + mass_axis_title = "m_{#mu#mu} (GeV/c^{2})"; + pair_pt_axis_title = "p_{T,#mu#mu} (GeV/c)"; + pair_dca_axis_title = "DCA_{#mu#mu}^{XY} (#sigma)"; + pair_y_axis_title = "y_{#mu#mu}"; + } + + // pair info + const o2::framework::AxisSpec axis_mass{ConfMllBins, mass_axis_title}; + const o2::framework::AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; + const o2::framework::AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; + const o2::framework::AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; + + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, false); + fRegistry.add("Pair/same/uls/hDeltaEtaDeltaPhi", "#Delta#eta-#Delta#varphi between 2 tracks;#Delta#varphi (rad.);#Delta#eta;", o2::framework::HistType::kTH2D, {{180, -M_PI, M_PI}, {400, -2, +2}}, false); + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.add("Pair/same/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, false); // phiv is only for dielectron + fRegistry.add("Pair/same/uls/hMvsOpAng", "m_{ee} vs. angle between 2 tracks;#omega (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2D, {{90, 0, M_PI}, {100, 0.0f, 1.0f}}, false); + fRegistry.add("Pair/same/uls/hDCA1vsDCA2", "DCA of leg1 vs. DCA of leg2;DCA1(#sigma);DCA2 (#sigma)", o2::framework::HistType::kTH2D, {{200, -10, 10.0}, {200, -10, 10}}, false); + } + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { + const o2::framework::AxisSpec axis_aco{10, 0, 1.f, "#alpha = 1 - #frac{|#varphi_{l^{+}} - #varphi_{l^{-}}|}{#pi}"}; + const o2::framework::AxisSpec axis_asym_pt{10, 0, 1.f, "A = #frac{|p_{T,l^{+}} - p_{T,l^{-}}|}{|p_{T,l^{+}} + p_{T,l^{-}}|}"}; + const o2::framework::AxisSpec axis_dphi_l_ll{18, 0, M_PI, "#Delta#varphi = #varphi_{l} - #varphi_{ll} (rad.)"}; + + std::string frameName = "CS"; + if (cfgPolarizationFrame == 0) { + frameName = "CS"; + } else if (cfgPolarizationFrame == 1) { + frameName = "HX"; + } else { + LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; + } + const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_aco, axis_asym_pt, axis_dphi_l_ll, axis_cos_theta}, false); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP)) { + nmod = 2; + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { + nmod = 3; + } + + const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; + + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp}, true); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.add("Pair/same/uls/hNUA", "NUA Histogram;#phi (rad.);#eta;VtxZ;", o2::framework::HistType::kTH3D, {flowcorrectionGroup.ConfNUAPhiBins, flowcorrectionGroup.ConfNUAEtaBins, flowcorrectionGroup.ConfNUAZVtxBins}, true); // for NUA correction needs to be filled with weights so sumw2 is true + } + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); + + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kPolarization)) { + std::string frameName = "CS"; + if (cfgPolarizationFrame == 0) { + frameName = "CS"; + } else if (cfgPolarizationFrame == 1) { + frameName = "HX"; + } else { + LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; + } + + const o2::framework::AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; + const o2::framework::AxisSpec axis_phi{ConfPolarizationPhiBins, Form("#varphi^{%s} (rad.)", frameName.data())}; + const o2::framework::AxisSpec axis_quadmom{ConfPolarizationQuadMomBins, Form("#frac{3 cos^{2}(#theta^{%s}) -1}{2}", frameName.data())}; + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_cos_theta, axis_phi, axis_quadmom}, false); + + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kHFll)) { + const o2::framework::AxisSpec axis_dphi_ll{36, -M_PI / 2., 3. / 2. * M_PI, "#Delta#varphi = #varphi_{l1} - #varphi_{l2} (rad.)"}; // for kHFll + const o2::framework::AxisSpec axis_deta_ll{40, -2., 2., "#Delta#eta = #eta_{l1} - #eta_{l2}"}; + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_dphi_ll, axis_deta_ll}, false); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { + nmod = 2; + const o2::framework::AxisSpec axis_sp{ConfSPBins, Form("#vec{u}_{%d,ll} #upoint #vec{Q}_{%d}^{%s}", nmod, nmod, qvec_det_names[cfgQvecEstimator].data())}; + const o2::framework::AxisSpec axis_bootstrap{cfgNumBootstrapSamples, 0.5, static_cast(cfgNumBootstrapSamples) + 0.5, "sample"}; // for bootstrap samples + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_sp, axis_bootstrap}, false); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, false); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistogramsBootstrap(&fRegistry, cfgNumBootstrapSamples); + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP)) { + nmod = 2; + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { + nmod = 3; + } + + // similar to Eq.1 in PRL 126, 162001 (2021) + const o2::framework::AxisSpec axis_ep{4, 0, 2 * M_PI / nmod, Form("#varphi_{ll} #minus #Psi_{%d}^{%s}", nmod, qvec_det_names[cfgQvecEstimator].data())}; + + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y, axis_ep}, true); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + + fRegistry.add("Pair/mix/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lspp/"); + fRegistry.addClone("Pair/mix/uls/", "Pair/mix/lsmm/"); + } else { // same as kQC to avoid seg. fault + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnD, {axis_mass, axis_pt, axis_dca, axis_y}, true); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + fRegistry.addClone("Pair/same/", "Pair/mix/"); + } + + // event info + if (nmod == 2) { + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<2>(&fRegistry); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<3>(&fRegistry); + } else { + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<-1>(&fRegistry); + } + fRegistry.add("Event/before/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[cfgEP2Estimator_for_Mix].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); + fRegistry.add("Event/after/hEP2_CentFT0C_forMix", Form("2nd harmonics event plane for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", qvec_det_names[cfgEP2Estimator_for_Mix].data()), o2::framework::HistType::kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); + } + + void DefineEMEventCut() + { + fEMEventCut = EMEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireVertexTOFmatched(eventcuts.cfgRequireVertexTOFmatched); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); + fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); + fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); + fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); + fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); + fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); + fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); + fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + } + + void DefineDielectronCut() + { + fDielectronCut = DielectronCut("fDielectronCut", "fDielectronCut"); + + // for pair + fDielectronCut.SetMeeRange(dielectroncuts.cfg_min_mass, dielectroncuts.cfg_max_mass); + fDielectronCut.SetPairPtRange(dielectroncuts.cfg_min_pair_pt, dielectroncuts.cfg_max_pair_pt); + fDielectronCut.SetPairYRange(dielectroncuts.cfg_min_pair_y, dielectroncuts.cfg_max_pair_y); + fDielectronCut.SetPairDCARange(dielectroncuts.cfg_min_pair_dca3d, dielectroncuts.cfg_max_pair_dca3d); // in sigma + fDielectronCut.SetMaxMeePhiVDep([&](float phiv) { return dielectroncuts.cfg_phiv_intercept + phiv * dielectroncuts.cfg_phiv_slope; }, dielectroncuts.cfg_min_phiv, dielectroncuts.cfg_max_phiv); + fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); + fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); + fDielectronCut.SetPairOpAng(dielectroncuts.cfg_min_opang, dielectroncuts.cfg_max_opang); + // fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); + + // for track + fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); + fDielectronCut.SetTrackEtaRange(dielectroncuts.cfg_min_eta_track, dielectroncuts.cfg_max_eta_track); + fDielectronCut.SetTrackPhiRange(dielectroncuts.cfg_min_phi_track, dielectroncuts.cfg_max_phi_track, dielectroncuts.cfg_mirror_phi_track, dielectroncuts.cfg_reject_phi_track); + fDielectronCut.SetMinNClustersTPC(dielectroncuts.cfg_min_ncluster_tpc); + fDielectronCut.SetMinNCrossedRowsTPC(dielectroncuts.cfg_min_ncrossedrows); + fDielectronCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); + fDielectronCut.SetMaxFracSharedClustersTPC(dielectroncuts.cfg_max_frac_shared_clusters_tpc); + fDielectronCut.SetChi2PerClusterTPC(0.0, dielectroncuts.cfg_max_chi2tpc); + fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); + fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); + fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, cfgDCAType); + fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); + fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); + fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); + fDielectronCut.RequireITSib1st(dielectroncuts.cfg_require_itsib_1st); + fDielectronCut.SetChi2TOF(0, dielectroncuts.cfg_max_chi2tof); + // fDielectronCut.SetRelDiffPin(dielectroncuts.cfg_min_rel_diff_pin, dielectroncuts.cfg_max_rel_diff_pin); + fDielectronCut.EnableTTCA(dielectroncuts.enableTTCA); + + // for eID + fDielectronCut.SetPIDScheme(dielectroncuts.cfg_pid_scheme); + fDielectronCut.SetTPCNsigmaElRange(dielectroncuts.cfg_min_TPCNsigmaEl, dielectroncuts.cfg_max_TPCNsigmaEl); + // fDielectronCut.SetTPCNsigmaMuRange(dielectroncuts.cfg_min_TPCNsigmaMu, dielectroncuts.cfg_max_TPCNsigmaMu); + fDielectronCut.SetTPCNsigmaPiRange(dielectroncuts.cfg_min_TPCNsigmaPi, dielectroncuts.cfg_max_TPCNsigmaPi); + fDielectronCut.SetTPCNsigmaKaRange(dielectroncuts.cfg_min_TPCNsigmaKa, dielectroncuts.cfg_max_TPCNsigmaKa); + fDielectronCut.SetTPCNsigmaPrRange(dielectroncuts.cfg_min_TPCNsigmaPr, dielectroncuts.cfg_max_TPCNsigmaPr); + fDielectronCut.SetTOFNsigmaElRange(dielectroncuts.cfg_min_TOFNsigmaEl, dielectroncuts.cfg_max_TOFNsigmaEl); + fDielectronCut.SetPinRangeForPionRejectionTPC(dielectroncuts.cfg_min_pin_pirejTPC, dielectroncuts.cfg_max_pin_pirejTPC); + + if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut + std::vector binsML{}; + binsML.reserve(dielectroncuts.binsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { + binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); + } + std::vector thresholdsML{}; + thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { + thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); + } + fDielectronCut.SetMLThresholds(binsML, thresholdsML); + } // end of PID ML + } + + void DefineDimuonCut() + { + fDimuonCut = DimuonCut("fDimuonCut", "fDimuonCut"); + + // for pair + fDimuonCut.SetMassRange(dimuoncuts.cfg_min_mass, dimuoncuts.cfg_max_mass); + fDimuonCut.SetPairPtRange(dimuoncuts.cfg_min_pair_pt, dimuoncuts.cfg_max_pair_pt); + fDimuonCut.SetPairYRange(dimuoncuts.cfg_min_pair_y, dimuoncuts.cfg_max_pair_y); + fDimuonCut.SetPairDCAxyRange(dimuoncuts.cfg_min_pair_dcaxy, dimuoncuts.cfg_max_pair_dcaxy); + fDimuonCut.SetMindEtadPhi(dimuoncuts.cfg_apply_detadphi, dimuoncuts.cfg_min_deta, dimuoncuts.cfg_min_dphi); + + // for track + fDimuonCut.SetTrackType(dimuoncuts.cfg_track_type); + fDimuonCut.SetTrackPtRange(dimuoncuts.cfg_min_pt_track, dimuoncuts.cfg_max_pt_track); + fDimuonCut.SetTrackEtaRange(dimuoncuts.cfg_min_eta_track, dimuoncuts.cfg_max_eta_track); + fDimuonCut.SetTrackPhiRange(dimuoncuts.cfg_min_phi_track, dimuoncuts.cfg_max_phi_track); + fDimuonCut.SetNClustersMFT(dimuoncuts.cfg_min_ncluster_mft, 10); + fDimuonCut.SetNClustersMCHMID(dimuoncuts.cfg_min_ncluster_mch, 20); + fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); + fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); + // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); + fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); + fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); + fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); + fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); + fDimuonCut.SetRabs(dimuoncuts.cfg_min_rabs, dimuoncuts.cfg_max_rabs); + fDimuonCut.SetMaxPDCARabsDep([&](float rabs) { return (rabs < 26.5 ? 594.f : 324.f); }); + fDimuonCut.SetMaxdPtdEtadPhiwrtMCHMID(dimuoncuts.cfg_max_relDPt_wrt_matchedMCHMID, dimuoncuts.cfg_max_DEta_wrt_matchedMCHMID, dimuoncuts.cfg_max_DPhi_wrt_matchedMCHMID); // this is relevant for global muons + fDimuonCut.SetMFTHitMap(dimuoncuts.requireMFTHitMap, dimuoncuts.requiredMFTDisks); + fDimuonCut.EnableTTCA(dimuoncuts.enableTTCA); + } + + template + bool isGoodQvector(TQvectors const& qvectors) + { + bool is_good = true; + for (const auto& qn : qvectors[nmod]) { + if (std::fabs(qn[0]) > 20.f || std::fabs(qn[1]) > 20.f) { + is_good = false; + break; + } + } + return is_good; + } + + float getSPresolution(const float centrality, const int occupancy) + { + if (h2sp_resolution == nullptr) { + return 1.f; + } else { + int binId_cen = h2sp_resolution->GetXaxis()->FindBin(centrality); + int binId_occ = h2sp_resolution->GetYaxis()->FindBin(occupancy); + + if (centrality < h2sp_resolution->GetXaxis()->GetXmin()) { + binId_cen = 1; + } + if (h2sp_resolution->GetXaxis()->GetXmax() < centrality) { + binId_cen = h2sp_resolution->GetXaxis()->GetNbins(); + } + + if (occupancy < h2sp_resolution->GetYaxis()->GetXmin()) { + binId_occ = 1; + } + if (h2sp_resolution->GetYaxis()->GetXmax() < occupancy) { + binId_occ = h2sp_resolution->GetYaxis()->GetNbins(); + } + return h2sp_resolution->GetBinContent(binId_cen, binId_occ); + } + } + + float getNUAweight(const int type_int, const float phi, const float eta, const float zVtx) + { + if (h3nua_weights.at(type_int) == nullptr) { + return 1.f; + } + int binId_phi = h3nua_weights.at(type_int)->GetXaxis()->FindBin(phi); + int binId_eta = h3nua_weights.at(type_int)->GetYaxis()->FindBin(eta); + int binId_zVtx = h3nua_weights.at(type_int)->GetZaxis()->FindBin(zVtx); + float nuaWeight = h3nua_weights.at(type_int)->GetBinContent(binId_phi, binId_eta, binId_zVtx); + + if (nuaWeight == 0 || std::isnan(nuaWeight) || std::isinf(nuaWeight)) { + nuaWeight = 1.f; + } + return nuaWeight; + } + + template + bool fillPairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&, const std::vector& weightvector) + { + if constexpr (ev_id == 0) { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } else { // cut-based + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { + return false; + } + + // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { + // return false; + // } + // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { + // return false; + // } + } + } + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } + + float weight = 1.f; + if constexpr (ev_id == 0) { + if (cfgApplyWeightTTCA) { + weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; + } + // LOGF(info, "ev_id = %d, t1.sign() = %d, t2.sign() = %d, map_weight[std::make_pair(%d, %d)] = %f", ev_id, t1.sign(), t2.sign(), t1.globalIndex(), t2.globalIndex(), weight); + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + + float pair_dca = 999.f; + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2)); + } + if (cfgDCAType == 1) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2)); + } + } else if (cfgDCAType == 2) { + if (cfgUseSignedDCA) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCASignQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), t1.sign(), t2.sign()); + } else { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2)); + } + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + pair_dca = o2::aod::pwgem::dilepton::utils::pairutil::pairDCAQuadSum(o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t2)); + } + + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kQC)) { + float deta = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); + float dphi = t1.sign() * v1.Pt() > t2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi + + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); + float opAng = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz()); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDeltaEtaDeltaPhi"), dphi, deta, weight); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } + } + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDeltaEtaDeltaPhi"), dphi, deta, weight); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } + } + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDeltaEtaDeltaPhi"), dphi, deta, weight); + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hMvsPhiV"), phiv, v12.M(), weight); + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hMvsOpAng"), opAng, v12.M(), weight); + if (cfgDCAType == 1) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2), weight); + } else if (cfgDCAType == 2) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hDCA1vsDCA2"), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1), o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2), weight); + } + } + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kUPC)) { + float dphi = v1.Phi() - v2.Phi(); + o2::math_utils::bringToPMPi(dphi); + float aco = 1.f - std::fabs(dphi) / M_PI; + float asym = std::fabs(v1.Pt() - v2.Pt()) / (v1.Pt() + v2.Pt()); + float dphi_l_ll = v1.Phi() - v12.Phi(); + o2::math_utils::bringToPMPi(dphi_l_ll); + + float cos_thetaPol = 999, phiPol = 999.f; + auto arrM = std::array{static_cast(v12.Px()), static_cast(v12.Py()), static_cast(v12.Pz()), static_cast(v12.M())}; + auto arrD = t1.sign() * t1.pt() > t2.sign() * t2.pt() ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}; + + if (cfgPolarizationFrame == 0) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } else if (cfgPolarizationFrame == 1) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } + + o2::math_utils::bringToPMPi(phiPol); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), aco, asym, std::fabs(dphi_l_ll), cos_thetaPol, weight); + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2SP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3SP)) { + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if constexpr (ev_id == 0) { + // LOGF(info, "collision.centFT0C() = %f, collision.trackOccupancyInTimeRange() = %d, getSPresolution = %f", collision.centFT0C(), collision.trackOccupancyInTimeRange(), getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange())); + if (flowcorrectionGroup.cfgApplyWeightNUA) { + weight *= 1. / getNUAweight(t1.sign() * t2.sign() < 0 ? 0 : (t1.sign() > 0 && t2.sign() > 0 ? 1 : 2), v12.Phi(), v12.Eta(), collision.posZ()); + } + + float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, weight); + if (flowcorrectionGroup.cfgFillNUAHist) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hNUA"), v12.Phi(), v12.Eta(), collision.posZ(), weight); + } + } + } else if constexpr (ev_id == 1) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kPolarization)) { + float cos_thetaPol = 999, phiPol = 999.f; + auto arrM = std::array{static_cast(v12.Px()), static_cast(v12.Py()), static_cast(v12.Pz()), static_cast(v12.M())}; + auto random_sign = std::pow(-1, engine() % 2); // -1^0 = +1 or -1^1 = -1; + auto arrD = t1.sign() * t2.sign() < 0 ? (t1.sign() > 0 ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}) : (random_sign > 0 ? std::array{t1.px(), t1.py(), t1.pz(), leptonM1} : std::array{t2.px(), t2.py(), t2.pz(), leptonM2}); + + if (cfgPolarizationFrame == 0) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } else if (cfgPolarizationFrame == 1) { + o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); + } + + o2::math_utils::bringToPMPi(phiPol); + float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kHFll)) { + float dphi = v1.Phi() - v2.Phi(); + dphi = RecoDecay::constrainAngle(dphi, -o2::constants::math::PIHalf); + float deta = v1.Eta() - v2.Eta(); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi, deta, weight); + } + + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if constexpr (ev_id == 0) { + // LOGF(info, "collision.centFT0C() = %f, collision.trackOccupancyInTimeRange() = %d, getSPresolution = %f", collision.centFT0C(), collision.trackOccupancyInTimeRange(), getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange())); + + float sp = RecoDecay::dotProd(std::array{static_cast(std::cos(nmod * v12.Phi())), static_cast(std::sin(nmod * v12.Phi()))}, qvectors[nmod][cfgQvecEstimator]) / getSPresolution(collision.centFT0C(), collision.trackOccupancyInTimeRange()); + for (int i = 0; i < cfgNumBootstrapSamples; i++) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), sp, i + 0.5, weightvector.at(i)); + } + } + } else if constexpr (ev_id == 1) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + } else if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV2EP) || cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kFlowV3EP)) { + if constexpr (ev_id == 0) { + std::vector> eventplanes = { + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 0th harmonics + {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, // 1st harmonics + {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}, // 2nd harmonics + {collision.ep3ft0m(), collision.ep3ft0a(), collision.ep3ft0c(), collision.ep3btot(), collision.ep3bpos(), collision.ep3bneg(), collision.ep3fv0a()}, // 3rd harmonics + }; + + float ep = RecoDecay::constrainAngle(eventplanes[nmod][cfgQvecEstimator], -o2::constants::math::PI / nmod, static_cast(nmod)); + float phi_ll = RecoDecay::constrainAngle(v12.Phi(), -o2::constants::math::PI / nmod, 1U); + float dphi_ll_ep = std::fabs(RecoDecay::constrainAngle(phi_ll - ep, 0.f, static_cast(nmod))); + + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), dphi_ll_ep, weight); + } + } else if constexpr (ev_id == 1) { + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + } else { // same as kQC to avoid seg. fault + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), weight); + } + } + + // store tracks for event mixing without double counting + if constexpr (ev_id == 0) { + std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t1.globalIndex()); + if (cfgDoMix) { + if (t1.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.dcaXY(), t1.dcaZ(), t1.cYY(), t1.cZY(), t1.cZZ())); + } + } + } + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t2.globalIndex()); + if (cfgDoMix) { + if (t2.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.dcaXY(), t2.dcaZ(), t2.cYY(), t2.cZY(), t2.cZZ())); + } + } + } + } else if (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t1.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t1.globalIndex()); + if (cfgDoMix) { + if (t1.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t1.pt(), t1.eta(), t1.phi(), leptonM1, t1.sign(), t1.fwdDcaX(), t1.fwdDcaY(), t1.cXXatDCA(), t1.cXYatDCA(), t1.cYYatDCA())); + } + } + } + if (std::find(used_trackIds_per_col.begin(), used_trackIds_per_col.end(), t2.globalIndex()) == used_trackIds_per_col.end()) { + used_trackIds_per_col.emplace_back(t2.globalIndex()); + if (cfgDoMix) { + if (t2.sign() > 0) { + emh_pos->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); + } else { + emh_neg->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::dilepton::utils::EMFwdTrack(t2.pt(), t2.eta(), t2.phi(), leptonM2, t2.sign(), t2.fwdDcaX(), t2.fwdDcaY(), t2.cXXatDCA(), t2.cXYatDCA(), t2.cYYatDCA())); + } + } + } + } + } + return true; + } + + o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); + o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; + o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; + o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; + using FilteredMyCollisions = o2::soa::Filtered; + using FilteredMyCollisionsSWT = o2::soa::Filtered; + + o2::framework::SliceCache cache; + // o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; + o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; + o2::framework::expressions::Filter trackFilter_electron = dielectroncuts.cfg_min_pt_track < o2::aod::track::pt && dielectroncuts.cfg_min_eta_track < o2::aod::track::eta && o2::aod::track::eta < dielectroncuts.cfg_max_eta_track && nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz && o2::aod::track::itsChi2NCl < dielectroncuts.cfg_max_chi2its && o2::aod::track::tpcChi2NCl < dielectroncuts.cfg_max_chi2tpc; + o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; + o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); + o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); + + o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfb >= static_cast(0)); + + o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); + o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); + + // o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; + o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; + o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track; + o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); + + o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); + o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); + + TEMH* emh_pos = nullptr; + TEMH* emh_neg = nullptr; + + std::map, uint64_t> map_mixed_eventId_to_globalBC; + std::unordered_map map_best_match_globalmuon; + + std::vector used_trackIds_per_col; + uint64_t nProcessedEvents = 0; + double maxBufferedEvents = 0.; + + void fillEventBufferLevel() + { + ++nProcessedEvents; + if (cfgDoMix && nProcessedEvents % 10000 == 0) { + const double fraction = static_cast(map_mixed_eventId_to_globalBC.size()) / maxBufferedEvents; + fRegistry.fill(HIST("Pair/mix/hEventBufferFillLevel"), nProcessedEvents, fraction); + } + } + + int ndf = 0; + + template + void runPairing(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) + { + for (const auto& collision : collisions) { + initCCDB(collision); + + const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + float centrality = centralities[cfgCentEstimator]; + if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { + fillEventBufferLevel(); + continue; + } + + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + const float eventplanes_2_for_mix[7] = {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}; + float ep2 = eventplanes_2_for_mix[cfgEP2Estimator_for_Mix]; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if (nmod == 2) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, 2>(&fRegistry, collision); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, 3>(&fRegistry, collision); + } else { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, -1>(&fRegistry, collision); + } + if (nmod < 0 || isGoodQvector(qvectors)) { + fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev - 1); // is qvector calibarated + } + fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); + + if (!fEMEventCut.IsSelected(collision)) { + fillEventBufferLevel(); + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + fillEventBufferLevel(); + continue; + } + + if (nmod > 0 && !isGoodQvector(qvectors)) { + fillEventBufferLevel(); + continue; + } + + if constexpr (isTriggerAnalysis) { + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + if (!collision.triggerMask_bit(emswtId)) { + fillEventBufferLevel(); + continue; + } + + // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // continue; + // } + + // auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected + // auto TOIcounter = zorro.getTOIcounters()[0]; // this has to be called after zorro::isSelected + // auto ATcounter = zorro.getATcounters()[mToIidx]; // this has to be called after zorro::isSelected + + // if (swt_bitset.test(mToIidx)) { + // while (ATcounter > mATCounter) { + // mATCounter++; + // fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), collision.runNumber()); + // } + + // while (TOIcounter > mTOICounter) { + // fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), collision.runNumber()); + // mTOICounter++; // always incremented by 1 in zorro!! + // } + // // LOGF(info, "collision.globalIndex() = %d, collision.globalBC() = %llu, mTOICounter = %d, mATcounter = %d", collision.globalIndex(), collision.globalBC(), mTOICounter, mATCounter); + // } + } + + std::vector bootstrapweights = {}; + if (cfgAnalysisType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonAnalysisType::kBootstrapv2)) { // bootstrapping for accepted events + int randomSeed = static_cast(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + TRandom3 randomNumber(randomSeed); + for (int i = 0; i < cfgNumBootstrapSamples; i++) { + float poissonweight = 0.; + poissonweight = static_cast(randomNumber.PoissonD(1.0)); + bootstrapweights.push_back(poissonweight); + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfoBootstrap(&fRegistry, collision, i, poissonweight); + } + } else { + bootstrapweights.push_back(1.0); // to pass as non-empyt dummy to use + } + + if (nmod == 2) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, 2>(&fRegistry, collision); + } else if (nmod == 3) { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, 3>(&fRegistry, collision); + } else { + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); + } + fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev - 1); // is qvector calibarated + + fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); + + auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + // LOGF(info, "collision.globalIndex() = %d , collision.posZ() = %f , collision.numContrib() = %d, centrality = %f , posTracks_per_coll.size() = %d, negTracks_per_coll.size() = %d", collision.globalIndex(), collision.posZ(), collision.numContrib(), centralities[cfgCentEstimator], posTracks_per_coll.size(), negTracks_per_coll.size()); + + used_trackIds_per_col.reserve(posTracks_per_coll.size() + negTracks_per_coll.size()); + int nuls = 0, nlspp = 0, nlsmm = 0; + for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS + bool is_pair_ok = fillPairInfo<0>(collision, pos, neg, cut, tracks, bootstrapweights); + if (is_pair_ok) { + nuls++; + } + } + for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ + bool is_pair_ok = fillPairInfo<0>(collision, pos1, pos2, cut, tracks, bootstrapweights); + if (is_pair_ok) { + nlspp++; + } + } + for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- + bool is_pair_ok = fillPairInfo<0>(collision, neg1, neg2, cut, tracks, bootstrapweights); + if (is_pair_ok) { + nlsmm++; + } + } + used_trackIds_per_col.clear(); + used_trackIds_per_col.shrink_to_fit(); + + if (!cfgDoMix || !(nuls > 0 || nlspp > 0 || nlsmm > 0)) { + fillEventBufferLevel(); + continue; + } + + // event mixing + int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; + if (zbin < 0) { + zbin = 0; + } else if (static_cast(zvtx_bin_edges.size()) - 2 < zbin) { + zbin = static_cast(zvtx_bin_edges.size()) - 2; + } + + int centbin = lower_bound(cent_bin_edges.begin(), cent_bin_edges.end(), centrality) - cent_bin_edges.begin() - 1; + if (centbin < 0) { + centbin = 0; + } else if (static_cast(cent_bin_edges.size()) - 2 < centbin) { + centbin = static_cast(cent_bin_edges.size()) - 2; + } + + int epbin = lower_bound(ep_bin_edges.begin(), ep_bin_edges.end(), ep2) - ep_bin_edges.begin() - 1; + if (epbin < 0) { + epbin = 0; + } else if (static_cast(ep_bin_edges.size()) - 2 < epbin) { + epbin = static_cast(ep_bin_edges.size()) - 2; + } + + int occbin = -1; + if (cfgOccupancyEstimator == 0) { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } else if (cfgOccupancyEstimator == 1) { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.trackOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } else { + occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; + } + + if (occbin < 0) { + occbin = 0; + } else if (static_cast(occ_bin_edges.size()) - 2 < occbin) { + occbin = static_cast(occ_bin_edges.size()) - 2; + } + + // LOGF(info, "collision.globalIndex() = %d, collision.posZ() = %f, centrality = %f, ep2 = %f, collision.trackOccupancyInTimeRange() = %d, zbin = %d, centbin = %d, epbin = %d, occbin = %d", collision.globalIndex(), collision.posZ(), centrality, ep2, collision.trackOccupancyInTimeRange(), zbin, centbin, epbin, occbin); + + std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); + std::pair key_df_collision = std::make_pair(ndf, collision.globalIndex()); // this gives the current event. + + // make a vector of selected photons in this collision. + const auto& selected_posTracks_in_this_event = emh_pos->GetTracksPerCollision(key_df_collision); + const auto& selected_negTracks_in_this_event = emh_neg->GetTracksPerCollision(key_df_collision); + // LOGF(info, "N selected tracks in current event (%d, %d), zvtx = %f, centrality = %f , npos = %d , nneg = %d, nuls = %d , nlspp = %d, nlsmm = %d", ndf, collision.globalIndex(), collision.posZ(), centralities[cfgCentEstimator], selected_posTracks_in_this_event.size(), selected_negTracks_in_this_event.size(), nuls, nlspp, nlsmm); + + const auto& collisionIds_in_mixing_pool = emh_pos->GetCollisionIdsFromEventPool(key_bin); // pos/neg does not matter. + // LOGF(info, "collisionIds_in_mixing_pool.size() = %d", collisionIds_in_mixing_pool.size()); + + for (const auto& mix_dfId_collisionId : collisionIds_in_mixing_pool) { + int mix_dfId = mix_dfId_collisionId.first; + int mix_collisionId = mix_dfId_collisionId.second; + if (collision.globalIndex() == mix_collisionId && ndf == mix_dfId) { // this never happens. only protection. + continue; + } + + auto globalBC_mix = map_mixed_eventId_to_globalBC[mix_dfId_collisionId]; + uint64_t diffBC = std::max(collision.globalBC(), globalBC_mix) - std::min(collision.globalBC(), globalBC_mix); + fRegistry.fill(HIST("Pair/mix/hDiffBC"), diffBC); + if (diffBC < ndiff_bc_mix) { + continue; + } + + const auto& posTracks_from_event_pool = emh_pos->GetTracksPerCollision(mix_dfId_collisionId); + const auto& negTracks_from_event_pool = emh_neg->GetTracksPerCollision(mix_dfId_collisionId); + // LOGF(info, "Do event mixing: current event (%d, %d) | event pool (%d, %d), npos = %d , nneg = %d", ndf, collision.globalIndex(), mix_dfId, mix_collisionId, posTracks_from_event_pool.size(), negTracks_from_event_pool.size()); + + for (const auto& pos : selected_posTracks_in_this_event) { // ULS mix + for (const auto& neg : negTracks_from_event_pool) { + fillPairInfo<1>(collision, pos, neg, cut, nullptr, bootstrapweights); + } + } + + for (const auto& neg : selected_negTracks_in_this_event) { // ULS mix + for (const auto& pos : posTracks_from_event_pool) { + fillPairInfo<1>(collision, neg, pos, cut, nullptr, bootstrapweights); + } + } + + for (const auto& pos1 : selected_posTracks_in_this_event) { // LS++ mix + for (const auto& pos2 : posTracks_from_event_pool) { + fillPairInfo<1>(collision, pos1, pos2, cut, nullptr, bootstrapweights); + } + } + + for (const auto& neg1 : selected_negTracks_in_this_event) { // LS-- mix + for (const auto& neg2 : negTracks_from_event_pool) { + fillPairInfo<1>(collision, neg1, neg2, cut, nullptr, bootstrapweights); + } + } + } // end of loop over mixed event pool + + if (nuls > 0 || nlspp > 0 || nlsmm > 0) { + map_mixed_eventId_to_globalBC[key_df_collision] = collision.globalBC(); + const auto evicted_pos = emh_pos->AddCollisionIdAtLast(key_bin, key_df_collision); + const auto evicted_neg = emh_neg->AddCollisionIdAtLast(key_bin, key_df_collision); + if (evicted_pos != evicted_neg) { + LOGF(fatal, "Positive and negative mixing pools are out of sync"); + } + if (evicted_pos) { + map_mixed_eventId_to_globalBC.erase(*evicted_pos); + } + + } // end of if pair exist + + fillEventBufferLevel(); + } // end of collision loop + + } // end of DF + + template + bool isPairOK(TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&) + { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } else { // cut-based + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.IsSelectedTrack(t1) || !cut.IsSelectedTrack(t2)) { + return false; + } + if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { + return false; + } + } + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + } + return true; + } + + std::map, float> map_weight; // -> float + template + void fillPairWeightMap(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) + { + std::vector> passed_pairIds; + passed_pairIds.reserve(posTracks.size() * negTracks.size()); + + for (const auto& collision : collisions) { + initCCDB(collision); + const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { + continue; + } + + // if constexpr (isTriggerAnalysis) { // don't call this here. this causes double counting. + // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // continue; + // } + // } + + std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; + std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; + std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; + std::array q2btot = {collision.q2xbtot(), collision.q2ybtot()}; + std::array q2bpos = {collision.q2xbpos(), collision.q2ybpos()}; + std::array q2bneg = {collision.q2xbneg(), collision.q2ybneg()}; + std::array q2fv0a = {collision.q2xfv0a(), collision.q2yfv0a()}; + std::array q3ft0m = {collision.q3xft0m(), collision.q3yft0m()}; + std::array q3ft0a = {collision.q3xft0a(), collision.q3yft0a()}; + std::array q3ft0c = {collision.q3xft0c(), collision.q3yft0c()}; + std::array q3btot = {collision.q3xbtot(), collision.q3ybtot()}; + std::array q3bpos = {collision.q3xbpos(), collision.q3ybpos()}; + std::array q3bneg = {collision.q3xbneg(), collision.q3ybneg()}; + std::array q3fv0a = {collision.q3xfv0a(), collision.q3yfv0a()}; + + std::vector>> qvectors = { + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 0th harmonics + {{999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}, {999.f, 999.f}}, // 1st harmonics + {q2ft0m, q2ft0a, q2ft0c, q2btot, q2bpos, q2bneg, q2fv0a}, // 2nd harmonics + {q3ft0m, q3ft0a, q3ft0c, q3btot, q3bpos, q3bneg, q3fv0a}, // 3rd harmonics + }; + + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + + if (nmod > 0 && !isGoodQvector(qvectors)) { + continue; + } + + auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + + for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS + if (isPairOK(pos, neg, cut, tracks)) { + passed_pairIds.emplace_back(std::make_pair(pos.globalIndex(), neg.globalIndex())); + } + } + for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ + if (isPairOK(pos1, pos2, cut, tracks)) { + passed_pairIds.emplace_back(std::make_pair(pos1.globalIndex(), pos2.globalIndex())); + } + } + for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- + if (isPairOK(neg1, neg2, cut, tracks)) { + passed_pairIds.emplace_back(std::make_pair(neg1.globalIndex(), neg2.globalIndex())); + } + } + } // end of collision loop + + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + for (const auto& pairId : passed_pairIds) { + auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); + auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); + + float n = 1.f; // include myself. + for (const auto& ambId1 : t1.ambiguousElectronsIds()) { + for (const auto& ambId2 : t2.ambiguousElectronsIds()) { + if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { + n += 1.f; + } + } + } + map_weight[pairId] = 1.f / n; + } // end of passed_pairIds loop + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + for (const auto& pairId : passed_pairIds) { + auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); + auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); + + float n = 1.f; // include myself. + for (const auto& ambId1 : t1.ambiguousMuonsIds()) { + for (const auto& ambId2 : t2.ambiguousMuonsIds()) { + if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { + n += 1.f; + } + } + } + map_weight[pairId] = 1.f / n; + } // end of passed_pairIds loop + } + passed_pairIds.clear(); + passed_pairIds.shrink_to_fit(); + } + + void processAnalysis(FilteredMyCollisions const& collisions, Types const&... args) + { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + auto electrons = std::get<0>(std::tie(args...)); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } + runPairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + auto muons = std::get<0>(std::tie(args...)); + map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(muons, fDimuonCut); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + map_weight.clear(); + map_best_match_globalmuon.clear(); + ndf++; + } + PROCESS_SWITCH(Dilepton, processAnalysis, "run dilepton analysis", true); + + void processTriggerAnalysis(FilteredMyCollisionsSWT const& collisions, Types const&... args, o2::aod::EMSWTriggerATCounters const& countersAT, o2::aod::EMSWTriggerATOICounters const& countersATOI) + { + if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { + auto electrons = std::get<0>(std::tie(args...)); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } + runPairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); + } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { + auto muons = std::get<0>(std::tie(args...)); + map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(muons, fDimuonCut); + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + map_weight.clear(); + map_best_match_globalmuon.clear(); + ndf++; + + // for nomalization + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + for (const auto& counter : countersAT) { + if (counter.isAT_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), mRunNumber); + } + } + for (const auto& counter : countersATOI) { + if (counter.isAToI_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), mRunNumber); + } + } + } + PROCESS_SWITCH(Dilepton, processTriggerAnalysis, "run dilepton analysis on triggered data", false); + + void processNorm(o2::aod::EMEventNormInfos const& collisions) + { + for (const auto& collision : collisions) { + if (collision.centFT0C() < eventcuts.cfgCentMin || eventcuts.cfgCentMax < collision.centFT0C()) { + continue; + } + fRegistry.fill(HIST("Event/norm/hZvtx"), collision.posZ()); + + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 1.0); + if (collision.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 2.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 3.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 4.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoSameBunchPileup)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 5.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodZvtxFT0vsPV)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 6.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsVertexITSTPC)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 7.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsVertexTRDmatched)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 8.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsVertexTOFmatched)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 9.0); + } + if (collision.sel8()) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 10.0); + } + if (std::fabs(collision.posZ()) < 10.0) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 11.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStandard)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 12.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInTimeRangeStrict)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 13.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStandard)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 14.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoCollInRofStrict)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 15.0); + } + if (collision.selection_bit(o2::aod::emevsel::kNoHighMultCollInPrevRof)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 16.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer3)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 17.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayer0123)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 18.0); + } + if (collision.selection_bit(o2::aod::emevsel::kIsGoodITSLayersAll)) { + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), 19.0); + } + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + fRegistry.fill(HIST("Event/norm/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + } // end of collision loop + } + PROCESS_SWITCH(Dilepton, processNorm, "process normalization info", false); + + void processBC(o2::aod::EMBCs const& bcs) + { + for (const auto& bc : bcs) { + if (bc.selection_bit(o2::aod::emevsel::kIsTriggerTVX)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 0.f); + + if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 1.f); + } + if (bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 2.f); + } + if (rctChecker(bc)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 3.f); + } + if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 4.f); + } + if (bc.selection_bit(o2::aod::emevsel::kNoTimeFrameBorder) && bc.selection_bit(o2::aod::emevsel::kNoITSROFrameBorder) && rctChecker(bc)) { + fRegistry.fill(HIST("BC/hTVXCounter"), 5.f); + } + } + } + } + PROCESS_SWITCH(Dilepton, processBC, "process BC counter", false); + + void processDummy(MyCollisions const&) {} + PROCESS_SWITCH(Dilepton, processDummy, "Dummy function", false); +}; + +#endif // PWGEM_DILEPTON_CORE_DILEPTONMIXINGTEST_H_ diff --git a/PWGEM/Dilepton/Core/DileptonProducer.h b/PWGEM/Dilepton/Core/DileptonProducer.h deleted file mode 100644 index 93077f63c32..00000000000 --- a/PWGEM/Dilepton/Core/DileptonProducer.h +++ /dev/null @@ -1,782 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code runs loop over leptons. -// Please write to: daiki.sekihata@cern.ch - -#ifndef PWGEM_DILEPTON_CORE_DILEPTONPRODUCER_H_ -#define PWGEM_DILEPTON_CORE_DILEPTONPRODUCER_H_ - -#include "PWGEM/Dilepton/Core/DielectronCut.h" -#include "PWGEM/Dilepton/Core/DimuonCut.h" -#include "PWGEM/Dilepton/Core/EMEventCut.h" -#include "PWGEM/Dilepton/DataModel/dileptonTables.h" -#include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using MyCollisions = o2::soa::Join; -using MyCollision = MyCollisions::iterator; - -using MyElectrons = o2::soa::Join; -using MyElectron = MyElectrons::iterator; -using FilteredMyElectrons = o2::soa::Filtered; -using FilteredMyElectron = FilteredMyElectrons::iterator; - -using MyMuons = o2::soa::Join; -using MyMuon = MyMuons::iterator; -using FilteredMyMuons = o2::soa::Filtered; -using FilteredMyMuon = FilteredMyMuons::iterator; - -template -struct DileptonProducer { - o2::framework::Produces eventTable; - o2::framework::Produces normTable; - o2::framework::Produces dileptonTable; - - // Configurables - o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; - o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; - - o2::framework::Configurable cfgEP2Estimator_for_Mix{"cfgEP2Estimator_for_Mix", 3, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5"}; - o2::framework::Configurable cfgQvecEstimator{"cfgQvecEstimator", 0, "FT0M:0, FT0A:1, FT0C:2, BTot:3, BPos:4, BNeg:5"}; - o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; - o2::framework::Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; - o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; - o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; - o2::framework::Configurable cfgStoreULS{"cfgStoreULS", true, "flag to store ULS pairs"}; - o2::framework::Configurable cfgStoreLS{"cfgStoreLS", true, "flag to store LS pairs"}; - - EMEventCut fEMEventCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "eventcut_group"; - o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; - o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; - o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; - o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; - o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; - o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; - o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; - o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. - o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. - o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; - o2::framework::Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; - o2::framework::Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; - o2::framework::Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "require no collision in time range standard"}; - o2::framework::Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "require no collision in time range strict"}; - o2::framework::Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "require no HM collision in previous ITS ROF"}; - o2::framework::Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "number of inactive chips on ITS layer 3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "number of inactive chips on ITS layers 0-3 are below threshold "}; - o2::framework::Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "number of inactive chips on all ITS layers are below threshold "}; - // for RCT - o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; - o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; - o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; - o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; - - o2::framework::Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; - o2::framework::Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; - o2::framework::Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; - o2::framework::Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; - } eventcuts; - - DielectronCut fDielectronCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dielectroncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pT"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pT"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -0.8, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.8, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dca3d{"cfg_min_pair_dca3d", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dca3d{"cfg_max_pair_dca3d", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_phiv{"cfg_apply_phiv", true, "flag to apply phiv cut"}; - o2::framework::Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; - o2::framework::Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; - o2::framework::Configurable cfg_min_phiv{"cfg_min_phiv", 0.0, "min phiv (constant)"}; - o2::framework::Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv (constant)"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut at PV"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.2, "min dphi between 2 electrons (elliptic cut)"}; - o2::framework::Configurable cfg_min_opang{"cfg_min_opang", 0.0, "min opening angle"}; - o2::framework::Configurable cfg_max_opang{"cfg_max_opang", 6.4, "max opening angle"}; - // o2::framework::Configurable cfg_require_diff_sides{"cfg_require_diff_sides", false, "flag to require 2 tracks are from different sides."}; - - o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; - o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply pair cut same as prefilter set in derived data"}; - o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_mirror_phi_track{"cfg_mirror_phi_track", false, "mirror the phi cut around Pi, min and max Phi should be in 0-Pi"}; - o2::framework::Configurable cfg_reject_phi_track{"cfg_reject_phi_track", false, "reject the phi interval"}; - o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; - o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; - o2::framework::Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 100, "min ncrossed rows"}; - o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; - o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 5.0, "max chi2/NclsITS"}; - o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 0.2, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 0.2, "max dca Z for single track in cm"}; - o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; - o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; - o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; - o2::framework::Configurable cfg_max_its_cluster_size{"cfg_max_its_cluster_size", 16.f, "max ITS cluster size"}; - // o2::framework::Configurable cfg_min_rel_diff_pin{"cfg_min_rel_diff_pin", -1e+10, "min rel. diff. between pin and ppv"}; - // o2::framework::Configurable cfg_max_rel_diff_pin{"cfg_max_rel_diff_pin", +1e+10, "max rel. diff. between pin and ppv"}; - o2::framework::Configurable cfgRefR{"cfgRefR", 0.50, "ref. radius (m) for calculating phi position"}; // 0.50 +/- 0.06 can be syst. unc. - o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; - o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; - - o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif : 4, kPIDML : 5, kTPChadrejORTOFreq_woTOFif : 6]"}; - o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; - // o2::framework::Configurable cfg_min_TPCNsigmaMu{"cfg_min_TPCNsigmaMu", -0.0, "min. TPC n sigma for muon exclusion"}; - // o2::framework::Configurable cfg_max_TPCNsigmaMu{"cfg_max_TPCNsigmaMu", +0.0, "max. TPC n sigma for muon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3.0, "max. TPC n sigma for pion exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3.0, "min. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3.0, "max. TPC n sigma for kaon exclusion"}; - o2::framework::Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3.0, "min. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3.0, "max. TPC n sigma for proton exclusion"}; - o2::framework::Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3.0, "min. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3.0, "max. TOF n sigma for electron inclusion"}; - o2::framework::Configurable cfg_min_pin_pirejTPC{"cfg_min_pin_pirejTPC", 0.f, "min. pin for pion rejection in TPC"}; - o2::framework::Configurable cfg_max_pin_pirejTPC{"cfg_max_pin_pirejTPC", 1e+10, "max. pin for pion rejection in TPC"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - - // configuration for PID ML - o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.98, 0.98, 0.9, 0.9, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; - o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - } dielectroncuts; - - DimuonCut fDimuonCut; - struct : o2::framework::ConfigurableGroup { - std::string prefix = "dimuoncut_group"; - o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; - o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; - o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pt"}; - o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pt"}; - o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -4.0, "min pair rapidity"}; - o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", -2.5, "max pair rapidity"}; - o2::framework::Configurable cfg_min_pair_dcaxy{"cfg_min_pair_dcaxy", 0.0, "min pair dca3d in sigma"}; - o2::framework::Configurable cfg_max_pair_dcaxy{"cfg_max_pair_dcaxy", 1e+10, "max pair dca3d in sigma"}; - o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut"}; - o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; - o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; - - o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; - o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h - - o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; - o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.2, "min pT for single track"}; - o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; - o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; - o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; - o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; - o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; - o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; - o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; - o2::framework::Configurable cfg_max_chi2mft{"cfg_max_chi2mft", 1e+6, "max chi2/ndf"}; - // o2::framework::Configurable cfg_max_matching_chi2_mftmch{"cfg_max_matching_chi2_mftmch", 40, "max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_border_pt_for_chi2mchmft{"cfg_border_pt_for_chi2mchmft", 0, "border pt for different max chi2 for MFT-MCH matching"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_lowPt{"cfg_max_matching_chi2_mftmch_lowPt", 8, "max chi2 for MFT-MCH matching for low pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mftmch_highPt{"cfg_max_matching_chi2_mftmch_highPt", 40, "max chi2 for MFT-MCH matching for high pT"}; - o2::framework::Configurable cfg_max_matching_chi2_mchmid{"cfg_max_matching_chi2_mchmid", 1e+10, "max chi2 for MCH-MID matching"}; - o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; - o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; - o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; - o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; - o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; - o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; - } dimuoncuts; - - o2::aod::rctsel::RCTFlagsChecker rctChecker; - o2::framework::Service ccdb; - int mRunNumber; - float d_bz; - - o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; - - float leptonM1 = 0.f; - float leptonM2 = 0.f; - - void init(o2::framework::InitContext& /*context*/) - { - mRunNumber = 0; - d_bz = 0; - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); - - DefineEMEventCut(); - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - DefineDielectronCut(); - leptonM1 = o2::constants::physics::MassElectron; - leptonM2 = o2::constants::physics::MassElectron; - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - DefineDimuonCut(); - leptonM1 = o2::constants::physics::MassMuon; - leptonM2 = o2::constants::physics::MassMuon; - } - } - - template - void initCCDB(TCollision const& collision) - { - if (mRunNumber == collision.runNumber()) { - return; - } - - // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { - d_bz = d_bz_input; - o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); - } - o2::base::Propagator::initFieldFromGRP(&grpmag); - mRunNumber = collision.runNumber(); - return; - } - - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - o2::base::Propagator::initFieldFromGRP(grpo); - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - o2::base::Propagator::initFieldFromGRP(grpmag); - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - } - mRunNumber = collision.runNumber(); - fDielectronCut.SetTrackPhiPositionRange(dielectroncuts.cfg_min_phiposition_track, dielectroncuts.cfg_max_phiposition_track, dielectroncuts.cfgRefR, d_bz, dielectroncuts.cfg_mirror_phi_track); - } - - ~DileptonProducer() {} - - void DefineEMEventCut() - { - fEMEventCut = EMEventCut("fEMEventCut", "fEMEventCut"); - fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); - fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); - fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); - fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); - fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); - fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); - fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); - fEMEventCut.SetRequireVertexTOFmatched(eventcuts.cfgRequireVertexTOFmatched); - fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); - fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); - fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); - fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); - fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); - fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); - fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); - fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); - fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); - } - - void DefineDielectronCut() - { - fDielectronCut = DielectronCut("fDielectronCut", "fDielectronCut"); - - // for pair - fDielectronCut.SetMeeRange(dielectroncuts.cfg_min_mass, dielectroncuts.cfg_max_mass); - fDielectronCut.SetPairPtRange(dielectroncuts.cfg_min_pair_pt, dielectroncuts.cfg_max_pair_pt); - fDielectronCut.SetPairYRange(dielectroncuts.cfg_min_pair_y, dielectroncuts.cfg_max_pair_y); - fDielectronCut.SetPairDCARange(dielectroncuts.cfg_min_pair_dca3d, dielectroncuts.cfg_max_pair_dca3d); // in sigma - fDielectronCut.SetMaxMeePhiVDep([&](float phiv) { return dielectroncuts.cfg_phiv_intercept + phiv * dielectroncuts.cfg_phiv_slope; }, dielectroncuts.cfg_min_phiv, dielectroncuts.cfg_max_phiv); - fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); - fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); - fDielectronCut.SetPairOpAng(dielectroncuts.cfg_min_opang, dielectroncuts.cfg_max_opang); - // fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); - - // for track - fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); - fDielectronCut.SetTrackEtaRange(dielectroncuts.cfg_min_eta_track, dielectroncuts.cfg_max_eta_track); - fDielectronCut.SetTrackPhiRange(dielectroncuts.cfg_min_phi_track, dielectroncuts.cfg_max_phi_track, dielectroncuts.cfg_mirror_phi_track, dielectroncuts.cfg_reject_phi_track); - fDielectronCut.SetMinNClustersTPC(dielectroncuts.cfg_min_ncluster_tpc); - fDielectronCut.SetMinNCrossedRowsTPC(dielectroncuts.cfg_min_ncrossedrows); - fDielectronCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); - fDielectronCut.SetMaxFracSharedClustersTPC(dielectroncuts.cfg_max_frac_shared_clusters_tpc); - fDielectronCut.SetChi2PerClusterTPC(0.0, dielectroncuts.cfg_max_chi2tpc); - fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); - fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); - fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); - fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); - fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); - fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); - fDielectronCut.RequireITSib1st(dielectroncuts.cfg_require_itsib_1st); - fDielectronCut.SetChi2TOF(0, dielectroncuts.cfg_max_chi2tof); - // fDielectronCut.SetRelDiffPin(dielectroncuts.cfg_min_rel_diff_pin, dielectroncuts.cfg_max_rel_diff_pin); - - // for eID - fDielectronCut.SetPIDScheme(dielectroncuts.cfg_pid_scheme); - fDielectronCut.SetTPCNsigmaElRange(dielectroncuts.cfg_min_TPCNsigmaEl, dielectroncuts.cfg_max_TPCNsigmaEl); - // fDielectronCut.SetTPCNsigmaMuRange(dielectroncuts.cfg_min_TPCNsigmaMu, dielectroncuts.cfg_max_TPCNsigmaMu); - fDielectronCut.SetTPCNsigmaPiRange(dielectroncuts.cfg_min_TPCNsigmaPi, dielectroncuts.cfg_max_TPCNsigmaPi); - fDielectronCut.SetTPCNsigmaKaRange(dielectroncuts.cfg_min_TPCNsigmaKa, dielectroncuts.cfg_max_TPCNsigmaKa); - fDielectronCut.SetTPCNsigmaPrRange(dielectroncuts.cfg_min_TPCNsigmaPr, dielectroncuts.cfg_max_TPCNsigmaPr); - fDielectronCut.SetTOFNsigmaElRange(dielectroncuts.cfg_min_TOFNsigmaEl, dielectroncuts.cfg_max_TOFNsigmaEl); - fDielectronCut.SetPinRangeForPionRejectionTPC(dielectroncuts.cfg_min_pin_pirejTPC, dielectroncuts.cfg_max_pin_pirejTPC); - - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut - std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMl.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMl.value[i]); - } - std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMl.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMl.value[i]); - } - fDielectronCut.SetMLThresholds(binsML, thresholdsML); - } // end of PID ML - } - - void DefineDimuonCut() - { - fDimuonCut = DimuonCut("fDimuonCut", "fDimuonCut"); - - // for pair - fDimuonCut.SetMassRange(dimuoncuts.cfg_min_mass, dimuoncuts.cfg_max_mass); - fDimuonCut.SetPairPtRange(dimuoncuts.cfg_min_pair_pt, dimuoncuts.cfg_max_pair_pt); - fDimuonCut.SetPairYRange(dimuoncuts.cfg_min_pair_y, dimuoncuts.cfg_max_pair_y); - fDimuonCut.SetPairDCAxyRange(dimuoncuts.cfg_min_pair_dcaxy, dimuoncuts.cfg_max_pair_dcaxy); - fDimuonCut.SetMindEtadPhi(dimuoncuts.cfg_apply_detadphi, dimuoncuts.cfg_min_deta, dimuoncuts.cfg_min_dphi); - - // for track - fDimuonCut.SetTrackType(dimuoncuts.cfg_track_type); - fDimuonCut.SetTrackPtRange(dimuoncuts.cfg_min_pt_track, dimuoncuts.cfg_max_pt_track); - fDimuonCut.SetTrackEtaRange(dimuoncuts.cfg_min_eta_track, dimuoncuts.cfg_max_eta_track); - fDimuonCut.SetTrackPhiRange(dimuoncuts.cfg_min_phi_track, dimuoncuts.cfg_max_phi_track); - fDimuonCut.SetNClustersMFT(dimuoncuts.cfg_min_ncluster_mft, 10); - fDimuonCut.SetNClustersMCHMID(dimuoncuts.cfg_min_ncluster_mch, 20); - fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); - fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); - // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); - fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); - fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); - fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); - fDimuonCut.SetRabs(dimuoncuts.cfg_min_rabs, dimuoncuts.cfg_max_rabs); - fDimuonCut.SetMaxPDCARabsDep([&](float rabs) { return (rabs < 26.5 ? 594.f : 324.f); }); - fDimuonCut.SetMaxdPtdEtadPhiwrtMCHMID(dimuoncuts.cfg_max_relDPt_wrt_matchedMCHMID, dimuoncuts.cfg_max_DEta_wrt_matchedMCHMID, dimuoncuts.cfg_max_DPhi_wrt_matchedMCHMID); // this is relevant for global muons - fDimuonCut.SetMFTHitMap(dimuoncuts.requireMFTHitMap, dimuoncuts.requiredMFTDisks); - } - - template - bool fillPairInfo(TCollision const&, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { - // return false; - // } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { - // return false; - // } - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2, d_bz, 0)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } - - float weight = 1.f; - if (cfgApplyWeightTTCA) { - weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; - } - - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), leptonM2); - // ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - float dca1 = 999.f, dca2 = 999.f; - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - dca1 = o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t1); - dca2 = o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(t2); - if (cfgDCAType == 1) { - dca1 = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t1); - dca2 = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(t2); - } else if (cfgDCAType == 2) { - dca1 = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t1); - dca2 = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(t2); - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - dca1 = o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t1); - dca2 = o2::aod::pwgem::dilepton::utils::emtrackutil::fwdDcaXYinSigma(t2); - } - - // fill table here - dileptonTable(eventTable.lastIndex() + 1, // lastIndex starts from -1. - t1.pt(), t1.eta(), t1.phi(), t1.sign(), dca1, - t2.pt(), t2.eta(), t2.phi(), t2.sign(), dca2, - weight); - - return true; - } - - o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); - o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; - o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; - o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; - using FilteredMyCollisions = o2::soa::Filtered; - - o2::framework::SliceCache cache; - o2::framework::Preslice perCollision_electron = o2::aod::emprimaryelectron::emeventId; - o2::framework::expressions::Filter trackFilter_electron = dielectroncuts.cfg_min_pt_track < o2::aod::track::pt && dielectroncuts.cfg_min_eta_track < o2::aod::track::eta && o2::aod::track::eta < dielectroncuts.cfg_max_eta_track && nabs(o2::aod::track::dcaXY) < dielectroncuts.cfg_max_dcaxy && nabs(o2::aod::track::dcaZ) < dielectroncuts.cfg_max_dcaz; - o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; - o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), o2::aod::emprimaryelectron::isAssociatedToMPC == true || o2::aod::emprimaryelectron::isAssociatedToMPC == false, o2::aod::emprimaryelectron::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); - - o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && - ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), - o2::aod::emprimaryelectron::pfb >= static_cast(0)); - - o2::framework::Partition positive_electrons = o2::aod::emprimaryelectron::sign > int8_t(0); - o2::framework::Partition negative_electrons = o2::aod::emprimaryelectron::sign < int8_t(0); - - o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; - o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track; - o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), o2::aod::emprimarymuon::isAssociatedToMPC == true || o2::aod::emprimarymuon::isAssociatedToMPC == false, o2::aod::emprimarymuon::isAssociatedToMPC == true); - o2::framework::expressions::Filter prefilter_derived_muon = ifnode(dimuoncuts.cfg_apply_cuts_from_prefilter_derived.node() && dimuoncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && - ifnode((dimuoncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimarymuon::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), - o2::aod::emprimarymuon::pfbderived >= static_cast(0)); - o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); - o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); - - template - void runPairing(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) - { - for (const auto& collision : collisions) { - initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - // float centrality = centralities[cfgCentEstimator]; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - float eventplanes_2_for_mix[7] = {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg(), collision.ep2fv0a()}; - float ep2 = eventplanes_2_for_mix[cfgEP2Estimator_for_Mix]; - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - - int nuls = 0, nlspp = 0, nlsmm = 0; - - if (cfgStoreULS) { - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - bool is_pair_ok = fillPairInfo(collision, pos, neg, cut, tracks); - if (is_pair_ok) { - nuls++; - } - } - } - if (cfgStoreLS) { - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - bool is_pair_ok = fillPairInfo(collision, pos1, pos2, cut, tracks); - if (is_pair_ok) { - nlspp++; - } - } - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - bool is_pair_ok = fillPairInfo(collision, neg1, neg2, cut, tracks); - if (is_pair_ok) { - nlsmm++; - } - } - } - - if (nuls > 0 || nlspp > 0 || nlsmm > 0) { - eventTable(collision.runNumber(), collision.globalBC(), collision.timestamp(), collision.posZ(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), centralities[cfgCentEstimator], ep2); - } - } // end of collision loop - } // end of DF - - template - bool isPairOK(TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, TAllTracks const&) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } else { // cut-based - if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { - return false; - } - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.IsSelectedTrack(t1) || !cut.IsSelectedTrack(t2)) { - return false; - } - if (!map_best_match_globalmuon[t1.globalIndex()] || !map_best_match_globalmuon[t2.globalIndex()]) { - return false; - } - - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t1, cut, tracks)) { - // return false; - // } - // if (!o2::aod::pwgem::dilepton::utils::emtrackutil::isBestMatch(t2, cut, tracks)) { - // return false; - // } - } - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.IsSelectedPair(t1, t2, d_bz, 0.0)) { - return false; - } - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - if (!cut.IsSelectedPair(t1, t2)) { - return false; - } - } - return true; - } - - std::map, float> map_weight; // -> float - template - void fillPairWeightMap(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) - { - std::vector> passed_pairIds; - passed_pairIds.reserve(posTracks.size() * negTracks.size()); - - for (const auto& collision : collisions) { - initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centralities[cfgCentEstimator]) { - continue; - } - - if (!fEMEventCut.IsSelected(collision)) { - continue; - } - if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); - - for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS - if (isPairOK(pos, neg, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos.globalIndex(), neg.globalIndex())); - } - } - for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ - if (isPairOK(pos1, pos2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(pos1.globalIndex(), pos2.globalIndex())); - } - } - for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- - if (isPairOK(neg1, neg2, cut, tracks)) { - passed_pairIds.emplace_back(std::make_pair(neg1.globalIndex(), neg2.globalIndex())); - } - } - } // end of collision loop - - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - // LOGF(info, "std::get<0>(pairId) = %d, std::get<1>(pairId) = %d, t1.globalIndex() = %d, t2.globalIndex() = %d", std::get<0>(pairId), std::get<1>(pairId), t1.globalIndex(), t2.globalIndex()); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousElectronsIds()) { - for (const auto& ambId2 : t2.ambiguousElectronsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - for (const auto& pairId : passed_pairIds) { - auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); - auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); - - float n = 1.f; // include myself. - for (const auto& ambId1 : t1.ambiguousMuonsIds()) { - for (const auto& ambId2 : t2.ambiguousMuonsIds()) { - if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { - n += 1.f; - } - } - } - map_weight[pairId] = 1.f / n; - } // end of passed_pairIds loop - } - passed_pairIds.clear(); - passed_pairIds.shrink_to_fit(); - } - - std::unordered_map map_best_match_globalmuon; - - void processAnalysis(FilteredMyCollisions const& collisions, Types const&... args) - { - if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - auto electrons = std::get<0>(std::tie(args...)); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); - } - runPairing(collisions, positive_electrons, negative_electrons, o2::aod::emprimaryelectron::emeventId, fDielectronCut, electrons); - } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { - auto muons = std::get<0>(std::tie(args...)); - map_best_match_globalmuon = o2::aod::pwgem::dilepton::utils::emtrackutil::findBestMatchMap(muons, fDimuonCut); - if (cfgApplyWeightTTCA) { - fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); - } - runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); - } - map_weight.clear(); - map_best_match_globalmuon.clear(); - } - PROCESS_SWITCH(DileptonProducer, processAnalysis, "run dilepton analysis", true); - - void processNorm(o2::aod::EMEventNormInfos const& collisions) - { - for (const auto& collision : collisions) { - if (collision.centFT0C() < eventcuts.cfgCentMin || eventcuts.cfgCentMax < collision.centFT0C()) { - continue; - } - - normTable(collision.selection_raw(), collision.rct_raw(), collision.posZint8(), collision.centFT0Muint8(), collision.centFT0Cuint8(), collision.centNTPVuint8() /*, collision.centNGlobaluint8()*/); - - } // end of collision loop - } - PROCESS_SWITCH(DileptonProducer, processNorm, "process normalization info", true); -}; - -#endif // PWGEM_DILEPTON_CORE_DILEPTONPRODUCER_H_ diff --git a/PWGEM/Dilepton/Core/DimuonCut.cxx b/PWGEM/Dilepton/Core/DimuonCut.cxx index f3a67d06d4a..1c9cd3f70fa 100644 --- a/PWGEM/Dilepton/Core/DimuonCut.cxx +++ b/PWGEM/Dilepton/Core/DimuonCut.cxx @@ -20,6 +20,7 @@ #include #include +#include #include ClassImp(DimuonCut); @@ -98,9 +99,14 @@ void DimuonCut::SetMatchingChi2MCHMFT(float min, float max) } void DimuonCut::SetMaxMatchingChi2MCHMFTPtDep(std::function PtDepCut) { - mMaxMatchingChi2MCHMFTPtDep = PtDepCut; + mMaxMatchingChi2MCHMFTPtDep = std::move(PtDepCut); LOG(info) << "Dimuon Cut, set matching chi2 MFT-MCH range: " << mMaxMatchingChi2MCHMFTPtDep(0.5); } +void DimuonCut::SetMaxDiffMatchingChi2MCHMFT(float diff) +{ + mMaxDiffMatchingChi2MCHMFT = diff; + LOG(info) << "Dimuon Cut, set max diff. matching chi2 MFT-MCH: " << mMaxDiffMatchingChi2MCHMFT; +} void DimuonCut::SetMatchingChi2MCHMID(float min, float max) { mMinMatchingChi2MCHMID = min; @@ -138,13 +144,13 @@ void DimuonCut::EnableTTCA(const bool flag) } void DimuonCut::SetMaxPDCARabsDep(std::function RabsDepCut) { - mMaxPDCARabsDep = RabsDepCut; + mMaxPDCARabsDep = std::move(RabsDepCut); LOG(info) << "Dimuon Cut, set max pDCA as a function of Rabs: " << mMaxPDCARabsDep(10.0); } void DimuonCut::SetMFTHitMap(bool flag, std::vector hitMap) { mApplyMFTHitMap = flag; - mRequiredMFTDisks = hitMap; + mRequiredMFTDisks = std::move(hitMap); if (mApplyMFTHitMap) { for (const auto& iDisk : mRequiredMFTDisks) { LOG(info) << "Dimuon Cut, require MFT hit on Disk: " << iDisk; diff --git a/PWGEM/Dilepton/Core/DimuonCut.h b/PWGEM/Dilepton/Core/DimuonCut.h index 4f009b456db..7f31744dc93 100644 --- a/PWGEM/Dilepton/Core/DimuonCut.h +++ b/PWGEM/Dilepton/Core/DimuonCut.h @@ -65,26 +65,27 @@ class DimuonCut : public TNamed kPDCA, kMFTHitMap, kDPtDEtaDPhiwrtMCHMID, + kDiffMatchingChi2MCHMFT, kTTCA, kNCuts }; - template - bool IsSelected(TPair const& pair) const - { - auto t1 = std::get<0>(pair); - auto t2 = std::get<1>(pair); + // template + // bool IsSelected(TPair const& pair) const + // { + // auto t1 = std::get<0>(pair); + // auto t2 = std::get<1>(pair); - if (!IsSelectedTrack(t1) || !IsSelectedTrack(t2)) { - return false; - } + // if (!IsSelectedTrack(t1) || !IsSelectedTrack(t2)) { + // return false; + // } - if (!IsSelectedPair(t1, t2)) { - return false; - } + // if (!IsSelectedPair(t1, t2)) { + // return false; + // } - return true; - } + // return true; + // } template bool IsSelectedPair(TTrack1 const& t1, TTrack2 const& t2) const @@ -177,6 +178,9 @@ class DimuonCut : public TNamed if (track.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !IsSelectedTrack(track, DimuonCuts::kDPtDEtaDPhiwrtMCHMID)) { return false; } + if (track.trackType() == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && !IsSelectedTrack(track, DimuonCuts::kDiffMatchingChi2MCHMFT)) { + return false; + } return true; } @@ -241,6 +245,9 @@ class DimuonCut : public TNamed case DimuonCuts::kDPtDEtaDPhiwrtMCHMID: return std::fabs(track.ptMatchedMCHMID() - track.pt()) / track.pt() < mMaxReldPtwrtMCHMID && std::sqrt(std::pow((track.etaMatchedMCHMID() - track.eta()) / mMaxdEtawrtMCHMID, 2) + std::pow((track.phiMatchedMCHMID() - track.phi()) / mMaxdPhiwrtMCHMID, 2)) < 1.f; + case DimuonCuts::kDiffMatchingChi2MCHMFT: + return track.diffChi2MatchingMCHMFT() > mMaxDiffMatchingChi2MCHMFT; + default: return false; } @@ -269,6 +276,7 @@ class DimuonCut : public TNamed void SetMFTHitMap(bool flag, std::vector hitMap); void SetMaxdPtdEtadPhiwrtMCHMID(float reldPtMax, float dEtaMax, float dPhiMax); // this is relevant for global muons void SetMaxMatchingChi2MCHMFTPtDep(std::function PtDepCut); + void SetMaxDiffMatchingChi2MCHMFT(float diff); void EnableTTCA(bool flag); private: @@ -301,6 +309,7 @@ class DimuonCut : public TNamed float mMinRabs{17.6}, mMaxRabs{89.5}; float mMinDcaXY{0.0f}, mMaxDcaXY{1e10f}; float mMaxReldPtwrtMCHMID{1e10f}, mMaxdEtawrtMCHMID{1e10f}, mMaxdPhiwrtMCHMID{1e10f}; + float mMaxDiffMatchingChi2MCHMFT{-1.f}; bool mApplyMFTHitMap{false}; std::vector mRequiredMFTDisks{}; diff --git a/PWGEM/Dilepton/Core/EMEventCut.h b/PWGEM/Dilepton/Core/EMEventCut.h index b0450f5b575..d9671356e24 100644 --- a/PWGEM/Dilepton/Core/EMEventCut.h +++ b/PWGEM/Dilepton/Core/EMEventCut.h @@ -16,7 +16,7 @@ #ifndef PWGEM_DILEPTON_CORE_EMEVENTCUT_H_ #define PWGEM_DILEPTON_CORE_EMEVENTCUT_H_ -#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" #include diff --git a/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h b/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h index fe78534478e..a0ba6d2b630 100644 --- a/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h +++ b/PWGEM/Dilepton/Core/PWGEMDileptonCoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ -#define PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -20,5 +19,3 @@ #pragma link C++ class DielectronCut + ; #pragma link C++ class DimuonCut + ; #pragma link C++ class EMTrackCut + ; - -#endif // PWGEM_DILEPTON_CORE_PWGEMDILEPTONCORELINKDEF_H_ diff --git a/PWGEM/Dilepton/Core/SingleTrackQC.h b/PWGEM/Dilepton/Core/SingleTrackQC.h index 487589c4862..0530a64e6e2 100644 --- a/PWGEM/Dilepton/Core/SingleTrackQC.h +++ b/PWGEM/Dilepton/Core/SingleTrackQC.h @@ -20,6 +20,7 @@ #include "PWGEM/Dilepton/Core/DielectronCut.h" #include "PWGEM/Dilepton/Core/DimuonCut.h" #include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" #include "PWGEM/Dilepton/DataModel/dileptonTables.h" #include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" #include "PWGEM/Dilepton/Utils/EventHistograms.h" @@ -54,6 +55,8 @@ #include #include +#include + #include #include #include @@ -67,7 +70,10 @@ using MyCollisions = o2::soa::Join; using MyCollision = MyCollisions::iterator; -using MyElectrons = o2::soa::Join; +using MyCollisionsSWT = o2::soa::Join; +using MyCollisionSWT = MyCollisionsSWT::iterator; + +using MyElectrons = o2::soa::Join; using MyElectron = MyElectrons::iterator; using FilteredMyElectrons = o2::soa::Filtered; using FilteredMyElectron = FilteredMyElectrons::iterator; @@ -155,6 +161,8 @@ struct SingleTrackQC { o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 0.2, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 0.2, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; + o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA. 0:3D, 1:XY, 2:Z, else:3D"}; o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; @@ -165,6 +173,12 @@ struct SingleTrackQC { o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; + o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; + o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h + + o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; + o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h + o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif = 4, kPIDML = 5]"}; o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; @@ -183,15 +197,15 @@ struct SingleTrackQC { o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; // configuration for PID ML - o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.98, 0.98, 0.9, 0.9, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; - o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + // o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + // o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; + // o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; + // o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; + // o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + // o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; } dielectroncuts; DimuonCut fDimuonCut; @@ -205,6 +219,7 @@ struct SingleTrackQC { o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "max phi for single track"}; o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; @@ -217,6 +232,7 @@ struct SingleTrackQC { o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; + o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; @@ -264,7 +280,6 @@ struct SingleTrackQC { // track info fRegistry.add("Track/positive/hs", "rec. single electron", o2::framework::HistType::kTHnSparseD, {axis_pt, axis_eta, axis_phi, axis_dca3D, axis_dcaXY, axis_dcaZ}, true); - fRegistry.add("Track/positive/hPhiPosition", Form("phi position at r_{xy} = %3.2f m", dielectroncuts.cfgRefR.value), o2::framework::HistType::kTH1F, {axis_phiposition}, false); fRegistry.add("Track/positive/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", o2::framework::HistType::kTH1F, {{4000, -20, 20}}, false); fRegistry.add("Track/positive/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", o2::framework::HistType::kTH2F, {{200, -1.0f, 1.0f}, {200, -1.f, 1.f}}, false); fRegistry.add("Track/positive/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", o2::framework::HistType::kTH2F, {{400, -20.0f, 20.0f}, {400, -20.0f, 20.0f}}, false); @@ -279,7 +294,7 @@ struct SingleTrackQC { fRegistry.add("Track/positive/hTPCNcls2Nf", "TPC Ncls/Nfindable;TPC N_{cls}/N_{cls}^{findable}", o2::framework::HistType::kTH1F, {{200, 0, 2}}, false); fRegistry.add("Track/positive/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); fRegistry.add("Track/positive/hNclsITS", "number of ITS clusters;ITS N_{cls}", o2::framework::HistType::kTH1F, {{8, -0.5, 7.5}}, false); - fRegistry.add("Track/positive/hChi2ITS", "chi2/number of ITS clusters;ITS #chi^{2}/N_{cls}", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Track/positive/hChi2ITS", "chi2/number of ITS clusters;ITS #chi^{2}/N_{cls}", o2::framework::HistType::kTH1F, {{400, 0, 40}}, false); fRegistry.add("Track/positive/hITSClusterMap", "ITS cluster map", o2::framework::HistType::kTH1F, {{128, -0.5, 127.5}}, false); fRegistry.add("Track/positive/hChi2TOF", "TOF Chi2;p_{pv} (GeV/c);TOF #chi^{2}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, 0, 10}}, false); @@ -297,7 +312,7 @@ struct SingleTrackQC { // fRegistry.add("Track/positive/hTOFNsigmaKa", "TOF n sigma ka;p_{pv} (GeV/c);n #sigma_{K}^{TOF}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); // fRegistry.add("Track/positive/hTOFNsigmaPr", "TOF n sigma pr;p_{pv} (GeV/c);n #sigma_{p}^{TOF}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - fRegistry.add("Track/positive/hPIDForTracking", "PID for trackng", o2::framework::HistType::kTH1F, {{9, -0.5, 8.5}}, false); // see numbering in O2/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h + fRegistry.add("Track/positive/hPIDForTracking", "PID for tracking", o2::framework::HistType::kTH1F, {{9, -0.5, 8.5}}, false); // see numbering in O2/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h fRegistry.add("Track/positive/hProbElBDT", "probability to be e from BDT;p_{in} (GeV/c);BDT score;", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); fRegistry.add("Track/positive/hMeanClusterSizeITS", "mean cluster size ITS;p_{pv} (GeV/c); on ITS #times cos(#lambda);", o2::framework::HistType::kTH2F, {{1000, 0.f, 10.f}, {150, 0, 15}}, false); fRegistry.add("Track/positive/hMeanClusterSizeITSib", "mean cluster size ITS inner barrel;p_{pv} (GeV/c); on ITS #times cos(#lambda);", o2::framework::HistType::kTH2F, {{1000, 0.f, 10.f}, {150, 0, 15}}, false); @@ -316,8 +331,10 @@ struct SingleTrackQC { fRegistry.add("Track/positive/hsDelta", "diff. between GL and associated SA;p_{T}^{gl} (GeV/c);(p_{T}^{sa} - p_{T}^{gl})/p_{T}^{gl};#Delta#eta;#Delta#varphi (rad.);", o2::framework::HistType::kTHnSparseF, {axis_pt, {100, -0.5, +0.5}, {100, -0.5, +0.5}, {90, -M_PI / 4, M_PI / 4}}, false); fRegistry.add("Track/positive/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", o2::framework::HistType::kTH1F, {{1000, -5, 5}}, false); fRegistry.add("Track/positive/hTrackType", "track type", o2::framework::HistType::kTH1F, {{6, -0.5f, 5.5}}, false); - fRegistry.add("Track/positive/hDCAxy", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", o2::framework::HistType::kTH2F, {{200, -0.5f, 0.5f}, {200, -0.5f, 0.5f}}, false); - fRegistry.add("Track/positive/hDCAxySigma", "DCA x vs. y;DCA_{x} (#sigma);DCA_{y} (#sigma)", o2::framework::HistType::kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); + fRegistry.add("Track/positive/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", o2::framework::HistType::kTH2F, {{200, -0.5f, 0.5f}, {200, -0.5f, 0.5f}}, false); + fRegistry.add("Track/positive/hDCAxy2DinSigma", "DCA x vs. y;DCA_{x} (#sigma);DCA_{y} (#sigma)", o2::framework::HistType::kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); + fRegistry.add("Track/positive/hDCAxy", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", o2::framework::HistType::kTH1F, {{1000, 0.f, 1.0f}}, false); // dummy comment + fRegistry.add("Track/positive/hDCAxyinSigma", "DCA x vs. y;DCA_{x} (#sigma);DCA_{y} (#sigma)", o2::framework::HistType::kTH1F, {{100, 0.0f, 10.0f}}, false); fRegistry.add("Track/positive/hDCAxRes_Pt", "DCA_{x} resolution vs. pT;p_{T} (GeV/c);DCA_{x} resolution (#mum)", o2::framework::HistType::kTH2F, {{200, 0, 10}, {500, 0, 500}}, false); fRegistry.add("Track/positive/hDCAyRes_Pt", "DCA_{y} resolution vs. pT;p_{T} (GeV/c);DCA_{y} resolution (#mum)", o2::framework::HistType::kTH2F, {{200, 0, 10}, {500, 0, 500}}, false); fRegistry.add("Track/positive/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", o2::framework::HistType::kTH2F, {{200, 0, 10}, {500, 0, 500}}, false); @@ -334,6 +351,7 @@ struct SingleTrackQC { fRegistry.add("Track/positive/hChi2MatchMCHMFT_Pt", "chi2 match MCH-MFT;p_{T,#mu} (GeV/c);chi2/ndf", o2::framework::HistType::kTH2F, {{200, 0, 10}, {200, 0.0f, 100}}, false); fRegistry.add("Track/positive/hMFTClusterMap", "MFT cluster map", o2::framework::HistType::kTH1F, {{1024, -0.5, 1023.5}}, false); fRegistry.add("Track/positive/hdR_Chi2MatchMCHMFT", "dr vs. matching chi2 MCH-MFT;chi2 match MCH-MFT;#DeltaR;", o2::framework::HistType::kTH2F, {{200, 0.0f, 100}, {200, 0, 0.5}}, false); + fRegistry.add("Track/positive/hLog10Chi2IP", "chi2IP;log_{10}(#chi^{2}_{IP})", o2::framework::HistType::kTH1F, {{100, -5, 5}}, false); fRegistry.addClone("Track/positive/", "Track/negative/"); } } @@ -476,6 +494,7 @@ struct SingleTrackQC { fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, dielectroncuts.cfgDCAType); fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); @@ -496,14 +515,14 @@ struct SingleTrackQC { if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMl.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMl.value[i]); + binsML.reserve(dielectroncuts.binsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { + binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); } std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMl.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMl.value[i]); + thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { + thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); } fDielectronCut.SetMLThresholds(binsML, thresholdsML); } // end of PID ML @@ -523,6 +542,7 @@ struct SingleTrackQC { fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); + fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); @@ -544,12 +564,9 @@ struct SingleTrackQC { float dca3D = o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(track); float dcaXY = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(track); float dcaZ = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(track); - float phiPosition = track.phi() + std::asin(-0.30282 * track.sign() * (d_bz * 0.1) * dielectroncuts.cfgRefR / (2.f * track.pt())); - o2::math_utils::bringTo02Pi(phiPosition); if (track.sign() > 0) { fRegistry.fill(HIST("Track/positive/hs"), track.pt(), track.eta(), track.phi(), dca3D, dcaXY, dcaZ, weight); - fRegistry.fill(HIST("Track/positive/hPhiPosition"), phiPosition); fRegistry.fill(HIST("Track/positive/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/positive/hDCAxyz"), track.dcaXY(), track.dcaZ()); fRegistry.fill(HIST("Track/positive/hDCAxyzSigma"), dcaXY, dcaZ); @@ -589,7 +606,6 @@ struct SingleTrackQC { // fRegistry.fill(HIST("Track/positive/hTOFNsigmaPr"), track.p(), track.tofNSigmaPr()); } else { fRegistry.fill(HIST("Track/negative/hs"), track.pt(), track.eta(), track.phi(), dca3D, dcaXY, dcaZ, weight); - fRegistry.fill(HIST("Track/negative/hPhiPosition"), phiPosition); fRegistry.fill(HIST("Track/negative/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/negative/hDCAxyz"), track.dcaXY(), track.dcaZ()); fRegistry.fill(HIST("Track/negative/hDCAxyzSigma"), dcaXY, dcaZ); @@ -650,10 +666,12 @@ struct SingleTrackQC { fRegistry.fill(HIST("Track/positive/hsDelta"), track.pt(), reldpt, deta, dphi, weight); fRegistry.fill(HIST("Track/positive/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/positive/hTrackType"), track.trackType()); - fRegistry.fill(HIST("Track/positive/hDCAxy"), track.fwdDcaX(), track.fwdDcaY()); - fRegistry.fill(HIST("Track/positive/hDCAxySigma"), track.fwdDcaX() / std::sqrt(track.cXX()), track.fwdDcaY() / std::sqrt(track.cYY())); - fRegistry.fill(HIST("Track/positive/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXX()) * 1e+4); - fRegistry.fill(HIST("Track/positive/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); + fRegistry.fill(HIST("Track/positive/hDCAxy"), std::hypot(track.fwdDcaX(), track.fwdDcaY())); + fRegistry.fill(HIST("Track/positive/hDCAxyinSigma"), dca_xy); + fRegistry.fill(HIST("Track/positive/hDCAxy2D"), track.fwdDcaX(), track.fwdDcaY()); + fRegistry.fill(HIST("Track/positive/hDCAxy2DinSigma"), track.fwdDcaX() / std::sqrt(track.cXXatDCA()), track.fwdDcaY() / std::sqrt(track.cYYatDCA())); + fRegistry.fill(HIST("Track/positive/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXXatDCA()) * 1e+4); + fRegistry.fill(HIST("Track/positive/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYYatDCA()) * 1e+4); fRegistry.fill(HIST("Track/positive/hDCAxyRes_Pt"), track.pt(), o2::aod::pwgem::dilepton::utils::emtrackutil::sigmaFwdDcaXY(track) * 1e+4); fRegistry.fill(HIST("Track/positive/hDCAx_PosZ"), collision.posZ(), track.fwdDcaX()); fRegistry.fill(HIST("Track/positive/hDCAy_PosZ"), collision.posZ(), track.fwdDcaY()); @@ -668,16 +686,19 @@ struct SingleTrackQC { fRegistry.fill(HIST("Track/positive/hChi2MatchMCHMFT_Pt"), track.pt(), track.chi2MatchMCHMFT()); fRegistry.fill(HIST("Track/positive/hMFTClusterMap"), track.mftClusterMap()); fRegistry.fill(HIST("Track/positive/hdR_Chi2MatchMCHMFT"), track.chi2MatchMCHMFT(), std::sqrt(deta * deta + dphi * dphi)); + fRegistry.fill(HIST("Track/positive/hLog10Chi2IP"), std::log10(track.chi2IP())); } else { fRegistry.fill(HIST("Track/negative/hs"), track.pt(), track.eta(), track.phi(), dca_xy, weight); fRegistry.fill(HIST("Track/negative/hEtaPhi_MatchMCHMID"), track.phiMatchedMCHMID(), track.etaMatchedMCHMID(), weight); fRegistry.fill(HIST("Track/negative/hsDelta"), track.pt(), reldpt, deta, dphi, weight); fRegistry.fill(HIST("Track/negative/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/negative/hTrackType"), track.trackType()); - fRegistry.fill(HIST("Track/negative/hDCAxy"), track.fwdDcaX(), track.fwdDcaY()); - fRegistry.fill(HIST("Track/negative/hDCAxySigma"), track.fwdDcaX() / std::sqrt(track.cXX()), track.fwdDcaY() / std::sqrt(track.cYY())); - fRegistry.fill(HIST("Track/negative/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXX()) * 1e+4); - fRegistry.fill(HIST("Track/negative/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); + fRegistry.fill(HIST("Track/negative/hDCAxy"), std::hypot(track.fwdDcaX(), track.fwdDcaY())); + fRegistry.fill(HIST("Track/negative/hDCAxyinSigma"), dca_xy); + fRegistry.fill(HIST("Track/negative/hDCAxy2D"), track.fwdDcaX(), track.fwdDcaY()); + fRegistry.fill(HIST("Track/negative/hDCAxy2DinSigma"), track.fwdDcaX() / std::sqrt(track.cXXatDCA()), track.fwdDcaY() / std::sqrt(track.cYYatDCA())); + fRegistry.fill(HIST("Track/negative/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXXatDCA()) * 1e+4); + fRegistry.fill(HIST("Track/negative/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYYatDCA()) * 1e+4); fRegistry.fill(HIST("Track/negative/hDCAxyRes_Pt"), track.pt(), o2::aod::pwgem::dilepton::utils::emtrackutil::sigmaFwdDcaXY(track) * 1e+4); fRegistry.fill(HIST("Track/negative/hDCAx_PosZ"), collision.posZ(), track.fwdDcaX()); fRegistry.fill(HIST("Track/negative/hDCAy_PosZ"), collision.posZ(), track.fwdDcaY()); @@ -692,6 +713,7 @@ struct SingleTrackQC { fRegistry.fill(HIST("Track/negative/hChi2MatchMCHMFT_Pt"), track.pt(), track.chi2MatchMCHMFT()); fRegistry.fill(HIST("Track/negative/hMFTClusterMap"), track.mftClusterMap()); fRegistry.fill(HIST("Track/negative/hdR_Chi2MatchMCHMFT"), track.chi2MatchMCHMFT(), std::sqrt(deta * deta + dphi * dphi)); + fRegistry.fill(HIST("Track/negative/hLog10Chi2IP"), std::log10(track.chi2IP())); } } @@ -714,26 +736,31 @@ struct SingleTrackQC { } if constexpr (isTriggerAnalysis) { - if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + if (!collision.triggerMask_bit(emswtId)) { continue; } - auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected - auto TOIcounter = zorro.getTOIcounters()[0]; // this has to be called after zorro::isSelected - auto ATcounter = zorro.getATcounters()[mToIidx]; // this has to be called after zorro::isSelected - - if (swt_bitset.test(mToIidx)) { - while (ATcounter > mATCounter) { - mATCounter++; - fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), collision.runNumber()); - } - - while (TOIcounter > mTOICounter) { - fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), collision.runNumber()); - mTOICounter++; // always incremented by 1 in zorro!! - } - // LOGF(info, "collision.globalIndex() = %d, collision.globalBC() = %llu, mTOICounter = %d, mATcounter = %d", collision.globalIndex(), collision.globalBC(), mTOICounter, mATCounter); - } + // if (!zorro.isSelected(collision.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + // continue; + // } + + // auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected + // auto TOIcounter = zorro.getTOIcounters()[0]; // this has to be called after zorro::isSelected + // auto ATcounter = zorro.getATcounters()[mToIidx]; // this has to be called after zorro::isSelected + + // if (swt_bitset.test(mToIidx)) { + // while (ATcounter > mATCounter) { + // mATCounter++; + // fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), collision.runNumber()); + // } + + // while (TOIcounter > mTOICounter) { + // fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), collision.runNumber()); + // mTOICounter++; // always incremented by 1 in zorro!! + // } + // // LOGF(info, "collision.globalIndex() = %d, collision.globalBC() = %llu, mTOICounter = %d, mATcounter = %d", collision.globalIndex(), collision.globalBC(), mTOICounter, mATCounter); + // } } o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); @@ -868,6 +895,24 @@ struct SingleTrackQC { o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); + o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); + + o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfb >= static_cast(0)); + o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track && dimuoncuts.cfg_min_phi_track < o2::aod::fwdtrack::phi && o2::aod::fwdtrack::phi < dimuoncuts.cfg_max_phi_track; o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); @@ -877,6 +922,7 @@ struct SingleTrackQC { o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; using FilteredMyCollisions = o2::soa::Filtered; + using FilteredMyCollisionsSWT = o2::soa::Filtered; void processQC(FilteredMyCollisions const& collisions, Types const&... args) { @@ -899,7 +945,7 @@ struct SingleTrackQC { } PROCESS_SWITCH(SingleTrackQC, processQC, "run single track QC", true); - void processQC_TriggeredData(FilteredMyCollisions const& collisions, Types const&... args) + void processQC_TriggeredData(FilteredMyCollisionsSWT const& collisions, Types const&... args, o2::aod::EMSWTriggerATCounters const& countersAT, o2::aod::EMSWTriggerATOICounters const& countersATOI) { if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { auto electrons = std::get<0>(std::tie(args...)); @@ -917,6 +963,19 @@ struct SingleTrackQC { } map_weight.clear(); map_best_match_globalmuon.clear(); + + // for nomalization + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(zorroGroup.cfg_swt_name.value); + for (const auto& counter : countersAT) { + if (counter.isAT_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedTrigger"), mRunNumber); + } + } + for (const auto& counter : countersATOI) { + if (counter.isAToI_bit(emswtId)) { + fRegistry.fill(HIST("Event/trigger/hAnalysedToI"), mRunNumber); + } + } } PROCESS_SWITCH(SingleTrackQC, processQC_TriggeredData, "run single track QC on triggered data", false); diff --git a/PWGEM/Dilepton/Core/SingleTrackQCMC.h b/PWGEM/Dilepton/Core/SingleTrackQCMC.h index 4abe7e585f7..314265b4e6b 100644 --- a/PWGEM/Dilepton/Core/SingleTrackQCMC.h +++ b/PWGEM/Dilepton/Core/SingleTrackQCMC.h @@ -20,6 +20,7 @@ #include "PWGEM/Dilepton/Core/DielectronCut.h" #include "PWGEM/Dilepton/Core/DimuonCut.h" #include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" #include "PWGEM/Dilepton/DataModel/dileptonTables.h" #include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" #include "PWGEM/Dilepton/Utils/EventHistograms.h" @@ -52,7 +53,8 @@ #include #include -#include + +#include #include #include @@ -70,7 +72,7 @@ using MyCollision = MyCollisions::iterator; using MyMCCollisions = o2::soa::Join; using MyMCCollision = MyMCCollisions::iterator; -using MyMCElectrons = o2::soa::Join; +using MyMCElectrons = o2::soa::Join; using MyMCElectron = MyMCElectrons::iterator; using FilteredMyMCElectrons = o2::soa::Filtered; @@ -164,6 +166,8 @@ struct SingleTrackQCMC { o2::framework::Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 0.2, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 0.2, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; + o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA. 0:3D, 1:XY, 2:Z, else:3D"}; o2::framework::Configurable cfg_require_itsib_any{"cfg_require_itsib_any", false, "flag to require ITS ib any hits"}; o2::framework::Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", true, "flag to require ITS ib 1st hit"}; o2::framework::Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; @@ -174,6 +178,12 @@ struct SingleTrackQCMC { o2::framework::Configurable cfg_min_phiposition_track{"cfg_min_phiposition_track", 0.f, "min phi position for single track at certain radius"}; o2::framework::Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; + o2::framework::Configurable cfg_apply_cuts_from_prefilter{"cfg_apply_cuts_from_prefilter", false, "flag to apply prefilter set when producing derived data"}; + o2::framework::Configurable cfg_prefilter_bits{"cfg_prefilter_bits", 0, "prefilter bits [kNone : 0, kElFromPC : 1, kElFromPi0_20MeV : 2, kElFromPi0_40MeV : 4, kElFromPi0_60MeV : 8, kElFromPi0_80MeV : 16, kElFromPi0_100MeV : 32, kElFromPi0_120MeV : 64, kElFromPi0_140MeV : 128] Please consider logical-OR among them."}; // see PairUtilities.h + + o2::framework::Configurable cfg_apply_cuts_from_prefilter_derived{"cfg_apply_cuts_from_prefilter_derived", false, "flag to apply prefilter set in derived data"}; + o2::framework::Configurable cfg_prefilter_bits_derived{"cfg_prefilter_bits_derived", 0, "prefilter bits [kNone : 0, kMee : 1, kPhiV : 2, kSplitOrMergedTrackLS : 4, kSplitOrMergedTrackULS : 8] Please consider logical-OR among them."}; // see PairUtilities.h + o2::framework::Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif = 4, kPIDML = 5]"}; o2::framework::Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; o2::framework::Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron inclusion"}; @@ -191,17 +201,19 @@ struct SingleTrackQCMC { o2::framework::Configurable cfg_max_pin_pirejTPC{"cfg_max_pin_pirejTPC", 1e+10, "max. pin for pion rejection in TPC"}; o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; o2::framework::Configurable includeITSsa{"includeITSsa", false, "Flag to include ITSsa tracks only for MC. switch ON only if needed."}; + o2::framework::Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. + o2::framework::Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. // configuration for PID ML - o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20.f}, "Bin limits for ML application"}; - o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.98, 0.98, 0.9, 0.9, 0.95, 0.95, 0.8, 0.8}, "ML cuts per bin"}; - o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + // o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + // o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + o2::framework::Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; + // o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; + // o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; + // o2::framework::Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + // o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; } dielectroncuts; DimuonCut fDimuonCut; @@ -214,6 +226,7 @@ struct SingleTrackQCMC { o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "max phi for single track"}; o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; @@ -226,6 +239,7 @@ struct SingleTrackQCMC { o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; + o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; o2::framework::Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; @@ -287,7 +301,6 @@ struct SingleTrackQCMC { fRegistry.add("Track/PromptLF/positive/hsGenRec", "rec. single electron", o2::framework::HistType::kTHnSparseD, {axis_pt, axis_eta, axis_phi, axis_dca3D, axis_dcaXY, axis_dcaZ, axis_charge_gen}, true); } if (cfgFillQA) { - fRegistry.add("Track/PromptLF/positive/hPhiPosition", Form("phi position at r_{xy} = %3.2f m", dielectroncuts.cfgRefR.value), o2::framework::HistType::kTH1F, {axis_phiposition}, false); fRegistry.add("Track/PromptLF/positive/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", o2::framework::HistType::kTH1F, {{4000, -20, 20}}, false); fRegistry.add("Track/PromptLF/positive/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", o2::framework::HistType::kTH2F, {{200, -1.0f, 1.0f}, {200, -1.f, 1.f}}, false); fRegistry.add("Track/PromptLF/positive/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", o2::framework::HistType::kTH2F, {{400, -20.0f, 20.0f}, {400, -20.0f, 20.0f}}, false); @@ -301,11 +314,11 @@ struct SingleTrackQCMC { fRegistry.add("Track/PromptLF/positive/hTPCNcls2Nf", "TPC Ncls/Nfindable;TPC N_{cls}/N_{cls}^{findable}", o2::framework::HistType::kTH1F, {{200, 0, 2}}, false); fRegistry.add("Track/PromptLF/positive/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); fRegistry.add("Track/PromptLF/positive/hNclsITS", "number of ITS clusters;ITS N_{cls}", o2::framework::HistType::kTH1F, {{8, -0.5, 7.5}}, false); - fRegistry.add("Track/PromptLF/positive/hChi2ITS", "chi2/number of ITS clusters;ITS #chi^{2}/N_{cls}", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Track/PromptLF/positive/hChi2ITS", "chi2/number of ITS clusters;ITS #chi^{2}/N_{cls}", o2::framework::HistType::kTH1F, {{400, 0, 40}}, false); fRegistry.add("Track/PromptLF/positive/hDeltaPin", "p_{in} vs. p_{pv};p_{in} (GeV/c);(p_{pv} - p_{in})/p_{in}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {200, -1, +1}}, false); fRegistry.add("Track/PromptLF/positive/hChi2TOF", "TOF Chi2;p_{pv} (GeV/c);TOF #chi^{2}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, 0, 10}}, false); fRegistry.add("Track/PromptLF/positive/hITSClusterMap", "ITS cluster map", o2::framework::HistType::kTH1F, {{128, -0.5, 127.5}}, false); - fRegistry.add("Track/PromptLF/positive/hPIDForTracking", "PID for trackng", o2::framework::HistType::kTH1F, {{9, -0.5, 8.5}}, false); // see numbering in O2/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h + fRegistry.add("Track/PromptLF/positive/hPIDForTracking", "PID for tracking", o2::framework::HistType::kTH1F, {{9, -0.5, 8.5}}, false); // see numbering in O2/DataFormats/Reconstruction/include/ReconstructionDataFormats/PID.h fRegistry.add("Track/PromptLF/positive/hPtGen_DeltaPtOverPtGen", "electron p_{T} resolution;p_{T}^{gen} (GeV/c);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", o2::framework::HistType::kTH2F, {{200, 0, 10}, {200, -1.0f, 1.0f}}, true); fRegistry.add("Track/PromptLF/positive/hPtGen_DeltaEta", "electron #eta resolution;p_{T}^{gen} (GeV/c);#eta^{rec} - #eta^{gen}", o2::framework::HistType::kTH2F, {{200, 0, 10}, {100, -0.05f, 0.05f}}, true); fRegistry.add("Track/PromptLF/positive/hPtGen_DeltaPhi", "electron #varphi resolution;p_{T}^{gen} (GeV/c);#varphi^{rec} - #varphi^{gen} (rad.)", o2::framework::HistType::kTH2F, {{200, 0, 10}, {100, -0.05f, 0.05f}}, true); @@ -323,6 +336,7 @@ struct SingleTrackQCMC { if (cfgFillQA) { fRegistry.add("Track/PID/positive/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + fRegistry.add("Track/PID/positive/hTPCdEdxMC", "TPC dE/dx MC;p_{in} (GeV/c);TPC dE/dx (a.u.)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); fRegistry.add("Track/PID/positive/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); // fRegistry.add("Track/PID/positive/hTPCNsigmaMu", "TPC n sigma mu;p_{in} (GeV/c);n #sigma_{#mu}^{TPC}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); fRegistry.add("Track/PID/positive/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); @@ -361,7 +375,8 @@ struct SingleTrackQCMC { fRegistry.add("Track/PromptLF/positive/hsDelta", "diff. between GL and associated SA;p_{T}^{gl} (GeV/c);(p_{T}^{sa} - p_{T}^{gl})/p_{T}^{gl};#Delta#eta;#Delta#varphi (rad.);", o2::framework::HistType::kTHnSparseF, {axis_pt, {100, -0.5, +0.5}, {100, -0.5, +0.5}, {90, -M_PI / 4, M_PI / 4}}, false); fRegistry.add("Track/PromptLF/positive/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", o2::framework::HistType::kTH1F, {{1000, -5, 5}}, false); fRegistry.add("Track/PromptLF/positive/hTrackType", "track type", o2::framework::HistType::kTH1F, {{6, -0.5f, 5.5}}, false); - fRegistry.add("Track/PromptLF/positive/hDCAxy", "DCAxy;DCA_{xy} (cm)", o2::framework::HistType::kTH1F, {{100, 0.f, 1.0f}}, false); + fRegistry.add("Track/PromptLF/positive/hDCAxy", "DCAxy;DCA_{xy} (cm)", o2::framework::HistType::kTH1F, {{1000, 0.f, 1.0f}}, false); + fRegistry.add("Track/PromptLF/positive/hDCAxyinSigma", "DCAxy;DCA_{xy} (#sigma)", o2::framework::HistType::kTH1F, {{100, 0.f, 10.0f}}, false); fRegistry.add("Track/PromptLF/positive/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", o2::framework::HistType::kTH2F, {{200, -0.5f, 0.5f}, {200, -0.5f, 0.5f}}, false); fRegistry.add("Track/PromptLF/positive/hDCAxy2DinSigma", "DCA x vs. y;DCA_{x} (#sigma);DCA_{y} (#sigma)", o2::framework::HistType::kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); fRegistry.add("Track/PromptLF/positive/hDCAxRes_Pt", "DCA_{x} resolution vs. pT;p_{T} (GeV/c);DCA_{x} resolution (#mum)", o2::framework::HistType::kTH2F, {{200, 0, 10}, {500, 0, 500}}, false); @@ -382,7 +397,7 @@ struct SingleTrackQCMC { fRegistry.add("Track/PromptLF/positive/hPtGen_DeltaPtOverPtGen", "muon p_{T} resolution;p_{T}^{gen} (GeV/c);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", o2::framework::HistType::kTH2F, {{200, 0, 10}, {200, -1.0f, 1.0f}}, true); fRegistry.add("Track/PromptLF/positive/hPtGen_DeltaEta", "muon #eta resolution;p_{T}^{gen} (GeV/c);#eta^{rec} - #eta^{gen}", o2::framework::HistType::kTH2F, {{200, 0, 10}, {100, -0.05f, 0.05f}}, true); fRegistry.add("Track/PromptLF/positive/hPtGen_DeltaPhi", "muon #varphi resolution;p_{T}^{gen} (GeV/c);#varphi^{rec} - #varphi^{gen} (rad.)", o2::framework::HistType::kTH2F, {{200, 0, 10}, {100, -0.05f, 0.05f}}, true); - fRegistry.add("Track/PromptLF/positive/hdR_Chi2MatchMCHMFT", "dr vs. matching chi2 MCH-MFT;chi2 match MCH-MFT;#DeltaR;", o2::framework::HistType::kTH2F, {{200, 0.0f, 100}, {200, 0, 0.5}}, false); + fRegistry.add("Track/PromptLF/positive/hLog10Chi2IP", "chi2IP;log_{10}(#chi^{2}_{IP})", o2::framework::HistType::kTH1F, {{100, -5, 5}}, false); } fRegistry.addClone("Track/PromptLF/positive/", "Track/PromptLF/negative/"); fRegistry.addClone("Track/PromptLF/", "Track/NonPromptLF/"); @@ -439,8 +454,8 @@ struct SingleTrackQCMC { } } - int mRunNumber; - float d_bz; + int mRunNumber{0}; + float d_bz{0}; template void initCCDB(TCollision const& collision) @@ -522,6 +537,7 @@ struct SingleTrackQCMC { fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, dielectroncuts.cfgDCAType); fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); @@ -543,14 +559,14 @@ struct SingleTrackQCMC { if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMl.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMl.value[i]); + binsML.reserve(dielectroncuts.binsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { + binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); } std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMl.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMl.value[i]); + thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { + thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); } fDielectronCut.SetMLThresholds(binsML, thresholdsML); } // end of PID ML @@ -570,6 +586,7 @@ struct SingleTrackQCMC { fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); // fDimuonCut.SetMatchingChi2MCHMFT(0.f, dimuoncuts.cfg_max_matching_chi2_mftmch); + fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); @@ -629,8 +646,6 @@ struct SingleTrackQCMC { float dca3D = o2::aod::pwgem::dilepton::utils::emtrackutil::dca3DinSigma(track); float dcaXY = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaXYinSigma(track); float dcaZ = o2::aod::pwgem::dilepton::utils::emtrackutil::dcaZinSigma(track); - float phiPosition = track.phi() + std::asin(-0.30282 * track.sign() * (d_bz * 0.1) * dielectroncuts.cfgRefR / (2.f * track.pt())); - o2::math_utils::bringTo02Pi(phiPosition); float weight = 1.f; if (cfgApplyWeightTTCA) { @@ -648,7 +663,6 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hsGenRec"), mctrack.pt(), mctrack.eta(), mctrack.phi(), dca3D, dcaXY, dcaZ, -mctrack.pdgCode() / pdg_lepton, weight); } if (cfgFillQA) { - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hPhiPosition"), phiPosition); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxyz"), track.dcaXY(), track.dcaZ()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxyzSigma"), dcaXY, dcaZ); @@ -674,6 +688,7 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hPtGen_DeltaPhi"), mctrack.pt(), track.phi() - mctrack.phi()); fRegistry.fill(HIST("Track/PID/positive/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); + fRegistry.fill(HIST("Track/PID/positive/hTPCdEdxMC"), track.tpcInnerParam(), track.mcTunedTPCSignal()); fRegistry.fill(HIST("Track/PID/positive/hTOFbeta"), track.p(), track.beta()); fRegistry.fill(HIST("Track/PID/positive/hMeanClusterSizeITS"), track.p(), track.meanClusterSizeITS() * std::cos(std::atan(track.tgl()))); fRegistry.fill(HIST("Track/PID/positive/hMeanClusterSizeITSib"), track.p(), track.meanClusterSizeITSib() * std::cos(std::atan(track.tgl()))); @@ -694,7 +709,6 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hsGenRec"), mctrack.pt(), mctrack.eta(), mctrack.phi(), dca3D, dcaXY, dcaZ, -mctrack.pdgCode() / pdg_lepton, weight); } if (cfgFillQA) { - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hPhiPosition"), phiPosition); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxyz"), track.dcaXY(), track.dcaZ()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxyzSigma"), dcaXY, dcaZ); @@ -720,6 +734,7 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hPtGen_DeltaPhi"), mctrack.pt(), track.phi() - mctrack.phi()); fRegistry.fill(HIST("Track/PID/negative/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); + fRegistry.fill(HIST("Track/PID/negative/hTPCdEdxMC"), track.tpcInnerParam(), track.mcTunedTPCSignal()); fRegistry.fill(HIST("Track/PID/negative/hTOFbeta"), track.p(), track.beta()); fRegistry.fill(HIST("Track/PID/negative/hMeanClusterSizeITS"), track.p(), track.meanClusterSizeITS() * std::cos(std::atan(track.tgl()))); fRegistry.fill(HIST("Track/PID/negative/hMeanClusterSizeITSib"), track.p(), track.meanClusterSizeITSib() * std::cos(std::atan(track.tgl()))); @@ -761,10 +776,11 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hTrackType"), track.trackType()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxy"), std::sqrt(std::pow(track.fwdDcaX(), 2) + std::pow(track.fwdDcaY(), 2))); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxyinSigma"), dca_xy); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxy2D"), track.fwdDcaX(), track.fwdDcaY()); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxy2DinSigma"), track.fwdDcaX() / std::sqrt(track.cXX()), track.fwdDcaY() / std::sqrt(track.cYY())); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXX()) * 1e+4); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxy2DinSigma"), track.fwdDcaX() / std::sqrt(track.cXXatDCA()), track.fwdDcaY() / std::sqrt(track.cYYatDCA())); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXXatDCA()) * 1e+4); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYYatDCA()) * 1e+4); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAxyRes_Pt"), track.pt(), o2::aod::pwgem::dilepton::utils::emtrackutil::sigmaFwdDcaXY(track) * 1e+4); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAx_PosZ"), collision.posZ(), track.fwdDcaX()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hDCAy_PosZ"), collision.posZ(), track.fwdDcaY()); @@ -778,7 +794,7 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hChi2MatchMCHMID_Pt"), track.pt(), track.chi2MatchMCHMID()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hChi2MatchMCHMFT_Pt"), track.pt(), track.chi2MatchMCHMFT()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hMFTClusterMap"), track.mftClusterMap()); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hdR_Chi2MatchMCHMFT"), track.chi2MatchMCHMFT(), std::sqrt(deta * deta + dphi * dphi)); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hLog10Chi2IP"), std::log10(track.chi2IP())); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hPtGen_DeltaPtOverPtGen"), mctrack.pt(), (track.pt() - mctrack.pt()) / mctrack.pt()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hPtGen_DeltaEta"), mctrack.pt(), track.eta() - mctrack.eta()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("positive/hPtGen_DeltaPhi"), mctrack.pt(), track.phi() - mctrack.phi()); @@ -794,10 +810,11 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hQoverPt"), track.sign() / track.pt()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hTrackType"), track.trackType()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxy"), std::sqrt(std::pow(track.fwdDcaX(), 2) + std::pow(track.fwdDcaY(), 2))); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxyinSigma"), dca_xy); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxy2D"), track.fwdDcaX(), track.fwdDcaY()); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxy2DinSigma"), track.fwdDcaX() / std::sqrt(track.cXX()), track.fwdDcaY() / std::sqrt(track.cYY())); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXX()) * 1e+4); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxy2DinSigma"), track.fwdDcaX() / std::sqrt(track.cXXatDCA()), track.fwdDcaY() / std::sqrt(track.cYYatDCA())); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxRes_Pt"), track.pt(), std::sqrt(track.cXXatDCA()) * 1e+4); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAyRes_Pt"), track.pt(), std::sqrt(track.cYYatDCA()) * 1e+4); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAxyRes_Pt"), track.pt(), o2::aod::pwgem::dilepton::utils::emtrackutil::sigmaFwdDcaXY(track) * 1e+4); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAx_PosZ"), collision.posZ(), track.fwdDcaX()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hDCAy_PosZ"), collision.posZ(), track.fwdDcaY()); @@ -811,7 +828,7 @@ struct SingleTrackQCMC { fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hChi2MatchMCHMID_Pt"), track.pt(), track.chi2MatchMCHMID()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hChi2MatchMCHMFT_Pt"), track.pt(), track.chi2MatchMCHMFT()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hMFTClusterMap"), track.mftClusterMap()); - fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hdR_Chi2MatchMCHMFT"), track.chi2MatchMCHMFT(), std::sqrt(deta * deta + dphi * dphi)); + fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hLog10Chi2IP"), std::log10(track.chi2IP())); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hPtGen_DeltaPtOverPtGen"), mctrack.pt(), (track.pt() - mctrack.pt()) / mctrack.pt()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hPtGen_DeltaEta"), mctrack.pt(), track.eta() - mctrack.eta()); fRegistry.fill(HIST("Track/") + HIST(lepton_source_types[lepton_source_id]) + HIST("negative/hPtGen_DeltaPhi"), mctrack.pt(), track.phi() - mctrack.phi()); @@ -874,6 +891,12 @@ struct SingleTrackQCMC { continue; } } + if (dielectroncuts.acceptOnlyCorrectMatch && o2::aod::pwgem::dilepton::utils::mcutil::hasFakeMatchITSTPC(track)) { + continue; + } + if (dielectroncuts.acceptOnlyWrongMatch && !o2::aod::pwgem::dilepton::utils::mcutil::hasFakeMatchITSTPC(track)) { + continue; + } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { if (!cut.template IsSelectedTrack(track)) { continue; @@ -953,6 +976,12 @@ struct SingleTrackQCMC { if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { continue; } + if (!(eventcuts.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgTrackOccupancyMax)) { + continue; + } + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + continue; + } fRegistry.fill(HIST("MCEvent/after/hZvtx"), mccollision.posZ()); auto leptonsMC_per_coll = leptonsMC.sliceByCachedUnsorted(o2::aod::emmcparticle::emmceventId, mccollision.globalIndex(), cache); @@ -1132,6 +1161,24 @@ struct SingleTrackQCMC { o2::framework::expressions::Filter pidFilter_electron = dielectroncuts.cfg_min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < dielectroncuts.cfg_max_TPCNsigmaEl; o2::framework::expressions::Filter ttcaFilter_electron = ifnode(dielectroncuts.enableTTCA.node(), true, o2::aod::emprimaryelectron::isAssociatedToMPC == true); + o2::framework::expressions::Filter prefilter_derived_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter_derived.node() && dielectroncuts.cfg_prefilter_bits_derived.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kMee))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kPhiV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits_derived.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) > static_cast(0), (o2::aod::emprimaryelectron::pfbderived & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfbderived >= static_cast(0)); + + o2::framework::expressions::Filter prefilter_electron = ifnode(dielectroncuts.cfg_apply_cuts_from_prefilter.node() && dielectroncuts.cfg_prefilter_bits.node() >= static_cast(1), + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_40MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_60MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_80MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_100MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_120MeV))) <= static_cast(0), true) && + ifnode((dielectroncuts.cfg_prefilter_bits.node() & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) > static_cast(0), (o2::aod::emprimaryelectron::pfb & static_cast(1 << int(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_140MeV))) <= static_cast(0), true), + o2::aod::emprimaryelectron::pfb >= static_cast(0)); + o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == dimuoncuts.cfg_track_type && dimuoncuts.cfg_min_phi_track < o2::aod::fwdtrack::phi && o2::aod::fwdtrack::phi < dimuoncuts.cfg_max_phi_track; o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); diff --git a/PWGEM/Dilepton/DataModel/EvSelFlags.h b/PWGEM/Dilepton/DataModel/EvSelFlags.h new file mode 100644 index 00000000000..ae4d65a548b --- /dev/null +++ b/PWGEM/Dilepton/DataModel/EvSelFlags.h @@ -0,0 +1,40 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ +#define PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ + +namespace o2::aod::emevsel +{ +// Event selection criteria. See O2Physics/Common/CCDB/EventSelectionParams.h +enum EventSelectionFlags { + kIsTriggerTVX = 0, // FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level + kNoITSROFrameBorder, // bunch crossing is far from ITS RO Frame border + kNoTimeFrameBorder, // bunch crossing is far from Time Frame borders + kNoSameBunchPileup, // reject collisions in case of pileup with another collision in the same foundBC + kIsGoodZvtxFT0vsPV, // small difference between z-vertex from PV and from FT0 + kIsVertexITSTPC, // at least one ITS-TPC track (reject vertices built from ITS-only tracks) + kIsVertexTOFmatched, // at least one of vertex contributors is matched to TOF + kIsVertexTRDmatched, // at least one of vertex contributors is matched to TRD + kNoCollInTimeRangeNarrow, // no other collisions in specified time range (narrower than Strict) + kNoCollInTimeRangeStrict, // no other collisions in specified time range + kNoCollInTimeRangeStandard, // no other collisions in specified time range with per-collision multiplicity above threshold + kNoCollInRofStrict, // no other collisions in this Readout Frame + kNoCollInRofStandard, // no other collisions in this Readout Frame with per-collision multiplicity above threshold + kNoHighMultCollInPrevRof, // veto an event if FT0C amplitude in previous ITS ROF is above threshold + kIsGoodITSLayer3, // number of inactive chips on ITS layer 3 is below maximum allowed value + kIsGoodITSLayer0123, // numbers of inactive chips on ITS layers 0-3 are below maximum allowed values + kIsGoodITSLayersAll, // numbers of inactive chips on all ITS layers are below maximum allowed values + kNsel // counter +}; +} // namespace o2::aod::emevsel + +#endif // PWGEM_DILEPTON_DATAMODEL_EVSELFLAGS_H_ diff --git a/PWGEM/Dilepton/DataModel/dileptonTables.h b/PWGEM/Dilepton/DataModel/dileptonTables.h index 73196abc058..991818d04e1 100644 --- a/PWGEM/Dilepton/DataModel/dileptonTables.h +++ b/PWGEM/Dilepton/DataModel/dileptonTables.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "EvSelFlags.h" + #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" @@ -25,6 +27,7 @@ #include #include +#include #include #include #include @@ -42,12 +45,10 @@ namespace pwgem::dilepton::swt enum class swtAliases : int { // software trigger aliases for EM kHighTrackMult = 0, kHighFt0cFv0Mult, - kSingleE, kLMeeIMR, kLMeeHMR, + kGlobalDimuon, kDiElectron, - kSingleMuLow, - kSingleMuHigh, kDiMuon, kNaliases }; @@ -55,40 +56,16 @@ enum class swtAliases : int { // software trigger aliases for EM const std::unordered_map aliasLabels = { {"fHighTrackMult", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kHighTrackMult)}, {"fHighFt0cFv0Mult", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kHighFt0cFv0Mult)}, - {"fSingleE", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kSingleE)}, {"fLMeeIMR", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kLMeeIMR)}, {"fLMeeHMR", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kLMeeHMR)}, + {"fGlobalDimuon", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kGlobalDimuon)}, {"fDiElectron", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kDiElectron)}, - {"fSingleMuLow", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kSingleMuLow)}, - {"fSingleMuHigh", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kSingleMuHigh)}, {"fDiMuon", static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kDiMuon)}, }; } // namespace pwgem::dilepton::swt namespace emevsel { -// Event selection criteria. See O2Physics/Common/CCDB/EventSelectionParams.h -enum EventSelectionFlags { - kIsTriggerTVX = 0, // FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level - kNoITSROFrameBorder, // bunch crossing is far from ITS RO Frame border - kNoTimeFrameBorder, // bunch crossing is far from Time Frame borders - kNoSameBunchPileup, // reject collisions in case of pileup with another collision in the same foundBC - kIsGoodZvtxFT0vsPV, // small difference between z-vertex from PV and from FT0 - kIsVertexITSTPC, // at least one ITS-TPC track (reject vertices built from ITS-only tracks) - kIsVertexTOFmatched, // at least one of vertex contributors is matched to TOF - kIsVertexTRDmatched, // at least one of vertex contributors is matched to TRD - kNoCollInTimeRangeNarrow, // no other collisions in specified time range (narrower than Strict) - kNoCollInTimeRangeStrict, // no other collisions in specified time range - kNoCollInTimeRangeStandard, // no other collisions in specified time range with per-collision multiplicity above threshold - kNoCollInRofStrict, // no other collisions in this Readout Frame - kNoCollInRofStandard, // no other collisions in this Readout Frame with per-collision multiplicity above threshold - kNoHighMultCollInPrevRof, // veto an event if FT0C amplitude in previous ITS ROF is above threshold - kIsGoodITSLayer3, // number of inactive chips on ITS layer 3 is below maximum allowed value - kIsGoodITSLayer0123, // numbers of inactive chips on ITS layers 0-3 are below maximum allowed values - kIsGoodITSLayersAll, // numbers of inactive chips on all ITS layers are below maximum allowed values - kNsel // counter -}; - DECLARE_SOA_BITMAP_COLUMN(Selection, selection, 32); //! Bitmask of selection flags DECLARE_SOA_DYNAMIC_COLUMN(Sel8, sel8, [](uint32_t selection_bit) -> bool { return (selection_bit & BIT(o2::aod::emevsel::kIsTriggerTVX)) && (selection_bit & BIT(o2::aod::emevsel::kNoTimeFrameBorder)) && (selection_bit & BIT(o2::aod::emevsel::kNoITSROFrameBorder)); }); @@ -150,65 +127,69 @@ uint32_t reduceSelectionBit(TBC const& bc) } return bitMap; } - } // namespace emevsel namespace emevent { DECLARE_SOA_COLUMN(CollisionId, collisionId, int); -DECLARE_SOA_BITMAP_COLUMN(SWTAliasTmp, swtaliastmp, 16); //! Bitmask of fired trigger aliases (see above for definitions) to be join to o2::aod::Collisions for skimming -DECLARE_SOA_BITMAP_COLUMN(SWTAlias, swtalias, 16); //! Bitmask of fired trigger aliases (see above for definitions) to be join to o2::aod::EMEvents for analysis +// DECLARE_SOA_BITMAP_COLUMN(TriggerMaskTmp, triggerMaskTmp, 16); //! Bitmask of fired trigger aliases (see above for definitions) to be join to o2::aod::Collisions for skimming +DECLARE_SOA_BITMAP_COLUMN(TriggerMask, triggerMask, 16); //! Bitmask of fired trigger aliases (see above for definitions) to be join to o2::aod::EMEvents for analysis DECLARE_SOA_COLUMN(NInspectedTVX, nInspectedTVX, uint64_t); //! the number of inspected TVX bcs per run DECLARE_SOA_COLUMN(NScalars, nScalers, std::vector); //! the number of triggered bcs before down scaling per run DECLARE_SOA_COLUMN(NSelections, nSelections, std::vector); //! the number of triggered bcs after down scaling per run -DECLARE_SOA_BITMAP_COLUMN(IsAnalyzed, isAnalyzed, 16); -DECLARE_SOA_BITMAP_COLUMN(IsAnalyzedToI, isAnalyzedToI, 16); +DECLARE_SOA_BITMAP_COLUMN(IsAT, isAT, 16); +DECLARE_SOA_BITMAP_COLUMN(IsAToI, isAToI, 16); DECLARE_SOA_COLUMN(NeeULS, neeuls, int); DECLARE_SOA_COLUMN(NeeLSpp, neelspp, int); DECLARE_SOA_COLUMN(NeeLSmm, neelsmm, int); -DECLARE_SOA_COLUMN(Bz, bz, float); //! kG -DECLARE_SOA_COLUMN(Q2xFT0M, q2xft0m, float); //! Qx for 2nd harmonics in FT0M -DECLARE_SOA_COLUMN(Q2yFT0M, q2yft0m, float); //! Qy for 2nd harmonics in FT0M -DECLARE_SOA_COLUMN(Q2xFT0A, q2xft0a, float); //! Qx for 2nd harmonics in FT0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q2yFT0A, q2yft0a, float); //! Qy for 2nd harmonics in FT0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q2xFT0C, q2xft0c, float); //! Qx for 2nd harmonics in FT0C (i.e. negative eta) -DECLARE_SOA_COLUMN(Q2yFT0C, q2yft0c, float); //! Qy for 2nd harmonics in FT0C (i.e. negative eta) -DECLARE_SOA_COLUMN(Q2xFV0A, q2xfv0a, float); //! Qx for 2nd harmonics in FV0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q2yFV0A, q2yfv0a, float); //! Qy for 2nd harmonics in FV0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q2xBPos, q2xbpos, float); //! Qx for 2nd harmonics in Barrel positive eta region -DECLARE_SOA_COLUMN(Q2yBPos, q2ybpos, float); //! Qy for 2nd harmonics in Barrel positive eta region -DECLARE_SOA_COLUMN(Q2xBNeg, q2xbneg, float); //! Qx for 2nd harmonics in Barrel negative eta region -DECLARE_SOA_COLUMN(Q2yBNeg, q2ybneg, float); //! Qy for 2nd harmonics in Barrel negative eta region -DECLARE_SOA_COLUMN(Q2xBTot, q2xbtot, float); //! Qx for 2nd harmonics in Barrel full eta region -DECLARE_SOA_COLUMN(Q2yBTot, q2ybtot, float); //! Qy for 2nd harmonics in Barrel full eta region -DECLARE_SOA_COLUMN(Q3xFT0M, q3xft0m, float); //! Qx for 3rd harmonics in FT0M -DECLARE_SOA_COLUMN(Q3yFT0M, q3yft0m, float); //! Qy for 3rd harmonics in FT0M -DECLARE_SOA_COLUMN(Q3xFT0A, q3xft0a, float); //! Qx for 3rd harmonics in FT0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q3yFT0A, q3yft0a, float); //! Qy for 3rd harmonics in FT0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q3xFT0C, q3xft0c, float); //! Qx for 3rd harmonics in FT0C (i.e. negative eta) -DECLARE_SOA_COLUMN(Q3yFT0C, q3yft0c, float); //! Qy for 3rd harmonics in FT0C (i.e. negative eta) -DECLARE_SOA_COLUMN(Q3xFV0A, q3xfv0a, float); //! Qx for 3rd harmonics in FV0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q3yFV0A, q3yfv0a, float); //! Qy for 3rd harmonics in FV0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q3xBPos, q3xbpos, float); //! Qx for 3rd harmonics in Barrel positive eta region -DECLARE_SOA_COLUMN(Q3yBPos, q3ybpos, float); //! Qy for 3rd harmonics in Barrel positive eta region -DECLARE_SOA_COLUMN(Q3xBNeg, q3xbneg, float); //! Qx for 3rd harmonics in Barrel negative eta region -DECLARE_SOA_COLUMN(Q3yBNeg, q3ybneg, float); //! Qy for 3rd harmonics in Barrel negative eta region -DECLARE_SOA_COLUMN(Q3xBTot, q3xbtot, float); //! Qx for 3rd harmonics in Barrel full eta region -DECLARE_SOA_COLUMN(Q3yBTot, q3ybtot, float); //! Qy for 3rd harmonics in Barrel full eta region -DECLARE_SOA_COLUMN(Q4xFT0M, q4xft0m, float); //! Qx for 4th harmonics in FT0M -DECLARE_SOA_COLUMN(Q4yFT0M, q4yft0m, float); //! Qy for 4th harmonics in FT0M -DECLARE_SOA_COLUMN(Q4xFT0A, q4xft0a, float); //! Qx for 4th harmonics in FT0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q4yFT0A, q4yft0a, float); //! Qy for 4th harmonics in FT0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q4xFT0C, q4xft0c, float); //! Qx for 4th harmonics in FT0C (i.e. negative eta) -DECLARE_SOA_COLUMN(Q4yFT0C, q4yft0c, float); //! Qy for 4th harmonics in FT0C (i.e. negative eta) -DECLARE_SOA_COLUMN(Q4xFV0A, q4xfv0a, float); //! Qx for 4th harmonics in FV0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q4yFV0A, q4yfv0a, float); //! Qy for 4th harmonics in FV0A (i.e. positive eta) -DECLARE_SOA_COLUMN(Q4xBPos, q4xbpos, float); //! Qx for 4th harmonics in Barrel positive eta region -DECLARE_SOA_COLUMN(Q4yBPos, q4ybpos, float); //! Qy for 4th harmonics in Barrel positive eta region -DECLARE_SOA_COLUMN(Q4xBNeg, q4xbneg, float); //! Qx for 4th harmonics in Barrel negative eta region -DECLARE_SOA_COLUMN(Q4yBNeg, q4ybneg, float); //! Qy for 4th harmonics in Barrel negative eta region -DECLARE_SOA_COLUMN(Q4xBTot, q4xbtot, float); //! Qx for 4th harmonics in Barrel full eta region -DECLARE_SOA_COLUMN(Q4yBTot, q4ybtot, float); //! Qy for 4th harmonics in Barrel full eta region +DECLARE_SOA_COLUMN(Bz, bz, float); //! kG +DECLARE_SOA_COLUMN(Q2xFT0M, q2xft0m, float); //! Qx for 2nd harmonics in FT0M +DECLARE_SOA_COLUMN(Q2yFT0M, q2yft0m, float); //! Qy for 2nd harmonics in FT0M +DECLARE_SOA_COLUMN(Q2xFT0A, q2xft0a, float); //! Qx for 2nd harmonics in FT0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q2yFT0A, q2yft0a, float); //! Qy for 2nd harmonics in FT0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q2xFT0C, q2xft0c, float); //! Qx for 2nd harmonics in FT0C (i.e. negative eta) +DECLARE_SOA_COLUMN(Q2yFT0C, q2yft0c, float); //! Qy for 2nd harmonics in FT0C (i.e. negative eta) +DECLARE_SOA_COLUMN(Q2xFV0A, q2xfv0a, float); //! Qx for 2nd harmonics in FV0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q2yFV0A, q2yfv0a, float); //! Qy for 2nd harmonics in FV0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q2xBPos, q2xbpos, float); //! Qx for 2nd harmonics in Barrel positive eta region +DECLARE_SOA_COLUMN(Q2yBPos, q2ybpos, float); //! Qy for 2nd harmonics in Barrel positive eta region +DECLARE_SOA_COLUMN(Q2xBNeg, q2xbneg, float); //! Qx for 2nd harmonics in Barrel negative eta region +DECLARE_SOA_COLUMN(Q2yBNeg, q2ybneg, float); //! Qy for 2nd harmonics in Barrel negative eta region +DECLARE_SOA_COLUMN(Q2xBTot, q2xbtot, float); //! Qx for 2nd harmonics in Barrel full eta region +DECLARE_SOA_COLUMN(Q2yBTot, q2ybtot, float); //! Qy for 2nd harmonics in Barrel full eta region +DECLARE_SOA_COLUMN(Q3xFT0M, q3xft0m, float); //! Qx for 3rd harmonics in FT0M +DECLARE_SOA_COLUMN(Q3yFT0M, q3yft0m, float); //! Qy for 3rd harmonics in FT0M +DECLARE_SOA_COLUMN(Q3xFT0A, q3xft0a, float); //! Qx for 3rd harmonics in FT0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q3yFT0A, q3yft0a, float); //! Qy for 3rd harmonics in FT0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q3xFT0C, q3xft0c, float); //! Qx for 3rd harmonics in FT0C (i.e. negative eta) +DECLARE_SOA_COLUMN(Q3yFT0C, q3yft0c, float); //! Qy for 3rd harmonics in FT0C (i.e. negative eta) +DECLARE_SOA_COLUMN(Q3xFV0A, q3xfv0a, float); //! Qx for 3rd harmonics in FV0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q3yFV0A, q3yfv0a, float); //! Qy for 3rd harmonics in FV0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q3xBPos, q3xbpos, float); //! Qx for 3rd harmonics in Barrel positive eta region +DECLARE_SOA_COLUMN(Q3yBPos, q3ybpos, float); //! Qy for 3rd harmonics in Barrel positive eta region +DECLARE_SOA_COLUMN(Q3xBNeg, q3xbneg, float); //! Qx for 3rd harmonics in Barrel negative eta region +DECLARE_SOA_COLUMN(Q3yBNeg, q3ybneg, float); //! Qy for 3rd harmonics in Barrel negative eta region +DECLARE_SOA_COLUMN(Q3xBTot, q3xbtot, float); //! Qx for 3rd harmonics in Barrel full eta region +DECLARE_SOA_COLUMN(Q3yBTot, q3ybtot, float); //! Qy for 3rd harmonics in Barrel full eta region +DECLARE_SOA_COLUMN(Q4xFT0M, q4xft0m, float); //! Qx for 4th harmonics in FT0M +DECLARE_SOA_COLUMN(Q4yFT0M, q4yft0m, float); //! Qy for 4th harmonics in FT0M +DECLARE_SOA_COLUMN(Q4xFT0A, q4xft0a, float); //! Qx for 4th harmonics in FT0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q4yFT0A, q4yft0a, float); //! Qy for 4th harmonics in FT0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q4xFT0C, q4xft0c, float); //! Qx for 4th harmonics in FT0C (i.e. negative eta) +DECLARE_SOA_COLUMN(Q4yFT0C, q4yft0c, float); //! Qy for 4th harmonics in FT0C (i.e. negative eta) +DECLARE_SOA_COLUMN(Q4xFV0A, q4xfv0a, float); //! Qx for 4th harmonics in FV0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q4yFV0A, q4yfv0a, float); //! Qy for 4th harmonics in FV0A (i.e. positive eta) +DECLARE_SOA_COLUMN(Q4xBPos, q4xbpos, float); //! Qx for 4th harmonics in Barrel positive eta region +DECLARE_SOA_COLUMN(Q4yBPos, q4ybpos, float); //! Qy for 4th harmonics in Barrel positive eta region +DECLARE_SOA_COLUMN(Q4xBNeg, q4xbneg, float); //! Qx for 4th harmonics in Barrel negative eta region +DECLARE_SOA_COLUMN(Q4yBNeg, q4ybneg, float); //! Qy for 4th harmonics in Barrel negative eta region +DECLARE_SOA_COLUMN(Q4xBTot, q4xbtot, float); //! Qx for 4th harmonics in Barrel full eta region +DECLARE_SOA_COLUMN(Q4yBTot, q4ybtot, float); //! Qy for 4th harmonics in Barrel full eta region +DECLARE_SOA_COLUMN(QxZDCA, qxZDCA, float); //! Qx in ZDCA (i.e. positive eta) +DECLARE_SOA_COLUMN(QyZDCA, qyZDCA, float); //! Qy in ZDCA (i.e. positive eta) +DECLARE_SOA_COLUMN(QxZDCC, qxZDCC, float); //! Qx in ZDCC (i.e. negative eta) +DECLARE_SOA_COLUMN(QyZDCC, qyZDCC, float); //! Qy in ZDCC (i.e. negative eta) + DECLARE_SOA_COLUMN(SpherocityPtWeighted, spherocity_ptweighted, float); //! transverse spherocity DECLARE_SOA_COLUMN(SpherocityPtUnWeighted, spherocity_ptunweighted, float); //! transverse spherocity DECLARE_SOA_COLUMN(NtrackSpherocity, ntspherocity, int); @@ -264,28 +245,6 @@ DECLARE_SOA_DYNAMIC_COLUMN(CentNTPV, centNTPV, [](uint8_t centuint8) -> float { DECLARE_SOA_DYNAMIC_COLUMN(CentNGlobal, centNGlobal, [](uint8_t centuint8) -> float { return centuint8 < 100 ? std::nextafter(centuint8 * 0.01f, std::numeric_limits::infinity()) : std::nextafter(centuint8 - 110.f, std::numeric_limits::infinity()); }); //! centrality is multiplied by 100 in createEMEventDilepton.cxx } // namespace emeventnorm -// namespace emcent -// { -// DECLARE_SOA_COLUMN(CentFT0Muint8, centFT0Muint8, uint8_t); //! this is only to reduce data size -// DECLARE_SOA_COLUMN(CentFT0Auint8, centFT0Auint8, uint8_t); //! this is only to reduce data size -// DECLARE_SOA_COLUMN(CentFT0Cuint8, centFT0Cuint8, uint8_t); //! this is only to reduce data size -// DECLARE_SOA_COLUMN(CentNTPVuint8, centNTPVuint8, uint8_t); //! this is only to reduce data size -// DECLARE_SOA_COLUMN(CentNGlobaluint8, centNGlobaluint8, uint8_t); //! this is only to reduce data size -// -// DECLARE_SOA_EXPRESSION_COLUMN(CentFT0A, centFT0A, float, 1.f * centFT0Auint8); // this must be inverse of calculation in createEMEventDilepton.cxx -// DECLARE_SOA_EXPRESSION_COLUMN(CentFT0M, centFT0M, float, 1.f * centFT0Muint8); // this must be inverse of calculation in createEMEventDilepton.cxx -// DECLARE_SOA_EXPRESSION_COLUMN(CentFT0C, centFT0C, float, 1.f * centFT0Cuint8); // this must be inverse of calculation in createEMEventDilepton.cxx -// DECLARE_SOA_EXPRESSION_COLUMN(CentNTPV, centNTPV, float, 1.f * centNTPVuint8); // this must be inverse of calculation in createEMEventDilepton.cxx -// DECLARE_SOA_EXPRESSION_COLUMN(CentNGlobal, centNGlobal, float, 1.f * centNGlobaluint8); // this must be inverse of calculation in createEMEventDilepton.cxx -// -// // DECLARE_SOA_EXPRESSION_COLUMN(CentFT0A, centFT0A, float, (centFT0Auint8 < 100) ? std::nextafter((1.f * centFT0Auint8) / 100.f, std::numeric_limits::infinity()) : std::nextafter((1.f * centFT0Auint8) - 110.f, std::numeric_limits::infinity())); // this must be inverse of calculation in createEMEventDilepton.cxx -// // DECLARE_SOA_EXPRESSION_COLUMN(CentFT0M, centFT0M, float, (centFT0Muint8 < 100) ? std::nextafter((1.f * centFT0Muint8) / 100.f, std::numeric_limits::infinity()) : std::nextafter((1.f * centFT0Muint8) - 110.f, std::numeric_limits::infinity())); // this must be inverse of calculation in createEMEventDilepton.cxx -// // DECLARE_SOA_EXPRESSION_COLUMN(CentFT0C, centFT0C, float, (centFT0Cuint8 < 100) ? std::nextafter((1.f * centFT0Cuint8) / 100.f, std::numeric_limits::infinity()) : std::nextafter((1.f * centFT0Cuint8) - 110.f, std::numeric_limits::infinity())); // this must be inverse of calculation in createEMEventDilepton.cxx -// // DECLARE_SOA_EXPRESSION_COLUMN(CentNTPV, centNTPV, float, (centNTPVuint8 < 100) ? std::nextafter((1.f * centNTPVuint8) / 100.f, std::numeric_limits::infinity()) : std::nextafter((1.f * centNTPVuint8) - 110.f, std::numeric_limits::infinity())); // this must be inverse of calculation in createEMEventDilepton.cxx -// // DECLARE_SOA_EXPRESSION_COLUMN(CentNGlobal, centNGlobal, float, (centNGlobaluint8 < 100) ? std::nextafter((1.f * centNGlobaluint8) / 100.f, std::numeric_limits::infinity()) : std::nextafter((1.f * centNGlobaluint8) - 110.f, std::numeric_limits::infinity())); // this must be inverse of calculation in createEMEventDilepton.cxx -// -// } // namespace emcent - DECLARE_SOA_TABLE(EMBCs_000, "AOD", "EMBC", //! bc information for normalization o2::soa::Index<>, evsel::Selection, evsel::Rct); @@ -357,18 +316,6 @@ DECLARE_SOA_TABLE_VERSIONED(EMEventsCent_001, "AOD", "EMEVENTCENT", 1, //! event using EMEventsCent = EMEventsCent_001; using EMEventCent = EMEventsCent::iterator; -// DECLARE_SOA_TABLE_VERSIONED(EMEventsCentBase_001, "AOD", "EMEVENTCENT", 1, //! event centrality table stored in AO2D -// emcent::CentFT0Muint8, emcent::CentFT0Auint8, emcent::CentFT0Cuint8, emcent::CentNTPVuint8, emcent::CentNGlobaluint8); -// -// using EMEventsCentBase = EMEventsCentBase_001; -// using EMEventCentBase = EMEventsCentBase::iterator; -// -// // Extended table with expression columns that can be used for o2::framework::expressions::Filter. -// DECLARE_SOA_EXTENDED_TABLE_USER(EMEventsCent, EMEventsCentBase, "EMCENTEXT", -// emcent::CentFT0M, emcent::CentFT0A, emcent::CentFT0C, emcent::CentNTPV, emcent::CentNGlobal); -// -// using EMEventCent = EMEventsCent::iterator; - DECLARE_SOA_TABLE_VERSIONED(EMEventsQvec_000, "AOD", "EMEVENTQVEC", 0, //! event q vector table, joinable to EMEvents emevent::Q2xFT0M, emevent::Q2yFT0M, emevent::Q2xFT0A, emevent::Q2yFT0A, emevent::Q2xFT0C, emevent::Q2yFT0C, emevent::Q2xBPos, emevent::Q2yBPos, emevent::Q2xBNeg, emevent::Q2yBNeg, emevent::Q2xBTot, emevent::Q2yBTot, @@ -448,33 +395,38 @@ DECLARE_SOA_TABLE_VERSIONED(EMEventsQvec3_000, "AOD", "EMEVENTQVEC3", 0, //! M using EMEventsQvec3 = EMEventsQvec3_000; using EMEventQvec3 = EMEventsQvec3::iterator; -DECLARE_SOA_TABLE(EMSWTriggerBits, "AOD", "EMSWTBIT", emevent::SWTAlias, o2::soa::Marker<1>); //! joinable to EMEvents +DECLARE_SOA_TABLE_VERSIONED(EMEventsZDC_000, "AOD", "EMEVENTZDC", 0, //! ZDC Qvector information + emevent::QxZDCA, emevent::QyZDCA, emevent::QxZDCC, emevent::QyZDCC); +using EMEventsZDC = EMEventsZDC_000; +using EMEventZDC = EMEventsZDC::iterator; + +DECLARE_SOA_TABLE(EMSWTriggerBits, "AOD", "EMSWTBIT", emevent::TriggerMask, o2::soa::Marker<1>); //! joinable to EMEvents using EMSWTriggerBit = EMSWTriggerBits::iterator; -DECLARE_SOA_TABLE(EMSWTriggerInfos, "AOD", "EMSWTINFO", bc::RunNumber, emevent::NInspectedTVX, emevent::NScalars, emevent::NSelections, o2::soa::Marker<1>); //! independent table. Don't join anything. -using EMSWTriggerInfo = EMSWTriggerInfos::iterator; +// DECLARE_SOA_TABLE(EMSWTriggerInfos, "AOD", "EMSWTINFO", bc::RunNumber, emevent::NInspectedTVX, emevent::NScalars, emevent::NSelections, o2::soa::Marker<1>); //! independent table. Don't join anything. +// using EMSWTriggerInfo = EMSWTriggerInfos::iterator; -DECLARE_SOA_TABLE(EMSWTriggerATCounters, "AOD", "EMSWTAT", emevent::IsAnalyzed, o2::soa::Marker<1>); //! independent table. Don't join anything. +DECLARE_SOA_TABLE(EMSWTriggerATCounters, "AOD", "EMSWTAT", emevent::IsAT, o2::soa::Marker<1>); //! independent table. Don't join anything. using EMSWTriggerATCounter = EMSWTriggerATCounters::iterator; -DECLARE_SOA_TABLE(EMSWTriggerTOICounters, "AOD", "EMSWTTOI", emevent::IsAnalyzedToI, o2::soa::Marker<1>); //! independent table. Don't join anything. -using EMSWTriggerTOICounter = EMSWTriggerTOICounters::iterator; +DECLARE_SOA_TABLE(EMSWTriggerATOICounters, "AOD", "EMSWTATOI", emevent::IsAToI, o2::soa::Marker<1>); //! independent table. Don't join anything. +using EMSWTriggerATOICounter = EMSWTriggerATOICounters::iterator; -DECLARE_SOA_TABLE(EMSWTriggerBitsTMP, "AOD", "EMSWTBITTMP", emevent::SWTAliasTmp, o2::soa::Marker<2>); //! joinable to o2::aod::Collisions +DECLARE_SOA_TABLE(EMSWTriggerBitsTMP, "AOD", "EMSWTBITTMP", emevent::TriggerMask, o2::soa::Marker<2>); //! joinable to o2::aod::Collisions using EMSWTriggerBitTMP = EMSWTriggerBitsTMP::iterator; -DECLARE_SOA_TABLE(EMSWTriggerInfosTMP, "AOD", "EMSWTINFOTMP", bc::RunNumber, emevent::NInspectedTVX, emevent::NScalars, emevent::NSelections, o2::soa::Marker<2>); -using EMSWTriggerInfoTMP = EMSWTriggerInfosTMP::iterator; +// DECLARE_SOA_TABLE(EMSWTriggerInfosTMP, "AOD", "EMSWTINFOTMP", bc::RunNumber, emevent::NInspectedTVX, emevent::NScalars, emevent::NSelections, o2::soa::Marker<2>); +// using EMSWTriggerInfoTMP = EMSWTriggerInfosTMP::iterator; -DECLARE_SOA_TABLE(EMSWTriggerATCountersTMP, "AOD", "EMSWTATTMP", emevent::IsAnalyzed, o2::soa::Marker<2>); //! independent table. Don't join anything. +DECLARE_SOA_TABLE(EMSWTriggerATCountersTMP, "AOD", "EMSWTATTMP", emevent::IsAT, o2::soa::Marker<2>); //! independent table. Don't join anything. using EMSWTriggerATCounterTMP = EMSWTriggerATCountersTMP::iterator; -DECLARE_SOA_TABLE(EMSWTriggerTOICountersTMP, "AOD", "EMSWTTOITMP", emevent::IsAnalyzedToI, o2::soa::Marker<2>); //! independent table. Don't join anything. -using EMSWTriggerTOICounterTMP = EMSWTriggerTOICountersTMP::iterator; +DECLARE_SOA_TABLE(EMSWTriggerATOICountersTMP, "AOD", "EMSWTATOITMP", emevent::IsAToI, o2::soa::Marker<2>); //! independent table. Don't join anything. +using EMSWTriggerATOICounterTMP = EMSWTriggerATOICountersTMP::iterator; -DECLARE_SOA_TABLE(EMEventsProperty, "AOD", "EMEVENTPROP", //! joinable to EMEvents - emevent::SpherocityPtWeighted, emevent::SpherocityPtUnWeighted, emevent::NtrackSpherocity); -using EMEventProperty = EMEventsProperty::iterator; +// DECLARE_SOA_TABLE(EMEventsProperty, "AOD", "EMEVENTPROP", //! joinable to EMEvents +// emevent::SpherocityPtWeighted, emevent::SpherocityPtUnWeighted, emevent::NtrackSpherocity); +// using EMEventProperty = EMEventsProperty::iterator; DECLARE_SOA_TABLE(EMEventsNee, "AOD", "EMEVENTNEE", emevent::NeeULS, emevent::NeeLSpp, emevent::NeeLSmm); // joinable to EMEvents or o2::aod::Collisions using EMEventNee = EMEventsNee::iterator; @@ -693,6 +645,23 @@ DECLARE_SOA_COLUMN(PrefilterBit, pfb, uint8_t); //! DECLARE_SOA_COLUMN(PrefilterBitDerived, pfbderived, uint16_t); //! DECLARE_SOA_COLUMN(ProbElBDT, probElBDT, float); //! +// As sum of probability is 1, keeping 3 out of 4 is enough. +// DECLARE_SOA_COLUMN(BDTScorePromptUINT8, bdtScorePromptUINT8, std::vector); //! scaling factor is 255. +DECLARE_SOA_COLUMN(BDTScorePromptHcUINT8, bdtScorePromptHcUINT8, std::vector); //! scaling factor is 255. +DECLARE_SOA_COLUMN(BDTScoreNonpromptHcUINT8, bdtScoreNonpromptHcUINT8, std::vector); //! scaling factor is 255. +DECLARE_SOA_COLUMN(BDTScoreHbUINT8, bdtScoreHbUINT8, std::vector); //! scaling factor is 255. +DECLARE_SOA_COLUMN(HadronType, hadronType, std::vector); //! 0:track, 1:K0S, 2:Lambda, 3:AntiLambda, 4:XiMinus, 5:XiPlus, 6:OmegaMinus, 7:OmegaPlus + +DECLARE_SOA_DYNAMIC_COLUMN(ProbaSCT, probaSCT, [](gsl::span p1, gsl::span p2, gsl::span p3, gsl::span type, const int index) -> std::array { + return std::array{ + 1.f - (std::nextafter(p1[index] / 255.f, std::numeric_limits::infinity()) + std::nextafter(p2[index] / 255.f, std::numeric_limits::infinity()) + std::nextafter(p3[index] / 255.f, std::numeric_limits::infinity())), + std::nextafter(p1[index] / 255.f, std::numeric_limits::infinity()), + std::nextafter(p2[index] / 255.f, std::numeric_limits::infinity()), + std::nextafter(p3[index] / 255.f, std::numeric_limits::infinity()), + static_cast(type[index])}; +}); +DECLARE_SOA_DYNAMIC_COLUMN(NSV, nSV, [](gsl::span type) -> size_t { return type.size(); }); + DECLARE_SOA_COLUMN(ITSNSigmaEl, itsNSigmaEl, float); //! DECLARE_SOA_COLUMN(ITSNSigmaMu, itsNSigmaMu, float); //! DECLARE_SOA_COLUMN(ITSNSigmaPi, itsNSigmaPi, float); //! @@ -1009,6 +978,13 @@ DECLARE_SOA_TABLE(EMPrimaryElectronsPrefilterBitDerived, "AOD", "PRMELPFBDERIVED // iterators using EMPrimaryElectronPrefilterBitDerived = EMPrimaryElectronsPrefilterBitDerived::iterator; +DECLARE_SOA_TABLE(EMPrimaryElectronsBDTSCT, "AOD", "ELBDTSCT", // To be joined with EMPrimaryElectrons table at analysis level. + /*emprimaryelectron::BDTScorePromptUINT8,*/ emprimaryelectron::BDTScorePromptHcUINT8, emprimaryelectron::BDTScoreNonpromptHcUINT8, emprimaryelectron::BDTScoreHbUINT8, emprimaryelectron::HadronType, + emprimaryelectron::NSV, + emprimaryelectron::ProbaSCT); +// iterators +using EMPrimaryElectronBDTSCT = EMPrimaryElectronsBDTSCT::iterator; + namespace emprimarymuon { DECLARE_SOA_INDEX_COLUMN(EMEvent, emevent); //! @@ -1019,28 +995,34 @@ DECLARE_SOA_COLUMN(MCHTrackId, mchtrackId, int); DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(GlobalMuonsWithSameMCHMID, globalMuonsWithSameMCHMID); //! self indices to global muons that have the same MCHTrackId DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(GlobalMuonsWithSameMFT, globalMuonsWithSameMFT); //! self indices to global muons that have the same MFTTrackId DECLARE_SOA_SELF_ARRAY_INDEX_COLUMN(AmbiguousMuons, ambiguousMuons); -DECLARE_SOA_COLUMN(CXXatDCA, cXXatDCA, float); //! DCAx resolution squared at DCA -DECLARE_SOA_COLUMN(CYYatDCA, cYYatDCA, float); //! DCAy resolution squared at DCA -DECLARE_SOA_COLUMN(CXYatDCA, cXYatDCA, float); //! correlation term of DCAx,y resolution at DCA -DECLARE_SOA_COLUMN(PtMatchedMCHMID, ptMatchedMCHMID, float); //! pt of MCH-MID track in MFT-MCH-MID track at PV -DECLARE_SOA_COLUMN(EtaMatchedMCHMID, etaMatchedMCHMID, float); //! eta of MCH-MID track in MFT-MCH-MID track at PV -DECLARE_SOA_COLUMN(PhiMatchedMCHMID, phiMatchedMCHMID, float); //! phi of MCH-MID track in MFT-MCH-MID track at PV -DECLARE_SOA_COLUMN(EtaMatchedMCHMIDatMP, etaMatchedMCHMIDatMP, float); //! eta of MCH-MID track in MFT-MCH-MID track at matching plane -DECLARE_SOA_COLUMN(PhiMatchedMCHMIDatMP, phiMatchedMCHMIDatMP, float); //! phi of MCH-MID track in MFT-MCH-MID track at matching plane -DECLARE_SOA_COLUMN(EtaMatchedMFTatMP, etaMatchedMFTatMP, float); //! eta of MFT track in MFT-MCH-MID track at matching plane -DECLARE_SOA_COLUMN(PhiMatchedMFTatMP, phiMatchedMFTatMP, float); //! phi of MFT track in MFT-MCH-MID track at matching plane -DECLARE_SOA_COLUMN(IsAssociatedToMPC, isAssociatedToMPC, bool); //! is associated to most probable collision -DECLARE_SOA_COLUMN(IsAmbiguous, isAmbiguous, bool); //! is ambiguous -DECLARE_SOA_COLUMN(IsCorrectMatchMFTMCH, isCorrectMatchMFTMCH, bool); //! is correct match between MFT and MCH, only for MC -DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! -DECLARE_SOA_COLUMN(Chi2MFT, chi2MFT, float); //! chi2 of MFT standalone track -DECLARE_SOA_COLUMN(PrefilterBitDerived, pfbderived, uint16_t); //! +DECLARE_SOA_COLUMN(CXXatDCA, cXXatDCA, float); //! DCAx resolution squared at DCA +DECLARE_SOA_COLUMN(CYYatDCA, cYYatDCA, float); //! DCAy resolution squared at DCA +DECLARE_SOA_COLUMN(CXYatDCA, cXYatDCA, float); //! correlation term of DCAx,y resolution at DCA +DECLARE_SOA_COLUMN(PtMatchedMCHMID, ptMatchedMCHMID, float); //! pt of MCH-MID track in MFT-MCH-MID track at PV +DECLARE_SOA_COLUMN(EtaMatchedMCHMID, etaMatchedMCHMID, float); //! eta of MCH-MID track in MFT-MCH-MID track at PV +DECLARE_SOA_COLUMN(PhiMatchedMCHMID, phiMatchedMCHMID, float); //! phi of MCH-MID track in MFT-MCH-MID track at PV +DECLARE_SOA_COLUMN(EtaMatchedMCHMIDatMP, etaMatchedMCHMIDatMP, float); //! eta of MCH-MID track in MFT-MCH-MID track at matching plane +DECLARE_SOA_COLUMN(PhiMatchedMCHMIDatMP, phiMatchedMCHMIDatMP, float); //! phi of MCH-MID track in MFT-MCH-MID track at matching plane +DECLARE_SOA_COLUMN(EtaMatchedMFTatMP, etaMatchedMFTatMP, float); //! eta of MFT track in MFT-MCH-MID track at matching plane +DECLARE_SOA_COLUMN(PhiMatchedMFTatMP, phiMatchedMFTatMP, float); //! phi of MFT track in MFT-MCH-MID track at matching plane +DECLARE_SOA_COLUMN(IsAssociatedToMPC, isAssociatedToMPC, bool); //! is associated to most probable collision +DECLARE_SOA_COLUMN(IsAmbiguous, isAmbiguous, bool); //! is ambiguous +DECLARE_SOA_COLUMN(IsCorrectMatchMFTMCH, isCorrectMatchMFTMCH, bool); //! is correct match between MFT and MCH, only for MC +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! +DECLARE_SOA_COLUMN(Chi2MFT, chi2MFT, float); //! chi2 of MFT standalone track +DECLARE_SOA_COLUMN(DiffChi2MatchMCHMFT, diffChi2MatchingMCHMFT, float); //! difference of matching chi2 between the best and the 2nd-best matched candidates +DECLARE_SOA_COLUMN(PrefilterBitDerived, pfbderived, uint16_t); //! DECLARE_SOA_DYNAMIC_COLUMN(Tgl, tgl, [](float eta) -> float { return std::tan(o2::constants::math::PIHalf - 2 * std::atan(std::exp(-eta))); }); DECLARE_SOA_DYNAMIC_COLUMN(Signed1Pt, signed1Pt, [](float pt, int8_t sign) -> float { return sign * 1. / pt; }); DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float pt, float eta) -> float { return pt * std::cosh(eta); }); DECLARE_SOA_DYNAMIC_COLUMN(Px, px, [](float pt, float phi) -> float { return pt * std::cos(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Py, py, [](float pt, float phi) -> float { return pt * std::sin(phi); }); DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, [](float pt, float eta) -> float { return pt * std::sinh(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(FwdDCAXY, fwdDcaXY, [](float dcaX, float dcaY) -> float { return std::hypot(dcaX, dcaY); }); +DECLARE_SOA_DYNAMIC_COLUMN(Chi2IP, chi2IP, [](float dcaX, float dcaY, float cXX, float cXY, float cYY) -> float { + float det = cXX * cYY - cXY * cXY; // determinanat + return (det < 0.f) ? 1e+10 : (dcaX * dcaX * cYY + dcaY * dcaY * cXX - 2.f * dcaX * dcaY * cXY) / det; +}); DECLARE_SOA_DYNAMIC_COLUMN(NClustersMFT, nClustersMFT, //! Number of MFT clusters [](uint64_t mftClusterSizesAndTrackFlags) -> uint8_t { uint8_t nClusters = 0; @@ -1133,7 +1115,55 @@ DECLARE_SOA_TABLE_VERSIONED(EMPrimaryMuons_002, "AOD", "EMPRIMARYMU", 2, //! emprimarymuon::Py, emprimarymuon::Pz); -using EMPrimaryMuons = EMPrimaryMuons_002; +DECLARE_SOA_TABLE_VERSIONED(EMPrimaryMuons_003, "AOD", "EMPRIMARYMU", 3, //! + o2::soa::Index<>, emprimarymuon::CollisionId, + emprimarymuon::FwdTrackId, emprimarymuon::MFTTrackId, emprimarymuon::MCHTrackId, fwdtrack::TrackType, + fwdtrack::Pt, fwdtrack::Eta, fwdtrack::Phi, emprimarymuon::Sign, + fwdtrack::FwdDcaX, fwdtrack::FwdDcaY, o2::aod::fwdtrack::CXX, o2::aod::fwdtrack::CYY, o2::aod::fwdtrack::CXY, + emprimarymuon::PtMatchedMCHMID, emprimarymuon::EtaMatchedMCHMID, emprimarymuon::PhiMatchedMCHMID, + + fwdtrack::NClusters, fwdtrack::PDca, fwdtrack::RAtAbsorberEnd, + fwdtrack::Chi2, fwdtrack::Chi2MatchMCHMID, fwdtrack::Chi2MatchMCHMFT, emprimarymuon::DiffChi2MatchMCHMFT, + fwdtrack::MCHBitMap, fwdtrack::MIDBitMap, fwdtrack::MIDBoards, + fwdtrack::MFTClusterSizesAndTrackFlags, emprimarymuon::Chi2MFT, emprimarymuon::IsAssociatedToMPC, emprimarymuon::IsAmbiguous, + + // dynamic column + emprimarymuon::Signed1Pt, + emprimarymuon::Tgl, + emprimarymuon::NClustersMFT, + fwdtrack::IsCA, + emprimarymuon::MFTClusterMap, + emprimarymuon::P, + emprimarymuon::Px, + emprimarymuon::Py, + emprimarymuon::Pz); + +DECLARE_SOA_TABLE_VERSIONED(EMPrimaryMuons_004, "AOD", "EMPRIMARYMU", 4, //! + o2::soa::Index<>, emprimarymuon::CollisionId, + emprimarymuon::FwdTrackId, emprimarymuon::MFTTrackId, emprimarymuon::MCHTrackId, fwdtrack::TrackType, + fwdtrack::Pt, fwdtrack::Eta, fwdtrack::Phi, emprimarymuon::Sign, + fwdtrack::FwdDcaX, fwdtrack::FwdDcaY, emprimarymuon::CXXatDCA, emprimarymuon::CYYatDCA, emprimarymuon::CXYatDCA, + emprimarymuon::PtMatchedMCHMID, emprimarymuon::EtaMatchedMCHMID, emprimarymuon::PhiMatchedMCHMID, + + fwdtrack::NClusters, fwdtrack::PDca, fwdtrack::RAtAbsorberEnd, + fwdtrack::Chi2, fwdtrack::Chi2MatchMCHMID, fwdtrack::Chi2MatchMCHMFT, emprimarymuon::DiffChi2MatchMCHMFT, + fwdtrack::MCHBitMap, fwdtrack::MIDBitMap, fwdtrack::MIDBoards, + fwdtrack::MFTClusterSizesAndTrackFlags, emprimarymuon::Chi2MFT, emprimarymuon::IsAssociatedToMPC, emprimarymuon::IsAmbiguous, + + // dynamic column + emprimarymuon::Signed1Pt, + emprimarymuon::Tgl, + emprimarymuon::NClustersMFT, + fwdtrack::IsCA, + emprimarymuon::MFTClusterMap, + emprimarymuon::P, + emprimarymuon::Px, + emprimarymuon::Py, + emprimarymuon::Pz, + emprimarymuon::FwdDCAXY, + emprimarymuon::Chi2IP); + +using EMPrimaryMuons = EMPrimaryMuons_004; // iterators using EMPrimaryMuon = EMPrimaryMuons::iterator; @@ -1154,7 +1184,16 @@ DECLARE_SOA_TABLE_VERSIONED(EMPrimaryMuonsCov_002, "AOD", "EMPRIMARYMUCOV", 2, / o2::aod::fwdtrack::C1PtPhi, o2::aod::fwdtrack::C1PtTgl, o2::aod::fwdtrack::C1Pt21Pt2); -using EMPrimaryMuonsCov = EMPrimaryMuonsCov_002; + +DECLARE_SOA_TABLE_VERSIONED(EMPrimaryMuonsCov_003, "AOD", "EMPRIMARYMUCOV", 3, //! at PV. Signed1Pt, Tgl and Phi are in EMPrimaryMuons table. + fwdtrack::X, fwdtrack::Y, fwdtrack::Z, + o2::aod::fwdtrack::CXX, + o2::aod::fwdtrack::CXY, o2::aod::fwdtrack::CYY, + o2::aod::fwdtrack::CPhiX, o2::aod::fwdtrack::CPhiY, o2::aod::fwdtrack::CPhiPhi, + o2::aod::fwdtrack::CTglX, o2::aod::fwdtrack::CTglY, o2::aod::fwdtrack::CTglPhi, o2::aod::fwdtrack::CTglTgl, + o2::aod::fwdtrack::C1PtX, o2::aod::fwdtrack::C1PtY, o2::aod::fwdtrack::C1PtPhi, o2::aod::fwdtrack::C1PtTgl, o2::aod::fwdtrack::C1Pt21Pt2); + +using EMPrimaryMuonsCov = EMPrimaryMuonsCov_003; // iterators using EMPrimaryMuonCov = EMPrimaryMuonsCov::iterator; @@ -1176,9 +1215,9 @@ DECLARE_SOA_TABLE(EMPrimaryMuonsPrefilterBitDerived, "AOD", "PRMMUPFBDERIVED", e // iterators using EMPrimaryMuonPrefilterBitDerived = EMPrimaryMuonsPrefilterBitDerived::iterator; -DECLARE_SOA_TABLE(EMPrimaryMuonsMatchMC, "AOD", "EMMUONMATCHMC", emprimarymuon::IsCorrectMatchMFTMCH); // To be joined with EMPrimaryMuons table at analysis level. only for MC. -// iterators -using EMPrimaryMuonMatchMC = EMPrimaryMuonsMatchMC::iterator; +// DECLARE_SOA_TABLE(EMPrimaryMuonsMatchMC, "AOD", "EMMUONMATCHMC", emprimarymuon::IsCorrectMatchMFTMCH); // To be joined with EMPrimaryMuons table at analysis level. only for MC. +// // iterators +// using EMPrimaryMuonMatchMC = EMPrimaryMuonsMatchMC::iterator; namespace oldemprimarytrack { @@ -1195,7 +1234,7 @@ DECLARE_SOA_COLUMN(Signed1Pt, signed1Pt, float); //! (sign of charge)/Pt in c/ DECLARE_SOA_COLUMN(Eta, eta, float); //! DECLARE_SOA_COLUMN(Phi, phi, float); //! DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float signed1Pt) -> float { return 1.f / std::fabs(signed1Pt); }); -DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, [](float signed1Pt) -> short { return (signed1Pt > 0) ? 1 : -1; }); //! Charge: positive: 1, negative: -1 +DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, [](float signed1Pt) -> int16_t { return (signed1Pt > 0) ? 1 : -1; }); //! Charge: positive: 1, negative: -1 } // namespace emprimarytrack DECLARE_SOA_TABLE_VERSIONED(EMPrimaryTracks_000, "AOD", "EMPRIMARYTRACK", 0, //! primary charged track table for 2PC @@ -1221,7 +1260,7 @@ using EMPrimaryTrackEMEventIdTMP = EMPrimaryTrackEMEventIdsTMP::iterator; namespace emthinevent { -DECLARE_SOA_COLUMN(EP2, ep2, float); //! +DECLARE_SOA_COLUMN(EP2, ep2, float); //! DECLARE_SOA_COLUMN(Centrality, centrality, float); //! } // namespace emthinevent DECLARE_SOA_TABLE_VERSIONED(EMThinEvents_000, "AOD", "EMTHINEVENT", 0, //! Thin event information table @@ -1242,13 +1281,13 @@ DECLARE_SOA_INDEX_COLUMN(EMThinEvent, emthinevent); //! DECLARE_SOA_COLUMN(Pt1, pt1, float); //! DECLARE_SOA_COLUMN(Eta1, eta1, float); //! DECLARE_SOA_COLUMN(Phi1, phi1, float); //! -DECLARE_SOA_COLUMN(Sign1, sign1, short); //! +DECLARE_SOA_COLUMN(Sign1, sign1, int16_t); //! DECLARE_SOA_COLUMN(DCA1, dca1, float); //! DCA in sigma. Users should decide 3D or XY or Z DECLARE_SOA_COLUMN(Pt2, pt2, float); //! DECLARE_SOA_COLUMN(Eta2, eta2, float); //! DECLARE_SOA_COLUMN(Phi2, phi2, float); //! DECLARE_SOA_COLUMN(DCA2, dca2, float); //! DCA in sigma. Users should decide 3D or XY or Z -DECLARE_SOA_COLUMN(Sign2, sign2, short); //! +DECLARE_SOA_COLUMN(Sign2, sign2, int16_t); //! DECLARE_SOA_COLUMN(Weight, weight, float); //! possible pair weight } // namespace emdilepton @@ -1259,48 +1298,6 @@ DECLARE_SOA_TABLE_VERSIONED(EMDileptons_000, "AOD", "EMDILEPTON", 0, emdilepton::Weight); using EMDileptons = EMDileptons_000; using EMDilepton = EMDileptons::iterator; - -// Dummy data for MC -namespace emdummydata -{ -DECLARE_SOA_COLUMN(A, a, float); -DECLARE_SOA_COLUMN(B, b, float); -DECLARE_SOA_COLUMN(C, c, float); -DECLARE_SOA_COLUMN(D, d, float); -DECLARE_SOA_COLUMN(E, e, float); -DECLARE_SOA_COLUMN(F, f, float); -DECLARE_SOA_COLUMN(G, g, float); -DECLARE_SOA_COLUMN(H, h, float); -DECLARE_SOA_COLUMN(I, i, float); -DECLARE_SOA_COLUMN(J, j, float); -DECLARE_SOA_COLUMN(K, k, float); -DECLARE_SOA_COLUMN(L, l, float); -DECLARE_SOA_COLUMN(M, m, float); -DECLARE_SOA_COLUMN(N, n, float); -DECLARE_SOA_COLUMN(O, o, float); -DECLARE_SOA_COLUMN(P, p, float); -DECLARE_SOA_COLUMN(Q, q, float); -DECLARE_SOA_COLUMN(R, r, float); -DECLARE_SOA_COLUMN(S, s, float); -DECLARE_SOA_COLUMN(T, t, float); -DECLARE_SOA_COLUMN(U, u, float); -DECLARE_SOA_COLUMN(V, v, float); -DECLARE_SOA_COLUMN(W, w, float); -DECLARE_SOA_COLUMN(X, x, float); -DECLARE_SOA_COLUMN(Y, y, float); -DECLARE_SOA_COLUMN(Z, z, float); -} // namespace emdummydata -DECLARE_SOA_TABLE(EMDummyDatas, "AOD", "EMDUMMYDATA", - o2::soa::Index<>, - emdummydata::A, emdummydata::B, emdummydata::C, emdummydata::D, emdummydata::E, - emdummydata::F, emdummydata::G, emdummydata::H, emdummydata::I, emdummydata::J, - emdummydata::K, emdummydata::L, emdummydata::M, emdummydata::N, emdummydata::O, - emdummydata::P, emdummydata::Q, emdummydata::R, emdummydata::S, emdummydata::T, - emdummydata::U, emdummydata::V, emdummydata::W, emdummydata::X, emdummydata::Y, - emdummydata::Z); - -// iterators -using EMDummyData = EMDummyDatas::iterator; } // namespace o2::aod #endif // PWGEM_DILEPTON_DATAMODEL_DILEPTONTABLES_H_ diff --git a/PWGEM/Dilepton/DataModel/lmeeMLTables.h b/PWGEM/Dilepton/DataModel/lmeeMLTables.h index a215928dfb7..f7965db9a57 100644 --- a/PWGEM/Dilepton/DataModel/lmeeMLTables.h +++ b/PWGEM/Dilepton/DataModel/lmeeMLTables.h @@ -13,7 +13,6 @@ #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" #include @@ -42,17 +41,28 @@ enum class Track_Type : uint8_t { } // namespace pwgem::dilepton::ml +namespace emmlevent +{ +DECLARE_SOA_COLUMN(SubGeneratorId, subGeneratorId, int); //! sub generator Id of mc collision +DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, float); //! +} // namespace emmlevent + +DECLARE_SOA_TABLE(EMMLEvents, "AOD", "EMMLEVENT", //! + o2::soa::Index<>, collision::NumContrib, evsel::NumTracksInTimeRange, evsel::SumAmpFT0CInTimeRange, emmlevent::HadronicRate); +// iterators +using EMMLEvent = EMMLEvents::iterator; + namespace emmltrack { +DECLARE_SOA_INDEX_COLUMN(EMMLEvent, emmlevent); //! index to event table DECLARE_SOA_COLUMN(CollisionId, collisionId, int); //! -DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, float); //! DECLARE_SOA_COLUMN(PIDLabel, pidlabel, uint8_t); //! DECLARE_SOA_COLUMN(TrackType, tracktype, uint8_t); //! DECLARE_SOA_COLUMN(TPCNClsFound, tpcNClsFound, uint8_t); //! DECLARE_SOA_COLUMN(TPCNClsCrossedRows, tpcNClsCrossedRows, uint8_t); //! DECLARE_SOA_COLUMN(TPCNClsPID, tpcNClsPID, uint8_t); //! DECLARE_SOA_COLUMN(IsForValidation, isForValidation, bool); //! -DECLARE_SOA_COLUMN(Sign, sign, short); //! +DECLARE_SOA_COLUMN(Sign, sign, int16_t); //! DECLARE_SOA_COLUMN(P, p, float); //! DECLARE_SOA_COLUMN(PtGen, ptGen, float); //! DECLARE_SOA_COLUMN(EtaGen, etaGen, float); //! @@ -93,22 +103,21 @@ DECLARE_SOA_DYNAMIC_COLUMN(MeanClusterSizeITSob, meanClusterSizeITSob, [](uint32 // reconstructed track information DECLARE_SOA_TABLE(EMTracksForMLPID, "AOD", "EMTRACKMLPID", //! - o2::soa::Index<>, collision::NumContrib, evsel::NumTracksInTimeRange, evsel::SumAmpFT0CInTimeRange, emmltrack::HadronicRate, - emmltrack::P, track::Tgl, emmltrack::Sign, + o2::soa::Index<>, track::Signed1Pt, track::Tgl, track::TPCNClsFindable, emmltrack::TPCNClsFound, emmltrack::TPCNClsCrossedRows, emmltrack::TPCNClsPID, track::TPCChi2NCl, track::TPCInnerParam, - track::TPCSignal, - pidtofbeta::Beta, - track::ITSClusterSizes, track::ITSChi2NCl, track::TOFChi2, track::DetectorMap, emmltrack::PIDLabel, + track::ITSClusterSizes, emmltrack::PIDLabel, // dynamic column emmltrack::MeanClusterSizeITS, emmltrack::MeanClusterSizeITSob); -DECLARE_SOA_TABLE(EMPIDsEl, "AOD", "EMPIDEL", pidtpc::TPCNSigmaEl, pidtof::TOFNSigmaEl); // Joinable with EMTracksForMLPID -DECLARE_SOA_TABLE(EMPIDsPi, "AOD", "EMPIDPI", pidtpc::TPCNSigmaPi, pidtof::TOFNSigmaPi); // Joinable with EMTracksForMLPID -DECLARE_SOA_TABLE(EMPIDsKa, "AOD", "EMPIDKA", pidtpc::TPCNSigmaKa, pidtof::TOFNSigmaKa); // Joinable with EMTracksForMLPID -DECLARE_SOA_TABLE(EMPIDsPr, "AOD", "EMPIDPR", pidtpc::TPCNSigmaPr, pidtof::TOFNSigmaPr); // Joinable with EMTracksForMLPID +DECLARE_SOA_TABLE(EMPIDs, "AOD", "EMPID", track::TPCSignal, pidtofbeta::Beta); // Joinable with EMTracksForMLPID +DECLARE_SOA_TABLE(EMPIDsEl, "AOD", "EMPIDEL", pidtpc::TPCNSigmaEl, pidtof::TOFNSigmaEl); // Joinable with EMTracksForMLPID +DECLARE_SOA_TABLE(EMPIDsPi, "AOD", "EMPIDPI", pidtpc::TPCNSigmaPi, pidtof::TOFNSigmaPi); // Joinable with EMTracksForMLPID +DECLARE_SOA_TABLE(EMPIDsKa, "AOD", "EMPIDKA", pidtpc::TPCNSigmaKa, pidtof::TOFNSigmaKa); // Joinable with EMTracksForMLPID +DECLARE_SOA_TABLE(EMPIDsPr, "AOD", "EMPIDPR", pidtpc::TPCNSigmaPr, pidtof::TOFNSigmaPr); // Joinable with EMTracksForMLPID +DECLARE_SOA_TABLE(EMPIDsSub, "AOD", "EMPIDSUB", track::ITSChi2NCl, track::TOFChi2, track::DetectorMap); // Joinable with EMTracksForMLPID, separated from track table because they are not useful for ML PID. // iterators using EMTrackForMLPID = EMTracksForMLPID::iterator; @@ -153,37 +162,261 @@ DECLARE_SOA_COLUMN(YMFTatMP, yMFTatMP, float); //! y of MFT track in MFT-M DECLARE_SOA_COLUMN(XErrMFTatMP, xErrMFTatMP, float); //! x error of MFT track in MFT-MCH-MID track at matching plane DECLARE_SOA_COLUMN(YErrMFTatMP, yErrMFTatMP, float); //! y error of MFT track in MFT-MCH-MID track at matching plane -DECLARE_SOA_COLUMN(Chi2MFT, chi2MFT, float); //! chi2/ndf of MFT track -DECLARE_SOA_COLUMN(Chi2MCHMID, chi2MCHMID, float); //! chi2/ndf of MCH-MID track -DECLARE_SOA_COLUMN(Chi2MFTMCHMID, chi2MFTMCHMID, float); //! chi2/ndf of MFT-MCH-MID track -DECLARE_SOA_COLUMN(NClustersMFT, nClustersMFT, uint8_t); //! -DECLARE_SOA_COLUMN(IsPrimary, isPrimary, bool); //! -DECLARE_SOA_COLUMN(IsCorrectMatch, isCorrectMatch, bool); //! +DECLARE_SOA_COLUMN(Chi2MFT, chi2MFT, float); //! chi2/ndf of MFT track +DECLARE_SOA_COLUMN(Chi2MCHMID, chi2MCHMID, float); //! chi2/ndf of MCH-MID track +DECLARE_SOA_COLUMN(Chi2MFTMCHMID, chi2MFTMCHMID, float); //! chi2/ndf of MFT-MCH-MID track +DECLARE_SOA_COLUMN(NClustersMFT, nClustersMFT, uint8_t); //! +DECLARE_SOA_COLUMN(IsPrimaryMFT, isPrimaryMFT, bool); //! +DECLARE_SOA_COLUMN(IsPrimaryMCHMID, isPrimaryMCHMID, bool); //! +DECLARE_SOA_COLUMN(IsCorrectMatch, isCorrectMatch, bool); //! +DECLARE_SOA_COLUMN(PdgCodeMFT, pdgCodeMFT, int); //! +DECLARE_SOA_COLUMN(PdgCodeMCHMID, pdgCodeMCHMID, int); //! +DECLARE_SOA_COLUMN(MatchMCHTrackId, mchTrackId, int); //! +DECLARE_SOA_COLUMN(DFId, dfId, uint64_t); //! + +DECLARE_SOA_COLUMN(MultMFT, multMFT, uint16_t); //! number of MFTsa tracks per collision } // namespace emmlfwdtrack DECLARE_SOA_TABLE(EMFwdTracksForML, "AOD", "EMFWDTRKML", //! - o2::soa::Index<>, collision::PosZ, collision::NumContrib, mult::MultFT0C, evsel::NumTracksInTimeRange, evsel::SumAmpFT0CInTimeRange, emmltrack::HadronicRate, - fwdtrack::TrackType, + o2::soa::Index<>, collision::PosZ, /*collision::NumContrib,*/ mult::MultFT0C, /*evsel::NumTracksInTimeRange,*/ evsel::SumAmpFT0CInTimeRange, emmlevent::HadronicRate, emmlfwdtrack::MultMFT, emmlfwdtrack::Signed1PtMFTatMP, emmlfwdtrack::TglMFTatMP, emmlfwdtrack::PhiMFTatMP, - emmlfwdtrack::Signed1PtErrMFTatMP, emmlfwdtrack::TglErrMFTatMP, emmlfwdtrack::PhiErrMFTatMP, emmlfwdtrack::XMFTatMP, emmlfwdtrack::YMFTatMP, - emmlfwdtrack::XErrMFTatMP, emmlfwdtrack::YErrMFTatMP, emmlfwdtrack::Signed1PtMCHMIDatMP, emmlfwdtrack::TglMCHMIDatMP, emmlfwdtrack::PhiMCHMIDatMP, - emmlfwdtrack::Signed1PtErrMCHMIDatMP, emmlfwdtrack::TglErrMCHMIDatMP, emmlfwdtrack::PhiErrMCHMIDatMP, emmlfwdtrack::XMCHMIDatMP, emmlfwdtrack::YMCHMIDatMP, - emmlfwdtrack::XErrMCHMIDatMP, emmlfwdtrack::YErrMCHMIDatMP, - // fwdtrack::FwdDcaX, fwdtrack::FwdDcaY, - fwdtrack::NClusters, fwdtrack::PDca, fwdtrack::RAtAbsorberEnd, - fwdtrack::Chi2MatchMCHMID, fwdtrack::Chi2MatchMCHMFT, - fwdtrack::MFTClusterSizesAndTrackFlags, emmlfwdtrack::Chi2MFTMCHMID, emmlfwdtrack::Chi2MCHMID, emmlfwdtrack::Chi2MFT, emmlfwdtrack::NClustersMFT, - mcparticle::PdgCode, emmlfwdtrack::IsPrimary, emmlfwdtrack::IsCorrectMatch, mcparticle::Pt, mcparticle::Eta, mcparticle::Phi); + fwdtrack::Chi2MatchMCHMFT, + emmlfwdtrack::PdgCodeMFT, emmlfwdtrack::IsPrimaryMFT, + emmlfwdtrack::PdgCodeMCHMID, emmlfwdtrack::IsPrimaryMCHMID, + emmlfwdtrack::IsCorrectMatch, emmlfwdtrack::MatchMCHTrackId, emmlfwdtrack::DFId); // iterators using EMFwdTrackForML = EMFwdTracksForML::iterator; +DECLARE_SOA_TABLE(EMFwdTrackErrsForML, "AOD", "EMFWDTRKERRML", //! Joinable with EMFwdTracksForML + /*emmlfwdtrack::Signed1PtErrMFTatMP,*/ emmlfwdtrack::TglErrMFTatMP, emmlfwdtrack::PhiErrMFTatMP, + emmlfwdtrack::XErrMFTatMP, emmlfwdtrack::YErrMFTatMP, + /*emmlfwdtrack::Signed1PtErrMCHMIDatMP,*/ emmlfwdtrack::TglErrMCHMIDatMP, emmlfwdtrack::PhiErrMCHMIDatMP, + emmlfwdtrack::XErrMCHMIDatMP, emmlfwdtrack::YErrMCHMIDatMP); + +// iterators +using EMFwdTrackErrForML = EMFwdTrackErrsForML::iterator; + +// for SemiCharmTag at midrapidity, only electrons +namespace emmltrack +{ +DECLARE_SOA_COLUMN(IsMotherFromBeauty, isMotherFromBeauty, bool); //! is b quark included in decay history +DECLARE_SOA_COLUMN(Signed1PtL, signedPtL, float); //! sign/pT of lepton +DECLARE_SOA_COLUMN(EtaL, etaL, float); //! eta of lepton +DECLARE_SOA_COLUMN(PhiL, phiL, float); //! phi of lepton +DECLARE_SOA_COLUMN(ImpParXYL, impParXYL, float); //! impact parameter for lepton in XY plane +DECLARE_SOA_COLUMN(ImpParZL, impParZL, float); //! impact parameter for lepton in Z plane +DECLARE_SOA_COLUMN(ImpParCYYL, impParCYYL, float); //! sigma of impact parameter for lepton in XY +DECLARE_SOA_COLUMN(ImpParCZYL, impParCZYL, float); //! sigma of impact parameter for lepton, correlaion term +DECLARE_SOA_COLUMN(ImpParCZZL, impParCZZL, float); //! sigma of impact parameter for lepton in Z +DECLARE_SOA_COLUMN(PdgCodeMother, pdgCodeMother, int); //! pdg code of mother of lepton +DECLARE_SOA_COLUMN(IsCorrectCollision, isCorrectCollision, bool); //! LH pair is associated to correct collision. +DECLARE_SOA_COLUMN(MotherId, motherId, int); //! globalIndex of mother of lepton +} // namespace emmltrack + +DECLARE_SOA_TABLE(EMMLLeptons, "AOD", "EMMLLEPTON", //! + o2::soa::Index<>, emmltrack::EMMLEventId, emmlevent::SubGeneratorId, + emmltrack::Signed1PtL, emmltrack::EtaL, emmltrack::PhiL, + emmltrack::ImpParXYL, emmltrack::ImpParZL, emmltrack::ImpParCYYL, emmltrack::ImpParCZYL, emmltrack::ImpParCZZL, + emmltrack::IsMotherFromBeauty, emmltrack::PdgCodeMother, emmltrack::MotherId, emmltrack::IsCorrectCollision); +// iterators +using EMMLLepton = EMMLLeptons::iterator; + +namespace emmllhpair +{ +DECLARE_SOA_INDEX_COLUMN(EMMLLepton, emmllepton); //! index to lepton table + +DECLARE_SOA_COLUMN(Signed1PtH, signedPtH, float); //! sign/pT of associated hadron +DECLARE_SOA_COLUMN(PtH, ptH, float); //! pT of associated hadron +DECLARE_SOA_COLUMN(EtaH, etaH, float); //! eta of associated hadron +DECLARE_SOA_COLUMN(PhiH, phiH, float); //! phi of associated hadron +DECLARE_SOA_COLUMN(RapidityV0, rapidityV0, float); //! rapidity of associated V0 +DECLARE_SOA_COLUMN(RapidityC, rapidityC, float); //! rapidity of associated Cascade + +DECLARE_SOA_COLUMN(ImpParXYH, impParXYH, float); //! impact parameter for V0/Cascade in XY plane +DECLARE_SOA_COLUMN(ImpParZH, impParZH, float); //! impact parameter for V0/Cascade in Z plane +DECLARE_SOA_COLUMN(ImpParCYYH, impParCYYH, float); //! sigma of impact parameter for V0/Cascade in XY +DECLARE_SOA_COLUMN(ImpParCZYH, impParCZYH, float); //! sigma of impact parameter for V0/Cascade, correlaion term +DECLARE_SOA_COLUMN(ImpParCZZH, impParCZZH, float); //! sigma of impact parameter for V0/Cascade in Z + +DECLARE_SOA_COLUMN(V0CPA, v0cpa, float); //! cosPA of V0 +DECLARE_SOA_COLUMN(V0CPAXY, v0cpaXY, float); //! cosPA of V0 in XY plane +DECLARE_SOA_COLUMN(V0CPARZ, v0cpaRZ, float); //! cosPA of V0 in RZ plane + +DECLARE_SOA_COLUMN(CascCPA, casccpa, float); //! cosPA of Cascade +DECLARE_SOA_COLUMN(CascCPAXY, casccpaXY, float); //! cosPA of Cascade in XY plane +DECLARE_SOA_COLUMN(CascCPARZ, casccpaRZ, float); //! cosPA of Cascade in RZ plane + +DECLARE_SOA_COLUMN(V0Type, v0Type, uint8_t); //! v0 type, 0 = K0S, 1 = Lambda, 2 = AntiLambda +DECLARE_SOA_COLUMN(CascadeType, cascadeType, uint8_t); //! cascade type, 0 = XiMunus, 1 = OmegaMinus + +// LH pair variables +DECLARE_SOA_COLUMN(MassLH, massLH, float); //! invariant mass of LH assuming kaon +// DECLARE_SOA_COLUMN(PtLH, ptLH, float); //! pt of LH +// DECLARE_SOA_COLUMN(PLH, pLH, float); //! p of LH +// DECLARE_SOA_COLUMN(DEtaLH, dEtaLH, float); //! deta between LH at SV +// DECLARE_SOA_COLUMN(DPhiLH, dPhiLH, float); //! dphi between LH at SV + +DECLARE_SOA_COLUMN(PtSVL, ptSVL, float); //! pT of lepton at SV +// DECLARE_SOA_COLUMN(PlSVL, plSVL, float); //! pL of lepton at SV +DECLARE_SOA_COLUMN(PtSVH, ptSVH, float); //! pT of associated hadron at SV +// DECLARE_SOA_COLUMN(PlSVH, plSVH, float); //! pL of associated hadron at SV + +// DECLARE_SOA_COLUMN(PtFDL, ptFDL, float); //! pT of lepton perpendicular to flight direction +// DECLARE_SOA_COLUMN(PlFDL, plFDL, float); //! pL of lepton in parallel to flight direction +// DECLARE_SOA_COLUMN(PtFDH, ptFDH, float); //! pT of associated hadron perpendicular to flight direction +// DECLARE_SOA_COLUMN(PlFDH, plFDH, float); //! pL of associated hadron in parallel to flight direction + +DECLARE_SOA_COLUMN(PtFD, ptFD, float); //! visible momentum of LH pair perpendicular to flight direction = - missing pT due to neutrino perpendicular to flight direction +DECLARE_SOA_COLUMN(PlFD, plFD, float); //! visible momentum of LH pair in parallel to flight direction + +// DECLARE_SOA_COLUMN(DcaLH, dcalh, float); //! DCA between lepton and hadron +DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); //! DCA between lepton and hadron +DECLARE_SOA_COLUMN(CPA, cpa, float); //! cosine of pointing angle of LH pair +DECLARE_SOA_COLUMN(CPAXY, cpaXY, float); //! cosine of pointing angle of LH pair in XY +DECLARE_SOA_COLUMN(CPARZ, cpaRZ, float); //! cosine of pointing angle of LH pair in RZ + +DECLARE_SOA_COLUMN(Lxy, lxy, float); //! decay length of LH pair +DECLARE_SOA_COLUMN(Lz, lz, float); //! decay length of LH pair +DECLARE_SOA_COLUMN(Lxyz, lxyz, float); //! decay length of LH pair +DECLARE_SOA_COLUMN(LxyErr, lxyErr, float); //! decay length resolution of LH pair +DECLARE_SOA_COLUMN(LzErr, lzErr, float); //! decay length resolution of LH pair +DECLARE_SOA_COLUMN(LxyzErr, lxyzErr, float); //! decay length resolution of LH pair + +// DECLARE_SOA_COLUMN(ImpParXY, impParXY, float); //! impact parameter for LH in XY plane +// DECLARE_SOA_COLUMN(ImpParZ, impParZ, float); //! impact parameter for LH in Z plane +// DECLARE_SOA_COLUMN(ImpParCYY, impParCYY, float); //! sigma of impact parameter for LH in XY +// DECLARE_SOA_COLUMN(ImpParCZY, impParCZY, float); //! sigma of impact parameter for LH, correlation term +// DECLARE_SOA_COLUMN(ImpParCZZ, impParCZZ, float); //! sigma of impact parameter for LH in Z + +DECLARE_SOA_COLUMN(PdgCodeH, pdgCodeH, int); //! pdg code of associated hadron +DECLARE_SOA_COLUMN(PdgCodeIM, pdgCodeIM, int); //! pdg code of intermediate hadron from HF hadrons. e.g K*, D* +DECLARE_SOA_COLUMN(FoundCommonMother, foundCommonMother, bool); //! decay length resolution of LH pair +} // namespace emmllhpair + +DECLARE_SOA_TABLE(EMMLLTPairs, "AOD", "EMMLLTPAIR", //! + emmllhpair::EMMLLeptonId, + emmllhpair::Signed1PtH, emmllhpair::EtaH, + emmllhpair::ImpParXYH, emmllhpair::ImpParZH, emmllhpair::ImpParCYYH, emmllhpair::ImpParCZYH, emmllhpair::ImpParCZZH, + // pidtpc::TPCNSigmaPi, pidtof::TOFNSigmaPi, + pidtpc::TPCNSigmaKa, pidtof::TOFNSigmaKa, + // pidtpc::TPCNSigmaPr, pidtof::TOFNSigmaPr, + emmllhpair::MassLH, /*emmllhpair::PLH, emmllhpair::DEtaLH, emmllhpair::DPhiLH,*/ + // emmllhpair::PtSVL, emmllhpair::PtSVH, emmllhpair::PtFD, emmllhpair::PlFD, + emmllhpair::Chi2PCA, emmllhpair::CPA, emmllhpair::CPAXY, emmllhpair::CPARZ, + emmllhpair::Lxy, emmllhpair::Lz, emmllhpair::Lxyz, emmllhpair::LxyErr, emmllhpair::LzErr, emmllhpair::LxyzErr, + // emmllhpair::ImpParXY, emmllhpair::ImpParZ, emmllhpair::ImpParCYY, emmllhpair::ImpParCZY, emmllhpair::ImpParCZZ, + emmllhpair::PdgCodeH, emmllhpair::PdgCodeIM, emmllhpair::FoundCommonMother); +// iterators +using EMMLLTPair = EMMLLTPairs::iterator; + +DECLARE_SOA_TABLE(EMMLLV0Pairs, "AOD", "EMMLLV0PAIR", //! + emmllhpair::EMMLLeptonId, emmllhpair::V0Type, + emmllhpair::PtH, emmllhpair::RapidityV0, + emmllhpair::V0CPA, emmllhpair::V0CPAXY, emmllhpair::V0CPARZ, + emmllhpair::ImpParXYH, emmllhpair::ImpParZH, emmllhpair::ImpParCYYH, emmllhpair::ImpParCZYH, emmllhpair::ImpParCZZH, + emmllhpair::MassLH, /*emmllhpair::PLH, emmllhpair::DEtaLH, emmllhpair::DPhiLH,*/ + // emmllhpair::PtSVL, emmllhpair::PtSVH, emmllhpair::PtFD, emmllhpair::PlFD, + emmllhpair::Chi2PCA, emmllhpair::CPA, emmllhpair::CPAXY, emmllhpair::CPARZ, + emmllhpair::Lxy, emmllhpair::Lz, emmllhpair::Lxyz, emmllhpair::LxyErr, emmllhpair::LzErr, emmllhpair::LxyzErr, + // emmllhpair::ImpParXY, emmllhpair::ImpParZ, emmllhpair::ImpParCYY, emmllhpair::ImpParCZY, emmllhpair::ImpParCZZ, + emmllhpair::PdgCodeH, emmllhpair::PdgCodeIM, emmllhpair::FoundCommonMother); +// iterators +using EMMLLV0Pair = EMMLLV0Pairs::iterator; + +DECLARE_SOA_TABLE(EMMLLCascPairs, "AOD", "EMMLLCPAIR", //! + emmllhpair::EMMLLeptonId, emmllhpair::CascadeType, + emmllhpair::Signed1PtH, emmllhpair::RapidityC, + emmllhpair::CascCPA, emmllhpair::CascCPAXY, emmllhpair::CascCPARZ, + emmllhpair::ImpParXYH, emmllhpair::ImpParZH, emmllhpair::ImpParCYYH, emmllhpair::ImpParCZYH, emmllhpair::ImpParCZZH, + emmllhpair::MassLH, /*emmllhpair::PLH, emmllhpair::DEtaLH, emmllhpair::DPhiLH,*/ + // emmllhpair::PtSVL, emmllhpair::PtSVH, emmllhpair::PtFD, emmllhpair::PlFD, + emmllhpair::Chi2PCA, emmllhpair::CPA, emmllhpair::CPAXY, emmllhpair::CPARZ, + emmllhpair::Lxy, emmllhpair::Lz, emmllhpair::Lxyz, emmllhpair::LxyErr, emmllhpair::LzErr, emmllhpair::LxyzErr, + // emmllhpair::ImpParXY, emmllhpair::ImpParZ, emmllhpair::ImpParCYY, emmllhpair::ImpParCZY, emmllhpair::ImpParCZZ, + emmllhpair::PdgCodeH, emmllhpair::PdgCodeIM, emmllhpair::FoundCommonMother); +// iterators +using EMMLLCascPair = EMMLLCascPairs::iterator; + +namespace emmldilepton +{ +DECLARE_SOA_INDEX_COLUMN(EMMLEvent, emmlevent); //! index to event table +DECLARE_SOA_COLUMN(Signed1Pt1, signed1Pt1, float); //! q/pt of lepton1 at PV +DECLARE_SOA_COLUMN(Eta1, eta1, float); //! eta of lepton1 at PV +DECLARE_SOA_COLUMN(ImpParXY1, impParXY1, float); //! impact parameter for lepton1 in XY plane +DECLARE_SOA_COLUMN(ImpParZ1, impParZ1, float); //! impact parameter for lepton1 in Z plane +DECLARE_SOA_COLUMN(ImpParCYY1, impParCYY1, float); //! sigma of impact parameter for lepton1 in XY +DECLARE_SOA_COLUMN(ImpParCZY1, impParCZY1, float); //! sigma of impact parameter for lepton1, correlaion term +DECLARE_SOA_COLUMN(ImpParCZZ1, impParCZZ1, float); //! sigma of impact parameter for lepton1 in Z + +DECLARE_SOA_COLUMN(UnbiasedImpParXY1, unbiasedImpParXY1, float); //! impact parameter for lepton1 in XY plane +DECLARE_SOA_COLUMN(UnbiasedImpParZ1, unbiasedImpParZ1, float); //! impact parameter for lepton1 in Z plane +DECLARE_SOA_COLUMN(UnbiasedImpParCYY1, unbiasedImpParCYY1, float); //! sigma of impact parameter for lepton1 in XY +DECLARE_SOA_COLUMN(UnbiasedImpParCZY1, unbiasedImpParCZY1, float); //! sigma of impact parameter for lepton1, correlaion term +DECLARE_SOA_COLUMN(UnbiasedImpParCZZ1, unbiasedImpParCZZ1, float); //! sigma of impact parameter for lepton1 in Z + +DECLARE_SOA_COLUMN(IsCorrectCollision1, isCorrectCollision1, bool); //! lepton1 is associated to correct collision. +DECLARE_SOA_COLUMN(IsReassociated1, isReassociated1, bool); //! lepton1 is reassociated. +DECLARE_SOA_COLUMN(PdgCodeMother1, pdgCodeMother1, int); //! pdg code of mother of lepton1 + +DECLARE_SOA_COLUMN(Signed1Pt2, signed1Pt2, float); //! q/pt of lepton2 at PV +DECLARE_SOA_COLUMN(Eta2, eta2, float); //! eta of lepton1 at PV +DECLARE_SOA_COLUMN(ImpParXY2, impParXY2, float); //! impact parameter for lepton2 in XY plane +DECLARE_SOA_COLUMN(ImpParZ2, impParZ2, float); //! impact parameter for lepton2 in Z plane +DECLARE_SOA_COLUMN(ImpParCYY2, impParCYY2, float); //! sigma of impact parameter for lepton2 in XY +DECLARE_SOA_COLUMN(ImpParCZY2, impParCZY2, float); //! sigma of impact parameter for lepton2, correlaion term +DECLARE_SOA_COLUMN(ImpParCZZ2, impParCZZ2, float); //! sigma of impact parameter for lepton2 in Z + +DECLARE_SOA_COLUMN(UnbiasedImpParXY2, unbiasedImpParXY2, float); //! impact parameter for lepton2 in XY plane +DECLARE_SOA_COLUMN(UnbiasedImpParZ2, unbiasedImpParZ2, float); //! impact parameter for lepton2 in Z plane +DECLARE_SOA_COLUMN(UnbiasedImpParCYY2, unbiasedImpParCYY2, float); //! sigma of impact parameter for lepton2 in XY +DECLARE_SOA_COLUMN(UnbiasedImpParCZY2, unbiasedImpParCZY2, float); //! sigma of impact parameter for lepton2, correlaion term +DECLARE_SOA_COLUMN(UnbiasedImpParCZZ2, unbiasedImpParCZZ2, float); //! sigma of impact parameter for lepton2 in Z + +DECLARE_SOA_COLUMN(IsCorrectCollision2, isCorrectCollision2, bool); //! lepton is associated to correct collision. +DECLARE_SOA_COLUMN(IsReassociated2, isReassociated2, bool); //! lepton2 is reassociated. +DECLARE_SOA_COLUMN(PdgCodeMother2, pdgCodeMother2, int); //! pdg code of mother of lepton1 + +DECLARE_SOA_COLUMN(Mass, mass, float); //! invariant mass of dilepton +DECLARE_SOA_COLUMN(Pt, pt, float); //! dilepton pair pt +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); //! dilepton pair rapidity +DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); //! chi2 at PCA of dilepton +DECLARE_SOA_COLUMN(CPA, cpa, float); //! cosine of pointing angle of dilepton +DECLARE_SOA_COLUMN(CPAXY, cpaXY, float); //! cosine of pointing angle of dilepton in XY +DECLARE_SOA_COLUMN(CPARZ, cpaRZ, float); //! cosine of pointing angle of dilepton in RZ + +DECLARE_SOA_COLUMN(Lxy, lxy, float); //! decay length of dilepton +DECLARE_SOA_COLUMN(Lz, lz, float); //! decay length of dilepton +DECLARE_SOA_COLUMN(Lxyz, lxyz, float); //! decay length of dilepton +DECLARE_SOA_COLUMN(LxyErr, lxyErr, float); //! decay length resolution of dilepton +DECLARE_SOA_COLUMN(LzErr, lzErr, float); //! decay length resolution of dilepton +DECLARE_SOA_COLUMN(LxyzErr, lxyzErr, float); //! decay length resolution of dilepton + +DECLARE_SOA_COLUMN(DileptonType, dileptonType, uint8_t); //! dilepton type: 0=combinatorial bkg, 1=prompt SM, 2=nonprompt SM, 3=c2l_c2l, 4=b2l_b2l, 5=b2bc2l_b2c2l, 6=b2c2e_b2e_sameb, 7=b2c2e_b2e_diffb +DECLARE_SOA_COLUMN(PdgCodeCommonMother, pdgCodeCommonMother, int); //! pdg code of common mother. If it is not found, -1. +} // namespace emmldilepton + +DECLARE_SOA_TABLE(EMMLDielectronsAtSV, "AOD", "EMMLEESV", //! + emmldilepton::EMMLEventId, + emmldilepton::Signed1Pt1, emmldilepton::Eta1, emmldilepton::ImpParXY1, emmldilepton::ImpParZ1, emmldilepton::ImpParCYY1, emmldilepton::ImpParCZY1, emmldilepton::ImpParCZZ1, emmldilepton::IsCorrectCollision1, emmldilepton::IsReassociated1, emmldilepton::PdgCodeMother1, + emmldilepton::Signed1Pt2, emmldilepton::Eta2, emmldilepton::ImpParXY2, emmldilepton::ImpParZ2, emmldilepton::ImpParCYY2, emmldilepton::ImpParCZY2, emmldilepton::ImpParCZZ2, emmldilepton::IsCorrectCollision2, emmldilepton::IsReassociated2, emmldilepton::PdgCodeMother2, + + emmldilepton::Mass, emmldilepton::Pt, emmldilepton::Rapidity, + emmldilepton::Chi2PCA, + emmldilepton::CPA, emmldilepton::CPAXY, emmldilepton::CPARZ, + emmldilepton::Lxy, emmldilepton::Lz, emmldilepton::Lxyz, + emmldilepton::LxyErr, emmldilepton::LzErr, emmldilepton::LxyzErr, + emmldilepton::DileptonType); + +// iterators +using EMMLDielectronAtSV = EMMLDielectronsAtSV::iterator; + } // namespace o2::aod #endif // PWGEM_DILEPTON_DATAMODEL_LMEEMLTABLES_H_ diff --git a/PWGEM/Dilepton/TableProducer/CMakeLists.txt b/PWGEM/Dilepton/TableProducer/CMakeLists.txt index 617d07a86d1..8a00046766a 100644 --- a/PWGEM/Dilepton/TableProducer/CMakeLists.txt +++ b/PWGEM/Dilepton/TableProducer/CMakeLists.txt @@ -31,6 +31,11 @@ o2physics_add_dpl_workflow(skimmer-primary-electron PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(skimmer-primary-electron-sv + SOURCES skimmerPrimaryElectronSV.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(skimmer-primary-electron-qc SOURCES skimmerPrimaryElectronQC.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::MLCore @@ -41,11 +46,6 @@ o2physics_add_dpl_workflow(skimmer-primary-muon PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(skimmer-primary-muon-qc - SOURCES skimmerPrimaryMuonQC.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(skimmer-primary-track SOURCES skimmerPrimaryTrack.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -81,16 +81,6 @@ o2physics_add_dpl_workflow(filter-eoi PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dielectron-producer - SOURCES dielectronProducer.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(dimuon-producer - SOURCES dimuonProducer.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(qvector-dummy-otf SOURCES qVectorDummyOTF.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -106,6 +96,11 @@ o2physics_add_dpl_workflow(qvector3-dummy-otf PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(prefilter-dielectron + SOURCES prefilterDielectron.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(prefilter-dimuon SOURCES prefilterDimuon.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore diff --git a/PWGEM/Dilepton/TableProducer/Converters/CMakeLists.txt b/PWGEM/Dilepton/TableProducer/Converters/CMakeLists.txt index d3b100b2a31..3185ecb31af 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/CMakeLists.txt +++ b/PWGEM/Dilepton/TableProducer/Converters/CMakeLists.txt @@ -70,6 +70,16 @@ o2physics_add_dpl_workflow(muon-converter2 PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(muon-converter3 + SOURCES muonConverter3.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(muon-converter4 + SOURCES muonConverter4.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(muon-selfid-converter1 SOURCES muonSelfIdConverter1.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGEM/Dilepton/TableProducer/Converters/bcConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/bcConverter1.cxx index 33aed751377..32d14783f02 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/bcConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/bcConverter1.cxx @@ -31,6 +31,7 @@ struct bcConverter1 { void process(aod::EMBCs_000 const& bcs) { + bc_001.reserve(bcs.size()); for (const auto& bc : bcs) { bc_001(o2::aod::emevsel::reduceSelectionBit(bc), bc.rct_raw()); } // end of bc loop diff --git a/PWGEM/Dilepton/TableProducer/Converters/electronConverter2.cxx b/PWGEM/Dilepton/TableProducer/Converters/electronConverter2.cxx index 86ed8dde8bd..7fd694068e4 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/electronConverter2.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/electronConverter2.cxx @@ -31,6 +31,7 @@ struct electronConverter2 { void process(aod::EMPrimaryElectrons_001 const& tracks) { + track_002.reserve(tracks.size()); for (const auto& track : tracks) { track_002(track.collisionId(), track.trackId(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/electronConverter3.cxx b/PWGEM/Dilepton/TableProducer/Converters/electronConverter3.cxx index c543037fbd1..a77f8a61be5 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/electronConverter3.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/electronConverter3.cxx @@ -31,6 +31,7 @@ struct electronConverter3 { void process(aod::EMPrimaryElectrons_002 const& tracks) { + track_003.reserve(tracks.size()); for (const auto& track : tracks) { track_003(track.collisionId(), track.trackId(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/electronConverter4.cxx b/PWGEM/Dilepton/TableProducer/Converters/electronConverter4.cxx index e9bc1fd78b2..968f7525764 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/electronConverter4.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/electronConverter4.cxx @@ -34,6 +34,7 @@ struct electronConverter4 { using MyElectrons002 = soa::Join; void process002to004(MyElectrons002 const& tracks) { + track_004.reserve(tracks.size()); for (const auto& track : tracks) { track_004(track.collisionId(), track.trackId(), @@ -78,6 +79,7 @@ struct electronConverter4 { using MyElectrons003 = soa::Join; void process003to004(MyElectrons003 const& tracks) { + track_004.reserve(tracks.size()); for (const auto& track : tracks) { track_004(track.collisionId(), track.trackId(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/electronConverter5.cxx b/PWGEM/Dilepton/TableProducer/Converters/electronConverter5.cxx index 5367e71f597..faf52e3ff99 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/electronConverter5.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/electronConverter5.cxx @@ -34,6 +34,7 @@ struct electronConverter5 { using MyElectrons002 = soa::Join; void process002to005(MyElectrons002 const& tracks) { + track_005.reserve(tracks.size()); for (const auto& track : tracks) { track_005(track.collisionId(), track.trackId(), @@ -79,6 +80,7 @@ struct electronConverter5 { using MyElectrons003 = soa::Join; void process003to005(MyElectrons003 const& tracks) { + track_005.reserve(tracks.size()); for (const auto& track : tracks) { track_005(track.collisionId(), track.trackId(), @@ -123,6 +125,7 @@ struct electronConverter5 { void process004to005(aod::EMPrimaryElectrons_004 const& tracks) { + track_005.reserve(tracks.size()); for (const auto& track : tracks) { track_005(track.collisionId(), track.trackId(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/electronConverter6.cxx b/PWGEM/Dilepton/TableProducer/Converters/electronConverter6.cxx index 7506fe10688..f5113e7aae9 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/electronConverter6.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/electronConverter6.cxx @@ -31,6 +31,7 @@ struct electronConverter6 { void process(aod::EMPrimaryElectrons_005 const& tracks) { + track_006.reserve(tracks.size()); for (const auto& track : tracks) { track_006(track.collisionId(), track.trackId(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventCentConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventCentConverter1.cxx index ed040916f03..143a92f353b 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventCentConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventCentConverter1.cxx @@ -31,6 +31,7 @@ struct eventCentConverter1 { void process(aod::EMEventsCent_000 const& collisions) { + cent_001.reserve(collisions.size()); for (const auto& collision : collisions) { cent_001( collision.centFT0M(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventConverter2.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventConverter2.cxx index 414241dea5c..7b9fdb57058 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventConverter2.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventConverter2.cxx @@ -31,6 +31,7 @@ struct eventConverter2 { void process(aod::EMEvents_001 const& collisions) { + event_002.reserve(collisions.size()); for (const auto& collision : collisions) { event_002( collision.globalIndex(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventConverter3.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventConverter3.cxx index 894d3d2984c..141ba63382c 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventConverter3.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventConverter3.cxx @@ -31,6 +31,7 @@ struct eventConverter3 { void process(aod::EMEvents_002 const& collisions) { + event_003.reserve(collisions.size()); for (const auto& collision : collisions) { event_003( collision.globalIndex(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventConverter4.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventConverter4.cxx index ff12b6a1405..f4521c0ca6e 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventConverter4.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventConverter4.cxx @@ -33,6 +33,7 @@ struct eventConverter4 { void process003to004(aod::EMEvents_003 const& collisions) { + event_004.reserve(collisions.size()); for (const auto& collision : collisions) { event_004( collision.globalIndex(), @@ -51,6 +52,7 @@ struct eventConverter4 { void processAlias(aod::EMEvents_003 const& collisions) { + eventalias_000.reserve(collisions.size()); for (const auto& collision : collisions) { eventalias_000( collision.alias_raw()); diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventConverter5.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventConverter5.cxx index f4298c119c6..46a26e300e2 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventConverter5.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventConverter5.cxx @@ -91,6 +91,7 @@ struct eventConverter5 { void process004to005(aod::EMEvents_004 const& collisions) { + event_005.reserve(collisions.size()); for (const auto& collision : collisions) { event_005( collision.globalIndex(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventMultConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventMultConverter1.cxx index c1c8cd61a2a..e666375387d 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventMultConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventMultConverter1.cxx @@ -31,6 +31,7 @@ struct eventMultConverter1 { void process(aod::EMEventsMult_000 const& collisions) { + mult_001.reserve(collisions.size()); for (const auto& collision : collisions) { mult_001( collision.multFT0A(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter1.cxx index f51122a77a2..e2a848c3fbb 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter1.cxx @@ -33,6 +33,7 @@ struct eventNormConverter1 { void process(aod::EMEventNormInfos_000 const& collisions) { + event_001.reserve(collisions.size()); for (const auto& collision : collisions) { event_001( collision.selection_raw(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter2.cxx b/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter2.cxx index 79dc86b136c..b42f9aa04fc 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter2.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/eventNormConverter2.cxx @@ -33,6 +33,7 @@ struct eventNormConverter2 { void process(aod::EMEventNormInfos_001 const& collisions) { + event_002.reserve(collisions.size()); for (const auto& collision : collisions) { int8_t posZint8 = static_cast(collision.posZ() * 2.f); diff --git a/PWGEM/Dilepton/TableProducer/Converters/mcParticleConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/mcParticleConverter1.cxx index 92df1709c3d..6b92eee9a8e 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/mcParticleConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/mcParticleConverter1.cxx @@ -33,6 +33,7 @@ struct mcParticleConverter1 { void process(aod::EMMCParticles_000 const& mcParticles) { + mcParticle_001.reserve(mcParticles.size()); for (const auto& mcParticle : mcParticles) { // LOGF(info, "mcParticles.emmceventId() = %d, mcParticle.mothersIds().size() = %d, mcParticle.daughtersIds().size() = %d", mcParticle.emmceventId(), mcParticle.mothersIds().size(), mcParticle.daughtersIds().size()); diff --git a/PWGEM/Dilepton/TableProducer/Converters/muonConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/muonConverter1.cxx index d0f5596f445..4cf40d3fe88 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/muonConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/muonConverter1.cxx @@ -31,6 +31,7 @@ struct muonConverter1 { void process(aod::EMPrimaryMuons_000 const& muons) { + muon_001.reserve(muons.size()); for (const auto& muon : muons) { muon_001( muon.collisionId(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/muonConverter2.cxx b/PWGEM/Dilepton/TableProducer/Converters/muonConverter2.cxx index 7bc2a63916b..e0f214bca00 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/muonConverter2.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/muonConverter2.cxx @@ -31,6 +31,7 @@ struct muonConverter2 { void process(aod::EMPrimaryMuons_001 const& muons) { + muon_002.reserve(muons.size()); for (const auto& muon : muons) { muon_002( muon.collisionId(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/muonConverter3.cxx b/PWGEM/Dilepton/TableProducer/Converters/muonConverter3.cxx new file mode 100644 index 00000000000..b6e45334276 --- /dev/null +++ b/PWGEM/Dilepton/TableProducer/Converters/muonConverter3.cxx @@ -0,0 +1,52 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code produces muon table 003 from 002. +// Please write to: daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct muonConverter3 { + Produces muon_003; + + void process(aod::EMPrimaryMuons_002 const& muons) + { + for (const auto& muon : muons) { + muon_003( + muon.collisionId(), + muon.fwdtrackId(), muon.mfttrackId(), muon.mchtrackId(), muon.trackType(), + muon.pt(), muon.eta(), muon.phi(), muon.sign(), + muon.fwdDcaX(), muon.fwdDcaY(), muon.cXX(), muon.cYY(), muon.cXY(), + muon.ptMatchedMCHMID(), muon.etaMatchedMCHMID(), muon.phiMatchedMCHMID(), + muon.nClusters(), muon.pDca(), muon.rAtAbsorberEnd(), + muon.chi2(), muon.chi2MatchMCHMID(), muon.chi2MatchMCHMFT(), 9999.f, + muon.mchBitMap(), muon.midBitMap(), muon.midBoards(), + muon.mftClusterSizesAndTrackFlags(), muon.chi2MFT(), muon.isAssociatedToMPC(), muon.isAmbiguous()); + } // end of muon loop + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"muon-converter3"})}; +} diff --git a/PWGEM/Dilepton/TableProducer/Converters/muonConverter4.cxx b/PWGEM/Dilepton/TableProducer/Converters/muonConverter4.cxx new file mode 100644 index 00000000000..a2917c791e5 --- /dev/null +++ b/PWGEM/Dilepton/TableProducer/Converters/muonConverter4.cxx @@ -0,0 +1,69 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code produces muon table 004 from 003. +// Please write to: daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct muonConverter4 { + Produces muon_004; + Produces muoncov_003; + + void processMuon(o2::aod::EMPrimaryMuons_003 const& muons) + { + for (const auto& muon : muons) { + muon_004( + muon.collisionId(), + muon.fwdtrackId(), muon.mfttrackId(), muon.mchtrackId(), muon.trackType(), + muon.pt(), muon.eta(), muon.phi(), muon.sign(), + muon.fwdDcaX(), muon.fwdDcaY(), muon.cXX(), muon.cYY(), muon.cXY(), + muon.ptMatchedMCHMID(), muon.etaMatchedMCHMID(), muon.phiMatchedMCHMID(), + muon.nClusters(), muon.pDca(), muon.rAtAbsorberEnd(), + muon.chi2(), muon.chi2MatchMCHMID(), muon.chi2MatchMCHMFT(), muon.diffChi2MatchingMCHMFT(), + muon.mchBitMap(), muon.midBitMap(), muon.midBoards(), + muon.mftClusterSizesAndTrackFlags(), muon.chi2MFT(), muon.isAssociatedToMPC(), muon.isAmbiguous()); + } // end of muon loop + } + PROCESS_SWITCH(muonConverter4, processMuon, "convert primary muon 003 into 004", true); + + void processMuonCov(soa::Join const& muons) + { + for (const auto& muon : muons) { + muoncov_003(muon.x(), muon.y(), muon.y(), + muon.cXX(), + muon.cYY(), muon.cXY(), + muon.cPhiX(), muon.cPhiY(), muon.cPhiPhi(), + muon.cTglX(), muon.cTglY(), muon.cTglPhi(), muon.cTglTgl(), + muon.c1PtX(), muon.c1PtY(), muon.c1PtPhi(), muon.c1PtTgl(), muon.c1Pt21Pt2()); + } // end of muon loop + } + PROCESS_SWITCH(muonConverter4, processMuonCov, "convert primary muon 002 into 003", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"muon-converter4"})}; +} diff --git a/PWGEM/Dilepton/TableProducer/Converters/muonSelfIdConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/muonSelfIdConverter1.cxx index 031c853b12d..fdf440d1ead 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/muonSelfIdConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/muonSelfIdConverter1.cxx @@ -33,6 +33,7 @@ struct muonSelfIdConverter1 { void process(aod::EMGlobalMuonSelfIds_000 const& muons) { + muon_001.reserve(muons.size()); for (const auto& muon : muons) { muon_001(std::vector{}, muon.globalMuonsWithSameMFTIds()); } // end of muon loop diff --git a/PWGEM/Dilepton/TableProducer/Converters/qvec2Converter0.cxx b/PWGEM/Dilepton/TableProducer/Converters/qvec2Converter0.cxx index 340b74077c9..edf53c43f75 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/qvec2Converter0.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/qvec2Converter0.cxx @@ -31,6 +31,7 @@ struct qvec2Converter0 { void process(aod::EMEventsQvec_001 const& collisions) { + qvec2_000.reserve(collisions.size()); for (const auto& collision : collisions) { qvec2_000( collision.q2xft0m(), collision.q2yft0m(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/qvec3Converter0.cxx b/PWGEM/Dilepton/TableProducer/Converters/qvec3Converter0.cxx index 2775c7275f7..27b7b1bf038 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/qvec3Converter0.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/qvec3Converter0.cxx @@ -31,6 +31,7 @@ struct qvec3Converter0 { void process(aod::EMEventsQvec_001 const& collisions) { + qvec3_000.reserve(collisions.size()); for (const auto& collision : collisions) { qvec3_000( collision.q3xft0m(), collision.q3yft0m(), diff --git a/PWGEM/Dilepton/TableProducer/Converters/qvecConverter.cxx b/PWGEM/Dilepton/TableProducer/Converters/qvecConverter.cxx index d0164dac5ab..ca80548669b 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/qvecConverter.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/qvecConverter.cxx @@ -29,6 +29,7 @@ struct QvecConverter { void process(aod::EMEventsQvec_000 const& emEventsQVec) { + qvec001.reserve(emEventsQVec.size()); for (const auto& qvec : emEventsQVec) { constexpr float EmptyV0 = -999.f; qvec001(qvec.q2xft0m(), qvec.q2yft0m(), qvec.q2xft0a(), qvec.q2yft0a(), qvec.q2xft0c(), qvec.q2yft0c(), EmptyV0, EmptyV0, qvec.q2xbpos(), qvec.q2ybpos(), qvec.q2xbneg(), qvec.q2ybneg(), qvec.q2xbtot(), qvec.q2ybtot(), qvec.q3xft0m(), qvec.q3yft0m(), qvec.q3xft0a(), qvec.q3yft0a(), qvec.q3xft0c(), qvec.q3yft0c(), EmptyV0, EmptyV0, qvec.q3xbpos(), qvec.q3ybpos(), qvec.q3xbneg(), qvec.q3ybneg(), qvec.q3xbtot(), qvec.q3ybtot()); diff --git a/PWGEM/Dilepton/TableProducer/Converters/trackConverter1.cxx b/PWGEM/Dilepton/TableProducer/Converters/trackConverter1.cxx index bcdce816813..061affca561 100644 --- a/PWGEM/Dilepton/TableProducer/Converters/trackConverter1.cxx +++ b/PWGEM/Dilepton/TableProducer/Converters/trackConverter1.cxx @@ -31,6 +31,7 @@ struct trackConverter1 { void process(aod::EMPrimaryTracks_000 const& tracks) { + track_001.reserve(tracks.size()); for (const auto& track : tracks) { track_001( // track.collisionId(), diff --git a/PWGEM/Dilepton/TableProducer/associateMCinfoDilepton.cxx b/PWGEM/Dilepton/TableProducer/associateMCinfoDilepton.cxx index 2e95f21f5c2..7ba4bf6d750 100644 --- a/PWGEM/Dilepton/TableProducer/associateMCinfoDilepton.cxx +++ b/PWGEM/Dilepton/TableProducer/associateMCinfoDilepton.cxx @@ -59,7 +59,6 @@ struct AssociateMCInfoDilepton { Produces emprimaryelectronmclabels; Produces emprimarymuonmclabels; Produces emmftmclabels; - Produces emdummydata; Configurable n_dummy_loop{"n_dummy_loop", 0, "for loop runs over n times"}; Configurable down_scaling_omega{"down_scaling_omega", 1.0, "down scaling factor to store omega"}; @@ -686,25 +685,11 @@ struct AssociateMCInfoDilepton { skimmingMC(collisions, bcs, mccollisions, mcTracks, o2tracks, o2fwdtracks, o2mfttracks, nullptr, nullptr, emprimaryelectrons, emprimarymuons); } - void processGenDummy(MyCollisionsMC const&) - { - for (int i = 0; i < n_dummy_loop; i++) { - emdummydata( - 0.f, 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f, 0.f, - 0.f, 0.f, 0.f, 0.f, 0.f, - 0.f); - } - } - void processDummy(MyCollisionsMC const&) {} PROCESS_SWITCH(AssociateMCInfoDilepton, processMC_Electron, "create em mc event table for Electron", false); PROCESS_SWITCH(AssociateMCInfoDilepton, processMC_FwdMuon, "create em mc event table for Forward Muon", false); PROCESS_SWITCH(AssociateMCInfoDilepton, processMC_Electron_FwdMuon, "create em mc event table for Electron, FwdMuon", false); - PROCESS_SWITCH(AssociateMCInfoDilepton, processGenDummy, "produce dummy data", false); PROCESS_SWITCH(AssociateMCInfoDilepton, processDummy, "processDummy", true); }; diff --git a/PWGEM/Dilepton/TableProducer/createEMEventDilepton.cxx b/PWGEM/Dilepton/TableProducer/createEMEventDilepton.cxx index f268cdb066f..23b90b26725 100644 --- a/PWGEM/Dilepton/TableProducer/createEMEventDilepton.cxx +++ b/PWGEM/Dilepton/TableProducer/createEMEventDilepton.cxx @@ -14,6 +14,7 @@ /// \author Daiki Sekihata, daiki.sekihata@cern.ch #include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGLF/DataModel/SPCalibrationTables.h" // for ZDC information #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" @@ -43,43 +44,52 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; -using MyBCs = soa::Join; -// using MyMults = soa::Join; -using MyCents = soa::Join; // centrality table has dependency on multiplicity table. -using MyQvectors = soa::Join; +struct CreateEMEventDilepton { + using MyBCs = soa::Join; + // using MyMults = soa::Join; + using MyCents = soa::Join; // centrality table has dependency on multiplicity table. + using MyQvectors = soa::Join; -using MyCollisions = soa::Join; -using MyCollisions_Cent = soa::Join; -using MyCollisions_Cent_Qvec = soa::Join; + using MyCollisions = soa::Join; + using MyCollisions_Cent = soa::Join; + using MyCollisions_Cent_Qvec = soa::Join; + using MyCollisions_Cent_ZDC = soa::Join; -using MyCollisionsWithSWT = soa::Join; -using MyCollisionsWithSWT_Cent = soa::Join; -using MyCollisionsWithSWT_Cent_Qvec = soa::Join; + using MyCollisionsWithSWT = soa::Join; + using MyCollisionsWithSWT_Cent = soa::Join; + using MyCollisionsWithSWT_Cent_Qvec = soa::Join; -using MyCollisionsMC = soa::Join; -using MyCollisionsMC_Cent = soa::Join; -using MyCollisionsMC_Cent_Qvec = soa::Join; + using MyCollisionsMC = soa::Join; + using MyCollisionsMC_Cent = soa::Join; + using MyCollisionsMC_Cent_Qvec = soa::Join; -struct CreateEMEventDilepton { Produces embc; Produces event; Produces eventXY; - // Produces eventcov; + Produces eventcov; Produces event_mult; Produces event_cent; // Produces event_qvec; Produces event_qvec2; Produces event_qvec3; + Produces event_zdc; Produces event_norm_info; + Produces emswtbit; + // Produces emswtinfo; + Produces emswtATcounter; + Produces emswtATOIcounter; + enum class EMEventType : int { kEvent = 0, kEvent_Cent = 1, kEvent_Cent_Qvec = 2, + kEvent_Cent_ZDC = 3, }; // // CCDB options // Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable applyMBtoNormTable{"applyMBtoNormTable", false, "flag to apply MB to norm information"}; // this is to reduce derived data size. HistogramRegistry registry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; void init(o2::framework::InitContext&) @@ -102,7 +112,6 @@ struct CreateEMEventDilepton { ~CreateEMEventDilepton() {} - int mRunNumber{0}; // Service ccdb; template @@ -111,7 +120,6 @@ struct CreateEMEventDilepton { for (const auto& bc : bcs) { if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { embc(o2::aod::emevsel::reduceSelectionBit(bc), bc.rct_raw()); // TVX is fired. - // embc(bc.selection_raw(), bc.rct_raw()); // TVX is fired. } } // end of bc loop @@ -126,6 +134,16 @@ struct CreateEMEventDilepton { // auto bc = collision.template foundBC_as(); auto bc = collision.template bc_as(); // use this for Zorro + if (applyMBtoNormTable && !collision.isSelected()) { // minimal cut for MB + continue; + } + + if constexpr (eventtype == EMEventType::kEvent_Cent_ZDC) { + if (!collision.triggereventsp()) { + continue; // dummy comment + } + } + if (collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { int8_t posZint8 = static_cast(collision.posZ() * 2.f); if (posZint8 == 0) { @@ -137,7 +155,7 @@ struct CreateEMEventDilepton { } if constexpr (eventtype == EMEventType::kEvent) { event_norm_info(o2::aod::emevsel::reduceSelectionBit(collision), collision.rct_raw(), posZint8, static_cast(105.f + 110.f), static_cast(105.f + 110.f), static_cast(105.f + 110.f) /*, static_cast(105.f + 110.f)*/); - } else if constexpr (eventtype == EMEventType::kEvent_Cent || eventtype == EMEventType::kEvent_Cent_Qvec) { + } else if constexpr (eventtype == EMEventType::kEvent_Cent || eventtype == EMEventType::kEvent_Cent_Qvec || eventtype == EMEventType::kEvent_Cent_ZDC) { uint8_t centFT0Muint8 = collision.centFT0M() < 1.f ? static_cast(collision.centFT0M() * 100.f) : static_cast(collision.centFT0M() + 110.f); uint8_t centFT0Cuint8 = collision.centFT0C() < 1.f ? static_cast(collision.centFT0C() * 100.f) : static_cast(collision.centFT0C() + 110.f); uint8_t centNTPVuint8 = collision.centNTPV() < 1.f ? static_cast(collision.centNTPV() * 100.f) : static_cast(collision.centNTPV() + 110.f); @@ -157,8 +175,10 @@ struct CreateEMEventDilepton { } if constexpr (isTriggerAnalysis) { - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; + } else { + emswtbit(collision.triggerMask_raw()); } } @@ -170,7 +190,7 @@ struct CreateEMEventDilepton { eventXY(collision.posX(), collision.posY()); - // eventcov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); + eventcov(collision.covXX(), collision.covXY(), collision.covXZ(), collision.covYY(), collision.covYZ(), collision.covZZ(), collision.chi2()); event_mult(collision.multFT0A(), collision.multFT0C(), collision.multNTracksPV() /*, collision.multNTracksGlobal()*/); @@ -191,9 +211,6 @@ struct CreateEMEventDilepton { event_cent(collision.centFT0M(), collision.centFT0A(), collision.centFT0C(), collision.centNTPV() /*, collision.centNGlobal()*/); event_qvec2(999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); event_qvec3(999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); - // event_qvec( - // 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, - // 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); } else if constexpr (eventtype == EMEventType::kEvent_Cent_Qvec) { registry.fill(HIST("hCentFT0M"), collision.centFT0M()); registry.fill(HIST("hCentFT0A"), collision.centFT0A()); @@ -214,14 +231,21 @@ struct CreateEMEventDilepton { q2xft0m = collision.qvecFT0MReVec()[0], q2xft0a = collision.qvecFT0AReVec()[0], q2xft0c = collision.qvecFT0CReVec()[0], q2xfv0a = collision.qvecFV0AReVec()[0], q2xbpos = collision.qvecBPosReVec()[0], q2xbneg = collision.qvecBNegReVec()[0], q2xbtot = collision.qvecBTotReVec()[0]; q2yft0m = collision.qvecFT0MImVec()[0], q2yft0a = collision.qvecFT0AImVec()[0], q2yft0c = collision.qvecFT0CImVec()[0], q2yfv0a = collision.qvecFV0AImVec()[0], q2ybpos = collision.qvecBPosImVec()[0], q2ybneg = collision.qvecBNegImVec()[0], q2ybtot = collision.qvecBTotImVec()[0]; } - // event_qvec(q2xft0m, q2yft0m, q2xft0a, q2yft0a, q2xft0c, q2yft0c, q2xfv0a, q2yfv0a, q2xbpos, q2ybpos, q2xbneg, q2ybneg, q2xbtot, q2ybtot, - // q3xft0m, q3yft0m, q3xft0a, q3yft0a, q3xft0c, q3yft0c, q3xfv0a, q3yfv0a, q3xbpos, q3ybpos, q3xbneg, q3ybneg, q3xbtot, q3ybtot); event_qvec2(q2xft0m, q2yft0m, q2xft0a, q2yft0a, q2xft0c, q2yft0c, q2xfv0a, q2yfv0a, q2xbpos, q2ybpos, q2xbneg, q2ybneg, q2xbtot, q2ybtot); event_qvec3(q3xft0m, q3yft0m, q3xft0a, q3yft0a, q3xft0c, q3yft0c, q3xfv0a, q3yfv0a, q3xbpos, q3ybpos, q3xbneg, q3ybneg, q3xbtot, q3ybtot); + } else if constexpr (eventtype == EMEventType::kEvent_Cent_ZDC) { + registry.fill(HIST("hCentFT0M"), collision.centFT0M()); + registry.fill(HIST("hCentFT0A"), collision.centFT0A()); + registry.fill(HIST("hCentFT0C"), collision.centFT0C()); + registry.fill(HIST("hCentNTPV"), collision.centNTPV()); + registry.fill(HIST("hCentNGlobal"), collision.centFT0M()); + + event_cent(collision.centFT0M(), collision.centFT0A(), collision.centFT0C(), collision.centNTPV() /*, collision.centNGlobal()*/); + event_qvec2(999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); + event_qvec3(999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); + event_zdc(collision.qxZDCA(), collision.qyZDCA(), collision.qxZDCC(), collision.qyZDCC()); } else { event_cent(105.f, 105.f, 105.f, 105.f); - // event_qvec( 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, - // 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); event_qvec2(999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); event_qvec3(999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f, 999.f); } @@ -248,23 +272,50 @@ struct CreateEMEventDilepton { } PROCESS_SWITCH(CreateEMEventDilepton, processEvent_Cent_Qvec, "process event info", false); + void processEvent_Cent_ZDC(MyCollisions_Cent_ZDC const& collisions, MyBCs const& bcs) + { + skimEvent(collisions, bcs); + } + PROCESS_SWITCH(CreateEMEventDilepton, processEvent_Cent_ZDC, "process event info with ZDC", false); + //---------- for data with swt ---------- - void processEvent_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs) + void processEvent_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs, o2::aod::EMSWTriggerATCountersTMP const& emswtATcounterstmp, o2::aod::EMSWTriggerATOICountersTMP const& emswtATOIcounterstmp) { skimEvent(collisions, bcs); + + for (const auto& counter : emswtATcounterstmp) { + emswtATcounter(counter.isAT_raw()); + } + for (const auto& counter : emswtATOIcounterstmp) { + emswtATOIcounter(counter.isAToI_raw()); + } } PROCESS_SWITCH(CreateEMEventDilepton, processEvent_SWT, "process event info", false); - void processEvent_SWT_Cent(MyCollisionsWithSWT_Cent const& collisions, MyBCs const& bcs) + void processEvent_SWT_Cent(MyCollisionsWithSWT_Cent const& collisions, MyBCs const& bcs, o2::aod::EMSWTriggerATCountersTMP const& emswtATcounterstmp, o2::aod::EMSWTriggerATOICountersTMP const& emswtATOIcounterstmp) { skimEvent(collisions, bcs); + + for (const auto& counter : emswtATcounterstmp) { + emswtATcounter(counter.isAT_raw()); + } + for (const auto& counter : emswtATOIcounterstmp) { + emswtATOIcounter(counter.isAToI_raw()); + } } PROCESS_SWITCH(CreateEMEventDilepton, processEvent_SWT_Cent, "process event info", false); - void processEvent_SWT_Cent_Qvec(MyCollisionsWithSWT_Cent_Qvec const& collisions, MyBCs const& bcs) + void processEvent_SWT_Cent_Qvec(MyCollisionsWithSWT_Cent_Qvec const& collisions, MyBCs const& bcs, o2::aod::EMSWTriggerATCountersTMP const& emswtATcounterstmp, o2::aod::EMSWTriggerATOICountersTMP const& emswtATOIcounterstmp) { skimEvent(collisions, bcs); + + for (const auto& counter : emswtATcounterstmp) { + emswtATcounter(counter.isAT_raw()); + } + for (const auto& counter : emswtATOIcounterstmp) { + emswtATOIcounter(counter.isAToI_raw()); + } } PROCESS_SWITCH(CreateEMEventDilepton, processEvent_SWT_Cent_Qvec, "process event info", false); @@ -312,6 +363,7 @@ struct AssociateDileptonToEMEvent { // LOGF(info, "collision.collisionId() = %d , nl = %d", collision.collisionId(), nl); for (int il = 0; il < nl; il++) { eventIds(collision.globalIndex()); + mCounter++; } // end of photon loop } // end of collision loop } @@ -319,6 +371,8 @@ struct AssociateDileptonToEMEvent { // This struct is for both data and MC. // Note that reconstructed collisions without mc collisions are already rejected in CreateEMEventDilepton in MC. + int mCounter{0}; + void processElectron(aod::EMEvents const& collisions, aod::EMPrimaryElectrons const& tracks) { fillEventId(collisions, tracks, prmeleventid, perCollision_el); @@ -326,7 +380,11 @@ struct AssociateDileptonToEMEvent { void processFwdMuon(aod::EMEvents const& collisions, aod::EMPrimaryMuons const& tracks) { + mCounter = 0; fillEventId(collisions, tracks, prmmueventid, perCollision_mu); + if (mCounter != tracks.size()) { + LOGF(fatal, "different table size is detected. mCounter = %d, muons.size() = %d", mCounter, tracks.size()); // this should never happen. + } } void processChargedTrack(aod::EMEvents const& collisions, soa::Join const& tracks) diff --git a/PWGEM/Dilepton/TableProducer/eventSelection.cxx b/PWGEM/Dilepton/TableProducer/eventSelection.cxx index 7c857fd6743..5b423d1d054 100644 --- a/PWGEM/Dilepton/TableProducer/eventSelection.cxx +++ b/PWGEM/Dilepton/TableProducer/eventSelection.cxx @@ -60,7 +60,6 @@ struct EMEventSelection { Configurable cfgZvtxMin{"cfgZvtxMin", -1e+10, "min. Zvtx"}; Configurable cfgZvtxMax{"cfgZvtxMax", 1e+10, "max. Zvtx"}; - Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; Configurable cfgRequireFT0AND{"cfgRequireFT0AND", false, "require FT0AND in event cut"}; Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; diff --git a/PWGEM/Dilepton/TableProducer/filterEoI.cxx b/PWGEM/Dilepton/TableProducer/filterEoI.cxx index 31bf18c40a3..217706c4d97 100644 --- a/PWGEM/Dilepton/TableProducer/filterEoI.cxx +++ b/PWGEM/Dilepton/TableProducer/filterEoI.cxx @@ -31,6 +31,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; @@ -49,12 +50,15 @@ struct filterEoI { Configurable inheritFromOtherTask{"inheritFromOtherTask", true, "Flag to iherit all common configurables from skimmerPrimaryElectron or skimmerPrimaryMuon"}; Configurable minNelectron{"minNelectron", -1, "min number of electron candidates per collision"}; Configurable minNmuon{"minNmuon", -1, "min number of muon candidates per collision"}; + Configurable minNgamma{"minNgamma", 1, "min number of V0-photon candidates per collision"}; + Configurable taskNameForNelectron{"taskNameForNelectron", "skimmer-primary-electron", "task name where minNelectron is defined."}; + Configurable varNameForNelectron{"varNameForNelectron", "minNelectron", "variable name for minNelectron"}; HistogramRegistry fRegistry{"output"}; void init(o2::framework::InitContext& initContext) { if (inheritFromOtherTask.value) { // Inheriting from other task - getTaskOptionValue(initContext, "skimmer-primary-electron", "minNelectron", minNelectron.value, true); + getTaskOptionValue(initContext, taskNameForNelectron.value, varNameForNelectron.value, minNelectron.value, true); getTaskOptionValue(initContext, "skimmer-primary-muon", "minNmuon", minNmuon.value, true); } @@ -104,7 +108,7 @@ struct filterEoI { } if constexpr (static_cast(system & kPCM)) { auto v0s_coll = v0s.sliceBy(perCollision_v0, collision.globalIndex()); - if (v0s_coll.size() >= 1) { + if (v0s_coll.size() >= minNgamma) { does_pcm_exist = true; fRegistry.fill(HIST("hEventCounter"), 4); } @@ -129,7 +133,11 @@ struct filterEoI { fRegistry.fill(HIST("hEventCounter"), 8); } - emeoi(does_electron_exist || does_fwdmuon_exist || does_pcm_exist || does_electronda_exist); + if constexpr (static_cast(system & kPCM) && static_cast(system & kElectronFromDalitz)) { + emeoi(does_pcm_exist && does_electronda_exist); + } else { + emeoi(does_electron_exist || does_fwdmuon_exist || does_pcm_exist || does_electronda_exist); + } } // end of collision loop @@ -165,6 +173,12 @@ struct filterEoI { selectEoI(collisions, electrons, muons, v0s, nullptr); } + void process_ElectronFromDalitz(aod::Collisions const& collisions, aod::EMPrimaryElectronsFromDalitz const& electronsda) + { + const uint8_t sysflag = kElectronFromDalitz; + selectEoI(collisions, nullptr, nullptr, nullptr, electronsda); + } + void process_PCM_ElectronFromDalitz(aod::Collisions const& collisions, aod::V0PhotonsKF const& v0s, aod::EMPrimaryElectronsFromDalitz const& electronsda) { const uint8_t sysflag = kPCM | kElectronFromDalitz; @@ -183,6 +197,7 @@ struct filterEoI { PROCESS_SWITCH(filterEoI, process_PCM, "create filter bit for PCM", false); PROCESS_SWITCH(filterEoI, process_Electron_FwdMuon, "create filter bit for Electron, FwdMuon", false); PROCESS_SWITCH(filterEoI, process_Electron_FwdMuon_PCM, "create filter bit for Electron, FwdMuon, PCM", false); + PROCESS_SWITCH(filterEoI, process_ElectronFromDalitz, "create filter bit for ElectronFromDalitz", false); PROCESS_SWITCH(filterEoI, process_PCM_ElectronFromDalitz, "create filter bit for PCM, ElectronFromDalitz", false); PROCESS_SWITCH(filterEoI, processDummy, "processDummy", true); }; diff --git a/PWGEM/Dilepton/Tasks/prefilterDielectron.cxx b/PWGEM/Dilepton/TableProducer/prefilterDielectron.cxx similarity index 87% rename from PWGEM/Dilepton/Tasks/prefilterDielectron.cxx rename to PWGEM/Dilepton/TableProducer/prefilterDielectron.cxx index b35ef08c80f..8460b0caf5c 100644 --- a/PWGEM/Dilepton/Tasks/prefilterDielectron.cxx +++ b/PWGEM/Dilepton/TableProducer/prefilterDielectron.cxx @@ -19,11 +19,11 @@ #include "PWGEM/Dilepton/DataModel/dileptonTables.h" #include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include -#include #include #include #include @@ -40,11 +40,12 @@ #include #include #include -#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include + #include #include #include @@ -67,7 +68,7 @@ struct prefilterDielectron { using MyCollisions = soa::Join; using MyCollision = MyCollisions::iterator; - using MyTracks = soa::Join; + using MyTracks = soa::Join; using MyTrack = MyTracks::iterator; Produces pfb_derived; @@ -76,8 +77,6 @@ struct prefilterDielectron { Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; - Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; @@ -90,7 +89,7 @@ struct prefilterDielectron { Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; - Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border in event cut"}; Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; @@ -109,19 +108,20 @@ struct prefilterDielectron { Configurable cfg_max_mass{"cfg_max_mass", 0.0, "max mass for prefilter ULS"}; // region to be rejected // for phiv prefilter - Configurable cfg_apply_phiv{"cfg_apply_phiv", false, "flag to apply phiv cut"}; // region to be rejected + Configurable cfg_apply_phiv{"cfg_apply_phiv", false, "flag to apply phiv cut for ULS"}; // region to be rejected Configurable cfg_phiv_slope{"cfg_phiv_slope", 0.0185, "slope for m vs. phiv"}; // region to be rejected Configurable cfg_phiv_intercept{"cfg_phiv_intercept", -0.0280, "intercept for m vs. phiv"}; // region to be rejected Configurable cfg_min_phiv{"cfg_min_phiv", -1.f, "min phiv"}; // region to be rejected Configurable cfg_max_phiv{"cfg_max_phiv", 3.2, "max phiv"}; // region to be rejected // for deta-dphi prefilter - Configurable cfg_apply_detadphi_uls{"cfg_apply_detadphi_uls", false, "flag to apply generator deta-dphi elliptic cut in ULS"}; // region to be rejected - Configurable cfg_apply_detadphi_ls{"cfg_apply_detadphi_ls", false, "flag to apply generator deta-dphi elliptic cut in LS"}; // region to be rejected - Configurable cfg_min_deta_ls{"cfg_min_deta_ls", 0.04, "deta between 2 electrons (elliptic cut)"}; // region to be rejected - Configurable cfg_min_dphi_ls{"cfg_min_dphi_ls", 0.2, "dphi between 2 electrons (elliptic cut)"}; // region to be rejected - Configurable cfg_min_deta_uls{"cfg_min_deta_uls", 0.04, "deta between 2 electrons (elliptic cut)"}; // region to be rejected - Configurable cfg_min_dphi_uls{"cfg_min_dphi_uls", 0.2, "dphi between 2 electrons (elliptic cut)"}; // region to be rejected + Configurable cfg_apply_detadphi_uls{"cfg_apply_detadphi_uls", false, "flag to apply deta-dphi elliptic cut in ULS"}; // region to be rejected + Configurable cfg_apply_detadphi_ls{"cfg_apply_detadphi_ls", false, "flag to apply deta-dphi elliptic cut in LS"}; // region to be rejected + Configurable cfg_min_deta_ls{"cfg_min_deta_ls", 0.04, "deta between 2 electrons (elliptic cut)"}; // region to be rejected + Configurable cfg_min_dphi_ls{"cfg_min_dphi_ls", 0.2, "dphi between 2 electrons (elliptic cut)"}; // region to be rejected + Configurable cfg_min_deta_uls{"cfg_min_deta_uls", 0.04, "deta between 2 electrons (elliptic cut)"}; // region to be rejected + Configurable cfg_min_dphi_uls{"cfg_min_dphi_uls", 0.2, "dphi between 2 electrons (elliptic cut)"}; // region to be rejected + Configurable cfgRefR{"cfgRefR", 0.5, "reference R (in m) for extrapolation"}; // https://cds.cern.ch/record/1419204 Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.15, "min pT for single track"}; Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; @@ -138,13 +138,14 @@ struct prefilterDielectron { Configurable cfg_max_chi2tof{"cfg_max_chi2tof", 1e+10, "max chi2 TOF"}; Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.f, "max dca XY for single track in cm"}; Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.f, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_max_dca_sigma{"cfg_max_dca_sigma", 1e+10, "max dca for single track in sigma"}; Configurable cfg_require_itsib_any{"cfg_require_itsib_any", true, "flag to require ITS ib any hits"}; Configurable cfg_require_itsib_1st{"cfg_require_itsib_1st", false, "flag to require ITS ib 1st hit"}; Configurable cfg_min_its_cluster_size{"cfg_min_its_cluster_size", 0.f, "min ITS cluster size"}; Configurable cfg_max_its_cluster_size{"cfg_max_its_cluster_size", 16.f, "max ITS cluster size"}; // Configurable cfg_min_rel_diff_pin{"cfg_min_rel_diff_pin", -1e+10, "min rel. diff. between pin and ppv"}; // Configurable cfg_max_rel_diff_pin{"cfg_max_rel_diff_pin", +1e+10, "max rel. diff. between pin and ppv"}; - // Configurable cfgRefR{"cfgRefR", 1.2, "reference R (in m) for extrapolation"}; // https://cds.cern.ch/record/1419204 + o2::framework::Configurable cfgDCAType{"cfgDCAType", 0, "type of DCA for output. 0:3D, 1:XY, 2:Z, else:3D"}; Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif : 4, kPIDML : 5, kTPChadrejORTOFreq_woTOFif : 6]"}; Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; @@ -160,25 +161,24 @@ struct prefilterDielectron { Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3.0, "min. TOF n sigma for electron inclusion"}; Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3.0, "max. TOF n sigma for electron inclusion"}; Configurable enableTTCA{"enableTTCA", true, "Flag to enable or disable TTCA"}; - Configurable includeITSsa{"includeITSsa", false, "Flag to enable ITSsa tracks"}; - Configurable cfg_max_pt_track_ITSsa{"cfg_max_pt_track_ITSsa", 0.15, "max pt for ITSsa tracks"}; + // Configurable includeITSsa{"includeITSsa", false, "Flag to enable ITSsa tracks"}; + // Configurable cfg_max_pt_track_ITSsa{"cfg_max_pt_track_ITSsa", 0.15, "max pt for ITSsa tracks"}; // configuration for PID ML - Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; - Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; - Configurable> binsMl{"binsMl", std::vector{-999999., 999999.}, "Bin limits for ML application"}; - Configurable> cutsMl{"cutsMl", std::vector{0.95}, "ML cuts per bin"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; - Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; - Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + // Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + // Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + Configurable> binsMLPID{"binsMLPID", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20.f}, "Bin limits for ML application"}; + Configurable> cutsMLPID{"cutsMLPID", std::vector{0.97, 0.97, 0.97, 0.8, 0.95, 0.95, 0.8, 0.8, 0.8}, "ML cuts per bin"}; + // Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; + // Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; + // Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + // Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; } dielectroncuts; - o2::ccdb::CcdbApi ccdbApi; Service ccdb; - int mRunNumber; - float d_bz; + int mRunNumber{0}; + float d_bz{0}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; @@ -205,23 +205,9 @@ struct prefilterDielectron { return; } - // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { - d_bz = d_bz_input; - o2::parameters::GRPMagField grpmag; - if (fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); - } - o2::base::Propagator::initFieldFromGRP(&grpmag); - mRunNumber = collision.runNumber(); - return; - } - auto run3grp_timestamp = collision.timestamp(); o2::parameters::GRPObject* grpo = 0x0; o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); // Fetch magnetic field from ccdb for current collision @@ -279,10 +265,8 @@ struct prefilterDielectron { fDielectronCut.SetPairYRange(-1e+10, +1e+10); fDielectronCut.SetPairDCARange(0.f, 1e+10); // in sigma fDielectronCut.ApplyPhiV(false); - fDielectronCut.ApplyPrefilter(false); fDielectronCut.SetMindEtadPhi(false, false, 1.f, 1.f); fDielectronCut.SetPairOpAng(0.f, 3.2f); - fDielectronCut.SetRequireDifferentSides(false); // for track fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); @@ -296,13 +280,14 @@ struct prefilterDielectron { fDielectronCut.SetChi2PerClusterITS(0.0, dielectroncuts.cfg_max_chi2its); fDielectronCut.SetNClustersITS(dielectroncuts.cfg_min_ncluster_its, 7); fDielectronCut.SetMeanClusterSizeITS(dielectroncuts.cfg_min_its_cluster_size, dielectroncuts.cfg_max_its_cluster_size); + fDielectronCut.SetTrackMaxDcaSigma(dielectroncuts.cfg_max_dca_sigma, dielectroncuts.cfgDCAType); fDielectronCut.SetTrackMaxDcaXY(dielectroncuts.cfg_max_dcaxy); fDielectronCut.SetTrackMaxDcaZ(dielectroncuts.cfg_max_dcaz); fDielectronCut.RequireITSibAny(dielectroncuts.cfg_require_itsib_any); fDielectronCut.RequireITSib1st(dielectroncuts.cfg_require_itsib_1st); fDielectronCut.SetChi2TOF(0, dielectroncuts.cfg_max_chi2tof); // fDielectronCut.SetRelDiffPin(dielectroncuts.cfg_min_rel_diff_pin, dielectroncuts.cfg_max_rel_diff_pin); - fDielectronCut.IncludeITSsa(dielectroncuts.includeITSsa, dielectroncuts.cfg_max_pt_track_ITSsa); + // fDielectronCut.IncludeITSsa(dielectroncuts.includeITSsa, dielectroncuts.cfg_max_pt_track_ITSsa); fDielectronCut.EnableTTCA(dielectroncuts.enableTTCA); // for eID @@ -316,14 +301,14 @@ struct prefilterDielectron { if (dielectroncuts.cfg_pid_scheme == static_cast(DielectronCut::PIDSchemes::kPIDML)) { // please call this at the end of DefineDileptonCut std::vector binsML{}; - binsML.reserve(dielectroncuts.binsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.binsMl.value.size(); i++) { - binsML.emplace_back(dielectroncuts.binsMl.value[i]); + binsML.reserve(dielectroncuts.binsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.binsMLPID.value.size(); i++) { + binsML.emplace_back(dielectroncuts.binsMLPID.value[i]); } std::vector thresholdsML{}; - thresholdsML.reserve(dielectroncuts.cutsMl.value.size()); - for (size_t i = 0; i < dielectroncuts.cutsMl.value.size(); i++) { - thresholdsML.emplace_back(dielectroncuts.cutsMl.value[i]); + thresholdsML.reserve(dielectroncuts.cutsMLPID.value.size()); + for (size_t i = 0; i < dielectroncuts.cutsMLPID.value.size(); i++) { + thresholdsML.emplace_back(dielectroncuts.cutsMLPID.value[i]); } fDielectronCut.SetMLThresholds(binsML, thresholdsML); } // end of PID ML @@ -341,7 +326,6 @@ struct prefilterDielectron { Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; using FilteredMyCollisions = soa::Filtered; - int ndf = 0; void processPFB(FilteredMyCollisions const& collisions, MyTracks const& tracks) { for (const auto& track : tracks) { @@ -383,7 +367,7 @@ struct prefilterDielectron { float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), pos.sign(), ele.sign(), d_bz); float deta = pos.sign() * v1.Pt() > ele.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); float dphi = pos.sign() * v1.Pt() > ele.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi fRegistry.fill(HIST("Pair/before/uls/hMvsPhiV"), phiv, v12.M()); fRegistry.fill(HIST("Pair/before/uls/hMvsPt"), v12.M(), v12.Pt()); @@ -403,6 +387,7 @@ struct prefilterDielectron { map_pfb[pos.globalIndex()] |= 1 << static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS); map_pfb[ele.globalIndex()] |= 1 << static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackULS); } + } // end of ULS pairing for (const auto& [pos1, pos2] : combinations(CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ @@ -417,7 +402,7 @@ struct prefilterDielectron { float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), pos1.sign(), pos2.sign(), d_bz); float deta = pos1.sign() * v1.Pt() > pos2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); float dphi = pos1.sign() * v1.Pt() > pos2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi fRegistry.fill(HIST("Pair/before/lspp/hMvsPt"), v12.M(), v12.Pt()); fRegistry.fill(HIST("Pair/before/lspp/hMvsPhiV"), phiv, v12.M()); @@ -427,6 +412,7 @@ struct prefilterDielectron { map_pfb[pos1.globalIndex()] |= 1 << static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS); map_pfb[pos2.globalIndex()] |= 1 << static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS); } + } // end of LS++ pairing for (const auto& [ele1, ele2] : combinations(CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- @@ -441,7 +427,7 @@ struct prefilterDielectron { float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), ele1.sign(), ele2.sign(), d_bz); float deta = ele1.sign() * v1.Pt() > ele2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); float dphi = ele1.sign() * v1.Pt() > ele2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi fRegistry.fill(HIST("Pair/before/lsmm/hMvsPt"), v12.M(), v12.Pt()); fRegistry.fill(HIST("Pair/before/lsmm/hMvsPhiV"), phiv, v12.M()); @@ -451,8 +437,8 @@ struct prefilterDielectron { map_pfb[ele1.globalIndex()] |= 1 << static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS); map_pfb[ele2.globalIndex()] |= 1 << static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBitDerived::kSplitOrMergedTrackLS); } - } // end of LS-- pairing + } // end of LS-- pairing } // end of collision loop for (const auto& track : tracks) { @@ -488,7 +474,7 @@ struct prefilterDielectron { float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), pos.sign(), ele.sign(), d_bz); float deta = pos.sign() * v1.Pt() > ele.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); float dphi = pos.sign() * v1.Pt() > ele.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi fRegistry.fill(HIST("Pair/after/uls/hMvsPhiV"), phiv, v12.M()); fRegistry.fill(HIST("Pair/after/uls/hMvsPt"), v12.M(), v12.Pt()); @@ -509,7 +495,7 @@ struct prefilterDielectron { float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), pos1.sign(), pos2.sign(), d_bz); float deta = pos1.sign() * v1.Pt() > pos2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); float dphi = pos1.sign() * v1.Pt() > pos2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi fRegistry.fill(HIST("Pair/after/lspp/hMvsPt"), v12.M(), v12.Pt()); fRegistry.fill(HIST("Pair/after/lspp/hMvsPhiV"), phiv, v12.M()); @@ -530,7 +516,7 @@ struct prefilterDielectron { float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), ele1.sign(), ele2.sign(), d_bz); float deta = ele1.sign() * v1.Pt() > ele2.sign() * v2.Pt() ? v1.Eta() - v2.Eta() : v2.Eta() - v1.Eta(); float dphi = ele1.sign() * v1.Pt() > ele2.sign() * v2.Pt() ? v1.Phi() - v2.Phi() : v2.Phi() - v1.Phi(); - o2::math_utils::bringToPMPi(dphi); + dphi = RecoDecay::constrainAngle(dphi, -M_PI, 1U); // -pi - +pi fRegistry.fill(HIST("Pair/after/lsmm/hMvsPt"), v12.M(), v12.Pt()); fRegistry.fill(HIST("Pair/after/lsmm/hMvsPhiV"), phiv, v12.M()); @@ -538,7 +524,6 @@ struct prefilterDielectron { } } // end of collision loop - ndf++; map_pfb.clear(); } // end of process PROCESS_SWITCH(prefilterDielectron, processPFB, "produce prefilter bit", false); diff --git a/PWGEM/Dilepton/TableProducer/skimmerOTS.cxx b/PWGEM/Dilepton/TableProducer/skimmerOTS.cxx index 0dcca6318c1..19e42b98774 100644 --- a/PWGEM/Dilepton/TableProducer/skimmerOTS.cxx +++ b/PWGEM/Dilepton/TableProducer/skimmerOTS.cxx @@ -30,6 +30,8 @@ #include +#include + #include #include #include @@ -41,25 +43,24 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; -using MyCollisions = soa::Join; - struct skimmerOTS { + // Produces swtinfo_tmp; // Join aod::Collision later. Produces swtbit_tmp; + Produces swtcounterAT_tmp; + Produces swtcounterATOI_tmp; // CCDB options Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable cfg_swt_names{"cfg_swt_names", "fLMeeIMR,fLMeeHMR", "comma-separated software trigger names"}; // !trigger names have to be pre-registered in dileptonTable.h for bit operation! + Configurable cfg_swt_names{"cfg_swt_names", "fLMeeIMR,fLMeeHMR", "comma-separated software trigger names"}; o2::framework::Configurable ccdbPathSoftwareTrigger{"ccdbPathSoftwareTrigger", "EventFiltering/Zorro/", "ccdb path for ZORRO objects"}; Configurable bcMarginForSoftwareTrigger{"bcMarginForSoftwareTrigger", 100, "Number of BCs of margin for software triggers"}; std::vector swt_names; - int mRunNumber; + int mRunNumber{0}; Service ccdb; Zorro zorro; std::vector mTOIidx; uint64_t mNinspectedTVX{0}; - std::vector mScalers; - std::vector mSelections; std::vector mTOICounters; std::vector mATCounters; @@ -88,16 +89,11 @@ struct skimmerOTS { } const int ntrg = static_cast(o2::aod::pwgem::dilepton::swt::swtAliases::kNaliases); - mNinspectedTVX = 0; - mScalers.resize(ntrg); - mSelections.resize(ntrg); mTOICounters.resize(ntrg); mATCounters.resize(ntrg); for (int idx = 0; idx < ntrg; idx++) { mTOICounters[idx] = 0; mATCounters[idx] = 0; - mScalers[idx] = 0; - mSelections[idx] = 0; } } @@ -129,13 +125,15 @@ struct skimmerOTS { for (size_t idx = 0; idx < mTOIidx.size(); idx++) { auto swtname = swt_names[idx]; int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(swtname); - mScalers[emswtId] = zorro.getScalers()->GetBinContent(mTOIidx[idx] + 2); - mSelections[emswtId] = zorro.getSelections()->GetBinContent(mTOIidx[idx] + 2); - LOGF(info, "Trigger of Interest : index = %d in Zorro, %d in EM, scaler = %llu, selection = %llu", mTOIidx[idx], emswtId, mScalers[emswtId], mSelections[emswtId]); + uint64_t nScalers = zorro.getScalers()->GetBinContent(mTOIidx[idx] + 2); + uint64_t nSelections = zorro.getSelections()->GetBinContent(mTOIidx[idx] + 2); + LOGF(info, "Trigger of Interest : index = %d in Zorro, %d in EM, scaler = %llu, selection = %llu", mTOIidx[idx], emswtId, nScalers, nSelections); } mRunNumber = bc.runNumber(); } + using MyCollisions = soa::Join; + void process(MyCollisions const& collisions, aod::BCsWithTimestamps const&) { for (const auto& collision : collisions) { @@ -150,34 +148,35 @@ struct skimmerOTS { if (collision.isSelected()) { if (zorro.isSelected(bc.globalBC(), bcMarginForSoftwareTrigger)) { // triggered event - // auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected, or simply call zorro.fetch - // auto TOIcounters = zorro.getTOIcounters(); // this has to be called after zorro::isSelected, or simply call zorro.fetch - // auto ATcounters = zorro.getATcounters(); // this has to be called after zorro::isSelected, or simply call zorro.fetch - trigger_bitmap = 1; - - // // LOGF(info, "swt_bitset.to_string().c_str() = %s", swt_bitset.to_string().c_str()); - // for (size_t idx = 0; idx < mTOIidx.size(); idx++) { - // if (swt_bitset.test(mTOIidx[idx])) { - // auto swtname = swt_names[idx]; - // int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(swtname); - // trigger_bitmap |= BIT(emswtId); - // // LOGF(info, "swtname = %s is fired. swt index in original swt table = %d, swt index for EM table = %d", swtname.data(), mTOIidx[idx], o2::aod::pwgem::dilepton::swt::aliasLabels.at(swtname)); - // registry.fill(HIST("hCollisionCounter"), idx + 2); // fired trigger - - // // LOGF(info, "ATcounters[mTOIidx[idx]] = %d, TOIcounters[idx] = %d", ATcounters[mTOIidx[idx]], TOIcounters[idx]); - - // while (ATcounters[mTOIidx[idx]] > mATCounters[emswtId]) { - // mATCounters[emswtId]++; - // swtcounterAT_tmp(BIT(emswtId)); - // } - - // while (TOIcounters[idx] > mTOICounters[emswtId]) { - // mTOICounters[emswtId]++; // always incremented by 1 in zorro!! - // swtcounterTOI_tmp(BIT(emswtId)); - // } - // // LOGF(info, "collision.globalIndex() = %d, bc.globalBC() = %llu, mTOICounters[%d] = %d, mATcounters[%d] = %d", collision.globalIndex(), bc.globalBC(), emswtId, mTOICounters[emswtId], emswtId, mATCounters[emswtId]); - // } - // } // end of TOI loop + + auto swt_bitset = zorro.getLastResult(); // this has to be called after zorro::isSelected, or simply call zorro.fetch + auto TOIcounters = zorro.getTOIcounters(); // this has to be called after zorro::isSelected, or simply call zorro.fetch + auto ATcounters = zorro.getATcounters(); // this has to be called after zorro::isSelected, or simply call zorro.fetch + + // trigger_bitmap = 1; + // LOGF(info, "swt_bitset.to_string().c_str() = %s", swt_bitset.to_string().c_str()); + for (size_t idx = 0; idx < mTOIidx.size(); idx++) { + if (swt_bitset.test(mTOIidx[idx])) { + auto swtname = swt_names[idx]; + int emswtId = o2::aod::pwgem::dilepton::swt::aliasLabels.at(swtname); + trigger_bitmap |= BIT(emswtId); + // LOGF(info, "swtname = %s is fired. swt index in original swt table = %d, swt index for EM table = %d", swtname.data(), mTOIidx[idx], o2::aod::pwgem::dilepton::swt::aliasLabels.at(swtname)); + registry.fill(HIST("hCollisionCounter"), idx + 2); // fired trigger + + // LOGF(info, "ATcounters[mTOIidx[idx]] = %d, TOIcounters[idx] = %d", ATcounters[mTOIidx[idx]], TOIcounters[idx]); + + while (ATcounters[mTOIidx[idx]] > mATCounters[emswtId]) { + mATCounters[emswtId]++; + swtcounterAT_tmp(BIT(emswtId)); + } + + while (TOIcounters[idx] > mTOICounters[emswtId]) { + mTOICounters[emswtId]++; // always incremented by 1 in zorro!! + swtcounterATOI_tmp(BIT(emswtId)); + } + // LOGF(info, "collision.globalIndex() = %d, bc.globalBC() = %llu, mTOICounters[%d] = %d, mATcounters[%d] = %d", collision.globalIndex(), bc.globalBC(), emswtId, mTOICounters[emswtId], emswtId, mATCounters[emswtId]); + } + } // end of TOI loop } } swtbit_tmp(trigger_bitmap); diff --git a/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectron.cxx b/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectron.cxx index e9fe1653412..78a104995b3 100644 --- a/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectron.cxx +++ b/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectron.cxx @@ -16,6 +16,7 @@ #include "PWGEM/Dilepton/Utils/MlResponseO2Track.h" #include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "Common/Core/PID/PIDTOFParamService.h" #include "Common/Core/TableHelper.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/CollisionAssociationTables.h" @@ -45,7 +46,6 @@ #include #include #include -#include #include #include @@ -168,7 +168,7 @@ struct skimmerPrimaryElectron { Configurable> cutsMl{"cutsMl", std::vector{0.95, 0.95, 0.7, 0.7, 0.8, 0.8, 0.7, 0.7}, "ML cuts per bin"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"tpcInnerParam", "tpcNClsFound", "tpcChi2NCl", "tpcNSigmaEl", "tofNSigmaEl", "meanClusterSizeITSobCos"}, "Names of ML model input features"}; Configurable nameBinningFeature{"nameBinningFeature", "tpcInnerParam", "Names of ML model binning feature"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; @@ -237,32 +237,6 @@ struct skimmerPrimaryElectron { fRegistry.add("Track/hProbElBDT", "probability to be e from BDT;p_{in} (GeV/c);BDT score;", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); fRegistry.add("Track/hNe", "electron counts;N_{e} per collision", kTH1F, {{51, -0.5, 50.5}}, false); } - - if (usePIDML) { - static constexpr int nClassesMl = 2; - const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutSmaller}; - const std::vector labelsClasses = {"Background", "Signal"}; - const uint32_t nBinsMl = binsMl.value.size() - 1; - const std::vector labelsBins(nBinsMl, "bin"); - double cutsMlArr[nBinsMl][nClassesMl]; - for (uint32_t i = 0; i < nBinsMl; i++) { - cutsMlArr[i][0] = 0.0; - cutsMlArr[i][1] = cutsMl.value[i]; - } - o2::framework::LabeledArray cutsMl = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; - - mlResponseSingleTrack.configure(binsMl.value, cutsMl, cutDirMl, nClassesMl); - if (loadModelsFromCCDB) { - ccdbApi.init(ccdburl); - mlResponseSingleTrack.setModelPathsCCDB(onnxFileNames.value, ccdbApi, onnxPathsCCDB.value, timestampCCDB.value); - } else { - mlResponseSingleTrack.setModelPathsLocal(onnxFileNames.value); - } - mlResponseSingleTrack.cacheInputFeaturesIndices(namesInputFeatures); - mlResponseSingleTrack.cacheBinningIndex(nameBinningFeature); - mlResponseSingleTrack.init(enableOptimizations.value); - mlResponseSingleTrack.useReassociatedTOF(useTOFNSigmaDeltaBC.value); - } // end of PID ML } void initCCDB(aod::BCsWithTimestamps::iterator const& bc) @@ -319,6 +293,33 @@ struct skimmerPrimaryElectron { d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; } + + if (usePIDML) { + static constexpr int nClassesMl = 2; + const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutSmaller}; + const std::vector labelsClasses = {"Background", "Signal"}; + const uint32_t nBinsMl = binsMl.value.size() - 1; + const std::vector labelsBins(nBinsMl, "bin"); + double cutsMlArr[nBinsMl][nClassesMl]; + for (uint32_t i = 0; i < nBinsMl; i++) { + cutsMlArr[i][0] = 0.0; + cutsMlArr[i][1] = cutsMl.value[i]; + } + o2::framework::LabeledArray cutsMl = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; + + mlResponseSingleTrack.configure(binsMl.value, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdburl); + mlResponseSingleTrack.setModelPathsCCDB(onnxFileNames.value, ccdbApi, onnxPathsCCDB.value, bc.timestamp()); + } else { + mlResponseSingleTrack.setModelPathsLocal(onnxFileNames.value); + } + mlResponseSingleTrack.cacheInputFeaturesIndices(namesInputFeatures); + mlResponseSingleTrack.cacheBinningIndex(nameBinningFeature); + mlResponseSingleTrack.init(enableOptimizations.value); + mlResponseSingleTrack.useReassociatedTOF(useTOFNSigmaDeltaBC.value); + } // end of PID ML + mRunNumber = bc.runNumber(); } @@ -911,7 +912,7 @@ struct skimmerPrimaryElectron { continue; } - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; } @@ -975,7 +976,7 @@ struct skimmerPrimaryElectron { if (!collision.isSelected()) { continue; } - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; } diff --git a/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectronQC.cxx b/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectronQC.cxx index 3815e0bc335..c3a4d6ca601 100644 --- a/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectronQC.cxx +++ b/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectronQC.cxx @@ -126,10 +126,18 @@ struct skimmerPrimaryElectronQC { Configurable maxchi2its{"maxchi2its", 5.0, "max. chi2/NclsITS"}; Configurable dca_xy_max{"dca_xy_max", 0.2, "max DCAxy in cm"}; Configurable dca_z_max{"dca_z_max", 0.2, "max DCAz in cm"}; - Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; // Don't change. - Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", +2.0, "max. TPC n sigma for electron inclusion"}; // Don't change. - Configurable minTOFNsigmaEl{"minTOFNsigmaEl", -2.0, "min. TOF n sigma for electron inclusion"}; // Don't change. - Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", +2.0, "max. TOF n sigma for electron inclusion"}; // Don't change. + Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; + Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", +2.0, "max. TPC n sigma for electron inclusion"}; + Configurable minTOFNsigmaEl{"minTOFNsigmaEl", -2.0, "min. TOF n sigma for electron inclusion"}; + Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", +2.0, "max. TOF n sigma for electron inclusion"}; + + Configurable maxTPCNsigmaPi{"maxTPCNsigmaPi", 2.5, "max. TPC n sigma for pion exclusion"}; + Configurable minTPCNsigmaPi{"minTPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; + Configurable maxTPCNsigmaKa{"maxTPCNsigmaKa", 2.5, "max. TPC n sigma for kaon exclusion"}; + Configurable minTPCNsigmaKa{"minTPCNsigmaKa", -2.5, "min. TPC n sigma for kaon exclusion"}; + Configurable maxTPCNsigmaPr{"maxTPCNsigmaPr", 2.5, "max. TPC n sigma for proton exclusion"}; + Configurable minTPCNsigmaPr{"minTPCNsigmaPr", -2.5, "min. TPC n sigma for proton exclusion"}; + Configurable requireTOF{"requireTOF", true, "require TOF hit"}; } tighttrackcut; Configurable storeOnlyTrueElectronMC{"storeOnlyTrueElectronMC", false, "Flag to store only true electron in MC"}; @@ -146,15 +154,15 @@ struct skimmerPrimaryElectronQC { Configurable> cutsMl{"cutsMl", std::vector{0.95}, "ML cuts per bin"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature"}, "Names of ML model input features"}; Configurable nameBinningFeature{"nameBinningFeature", "pt", "Names of ML model binning feature"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + // Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; o2::analysis::MlResponseO2Track mlResponseSingleTrack; - int mRunNumber; - float d_bz; + int mRunNumber{0}; + float d_bz{0}; Service ccdb; // o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; @@ -212,31 +220,6 @@ struct skimmerPrimaryElectronQC { fRegistry.add("Track/hProbElBDT", "probability to be e from BDT;p_{in} (GeV/c);BDT score;", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); fRegistry.add("Pair/hMvsPhiV", "m_{ee} vs. #varphi_{V} ULS;#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{90, 0.f, M_PI}, {100, 0, 0.1}}); } - - if (usePIDML) { - static constexpr int nClassesMl = 2; - const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutSmaller}; - const std::vector labelsClasses = {"Background", "Signal"}; - const uint32_t nBinsMl = binsMl.value.size() - 1; - const std::vector labelsBins(nBinsMl, "bin"); - double cutsMlArr[nBinsMl][nClassesMl]; - for (uint32_t i = 0; i < nBinsMl; i++) { - cutsMlArr[i][0] = 0.0; - cutsMlArr[i][1] = cutsMl.value[i]; - } - o2::framework::LabeledArray cutsMl = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; - - mlResponseSingleTrack.configure(binsMl.value, cutsMl, cutDirMl, nClassesMl); - if (loadModelsFromCCDB) { - ccdbApi.init(ccdburl); - mlResponseSingleTrack.setModelPathsCCDB(onnxFileNames.value, ccdbApi, onnxPathsCCDB.value, timestampCCDB.value); - } else { - mlResponseSingleTrack.setModelPathsLocal(onnxFileNames.value); - } - mlResponseSingleTrack.cacheInputFeaturesIndices(namesInputFeatures); - mlResponseSingleTrack.cacheBinningIndex(nameBinningFeature); - mlResponseSingleTrack.init(enableOptimizations.value); - } // end of PID ML } void initCCDB(aod::BCsWithTimestamps::iterator const& bc) @@ -293,6 +276,32 @@ struct skimmerPrimaryElectronQC { d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; } + + if (usePIDML) { + static constexpr int nClassesMl = 2; + const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutSmaller}; + const std::vector labelsClasses = {"Background", "Signal"}; + const uint32_t nBinsMl = binsMl.value.size() - 1; + const std::vector labelsBins(nBinsMl, "bin"); + double cutsMlArr[nBinsMl][nClassesMl]; + for (uint32_t i = 0; i < nBinsMl; i++) { + cutsMlArr[i][0] = 0.0; + cutsMlArr[i][1] = cutsMl.value[i]; + } + o2::framework::LabeledArray cutsMltmp = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; + + mlResponseSingleTrack.configure(binsMl.value, cutsMltmp, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdburl); + mlResponseSingleTrack.setModelPathsCCDB(onnxFileNames.value, ccdbApi, onnxPathsCCDB.value, bc.timestamp()); + } else { + mlResponseSingleTrack.setModelPathsLocal(onnxFileNames.value); + } + mlResponseSingleTrack.cacheInputFeaturesIndices(namesInputFeatures); + mlResponseSingleTrack.cacheBinningIndex(nameBinningFeature); + mlResponseSingleTrack.init(enableOptimizations.value); + } // end of PID ML + mRunNumber = bc.runNumber(); } @@ -467,9 +476,19 @@ struct skimmerPrimaryElectronQC { template bool isElectronTight(TTrack const& track) { - bool is_El_TPC = tighttrackcut.minTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < tighttrackcut.maxTPCNsigmaEl; - bool is_El_TOF = tighttrackcut.minTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < tighttrackcut.maxTOFNsigmaEl; - return is_El_TPC && is_El_TOF; + if (tighttrackcut.requireTOF) { + bool is_El_TPC = tighttrackcut.minTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < tighttrackcut.maxTPCNsigmaEl; + bool is_El_TOF = tighttrackcut.minTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < tighttrackcut.maxTOFNsigmaEl; + return is_El_TPC && is_El_TOF; + } else { // TPC hadron band rejection OR TOFreq + bool is_el_included_TPC = tighttrackcut.minTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < tighttrackcut.maxTPCNsigmaEl; + bool is_el_included_TOF = track.hasTOF() ? tighttrackcut.minTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < tighttrackcut.maxTOFNsigmaEl : true; + bool is_pi_excluded_TPC = !(tighttrackcut.minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < tighttrackcut.maxTPCNsigmaPi); + bool is_ka_excluded_TPC = !(tighttrackcut.minTPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < tighttrackcut.maxTPCNsigmaKa); + bool is_pr_excluded_TPC = !(tighttrackcut.minTPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < tighttrackcut.maxTPCNsigmaPr); + bool is_el_included_TOFreq = tighttrackcut.minTOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < tighttrackcut.maxTOFNsigmaEl; + return (is_el_included_TPC && is_el_included_TOF && is_pi_excluded_TPC && is_ka_excluded_TPC && is_pr_excluded_TPC) || (is_el_included_TPC && is_pi_excluded_TPC && is_el_included_TOFreq); + } } template @@ -705,7 +724,7 @@ struct skimmerPrimaryElectronQC { continue; } - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; } diff --git a/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectronSV.cxx b/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectronSV.cxx new file mode 100644 index 00000000000..49f289588db --- /dev/null +++ b/PWGEM/Dilepton/TableProducer/skimmerPrimaryElectronSV.cxx @@ -0,0 +1,269 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief write relevant information about primary electrons. +/// \author daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/Dilepton/Utils/ElectronModule.h" + +#include "Common/Core/PID/PIDTOFParamService.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct skimmerPrimaryElectronSV { + using MyBCs = o2::soa::Join; + using MyCollisions = o2::soa::Join; + using MyCollisionsWithSWT = o2::soa::Join; + + using MyTracks = o2::soa::Join; + using MyTrack = MyTracks::iterator; + using MyTracksMC = o2::soa::Join; + using MyTrackMC = MyTracksMC::iterator; + + // using MyV0s = o2::soa::Join; + // using MyCascades = o2::soa::Join; + + struct : o2::framework::ConfigurableGroup { + o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + o2::framework::Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + o2::framework::Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; + } ccdbConfig; + + o2::framework::Configurable doSCTwithTracks{"doSCTwithTracks", false, "flag to tag electrons with tracks"}; + // o2::framework::Configurable doSCTwithV0s{"doSCTwithV0s", false, "flag to tag electrons with v0s"}; + // o2::framework::Configurable doSCTwithCascades{"doSCTwithCascades", false, "flag to tag electrons with cascades"}; + + o2::framework::Service ccdb; + o2::framework::Service mTOFResponse; + + o2::framework::SliceCache cache; + o2::framework::Preslice perCol_track = o2::aod::track::collisionId; + o2::framework::Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + // o2::framework::Preslice perCol_v0 = o2::aod::v0data::collisionId; + // o2::framework::Preslice perCol_casc = o2::aod::cascdata::collisionId; + + void init(o2::framework::InitContext& initContext) + { + mRunNumber = 0; + d_bz = 0; + + LOGF(info, "initializing CCDB"); + ccdb->setURL(ccdbConfig.ccdburl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + LOGF(info, "initializing electronModule"); + electronModule.init(cfgEelectronCut, cfgEelectronPFCut, cfgHadronCut, /*cfgV0Cut, cfgCascadeCut,*/ cfgDFeT, /*cfgDFeV0, cfgDFeC,*/ initContext, ccdb, mTOFResponse, ccdbConfig.ccdburl.value); + electronModule.addHistograms(mRegistry); + + electronModule.doSCTwithTracks(doSCTwithTracks.value); + // electronModule.doSCTwithV0s(doSCTwithV0s.value); + // electronModule.doSCTwithCascades(doSCTwithCascades.value); + } + + o2::pwgem::dilepton::utils::ElectronModule electronModule; + o2::pwgem::dilepton::utils::ElectronProducts products; + o2::pwgem::dilepton::utils::electronCut cfgEelectronCut; + o2::pwgem::dilepton::utils::electronPFCut cfgEelectronPFCut; + o2::pwgem::dilepton::utils::hadronCut cfgHadronCut; + // o2::pwgem::dilepton::utils::v0Cut cfgV0Cut; + // o2::pwgem::dilepton::utils::cascadeCut cfgCascadeCut; + o2::pwgem::dilepton::utils::cfgDFeT cfgDFeT; + // o2::pwgem::dilepton::utils::cfgDFeV0 cfgDFeV0; + // o2::pwgem::dilepton::utils::cfgDFeC cfgDFeC; + + o2::framework::HistogramRegistry mRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; + // ---------- for data ---------- + + int mRunNumber{0}; + float d_bz{0}; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + o2::dataformats::VertexBase mVtx; + // const o2::dataformats::MeanVertexObject* mMeanVtx = nullptr; + o2::base::MatLayerCylSet* lut = nullptr; + + template + void initCCDB(TBC const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + // load matLUT for this timestamp + if (!lut) { + LOG(info) << "Loading material look-up table for timestamp: " << bc.timestamp(); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp(ccdbConfig.lutPath, bc.timestamp())); + } else { + LOG(info) << "Material look-up table already in place. Not reloading."; + } + + auto run3grp_timestamp = bc.timestamp(); + o2::parameters::GRPMagField* grpmag = 0x0; + o2::parameters::GRPObject* grpo = ccdb->getForTimeStamp(ccdbConfig.grpPath, run3grp_timestamp); + if (grpo) { + o2::base::Propagator::initFieldFromGRP(grpo); + o2::base::Propagator::Instance()->setMatLUT(lut); + // mMeanVtx = ccdb->getForTimeStamp(ccdbConfig.mVtxPath, bc.timestamp()); + // Fetch magnetic field from ccdb for current collision + d_bz = grpo->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + } else { + grpmag = ccdb->getForTimeStamp(ccdbConfig.grpmagPath, run3grp_timestamp); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfig.grpmagPath << " of object GRPMagField and " << ccdbConfig.grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + o2::base::Propagator::Instance()->setMatLUT(lut); + // mMeanVtx = ccdb->getForTimeStamp(ccdbConfig.mVtxPath, bc.timestamp()); + + // Fetch magnetic field from ccdb for current collision + d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + } + + mRunNumber = bc.runNumber(); + } + + // //! type of V0. 0: built solely for cascades (does not pass standard V0 cut), 1: standard 2, 3: photon-like with TPC-only use. Regular analysis should always use type 1. + // o2::framework::expressions::Filter v0Filter = o2::aod::v0data::v0Type == uint8_t(1) && o2::aod::v0data::v0cosPA > cfgV0Cut.cfg_min_cospa&& o2::aod::v0data::dcaV0daughters cfgV0Cut.cfg_min_dcaxy&& nabs(o2::aod::v0data::dcanegtopv) > cfgV0Cut.cfg_min_dcaxy; + // using filteredMyV0s = o2::soa::Filtered; + + // o2::framework::expressions::Filter cascadeFilter = nabs(o2::aod::cascdata::dcanegtopv) > cfgCascadeCut.cfg_min_dcaxy_v0leg&& nabs(o2::aod::cascdata::dcanegtopv) > cfgCascadeCut.cfg_min_dcaxy_v0leg&& nabs(o2::aod::cascdata::dcabachtopv) > cfgCascadeCut.cfg_min_dcaxy_bachelor&& o2::aod::cascdata::dcacascdaughters < cfgCascadeCut.cfg_max_dcadau&& o2::aod::cascdata::dcaV0daughters < cfgCascadeCut.cfg_max_dcadau_v0; + // using filteredMyCascades = o2::soa::Filtered; + + void processRec_SA(MyCollisions const& collisions, MyBCs const& bcs, MyTracks const& tracks /*, filteredMyV0s const& v0s, filteredMyCascades const& cascades*/) + { + if (bcs.size() == 0) { + return; + } + auto bc = bcs.begin(); + initCCDB(bc); + electronModule.processWithoutTTCA(bcs, collisions, tracks, /*v0s, cascades,*/ nullptr, products, mRegistry); + } + PROCESS_SWITCH(skimmerPrimaryElectronSV, processRec_SA, "process reconstructed info only", true); // standalone + + void processRec_TTCA(MyCollisions const& collisions, MyBCs const& bcs, MyTracks const& tracks, o2::aod::TrackAssoc const& trackIndices /*, filteredMyV0s const& v0s, filteredMyCascades const& cascades*/) + { + if (bcs.size() == 0) { + return; + } + auto bc = bcs.begin(); + initCCDB(bc); + electronModule.processWithTTCA(bcs, collisions, tracks, /*v0s, cascades,*/ trackIndices, nullptr, products, mRegistry, cache, perCol_track, trackIndicesPerCollision /*, perCol_v0, perCol_casc*/); + } + PROCESS_SWITCH(skimmerPrimaryElectronSV, processRec_TTCA, "process reconstructed info only", false); // with TTCA + + void processRec_SA_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs, MyTracks const& tracks /*, filteredMyV0s const& v0s, filteredMyCascades const& cascades*/) + { + if (bcs.size() == 0) { + return; + } + auto bc = bcs.begin(); + initCCDB(bc); + electronModule.processWithoutTTCA(bcs, collisions, tracks, /*v0s, cascades,*/ nullptr, products, mRegistry); + } + PROCESS_SWITCH(skimmerPrimaryElectronSV, processRec_SA_SWT, "process reconstructed info only", false); // standalone with swt + + void processRec_TTCA_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs, MyTracks const& tracks, o2::aod::TrackAssoc const& trackIndices /*, filteredMyV0s const& v0s, filteredMyCascades const& cascades*/) + { + if (bcs.size() == 0) { + return; + } + auto bc = bcs.begin(); + initCCDB(bc); + electronModule.processWithTTCA(bcs, collisions, tracks, /*v0s, cascades,*/ trackIndices, nullptr, products, mRegistry, cache, perCol_track, trackIndicesPerCollision /*, perCol_v0, perCol_casc*/); + } + PROCESS_SWITCH(skimmerPrimaryElectronSV, processRec_TTCA_SWT, "process reconstructed info only", false); // with TTCA with swt + + // ---------- for MC ---------- + + void processMC_SA(o2::soa::Join const& collisions, MyBCs const& bcs, MyTracksMC const& tracks, /*filteredMyV0s const& v0s, filteredMyCascades const& cascades,*/ o2::aod::McParticles const& mcParticles) + { + if (bcs.size() == 0) { + return; + } + auto bc = bcs.begin(); + initCCDB(bc); + electronModule.processWithoutTTCA(bcs, collisions, tracks, /*v0s, cascades,*/ mcParticles, products, mRegistry); + } + PROCESS_SWITCH(skimmerPrimaryElectronSV, processMC_SA, "process reconstructed and MC info ", false); // without TTCA in MC + + void processMC_TTCA(o2::soa::Join const& collisions, MyBCs const& bcs, MyTracksMC const& tracks, o2::aod::TrackAssoc const& trackIndices, /*filteredMyV0s const& v0s, filteredMyCascades const& cascades,*/ o2::aod::McParticles const& mcParticles) + { + if (bcs.size() == 0) { + return; + } + auto bc = bcs.begin(); + initCCDB(bc); + electronModule.processWithTTCA(bcs, collisions, tracks, /*v0s, cascades,*/ trackIndices, mcParticles, products, mRegistry, cache, perCol_track, trackIndicesPerCollision /*, perCol_v0, perCol_casc*/); + } + PROCESS_SWITCH(skimmerPrimaryElectronSV, processMC_TTCA, "process reconstructed info only", false); // with TTCA in MC +}; + +struct associateAmbiguousElectron { + o2::framework::Produces em_amb_ele_ids; + + o2::framework::SliceCache cache; + o2::framework::PresliceUnsorted perTrack = o2::aod::emprimaryelectron::trackId; + std::vector ambele_self_Ids; + + void process(o2::aod::EMPrimaryElectrons const& electrons) + { + for (const auto& electron : electrons) { + auto electrons_with_same_trackId = electrons.sliceBy(perTrack, electron.trackId()); + ambele_self_Ids.reserve(electrons_with_same_trackId.size()); + for (const auto& amb_ele : electrons_with_same_trackId) { + if (amb_ele.globalIndex() == electron.globalIndex()) { // don't store myself. + continue; + } + ambele_self_Ids.emplace_back(amb_ele.globalIndex()); + } + em_amb_ele_ids(ambele_self_Ids); + ambele_self_Ids.clear(); + ambele_self_Ids.shrink_to_fit(); + } + } +}; +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + o2::pid::tof::TOFResponseImpl::metadataInfo.initMetadata(cfgc); + return o2::framework::WorkflowSpec{ + adaptAnalysisTask(cfgc, o2::framework::TaskName{"skimmer-primary-electron-sv"}), + adaptAnalysisTask(cfgc, o2::framework::TaskName{"associate-ambiguous-electron"})}; +} diff --git a/PWGEM/Dilepton/TableProducer/skimmerPrimaryMFTTrack.cxx b/PWGEM/Dilepton/TableProducer/skimmerPrimaryMFTTrack.cxx index 1cf936a061f..b3dbb015003 100644 --- a/PWGEM/Dilepton/TableProducer/skimmerPrimaryMFTTrack.cxx +++ b/PWGEM/Dilepton/TableProducer/skimmerPrimaryMFTTrack.cxx @@ -313,7 +313,7 @@ struct skimmerPrimaryMFTTrack { if (!collision.isEoI()) { continue; } - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; } diff --git a/PWGEM/Dilepton/TableProducer/skimmerPrimaryMuon.cxx b/PWGEM/Dilepton/TableProducer/skimmerPrimaryMuon.cxx index 674902c2746..8ae3d9cd690 100644 --- a/PWGEM/Dilepton/TableProducer/skimmerPrimaryMuon.cxx +++ b/PWGEM/Dilepton/TableProducer/skimmerPrimaryMuon.cxx @@ -43,11 +43,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -77,7 +77,6 @@ struct skimmerPrimaryMuon { Produces emprimarymuons; Produces emprimarymuonscov; - Produces emprimarymuonsmatchmc; // Configurables Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -105,25 +104,40 @@ struct skimmerPrimaryMuon { Configurable minNmuon{"minNmuon", 0, "min number of muon candidates per collision"}; Configurable maxDEta{"maxDEta", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; Configurable maxDPhi{"maxDPhi", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; - Configurable cfgApplyPreselectionInBestMatch{"cfgApplyPreselectionInBestMatch", false, "flag to apply preselection in find best match function"}; - Configurable cfgRequireSameSign{"cfgRequireSameSign", false, "flag to require same sign between MFT and MCH-MID"}; + // Configurable cfgApplyPreselectionInBestMatch{"cfgApplyPreselectionInBestMatch", false, "flag to apply preselection in find best match function"}; + Configurable cfgKeepOnlySAmuons{"cfgKeepOnlySAmuons", false, "flag to store only MCH-MID tracks"}; + Configurable cfgKeepOnlyGLmuons{"cfgKeepOnlyGLmuons", false, "flag to store only MFT-MCH-MID tracks"}; // for z shift for propagation Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift"}; Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; - Configurable cfgManualZShift{"cfgManualZShift", 0, "manual z-shift for propagation of global muon to PV"}; + Configurable cfgManualXShiftMFTtop{"cfgManualXShiftMFTtop", 0, "manual x shift on MFT top when propagating global muon to PV"}; + Configurable cfgManualYShiftMFTtop{"cfgManualYShiftMFTtop", 0, "manual y shift on MFT top when propagating global muon to PV"}; + Configurable cfgManualZShiftMFTtop{"cfgManualZShiftMFTtop", 0, "manual z shift on MFT top when propagating global muon to PV"}; + Configurable cfgManualXShiftMFTbottom{"cfgManualXShiftMFTbottom", 0, "manual x shift on MFT bottom when propagating global muon to PV"}; + Configurable cfgManualYShiftMFTbottom{"cfgManualYShiftMFTbottom", 0, "manual y shift on MFT bottom when propagating global muon to PV"}; + Configurable cfgManualZShiftMFTbottom{"cfgManualZShiftMFTbottom", 0, "manual z shift on MFT bottom when propagating global muon to PV"}; o2::ccdb::CcdbApi ccdbApi; Service ccdb; int mRunNumber = 0; float mBz = 0; - float mZShift = 0; + float mXShiftMFTtop = 0; + float mYShiftMFTtop = 0; + float mZShiftMFTtop = 0; + float mXShiftMFTbottom = 0; + float mYShiftMFTbottom = 0; + float mZShiftMFTbottom = 0; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view muon_types[5] = {"MFTMCHMID/", "MFTMCHMIDOtherMatch/", "MFTMCH/", "MCHMID/", "MCH/"}; + // static constexpr std::string_view muon_types[5] = {"MFTMCHMID/", "MFTMCHMIDOtherMatch/", "MFTMCH/", "MCHMID/", "MCH/"}; void init(InitContext&) { + if (cfgKeepOnlySAmuons && cfgKeepOnlyGLmuons) { + LOGF(fatal, "cfgKeepOnlySAmuons and cfgKeepOnlyGLmuons cannot be true simultaneously. Please disable both or choose one."); + } + ccdb->setURL(ccdburl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -135,7 +149,12 @@ struct skimmerPrimaryMuon { } mRunNumber = 0; mBz = 0; - mZShift = 0; + mXShiftMFTtop = 0; + mYShiftMFTtop = 0; + mZShiftMFTtop = 0; + mXShiftMFTbottom = 0; + mYShiftMFTbottom = 0; + mZShiftMFTbottom = 0; } void initCCDB(aod::BCsWithTimestamps::iterator const& bc) @@ -163,14 +182,22 @@ struct skimmerPrimaryMuon { auto* zShift = ccdb->getForTimeStamp>(cfgZShiftPath, bc.timestamp()); if (zShift != nullptr && !zShift->empty()) { LOGF(info, "reading z shift %f from %s", (*zShift)[0], cfgZShiftPath.value); - mZShift = (*zShift)[0]; + mZShiftMFTtop = (*zShift)[0]; + mZShiftMFTbottom = (*zShift)[0]; } else { LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", cfgZShiftPath.value); - mZShift = 0; + mZShiftMFTtop = 0; + mZShiftMFTbottom = 0; } } else { - LOGF(info, "z shift is manually set to %f cm", cfgManualZShift.value); - mZShift = cfgManualZShift; + LOGF(info, "z shift on MFT top is manually set to %f cm", cfgManualZShiftMFTtop.value); + LOGF(info, "z shift on MFT bottom is manually set to %f cm", cfgManualZShiftMFTbottom.value); + mXShiftMFTtop = cfgManualXShiftMFTtop; + mYShiftMFTtop = cfgManualYShiftMFTtop; + mZShiftMFTtop = cfgManualZShiftMFTtop; + mXShiftMFTbottom = cfgManualXShiftMFTbottom; + mYShiftMFTbottom = cfgManualYShiftMFTbottom; + mZShiftMFTbottom = cfgManualZShiftMFTbottom; } } @@ -200,11 +227,14 @@ struct skimmerPrimaryMuon { fRegistry.add("MFTMCHMID/hChi2MatchMCHMFT_Pt", "chi2 match MCH-MFT;p_{T} (GeV/c);matching chi2 MCH-MFT", kTH2F, {{100, 0, 10}, {200, 0.0f, 100}}, false); fRegistry.add("MFTMCHMID/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", kTH2F, {{200, -1, 1}, {200, -1, +1}}, false); fRegistry.add("MFTMCHMID/hDCAxy2DinSigma", "DCA x vs. y in sigma;DCA_{x} (#sigma);DCA_{y} (#sigma)", kTH2F, {{200, -10, 10}, {200, -10, +10}}, false); - fRegistry.add("MFTMCHMID/hDCAxy", "DCAxy;DCA_{xy} (cm);", kTH1F, {{100, 0, 1}}, false); - fRegistry.add("MFTMCHMID/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{100, 0, 1}, {200, -0.1, 0.1}}, false); + fRegistry.add("MFTMCHMID/hDCAxy", "DCAxy;DCA_{xy} (cm);", kTH1F, {{1000, 0, 1}}, false); fRegistry.add("MFTMCHMID/hDCAxyinSigma", "DCAxy in sigma;DCA_{xy} (#sigma);", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("MFTMCHMID/hDCAx_PosZ", "DCAx vs. posZ;Z_{vtx} (cm);DCA_{x} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hDCAy_PosZ", "DCAy vs. posZ;Z_{vtx} (cm);DCA_{y} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/hLog10Chi2IP", "chi2IP;log_{10}(#chi^{2}_{IP})", kTH1F, {{100, -5, 5}}, false); + fRegistry.add("MFTMCHMID/hSqrtChi2IP", "chi2IP;#sqrt{#chi^{2}_{IP}}", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("MFTMCHMID/hDCAx_PosZ_MFTtop", "DCAx vs. posZ;Z_{vtx} (cm);DCA_{x} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/hDCAy_PosZ_MFTtop", "DCAy vs. posZ;Z_{vtx} (cm);DCA_{y} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/hDCAx_PosZ_MFTbottom", "DCAx vs. posZ;Z_{vtx} (cm);DCA_{x} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); + fRegistry.add("MFTMCHMID/hDCAy_PosZ_MFTbottom", "DCAy vs. posZ;Z_{vtx} (cm);DCA_{y} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); fRegistry.add("MFTMCHMID/hDCAx_Phi", "DCAx vs. #varphi;#varphi (rad.);DCA_{x} (cm)", kTH2F, {{180, -M_PI, M_PI}, {400, -0.2, +0.2}}, false); fRegistry.add("MFTMCHMID/hDCAy_Phi", "DCAy vs. #varphi;#varphi (rad.);DCA_{y} (cm)", kTH2F, {{180, -M_PI, M_PI}, {400, -0.2, +0.2}}, false); fRegistry.add("MFTMCHMID/hMeanDCAx", ";X_{IU} (cm);Y_{IU} (cm); (cm)", kTProfile2D, {{240, -12, +12}, {240, -12, +12}}, false); @@ -263,7 +293,7 @@ struct skimmerPrimaryMuon { } template - bool fillFwdTrackTable(TCollision const& collision, TFwdTrack fwdtrack, TMFTTracksCov const& mftCovs, const bool isAmbiguous) + bool fillFwdTrackTable(TCollision const& collision, const TFwdTrack& fwdtrack, TMFTTracksCov const& mftCovs, const bool isAmbiguous) { if (fwdtrack.chi2MatchMCHMID() < 0.f) { // this should never happen. only for protection. return false; @@ -273,55 +303,30 @@ struct skimmerPrimaryMuon { return false; } - o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, mZShift); - float pt = propmuonAtPV.getPt(); - float eta = propmuonAtPV.getEta(); - float phi = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phi); - - float dcaX = propmuonAtPV.getX() - collision.posX(); - float dcaY = propmuonAtPV.getY() - collision.posY(); - float dcaZ = propmuonAtPV.getZ() - collision.posZ(); - float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - float rAtAbsorberEnd = fwdtrack.rAtAbsorberEnd(); // this works only for GlobalMuonTrack - float cXX = propmuonAtPV.getSigma2X(); - float cYY = propmuonAtPV.getSigma2Y(); - float cXY = propmuonAtPV.getSigmaXY(); - - float det = cXX * cYY - cXY * cXY; // determinanat + o2::dataformats::GlobalFwdTrack propmuonAtPV; + float pt = 0.f, eta = 0.f, phi = 0.f; + + o2::dataformats::GlobalFwdTrack propmuonAtDCA; + float dcaX = 999.f, dcaY = 999.f, dcaXY = 999.f; + float rAtAbsorberEnd = 1e+10; + float cXXatDCA = 1e+10, cYYatDCA = 1e+10, cXYatDCA = 1e+10; float dcaXYinSigma = 999.f; - if (det < 0) { - dcaXYinSigma = 999.f; - } else { - dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYY + dcaY * dcaY * cXX - 2.f * dcaX * dcaY * cXY) / det / 2.f)); // dca xy in sigma - } - float sigma_dcaXY = dcaXY / dcaXYinSigma; + float sigma_dcaXY = 1e+10; - float pDCA = propmuonAtPV.getP() * dcaXY; + float pDCA = 1e+10; int nClustersMFT = 0; - float ptMatchedMCHMID = propmuonAtPV.getPt(); - float etaMatchedMCHMID = propmuonAtPV.getEta(); - float phiMatchedMCHMID = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMID); - // float x = fwdtrack.x(); - // float y = fwdtrack.y(); - // float z = fwdtrack.z(); - // float tgl = fwdtrack.tgl(); + float ptMatchedMCHMID = 1e+10, etaMatchedMCHMID = 1e+10, phiMatchedMCHMID = 1e+10; float chi2mft = 0.f; uint64_t mftClusterSizesAndTrackFlags = 0; int ndf_mchmft = 1; int ndf_mft = 1; - // float etaMatchedMCHMIDatMP = 999.f; - float phiMatchedMCHMIDatMP = 999.f; - // float etaMatchedMFTatMP = 999.f; - float phiMatchedMFTatMP = 999.f; - float deta = 999.f; float dphi = 999.f; - bool isCorrectMatchMFTMCH = true; // by default, it is true. it is evaluated for global muons in MC. float xMFT = 0.f, yMFT = 0.f; + float chi2IP = -1; + float sfPt = 1.f; // scaling factor for pT of global muons if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { if (fwdtrack.chi2MatchMCHMFT() < 0.f || maxMatchingChi2MCHMFT < fwdtrack.chi2MatchMCHMFT()) { @@ -333,18 +338,37 @@ struct skimmerPrimaryMuon { return false; } - // apply dca cut here to minimize the number of calling propagateMuon. - if (maxDCAxy < dcaXY) { - return false; - } - auto mchtrack = fwdtrack.template matchMCHTrack_as(); // MCH-MID auto mfttrack = fwdtrack.template matchMFTTrack_as(); // MFTsa if (mfttrack.chi2() < 0.f) { return false; } - if (cfgRequireSameSign && (mfttrack.sign() != mchtrack.sign())) { + propmuonAtPV = std::atan2(mfttrack.y(), mfttrack.x()) > 0.f ? propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); + pt = propmuonAtPV.getPt(); + eta = propmuonAtPV.getEta(); + phi = propmuonAtPV.getPhi(); + o2::math_utils::bringTo02Pi(phi); + + propmuonAtDCA = std::atan2(mfttrack.y(), mfttrack.x()) > 0.f ? propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); + dcaX = propmuonAtDCA.getX() - collision.posX(); + dcaY = propmuonAtDCA.getY() - collision.posY(); + dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); + cXXatDCA = propmuonAtDCA.getSigma2X(); + cYYatDCA = propmuonAtDCA.getSigma2Y(); + cXYatDCA = propmuonAtDCA.getSigmaXY(); + rAtAbsorberEnd = fwdtrack.rAtAbsorberEnd(); // this works only for GlobalMuonTrack + + float det = cXXatDCA * cYYatDCA - cXYatDCA * cXYatDCA; // determinant + if (det < 0) { + dcaXYinSigma = 999.f; + } else { + dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYYatDCA + dcaY * dcaY * cXXatDCA - 2.f * dcaX * dcaY * cXYatDCA) / det / 2.f)); // dca xy in sigma + } + sigma_dcaXY = dcaXY / dcaXYinSigma / std::sqrt(2); + + // apply dca cut here to minimize the number of calling propagateMuon. + if (maxDCAxy < dcaXY) { return false; } @@ -356,9 +380,8 @@ struct skimmerPrimaryMuon { return false; } // auto mcParticle_MFTMCHMID = fwdtrack.template mcParticle_as(); // this is identical to mcParticle_MCHMID - auto mcParticle_MCHMID = mchtrack.template mcParticle_as(); // this is identical to mcParticle_MFTMCHMID - auto mcParticle_MFT = mfttrack.template mcParticle_as(); - isCorrectMatchMFTMCH = static_cast(mcParticle_MCHMID.globalIndex() == mcParticle_MFT.globalIndex()); + // auto mcParticle_MCHMID = mchtrack.template mcParticle_as(); // this is identical to mcParticle_MFTMCHMID + // auto mcParticle_MFT = mfttrack.template mcParticle_as(); } nClustersMFT = mfttrack.nClusters(); @@ -366,97 +389,136 @@ struct skimmerPrimaryMuon { ndf_mchmft = 2.f * (mchtrack.nClusters() + nClustersMFT) - 5.f; ndf_mft = 2.f * nClustersMFT - 5.f; chi2mft = mfttrack.chi2(); - // chi2mft = mfttrack.chi2() / (2.f * nClustersMFT - 5.f); // apply chi2/ndf cut here to minimize the number of calling propagateMuon. if (maxChi2GL < fwdtrack.chi2() / ndf_mchmft) { return false; } - o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, mZShift); + o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, 0.f, 0.f, 0.f); ptMatchedMCHMID = propmuonAtPV_Matched.getPt(); etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); o2::math_utils::bringTo02Pi(phiMatchedMCHMID); - o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, mZShift); - float dcaX_Matched = propmuonAtDCA_Matched.getX() - collision.posX(); - float dcaY_Matched = propmuonAtDCA_Matched.getY() - collision.posY(); - float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); - pDCA = mchtrack.p() * dcaXY_Matched; + o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, 0.f, 0.f, 0.f); + // float dcaXY_Matched = std::hypot(propmuonAtDCA_Matched.getX() - collision.posX(), propmuonAtDCA_Matched.getY() - collision.posY()); + pDCA = mchtrack.p() * std::hypot(propmuonAtDCA_Matched.getX() - collision.posX(), propmuonAtDCA_Matched.getY() - collision.posY()); if constexpr (withMFTCov) { auto mfttrackcov = mftCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); - auto muonAtMP = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToMatchingPlane, matchingZ, mBz, mZShift); // propagated to matching plane - o2::track::TrackParCovFwd mftsaAtMP = getTrackParCovFwdShift(mfttrack, mZShift, mfttrackcov); // values at innermost update - mftsaAtMP.propagateToZhelix(matchingZ, mBz); // propagated to matching plane - // etaMatchedMFTatMP = mftsaAtMP.getEta(); - phiMatchedMFTatMP = mftsaAtMP.getPhi(); - // etaMatchedMCHMIDatMP = muonAtMP.getEta(); - phiMatchedMCHMIDatMP = muonAtMP.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMIDatMP); - o2::math_utils::bringTo02Pi(phiMatchedMFTatMP); - - o2::track::TrackParCovFwd mftsa = getTrackParCovFwdShift(mfttrack, mZShift, mfttrackcov); // values at innermost update - o2::dataformats::GlobalFwdTrack globalMuonRefit = o2::aod::fwdtrackutils::refitGlobalMuonCov(propmuonAtPV_Matched, mftsa); // this is track at IU. + // auto muonAtMP = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToMatchingPlane, matchingZ, mBz, 0.f, 0.f, 0.f); // propagated to matching plane + // o2::track::TrackParCovFwd mftsaAtMP = std::atan2(mfttrack.y(), mfttrack.x()) > 0.f ? getTrackParCovFwd3DShift(mfttrack, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop, mfttrackcov) : getTrackParCovFwd3DShift(mfttrack, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom, mfttrackcov); // values at innermost update + // mftsaAtMP.propagateToZhelix(matchingZ, mBz); // propagated to matching plane + + o2::track::TrackParCovFwd mftsa = std::atan2(mfttrack.y(), mfttrack.x()) > 0.f ? getTrackParCovFwd3DShift(mfttrack, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop, mfttrackcov) : getTrackParCovFwd3DShift(mfttrack, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom, mfttrackcov); // values at innermost update + o2::dataformats::GlobalFwdTrack globalMuonRefit = o2::aod::fwdtrackutils::refitGlobalMuonCov(propmuonAtPV_Matched, mftsa); // this is track at IU. auto globalMuon = o2::aod::fwdtrackutils::propagateTrackParCovFwd(globalMuonRefit, fwdtrack.trackType(), collision, propagationPoint::kToVertex, matchingZ, mBz); pt = globalMuon.getPt(); eta = globalMuon.getEta(); phi = globalMuon.getPhi(); o2::math_utils::bringTo02Pi(phi); - cXX = globalMuon.getSigma2X(); - cYY = globalMuon.getSigma2Y(); - cXY = globalMuon.getSigmaXY(); + cXXatDCA = globalMuon.getSigma2X(); + cYYatDCA = globalMuon.getSigma2Y(); + cXYatDCA = globalMuon.getSigmaXY(); dcaX = globalMuon.getX() - collision.posX(); dcaY = globalMuon.getY() - collision.posY(); - dcaZ = globalMuon.getZ() - collision.posZ(); dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - det = cXX * cYY - cXY * cXY; // determinanat - dcaXYinSigma = 999.f; + det = cXXatDCA * cYYatDCA - cXYatDCA * cXYatDCA; // determinant if (det < 0) { dcaXYinSigma = 999.f; } else { - dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYY + dcaY * dcaY * cXX - 2.f * dcaX * dcaY * cXY) / det / 2.f)); // dca xy in sigma + dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYYatDCA + dcaY * dcaY * cXXatDCA - 2.f * dcaX * dcaY * cXYatDCA) / det / 2.f)); // dca xy in sigma } sigma_dcaXY = dcaXY / dcaXYinSigma; - } + } // end of withMFTCov deta = etaMatchedMCHMID - eta; dphi = phiMatchedMCHMID - phi; o2::math_utils::bringToPMPi(dphi); + chi2IP = std::atan2(mfttrack.y(), mfttrack.x()) > 0.f ? getFwdChi2IP(fwdtrack, collision, mBz, mXShiftMFTtop, mYShiftMFTtop, mZShiftMFTtop) : getFwdChi2IP(fwdtrack, collision, mBz, mXShiftMFTbottom, mYShiftMFTbottom, mZShiftMFTbottom); + if (std::sqrt(std::pow(deta / maxDEta, 2) + std::pow(dphi / maxDPhi, 2)) > 1.f) { return false; } - if (refitGlobalMuon) { - pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); + if (refitGlobalMuon) { // if pT of global muon is modified, one has to redo propagations with updated covariance matrix. + sfPt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))) / propmuonAtPV.getPt(); + pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); // sfPt * propmuonAtPV.getPt(); + + const auto& fwdcov = propmuonAtPV.getCovariances(); // covatiance matrix at PV + + auto globalMuonManual = getTrackParCovFwd3DShiftManual( + propmuonAtPV.getX(), propmuonAtPV.getY(), propmuonAtPV.getPhi(), propmuonAtPV.getTgl(), propmuonAtPV.getInvQPt() / sfPt, + fwdcov(0, 0), + fwdcov(1, 0), fwdcov(1, 1), + fwdcov(2, 0), fwdcov(2, 1), fwdcov(2, 2), + fwdcov(3, 0), fwdcov(3, 1), fwdcov(3, 2), fwdcov(3, 3), + fwdcov(4, 0) / sfPt, fwdcov(4, 1) / sfPt, fwdcov(4, 2) / sfPt, fwdcov(4, 3) / sfPt, fwdcov(4, 4) / sfPt / sfPt, + propmuonAtPV.getZ(), 0.0, 0.0, 0.0, fwdtrack.chi2()); + + auto globalMuonManualAtZPV = o2::aod::fwdtrackutils::propagateTrackParCovFwd(globalMuonManual, fwdtrack.trackType(), collision, propagationPoint::kToDCA, matchingZ, mBz); + + dcaX = globalMuonManualAtZPV.getX() - collision.posX(); + dcaY = globalMuonManualAtZPV.getY() - collision.posY(); + cXXatDCA = globalMuonManualAtZPV.getSigma2X(); + cYYatDCA = globalMuonManualAtZPV.getSigma2Y(); + cXYatDCA = globalMuonManualAtZPV.getSigmaXY(); + // add convariance matrix of vertex + + float tx = std::cos(globalMuonManualAtZPV.getPhi()) / globalMuonManualAtZPV.getTgl(); // dx/dz + float ty = std::sin(globalMuonManualAtZPV.getPhi()) / globalMuonManualAtZPV.getTgl(); // dy/dz + + // PV covariance projected into the (dx, dy) residual space. + float resPVXX = collision.covXX() - 2.0 * tx * collision.covXZ() + tx * tx * collision.covZZ(); + float resPVXY = collision.covXY() - ty * collision.covXZ() - tx * collision.covYZ() + tx * ty * collision.covZZ(); + float resPVYY = collision.covYY() - 2.0 * ty * collision.covYZ() + ty * ty * collision.covZZ(); + + // Full residual covariance. + cXXatDCA = cXXatDCA + resPVXX; + cXYatDCA = cXYatDCA + resPVXY; + cYYatDCA = cYYatDCA + resPVYY; + + det = cXXatDCA * cYYatDCA - cXYatDCA * cXYatDCA; // determinant + if (det < 0) { + dcaXYinSigma = 999.f; + } else { + dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYYatDCA + dcaY * dcaY * cXXatDCA - 2.f * dcaX * dcaY * cXYatDCA) / det / 2.f)); // dca xy in sigma + } + sigma_dcaXY = dcaXY / dcaXYinSigma / std::sqrt(2); + + chi2IP = getFwdChi2IP(globalMuonManual, collision, mBz); } } else if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { - o2::dataformats::GlobalFwdTrack propmuonAtRabs = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToRabs, matchingZ, mBz, mZShift); // this is necessary only for MuonStandaloneTrack + propmuonAtPV = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, 0.f, 0.f, 0.f); + pt = propmuonAtPV.getPt(); + eta = propmuonAtPV.getEta(); + phi = propmuonAtPV.getPhi(); + o2::math_utils::bringTo02Pi(phi); + + o2::dataformats::GlobalFwdTrack propmuonAtRabs = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToRabs, matchingZ, mBz, 0.f, 0.f, 0.f); // this is necessary only for MuonStandaloneTrack float xAbs = propmuonAtRabs.getX(); float yAbs = propmuonAtRabs.getY(); rAtAbsorberEnd = std::sqrt(xAbs * xAbs + yAbs * yAbs); // Redo propagation only for muon tracks // propagation of MFT tracks alredy done in reconstruction - o2::dataformats::GlobalFwdTrack propmuonAtDCA = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, mZShift); - cXX = propmuonAtDCA.getSigma2X(); - cYY = propmuonAtDCA.getSigma2Y(); - cXY = propmuonAtDCA.getSigmaXY(); + propmuonAtDCA = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, 0.f, 0.f, 0.f); + cXXatDCA = propmuonAtDCA.getSigma2X(); + cYYatDCA = propmuonAtDCA.getSigma2Y(); + cXYatDCA = propmuonAtDCA.getSigmaXY(); dcaX = propmuonAtDCA.getX() - collision.posX(); dcaY = propmuonAtDCA.getY() - collision.posY(); - dcaZ = propmuonAtDCA.getZ() - collision.posZ(); dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); pDCA = fwdtrack.p() * dcaXY; - det = cXX * cYY - cXY * cXY; // determinanat - dcaXYinSigma = 999.f; + float det = cXXatDCA * cYYatDCA - cXYatDCA * cXYatDCA; // determinant if (det < 0) { dcaXYinSigma = 999.f; } else { - dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYY + dcaY * dcaY * cXX - 2.f * dcaX * dcaY * cXY) / det / 2.f)); // dca xy in sigma + dcaXYinSigma = std::sqrt(std::fabs((dcaX * dcaX * cYYatDCA + dcaY * dcaY * cXXatDCA - 2.f * dcaX * dcaY * cXYatDCA) / det / 2.f)); // dca xy in sigma } - sigma_dcaXY = dcaXY / dcaXYinSigma; + sigma_dcaXY = dcaXY / dcaXYinSigma / std::sqrt(2); } else { return false; } @@ -468,27 +530,22 @@ struct skimmerPrimaryMuon { if constexpr (fillTable) { float dpt = (ptMatchedMCHMID - pt) / pt; - // float detaMP = etaMatchedMCHMIDatMP - etaMatchedMFTatMP; - // float dphiMP = phiMatchedMCHMIDatMP - phiMatchedMFTatMP; - // o2::math_utils::bringToPMPi(dphiMP); - bool isAssociatedToMPC = fwdtrack.collisionId() == collision.globalIndex(); // LOGF(info, "isAmbiguous = %d, isAssociatedToMPC = %d, fwdtrack.globalIndex() = %d, fwdtrack.collisionId() = %d, collision.globalIndex() = %d", isAmbiguous, isAssociatedToMPC, fwdtrack.globalIndex(), fwdtrack.collisionId(), collision.globalIndex()); emprimarymuons(collision.globalIndex(), fwdtrack.globalIndex(), fwdtrack.matchMFTTrackId(), fwdtrack.matchMCHTrackId(), fwdtrack.trackType(), - pt, eta, phi, fwdtrack.sign(), dcaX, dcaY, cXX, cYY, cXY, ptMatchedMCHMID, etaMatchedMCHMID, phiMatchedMCHMID, - // etaMatchedMCHMIDatMP, phiMatchedMCHMIDatMP, etaMatchedMFTatMP, phiMatchedMFTatMP, - fwdtrack.nClusters(), pDCA, rAtAbsorberEnd, fwdtrack.chi2(), fwdtrack.chi2MatchMCHMID(), fwdtrack.chi2MatchMCHMFT(), + pt, eta, phi, fwdtrack.sign(), dcaX, dcaY, cXXatDCA, cYYatDCA, cXYatDCA, ptMatchedMCHMID, etaMatchedMCHMID, phiMatchedMCHMID, + fwdtrack.nClusters(), pDCA, rAtAbsorberEnd, fwdtrack.chi2(), fwdtrack.chi2MatchMCHMID(), fwdtrack.chi2MatchMCHMFT(), map_diff_chi2MatchMCHMFT[fwdtrack.globalIndex()], fwdtrack.mchBitMap(), fwdtrack.midBitMap(), fwdtrack.midBoards(), mftClusterSizesAndTrackFlags, chi2mft, isAssociatedToMPC, isAmbiguous); const auto& fwdcov = propmuonAtPV.getCovariances(); // covatiance matrix at PV emprimarymuonscov( propmuonAtPV.getX(), propmuonAtPV.getY(), propmuonAtPV.getZ(), - // fwdcov(0, 0), - // fwdcov(0, 1), fwdcov(1, 1), + fwdcov(0, 0), + fwdcov(1, 0), fwdcov(1, 1), fwdcov(2, 0), fwdcov(2, 1), fwdcov(2, 2), fwdcov(3, 0), fwdcov(3, 1), fwdcov(3, 2), fwdcov(3, 3), - fwdcov(4, 0), fwdcov(4, 1), fwdcov(4, 2), fwdcov(4, 3), fwdcov(4, 4)); + fwdcov(4, 0) / sfPt, fwdcov(4, 1) / sfPt, fwdcov(4, 2) / sfPt, fwdcov(4, 3) / sfPt, fwdcov(4, 4) / sfPt / sfPt); // See definition DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h // Covariance matrix of track parameters, ordered as follows: @@ -498,8 +555,6 @@ struct skimmerPrimaryMuon { // // - emprimarymuonsmatchmc(isCorrectMatchMFTMCH); - if (fillQAHistograms) { fRegistry.fill(HIST("hMuonType"), fwdtrack.trackType()); if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { @@ -519,15 +574,21 @@ struct skimmerPrimaryMuon { fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMID_Pt"), pt, fwdtrack.chi2MatchMCHMID()); fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMFT_Pt"), pt, fwdtrack.chi2MatchMCHMFT()); fRegistry.fill(HIST("MFTMCHMID/hDCAxy2D"), dcaX, dcaY); - fRegistry.fill(HIST("MFTMCHMID/hDCAxy2DinSigma"), dcaX / std::sqrt(cXX), dcaY / std::sqrt(cYY)); + fRegistry.fill(HIST("MFTMCHMID/hDCAxy2DinSigma"), dcaX / std::sqrt(cXXatDCA), dcaY / std::sqrt(cYYatDCA)); fRegistry.fill(HIST("MFTMCHMID/hDCAxy"), dcaXY); - fRegistry.fill(HIST("MFTMCHMID/hDCAxyz"), dcaXY, dcaZ); fRegistry.fill(HIST("MFTMCHMID/hDCAxyinSigma"), dcaXYinSigma); - fRegistry.fill(HIST("MFTMCHMID/hDCAxResolutionvsPt"), pt, std::sqrt(cXX) * 1e+4); // convert cm to um - fRegistry.fill(HIST("MFTMCHMID/hDCAyResolutionvsPt"), pt, std::sqrt(cYY) * 1e+4); // convert cm to um - fRegistry.fill(HIST("MFTMCHMID/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um - fRegistry.fill(HIST("MFTMCHMID/hDCAx_PosZ"), collision.posZ(), dcaX); - fRegistry.fill(HIST("MFTMCHMID/hDCAy_PosZ"), collision.posZ(), dcaY); + fRegistry.fill(HIST("MFTMCHMID/hDCAxResolutionvsPt"), pt, std::sqrt(cXXatDCA) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/hDCAyResolutionvsPt"), pt, std::sqrt(cYYatDCA) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um + fRegistry.fill(HIST("MFTMCHMID/hLog10Chi2IP"), std::log10(chi2IP)); + fRegistry.fill(HIST("MFTMCHMID/hSqrtChi2IP"), std::sqrt(chi2IP)); + if (std::atan2(yMFT, xMFT) > 0.f) { + fRegistry.fill(HIST("MFTMCHMID/hDCAx_PosZ_MFTtop"), collision.posZ(), dcaX); + fRegistry.fill(HIST("MFTMCHMID/hDCAy_PosZ_MFTtop"), collision.posZ(), dcaY); + } else { + fRegistry.fill(HIST("MFTMCHMID/hDCAx_PosZ_MFTbottom"), collision.posZ(), dcaX); + fRegistry.fill(HIST("MFTMCHMID/hDCAy_PosZ_MFTbottom"), collision.posZ(), dcaY); + } fRegistry.fill(HIST("MFTMCHMID/hDCAx_Phi"), std::atan2(yMFT, xMFT), dcaX); fRegistry.fill(HIST("MFTMCHMID/hDCAy_Phi"), std::atan2(yMFT, xMFT), dcaY); fRegistry.fill(HIST("MFTMCHMID/hMeanDCAx"), fwdtrack.x(), fwdtrack.y(), dcaX); @@ -549,13 +610,12 @@ struct skimmerPrimaryMuon { fRegistry.fill(HIST("MCHMID/hChi2MatchMCHMID_Pt"), pt, fwdtrack.chi2MatchMCHMID()); fRegistry.fill(HIST("MCHMID/hChi2MatchMCHMFT_Pt"), pt, fwdtrack.chi2MatchMCHMFT()); fRegistry.fill(HIST("MCHMID/hDCAxy2D"), dcaX, dcaY); - fRegistry.fill(HIST("MCHMID/hDCAxy2DinSigma"), dcaX / std::sqrt(cXX), dcaY / std::sqrt(cYY)); + fRegistry.fill(HIST("MCHMID/hDCAxy2DinSigma"), dcaX / std::sqrt(cXXatDCA), dcaY / std::sqrt(cYYatDCA)); fRegistry.fill(HIST("MCHMID/hDCAxy"), dcaXY); - fRegistry.fill(HIST("MCHMID/hDCAxyz"), dcaXY, dcaZ); fRegistry.fill(HIST("MCHMID/hDCAxyinSigma"), dcaXYinSigma); - fRegistry.fill(HIST("MCHMID/hDCAxResolutionvsPt"), pt, std::sqrt(cXX) * 1e+4); // convert cm to um - fRegistry.fill(HIST("MCHMID/hDCAyResolutionvsPt"), pt, std::sqrt(cYY) * 1e+4); // convert cm to um - fRegistry.fill(HIST("MCHMID/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um + fRegistry.fill(HIST("MCHMID/hDCAxResolutionvsPt"), pt, std::sqrt(cXXatDCA) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MCHMID/hDCAyResolutionvsPt"), pt, std::sqrt(cYYatDCA) * 1e+4); // convert cm to um + fRegistry.fill(HIST("MCHMID/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um } } } @@ -563,10 +623,11 @@ struct skimmerPrimaryMuon { } std::unordered_map map_mfttrackcovs; - std::vector> vec_min_chi2MatchMCHMFT; // std::pair -> chi2MatchMCHMFT; + std::vector> vec_min_chi2MatchMCHMFT; + std::unordered_map map_diff_chi2MatchMCHMFT; // map fwdtrack.globalIndex -> diff chi2 template - void findBestMatchPerMCHMID(TCollision const& collision, TFwdTrack const& fwdtrack, TFwdTracks const& fwdtracks, TMFTTracks const&) + void findBestMatchPerMCHMID(TCollision const&, TFwdTrack const& fwdtrack, TFwdTracks const& fwdtracks, TMFTTracks const&) { if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { return; @@ -581,73 +642,43 @@ struct skimmerPrimaryMuon { // LOGF(info, "stanadalone: muon.globalIndex() = %d, muon.chi2MatchMCHMFT() = %f", muon.globalIndex(), muon.chi2MatchMCHMFT()); // LOGF(info, "muons_per_MCHMID.size() = %d", muons_per_MCHMID.size()); - o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, mZShift); - float etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); - float phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMID); - - o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, mZShift); - float dcaX_Matched = propmuonAtDCA_Matched.getX() - collision.posX(); - float dcaY_Matched = propmuonAtDCA_Matched.getY() - collision.posY(); - float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); - float pDCA = fwdtrack.p() * dcaXY_Matched; - float min_chi2MatchMCHMFT = 1e+10; - std::tuple tupleIds_at_min_chi2mftmch; + std::tuple tupleId_at_min_chi2mftmch; + std::vector vec_chi2tmp; + vec_chi2tmp.reserve(muons_per_MCHMID.size()); + for (const auto& muon_tmp : muons_per_MCHMID) { if (muon_tmp.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { auto tupleId = std::make_tuple(muon_tmp.globalIndex(), muon_tmp.matchMCHTrackId(), muon_tmp.matchMFTTrackId()); - auto mchtrack = muon_tmp.template matchMCHTrack_as(); // MCH-MID - auto mfttrack = muon_tmp.template matchMFTTrack_as(); // MFTsa + // auto mchtrack = muon_tmp.template matchMCHTrack_as(); // MCH-MID + // auto mfttrack = muon_tmp.template matchMFTTrack_as(); // MFTsa - if (muon_tmp.chi2() < 0.f || muon_tmp.chi2MatchMCHMFT() < 0.f || muon_tmp.chi2MatchMCHMID() < 0.f || mfttrack.chi2() < 0.f) { // reject negative chi2, i.e. wrong. - // LOGF(info, "reject: muon_tmp.globalIndex() = %d, muon_tmp.chi2MatchMCHMFT() = %f, muon_tmp.chi2MatchMCHMID() = %f, muon_tmp.chi2() = %f, mfttrack.chi2() = %f", muon_tmp.globalIndex(), muon_tmp.chi2MatchMCHMFT(), muon_tmp.chi2MatchMCHMID(), muon_tmp.chi2(), mfttrack.chi2()); + if (muon_tmp.chi2MatchMCHMFT() < 0.f) { // reject negative chi2, i.e. wrong. continue; } - o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(muon_tmp, muon_tmp, collision, propagationPoint::kToVertex, matchingZ, mBz, mZShift); - float pt = propmuonAtPV.getPt(); - float eta = propmuonAtPV.getEta(); - float phi = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phi); - - if (refitGlobalMuon) { - pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); - } - - float deta = etaMatchedMCHMID - eta; - float dphi = phiMatchedMCHMID - phi; - o2::math_utils::bringToPMPi(dphi); - int ndf = 2 * (mchtrack.nClusters() + mfttrack.nClusters()) - 5; - - float dcaX = propmuonAtPV.getX() - collision.posX(); - float dcaY = propmuonAtPV.getY() - collision.posY(); - float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - - if (cfgApplyPreselectionInBestMatch) { - if (!isSelected(pt, eta, muon_tmp.rAtAbsorberEnd(), pDCA, muon_tmp.chi2() / ndf, muon_tmp.trackType(), dcaXY)) { - continue; - } - if (std::sqrt(std::pow(deta / maxDEta, 2) + std::pow(dphi / maxDPhi, 2)) > 1.f) { - continue; - } - if (muon_tmp.chi2MatchMCHMFT() > maxMatchingChi2MCHMFT) { - continue; - } - if (cfgRequireSameSign && (mfttrack.sign() != mchtrack.sign())) { - continue; - } - } - + vec_chi2tmp.emplace_back(muon_tmp.chi2MatchMCHMFT()); if (0.f < muon_tmp.chi2MatchMCHMFT() && muon_tmp.chi2MatchMCHMFT() < min_chi2MatchMCHMFT) { min_chi2MatchMCHMFT = muon_tmp.chi2MatchMCHMFT(); - tupleIds_at_min_chi2mftmch = tupleId; + tupleId_at_min_chi2mftmch = tupleId; } } } - vec_min_chi2MatchMCHMFT.emplace_back(tupleIds_at_min_chi2mftmch); + vec_min_chi2MatchMCHMFT.emplace_back(tupleId_at_min_chi2mftmch); + + float diff_chi2 = 1e+10; + std::nth_element(vec_chi2tmp.begin(), vec_chi2tmp.begin() + 1, vec_chi2tmp.end()); // sort only 0, 1. + if (vec_chi2tmp.size() >= 2) { + diff_chi2 = vec_chi2tmp[1] - vec_chi2tmp[0]; + } else { + diff_chi2 = 1e+10; + } + map_diff_chi2MatchMCHMFT[std::get<0>(tupleId_at_min_chi2mftmch)] = diff_chi2; - // LOGF(info, "min: muon_tmp.globalIndex() = %d, muon_tmp.matchMCHTrackId() = %d, muon_tmp.matchMFTTrackId() = %d, muon_tmp.chi2MatchMCHMFT() = %f", std::get<0>(tupleIds_at_min), std::get<1>(tupleIds_at_min), std::get<2>(tupleIds_at_min), min_chi2MatchMCHMFT); + vec_chi2tmp.clear(); + vec_chi2tmp.shrink_to_fit(); + + // LOGF(info, "min: muon_tmp.globalIndex() = %d, muon_tmp.matchMCHTrackId() = %d, muon_tmp.matchMFTTrackId() = %d, muon_tmp.chi2MatchMCHMFT() = %f", std::get<0>(tupleId_at_min), std::get<1>(tupleId_at_min), std::get<2>(tupleId_at_min), min_chi2MatchMCHMFT); } SliceCache cache; @@ -685,9 +716,16 @@ struct skimmerPrimaryMuon { continue; } - // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // continue; - // } + if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + continue; + } + + if (cfgKeepOnlyGLmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + continue; + } + if (cfgKeepOnlySAmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { + continue; + } if (!fillFwdTrackTable(collision, fwdtrack, nullptr, false)) { continue; @@ -731,6 +769,7 @@ struct skimmerPrimaryMuon { map_mfttrackcovs.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); + map_diff_chi2MatchMCHMFT.clear(); } PROCESS_SWITCH(skimmerPrimaryMuon, processRec_SA, "process reconstructed info", false); @@ -771,9 +810,16 @@ struct skimmerPrimaryMuon { continue; } - // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // continue; - // } + if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + continue; + } + + if (cfgKeepOnlyGLmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + continue; + } + if (cfgKeepOnlySAmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { + continue; + } if (!fillFwdTrackTable(collision, fwdtrack, nullptr, mapAmb[fwdtrack.globalIndex()])) { continue; @@ -818,6 +864,7 @@ struct skimmerPrimaryMuon { map_mfttrackcovs.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); + map_diff_chi2MatchMCHMFT.clear(); } PROCESS_SWITCH(skimmerPrimaryMuon, processRec_TTCA, "process reconstructed info", false); @@ -860,9 +907,9 @@ struct skimmerPrimaryMuon { // continue; // } - // // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // // continue; - // // } + // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + // continue; + // } // if (!fillFwdTrackTable(collision, fwdtrack, mftCovs, mapAmb[fwdtrack.globalIndex()])) { // continue; @@ -907,6 +954,7 @@ struct skimmerPrimaryMuon { // map_mfttrackcovs.clear(); // vec_min_chi2MatchMCHMFT.clear(); // vec_min_chi2MatchMCHMFT.shrink_to_fit(); + // map_diff_chi2MatchMCHMFT.clear(); // } // PROCESS_SWITCH(skimmerPrimaryMuon, processRec_TTCA_withMFTCov, "process reconstructed info", false); @@ -930,7 +978,7 @@ struct skimmerPrimaryMuon { continue; } - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; } @@ -939,9 +987,16 @@ struct skimmerPrimaryMuon { if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { continue; } - // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // continue; - // } + if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + continue; + } + + if (cfgKeepOnlyGLmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + continue; + } + if (cfgKeepOnlySAmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { + continue; + } if (!fillFwdTrackTable(collision, fwdtrack, nullptr, false)) { continue; @@ -985,6 +1040,7 @@ struct skimmerPrimaryMuon { map_mfttrackcovs.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); + map_diff_chi2MatchMCHMFT.clear(); } PROCESS_SWITCH(skimmerPrimaryMuon, processRec_SA_SWT, "process reconstructed info only with standalone", false); @@ -1014,7 +1070,7 @@ struct skimmerPrimaryMuon { if (!collision.isSelected()) { continue; } - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; } @@ -1024,9 +1080,16 @@ struct skimmerPrimaryMuon { if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { continue; } - // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // continue; - // } + if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + continue; + } + + if (cfgKeepOnlyGLmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + continue; + } + if (cfgKeepOnlySAmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { + continue; + } if (!fillFwdTrackTable(collision, fwdtrack, nullptr, mapAmb[fwdtrack.globalIndex()])) { continue; @@ -1071,6 +1134,7 @@ struct skimmerPrimaryMuon { map_mfttrackcovs.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); + map_diff_chi2MatchMCHMFT.clear(); } PROCESS_SWITCH(skimmerPrimaryMuon, processRec_TTCA_SWT, "process reconstructed info", false); @@ -1103,7 +1167,7 @@ struct skimmerPrimaryMuon { // if (!collision.isSelected()) { // continue; // } - // if (collision.swtaliastmp_raw() == 0) { + // if (collision.triggerMask_raw() == 0) { // continue; // } @@ -1113,9 +1177,9 @@ struct skimmerPrimaryMuon { // if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { // continue; // } - // // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // // continue; - // // } + // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + // continue; + // } // if (!fillFwdTrackTable(collision, fwdtrack, mftCovs, mapAmb[fwdtrack.globalIndex()])) { // continue; @@ -1160,6 +1224,7 @@ struct skimmerPrimaryMuon { // map_mfttrackcovs.clear(); // vec_min_chi2MatchMCHMFT.clear(); // vec_min_chi2MatchMCHMFT.shrink_to_fit(); + // map_diff_chi2MatchMCHMFT.clear(); // } // PROCESS_SWITCH(skimmerPrimaryMuon, processRec_TTCA_SWT_withMFTCov, "process reconstructed info", false); @@ -1193,9 +1258,16 @@ struct skimmerPrimaryMuon { if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { continue; } - // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // continue; - // } + if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + continue; + } + + if (cfgKeepOnlyGLmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + continue; + } + if (cfgKeepOnlySAmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { + continue; + } if (!fillFwdTrackTable(collision, fwdtrack, nullptr, false)) { continue; @@ -1239,6 +1311,7 @@ struct skimmerPrimaryMuon { map_mfttrackcovs.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); + map_diff_chi2MatchMCHMFT.clear(); } PROCESS_SWITCH(skimmerPrimaryMuon, processMC_SA, "process reconstructed and MC info", false); @@ -1281,9 +1354,16 @@ struct skimmerPrimaryMuon { if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { continue; } - // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // continue; - // } + if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + continue; + } + + if (cfgKeepOnlyGLmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + continue; + } + if (cfgKeepOnlySAmuons && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { + continue; + } if (!fillFwdTrackTable(collision, fwdtrack, nullptr, mapAmb[fwdtrack.globalIndex()])) { continue; @@ -1328,6 +1408,7 @@ struct skimmerPrimaryMuon { map_mfttrackcovs.clear(); vec_min_chi2MatchMCHMFT.clear(); vec_min_chi2MatchMCHMFT.shrink_to_fit(); + map_diff_chi2MatchMCHMFT.clear(); } PROCESS_SWITCH(skimmerPrimaryMuon, processMC_TTCA, "process reconstructed and MC info", false); @@ -1373,9 +1454,9 @@ struct skimmerPrimaryMuon { // if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { // continue; // } - // // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { - // // continue; - // // } + // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && std::find(vec_min_chi2MatchMCHMFT.begin(), vec_min_chi2MatchMCHMFT.end(), std::make_tuple(fwdtrack.globalIndex(), fwdtrack.matchMCHTrackId(), fwdtrack.matchMFTTrackId())) == vec_min_chi2MatchMCHMFT.end()) { + // continue; + // } // if (!fillFwdTrackTable(collision, fwdtrack, mftCovs, mapAmb[fwdtrack.globalIndex()])) { // continue; @@ -1420,6 +1501,7 @@ struct skimmerPrimaryMuon { // map_mfttrackcovs.clear(); // vec_min_chi2MatchMCHMFT.clear(); // vec_min_chi2MatchMCHMFT.shrink_to_fit(); + // map_diff_chi2MatchMCHMFT.clear(); // } // PROCESS_SWITCH(skimmerPrimaryMuon, processMC_TTCA_withMFTCov, "process reconstructed and MC with MFTCov info", false); diff --git a/PWGEM/Dilepton/TableProducer/skimmerPrimaryMuonQC.cxx b/PWGEM/Dilepton/TableProducer/skimmerPrimaryMuonQC.cxx deleted file mode 100644 index d5f410eafaa..00000000000 --- a/PWGEM/Dilepton/TableProducer/skimmerPrimaryMuonQC.cxx +++ /dev/null @@ -1,797 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \brief write relevant information for muons. -/// \author daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/DataModel/dileptonTables.h" - -#include "Common/Core/fwdtrackUtilities.h" -#include "Common/DataModel/CollisionAssociationTables.h" -#include "Common/DataModel/EventSelection.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include - -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::soa; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace o2::constants::physics; -using namespace o2::aod::fwdtrackutils; - -struct skimmerPrimaryMuonQC { - using MyCollisions = soa::Join; - using MyCollisionsWithSWT = soa::Join; - - using MyFwdTracks = soa::Join; // muon tracks are repeated. i.e. not exclusive. - using MyFwdTrack = MyFwdTracks::iterator; - - using MyFwdTracksMC = soa::Join; - using MyFwdTrackMC = MyFwdTracksMC::iterator; - - using MFTTracksMC = soa::Join; - using MFTTrackMC = MFTTracksMC::iterator; - - Produces emprimarymuons; - Produces emprimarymuonscov; - - // Configurables - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable fillQAHistograms{"fillQAHistograms", false, "flag to fill QA histograms"}; - - // for z shift for propagation - Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift"}; - Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; - Configurable cfgManualZShift{"cfgManualZShift", 0, "manual z-shift for propagation of global muon to PV"}; - Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; - Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; - - struct : ConfigurableGroup { // tight cut - std::string prefix = "tagMuonCut"; - Configurable minPt{"minPt", 0.8, "min pt for muon"}; - Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; - Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; - Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; - Configurable minRabs{"minRabs", 27.6, "min. R at absorber end for global muon (min. eta = -3.6)"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 - Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; - Configurable maxDCAxy{"maxDCAxy", 0.06, "max. DCAxy for global muons"}; - Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 40.f, "max. chi2 for MCH-MFT matching"}; - Configurable maxChi2{"maxChi2", 4.f, "max. chi2/ndf for global muon"}; - // Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; - // Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; - Configurable maxDEta{"maxDEta", 0.08, "max. deta between MFT-MCH-MID and MCH-MID"}; - Configurable maxDPhi{"maxDPhi", 0.08, "max. dphi between MFT-MCH-MID and MCH-MID"}; - } tagMuonCut; - - struct : ConfigurableGroup { // loose cut - std::string prefix = "probeMuonCut"; - Configurable minPt{"minPt", 0.01, "min pt for muon"}; - Configurable maxPt{"maxPt", 1e+10, "max pt for muon"}; - Configurable minEtaSA{"minEtaSA", -4.0, "min. eta acceptance for MFT-MCH"}; - Configurable maxEtaSA{"maxEtaSA", -2.5, "max. eta acceptance for MFT-MCH"}; - Configurable minEtaGL{"minEtaGL", -3.6, "min. eta acceptance for MFT-MCH-MID"}; - Configurable maxEtaGL{"maxEtaGL", -2.5, "max. eta acceptance for MFT-MCH-MID"}; - Configurable minRabs{"minRabs", 17.6, "min. R at absorber end for global muon (min. eta = -3.6)"}; // std::tan(2.f * std::atan(std::exp(- -3.6)) ) * -505. = 27.6 - Configurable midRabs{"midRabs", 26.5, "middle R at absorber end for pDCA cut"}; - Configurable maxRabs{"maxRabs", 89.5, "max. R at absorber end"}; - Configurable maxDCAxy{"maxDCAxy", 1.f, "max. DCAxy for global muons"}; - Configurable maxPDCAforLargeR{"maxPDCAforLargeR", 324.f, "max. pDCA for large R at absorber end"}; - Configurable maxPDCAforSmallR{"maxPDCAforSmallR", 594.f, "max. pDCA for small R at absorber end"}; - Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 100, "max. chi2 for MCH-MFT matching"}; - Configurable maxChi2{"maxChi2", 1e+10, "max. chi2/ndf for global muon"}; - // Configurable minNclsMFT{"minNclsMFT", 5, "min ncluster of MFT"}; - // Configurable minNclsMCH{"minNclsMCH", 5, "min ncluster of MCH"}; - Configurable maxDEta{"maxDEta", 1e+10, "max. deta between MFT-MCH-MID and MCH-MID"}; - Configurable maxDPhi{"maxDPhi", 1e+10, "max. dphi between MFT-MCH-MID and MCH-MID"}; - } probeMuonCut; - - struct : ConfigurableGroup { - std::string prefix = "pairCuts"; - Configurable minMass{"minMass", 0.21, "min mass"}; - Configurable maxMass{"maxMass", 0.30, "max mass"}; - } pairCuts; - - o2::ccdb::CcdbApi ccdbApi; - Service ccdb; - int mRunNumber = 0; - float mBz = 0; - float mZShift = 0; - - HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - // static constexpr std::string_view muon_types[5] = {"MFTMCHMID/", "MFTMCHMIDOtherMatch/", "MFTMCH/", "MCHMID/", "MCH/"}; - - void init(InitContext&) - { - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - ccdbApi.init(ccdburl); - - if (fillQAHistograms) { - addHistograms(); - } - mRunNumber = 0; - mBz = 0; - mZShift = 0; - } - - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - mRunNumber = bc.runNumber(); - - std::map metadata; - auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, mRunNumber); - auto ts = soreor.first; - auto grpmag = ccdbApi.retrieveFromTFileAny(grpmagPath, metadata, ts); - o2::base::Propagator::initFieldFromGRP(grpmag); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - ccdb->get(geoPath); - } - o2::mch::TrackExtrap::setField(); - const double centerMFT[3] = {0, 0, -61.4}; - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - mBz = field->getBz(centerMFT); // Get field at centre of MFT - LOGF(info, "Bz at center of MFT = %f kZG", mBz); - - if (cfgApplyZShiftFromCCDB) { - auto* zShift = ccdb->getForTimeStamp>(cfgZShiftPath, bc.timestamp()); - if (zShift != nullptr && !zShift->empty()) { - LOGF(info, "reading z shift %f from %s", (*zShift)[0], cfgZShiftPath.value); - mZShift = (*zShift)[0]; - } else { - LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", cfgZShiftPath.value); - mZShift = 0; - } - } else { - LOGF(info, "z shift is manually set to %f cm", cfgManualZShift.value); - mZShift = cfgManualZShift; - } - } - - void addHistograms() - { - // auto hMuonType = fRegistry.add("hMuonType", "muon type", kTH1F, {{5, -0.5f, 4.5f}}, false); - // hMuonType->GetXaxis()->SetBinLabel(1, "MFT-MCH-MID (global muon)"); - // hMuonType->GetXaxis()->SetBinLabel(2, "MFT-MCH-MID (global muon other match)"); - // hMuonType->GetXaxis()->SetBinLabel(3, "MFT-MCH"); - // hMuonType->GetXaxis()->SetBinLabel(4, "MCH-MID"); - // hMuonType->GetXaxis()->SetBinLabel(5, "MCH standalone"); - - // fRegistry.add("MFTMCHMID/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{200, 0.0f, 10}}, false); - // fRegistry.add("MFTMCHMID/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {80, -4.f, -2.f}}, false); - // fRegistry.add("MFTMCHMID/hEtaPhi_MatchedMCHMID", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {80, -4.f, -2.f}}, false); - // fRegistry.add("MFTMCHMID/hDeltaPt_Pt", "#Deltap_{T}/p_{T} vs. p_{T};p_{T}^{gl} (GeV/c);(p_{T}^{sa} - p_{T}^{gl})/p_{T}^{gl}", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - // fRegistry.add("MFTMCHMID/hDeltaEta_Pt", "#Delta#eta vs. p_{T};p_{T}^{gl} (GeV/c);#Delta#eta", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - // fRegistry.add("MFTMCHMID/hDeltaPhi_Pt", "#Delta#varphi vs. p_{T};p_{T}^{gl} (GeV/c);#Delta#varphi (rad.)", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - // fRegistry.add("MFTMCHMID/hSign", "sign;sign", kTH1F, {{3, -1.5, +1.5}}, false); - // fRegistry.add("MFTMCHMID/hNclusters", "Nclusters;Nclusters", kTH1F, {{21, -0.5f, 20.5}}, false); - // fRegistry.add("MFTMCHMID/hNclustersMFT", "NclustersMFT;Nclusters MFT", kTH1F, {{11, -0.5f, 10.5}}, false); - // fRegistry.add("MFTMCHMID/hRatAbsorberEnd", "R at absorber end;R at absorber end (cm)", kTH1F, {{100, 0.0f, 100}}, false); - // fRegistry.add("MFTMCHMID/hPDCA_Rabs", "pDCA vs. Rabs;R at absorber end (cm);p #times DCA (GeV/c #upoint cm)", kTH2F, {{100, 0, 100}, {100, 0.0f, 1000}}, false); - // fRegistry.add("MFTMCHMID/hChi2", "chi2;chi2/ndf", kTH1F, {{200, 0.0f, 20}}, false); - // fRegistry.add("MFTMCHMID/hChi2MFT", "chi2 MFT;chi2 MFT/ndf", kTH1F, {{200, 0.0f, 20}}, false); - // fRegistry.add("MFTMCHMID/hChi2MatchMCHMID", "chi2 match MCH-MID;chi2", kTH1F, {{200, 0.0f, 20}}, false); - // fRegistry.add("MFTMCHMID/hChi2MatchMCHMFT", "chi2 match MCH-MFT;chi2", kTH1F, {{200, 0.0f, 100}}, false); - // fRegistry.add("MFTMCHMID/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", kTH2F, {{200, -1, 1}, {200, -1, +1}}, false); - // fRegistry.add("MFTMCHMID/hDCAxy2DinSigma", "DCA x vs. y in sigma;DCA_{x} (#sigma);DCA_{y} (#sigma)", kTH2F, {{200, -10, 10}, {200, -10, +10}}, false); - // fRegistry.add("MFTMCHMID/hDCAxy", "DCAxy;DCA_{xy} (cm);", kTH1F, {{100, 0, 1}}, false); - // fRegistry.add("MFTMCHMID/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{100, 0, 1}, {200, -0.1, 0.1}}, false); - // fRegistry.add("MFTMCHMID/hDCAxyinSigma", "DCAxy in sigma;DCA_{xy} (#sigma);", kTH1F, {{100, 0, 10}}, false); - // fRegistry.add("MFTMCHMID/hDCAx_PosZ", "DCAx vs. posZ;Z_{vtx} (cm);DCA_{x} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); - // fRegistry.add("MFTMCHMID/hDCAy_PosZ", "DCAy vs. posZ;Z_{vtx} (cm);DCA_{y} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); - // fRegistry.add("MFTMCHMID/hDCAx_Phi", "DCAx vs. #varphi;#varphi (rad.);DCA_{x} (cm)", kTH2F, {{90, 0, 2 * M_PI}, {400, -0.2, +0.2}}, false); - // fRegistry.add("MFTMCHMID/hDCAy_Phi", "DCAy vs. #varphi;#varphi (rad.);DCA_{y} (cm)", kTH2F, {{90, 0, 2 * M_PI}, {400, -0.2, +0.2}}, false); - // fRegistry.add("MFTMCHMID/hNmu", "#mu multiplicity;N_{#mu} per collision", kTH1F, {{21, -0.5, 20.5}}, false); - - // fRegistry.addClone("MFTMCHMID/", "MCHMID/"); - // fRegistry.add("MFTMCHMID/hDCAxResolutionvsPt", "DCA_{x} vs. p_{T};p_{T} (GeV/c);DCA_{x} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 500}}, false); - // fRegistry.add("MFTMCHMID/hDCAyResolutionvsPt", "DCA_{y} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 500}}, false); - // fRegistry.add("MFTMCHMID/hDCAxyResolutionvsPt", "DCA_{xy} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 500}}, false); - // fRegistry.add("MCHMID/hDCAxResolutionvsPt", "DCA_{x} vs. p_{T};p_{T} (GeV/c);DCA_{x} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 5e+5}}, false); - // fRegistry.add("MCHMID/hDCAyResolutionvsPt", "DCA_{y} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 5e+5}}, false); - // fRegistry.add("MCHMID/hDCAxyResolutionvsPt", "DCA_{xy} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 5e+5}}, false); - - fRegistry.add("Pair/uls/gl_gl/hMvsPt", "dimuon;m_{#mu#mu} (GeV/c^{2});p_{T,#mu} (GeV/c);", kTH2F, {{380, 0.2, 4.f}, {100, 0, 10}}, false); - fRegistry.add("Pair/uls/gl_sa/hMvsPt", "dimuon;m_{#mu#mu} (GeV/c^{2});p_{T,#mu} (GeV/c);", kTH2F, {{380, 0.2, 4.f}, {100, 0, 10}}, false); - } - - struct Muon { - int globalIndex = -1; - int collisionId = -1; - int matchMCHTrackId = -1; - int matchMFTTrackId = -1; - uint8_t trackType = 99; - int8_t sign = 0; - float pt = 0; - float eta = 0; - float phi = 0; - float dcaX = 0; // in cm - float dcaY = 0; // in cm - float dcaXY = 0; // in cm - float cXX = 0; - float cYY = 0; - float cXY = 0; - float rAtAbsorberEnd = 0; - float pDCA = 0; - float chi2ndf = 0; - float chi2MatchMCHMID = 0; - - // only for global muons - float ptMatchedMCHMID = 0; - float etaMatchedMCHMID = 0; - float phiMatchedMCHMID = 0; - float chi2MatchMCHMFT = 0; - float chi2mft = -999.f; - uint64_t mftClusterSizesAndTrackFlags = 0; - }; - - bool isSelected(Muon const& muon) - { - if (muon.pt < probeMuonCut.minPt || probeMuonCut.maxPt < muon.pt) { - return false; - } - - if (muon.rAtAbsorberEnd < probeMuonCut.minRabs || probeMuonCut.maxRabs < muon.rAtAbsorberEnd) { - return false; - } - - if (muon.trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - if (muon.eta < probeMuonCut.minEtaGL || probeMuonCut.maxEtaGL < muon.eta) { - return false; - } - if (probeMuonCut.maxDCAxy < muon.dcaXY) { - return false; - } - if (probeMuonCut.maxMatchingChi2MCHMFT < muon.chi2MatchMCHMFT) { - return false; - } - } else if (muon.trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - if (muon.eta < probeMuonCut.minEtaSA || probeMuonCut.maxEtaSA < muon.eta) { - return false; - } - if (muon.rAtAbsorberEnd < probeMuonCut.midRabs ? muon.pDCA > probeMuonCut.maxPDCAforSmallR : muon.pDCA > probeMuonCut.maxPDCAforLargeR) { - return false; - } - } else { - return false; - } - - return true; - } - - bool isSelectedTight(Muon const& muon) - { - if (muon.trackType != static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { // tag muon should be tight. - return false; - } - - if (muon.pt < tagMuonCut.minPt || tagMuonCut.maxPt < muon.pt) { - return false; - } - - if (muon.rAtAbsorberEnd < tagMuonCut.minRabs || tagMuonCut.maxRabs < muon.rAtAbsorberEnd) { - return false; - } - - if (muon.chi2ndf < 0.f || tagMuonCut.maxChi2 < muon.chi2ndf) { - return false; - } - - if (muon.eta < tagMuonCut.minEta || tagMuonCut.maxEta < muon.eta) { - return false; - } - - if (tagMuonCut.maxDCAxy < muon.dcaXY) { - return false; - } - - if (tagMuonCut.maxMatchingChi2MCHMFT < muon.chi2MatchMCHMFT) { - return false; - } - - float deta = muon.etaMatchedMCHMID - muon.eta; - float dphi = muon.phiMatchedMCHMID - muon.phi; - // LOGF(info, "muon.trackType = %d, deta = %f, dphi = %f", muon.trackType, deta, dphi); - if (std::sqrt(std::pow(deta / tagMuonCut.maxDEta, 2) + std::pow(dphi / tagMuonCut.maxDPhi, 2)) > 1.f) { - return false; - } - - return true; - } - - template - bool fillMuonInfo(TCollision const& collision, TFwdTrack fwdtrack) - { - if (fwdtrack.trackType() != static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) && fwdtrack.trackType() != static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - return false; - } - - if (fwdtrack.chi2MatchMCHMID() < 0.f) { // this should never happen. only for protection. - return false; - } - - if (fwdtrack.chi2() < 0.f) { // this should never happen. only for protection. - return false; - } - - o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, mZShift); - float pt = propmuonAtPV.getPt(); - float eta = propmuonAtPV.getEta(); - float phi = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phi); - - float dcaX = propmuonAtPV.getX() - collision.posX(); - float dcaY = propmuonAtPV.getY() - collision.posY(); - float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - float rAtAbsorberEnd = fwdtrack.rAtAbsorberEnd(); // this works only for GlobalMuonTrack - float cXX = propmuonAtPV.getSigma2X(); - float cYY = propmuonAtPV.getSigma2Y(); - float cXY = propmuonAtPV.getSigmaXY(); - - float pDCA = propmuonAtPV.getP() * dcaXY; - int nClustersMFT = 0; - float ptMatchedMCHMID = propmuonAtPV.getPt(); - float etaMatchedMCHMID = propmuonAtPV.getEta(); - float phiMatchedMCHMID = propmuonAtPV.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMID); - float chi2mft = -999.f; - uint64_t mftClusterSizesAndTrackFlags = 0; - int ndf_mchmft = 1; - - if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { - if (fwdtrack.chi2MatchMCHMFT() < 0.f) { - return false; - } // Users have to decide the best match between MFT and MCH-MID at analysis level. The same global muon is repeatedly stored. - - auto mchtrack = fwdtrack.template matchMCHTrack_as(); // MCH-MID - auto mfttrack = fwdtrack.template matchMFTTrack_as(); // MFTsa - if (mfttrack.chi2() < 0.f) { - return false; - } - - if constexpr (isMC) { - if (!mfttrack.has_mcParticle() || !mchtrack.has_mcParticle() || !fwdtrack.has_mcParticle()) { - return false; - } - // auto mcParticle_MFTMCHMID = fwdtrack.template mcParticle_as(); // this is identical to mcParticle_MCHMID - auto mcParticle_MCHMID = mchtrack.template mcParticle_as(); // this is identical to mcParticle_MFTMCHMID - auto mcParticle_MFT = mfttrack.template mcParticle_as(); - } - - nClustersMFT = mfttrack.nClusters(); - mftClusterSizesAndTrackFlags = mfttrack.mftClusterSizesAndTrackFlags(); - ndf_mchmft = 2.f * (mchtrack.nClusters() + nClustersMFT) - 5.f; - chi2mft = mfttrack.chi2(); - - o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToVertex, matchingZ, mBz, mZShift); - ptMatchedMCHMID = propmuonAtPV_Matched.getPt(); - etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); - phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); - o2::math_utils::bringTo02Pi(phiMatchedMCHMID); - - o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, mZShift); - float dcaX_Matched = propmuonAtDCA_Matched.getX() - collision.posX(); - float dcaY_Matched = propmuonAtDCA_Matched.getY() - collision.posY(); - float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); - pDCA = mchtrack.p() * dcaXY_Matched; - - if (refitGlobalMuon) { - pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); - } - } else if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { - o2::dataformats::GlobalFwdTrack propmuonAtRabs = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToRabs, matchingZ, mBz, mZShift); // this is necessary only for MuonStandaloneTrack - float xAbs = propmuonAtRabs.getX(); - float yAbs = propmuonAtRabs.getY(); - rAtAbsorberEnd = std::sqrt(xAbs * xAbs + yAbs * yAbs); // Redo propagation only for muon tracks // propagation of MFT tracks alredy done in reconstruction - ndf_mchmft = 1; // chi2 is already normalized by ndf for MCH-MID tracks. - - o2::dataformats::GlobalFwdTrack propmuonAtDCA = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToDCA, matchingZ, mBz, mZShift); - cXX = propmuonAtDCA.getSigma2X(); - cYY = propmuonAtDCA.getSigma2Y(); - cXY = propmuonAtDCA.getSigmaXY(); - dcaX = propmuonAtDCA.getX() - collision.posX(); - dcaY = propmuonAtDCA.getY() - collision.posY(); - dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - pDCA = fwdtrack.p() * dcaXY; - } else { - return false; - } - - Muon muon; - muon.globalIndex = fwdtrack.globalIndex(); - muon.collisionId = collision.globalIndex(); - muon.trackType = fwdtrack.trackType(); - muon.sign = fwdtrack.sign(); - muon.pt = pt; - muon.eta = eta; - muon.phi = phi; - muon.dcaX = dcaX; - muon.dcaY = dcaY; - muon.dcaXY = dcaXY; - muon.cXX = cXX; - muon.cYY = cYY; - muon.cXY = cXY; - muon.rAtAbsorberEnd = rAtAbsorberEnd; - muon.pDCA = pDCA; - muon.chi2ndf = fwdtrack.chi2() / ndf_mchmft; - muon.ptMatchedMCHMID = ptMatchedMCHMID; - muon.etaMatchedMCHMID = etaMatchedMCHMID; - muon.phiMatchedMCHMID = phiMatchedMCHMID; - muon.chi2mft = chi2mft; - muon.matchMCHTrackId = fwdtrack.matchMCHTrackId(); - muon.matchMFTTrackId = fwdtrack.matchMFTTrackId(); - muon.mftClusterSizesAndTrackFlags = mftClusterSizesAndTrackFlags; - - vecMuons.emplace_back(muon); - return true; - } - - template - bool fillFwdTrackTable(TCollision const& collision, TMuon const& muon, TFwdTrack const& fwdtrack) - { - emprimarymuons(collision.globalIndex(), fwdtrack.globalIndex(), fwdtrack.matchMFTTrackId(), fwdtrack.matchMCHTrackId(), fwdtrack.trackType(), - muon.pt, muon.eta, muon.phi, fwdtrack.sign(), muon.dcaX, muon.dcaY, muon.cXX, muon.cYY, muon.cXY, muon.ptMatchedMCHMID, muon.etaMatchedMCHMID, muon.phiMatchedMCHMID, - fwdtrack.nClusters(), muon.pDCA, muon.rAtAbsorberEnd, fwdtrack.chi2(), fwdtrack.chi2MatchMCHMID(), fwdtrack.chi2MatchMCHMFT(), - fwdtrack.mchBitMap(), fwdtrack.midBitMap(), fwdtrack.midBoards(), muon.mftClusterSizesAndTrackFlags, muon.chi2mft, true, false); - - // if (fillQAHistograms) { - // fRegistry.fill(HIST("hMuonType"), fwdtrack.trackType()); - // if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { - // fRegistry.fill(HIST("MFTMCHMID/hPt"), pt); - // fRegistry.fill(HIST("MFTMCHMID/hEtaPhi"), phi, eta); - // fRegistry.fill(HIST("MFTMCHMID/hEtaPhi_MatchedMCHMID"), phiMatchedMCHMID, etaMatchedMCHMID); - // fRegistry.fill(HIST("MFTMCHMID/hDeltaPt_Pt"), pt, dpt); - // fRegistry.fill(HIST("MFTMCHMID/hDeltaEta_Pt"), pt, deta); - // fRegistry.fill(HIST("MFTMCHMID/hDeltaPhi_Pt"), pt, dphi); - // fRegistry.fill(HIST("MFTMCHMID/hSign"), fwdtrack.sign()); - // fRegistry.fill(HIST("MFTMCHMID/hNclusters"), fwdtrack.nClusters()); - // fRegistry.fill(HIST("MFTMCHMID/hNclustersMFT"), nClustersMFT); - // fRegistry.fill(HIST("MFTMCHMID/hPDCA_Rabs"), rAtAbsorberEnd, pDCA); - // fRegistry.fill(HIST("MFTMCHMID/hRatAbsorberEnd"), rAtAbsorberEnd); - // fRegistry.fill(HIST("MFTMCHMID/hChi2"), fwdtrack.chi2() / ndf_mchmft); - // fRegistry.fill(HIST("MFTMCHMID/hChi2MFT"), chi2mft / ndf_mft); - // fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMID"), fwdtrack.chi2MatchMCHMID()); - // fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMFT"), fwdtrack.chi2MatchMCHMFT()); - // fRegistry.fill(HIST("MFTMCHMID/hDCAxy2D"), dcaX, dcaY); - // fRegistry.fill(HIST("MFTMCHMID/hDCAxy2DinSigma"), dcaX / std::sqrt(cXX), dcaY / std::sqrt(cYY)); - // fRegistry.fill(HIST("MFTMCHMID/hDCAxy"), dcaXY); - // fRegistry.fill(HIST("MFTMCHMID/hDCAxyz"), dcaXY, dcaZ); - // fRegistry.fill(HIST("MFTMCHMID/hDCAxyinSigma"), dcaXYinSigma); - // fRegistry.fill(HIST("MFTMCHMID/hDCAxResolutionvsPt"), pt, std::sqrt(cXX) * 1e+4); // convert cm to um - // fRegistry.fill(HIST("MFTMCHMID/hDCAyResolutionvsPt"), pt, std::sqrt(cYY) * 1e+4); // convert cm to um - // fRegistry.fill(HIST("MFTMCHMID/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um - // fRegistry.fill(HIST("MFTMCHMID/hDCAx_PosZ"), collision.posZ(), dcaX); - // fRegistry.fill(HIST("MFTMCHMID/hDCAy_PosZ"), collision.posZ(), dcaY); - // fRegistry.fill(HIST("MFTMCHMID/hDCAx_Phi"), phi, dcaX); - // fRegistry.fill(HIST("MFTMCHMID/hDCAy_Phi"), phi, dcaY); - // } else if (fwdtrack.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { - // fRegistry.fill(HIST("MCHMID/hPt"), pt); - // fRegistry.fill(HIST("MCHMID/hEtaPhi"), phi, eta); - // fRegistry.fill(HIST("MCHMID/hEtaPhi_MatchedMCHMID"), phiMatchedMCHMID, etaMatchedMCHMID); - // fRegistry.fill(HIST("MCHMID/hDeltaPt_Pt"), pt, dpt); - // fRegistry.fill(HIST("MCHMID/hDeltaEta_Pt"), pt, deta); - // fRegistry.fill(HIST("MCHMID/hDeltaPhi_Pt"), pt, dphi); - // fRegistry.fill(HIST("MCHMID/hSign"), fwdtrack.sign()); - // fRegistry.fill(HIST("MCHMID/hNclusters"), fwdtrack.nClusters()); - // fRegistry.fill(HIST("MCHMID/hNclustersMFT"), nClustersMFT); - // fRegistry.fill(HIST("MCHMID/hPDCA_Rabs"), rAtAbsorberEnd, pDCA); - // fRegistry.fill(HIST("MCHMID/hRatAbsorberEnd"), rAtAbsorberEnd); - // fRegistry.fill(HIST("MCHMID/hChi2"), fwdtrack.chi2()); - // fRegistry.fill(HIST("MCHMID/hChi2MFT"), chi2mft / ndf_mft); - // fRegistry.fill(HIST("MCHMID/hChi2MatchMCHMID"), fwdtrack.chi2MatchMCHMID()); - // fRegistry.fill(HIST("MCHMID/hChi2MatchMCHMFT"), fwdtrack.chi2MatchMCHMFT()); - // fRegistry.fill(HIST("MCHMID/hDCAxy2D"), dcaX, dcaY); - // fRegistry.fill(HIST("MCHMID/hDCAxy2DinSigma"), dcaX / std::sqrt(cXX), dcaY / std::sqrt(cYY)); - // fRegistry.fill(HIST("MCHMID/hDCAxy"), dcaXY); - // fRegistry.fill(HIST("MCHMID/hDCAxyz"), dcaXY, dcaZ); - // fRegistry.fill(HIST("MCHMID/hDCAxyinSigma"), dcaXYinSigma); - // fRegistry.fill(HIST("MCHMID/hDCAxResolutionvsPt"), pt, std::sqrt(cXX) * 1e+4); // convert cm to um - // fRegistry.fill(HIST("MCHMID/hDCAyResolutionvsPt"), pt, std::sqrt(cYY) * 1e+4); // convert cm to um - // fRegistry.fill(HIST("MCHMID/hDCAxyResolutionvsPt"), pt, sigma_dcaXY * 1e+4); // convert cm to um - // } - // } - return true; - } - - SliceCache cache; - Preslice perCollision = o2::aod::fwdtrack::collisionId; - Preslice fwdtrackIndicesPerCollision = aod::track_association::collisionId; - PresliceUnsorted fwdtrackIndicesPerFwdTrack = aod::track_association::fwdtrackId; - PresliceUnsorted fwdtracksPerMCHTrack = aod::fwdtrack::matchMCHTrackId; - - // Filter trackFilter = o2::aod::fwdtrack::trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) || o2::aod::fwdtrack::trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack); - // using filteredMyFwdTracks = soa::Filtered; - // Partition posTracks = o2::aod::fwdtrack::signed1Pt > 0.f; - // Partition negTracks = o2::aod::fwdtrack::signed1Pt < 0.f; - - std::vector vecMuons; - - void processRec(MyCollisions const& collisions, MyFwdTracks const& fwdtracks, aod::MFTTracks const&, aod::BCsWithTimestamps const&) - { - vecMuons.reserve(fwdtracks.size()); - - for (const auto& collision : collisions) { - auto bc = collision.template bc_as(); - initCCDB(bc); - - if (!collision.isSelected()) { - continue; - } - - auto fwdtracks_per_coll = fwdtracks.sliceBy(perCollision, collision.globalIndex()); - for (const auto& fwdtrack : fwdtracks_per_coll) { - fillMuonInfo(collision, fwdtrack); - } - - auto pos_muons_per_col = std::views::filter(vecMuons, [](Muon muon) { return muon.sign > 0; }); - auto neg_muons_per_col = std::views::filter(vecMuons, [](Muon muon) { return muon.sign < 0; }); - - // ULS - for (const auto& pos : pos_muons_per_col) { - if (!isSelectedTight(pos)) { // pos is tag, neg is probe - continue; - } - for (const auto& neg : neg_muons_per_col) { - if (!isSelected(neg)) { - continue; - } - ROOT::Math::PtEtaPhiMVector v1(pos.pt, pos.eta, pos.phi, o2::constants::physics::MassMuon); // tag - ROOT::Math::PtEtaPhiMVector v2(neg.pt, neg.eta, neg.phi, o2::constants::physics::MassMuon); // probe - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - if (neg.trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - if (pos.matchMCHTrackId == neg.matchMCHTrackId || pos.matchMFTTrackId == neg.matchMFTTrackId) { // this should not happen in ULS. only for protection. - continue; - } - fRegistry.fill(HIST("Pair/uls/gl_gl/hMvsPt"), v12.M(), v2.Pt()); - } else if (neg.trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - if (pos.matchMCHTrackId == neg.globalIndex) { // this should not happen in ULS. only for protection. - continue; - } - fRegistry.fill(HIST("Pair/uls/gl_sa/hMvsPt"), v12.M(), v2.Pt()); - } - if (pairCuts.minMass < v12.M() && v12.M() < pairCuts.maxMass) { - fillFwdTrackTable(collision, neg, fwdtracks.rawIteratorAt(neg.globalIndex)); - } - } // end of neg - } // end of pos - - // ULS - for (const auto& neg : neg_muons_per_col) { - if (!isSelectedTight(neg)) { // neg is tag, pos is probe - continue; - } - for (const auto& pos : pos_muons_per_col) { - if (!isSelected(pos)) { - continue; - } - ROOT::Math::PtEtaPhiMVector v1(neg.pt, neg.eta, neg.phi, o2::constants::physics::MassMuon); // tag - ROOT::Math::PtEtaPhiMVector v2(pos.pt, pos.eta, pos.phi, o2::constants::physics::MassMuon); // probe - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - if (pos.trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack)) { - if (pos.matchMCHTrackId == neg.matchMCHTrackId || pos.matchMFTTrackId == neg.matchMFTTrackId) { // this should not happen in ULS. only for protection. - continue; - } - fRegistry.fill(HIST("Pair/uls/gl_gl/hMvsPt"), v12.M(), v2.Pt()); - } else if (pos.trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack)) { - if (neg.matchMCHTrackId == pos.globalIndex) { // this should not happen in ULS. only for protection. - continue; - } - fRegistry.fill(HIST("Pair/uls/gl_sa/hMvsPt"), v12.M(), v2.Pt()); - } - if (pairCuts.minMass < v12.M() && v12.M() < pairCuts.maxMass) { - fillFwdTrackTable(collision, pos, fwdtracks.rawIteratorAt(pos.globalIndex)); - } - } // end of pos - } // end of neg - - } // end of collision loop - - vecMuons.clear(); - vecMuons.shrink_to_fit(); - } - PROCESS_SWITCH(skimmerPrimaryMuonQC, processRec, "process reconstructed info", false); - - void processRec_SWT(MyCollisionsWithSWT const& collisions, MyFwdTracks const& fwdtracks, aod::MFTTracks const&, aod::BCsWithTimestamps const&) - { - vecMuons.reserve(fwdtracks.size()); - - for (const auto& collision : collisions) { - const auto& bc = collision.template bc_as(); - initCCDB(bc); - - if (!collision.isSelected()) { - continue; - } - - if (collision.swtaliastmp_raw() == 0) { - continue; - } - - // const auto& fwdtracks_per_coll = fwdtracks.sliceBy(perCollision, collision.globalIndex()); - // for (const auto& fwdtrack : fwdtracks_per_coll) { - // if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { - // continue; - // } - - // if (!fillFwdTrackTable(collision, fwdtrack, false)) { - // continue; - // } - - // } // end of fwdtrack loop - } // end of collision loop - - vecMuons.clear(); - vecMuons.shrink_to_fit(); - } - PROCESS_SWITCH(skimmerPrimaryMuonQC, processRec_SWT, "process reconstructed info only with standalone", false); - - using filteredMyFwdTracksMC = soa::Filtered; - void processMC(soa::Join const& collisions, MyFwdTracksMC const& fwdtracks, MFTTracksMC const&, aod::BCsWithTimestamps const&, aod::McParticles const&) - { - vecMuons.reserve(fwdtracks.size()); - - for (const auto& collision : collisions) { - auto bc = collision.template bc_as(); - initCCDB(bc); - if (!collision.isSelected()) { - continue; - } - if (!collision.has_mcCollision()) { - continue; - } - - // auto fwdtracks_per_coll = fwdtracks.sliceBy(perCollision, collision.globalIndex()); - // for (const auto& fwdtrack : fwdtracks_per_coll) { - // if (!fwdtrack.has_mcParticle()) { - // continue; - // } - // if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack && fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { - // continue; - // } - - // if (!fillFwdTrackTable(collision, fwdtrack, false)) { - // continue; - // } - - // } // end of fwdtrack loop - } // end of collision loop - - vecMuons.clear(); - vecMuons.shrink_to_fit(); - } - PROCESS_SWITCH(skimmerPrimaryMuonQC, processMC, "process reconstructed and MC info", false); - - void processDummy(aod::Collisions const&) {} - PROCESS_SWITCH(skimmerPrimaryMuonQC, processDummy, "process dummy", true); -}; -struct associateAmbiguousMuon { - Produces em_amb_muon_ids; - - SliceCache cache; - PresliceUnsorted perTrack = o2::aod::emprimarymuon::fwdtrackId; - std::vector ambmuon_self_Ids; - - void process(aod::EMPrimaryMuons const& muons) - { - for (const auto& muon : muons) { - auto muons_with_same_trackId = muons.sliceBy(perTrack, muon.fwdtrackId()); - ambmuon_self_Ids.reserve(muons_with_same_trackId.size()); - for (const auto& amb_muon : muons_with_same_trackId) { - if (amb_muon.globalIndex() == muon.globalIndex()) { // don't store myself. - continue; - } - ambmuon_self_Ids.emplace_back(amb_muon.globalIndex()); - } - em_amb_muon_ids(ambmuon_self_Ids); - ambmuon_self_Ids.clear(); - ambmuon_self_Ids.shrink_to_fit(); - } - } -}; - -struct associateSameMuonElement { - Produces glmuon_same_ids; - - SliceCache cache; - PresliceUnsorted perMFTTrack = o2::aod::emprimarymuon::mfttrackId; - PresliceUnsorted perMCHTrack = o2::aod::emprimarymuon::mchtrackId; - std::vector selfIds_per_MFT; - std::vector selfIds_per_MCHMID; - - // Multiple MCH-MID tracks can match with the same MFTsa. This function is to reject such global muons. - void process(aod::EMPrimaryMuons const& muons) - { - for (const auto& muon : muons) { - if (muon.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { - auto muons_with_same_mfttrackId = muons.sliceBy(perMFTTrack, muon.mfttrackId()); - auto muons_with_same_mchtrackId = muons.sliceBy(perMCHTrack, muon.mchtrackId()); - selfIds_per_MFT.reserve(muons_with_same_mfttrackId.size()); - selfIds_per_MCHMID.reserve(muons_with_same_mchtrackId.size()); - // LOGF(info, "muons_with_same_mchtrackId.size() = %d, muons_with_same_mfttrackId.size() = %d", muons_with_same_mchtrackId.size(), muons_with_same_mfttrackId.size()); - - for (const auto& global_muon : muons_with_same_mfttrackId) { - // LOGF(info, "same MFT: global_muon.globalIndex() = %d, global_muon.mchtrackId() = %d, global_muon.mfttrackId() = %d, global_muon.collisionId() = %d", global_muon.globalIndex(), global_muon.mchtrackId(), global_muon.mfttrackId(), global_muon.collisionId()); - if (global_muon.globalIndex() == muon.globalIndex()) { // don't store myself. - continue; - } - if (global_muon.collisionId() == muon.collisionId()) { // the same global muon is repeatedly stored and associated to different collisions if FTTCA is used. - selfIds_per_MFT.emplace_back(global_muon.globalIndex()); - } - } - - for (const auto& global_muon : muons_with_same_mchtrackId) { - // LOGF(info, "same MCH: global_muon.globalIndex() = %d, global_muon.mchtrackId() = %d, global_muon.mfttrackId() = %d, global_muon.collisionId() = %d", global_muon.globalIndex(), global_muon.mchtrackId(), global_muon.mfttrackId(), global_muon.collisionId()); - if (global_muon.globalIndex() == muon.globalIndex()) { // don't store myself. - continue; - } - if (global_muon.collisionId() == muon.collisionId()) { // the same global muon is repeatedly stored and associated to different collisions if FTTCA is used. - selfIds_per_MCHMID.emplace_back(global_muon.globalIndex()); - } - } - - glmuon_same_ids(selfIds_per_MCHMID, selfIds_per_MFT); - selfIds_per_MFT.clear(); - selfIds_per_MFT.shrink_to_fit(); - selfIds_per_MCHMID.clear(); - selfIds_per_MCHMID.shrink_to_fit(); - } else { - glmuon_same_ids(std::vector{}, std::vector{}); // empty for standalone muons - selfIds_per_MFT.clear(); - selfIds_per_MFT.shrink_to_fit(); - selfIds_per_MCHMID.clear(); - selfIds_per_MCHMID.shrink_to_fit(); - } - } // end of muon loop - } -}; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"skimmer-primary-muon-qc"}), - adaptAnalysisTask(cfgc, TaskName{"associate-ambiguous-muon"}), - adaptAnalysisTask(cfgc, TaskName{"associate-same-muon-element"})}; -} diff --git a/PWGEM/Dilepton/TableProducer/skimmerPrimaryTrack.cxx b/PWGEM/Dilepton/TableProducer/skimmerPrimaryTrack.cxx index bc8402d6e7a..2a13bb56c32 100644 --- a/PWGEM/Dilepton/TableProducer/skimmerPrimaryTrack.cxx +++ b/PWGEM/Dilepton/TableProducer/skimmerPrimaryTrack.cxx @@ -395,7 +395,7 @@ struct skimmerPrimaryTrack { if (!collision.isEoI()) { // events with at least 1 lepton for data reduction. continue; } - if (collision.swtaliastmp_raw() == 0) { + if (collision.triggerMask_raw() == 0) { continue; } diff --git a/PWGEM/Dilepton/TableProducer/treeCreatorElectronMLDDA.cxx b/PWGEM/Dilepton/TableProducer/treeCreatorElectronMLDDA.cxx index 7e04ba0aa5e..bd787d9846c 100644 --- a/PWGEM/Dilepton/TableProducer/treeCreatorElectronMLDDA.cxx +++ b/PWGEM/Dilepton/TableProducer/treeCreatorElectronMLDDA.cxx @@ -21,6 +21,7 @@ #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/CollisionTypeHelper.h" +#include "Common/Core/PID/PIDTOFParamService.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/Zorro.h" #include "Common/Core/trackUtilities.h" @@ -45,7 +46,7 @@ #include #include #include -#include +#include #include #include #include @@ -53,8 +54,6 @@ #include -#include - #include #include #include @@ -83,7 +82,10 @@ struct TreeCreatorElectronMLDDA { using MyTrack = MyTracks::iterator; SliceCache cache; - Produces emprimarytracks; // flat table containing collision + track information + Produces eventTable; + Produces trackTable; + Produces empid; + Produces subTable; Produces empidel; Produces empidpi; Produces empidka; @@ -154,21 +156,23 @@ struct TreeCreatorElectronMLDDA { Configurable downscaling_kaon_highP{"downscaling_kaon_highP", 1.1, "down scaling factor to store kaon at high p"}; Configurable downscaling_proton_highP{"downscaling_proton_highP", 1.1, "down scaling factor to store proton at high p"}; - Configurable downscaling_electron_midP{"downscaling_electron_midP", 0.1, "down scaling factor to store electron at intermediate p"}; + Configurable downscaling_electron_midP{"downscaling_electron_midP", 0.05, "down scaling factor to store electron at intermediate p"}; + Configurable downscaling_pion_midP{"downscaling_pion_midP", 0.1, "down scaling factor to store pion at intermediate p"}; - Configurable downscaling_electron_lowP{"downscaling_electron_lowP", 0.01, "down scaling factor to store electron at low p"}; + Configurable downscaling_electron_lowP{"downscaling_electron_lowP", 0.005, "down scaling factor to store electron at low p"}; Configurable downscaling_pion_lowP{"downscaling_pion_lowP", 0.01, "down scaling factor to store pion at low p"}; Configurable downscaling_kaon_lowP{"downscaling_kaon_lowP", 1.1, "down scaling factor to store kaon at low p"}; - Configurable downscaling_proton_lowP{"downscaling_proton_lowP", 0.01, "down scaling factor to store proton at low p"}; + Configurable downscaling_proton_lowP{"downscaling_proton_lowP", 0.002, "down scaling factor to store proton at low p"}; Configurable mid_p_for_downscaling_electron{"mid_p_for_downscaling_electron", 0.8, "intermediate p to apply down scaling factor to store electron"}; + Configurable mid_p_for_downscaling_pion{"mid_p_for_downscaling_pion", 0.25, "intermediate p to apply down scaling factor to store pion"}; Configurable max_p_for_downscaling_electron{"max_p_for_downscaling_electron", 2.0, "max p to apply down scaling factor to store electron"}; - Configurable max_p_for_downscaling_pion{"max_p_for_downscaling_pion", 2.0, "max p to apply down scaling factor to store pion"}; + Configurable max_p_for_downscaling_pion{"max_p_for_downscaling_pion", 0.4, "max p to apply down scaling factor to store pion"}; Configurable max_p_for_downscaling_kaon{"max_p_for_downscaling_kaon", 0.0, "max p to apply down scaling factor to store kaon"}; - Configurable max_p_for_downscaling_proton{"max_p_for_downscaling_proton", 2.0, "max p to apply down scaling factor to store proton"}; - Configurable store_ele_band_only{"store_ele_band_only", false, "flag to store tracks around electron band only to reduce output size"}; - Configurable reject_v0leg_with_itsib{"reject_v0leg_with_itsib", false, "flag to reject v0 leg with ITSib hits"}; + Configurable max_p_for_downscaling_proton{"max_p_for_downscaling_proton", 1e+10, "max p to apply down scaling factor to store proton"}; + Configurable store_ele_band_only{"store_ele_band_only", true, "flag to store tracks around electron band only to reduce output size"}; + Configurable reject_v0leg_with_itsib{"reject_v0leg_with_itsib", true, "flag to reject v0 leg with ITSib hits"}; struct : ConfigurableGroup { std::string prefix = "eventcut_group"; @@ -177,7 +181,7 @@ struct TreeCreatorElectronMLDDA { Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border in event cut"}; - Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", true, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; @@ -228,7 +232,7 @@ struct TreeCreatorElectronMLDDA { Configurable cfg_min_ncluster_itsib{"cfg_min_ncluster_itsib", 0, "min ncluster itsib"}; Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 5.0, "max chi2/NclsTPC"}; Configurable cfg_max_chi2its{"cfg_max_chi2its", 36.0, "max chi2/NclsITS"}; - Configurable cfg_min_chi2its{"cfg_min_chi2its", 0.0, "min chi2/NclsITS"}; // remove ITS afterburner + Configurable cfg_min_chi2its{"cfg_min_chi2its", -1e+10, "min chi2/NclsITS"}; Configurable cfg_min_dcaxy_v0leg{"cfg_min_dcaxy_v0leg", 0.1, "min dca XY to PV for v0 legs in cm"}; Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -5, "min n sigma e in TPC"}; @@ -634,15 +638,6 @@ struct TreeCreatorElectronMLDDA { return is_Pr_TPC && is_Pr_TOF; } - // template - // bool isPionTightTOFreq(TCollision const& collision, TTrack const& track, o2::track::PID::ID id) - // { - // float tofNSigmaPi = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), id, o2::track::PID::Pion)]; - // bool is_Pi_TPC = tightv0cuts.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < tightv0cuts.cfg_max_TPCNsigmaPi; - // bool is_Pi_TOF = tightv0cuts.cfg_min_TOFNsigmaPi < tofNSigmaPi && tofNSigmaPi < tightv0cuts.cfg_max_TOFNsigmaPi && std::fabs(track.tofChi2()) < tightv0cuts.cfg_max_chi2tof; // TOFreq - // return is_Pi_TPC && is_Pi_TOF; - // } - template void fillTrackTable(TCollision const& collision, TTrack const& track, const uint8_t pidlabel, const std::array tofNSigmas, const float beta, const float hadronicRate) { @@ -681,10 +676,14 @@ struct TreeCreatorElectronMLDDA { } } } else if (pidlabel == static_cast(o2::aod::pwgem::dilepton::ml::PID_Label::kPion)) { - if (track.tpcInnerParam() < max_p_for_downscaling_pion) { + if (track.tpcInnerParam() < mid_p_for_downscaling_pion) { if (dist01(engine) > downscaling_pion_lowP) { return; } + } else if (track.tpcInnerParam() < max_p_for_downscaling_pion) { + if (dist01(engine) > downscaling_pion_midP) { + return; + } } else { if (dist01(engine) > downscaling_pion_highP) { return; @@ -713,14 +712,14 @@ struct TreeCreatorElectronMLDDA { } if (std::find(stored_trackIds.begin(), stored_trackIds.end(), track.globalIndex()) == stored_trackIds.end()) { - emprimarytracks(collision.numContrib(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), hadronicRate, - trackParCov.getP(), trackParCov.getTgl(), track.sign(), - track.tpcNClsFindable(), track.tpcNClsFound(), track.tpcNClsCrossedRows(), track.tpcNClsPID(), - track.tpcChi2NCl(), track.tpcInnerParam(), - track.tpcSignal(), - beta, - track.itsClusterSizes(), track.itsChi2NCl(), track.tofChi2(), track.detectorMap(), pidlabel); - + eventTable(collision.numContrib(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), hadronicRate); + trackTable(trackParCov.getQ2Pt(), trackParCov.getTgl(), + track.tpcNClsFindable(), track.tpcNClsFound(), track.tpcNClsCrossedRows(), track.tpcNClsPID(), + track.tpcChi2NCl(), track.tpcInnerParam(), + track.itsClusterSizes(), pidlabel); + + subTable(track.itsChi2NCl(), track.tofChi2(), track.detectorMap()); + empid(track.tpcSignal(), beta); empidel(track.tpcNSigmaEl(), tofNSigmas[0]); empidpi(track.tpcNSigmaPi(), tofNSigmas[1]); empidka(track.tpcNSigmaKa(), tofNSigmas[2]); @@ -1209,7 +1208,6 @@ struct TreeCreatorElectronMLDDA { if (v0cuts.cfg_min_qt_strangeness < v0.qtarm()) { if (v0cuts.cfg_min_qt_k0s < v0.qtarm()) { if (!(v0cuts.cfg_min_mass_lambda_veto < v0.mLambda() && v0.mLambda() < v0cuts.cfg_max_mass_lambda_veto) && !(v0cuts.cfg_min_mass_lambda_veto < v0.mAntiLambda() && v0.mAntiLambda() < v0cuts.cfg_max_mass_lambda_veto)) { - // if ((isPionTightTOFreq(collision, pos, o2::track::PID::K0) && isSelectedV0LegTight(collision, pos)) && (isPion(collision, neg, o2::track::PID::K0) && isSelectedV0Leg(collision, neg))) { if ((isPionTight(collision, pos, o2::track::PID::K0) && isSelectedV0LegTight(collision, pos)) && (isPion(collision, neg, o2::track::PID::K0) && isSelectedV0Leg(collision, neg))) { registry.fill(HIST("V0/hMassK0Short"), v0.mK0Short()); if (v0cuts.cfg_min_mass_k0s < v0.mK0Short() && v0.mK0Short() < v0cuts.cfg_max_mass_k0s) { @@ -1224,7 +1222,6 @@ struct TreeCreatorElectronMLDDA { } } - // if (isPion(collision, pos, o2::track::PID::K0) && isSelectedV0Leg(collision, pos) && isPionTightTOFreq(collision, neg, o2::track::PID::K0) && isSelectedV0LegTight(collision, neg)) { if (isPion(collision, pos, o2::track::PID::K0) && isSelectedV0Leg(collision, pos) && isPionTight(collision, neg, o2::track::PID::K0) && isSelectedV0LegTight(collision, neg)) { registry.fill(HIST("V0/hMassK0Short"), v0.mK0Short()); if (v0cuts.cfg_min_mass_k0s < v0.mK0Short() && v0.mK0Short() < v0cuts.cfg_max_mass_k0s) { @@ -1370,19 +1367,12 @@ struct TreeCreatorElectronMLDDA { registry.fill(HIST("Cascade/hPCA"), cascade.dcacascdaughters()); // distance between bachelor and V0. registry.fill(HIST("Cascade/hCosPA"), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); - // float length = std::sqrt(std::pow(cascade.x() - collision.posX(), 2) + std::pow(cascade.y() - collision.posY(), 2) + std::pow(cascade.z() - collision.posZ(), 2)); - // float mom = cascade.p(); - // float ctauXi = length / mom * o2::constants::physics::MassXiMinus; // 4.91 cm in PDG - // float ctauOmega = length / mom * o2::constants::physics::MassOmegaMinus; // 2.46 cm in PDG - bool isLaProtonPiMinusFromXiMinus = isPionTight(neg.tpcNSigmaPi(), tofNSigmaPiNegFromLambdaFromXi, neg.hasTOF()) && isProtonTight(pos.tpcNSigmaPr(), tofNSigmaPrPosFromLambdaFromXi, pos.hasTOF()); bool isALaAPrPiPlusFromXiPlus = isPionTight(pos.tpcNSigmaPi(), tofNSigmaPiPosFromLambdaFromXi, pos.hasTOF()) && isProtonTight(neg.tpcNSigmaPr(), tofNSigmaPrNegFromLambdaFromXi, neg.hasTOF()); if (isPion(collision, bachelor, o2::track::PID::XiMinus) && (cascade.sign() < 0 ? isLaProtonPiMinusFromXiMinus : isALaAPrPiPlusFromXiPlus)) { registry.fill(HIST("Cascade/hMassXi"), cascade.mXi()); registry.fill(HIST("Cascade/hMassPt_Xi"), cascade.mXi(), cascade.pt()); - // registry.fill(HIST("Cascade/hRxy_Xi"), cascade.mXi(), cascade.cascradius()); - // registry.fill(HIST("Cascade/hCTau_Xi"), cascade.mXi(), ctauXi); } bool isLaProtonPiMinusFromOmegaMinus = isPionTight(neg.tpcNSigmaPi(), tofNSigmaPiNegFromLambdaFromOmega, neg.hasTOF()) && isProtonTight(pos.tpcNSigmaPr(), tofNSigmaPrPosFromLambdaFromOmega, pos.hasTOF()); @@ -1392,8 +1382,6 @@ struct TreeCreatorElectronMLDDA { if (isKaon(collision, bachelor, o2::track::PID::OmegaMinus) && (cascade.sign() < 0 ? isLaProtonPiMinusFromOmegaMinus : isALaAPrPiPlusFromOmegaPlus)) { registry.fill(HIST("Cascade/hMassOmega"), cascade.mOmega()); registry.fill(HIST("Cascade/hMassPt_Omega"), cascade.mOmega(), cascade.pt()); - // registry.fill(HIST("Cascade/hRxy_Omega"), cascade.mOmega(), cascade.cascradius()); - // registry.fill(HIST("Cascade/hCTau_Omega"), cascade.mOmega(), ctauOmega); if (cascadecuts.cfg_min_mass_Omega < cascade.mOmega() && cascade.mOmega() < cascadecuts.cfg_max_mass_Omega) { // select Omega candidates registry.fill(HIST("V0/hTPCdEdx_P_Ka"), bachelor.tpcInnerParam(), bachelor.tpcSignal()); registry.fill(HIST("V0/hTOFbeta_P_Ka"), bachelor.tpcInnerParam(), mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)]); @@ -1408,6 +1396,7 @@ struct TreeCreatorElectronMLDDA { } } // end of cascade loop } // end of collision loop + stored_trackIds.clear(); stored_trackIds.shrink_to_fit(); @@ -1458,7 +1447,7 @@ struct MLTrackQC { }, }; - using MyPIDTracks = soa::Join; + using MyPIDTracks = soa::Join; void processQC(MyPIDTracks const& tracks) { diff --git a/PWGEM/Dilepton/TableProducer/treeCreatorMuonML.cxx b/PWGEM/Dilepton/TableProducer/treeCreatorMuonML.cxx index 6baf82a8c35..d9ff8eee3ba 100644 --- a/PWGEM/Dilepton/TableProducer/treeCreatorMuonML.cxx +++ b/PWGEM/Dilepton/TableProducer/treeCreatorMuonML.cxx @@ -23,6 +23,7 @@ #include "Common/Core/RecoDecay.h" #include "Common/Core/fwdtrackUtilities.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include #include @@ -42,7 +43,6 @@ #include #include #include -#include #include #include @@ -50,6 +50,7 @@ #include #include +#include #include #include #include @@ -76,7 +77,8 @@ struct TreeCreatorMuonML { using MyMFTTracksMC = soa::Join; using MyMFTTrackMC = MyMFTTracksMC::iterator; - Produces mltable; + Produces trackTable; + Produces trackErrTable; // Configurables Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -88,6 +90,8 @@ struct TreeCreatorMuonML { Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; Configurable cfgManualZShift{"cfgManualZShift", 0, "manual z-shift for propagation of global muon to PV"}; Configurable cfgDownSampling{"cfgDownSampling", 1.1, "down sampling for fake matches"}; + Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; + Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 100.f, "max. chi2 for MCH-MFT matching"}; struct : ConfigurableGroup { std::string prefix = "eventCutGroup"; @@ -107,14 +111,25 @@ struct TreeCreatorMuonML { struct : ConfigurableGroup { std::string prefix = "glMuonCutGroup"; - Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; - Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; - Configurable maxMatchingChi2MCHMFT{"maxMatchingChi2MCHMFT", 100.f, "max. chi2 for MCH-MFT matching"}; - Configurable maxChi2{"maxChi2", 20.f, "max. chi2 for global muon"}; + // Configurable minEta{"minEta", -3.6, "min. eta acceptance for MFT-MCH-MID"}; + // Configurable maxEta{"maxEta", -2.5, "max. eta acceptance for MFT-MCH-MID"}; + // Configurable maxChi2{"maxChi2", 1e+10, "max. chi2 for global muon"}; Configurable refitGlobalMuon{"refitGlobalMuon", true, "flag to refit global muon"}; - Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; } glMuonCutGroup; + struct : ConfigurableGroup { + std::string prefix = "MFTCutGroup"; + Configurable minPt{"minPt", 0.04f, "min. pT for MFTsa to reject crazy tracks"}; + Configurable minEta{"minEta", -4.6f, "min. eta acceptance for MFTsa to reject crazy tracks"}; + Configurable maxEta{"maxEta", -1.5f, "max. eta acceptance for MFTsa to reject crazy tracks"}; + } MFTCutGroup; + + struct : ConfigurableGroup { + std::string prefix = "MCHMIDCutGroup"; + Configurable minEta{"minEta", -4.6f, "min. eta acceptance for MCHMID to reject crazy tracks"}; + Configurable maxEta{"maxEta", -1.5f, "max. eta acceptance for MCHMID to reject crazy tracks"}; + } MCHMIDCutGroup; + o2::ccdb::CcdbApi ccdbApi; Service ccdb; std::mt19937 engine; @@ -198,6 +213,8 @@ struct TreeCreatorMuonML { void addHistograms() { + fRegistry.add("Event/hCorrFT0CvsMFT", "mult. corr. between FT0C and MFT;multFT0C;N_{MFTsa}", kTH2F, {{600, 0, 60000}, {100, 0, 10000}}, false); + auto hMuonType = fRegistry.add("hMuonType", "muon type", kTH1F, {{5, -0.5f, 4.5f}}, false); hMuonType->GetXaxis()->SetBinLabel(1, "MFT-MCH-MID (global muon)"); hMuonType->GetXaxis()->SetBinLabel(2, "MFT-MCH-MID (global muon other match)"); @@ -205,44 +222,16 @@ struct TreeCreatorMuonML { hMuonType->GetXaxis()->SetBinLabel(4, "MCH-MID"); hMuonType->GetXaxis()->SetBinLabel(5, "MCH standalone"); - fRegistry.add("MFTMCHMID/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{100, 0.0f, 10}}, false); - fRegistry.add("MFTMCHMID/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {60, -5.f, -2.f}}, false); - fRegistry.add("MFTMCHMID/hEtaPhi_MatchedMCHMID", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {60, -5.f, -2.f}}, false); - fRegistry.add("MFTMCHMID/hDeltaPt_Pt", "#Deltap_{T}/p_{T} vs. p_{T};p_{T}^{gl} (GeV/c);(p_{T}^{sa} - p_{T}^{gl})/p_{T}^{gl}", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - fRegistry.add("MFTMCHMID/hDeltaEta_Pt", "#Delta#eta vs. p_{T};p_{T}^{gl} (GeV/c);#Delta#eta", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - fRegistry.add("MFTMCHMID/hDeltaPhi_Pt", "#Delta#varphi vs. p_{T};p_{T}^{gl} (GeV/c);#Delta#varphi (rad.)", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - fRegistry.add("MFTMCHMID/hDeltaEtaAtMP_Pt", "#Delta#eta vs. p_{T} at MP;p_{T}^{gl} (GeV/c);#Delta#eta", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - fRegistry.add("MFTMCHMID/hDeltaPhiAtMP_Pt", "#Delta#varphi vs. p_{T} at MP;p_{T}^{gl} (GeV/c);#Delta#varphi (rad.)", kTH2F, {{100, 0, 10}, {200, -0.5, +0.5}}, false); - fRegistry.add("MFTMCHMID/hSign", "sign;sign", kTH1F, {{3, -1.5, +1.5}}, false); - fRegistry.add("MFTMCHMID/hNclusters", "Nclusters;Nclusters", kTH1F, {{21, -0.5f, 20.5}}, false); - fRegistry.add("MFTMCHMID/hNclustersMFT", "NclustersMFT;Nclusters MFT", kTH1F, {{11, -0.5f, 10.5}}, false); - fRegistry.add("MFTMCHMID/hRatAbsorberEnd", "R at absorber end;R at absorber end (cm)", kTH1F, {{100, 0.0f, 100}}, false); - fRegistry.add("MFTMCHMID/hPDCA_Rabs", "pDCA vs. Rabs;R at absorber end (cm);p #times DCA (GeV/c #upoint cm)", kTH2F, {{100, 0, 100}, {100, 0.0f, 1000}}, false); - fRegistry.add("MFTMCHMID/hChi2", "chi2;chi2/ndf", kTH1F, {{100, 0.0f, 10}}, false); - fRegistry.add("MFTMCHMID/hChi2MFT", "chi2 MFT;chi2 MFT/ndf", kTH1F, {{100, 0.0f, 10}}, false); - fRegistry.add("MFTMCHMID/hChi2MatchMCHMID", "chi2 match MCH-MID;chi2", kTH1F, {{100, 0.0f, 100}}, false); - fRegistry.add("MFTMCHMID/hChi2MatchMCHMFT", "chi2 match MCH-MFT;chi2", kTH1F, {{100, 0.0f, 100}}, false); - fRegistry.add("MFTMCHMID/hDCAxy2D", "DCA x vs. y;DCA_{x} (cm);DCA_{y} (cm)", kTH2F, {{200, -1, 1}, {200, -1, +1}}, false); - fRegistry.add("MFTMCHMID/hDCAxy2DinSigma", "DCA x vs. y in sigma;DCA_{x} (#sigma);DCA_{y} (#sigma)", kTH2F, {{200, -10, 10}, {200, -10, +10}}, false); - fRegistry.add("MFTMCHMID/hDCAxy", "DCAxy;DCA_{xy} (cm);", kTH1F, {{100, 0, 1}}, false); - fRegistry.add("MFTMCHMID/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{100, 0, 1}, {200, -0.1, 0.1}}, false); - fRegistry.add("MFTMCHMID/hDCAxyinSigma", "DCAxy in sigma;DCA_{xy} (#sigma);", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("MFTMCHMID/hDCAx_PosZ", "DCAx vs. posZ;Z_{vtx} (cm);DCA_{x} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hDCAy_PosZ", "DCAy vs. posZ;Z_{vtx} (cm);DCA_{y} (cm)", kTH2F, {{200, -10, +10}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hDCAx_Phi", "DCAx vs. #varphi;#varphi (rad.);DCA_{x} (cm)", kTH2F, {{90, 0, 2 * M_PI}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hDCAy_Phi", "DCAy vs. #varphi;#varphi (rad.);DCA_{y} (cm)", kTH2F, {{90, 0, 2 * M_PI}, {400, -0.2, +0.2}}, false); - fRegistry.add("MFTMCHMID/hNmu", "#mu multiplicity;N_{#mu} per collision", kTH1F, {{21, -0.5, 20.5}}, false); - fRegistry.add("MFTMCHMID/hdR_Chi2MatchMCHMFT", "dr vs. matching chi2 MCH-MFT;chi2 match MCH-MFT;#DeltaR", kTH2F, {{200, 0, 50}, {200, 0, 0.5}}, false); - fRegistry.add("MFTMCHMID/hdR_Chi2", "dr vs. chi2;global chi2/ndf;#DeltaR", kTH2F, {{100, 0, 10}, {200, 0, 0.5}}, false); - fRegistry.add("MFTMCHMID/hChi2_Chi2MatchMCHMFT", "chi2 vs. matching chi2 MCH-MFT;chi2 match MCH-MFT;global chi2/ndf", kTH2F, {{200, 0, 50}, {100, 0, 10}}, false); - - fRegistry.addClone("MFTMCHMID/", "MCHMID/"); - fRegistry.add("MFTMCHMID/hDCAxResolutionvsPt", "DCA_{x} vs. p_{T};p_{T} (GeV/c);DCA_{x} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 500}}, false); - fRegistry.add("MFTMCHMID/hDCAyResolutionvsPt", "DCA_{y} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 500}}, false); - fRegistry.add("MFTMCHMID/hDCAxyResolutionvsPt", "DCA_{xy} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 500}}, false); - fRegistry.add("MCHMID/hDCAxResolutionvsPt", "DCA_{x} vs. p_{T};p_{T} (GeV/c);DCA_{x} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 5e+5}}, false); - fRegistry.add("MCHMID/hDCAyResolutionvsPt", "DCA_{y} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 5e+5}}, false); - fRegistry.add("MCHMID/hDCAxyResolutionvsPt", "DCA_{xy} vs. p_{T};p_{T} (GeV/c);DCA_{y} resolution (#mum);", kTH2F, {{100, 0, 10.f}, {500, 0, 5e+5}}, false); + // information at matching plane + fRegistry.add("MCHMID/correct/hP", "p;p (GeV/c)", kTH1F, {{1000, 0.0f, 100}}, false); + fRegistry.add("MCHMID/correct/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("MCHMID/correct/hPz", "pz;p_{z} (GeV/c)", kTH1F, {{1000, -100, 0}}, false); + fRegistry.add("MCHMID/correct/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {80, -5.f, -1.f}}, false); + fRegistry.add("MCHMID/correct/hPEta", "p vs. #eta;#eta;p (GeV/c)", kTH2F, {{80, -5.f, -1.f}, {1000, 0, 100}}, false); + fRegistry.add("MCHMID/correct/hPtEta", "pT vs. #eta;#eta;p_{T} (GeV/c)", kTH2F, {{80, -5.f, -1.f}, {1000, 0, 10}}, false); + fRegistry.add("MCHMID/correct/hPzEta", "pz vs. #eta;#eta;p_{z} (GeV/c)", kTH2F, {{80, -5.f, -1.f}, {1000, -100, 0}}, false); + fRegistry.addClone("MCHMID/correct/", "MCHMID/wrong/"); + fRegistry.addClone("MCHMID/", "MFT/"); } template @@ -269,175 +258,21 @@ struct TreeCreatorMuonML { return true; } - template - bool fillFwdTrackTable(TCollision const& collision, TFwdTrack const& fwdtrack, TFwdTracks const&, TMFTTracks const&, TMFTTracksCov const& mftCovs, const float hadronicRate) - { - if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { - return false; - } - - auto mchtrack = fwdtrack.template matchMCHTrack_as(); // MCH-MID - auto mfttrack = fwdtrack.template matchMFTTrack_as(); // MFTsa - if (!mfttrack.has_mcParticle() || !mchtrack.has_mcParticle() || !fwdtrack.has_mcParticle()) { - return false; - } - float chi2 = fwdtrack.chi2() / (2.f * (mchtrack.nClusters() + mfttrack.nClusters()) - 5.f); - float chi2mft = mfttrack.chi2() / (2.f * mfttrack.nClusters() - 5.f); - - // auto mcParticle_MFTMCHMID = fwdtrack.template mcParticle_as(); // this is identical to mcParticle_MCHMID - auto mcParticle_MCHMID = mchtrack.template mcParticle_as(); // this is identical to mcParticle_MFTMCHMID - auto mcParticle_MFT = mfttrack.template mcParticle_as(); - - int pdgCode = mcParticle_MCHMID.pdgCode(); - bool isPrimary = mcParticle_MCHMID.isPhysicalPrimary() || mcParticle_MCHMID.producedByGenerator(); - bool isMatched = (mcParticle_MFT.globalIndex() == mcParticle_MCHMID.globalIndex()) && (mcParticle_MFT.mcCollisionId() == mcParticle_MCHMID.mcCollisionId()); - - if (!isMatched && dist01(engine) > cfgDownSampling) { - return false; - } - - if (fwdtrack.chi2MatchMCHMID() < 0.f) { // this should never happen. only for protection. - return false; - } - if (fwdtrack.chi2MatchMCHMFT() < 0.f || glMuonCutGroup.maxMatchingChi2MCHMFT < fwdtrack.chi2MatchMCHMFT()) { // this should never happen. only for protection. - return false; - } - if (fwdtrack.chi2() < 0.f || glMuonCutGroup.maxChi2 < chi2) { // this should never happen. only for protection. - return false; - } - if (mfttrack.chi2() < 0.f) { // this should never happen. only for protection. - return false; - } - - o2::dataformats::GlobalFwdTrack propmuonAtPV = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mZShift); - float pt = propmuonAtPV.getPt(); - float eta = propmuonAtPV.getEta(); - float phi = propmuonAtPV.getPhi(); - // o2::math_utils::bringTo02Pi(phi); - phi = RecoDecay::constrainAngle(phi, 0, 1U); - - if (eta < glMuonCutGroup.minEta || glMuonCutGroup.maxEta < eta) { - return false; - } - - float dcaX = propmuonAtPV.getX() - collision.posX(); - float dcaY = propmuonAtPV.getY() - collision.posY(); - // float dcaZ = propmuonAtPV.getZ() - collision.posZ(); - float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); - - o2::dataformats::GlobalFwdTrack propmuonAtPV_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToVertex, glMuonCutGroup.matchingZ, mBz, mZShift); - // float ptMatchedMCHMID = propmuonAtPV_Matched.getPt(); - float etaMatchedMCHMID = propmuonAtPV_Matched.getEta(); - float phiMatchedMCHMID = propmuonAtPV_Matched.getPhi(); - // o2::math_utils::bringTo02Pi(phiMatchedMCHMID); - phiMatchedMCHMID = RecoDecay::constrainAngle(phiMatchedMCHMID, 0, 1U); - if (glMuonCutGroup.refitGlobalMuon) { - pt = propmuonAtPV_Matched.getP() * std::sin(2.f * std::atan(std::exp(-eta))); - } - - o2::dataformats::GlobalFwdTrack propmuonAtDCA_Matched = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToDCA, glMuonCutGroup.matchingZ, mBz, mZShift); - float dcaX_Matched = propmuonAtDCA_Matched.getX() - collision.posX(); - float dcaY_Matched = propmuonAtDCA_Matched.getY() - collision.posY(); - float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); - float pDCA = mchtrack.p() * dcaXY_Matched; - float rAtAbsorberEnd = fwdtrack.rAtAbsorberEnd(); // this works only for GlobalMuonTrack - - float xMFTatMP = 999.f, yMFTatMP = 999.f; - float xMCHMIDatMP = 999.f, yMCHMIDatMP = 999.f; - - float xErrMFTatMP = 999.f, yErrMFTatMP = 999.f; - float xErrMCHMIDatMP = 999.f, yErrMCHMIDatMP = 999.f; - float signed1PtMFTatMP = 999.f, tglMFTatMP = 999.f, phiMFTatMP = 999.f; - float signed1PtMCHMIDatMP = 999.f, tglMCHMIDatMP = 999.f, phiMCHMIDatMP = 999.f; - - float signed1PtErrMFTatMP = 999.f, tglErrMFTatMP = 999.f, phiErrMFTatMP = 999.f; - float signed1PtErrMCHMIDatMP = 999.f, tglErrMCHMIDatMP = 999.f, phiErrMCHMIDatMP = 999.f; - - if constexpr (withMFTCov) { - auto mfttrackcov = mftCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); - o2::track::TrackParCovFwd mftsaAtMP = getTrackParCovFwdShift(mfttrack, mZShift, mfttrackcov); // values at innermost update - mftsaAtMP.propagateToZhelix(glMuonCutGroup.matchingZ, mBz); // propagated to matching plane - xMFTatMP = mftsaAtMP.getX(); - yMFTatMP = mftsaAtMP.getY(); - xErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2X()); - yErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2Y()); - - signed1PtMFTatMP = mftsaAtMP.getInvQPt(); - tglMFTatMP = mftsaAtMP.getTanl(); - phiMFTatMP = RecoDecay::constrainAngle(mftsaAtMP.getPhi(), 0, 1U); - signed1PtErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2InvQPt()); - tglErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2Tanl()); - phiErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2Phi()); - - auto muonAtMP = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToMatchingPlane, glMuonCutGroup.matchingZ, mBz, mZShift); // propagated to matching plane - xMCHMIDatMP = muonAtMP.getX(); - yMCHMIDatMP = muonAtMP.getY(); - xErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2X()); - yErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2Y()); - - signed1PtMCHMIDatMP = muonAtMP.getInvQPt(); - tglMCHMIDatMP = muonAtMP.getTanl(); - phiMCHMIDatMP = RecoDecay::constrainAngle(muonAtMP.getPhi(), 0, 1U); - signed1PtErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2InvQPt()); - tglErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2Tanl()); - phiErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2Phi()); - } - - float deta = etaMatchedMCHMID - eta; - float dphi = phiMatchedMCHMID - phi; - o2::math_utils::bringToPMPi(dphi); - - mltable(collision.posZ(), collision.numContrib(), collision.multFT0C(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), hadronicRate, - fwdtrack.trackType(), - - signed1PtMFTatMP, tglMFTatMP, phiMFTatMP, - signed1PtErrMFTatMP, tglErrMFTatMP, phiErrMFTatMP, - xMFTatMP, yMFTatMP, - xErrMFTatMP, yErrMFTatMP, - - signed1PtMCHMIDatMP, tglMCHMIDatMP, phiMCHMIDatMP, - signed1PtErrMCHMIDatMP, tglErrMCHMIDatMP, phiErrMCHMIDatMP, - xMCHMIDatMP, yMCHMIDatMP, - xErrMCHMIDatMP, yErrMCHMIDatMP, - - // dcaX, dcaY, - fwdtrack.nClusters(), pDCA, rAtAbsorberEnd, fwdtrack.chi2MatchMCHMID(), fwdtrack.chi2MatchMCHMFT(), - mfttrack.mftClusterSizesAndTrackFlags(), chi2, mchtrack.chi2(), chi2mft, mfttrack.nClusters(), - pdgCode, isPrimary, isMatched, - mcParticle_MCHMID.pt(), mcParticle_MCHMID.eta(), mcParticle_MCHMID.phi()); - - fRegistry.fill(HIST("hMuonType"), fwdtrack.trackType()); - fRegistry.fill(HIST("MFTMCHMID/hPt"), pt); - fRegistry.fill(HIST("MFTMCHMID/hEtaPhi"), phi, eta); - fRegistry.fill(HIST("MFTMCHMID/hEtaPhi_MatchedMCHMID"), phiMatchedMCHMID, etaMatchedMCHMID); - fRegistry.fill(HIST("MFTMCHMID/hDeltaEta_Pt"), pt, deta); - fRegistry.fill(HIST("MFTMCHMID/hDeltaPhi_Pt"), pt, dphi); - fRegistry.fill(HIST("MFTMCHMID/hSign"), fwdtrack.sign()); - fRegistry.fill(HIST("MFTMCHMID/hNclusters"), fwdtrack.nClusters()); - fRegistry.fill(HIST("MFTMCHMID/hNclustersMFT"), mfttrack.nClusters()); - fRegistry.fill(HIST("MFTMCHMID/hPDCA_Rabs"), rAtAbsorberEnd, pDCA); - fRegistry.fill(HIST("MFTMCHMID/hRatAbsorberEnd"), rAtAbsorberEnd); - fRegistry.fill(HIST("MFTMCHMID/hChi2"), chi2); - fRegistry.fill(HIST("MFTMCHMID/hChi2MFT"), chi2mft); - fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMID"), fwdtrack.chi2MatchMCHMID()); - fRegistry.fill(HIST("MFTMCHMID/hChi2MatchMCHMFT"), fwdtrack.chi2MatchMCHMFT()); - fRegistry.fill(HIST("MFTMCHMID/hDCAxy2D"), dcaX, dcaY); - fRegistry.fill(HIST("MFTMCHMID/hDCAxy"), dcaXY); - fRegistry.fill(HIST("MFTMCHMID/hDCAx_PosZ"), collision.posZ(), dcaX); - fRegistry.fill(HIST("MFTMCHMID/hDCAy_PosZ"), collision.posZ(), dcaY); - fRegistry.fill(HIST("MFTMCHMID/hDCAx_Phi"), phi, dcaX); - fRegistry.fill(HIST("MFTMCHMID/hDCAy_Phi"), phi, dcaY); - return true; - } - SliceCache cache; Preslice perCollision = o2::aod::fwdtrack::collisionId; + Preslice perCollision_MFT = o2::aod::fwdtrack::collisionId; + PresliceUnsorted fwdtracksPerMCHTrack = aod::fwdtrack::matchMCHTrackId; + Partition glMuons = o2::aod::fwdtrack::trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack); + Partition saMuons = o2::aod::fwdtrack::trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack); std::unordered_map map_mfttrackcovs; - void processWithMFTCov(MyCollisionsMC const& collisions, aod::BCsWithTimestamps const&, MyFwdTracksMC const& fwdtracks, MyMFTTracksMC const& mfttracks, aod::MFTTracksCov const& mftCovs, aod::McParticles const&, aod::McCollisions const&) + + void processWithMFTCov(MyCollisionsMC const& collisions, aod::BCsWithTimestamps const&, MyFwdTracksMC const&, MyMFTTracksMC const& mfttracks, aod::MFTTracksCov const& mftCovs, aod::McParticles const&, aod::McCollisions const&, aod::Origins const& origins) { - for (const auto& mfttrackConv : mftCovs) { - map_mfttrackcovs[mfttrackConv.matchMFTTrackId()] = mfttrackConv.globalIndex(); + uint64_t dfId = origins.iteratorAt(0).dataframeID(); + + for (const auto& mfttrackCov : mftCovs) { + map_mfttrackcovs[mfttrackCov.matchMFTTrackId()] = mfttrackCov.globalIndex(); } for (const auto& collision : collisions) { @@ -458,59 +293,140 @@ struct TreeCreatorMuonML { float hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSourceForCptFetcher) * 1.e-3; // kHz - auto fwdtracks_coll = fwdtracks.sliceBy(perCollision, collision.globalIndex()); - for (const auto& fwdtrack : fwdtracks_coll) { - if (!fwdtrack.has_mcParticle()) { + auto mfttracks_per_collision = mfttracks.sliceBy(perCollision_MFT, collision.globalIndex()); + uint16_t multMFT = mfttracks_per_collision.size(); + fRegistry.fill(HIST("Event/hCorrFT0CvsMFT"), collision.multFT0C(), mfttracks_per_collision.size()); + + auto saMuons_per_coll = saMuons->sliceByCached(o2::aod::fwdtrack::collisionId, collision.globalIndex(), cache); + for (const auto& mchtrack : saMuons_per_coll) { + if (!mchtrack.has_mcParticle()) { continue; } - if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + if (mchtrack.chi2() < 0.f) { + continue; + } + if (mchtrack.chi2MatchMCHMID() < 0.f) { continue; } - fillFwdTrackTable(collision, fwdtrack, fwdtracks, mfttracks, mftCovs, hadronicRate); - - } // end of fwdtrack loop - } // end of collision loop - - map_mfttrackcovs.clear(); - } - PROCESS_SWITCH(TreeCreatorMuonML, processWithMFTCov, "produce ML input for single track level", true); - - void processWithoutMFTCov(MyCollisionsMC const& collisions, aod::BCsWithTimestamps const&, MyFwdTracksMC const& fwdtracks, MyMFTTracksMC const& mfttracks, aod::McParticles const&, aod::McCollisions const&) - { - for (const auto& collision : collisions) { - auto bc = collision.template foundBC_as(); - initCCDB(bc); - - if (!collision.has_mcCollision()) { - continue; - } - - if (!isSelectedCollision(collision)) { - continue; - } - - if (eventCutGroup.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { - continue; - } - - float hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSourceForCptFetcher) * 1.e-3; // kHz - - auto fwdtracks_coll = fwdtracks.sliceBy(perCollision, collision.globalIndex()); - for (const auto& fwdtrack : fwdtracks_coll) { - if (!fwdtrack.has_mcParticle()) { + if (dist01(engine) > cfgDownSampling) { // random sampling, if necessary continue; } - if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { + + auto mcParticle_MCHMID = mchtrack.template mcParticle_as(); // this is identical to mcParticle_MFTMCHMID + bool isPrimary_MCHMID = mcParticle_MCHMID.isPhysicalPrimary() || mcParticle_MCHMID.producedByGenerator(); + + auto muonAtMP = propagateMuon(mchtrack, mchtrack, collision, propagationPoint::kToMatchingPlane, matchingZ, mBz, mZShift); // propagated to matching plane + float signed1PtMCHMIDatMP = muonAtMP.getInvQPt(); + float tglMCHMIDatMP = muonAtMP.getTanl(); + float phiMCHMIDatMP = RecoDecay::constrainAngle(muonAtMP.getPhi(), 0, 1U); + float xMCHMIDatMP = muonAtMP.getX(); + float yMCHMIDatMP = muonAtMP.getY(); + + float tglErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2Tanl()); + float phiErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2Phi()); + float xErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2X()); + float yErrMCHMIDatMP = std::sqrt(muonAtMP.getSigma2Y()); + if (muonAtMP.getEta() < MCHMIDCutGroup.minEta || MCHMIDCutGroup.maxEta < muonAtMP.getEta()) { continue; } - fillFwdTrackTable(collision, fwdtrack, fwdtracks, mfttracks, nullptr, hadronicRate); + // auto glMuons_per_MCHMID = fwdtracks.sliceBy(fwdtracksPerMCHTrack, mchtrack.globalIndex()); + auto glMuons_per_MCHMID = glMuons->sliceByCachedUnsorted(o2::aod::fwdtrack::matchMCHTrackId, mchtrack.globalIndex(), cache); + // LOGF(info, "glMuons_per_MCHMID.size() = %d", glMuons_per_MCHMID.size()); + + for (const auto& fwdtrack : glMuons_per_MCHMID) { + // LOGF(info, "mchtrack.globalIndex() = %d, fwdtrack.globalIndex() = %d, mchtrack.collisionId() = %d, fwdtrack.collisionId() = %d, fwdtrack.trackType() = %d", mchtrack.globalIndex(), fwdtrack.globalIndex(), mchtrack.collisionId(), fwdtrack.collisionId(), fwdtrack.trackType()); + if (!fwdtrack.has_mcParticle()) { + continue; + } + // auto mcParticle_MFTMCHMID = fwdtrack.template mcParticle_as(); // this is identical to mcParticle_MCHMID + if (fwdtrack.chi2MatchMCHMFT() < 0.f || maxMatchingChi2MCHMFT < fwdtrack.chi2MatchMCHMFT()) { + continue; + } + if (fwdtrack.chi2MatchMCHMID() < 0.f) { + continue; + } + + auto mfttrack = fwdtrack.template matchMFTTrack_as(); // MFTsa + if (mfttrack.eta() < MFTCutGroup.minEta || MFTCutGroup.maxEta < mfttrack.eta() || mfttrack.pt() < MFTCutGroup.minPt || mfttrack.chi2() < 0.f) { + continue; + } + if (!mfttrack.has_mcParticle()) { + continue; + } + auto mcParticle_MFT = mfttrack.template mcParticle_as(); + bool isPrimary_MFT = mcParticle_MFT.isPhysicalPrimary() || mcParticle_MFT.producedByGenerator(); + + bool isMatched = (mcParticle_MFT.globalIndex() == mcParticle_MCHMID.globalIndex()) && (mcParticle_MFT.mcCollisionId() == mcParticle_MCHMID.mcCollisionId()); + + auto mfttrackcov = mftCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); + o2::track::TrackParCovFwd mftsaAtMP = getTrackParCovFwdShift(mfttrack, mZShift, mfttrackcov); // values at innermost update + mftsaAtMP.propagateToZhelix(matchingZ, mBz); // propagated to matching plane + + float signed1PtMFTatMP = mftsaAtMP.getInvQPt(); + float tglMFTatMP = mftsaAtMP.getTanl(); + float phiMFTatMP = RecoDecay::constrainAngle(mftsaAtMP.getPhi(), 0, 1U); + float xMFTatMP = mftsaAtMP.getX(); + float yMFTatMP = mftsaAtMP.getY(); + + float tglErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2Tanl()); + float phiErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2Phi()); + float xErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2X()); + float yErrMFTatMP = std::sqrt(mftsaAtMP.getSigma2Y()); + + trackTable(collision.posZ(), collision.multFT0C(), collision.ft0cOccupancyInTimeRange(), hadronicRate, multMFT, + signed1PtMFTatMP, tglMFTatMP, phiMFTatMP, xMFTatMP, yMFTatMP, + signed1PtMCHMIDatMP, tglMCHMIDatMP, phiMCHMIDatMP, xMCHMIDatMP, yMCHMIDatMP, + fwdtrack.chi2MatchMCHMFT(), + mcParticle_MFT.pdgCode(), isPrimary_MFT, + mcParticle_MCHMID.pdgCode(), isPrimary_MCHMID, + isMatched, mchtrack.globalIndex(), dfId); + + trackErrTable(tglErrMFTatMP, phiErrMFTatMP, xErrMFTatMP, yErrMFTatMP, + tglErrMCHMIDatMP, phiErrMCHMIDatMP, xErrMCHMIDatMP, yErrMCHMIDatMP); + + if (isMatched) { + fRegistry.fill(HIST("MCHMID/correct/hPt"), muonAtMP.getPt()); + fRegistry.fill(HIST("MCHMID/correct/hPz"), muonAtMP.getPz()); + fRegistry.fill(HIST("MCHMID/correct/hP"), muonAtMP.getP()); + fRegistry.fill(HIST("MCHMID/correct/hEtaPhi"), RecoDecay::constrainAngle(muonAtMP.getPhi(), 0, 1U), muonAtMP.getEta()); + fRegistry.fill(HIST("MCHMID/correct/hPEta"), muonAtMP.getEta(), muonAtMP.getP()); + fRegistry.fill(HIST("MCHMID/correct/hPtEta"), muonAtMP.getEta(), muonAtMP.getPt()); + fRegistry.fill(HIST("MCHMID/correct/hPzEta"), muonAtMP.getEta(), muonAtMP.getPz()); + + fRegistry.fill(HIST("MFT/correct/hPt"), mftsaAtMP.getPt()); + fRegistry.fill(HIST("MFT/correct/hPz"), mftsaAtMP.getPz()); + fRegistry.fill(HIST("MFT/correct/hP"), mftsaAtMP.getP()); + fRegistry.fill(HIST("MFT/correct/hEtaPhi"), RecoDecay::constrainAngle(mftsaAtMP.getPhi(), 0, 1U), mftsaAtMP.getEta()); + fRegistry.fill(HIST("MFT/correct/hPEta"), mftsaAtMP.getEta(), mftsaAtMP.getP()); + fRegistry.fill(HIST("MFT/correct/hPtEta"), mftsaAtMP.getEta(), mftsaAtMP.getPt()); + fRegistry.fill(HIST("MFT/correct/hPzEta"), mftsaAtMP.getEta(), mftsaAtMP.getPz()); + } else { + fRegistry.fill(HIST("MCHMID/wrong/hPt"), muonAtMP.getPt()); + fRegistry.fill(HIST("MCHMID/wrong/hPz"), muonAtMP.getPz()); + fRegistry.fill(HIST("MCHMID/wrong/hP"), muonAtMP.getP()); + fRegistry.fill(HIST("MCHMID/wrong/hEtaPhi"), RecoDecay::constrainAngle(muonAtMP.getPhi(), 0, 1U), muonAtMP.getEta()); + fRegistry.fill(HIST("MCHMID/wrong/hPEta"), muonAtMP.getEta(), muonAtMP.getP()); + fRegistry.fill(HIST("MCHMID/wrong/hPtEta"), muonAtMP.getEta(), muonAtMP.getPt()); + fRegistry.fill(HIST("MCHMID/wrong/hPzEta"), muonAtMP.getEta(), muonAtMP.getPz()); + + fRegistry.fill(HIST("MFT/wrong/hPt"), mftsaAtMP.getPt()); + fRegistry.fill(HIST("MFT/wrong/hPz"), mftsaAtMP.getPz()); + fRegistry.fill(HIST("MFT/wrong/hP"), mftsaAtMP.getP()); + fRegistry.fill(HIST("MFT/wrong/hEtaPhi"), RecoDecay::constrainAngle(mftsaAtMP.getPhi(), 0, 1U), mftsaAtMP.getEta()); + fRegistry.fill(HIST("MFT/wrong/hPEta"), mftsaAtMP.getEta(), mftsaAtMP.getP()); + fRegistry.fill(HIST("MFT/wrong/hPtEta"), mftsaAtMP.getEta(), mftsaAtMP.getPt()); + fRegistry.fill(HIST("MFT/wrong/hPzEta"), mftsaAtMP.getEta(), mftsaAtMP.getPz()); + } + } + } // end of MCHMID track - } // end of fwdtrack loop } // end of collision loop + + map_mfttrackcovs.clear(); } - PROCESS_SWITCH(TreeCreatorMuonML, processWithoutMFTCov, "produce ML input for single track level", false); + PROCESS_SWITCH(TreeCreatorMuonML, processWithMFTCov, "produce ML input for single track level", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGEM/Dilepton/Tasks/CMakeLists.txt b/PWGEM/Dilepton/Tasks/CMakeLists.txt index a32c3768bd5..845a8fe4d98 100644 --- a/PWGEM/Dilepton/Tasks/CMakeLists.txt +++ b/PWGEM/Dilepton/Tasks/CMakeLists.txt @@ -55,6 +55,11 @@ o2physics_add_dpl_workflow(table-reader-barrel PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2Physics::PWGDQCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(create-ttp + SOURCES createTTP.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(event-qc SOURCES eventQC.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::EventFilteringUtils @@ -100,8 +105,8 @@ o2physics_add_dpl_workflow(dimuon-mc PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(prefilter-dielectron - SOURCES prefilterDielectron.cxx +o2physics_add_dpl_workflow(dimuon-v1 + SOURCES dimuonV1.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) @@ -117,7 +122,7 @@ o2physics_add_dpl_workflow(study-mc-truth o2physics_add_dpl_workflow(matching-mft SOURCES matchingMFT.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tagging-hfe @@ -142,16 +147,16 @@ o2physics_add_dpl_workflow(mc-particle-predictions-otf o2physics_add_dpl_workflow(study-dcafitter SOURCES studyDCAFitter.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsVertexing O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(evaluate-acceptance - SOURCES evaluateAcceptance.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore +o2physics_add_dpl_workflow(test-pv + SOURCES testPV.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dilepton-polarization - SOURCES dileptonPolarization.cxx +o2physics_add_dpl_workflow(evaluate-acceptance + SOURCES evaluateAcceptance.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -164,3 +169,8 @@ o2physics_add_dpl_workflow(check-mc-pair-template SOURCES checkMCPairTemplate.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(dielectron-mixing-test + SOURCES dielectronMixingTest.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMDileptonCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGEM/Dilepton/Tasks/MCtemplates.cxx b/PWGEM/Dilepton/Tasks/MCtemplates.cxx index d44dd156c62..9c69ee77dbd 100644 --- a/PWGEM/Dilepton/Tasks/MCtemplates.cxx +++ b/PWGEM/Dilepton/Tasks/MCtemplates.cxx @@ -109,7 +109,7 @@ constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::ReducedTrack | constexpr static uint32_t gkTrackFillMapWithCov = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID; constexpr static uint32_t gkParticleMCFillMap = VarManager::ObjTypes::ParticleMC; -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar); // defines histograms for all tasks struct AnalysisEventSelection { Produces eventSel; @@ -698,7 +698,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGEM/Dilepton/Tasks/associateMCcollision.cxx b/PWGEM/Dilepton/Tasks/associateMCcollision.cxx index 9d66de55cd5..a48141be999 100644 --- a/PWGEM/Dilepton/Tasks/associateMCcollision.cxx +++ b/PWGEM/Dilepton/Tasks/associateMCcollision.cxx @@ -59,7 +59,7 @@ struct associateMCcollision { void runMC(TMCCollisions const& mcCollisions, TCollisions const& collisions, TPreslice const& perMCCollision) { - for (auto& mcCollision : mcCollisions) { + for (const auto& mcCollision : mcCollisions) { auto rec_colls_per_mccoll = collisions.sliceBy(perMCCollision, mcCollision.globalIndex()); fRegistry.fill(HIST("hReccollsPerMCcoll"), rec_colls_per_mccoll.size()); uint32_t maxNumContrib = 0; diff --git a/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx b/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx index b6e0118bd15..62ef21abaab 100644 --- a/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx +++ b/PWGEM/Dilepton/Tasks/checkMCPairTemplate.cxx @@ -17,6 +17,7 @@ #include "PWGEM/Dilepton/Core/DielectronCut.h" #include "PWGEM/Dilepton/Core/DimuonCut.h" #include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" #include "PWGEM/Dilepton/DataModel/dileptonTables.h" #include "PWGEM/Dilepton/Utils/EMTrackUtilities.h" #include "PWGEM/Dilepton/Utils/EventHistograms.h" @@ -375,6 +376,8 @@ struct checkMCPairTemplate { std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; std::string pair_y_axis_title = "y_{ll}"; std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; + std::string mother1_pt_axis_title = "p_{T,Mother1} (GeV/c)"; + std::string mother2_pt_axis_title = "p_{T,Mother2} (GeV/c)"; if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { mass_axis_title = "m_{ee} (GeV/c^{2})"; pair_pt_axis_title = "p_{T,ee} (GeV/c)"; @@ -395,6 +398,8 @@ struct checkMCPairTemplate { // pair info const AxisSpec axis_mass{ConfMllBins, mass_axis_title}; const AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; + const AxisSpec axis_M1_pt{ConfPtllBins, mother1_pt_axis_title}; + const AxisSpec axis_M2_pt{ConfPtllBins, mother2_pt_axis_title}; const AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; const AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; const AxisSpec axis_pt_meson{ConfPtllBins, "p_{T}^{VM} (GeV/c)"}; // for omega, phi meson pT spectra @@ -454,16 +459,17 @@ struct checkMCPairTemplate { fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2c2l_b2l_diffb/"); // LS fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2cc2l_b2c2l/"); fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2cc2l_b2cc2l/"); + fRegistry.addClone("Generated/ccbar/c2l_c2l/", "Generated/bbbar/b2cc2ll/"); // for charmed hadrons // create 28 combinations static constexpr std::string_view charmed_mesons[] = {"Dplus", "D0", "Dsplus"}; // 411, 421, 431 static constexpr std::string_view anti_charmed_mesons[] = {"Dminus", "D0bar", "Dsminus"}; const int nm_c = sizeof(charmed_mesons) / sizeof(charmed_mesons[0]); - static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xicplus", "Xic0", "Omegac0"}; // 4122, 4232, 4132, 4332 - static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xicminus", "Xic0bar", "Omegac0bar"}; + static constexpr std::string_view charmed_baryons[] = {"Lcplus", "Xic0", "Xicplus", "Omegac0"}; // 4122, 4132, 4232, 4332 + static constexpr std::string_view anti_charmed_baryons[] = {"Lcminus", "Xic0bar", "Xicminus", "Omegac0bar"}; const int nb_c = sizeof(charmed_baryons) / sizeof(charmed_baryons[0]); static constexpr std::string_view sum_charmed_mesons[] = {"Dpm", "D0", "Dspm"}; - static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xicpm", "Xic0", "Omegac0"}; + static constexpr std::string_view sum_charmed_baryons[] = {"Lcpm", "Xic0", "Xicpm", "Omegac0"}; if (cfgFillSeparateCharmHadronPairs) { for (int im = 0; im < nm_c; im++) { @@ -544,7 +550,7 @@ struct checkMCPairTemplate { fRegistry.addClone("Generated/VM/All/", "Generated/VM/Acc/"); // reconstructed pair info - fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca}, true); + fRegistry.add("Pair/sm/Photon/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lspp/"); fRegistry.addClone("Pair/sm/Photon/uls/", "Pair/sm/Photon/lsmm/"); @@ -584,17 +590,20 @@ struct checkMCPairTemplate { } } - fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca}, true); + fRegistry.add("Pair/ccbar/c2l_c2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lspp/"); fRegistry.addClone("Pair/ccbar/c2l_c2l/uls/", "Pair/ccbar/c2l_c2l/lsmm/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/"); + fRegistry.add("Pair/bbbar/b2l_b2l/uls/hs", "rec. dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_M1_pt, axis_M2_pt}, true); + fRegistry.addClone("Pair/bbbar/b2l_b2l/uls/", "Pair/bbbar/b2l_b2l/lspp/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/uls/", "Pair/bbbar/b2l_b2l/lsmm/"); + // fRegistry.addClone("Pair/bbbar/c2l_c2l/", "Pair/bbbar/b2l_b2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2l_sameb/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2l_diffb/"); // LS + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/"); if (cfgFillSeparateCharmHadronPairs) { for (int im = 0; im < nm_c; im++) { @@ -623,57 +632,57 @@ struct checkMCPairTemplate { if (cfgFillSeparateBeautyHadronPairs) { // // (Hb->l) (Hb->l) combinations // for (int im = 0; im < nm_b; im++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", beauty_mesons[im].data(), anti_beauty_mesons[im].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", beauty_mesons[im].data(), anti_beauty_mesons[im].data())); // } // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", beauty_baryons[ib].data(), anti_beauty_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", beauty_baryons[ib].data(), anti_beauty_baryons[ib].data())); // } // for (int im1 = 0; im1 < nm_b - 1; im1++) { // for (int im2 = im1 + 1; im2 < nm_b; im2++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im1].data(), sum_beauty_mesons[im2].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im1].data(), sum_beauty_mesons[im2].data())); // } // } // for (int ib1 = 0; ib1 < nb_b - 1; ib1++) { // for (int ib2 = ib1 + 1; ib2 < nb_b; ib2++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_baryons[ib1].data(), sum_beauty_baryons[ib2].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_baryons[ib1].data(), sum_beauty_baryons[ib2].data())); // } // } // for (int im = 0; im < nm_b; im++) { // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im].data(), sum_beauty_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/%s_%s/", sum_beauty_mesons[im].data(), sum_beauty_baryons[ib].data())); // } // } // // (Hb->Hc->l) (Hb->Hc->l) combinations // for (int im = 0; im < nm_b; im++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_mesons[im].data(), sum_charmed_mesons[im].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_mesons[im].data(), sum_charmed_mesons[im].data())); // } // for (int ib = 0; ib < nb_b; ib++) { - // fRegistry.addClone("Pair/ccbar/c2l_c2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_baryons[ib].data(), sum_charmed_baryons[ib].data())); + // fRegistry.addClone("Pair/bbbar/b2l_b2l/", Form("Pair/bbbar/b2%s2l_b2%s2l/", sum_charmed_baryons[ib].data(), sum_charmed_baryons[ib].data())); // } - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/"); - fRegistry.addClone("Pair/ccbar/c2l_c2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/"); + fRegistry.addClone("Pair/bbbar/b2l_b2l/", "Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/"); } // for correlated bkg due to mis-identified hadrons, and true combinatorial bkg @@ -848,7 +857,7 @@ struct checkMCPairTemplate { fDielectronCut.ApplyPhiV(dielectroncuts.cfg_apply_phiv); fDielectronCut.SetMindEtadPhi(dielectroncuts.cfg_apply_detadphi, false, dielectroncuts.cfg_min_deta, dielectroncuts.cfg_min_dphi); fDielectronCut.SetPairOpAng(dielectroncuts.cfg_min_opang, dielectroncuts.cfg_max_opang); - fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); + // fDielectronCut.SetRequireDifferentSides(dielectroncuts.cfg_require_diff_sides); // for track fDielectronCut.SetTrackPtRange(dielectroncuts.cfg_min_pt_track, dielectroncuts.cfg_max_pt_track); @@ -1137,11 +1146,11 @@ struct checkMCPairTemplate { } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/uls/hs"), mass, pt, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/lspp/hs"), mass, pt, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Generated/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, weight); + fRegistry.fill(HIST("Generated/ccbar/Xic0_Xicpm/lsmm/hs"), mass, pt, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS @@ -1419,240 +1428,265 @@ struct checkMCPairTemplate { } template - void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float pair_dca, const float weight) + void fillRecHistograms(const int sign1, const int sign2, const int pdgMotherC1, const int pdgMotherC2, const float mass, const float pt, const float pair_dca, const float weight, const float ptMotherC1, const float ptMotherC2) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("uls/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lspp/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, pair_dca, weight); - } + fRegistry.fill(HIST("Pair/") + HIST(dilepton_source_types[sourceId]) + HIST("lsmm/hs"), mass, pt, pair_dca, ptMotherC1, ptMotherC2, weight); + } + + // Keep the mother-pt axes in the same order as the hadron names in the histogram path. + const auto motherOrder = [](int pdg) { + switch (std::abs(pdg)) { + case 411: + return 0; // Dpm + case 421: + return 1; // D0 + case 431: + return 2; // Dspm + case 4122: + return 3; // Lcpm + case 4132: + return 4; // Xic0 + case 4232: + return 5; // Xicpm + case 4332: + return 6; // Omegac0 + default: + return 7; + } + }; + const bool motherOrderMatches = motherOrder(pdgMotherC1) <= motherOrder(pdgMotherC2); + const float ptMotherFirst = motherOrderMatches ? ptMotherC1 : ptMotherC2; + const float ptMotherSecond = motherOrderMatches ? ptMotherC2 : ptMotherC1; if (dilepton_source_types[sourceId].find("ccbar") != std::string_view::npos && cfgFillSeparateCharmHadronPairs) { if (std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 411) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dplus_Dminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 421) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_D0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 431) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dsplus_Dsminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 421) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 421)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_D0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 431)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Dspm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 431) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 431)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Dspm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4122) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcplus_Lcminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4232) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicplus_Xicminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4132) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xic0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == 4332 && std::abs(pdgMotherC2) == 4332) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Omegac0_Omegac0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4122 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4122 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Lcpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4232 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4232 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xicpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 4132 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 4132 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Xic0_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 411 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 411 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dpm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 421 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 421 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/D0_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4122) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4122)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Lcpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4232) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4232)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xicpm/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4132) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4132)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Xic0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == 431 && std::abs(pdgMotherC2) == 4332) || (std::abs(pdgMotherC2) == 431 && std::abs(pdgMotherC1) == 4332)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/ccbar/Dspm_Omegac0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } @@ -1661,308 +1695,308 @@ struct checkMCPairTemplate { // (Hb->l) (Hb->l)combinations // if (std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kBPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bplus_Bminus/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kB0) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_B0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kBS && std::abs(pdgMotherC2) == kBS) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Bs0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kLambdaB0 && std::abs(pdgMotherC2) == kLambdaB0) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Lb0_Lb0bar/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kB0) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_B0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kB0 && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBPlus && std::abs(pdgMotherC2) == kBS) || (std::abs(pdgMotherC2) == kBPlus && std::abs(pdgMotherC1) == kBS)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bpm_Bs0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kB0 && std::abs(pdgMotherC2) == kBS) || (std::abs(pdgMotherC2) == kB0 && std::abs(pdgMotherC1) == kBS)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/B0_Bs0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if ((std::abs(pdgMotherC1) == kBS && std::abs(pdgMotherC2) == kLambdaB0) || (std::abs(pdgMotherC2) == kBS && std::abs(pdgMotherC1) == kLambdaB0)) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/Bs0_Lb0/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kD0 && std::abs(pdgMotherC2) == kD0) { // (Hb->Hc->l) (Hb->Hc->l) combinations // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2D02l_b2D02l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Dpm2l_b2Dpm2l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } else if (std::abs(pdgMotherC1) == kLambdaCPlus && std::abs(pdgMotherC2) == kLambdaCPlus) { // if (sign1 * sign2 < 0) { // ULS - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/uls/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 > 0 && sign2 > 0) { // LS++ - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lspp/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } else if (sign1 < 0 && sign2 < 0) { // LS-- - // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lsmm/hs"), mass, pt, pair_dca, weight); + // fRegistry.fill(HIST("Pair/bbbar/b2Lcpm2l_b2Lcpm2l/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); // } // } if (sourceId == 17) { // (Hb->Hc->l) (Hb->Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2c2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 20) { // (Hb->Hc+Hc->l) (Hb->Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2c2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 21) { // (Hb->Hc+Hc->l) (Hb->Hc+Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2l_b2cc2l/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } else if (sourceId == 22) { // (Hb->Hc+Hc->l) (Hb->Hc+Hc->l) combinations categorized by Hc lifetime if ((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if ((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (std::abs(pdgMotherC1) == kDPlus && std::abs(pdgMotherC2) == kDPlus) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau130/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kLambdaCPlus || std::abs(pdgMotherC1) == kXiC0) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kLambdaCPlus || std::abs(pdgMotherC2) == kXiC0) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau50_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } else if (((std::abs(pdgMotherC1) == kD0 || std::abs(pdgMotherC1) == kDS || std::abs(pdgMotherC1) == kXiCPlus) && (std::abs(pdgMotherC2) == kDPlus)) || ((std::abs(pdgMotherC2) == kD0 || std::abs(pdgMotherC2) == kDS || std::abs(pdgMotherC2) == kXiCPlus) && (std::abs(pdgMotherC1) == kDPlus))) { if (sign1 * sign2 < 0) { // ULS - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/uls/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 > 0 && sign2 > 0) { // LS++ - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lspp/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } else if (sign1 < 0 && sign2 < 0) { // LS-- - fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, weight); + fRegistry.fill(HIST("Pair/bbbar/b2cc2ll/Hc_ctau130_ctau300/lsmm/hs"), mass, pt, pair_dca, ptMotherFirst, ptMotherSecond, weight); } } } @@ -1998,7 +2032,7 @@ struct checkMCPairTemplate { return false; } } - if (!cut.IsSelectedPair(t1, t2, d_bz, 0.0)) { + if (!cut.IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { @@ -2180,8 +2214,8 @@ struct checkMCPairTemplate { float deltaPt2 = t2mc.pt() - t2.pt(); switch (std::abs(mcmother.pdgCode())) { case 111: - if (IsFromCharm(mcmother, mcparticles) < 0 && IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 - fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt pi0 + if (IsFromCharm(mcmother, mcparticles) < 0 && IsFromBeauty(mcmother, mcparticles) < 0) { // prompt pi0 + fillRecHistograms<1>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt pi0 if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/PromptPi0/uls/hMvsPhiV"), phiv, v12.M()); @@ -2197,8 +2231,8 @@ struct checkMCPairTemplate { fRegistry.fill(HIST("Pair/sm/PromptPi0/lsmm/hDCAz1vsDCAz2"), dcaZinSigma(t1), dcaZinSigma(t2)); } } - } else { // non-prompt pi0 - fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt pi0 + } else { // non-prompt pi0 + fillRecHistograms<2>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt pi0 if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/NonPromptPi0/uls/hMvsPhiV"), phiv, v12.M()); @@ -2217,33 +2251,33 @@ struct checkMCPairTemplate { } break; case 221: - fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // eta + fillRecHistograms<3>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // eta break; case 331: - fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // eta' + fillRecHistograms<4>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // eta' break; case 113: - fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // rho + fillRecHistograms<5>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // rho break; case 223: - fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // omega + fillRecHistograms<6>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // omega if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight); // omeag->ee + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/sm/Omega2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // omeag->ee } } break; case 333: - fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // phi + fillRecHistograms<7>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // phi if (mcmother.daughtersIds().size() == 2) { - if (t1.sign() * t2.sign() < 0) { // ULS - fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight); // phi->ee + if (t1.sign() * t2.sign() < 0) { // ULS + fRegistry.fill(HIST("Pair/sm/Phi2ll/uls/hs"), v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // phi->ee } } break; case 443: if (IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt J/psi + fillRecHistograms<9>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt J/psi if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/NonPromptJPsi/uls/hDeltaPtvsDCA"), pair_dca, deltaPt1 + deltaPt2); @@ -2257,7 +2291,7 @@ struct checkMCPairTemplate { } } } else { - fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt J/psi + fillRecHistograms<8>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt J/psi if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { if (t1.sign() * t2.sign() < 0) { // ULS fRegistry.fill(HIST("Pair/sm/PromptJPsi/uls/hDeltaPtvsDCA"), pair_dca, deltaPt1 + deltaPt2); @@ -2274,9 +2308,9 @@ struct checkMCPairTemplate { break; case 100443: if (IsFromBeauty(mcmother, mcparticles) > 0) { - fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // non-prompt psi2S + fillRecHistograms<11>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // non-prompt psi2S } else { - fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // prompt psi2S + fillRecHistograms<10>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // prompt psi2S } break; default: @@ -2285,7 +2319,7 @@ struct checkMCPairTemplate { } else if (!(t1mc.isPhysicalPrimary() || t1mc.producedByGenerator()) && !(t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { switch (std::abs(mcmother.pdgCode())) { case 22: - fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight); // photon conversion + fillRecHistograms<0>(t1.sign(), t2.sign(), 0, 0, v12.M(), v12.Pt(), pair_dca, weight, 0, 0); // photon conversion if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { fRegistry.fill(HIST("Pair/sm/Photon/uls/hMvsPhiV"), phiv, v12.M()); float rxy_gen = std::sqrt(std::pow(t1mc.vx(), 2) + std::pow(t1mc.vy(), 2)); @@ -2303,28 +2337,28 @@ struct checkMCPairTemplate { auto mp2 = mcparticles.iteratorAt(t2mc.mothersIds()[0]); switch (hfee_type) { case static_cast(EM_HFeeType::kCe_Ce): - fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // c2l_c2l + fillRecHistograms<15>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // c2l_c2l break; case static_cast(EM_HFeeType::kBe_Be): - fillRecHistograms<16>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2l_b2l + fillRecHistograms<16>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2l_b2l break; case static_cast(EM_HFeeType::kBCe_BCe): - fillRecHistograms<17>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2c2l + fillRecHistograms<17>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2c2l break; case static_cast(EM_HFeeType::kBCe_Be_SameB): - fillRecHistograms<18>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2l_sameb + fillRecHistograms<18>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2l_sameb break; case static_cast(EM_HFeeType::kBCe_Be_DiffB): - fillRecHistograms<19>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2c2l_b2l_diffb + fillRecHistograms<19>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2c2l_b2l_diffb break; case static_cast(EM_HFeeType::kBCCe_BCe): - fillRecHistograms<20>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2l_b2c2l + fillRecHistograms<20>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2l_b2c2l break; case static_cast(EM_HFeeType::kBCCe_BCCe): - fillRecHistograms<21>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2l_b2cc2l + fillRecHistograms<21>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2l_b2cc2l break; case static_cast(EM_HFeeType::kBCCee): - fillRecHistograms<22>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight); // b2cc2ll + fillRecHistograms<22>(t1.sign(), t2.sign(), mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), pair_dca, weight, mp1.pt(), mp2.pt()); // b2cc2ll break; default: break; @@ -2518,6 +2552,9 @@ struct checkMCPairTemplate { case static_cast(EM_HFeeType::kBCCe_BCCe): fillGenHistograms<21>(sign1, sign2, mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), weight); // b2cc2l_b2cc2l break; + case static_cast(EM_HFeeType::kBCCee): + fillGenHistograms<22>(sign1, sign2, mp1.pdgCode(), mp2.pdgCode(), v12.M(), v12.Pt(), weight); // b2cc2ll + break; default: break; } @@ -2753,7 +2790,7 @@ struct checkMCPairTemplate { } if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron) { - if (!cut.template IsSelectedPair(t1, t2, d_bz, 0.0)) { + if (!cut.template IsSelectedPair(t1, t2)) { return false; } } else if constexpr (pairtype == o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon) { diff --git a/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx b/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx index a608e07d79a..a322e7b0d97 100644 --- a/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx +++ b/PWGEM/Dilepton/Tasks/createResolutionMapDerived.cxx @@ -142,6 +142,7 @@ struct createResolutionMapDerived { Configurable cfg_max_phiposition_track{"cfg_max_phiposition_track", 6.3, "max phi position for single track at certain radius"}; Configurable acceptOnlyCorrectMatch{"acceptOnlyCorrectMatch", false, "flag to accept only correct match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. Configurable acceptOnlyWrongMatch{"acceptOnlyWrongMatch", false, "flag to accept only wrong match between ITS and TPC"}; // this is only for MC study, as we don't know correct match in data. + Configurable acceptOnlyPhysicalPrimary{"acceptOnlyPhysicalPrimary", false, "flag to accept only Physical Primary electrons"}; Configurable cfg_pid_scheme{"cfg_pid_scheme", static_cast(DielectronCut::PIDSchemes::kTPChadrejORTOFreq), "pid scheme [kTOFreq : 0, kTPChadrej : 1, kTPChadrejORTOFreq : 2, kTPConly : 3, kTOFif = 4, kPIDML = 5]"}; Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; @@ -456,6 +457,9 @@ struct createResolutionMapDerived { if (electroncuts.acceptOnlyWrongMatch && !o2::aod::pwgem::dilepton::utils::mcutil::hasFakeMatchITSTPC(track)) { continue; } + if (electroncuts.acceptOnlyPhysicalPrimary && !mcParticle.isPhysicalPrimary()) { + continue; + } registry.fill(HIST("Electron/hPIDForTracking"), track.pidForTracking()); if (electroncuts.checkPIDForTracking && track.pidForTracking() != static_cast(electroncuts.PartIdentifier)) { diff --git a/PWGEM/Dilepton/Tasks/createTTP.cxx b/PWGEM/Dilepton/Tasks/createTTP.cxx new file mode 100644 index 00000000000..7bef5555eba --- /dev/null +++ b/PWGEM/Dilepton/Tasks/createTTP.cxx @@ -0,0 +1,858 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code creates parameters used in track tuner. +// Please write to: daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/Utils/MCUtilities.h" +#include "PWGEM/Dilepton/Utils/PairUtilities.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct createTTP { + // Configurables + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable cfgEventGeneratorType{"cfgEventGeneratorType", -1, "if positive, select event generator type. i.e. gap or signal"}; + + ConfigurableAxis ConfPtBins{"ConfPtBins", {VARIABLE_WIDTH, 0.00, 0.10, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8, 5.0, 5.5, 6.0, 7.5, 7.0, 7.5, 8.0, 9.0, 10.0, 15, 20}, "pT bins for output histograms"}; + Configurable cfgNbinsEta{"cfgNbinsEta", 20, "number of eta bins for output histograms"}; + Configurable cfgNbinsPhi{"cfgNbinsPhi", 36, "number of phi bins for output histograms"}; + ConfigurableAxis ConfDCABins{"ConfDCABins", {2000, -1000, +1000}, "DCA bins for output histograms"}; + ConfigurableAxis ConfDCASigmaBins{"ConfDCASigmaBins", {200, -10, +10}, "DCA in sigma bins for output histograms"}; + + o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {400, 0, 4}, "mll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; + + struct : ConfigurableGroup { + std::string prefix = "eventCut"; + Configurable cfgZvtxMin{"cfgZvtxMin", -10, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", +10, "max. Zvtx"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. track occupancy"}; + Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. track occupancy"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + + Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + Configurable cfgCentMin{"cfgCentMin", -1.f, "min. centrality"}; + Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; + Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; + Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; + + // for RCT + Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", true, "require good detector flag in run condtion table"}; + Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } eventCut; + + struct : ConfigurableGroup { + std::string prefix = "tagCut"; + Configurable min_pt_track{"min_pt_track", 0.4, "min pT for single track"}; + Configurable max_pt_track{"max_pt_track", 1e+10, "max pT for single track"}; + Configurable min_eta_track{"min_eta_track", -0.8, "min eta for single track"}; + Configurable max_eta_track{"max_eta_track", +0.8, "max eta for single track"}; + Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; + Configurable min_ncrossedrows_tpc{"min_ncrossedrows_tpc", 120, "min. crossed rows"}; + Configurable min_ncluster_its{"min_ncluster_its", 5, "min ncluster its"}; + Configurable min_ncluster_itsib{"min_ncluster_itsib", 3, "min ncluster itsib"}; + Configurable min_tpc_cr_findable_ratio{"min_tpc_cr_findable_ratio", 0.8, "min. TPC Ncr/Nf ratio"}; + Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 0.7, "max fraction of shared clusters in TPC"}; + Configurable max_chi2tpc{"max_chi2tpc", 2.5, "max. chi2/NclsTPC"}; + Configurable max_chi2its{"max_chi2its", 36.0, "max. chi2/NclsITS"}; + Configurable max_dcaxy{"max_dcaxy", 0.2, "max DCAxy in cm"}; + Configurable max_dcaz{"max_dcaz", 0.2, "max DCAz in cm"}; + Configurable max_dca_in_sigma{"max_dca_in_sigma", 1e+10, "max dca in sigma for a single track"}; + + Configurable min_TPCNsigmaEl{"min_TPCNsigmaEl", -2, "min. TPC n sigma for electron inclusion"}; + Configurable max_TPCNsigmaEl{"max_TPCNsigmaEl", +3, "max. TPC n sigma for electron inclusion"}; + Configurable min_TPCNsigmaPi{"min_TPCNsigmaPi", -1e+10, "min n sigma pi in TPC for exclusion"}; + Configurable max_TPCNsigmaPi{"max_TPCNsigmaPi", +3, "max n sigma pi in TPC for exclusion"}; + Configurable min_TPCNsigmaKa{"min_TPCNsigmaKa", -3, "min n sigma ka in TPC for exclusion"}; + Configurable max_TPCNsigmaKa{"max_TPCNsigmaKa", +3, "max n sigma ka in TPC for exclusion"}; + Configurable min_TPCNsigmaPr{"min_TPCNsigmaPr", -3, "min n sigma pr in TPC for exclusion"}; + Configurable max_TPCNsigmaPr{"max_TPCNsigmaPr", +3, "max n sigma pr in TPC for exclusion"}; + Configurable min_TOFNsigmaEl{"min_TOFNsigmaEl", -3, "min. TOF n sigma for electron inclusion"}; + Configurable max_TOFNsigmaEl{"max_TOFNsigmaEl", +3, "max. TOF n sigma for electron inclusion"}; + } tagCut; + + struct : ConfigurableGroup { + std::string prefix = "probeCut"; + Configurable min_pt_track{"min_pt_track", 0.05, "min pT for single track"}; + Configurable max_pt_track{"max_pt_track", 1e+10, "max pT for single track"}; + Configurable min_eta_track{"min_eta_track", -0.8, "min eta for single track"}; + Configurable max_eta_track{"max_eta_track", +0.8, "max eta for single track"}; + Configurable min_ncrossedrows_tpc{"min_ncrossedrows_tpc", 80, "min ncrossed rows"}; + Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; + Configurable min_ncluster_its{"min_ncluster_its", 5, "min ncluster its"}; + Configurable min_ncluster_itsib{"min_ncluster_itsib", 1, "min ncluster itsib"}; + Configurable min_cr2findable_ratio_tpc{"min_cr2findable_ratio_tpc", 0.8, "min. TPC Ncr/Nf ratio"}; + Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 0.7f, "max fraction of shared clusters in TPC"}; + Configurable max_chi2tpc{"max_chi2tpc", 4.0, "max chi2/NclsTPC"}; + Configurable max_chi2its{"max_chi2its", 36.0, "max chi2/NclsITS"}; + Configurable max_dcaxy{"max_dcaxy", 1, "max dca XY for single track in cm"}; + Configurable max_dcaz{"max_dcaz", 1, "max dca Z for single track in cm"}; + + Configurable min_TPCNsigmaEl{"min_TPCNsigmaEl", -2, "min n sigma e in TPC"}; + Configurable max_TPCNsigmaEl{"max_TPCNsigmaEl", +3, "max n sigma e in TPC"}; + Configurable min_TOFNsigmaEl{"min_TOFNsigmaEl", -3, "min n sigma e in TOF"}; + Configurable max_TOFNsigmaEl{"max_TOFNsigmaEl", +3, "max n sigma e in TOF"}; + } probeCut; + + struct : ConfigurableGroup { + std::string prefix = "pairCut"; + Configurable minMee{"minMee", 0.00, "min mee of pi0 -> ee"}; + Configurable maxMee{"maxMee", 0.01, "max mee of pi0 -> ee"}; + Configurable minPhiV{"minPhiV", 0.f, "min phiv of pi0 -> ee"}; + Configurable maxPhiV{"maxPhiV", M_PI / 2, "max phiv of pi0 -> ee"}; + } pairCut; + + struct : ConfigurableGroup { + std::string prefix = "pfGroup"; + Configurable maxMee{"maxMee", 0.06, "max mee of pi0 -> ee for prefilter to improve S/B for peak extraction of VMs"}; + } pfGroup; + + o2::aod::rctsel::RCTFlagsChecker rctChecker; + + int mRunNumber{0}; + float d_bz{0}; + Service ccdb; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + o2::dataformats::VertexBase mVtx; + o2::base::MatLayerCylSet* lut = nullptr; + + HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + void init(InitContext&) + { + mRunNumber = 0; + d_bz = 0; + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + rctChecker.init(eventCut.cfgRCTLabel.value, eventCut.cfgCheckZDC.value, eventCut.cfgTreatLimitedAcceptanceAsBad.value); + + addhistograms(); + } + + ~createTTP() {} + + template + void initCCDB(TBC const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + // load matLUT for this timestamp + if (!lut) { + LOG(info) << "Loading material look-up table for timestamp: " << bc.timestamp(); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp(lutPath, bc.timestamp())); + } else { + LOG(info) << "Material look-up table already in place. Not reloading."; + } + + auto run3grp_timestamp = bc.timestamp(); + o2::parameters::GRPObject* grpo = 0x0; + o2::parameters::GRPMagField* grpmag = 0x0; + if (grpo) { + o2::base::Propagator::initFieldFromGRP(grpo); + o2::base::Propagator::Instance()->setMatLUT(lut); + // Fetch magnetic field from ccdb for current collision + d_bz = grpo->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + } else { + grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + o2::base::Propagator::Instance()->setMatLUT(lut); + // Fetch magnetic field from ccdb for current collision + d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + } + + mRunNumber = bc.runNumber(); + } + + void addhistograms() + { + // event info + auto hCollisionCounter = fRegistry.add("Event/hCollisionCounter", "collision counter;;Number of events", kTH1D, {{2, -0.5, 1.5}}, false); + hCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); + hCollisionCounter->GetXaxis()->SetBinLabel(2, "accepted"); + + const AxisSpec axis_pt{ConfPtBins, "p_{T} (GeV/c)"}; + const AxisSpec axis_eta{cfgNbinsEta, -1, +1, "#eta"}; + const AxisSpec axis_phi{cfgNbinsPhi, 0.0, 2 * M_PI, "#varphi (rad.)"}; + const AxisSpec axis_sign{3, -1.5, +1.5, "sign"}; + const AxisSpec axis_mass{ConfMllBins, "m_{ee} (GeV/c^{2})"}; + const AxisSpec axis_ptee{ConfPtllBins, "p_{T,ee} (GeV/c)"}; + + const AxisSpec axis_mean_dcaXY{ConfDCABins, "DCA_{xy} (#mum)"}; + const AxisSpec axis_mean_dcaZ{ConfDCABins, "DCA_{z} (#mum)"}; + const AxisSpec axis_pull_dcaXY{ConfDCASigmaBins, "DCA_{xy}/#sigma_{xy}^{DCA}"}; + const AxisSpec axis_pull_dcaZ{ConfDCASigmaBins, "DCA_{z}/#sigma_{z}^{DCA}"}; + + fRegistry.add("Track/hs", "probe electron", kTHnSparseD, {axis_pt, axis_eta, axis_phi, axis_sign, axis_mean_dcaXY, axis_mean_dcaZ, axis_pull_dcaXY, axis_pull_dcaZ}, false); + fRegistry.add("Track/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5.f, +5.f}}, false); + fRegistry.add("Pair/uls/hMvsPt", "dielectron", kTH2D, {axis_mass, axis_ptee}, false); + fRegistry.addClone("Pair/uls/", "Pair/lspp/"); + fRegistry.addClone("Pair/uls/", "Pair/lsmm/"); + fRegistry.add("TAP/hMvsPhiV", "m_{ee} vs. #varphi_{V} ULS;#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{90, 0.f, M_PI}, {100, 0, 0.1}}); + + if (doprocessMC) { + fRegistry.add("Pair/hMvsPt_omega", "#omega->ee", kTH2D, {axis_mass, axis_ptee}, false); + fRegistry.add("Pair/hMvsPt_phi", "#phi->ee", kTH2D, {axis_mass, axis_ptee}, false); + fRegistry.add("Pair/hMvsPt_jpsi", "J/#psi->ee", kTH2D, {axis_mass, axis_ptee}, false); + } + } + + template + bool isTag(TTrack const& track, const float pt, const float eta, const float dcaXY, const float dcaZ, const float cYY, const float cZZ, const float cZY) + { + if (!track.hasITS() || !track.hasTPC()) { + return false; + } + + if (pt < tagCut.min_pt_track || tagCut.max_pt_track < pt) { + return false; + } + + if (eta < tagCut.min_eta_track || tagCut.max_eta_track < eta) { + return false; + } + + if (std::fabs(dcaXY) > tagCut.max_dcaxy || std::fabs(dcaZ) > tagCut.max_dcaz) { + return false; + } + + if (dca3DinSigmaOTF(dcaXY, dcaZ, cYY, cZZ, cZY) > tagCut.max_dca_in_sigma) { + return false; + } + + if (track.itsChi2NCl() < 0.f || tagCut.max_chi2its < track.itsChi2NCl()) { + return false; + } + if (track.itsNCls() < tagCut.min_ncluster_its) { + return false; + } + if (track.itsNClsInnerBarrel() < tagCut.min_ncluster_itsib) { + return false; + } + + if (track.tpcChi2NCl() < 0.f || tagCut.max_chi2tpc < track.tpcChi2NCl()) { + return false; + } + + if (track.tpcNClsFound() < tagCut.min_ncluster_tpc) { + return false; + } + + if (track.tpcNClsCrossedRows() < tagCut.min_ncrossedrows_tpc) { + return false; + } + + if (track.tpcCrossedRowsOverFindableCls() < tagCut.min_tpc_cr_findable_ratio) { + return false; + } + + if (track.tpcFractionSharedCls() > tagCut.max_frac_shared_clusters_tpc) { + return false; + } + + return true; + } + + template + bool isProbe(TTrack const& track, const float pt, const float eta, const float dcaXY, const float dcaZ) + { + if (!track.hasITS() || !track.hasTPC()) { + return false; + } + + if (pt < probeCut.min_pt_track || probeCut.max_pt_track < pt) { + return false; + } + + if (eta < probeCut.min_eta_track || probeCut.max_eta_track < eta) { + return false; + } + + if (std::fabs(dcaXY) > probeCut.max_dcaxy) { + return false; + } + + if (std::fabs(dcaZ) > probeCut.max_dcaz) { + return false; + } + + if (track.itsChi2NCl() > probeCut.max_chi2its) { + return false; + } + + if (track.itsNCls() < probeCut.min_ncluster_its) { + return false; + } + + if (track.itsNClsInnerBarrel() < probeCut.min_ncluster_itsib) { + return false; + } + + if (track.tpcChi2NCl() > probeCut.max_chi2tpc) { + return false; + } + + if (track.tpcNClsFound() < probeCut.min_ncluster_tpc) { + return false; + } + + if (track.tpcNClsCrossedRows() < probeCut.min_ncrossedrows_tpc) { + return false; + } + + if (track.tpcCrossedRowsOverFindableCls() < probeCut.min_cr2findable_ratio_tpc) { + return false; + } + + if (track.tpcFractionSharedCls() > probeCut.max_frac_shared_clusters_tpc) { + return false; + } + + return true; + } + + template + bool isTagElectron(TTrack const& track) + { + bool is_OK_El_TPC = tagCut.min_TPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < tagCut.max_TPCNsigmaEl; + bool is_OK_El_TOFreq = tagCut.min_TOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < tagCut.max_TOFNsigmaEl; + + bool is_OK_Pi_TPC = !(tagCut.min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < tagCut.max_TPCNsigmaPi); + bool is_OK_Ka_TPC = !(tagCut.min_TPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < tagCut.max_TPCNsigmaKa); + bool is_OK_Pr_TPC = !(tagCut.min_TPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < tagCut.max_TPCNsigmaPr); + bool is_OK_El_TOFif = track.hasTOF() ? tagCut.min_TOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < tagCut.max_TOFNsigmaEl : true; + + bool isTOFreq = is_OK_El_TPC && is_OK_Pi_TPC && is_OK_El_TOFreq; + bool isTPC_had_rej = is_OK_El_TPC && is_OK_Pi_TPC && is_OK_Ka_TPC && is_OK_Pr_TPC && is_OK_El_TOFif; + + return isTOFreq || isTPC_had_rej; + } + + template + bool isProbeElectron(TTrack const& track) + { + bool is_El_TPC = probeCut.min_TPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < probeCut.max_TPCNsigmaEl; + bool is_El_TOF = track.hasTOF() ? probeCut.min_TOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < probeCut.max_TOFNsigmaEl : true; + return is_El_TPC && is_El_TOF; + } + + template + bool isSelectedCollision(TCollision const& collision) + { + if (collision.posZ() < eventCut.cfgZvtxMin || eventCut.cfgZvtxMax < collision.posZ()) { + return false; + } + + if (eventCut.cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + + if (eventCut.cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + + if (eventCut.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + + if (eventCut.cfgRequireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + + if (eventCut.cfgRequireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + + if (eventCut.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + + if (eventCut.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + + if (!(eventCut.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventCut.cfgTrackOccupancyMax)) { + return false; + } + + if (!(eventCut.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventCut.cfgFT0COccupancyMax)) { + return false; + } + + if (eventCut.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + return false; + } + + return true; + } + + float dca3DinSigmaOTF(const float dcaXY, const float dcaZ, const float cYY, const float cZZ, const float cZY) + { + float det = cYY * cZZ - cZY * cZY; // determinant + if (det < 0) { + return 999.f; + } else { + return std::sqrt(std::fabs((dcaXY * dcaXY * cZZ + dcaZ * dcaZ * cYY - 2. * dcaXY * dcaZ * cZY) / det / 2.)); // dca 3d in sigma + } + } + + struct lepton { + float pt{0}; + float eta{0}; + float phi{0}; + int8_t sign{0}; + float tpcInnerParam{0}; + float tpcNSigmaEl{999}; + + float dcaXY{999}; + float dcaZ{999}; + float dcaXYinSigma{999}; + float dcaZinSigma{999}; + + bool passTrackTag{false}; + bool passTrackProbe{false}; + bool passPIDTag{false}; + bool passPIDProbe{false}; + int globalIndex{-1}; + int mcParticleId{-1}; + }; + + using MyBCs = soa::Join; + using MyCollisions = soa::Join; + using MyTracks = soa::Join; + + Filter collisionFilter_zvtx = eventCut.cfgZvtxMin < o2::aod::collision::posZ && o2::aod::collision::posZ < eventCut.cfgZvtxMax; + Filter collisionFilter_centrality = (eventCut.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventCut.cfgCentMax) || (eventCut.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventCut.cfgCentMax) || (eventCut.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventCut.cfgCentMax); + Filter collisionFilter_numContrib = eventCut.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventCut.cfgNumContribMax; + Filter collisionFilter_track_occupancy = eventCut.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventCut.cfgTrackOccupancyMax; + Filter collisionFilter_ft0c_occupancy = eventCut.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventCut.cfgFT0COccupancyMax; + + using FilteredMyCollisions = soa::Filtered; + using FilteredMyCollision = FilteredMyCollisions::iterator; + + Filter trackFilter_itstpc = ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) == true && ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC) == true; + Filter trackFilter_tpcpid = probeCut.min_TPCNsigmaEl < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < probeCut.max_TPCNsigmaEl; + using FilteredMyTracks = soa::Filtered; + + SliceCache cache; + Preslice perCol = o2::aod::track::collisionId; + // Partition posTracks = o2::aod::track::signed1Pt > 0.f; + // Partition negTracks = o2::aod::track::signed1Pt < 0.f; + + template + void runTAP(TBCs const&, TCollisions const& collisions, TTracks const& tracks) + { + for (const auto& collision : collisions) { + auto bc = collision.template bc_as(); + initCCDB(bc); + + float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[eventCut.cfgCentEstimator] < eventCut.cfgCentMin || eventCut.cfgCentMax < centralities[eventCut.cfgCentEstimator]) { + continue; + } + + fRegistry.fill(HIST("Event/hCollisionCounter"), 0); + if (!isSelectedCollision(collision)) { + continue; + } + fRegistry.fill(HIST("Event/hCollisionCounter"), 1); + + mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); + mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + + // auto posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + // auto negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + // // LOGF(info, "posTracks_per_coll.size() = %d, negTracks_per_coll.size() = %d", posTracks_per_coll.size(), negTracks_per_coll.size()); + // if (posTracks_per_coll.size() + negTracks_per_coll.size() < 2) { + // continue; + // } + + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + if (tracks_per_coll.size() < 2) { // at least 2 tracks are necessary to make a pair. + continue; + } + + o2::dataformats::DCA mDcaInfoCov; + std::vector posLeptons; + std::vector negLeptons; + posLeptons.reserve(tracks_per_coll.size()); + negLeptons.reserve(tracks_per_coll.size()); + + for (const auto& track : tracks_per_coll) { + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(o2::track::PID::Electron); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + continue; + } + + lepton tmp; + tmp.pt = trackParCov.getPt(); + tmp.eta = trackParCov.getEta(); + tmp.phi = RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U); + tmp.sign = track.sign(); + tmp.tpcInnerParam = track.tpcInnerParam(); + tmp.tpcNSigmaEl = track.tpcNSigmaEl(); + tmp.dcaXY = mDcaInfoCov.getY() * 1e+4; // in um + tmp.dcaZ = mDcaInfoCov.getZ() * 1e+4; // in um + tmp.dcaXYinSigma = mDcaInfoCov.getY() / std::sqrt(trackParCov.getSigmaY2()); + tmp.dcaZinSigma = mDcaInfoCov.getZ() / std::sqrt(trackParCov.getSigmaZ2()); + tmp.passTrackTag = isTag(track, trackParCov.getPt(), trackParCov.getEta(), mDcaInfoCov.getY(), mDcaInfoCov.getZ(), trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()); + tmp.passTrackProbe = isProbe(track, trackParCov.getPt(), trackParCov.getEta(), mDcaInfoCov.getY(), mDcaInfoCov.getZ()); + tmp.passPIDTag = isTagElectron(track); + tmp.passPIDProbe = isProbeElectron(track); + tmp.globalIndex = track.globalIndex(); + tmp.mcParticleId = -1; + + if (tmp.passTrackTag || tmp.passTrackProbe) { + if (track.sign() > 0) { + posLeptons.emplace_back(tmp); + } else { + negLeptons.emplace_back(tmp); + } + } + } // end of track loop + + auto tagPositrons = std::views::filter(posLeptons, [](lepton t) { return t.passTrackTag == true && t.passPIDTag == true; }); + auto tagElectrons = std::views::filter(negLeptons, [](lepton t) { return t.passTrackTag == true && t.passPIDTag == true; }); + auto probePositrons = std::views::filter(posLeptons, [](lepton t) { return t.passTrackProbe == true && t.passPIDProbe == true; }); + auto probeElectrons = std::views::filter(negLeptons, [](lepton t) { return t.passTrackProbe == true && t.passPIDProbe == true; }); + + for (const auto& tag : tagPositrons) { + for (const auto& probe : probeElectrons) { + ROOT::Math::PtEtaPhiMVector v1(tag.pt, tag.eta, RecoDecay::constrainAngle(tag.phi, 0, 1U), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(probe.pt, probe.eta, RecoDecay::constrainAngle(probe.phi, 0, 1U), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float mee = v12.M(); + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), tag.sign, probe.sign, d_bz); + fRegistry.fill(HIST("TAP/hMvsPhiV"), phiv, mee); + + if ((pairCut.minMee < mee && mee < pairCut.maxMee) && (pairCut.minPhiV < phiv && phiv < pairCut.maxPhiV)) { + fRegistry.fill(HIST("Track/hs"), probe.pt, probe.eta, RecoDecay::constrainAngle(probe.phi, 0, 1U), probe.sign, probe.dcaXY, probe.dcaZ, probe.dcaXYinSigma, probe.dcaZinSigma); + fRegistry.fill(HIST("Track/hTPCNsigmaEl"), probe.tpcInnerParam, probe.tpcNSigmaEl); + } + } // end of electron loop + } // end of positron loop + + for (const auto& tag : tagElectrons) { + for (const auto& probe : probePositrons) { + ROOT::Math::PtEtaPhiMVector v1(tag.pt, tag.eta, RecoDecay::constrainAngle(tag.phi, 0, 1U), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(probe.pt, probe.eta, RecoDecay::constrainAngle(probe.phi, 0, 1U), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float mee = v12.M(); + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), tag.sign, probe.sign, d_bz); + fRegistry.fill(HIST("TAP/hMvsPhiV"), phiv, mee); + + if ((pairCut.minMee < mee && mee < pairCut.maxMee) && (pairCut.minPhiV < phiv && phiv < pairCut.maxPhiV)) { + fRegistry.fill(HIST("Track/hs"), probe.pt, probe.eta, RecoDecay::constrainAngle(probe.phi, 0, 1U), probe.sign, probe.dcaXY, probe.dcaZ, probe.dcaXYinSigma, probe.dcaZinSigma); + fRegistry.fill(HIST("Track/hTPCNsigmaEl"), probe.tpcInnerParam, probe.tpcNSigmaEl); + } + } // end of positron loop + } // end of electron loop + + auto probePositronsVM = std::views::filter(posLeptons, [](lepton t) { return t.passTrackProbe == true && t.passPIDTag == true; }); + auto probeElectronsVM = std::views::filter(negLeptons, [](lepton t) { return t.passTrackProbe == true && t.passPIDTag == true; }); + + // for VM peak analyses + std::vector trackIdsFromPi0; + trackIdsFromPi0.reserve(posLeptons.size() + negLeptons.size()); + + for (const auto& t1 : probePositronsVM) { + for (const auto& t2 : probeElectronsVM) { + ROOT::Math::PtEtaPhiMVector v1(t1.pt, t1.eta, t1.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt, t2.eta, t2.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + if (v12.M() < pfGroup.maxMee) { + trackIdsFromPi0.emplace_back(t1.globalIndex); + trackIdsFromPi0.emplace_back(t2.globalIndex); + } + } // end of electron loop + } // end of positron loop + + for (const auto& t1 : probePositronsVM) { + for (const auto& t2 : probeElectronsVM) { + if (std::find(trackIdsFromPi0.begin(), trackIdsFromPi0.end(), t1.globalIndex) != trackIdsFromPi0.end()) { + continue; + } + if (std::find(trackIdsFromPi0.begin(), trackIdsFromPi0.end(), t2.globalIndex) != trackIdsFromPi0.end()) { + continue; + } + ROOT::Math::PtEtaPhiMVector v1(t1.pt, t1.eta, t1.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt, t2.eta, t2.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + fRegistry.fill(HIST("Pair/uls/hMvsPt"), v12.M(), v12.Pt()); + } // end of electron loop + } // end of positron loop + + size_t nposVM = static_cast(std::ranges::distance(probePositronsVM)); + size_t neleVM = static_cast(std::ranges::distance(probeElectronsVM)); + + for (size_t i = 0; i < nposVM; i++) { + auto t1 = posLeptons[i]; + for (size_t j = i + 1; j < nposVM; j++) { + auto t2 = posLeptons[j]; + if (std::find(trackIdsFromPi0.begin(), trackIdsFromPi0.end(), t1.globalIndex) != trackIdsFromPi0.end()) { + continue; + } + if (std::find(trackIdsFromPi0.begin(), trackIdsFromPi0.end(), t2.globalIndex) != trackIdsFromPi0.end()) { + continue; + } + ROOT::Math::PtEtaPhiMVector v1(t1.pt, t1.eta, t1.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt, t2.eta, t2.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + fRegistry.fill(HIST("Pair/lspp/hMvsPt"), v12.M(), v12.Pt()); + } // end of positron loop + } // end of positron loop + + for (size_t i = 0; i < neleVM; i++) { + auto t1 = negLeptons[i]; + for (size_t j = i + 1; j < neleVM; j++) { + auto t2 = negLeptons[j]; + if (std::find(trackIdsFromPi0.begin(), trackIdsFromPi0.end(), t1.globalIndex) != trackIdsFromPi0.end()) { + continue; + } + if (std::find(trackIdsFromPi0.begin(), trackIdsFromPi0.end(), t2.globalIndex) != trackIdsFromPi0.end()) { + continue; + } + ROOT::Math::PtEtaPhiMVector v1(t1.pt, t1.eta, t1.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt, t2.eta, t2.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + fRegistry.fill(HIST("Pair/lsmm/hMvsPt"), v12.M(), v12.Pt()); + } // end of electron loop + } // end of electron loop + + posLeptons.clear(); + posLeptons.shrink_to_fit(); + negLeptons.clear(); + negLeptons.shrink_to_fit(); + trackIdsFromPi0.clear(); + trackIdsFromPi0.shrink_to_fit(); + + } // end of collision loop + } // end of runTAP + + void processData(MyBCs const& bcs, FilteredMyCollisions const& collisions, FilteredMyTracks const& tracks) + { + runTAP(bcs, collisions, tracks); + } + PROCESS_SWITCH(createTTP, processData, "process data", true); + + using MyCollisionsMC = soa::Join; + using MyTracksMC = soa::Join; + + using FilteredMyCollisionsMC = soa::Filtered; + using FilteredMyTracksMC = soa::Filtered; + + void processMC(MyBCs const&, FilteredMyCollisionsMC const& collisions, FilteredMyTracksMC const& tracks, aod::McCollisions const&, aod::McParticles const& mcParticles) + { + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + + auto bc = collision.template bc_as(); + initCCDB(bc); + + float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[eventCut.cfgCentEstimator] < eventCut.cfgCentMin || eventCut.cfgCentMax < centralities[eventCut.cfgCentEstimator]) { + continue; + } + + fRegistry.fill(HIST("Event/hCollisionCounter"), 0); + if (!isSelectedCollision(collision)) { + continue; + } + fRegistry.fill(HIST("Event/hCollisionCounter"), 1); + + mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); + mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + std::vector posLeptons; + std::vector negLeptons; + posLeptons.reserve(tracks_per_coll.size()); + negLeptons.reserve(tracks_per_coll.size()); + + o2::dataformats::DCA mDcaInfoCov; + for (const auto& track : tracks_per_coll) { + if (!track.has_mcParticle()) { + continue; + } + auto mcParticle = track.template mcParticle_as(); + auto mcCollision = mcParticle.template mcCollision_as(); + if (std::abs(mcParticle.pdgCode()) != 11) { + continue; + } + if (!(mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator())) { + continue; + } + if (!mcParticle.has_mothers()) { + continue; + } + + if (cfgEventGeneratorType >= 0 && mcCollision.getSubGeneratorId() != cfgEventGeneratorType) { + continue; + } + + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(o2::track::PID::Electron); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + continue; + } + if (!isProbe(track, trackParCov.getPt(), trackParCov.getEta(), mDcaInfoCov.getY(), mDcaInfoCov.getZ())) { // PID cut is not necessary, because this is true electron. + continue; + } + + fRegistry.fill(HIST("Track/hs"), trackParCov.getPt(), trackParCov.getEta(), RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U), track.sign(), mDcaInfoCov.getY() * 1e+4, mDcaInfoCov.getZ() * 1e+4, mDcaInfoCov.getY() / std::sqrt(trackParCov.getSigmaY2()), mDcaInfoCov.getZ() / std::sqrt(trackParCov.getSigmaZ2())); + + lepton tmp; + tmp.pt = trackParCov.getPt(); + tmp.eta = trackParCov.getEta(); + tmp.phi = RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U); + tmp.globalIndex = track.globalIndex(); + tmp.mcParticleId = mcParticle.globalIndex(); + + if (track.sign() > 0) { + posLeptons.emplace_back(tmp); + } else { + negLeptons.emplace_back(tmp); + } + } // end of track loop + + for (const auto& pos : posLeptons) { + for (const auto& neg : negLeptons) { + ROOT::Math::PtEtaPhiMVector v1(pos.pt, pos.eta, pos.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(neg.pt, neg.eta, neg.phi, o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + + auto posmc = mcParticles.rawIteratorAt(pos.mcParticleId); + auto negmc = mcParticles.rawIteratorAt(neg.mcParticleId); + + int omegaId = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(posmc, negmc, -11, 11, 223, mcParticles); + int phiId = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(posmc, negmc, -11, 11, 333, mcParticles); + int jpsiId = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(posmc, negmc, -11, 11, 443, mcParticles); + if (omegaId > 0) { + auto mcMother = mcParticles.rawIteratorAt(omegaId); + int ndau = mcMother.daughtersIds()[1] - mcMother.daughtersIds()[0] + 1; + if (ndau == 2) { + fRegistry.fill(HIST("Pair/hMvsPt_omega"), v12.M(), v12.Pt()); + } + } else if (phiId > 0) { + auto mcMother = mcParticles.rawIteratorAt(phiId); + int ndau = mcMother.daughtersIds()[1] - mcMother.daughtersIds()[0] + 1; + if (ndau == 2) { + fRegistry.fill(HIST("Pair/hMvsPt_phi"), v12.M(), v12.Pt()); + } + } else if (jpsiId > 0) { + auto mcMother = mcParticles.rawIteratorAt(jpsiId); + int ndau = mcMother.daughtersIds()[1] - mcMother.daughtersIds()[0] + 1; + if (ndau == 2) { + fRegistry.fill(HIST("Pair/hMvsPt_jpsi"), v12.M(), v12.Pt()); + } + } + } // end of electron loop + } // end of positron loop + + posLeptons.clear(); + posLeptons.shrink_to_fit(); + negLeptons.clear(); + negLeptons.shrink_to_fit(); + + } // end of collision loop + } + PROCESS_SWITCH(createTTP, processMC, "process MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"create-ttp"})}; +} diff --git a/PWGEM/Dilepton/TableProducer/dielectronProducer.cxx b/PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx similarity index 77% rename from PWGEM/Dilepton/TableProducer/dielectronProducer.cxx rename to PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx index 945ac2853c9..70ed37ceb05 100644 --- a/PWGEM/Dilepton/TableProducer/dielectronProducer.cxx +++ b/PWGEM/Dilepton/Tasks/dielectronMixingTest.cxx @@ -14,7 +14,7 @@ // This code is for dielectron analyses. // Please write to: daiki.sekihata@cern.ch -#include "PWGEM/Dilepton/Core/DileptonProducer.h" +#include "PWGEM/Dilepton/Core/DileptonMixingTest.h" #include "PWGEM/Dilepton/Utils/PairUtilities.h" #include @@ -24,5 +24,6 @@ using namespace o2::framework; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask>(cfgc, TaskName{"dielectron-producer"})}; + return WorkflowSpec{ + adaptAnalysisTask>(cfgc, TaskName{"dielectronMixingTests"})}; } diff --git a/PWGEM/Dilepton/Tasks/dileptonPolarization.cxx b/PWGEM/Dilepton/Tasks/dileptonPolarization.cxx deleted file mode 100644 index df4f9c14fda..00000000000 --- a/PWGEM/Dilepton/Tasks/dileptonPolarization.cxx +++ /dev/null @@ -1,650 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// ======================== -// -// This code runs loop over leptons. -// Please write to: daiki.sekihata@cern.ch - -#include "PWGEM/Dilepton/DataModel/dileptonTables.h" -#include "PWGEM/Dilepton/Utils/EMTrack.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" - -#include "Common/Core/RecoDecay.h" -#include "Common/DataModel/EventSelection.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace o2; -using namespace o2::aod; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace o2::soa; -using namespace o2::aod::pwgem::dilepton::utils; -using namespace o2::aod::pwgem::dilepton::utils::pairutil; - -struct DileptonPolarization { - // Configurables - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - - Configurable cfgPairType{"cfgPairType", 0, "0:dielectron, 1:dimuon"}; - Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; - Configurable cfgDoMix{"cfgDoMix", true, "flag for event mixing"}; - // Configurable ndepth{"ndepth", 10000, "depth for event mixing"}; - Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; - ConfigurableAxis ConfVtxBins{"ConfVtxBins", {10, -10.0f, 10.f}, "Mixing bins - z-vertex"}; - ConfigurableAxis ConfCentBins{"ConfCentBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; - ConfigurableAxis ConfEPBins{"ConfEPBins", {1, -M_PI / 2, +M_PI / 2}, "Mixing bins - event plane angle"}; - ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; - Configurable cfgPolarizationFrame{"cfgPolarizationFrame", 0, "frame of polarization. 0:CS, 1:HX, else:FATAL"}; - Configurable cfgUseAbs{"cfgUseAbs", false, "flag to use absolute value for cos_theta and phi"}; // this is to increase statistics per bin. - Configurable cfgDoULS{"cfgDoULS", true, "flag to perform ULS pairing"}; - Configurable cfgDoLS{"cfgDoLS", true, "flag to perform LS pairing"}; - - ConfigurableAxis ConfMllBins{"ConfMllBins", {VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.1, 11.2, 11.3, 11.4, 11.50, 11.6, 11.7, 11.8, 11.9, 12.0}, "mll bins for output histograms"}; - ConfigurableAxis ConfPtllBins{"ConfPtllBins", {VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; - ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; - ConfigurableAxis ConfYllBins{"ConYllBins", {1, -1.f, 1.f}, "yll bins for output histograms"}; // pair rapidity - - ConfigurableAxis ConfPolarizationCosThetaBins{"ConfPolarizationCosThetaBins", {20, -1.f, 1.f}, "cos(theta) bins for polarization analysis"}; - ConfigurableAxis ConfPolarizationPhiBins{"ConfPolarizationPhiBins", {1, -M_PI, M_PI}, "phi bins for polarization analysis"}; - ConfigurableAxis ConfPolarizationQuadMomBins{"ConfPolarizationQuadMomBins", {15, -0.5, 1}, "quadrupole moment bins for polarization analysis"}; // quardrupole moment <(3 x cos^2(theta) -1)/2> - - struct : ConfigurableGroup { - std::string prefix = "eventcut_group"; - Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; - Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; - // Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; - // Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; - // Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; - // Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; - // Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; - // Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. - // Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. - // Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; - Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; - Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; - Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; - Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; - Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; - Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; - } eventcuts; - - struct : ConfigurableGroup { - std::string prefix = "dileptoncut_group"; - Configurable cfg_min_pair_mass{"cfg_min_pair_mass", 0.0, "min pair mass"}; - Configurable cfg_max_pair_mass{"cfg_max_pair_mass", 1e+10, "max pair mass"}; - Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pT"}; - Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pT"}; - Configurable cfg_min_pair_y{"cfg_min_pair_y", -0.9, "min pair rapidity"}; - Configurable cfg_max_pair_y{"cfg_max_pair_y", +0.9, "max pair rapidity"}; - - Configurable cfg_min_track_pt{"cfg_min_track_pt", 0.2, "min pT for single track"}; - Configurable cfg_max_track_pt{"cfg_max_track_pt", 1e+10, "max pT for single track"}; - Configurable cfg_min_track_eta{"cfg_min_track_eta", -0.9, "min eta for single track"}; - Configurable cfg_max_track_eta{"cfg_max_track_eta", +0.9, "max eta for single track"}; - } dileptoncuts; - - struct : ConfigurableGroup { - std::string prefix = "accBins"; - Configurable cfgDM{"cfgDM", 0.01, "dm for lorentz boost"}; - Configurable cfgDPt{"cfgDPt", 0.1, "dpT for lorentz boost"}; - Configurable cfgDEta{"cfgDEta", 0.1, "deta for lorentz boost"}; - Configurable cfgDPhi{"cfgDPhi", 0.087, "dphi (rad.) for lorentz boost"}; - } accBins; - - Service ccdb; - int mRunNumber; - // float d_bz; - - HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; - static constexpr std::string_view pair_sign_types[3] = {"uls/", "lspp/", "lsmm/"}; - - std::mt19937 engine; - std::vector zvtx_bin_edges; - std::vector cent_bin_edges; - std::vector ep_bin_edges; - std::vector occ_bin_edges; - - float leptonM1 = 0.f; - float leptonM2 = 0.f; - - float beamM1 = o2::constants::physics::MassProton; // mass of beam - float beamM2 = o2::constants::physics::MassProton; // mass of beam - float beamE1 = 0.f; // beam energy - float beamE2 = 0.f; // beam energy - float beamP1 = 0.f; // beam momentum - float beamP2 = 0.f; // beam momentum - - void init(InitContext& /*context*/) - { - mRunNumber = 0; - // d_bz = 0; - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - - if (cfgPairType.value == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron)) { - leptonM1 = o2::constants::physics::MassElectron; - leptonM2 = o2::constants::physics::MassElectron; - } else if (cfgPairType.value == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon)) { - leptonM1 = o2::constants::physics::MassMuon; - leptonM2 = o2::constants::physics::MassMuon; - } else { - LOG(fatal) << "Please select either dielectron or dimuon"; - } - - if (ConfVtxBins.value[0] == VARIABLE_WIDTH) { - zvtx_bin_edges = std::vector(ConfVtxBins.value.begin(), ConfVtxBins.value.end()); - zvtx_bin_edges.erase(zvtx_bin_edges.begin()); - for (const auto& edge : zvtx_bin_edges) { - LOGF(info, "VARIABLE_WIDTH: zvtx_bin_edges = %f", edge); - } - } else { - int nbins = static_cast(ConfVtxBins.value[0]); - float xmin = static_cast(ConfVtxBins.value[1]); - float xmax = static_cast(ConfVtxBins.value[2]); - zvtx_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - zvtx_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - LOGF(info, "FIXED_WIDTH: zvtx_bin_edges[%d] = %f", i, zvtx_bin_edges[i]); - } - } - - if (ConfCentBins.value[0] == VARIABLE_WIDTH) { - cent_bin_edges = std::vector(ConfCentBins.value.begin(), ConfCentBins.value.end()); - cent_bin_edges.erase(cent_bin_edges.begin()); - for (const auto& edge : cent_bin_edges) { - LOGF(info, "VARIABLE_WIDTH: cent_bin_edges = %f", edge); - } - } else { - int nbins = static_cast(ConfCentBins.value[0]); - float xmin = static_cast(ConfCentBins.value[1]); - float xmax = static_cast(ConfCentBins.value[2]); - cent_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - cent_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - LOGF(info, "FIXED_WIDTH: cent_bin_edges[%d] = %f", i, cent_bin_edges[i]); - } - } - - if (ConfEPBins.value[0] == VARIABLE_WIDTH) { - ep_bin_edges = std::vector(ConfEPBins.value.begin(), ConfEPBins.value.end()); - ep_bin_edges.erase(ep_bin_edges.begin()); - for (const auto& edge : ep_bin_edges) { - LOGF(info, "VARIABLE_WIDTH: ep_bin_edges = %f", edge); - } - } else { - int nbins = static_cast(ConfEPBins.value[0]); - float xmin = static_cast(ConfEPBins.value[1]); - float xmax = static_cast(ConfEPBins.value[2]); - ep_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - ep_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - LOGF(info, "FIXED_WIDTH: ep_bin_edges[%d] = %f", i, ep_bin_edges[i]); - } - } - - LOGF(info, "cfgOccupancyEstimator = %d", cfgOccupancyEstimator.value); - if (ConfOccupancyBins.value[0] == VARIABLE_WIDTH) { - occ_bin_edges = std::vector(ConfOccupancyBins.value.begin(), ConfOccupancyBins.value.end()); - occ_bin_edges.erase(occ_bin_edges.begin()); - for (const auto& edge : occ_bin_edges) { - LOGF(info, "VARIABLE_WIDTH: occ_bin_edges = %f", edge); - } - } else { - int nbins = static_cast(ConfOccupancyBins.value[0]); - float xmin = static_cast(ConfOccupancyBins.value[1]); - float xmax = static_cast(ConfOccupancyBins.value[2]); - occ_bin_edges.resize(nbins + 1); - for (int i = 0; i < nbins + 1; i++) { - occ_bin_edges[i] = (xmax - xmin) / (nbins)*i + xmin; - LOGF(info, "FIXED_WIDTH: occ_bin_edges[%d] = %f", i, occ_bin_edges[i]); - } - } - - std::random_device seed_gen; - engine = std::mt19937(seed_gen()); - - addhistograms(); - - fRegistry.add("Pair/mix/hDiffBC", "diff. global BC in mixed event;|BC_{current} - BC_{mixed}|", kTH1D, {{10001, -0.5, 10000.5}}, true); - } - - template - void initCCDB(TCollision const& collision) - { - if (mRunNumber == collision.runNumber()) { - return; - } - - // // In case override, don't proceed, please - no CCDB access required - // if (d_bz_input > -990) { - // d_bz = d_bz_input; - // o2::parameters::GRPMagField grpmag; - // if (std::fabs(d_bz) > 1e-5) { - // grpmag.setL3Current(30000.f / (d_bz / 5.0f)); - // } - // o2::base::Propagator::initFieldFromGRP(&grpmag); - // mRunNumber = collision.runNumber(); - // return; - // } - - // auto run3grp_timestamp = collision.timestamp(); - // o2::parameters::GRPObject* grpo = 0x0; - // o2::parameters::GRPMagField* grpmag = 0x0; - // if (!skipGRPOquery) - // grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - // if (grpo) { - // o2::base::Propagator::initFieldFromGRP(grpo); - // // Fetch magnetic field from ccdb for current collision - // d_bz = grpo->getNominalL3Field(); - // LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - // } else { - // grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - // if (!grpmag) { - // LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - // } - // o2::base::Propagator::initFieldFromGRP(grpmag); - // // Fetch magnetic field from ccdb for current collision - // d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - // LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kG"; - // } - - auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", collision.timestamp()); - int beamZ1 = grplhcif->getBeamZ(o2::constants::lhc::BeamC); - int beamZ2 = grplhcif->getBeamZ(o2::constants::lhc::BeamA); - int beamA1 = grplhcif->getBeamA(o2::constants::lhc::BeamC); - int beamA2 = grplhcif->getBeamA(o2::constants::lhc::BeamA); - beamE1 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamC); - beamE2 = grplhcif->getBeamEnergyPerNucleonInGeV(o2::constants::lhc::BeamA); - beamM1 = o2::constants::physics::MassProton * beamA1; - beamM2 = o2::constants::physics::MassProton * beamA2; - beamP1 = std::sqrt(std::pow(beamE1, 2) - std::pow(beamM1, 2)); - beamP2 = std::sqrt(std::pow(beamE2, 2) - std::pow(beamM2, 2)); - LOGF(info, "beamZ1 = %d, beamZ2 = %d, beamA1 = %d, beamA2 = %d, beamE1 = %f (GeV), beamE2 = %f (GeV), beamM1 = %f (GeV), beamM2 = %f (GeV), beamP1 = %f (GeV), beamP2 = %f (GeV)", beamZ1, beamZ2, beamA1, beamA2, beamE1, beamE2, beamM1, beamM2, beamP1, beamP2); - - mRunNumber = collision.runNumber(); - } - - ~DileptonPolarization() {} - - void addhistograms() - { - auto hCollisionCounter = fRegistry.add("Event/before/hCollisionCounter", "collision counter;;Number of events", kTH1D, {{9, 0.5, 9 + 0.5}}, false); - hCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); - hCollisionCounter->GetXaxis()->SetBinLabel(2, "FT0AND"); - hCollisionCounter->GetXaxis()->SetBinLabel(3, "No TF border"); - hCollisionCounter->GetXaxis()->SetBinLabel(4, "No ITS ROF border"); - hCollisionCounter->GetXaxis()->SetBinLabel(5, "No Same Bunch Pileup"); - hCollisionCounter->GetXaxis()->SetBinLabel(6, "Is Good Zvtx FT0vsPV"); - hCollisionCounter->GetXaxis()->SetBinLabel(7, "sel8"); - hCollisionCounter->GetXaxis()->SetBinLabel(8, "|Z_{vtx}| < 10 cm"); - hCollisionCounter->GetXaxis()->SetBinLabel(9, "accepted"); - - fRegistry.add("Event/before/hZvtx", "vertex z; Z_{vtx} (cm)", kTH1D, {{100, -50, +50}}, false); - fRegistry.add("Event/before/hCentFT0C", "hCentFT0C;centrality FT0C (%)", kTH1D, {{110, 0, 110}}, false); - fRegistry.add("Event/before/hCorrOccupancy", "occupancy correlation;FT0C occupancy;track occupancy", kTH2D, {{200, 0, 200000}, {200, 0, 20000}}, false); - fRegistry.add("Event/before/hEP2_CentFT0C_forMix", "2nd harmonics event plane for mix;centrality FT0C (%);#Psi_{2} (rad.)", kTH2F, {{110, 0, 110}, {180, -M_PI_2, +M_PI_2}}, false); - fRegistry.addClone("Event/before/", "Event/after/"); - - std::string mass_axis_title = "m_{ll} (GeV/c^{2})"; - std::string pair_pt_axis_title = "p_{T,ll} (GeV/c)"; - std::string pair_dca_axis_title = "DCA_{ll} (#sigma)"; - std::string pair_y_axis_title = "y_{ll}"; - - if (cfgPairType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDielectron)) { - mass_axis_title = "m_{ee} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,ee} (GeV/c)"; - pair_dca_axis_title = "DCA_{ee} (#sigma)"; - pair_y_axis_title = "y_{ee}"; - } else if (cfgPairType == static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPairType::kDimuon)) { - mass_axis_title = "m_{#mu#mu} (GeV/c^{2})"; - pair_pt_axis_title = "p_{T,#mu#mu} (GeV/c)"; - pair_dca_axis_title = "DCA_{#mu#mu} (#sigma)"; - pair_y_axis_title = "y_{#mu#mu}"; - } - - // pair info - const AxisSpec axis_mass{ConfMllBins, mass_axis_title}; - const AxisSpec axis_pt{ConfPtllBins, pair_pt_axis_title}; - const AxisSpec axis_dca{ConfDCAllBins, pair_dca_axis_title}; - const AxisSpec axis_y{ConfYllBins, pair_y_axis_title}; - - std::string frameName = "CS"; - if (cfgPolarizationFrame == 0) { - frameName = "CS"; - } else if (cfgPolarizationFrame == 1) { - frameName = "HX"; - } else { - LOG(fatal) << "set 0 or 1 to cfgPolarizationFrame!"; - } - - const AxisSpec axis_cos_theta{ConfPolarizationCosThetaBins, Form("cos(#theta^{%s})", frameName.data())}; - const AxisSpec axis_phi{ConfPolarizationPhiBins, Form("#varphi^{%s} (rad.)", frameName.data())}; - const AxisSpec axis_quadmom{ConfPolarizationQuadMomBins, Form("#frac{3 cos^{2}(#theta^{%s}) -1}{2}", frameName.data())}; - fRegistry.add("Pair/same/uls/hs", "dilepton", kTHnSparseD, {axis_mass, axis_pt, axis_dca, axis_y, axis_cos_theta, axis_phi, axis_quadmom}, true); - - fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); - fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); - fRegistry.addClone("Pair/same/", "Pair/mix/"); - fRegistry.add("Pair/same/uls/hEta", "#eta_{ll}", kTH1D, {{2000, -10, 10}}, true); - // fRegistry.add("Pair/mix/uls/hBeta", "#beta for Lorentz boost;#beta^{same};(#beta^{mix} - #beta^{same})/#beta^{same}", kTH2D, {{100, 0, 1}, {400, -0.2, 0.2}}, true); - } - - template - bool fillPairInfo(TCollision const& collision, TDilepton const& dilepton, TMixingBin const& mixingBin) - { - float weight = 1.f; - ROOT::Math::PtEtaPhiMVector v1(dilepton.pt1(), dilepton.eta1(), dilepton.phi1(), leptonM1); - ROOT::Math::PtEtaPhiMVector v2(dilepton.pt2(), dilepton.eta2(), dilepton.phi2(), leptonM2); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - - if (v12.M() < dileptoncuts.cfg_min_pair_mass || dileptoncuts.cfg_max_pair_mass < v12.M()) { - return false; - } - - if (v12.Pt() < dileptoncuts.cfg_min_pair_pt || dileptoncuts.cfg_max_pair_pt < v12.Pt()) { - return false; - } - - if (v12.Rapidity() < dileptoncuts.cfg_min_pair_y || dileptoncuts.cfg_max_pair_y < v12.Rapidity()) { - return false; - } - - float pair_dca = pairDCAQuadSum(dilepton.dca1(), dilepton.dca2()); - float cos_thetaPol = 999, phiPol = 999.f; - auto arrM = std::array{static_cast(v12.Px()), static_cast(v12.Py()), static_cast(v12.Pz()), static_cast(v12.M())}; - auto random_sign = std::pow(-1, engine() % 2); // -1^0 = +1 or -1^1 = -1; - auto arrD = dilepton.sign1() * dilepton.sign2() < 0 ? (dilepton.sign1() > 0 ? std::array{static_cast(v1.Px()), static_cast(v1.Py()), static_cast(v1.Pz()), leptonM1} : std::array{static_cast(v2.Px()), static_cast(v2.Py()), static_cast(v2.Pz()), leptonM2}) : (random_sign > 0 ? std::array{static_cast(v1.Px()), static_cast(v1.Py()), static_cast(v1.Pz()), leptonM1} : std::array{static_cast(v2.Px()), static_cast(v2.Py()), static_cast(v2.Pz()), leptonM2}); - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - - if (cfgUseAbs) { - cos_thetaPol = std::fabs(cos_thetaPol); - phiPol = std::fabs(phiPol); - } - - if (dilepton.sign1() * dilepton.sign2() < 0) { // ULS - fRegistry.fill(HIST("Pair/same/uls/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); - fRegistry.fill(HIST("Pair/same/uls/hEta"), v12.Eta(), weight); - } else if (dilepton.sign1() > 0 && dilepton.sign2() > 0) { // LS++ - fRegistry.fill(HIST("Pair/same/lspp/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); - fRegistry.fill(HIST("Pair/same/lspp/hEta"), v12.Eta(), weight); - } else if (dilepton.sign1() < 0 && dilepton.sign2() < 0) { // LS-- - fRegistry.fill(HIST("Pair/same/lsmm/hs"), v12.M(), v12.Pt(), pair_dca, v12.Rapidity(), cos_thetaPol, phiPol, quadmom, weight); - fRegistry.fill(HIST("Pair/same/lsmm/hEta"), v12.Eta(), weight); - } - - float phi12_tmp = v12.Phi(); - o2::math_utils::bringTo02Pi(phi12_tmp); - EMPair empair = EMPair(v12.Pt(), v12.Eta(), phi12_tmp, v12.M(), 0); - empair.setPositiveLegPxPyPzM(arrD[0], arrD[1], arrD[2], leptonM1); - // empair.setNegativeLegPtEtaPhiM(t2.pt(), t2.eta(), t2.phi(), leptonM2); - empair.setPairDCA(pair_dca); - auto pair_tmp = std::make_pair(collision.globalIndex(), empair); - if (dilepton.sign1() * dilepton.sign2() < 0) { // ULS - mapMixingULS[mixingBin].emplace_back(pair_tmp); - } else if (dilepton.sign1() > 0 && dilepton.sign2() > 0) { // LS++ - mapMixingLSPP[mixingBin].emplace_back(pair_tmp); - } else if (dilepton.sign1() < 0 && dilepton.sign2() < 0) { // LS-- - mapMixingLSMM[mixingBin].emplace_back(pair_tmp); - } - return true; - } - - template - void fillMixedPairInfo(TPairs const& pairs) - { - float weight = 1.f; - - for (const auto& pair1 : pairs) { - auto globalBC1 = map_mixed_eventId_to_globalBC[std::get<0>(pair1)]; - auto empair1 = std::get<1>(pair1); - auto v_pos = empair1.getPositiveLeg(); // pt, eta, phi, M - auto arrD = std::array{static_cast(v_pos.Px()), static_cast(v_pos.Py()), static_cast(v_pos.Pz()), leptonM1}; - - auto pairs_in_same_ptetaphim_bin = std::views::filter(pairs, [&](std::pair t) { return std::get<0>(t) != std::get<0>(pair1) && std::fabs(std::get<1>(t).mass() - empair1.mass()) < accBins.cfgDM && std::fabs(std::get<1>(t).pt() - empair1.pt()) < accBins.cfgDPt && std::fabs(std::get<1>(t).eta() - empair1.eta()) < accBins.cfgDEta && std::fabs(RecoDecay::constrainAngle(std::get<1>(t).phi() - empair1.phi(), -o2::constants::math::PI, 1U)) < accBins.cfgDPhi; }); - - for (const auto& pair2 : pairs_in_same_ptetaphim_bin) { - auto globalBC2 = map_mixed_eventId_to_globalBC[std::get<0>(pair2)]; - uint64_t diffBC = std::max(globalBC1, globalBC2) - std::min(globalBC1, globalBC2); - fRegistry.fill(HIST("Pair/mix/hDiffBC"), diffBC); - if (diffBC < ndiff_bc_mix) { - continue; - } - - auto empair2 = std::get<1>(pair2); - auto arrM = std::array{static_cast(empair2.px()), static_cast(empair2.py()), static_cast(empair2.pz()), static_cast(empair2.mass())}; - - float cos_thetaPol = 999.f, phiPol = 999.f; - if (cfgPolarizationFrame == 0) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleCS(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } else if (cfgPolarizationFrame == 1) { - o2::aod::pwgem::dilepton::utils::pairutil::getAngleHX(arrM, arrD, beamE1, beamE2, beamP1, beamP2, cos_thetaPol, phiPol); - } - o2::math_utils::bringToPMPi(phiPol); - float quadmom = (3.f * std::pow(cos_thetaPol, 2) - 1.f) / 2.f; - if (cfgUseAbs) { - cos_thetaPol = std::fabs(cos_thetaPol); - phiPol = std::fabs(phiPol); - } - fRegistry.fill(HIST("Pair/mix/") + HIST(pair_sign_types[signType]) + HIST("hs"), empair1.mass(), empair1.pt(), empair1.getPairDCA(), empair1.rapidity(), cos_thetaPol, phiPol, quadmom, weight); - // fRegistry.fill(HIST("Pair/mix/") + HIST(pair_sign_types[signType]) + HIST("hBeta"), empair1.p() / empair1.e(), (empair2.p() / empair2.e() - empair1.p() / empair1.e()) / (empair1.p() / empair1.e())); - - } // end of pair2 loop - } // end of pair1 loop - } - - Filter collisionFilter_centrality = eventcuts.cfgCentMin < o2::aod::emthinevent::centrality && o2::aod::emthinevent::centrality < eventcuts.cfgCentMax; - Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; - Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; - using filteredCollisions = soa::Filtered; - - Filter dileptonFilter_track1 = (dileptoncuts.cfg_min_track_pt < o2::aod::emdilepton::pt1 && o2::aod::emdilepton::pt1 < dileptoncuts.cfg_max_track_pt) && (dileptoncuts.cfg_min_track_eta < o2::aod::emdilepton::eta1 && o2::aod::emdilepton::eta1 < dileptoncuts.cfg_max_track_eta); - Filter dileptonFilter_track2 = (dileptoncuts.cfg_min_track_pt < o2::aod::emdilepton::pt2 && o2::aod::emdilepton::pt2 < dileptoncuts.cfg_max_track_pt) && (dileptoncuts.cfg_min_track_eta < o2::aod::emdilepton::eta2 && o2::aod::emdilepton::eta2 < dileptoncuts.cfg_max_track_eta); - using filteredDileptons = soa::Filtered; - - SliceCache cache; - Preslice perCollision = aod::emdilepton::emthineventId; - Partition dileptonsULS = (o2::aod::emdilepton::sign1 > static_cast(0) && o2::aod::emdilepton::sign2 < static_cast(0)) || (o2::aod::emdilepton::sign1 < static_cast(0) && o2::aod::emdilepton::sign2 > static_cast(0)); - Partition dileptonsLSPP = o2::aod::emdilepton::sign1 > static_cast(0) && o2::aod::emdilepton::sign2 > static_cast(0); - Partition dileptonsLSMM = o2::aod::emdilepton::sign1 < static_cast(0) && o2::aod::emdilepton::sign2 < static_cast(0); - - std::map, std::vector>> mapMixingULS; // -> vector of for ULS pairs - std::map, std::vector>> mapMixingLSPP; // -> vector of for LSPP pairs - std::map, std::vector>> mapMixingLSMM; // -> vector of for LSMM pairs - - std::unordered_map map_mixed_eventId_to_globalBC; - - template - void runPairing(TCollisions const& collisions, TDileptons const&) - { - for (const auto& collision : collisions) { - initCCDB(collision); - float centrality = collision.centrality(); - if (centrality < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centrality) { - continue; - } - - float ep2 = collision.ep2(); - fRegistry.fill(HIST("Event/after/hZvtx"), collision.posZ()); - fRegistry.fill(HIST("Event/after/hCollisionCounter"), 9); - fRegistry.fill(HIST("Event/after/hCorrOccupancy"), collision.ft0cOccupancyInTimeRange(), collision.trackOccupancyInTimeRange()); - fRegistry.fill(HIST("Event/after/hEP2_CentFT0C_forMix"), centrality, ep2); - - // event mixing - int zbin = lower_bound(zvtx_bin_edges.begin(), zvtx_bin_edges.end(), collision.posZ()) - zvtx_bin_edges.begin() - 1; - if (zbin < 0) { - zbin = 0; - } else if (static_cast(zvtx_bin_edges.size()) - 2 < zbin) { - zbin = static_cast(zvtx_bin_edges.size()) - 2; - } - - int centbin = lower_bound(cent_bin_edges.begin(), cent_bin_edges.end(), centrality) - cent_bin_edges.begin() - 1; - if (centbin < 0) { - centbin = 0; - } else if (static_cast(cent_bin_edges.size()) - 2 < centbin) { - centbin = static_cast(cent_bin_edges.size()) - 2; - } - - int epbin = lower_bound(ep_bin_edges.begin(), ep_bin_edges.end(), ep2) - ep_bin_edges.begin() - 1; - if (epbin < 0) { - epbin = 0; - } else if (static_cast(ep_bin_edges.size()) - 2 < epbin) { - epbin = static_cast(ep_bin_edges.size()) - 2; - } - - int occbin = -1; - if (cfgOccupancyEstimator == 0) { - occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; - } else if (cfgOccupancyEstimator == 1) { - occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.trackOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; - } else { - occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; - } - - if (occbin < 0) { - occbin = 0; - } else if (static_cast(occ_bin_edges.size()) - 2 < occbin) { - occbin = static_cast(occ_bin_edges.size()) - 2; - } - - auto mixingBin = std::make_tuple(zbin, centbin, epbin, occbin); - - auto dileptons_uls_per_coll = dileptonsULS->sliceByCached(aod::emdilepton::emthineventId, collision.globalIndex(), cache); - auto dileptons_lspp_per_coll = dileptonsLSPP->sliceByCached(aod::emdilepton::emthineventId, collision.globalIndex(), cache); - auto dileptons_lsmm_per_coll = dileptonsLSMM->sliceByCached(aod::emdilepton::emthineventId, collision.globalIndex(), cache); - // LOGF(info, "collision.globalIndex() = %d, dileptons_uls_per_coll.size() = %d, dileptons_lspp_per_coll.size() = %d, dileptons_lsmm_per_coll.size() = %d", collision.globalIndex(), dileptons_uls_per_coll.size(), dileptons_lspp_per_coll.size(), dileptons_lsmm_per_coll.size()); - - int nuls = 0, nlspp = 0, nlsmm = 0; - if (cfgDoULS) { - for (const auto& dilepton : dileptons_uls_per_coll) { // ULS - bool is_pair_ok = fillPairInfo(collision, dilepton, mixingBin); - if (is_pair_ok) { - nuls++; - } - } - } - - if (cfgDoLS) { - for (const auto& dilepton : dileptons_lspp_per_coll) { // LS++ - bool is_pair_ok = fillPairInfo(collision, dilepton, mixingBin); - if (is_pair_ok) { - nlspp++; - } - } - for (const auto& dilepton : dileptons_lsmm_per_coll) { // LS-- - bool is_pair_ok = fillPairInfo(collision, dilepton, mixingBin); - if (is_pair_ok) { - nlsmm++; - } - } - } - - if (!cfgDoMix || !(nuls > 0 || nlspp > 0 || nlsmm > 0)) { - continue; - } - - if (nuls > 0 || nlspp > 0 || nlsmm > 0) { - map_mixed_eventId_to_globalBC[collision.globalIndex()] = collision.globalBC(); - } // end of if pair exist - - } // end of collision loop - - for (int iz = 0; iz < static_cast(zvtx_bin_edges.size()) - 1; iz++) { - for (int icent = 0; icent < static_cast(cent_bin_edges.size()) - 1; icent++) { - for (int iep = 0; iep < static_cast(ep_bin_edges.size()) - 1; iep++) { - for (int iocc = 0; iocc < static_cast(occ_bin_edges.size()) - 1; iocc++) { - auto key_bin = std::make_tuple(iz, icent, iep, iocc); - - auto pairsULS = mapMixingULS[key_bin]; - auto pairsLSPP = mapMixingLSPP[key_bin]; - auto pairsLSMM = mapMixingLSMM[key_bin]; - LOGF(info, "iz = %d, icent = %d, iep = %d, iocc = %d, pairsULS.size() = %d, pairsLSPP.size() = %d, pairsLSMM.size() = %d", iz, icent, iep, iocc, pairsULS.size(), pairsLSPP.size(), pairsLSMM.size()); - - if (cfgDoULS) { - fillMixedPairInfo<0>(pairsULS); - } - if (cfgDoLS) { - fillMixedPairInfo<1>(pairsLSPP); - fillMixedPairInfo<2>(pairsLSMM); - } - - } // end of iocc loop - } // end of iep loop - } // end of icent loop - } // end of iz loop - - mapMixingULS.clear(); - mapMixingLSPP.clear(); - mapMixingLSMM.clear(); - map_mixed_eventId_to_globalBC.clear(); - } // end of DF - - void processAnalysis(filteredCollisions const& collisions, filteredDileptons const& dileptons) - { - runPairing(collisions, dileptons); - } - PROCESS_SWITCH(DileptonPolarization, processAnalysis, "run dilepton analysis", true); - - void processDummy(aod::EMThinEvents const&) {} - PROCESS_SWITCH(DileptonPolarization, processDummy, "Dummy function", false); -}; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"dilepton-polarization"})}; -} diff --git a/PWGEM/Dilepton/Tasks/dimuonV1.cxx b/PWGEM/Dilepton/Tasks/dimuonV1.cxx new file mode 100644 index 00000000000..b069475d676 --- /dev/null +++ b/PWGEM/Dilepton/Tasks/dimuonV1.cxx @@ -0,0 +1,553 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code runs loop over leptons. +// Please write to: daiki.sekihata@cern.ch + +#include "PWGEM/Dilepton/Core/DimuonCut.h" +#include "PWGEM/Dilepton/Core/EMEventCut.h" +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/Dilepton/Utils/EventHistograms.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dimuonV1 { + + // // Configurables + o2::framework::Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + // o2::framework::Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; + // o2::framework::Configurable cfgDoMix{"cfgDoMix", false, "flag for event mixing"}; + // o2::framework::Configurable ndepth{"ndepth", 1000, "depth for event mixing"}; + // o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; + // o2::framework::ConfigurableAxis ConfVtxBins{"ConfVtxBins", {o2::framework::VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + // o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; + // o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {16, -M_PI / 2, +M_PI / 2}, "Mixing bins - event plane angle"}; + // o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + + o2::framework::Configurable cfgApplyWeightTTCA{"cfgApplyWeightTTCA", false, "flag to apply weighting by 1/N"}; + + o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.3, 1.4, 1.5, 2.00, 2.01, 2.02, 2.03, 2.04, 2.05, 2.06, 2.07, 2.08, 2.09, 2.1, 2.11, 2.12, 2.13, 2.14, 2.15, 2.16, 2.17, 2.18, 2.19, 2.2, 2.21, 2.22, 2.23, 2.24, 2.25, 2.26, 2.27, 2.28, 2.29, 2.3, 2.31, 2.32, 2.33, 2.34, 2.35, 2.36, 2.37, 2.38, 2.39, 2.4, 2.41, 2.42, 2.43, 2.44, 2.45, 2.46, 2.47, 2.48, 2.49, 2.5, 2.51, 2.52, 2.53, 2.54, 2.55, 2.56, 2.57, 2.58, 2.59, 2.6, 2.61, 2.62, 2.63, 2.64, 2.65, 2.66, 2.67, 2.68, 2.69, 2.7, 2.71, 2.72, 2.73, 2.74, 2.75, 2.76, 2.77, 2.78, 2.79, 2.8, 2.81, 2.82, 2.83, 2.84, 2.85, 2.86, 2.87, 2.88, 2.89, 2.9, 2.91, 2.92, 2.93, 2.94, 2.95, 2.96, 2.97, 2.98, 2.99, 3., 3.01, 3.02, 3.03, 3.04, 3.05, 3.06, 3.07, 3.08, 3.09, 3.1, 3.11, 3.12, 3.13, 3.14, 3.15, 3.16, 3.17, 3.18, 3.19, 3.2, 3.21, 3.22, 3.23, 3.24, 3.25, 3.26, 3.27, 3.28, 3.29, 3.3, 3.31, 3.32, 3.33, 3.34, 3.35, 3.36, 3.37, 3.38, 3.39, 3.4, 3.41, 3.42, 3.43, 3.44, 3.45, 3.46, 3.47, 3.48, 3.49, 3.5, 3.51, 3.52, 3.53, 3.54, 3.55, 3.56, 3.57, 3.58, 3.59, 3.6, 3.61, 3.62, 3.63, 3.64, 3.65, 3.66, 3.67, 3.68, 3.69, 3.7, 3.71, 3.72, 3.73, 3.74, 3.75, 3.76, 3.77, 3.78, 3.79, 3.8, 3.81, 3.82, 3.83, 3.84, 3.85, 3.86, 3.87, 3.88, 3.89, 3.9, 3.91, 3.92, 3.93, 3.94, 3.95, 3.96, 3.97, 3.98, 3.99, 4.0, 6.00, 6.10, 6.20, 6.30, 6.40, 6.50, 6.60, 6.70, 6.80, 6.90, 7.00, 7.10, 7.20, 7.30, 7.40, 7.50, 7.60, 7.70, 7.80, 7.90, 8.00, 8.10, 8.20, 8.30, 8.40, 8.50, 8.60, 8.70, 8.80, 8.90, 9.00, 9.10, 9.20, 9.30, 9.40, 9.50, 9.60, 9.70, 9.80, 9.90, 10.00, 10.10, 10.20, 10.30, 10.40, 10.50, 10.60, 10.70, 10.80, 10.90, 11.00, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0}, "mmumu bins for output histograms"}; + + o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {10, 0, 10}, "pTll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfYllBins{"ConfYllBins", {30, -5, -2}, "yll or etall bins for output histograms"}; + o2::framework::ConfigurableAxis ConfUQBins{"ConfUQBins", {400, -1, 1}, "uQ bins for output histograms"}; + o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {20, 10, 30}, "centrality bins for output histograms"}; + o2::framework::Configurable cfgNrotation{"cfgNrotation", 1, "number of rotation bkg"}; + o2::framework::Configurable cfgRandomSeed{"cfgRandomSeed", 1, "randam seed for rotation bkg"}; + o2::framework::Configurable cfgRotationMin{"cfgRotationMin", -M_PI / 4, "min. rotation angle for rotation bkg"}; + o2::framework::Configurable cfgRotationMax{"cfgRotationMax", +M_PI / 4, "max. rotation angle for rotation bkg"}; + o2::framework::Configurable cfgUseRapidity{"cfgUseRapidity", true, "flag to use rapidity. if false, pseudorapidity"}; + o2::framework::Configurable cfgUsePDGMass{"cfgUsePDGMass", true, "flag to use pdg mass of Jpsi"}; + o2::framework::Configurable cfgPDGMass{"cfgPDGMass", o2::constants::physics::MassJPsi, "pdg mass for quarkonia"}; + + EMEventCut fEMEventCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "eventcut_group"; + o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; + o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; + o2::framework::Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 in event cut"}; + o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border in event cut"}; + o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", true, "require no ITS readout frame border in event cut"}; + o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", true, "require no same bunch pileup in event cut"}; + o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. + o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", true, "require good Zvtx between FT0 vs. PV in event cut"}; + o2::framework::Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. occupancy"}; + o2::framework::Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. occupancy"}; + o2::framework::Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. FT0C occupancy"}; + o2::framework::Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + + // o2::framework::Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; + // o2::framework::Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "require no collision in time range strict"}; + // o2::framework::Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "require no collision in time range standard"}; + // o2::framework::Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "require no collision in time range strict"}; + // o2::framework::Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "require no HM collision in previous ITS ROF"}; + // o2::framework::Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "number of inactive chips on ITS layer 3 are below threshold "}; + // o2::framework::Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "number of inactive chips on ITS layers 0-3 are below threshold "}; + // o2::framework::Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "number of inactive chips on all ITS layers are below threshold "}; + // for RCT + o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", true, "require good detector flag in run condtion table"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_muon", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", true, "set ZDC flag for PbPb"}; + o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + + o2::framework::Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; + o2::framework::Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; + o2::framework::Configurable cfgNumContribMin{"cfgNumContribMin", 0, "min. numContrib"}; + o2::framework::Configurable cfgNumContribMax{"cfgNumContribMax", 65000, "max. numContrib"}; + } eventcuts; + + DimuonCut fDimuonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "dimuoncut_group"; + o2::framework::Configurable cfg_min_mass{"cfg_min_mass", 0.0, "min mass"}; + o2::framework::Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass"}; + o2::framework::Configurable cfg_min_pair_pt{"cfg_min_pair_pt", 0.0, "min pair pt"}; + o2::framework::Configurable cfg_max_pair_pt{"cfg_max_pair_pt", 1e+10, "max pair pt"}; + o2::framework::Configurable cfg_min_pair_y{"cfg_min_pair_y", -4.0, "min pair rapidity"}; + o2::framework::Configurable cfg_max_pair_y{"cfg_max_pair_y", -2.5, "max pair rapidity"}; + o2::framework::Configurable cfg_min_pair_dcaxy{"cfg_min_pair_dcaxy", 0.0, "min pair dca3d in sigma"}; + o2::framework::Configurable cfg_max_pair_dcaxy{"cfg_max_pair_dcaxy", 1e+10, "max pair dca3d in sigma"}; + o2::framework::Configurable cfg_apply_detadphi{"cfg_apply_detadphi", false, "flag to apply deta-dphi elliptic cut"}; + o2::framework::Configurable cfg_min_deta{"cfg_min_deta", 0.02, "min deta between 2 muons (elliptic cut)"}; + o2::framework::Configurable cfg_min_dphi{"cfg_min_dphi", 0.02, "min dphi between 2 muons (elliptic cut)"}; + + // o2::framework::Configurable cfg_track_type{"cfg_track_type", 3, "muon track type [0: MFT-MCH-MID, 3: MCH-MID]"}; + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.8, "min pT for single track"}; + o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; + o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -4.0, "min eta for single track"}; + o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", -2.5, "max eta for single track"}; + o2::framework::Configurable cfg_min_phi_track{"cfg_min_phi_track", 0.f, "min phi for single track"}; + o2::framework::Configurable cfg_max_phi_track{"cfg_max_phi_track", 6.3, "max phi for single track"}; + o2::framework::Configurable cfg_min_ncluster_mft{"cfg_min_ncluster_mft", 5, "min ncluster MFT"}; + o2::framework::Configurable cfg_min_ncluster_mch{"cfg_min_ncluster_mch", 5, "min ncluster MCH"}; + o2::framework::Configurable cfg_max_chi2{"cfg_max_chi2", 1e+6, "max chi2/ndf"}; + o2::framework::Configurable cfg_max_chi2mft{"cfg_max_chi2mft", 1e+6, "max chi2/ndf"}; + o2::framework::Configurable cfg_border_pt_for_chi2mchmft{"cfg_border_pt_for_chi2mchmft", 0, "border pt for different max chi2 for MFT-MCH matching"}; + o2::framework::Configurable cfg_max_matching_chi2_mftmch_lowPt{"cfg_max_matching_chi2_mftmch_lowPt", 8, "max chi2 for MFT-MCH matching for low pT"}; + o2::framework::Configurable cfg_max_matching_chi2_mftmch_highPt{"cfg_max_matching_chi2_mftmch_highPt", 40, "max chi2 for MFT-MCH matching for high pT"}; + o2::framework::Configurable cfg_max_matching_chi2_mchmid{"cfg_max_matching_chi2_mchmid", 1e+10, "max chi2 for MCH-MID matching"}; + o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1e+10, "max dca XY for single track in cm"}; + o2::framework::Configurable cfg_min_rabs{"cfg_min_rabs", 17.6, "min Radius at the absorber end"}; + o2::framework::Configurable cfg_max_rabs{"cfg_max_rabs", 89.5, "max Radius at the absorber end"}; + o2::framework::Configurable cfg_max_diff_chi2_mftmch{"cfg_max_diff_chi2_mftmch", -1.f, "max. diff chi2MatchingMCHMFT between the best and the 2nd best matched candidates"}; + o2::framework::Configurable enableTTCA{"enableTTCA", false, "Flag to enable or disable TTCA"}; + o2::framework::Configurable cfg_max_relDPt_wrt_matchedMCHMID{"cfg_max_relDPt_wrt_matchedMCHMID", 1e+10f, "max. relative dpt between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable cfg_max_DEta_wrt_matchedMCHMID{"cfg_max_DEta_wrt_matchedMCHMID", 1e+10f, "max. deta between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable cfg_max_DPhi_wrt_matchedMCHMID{"cfg_max_DPhi_wrt_matchedMCHMID", 1e+10f, "max. dphi between MFT-MCH-MID and MCH-MID"}; + o2::framework::Configurable requireMFTHitMap{"requireMFTHitMap", false, "flag to apply MFT hit map"}; + o2::framework::Configurable> requiredMFTDisks{"requiredMFTDisks", std::vector{0}, "hit map on MFT disks [0,1,2,3,4]. logical-OR of each double-sided disk"}; + } dimuoncuts; + + o2::aod::rctsel::RCTFlagsChecker rctChecker; + int mRunNumber{0}; + std::mt19937 engine; + std::uniform_real_distribution distDPhi; + + o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; + static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; + + void init(o2::framework::InitContext& /*context*/) + { + mRunNumber = 0; + + // std::random_device seed_gen; + engine = std::mt19937(cfgRandomSeed); + distDPhi = std::uniform_real_distribution(cfgRotationMin, cfgRotationMax); + + rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); + + // emh_pos = new MyEMH_muon(ndepth); + // emh_neg = new MyEMH_muon(ndepth); + + addhistograms(); + DefineEMEventCut(); + DefineDimuonCut(); + } + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + } + + void addhistograms() + { + // event info + o2::aod::pwgem::dilepton::utils::eventhistogram::addEventHistograms<-1>(&fRegistry); + + fRegistry.add("Event/before/ZDC/hQxt", "Q_{x}^{t} vs. centrality;centrality FT0C (%);Q_{x}^{t}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {200, -1, +1}}, false); + fRegistry.add("Event/before/ZDC/hQyt", "Q_{y}^{t} vs. centrality;centrality FT0C (%);Q_{y}^{t}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {200, -1, +1}}, false); + fRegistry.add("Event/before/ZDC/hQxp", "Q_{x}^{p} vs. centrality;centrality FT0C (%);Q_{x}^{p}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {200, -1, +1}}, false); + fRegistry.add("Event/before/ZDC/hQyp", "Q_{y}^{p} vs. centrality;centrality FT0C (%);Q_{y}^{p}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {200, -1, +1}}, false); + + fRegistry.add("Event/before/ZDC/hQxtQxp", "Q_{x}^{t} #upoint Q_{x}^{p} vs. centrality;centrality FT0C (%);Q_{x}^{t} #upoint Q_{x}^{p}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {1000, -1, +1}}, false); + fRegistry.add("Event/before/ZDC/hQytQyp", "Q_{y}^{t} #upoint Q_{y}^{p} vs. centrality;centrality FT0C (%);Q_{y}^{t} #upoint Q_{y}^{p}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {1000, -1, +1}}, false); + fRegistry.add("Event/before/ZDC/hQxtQyp", "Q_{x}^{t} #upoint Q_{y}^{p} vs. centrality;centrality FT0C (%);Q_{x}^{t} #upoint Q_{y}^{p}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {1000, -1, +1}}, false); + fRegistry.add("Event/before/ZDC/hQytQxp", "Q_{y}^{t} #upoint Q_{x}^{p} vs. centrality;centrality FT0C (%);Q_{y}^{t} #upoint Q_{x}^{p}", o2::framework::HistType::kTH2D, {{110, 0, 110}, {1000, -1, +1}}, false); + fRegistry.addClone("Event/before/ZDC/", "Event/after/ZDC/"); + + // pair info + const o2::framework::AxisSpec axis_mass{ConfMllBins, "m_{#mu#mu} (GeV/c^{2})"}; + const o2::framework::AxisSpec axis_pt{ConfPtllBins, "p_{T,#mu#mu} (GeV/c)"}; + const o2::framework::AxisSpec axis_y{ConfYllBins, "y_{#mu#mu}"}; + const o2::framework::AxisSpec axis_eta{ConfYllBins, "#eta_{#mu#mu}"}; + + // const o2::framework::AxisSpec axis_uxQxt{ConfUQBins, "u_{x}Q_{x}^{t}"}; + // const o2::framework::AxisSpec axis_uxQxp{ConfUQBins, "u_{x}Q_{x}^{p}"}; + // const o2::framework::AxisSpec axis_uyQyt{ConfUQBins, "u_{y}Q_{y}^{t}"}; + // const o2::framework::AxisSpec axis_uyQyp{ConfUQBins, "u_{y}Q_{y}^{p}"}; + + const o2::framework::AxisSpec axis_uxQx{ConfUQBins, "u_{x}Q_{x}^{p} - u_{x}Q_{x}^{t}"}; + const o2::framework::AxisSpec axis_uyQy{ConfUQBins, "u_{y}Q_{y}^{p} - u_{y}Q_{y}^{t}"}; + + const o2::framework::AxisSpec axis_centrality{ConfCentBins, "centrality (%)"}; + + fRegistry.add("Track/hs", "single muon;p_{T} (GeV/c);#eta;#varphi (rad.);sign;", o2::framework::HistType::kTHnSparseD, {{100, 0, 10}, {30, -5, -2}, {36, 0, 2 * M_PI}, {3, -1.5, +1.5}}, true); + + if (cfgUseRapidity) { + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y, axis_uxQx, axis_uyQy, axis_centrality}, true); + fRegistry.add("Pair/same/uls/hsRotBkg", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_y}, true); + } else { + fRegistry.add("Pair/same/uls/hs", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_eta, axis_uxQx, axis_uyQy, axis_centrality}, true); + fRegistry.add("Pair/same/uls/hsRotBkg", "dilepton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt, axis_eta}, true); + } + + // fRegistry.addClone("Pair/same/uls/", "Pair/same/lspp/"); + // fRegistry.addClone("Pair/same/uls/", "Pair/same/lsmm/"); + // fRegistry.addClone("Pair/same/", "Pair/mix/"); + } + + void DefineEMEventCut() + { + fEMEventCut = EMEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireVertexTOFmatched(eventcuts.cfgRequireVertexTOFmatched); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + // fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); + // fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); + // fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); + // fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); + // fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); + // fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); + // fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); + // fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + } + + void DefineDimuonCut() + { + fDimuonCut = DimuonCut("fDimuonCut", "fDimuonCut"); + + // for pair + fDimuonCut.SetMassRange(dimuoncuts.cfg_min_mass, dimuoncuts.cfg_max_mass); + fDimuonCut.SetPairPtRange(dimuoncuts.cfg_min_pair_pt, dimuoncuts.cfg_max_pair_pt); + fDimuonCut.SetPairYRange(dimuoncuts.cfg_min_pair_y, dimuoncuts.cfg_max_pair_y); + fDimuonCut.SetPairDCAxyRange(dimuoncuts.cfg_min_pair_dcaxy, dimuoncuts.cfg_max_pair_dcaxy); + fDimuonCut.SetMindEtadPhi(dimuoncuts.cfg_apply_detadphi, dimuoncuts.cfg_min_deta, dimuoncuts.cfg_min_dphi); + + // for track + // fDimuonCut.SetTrackType(dimuoncuts.cfg_track_type); + fDimuonCut.SetTrackType(3); + fDimuonCut.SetTrackPtRange(dimuoncuts.cfg_min_pt_track, dimuoncuts.cfg_max_pt_track); + fDimuonCut.SetTrackEtaRange(dimuoncuts.cfg_min_eta_track, dimuoncuts.cfg_max_eta_track); + fDimuonCut.SetTrackPhiRange(dimuoncuts.cfg_min_phi_track, dimuoncuts.cfg_max_phi_track); + fDimuonCut.SetNClustersMFT(dimuoncuts.cfg_min_ncluster_mft, 10); + fDimuonCut.SetNClustersMCHMID(dimuoncuts.cfg_min_ncluster_mch, 20); + fDimuonCut.SetChi2(0.f, dimuoncuts.cfg_max_chi2); + fDimuonCut.SetChi2MFT(0.f, dimuoncuts.cfg_max_chi2mft); + fDimuonCut.SetMaxDiffMatchingChi2MCHMFT(dimuoncuts.cfg_max_diff_chi2_mftmch); + fDimuonCut.SetMaxMatchingChi2MCHMFTPtDep([&](float pt) { return (pt < dimuoncuts.cfg_border_pt_for_chi2mchmft ? dimuoncuts.cfg_max_matching_chi2_mftmch_lowPt : dimuoncuts.cfg_max_matching_chi2_mftmch_highPt); }); + fDimuonCut.SetMatchingChi2MCHMID(0.f, dimuoncuts.cfg_max_matching_chi2_mchmid); + fDimuonCut.SetDCAxy(0.f, dimuoncuts.cfg_max_dcaxy); + fDimuonCut.SetRabs(dimuoncuts.cfg_min_rabs, dimuoncuts.cfg_max_rabs); + fDimuonCut.SetMaxPDCARabsDep([&](float rabs) { return (rabs < 26.5 ? 594.f : 324.f); }); + fDimuonCut.SetMaxdPtdEtadPhiwrtMCHMID(dimuoncuts.cfg_max_relDPt_wrt_matchedMCHMID, dimuoncuts.cfg_max_DEta_wrt_matchedMCHMID, dimuoncuts.cfg_max_DPhi_wrt_matchedMCHMID); // this is relevant for global muons + fDimuonCut.SetMFTHitMap(dimuoncuts.requireMFTHitMap, dimuoncuts.requiredMFTDisks); + fDimuonCut.EnableTTCA(dimuoncuts.enableTTCA); + } + + template + bool fillPairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TCut const& cut, const float centrality) + { + if (!cut.template IsSelectedTrack(t1) || !cut.template IsSelectedTrack(t2)) { + return false; + } + + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + + float weight = 1.f; + if constexpr (ev_id == 0) { + if (cfgApplyWeightTTCA) { + weight = map_weight[std::make_pair(t1.globalIndex(), t2.globalIndex())]; + } + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), RecoDecay::constrainAngle(t1.phi(), 0, 1U), o2::constants::physics::MassMuon); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), RecoDecay::constrainAngle(t2.phi(), 0, 1U), o2::constants::physics::MassMuon); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + ROOT::Math::PtEtaPhiMVector v12pdg(v12.Pt(), v12.Eta(), v12.Phi(), cfgUsePDGMass ? cfgPDGMass : v12.M()); + float phi = RecoDecay::constrainAngle(v12.Phi(), 0, 1U); + + float uxQxt = std::cos(1.f * phi) * collision.qxZDCC(); + float uxQxp = std::cos(1.f * phi) * collision.qxZDCA(); + float uyQyt = std::sin(1.f * phi) * collision.qyZDCC(); + float uyQyp = std::sin(1.f * phi) * collision.qyZDCA(); + + if (t1.sign() * t2.sign() < 0) { // ULS + if (cfgUseRapidity) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), v12pdg.Rapidity(), uxQxp - uxQxt, uyQyp - uyQyt, centrality, weight); + } else { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hs"), v12.M(), v12.Pt(), v12pdg.Eta(), uxQxp - uxQxt, uyQyp - uyQyt, centrality, weight); + } + + for (int i = 0; i < cfgNrotation; i++) { + float dphi = distDPhi(engine); + ROOT::Math::PtEtaPhiMVector v2rot(t2.pt(), t2.eta(), RecoDecay::constrainAngle(t2.phi() + M_PI + dphi, 0, 1U), o2::constants::physics::MassMuon); + ROOT::Math::PtEtaPhiMVector v12bkg = v1 + v2rot; + ROOT::Math::PtEtaPhiMVector v12bkgpdg(v12bkg.Pt(), v12bkg.Eta(), v12bkg.Phi(), cfgUsePDGMass ? cfgPDGMass : v12bkg.M()); + if (cfgUseRapidity) { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hsRotBkg"), v12bkg.M(), v12bkg.Pt(), v12bkgpdg.Rapidity(), weight * 1.f / static_cast(cfgNrotation)); + } else { + fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("uls/hsRotBkg"), v12bkg.M(), v12bkg.Pt(), v12bkgpdg.Eta(), weight * 1.f / static_cast(cfgNrotation)); + } + } + + // } else if (t1.sign() > 0 && t2.sign() > 0) { // LS++ + // fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lspp/hs"), v12.M(), v12.Pt(), v12.Eta(), uxQxt, uxQxp, uyQyt, uyQyp, weight); + // } else if (t1.sign() < 0 && t2.sign() < 0) { // LS-- + // fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("lsmm/hs"), v12.M(), v12.Pt(), v12.Eta(), uxQxt, uxQxp, uyQyt, uyQyp, weight); + } + return true; + } + + template + void runPairing(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut) + { + for (const auto& collision : collisions) { + initCCDB(collision); + + float centrality = std::array{collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}[cfgCentEstimator]; + if (centrality < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centrality) { + continue; + } + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<0, -1>(&fRegistry, collision); + + float QxtQxp = collision.qxZDCA() * collision.qxZDCC(); + float QytQyp = collision.qyZDCA() * collision.qyZDCC(); + float QytQxp = collision.qxZDCA() * collision.qyZDCC(); + float QxtQyp = collision.qxZDCC() * collision.qyZDCA(); + + fRegistry.fill(HIST("Event/before/ZDC/hQxt"), centrality, collision.qxZDCC()); + fRegistry.fill(HIST("Event/before/ZDC/hQyt"), centrality, collision.qyZDCC()); + fRegistry.fill(HIST("Event/before/ZDC/hQxp"), centrality, collision.qxZDCA()); + fRegistry.fill(HIST("Event/before/ZDC/hQyp"), centrality, collision.qyZDCA()); + + fRegistry.fill(HIST("Event/before/ZDC/hQxtQxp"), centrality, QxtQxp); + fRegistry.fill(HIST("Event/before/ZDC/hQytQyp"), centrality, QytQyp); + fRegistry.fill(HIST("Event/before/ZDC/hQxtQyp"), centrality, QxtQyp); + fRegistry.fill(HIST("Event/before/ZDC/hQytQxp"), centrality, QytQxp); + + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + + o2::aod::pwgem::dilepton::utils::eventhistogram::fillEventInfo<1, -1>(&fRegistry, collision); + + fRegistry.fill(HIST("Event/before/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + fRegistry.fill(HIST("Event/after/hCollisionCounter"), o2::aod::pwgem::dilepton::utils::eventhistogram::nbin_ev); // accepted + + fRegistry.fill(HIST("Event/after/ZDC/hQxt"), centrality, collision.qxZDCC()); + fRegistry.fill(HIST("Event/after/ZDC/hQyt"), centrality, collision.qyZDCC()); + fRegistry.fill(HIST("Event/after/ZDC/hQxp"), centrality, collision.qxZDCA()); + fRegistry.fill(HIST("Event/after/ZDC/hQyp"), centrality, collision.qyZDCA()); + + fRegistry.fill(HIST("Event/after/ZDC/hQxtQxp"), centrality, QxtQxp); + fRegistry.fill(HIST("Event/after/ZDC/hQytQyp"), centrality, QytQyp); + fRegistry.fill(HIST("Event/after/ZDC/hQxtQyp"), centrality, QxtQyp); + fRegistry.fill(HIST("Event/after/ZDC/hQytQxp"), centrality, QytQxp); + + auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + + for (const auto& track : posTracks_per_coll) { + if (cut.IsSelectedTrack(track)) { + fRegistry.fill(HIST("Track/hs"), track.pt(), track.eta(), track.phi(), track.sign()); + } + } + for (const auto& track : negTracks_per_coll) { + if (cut.IsSelectedTrack(track)) { + fRegistry.fill(HIST("Track/hs"), track.pt(), track.eta(), track.phi(), track.sign()); + } + } + + for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS + fillPairInfo<0>(collision, pos, neg, cut, centrality); + } + + // for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ + // fillPairInfo<0>(collision, pos1, pos2, cut, centrality); + // } + // for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- + // fillPairInfo<0>(collision, neg1, neg2, cut, centrality); + // } + + } // end of collision loop + } // end of DF + + template + bool isPairOK(TTrack1 const& t1, TTrack2 const& t2, TCut const& cut) + { + if (!cut.IsSelectedTrack(t1) || !cut.IsSelectedTrack(t2)) { + return false; + } + + if (!cut.IsSelectedPair(t1, t2)) { + return false; + } + return true; + } + + std::map, float> map_weight; // -> float + template + void fillPairWeightMap(TCollisions const& collisions, TLeptons const& posTracks, TLeptons const& negTracks, TPresilce const& perCollision, TCut const& cut, TAllTracks const& tracks) + { + std::vector> passed_pairIds; + passed_pairIds.reserve(posTracks.size() * negTracks.size()); + + for (const auto& collision : collisions) { + initCCDB(collision); + float centrality = std::array{collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}[cfgCentEstimator]; + if (centrality < eventcuts.cfgCentMin || eventcuts.cfgCentMax < centrality) { + continue; + } + + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + if (eventcuts.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + + auto posTracks_per_coll = posTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + auto negTracks_per_coll = negTracks.sliceByCached(perCollision, collision.globalIndex(), cache); + + for (const auto& [pos, neg] : combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_per_coll, negTracks_per_coll))) { // ULS + if (isPairOK(pos, neg, cut)) { + passed_pairIds.emplace_back(std::make_pair(pos.globalIndex(), neg.globalIndex())); + } + } + for (const auto& [pos1, pos2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posTracks_per_coll, posTracks_per_coll))) { // LS++ + if (isPairOK(pos1, pos2, cut)) { + passed_pairIds.emplace_back(std::make_pair(pos1.globalIndex(), pos2.globalIndex())); + } + } + for (const auto& [neg1, neg2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negTracks_per_coll, negTracks_per_coll))) { // LS-- + if (isPairOK(neg1, neg2, cut)) { + passed_pairIds.emplace_back(std::make_pair(neg1.globalIndex(), neg2.globalIndex())); + } + } + } // end of collision loop + + for (const auto& pairId : passed_pairIds) { + auto t1 = tracks.rawIteratorAt(std::get<0>(pairId)); + auto t2 = tracks.rawIteratorAt(std::get<1>(pairId)); + + float n = 1.f; // include myself. + for (const auto& ambId1 : t1.ambiguousMuonsIds()) { + for (const auto& ambId2 : t2.ambiguousMuonsIds()) { + if (std::find(passed_pairIds.begin(), passed_pairIds.end(), std::make_pair(ambId1, ambId2)) != passed_pairIds.end()) { + n += 1.f; + } + } + } + map_weight[pairId] = 1.f / n; + } // end of passed_pairIds loop + passed_pairIds.clear(); + passed_pairIds.shrink_to_fit(); + } + + using MyCollisions = o2::soa::Join; + using MyCollision = MyCollisions::iterator; + + using MyMuons = o2::soa::Join; + using MyMuon = MyMuons::iterator; + + // using MyEMH_muon = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::dilepton::utils::EMFwdTrack>; + + o2::framework::expressions::Filter collisionFilter_centrality = (eventcuts.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventcuts.cfgCentMax) || (eventcuts.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventcuts.cfgCentMax); + o2::framework::expressions::Filter collisionFilter_numContrib = eventcuts.cfgNumContribMin <= o2::aod::collision::numContrib && o2::aod::collision::numContrib < eventcuts.cfgNumContribMax; + o2::framework::expressions::Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; + o2::framework::expressions::Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; + using FilteredMyCollisions = o2::soa::Filtered; + using FilteredMyCollision = FilteredMyCollisions::iterator; + + o2::framework::SliceCache cache; + o2::framework::Preslice perCollision_muon = o2::aod::emprimarymuon::emeventId; + + o2::framework::expressions::Filter trackFilter_muon = o2::aod::fwdtrack::trackType == static_cast(o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) && dimuoncuts.cfg_min_pt_track < o2::aod::fwdtrack::pt && o2::aod::fwdtrack::pt < dimuoncuts.cfg_max_pt_track && dimuoncuts.cfg_min_eta_track < o2::aod::fwdtrack::eta && o2::aod::fwdtrack::eta < dimuoncuts.cfg_max_eta_track; + o2::framework::expressions::Filter ttcaFilter_muon = ifnode(dimuoncuts.enableTTCA.node(), true, o2::aod::emprimarymuon::isAssociatedToMPC == true); + using FilteredMyMuons = o2::soa::Filtered; + using FilteredMyMuon = FilteredMyMuons::iterator; + + o2::framework::Partition positive_muons = o2::aod::emprimarymuon::sign > int8_t(0); + o2::framework::Partition negative_muons = o2::aod::emprimarymuon::sign < int8_t(0); + + // MyEMH_muon* emh_pos = nullptr; + // MyEMH_muon* emh_neg = nullptr; + + void processAnalysis(FilteredMyCollisions const& collisions, FilteredMyMuons const& muons) + { + if (cfgApplyWeightTTCA) { + fillPairWeightMap(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut, muons); + } + runPairing(collisions, positive_muons, negative_muons, o2::aod::emprimarymuon::emeventId, fDimuonCut); + } + PROCESS_SWITCH(dimuonV1, processAnalysis, "run dimuon v1 analysis", true); + + void processDummy(MyCollisions const&) {} + PROCESS_SWITCH(dimuonV1, processDummy, "Dummy function", false); +}; +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + return o2::framework::WorkflowSpec{adaptAnalysisTask(cfgc, o2::framework::TaskName{"dimuon-v1"})}; +} diff --git a/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx b/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx index d223c5d2c1c..a739ff9ca50 100644 --- a/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx +++ b/PWGEM/Dilepton/Tasks/emEfficiencyEE.cxx @@ -118,7 +118,7 @@ constexpr static uint32_t gkMCEventFillMap = VarManager::ObjTypes::ReducedEventM constexpr static uint32_t gkTrackFillMap = VarManager::ObjTypes::ReducedTrack | VarManager::ObjTypes::ReducedTrackBarrel | VarManager::ObjTypes::ReducedTrackBarrelCov | VarManager::ObjTypes::ReducedTrackBarrelPID; constexpr static uint32_t gkParticleMCFillMap = VarManager::ObjTypes::ParticleMC; -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar); // defines histograms for all tasks void SetBinsLinear(std::vector& fBins, const double min, const double max, const unsigned int steps); struct AnalysisEventSelection { @@ -324,7 +324,7 @@ struct AnalysisEventQa { { Int_t midrap = 0; - for (auto mctrack : groupedMCTracks) { + for (const auto& mctrack : groupedMCTracks) { if (TMath::Abs(mctrack.eta()) < 0.5 && mctrack.isPhysicalPrimary() && (TMath::Abs(mctrack.pdgCode()) == 211 || mctrack.pdgCode() == 111)) { midrap++; } @@ -779,7 +779,7 @@ struct AnalysisTrackSelection { } template - void runDataFillMore(TEvents const& events, TEventsMC eventsMC, TTracks const& tracks, TTracksMC const& tracksMC, TAmbigTracks const& ambiTracksMid) + void runDataFillMore(TEvents const& events, const TEventsMC& eventsMC, TTracks const& tracks, TTracksMC const& tracksMC, TAmbigTracks const& ambiTracksMid) { VarManager::ResetValues(0, VarManager::kNEventWiseVariables); @@ -1043,8 +1043,7 @@ struct AnalysisTrackSelection { template void runRecTrackMore(TEvents const& events, TEventsMC const& /*eventsMC*/, TTracks const& groupedTracks, TTracksMC const& tracksMC, TAmbigTracks const& ambiTracksMid) { - - std::map fRecTrackLabels[fTrackCuts.size() + 1]; + std::vector> fRecTrackLabels(fTrackCuts.size() + 1); uint32_t filterMap = 0; trackSel.reserve(groupedTracks.size()); @@ -1052,10 +1051,7 @@ struct AnalysisTrackSelection { for (auto& track : groupedTracks) { // How many time the associated MC track was seen for this cut - Int_t fRecCounters[fTrackCuts.size() + 1]; - for (unsigned int k = 0; k < fTrackCuts.size() + 1; k++) { - fRecCounters[k] = 0; - } + std::vector fRecCounters(fTrackCuts.size() + 1, 0); filterMap = 0; Int_t ambiguousinfo = 0; @@ -1157,10 +1153,7 @@ struct AnalysisTrackSelection { uint32_t mcDecision = 0; int isig = 0; Int_t mctrackindex = -999; - Int_t doublereconstructedtrack[fTrackCuts.size() + 1]; - for (unsigned int k = 0; k < fTrackCuts.size() + 1; k++) { - doublereconstructedtrack[k] = 0; - } + std::vector doublereconstructedtrack(fTrackCuts.size() + 1, 0); for (auto sig = fMCSignals.begin(); sig != fMCSignals.end(); sig++, isig++) { if constexpr ((TTrackFillMap & VarManager::ObjTypes::Track) > 0) { if (track.has_mcParticle()) { @@ -2012,7 +2005,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGEM/Dilepton/Tasks/lmeeLFCocktail.cxx b/PWGEM/Dilepton/Tasks/lmeeLFCocktail.cxx index 56bd936d9f3..b77c15dc37c 100644 --- a/PWGEM/Dilepton/Tasks/lmeeLFCocktail.cxx +++ b/PWGEM/Dilepton/Tasks/lmeeLFCocktail.cxx @@ -127,7 +127,7 @@ struct lmeelfcocktail { return true; } - bool isAcceptedSingle(ROOT::Math::PtEtaPhiMVector p1) + bool isAcceptedSingle(const ROOT::Math::PtEtaPhiMVector& p1) { if (p1.Pt() < fConfigMinPt) return false; @@ -138,7 +138,7 @@ struct lmeelfcocktail { return true; } - bool isAcceptedPair(ROOT::Math::PtEtaPhiMVector p1, ROOT::Math::PtEtaPhiMVector p2) + bool isAcceptedPair(const ROOT::Math::PtEtaPhiMVector& p1, const ROOT::Math::PtEtaPhiMVector& p2) { if (!isAcceptedSingle(p1)) { return false; @@ -167,7 +167,7 @@ struct lmeelfcocktail { } template - bool isAcceptedPair(T& p1, T& p2) + bool isAcceptedPair(const T& p1, const T& p2) { ROOT::Math::PtEtaPhiMVector v1(p1.ptSmeared(), p1.etaSmeared(), p1.phiSmeared(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v2(p2.ptSmeared(), p2.etaSmeared(), p2.phiSmeared(), o2::constants::physics::MassElectron); @@ -175,13 +175,13 @@ struct lmeelfcocktail { } template - bool isAcceptedSingle(T& p1) + bool isAcceptedSingle(const T& p1) { ROOT::Math::PtEtaPhiMVector v1(p1.ptSmeared(), p1.etaSmeared(), p1.phiSmeared(), o2::constants::physics::MassElectron); return isAcceptedSingle(v1); } - void addHistogram1D_stage(TString histname, AxisSpec axis, int& i, TString s) + void addHistogram1D_stage(const TString& histname, const AxisSpec& axis, int& i, const TString& s) { i++; TString name = s + histname; @@ -201,14 +201,14 @@ struct lmeelfcocktail { } } - void addHistogram1D(TString histname, AxisSpec axis, int& i) + void addHistogram1D(const TString& histname, const AxisSpec& axis, int& i) { - for (auto s : stage) { + for (const auto& s : stage) { addHistogram1D_stage(histname, axis, i, s); } } - void addHistogram1D_mother(TString histname, AxisSpec axis, int& i) // mother histograms only for gen. level, no decay channels + void addHistogram1D_mother(const TString& histname, const AxisSpec& axis, int& i) // mother histograms only for gen. level, no decay channels { i++; TString name = stage[0] + histname; @@ -222,7 +222,7 @@ struct lmeelfcocktail { } } - void addHistogram2D_stage(TString histname, AxisSpec axis1, AxisSpec axis2, int& i, TString s) + void addHistogram2D_stage(const TString& histname, const AxisSpec& axis1, const AxisSpec& axis2, int& i, const TString& s) { i++; TString name = s + histname; @@ -242,15 +242,15 @@ struct lmeelfcocktail { } } - void addHistogram2D(TString histname, AxisSpec axis1, AxisSpec axis2, int& i) + void addHistogram2D(const TString& histname, const AxisSpec& axis1, const AxisSpec& axis2, int& i) { - for (auto s : stage) { + for (const auto& s : stage) { addHistogram2D_stage(histname, axis1, axis2, i, s); } } template - void addHistogramND_stage(TString histname, TAxes const& axes, int& i, TString s) + void addHistogramND_stage(const TString& histname, TAxes const& axes, int& i, const TString& s) { i++; TString name = s + histname; @@ -271,34 +271,34 @@ struct lmeelfcocktail { } template - void addHistogramND(TString histname, TAxes const& axes, int& i) + void addHistogramND(const TString& histname, TAxes const& axes, int& i) { - for (auto s : stage) { + for (const auto& s : stage) { addHistogramND_stage(histname, axes, i, s); } } - void fillHistogram1D(TString histname, int s, int pdg, int other_daughter_pdg, float value, float weight) + void fillHistogram1D(const TString& histname, int s, int pdg, int other_daughter_pdg, float value, float weight) { histograms1D[histogramId[stage[s] + histname]]->Fill(value, weight); histograms1D[histogramId[stage[s] + mesons[pdg].name + histname]]->Fill(value, weight); histograms1D[histogramId[stage[s] + mesons[pdg].name + decays[other_daughter_pdg] + histname]]->Fill(value, weight); } - void fillHistogram1D_mother(TString histname, int pdg, float value, float weight) + void fillHistogram1D_mother(const TString& histname, int pdg, float value, float weight) { histograms1D[histogramId[stage[0] + histname]]->Fill(value, weight); histograms1D[histogramId[stage[0] + mesons[pdg].name + histname]]->Fill(value, weight); } - void fillHistogram2D(TString histname, int s, int pdg, int other_daughter_pdg, float value1, float value2, float weight) + void fillHistogram2D(const TString& histname, int s, int pdg, int other_daughter_pdg, float value1, float value2, float weight) { histograms2D[histogramId[stage[s] + histname]]->Fill(value1, value2, weight); histograms2D[histogramId[stage[s] + mesons[pdg].name + histname]]->Fill(value1, value2, weight); histograms2D[histogramId[stage[s] + mesons[pdg].name + decays[other_daughter_pdg] + histname]]->Fill(value1, value2, weight); } - void fillHistogramND(TString histname, int s, int pdg, int other_daughter_pdg, double* values, double weight) + void fillHistogramND(const TString& histname, int s, int pdg, int other_daughter_pdg, double* values, double weight) { histogramsND[histogramId[stage[s] + histname]]->Fill(values, weight); histogramsND[histogramId[stage[s] + mesons[pdg].name + histname]]->Fill(values, weight); diff --git a/PWGEM/Dilepton/Tasks/matchingMFT.cxx b/PWGEM/Dilepton/Tasks/matchingMFT.cxx index d962d51b94d..6d95d3abaa2 100644 --- a/PWGEM/Dilepton/Tasks/matchingMFT.cxx +++ b/PWGEM/Dilepton/Tasks/matchingMFT.cxx @@ -13,15 +13,20 @@ /// \brief a task to study matching MFT-[MCH-MID] in MC /// \author daiki.sekihata@cern.ch +#include "PWGEM/Dilepton/Utils/MlResponseFwdTrack.h" + #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/fwdtrackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Tools/ML/MlResponse.h" #include +#include #include #include #include @@ -30,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +52,7 @@ #include #include +#include #include #include #include @@ -111,6 +118,18 @@ struct matchingMFT { Configurable matchingZ{"matchingZ", -77.5, "z position where matching is performed"}; Configurable cfgApplyPreselectionInBestMatch{"cfgApplyPreselectionInBestMatch", false, "flag to apply preselection in find best match function"}; + // configuration for matching with ML + Configurable useMLmatching{"useMLmatching", false, "Flag to use ML for matching between MFT and MCH-MID"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 20}, "Bin limits for ML application"}; + Configurable> cutsMl{"cutsMl", std::vector{0.95, 0.95, 0.7, 0.7, 0.8, 0.8, 0.7, 0.7}, "ML cuts per bin"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"multFT0C", "ptMCHMID", "rSigned1Pt", "dEta", "dPhi", "dX", "dY", "chi2MatchMCHMFT"}, "Names of ML model input features"}; + Configurable nameBinningFeature{"nameBinningFeature", "multFT0C", "Names of ML model binning feature"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + struct : ConfigurableGroup { std::string prefix = "eventcut_group"; Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; @@ -151,10 +170,28 @@ struct matchingMFT { } eventcuts; o2::aod::rctsel::RCTFlagsChecker rctChecker; + o2::analysis::MlResponseFwdTrack mlResponseFwdTrack; HistogramRegistry fRegistry{"fRegistry"}; static constexpr std::string_view muon_types[5] = {"MFTMCHMID/", "MFTMCHMIDOtherMatch/", "MFTMCH/", "MCHMID/", "MCH/"}; + struct matchedCandidate { + float multFT0C{0}; + float multMFT{0}; + float ptMCHMID{0}; + float rSigned1Pt{1e+10}; + float dEta{1e+10}; + float dPhi{1e+10}; + float dX{1e+10}; + float dY{1e+10}; + float chi2MatchMCHMFT{1e+10}; + + float sigmaPhiMFT{1e+10}; + float sigmaTglMFT{1e+10}; + float sigmaPhiMCHMID{1e+10}; + float sigmaTglMCHMID{1e+10}; + }; + void init(o2::framework::InitContext&) { if (doprocessWithoutFTTCA && doprocessWithFTTCA) { @@ -169,6 +206,31 @@ struct matchingMFT { rctChecker.init(eventcuts.cfgRCTLabel.value, eventcuts.cfgCheckZDC.value, eventcuts.cfgTreatLimitedAcceptanceAsBad.value); addHistograms(); + + if (useMLmatching) { + static constexpr int nClassesMl = 2; + const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutSmaller}; + const std::vector labelsClasses = {"Background", "Signal"}; + const uint32_t nBinsMl = binsMl.value.size() - 1; + const std::vector labelsBins(nBinsMl, "bin"); + double cutsMlArr[nBinsMl][nClassesMl]; + for (uint32_t i = 0; i < nBinsMl; i++) { + cutsMlArr[i][0] = 0.0; + cutsMlArr[i][1] = cutsMl.value[i]; + } + o2::framework::LabeledArray cutsMl = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; + + mlResponseFwdTrack.configure(binsMl.value, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdburl); + mlResponseFwdTrack.setModelPathsCCDB(onnxFileNames.value, ccdbApi, onnxPathsCCDB.value, timestampCCDB.value); + } else { + mlResponseFwdTrack.setModelPathsLocal(onnxFileNames.value); + } + mlResponseFwdTrack.cacheInputFeaturesIndices(namesInputFeatures); + mlResponseFwdTrack.cacheBinningIndex(nameBinningFeature); + mlResponseFwdTrack.init(enableOptimizations.value); + } // end of ML configuration } o2::ccdb::CcdbApi ccdbApi; @@ -415,28 +477,6 @@ struct matchingMFT { return (clmap > 0); } - // template - // float meanClusterSizeMFT(T const& track) - // { - // uint64_t mftClusterSizesAndTrackFlags = track.mftClusterSizesAndTrackFlags(); - // uint16_t clsSize = 0; - // uint16_t n = 0; - // for (unsigned int layer = 0; layer < 10; layer++) { - // uint16_t size_per_layer = (mftClusterSizesAndTrackFlags >> (layer * 6)) & 0x3f; - // clsSize += size_per_layer; - // if (size_per_layer > 0) { - // n++; - // } - // // LOGF(info, "track.globalIndex() = %d, layer = %d, size_per_layer = %d", track.globalIndex(), layer, size_per_layer); - // } - - // if (n > 0) { - // return static_cast(clsSize) / static_cast(n) * std::fabs(std::sin(std::atan(track.tgl()))); - // } else { - // return 0.f; - // } - // } - template void getDxDyAtMatchingPlane(TCollision const& collision, TFwdTrack const& fwdtrack, TMFTrackCov const& mftCovs, float& dx, float& dy) { @@ -475,7 +515,7 @@ struct matchingMFT { } template - void fillHistograms(TCollision const& collision, TFwdTrack fwdtrack, TFwdTracks const&, TMFTTracks const&, TMFTTracksCov const& mftCovs) + void fillHistograms(TCollision const& collision, const TFwdTrack& fwdtrack, TFwdTracks const&, TMFTTracks const&, TMFTTracksCov const& mftCovs) { if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { // only for protection return; @@ -817,8 +857,8 @@ struct matchingMFT { std::vector> vec_min_chi2MatchMCHMFT; // std::pair -> chi2MatchMCHMFT; // std::map, bool> mapCorrectMatch; - template - void findBestMatchPerMCHMID(TCollision const& collision, TFwdTrack const& fwdtrack, TFwdTracks const& fwdtracks, TMFTTracks const&) + template + void findBestMatchPerMCHMID(TCollision const& collision, TFwdTrack const& fwdtrack, TFwdTracks const& fwdtracks, TMFTTracks const& mfttracks, TMFTTracksCov const& mftCovs) { if (fwdtrack.trackType() != o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) { return; @@ -827,6 +867,8 @@ struct matchingMFT { return; } + auto mfttracks_per_collision = mfttracks.sliceBy(perCollision_MFT, collision.globalIndex()); + std::tuple tupleIds_at_min_chi2mftmch; float min_chi2MatchMCHMFT = 1e+10; auto muons_per_MCHMID = fwdtracks.sliceBy(fwdtracksPerMCHTrack, fwdtrack.globalIndex()); @@ -843,6 +885,9 @@ struct matchingMFT { float dcaXY_Matched = std::sqrt(dcaX_Matched * dcaX_Matched + dcaY_Matched * dcaY_Matched); float pDCA = fwdtrack.p() * dcaXY_Matched; + o2::dataformats::GlobalFwdTrack muonAtMP = propagateMuon(fwdtrack, fwdtrack, collision, propagationPoint::kToMatchingPlane, matchingZ, mBz, mZShift); // propagated to matching plane + float phiMCHMIDatMP = RecoDecay::constrainAngle(muonAtMP.getPhi(), 0, 1U); + for (const auto& muon_tmp : muons_per_MCHMID) { if (muon_tmp.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack) { auto tupleId = std::make_tuple(muon_tmp.globalIndex(), muon_tmp.matchMCHTrackId(), muon_tmp.matchMFTTrackId()); @@ -885,6 +930,44 @@ struct matchingMFT { float dcaY = propmuonAtPV.getY() - collision.posY(); float dcaXY = std::sqrt(dcaX * dcaX + dcaY * dcaY); + if constexpr (withMFTCov) { + if (useMLmatching) { + matchedCandidate candidate; + candidate.multFT0C = collision.multFT0C(); + candidate.multMFT = static_cast(mfttracks_per_collision.size()); + candidate.chi2MatchMCHMFT = muon_tmp.chi2MatchMCHMFT(); + + auto mfttrackcov = mftCovs.rawIteratorAt(map_mfttrackcovs[mfttrack.globalIndex()]); + o2::track::TrackParCovFwd mftsaAtMP = getTrackParCovFwdShift(mfttrack, mZShift, mfttrackcov); // values at innermost update + mftsaAtMP.propagateToZhelix(matchingZ, mBz); // propagated to matching plane + float phiMFTatMP = RecoDecay::constrainAngle(mftsaAtMP.getPhi(), 0, 1U); + + candidate.rSigned1Pt = mftsaAtMP.getInvQPt() / muonAtMP.getInvQPt(); + candidate.dEta = mftsaAtMP.getEta() - muonAtMP.getEta(); + candidate.dPhi = RecoDecay::constrainAngle(phiMFTatMP - phiMCHMIDatMP, -o2::constants::math::PIHalf, 1U); + candidate.dX = mftsaAtMP.getX() - muonAtMP.getX(); + candidate.dY = mftsaAtMP.getY() - muonAtMP.getY(); + + candidate.sigmaTglMCHMID = std::sqrt(muonAtMP.getSigma2Tanl()); + candidate.sigmaPhiMCHMID = std::sqrt(muonAtMP.getSigma2Phi()); + candidate.sigmaTglMFT = std::sqrt(mftsaAtMP.getSigma2Tanl()); + candidate.sigmaPhiMFT = std::sqrt(mftsaAtMP.getSigma2Phi()); + + std::vector inputFeatures = mlResponseFwdTrack.getInputFeatures(candidate); + float binningFeature = mlResponseFwdTrack.getBinningFeature(candidate); + int pbin = lower_bound(binsMl.value.begin(), binsMl.value.end(), binningFeature) - binsMl.value.begin() - 1; + if (pbin < 0) { + pbin = 0; + } else if (static_cast(binsMl.value.size()) - 2 < pbin) { + pbin = static_cast(binsMl.value.size()) - 2; + } + float probaEl = mlResponseFwdTrack.getModelOutput(inputFeatures, pbin)[1]; // 0: wrong, 1:correct + if (probaEl < cutsMl.value[pbin]) { + continue; + } + } + } + if (isPrimary) { if (isMatched) { fRegistry.fill(HIST("MFTMCHMID/primary/correct/hdR_Chi2MatchMCHMFT"), muon_tmp.chi2MatchMCHMFT(), dr); @@ -1092,7 +1175,7 @@ struct matchingMFT { initCCDB(bc); auto fwdtracks_per_coll = fwdtracks.sliceBy(perCollision, collision.globalIndex()); for (const auto& fwdtrack : fwdtracks_per_coll) { - findBestMatchPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks); + findBestMatchPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks, nullptr); } // end of fwdtrack loop } // end of collision loop @@ -1150,7 +1233,7 @@ struct matchingMFT { auto fwdtrackIdsThisCollision = fwdtrackIndices.sliceBy(fwdtrackIndicesPerCollision, collision.globalIndex()); for (const auto& fwdtrackId : fwdtrackIdsThisCollision) { auto fwdtrack = fwdtrackId.template fwdtrack_as(); - findBestMatchPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks); + findBestMatchPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks, nullptr); } // end of fwdtrack loop } // end of collision loop @@ -1213,7 +1296,7 @@ struct matchingMFT { auto fwdtrackIdsThisCollision = fwdtrackIndices.sliceBy(fwdtrackIndicesPerCollision, collision.globalIndex()); for (const auto& fwdtrackId : fwdtrackIdsThisCollision) { auto fwdtrack = fwdtrackId.template fwdtrack_as(); - findBestMatchPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks); + findBestMatchPerMCHMID(collision, fwdtrack, fwdtracks, mfttracks, mftCovs); } // end of fwdtrack loop } // end of collision loop diff --git a/PWGEM/Dilepton/Tasks/smearing.cxx b/PWGEM/Dilepton/Tasks/smearing.cxx index 3d5a8668244..0d1e850413a 100644 --- a/PWGEM/Dilepton/Tasks/smearing.cxx +++ b/PWGEM/Dilepton/Tasks/smearing.cxx @@ -129,8 +129,30 @@ struct ApplySmearing { MomentumSmearer smearer_StandaloneMuon; MomentumSmearer smearer_GlobalMuon; Service ccdb; + int mRunNumber{0}; void init(InitContext&) + { + mRunNumber = 0; + if (fTimestamp < 0) { + LOG(fatal) << "Don't use time stamp = -1"; + } + if (doprocessCocktail) { + initResolutionMap(fTimestamp); + } + } + + template + void initCCDB(TBC const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + initResolutionMap(bc.timestamp()); + mRunNumber = bc.runNumber(); + } + + void initResolutionMap(const int64_t timestamp) { smearer_Electron.setNDSmearing(electron_filenames.fConfigNDSmearing.value); smearer_Electron.setResFileName(TString(electron_filenames.fConfigResFileName)); @@ -180,19 +202,19 @@ struct ApplySmearing { smearer_Electron.setCcdbPathRes(TString(electron_filenames.fConfigCcdbPathRes)); smearer_Electron.setCcdbPathEff(TString(electron_filenames.fConfigCcdbPathEff)); smearer_Electron.setCcdbPathDCA(TString(electron_filenames.fConfigCcdbPathDCA)); - smearer_Electron.setTimestamp(fTimestamp); + smearer_Electron.setTimestamp(timestamp); smearer_Electron.setCcdb(ccdb); smearer_StandaloneMuon.setCcdbPathRes(TString(sa_muon_filenames.fConfigCcdbPathRes)); smearer_StandaloneMuon.setCcdbPathEff(TString(sa_muon_filenames.fConfigCcdbPathEff)); smearer_StandaloneMuon.setCcdbPathDCA(TString(sa_muon_filenames.fConfigCcdbPathDCA)); - smearer_StandaloneMuon.setTimestamp(fTimestamp); + smearer_StandaloneMuon.setTimestamp(timestamp); smearer_StandaloneMuon.setCcdb(ccdb); smearer_GlobalMuon.setCcdbPathRes(TString(gl_muon_filenames.fConfigCcdbPathRes)); smearer_GlobalMuon.setCcdbPathEff(TString(gl_muon_filenames.fConfigCcdbPathEff)); smearer_GlobalMuon.setCcdbPathDCA(TString(gl_muon_filenames.fConfigCcdbPathDCA)); - smearer_GlobalMuon.setTimestamp(fTimestamp); + smearer_GlobalMuon.setTimestamp(timestamp); smearer_GlobalMuon.setCcdb(ccdb); } smearer_Electron.init(); @@ -203,7 +225,7 @@ struct ApplySmearing { template void applySmearing(TTracksMC const& tracksMC, TCollisions const& collisions, TMCCollisions const&) { - for (auto& mctrack : tracksMC) { + for (const auto& mctrack : tracksMC) { float ptgen = mctrack.pt(); float etagen = mctrack.eta(); float phigen = mctrack.phi(); @@ -269,14 +291,10 @@ struct ApplySmearing { void processMCanalysisEM(aod::EMMCParticles const& tracksMC, MyCollisions const& collisions, MyMCCollisions const& mccollisions) { + initCCDB(collisions.iteratorAt(0)); applySmearing(tracksMC, collisions, mccollisions); } - // void processMCanalysisDQ(ReducedMCTracks const& tracksMC) - // { - // applySmearing(tracksMC); - // } - void processCocktail(aod::McParticles const& tracksMC) { applySmearing(tracksMC, nullptr, nullptr); @@ -285,7 +303,7 @@ struct ApplySmearing { void processDummyCocktail(aod::McParticles const& tracksMC) { // don't apply smearing - for (auto& mctrack : tracksMC) { + for (const auto& mctrack : tracksMC) { int pdgCode = mctrack.pdgCode(); if (std::abs(pdgCode) == 11) { smearedelectron(mctrack.pt(), mctrack.eta(), mctrack.phi(), 1.0, 0.0); @@ -356,7 +374,7 @@ struct CheckSmearing { template void Check(TTracksMC const& tracksMC, TMCCollisions const&) { - for (auto& mctrack : tracksMC) { + for (const auto& mctrack : tracksMC) { if (std::abs(mctrack.pdgCode()) != fPdgCode) { continue; } diff --git a/PWGEM/Dilepton/Tasks/studyDCAFitter.cxx b/PWGEM/Dilepton/Tasks/studyDCAFitter.cxx index 7dc849507e2..0383fe3868c 100644 --- a/PWGEM/Dilepton/Tasks/studyDCAFitter.cxx +++ b/PWGEM/Dilepton/Tasks/studyDCAFitter.cxx @@ -13,26 +13,28 @@ /// \brief a task to study tagging e from charm hadron decays in MC /// \author daiki.sekihata@cern.ch +#include "PWGEM/Dilepton/DataModel/lmeeMLTables.h" #include "PWGEM/Dilepton/Utils/MCUtilities.h" #include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" #include #include +#include #include -#include #include #include #include #include +#include // for PV refit +#include #include #include #include @@ -43,6 +45,8 @@ #include #include #include +#include +#include // for PV refit #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -50,12 +54,14 @@ #include #include +#include #include +#include #include -#include -#include #include +#include + using namespace o2; using namespace o2::soa; using namespace o2::framework; @@ -66,78 +72,71 @@ using namespace o2::aod::pwgem::dilepton::utils::pairutil; struct studyDCAFitter { using MyCollisions = soa::Join; + using MyTracks = soa::Join; + using MyTrack = MyTracks::iterator; + using MyBCs = soa::Join; - using MyTracks = soa::Join; - - struct DielectronAtSV { // ee pair at SV - bool isfound{false}; - float mass{-999.f}; - float pt{-999.f}; - float dca2legs{-999.f}; - float cospa{-999.f}; - float lxy{-999.f}; - float lz{-999.f}; - float lxyz = std::sqrt(std::pow(lxy, 2) + std::pow(lz, 2)); - }; + Produces eventTable; + Produces dileptonTable; // Configurables Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; - Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; - Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; - Configurable d_UseAbsDCA{"d_UseAbsDCA", true, "Use Abs DCAs"}; - Configurable d_UseWeightedPCA{"d_UseWeightedPCA", false, "Vertices use cov matrices"}; + // Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; + Configurable cfgDownSampling{"cfgDownSampling", 1.1, "down sampling for wrongly found SV"}; struct : ConfigurableGroup { - std::string prefix = "electroncut"; - Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.05, "min pT for single track"}; + std::string prefix = "electronCut"; + Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.4, "min pT for single track"}; Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.9, "min eta for single track"}; - Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.9, "max eta for single track"}; + Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; + Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.8, "min. TPC Ncr/Nf ratio"}; Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 0.7, "max fraction of shared clusters in TPC"}; Configurable cfg_min_ncrossedrows_tpc{"cfg_min_ncrossedrows_tpc", 80, "min ncrossed rows"}; Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 5, "min ncluster its"}; - Configurable cfg_min_ncluster_itsib{"cfg_min_ncluster_itsib", 3, "min ncluster itsib"}; + Configurable cfg_min_ncluster_itsib{"cfg_min_ncluster_itsib", 1, "min ncluster itsib"}; Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; Configurable cfg_max_chi2its{"cfg_max_chi2its", 5.0, "max chi2/NclsITS"}; Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; - - Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -2, "min TPC n sigma el inclusion"}; - Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3, "max TPC n sigma el inclusion"}; - Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -1e+10, "min TPC n sigma pi exclusion"}; - Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3, "max TPC n sigma pi exclusion"}; - Configurable cfg_min_TOFNsigmaEl{"cfg_min_TOFNsigmaEl", -3, "min TOF n sigma el inclusion"}; - Configurable cfg_max_TOFNsigmaEl{"cfg_max_TOFNsigmaEl", +3, "max TOF n sigma el inclusion"}; - } electroncut; + } electronCut; struct : ConfigurableGroup { - std::string prefix = "svcut"; - Configurable cfg_min_cospa{"cfg_min_cospa", 0.999, "min cospa"}; - Configurable cfg_max_dca2legs{"cfg_max_dca2legs", 0.1, "max distance between 2 legs"}; - } svcut; + std::string prefix = "svCut"; + Configurable d_UseAbsDCA{"d_UseAbsDCA", true, "Use Abs DCAs"}; + Configurable d_UseWeightedPCA{"d_UseWeightedPCA", false, "Vertices use cov matrices"}; + Configurable cfg_max_chi2PCA{"cfg_max_chi2PCA", 1.0, "max chi2PCA"}; + } svCut; - Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; - Configurable cfgCentMin{"cfgCentMin", -1.f, "min. centrality"}; - Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; - Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; - Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; - - Configurable cfgEventGeneratorType{"cfgEventGeneratorType", -1, "if positive, select event generator type. i.e. gap or signal"}; + struct : ConfigurableGroup { + std::string prefix = "eventCut"; + Configurable cfgRejectEventGenerator{"cfgRejectEventGenerator", 999, "reject event generator. e.g. reject tracks from gap events"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + Configurable cfgCentMin{"cfgCentMin", -1.f, "min. centrality"}; + Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; + Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + // for RCT + o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", true, "require good detector flag in run condtion table"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for AA"}; + o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } eventCut; + + o2::framework::ConfigurableAxis ConfMllBins{"ConfMllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.31, 0.32, 0.33, 0.34, 0.35, 0.36, 0.37, 0.38, 0.39, 0.40, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.50, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.60, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.70, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.80, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.90, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.00, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07, 1.08, 1.09, 1.10, 1.11, 1.12, 1.13, 1.14, 1.15, 1.16, 1.17, 1.18, 1.19, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.75, 2.80, 2.85, 2.90, 2.95, 3.00, 3.05, 3.10, 3.15, 3.20, 3.25, 3.30, 3.35, 3.40, 3.45, 3.50, 3.55, 3.60, 3.65, 3.70, 3.75, 3.80, 3.85, 3.90, 3.95, 4.00, 4.50, 5.00, 5.50, 6.00, 6.50, 7.00, 7.50, 8.00}, "mll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfPtllBins{"ConfPtllBins", {o2::framework::VARIABLE_WIDTH, 0.00, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.50, 3.00, 3.50, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00, 9.00, 10.00}, "pTll bins for output histograms"}; + o2::framework::ConfigurableAxis ConfDCAllBins{"ConfDCAllBins", {o2::framework::VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "DCAll bins for output histograms"}; HistogramRegistry fRegistry{"fRegistry"}; - static constexpr std::string_view hadron_names[6] = {"LF/", "Jpsi/", "D0/", "Dpm/", "Ds/", "Lc/"}; - static constexpr std::string_view pair_names[3] = {"e_Kpm/", "e_K0S/", "e_Lambda/"}; - static constexpr std::string_view hTypes[4] = {"findable/", "correct/", "fake/", "miss/"}; - static constexpr std::string_view promptTypes[2] = {"prompt/", "nonprompt/"}; void init(o2::framework::InitContext&) { @@ -147,29 +146,39 @@ struct studyDCAFitter { ccdb->setFatalWhenNull(false); fitter.setPropagateToPCA(true); - fitter.setMaxR(5.f); + fitter.setMaxR(200.f); fitter.setMinParamChange(1e-3); fitter.setMinRelChi2Change(0.9); fitter.setMaxDZIni(1e9); fitter.setMaxChi2(1e9); - fitter.setUseAbsDCA(d_UseAbsDCA); - fitter.setWeightedFinalPCA(d_UseWeightedPCA); + fitter.setUseAbsDCA(svCut.d_UseAbsDCA); + fitter.setWeightedFinalPCA(svCut.d_UseWeightedPCA); fitter.setMatCorrType(matCorr); + mRunNumber = 0; + d_bz = 0; + + std::random_device seed_gen; + engine = std::mt19937(seed_gen()); + dist01 = std::uniform_real_distribution(0.0f, 1.0f); + addHistograms(); } - int mRunNumber; - float d_bz; + int mRunNumber{0}; + float d_bz{0}; Service ccdb; // o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - const o2::dataformats::MeanVertexObject* mMeanVtx = nullptr; + // const o2::dataformats::MeanVertexObject* mMeanVtx = nullptr; o2::base::MatLayerCylSet* lut = nullptr; o2::vertexing::DCAFitterN<2> fitter; o2::dataformats::DCA mDcaInfoCov; o2::dataformats::VertexBase mVtx; + std::mt19937 engine; + std::uniform_real_distribution dist01; + template void initCCDB(TBC const& bc) { @@ -185,30 +194,13 @@ struct studyDCAFitter { LOG(info) << "Material look-up table already in place. Not reloading."; } - // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { - d_bz = d_bz_input; - o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); - } - o2::base::Propagator::initFieldFromGRP(&grpmag); - o2::base::Propagator::Instance()->setMatLUT(lut); - mMeanVtx = ccdb->getForTimeStamp(mVtxPath, bc.timestamp()); - mRunNumber = bc.runNumber(); - return; - } - auto run3grp_timestamp = bc.timestamp(); o2::parameters::GRPObject* grpo = 0x0; o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) { - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - } if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); o2::base::Propagator::Instance()->setMatLUT(lut); - mMeanVtx = ccdb->getForTimeStamp(mVtxPath, bc.timestamp()); + // mMeanVtx = ccdb->getForTimeStamp(mVtxPath, bc.timestamp()); // Fetch magnetic field from ccdb for current collision d_bz = grpo->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; @@ -219,7 +211,7 @@ struct studyDCAFitter { } o2::base::Propagator::initFieldFromGRP(grpmag); o2::base::Propagator::Instance()->setMatLUT(lut); - mMeanVtx = ccdb->getForTimeStamp(mVtxPath, bc.timestamp()); + // mMeanVtx = ccdb->getForTimeStamp(mVtxPath, bc.timestamp()); // Fetch magnetic field from ccdb for current collision d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); @@ -245,47 +237,77 @@ struct studyDCAFitter { fRegistry.add("Event/hCentFT0CvsMultNTracksPV", "hCentFT0CvsMultNTracksPV;centrality FT0C (%);N_{track} to PV", kTH2F, {{110, 0, 110}, {600, 0, 6000}}, false); fRegistry.add("Event/hMultFT0CvsMultNTracksPV", "hMultFT0CvsMultNTracksPV;mult. FT0C;N_{track} to PV", kTH2F, {{60, 0, 60000}, {600, 0, 6000}}, false); + // fRegistry.add("Event/refitPV/remove/hNContrib", "hNContrib;before;after", kTH2F, {{1001, -0.5, 1000.5}, {1001, -0.5, 1000.5}}, false); + // fRegistry.add("Event/refitPV/remove/hChi2", "hChi2;before;after", kTH2F, {{100, 0, 1000}, {100, 0, 1000}}, false); + // fRegistry.add("Event/refitPV/remove/hDeltaXvsNContrib", "hDeltaXvsNContrib;numContrib after;X_{after} - X_{before} (cm)", kTH2F, {{1001, -0.5, 1000.5}, {200, -0.01, 0.01}}, false); + // fRegistry.add("Event/refitPV/remove/hDeltaYvsNContrib", "hDeltaYvsNContrib;numContrib after;Y_{after} - Y_{before} (cm)", kTH2F, {{1001, -0.5, 1000.5}, {200, -0.01, 0.01}}, false); + // fRegistry.add("Event/refitPV/remove/hDeltaZvsNContrib", "hDeltaZvsNContrib;numContrib after;Z_{after} - Z_{before} (cm)", kTH2F, {{1001, -0.5, 1000.5}, {200, -0.01, 0.01}}, false); + // fRegistry.addClone("Event/refitPV/remove/", "Event/refitPV/add/"); + + const o2::framework::AxisSpec axis_mass{ConfMllBins, "m_{ll} (GeV/c^{2})"}; + const o2::framework::AxisSpec axis_pt{ConfPtllBins, "p_{T,ee} (GeV/c)"}; + const o2::framework::AxisSpec axis_dca{ConfDCAllBins, "DCA_{ee}^{3D} (#sigma)"}; + // const o2::framework::AxisSpec axis_dca_remove1track{ConfDCAllBins, "DCA_{ee}^{3D, remove 1 track} (#sigma)"}; + // const o2::framework::AxisSpec axis_dca_remove2track{ConfDCAllBins, "DCA_{ee}^{3D, remove 2 tracks} (#sigma)"}; + + // for single tracks + fRegistry.add("Track/Zboson/hs", "hs;p_{T,e} (GeV/c);#eta_{e};#varphi_{e} (rad);DCA_{e}^{3D} (#sigma);", kTHnSparseF, {{100, 0, 10}, {20, -1, +1}, {36, 0, 2 * M_PI}, {100, 0, 10}}, false); + // fRegistry.add("Track/Zboson/hChi2PV", "log_{10}(#chi^{2}_{IP})", kTH1F, {{400, -200, 200}}, false); + // fRegistry.add("Track/Zboson/hDiffDCAinSigma3D", "diff DCA 3D(#sigma)", kTH1F, {{200, -10, 10}}, false); + fRegistry.addClone("Track/Zboson/", "Track/PromptJpsi/"); + fRegistry.addClone("Track/Zboson/", "Track/NonPromptJpsi/"); + fRegistry.addClone("Track/Zboson/", "Track/c2e/"); + fRegistry.addClone("Track/Zboson/", "Track/b2e/"); + fRegistry.addClone("Track/Zboson/", "Track/b2c2e/"); + // for pairs - fRegistry.add("Pair/PV/Data/uls/hs", "hs;m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c);DCA_{ee}^{3D} (#sigma);", kTHnSparseF, {{500, 0, 5}, {100, 0, 10}, {100, 0, 10}}, true); - fRegistry.addClone("Pair/PV/Data/uls/", "Pair/PV/Data/lspp/"); - fRegistry.addClone("Pair/PV/Data/uls/", "Pair/PV/Data/lsmm/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/PromptPhi/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/NonPromptPhi/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/PromptOmega/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/NonPromptOmega/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/PromptJpsi/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/NonPromptJpsi/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/c2e_c2e/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/b2e_b2e/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/b2c2e_b2c2e/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/b2c2e_b2e_sameb/"); - fRegistry.addClone("Pair/PV/Data/", "Pair/PV/MC/b2c2e_b2e_diffb/"); - - fRegistry.add("Pair/SV/Data/uls/hs", "hs;m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c);L_{xy} m_{ee}/p_{T,ee} (mm);", kTHnSparseF, {{500, 0, 5}, {100, 0, 10}, {200, -10, 10}}, true); - fRegistry.add("Pair/SV/Data/uls/hCosPA", "cosPA;cosPA;", kTH1F, {{200, -1, 1}}, false); - fRegistry.add("Pair/SV/Data/uls/hDCA2Legs", "distance between 2 legs at PCA;distance between 2 legs (cm);", kTH1F, {{100, 0, 0.1}}, false); - fRegistry.addClone("Pair/SV/Data/uls/", "Pair/SV/Data/lspp/"); - fRegistry.addClone("Pair/SV/Data/uls/", "Pair/SV/Data/lsmm/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/PromptPhi/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/NonPromptPhi/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/PromptOmega/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/NonPromptOmega/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/PromptJpsi/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/NonPromptJpsi/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/c2e_c2e/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/b2e_b2e/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/b2c2e_b2c2e/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/b2c2e_b2e_sameb/"); - fRegistry.addClone("Pair/SV/Data/", "Pair/SV/MC/b2c2e_b2e_diffb/"); + fRegistry.add("Pair/PV/Zboson/uls/hs", "hs;m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c);DCA_{ee}^{3D} (#sigma);", kTHnSparseF, {axis_mass, axis_pt, axis_dca}, false); + fRegistry.addClone("Pair/PV/Zboson/uls/", "Pair/PV/Zboson/lspp/"); + fRegistry.addClone("Pair/PV/Zboson/uls/", "Pair/PV/Zboson/lsmm/"); + fRegistry.addClone("Pair/PV/Zboson/", "Pair/PV/PromptJpsi/"); + fRegistry.addClone("Pair/PV/Zboson/", "Pair/PV/NonPromptJpsi/"); + fRegistry.addClone("Pair/PV/Zboson/", "Pair/PV/c2e_c2e/"); + fRegistry.addClone("Pair/PV/Zboson/", "Pair/PV/b2e_b2e/"); + fRegistry.addClone("Pair/PV/Zboson/", "Pair/PV/b2c2e_b2c2e/"); + fRegistry.addClone("Pair/PV/Zboson/", "Pair/PV/b2c2e_b2e_sameb/"); + fRegistry.addClone("Pair/PV/Zboson/", "Pair/PV/b2c2e_b2e_diffb/"); + + fRegistry.add("Pair/SV/Zboson/uls/hs", "hs;m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c);DCA_{ee}^{3D} (#sigma);", kTHnSparseF, {axis_mass, axis_pt, axis_dca, {200, -1, 1}}, false); + fRegistry.add("Pair/SV/Zboson/uls/hCosPA", "cosPA;cosPA;", kTH1F, {{200, -1, 1}}, false); + fRegistry.add("Pair/SV/Zboson/uls/hChi2PCA", "chi2 at PCA;log_{10}(#chi^{2}_{PCA});", kTH1F, {{1000, -10, 0}}, false); + fRegistry.addClone("Pair/SV/Zboson/uls/", "Pair/SV/Zboson/lspp/"); + fRegistry.addClone("Pair/SV/Zboson/uls/", "Pair/SV/Zboson/lsmm/"); + fRegistry.addClone("Pair/SV/Zboson/", "Pair/SV/PromptJpsi/"); + fRegistry.addClone("Pair/SV/Zboson/", "Pair/SV/NonPromptJpsi/"); + fRegistry.addClone("Pair/SV/Zboson/", "Pair/SV/c2e_c2e/"); + fRegistry.addClone("Pair/SV/Zboson/", "Pair/SV/b2e_b2e/"); + fRegistry.addClone("Pair/SV/Zboson/", "Pair/SV/b2c2e_b2c2e/"); + fRegistry.addClone("Pair/SV/Zboson/", "Pair/SV/b2c2e_b2e_sameb/"); + fRegistry.addClone("Pair/SV/Zboson/", "Pair/SV/b2c2e_b2e_diffb/"); } - template - bool isElectron(TTrack const& track) + template + bool isSelectedCollision(TCollision const& collision) { - // TOFif - bool is_el_included_TPC = electroncut.cfg_min_TPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < electroncut.cfg_max_TPCNsigmaEl; - bool is_el_included_TOF = track.hasTOF() ? (electroncut.cfg_min_TOFNsigmaEl < track.tofNSigmaEl() && track.tofNSigmaEl() < electroncut.cfg_max_TOFNsigmaEl) : true; - return is_el_included_TPC && is_el_included_TOF; + if (!(eventCut.cfgZvtxMin < collision.posZ() && collision.posZ() < eventCut.cfgZvtxMax)) { + return false; + } + if (eventCut.cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + if (eventCut.cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (eventCut.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (eventCut.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (eventCut.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + return true; } template @@ -296,51 +318,51 @@ struct studyDCAFitter { return false; } - if (trackParCov.getPt() < electroncut.cfg_min_pt_track || electroncut.cfg_max_pt_track < trackParCov.getPt()) { + if (trackParCov.getPt() < electronCut.cfg_min_pt_track || electronCut.cfg_max_pt_track < trackParCov.getPt()) { return false; } - if (trackParCov.getEta() < electroncut.cfg_min_eta_track || electroncut.cfg_max_eta_track < trackParCov.getEta()) { + if (trackParCov.getEta() < electronCut.cfg_min_eta_track || electronCut.cfg_max_eta_track < trackParCov.getEta()) { return false; } - if (std::fabs(dcaXY) > electroncut.cfg_max_dcaxy) { + if (std::fabs(dcaXY) > electronCut.cfg_max_dcaxy) { return false; } - if (std::fabs(dcaZ) > electroncut.cfg_max_dcaz) { + if (std::fabs(dcaZ) > electronCut.cfg_max_dcaz) { return false; } - if (track.itsChi2NCl() > electroncut.cfg_max_chi2its) { + if (track.itsChi2NCl() > electronCut.cfg_max_chi2its) { return false; } - if (track.itsNCls() < electroncut.cfg_min_ncluster_its) { + if (track.itsNCls() < electronCut.cfg_min_ncluster_its) { return false; } - if (track.itsNClsInnerBarrel() < electroncut.cfg_min_ncluster_itsib) { + if (track.itsNClsInnerBarrel() < electronCut.cfg_min_ncluster_itsib) { return false; } - if (track.tpcChi2NCl() > electroncut.cfg_max_chi2tpc) { + if (track.tpcChi2NCl() > electronCut.cfg_max_chi2tpc) { return false; } - if (track.tpcNClsFound() < electroncut.cfg_min_ncluster_tpc) { + if (track.tpcNClsFound() < electronCut.cfg_min_ncluster_tpc) { return false; } - if (track.tpcNClsCrossedRows() < electroncut.cfg_min_ncrossedrows_tpc) { + if (track.tpcNClsCrossedRows() < electronCut.cfg_min_ncrossedrows_tpc) { return false; } - if (track.tpcCrossedRowsOverFindableCls() < electroncut.cfg_min_cr2findable_ratio_tpc) { + if (track.tpcCrossedRowsOverFindableCls() < electronCut.cfg_min_cr2findable_ratio_tpc) { return false; } - if (track.tpcFractionSharedCls() > electroncut.cfg_max_frac_shared_clusters_tpc) { + if (track.tpcFractionSharedCls() > electronCut.cfg_max_frac_shared_clusters_tpc) { return false; } @@ -361,15 +383,48 @@ struct studyDCAFitter { fRegistry.fill(HIST("Event/hMultFT0CvsMultNTracksPV"), collision.multFT0C(), collision.multNTracksPV()); } - template - void fillElectronHistograms(TTrack const& track, TTrackParCov const& trackParCov, const float dcaXY, const float dcaZ) + template + void fillElectronHistograms(TCollision const&, TTrack const& track, TMCParticles const& mcParticles /*, TRefittedPV const& refittedPV, TRefittedPV const& refittedPVwoMod*/) { - if (std::find(used_electronIds.begin(), used_electronIds.end(), std::make_pair(findId, track.globalIndex())) == used_electronIds.end()) { - float dca3DinSigma = dca3DinSigmaOTF(dcaXY, dcaZ, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()); - fRegistry.fill(HIST(hadron_names[charmHadronId]) + HIST("electron/") + HIST(promptTypes[promptId]) + HIST(hTypes[findId]) + HIST("hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - fRegistry.fill(HIST(hadron_names[charmHadronId]) + HIST("electron/") + HIST(promptTypes[promptId]) + HIST(hTypes[findId]) + HIST("hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); - fRegistry.fill(HIST(hadron_names[charmHadronId]) + HIST("electron/") + HIST(promptTypes[promptId]) + HIST(hTypes[findId]) + HIST("hTOFbeta"), trackParCov.getP(), track.beta()); - used_electronIds.emplace_back(std::make_pair(findId, track.globalIndex())); + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(o2::track::PID::Electron); + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + float dcaXY = mDcaInfoCov.getY(); + float dcaZ = mDcaInfoCov.getZ(); + float dca3DinSigma = dca3DinSigmaOTF(dcaXY, dcaZ, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()); + + float pt = trackParCov.getPt(); + float eta = trackParCov.getEta(); + float phi = RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U); + + // mDcaInfoCov.set(999, 999, 999, 999, 999); + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + // dcaXY = mDcaInfoCov.getY(); + // dcaZ = mDcaInfoCov.getZ(); + // float dca3DinSigma_unbiased = dca3DinSigmaOTF(dcaXY, dcaZ, mDcaInfoCov.getSigmaY2(), mDcaInfoCov.getSigmaZ2(), mDcaInfoCov.getSigmaYZ()); + // float diff = dca3DinSigma_unbiased - dca3DinSigma_biased; + // float diffChi2PV = refittedPVwoMod.getChi2() - refittedPV.getChi2(); + + auto mcParticle = track.template mcParticle_as(); // electron + auto mcMother = mcParticle.template mothers_as()[0]; + + if (isFromGammaZ(mcParticle, mcParticles) > -1) { + fRegistry.fill(HIST("Track/Zboson/hs"), pt, eta, phi, dca3DinSigma); + } else if (std::abs(mcMother.pdgCode()) == 443) { + if (IsFromCharm(mcMother, mcParticles) < 0 && IsFromBeauty(mcMother, mcParticles) < 0) { // prompt + fRegistry.fill(HIST("Track/PromptJpsi/hs"), pt, eta, phi, dca3DinSigma); + } else { // nonprompt + fRegistry.fill(HIST("Track/NonPromptJpsi/hs"), pt, eta, phi, dca3DinSigma); + } + } else if (isCharmMeson(mcMother) || isCharmBaryon(mcMother)) { + if (IsFromBeauty(mcMother, mcParticles) < 0) { // prompt + fRegistry.fill(HIST("Track/c2e/hs"), pt, eta, phi, dca3DinSigma); + } else { // nonprompt + fRegistry.fill(HIST("Track/b2c2e/hs"), pt, eta, phi, dca3DinSigma); + } + } else if (isBeautyMeson(mcMother) || isBeautyBaryon(mcMother)) { + fRegistry.fill(HIST("Track/b2e/hs"), pt, eta, phi, dca3DinSigma); } } @@ -384,10 +439,10 @@ struct studyDCAFitter { } template - int FindLF(TTrack const& posmc, TTrack const& negmc, TMCParticles const& mcparticles) + int FindCommonMother(TTrack const& posmc, TTrack const& negmc, TMCParticles const& mcparticles) { int arr[] = { - FindCommonMotherFrom2Prongs(posmc, negmc, -11, 11, 22, mcparticles), + isPairFromGammaZ(posmc, negmc, mcparticles), FindCommonMotherFrom2Prongs(posmc, negmc, -11, 11, 111, mcparticles), FindCommonMotherFrom2Prongs(posmc, negmc, -11, 11, 221, mcparticles), FindCommonMotherFrom2Prongs(posmc, negmc, -11, 11, 331, mcparticles), @@ -405,7 +460,7 @@ struct studyDCAFitter { return max; } - template + template void runPairingAtPV(TTrack const& t1, TTrack const& t2, TMCParticles const& mcParticles) { mDcaInfoCov.set(999, 999, 999, 999, 999); @@ -429,161 +484,314 @@ struct studyDCAFitter { float dca3DinSigma2 = dca3DinSigmaOTF(dcaXY2, dcaZ2, trackParCov2.getSigmaY2(), trackParCov2.getSigmaZ2(), trackParCov2.getSigmaZY()); float pair_dca = pairDCAQuadSum(dca3DinSigma1, dca3DinSigma2); - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/Data/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/Data/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/Data/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - - if constexpr (isMC) { - const auto& t1mc = t1.template mcParticle_as(); - const auto& t2mc = t2.template mcParticle_as(); - if (std::abs(t1mc.pdgCode()) != 11) { - return; - } - if (!(t1mc.isPhysicalPrimary() || t1mc.producedByGenerator())) { - return; - } - if (std::abs(t2mc.pdgCode()) != 11) { - return; - } - if (!(t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - return; - } - // const auto& mp1 = t1mc.template mothers_first_as(); // mother particle of t1 - // const auto& mp2 = t2mc.template mothers_first_as(); // mother particle of t2 - int mcCommonMotherid = FindLF(t1mc, t2mc, mcParticles); - int hfee_type = IsHF(t1mc, t2mc, mcParticles); - - if (mcCommonMotherid > -1) { - const auto cmp = mcParticles.rawIteratorAt(mcCommonMotherid); - switch (std::abs(cmp.pdgCode())) { - case 223: - if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/PromptOmega/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/PromptOmega/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/PromptOmega/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - } else { // nonprompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/NonPromptOmega/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/NonPromptOmega/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/NonPromptOmega/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - } - break; - case 333: - if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/PromptPhi/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/PromptPhi/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/PromptPhi/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - } else { // nonprompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/NonPromptPhi/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/NonPromptPhi/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/NonPromptPhi/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - } - break; - case 443: - if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/PromptJpsi/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/PromptJpsi/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/PromptJpsi/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - } else { // nonprompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/NonPromptJpsi/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/NonPromptJpsi/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/NonPromptJpsi/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - } - break; - default: - break; - } // end of switch for LF - } else if (hfee_type > -1) { - switch (hfee_type) { - case static_cast(EM_HFeeType::kCe_Ce): - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/c2e_c2e/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/c2e_c2e/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/c2e_c2e/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - break; - case static_cast(EM_HFeeType::kBe_Be): - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/b2e_b2e/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/b2e_b2e/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/b2e_b2e/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - break; - case static_cast(EM_HFeeType::kBCe_BCe): - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2c2e/uls/hs"), v12.M(), v12.Pt(), pair_dca); + auto t1mc = t1.template mcParticle_as(); + auto t2mc = t2.template mcParticle_as(); + + int mcCommonMotherId = FindCommonMother(t1mc, t2mc, mcParticles); + int hfee_type = IsHF(t1mc, t2mc, mcParticles); + + if (mcCommonMotherId > -1) { + auto cmp = mcParticles.rawIteratorAt(mcCommonMotherId); + switch (std::abs(cmp.pdgCode())) { + case 23: + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/PV/Zboson/uls/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/PV/Zboson/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/PV/Zboson/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + } + break; + case 443: + if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/PV/PromptJpsi/uls/hs"), v12.M(), v12.Pt(), pair_dca); } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2c2e/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + fRegistry.fill(HIST("Pair/PV/PromptJpsi/lspp/hs"), v12.M(), v12.Pt(), pair_dca); } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2c2e/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + fRegistry.fill(HIST("Pair/PV/PromptJpsi/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); } - break; - case static_cast(EM_HFeeType::kBCe_Be_SameB): + } else { // nonprompt if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2e_sameb/uls/hs"), v12.M(), v12.Pt(), pair_dca); + fRegistry.fill(HIST("Pair/PV/NonPromptJpsi/uls/hs"), v12.M(), v12.Pt(), pair_dca); } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2e_sameb/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + fRegistry.fill(HIST("Pair/PV/NonPromptJpsi/lspp/hs"), v12.M(), v12.Pt(), pair_dca); } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2e_sameb/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); - } - break; - case static_cast(EM_HFeeType::kBCe_Be_DiffB): - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2e_diffb/uls/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 1) { // LS+diff - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2e_diffb/lspp/hs"), v12.M(), v12.Pt(), pair_dca); - } else if constexpr (signType == 2) { // LS-diff - fRegistry.fill(HIST("Pair/PV/MC/b2c2e_b2e_diffb/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + fRegistry.fill(HIST("Pair/PV/NonPromptJpsi/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); } - break; + } + break; + default: + break; + } // end of switch for LF + } else if (hfee_type > -1) { + switch (hfee_type) { + case static_cast(EM_HFeeType::kCe_Ce): + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/PV/c2e_c2e/uls/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/PV/c2e_c2e/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/PV/c2e_c2e/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + } + break; + case static_cast(EM_HFeeType::kBe_Be): + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/PV/b2e_b2e/uls/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/PV/b2e_b2e/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/PV/b2e_b2e/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + } + break; + case static_cast(EM_HFeeType::kBCe_BCe): + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/PV/b2c2e_b2c2e/uls/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/PV/b2c2e_b2c2e/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/PV/b2c2e_b2c2e/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + } + break; + case static_cast(EM_HFeeType::kBCe_Be_SameB): + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/PV/b2c2e_b2e_sameb/uls/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/PV/b2c2e_b2e_sameb/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/PV/b2c2e_b2e_sameb/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + } + break; + case static_cast(EM_HFeeType::kBCe_Be_DiffB): + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/PV/b2c2e_b2e_diffb/uls/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 1) { // LS+diff + fRegistry.fill(HIST("Pair/PV/b2c2e_b2e_diffb/lspp/hs"), v12.M(), v12.Pt(), pair_dca); + } else if constexpr (signType == 2) { // LS-diff + fRegistry.fill(HIST("Pair/PV/b2c2e_b2e_diffb/lsmm/hs"), v12.M(), v12.Pt(), pair_dca); + } + break; + default: + break; + } // end of switch for HFee + } // end of HFee + } + + template + o2::dataformats::VertexBase refitPV(TCollision const& collision, std::vector const& vecPvContributorTrackParCov, std::vector const& vecPvContributorGlobalId, std::vector const& tracksToRemove) + { + std::vector vecPvRefitContributorUsed(vecPvContributorGlobalId.size(), true); + + // build the VertexBase to initialize the vertexer + o2::dataformats::VertexBase primVtx; + primVtx.setX(collision.posX()); + primVtx.setY(collision.posY()); + primVtx.setZ(collision.posZ()); + primVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + + o2::vertexing::PVertexer vertexer; + o2::conf::ConfigurableParam::updateFromString("pvertexer.useMeanVertexConstraint=false"); // remove diamond constraint + o2::conf::ConfigurableParam::updateFromString("pvertexer.tukey=100."); + vertexer.init(); + const bool pvRefitDoable = vertexer.prepareVertexRefit(vecPvContributorTrackParCov, primVtx); + if (!pvRefitDoable) { + LOG(info) << "Not enough tracks accepted for the refit"; // this should not happen by definition, because nPV>=2 is required in the reconstruction. I analyze the reconstructed collisions in AO2D. + return primVtx; + } + + for (const auto& trackToRemove : tracksToRemove) { + const auto trackIterator = std::find(vecPvContributorGlobalId.begin(), vecPvContributorGlobalId.end(), trackToRemove.globalIndex()); // track global index + if (trackIterator != vecPvContributorGlobalId.end()) { + const int entry = std::distance(vecPvContributorGlobalId.begin(), trackIterator); // this track contributed to this collision: let's do the refit without it + vecPvRefitContributorUsed[entry] = false; // remove the track from the PV refitting + } + } - default: - break; - } // end of switch for HFee - } // end of HFee - } // end of isMC + const auto primVtxRefitted = vertexer.refitVertex(vecPvRefitContributorUsed, primVtx); // vertex refit + // LOG(info) << "refit for track with global index " << static_cast(trackToRemove.globalIndex()) << " " << primVtxRefitted.asString(); + // LOG(info) << "refitVertex: collision.globalIndex() = " << collision.globalIndex() << " , " << primVtxRefitted.asString(); + if (primVtxRefitted.getChi2() < 0) { + // LOG(info) << "---> (when removing tracks) Refitted vertex has bad chi2 = " << primVtxRefitted.getChi2(); + return primVtx; + } + + fRegistry.fill(HIST("Event/refitPV/remove/hNContrib"), collision.numContrib(), primVtxRefitted.getNContributors()); + fRegistry.fill(HIST("Event/refitPV/remove/hChi2"), collision.chi2(), primVtxRefitted.getChi2()); + fRegistry.fill(HIST("Event/refitPV/remove/hDeltaXvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getX() - primVtx.getX()); + fRegistry.fill(HIST("Event/refitPV/remove/hDeltaYvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getY() - primVtx.getY()); + fRegistry.fill(HIST("Event/refitPV/remove/hDeltaZvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getZ() - primVtx.getZ()); + + o2::dataformats::VertexBase primVtxBaseRecalc; + primVtxBaseRecalc.setX(primVtxRefitted.getX()); + primVtxBaseRecalc.setY(primVtxRefitted.getY()); + primVtxBaseRecalc.setZ(primVtxRefitted.getZ()); + primVtxBaseRecalc.setCov(primVtxRefitted.getSigmaX2(), primVtxRefitted.getSigmaXY(), primVtxRefitted.getSigmaY2(), primVtxRefitted.getSigmaXZ(), primVtxRefitted.getSigmaYZ(), primVtxRefitted.getSigmaZ2()); + + vecPvRefitContributorUsed.clear(); + vecPvRefitContributorUsed.shrink_to_fit(); + return primVtxRefitted; + // return primVtxBaseRecalc; } - template - void runSVFinder(TCollision const& collision, TTrack const& t1, TTrack const& t2, TMCParticles const& mcParticles) + template + // o2::dataformats::VertexBase refitPVFullByRemovingTracks(TCollision const& collision, std::vector const& vecPvContributorTrackParCov, std::vector const& vecPvContributorGlobalId, std::vector const& tracksToRemove) + o2::vertexing::PVertex refitPVFullByRemovingTracks(TCollision const& collision, std::vector const& vecPvContributorTrackParCov, std::vector const& vecPvContributorGlobalId, std::vector const& tracksToRemove) { - DielectronAtSV eeatsv; + std::vector vecPvRefitContributorUsed(vecPvContributorGlobalId.size(), true); + + // build the VertexBase to initialize the vertexer + o2::dataformats::VertexBase primVtx; + primVtx.setX(collision.posX()); + primVtx.setY(collision.posY()); + primVtx.setZ(collision.posZ()); + primVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + + o2::vertexing::PVertexer vertexer; + o2::conf::ConfigurableParam::updateFromString("pvertexer.useMeanVertexConstraint=false"); // remove diamond constraint + o2::conf::ConfigurableParam::updateFromString("pvertexer.tukey=100."); + vertexer.init(); + const bool pvRefitDoable = vertexer.prepareVertexRefit(vecPvContributorTrackParCov, primVtx); + if (!pvRefitDoable) { + LOG(info) << "Not enough tracks accepted for the refit"; // this should not happen by definition, because nPV>=2 is required in the reconstruction. I analyze the reconstructed collisions in AO2D. + // return primVtx; + } + + for (const auto& trackToRemove : tracksToRemove) { + const auto trackIterator = std::find(vecPvContributorGlobalId.begin(), vecPvContributorGlobalId.end(), trackToRemove.globalIndex()); // track global index + if (trackIterator != vecPvContributorGlobalId.end()) { + const int entry = std::distance(vecPvContributorGlobalId.begin(), trackIterator); // this track contributed to this collision: let's do the refit without it + vecPvRefitContributorUsed[entry] = false; // remove the track from the PV refitting + } + } + + const auto primVtxRefitted = vertexer.refitVertexFull(vecPvRefitContributorUsed, primVtx); // vertex refit + // LOG(info) << "refit for track with global index " << static_cast(trackToRemove.globalIndex()) << " " << primVtxRefitted.asString(); + // LOG(info) << "refitVertexFull: collision.globalIndex() = " << collision.globalIndex() << " , " << primVtxRefitted.asString(); + if (primVtxRefitted.getChi2() < 0) { + // LOG(info) << "---> (when removing tracks) Refitted vertex has bad chi2 = " << primVtxRefitted.getChi2(); + return primVtxRefitted; + } + + fRegistry.fill(HIST("Event/refitPV/remove/hNContrib"), collision.numContrib(), primVtxRefitted.getNContributors()); + fRegistry.fill(HIST("Event/refitPV/remove/hChi2"), collision.chi2(), primVtxRefitted.getChi2()); + fRegistry.fill(HIST("Event/refitPV/remove/hDeltaXvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getX() - primVtx.getX()); + fRegistry.fill(HIST("Event/refitPV/remove/hDeltaYvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getY() - primVtx.getY()); + fRegistry.fill(HIST("Event/refitPV/remove/hDeltaZvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getZ() - primVtx.getZ()); + + o2::dataformats::VertexBase primVtxBaseRecalc; + primVtxBaseRecalc.setX(primVtxRefitted.getX()); + primVtxBaseRecalc.setY(primVtxRefitted.getY()); + primVtxBaseRecalc.setZ(primVtxRefitted.getZ()); + primVtxBaseRecalc.setCov(primVtxRefitted.getSigmaX2(), primVtxRefitted.getSigmaXY(), primVtxRefitted.getSigmaY2(), primVtxRefitted.getSigmaXZ(), primVtxRefitted.getSigmaYZ(), primVtxRefitted.getSigmaZ2()); + + vecPvRefitContributorUsed.clear(); + vecPvRefitContributorUsed.shrink_to_fit(); + return primVtxRefitted; + // return primVtxBaseRecalc; + } + template + o2::dataformats::VertexBase refitPVFullByAddingTracks(TCollision const& collision, std::vector vecPvContributorTrackParCov, std::vector vecPvContributorGlobalId, std::vector const& tracksToAdd) + { + // build the VertexBase to initialize the vertexer + o2::dataformats::VertexBase primVtx; + primVtx.setX(collision.posX()); + primVtx.setY(collision.posY()); + primVtx.setZ(collision.posZ()); + primVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + + for (const auto& trackToAdd : tracksToAdd) { + if (!trackToAdd.isPVContributor() && trackToAdd.collisionId() == collision.globalIndex()) { // this track belongs to this collision, but not a PV contributor + vecPvContributorTrackParCov.emplace_back(getTrackParCov(trackToAdd)); + vecPvContributorGlobalId.emplace_back(trackToAdd.globalIndex()); + } + } + std::vector vecPvRefitContributorUsed(vecPvContributorGlobalId.size(), true); + + o2::vertexing::PVertexer vertexer; + o2::conf::ConfigurableParam::updateFromString("pvertexer.useMeanVertexConstraint=false"); // remove diamond constraint + o2::conf::ConfigurableParam::updateFromString("pvertexer.tukey=100."); + vertexer.init(); + const bool pvRefitDoable = vertexer.prepareVertexRefit(vecPvContributorTrackParCov, primVtx); + if (!pvRefitDoable) { + LOG(info) << "Not enough tracks accepted for the refit"; // this should not happen by definition, because nPV>=2 is required in the reconstruction. I analyze the reconstructed collisions in AO2D. + return primVtx; + } + + const auto primVtxRefitted = vertexer.refitVertexFull(vecPvRefitContributorUsed, primVtx); // vertex refit + // LOG(info) << "refit for track with global index " << static_cast(trackToAdd.globalIndex()) << " " << primVtxRefitted.asString(); + // LOG(info) << "refitVertexFull + adding a track: collision.globalIndex() = " << collision.globalIndex() << " , " << primVtxRefitted.asString(); + + if (primVtxRefitted.getChi2() < 0) { + // LOG(info) << "---> (when adding tracks) Refitted vertex has bad chi2 = " << primVtxRefitted.getChi2(); + return primVtx; + } + + fRegistry.fill(HIST("Event/refitPV/add/hNContrib"), collision.numContrib(), primVtxRefitted.getNContributors()); + fRegistry.fill(HIST("Event/refitPV/add/hChi2"), collision.chi2(), primVtxRefitted.getChi2()); + fRegistry.fill(HIST("Event/refitPV/add/hDeltaXvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getX() - primVtx.getX()); + fRegistry.fill(HIST("Event/refitPV/add/hDeltaYvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getY() - primVtx.getY()); + fRegistry.fill(HIST("Event/refitPV/add/hDeltaZvsNContrib"), primVtxRefitted.getNContributors(), primVtxRefitted.getZ() - primVtx.getZ()); + + o2::dataformats::VertexBase primVtxBaseRecalc; + primVtxBaseRecalc.setX(primVtxRefitted.getX()); + primVtxBaseRecalc.setY(primVtxRefitted.getY()); + primVtxBaseRecalc.setZ(primVtxRefitted.getZ()); + primVtxBaseRecalc.setCov(primVtxRefitted.getSigmaX2(), primVtxRefitted.getSigmaXY(), primVtxRefitted.getSigmaY2(), primVtxRefitted.getSigmaXZ(), primVtxRefitted.getSigmaYZ(), primVtxRefitted.getSigmaZ2()); + + vecPvRefitContributorUsed.clear(); + vecPvRefitContributorUsed.shrink_to_fit(); + return primVtxRefitted; + // return primVtxBaseRecalc; + } + + template + void runSVFinder(TCollision const& collision, TTrack const& t1, TTrack const& t2, TMCParticles const& mcParticles /*, std::vector const& vecPvContributorTrackParCov, std::vector const& vecPvContributorGlobalId*/) + { auto trackParCov1 = getTrackParCov(t1); trackParCov1.setPID(o2::track::PID::Electron); + mDcaInfoCov.set(999, 999, 999, 999, 999); + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov1, 2.f, matCorr, &mDcaInfoCov); + float dcaXY1 = mDcaInfoCov.getY(); + float dcaZ1 = mDcaInfoCov.getZ(); + float CYY1 = trackParCov1.getSigmaY2(); + float CZY1 = trackParCov1.getSigmaZY(); + float CZZ1 = trackParCov1.getSigmaZ2(); + float signed1Pt1 = trackParCov1.getQ2Pt(); // at PV + float eta1 = trackParCov1.getEta(); // at PV + float dca3DinSigma1 = dca3DinSigmaOTF(dcaXY1, dcaZ1, trackParCov1.getSigmaY2(), trackParCov1.getSigmaZ2(), trackParCov1.getSigmaZY()); + auto trackParCov2 = getTrackParCov(t2); trackParCov2.setPID(o2::track::PID::Electron); + mDcaInfoCov.set(999, 999, 999, 999, 999); + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov2, 2.f, matCorr, &mDcaInfoCov); + float dcaXY2 = mDcaInfoCov.getY(); + float dcaZ2 = mDcaInfoCov.getZ(); + float CYY2 = trackParCov2.getSigmaY2(); + float CZY2 = trackParCov2.getSigmaZY(); + float CZZ2 = trackParCov2.getSigmaZ2(); + float signed1Pt2 = trackParCov2.getQ2Pt(); // at PV + float eta2 = trackParCov2.getEta(); // at PV + float dca3DinSigma2 = dca3DinSigmaOTF(dcaXY2, dcaZ2, trackParCov2.getSigmaY2(), trackParCov2.getSigmaZ2(), trackParCov2.getSigmaZY()); + float pairDCA = pairDCAQuadSum(dca3DinSigma1, dca3DinSigma2); + + // auto refittedPV_remove_t1 = refitPVFullByRemovingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{t1}); + // mDcaInfoCov.set(999, 999, 999, 999, 999); + // o2::base::Propagator::Instance()->propagateToDCABxByBz(refittedPV_remove_t1, trackParCov1, 2.f, matCorr, &mDcaInfoCov); + // float dca3DinSigma1_remove_1track = dca3DinSigmaOTF(mDcaInfoCov.getY(), mDcaInfoCov.getZ(), trackParCov1.getSigmaY2(), trackParCov1.getSigmaZ2(), trackParCov1.getSigmaZY()); + // auto refittedPV_remove_t2 = refitPVFullByRemovingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{t2}); + // mDcaInfoCov.set(999, 999, 999, 999, 999); + // o2::base::Propagator::Instance()->propagateToDCABxByBz(refittedPV_remove_t2, trackParCov2, 2.f, matCorr, &mDcaInfoCov); + // float dca3DinSigma2_remove_1track = dca3DinSigmaOTF(mDcaInfoCov.getY(), mDcaInfoCov.getZ(), trackParCov2.getSigmaY2(), trackParCov2.getSigmaZ2(), trackParCov2.getSigmaZY()); + // float pairDCA_remove_1track = pairDCAQuadSum(dca3DinSigma1_remove_1track, dca3DinSigma2_remove_1track); + + // auto refittedPV_remove_t12 = refitPVFullByRemovingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{t1, t2}); + // mDcaInfoCov.set(999, 999, 999, 999, 999); + // o2::base::Propagator::Instance()->propagateToDCABxByBz(refittedPV_remove_t12, trackParCov1, 2.f, matCorr, &mDcaInfoCov); + // float dca3DinSigma1_remove_2track = dca3DinSigmaOTF(mDcaInfoCov.getY(), mDcaInfoCov.getZ(), trackParCov1.getSigmaY2(), trackParCov1.getSigmaZ2(), trackParCov1.getSigmaZY()); + // o2::base::Propagator::Instance()->propagateToDCABxByBz(refittedPV_remove_t12, trackParCov2, 2.f, matCorr, &mDcaInfoCov); + // float dca3DinSigma2_remove_2track = dca3DinSigmaOTF(mDcaInfoCov.getY(), mDcaInfoCov.getZ(), trackParCov2.getSigmaY2(), trackParCov2.getSigmaZ2(), trackParCov2.getSigmaZY()); + // float pairDCA_remove_2track = pairDCAQuadSum(dca3DinSigma1_remove_2track, dca3DinSigma2_remove_2track); std::array pVtx = {collision.posX(), collision.posY(), collision.posZ()}; std::array svpos = {0.}; // secondary vertex position @@ -601,7 +809,7 @@ struct studyDCAFitter { return; } - fitter.propagateTracksToVertex(); // propagate e and K to D vertex + fitter.propagateTracksToVertex(); // propagate ee to SV if (!fitter.isPropagateTracksToVertexDone()) { return; } @@ -614,287 +822,283 @@ struct studyDCAFitter { std::array pvecSum = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; float cpa = RecoDecay::cpa(pVtx, svpos, pvecSum); - float dca2legs = std::sqrt(fitter.getChi2AtPCACandidate()); - // float lxy = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2)); // in cm - float lz = std::fabs(svpos[2] - collision.posZ()); // in cm - - float meeAtSV = RecoDecay::m(std::array{pvec0, pvec1}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - float pteeAtSV = RecoDecay::sqrtSumOfSquares(pvecSum[0], pvecSum[1]); - float lxy = RecoDecay::dotProd(std::array{pvecSum[0], pvecSum[1]}, std::array{svpos[0] - collision.posX(), svpos[1] - collision.posY()}) / pteeAtSV; - float ppdl = lxy * 1e-2 * meeAtSV / pteeAtSV * 1e+3; // pseudo-proper decay length in mm - - if (cpa < svcut.cfg_min_cospa || svcut.cfg_max_dca2legs < dca2legs) { + float cpaXY = RecoDecay::cpaXY(pVtx, svpos, pvecSum); + float cpaRZ = RecoDecay::cpaRZ(pVtx, svpos, pvecSum); + float chi2PCA = fitter.getChi2AtPCACandidate(); + if (svCut.cfg_max_chi2PCA < chi2PCA) { return; } - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/Data/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/Data/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/Data/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/Data/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/Data/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/Data/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/Data/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/Data/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/Data/lsmm/hDCA2Legs"), dca2legs); - } - - if constexpr (isMC) { - const auto& t1mc = t1.template mcParticle_as(); - const auto& t2mc = t2.template mcParticle_as(); - if (std::abs(t1mc.pdgCode()) != 11) { - return; - } - if (!(t1mc.isPhysicalPrimary() || t1mc.producedByGenerator())) { - return; - } - if (std::abs(t2mc.pdgCode()) != 11) { - return; - } - if (!(t2mc.isPhysicalPrimary() || t2mc.producedByGenerator())) { - return; - } - // const auto& mp1 = t1mc.template mothers_first_as(); // mother particle of t1 - // const auto& mp2 = t2mc.template mothers_first_as(); // mother particle of t2 - int mcCommonMotherid = FindLF(t1mc, t2mc, mcParticles); - int hfee_type = IsHF(t1mc, t2mc, mcParticles); - - if (mcCommonMotherid > -1) { - const auto cmp = mcParticles.rawIteratorAt(mcCommonMotherid); - switch (cmp.pdgCode()) { - case 223: - if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptOmega/lsmm/hDCA2Legs"), dca2legs); - } - } else { // nonprompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptOmega/lsmm/hDCA2Legs"), dca2legs); - } - } - break; - case 333: - if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptPhi/lsmm/hDCA2Legs"), dca2legs); - } - } else { // nonprompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptPhi/lsmm/hDCA2Legs"), dca2legs); - } - } - break; - case 443: - if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/PromptJpsi/lsmm/hDCA2Legs"), dca2legs); - } - } else { // nonprompt - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/NonPromptJpsi/lsmm/hDCA2Legs"), dca2legs); - } - } - break; - default: - break; - } // end of switch for LF - } else if (hfee_type > -1) { - switch (hfee_type) { - case static_cast(EM_HFeeType::kCe_Ce): - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/c2e_c2e/lsmm/hDCA2Legs"), dca2legs); - } - break; - case static_cast(EM_HFeeType::kBe_Be): - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2e_b2e/lsmm/hDCA2Legs"), dca2legs); - } - break; - case static_cast(EM_HFeeType::kBCe_BCe): + float lxy = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2)); // in cm + float lz = svpos[2] - collision.posZ(); // in cm + float lxyz = std::sqrt(std::pow(lxy, 2) + std::pow(lz, 2)); + + auto primaryVertex = getPrimaryVertex(collision); + std::array covVtx = fitter.calcPCACovMatrixFlat(); + double phi{}, theta{}; + getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, svpos, phi, theta); + float lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, 0.) + getRotatedCovMatrixXX(covVtx, phi, 0.)); + float lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, theta) + getRotatedCovMatrixXX(covVtx, 0, theta)); + float lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phi, theta) + getRotatedCovMatrixXX(covVtx, phi, theta)); + + float meeAtSV = RecoDecay::m(std::array{pvec0, pvec1}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); + float pteeAtSV = RecoDecay::sqrtSumOfSquares(pvecSum[0], pvecSum[1]); + float yeeAtSV = RecoDecay::y(pvecSum, meeAtSV); + + auto t1mc = t1.template mcParticle_as(); // true lepton + auto t2mc = t2.template mcParticle_as(); // true lepton + bool isCorrectCollision1 = t1mc.mcCollisionId() == collision.mcCollisionId(); + bool isCorrectCollision2 = t2mc.mcCollisionId() == collision.mcCollisionId(); + bool isReassociated1 = !(t1.collisionId() == collision.globalIndex()); + bool isReassociated2 = !(t2.collisionId() == collision.globalIndex()); + int pdgCodeMother1 = 0; + int pdgCodeMother2 = 0; + + int mcCommonMotherId = FindCommonMother(t1mc, t2mc, mcParticles); + int hfee_type = IsHF(t1mc, t2mc, mcParticles); + bool keepSignal = false; + uint8_t dileptonType = 0; + + if (mcCommonMotherId > -1) { + auto cmp = mcParticles.rawIteratorAt(mcCommonMotherId); + pdgCodeMother1 = cmp.pdgCode(); + pdgCodeMother2 = cmp.pdgCode(); + switch (std::abs(cmp.pdgCode())) { + case 23: + keepSignal = true; + dileptonType = 1; + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/SV/Zboson/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/Zboson/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/Zboson/uls/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/SV/Zboson/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/Zboson/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/Zboson/lspp/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/SV/Zboson/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/Zboson/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/Zboson/lsmm/hChi2PCA"), std::log10(chi2PCA)); + } + break; + case 443: + keepSignal = true; + if (IsFromCharm(cmp, mcParticles) < 0 && IsFromBeauty(cmp, mcParticles) < 0) { // prompt + dileptonType = 1; if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/uls/hDCA2Legs"), dca2legs); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/uls/hChi2PCA"), std::log10(chi2PCA)); } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/lspp/hDCA2Legs"), dca2legs); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/lspp/hChi2PCA"), std::log10(chi2PCA)); } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2c2e/lsmm/hDCA2Legs"), dca2legs); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/PromptJpsi/lsmm/hChi2PCA"), std::log10(chi2PCA)); } - break; - case static_cast(EM_HFeeType::kBCe_Be_SameB): + } else { // nonprompt + dileptonType = 2; if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/uls/hDCA2Legs"), dca2legs); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/uls/hChi2PCA"), std::log10(chi2PCA)); } else if constexpr (signType == 1) { // LS++ - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/lspp/hDCA2Legs"), dca2legs); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/lspp/hChi2PCA"), std::log10(chi2PCA)); } else if constexpr (signType == 2) { // LS-- - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_sameb/lsmm/hDCA2Legs"), dca2legs); - } - break; - case static_cast(EM_HFeeType::kBCe_Be_DiffB): - if constexpr (signType == 0) { // ULS - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/uls/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/uls/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/uls/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 1) { // LS+diff - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/lspp/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/lspp/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/lspp/hDCA2Legs"), dca2legs); - } else if constexpr (signType == 2) { // LS-diff - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/lsmm/hs"), meeAtSV, pteeAtSV, ppdl); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/lsmm/hCosPA"), cpa); - fRegistry.fill(HIST("Pair/SV/MC/b2c2e_b2e_diffb/lsmm/hDCA2Legs"), dca2legs); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/NonPromptJpsi/lsmm/hChi2PCA"), std::log10(chi2PCA)); } - break; - - default: - break; - } // end of switch for HFee - } // end of HFee - } // end of isMC - - eeatsv.isfound = true; - eeatsv.mass = meeAtSV; - eeatsv.pt = pteeAtSV; - eeatsv.cospa = cpa; - eeatsv.dca2legs = dca2legs; - eeatsv.lxy = lxy; - eeatsv.lz = lz; - return; + } + break; + default: + keepSignal = false; + break; + } // end of switch for LF + } else if (hfee_type > -1) { + keepSignal = true; + auto t1mcMother = t1mc.template mothers_first_as(); + auto t2mcMother = t2mc.template mothers_first_as(); + pdgCodeMother1 = t1mcMother.pdgCode(); + pdgCodeMother2 = t2mcMother.pdgCode(); + switch (hfee_type) { + case static_cast(EM_HFeeType::kCe_Ce): + dileptonType = 3; + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/SV/c2e_c2e/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/c2e_c2e/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/c2e_c2e/uls/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/SV/c2e_c2e/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/c2e_c2e/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/c2e_c2e/lspp/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/SV/c2e_c2e/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/c2e_c2e/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/c2e_c2e/lsmm/hChi2PCA"), std::log10(chi2PCA)); + } + break; + case static_cast(EM_HFeeType::kBe_Be): + dileptonType = 4; + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/SV/b2e_b2e/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2e_b2e/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2e_b2e/uls/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/SV/b2e_b2e/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2e_b2e/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2e_b2e/lspp/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/SV/b2e_b2e/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2e_b2e/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2e_b2e/lsmm/hChi2PCA"), std::log10(chi2PCA)); + } + break; + case static_cast(EM_HFeeType::kBCe_BCe): + dileptonType = 5; + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/uls/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/lspp/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2c2e/lsmm/hChi2PCA"), std::log10(chi2PCA)); + } + break; + case static_cast(EM_HFeeType::kBCe_Be_SameB): + dileptonType = 6; + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/uls/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 1) { // LS++ + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/lspp/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 2) { // LS-- + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_sameb/lsmm/hChi2PCA"), std::log10(chi2PCA)); + } + break; + case static_cast(EM_HFeeType::kBCe_Be_DiffB): + dileptonType = 7; + if constexpr (signType == 0) { // ULS + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/uls/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/uls/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/uls/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 1) { // LS+diff + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/lspp/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/lspp/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/lspp/hChi2PCA"), std::log10(chi2PCA)); + } else if constexpr (signType == 2) { // LS-diff + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/lsmm/hs"), meeAtSV, pteeAtSV, pairDCA, cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/lsmm/hCosPA"), cpa); + fRegistry.fill(HIST("Pair/SV/b2c2e_b2e_diffb/lsmm/hChi2PCA"), std::log10(chi2PCA)); + } + break; + default: + break; + } // end of switch for HFee + } else { + keepSignal = true; + dileptonType = 0; // bkg + auto t1mcMother = t1mc.template mothers_first_as(); + auto t2mcMother = t2mc.template mothers_first_as(); + pdgCodeMother1 = t1mcMother.pdgCode(); + pdgCodeMother2 = t2mcMother.pdgCode(); + } + + if (keepSignal) { + if (dileptonType == 0 && dist01(engine) > cfgDownSampling) { // random sampling, if necessary + return; + } + + dileptonTable(eventTable.lastIndex() + 1, + signed1Pt1, eta1, dcaXY1, dcaZ1, CYY1, CZY1, CZZ1, isCorrectCollision1, isReassociated1, pdgCodeMother1, + signed1Pt2, eta2, dcaXY2, dcaZ2, CYY2, CZY2, CZZ2, isCorrectCollision2, isReassociated2, pdgCodeMother2, + meeAtSV, pteeAtSV, yeeAtSV, + chi2PCA, + cpa, cpaXY, cpaRZ, + lxy, lz, lxyz, + lxyErr, lzErr, lxyzErr, + dileptonType); + } } - template + template void run(TBCs const&, TCollisions const& collisions, TTracks const& tracks, TTrackAssoc const& trackIndices, TMCCollisions const&, TMCParticles const& mcParticles) { - used_electronIds.reserve(tracks.size()); - for (const auto& collision : collisions) { - const auto& bc = collision.template foundBC_as(); + if (!collision.has_mcCollision()) { + continue; + } + + auto mcCollision_from_collision = collision.template mcCollision_as(); + if (mcCollision_from_collision.getSubGeneratorId() == eventCut.cfgRejectEventGenerator) { + continue; + } + + auto bc = collision.template bc_as(); initCCDB(bc); fRegistry.fill(HIST("Event/hCollisionCounter"), 0); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { + if (!isSelectedCollision(collision)) { + continue; + } + + float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[eventCut.cfgCentEstimator] < eventCut.cfgCentMin || eventCut.cfgCentMax < centralities[eventCut.cfgCentEstimator]) { continue; } fRegistry.fill(HIST("Event/hCollisionCounter"), 1); fillEventHistograms(collision); - const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + std::vector electronIds; + std::vector positronIds; electronIds.reserve(trackIdsThisCollision.size()); positronIds.reserve(trackIdsThisCollision.size()); mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); for (const auto& trackId : trackIdsThisCollision) { - const auto& track = trackId.template track_as(); + auto track = trackId.template track_as(); if (!track.hasITS() || !track.hasTPC()) { continue; } - if constexpr (isMC) { - if (!track.has_mcParticle()) { - continue; - } - const auto& mctrack = track.template mcParticle_as(); - const auto& mcCollision = mctrack.template mcCollision_as(); - if (cfgEventGeneratorType >= 0 && mcCollision.getSubGeneratorId() != cfgEventGeneratorType) { - continue; - } - if (!mctrack.has_mothers()) { - continue; - } + if (!track.has_mcParticle()) { + continue; + } + auto mctrack = track.template mcParticle_as(); + auto mcCollision = mctrack.template mcCollision_as(); + if (mcCollision.getSubGeneratorId() == eventCut.cfgRejectEventGenerator) { + continue; + } + + if (!mctrack.has_mothers()) { + continue; + } + if (std::abs(mctrack.pdgCode()) != 11) { + continue; + } + if (!(mctrack.isPhysicalPrimary() || mctrack.producedByGenerator())) { + continue; + } + + auto mcMother = mctrack.template mothers_first_as(); + if (std::abs(mcMother.pdgCode()) > 1e+9) { + continue; } mDcaInfoCov.set(999, 999, 999, 999, 999); @@ -904,76 +1108,108 @@ struct studyDCAFitter { float dcaXY = mDcaInfoCov.getY(); float dcaZ = mDcaInfoCov.getZ(); - if (isSelectedTrack(track, trackParCov, dcaXY, dcaZ) && isElectron(track)) { + if (isSelectedTrack(track, trackParCov, dcaXY, dcaZ)) { if (track.sign() > 0) { // positron - positronIds.emplace_back(trackId.trackId()); + positronIds.emplace_back(track.globalIndex()); } else { // electron - electronIds.emplace_back(trackId.trackId()); + electronIds.emplace_back(track.globalIndex()); } } } // end of track loop for electron selection + // auto pvContributors_per_collision = pvContributors->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + // std::vector vecPvContributorTrackParCov; + // std::vector vecPvContributorGlobalId; + // vecPvContributorTrackParCov.reserve(pvContributors_per_collision.size()); + // vecPvContributorGlobalId.reserve(pvContributors_per_collision.size()); + + // for (const auto& track : pvContributors_per_collision) { + // vecPvContributorGlobalId.push_back(track.globalIndex()); + // vecPvContributorTrackParCov.push_back(getTrackParCov(track)); + // } + + // auto refittedPVFull_wo_modification = refitPVFullByRemovingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{}); + // for (const auto& posId : positronIds) { + // auto pos = tracks.rawIteratorAt(posId); + // refitPVFullByAddingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{pos}); + // } + // for (const auto& eleId : electronIds) { + // auto ele = tracks.rawIteratorAt(eleId); + // refitPVFullByAddingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{ele}); + // } + for (const auto& posId : positronIds) { - const auto& pos = tracks.rawIteratorAt(posId); + auto pos = tracks.rawIteratorAt(posId); + // auto refittedPV = refitPVFullByRemovingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{pos}); + fillElectronHistograms(collision, pos, mcParticles); + } + for (const auto& eleId : electronIds) { + auto ele = tracks.rawIteratorAt(eleId); + // auto refittedPV = refitPVFullByRemovingTracks(collision, vecPvContributorTrackParCov, vecPvContributorGlobalId, std::vector{ele}); + fillElectronHistograms(collision, ele, mcParticles); + } + + for (const auto& posId : positronIds) { + auto pos = tracks.rawIteratorAt(posId); for (const auto& eleId : electronIds) { - const auto& ele = tracks.rawIteratorAt(eleId); - runSVFinder(collision, pos, ele, mcParticles); - runPairingAtPV(pos, ele, mcParticles); + auto ele = tracks.rawIteratorAt(eleId); + runSVFinder<0>(collision, pos, ele, mcParticles); + runPairingAtPV<0>(pos, ele, mcParticles); } // end of electron loop } // end of positron loop - for (const auto& posId1 : positronIds) { - const auto& pos1 = tracks.rawIteratorAt(posId1); - for (const auto& posId2 : positronIds) { - const auto& pos2 = tracks.rawIteratorAt(posId2); - if (pos1.globalIndex() == pos2.globalIndex()) { - continue; - } - runSVFinder(collision, pos1, pos2, mcParticles); - runPairingAtPV(pos1, pos2, mcParticles); + for (size_t i = 0; i < positronIds.size(); i++) { + auto pos1 = tracks.rawIteratorAt(positronIds[i]); + for (size_t j = i + 1; j < positronIds.size(); j++) { + auto pos2 = tracks.rawIteratorAt(positronIds[j]); + runSVFinder<1>(collision, pos1, pos2, mcParticles); + runPairingAtPV<1>(pos1, pos2, mcParticles); } // end of positron loop } // end of positron loop - for (const auto& eleId1 : electronIds) { - const auto& ele1 = tracks.rawIteratorAt(eleId1); - for (const auto& eleId2 : electronIds) { - const auto& ele2 = tracks.rawIteratorAt(eleId2); - if (ele1.globalIndex() == ele2.globalIndex()) { - continue; - } - runSVFinder(collision, ele1, ele2, mcParticles); - runPairingAtPV(ele1, ele2, mcParticles); + for (size_t i = 0; i < electronIds.size(); i++) { + auto ele1 = tracks.rawIteratorAt(electronIds[i]); + for (size_t j = i + 1; j < electronIds.size(); j++) { + auto ele2 = tracks.rawIteratorAt(electronIds[j]); + runSVFinder<2>(collision, ele1, ele2, mcParticles); + runPairingAtPV<2>(ele1, ele2, mcParticles); } // end of electron loop } // end of electron loop + if (positronIds.size() + electronIds.size() > 1) { // fill event table if at least 1 dilepton exists. + eventTable(collision.numContrib(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), 0); + } + electronIds.clear(); electronIds.shrink_to_fit(); positronIds.clear(); positronIds.shrink_to_fit(); - } // end of collision loop - used_electronIds.clear(); - used_electronIds.shrink_to_fit(); + // vecPvContributorGlobalId.clear(); + // vecPvContributorTrackParCov.clear(); + + // vecPvContributorGlobalId.shrink_to_fit(); + // vecPvContributorTrackParCov.shrink_to_fit(); + + } // end of collision loop } SliceCache cache; Preslice perCol = o2::aod::track::collisionId; - Filter collisionFilter_evsel = o2::aod::evsel::sel8 == true && (cfgZvtxMin < o2::aod::collision::posZ && o2::aod::collision::posZ < cfgZvtxMax); - Filter collisionFilter_centrality = (cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < cfgCentMax); + Filter collisionFilter_evsel = eventCut.cfgZvtxMin < o2::aod::collision::posZ && o2::aod::collision::posZ < eventCut.cfgZvtxMax; + Filter collisionFilter_centrality = (eventCut.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventCut.cfgCentMax) || (eventCut.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventCut.cfgCentMax) || (eventCut.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventCut.cfgCentMax); using FilteredMyCollisions = soa::Filtered; + // Partition pvContributors = ((o2::aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); + Preslice trackIndicesPerCollision = aod::track_association::collisionId; // Partition posTracks = o2::aod::track::signed1Pt > 0.f; // Partition negTracks = o2::aod::track::signed1Pt < 0.f; - std::vector electronIds; - std::vector positronIds; - std::vector> used_electronIds; // pair of hTypeId and electronId - - void processMC(FilteredMyCollisions const& collisions, aod::BCsWithTimestamps const& bcs, MyTracks const& tracks, aod::TrackAssoc const& trackIndices, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) + void processMC(FilteredMyCollisions const& collisions, MyBCs const& bcs, MyTracks const& tracks, aod::TrackAssoc const& trackIndices, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) { - run(bcs, collisions, tracks, trackIndices, mcCollisions, mcParticles); + run(bcs, collisions, tracks, trackIndices, mcCollisions, mcParticles); } PROCESS_SWITCH(studyDCAFitter, processMC, "processMC", true); }; diff --git a/PWGEM/Dilepton/Tasks/tableReaderBarrel.cxx b/PWGEM/Dilepton/Tasks/tableReaderBarrel.cxx index b4735bd0899..46cfe0d6e90 100644 --- a/PWGEM/Dilepton/Tasks/tableReaderBarrel.cxx +++ b/PWGEM/Dilepton/Tasks/tableReaderBarrel.cxx @@ -91,8 +91,8 @@ using MyEventsVtxCovSelectedQvector = soa::Join; using MyEventsQvector = soa::Join; using MyEventsQvectorMultExtra = soa::Join; -using MyEventsQvectorCentr = soa::Join; -using MyEventsQvectorCentrMultExtra = soa::Join; +using MyEventsQvectorCentr = soa::Join; +using MyEventsQvectorCentrMultExtra = soa::Join; using MyEventsHashSelectedQvector = soa::Join; // using MyEventsHashSelectedQvectorCentr = soa::Join; @@ -119,7 +119,7 @@ constexpr static uint32_t gkTrackFillMapWithColl = VarManager::ObjTypes::Reduced constexpr static int pairTypeEE = VarManager::kDecayToEE; // Global function used to define needed histogram classes -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar); // defines histograms for all tasks +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar); // defines histograms for all tasks struct AnalysisEventSelection { Produces eventSel; @@ -1235,7 +1235,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) adaptAnalysisTask(cfgc)}; } -void DefineHistograms(HistogramManager* histMan, TString histClasses, Configurable configVar) +void DefineHistograms(HistogramManager* histMan, const TString& histClasses, const Configurable& configVar) { // // Define here the histograms for all the classes required in analysis. diff --git a/PWGEM/Dilepton/Tasks/taggingHFE.cxx b/PWGEM/Dilepton/Tasks/taggingHFE.cxx index 0b5df3eddc7..27bf830e4f5 100644 --- a/PWGEM/Dilepton/Tasks/taggingHFE.cxx +++ b/PWGEM/Dilepton/Tasks/taggingHFE.cxx @@ -13,10 +13,14 @@ /// \brief a task to study tagging e from charm hadron decays in MC /// \author daiki.sekihata@cern.ch +#include "PWGEM/Dilepton/DataModel/lmeeMLTables.h" #include "PWGEM/Dilepton/Utils/MCUtilities.h" #include "PWGEM/Dilepton/Utils/SemiCharmTag.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/PID/PIDTOFParamService.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" @@ -42,20 +46,24 @@ #include #include #include +#include #include #include #include +#include -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include #include +#include #include #include #include #include +#include +#include #include #include +#include #include #include @@ -69,28 +77,25 @@ using namespace o2::constants::physics; using namespace o2::aod::pwgem::dilepton::utils::mcutil; struct taggingHFE { + using MyBCs = soa::Join; + using MyBC = MyBCs::iterator; + using MyCollisions = soa::Join; using MyCollisionsWithMCLabel = soa::Join; using MyTracks = soa::Join; + aod::pidTOFFullEl, aod::pidTOFFullPi, aod::pidTOFFullKa, aod::pidTOFFullPr, aod::pidTOFbeta, aod::TOFSignal, aod::TOFEvTime>; using MyTracksWithMCLabel = soa::Join; - // using MyV0s = soa::Join; - // using MyCascades = soa::Join; using MyV0s = soa::Join; using MyCascades = soa::Join; - struct EBPair { // electron-baryon pair - float mass{-999.f}; - float dca2legs{-999.f}; - float cospa{-999.f}; - float lxy{-999.f}; - float lz{-999.f}; - float ptepv{-999.f}; - float dca3dinsigma{-999.f}; - }; + Produces eventTable; + Produces leptonTable; + Produces emmlltpair; + Produces emmllv0pair; + Produces emmllcascpair; // Configurables Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -101,6 +106,9 @@ struct taggingHFE { Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; Configurable cfgPdgLepton{"cfgPdgLepton", 11, "pdg code of desired lepton: 11 or 13"}; + Configurable cfgDownSampling{"cfgDownSampling", 1.1, "down sampling for wrongly found SV"}; + Configurable useTOFNSigmaDeltaBC{"useTOFNSigmaDeltaBC", true, "Flag to shift delta BC for TOF n sigma (with TTCA and for V0s)"}; + // Configurable storeHFeWithRandomHadron{"storeHFeWithRandomHadron", false, "Flag to store random lh pairs also for HFl"}; struct : ConfigurableGroup { std::string prefix = "dcaFitterGroup_eK"; @@ -122,10 +130,10 @@ struct taggingHFE { struct : ConfigurableGroup { std::string prefix = "electronCut"; - Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.4, "min pT for single track"}; + Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.1, "min pT for single track"}; Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; - Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.5, "min eta for single track"}; - Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.5, "max eta for single track"}; + Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.8, "min eta for single track"}; + Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.8, "max eta for single track"}; Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.8, "min. TPC Ncr/Nf ratio"}; Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 0.7, "max fraction of shared clusters in TPC"}; Configurable cfg_min_ncrossedrows_tpc{"cfg_min_ncrossedrows_tpc", 80, "min ncrossed rows"}; @@ -143,13 +151,13 @@ struct taggingHFE { } electronCut; struct : ConfigurableGroup { - std::string prefix = "kaonCut"; + std::string prefix = "hadronCut"; Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.01, "min pT for single track"}; Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.9, "min eta for single track"}; Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.9, "max eta for single track"}; - Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.8, "min. TPC Ncr/Nf ratio"}; - Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 0.7, "max fraction of shared clusters in TPC"}; + Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.f, "min. TPC Ncr/Nf ratio"}; + Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; Configurable cfg_min_ncrossedrows_tpc{"cfg_min_ncrossedrows_tpc", 70, "min ncrossed rows"}; Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 4, "min ncluster its"}; @@ -158,112 +166,147 @@ struct taggingHFE { Configurable cfg_max_chi2its{"cfg_max_chi2its", 36.0, "max chi2/NclsITS"}; Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; - Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -2, "min n sigma ka in TPC"}; - Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +2, "max n sigma ka in TPC"}; - Configurable cfg_min_TOFNsigmaKa{"cfg_min_TOFNsigmaKa", -2, "min n sigma ka in TOF"}; - Configurable cfg_max_TOFNsigmaKa{"cfg_max_TOFNsigmaKa", +2, "max n sigma ka in TOF"}; - Configurable requireTOF{"requireTOF", true, "require TOF hit"}; - Configurable cfg_min_pin_TOFreq{"cfg_min_pin_TOFreq", 0.4, "min pin for TOFreq"}; - } kaonCut; + Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -3, "min n sigma pi in TPC"}; + Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3, "max n sigma pi in TPC"}; + Configurable cfg_min_TOFNsigmaPi{"cfg_min_TOFNsigmaPi", -3, "min n sigma pi in TOF"}; + Configurable cfg_max_TOFNsigmaPi{"cfg_max_TOFNsigmaPi", +3, "max n sigma pi in TOF"}; + Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3, "min n sigma ka in TPC"}; + Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3, "max n sigma ka in TPC"}; + Configurable cfg_min_TOFNsigmaKa{"cfg_min_TOFNsigmaKa", -3, "min n sigma ka in TOF"}; + Configurable cfg_max_TOFNsigmaKa{"cfg_max_TOFNsigmaKa", +3, "max n sigma ka in TOF"}; + // Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3, "min n sigma pr in TPC"}; + // Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3, "max n sigma pr in TPC"}; + // Configurable cfg_min_TOFNsigmaPr{"cfg_min_TOFNsigmaPr", -3, "min n sigma pr in TOF"}; + // Configurable cfg_max_TOFNsigmaPr{"cfg_max_TOFNsigmaPr", +3, "max n sigma pr in TOF"}; + Configurable requirePiKa{"requirePiKa", true, "require hadron to be pion or kaon or proton"}; + Configurable applyTOFif{"applyTOFif", false, "apply TOFif for hadron identification"}; + } hadronCut; struct : ConfigurableGroup { std::string prefix = "v0Cut"; - Configurable cfg_min_mass_k0s{"cfg_min_mass_k0s", 0.49, "min mass for K0S"}; + Configurable cfg_min_mass_k0s{"cfg_min_mass_k0s", 0.48, "min mass for K0S"}; Configurable cfg_max_mass_k0s{"cfg_max_mass_k0s", 0.51, "max mass for K0S"}; - Configurable cfg_min_mass_k0s_veto{"cfg_min_mass_k0s_veto", 0.47, "min mass for K0S veto for Lambda"}; - Configurable cfg_max_mass_k0s_veto{"cfg_max_mass_k0s_veto", 0.52, "max mass for K0S veto for Lambda"}; - Configurable cfg_min_mass_lambda{"cfg_min_mass_lambda", 1.113, "min mass for Lambda"}; - Configurable cfg_max_mass_lambda{"cfg_max_mass_lambda", 1.118, "max mass for Lambda"}; - Configurable cfg_min_cospa{"cfg_min_cospa", 0.9, "min cospa for v0"}; + Configurable cfg_min_mass_k0s_veto{"cfg_min_mass_k0s_veto", 0.48, "min mass for K0S veto for Lambda"}; + Configurable cfg_max_mass_k0s_veto{"cfg_max_mass_k0s_veto", 0.51, "max mass for K0S veto for Lambda"}; + Configurable cfg_min_mass_lambda{"cfg_min_mass_lambda", 1.11, "min mass for Lambda"}; + Configurable cfg_max_mass_lambda{"cfg_max_mass_lambda", 1.12, "max mass for Lambda"}; + Configurable cfg_min_mass_lambda_veto{"cfg_min_mass_lambda_veto", 1.11, "min mass for Lambda veto for K0S"}; + Configurable cfg_max_mass_lambda_veto{"cfg_max_mass_lambda_veto", 1.12, "max mass for Lambda veto for K0S"}; + Configurable cfg_min_cospa{"cfg_min_cospa", 0.95, "min cospa for v0"}; Configurable cfg_max_dca2legs{"cfg_max_dca2legs", 0.1, "max distance between 2 legs for v0"}; - // Configurable cfg_min_radius{"cfg_min_radius", 0.1, "min rxy for v"}; - Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.8, "min. TPC Ncr/Nf ratio"}; - Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 0.7, "max fraction of shared clusters in TPC"}; + Configurable cfg_min_radius{"cfg_min_radius", 0.1, "min rxy for v"}; + Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.f, "min. TPC Ncr/Nf ratio"}; + Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; Configurable cfg_min_ncrossedrows_tpc{"cfg_min_ncrossedrows_tpc", 70, "min ncrossed rows"}; Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; Configurable cfg_max_chi2its{"cfg_max_chi2its", 36.0, "max chi2/NclsITS"}; Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 2, "min ncluster its"}; Configurable cfg_min_ncluster_itsib{"cfg_min_ncluster_itsib", 0, "min ncluster itsib"}; + Configurable cfg_itsib_type{"cfg_itsib_type", 0, "0:free, 1:OR, 2:AND between 2 legs, else:free"}; Configurable cfg_min_dcaxy{"cfg_min_dcaxy", 0.1, "min dca XY for v0 legs in cm"}; - - Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -2, "min n sigma pi in TPC"}; - Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +2, "max n sigma pi in TPC"}; - Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -2, "min n sigma pr in TPC"}; - Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +2, "max n sigma pr in TPC"}; - Configurable cfg_min_TOFNsigmaPi{"cfg_min_TOFNsigmaPi", -2, "min n sigma pi in TOF"}; - Configurable cfg_max_TOFNsigmaPi{"cfg_max_TOFNsigmaPi", +2, "max n sigma pi in TOF"}; - Configurable cfg_min_TOFNsigmaPr{"cfg_min_TOFNsigmaPr", -2, "min n sigma pr in TOF"}; - Configurable cfg_max_TOFNsigmaPr{"cfg_max_TOFNsigmaPr", +2, "max n sigma pr in TOF"}; + Configurable cfg_max_alpha_veto{"cfg_max_alpha_veto", 0.95, "max alpha for photon conversion rejection"}; + Configurable cfg_max_qt_veto{"cfg_max_qt_veto", 0.01, "max qT for photon conversion rejection"}; + + // for both v0 and cascade + Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -3, "min n sigma pi in TPC"}; + Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3, "max n sigma pi in TPC"}; + Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3, "min n sigma ka in TPC"}; + Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3, "max n sigma ka in TPC"}; + Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3, "min n sigma pr in TPC"}; + Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3, "max n sigma pr in TPC"}; + Configurable cfg_min_TOFNsigmaPi{"cfg_min_TOFNsigmaPi", -3, "min n sigma pi in TOF"}; + Configurable cfg_max_TOFNsigmaPi{"cfg_max_TOFNsigmaPi", +3, "max n sigma pi in TOF"}; + Configurable cfg_min_TOFNsigmaKa{"cfg_min_TOFNsigmaKa", -3, "min n sigma ka in TOF"}; + Configurable cfg_max_TOFNsigmaKa{"cfg_max_TOFNsigmaKa", +3, "max n sigma ka in TOF"}; + Configurable cfg_min_TOFNsigmaPr{"cfg_min_TOFNsigmaPr", -3, "min n sigma pr in TOF"}; + Configurable cfg_max_TOFNsigmaPr{"cfg_max_TOFNsigmaPr", +3, "max n sigma pr in TOF"}; + Configurable applyTOFif{"applyTOFif", false, "apply TOFif for hadron identification"}; } v0Cut; struct : ConfigurableGroup { std::string prefix = "cascadeCut"; Configurable cfg_min_mass_lambda{"cfg_min_mass_lambda", 1.11, "min mass for lambda in cascade"}; Configurable cfg_max_mass_lambda{"cfg_max_mass_lambda", 1.12, "max mass for lambda in cascade"}; - Configurable cfg_min_mass_Xi{"cfg_min_mass_Xi", 1.316, "min mass for Xi"}; - Configurable cfg_max_mass_Xi{"cfg_max_mass_Xi", 1.326, "max mass for Xi"}; + Configurable cfg_min_mass_Xi{"cfg_min_mass_Xi", 1.314, "min mass for Xi"}; + Configurable cfg_max_mass_Xi{"cfg_max_mass_Xi", 1.328, "max mass for Xi"}; Configurable cfg_min_mass_Xi_veto{"cfg_min_mass_Xi_veto", 1.31, "min mass for Xi veto"}; Configurable cfg_max_mass_Xi_veto{"cfg_max_mass_Xi_veto", 1.33, "max mass for Xi veto"}; - Configurable cfg_min_mass_Omega{"cfg_min_mass_Omega", 1.669, "min mass for Omega"}; - Configurable cfg_max_mass_Omega{"cfg_max_mass_Omega", 1.675, "max mass for Omega"}; - Configurable cfg_min_mass_Omega_veto{"cfg_min_mass_Omega_veto", 1.66, "min mass for Omega veto"}; - Configurable cfg_max_mass_Omega_veto{"cfg_max_mass_Omega_veto", 1.68, "max mass for Omega veto"}; - Configurable cfg_min_cospa_v0{"cfg_min_cospa_v0", 0.9, "minimum V0 CosPA in cascade"}; + Configurable cfg_min_mass_Omega{"cfg_min_mass_Omega", 1.668, "min mass for Omega"}; + Configurable cfg_max_mass_Omega{"cfg_max_mass_Omega", 1.678, "max mass for Omega"}; + Configurable cfg_min_mass_Omega_veto{"cfg_min_mass_Omega_veto", 1.665, "min mass for Omega veto"}; + Configurable cfg_max_mass_Omega_veto{"cfg_max_mass_Omega_veto", 1.680, "max mass for Omega veto"}; + Configurable cfg_min_cospa_v0{"cfg_min_cospa_v0", 0.95, "minimum V0 CosPA in cascade"}; Configurable cfg_max_dcadau_v0{"cfg_max_dcadau_v0", 0.1, "max distance between V0 Daughters in cascade"}; - Configurable cfg_min_cospa{"cfg_min_cospa", 0.9, "minimum cascade CosPA"}; + Configurable cfg_min_cospa{"cfg_min_cospa", 0.95, "minimum cascade CosPA"}; Configurable cfg_max_dcadau{"cfg_max_dcadau", 0.1, "max distance between bachelor and V0"}; - Configurable cfg_min_rxy_v0{"cfg_min_rxy_v0", 1.2, "minimum V0 rxy in cascade"}; - Configurable cfg_min_rxy{"cfg_min_rxy", 0.5, "minimum V0 rxy in cascade"}; + Configurable cfg_min_rxy_v0{"cfg_min_rxy_v0", 0.1, "minimum V0 rxy in cascade"}; + Configurable cfg_min_rxy{"cfg_min_rxy", 0.1, "minimum V0 rxy in cascade"}; Configurable cfg_min_dcaxy_v0leg{"cfg_min_dcaxy_v0leg", 0.1, "min dca XY for v0 legs in cm"}; Configurable cfg_min_dcaxy_bachelor{"cfg_min_dcaxy_bachelor", 0.05, "min dca XY for bachelor in cm"}; - Configurable cfg_min_dcaxy_v0{"cfg_min_dcaxy_v0", 0.05, "min dca XY for V0 in cm"}; + Configurable cfg_min_dcaxy_v0{"cfg_min_dcaxy_v0", 0.0, "min dca XY for V0 in cm"}; + + Configurable cfg_itsib_type{"cfg_itsib_type", 0, "0:free, 1:OR, 2:AND between 2 legs, else:free"}; + Configurable cfg_min_ncluster_its_v0leg{"cfg_min_ncluster_its_v0leg", 2, "min ncluster its"}; + Configurable cfg_min_ncluster_itsib_v0leg{"cfg_min_ncluster_itsib_v0leg", 0, "min ncluster itsib"}; + Configurable cfg_min_ncluster_its_bachelor{"cfg_min_ncluster_its_bachelor", 2, "min ncluster its"}; + Configurable cfg_min_ncluster_itsib_bachelor{"cfg_min_ncluster_itsib_bachelor", 0, "min ncluster itsib"}; } cascadeCut; struct : ConfigurableGroup { std::string prefix = "eventCut"; + Configurable cfgRejectEventGenerator{"cfgRejectEventGenerator", 999, "reject event generator. e.g. reject tracks from gap events"}; Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; Configurable cfgCentMin{"cfgCentMin", -1.f, "min. centrality"}; Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; - Configurable cfgEventGeneratorType{"cfgEventGeneratorType", -1, "if positive, select event generator type. i.e. gap or signal"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + // for RCT + o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for AA"}; + o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; } eventCut; struct : ConfigurableGroup { std::string prefix = "lKPairCut"; - Configurable cfg_min_mass{"cfg_min_mass", 0, "min mass at SV"}; - Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass at SV"}; Configurable cfg_min_cospa{"cfg_min_cospa", -1e+10, "min cospa"}; Configurable cfg_max_lxyz{"cfg_max_lxyz", 1e+10, "min rxy for v0hadron"}; - Configurable cfg_max_dca2legs{"cfg_max_dca2legs", 1e+10, "max distance between 2 legs"}; + Configurable cfg_max_chi2PCA{"cfg_max_chi2PCA", 1.0, "max chi2 at PCA"}; + o2::framework::Configurable cfg_max_massLH{"cfg_max_massLH", 1e+10, "max massLH in GeV/c2"}; // set hb mass. SVs whose mass is above this mass cannot be HF hadrons. } lKPairCut; struct : ConfigurableGroup { std::string prefix = "lV0PairCut"; - Configurable cfg_min_mass{"cfg_min_mass", 0, "min mass at SV"}; - Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass at SV"}; Configurable cfg_min_cospa{"cfg_min_cospa", -1e+10, "min cospa"}; Configurable cfg_max_lxyz{"cfg_max_lxyz", 1e+10, "min rxy for v0hadron"}; - Configurable cfg_max_dca2legs{"cfg_max_dca2legs", 1e+10, "max distance between 2 legs"}; + Configurable cfg_max_chi2PCA{"cfg_max_chi2PCA", 1.0, "max chi2 at PCA"}; + o2::framework::Configurable cfg_max_massLH{"cfg_max_massLH", 1e+10, "max massLH in GeV/c2"}; // set hb mass. SVs whose mass is above this mass cannot be HF hadrons. } lV0PairCut; struct : ConfigurableGroup { std::string prefix = "lCPairCut"; - Configurable cfg_min_mass{"cfg_min_mass", 0, "min mass at SV"}; - Configurable cfg_max_mass{"cfg_max_mass", 1e+10, "max mass at SV"}; Configurable cfg_min_cospa{"cfg_min_cospa", -1e+10, "min cospa"}; Configurable cfg_max_lxyz{"cfg_max_lxyz", 1e+10, "min rxy for v0hadron"}; - Configurable cfg_max_dca2legs{"cfg_max_dca2legs", 1e+10, "max distance between 2 legs"}; + Configurable cfg_max_chi2PCA{"cfg_max_chi2PCA", 1.0, "max chi2 at PCA"}; + o2::framework::Configurable cfg_max_massLH{"cfg_max_massLH", 1e+10, "max massLH in GeV/c2"}; // set hb mass. SVs whose mass is above this mass cannot be HF hadrons. } lCPairCut; + o2::aod::rctsel::RCTFlagsChecker rctChecker; + Service mTOFResponse; + HistogramRegistry fRegistry{"fRegistry"}; static constexpr std::string_view hadron_names[6] = {"LF/", "Jpsi/", "D0/", "Dpm/", "Ds/", "Lc/"}; static constexpr std::string_view pair_names[3] = {"e_Kpm/", "e_K0S/", "e_Lambda/"}; static constexpr std::string_view hTypes[4] = {"findable/", "correct/", "fake/", "miss/"}; static constexpr std::string_view promptTypes[2] = {"prompt/", "nonprompt/"}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& initContext) { // if (doprocessSA && doprocessTTCA) { // LOGF(fatal, "Cannot enable doprocessWithoutFTTCA and doprocessWithFTTCA at the same time. Please choose one."); @@ -273,9 +316,17 @@ struct taggingHFE { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); + rctChecker.init(eventCut.cfgRCTLabel.value, eventCut.cfgCheckZDC.value, eventCut.cfgTreatLimitedAcceptanceAsBad.value); + + LOGF(info, "intializing TOFResponse"); + mTOFResponse->initSetup(ccdb, initContext); + + std::random_device seed_gen; + engine = std::mt19937(seed_gen()); + dist01 = std::uniform_real_distribution(0.0f, 1.0f); fitter_eK.setPropagateToPCA(true); - fitter_eK.setMaxR(20.f); + fitter_eK.setMaxR(200.f); fitter_eK.setMinParamChange(1e-3); fitter_eK.setMinRelChi2Change(0.9); fitter_eK.setMaxDZIni(1e9); @@ -285,7 +336,7 @@ struct taggingHFE { fitter_eK.setMatCorrType(matCorr); fitter_eV0.setPropagateToPCA(true); - fitter_eV0.setMaxR(20.f); + fitter_eV0.setMaxR(200.f); fitter_eV0.setMinParamChange(1e-3); fitter_eV0.setMinRelChi2Change(0.9); fitter_eV0.setMaxDZIni(1e9); @@ -295,7 +346,7 @@ struct taggingHFE { fitter_eV0.setMatCorrType(matCorr); fitter_eCascade.setPropagateToPCA(true); - fitter_eCascade.setMaxR(20.f); + fitter_eCascade.setMaxR(200.f); fitter_eCascade.setMinParamChange(1e-3); fitter_eCascade.setMinRelChi2Change(0.9); fitter_eCascade.setMaxDZIni(1e9); @@ -310,6 +361,9 @@ struct taggingHFE { int mRunNumber; float d_bz; Service ccdb; + std::mt19937 engine; + std::uniform_real_distribution dist01; + // o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; const o2::dataformats::MeanVertexObject* mMeanVtx = nullptr; @@ -320,7 +374,8 @@ struct taggingHFE { o2::dataformats::DCA mDcaInfoCov; o2::dataformats::VertexBase mVtx; - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + template + void initCCDB(TBC const& bc) { if (mRunNumber == bc.runNumber()) { return; @@ -400,129 +455,171 @@ struct taggingHFE { fRegistry.add("Generated/D0/hsAcc", "pT-#eta acc.;p_{T,l} (GeV/c);p_{T,K} (GeV/c);#eta_{l};#eta_{K};", kTHnSparseF, {{100, 0, 10}, {100, 0, 10}, {100, -5, +5}, {100, -5, +5}}, false); fRegistry.add("Generated/Lc/hsAcc", "pT-#eta acc.;p_{T,l} (GeV/c);p_{T,#Lambda} (GeV/c);#eta_{l};#eta_{#Lambda};", kTHnSparseF, {{100, 0, 10}, {100, 0, 10}, {100, -5, +5}, {100, -5, +5}}, false); - fRegistry.add("Track/Electron/hTPCdEdx", "TPC dE/dx vs. pin;p_{in} (GeV/c);TPC dE/dx", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); - fRegistry.add("Track/Electron/hTOFbeta", "TOF #beta vs. p;p_{pv} (GeV/c);TOF #beta", kTH2F, {{1000, 0, 10}, {600, 0, 1.2}}, false); - fRegistry.add("Track/Kaon/hTPCdEdx", "TPC dE/dx vs. pin;p_{in} (GeV/c);TPC dE/dx", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); - fRegistry.add("Track/Kaon/hTOFbeta", "TOF #beta vs. p;p_{pv} (GeV/c);TOF #beta", kTH2F, {{1000, 0, 10}, {600, 0, 1.2}}, false); - - // electron-related histograms - fRegistry.add("Data/electron/hs", "hs;p_{T,e} (GeV/c);#eta_{e};#varphi_{e} (rad.);DCA_{e}^{3D} (#sigma);", kTHnSparseF, {{100, 0, 10}, {20, -1, +1}, {90, 0, 2 * M_PI}, {100, 0, 10}}, false); - fRegistry.addClone("Data/electron/", "MC/eFromPromptLF/"); - fRegistry.addClone("Data/electron/", "MC/eFromNonPromptLF/"); - fRegistry.addClone("Data/electron/", "MC/eFromPromptJpsi/"); - fRegistry.addClone("Data/electron/", "MC/eFromNonPromptJpsi/"); - - fRegistry.addClone("Data/electron/", "MC/eFromD0/"); - fRegistry.addClone("Data/electron/", "MC/eFromDpm/"); - // fRegistry.addClone("Data/electron/", "MC/eFromDs/"); - fRegistry.addClone("Data/electron/", "MC/eFromLcpm/"); - fRegistry.addClone("Data/electron/", "MC/eFromXic0/"); - // fRegistry.addClone("Data/electron/", "MC/eFromXicpm/"); // cannot be detected - fRegistry.addClone("Data/electron/", "MC/eFromOmegac0/"); - - // fRegistry.addClone("Data/electron/", "MC/eFromPromptD0/"); - // fRegistry.addClone("Data/electron/", "MC/eFromPromptDpm/"); - // fRegistry.addClone("Data/electron/", "MC/eFromPromptDs/"); - // fRegistry.addClone("Data/electron/", "MC/eFromPromptLcpm/"); - // fRegistry.addClone("Data/electron/", "MC/eFromPromptXic0/"); - // // fRegistry.addClone("Data/electron/", "MC/eFromPromptXicpm/"); // cannot be detected - // fRegistry.addClone("Data/electron/", "MC/eFromPromptOmegac0/"); - // fRegistry.addClone("Data/electron/", "MC/eFromNonPromptD0/"); - // fRegistry.addClone("Data/electron/", "MC/eFromNonPromptDpm/"); - // fRegistry.addClone("Data/electron/", "MC/eFromNonPromptDs/"); - // fRegistry.addClone("Data/electron/", "MC/eFromNonPromptLcpm/"); - // fRegistry.addClone("Data/electron/", "MC/eFromNonPromptXic0/"); - // // fRegistry.addClone("Data/electron/", "MC/eFromNonPromptXicpm/"); // cannot be detected - // fRegistry.addClone("Data/electron/", "MC/eFromNonPromptOmegac0/"); - // fRegistry.addClone("Data/electron/", "MC/eFromB0/"); - // fRegistry.addClone("Data/electron/", "MC/eFromBpm/"); - // fRegistry.addClone("Data/electron/", "MC/eFromBs/"); - // fRegistry.addClone("Data/electron/", "MC/eFromBc/"); - // fRegistry.addClone("Data/electron/", "MC/eFromLb0/"); - - // for V0 (Lambda) - fRegistry.add("Data/V0/hPt", "pT of V0;p_{T} (GeV/c)", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("Data/V0/hYPhi", "rapidity vs. #varphi of V0;#varphi (rad.);rapidity_{#Lambda}", kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); - fRegistry.add("Data/V0/hAP", "Ap plot;#alpha;q_{T} (GeV/c)", kTH2F, {{200, -1, 1}, {250, 0, 0.25}}, false); - fRegistry.add("Data/V0/hLxy", "decay length from PV;L_{xy} (cm)", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("Data/V0/hCosPA", "cosPA;cosine of pointing angle", kTH1F, {{100, 0.9, 1}}, false); - fRegistry.add("Data/V0/hDCA2Legs", "distance between 2 legs at PCA;distance between 2 legs (cm)", kTH1F, {{100, 0, 1}}, false); - fRegistry.add("Data/V0/hMassK0S", "K0S mass;m_{#pi#pi} (GeV/c^{2})", kTH1F, {{100, 0.45, 0.55}}, false); - fRegistry.add("Data/V0/hMassLambda", "Lambda mass;m_{p#pi^{#minus}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); - fRegistry.add("Data/V0/hMassAntiLambda", "Anti-Lambda mass;m_{#bar{p}#pi^{+}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); + fRegistry.add("Electron/hs", "hs;p_{T} (GeV/c);#eta;#varphi (rad.)", kTHnSparseF, {{100, 0, 10}, {80, -2, 2}, {36, 0, 2 * M_PI}}, false); + fRegistry.add("Electron/hDCA", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -1, 1}, {200, -1, 1}}, false); + fRegistry.add("Electron/hTPCdEdx", "TPC dE/dx vs. pin;p_{in} (GeV/c);TPC dE/dx", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + fRegistry.add("Electron/hTOFbeta", "TOF #beta vs. p;p_{pv} (GeV/c);TOF #beta", kTH2F, {{1000, 0, 10}, {600, 0, 1.2}}, false); + fRegistry.addClone("Electron/", "Hadron/"); + + // for V0 + fRegistry.add("V0/hPt", "pT of V0;p_{T} (GeV/c)", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("V0/hYPhi", "rapidity vs. #varphi of V0;#varphi (rad.);rapidity_{#Lambda}", kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); + fRegistry.add("V0/hAP", "Ap plot;#alpha;q_{T} (GeV/c)", kTH2F, {{200, -1, 1}, {250, 0, 0.25}}, false); + fRegistry.add("V0/hLxy", "decay length from PV;L_{xy} (cm)", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("V0/hCosPA", "cosPA;cosine of pointing angle", kTH1F, {{100, 0.9, 1}}, false); + fRegistry.add("V0/hDCA2Legs", "distance between 2 legs at PCA;distance between 2 legs (cm)", kTH1F, {{100, 0, 1}}, false); + fRegistry.add("V0/hMassK0S", "K0S mass;m_{#pi#pi} (GeV/c^{2})", kTH1F, {{100, 0.45, 0.55}}, false); + fRegistry.add("V0/hMassLambda", "Lambda mass;m_{p#pi^{#minus}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); + fRegistry.add("V0/hMassAntiLambda", "Anti-Lambda mass;m_{#bar{p}#pi^{+}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); + fRegistry.add("V0/hMassGamma_misid", "#gamma mass;m_{ee} (GeV/c^{2})", kTH1F, {{100, 0, 0.1}}, false); + fRegistry.add("V0/hMassK0S_misid", "K0S mass;m_{#pi#pi} (GeV/c^{2})", kTH1F, {{100, 0.45, 0.55}}, false); + fRegistry.add("V0/hMassLambda_misid", "Lambda mass;m_{p#pi^{#minus}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); + fRegistry.add("V0/hMassAntiLambda_misid", "Anti-Lambda mass;m_{#bar{p}#pi^{+}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); // for cascade - fRegistry.add("Data/Cascade/hPt", "pT of V0;p_{T} (GeV/c)", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("Data/Cascade/hYPhi", "rapidity vs. #varphi of V0;#varphi (rad.);rapidity_{#Lambda}", kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); - fRegistry.add("Data/Cascade/hCosPA", "cosPA;cosine of pointing angle", kTH1F, {{100, 0.9, 1}}, false); - fRegistry.add("Data/Cascade/hDCA2Legs", "distance between 2 legs at PCA;distance between 2 legs (cm)", kTH1F, {{100, 0, 1}}, false); - fRegistry.add("Data/Cascade/hV0CosPA", "cosPA of V0 in cascade;cosine of pointing angle", kTH1F, {{100, 0.9, 1}}, false); - fRegistry.add("Data/Cascade/hV0DCA2Legs", "distance between 2 legs at PCA of V0 in cascade;distance between 2 legs (cm)", kTH1F, {{100, 0, 1}}, false); - - fRegistry.add("Data/Cascade/hMassLambda", "Lambda mass;m_{p#pi^{-}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); - fRegistry.add("Data/Cascade/hMassXi", "#Xi mass;m_{#Lambda#pi} (GeV/c^{2})", kTH1F, {{100, 1.27, 1.37}}, false); - fRegistry.add("Data/Cascade/hMassOmega", "#Omega mass;m_{#LambdaK} (GeV/c^{2})", kTH1F, {{100, 1.62, 1.72}}, false); - - // for e-K pair - fRegistry.add("Data/eK/hs", "hs;p_{T,l} (GeV/c);DCA_{l}^{3D} (#sigma);p_{T,K} (GeV/c);DCA_{K}^{3D} (#sigma);m_{eK} (GeV/c^{2});L_{xyz} (#sigma);cosPA;DCA 2 legs (cm);", kTHnSparseF, {{100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {90, 0.5, 5.0}, {100, 0, 10}, {200, -1, 1}, {500, 0.0, 0.5}}, false); - fRegistry.addClone("Data/eK/", "MC/eKfromD0/"); - fRegistry.addClone("Data/eK/", "MC/eKfromDpm/"); - - // for e-K0 pair - fRegistry.add("Data/eK0/hs", "hs;p_{T,l} (GeV/c);DCA_{l}^{3D} (#sigma);p_{T,K0} (GeV/c);L_{xyz}^{K0} (cm);m_{eK0} (GeV/c^{2});L_{xyz} (#sigma);cosPA;DCA 2 legs (cm);", kTHnSparseF, {{100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {90, 0.5, 5.0}, {100, 0, 10}, {200, -1, 1}, {500, 0.0, 0.5}}, false); - fRegistry.addClone("Data/eK0/", "MC/eK0fromD0/"); - fRegistry.addClone("Data/eK0/", "MC/eK0fromDpm/"); - // fRegistry.addClone("Data/eK0/", "MC/eK0fromDspm/"); - - // for e-L pair - fRegistry.add("Data/eL/hs", "hs;p_{T,l} (GeV/c);DCA_{l}^{3D} (#sigma);p_{T,#Lambda} (GeV/c);L_{xyz}^{#Lambda} (cm);m_{e#Lambda} (GeV/c^{2});L_{xyz} (#sigma);cosPA;DCA 2 legs (cm);", kTHnSparseF, {{100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {90, 0.5, 5.0}, {100, 0, 10}, {200, -1, 1}, {500, 0.0, 0.5}}, false); - fRegistry.addClone("Data/eL/", "MC/eLfromLcpm/"); - - // for e-Xi pair - fRegistry.add("Data/eXi/hs", "hs;p_{T,l} (GeV/c);DCA_{l}^{3D} (#sigma);p_{T,#Xi} (GeV/c);L_{xyz}^{#Xi} (cm);m_{e#Xi} (GeV/c^{2});L_{xyz} (#sigma);cosPA;DCA 2 legs (cm);", kTHnSparseF, {{100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {90, 0.5, 5.0}, {100, 0, 10}, {200, -1, 1}, {500, 0.0, 0.5}}, false); - fRegistry.addClone("Data/eXi/", "MC/eXifromXic0/"); - - // for e-Omega pair - fRegistry.add("Data/eOmega/hs", "hs;p_{T,l} (GeV/c);DCA_{l}^{3D} (#sigma);p_{T,#Omega} (GeV/c);L_{xyz}^{#Omega} (cm);m_{e#Omega} (GeV/c^{2});L_{#sigma} (cm);cosPA;DCA 2 legs (cm);", kTHnSparseF, {{100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {100, 0, 10}, {90, 0.5, 5.0}, {100, 0, 10}, {200, -1, 1}, {500, 0.0, 0.5}}, false); - fRegistry.addClone("Data/eOmega/", "MC/eOmegafromOmegac0/"); + fRegistry.add("Cascade/hPt", "pT of cascade;p_{T} (GeV/c)", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Cascade/hYPhi", "rapidity vs. #varphi of cascade;#varphi (rad.);rapidity_{#Lambda}", kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); + fRegistry.add("Cascade/hCosPA", "cosPA;cosine of pointing angle", kTH1F, {{100, 0.9, 1}}, false); + fRegistry.add("Cascade/hLxy", "decay length from PV;L_{xy} (cm)", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Cascade/hDCA2Legs", "distance between 2 legs at PCA;distance between 2 legs (cm)", kTH1F, {{100, 0, 1}}, false); + fRegistry.add("Cascade/hV0CosPA", "cosPA of V0 in cascade;cosine of pointing angle", kTH1F, {{100, 0.9, 1}}, false); + fRegistry.add("Cascade/hV0DCA2Legs", "distance between 2 legs at PCA of V0 in cascade;distance between 2 legs (cm)", kTH1F, {{100, 0, 1}}, false); + fRegistry.add("Cascade/hV0Lxy", "decay length from PV of V0;L_{xy} (cm)", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Cascade/hMassLambda", "Lambda mass;m_{p#pi^{-}} (GeV/c^{2})", kTH1F, {{100, 1.08, 1.18}}, false); + fRegistry.add("Cascade/hMassXi", "#Xi mass;m_{#Lambda#pi} (GeV/c^{2})", kTH1F, {{100, 1.27, 1.37}}, false); + fRegistry.add("Cascade/hMassOmega", "#Omega mass;m_{#LambdaK} (GeV/c^{2})", kTH1F, {{100, 1.62, 1.72}}, false); + fRegistry.add("Cascade/hMassXi_misid", "#Xi mass;m_{#Lambda#pi} (GeV/c^{2})", kTH1F, {{100, 1.27, 1.37}}, false); + fRegistry.add("Cascade/hMassOmega_misid", "#Omega mass;m_{#LambdaK} (GeV/c^{2})", kTH1F, {{100, 1.62, 1.72}}, false); } - template - bool isKaon(TTrack const& track) + template + bool isPiKa(TCollision const& collision, TTrack const& track) { - // TOFif - bool is_ka_included_TPC = kaonCut.cfg_min_TPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < kaonCut.cfg_max_TPCNsigmaKa; - bool is_ka_included_TOF = track.hasTOF() ? (kaonCut.cfg_min_TOFNsigmaKa < track.tofNSigmaKa() && track.tofNSigmaKa() < kaonCut.cfg_max_TOFNsigmaKa) : true; - if (kaonCut.requireTOF) { - if (track.tpcInnerParam() < kaonCut.cfg_min_pin_TOFreq) { - return is_ka_included_TPC; - } else { - is_ka_included_TOF = kaonCut.cfg_min_TOFNsigmaKa < track.tofNSigmaKa() && track.tofNSigmaKa() < kaonCut.cfg_max_TOFNsigmaKa; - return is_ka_included_TPC && is_ka_included_TOF; - } - } else { - return is_ka_included_TPC && is_ka_included_TOF; // TOFif + float tofNSigmaPi = mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + float tofNSigmaKa = mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + // float tofNSigmaPr = mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + bool is_pi_included_TPC = hadronCut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < hadronCut.cfg_max_TPCNsigmaPi; + bool is_pi_included_TOF = track.hasTOF() ? (hadronCut.cfg_min_TOFNsigmaPi < tofNSigmaPi && tofNSigmaPi < hadronCut.cfg_max_TOFNsigmaPi) : true; + bool is_ka_included_TPC = hadronCut.cfg_min_TPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < hadronCut.cfg_max_TPCNsigmaKa; + bool is_ka_included_TOF = track.hasTOF() ? (hadronCut.cfg_min_TOFNsigmaKa < tofNSigmaKa && tofNSigmaKa < hadronCut.cfg_max_TOFNsigmaKa) : true; + // bool is_pr_included_TPC = hadronCut.cfg_min_TPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < hadronCut.cfg_max_TPCNsigmaPr; + // bool is_pr_included_TOF = track.hasTOF() ? (hadronCut.cfg_min_TOFNsigmaPr < tofNSigmaPr && tofNSigmaPr < hadronCut.cfg_max_TOFNsigmaPr) : true; + if (!hadronCut.applyTOFif) { + is_pi_included_TOF = true; + is_ka_included_TOF = true; + // is_pr_included_TOF = true; + } + return (is_pi_included_TPC && is_pi_included_TOF) || (is_ka_included_TPC && is_ka_included_TOF) /* || (is_pr_included_TPC && is_pr_included_TOF)*/; + } + + template + bool isPionFromK0S(TCollision const& collision, TTrack const& track) + { + float tofNSigmaPi = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::K0, o2::track::PID::Pion)]; + bool is_pi_included_TPC = v0Cut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < v0Cut.cfg_max_TPCNsigmaPi; + bool is_pi_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPi < tofNSigmaPi && tofNSigmaPi < v0Cut.cfg_max_TOFNsigmaPi) : true; + if (!v0Cut.applyTOFif) { + is_pi_included_TOF = true; } + return is_pi_included_TPC && is_pi_included_TOF; } - template - bool isPion(TTrack const& track) + template + bool isPionFromLambda(TCollision const& collision, TTrack const& track) { - // TOFif + float tofNSigmaPi = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Pion)]; bool is_pi_included_TPC = v0Cut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < v0Cut.cfg_max_TPCNsigmaPi; - bool is_pi_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPi < track.tofNSigmaPi() && track.tofNSigmaPi() < v0Cut.cfg_max_TOFNsigmaPi) : true; + bool is_pi_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPi < tofNSigmaPi && tofNSigmaPi < v0Cut.cfg_max_TOFNsigmaPi) : true; + if (!v0Cut.applyTOFif) { + is_pi_included_TOF = true; + } return is_pi_included_TPC && is_pi_included_TOF; } - template - bool isProton(TTrack const& track) + template + bool isProtonFromLambda(TCollision const& collision, TTrack const& track) { - // TOFif + float tofNSigmaPr = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Proton)]; bool is_pr_included_TPC = v0Cut.cfg_min_TPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < v0Cut.cfg_max_TPCNsigmaPr; - bool is_pr_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPr < track.tofNSigmaPr() && track.tofNSigmaPr() < v0Cut.cfg_max_TOFNsigmaPr) : true; + bool is_pr_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPr < tofNSigmaPr && tofNSigmaPr < v0Cut.cfg_max_TOFNsigmaPr) : true; + if (!v0Cut.applyTOFif) { + is_pr_included_TOF = true; + } return is_pr_included_TPC && is_pr_included_TOF; } + template + bool isPionFromLambdaFromXi(TCollision const& collision, TTrack const& track) + { + float tofNSigmaPi = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)]; + bool is_pi_included_TPC = v0Cut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < v0Cut.cfg_max_TPCNsigmaPi; + bool is_pi_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPi < tofNSigmaPi && tofNSigmaPi < v0Cut.cfg_max_TOFNsigmaPi) : true; + if (!v0Cut.applyTOFif) { + is_pi_included_TOF = true; + } + return is_pi_included_TPC && is_pi_included_TOF; + } + + template + bool isPionFromLambdaFromOmega(TCollision const& collision, TTrack const& track) + { + float tofNSigmaPi = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Pion)]; + bool is_pi_included_TPC = v0Cut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < v0Cut.cfg_max_TPCNsigmaPi; + bool is_pi_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPi < tofNSigmaPi && tofNSigmaPi < v0Cut.cfg_max_TOFNsigmaPi) : true; + if (!v0Cut.applyTOFif) { + is_pi_included_TOF = true; + } + return is_pi_included_TPC && is_pi_included_TOF; + } + + template + bool isProtonFromLambdaFromXi(TCollision const& collision, TTrack const& track) + { + float tofNSigmaPr = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Proton)]; + bool is_pr_included_TPC = v0Cut.cfg_min_TPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < v0Cut.cfg_max_TPCNsigmaPr; + bool is_pr_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPr < tofNSigmaPr && tofNSigmaPr < v0Cut.cfg_max_TOFNsigmaPr) : true; + if (!v0Cut.applyTOFif) { + is_pr_included_TOF = true; + } + return is_pr_included_TPC && is_pr_included_TOF; + } + + template + bool isProtonFromLambdaFromOmega(TCollision const& collision, TTrack const& track) + { + float tofNSigmaPr = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Proton)]; + bool is_pr_included_TPC = v0Cut.cfg_min_TPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < v0Cut.cfg_max_TPCNsigmaPr; + bool is_pr_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPr < tofNSigmaPr && tofNSigmaPr < v0Cut.cfg_max_TOFNsigmaPr) : true; + if (!v0Cut.applyTOFif) { + is_pr_included_TOF = true; + } + return is_pr_included_TPC && is_pr_included_TOF; + } + + template + bool isPionFromXi(TCollision const& collision, TTrack const& track) + { + float tofNSigmaPi = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)]; + bool is_pi_included_TPC = v0Cut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < v0Cut.cfg_max_TPCNsigmaPi; + bool is_pi_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaPi < tofNSigmaPi && tofNSigmaPi < v0Cut.cfg_max_TOFNsigmaPi) : true; + if (!v0Cut.applyTOFif) { + is_pi_included_TOF = true; + } + return is_pi_included_TPC && is_pi_included_TOF; + } + + template + bool isKaonFromOmega(TCollision const& collision, TTrack const& track) + { + float tofNSigmaKa = mapTOFNSigma[std::make_tuple(collision.globalIndex(), track.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)]; + bool is_ka_included_TPC = v0Cut.cfg_min_TPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < v0Cut.cfg_max_TPCNsigmaKa; + bool is_ka_included_TOF = track.hasTOF() ? (v0Cut.cfg_min_TOFNsigmaKa < tofNSigmaKa && tofNSigmaKa < v0Cut.cfg_max_TOFNsigmaKa) : true; + if (!v0Cut.applyTOFif) { + is_ka_included_TOF = true; + } + return is_ka_included_TPC && is_ka_included_TOF; + } + // template // bool isElectron(TTrack const& track) // { @@ -532,15 +629,6 @@ struct taggingHFE { // return is_el_included_TPC && is_el_included_TOF; // } - template - bool isKaonBachelor(TTrack const& track) - { - // TOFif - bool is_ka_included_TPC = kaonCut.cfg_min_TPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < kaonCut.cfg_max_TPCNsigmaKa; - bool is_ka_included_TOF = track.hasTOF() ? (kaonCut.cfg_min_TOFNsigmaKa < track.tofNSigmaKa() && track.tofNSigmaKa() < kaonCut.cfg_max_TOFNsigmaKa) : true; - return is_ka_included_TPC && is_ka_included_TOF; - } - template bool isSelectedTrack(TTrack const& track, TTrackParCov const& trackParCov, const float dcaXY, const float dcaZ) { @@ -603,62 +691,62 @@ struct taggingHFE { return true; } - template - bool isSelectedKaon(TTrack const& track, TTrackParCov const& trackParCov, const float dcaXY, const float dcaZ) + template + bool isSelectedHadron(TCollision const& collision, TTrack const& track, TTrackParCov const& trackParCov, const float dcaXY, const float dcaZ) { if (!track.hasITS() || !track.hasTPC()) { return false; } - if (trackParCov.getPt() < kaonCut.cfg_min_pt_track || kaonCut.cfg_max_pt_track < trackParCov.getPt()) { + if (trackParCov.getPt() < hadronCut.cfg_min_pt_track || hadronCut.cfg_max_pt_track < trackParCov.getPt()) { return false; } - if (trackParCov.getEta() < kaonCut.cfg_min_eta_track || kaonCut.cfg_max_eta_track < trackParCov.getEta()) { + if (trackParCov.getEta() < hadronCut.cfg_min_eta_track || hadronCut.cfg_max_eta_track < trackParCov.getEta()) { return false; } - if (std::fabs(dcaXY) > kaonCut.cfg_max_dcaxy) { + if (std::fabs(dcaXY) > hadronCut.cfg_max_dcaxy) { return false; } - if (std::fabs(dcaZ) > kaonCut.cfg_max_dcaz) { + if (std::fabs(dcaZ) > hadronCut.cfg_max_dcaz) { return false; } - if (track.itsChi2NCl() < 0.f || kaonCut.cfg_max_chi2its < track.itsChi2NCl()) { + if (track.itsChi2NCl() < 0.f || hadronCut.cfg_max_chi2its < track.itsChi2NCl()) { return false; } - if (track.itsNCls() < kaonCut.cfg_min_ncluster_its) { + if (track.itsNCls() < hadronCut.cfg_min_ncluster_its) { return false; } - if (track.itsNClsInnerBarrel() < kaonCut.cfg_min_ncluster_itsib) { + if (track.itsNClsInnerBarrel() < hadronCut.cfg_min_ncluster_itsib) { return false; } - if (track.tpcChi2NCl() < 0.f || kaonCut.cfg_max_chi2tpc < track.tpcChi2NCl()) { + if (track.tpcChi2NCl() < 0.f || hadronCut.cfg_max_chi2tpc < track.tpcChi2NCl()) { return false; } - if (track.tpcNClsFound() < kaonCut.cfg_min_ncluster_tpc) { + if (track.tpcNClsFound() < hadronCut.cfg_min_ncluster_tpc) { return false; } - if (track.tpcNClsCrossedRows() < kaonCut.cfg_min_ncrossedrows_tpc) { + if (track.tpcNClsCrossedRows() < hadronCut.cfg_min_ncrossedrows_tpc) { return false; } - if (track.tpcCrossedRowsOverFindableCls() < kaonCut.cfg_min_cr2findable_ratio_tpc) { + if (track.tpcCrossedRowsOverFindableCls() < hadronCut.cfg_min_cr2findable_ratio_tpc) { return false; } - if (track.tpcFractionSharedCls() > kaonCut.cfg_max_frac_shared_clusters_tpc) { + if (track.tpcFractionSharedCls() > hadronCut.cfg_max_frac_shared_clusters_tpc) { return false; } - if (!isKaon(track)) { + if (hadronCut.requirePiKa && !isPiKa(collision, track)) { return false; } @@ -668,7 +756,7 @@ struct taggingHFE { template bool isK0S(TV0 const& v0) { - return (v0Cut.cfg_min_mass_k0s < v0.mK0Short() && v0.mK0Short() < v0Cut.cfg_max_mass_k0s); + return (v0Cut.cfg_min_mass_k0s < v0.mK0Short() && v0.mK0Short() < v0Cut.cfg_max_mass_k0s) && (v0.mLambda() < v0Cut.cfg_min_mass_lambda_veto || v0Cut.cfg_max_mass_lambda_veto < v0.mLambda()) && (v0.mAntiLambda() < v0Cut.cfg_min_mass_lambda_veto || v0Cut.cfg_max_mass_lambda_veto < v0.mAntiLambda()); } template @@ -695,9 +783,10 @@ struct taggingHFE { return (cascadeCut.cfg_min_mass_Omega < cascade.mOmega() && cascade.mOmega() < cascadeCut.cfg_max_mass_Omega) && (cascade.mXi() < cascadeCut.cfg_min_mass_Xi_veto || cascadeCut.cfg_max_mass_Xi_veto < cascade.mXi()); } - template + template bool isSelectedV0Leg(TTrack const& track) { + // trackType = 0:v0leg, 1:v0leg in cascade, 2:bachelor of cascade only for ITS requirements if constexpr (isMC) { if (!track.has_mcParticle()) { return false; @@ -708,15 +797,21 @@ struct taggingHFE { return false; } - if (track.itsChi2NCl() > v0Cut.cfg_max_chi2its) { - return false; - } - - if (track.itsNCls() < v0Cut.cfg_min_ncluster_its) { - return false; + if constexpr (trackType == 0) { + if (track.itsNCls() < v0Cut.cfg_min_ncluster_its) { // must be 2 + return false; + } + } else if constexpr (trackType == 1) { + if (track.itsNCls() < cascadeCut.cfg_min_ncluster_its_v0leg) { // must be 2 + return false; + } + } else if constexpr (trackType == 2) { + if (track.itsNCls() < cascadeCut.cfg_min_ncluster_its_bachelor) { // must be 2 + return false; + } } - if (track.itsNClsInnerBarrel() < v0Cut.cfg_min_ncluster_itsib) { + if (track.itsChi2NCl() > v0Cut.cfg_max_chi2its) { return false; } @@ -743,6 +838,35 @@ struct taggingHFE { return true; } + template + bool checkITSibForV0Legs(TTrack const& t1, TTrack const& t2) + { + // typeSV = 0:v0, 1:cascade + if constexpr (typeSV == 0) { // V0 legs + if (v0Cut.cfg_itsib_type == 0) { // free + return true; + } else if (v0Cut.cfg_itsib_type == 1) { // OR + return t1.itsNClsInnerBarrel() >= v0Cut.cfg_min_ncluster_itsib || t2.itsNClsInnerBarrel() >= v0Cut.cfg_min_ncluster_itsib; + } else if (v0Cut.cfg_itsib_type == 2) { // AND + return t1.itsNClsInnerBarrel() >= v0Cut.cfg_min_ncluster_itsib && t2.itsNClsInnerBarrel() >= v0Cut.cfg_min_ncluster_itsib; + } else { + return true; + } + } else if constexpr (typeSV == 1) { // V0 legs in cascade + if (cascadeCut.cfg_itsib_type == 0) { // free + return true; + } else if (cascadeCut.cfg_itsib_type == 1) { // OR + return t1.itsNClsInnerBarrel() >= cascadeCut.cfg_min_ncluster_itsib_v0leg || t2.itsNClsInnerBarrel() >= cascadeCut.cfg_min_ncluster_itsib_v0leg; + } else if (cascadeCut.cfg_itsib_type == 2) { // AND + return t1.itsNClsInnerBarrel() >= cascadeCut.cfg_min_ncluster_itsib_v0leg && t2.itsNClsInnerBarrel() >= cascadeCut.cfg_min_ncluster_itsib_v0leg; + } else { + return true; + } + } else { + return true; + } + } + template void fillEventHistograms(TCollision const& collision) { @@ -757,141 +881,86 @@ struct taggingHFE { fRegistry.fill(HIST("Event/hMultFT0CvsMultNTracksPV"), collision.multFT0C(), collision.multNTracksPV()); } - template - void fillV0Histograms(TV0 const& v0) + template + void fillV0Histograms(TCollision const& collision, TV0 const& v0) { auto pos = v0.template posTrack_as(); auto neg = v0.template negTrack_as(); - fRegistry.fill(HIST("Data/V0/hPt"), v0.pt()); - fRegistry.fill(HIST("Data/V0/hYPhi"), v0.phi(), v0.yLambda()); - fRegistry.fill(HIST("Data/V0/hAP"), v0.alpha(), v0.qtarm()); - fRegistry.fill(HIST("Data/V0/hCosPA"), v0.v0cosPA()); - fRegistry.fill(HIST("Data/V0/hLxy"), v0.v0radius()); - fRegistry.fill(HIST("Data/V0/hDCA2Legs"), v0.dcaV0daughters()); - - if (isPion(pos) && isPion(neg)) { - fRegistry.fill(HIST("Data/V0/hMassK0S"), v0.mK0Short()); + fRegistry.fill(HIST("V0/hPt"), v0.pt()); + fRegistry.fill(HIST("V0/hYPhi"), v0.phi(), v0.yLambda()); + fRegistry.fill(HIST("V0/hAP"), v0.alpha(), v0.qtarm()); + fRegistry.fill(HIST("V0/hCosPA"), v0.v0cosPA()); + fRegistry.fill(HIST("V0/hLxy"), v0.v0radius()); + fRegistry.fill(HIST("V0/hDCA2Legs"), v0.dcaV0daughters()); + + if (isPionFromK0S(collision, pos) && isPionFromK0S(collision, neg)) { + if ((v0.mLambda() < v0Cut.cfg_min_mass_lambda_veto || v0Cut.cfg_max_mass_lambda_veto < v0.mLambda()) && (v0.mAntiLambda() < v0Cut.cfg_min_mass_lambda_veto || v0Cut.cfg_max_mass_lambda_veto < v0.mAntiLambda())) { + fRegistry.fill(HIST("V0/hMassK0S"), v0.mK0Short()); + } + fRegistry.fill(HIST("V0/hMassGamma_misid"), v0.mGamma()); + fRegistry.fill(HIST("V0/hMassLambda_misid"), v0.mLambda()); + fRegistry.fill(HIST("V0/hMassAntiLambda_misid"), v0.mAntiLambda()); } - if (isProton(pos) && isPion(neg)) { - fRegistry.fill(HIST("Data/V0/hMassLambda"), v0.mLambda()); + + if (isProtonFromLambda(collision, pos) && isPionFromLambda(collision, neg)) { + if (v0.mK0Short() < v0Cut.cfg_min_mass_k0s_veto || v0Cut.cfg_max_mass_k0s_veto < v0.mK0Short()) { + fRegistry.fill(HIST("V0/hMassLambda"), v0.mLambda()); + } + fRegistry.fill(HIST("V0/hMassGamma_misid"), v0.mGamma()); + fRegistry.fill(HIST("V0/hMassK0S_misid"), v0.mK0Short()); } - if (isProton(neg) && isPion(pos)) { - fRegistry.fill(HIST("Data/V0/hMassAntiLambda"), v0.mAntiLambda()); + + if (isProtonFromLambda(collision, neg) && isPionFromLambda(collision, pos)) { + if (v0.mK0Short() < v0Cut.cfg_min_mass_k0s_veto || v0Cut.cfg_max_mass_k0s_veto < v0.mK0Short()) { + fRegistry.fill(HIST("V0/hMassAntiLambda"), v0.mAntiLambda()); + } + fRegistry.fill(HIST("V0/hMassGamma_misid"), v0.mGamma()); + fRegistry.fill(HIST("V0/hMassK0S_misid"), v0.mK0Short()); } } template void fillCascadeHistograms(TCollision const& collision, TCascade const& cascade) { + auto pos = cascade.template posTrack_as(); + auto neg = cascade.template negTrack_as(); auto bachelor = cascade.template bachelor_as(); - fRegistry.fill(HIST("Data/Cascade/hPt"), cascade.pt()); - fRegistry.fill(HIST("Data/Cascade/hMassLambda"), cascade.mLambda()); - fRegistry.fill(HIST("Data/Cascade/hCosPA"), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); - fRegistry.fill(HIST("Data/Cascade/hDCA2Legs"), cascade.dcacascdaughters()); - fRegistry.fill(HIST("Data/Cascade/hV0CosPA"), cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); - fRegistry.fill(HIST("Data/Cascade/hV0DCA2Legs"), cascade.dcaV0daughters()); - - if (isPion(bachelor)) { - fRegistry.fill(HIST("Data/Cascade/hMassXi"), cascade.mXi()); - } - if (isKaonBachelor(bachelor)) { - fRegistry.fill(HIST("Data/Cascade/hMassOmega"), cascade.mOmega()); - } - } - - template - void fillElectronHistograms(TTrack const& track, TMCParticles const& mcParticles) - { - mDcaInfoCov.set(999, 999, 999, 999, 999); - auto trackParCov = getTrackParCov(track); - trackParCov.setPID(o2::track::PID::Electron); - o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); - float dcaXY = mDcaInfoCov.getY(); - float dcaZ = mDcaInfoCov.getZ(); - float dca3DinSigma = dca3DinSigmaOTF(dcaXY, dcaZ, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()); - - if (!isSelectedTrack(track, trackParCov, dcaXY, dcaZ)) { - return; - } - fRegistry.fill(HIST("Data/electron/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - if constexpr (isMC) { - const auto& mctrack = track.template mcParticle_as(); - if (std::abs(mctrack.pdgCode()) != 11) { - return; + fRegistry.fill(HIST("Cascade/hPt"), cascade.pt()); + fRegistry.fill(HIST("Cascade/hMassLambda"), cascade.mLambda()); + fRegistry.fill(HIST("Cascade/hCosPA"), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + fRegistry.fill(HIST("Cascade/hDCA2Legs"), cascade.dcacascdaughters()); + fRegistry.fill(HIST("Cascade/hLxy"), cascade.cascradius()); + fRegistry.fill(HIST("Cascade/hV0CosPA"), cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + fRegistry.fill(HIST("Cascade/hV0DCA2Legs"), cascade.dcaV0daughters()); + fRegistry.fill(HIST("Cascade/hV0Lxy"), cascade.v0radius()); + + if (cascade.sign() < 0) { // Xi- or Omega- + if (isPionFromXi(collision, bachelor) && isProtonFromLambdaFromXi(collision, pos) && isPionFromLambdaFromXi(collision, neg)) { + if (cascade.mOmega() < cascadeCut.cfg_min_mass_Omega_veto || cascadeCut.cfg_max_mass_Omega_veto < cascade.mOmega()) { + fRegistry.fill(HIST("Cascade/hMassXi"), cascade.mXi()); + } + fRegistry.fill(HIST("Cascade/hMassOmega_misid"), cascade.mOmega()); } - if (!(mctrack.isPhysicalPrimary() || mctrack.producedByGenerator())) { - return; + if (isKaonFromOmega(collision, bachelor) && isProtonFromLambdaFromOmega(collision, pos) && isPionFromLambdaFromOmega(collision, neg)) { + if (cascade.mXi() < cascadeCut.cfg_min_mass_Xi_veto || cascadeCut.cfg_max_mass_Xi_veto < cascade.mXi()) { + fRegistry.fill(HIST("Cascade/hMassOmega"), cascade.mOmega()); + } + fRegistry.fill(HIST("Cascade/hMassXi_misid"), cascade.mXi()); } - const auto& mcmother = mctrack.template mothers_first_as(); // mother particle of electron - int pdg_mother = std::abs(mcmother.pdgCode()); - - if (pdg_mother == 111 || pdg_mother == 221 || pdg_mother == 331 || pdg_mother == 113 || pdg_mother == 223 || pdg_mother == 333) { // LF - if (IsFromCharm(mcmother, mcParticles) < 0 && IsFromBeauty(mcmother, mcParticles) < 0) { - fRegistry.fill(HIST("MC/eFromPromptLF/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - } else { - fRegistry.fill(HIST("MC/eFromNonPromptLF/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); + } else { // Xi+ or Omega+ + if (isPionFromXi(collision, bachelor) && isProtonFromLambdaFromXi(collision, neg) && isPionFromLambdaFromXi(collision, pos)) { + if (cascade.mOmega() < cascadeCut.cfg_min_mass_Omega_veto || cascadeCut.cfg_max_mass_Omega_veto < cascade.mOmega()) { + fRegistry.fill(HIST("Cascade/hMassXi"), cascade.mXi()); } - } else if (pdg_mother == 443) { // Jpsi - if (IsFromBeauty(mcmother, mcParticles) < 0) { - fRegistry.fill(HIST("MC/eFromPromptJpsi/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - } else { - fRegistry.fill(HIST("MC/eFromNonPromptJpsi/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); + fRegistry.fill(HIST("Cascade/hMassOmega_misid"), cascade.mOmega()); + } + if (isKaonFromOmega(collision, bachelor) && isProtonFromLambdaFromOmega(collision, neg) && isPionFromLambdaFromOmega(collision, pos)) { + if (cascade.mXi() < cascadeCut.cfg_min_mass_Xi_veto || cascadeCut.cfg_max_mass_Xi_veto < cascade.mXi()) { + fRegistry.fill(HIST("Cascade/hMassOmega"), cascade.mOmega()); } - } else if (pdg_mother == 411) { // D+/- - fRegistry.fill(HIST("MC/eFromDpm/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // if (IsFromBeauty(mcmother, mcParticles) < 0) { - // fRegistry.fill(HIST("MC/eFromPromptDpm/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else { - // fRegistry.fill(HIST("MC/eFromNonPromptDpm/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } - } else if (pdg_mother == 421) { // D0 - fRegistry.fill(HIST("MC/eFromD0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // if (IsFromBeauty(mcmother, mcParticles) < 0) { - // fRegistry.fill(HIST("MC/eFromPromptD0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else { - // fRegistry.fill(HIST("MC/eFromNonPromptD0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } - } else if (pdg_mother == 431) { // Ds+/- - // fRegistry.fill(HIST("MC/eFromDs/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // if (IsFromBeauty(mcmother, mcParticles) < 0) { - // fRegistry.fill(HIST("MC/eFromPromptDs/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else { - // fRegistry.fill(HIST("MC/eFromNonPromptDs/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } - } else if (pdg_mother == 4122) { // Lc+/- - fRegistry.fill(HIST("MC/eFromLcpm/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // if (IsFromBeauty(mcmother, mcParticles) < 0) { - // fRegistry.fill(HIST("MC/eFromPromptLcpm/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else { - // fRegistry.fill(HIST("MC/eFromNonPromptLcpm/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } - } else if (pdg_mother == 4132) { // Xic0 - fRegistry.fill(HIST("MC/eFromXic0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // if (IsFromBeauty(mcmother, mcParticles) < 0) { - // fRegistry.fill(HIST("MC/eFromPromptXic0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else { - // fRegistry.fill(HIST("MC/eFromNonPromptXic0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } - } else if (pdg_mother == 4332) { // Omegac0 - fRegistry.fill(HIST("MC/eFromOmegac0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // if (IsFromBeauty(mcmother, mcParticles) < 0) { - // fRegistry.fill(HIST("MC/eFromPromptOmegac0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else { - // fRegistry.fill(HIST("MC/eFromNonPromptOmegac0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } + fRegistry.fill(HIST("Cascade/hMassXi_misid"), cascade.mXi()); } - // else if (pdg_mother == 511) { // B0 - // fRegistry.fill(HIST("MC/eFromB0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else if (pdg_mother == 521) { // B+/- - // fRegistry.fill(HIST("MC/eFromBpm/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else if (pdg_mother == 531) { // Bs0 - // fRegistry.fill(HIST("MC/eFromBs/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else if (pdg_mother == 541) { // Bc+/- - // fRegistry.fill(HIST("MC/eFromBc/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } else if (pdg_mother == 5122) { // Lb0 - // fRegistry.fill(HIST("MC/eFromLb0/hs"), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), dca3DinSigma); - // } } } @@ -905,6 +974,36 @@ struct taggingHFE { } } + template + bool isSemiLeptonic(TMCParticle const& mcParticle, TMCParticles const& mcParticles, const int pdgLepton, const int pdgNeutrino) + { + if (!mcParticle.has_daughters()) { + return false; + } + bool is_lepton_involved = false; + bool is_neutrino_involved = false; + for (int d = mcParticle.daughtersIds()[0]; d <= mcParticle.daughtersIds()[1]; ++d) { + if (d < mcParticles.size()) { // protect against bad daughter indices + auto daughter = mcParticles.rawIteratorAt(d); + if (daughter.pdgCode() == pdgLepton) { + is_lepton_involved = true; + } else if (daughter.pdgCode() == pdgNeutrino) { + is_neutrino_involved = true; + } + } else { + std::cout << "Daughter label (" << d << ") exceeds the McParticles size (" << mcParticles.size() << ")" << std::endl; + std::cout << " Check the MC generator" << std::endl; + return false; + } + } + + if (is_lepton_involved && is_neutrino_involved) { + return true; + } else { + return false; + } + } + template bool isSemiLeptonic(TMCParticle const& mcParticle, TMCParticles const& mcParticles, const int pdgLepton, const int pdgNeutrino, const int pdgStrHad) { @@ -916,7 +1015,7 @@ struct taggingHFE { bool is_strhad_involved = false; for (int d = mcParticle.daughtersIds()[0]; d <= mcParticle.daughtersIds()[1]; ++d) { if (d < mcParticles.size()) { // protect against bad daughter indices - const auto& daughter = mcParticles.rawIteratorAt(d); + auto daughter = mcParticles.rawIteratorAt(d); if (daughter.pdgCode() == pdgLepton) { is_lepton_involved = true; } else if (daughter.pdgCode() == pdgNeutrino) { @@ -939,38 +1038,471 @@ struct taggingHFE { } } + template + bool isSelectedCollision(TCollision const& collision) + { + if (!(eventCut.cfgZvtxMin < collision.posZ() && collision.posZ() < eventCut.cfgZvtxMax)) { + return false; + } + if (eventCut.cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + if (eventCut.cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (eventCut.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (eventCut.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (eventCut.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + return true; + } + + std::map, float> mapTOFNSigma; // map -> tof n sigma + std::map, float> mapTOFBeta; // map -> tof beta + + template + float recalculateTOFNSigmaTrackFromV0FromCascade(TCollision const& collision, TCascade const& cascade, const float massCascade, float& beta) + { + if (mapCollisionTime.find(cascade.collisionId()) == mapCollisionTime.end()) { + beta = -1.f; + return -999.f; + } + + auto track = sign > 0 ? cascade.template posTrack_as() : cascade.template negTrack_as(); + auto arrMomTrack = sign > 0 ? std::array{cascade.pxpos(), cascade.pypos(), cascade.pzpos()} : std::array{cascade.pxneg(), cascade.pyneg(), cascade.pzneg()}; + + if (!track.hasTOF() || !track.has_collision()) { + beta = -1.f; + return -999.f; + } + auto bcCascade = collision.template bc_as(); // collision in this function is the collision where this cascade belong. + + o2::track::TrackPar trackPar = o2::track::TrackPar({cascade.xlambda(), cascade.ylambda(), cascade.zlambda()}, arrMomTrack, sign, false); + auto collisionTrack = track.template collision_as(); + auto bcTrack = collisionTrack.template bc_as(); + double deltaTime = track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCascade.globalBC()) - timeCascade(collision, cascade, massCascade) - timeV0inCascade(cascade); // in ps + + const o2::math_utils::Point3D trackVertex{collisionTrack.posX(), collisionTrack.posY(), collisionTrack.posZ()}; + o2::track::TrackLTIntegral ltIntegral; + bool successPropag = o2::base::Propagator::Instance()->propagateToDCA(trackVertex, trackPar, d_bz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, nullptr, <Integral); + if (!successPropag) { + beta = -1.f; + return -999.f; + } + + float length = track.length() - ltIntegral.getL(); + if (length < 0.f) { + beta = -1.f; + return -999.f; + } + + beta = length / (deltaTime - mapCollisionTime[cascade.collisionId()]) / (TMath::C() * 1e+2 * 1e-12); + return mTOFResponse->nSigma(deltaTime, track.tofExpMom(), length, trackPar.getP(), trackPar.getEta(), mapCollisionTime[cascade.collisionId()], mapCollisionTimeError[cascade.collisionId()]); + } + + template + float recalculateTOFNSigmaBachelorFromCascade(TCollision const& collision, TCascade const& cascade, const float massCascade, float& beta) + { + if (mapCollisionTime.find(cascade.collisionId()) == mapCollisionTime.end()) { + beta = -1.f; + return -999.f; + } + + auto bachelor = cascade.template bachelor_as(); + if (!bachelor.hasTOF() || !bachelor.has_collision()) { + beta = -1.f; + return -999.f; + } + + auto bcCascade = collision.template bc_as(); // collision in this function is the collision where this cascade belong. + auto collisionBachelor = bachelor.template collision_as(); + auto bcBachelor = collisionBachelor.template bc_as(); + double deltaTime = bachelor.tofSignalInAnotherBC(bcBachelor.globalBC(), bcCascade.globalBC()) - timeCascade(collision, cascade, massCascade); // in ps + + o2::track::TrackPar bTrackPar = o2::track::TrackPar({cascade.x(), cascade.y(), cascade.z()}, {cascade.pxbach(), cascade.pybach(), cascade.pzbach()}, cascade.sign(), false); // at SV + const o2::math_utils::Point3D trackVertex{collisionBachelor.posX(), collisionBachelor.posY(), collisionBachelor.posZ()}; + o2::track::TrackLTIntegral ltIntegral; + bool successPropag = o2::base::Propagator::Instance()->propagateToDCA(trackVertex, bTrackPar, d_bz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, nullptr, <Integral); + if (!successPropag) { + beta = -1.f; + return -999.f; + } + + float length = bachelor.length() - ltIntegral.getL(); + if (length < 0.f) { + beta = -1.f; + return -999.f; + } + + beta = length / (deltaTime - mapCollisionTime[cascade.collisionId()]) / (TMath::C() * 1e+2 * 1e-12); + return mTOFResponse->nSigma(deltaTime, bachelor.tofExpMom(), length, bTrackPar.getP(), bTrackPar.getEta(), mapCollisionTime[cascade.collisionId()], mapCollisionTimeError[cascade.collisionId()]); + } + + template + float recalculateTOFNSigmaLegFromV0(TCollision const& collision, TV0 const& v0, const float massV0, float& beta) + { + if (mapCollisionTime.find(v0.collisionId()) == mapCollisionTime.end()) { + beta = -1.f; + return -999.f; + } + + auto track = sign > 0 ? v0.template posTrack_as() : v0.template negTrack_as(); + if (!track.hasTOF() || !track.has_collision()) { + beta = -1.f; + return -999.f; + } + + auto bcV0 = collision.template bc_as(); // collision in this function is the collision where this v0 belong. + auto arrMomTrack = sign > 0 ? std::array{v0.pxpos(), v0.pypos(), v0.pzpos()} : std::array{v0.pxneg(), v0.pyneg(), v0.pzneg()}; + + auto collisionTrack = track.template collision_as(); + auto bcTrack = collisionTrack.template bc_as(); + double deltaTime = track.tofSignalInAnotherBC(bcTrack.globalBC(), bcV0.globalBC()) - timeV0(collision, v0, massV0); // in ps + + o2::track::TrackPar trackPar = o2::track::TrackPar({v0.x(), v0.y(), v0.z()}, arrMomTrack, track.sign(), false); // at SV + const o2::math_utils::Point3D trackVertex{collisionTrack.posX(), collisionTrack.posY(), collisionTrack.posZ()}; + o2::track::TrackLTIntegral ltIntegral; + bool successPropag = o2::base::Propagator::Instance()->propagateToDCA(trackVertex, trackPar, d_bz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrNONE, nullptr, <Integral); + if (!successPropag) { + beta = -1.f; + return -999.f; + } + + float length = track.length() - ltIntegral.getL(); // in cm + if (length < 0.f) { + beta = -1.f; + return -999.f; + } + beta = length / (deltaTime - mapCollisionTime[v0.collisionId()]) / (TMath::C() * 1e+2 * 1e-12); + return mTOFResponse->nSigma(deltaTime, track.tofExpMom(), length, trackPar.getP(), trackPar.getEta(), mapCollisionTime[v0.collisionId()], mapCollisionTimeError[v0.collisionId()]); + } + + template + float timeV0inCascade(TCascade const& cascade) + { + float p = RecoDecay::sqrtSumOfSquares(cascade.pxlambda(), cascade.pylambda(), cascade.pzlambda()); + float length = std::hypot(cascade.xlambda() - cascade.x(), cascade.ylambda() - cascade.y(), cascade.zlambda() - cascade.z()); // cm + float velocity = TMath::C() * 1e+2 * 1e-12 * std::sqrt((p * p) / (p * p + o2::constants::physics::MassLambda * o2::constants::physics::MassLambda)); // cm/ps + return length / velocity; // ps + } + + template + float timeCascade(TCollision const& collision, TCascade const& cascade, const float massCascade) + { + float length = std::hypot(cascade.x() - collision.posX(), cascade.y() - collision.posY(), cascade.z() - collision.posZ()); // cm + float velocity = TMath::C() * 1e+2 * 1e-12 * std::sqrt((cascade.p() * cascade.p()) / (cascade.p() * cascade.p() + massCascade * massCascade)); // cm/ps + return length / velocity; // ps + } + + template + float timeV0(TCollision const& collision, TV0 const& v0, const float massV0) + { + float length = std::hypot(v0.x() - collision.posX(), v0.y() - collision.posY(), v0.z() - collision.posZ()); // cm + float velocity = TMath::C() * 1e+2 * 1e-12 * std::sqrt((v0.p() * v0.p()) / (v0.p() * v0.p() + massV0 * massV0)); // cm/ps + return length / velocity; // ps + } + + template + void calculateTOFNSigmasForV0sAndCascades(TCollisions const& collisions, TBCs const&, TV0s const& v0s, TCascades const& cascades, TTracks const& tracks) + { + for (const auto& track : tracks) { + if (mapCollisionTime.find(track.collisionId()) == mapCollisionTime.end()) { + mapCollisionTime[track.collisionId()] = track.tofEvTime(); + mapCollisionTimeError[track.collisionId()] = track.tofEvTimeErr(); + } + } // end of track loop + + for (const auto& collision : collisions) { + if (!isSelectedCollision(collision)) { + continue; + } + if (eventCut.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + + auto v0s_coll = v0s.sliceBy(perCol_v0, collision.globalIndex()); + for (const auto& v0 : v0s_coll) { + auto pos = v0.template posTrack_as(); + auto neg = v0.template negTrack_as(); + + if (pos.sign() * neg.sign() > 0) { + continue; + } + if (!isSelectedV0Leg<0, true>(pos) || !isSelectedV0Leg<0, true>(neg)) { + continue; + } + + float beta = -1.f; + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : pos.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : pos.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : pos.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : neg.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : neg.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : neg.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : pos.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : pos.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : pos.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : neg.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : neg.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : neg.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : pos.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : pos.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : pos.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : neg.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : neg.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : neg.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : pos.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Photon, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : pos.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::K0, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : pos.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassPhoton, beta) : neg.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Photon, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassKaonNeutral, beta) : neg.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::K0, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaLegFromV0(collision, v0, o2::constants::physics::MassLambda, beta) : neg.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::Lambda, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + + } // end of v0 loop + + auto cascades_coll = cascades.sliceBy(perCol_casc, collision.globalIndex()); + for (const auto& cascade : cascades_coll) { + // Track casting + auto bachelor = cascade.template bachelor_as(); + auto pos = cascade.template posTrack_as(); + auto neg = cascade.template negTrack_as(); + if (pos.sign() * neg.sign() > 0) { + continue; + } + + if (!(cascadeCut.cfg_min_mass_lambda < cascade.mLambda() && cascade.mLambda() < cascadeCut.cfg_max_mass_lambda)) { + continue; + } + + if (!isSelectedV0Leg<1, true>(pos) || !isSelectedV0Leg<1, true>(neg) || !isSelectedV0Leg<2, true>(bachelor)) { + continue; + } + + // for bachelor tracks + float beta = -1.f; + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : bachelor.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : bachelor.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : bachelor.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : bachelor.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : bachelor.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : bachelor.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : bachelor.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaBachelorFromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : bachelor.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), bachelor.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : bachelor.beta(); + + // for tracks from lambda from cascade + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : pos.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : pos.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : pos.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : pos.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : neg.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : neg.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : neg.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassXiMinus, beta) : neg.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::XiMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : pos.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : pos.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : pos.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : pos.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), pos.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : pos.beta(); + + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : neg.tofNSigmaEl(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Electron)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : neg.tofNSigmaPi(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Pion)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : neg.tofNSigmaKa(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Kaon)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + mapTOFNSigma[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? recalculateTOFNSigmaTrackFromV0FromCascade(collision, cascade, o2::constants::physics::MassOmegaMinus, beta) : neg.tofNSigmaPr(); + mapTOFBeta[std::make_tuple(collision.globalIndex(), neg.globalIndex(), o2::track::PID::OmegaMinus, o2::track::PID::Proton)] = useTOFNSigmaDeltaBC ? beta : neg.beta(); + + } // end of cascade loop + } // end of collision loop + } + + std::map, float> mapTOFNsigmaPiReassociated; // map pair(collisionId, trackId) -> tof n sigma pi + std::map, float> mapTOFNsigmaKaReassociated; // map pair(collisionId, trackId) -> tof n sigma ka + std::map, float> mapTOFNsigmaPrReassociated; // map pair(collisionId, trackId) -> tof n sigma pr + std::map, float> mapTOFBetaReassociated; // map pair(collisionId, trackId) -> tof beta + std::unordered_map mapCollisionTime; + std::unordered_map mapCollisionTimeError; + + template + void calculateTOFNSigmaWithReassociation(TCollisions const& collisions, TBCs const&, TTracks const& tracks, TTrackAssoc const& trackIndices) + { + if (useTOFNSigmaDeltaBC) { + if constexpr (withTTCA) { + for (const auto& collision : collisions) { + if (mapCollisionTime.find(collision.globalIndex()) == mapCollisionTime.end()) { + continue; + } + auto bcCollision = collision.template bc_as(); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + + if (track.hasTOF() && track.has_collision()) { // TTCA may use orphan tracks. + auto bcTrack = track.template collision_as().template bc_as(); + float tofNSigmaPi = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + float tofNSigmaKa = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + float tofNSigmaPr = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + float beta = track.length() / (track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()) - mapCollisionTime[collision.globalIndex()]) / (TMath::C() * 1e+2 * 1e-12); + mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = tofNSigmaPi; + mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = tofNSigmaKa; + mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = tofNSigmaPr; + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = beta; + } else { + mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } + } // end of track loop + } // end of collision loop + } else { + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } + } // end of track loop + } // end of collision loop + } else { + if constexpr (withTTCA) { + for (const auto& collision : collisions) { + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } // end of track loop + } // end of collision loop + } else { + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } + } // end of track loop + } // end of collision loop + } + } + template void runPairing(TBCs const&, TCollisions const& collisions, TTracks const& tracks, TTrackAssoc const& trackIndices, TV0s const& v0s, TCascades const& cascades, TMCCollisions const&, TMCParticles const& mcParticles) { used_electronIds.reserve(tracks.size()); for (const auto& collision : collisions) { - const auto& bc = collision.template foundBC_as(); + auto bc = collision.template foundBC_as(); initCCDB(bc); - fRegistry.fill(HIST("Event/hCollisionCounter"), 0); if (!collision.has_mcCollision()) { continue; } + + auto mcCollision_from_collision = collision.template mcCollision_as(); + fRegistry.fill(HIST("Event/hCollisionCounter"), 0); + const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[eventCut.cfgCentEstimator] < eventCut.cfgCentMin || eventCut.cfgCentMax < centralities[eventCut.cfgCentEstimator]) { continue; } - fRegistry.fill(HIST("Event/hCollisionCounter"), 1); - const auto& mcCollision = collision.template mcCollision_as(); - if (eventCut.cfgEventGeneratorType < 0 || mcCollision.getSubGeneratorId() == eventCut.cfgEventGeneratorType) { - fillEventHistograms(collision); + + if (!isSelectedCollision(collision)) { + continue; } + if (eventCut.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + continue; + } + + fillEventHistograms(collision); + fRegistry.fill(HIST("Event/hCollisionCounter"), 1); mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); - const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); electronIds.reserve(trackIdsThisCollision.size()); positronIds.reserve(trackIdsThisCollision.size()); kaonPlusIds.reserve(trackIdsThisCollision.size()); kaonMinusIds.reserve(trackIdsThisCollision.size()); for (const auto& trackId : trackIdsThisCollision) { - const auto& track = trackId.template track_as(); + auto track = trackId.template track_as(); if (!track.hasITS() || !track.hasTPC()) { continue; } @@ -978,16 +1510,11 @@ struct taggingHFE { if (!track.has_mcParticle()) { continue; } - const auto& mcParticle = track.template mcParticle_as(); - const auto& mcCollision = mcParticle.template mcCollision_as(); - if (eventCut.cfgEventGeneratorType >= 0 && mcCollision.getSubGeneratorId() != eventCut.cfgEventGeneratorType) { - continue; - } - if (!mcParticle.has_mothers() || !(mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator())) { + auto mcParticle = track.template mcParticle_as(); + if (!mcParticle.has_mothers()) { continue; } - - fillElectronHistograms(track, mcParticles); + bool isPrimary = mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator(); auto trackParCov = getTrackParCov(track); mDcaInfoCov.set(999, 999, 999, 999, 999); @@ -996,9 +1523,16 @@ struct taggingHFE { float dcaXY = mDcaInfoCov.getY(); float dcaZ = mDcaInfoCov.getZ(); - if (isSelectedTrack(track, trackParCov, dcaXY, dcaZ) && std::abs(mcParticle.pdgCode()) == cfgPdgLepton) { - fRegistry.fill(HIST("Track/Electron/hTPCdEdx"), track.tpcInnerParam(), track.mcTunedTPCSignal()); - fRegistry.fill(HIST("Track/Electron/hTOFbeta"), track.p(), track.beta()); + if (isSelectedTrack(track, trackParCov, dcaXY, dcaZ) && std::abs(mcParticle.pdgCode()) == cfgPdgLepton && isPrimary) { // keep truely primary electrons + auto mcCollision_ele = mcParticle.template mcCollision_as(); + if (mcCollision_ele.getSubGeneratorId() == eventCut.cfgRejectEventGenerator) { + continue; + } + fRegistry.fill(HIST("Electron/hs"), trackParCov.getPt(), trackParCov.getEta(), RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U)); + fRegistry.fill(HIST("Electron/hDCA"), dcaXY, dcaZ); + fRegistry.fill(HIST("Electron/hTPCdEdx"), track.tpcInnerParam(), track.mcTunedTPCSignal()); + fRegistry.fill(HIST("Electron/hTOFbeta"), track.p(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + if (track.sign() > 0) { // positron positronIds.emplace_back(trackId.trackId()); } else { // electron @@ -1007,14 +1541,16 @@ struct taggingHFE { } mDcaInfoCov.set(999, 999, 999, 999, 999); - trackParCov.setPID(o2::track::PID::Kaon); + trackParCov.setPID(track.pidForTracking()); o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); dcaXY = mDcaInfoCov.getY(); dcaZ = mDcaInfoCov.getZ(); - if (isSelectedKaon(track, trackParCov, dcaXY, dcaZ)) { - fRegistry.fill(HIST("Track/Kaon/hTPCdEdx"), track.tpcInnerParam(), track.mcTunedTPCSignal()); - fRegistry.fill(HIST("Track/Kaon/hTOFbeta"), track.p(), track.beta()); + if (isSelectedHadron(collision, track, trackParCov, dcaXY, dcaZ)) { // electrons can be included in hadron sample. + fRegistry.fill(HIST("Hadron/hs"), trackParCov.getPt(), trackParCov.getEta(), RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U)); + fRegistry.fill(HIST("Hadron/hDCA"), dcaXY, dcaZ); + fRegistry.fill(HIST("Hadron/hTPCdEdx"), track.tpcInnerParam(), track.mcTunedTPCSignal()); + fRegistry.fill(HIST("Hadron/hTOFbeta"), track.p(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); if (track.sign() > 0) { // K+ kaonPlusIds.emplace_back(trackId.trackId()); } else { // K- @@ -1024,7 +1560,7 @@ struct taggingHFE { } // end of track loop for electron selection - const auto& v0s_per_coll = v0s.sliceBy(perCol_v0, collision.globalIndex()); + auto v0s_per_coll = v0s.sliceBy(perCol_v0, collision.globalIndex()); k0Ids.reserve(v0s_per_coll.size()); k0Ids.reserve(v0s_per_coll.size()); lambdaIds.reserve(v0s_per_coll.size()); @@ -1034,23 +1570,44 @@ struct taggingHFE { for (const auto& v0 : v0s_per_coll) { auto pos = v0.template posTrack_as(); auto neg = v0.template negTrack_as(); - if (!isSelectedV0Leg(pos) || !isSelectedV0Leg(neg)) { + if (!isSelectedV0Leg<0, isMC>(pos) || !isSelectedV0Leg<0, isMC>(neg)) { + continue; + } + + if (!checkITSibForV0Legs<0>(pos, neg)) { + continue; + } + + if (v0.dcaV0daughters() > v0Cut.cfg_max_dca2legs) { + continue; + } + + if (v0.v0radius() < v0Cut.cfg_min_radius) { continue; } - fillV0Histograms<>(v0); - if (isK0S(v0) && isPion(pos) && isPion(neg)) { + if (v0.v0cosPA() < v0Cut.cfg_min_cospa) { + continue; + } + + fillV0Histograms(collision, v0); + + if (std::sqrt(std::pow(v0.alpha() / v0Cut.cfg_max_alpha_veto, 2) + std::pow(v0.qtarm() / v0Cut.cfg_max_qt_veto, 2)) < 1.f) { // photon conversion rejection at small qT + continue; + } + + if (isK0S(v0) && isPionFromK0S(collision, pos) && isPionFromK0S(collision, neg)) { k0Ids.emplace_back(v0.globalIndex()); } - if (isLambda(v0) && isProton(pos) && isPion(neg)) { + if (isLambda(v0) && isProtonFromLambda(collision, pos) && isPionFromLambda(collision, neg)) { lambdaIds.emplace_back(v0.globalIndex()); - } else if (isAntiLambda(v0) && isProton(neg) && isPion(pos)) { + } else if (isAntiLambda(v0) && isProtonFromLambda(collision, neg) && isPionFromLambda(collision, pos)) { antilambdaIds.emplace_back(v0.globalIndex()); } } // end of V0 loop - const auto& cascades_per_coll = cascades.sliceBy(perCol_casc, collision.globalIndex()); + auto cascades_per_coll = cascades.sliceBy(perCol_casc, collision.globalIndex()); xiPlusIds.reserve(cascades_per_coll.size()); xiPlusIds.reserve(cascades_per_coll.size()); xiMinusIds.reserve(cascades_per_coll.size()); @@ -1070,18 +1627,11 @@ struct taggingHFE { continue; } - if (!isSelectedV0Leg(pos) || !isSelectedV0Leg(neg) || !isSelectedV0Leg(bachelor)) { + if (!isSelectedV0Leg<1, isMC>(pos) || !isSelectedV0Leg<1, isMC>(neg) || !isSelectedV0Leg<2, isMC>(bachelor)) { continue; } - - if (cascade.sign() < 0) { // L-> p pi- - if (!isProton(pos) || !isPion(neg)) { - continue; - } - } else { // Lbar-> pbar pi+ - if (!isProton(neg) || !isPion(pos)) { - continue; - } + if (!checkITSibForV0Legs<1>(pos, neg)) { + continue; } if (cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < cascadeCut.cfg_min_cospa) { @@ -1091,382 +1641,900 @@ struct taggingHFE { continue; } + if (cascade.cascradius() > cascade.v0radius()) { + continue; + } + + if (cascade.dcaV0daughters() > cascadeCut.cfg_max_dcadau_v0) { + continue; + } + if (cascade.v0radius() < cascadeCut.cfg_min_rxy_v0) { + continue; + } + if (cascade.cascradius() < cascadeCut.cfg_min_rxy) { + continue; + } + + if (cascade.dcacascdaughters() > cascadeCut.cfg_max_dcadau) { + continue; + } + + if (std::fabs(cascade.dcav0topv(collision.posX(), collision.posY(), collision.posZ())) < cascadeCut.cfg_min_dcaxy_v0) { + continue; + } + fillCascadeHistograms(collision, cascade); + if (cascade.sign() < 0) { // Xi- or Omega- - if (isXi(cascade) && isPion(bachelor)) { + if (isXi(cascade) && isPionFromXi(collision, bachelor) && isProtonFromLambdaFromXi(collision, pos) && isPionFromLambdaFromXi(collision, neg)) { xiMinusIds.emplace_back(cascade.globalIndex()); } - if (isOmega(cascade) && isKaonBachelor(bachelor)) { + if (isOmega(cascade) && isKaonFromOmega(collision, bachelor) && isProtonFromLambdaFromOmega(collision, pos) && isPionFromLambdaFromOmega(collision, neg)) { omegaMinusIds.emplace_back(cascade.globalIndex()); } } else { // Xi+ or Omega+ - if (isXi(cascade) && isPion(bachelor)) { + if (isXi(cascade) && isPionFromXi(collision, bachelor) && isProtonFromLambdaFromXi(collision, neg) && isPionFromLambdaFromXi(collision, pos)) { xiPlusIds.emplace_back(cascade.globalIndex()); } - if (isOmega(cascade) && isKaonBachelor(bachelor)) { + if (isOmega(cascade) && isKaonFromOmega(collision, bachelor) && isProtonFromLambdaFromOmega(collision, neg) && isPionFromLambdaFromOmega(collision, pos)) { omegaPlusIds.emplace_back(cascade.globalIndex()); } } } // end of cascade loop - // // if (electronIds.size() > 0 || positronIds.size() > 0) { - // if ((electronIds.size() > 0 || positronIds.size() > 0) && (xiMinusIds.size() > 0 || xiPlusIds.size() > 0)) { - // LOGF(info, "collision.globalIndex() = %d, electronIds.size() = %d, positronIds.size() = %d, kaonMinusIds.size() = %d, kaonPlusIds.size() = %d, k0Ids.size() = %d, lambdaIds.size() = %d, antilambdaIds.size() = %d, xiMinusIds.size() = %d, xiPlusIds.size() = %d, omegaMinusIds.size() = %d, omegaPlusIds.size() = %d", - // collision.globalIndex(), electronIds.size(), positronIds.size(), kaonMinusIds.size(), kaonPlusIds.size(), k0Ids.size(), lambdaIds.size(), antilambdaIds.size(), xiMinusIds.size(), xiPlusIds.size(), omegaMinusIds.size(), omegaPlusIds.size()); - // } - + int npos = 0; for (const auto& positronId : positronIds) { - const auto& pos = tracks.rawIteratorAt(positronId); + auto pos = tracks.rawIteratorAt(positronId); mDcaInfoCov.set(999, 999, 999, 999, 999); auto leptonParCov = getTrackParCov(pos); leptonParCov.setPID(o2::track::PID::Electron); o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, leptonParCov, 2.f, matCorr, &mDcaInfoCov); float dcaXY_lepton = mDcaInfoCov.getY(); float dcaZ_lepton = mDcaInfoCov.getZ(); - float dca3DinSigma_lepton = dca3DinSigmaOTF(dcaXY_lepton, dcaZ_lepton, leptonParCov.getSigmaY2(), leptonParCov.getSigmaZ2(), leptonParCov.getSigmaZY()); - const auto& mcpos = pos.template mcParticle_as(); + + auto mcpos = pos.template mcParticle_as(); + auto mcMother = mcpos.template mothers_as()[0]; + bool isMotherFromB = IsFromBeauty(mcMother, mcParticles) > -1; + auto mcCollision_mcpos = mcpos.template mcCollision_as(); + bool isCorrectCollision = mcCollision_mcpos.globalIndex() == mcCollision_from_collision.globalIndex(); + + bool is_e_from_dy = std::abs(mcMother.pdgCode()) == 23; // virtual photon is Z in simulation. + bool is_e_from_jpsi = std::abs(mcMother.pdgCode()) == 443; // e from prompt J/psi is treated as the same as Z. // e from nonprompt J/psi is treated as the same as B. + bool is_e_from_hc = (std::abs(mcMother.pdgCode()) == 411 || std::abs(mcMother.pdgCode()) == 421 || std::abs(mcMother.pdgCode()) == 431 || std::abs(mcMother.pdgCode()) == 4122 || std::abs(mcMother.pdgCode()) == 4132 || std::abs(mcMother.pdgCode()) == 4232 || std::abs(mcMother.pdgCode()) == 4332) && isSemiLeptonic(mcMother, mcParticles, -cfgPdgLepton, cfgPdgLepton + 1); + bool is_e_from_hb = (std::abs(mcMother.pdgCode()) == 511 || std::abs(mcMother.pdgCode()) == 521 || std::abs(mcMother.pdgCode()) == 531 || std::abs(mcMother.pdgCode()) == 541 || std::abs(mcMother.pdgCode()) == 5122 || std::abs(mcMother.pdgCode()) == 5132 || std::abs(mcMother.pdgCode()) == 5232 || std::abs(mcMother.pdgCode()) == 5332) && isSemiLeptonic(mcMother, mcParticles, -cfgPdgLepton, cfgPdgLepton + 1); + if (!(is_e_from_dy || is_e_from_jpsi || is_e_from_hc || is_e_from_hb)) { + continue; + } + + if ((is_e_from_dy || is_e_from_jpsi) && dist01(engine) > cfgDownSampling) { // random sampling, if necessary + continue; + } + + npos++; + leptonTable(eventTable.lastIndex() + 1, mcCollision_mcpos.getSubGeneratorId(), + leptonParCov.getQ2Pt(), leptonParCov.getEta(), RecoDecay::constrainAngle(leptonParCov.getPhi(), 0, 1U), dcaXY_lepton, dcaZ_lepton, leptonParCov.getSigmaY2(), leptonParCov.getSigmaZY(), leptonParCov.getSigmaZ2(), + isMotherFromB, mcMother.pdgCode(), mcMother.globalIndex(), isCorrectCollision); // D0 -> e+ nu_e K-, br = 0.03538, ctau = 123.01 um, m = 1864 MeV/c2 for (const auto& kaonId : kaonMinusIds) { - const auto& kaon = tracks.rawIteratorAt(kaonId); + auto kaon = tracks.rawIteratorAt(kaonId); mDcaInfoCov.set(999, 999, 999, 999, 999); auto trackParCov = getTrackParCov(kaon); - trackParCov.setPID(o2::track::PID::Kaon); + trackParCov.setPID(kaon.pidForTracking()); o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); float dcaXY_kaon = mDcaInfoCov.getY(); float dcaZ_kaon = mDcaInfoCov.getZ(); - float dca3DinSigma_kaon = dca3DinSigmaOTF(dcaXY_kaon, dcaZ_kaon, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()); if (positronId == kaonId) { continue; } - const auto& eKpair = o2::aod::pwgem::dilepton::utils::makePairLeptonTrack(fitter_eK, collision, pos, kaon, o2::track::PID::Electron, o2::track::PID::Kaon); + auto eKpair = o2::aod::pwgem::dilepton::utils::makePairLeptonTrack(fitter_eK, collision, pos, kaon, o2::track::PID::Electron, kaon.pidForTracking(), o2::constants::physics::MassElectron); if (!eKpair.isOK) { continue; } - if (!(lKPairCut.cfg_min_mass < eKpair.mass && eKpair.mass < lKPairCut.cfg_max_mass) || eKpair.cospa < lKPairCut.cfg_min_cospa || lKPairCut.cfg_max_lxyz < eKpair.lxyz / eKpair.lxyzErr || lKPairCut.cfg_max_dca2legs < eKpair.dca2legs) { + if (eKpair.cospa < lKPairCut.cfg_min_cospa || lKPairCut.cfg_max_lxyz < eKpair.lxyz || lKPairCut.cfg_max_chi2PCA < eKpair.chi2PCA || lKPairCut.cfg_max_massLH < eKpair.mass) { + continue; + } + + int pdgCodeIM = 0; + auto mckaon = kaon.template mcParticle_as(); + bool foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mckaon) > 0; + if (mckaon.has_mothers() && !foundCommonMother) { + auto mcMother_of_kaon = mckaon.template mothers_first_as(); + if (mcMother_of_kaon.pdgCode() == -323 || mcMother_of_kaon.pdgCode() == -313 || mcMother_of_kaon.pdgCode() == 333 || mcMother_of_kaon.pdgCode() == -413) { // accept short-lived resonances such as phi->KK, K*(892)->Kpi for D+ -> anti-K*0(892) e+ nu_e -> (K- pi+) e+ nu_e and Ds+ -> phi e+ nu_e + int commonMotherId = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mcMother_of_kaon); + if (commonMotherId > 0) { + foundCommonMother = true; + pdgCodeIM = mcMother_of_kaon.pdgCode(); + } + } + } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && foundCommonMother && std::abs(mckaon.pdgCode()) == cfgPdgLepton))) { + // I want 3 types. + // 1. truely found HF->eh (SV should found by eh, and truely found.) For signal sample in ML. + // 2. mistakenly found DY->eh (SV should not be found by eh, but found.) For bkg sample in ML. + // 3. truely found DY->ee with misidentified ee. (SV may be found at the same position of PV.) For bkg sample in ML. + continue; + } + + // float tofNSigmaPi = mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + float tofNSigmaKa = mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + // float tofNSigmaPr = mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + + emmlltpair(leptonTable.lastIndex(), + trackParCov.getQ2Pt(), trackParCov.getEta(), dcaXY_kaon, dcaZ_kaon, trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), + // kaon.tpcNSigmaPi(), tofNSigmaPi, + kaon.tpcNSigmaKa(), tofNSigmaKa, + // kaon.tpcNSigmaPr(), tofNSigmaPr, + eKpair.mass, /*eKpair.p, eKpair.deta, eKpair.dphi,*/ + eKpair.chi2PCA, eKpair.cospa, eKpair.cospaXY, eKpair.cospaRZ, + eKpair.lxy, eKpair.lz, eKpair.lxyz, eKpair.lxyErr, eKpair.lzErr, eKpair.lxyzErr, + mckaon.pdgCode(), pdgCodeIM, foundCommonMother); + + } // end of kaon loop + + for (const auto& kaonId : kaonPlusIds) { + auto kaon = tracks.rawIteratorAt(kaonId); + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(kaon); + trackParCov.setPID(kaon.pidForTracking()); + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + float dcaXY_kaon = mDcaInfoCov.getY(); + float dcaZ_kaon = mDcaInfoCov.getZ(); + + if (positronId == kaonId) { + continue; + } + + auto eKpair = o2::aod::pwgem::dilepton::utils::makePairLeptonTrack(fitter_eK, collision, pos, kaon, o2::track::PID::Electron, kaon.pidForTracking(), o2::constants::physics::MassElectron); + if (!eKpair.isOK) { + continue; + } + if (eKpair.cospa < lKPairCut.cfg_min_cospa || lKPairCut.cfg_max_lxyz < eKpair.lxyz || lKPairCut.cfg_max_chi2PCA < eKpair.chi2PCA || lKPairCut.cfg_max_massLH < eKpair.mass) { continue; } - fRegistry.fill(HIST("Data/eK/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, trackParCov.getPt(), dca3DinSigma_kaon, eKpair.mass, eKpair.lxyz / eKpair.lxyzErr, eKpair.cospa, eKpair.dca2legs); + int pdgCodeIM = 0; + auto mckaon = kaon.template mcParticle_as(); + bool foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mckaon) > 0; + if (mckaon.has_mothers() && !foundCommonMother) { + auto mcMother_of_kaon = mckaon.template mothers_first_as(); + if (mcMother_of_kaon.pdgCode() == -323 || mcMother_of_kaon.pdgCode() == -313 || mcMother_of_kaon.pdgCode() == 333 || mcMother_of_kaon.pdgCode() == -413) { // accept short-lived resonances such as phi->KK, K*(892)->Kpi for D+ -> anti-K*0(892) e+ nu_e -> (K- pi+) e+ nu_e and Ds+ -> phi e+ nu_e + int commonMotherId = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mcMother_of_kaon); + if (commonMotherId > 0) { + foundCommonMother = true; + pdgCodeIM = mcMother_of_kaon.pdgCode(); + } + } + } - const auto& mckaon = kaon.template mcParticle_as(); - int mcD0Id = FindCommonMotherFrom2Prongs(mcpos, mckaon, -11, -321, 421, mcParticles); - int mcDpmId = FindCommonMotherFrom2Prongs(mcpos, mckaon, -11, -321, 411, mcParticles); - if (mcD0Id > 0) { // true D0 - // const auto& mcD0 = mcParticles.rawIteratorAt(mcD0Id); - fRegistry.fill(HIST("MC/eKfromD0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, trackParCov.getPt(), dca3DinSigma_kaon, eKpair.mass, eKpair.lxyz / eKpair.lxyzErr, eKpair.cospa, eKpair.dca2legs); - } else if (mcDpmId > 0) { // true D+ - // const auto& mcD0 = mcParticles.rawIteratorAt(mcD0Id); - fRegistry.fill(HIST("MC/eKfromDpm/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, trackParCov.getPt(), dca3DinSigma_kaon, eKpair.mass, eKpair.lxyz / eKpair.lxyzErr, eKpair.cospa, eKpair.dca2legs); + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && foundCommonMother && std::abs(mckaon.pdgCode()) == cfgPdgLepton))) { + // I want 3 types. + // 1. truely found HF->eh (SV should found by eh, and truely found.) For signal sample in ML. + // 2. mistakenly found DY->eh (SV should not be found by eh, but found.) For bkg sample in ML. + // 3. truely found DY->ee with misidentified ee. (SV may be found at the same position of PV.) For bkg sample in ML. + continue; } + + // float tofNSigmaPi = mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + float tofNSigmaKa = mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + // float tofNSigmaPr = mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + + emmlltpair(leptonTable.lastIndex(), + trackParCov.getQ2Pt(), trackParCov.getEta(), dcaXY_kaon, dcaZ_kaon, trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), + // kaon.tpcNSigmaPi(), tofNSigmaPi, + kaon.tpcNSigmaKa(), tofNSigmaKa, + // kaon.tpcNSigmaPr(), tofNSigmaPr, + eKpair.mass, /*eKpair.p, eKpair.deta, eKpair.dphi,*/ + eKpair.chi2PCA, eKpair.cospa, eKpair.cospaXY, eKpair.cospaRZ, + eKpair.lxy, eKpair.lz, eKpair.lxyz, eKpair.lxyErr, eKpair.lzErr, eKpair.lxyzErr, + mckaon.pdgCode(), pdgCodeIM, foundCommonMother); + } // end of kaon loop // D+ -> e+ K0S nu_e for (const auto& k0Id : k0Ids) { - const auto& v0 = v0s.rawIteratorAt(k0Id); - float lxyz_v0 = std::sqrt(std::pow(v0.x() - collision.posX(), 2) + std::pow(v0.y() - collision.posY(), 2) + std::pow(v0.z() - collision.posZ(), 2)); - const auto& eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, pos, v0, o2::track::PID::Electron, o2::track::PID::K0); + auto v0 = v0s.rawIteratorAt(k0Id); + if (v0.posTrackId() == positronId || v0.negTrackId() == positronId) { + continue; + } + + const std::array vertexV0 = {v0.x(), v0.y(), v0.z()}; + const std::array momV0 = {v0.px(), v0.py(), v0.pz()}; + std::array covV0 = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covV0[MomInd[i]] = v0.momentumCovMat()[i]; + covV0[i] = v0.positionCovMat()[i]; + } + auto trackV0 = o2::track::TrackParCov(vertexV0, momV0, covV0, 0, true); + trackV0.setAbsCharge(0); + trackV0.setPID(o2::track::PID::K0); + o2::dataformats::DCA impactParameterV0; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackV0, 2.f, matCorr, &impactParameterV0); // trackV0 is TrackParCov object + + auto eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, pos, v0, o2::track::PID::Electron, o2::track::PID::K0, o2::constants::physics::MassElectron); if (!eV0pair.isOK) { continue; } - if (!(lV0PairCut.cfg_min_mass < eV0pair.mass && eV0pair.mass < lV0PairCut.cfg_max_mass) || eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz / eV0pair.lxyzErr || lV0PairCut.cfg_max_dca2legs < eV0pair.dca2legs) { + if (eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz || lV0PairCut.cfg_max_chi2PCA < eV0pair.chi2PCA || lV0PairCut.cfg_max_massLH < eV0pair.mass) { continue; } - fRegistry.fill(HIST("Data/eK0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); auto posLeg = v0.template posTrack_as(); auto negLeg = v0.template negTrack_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); - int mcK0Id = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 211, -211, 310, mcParticles); - if (mcK0Id > 0) { // true K0S - const auto& mcK0 = mcParticles.rawIteratorAt(mcK0Id); - int mcDpmId = FindCommonMotherFrom2Prongs(mcpos, mcK0, -11, 310, 411, mcParticles); - if (mcDpmId > 0) { // true D+ - // const auto& mcDpm = mcParticles.rawIteratorAt(mcDpmId); - fRegistry.fill(HIST("MC/eK0fromDpm/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); + int mcK0SId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 211, -211, 310, mcParticles); + int pdgCodeV0 = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; + if (mcK0SId > 0) { // true K0S + pdgCodeV0 = 310; + auto mcK0S = mcParticles.rawIteratorAt(mcK0SId); + auto mcK0 = mcK0S.template mothers_first_as(); // mother of K0S is K0 in simulation. + // LOGF(info, "mcK0S.pdgCode() = %d, mcK0.pdgCode() = %d", mcK0S.pdgCode(), mcK0.pdgCode()); + if (std::abs(mcK0.pdgCode()) == 311) { + mcK0S = mcK0; } + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mcK0S) > 0; + + if (mcK0S.has_mothers() && !foundCommonMother) { + auto mcMother_of_k0s = mcK0S.template mothers_first_as(); + if (mcMother_of_k0s.pdgCode() == -323 || mcMother_of_k0s.pdgCode() == -313 || mcMother_of_k0s.pdgCode() == 333) { // accept short-lived resonances such as phi->KK, K*(892)->Kpi for D+ -> anti-K*0(892) e+ nu_e -> (K- pi+) e+ nu_e and Ds+ -> phi e+ nu_e + int commonMotherId = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mcMother_of_k0s); + if (commonMotherId > 0) { + foundCommonMother = true; + pdgCodeIM = mcMother_of_k0s.pdgCode(); + } + } + } + } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; } + + emmllv0pair(leptonTable.lastIndex(), 0, + v0.pt(), v0.rapidity(0), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + impactParameterV0.getY(), impactParameterV0.getZ(), impactParameterV0.getSigmaY2(), impactParameterV0.getSigmaYZ(), impactParameterV0.getSigmaZ2(), + eV0pair.mass, /*eV0pair.p, eV0pair.deta, eV0pair.dphi,*/ + eV0pair.chi2PCA, eV0pair.cospa, eV0pair.cospaXY, eV0pair.cospaRZ, + eV0pair.lxy, eV0pair.lz, eV0pair.lxyz, eV0pair.lxyErr, eV0pair.lzErr, eV0pair.lxyzErr, + pdgCodeV0, pdgCodeIM, foundCommonMother); + } // end of K0S loop // Lc+ -> e+ Lambda nu_e, br = 0.0356, ctau = 60.75 um, m = 2286 MeV/c2 for (const auto& lambdaId : lambdaIds) { - const auto& v0 = v0s.rawIteratorAt(lambdaId); - float lxyz_v0 = std::sqrt(std::pow(v0.x() - collision.posX(), 2) + std::pow(v0.y() - collision.posY(), 2) + std::pow(v0.z() - collision.posZ(), 2)); - const auto& eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, pos, v0, o2::track::PID::Electron, o2::track::PID::Lambda); + auto v0 = v0s.rawIteratorAt(lambdaId); + if (v0.posTrackId() == positronId || v0.negTrackId() == positronId) { + continue; + } + + const std::array vertexV0 = {v0.x(), v0.y(), v0.z()}; + const std::array momV0 = {v0.px(), v0.py(), v0.pz()}; + std::array covV0 = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covV0[MomInd[i]] = v0.momentumCovMat()[i]; + covV0[i] = v0.positionCovMat()[i]; + } + auto trackV0 = o2::track::TrackParCov(vertexV0, momV0, covV0, 0, true); + trackV0.setAbsCharge(0); + trackV0.setPID(o2::track::PID::Lambda); + o2::dataformats::DCA impactParameterV0; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackV0, 2.f, matCorr, &impactParameterV0); // trackV0 is TrackParCov object + + auto eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, pos, v0, o2::track::PID::Electron, o2::track::PID::Lambda, o2::constants::physics::MassElectron); if (!eV0pair.isOK) { continue; } - if (!(lV0PairCut.cfg_min_mass < eV0pair.mass && eV0pair.mass < lV0PairCut.cfg_max_mass) || eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz / eV0pair.lxyzErr || lV0PairCut.cfg_max_dca2legs < eV0pair.dca2legs) { + if (eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz || lV0PairCut.cfg_max_chi2PCA < eV0pair.chi2PCA || lV0PairCut.cfg_max_massLH < eV0pair.mass) { continue; } - fRegistry.fill(HIST("Data/eL/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); auto posLeg = v0.template posTrack_as(); auto negLeg = v0.template negTrack_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); int mcLambdaId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 2212, -211, 3122, mcParticles); + int pdgCodeV0 = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; if (mcLambdaId > 0) { // true v0 - const auto& mcLambda = mcParticles.rawIteratorAt(mcLambdaId); - int mcLambdacId = FindCommonMotherFrom2Prongs(mcpos, mcLambda, -11, 3122, 4122, mcParticles); - if (mcLambdacId > 0) { // true Lc0 - // const auto& mcLambdac0 = mcParticles.rawIteratorAt(mcLambdacId); - fRegistry.fill(HIST("MC/eLfromLcpm/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); - } + auto mcLambda = mcParticles.rawIteratorAt(mcLambdaId); + pdgCodeV0 = mcLambda.pdgCode(); + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mcLambda) > 0; + } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; } + + emmllv0pair(leptonTable.lastIndex(), 1, + v0.pt(), v0.rapidity(1), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + impactParameterV0.getY(), impactParameterV0.getZ(), impactParameterV0.getSigmaY2(), impactParameterV0.getSigmaYZ(), impactParameterV0.getSigmaZ2(), + eV0pair.mass, /*eV0pair.p, eV0pair.deta, eV0pair.dphi,*/ + eV0pair.chi2PCA, eV0pair.cospa, eV0pair.cospaXY, eV0pair.cospaRZ, + eV0pair.lxy, eV0pair.lz, eV0pair.lxyz, eV0pair.lxyErr, eV0pair.lzErr, eV0pair.lxyzErr, + pdgCodeV0, pdgCodeIM, foundCommonMother); + } // end of Lambda loop for (const auto& cascadeId : xiMinusIds) { - const auto& cascade = cascades.rawIteratorAt(cascadeId); - float lxyz_cascade = std::sqrt(std::pow(cascade.x() - collision.posX(), 2) + std::pow(cascade.y() - collision.posY(), 2) + std::pow(cascade.z() - collision.posZ(), 2)); - const auto& eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, pos, cascade, o2::track::PID::Electron, o2::track::PID::XiMinus); + auto cascade = cascades.rawIteratorAt(cascadeId); + if (cascade.posTrackId() == positronId || cascade.negTrackId() == positronId || cascade.bachelorId() == positronId) { + continue; + } + + const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + std::array covCasc = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + covCasc[i] = cascade.positionCovMat()[i]; + } + auto trackCasc = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + trackCasc.setAbsCharge(1); + trackCasc.setPID(o2::track::PID::XiMinus); + o2::dataformats::DCA impactParameterCasc; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackCasc, 2.f, matCorr, &impactParameterCasc); // trackCasc is TrackParCov object + + auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, pos, cascade, o2::track::PID::Electron, o2::track::PID::XiMinus, o2::constants::physics::MassElectron); if (!eCpair.isOK) { continue; } - if (!(lCPairCut.cfg_min_mass < eCpair.mass && eCpair.mass < lCPairCut.cfg_max_mass) || eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz / eCpair.lxyzErr || lCPairCut.cfg_max_dca2legs < eCpair.dca2legs) { + if (eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz || lCPairCut.cfg_max_chi2PCA < eCpair.chi2PCA || lCPairCut.cfg_max_massLH < eCpair.mass) { continue; } - fRegistry.fill(HIST("Data/eXi/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); auto posLeg = cascade.template posTrack_as(); auto negLeg = cascade.template negTrack_as(); auto bachelor = cascade.template bachelor_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); - const auto& mcbachelor = bachelor.template mcParticle_as(); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); + auto mcbachelor = bachelor.template mcParticle_as(); int mcLambdaId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 2212, -211, 3122, mcParticles); + int pdgCodeCascade = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; if (mcLambdaId > 0) { // true Lambda - const auto& mcLambda = mcParticles.rawIteratorAt(mcLambdaId); + auto mcLambda = mcParticles.rawIteratorAt(mcLambdaId); int mcXiId = FindCommonMotherFrom2Prongs(mcLambda, mcbachelor, 3122, -211, 3312, mcParticles); if (mcXiId > 0) { // true xiMinus - const auto& mcXi = mcParticles.rawIteratorAt(mcXiId); - int mcXic0Id = FindCommonMotherFrom2Prongs(mcpos, mcXi, -11, 3312, 4132, mcParticles); - if (mcXic0Id > 0) { // true Xic0 - // const auto& mcXic0 = mcParticles.rawIteratorAt(mcXic0Id); - fRegistry.fill(HIST("MC/eXifromXic0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); - } + auto mcXi = mcParticles.rawIteratorAt(mcXiId); + pdgCodeCascade = mcXi.pdgCode(); + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mcXi) > 0; } } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; + } + + emmllcascpair(leptonTable.lastIndex(), 0, + cascade.sign() / cascade.pt(), cascade.rapidity(0), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + impactParameterCasc.getY(), impactParameterCasc.getZ(), impactParameterCasc.getSigmaY2(), impactParameterCasc.getSigmaYZ(), impactParameterCasc.getSigmaZ2(), + eCpair.mass, /*eCpair.p, eCpair.deta, eCpair.dphi,*/ + eCpair.chi2PCA, eCpair.cospa, eCpair.cospaXY, eCpair.cospaRZ, + eCpair.lxy, eCpair.lz, eCpair.lxyz, eCpair.lxyErr, eCpair.lzErr, eCpair.lxyzErr, + pdgCodeCascade, pdgCodeIM, foundCommonMother); + } // end of Xi- loop for (const auto& cascadeId : omegaMinusIds) { - const auto& cascade = cascades.rawIteratorAt(cascadeId); - float lxyz_cascade = std::sqrt(std::pow(cascade.x() - collision.posX(), 2) + std::pow(cascade.y() - collision.posY(), 2) + std::pow(cascade.z() - collision.posZ(), 2)); - const auto& eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, pos, cascade, o2::track::PID::Electron, o2::track::PID::OmegaMinus); + auto cascade = cascades.rawIteratorAt(cascadeId); + if (cascade.posTrackId() == positronId || cascade.negTrackId() == positronId || cascade.bachelorId() == positronId) { + continue; + } + + const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + std::array covCasc = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + covCasc[i] = cascade.positionCovMat()[i]; + } + auto trackCasc = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + trackCasc.setAbsCharge(1); + trackCasc.setPID(o2::track::PID::OmegaMinus); + o2::dataformats::DCA impactParameterCasc; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackCasc, 2.f, matCorr, &impactParameterCasc); // trackCasc is TrackParCov object + + auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, pos, cascade, o2::track::PID::Electron, o2::track::PID::OmegaMinus, o2::constants::physics::MassElectron); if (!eCpair.isOK) { continue; } - if (!(lCPairCut.cfg_min_mass < eCpair.mass && eCpair.mass < lCPairCut.cfg_max_mass) || eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz / eCpair.lxyzErr || lCPairCut.cfg_max_dca2legs < eCpair.dca2legs) { + if (eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz || lCPairCut.cfg_max_chi2PCA < eCpair.chi2PCA || lCPairCut.cfg_max_massLH < eCpair.mass) { continue; } - fRegistry.fill(HIST("Data/eOmega/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); auto posLeg = cascade.template posTrack_as(); auto negLeg = cascade.template negTrack_as(); auto bachelor = cascade.template bachelor_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); - const auto& mcbachelor = bachelor.template mcParticle_as(); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); + auto mcbachelor = bachelor.template mcParticle_as(); int mcLambdaId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 2212, -211, 3122, mcParticles); + int pdgCodeCascade = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; if (mcLambdaId > 0) { // true Lambda - const auto& mcLambda = mcParticles.rawIteratorAt(mcLambdaId); + auto mcLambda = mcParticles.rawIteratorAt(mcLambdaId); int mcOmegaId = FindCommonMotherFrom2Prongs(mcLambda, mcbachelor, 3122, -321, 3334, mcParticles); if (mcOmegaId > 0) { // true omegaMinus - const auto& mcOmega = mcParticles.rawIteratorAt(mcOmegaId); - int mcOmegac0Id = FindCommonMotherFrom2Prongs(mcpos, mcOmega, -11, 3334, 4332, mcParticles); - if (mcOmegac0Id > 0) { // true Omegac0 - // const auto& mcOmegac0 = mcParticles.rawIteratorAt(mcOmegac0Id); - fRegistry.fill(HIST("MC/eOmegafromOmegac0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); - } + auto mcOmega = mcParticles.rawIteratorAt(mcOmegaId); + pdgCodeCascade = mcOmega.pdgCode(); + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcpos, mcOmega) > 0; } } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; + } + + emmllcascpair(leptonTable.lastIndex(), 1, + cascade.sign() / cascade.pt(), cascade.rapidity(2), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + impactParameterCasc.getY(), impactParameterCasc.getZ(), impactParameterCasc.getSigmaY2(), impactParameterCasc.getSigmaYZ(), impactParameterCasc.getSigmaZ2(), + eCpair.mass, /*eCpair.p, eCpair.deta, eCpair.dphi,*/ + eCpair.chi2PCA, eCpair.cospa, eCpair.cospaXY, eCpair.cospaRZ, + eCpair.lxy, eCpair.lz, eCpair.lxyz, eCpair.lxyErr, eCpair.lzErr, eCpair.lxyzErr, + pdgCodeCascade, pdgCodeIM, foundCommonMother); + } // end of Omega- loop } // end of main positron sample + int nele = 0; for (const auto& electronId : electronIds) { - const auto& ele = tracks.rawIteratorAt(electronId); + auto ele = tracks.rawIteratorAt(electronId); mDcaInfoCov.set(999, 999, 999, 999, 999); auto leptonParCov = getTrackParCov(ele); leptonParCov.setPID(o2::track::PID::Electron); o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, leptonParCov, 2.f, matCorr, &mDcaInfoCov); float dcaXY_lepton = mDcaInfoCov.getY(); float dcaZ_lepton = mDcaInfoCov.getZ(); - float dca3DinSigma_lepton = dca3DinSigmaOTF(dcaXY_lepton, dcaZ_lepton, leptonParCov.getSigmaY2(), leptonParCov.getSigmaZ2(), leptonParCov.getSigmaZY()); - const auto& mcele = ele.template mcParticle_as(); + + auto mcele = ele.template mcParticle_as(); + auto mcMother = mcele.template mothers_as()[0]; + bool isMotherFromB = IsFromBeauty(mcMother, mcParticles) > -1; + auto mcCollision_mcele = mcele.template mcCollision_as(); + bool isCorrectCollision = mcCollision_mcele.globalIndex() == mcCollision_from_collision.globalIndex(); + + bool is_e_from_dy = std::abs(mcMother.pdgCode()) == 23; // virtual photon is Z in simulation. + bool is_e_from_jpsi = std::abs(mcMother.pdgCode()) == 443; // e from prompt J/psi is treated as the same as Z. // e from nonprompt J/psi is treated as the same as B. // B+ -> J/psi K+ -> e+ e- K+ + bool is_e_from_hc = (std::abs(mcMother.pdgCode()) == 411 || std::abs(mcMother.pdgCode()) == 421 || std::abs(mcMother.pdgCode()) == 431 || std::abs(mcMother.pdgCode()) == 4122 || std::abs(mcMother.pdgCode()) == 4132 || std::abs(mcMother.pdgCode()) == 4232 || std::abs(mcMother.pdgCode()) == 4332) && isSemiLeptonic(mcMother, mcParticles, cfgPdgLepton, -cfgPdgLepton - 1); + bool is_e_from_hb = (std::abs(mcMother.pdgCode()) == 511 || std::abs(mcMother.pdgCode()) == 521 || std::abs(mcMother.pdgCode()) == 531 || std::abs(mcMother.pdgCode()) == 541 || std::abs(mcMother.pdgCode()) == 5122 || std::abs(mcMother.pdgCode()) == 5132 || std::abs(mcMother.pdgCode()) == 5232 || std::abs(mcMother.pdgCode()) == 5332) && isSemiLeptonic(mcMother, mcParticles, cfgPdgLepton, -cfgPdgLepton - 1); + if (!(is_e_from_dy || is_e_from_jpsi || is_e_from_hc || is_e_from_hb)) { + continue; + } + + if ((is_e_from_dy || is_e_from_jpsi) && dist01(engine) > cfgDownSampling) { // random sampling, if necessary + continue; + } + + nele++; + leptonTable(eventTable.lastIndex() + 1, mcCollision_mcele.getSubGeneratorId(), + leptonParCov.getQ2Pt(), leptonParCov.getEta(), RecoDecay::constrainAngle(leptonParCov.getPhi(), 0, 1U), dcaXY_lepton, dcaZ_lepton, leptonParCov.getSigmaY2(), leptonParCov.getSigmaZY(), leptonParCov.getSigmaZ2(), + isMotherFromB, mcMother.pdgCode(), mcMother.globalIndex(), isCorrectCollision); + + for (const auto& kaonId : kaonMinusIds) { + auto kaon = tracks.rawIteratorAt(kaonId); + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(kaon); + trackParCov.setPID(kaon.pidForTracking()); + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + float dcaXY_kaon = mDcaInfoCov.getY(); + float dcaZ_kaon = mDcaInfoCov.getZ(); + + if (electronId == kaonId) { + continue; + } + + auto eKpair = o2::aod::pwgem::dilepton::utils::makePairLeptonTrack(fitter_eK, collision, ele, kaon, o2::track::PID::Electron, kaon.pidForTracking(), o2::constants::physics::MassElectron); + if (!eKpair.isOK) { + continue; + } + + if (eKpair.cospa < lKPairCut.cfg_min_cospa || lKPairCut.cfg_max_lxyz < eKpair.lxyz || lKPairCut.cfg_max_chi2PCA < eKpair.chi2PCA || lKPairCut.cfg_max_massLH < eKpair.mass) { + continue; + } + + int pdgCodeIM = 0; + auto mckaon = kaon.template mcParticle_as(); + bool foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mckaon) > 0; + if (mckaon.has_mothers() && !foundCommonMother) { + auto mcMother_of_kaon = mckaon.template mothers_first_as(); + if (mcMother_of_kaon.pdgCode() == 323 || mcMother_of_kaon.pdgCode() == 313 || mcMother_of_kaon.pdgCode() == 333 || mcMother_of_kaon.pdgCode() == 413) { // accept short-lived resonances such as phi->KK, K*(892)->Kpi for D+ -> anti-K*0(892) e+ nu_e -> (K- pi+) e+ nu_e and Ds+ -> phi e+ nu_e + int commonMotherId = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mcMother_of_kaon); + if (commonMotherId > 0) { + foundCommonMother = true; + pdgCodeIM = mcMother_of_kaon.pdgCode(); + } + } + } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && foundCommonMother && std::abs(mckaon.pdgCode()) == cfgPdgLepton))) { + // I want 3 types. + // 1. truely found HF->eh (SV should found by eh, and truely found.) For signal sample in ML. + // 2. mistakenly found DY->eh (SV should not be found by eh, but found.) For bkg sample in ML. + // 3. truely found DY->ee with misidentified ee. (SV may be found at the same position of PV.) For bkg sample in ML. + continue; + } + + // float tofNSigmaPi = mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + float tofNSigmaKa = mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + // float tofNSigmaPr = mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + + emmlltpair(leptonTable.lastIndex(), + trackParCov.getQ2Pt(), trackParCov.getEta(), dcaXY_kaon, dcaZ_kaon, trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), + // kaon.tpcNSigmaPi(), tofNSigmaPi, + kaon.tpcNSigmaKa(), tofNSigmaKa, + // kaon.tpcNSigmaPr(), tofNSigmaPr, + eKpair.mass, /*eKpair.p, eKpair.deta, eKpair.dphi,*/ + eKpair.chi2PCA, eKpair.cospa, eKpair.cospaXY, eKpair.cospaRZ, + eKpair.lxy, eKpair.lz, eKpair.lxyz, eKpair.lxyErr, eKpair.lzErr, eKpair.lxyzErr, + mckaon.pdgCode(), pdgCodeIM, foundCommonMother); + + } // end of kaon loop // D0bar -> e- anti-nu_e K+, br = 0.03538, ctau = 123.01 um, m = 1864 MeV/c2 for (const auto& kaonId : kaonPlusIds) { - const auto& kaon = tracks.rawIteratorAt(kaonId); + auto kaon = tracks.rawIteratorAt(kaonId); mDcaInfoCov.set(999, 999, 999, 999, 999); auto trackParCov = getTrackParCov(kaon); - trackParCov.setPID(o2::track::PID::Kaon); + trackParCov.setPID(kaon.pidForTracking()); o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); float dcaXY_kaon = mDcaInfoCov.getY(); float dcaZ_kaon = mDcaInfoCov.getZ(); - float dca3DinSigma_kaon = dca3DinSigmaOTF(dcaXY_kaon, dcaZ_kaon, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()); if (electronId == kaonId) { continue; } - const auto& eKpair = o2::aod::pwgem::dilepton::utils::makePairLeptonTrack(fitter_eK, collision, ele, kaon, o2::track::PID::Electron, o2::track::PID::Kaon); + auto eKpair = o2::aod::pwgem::dilepton::utils::makePairLeptonTrack(fitter_eK, collision, ele, kaon, o2::track::PID::Electron, kaon.pidForTracking(), o2::constants::physics::MassElectron); if (!eKpair.isOK) { continue; } - if (!(lKPairCut.cfg_min_mass < eKpair.mass && eKpair.mass < lKPairCut.cfg_max_mass) || eKpair.cospa < lKPairCut.cfg_min_cospa || lKPairCut.cfg_max_lxyz < eKpair.lxyz / eKpair.lxyzErr || lKPairCut.cfg_max_dca2legs < eKpair.dca2legs) { + if (eKpair.cospa < lKPairCut.cfg_min_cospa || lKPairCut.cfg_max_lxyz < eKpair.lxyz || lKPairCut.cfg_max_chi2PCA < eKpair.chi2PCA || lKPairCut.cfg_max_massLH < eKpair.mass) { continue; } - fRegistry.fill(HIST("Data/eK/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, trackParCov.getPt(), dca3DinSigma_kaon, eKpair.mass, eKpair.lxyz / eKpair.lxyzErr, eKpair.cospa, eKpair.dca2legs); - const auto& mckaon = kaon.template mcParticle_as(); - int mcD0Id = FindCommonMotherFrom2Prongs(mcele, mckaon, 11, 321, -421, mcParticles); - int mcDpmId = FindCommonMotherFrom2Prongs(mcele, mckaon, 11, 321, -411, mcParticles); - if (mcD0Id > 0) { // true D0 - // const auto& mcD0 = mcParticles.rawIteratorAt(mcD0Id); - fRegistry.fill(HIST("MC/eKfromD0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, trackParCov.getPt(), dca3DinSigma_kaon, eKpair.mass, eKpair.lxyz / eKpair.lxyzErr, eKpair.cospa, eKpair.dca2legs); - } else if (mcDpmId > 0) { // true D- - // const auto& mcD0 = mcParticles.rawIteratorAt(mcD0Id); - fRegistry.fill(HIST("MC/eKfromDpm/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, trackParCov.getPt(), dca3DinSigma_kaon, eKpair.mass, eKpair.lxyz / eKpair.lxyzErr, eKpair.cospa, eKpair.dca2legs); + int pdgCodeIM = 0; + auto mckaon = kaon.template mcParticle_as(); + bool foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mckaon) > 0; + if (mckaon.has_mothers() && !foundCommonMother) { + auto mcMother_of_kaon = mckaon.template mothers_first_as(); + if (mcMother_of_kaon.pdgCode() == 323 || mcMother_of_kaon.pdgCode() == 313 || mcMother_of_kaon.pdgCode() == 333 || mcMother_of_kaon.pdgCode() == 413) { // accept short-lived resonances such as phi->KK, K*(892)->Kpi for D+ -> anti-K*0(892) e+ nu_e -> (K- pi+) e+ nu_e and Ds+ -> phi e+ nu_e + int commonMotherId = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mcMother_of_kaon); + if (commonMotherId > 0) { + foundCommonMother = true; + pdgCodeIM = mcMother_of_kaon.pdgCode(); + } + } + } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && foundCommonMother && std::abs(mckaon.pdgCode()) == cfgPdgLepton))) { + // I want 3 types. + // 1. truely found HF->eh (SV should found by eh, and truely found.) For signal sample in ML. + // 2. mistakenly found DY->eh (SV should not be found by eh, but found.) For bkg sample in ML. + // 3. truely found DY->ee with misidentified ee. (SV may be found at the same position of PV.) For bkg sample in ML. + continue; } + + // float tofNSigmaPi = mapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + float tofNSigmaKa = mapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + // float tofNSigmaPr = mapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), kaon.globalIndex())]; + + emmlltpair(leptonTable.lastIndex(), + trackParCov.getQ2Pt(), trackParCov.getEta(), dcaXY_kaon, dcaZ_kaon, trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), + // kaon.tpcNSigmaPi(), tofNSigmaPi, + kaon.tpcNSigmaKa(), tofNSigmaKa, + // kaon.tpcNSigmaPr(), tofNSigmaPr, + eKpair.mass, /*eKpair.p, eKpair.deta, eKpair.dphi,*/ + eKpair.chi2PCA, eKpair.cospa, eKpair.cospaXY, eKpair.cospaRZ, + eKpair.lxy, eKpair.lz, eKpair.lxyz, eKpair.lxyErr, eKpair.lzErr, eKpair.lxyzErr, + mckaon.pdgCode(), pdgCodeIM, foundCommonMother); + } // end of kaon loop - // D- -> e0 anti-K0S anti-nu_e + // D- -> e- anti-K0S anti-nu_e for (const auto& k0Id : k0Ids) { - const auto& v0 = v0s.rawIteratorAt(k0Id); - float lxyz_v0 = std::sqrt(std::pow(v0.x() - collision.posX(), 2) + std::pow(v0.y() - collision.posY(), 2) + std::pow(v0.z() - collision.posZ(), 2)); - const auto& eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, ele, v0, o2::track::PID::Electron, o2::track::PID::K0); + auto v0 = v0s.rawIteratorAt(k0Id); + if (v0.posTrackId() == electronId || v0.negTrackId() == electronId) { + continue; + } + + const std::array vertexV0 = {v0.x(), v0.y(), v0.z()}; + const std::array momV0 = {v0.px(), v0.py(), v0.pz()}; + std::array covV0 = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covV0[MomInd[i]] = v0.momentumCovMat()[i]; + covV0[i] = v0.positionCovMat()[i]; + } + auto trackV0 = o2::track::TrackParCov(vertexV0, momV0, covV0, 0, true); + trackV0.setAbsCharge(0); + trackV0.setPID(o2::track::PID::K0); + o2::dataformats::DCA impactParameterV0; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackV0, 2.f, matCorr, &impactParameterV0); // trackV0 is TrackParCov object + + auto eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, ele, v0, o2::track::PID::Electron, o2::track::PID::K0, o2::constants::physics::MassElectron); if (!eV0pair.isOK) { continue; } - if (!(lV0PairCut.cfg_min_mass < eV0pair.mass && eV0pair.mass < lV0PairCut.cfg_max_mass) || eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz / eV0pair.lxyzErr || lV0PairCut.cfg_max_dca2legs < eV0pair.dca2legs) { + if (eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz || lV0PairCut.cfg_max_chi2PCA < eV0pair.chi2PCA || lV0PairCut.cfg_max_massLH < eV0pair.mass) { continue; } - fRegistry.fill(HIST("Data/eK0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); auto posLeg = v0.template posTrack_as(); auto negLeg = v0.template negTrack_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); - int mcK0Id = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 211, -211, 310, mcParticles); - if (mcK0Id > 0) { // true K0S - const auto& mcK0 = mcParticles.rawIteratorAt(mcK0Id); - int mcDpmId = FindCommonMotherFrom2Prongs(mcele, mcK0, 11, 310, -411, mcParticles); - if (mcDpmId > 0) { // true D+ - // const auto& mcDpm = mcParticles.rawIteratorAt(mcDpmId); - fRegistry.fill(HIST("MC/eK0fromDpm/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); + int mcK0SId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 211, -211, 310, mcParticles); + int pdgCodeV0 = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; + if (mcK0SId > 0) { // true K0S + pdgCodeV0 = 310; + auto mcK0S = mcParticles.rawIteratorAt(mcK0SId); + auto mcK0 = mcK0S.template mothers_first_as(); // mother of K0S is K0 in simulation. + // LOGF(info, "mcK0S.pdgCode() = %d, mcK0.pdgCode() = %d", mcK0S.pdgCode(), mcK0.pdgCode()); + if (std::abs(mcK0.pdgCode()) == 311) { + mcK0S = mcK0; + } + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mcK0S) > 0; + if (mcK0S.has_mothers() && !foundCommonMother) { + auto mcMother_of_k0s = mcK0S.template mothers_first_as(); + if (mcMother_of_k0s.pdgCode() == 323 || mcMother_of_k0s.pdgCode() == 313 || mcMother_of_k0s.pdgCode() == 333) { // accept short-lived resonances such as phi->KK, K*(892)->Kpi for D+ -> anti-K*0(892) e+ nu_e -> (K- pi+) e+ nu_e and Ds+ -> phi e+ nu_e + int commonMotherId = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mcMother_of_k0s); + if (commonMotherId > 0) { + foundCommonMother = true; + pdgCodeIM = mcMother_of_k0s.pdgCode(); + } + } } } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; + } + + emmllv0pair(leptonTable.lastIndex(), 0, + v0.pt(), v0.rapidity(0), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + impactParameterV0.getY(), impactParameterV0.getZ(), impactParameterV0.getSigmaY2(), impactParameterV0.getSigmaYZ(), impactParameterV0.getSigmaZ2(), + eV0pair.mass, /*eV0pair.p, eV0pair.deta, eV0pair.dphi,*/ + eV0pair.chi2PCA, eV0pair.cospa, eV0pair.cospaXY, eV0pair.cospaRZ, + eV0pair.lxy, eV0pair.lz, eV0pair.lxyz, eV0pair.lxyErr, eV0pair.lzErr, eV0pair.lxyzErr, + pdgCodeV0, pdgCodeIM, foundCommonMother); + } // end of K0S loop // Lc- -> e- anti-Lambda anti-nu_e, br = 0.0356, ctau = 60.75 um, m = 2286 MeV/c2 for (const auto& lambdaId : antilambdaIds) { - const auto& v0 = v0s.rawIteratorAt(lambdaId); - float lxyz_v0 = std::sqrt(std::pow(v0.x() - collision.posX(), 2) + std::pow(v0.y() - collision.posY(), 2) + std::pow(v0.z() - collision.posZ(), 2)); - const auto& eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, ele, v0, o2::track::PID::Electron, o2::track::PID::Lambda); + auto v0 = v0s.rawIteratorAt(lambdaId); + if (v0.posTrackId() == electronId || v0.negTrackId() == electronId) { + continue; + } + + const std::array vertexV0 = {v0.x(), v0.y(), v0.z()}; + const std::array momV0 = {v0.px(), v0.py(), v0.pz()}; + std::array covV0 = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covV0[MomInd[i]] = v0.momentumCovMat()[i]; + covV0[i] = v0.positionCovMat()[i]; + } + auto trackV0 = o2::track::TrackParCov(vertexV0, momV0, covV0, 0, true); + trackV0.setAbsCharge(0); + trackV0.setPID(o2::track::PID::Lambda); + o2::dataformats::DCA impactParameterV0; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackV0, 2.f, matCorr, &impactParameterV0); // trackV0 is TrackParCov object + + auto eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(fitter_eV0, collision, ele, v0, o2::track::PID::Electron, o2::track::PID::Lambda, o2::constants::physics::MassElectron); if (!eV0pair.isOK) { continue; } - if (!(lV0PairCut.cfg_min_mass < eV0pair.mass && eV0pair.mass < lV0PairCut.cfg_max_mass) || eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz / eV0pair.lxyzErr || lV0PairCut.cfg_max_dca2legs < eV0pair.dca2legs) { + if (eV0pair.cospa < lV0PairCut.cfg_min_cospa || lV0PairCut.cfg_max_lxyz < eV0pair.lxyz || lV0PairCut.cfg_max_chi2PCA < eV0pair.chi2PCA || lV0PairCut.cfg_max_massLH < eV0pair.mass) { continue; } - fRegistry.fill(HIST("Data/eL/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); auto posLeg = v0.template posTrack_as(); auto negLeg = v0.template negTrack_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); int mcLambdaId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 211, -2212, -3122, mcParticles); + int pdgCodeV0 = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; if (mcLambdaId > 0) { // true v0 - const auto& mcLambda = mcParticles.rawIteratorAt(mcLambdaId); - int mcLambdacId = FindCommonMotherFrom2Prongs(mcele, mcLambda, 11, -3122, -4122, mcParticles); - if (mcLambdacId > 0) { // true Lc0 - // const auto& mcLambdac0 = mcParticles.rawIteratorAt(mcLambdacId); - fRegistry.fill(HIST("MC/eLfromLcpm/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, v0.pt(), lxyz_v0, eV0pair.mass, eV0pair.lxyz / eV0pair.lxyzErr, eV0pair.cospa, eV0pair.dca2legs); - } + auto mcLambda = mcParticles.rawIteratorAt(mcLambdaId); + pdgCodeV0 = mcLambda.pdgCode(); + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mcLambda) > 0; + } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; } + + emmllv0pair(leptonTable.lastIndex(), 2, + v0.pt(), v0.rapidity(1), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{v0.x(), v0.y(), v0.z()}, std::array{v0.px(), v0.py(), v0.pz()}), + impactParameterV0.getY(), impactParameterV0.getZ(), impactParameterV0.getSigmaY2(), impactParameterV0.getSigmaYZ(), impactParameterV0.getSigmaZ2(), + eV0pair.mass, /*eV0pair.p, eV0pair.deta, eV0pair.dphi,*/ + eV0pair.chi2PCA, eV0pair.cospa, eV0pair.cospaXY, eV0pair.cospaRZ, + eV0pair.lxy, eV0pair.lz, eV0pair.lxyz, eV0pair.lxyErr, eV0pair.lzErr, eV0pair.lxyzErr, + pdgCodeV0, pdgCodeIM, foundCommonMother); + } // end of Anti-Lambda loop for (const auto& cascadeId : xiPlusIds) { - const auto& cascade = cascades.rawIteratorAt(cascadeId); - float lxyz_cascade = std::sqrt(std::pow(cascade.x() - collision.posX(), 2) + std::pow(cascade.y() - collision.posY(), 2) + std::pow(cascade.z() - collision.posZ(), 2)); - const auto& eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, ele, cascade, o2::track::PID::Electron, o2::track::PID::XiMinus); + auto cascade = cascades.rawIteratorAt(cascadeId); + if (cascade.posTrackId() == electronId || cascade.negTrackId() == electronId || cascade.bachelorId() == electronId) { + continue; + } + + const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + std::array covCasc = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + covCasc[i] = cascade.positionCovMat()[i]; + } + auto trackCasc = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + trackCasc.setAbsCharge(1); + trackCasc.setPID(o2::track::PID::XiMinus); + o2::dataformats::DCA impactParameterCasc; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackCasc, 2.f, matCorr, &impactParameterCasc); // trackCasc is TrackParCov object + + auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, ele, cascade, o2::track::PID::Electron, o2::track::PID::XiMinus, o2::constants::physics::MassElectron); if (!eCpair.isOK) { continue; } - if (!(lCPairCut.cfg_min_mass < eCpair.mass && eCpair.mass < lCPairCut.cfg_max_mass) || eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz / eCpair.lxyzErr || lCPairCut.cfg_max_dca2legs < eCpair.dca2legs) { + if (eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz || lCPairCut.cfg_max_chi2PCA < eCpair.chi2PCA || lCPairCut.cfg_max_massLH < eCpair.mass) { continue; } - fRegistry.fill(HIST("Data/eXi/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); auto posLeg = cascade.template posTrack_as(); auto negLeg = cascade.template negTrack_as(); auto bachelor = cascade.template bachelor_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); - const auto& mcbachelor = bachelor.template mcParticle_as(); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); + auto mcbachelor = bachelor.template mcParticle_as(); int mcLambdaId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 211, -2212, -3122, mcParticles); + int pdgCodeCascade = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; if (mcLambdaId > 0) { // true Lambda - const auto& mcLambda = mcParticles.rawIteratorAt(mcLambdaId); + auto mcLambda = mcParticles.rawIteratorAt(mcLambdaId); int mcXiId = FindCommonMotherFrom2Prongs(mcLambda, mcbachelor, -3122, 211, -3312, mcParticles); if (mcXiId > 0) { // true xiMinus - const auto& mcXi = mcParticles.rawIteratorAt(mcXiId); - int mcXic0Id = FindCommonMotherFrom2Prongs(mcele, mcXi, 11, -3312, -4132, mcParticles); - if (mcXic0Id > 0) { // true Xic0 - // const auto& mcXic0 = mcParticles.rawIteratorAt(mcXic0Id); - fRegistry.fill(HIST("MC/eXifromXic0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); - } + auto mcXi = mcParticles.rawIteratorAt(mcXiId); + pdgCodeCascade = mcXi.pdgCode(); + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mcXi) > 0; } } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; + } + + emmllcascpair(leptonTable.lastIndex(), 0, + cascade.sign() / cascade.pt(), cascade.rapidity(0), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + impactParameterCasc.getY(), impactParameterCasc.getZ(), impactParameterCasc.getSigmaY2(), impactParameterCasc.getSigmaYZ(), impactParameterCasc.getSigmaZ2(), + eCpair.mass, /*eCpair.p, eCpair.deta, eCpair.dphi,*/ + eCpair.chi2PCA, eCpair.cospa, eCpair.cospaXY, eCpair.cospaRZ, + eCpair.lxy, eCpair.lz, eCpair.lxyz, eCpair.lxyErr, eCpair.lzErr, eCpair.lxyzErr, + pdgCodeCascade, pdgCodeIM, foundCommonMother); + } // end of Xi+ loop for (const auto& cascadeId : omegaPlusIds) { - const auto& cascade = cascades.rawIteratorAt(cascadeId); - float lxyz_cascade = std::sqrt(std::pow(cascade.x() - collision.posX(), 2) + std::pow(cascade.y() - collision.posY(), 2) + std::pow(cascade.z() - collision.posZ(), 2)); - const auto& eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, ele, cascade, o2::track::PID::Electron, o2::track::PID::OmegaMinus); + auto cascade = cascades.rawIteratorAt(cascadeId); + if (cascade.posTrackId() == electronId || cascade.negTrackId() == electronId || cascade.bachelorId() == electronId) { + continue; + } + + const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + std::array covCasc = {0.}; + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 6; i++) { + covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + covCasc[i] = cascade.positionCovMat()[i]; + } + auto trackCasc = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + trackCasc.setAbsCharge(1); + trackCasc.setPID(o2::track::PID::OmegaMinus); + o2::dataformats::DCA impactParameterCasc; + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackCasc, 2.f, matCorr, &impactParameterCasc); // trackCasc is TrackParCov object + + auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(fitter_eCascade, collision, ele, cascade, o2::track::PID::Electron, o2::track::PID::OmegaMinus, o2::constants::physics::MassElectron); if (!eCpair.isOK) { continue; } - if (!(lCPairCut.cfg_min_mass < eCpair.mass && eCpair.mass < lCPairCut.cfg_max_mass) || eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz / eCpair.lxyzErr || lCPairCut.cfg_max_dca2legs < eCpair.dca2legs) { + if (eCpair.cospa < lCPairCut.cfg_min_cospa || lCPairCut.cfg_max_lxyz < eCpair.lxyz || lCPairCut.cfg_max_chi2PCA < eCpair.chi2PCA || lCPairCut.cfg_max_massLH < eCpair.mass) { continue; } - fRegistry.fill(HIST("Data/eOmega/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); auto posLeg = cascade.template posTrack_as(); auto negLeg = cascade.template negTrack_as(); auto bachelor = cascade.template bachelor_as(); - const auto& mcposLeg = posLeg.template mcParticle_as(); - const auto& mcnegLeg = negLeg.template mcParticle_as(); - const auto& mcbachelor = bachelor.template mcParticle_as(); + auto mcposLeg = posLeg.template mcParticle_as(); + auto mcnegLeg = negLeg.template mcParticle_as(); + auto mcbachelor = bachelor.template mcParticle_as(); int mcLambdaId = FindCommonMotherFrom2Prongs(mcposLeg, mcnegLeg, 211, -2212, -3122, mcParticles); + int pdgCodeCascade = 0; + bool foundCommonMother = false; + int pdgCodeIM = 0; if (mcLambdaId > 0) { // true Lambda - const auto& mcLambda = mcParticles.rawIteratorAt(mcLambdaId); + auto mcLambda = mcParticles.rawIteratorAt(mcLambdaId); int mcOmegaId = FindCommonMotherFrom2Prongs(mcLambda, mcbachelor, -3122, 321, -3334, mcParticles); if (mcOmegaId > 0) { // true omegaMinus - const auto& mcOmega = mcParticles.rawIteratorAt(mcOmegaId); - int mcOmegac0Id = FindCommonMotherFrom2Prongs(mcele, mcOmega, 11, -3334, -4332, mcParticles); - if (mcOmegac0Id > 0) { // true Omegac0 - // const auto& mcOmegac0 = mcParticles.rawIteratorAt(mcOmegac0Id); - fRegistry.fill(HIST("MC/eOmegafromOmegac0/hs"), leptonParCov.getPt(), dca3DinSigma_lepton, cascade.pt(), lxyz_cascade, eCpair.mass, eCpair.lxyz / eCpair.lxyzErr, eCpair.cospa, eCpair.dca2legs); - } + auto mcOmega = mcParticles.rawIteratorAt(mcOmegaId); + pdgCodeCascade = mcOmega.pdgCode(); + foundCommonMother = FindCommonMotherFrom2ProngsWithoutPDG(mcele, mcOmega) > 0; } } + + if (!(((is_e_from_hc || is_e_from_hb) && foundCommonMother) || ((is_e_from_dy || is_e_from_jpsi) && !foundCommonMother))) { + continue; + } + + emmllcascpair(leptonTable.lastIndex(), 1, + cascade.sign() / cascade.pt(), cascade.rapidity(2), + RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaXY(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + RecoDecay::cpaRZ(std::array{collision.posX(), collision.posY(), collision.posZ()}, std::array{cascade.x(), cascade.y(), cascade.z()}, std::array{cascade.px(), cascade.py(), cascade.pz()}), + impactParameterCasc.getY(), impactParameterCasc.getZ(), impactParameterCasc.getSigmaY2(), impactParameterCasc.getSigmaYZ(), impactParameterCasc.getSigmaZ2(), + eCpair.mass, /*eCpair.p, eCpair.deta, eCpair.dphi,*/ + eCpair.chi2PCA, eCpair.cospa, eCpair.cospaXY, eCpair.cospaRZ, + eCpair.lxy, eCpair.lz, eCpair.lxyz, eCpair.lxyErr, eCpair.lzErr, eCpair.lxyzErr, + pdgCodeCascade, pdgCodeIM, foundCommonMother); + } // end of Omega+ loop } // end of main electron sample + if (npos + nele > 0) { // fill eventTable only if at least 1 electron or positron exists. + eventTable(collision.numContrib(), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), 0); + } + electronIds.clear(); electronIds.shrink_to_fit(); positronIds.clear(); @@ -1507,12 +2575,14 @@ struct taggingHFE { auto mcDpms_per_mccollision = mcDpms.sliceBy(perMcCollision, mcCollision.globalIndex()); for (const auto& mcParticle : mcDpms_per_mccollision) { + // LOGF(info, "Dpm: mcParticle.pdgCode() = %d", mcParticle.pdgCode()); // for (int d = mcParticle.daughtersIds()[0]; d <= mcParticle.daughtersIds()[1]; ++d) { // auto daughter = mcParticles.rawIteratorAt(d); // LOGF(info, "daughter.pdgCode() = %d", daughter.pdgCode()); // } - if (isSemiLeptonic(mcParticle, mcParticles, -cfgPdgLepton, cfgPdgLepton + 1, -321) || isSemiLeptonic(mcParticle, mcParticles, cfgPdgLepton, -cfgPdgLepton - 1, 321)) { // D+ -> l+ nul K- pi+ or D- -> l- anti-nul K+ pi- + // if (isSemiLeptonic(mcParticle, mcParticles, -cfgPdgLepton, cfgPdgLepton + 1, -321) || isSemiLeptonic(mcParticle, mcParticles, cfgPdgLepton, -cfgPdgLepton - 1, 321)) { // D+ -> l+ nul K- pi+ or D- -> l- anti-nul K+ pi- + if (isSemiLeptonic(mcParticle, mcParticles, -cfgPdgLepton, cfgPdgLepton + 1, 311) || isSemiLeptonic(mcParticle, mcParticles, cfgPdgLepton, -cfgPdgLepton - 1, 311)) { // D+ -> l+ nul K- pi+ or D- -> l- anti-nul K+ pi- // LOGF(info, "semileptonic decay is found."); float ptLepton = 0, ptHadron = 0, etaLepton = 999.f, etaHadron = 999.f; for (int d = mcParticle.daughtersIds()[0]; d <= mcParticle.daughtersIds()[1]; ++d) { @@ -1520,7 +2590,7 @@ struct taggingHFE { if (std::abs(daughter.pdgCode()) == cfgPdgLepton) { ptLepton = daughter.pt(); etaLepton = daughter.eta(); - } else if (std::abs(daughter.pdgCode()) == 321) { + } else if (std::abs(daughter.pdgCode()) == 311) { ptHadron = daughter.pt(); etaHadron = daughter.eta(); } @@ -1578,7 +2648,16 @@ struct taggingHFE { fRegistry.fill(HIST("Generated/Lc/hsAcc"), ptLepton, ptHadron, etaLepton, etaHadron); } - } // end of D0 loop per mcCollision + } // end of Lc loop per mcCollision + + // auto mcB0s_per_mccollision = mcB0s.sliceBy(perMcCollision, mcCollision.globalIndex()); + // for (const auto& mcParticle : mcB0s_per_mccollision) { + // LOGF(info, "B0: mcParticle.pdgCode() = %d", mcParticle.pdgCode()); + // for (int d = mcParticle.daughtersIds()[0]; d <= mcParticle.daughtersIds()[1]; ++d) { + // auto daughter = mcParticles.rawIteratorAt(d); + // LOGF(info, "B0: daughter.pdgCode() = %d", daughter.pdgCode()); + // } + // } // end of B0 loop per mcCollision } } @@ -1586,8 +2665,9 @@ struct taggingHFE { Partition mcD0s = nabs(o2::aod::mcparticle::pdgCode) == 421; Partition mcDspms = nabs(o2::aod::mcparticle::pdgCode) == 431; Partition mcLcs = nabs(o2::aod::mcparticle::pdgCode) == 4122; - Partition mcXic0s = nabs(o2::aod::mcparticle::pdgCode) == 4232; - Partition mcOmegac0s = nabs(o2::aod::mcparticle::pdgCode) == 4332; + // Partition mcXic0s = nabs(o2::aod::mcparticle::pdgCode) == 4232; + // Partition mcOmegac0s = nabs(o2::aod::mcparticle::pdgCode) == 4332; + // Partition mcB0s = nabs(o2::aod::mcparticle::pdgCode) == 511; SliceCache cache; Preslice perCol = o2::aod::track::collisionId; @@ -1595,7 +2675,7 @@ struct taggingHFE { Preslice perCol_casc = o2::aod::cascdata::collisionId; Preslice perMcCollision = aod::mcparticle::mcCollisionId; - Filter collisionFilter_evsel = o2::aod::evsel::sel8 == true && (eventCut.cfgZvtxMin < o2::aod::collision::posZ && o2::aod::collision::posZ < eventCut.cfgZvtxMax); + Filter collisionFilter_evsel = eventCut.cfgZvtxMin < o2::aod::collision::posZ && o2::aod::collision::posZ < eventCut.cfgZvtxMax; Filter collisionFilter_centrality = (eventCut.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < eventCut.cfgCentMax) || (eventCut.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < eventCut.cfgCentMax) || (eventCut.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < eventCut.cfgCentMax); using FilteredMyCollisions = soa::Filtered; using FilteredMyCollisionsWithMCLabel = soa::Filtered; @@ -1629,10 +2709,34 @@ struct taggingHFE { std::vector> used_electronIds; // pair of hTypeId and electronId - void processMC(FilteredMyCollisionsWithMCLabel const& collisions, aod::BCsWithTimestamps const& bcs, MyTracksWithMCLabel const& tracks, aod::TrackAssoc const& trackIndices, filteredV0s const& v0s, filteredMyCascades const& cascades, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) + // void processMC(FilteredMyCollisionsWithMCLabel const& collisions, MyBCs const& bcs, MyTracksWithMCLabel const& tracks, aod::TrackAssoc const& trackIndices, filteredV0s const& v0s, filteredMyCascades const& cascades, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) + void processMC(MyCollisionsWithMCLabel const& collisions, MyBCs const& bcs, MyTracksWithMCLabel const& tracks, aod::TrackAssoc const& trackIndices, filteredV0s const& v0s, filteredMyCascades const& cascades, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) { + initCCDB(bcs.iteratorAt(0)); + mTOFResponse->processSetup(bcs.iteratorAt(0)); + + for (const auto& track : tracks) { + if (mapCollisionTime.find(track.collisionId()) == mapCollisionTime.end()) { + // LOGF(info, "track.collisionId() = %d, track.tofEvTime() = %f, track.tofEvTimeErr() = %f", track.collisionId(), track.tofEvTime(), track.tofEvTimeErr()); + mapCollisionTime[track.collisionId()] = track.tofEvTime(); + mapCollisionTimeError[track.collisionId()] = track.tofEvTimeErr(); + } + } + calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); // for primary tracks + calculateTOFNSigmasForV0sAndCascades(collisions, bcs, v0s, cascades, tracks); // for secondary tracks + runPairing(bcs, collisions, tracks, trackIndices, v0s, cascades, mcCollisions, mcParticles); runGen(mcCollisions, mcParticles); + + mapCollisionTime.clear(); + mapCollisionTimeError.clear(); + mapTOFNsigmaPiReassociated.clear(); + mapTOFNsigmaKaReassociated.clear(); + mapTOFNsigmaPrReassociated.clear(); + mapTOFBetaReassociated.clear(); + + mapTOFNSigma.clear(); + mapTOFBeta.clear(); } PROCESS_SWITCH(taggingHFE, processMC, "process with TTCA", true); @@ -1641,5 +2745,6 @@ struct taggingHFE { }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { + o2::pid::tof::TOFResponseImpl::metadataInfo.initMetadata(cfgc); return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"tagging-hfe"})}; } diff --git a/PWGEM/Dilepton/Tasks/testPV.cxx b/PWGEM/Dilepton/Tasks/testPV.cxx new file mode 100644 index 00000000000..7c7057c3681 --- /dev/null +++ b/PWGEM/Dilepton/Tasks/testPV.cxx @@ -0,0 +1,250 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file testPV.cxx +/// \brief a task to check PV quality in MC +/// \author daiki.sekihata@cern.ch + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/Zorro.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct testPV { + + // Configurables + o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "eventCut"; + o2::framework::Configurable cfgEventGeneratorId{"cfgEventGeneratorId", -1, "event generator index. e.g. select gap/signal events"}; + o2::framework::Configurable cfgChi2PerNcontribMax{"cfgChi2PerNcontribMax", 1e+10, "max. chi2/Ncontrib of PV"}; + o2::framework::Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; + o2::framework::Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + o2::framework::Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND"}; + o2::framework::Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require No time frame border"}; + o2::framework::Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border"}; + o2::framework::Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + o2::framework::Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + o2::framework::Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + o2::framework::Configurable cfgRequireVertexTOFmatched{"cfgRequireVertexTOFmatched", false, "require Vertex TOFmatched in event cut"}; // ITS-TPC-TOF matched track contributes PV. + + // for RCT + o2::framework::Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", true, "require good detector flag in run condtion table"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for AA"}; + o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } eventCut; + + // for zorro + struct : o2::framework::ConfigurableGroup { + std::string prefix = "zorroGroup"; + o2::framework::Configurable cfgTriggerName{"cfgTriggerName", "fGlobalDimuon", "desired software trigger name"}; + o2::framework::Configurable ccdbPathSoftwareTrigger{"ccdbPathSoftwareTrigger", "EventFiltering/Zorro/", "ccdb path for ZORRO objects"}; + o2::framework::Configurable bcMarginForSoftwareTrigger{"bcMarginForSoftwareTrigger", 100, "Number of BCs of margin for software triggers"}; + } zorroGroup; + + o2::framework::HistogramRegistry fRegistry{"fRegistry"}; + Zorro zorro; + + void init(o2::framework::InitContext&) + { + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + mRunNumber = 0; + + addHistograms(); + } + + int mRunNumber{0}; + o2::framework::Service ccdb; + + template + void initCCDB(TBC const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + if constexpr (isTriggerAnalysis) { + zorro.setCCDBpath(zorroGroup.ccdbPathSoftwareTrigger); + zorro.setBCtolerance(zorroGroup.bcMarginForSoftwareTrigger); // this does nothing. + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroGroup.cfgTriggerName.value); + } + + mRunNumber = bc.runNumber(); + } + + void addHistograms() + { + auto hCollisionCounter = fRegistry.add("hCollisionCounter", "collision counter", o2::framework::HistType::kTH1D, {{2, 0.5f, 2.5f}}, false); + hCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); + hCollisionCounter->GetXaxis()->SetBinLabel(2, "accepted"); + + fRegistry.add("Vertex/hZvtx", "vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1F, {{100, -50, +50}}, false); + fRegistry.add("Vertex/hNContrib", "Number of PV contributors;N_{contrib}", o2::framework::HistType::kTH1F, {{101, -0.5, 100.5}}, false); + fRegistry.add("Vertex/hChi2", "vertex chi2;#chi^{2}/N_{contrib}", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Vertex/hChi2vsNContrib", "vertex #chi^{2}/N_{contrib} vs. N_{contrib};N_{contrib};#chi^{2}/N_{contrib}", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {100, 0, 10}}, false); + + fRegistry.add("Vertex/hSigmaX", "vertex #sigma_{X} vs. N_{contrib};N_{contrib};#sigma_{X} (#mum)", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {1000, 0, 100}}, false); + fRegistry.add("Vertex/hSigmaY", "vertex #sigma_{Y} vs. N_{contrib};N_{contrib};#sigma_{Y} (#mum)", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {1000, 0, 100}}, false); + fRegistry.add("Vertex/hSigmaZ", "vertex #sigma_{Z} vs. N_{contrib};N_{contrib};#sigma_{Z} (#mum)", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {1000, 0, 100}}, false); + + fRegistry.add("Vertex/hCollisionTime", "vertex time;N_{contrib};collision time (ns)", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {500, -25, 25}}, false); + fRegistry.add("Vertex/hCollisionTimeRes", "vertex time resolution;N_{contrib};collision time resolution (ns)", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {250, 0, 25}}, false); + + if (doprocessMC) { + fRegistry.add("Vertex/hDeltaX", "vertex #DeltaX vs. N_{contrib};N_{contrib};#DeltaX = (X_{rec} #minus X_{gen})/#sigma_{X}", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {200, -10, 10}}, false); + fRegistry.add("Vertex/hDeltaY", "vertex #DeltaY vs. N_{contrib};N_{contrib};#DeltaY = (Y_{rec} #minus Y_{gen})/#sigma_{Y}", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {200, -10, 10}}, false); + fRegistry.add("Vertex/hDeltaZ", "vertex #DeltaZ vs. N_{contrib};N_{contrib};#DeltaZ = (Z_{rec} #minus Z_{gen})/#sigma_{Z}", o2::framework::HistType::kTH2F, {{101, -0.5, 100.5}, {200, -10, 10}}, false); + } + } + + template + bool isSelectedCollision(TCollision const& collision) + { + if (!(eventCut.cfgZvtxMin < collision.posZ() && collision.posZ() < eventCut.cfgZvtxMax)) { + return false; + } + if (eventCut.cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + if (eventCut.cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (eventCut.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (eventCut.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (eventCut.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (eventCut.cfgRequireVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (eventCut.cfgRequireVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (collision.chi2() / collision.numContrib() > eventCut.cfgChi2PerNcontribMax) { + return false; + } + return true; + } + + template + void fillVertexHistograms(TCollision const& collision) + { + fRegistry.fill(HIST("Vertex/hZvtx"), collision.posZ()); + fRegistry.fill(HIST("Vertex/hNContrib"), collision.numContrib()); + fRegistry.fill(HIST("Vertex/hChi2"), collision.chi2() / collision.numContrib()); + fRegistry.fill(HIST("Vertex/hChi2vsNContrib"), collision.numContrib(), collision.chi2() / collision.numContrib()); + + fRegistry.fill(HIST("Vertex/hSigmaX"), collision.numContrib(), std::sqrt(collision.covXX()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/hSigmaY"), collision.numContrib(), std::sqrt(collision.covYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST("Vertex/hSigmaZ"), collision.numContrib(), std::sqrt(collision.covZZ()) * 1e+4); // convert cm to um + + fRegistry.fill(HIST("Vertex/hCollisionTime"), collision.numContrib(), collision.collisionTime()); + fRegistry.fill(HIST("Vertex/hCollisionTimeRes"), collision.numContrib(), collision.collisionTimeRes()); + } + + template + void run(TBCs const&, TCollisions const& collisions, TMCCollisions const&, TMCParticles const&) + { + for (const auto& collision : collisions) { + auto bc = collision.template bc_as(); + initCCDB(bc); + + if constexpr (isMC) { + if (!collision.has_mcCollision()) { + continue; + } + auto mcCollision = collision.template mcCollision_as(); + if (eventCut.cfgEventGeneratorId > -1 && mcCollision.getSubGeneratorId() != eventCut.cfgEventGeneratorId) { + continue; + } + } + + if constexpr (isTriggerAnalysis) { + if (!zorro.isSelected(bc.globalBC(), zorroGroup.bcMarginForSoftwareTrigger)) { // triggered event + continue; + } + } + + fRegistry.fill(HIST("hCollisionCounter"), 1); + + if (!isSelectedCollision(collision)) { + continue; + } + + fRegistry.fill(HIST("hCollisionCounter"), 2); + fillVertexHistograms(collision); + if constexpr (isMC) { + auto mcCollision = collision.template mcCollision_as(); + fRegistry.fill(HIST("Vertex/hDeltaX"), collision.numContrib(), (collision.posX() - mcCollision.posX()) / std::sqrt(collision.covXX())); + fRegistry.fill(HIST("Vertex/hDeltaY"), collision.numContrib(), (collision.posY() - mcCollision.posY()) / std::sqrt(collision.covYY())); + fRegistry.fill(HIST("Vertex/hDeltaZ"), collision.numContrib(), (collision.posZ() - mcCollision.posZ()) / std::sqrt(collision.covZZ())); + } + + } // end of collision loop + } + + using MyCollisions = o2::soa::Join; + using MyCollisionsMC = o2::soa::Join; + using MyBCs = o2::soa::Join; + + o2::framework::expressions::Filter collisionFilter_evsel = eventCut.cfgZvtxMin < o2::aod::collision::posZ && o2::aod::collision::posZ < eventCut.cfgZvtxMax; + using FilteredMyCollisions = o2::soa::Filtered; + using FilteredMyCollisionsMC = o2::soa::Filtered; + + void processData(FilteredMyCollisions const& collisions, MyBCs const& bcs) + { + run(bcs, collisions, nullptr, nullptr); + } + PROCESS_SWITCH(testPV, processData, "processData", true); + + void processTriggeredData(FilteredMyCollisions const& collisions, MyBCs const& bcs) + { + run(bcs, collisions, nullptr, nullptr); + } + PROCESS_SWITCH(testPV, processTriggeredData, "processTriggeredData", false); + + void processMC(FilteredMyCollisionsMC const& collisions, MyBCs const& bcs, o2::aod::McCollisions const& mcCollisions, o2::aod::McParticles const& mcParticles) + { + run(bcs, collisions, mcCollisions, mcParticles); + } + PROCESS_SWITCH(testPV, processMC, "processMC", false); + + void processDummy(MyCollisions const&) {} + PROCESS_SWITCH(testPV, processDummy, "processDummy", false); +}; +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + return o2::framework::WorkflowSpec{adaptAnalysisTask(cfgc, o2::framework::TaskName{"test-pv"})}; +} diff --git a/PWGEM/Dilepton/Utils/CMakeLists.txt b/PWGEM/Dilepton/Utils/CMakeLists.txt new file mode 100644 index 00000000000..6a57e353eda --- /dev/null +++ b/PWGEM/Dilepton/Utils/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_library(EmDileptonMlCore + SOURCES EmDileptonMlResponse.cxx + PUBLIC_LINK_LIBRARIES O2Physics::MLCore) diff --git a/PWGEM/Dilepton/Utils/EMFwdTrack.h b/PWGEM/Dilepton/Utils/EMFwdTrack.h index 72b384560ef..a4ad5c43f14 100644 --- a/PWGEM/Dilepton/Utils/EMFwdTrack.h +++ b/PWGEM/Dilepton/Utils/EMFwdTrack.h @@ -51,9 +51,9 @@ class EMFwdTrack float pz() const { return fPt * std::sinh(fEta); } float signed1Pt() const { return fSign / fPt; } - float cXX() const { return fCXX; } - float cXY() const { return fCXY; } - float cYY() const { return fCYY; } + float cXXatDCA() const { return fCXX; } + float cXYatDCA() const { return fCXY; } + float cYYatDCA() const { return fCYY; } protected: float fPt; diff --git a/PWGEM/Dilepton/Utils/EMTrackUtilities.h b/PWGEM/Dilepton/Utils/EMTrackUtilities.h index c06903b1eee..09cdd18f820 100644 --- a/PWGEM/Dilepton/Utils/EMTrackUtilities.h +++ b/PWGEM/Dilepton/Utils/EMTrackUtilities.h @@ -108,9 +108,9 @@ float dcaZinSigma(T const& track) template float fwdDcaXYinSigma(T const& track) { - float cXX = track.cXX(); // in cm^2 - float cYY = track.cYY(); // in cm^2 - float cXY = track.cXY(); // in cm^2 + float cXX = track.cXXatDCA(); // in cm^2 + float cYY = track.cYYatDCA(); // in cm^2 + float cXY = track.cXYatDCA(); // in cm^2 float dcaX = track.fwdDcaX(); // in cm float dcaY = track.fwdDcaY(); // in cm float det = cXX * cYY - cXY * cXY; // determinant diff --git a/PWGEM/Dilepton/Utils/ElectronModule.h b/PWGEM/Dilepton/Utils/ElectronModule.h new file mode 100644 index 00000000000..9f9d4a9131e --- /dev/null +++ b/PWGEM/Dilepton/Utils/ElectronModule.h @@ -0,0 +1,2206 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ElectronModule.h +/// \brief write relevant information about primary electrons. +/// \author daiki.sekihata@cern.ch + +#ifndef PWGEM_DILEPTON_UTILS_ELECTRONMODULE_H_ +#define PWGEM_DILEPTON_UTILS_ELECTRONMODULE_H_ + +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/Dilepton/Utils/MlResponsePID.h" +#include "PWGEM/Dilepton/Utils/MlResponseSCT.h" +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/Dilepton/Utils/SemiCharmTag.h" + +#include "Common/Core/PID/PIDTOFParamService.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace o2 +{ +namespace pwgem::dilepton::utils +{ + +struct ElectronProducts : o2::framework::ProducesGroup { + o2::framework::Produces electronTable; + o2::framework::Produces electronCovTable; + o2::framework::Produces electronPFTable; + o2::framework::Produces sctTable; +}; + +struct electronCut : o2::framework::ConfigurableGroup { + std::string prefix = "electronCut"; + o2::framework::Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; + o2::framework::Configurable mincrossedrows{"mincrossedrows", 70, "min. crossed rows"}; + o2::framework::Configurable min_tpc_cr_findable_ratio{"min_tpc_cr_findable_ratio", 0.8, "min. TPC Ncr/Nf ratio"}; + o2::framework::Configurable min_ncluster_its{"min_ncluster_its", 4, "min ncluster its"}; + o2::framework::Configurable min_ncluster_itsib{"min_ncluster_itsib", 1, "min ncluster itsib"}; + o2::framework::Configurable minchi2tpc{"minchi2tpc", 0.0, "min. chi2/NclsTPC"}; + o2::framework::Configurable minchi2its{"minchi2its", 0.0, "min. chi2/NclsITS"}; + o2::framework::Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max. chi2/NclsTPC"}; + o2::framework::Configurable maxchi2its{"maxchi2its", 36.0, "max. chi2/NclsITS"}; + o2::framework::Configurable minpt{"minpt", 0.05, "min pt"}; + o2::framework::Configurable maxeta{"maxeta", 0.9, "max eta"}; + o2::framework::Configurable dca_xy_max{"dca_xy_max", 1.0, "max DCAxy in cm"}; + o2::framework::Configurable dca_z_max{"dca_z_max", 1.0, "max DCAz in cm"}; + o2::framework::Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.5, "min. TPC n sigma for electron inclusion"}; + o2::framework::Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", 3.5, "max. TPC n sigma for electron inclusion"}; + o2::framework::Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", 3.5, "max. TOF n sigma for electron inclusion"}; + o2::framework::Configurable maxTPCNsigmaPi{"maxTPCNsigmaPi", 2.5, "max. TPC n sigma for pion exclusion"}; + o2::framework::Configurable minTPCNsigmaPi{"minTPCNsigmaPi", -1e+10, "min. TPC n sigma for pion exclusion"}; // set to -2 for lowB, -1e+10 for nominalB + o2::framework::Configurable maxTPCNsigmaKa{"maxTPCNsigmaKa", 2.5, "max. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable minTPCNsigmaKa{"minTPCNsigmaKa", -2.5, "min. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable maxTPCNsigmaPr{"maxTPCNsigmaPr", 2.5, "max. TPC n sigma for proton exclusion"}; + o2::framework::Configurable minTPCNsigmaPr{"minTPCNsigmaPr", -2.5, "min. TPC n sigma for proton exclusion"}; + o2::framework::Configurable requireTOF{"requireTOF", false, "require TOF hit"}; + o2::framework::Configurable min_pin_for_pion_rejection{"min_pin_for_pion_rejection", 0.0, "pion rejection is applied above this pin"}; // this is used only in TOFreq + o2::framework::Configurable max_pin_for_pion_rejection{"max_pin_for_pion_rejection", 0.5, "pion rejection is applied below this pin"}; + o2::framework::Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 0.7f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable maxMeanITSClusterSize{"maxMeanITSClusterSize", 16, "max x cos(lambda)"}; + o2::framework::Configurable storeOnlyTrueElectronMC{"storeOnlyTrueElectronMC", false, "Flag to store only true electron in MC"}; + o2::framework::Configurable minNelectron{"minNelectron", 0, "min number of electron candidates per collision"}; + o2::framework::Configurable includeITSsa{"includeITSsa", false, "Flag to include ITSsa tracks only for MC. switch ON only if needed."}; + o2::framework::Configurable useTOFNSigmaDeltaBC{"useTOFNSigmaDeltaBC", false, "Flag to shift delta BC for TOF n sigma (only with TTCA)"}; + o2::framework::Configurable useElectronHypothesis{"useElectronHypothesis", true, "force to use trackParCov.setPID(o2::track::PID::Electron)"}; + + o2::framework::Configurable dcaType{"dcaType", 0, "type of DCA cut. 0:3D, 1:XY, 2:Z, else:3D"}; + o2::framework::Configurable max_dca_in_sigma{"max_dca_in_sigma", 1e+10, "max dca in sigma for a single track"}; + + // configuration for PID ML + o2::framework::Configurable usePIDML{"usePIDML", false, "Flag to use PID ML"}; + o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.9, 0.9, 0.9, 0.7, 0.8, 0.8, 0.7, 0.7, 0.7}, "ML cuts per bin"}; + o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"tpcInnerParam", "tpcNClsFound", "tpcChi2NCl", "tpcNSigmaEl", "tofNSigmaEl", "meanClusterSizeITSobCosTgl"}, "Names of ML model input features"}; + o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "tpcInnerParam", "Names of ML model binning feature"}; + o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; +}; + +struct electronPFCut : o2::framework::ConfigurableGroup { + std::string prefix = "electronPFCut"; + o2::framework::Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; + o2::framework::Configurable mincrossedrows{"mincrossedrows", 70, "min. crossed rows"}; + o2::framework::Configurable min_tpc_cr_findable_ratio{"min_tpc_cr_findable_ratio", 0.8, "min. TPC Ncr/Nf ratio"}; + o2::framework::Configurable min_ncluster_its{"min_ncluster_its", 4, "min ncluster its"}; + o2::framework::Configurable min_ncluster_itsib{"min_ncluster_itsib", 1, "min ncluster itsib"}; + o2::framework::Configurable minchi2tpc{"minchi2tpc", 0.0, "min. chi2/NclsTPC"}; + o2::framework::Configurable minchi2its{"minchi2its", 0.0, "min. chi2/NclsITS"}; + o2::framework::Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max. chi2/NclsTPC"}; + o2::framework::Configurable maxchi2its{"maxchi2its", 36.0, "max. chi2/NclsITS"}; + o2::framework::Configurable minpt{"minpt", 0.05, "min pt"}; + o2::framework::Configurable maxeta{"maxeta", 0.9, "max eta"}; + o2::framework::Configurable dca_xy_max{"dca_xy_max", 1.0, "max DCAxy in cm"}; + o2::framework::Configurable dca_z_max{"dca_z_max", 1.0, "max DCAz in cm"}; + o2::framework::Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.0, "min. TPC n sigma for electron inclusion"}; + o2::framework::Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", 3.0, "max. TPC n sigma for electron inclusion"}; + o2::framework::Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", 3.0, "max. TOF n sigma for electron inclusion"}; + o2::framework::Configurable maxTPCNsigmaPi{"maxTPCNsigmaPi", 0.0, "max. TPC n sigma for pion exclusion"}; + o2::framework::Configurable minTPCNsigmaPi{"minTPCNsigmaPi", 0.0, "min. TPC n sigma for pion exclusion"}; // set to -2 for lowB, -1e+10 for nominalB + o2::framework::Configurable maxTPCNsigmaKa{"maxTPCNsigmaKa", 0.0, "max. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable minTPCNsigmaKa{"minTPCNsigmaKa", 0.0, "min. TPC n sigma for kaon exclusion"}; + o2::framework::Configurable maxTPCNsigmaPr{"maxTPCNsigmaPr", 0.0, "max. TPC n sigma for proton exclusion"}; + o2::framework::Configurable minTPCNsigmaPr{"minTPCNsigmaPr", 0.0, "min. TPC n sigma for proton exclusion"}; + // o2::framework::Configurable requireTOF{"requireTOF", false, "require TOF hit"}; + o2::framework::Configurable min_pin_for_pion_rejection{"min_pin_for_pion_rejection", 0.0, "pion rejection is applied above this pin"}; // this is used only in TOFreq + o2::framework::Configurable max_pin_for_pion_rejection{"max_pin_for_pion_rejection", 0.5, "pion rejection is applied below this pin"}; + o2::framework::Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable maxMeanITSClusterSize{"maxMeanITSClusterSize", 16, "max x cos(lambda)"}; + + // configuration for PID ML + o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.15, 0.2, 0.25, 0.4, 0.8, 1.6, 2.0, 4.0, 20}, "Bin limits for ML application"}; + o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.85, 0.85, 0.85, 0.65, 0.75, 0.75, 0.65, 0.65, 0.65}, "ML cuts per bin"}; + + // for pair + o2::framework::Configurable slope{"slope", 0.0185, "slope for m vs. phiv"}; + o2::framework::Configurable intercept{"intercept", -0.0280, "intercept for m vs. phiv"}; + o2::framework::Configurable doPF{"doPF", false, "flag to set pion prefilter"}; +}; + +struct hadronCut : o2::framework::ConfigurableGroup { + std::string prefix = "hadronCut"; + o2::framework::Configurable cfg_min_pt_track{"cfg_min_pt_track", 0.01, "min pT for single track"}; + o2::framework::Configurable cfg_max_pt_track{"cfg_max_pt_track", 1e+10, "max pT for single track"}; + o2::framework::Configurable cfg_min_eta_track{"cfg_min_eta_track", -0.9, "min eta for single track"}; + o2::framework::Configurable cfg_max_eta_track{"cfg_max_eta_track", +0.9, "max eta for single track"}; + o2::framework::Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.f, "min. TPC Ncr/Nf ratio"}; + o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable cfg_min_ncrossedrows_tpc{"cfg_min_ncrossedrows_tpc", 70, "min ncrossed rows"}; + o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; + o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 4, "min ncluster its"}; + o2::framework::Configurable cfg_min_ncluster_itsib{"cfg_min_ncluster_itsib", 1, "min ncluster itsib"}; + o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; + o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 36.0, "max chi2/NclsITS"}; + o2::framework::Configurable cfg_max_dcaxy{"cfg_max_dcaxy", 1.0, "max dca XY for single track in cm"}; + o2::framework::Configurable cfg_max_dcaz{"cfg_max_dcaz", 1.0, "max dca Z for single track in cm"}; + o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -3, "min n sigma pi in TPC"}; + o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3, "max n sigma pi in TPC"}; + o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3, "min n sigma ka in TPC"}; + o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3, "max n sigma ka in TPC"}; + o2::framework::Configurable requirePiKa{"requirePiKa", true, "require hadron to be pion or kaon"}; // protons are not involved in semileptonic decays of HF hadrons. +}; + +// struct v0Cut : o2::framework::ConfigurableGroup { +// std::string prefix = "v0Cut"; +// o2::framework::Configurable cfg_min_mass_k0s{"cfg_min_mass_k0s", 0.48, "min mass for K0S"}; +// o2::framework::Configurable cfg_max_mass_k0s{"cfg_max_mass_k0s", 0.51, "max mass for K0S"}; +// o2::framework::Configurable cfg_min_mass_k0s_veto{"cfg_min_mass_k0s_veto", 0.48, "min mass for K0S veto for Lambda"}; +// o2::framework::Configurable cfg_max_mass_k0s_veto{"cfg_max_mass_k0s_veto", 0.51, "max mass for K0S veto for Lambda"}; +// o2::framework::Configurable cfg_min_mass_lambda{"cfg_min_mass_lambda", 1.11, "min mass for Lambda"}; +// o2::framework::Configurable cfg_max_mass_lambda{"cfg_max_mass_lambda", 1.12, "max mass for Lambda"}; +// o2::framework::Configurable cfg_min_mass_lambda_veto{"cfg_min_mass_lambda_veto", 1.11, "min mass for Lambda veto for K0S"}; +// o2::framework::Configurable cfg_max_mass_lambda_veto{"cfg_max_mass_lambda_veto", 1.12, "max mass for Lambda veto for K0S"}; +// o2::framework::Configurable cfg_min_cospa{"cfg_min_cospa", 0.95, "min cospa for v0"}; +// o2::framework::Configurable cfg_max_dca2legs{"cfg_max_dca2legs", 0.1, "max distance between 2 legs for v0"}; +// o2::framework::Configurable cfg_min_radius{"cfg_min_radius", 0.1, "min rxy for v"}; +// o2::framework::Configurable cfg_min_cr2findable_ratio_tpc{"cfg_min_cr2findable_ratio_tpc", 0.f, "min. TPC Ncr/Nf ratio"}; +// o2::framework::Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; +// o2::framework::Configurable cfg_min_ncrossedrows_tpc{"cfg_min_ncrossedrows_tpc", 70, "min ncrossed rows"}; +// o2::framework::Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; +// o2::framework::Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; +// o2::framework::Configurable cfg_max_chi2its{"cfg_max_chi2its", 36.0, "max chi2/NclsITS"}; +// o2::framework::Configurable cfg_min_ncluster_its{"cfg_min_ncluster_its", 2, "min ncluster its"}; +// o2::framework::Configurable cfg_min_ncluster_itsib{"cfg_min_ncluster_itsib", 0, "min ncluster itsib"}; +// o2::framework::Configurable cfg_min_dcaxy{"cfg_min_dcaxy", 0.1, "min dca XY for v0 legs in cm"}; +// +// o2::framework::Configurable cfg_max_alpha_veto{"cfg_max_alpha_veto", 0.95, "max alpha for photon conversion rejection"}; +// o2::framework::Configurable cfg_max_qt_veto{"cfg_max_qt_veto", 0.01, "max qT for photon conversion rejection"}; +// +// // for both v0 and cascade +// o2::framework::Configurable cfg_min_TPCNsigmaPi{"cfg_min_TPCNsigmaPi", -3, "min n sigma pi in TPC"}; +// o2::framework::Configurable cfg_max_TPCNsigmaPi{"cfg_max_TPCNsigmaPi", +3, "max n sigma pi in TPC"}; +// o2::framework::Configurable cfg_min_TPCNsigmaKa{"cfg_min_TPCNsigmaKa", -3, "min n sigma ka in TPC"}; +// o2::framework::Configurable cfg_max_TPCNsigmaKa{"cfg_max_TPCNsigmaKa", +3, "max n sigma ka in TPC"}; +// o2::framework::Configurable cfg_min_TPCNsigmaPr{"cfg_min_TPCNsigmaPr", -3, "min n sigma pr in TPC"}; +// o2::framework::Configurable cfg_max_TPCNsigmaPr{"cfg_max_TPCNsigmaPr", +3, "max n sigma pr in TPC"}; +// }; +// +// struct cascadeCut : o2::framework::ConfigurableGroup { +// std::string prefix = "cascadeCut"; +// o2::framework::Configurable cfg_min_mass_lambda{"cfg_min_mass_lambda", 1.11, "min mass for lambda in cascade"}; +// o2::framework::Configurable cfg_max_mass_lambda{"cfg_max_mass_lambda", 1.12, "max mass for lambda in cascade"}; +// o2::framework::Configurable cfg_min_mass_Xi{"cfg_min_mass_Xi", 1.314, "min mass for Xi"}; +// o2::framework::Configurable cfg_max_mass_Xi{"cfg_max_mass_Xi", 1.328, "max mass for Xi"}; +// o2::framework::Configurable cfg_min_mass_Xi_veto{"cfg_min_mass_Xi_veto", 1.31, "min mass for Xi veto"}; +// o2::framework::Configurable cfg_max_mass_Xi_veto{"cfg_max_mass_Xi_veto", 1.33, "max mass for Xi veto"}; +// o2::framework::Configurable cfg_min_mass_Omega{"cfg_min_mass_Omega", 1.668, "min mass for Omega"}; +// o2::framework::Configurable cfg_max_mass_Omega{"cfg_max_mass_Omega", 1.678, "max mass for Omega"}; +// o2::framework::Configurable cfg_min_mass_Omega_veto{"cfg_min_mass_Omega_veto", 1.665, "min mass for Omega veto"}; +// o2::framework::Configurable cfg_max_mass_Omega_veto{"cfg_max_mass_Omega_veto", 1.680, "max mass for Omega veto"}; +// o2::framework::Configurable cfg_min_cospa_v0{"cfg_min_cospa_v0", 0.95, "minimum V0 CosPA in cascade"}; +// o2::framework::Configurable cfg_max_dcadau_v0{"cfg_max_dcadau_v0", 0.1, "max distance between V0 Daughters in cascade"}; +// o2::framework::Configurable cfg_min_cospa{"cfg_min_cospa", 0.95, "minimum cascade CosPA"}; +// o2::framework::Configurable cfg_max_dcadau{"cfg_max_dcadau", 0.1, "max distance between bachelor and V0"}; +// o2::framework::Configurable cfg_min_rxy_v0{"cfg_min_rxy_v0", 0.1, "minimum V0 rxy in cascade"}; +// o2::framework::Configurable cfg_min_rxy{"cfg_min_rxy", 0.1, "minimum V0 rxy in cascade"}; +// o2::framework::Configurable cfg_min_dcaxy_v0leg{"cfg_min_dcaxy_v0leg", 0.1, "min dca XY for v0 legs in cm"}; +// o2::framework::Configurable cfg_min_dcaxy_bachelor{"cfg_min_dcaxy_bachelor", 0.05, "min dca XY for bachelor in cm"}; +// o2::framework::Configurable cfg_min_dcaxy_v0{"cfg_min_dcaxy_v0", 0.0, "min dca XY for V0 in cm"}; +// }; + +struct cfgDFeT : o2::framework::ConfigurableGroup { + std::string prefix = "cfgDFeT"; + o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; + o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; + o2::framework::Configurable maxChi2PCA{"maxChi2PCA", 1.0, "max chi2 at PCA"}; + o2::framework::Configurable maxMassLH{"maxMassLH", 5.5, "max massLH in GeV/c2"}; // set hb mass. SVs whose mass is above this mass cannot be HF hadrons. + // configuration for PID ML + o2::framework::Configurable useML{"useML", false, "Flag to use PID ML"}; + o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; + o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; + o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.4, 0.8, 1.0, 2.0, 4, 20}, "Bin limits for ML application"}; + // o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9}, "ML cuts per bin"}; + o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"ptH", "impPar3DHinSigma", "tpcNSigmaKa", "signedMassLH", "logChi2PCA", "cpa", "cpaXY", "decayLength3DinSigma"}, "Names of ML model input features"}; + o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "ptL", "Names of ML model binning feature"}; + o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; +}; + +// struct cfgDFeV0 : o2::framework::ConfigurableGroup { +// std::string prefix = "cfgDFeV0"; +// o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; +// o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; +// o2::framework::Configurable maxChi2PCA{"maxChi2PCA", 1.0, "max chi2 at PCA"}; +// o2::framework::Configurable maxMassLH{"maxMassLH", 5.5, "max massLH in GeV/c2"}; // set hb mass. SVs whose mass is above this mass cannot be HF hadrons. +// // configuration for PID ML +// o2::framework::Configurable useML{"useML", false, "Flag to use PID ML"}; +// o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; +// o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; +// o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.4, 0.8, 1.0, 2.0, 4, 20}, "Bin limits for ML application"}; +// // o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9}, "ML cuts per bin"}; +// o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"ptH", "impPar3DHinSigma", "massLH", "logChi2PCA", "cpa", "cpaXY", "decayLength3DinSigma"}, "Names of ML model input features"}; +// o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "ptL", "Names of ML model binning feature"}; +// o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; +// o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; +// }; +// +// struct cfgDFeC : o2::framework::ConfigurableGroup { +// std::string prefix = "cfgDFeC"; +// o2::framework::Configurable useAbsDCA{"useAbsDCA", true, "Minimise abs. distance rather than chi2"}; +// o2::framework::Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; +// o2::framework::Configurable maxChi2PCA{"maxChi2PCA", 1.0, "max chi2 at PCA"}; +// o2::framework::Configurable maxMassLH{"maxMassLH", 5.5, "max massLH in GeV/c2"}; // set hb mass. SVs whose mass is above this mass cannot be HF hadrons. +// // configuration for PID ML +// o2::framework::Configurable useML{"useML", false, "Flag to use PID ML"}; +// o2::framework::Configurable> onnxFileNames{"onnxFileNames", std::vector{"filename"}, "ONNX file names for each bin (if not from CCDB full path)"}; +// o2::framework::Configurable> onnxPathsCCDB{"onnxPathsCCDB", std::vector{"path"}, "Paths of models on CCDB"}; +// o2::framework::Configurable> binsMl{"binsMl", std::vector{0.1, 0.4, 0.8, 1.0, 2.0, 4, 20}, "Bin limits for ML application"}; +// // o2::framework::Configurable> cutsMl{"cutsMl", std::vector{0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9}, "ML cuts per bin"}; +// o2::framework::Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"ptH", "impPar3DHinSigma", "massLH", "logChi2PCA", "cpa", "cpaXY", "decayLength3DinSigma"}, "Names of ML model input features"}; +// o2::framework::Configurable nameBinningFeature{"nameBinningFeature", "ptL", "Names of ML model binning feature"}; +// o2::framework::Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; +// o2::framework::Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; +// }; + +class ElectronModule +{ + public: + ElectronModule() + { + // constructor + } + ~ElectronModule() + { + // destructor + } + + // struct looseElectron { + // float pt{1e+10}; + // float eta{1e+10}; + // float phi{1e+10}; + // }; + + template + void init(TElectronCut const& eCut, TElectronPFCut const& ePFCut, THadronCut const& hCut, /*TV0Cut const& v0Cut, TCascadeCut const& cascadeCut,*/ TDFConfigET const& cfgET, /*TDFConfigEV0 const& cfgEV0, TDFConfigEC const& cfgEC,*/ TInitContext& initContext, TCCDB& ccdb, TTOFResponse const& tofResponse, std::string const& ccdburl) + { + mRunNumber = 0; + d_bz = 0; + + ccdbApi.init(ccdburl); + + LOGF(info, "intializing configurations"); + fElectronCut = eCut; + fElectronPFCut = ePFCut; + fHadronCut = hCut; + // fV0Cut = v0Cut; + // fCascadeCut = cascadeCut; + fConfigDFeT = cfgET; + // fConfigDFeV0 = cfgEV0; + // fConfigDFeC = cfgEC; + + LOGF(info, "intializing TOFResponse"); + mTOFResponse = tofResponse; + mTOFResponse->initSetup(ccdb, initContext); + + dfeT.setPropagateToPCA(true); + dfeT.setMaxR(200.f); + dfeT.setMinParamChange(1e-3); + dfeT.setMinRelChi2Change(0.9); + dfeT.setMaxDZIni(1e9); + dfeT.setMaxChi2(1e9); + dfeT.setUseAbsDCA(fConfigDFeT.useAbsDCA); + dfeT.setWeightedFinalPCA(fConfigDFeT.useWeightedFinalPCA); + dfeT.setMatCorrType(matCorr); + + // dfeV0.setPropagateToPCA(true); + // dfeV0.setMaxR(200.f); + // dfeV0.setMinParamChange(1e-3); + // dfeV0.setMinRelChi2Change(0.9); + // dfeV0.setMaxDZIni(1e9); + // dfeV0.setMaxChi2(1e9); + // dfeV0.setUseAbsDCA(fConfigDFeV0.useAbsDCA); + // dfeV0.setWeightedFinalPCA(fConfigDFeV0.useWeightedFinalPCA); + // dfeV0.setMatCorrType(matCorr); + + // dfeC.setPropagateToPCA(true); + // dfeC.setMaxR(200.f); + // dfeC.setMinParamChange(1e-3); + // dfeC.setMinRelChi2Change(0.9); + // dfeC.setMaxDZIni(1e9); + // dfeC.setMaxChi2(1e9); + // dfeC.setUseAbsDCA(fConfigDFeC.useAbsDCA); + // dfeC.setWeightedFinalPCA(fConfigDFeC.useWeightedFinalPCA); + // dfeC.setMatCorrType(matCorr); + } + + template + void initCCDB(TBC const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + d_bz = o2::base::Propagator::Instance()->getNominalBz(); + LOG(info) << "Configuring for timestamp " << bc.timestamp() << " with magnetic field of " << d_bz << " kG"; + + dfeT.setBz(d_bz); + // dfeV0.setBz(d_bz); + // dfeC.setBz(d_bz); + + // initialize MLResponse + if (fElectronCut.usePIDML) { + LOGF(info, "loading ONNX for ML PID"); + static constexpr int nClassesMl = 2; + const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutSmaller}; + const std::vector labelsClasses = {"Background", "Signal"}; + const uint32_t nBinsMl = fElectronCut.binsMl.value.size() - 1; + const std::vector labelsBins(nBinsMl, "bin"); + double cutsMlArr[nBinsMl][nClassesMl]; + for (uint32_t i = 0; i < nBinsMl; i++) { + cutsMlArr[i][0] = 0.0; + cutsMlArr[i][1] = fElectronCut.cutsMl.value[i]; + } + o2::framework::LabeledArray cutsMltmp = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; + + mlResponsePID.configure(fElectronCut.binsMl.value, cutsMltmp, cutDirMl, nClassesMl); + if (fElectronCut.loadModelsFromCCDB) { + mlResponsePID.setModelPathsCCDB(fElectronCut.onnxFileNames, ccdbApi, fElectronCut.onnxPathsCCDB, bc.timestamp()); + } else { + mlResponsePID.setModelPathsLocal(fElectronCut.onnxFileNames); + } + mlResponsePID.cacheInputFeaturesIndices(fElectronCut.namesInputFeatures); + mlResponsePID.cacheBinningIndex(fElectronCut.nameBinningFeature); + mlResponsePID.init(fElectronCut.enableOptimizations); + } // end of ML PID + + if (fConfigDFeT.useML) { + LOGF(info, "loading ONNX for SCT eT"); + static constexpr int nClassesMl = 4; + const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutNot, o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}; + const std::vector labelsClasses = {"background", "prompthc", "nonprompthc", "hb"}; + const uint32_t nBinsMl = fConfigDFeT.binsMl.value.size() - 1; + const std::vector labelsBins(nBinsMl, "bin"); + double cutsMlArr[nBinsMl][nClassesMl]; + for (uint32_t i = 0; i < nBinsMl; i++) { + cutsMlArr[i][0] = 0.0; + cutsMlArr[i][1] = 0.0; + cutsMlArr[i][2] = 0.0; + cutsMlArr[i][3] = 0.0; + } + o2::framework::LabeledArray cutsMltmp = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; + + mlResponseSCTeT.configure(fConfigDFeT.binsMl.value, cutsMltmp, cutDirMl, nClassesMl); + if (fConfigDFeT.loadModelsFromCCDB) { + mlResponseSCTeT.setModelPathsCCDB(fConfigDFeT.onnxFileNames, ccdbApi, fConfigDFeT.onnxPathsCCDB, bc.timestamp()); + } else { + mlResponseSCTeT.setModelPathsLocal(fConfigDFeT.onnxFileNames); + } + mlResponseSCTeT.cacheInputFeaturesIndices(fConfigDFeT.namesInputFeatures); + mlResponseSCTeT.cacheBinningIndex(fConfigDFeT.nameBinningFeature); + mlResponseSCTeT.init(fConfigDFeT.enableOptimizations); + } // end of ML SCTeT + + // if (fConfigDFeV0.useML) { + // LOGF(info, "loading ONNX for SCT eV0"); + // static constexpr int nClassesMl = 4; + // const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutNot, o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}; + // const std::vector labelsClasses = {"background", "prompthc", "nonprompthc", "hb"}; + // const uint32_t nBinsMl = fConfigDFeV0.binsMl.value.size() - 1; + // const std::vector labelsBins(nBinsMl, "bin"); + // double cutsMlArr[nBinsMl][nClassesMl]; + // for (uint32_t i = 0; i < nBinsMl; i++) { + // cutsMlArr[i][0] = 0.0; + // cutsMlArr[i][1] = 0.0; + // cutsMlArr[i][2] = 0.0; + // cutsMlArr[i][3] = 0.0; + // } + // o2::framework::LabeledArray cutsMltmp = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; + + // mlResponseSCTeV0.configure(fConfigDFeV0.binsMl.value, cutsMltmp, cutDirMl, nClassesMl); + // if (fConfigDFeV0.loadModelsFromCCDB) { + // mlResponseSCTeV0.setModelPathsCCDB(fConfigDFeV0.onnxFileNames, ccdbApi, fConfigDFeV0.onnxPathsCCDB, bc.timestamp()); + // } else { + // mlResponseSCTeV0.setModelPathsLocal(fConfigDFeV0.onnxFileNames); + // } + // mlResponseSCTeV0.cacheInputFeaturesIndices(fConfigDFeV0.namesInputFeatures); + // mlResponseSCTeV0.cacheBinningIndex(fConfigDFeV0.nameBinningFeature); + // mlResponseSCTeV0.init(fConfigDFeV0.enableOptimizations); + // } // end of ML SCTeV0 + + // if (fConfigDFeC.useML) { + // LOGF(info, "loading ONNX for SCT eC"); + // static constexpr int nClassesMl = 3; + // const std::vector cutDirMl = {o2::cuts_ml::CutNot, o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}; + // const std::vector labelsClasses = {"background", "prompthc", "nonprompthc"}; + // const uint32_t nBinsMl = fConfigDFeC.binsMl.value.size() - 1; + // const std::vector labelsBins(nBinsMl, "bin"); + // double cutsMlArr[nBinsMl][nClassesMl]; + // for (uint32_t i = 0; i < nBinsMl; i++) { + // cutsMlArr[i][0] = 0.0; + // cutsMlArr[i][1] = 0.0; + // cutsMlArr[i][2] = 0.0; + // } + // o2::framework::LabeledArray cutsMltmp = {cutsMlArr[0], nBinsMl, nClassesMl, labelsBins, labelsClasses}; + + // mlResponseSCTeC.configure(fConfigDFeC.binsMl.value, cutsMltmp, cutDirMl, nClassesMl); + // if (fConfigDFeC.loadModelsFromCCDB) { + // mlResponseSCTeC.setModelPathsCCDB(fConfigDFeC.onnxFileNames, ccdbApi, fConfigDFeC.onnxPathsCCDB, bc.timestamp()); + // } else { + // mlResponseSCTeC.setModelPathsLocal(fConfigDFeC.onnxFileNames); + // } + // mlResponseSCTeC.cacheInputFeaturesIndices(fConfigDFeC.namesInputFeatures); + // mlResponseSCTeC.cacheBinningIndex(fConfigDFeC.nameBinningFeature); + // mlResponseSCTeC.init(fConfigDFeC.enableOptimizations); + // } // end of ML SCTeC + + mRunNumber = bc.runNumber(); + mTOFResponse->processSetup(bc); + } + + template + void addHistograms(THistoregistry& registry) + { + registry.add("Track/hPt", "pT;p_{T} (GeV/c)", o2::framework::HistType::kTH1F, {{1000, 0.0f, 10}}, false); + registry.add("Track/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", o2::framework::HistType::kTH1F, {{4000, -20, 20}}, false); + registry.add("Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", o2::framework::HistType::kTH2F, {{180, 0, 2 * M_PI}, {20, -1.0f, 1.0f}}, false); + registry.add("Track/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", o2::framework::HistType::kTH2F, {{200, -1.0f, 1.0f}, {200, -1.0f, 1.0f}}, false); + registry.add("Track/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", o2::framework::HistType::kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); + registry.add("Track/hDCA3dSigma", "DCA 3d;DCA_{3D} (#sigma);", o2::framework::HistType::kTH1F, {{100, 0.0f, 10.0f}}, false); + registry.add("Track/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {500, 0., 500}}, false); + registry.add("Track/hDCAzRes_Pt", "DCA_{z} resolution vs. pT;p_{T} (GeV/c);DCA_{z} resolution (#mum)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {500, 0., 500}}, false); + registry.add("Track/hNclsTPC", "number of TPC clusters", o2::framework::HistType::kTH1F, {{161, -0.5, 160.5}}, false); + registry.add("Track/hNcrTPC", "number of TPC crossed rows", o2::framework::HistType::kTH1F, {{161, -0.5, 160.5}}, false); + registry.add("Track/hChi2TPC", "chi2/number of TPC clusters", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + registry.add("Track/hChi2TOF", "chi2 of TOF", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + registry.add("Track/hTPCNcr2Nf", "TPC Ncr/Nfindable", o2::framework::HistType::kTH1F, {{200, 0, 2}}, false); + registry.add("Track/hTPCNcls2Nf", "TPC Ncls/Nfindable", o2::framework::HistType::kTH1F, {{200, 0, 2}}, false); + registry.add("Track/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); + registry.add("Track/hNclsITS", "number of ITS clusters", o2::framework::HistType::kTH1F, {{8, -0.5, 7.5}}, false); + registry.add("Track/hChi2ITS", "chi2/number of ITS clusters", o2::framework::HistType::kTH1F, {{72, 0, 36}}, false); + registry.add("Track/hITSClusterMap", "ITS cluster map", o2::framework::HistType::kTH1F, {{128, -0.5, 127.5}}, false); + registry.add("Track/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + registry.add("Track/hTPCdEdxMC", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + registry.add("Track/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + registry.add("Track/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + registry.add("Track/hTPCNsigmaKa", "TPC n sigma ka;p_{in} (GeV/c);n #sigma_{K}^{TPC}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + registry.add("Track/hTPCNsigmaPr", "TPC n sigma pr;p_{in} (GeV/c);n #sigma_{p}^{TPC}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + registry.add("Track/hTOFbeta", "TOF beta;p_{pv} (GeV/c);#beta", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {240, 0, 1.2}}, false); + registry.add("Track/hTOFNsigmaEl", "TOF n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TOF}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + // registry.add("Track/hTOFNsigmaPi", "TOF n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TOF}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + // registry.add("Track/hTOFNsigmaKa", "TOF n sigma ka;p_{in} (GeV/c);n #sigma_{K}^{TOF}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + // registry.add("Track/hTOFNsigmaPr", "TOF n sigma pr;p_{in} (GeV/c);n #sigma_{p}^{TOF}", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + registry.add("Track/hMeanClusterSizeITS", "mean cluster size ITS;p_{pv} (GeV/c); #times cos(#lambda)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + registry.add("Track/hMeanClusterSizeITSib", "mean cluster size ITSib;p_{pv} (GeV/c); #times cos(#lambda)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + registry.add("Track/hMeanClusterSizeITSob", "mean cluster size ITSob;p_{pv} (GeV/c); #times cos(#lambda)", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + registry.add("Track/hProbElBDT", "probability to be e from BDT;p_{in} (GeV/c);BDT score;", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); + registry.add("Track/hNe", "electron counts;N_{e} per collision", o2::framework::HistType::kTH1F, {{51, -0.5, 50.5}}, false); + + registry.add("Prefilter/Track/hPt", "pT;p_{T} (GeV/c)", o2::framework::HistType::kTH1F, {{1000, 0.0f, 10}}, false); + registry.add("Prefilter/Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", o2::framework::HistType::kTH2F, {{180, 0, 2 * M_PI}, {40, -2, 2}}, false); + registry.add("Prefilter/Track/hTPCdEdx", "TPC dE/dx vs. pin;p_{in} (GeV/c);TPC dE/dx", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + registry.add("Prefilter/Track/hTOFbeta", "TOF #beta vs. p;p_{pv} (GeV/c);TOF #beta", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {600, 0, 1.2}}, false); + registry.add("Prefilter/Pair/hMvsPhiV", "mass vs. phiv;#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH2F, {{90, 0.f, M_PI}, {100, 0, 0.1}}); + + // for charged hadron + registry.add("SCT/Track/hs", "hs;p_{T} (GeV/c);#eta;#varphi (rad.)", o2::framework::HistType::kTHnSparseF, {{100, 0, 10}, {80, -2, 2}, {36, 0, 2 * M_PI}}, false); + registry.add("SCT/Track/hDCA", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", o2::framework::HistType::kTH2F, {{200, -1, 1}, {200, -1, 1}}, false); + registry.add("SCT/Track/hTPCdEdx", "TPC dE/dx vs. pin;p_{in} (GeV/c);TPC dE/dx", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + registry.add("SCT/Track/hTOFbeta", "TOF #beta vs. p;p_{pv} (GeV/c);TOF #beta", o2::framework::HistType::kTH2F, {{1000, 0, 10}, {600, 0, 1.2}}, false); + + // for V0 + registry.add("SCT/V0/hPt", "pT of V0;p_{T} (GeV/c)", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + registry.add("SCT/V0/hYPhi_K0S", "rapidity vs. #varphi of V0;#varphi (rad.);rapidity_{K0S}", o2::framework::HistType::kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); + registry.add("SCT/V0/hYPhi_Lambda", "rapidity vs. #varphi of V0;#varphi (rad.);rapidity_{#Lambda}", o2::framework::HistType::kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); + registry.add("SCT/V0/hAP", "AP plot;#alpha;q_{T} (GeV/c)", o2::framework::HistType::kTH2F, {{200, -1, 1}, {250, 0, 0.25}}, false); + registry.add("SCT/V0/hLxy", "decay length from PV;L_{xy} (cm)", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + registry.add("SCT/V0/hCosPA", "cosPA;cosine of pointing angle", o2::framework::HistType::kTH1F, {{100, 0.9, 1}}, false); + registry.add("SCT/V0/hDCA2Legs", "distance between 2 legs at PCA;distance between 2 legs (cm)", o2::framework::HistType::kTH1F, {{100, 0, 1}}, false); + registry.add("SCT/V0/hMassK0S", "K0S mass;m_{#pi#pi} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 0.45, 0.55}}, false); + registry.add("SCT/V0/hMassLambda", "Lambda mass;m_{p#pi^{#minus}} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.08, 1.18}}, false); + registry.add("SCT/V0/hMassAntiLambda", "Anti-Lambda mass;m_{#bar{p}#pi^{+}} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.08, 1.18}}, false); + registry.add("SCT/V0/hMassGamma_misid", "#gamma mass;m_{ee} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 0, 0.1}}, false); + registry.add("SCT/V0/hMassK0S_misid", "K0S mass;m_{#pi#pi} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 0.45, 0.55}}, false); + registry.add("SCT/V0/hMassLambda_misid", "Lambda mass;m_{p#pi^{#minus}} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.08, 1.18}}, false); + registry.add("SCT/V0/hMassAntiLambda_misid", "Anti-Lambda mass;m_{#bar{p}#pi^{+}} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.08, 1.18}}, false); + + // for cascadeSCT/ + registry.add("SCT/Cascade/hPt", "pT of cascade;p_{T} (GeV/c)", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + registry.add("SCT/Cascade/hYPhi_Xi", "rapidity vs. #varphi of cascade;#varphi (rad.);rapidity_{#Xi}", o2::framework::HistType::kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); + registry.add("SCT/Cascade/hYPhi_Omega", "rapidity vs. #varphi of cascade;#varphi (rad.);rapidity_{#Omega}", o2::framework::HistType::kTH2F, {{90, 0, 2 * M_PI}, {80, -2, +2}}, false); + registry.add("SCT/Cascade/hCosPA", "cosPA;cosine of pointing angle", o2::framework::HistType::kTH1F, {{100, 0.9, 1}}, false); + registry.add("SCT/Cascade/hDCA2Legs", "distance between 2 legs at PCA;distance between 2 legs (cm)", o2::framework::HistType::kTH1F, {{100, 0, 1}}, false); + registry.add("SCT/Cascade/hV0CosPA", "cosPA of V0 in cascade;cosine of pointing angle", o2::framework::HistType::kTH1F, {{100, 0.9, 1}}, false); + registry.add("SCT/Cascade/hV0DCA2Legs", "distance between 2 legs at PCA of V0 in cascade;distance between 2 legs (cm)", o2::framework::HistType::kTH1F, {{100, 0, 1}}, false); + registry.add("SCT/Cascade/hMassLambda", "Lambda mass;m_{p#pi^{-}} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.08, 1.18}}, false); + registry.add("SCT/Cascade/hMassXi", "#Xi mass;m_{#Lambda#pi} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.27, 1.37}}, false); + registry.add("SCT/Cascade/hMassOmega", "#Omega mass;m_{#LambdaK} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.62, 1.72}}, false); + registry.add("SCT/Cascade/hMassXi_misid", "#Xi mass;m_{#Lambda#pi} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.27, 1.37}}, false); + registry.add("SCT/Cascade/hMassOmega_misid", "#Omega mass;m_{#LambdaK} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 1.62, 1.72}}, false); + + registry.add("SCT/eT/hDecayLength", "eH decay length;L_{xy} (cm);L_{z} (cm);", o2::framework::HistType::kTH2F, {{100, 0, 1}, {200, -1, 1}}, false); + registry.add("SCT/eT/hCosPA", "eH cosPA;cosine of pointing angle", o2::framework::HistType::kTH1F, {{200, -1, 1}}, false); + registry.add("SCT/eT/hDCA2legs", "dca between e and hadron;dca between 2legs (cm)", o2::framework::HistType::kTH1F, {{100, 0, 1}}, false); + registry.add("SCT/eT/hMass", "invariant mass of e-h;m_{eh} (GeV/c^{2})", o2::framework::HistType::kTH1F, {{100, 0, 10}}, false); + registry.addClone("SCT/eT/", "SCT/eV0/"); + registry.addClone("SCT/eT/", "SCT/eC/"); + } + + template + void calculateTOFNSigmaWithReassociation(TCollisions const& collisions, TBCs const&, TTracks const& tracks, TTrackAssoc const& trackIndices, TSliceCache const&, TPresliceTrack const& perColTrack, TPresliceTrackAssoc const& trackIndicesPerCollision) + { + if (fElectronCut.useTOFNSigmaDeltaBC) { + if constexpr (withTTCA) { + std::unordered_map mapCollisionTime; + std::unordered_map mapCollisionTimeError; + for (const auto& track : tracks) { + if (mapCollisionTime.find(track.collisionId()) == mapCollisionTime.end()) { + mapCollisionTime[track.collisionId()] = track.tofEvTime(); + mapCollisionTimeError[track.collisionId()] = track.tofEvTimeErr(); + } + } + + for (const auto& collision : collisions) { + if (mapCollisionTime.find(collision.globalIndex()) == mapCollisionTime.end()) { + continue; + } + auto bcCollision = collision.template bc_as(); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + + if (track.hasTOF() && track.has_collision()) { // TTCA may use orphan tracks. + auto bcTrack = track.template collision_as().template bc_as(); + fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + // fMapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + // fMapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + // fMapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.length() / (track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()) - mapCollisionTime[collision.globalIndex()]) / (TMath::C() * 1e+2 * 1e-12); + } else { + fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + // fMapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + // fMapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + // fMapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } + } // end of track loop + } // end of collision loop + mapCollisionTime.clear(); + mapCollisionTimeError.clear(); + } else { // without TTCA + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perColTrack, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + // fMapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + // fMapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + // fMapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } + } // end of track loop + } // end of collision loop + } else { + if constexpr (withTTCA) { + for (const auto& collision : collisions) { + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + // fMapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + // fMapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + // fMapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } // end of track loop + } // end of collision loop + } else { + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perColTrack, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + // fMapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPi(); + // fMapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaKa(); + // fMapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaPr(); + fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + } + } // end of track loop + } // end of collision loop + } + } + + template + void fillMapMLPID(TCollision const& collision, TTrack const& track) + { + if (fElectronCut.usePIDML) { + if (!isElectron_TOFif(track, collision, fElectronCut)) { // minimal n sigma cut is taken from the main electron cut. + return; + } + + o2::dataformats::DCA mDcaInfoCov; + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : track.pidForTracking()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + return; + } + + o2::analysis::pwgem::dilepton::mlpid::candidate candidate; + candidate.tpcInnerParam = track.tpcInnerParam(); + candidate.tpcNClsFound = track.tpcNClsFound(); + candidate.tpcChi2NCl = track.tpcChi2NCl(); + candidate.tpcNSigmaEl = track.tpcNSigmaEl(); + candidate.tofNSigmaEl = fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + int total_cluster_size_ob = 0, nl_ob = 0; + for (unsigned int layer = 3; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl_ob++; + } + total_cluster_size_ob += cluster_size_per_layer; + } + candidate.meanClusterSizeITSobCosTgl = static_cast(total_cluster_size_ob) / static_cast(nl_ob) * std::cos(std::atan(trackParCov.getTgl())); + + auto inputFeatures = mlResponsePID.getInputFeatures(candidate); + float binningFeature = mlResponsePID.getBinningFeature(candidate); + + int pbin = lower_bound(fElectronCut.binsMl.value.begin(), fElectronCut.binsMl.value.end(), binningFeature) - fElectronCut.binsMl.value.begin() - 1; + if (pbin < 0) { + pbin = 0; + } else if (static_cast(fElectronCut.binsMl.value.size()) - 2 < pbin) { + pbin = static_cast(fElectronCut.binsMl.value.size()) - 2; + } + + float probaEl = mlResponsePID.getModelOutput(inputFeatures, pbin)[1]; // 0: hadron, 1:electron + fMapProbaEl[std::make_pair(collision.globalIndex(), track.globalIndex())] = probaEl; + } else { + fMapProbaEl[std::make_pair(collision.globalIndex(), track.globalIndex())] = 1.0; + } + } + + template + bool checkTrack(TCollision const& collision, TTrack const& track) + { + if constexpr (isMC) { + if (!track.has_mcParticle()) { + return false; + } + if (fElectronCut.storeOnlyTrueElectronMC) { + const auto& mcParticle = track.template mcParticle_as(); + if (std::abs(mcParticle.pdgCode()) != 11) { + return false; + } + } + } + + float tofNSigmaEl = fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + if (fElectronCut.requireTOF && !(track.hasTOF() && std::fabs(tofNSigmaEl) < fElectronCut.maxTOFNsigmaEl)) { + return false; + } + + if (!track.hasITS()) { + return false; + } + + if (track.itsChi2NCl() < fElectronCut.minchi2its || fElectronCut.maxchi2its < track.itsChi2NCl()) { + return false; + } + if (track.itsNCls() < fElectronCut.min_ncluster_its) { + return false; + } + if (track.itsNClsInnerBarrel() < fElectronCut.min_ncluster_itsib) { + return false; + } + + if (!fElectronCut.includeITSsa && (!track.hasITS() || !track.hasTPC())) { + return false; + } + + if (track.hasTPC()) { + if (track.tpcChi2NCl() < fElectronCut.minchi2tpc || fElectronCut.maxchi2tpc < track.tpcChi2NCl()) { + return false; + } + + if (track.tpcNClsFound() < fElectronCut.min_ncluster_tpc) { + return false; + } + + if (track.tpcNClsCrossedRows() < fElectronCut.mincrossedrows) { + return false; + } + + if (track.tpcCrossedRowsOverFindableCls() < fElectronCut.min_tpc_cr_findable_ratio) { + return false; + } + + if (track.tpcFractionSharedCls() > fElectronCut.max_frac_shared_clusters_tpc) { + return false; + } + } + + o2::dataformats::DCA mDcaInfoCov; + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : track.pidForTracking()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + return false; + } + float dcaXY = mDcaInfoCov.getY(); + float dcaZ = mDcaInfoCov.getZ(); + + if (std::fabs(dcaXY) > fElectronCut.dca_xy_max || std::fabs(dcaZ) > fElectronCut.dca_z_max) { + return false; + } + + if ((std::array{dca3DinSigmaOTF(dcaXY, dcaZ, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()), dcaXY / std::sqrt(trackParCov.getSigmaY2()), dcaZ / std::sqrt(trackParCov.getSigmaZ2())}[fElectronCut.dcaType]) > fElectronCut.max_dca_in_sigma) { + return false; + } + + if (trackParCov.getPt() < fElectronCut.minpt || std::fabs(trackParCov.getEta()) > fElectronCut.maxeta) { + return false; + } + + int total_cluster_size = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl++; + } + total_cluster_size += cluster_size_per_layer; + } + + if (fElectronCut.maxMeanITSClusterSize < static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))) { + return false; + } + + return true; + } + + template + bool checkTrackPF(TCollision const&, TTrack const& track) + { + if constexpr (isMC) { + if (!track.has_mcParticle()) { + return false; + } + } + + if (!track.hasITS() || !track.hasTPC()) { + return false; + } + + if (track.itsChi2NCl() < fElectronPFCut.minchi2its || fElectronPFCut.maxchi2its < track.itsChi2NCl()) { + return false; + } + if (track.itsNCls() < fElectronPFCut.min_ncluster_its) { + return false; + } + if (track.itsNClsInnerBarrel() < fElectronPFCut.min_ncluster_itsib) { + return false; + } + + if (track.tpcChi2NCl() < fElectronPFCut.minchi2tpc || fElectronPFCut.maxchi2tpc < track.tpcChi2NCl()) { + return false; + } + + if (track.tpcNClsFound() < fElectronPFCut.min_ncluster_tpc) { + return false; + } + + if (track.tpcNClsCrossedRows() < fElectronPFCut.mincrossedrows) { + return false; + } + + if (track.tpcCrossedRowsOverFindableCls() < fElectronPFCut.min_tpc_cr_findable_ratio) { + return false; + } + + if (track.tpcFractionSharedCls() > fElectronPFCut.max_frac_shared_clusters_tpc) { + return false; + } + + o2::dataformats::DCA mDcaInfoCov; + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : track.pidForTracking()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + return false; + } + float dcaXY = mDcaInfoCov.getY(); + float dcaZ = mDcaInfoCov.getZ(); + + if (std::fabs(dcaXY) > fElectronPFCut.dca_xy_max || std::fabs(dcaZ) > fElectronPFCut.dca_z_max) { + return false; + } + + if (trackParCov.getPt() < fElectronPFCut.minpt || std::fabs(trackParCov.getEta()) > fElectronPFCut.maxeta) { + return false; + } + + int total_cluster_size = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl++; + } + total_cluster_size += cluster_size_per_layer; + } + + if (fElectronPFCut.maxMeanITSClusterSize < static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))) { + return false; + } + + return true; + } + + template + bool isElectron(TCollision const& collision, TTrack const& track, TConfig const& eCut) + { + if (fElectronCut.usePIDML) { // keep fElectronCut here intentinoally + if (isElectron_TOFif(track, collision, eCut)) { + o2::analysis::pwgem::dilepton::mlpid::candidate candidate; + candidate.tpcInnerParam = track.tpcInnerParam(); + float binningFeature = mlResponsePID.getBinningFeature(candidate); + int pbin = lower_bound(eCut.binsMl.value.begin(), eCut.binsMl.value.end(), binningFeature) - eCut.binsMl.value.begin() - 1; + if (pbin < 0) { + pbin = 0; + } else if (static_cast(eCut.binsMl.value.size()) - 2 < pbin) { + pbin = static_cast(eCut.binsMl.value.size()) - 2; + } + return fMapProbaEl[std::make_pair(collision.globalIndex(), track.globalIndex())] > eCut.cutsMl.value[pbin]; + } else { + return false; + } + } else { + return isElectron_TPChadrej(track, collision, eCut) || isElectron_TOFreq(track, collision, eCut); + } + } + + template + bool isElectron_TOFif(TTrack const& track, TCollision const& collision, TConfig const& eCut) + { + float tofNSigmaEl = fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + bool is_EL_TPC = eCut.minTPCNsigmaEl.value < track.tpcNSigmaEl() && track.tpcNSigmaEl() < eCut.maxTPCNsigmaEl.value; + bool is_EL_TOF = track.hasTOF() ? (std::fabs(tofNSigmaEl) < eCut.maxTOFNsigmaEl.value) : true; // TOFif + return is_EL_TPC && is_EL_TOF; + } + + template + bool isElectron_TPChadrej(TTrack const& track, TCollision const& collision, TConfig const& eCut) + { + float tofNSigmaEl = fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + if (track.tpcNSigmaEl() < eCut.minTPCNsigmaEl.value || eCut.maxTPCNsigmaEl.value < track.tpcNSigmaEl()) { + return false; + } + if (eCut.minTPCNsigmaPi.value < track.tpcNSigmaPi() && track.tpcNSigmaPi() < eCut.maxTPCNsigmaPi.value && track.tpcInnerParam() < eCut.max_pin_for_pion_rejection.value) { + return false; + } + if (eCut.minTPCNsigmaKa.value < track.tpcNSigmaKa() && track.tpcNSigmaKa() < eCut.maxTPCNsigmaKa.value) { + return false; + } + if (eCut.minTPCNsigmaPr.value < track.tpcNSigmaPr() && track.tpcNSigmaPr() < eCut.maxTPCNsigmaPr.value) { + return false; + } + if (track.hasTOF() && (eCut.maxTOFNsigmaEl.value < std::fabs(tofNSigmaEl))) { + return false; + } + return true; + } + + template + bool isElectron_TOFreq(TTrack const& track, TCollision const& collision, TConfig const& eCut) + { + float tofNSigmaEl = fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + if (eCut.minTPCNsigmaPi.value < track.tpcNSigmaPi() && track.tpcNSigmaPi() < eCut.maxTPCNsigmaPi.value && (eCut.min_pin_for_pion_rejection.value < track.tpcInnerParam() && track.tpcInnerParam() < eCut.max_pin_for_pion_rejection.value)) { + return false; + } + return eCut.minTPCNsigmaEl.value < track.tpcNSigmaEl() && track.tpcNSigmaEl() < eCut.maxTPCNsigmaEl.value && std::fabs(tofNSigmaEl) < eCut.maxTOFNsigmaEl.value; + } + + template + bool isSelectedHadron(TCollision const&, TTrack const& track, TTrackParCov const& trackParCov, const float dcaXY, const float dcaZ) + { + if (!track.hasITS() || !track.hasTPC()) { + return false; + } + + if (trackParCov.getPt() < fHadronCut.cfg_min_pt_track || fHadronCut.cfg_max_pt_track < trackParCov.getPt()) { + return false; + } + + if (trackParCov.getEta() < fHadronCut.cfg_min_eta_track || fHadronCut.cfg_max_eta_track < trackParCov.getEta()) { + return false; + } + + if (std::fabs(dcaXY) > fHadronCut.cfg_max_dcaxy) { + return false; + } + + if (std::fabs(dcaZ) > fHadronCut.cfg_max_dcaz) { + return false; + } + + if (track.itsChi2NCl() < 0.f || fHadronCut.cfg_max_chi2its < track.itsChi2NCl()) { + return false; + } + + if (track.itsNCls() < fHadronCut.cfg_min_ncluster_its) { + return false; + } + + if (track.itsNClsInnerBarrel() < fHadronCut.cfg_min_ncluster_itsib) { + return false; + } + + if (track.tpcChi2NCl() < 0.f || fHadronCut.cfg_max_chi2tpc < track.tpcChi2NCl()) { + return false; + } + + if (track.tpcNClsFound() < fHadronCut.cfg_min_ncluster_tpc) { + return false; + } + + if (track.tpcNClsCrossedRows() < fHadronCut.cfg_min_ncrossedrows_tpc) { + return false; + } + + if (track.tpcCrossedRowsOverFindableCls() < fHadronCut.cfg_min_cr2findable_ratio_tpc) { + return false; + } + + if (track.tpcFractionSharedCls() > fHadronCut.cfg_max_frac_shared_clusters_tpc) { + return false; + } + + if (fHadronCut.requirePiKa && !isPiKa(track)) { + return false; + } + + return true; + } + + template + void fillElectronTable(TCollision const& collision, TTrack const& track, TTrackParCov const& trackParCov, const float dcaXY, const float dcaZ, TProducts& products, THistoregistry& registry) + { + float pt = trackParCov.getPt(); + float eta = trackParCov.getEta(); + float phi = RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U); + + float tofNSigmaEl = fMapTOFNsigmaElReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + // float tofNSigmaPi = fMapTOFNsigmaPiReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + // float tofNSigmaKa = fMapTOFNsigmaKaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + // float tofNSigmaPr = fMapTOFNsigmaPrReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + float beta = fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + float probaEl = fMapProbaEl[std::make_pair(collision.globalIndex(), track.globalIndex())]; + + bool isAssociatedToMPC = collision.globalIndex() == track.collisionId(); + float mcTunedTPCSignal = 0.f; + if constexpr (isMC) { + mcTunedTPCSignal = track.mcTunedTPCSignal(); + } + + products.electronTable(collision.globalIndex(), track.globalIndex(), track.sign(), + pt, eta, phi, + dcaXY, dcaZ, trackParCov.getSigmaY2(), trackParCov.getSigmaZY(), trackParCov.getSigmaZ2(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusPID(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), + track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), + beta, tofNSigmaEl, + track.itsClusterSizes(), + track.itsChi2NCl(), track.tofChi2(), track.detectorMap(), + isAssociatedToMPC, false, probaEl, track.flags(), mcTunedTPCSignal); + + products.electronCovTable( + trackParCov.getX(), + trackParCov.getAlpha(), + trackParCov.getY(), + trackParCov.getZ(), + trackParCov.getSnp(), + // trackParCov.getTgl(), + // trackParCov.getSigmaY2(), + // trackParCov.getSigmaZY(), + // trackParCov.getSigmaZ2(), + trackParCov.getSigmaSnpY(), + trackParCov.getSigmaSnpZ(), + trackParCov.getSigmaSnp2(), + trackParCov.getSigmaTglY(), + trackParCov.getSigmaTglZ(), + trackParCov.getSigmaTglSnp(), + trackParCov.getSigmaTgl2(), + trackParCov.getSigma1PtY(), + trackParCov.getSigma1PtZ(), + trackParCov.getSigma1PtSnp(), + trackParCov.getSigma1PtTgl(), + trackParCov.getSigma1Pt2()); + + int total_cluster_size = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl++; + } + total_cluster_size += cluster_size_per_layer; + } + + int total_cluster_size_ib = 0, nl_ib = 0; + for (unsigned int layer = 0; layer < 3; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl_ib++; + } + total_cluster_size_ib += cluster_size_per_layer; + } + + int total_cluster_size_ob = 0, nl_ob = 0; + for (unsigned int layer = 3; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl_ob++; + } + total_cluster_size_ob += cluster_size_per_layer; + } + + registry.fill(HIST("Track/hPt"), pt); + registry.fill(HIST("Track/hQoverPt"), track.sign() / pt); + registry.fill(HIST("Track/hEtaPhi"), phi, eta); + registry.fill(HIST("Track/hDCAxyz"), dcaXY, dcaZ); + registry.fill(HIST("Track/hDCAxyzSigma"), dcaXY / std::sqrt(trackParCov.getSigmaY2()), dcaZ / std::sqrt(trackParCov.getSigmaZ2())); + registry.fill(HIST("Track/hDCA3dSigma"), dca3DinSigmaOTF(dcaXY, dcaZ, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY())); + registry.fill(HIST("Track/hDCAxyRes_Pt"), pt, std::sqrt(trackParCov.getSigmaY2()) * 1e+4); // convert cm to um + registry.fill(HIST("Track/hDCAzRes_Pt"), pt, std::sqrt(trackParCov.getSigmaZ2()) * 1e+4); // convert cm to um + registry.fill(HIST("Track/hNclsITS"), track.itsNCls()); + registry.fill(HIST("Track/hNclsTPC"), track.tpcNClsFound()); + registry.fill(HIST("Track/hNcrTPC"), track.tpcNClsCrossedRows()); + registry.fill(HIST("Track/hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); + registry.fill(HIST("Track/hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); + registry.fill(HIST("Track/hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); + registry.fill(HIST("Track/hChi2TPC"), track.tpcChi2NCl()); + registry.fill(HIST("Track/hChi2ITS"), track.itsChi2NCl()); + registry.fill(HIST("Track/hChi2TOF"), track.tofChi2()); + registry.fill(HIST("Track/hITSClusterMap"), track.itsClusterMap()); + registry.fill(HIST("Track/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); + registry.fill(HIST("Track/hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); + registry.fill(HIST("Track/hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); + registry.fill(HIST("Track/hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); + registry.fill(HIST("Track/hTPCNsigmaKa"), track.tpcInnerParam(), track.tpcNSigmaKa()); + registry.fill(HIST("Track/hTPCNsigmaPr"), track.tpcInnerParam(), track.tpcNSigmaPr()); + registry.fill(HIST("Track/hTOFbeta"), trackParCov.getP(), beta); + registry.fill(HIST("Track/hTOFNsigmaEl"), track.tpcInnerParam(), tofNSigmaEl); + // registry.fill(HIST("Track/hTOFNsigmaPi"), track.tpcInnerParam(), tofNSigmaPi); + // registry.fill(HIST("Track/hTOFNsigmaKa"), track.tpcInnerParam(), tofNSigmaKa); + // registry.fill(HIST("Track/hTOFNsigmaPr"), track.tpcInnerParam(), tofNSigmaPr); + registry.fill(HIST("Track/hMeanClusterSizeITS"), trackParCov.getP(), static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))); + registry.fill(HIST("Track/hMeanClusterSizeITSib"), trackParCov.getP(), static_cast(total_cluster_size_ib) / static_cast(nl_ib) * std::cos(std::atan(trackParCov.getTgl()))); + registry.fill(HIST("Track/hMeanClusterSizeITSob"), trackParCov.getP(), static_cast(total_cluster_size_ob) / static_cast(nl_ob) * std::cos(std::atan(trackParCov.getTgl()))); + registry.fill(HIST("Track/hProbElBDT"), track.tpcInnerParam(), probaEl); + } + + template + bool isPiKa(TTrack const& track) + { + bool is_pi_included_TPC = fHadronCut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < fHadronCut.cfg_max_TPCNsigmaPi; + bool is_ka_included_TPC = fHadronCut.cfg_min_TPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < fHadronCut.cfg_max_TPCNsigmaKa; + // bool is_pr_included_TPC = fHadronCut.cfg_min_TPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < fHadronCut.cfg_max_TPCNsigmaPr; + return is_pi_included_TPC || is_ka_included_TPC; + } + + // template + // bool isSelectedV0Leg(TTrack const& track) + // { + // if constexpr (isMC) { + // if (!track.has_mcParticle()) { + // return false; + // } + // } + + // if (!track.hasITS() || !track.hasTPC()) { + // return false; + // } + + // if (track.itsChi2NCl() > fV0Cut.cfg_max_chi2its) { + // return false; + // } + + // if (track.itsNCls() < fV0Cut.cfg_min_ncluster_its) { + // return false; + // } + + // if (track.itsNClsInnerBarrel() < fV0Cut.cfg_min_ncluster_itsib) { + // return false; + // } + + // if (track.tpcChi2NCl() > fV0Cut.cfg_max_chi2tpc) { + // return false; + // } + + // if (track.tpcNClsFound() < fV0Cut.cfg_min_ncluster_tpc) { + // return false; + // } + + // if (track.tpcNClsCrossedRows() < fV0Cut.cfg_min_ncrossedrows_tpc) { + // return false; + // } + + // if (track.tpcCrossedRowsOverFindableCls() < fV0Cut.cfg_min_cr2findable_ratio_tpc) { + // return false; + // } + + // if (track.tpcFractionSharedCls() > fV0Cut.cfg_max_frac_shared_clusters_tpc) { + // return false; + // } + + // return true; + // } + + // template + // bool isPion(TTrack const& track) + // { + // return fV0Cut.cfg_min_TPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < fV0Cut.cfg_max_TPCNsigmaPi; + // } + + // template + // bool isKaon(TTrack const& track) + // { + // return fV0Cut.cfg_min_TPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < fV0Cut.cfg_max_TPCNsigmaKa; + // } + + // template + // bool isProton(TTrack const& track) + // { + // return fV0Cut.cfg_min_TPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < fV0Cut.cfg_max_TPCNsigmaPr; + // } + + // template + // bool isK0S(TV0 const& v0) + // { + // return (fV0Cut.cfg_min_mass_k0s < v0.mK0Short() && v0.mK0Short() < fV0Cut.cfg_max_mass_k0s) && (v0.mLambda() < fV0Cut.cfg_min_mass_lambda_veto || fV0Cut.cfg_max_mass_lambda_veto < v0.mLambda()) && (v0.mAntiLambda() < fV0Cut.cfg_min_mass_lambda_veto || fV0Cut.cfg_max_mass_lambda_veto < v0.mAntiLambda()); + // } + + // template + // bool isLambda(TV0 const& v0) + // { + // return (fV0Cut.cfg_min_mass_lambda < v0.mLambda() && v0.mLambda() < fV0Cut.cfg_max_mass_lambda) && (v0.mK0Short() < fV0Cut.cfg_min_mass_k0s_veto || fV0Cut.cfg_max_mass_k0s_veto < v0.mK0Short()); + // } + + // template + // bool isAntiLambda(TV0 const& v0) + // { + // return (fV0Cut.cfg_min_mass_lambda < v0.mAntiLambda() && v0.mAntiLambda() < fV0Cut.cfg_max_mass_lambda) && (v0.mK0Short() < fV0Cut.cfg_min_mass_k0s_veto || fV0Cut.cfg_max_mass_k0s_veto < v0.mK0Short()); + // } + + // template + // bool isXi(TCascade const& cascade) + // { + // return (fCascadeCut.cfg_min_mass_Xi < cascade.mXi() && cascade.mXi() < fCascadeCut.cfg_max_mass_Xi) && (cascade.mOmega() < fCascadeCut.cfg_min_mass_Omega_veto || fCascadeCut.cfg_max_mass_Omega_veto < cascade.mOmega()); + // } + + // template + // bool isOmega(TCascade const& cascade) + // { + // return (fCascadeCut.cfg_min_mass_Omega < cascade.mOmega() && cascade.mOmega() < fCascadeCut.cfg_max_mass_Omega) && (cascade.mXi() < fCascadeCut.cfg_min_mass_Xi_veto || fCascadeCut.cfg_max_mass_Xi_veto < cascade.mXi()); + // } + + // template + // void fillV0Histograms(TCollision const&, TV0 const& v0, THistoregistry& registry) + // { + // auto pos = v0.template posTrack_as(); + // auto neg = v0.template negTrack_as(); + // registry.fill(HIST("SCT/V0/hPt"), v0.pt()); + // registry.fill(HIST("SCT/V0/hAP"), v0.alpha(), v0.qtarm()); + // registry.fill(HIST("SCT/V0/hCosPA"), v0.v0cosPA()); + // registry.fill(HIST("SCT/V0/hLxy"), v0.v0radius()); + // registry.fill(HIST("SCT/V0/hDCA2Legs"), v0.dcaV0daughters()); + + // if (isPion(pos) && isPion(neg)) { + // if ((v0.mLambda() < fV0Cut.cfg_min_mass_lambda_veto || fV0Cut.cfg_max_mass_lambda_veto < v0.mLambda()) && (v0.mAntiLambda() < fV0Cut.cfg_min_mass_lambda_veto || fV0Cut.cfg_max_mass_lambda_veto < v0.mAntiLambda())) { + // registry.fill(HIST("SCT/V0/hMassK0S"), v0.mK0Short()); + // registry.fill(HIST("SCT/V0/hYPhi_K0S"), v0.phi(), v0.yK0Short()); + // } + // registry.fill(HIST("SCT/V0/hMassGamma_misid"), v0.mGamma()); + // registry.fill(HIST("SCT/V0/hMassLambda_misid"), v0.mLambda()); + // registry.fill(HIST("SCT/V0/hMassAntiLambda_misid"), v0.mAntiLambda()); + // } + + // if (isProton(pos) && isPion(neg)) { + // if (v0.mK0Short() < fV0Cut.cfg_min_mass_k0s_veto || fV0Cut.cfg_max_mass_k0s_veto < v0.mK0Short()) { + // registry.fill(HIST("SCT/V0/hMassLambda"), v0.mLambda()); + // registry.fill(HIST("SCT/V0/hYPhi_Lambda"), v0.phi(), v0.yLambda()); + // } + // registry.fill(HIST("SCT/V0/hMassGamma_misid"), v0.mGamma()); + // registry.fill(HIST("SCT/V0/hMassK0S_misid"), v0.mK0Short()); + // } + + // if (isProton(neg) && isPion(pos)) { + // if (v0.mK0Short() < fV0Cut.cfg_min_mass_k0s_veto || fV0Cut.cfg_max_mass_k0s_veto < v0.mK0Short()) { + // registry.fill(HIST("SCT/V0/hMassAntiLambda"), v0.mAntiLambda()); + // registry.fill(HIST("SCT/V0/hYPhi_Lambda"), v0.phi(), v0.yLambda()); + // } + // registry.fill(HIST("SCT/V0/hMassGamma_misid"), v0.mGamma()); + // registry.fill(HIST("SCT/V0/hMassK0S_misid"), v0.mK0Short()); + // } + // } + + // template + // void fillCascadeHistograms(TCollision const& collision, TCascade const& cascade, THistoregistry& registry) + // { + // auto pos = cascade.template posTrack_as(); + // auto neg = cascade.template negTrack_as(); + // auto bachelor = cascade.template bachelor_as(); + + // registry.fill(HIST("SCT/Cascade/hPt"), cascade.pt()); + // registry.fill(HIST("SCT/Cascade/hMassLambda"), cascade.mLambda()); + // registry.fill(HIST("SCT/Cascade/hCosPA"), cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ())); + // registry.fill(HIST("SCT/Cascade/hDCA2Legs"), cascade.dcacascdaughters()); + // registry.fill(HIST("SCT/Cascade/hV0CosPA"), cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ())); + // registry.fill(HIST("SCT/Cascade/hV0DCA2Legs"), cascade.dcaV0daughters()); + + // if (cascade.sign() < 0) { // Xi- or Omega- + // if (isPion(bachelor) && isProton(pos) && isPion(neg)) { + // if (cascade.mOmega() < fCascadeCut.cfg_min_mass_Omega_veto || fCascadeCut.cfg_max_mass_Omega_veto < cascade.mOmega()) { + // registry.fill(HIST("SCT/Cascade/hMassXi"), cascade.mXi()); + // registry.fill(HIST("SCT/Cascade/hYPhi_Xi"), cascade.phi(), cascade.yXi()); + // } + // registry.fill(HIST("SCT/Cascade/hMassOmega_misid"), cascade.mOmega()); + // } + // if (isKaon(bachelor) && isProton(pos) && isPion(neg)) { + // if (cascade.mXi() < fCascadeCut.cfg_min_mass_Xi_veto || fCascadeCut.cfg_max_mass_Xi_veto < cascade.mXi()) { + // registry.fill(HIST("SCT/Cascade/hMassOmega"), cascade.mOmega()); + // registry.fill(HIST("SCT/Cascade/hYPhi_Omega"), cascade.phi(), cascade.yOmega()); + // } + // registry.fill(HIST("SCT/Cascade/hMassXi_misid"), cascade.mXi()); + // } + // } else { // Xi+ or Omega+ + // if (isPion(bachelor) && isProton(neg) && isPion(pos)) { + // if (cascade.mOmega() < fCascadeCut.cfg_min_mass_Omega_veto || fCascadeCut.cfg_max_mass_Omega_veto < cascade.mOmega()) { + // registry.fill(HIST("SCT/Cascade/hMassXi"), cascade.mXi()); + // registry.fill(HIST("SCT/Cascade/hYPhi_Xi"), cascade.phi(), cascade.yXi()); + // } + // registry.fill(HIST("SCT/Cascade/hMassOmega_misid"), cascade.mOmega()); + // } + // if (isKaon(bachelor) && isProton(neg) && isPion(pos)) { + // if (cascade.mXi() < fCascadeCut.cfg_min_mass_Xi_veto || fCascadeCut.cfg_max_mass_Xi_veto < cascade.mXi()) { + // registry.fill(HIST("SCT/Cascade/hMassOmega"), cascade.mOmega()); + // registry.fill(HIST("SCT/Cascade/hYPhi_Omega"), cascade.phi(), cascade.yOmega()); + // } + // registry.fill(HIST("SCT/Cascade/hMassXi_misid"), cascade.mXi()); + // } + // } + // } + + template + void processWithTTCA(TBCs const& bcs, TCollisions const& collisions, TTracks const& tracks, /*TV0s const& v0s, TCascades const& cascades,*/ TTrackAssoc const& trackIndices, TMCParticles const&, TProducts& products, THistoregistry& registry, TSliceCache& cache, TPresliceTrack const& perColTrack, TPresliceTrackAssoc const& trackIndicesPerCollision /*, TPresliceV0 const& perColV0, TPresliceCascade const& perColCasc*/) + { + if (bcs.size() == 0) { + return; + } + initCCDB(bcs.begin()); + + calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices, cache, perColTrack, trackIndicesPerCollision); + + for (const auto& collision : collisions) { + auto bc = collision.template bc_as(); + initCCDB(bc); + + if constexpr (isMC) { + if (!collision.has_mcCollision()) { + continue; + } + } + + if (!collision.isSelected()) { + continue; + } + + if constexpr (isTriggerAnalysis) { + if (collision.triggerMask_raw() == 0) { + continue; + } + } + + mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); + mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + std::unordered_multimap multiMapTracksPerCollision; // collisionId -> trackIds + + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + + std::vector looseElectronIds; // for pion prefilter + if (fElectronPFCut.doPF) { + looseElectronIds.reserve(trackIdsThisCollision.size()); + } + + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!checkTrack(collision, track) && !checkTrackPF(collision, track)) { + continue; + } + + fillMapMLPID(collision, track); // call this function for all track also for loose electron sample. + + if (checkTrack(collision, track) && isElectron(collision, track, fElectronCut)) { + multiMapTracksPerCollision.insert(std::make_pair(collision.globalIndex(), track.globalIndex())); + } + + if (fElectronPFCut.doPF && checkTrackPF(collision, track) && isElectron(collision, track, fElectronPFCut)) { + looseElectronIds.emplace_back(track.globalIndex()); + } + } // end of track loop + + int count_electrons = multiMapTracksPerCollision.count(collision.globalIndex()); + registry.fill(HIST("Track/hNe"), count_electrons); + if (count_electrons < fElectronCut.minNelectron) { + continue; + } + + std::vector hadronIds; + // std::vector k0sIds; + // std::vector lambdaIds; + // std::vector antilambdaIds; + // std::vector xiMinusIds; + // std::vector xiPlusIds; + // std::vector omegaMinusIds; + // std::vector omegaPlusIds; + + if (fDoSCTwithTracks) { + hadronIds.reserve(trackIdsThisCollision.size()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + auto trackParCov = getTrackParCov(track); + o2::dataformats::DCA mDcaInfoCov; + mDcaInfoCov.set(999, 999, 999, 999, 999); + trackParCov.setPID(track.pidForTracking()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + continue; + } + float dcaXY = mDcaInfoCov.getY(); + float dcaZ = mDcaInfoCov.getZ(); + if (isSelectedHadron(collision, track, trackParCov, dcaXY, dcaZ)) { + float tpcSignal = track.tpcSignal(); + if constexpr (isMC) { + tpcSignal = track.mcTunedTPCSignal(); + } + registry.fill(HIST("SCT/Track/hs"), trackParCov.getPt(), trackParCov.getEta(), RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U)); + registry.fill(HIST("SCT/Track/hDCA"), dcaXY, dcaZ); + registry.fill(HIST("SCT/Track/hTPCdEdx"), track.tpcInnerParam(), tpcSignal); + registry.fill(HIST("SCT/Track/hTOFbeta"), trackParCov.getP(), fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + hadronIds.emplace_back(track.globalIndex()); + } + } // end of track loop + } + + // if (fDoSCTwithV0s) { + // auto v0s_per_collision = v0s.sliceBy(perColV0, collision.globalIndex()); + // k0sIds.reserve(v0s_per_collision.size()); + // lambdaIds.reserve(v0s_per_collision.size()); + // antilambdaIds.reserve(v0s_per_collision.size()); + + // for (const auto& v0 : v0s_per_collision) { + // auto pos = v0.template posTrack_as(); + // auto neg = v0.template negTrack_as(); + + // if (pos.sign() * neg.sign() > 0) { + // continue; + // } + // if (!isSelectedV0Leg(pos) || !isSelectedV0Leg(neg)) { + // continue; + // } + + // if (v0.dcaV0daughters() > fV0Cut.cfg_max_dca2legs) { + // continue; + // } + + // if (v0.v0radius() < fV0Cut.cfg_min_radius) { + // continue; + // } + + // if (v0.v0cosPA() < fV0Cut.cfg_min_cospa) { + // continue; + // } + + // if (std::sqrt(std::pow(v0.alpha() / fV0Cut.cfg_max_alpha_veto, 2) + std::pow(v0.qtarm() / fV0Cut.cfg_max_qt_veto, 2)) < 1.f) { // photon conversion rejection at small qT + // continue; + // } + + // fillV0Histograms(collision, v0, registry); + + // if (isK0S(v0) && isPion(pos) && isPion(neg)) { + // k0sIds.emplace_back(v0.globalIndex()); + // } + + // if (isLambda(v0) && isProton(pos) && isPion(neg)) { + // lambdaIds.emplace_back(v0.globalIndex()); + // } else if (isAntiLambda(v0) && isProton(neg) && isPion(pos)) { + // antilambdaIds.emplace_back(v0.globalIndex()); + // } + + // } // end of v0 loop + // } + + // if (fDoSCTwithCascades) { + // auto cascades_per_collision = cascades.sliceBy(perColCasc, collision.globalIndex()); + // xiMinusIds.reserve(cascades_per_collision.size()); + // xiPlusIds.reserve(cascades_per_collision.size()); + // omegaMinusIds.reserve(cascades_per_collision.size()); + // omegaPlusIds.reserve(cascades_per_collision.size()); + + // for (const auto& cascade : cascades_per_collision) { + // auto pos = cascade.template posTrack_as(); + // auto neg = cascade.template negTrack_as(); + // auto bachelor = cascade.template bachelor_as(); + // if (pos.sign() * neg.sign() > 0) { + // continue; + // } + // if (cascade.mLambda() < fCascadeCut.cfg_min_mass_lambda || fCascadeCut.cfg_max_mass_lambda < cascade.mLambda()) { + // continue; + // } + + // if (!isSelectedV0Leg(pos) || !isSelectedV0Leg(neg) || !isSelectedV0Leg(bachelor)) { + // continue; + // } + + // if (cascade.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < fCascadeCut.cfg_min_cospa) { + // continue; + // } + // if (cascade.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) < fCascadeCut.cfg_min_cospa_v0) { + // continue; + // } + + // if (cascade.cascradius() > cascade.v0radius()) { + // continue; + // } + + // if (cascade.dcaV0daughters() > fCascadeCut.cfg_max_dcadau_v0) { + // continue; + // } + // if (cascade.v0radius() < fCascadeCut.cfg_min_rxy_v0) { + // continue; + // } + // if (cascade.cascradius() < fCascadeCut.cfg_min_rxy) { + // continue; + // } + + // if (cascade.dcacascdaughters() > fCascadeCut.cfg_max_dcadau) { + // continue; + // } + + // if (std::fabs(cascade.dcav0topv(collision.posX(), collision.posY(), collision.posZ())) < fCascadeCut.cfg_min_dcaxy_v0) { + // continue; + // } + + // fillCascadeHistograms(collision, cascade, registry); + + // if (cascade.sign() < 0) { // Xi- or Omega- + // if (isXi(cascade) && isPion(bachelor) && isProton(pos) && isPion(neg)) { + // xiMinusIds.emplace_back(cascade.globalIndex()); + // } + // if (isOmega(cascade) && isKaon(bachelor) && isProton(pos) && isPion(neg)) { + // omegaMinusIds.emplace_back(cascade.globalIndex()); + // } + // } else { // Xi+ or Omega+ + // if (isXi(cascade) && isPion(bachelor) && isProton(neg) && isPion(pos)) { + // xiPlusIds.emplace_back(cascade.globalIndex()); + // } + // if (isOmega(cascade) && isKaon(bachelor) && isProton(neg) && isPion(pos)) { + // omegaPlusIds.emplace_back(cascade.globalIndex()); + // } + // } + // } // end of cascade loop + // } + + auto range_electrons = multiMapTracksPerCollision.equal_range(collision.globalIndex()); + for (auto it = range_electrons.first; it != range_electrons.second; it++) { + auto electron = tracks.rawIteratorAt(it->second); + o2::dataformats::DCA mDcaInfoCov; + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(electron); + trackParCov.setPID(fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + continue; + } + float dcaXY = mDcaInfoCov.getY(); + float dcaZ = mDcaInfoCov.getZ(); + fillElectronTable(collision, electron, trackParCov, dcaXY, dcaZ, products, registry); + ROOT::Math::PtEtaPhiMVector v1(trackParCov.getPt(), trackParCov.getEta(), RecoDecay::constrainAngle(trackParCov.getPhi(), 0, 1U), o2::constants::physics::MassElectron); // main electron + + // apply pion prefilter + uint8_t pfb = 0; + for (const auto& looseElectronId : looseElectronIds) { + auto looseElectron = tracks.rawIteratorAt(looseElectronId); + if (looseElectron.globalIndex() == electron.globalIndex()) { + continue; + } + if (looseElectron.sign() * electron.sign() > 0) { // reject LS, because pi0dalitz and photon conversion must be ULS. + continue; + } + + auto trackParCov2 = getTrackParCov(looseElectron); + trackParCov2.setPID(fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : looseElectron.pidForTracking()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov2, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + continue; + } + + float tpcSignal = looseElectron.tpcSignal(); + if constexpr (isMC) { + tpcSignal = looseElectron.mcTunedTPCSignal(); + } + registry.fill(HIST("Prefilter/Track/hPt"), trackParCov2.getPt()); + registry.fill(HIST("Prefilter/Track/hEtaPhi"), RecoDecay::constrainAngle(trackParCov2.getPhi(), 0, 1U), trackParCov2.getEta()); + registry.fill(HIST("Prefilter/Track/hTPCdEdx"), looseElectron.tpcInnerParam(), tpcSignal); + registry.fill(HIST("Prefilter/Track/hTOFbeta"), trackParCov2.getP(), fMapTOFBetaReassociated[std::make_pair(collision.globalIndex(), looseElectron.globalIndex())]); + + ROOT::Math::PtEtaPhiMVector v2(trackParCov2.getPt(), trackParCov2.getEta(), RecoDecay::constrainAngle(trackParCov2.getPhi(), 0, 1U), o2::constants::physics::MassElectron); // loose electron + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(v1.Px(), v1.Py(), v1.Pz(), v2.Px(), v2.Py(), v2.Pz(), electron.sign(), looseElectron.sign(), d_bz); + registry.fill(HIST("Prefilter/Pair/hMvsPhiV"), phiv, v12.M()); + + if (v12.M() < fElectronPFCut.slope * phiv + fElectronPFCut.intercept) { + pfb |= (uint8_t(1) << static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPC)); + } + + for (int i = 0; i < static_cast(max_mee_vec.size()); i++) { + if (v12.M() < max_mee_vec.at(i)) { + pfb |= (uint8_t(1) << (static_cast(o2::aod::pwgem::dilepton::utils::pairutil::DileptonPrefilterBit::kElFromPi0_20MeV) + i)); + } + } // end of mee loop + + } // end of loose electron loop + products.electronPFTable(pfb); + + // perform SCT from here + std::vector bdtScoreBkg; + std::vector bdtScorePromptHc; + std::vector bdtScoreNonpromptHc; + std::vector bdtScoreHb; + std::vector hadronType; + + bdtScoreBkg.reserve(hadronIds.size() /*+ k0sIds.size() + lambdaIds.size() + antilambdaIds.size()*/); + bdtScorePromptHc.reserve(hadronIds.size() /*+ k0sIds.size() + lambdaIds.size() + antilambdaIds.size()*/); + bdtScoreNonpromptHc.reserve(hadronIds.size() /*+ k0sIds.size() + lambdaIds.size() + antilambdaIds.size()*/); + bdtScoreHb.reserve(hadronIds.size() /*+ k0sIds.size() + lambdaIds.size() + antilambdaIds.size()*/); + hadronType.reserve(hadronIds.size() /*+ k0sIds.size() + lambdaIds.size() + antilambdaIds.size()*/); + + // eTrack pair + for (const auto& hadronId : hadronIds) { + auto hadron = tracks.rawIteratorAt(hadronId); + if (hadron.globalIndex() == electron.globalIndex()) { + continue; + } + + auto hadronParCov = getTrackParCov(hadron); + o2::dataformats::DCA mDcaInfoCov; + mDcaInfoCov.set(999, 999, 999, 999, 999); + hadronParCov.setPID(hadron.pidForTracking()); + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, hadronParCov, 2.f, matCorr, &mDcaInfoCov); + + auto eTpair = o2::aod::pwgem::dilepton::utils::makePairLeptonTrack(dfeT, collision, electron, hadron, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), hadron.pidForTracking(), o2::constants::physics::MassElectron); + registry.fill(HIST("SCT/eT/hDecayLength"), eTpair.lxy, eTpair.lz); + registry.fill(HIST("SCT/eT/hCosPA"), eTpair.cospa); + registry.fill(HIST("SCT/eT/hDCA2legs"), eTpair.dca2legs); + registry.fill(HIST("SCT/eT/hMass"), eTpair.mass); + if (eTpair.isOK && fConfigDFeT.useML && eTpair.chi2PCA < fConfigDFeT.maxChi2PCA && eTpair.mass < fConfigDFeT.maxMassLH) { + o2::analysis::pwgem::dilepton::sct::candidate candidate; + fillCandidate(candidate, eTpair, hadronParCov, mDcaInfoCov); + candidate.ptL = trackParCov.getPt(); + candidate.signLH = electron.sign() * hadron.sign(); + candidate.signedMassLH = electron.sign() * hadron.sign() * eTpair.mass; + candidate.tpcNSigmaKa = hadron.tpcNSigmaKa(); + + auto inputFeatures = mlResponseSCTeT.getInputFeatures(candidate); + float binningFeature = mlResponseSCTeT.getBinningFeature(candidate); + + int pbin = lower_bound(fConfigDFeT.binsMl.value.begin(), fConfigDFeT.binsMl.value.end(), binningFeature) - fConfigDFeT.binsMl.value.begin() - 1; + if (pbin < 0) { + pbin = 0; + } else if (static_cast(fConfigDFeT.binsMl.value.size()) - 2 < pbin) { + pbin = static_cast(fConfigDFeT.binsMl.value.size()) - 2; + } + + auto vecProb = mlResponseSCTeT.getModelOutput(inputFeatures, pbin); + bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + bdtScoreHb.emplace_back(static_cast(vecProb[3] * 255.f)); + hadronType.emplace_back(0); + } + } // end of charged track loop + + // // eK0S pair + // for (const auto& k0sId : k0sIds) { + // auto v0 = v0s.rawIteratorAt(k0sId); + // if (v0.posTrackId() == electron.globalIndex() || v0.negTrackId() == electron.globalIndex()) { + // continue; + // } + // const std::array vertexV0 = {v0.x(), v0.y(), v0.z()}; + // const std::array momV0 = {v0.px(), v0.py(), v0.pz()}; + // std::array covV0 = {0.}; + // constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + // for (int i = 0; i < 6; i++) { + // covV0[MomInd[i]] = v0.momentumCovMat()[i]; + // covV0[i] = v0.positionCovMat()[i]; + // } + // auto v0ParCov = o2::track::TrackParCov(vertexV0, momV0, covV0, 0, true); + // v0ParCov.setAbsCharge(0); + // v0ParCov.setPID(o2::track::PID::K0); + // o2::dataformats::DCA impactParameterV0; + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, v0ParCov, 2.f, matCorr, &impactParameterV0); // v0ParCov is TrackParCov object + + // auto eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(dfeV0, collision, electron, v0, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), o2::track::PID::K0, o2::constants::physics::MassElectron); + // registry.fill(HIST("SCT/eV0/hDecayLength"), eV0pair.lxy, eV0pair.lz); + // registry.fill(HIST("SCT/eV0/hCosPA"), eV0pair.cospa); + // registry.fill(HIST("SCT/eV0/hDCA2legs"), eV0pair.dca2legs); + // registry.fill(HIST("SCT/eV0/hMass"), eV0pair.mass); + // if (eV0pair.isOK && fConfigDFeV0.useML && eV0pair.chi2PCA < fConfigDFeV0.maxChi2PCA && eV0pair.mass < fConfigDFeV0.maxMassLH) { + // o2::analysis::pwgem::dilepton::sct::candidate candidate; + // fillCandidate(candidate, eV0pair, v0ParCov, impactParameterV0); + // candidate.ptL = trackParCov.getPt(); + // candidate.massH = o2::constants::physics::MassK0Short; + + // auto inputFeatures = mlResponseSCTeV0.getInputFeatures(candidate); + // float binningFeature = mlResponseSCTeV0.getBinningFeature(candidate); + + // int pbin = lower_bound(fConfigDFeV0.binsMl.value.begin(), fConfigDFeV0.binsMl.value.end(), binningFeature) - fConfigDFeV0.binsMl.value.begin() - 1; + // if (pbin < 0) { + // pbin = 0; + // } else if (static_cast(fConfigDFeV0.binsMl.value.size()) - 2 < pbin) { + // pbin = static_cast(fConfigDFeV0.binsMl.value.size()) - 2; + // } + + // auto vecProb = mlResponseSCTeV0.getModelOutput(inputFeatures, pbin); + // bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + // bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + // bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + // bdtScoreHb.emplace_back(static_cast(vecProb[3] * 255.f)); + // hadronType.emplace_back(1); + // } + // } // end of k0s loop + + if (electron.sign() > 0) { // positron + // eL pair // sign is restricted in baryon decay: Lc+ -> e+ nu_e Lambda + // for (const auto& lambdaId : lambdaIds) { + // auto v0 = v0s.rawIteratorAt(lambdaId); + // if (v0.posTrackId() == electron.globalIndex() || v0.negTrackId() == electron.globalIndex()) { + // continue; + // } + // const std::array vertexV0 = {v0.x(), v0.y(), v0.z()}; + // const std::array momV0 = {v0.px(), v0.py(), v0.pz()}; + // std::array covV0 = {0.}; + // constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + // for (int i = 0; i < 6; i++) { + // covV0[MomInd[i]] = v0.momentumCovMat()[i]; + // covV0[i] = v0.positionCovMat()[i]; + // } + // auto v0ParCov = o2::track::TrackParCov(vertexV0, momV0, covV0, 0, true); + // v0ParCov.setAbsCharge(0); + // v0ParCov.setPID(o2::track::PID::Lambda); + // o2::dataformats::DCA impactParameterV0; + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, v0ParCov, 2.f, matCorr, &impactParameterV0); // v0ParCov is TrackParCov object + + // auto eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(dfeV0, collision, electron, v0, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), o2::track::PID::Lambda, o2::constants::physics::MassElectron); + // registry.fill(HIST("SCT/eV0/hDecayLength"), eV0pair.lxy, eV0pair.lz); + // registry.fill(HIST("SCT/eV0/hCosPA"), eV0pair.cospa); + // registry.fill(HIST("SCT/eV0/hDCA2legs"), eV0pair.dca2legs); + // registry.fill(HIST("SCT/eV0/hMass"), eV0pair.mass); + // if (eV0pair.isOK && fConfigDFeV0.useML && eV0pair.chi2PCA < fConfigDFeV0.maxChi2PCA && eV0pair.mass < fConfigDFeV0.maxMassLH) { + // o2::analysis::pwgem::dilepton::sct::candidate candidate; + // fillCandidate(candidate, eV0pair, v0ParCov, impactParameterV0); + // candidate.ptL = trackParCov.getPt(); + // candidate.massH = o2::constants::physics::MassLambda; + + // auto inputFeatures = mlResponseSCTeV0.getInputFeatures(candidate); + // float binningFeature = mlResponseSCTeV0.getBinningFeature(candidate); + + // int pbin = lower_bound(fConfigDFeV0.binsMl.value.begin(), fConfigDFeV0.binsMl.value.end(), binningFeature) - fConfigDFeV0.binsMl.value.begin() - 1; + // if (pbin < 0) { + // pbin = 0; + // } else if (static_cast(fConfigDFeV0.binsMl.value.size()) - 2 < pbin) { + // pbin = static_cast(fConfigDFeV0.binsMl.value.size()) - 2; + // } + + // auto vecProb = mlResponseSCTeV0.getModelOutput(inputFeatures, pbin); + // bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + // bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + // bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + // bdtScoreHb.emplace_back(static_cast(vecProb[3] * 255.f)); + // hadronType.emplace_back(2); + // } + // } // end of lambda loop + + // eXi pair // sign is restricted in baryon decay: Xic0 -> e+ nu_e Xi- + // for (const auto& xiId : xiMinusIds) { + // auto cascade = cascades.rawIteratorAt(xiId); + // if (cascade.posTrackId() == electron.globalIndex() || cascade.negTrackId() == electron.globalIndex() || cascade.bachelorId() == electron.globalIndex()) { + // continue; + // } + + // const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + // const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + // std::array covCasc = {0.}; + // constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + // for (int i = 0; i < 6; i++) { + // covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + // covCasc[i] = cascade.positionCovMat()[i]; + // } + // auto cascadeParCov = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + // cascadeParCov.setAbsCharge(1); + // cascadeParCov.setPID(o2::track::PID::XiMinus); + // o2::dataformats::DCA impactParameterCasc; + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, cascadeParCov, 2.f, matCorr, &impactParameterCasc); // cascadeParCov is TrackParCov object + + // auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(dfeC, collision, electron, cascade, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), o2::track::PID::XiMinus, o2::constants::physics::MassElectron); + // registry.fill(HIST("SCT/eC/hDecayLength"), eCpair.lxy, eCpair.lz); + // registry.fill(HIST("SCT/eC/hCosPA"), eCpair.cospa); + // registry.fill(HIST("SCT/eC/hDCA2legs"), eCpair.dca2legs); + // registry.fill(HIST("SCT/eC/hMass"), eCpair.mass); + // if (eCpair.isOK && fConfigDFeC.useML && eCpair.chi2PCA < fConfigDFeC.maxChi2PCA && eCpair.mass < fConfigDFeC.maxMassLH) { + // o2::analysis::pwgem::dilepton::sct::candidate candidate; + // fillCandidate(candidate, eCpair, cascadeParCov, impactParameterCasc); + // candidate.ptL = trackParCov.getPt(); + // candidate.massH = o2::constants::physics::MassXiMinus; + + // auto inputFeatures = mlResponseSCTeC.getInputFeatures(candidate); + // float binningFeature = mlResponseSCTeC.getBinningFeature(candidate); + + // int pbin = lower_bound(fConfigDFeC.binsMl.value.begin(), fConfigDFeC.binsMl.value.end(), binningFeature) - fConfigDFeC.binsMl.value.begin() - 1; + // if (pbin < 0) { + // pbin = 0; + // } else if (static_cast(fConfigDFeC.binsMl.value.size()) - 2 < pbin) { + // pbin = static_cast(fConfigDFeC.binsMl.value.size()) - 2; + // } + + // auto vecProb = mlResponseSCTeC.getModelOutput(inputFeatures, pbin); + // bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + // bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + // bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + // bdtScoreHb.emplace_back(static_cast(0)); + // hadronType.emplace_back(4); + // } + // } // end of Xi- loop + + // // eOmega pair // sign is restricted in baryon decay: Omegac0 -> e+ nu_e Omega- + // for (const auto& omegaId : omegaMinusIds) { + // auto cascade = cascades.rawIteratorAt(omegaId); + // if (cascade.posTrackId() == electron.globalIndex() || cascade.negTrackId() == electron.globalIndex() || cascade.bachelorId() == electron.globalIndex()) { + // continue; + // } + + // const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + // const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + // std::array covCasc = {0.}; + // constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + // for (int i = 0; i < 6; i++) { + // covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + // covCasc[i] = cascade.positionCovMat()[i]; + // } + // auto cascadeParCov = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + // cascadeParCov.setAbsCharge(1); + // cascadeParCov.setPID(o2::track::PID::OmegaMinus); + // o2::dataformats::DCA impactParameterCasc; + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, cascadeParCov, 2.f, matCorr, &impactParameterCasc); // cascadeParCov is TrackParCov object + + // auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(dfeC, collision, electron, cascade, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), o2::track::PID::OmegaMinus, o2::constants::physics::MassElectron); + // registry.fill(HIST("SCT/eC/hDecayLength"), eCpair.lxy, eCpair.lz); + // registry.fill(HIST("SCT/eC/hCosPA"), eCpair.cospa); + // registry.fill(HIST("SCT/eC/hDCA2legs"), eCpair.dca2legs); + // registry.fill(HIST("SCT/eC/hMass"), eCpair.mass); + // if (eCpair.isOK && fConfigDFeC.useML && eCpair.chi2PCA < fConfigDFeC.maxChi2PCA && eCpair.mass < fConfigDFeC.maxMassLH) { + // o2::analysis::pwgem::dilepton::sct::candidate candidate; + // fillCandidate(candidate, eCpair, cascadeParCov, impactParameterCasc); + // candidate.ptL = trackParCov.getPt(); + // candidate.massH = o2::constants::physics::MassOmegaMinus; + + // auto inputFeatures = mlResponseSCTeC.getInputFeatures(candidate); + // float binningFeature = mlResponseSCTeC.getBinningFeature(candidate); + + // int pbin = lower_bound(fConfigDFeC.binsMl.value.begin(), fConfigDFeC.binsMl.value.end(), binningFeature) - fConfigDFeC.binsMl.value.begin() - 1; + // if (pbin < 0) { + // pbin = 0; + // } else if (static_cast(fConfigDFeC.binsMl.value.size()) - 2 < pbin) { + // pbin = static_cast(fConfigDFeC.binsMl.value.size()) - 2; + // } + + // auto vecProb = mlResponseSCTeC.getModelOutput(inputFeatures, pbin); + // bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + // bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + // bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + // bdtScoreHb.emplace_back(static_cast(0)); + // hadronType.emplace_back(6); + // } + // } // end of Omega- loop + + } else { // electron + // eL pair // sign is restricted in baryon decay: Lc- -> e- anti_nu_e antiLambda + // for (const auto& antilambdaId : antilambdaIds) { + // auto v0 = v0s.rawIteratorAt(antilambdaId); + // if (v0.posTrackId() == electron.globalIndex() || v0.negTrackId() == electron.globalIndex()) { + // continue; + // } + // const std::array vertexV0 = {v0.x(), v0.y(), v0.z()}; + // const std::array momV0 = {v0.px(), v0.py(), v0.pz()}; + // std::array covV0 = {0.}; + // constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + // for (int i = 0; i < 6; i++) { + // covV0[MomInd[i]] = v0.momentumCovMat()[i]; + // covV0[i] = v0.positionCovMat()[i]; + // } + // auto v0ParCov = o2::track::TrackParCov(vertexV0, momV0, covV0, 0, true); + // v0ParCov.setAbsCharge(0); + // v0ParCov.setPID(o2::track::PID::Lambda); + // o2::dataformats::DCA impactParameterV0; + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, v0ParCov, 2.f, matCorr, &impactParameterV0); // v0ParCov is TrackParCov object + + // auto eV0pair = o2::aod::pwgem::dilepton::utils::makePairLeptonV0(dfeV0, collision, electron, v0, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), o2::track::PID::Lambda, o2::constants::physics::MassElectron); + // registry.fill(HIST("SCT/eV0/hDecayLength"), eV0pair.lxy, eV0pair.lz); + // registry.fill(HIST("SCT/eV0/hCosPA"), eV0pair.cospa); + // registry.fill(HIST("SCT/eV0/hDCA2legs"), eV0pair.dca2legs); + // registry.fill(HIST("SCT/eV0/hMass"), eV0pair.mass); + // if (eV0pair.isOK && fConfigDFeV0.useML && eV0pair.chi2PCA < fConfigDFeV0.maxChi2PCA && eV0pair.mass < fConfigDFeV0.maxMassLH) { + // o2::analysis::pwgem::dilepton::sct::candidate candidate; + // fillCandidate(candidate, eV0pair, v0ParCov, impactParameterV0); + // candidate.ptL = trackParCov.getPt(); + // candidate.massH = o2::constants::physics::MassLambda; + + // auto inputFeatures = mlResponseSCTeV0.getInputFeatures(candidate); + // float binningFeature = mlResponseSCTeV0.getBinningFeature(candidate); + + // int pbin = lower_bound(fConfigDFeV0.binsMl.value.begin(), fConfigDFeV0.binsMl.value.end(), binningFeature) - fConfigDFeV0.binsMl.value.begin() - 1; + // if (pbin < 0) { + // pbin = 0; + // } else if (static_cast(fConfigDFeV0.binsMl.value.size()) - 2 < pbin) { + // pbin = static_cast(fConfigDFeV0.binsMl.value.size()) - 2; + // } + + // auto vecProb = mlResponseSCTeV0.getModelOutput(inputFeatures, pbin); + // bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + // bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + // bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + // bdtScoreHb.emplace_back(static_cast(vecProb[3] * 255.f)); + // hadronType.emplace_back(3); + // } + // } // end of antilambda loop + + // // eXi pair // sign is restricted in baryon decay: Xic0bar -> e- anti_nu_e Xi+ + // for (const auto& xiId : xiPlusIds) { + // auto cascade = cascades.rawIteratorAt(xiId); + // if (cascade.posTrackId() == electron.globalIndex() || cascade.negTrackId() == electron.globalIndex() || cascade.bachelorId() == electron.globalIndex()) { + // continue; + // } + + // const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + // const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + // std::array covCasc = {0.}; + // constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + // for (int i = 0; i < 6; i++) { + // covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + // covCasc[i] = cascade.positionCovMat()[i]; + // } + // auto cascadeParCov = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + // cascadeParCov.setAbsCharge(1); + // cascadeParCov.setPID(o2::track::PID::XiMinus); + // o2::dataformats::DCA impactParameterCasc; + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, cascadeParCov, 2.f, matCorr, &impactParameterCasc); // cascadeParCov is TrackParCov object + + // auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(dfeC, collision, electron, cascade, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), o2::track::PID::XiMinus, o2::constants::physics::MassElectron); + // registry.fill(HIST("SCT/eC/hDecayLength"), eCpair.lxy, eCpair.lz); + // registry.fill(HIST("SCT/eC/hCosPA"), eCpair.cospa); + // registry.fill(HIST("SCT/eC/hDCA2legs"), eCpair.dca2legs); + // registry.fill(HIST("SCT/eC/hMass"), eCpair.mass); + // if (eCpair.isOK && fConfigDFeC.useML && eCpair.chi2PCA < fConfigDFeC.maxChi2PCA && eCpair.mass < fConfigDFeC.maxMassLH) { + // o2::analysis::pwgem::dilepton::sct::candidate candidate; + // fillCandidate(candidate, eCpair, cascadeParCov, impactParameterCasc); + // candidate.ptL = trackParCov.getPt(); + // candidate.massH = o2::constants::physics::MassXiMinus; + + // auto inputFeatures = mlResponseSCTeC.getInputFeatures(candidate); + // float binningFeature = mlResponseSCTeC.getBinningFeature(candidate); + + // int pbin = lower_bound(fConfigDFeC.binsMl.value.begin(), fConfigDFeC.binsMl.value.end(), binningFeature) - fConfigDFeC.binsMl.value.begin() - 1; + // if (pbin < 0) { + // pbin = 0; + // } else if (static_cast(fConfigDFeC.binsMl.value.size()) - 2 < pbin) { + // pbin = static_cast(fConfigDFeC.binsMl.value.size()) - 2; + // } + + // auto vecProb = mlResponseSCTeC.getModelOutput(inputFeatures, pbin); + // bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + // bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + // bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + // bdtScoreHb.emplace_back(static_cast(0)); + // hadronType.emplace_back(5); + // } + // } // end of Xi- loop + + // // eOmega pair // sign is restricted in baryon decay: Omegac0bar -> e- anti_nu_e Omega+ + // for (const auto& omegaId : omegaPlusIds) { + // auto cascade = cascades.rawIteratorAt(omegaId); + // if (cascade.posTrackId() == electron.globalIndex() || cascade.negTrackId() == electron.globalIndex() || cascade.bachelorId() == electron.globalIndex()) { + // continue; + // } + + // const std::array vertexCasc = {cascade.x(), cascade.y(), cascade.z()}; + // const std::array momCasc = {cascade.px(), cascade.py(), cascade.pz()}; + // std::array covCasc = {0.}; + // constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + // for (int i = 0; i < 6; i++) { + // covCasc[MomInd[i]] = cascade.momentumCovMat()[i]; + // covCasc[i] = cascade.positionCovMat()[i]; + // } + // auto cascadeParCov = o2::track::TrackParCov(vertexCasc, momCasc, covCasc, cascade.sign(), true); + // cascadeParCov.setAbsCharge(1); + // cascadeParCov.setPID(o2::track::PID::OmegaMinus); + // o2::dataformats::DCA impactParameterCasc; + // o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, cascadeParCov, 2.f, matCorr, &impactParameterCasc); // cascadeParCov is TrackParCov object + + // auto eCpair = o2::aod::pwgem::dilepton::utils::makePairLeptonCascade(dfeC, collision, electron, cascade, fElectronCut.useElectronHypothesis ? o2::track::PID::Electron : electron.pidForTracking(), o2::track::PID::OmegaMinus, o2::constants::physics::MassElectron); + // registry.fill(HIST("SCT/eC/hDecayLength"), eCpair.lxy, eCpair.lz); + // registry.fill(HIST("SCT/eC/hCosPA"), eCpair.cospa); + // registry.fill(HIST("SCT/eC/hDCA2legs"), eCpair.dca2legs); + // registry.fill(HIST("SCT/eC/hMass"), eCpair.mass); + // if (eCpair.isOK && fConfigDFeC.useML && eCpair.chi2PCA < fConfigDFeC.maxChi2PCA && eCpair.mass < fConfigDFeC.maxMassLH) { + // o2::analysis::pwgem::dilepton::sct::candidate candidate; + // fillCandidate(candidate, eCpair, cascadeParCov, impactParameterCasc); + // candidate.ptL = trackParCov.getPt(); + // candidate.massH = o2::constants::physics::MassOmegaMinus; + + // auto inputFeatures = mlResponseSCTeC.getInputFeatures(candidate); + // float binningFeature = mlResponseSCTeC.getBinningFeature(candidate); + + // int pbin = lower_bound(fConfigDFeC.binsMl.value.begin(), fConfigDFeC.binsMl.value.end(), binningFeature) - fConfigDFeC.binsMl.value.begin() - 1; + // if (pbin < 0) { + // pbin = 0; + // } else if (static_cast(fConfigDFeC.binsMl.value.size()) - 2 < pbin) { + // pbin = static_cast(fConfigDFeC.binsMl.value.size()) - 2; + // } + + // auto vecProb = mlResponseSCTeC.getModelOutput(inputFeatures, pbin); + // bdtScoreBkg.emplace_back(static_cast(vecProb[0] * 255.f)); + // bdtScorePromptHc.emplace_back(static_cast(vecProb[1] * 255.f)); + // bdtScoreNonpromptHc.emplace_back(static_cast(vecProb[2] * 255.f)); + // bdtScoreHb.emplace_back(static_cast(0)); + // hadronType.emplace_back(7); + // } + // } // end of Omega- loop + } + + products.sctTable(/*bdtScoreBkg,*/ bdtScorePromptHc, bdtScoreNonpromptHc, bdtScoreHb, hadronType); + + bdtScoreBkg.clear(); + bdtScorePromptHc.clear(); + bdtScoreNonpromptHc.clear(); + bdtScoreHb.clear(); + hadronType.clear(); + bdtScoreBkg.shrink_to_fit(); + bdtScorePromptHc.shrink_to_fit(); + bdtScoreNonpromptHc.shrink_to_fit(); + bdtScoreHb.shrink_to_fit(); + hadronType.shrink_to_fit(); + } // end of main electron loop + + hadronIds.clear(); + hadronIds.shrink_to_fit(); + + // k0sIds.clear(); + // k0sIds.shrink_to_fit(); + + // lambdaIds.clear(); + // lambdaIds.shrink_to_fit(); + // antilambdaIds.clear(); + // antilambdaIds.shrink_to_fit(); + + // xiMinusIds.clear(); + // xiMinusIds.shrink_to_fit(); + // xiPlusIds.clear(); + // xiPlusIds.shrink_to_fit(); + + // omegaMinusIds.clear(); + // omegaMinusIds.shrink_to_fit(); + // omegaPlusIds.clear(); + // omegaPlusIds.shrink_to_fit(); + + looseElectronIds.clear(); + looseElectronIds.shrink_to_fit(); + multiMapTracksPerCollision.clear(); + + fMapProbaEl.clear(); + + } // end of collision loop + clear(); + } + + template + void processWithoutTTCA(TBCs const&, TCollisions const&, TTracks const&, /*TV0s const&, TCascades const&,*/ TMCParticles const&, TProducts&, THistoregistry&) + { + LOGF(info, "processWithoutTTCA is not supported. Bye."); + + // initCCDB(bcs.begin()); + // // calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, nullptr); + + // for (const auto& collision : collisions) { + // auto bc = collision.template bc_as(); + // initCCDB(bc); + + // if constexpr (isMC) { + // if (!collision.has_mcCollision()) { + // continue; + // } + // } + + // if (!collision.isSelected()) { + // continue; + // } + + // if constexpr (isTriggerAnalysis) { + // if (collision.triggerMask_raw() == 0) { + // continue; + // } + // } + + // } // end of collision loop + + // clear(); + } + + void clear() + { + // this should be called at the end of DF. + fMapProbaEl.clear(); + fMapTOFNsigmaElReassociated.clear(); + // fMapTOFNsigmaPiReassociated.clear(); + // fMapTOFNsigmaKaReassociated.clear(); + // fMapTOFNsigmaPrReassociated.clear(); + fMapTOFBetaReassociated.clear(); + } + + void doSCTwithTracks(const bool flag) { fDoSCTwithTracks = flag; } + // void doSCTwithV0s(const bool flag) { fDoSCTwithV0s = flag; } + // void doSCTwithCascades(const bool flag) { fDoSCTwithCascades = flag; } + + float dca3DinSigmaOTF(const float dcaXY, const float dcaZ, const float cYY, const float cZZ, const float cZY) + { + float det = cYY * cZZ - cZY * cZY; // determinant + if (det < 0) { + return 999.f; + } else { + return std::sqrt(std::fabs((dcaXY * dcaXY * cZZ + dcaZ * dcaZ * cYY - 2. * dcaXY * dcaZ * cZY) / det / 2.)); // dca 3d in sigma + } + } + + template + void fillCandidate(TCandidate& candidate, TPair const& pair, TTrackParCov const& trackParCov, TDCA const& dcaInfo) + { + candidate.ptH = trackParCov.getPt(); + candidate.tpcNSigmaKa = 0; + candidate.impParXYH = dcaInfo.getY(); + candidate.impParZH = dcaInfo.getZ(); + candidate.impPar3DH = std::sqrt(std::pow(candidate.impParXYH, 2) + std::pow(candidate.impParZH, 2)); + candidate.impParXYHinSigma = candidate.impParXYH / std::sqrt(trackParCov.getSigmaY2()); + candidate.impParZHinSigma = candidate.impParZH / std::sqrt(trackParCov.getSigmaZ2()); + candidate.impPar3DHinSigma = dca3DinSigmaOTF(candidate.impParXYH, candidate.impParZH, trackParCov.getSigmaY2(), trackParCov.getSigmaZ2(), trackParCov.getSigmaZY()); + candidate.signLH = 0; + candidate.dcaLH = std::sqrt(pair.chi2PCA); + candidate.logChi2PCA = std::log10(pair.chi2PCA); + candidate.massLH = pair.mass; + candidate.signedMassLH = pair.mass; + candidate.cpa = pair.cospa; + candidate.cpaXY = pair.cospaXY; + // candidate.impParXY = pair.impParXY; + // candidate.impParZ = pair.impParZ; + // candidate.impPar3D = std::sqrt(std::pow(candidate.impParXY, 2) + std::pow(candidate.impParZ, 2)); + // candidate.impParXYinSigma = candidate.impParXY / std::sqrt(pair.impParCYY); + // candidate.impParZinSigma = candidate.impParZ / std::sqrt(pair.impParCZZ); + // candidate.impPar3DinSigma = dca3DinSigmaOTF(candidate.impParXY, candidate.impParZ, pair.impParCYY, pair.impParCZY, pair.impParCZZ); + candidate.decayLengthXY = pair.lxy; + candidate.decayLengthZ = pair.lz; + candidate.decayLength3D = pair.lxyz; + candidate.decayLengthXYinSigma = pair.lxy / pair.lxyErr; + candidate.decayLengthZinSigma = pair.lz / pair.lzErr; + candidate.decayLength3DinSigma = pair.lxyz / pair.lxyzErr; + } + + protected: + electronCut fElectronCut; + electronPFCut fElectronPFCut; + hadronCut fHadronCut; + // v0Cut fV0Cut; + // cascadeCut fCascadeCut; + cfgDFeT fConfigDFeT; + // cfgDFeV0 fConfigDFeV0; + // cfgDFeC fConfigDFeC; + + std::map, float> fMapProbaEl; // map pair(collisionId, trackId) -> probaEl + std::map, float> fMapTOFNsigmaElReassociated; // map pair(collisionId, trackId) -> tof n sigma el + // std::map, float> fMapTOFNsigmaPiReassociated; // map pair(collisionId, trackId) -> tof n sigma pi + // std::map, float> fMapTOFNsigmaKaReassociated; // map pair(collisionId, trackId) -> tof n sigma ka + // std::map, float> fMapTOFNsigmaPrReassociated; // map pair(collisionId, trackId) -> tof n sigma pr + std::map, float> fMapTOFBetaReassociated; // map pair(collisionId, trackId) -> tof beta + + int mRunNumber{0}; + float d_bz{0}; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + o2::dataformats::VertexBase mVtx; + o2::framework::Service mTOFResponse; + o2::analysis::MlResponsePID mlResponsePID; + o2::analysis::MlResponseSCT mlResponseSCTeT; + // o2::analysis::MlResponseSCT mlResponseSCTeV0; + // o2::analysis::MlResponseSCT mlResponseSCTeC; + + bool fDoSCTwithTracks{false}; + // bool fDoSCTwithV0s{false}; + // bool fDoSCTwithCascades{false}; + const std::vector max_mee_vec{0.02, 0.04, 0.06, 0.08, 0.10, 0.12, 0.14}; + o2::vertexing::DCAFitterN<2> dfeT; + // o2::vertexing::DCAFitterN<2> dfeV0; + // o2::vertexing::DCAFitterN<2> dfeC; + o2::ccdb::CcdbApi ccdbApi; + +}; // end ElectronModule + +} // namespace pwgem::dilepton::utils +} // namespace o2 + +#endif // PWGEM_DILEPTON_UTILS_ELECTRONMODULE_H_ diff --git a/PWGEM/Dilepton/Utils/EmDileptonMlResponse.cxx b/PWGEM/Dilepton/Utils/EmDileptonMlResponse.cxx new file mode 100644 index 00000000000..4dc3b4b32a0 --- /dev/null +++ b/PWGEM/Dilepton/Utils/EmDileptonMlResponse.cxx @@ -0,0 +1,22 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file EmDileptonMlResponse.cxx +/// \brief Helper file to provide compilation command for headers. +/// +/// \author Vít Kučera , Inha University + +#include "ElectronModule.h" // IWYU pragma: keep +#include "MlResponseDielectronSingleTrack.h" // IWYU pragma: keep +#include "MlResponseFwdTrack.h" // IWYU pragma: keep +#include "MlResponseO2Track.h" // IWYU pragma: keep +#include "MlResponsePID.h" // IWYU pragma: keep +#include "MlResponseSCT.h" // IWYU pragma: keep diff --git a/PWGEM/Dilepton/Utils/EventHistograms.h b/PWGEM/Dilepton/Utils/EventHistograms.h index da8d3734794..cafc4a8d756 100644 --- a/PWGEM/Dilepton/Utils/EventHistograms.h +++ b/PWGEM/Dilepton/Utils/EventHistograms.h @@ -15,6 +15,7 @@ #ifndef PWGEM_DILEPTON_UTILS_EVENTHISTOGRAMS_H_ #define PWGEM_DILEPTON_UTILS_EVENTHISTOGRAMS_H_ +#include "PWGEM/Dilepton/DataModel/EvSelFlags.h" #include "PWGEM/Dilepton/DataModel/dileptonTables.h" #include "Common/Core/RecoDecay.h" diff --git a/PWGEM/Dilepton/Utils/EventMixingHandler.h b/PWGEM/Dilepton/Utils/EventMixingHandler.h index 4698654be49..1c69210490f 100644 --- a/PWGEM/Dilepton/Utils/EventMixingHandler.h +++ b/PWGEM/Dilepton/Utils/EventMixingHandler.h @@ -16,6 +16,8 @@ #define PWGEM_DILEPTON_UTILS_EVENTMIXINGHANDLER_H_ #include +#include +#include #include namespace o2::aod::pwgem::dilepton::utils @@ -36,6 +38,9 @@ class EventMixingHandler fNdepth = ndepth; fMapMixBins.clear(); fMap_Tracks_per_collision.clear(); + if (fNdepth <= 0) { + throw std::invalid_argument("mixing depth must be positive"); + } } ~EventMixingHandler() @@ -44,7 +49,13 @@ class EventMixingHandler fMap_Tracks_per_collision.clear(); } - void SetNdepth(int ndepth) { fNdepth = ndepth; } + void SetNdepth(int ndepth) + { + if (ndepth <= 0) { + throw std::invalid_argument("mixing depth must be positive"); + } + fNdepth = ndepth; + } void ReserveNTracksPerCollision(U key_df_collision, int ntrack) { @@ -56,20 +67,33 @@ class EventMixingHandler fMap_Tracks_per_collision[key_df_collision].emplace_back(obj); } - std::vector GetCollisionIdsFromEventPool(T key_bin) { return fMapMixBins[key_bin]; } - std::vector GetTracksPerCollision(T key_bin, int index) { return fMap_Tracks_per_collision[fMapMixBins[key_bin][index]]; } - std::vector GetTracksPerCollision(U key_df_collision) { return fMap_Tracks_per_collision[key_df_collision]; } + const std::vector& GetCollisionIdsFromEventPool(const T& key_bin) const + { + const auto it = fMapMixBins.find(key_bin); + static const std::vector empty; + return it == fMapMixBins.end() ? empty : it->second; + } + + const std::vector& GetTracksPerCollision(const U& key) const + { + const auto it = fMap_Tracks_per_collision.find(key); + static const std::vector empty; + return it == fMap_Tracks_per_collision.end() ? empty : it->second; + } // call this function at the end of collision loop - void AddCollisionIdAtLast(T key_bin, U key_df_collision) + std::optional AddCollisionIdAtLast(T key_bin, U key_df_collision) { // LOGF(info, "fMapMixBins[key_bin].size() = %d", fMapMixBins[key_bin].size()); - if (static_cast(fMapMixBins[key_bin].size()) >= fNdepth) { - fMap_Tracks_per_collision[fMapMixBins[key_bin][0]].clear(); - fMap_Tracks_per_collision[fMapMixBins[key_bin][0]].shrink_to_fit(); - fMapMixBins[key_bin].erase(fMapMixBins[key_bin].begin()); + auto& ids = fMapMixBins[key_bin]; + std::optional evicted; + if (static_cast(ids.size()) >= fNdepth) { + evicted = ids.front(); + fMap_Tracks_per_collision.erase(*evicted); + ids.erase(ids.begin()); } - fMapMixBins[key_bin].emplace_back(key_df_collision); + ids.emplace_back(key_df_collision); + return evicted; } private: diff --git a/PWGEM/Dilepton/Utils/MCUtilities.h b/PWGEM/Dilepton/Utils/MCUtilities.h index d0a9a69df0e..51753c2da09 100644 --- a/PWGEM/Dilepton/Utils/MCUtilities.h +++ b/PWGEM/Dilepton/Utils/MCUtilities.h @@ -39,7 +39,7 @@ enum class EM_HFeeType : int { //_______________________________________________________________________ template -int hasFakeMatchITSTPC(TTrack const& track) +bool hasFakeMatchITSTPC(TTrack const& track) { // track and mctracklabel have to be joined. // bit 13 -- ITS/TPC labels are not equal @@ -52,7 +52,7 @@ int hasFakeMatchITSTPC(TTrack const& track) } //_______________________________________________________________________ template -int hasFakeMatchITSTPCTOF(TTrack const&) +bool hasFakeMatchITSTPCTOF(TTrack const&) { // track and mctracklabel have to be joined. return false; @@ -64,7 +64,7 @@ int hasFakeMatchITSTPCTOF(TTrack const&) } //_______________________________________________________________________ template -int hasFakeMatchMFTMCH(TTrack const& track) +bool hasFakeMatchMFTMCH(TTrack const& track) { // fwdtrack and mcfwdtracklabel have to be joined. if ((track.mcMask() & 1 << 7)) { @@ -74,12 +74,50 @@ int hasFakeMatchMFTMCH(TTrack const& track) } } //_______________________________________________________________________ +template +int isFromGammaZ(T const& track, TMCParticles const& mcParticles) +{ + if (!track.has_mothers()) { + return -999; + } + + int motherId = track.mothersIds()[0]; + while (motherId > -1) { + auto mp = mcParticles.rawIteratorAt(motherId); + if (std::abs(mp.pdgCode()) == 23) { + return mp.globalIndex(); + } + + if (mp.has_mothers()) { + motherId = mp.mothersIds()[0]; + } else { + motherId = -999; + } + } + return -999; +} +//_______________________________________________________________________ +template +int isPairFromGammaZ(T const& t1, T const& t2, TMCParticles const& mcParticles) +{ + int id1 = isFromGammaZ(t1, mcParticles); + int id2 = isFromGammaZ(t2, mcParticles); + if ((id1 > -1 && id2 > -1) && (id1 == id2)) { + return id1; + } else { + return -999; + } +} +//_______________________________________________________________________ template bool isCharmonia(T const& track) { if (std::abs(track.pdgCode()) < 100) { return false; } + if (std::abs(track.pdgCode()) > 1e+9) { + return false; + } std::string pdgStr = std::to_string(std::abs(track.pdgCode())); int n = pdgStr.length(); @@ -95,11 +133,22 @@ bool isCharmonia(T const& track) template bool isCharmMeson(T const& track) { + if (std::abs(track.pdgCode()) < 100) { + return false; + } + if (std::abs(track.pdgCode()) > 1e+9) { + return false; + } + if (isCharmonia(track)) { return false; } - if (400 < std::abs(track.pdgCode()) && std::abs(track.pdgCode()) < 500) { + std::string pdgStr = std::to_string(std::abs(track.pdgCode())); + int n = pdgStr.length(); + int pdg3 = std::stoi(pdgStr.substr(n - 3, 3)); // excited states are included too. + + if (400 < pdg3 && pdg3 < 500) { return true; } else { return false; @@ -109,6 +158,18 @@ bool isCharmMeson(T const& track) template bool isCharmBaryon(T const& track) { + if (std::abs(track.pdgCode()) < 100) { + return false; + } + if (std::abs(track.pdgCode()) > 1e+9) { + return false; + } + + // reject diquarks + if (std::abs(track.pdgCode()) == 4101 || std::abs(track.pdgCode()) == 4103 || std::abs(track.pdgCode()) == 4201 || std::abs(track.pdgCode()) == 4203 || std::abs(track.pdgCode()) == 4301 || std::abs(track.pdgCode()) == 4303 || std::abs(track.pdgCode()) == 4403) { + return false; + } + if (4000 < std::abs(track.pdgCode()) && std::abs(track.pdgCode()) < 5000) { return true; } else { @@ -122,6 +183,9 @@ bool isBottomonia(T const& track) if (std::abs(track.pdgCode()) < 100) { return false; } + if (std::abs(track.pdgCode()) > 1e+9) { + return false; + } std::string pdgStr = std::to_string(std::abs(track.pdgCode())); int n = pdgStr.length(); @@ -137,11 +201,22 @@ bool isBottomonia(T const& track) template bool isBeautyMeson(T const& track) { + if (std::abs(track.pdgCode()) < 100) { + return false; + } + if (std::abs(track.pdgCode()) > 1e+9) { + return false; + } + if (isBottomonia(track)) { return false; } - if (500 < std::abs(track.pdgCode()) && std::abs(track.pdgCode()) < 600) { + std::string pdgStr = std::to_string(std::abs(track.pdgCode())); + int n = pdgStr.length(); + int pdg3 = std::stoi(pdgStr.substr(n - 3, 3)); // excited states are included too. + + if (500 < pdg3 && pdg3 < 600) { return true; } else { return false; @@ -151,6 +226,18 @@ bool isBeautyMeson(T const& track) template bool isBeautyBaryon(T const& track) { + if (std::abs(track.pdgCode()) < 100) { + return false; + } + if (std::abs(track.pdgCode()) > 1e+9) { + return false; + } + + // reject diquarks + if (std::abs(track.pdgCode()) == 5101 || std::abs(track.pdgCode()) == 5103 || std::abs(track.pdgCode()) == 5201 || std::abs(track.pdgCode()) == 5203 || std::abs(track.pdgCode()) == 5301 || std::abs(track.pdgCode()) == 5303 || std::abs(track.pdgCode()) == 5401 || std::abs(track.pdgCode()) == 5403 || std::abs(track.pdgCode()) == 5503) { + return false; + } + if (5000 < std::abs(track.pdgCode()) && std::abs(track.pdgCode()) < 6000) { return true; } else { @@ -409,14 +496,14 @@ int IsFromBeauty(TMCParticle const& p, TMCParticles const& mcparticles) int motherid = p.mothersIds()[0]; // first mother index auto mp_tmp = mcparticles.iteratorAt(motherid); - if (std::abs(mp_tmp.pdgCode()) < 1e+9 && (std::to_string(std::abs(mp_tmp.pdgCode()))[std::to_string(std::abs(mp_tmp.pdgCode())).length() - 2] == '5' && std::to_string(std::abs(mp_tmp.pdgCode()))[std::to_string(std::abs(mp_tmp.pdgCode())).length() - 3] == '5') && std::abs(mp_tmp.pdgCode()) % 2 == 1) { + if (isBottomonia(mp_tmp)) { return -999; // reject bottomonia } while (motherid > -1) { if (motherid < mcparticles.size()) { // protect against bad mother indices. why is this needed? auto mp = mcparticles.iteratorAt(motherid); - if (std::abs(mp.pdgCode()) < 1e+9 && (std::to_string(std::abs(mp.pdgCode()))[std::to_string(std::abs(mp.pdgCode())).length() - 3] == '5' || std::to_string(std::abs(mp.pdgCode()))[std::to_string(std::abs(mp.pdgCode())).length() - 4] == '5')) { + if (isBeautyMeson(mp) || isBeautyBaryon(mp)) { return motherid; } if (mp.has_mothers()) { @@ -431,7 +518,6 @@ int IsFromBeauty(TMCParticle const& p, TMCParticles const& mcparticles) return -999; } - //_______________________________________________________________________ template int IsFromCharm(TMCParticle const& p, TMCParticles const& mcparticles) @@ -442,13 +528,13 @@ int IsFromCharm(TMCParticle const& p, TMCParticles const& mcparticles) int motherid = p.mothersIds()[0]; // first mother index auto mp_tmp = mcparticles.iteratorAt(motherid); - if (std::abs(mp_tmp.pdgCode()) < 1e+9 && (std::to_string(std::abs(mp_tmp.pdgCode()))[std::to_string(std::abs(mp_tmp.pdgCode())).length() - 2] == '4' && std::to_string(std::abs(mp_tmp.pdgCode()))[std::to_string(std::abs(mp_tmp.pdgCode())).length() - 3] == '4') && std::abs(mp_tmp.pdgCode()) % 2 == 1) { - return -999; // reject bottomonia + if (isCharmonia(mp_tmp)) { + return -999; // reject charmonia } while (motherid > -1) { if (motherid < mcparticles.size()) { // protect against bad mother indices. why is this needed? auto mp = mcparticles.iteratorAt(motherid); - if (std::abs(mp.pdgCode()) < 1e+9 && (std::to_string(std::abs(mp.pdgCode()))[std::to_string(std::abs(mp.pdgCode())).length() - 3] == '4' || std::to_string(std::abs(mp.pdgCode()))[std::to_string(std::abs(mp.pdgCode())).length() - 4] == '4')) { + if (isCharmMeson(mp) || isCharmBaryon(mp)) { return motherid; } if (mp.has_mothers()) { diff --git a/PWGEM/Dilepton/Utils/MlResponseFwdTrack.h b/PWGEM/Dilepton/Utils/MlResponseFwdTrack.h new file mode 100644 index 00000000000..073d3df9226 --- /dev/null +++ b/PWGEM/Dilepton/Utils/MlResponseFwdTrack.h @@ -0,0 +1,157 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MlResponseFwdTrack.h +/// \brief Class to compute the ML response for fwdtracks +/// \author Daiki Sekihata + +#ifndef PWGEM_DILEPTON_UTILS_MLRESPONSEFWDTRACK_H_ +#define PWGEM_DILEPTON_UTILS_MLRESPONSEFWDTRACK_H_ + +#include "Tools/ML/MlResponse.h" + +#include + +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_TRACK(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesFwdTrack::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER=FEATURE from track +#define CHECK_AND_FILL_TRACK(GETTER) \ + case static_cast(InputFeaturesFwdTrack::GETTER): { \ + inputFeature = track.GETTER; \ + break; \ + } + +namespace o2::analysis +{ +// possible input features for ML +enum class InputFeaturesFwdTrack : uint8_t { + multFT0C, + multMFT, + ptMCHMID, + rSigned1Pt, + dEta, + dPhi, + dX, + dY, + chi2MatchMCHMFT, + sigmaPhiMFT, + sigmaTglMFT, + sigmaPhiMCHMID, + sigmaTglMCHMID, +}; + +template +class MlResponseFwdTrack : public MlResponse +{ + public: + /// Default constructor + MlResponseFwdTrack() = default; + /// Default destructor + virtual ~MlResponseFwdTrack() = default; + + template + float return_feature(uint8_t idx, T const& track) + { + float inputFeature = 0.; + switch (idx) { + CHECK_AND_FILL_TRACK(multFT0C); + CHECK_AND_FILL_TRACK(multMFT); + CHECK_AND_FILL_TRACK(ptMCHMID); + CHECK_AND_FILL_TRACK(rSigned1Pt); + CHECK_AND_FILL_TRACK(dEta); + CHECK_AND_FILL_TRACK(dPhi); + CHECK_AND_FILL_TRACK(dX); + CHECK_AND_FILL_TRACK(dY); + CHECK_AND_FILL_TRACK(chi2MatchMCHMFT); + CHECK_AND_FILL_TRACK(sigmaPhiMFT); + CHECK_AND_FILL_TRACK(sigmaTglMFT); + CHECK_AND_FILL_TRACK(sigmaPhiMCHMID); + CHECK_AND_FILL_TRACK(sigmaTglMCHMID); + } + + return inputFeature; + } + + /// Method to get the input features vector needed for ML inference + /// \param track is the single track, \param collision is the collision + /// \return inputFeatures vector + template + std::vector getInputFeatures(T const& track) + { + std::vector inputFeatures; + for (const auto& idx : MlResponse::mCachedIndices) { + float inputFeature = return_feature(idx, track); + inputFeatures.emplace_back(inputFeature); + } + return inputFeatures; + } + + /// Method to get the value of variable chosen for binning + /// \param track is the single track, \param collision is the collision + /// \return binning variable + template + float getBinningFeature(T const& track) + { + return return_feature(mCachedIndexBinning, track); + } + + void cacheBinningIndex(std::string const& cfgBinningFeature) + { + setAvailableInputFeatures(); + if (MlResponse::mAvailableInputFeatures.count(cfgBinningFeature)) { + mCachedIndexBinning = MlResponse::mAvailableInputFeatures[cfgBinningFeature]; + } else { + LOG(fatal) << "Binning feature " << cfgBinningFeature << " not available! Please check your configurables."; + } + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + MlResponse::mAvailableInputFeatures = { + FILL_MAP_TRACK(multFT0C), + FILL_MAP_TRACK(multMFT), + FILL_MAP_TRACK(ptMCHMID), + FILL_MAP_TRACK(rSigned1Pt), + FILL_MAP_TRACK(dEta), + FILL_MAP_TRACK(dPhi), + FILL_MAP_TRACK(dX), + FILL_MAP_TRACK(dY), + FILL_MAP_TRACK(chi2MatchMCHMFT), + FILL_MAP_TRACK(sigmaPhiMFT), + FILL_MAP_TRACK(sigmaTglMFT), + FILL_MAP_TRACK(sigmaPhiMCHMID), + FILL_MAP_TRACK(sigmaTglMCHMID), + }; + } + + uint8_t mCachedIndexBinning; // index correspondance between configurable and available input features +}; + +} // namespace o2::analysis + +#undef FILL_MAP_TRACK +#undef CHECK_AND_FILL_TRACK + +#endif // PWGEM_DILEPTON_UTILS_MLRESPONSEFWDTRACK_H_ diff --git a/PWGEM/Dilepton/Utils/MlResponseO2Track.h b/PWGEM/Dilepton/Utils/MlResponseO2Track.h index 0b1bdbbe2be..80824c3ff87 100644 --- a/PWGEM/Dilepton/Utils/MlResponseO2Track.h +++ b/PWGEM/Dilepton/Utils/MlResponseO2Track.h @@ -58,10 +58,10 @@ int nsize = 0; \ int ncls = 0; \ for (int il = v1; il < v2; il++) { \ - nsize += track.itsClsSizeInLayer(il); \ - if (nsize > 0) { \ + if (track.itsClsSizeInLayer(il) > 0) { \ ncls++; \ } \ + nsize += track.itsClsSizeInLayer(il); \ } \ inputFeature = static_cast(nsize) / static_cast(ncls); \ break; \ @@ -73,10 +73,10 @@ int nsize = 0; \ int ncls = 0; \ for (int il = v1; il < v2; il++) { \ - nsize += track.itsClsSizeInLayer(il); \ - if (nsize > 0) { \ + if (track.itsClsSizeInLayer(il) > 0) { \ ncls++; \ } \ + nsize += track.itsClsSizeInLayer(il); \ } \ inputFeature = static_cast(nsize) / static_cast(ncls) * std::cos(std::atan(trackParCov.getTgl())); \ break; \ @@ -107,16 +107,6 @@ break; \ } -// Check if the index of mCachedIndices (index associated to a FEATURE) -// matches the entry in EnumInputFeatures associated to this FEATURE -// if so, the inputFeatures vector is filled with the FEATURE's value -// by calling the corresponding GETTER1 from track and multiplying with cos(atan(GETTER2)) -#define CHECK_AND_FILL_O2_TRACK_COS(FEATURE, GETTER1, GETTER2) \ - case static_cast(InputFeaturesO2Track::FEATURE): { \ - inputFeature = track.GETTER1() * std::cos(std::atan(track.GETTER2())); \ - break; \ - } - // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE // if so, the inputFeatures vector is filled with the FEATURE's value @@ -343,7 +333,6 @@ class MlResponseO2Track : public MlResponse #undef CHECK_AND_FILL_O2_TRACK_MEAN_ITSCLUSTER_SIZE #undef CHECK_AND_FILL_O2_TRACK_MEAN_ITSCLUSTER_SIZE_COS #undef CHECK_AND_FILL_O2_TRACK_SQRT -#undef CHECK_AND_FILL_O2_TRACK_COS #undef CHECK_AND_FILL_O2_TRACK_TPCTOF #undef CHECK_AND_FILL_O2_TRACK_RELDIFF #undef CHECK_AND_FILL_DIELECTRON_COLLISION diff --git a/PWGEM/Dilepton/Utils/MlResponsePID.h b/PWGEM/Dilepton/Utils/MlResponsePID.h new file mode 100644 index 00000000000..13e8048f798 --- /dev/null +++ b/PWGEM/Dilepton/Utils/MlResponsePID.h @@ -0,0 +1,147 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MlResponsePID.h +/// \brief Class to compute the ML response for fwdtracks +/// \author Daiki Sekihata + +#ifndef PWGEM_DILEPTON_UTILS_MLRESPONSEPID_H_ +#define PWGEM_DILEPTON_UTILS_MLRESPONSEPID_H_ + +#include "Tools/ML/MlResponse.h" + +#include + +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_TRACK(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesPID::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER=FEATURE from track +#define CHECK_AND_FILL_TRACK(GETTER) \ + case static_cast(InputFeaturesPID::GETTER): { \ + inputFeature = track.GETTER; \ + break; \ + } + +namespace o2::analysis +{ +// possible input features for ML +enum class InputFeaturesPID : uint8_t { + tpcInnerParam, + tpcNClsFound, + tpcChi2NCl, + tpcNSigmaEl, + tofNSigmaEl, + meanClusterSizeITSobCosTgl, +}; + +namespace pwgem::dilepton::mlpid +{ +struct candidate { + float tpcInnerParam{0}; + int tpcNClsFound{0}; + float tpcChi2NCl{0}; + float tpcNSigmaEl{0}; + float tofNSigmaEl{0}; + float meanClusterSizeITSobCosTgl{0}; +}; +} // namespace pwgem::dilepton::mlpid + +template +class MlResponsePID : public MlResponse +{ + public: + /// Default constructor + MlResponsePID() = default; + /// Default destructor + virtual ~MlResponsePID() = default; + + template + float return_feature(uint8_t idx, T const& track) + { + float inputFeature = 0.; + switch (idx) { + CHECK_AND_FILL_TRACK(tpcInnerParam); + CHECK_AND_FILL_TRACK(tpcNClsFound); + CHECK_AND_FILL_TRACK(tpcChi2NCl); + CHECK_AND_FILL_TRACK(tpcNSigmaEl); + CHECK_AND_FILL_TRACK(tofNSigmaEl); + CHECK_AND_FILL_TRACK(meanClusterSizeITSobCosTgl); + } + return inputFeature; + } + + /// Method to get the input features vector needed for ML inference + /// \param track is the single track, \param collision is the collision + /// \return inputFeatures vector + template + std::vector getInputFeatures(T const& track) + { + std::vector inputFeatures; + for (const auto& idx : MlResponse::mCachedIndices) { + float inputFeature = return_feature(idx, track); + inputFeatures.emplace_back(inputFeature); + } + return inputFeatures; + } + + /// Method to get the value of variable chosen for binning + /// \param track is the single track, \param collision is the collision + /// \return binning variable + template + float getBinningFeature(T const& track) + { + return return_feature(mCachedIndexBinning, track); + } + + void cacheBinningIndex(std::string const& cfgBinningFeature) + { + setAvailableInputFeatures(); + if (MlResponse::mAvailableInputFeatures.count(cfgBinningFeature)) { + mCachedIndexBinning = MlResponse::mAvailableInputFeatures[cfgBinningFeature]; + } else { + LOG(fatal) << "Binning feature " << cfgBinningFeature << " not available! Please check your configurables."; + } + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + MlResponse::mAvailableInputFeatures = { + FILL_MAP_TRACK(tpcInnerParam), + FILL_MAP_TRACK(tpcNClsFound), + FILL_MAP_TRACK(tpcChi2NCl), + FILL_MAP_TRACK(tpcNSigmaEl), + FILL_MAP_TRACK(tofNSigmaEl), + FILL_MAP_TRACK(meanClusterSizeITSobCosTgl), + }; + } + + uint8_t mCachedIndexBinning; // index correspondance between configurable and available input features +}; + +} // namespace o2::analysis + +#undef FILL_MAP_TRACK +#undef CHECK_AND_FILL_TRACK + +#endif // PWGEM_DILEPTON_UTILS_MLRESPONSEPID_H_ diff --git a/PWGEM/Dilepton/Utils/MlResponseSCT.h b/PWGEM/Dilepton/Utils/MlResponseSCT.h new file mode 100644 index 00000000000..172b2043ce6 --- /dev/null +++ b/PWGEM/Dilepton/Utils/MlResponseSCT.h @@ -0,0 +1,276 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file MlResponseSCT.h +/// \brief Class to compute the ML response for fwdtracks +/// \author Daiki Sekihata + +#ifndef PWGEM_DILEPTON_UTILS_MLRESPONSESCT_H_ +#define PWGEM_DILEPTON_UTILS_MLRESPONSESCT_H_ + +#include "Tools/ML/MlResponse.h" + +#include + +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_TRACK(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesSCT::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER=FEATURE from track +#define CHECK_AND_FILL_TRACK(GETTER) \ + case static_cast(InputFeaturesSCT::GETTER): { \ + inputFeature = track.GETTER; \ + break; \ + } + +namespace o2::analysis +{ +// possible input features for ML +enum class InputFeaturesSCT : uint8_t { + ptL, + impParXYL, + impParZL, + impPar3DL, + impParXYLinSigma, + impParZLinSigma, + impPar3DLinSigma, + ptH, + massH, + tpcNSigmaKa, + impParXYH, + impParZH, + impPar3DH, + impParXYHinSigma, + impParZHinSigma, + impPar3DHinSigma, + signLH, + dcaLH, + logChi2PCA, + massLH, + signedMassLH, + missingPtNuPerpToFD, + correctedMass, + cpa, + cpaXY, + // impParXY, + // impParZ, + // impPar3D, + // impParXYinSigma, + // impParZinSigma, + // impPar3DinSigma, + decayLengthXY, + decayLengthZ, + decayLength3D, + decayLengthXYinSigma, + decayLengthZinSigma, + decayLength3DinSigma, +}; + +namespace pwgem::dilepton::sct +{ +struct candidate { + // lepton info. (don't use them to avoid bias to single lepton) + float ptL{0}; + float impParXYL{0}; + float impParZL{0}; + float impPar3DL{0}; + float impParXYLinSigma{0}; + float impParZLinSigma{0}; + float impPar3DLinSigma{0}; + + // hadron information + float ptH{0}; + float massH{0}; // only for V0s and Cascades + float tpcNSigmaKa{0}; + float impParXYH{0}; + float impParZH{0}; + float impPar3DH{0}; + float impParXYHinSigma{0}; + float impParZHinSigma{0}; + float impPar3DHinSigma{0}; + + // LH pair information + int signLH{0}; + float dcaLH{0}; + float logChi2PCA{0}; + float massLH{0}; + float signedMassLH{0}; + float missingPtNuPerpToFD{0}; + float correctedMass{0}; + float cpa{0}; + float cpaXY{0}; + // float impParXY{0}; + // float impParZ{0}; + // float impPar3D{0}; + // float impParXYinSigma{0}; + // float impParZinSigma{0}; + // float impPar3DinSigma{0}; + float decayLengthXY{0}; + float decayLengthZ{0}; + float decayLength3D{0}; + float decayLengthXYinSigma{0}; + float decayLengthZinSigma{0}; + float decayLength3DinSigma{0}; +}; +} // namespace pwgem::dilepton::sct + +template +class MlResponseSCT : public MlResponse +{ + public: + /// Default constructor + MlResponseSCT() = default; + /// Default destructor + virtual ~MlResponseSCT() = default; + + template + float return_feature(uint8_t idx, T const& track) + { + float inputFeature = 0.; + switch (idx) { + CHECK_AND_FILL_TRACK(ptL); + CHECK_AND_FILL_TRACK(impParXYL); + CHECK_AND_FILL_TRACK(impParZL); + CHECK_AND_FILL_TRACK(impPar3DL); + CHECK_AND_FILL_TRACK(impParXYLinSigma); + CHECK_AND_FILL_TRACK(impParZLinSigma); + CHECK_AND_FILL_TRACK(impPar3DLinSigma); + CHECK_AND_FILL_TRACK(ptH); + CHECK_AND_FILL_TRACK(massH); + CHECK_AND_FILL_TRACK(tpcNSigmaKa); + CHECK_AND_FILL_TRACK(impParXYH); + CHECK_AND_FILL_TRACK(impParZH); + CHECK_AND_FILL_TRACK(impPar3DH); + CHECK_AND_FILL_TRACK(impParXYHinSigma); + CHECK_AND_FILL_TRACK(impParZHinSigma); + CHECK_AND_FILL_TRACK(impPar3DHinSigma); + CHECK_AND_FILL_TRACK(signLH); + CHECK_AND_FILL_TRACK(dcaLH); + CHECK_AND_FILL_TRACK(logChi2PCA); + CHECK_AND_FILL_TRACK(massLH); + CHECK_AND_FILL_TRACK(signedMassLH); + CHECK_AND_FILL_TRACK(missingPtNuPerpToFD); + CHECK_AND_FILL_TRACK(correctedMass); + CHECK_AND_FILL_TRACK(cpa); + CHECK_AND_FILL_TRACK(cpaXY); + // CHECK_AND_FILL_TRACK(impParXY); + // CHECK_AND_FILL_TRACK(impParZ); + // CHECK_AND_FILL_TRACK(impPar3D); + // CHECK_AND_FILL_TRACK(impParXYinSigma); + // CHECK_AND_FILL_TRACK(impParZinSigma); + // CHECK_AND_FILL_TRACK(impPar3DinSigma); + CHECK_AND_FILL_TRACK(decayLengthXY); + CHECK_AND_FILL_TRACK(decayLengthZ); + CHECK_AND_FILL_TRACK(decayLength3D); + CHECK_AND_FILL_TRACK(decayLengthXYinSigma); + CHECK_AND_FILL_TRACK(decayLengthZinSigma); + CHECK_AND_FILL_TRACK(decayLength3DinSigma); + } + return inputFeature; + } + + /// Method to get the input features vector needed for ML inference + /// \param track is the single track, \param collision is the collision + /// \return inputFeatures vector + template + std::vector getInputFeatures(T const& track) + { + std::vector inputFeatures; + for (const auto& idx : MlResponse::mCachedIndices) { + float inputFeature = return_feature(idx, track); + inputFeatures.emplace_back(inputFeature); + } + return inputFeatures; + } + + /// Method to get the value of variable chosen for binning + /// \param track is the single track, \param collision is the collision + /// \return binning variable + template + float getBinningFeature(T const& track) + { + return return_feature(mCachedIndexBinning, track); + } + + void cacheBinningIndex(std::string const& cfgBinningFeature) + { + setAvailableInputFeatures(); + if (MlResponse::mAvailableInputFeatures.count(cfgBinningFeature)) { + mCachedIndexBinning = MlResponse::mAvailableInputFeatures[cfgBinningFeature]; + } else { + LOG(fatal) << "Binning feature " << cfgBinningFeature << " not available! Please check your configurables."; + } + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + MlResponse::mAvailableInputFeatures = { + FILL_MAP_TRACK(ptL), + FILL_MAP_TRACK(impParXYL), + FILL_MAP_TRACK(impParZL), + FILL_MAP_TRACK(impPar3DL), + FILL_MAP_TRACK(impParXYLinSigma), + FILL_MAP_TRACK(impParZLinSigma), + FILL_MAP_TRACK(impPar3DLinSigma), + FILL_MAP_TRACK(ptH), + FILL_MAP_TRACK(massH), + FILL_MAP_TRACK(tpcNSigmaKa), + FILL_MAP_TRACK(impParXYH), + FILL_MAP_TRACK(impParZH), + FILL_MAP_TRACK(impPar3DH), + FILL_MAP_TRACK(impParXYHinSigma), + FILL_MAP_TRACK(impParZHinSigma), + FILL_MAP_TRACK(impPar3DHinSigma), + FILL_MAP_TRACK(signLH), + FILL_MAP_TRACK(dcaLH), + FILL_MAP_TRACK(logChi2PCA), + FILL_MAP_TRACK(massLH), + FILL_MAP_TRACK(signedMassLH), + FILL_MAP_TRACK(missingPtNuPerpToFD), + FILL_MAP_TRACK(correctedMass), + FILL_MAP_TRACK(cpa), + FILL_MAP_TRACK(cpaXY), + // FILL_MAP_TRACK(impParXY), + // FILL_MAP_TRACK(impParZ), + // FILL_MAP_TRACK(impPar3D), + // FILL_MAP_TRACK(impParXYinSigma), + // FILL_MAP_TRACK(impParZinSigma), + // FILL_MAP_TRACK(impPar3DinSigma), + FILL_MAP_TRACK(decayLengthXY), + FILL_MAP_TRACK(decayLengthZ), + FILL_MAP_TRACK(decayLength3D), + FILL_MAP_TRACK(decayLengthXYinSigma), + FILL_MAP_TRACK(decayLengthZinSigma), + FILL_MAP_TRACK(decayLength3DinSigma), + }; + } + + uint8_t mCachedIndexBinning; // index correspondance between configurable and available input features +}; + +} // namespace o2::analysis + +#undef FILL_MAP_TRACK +#undef CHECK_AND_FILL_TRACK + +#endif // PWGEM_DILEPTON_UTILS_MLRESPONSESCT_H_ diff --git a/PWGEM/Dilepton/Utils/MomentumSmearer.h b/PWGEM/Dilepton/Utils/MomentumSmearer.h index 59bb41f948c..878f3403109 100644 --- a/PWGEM/Dilepton/Utils/MomentumSmearer.h +++ b/PWGEM/Dilepton/Utils/MomentumSmearer.h @@ -32,6 +32,7 @@ #include #include +#include #include class MomentumSmearer @@ -43,11 +44,11 @@ class MomentumSmearer /// Constructor with resolution histograms MomentumSmearer(TString resFileName, TString resPtHistName, TString resEtaHistName, TString resPhiPosHistName, TString resPhiNegHistName) { - setResFileName(resFileName); - setResPtHistName(resPtHistName); - setResEtaHistName(resEtaHistName); - setResPhiPosHistName(resPhiPosHistName); - setResPhiNegHistName(resPhiNegHistName); + setResFileName(std::move(resFileName)); + setResPtHistName(std::move(resPtHistName)); + setResEtaHistName(std::move(resEtaHistName)); + setResPhiPosHistName(std::move(resPhiPosHistName)); + setResPhiNegHistName(std::move(resPhiNegHistName)); setEffFileName(""); setEffHistName(""); setDCAFileName(""); @@ -58,8 +59,8 @@ class MomentumSmearer /// Constructor with resolution ND sparse histogram MomentumSmearer(TString resFileName, TString resNDHistName) { - setResFileName(resFileName); - setResNDHistName(resNDHistName); + setResFileName(std::move(resFileName)); + setResNDHistName(std::move(resNDHistName)); setResPtHistName(""); setResEtaHistName(""); setResPhiPosHistName(""); @@ -75,13 +76,13 @@ class MomentumSmearer /// Constructor with resolution histograms and efficiency MomentumSmearer(TString resFileName, TString resPtHistName, TString resEtaHistName, TString resPhiPosHistName, TString resPhiNegHistName, TString effFileName, TString effHistName) { - setResFileName(resFileName); - setResPtHistName(resPtHistName); - setResEtaHistName(resEtaHistName); - setResPhiPosHistName(resPhiPosHistName); - setResPhiNegHistName(resPhiNegHistName); - setEffFileName(effFileName); - setEffHistName(effHistName); + setResFileName(std::move(resFileName)); + setResPtHistName(std::move(resPtHistName)); + setResEtaHistName(std::move(resEtaHistName)); + setResPhiPosHistName(std::move(resPhiPosHistName)); + setResPhiNegHistName(std::move(resPhiNegHistName)); + setEffFileName(std::move(effFileName)); + setEffHistName(std::move(effHistName)); setDCAFileName(""); setDCAHistName(""); init(); @@ -90,15 +91,15 @@ class MomentumSmearer /// Constructor with resolution histograms and efficiency and dca MomentumSmearer(TString resFileName, TString resPtHistName, TString resEtaHistName, TString resPhiPosHistName, TString resPhiNegHistName, TString effFileName, TString effHistName, TString dcaFileName, TString dcaHistName) { - setResFileName(resFileName); - setResPtHistName(resPtHistName); - setResEtaHistName(resEtaHistName); - setResPhiPosHistName(resPhiPosHistName); - setResPhiNegHistName(resPhiNegHistName); - setEffFileName(effFileName); - setEffHistName(effHistName); - setDCAFileName(dcaFileName); - setDCAHistName(dcaHistName); + setResFileName(std::move(resFileName)); + setResPtHistName(std::move(resPtHistName)); + setResEtaHistName(std::move(resEtaHistName)); + setResPhiPosHistName(std::move(resPhiPosHistName)); + setResPhiNegHistName(std::move(resPhiNegHistName)); + setEffFileName(std::move(effFileName)); + setEffHistName(std::move(effHistName)); + setDCAFileName(std::move(dcaFileName)); + setDCAHistName(std::move(dcaHistName)); init(); } @@ -560,19 +561,19 @@ class MomentumSmearer // setters void setNDSmearing(bool flag) { fDoNDSmearing = flag; } - void setResFileName(TString resFileName) { fResFileName = resFileName; } - void setResNDHistName(TString resNDHistName) { fResNDHistName = resNDHistName; } - void setResPtHistName(TString resPtHistName) { fResPtHistName = resPtHistName; } - void setResEtaHistName(TString resEtaHistName) { fResEtaHistName = resEtaHistName; } - void setResPhiPosHistName(TString resPhiPosHistName) { fResPhiPosHistName = resPhiPosHistName; } - void setResPhiNegHistName(TString resPhiNegHistName) { fResPhiNegHistName = resPhiNegHistName; } - void setEffFileName(TString effFileName) { fEffFileName = effFileName; } - void setEffHistName(TString effHistName) { fEffHistName = effHistName; } - void setDCAFileName(TString dcaFileName) { fDCAFileName = dcaFileName; } - void setDCAHistName(TString dcaHistName) { fDCAHistName = dcaHistName; } - void setCcdbPathRes(TString ccdbPathRes) { fCcdbPathRes = ccdbPathRes; } - void setCcdbPathEff(TString ccdbPathEff) { fCcdbPathEff = ccdbPathEff; } - void setCcdbPathDCA(TString ccdbPathDCA) { fCcdbPathDCA = ccdbPathDCA; } + void setResFileName(TString resFileName) { fResFileName = std::move(resFileName); } + void setResNDHistName(TString resNDHistName) { fResNDHistName = std::move(resNDHistName); } + void setResPtHistName(TString resPtHistName) { fResPtHistName = std::move(resPtHistName); } + void setResEtaHistName(TString resEtaHistName) { fResEtaHistName = std::move(resEtaHistName); } + void setResPhiPosHistName(TString resPhiPosHistName) { fResPhiPosHistName = std::move(resPhiPosHistName); } + void setResPhiNegHistName(TString resPhiNegHistName) { fResPhiNegHistName = std::move(resPhiNegHistName); } + void setEffFileName(TString effFileName) { fEffFileName = std::move(effFileName); } + void setEffHistName(TString effHistName) { fEffHistName = std::move(effHistName); } + void setDCAFileName(TString dcaFileName) { fDCAFileName = std::move(dcaFileName); } + void setDCAHistName(TString dcaHistName) { fDCAHistName = std::move(dcaHistName); } + void setCcdbPathRes(TString ccdbPathRes) { fCcdbPathRes = std::move(ccdbPathRes); } + void setCcdbPathEff(TString ccdbPathEff) { fCcdbPathEff = std::move(ccdbPathEff); } + void setCcdbPathDCA(TString ccdbPathDCA) { fCcdbPathDCA = std::move(ccdbPathDCA); } void setCcdb(o2::framework::Service ccdb) { fCcdb = ccdb; diff --git a/PWGEM/Dilepton/Utils/PairUtilities.h b/PWGEM/Dilepton/Utils/PairUtilities.h index f250ef8aa98..55e9b08f08e 100644 --- a/PWGEM/Dilepton/Utils/PairUtilities.h +++ b/PWGEM/Dilepton/Utils/PairUtilities.h @@ -71,12 +71,18 @@ enum class DileptonPrefilterBit : int { }; enum class DileptonPrefilterBitDerived : int { - kMee = 0, // reject tracks from pi0 dalitz decays at very low mass where S/B > 1 + kMee = 0, // reject tracks from pi0 dalitz decays at very low mass kPhiV = 1, // reject tracks from photon conversions kSplitOrMergedTrackLS = 2, // reject split or marged tracks in LS pairs based on momentum deta-dphi at PV kSplitOrMergedTrackULS = 3, // reject split or marged tracks in ULS pairs based on momentum deta-dphi at PV }; +enum class MatCorrType : int { + kNone = 0, + kTGeo = 1, + kLUT = 2 +}; + using SMatrix55 = ROOT::Math::SMatrix>; using SMatrix5 = ROOT::Math::SVector; diff --git a/PWGEM/Dilepton/Utils/SemiCharmTag.h b/PWGEM/Dilepton/Utils/SemiCharmTag.h index ef211ccbef5..f2ca9343986 100644 --- a/PWGEM/Dilepton/Utils/SemiCharmTag.h +++ b/PWGEM/Dilepton/Utils/SemiCharmTag.h @@ -23,31 +23,58 @@ #include #include #include -#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include +#include + namespace o2::aod::pwgem::dilepton::utils { struct LHPair { // struct to store electron-hadron pair information float mass{-999.f}; + float pt{-999.f}; + float p{-999.f}; + float deta{-999.f}; + float dphi{-999.f}; float dca2legs{-999.f}; + float chi2PCA{-999.f}; float cospa{-999.f}; + float cospaXY{-999.f}; + float cospaRZ{-999.f}; float lxy{-999.f}; - float lxyErr{-999.f}; float lz{-999.f}; - float lzErr{-999.f}; float lxyz{-999.f}; + float lxyErr{-999.f}; + float lzErr{-999.f}; float lxyzErr{-999.f}; + float impParXY{-999.f}; + float impParZ{-999.f}; + float impParCYY{-999.f}; + float impParCZY{-999.f}; + float impParCZZ{-999.f}; + + // float ptSVL{-999.f}; + // float plSVL{-999.f}; + // float ptSVH{-999.f}; + // float plSVH{-999.f}; + + // float ptFDL{-999.f}; + // float plFDL{-999.f}; + // float ptFDH{-999.f}; + // float plFDH{-999.f}; + + // float ptFD{-999.f}; + // float plFD{-999.f}; + bool isOK{false}; }; template -LHPair makePairLeptonTrack(TFitter& fitter, TCollision const& collision, TLepton const& lepton, TTrack const& track, o2::track::PID::ID leptonId, o2::track::PID::ID strHadId) +LHPair makePairLeptonTrack(TFitter& fitter, TCollision const& collision, TLepton const& lepton, TTrack const& track, const o2::track::PID::ID leptonId, const o2::track::PID::ID strHadId, const float massLepton) { LHPair pair; auto leptonParCov = getTrackParCov(lepton); @@ -85,54 +112,68 @@ LHPair makePairLeptonTrack(TFitter& fitter, TCollision const& collision, TLepton fitter.getTrack(1).getPxPyPzGlo(pvec1); // track std::array pvecSum = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; + // pair.ptSVL = RecoDecay::sqrtSumOfSquares(pvec0[0], pvec0[1]); + // pair.ptSVH = RecoDecay::sqrtSumOfSquares(pvec1[0], pvec1[1]); + // pair.plSVL = pvec0[2]; + // pair.plSVH = pvec1[2]; + pair.cospa = RecoDecay::cpa(vertex, svpos, pvecSum); + pair.cospaXY = RecoDecay::cpaXY(vertex, svpos, pvecSum); + pair.cospaRZ = RecoDecay::cpaRZ(vertex, svpos, pvecSum); pair.dca2legs = std::sqrt(fitter.getChi2AtPCACandidate()); + pair.chi2PCA = fitter.getChi2AtPCACandidate(); pair.lxy = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2)); - pair.lz = std::fabs(svpos[2] - collision.posZ()); + pair.lz = svpos[2] - collision.posZ(); pair.lxyz = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2) + std::pow(svpos[2] - collision.posZ(), 2)); auto primaryVertex = getPrimaryVertex(collision); - std::array covVtxLK = fitter.calcPCACovMatrixFlat(); - double phiLK{}, thetaLK{}; - getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, svpos, phiLK, thetaLK); - pair.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phiLK, thetaLK) + getRotatedCovMatrixXX(covVtxLK, phiLK, thetaLK)); - pair.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phiLK, 0.) + getRotatedCovMatrixXX(covVtxLK, phiLK, 0.)); - pair.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, thetaLK) + getRotatedCovMatrixXX(covVtxLK, 0, thetaLK)); - - ROOT::Math::PxPyPzMVector v1(pvec0[0], pvec0[1], pvec0[2], o2::constants::physics::MassElectron); - if (leptonId == o2::track::PID::Electron) { - v1.SetM(o2::constants::physics::MassElectron); - } else if (leptonId == o2::track::PID::Muon) { - v1.SetM(o2::constants::physics::MassMuon); - } else { - LOGF(info, "leptonId supports only Electron or Muon."); - pair.isOK = false; - return pair; - } - + std::array covVtxLH = fitter.calcPCACovMatrixFlat(); + double phiLH{}, thetaLH{}; + getPointDirection(std::array{primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, svpos, phiLH, thetaLH); + pair.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phiLH, thetaLH) + getRotatedCovMatrixXX(covVtxLH, phiLH, thetaLH)); + pair.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phiLH, 0.) + getRotatedCovMatrixXX(covVtxLH, phiLH, 0.)); + pair.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, thetaLH) + getRotatedCovMatrixXX(covVtxLH, 0, thetaLH)); + + // std::array uvFD = {(svpos[0] - collision.posX()) / pair.lxyz, (svpos[1] - collision.posY()) / pair.lxyz, (svpos[2] - collision.posZ()) / pair.lxyz}; // unit vector of flight direction + // pair.plFD = RecoDecay::dotProd(pvecSum, uvFD); + // pair.ptFD = RecoDecay::sqrtSumOfSquares(pvecSum[0] - pair.plFD * uvFD[0], pvecSum[1] - pair.plFD * uvFD[1], pvecSum[2] - pair.plFD * uvFD[2]); + + // pair.plFDL = RecoDecay::dotProd(pvec0, uvFD); + // pair.plFDH = RecoDecay::dotProd(pvec1, uvFD); + // pair.ptFDL = RecoDecay::sqrtSumOfSquares(pvec0[0] - pair.plFDL * uvFD[0], pvec0[1] - pair.plFDL * uvFD[1], pvec0[2] - pair.plFDL * uvFD[2]); + // pair.ptFDH = RecoDecay::sqrtSumOfSquares(pvec1[0] - pair.plFDH * uvFD[0], pvec1[1] - pair.plFDH * uvFD[1], pvec1[2] - pair.plFDH * uvFD[2]); + + // // propagate the 2 prongs to the secondary vertex + // leptonParCov.propagateTo(vtx[0], fitter.getBz()); + // trackParCov.propagateTo(vtx[0], fitter.getBz()); + + // // calculate impact parameter + // o2::dataformats::DCA dcaLH; + // auto trackParCovLH = o2::dataformats::V0(fitter.getPCACandidatePos(), pvecSum, fitter.calcPCACovMatrixFlat(), leptonParCov, trackParCov); + // trackParCovLH.propagateToDCA(primaryVertex, fitter.getBz(), &dcaLH); + + // pair.impParXY = dcaLH.getY(); + // pair.impParZ = dcaLH.getZ(); + // pair.impParCYY = dcaLH.getSigmaY2(); + // pair.impParCZY = dcaLH.getSigmaYZ(); + // pair.impParCZZ = dcaLH.getSigmaZ2(); + + // LOGF(info, "fitter.getBz() = %f, dcaLH.getY() = %f, dcaLH.getZ() = %f", fitter.getBz(), dcaLH.getY(), dcaLH.getZ()); + + ROOT::Math::PxPyPzMVector v1(pvec0[0], pvec0[1], pvec0[2], massLepton); ROOT::Math::PxPyPzMVector v2(pvec1[0], pvec1[1], pvec1[2], o2::constants::physics::MassKaonCharged); - if (strHadId == o2::track::PID::Kaon) { - v2.SetM(o2::constants::physics::MassKaonCharged); - } else { - LOGF(info, "strHadId supports only Kaon."); - pair.isOK = false; - return pair; - } - ROOT::Math::PxPyPzMVector v12 = v1 + v2; - pair.mass = v12.M(); - // pair.dca2legs = dca2legs; - // pair.cospa = cospa; - // pair.lxy = lxy; - // pair.lz = lz; + pair.pt = v12.Pt(); + pair.p = v12.P(); + pair.deta = v1.Eta() - v2.Eta(); // lepton - hadron + pair.dphi = RecoDecay::constrainAngle(RecoDecay::constrainAngle(v1.Phi(), 0, 1U) - RecoDecay::constrainAngle(v2.Phi(), 0, 1U), -M_PI, 1U); // lepton - hadron pair.isOK = true; - return pair; } template -LHPair makePairLeptonV0(TFitter& fitter, TCollision const& collision, TLepton const& lepton, TV0 const& v0, o2::track::PID::ID leptonId, o2::track::PID::ID strHadId) +LHPair makePairLeptonV0(TFitter& fitter, TCollision const& collision, TLepton const& lepton, TV0 const& v0, o2::track::PID::ID leptonId, o2::track::PID::ID strHadId, const float massLepton) { LHPair pair; auto leptonParCov = getTrackParCov(lepton); @@ -176,14 +217,22 @@ LHPair makePairLeptonV0(TFitter& fitter, TCollision const& collision, TLepton co for (int i = 0; i < 3; i++) { svpos[i] = vtx[i]; } - fitter.getTrack(0).getPxPyPzGlo(pvec0); // electron + fitter.getTrack(0).getPxPyPzGlo(pvec0); // lepton fitter.getTrack(1).getPxPyPzGlo(pvec1); // v0 std::array pvecSum = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; + // pair.ptSVL = RecoDecay::sqrtSumOfSquares(pvec0[0], pvec0[1]); + // pair.ptSVH = RecoDecay::sqrtSumOfSquares(pvec1[0], pvec1[1]); + // pair.plSVL = pvec0[2]; + // pair.plSVH = pvec1[2]; + pair.cospa = RecoDecay::cpa(vertex, svpos, pvecSum); + pair.cospaXY = RecoDecay::cpaXY(vertex, svpos, pvecSum); + pair.cospaRZ = RecoDecay::cpaRZ(vertex, svpos, pvecSum); pair.dca2legs = std::sqrt(fitter.getChi2AtPCACandidate()); + pair.chi2PCA = fitter.getChi2AtPCACandidate(); pair.lxy = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2)); - pair.lz = std::fabs(svpos[2] - collision.posZ()); + pair.lz = svpos[2] - collision.posZ(); pair.lxyz = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2) + std::pow(svpos[2] - collision.posZ(), 2)); auto primaryVertex = getPrimaryVertex(collision); @@ -194,17 +243,31 @@ LHPair makePairLeptonV0(TFitter& fitter, TCollision const& collision, TLepton co pair.lxyErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phiLV0, 0.) + getRotatedCovMatrixXX(covVtxLV0, phiLV0, 0.)); pair.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, thetaLV0) + getRotatedCovMatrixXX(covVtxLV0, 0, thetaLV0)); - ROOT::Math::PxPyPzMVector v1(pvec0[0], pvec0[1], pvec0[2], o2::constants::physics::MassElectron); - if (leptonId == o2::track::PID::Electron) { - v1.SetM(o2::constants::physics::MassElectron); - } else if (leptonId == o2::track::PID::Muon) { - v1.SetM(o2::constants::physics::MassMuon); - } else { - LOGF(info, "leptonId supports only Electron or Muon."); - pair.isOK = false; - return pair; - } + // std::array uvFD = {(svpos[0] - collision.posX()) / pair.lxyz, (svpos[1] - collision.posY()) / pair.lxyz, (svpos[2] - collision.posZ()) / pair.lxyz}; // unit vector of flight direction + // pair.plFD = RecoDecay::dotProd(pvecSum, uvFD); + // pair.ptFD = RecoDecay::sqrtSumOfSquares(pvecSum[0] - pair.plFD * uvFD[0], pvecSum[1] - pair.plFD * uvFD[1], pvecSum[2] - pair.plFD * uvFD[2]); + + // pair.plFDL = RecoDecay::dotProd(pvec0, uvFD); + // pair.plFDH = RecoDecay::dotProd(pvec1, uvFD); + // pair.ptFDL = RecoDecay::sqrtSumOfSquares(pvec0[0] - pair.plFDL * uvFD[0], pvec0[1] - pair.plFDL * uvFD[1], pvec0[2] - pair.plFDL * uvFD[2]); + // pair.ptFDH = RecoDecay::sqrtSumOfSquares(pvec1[0] - pair.plFDH * uvFD[0], pvec1[1] - pair.plFDH * uvFD[1], pvec1[2] - pair.plFDH * uvFD[2]); + // // propagate the 2 prongs to the secondary vertex + // leptonParCov.propagateTo(vtx[0], fitter.getBz()); + // v0ParCov.propagateTo(vtx[0], fitter.getBz()); + + // // calculate impact parameter + // o2::dataformats::DCA dcaLH; + // auto trackParCovLH = o2::dataformats::V0(fitter.getPCACandidatePos(), pvecSum, fitter.calcPCACovMatrixFlat(), leptonParCov, v0ParCov); + // trackParCovLH.propagateToDCA(primaryVertex, fitter.getBz(), &dcaLH); + + // pair.impParXY = dcaLH.getY(); + // pair.impParZ = dcaLH.getZ(); + // pair.impParCYY = dcaLH.getSigmaY2(); + // pair.impParCZY = dcaLH.getSigmaYZ(); + // pair.impParCZZ = dcaLH.getSigmaZ2(); + + ROOT::Math::PxPyPzMVector v1(pvec0[0], pvec0[1], pvec0[2], massLepton); ROOT::Math::PxPyPzMVector v2(pvec1[0], pvec1[1], pvec1[2], o2::constants::physics::MassLambda); if (strHadId == o2::track::PID::Lambda) { v2.SetM(o2::constants::physics::MassLambda); @@ -217,19 +280,18 @@ LHPair makePairLeptonV0(TFitter& fitter, TCollision const& collision, TLepton co } ROOT::Math::PxPyPzMVector v12 = v1 + v2; - pair.mass = v12.M(); - // pair.dca2legs = dca2legs; - // pair.cospa = cospa; - // pair.lxy = lxy; - // pair.lz = lz; + pair.pt = v12.Pt(); + pair.p = v12.P(); + pair.deta = v1.Eta() - v2.Eta(); // lepton - hadron + pair.dphi = RecoDecay::constrainAngle(RecoDecay::constrainAngle(v1.Phi(), 0, 1U) - RecoDecay::constrainAngle(v2.Phi(), 0, 1U), -M_PI, 1U); // lepton - hadron pair.isOK = true; return pair; } template -LHPair makePairLeptonCascade(TFitter& fitter, TCollision const& collision, TLepton const& lepton, TCascade const& cascade, o2::track::PID::ID leptonId, o2::track::PID::ID strHadId) +LHPair makePairLeptonCascade(TFitter& fitter, TCollision const& collision, TLepton const& lepton, TCascade const& cascade, o2::track::PID::ID leptonId, o2::track::PID::ID strHadId, const float massLepton) { LHPair pair; auto leptonParCov = getTrackParCov(lepton); @@ -277,14 +339,22 @@ LHPair makePairLeptonCascade(TFitter& fitter, TCollision const& collision, TLept for (int i = 0; i < 3; i++) { svpos[i] = vtx[i]; } - fitter.getTrack(0).getPxPyPzGlo(pvec0); // electron - fitter.getTrack(1).getPxPyPzGlo(pvec1); // v0 + fitter.getTrack(0).getPxPyPzGlo(pvec0); // lepton + fitter.getTrack(1).getPxPyPzGlo(pvec1); // cascade std::array pvecSum = {pvec0[0] + pvec1[0], pvec0[1] + pvec1[1], pvec0[2] + pvec1[2]}; + // pair.ptSVL = RecoDecay::sqrtSumOfSquares(pvec0[0], pvec0[1]); + // pair.ptSVH = RecoDecay::sqrtSumOfSquares(pvec1[0], pvec1[1]); + // pair.plSVL = pvec0[2]; + // pair.plSVH = pvec1[2]; + pair.cospa = RecoDecay::cpa(vertex, svpos, pvecSum); + pair.cospaXY = RecoDecay::cpaXY(vertex, svpos, pvecSum); + pair.cospaRZ = RecoDecay::cpaRZ(vertex, svpos, pvecSum); pair.dca2legs = std::sqrt(fitter.getChi2AtPCACandidate()); + pair.chi2PCA = fitter.getChi2AtPCACandidate(); pair.lxy = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2)); - pair.lz = std::fabs(svpos[2] - collision.posZ()); + pair.lz = svpos[2] - collision.posZ(); pair.lxyz = std::sqrt(std::pow(svpos[0] - collision.posX(), 2) + std::pow(svpos[1] - collision.posY(), 2) + std::pow(svpos[2] - collision.posZ(), 2)); auto primaryVertex = getPrimaryVertex(collision); @@ -295,17 +365,31 @@ LHPair makePairLeptonCascade(TFitter& fitter, TCollision const& collision, TLept pair.lzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), 0, thetaLC) + getRotatedCovMatrixXX(covVtxLC, 0, thetaLC)); pair.lxyzErr = std::sqrt(getRotatedCovMatrixXX(primaryVertex.getCov(), phiLC, thetaLC) + getRotatedCovMatrixXX(covVtxLC, phiLC, thetaLC)); - ROOT::Math::PxPyPzMVector v1(pvec0[0], pvec0[1], pvec0[2], o2::constants::physics::MassElectron); - if (leptonId == o2::track::PID::Electron) { - v1.SetM(o2::constants::physics::MassElectron); - } else if (leptonId == o2::track::PID::Muon) { - v1.SetM(o2::constants::physics::MassMuon); - } else { - LOGF(info, "leptonId supports only Electron or Muon."); - pair.isOK = false; - return pair; - } + // std::array uvFD = {(svpos[0] - collision.posX()) / pair.lxyz, (svpos[1] - collision.posY()) / pair.lxyz, (svpos[2] - collision.posZ()) / pair.lxyz}; // unit vector of flight direction + // pair.plFD = RecoDecay::dotProd(pvecSum, uvFD); + // pair.ptFD = RecoDecay::sqrtSumOfSquares(pvecSum[0] - pair.plFD * uvFD[0], pvecSum[1] - pair.plFD * uvFD[1], pvecSum[2] - pair.plFD * uvFD[2]); + // pair.plFDL = RecoDecay::dotProd(pvec0, uvFD); + // pair.plFDH = RecoDecay::dotProd(pvec1, uvFD); + // pair.ptFDL = RecoDecay::sqrtSumOfSquares(pvec0[0] - pair.plFDL * uvFD[0], pvec0[1] - pair.plFDL * uvFD[1], pvec0[2] - pair.plFDL * uvFD[2]); + // pair.ptFDH = RecoDecay::sqrtSumOfSquares(pvec1[0] - pair.plFDH * uvFD[0], pvec1[1] - pair.plFDH * uvFD[1], pvec1[2] - pair.plFDH * uvFD[2]); + + // // propagate the 2 prongs to the secondary vertex + // leptonParCov.propagateTo(vtx[0], fitter.getBz()); + // cascParCov.propagateTo(vtx[0], fitter.getBz()); + + // // calculate impact parameter + // o2::dataformats::DCA dcaLH; + // auto trackParCovLH = o2::dataformats::V0(fitter.getPCACandidatePos(), pvecSum, fitter.calcPCACovMatrixFlat(), leptonParCov, cascParCov); + // trackParCovLH.propagateToDCA(primaryVertex, fitter.getBz(), &dcaLH); + + // pair.impParXY = dcaLH.getY(); + // pair.impParZ = dcaLH.getZ(); + // pair.impParCYY = dcaLH.getSigmaY2(); + // pair.impParCZY = dcaLH.getSigmaYZ(); + // pair.impParCZZ = dcaLH.getSigmaZ2(); + + ROOT::Math::PxPyPzMVector v1(pvec0[0], pvec0[1], pvec0[2], massLepton); ROOT::Math::PxPyPzMVector v2(pvec1[0], pvec1[1], pvec1[2], o2::constants::physics::MassXiMinus); if (strHadId == o2::track::PID::XiMinus) { v2.SetM(o2::constants::physics::MassXiMinus); @@ -318,12 +402,11 @@ LHPair makePairLeptonCascade(TFitter& fitter, TCollision const& collision, TLept } ROOT::Math::PxPyPzMVector v12 = v1 + v2; - pair.mass = v12.M(); - // pair.dca2legs = dca2legs; - // pair.cospa = cospa; - // pair.lxy = lxy; - // pair.lz = lz; + pair.pt = v12.Pt(); + pair.p = v12.P(); + pair.deta = v1.Eta() - v2.Eta(); // lepton - hadron + pair.dphi = RecoDecay::constrainAngle(RecoDecay::constrainAngle(v1.Phi(), 0, 1U) - RecoDecay::constrainAngle(v2.Phi(), 0, 1U), -M_PI, 1U); // lepton - hadron pair.isOK = true; return pair; diff --git a/PWGEM/PhotonMeson/CMakeLists.txt b/PWGEM/PhotonMeson/CMakeLists.txt index 1ff6de24294..f23f9fb3690 100644 --- a/PWGEM/PhotonMeson/CMakeLists.txt +++ b/PWGEM/PhotonMeson/CMakeLists.txt @@ -14,3 +14,5 @@ add_subdirectory(Core) add_subdirectory(Tasks) add_subdirectory(TableProducer) + +#add_subdirectory(Legacy) diff --git a/PWGEM/PhotonMeson/Core/CMakeLists.txt b/PWGEM/PhotonMeson/Core/CMakeLists.txt index 1eb9558356d..0150236c53e 100644 --- a/PWGEM/PhotonMeson/Core/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Core/CMakeLists.txt @@ -21,16 +21,3 @@ o2physics_add_library(PWGEMPhotonMesonCore EMBitFlags.cxx EMNonLin.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::MLCore O2Physics::PWGEMDileptonCore KFParticle::KFParticle) - -o2physics_target_root_dictionary(PWGEMPhotonMesonCore - HEADERS V0PhotonCut.h - DalitzEECut.h - PHOSPhotonCut.h - EMCPhotonCut.h - PairCut.h - EMPhotonEventCut.h - CutsLibrary.h - HistogramsLibrary.h - EMBitFlags.h - EMNonLin.h - LINKDEF PWGEMPhotonMesonCoreLinkDef.h) diff --git a/PWGEM/PhotonMeson/Core/DalitzEECut.cxx b/PWGEM/PhotonMeson/Core/DalitzEECut.cxx index 25645430f27..027f807355d 100644 --- a/PWGEM/PhotonMeson/Core/DalitzEECut.cxx +++ b/PWGEM/PhotonMeson/Core/DalitzEECut.cxx @@ -17,16 +17,12 @@ #include -#include - #include #include #include #include -ClassImp(DalitzEECut); - -// const char* DalitzEECut::mCutNames[static_cast(DalitzEECut::DalitzEECuts::kNCuts)] = {"Mee", "PairPtRange", "PairRapidityRange", "PairDCARange", "PhivPair", "TrackPtRange", "TrackEtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCNsigmaEl", "TPCNsigmaMu", "TPCNsigmaPi", "TPCNsigmaKa", "TPCNsigmaPr", "TOFNsigmaEl", "TOFNsigmaMu", "TOFNsigmaPi", "TOFNsigmaKa", "TOFNsigmaPr", "DCA3Dsigma", "DCAxy", "DCAz", "ITSNCls", "ITSChi2NDF", "ITSClusterSize", "Prefilter"}; +// std::array(DalitzEECut::DalitzEECuts::kNCuts)> DalitzEECut::mCutNames = {"Mee", "PairPtRange", "PairRapidityRange", "PairDCARange", "PhivPair", "TrackPtRange", "TrackEtaRange", "TPCNCls", "TPCCrossedRows", "TPCCrossedRowsOverNCls", "TPCChi2NDF", "TPCNsigmaEl", "TPCNsigmaMu", "TPCNsigmaPi", "TPCNsigmaKa", "TPCNsigmaPr", "TOFNsigmaEl", "TOFNsigmaMu", "TOFNsigmaPi", "TOFNsigmaKa", "TOFNsigmaPr", "DCA3Dsigma", "DCAxy", "DCAz", "ITSNCls", "ITSChi2NDF", "ITSClusterSize", "Prefilter"}; const std::pair> DalitzEECut::its_ib_any_Requirement = {1, {0, 1, 2}}; // hits on any ITS ib layers. const std::pair> DalitzEECut::its_ib_1st_Requirement = {1, {0}}; // hit on 1st ITS ib layers. @@ -49,7 +45,7 @@ void DalitzEECut::SetMeeRange(float min, float max) mMaxMee = max; LOG(info) << "DalitzEE Cut, set mee range: " << mMinMee << " - " << mMaxMee; } -void DalitzEECut::SetMaxPhivPairMeeDep(std::function meeDepCut) +void DalitzEECut::SetMaxPhivPairMeeDep(const std::function& meeDepCut) { mMaxPhivPairMeeDep = meeDepCut; LOG(info) << "DalitzEE Cut, set max phiv pair mee dep: " << mMaxPhivPairMeeDep(0.02); @@ -133,7 +129,7 @@ void DalitzEECut::SetMaxDcaZ(float maxDcaZ) LOG(info) << "DalitzEE Cut, set max DCA z: " << mMaxDcaZ; } -void DalitzEECut::SetMaxDcaXYPtDep(std::function ptDepCut) +void DalitzEECut::SetMaxDcaXYPtDep(const std::function& ptDepCut) { mMaxDcaXYPtDep = ptDepCut; LOG(info) << "DalitzEE Cut, set max DCA xy pt dep: " << mMaxDcaXYPtDep(1.0); @@ -182,7 +178,7 @@ void DalitzEECut::SetChi2TOF(float min, float max) void DalitzEECut::SetPIDScheme(int scheme) { mPIDScheme = scheme; - LOG(info) << "DalitzEE Cut, PID scheme: " << static_cast(mPIDScheme); + LOG(info) << "DalitzEE Cut, PID scheme: " << mPIDScheme; } void DalitzEECut::IncludeITSsa(bool flag, float max) { diff --git a/PWGEM/PhotonMeson/Core/DalitzEECut.h b/PWGEM/PhotonMeson/Core/DalitzEECut.h index 293a52424c0..0e04d27f7d1 100644 --- a/PWGEM/PhotonMeson/Core/DalitzEECut.h +++ b/PWGEM/PhotonMeson/Core/DalitzEECut.h @@ -20,25 +20,23 @@ #include "PWGEM/Dilepton/Utils/PairUtilities.h" #include -#include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include - -#include #include #include #include #include +#include #include -class DalitzEECut : public TNamed +class DalitzEECut { public: DalitzEECut() = default; - DalitzEECut(const char* name, const char* title) : TNamed(name, title) {} + DalitzEECut(const char* name, const char* title) : name(name), title(title) {} enum class DalitzEECuts : int { // pair cut @@ -63,7 +61,7 @@ class DalitzEECut : public TNamed kITSChi2NDF, kNCuts }; - static const char* mCutNames[static_cast(DalitzEECuts::kNCuts)]; + // static std::array(DalitzEECuts::kNCuts)> mCutNames; enum class PIDSchemes : int { kUnDef = -1, @@ -71,6 +69,9 @@ class DalitzEECut : public TNamed kTPConly = 1, }; + [[nodiscard]] const std::string& getName() const { return name; } + [[nodiscard]] const std::string& getTitle() const { return title; } + template bool IsSelected(TTrack1 const& t1, TTrack2 const& t2, float bz) const { @@ -204,8 +205,6 @@ class DalitzEECut : public TNamed return PassTOFif(track); case static_cast(PIDSchemes::kUnDef): - return true; - default: return true; } @@ -277,7 +276,7 @@ class DalitzEECut : public TNamed void SetPairPtRange(float minPt = 0.f, float maxPt = 1e10f); void SetPairYRange(float minY = -1e10f, float maxY = 1e10f); void SetMeeRange(float min = 0.f, float max = 0.04); - void SetMaxPhivPairMeeDep(std::function meeDepCut); + void SetMaxPhivPairMeeDep(const std::function& meeDepCut); void SelectPhotonConversion(bool flag); void SetTrackPtRange(float minPt = 0.f, float maxPt = 1e10f); @@ -302,15 +301,17 @@ class DalitzEECut : public TNamed void SetTrackDca3DRange(float min, float max); // in sigma void SetMaxDcaXY(float maxDcaXY); // in cm void SetMaxDcaZ(float maxDcaZ); // in cm - void SetMaxDcaXYPtDep(std::function ptDepCut); + void SetMaxDcaXYPtDep(const std::function& ptDepCut); void ApplyPrefilter(bool flag); void ApplyPhiV(bool flag); void IncludeITSsa(bool flag, float maxpt); // Getters - bool IsPhotonConversionSelected() const { return mSelectPC; } + [[nodiscard]] bool IsPhotonConversionSelected() const { return mSelectPC; } private: + std::string name; + std::string title; static const std::pair> its_ib_any_Requirement; static const std::pair> its_ib_1st_Requirement; // pair cuts @@ -318,8 +319,8 @@ class DalitzEECut : public TNamed float mMinPairPt{0.f}, mMaxPairPt{1e10f}; // range in pT float mMinPairY{-1e10f}, mMaxPairY{1e10f}; // range in rapidity float mMinPhivPair{0.f}, mMaxPhivPair{+3.2}; - std::function mMaxPhivPairMeeDep{}; // max phiv as a function of mee - bool mSelectPC{false}; // flag to select photon conversion used in mMaxPhivPairMeeDep + std::function mMaxPhivPairMeeDep; // max phiv as a function of mee + bool mSelectPC{false}; // flag to select photon conversion used in mMaxPhivPairMeeDep // kinematic cuts float mMinTrackPt{0.f}, mMaxTrackPt{1e10f}; // range in pT @@ -337,11 +338,11 @@ class DalitzEECut : public TNamed bool mRequireITSibAny{true}; bool mRequireITSib1st{false}; - float mMinDca3D{0.0f}; // min dca in 3D in units of sigma - float mMaxDca3D{1e+10}; // max dca in 3D in units of sigma - float mMaxDcaXY{1.0f}; // max dca in xy plane - float mMaxDcaZ{1.0f}; // max dca in z direction - std::function mMaxDcaXYPtDep{}; // max dca in xy plane as function of pT + float mMinDca3D{0.0f}; // min dca in 3D in units of sigma + float mMaxDca3D{1e+10}; // max dca in 3D in units of sigma + float mMaxDcaXY{1.0f}; // max dca in xy plane + float mMaxDcaZ{1.0f}; // max dca in z direction + std::function mMaxDcaXYPtDep; // max dca in xy plane as function of pT bool mApplyPhiV{true}; float mMinMeanClusterSizeITS{-1e10f}, mMaxMeanClusterSizeITS{1e10f}; // x cos(lmabda) float mMinChi2TOF{-1e10f}, mMaxChi2TOF{1e10f}; // max tof chi2 per @@ -353,8 +354,6 @@ class DalitzEECut : public TNamed float mMinTPCNsigmaEl{-1e+10}, mMaxTPCNsigmaEl{+1e+10}; float mMinTPCNsigmaPi{0}, mMaxTPCNsigmaPi{0}; float mMinTOFNsigmaEl{-1e+10}, mMaxTOFNsigmaEl{+1e+10}; - - ClassDef(DalitzEECut, 2); }; #endif // PWGEM_PHOTONMESON_CORE_DALITZEECUT_H_ diff --git a/PWGEM/PhotonMeson/Core/EMBitFlags.cxx b/PWGEM/PhotonMeson/Core/EMBitFlags.cxx index d182eb03f40..09dacf35760 100644 --- a/PWGEM/PhotonMeson/Core/EMBitFlags.cxx +++ b/PWGEM/PhotonMeson/Core/EMBitFlags.cxx @@ -33,7 +33,7 @@ std::size_t EMBitFlags::size() const bool EMBitFlags::test(std::size_t i) const { assert(i < mSize); - return mBits[word(i)] & mask(i); + return static_cast(mBits[word(i)] & mask(i)); } void EMBitFlags::set(std::size_t i) diff --git a/PWGEM/PhotonMeson/Core/EMBitFlags.h b/PWGEM/PhotonMeson/Core/EMBitFlags.h index d8a47390cc1..99aac0dfd6a 100644 --- a/PWGEM/PhotonMeson/Core/EMBitFlags.h +++ b/PWGEM/PhotonMeson/Core/EMBitFlags.h @@ -33,21 +33,22 @@ class EMBitFlags explicit EMBitFlags(std::size_t nBits = 0); /// \brief get number of stored bits - std::size_t size() const; + [[nodiscard]] std::size_t size() const; /// \brief check bit i /// \param i index of bit that should be checked - bool test(std::size_t i) const; + /// \return false if the bit was set before + [[nodiscard]] bool test(std::size_t i) const; - /// \brief set bit i + /// \brief set bit i to false /// \param i index of bit which value should be set void set(std::size_t i); - /// \brief reset bit i + /// \brief reset bit i to true /// \param i index of bit which value should be reset void reset(std::size_t i); - /// \brief resetting all flags to false + /// \brief resetting all flags to true void clear(); /// \brief reserve space in the underlying storage for nBits bits diff --git a/PWGEM/PhotonMeson/Core/EMCConversionCandidate.h b/PWGEM/PhotonMeson/Core/EMCConversionCandidate.h new file mode 100644 index 00000000000..bc8a6f79d2e --- /dev/null +++ b/PWGEM/PhotonMeson/Core/EMCConversionCandidate.h @@ -0,0 +1,85 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file EMCConversionCandidate.h +/// \brief Header file that defines EMCConversionCandidate, a struct to be used with the EMCConversion ML model +/// \author Marvin Hemmer + +#ifndef PWGEM_PHOTONMESON_CORE_EMCCONVERSIONCANDIDATE_H_ +#define PWGEM_PHOTONMESON_CORE_EMCCONVERSIONCANDIDATE_H_ + +#include +#include + +namespace o2::analysis::em +{ + +// Requires T to expose the specific named getters getInputFeatures() needs, +// each returning something convertible to float. +template +concept IsEmcConversionCandidate = requires(T const& c) { + { c.minv() } -> std::convertible_to; + { c.deltaEta() } -> std::convertible_to; + { c.deltaR() } -> std::convertible_to; + { c.phiv() } -> std::convertible_to; + { c.rConv() } -> std::convertible_to; + { c.totE() } -> std::convertible_to; + { c.e2() } -> std::convertible_to; + { c.e1() } -> std::convertible_to; + { c.deltaPhi() } -> std::convertible_to; + { c.harmonicEt() } -> std::convertible_to; + { c.m021() } -> std::convertible_to; + { c.m022() } -> std::convertible_to; + { c.time1() } -> std::convertible_to; + { c.time2() } -> std::convertible_to; + { c.ncell1() } -> std::convertible_to; + { c.ncell2() } -> std::convertible_to; +}; + +struct EMCConversionCandidate { + float mMinv; + float mDeltaEta; + float mDeltaR; + float mPhiv; + float mRConv; + float mTotE; + float mE2; + float mE1; + float mDeltaPhi; + float mHarmonicEt; + float mM021; + float mM022; + float mTime1; + float mTime2; + uint8_t mNcell1; + uint8_t mNcell2; + + [[nodiscard]] float minv() const { return mMinv; } + [[nodiscard]] float deltaEta() const { return mDeltaEta; } + [[nodiscard]] float deltaR() const { return mDeltaR; } + [[nodiscard]] float phiv() const { return mPhiv; } + [[nodiscard]] float rConv() const { return mRConv; } + [[nodiscard]] float totE() const { return mTotE; } + [[nodiscard]] float e2() const { return mE2; } + [[nodiscard]] float e1() const { return mE1; } + [[nodiscard]] float deltaPhi() const { return mDeltaPhi; } + [[nodiscard]] float harmonicEt() const { return mHarmonicEt; } + [[nodiscard]] float m021() const { return mM021; } + [[nodiscard]] float m022() const { return mM022; } + [[nodiscard]] float time1() const { return mTime1; } + [[nodiscard]] float time2() const { return mTime2; } + [[nodiscard]] uint8_t ncell1() const { return mNcell1; } + [[nodiscard]] uint8_t ncell2() const { return mNcell2; } +}; + +} // namespace o2::analysis::em + +#endif // PWGEM_PHOTONMESON_CORE_EMCCONVERSIONCANDIDATE_H_ diff --git a/PWGEM/PhotonMeson/Core/EMCPhotonCut.cxx b/PWGEM/PhotonMeson/Core/EMCPhotonCut.cxx index 9e75d9e558e..b46f1a11b32 100644 --- a/PWGEM/PhotonMeson/Core/EMCPhotonCut.cxx +++ b/PWGEM/PhotonMeson/Core/EMCPhotonCut.cxx @@ -19,15 +19,13 @@ #include -#include - +#include +#include #include -ClassImp(EMCPhotonCut); - -const char* EMCPhotonCut::mCutNames[static_cast(EMCPhotonCut::EMCPhotonCuts::kNCuts)] = {"Definition", "Energy", "NCell", "M02", "Timing", "TrackMatching", "SecTrackMatching", "Exotic"}; +const std::array(EMCPhotonCut::EMCPhotonCuts::kNCuts)> EMCPhotonCut::mCutNames = {"Definition", "Energy", "NCell", "M02", "Timing", "TrackMatching", "SecTrackMatching", "Exotic"}; -void EMCPhotonCut::SetClusterizer(std::string clusterDefinitionString) +void EMCPhotonCut::SetClusterizer(const std::string& clusterDefinitionString) { mDefinition = static_cast(o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionString)); LOG(info) << "EMCal Photon Cut, set cluster definition to: " << mDefinition << " (" << clusterDefinitionString << ")"; diff --git a/PWGEM/PhotonMeson/Core/EMCPhotonCut.h b/PWGEM/PhotonMeson/Core/EMCPhotonCut.h index bba559ed8cb..1e93046b573 100644 --- a/PWGEM/PhotonMeson/Core/EMCPhotonCut.h +++ b/PWGEM/PhotonMeson/Core/EMCPhotonCut.h @@ -17,20 +17,20 @@ #define PWGEM_PHOTONMESON_CORE_EMCPHOTONCUT_H_ #include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" // IWYU pragma: keep #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include #include +#include #include #include #include -#include #include -#include - +#include #include #include #include @@ -63,7 +63,7 @@ concept IsNonLinContainer = o2::soa::is_table && requires(T t) { }; template -concept IsTrackIterator = o2::soa::is_iterator && requires(T t) { +concept IsFullTrackIterator = o2::soa::is_iterator && !o2::soa::is_filtered_iterator && requires(T t) { // Check that the *elements* of the container have the required methods: { t.deltaEta() } -> std::same_as; { t.deltaPhi() } -> std::same_as; @@ -72,7 +72,7 @@ concept IsTrackIterator = o2::soa::is_iterator && requires(T t) { }; template -concept IsTrackContainer = o2::soa::is_table && requires(T t) { +concept IsFullTrackContainer = o2::soa::is_table && o2::soa::is_not_filtered_table && requires(T t) { // Check that the *elements* of the container have the required methods: { t.begin().deltaEta() } -> std::same_as; { t.begin().deltaPhi() } -> std::same_as; @@ -108,11 +108,11 @@ struct TrackMatchingParams { float c{-2.5f}; }; -class EMCPhotonCut : public TNamed +class EMCPhotonCut { public: EMCPhotonCut() = default; - EMCPhotonCut(const char* name, const char* title) : TNamed(name, title) {} + EMCPhotonCut(const char* name, const char* title) : name(name), title(title) {} enum class EMCPhotonCuts : std::uint8_t { // cluster cut @@ -133,7 +133,10 @@ class EMCPhotonCut : public TNamed kSecondary, }; - static const char* mCutNames[static_cast(EMCPhotonCuts::kNCuts)]; + const std::string& getName() const { return name; } + const std::string& getTitle() const { return title; } + + static const std::array(EMCPhotonCut::EMCPhotonCuts::kNCuts)> mCutNames; static constexpr auto getClusterId(o2::soa::is_iterator auto const& t) { @@ -197,18 +200,18 @@ class EMCPhotonCut : public TNamed /// \brief performs check if track is matched with given cluster /// \param cluster cluster to be checked /// \param emcmatchedtrack matched track iterator - /// \param emcmatchedtrackEnd matched track end iterator + /// \param nMatchedTracks number of matched tracks /// \param GetEtaCut lambda to get the eta cut value /// \param GetPhiCut lambda to get the phi cut value /// \param applyEoverP bool to check if E/p should be checked (for secondaries we do not check this!) - bool checkTrackMatching(o2::soa::is_iterator auto const& cluster, IsTrackIterator auto& emcmatchedtrack, o2::soa::RowViewSentinel const emcmatchedtrackEnd, bool applyEoverP, auto GetEtaCut, auto GetPhiCut, o2::framework::HistogramRegistry* fRegistry = nullptr, TrackType trackType = TrackType::kPrimary) const + bool checkTrackMatching(o2::soa::is_iterator auto const& cluster, IsFullTrackIterator auto& emcmatchedtrack, int64_t const nMatchedTracks, bool applyEoverP, auto GetEtaCut, auto GetPhiCut, o2::framework::HistogramRegistry* fRegistry = nullptr, TrackType trackType = TrackType::kPrimary) const { // advance to cluster - while (emcmatchedtrack != emcmatchedtrackEnd && getClusterId(emcmatchedtrack) < cluster.globalIndex()) { + while (emcmatchedtrack.globalIndex() < nMatchedTracks && getClusterId(emcmatchedtrack) < cluster.globalIndex()) { ++emcmatchedtrack; } // all matched tracks have been checked - if (emcmatchedtrack == emcmatchedtrackEnd) { + if (emcmatchedtrack.globalIndex() >= nMatchedTracks) { return true; } // if all remaining tracks are beyond this cluster, it survives @@ -216,7 +219,7 @@ class EMCPhotonCut : public TNamed return true; } // iterate over tracks belonging to this cluster - while (emcmatchedtrack != emcmatchedtrackEnd && getClusterId(emcmatchedtrack) == cluster.globalIndex()) { + while (emcmatchedtrack.globalIndex() < nMatchedTracks && getClusterId(emcmatchedtrack) == cluster.globalIndex()) { const auto dEta = emcmatchedtrack.deltaEta(); const auto dPhi = emcmatchedtrack.deltaPhi(); const auto fdEta = std::fabs(emcmatchedtrack.deltaEta()); @@ -253,7 +256,7 @@ class EMCPhotonCut : public TNamed void fillBeforeClusterHistogram(o2::soa::is_iterator auto const& cluster, o2::framework::HistogramRegistry* fRegistry = nullptr) const { - if (mDoQA == false || fRegistry == nullptr) { + if (!mDoQA || fRegistry == nullptr) { return; } @@ -269,7 +272,7 @@ class EMCPhotonCut : public TNamed void fillAfterClusterHistogram(o2::soa::is_iterator auto const& cluster, o2::framework::HistogramRegistry* fRegistry = nullptr) const { - if (mDoQA == false || fRegistry == nullptr) { + if (!mDoQA || fRegistry == nullptr) { return; } @@ -285,18 +288,18 @@ class EMCPhotonCut : public TNamed /// \brief check if given clusters survives all cuts /// \param flags EMBitFlags where results will be stored /// \param cluster cluster table to check - /// \param matchedTracks matched primary tracks table - /// \param matchedSecondaries matched secondary tracks table + /// \param matchedTracks matched primary tracks table. HAS TO BE FULL TABLE! + /// \param matchedSecondaries matched secondary tracks table. HAS TO BE FULL TABLE! /// \param fRegistry o2::framework::HistogramRegistry pointer of the main task - void AreSelectedRunning(EMBitFlags& flags, o2::soa::is_table auto const& clusters, IsTrackContainer auto const& emcmatchedtracks, IsTrackContainer auto const& secondaries, o2::framework::HistogramRegistry* fRegistry = nullptr) const + void AreSelectedRunning(EMBitFlags& flags, o2::soa::is_table auto const& clusters, IsFullTrackContainer auto const& emcmatchedtracks, IsFullTrackContainer auto const& secondaries, o2::framework::HistogramRegistry* fRegistry = nullptr) const { if (clusters.size() <= 0) { return; } auto emcmatchedtrackIter = emcmatchedtracks.begin(); - auto emcmatchedtrackEnd = emcmatchedtracks.end(); + int64_t nMatchedTracks = emcmatchedtracks.size(); auto secondaryIter = secondaries.begin(); - auto secondaryEnd = secondaries.end(); + int64_t nSecondaryTracks = secondaries.size(); size_t iCluster = 0; const bool doQA = mDoQA && fRegistry != nullptr; @@ -308,7 +311,7 @@ class EMCPhotonCut : public TNamed if (doQA) { fillBeforeClusterHistogram(cluster, fRegistry); } - if (!IsSelectedRunning(cluster, emcmatchedtrackIter, emcmatchedtrackEnd, secondaryIter, secondaryEnd, fRegistry)) { + if (!IsSelectedRunning(cluster, emcmatchedtrackIter, nMatchedTracks, secondaryIter, nSecondaryTracks, fRegistry)) { flags.set(iCluster); } else if (doQA) { fillAfterClusterHistogram(cluster, fRegistry); @@ -328,11 +331,11 @@ class EMCPhotonCut : public TNamed /// \brief check if given cluster survives all cuts /// \param cluster cluster to check /// \param emcmatchedtrackIter current iterator of matched primary tracks - /// \param emcmatchedtrackEnd end iterator of matched primary tracks + /// \param nMatchedTracks number of matched primary tracks /// \param secondaryIter current iterator of matched secondary tracks - /// \param secondaryEnd end iterator of matched secondary tracks + /// \param nSecondaryTracks number of matched secondary tracks /// \return true if cluster survives all cuts else false - bool IsSelectedRunning(o2::soa::is_iterator auto const& cluster, IsTrackIterator auto& emcmatchedtrackIter, o2::soa::RowViewSentinel const emcmatchedtrackEnd, IsTrackIterator auto& secondaryIter, o2::soa::RowViewSentinel const secondaryEnd, o2::framework::HistogramRegistry* fRegistry = nullptr) const + bool IsSelectedRunning(o2::soa::is_iterator auto const& cluster, IsFullTrackIterator auto& emcmatchedtrackIter, int64_t const nMatchedTracks, IsFullTrackIterator auto& secondaryIter, int64_t const nSecondaryTracks, o2::framework::HistogramRegistry* fRegistry = nullptr) const { const bool doQA = mDoQA && fRegistry != nullptr; if (!IsSelectedEMCalRunning(EMCPhotonCuts::kDefinition, cluster)) { @@ -365,13 +368,13 @@ class EMCPhotonCut : public TNamed } return false; } - if (mUseTM && (!IsSelectedEMCalRunning(EMCPhotonCuts::kTM, cluster, emcmatchedtrackIter, emcmatchedtrackEnd, fRegistry))) { + if (mUseTM && (!IsSelectedEMCalRunning(EMCPhotonCuts::kTM, cluster, emcmatchedtrackIter, nMatchedTracks, fRegistry))) { if (doQA) { fRegistry->fill(HIST("QA/Cluster/hClusterQualityCuts"), static_cast(EMCPhotonCuts::kTM) + 1, cluster.e()); } return false; } - if (mUseSecondaryTM && (!IsSelectedEMCalRunning(EMCPhotonCuts::kSecondaryTM, cluster, secondaryIter, secondaryEnd, fRegistry))) { + if (mUseSecondaryTM && (!IsSelectedEMCalRunning(EMCPhotonCuts::kSecondaryTM, cluster, secondaryIter, nSecondaryTracks, fRegistry))) { if (doQA) { fRegistry->fill(HIST("QA/Cluster/hClusterQualityCuts"), static_cast(EMCPhotonCuts::kSecondaryTM) + 1, cluster.e()); } @@ -421,8 +424,6 @@ class EMCPhotonCut : public TNamed return mMinTime <= cluster.time() && cluster.time() <= mMaxTime; case EMCPhotonCuts::kTM: - return false; - case EMCPhotonCuts::kSecondaryTM: return false; @@ -438,16 +439,20 @@ class EMCPhotonCut : public TNamed /// \param cut enum of the cluster cut to check /// \param cluster cluster to check /// \param matchedTrackIter current iterator of matched primary or secondary tracks - /// \param matchedTrackEnd end iterator of matched primary or secondary tracks + /// \param nMatchedTracks number of matched primary or secondary tracks /// \return true if cluster survives cut else false - bool IsSelectedEMCalRunning(const EMCPhotonCuts& cut, o2::soa::is_iterator auto const& cluster, IsTrackIterator auto& matchedTrackIter, o2::soa::RowViewSentinel const matchedTrackEnd, o2::framework::HistogramRegistry* fRegistry = nullptr) const + bool IsSelectedEMCalRunning(const EMCPhotonCuts& cut, o2::soa::is_iterator auto const& cluster, IsFullTrackIterator auto& matchedTrackIter, int64_t const nMatchedTracks, o2::framework::HistogramRegistry* fRegistry = nullptr) const { + if (nMatchedTracks == 0) { + // there are not tracks to match with, so its true + return true; + } switch (cut) { case EMCPhotonCuts::kTM: - return checkTrackMatching(cluster, matchedTrackIter, matchedTrackEnd, true, [this](float pt) { return GetTrackMatchingEta(pt); }, [this](float pt) { return GetTrackMatchingPhi(pt); }, fRegistry, TrackType::kPrimary); + return checkTrackMatching(cluster, matchedTrackIter, nMatchedTracks, true, [this](float pt) { return GetTrackMatchingEta(pt); }, [this](float pt) { return GetTrackMatchingPhi(pt); }, fRegistry, TrackType::kPrimary); case EMCPhotonCuts::kSecondaryTM: - return checkTrackMatching(cluster, matchedTrackIter, matchedTrackEnd, false, [this](float pt) { return GetSecTrackMatchingEta(pt); }, [this](float pt) { return GetSecTrackMatchingPhi(pt); }, fRegistry, TrackType::kSecondary); + return checkTrackMatching(cluster, matchedTrackIter, nMatchedTracks, false, [this](float pt) { return GetSecTrackMatchingEta(pt); }, [this](float pt) { return GetSecTrackMatchingPhi(pt); }, fRegistry, TrackType::kSecondary); default: return true; @@ -519,7 +524,7 @@ class EMCPhotonCut : public TNamed return mMinTime <= cluster.time() && cluster.time() <= mMaxTime; case EMCPhotonCuts::kTM: { - if constexpr (IsTrackContainer) { + if constexpr (IsFullTrackContainer) { for (const auto& emcmatchedtrack : emcmatchedtracks) { auto dEta = std::fabs(emcmatchedtrack.deltaEta()); auto dPhi = std::fabs(emcmatchedtrack.deltaPhi()); @@ -552,7 +557,7 @@ class EMCPhotonCut : public TNamed return true; // when we don't have any tracks the cluster should always survive the TM cut! } case EMCPhotonCuts::kSecondaryTM: { - if constexpr (IsTrackContainer) { + if constexpr (IsFullTrackContainer) { for (const auto& emcmatchedtrack : emcmatchedtracks) { auto dEta = std::fabs(emcmatchedtrack.deltaEta()); auto dPhi = std::fabs(emcmatchedtrack.deltaPhi()); @@ -593,7 +598,7 @@ class EMCPhotonCut : public TNamed // Setters /// \brief Set clusterizer /// \param clusterDefinitionString name of the clusterizer - void SetClusterizer(std::string clusterDefinitionString = "kV3Default"); + void SetClusterizer(const std::string& clusterDefinitionString = "kV3Default"); /// \brief Set minimum cluster energy /// \param min minimum cluster energy @@ -639,7 +644,7 @@ class EMCPhotonCut : public TNamed /// \param c c in a + (pT + b)^c void SetTrackMatchingEtaParams(float a, float b, float c) { - mTrackMatchingEtaParams = {a, b, c}; + mTrackMatchingEtaParams = {.a = a, .b = b, .c = c}; } /// \brief Set parameters for track matching delta phi = a + (pT + b)^c @@ -648,7 +653,7 @@ class EMCPhotonCut : public TNamed /// \param c c in a + (pT + b)^c void SetTrackMatchingPhiParams(float a, float b, float c) { - mTrackMatchingPhiParams = {a, b, c}; + mTrackMatchingPhiParams = {.a = a, .b = b, .c = c}; } /// \brief Set parameters for secondary track matching delta eta = a + (pT + b)^c @@ -657,7 +662,7 @@ class EMCPhotonCut : public TNamed /// \param c c in a + (pT + b)^c void SetSecTrackMatchingEtaParams(float a, float b, float c) { - mSecTrackMatchingEtaParams = {a, b, c}; + mSecTrackMatchingEtaParams = {.a = a, .b = b, .c = c}; } /// \brief Set parameters for secondary track matching delta phi = a + (pT + b)^c @@ -666,7 +671,7 @@ class EMCPhotonCut : public TNamed /// \param c c in a + (pT + b)^c void SetSecTrackMatchingPhiParams(float a, float b, float c) { - mSecTrackMatchingPhiParams = {a, b, c}; + mSecTrackMatchingPhiParams = {.a = a, .b = b, .c = c}; } /// \brief calculate delta eta for track matching at given track pT @@ -701,6 +706,8 @@ class EMCPhotonCut : public TNamed void print() const; private: + std::string name; + std::string title; // EMCal cluster cuts int mDefinition{10}; ///< clusterizer definition float mMinE{0.7f}; ///< minimum energy @@ -719,12 +726,10 @@ class EMCPhotonCut : public TNamed mutable uint nAccClusterPerColl{0}; ///< running number of accepted cluster per collision used for QA mutable int currentCollID{-1}; ///< running collision ID of clusters used for QA - TrackMatchingParams mTrackMatchingEtaParams = {-1.f, 0.f, 0.f}; - TrackMatchingParams mTrackMatchingPhiParams = {-1.f, 0.f, 0.f}; - TrackMatchingParams mSecTrackMatchingEtaParams = {-1.f, 0.f, 0.f}; - TrackMatchingParams mSecTrackMatchingPhiParams = {-1.f, 0.f, 0.f}; - - ClassDef(EMCPhotonCut, 3); + TrackMatchingParams mTrackMatchingEtaParams = {.a = -1.f, .b = 0.f, .c = 0.f}; + TrackMatchingParams mTrackMatchingPhiParams = {.a = -1.f, .b = 0.f, .c = 0.f}; + TrackMatchingParams mSecTrackMatchingEtaParams = {.a = -1.f, .b = 0.f, .c = 0.f}; + TrackMatchingParams mSecTrackMatchingPhiParams = {.a = -1.f, .b = 0.f, .c = 0.f}; }; #endif // PWGEM_PHOTONMESON_CORE_EMCPHOTONCUT_H_ diff --git a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h index 5b14aa8d2ef..26b1f2c9490 100644 --- a/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h +++ b/PWGEM/PhotonMeson/Core/EMPhotonEventCut.h @@ -19,15 +19,13 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" -#include +#include -#include - -class EMPhotonEventCut : public TNamed +class EMPhotonEventCut { public: EMPhotonEventCut() = default; - EMPhotonEventCut(const char* name, const char* title) : TNamed(name, title) {} + EMPhotonEventCut(const char* name, const char* title) : name(name), title(title) {} enum class EMPhotonEventCuts : int { kSel8 = 0, @@ -52,6 +50,9 @@ class EMPhotonEventCut : public TNamed kNCuts }; + [[nodiscard]] const std::string& getName() const { return name; } + [[nodiscard]] const std::string& getTitle() const { return title; } + template bool IsSelected(T const& collision) const { @@ -203,6 +204,8 @@ class EMPhotonEventCut : public TNamed void SetRequireEMCHardwareTriggered(bool flag); private: + std::string name; + std::string title; bool mRequireSel8{false}; bool mRequireFT0AND{true}; float mMinZvtx{-10.f}, mMaxZvtx{+10.f}; @@ -222,8 +225,6 @@ class EMPhotonEventCut : public TNamed bool mRequireGoodITSLayersAll{false}; bool mRequireEMCReadoutInMB{false}; bool mRequireEMCHardwareTriggered{false}; - - ClassDef(EMPhotonEventCut, 1); }; #endif // PWGEM_PHOTONMESON_CORE_EMPHOTONEVENTCUT_H_ diff --git a/PWGEM/PhotonMeson/Core/EmMlResponseEMCConversion.h b/PWGEM/PhotonMeson/Core/EmMlResponseEMCConversion.h new file mode 100644 index 00000000000..3565a6b82bd --- /dev/null +++ b/PWGEM/PhotonMeson/Core/EmMlResponseEMCConversion.h @@ -0,0 +1,118 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file EmMlResponseEMCConversion.h +/// \brief Class to compute the ML response for EMC conversion selections +/// \author Marvin Hemmer + +#ifndef PWGEM_PHOTONMESON_CORE_EMMLRESPONSEEMCCONVERSION_H_ +#define PWGEM_PHOTONMESON_CORE_EMMLRESPONSEEMCCONVERSION_H_ + +#include "PWGEM/PhotonMeson/Core/EMCConversionCandidate.h" + +#include "Tools/ML/MlResponse.h" + +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_EMC_CONV(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesEMCConversion::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER from OBJECT +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define CHECK_AND_FILL_VEC_EMC_CONV(GETTER) \ + case static_cast(InputFeaturesEMCConversion::GETTER): { \ + inputFeatures.emplace_back(candidate.GETTER()); \ + break; \ + } + +namespace o2::analysis::em::emcconv +{ + +// input feature used in the ml model +enum class InputFeaturesEMCConversion : uint8_t { + minv, + deltaEta, + deltaR, + phiv, + rConv, + totE, + e2, + e1, + deltaPhi, + harmonicEt, + m021, + m022, + time1, + time2, + ncell1, + ncell2 +}; + +template +class EmMlResponseEMCConversion : public MlResponse +{ + public: + EmMlResponseEMCConversion() = default; + virtual ~EmMlResponseEMCConversion() = default; + + template + void getInputFeatures(TCandidate const& candidate, std::vector& inputFeatures) + { + inputFeatures.clear(); + for (const auto& idx : MlResponse::mCachedIndices) { + switch (idx) { + CHECK_AND_FILL_VEC_EMC_CONV(minv) + CHECK_AND_FILL_VEC_EMC_CONV(deltaEta) + CHECK_AND_FILL_VEC_EMC_CONV(deltaR) + CHECK_AND_FILL_VEC_EMC_CONV(phiv) + CHECK_AND_FILL_VEC_EMC_CONV(rConv) + CHECK_AND_FILL_VEC_EMC_CONV(totE) + CHECK_AND_FILL_VEC_EMC_CONV(e2) + CHECK_AND_FILL_VEC_EMC_CONV(e1) + CHECK_AND_FILL_VEC_EMC_CONV(deltaPhi) + CHECK_AND_FILL_VEC_EMC_CONV(harmonicEt) + CHECK_AND_FILL_VEC_EMC_CONV(m021) + CHECK_AND_FILL_VEC_EMC_CONV(m022) + CHECK_AND_FILL_VEC_EMC_CONV(time1) + CHECK_AND_FILL_VEC_EMC_CONV(time2) + CHECK_AND_FILL_VEC_EMC_CONV(ncell1) + CHECK_AND_FILL_VEC_EMC_CONV(ncell2) + } + } + } + + protected: + void setAvailableInputFeatures() + { + MlResponse::mAvailableInputFeatures = { + FILL_MAP_EMC_CONV(minv), FILL_MAP_EMC_CONV(deltaEta), FILL_MAP_EMC_CONV(deltaR), + FILL_MAP_EMC_CONV(phiv), FILL_MAP_EMC_CONV(rConv), FILL_MAP_EMC_CONV(totE), + FILL_MAP_EMC_CONV(e2), FILL_MAP_EMC_CONV(e1), FILL_MAP_EMC_CONV(deltaPhi), + FILL_MAP_EMC_CONV(harmonicEt), FILL_MAP_EMC_CONV(m021), FILL_MAP_EMC_CONV(m022), + FILL_MAP_EMC_CONV(time1), FILL_MAP_EMC_CONV(time2), FILL_MAP_EMC_CONV(ncell1), + FILL_MAP_EMC_CONV(ncell2)}; + } +}; + +} // namespace o2::analysis::em::emcconv + +#undef FILL_MAP_EMC_CONV +#undef CHECK_AND_FILL_VEC_EMC_CONV + +#endif // PWGEM_PHOTONMESON_CORE_EMMLRESPONSEEMCCONVERSION_H_ diff --git a/PWGEM/PhotonMeson/Core/EmMlResponsePCM.h b/PWGEM/PhotonMeson/Core/EmMlResponsePCM.h index 7a908cbf4b9..ffe07edef01 100644 --- a/PWGEM/PhotonMeson/Core/EmMlResponsePCM.h +++ b/PWGEM/PhotonMeson/Core/EmMlResponsePCM.h @@ -35,14 +35,16 @@ // matches the entry in EnumInputFeatures associated to this FEATURE // if so, the inputFeatures vector is filled with the FEATURE's value // by calling the corresponding GETTER from OBJECT +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define CHECK_AND_FILL_VEC_PCM_FULL(OBJECT, FEATURE, GETTER) \ case static_cast(InputFeaturesPCM::FEATURE): { \ - inputFeatures.emplace_back(OBJECT.GETTER()); \ + inputFeatures.emplace_back((OBJECT).GETTER()); \ break; \ } // Specific case of CHECK_AND_FILL_VEC_PCM_FULL(OBJECT, FEATURE, GETTER) // where OBJECT is named candidate and FEATURE = GETTER +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define CHECK_AND_FILL_VEC_PCM(GETTER) \ case static_cast(InputFeaturesPCM::GETTER): { \ inputFeatures.emplace_back(candidate.GETTER()); \ @@ -52,6 +54,11 @@ namespace o2::analysis { +enum NClassesML { + kTwo = 2, + kThree +}; + enum class InputFeaturesPCM : uint8_t { v0PhotonCandidatefVx, v0PhotonCandidatefVy, diff --git a/PWGEM/PhotonMeson/Core/PHOSPhotonCut.cxx b/PWGEM/PhotonMeson/Core/PHOSPhotonCut.cxx index 1fc60c9e30f..2170f15b4e8 100644 --- a/PWGEM/PhotonMeson/Core/PHOSPhotonCut.cxx +++ b/PWGEM/PhotonMeson/Core/PHOSPhotonCut.cxx @@ -17,11 +17,10 @@ #include -#include +#include +#include -ClassImp(PHOSPhotonCut); - -const char* PHOSPhotonCut::mCutNames[static_cast(PHOSPhotonCut::PHOSPhotonCuts::kNCuts)] = {"Energy", "Dispersion", "CPV"}; +const std::array(PHOSPhotonCut::PHOSPhotonCuts::kNCuts)> PHOSPhotonCut::mCutNames = {"Energy", "Dispersion", "CPV"}; void PHOSPhotonCut::SetEnergyRange(float min, float max) { diff --git a/PWGEM/PhotonMeson/Core/PHOSPhotonCut.h b/PWGEM/PhotonMeson/Core/PHOSPhotonCut.h index ea7fd3440ab..1fd7e753a6f 100644 --- a/PWGEM/PhotonMeson/Core/PHOSPhotonCut.h +++ b/PWGEM/PhotonMeson/Core/PHOSPhotonCut.h @@ -16,17 +16,17 @@ #ifndef PWGEM_PHOTONMESON_CORE_PHOSPHOTONCUT_H_ #define PWGEM_PHOTONMESON_CORE_PHOSPHOTONCUT_H_ -#include +#include -#include +#include +#include +#include -#include - -class PHOSPhotonCut : public TNamed +class PHOSPhotonCut { public: PHOSPhotonCut() = default; - PHOSPhotonCut(const char* name, const char* title) : TNamed(name, title) {} + PHOSPhotonCut(const char* name, const char* title) : name(name), title(title) {} enum class PHOSPhotonCuts : int { kEnergy = 0, @@ -35,7 +35,10 @@ class PHOSPhotonCut : public TNamed kNCuts }; - static const char* mCutNames[static_cast(PHOSPhotonCuts::kNCuts)]; + [[nodiscard]] const std::string& getName() const { return name; } + [[nodiscard]] const std::string& getTitle() const { return title; } + + static const std::array(PHOSPhotonCuts::kNCuts)> mCutNames; // Temporary function to check if track passes selection criteria. To be replaced by framework filters. template @@ -53,10 +56,10 @@ class PHOSPhotonCut : public TNamed } // only temporary solution to avoid noisy channels. - if (-1.20 + 10.2 * sqrt(cluster.e()) < cluster.nCells()) { + if (-1.20 + 10.2 * std::sqrt(cluster.e()) < cluster.nCells()) { return false; } - if (cluster.nCells() < -3.04 + 3.14 * sqrt(cluster.e())) { + if (cluster.nCells() < -3.04 + 3.14 * std::sqrt(cluster.e())) { return false; } @@ -79,8 +82,6 @@ class PHOSPhotonCut : public TNamed return cls.e() >= mMinEnergy && cls.e() <= mMaxEnergy; case PHOSPhotonCuts::kDispersion: - return true; - case PHOSPhotonCuts::kCPV: return true; @@ -96,9 +97,9 @@ class PHOSPhotonCut : public TNamed void print() const; private: + std::string name; + std::string title; float mMinEnergy{0.1f}, mMaxEnergy{1e+10f}; - - ClassDef(PHOSPhotonCut, 2); }; #endif // PWGEM_PHOTONMESON_CORE_PHOSPHOTONCUT_H_ diff --git a/PWGEM/PhotonMeson/Core/PWGEMPhotonMesonCoreLinkDef.h b/PWGEM/PhotonMeson/Core/PWGEMPhotonMesonCoreLinkDef.h deleted file mode 100644 index 2a1a0ff7226..00000000000 --- a/PWGEM/PhotonMeson/Core/PWGEMPhotonMesonCoreLinkDef.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef PWGEM_PHOTONMESON_CORE_PWGEMPHOTONMESONCORELINKDEF_H_ -#define PWGEM_PHOTONMESON_CORE_PWGEMPHOTONMESONCORELINKDEF_H_ - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class V0PhotonCut + ; -#pragma link C++ class DalitzEECut + ; -#pragma link C++ class PHOSPhotonCut + ; -#pragma link C++ class EMCPhotonCut + ; -#pragma link C++ class EMPhotonEventCut + ; -#pragma link C++ class PairCut + ; -#pragma link C++ class EMBitFlags + ; - -#endif // PWGEM_PHOTONMESON_CORE_PWGEMPHOTONMESONCORELINKDEF_H_ diff --git a/PWGEM/PhotonMeson/Core/PairCut.cxx b/PWGEM/PhotonMeson/Core/PairCut.cxx index 5bf8273cb39..24f50ef2c23 100644 --- a/PWGEM/PhotonMeson/Core/PairCut.cxx +++ b/PWGEM/PhotonMeson/Core/PairCut.cxx @@ -17,11 +17,11 @@ #include -#include +#include +#include +#include -ClassImp(PairCut); - -const char* PairCut::mCutNames[static_cast(PairCut::PairCuts::kNCuts)] = {"Asym"}; +const std::array(PairCut::PairCuts::kNCuts)> PairCut::mCutNames = {"Asym"}; void PairCut::SetAsymRange(float min, float max) { diff --git a/PWGEM/PhotonMeson/Core/PairCut.h b/PWGEM/PhotonMeson/Core/PairCut.h index f30f7dae7a9..aa03cb19ed8 100644 --- a/PWGEM/PhotonMeson/Core/PairCut.h +++ b/PWGEM/PhotonMeson/Core/PairCut.h @@ -16,15 +16,16 @@ #ifndef PWGEM_PHOTONMESON_CORE_PAIRCUT_H_ #define PWGEM_PHOTONMESON_CORE_PAIRCUT_H_ -#include +#include +#include +#include +#include -#include - -class PairCut : public TNamed +class PairCut { public: PairCut() = default; - PairCut(const char* name, const char* title) : TNamed(name, title) {} + PairCut(const char* name, const char* title) : name(name), title(title) {} enum class PairCuts : int { // v0 cut @@ -32,16 +33,15 @@ class PairCut : public TNamed kNCuts }; - static const char* mCutNames[static_cast(PairCuts::kNCuts)]; + [[nodiscard]] const std::string& getName() const { return name; } + [[nodiscard]] const std::string& getTitle() const { return title; } + + static const std::array(PairCuts::kNCuts)> mCutNames; template bool IsSelected(G1 const& g1, G2 const& g2) const { - if (!IsSelectedPair(g1, g2, PairCuts::kAsym)) { - return false; - } - - return true; + return IsSelectedPair(g1, g2, PairCuts::kAsym); } // template @@ -50,7 +50,7 @@ class PairCut : public TNamed { switch (cut) { case PairCuts::kAsym: { - float asym = abs(g1.e() - g2.e()) / (g1.e() + g2.e()); + float asym = std::abs(g1.e() - g2.e()) / (g1.e() + g2.e()); // float asym = abs(g1.p() - g2.p()) / (g1.p() + g2.p()); return mMinAsym < asym && asym < mMaxAsym; } @@ -66,9 +66,9 @@ class PairCut : public TNamed void print() const; private: + std::string name; + std::string title; float mMinAsym{-1e+10}, mMaxAsym{1e+10}; - - ClassDef(PairCut, 1); }; #endif // PWGEM_PHOTONMESON_CORE_PAIRCUT_H_ diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h index e09acb54761..22ee95e1bc3 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h @@ -23,6 +23,7 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCandidate.h" #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/EMPhoton.h" #include "PWGEM/PhotonMeson/Utils/EventHistograms.h" @@ -40,13 +41,15 @@ #include #include #include -#include +#include #include #include +#include #include #include #include #include +#include #include #include #include @@ -60,6 +63,7 @@ #include #include +#include #include #include #include @@ -79,9 +83,6 @@ enum AlphaMesonCutOption { template struct Pi0EtaToGammaGamma { o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; @@ -97,12 +98,45 @@ struct Pi0EtaToGammaGamma { o2::framework::ConfigurableAxis ConfCentBins{"ConfCentBins", {o2::framework::VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f, 999.f}, "Mixing bins - centrality"}; o2::framework::ConfigurableAxis ConfEPBins{"ConfEPBins", {o2::framework::VARIABLE_WIDTH, -o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0.0f, +o2::constants::math::PIQuarter, +o2::constants::math::PIHalf}, "Mixing bins - event plane angle"}; o2::framework::ConfigurableAxis ConfOccupancyBins{"ConfOccupancyBins", {o2::framework::VARIABLE_WIDTH, -1, 1e+10}, "Mixing bins - occupancy"}; + o2::framework::Configurable cfgSparseFullAxes{"cfgSparseFullAxes", false, "add event plane, occupancy and z-vertex axes to Pair/same/hs and Pair/mix/hs"}; + o2::framework::Configurable cfgNbinsMass{"cfgNbinsMass", 400, "number of bins for the mass axis in same and mixed events"}; + o2::framework::Configurable> cfgMassBinRange{"cfgMassBinRange", {0., 0.8}, "range for the mass-axis in GeV/c2"}; o2::framework::Configurable cfgAlphaMesonCut{"cfgAlphaMesonCut", 0, "flag for photon energy asymmetry distribution cut: 0: no cut, 1: cut specific value, 2: cut depending on pT"}; o2::framework::Configurable cfgAlphaMeson{"cfgAlphaMeson", 0.65, "photon energy asymmetry distribution parameter for specific value cut"}; o2::framework::Configurable cfgAlphaMesonA{"cfgAlphaMesonA", 0.65, "photon energy asymmetry distribution parameter A for pT dependent cut (A * tanh(B*pT))"}; o2::framework::Configurable cfgAlphaMesonB{"cfgAlphaMesonB", 1.2, "photon energy asymmetry distribution parameter B for pT dependent cut (A * tanh(B*pT))"}; + o2::framework::Configurable cfgDoPhotonClassPairCut{"cfgDoPhotonClassPairCut", false, "apply photon-class pair selection A x B (leg track composition, PCM-PCM only)"}; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "photonclassA_group"; + o2::framework::Configurable cfgMinNLegsITSTPC{"cfgMinNLegsITSTPC", 0, "min number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSTPC{"cfgMaxNLegsITSTPC", 2, "max number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsITSOnly{"cfgMinNLegsITSOnly", 0, "min number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSOnly{"cfgMaxNLegsITSOnly", 2, "max number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTPCOnly{"cfgMinNLegsTPCOnly", 0, "min number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTPCOnly{"cfgMaxNLegsTPCOnly", 2, "max number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTRD{"cfgMinNLegsTRD", 0, "min number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTRD{"cfgMaxNLegsTRD", 2, "max number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMinNLegsTOF{"cfgMinNLegsTOF", 0, "min number of legs with TOF (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTOF{"cfgMaxNLegsTOF", 2, "max number of legs with TOF (0-2)"}; + } photonclassA; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "photonclassB_group"; + o2::framework::Configurable cfgMinNLegsITSTPC{"cfgMinNLegsITSTPC", 0, "min number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSTPC{"cfgMaxNLegsITSTPC", 2, "max number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsITSOnly{"cfgMinNLegsITSOnly", 0, "min number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSOnly{"cfgMaxNLegsITSOnly", 2, "max number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTPCOnly{"cfgMinNLegsTPCOnly", 0, "min number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTPCOnly{"cfgMaxNLegsTPCOnly", 2, "max number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTRD{"cfgMinNLegsTRD", 0, "min number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTRD{"cfgMaxNLegsTRD", 2, "max number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMinNLegsTOF{"cfgMinNLegsTOF", 0, "min number of legs with TOF (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTOF{"cfgMaxNLegsTOF", 2, "max number of legs with TOF (0-2)"}; + } photonclassB; + EMPhotonEventCut fEMEventCut; struct : o2::framework::ConfigurableGroup { std::string prefix = "eventcut_group"; @@ -136,6 +170,8 @@ struct Pi0EtaToGammaGamma { o2::framework::Configurable cfg_max_eta_v0{"cfg_max_eta_v0", 0.8, "max eta for v0 photons at PV"}; o2::framework::Configurable cfg_min_v0radius{"cfg_min_v0radius", 4.0, "min v0 radius"}; o2::framework::Configurable cfg_max_v0radius{"cfg_max_v0radius", 90.0, "max v0 radius"}; + o2::framework::Configurable cfg_midL_v0radius{"cfg_midL_v0radius", -1.0, "middle low v0 radius for rejection if >0"}; + o2::framework::Configurable cfg_midH_v0radius{"cfg_midH_v0radius", -1.0, "middle high v0 radius for rejection if >0"}; o2::framework::Configurable cfg_max_alpha_ap{"cfg_max_alpha_ap", 0.95, "max alpha for AP cut"}; o2::framework::Configurable cfg_max_qt_ap{"cfg_max_qt_ap", 0.01, "max qT for AP cut"}; o2::framework::Configurable cfg_min_cospa{"cfg_min_cospa", 0.999, "min V0 CosPA"}; @@ -238,13 +274,13 @@ struct Pi0EtaToGammaGamma { //--------------------------------------------------------------------------- // Preslices and partitions o2::framework::SliceCache cache; - o2::framework::PresliceOptional>> perCollision_pcm = o2::aod::v0photonkf::pmeventId; - o2::framework::PresliceOptional> perCollision_emc = o2::aod::emccluster::pmeventId; - o2::framework::PresliceOptional> perCollision_phos = o2::aod::phoscluster::pmeventId; - o2::framework::PresliceOptional>> perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; + o2::framework::PresliceOptional perCollision_pcm = o2::aod::v0photonkf::pmeventId; + o2::framework::PresliceOptional perCollision_emc = o2::aod::emccluster::pmeventId; + o2::framework::PresliceOptional perCollision_phos = o2::aod::phoscluster::pmeventId; + o2::framework::PresliceOptional perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; - o2::framework::PresliceOptional perEMCClusterMT = o2::aod::trackmatching::emEmcClusterId; - o2::framework::PresliceOptional perEMCClusterMS = o2::aod::trackmatching::emEmcClusterId; + o2::framework::PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + o2::framework::PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; @@ -263,10 +299,10 @@ struct Pi0EtaToGammaGamma { std::vector occ_bin_edges; o2::ccdb::CcdbApi ccdbApi; - o2::framework::Service ccdb; - int mRunNumber; - float d_bz; - o2::emcal::Geometry* emcalGeom; + o2::framework::Service ccdb{}; + int mRunNumber = -1; + float d_bz = 0; + o2::emcal::Geometry* emcalGeom = nullptr; //--------------------------------------------------------------------------- // In the following are tags defined which help to select the correct preslice and cuts @@ -301,13 +337,13 @@ struct Pi0EtaToGammaGamma { static auto& perClusterMT() { - static auto slice{o2::aod::trackmatching::emEmcClusterId}; + static auto slice{o2::aod::mintm::minClusterId}; return slice; } static auto& perClusterMS() { - static auto slice{o2::aod::trackmatching::emEmcClusterId}; + static auto slice{o2::aod::mintm::minClusterId}; return slice; } @@ -368,6 +404,9 @@ struct Pi0EtaToGammaGamma { } }; + o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonClassSelection mPhotonClassSelA{}; + o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonClassSelection mPhotonClassSelB{}; + void init(o2::framework::InitContext&) { zvtx_bin_edges = std::vector(ConfVtxBins.value.begin(), ConfVtxBins.value.end()); @@ -387,13 +426,19 @@ struct Pi0EtaToGammaGamma { emh2 = new o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, o2::aod::pwgem::photonmeson::utils::EMPhoton>(ndepth); o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + std::vector occBinsForHs(occ_bin_edges.begin(), occ_bin_edges.end()); + std::vector epBinsForHs(ep_bin_edges.begin(), ep_bin_edges.end()); + std::vector vtxBinsForHs(zvtx_bin_edges.begin(), zvtx_bin_edges.end()); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "ee#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, false, "#gamma#gamma", cfgSparseFullAxes.value, occBinsForHs, epBinsForHs, vtxBinsForHs, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } DefineEMEventCut(); DefinePCMCut(); + + mPhotonClassSelA = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassA); + mPhotonClassSelB = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassB); DefineDileptonCut(); DefineEMCCut(); DefinePHOSCut(); @@ -413,52 +458,45 @@ struct Pi0EtaToGammaGamma { ccdb->setFatalWhenNull(false); } - template + template void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { return; } + mRunNumber = collision.runNumber(); // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { + if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) d_bz = d_bz_input; o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) } - mRunNumber = collision.runNumber(); + o2::base::Propagator::initFieldFromGRP(&grpmag); return; } - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + // o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // Fetch magnetic field from ccdb for current collision + // d_bz = collision.grpMagField().getNominalL3Field(); + + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); } + d_bz = grpmag->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + fV0PhotonCut.SetD_Bz(d_bz); - mRunNumber = collision.runNumber(); } ~Pi0EtaToGammaGamma() { delete emh1; - emh1 = 0x0; + emh1 = nullptr; delete emh2; - emh2 = 0x0; + emh2 = nullptr; used_photonIds_per_col.clear(); used_photonIds_per_col.shrink_to_fit(); @@ -492,7 +530,7 @@ struct Pi0EtaToGammaGamma { fV0PhotonCut.SetMinCosPA(pcmcuts.cfg_min_cospa); fV0PhotonCut.SetMaxPCA(pcmcuts.cfg_max_pca); fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfg_max_chi2kf); - fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius); + fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius, pcmcuts.cfg_midL_v0radius, pcmcuts.cfg_midH_v0radius); fV0PhotonCut.SetAPRange(pcmcuts.cfg_max_alpha_ap, pcmcuts.cfg_max_qt_ap); fV0PhotonCut.RejectITSib(pcmcuts.cfg_reject_v0_on_itsib); @@ -519,8 +557,7 @@ struct Pi0EtaToGammaGamma { fV0PhotonCut.SetNClassesMl(pcmcuts.cfg_nclasses_ml); fV0PhotonCut.SetMlTimestampCCDB(pcmcuts.cfg_timestamp_ccdb); fV0PhotonCut.SetCcdbUrl(ccdburl); - CentType mCentralityTypeMlEnum; - mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); + auto mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); fV0PhotonCut.SetCentralityTypeMl(mCentralityTypeMlEnum); fV0PhotonCut.SetCutDirMl(pcmcuts.cfg_cut_dir_ml); fV0PhotonCut.SetMlModelPathsCCDB(pcmcuts.cfg_model_paths_ccdb); @@ -614,9 +651,7 @@ struct Pi0EtaToGammaGamma { int iRowLast = 24; if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_HALF) { iRowLast /= 2; // 2/3 sm case - } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_THIRD) { - iRowLast /= 3; // 1/3 sm case - } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::DCAL_EXT) { + } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_THIRD || emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::DCAL_EXT) { iRowLast /= 3; // 1/3 sm case } @@ -631,7 +666,7 @@ struct Pi0EtaToGammaGamma { } /// \brief Calculate background (using rotation background method only for EMCal!) - template + template void RotationBackground(const ROOT::Math::PtEtaPhiMVector& meson, ROOT::Math::PtEtaPhiMVector photon1, ROOT::Math::PtEtaPhiMVector photon2, TPhotons const& photons_coll, unsigned int ig1, unsigned int ig2, float eventWeight) { // if less than 3 clusters are present skip event since we need at least 3 clusters @@ -690,7 +725,6 @@ struct Pi0EtaToGammaGamma { fRegistry.fill(HIST("Pair/rotation/hs"), mother2.M(), mother2.Pt(), eventWeight); } } - return; } /// \brief function to run the photon pairing @@ -729,7 +763,7 @@ struct Pi0EtaToGammaGamma { continue; } - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; fV0PhotonCut.SetCentrality(centralities[cfgCentEstimator]); if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; @@ -767,9 +801,7 @@ struct Pi0EtaToGammaGamma { } int occbin = -1; - if (cfgOccupancyEstimator == 0) { - occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; - } else if (cfgOccupancyEstimator == 1) { + if (cfgOccupancyEstimator == 1) { occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.trackOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; } else { occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; @@ -781,6 +813,8 @@ struct Pi0EtaToGammaGamma { occbin = static_cast(occ_bin_edges.size()) - 2; } + float occupancy = (cfgOccupancyEstimator == 1) ? static_cast(collision.trackOccupancyInTimeRange()) : collision.ft0cOccupancyInTimeRange(); + // LOGF(info, "collision.globalIndex() = %d, collision.posZ() = %f, centrality = %f, ep2 = %f, collision.trackOccupancyInTimeRange() = %d, zbin = %d, centbin = %d, epbin = %d, occbin = %d", collision.globalIndex(), collision.posZ(), centrality, ep2, collision.trackOccupancyInTimeRange(), zbin, centbin, epbin, occbin); std::tuple key_bin = std::make_tuple(zbin, centbin, epbin, occbin); @@ -801,6 +835,7 @@ struct Pi0EtaToGammaGamma { continue; } } + auto pos1 = g1.template posTrack_as(); auto ele1 = g1.template negTrack_as(); ROOT::Math::PtEtaPhiMVector v_gamma(g1.pt(), g1.eta(), g1.phi(), 0.); @@ -839,7 +874,11 @@ struct Pi0EtaToGammaGamma { continue; } - fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/same/hs"), veeg.M(), veeg.Pt(), weight); + } std::pair tuple_tmp_id2 = std::make_pair(pos2.trackId(), ele2.trackId()); if (std::find(used_photonIds_per_col.begin(), used_photonIds_per_col.end(), g1.globalIndex()) == used_photonIds_per_col.end()) { @@ -884,6 +923,20 @@ struct Pi0EtaToGammaGamma { } } + o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonLegCounts legCounts1{}, legCounts2{}; + if constexpr (std::is_same_v && std::is_same_v) { + auto pos1 = g1.template posTrack_as(); + auto neg1 = g1.template negTrack_as(); + auto pos2 = g2.template posTrack_as(); + auto neg2 = g2.template negTrack_as(); + legCounts1 = o2::aod::pwgem::photonmeson::utils::pairutil::getV0PhotonLegCounts(pos1, neg1); + legCounts2 = o2::aod::pwgem::photonmeson::utils::pairutil::getV0PhotonLegCounts(pos2, neg2); + if (cfgDoPhotonClassPairCut.value && + !o2::aod::pwgem::photonmeson::utils::pairutil::isPairPhotonClassSelected(legCounts1, legCounts2, mPhotonClassSelA, mPhotonClassSelB)) { + continue; + } + } + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; @@ -919,14 +972,26 @@ struct Pi0EtaToGammaGamma { wpair *= g2.omegaMBWeight(); } - fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), wpair); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), wpair); + } else { + fRegistry.fill(HIST("Pair/same/hs"), v12.M(), v12.Pt(), wpair); + } if (std::find(used_photonIds_per_col.begin(), used_photonIds_per_col.end(), g1.globalIndex()) == used_photonIds_per_col.end()) { - emh1->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::photonmeson::utils::EMPhoton(g1.pt(), g1.eta(), g1.phi(), 0)); + auto emphoton1 = o2::aod::pwgem::photonmeson::utils::EMPhoton(g1.pt(), g1.eta(), g1.phi(), 0); + if constexpr (std::is_same_v && std::is_same_v) { + emphoton1.setLegCounts(legCounts1); + } + emh1->AddTrackToEventPool(key_df_collision, emphoton1); used_photonIds_per_col.emplace_back(g1.globalIndex()); } if (std::find(used_photonIds_per_col.begin(), used_photonIds_per_col.end(), g2.globalIndex()) == used_photonIds_per_col.end()) { - emh2->AddTrackToEventPool(key_df_collision, o2::aod::pwgem::photonmeson::utils::EMPhoton(g2.pt(), g2.eta(), g2.phi(), 0)); + auto emphoton2 = o2::aod::pwgem::photonmeson::utils::EMPhoton(g2.pt(), g2.eta(), g2.phi(), 0); + if constexpr (std::is_same_v && std::is_same_v) { + emphoton2.setLegCounts(legCounts1); + } + emh2->AddTrackToEventPool(key_df_collision, emphoton2); used_photonIds_per_col.emplace_back(g2.globalIndex()); } ndiphoton++; @@ -944,11 +1009,11 @@ struct Pi0EtaToGammaGamma { } // make a vector of selected photons in this collision. - auto selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); - auto selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); + const auto& selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); + const auto& selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); - auto collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); - auto collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC) { // same kinds pairing for (const auto& mix_dfId_collisionId : collisionIds1_in_mixing_pool) { @@ -966,19 +1031,50 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), ngamma = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons1_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { for (const auto& g2 : photons1_from_event_pool) { + if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { + if (cfgDoPhotonClassPairCut.value) { + if (!g1.hasLegCounts() || !g2.hasLegCounts() || + !o2::aod::pwgem::photonmeson::utils::pairutil::isPairPhotonClassSelected(g1.legCounts(), g2.legCounts(), mPhotonClassSelA, mPhotonClassSelB)) { + continue; + } + } + } ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; if (std::fabs(v12.Rapidity()) > maxY) { continue; } + // as photon has mass= 0 e = p + float alphaMeson = std::fabs(g1.p() - g2.p()) / (g1.p() + g2.p()); + float alphaCut = 999.f; + switch (static_cast(cfgAlphaMesonCut.value)) { + case AlphaMesonCutOption::Off: + break; + case AlphaMesonCutOption::SpecificValue: + alphaCut = cfgAlphaMeson; + break; + case AlphaMesonCutOption::PTDependent: { + alphaCut = cfgAlphaMesonA * std::tanh(cfgAlphaMesonB * v12.pt()); + break; + } + default: + LOGF(error, "Invalid option for alpha meson cut. No alpha cut will be applied."); + } + if (alphaMeson > alphaCut) { + continue; + } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -999,7 +1095,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), nll = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons2_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { @@ -1013,7 +1109,11 @@ struct Pi0EtaToGammaGamma { if (std::fabs(v12.Rapidity()) > maxY) { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -1032,7 +1132,7 @@ struct Pi0EtaToGammaGamma { continue; } - auto photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons1_from_event_pool = emh1->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), nll = %d | event pool (%d, %d), ngamma = %d", ndf, collision.globalIndex(), selected_photons2_in_this_event.size(), mix_dfId, mix_collisionId, photons1_from_event_pool.size()); for (const auto& g1 : selected_photons2_in_this_event) { @@ -1046,7 +1146,11 @@ struct Pi0EtaToGammaGamma { if (std::fabs(v12.Rapidity()) > maxY) { continue; } - fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + if (cfgSparseFullAxes.value) { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), ep2, occupancy, collision.posZ(), weight); + } else { + fRegistry.fill(HIST("Pair/mix/hs"), v12.M(), v12.Pt(), weight); + } } } } // end of loop over mixed event pool @@ -1107,7 +1211,7 @@ struct Pi0EtaToGammaGamma { PROCESS_SWITCH(Pi0EtaToGammaGamma, processAnalysis, "process pair analysis", true); // using FilteredMyCollisionsWithJJMC = o2::soa::Filtered, o2::aod::EMEventsWeight>>; - void processAnalysisJJMC(o2::soa::Filtered, o2::aod::EMEventsWeight>> const& collisions, Types const&... args) + void processAnalysisJJMC(o2::soa::Filtered> const& collisions, Types const&... args) { // LOGF(info, "ndf = %d", ndf); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { diff --git a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h index 905027a8456..e8c2333ccd3 100644 --- a/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h +++ b/PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h @@ -23,6 +23,7 @@ #include "PWGEM/PhotonMeson/Core/V0PhotonCandidate.h" #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/EventHistograms.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" @@ -39,11 +40,13 @@ #include #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -55,8 +58,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -74,9 +79,6 @@ enum AlphaMesonCutOption { template struct Pi0EtaToGammaGammaMC { o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; o2::framework::Configurable cfgQvecEstimator{"cfgQvecEstimator", 0, "FT0M:0, FT0A:1, FT0C:2"}; @@ -87,10 +89,44 @@ struct Pi0EtaToGammaGammaMC { o2::framework::Configurable fd_k0s_to_pi0{"fd_k0s_pi0", "1.0", "feed down correction to pi0"}; o2::framework::Configurable cfgRequireTrueAssociation{"cfgRequireTrueAssociation", false, "flag to require true mc collision association"}; + o2::framework::Configurable cfgNbinsMass{"cfgNbinsMass", 400, "number of bins for the mass axis in same and mixed events"}; + o2::framework::Configurable> cfgMassBinRange{"cfgMassBinRange", {0., 0.8}, "range for the mass-axis in GeV/c2"}; + o2::framework::Configurable cfgAlphaMesonCut{"cfgAlphaMesonCut", 0, "flag for photon energy asymmetry distribution cut: 0: no cut, 1: cut specific value, 2: cut depending on pT"}; o2::framework::Configurable cfgAlphaMeson{"cfgAlphaMeson", 0.65, "photon energy asymmetry distribution parameter for specific value cut"}; o2::framework::Configurable cfgAlphaMesonA{"cfgAlphaMesonA", 0.65, "photon energy asymmetry distribution parameter A for pT dependent cut (A * tanh(B*pT))"}; o2::framework::Configurable cfgAlphaMesonB{"cfgAlphaMesonB", 1.2, "photon energy asymmetry distribution parameter B for pT dependent cut (A * tanh(B*pT))"}; + o2::framework::Configurable cfgGGContaCheck{"cfgGGContaCheck", false, "check gamma gamma contamination of dalitz"}; + + o2::framework::Configurable cfgDoPhotonClassPairCut{"cfgDoPhotonClassPairCut", false, "apply photon-class pair selection A x B (leg track composition, PCM-PCM only)"}; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "photonclassA_group"; + o2::framework::Configurable cfgMinNLegsITSTPC{"cfgMinNLegsITSTPC", 0, "min number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSTPC{"cfgMaxNLegsITSTPC", 2, "max number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsITSOnly{"cfgMinNLegsITSOnly", 0, "min number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSOnly{"cfgMaxNLegsITSOnly", 2, "max number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTPCOnly{"cfgMinNLegsTPCOnly", 0, "min number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTPCOnly{"cfgMaxNLegsTPCOnly", 2, "max number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTRD{"cfgMinNLegsTRD", 0, "min number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTRD{"cfgMaxNLegsTRD", 2, "max number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMinNLegsTOF{"cfgMinNLegsTOF", 0, "min number of legs with TOF (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTOF{"cfgMaxNLegsTOF", 2, "max number of legs with TOF (0-2)"}; + } photonclassA; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "photonclassB_group"; + o2::framework::Configurable cfgMinNLegsITSTPC{"cfgMinNLegsITSTPC", 0, "min number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSTPC{"cfgMaxNLegsITSTPC", 2, "max number of ITS-TPC legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsITSOnly{"cfgMinNLegsITSOnly", 0, "min number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsITSOnly{"cfgMaxNLegsITSOnly", 2, "max number of ITS-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTPCOnly{"cfgMinNLegsTPCOnly", 0, "min number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTPCOnly{"cfgMaxNLegsTPCOnly", 2, "max number of TPC-only legs (0-2)"}; + o2::framework::Configurable cfgMinNLegsTRD{"cfgMinNLegsTRD", 0, "min number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTRD{"cfgMaxNLegsTRD", 2, "max number of legs with TRD (0-2)"}; + o2::framework::Configurable cfgMinNLegsTOF{"cfgMinNLegsTOF", 0, "min number of legs with TOF (0-2)"}; + o2::framework::Configurable cfgMaxNLegsTOF{"cfgMaxNLegsTOF", 2, "max number of legs with TOF (0-2)"}; + } photonclassB; EMPhotonEventCut fEMEventCut; struct : o2::framework::ConfigurableGroup { @@ -113,6 +149,9 @@ struct Pi0EtaToGammaGammaMC { o2::framework::Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; } eventcuts; + o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonClassSelection mPhotonClassSelA{}; + o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonClassSelection mPhotonClassSelB{}; + V0PhotonCut fV0PhotonCut; struct : o2::framework::ConfigurableGroup { std::string prefix = "pcmcut_group"; @@ -125,6 +164,8 @@ struct Pi0EtaToGammaGammaMC { o2::framework::Configurable cfg_max_eta_v0{"cfg_max_eta_v0", 0.8, "max eta for v0 photons at PV"}; o2::framework::Configurable cfg_min_v0radius{"cfg_min_v0radius", 4.0, "min v0 radius"}; o2::framework::Configurable cfg_max_v0radius{"cfg_max_v0radius", 90.0, "max v0 radius"}; + o2::framework::Configurable cfg_midL_v0radius{"cfg_midL_v0radius", -1.0, "middle low v0 radius for rejection if >0"}; + o2::framework::Configurable cfg_midH_v0radius{"cfg_midH_v0radius", -1.0, "middle high v0 radius for rejection if >0"}; o2::framework::Configurable cfg_max_alpha_ap{"cfg_max_alpha_ap", 0.95, "max alpha for AP cut"}; o2::framework::Configurable cfg_max_qt_ap{"cfg_max_qt_ap", 0.01, "max qT for AP cut"}; o2::framework::Configurable cfg_min_cospa{"cfg_min_cospa", 0.999, "min V0 CosPA"}; @@ -223,27 +264,29 @@ struct Pi0EtaToGammaGammaMC { o2::framework::Configurable cfg_min_Ecluster{"cfg_min_Ecluster", 0.3, "Minimum cluster energy for PHOS in GeV"}; } phoscuts; - TF1* f1fd_k0s_to_pi0; + TF1* f1fd_k0s_to_pi0 = nullptr; o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; // static constexpr std::string_view event_types[2] = {"before/", "after/"}; // static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; - static constexpr std::string_view kParnames[2] = {"Pi0/", "Eta/"}; + static constexpr std::array kParnames = {"Pi0/", "Eta/"}; o2::ccdb::CcdbApi ccdbApi; - o2::framework::Service ccdb; - int mRunNumber; - float d_bz; + o2::framework::Service ccdb{}; + int mRunNumber = 0; + float d_bz = 0; void init(o2::framework::InitContext&) { o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMDalitzEE) { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "ee#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "ee#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } else { - o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "#gamma#gamma"); + o2::aod::pwgem::photonmeson::utils::nmhistogram::addNMHistograms(&fRegistry, true, "#gamma#gamma", false, {}, {}, {}, cfgNbinsMass, {cfgMassBinRange->at(0), cfgMassBinRange->at(1)}); } DefineEMEventCut(); DefinePCMCut(); + mPhotonClassSelA = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassA); + mPhotonClassSelB = o2::aod::pwgem::photonmeson::utils::pairutil::buildV0PhotonClassSelection(photonclassB); DefineDileptonCut(); DefineEMCCut(); DefinePHOSCut(); @@ -251,6 +294,12 @@ struct Pi0EtaToGammaGammaMC { f1fd_k0s_to_pi0 = new TF1("f1fd_k0s_to_pi0", TString(fd_k0s_to_pi0), 0.f, 100.f); fRegistry.add("Event/hNrecPerMCCollision", "Nrec per mc collision;N_{rec} collisions per MC collision", o2::framework::HistType::kTH1F, {{21, -0.5f, 20.5f}}, false); + if (cfgGGContaCheck) { + fRegistry.add("Event/hNGGContamEta", "Number of Eta from etaToGammaGamma; p_{T, #eta} (GeV/#it{c}); N", o2::framework::HistType::kTH1F, {{40, -0.5f, 20.5f}}, false); + fRegistry.add("Event/hNGGContamPion", "Number of Pion from etaToGammaGamma; p_{T, #pi} (GeV/#it{c}); N", o2::framework::HistType::kTH1F, {{40, -0.5f, 20.5f}}, false); + } + fRegistry.add("Event/hNDalitzEtaPt", "Number of DalitzEta; p_{T, #eta} (GeV/#it{c}); N", o2::framework::HistType::kTH1F, {{40, -0.5f, 20.5f}}, false); + fRegistry.add("Event/hNDalitzPionPt", "Number of DalitzPion; p_{T, #pi} (GeV/#it{c}) ; N", o2::framework::HistType::kTH1F, {{40, -0.5f, 20.5f}}, false); mRunNumber = 0; d_bz = 0; @@ -261,50 +310,41 @@ struct Pi0EtaToGammaGammaMC { ccdb->setFatalWhenNull(false); } - template + template void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { return; } + mRunNumber = collision.runNumber(); // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { + if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) d_bz = d_bz_input; o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) } - mRunNumber = collision.runNumber(); + o2::base::Propagator::initFieldFromGRP(&grpmag); return; } - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + // o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // // Fetch magnetic field from ccdb for current collision + // d_bz = collision.grpMagField().getNominalL3Field(); + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); } + d_bz = grpmag->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; fV0PhotonCut.SetD_Bz(d_bz); - mRunNumber = collision.runNumber(); } ~Pi0EtaToGammaGammaMC() { delete f1fd_k0s_to_pi0; - f1fd_k0s_to_pi0 = 0x0; + f1fd_k0s_to_pi0 = nullptr; } void DefineEMEventCut() @@ -332,7 +372,7 @@ struct Pi0EtaToGammaGammaMC { fV0PhotonCut.SetMinCosPA(pcmcuts.cfg_min_cospa); fV0PhotonCut.SetMaxPCA(pcmcuts.cfg_max_pca); fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfg_max_chi2kf); - fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius); + fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius, pcmcuts.cfg_midL_v0radius, pcmcuts.cfg_midH_v0radius); fV0PhotonCut.SetAPRange(pcmcuts.cfg_max_alpha_ap, pcmcuts.cfg_max_qt_ap); fV0PhotonCut.RejectITSib(pcmcuts.cfg_reject_v0_on_itsib); @@ -359,8 +399,7 @@ struct Pi0EtaToGammaGammaMC { fV0PhotonCut.SetNClassesMl(pcmcuts.cfg_nclasses_ml); fV0PhotonCut.SetMlTimestampCCDB(pcmcuts.cfg_timestamp_ccdb); fV0PhotonCut.SetCcdbUrl(ccdburl); - CentType mCentralityTypeMlEnum; - mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); + auto mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); fV0PhotonCut.SetCentralityTypeMl(mCentralityTypeMlEnum); fV0PhotonCut.SetCutDirMl(pcmcuts.cfg_cut_dir_ml); fV0PhotonCut.SetMlModelPathsCCDB(pcmcuts.cfg_model_paths_ccdb); @@ -435,13 +474,13 @@ struct Pi0EtaToGammaGammaMC { } o2::framework::SliceCache cache; - o2::framework::PresliceOptional>> perCollision_pcm = o2::aod::v0photonkf::pmeventId; - o2::framework::PresliceOptional> perCollision_emc = o2::aod::emccluster::pmeventId; - o2::framework::PresliceOptional> perCollision_phos = o2::aod::phoscluster::pmeventId; - o2::framework::PresliceOptional>> perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; + o2::framework::PresliceOptional perCollision_pcm = o2::aod::v0photonkf::pmeventId; + o2::framework::PresliceOptional perCollision_emc = o2::aod::emccluster::pmeventId; + o2::framework::PresliceOptional perCollision_phos = o2::aod::phoscluster::pmeventId; + o2::framework::PresliceOptional perCollision_electron = o2::aod::emprimaryelectronda::pmeventId; - o2::framework::PresliceOptional perEMCClusterMT = o2::aod::trackmatching::emEmcClusterId; - o2::framework::PresliceOptional perEMCClusterMS = o2::aod::trackmatching::emEmcClusterId; + o2::framework::PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + o2::framework::PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; o2::framework::Partition>> positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt&& nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; o2::framework::Partition>> electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && dileptoncuts.cfg_min_pt_track < o2::aod::track::pt && nabs(o2::aod::track::eta) < dileptoncuts.cfg_max_eta_track; @@ -462,7 +501,7 @@ struct Pi0EtaToGammaGammaMC { return s.fV0PhotonCut; } - template + template static bool applyCut(Self& s, Photon const& g) { return s.fV0PhotonCut.template IsSelected>(g); @@ -479,13 +518,13 @@ struct Pi0EtaToGammaGammaMC { static auto& perClusterMT() { - static auto slice{o2::aod::trackmatching::emEmcClusterId}; + static auto slice{o2::aod::mintm::minClusterId}; return slice; } static auto& perClusterMS() { - static auto slice{o2::aod::trackmatching::emEmcClusterId}; + static auto slice{o2::aod::mintm::minClusterId}; return slice; } @@ -496,10 +535,10 @@ struct Pi0EtaToGammaGammaMC { } // EMCal version has optional tables for matched tracks (global and secondaries) - template - static bool applyCut(Self& s, Cluster const& c, TMatchedTracks const& emcmatchedtracks = nullptr, TMatchedSecondaries const& secondaries = nullptr) + template + static bool applyCut(Self& s, Cluster const& cluster, TMatchedTracks const& emcmatchedtracks = nullptr, TMatchedSecondaries const& secondaries = nullptr) { - return s.fEMCCut.IsSelected(c, emcmatchedtracks, secondaries); + return s.fEMCCut.IsSelected(cluster, emcmatchedtracks, secondaries); } }; @@ -517,10 +556,10 @@ struct Pi0EtaToGammaGammaMC { return s.fPHOSCut; } - template - static bool applyCut(Self& s, Cluster const& c) + template + static bool applyCut(Self& s, Cluster const& cluster) { - return s.fPHOSCut.IsSelected(c); + return s.fPHOSCut.IsSelected(cluster); } }; @@ -538,7 +577,7 @@ struct Pi0EtaToGammaGammaMC { } // Dalitz version has two tracks as argument + B_z - template + template static bool applyCut(Self& s, Track1 const& track1, Track2 const& track2, float bz) { return s.fDileptonCut.IsSelected(track1, track2, bz); @@ -586,7 +625,7 @@ struct Pi0EtaToGammaGammaMC { continue; } - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; fV0PhotonCut.SetCentrality(centralities[cfgCentEstimator]); if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; @@ -601,7 +640,7 @@ struct Pi0EtaToGammaGammaMC { fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0, weight); // accepted int photonid1 = -1, photonid2 = -1, pi0id = -1, etaid = -1; - if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC) { // same kinds pairing + if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kEMCEMC || pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { // same kinds pairing and PCM-EMC auto photons1_per_collision = photons1.sliceByCached(TDetectorTag1::perCollision(), collision.globalIndex(), cache); auto photons2_per_collision = photons2.sliceByCached(TDetectorTag2::perCollision(), collision.globalIndex(), cache); @@ -616,7 +655,7 @@ struct Pi0EtaToGammaGammaMC { continue; } } else { - if (!TDetectorTag1::applyCut(*this, g1) || !TDetectorTag2::applyCut(*this, g2)) { + if (!TDetectorTag1::applyCut(*this, g1)) { // g2 is checked below with its own tag (and matched tracks for EMC) continue; } } @@ -639,6 +678,14 @@ struct Pi0EtaToGammaGammaMC { auto pos2 = g2.template posTrack_as(); auto ele2 = g2.template negTrack_as(); + if (cfgDoPhotonClassPairCut.value && + !o2::aod::pwgem::photonmeson::utils::pairutil::isPairPhotonClassSelected( + o2::aod::pwgem::photonmeson::utils::pairutil::getV0PhotonLegCounts(pos1, ele1), + o2::aod::pwgem::photonmeson::utils::pairutil::getV0PhotonLegCounts(pos2, ele2), + mPhotonClassSelA, mPhotonClassSelB)) { + continue; + } + auto pos1mc = pos1.template emmcparticle_as(); auto ele1mc = ele1.template emmcparticle_as(); auto pos2mc = pos2.template emmcparticle_as(); @@ -652,6 +699,20 @@ struct Pi0EtaToGammaGammaMC { photonid1 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster1mcparticle, mcparticles, std::vector{111, 221}); photonid2 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster2mcparticle, mcparticles, std::vector{111, 221}); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + // PCM: true conversion as for PCM-PCM + auto pos1 = g1.template posTrack_as(); + auto ele1 = g1.template negTrack_as(); + auto pos1mc = pos1.template emmcparticle_as(); + auto ele1mc = ele1.template emmcparticle_as(); + photonid1 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos1mc, ele1mc, -11, 11, 22, mcparticles); + + // EMC: photon below the pi0/eta of the leading contributor as for EMC-EMC + if (g2.emmcparticleIds().empty()) { + continue; // noise cluster without MC label + } + auto cluster2mcparticle = mcparticles.iteratorAt(g2.emmcparticleIds()[0]); + photonid2 = o2::aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(cluster2mcparticle, mcparticles, std::vector{111, 221}); } else { photonid1 = -1; photonid2 = -1; @@ -667,6 +728,10 @@ struct Pi0EtaToGammaGammaMC { if (!o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g1mc, maxRgen, maxY_gen, margin_z_mc, mcparticles) || !o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g2mc, maxRgen, maxY_gen, margin_z_mc, mcparticles)) { continue; } + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + if (!o2::aod::pwgem::photonmeson::utils::mcutil::IsConversionPointInAcceptance(g1mc, maxRgen, maxY_gen, margin_z_mc, mcparticles)) { + continue; + } } pi0id = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(g1mc, g2mc, 22, 22, 111, mcparticles); @@ -719,10 +784,11 @@ struct Pi0EtaToGammaGammaMC { } if (g1mc.globalIndex() == g2mc.globalIndex()) { - if (o2::aod::pwgem::dilepton::utils::mcutil::getMotherPDGCode(g1mc, mcparticles) == 111) + if (o2::aod::pwgem::dilepton::utils::mcutil::getMotherPDGCode(g1mc, mcparticles) == PDG_t::kPi0) { fRegistry.fill(HIST("Pair/Pi0/hs_FromSameGamma"), v12.M(), v12.Pt(), wpair); - else if (o2::aod::pwgem::dilepton::utils::mcutil::getMotherPDGCode(g1mc, mcparticles) == 221) + } else if (o2::aod::pwgem::dilepton::utils::mcutil::getMotherPDGCode(g1mc, mcparticles) == o2::constants::physics::Pdg::kEta) { fRegistry.fill(HIST("Pair/Eta/hs_FromSameGamma"), v12.M(), v12.Pt(), wpair); + } continue; } @@ -791,6 +857,25 @@ struct Pi0EtaToGammaGammaMC { auto pos2mc = mcparticles.iteratorAt(pos2.emmcparticleId()); auto ele2mc = mcparticles.iteratorAt(ele2.emmcparticleId()); + if (cfgGGContaCheck) { + photonid2 = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(pos2mc, ele2mc, -11, 11, 22, mcparticles); // check possible contamination + if (photonid2 > 0) { + auto photon2 = mcparticles.iteratorAt(photonid2); + int photon2pdg = photon2.pdgCode(); + int photon2mothid = photon2.mothersIds()[0]; + auto photon2moth = mcparticles.iteratorAt(photon2mothid); + if (photon2pdg == PDG_t::kGamma && (o2::aod::pwgem::photonmeson::utils::mcutil::isGammaGammaDecay(photon2moth, mcparticles))) { + int mothID = o2::aod::pwgem::dilepton::utils::mcutil::getMotherPDGCode(photon2, mcparticles); + if (mothID == o2::constants::physics::Pdg::kEta) { + fRegistry.fill(HIST("Event/hNGGContamEta"), photon2moth.pt()); + } + if (mothID == PDG_t::kPi0) { + fRegistry.fill(HIST("Event/hNGGContamPion"), photon2moth.pt()); + } + } + } + } + pi0id = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom3Prongs(g1mc, pos2mc, ele2mc, 22, -11, 11, 111, mcparticles); etaid = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom3Prongs(g1mc, pos2mc, ele2mc, 22, -11, 11, 221, mcparticles); if (pi0id < 0 && etaid < 0) { @@ -804,12 +889,14 @@ struct Pi0EtaToGammaGammaMC { } if (pi0id > 0) { auto pi0mc = mcparticles.iteratorAt(pi0id); + fRegistry.fill(HIST("Event/hNDalitzPionPt"), pi0mc.pt()); if (cfgRequireTrueAssociation && (pi0mc.emmceventId() != collision.emmceventId())) { continue; } o2::aod::pwgem::photonmeson::utils::nmhistogram::fillTruePairInfo(&fRegistry, veeg, pi0mc, mcparticles, mccollisions, f1fd_k0s_to_pi0, weight); } else if (etaid > 0) { auto etamc = mcparticles.iteratorAt(etaid); + fRegistry.fill(HIST("Event/hNDalitzEtaPt"), etamc.pt()); if (cfgRequireTrueAssociation && (etamc.emmceventId() != collision.emmceventId())) { continue; } @@ -891,9 +978,9 @@ struct Pi0EtaToGammaGammaMC { } o2::framework::PresliceUnsorted perMcCollision = o2::aod::emmcparticle::emmceventId; - o2::framework::PresliceUnsorted> rec_perMcCollision = o2::aod::emmceventlabel::emmceventId; + o2::framework::PresliceUnsorted rec_perMcCollision = o2::aod::emmceventlabel::emmceventId; - template + template void runGenInfo(TCollisions const& collisions, TMCCollisions const& mccollisions, TMCParticles const& /*mcparticles*/) { // loop over mc stack and fill histograms for pure MC truth signals @@ -919,7 +1006,7 @@ struct Pi0EtaToGammaGammaMC { continue; } - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -958,19 +1045,17 @@ struct Pi0EtaToGammaGammaMC { auto&& [emcclusters, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); runTruePairing(collisions, emcclusters, emcclusters, mccollisions, mcparticles, nullptr, emcMatchedTracks, emcMatchedSecondaries); runGenInfo(collisions, mccollisions, mcparticles); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + auto&& [v0photons, emcclusters, v0legs, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); + runTruePairing(collisions, v0photons, emcclusters, mccollisions, mcparticles, v0legs, emcMatchedTracks, emcMatchedSecondaries); + runGenInfo(collisions, mccollisions, mcparticles); } // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS) { // auto phosclusters = std::get<0>(std::tie(args...)); // runPairing(collisions, phosclusters, phosclusters, nullptr, nullptr, perCollision_phos, perCollision_phos, fPHOSCut, fPHOSCut, nullptr, nullptr); // } - // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { - // auto v0photons = std::get<0>(std::tie(args...)); - // auto v0legs = std::get<1>(std::tie(args...)); - // auto emcclusters = std::get<2>(std::tie(args...)); - // auto emcmatchedtracks = std::get<3>(std::tie(args...)); - // runPairing(collisions, v0photons, emcclusters, v0legs, nullptr, perCollision_pcm, perCollision_emc, fV0PhotonCut, fEMCCut, emcmatchedtracks, nullptr); - // } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { + // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { // auto v0photons = std::get<0>(std::tie(args...)); // auto v0legs = std::get<1>(std::tie(args...)); // auto phosclusters = std::get<2>(std::tie(args...)); @@ -980,7 +1065,7 @@ struct Pi0EtaToGammaGammaMC { PROCESS_SWITCH(Pi0EtaToGammaGammaMC, processAnalysis, "process pair analysis", true); // using FilteredMyCollisionsWithJJMC = o2::soa::Filtered, o2::aod::EMEventsWeight>>; - void processAnalysisJJMC(o2::soa::Filtered, o2::aod::EMEventsWeight>> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) + void processAnalysisJJMC(o2::soa::Filtered> const& collisions, o2::soa::Join const& mccollisions, o2::aod::EMMCParticles const& mcparticles, Types const&... args) { if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPCM) { auto&& [v0photons, v0legs] = std::forward_as_tuple(args...); @@ -1004,19 +1089,17 @@ struct Pi0EtaToGammaGammaMC { // runTruePairing(collisions, emcclusters, emcclusters, nullptr, nullptr, perCollision_emc, perCollision_emc, fEMCCut, fEMCCut, mccollisions, mcparticles); runTruePairing(collisions, emcclusters, emcclusters, mccollisions, mcparticles, nullptr, emcMatchedTracks, emcMatchedSecondaries); runGenInfo(collisions, mccollisions, mcparticles); + } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { + auto&& [v0photons, emcclusters, v0legs, emcMatchedTracks, emcMatchedSecondaries] = std::forward_as_tuple(args...); + runTruePairing(collisions, v0photons, emcclusters, mccollisions, mcparticles, v0legs, emcMatchedTracks, emcMatchedSecondaries); + runGenInfo(collisions, mccollisions, mcparticles); } // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPHOSPHOS) { // auto phosclusters = std::get<0>(std::tie(args...)); // runPairing(collisions, phosclusters, phosclusters, nullptr, nullptr, perCollision_phos, perCollision_phos, fPHOSCut, fPHOSCut, nullptr, nullptr); // } - // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMEMC) { - // auto v0photons = std::get<0>(std::tie(args...)); - // auto v0legs = std::get<1>(std::tie(args...)); - // auto emcclusters = std::get<2>(std::tie(args...)); - // auto emcmatchedtracks = std::get<3>(std::tie(args...)); - // runPairing(collisions, v0photons, emcclusters, v0legs, nullptr, perCollision_pcm, perCollision_emc, fV0PhotonCut, fEMCCut, emcmatchedtracks, nullptr); - // } else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { + // else if constexpr (pairtype == o2::aod::pwgem::photonmeson::photonpair::PairType::kPCMPHOS) { // auto v0photons = std::get<0>(std::tie(args...)); // auto v0legs = std::get<1>(std::tie(args...)); // auto phosclusters = std::get<2>(std::tie(args...)); diff --git a/PWGEM/PhotonMeson/Core/TaggingPi0.h b/PWGEM/PhotonMeson/Core/TaggingPi0.h index 59adf8f0def..64080cd2a2d 100644 --- a/PWGEM/PhotonMeson/Core/TaggingPi0.h +++ b/PWGEM/PhotonMeson/Core/TaggingPi0.h @@ -37,11 +37,13 @@ #include #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -54,6 +56,7 @@ #include #include +#include #include #include #include @@ -64,7 +67,7 @@ #include #include -using MyCollisions = o2::soa::Join; +using MyCollisions = o2::soa::Join; using MyCollision = MyCollisions::iterator; using MyCollisionsWithJJMC = o2::soa::Join; @@ -85,9 +88,6 @@ using MyEMCCluster = MyEMCClusters::iterator; template struct TaggingPi0 { o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; o2::framework::Configurable ndiff_bc_mix{"ndiff_bc_mix", 594, "difference in global BC required in mixed events"}; @@ -213,8 +213,8 @@ struct TaggingPi0 { // } phoscuts; o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_types[2] = {"before/", "after/"}; - static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; + static constexpr std::array event_types = {"before/", "after/"}; + static constexpr std::array event_pair_types = {"same/", "mix/"}; std::vector zvtx_bin_edges; std::vector cent_bin_edges; @@ -222,9 +222,9 @@ struct TaggingPi0 { std::vector occ_bin_edges; o2::ccdb::CcdbApi ccdbApi; - o2::framework::Service ccdb; - int mRunNumber; - float d_bz; + o2::framework::Service ccdb{}; + int mRunNumber = 0; + float d_bz = 0; void init(o2::framework::InitContext&) { @@ -262,51 +262,40 @@ struct TaggingPi0 { ccdb->setFatalWhenNull(false); } - template + template void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { return; } + if (mRunNumber == collision.runNumber()) { + return; + } // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { + if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) d_bz = d_bz_input; o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) } + o2::base::Propagator::initFieldFromGRP(&grpmag); mRunNumber = collision.runNumber(); return; } - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } - mRunNumber = collision.runNumber(); + o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // Fetch magnetic field from ccdb for current collision + d_bz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; } ~TaggingPi0() { delete emh1; - emh1 = 0x0; + emh1 = nullptr; delete emh2; - emh2 = 0x0; + emh2 = nullptr; used_photonIds_per_col.clear(); used_photonIds_per_col.shrink_to_fit(); @@ -449,7 +438,7 @@ struct TaggingPi0 { std::vector> used_dileptonIds_per_col; // std::map, uint64_t> map_mixed_eventId_to_globalBC; - template + template void runPairing(TCollisions const& collisions, TPhotons1 const& photons1, TPhotons2 const& photons2, TSubInfos1 const&, TSubInfos2 const&, @@ -469,7 +458,7 @@ struct TaggingPi0 { continue; } - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -506,9 +495,7 @@ struct TaggingPi0 { } int occbin = -1; - if (cfgOccupancyEstimator == 0) { - occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; - } else if (cfgOccupancyEstimator == 1) { + if (cfgOccupancyEstimator == 1) { occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.trackOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; } else { occbin = lower_bound(occ_bin_edges.begin(), occ_bin_edges.end(), collision.ft0cOccupancyInTimeRange()) - occ_bin_edges.begin() - 1; @@ -612,11 +599,11 @@ struct TaggingPi0 { } // make a vector of selected photons in this collision. - auto selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); - // auto selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); + const auto& selected_photons1_in_this_event = emh1->GetTracksPerCollision(key_df_collision); + // const auto& selected_photons2_in_this_event = emh2->GetTracksPerCollision(key_df_collision); - // auto collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); - auto collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); + // const auto& collisionIds1_in_mixing_pool = emh1->GetCollisionIdsFromEventPool(key_bin); + const auto& collisionIds2_in_mixing_pool = emh2->GetCollisionIdsFromEventPool(key_bin); for (const auto& mix_dfId_collisionId : collisionIds2_in_mixing_pool) { int mix_dfId = mix_dfId_collisionId.first; @@ -633,7 +620,7 @@ struct TaggingPi0 { continue; } - auto photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); + const auto& photons2_from_event_pool = emh2->GetTracksPerCollision(mix_dfId_collisionId); // LOGF(info, "Do event mixing: current event (%d, %d), ngamma = %d | event pool (%d, %d), nll = %d", ndf, collision.globalIndex(), selected_photons1_in_this_event.size(), mix_dfId, mix_collisionId, photons2_from_event_pool.size()); for (const auto& g1 : selected_photons1_in_this_event) { // [photon from event1, dilepton from event2] diff --git a/PWGEM/PhotonMeson/Core/TaggingPi0MC.h b/PWGEM/PhotonMeson/Core/TaggingPi0MC.h index 1334a89bf98..4588f70182d 100644 --- a/PWGEM/PhotonMeson/Core/TaggingPi0MC.h +++ b/PWGEM/PhotonMeson/Core/TaggingPi0MC.h @@ -38,11 +38,13 @@ #include #include #include -#include +#include +#include #include #include #include #include +#include #include #include #include @@ -55,6 +57,7 @@ #include #include +#include #include #include #include @@ -62,7 +65,7 @@ #include #include -using MyCollisions = o2::soa::Join; +using MyCollisions = o2::soa::Join; using MyCollision = MyCollisions::iterator; using MyCollisionsWithJJMC = o2::soa::Join; @@ -74,7 +77,7 @@ using MyMCCollision = MyMCCollisions::iterator; using MyV0Photons = o2::soa::Join; using MyV0Photon = MyV0Photons::iterator; -using MyEMCClusters = o2::soa::Join; +using MyEMCClusters = o2::soa::Join; using MyEMCCluster = MyEMCClusters::iterator; // using MyPHOSClusters = o2::soa::Join; @@ -89,9 +92,6 @@ using MyMCElectron = MyMCElectrons::iterator; template struct TaggingPi0MC { o2::framework::Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; o2::framework::Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; o2::framework::Configurable cfgQvecEstimator{"cfgQvecEstimator", 0, "FT0M:0, FT0A:1, FT0C:2"}; @@ -209,15 +209,15 @@ struct TaggingPi0MC { // } phoscuts; o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_types[2] = {"before/", "after/"}; - static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; - static constexpr std::string_view parnames[2] = {"Pi0/", "Eta/"}; + static constexpr std::array event_types = {"before/", "after/"}; + static constexpr std::array event_pair_types = {"same/", "mix/"}; + static constexpr std::array parnames = {"Pi0/", "Eta/"}; o2::ccdb::CcdbApi ccdbApi; - o2::framework::Service ccdb; - int mRunNumber; - float d_bz; - TF1* f1fd_k0s_to_pi0; + o2::framework::Service ccdb{}; + int mRunNumber = 0; + float d_bz = 0; + TF1* f1fd_k0s_to_pi0 = nullptr; void init(o2::framework::InitContext&) { @@ -239,49 +239,38 @@ struct TaggingPi0MC { ccdb->setFatalWhenNull(false); } - template + template void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { return; } + if (mRunNumber == collision.runNumber()) { + return; + } // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { + if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) d_bz = d_bz_input; o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) } + o2::base::Propagator::initFieldFromGRP(&grpmag); mRunNumber = collision.runNumber(); return; } - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } - mRunNumber = collision.runNumber(); + o2::base::Propagator::initFieldFromGRP(&collision.grpMagField()); + // Fetch magnetic field from ccdb for current collision + d_bz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; } ~TaggingPi0MC() { delete f1fd_k0s_to_pi0; - f1fd_k0s_to_pi0 = 0x0; + f1fd_k0s_to_pi0 = nullptr; } void addHistogrms() @@ -418,7 +407,7 @@ struct TaggingPi0MC { o2::framework::Partition positrons = o2::aod::emprimaryelectron::sign > int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt&& nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl&& o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); o2::framework::Partition electrons = o2::aod::emprimaryelectron::sign < int8_t(0) && static_cast(dileptoncuts.cfg_min_pt_track) < o2::aod::track::pt && nabs(o2::aod::track::eta) < static_cast(dileptoncuts.cfg_max_eta_track) && static_cast(dileptoncuts.cfg_min_TPCNsigmaEl) < o2::aod::pidtpc::tpcNSigmaEl && o2::aod::pidtpc::tpcNSigmaEl < static_cast(dileptoncuts.cfg_max_TPCNsigmaEl); - template + template void runTruePairing(TCollisions const& collisions, TPhotons1 const& photons1, TPhotons2 const& photons2, TSubInfos1 const&, TSubInfos2 const&, @@ -438,7 +427,7 @@ struct TaggingPi0MC { continue; } - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } diff --git a/PWGEM/PhotonMeson/Core/V0PhotonCandidate.h b/PWGEM/PhotonMeson/Core/V0PhotonCandidate.h index af9acc7003a..d653194ca4d 100644 --- a/PWGEM/PhotonMeson/Core/V0PhotonCandidate.h +++ b/PWGEM/PhotonMeson/Core/V0PhotonCandidate.h @@ -38,7 +38,7 @@ struct V0PhotonCandidate { V0PhotonCandidate() = default; // Set method for photonconversionbuilder template - void setPhotonCandidate(const KFParticle& v0DecayVtx, const KFParticle& v0PV, const TTrack& pos, const KFParticle& posDecayVtx, const TTrack& ele, const KFParticle& eleDecayVtx, const auto& collision, float cospa, float cospaRZ, float cospaXY, float psipair, float phiv, CentType centType, auto posdcaXY, auto eledcaXY, auto posdcaZ, auto eledcaZ) + void setPhotonCandidate(const KFParticle& v0DecayVtx, const KFParticle& v0PV, const TTrack& pos, const KFParticle& posDecayVtx, const TTrack& ele, const KFParticle& eleDecayVtx, const auto& collision, float cospa_, float cospaRZ_, float cospaXY_, float psipair_, float phiv_, CentType centType_, auto posdcaXY_, auto eledcaXY_, auto posdcaZ_, auto eledcaZ_) { conversionPointx = v0DecayVtx.GetX(); conversionPointy = v0DecayVtx.GetY(); @@ -82,28 +82,28 @@ struct V0PhotonCandidate { float v0mom = RecoDecay::sqrtSumOfSquares(v0DecayVtx.GetPx(), v0DecayVtx.GetPy(), v0DecayVtx.GetPz()); float length = RecoDecay::sqrtSumOfSquares(v0DecayVtx.GetX() - collision.posX(), v0DecayVtx.GetY() - collision.posY(), v0DecayVtx.GetZ() - collision.posZ()); - float dcaXV0ToPV = (v0DecayVtx.GetX() - v0DecayVtx.GetPx() * cospa * length / v0mom) - collision.posX(); - float dcaYV0ToPV = (v0DecayVtx.GetY() - v0DecayVtx.GetPy() * cospa * length / v0mom) - collision.posY(); + float dcaXV0ToPV = (v0DecayVtx.GetX() - v0DecayVtx.GetPx() * cospa_ * length / v0mom) - collision.posX(); + float dcaYV0ToPV = (v0DecayVtx.GetY() - v0DecayVtx.GetPy() * cospa_ * length / v0mom) - collision.posY(); float tmpSign = (dcaXV0ToPV * dcaYV0ToPV > 0.f) ? +1.f : -1.f; dcaXYV0ToPV = RecoDecay::sqrtSumOfSquares(dcaXV0ToPV, dcaYV0ToPV) * tmpSign; - dcaZV0ToPV = (v0DecayVtx.GetZ() - v0DecayVtx.GetPz() * cospa * length / v0mom) - collision.posZ(); + dcaZV0ToPV = (v0DecayVtx.GetZ() - v0DecayVtx.GetPz() * cospa_ * length / v0mom) - collision.posZ(); - alpha = v0_alpha(posPx, posPy, posPz, elePx, elePy, elePz); - qt = v0_qt(posPx, posPy, posPz, elePx, elePy, elePz); + alpha = v0Alpha(posPx, posPy, posPz, elePx, elePy, elePz); + qt = v0Qt(posPx, posPy, posPz, elePx, elePy, elePz); - this->cospa = cospa; - this->cospaRZ = cospaRZ; - this->cospaXY = cospaXY; - this->psipair = psipair; - this->phiv = phiv; - this->centType = centType; - this->posdcaXY = posdcaXY; - this->eledcaXY = eledcaXY; - this->posdcaZ = posdcaZ; - this->eledcaZ = eledcaZ; + cospa = cospa_; + cospaRZ = cospaRZ_; + cospaXY = cospaXY_; + psipair = psipair_; + phiv = phiv_; + centType = centType_; + posdcaXY = posdcaXY_; + eledcaXY = eledcaXY_; + posdcaZ = posdcaZ_; + eledcaZ = eledcaZ_; - switch (centType) { + switch (centType_) { case CentType::CentFT0A: cent = collision.centFT0A(); break; @@ -117,7 +117,7 @@ struct V0PhotonCandidate { } // Set-Method for V0PhotonCut - void setPhoton(const auto& v0, const auto& pos, const auto& ele, float cent, CentType centType) + void setPhoton(const auto& v0, const auto& pos, const auto& ele, float cent_, CentType centType_) { conversionPointx = v0.vx(); conversionPointy = v0.vy(); @@ -174,60 +174,60 @@ struct V0PhotonCandidate { psipair = v0.psipair(); phiv = v0.phiv(); } - this->cent = cent; - this->centType = centType; + cent = cent_; + centType = centType_; } // Getter functions - float getPosPt() const { return posPT; } - float getElePt() const { return elePT; } - float getChi2NDF() const { return chi2ndf; } - float getDcaXYToPV() const { return dcaXYV0ToPV; } - float getDcaZToPV() const { return dcaZV0ToPV; } - float getAlpha() const { return alpha; } - float getQt() const { return qt; } - float getPhiV() const { return phiv; } - float getPsiPair() const { return psipair; } - float getCosPA() const { return cospa; } - float getCosPARZ() const { return cospaRZ; } - float getCosPAXY() const { return cospaXY; } - float getEta() const { return eta; } - float getPosEta() const { return posEta; } - float getEleEta() const { return eleEta; } - float getConversionPointX() const { return conversionPointx; } - float getConversionPointY() const { return conversionPointy; } - float getConversionPointZ() const { return conversionPointz; } - float getPx() const { return px; } - float getPy() const { return py; } - float getPz() const { return pz; } - float getPt() const { return pT; } - float getPosPx() const { return posPx; } - float getPosPy() const { return posPy; } - float getPosPz() const { return posPz; } - float getElePx() const { return elePx; } - float getElePy() const { return elePy; } - float getElePz() const { return elePz; } - float getPosDcaXY() const { return posdcaXY; } - float getPosDcaZ() const { return posdcaZ; } - float getEleDcaXY() const { return eledcaXY; } - float getEleDcaZ() const { return eledcaZ; } - float getPosTPCNClsShared() const { return posTPCNClsShared; } - float getPosTPCNClsFindable() const { return posTPCNClsFindable; } - float getPosTPCNClsFindableMinusShared() const { return posTPCNClsFindableMinusShared; } - float getPosTPCNClsFindableMinusCrossedRows() const { return posTPCNClsFindableMinusCrossedRows; } - float getPosTPCChi2NCl() const { return posTPCChi2NCl; } - float getPosTPCSignal() const { return posTPCSignal; } - float getPosITSClusterSizes() const { return posITSClusterSizes; } - float getEleTPCNClsShared() const { return eleTPCNClsShared; } - float getEleTPCNClsFindable() const { return eleTPCNClsFindable; } - float getEleTPCNClsFindableMinusShared() const { return eleTPCNClsFindableMinusShared; } - float getEleTPCNClsFindableMinusCrossedRows() const { return eleTPCNClsFindableMinusCrossedRows; } - float getEleTPCChi2NCl() const { return eleTPCChi2NCl; } - float getEleTPCSignal() const { return eleTPCSignal; } - float getEleITSClusterSizes() const { return eleITSClusterSizes; } - float getCent() const { return cent; } - float getPCA() const { return pca; } - CentType getCentType() const { return centType; } + [[nodiscard]] float getPosPt() const { return posPT; } + [[nodiscard]] float getElePt() const { return elePT; } + [[nodiscard]] float getChi2NDF() const { return chi2ndf; } + [[nodiscard]] float getDcaXYToPV() const { return dcaXYV0ToPV; } + [[nodiscard]] float getDcaZToPV() const { return dcaZV0ToPV; } + [[nodiscard]] float getAlpha() const { return alpha; } + [[nodiscard]] float getQt() const { return qt; } + [[nodiscard]] float getPhiV() const { return phiv; } + [[nodiscard]] float getPsiPair() const { return psipair; } + [[nodiscard]] float getCosPA() const { return cospa; } + [[nodiscard]] float getCosPARZ() const { return cospaRZ; } + [[nodiscard]] float getCosPAXY() const { return cospaXY; } + [[nodiscard]] float getEta() const { return eta; } + [[nodiscard]] float getPosEta() const { return posEta; } + [[nodiscard]] float getEleEta() const { return eleEta; } + [[nodiscard]] float getConversionPointX() const { return conversionPointx; } + [[nodiscard]] float getConversionPointY() const { return conversionPointy; } + [[nodiscard]] float getConversionPointZ() const { return conversionPointz; } + [[nodiscard]] float getPx() const { return px; } + [[nodiscard]] float getPy() const { return py; } + [[nodiscard]] float getPz() const { return pz; } + [[nodiscard]] float getPt() const { return pT; } + [[nodiscard]] float getPosPx() const { return posPx; } + [[nodiscard]] float getPosPy() const { return posPy; } + [[nodiscard]] float getPosPz() const { return posPz; } + [[nodiscard]] float getElePx() const { return elePx; } + [[nodiscard]] float getElePy() const { return elePy; } + [[nodiscard]] float getElePz() const { return elePz; } + [[nodiscard]] float getPosDcaXY() const { return posdcaXY; } + [[nodiscard]] float getPosDcaZ() const { return posdcaZ; } + [[nodiscard]] float getEleDcaXY() const { return eledcaXY; } + [[nodiscard]] float getEleDcaZ() const { return eledcaZ; } + [[nodiscard]] float getPosTPCNClsShared() const { return posTPCNClsShared; } + [[nodiscard]] float getPosTPCNClsFindable() const { return posTPCNClsFindable; } + [[nodiscard]] float getPosTPCNClsFindableMinusShared() const { return posTPCNClsFindableMinusShared; } + [[nodiscard]] float getPosTPCNClsFindableMinusCrossedRows() const { return posTPCNClsFindableMinusCrossedRows; } + [[nodiscard]] float getPosTPCChi2NCl() const { return posTPCChi2NCl; } + [[nodiscard]] float getPosTPCSignal() const { return posTPCSignal; } + [[nodiscard]] float getPosITSClusterSizes() const { return posITSClusterSizes; } + [[nodiscard]] float getEleTPCNClsShared() const { return eleTPCNClsShared; } + [[nodiscard]] float getEleTPCNClsFindable() const { return eleTPCNClsFindable; } + [[nodiscard]] float getEleTPCNClsFindableMinusShared() const { return eleTPCNClsFindableMinusShared; } + [[nodiscard]] float getEleTPCNClsFindableMinusCrossedRows() const { return eleTPCNClsFindableMinusCrossedRows; } + [[nodiscard]] float getEleTPCChi2NCl() const { return eleTPCChi2NCl; } + [[nodiscard]] float getEleTPCSignal() const { return eleTPCSignal; } + [[nodiscard]] float getEleITSClusterSizes() const { return eleITSClusterSizes; } + [[nodiscard]] float getCent() const { return cent; } + [[nodiscard]] float getPCA() const { return pca; } + [[nodiscard]] CentType getCentType() const { return centType; } private: float conversionPointx{0.f}; diff --git a/PWGEM/PhotonMeson/Core/V0PhotonCut.cxx b/PWGEM/PhotonMeson/Core/V0PhotonCut.cxx index 03428137217..cc4539141c2 100644 --- a/PWGEM/PhotonMeson/Core/V0PhotonCut.cxx +++ b/PWGEM/PhotonMeson/Core/V0PhotonCut.cxx @@ -74,14 +74,20 @@ void V0PhotonCut::SetAPRange(float max_alpha, float max_qt) } void V0PhotonCut::SetMaxMeePsiPairDep(std::function psiDepCut) { - mMaxMeePsiPairDep = psiDepCut; + mMaxMeePsiPairDep = std::move(psiDepCut); LOG(info) << "V0 Photon Cut, set max mee psi pair dep: " << mMaxMeePsiPairDep(0.1); } -void V0PhotonCut::SetRxyRange(float min, float max) +void V0PhotonCut::SetRxyRange(float min, float max, float midL, float midH) { mMinRxy = min; mMaxRxy = max; + mMidRxyLow = midL; + mMidRxyHigh = midH; LOG(info) << "V0 Photon selection, set Rxy range: " << mMinRxy << " - " << mMaxRxy; + if (midL >= 0.f && midH >= 0.f && midH > midL) { + LOG(info) << "Rejecting Rxy interval: " + << mMidRxyLow << " - " << mMidRxyHigh; + } } void V0PhotonCut::SetMinCosPA(float min) { @@ -203,7 +209,7 @@ void V0PhotonCut::SetMaxDcaZ(float maxDcaZ) void V0PhotonCut::SetMaxDcaXYPtDep(std::function ptDepCut) { - mMaxDcaXYPtDep = ptDepCut; + mMaxDcaXYPtDep = std::move(ptDepCut); LOG(info) << "V0 Photon Cut, set max DCA xy pt dep: " << mMaxDcaXYPtDep(1.0); } @@ -335,9 +341,9 @@ void V0PhotonCut::SetNClassesMl(int nClasses) LOG(info) << "V0 Photon Cut, set number of classes ML: " << mNClassesMl; } -void V0PhotonCut::SetNamesInputFeatures(const std::vector& featureNames) +void V0PhotonCut::SetNamesInputFeatures(const std::vector& namesInputFeaturesVec) { - mNamesInputFeatures = featureNames; + mNamesInputFeatures = namesInputFeaturesVec; mMlInputFeatures.reserve(mNamesInputFeatures.size()); LOG(info) << "V0 Photon Cut, set ML input feature names with size:" << mNamesInputFeatures.size(); } diff --git a/PWGEM/PhotonMeson/Core/V0PhotonCut.h b/PWGEM/PhotonMeson/Core/V0PhotonCut.h index 741f72f8df9..1921a71c79b 100644 --- a/PWGEM/PhotonMeson/Core/V0PhotonCut.h +++ b/PWGEM/PhotonMeson/Core/V0PhotonCut.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -34,9 +35,8 @@ #include -#include - #include +#include #include #include #include @@ -48,11 +48,7 @@ #include #include -namespace o2::analysis -{ - -// namespace per channel -namespace em_cuts_ml +namespace o2::analysis::em_cuts_ml { // direction of the cut enum CutDirection { @@ -66,7 +62,7 @@ static constexpr int NBins = 12; static constexpr int NBinsPt = 12; static constexpr int NCutScores = 2; // default values for the pT bin edges, offset by 1 from the bin numbers in cuts array -constexpr double BinsPt[NBinsPt + 1] = { +constexpr std::array BinsPt = { 0., 0.25, 0.5, @@ -80,9 +76,9 @@ constexpr double BinsPt[NBinsPt + 1] = { 20., 50., 100.}; -const auto vecBinsPt = std::vector{BinsPt, BinsPt + NBinsPt + 1}; +const auto vecBinsPt = std::vector{BinsPt.begin(), BinsPt.end()}; static constexpr int NBinsCent = 11; -constexpr double BinsCent[NBinsCent + 1] = { +constexpr std::array BinsCent = { 0., 5, 10, @@ -95,30 +91,29 @@ constexpr double BinsCent[NBinsCent + 1] = { 80, 90, 100.}; -const auto vecBinsCent = std::vector{BinsCent, BinsCent + NBinsCent + 1}; +const auto vecBinsCent = std::vector{BinsCent.begin(), BinsCent.end()}; // default values for the ML model paths, one model per pT bin static const std::vector modelPaths = { ""}; // default values for the cut directions -constexpr int CutDir[NCutScores] = {CutGreater, CutSmaller}; -const auto vecCutDir = std::vector{CutDir, CutDir + NCutScores}; +constexpr std::array CutDir = {CutGreater, CutSmaller}; +const auto vecCutDir = std::vector{CutDir.begin(), CutDir.end()}; // default values for the cuts -constexpr double Cuts[NBins][NCutScores] = { - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}, - {0.5, 0.5}}; +constexpr std::array, NBins> Cuts = {{{0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}, + {0.5, 0.5}}}; // row labels static const std::vector labelsPt = { @@ -150,9 +145,8 @@ static const std::vector labelsCent = { // column labels static const std::vector labelsCutScore = {"score background", "score primary photons"}; -} // namespace em_cuts_ml -} // namespace o2::analysis +} // namespace o2::analysis::em_cuts_ml namespace o2::analysis::em::v0 { @@ -168,7 +162,7 @@ class V0PhotonCut : public TNamed { public: V0PhotonCut() = default; - V0PhotonCut(const char* name, const char* title) : TNamed(name, title) {} + V0PhotonCut(const char* name, const char* title) : name(name), title(title) {} ~V0PhotonCut() override { delete mEmMlResponse; }; enum class V0PhotonCuts : int { @@ -216,6 +210,9 @@ class V0PhotonCut : public TNamed kRadAndAngle = 2 }; + const std::string& getName() const { return name; } + const std::string& getTitle() const { return title; } + /// \brief add histograms to registry /// \param fRegistry pointer to histogram registry void addQAHistograms(o2::framework::HistogramRegistry* fRegistry = nullptr) const @@ -306,7 +303,7 @@ class V0PhotonCut : public TNamed void fillBeforePhotonHistogram(TV0 const& v0, TLeg1 const& pos, TLeg2 const& ele, o2::framework::HistogramRegistry* fRegistry = nullptr) const { - if (mDoQA == false || fRegistry == nullptr) { + if (!mDoQA || fRegistry == nullptr) { return; } @@ -336,7 +333,7 @@ class V0PhotonCut : public TNamed void fillAfterPhotonHistogram(TV0 const& v0, TLeg1 const& pos, TLeg2 const& ele, o2::framework::HistogramRegistry* fRegistry = nullptr) const { - if (mDoQA == false || fRegistry == nullptr) { + if (!mDoQA || fRegistry == nullptr) { return; } @@ -841,28 +838,29 @@ class V0PhotonCut : public TNamed return std::pow(v0.alpha() / mMaxAlpha, 2) + std::pow(v0.qtarm() / mMaxQt, 2) < 1.0; // TODO: implement fully - case V0PhotonCuts::kPsiPair: { - if constexpr (requires { v0.psipair(); }) { - // return (std::fabs(v0.psipair() < 0.18f * std::exp( -0.55f * v0.chiSquareNDF()))); - return true; - } else { - return true; - } - } - + case V0PhotonCuts::kPsiPair: + // if constexpr (requires { v0.psipair(); }) { + // return (std::fabs(v0.psipair() < 0.18f * std::exp( -0.55f * v0.chiSquareNDF()))); + // } else { + // return true; + // } // TODO: implement fully - case V0PhotonCuts::kPhiV: { - if constexpr (requires { v0.phiv(); }) { - return true; - } else { - return true; - } - } + case V0PhotonCuts::kPhiV: + return true; + // if constexpr (requires { v0.phiv(); }) { + // return true; + // } else { + // return true; + // } case V0PhotonCuts::kRxy: { if (v0.v0radius() < mMinRxy || mMaxRxy < v0.v0radius()) { return false; } + if (mMidRxyLow > 0.f && mMidRxyHigh > 0.f && + v0.v0radius() >= mMidRxyLow && v0.v0radius() <= mMidRxyHigh) { + return false; + } return true; } @@ -922,7 +920,7 @@ class V0PhotonCut : public TNamed return !(dxy > margin_xy); } case V0PhotonCuts::kIsTooClose: { - if (mRejectMask.size() == 0) { + if (mRejectMask.empty()) { return true; } return (mRejectMask[v0.globalIndex()] == 0); @@ -1013,7 +1011,7 @@ class V0PhotonCut : public TNamed int binsNCent = static_cast(mBinsCentMl.size()) - 1; int binsN = binsNPt * binsNCent; if (binsN * static_cast(mCutDirMl.size()) != static_cast(mCutsMlFlat.size())) { - LOG(fatal) << "Mismatch in number of bins and cuts provided for 2D ML application: binsN * mCutDirMl: " << int(binsN) * int(mCutDirMl.size()) << " bins vs. mCutsMlFlat: " << mCutsMlFlat.size() << " cuts"; + LOG(fatal) << "Mismatch in number of bins and cuts provided for 2D ML application: binsN * mCutDirMl: " << binsN * static_cast(mCutDirMl.size()) << " bins vs. mCutsMlFlat: " << mCutsMlFlat.size() << " cuts"; } if (binsN != static_cast(mOnnxFileNames.size())) { LOG(fatal) << "Mismatch in number of bins and ONNX files provided for 2D ML application: binsN " << binsN << " bins vs. mOnnxFileNames: " << mOnnxFileNames.size() << " ONNX files"; @@ -1059,7 +1057,7 @@ class V0PhotonCut : public TNamed mEmMlResponse->init(); } - const std::span getBDTValue() const + std::span getBDTValue() const { return mMlBDTScores; } @@ -1092,7 +1090,7 @@ class V0PhotonCut : public TNamed void SetPsiPairRange(float min = -3.15, float max = +3.15); void SetPhivPairRange(float min = 0.f, float max = +3.15); void SetAPRange(float max_alpha = 0.95, float max_qt = 0.05); // Armenteros Podolanski - void SetRxyRange(float min = 0.f, float max = 180.f); + void SetRxyRange(float min = 0.f, float max = 180.f, float midL = -1.f, float midH = -1.f); void SetMinCosPA(float min = 0.95); void SetMaxPCA(float max = 2.f); void SetMaxChi2KF(float max = 1e+10); @@ -1155,6 +1153,8 @@ class V0PhotonCut : public TNamed void setDoQA(bool flag = false); private: + std::string name; + std::string title; static const std::pair> its_ib_Requirement; static const std::pair> its_ob_Requirement; static const std::pair> its_ob_Requirement_ITSTPC; @@ -1166,11 +1166,12 @@ class V0PhotonCut : public TNamed float mMinPsiPair{-3.15}, mMaxPsiPair{+3.15}; float mMinPhivPair{0.f}, mMaxPhivPair{+3.15}; float mMinRxy{0.f}, mMaxRxy{180.f}; + float mMidRxyLow{-1.f}, mMidRxyHigh{-1.f}; float mMinCosPA{0.95}; float mMaxPCA{2.f}; float mMaxChi2KF{1e+10}; float mMaxMarginZ{7.f}; - std::function mMaxMeePsiPairDep{}; // max mee as a function of psipair + std::function mMaxMeePsiPairDep; // max mee as a function of psipair bool mIsOnWwireIB{false}; bool mIsOnWwireOB{false}; bool mRejectITSib{false}; @@ -1178,7 +1179,7 @@ class V0PhotonCut : public TNamed float mMinV0DistSquared{1.}; // for TooCloseV0Cut: cut value when using squared distance between conversion points float mDeltaR{6.}; // for TooCloseV0Cut: V0PhotonCut::TooCloseCuts::kRadAndAngle when deltaR < this -> compare chi2 float mMinOpeningAngle{0.02}; // for TooCloseV0Cut: V0PhotonCut::TooCloseCuts::kRadAndAngle when opening angle < this -> compare chi2 - mutable std::vector mRejectMask{}; + mutable std::vector mRejectMask; // ML cuts bool mApplyMlCuts{false}; @@ -1200,9 +1201,9 @@ class V0PhotonCut : public TNamed std::vector mCutsMlFlat{std::vector{0.5}}; o2::analysis::EmMlResponsePCM* mEmMlResponse{nullptr}; mutable bool mIsSelectedMl{false}; - mutable std::vector mOutputML{}; - mutable std::vector mMlInputFeatures{}; - mutable std::span mMlBDTScores{}; + mutable std::vector mOutputML; + mutable std::vector mMlInputFeatures; + mutable std::span mMlBDTScores; CentType mCentralityTypeMl{CentType::CentFT0C}; mutable V0PhotonCandidate mV0PhotonForMl; @@ -1224,9 +1225,9 @@ class V0PhotonCut : public TNamed float mMinChi2PerClusterITS{-1e10f}, mMaxChi2PerClusterITS{1e10f}; // max its fit chi2 per ITS cluster float mMinMeanClusterSizeITS{-1e10f}, mMaxMeanClusterSizeITS{1e10f}; // max x cos(Lmabda) - float mMaxDcaXY{1e10f}; // max dca in xy plane - float mMaxDcaZ{1e10f}; // max dca in z direction - std::function mMaxDcaXYPtDep{}; // max dca in xy plane as function of pT + float mMaxDcaXY{1e10f}; // max dca in xy plane + float mMaxDcaZ{1e10f}; // max dca in z direction + std::function mMaxDcaXYPtDep; // max dca in xy plane as function of pT bool mRequireITSTPC{false}; bool mRequireITSonly{false}; bool mRequireTPConly{false}; @@ -1238,8 +1239,6 @@ class V0PhotonCut : public TNamed bool mDoQA{false}; ///< flag to decide if QA should be done or not mutable uint nAccV0PerColl{0}; ///< running number of accepted v0 photons per collision used for QA mutable int currentCollID{-1}; ///< running collision ID of v0 photon used for QA - - ClassDef(V0PhotonCut, 5); }; #endif // PWGEM_PHOTONMESON_CORE_V0PHOTONCUT_H_ diff --git a/PWGEM/PhotonMeson/DataModel/ConversionMl.h b/PWGEM/PhotonMeson/DataModel/ConversionMl.h new file mode 100644 index 00000000000..656a597829f --- /dev/null +++ b/PWGEM/PhotonMeson/DataModel/ConversionMl.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ConversionMl.h +/// \brief This header provides the table definitions to store cluster pairs to analyze and potentially tag late conversions +/// \author Marvin Hemmer (marvin.hemmer@cern.ch) - Goethe University Frankfurt + +#ifndef PWGEM_PHOTONMESON_DATAMODEL_CONVERSIONML_H_ +#define PWGEM_PHOTONMESON_DATAMODEL_CONVERSIONML_H_ + +#include + +#include + +namespace o2::aod +{ + +namespace convtag +{ +DECLARE_SOA_COLUMN(PMEvent, pmevent, int); //! index column to the pmevent +DECLARE_SOA_COLUMN(Minv, minv, float); //! invariant mass of the pair +DECLARE_SOA_COLUMN(HarmonicET, harmonicET, float); //! ET1 * ET2 / (ET1 + ET2) in GeV +DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); //! difference in eta of the two clusters +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); //! difference in phi of the two clusters in rad +DECLARE_SOA_COLUMN(Phiv, phiv, float); //! angle between plane spanned by the two clusters and the plane between the B-field direction the their combined momentum +DECLARE_SOA_COLUMN(E1, e1, float); //! energy of first cluster in GeV +DECLARE_SOA_COLUMN(E2, e2, float); //! energy of second cluster in GeV +DECLARE_SOA_COLUMN(M021, m021, float); //! M02 of first cluster +DECLARE_SOA_COLUMN(M022, m022, float); //! M02 of second cluster +DECLARE_SOA_COLUMN(Time1, time1, float); //! time of first cluster in ns +DECLARE_SOA_COLUMN(Time2, time2, float); //! time of second cluster in ns +DECLARE_SOA_COLUMN(NCells1, nCells1, uint8_t); //! NCells of first cluster +DECLARE_SOA_COLUMN(NCells2, nCells2, uint8_t); //! NCells of second cluster +DECLARE_SOA_COLUMN(TruthLabel, truthLabel, int8_t); //! truth label for ML training -- see TruthClass enum for the mapping +DECLARE_SOA_COLUMN(CentOrMult, centOrMult, float); //! centrality or multiplicity value of the collision +DECLARE_SOA_COLUMN(Purity1, purity1, float); // leading-contributor amplitude fraction, cluster 1 +DECLARE_SOA_COLUMN(Purity2, purity2, float); // leading-contributor amplitude fraction, cluster 2 + +} // namespace convtag +DECLARE_SOA_TABLE(ConvTagCandidates, "AOD", "CONVTAGCAND", + convtag::PMEvent, + convtag::Minv, convtag::HarmonicET, convtag::DeltaEta, convtag::DeltaPhi, convtag::Phiv, + convtag::E1, convtag::E2, convtag::M021, convtag::M022, + convtag::Time1, convtag::Time2, convtag::NCells1, convtag::NCells2, + convtag::TruthLabel, convtag::CentOrMult); + +DECLARE_SOA_TABLE_VERSIONED(ConvTagCandidates_001, "AOD", "CONVTAGCAND", 1, + convtag::PMEvent, + convtag::Minv, convtag::HarmonicET, convtag::DeltaEta, convtag::DeltaPhi, convtag::Phiv, + convtag::E1, convtag::E2, convtag::M021, convtag::M022, + convtag::Time1, convtag::Time2, convtag::NCells1, convtag::NCells2, convtag::Purity1, convtag::Purity2, + convtag::TruthLabel, convtag::CentOrMult); + +} // namespace o2::aod + +#endif // PWGEM_PHOTONMESON_DATAMODEL_CONVERSIONML_H_ diff --git a/PWGEM/PhotonMeson/DataModel/EventTables.h b/PWGEM/PhotonMeson/DataModel/EventTables.h index 05e1541493b..dd24a33457b 100644 --- a/PWGEM/PhotonMeson/DataModel/EventTables.h +++ b/PWGEM/PhotonMeson/DataModel/EventTables.h @@ -10,51 +10,74 @@ // or submit itself to any jurisdiction. /// \file EventTables.h -/// \brief This header provides the table definitions to store photon meson event tables +/// \brief This header provides the table definitions to store photon meson event tables and ccdb tables /// \author Marvin Hemmer (marvin.hemmer@cern.ch) - Goethe University Frankfurt +#ifndef PWGEM_PHOTONMESON_DATAMODEL_EVENTTABLES_H_ +#define PWGEM_PHOTONMESON_DATAMODEL_EVENTTABLES_H_ + #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/EventSelection.h" +#include +#include #include -#include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) -#include -#include +#include // for BIT -#ifndef PWGEM_PHOTONMESON_DATAMODEL_EVENTTABLES_H_ -#define PWGEM_PHOTONMESON_DATAMODEL_EVENTTABLES_H_ +#include +#include +#include namespace o2::aod { namespace pmevsel { -// Event selection criteria. See O2Physics/Common/CCDB/EventSelectionParams.h -enum EventSelectionFlags { - kIsTriggerTVX = 0, // FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level - kNoITSROFrameBorder, // bunch crossing is far from ITS RO Frame border - kNoTimeFrameBorder, // bunch crossing is far from Time Frame borders - kNoSameBunchPileup, // reject collisions in case of pileup with another collision in the same foundBC - kIsGoodZvtxFT0vsPV, // small difference between z-vertex from PV and from FT0 - kIsVertexITSTPC, // at least one ITS-TPC track (reject vertices built from ITS-only tracks) - kIsVertexTOFmatched, // at least one of vertex contributors is matched to TOF - kIsVertexTRDmatched, // at least one of vertex contributors is matched to TRD - kNoCollInTimeRangeNarrow, // no other collisions in specified time range (narrower than Strict) - kNoCollInTimeRangeStrict, // no other collisions in specified time range - kNoCollInTimeRangeStandard, // no other collisions in specified time range with per-collision multiplicity above threshold - kNoCollInRofStrict, // no other collisions in this Readout Frame - kNoCollInRofStandard, // no other collisions in this Readout Frame with per-collision multiplicity above threshold - kNoHighMultCollInPrevRof, // veto an event if FT0C amplitude in previous ITS ROF is above threshold - kIsGoodITSLayer3, // number of inactive chips on ITS layer 3 is below maximum allowed value - kIsGoodITSLayer0123, // numbers of inactive chips on ITS layers 0-3 are below maximum allowed values - kIsGoodITSLayersAll, // numbers of inactive chips on all ITS layers are below maximum allowed values - kNsel // counter + +DECLARE_SOA_DYNAMIC_COLUMN(Sel8, sel8, [](uint64_t selection_bit, int runNumber) -> bool { + return (selection_bit & BIT(o2::aod::evsel::kIsTriggerTVX)) && (selection_bit & BIT(o2::aod::evsel::kNoTimeFrameBorder)) && (runNumber < 568873 ? (selection_bit & BIT(o2::aod::evsel::kNoITSROFrameBorder)) : true); // o2-linter: disable=magic-number (hard-coded run range to indicate 2026 datataking) +}); + +// Enum used for filling table for event-norm purposes +enum EventAcceptanceBits { + kAll = 0, // o2-linter: disable=magic-number (enum) + kHasMCColl, + kGoodZVtx, + kIsFT0AND, + kNoTFB, + kITSROFB, + kNoSameBunchPileUp, + kGoodZVtxFTOPV, + kNoCollInTimeRange, + kGoodTrackOccupancy, + kGoodFT0Occupancy, + kTVXInEMC, + kGoodCent, + kGoodRCT, + kGoodSel8, + kSize }; -DECLARE_SOA_BITMAP_COLUMN(Selection, selection, 32); //! Bitmask of selection flags -DECLARE_SOA_DYNAMIC_COLUMN(Sel8, sel8, [](uint32_t selection_bit) -> bool { return (selection_bit & BIT(o2::aod::pmevsel::kIsTriggerTVX)) && (selection_bit & BIT(o2::aod::pmevsel::kNoTimeFrameBorder)) && (selection_bit & BIT(o2::aod::pmevsel::kNoITSROFrameBorder)); }); +const std::vector vecEventAccBitNames = { + "all", + "has MC coll", + "good zVtx", + "is FT0AND", + "No TFB", + "ITS ROFB", + "Same bunch Pileup", + "ZVtxFT0PV", + "No coll in time range", + "good track occi", + "Good FT0 occupancy", + "kTVXinEMC", + "Good centrality", + "Good RCT", + "Good Sel8"}; } // namespace pmevsel @@ -62,16 +85,57 @@ namespace pmevent { DECLARE_SOA_COLUMN(CollisionId, collisionId, int); -DECLARE_SOA_DYNAMIC_COLUMN(Sel8, sel8, [](uint64_t selection_bit) -> bool { return (selection_bit & BIT(o2::aod::evsel::kIsTriggerTVX)) && (selection_bit & BIT(o2::aod::evsel::kNoTimeFrameBorder)) && (selection_bit & BIT(o2::aod::evsel::kNoITSROFrameBorder)); }); } // namespace pmevent DECLARE_SOA_TABLE(PMEvents, "AOD", "PMEVENT", //! Main event information table o2::soa::Index<>, pmevent::CollisionId, bc::RunNumber, bc::GlobalBC, evsel::Selection, evsel::Rct, timestamp::Timestamp, collision::PosZ, - collision::NumContrib, evsel::NumTracksInTimeRange, evsel::SumAmpFT0CInTimeRange, pmevent::Sel8); + collision::NumContrib, evsel::NumTracksInTimeRange, evsel::SumAmpFT0CInTimeRange, pmevsel::Sel8); using PMEvent = PMEvents::iterator; +// Tables for event selection and event bookkeeping + +DECLARE_SOA_COLUMN(IsSelected, isSelected, bool); //! MB event selection info +DECLARE_SOA_TABLE(PMEvSels, "AOD", "PMEVSEL", //! joinable to o2::aod::Collisions + IsSelected); +using PMEvSel = PMEvSels::iterator; + +DECLARE_SOA_COLUMN(EventSelectionBit, eventSelectionBit, std::vector); //! Event selection info stored in binned data for each DF +DECLARE_SOA_TABLE(PMEvSelBits, "AOD", "PMEVSELBITS", //! produces binned data that can be loaded in analysis task for event counting + EventSelectionBit); +using PMEvSelBit = PMEvSelBits::iterator; + +namespace ccdbPcm +{ +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +DECLARE_SOA_CCDB_COLUMN(GRPMagField, grpMagField, o2::parameters::GRPMagField, "GLO/Config/GRPMagField"); //! +} // namespace ccdbPcm + +/// Full table — join with aod::BCsWithTimestamps to obtain all four objects. +DECLARE_SOA_TIMESTAMPED_TABLE(PcmObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 0, "PCMOBJECTS", //! + ccdbPcm::GRPMagField); + +namespace em::ccdbMagField +{ +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +DECLARE_SOA_CCDB_COLUMN(GRPMagField, grpMagField, o2::parameters::GRPMagField, "GLO/Config/GRPMagField"); //! +} // namespace em::ccdbMagField + +/// Full table — join with aod::BCsWithTimestamps to obtain all four objects. +DECLARE_SOA_TIMESTAMPED_TABLE(EmMagFields, aod::PMEvents, o2::aod::timestamp::Timestamp, 0, "EMMAGFIELDS", //! + em::ccdbMagField::GRPMagField); + +namespace em::ccdbEmcal +{ +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +DECLARE_SOA_CCDB_COLUMN(BadChannelMap, badChannelMap, o2::emcal::BadChannelMap, "EMC/Calib/BadChannelMap"); //! EMCal BadChannelMap for EmEvents +} // namespace em::ccdbEmcal + +/// Full table — join with aod::BCsWithTimestamps to obtain all four objects. +DECLARE_SOA_TIMESTAMPED_TABLE(EmEmcalObjects, aod::PMEvents, o2::aod::timestamp::Timestamp, 0, "EMEMCALOBJECTS", //! + em::ccdbEmcal::BadChannelMap); + } // namespace o2::aod #endif // PWGEM_PHOTONMESON_DATAMODEL_EVENTTABLES_H_ diff --git a/PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h b/PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h index 79219f35fa4..b989535162b 100644 --- a/PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h +++ b/PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h @@ -19,10 +19,10 @@ #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include #include #include +#include #include #include #include @@ -46,7 +46,7 @@ enum Observable { }; // Values in tables are stored in downscaled format to save disk space -const float downscalingFactors[nObservables]{ +constexpr std::array downscalingFactors{ 1E0, // Cluster definition 1E3, // Cluster energy 1E4, // Cluster eta @@ -120,11 +120,11 @@ DECLARE_SOA_INDEX_COLUMN(MinCluster, minCluster); //! } // namespace mintm DECLARE_SOA_TABLE(MinMTracks, "AOD", "MINMTRACK", //! - mintm::MinClusterId, mincluster::StoredDeltaPhi, mincluster::StoredDeltaEta, mincluster::StoredP, mincluster::StoredPt, + o2::soa::Index<>, mintm::MinClusterId, mincluster::StoredDeltaPhi, mincluster::StoredDeltaEta, mincluster::StoredP, mincluster::StoredPt, mincluster::DeltaPhi, mincluster::DeltaEta, mincluster::TrackP, mincluster::TrackPt); DECLARE_SOA_TABLE(MinMSTracks, "AOD", "MINMSTRACK", //! - mintm::MinClusterId, mincluster::StoredDeltaPhi, mincluster::StoredDeltaEta, mincluster::StoredP, mincluster::StoredPt, + o2::soa::Index<>, mintm::MinClusterId, mincluster::StoredDeltaPhi, mincluster::StoredDeltaEta, mincluster::StoredP, mincluster::StoredPt, mincluster::DeltaPhi, mincluster::DeltaEta, mincluster::TrackP, mincluster::TrackPt); } // namespace o2::aod diff --git a/PWGEM/PhotonMeson/DataModel/bcWiseTables.h b/PWGEM/PhotonMeson/DataModel/bcWiseTables.h index 4f64bf08fb0..d2eed4b2f58 100644 --- a/PWGEM/PhotonMeson/DataModel/bcWiseTables.h +++ b/PWGEM/PhotonMeson/DataModel/bcWiseTables.h @@ -8,19 +8,21 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// + /// \file bcWiseTables.h -/// /// \brief This header provides the table definitions to store very lightweight EMCal clusters per BC -/// /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) - Goethe University Frankfurt -/// #ifndef PWGEM_PHOTONMESON_DATAMODEL_BCWISETABLES_H_ #define PWGEM_PHOTONMESON_DATAMODEL_BCWISETABLES_H_ +#include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + +#include #include #include #include @@ -48,7 +50,7 @@ enum Observable { }; // Values in tables are stored in downscaled format to save disk space -const float downscalingFactors[nObservables]{ +constexpr std::array downscalingFactors{ 1E0, // Cluster definition 1E3, // Cluster energy 1E4, // Cluster eta @@ -97,6 +99,17 @@ DECLARE_SOA_DYNAMIC_COLUMN(ZVtx, zVtx, [](int16_t storedzvtx) -> float { return DECLARE_SOA_TABLE(BCWiseCollisions, "AOD", "BCWISECOLL", //! table of skimmed EMCal clusters o2::soa::Index<>, BCWiseBCId, bcwisecollision::StoredZVtx, bcwisecollision::ZVtx); +// ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); +namespace bcwiseCcdb +{ +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +DECLARE_SOA_CCDB_COLUMN(GrpLhcFData, grpLhcFData, o2::parameters::GRPLHCIFData, "GLO/Config/GRPLHCIF"); //! GRPLHCIFData for BC Timestamps +} // namespace bcwiseCcdb + +/// Full table — join with aod::BCsWithTimestamps to obtain all four objects. +DECLARE_SOA_TIMESTAMPED_TABLE(BcLhcIfObjects, aod::Timestamps, o2::aod::timestamp::Timestamp, 0, "BCLHCIFOBJECTS", //! + bcwiseCcdb::GrpLhcFData); + namespace bcwisecluster { DECLARE_SOA_COLUMN(StoredDefinition, storedDefinition, int8_t); //! cluster definition, see EMCALClusterDefinition.h diff --git a/PWGEM/PhotonMeson/DataModel/gammaTables.h b/PWGEM/PhotonMeson/DataModel/gammaTables.h index 9ef4b45727b..f0e0bdf8d85 100644 --- a/PWGEM/PhotonMeson/DataModel/gammaTables.h +++ b/PWGEM/PhotonMeson/DataModel/gammaTables.h @@ -14,7 +14,7 @@ /// \author D. Sekihata, daiki.sekihata@cern.ch; Marvin Hemmer (marvin.hemmer@cern.ch) - Goethe University Frankfurt #include "PWGEM/Dilepton/DataModel/dileptonTables.h" -#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" // IWYU pragma: keep (needed) #include "Common/Core/RecoDecay.h" #include "Common/DataModel/CaloClusters.h" @@ -23,7 +23,6 @@ #include "Common/DataModel/TrackSelectionTables.h" #include -#include #include #include @@ -102,23 +101,31 @@ using V0LegMCLabel = V0LegMCLabels::iterator; // 3. EMEMCClusterMCLabels: Vector of EM MC particle ID of largest contributor to cluster namespace emcclustermclabel { -DECLARE_SOA_ARRAY_INDEX_COLUMN(McParticle, emmcparticle) //! +DECLARE_SOA_ARRAY_INDEX_COLUMN(McParticle, mcParticle) //! Array of indice of McParticles +DECLARE_SOA_COLUMN(Amplitude, amplitude, std::vector); //! vector of aplitudes stored in cluster. Ordering is identical to the ordering of EMMCParticleId } // namespace emcclustermclabel // NOTE: MC labels. This table has one vector of global mc particle ids for each reconstructed emc cluster (joinable with emccluster table) DECLARE_SOA_TABLE(EMCClusterMCLabels, "AOD", "EMCClsMCLABEL", //! emcclustermclabel::McParticleIds); -using EMCClusterMCLabel = EMCClusterMCLabels::iterator; + +DECLARE_SOA_TABLE_VERSIONED(EMCClusterMCLabels_001, "AOD", "EMCClsMCLABEL", 1, //! + emcclustermclabel::McParticleIds, emcclustermclabel::Amplitude); +using EMCClusterMCLabel = EMCClusterMCLabels_001::iterator; namespace ememcclustermclabel { -DECLARE_SOA_ARRAY_INDEX_COLUMN(EMMCParticle, emmcparticle); //! +DECLARE_SOA_ARRAY_INDEX_COLUMN(EMMCParticle, emmcparticle); //! Array of indice of EmMcParticles +DECLARE_SOA_COLUMN(Amplitude, amplitude, std::vector); //! vector of aplitudes stored in cluster. Ordering is identical to the ordering of EMMCParticleId } // namespace ememcclustermclabel // NOTE: MC labels. This table has a vector of entries for each reconstructed emc cluster (joinable with emccluster table) DECLARE_SOA_TABLE(EMEMCClusterMCLabels, "AOD", "EMEMCClsMCLABEL", //! ememcclustermclabel::EMMCParticleIds); -using EMEMCClusterMCLabel = EMEMCClusterMCLabels::iterator; + +DECLARE_SOA_TABLE_VERSIONED(EMEMCClusterMCLabels_001, "AOD", "EMEMCClsMCLABEL", 1, //! + ememcclustermclabel::EMMCParticleIds, ememcclustermclabel::Amplitude); +using EMEMCClusterMCLabel = EMEMCClusterMCLabels_001::iterator; namespace v0leg { @@ -146,9 +153,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(MeanClusterSizeITS, meanClusterSizeITS, [](uint32_t i } if (nl > 0) { return static_cast(total_cluster_size) / static_cast(nl); - } else { - return 0; } + return 0; }); DECLARE_SOA_DYNAMIC_COLUMN(MeanClusterSizeITSib, meanClusterSizeITSib, [](uint32_t itsClusterSizes) -> float { int total_cluster_size = 0, nl = 0; @@ -161,9 +167,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(MeanClusterSizeITSib, meanClusterSizeITSib, [](uint32 } if (nl > 0) { return static_cast(total_cluster_size) / static_cast(nl); - } else { - return 0; } + return 0; }); DECLARE_SOA_DYNAMIC_COLUMN(MeanClusterSizeITSob, meanClusterSizeITSob, [](uint32_t itsClusterSizes) -> float { int total_cluster_size = 0, nl = 0; @@ -176,9 +181,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(MeanClusterSizeITSob, meanClusterSizeITSob, [](uint32 } if (nl > 0) { return static_cast(total_cluster_size) / static_cast(nl); - } else { - return 0; } + return 0; }); } // namespace v0leg DECLARE_SOA_TABLE(V0Legs_000, "AOD", "V0LEG", //! @@ -238,16 +242,56 @@ using V0Legs = V0Legs_001; // iterators using V0Leg = V0Legs::iterator; +// copy of V0Legs table +DECLARE_SOA_TABLE(V0LegsTmp, "AOD", "V0LEGTMP", //! + o2::soa::Index<>, v0leg::CollisionId, v0leg::TrackId, v0leg::Sign, + v0leg::Px, v0leg::Py, v0leg::Pz, + track::DcaXY, track::DcaZ, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, + track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + track::ITSClusterSizes, track::ITSChi2NCl, track::DetectorMap, + + // dynamic column + v0leg::P, + v0leg::Pt, + v0leg::Eta, + v0leg::Phi, + v0leg::Tgl, + + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF, + v0leg::MeanClusterSizeITS, + v0leg::MeanClusterSizeITSib, + v0leg::MeanClusterSizeITSob); +// iterators +using V0LegTmp = V0LegsTmp::iterator; + DECLARE_SOA_TABLE_VERSIONED(V0LegsXYZ_000, "AOD", "V0LEGXYZ", 0, track::X, track::Y, track::Z); using V0LegsXYZ = V0LegsXYZ_000; // iterators using V0LegXYZ = V0LegsXYZ::iterator; +// copy of V0Legs table +DECLARE_SOA_TABLE(V0LegsXYZTmp, "AOD", "V0LEGXYZTMP", track::X, track::Y, track::Z); +// iterators +using V0LegXYZTmp = V0LegsXYZTmp::iterator; + DECLARE_SOA_TABLE_VERSIONED(V0LegsDeDxMC_000, "AOD", "V0LEGDEDXMC", 0, mcpidtpc::DeDxTunedMc, o2::soa::Marker<2>); using V0LegsDeDxMC = V0LegsDeDxMC_000; // iterators using V0LegDeDxMC = V0LegsDeDxMC::iterator; +// copy of V0Legs table +DECLARE_SOA_TABLE_VERSIONED(V0LegsDeDxMCTmp, "AOD", "V0LEGDEDXMCTMP", 0, mcpidtpc::DeDxTunedMc, o2::soa::Marker<2>); +// iterators +using V0LegDeDxMCTmp = V0LegsDeDxMCTmp::iterator; + namespace emevent { DECLARE_SOA_COLUMN(NgPCM, ngpcm, int); @@ -343,6 +387,27 @@ using V0PhotonsKF = V0PhotonsKF_001; // iterators using V0PhotonKF = V0PhotonsKF::iterator; +// Clone of V0PhotonKF table for intermediate processing needed for dalitz +DECLARE_SOA_TABLE(V0PhotonsKFTmp, "AOD", "V0PHOTONKFTMP", //! + o2::soa::Index<>, v0photonkf::CollisionId, v0photonkf::V0Id, v0photonkf::PosTrackId, v0photonkf::NegTrackId, + v0photonkf::Vx, v0photonkf::Vy, v0photonkf::Vz, + v0photonkf::Px, v0photonkf::Py, v0photonkf::Pz, + v0photonkf::MGamma, + v0photonkf::DCAxyToPV, v0photonkf::DCAzToPV, + v0photonkf::CosPA, v0photonkf::CosPAXY, v0photonkf::CosPARZ, v0photonkf::PCA, + v0photonkf::Alpha, v0photonkf::QtArm, + v0photonkf::ChiSquareNDF, + + // dynamic column + v0photonkf::E, + v0photonkf::Pt, + v0photonkf::Eta, + v0photonkf::Phi, + v0photonkf::P, + v0photonkf::V0Radius); +// iterators +using V0PhotonKFTmp = V0PhotonsKFTmp::iterator; + DECLARE_SOA_TABLE(V0KFEMEventIds_000, "AOD", "V0KFEMEVENTID", v0photonkf::EMEventId); // To be joined with V0PhotonsKF table at analysis level. DECLARE_SOA_TABLE_VERSIONED(V0KFEMEventIds_001, "AOD", "V0KFEMEVENTID", 1, v0photonkf::PMEventId); // To be joined with V0PhotonsKF table at analysis level. using V0KFEMEventIds = V0KFEMEventIds_001; @@ -419,6 +484,45 @@ using EMPrimaryElectronsFromDalitz = EMPrimaryElectronsFromDalitz_001; // iterators using EMPrimaryElectronFromDalitz = EMPrimaryElectronsFromDalitz::iterator; +// Table that is a clone of EMPrimaryELectronsFromDalitz but needed for event selection purposes +DECLARE_SOA_TABLE(EMPrimaryElectronsFromDalitzTmp, "AOD", "EMPRIMARYELDATMP", //! + o2::soa::Index<>, emprimaryelectron::CollisionId, + emprimaryelectron::TrackId, emprimaryelectron::Sign, + track::Pt, track::Eta, track::Phi, track::DcaXY, track::DcaZ, track::CYY, track::CZY, track::CZZ, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, + track::TPCChi2NCl, track::TPCInnerParam, + track::TPCSignal, pidtpc::TPCNSigmaEl, pidtpc::TPCNSigmaPi, + pidtofbeta::Beta, pidtof::TOFNSigmaEl, + track::ITSClusterSizes, track::ITSChi2NCl, track::TOFChi2, track::DetectorMap, + + // dynamic column + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::TPCFractionSharedCls, + track::HasITS, track::HasTPC, track::HasTRD, track::HasTOF, + + emprimaryelectron::Signed1Pt, + emprimaryelectron::P, + emprimaryelectron::Px, + emprimaryelectron::Py, + emprimaryelectron::Pz, + emprimaryelectron::Tgl, + emprimaryelectron::MeanClusterSizeITS, + emprimaryelectron::MeanClusterSizeITSib, + emprimaryelectron::MeanClusterSizeITSob); + +// iterators +using EMPrimaryElectronFromDalitzTmp = EMPrimaryElectronsFromDalitzTmp::iterator; + +// copy of EMPrimaryElectronDeDxMC to use as intermediate step for Dalitz analysis +DECLARE_SOA_TABLE(EMPrimaryElectronsDeDxMCTmp, "AOD", "EMELDEDXMCTMP", mcpidtpc::DeDxTunedMc, o2::soa::Marker<1>); +using EMPrimaryElectronsDeDxMCTmp = EMPrimaryElectronsDeDxMCTmp; +// iterators +using EMPrimaryElectronDeDxMCTmp = EMPrimaryElectronsDeDxMCTmp::iterator; + namespace emprimaryelectronda { DECLARE_SOA_INDEX_COLUMN(EMEvent, emevent); //! @@ -441,6 +545,12 @@ DECLARE_SOA_TABLE(V0PhotonsPhiVPsi, "AOD", "V0PHOTONPHIVPSI", //! // iterators using V0PhotonsPhiVPsi = V0PhotonsPhiVPsi; +// clone for temporary table +DECLARE_SOA_TABLE(V0PhotonsPhiVPsiTmp, "AOD", "V0PHOTONPHIVTMP", //! + o2::soa::Index<>, v0photonsphivpsi::PhiV, v0photonsphivpsi::PsiPair); +// iterators +using V0PhotonsPhiVPsiTmp = V0PhotonsPhiVPsiTmp; + namespace dalitzee { DECLARE_SOA_INDEX_COLUMN(EMEvent, emevent); //! @@ -643,10 +753,10 @@ DECLARE_SOA_INDEX_COLUMN(EmEmcCluster, emEmcCluster); //! } // namespace trackmatching DECLARE_SOA_TABLE(EmEmcMTracks, "AOD", "EMEMCMTRACK", //! - trackmatching::EmEmcClusterId, emctm::DeltaPhi, emctm::DeltaEta, emctm::TrackP, emctm::TrackPt); + o2::soa::Index<>, trackmatching::EmEmcClusterId, emctm::DeltaPhi, emctm::DeltaEta, emctm::TrackP, emctm::TrackPt); DECLARE_SOA_TABLE(EmEmcMSTracks, "AOD", "EMEMCMSTRACK", //! - trackmatching::EmEmcClusterId, emctm::DeltaPhi, emctm::DeltaEta, emctm::TrackP, emctm::TrackPt); + o2::soa::Index<>, trackmatching::EmEmcClusterId, emctm::DeltaPhi, emctm::DeltaEta, emctm::TrackP, emctm::TrackPt); DECLARE_SOA_TABLE(EMCEMEventIds_000, "AOD", "EMCEMEVENTID", emccluster::EMEventId); // To be joined with SkimEMCClusters table at analysis level. DECLARE_SOA_TABLE_VERSIONED(EMCEMEventIds_001, "AOD", "EMCEMEVENTID", 1, emccluster::PMEventId); // To be joined with SkimEMCClusters table at analysis level. @@ -727,6 +837,7 @@ DECLARE_SOA_COLUMN(OmegaMBWeight, omegaMBWeight, float); DECLARE_SOA_TABLE(V0PhotonOmegaMBWeights, "AOD", "V0PHOTONMBW", v0photonMBweights::OmegaMBWeight); // store MB weights. To be joined with V0PhotonsKF table at analysis level. using V0PhotonOmegaMBWeight = V0PhotonOmegaMBWeights::iterator; + } // namespace o2::aod #endif // PWGEM_PHOTONMESON_DATAMODEL_GAMMATABLES_H_ diff --git a/PWGEM/PhotonMeson/DataModel/mcV0Tables.h b/PWGEM/PhotonMeson/DataModel/mcV0Tables.h index 95b560a7501..dc2d99d7d2f 100644 --- a/PWGEM/PhotonMeson/DataModel/mcV0Tables.h +++ b/PWGEM/PhotonMeson/DataModel/mcV0Tables.h @@ -15,7 +15,7 @@ #ifndef PWGEM_PHOTONMESON_DATAMODEL_MCV0TABLES_H_ #define PWGEM_PHOTONMESON_DATAMODEL_MCV0TABLES_H_ -#include +#include #include diff --git a/PWGEM/PhotonMeson/Legacy/CMakeLists.txt b/PWGEM/PhotonMeson/Legacy/CMakeLists.txt new file mode 100644 index 00000000000..0fae049d13a --- /dev/null +++ b/PWGEM/PhotonMeson/Legacy/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(skimmer-gamma-conversion + SOURCES skimmerGammaConversion.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore KFParticle::KFParticle + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(skimmer-gamma-conversion-truthonlymc + SOURCES skimmerGammaConversionTruthOnlyMc.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(skimmer-dalitz-ee + SOURCES skimmerDalitzEE.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(create-pcm + SOURCES createPCM.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(gammaconversions + SOURCES gammaConversions.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(gammaconversionstruthonlymc + SOURCES gammaConversionsTruthOnlyMc.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(material-budget + SOURCES MaterialBudget.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(material-budget-mc + SOURCES MaterialBudgetMC.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pcm-qc-mc + SOURCES pcmQCMC.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) diff --git a/PWGEM/PhotonMeson/Tasks/MaterialBudget.cxx b/PWGEM/PhotonMeson/Legacy/MaterialBudget.cxx similarity index 95% rename from PWGEM/PhotonMeson/Tasks/MaterialBudget.cxx rename to PWGEM/PhotonMeson/Legacy/MaterialBudget.cxx index 2ecfd14711d..5baa0ebf6bc 100644 --- a/PWGEM/PhotonMeson/Tasks/MaterialBudget.cxx +++ b/PWGEM/PhotonMeson/Legacy/MaterialBudget.cxx @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -122,11 +123,11 @@ struct MaterialBudget { HistogramRegistry registry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view ItsClsNames[] = { + static constexpr std::array ItsClsNames{ "ITSCls0", "ITSCls1", "ITSCls2", "ITSCls3", "ITSCls4", "ITSCls5", "ITSCls6", "ITSCls7"}; - static constexpr std::string_view EventTypes[2] = {"before/", "after/"}; + static constexpr std::array EventTypes{"before/", "after/"}; Configurable cfgPlotBremsstrahlung{"cfgPlotBremsstrahlung", false, "produce plots to study Bremsstrahlung"}; Configurable cfgPlotResolution{"cfgPlotResolution", false, "produce plots to study resolution"}; @@ -136,7 +137,6 @@ struct MaterialBudget { Configurable cfgPlotMBCollisions{"cfgPlotMBCollisions", false, "produce plots to study material distribution if collision association is wrong"}; ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 2.0}, ""}; - const AxisSpec axisPt{binsPt, "#it{p}_{T} [GeV/c]"}; EMPhotonEventCut fEMEventCut; struct : ConfigurableGroup { @@ -317,7 +317,7 @@ struct MaterialBudget { void addhistograms() { - + const AxisSpec axisPt{binsPt, "#it{p}_{T} [GeV/c]"}; auto hCollisionCounter = registry.add("Event/before/hCollisionCounter", "collision counter;;Number of events", kTH1F, {{10, 0.5, 10.5}}, false); hCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); hCollisionCounter->GetXaxis()->SetBinLabel(2, "No TF border"); @@ -608,43 +608,59 @@ struct MaterialBudget { template void fillITSClsNeg(HistogramRegistry& reg, float value) { - if constexpr (N == 0) // o2-linter: disable=magic-number (just numbers for ITS cluster) + if constexpr (N == 0) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls0"), value); - if constexpr (N == 1) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 1) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls1"), value); - if constexpr (N == 2) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 2) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls2"), value); - if constexpr (N == 3) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 3) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls3"), value); - if constexpr (N == 4) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 4) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls4"), value); - if constexpr (N == 5) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 5) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls5"), value); - if constexpr (N == 6) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 6) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls6"), value); - if constexpr (N == 7) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 7) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Neg/ITSCls7"), value); + } } template void fillITSClsPos(HistogramRegistry& reg, float value) { - if constexpr (N == 0) // o2-linter: disable=magic-number (just numbers for ITS cluster) + if constexpr (N == 0) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls0"), value); - if constexpr (N == 1) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 1) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls1"), value); - if constexpr (N == 2) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 2) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls2"), value); - if constexpr (N == 3) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 3) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls3"), value); - if constexpr (N == 4) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 4) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls4"), value); - if constexpr (N == 5) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 5) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls5"), value); - if constexpr (N == 6) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 6) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls6"), value); - if constexpr (N == 7) // o2-linter: disable=magic-number (just numbers for ITS cluster) + } + if constexpr (N == 7) { // o2-linter: disable=magic-number (just numbers for ITS cluster) reg.fill(HIST("ITSHits/Pos/ITSCls7"), value); + } } template @@ -847,7 +863,7 @@ struct MaterialBudget { { for (const auto& collision : collisions) { - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -983,24 +999,33 @@ struct MaterialBudget { if (pdgNeg == kElectron && pdgPos == kPositron) { return 1; } - if (pdgNeg == kElectron && pdgPos == kPiPlus) + if (pdgNeg == kElectron && pdgPos == kPiPlus) { return 2; - if (pdgNeg == kPiMinus && pdgPos == kPositron) + } + if (pdgNeg == kPiMinus && pdgPos == kPositron) { return 3; - if (pdgNeg == kKMinus && pdgPos == kPositron) + } + if (pdgNeg == kKMinus && pdgPos == kPositron) { return 4; - if (pdgNeg == kElectron && pdgPos == kKPlus) + } + if (pdgNeg == kElectron && pdgPos == kKPlus) { return 5; - if (pdgNeg == kProtonBar && pdgPos == kProton) + } + if (pdgNeg == kProtonBar && pdgPos == kProton) { return 6; - if (pdgNeg == kKMinus && pdgPos == kKPlus) + } + if (pdgNeg == kKMinus && pdgPos == kKPlus) { return 7; - if (pdgNeg == kPiMinus && pdgPos == kProton) + } + if (pdgNeg == kPiMinus && pdgPos == kProton) { return 8; - if (pdgNeg == kProtonBar && pdgPos == kPiPlus) + } + if (pdgNeg == kProtonBar && pdgPos == kPiPlus) { return 9; - if (pdgNeg == kMuonMinus && pdgPos == kMuonPlus) + } + if (pdgNeg == kMuonMinus && pdgPos == kMuonPlus) { return 10; + } return 11; } @@ -1017,7 +1042,7 @@ struct MaterialBudget { { for (const auto& collision : filteredCollisions) { - const float centralities[3] = { + const std::array centralities = { collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; @@ -1235,7 +1260,6 @@ struct MaterialBudget { registry.fill(HIST("Bremsstrahlung/relativeResoPtWOBrems"), trk.pt(), trk.pt() * std::sqrt(trk.c1Pt21Pt2())); - bool isFirst = true; for (const auto& dId : mc.daughtersIds()) { if (dId < 0 || dId >= mcparticles.size()) { continue; @@ -1259,9 +1283,7 @@ struct MaterialBudget { registry.fill(HIST("Bremsstrahlung/EnergyLossXY"), daughter.vx(), daughter.vy()); registry.fill(HIST("Bremsstrahlung/EnergyLossXYWeigh"), daughter.vx(), daughter.vy(), daughter.e()); - if (isFirst) { - registry.fill(HIST("Bremsstrahlung/relativeResoPtWBrems"), trk.pt(), trk.pt() * std::sqrt(trk.c1Pt21Pt2())); - } + registry.fill(HIST("Bremsstrahlung/relativeResoPtWBrems"), trk.pt(), trk.pt() * std::sqrt(trk.c1Pt21Pt2())); registry.fill(HIST("Bremsstrahlung/Sigma1PtVsR"), r, trk.sigma1Pt()); @@ -1279,7 +1301,6 @@ struct MaterialBudget { } registry.fill(HIST("Bremsstrahlung/NBrem"), nBrem); - isFirst = false; } } @@ -1289,8 +1310,8 @@ struct MaterialBudget { PROCESS_SWITCH(MaterialBudget, processBremsstrahlung, "process material budget", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/MaterialBudgetMC.cxx b/PWGEM/PhotonMeson/Legacy/MaterialBudgetMC.cxx similarity index 74% rename from PWGEM/PhotonMeson/Tasks/MaterialBudgetMC.cxx rename to PWGEM/PhotonMeson/Legacy/MaterialBudgetMC.cxx index 463bc9045a8..4db16a9e817 100644 --- a/PWGEM/PhotonMeson/Tasks/MaterialBudgetMC.cxx +++ b/PWGEM/PhotonMeson/Legacy/MaterialBudgetMC.cxx @@ -30,6 +30,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include #include #include #include @@ -44,7 +45,7 @@ #include #include -#include +#include #include #include #include @@ -83,7 +84,7 @@ struct MaterialBudgetMC { Configurable fConfigEMEventCut{"cfgEMEventCut", "minbias", "em event cut"}; // only 1 event cut per wagon EMPhotonEventCut fEMEventCut; - static constexpr std::string_view event_types[2] = {"before", "after"}; + static constexpr std::array event_types = {"before", "after"}; OutputObj fOutputEvent{"Event"}; OutputObj fOutputV0{"V0"}; @@ -109,40 +110,37 @@ struct MaterialBudgetMC { TString ev_cut_name = fConfigEMEventCut.value; fEMEventCut = *eventcuts::GetCut(ev_cut_name.Data()); - fOutputEvent.setObject(reinterpret_cast(fMainList->FindObject("Event"))); - fOutputV0.setObject(reinterpret_cast(fMainList->FindObject("V0"))); - fOutputPair.setObject(reinterpret_cast(fMainList->FindObject("Pair"))); - fOutputGen.setObject(reinterpret_cast(fMainList->FindObject("Generated"))); + fOutputEvent.setObject(dynamic_cast(fMainList->FindObject("Event"))); + fOutputV0.setObject(dynamic_cast(fMainList->FindObject("V0"))); + fOutputPair.setObject(dynamic_cast(fMainList->FindObject("Pair"))); + fOutputGen.setObject(dynamic_cast(fMainList->FindObject("Generated"))); } template - void add_pair_histograms(THashList* list_pair, const std::string pairname, TCuts1 const& tagcuts, TCuts2 const& probecuts, TCuts3 const& cuts3) + void add_pair_histograms(THashList* list_pair, std::string const& pairname, TCuts1 const& tagcuts, TCuts2 const& probecuts, TCuts3 const& cuts3) { for (const auto& tagcut : tagcuts) { for (const auto& probecut : probecuts) { - std::string cutname1 = tagcut.GetName(); - std::string cutname2 = probecut.GetName(); + std::string const& cutname1 = tagcut.getName(); + std::string const& cutname2 = probecut.getName(); - // if (cutname1 == cutname2) { - // continue; - // } - - THashList* list_pair_subsys = reinterpret_cast(list_pair->FindObject(pairname.data())); - std::string photon_cut_name = cutname1 + "_" + cutname2; + auto* list_pair_subsys = dynamic_cast(list_pair->FindObject(pairname.data())); + std::string photon_cut_name = cutname1; + photon_cut_name.append("_").append(cutname2); o2::aod::pwgem::photon::histogram::AddHistClass(list_pair_subsys, photon_cut_name.data()); - THashList* list_pair_subsys_photoncut = reinterpret_cast(list_pair_subsys->FindObject(photon_cut_name.data())); + auto* list_pair_subsys_photoncut = dynamic_cast(list_pair_subsys->FindObject(photon_cut_name.data())); for (const auto& cut3 : cuts3) { - std::string pair_cut_name = cut3.GetName(); + std::string const& pair_cut_name = cut3.getName(); o2::aod::pwgem::photon::histogram::AddHistClass(list_pair_subsys_photoncut, pair_cut_name.data()); - THashList* list_pair_subsys_paircut = reinterpret_cast(list_pair_subsys_photoncut->FindObject(pair_cut_name.data())); + auto* list_pair_subsys_paircut = dynamic_cast(list_pair_subsys_photoncut->FindObject(pair_cut_name.data())); o2::aod::pwgem::photon::histogram::DefineHistograms(list_pair_subsys_paircut, "material_budget_study", "Pair"); } // end of cut3 loop pair cut } // end of probecut loop } // end of tagcut loop } - static constexpr std::string_view pairnames[9] = {"PCMPCM", "PHOSPHOS", "EMCEMC", "PCMPHOS", "PCMEMC", "PCMDalitzEE", "PCMDalitzMuMu", "PHOSEMC", "DalitzEEDalitzEE"}; + static constexpr std::array pairnames = {"PCMPCM", "PHOSPHOS", "EMCEMC", "PCMPHOS", "PCMEMC", "PCMDalitzEE", "PCMDalitzMuMu", "PHOSEMC", "DalitzEEDalitzEE"}; void addhistograms() { fMainList->SetOwner(true); @@ -150,27 +148,27 @@ struct MaterialBudgetMC { // create sub lists first. o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Event"); - THashList* list_ev = reinterpret_cast(fMainList->FindObject("Event")); + auto* list_ev = dynamic_cast(fMainList->FindObject("Event")); o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Pair"); - THashList* list_pair = reinterpret_cast(fMainList->FindObject("Pair")); + auto* list_pair = dynamic_cast(fMainList->FindObject("Pair")); o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "V0"); - THashList* list_v0 = reinterpret_cast(fMainList->FindObject("V0")); + auto* list_v0 = dynamic_cast(fMainList->FindObject("V0")); // for V0s for (const auto& cut : fProbeCuts) { - const char* cutname = cut.GetName(); - THashList* list_v0_cut = o2::aod::pwgem::photon::histogram::AddHistClass(list_v0, cutname); + std::string cutname = cut.getName(); + THashList* list_v0_cut = o2::aod::pwgem::photon::histogram::AddHistClass(list_v0, cutname.c_str()); o2::aod::pwgem::photon::histogram::DefineHistograms(list_v0_cut, "material_budget_study", "V0"); } for (const auto& pairname : fPairNames) { LOGF(info, "Enabled pairs = %s", pairname.data()); - THashList* list_ev_pair = reinterpret_cast(o2::aod::pwgem::photon::histogram::AddHistClass(list_ev, pairname.data())); + THashList* list_ev_pair = o2::aod::pwgem::photon::histogram::AddHistClass(list_ev, pairname.data()); for (const auto& evtype : event_types) { - THashList* list_ev_type = reinterpret_cast(o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_pair, evtype.data())); + THashList* list_ev_type = o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_pair, evtype.data()); o2::aod::pwgem::photon::histogram::DefineHistograms(list_ev_type, "Event", evtype.data()); } @@ -183,48 +181,57 @@ struct MaterialBudgetMC { } // end of pair name loop o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Generated"); - THashList* list_gen = reinterpret_cast(fMainList->FindObject("Generated")); + auto* list_gen = dynamic_cast(fMainList->FindObject("Generated")); o2::aod::pwgem::photon::histogram::DefineHistograms(list_gen, "Generated", ""); } void DefineTagCuts() { - TString cutNamesStr = fConfigTagCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - fTagCuts.push_back(*pcmcuts::GetCut(cutname)); - } + if (fConfigTagCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigTagCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add tag cut : %s", cutname); + fTagCuts.push_back(*pcmcuts::GetCut(cutname)); } LOGF(info, "Number of Tag PCM cuts = %d", fTagCuts.size()); } void DefineProbeCuts() { - TString cutNamesStr = fConfigProbeCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - fProbeCuts.push_back(*pcmcuts::GetCut(cutname)); - } + if (fConfigProbeCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigProbeCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add probe cut : %s", cutname); + fProbeCuts.push_back(*pcmcuts::GetCut(cutname)); } LOGF(info, "Number of Probe PCM cuts = %d", fProbeCuts.size()); } void DefinePairCuts() { - TString cutNamesStr = fConfigPairCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - fPairCuts.push_back(*paircuts::GetCut(cutname)); - } + if (fConfigPairCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigPairCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add pair cut : %s", cutname); + fPairCuts.push_back(*paircuts::GetCut(cutname)); } LOGF(info, "Number of Pair cuts = %d", fPairCuts.size()); } @@ -240,12 +247,12 @@ struct MaterialBudgetMC { template void fillsinglephoton(TEvents const& collisions, TPhotons const& photons, TPreslice const& perCollision, TCuts const& cuts, TLegs const& /*legs*/, TMCParticles const& mcparticles, TMCEvents const&) { - THashList* list_ev_pair_before = static_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[0].data())); - THashList* list_ev_pair_after = static_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[1].data())); - THashList* list_v0 = static_cast(fMainList->FindObject("V0")); - double value[4] = {0.f}; + auto* list_ev_pair_before = dynamic_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[0].data())); + auto* list_ev_pair_after = dynamic_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[1].data())); + auto* list_v0 = dynamic_cast(fMainList->FindObject("V0")); + std::array value = {0.f}; for (const auto& collision : collisions) { - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities{collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -255,8 +262,8 @@ struct MaterialBudgetMC { continue; } o2::aod::pwgem::photon::histogram::FillHistClass(list_ev_pair_after, "", collision); - reinterpret_cast(list_ev_pair_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); - reinterpret_cast(list_ev_pair_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_pair_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_pair_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); auto photons_coll = photons.sliceBy(perCollision, collision.globalIndex()); for (const auto& cut : cuts) { @@ -288,7 +295,7 @@ struct MaterialBudgetMC { value[1] = photon.v0radius(); value[2] = RecoDecay::constrainAngle(phi_cp); value[3] = eta_cp; - reinterpret_cast(list_v0->FindObject(cut.GetName())->FindObject("hs_conv_point"))->Fill(value); + dynamic_cast(list_v0->FindObject(cut.getName().c_str())->FindObject("hs_conv_point"))->Fill(value.data()); } // end of photon loop } // end of cut loop @@ -299,10 +306,10 @@ struct MaterialBudgetMC { template void TruePairing(TEvents const& collisions, TPhotons1 const& photons1, TPhotons2 const& photons2, TPreslice1 const& perCollision1, TPreslice2 const& perCollision2, TCuts1 const& tagcuts, TCuts2 const& probecuts, TPairCuts const& paircuts, TLegs const& /*legs*/, TMCParticles const& mcparticles, TMCEvents const&) { - THashList* list_pair_ss = static_cast(fMainList->FindObject("Pair")->FindObject(pairnames[pairtype].data())); + auto* list_pair_ss = dynamic_cast(fMainList->FindObject("Pair")->FindObject(pairnames[pairtype].data())); for (const auto& collision : collisions) { - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities{collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -313,7 +320,7 @@ struct MaterialBudgetMC { auto photons1_coll = photons1.sliceBy(perCollision1, collision.globalIndex()); auto photons2_coll = photons2.sliceBy(perCollision2, collision.globalIndex()); - double value[6] = {0.f}; + std::array value{0.}; float phi_cp2 = 0.f, eta_cp2 = 0.f; for (const auto& tagcut : tagcuts) { for (const auto& probecut : probecuts) { @@ -381,7 +388,7 @@ struct MaterialBudgetMC { value[3] = g2.v0radius(); value[4] = RecoDecay::constrainAngle(phi_cp2); value[5] = eta_cp2; - reinterpret_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.GetName(), probecut.GetName()))->FindObject(paircut.GetName())->FindObject("hs_conv_point_same"))->Fill(value); + dynamic_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.getName().c_str(), probecut.getName().c_str()))->FindObject(paircut.getName().c_str())->FindObject("hs_conv_point_same"))->Fill(value.data()); } // end of pair cut loop } // end of g2 loop } // end of g1 loop @@ -408,30 +415,30 @@ struct MaterialBudgetMC { // all MC tracks which belong to the MC event corresponding to the current reconstructed event for (const auto& collision : grouped_collisions) { - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } auto mccollision = collision.emmcevent(); // LOGF(info, "mccollision.globalIndex() = %d", mccollision.globalIndex()); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(1.0); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_before"))->Fill(mccollision.posZ()); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(1.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_before"))->Fill(mccollision.posZ()); if (!collision.sel8()) { continue; } - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(2.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(2.0); if (collision.numContrib() < 0.5) { continue; } - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(3.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(3.0); if (std::abs(collision.posZ()) > 10.0) { continue; } - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(4.0); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_after"))->Fill(mccollision.posZ()); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(4.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_after"))->Fill(mccollision.posZ()); } // end of collision loop } @@ -442,8 +449,8 @@ struct MaterialBudgetMC { PROCESS_SWITCH(MaterialBudgetMC, processDummy, "Dummy function", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"material-budget-mc"})}; + adaptAnalysisTask(context, TaskName{"material-budget-mc"})}; } diff --git a/PWGEM/PhotonMeson/Legacy/README.md b/PWGEM/PhotonMeson/Legacy/README.md new file mode 100644 index 00000000000..c275f700cbb --- /dev/null +++ b/PWGEM/PhotonMeson/Legacy/README.md @@ -0,0 +1,4 @@ +# Legacy + +This is a collection of old code. +Instead of just removing it we want to preserve the code here, while excluding it from compilation to reduce unecessary compile time. diff --git a/PWGEM/PhotonMeson/TableProducer/createPCM.cxx b/PWGEM/PhotonMeson/Legacy/createPCM.cxx similarity index 96% rename from PWGEM/PhotonMeson/TableProducer/createPCM.cxx rename to PWGEM/PhotonMeson/Legacy/createPCM.cxx index 70457782ae6..539ea496a5e 100644 --- a/PWGEM/PhotonMeson/TableProducer/createPCM.cxx +++ b/PWGEM/PhotonMeson/Legacy/createPCM.cxx @@ -12,6 +12,7 @@ /// \file createPCM.cxx /// \brief This code produces photon data tables. /// \author Daiki Sekihata , Tokyo +/// \note legacy code, please use the photonconversionbuilder tasks. #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" @@ -35,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -113,9 +115,9 @@ struct createPCM { Configurable max_r_req_its{"max_r_req_its", 16.0, "min Rxy for V0 with ITS hits"}; Configurable min_r_tpconly{"min_r_tpconly", 32.0, "min Rxy for V0 with TPConly tracks"}; - int mRunNumber; - float d_bz; - Service ccdb; + int mRunNumber = 0; + float d_bz = 0; + Service ccdb{}; o2::base::MatLayerCylSet* lut = nullptr; o2::vertexing::DCAFitterN<2> fitter; // Material correction in the DCA fitter @@ -213,7 +215,7 @@ struct createPCM { } } - template + template bool reconstructV0(TTrack const& ele, TTrack const& pos) { bool isITSonly_pos = pos.hasITS() && !pos.hasTPC(); @@ -257,7 +259,7 @@ struct createPCM { return false; } - float xyz[3] = {0.f, 0.f, 0.f}; + std::array xyz = {0.f, 0.f, 0.f}; Vtx_recalculation(o2::base::Propagator::Instance(), pos, ele, xyz, matCorr); float recalculatedVtxR = std::sqrt(std::pow(xyz[0], 2) + std::pow(xyz[1], 2)); // LOGF(info, "recalculated vtx : x = %f , y = %f , z = %f", xyz[0], xyz[1], xyz[2]); @@ -275,7 +277,7 @@ struct createPCM { return true; } - template + template void fillV0Table(TCollision const& collision, TTrack const& ele, TTrack const& pos, const bool filltable) { std::array pVtx = {collision.posX(), collision.posY(), collision.posZ()}; @@ -323,7 +325,7 @@ struct createPCM { } registry.fill(HIST("hV0xy"), svpos[0], svpos[1]); // this should have worst resolution - float xyz_tmp[3] = {0.f, 0.f, 0.f}; + std::array xyz_tmp = {0.f, 0.f, 0.f}; Vtx_recalculation(o2::base::Propagator::Instance(), pos, ele, xyz_tmp, matCorr); registry.fill(HIST("hV0xy_recalculated"), xyz_tmp[0], xyz_tmp[1]); // this should have good resolution @@ -343,7 +345,7 @@ struct createPCM { } std::pair> its_ib_Requirement = {0, {0, 1, 2}}; // no hit on 3 ITS ib layers. - template + template bool isSelected(TTrack const& track) { if (track.pt() < minpt || std::abs(track.eta()) > maxeta) { @@ -420,7 +422,7 @@ struct createPCM { // registry.fill(HIST("hEventCounter"), 1); int32_t min_sw = std::max(static_cast(0), collision.globalIndex()); - int32_t max_sw = std::min(static_cast(min_sw + nsw), static_cast(collisions.size())); + int32_t max_sw = std::min(static_cast(min_sw + nsw), collisions.size()); // LOGF(info, "orphan_posTracks.size() = %d, orphan_negTracks.size() = %d", orphan_posTracks.size(), orphan_negTracks.size()); negTracks_sw.reserve(max_sw - min_sw); @@ -477,7 +479,7 @@ struct createPCM { vec_cospa.reserve(max_sw - min_sw); for (int32_t isw = min_sw; isw < max_sw; isw++) { auto collision_in_sw = collisions.rawIteratorAt(isw); - if (cospa_map.find(std::make_tuple(pos.globalIndex(), ele.globalIndex(), collision_in_sw.globalIndex())) != cospa_map.end()) { + if (cospa_map.contains(std::make_tuple(pos.globalIndex(), ele.globalIndex(), collision_in_sw.globalIndex()))) { vec_cospa.emplace_back(cospa_map[std::make_tuple(pos.globalIndex(), ele.globalIndex(), collision_in_sw.globalIndex())]); } else { vec_cospa.emplace_back(-999.f); @@ -516,7 +518,7 @@ struct createPCM { } } // end of pca_map loop - if (is_closest_v0 && used_pair_map.find(std::make_pair(pos.globalIndex(), ele.globalIndex())) == used_pair_map.end()) { + if (is_closest_v0 && !used_pair_map.contains(std::make_pair(pos.globalIndex(), ele.globalIndex()))) { // LOGF(info, "store : pos.globalIndex() = %d , ele.globalIndex() = %d , collision.globalIndex() = %d , cospa = %f , pca = %f", std::get<0>(key), std::get<1>(key), std::get<2>(key), value, pca_map[key]); fillV0Table(collision_most_prob, ele, pos, true); used_pair_map[std::make_pair(pos.globalIndex(), ele.globalIndex())] = true; @@ -563,7 +565,7 @@ struct createPCM { if (ele.sign() < 0) { fillV0Table(collision, ele, pos, true); } else { - fillV0Table(collision, pos, ele, true); + fillV0Table(collision, pos, ele, true); // NOLINT(readability-suspicious-call-argument) in case the ele is actually positivley charged } } } // end of collision loop @@ -571,8 +573,8 @@ struct createPCM { PROCESS_SWITCH(createPCM, processTrkCollAsso, "create V0s with track-to-collision associator", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"v0-finder"})}; + adaptAnalysisTask(context, TaskName{"v0-finder"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/gammaConversions.cxx b/PWGEM/PhotonMeson/Legacy/gammaConversions.cxx similarity index 95% rename from PWGEM/PhotonMeson/Tasks/gammaConversions.cxx rename to PWGEM/PhotonMeson/Legacy/gammaConversions.cxx index 7c436bf018c..d2833791f39 100644 --- a/PWGEM/PhotonMeson/Tasks/gammaConversions.cxx +++ b/PWGEM/PhotonMeson/Legacy/gammaConversions.cxx @@ -12,9 +12,10 @@ /// \file gammaConversions.cxx /// \brief perform photon conversion analysis on V0 candidates from aod::StoredV0Datas /// \author stephan.friedrich.stiefelmaier@cern.ch +/// \note legacy code, please use the Pi0EtaToGammaGamma tasks for the standard neutral meson analysis. /// dependencies: o2-analysis-lf-lambdakzerobuilder -#include "PWGEM/PhotonMeson/Tasks/gammaConversions.h" +#include "PWGEM/PhotonMeson/Legacy/gammaConversions.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" @@ -36,10 +37,12 @@ #include #include +#include #include #include #include #include +#include #include #include @@ -133,6 +136,7 @@ struct GammaConversions { void init(InitContext const&) { + o2::framework::AxisSpec gAxis_pT_log{800, 0.01f, 25.f}; // make axis logarithmic gAxis_pT_log.makeLogarithmic(); @@ -337,9 +341,9 @@ struct GammaConversions { TMCGAMMA const& theMcPhoton, TV0 const& theV0, float const& theV0CosinePA, - int PDGCode[], + std::span PDGCode, bool const& sameMother, - float McTrackmomentum[]) + std::span McTrackmomentum) { fillV0Histograms( fMyRegistry.mV0.mRejectedByMc[theRejReason].mBeforeAfterRecCuts[theBefAftRec].mV0Kind[kRec].mContainer, @@ -365,7 +369,7 @@ struct GammaConversions { } template - bool v0IsGoodValidatedMcPhoton(TMCGAMMA const& theMcPhoton, TV0 const& theV0, float const& theV0CosinePA, bool theV0PassesRecCuts, int PDGCode[], bool const& sameMother, float McTrackmomentum[]) + bool v0IsGoodValidatedMcPhoton(TMCGAMMA const& theMcPhoton, TV0 const& theV0, float const& theV0CosinePA, bool theV0PassesRecCuts, std::span PDGCode, bool const& sameMother, std::span McTrackmomentum) { auto fillRejectedV0HistosI = [&](eMcRejectedSaved theRejReason) { fillAllV0HistogramsForRejectedByMc(static_cast(theRejReason), @@ -417,9 +421,9 @@ struct GammaConversions { TV0 const& theV0, float const& theV0CosinePA, bool theV0PassesRecCuts, - int PDGCode[], + std::span PDGCode, bool const& sameMother, - float McTrackmomentum[]) + std::span McTrackmomentum) { fillV0McValidationHisto(eV0McValidation::kV0in); @@ -456,10 +460,10 @@ struct GammaConversions { } } - void processPDGHistos(int const PDGCode[], - bool const& sameMother, - float const McTrackmomentum[], - int const& theV0PassesRecCuts) + void processPDGHistos(std::span PDGCode, + bool const sameMother, + std::span McTrackmomentum, + bool const theV0PassesRecCuts) { lfillPDGHist(fMyRegistry.mV0.mBeforeAfterRecCuts[kBeforeRecCuts].mV0Kind[kRec].mContainer, PDGCode); @@ -478,17 +482,17 @@ struct GammaConversions { } } - void lfillPDGHist(mapStringHistPtr& theContainer, - int const PDGCode[]) + void lfillPDGHist(mapStringHistPtr const& theContainer, + std::span const PDGCode) { fillTH1(theContainer, "hPDGCode", PDGCode[0]); fillTH1(theContainer, "hPDGCode", PDGCode[1]); } - void lfillDecaysHist(mapStringHistPtr& theContainer, - int const PDGCode[], - int const& sameMother, - float const McTrackmomentum[]) + void lfillDecaysHist(mapStringHistPtr const& theContainer, + std::span PDGCode, + const bool sameMother, + std::span McTrackmomentum) { float MCV0p = RecoDecay::sqrtSumOfSquares(McTrackmomentum[0] + McTrackmomentum[3], McTrackmomentum[1] + McTrackmomentum[4]); @@ -539,7 +543,7 @@ struct GammaConversions { } template - void fillTruePhotonHistograms(int theBefAftRec, TMCGAMMA const& theMcPhoton, TV0 const& theV0, float const& theV0CosinePA, float McTrackmomentum[]) + void fillTruePhotonHistograms(int theBefAftRec, TMCGAMMA const& theMcPhoton, TV0 const& theV0, float const& theV0CosinePA, std::span McTrackmomentum) { fillV0HistogramsMcGamma( fMyRegistry.mV0.mBeforeAfterRecCuts[theBefAftRec].mV0Kind[kMCTrue].mContainer, @@ -567,7 +571,7 @@ struct GammaConversions { TMCGAMMA const& theMcPhoton, TV0 const& theV0, float const& theV0CosinePA, - float McTrackmomentum[]) + std::span McTrackmomentum) { fillV0HistogramsMcGamma( fMyRegistry.mV0.mRejectedByMc[theRejReason].mBeforeAfterRecCuts[theBefAftRec].mV0Kind[kMCTrue].mContainer, @@ -590,9 +594,9 @@ struct GammaConversions { } template - void fillV0MCDaughterParticlesArrays(TTRACKS ele, TTRACKS pos, - int PDGCode[], - float McParticleMomentum[], + void fillV0MCDaughterParticlesArrays(TTRACKS const& ele, TTRACKS const& pos, + std::span PDGCode, + std::span McParticleMomentum, bool& sameMother) { if ((ele.has_v0DaughterMcParticle()) && (pos.has_v0DaughterMcParticle())) { @@ -666,9 +670,9 @@ struct GammaConversions { // check if V0 passes rec cuts and fill beforeRecCuts,afterRecCuts [kRec] bool lV0PassesRecCuts = processV0(lV0, lV0CosinePA, ele, pos); - int PDGCode[2]; // Pos, then Neg - float McParticleMomentum[6]; // Mc momentum of the two daughter tracks, 0-2 = one 3-5 = two, no need to check the charges - bool sameMother; + std::array PDGCode{}; + std::array McParticleMomentum{}; + bool sameMother{}; fillV0MCDaughterParticlesArrays(ele, pos, PDGCode, McParticleMomentum, sameMother); // pointers are passed so they can be later on used here // this process function has to exist seperatly because it is only for MC Rec @@ -772,7 +776,7 @@ struct GammaConversions { // SFS todo: combine fillV0Histograms and fillV0HistogramsMcGamma template - void fillV0HistogramsMcGamma(mapStringHistPtr& theContainer, TMCGAMMA const& theMcGamma, float McTrackmomentum[]) + void fillV0HistogramsMcGamma(mapStringHistPtr& theContainer, TMCGAMMA const& theMcGamma, std::span McTrackmomentum) { fillTH1(theContainer, "hEta", theMcGamma.eta()); fillTH1(theContainer, "hPhi", theMcGamma.phi()); @@ -821,10 +825,7 @@ struct GammaConversions { bool pass_ele = trackPassesCuts(ele); bool pass_pos = trackPassesCuts(pos); - if (pass_ele && pass_pos) - return true; - else - return false; + return (pass_ele && pass_pos); } template @@ -923,7 +924,7 @@ struct GammaConversions { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/gammaConversions.h b/PWGEM/PhotonMeson/Legacy/gammaConversions.h similarity index 95% rename from PWGEM/PhotonMeson/Tasks/gammaConversions.h rename to PWGEM/PhotonMeson/Legacy/gammaConversions.h index 4e985ad524d..7733277880a 100644 --- a/PWGEM/PhotonMeson/Tasks/gammaConversions.h +++ b/PWGEM/PhotonMeson/Legacy/gammaConversions.h @@ -13,8 +13,8 @@ /// \brief contains type definitions for gammaConversions.cxx /// \author stephan.friedrich.stiefelmaier@cern.ch -#ifndef PWGEM_PHOTONMESON_TASKS_GAMMACONVERSIONS_H_ -#define PWGEM_PHOTONMESON_TASKS_GAMMACONVERSIONS_H_ +#ifndef PWGEM_PHOTONMESON_LEGACY_GAMMACONVERSIONS_H_ +#define PWGEM_PHOTONMESON_LEGACY_GAMMACONVERSIONS_H_ #include #include @@ -34,9 +34,9 @@ typedef std::map mapStringHistPtr; // define this in order to have a constructor of the HistogramSpec which copies the name into the title and to have dataOnly flag struct MyHistogramSpec { MyHistogramSpec(char const* const name_, char const* const title_, o2::framework::HistogramConfigSpec config_, bool callSumw2_ = false, bool dataOnly_ = false) - : m{name_, title_, config_, callSumw2_}, fDataOnly(dataOnly_) {} + : m{name_, title_, std::move(config_), callSumw2_}, fDataOnly(dataOnly_) {} MyHistogramSpec(char const* const name_, o2::framework::HistogramConfigSpec config_, bool callSumw2_ = false, bool dataOnly_ = false) - : m{name_, name_, config_, callSumw2_}, fDataOnly(dataOnly_) {} + : m{name_, name_, std::move(config_), callSumw2_}, fDataOnly(dataOnly_) {} o2::framework::HistogramSpec m{}; bool fDataOnly{false}; }; @@ -177,4 +177,4 @@ struct tHistoRegistry { tHistoFolderCTV mV0{mPath + "V0/"}; }; -#endif // PWGEM_PHOTONMESON_TASKS_GAMMACONVERSIONS_H_ +#endif // PWGEM_PHOTONMESON_LEGACY_GAMMACONVERSIONS_H_ diff --git a/PWGEM/PhotonMeson/Tasks/gammaConversionsTruthOnlyMc.cxx b/PWGEM/PhotonMeson/Legacy/gammaConversionsTruthOnlyMc.cxx similarity index 99% rename from PWGEM/PhotonMeson/Tasks/gammaConversionsTruthOnlyMc.cxx rename to PWGEM/PhotonMeson/Legacy/gammaConversionsTruthOnlyMc.cxx index 94ad627191a..c40ce692e52 100644 --- a/PWGEM/PhotonMeson/Tasks/gammaConversionsTruthOnlyMc.cxx +++ b/PWGEM/PhotonMeson/Legacy/gammaConversionsTruthOnlyMc.cxx @@ -91,7 +91,7 @@ struct gammaConversionsTruthOnlyMc { } } template - void fillAsymmetryHistograms(MCGAMMA const& theMcConvGamma, MCDAUONE const theFirstDaughter, MCDAUTWO theSecondDaughter) + void fillAsymmetryHistograms(MCGAMMA const& theMcConvGamma, MCDAUONE const& theFirstDaughter, const MCDAUTWO& theSecondDaughter) { float lConversionRadius = theMcConvGamma.v0Radius(); float lGammaMomentum = theMcConvGamma.p(); diff --git a/PWGEM/PhotonMeson/Tasks/pcmQCMC.cxx b/PWGEM/PhotonMeson/Legacy/pcmQCMC.cxx similarity index 93% rename from PWGEM/PhotonMeson/Tasks/pcmQCMC.cxx rename to PWGEM/PhotonMeson/Legacy/pcmQCMC.cxx index 264808e451a..dc432d670e5 100644 --- a/PWGEM/PhotonMeson/Tasks/pcmQCMC.cxx +++ b/PWGEM/PhotonMeson/Legacy/pcmQCMC.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file MaterialBudget.cxx +/// \file pcmQCMC.cxx /// \brief This code runs loop over v0 photons for PCM QC in MC. /// \author Daiki Sekihata, daiki.sekihata@cern.ch @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include +#include #include #include #include @@ -90,6 +92,7 @@ struct PCMQCMC { Configurable maxRgen{"maxRgen", 90.f, "maximum radius for generated particles"}; Configurable margin_z_mc{"margin_z_mc", 7.0, "margin for z cut in cm for MC"}; Configurable cfgRequireTrueAssociation{"cfgRequireTrueAssociation", false, "flag to require true mc collision association"}; + Configurable cfgResolDetailPlots{"cfgResolDetailPlots", false, "flag to enable detailed resolution plots"}; EMPhotonEventCut fEMEventCut; struct : ConfigurableGroup { @@ -125,6 +128,8 @@ struct PCMQCMC { Configurable cfg_max_eta_v0{"cfg_max_eta_v0", +0.8, "max eta for v0 photons at PV"}; Configurable cfg_min_v0radius{"cfg_min_v0radius", 4.0, "min v0 radius"}; Configurable cfg_max_v0radius{"cfg_max_v0radius", 90.0, "max v0 radius"}; + Configurable cfg_midL_v0radius{"cfg_midL_v0radius", -1.0, "middle low v0 radius for rejection if >0"}; + Configurable cfg_midH_v0radius{"cfg_midH_v0radius", -1.0, "middle high v0 radius for rejection if >0"}; Configurable cfg_max_alpha_ap{"cfg_max_alpha_ap", 0.95, "max alpha for AP cut"}; Configurable cfg_max_qt_ap{"cfg_max_qt_ap", 0.01, "max qT for AP cut"}; Configurable cfg_min_cospa{"cfg_min_cospa", 0.999, "min V0 CosPA"}; @@ -158,12 +163,12 @@ struct PCMQCMC { } pcmcuts; o2::ccdb::CcdbApi ccdbApi; - o2::framework::Service ccdb; - int mRunNumber; - float d_bz; + o2::framework::Service ccdb{}; + int mRunNumber = 0; + float d_bz = 0; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_types[2] = {"before/", "after/"}; - static constexpr std::string_view mcphoton_types[5] = {"primary/", "fromWD/", "fromHS/", "fromPi0Dalitz/", "fromEtaDalitz/"}; + static constexpr std::array event_types = {"before/", "after/"}; + static constexpr std::array mcphoton_types = {"primary/", "fromWD/", "fromHS/", "fromPi0Dalitz/", "fromEtaDalitz/"}; void init(InitContext&) { @@ -199,10 +204,11 @@ struct PCMQCMC { } auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) + o2::parameters::GRPObject* grpo = nullptr; + o2::parameters::GRPMagField* grpmag = nullptr; + if (!skipGRPOquery) { grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); + } if (grpo) { // Fetch magnetic field from ccdb for current collision d_bz = grpo->getNominalL3Field(); @@ -223,6 +229,7 @@ struct PCMQCMC { void addhistograms() { std::vector ptbins; + ptbins.reserve(72); for (int i = 0; i < 2; i++) { ptbins.emplace_back(0.05 * (i - 0) + 0.0); // from 0 to 0.05 GeV/c, every 0.05 GeV/c } @@ -248,6 +255,9 @@ struct PCMQCMC { fRegistry.add("Generated/hRZ", "conversion point in RZ MC;V_{z} (cm);R_{xy} (cm)", kTH2F, {{400, -100.0f, 100.0f}, {400, 0.f, 100.0f}}, true); fRegistry.add("Generated/hRPhi", "conversion point of #varphi vs. R_{xy} MC;#varphi (rad.);R_{xy} (cm);N_{e}", kTH2F, {{360, 0.0f, o2::constants::math::TwoPI}, {400, 0, 100}}, true); fRegistry.add("Generated/hsConvPoint", "photon conversion point;r_{xy} (cm);#varphi (rad.);#eta;", kTHnSparseF, {{100, 0.0f, 100}, {90, 0, o2::constants::math::TwoPI}, {80, -2, +2}}, true); + if (cfgResolDetailPlots) { + fRegistry.add("Generated/hPtEtaPhi", "Photon pt vs eta, and phi;p_{T, gen} (GeV/c);#eta;#phi", kTHnSparseF, {{200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + } } // event info @@ -297,8 +307,8 @@ struct PCMQCMC { fRegistry.add("V0/primary/hConvPoint_diffY", "conversion point diff Y MC;Y_{MC} (cm);Y_{rec} - Y_{MC} (cm)", kTH2F, {{200, -100, +100}, {100, -50.0f, 50.0f}}, true); fRegistry.add("V0/primary/hConvPoint_diffZ", "conversion point diff Z MC;Z_{MC} (cm);Z_{rec} - Z_{MC} (cm)", kTH2F, {{200, -100, +100}, {100, -50.0f, 50.0f}}, true); fRegistry.add("V0/primary/hPtGen_DeltaPtOverPtGen", "photon p_{T} resolution;p_{T}^{gen} (GeV/c);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", kTH2F, {{1000, 0, 10}, {200, -1.0f, 1.0f}}, true); - fRegistry.add("V0/primary/hPtGen_DeltaEta", "photon #eta resolution;p_{T}^{gen} (GeV/c);#eta^{rec} - #eta^{gen}", kTH2F, {{1000, 0, 10}, {100, -0.5f, 0.5f}}, true); - fRegistry.add("V0/primary/hPtGen_DeltaPhi", "photon #varphi resolution;p_{T}^{gen} (GeV/c);#varphi^{rec} - #varphi^{gen} (rad.)", kTH2F, {{1000, 0, 10}, {100, -0.5f, 0.5f}}, true); + fRegistry.add("V0/primary/hPtGen_DeltaEta", "photon #eta resolution;p_{T}^{gen} (GeV/c);#eta^{rec} - #eta^{gen}", kTH2F, {{200, 0, 20}, {199, -0.099, 0.099}}, true); + fRegistry.add("V0/primary/hPtGen_DeltaPhi", "photon #varphi resolution;p_{T}^{gen} (GeV/c);#varphi^{rec} - #varphi^{gen} (rad.)", kTH2F, {{200, 0, 20}, {199, -0.099, 0.099}}, true); fRegistry.add("V0/primary/hRxyGen_DeltaPtOverPtGen", "photon p_{T} resolution; R_{xy}^{gen} (cm);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", kTH2F, {{100, 0, 100}, {200, -1.0f, 1.0f}}, true); fRegistry.add("V0/primary/hRxyGen_DeltaEta", "photon #eta resolution;R_{xy}^{gen} (cm);#eta^{rec} - #eta^{gen}", kTH2F, {{100, 0, 100}, {100, -0.5f, 0.5f}}, true); fRegistry.add("V0/primary/hRxyGen_DeltaPhi", "photon #varphi resolution;R_{xy}^{gen} (cm);#varphi^{rec} - #varphi^{gen} (rad.)", kTH2F, {{100, 0, 100}, {100, -0.5f, 0.5f}}, true); @@ -306,6 +316,12 @@ struct PCMQCMC { fRegistry.add("V0/primary/hXY_MC", "X vs. Y of true photon conversion point.;X (cm);Y (cm)", kTH2F, {{400, -100.0f, +100}, {400, -100, +100}}, true); fRegistry.add("V0/primary/hRZ_MC", "R vs. Z of true photon conversion point;Z (cm);R_{xy} (cm)", kTH2F, {{200, -100.0f, +100}, {200, 0, 100}}, true); fRegistry.add("V0/primary/hsConvPoint", "photon conversion point;r_{xy} (cm);#varphi (rad.);#eta;", kTHnSparseF, {{100, 0.0f, 100}, {90, 0, o2::constants::math::TwoPI}, {80, -2, +2}}, false); + if (cfgResolDetailPlots) { + fRegistry.add("V0/primary/hEtaPhiResol", "Photon eta-phi resolution;p_{T} (GeV/c);#eta-diff;#phi-diff", kTH3F, {{200, 0., 20.}, {99, -0.049, 0.049}, {99, -0.049, 0.049}}, false); + fRegistry.add("V0/primary/hPtResolPtEtaPhi", "Photon resolution vs. pt, eta, and phi;p_{T_rec} - p_{T true} / p_{T true};p_{T} (GeV/c);#eta;#phi", kTHnSparseF, {{199, -0.995, 0.995}, {200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + fRegistry.add("V0/primary/hMomResolPtEtaPhi", "Photon momentum resolution vs. p, eta, and phi;p_{rec} - p_{true} / p_{true};p_{T} (GeV/c);#eta;#phi", kTHnSparseF, {{199, -0.995, 0.995}, {200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + fRegistry.add("V0/primary/hPtEtaPhi", "pt, eta, and phi;p_{T} (GeV/c);#eta;#phi", kTHnSparseF, {{200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + } if (pcmcuts.cfg_apply_ml_cuts) { if (pcmcuts.cfg_nclasses_ml == 2) { fRegistry.add("V0/primary/hBDTBackgroundScoreVsPt", "BDT background score vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{200, 0.0f, 20.0f}, {100, 0.0f, 1.0f}}}); @@ -394,7 +410,7 @@ struct PCMQCMC { fV0PhotonCut.SetMinCosPA(pcmcuts.cfg_min_cospa); fV0PhotonCut.SetMaxPCA(pcmcuts.cfg_max_pca); fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfg_max_chi2kf); - fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius); + fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius, pcmcuts.cfg_midL_v0radius, pcmcuts.cfg_midH_v0radius); fV0PhotonCut.SetAPRange(pcmcuts.cfg_max_alpha_ap, pcmcuts.cfg_max_qt_ap); fV0PhotonCut.RejectITSib(pcmcuts.cfg_reject_v0_on_itsib); @@ -421,8 +437,7 @@ struct PCMQCMC { fV0PhotonCut.SetNClassesMl(pcmcuts.cfg_nclasses_ml); fV0PhotonCut.SetMlTimestampCCDB(pcmcuts.cfg_timestamp_ccdb); fV0PhotonCut.SetCcdbUrl(ccdburl); - CentType mCentralityTypeMlEnum; - mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); + auto mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); fV0PhotonCut.SetCentralityTypeMl(mCentralityTypeMlEnum); fV0PhotonCut.SetCutDirMl(pcmcuts.cfg_cut_dir_ml); fV0PhotonCut.SetMlModelPathsCCDB(pcmcuts.cfg_model_paths_ccdb); @@ -514,6 +529,16 @@ struct PCMQCMC { fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hXY_MC"), mcleg.vx(), mcleg.vy()); fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRZ_MC"), mcleg.vz(), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2))); + if (cfgResolDetailPlots) { + // Resolution vs. pt, eta and phi + float resolPt = (v0.pt() - mcphoton.pt()) / mcphoton.pt(); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPtResolPtEtaPhi"), resolPt, mcphoton.pt(), mcphoton.eta(), mcphoton.phi()); + float resolMom = (v0.p() - mcphoton.p()) / mcphoton.p(); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hMomResolPtEtaPhi"), resolMom, mcphoton.pt(), mcphoton.eta(), mcphoton.phi()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPtEtaPhi"), mcphoton.pt(), mcphoton.eta(), mcphoton.phi()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hEtaPhiResol"), mcphoton.pt(), v0.eta() - mcphoton.eta(), v0.phi() - mcphoton.phi()); + } + float phi_cp = std::atan2(v0.vy(), v0.vx()); o2::math_utils::bringTo02Pi(phi_cp); float eta_cp = std::atanh(v0.vz() / std::sqrt(std::pow(v0.vx(), 2) + std::pow(v0.vy(), 2) + std::pow(v0.vz(), 2))); @@ -587,7 +612,7 @@ struct PCMQCMC { { for (const auto& collision : collisions) { initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -730,7 +755,7 @@ struct PCMQCMC { // all MC tracks which belong to the MC event corresponding to the current reconstructed event for (const auto& collision : collisions) { - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -751,6 +776,12 @@ struct PCMQCMC { } if (std::abs(mctrack.pdgCode()) == PDG_t::kGamma && (mctrack.isPhysicalPrimary() || mctrack.producedByGenerator())) { + if (cfgResolDetailPlots) { + fRegistry.fill(HIST("Generated/hPtEtaPhi"), mctrack.pt(), mctrack.eta(), mctrack.phi()); // fill for all generated photons before any kinematic cut + } + if (mctrack.daughtersIds().empty()) { + continue; + } auto daughter = mcparticles.iteratorAt(mctrack.daughtersIds()[0]); // choose ele or pos. float rxy_gen_e = std::sqrt(std::pow(daughter.vx(), 2) + std::pow(daughter.vy(), 2)); float phi_cp = std::atan2(daughter.vy(), daughter.vx()); @@ -784,8 +815,8 @@ struct PCMQCMC { PROCESS_SWITCH(PCMQCMC, processDummy, "Dummy function", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"pcm-qc-mc"})}; + adaptAnalysisTask(context, TaskName{"pcm-qc-mc"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEE.cxx b/PWGEM/PhotonMeson/Legacy/skimmerDalitzEE.cxx similarity index 99% rename from PWGEM/PhotonMeson/TableProducer/skimmerDalitzEE.cxx rename to PWGEM/PhotonMeson/Legacy/skimmerDalitzEE.cxx index f522e113386..fc64bbba7fd 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEE.cxx +++ b/PWGEM/PhotonMeson/Legacy/skimmerDalitzEE.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file skimmerDalitzEE /// \brief write relevant information for dalitz ee analysis to an AO2D.root file. This file is then the only necessary input to perform pcm analysis. /// \author daiki.sekihata@cern.ch @@ -22,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerGammaConversion.cxx b/PWGEM/PhotonMeson/Legacy/skimmerGammaConversion.cxx similarity index 95% rename from PWGEM/PhotonMeson/TableProducer/skimmerGammaConversion.cxx rename to PWGEM/PhotonMeson/Legacy/skimmerGammaConversion.cxx index c0b14051d74..6b2f9d20e13 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerGammaConversion.cxx +++ b/PWGEM/PhotonMeson/Legacy/skimmerGammaConversion.cxx @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,7 @@ #include #include +#include #include #include #include @@ -104,7 +106,7 @@ struct skimmerGammaConversion { }; // declare this here in order to be able to access it from a lambda - std::shared_ptr fMotherSizesHisto{}; + std::shared_ptr fMotherSizesHisto; enum eV0Confirmation { kV0In, @@ -131,7 +133,7 @@ struct skimmerGammaConversion { Produces fFuncTableMCTrackInformation; Produces fIndexTableMCTrackIndex; - Service ccdb; + Service ccdb{}; int runNumber = -1; o2::base::MatLayerCylSet* lut = nullptr; @@ -166,7 +168,7 @@ struct skimmerGammaConversion { } auto run3grp_timestamp = bc.timestamp(); - o2::parameters::GRPObject* grpo = ccdb->getForTimeStamp(ccdbPath, run3grp_timestamp); + auto* grpo = ccdb->getForTimeStamp(ccdbPath, run3grp_timestamp); o2::parameters::GRPMagField* grpmag = nullptr; if (grpo) { @@ -189,7 +191,7 @@ struct skimmerGammaConversion { KFParticle::SetField(magneticField); } - template + template void fillTrackTable(TTRACK const& theTrack, TKFP const& kfp) { v0legs(theTrack.collisionId(), @@ -201,8 +203,8 @@ struct skimmerGammaConversion { theTrack.itsClusterSizes(), theTrack.itsChi2NCl(), theTrack.detectorMap()); } - template - void fillfFuncTableMCTrackInformation(TTRACK theTrack, bool sameMother) + template + void fillfFuncTableMCTrackInformation(TTRACK const& theTrack, bool sameMother) { fFuncTableMCTrackInformation( theTrack.mcParticle().pdgCode(), @@ -212,7 +214,7 @@ struct skimmerGammaConversion { sameMother); } - template + template bool checkV0leg(TTrack const& track) { if (track.pt() < minpt || abs(track.eta()) > maxeta) { @@ -236,24 +238,24 @@ struct skimmerGammaConversion { return true; } - template + template void fillV0KF(TCollision const& collision, TV0 const& v0) { auto pos = v0.template posTrack_as(); // positive daughter auto ele = v0.template negTrack_as(); // negative daughter - float xyz[3] = {0.f, 0.f, 0.f}; + std::array xyz = {0.f, 0.f, 0.f}; Vtx_recalculation(o2::base::Propagator::Instance(), pos, ele, xyz); KFPTrack kfp_track_pos = createKFPTrackFromTrack(pos); KFPTrack kfp_track_ele = createKFPTrackFromTrack(ele); KFParticle kfp_pos(kfp_track_pos, -11); KFParticle kfp_ele(kfp_track_ele, 11); - const KFParticle* GammaDaughters[2] = {&kfp_pos, &kfp_ele}; + std::array GammaDaughters = {&kfp_pos, &kfp_ele}; KFParticle gammaKF; gammaKF.SetConstructMethod(2); - gammaKF.Construct(GammaDaughters, 2); + gammaKF.Construct(GammaDaughters.data(), 2); if (kfMassConstrain > -0.1) { gammaKF.SetNonlinearMassConstraint(kfMassConstrain); } @@ -265,7 +267,7 @@ struct skimmerGammaConversion { // Transport the gamma to the recalculated decay vertex KFParticle gammaKF_DecayVtx = gammaKF; // with respect to (0,0,0) - gammaKF_DecayVtx.TransportToPoint(xyz); + gammaKF_DecayVtx.TransportToPoint(xyz.data()); //// Apply a topological constraint of the gamma to the PV. Parameters will be given at the primary vertex. // KFParticle gammaKF_PV = gammaKF_DecayVtx; @@ -278,8 +280,8 @@ struct skimmerGammaConversion { KFParticle kfp_pos_DecayVtx = kfp_pos; KFParticle kfp_ele_DecayVtx = kfp_ele; - kfp_pos_DecayVtx.TransportToPoint(xyz); - kfp_ele_DecayVtx.TransportToPoint(xyz); + kfp_pos_DecayVtx.TransportToPoint(xyz.data()); + kfp_ele_DecayVtx.TransportToPoint(xyz.data()); // KFParticle kfp_pos_PV = kfp_pos_DecayVtx; // KFParticle kfp_ele_PV = kfp_ele_DecayVtx; @@ -407,7 +409,7 @@ struct skimmerGammaConversion { } PROCESS_SWITCH(skimmerGammaConversion, processMc, "process reconstructed and mc info ", false); - template + template eV0Confirmation isTrueV0(TV0 const& /*theV0*/, TTRACK const& theTrackPos, TTRACK const& theTrackNeg) @@ -516,7 +518,7 @@ struct skimmerGammaConversion { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"skimmer-gamma-conversion"})}; + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"skimmer-gamma-conversion"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerGammaConversionTruthOnlyMc.cxx b/PWGEM/PhotonMeson/Legacy/skimmerGammaConversionTruthOnlyMc.cxx similarity index 97% rename from PWGEM/PhotonMeson/TableProducer/skimmerGammaConversionTruthOnlyMc.cxx rename to PWGEM/PhotonMeson/Legacy/skimmerGammaConversionTruthOnlyMc.cxx index e44600aafaa..a9eb488775b 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerGammaConversionTruthOnlyMc.cxx +++ b/PWGEM/PhotonMeson/Legacy/skimmerGammaConversionTruthOnlyMc.cxx @@ -111,7 +111,7 @@ struct skimmerGammaConversionTruthOnlyMc { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"skimmer-gamma-conversion-truthonlymc"})}; + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"skimmer-gamma-conversion-truthonlymc"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt index 99bccdf5c49..b7eaafafcaf 100644 --- a/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt +++ b/PWGEM/PhotonMeson/TableProducer/CMakeLists.txt @@ -11,24 +11,14 @@ add_subdirectory(Converters) -o2physics_add_dpl_workflow(skimmer-gamma-conversion - SOURCES skimmerGammaConversion.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore KFParticle::KFParticle - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(skimmer-gamma-conversion-truthonlymc - SOURCES skimmerGammaConversionTruthOnlyMc.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(photon-conversion-builder SOURCES photonconversionbuilder.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(create-pcm - SOURCES createPCM.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore +o2physics_add_dpl_workflow(photon-conversion-builder-tmptable + SOURCES photonconversionbuilderTmpTable.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(create-emevent-photon @@ -61,8 +51,13 @@ o2physics_add_dpl_workflow(skimmer-primary-electron-from-dalitzee PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(skimmer-dalitz-ee - SOURCES skimmerDalitzEE.cxx +o2physics_add_dpl_workflow(skimmer-primary-electron-from-dalitzee-tmptable + SOURCES skimmerPrimaryElectronFromDalitzEETmpTable.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(skimmer-dalitz-events + SOURCES skimmerDalitzEvents.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -80,3 +75,8 @@ o2physics_add_dpl_workflow(material-budget-weights SOURCES materialBudgetWeights.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::CCDB ROOT::Hist ROOT::Core COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(event-selection-photon + SOURCES eventSelection.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGEM/PhotonMeson/TableProducer/Converters/converterEmeventPmevent.cxx b/PWGEM/PhotonMeson/TableProducer/Converters/converterEmeventPmevent.cxx index cd159115d4f..4909b000e3e 100644 --- a/PWGEM/PhotonMeson/TableProducer/Converters/converterEmeventPmevent.cxx +++ b/PWGEM/PhotonMeson/TableProducer/Converters/converterEmeventPmevent.cxx @@ -31,6 +31,7 @@ struct ConverterEmeventPmevent { void process(EMEvents_004 const& collisions) { + pmEvents.reserve(collisions.size()); for (const auto& collision : collisions) { pmEvents( collision.collisionId(), diff --git a/PWGEM/PhotonMeson/TableProducer/Converters/emcIdConverter1.cxx b/PWGEM/PhotonMeson/TableProducer/Converters/emcIdConverter1.cxx index 9c7027076de..f409f11c0bf 100644 --- a/PWGEM/PhotonMeson/TableProducer/Converters/emcIdConverter1.cxx +++ b/PWGEM/PhotonMeson/TableProducer/Converters/emcIdConverter1.cxx @@ -26,6 +26,7 @@ struct emcIdConverter1 { void process(aod::EMCEMEventIds_000 const& ids) { + id_001.reserve(ids.size()); for (const auto& id : ids) { id_001(id.emeventId()); } // end of id loop diff --git a/PWGEM/PhotonMeson/TableProducer/Converters/phosIdConverter1.cxx b/PWGEM/PhotonMeson/TableProducer/Converters/phosIdConverter1.cxx index d6d188abb96..4e69d5e6541 100644 --- a/PWGEM/PhotonMeson/TableProducer/Converters/phosIdConverter1.cxx +++ b/PWGEM/PhotonMeson/TableProducer/Converters/phosIdConverter1.cxx @@ -26,6 +26,7 @@ struct phosIdConverter1 { void process(aod::PHOSEMEventIds_000 const& ids) { + id_001.reserve(ids.size()); for (const auto& id : ids) { id_001(id.emeventId()); } // end of id loop diff --git a/PWGEM/PhotonMeson/TableProducer/Converters/v0photonIdConverter1.cxx b/PWGEM/PhotonMeson/TableProducer/Converters/v0photonIdConverter1.cxx index c3b82a8b204..15a1b2c2fca 100644 --- a/PWGEM/PhotonMeson/TableProducer/Converters/v0photonIdConverter1.cxx +++ b/PWGEM/PhotonMeson/TableProducer/Converters/v0photonIdConverter1.cxx @@ -26,6 +26,7 @@ struct v0photonIdConverter1 { void process(aod::V0KFEMEventIds_000 const& ids) { + id_001.reserve(ids.size()); for (const auto& id : ids) { id_001(id.emeventId()); } // end of id loop diff --git a/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx b/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx index 0bfbe804e31..7248b009e04 100644 --- a/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx +++ b/PWGEM/PhotonMeson/TableProducer/associateMCinfoPhoton.cxx @@ -8,14 +8,12 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// + /// \file associateMCinfoPhoton.cxx -/// -/// \brief This code produces reduced events for photon analyses -/// -/// \author Daiki Sekihata (daiki.sekihata@cern.ch) -/// +/// \brief This code produces EmMc tables were the McParticleIds get reshuffled due to not storing all McParticles +/// \author Daiki Sekihata (daiki.sekihata@cern.ch), Marvin Hemmer (marvin.hemmer@cern.ch), Nicolas Strangmann (nicolas.strangmann@cern.ch) +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" @@ -23,9 +21,11 @@ #include "Common/DataModel/EventSelection.h" #include +#include #include #include #include +#include #include #include #include @@ -40,12 +40,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include using namespace o2; @@ -55,10 +57,15 @@ using namespace o2::soa; using namespace o2::aod::pwgem::photonmeson::utils::mcutil; using namespace o2::constants::physics; -using MyCollisionsMC = soa::Join; +using MyCollisionsMC = soa::Join; using TracksMC = soa::Join; using FwdTracksMC = soa::Join; -using MyEMCClusters = soa::Join; +using MyEMCClusters = soa::Join; + +struct Counter { + int particles{0}; + int events{0}; +}; struct AssociateMCInfoPhoton { enum SubSystem { @@ -68,12 +75,14 @@ struct AssociateMCInfoPhoton { kElectron = 0x8, }; + static constexpr uint16_t kNoMcParticleMask = (1 << 14); // isNoise bit: no genuine MC particle for this leg/track + Produces mcevents; Produces mceventlabels; Produces emmcparticles; Produces v0legmclabels; Produces emprimaryelectronmclabels; - Produces ememcclustermclabels; + Produces ememcclustermclabels; Produces binnedGenPt; @@ -82,6 +91,11 @@ struct AssociateMCInfoPhoton { Configurable max_rxy_gen{"max_rxy_gen", 100, "max rxy to store generated information"}; Configurable requireGammaGammaDecay{"requireGammaGammaDecay", false, "require gamma gamma decay for generated pi0 and eta meson"}; Configurable cfg_max_eta_photon{"cfg_max_eta_gamma", 0.8, "max eta for photons at PV"}; + Configurable maxPt{"maxPt", 20.f, "max pT for BinnedGenPts table"}; + Configurable maxY{"maxY", 0.9f, "max |rapidity| for BinnedGenPts table"}; + Configurable doStoreAllDaughters{"doStoreAllDaughters", false, "flag to enable storing of all photon, pi0, eta, eta' and omega daughters. This will increase the dervied data size!"}; + Configurable useIsSelected{"useIsSelected", true, "flag to enable the usage of isSelected from our EM event selection. Disabling this will increase the derived data size!"}; + Configurable useIsEOI{"useIsEOI", true, "flag to enable the usage of the EOI table"}; HistogramRegistry registry{"EMMCEvent"}; @@ -93,6 +107,7 @@ struct AssociateMCInfoPhoton { // !!Don't change pt,eta,y binning. These binnings have to be consistent with binned data at analysis.!! std::vector ptbins; + ptbins.reserve(72); for (int i = 0; i < 2; i++) { // o2-linter: disable=magic-number (just numbers for binning) ptbins.emplace_back(0.05 * (i - 0) + 0.0); // from 0 to 0.05 GeV/c, every 0.05 GeV/c } @@ -109,7 +124,7 @@ struct AssociateMCInfoPhoton { const AxisSpec axisRapidity{{0.0, +0.8, +0.9}, "rapidity |y|"}; static constexpr uint NParticleNames = 9; - static constexpr std::string_view ParticleNames[NParticleNames] = { + static constexpr std::array ParticleNames = { "Gamma", "Pi0", "Eta", "Omega", "Phi", "ChargedPion", "ChargedKaon", "K0S", "Lambda"}; @@ -136,16 +151,95 @@ struct AssociateMCInfoPhoton { std::vector genPi0; // primary, pt, y std::vector genEta; // primary, pt, y + template + void selectDaughtersToStore(TMCParticle& particle, int64_t mcParticleSize, TMCDaughter& daughterIter, int eventIdx, std::unordered_map& fNewLabels, std::map& fNewLabelsReversed, std::unordered_map& fEventIdx, Counter& fCounter) + { + if (!particle.has_daughters()) { + return; + } + for (int daughterId = particle.daughtersIds()[0]; daughterId <= particle.daughtersIds()[1]; ++daughterId) { + if (daughterId < mcParticleSize) { + daughterIter.setCursor(daughterId); + if (!fNewLabels.contains(daughterIter.globalIndex())) { + fNewLabels[daughterIter.globalIndex()] = fCounter.particles; + fNewLabelsReversed[fCounter.particles] = daughterIter.globalIndex(); + fEventIdx[daughterIter.globalIndex()] = eventIdx; + fCounter.particles++; + } + } + } + } + + template + bool passesCollisionGate(TCollision const& collisionIter) + { + // event selection same as in createEMEventPhoton + if (!collisionIter.has_mcCollision()) { + return false; + } + if (useIsSelected.value && !collisionIter.isSelected()) { + return false; + } + if (useIsEOI.value && !collisionIter.isEoI()) { + return false; + } + return true; + } + + template + void selectMothersToStore(int motherId, int64_t mcParticleSize, TMCParticle& motherParticle, TMCParticle& daughterIter, TMCCollision const& mcCollisionIter, std::unordered_map& fNewLabels, std::map& fNewLabelsReversed, std::unordered_map& fEventIdx, std::unordered_map& fEventLabels, Counter& fCounter) + { + while (motherId > -1) { + if (motherId < mcParticleSize) { + motherParticle.setCursor(motherId); + + auto evIt = fEventLabels.find(mcCollisionIter.globalIndex()); + if (evIt == fEventLabels.end()) { + // Can't attribute this ancestor to a known event; stop walking rather than risk UB. + LOG(warning) << "selectMothersToStore: mcCollision " << mcCollisionIter.globalIndex() + << " not found in fEventLabels, aborting mother-chain walk."; + break; + } + int eventIdx = evIt->second; + + if (!fNewLabels.contains(motherParticle.globalIndex())) { + fNewLabels[motherParticle.globalIndex()] = fCounter.particles; + fNewLabelsReversed[fCounter.particles] = motherParticle.globalIndex(); + fEventIdx[motherParticle.globalIndex()] = eventIdx; + fCounter.particles++; + } + // If this ancestor is a photon, pi0, eta, omega or eta prime store all its daughters + if ((doStoreAllDaughters) && (std::abs(motherParticle.pdgCode()) == PDG_t::kGamma || + std::abs(motherParticle.pdgCode()) == PDG_t::kPi0 || + std::abs(motherParticle.pdgCode()) == o2::constants::physics::Pdg::kEta || + std::abs(motherParticle.pdgCode()) == o2::constants::physics::Pdg::kEtaPrime || + std::abs(motherParticle.pdgCode()) == o2::constants::physics::Pdg::kOmega)) { + selectDaughtersToStore(motherParticle, mcParticleSize, daughterIter, + eventIdx, fNewLabels, fNewLabelsReversed, + fEventIdx, fCounter); + } + + if (motherParticle.has_mothers()) { + motherId = motherParticle.mothersIds()[0]; // first mother index + } else { + motherId = -999; + } + } else { + motherId = -999; + } + } // end of mother chain loop + } + template void skimmingMC(MyCollisionsMC const& collisions, aod::BCs const&, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, TTracks const& o2tracks, TFwdTracks const&, TPCMs const& v0photons, TPCMLegs const& legs, TPHOSs const&, TEMCs const& emcclusters, TEMPrimaryElectrons const& emprimaryelectrons) { // temporary variables used for the indexing of the skimmed MC stack - std::map fNewLabels; + std::unordered_map fNewLabels; std::map fNewLabelsReversed; // std::map fMCFlags; - std::map fEventIdx; - std::map fEventLabels; - int fCounters[2] = {0, 0}; //! [0] - particle counter, [1] - event counter + std::unordered_map fEventIdx; + std::unordered_map fEventLabels; + Counter fCounter{.particles = 0, .events = 0}; //! [0] - particle counter, [1] - event counter auto hBinFinder = registry.get(HIST("Generated/h2PtY_Gamma")); // collision iterator from EMCal cluster @@ -156,6 +250,7 @@ struct AssociateMCInfoPhoton { // mc particles iterator for mother auto motherParticle = mcParticles.begin(); + auto daughterIter = mcParticles.begin(); // mc particles iterator for mother and other mc particles auto mcParticleIter = mcParticles.begin(); @@ -166,12 +261,7 @@ struct AssociateMCInfoPhoton { for (const auto& collision : collisions) { registry.fill(HIST("hEventCounter"), 1); - // TODO: investigate the collisions without corresponding mcCollision - if (!collision.has_mcCollision()) { - continue; - } - - if (!collision.isSelected()) { + if (!passesCollisionGate(collision)) { continue; } @@ -186,7 +276,7 @@ struct AssociateMCInfoPhoton { auto groupedMcParticles = mcParticles.sliceBy(perMcCollision, mcCollisionIter.globalIndex()); for (const auto& mcParticle : groupedMcParticles) { // store necessary information for denominator of efficiency - if ((mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator()) && std::fabs(mcParticle.y()) < 0.9f && mcParticle.pt() < 20.f) { + if ((mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator()) && std::fabs(mcParticle.y()) < maxY && mcParticle.pt() < maxPt) { auto binNumber = hBinFinder->FindBin(mcParticle.pt(), std::fabs(mcParticle.y())); // caution: pack bool isMesonAccepted = false; @@ -203,8 +293,9 @@ struct AssociateMCInfoPhoton { genGamma[binNumber]++; break; case PDG_t::kPi0: - if (requireGammaGammaDecay && !isGammaGammaDecay(mcParticle, mcParticles)) + if (requireGammaGammaDecay && !isGammaGammaDecay(mcParticle, mcParticles)) { continue; + } registry.fill(HIST("Generated/h2PtY_Pi0"), mcParticle.pt(), std::fabs(mcParticle.y())); genPi0[binNumber]++; if (isMesonAccepted) { @@ -212,8 +303,9 @@ struct AssociateMCInfoPhoton { } break; case Pdg::kEta: - if (requireGammaGammaDecay && !isGammaGammaDecay(mcParticle, mcParticles)) + if (requireGammaGammaDecay && !isGammaGammaDecay(mcParticle, mcParticles)) { continue; + } registry.fill(HIST("Generated/h2PtY_Eta"), mcParticle.pt(), std::fabs(mcParticle.y())); genEta[binNumber]++; if (isMesonAccepted) { @@ -227,10 +319,10 @@ struct AssociateMCInfoPhoton { } // end of mc track loop // make an entry for this MC event only if it was not already added to the table - if (!(fEventLabels.find(mcCollisionIter.globalIndex()) != fEventLabels.end())) { + if (!fEventLabels.contains(mcCollisionIter.globalIndex())) { mcevents(mcCollisionIter.globalIndex(), mcCollisionIter.generatorsID(), mcCollisionIter.posX(), mcCollisionIter.posY(), mcCollisionIter.posZ(), mcCollisionIter.impactParameter(), mcCollisionIter.eventPlaneAngle()); - fEventLabels[mcCollisionIter.globalIndex()] = fCounters[1]; - fCounters[1]++; + fEventLabels[mcCollisionIter.globalIndex()] = fCounter.events; + fCounter.events++; binnedGenPt(genGamma, genPi0, genEta); } @@ -259,21 +351,21 @@ struct AssociateMCInfoPhoton { if (motherParticle.pdgCode() == PDG_t::kGamma && (motherParticle.isPhysicalPrimary() || motherParticle.producedByGenerator()) && std::fabs(motherParticle.eta()) < max_eta_gen_secondary) { // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(mcParticle.globalIndex()) != fNewLabels.end())) { // store electron information. !!Not photon!! - fNewLabels[mcParticle.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = mcParticle.globalIndex(); + if (!fNewLabels.contains(mcParticle.globalIndex())) { // store electron information. !!Not photon!! + fNewLabels[mcParticle.globalIndex()] = fCounter.particles; + fNewLabelsReversed[fCounter.particles] = mcParticle.globalIndex(); // fMCFlags[mcParticle.globalIndex()] = mcflags; fEventIdx[mcParticle.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; + fCounter.particles++; } // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(motherParticle.globalIndex()) != fNewLabels.end())) { // store conversion photon - fNewLabels[motherParticle.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = motherParticle.globalIndex(); + if (!fNewLabels.contains(motherParticle.globalIndex())) { // store conversion photon + fNewLabels[motherParticle.globalIndex()] = fCounter.particles; + fNewLabelsReversed[fCounter.particles] = motherParticle.globalIndex(); // fMCFlags[motherParticle.globalIndex()] = mcflags; fEventIdx[motherParticle.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; + fCounter.particles++; } } } @@ -291,185 +383,135 @@ struct AssociateMCInfoPhoton { auto o2TrackIter = o2tracks.begin(); for (const auto& v0 : v0photons) { collisionIter.setCursor(v0.collisionId()); - if (!collisionIter.has_mcCollision()) { - continue; - } - mcCollisionIter.setCursor(collisionIter.mcCollisionId()); - ele.setCursor(v0.negTrackId()); - pos.setCursor(v0.posTrackId()); + // check if the collision is valid AND the legs have valid MC particles + bool validMc = false; + if (passesCollisionGate(collisionIter)) { + mcCollisionIter.setCursor(collisionIter.mcCollisionId()); - o2TrackEle.setCursor(ele.trackId()); - o2TrackPos.setCursor(pos.trackId()); + ele.setCursor(v0.negTrackId()); + pos.setCursor(v0.posTrackId()); + o2TrackEle.setCursor(ele.trackId()); + o2TrackPos.setCursor(pos.trackId()); - if (!o2TrackEle.has_mcParticle() || !o2TrackPos.has_mcParticle()) { - continue; // If no MC particle is found, skip the v0 + validMc = o2TrackEle.has_mcParticle() && o2TrackPos.has_mcParticle(); } - for (const auto& leg : {pos, ele}) { // be carefull of order {pos, ele}! - o2TrackIter.setCursor(leg.trackId()); - mcParticleIter.setCursor(o2TrackIter.mcParticleId()); - // LOGF(info, "mcParticleIter.globalIndex() = %d, mcParticleIter.index() = %d", mcParticleIter.globalIndex(), mcParticleIter.index()); // these are exactly the same. - - // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(mcParticleIter.globalIndex()) != fNewLabels.end())) { - fNewLabels[mcParticleIter.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = mcParticleIter.globalIndex(); - // fMCFlags[mcParticleIter.globalIndex()] = mcflags; - fEventIdx[mcParticleIter.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; - } - v0legmclabels(fNewLabels.find(mcParticleIter.index())->second, o2TrackIter.mcMask()); - - // Next, store mother-chain of this reconstructed track. - int motherid = -999; // first mother index - if (mcParticleIter.has_mothers()) { - motherid = mcParticleIter.mothersIds()[0]; // first mother index - } - while (motherid > -1) { - if (motherid < mcParticles.size()) { // protect against bad mother indices. why is this needed? - motherParticle.setCursor(motherid); - - // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(motherParticle.globalIndex()) != fNewLabels.end())) { - fNewLabels[motherParticle.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = motherParticle.globalIndex(); - // fMCFlags[motherParticle.globalIndex()] = mcflags; - fEventIdx[motherParticle.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; + for (const auto& leg : {pos, ele}) { // careful: order {pos, ele}, must stay fixed either way + if (validMc) { + o2TrackIter.setCursor(leg.trackId()); + mcParticleIter.setCursor(o2TrackIter.mcParticleId()); + + auto [iter, isNew] = fNewLabels.try_emplace(mcParticleIter.globalIndex(), fCounter.particles); + if (isNew) { + fNewLabelsReversed[fCounter.particles] = mcParticleIter.globalIndex(); + auto evIt = fEventLabels.find(mcCollisionIter.globalIndex()); + if (evIt != fEventLabels.end()) { + fEventIdx[mcParticleIter.globalIndex()] = evIt->second; } + fCounter.particles++; + } + v0legmclabels(iter->second, o2TrackIter.mcMask()); - if (motherParticle.has_mothers()) { - motherid = motherParticle.mothersIds()[0]; // first mother index - } else { - motherid = -999; - } - } else { - motherid = -999; + int motherid = -999; + if (mcParticleIter.has_mothers()) { + motherid = mcParticleIter.mothersIds()[0]; } - } // end of mother chain loop - } // end of leg loop + selectMothersToStore(motherid, mcParticles.size(), motherParticle, daughterIter, mcCollisionIter, + fNewLabels, fNewLabelsReversed, fEventIdx, fEventLabels, fCounter); + } else { + v0legmclabels(-1, kNoMcParticleMask); // no MC match for this leg, but the row must still exist + } + } // end of loop over legs {pos, ele} } // end of v0 loop } if constexpr (static_cast(system & kElectron)) { auto o2TrackIter = o2tracks.begin(); - // auto emprimaryelectrons_coll = emprimaryelectrons.sliceBy(perCollisionEl, collision.globalIndex()); for (const auto& emprimaryelectron : emprimaryelectrons) { collisionIter.setCursor(emprimaryelectron.collisionId()); - if (!collisionIter.has_mcCollision()) { - continue; - } - mcCollisionIter.setCursor(collisionIter.mcCollisionId()); - o2TrackIter.setCursor(emprimaryelectron.trackId()); - if (!o2TrackIter.has_mcParticle()) { - continue; // If no MC particle is found, skip the dilepton - } - mcParticleIter.setCursor(o2TrackIter.mcParticleId()); - - // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(mcParticleIter.globalIndex()) != fNewLabels.end())) { - fNewLabels[mcParticleIter.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = mcParticleIter.globalIndex(); - // fMCFlags[mcParticleIter.globalIndex()] = mcflags; - fEventIdx[mcParticleIter.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; - } - emprimaryelectronmclabels(fNewLabels.find(mcParticleIter.index())->second, o2TrackIter.mcMask()); + int32_t mcLabel = -1; + uint16_t mcMask = kNoMcParticleMask; - // Next, store mother-chain of this reconstructed track. - int motherid = -999; // first mother index - if (mcParticleIter.has_mothers()) { - motherid = mcParticleIter.mothersIds()[0]; // first mother index - } - while (motherid > -1) { - if (motherid < mcParticles.size()) { // protect against bad mother indices. why is this needed? - motherParticle.setCursor(motherid); + if (passesCollisionGate(collisionIter)) { + mcCollisionIter.setCursor(collisionIter.mcCollisionId()); + o2TrackIter.setCursor(emprimaryelectron.trackId()); + + if (o2TrackIter.has_mcParticle()) { + mcParticleIter.setCursor(o2TrackIter.mcParticleId()); // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(motherParticle.globalIndex()) != fNewLabels.end())) { - fNewLabels[motherParticle.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = motherParticle.globalIndex(); - // fMCFlags[motherParticle.globalIndex()] = mcflags; - fEventIdx[motherParticle.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; + auto [iter, isNew] = fNewLabels.try_emplace(mcParticleIter.globalIndex(), fCounter.particles); + if (isNew) { + fNewLabelsReversed[fCounter.particles] = mcParticleIter.globalIndex(); + auto evIt = fEventLabels.find(mcCollisionIter.globalIndex()); + if (evIt != fEventLabels.end()) { + fEventIdx[mcParticleIter.globalIndex()] = evIt->second; + } + fCounter.particles++; } + mcLabel = iter->second; + mcMask = o2TrackIter.mcMask(); - if (motherParticle.has_mothers()) { - motherid = motherParticle.mothersIds()[0]; // first mother index - } else { - motherid = -999; + int motherid = -999; + if (mcParticleIter.has_mothers()) { + motherid = mcParticleIter.mothersIds()[0]; } - } else { - motherid = -999; - } - } // end of mother chain loop - + selectMothersToStore(motherid, mcParticles.size(), motherParticle, daughterIter, mcCollisionIter, + fNewLabels, fNewLabelsReversed, fEventIdx, fEventLabels, fCounter); + } // if (o2TrackIter.has_mcParticle()) + } + emprimaryelectronmclabels(mcLabel, mcMask); // always exactly one row per emprimaryelectron } // end of em primary electron loop } if constexpr (static_cast(system & kEMC)) { // for emc photons - // auto ememcclusters_coll = emcclusters.sliceBy(perCollisionEMC, collision.globalIndex()); for (const auto& emccluster : emcclusters) { collisionIter.setCursor(emccluster.collisionId()); - if (!collisionIter.has_mcCollision()) { - continue; - } - mcCollisionIter.setCursor(collisionIter.mcCollisionId()); - // TODO: test - if (emccluster.emmcparticleIds().size() <= 0) { - continue; - } std::vector vEmcMcParticleIds; + std::vector vAmplitudes; - vEmcMcParticleIds.reserve(emccluster.emmcparticleIds().size()); + if (passesCollisionGate(collisionIter)) { + mcCollisionIter.setCursor(collisionIter.mcCollisionId()); - for (const auto& emcParticleId : emccluster.emmcparticleIds()) { - mcPhoton.setCursor(emcParticleId); - - // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(mcPhoton.globalIndex()) != fNewLabels.end())) { - fNewLabels[mcPhoton.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = mcPhoton.globalIndex(); - fEventIdx[mcPhoton.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; + if (emccluster.mcParticleIds().size() != emccluster.amplitude().size()) { + LOG(warning) << "Cluster " << emccluster.globalIndex() + << ": mcParticleIds/amplitude size mismatch (" + << emccluster.mcParticleIds().size() << " vs " << emccluster.amplitude().size() << ")"; } - vEmcMcParticleIds.emplace_back(fNewLabels.find(mcPhoton.index())->second); - // ememcclustermclabels(fNewLabels.find(mcPhoton.index())->second); + size_t nCont = std::min(emccluster.mcParticleIds().size(), emccluster.amplitude().size()); - // Next, store mother-chain of this reconstructed track. - int motherid = -999; // first mother index - if (mcPhoton.has_mothers()) { - motherid = mcPhoton.mothersIds()[0]; // first mother index - } - while (motherid > -1) { - if (motherid < mcParticles.size()) { // protect against bad mother indices. why is this needed? - motherParticle.setCursor(motherid); - - // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack - if (!(fNewLabels.find(motherParticle.globalIndex()) != fNewLabels.end())) { - fNewLabels[motherParticle.globalIndex()] = fCounters[0]; - fNewLabelsReversed[fCounters[0]] = motherParticle.globalIndex(); - fEventIdx[motherParticle.globalIndex()] = fEventLabels.find(mcCollisionIter.globalIndex())->second; - fCounters[0]++; - } + vEmcMcParticleIds.reserve(nCont); + vAmplitudes.reserve(nCont); - if (motherParticle.has_mothers()) { - motherid = motherParticle.mothersIds()[0]; // first mother index - } else { - motherid = -999; + for (size_t iCont = 0; iCont < nCont; iCont++) { + mcPhoton.setCursor(emccluster.mcParticleIds()[iCont]); + // if the MC truth particle corresponding to this reconstructed track which is not already written, add it to the skimmed MC stack + auto [iter, isNew] = fNewLabels.try_emplace(mcPhoton.globalIndex(), fCounter.particles); + if (isNew) { + fNewLabelsReversed[fCounter.particles] = mcPhoton.globalIndex(); + auto evIt = fEventLabels.find(mcCollisionIter.globalIndex()); + if (evIt != fEventLabels.end()) { // keep your earlier guard here too + fEventIdx[mcPhoton.globalIndex()] = evIt->second; } - } else { - motherid = -999; + fCounter.particles++; } - } // end of mother chain loop + vEmcMcParticleIds.emplace_back(iter->second); + vAmplitudes.emplace_back(emccluster.amplitude()[iCont]); + int motherid = -999; + if (mcPhoton.has_mothers()) { + motherid = mcPhoton.mothersIds()[0]; + } + selectMothersToStore(motherid, mcParticles.size(), motherParticle, daughterIter, mcCollisionIter, + fNewLabels, fNewLabelsReversed, fEventIdx, fEventLabels, fCounter); + } } // end of loop over mc particles of the current emc cluster - ememcclustermclabels(vEmcMcParticleIds); - - } // end of em emc cluster loop + // whether or not the collision was okay, exactly one row is always written here so later the tables are ensured to have same size! + ememcclustermclabels(vEmcMcParticleIds, vAmplitudes); + } } // Loop over the label map, create the mother/daughter relationships if these exist and write the skimmed MC stack @@ -481,8 +523,9 @@ struct AssociateMCInfoPhoton { if (mcParticleIter.has_mothers()) { for (const auto& m : mcParticleIter.mothersIds()) { if (m < mcParticles.size()) { // protect against bad mother indices - if (fNewLabels.find(m) != fNewLabels.end()) { - mothers.push_back(fNewLabels.find(m)->second); + auto iter = fNewLabels.find(m); + if (iter != fNewLabels.end()) { + mothers.push_back(iter->second); } } else { LOG(info) << "Mother label (" << m << ") exceeds the McParticles size (" << mcParticles.size() << ")"; @@ -500,8 +543,9 @@ struct AssociateMCInfoPhoton { if (d < mcParticles.size()) { // protect against bad daughter indices // auto dau_tmp = mcParticles.iteratorAt(d); // LOGF(info, "daughter pdg = %d", dau_tmp.pdgCode()); - if (fNewLabels.find(d) != fNewLabels.end()) { - daughters.push_back(fNewLabels.find(d)->second); + auto iter = fNewLabels.find(d); + if (iter != fNewLabels.end()) { + daughters.push_back(iter->second); } } else { LOG(error) << "Daughter label (" << d << ") exceeds the McParticles size (" << mcParticles.size() << ")"; @@ -510,7 +554,12 @@ struct AssociateMCInfoPhoton { } } - emmcparticles(fEventIdx.find(oldLabel)->second, mcParticleIter.pdgCode(), mcParticleIter.flags(), mcParticleIter.statusCode(), + auto evIt = fEventIdx.find(oldLabel); + if (evIt == fEventIdx.end()) { + LOG(warning) << "emmcparticles: no event index for particle label " << oldLabel << ", skipping."; + continue; + } + emmcparticles(evIt->second, mcParticleIter.pdgCode(), mcParticleIter.flags(), mcParticleIter.statusCode(), mothers, daughters, mcParticleIter.px(), mcParticleIter.py(), mcParticleIter.pz(), mcParticleIter.e(), mcParticleIter.vx(), mcParticleIter.vy(), mcParticleIter.vz()); @@ -521,11 +570,11 @@ struct AssociateMCInfoPhoton { // fMCFlags.clear(); fEventIdx.clear(); fEventLabels.clear(); - fCounters[0] = 0; - fCounters[1] = 0; + fCounter.particles = 0; + fCounter.events = 0; } // end of skimmingMC - template + template inline bool areTwoPhotonDaughtersAccepted(const Daughters& lDaughters, float maxEta) { if (lDaughters.size() != 2) { @@ -625,8 +674,8 @@ struct AssociateMCInfoPhoton { PROCESS_SWITCH(AssociateMCInfoPhoton, processDummy, "processDummy", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"associate-mc-info-photon"})}; + adaptAnalysisTask(context, TaskName{"associate-mc-info-photon"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/bcWiseClusterSkimmer.cxx b/PWGEM/PhotonMeson/TableProducer/bcWiseClusterSkimmer.cxx index 75838125d69..f180622e9c0 100644 --- a/PWGEM/PhotonMeson/TableProducer/bcWiseClusterSkimmer.cxx +++ b/PWGEM/PhotonMeson/TableProducer/bcWiseClusterSkimmer.cxx @@ -12,7 +12,6 @@ /// \file bcWiseClusterSkimmer.cxx /// \brief This task creates minimalistic skimmed tables containing EMC clusters and centrality information /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) - Goethe University Frankfurt -/// #include "PWGEM/PhotonMeson/DataModel/bcWiseTables.h" #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" @@ -28,13 +27,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include @@ -47,6 +46,7 @@ #include #include +#include #include #include #include @@ -64,7 +64,7 @@ using namespace o2::constants::physics; using MyCollisions = soa::Join; using MyMCCollisions = soa::Join; -using MyBCs = soa::Join; +using MyBCs = soa::Join; using SelectedUniqueClusters = soa::Filtered; // Clusters from collisions with only one collision in the BC using SelectedUniqueMCClusters = soa::Filtered>; // Clusters from collisions with only one collision in the BC @@ -102,7 +102,6 @@ struct bcWiseClusterSkimmer { ConfigurableAxis cfgMultiplicityBinning{"cfgMultiplicityBinning", {1000, 0, 10000}, "Binning used for the binning of the number of particles in the event"}; aod::rctsel::RCTFlagsChecker isFT0EMCGoodRCTChecker{aod::rctsel::kFT0Bad, aod::rctsel::kEMCBad}; - parameters::GRPLHCIFData* mLHCIFdata = nullptr; int mRunNumber = -1; ctpRateFetcher mRateFetcher; @@ -120,9 +119,10 @@ struct bcWiseClusterSkimmer { { const int nEventBins = 6; mHistManager.add("nBCs", "Number of BCs;;#bf{#it{N}_{BCs}}", HistType::kTH1F, {{nEventBins, -0.5, 5.5}}); - const TString binLabels[nEventBins] = {"All", "FT0", "TVX", "kTVXinEMC", "Cell", "HasMCColl"}; - for (int iBin = 0; iBin < nEventBins; iBin++) + const std::array binLabels = {"All", "FT0", "TVX", "kTVXinEMC", "Cell", "HasMCColl"}; + for (int iBin = 0; iBin < nEventBins; iBin++) { mHistManager.get(HIST("nBCs"))->GetXaxis()->SetBinLabel(iBin + 1, binLabels[iBin]); + } mHistManager.add("CentralityVsGenMultiplicity", "Centrality vs number of generated MC particles;Centrality;#bf{#it{N}_{gen}}", HistType::kTH2F, {{102, 0., 102}, cfgMultiplicityBinning}); mHistManager.add("CentralityFT0CVsM", "Centrality of FT0C vs FT0M;FT0C Cent;FT0M Cent", HistType::kTH2F, {{102, 0., 102}, {102, 0., 102}}); @@ -144,8 +144,9 @@ struct bcWiseClusterSkimmer { LOG(info) << "| Min gen pt: pT > " << cfgMinPtGen; o2::emcal::Geometry::GetInstanceFromRunNumber(300000); - if (cfgRequireGoodRCTQuality) + if (cfgRequireGoodRCTQuality) { isFT0EMCGoodRCTChecker.init({aod::rctsel::kFT0Bad, aod::rctsel::kEMCBad}); + } } /// \brief Process EMCAL clusters (either ambigous or unique) @@ -195,27 +196,32 @@ struct bcWiseClusterSkimmer { int daughterId = aod::pwgem::photonmeson::utils::mcutil::FindMotherInChain(clusterInducer, mcParticles, std::vector{111, 221}); if (daughterId > 0) { mesonMCIndex = mcParticles.iteratorAt(daughterId).mothersIds()[0]; - if (mcParticles.iteratorAt(mesonMCIndex).pt() < cfgMinPtGen) + if (mcParticles.iteratorAt(mesonMCIndex).pt() < cfgMinPtGen) { mesonMCIndex = -1; + } } - if (mesonMCIndex != -1) + if (mesonMCIndex != -1) { break; - if (cfgOnlyCheckFirstTrueContributor) + } + if (cfgOnlyCheckFirstTrueContributor) { break; + } } bool isEta = false; if (mesonMCIndex >= 0) { if (mcParticles.iteratorAt(mesonMCIndex).pdgCode() == PDG_t::kPi0) { - if (fMapPi0Index.find(mesonMCIndex) != fMapPi0Index.end()) // Some pi0s might not be found (not gg decay or too large y) - mesonMCIndex = fMapPi0Index[mesonMCIndex]; // If pi0 was stored in table, change index from the MC index to the pi0 index from this task - else // If pi0 was not stored, treat photon as if not from pi0 + if (fMapPi0Index.contains(mesonMCIndex)) { // Some pi0s might not be found (not gg decay or too large y) + mesonMCIndex = fMapPi0Index[mesonMCIndex]; // If pi0 was stored in table, change index from the MC index to the pi0 index from this task + } else { // If pi0 was not stored, treat photon as if not from pi0 mesonMCIndex = -1; + } } else if (mcParticles.iteratorAt(mesonMCIndex).pdgCode() == Pdg::kEta) { isEta = true; - if (fMapEtaIndex.find(mesonMCIndex) != fMapEtaIndex.end()) // Some etas might not be found (not gg decay or too large y) - mesonMCIndex = fMapEtaIndex[mesonMCIndex]; // If eta was stored in table, change index from the MC index to the eta index from this task - else // If eta was not stored, treat photon as if not from eta + if (fMapEtaIndex.contains(mesonMCIndex)) { // Some etas might not be found (not gg decay or too large y) + mesonMCIndex = fMapEtaIndex[mesonMCIndex]; // If eta was stored in table, change index from the MC index to the eta index from this task + } else { // If eta was not stored, treat photon as if not from eta mesonMCIndex = -1; + } } else { mesonMCIndex = -1; // Not a pi0 or eta } @@ -226,37 +232,21 @@ struct bcWiseClusterSkimmer { bool isBCSelected(const auto& bc) { - if (cfgRequirekTVXinEMC && !bc.alias_bit(kTVXinEMC)) + if (cfgRequirekTVXinEMC && !bc.alias_bit(kTVXinEMC)) { return false; - if (cfgRequireGoodRCTQuality && !isFT0EMCGoodRCTChecker(bc)) + } + if (cfgRequireGoodRCTQuality && !isFT0EMCGoodRCTChecker(bc)) { return false; + } return true; } - void setLHCIFData(const auto& bc) - { - if (mRunNumber == bc.runNumber()) - return; - - auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); - uint64_t timeStamp = bc.timestamp(); - - std::map metadata; - mLHCIFdata = ccdbMgr.getSpecific("GLO/Config/GRPLHCIF", timeStamp, metadata); - if (mLHCIFdata == nullptr) - LOG(fatal) << "GRPLHCIFData not in database, timestamp:" << timeStamp; - mRunNumber = bc.runNumber(); - LOG(info) << "LHCIF data fetched for run " << mRunNumber << " and timestamp " << timeStamp; - - return; - } - double calculateMu(const auto& bc) { auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); uint64_t timeStamp = bc.timestamp(); - auto bfilling = mLHCIFdata->getBunchFilling(); + auto bfilling = bc.grpLhcFData().getBunchFilling(); double nbc = bfilling.getFilledBCs().size(); double tvxRate = mRateFetcher.fetch(&ccdbMgr, timeStamp, bc.runNumber(), "T0VTX"); double nTriggersPerFilledBC = tvxRate / nbc / o2::constants::lhc::LHCRevFreq; @@ -289,25 +279,29 @@ struct bcWiseClusterSkimmer { bool haskTVXinEMC = bc.alias_bit(kTVXinEMC); bool hasEMCCell = cellsInBC.size() > 0; mHistManager.fill(HIST("nBCs"), 0); - if (hasFoundFT0) + if (hasFoundFT0) { mHistManager.fill(HIST("nBCs"), 1); - if (hasTVX) + } + if (hasTVX) { mHistManager.fill(HIST("nBCs"), 2); - if (haskTVXinEMC) + } + if (haskTVXinEMC) { mHistManager.fill(HIST("nBCs"), 3); - if (hasEMCCell) + } + if (hasEMCCell) { mHistManager.fill(HIST("nBCs"), 4); - if (hasMCCollision) + } + if (hasMCCollision) { mHistManager.fill(HIST("nBCs"), 5); + } - if (cfgStoreMu || cfgStoreTime) - setLHCIFData(bc); double mu = cfgStoreMu ? calculateMu(bc) : 0.; - float timeSinceSOF = cfgStoreTime ? (bc.timestamp() - mLHCIFdata->getFillNumberTime()) / 1e3 : 0.; // Convert to seconds + float timeSinceSOF = cfgStoreTime ? (bc.timestamp() - bc.grpLhcFData().getFillNumberTime()) / 1e3 : 0.; // Convert to seconds float ft0Amp = hasFoundFT0 ? bc.foundFT0().sumAmpA() + bc.foundFT0().sumAmpC() : 0.; double centralityOfCollision = 101.5; - if (collisionsInBC.size() > 0) + if (collisionsInBC.size() > 0) { centralityOfCollision = collisionsInBC.iteratorAt(0).centFT0C(); + } double centralityOfBC = bc.centFT0C(); mHistManager.fill(HIST("BCCentVsCollCent"), centralityOfBC, centralityOfCollision); @@ -315,19 +309,22 @@ struct bcWiseClusterSkimmer { bcTable(hasFoundFT0, hasTVX, haskTVXinEMC, hasEMCCell, convertForStorage(centralityOfBC, kCent), convertForStorage(bc.centFT0M(), kCent), convertForStorage(ft0Amp, kFT0Amp), convertForStorage(mu, kMu), convertForStorage(timeSinceSOF, kTimeSinceSOF)); - for (const auto& collision : collisionsInBC) + for (const auto& collision : collisionsInBC) { collisionTable(bcTable.lastIndex(), convertForStorage(collision.posZ(), kZVtx)); + } } template bool isGammaGammaDecay(TMCParticle const& mcParticle, TMCParticles const& mcParticles) { auto daughtersIds = mcParticle.daughtersIds(); - if (daughtersIds.size() != 2) + if (daughtersIds.size() != 2) { return false; + } for (const auto& daughterId : daughtersIds) { - if (mcParticles.iteratorAt(daughterId).pdgCode() != PDG_t::kGamma) + if (mcParticles.iteratorAt(daughterId).pdgCode() != PDG_t::kGamma) { return false; + } } return true; } @@ -336,19 +333,22 @@ struct bcWiseClusterSkimmer { bool isAccepted(TMCParticle const& mcParticle, TMCParticles const& mcParticles) { auto daughtersIds = mcParticle.daughtersIds(); - if (daughtersIds.size() != 2) + if (daughtersIds.size() != 2) { return false; + } for (const auto& daughterId : daughtersIds) { - if (mcParticles.iteratorAt(daughterId).pdgCode() != PDG_t::kGamma) + if (mcParticles.iteratorAt(daughterId).pdgCode() != PDG_t::kGamma) { return false; + } int iCellID = -1; try { iCellID = emcal::Geometry::GetInstance()->GetAbsCellIdFromEtaPhi(mcParticles.iteratorAt(daughterId).eta(), mcParticles.iteratorAt(daughterId).phi()); - } catch (emcal::InvalidPositionException& e) { + } catch (const emcal::InvalidPositionException& e) { iCellID = -1; } - if (iCellID == -1) + if (iCellID == -1) { return false; + } } return true; } @@ -356,8 +356,9 @@ struct bcWiseClusterSkimmer { void processData(MyBCs const& bcs, MyCollisions const& collisions, aod::FT0s const&, SelectedCells const& cells, SelectedUniqueClusters const& uClusters, SelectedAmbiguousClusters const& aClusters) { for (const auto& bc : bcs) { - if (!isBCSelected(bc)) + if (!isBCSelected(bc)) { continue; + } auto collisionsInBC = collisions.sliceBy(perFoundBC, bc.globalIndex()); auto cellsInBC = cells.sliceBy(cellsPerBC, bc.globalIndex()); @@ -380,8 +381,9 @@ struct bcWiseClusterSkimmer { void processMC(MyBCs const& bcs, MyMCCollisions const& collisions, aod::McCollisions const& mcCollisions, aod::FT0s const&, SelectedCells const& cells, SelectedUniqueMCClusters const& uClusters, SelectedAmbiguousMCClusters const& aClusters, aod::McParticles const& mcParticles) { for (const auto& bc : bcs) { - if (!isBCSelected(bc)) + if (!isBCSelected(bc)) { continue; + } auto collisionsInBC = collisions.sliceBy(perFoundBC, bc.globalIndex()); auto cellsInBC = cells.sliceBy(cellsPerBC, bc.globalIndex()); @@ -391,13 +393,15 @@ struct bcWiseClusterSkimmer { for (const auto& mcCollision : mcCollisionsBC) { mHistManager.fill(HIST("MCZVtx"), mcCollision.posZ()); - if (bc.selection_bit(aod::evsel::kIsTriggerTVX)) + if (bc.selection_bit(aod::evsel::kIsTriggerTVX)) { mHistManager.fill(HIST("MCZVtx_TVX"), mcCollision.posZ()); + } auto mcParticlesInColl = mcParticles.sliceBy(perMcCollision, mcCollision.globalIndex()); mHistManager.fill(HIST("CentralityVsGenMultiplicity"), bc.centFT0M(), mcParticlesInColl.size()); for (const auto& mcParticle : mcParticlesInColl) { - if (std::abs(mcParticle.y()) > cfgRapidityCut || !isGammaGammaDecay(mcParticle, mcParticles) || mcParticle.pt() < cfgMinPtGen) + if (std::abs(mcParticle.y()) > cfgRapidityCut || !isGammaGammaDecay(mcParticle, mcParticles) || mcParticle.pt() < cfgMinPtGen) { continue; + } bool isPrimary = mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator(); bool isFromWD = (aod::pwgem::photonmeson::utils::mcutil::IsFromWD(mcCollision, mcParticle, mcParticles)) > 0; @@ -427,4 +431,7 @@ struct bcWiseClusterSkimmer { PROCESS_SWITCH(bcWiseClusterSkimmer, processMC, "Run skimming for MC", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx b/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx index 01965fc08ae..03aba7421de 100644 --- a/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx +++ b/PWGEM/PhotonMeson/TableProducer/createEMEventPhoton.cxx @@ -25,6 +25,7 @@ #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/Qvectors.h" +#include #include #include #include @@ -37,7 +38,6 @@ #include #include -#include #include #include #include @@ -50,23 +50,17 @@ using namespace o2::soa; using MyBCs = soa::Join; using MyQvectors = soa::Join; -using MyCollisions = soa::Join; +using MyCollisions = soa::Join; using MyCollisionsCent = soa::Join; // centrality table has dependency on multiplicity table. using MyCollisionsCentQvec = soa::Join; -using MyCollisionsWithSWT = soa::Join; -using MyCollisionsWithSWT_Cent = soa::Join; // centrality table has dependency on multiplicity table. -using MyCollisionsWithSWT_Cent_Qvec = soa::Join; - using MyCollisionsMC = soa::Join; using MyCollisionsMCCent = soa::Join; // centrality table has dependency on multiplicity table. using MyCollisionsMCCentQvec = soa::Join; struct CreateEMEventPhoton { - // Produces embc; Produces event; Produces eventalias; - // Produces eventCov; Produces eventMult; Produces eventCent; Produces eventQvec; @@ -81,12 +75,6 @@ struct CreateEMEventPhoton { kEvent_JJ = 3, }; - // CCDB options - // Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - // Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - // Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - // Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; - // Configurable dBzInput{"d_bz", -999, "bz field, -999 is automatic"}; Configurable needEMCTrigger{"needEMCTrigger", false, "flag to only save events which have kTVXinEMC trigger bit. To reduce PbPb derived data size"}; Configurable needPHSTrigger{"needPHSTrigger", false, "flag to only save events which have kTVXinPHOS trigger bit. To reduce PbPb derived data size"}; Configurable enableJJHistograms{"enableJJHistograms", false, "flag to fill JJ QA histograms for outlier rejection"}; @@ -104,57 +92,9 @@ struct CreateEMEventPhoton { } } - int mRunNumber; - // float dBz; - // Service ccdb; - - template - void initCCDB(TBC const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - - // // In case override, don't proceed, please - no CCDB access required - // if (dBzInput > -990) { - // dBz = dBzInput; - // o2::parameters::GRPMagField grpmag; - // if (std::fabs(dBz) > 1e-5) { - // grpmag.setL3Current(30000.f / (dBz / 5.0f)); - // } - // mRunNumber = bc.runNumber(); - // return; - // } - - // auto run3GRPTimestamp = bc.timestamp(); - // o2::parameters::GRPObject* grpo = 0x0; - // o2::parameters::GRPMagField* grpmag = 0x0; - // if (!skipGRPOquery) - // grpo = ccdb->getForTimeStamp(grpPath, run3GRPTimestamp); - // if (grpo) { - // // Fetch magnetic field from ccdb for current collision - // dBz = grpo->getNominalL3Field(); - // LOG(info) << "Retrieved GRP for timestamp " << run3GRPTimestamp << " with magnetic field of " << dBz << " kZG"; - // } else { - // grpmag = ccdb->getForTimeStamp(grpmagPath, run3GRPTimestamp); - // if (!grpmag) { - // LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3GRPTimestamp; - // } - // // Fetch magnetic field from ccdb for current collision - // dBz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - // LOG(info) << "Retrieved GRP for timestamp " << run3GRPTimestamp << " with magnetic field of " << dBz << " kZG"; - // } - mRunNumber = bc.runNumber(); - } - template void skimEvent(TCollisions const& collisions, TBCs const&) { - // for (const auto& bc : bcs) { - // if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - // embc(bc.selection_raw(), bc.rct_raw()); // TVX is fired. - // } - // } // end of bc loop for (const auto& collision : collisions) { if constexpr (isMC) { @@ -163,9 +103,7 @@ struct CreateEMEventPhoton { } } - // auto bc = collision.template foundBC_as(); auto bc = collision.template bc_as(); // use this for Zorro - initCCDB(bc); if (needEMCTrigger && !collision.alias_bit(kTVXinEMC)) { continue; @@ -175,7 +113,7 @@ struct CreateEMEventPhoton { } if (collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - int16_t posZint16 = static_cast(collision.posZ() * 100.f); + auto posZint16 = static_cast(collision.posZ() * 100.f); if (posZint16 == 0.f) { if (collision.posZ() < 0) { posZint16 = -1; @@ -183,9 +121,7 @@ struct CreateEMEventPhoton { posZint16 = +1; } } - if constexpr (eventtype == EMEventType::kEvent) { - event_norm_info(collision.selection_raw(), collision.rct_raw(), posZint16, static_cast(105.f * 500.f)); - } else if constexpr (eventtype == EMEventType::kEvent_Cent || eventtype == EMEventType::kEvent_Cent_Qvec) { + if constexpr (eventtype == EMEventType::kEvent_Cent || eventtype == EMEventType::kEvent_Cent_Qvec) { event_norm_info(collision.selection_raw(), collision.rct_raw(), posZint16, static_cast(collision.centFT0C() * 500.f)); } else { event_norm_info(collision.selection_raw(), collision.rct_raw(), posZint16, static_cast(105.f * 500.f)); @@ -200,14 +136,6 @@ struct CreateEMEventPhoton { continue; } - if constexpr (isTriggerAnalysis) { - if (collision.swtaliastmp_raw() == 0) { - continue; - } else { - emswtbit(collision.swtaliastmp_raw()); - } - } - const float qDefault = 999.f; // default value for q vectors if not obtained registry.fill(HIST("hEventCounter"), 1); @@ -229,11 +157,7 @@ struct CreateEMEventPhoton { eventWeights(1.f); } - if constexpr (eventtype == EMEventType::kEvent) { - eventCent(105.f, 105.f, 105.f); - eventQvec(qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, - qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault); - } else if constexpr (eventtype == EMEventType::kEvent_Cent) { + if constexpr (eventtype == EMEventType::kEvent_Cent) { eventCent(collision.centFT0M(), collision.centFT0A(), collision.centFT0C()); eventQvec(qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault, qDefault); @@ -260,7 +184,7 @@ struct CreateEMEventPhoton { using MyJJCollisions = soa::Join; - void fillEventWeights(MyCollisionsMC const& collisions, MyJJCollisions const&, MyBCs const&, aod::FullMCParticleLevelJets const& jets) + void fillEventWeights(MyCollisionsMC const& collisions, MyJJCollisions const&, aod::FullMCParticleLevelJets const& jets) { for (const auto& collision : collisions) { if (!collision.has_mcCollision()) { @@ -270,9 +194,6 @@ struct CreateEMEventPhoton { continue; } - auto bc = collision.template foundBC_as(); - initCCDB(bc); - auto mcCollision = collision.mcCollision_as(); // Outlier rejection: Set weight to 0 for events with large pTJet/pTHard @@ -280,8 +201,9 @@ struct CreateEMEventPhoton { auto jetsInThisCollision = jets.sliceBy(perCollision_jet, mcCollision.globalIndex()); float collisionWeight = mcCollision.weight(); for (const auto& jet : jetsInThisCollision) { - if (jet.pt() > maxpTJetOverpTHard * mcCollision.ptHard()) + if (jet.pt() > maxpTJetOverpTHard * mcCollision.ptHard()) { collisionWeight = 0.f; + } registry.fill(HIST("hJJ_pTHardVsJetpT"), mcCollision.ptHard(), jet.pt()); } @@ -308,23 +230,23 @@ struct CreateEMEventPhoton { } PROCESS_SWITCH(CreateEMEventPhoton, processEvent_Cent_Qvec, "process event info", false); - void processEvent_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs) - { - skimEvent(collisions, bcs); - } - PROCESS_SWITCH(CreateEMEventPhoton, processEvent_SWT, "process event info", false); + // void processEvent_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs) + // { + // skimEvent(collisions, bcs); + // } + // PROCESS_SWITCH(CreateEMEventPhoton, processEvent_SWT, "process event info", false); - void processEvent_SWT_Cent(MyCollisionsWithSWT_Cent const& collisions, MyBCs const& bcs) - { - skimEvent(collisions, bcs); - } - PROCESS_SWITCH(CreateEMEventPhoton, processEvent_SWT_Cent, "process event info", false); + // void processEvent_SWT_Cent(MyCollisionsWithSWT_Cent const& collisions, MyBCs const& bcs) + // { + // skimEvent(collisions, bcs); + // } + // PROCESS_SWITCH(CreateEMEventPhoton, processEvent_SWT_Cent, "process event info", false); - void processEvent_SWT_Cent_Qvec(MyCollisionsWithSWT_Cent_Qvec const& collisions, MyBCs const& bcs) - { - skimEvent(collisions, bcs); - } - PROCESS_SWITCH(CreateEMEventPhoton, processEvent_SWT_Cent_Qvec, "process event info", false); + // void processEvent_SWT_Cent_Qvec(MyCollisionsWithSWT_Cent_Qvec const& collisions, MyBCs const& bcs) + // { + // skimEvent(collisions, bcs); + // } + // PROCESS_SWITCH(CreateEMEventPhoton, processEvent_SWT_Cent_Qvec, "process event info", false); // for MC void processEventMC(MyCollisionsMC const& collisions, MyBCs const& bcs) @@ -336,7 +258,7 @@ struct CreateEMEventPhoton { void processEventJJMC(MyCollisionsMC const& collisions, MyJJCollisions const& mcCollisions, MyBCs const& bcs, aod::FullMCParticleLevelJets const& jets) { skimEvent(collisions, bcs); - fillEventWeights(collisions, mcCollisions, bcs, jets); + fillEventWeights(collisions, mcCollisions, jets); } PROCESS_SWITCH(CreateEMEventPhoton, processEventJJMC, "process event info", false); @@ -419,10 +341,10 @@ struct AssociatePhotonToEMEvent { // PROCESS_SWITCH(AssociatePhotonToEMEvent, processChargedTrack, "process indexing for charged tracks", false); PROCESS_SWITCH(AssociatePhotonToEMEvent, processDummy, "process dummy", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"create-emevent-photon"}), - adaptAnalysisTask(cfgc, TaskName{"associate-photon-to-emevent"}), + adaptAnalysisTask(context, TaskName{"create-emevent-photon"}), + adaptAnalysisTask(context, TaskName{"associate-photon-to-emevent"}), }; } diff --git a/PWGEM/PhotonMeson/TableProducer/eventSelection.cxx b/PWGEM/PhotonMeson/TableProducer/eventSelection.cxx new file mode 100644 index 00000000000..64c46cda5b3 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/eventSelection.cxx @@ -0,0 +1,196 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// This code produces event selection table for PWG-EM Photon/Meson. + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +// +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using EventAccBits = o2::aod::pmevsel::EventAcceptanceBits; + +using MyCollisions = soa::Join; +using MyCollisions_Cent = soa::Join; + +using MyCollisionsMC = soa::Join; +using MyCollisionsMC_Cent = soa::Join; + +struct PMEventSelection { + Produces pmevsel; + Produces pmevselbits; + + // Configurables + Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + Configurable cfgCentMin{"cfgCentMin", -1.f, "min. centrality"}; + Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; + + // for RCT + Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; + Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + + Configurable cfgZvtxMin{"cfgZvtxMin", -1e+10, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", 1e+10, "max. Zvtx"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", false, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. track occupancy"}; + Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. track occupancy"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. occupancy"}; + Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; + + Configurable cfgRequireTVXinEMC{"cfgRequireTVXinEMC", false, "require kTVXinEMC (only for EMC analyses)"}; + + Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 condition"}; + + o2::aod::rctsel::RCTFlagsChecker rctChecker; + + std::vector vecEvSelBits; + + void init(InitContext&) + { + rctChecker.init(cfgRCTLabel.value, cfgCheckZDC.value, cfgTreatLimitedAcceptanceAsBad.value); + vecEvSelBits.assign(EventAccBits::kSize, 0); + } + + template + bool isSelectedEvent(TCollision const& collision) + { + vecEvSelBits[EventAccBits::kAll]++; + if constexpr (std::is_same_v, MyCollisionsMC::iterator> || std::is_same_v, MyCollisionsMC_Cent::iterator>) { + if (!collision.has_mcCollision()) { + return false; + } + } + vecEvSelBits[EventAccBits::kHasMCColl]++; + + if (collision.posZ() < cfgZvtxMin || cfgZvtxMax < collision.posZ()) { + return false; + } + vecEvSelBits[EventAccBits::kGoodZVtx]++; + + if (cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + vecEvSelBits[EventAccBits::kIsFT0AND]++; + + if (cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + vecEvSelBits[EventAccBits::kNoTFB]++; + + if (cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + vecEvSelBits[EventAccBits::kITSROFB]++; + + if (cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + vecEvSelBits[EventAccBits::kNoSameBunchPileUp]++; + + if (cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + vecEvSelBits[EventAccBits::kGoodZVtxFTOPV]++; + + if (cfgRequireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + vecEvSelBits[EventAccBits::kNoCollInTimeRange]++; + + if (!(cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < cfgTrackOccupancyMax)) { + return false; + } + vecEvSelBits[EventAccBits::kGoodTrackOccupancy]++; + + if (!(cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < cfgFT0COccupancyMax)) { + return false; + } + vecEvSelBits[EventAccBits::kGoodFT0Occupancy]++; + + if (cfgRequireTVXinEMC && !collision.alias_bit(triggerAliases::kTVXinEMC)) { + return false; + } + vecEvSelBits[EventAccBits::kTVXInEMC]++; + + if constexpr (std::is_same_v, MyCollisions_Cent::iterator>) { + std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { + return false; + } + } + vecEvSelBits[EventAccBits::kGoodCent]++; + + if (cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + // LOGF(info, "rejected by RCT flag"); + return false; + } + vecEvSelBits[EventAccBits::kGoodRCT]++; + + if (cfgRequireSel8 && !collision.sel8()) { + return false; + } + vecEvSelBits[EventAccBits::kGoodSel8]++; + + return true; + } + + template + void processEventSelection(TCollisions const& collisions) + { + // reset the event counter to zero for all elements + vecEvSelBits.assign(EventAccBits::kSize, 0); + + for (const auto& collision : collisions) { + pmevsel(isSelectedEvent(collision)); + } // end of collision loop + // Write event selection info at the end of DF + pmevselbits(vecEvSelBits); + } // end of process + + PROCESS_SWITCH_FULL(PMEventSelection, processEventSelection, processEventSelection, "event selection", true); + PROCESS_SWITCH_FULL(PMEventSelection, processEventSelection, processEventSelection_Cent, "event selection with cent", false); + PROCESS_SWITCH_FULL(PMEventSelection, processEventSelection, processEventSelectionMC, "event selection MC", false); + PROCESS_SWITCH_FULL(PMEventSelection, processEventSelection, processEventSelectionMC_Cent, "event selection MC with cent", false); +}; +WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"em-event-selection"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/nonLinProducer.cxx b/PWGEM/PhotonMeson/TableProducer/nonLinProducer.cxx index 57a551a40c1..74aa9d98e11 100644 --- a/PWGEM/PhotonMeson/TableProducer/nonLinProducer.cxx +++ b/PWGEM/PhotonMeson/TableProducer/nonLinProducer.cxx @@ -21,9 +21,11 @@ #include "PWGEM/PhotonMeson/Utils/emcalHistoDefinitions.h" #include +#include #include #include #include +#include #include #include #include @@ -47,7 +49,7 @@ struct NonLinProducer { enum CentralityEstimator : uint8_t { None = 0, - CFT0A = 1, + CFT0A, CFT0C, CFT0M, NCentralityEstimators @@ -76,7 +78,7 @@ struct NonLinProducer { EMNonLin::Context emNonLinContextEMC; EMNonLin::Context emNonLinContextPCM; - o2::framework::Service ccdb; + o2::framework::Service ccdb{}; TMatrixD* emcalMatrix = nullptr; TMatrixD* pcmMatrix = nullptr; @@ -245,8 +247,8 @@ struct NonLinProducer { PROCESS_SWITCH(NonLinProducer, processPCMDummy, "Createdumy Non Lin table for PCM.", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + WorkflowSpec workflow{adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx index 1199b2415a2..6ee033f2817 100644 --- a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.cxx @@ -10,1035 +10,18 @@ // or submit itself to any jurisdiction. // -/// \file photonconversionbuilder.cxx +/// \file photonconversionbuilder.ch /// \brief this task produces photon data table with KFParticle. /// \author Daiki Sekihata , Tokyo -#ifndef HomogeneousField -#define HomogeneousField // needed for KFParticle::SetField(magneticField); -#endif +#include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" -#include "PWGEM/Dilepton/Utils/PairUtilities.h" -#include "PWGEM/PhotonMeson/Core/EmMlResponsePCM.h" -#include "PWGEM/PhotonMeson/Core/V0PhotonCandidate.h" -#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" -#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" -#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" -#include "PWGEM/PhotonMeson/Utils/TrackSelection.h" - -#include "Common/Core/RecoDecay.h" -#include "Common/Core/TPCVDriftManager.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Tools/KFparticle/KFUtilities.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace o2; -using namespace o2::soa; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace o2::constants::physics; -using namespace o2::pwgem::photonmeson; -using std::array; - -using MyCollisions = soa::Join; -using MyCollisionsWithSWT = soa::Join; -using MyCollisionsMC = soa::Join; - -using MyTracksIU = soa::Join; -using MyTracksIUMC = soa::Join; - -enum MatCorrType { - None = 0, - TGeo = 1, - LUT = 2 -}; - -struct PhotonConversionBuilder { - Produces v0photonskf; - Produces v0legs; - Produces v0legsXYZ; - Produces v0legsDeDxMC; - Produces v0photonsphivpsi; - // Produces v0photonskfcov; - // Produces events_ngpcm; - - // CCDB options - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; - - // Operation and minimisation criteria - Configurable d_bz_input{"d_bz", -999, "bz field, -999 is automatic"}; - Configurable useMatCorrType{"useMatCorrType", 0, "0: none, 1: TGeo, 2: LUT"}; - - // single track cuts - Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; - Configurable mincrossedrows{"mincrossedrows", 40, "min crossed rows"}; - Configurable moveTPCTracks{"moveTPCTracks", true, "Move TPC-only tracks under the collision assumption"}; - Configurable disableITSonlyTracks{"disableITSonlyTracks", false, "disable ITSonly tracks in V0 legs"}; - Configurable disableTPConlyTracks{"disableTPConlyTracks", false, "disable TPConly tracks in V0 legs"}; - Configurable requireITShit{"requireITShit", false, "require ITS hit to V0 legs"}; - - Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max chi2/NclsTPC"}; // default 4.0 + 1.0 - Configurable maxchi2its{"maxchi2its", 6.0, "max chi2/NclsITS"}; // default 5.0 + 1.0 - Configurable maxpt_itsonly{"maxpt_itsonly", 0.15, "max pT for ITSonly tracks at SV"}; - Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", 4.0, "max. TPC n sigma for electron"}; - Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -4.0, "min. TPC n sigma for electron"}; - Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - Configurable dcanegtopv{"dcanegtopv", 0.1, "DCA Neg To PV"}; - Configurable dcapostopv{"dcapostopv", 0.1, "DCA Pos To PV"}; - Configurable maxX{"maxX", 83.1, "max X for track IU"}; - Configurable min_pt_trackiu{"min_pt_trackiu", 0.05, "min pT for trackiu"}; // this comes from online processing. pT of track seed is above 50 MeV/c in B = 0.5 T, 20 MeV/c in B = 0.2 T. - - // v0 cuts - Configurable min_v0cospa_tpconly{"min_v0cospa_tpconly", 0.99, "min V0 CosPA to V0s with TPConly tracks"}; // double -> N.B. dcos(x)/dx = 0 at x=0) - Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; // double -> N.B. dcos(x)/dx = 0 at x=0) - Configurable max_dcav0dau_tpconly{"max_dcav0dau_tpconly", 3.0, "max distance btween 2 legs to V0s with TPConly tracks"}; - Configurable max_dcav0dau_its{"max_dcav0dau_its", 0.5, "max distance btween 2 legs to V0s with ITS hits"}; - Configurable max_dcav0dau_itsibss{"max_dcav0dau_itsibss", 1.0, "max distance btween 2 legs to V0s with ITS hits on ITSib SS"}; - Configurable max_dcav0dau_tpc_inner_fc{"max_dcav0dau_tpc_inner_fc", 1.5, "max distance btween 2 legs to V0s with ITS hits on TPC inner FC"}; - Configurable min_v0radius{"min_v0radius", 1.0, "min v0 radius"}; - Configurable max_v0radius{"max_v0radius", 90.0, "max v0 radius"}; - Configurable margin_r_its{"margin_r_its", 3.0, "margin for r cut in cm"}; - Configurable margin_r_tpc{"margin_r_tpc", 7.0, "margin for r cut in cm"}; - Configurable margin_r_itstpc_tpc{"margin_r_itstpc_tpc", 7.0, "margin for r cut in cm"}; - Configurable margin_z{"margin_z", 7.0, "margin for z cut in cm"}; - Configurable max_alpha_ap{"max_alpha_ap", 0.95, "max alpha for AP cut"}; - Configurable max_qt_ap{"max_qt_ap", 0.01, "max qT for AP cut"}; - Configurable min_pt_v0{"min_pt_v0", 0.1, "min pT for v0 photons at PV"}; - Configurable max_pt_v0_itsonly{"max_pt_v0_itsonly", 0.3, "max pT for v0 photons wth 2 ITSonly tracks at PV"}; - Configurable max_eta_v0{"max_eta_v0", 0.9, "max eta for v0 photons at PV"}; - Configurable kfMassConstrain{"kfMassConstrain", -1.f, "mass constrain for the KFParticle mother particle"}; - Configurable max_r_req_its{"max_r_req_its", 16.0, "max Rxy for V0 with ITS hits"}; - Configurable min_r_tpconly{"min_r_tpconly", 32.0, "min Rxy for V0 with TPConly tracks"}; - Configurable max_r_itsmft_ss{"max_r_itsmft_ss", 66.0, "max Rxy for ITS/MFT SS"}; - Configurable max_dcatopv_xy_v0{"max_dcatopv_xy_v0", +1e+10, "max. DCAxy to PV for V0"}; - Configurable max_dcatopv_z_v0{"max_dcatopv_z_v0", +1e+10, "max. DCAz to PV for V0"}; - Configurable reject_v0_on_itsib{"reject_v0_on_itsib", true, "flag to reject v0s on ITSib"}; - - // PCM ML inference - Configurable applyPCMMl{"applyPCMMl", false, "Flag to apply ML selections"}; - Configurable use2DBinning{"use2DBinning", false, "Flag to enable/disable 2D binning for ML application"}; - Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - Configurable nClassesPCMMl{"nClassesPCMMl", static_cast(o2::analysis::em_cuts_ml::NCutScores), "Number of classes in ML model"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; - Configurable centTypePCMMl{"centTypePCMMl", 2, "Centrality type for 2D ML application: FT0M:0, FT0A:1, FT0C:2"}; - Configurable> cutDirPCMMl{"cutDirPCMMl", std::vector{o2::analysis::em_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; - Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"path_ccdb/BDT_PCM/"}, "Paths of models on CCDB"}; - Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_PCM.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; - Configurable> labelsBinsPCMMl{"labelsBinsPCMMl", std::vector{"bin 0", "bin 1"}, "Labels for bins"}; - Configurable> labelsCutScoresPCMMl{"labelsCutScoresPCMMl", std::vector{o2::analysis::em_cuts_ml::labelsCutScore}, "Labels for cut scores"}; - Configurable> binsPtPCMMl{"binsPtPCMMl", std::vector{0.0, +1e+10}, "pT bin limits for ML application"}; - Configurable> binsCentPCMMl{"binsCentPCMMl", std::vector{0.0, 100.0}, "Centrality bin limits for ML application"}; - Configurable> cutsPCMMlFlat{"cutsPCMMlFlat", {0.5}, "Flattened ML cuts: [bin0_score0, bin0_score1, ..., binN_scoreM]"}; - - Configurable propV0LegsRadius{"propV0LegsRadius", 60.f, "Radius to which the V0 legs are propagated to calculate psipair and phiV"}; - - o2::analysis::EmMlResponsePCM emMlResponse; - std::vector outputML; - V0PhotonCandidate v0photoncandidate; - o2::ccdb::CcdbApi ccdbApi; - - int mRunNumber; - float d_bz; - float maxSnp; // max sine phi for propagation - float maxStep; // max step size (cm) for propagation - Service ccdb; - o2::base::MatLayerCylSet* lut = nullptr; - o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - o2::aod::common::TPCVDriftManager mVDriftMgr; - - HistogramRegistry registry{ - "registry", - { - {"hCollisionCounter", "hCollisionCounter", {HistType::kTH1F, {{1, 0.5f, 1.5f}}}}, - {"V0/hAP", "Armenteros Podolanski;#alpha;q_{T} (GeV/c)", {HistType::kTH2F, {{200, -1.0f, 1.0f}, {250, 0, 0.25}}}}, - {"V0/hConversionPointXY", "conversion point in XY;X (cm);Y (cm)", {HistType::kTH2F, {{400, -100.0f, 100.0f}, {400, -100.f, 100.f}}}}, - {"V0/hConversionPointRZ", "conversion point in RZ;Z (cm);R_{xy} (cm)", {HistType::kTH2F, {{200, -100.0f, 100.0f}, {200, 0.f, 100.f}}}}, - {"V0/hPt", "pT of V0 at PV;p_{T,#gamma} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, - {"V0/hEtaPhi", "#eta vs. #varphi of V0 at PV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, - {"V0/hCosPA", "cosine of pointing angle;cosine of pointing angle", {HistType::kTH1F, {{100, 0.99f, 1.f}}}}, - {"V0/hCosPA_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, - {"V0/hCosPAXY_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, - {"V0/hCosPARZ_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, - {"V0/hPCA", "distance between 2 legs at SV;PCA (cm)", {HistType::kTH1F, {{500, 0.0f, 5.f}}}}, - {"V0/hPCA_Rxy", "distance between 2 legs at SV;R_{xy} (cm);PCA (cm)", {HistType::kTH2F, {{200, 0, 100}, {500, 0.0f, 5.f}}}}, - {"V0/hPCA_CosPA", "distance between 2 legs at SV vs. cosPA;cosine of pointing angle;PCA (cm)", {HistType::kTH2F, {{100, 0.99, 1}, {500, 0.0f, 5.f}}}}, - {"V0/hDCAxyz", "DCA to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -5.f, +5.f}, {200, -5.f, +5.f}}}}, - {"V0/hMeeSV_Rxy", "mee at SV vs. R_{xy};R_{xy} (cm);m_{ee} at SV (GeV/c^{2})", {HistType::kTH2F, {{200, 0.0f, 100.f}, {100, 0, 0.1f}}}}, - {"V0/hRxy_minX_ITSonly_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_ITSTPC_ITSTPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_ITSTPC_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_ITSTPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hRxy_minX_TPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, - {"V0/hPCA_diffX", "PCA vs. trackiu X - R_{xy};distance btween 2 legs (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{500, 0.0f, 5.f}, {100, -50.0, 50.0f}}}}, - {"V0/hPhiVPsiPair", "phiV vs. psi pair;#psi_{pair} (rad.);#phi_{V} (rad.)", {HistType::kTH2F, {{500, -o2::constants::math::PI, o2::constants::math::PI}, {500, 0.0f, o2::constants::math::TwoPI}}}}, - {"V0Leg/hPt", "pT of leg at SV;p_{T,e} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, - {"V0Leg/hEtaPhi", "#eta vs. #varphi of leg at SV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, - {"V0Leg/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, 0, 1}}}}, - {"V0Leg/hDCAxyz", "DCA xy vs. z to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -50.f, 50.f}, {200, -50.f, +50.f}}}}, - {"V0Leg/hdEdx_Pin", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);TPC dE/dx", {HistType::kTH2F, {{1000, 0.f, 10.f}, {200, 0.f, 200.f}}}}, - {"V0Leg/hTPCNsigmaEl", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);n #sigma_{e}^{TPC}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, -5.f, +5.f}}}}, - {"V0Leg/hXZ", "track iu x vs. z;z (cm);x (cm)", {HistType::kTH2F, {{200, -100.f, 100.f}, {200, 0.f, 100.f}}}}, - }}; - - void init(InitContext&) - { - mRunNumber = 0; - d_bz = 0; - maxSnp = 0.85f; // could be changed later - maxStep = 2.00f; // could be changed later - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - switch (useMatCorrType) { - case MatCorrType::TGeo: - LOGF(info, "TGeo correction requested, loading geometry"); - if (!o2::base::GeometryManager::isGeometryLoaded()) { - ccdb->get(geoPath); - } - matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; - break; - case MatCorrType::LUT: - LOGF(info, "LUT correction requested, loading LUT"); - lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - break; - default: - LOGF(info, "no correction requested, loading LUT by default!"); - lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); - matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; - break; - } - - if (applyPCMMl) { - if (use2DBinning) { - int binsNPt = static_cast(binsPtPCMMl->size()) - 1; - int binsNCent = static_cast(binsCentPCMMl->size()) - 1; - int binsN = binsNPt * binsNCent; - if (binsN * static_cast(cutDirPCMMl->size()) != static_cast(cutsPCMMlFlat->size())) { - LOG(fatal) << "Mismatch in number of bins and cuts provided for 2D ML application: binsN * cutDirPCMMl: " << int(binsN) * int(cutDirPCMMl->size()) << " bins vs. cutsPCMMlFlat: " << cutsPCMMlFlat->size() << " cuts"; - } - if (binsN != static_cast(onnxFileNames->size())) { - LOG(fatal) << "Mismatch in number of bins and ONNX files provided for 2D ML application: binsN " << binsN << " bins vs. onnxFileNames: " << onnxFileNames->size() << " ONNX files"; - } - if (binsN != static_cast(labelsBinsPCMMl->size())) { - LOG(fatal) << "Mismatch in number of bins and labels provided for 2D ML application: binsN:" << binsN << " bins vs. labelsBinsPCMMl: " << labelsBinsPCMMl->size() << " labels"; - } - if (static_cast(cutDirPCMMl->size()) != nClassesPCMMl) { - LOG(fatal) << "Mismatch in number of classes and cut directions provided for 2D ML application: nClassesPCMMl: " << nClassesPCMMl << " classes vs. cutDirPCMMl: " << cutDirPCMMl->size() << " cut directions"; - } - if (static_cast(labelsCutScoresPCMMl->size()) != nClassesPCMMl) { - LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for 2D ML application: nClassesPCMMl: " << nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << labelsCutScoresPCMMl->size() << " labels"; - } - LabeledArray cutsPCMMl(cutsPCMMlFlat->data(), binsN, nClassesPCMMl, labelsBinsPCMMl, labelsCutScoresPCMMl); - emMlResponse.configure2D(binsPtPCMMl, binsCentPCMMl, cutsPCMMl, cutDirPCMMl, nClassesPCMMl); - } else { - int binsNPt = static_cast(binsPtPCMMl->size()) - 1; - if (binsNPt * static_cast(cutDirPCMMl->size()) != static_cast(cutsPCMMlFlat->size())) { - LOG(fatal) << "Mismatch in number of pT bins and cuts provided for ML application: binsNPt * cutDirPCMMl:" << binsNPt * cutDirPCMMl->size() << " bins vs. cutsPCMMlFlat: " << cutsPCMMlFlat->size() << " cuts"; - } - if (binsNPt != static_cast(onnxFileNames->size())) { - LOG(fatal) << "Mismatch in number of pT bins and ONNX files provided for ML application: binsNPt " << binsNPt << " bins vs. onnxFileNames: " << onnxFileNames->size() << " ONNX files"; - } - if (binsNPt != static_cast(labelsBinsPCMMl->size())) { - LOG(fatal) << "Mismatch in number of pT bins and labels provided for ML application: binsNPt:" << binsNPt << " bins vs. labelsBinsPCMMl: " << labelsBinsPCMMl->size() << " labels"; - } - if (nClassesPCMMl != static_cast(cutDirPCMMl->size())) { - LOG(fatal) << "Mismatch in number of classes and cut directions provided for ML application: nClassesPCMMl: " << nClassesPCMMl << " classes vs. cutDirPCMMl: " << cutDirPCMMl->size() << " cut directions"; - } - if (static_cast(labelsCutScoresPCMMl->size()) != nClassesPCMMl) { - LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for ML application: nClassesPCMMl:" << nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << labelsCutScoresPCMMl->size() << " labels"; - } - LabeledArray cutsPCMMl(cutsPCMMlFlat->data(), binsNPt, nClassesPCMMl, labelsBinsPCMMl, labelsCutScoresPCMMl); - emMlResponse.configure(binsPtPCMMl, cutsPCMMl, cutDirPCMMl, nClassesPCMMl); - } - if (loadModelsFromCCDB) { - ccdbApi.init(ccdburl); - emMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); - } else { - emMlResponse.setModelPathsLocal(onnxFileNames); - } - emMlResponse.cacheInputFeaturesIndices(namesInputFeatures); - emMlResponse.init(); - if (nClassesPCMMl == 2) { - registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSignalScoreBeforeCutVsPt", "BDT signal score before cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSignalScoreAfterCutVsPt", "BDT signal score after cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - } else if (nClassesPCMMl == 3) { - registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt", "BDT primary photon score before cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTPrimaryPhotonScoreAfterCutVsPt", "BDT primary photon score after cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt", "BDT secondary photon score before cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTSecondaryPhotonScoreAfterCutVsPt", "BDT secondary photon score after cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - } else { - registry.add("V0/hBDTScoreBeforeCutVsPt", "BDT score before cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - registry.add("V0/hBDTScoreAfterCutVsPt", "BDT score after cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); - } - } - } - - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - - // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { - d_bz = d_bz_input; - o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); - } - o2::base::Propagator::initFieldFromGRP(&grpmag); - mRunNumber = bc.runNumber(); - return; - } - - auto run3grp_timestamp = bc.timestamp(); - o2::parameters::GRPObject* grpo = nullptr; - o2::parameters::GRPMagField* grpmag = nullptr; - if (!skipGRPOquery) { - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - } - if (grpo != nullptr) { - o2::base::Propagator::initFieldFromGRP(grpo); - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (grpmag == nullptr) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - o2::base::Propagator::initFieldFromGRP(grpmag); - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } - mRunNumber = bc.runNumber(); - - if (useMatCorrType == 2) { - // setMatLUT only after magfield has been initalized (setMatLUT has implicit and problematic init field call if not) - o2::base::Propagator::Instance()->setMatLUT(lut); - } - /// Set magnetic field for KF vertexing - const float magneticField = o2::base::Propagator::Instance()->getNominalBz(); - KFParticle::SetField(magneticField); - - mVDriftMgr.init(&ccdb->instance()); - } - - void updateCCDB(aod::BCsWithTimestamps::iterator const& bc) - { - auto timestamp = bc.timestamp(); - - mVDriftMgr.update(timestamp); - } - - std::pair> its_ib_Requirement = {0, {0, 1, 2}}; // no hit on 3 ITS ib layers. - template - bool checkV0leg(TTrack const& track) - { - if constexpr (isMC) { - if (!track.has_mcParticle()) { - return false; - } - } - - if (disableITSonlyTracks && isITSonlyTrack(track)) { - return false; - } - - if (disableTPConlyTracks && isTPConlyTrack(track)) { - return false; - } - - if (requireITShit && !track.hasITS()) { - return false; - } - - if (track.x() > maxX) { - return false; - } - - if (!track.hasITS() && !track.hasTPC()) { - return false; - } - - if (track.hasITS() && !track.hasTPC() && (track.hasTRD() || track.hasTOF())) { // remove unrealistic track. this should not happen. - return false; - } - - if (track.hasTPC()) { - if (track.tpcNClsFound() < min_ncluster_tpc) { - return false; - } - if (track.tpcNClsCrossedRows() < mincrossedrows || track.tpcChi2NCl() > maxchi2tpc) { - return false; - } - if (track.tpcFractionSharedCls() > max_frac_shared_clusters_tpc) { - return false; - } - if (track.tpcNSigmaEl() < minTPCNsigmaEl || maxTPCNsigmaEl < track.tpcNSigmaEl()) { - return false; - } - } - - if (track.hasITS()) { - if (track.itsChi2NCl() > maxchi2its) { - return false; - } - - if (reject_v0_on_itsib) { - auto hits_ib = std::count_if(its_ib_Requirement.second.begin(), its_ib_Requirement.second.end(), [&](auto&& requiredLayer) { return track.itsClusterMap() & (1 << requiredLayer); }); - bool its_ob_only = hits_ib <= its_ib_Requirement.first; - if (!its_ob_only) { - return false; - } - } - } - - return true; - } - - float cospaXY_KF(KFParticle kfp, KFParticle PV) - { - float lx = kfp.GetX() - PV.GetX(); // flight length X - float ly = kfp.GetY() - PV.GetY(); // flight length Y - - float px = kfp.GetPx(); - float py = kfp.GetPy(); - float cospaXY = RecoDecay::dotProd(std::array{lx, ly}, std::array{px, py}) / (RecoDecay::sqrtSumOfSquares(lx, ly) * RecoDecay::sqrtSumOfSquares(px, py)); - if (cospaXY < -1.f) { - return -1.f; - } else if (cospaXY > 1.f) { - return 1.f; - } - return cospaXY; - } - - float cospaRZ_KF(KFParticle kfp, KFParticle PV) - { - float lx = kfp.GetX() - PV.GetX(); // flight length X - float ly = kfp.GetY() - PV.GetY(); // flight length Y - float lz = kfp.GetZ() - PV.GetZ(); // flight length Z - float lt = RecoDecay::sqrtSumOfSquares(lx, ly); // flight length R, i.e. transverse plane. - - float pt = RecoDecay::sqrtSumOfSquares(kfp.GetPx(), kfp.GetPy()); - float pz = kfp.GetPz(); - - float cospaRZ = RecoDecay::dotProd(std::array{lt, lz}, std::array{pt, pz}) / (RecoDecay::sqrtSumOfSquares(lt, lz) * RecoDecay::sqrtSumOfSquares(pt, pz)); - if (cospaRZ < -1.f) { - return -1.f; - } else if (cospaRZ > 1.f) { - return 1.f; - } - return cospaRZ; - } - - template - void fillTrackTable(TTrack const& track, TShiftedTrack const& shiftedtrack, const float dcaXY, const float dcaZ) - { - v0legs(track.collisionId(), track.globalIndex(), track.sign(), - shiftedtrack.GetPx(), shiftedtrack.GetPy(), shiftedtrack.GetPz(), dcaXY, dcaZ, - track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), - track.tpcChi2NCl(), track.tpcInnerParam(), track.tpcSignal(), - track.tpcNSigmaEl(), track.tpcNSigmaPi(), - track.itsClusterSizes(), track.itsChi2NCl(), track.detectorMap()); - - v0legsXYZ(shiftedtrack.GetX(), shiftedtrack.GetY(), shiftedtrack.GetZ()); - - if constexpr (isMC) { - v0legsDeDxMC(track.mcTunedTPCSignal()); - } - } - - template - void fillV0Table(TV0 const& v0, const bool filltable) - { - // Get tracks - const auto& pos = v0.template posTrack_as(); - const auto& ele = v0.template negTrack_as(); - const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. - // LOGF(info, "v0.collisionId() = %d, pos.collisionId() = %d, ele.collisionId() = %d", v0.collisionId(), pos.collisionId(), ele.collisionId()); - - if (pos.sign() * ele.sign() > 0) { // reject same sign pair - return; - } - - if (pos.pt() < min_pt_trackiu || ele.pt() < min_pt_trackiu) { - return; - } - - if (pos.globalIndex() == ele.globalIndex()) { - return; - } - - if (isITSonlyTrack(pos) && !ele.hasITS()) { - return; - } - - if (isITSonlyTrack(ele) && !pos.hasITS()) { - return; - } - - if (!checkV0leg(pos) || !checkV0leg(ele)) { - return; - } - - // LOGF(info, "v0.collisionId() = %d , v0.posTrackId() = %d , v0.negTrackId() = %d", v0.collisionId(), v0.posTrackId(), v0.negTrackId()); - - // if(isTPConlyTrack(ele)){ - // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.x() = %f, ele.y() = %f, ele.z() = %f, ele.tgl() = %f, ele.alpha() = %f, ele.snp() = %f, ele.signed1Pt() = %f", ele.globalIndex(), ele.x(), ele.y(), ele.z(), ele.tgl(), ele.alpha(), ele.snp(), ele.signed1Pt()); - // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.cYY() = %f, ele.cZY() = %f, ele.cZZ() = %f, ele.cSnpY() = %f, ele.cSnpZ() = %f, ele.cSnpSnp() = %f, ele.cTglY() = %f, ele.cTglZ() = %f, ele.cTglSnp() = %f, ele.cTglTgl() = %f, ele.c1PtY() = %f, ele.c1PtZ() = %f, ele.c1PtSnp() = %f, ele.c1PtTgl() = %f, ele.c1Pt21Pt2() = %f", ele.globalIndex(), ele.cYY(), ele.cZY(), ele.cZZ(), ele.cSnpY(), ele.cSnpZ(), ele.cSnpSnp(), ele.cTglY(), ele.cTglZ(), ele.cTglSnp(), ele.cTglTgl(), ele.c1PtY(), ele.c1PtZ(), ele.c1PtSnp(), ele.c1PtTgl(), ele.c1Pt21Pt2()); - // } - - // Calculate DCA with respect to the collision associated to the v0, not individual tracks - std::array dcaInfo; - - auto pTrack = getTrackParCov(pos); - if (moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack)) { - LOGP(error, "failed correction for positive tpc track"); - return; - } - auto pTrackC = pTrack; - pTrackC.setPID(o2::track::PID::Electron); - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackC, 2.f, matCorr, &dcaInfo); - auto posdcaXY = dcaInfo[0]; - auto posdcaZ = dcaInfo[1]; - - auto nTrack = getTrackParCov(ele); - if (moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack)) { - LOGP(error, "failed correction for negative tpc track"); - return; - } - auto nTrackC = nTrack; - nTrackC.setPID(o2::track::PID::Electron); - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackC, 2.f, matCorr, &dcaInfo); - auto eledcaXY = dcaInfo[0]; - auto eledcaZ = dcaInfo[1]; - - if (std::fabs(posdcaXY) < dcapostopv || std::fabs(eledcaXY) < dcanegtopv) { - return; - } - - float xyz[3] = {0.f, 0.f, 0.f}; - Vtx_recalculationParCov(o2::base::Propagator::Instance(), pTrack, nTrack, xyz, matCorr); - float rxy_tmp = RecoDecay::sqrtSumOfSquares(xyz[0], xyz[1]); - if (rxy_tmp > maxX + margin_r_tpc) { - return; - } - if (rxy_tmp < std::fabs(xyz[2]) * std::tan(2 * std::atan(std::exp(-max_eta_v0))) - margin_z) { - return; // RZ line cut - } - - float phiv = 999.f; - float psipair = 999.f; - float baseR = std::hypot(xyz[0], xyz[1]); - float offsetsR[3] = {propV0LegsRadius, 30.f, 10.f}; - bool pPropagatedSuccess = false; - bool nPropagatedSuccess = false; - auto pTrackProp = pTrack; - auto nTrackProp = nTrack; - for (float offsetR : offsetsR) { - pTrackProp = pTrack; - pTrackProp.setPID(o2::track::PID::Electron); - nTrackProp = nTrack; - nTrackProp.setPID(o2::track::PID::Electron); - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackProp, 2.f, matCorr, &dcaInfo); - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackProp, 2.f, matCorr, &dcaInfo); - pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); - nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); - if (pPropagatedSuccess && nPropagatedSuccess) { - KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); - KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); - phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); - psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); - break; - } else { - LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; - } - } - if (phiv == 999.f || psipair == 999.f) { - LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; - } - - KFPTrack kfp_track_pos = createKFPTrackFromTrackParCov(pTrack, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); - KFPTrack kfp_track_ele = createKFPTrackFromTrackParCov(nTrack, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); - KFParticle kfp_pos(kfp_track_pos, kPositron); - KFParticle kfp_ele(kfp_track_ele, kElectron); - const KFParticle* GammaDaughters[2] = {&kfp_pos, &kfp_ele}; - - KFParticle gammaKF; - gammaKF.SetConstructMethod(2); - gammaKF.Construct(GammaDaughters, 2); - if (kfMassConstrain > -0.1) { - gammaKF.SetNonlinearMassConstraint(kfMassConstrain); - } - KFPVertex kfpVertex = createKFPVertexFromCollision(collision); - KFParticle KFPV(kfpVertex); - - // Transport the gamma to the recalculated decay vertex - KFParticle gammaKF_DecayVtx = gammaKF; // with respect to (0,0,0) - gammaKF_DecayVtx.TransportToPoint(xyz); - - float cospa_kf = cpaFromKF(gammaKF_DecayVtx, KFPV); - if (!ele.hasITS() && !pos.hasITS()) { - if (cospa_kf < min_v0cospa_tpconly) { - return; - } - } else { - if (cospa_kf < min_v0cospa_its) { - return; - } - } - - float rxy = RecoDecay::sqrtSumOfSquares(gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); - if (rxy > maxX + margin_r_tpc) { - return; - } - if (rxy < std::fabs(gammaKF_DecayVtx.GetZ()) * std::tan(2 * std::atan(std::exp(-max_eta_v0))) - margin_z) { - return; // RZ line cut - } - if (rxy < min_v0radius || max_v0radius < rxy) { - return; - } - - if (!filltable) { - if (isITSTPCTrack(pos) && isITSTPCTrack(ele)) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSTPC"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - } else if (isITSonlyTrack(pos) && isITSonlyTrack(ele)) { - registry.fill(HIST("V0/hRxy_minX_ITSonly_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - } else if ((isITSTPCTrack(pos) && isITSonlyTrack(ele)) || (isITSTPCTrack(ele) && isITSonlyTrack(pos))) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - } else if (isITSTPCTrack(pos) && !ele.hasITS()) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(pTrack.getX(), 83.f), std::min(pTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive - } else if (isITSTPCTrack(ele) && !pos.hasITS()) { - registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(nTrack.getX(), 83.f), std::min(nTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive - } else { - registry.fill(HIST("V0/hRxy_minX_TPC_TPC"), std::min(83.f, 83.f), std::min(83.f, 83.f) - rxy); // trackiu.x() - rxy should be positive - } - } - - if (pos.hasITS() && ele.hasITS()) { // ITSonly-ITSonly, ITSTPC-ITSTPC, ITSTPC-ITSonly - if (rxy > std::min(pTrack.getX(), nTrack.getX()) + margin_r_its) { - return; - } - } else if (!pos.hasITS() && ele.hasITS()) { // ITSTPC-TPC - if (rxy > std::min(83.f, nTrack.getX()) + margin_r_itstpc_tpc) { - return; - } - } else if (pos.hasITS() && !ele.hasITS()) { // ITSTPC-TPC - if (rxy > std::min(pTrack.getX(), 83.f) + margin_r_itstpc_tpc) { - return; - } - } else if (!pos.hasITS() && !ele.hasITS()) { // TPC-TPC - if (rxy > std::min(83.f, 83.f) + margin_r_tpc) { - return; - } - } - - if ((!pos.hasITS() || !ele.hasITS()) && rxy < max_r_req_its) { // conversion points smaller than max_r_req_its have to be detected with ITS hits. - return; - } - - if ((!pos.hasITS() && !ele.hasITS()) && rxy < min_r_tpconly) { // TPConly tracks can detect conversion points larger than min_r_tpconly. - return; - } - - // Apply a topological constraint of the gamma to the PV. Parameters will be given at the primary vertex. - KFParticle gammaKF_PV = gammaKF; - gammaKF_PV.SetProductionVertex(KFPV); - float v0pt = RecoDecay::sqrtSumOfSquares(gammaKF_PV.GetPx(), gammaKF_PV.GetPy()); - float v0eta = RecoDecay::eta(std::array{gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz()}); - float v0phi = RecoDecay::constrainAngle(RecoDecay::phi(gammaKF_PV.GetPx(), gammaKF_PV.GetPy())); - - // KFParticle gammaKF_DecayVtx2 = gammaKF; - // gammaKF_DecayVtx2.SetProductionVertex(KFPV); - // gammaKF_DecayVtx2.TransportToPoint(xyz); - // LOGF(info, "gammaKF_PV.GetPx() = %f, gammaKF_DecayVtx.GetPx() = %f, gammaKF_DecayVtx2.GetPx() = %f", gammaKF_PV.GetPx(), gammaKF_DecayVtx.GetPx(), gammaKF_DecayVtx2.GetPx()); - // LOGF(info, "gammaKF_PV.GetPy() = %f, gammaKF_DecayVtx.GetPy() = %f, gammaKF_DecayVtx2.GetPy() = %f", gammaKF_PV.GetPy(), gammaKF_DecayVtx.GetPy(), gammaKF_DecayVtx2.GetPy()); - // LOGF(info, "gammaKF_PV.GetPz() = %f, gammaKF_DecayVtx.GetPz() = %f, gammaKF_DecayVtx2.GetPz() = %f", gammaKF_PV.GetPz(), gammaKF_DecayVtx.GetPz(), gammaKF_DecayVtx2.GetPz()); - - if (std::fabs(v0eta) > max_eta_v0 || v0pt < min_pt_v0) { - return; - } - - if (isITSonlyTrack(ele) && isITSonlyTrack(pos) && v0pt > max_pt_v0_itsonly) { - return; - } - - KFParticle kfp_pos_DecayVtx = kfp_pos; // Don't set Primary Vertex - KFParticle kfp_ele_DecayVtx = kfp_ele; // Don't set Primary Vertex - kfp_pos_DecayVtx.TransportToPoint(xyz); // Don't set Primary Vertex - kfp_ele_DecayVtx.TransportToPoint(xyz); // Don't set Primary Vertex - - float cospaXYKF = cospaXY_KF(gammaKF_DecayVtx, KFPV); - float cospaRZKF = cospaRZ_KF(gammaKF_DecayVtx, KFPV); - CentType centType = static_cast(centTypePCMMl.value); - v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, posdcaZ, eledcaXY, eledcaZ); - - if (!ele.hasITS() && !pos.hasITS()) { // V0s with TPConly-TPConly - if (max_r_itsmft_ss < rxy && rxy < maxX + margin_r_tpc) { - if (v0photoncandidate.getPCA() > max_dcav0dau_tpc_inner_fc) { - return; - } - } else { - if (v0photoncandidate.getPCA() > max_dcav0dau_tpconly) { - return; - } - } - } else { // V0s with ITS hits - if (rxy < max_r_req_its) { - if (v0photoncandidate.getPCA() > max_dcav0dau_itsibss) { - return; - } - } else { - if (v0photoncandidate.getPCA() > max_dcav0dau_its) { - return; - } - } - } - - if (isITSonlyTrack(pos) && v0photoncandidate.getPosPt() > maxpt_itsonly) { - return; - } - - if (isITSonlyTrack(ele) && v0photoncandidate.getElePt() > maxpt_itsonly) { - return; - } - - if (v0photoncandidate.getChi2NDF() > 6e+3) { // protection for uint16. - return; - } - - if (std::fabs(v0photoncandidate.getDcaXYToPV()) > max_dcatopv_xy_v0 || std::fabs(v0photoncandidate.getDcaZToPV()) > max_dcatopv_z_v0) { - return; - } - - if (!checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), max_alpha_ap, max_qt_ap)) { // store only photon conversions - return; - } - pca_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getPCA(); - cospa_map[std::make_tuple(v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex())] = v0photoncandidate.getCosPA(); - - if (applyPCMMl) { - bool isSelectedML = false; - std::vector mlInputFeatures = emMlResponse.getInputFeatures(v0photoncandidate, pos, ele); - if (use2DBinning) { - isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), v0photoncandidate.getCent(), outputML); - } else { - isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), outputML); - } - if (filltable) { - if (nClassesPCMMl == 2) { - registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSignalScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - } else if (nClassesPCMMl == 3) { - registry.fill(HIST("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[2]); - } else { - registry.fill(HIST("V0/hBDTScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - } - } - if (!isSelectedML) { - return; - } - if (filltable) { - if (nClassesPCMMl == 2) { - registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSignalScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - } else if (nClassesPCMMl == 3) { - registry.fill(HIST("V0/hBDTPrimaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - registry.fill(HIST("V0/hBDTSecondaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); - registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[2]); - } else { - registry.fill(HIST("V0/hBDTScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); - } - } - } - - if (filltable) { - registry.fill(HIST("V0/hAP"), v0photoncandidate.getAlpha(), v0photoncandidate.getQt()); - registry.fill(HIST("V0/hConversionPointXY"), gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); - registry.fill(HIST("V0/hConversionPointRZ"), gammaKF_DecayVtx.GetZ(), rxy); - registry.fill(HIST("V0/hPt"), v0photoncandidate.getPt()); - registry.fill(HIST("V0/hEtaPhi"), v0phi, v0eta); - registry.fill(HIST("V0/hCosPA"), v0photoncandidate.getCosPA()); - registry.fill(HIST("V0/hCosPA_Rxy"), rxy, v0photoncandidate.getCosPA()); - registry.fill(HIST("V0/hPCA"), v0photoncandidate.getPCA()); - registry.fill(HIST("V0/hPCA_CosPA"), v0photoncandidate.getCosPA(), v0photoncandidate.getPCA()); - registry.fill(HIST("V0/hPCA_Rxy"), rxy, v0photoncandidate.getPCA()); - registry.fill(HIST("V0/hDCAxyz"), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV()); - registry.fill(HIST("V0/hPCA_diffX"), v0photoncandidate.getPCA(), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive - registry.fill(HIST("V0/hPhiVPsiPair"), v0photoncandidate.getPsiPair(), v0photoncandidate.getPhiV()); - - // LOGF(info, "cospa_kf = %f, cospaXY_kf = %f, cospaRZ_kf = %f", cospa_kf, cospaXY_kf, cospaRZ_kf); - registry.fill(HIST("V0/hCosPAXY_Rxy"), rxy, cospaXYKF); - registry.fill(HIST("V0/hCosPARZ_Rxy"), rxy, cospaRZKF); - - for (const auto& leg : {kfp_pos_DecayVtx, kfp_ele_DecayVtx}) { - float legpt = RecoDecay::sqrtSumOfSquares(leg.GetPx(), leg.GetPy()); - float legeta = RecoDecay::eta(std::array{leg.GetPx(), leg.GetPy(), leg.GetPz()}); - float legphi = RecoDecay::constrainAngle(RecoDecay::phi(leg.GetPx(), leg.GetPy())); - registry.fill(HIST("V0Leg/hPt"), legpt); - registry.fill(HIST("V0Leg/hEtaPhi"), legphi, legeta); - } // end of leg loop - for (const auto& leg : {pos, ele}) { - registry.fill(HIST("V0Leg/hdEdx_Pin"), leg.tpcInnerParam(), leg.tpcSignal()); - registry.fill(HIST("V0Leg/hTPCNsigmaEl"), leg.tpcInnerParam(), leg.tpcNSigmaEl()); - } // end of leg loop - for (const auto& leg : {pTrack, nTrack}) { - registry.fill(HIST("V0Leg/hXZ"), leg.getZ(), leg.getX()); - registry.fill(HIST("V0Leg/hRelDeltaPt"), leg.getPt(), leg.getPt() * std::sqrt(leg.getSigma1Pt2())); - } // end of leg loop - registry.fill(HIST("V0Leg/hDCAxyz"), posdcaXY, posdcaZ); - registry.fill(HIST("V0Leg/hDCAxyz"), eledcaXY, eledcaZ); - - ROOT::Math::PxPyPzMVector vpos_sv(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); - ROOT::Math::PxPyPzMVector vele_sv(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); - ROOT::Math::PxPyPzMVector v0_sv = vpos_sv + vele_sv; - registry.fill(HIST("V0/hMeeSV_Rxy"), rxy, v0_sv.M()); - - v0photonskf(collision.globalIndex(), v0.globalIndex(), v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, - gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY(), gammaKF_DecayVtx.GetZ(), - gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz(), - v0_sv.M(), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV(), - cospa_kf, cospaXYKF, cospaRZKF, - v0photoncandidate.getPCA(), v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0photoncandidate.getChi2NDF()); - v0photonsphivpsi(v0photoncandidate.getPhiV(), v0photoncandidate.getPsiPair()); - - // v0photonskfcov(gammaKF_PV.GetCovariance(9), gammaKF_PV.GetCovariance(14), gammaKF_PV.GetCovariance(20), gammaKF_PV.GetCovariance(13), gammaKF_PV.GetCovariance(19), gammaKF_PV.GetCovariance(18)); - - fillTrackTable(pos, kfp_pos_DecayVtx, posdcaXY, posdcaZ); // positive leg first - fillTrackTable(ele, kfp_ele_DecayVtx, eledcaXY, eledcaZ); // negative leg second - } // end of fill table - } - - Preslice perCollision = o2::aod::v0::collisionId; - std::map, float> pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca - std::map, float> cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa - std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) - std::vector> stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) - std::unordered_map nv0_map; // map collisionId -> nv0 - - template - void build(TCollisions const& collisions, TV0s const& v0s, TTracks const&, TBCs const&) - { - for (const auto& collision : collisions) { - if constexpr (isMC) { - if (!collision.has_mcCollision()) { - continue; - } - } - - if (!collision.isSelected()) { - continue; - } - - if constexpr (isTriggerAnalysis) { - if (collision.swtaliastmp_raw() == 0) { - continue; - } - } - - nv0_map[collision.globalIndex()] = 0; - - const auto& bc = collision.template foundBC_as(); - initCCDB(bc); - registry.fill(HIST("hCollisionCounter"), 1); - - updateCCDB(bc); // delay update until is needed - - const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); - // LOGF(info, "n v0 = %d", v0s_per_coll.size()); - for (const auto& v0 : v0s_per_coll) { - // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); - fillV0Table(v0, false); - } // end of v0 loop - } // end of collision loop - - stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF - stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF - - // find minimal pca - for (const auto& [key, value] : pca_map) { - auto v0Id = std::get<0>(key); - auto collisionId = std::get<1>(key); - auto posId = std::get<2>(key); - auto eleId = std::get<3>(key); - float v0pca = value; - float cospa = cospa_map[key]; - bool is_closest_v0 = true; - bool is_most_aligned_v0 = true; - - for (const auto& [key_tmp, value_tmp] : pca_map) { - auto v0Id_tmp = std::get<0>(key_tmp); - auto collisionId_tmp = std::get<1>(key_tmp); - auto posId_tmp = std::get<2>(key_tmp); - auto eleId_tmp = std::get<3>(key_tmp); - float v0pca_tmp = value_tmp; - float cospa_tmp = cospa_map[key_tmp]; - - if (v0Id == v0Id_tmp) { // skip exactly the same v0 - continue; - } - - if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { // same ele and pos, but attached to different collision - // LOGF(info, "!reject! | collision id = %d | posid1 = %d , eleid1 = %d , posid2 = %d , eleid2 = %d , cospa1 = %f , cospa2 = %f", collisionId, posId, eleId, posId_tmp, eleId_tmp, cospa, cospa_tmp); - is_most_aligned_v0 = false; - break; - } - - if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { - // LOGF(info, "!reject! | collision id = %d | posid1 = %d , eleid1 = %d , posid2 = %d , eleid2 = %d , pca1 = %f , pca2 = %f", collisionId, posId, eleId, posId_tmp, eleId_tmp, v0pca, v0pca_tmp); - is_closest_v0 = false; - break; - } - } // end of pca_map tmp loop - - bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); - if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { - // auto v0 = v0s.rawIteratorAt(v0Id); - // auto collision = collisions.rawIteratorAt(collisionId); - // auto pos = tracks.rawIteratorAt(posId); - // auto ele = tracks.rawIteratorAt(eleId); - // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); - - // fillV0Table(v0, true); - stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); - stored_fullv0Ids.emplace_back(std::make_tuple(v0Id, collisionId, posId, eleId)); - nv0_map[collisionId]++; - } - } // end of pca_map loop - // LOGF(info, "pca_map.size() = %d", pca_map.size()); - - for (const auto& fullv0Id : stored_fullv0Ids) { - auto v0Id = std::get<0>(fullv0Id); - // auto collisionId = std::get<1>(fullv0Id); - // auto posId = std::get<2>(fullv0Id); - // auto eleId = std::get<3>(fullv0Id); - // LOGF(info, "!accept! | collision id = %d | v0id = %d , posid = %d , eleid = %d", collisionId, v0Id, posId, eleId); - - auto v0 = v0s.rawIteratorAt(v0Id); - if constexpr (enableFilter) { - auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. - if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { - continue; - } - // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); - } - - fillV0Table(v0, true); - } // end of fullv0Id loop - - for (const auto& collision : collisions) { - if constexpr (isMC) { - if (!collision.has_mcCollision()) { - continue; - } - } - // events_ngpcm(nv0_map[collision.globalIndex()]); - } // end of collision loop - - pca_map.clear(); - cospa_map.clear(); - nv0_map.clear(); - stored_v0Ids.clear(); - stored_v0Ids.shrink_to_fit(); - stored_fullv0Ids.clear(); - stored_fullv0Ids.shrink_to_fit(); - } // end of build - - //! type of V0. 0: built solely for cascades (does not pass standard V0 cuts), 1: standard 2, 3: photon-like with TPC-only use. Regular analysis should always use type 1 or 3. - Filter v0Filter = o2::aod::v0::v0Type > (uint8_t)0; - using filteredV0s = soa::Filtered; - - void processRec(MyCollisions const& collisions, filteredV0s const& v0s, MyTracksIU const& tracks, aod::BCsWithTimestamps const& bcs) - { - build(collisions, v0s, tracks, bcs); - } - PROCESS_SWITCH(PhotonConversionBuilder, processRec, "process reconstructed info for data", true); - - void processRec_SWT(MyCollisionsWithSWT const& collisions, filteredV0s const& v0s, MyTracksIU const& tracks, aod::BCsWithTimestamps const& bcs) - { - build(collisions, v0s, tracks, bcs); - } - PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT, "process reconstructed info for data", false); - - void processMC(MyCollisionsMC const& collisions, filteredV0s const& v0s, MyTracksIUMC const& tracks, aod::BCsWithTimestamps const& bcs) - { - build(collisions, v0s, tracks, bcs); - } - PROCESS_SWITCH(PhotonConversionBuilder, processMC, "process reconstructed info for MC", false); - - void processRec_OnlyIfDielectron(soa::Join const& collisions, filteredV0s const& v0s, MyTracksIU const& tracks, aod::BCsWithTimestamps const& bcs) - { - build(collisions, v0s, tracks, bcs); - } - PROCESS_SWITCH(PhotonConversionBuilder, processRec_OnlyIfDielectron, "process reconstructed info for data", false); - - void processRec_SWT_OnlyIfDielectron(soa::Join const& collisions, filteredV0s const& v0s, MyTracksIU const& tracks, aod::BCsWithTimestamps const& bcs) - { - build(collisions, v0s, tracks, bcs); - } - PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT_OnlyIfDielectron, "process reconstructed info for data", false); -}; +#include +#include -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"photon-conversion-builder"})}; + return o2::framework::WorkflowSpec{ + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h new file mode 100644 index 00000000000..c74bc6fcbc4 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h @@ -0,0 +1,2533 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file photonconversionbuilder.ch +/// \brief this task produces photon data table with KFParticle. +/// \author Daiki Sekihata , Tokyo +#ifndef PWGEM_PHOTONMESON_TABLEPRODUCER_PHOTONCONVERSIONBUILDER_H_ +#define PWGEM_PHOTONMESON_TABLEPRODUCER_PHOTONCONVERSIONBUILDER_H_ + +#ifndef HomogeneousField +#define HomogeneousField // o2-linter: disable=name/macro (name coming from KFParticle, not us, needed for KFParticle::SetField) +#endif + +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/Core/EmMlResponsePCM.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCandidate.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/ConversionPointProfile.h" +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" +#include "PWGEM/PhotonMeson/Utils/TrackSelection.h" +#include "PWGEM/PhotonMeson/Utils/V0DedupUtilities.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TPCVDriftManager.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Tools/KFparticle/KFUtilities.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(google-global-names-in-headers) +using o2::framework::AxisSpec; +using o2::framework::Configurable; +using o2::framework::ConfigurableGroup; +using o2::framework::HistType; +using o2::framework::kTH1D; +using o2::framework::kTH1F; +using o2::framework::kTH2F; +using o2::framework::kTH3F; +using o2::framework::kTHnSparseF; +using o2::framework::Preslice; +using o2::framework::Produces; +using o2::pwgem::photonmeson::cpRadiusProfile; +using o2::pwgem::photonmeson::cpScanLeg; +using o2::pwgem::photonmeson::FinderCollision; +using o2::pwgem::photonmeson::FinderCuts; +using o2::pwgem::photonmeson::FinderLeg; +using o2::pwgem::photonmeson::FinderStep; +using o2::pwgem::photonmeson::LegScan; +using o2::pwgem::photonmeson::qInv; +using o2::pwgem::photonmeson::RadiusProfile; +using o2::pwgem::photonmeson::solveConflictGroup; +// NOLINTEND(google-global-names-in-headers) + +using MyCollisions = o2::soa::Join; +// using MyCollisionsWithSWT = soa::Join; +using MyCollisionsMC = o2::soa::Join; +using MyBCs = o2::soa::Join; + +using MyTracksIU = o2::soa::Join; +using MyTracksIUMC = o2::soa::Join; + +enum MatCorrTypeEM { + None = 0, + TGeo = 1, + LUT = 2 +}; + +enum TrackPropMode { + kProper = 0, + kFast = 1, + kBoth = 2 +}; + +enum V0DeduplicationMode { + Pairwise = 0, // old algorithm, V0 with best PCA + cosPA at the same time in a group wins + GreedyMatching = 1, // new algorithm, best score wins (score combines PCA and cosPA -- see detailed implementation in PCMUtilities.cxx) + GroupMatching = 2, // new algorithm, best combination of leg-disjoint V0s wins, not the single best V0 + KeepAll = 3 // no deduplication, keep all V0s +}; + +// Struct needed for deduplication mode 1. Used to keep track of v0 properties and a score based on pca and cosPA +struct V0CandidateHelper { + int v0ID = -1; + int colID = -1; + int posID = -1; + int eleID = -1; + float cosPA = -1.f; + float pca = -1.f; + float score = -1.f; + float mee = 0.f; // e+e- mass at the secondary vertex (GeV/c^2) + float pxAtPV = 0.f; + float pyAtPV = 0.f; + float pzAtPV = 0.f; + float svX = 0.f; + float svY = 0.f; + float svZ = 0.f; + float purity = -999.f; + float cosPAxy = -2.f; // transverse pointing angle + float cosPA3D = -2.f; // 3D pointing angle + + V0CandidateHelper() = default; + + V0CandidateHelper(int v0, int col, int pos, int ele, float cpa, float p, float s, float m = 0.f) + : v0ID(v0), colID(col), posID(pos), eleID(ele), cosPA(cpa), pca(p), score(s), mee(m) + { + } + + bool operator==(const V0CandidateHelper& other) const { return score == other.score; } + bool operator<(const V0CandidateHelper& other) const { return score < other.score; } + bool operator>(const V0CandidateHelper& other) const { return score > other.score; } +}; + +// (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) +struct CandKey { + int64_t v0Id = -1; // SVertexer V0 index, -1 for finder and resolver photons + int64_t collisionId = -1; + int64_t posId = -1; + int64_t eleId = -1; + auto operator<=>(const CandKey&) const = default; +}; + +// result of a probe fit (FitOptions::probe): leg pair passed the cuts, and its dedup score +struct PairFit { + bool ok = false; + float score = 999.f; +}; + +// options for fitting a leg pair that was not built by the S-Vertexer (finder, ambiguity resolver) +struct FitOptions { + bool nonSVertexer = false; // finder PCA cut for mixed legs, side-based TPC drift correction + bool relaxAP = false; // no Armenteros-Podolanski cut + bool purityGate = false; // finder: require purity score > finderMinScore + PairFit* probe = nullptr; // only return the dedup score, do not add a candidate +}; + +// one tested pair of stored photons in the ambiguity resolver (also read by the MC diagnostics) +struct SwapRecord { + CandKey keyI{}, keyJ{}; + float q = -1.f; + bool complete = false; + bool swapped = false; +}; + +struct DedupDiag { + std::map> blockersByKey; // rejected candidate -> candidates that took one of its legs + std::vector snapshot; // all candidates, before deduplication + std::vector stored; // surviving candidates + std::map> lossMargin; + std::vector swaps; +}; + +template +struct PhotonConversionBuilder { + + // using namespace o2; + // using namespace o2::soa; + // using namespace o2::framework; + // using namespace o2::framework::expressions; + // using namespace o2::constants::physics; + // using namespace o2::pwgem::photonmeson; + + Produces v0photonskf; + Produces v0legs; + Produces v0legsXYZ; + Produces v0legsDeDxMC; + Produces v0photonsphivpsi; + + // Produces v0photonskfcov; + // Produces events_ngpcm; + + // CCDB options + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + + // Operation and minimisation criteria + Configurable d_bz_input{"d_bz", -999, "bz field, -999 is automatic"}; + Configurable useMatCorrType{"useMatCorrType", 0, "0: none, 1: TGeo, 2: LUT"}; + Configurable modeTrackPropagation{"modeTrackPropagation", 0, "0: use real track propagation, including material, 1: use fast approximation using only geometry, 2: Use real track propagation and make comparison to fast propagation (only for debugging and testing)"}; + Configurable propV0LegsRadius{"propV0LegsRadius", 60.f, "Radius to which the V0 legs are propagated to calculate psipair and phiV"}; + o2::framework::Configurable useSideBasedCorrection{"useSideBasedCorrection", false, "when moving TPC-only tracks, use TPC side flags (with legacy fallback) instead of tgl sign"}; + + // deduplication settings + struct : ConfigurableGroup { + std::string prefix = "dedup_group"; + Configurable deduplicationMode{"deduplicationMode", 0, "0: Pairwise deduplication, 1: Based on Greedy matching (best score wins), 2: Based on Group matching (crossed pairs are both kept), 3: Keep all V0s, our default in the config is mode 0, however if a wrong configuration is used, the default will be mode 1 (Greedy matching) to avoid crashes"}; + Configurable deduplicationScoreWeight{"deduplicationScoreWeight", 0.5f, "0.:only pca goes into the score, 1: only cosPA goes itno score, any number in between is a mix of pca and cosPA"}; + Configurable cfgDedupTruthMaps{"cfgDedupTruthMaps", true, "fill the fine (dEta, dPhi, q) truth maps in MC"}; + Configurable dedupMaxGroupSize{"dedupMaxGroupSize", 12, "group matching (mode 2): conflict groups up to this size are solved exactly, larger ones greedily (capped at 16)"}; + Configurable groupMatchingScoreThreshold{"groupMatchingScoreThreshold", -1.f, "dedup mode 2, choice of the best leg-disjoint subset of a conflict group. <= 0 (master): most photons first, then lowest summed score. > 0: largest sum of max(threshold - score, 1e-3) over the photons, so a photon with a score near or above the threshold adds almost nothing and two poor candidates cannot outweigh one good one (e.g. 0.4)"}; + Configurable gapWeight{"gapWeight", 0.f, "dedup score: weight of the helix-circle gap of the legs (0 = off)"}; + Configurable gapScale{"gapScale", 1.f, "dedup score: scale of the circle gap (cm)"}; + Configurable useCosPA3D{"useCosPA3D", false, "use the 3D cosPA instead of the xy cosPA in V0PhotonCandidate"}; + Configurable resolveSplitLegs{"resolveSplitLegs", false, "after the dedup, remove photons that share a split track with a better photon - avoids duplicates for photon candidates"}; + Configurable splitLegMaxDTgl{"splitLegMaxDTgl", 0.03f, "split legs: max |tgl1 - tgl2|"}; + Configurable splitLegMaxChi2{"splitLegMaxChi2", 15.f, "split legs: max chi2 of two track pieces"}; + Configurable cfgExtendedDiag{"cfgExtendedDiag", false, "MC: truth of the stored photons, pairs and swaps"}; + } dedup; + + // recovery finder for not thinned data (step 2) and ambiguity resolver settings (step 5) + struct : ConfigurableGroup { + std::string prefix = "recovery_group"; + Configurable runFinder{"runFinder", false, "photon finder for free legs without ITS, candidates enter the dedup"}; + Configurable finderMinScore{"finderMinScore", 0.f, "finder: min. purity score (log-likelihood ratio true / fake)"}; + Configurable finderMaxDeltaTgl{"finderMaxDeltaTgl", 0.15f, "finder: max |tgl+ - tgl-|"}; + Configurable finderMaxCircleGap{"finderMaxCircleGap", 5.f, "finder: max |circle gap| (cm)"}; + Configurable finderMinCosXY{"finderMinCosXY", 0.9f, "finder: min. xy pointing at the touching point"}; + Configurable finderMaxDeltaZ{"finderMaxDeltaZ", 3.f, "finder: max |z+ - z-| at the touching point (cm)"}; + Configurable finderMaxDz{"finderMaxDz", 4.f, "finder: max |z of the photon line at the beam axis - z_PV| (cm)"}; + Configurable finderRMargin{"finderRMargin", 5.f, "finder: margin below the min. conversion radius (cm)"}; + Configurable finderTimeMargin{"finderTimeMargin", 100.f, "finder: margin of the leg time windows (ns)"}; + Configurable finderTimeMarginFwd{"finderTimeMarginFwd", 3000.f, "finder: extra late margin for TPC-only legs (ns)"}; + Configurable finderMaxPCAMixed{"finderMaxPCAMixed", -1.f, "finder: max PCA for an ITS and a non-ITS leg (cm), < 0 builder cuts"}; + Configurable finderFreeCollinearLegs{"finderFreeCollinearLegs", 0.f, "finder: two candidates of one collision with q_inv below this (GeV/c) count as collinear; their legs stay available to the finder (leg pairs already built are not refitted). Recovers true photons next to a close partner photon. 0 = off"}; + Configurable runSwapResolver{"runSwapResolver", false, "ambiguity resolver: for close stored photon pairs, fit the crossed leg pairing (no AP cut) and keep the pairing with the lower dedup score"}; + Configurable swapMaxQ{"swapMaxQ", 0.1f, "swap resolver: max q_inv of the two photons (GeV/c)"}; + Configurable swapMaxConvDist{"swapMaxConvDist", 20.f, "swap resolver: max distance of the conversion points (cm)"}; + } recovery; + + // event-level skim for derived data + struct : ConfigurableGroup { + std::string prefix = "skim_group"; + Configurable minNv0PerCollision{"minNv0PerCollision", -1, "store V0 photons only for collisions with at least this many surviving candidates (event-level skim for derived data). negative = store all"}; + Configurable minMinvPairPerCollision{"minMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv (minv) above this (GeV/c^2). negative = disabled"}; + Configurable maxMinvPairPerCollision{"maxMinvPairPerCollision", -1.f, "store a collision's photons only if at least one photon pair has qinv (minv) below this (GeV/c^2). negative = disabled"}; + } skim; + + // single track cuts + struct : ConfigurableGroup { + std::string prefix = "trackcut_group"; + Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; + Configurable mincrossedrows{"mincrossedrows", 40, "min crossed rows"}; + Configurable moveTPCTracks{"moveTPCTracks", true, "Move TPC-only tracks under the collision assumption"}; + Configurable disableITSonlyTracks{"disableITSonlyTracks", false, "disable ITSonly tracks in V0 legs"}; + Configurable disableTPConlyTracks{"disableTPConlyTracks", false, "disable TPConly tracks in V0 legs"}; + Configurable requireITShit{"requireITShit", false, "require ITS hit to V0 legs"}; + Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max chi2/NclsTPC"}; // default 4.0 + 1.0 + Configurable maxchi2its{"maxchi2its", 6.0, "max chi2/NclsITS"}; // default 5.0 + 1.0 + Configurable maxpt_itsonly{"maxpt_itsonly", 0.15, "max pT for ITSonly tracks at SV"}; + Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", 4.0, "max. TPC n sigma for electron"}; + Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -4.0, "min. TPC n sigma for electron"}; + Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + Configurable dcanegtopv{"dcanegtopv", 0.1, "DCA Neg To PV"}; + Configurable dcapostopv{"dcapostopv", 0.1, "DCA Pos To PV"}; + Configurable maxX{"maxX", 83.1, "max X for track IU"}; + Configurable min_pt_trackiu{"min_pt_trackiu", 0.05, "min pT for trackiu"}; // this comes from online processing. pT of track seed is above 50 MeV/c in B = 0.5 T, 20 MeV/c in B = 0.2 T. + } trackcuts; + + // v0 photon cuts + struct : ConfigurableGroup { + std::string prefix = "v0cut_group"; + Configurable min_v0cospa_tpconly{"min_v0cospa_tpconly", 0.99, "min V0 CosPA to V0s with TPConly tracks"}; // double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable min_v0cospa_its{"min_v0cospa_its", 0.99, "min V0 CosPA to V0s with ITs hits"}; // double -> N.B. dcos(x)/dx = 0 at x=0) + Configurable max_dcav0dau_tpconly{"max_dcav0dau_tpconly", 3.0, "max distance btween 2 legs to V0s with TPConly tracks"}; + Configurable max_dcav0dau_its{"max_dcav0dau_its", 0.5, "max distance btween 2 legs to V0s with ITS hits"}; + Configurable max_dcav0dau_itsibss{"max_dcav0dau_itsibss", 1.0, "max distance btween 2 legs to V0s with ITS hits on ITSib SS"}; + Configurable max_dcav0dau_tpc_inner_fc{"max_dcav0dau_tpc_inner_fc", 1.5, "max distance btween 2 legs to V0s with ITS hits on TPC inner FC"}; + Configurable min_v0radius{"min_v0radius", 1.0, "min v0 radius"}; + Configurable max_v0radius{"max_v0radius", 90.0, "max v0 radius"}; + Configurable conversionPointMethod{"conversionPointMethod", 0, "0: helix touching point of the legs (Run 2), 1: radius-profiled pair fit through the material, falls back to 0 without a valid minimum"}; + Configurable margin_r_its{"margin_r_its", 3.0, "margin for r cut in cm"}; + Configurable margin_r_tpc{"margin_r_tpc", 7.0, "margin for r cut in cm"}; + Configurable margin_r_itstpc_tpc{"margin_r_itstpc_tpc", 7.0, "margin for r cut in cm"}; + Configurable margin_z{"margin_z", 7.0, "margin for z cut in cm"}; + Configurable max_alpha_ap{"max_alpha_ap", 0.95, "max alpha for AP cut"}; + Configurable max_qt_ap{"max_qt_ap", 0.01, "max qT for AP cut"}; + Configurable min_pt_v0{"min_pt_v0", 0.1, "min pT for v0 photons at PV"}; + Configurable max_pt_v0_itsonly{"max_pt_v0_itsonly", 0.3, "max pT for v0 photons wth 2 ITSonly tracks at PV"}; + Configurable max_eta_v0{"max_eta_v0", 0.9, "max eta for v0 photons at PV"}; + Configurable kfMassConstrain{"kfMassConstrain", -1.f, "mass constrain for the KFParticle mother particle"}; + Configurable max_r_req_its{"max_r_req_its", 16.0, "max Rxy for V0 with ITS hits"}; + Configurable min_r_tpconly{"min_r_tpconly", 32.0, "min Rxy for V0 with TPConly tracks"}; + Configurable max_r_itsmft_ss{"max_r_itsmft_ss", 66.0, "max Rxy for ITS/MFT SS"}; + Configurable max_dcatopv_xy_v0{"max_dcatopv_xy_v0", +1e+10, "max. DCAxy to PV for V0"}; + Configurable max_dcatopv_z_v0{"max_dcatopv_z_v0", +1e+10, "max. DCAz to PV for V0"}; + Configurable reject_v0_on_itsib{"reject_v0_on_itsib", true, "flag to reject v0s on ITSib"}; + } v0cuts; + + // PCM ML inference + struct : ConfigurableGroup { + std::string prefix = "mlcut_group"; + Configurable applyPCMMl{"applyPCMMl", false, "Flag to apply ML selections"}; + Configurable use2DBinning{"use2DBinning", false, "Flag to enable/disable 2D binning for ML application"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable nClassesPCMMl{"nClassesPCMMl", static_cast(o2::analysis::em_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable centTypePCMMl{"centTypePCMMl", 2, "Centrality type for 2D ML application: FT0M:0, FT0A:1, FT0C:2"}; + Configurable> cutDirPCMMl{"cutDirPCMMl", std::vector{o2::analysis::em_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"path_ccdb/BDT_PCM/"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_PCM.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable> labelsBinsPCMMl{"labelsBinsPCMMl", std::vector{"bin 0", "bin 1"}, "Labels for bins"}; + Configurable> labelsCutScoresPCMMl{"labelsCutScoresPCMMl", std::vector{o2::analysis::em_cuts_ml::labelsCutScore}, "Labels for cut scores"}; + Configurable> binsPtPCMMl{"binsPtPCMMl", std::vector{0.0, +1e+10}, "pT bin limits for ML application"}; + Configurable> binsCentPCMMl{"binsCentPCMMl", std::vector{0.0, 100.0}, "Centrality bin limits for ML application"}; + Configurable> cutsPCMMlFlat{"cutsPCMMlFlat", {0.5}, "Flattened ML cuts: [bin0_score0, bin0_score1, ..., binN_scoreM]"}; + } mlcuts; + + o2::analysis::EmMlResponsePCM emMlResponse; + std::vector outputML; + V0PhotonCandidate v0photoncandidate; + o2::ccdb::CcdbApi ccdbApi; + + int mRunNumber{}; + float d_bz{}; + float maxSnp{}; // max sine phi for propagation + float maxStep{}; // max step size (cm) for propagation + o2::framework::Service ccdb{}; + o2::base::MatLayerCylSet* lut = nullptr; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + o2::aod::common::TPCVDriftManager mVDriftMgr; + uint64_t mLastCCDBTimestamp{0}; + std::set mRelaxedKeys; + + o2::framework::HistogramRegistry registry{ + "registry", + { + {"hCollisionCounter", "hCollisionCounter", {HistType::kTH1F, {{1, 0.5f, 1.5f}}}}, + {"V0/hAP", "Armenteros Podolanski;#alpha;q_{T} (GeV/c)", {HistType::kTH2F, {{200, -1.0f, 1.0f}, {250, 0, 0.25}}}}, + {"V0/hConversionPointXY", "conversion point in XY;X (cm);Y (cm)", {HistType::kTH2F, {{400, -100.0f, 100.0f}, {400, -100.f, 100.f}}}}, + {"V0/hConversionPointRZ", "conversion point in RZ;Z (cm);R_{xy} (cm)", {HistType::kTH2F, {{200, -100.0f, 100.0f}, {200, 0.f, 100.f}}}}, + {"V0/hPt", "pT of V0 at PV;p_{T,#gamma} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, + {"V0/hEtaPhi", "#eta vs. #varphi of V0 at PV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, + {"V0/hCosPA", "cosine of pointing angle;cosine of pointing angle", {HistType::kTH1F, {{100, 0.99f, 1.f}}}}, + {"V0/hCosPA_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, + {"V0/hCosPAXY_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, + {"V0/hCosPARZ_Rxy", "cosine of pointing angle;r_{xy} (cm);cosine of pointing angle", {HistType::kTH2F, {{200, 0, 100}, {100, 0.99f, 1.f}}}}, + {"V0/hPCA", "distance between 2 legs at SV;PCA (cm)", {HistType::kTH1F, {{500, 0.0f, 5.f}}}}, + {"V0/hPCA_Rxy", "distance between 2 legs at SV;R_{xy} (cm);PCA (cm)", {HistType::kTH2F, {{200, 0, 100}, {500, 0.0f, 5.f}}}}, + {"V0/hPCA_CosPA", "distance between 2 legs at SV vs. cosPA;cosine of pointing angle;PCA (cm)", {HistType::kTH2F, {{100, 0.99, 1}, {500, 0.0f, 5.f}}}}, + {"V0/hDCAxyz", "DCA to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -5.f, +5.f}, {200, -5.f, +5.f}}}}, + {"V0/hMeeSV_Rxy", "mee at SV vs. R_{xy};R_{xy} (cm);m_{ee} at SV (GeV/c^{2})", {HistType::kTH2F, {{200, 0.0f, 100.f}, {100, 0, 0.1f}}}}, + {"V0/hRxy_minX_ITSonly_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_ITSTPC_ITSTPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_ITSTPC_ITSonly", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_ITSTPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hRxy_minX_TPC_TPC", "min trackiu X vs. R_{xy};trackiu X (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{100, 0.0f, 100.f}, {100, -50.0, 50.0f}}}}, + {"V0/hPCA_diffX", "PCA vs. trackiu X - R_{xy};distance btween 2 legs (cm);min trackiu X - R_{xy} (cm)", {HistType::kTH2F, {{500, 0.0f, 5.f}, {100, -50.0, 50.0f}}}}, + {"V0/hPhiVPsiPair", "phiV vs. psi pair;#psi_{pair} (rad.);#phi_{V} (rad.)", {HistType::kTH2F, {{500, -o2::constants::math::PI, o2::constants::math::PI}, {500, 0.0f, o2::constants::math::TwoPI}}}}, + {"V0Leg/hPt", "pT of leg at SV;p_{T,e} (GeV/c)", {HistType::kTH1F, {{1000, 0.0f, 10.0f}}}}, + {"V0Leg/hEtaPhi", "#eta vs. #varphi of leg at SV;#varphi (rad.);#eta", {HistType::kTH2F, {{72, 0.0f, o2::constants::math::TwoPI}, {200, -1, +1}}}}, + {"V0Leg/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, 0, 1}}}}, + {"V0Leg/hDCAxyz", "DCA xy vs. z to PV;DCA_{xy} (cm);DCA_{z} (cm)", {HistType::kTH2F, {{200, -50.f, 50.f}, {200, -50.f, +50.f}}}}, + {"V0Leg/hdEdx_Pin", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);TPC dE/dx", {HistType::kTH2F, {{1000, 0.f, 10.f}, {200, 0.f, 200.f}}}}, + {"V0Leg/hTPCNsigmaEl", "TPC dE/dx vs. p_{in};p_{in} (GeV/c);n #sigma_{e}^{TPC}", {HistType::kTH2F, {{1000, 0.f, 10.f}, {100, -5.f, +5.f}}}}, + {"V0Leg/hXZ", "track iu x vs. z;z (cm);x (cm)", {HistType::kTH2F, {{200, -100.f, 100.f}, {200, 0.f, 100.f}}}}, + }}; + + void init(o2::framework::InitContext&) + { + mRunNumber = 0; + d_bz = 0; + maxSnp = 0.85f; // could be changed later + maxStep = 2.00f; // could be changed later + + static constexpr std::array DedupNames = {"pairwise", "greedy matching", "group matching", "keep all"}; + if (dedup.deduplicationMode < 0 || dedup.deduplicationMode >= static_cast(DedupNames.size())) { + LOG(fatal) << "unknown deduplicationMode " << dedup.deduplicationMode.value; + } + LOGF(info, "photon-conversion-builder: deduplicationMode = %d (%s), score weight = %.2f", + dedup.deduplicationMode.value, DedupNames[dedup.deduplicationMode.value], dedup.deduplicationScoreWeight.value); + LOGF(info, "photon-conversion-builder: minNv0PerCollision = %d, minMinvPairPerCollision = %f, maxMinvPairPerCollision = %f", + skim.minNv0PerCollision.value, skim.minMinvPairPerCollision.value, skim.maxMinvPairPerCollision.value); + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + switch (static_cast(useMatCorrType.value)) { + case MatCorrTypeEM::TGeo: + LOGF(info, "TGeo correction requested, loading geometry"); + if (!o2::base::GeometryManager::isGeometryLoaded()) { + ccdb->get(geoPath); + } + matCorr = o2::base::Propagator::MatCorrType::USEMatCorrTGeo; + break; + case MatCorrTypeEM::LUT: + LOGF(info, "LUT correction requested, loading LUT"); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); + matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + break; + default: + LOGF(info, "no correction requested, loading LUT by default!"); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); + matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + break; + } + + if (mlcuts.applyPCMMl) { + if (mlcuts.use2DBinning) { + int binsNPt = static_cast(mlcuts.binsPtPCMMl->size()) - 1; + int binsNCent = static_cast(mlcuts.binsCentPCMMl->size()) - 1; + int binsN = binsNPt * binsNCent; + if (binsN * static_cast(mlcuts.cutDirPCMMl->size()) != static_cast(mlcuts.cutsPCMMlFlat->size())) { + LOG(fatal) << "Mismatch in number of bins and cuts provided for 2D ML application: binsN * cutDirPCMMl: " << binsN * static_cast(mlcuts.cutDirPCMMl->size()) << " bins vs. cutsPCMMlFlat: " << mlcuts.cutsPCMMlFlat->size() << " cuts"; + } + if (binsN != static_cast(mlcuts.onnxFileNames->size())) { + LOG(fatal) << "Mismatch in number of bins and ONNX files provided for 2D ML application: binsN " << binsN << " bins vs. onnxFileNames: " << mlcuts.onnxFileNames->size() << " ONNX files"; + } + if (binsN != static_cast(mlcuts.labelsBinsPCMMl->size())) { + LOG(fatal) << "Mismatch in number of bins and labels provided for 2D ML application: binsN:" << binsN << " bins vs. labelsBinsPCMMl: " << mlcuts.labelsBinsPCMMl->size() << " labels"; + } + if (static_cast(mlcuts.cutDirPCMMl->size()) != mlcuts.nClassesPCMMl) { + LOG(fatal) << "Mismatch in number of classes and cut directions provided for 2D ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. cutDirPCMMl: " << mlcuts.cutDirPCMMl->size() << " cut directions"; + } + if (static_cast(mlcuts.labelsCutScoresPCMMl->size()) != mlcuts.nClassesPCMMl) { + LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for 2D ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << mlcuts.labelsCutScoresPCMMl->size() << " labels"; + } + o2::framework::LabeledArray cutsPCMMl(mlcuts.cutsPCMMlFlat->data(), binsN, mlcuts.nClassesPCMMl, mlcuts.labelsBinsPCMMl, mlcuts.labelsCutScoresPCMMl); + emMlResponse.configure2D(mlcuts.binsPtPCMMl, mlcuts.binsCentPCMMl, cutsPCMMl, mlcuts.cutDirPCMMl, mlcuts.nClassesPCMMl); + } else { + int binsNPt = static_cast(mlcuts.binsPtPCMMl->size()) - 1; + if (binsNPt * static_cast(mlcuts.cutDirPCMMl->size()) != static_cast(mlcuts.cutsPCMMlFlat->size())) { + LOG(fatal) << "Mismatch in number of pT bins and cuts provided for ML application: binsNPt * cutDirPCMMl:" << binsNPt * mlcuts.cutDirPCMMl->size() << " bins vs. cutsPCMMlFlat: " << mlcuts.cutsPCMMlFlat->size() << " cuts"; + } + if (binsNPt != static_cast(mlcuts.onnxFileNames->size())) { + LOG(fatal) << "Mismatch in number of pT bins and ONNX files provided for ML application: binsNPt " << binsNPt << " bins vs. onnxFileNames: " << mlcuts.onnxFileNames->size() << " ONNX files"; + } + if (binsNPt != static_cast(mlcuts.labelsBinsPCMMl->size())) { + LOG(fatal) << "Mismatch in number of pT bins and labels provided for ML application: binsNPt:" << binsNPt << " bins vs. labelsBinsPCMMl: " << mlcuts.labelsBinsPCMMl->size() << " labels"; + } + if (mlcuts.nClassesPCMMl != static_cast(mlcuts.cutDirPCMMl->size())) { + LOG(fatal) << "Mismatch in number of classes and cut directions provided for ML application: nClassesPCMMl: " << mlcuts.nClassesPCMMl << " classes vs. cutDirPCMMl: " << mlcuts.cutDirPCMMl->size() << " cut directions"; + } + if (static_cast(mlcuts.labelsCutScoresPCMMl->size()) != mlcuts.nClassesPCMMl) { + LOG(fatal) << "Mismatch in number of labels for cut scores and number of classes provided for ML application: nClassesPCMMl:" << mlcuts.nClassesPCMMl << " classes vs. labelsCutScoresPCMMl: " << mlcuts.labelsCutScoresPCMMl->size() << " labels"; + } + o2::framework::LabeledArray cutsPCMMl(mlcuts.cutsPCMMlFlat->data(), binsNPt, mlcuts.nClassesPCMMl, mlcuts.labelsBinsPCMMl, mlcuts.labelsCutScoresPCMMl); + emMlResponse.configure(mlcuts.binsPtPCMMl, cutsPCMMl, mlcuts.cutDirPCMMl, mlcuts.nClassesPCMMl); + } + if (mlcuts.loadModelsFromCCDB) { + ccdbApi.init(ccdburl); + emMlResponse.setModelPathsCCDB(mlcuts.onnxFileNames, ccdbApi, mlcuts.modelPathsCCDB, mlcuts.timestampCCDB); + } else { + emMlResponse.setModelPathsLocal(mlcuts.onnxFileNames); + } + emMlResponse.cacheInputFeaturesIndices(mlcuts.namesInputFeatures); + emMlResponse.init(); + if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { + registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSignalScoreBeforeCutVsPt", "BDT signal score before cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSignalScoreAfterCutVsPt", "BDT signal score after cut vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { + registry.add("V0/hBDTBackgroundScoreBeforeCutVsPt", "BDT background score before cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTBackgroundScoreAfterCutVsPt", "BDT background score after cut vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt", "BDT primary photon score before cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTPrimaryPhotonScoreAfterCutVsPt", "BDT primary photon score after cut vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt", "BDT secondary photon score before cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTSecondaryPhotonScoreAfterCutVsPt", "BDT secondary photon score after cut vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + } else { + registry.add("V0/hBDTScoreBeforeCutVsPt", "BDT score before cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + registry.add("V0/hBDTScoreAfterCutVsPt", "BDT score after cut vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); + } + } + + // Compare proper propagation and fast geometrical propagation + if (modeTrackPropagation == TrackPropMode::kBoth) { + registry.add("V0Leg/hDCAxyPropagationCompare", "Comparison of DCA_{xy} propagation;DCA_{xy} (cm) proper propagation; DCA_{xy} (cm) geom. propagation", {HistType::kTH2F, {{200, -10., 10.}, {200, -10., 10.}}}); + registry.add("V0Leg/hDCAzPropagationCompare", "Comparison of DCA_{z} propagation;DCA_{z} (cm) proper propagation; DCA_{z} (cm) geom. propagation", {HistType::kTH2F, {{200, -10., 10.}, {200, -10., 10.}}}); + registry.add("V0/hPhivPropagationCompare", "Comparison of #phi_{v};#phi_{v} proper propagation; #phi_{v} geom. propagation", {HistType::kTH2F, {{100, 0., 1.6}, {100, 0., 1.6}}}); + registry.add("V0/hPsiPairPropagationCompare", "Comparison of #Psi_{pair};#Psi_{pair} proper propagation; #Psi_{pair} geom. propagation", {HistType::kTH2F, {{100, 0., 1.6}, {100, 0., 1.6}}}); + } + + // Make sure the dedup.deduplicationScoreWeight is between 0 and 1 + if (dedup.deduplicationScoreWeight > 1.f) { // o2-linter: disable=magic-number (score has to be below unity) + LOG(warning) << "deduplicationScoreWeight is larger than unity which is not allowed"; + } + if (dedup.deduplicationScoreWeight < 0.f) { // o2-linter: disable=magic-number (score has to be above zero) + LOG(warning) << "deduplicationScoreWeight is smaller than zero which is not allowed"; + } + + if (v0cuts.conversionPointMethod == 1) { + registry.add("V0/hConvPointProfile", "conversionPointMethod 1;0 fallback to the touching point, 1 profile minimum used;candidates", kTH1F, {{2, -0.5f, 1.5f}}); + registry.add("V0/hConvPointProfileShift", "conversionPointMethod 1;R_{profile} - R_{touching point} (cm);candidates", kTH1F, {{200, -10.f, 10.f}}); + } + if (recovery.runSwapResolver && dedup.deduplicationMode == V0DeduplicationMode::KeepAll) { + LOG(fatal) << "runSwapResolver needs deduplicationMode 0, 1 or 2"; + } + if (dedup.resolveSplitLegs) { + registry.add("Dedup/hSplitLegRemoved", "split legs: photons removed per DF;photons;DFs", kTH1F, {{100, -0.5f, 99.5f}}); + } + if (recovery.runFinder) { + registry.add("Finder/hSteps", "finder;step passed;pairs", kTH1D, {{FinderStep::kNFindStep, -0.5f, static_cast(FinderStep::kNFindStep) - 0.5f}}); + registry.add("Finder/hScore", "finder;purity score;pairs", kTH1F, {{100, -20.f, 10.f}}); + registry.add("Finder/hNAdded", "finder;photons added per DF;DFs", kTH1F, {{200, 0.f, 2000.f}}); + } + if (recovery.runSwapResolver) { + registry.add("Swap/hTested", "swap resolver;0 tested, 1 swapped;pairs", kTH1D, {{2, -0.5f, 1.5f}}); + registry.add("Swap/hQ", "swap resolver;q_{inv} (GeV/c);0 tested, 1 swapped", kTH2F, {{100, 0.f, 0.1f}, {2, -0.5f, 1.5f}}); + } + + if (doprocessMC) { + const AxisSpec axQ{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; + const AxisSpec axDEta{80, -1.6f, 1.6f, "#Delta#eta_{#gamma#gamma}^{true}"}; + const AxisSpec axClass{3, -0.5f, 2.5f, "0 = true, 1 = cross-leg fake, 2 = other fake"}; + // lostByPartnerFake is a subset of lostByCrossFake, and a pair can appear in several of them. + for (const auto& nm : {"before", "bothSurvive", "bothSurviveSameColl", "surviveAsCrossedPairing", "halfCrossedStored", "oneLost", "bothLost", + "lostByCrossFake", "lostByPartnerFake", "lostByOtherFake", "lostByTrue"}) { + const std::string dir = std::string("MCDedup/Pairs/") + nm + "/"; + registry.add((dir + "hQ").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;q_{inv}^{true} (GeV/c);pairs", kTH1F, {axQ}, true); + registry.add((dir + "hDEta").c_str(), "truth photon pairs with >=1 true V0 candidate before deduplication;#Delta#eta^{true};pairs", kTH1F, {axDEta}, true); + } + registry.add("MCDedup/Candidates/hClassStage", "candidate composition;class;0 = before, 1 = stored", kTH2F, {axClass, {2, -0.5f, 1.5f}}, true); + + const AxisSpec axBlocker{4, -0.5f, 3.5f, "0 = true, 1 = cross-leg fake, 2 = other fake, 3 = no blocker (cut, not dedup)"}; + + registry.add("MCDedup/Photons/hFate", "fate of the true photons;0 = alive, 1 = lost (blocked), 2 = lost (no blocker);photons", kTH1F, {{3, -0.5f, 2.5f}}, true); + registry.add("MCDedup/Photons/hBlockerClass", "blockers of a killed true photon (ENTRIES = blockers, not photons);class of the blocker;blocker entries", kTH1F, {axBlocker}, true); + registry.add("MCDedup/Photons/hKillMargin", "mode 0/1 only;S_{victim} - S_{winner};kills", kTH1F, {{200, 0.f, 1.f}}, true); + registry.add("MCDedup/Photons/hVictimVsWinnerPCA", "PCA of the two;PCA_{victim} (cm);PCA_{winner} (cm)", kTH2F, {{60, 0.f, 3.f}, {60, 0.f, 3.f}}, true); + // Reason for photon building failure + // x = 0 -> an equally large solution containing it existed + // x > 0 -> keeping it would have cost x candidates + registry.add("MCDedup/Photons/hLostMargin", "why the true photon lost (mode 2);cardinality deficit;#Sigma S(best set with it) - #Sigma S(winner)", kTH2F, {{5, -0.5f, 4.5f}, {100, 0.f, 2.f}}, true); + if (dedup.cfgDedupTruthMaps) { + const AxisSpec axDEtaFine{640, -1.6f, 1.6f, "#Delta#eta^{true}"}; + const AxisSpec axDPhi{144, -o2::constants::math::PI, o2::constants::math::PI, "#Delta#varphi^{true} (rad)"}; + for (const auto& nm : {"before", "bothSurvive", "bothLost", "lostByPartnerFake"}) { + registry.add((std::string("MCDedup/Pairs/") + nm + "/hMap").c_str(), "truth photon pairs", kTHnSparseF, {axDEtaFine, axDPhi, axQ}, true); + } + } + if (dedup.cfgExtendedDiag) { + const AxisSpec axPt{50, 0.f, 5.f, "p_{T,#gamma} (GeV/c)"}; + const AxisSpec axQrec{50, 0.f, 0.1f, "q_{inv} (GeV/c)"}; + registry.add("MCDedup/Stored/hClassSource", "stored photons;class;0 SVertexer, 1 finder or swap;p_{T,#gamma} (GeV/c)", kTH3F, {axClass, {2, -0.5f, 1.5f}, axPt}); + registry.add("MCDedup/Stored/hPairClass", "stored photon pairs, same collision;0 true-true, 1 crossed doublet, 2 other;q_{inv} (GeV/c)", kTH2F, {{3, -0.5f, 2.5f}, axQrec}); + registry.add("MCDedup/Copies/hChoice", "true photons with a candidate in the dedup sample;collisions with a true candidate;0 = right coll., 1 = wrong (right available), 2 = wrong (no right copy), 3 = lost", kTH2F, {{5, 0.5f, 5.5f}, {4, -0.5f, 3.5f}}); + registry.add("MCDedup/Copies/hRule", "true photons, >= 2 copies, right copy available;0 = current dedup choice, 1 = smallest |#Deltaz| of the photon line, 2 = largest xy cos(PA) (master), 3 = largest 3D cos(PA);0 = wrong, 1 = right", kTH2F, {{4, -0.5f, 3.5f}, {2, -0.5f, 1.5f}}); + registry.add("MCDedup/Copies/hPAxy", "copies of true photons with >= 2 copies;0 = wrong coll., 1 = right coll.;transverse pointing angle (rad)", kTH2F, {{2, -0.5f, 1.5f}, {200, 0.f, 0.2f}}); + registry.add("MCDedup/Copies/hPA3D", "copies of true photons with >= 2 copies;0 = wrong coll., 1 = right coll.;3D pointing angle (rad)", kTH2F, {{2, -0.5f, 1.5f}, {200, 0.f, 0.2f}}); + registry.add("MCDedup/Swap/hCategory", "swap resolver;3 #times true stored + true crossed;0 kept, 1 swapped;q_{inv} (GeV/c)", kTH3F, {{9, -0.5f, 8.5f}, {2, -0.5f, 1.5f}, axQrec}); + } + } + + mVDriftMgr.setUseSideBasedCorrection(useSideBasedCorrection.value); + } + + void initCCDB(MyBCs::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + // In case override, don't proceed, please - no CCDB access required + if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) + d_bz = d_bz_input; + o2::parameters::GRPMagField grpmag; + if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) + } + o2::base::Propagator::initFieldFromGRP(&grpmag); + + return; + } + + auto run3grp_timestamp = bc.timestamp(); + o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); + // Fetch magnetic field from ccdb for current collision + d_bz = bc.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + + if (useMatCorrType == 2) { // o2-linter: disable=magic-number (material budget correction) + // setMatLUT only after magfield has been initalized (setMatLUT has implicit and problematic init field call if not) + o2::base::Propagator::Instance()->setMatLUT(lut); + } + /// Set magnetic field for KF vertexing + const float magneticField = o2::base::Propagator::Instance()->getNominalBz(); + KFParticle::SetField(magneticField); + + mVDriftMgr.init(&ccdb->instance()); + } + + void updateCCDB(MyBCs::iterator const& bc) + { + auto timestamp = bc.timestamp(); + + mVDriftMgr.update(timestamp); + mLastCCDBTimestamp = timestamp; + } + + std::pair> its_ib_Requirement = {0, {0, 1, 2}}; // no hit on 3 ITS ib layers. + template + bool checkV0leg(TTrack const& track) + { + using namespace o2::pwgem::photonmeson; + if constexpr (isMC) { + if (!track.has_mcParticle()) { + return false; + } + } + + if (trackcuts.disableITSonlyTracks && isITSonlyTrack(track)) { + return false; + } + + if (trackcuts.disableTPConlyTracks && isTPConlyTrack(track)) { + return false; + } + + if (trackcuts.requireITShit && !track.hasITS()) { + return false; + } + + if (track.x() > trackcuts.maxX) { + return false; + } + + if (!track.hasITS() && !track.hasTPC()) { + return false; + } + + if (track.hasITS() && !track.hasTPC() && (track.hasTRD() || track.hasTOF())) { // remove unrealistic track. this should not happen. + return false; + } + + if (track.hasTPC()) { + if (track.tpcNClsFound() < trackcuts.min_ncluster_tpc) { + return false; + } + if (track.tpcNClsCrossedRows() < trackcuts.mincrossedrows || track.tpcChi2NCl() > trackcuts.maxchi2tpc) { + return false; + } + if (track.tpcFractionSharedCls() > trackcuts.max_frac_shared_clusters_tpc) { + return false; + } + if (track.tpcNSigmaEl() < trackcuts.minTPCNsigmaEl || trackcuts.maxTPCNsigmaEl < track.tpcNSigmaEl()) { + return false; + } + } + + if (track.hasITS()) { + if (track.itsChi2NCl() > trackcuts.maxchi2its) { + return false; + } + + if (v0cuts.reject_v0_on_itsib) { + auto hits_ib = std::count_if(its_ib_Requirement.second.begin(), its_ib_Requirement.second.end(), [&](auto&& requiredLayer) { return track.itsClusterMap() & (1 << requiredLayer); }); + bool its_ob_only = hits_ib <= its_ib_Requirement.first; + if (!its_ob_only) { + return false; + } + } + } + + return true; + } + + float cospaXY_KF(const KFParticle& kfp, const KFParticle& PV) + { + float lx = kfp.GetX() - PV.GetX(); // flight length X + float ly = kfp.GetY() - PV.GetY(); // flight length Y + + float px = kfp.GetPx(); + float py = kfp.GetPy(); + float cospaXY = RecoDecay::dotProd(std::array{lx, ly}, std::array{px, py}) / (RecoDecay::sqrtSumOfSquares(lx, ly) * RecoDecay::sqrtSumOfSquares(px, py)); + if (cospaXY < -1.f) { + return -1.f; + } + if (cospaXY > 1.f) { + return 1.f; + } + return cospaXY; + } + + float cospaRZ_KF(const KFParticle& kfp, const KFParticle& PV) + { + float lx = kfp.GetX() - PV.GetX(); // flight length X + float ly = kfp.GetY() - PV.GetY(); // flight length Y + float lz = kfp.GetZ() - PV.GetZ(); // flight length Z + float lt = RecoDecay::sqrtSumOfSquares(lx, ly); // flight length R, i.e. transverse plane. + + float pt = RecoDecay::sqrtSumOfSquares(kfp.GetPx(), kfp.GetPy()); + float pz = kfp.GetPz(); + + float cospaRZ = RecoDecay::dotProd(std::array{lt, lz}, std::array{pt, pz}) / (RecoDecay::sqrtSumOfSquares(lt, lz) * RecoDecay::sqrtSumOfSquares(pt, pz)); + if (cospaRZ < -1.f) { + return -1.f; + } + if (cospaRZ > 1.f) { + return 1.f; + } + return cospaRZ; + } + + template + void fillTrackTable(TTrack const& track, TShiftedTrack const& shiftedtrack, const float dcaXY, const float dcaZ) + { + v0legs(track.collisionId(), track.globalIndex(), track.sign(), + shiftedtrack.GetPx(), shiftedtrack.GetPy(), shiftedtrack.GetPz(), dcaXY, dcaZ, + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), track.tpcSignal(), + track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.itsClusterSizes(), track.itsChi2NCl(), track.detectorMap()); + + v0legsXYZ(shiftedtrack.GetX(), shiftedtrack.GetY(), shiftedtrack.GetZ()); + + if constexpr (isMC) { + v0legsDeDxMC(track.mcTunedTPCSignal()); + } + } + + template + void fillV0Table(TV0 const& v0, const bool filltable) + { + const auto& pos = v0.template posTrack_as(); + const auto& ele = v0.template negTrack_as(); + const auto& collision = v0.template collision_as(); // collision where this v0 belongs to. + fitV0Candidate(collision, pos, ele, v0.globalIndex(), filltable); + } + + // the purity score (finder LLR tables) is only needed for the finder gate and the split-leg order + bool usePurity() const { return recovery.runFinder.value || dedup.resolveSplitLegs.value; } + + template + static float purityScore(TTrack const& pos, TTrack const& ele, const float cosPA3D, const float pca, const float mee, const float gap, const float psipair) + { + return o2::pwgem::photonmeson::finderPurityScore({cosPA3D, pca, mee, gap, std::fabs(pos.tgl() - ele.tgl()), psipair, + std::max(std::fabs(pos.tpcNSigmaEl()), std::fabs(ele.tpcNSigmaEl())), + std::min(std::fabs(pos.tpcNSigmaPi()), std::fabs(ele.tpcNSigmaPi()))}); + } + + // deduplication score, lower = better: master score (cosPA, PCA) + optional circle-gap term + float dedupScore(const float cosPA, const float pca, const float gap) + { + float score = getScoreV0(cosPA, pca, dedup.deduplicationScoreWeight); + if (dedup.gapWeight > 0.f) { + score += dedup.gapWeight * std::min(std::fabs(gap) / dedup.gapScale.value, 3.f); // o2-linter: disable=magic-number (cap) + } + return score; + } + + // fit of one leg pair with the V0 cuts; filltable = false adds a dedup candidate, + // true writes the tables; opt.probe only returns the score + template + void fitV0Candidate(TCollision const& collision, TTrack const& pos, TTrack const& ele, const int64_t v0Id, const bool filltable, FitOptions const& opt = {}) + { + using namespace o2::pwgem::photonmeson; + mVDriftMgr.setUseSideBasedCorrection(opt.nonSVertexer || useSideBasedCorrection.value); + + if (pos.sign() * ele.sign() > 0) { // reject same sign pair + return; + } + + if (pos.pt() < trackcuts.min_pt_trackiu || ele.pt() < trackcuts.min_pt_trackiu) { + return; + } + + if (pos.globalIndex() == ele.globalIndex()) { + return; + } + + if (isITSonlyTrack(pos) && !ele.hasITS()) { + return; + } + + if (isITSonlyTrack(ele) && !pos.hasITS()) { + return; + } + + if (!checkV0leg(pos) || !checkV0leg(ele)) { + return; + } + + // LOGF(info, "v0.collisionId() = %d , v0.posTrackId() = %d , v0.negTrackId() = %d", v0.collisionId(), v0.posTrackId(), v0.negTrackId()); + + // if(isTPConlyTrack(ele)){ + // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.x() = %f, ele.y() = %f, ele.z() = %f, ele.tgl() = %f, ele.alpha() = %f, ele.snp() = %f, ele.signed1Pt() = %f", ele.globalIndex(), ele.x(), ele.y(), ele.z(), ele.tgl(), ele.alpha(), ele.snp(), ele.signed1Pt()); + // // LOGF(info, "TPConly: ele.globalIndex() = %d, ele.cYY() = %f, ele.cZY() = %f, ele.cZZ() = %f, ele.cSnpY() = %f, ele.cSnpZ() = %f, ele.cSnpSnp() = %f, ele.cTglY() = %f, ele.cTglZ() = %f, ele.cTglSnp() = %f, ele.cTglTgl() = %f, ele.c1PtY() = %f, ele.c1PtZ() = %f, ele.c1PtSnp() = %f, ele.c1PtTgl() = %f, ele.c1Pt21Pt2() = %f", ele.globalIndex(), ele.cYY(), ele.cZY(), ele.cZZ(), ele.cSnpY(), ele.cSnpZ(), ele.cSnpSnp(), ele.cTglY(), ele.cTglZ(), ele.cTglSnp(), ele.cTglTgl(), ele.c1PtY(), ele.c1PtZ(), ele.c1PtSnp(), ele.c1PtTgl(), ele.c1Pt21Pt2()); + // } + + // Calculate DCA with respect to the collision associated to the v0, not individual tracks + std::array dcaInfo{}; + + auto pTrack = getTrackParCov(pos); + if (trackcuts.moveTPCTracks && isTPConlyTrack(pos) && !mVDriftMgr.moveTPCTrack(collision, pos, pTrack)) { + if (opt.probe == nullptr) { + LOGP(error, "failed correction for positive tpc track"); + } + return; + } + auto pTrackC = pTrack; + o2::math_utils::Point3D vtxPrim{ + collision.posX(), + collision.posY(), + collision.posZ()}; + if (modeTrackPropagation != TrackPropMode::kProper) { + dcaInfo = CalculateDCAFast(pTrackC, vtxPrim, d_bz); + } + pTrackC.setPID(o2::track::PID::Electron); + + std::array dcaInfoFast = dcaInfo; + if (modeTrackPropagation != TrackPropMode::kFast) { + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackC, 2.f, matCorr, &dcaInfo); + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); + registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); + } + } + auto posdcaXY = dcaInfo[0]; + auto posdcaZ = dcaInfo[1]; + + auto nTrack = getTrackParCov(ele); + if (trackcuts.moveTPCTracks && isTPConlyTrack(ele) && !mVDriftMgr.moveTPCTrack(collision, ele, nTrack)) { + if (opt.probe == nullptr) { + LOGP(error, "failed correction for negative tpc track"); + } + return; + } + auto nTrackC = nTrack; + if (modeTrackPropagation != TrackPropMode::kProper) { + dcaInfo = CalculateDCAFast(nTrackC, vtxPrim, d_bz); + } + nTrackC.setPID(o2::track::PID::Electron); + + dcaInfoFast = dcaInfo; + if (modeTrackPropagation != TrackPropMode::kFast) { + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackC, 2.f, matCorr, &dcaInfo); + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0Leg/hDCAxyPropagationCompare"), dcaInfo[0], dcaInfoFast[0]); + registry.fill(HIST("V0Leg/hDCAzPropagationCompare"), dcaInfo[1], dcaInfoFast[1]); + } + } + auto eledcaXY = dcaInfo[0]; + auto eledcaZ = dcaInfo[1]; + + if (std::fabs(posdcaXY) < trackcuts.dcapostopv || std::fabs(eledcaXY) < trackcuts.dcanegtopv) { + return; + } + + std::array xyz = {0.f, 0.f, 0.f}; + Vtx_recalculationParCov(o2::base::Propagator::Instance(), pTrack, nTrack, xyz, matCorr); + if (v0cuts.conversionPointMethod == 1) { + LegScan scanP, scanN; + cpScanLeg(pTrack, d_bz, matCorr, scanP); + cpScanLeg(nTrack, d_bz, matCorr, scanN); + RadiusProfile prof; + cpRadiusProfile(scanP, scanN, pos.hasITS() ? 0.f : 0.25f, ele.hasITS() ? 0.f : 0.25f, prof); // o2-linter: disable=magic-number (0.5 cm extra z for legs without ITS) + const bool useProfile = prof.iMin >= 0 && prof.status != 4; // o2-linter: disable=magic-number (profile class: failed) + if (useProfile) { + registry.fill(HIST("V0/hConvPointProfileShift"), prof.rMin - std::hypot(xyz[0], xyz[1])); + xyz = prof.xyz; + } + registry.fill(HIST("V0/hConvPointProfile"), useProfile ? 1.f : 0.f); + } + float rxy_tmp = RecoDecay::sqrtSumOfSquares(xyz[0], xyz[1]); + if (rxy_tmp > trackcuts.maxX + v0cuts.margin_r_tpc) { + return; + } + if (rxy_tmp < std::fabs(xyz[2]) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { + return; // RZ line cut + } + + float phiv = 999.f; + float psipair = 999.f; + float phivFast = 999.f; + float psipairFast = 999.f; + float baseR = std::hypot(xyz[0], xyz[1]); + // This method uses the track Helix instead of the full propagation. + // Hence, it is only an approximation but much faster + if (modeTrackPropagation != TrackPropMode::kProper) { + + o2::track::TrackAuxPar helixPosEle(nTrack, d_bz); + o2::track::TrackAuxPar helixPosPos(pTrack, d_bz); + + float diffX = helixPosEle.xC - helixPosPos.xC; + float diffY = helixPosEle.yC - helixPosPos.yC; + auto phiHelix = RecoDecay::constrainAngle(std::atan2(diffY, diffX) - o2::constants::math::PI / 2.); + + // Electron + float arcLenghtEle = helixPosEle.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosEle.rC) * helixPosEle.rC : o2::constants::math::PI / 2.2 * helixPosEle.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackEle = getPropMomentumFromTrackHelix(arcLenghtEle, ele, helixPosEle, d_bz / 10., phiHelix - ele.phi()); + // Positron + float arcLenghtPos = helixPosPos.rC * 0.9 > propV0LegsRadius ? std::asin(propV0LegsRadius / helixPosPos.rC) * helixPosPos.rC : o2::constants::math::PI / 2.2 * helixPosPos.rC; // This assumes that the photon momentum vector is a tangent of the circle // o2-linter: disable=magic-number (geometrical assumption for propagation) + auto propTrackPos = getPropMomentumFromTrackHelix(arcLenghtPos, pos, helixPosPos, d_bz / 10., phiHelix - pos.phi()); + + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2], pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(propTrackPos[0], propTrackPos[1], propTrackPos[2], propTrackEle[0], propTrackEle[1], propTrackEle[2]); + + // Store values for later comparison + phivFast = phiv; + psipairFast = psipair; + } + // This uses the full propagation including material effects + if (modeTrackPropagation != TrackPropMode::kFast) { + std::array offsetsR = {propV0LegsRadius, 30.f, 10.f}; + bool pPropagatedSuccess = false; + bool nPropagatedSuccess = false; + auto pTrackProp = pTrack; + auto nTrackProp = nTrack; + for (const auto& offsetR : offsetsR) { + pTrackProp = pTrack; + pTrackProp.setPID(o2::track::PID::Electron); + nTrackProp = nTrack; + nTrackProp.setPID(o2::track::PID::Electron); + + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, pTrackProp, 2.f, matCorr, &dcaInfo); + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, nTrackProp, 2.f, matCorr, &dcaInfo); + + pPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(pTrackProp, baseR + offsetR); + nPropagatedSuccess = o2::base::Propagator::Instance()->propagateToR(nTrackProp, baseR + offsetR); + + if (pPropagatedSuccess && nPropagatedSuccess) { + KFPTrack kfp_track_posProp = createKFPTrackFromTrackParCov(pTrackProp, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); + KFPTrack kfp_track_eleProp = createKFPTrackFromTrackParCov(nTrackProp, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); + phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz(), pos.sign(), ele.sign(), d_bz); + psipair = o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(kfp_track_posProp.GetPx(), kfp_track_posProp.GetPy(), kfp_track_posProp.GetPz(), kfp_track_eleProp.GetPx(), kfp_track_eleProp.GetPy(), kfp_track_eleProp.GetPz()); + break; + } + LOG(debug) << "Propagation to offset" << offsetR << " cm failed for " << (pPropagatedSuccess ? "negative" : "positive") << " track. Trying smaller offset."; + } + if (filltable && modeTrackPropagation == TrackPropMode::kBoth) { + registry.fill(HIST("V0/hPhivPropagationCompare"), phiv, phivFast); + registry.fill(HIST("V0/hPsiPairPropagationCompare"), psipair, psipairFast); + } + } + + if (phiv == 999.f || psipair == 999.f) { // o2-linter: disable=magic-number (nonsensical default value) + LOG(debug) << "Propagation failed for all radii (" << propV0LegsRadius << ", 30, 10 cm). Using default values for phiv and psipair (999.f)."; + } + + KFPTrack kfp_track_pos = createKFPTrackFromTrackParCov(pTrack, pos.sign(), pos.tpcNClsFound(), pos.tpcChi2NCl()); + KFPTrack kfp_track_ele = createKFPTrackFromTrackParCov(nTrack, ele.sign(), ele.tpcNClsFound(), ele.tpcChi2NCl()); + KFParticle kfp_pos(kfp_track_pos, kPositron); + KFParticle kfp_ele(kfp_track_ele, kElectron); + std::array GammaDaughters{&kfp_pos, &kfp_ele}; + + KFParticle gammaKF; + gammaKF.SetConstructMethod(2); + gammaKF.Construct(GammaDaughters.data(), 2); + if (v0cuts.kfMassConstrain > -0.1) { // o2-linter: disable=magic-number (nonsensical default value) + gammaKF.SetNonlinearMassConstraint(v0cuts.kfMassConstrain); + } + KFPVertex kfpVertex = createKFPVertexFromCollision(collision); + KFParticle KFPV(kfpVertex); + + // Transport the gamma to the recalculated decay vertex + KFParticle gammaKF_DecayVtx = gammaKF; // with respect to (0,0,0) + gammaKF_DecayVtx.TransportToPoint(xyz.data()); + + float cospa_kf = cpaFromKF(gammaKF_DecayVtx, KFPV); + if (!ele.hasITS() && !pos.hasITS()) { + if (cospa_kf < v0cuts.min_v0cospa_tpconly) { + return; + } + } else { + if (cospa_kf < v0cuts.min_v0cospa_its) { + return; + } + } + + float rxy = RecoDecay::sqrtSumOfSquares(gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); + if (rxy > trackcuts.maxX + v0cuts.margin_r_tpc) { + return; + } + if (rxy < std::fabs(gammaKF_DecayVtx.GetZ()) * std::tan(2 * std::atan(std::exp(-v0cuts.max_eta_v0))) - v0cuts.margin_z) { + return; // RZ line cut + } + if (rxy < v0cuts.min_v0radius || v0cuts.max_v0radius < rxy) { + return; + } + + if (!filltable && opt.probe == nullptr) { + if (isITSTPCTrack(pos) && isITSTPCTrack(ele)) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSTPC"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + } else if (isITSonlyTrack(pos) && isITSonlyTrack(ele)) { + registry.fill(HIST("V0/hRxy_minX_ITSonly_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + } else if ((isITSTPCTrack(pos) && isITSonlyTrack(ele)) || (isITSTPCTrack(ele) && isITSonlyTrack(pos))) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_ITSonly"), std::min(pTrack.getX(), nTrack.getX()), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + } else if (isITSTPCTrack(pos) && !ele.hasITS()) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(pTrack.getX(), 83.f), std::min(pTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive + } else if (isITSTPCTrack(ele) && !pos.hasITS()) { + registry.fill(HIST("V0/hRxy_minX_ITSTPC_TPC"), std::min(nTrack.getX(), 83.f), std::min(nTrack.getX(), 83.f) - rxy); // trackiu.x() - rxy should be positive + } else { + registry.fill(HIST("V0/hRxy_minX_TPC_TPC"), std::min(83.f, 83.f), std::min(83.f, 83.f) - rxy); // trackiu.x() - rxy should be positive + } + } + + if (pos.hasITS() && ele.hasITS()) { // ITSonly-ITSonly, ITSTPC-ITSTPC, ITSTPC-ITSonly + if (rxy > std::min(pTrack.getX(), nTrack.getX()) + v0cuts.margin_r_its) { + return; + } + } else if (!pos.hasITS() && ele.hasITS()) { // ITSTPC-TPC + if (rxy > std::min(83.f, nTrack.getX()) + v0cuts.margin_r_itstpc_tpc) { + return; + } + } else if (pos.hasITS() && !ele.hasITS()) { // ITSTPC-TPC + if (rxy > std::min(pTrack.getX(), 83.f) + v0cuts.margin_r_itstpc_tpc) { + return; + } + } else if (!pos.hasITS() && !ele.hasITS()) { // TPC-TPC + if (rxy > std::min(83.f, 83.f) + v0cuts.margin_r_tpc) { + return; + } + } + + if ((!pos.hasITS() || !ele.hasITS()) && rxy < v0cuts.max_r_req_its) { // conversion points smaller than v0cuts.max_r_req_its have to be detected with ITS hits. + return; + } + + if ((!pos.hasITS() && !ele.hasITS()) && rxy < v0cuts.min_r_tpconly) { // TPConly tracks can detect conversion points larger than v0cuts.min_r_tpconly. + return; + } + + // Apply a topological constraint of the gamma to the PV. Parameters will be given at the primary vertex. + KFParticle gammaKF_PV = gammaKF; + gammaKF_PV.SetProductionVertex(KFPV); + float v0pt = RecoDecay::sqrtSumOfSquares(gammaKF_PV.GetPx(), gammaKF_PV.GetPy()); + float v0eta = RecoDecay::eta(std::array{gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz()}); + float v0phi = RecoDecay::constrainAngle(RecoDecay::phi(gammaKF_PV.GetPx(), gammaKF_PV.GetPy())); + + // KFParticle gammaKF_DecayVtx2 = gammaKF; + // gammaKF_DecayVtx2.SetProductionVertex(KFPV); + // gammaKF_DecayVtx2.TransportToPoint(xyz); + // LOGF(info, "gammaKF_PV.GetPx() = %f, gammaKF_DecayVtx.GetPx() = %f, gammaKF_DecayVtx2.GetPx() = %f", gammaKF_PV.GetPx(), gammaKF_DecayVtx.GetPx(), gammaKF_DecayVtx2.GetPx()); + // LOGF(info, "gammaKF_PV.GetPy() = %f, gammaKF_DecayVtx.GetPy() = %f, gammaKF_DecayVtx2.GetPy() = %f", gammaKF_PV.GetPy(), gammaKF_DecayVtx.GetPy(), gammaKF_DecayVtx2.GetPy()); + // LOGF(info, "gammaKF_PV.GetPz() = %f, gammaKF_DecayVtx.GetPz() = %f, gammaKF_DecayVtx2.GetPz() = %f", gammaKF_PV.GetPz(), gammaKF_DecayVtx.GetPz(), gammaKF_DecayVtx2.GetPz()); + + if (std::fabs(v0eta) > v0cuts.max_eta_v0 || v0pt < v0cuts.min_pt_v0) { + return; + } + + if (isITSonlyTrack(ele) && isITSonlyTrack(pos) && v0pt > v0cuts.max_pt_v0_itsonly) { + return; + } + + KFParticle kfp_pos_DecayVtx = kfp_pos; // Don't set Primary Vertex + KFParticle kfp_ele_DecayVtx = kfp_ele; // Don't set Primary Vertex + kfp_pos_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex + kfp_ele_DecayVtx.TransportToPoint(xyz.data()); // Don't set Primary Vertex + + float cospaXYKF = cospaXY_KF(gammaKF_DecayVtx, KFPV); + float cospaRZKF = cospaRZ_KF(gammaKF_DecayVtx, KFPV); + auto centType = static_cast(mlcuts.centTypePCMMl.value); + // cosPA slot of the candidate (dedup score, cosPA cut): xy as in master, or 3D with dedup_group.useCosPA3D + v0photoncandidate.setPhotonCandidate(gammaKF_DecayVtx, gammaKF_PV, pos, kfp_pos_DecayVtx, ele, kfp_ele_DecayVtx, collision, dedup.useCosPA3D ? cospa_kf : cospaXYKF, cospaRZKF, cospaXYKF, psipair, phiv, centType, posdcaXY, eledcaXY, posdcaZ, eledcaZ); + + if (!ele.hasITS() && !pos.hasITS()) { // V0s with TPConly-TPConly + if (v0cuts.max_r_itsmft_ss < rxy && rxy < trackcuts.maxX + v0cuts.margin_r_tpc) { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpc_inner_fc) { + return; + } + } else { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_tpconly) { + return; + } + } + } else if (opt.nonSVertexer && recovery.finderMaxPCAMixed > 0.f && pos.hasITS() != ele.hasITS()) { + if (v0photoncandidate.getPCA() > recovery.finderMaxPCAMixed) { + return; + } + } else { // V0s with ITS hits + if (rxy < v0cuts.max_r_req_its) { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_itsibss) { + return; + } + } else { + if (v0photoncandidate.getPCA() > v0cuts.max_dcav0dau_its) { + return; + } + } + } + + if (isITSonlyTrack(pos) && v0photoncandidate.getPosPt() > trackcuts.maxpt_itsonly) { + return; + } + + if (isITSonlyTrack(ele) && v0photoncandidate.getElePt() > trackcuts.maxpt_itsonly) { + return; + } + + if (v0photoncandidate.getChi2NDF() > 6e+3) { // protection for uint16. // o2-linter: disable=magic-number (protection for uint16) + return; + } + + if (std::fabs(v0photoncandidate.getDcaXYToPV()) > v0cuts.max_dcatopv_xy_v0 || std::fabs(v0photoncandidate.getDcaZToPV()) > v0cuts.max_dcatopv_z_v0) { + return; + } + + if (!opt.relaxAP && !checkAP(v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0cuts.max_alpha_ap, v0cuts.max_qt_ap)) { // store only photon conversions + return; + } + + ROOT::Math::PxPyPzMVector vposDedup(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + ROOT::Math::PxPyPzMVector veleDedup(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + const auto meeSV = static_cast((vposDedup + veleDedup).M()); + + // passed all cuts: score and dedup candidate + const float gap = circleGap(o2::track::TrackAuxPar(pTrack, d_bz), o2::track::TrackAuxPar(nTrack, d_bz)); + const float score = dedupScore(v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), gap); + if (opt.probe != nullptr) { + *opt.probe = {true, score}; + return; + } + const float purity = usePurity() ? purityScore(pos, ele, cospa_kf, v0photoncandidate.getPCA(), meeSV, gap, v0photoncandidate.getPsiPair()) : -999.f; + if (opt.purityGate) { + registry.fill(HIST("Finder/hScore"), purity); + if (purity <= recovery.finderMinScore) { + return; + } + } + + pca_map[CandKey{v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex()}] = v0photoncandidate.getPCA(); + cospa_map[CandKey{v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex()}] = v0photoncandidate.getCosPA(); + + V0CandidateHelper v0Helper(v0Id, collision.globalIndex(), pos.globalIndex(), ele.globalIndex(), + v0photoncandidate.getCosPA(), v0photoncandidate.getPCA(), score, meeSV); + v0Helper.pxAtPV = gammaKF_PV.GetPx(); + v0Helper.pyAtPV = gammaKF_PV.GetPy(); + v0Helper.pzAtPV = gammaKF_PV.GetPz(); + v0Helper.svX = gammaKF_DecayVtx.GetX(); + v0Helper.svY = gammaKF_DecayVtx.GetY(); + v0Helper.svZ = gammaKF_DecayVtx.GetZ(); + v0Helper.purity = purity; + v0Helper.cosPAxy = cospaXYKF; + v0Helper.cosPA3D = cospa_kf; + vecV0Dedup.emplace_back(v0Helper); + + if (mlcuts.applyPCMMl) { + bool isSelectedML = false; + std::vector mlInputFeatures = emMlResponse.getInputFeatures(v0photoncandidate, pos, ele); + if (mlcuts.use2DBinning) { + isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), v0photoncandidate.getCent(), outputML); + } else { + isSelectedML = emMlResponse.isSelectedMl(mlInputFeatures, v0photoncandidate.getPt(), outputML); + } + if (filltable) { + if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { + registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSignalScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { + registry.fill(HIST("V0/hBDTPrimaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSecondaryPhotonScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + registry.fill(HIST("V0/hBDTBackgroundScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[2]); + } else { + registry.fill(HIST("V0/hBDTScoreBeforeCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + } + } + if (!isSelectedML) { + return; + } + if (filltable) { + if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kTwo) { + registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSignalScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + } else if (mlcuts.nClassesPCMMl == o2::analysis::NClassesML::kThree) { + registry.fill(HIST("V0/hBDTPrimaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + registry.fill(HIST("V0/hBDTSecondaryPhotonScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[1]); + registry.fill(HIST("V0/hBDTBackgroundScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[2]); + } else { + registry.fill(HIST("V0/hBDTScoreAfterCutVsPt"), v0photoncandidate.getPt(), outputML[0]); + } + } + } + + if (filltable) { + registry.fill(HIST("V0/hAP"), v0photoncandidate.getAlpha(), v0photoncandidate.getQt()); + registry.fill(HIST("V0/hConversionPointXY"), gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY()); + registry.fill(HIST("V0/hConversionPointRZ"), gammaKF_DecayVtx.GetZ(), rxy); + registry.fill(HIST("V0/hPt"), v0photoncandidate.getPt()); + registry.fill(HIST("V0/hEtaPhi"), v0phi, v0eta); + registry.fill(HIST("V0/hCosPA"), v0photoncandidate.getCosPA()); + registry.fill(HIST("V0/hCosPA_Rxy"), rxy, v0photoncandidate.getCosPA()); + registry.fill(HIST("V0/hPCA"), v0photoncandidate.getPCA()); + registry.fill(HIST("V0/hPCA_CosPA"), v0photoncandidate.getCosPA(), v0photoncandidate.getPCA()); + registry.fill(HIST("V0/hPCA_Rxy"), rxy, v0photoncandidate.getPCA()); + registry.fill(HIST("V0/hDCAxyz"), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV()); + registry.fill(HIST("V0/hPCA_diffX"), v0photoncandidate.getPCA(), std::min(pTrack.getX(), nTrack.getX()) - rxy); // trackiu.x() - rxy should be positive + registry.fill(HIST("V0/hPhiVPsiPair"), v0photoncandidate.getPsiPair(), v0photoncandidate.getPhiV()); + + // LOGF(info, "cospa_kf = %f, cospaXY_kf = %f, cospaRZ_kf = %f", cospa_kf, cospaXY_kf, cospaRZ_kf); + registry.fill(HIST("V0/hCosPAXY_Rxy"), rxy, cospaXYKF); + registry.fill(HIST("V0/hCosPARZ_Rxy"), rxy, cospaRZKF); + + for (const auto& leg : {kfp_pos_DecayVtx, kfp_ele_DecayVtx}) { + float legpt = RecoDecay::sqrtSumOfSquares(leg.GetPx(), leg.GetPy()); + float legeta = RecoDecay::eta(std::array{leg.GetPx(), leg.GetPy(), leg.GetPz()}); + float legphi = RecoDecay::constrainAngle(RecoDecay::phi(leg.GetPx(), leg.GetPy())); + registry.fill(HIST("V0Leg/hPt"), legpt); + registry.fill(HIST("V0Leg/hEtaPhi"), legphi, legeta); + } // end of leg loop + for (const auto& leg : {pos, ele}) { + registry.fill(HIST("V0Leg/hdEdx_Pin"), leg.tpcInnerParam(), leg.tpcSignal()); + registry.fill(HIST("V0Leg/hTPCNsigmaEl"), leg.tpcInnerParam(), leg.tpcNSigmaEl()); + } // end of leg loop + for (const auto& leg : {pTrack, nTrack}) { + registry.fill(HIST("V0Leg/hXZ"), leg.getZ(), leg.getX()); + registry.fill(HIST("V0Leg/hRelDeltaPt"), leg.getPt(), leg.getPt() * std::sqrt(leg.getSigma1Pt2())); + } // end of leg loop + registry.fill(HIST("V0Leg/hDCAxyz"), posdcaXY, posdcaZ); + registry.fill(HIST("V0Leg/hDCAxyz"), eledcaXY, eledcaZ); + + ROOT::Math::PxPyPzMVector vpos_sv(kfp_pos_DecayVtx.GetPx(), kfp_pos_DecayVtx.GetPy(), kfp_pos_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + ROOT::Math::PxPyPzMVector vele_sv(kfp_ele_DecayVtx.GetPx(), kfp_ele_DecayVtx.GetPy(), kfp_ele_DecayVtx.GetPz(), o2::constants::physics::MassElectron); + ROOT::Math::PxPyPzMVector v0_sv = vpos_sv + vele_sv; + registry.fill(HIST("V0/hMeeSV_Rxy"), rxy, v0_sv.M()); + + v0photonskf(collision.globalIndex(), v0Id, v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, + gammaKF_DecayVtx.GetX(), gammaKF_DecayVtx.GetY(), gammaKF_DecayVtx.GetZ(), + gammaKF_PV.GetPx(), gammaKF_PV.GetPy(), gammaKF_PV.GetPz(), + v0_sv.M(), v0photoncandidate.getDcaXYToPV(), v0photoncandidate.getDcaZToPV(), + cospa_kf, cospaXYKF, cospaRZKF, + v0photoncandidate.getPCA(), v0photoncandidate.getAlpha(), v0photoncandidate.getQt(), v0photoncandidate.getChi2NDF()); + v0photonsphivpsi(v0photoncandidate.getPhiV(), v0photoncandidate.getPsiPair()); + + // v0photonskfcov(gammaKF_PV.GetCovariance(9), gammaKF_PV.GetCovariance(14), gammaKF_PV.GetCovariance(20), gammaKF_PV.GetCovariance(13), gammaKF_PV.GetCovariance(19), gammaKF_PV.GetCovariance(18)); + + fillTrackTable(pos, kfp_pos_DecayVtx, posdcaXY, posdcaZ); // positive leg first + fillTrackTable(ele, kfp_ele_DecayVtx, eledcaXY, eledcaZ); // negative leg second + } // end of fill table + } + + // bookkeeping of candidates and stored photons + static CandKey keyOf(V0CandidateHelper const& c) + { + return CandKey{c.v0ID, c.colID, c.posID, c.eleID}; + } + + std::map candidateIndexByKey() const + { + std::map index; + for (int i = 0; i < static_cast(vecV0Dedup.size()); ++i) { + index[keyOf(vecV0Dedup[i])] = i; + } + return index; + } + + // replaces the stored photons after the dedup and keeps stored_v0Ids and nv0_map consistent + void setStored(std::vector keys) + { + std::stable_sort(keys.begin(), keys.end(), [](const auto& a, const auto& b) { return a.collisionId < b.collisionId; }); + stored_fullv0Ids.swap(keys); + stored_v0Ids.clear(); + for (auto& [col, n] : nv0_map) { // o2-linter: disable=const-ref-in-for-loop (counters are reset in place) + n = 0; + } + for (const auto& key : stored_fullv0Ids) { + stored_v0Ids.emplace_back(key.posId, key.eleId); + nv0_map[key.collisionId]++; + } + } + + // photon finder + // finder leg: helix circle and the time window in which the track can belong to a collision (TPC-only legs: from the drift-time asymmetry) + template + bool makeFinderLeg(TTrack const& track, TCollisions const& collisions, const int64_t bcRef, FinderLeg& leg) + { + using namespace o2::pwgem::photonmeson; + if (track.collisionId() < 0 || !track.hasTPC()) { + return false; + } + leg.id = track.globalIndex(); + leg.tgl = track.tgl(); + leg.noITS = !track.hasITS(); + leg.movable = isTPConlyTrack(track) && (track.flags() & o2::aod::track::TrackTimeAsym) != 0; + const auto bc = collisions.rawIteratorAt(track.collisionId()).template bc_as(); + leg.t = static_cast(static_cast(bc.globalBC()) - bcRef) * o2::constants::lhc::LHCBunchSpacingNS + track.trackTime(); + if (leg.movable) { + const int side = tpcDriftSide(track); + if (side == TPCSideUndetermined) { + return false; + } + leg.side = side; + leg.tMin = leg.t - track.tpcDeltaTBwd() - recovery.finderTimeMargin; + leg.tMax = leg.t + track.tpcDeltaTFwd() + recovery.finderTimeMarginFwd; + } else { + constexpr float NSigmaTime = 4.f; + const float sigma = std::max(track.trackTimeRes(), 1.f); + const float halfWidth = (track.flags() & o2::aod::track::TrackTimeResIsRange) != 0 ? sigma : NSigmaTime * sigma; + leg.side = 0; + leg.tMin = leg.t - halfWidth - recovery.finderTimeMargin; + leg.tMax = leg.t + halfWidth + recovery.finderTimeMargin; + } + leg.par = getTrackPar(track); + leg.circle = o2::track::TrackAuxPar(leg.par, d_bz); + return true; + } + + // pairs the free legs of the whole DF (not used by any candidate unless finderFreeCollinearLegs, at least one leg without ITS); a pair that passes the geometry + // (evalFinderPair) and matches a collision in z is fitted and becomes a dedup candidate with v0Id = -1 + template + void runPhotonFinder(TCollisions const& collisions, TTracks const& tracks) + { + std::set usedLegs; + std::set> builtPairs; + // legs of accepted candidates are not free; with finderFreeCollinearLegs, legs of collinear candidate pairs stay free + std::vector collinear(vecV0Dedup.size(), false); + if (recovery.finderFreeCollinearLegs > 0.f) { + std::map> candsOfColl; + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + candsOfColl[vecV0Dedup[i].colID].push_back(i); + } + for (const auto& [colId, members] : candsOfColl) { + for (size_t a = 0; a < members.size(); ++a) { + for (size_t b = a + 1; b < members.size(); ++b) { + const auto& ci = vecV0Dedup[members[a]]; + const auto& cj = vecV0Dedup[members[b]]; + if (ci.posID == cj.posID || ci.eleID == cj.eleID) { + continue; + } + if (qInv(ci.pxAtPV, ci.pyAtPV, ci.pzAtPV, cj.pxAtPV, cj.pyAtPV, cj.pzAtPV) < recovery.finderFreeCollinearLegs) { + collinear[members[a]] = true; + collinear[members[b]] = true; + } + } + } + } + } + for (size_t i = 0; i < vecV0Dedup.size(); ++i) { + const auto& c = vecV0Dedup[i]; + builtPairs.emplace(c.posID, c.eleID); + if (!collinear[i]) { + usedLegs.insert(c.posID); + usedLegs.insert(c.eleID); + } + } + std::vector colls; + int64_t bcRef = -1; + for (const auto& col : collisions) { + if constexpr (isMC) { + if (!col.has_mcCollision()) { + continue; + } + } + if (!col.isSelected()) { + continue; + } + const auto bc = col.template bc_as(); + if (bcRef < 0) { + bcRef = static_cast(bc.globalBC()); + } + colls.push_back({static_cast(static_cast(bc.globalBC()) - bcRef) * o2::constants::lhc::LHCBunchSpacingNS + col.collisionTime(), col.globalIndex(), col.posX(), col.posY(), col.posZ()}); + } + if (colls.empty()) { + return; + } + std::sort(colls.begin(), colls.end(), [](const FinderCollision& a, const FinderCollision& b) { return a.t < b.t; }); + const auto* vd = ccdb->getForTimeStamp("TPC/Calib/VDriftTgl", mLastCCDBTimestamp); + if (vd == nullptr) { + LOG(warning) << "photon finder: no TPC drift velocity for timestamp " << mLastCCDBTimestamp; + return; + } + const double vDrift = vd->refVDrift * vd->corrFact * 1e-3; + float xB = 0.f, yB = 0.f; + for (const auto& c : colls) { + xB += c.x / static_cast(colls.size()); + yB += c.y / static_cast(colls.size()); + } + + std::vector posLegs, eleLegs; + for (const auto& track : tracks) { + if (usedLegs.contains(track.globalIndex()) || track.pt() < trackcuts.min_pt_trackiu || !checkV0leg(track)) { + continue; + } + FinderLeg leg; + if (makeFinderLeg(track, collisions, bcRef, leg)) { + (track.sign() > 0 ? posLegs : eleLegs).push_back(leg); + } + } + std::sort(eleLegs.begin(), eleLegs.end(), [](const FinderLeg& a, const FinderLeg& b) { return a.tgl < b.tgl; }); + + const FinderCuts cuts{recovery.finderMaxDeltaTgl, recovery.finderMaxCircleGap, v0cuts.min_r_tpconly, v0cuts.max_r_req_its, recovery.finderRMargin, + trackcuts.maxX + v0cuts.margin_r_tpc, recovery.finderMinCosXY, recovery.finderMaxDeltaZ, recovery.finderMaxDz, d_bz}; + const FitOptions finderFit{.nonSVertexer = true, .purityGate = true}; + const size_t nBefore = vecV0Dedup.size(); + for (const auto& p : posLegs) { + for (auto itE = std::lower_bound(eleLegs.begin(), eleLegs.end(), p.tgl - recovery.finderMaxDeltaTgl, [](const FinderLeg& c, float v) { return c.tgl < v; }); + itE != eleLegs.end() && itE->tgl <= p.tgl + recovery.finderMaxDeltaTgl; ++itE) { + if ((!p.noITS && !itE->noITS) || builtPairs.contains({p.id, itE->id})) { + continue; + } + int64_t colId = -1; + const int step = evalFinderPair(p, *itE, colls, cuts, xB, yB, vDrift, colId); + if (step >= 0) { + registry.fill(HIST("Finder/hSteps"), static_cast(step)); + } + if (step == FinderStep::kFindCollision) { + fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(p.id), tracks.rawIteratorAt(itE->id), -1, false, finderFit); + } + } + } + registry.fill(HIST("Finder/hNAdded"), static_cast(vecV0Dedup.size() - nBefore)); + } + + // two stored photons that use two pieces of one track (same tgl, compatible parameters): keep the better one + template + void resolveSplitLegConflicts(TTracks const& tracks, DedupDiag* diag) + { + const auto indexOfKey = candidateIndexByKey(); + std::vector> order; + order.reserve(stored_fullv0Ids.size()); + for (size_t k = 0; k < stored_fullv0Ids.size(); ++k) { + const auto it = indexOfKey.find(stored_fullv0Ids[k]); + order.emplace_back(it == indexOfKey.end() ? std::numeric_limits::max() : (usePurity() ? -vecV0Dedup[it->second].purity : vecV0Dedup[it->second].score), k); + } + std::stable_sort(order.begin(), order.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); + auto samePiece = [&](int64_t idA, int64_t idB) { + if (idA == idB) { + return false; + } + const auto trA = tracks.rawIteratorAt(idA); + const auto trB = tracks.rawIteratorAt(idB); + if (std::fabs(trA.tgl() - trB.tgl()) > dedup.splitLegMaxDTgl) { + return false; + } + const float chi2 = splitPieceChi2(trA, trB, d_bz); + return chi2 >= 0.f && chi2 < dedup.splitLegMaxChi2; + }; + std::vector> keptPos, keptEle; + std::vector removed(stored_fullv0Ids.size(), 0); + int nRemoved = 0; + for (const auto& [rank, k] : order) { + const auto& key = stored_fullv0Ids[k]; + int64_t owner = -1; + for (size_t m = 0; m < keptPos.size() && owner < 0; ++m) { + if (samePiece(key.posId, keptPos[m].first)) { + owner = static_cast(keptPos[m].second); + } + } + for (size_t m = 0; m < keptEle.size() && owner < 0; ++m) { + if (samePiece(key.eleId, keptEle[m].first)) { + owner = static_cast(keptEle[m].second); + } + } + if (owner >= 0) { + removed[k] = 1; + ++nRemoved; + if (diag != nullptr) { + diag->blockersByKey[key].push_back(stored_fullv0Ids[owner]); + } + continue; + } + keptPos.emplace_back(key.posId, k); + keptEle.emplace_back(key.eleId, k); + } + if (nRemoved > 0) { + std::vector kept; + kept.reserve(stored_fullv0Ids.size() - nRemoved); + for (size_t k = 0; k < stored_fullv0Ids.size(); ++k) { + if (removed[k] == 0) { + kept.push_back(stored_fullv0Ids[k]); + } + } + setStored(std::move(kept)); + } + registry.fill(HIST("Dedup/hSplitLegRemoved"), static_cast(nRemoved)); + } + + // ambiguity resolver + // two stored photons of one collision, close in q and in space: fit the crossed leg pairing; if both crossed photons pass and + // their summed score is lower, swap + template + void runSwapResolver(TCollisions const& collisions, TTracks const& tracks, DedupDiag* diag) + { + const auto indexOfKey = candidateIndexByKey(); + std::map> storedOfColl; + for (const auto& key : stored_fullv0Ids) { + if (const auto it = indexOfKey.find(key); it != indexOfKey.end()) { + storedOfColl[key.collisionId].push_back(it->second); + } + } + // the AP cut is the main reason the true pairing of a crossed group was not built + auto fitPair = [&](int64_t colId, int64_t posId, int64_t eleId, PairFit* probe) { + fitV0Candidate(collisions.rawIteratorAt(colId), tracks.rawIteratorAt(posId), tracks.rawIteratorAt(eleId), -1, false, + FitOptions{.nonSVertexer = true, .relaxAP = true, .probe = probe}); + }; + auto probeFit = [&](int64_t colId, int64_t posId, int64_t eleId) { + PairFit out; + fitPair(colId, posId, eleId, &out); + return out; + }; + auto crossedPrefilter = [&](int64_t posId, int64_t eleId) { + const auto pos = tracks.rawIteratorAt(posId); + const auto ele = tracks.rawIteratorAt(eleId); + if (std::fabs(pos.tgl() - ele.tgl()) > recovery.finderMaxDeltaTgl) { + return false; + } + const o2::track::TrackAuxPar cp(getTrackPar(pos), d_bz); + const o2::track::TrackAuxPar ce(getTrackPar(ele), d_bz); + return std::fabs(circleGap(cp, ce)) < recovery.finderMaxCircleGap; + }; + std::map storedFit; + auto storedFitOf = [&](int idx) -> const PairFit& { + auto it = storedFit.find(idx); + if (it == storedFit.end()) { + const auto& c = vecV0Dedup[idx]; + it = storedFit.emplace(idx, probeFit(c.colID, c.posID, c.eleID)).first; + } + return it->second; + }; + + // test all close pairs of stored photons + struct Test { + SwapRecord rec; + float gain = -999.f; + }; + std::vector swaps; + for (const auto& [colId, members] : storedOfColl) { + std::vector tests; + for (size_t a = 0; a < members.size(); ++a) { + for (size_t b = a + 1; b < members.size(); ++b) { + const auto& ci = vecV0Dedup[members[a]]; + const auto& cj = vecV0Dedup[members[b]]; + if (ci.posID == cj.posID || ci.eleID == cj.eleID) { + continue; + } + const float q = qInv(ci.pxAtPV, ci.pyAtPV, ci.pzAtPV, cj.pxAtPV, cj.pyAtPV, cj.pzAtPV); + if (q > recovery.swapMaxQ || std::hypot(ci.svX - cj.svX, ci.svY - cj.svY, ci.svZ - cj.svZ) > recovery.swapMaxConvDist) { + continue; + } + const PairFit storedI = storedFitOf(members[a]); + const PairFit storedJ = storedFitOf(members[b]); + const PairFit crossedI = crossedPrefilter(ci.posID, cj.eleID) ? probeFit(colId, ci.posID, cj.eleID) : PairFit{}; + const PairFit crossedJ = crossedPrefilter(cj.posID, ci.eleID) ? probeFit(colId, cj.posID, ci.eleID) : PairFit{}; + Test t; + t.rec = {keyOf(ci), keyOf(cj), q, storedI.ok && storedJ.ok && crossedI.ok && crossedJ.ok, false}; + if (t.rec.complete) { // gain > 0: the crossed pairing has the lower (better) summed dedup score + t.gain = (storedI.score + storedJ.score) - (crossedI.score + crossedJ.score); + } + tests.push_back(t); + } + } + std::vector order(tests.size()); + std::iota(order.begin(), order.end(), 0); + std::sort(order.begin(), order.end(), [&](int x, int y) { return tests[x].gain > tests[y].gain; }); + std::set used; + for (const int& k : order) { + auto& t = tests[k]; + if (t.rec.complete && t.gain > 0.f && !used.contains(t.rec.keyI) && !used.contains(t.rec.keyJ)) { + t.rec.swapped = true; + used.insert(t.rec.keyI); + used.insert(t.rec.keyJ); + swaps.push_back(t.rec); + } + registry.fill(HIST("Swap/hTested"), 0.f); + registry.fill(HIST("Swap/hQ"), t.rec.q, t.rec.swapped ? 1.f : 0.f); + if (diag != nullptr) { + diag->swaps.push_back(t.rec); + } + } + } + if (swaps.empty()) { + return; + } + + // apply swaps: the crossed photons become candidates and replace the stored pair + const std::set storedBefore(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); + std::set removedKeys; + std::vector addedKeys; + for (const auto& s : swaps) { + const int64_t colId = s.keyI.collisionId; + const int64_t posI = s.keyI.posId, eleI = s.keyI.eleId, posJ = s.keyJ.posId, eleJ = s.keyJ.eleId; + const CandKey a1 = CandKey{-1, colId, posI, eleJ}; + const CandKey a2 = CandKey{-1, colId, posJ, eleI}; + if (storedBefore.contains(a1) || storedBefore.contains(a2)) { + continue; + } + if (!pca_map.contains(a1)) { + fitPair(colId, posI, eleJ, nullptr); + } + if (!pca_map.contains(a2)) { + fitPair(colId, posJ, eleI, nullptr); + } + if (!pca_map.contains(a1) || !pca_map.contains(a2)) { + continue; + } + removedKeys.insert(s.keyI); + removedKeys.insert(s.keyJ); + addedKeys.push_back(a1); + addedKeys.push_back(a2); + mRelaxedKeys.insert(a1); // refitted for the table without the AP cut, as in the resolver fit + mRelaxedKeys.insert(a2); + registry.fill(HIST("Swap/hTested"), 1.f); + if (diag != nullptr) { + diag->blockersByKey[s.keyI] = {a1, a2}; + diag->blockersByKey[s.keyJ] = {a1, a2}; + } + } + std::vector kept; + kept.reserve(stored_fullv0Ids.size()); + for (const auto& key : stored_fullv0Ids) { + if (!removedKeys.contains(key)) { + kept.push_back(key); + } + } + kept.insert(kept.end(), addedKeys.begin(), addedKeys.end()); + setStored(std::move(kept)); + } + + // deduplication + Preslice perCollision = o2::aod::v0::collisionId; + std::map pca_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> pca + std::map cospa_map; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) -> cospa + std::vector vecV0Dedup; // vector with all V0Candidates that is used to sort them by score (see struct V0CandidateHelper for more details of content) + std::vector> stored_v0Ids; // (pos.globalIndex(), ele.globalIndex()) + std::vector stored_fullv0Ids; // (v0.globalIndex(), collision.globalIndex(), pos.globalIndex(), ele.globalIndex()) + std::unordered_map nv0_map; // map collisionId -> nv0 + + // mode 0: a candidate is kept if no other candidate sharing a leg has a smaller PCA and no copy in another collision has a larger cosPA + void selectPairwise(DedupDiag* diag) + { + for (const auto& [key, value] : pca_map) { + auto v0Id = key.v0Id; + auto collisionId = key.collisionId; + auto posId = key.posId; + auto eleId = key.eleId; + float v0pca = value; + float cospa = cospa_map[key]; + bool is_closest_v0 = true; + bool is_most_aligned_v0 = true; + + for (const auto& [key_tmp, value_tmp] : pca_map) { + auto collisionId_tmp = key_tmp.collisionId; + auto posId_tmp = key_tmp.posId; + auto eleId_tmp = key_tmp.eleId; + float v0pca_tmp = value_tmp; + float cospa_tmp = cospa_map[key_tmp]; + + if (key == key_tmp) { // skip exactly the same candidate + continue; + } + + if (collisionId != collisionId_tmp && eleId == eleId_tmp && posId == posId_tmp && cospa < cospa_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); + } + is_most_aligned_v0 = false; + break; + } + if ((eleId == eleId_tmp || posId == posId_tmp) && v0pca > v0pca_tmp) { + if (diag != nullptr) { + diag->blockersByKey[key].push_back(key_tmp); + } + is_closest_v0 = false; + break; + } + } // end of pca_map tmp loop + + bool is_stored = std::find(stored_v0Ids.begin(), stored_v0Ids.end(), std::make_pair(posId, eleId)) != stored_v0Ids.end(); + if (is_closest_v0 && is_most_aligned_v0 && !is_stored) { + // auto v0 = v0s.rawIteratorAt(v0Id); + // auto collision = collisions.rawIteratorAt(collisionId); + // auto pos = tracks.rawIteratorAt(posId); + // auto ele = tracks.rawIteratorAt(eleId); + // LOGF(info, "!accept! | collision id = %d | v0id1 = %d , posid1 = %d , eleid1 = %d , pca1 = %f , cospa = %f", collisionId, v0Id, posId, eleId, v0pca, cospa); + + // fillV0Table(v0, true); + stored_v0Ids.emplace_back(std::make_pair(posId, eleId)); + stored_fullv0Ids.push_back(CandKey{v0Id, collisionId, posId, eleId}); + nv0_map[collisionId]++; + } + } // end of pca_map loop + if (recovery.runFinder || recovery.runSwapResolver) { + std::stable_sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), [](const auto& a, const auto& b) { return a.collisionId < b.collisionId; }); + } + // LOGF(info, "pca_map.size() = %d", pca_map.size()); + } + + // mode 3: every candidate is kept, including all duplicates + void keepAll() + { + // Keep-all: Every candidate that survived the quality cuts is stored, including all duplicates + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.push_back(keyOf(v0Cand)); + nv0_map[v0Cand.colID]++; + } + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + // mode 2: candidates sharing a leg form a conflict group; per group the best leg-disjoint subset is kept + void selectByConflictGroup(DedupDiag* diag) + { + const int maxEnum = std::min(dedup.dedupMaxGroupSize.value, 16); // o2-linter: disable=magic-number (subset check of photons) + const int nCand = static_cast(vecV0Dedup.size()); + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + int nGreedyFallback = 0; + + // conflict groups: candidates connected through a shared leg + std::vector parent(nCand); + for (int i = 0; i < nCand; ++i) { + parent[i] = i; + } + auto findRoot = [&parent](int x) { + while (parent[x] != x) { + parent[x] = parent[parent[x]]; // path halving + x = parent[x]; + } + return x; + }; + std::unordered_map firstCandOfTrack; + for (int i = 0; i < nCand; ++i) { + for (const int& trackId : {vecV0Dedup[i].posID, vecV0Dedup[i].eleID}) { + auto [it, inserted] = firstCandOfTrack.try_emplace(trackId, i); + if (!inserted) { + parent[findRoot(i)] = findRoot(it->second); + } + } + } + std::map> groups; + for (int i = 0; i < nCand; ++i) { + groups[findRoot(i)].push_back(i); + } + + // decide each group + for (auto& [root, members] : groups) { // o2-linter: disable=const-ref-in-for-loop (members is sorted in place below) + const int nMembers = static_cast(members.size()); + std::vector selected(nMembers, 0); + + // Fix the order inside the group: best score first + std::sort(members.begin(), members.end(), [this](int a, int b) { + if (vecV0Dedup[a].score != vecV0Dedup[b].score) { + return vecV0Dedup[a].score < vecV0Dedup[b].score; + } + if (vecV0Dedup[a].v0ID != vecV0Dedup[b].v0ID) { + return vecV0Dedup[a].v0ID < vecV0Dedup[b].v0ID; + } + return std::tie(vecV0Dedup[a].posID, vecV0Dedup[a].eleID, vecV0Dedup[a].colID) < std::tie(vecV0Dedup[b].posID, vecV0Dedup[b].eleID, vecV0Dedup[b].colID); + }); + + if (nMembers == 1) { + selected[0] = 1; + } else if (nMembers <= maxEnum) { + + std::unordered_map legBit; + std::vector legMask(nMembers, 0); + for (int k = 0; k < nMembers; ++k) { + for (const int& trackId : {vecV0Dedup[members[k]].posID, vecV0Dedup[members[k]].eleID}) { + const int bit = legBit.try_emplace(trackId, static_cast(legBit.size())).first->second; + legMask[k] |= (1ull << bit); // at most 2 * 16 = 32 legs, fits into 64 bit + } + } + std::vector scores(nMembers); + for (int k = 0; k < nMembers; ++k) { + scores[k] = vecV0Dedup[members[k]].score; + } + const auto sol = solveConflictGroup(legMask, scores, dedup.groupMatchingScoreThreshold.value, diag != nullptr); + for (int k = 0; k < nMembers; ++k) { + selected[k] = ((sol.bestMask & (1u << k)) != 0) ? 1 : 0; + if (diag != nullptr && selected[k] == 0 && sol.bestWithCount[k] >= 0) { + const auto& cand = vecV0Dedup[members[k]]; + diag->lossMargin[keyOf(cand)] = + std::make_pair(sol.bestCount - sol.bestWithCount[k], sol.bestWithScore[k] - sol.bestScore); + } + } + } else { + // Too large to enumerate: greedy by score + ++nGreedyFallback; + std::vector usedTracks; + usedTracks.reserve(2 * nMembers); + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (std::find(usedTracks.begin(), usedTracks.end(), cand.posID) != usedTracks.end() || + std::find(usedTracks.begin(), usedTracks.end(), cand.eleID) != usedTracks.end()) { + continue; + } + usedTracks.push_back(cand.posID); + usedTracks.push_back(cand.eleID); + selected[k] = 1; + } + } + + // store the winners, book the blockers of the losers + for (int k = 0; k < nMembers; ++k) { + const auto& cand = vecV0Dedup[members[k]]; + if (selected[k] != 0) { + stored_v0Ids.emplace_back(cand.posID, cand.eleID); + stored_fullv0Ids.push_back(keyOf(cand)); + nv0_map[cand.colID]++; + continue; + } + if (diag == nullptr) { + continue; + } + // all winners that took one leg + const CandKey victimKey = keyOf(cand); + for (int j = 0; j < nMembers; ++j) { + if (selected[j] == 0) { + continue; + } + const auto& winner = vecV0Dedup[members[j]]; + if (winner.posID == cand.posID || winner.eleID == cand.eleID) { + diag->blockersByKey[victimKey].push_back(keyOf(winner)); + } + } + } + } + + if (nGreedyFallback > 0) { + LOGF(warning, "group matching: %d conflict group(s) larger than dedupMaxGroupSize = %d were solved greedily", nGreedyFallback, maxEnum); + } + + // the table expects candidates ordered by collision + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + // mode 1: candidates sorted by score (lower = better), a candidate is kept if both legs are still free + void selectGreedy(const size_t nTracks, DedupDiag* diag) + { + // Sort best candidates first, depending on score + std::sort(vecV0Dedup.begin(), vecV0Dedup.end()); + // container to keep track of which electron and positron tracks have already been used + std::vector usedLegs(nTracks, false); + std::unordered_map ownerOfLeg; // diagnostics only + stored_v0Ids.clear(); + stored_fullv0Ids.clear(); + nv0_map.clear(); + for (const auto& v0Cand : vecV0Dedup) { + const CandKey candKey = keyOf(v0Cand); + if (usedLegs[v0Cand.posID] || usedLegs[v0Cand.eleID]) { + if (diag != nullptr) { + // both legs, not just the first one found: a cross-leg fake is usually blocked by + // two different winners, and that is exactly the case being measured here + auto& blockers = diag->blockersByKey[candKey]; + for (const int& legId : {v0Cand.posID, v0Cand.eleID}) { + const auto itOwner = ownerOfLeg.find(legId); + if (itOwner == ownerOfLeg.end()) { + continue; + } + if (std::find(blockers.begin(), blockers.end(), itOwner->second) == blockers.end()) { + blockers.push_back(itOwner->second); // the same winner may hold both legs + } + } + } + continue; + } + usedLegs[v0Cand.posID] = true; + usedLegs[v0Cand.eleID] = true; + if (diag != nullptr) { + ownerOfLeg[v0Cand.posID] = candKey; + ownerOfLeg[v0Cand.eleID] = candKey; + } + stored_v0Ids.emplace_back(v0Cand.posID, v0Cand.eleID); + stored_fullv0Ids.push_back(keyOf(v0Cand)); + nv0_map[v0Cand.colID]++; + } + + // sort accepted candidates by collision ID to be compatible with table + std::sort(stored_fullv0Ids.begin(), stored_fullv0Ids.end(), + [](const auto& a, const auto& b) { + return a.collisionId < b.collisionId; + }); + } + + template + void build(TCollisions const& collisions, TV0s const& v0s, TTracks const& tracks, TBCs const&, DedupDiag* diag = nullptr) + { + mRelaxedKeys.clear(); + // step 1: SVertexer V0s -> dedup candidates + for (const auto& collision : collisions) { + if constexpr (isMC) { + if (!collision.has_mcCollision()) { + continue; + } + } + + if (!collision.isSelected()) { + continue; + } + + // if constexpr (isTriggerAnalysis) { + // if (collision.triggerMask_raw() == 0) { + // continue; + // } + // } + + nv0_map[collision.globalIndex()] = 0; + + const auto& bc = collision.template foundBC_as(); + initCCDB(bc); + registry.fill(HIST("hCollisionCounter"), 1); + + updateCCDB(bc); // delay update until is needed + + vecV0Dedup.reserve(30000); // rough estimate for number of V0s per DF + const auto& v0s_per_coll = v0s.sliceBy(perCollision, collision.globalIndex()); + // LOGF(info, "n v0 = %d", v0s_per_coll.size()); + for (const auto& v0 : v0s_per_coll) { + // LOGF(info, "collision.globalIndex() = %d, v0.globalIndex() = %d, v0.posTrackId() = %d, v0.negTrackId() = %d", collision.globalIndex(), v0.globalIndex(), v0.posTrackId() , v0.negTrackId()); + fillV0Table(v0, false); + } // end of v0 loop + } // end of collision loop + + // photon finder + if (recovery.runFinder) { + runPhotonFinder(collisions, tracks); + } + + stored_v0Ids.reserve(pca_map.size()); // number of photon candidates per DF + stored_fullv0Ids.reserve(pca_map.size()); // number of photon candidates per DF + + // step 3: deduplication + switch (dedup.deduplicationMode.value) { + case V0DeduplicationMode::Pairwise: + selectPairwise(diag); + break; + case V0DeduplicationMode::KeepAll: + keepAll(); + break; + case V0DeduplicationMode::GroupMatching: + selectByConflictGroup(diag); + break; + default: + selectGreedy(tracks.size(), diag); + break; + } + + // split legs, ambiguity resolver (on the stored photons only) + if (dedup.resolveSplitLegs) { + resolveSplitLegConflicts(tracks, diag); + } + if (recovery.runSwapResolver) { + runSwapResolver(collisions, tracks, diag); + } + + if (diag != nullptr) { + diag->snapshot = vecV0Dedup; + diag->stored = stored_fullv0Ids; + } + + std::set skimRejected; + if (skim.minNv0PerCollision > 0 || skim.maxMinvPairPerCollision > 0.f || skim.minMinvPairPerCollision > 0.f) { // o2-linter: disable=magic-number (check if configurable is enabled) + const std::set storedSetSkim(stored_fullv0Ids.begin(), stored_fullv0Ids.end()); + std::unordered_map> candsPerColl; + for (const auto& c : vecV0Dedup) { + if (storedSetSkim.contains(keyOf(c))) { + candsPerColl[c.colID].push_back(&c); + } + } + for (const auto& [colId, cands] : candsPerColl) { + if (skim.minNv0PerCollision > 0 && static_cast(cands.size()) < skim.minNv0PerCollision) { + skimRejected.insert(colId); + continue; + } + if (skim.maxMinvPairPerCollision > 0.f || skim.minMinvPairPerCollision > 0.f) { // o2-linter: disable=magic-number (check if configurable is enabled) + bool hasPairOfInterest = false; + const float minMinv = skim.minMinvPairPerCollision > 0.f ? skim.minMinvPairPerCollision.value : 0.f; + const float maxMinv = skim.maxMinvPairPerCollision > 0.f ? skim.maxMinvPairPerCollision.value : std::numeric_limits::max(); + for (size_t i = 0; i < cands.size() && !hasPairOfInterest; ++i) { + for (size_t j = i + 1; j < cands.size(); ++j) { + ROOT::Math::PxPyPzMVector v1(cands[i]->pxAtPV, cands[i]->pyAtPV, cands[i]->pzAtPV, 0.f); + ROOT::Math::PxPyPzMVector v2(cands[j]->pxAtPV, cands[j]->pyAtPV, cands[j]->pzAtPV, 0.f); + const double massPair = (v1 + v2).M(); + if (massPair > minMinv && massPair < maxMinv) { + hasPairOfInterest = true; + break; + } + } + } + if (!hasPairOfInterest) { + skimRejected.insert(colId); + } + } + } + } + + // refit the stored photons and write the tables (SVertexer V0s through fillV0Table, the others directly) + for (const auto& fullv0Id : stored_fullv0Ids) { + auto v0Id = fullv0Id.v0Id; + const auto collisionId = fullv0Id.collisionId; + if (skimRejected.contains(collisionId)) { + continue; // collision failed the skim - do not store its photons/legs + } + // auto posId = fullv0Id.posId; + // auto eleId = fullv0Id.eleId; + // LOGF(info, "!accept! | collision id = %d | v0id = %d , posid = %d , eleid = %d", collisionId, v0Id, posId, eleId); + + if (v0Id >= 0) { + auto v0 = v0s.rawIteratorAt(v0Id); + if constexpr (enableFilter) { + auto collision_tmp = v0.template collision_as(); // collision where this v0 belongs. + if (!(collision_tmp.neeuls() >= 1 || collision_tmp.neeuls() + nv0_map[collision_tmp.globalIndex()] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) + continue; + } + // LOGF(info, "collision_tmp.globalIndex() = %d, collision_tmp.neeuls() = %d, nv0_map = %d", collision_tmp.globalIndex(), collision_tmp.neeuls(), nv0_map[collision_tmp.globalIndex()]); + } + + fillV0Table(v0, true); + continue; + } + const auto collision = collisions.rawIteratorAt(collisionId); + if constexpr (enableFilter) { + if (!(collision.neeuls() >= 1 || collision.neeuls() + nv0_map[collisionId] >= 2)) { // o2-linter: disable=magic-number (nonsensical default value) + continue; + } + } + fitV0Candidate(collision, tracks.rawIteratorAt(fullv0Id.posId), tracks.rawIteratorAt(fullv0Id.eleId), v0Id, true, + FitOptions{.nonSVertexer = true, .relaxAP = mRelaxedKeys.contains(fullv0Id)}); + } // end of fullv0Id loop + + // for (const auto& collision : collisions) { + // if constexpr (isMC) { + // if (!collision.has_mcCollision()) { + // continue; + // } + // } + // // events_ngpcm(nv0_map[collision.globalIndex()]); + // } // end of collision loop + + pca_map.clear(); + cospa_map.clear(); + nv0_map.clear(); + stored_v0Ids.clear(); + stored_v0Ids.shrink_to_fit(); + stored_fullv0Ids.clear(); + stored_fullv0Ids.shrink_to_fit(); + vecV0Dedup.clear(); + } // end of build + + o2::framework::expressions::Filter v0Filter = o2::aod::v0::v0Type > (uint8_t)0; + using FilteredV0s = o2::soa::Filtered; + + enum PairFate { + kBefore = 0, + kBothSurvive, + kOneLost, + kBothLost, + kLostByCrossFake, + kLostByPartnerFake, + kLostByOtherFake, + kLostByTrue + }; + + void fillTruthPair(int fate, float q, float dEta) + { + switch (fate) { + case kBefore: + registry.fill(HIST("MCDedup/Pairs/before/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/before/hDEta"), dEta); + break; + case kBothSurvive: + registry.fill(HIST("MCDedup/Pairs/bothSurvive/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/bothSurvive/hDEta"), dEta); + break; + case kOneLost: + registry.fill(HIST("MCDedup/Pairs/oneLost/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/oneLost/hDEta"), dEta); + break; + case kBothLost: + registry.fill(HIST("MCDedup/Pairs/bothLost/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/bothLost/hDEta"), dEta); + break; + case kLostByCrossFake: + registry.fill(HIST("MCDedup/Pairs/lostByCrossFake/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByCrossFake/hDEta"), dEta); + break; + case kLostByPartnerFake: + registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hDEta"), dEta); + break; + case kLostByOtherFake: + registry.fill(HIST("MCDedup/Pairs/lostByOtherFake/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByOtherFake/hDEta"), dEta); + break; + case kLostByTrue: + registry.fill(HIST("MCDedup/Pairs/lostByTrue/hQ"), q); + registry.fill(HIST("MCDedup/Pairs/lostByTrue/hDEta"), dEta); + break; + default: + break; + } + } + + void fillTruthPairMap(int fate, float dEta, float dPhi, float q) + { + if (!dedup.cfgDedupTruthMaps) { + return; + } + switch (fate) { + case kBefore: + registry.fill(HIST("MCDedup/Pairs/before/hMap"), dEta, dPhi, q); + break; + case kBothSurvive: + registry.fill(HIST("MCDedup/Pairs/bothSurvive/hMap"), dEta, dPhi, q); + break; + case kBothLost: + registry.fill(HIST("MCDedup/Pairs/bothLost/hMap"), dEta, dPhi, q); + break; + case kLostByPartnerFake: + registry.fill(HIST("MCDedup/Pairs/lostByPartnerFake/hMap"), dEta, dPhi, q); + break; + default: + break; + } + } + + struct PhotonFate { + bool alive = false; + int best = -1; // best-score candidate + std::vector cands; // all true candidates of this photon + std::set aliveColls; // collisions with a surviving candidate + }; + + // which collision the copies of a true photon end up in + template + void fillCopyDiagnostics(TCollisions const& collisions, TMCParticles const& mcparticles, DedupDiag const& diag, std::map const& fate) + { + auto isRightColl = [&](int64_t colId, int64_t mid) { + const auto col = collisions.rawIteratorAt(colId); + return col.has_mcCollision() && col.mcCollisionId() == mcparticles.iteratorAt(mid).mcCollisionId(); + }; + auto dzPointing = [&](V0CandidateHelper const& c) { + const auto col = collisions.rawIteratorAt(c.colID); + const float pt = std::hypot(c.pxAtPV, c.pyAtPV); + return pt > 0.f ? std::fabs(c.svZ - std::hypot(c.svX - col.posX(), c.svY - col.posY()) * c.pzAtPV / pt - col.posZ()) : 999.f; // o2-linter: disable=magic-number (no direction) + }; + for (const auto& [mid, f] : fate) { + std::set copyColls; + bool rightAvailable = false; + for (const int& ci : f.cands) { + copyColls.insert(diag.snapshot[ci].colID); + rightAvailable = rightAvailable || isRightColl(diag.snapshot[ci].colID, mid); + } + int choice = 3; // o2-linter: disable=magic-number (bin 3 = lost) + if (f.alive) { + const bool storedRight = std::any_of(f.aliveColls.begin(), f.aliveColls.end(), [&](int64_t c) { return isRightColl(c, mid); }); + choice = storedRight ? 0 : (rightAvailable ? 1 : 2); + } + const int nCopies = static_cast(copyColls.size()); + registry.fill(HIST("MCDedup/Copies/hChoice"), static_cast(std::min(nCopies, 5)), static_cast(choice)); // o2-linter: disable=magic-number (last bin) + constexpr int MinCopies = 2; + if (nCopies < MinCopies || !rightAvailable) { + continue; + } + int bestDz = -1, bestPA = -1, bestPA3D = -1; + for (const int& ci : f.cands) { + const auto& c = diag.snapshot[ci]; + const float right = isRightColl(c.colID, mid) ? 1.f : 0.f; + registry.fill(HIST("MCDedup/Copies/hPAxy"), right, std::acos(std::clamp(c.cosPAxy, -1.f, 1.f))); + registry.fill(HIST("MCDedup/Copies/hPA3D"), right, std::acos(std::clamp(c.cosPA3D, -1.f, 1.f))); + if (bestDz < 0 || dzPointing(c) < dzPointing(diag.snapshot[bestDz])) { + bestDz = ci; + } + if (bestPA < 0 || c.cosPAxy > diag.snapshot[bestPA].cosPAxy) { + bestPA = ci; + } + if (bestPA3D < 0 || c.cosPA3D > diag.snapshot[bestPA3D].cosPA3D) { + bestPA3D = ci; + } + } + if (f.alive) { // same photons for all rules + registry.fill(HIST("MCDedup/Copies/hRule"), 0.f, choice == 0 ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 1.f, isRightColl(diag.snapshot[bestDz].colID, mid) ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 2.f, isRightColl(diag.snapshot[bestPA].colID, mid) ? 1.f : 0.f); + registry.fill(HIST("MCDedup/Copies/hRule"), 3.f, isRightColl(diag.snapshot[bestPA3D].colID, mid) ? 1.f : 0.f); + } + } + } + + template + void fillDedupTruthDiagnostics(TCollisions const& collisions, TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) + { + const int nCand = static_cast(diag.snapshot.size()); + if (nCand == 0) { + return; + } + const std::set storedSet(diag.stored.begin(), diag.stored.end()); + + std::vector key(nCand); + std::vector cls(nCand, static_cast(kV0OtherFake)); + std::vector motherPos(nCand, -1), motherEle(nCand, -1); + std::map indexOfKey; + for (int i = 0; i < nCand; ++i) { + const auto& c = diag.snapshot[i]; + key[i] = keyOf(c); + indexOfKey[key[i]] = i; + cls[i] = static_cast(classifyV0Truth(tracks.rawIteratorAt(c.posID), tracks.rawIteratorAt(c.eleID), + mcparticles, motherPos[i], motherEle[i])); + registry.fill(HIST("MCDedup/Candidates/hClassStage"), static_cast(cls[i]), 0.f); + if (storedSet.contains(key[i]) > 0) { + registry.fill(HIST("MCDedup/Candidates/hClassStage"), static_cast(cls[i]), 1.f); + } + } + + std::map fate; + for (int i = 0; i < nCand; ++i) { + if (cls[i] != kV0True) { + continue; + } + auto& f = fate[motherPos[i]]; + f.cands.push_back(i); + if (storedSet.contains(key[i]) > 0) { + f.alive = true; + f.aliveColls.insert(key[i].collisionId); + } + if (f.best < 0 || diag.snapshot[i].score < diag.snapshot[f.best].score) { + f.best = i; + } + } + + std::map> blockersOfPhoton; + for (const auto& [mid, f] : fate) { + if (f.alive) { + registry.fill(HIST("MCDedup/Photons/hFate"), 0.f); + continue; + } + auto& blockers = blockersOfPhoton[mid]; + for (const int& ci : f.cands) { + const auto itBlocker = diag.blockersByKey.find(key[ci]); + if (itBlocker == diag.blockersByKey.end()) { + continue; + } + for (const auto& bKey : itBlocker->second) { + const auto itIndex = indexOfKey.find(bKey); + if (itIndex == indexOfKey.end()) { + continue; + } + if (std::find(blockers.begin(), blockers.end(), itIndex->second) == blockers.end()) { + blockers.push_back(itIndex->second); + } + } + } + // Why was it not in the winning set? Take the most favourable of its candidates: the one + // that could have been kept at the smallest cost in cardinality. + int bestDeficit = -1; + float bestExcess = 0.f; + for (const int& ci : f.cands) { + const auto itMargin = diag.lossMargin.find(key[ci]); + if (itMargin == diag.lossMargin.end()) { + continue; + } + if (bestDeficit < 0 || itMargin->second.first < bestDeficit || + (itMargin->second.first == bestDeficit && itMargin->second.second < bestExcess)) { + bestDeficit = itMargin->second.first; + bestExcess = itMargin->second.second; + } + } + if (bestDeficit >= 0) { + registry.fill(HIST("MCDedup/Photons/hLostMargin"), static_cast(bestDeficit), bestExcess); + } + // hVictimBlockerClass below counts one entry per blocker and a victim can have two. + registry.fill(HIST("MCDedup/Photons/hFate"), blockers.empty() ? 2.f : 1.f); + + if (blockers.empty()) { + registry.fill(HIST("MCDedup/Photons/hBlockerClass"), 3.f); // o2-linter: disable=magic-number (bin 3 = no blocker) + continue; + } + for (const int& w : blockers) { + registry.fill(HIST("MCDedup/Photons/hBlockerClass"), static_cast(cls[w])); + } + // margin and PCA comparison relation the best candidate to its blocker + + if (f.best >= 0 && dedup.deduplicationMode.value < static_cast(V0DeduplicationMode::GroupMatching)) { + const int w = blockers.front(); + registry.fill(HIST("MCDedup/Photons/hKillMargin"), diag.snapshot[f.best].score - diag.snapshot[w].score); + registry.fill(HIST("MCDedup/Photons/hVictimVsWinnerPCA"), diag.snapshot[f.best].pca, diag.snapshot[w].pca); + } + } + + if (dedup.cfgExtendedDiag) { + fillCopyDiagnostics(collisions, mcparticles, diag, fate); + } + + // stored cross-leg fakes by collisions + std::map, std::set> storedCrossColl; + for (int i = 0; i < nCand; ++i) { + if (cls[i] == kV0CrossLegFake && storedSet.contains(key[i])) { + storedCrossColl[{motherPos[i], motherEle[i]}].insert(key[i].collisionId); + } + } + + // truth pairs, grouped by MC collision + std::map> mothersByMcColl; + for (const auto& [mid, f] : fate) { + mothersByMcColl[mcparticles.iteratorAt(mid).mcCollisionId()].push_back(mid); + } + constexpr float MaxQTruth = 0.3f; + for (const auto& [mcCol, mids] : mothersByMcColl) { + for (size_t a = 0; a < mids.size(); ++a) { + for (size_t b = a + 1; b < mids.size(); ++b) { + const auto g1 = mcparticles.iteratorAt(mids[a]); + const auto g2 = mcparticles.iteratorAt(mids[b]); + const float e1 = std::hypot(g1.px(), g1.py(), g1.pz()); + const float e2 = std::hypot(g2.px(), g2.py(), g2.pz()); + const float qTrue = std::sqrt(std::max(0.f, 2.f * (e1 * e2 - g1.px() * g2.px() - g1.py() * g2.py() - g1.pz() * g2.pz()))); + if (qTrue > MaxQTruth) { + continue; + } + const float dEta = g1.eta() - g2.eta(); + const float dPhi = RecoDecay::constrainAngle(static_cast(g1.phi() - g2.phi()), -o2::constants::math::PI); + fillTruthPair(kBefore, qTrue, dEta); + fillTruthPairMap(kBefore, dEta, dPhi, qTrue); + + const auto& f1 = fate.at(mids[a]); + const auto& f2 = fate.at(mids[b]); + const int nLost = (f1.alive ? 0 : 1) + (f2.alive ? 0 : 1); + if (nLost == 0) { + fillTruthPair(kBothSurvive, qTrue, dEta); + fillTruthPairMap(kBothSurvive, dEta, dPhi, qTrue); + bool sameColl = false; + for (const auto& c1 : f1.aliveColls) { + sameColl = sameColl || (f2.aliveColls.count(c1) > 0); + } + if (sameColl) { + registry.fill(HIST("MCDedup/Pairs/bothSurviveSameColl/hQ"), qTrue); + } + continue; + } + { // the pair stored as the crossed pairing (A+B-, B+A-) in one collision or one crossed pair only + const auto itAB = storedCrossColl.find({mids[a], mids[b]}); + const auto itBA = storedCrossColl.find({mids[b], mids[a]}); + bool bothCrossed = false; + if (itAB != storedCrossColl.end() && itBA != storedCrossColl.end()) { + for (const auto& c : itAB->second) { + bothCrossed = bothCrossed || itBA->second.contains(c); + } + } + if (bothCrossed) { + registry.fill(HIST("MCDedup/Pairs/surviveAsCrossedPairing/hQ"), qTrue); + } else if (itAB != storedCrossColl.end() || itBA != storedCrossColl.end()) { + registry.fill(HIST("MCDedup/Pairs/halfCrossedStored/hQ"), qTrue); + } + } + if (nLost == 1) { + fillTruthPair(kOneLost, qTrue, dEta); + } else { + fillTruthPair(kBothLost, qTrue, dEta); + fillTruthPairMap(kBothLost, dEta, dPhi, qTrue); + } + + // Reason for photon building failure + bool byCross = false, byPartner = false, byOther = false, byTrue = false; + auto attribute = [&](int64_t victim, int64_t partner) { + if (fate.at(victim).alive) { + return; + } + const auto itB = blockersOfPhoton.find(victim); + if (itB == blockersOfPhoton.end()) { + return; + } + for (const int& w : itB->second) { + if (cls[w] == kV0CrossLegFake) { + byCross = true; + // partner fake = a cross-leg fake using a leg of the other photon + if (motherPos[w] == partner || motherEle[w] == partner) { + byPartner = true; + } + } else if (cls[w] == kV0OtherFake) { + byOther = true; + } else if (cls[w] == kV0True) { + byTrue = true; // real photon against real photon + } + } + }; + attribute(mids[a], mids[b]); + attribute(mids[b], mids[a]); + if (byCross) { + fillTruthPair(kLostByCrossFake, qTrue, dEta); + } + if (byPartner) { + fillTruthPair(kLostByPartnerFake, qTrue, dEta); + fillTruthPairMap(kLostByPartnerFake, dEta, dPhi, qTrue); + } + if (byOther) { + fillTruthPair(kLostByOtherFake, qTrue, dEta); + } + if (byTrue) { + fillTruthPair(kLostByTrue, qTrue, dEta); + } + } + } + } + } + + // MC, cfgExtendedDiag: class of the stored photons by source, stored photon pairs at low q, outcome of the resolver tests + template + void fillExtendedDiagnostics(TTracks const& tracks, TMCParticles const& mcparticles, DedupDiag const& diag) + { + struct Stored { + CandKey key; + int cls = 0; + int64_t mPos = -1; + int64_t mEle = -1; + std::array p{}; + }; + std::map candOfKey; + for (const auto& c : diag.snapshot) { + candOfKey[keyOf(c)] = &c; + } + std::map> storedOfColl; + for (const auto& key : diag.stored) { + const auto it = candOfKey.find(key); + if (it == candOfKey.end()) { + continue; + } + Stored s{key, 0, -1, -1, {it->second->pxAtPV, it->second->pyAtPV, it->second->pzAtPV}}; + s.cls = classifyV0Truth(tracks.rawIteratorAt(key.posId), tracks.rawIteratorAt(key.eleId), mcparticles, s.mPos, s.mEle); + const float pt = std::hypot(s.p[0], s.p[1]); + registry.fill(HIST("MCDedup/Stored/hClassSource"), static_cast(s.cls), key.v0Id < 0 ? 1.f : 0.f, pt); + storedOfColl[key.collisionId].push_back(s); + } + constexpr float MaxQ = 0.1f; + for (const auto& [col, photons] : storedOfColl) { + for (size_t i = 0; i < photons.size(); ++i) { + for (size_t j = i + 1; j < photons.size(); ++j) { + const auto& a = photons[i]; + const auto& b = photons[j]; + const float q = qInv(a.p[0], a.p[1], a.p[2], b.p[0], b.p[1], b.p[2]); + if (q > MaxQ) { + continue; + } + int cat = 2; + if (a.cls == kV0True && b.cls == kV0True && a.mPos != b.mPos) { + cat = 0; + } else if (a.cls == kV0CrossLegFake && b.cls == kV0CrossLegFake && a.mPos == b.mEle && a.mEle == b.mPos) { + cat = 1; + } + registry.fill(HIST("MCDedup/Stored/hPairClass"), static_cast(cat), q); + } + } + } + for (const auto& s : diag.swaps) { + const std::array, 4> legs = {{{s.keyI.posId, s.keyI.eleId}, {s.keyJ.posId, s.keyJ.eleId}, {s.keyI.posId, s.keyJ.eleId}, {s.keyJ.posId, s.keyI.eleId}}}; + std::array isTrue{}; + for (int k = 0; k < 4; ++k) { // o2-linter: disable=magic-number (four leg pairs) + int64_t mPos = -1, mEle = -1; + isTrue[k] = classifyV0Truth(tracks.rawIteratorAt(legs[k].first), tracks.rawIteratorAt(legs[k].second), mcparticles, mPos, mEle) == kV0True ? 1 : 0; + } + registry.fill(HIST("MCDedup/Swap/hCategory"), static_cast(3 * (isTrue[0] + isTrue[1]) + isTrue[2] + isTrue[3]), s.swapped ? 1.f : 0.f, s.q); + } + } + + void processRec(MyCollisions const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + { + build(collisions, v0s, tracks, bcs); + } + PROCESS_SWITCH(PhotonConversionBuilder, processRec, "process reconstructed info for data", true); + + // void processRec_SWT(MyCollisionsWithSWT const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + // { + // build(collisions, v0s, tracks, bcs); + // } + // PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT, "process reconstructed info for data", false); + + void processMC(MyCollisionsMC const& collisions, FilteredV0s const& v0s, MyTracksIUMC const& tracks, + MyBCs const& bcs, o2::aod::McParticles const& mcparticles) + { + DedupDiag diag; + build(collisions, v0s, tracks, bcs, &diag); + fillDedupTruthDiagnostics(collisions, tracks, mcparticles, diag); + if (dedup.cfgExtendedDiag) { + fillExtendedDiagnostics(tracks, mcparticles, diag); + } + } + PROCESS_SWITCH(PhotonConversionBuilder, processMC, "process reconstructed info for MC", false); + + void processRec_OnlyIfDielectron(o2::soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + { + build(collisions, v0s, tracks, bcs); + } + PROCESS_SWITCH(PhotonConversionBuilder, processRec_OnlyIfDielectron, "process reconstructed info for data", false); + + // void processRec_SWT_OnlyIfDielectron(soa::Join const& collisions, FilteredV0s const& v0s, MyTracksIU const& tracks, MyBCs const& bcs) + // { + // build(collisions, v0s, tracks, bcs); + // } + // PROCESS_SWITCH(PhotonConversionBuilder, processRec_SWT_OnlyIfDielectron, "process reconstructed info for data", false); +}; + +#endif // PWGEM_PHOTONMESON_TABLEPRODUCER_PHOTONCONVERSIONBUILDER_H_ diff --git a/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx new file mode 100644 index 00000000000..0346617358c --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/photonconversionbuilderTmpTable.cxx @@ -0,0 +1,27 @@ +// 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file photonconversionbuilder.ch +/// \brief this task produces photon data table with KFParticle. +/// \author Daiki Sekihata , Tokyo + +#include "PWGEM/PhotonMeson/TableProducer/photonconversionbuilder.h" + +#include +#include +#include + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return o2::framework::WorkflowSpec{ + adaptAnalysisTask>(context, o2::framework::TaskName{"photon-conversion-builder-tmptable"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx new file mode 100644 index 00000000000..4d5bcf13a00 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/skimmerDalitzEvents.cxx @@ -0,0 +1,168 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file skimmerDalitzEvents.cxx +/// \brief write tables for photons and electrons for dalitz decay +/// \author josuha.konig@cern.ch + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::common::core; + +using MyCollisions = soa::Join; +using MyElectrons = o2::aod::EMPrimaryElectronsFromDalitzTmp; +using MyV0Photons = o2::soa::Join; +using MyV0PhotonsLegs = o2::soa::Join; + +using MyElectronsMC = o2::soa::Join; +using MyV0PhotonsLegsMC = o2::soa::Join; + +struct skimmerDalitzEvents { + + SliceCache cache; + PresliceUnsorted perCollisionEl = aod::emprimaryelectron::collisionId; + PresliceUnsorted perCol_pcm = o2::aod::v0photonkf::collisionId; + PresliceUnsorted perCol_legs = o2::aod::v0leg::collisionId; + PresliceUnsorted perId_legs = o2::aod::v0leg::trackId; + + Produces v0photonskf; + Produces v0legs; + Produces v0legsXYZ; + Produces v0legsDeDxMC; + Produces v0photonsphivpsi; + + Produces emprimaryelectrons; + Produces emprimaryelectronsDeDxMC; + + Configurable minNElecCand{"minNElecCand", 2, "minimum number of electrons/positron candidates in one event"}; + Configurable minNGammaCand{"minNGammaCand", 1, "minimum number of V0 photon candidates in one event"}; + Configurable accPCMGGAndDalitz{"accPCMGGAndDalitz", false, "set to true if you want to create DD for both PCM-PCM and PCM-Dalitz"}; + + // ---------- for data ---------- + template + void coreProcess(MyCollisions const& collisions, TElectrons const& emPrimaryElecTmp, TV0Photons const& v0photonskfTmp, TV0Legs const& v0LegsTmp) + { + + auto leg = v0LegsTmp.begin(); + for (const auto& collision : collisions) { + // get electrons for current collision + auto tracks = emPrimaryElecTmp.sliceBy(perCollisionEl, collision.globalIndex()); + + // check that there are at least minNElecCand in the collision + if (!accPCMGGAndDalitz && tracks.size() < minNElecCand) { + continue; + } + + // get photons for current collision + auto photonskf = v0photonskfTmp.sliceBy(perCol_pcm, collision.globalIndex()); + + // check that there are at least minNGammaCand in the collision + if (!accPCMGGAndDalitz && photonskf.size() < minNGammaCand) { + continue; + } + + if (accPCMGGAndDalitz) { + if (!(photonskf.size() >= 2 || (tracks.size() >= 2 && photonskf.size() >= 1))) { // o2-linter: disable=magic-number (settings to obtain either two photons in the event or one photon with one dielectron) + continue; + } + } + + // Write electron tracks + for (const auto& track : tracks) { + emprimaryelectrons(track.collisionId(), track.trackId(), track.sign(), + track.pt(), track.eta(), track.phi(), + track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), + track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.beta(), track.tofNSigmaEl(), + track.itsClusterSizes(), track.itsChi2NCl(), track.tofChi2(), track.detectorMap()); + + if constexpr (isMC) { + emprimaryelectronsDeDxMC( + track.mcTunedTPCSignal()); + } + } + + for (const auto& v0 : photonskf) { + v0photonskf(v0.collisionId(), v0.v0Id(), v0legs.lastIndex() + 1, v0legs.lastIndex() + 2, // o2-linter: disable=magic-number (indexing for v0 leg table) + v0.vx(), v0.vy(), v0.vz(), + v0.px(), v0.py(), v0.pz(), + v0.mGamma(), + v0.dcaXYtopv(), v0.dcaZtopv(), + v0.cospa(), v0.cospaXY(), v0.cospaRZ(), v0.pca(), + v0.alpha(), v0.qtarm(), + v0.chiSquareNDF()); + + v0photonsphivpsi( + v0.phiv(), v0.psipair()); + + for (const auto& legId : {v0.posTrackId(), v0.negTrackId()}) { + if (legId < 0 || legId >= static_cast(v0LegsTmp.size())) { + LOG(warning) << "legID " << legId << " is outside of range of v0legs table with size " << static_cast(v0LegsTmp.size()); + continue; + } + leg.setCursor(legId); + v0legs( + leg.collisionId(), leg.trackId(), leg.sign(), + leg.px(), leg.py(), leg.pz(), + leg.dcaXY(), leg.dcaZ(), + leg.tpcNClsFindable(), leg.tpcNClsFindableMinusFound(), leg.tpcNClsFindableMinusCrossedRows(), leg.tpcNClsShared(), + leg.tpcChi2NCl(), leg.tpcInnerParam(), + leg.tpcSignal(), leg.tpcNSigmaEl(), leg.tpcNSigmaPi(), + leg.itsClusterSizes(), leg.itsChi2NCl(), leg.detectorMap()); + + v0legsXYZ( + leg.x(), leg.y(), leg.z()); + + if constexpr (isMC) { + v0legsDeDxMC( + leg.mcTunedTPCSignal()); + } + } + } + } + } + + void processRec(MyCollisions const& collisions, MyElectrons const& emPrimaryElecTmp, MyV0Photons const& v0photonskfTmp, MyV0PhotonsLegs const& v0LegsTmp) + { + coreProcess(collisions, emPrimaryElecTmp, v0photonskfTmp, v0LegsTmp); + } + + void processMC(MyCollisions const& collisions, MyElectronsMC const& emPrimaryElecTmp, MyV0Photons const& v0photonskfTmp, MyV0PhotonsLegsMC const& v0LegsTmp) + { + coreProcess(collisions, emPrimaryElecTmp, v0photonskfTmp, v0LegsTmp); + } + + PROCESS_SWITCH(skimmerDalitzEvents, processRec, "process reconstructed info only", false); // data + PROCESS_SWITCH(skimmerDalitzEvents, processMC, "process reconstructed and MC info", false); // MC +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask(context, TaskName{"skimmer-dalitz-events"})}; +} diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerGammaCalo.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerGammaCalo.cxx index e4f11543583..32d3d7c3407 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerGammaCalo.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerGammaCalo.cxx @@ -14,6 +14,7 @@ /// \author marvin.hemmer@cern.ch /// dependencies: emcal-correction-task +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/emcalHistoDefinitions.h" @@ -22,6 +23,7 @@ #include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/EventSelection.h" +#include #include #include #include @@ -39,6 +41,7 @@ #include #include #include +#include #include using namespace o2; @@ -53,7 +56,7 @@ struct SkimmerGammaCalo { Preslice psMSperCluster = o2::aod::emcalclustercell::emcalclusterId; Produces tableGammaEMCReco; - Produces tableEMCClusterMCLabels; + Produces tableEMCClusterMCLabels; Produces tableCellEMCReco; Produces tableEmEmcClusters; @@ -72,7 +75,7 @@ struct SkimmerGammaCalo { Configurable minE{"minE", 0.5, "Minimum energy for energy cut"}; Configurable maxE{"maxE", std::numeric_limits::max(), "Maximum energy for energy cut"}; Configurable removeExotic{"removeExotic", false, "Flag to enable the removal of exotic clusters."}; - Configurable> clusterDefinitions{"clusterDefinitions", {0, 1, 2, 10, 11, 12, 13, 20, 21, 22, 30, 40, 41, 42, 43, 44, 45}, "Cluster definitions to be accepted (e.g. 13 for kV3MostSplitLowSeed)"}; + Configurable> clusterDefinitions{"clusterDefinitions", {0, 1, 2, 10, 11, 12, 13, 20, 21, 22, 30, 40, 41, 42, 43, 44, 45}, "Cluster definitions storageID to be accepted (see O2Physics/PWGJE/DataModel/EMCALClusters.h for the storageID)"}; Configurable maxdEta{"maxdEta", 0.1, "Set a maximum difference in eta for tracks and cluster to still count as matched"}; Configurable maxdPhi{"maxdPhi", 0.1, "Set a maximum difference in phi for tracks and cluster to still count as matched"}; Configurable maxEoverP{"maxEoverP", 1.5, "Set a maximum for cluster E / track p for track matching."}; @@ -84,8 +87,14 @@ struct SkimmerGammaCalo { void init(o2::framework::InitContext&) { - historeg.add("DefinitionIn", "Cluster definitions before cuts;#bf{Cluster definition};#bf{#it{N}_{clusters}}", HistType::kTH1F, {{51, -0.5, 50.5}}); - historeg.add("DefinitionOut", "Cluster definitions after cuts;#bf{Cluster definition};#bf{#it{N}_{clusters}}", HistType::kTH1F, {{51, -0.5, 50.5}}); + if ((doprocessRec || doprocessRecWithSecondaries) && + (doprocessRecMC || doprocessRecMCWithSecondaries)) { + LOG(fatal) << "processRec(WithSecondaries) and processRecMC(WithSecondaries) fill the same " + << "cluster table and must not be enabled together — this doubles MinClusters rows " + << "relative to EMCClusterMCLabels_001."; + } + historeg.add("DefinitionIn", "Cluster definitions before cuts;#bf{Cluster definition};#bf{#it{N}_{clusters}}", HistType::kTH1F, {{53, -0.5, 52.5}}); + historeg.add("DefinitionOut", "Cluster definitions after cuts;#bf{Cluster definition};#bf{#it{N}_{clusters}}", HistType::kTH1F, {{53, -0.5, 52.5}}); historeg.add("EIn", "Energy of clusters before cuts", gHistoSpecClusterE); historeg.add("EOut", "Energy of clusters after cuts", gHistoSpecClusterE); historeg.add("MTEtaPhiBeforeTM", "Eta phi of matched tracks before TM cuts", gHistoSpecClusterTMdEtadPhi); @@ -131,6 +140,9 @@ struct SkimmerGammaCalo { template static constexpr bool HasSecondaries = !std::is_same_v; + template + static constexpr bool HasMcLabels = requires(TCluster c) { c.mcParticleIds(); }; + template void runAnalysis(TCollision const& collision, TClusters const& emcclusters, TClusterCells const& emcclustercells, TMatchedTracks const& emcmatchedtracks, TTracks const& /*tracks*/, TMatchedSecondaries const& secondaries = nullptr) { @@ -258,7 +270,17 @@ struct SkimmerGammaCalo { convertForStorage(emccluster.m02(), Observable::kM02), convertForStorage(emccluster.time(), Observable::kTime)); - if (vEta.size() > 0) { + if constexpr (HasMcLabels>) { + if (emccluster.mcParticleIds().size() != emccluster.amplitudeA().size()) { + LOG(warning) << "Mismatched MC label/amplitude array sizes for cluster " << emccluster.globalIndex() + << ": " << emccluster.mcParticleIds().size() << " vs " << emccluster.amplitudeA().size(); + } + std::vector mcLabels(emccluster.mcParticleIds().begin(), emccluster.mcParticleIds().end()); + std::vector amplitudes(emccluster.amplitudeA().begin(), emccluster.amplitudeA().end()); + tableEMCClusterMCLabels(mcLabels, amplitudes); + } + + if (!vEta.empty()) { for (size_t iPart = 0; iPart < vEta.size(); ++iPart) { tableEmEmcMTracks(tableEmEmcClusters.lastIndex(), vEta[iPart], vPhi[iPart], vP[iPart], vPt[iPart]); tableMinMTracks(tableMinClusters.lastIndex(), @@ -268,7 +290,7 @@ struct SkimmerGammaCalo { convertForStorage(vPt[iPart], Observable::kEnergy)); } } - if (vEtaSecondaries.size() > 0) { + if (!vEtaSecondaries.empty()) { for (size_t iPart = 0; iPart < vEtaSecondaries.size(); ++iPart) { tableEmEmcMSTracks(tableEmEmcClusters.lastIndex(), vEtaSecondaries[iPart], vPhiSecondaries[iPart], vPSecondaries[iPart], vPtSecondaries[iPart]); tableMinMSTracks(tableMinClusters.lastIndex(), @@ -290,59 +312,35 @@ struct SkimmerGammaCalo { } } - void processRec(soa::Join::iterator const& collision, aod::EMCALClusters const& emcclusters, aod::EMCALClusterCells const& emcclustercells, aod::EMCALMatchedTracks const& emcmatchedtracks, aod::FullTracks const& tracks) + void processRec(soa::Join::iterator const& collision, aod::EMCALClusters const& emcclusters, aod::EMCALClusterCells const& emcclustercells, aod::EMCALMatchedTracks const& emcmatchedtracks, aod::FullTracks const& tracks) { runAnalysis(collision, emcclusters, emcclustercells, emcmatchedtracks, tracks); } PROCESS_SWITCH(SkimmerGammaCalo, processRec, "process only reconstructed info", true); - void processRecWithSecondaries(soa::Join::iterator const& collision, aod::EMCALClusters const& emcclusters, aod::EMCALClusterCells const& emcclustercells, aod::EMCALMatchedTracks const& emcmatchedtracks, aod::FullTracks const& tracks, aod::EMCMatchSecs const& emcmatchedsecondaries) + void processRecWithSecondaries(soa::Join::iterator const& collision, aod::EMCALClusters const& emcclusters, aod::EMCALClusterCells const& emcclustercells, aod::EMCALMatchedTracks const& emcmatchedtracks, aod::FullTracks const& tracks, aod::EMCMatchSecs const& emcmatchedsecondaries) { runAnalysis(collision, emcclusters, emcclustercells, emcmatchedtracks, tracks, emcmatchedsecondaries); } PROCESS_SWITCH(SkimmerGammaCalo, processRecWithSecondaries, "process reconstructed info with secondary track matching.", false); - void processMC(soa::Join::iterator const& collision, soa::Join const& emcclusters, aod::McParticles const&) + void processRecMC(soa::Join::iterator const& collision, + soa::Join const& emcclusters, + aod::EMCALClusterCells const& emcclustercells, aod::EMCALMatchedTracks const& emcmatchedtracks, + aod::FullTracks const& tracks) { - if (!collision.isSelected()) { - return; - } - - if (needEMCTrigger.value && !collision.alias_bit(kTVXinEMC)) { - return; - } - - for (const auto& emccluster : emcclusters) { + runAnalysis(collision, emcclusters, emcclustercells, emcmatchedtracks, tracks); + } + PROCESS_SWITCH(SkimmerGammaCalo, processRecMC, "process reconstructed info + MC labels at once", false); - // Definition cut - if (!(std::find(clusterDefinitions.value.begin(), clusterDefinitions.value.end(), emccluster.definition()) != clusterDefinitions.value.end())) { - continue; - } - // Energy cut - if (emccluster.energy() < minE) { - continue; - } - // timing cut - if (emccluster.time() > maxTime || emccluster.time() < minTime) { - continue; - } - // M02 cut - if (emccluster.nCells() > 1 && (emccluster.m02() > maxM02 || emccluster.m02() < minM02)) { - continue; - } - std::vector mcLabels; - for (size_t iCont = 0; iCont < emccluster.amplitudeA().size(); iCont++) { - mcLabels.push_back(emccluster.mcParticleIds()[iCont]); - } - // LOGF(info, "---- New Cluster ---"); - // for (unsigned long int iCont = 0; iCont < mcLabels.size(); iCont++) { - // LOGF(info, "iCont = %d, mcParticle = %d, amplitudeA = %.5f", iCont, mcLabels.at(iCont), emccluster.amplitudeA()[iCont]); - // } - tableEMCClusterMCLabels(mcLabels); - mcLabels.clear(); - } + void processRecMCWithSecondaries(soa::Join::iterator const& collision, + soa::Join const& emcclusters, + aod::EMCALClusterCells const& emcclustercells, aod::EMCALMatchedTracks const& emcmatchedtracks, + aod::FullTracks const& tracks, aod::EMCMatchSecs const& emcmatchedsecondaries) + { + runAnalysis(collision, emcclusters, emcclustercells, emcmatchedtracks, tracks, emcmatchedsecondaries); } - PROCESS_SWITCH(SkimmerGammaCalo, processMC, "process MC info", false); // Run this in addition to processRec for MCs to copy the cluster mc labels from the EMCALMCClusters to the skimmed EMCClusterMCLabels table + PROCESS_SWITCH(SkimmerGammaCalo, processRecMCWithSecondaries, "process reconstructed info + MC labels at once with secondary track matching", false); void processDummy(aod::Collision const&) { @@ -351,8 +349,8 @@ struct SkimmerGammaCalo { PROCESS_SWITCH(SkimmerGammaCalo, processDummy, "Dummy function", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - WorkflowSpec workflow{adaptAnalysisTask(cfgc)}; + WorkflowSpec workflow{adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx index 5efa2817617..1efd61d1ab7 100644 --- a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.cxx @@ -9,636 +9,26 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \brief write relevant information about primary electrons. -/// \author daiki.sekihata@cern.ch +/// \file skimmerPrimaryElectronFromDalitzEE.cxx +/// \brief write electrons for Dalitz into new table +/// \author lgansbartl@stud.uni-frankfurt.de -#include "PWGEM/Dilepton/Utils/PairUtilities.h" -#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" +#include "PID/PIDTOFParamService.h" -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include - -#include -#include -#include -#include -#include -#include - -#include - -using namespace o2; -using namespace o2::soa; using namespace o2::framework; +using namespace o2::aod; using namespace o2::framework::expressions; -using namespace o2::constants::physics; - -using MyCollisions = soa::Join; -using MyCollisionsWithSWT = soa::Join; - -using MyCollisionsMC = soa::Join; -using MyTracks = soa::Join; -using MyTrack = MyTracks::iterator; -using MyTracksMC = soa::Join; -using MyTrackMC = MyTracksMC::iterator; - -struct skimmerPrimaryElectronFromDalitzEE { - SliceCache cache; - Preslice perCol = o2::aod::track::collisionId; - Preslice perCol_pcm = o2::aod::v0photonkf::collisionId; - Produces emprimaryelectrons; - Produces emprimaryelectronsDeDxMC; - - // Configurables - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; - - // Operation and minimisation criteria - Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; - Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; - Configurable mincrossedrows{"mincrossedrows", 70, "min. crossed rows"}; - Configurable min_tpc_cr_findable_ratio{"min_tpc_cr_findable_ratio", 0.8, "min. TPC Ncr/Nf ratio"}; - Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - Configurable min_ncluster_its{"min_ncluster_its", 4, "min ncluster its"}; - Configurable min_ncluster_itsib{"min_ncluster_itsib", 1, "min ncluster itsib"}; - Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max. chi2/NclsTPC"}; - Configurable maxchi2its{"maxchi2its", 36.0, "max. chi2/NclsITS"}; - Configurable minpt{"minpt", 0.05, "min pt for ITS-TPC track"}; - Configurable maxeta{"maxeta", 2.0, "max eta acceptance"}; - Configurable dca_xy_max{"dca_xy_max", 1, "max DCAxy in cm"}; - Configurable dca_z_max{"dca_z_max", 1, "max DCAz in cm"}; - Configurable dca_3d_sigma_max{"dca_3d_sigma_max", 2, "max DCA 3D in sigma"}; - Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.5, "min. TPC n sigma for electron inclusion"}; - Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", +3.5, "max. TPC n sigma for electron inclusion"}; - Configurable maxTPCNsigmaPi{"maxTPCNsigmaPi", 0.0, "max. TPC n sigma for pion exclusion"}; - Configurable minTPCNsigmaPi{"minTPCNsigmaPi", 0.0, "min. TPC n sigma for pion exclusion"}; - Configurable minTOFNsigmaEl{"minTOFNsigmaEl", -3.5, "min. TOF n sigma for electron inclusion"}; - Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", +3.5, "max. TOF n sigma for electron inclusion"}; - Configurable maxMee{"maxMee", 0.04, "max. mee to store dalitz ee pairs"}; - Configurable fillLS{"fillLS", true, "flag to fill LS histograms for QA"}; - Configurable includeITSsa{"includeITSsa", false, "Flag to include ITSsa tracks"}; - Configurable maxpt_itssa{"maxpt_itssa", 0.15, "max pt for ITSsa track"}; - Configurable maxMeanITSClusterSize{"maxMeanITSClusterSize", 16, "max x cos(lambda)"}; - Configurable slope{"slope", 0.0185, "slope for m vs. phiv"}; - Configurable intercept{"intercept", -0.0380, "intercept for m vs. phiv"}; - - HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view dileptonSigns[3] = {"uls/", "lspp/", "lsmm/"}; - - int mRunNumber; - float d_bz; - Service ccdb; - o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; - - void init(InitContext&) - { - if (doprocessRec && doprocessRec_SWT) { - LOGF(fatal, "Cannot enable doprocessRec and doprocessRec_SWT at the same time. Please choose one."); - } - - mRunNumber = 0; - d_bz = 0; - - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - - fRegistry.add("Track/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); - fRegistry.add("Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {400, -2.0f, 2.0f}}, false); - fRegistry.add("Track/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{400, -20, 20}}, false); - fRegistry.add("Track/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", kTH2F, {{1000, 0, 10}, {100, 0, 0.1}}, false); - fRegistry.add("Track/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -1.0f, 1.0f}, {200, -1.0f, 1.0f}}, false); - fRegistry.add("Track/hDCAxy_Pt", "DCA_{xy} vs. pT;p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); - fRegistry.add("Track/hDCAz_Pt", "DCA_{z} vs. pT;p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); - fRegistry.add("Track/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); - fRegistry.add("Track/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); - fRegistry.add("Track/hDCAzRes_Pt", "DCA_{z} resolution vs. pT;p_{T} (GeV/c);DCA_{z} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); - - // TPC - fRegistry.add("Track/hNclsTPC", "number of TPC clusters", kTH1F, {{161, -0.5, 160.5}}, false); - fRegistry.add("Track/hNcrTPC", "number of TPC crossed rows", kTH1F, {{161, -0.5, 160.5}}, false); - fRegistry.add("Track/hChi2TPC", "chi2/number of TPC clusters", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("Track/hTPCNcr2Nf", "TPC Ncr/Nfindable", kTH1F, {{200, 0, 2}}, false); - fRegistry.add("Track/hTPCNcls2Nf", "TPC Ncls/Nfindable", kTH1F, {{200, 0, 2}}, false); - fRegistry.add("Track/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); - fRegistry.add("Track/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); - fRegistry.add("Track/hTPCdEdxMC", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); - fRegistry.add("Track/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - fRegistry.add("Track/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - - // ITS - fRegistry.add("Track/hNclsITS", "number of ITS clusters", kTH1F, {{8, -0.5, 7.5}}, false); - fRegistry.add("Track/hChi2ITS", "chi2/number of ITS clusters", kTH1F, {{400, 0, 40}}, false); - fRegistry.add("Track/hITSClusterMap", "ITS cluster map", kTH1F, {{128, -0.5, 127.5}}, false); - fRegistry.add("Track/hMeanClusterSizeITS", "mean cluster size ITS;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); - fRegistry.add("Track/hMeanClusterSizeITSib", "mean cluster size ITSib;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); - fRegistry.add("Track/hMeanClusterSizeITSob", "mean cluster size ITSob;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); - - // TOF - fRegistry.add("Track/hChi2TOF", "chi2 of TOF", kTH1F, {{100, 0, 10}}, false); - fRegistry.add("Track/hTOFbeta", "TOF beta;p_{pv} (GeV/c);#beta", kTH2F, {{1000, 0, 10}, {240, 0, 1.2}}, false); - fRegistry.add("Track/hTOFNsigmaEl", "TOF n sigma el;p_{pv} (GeV/c);n #sigma_{e}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - fRegistry.add("Track/hTOFNsigmaPi", "TOF n sigma pi;p_{pv} (GeV/c);n #sigma_{#pi}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); - - // pair - fRegistry.add("Pair/uls/hMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); - fRegistry.add("Pair/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, M_PI}, {100, 0, 0.1}}, false); - fRegistry.addClone("Pair/uls/", "Pair/lspp/"); - fRegistry.addClone("Pair/uls/", "Pair/lsmm/"); - } - - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - - // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { - d_bz = d_bz_input; - o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); - } - o2::base::Propagator::initFieldFromGRP(&grpmag); - mRunNumber = bc.runNumber(); - return; - } - - auto run3grp_timestamp = bc.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - o2::base::Propagator::initFieldFromGRP(grpo); - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - o2::base::Propagator::initFieldFromGRP(grpmag); - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } - mRunNumber = bc.runNumber(); - } - - template - bool checkTrack(TCollision const&, TTrack const& track) - { - if constexpr (isMC) { - if (!track.has_mcParticle()) { - return false; - } - } - - if (!track.hasITS()) { - return false; - } - - if (track.itsChi2NCl() < 0.f || maxchi2its < track.itsChi2NCl()) { - return false; - } - - if (track.itsNCls() < min_ncluster_its) { - return false; - } - if (track.itsNClsInnerBarrel() < min_ncluster_itsib) { - return false; - } - - if (!includeITSsa && (!track.hasITS() || !track.hasTPC())) { - return false; - } - - if (track.hasTPC()) { - if (track.tpcChi2NCl() < 0.f || maxchi2tpc < track.tpcChi2NCl()) { - return false; - } - - if (track.tpcNClsFound() < min_ncluster_tpc) { - return false; - } - - if (track.tpcNClsCrossedRows() < mincrossedrows) { - return false; - } - - if (track.tpcCrossedRowsOverFindableCls() < min_tpc_cr_findable_ratio) { - return false; - } - - if (track.tpcFractionSharedCls() > max_frac_shared_clusters_tpc) { - return false; - } - } - - if (std::fabs(track.dcaXY()) > dca_xy_max || std::fabs(track.dcaZ()) > dca_z_max) { - return false; - } - - float dca_3d = 999.f; - float det = track.cYY() * track.cZZ() - track.cZY() * track.cZY(); - if (det < 0) { - dca_3d = 999.f; - } else { - float chi2 = (track.dcaXY() * track.dcaXY() * track.cZZ() + track.dcaZ() * track.dcaZ() * track.cYY() - 2. * track.dcaXY() * track.dcaZ() * track.cZY()) / det; - dca_3d = std::sqrt(std::fabs(chi2) / 2.); - } - if (dca_3d > dca_3d_sigma_max) { - return false; - } - - if (std::fabs(track.eta()) > maxeta) { - return false; - } - if ((track.hasITS() && track.hasTPC()) && track.pt() < minpt) { - return false; - } - if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { - return false; - } - - return true; - } - - template - bool isElectron(TTrack const& track) - { - if (includeITSsa && (track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF())) { - int total_cluster_size = 0, nl = 0; - for (unsigned int layer = 0; layer < 7; layer++) { - int cluster_size_per_layer = track.itsClsSizeInLayer(layer); - if (cluster_size_per_layer > 0) { - nl++; - } - total_cluster_size += cluster_size_per_layer; - } - - if (maxMeanITSClusterSize > static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(track.tgl()))) { - return true; - } else { - return false; - } - } - - if (track.tpcNSigmaEl() < minTPCNsigmaEl || maxTPCNsigmaEl < track.tpcNSigmaEl()) { - return false; - } - if (minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < maxTPCNsigmaPi) { - return false; - } - if (track.hasTOF() && (track.tofNSigmaEl() < minTOFNsigmaEl || maxTOFNsigmaEl < track.tofNSigmaEl())) { // TOFif - return false; - } - return true; - } - - template - void fillTrackTable(TCollision const& collision, TTrack const& track) - { - emprimaryelectrons(collision.globalIndex(), track.globalIndex(), track.sign(), - track.pt(), track.eta(), track.phi(), track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ(), - track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), - track.tpcChi2NCl(), track.tpcInnerParam(), - track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), - track.beta(), track.tofNSigmaEl(), - track.itsClusterSizes(), track.itsChi2NCl(), track.tofChi2(), track.detectorMap()); - if constexpr (isMC) { - emprimaryelectronsDeDxMC(track.mcTunedTPCSignal()); - } - } - - template - void fillTrackHistograms(TTrack const& track) - { - float mcTunedTPCSignal = 0.f; - if constexpr (isMC) { - mcTunedTPCSignal = track.mcTunedTPCSignal(); - } - - fRegistry.fill(HIST("Track/hPt"), track.pt()); - fRegistry.fill(HIST("Track/hEtaPhi"), track.phi(), track.eta()); - fRegistry.fill(HIST("Track/hQoverPt"), track.sign() / track.pt()); - fRegistry.fill(HIST("Track/hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); - fRegistry.fill(HIST("Track/hDCAxyz"), track.dcaXY(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxy_Pt"), track.pt(), track.dcaXY()); - fRegistry.fill(HIST("Track/hDCAz_Pt"), track.pt(), track.dcaZ()); - fRegistry.fill(HIST("Track/hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); - fRegistry.fill(HIST("Track/hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um - fRegistry.fill(HIST("Track/hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um - - fRegistry.fill(HIST("Track/hNclsTPC"), track.tpcNClsFound()); - fRegistry.fill(HIST("Track/hNcrTPC"), track.tpcNClsCrossedRows()); - fRegistry.fill(HIST("Track/hChi2TPC"), track.tpcChi2NCl()); - fRegistry.fill(HIST("Track/hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); - fRegistry.fill(HIST("Track/hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); - fRegistry.fill(HIST("Track/hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); - fRegistry.fill(HIST("Track/hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); - fRegistry.fill(HIST("Track/hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); - fRegistry.fill(HIST("Track/hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); - - fRegistry.fill(HIST("Track/hChi2TOF"), track.tofChi2()); - fRegistry.fill(HIST("Track/hTOFbeta"), track.p(), track.beta()); - fRegistry.fill(HIST("Track/hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); - fRegistry.fill(HIST("Track/hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); - - fRegistry.fill(HIST("Track/hNclsITS"), track.itsNCls()); - fRegistry.fill(HIST("Track/hChi2ITS"), track.itsChi2NCl()); - fRegistry.fill(HIST("Track/hITSClusterMap"), track.itsClusterMap()); - - int total_cluster_size = 0, nl = 0; - for (unsigned int layer = 0; layer < 7; layer++) { - int cluster_size_per_layer = track.itsClsSizeInLayer(layer); - if (cluster_size_per_layer > 0) { - nl++; - } - total_cluster_size += cluster_size_per_layer; - } - - int total_cluster_size_ib = 0, nl_ib = 0; - for (unsigned int layer = 0; layer < 3; layer++) { - int cluster_size_per_layer = track.itsClsSizeInLayer(layer); - if (cluster_size_per_layer > 0) { - nl_ib++; - } - total_cluster_size_ib += cluster_size_per_layer; - } - - int total_cluster_size_ob = 0, nl_ob = 0; - for (unsigned int layer = 3; layer < 7; layer++) { - int cluster_size_per_layer = track.itsClsSizeInLayer(layer); - if (cluster_size_per_layer > 0) { - nl_ob++; - } - total_cluster_size_ob += cluster_size_per_layer; - } - fRegistry.fill(HIST("Track/hMeanClusterSizeITS"), track.p(), static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSib"), track.p(), static_cast(total_cluster_size_ib) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); - fRegistry.fill(HIST("Track/hMeanClusterSizeITSob"), track.p(), static_cast(total_cluster_size_ob) / static_cast(nl_ob) * std::cos(std::atan(track.tgl()))); - } - - template - void fillPairInfo(TCollision const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) - { - if constexpr (pairtype == 0) { // ULS - for (const auto& [t1, t2] : combinations(CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { - continue; - } - if (!isElectron(t1) || !isElectron(t2)) { - continue; - } - - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); - fRegistry.fill(HIST("Pair/") + HIST(dileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); - fRegistry.fill(HIST("Pair/") + HIST(dileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); - - if (v12.M() > maxMee) { // don't store - continue; - } - - if (v12.M() < slope * phiv + intercept) { - continue; - } - - if (t1.sign() > 0) { // for positron - if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t1.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { - fillTrackHistograms(t1); - acceptedPosTrackIds_per_collision.emplace_back(t1.globalIndex()); - } - } else { // for electron - if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t1.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { - fillTrackHistograms(t1); - acceptedNegTrackIds_per_collision.emplace_back(t1.globalIndex()); - } - } - - if (t2.sign() > 0) { // for positron - if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t2.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { - fillTrackHistograms(t2); - acceptedPosTrackIds_per_collision.emplace_back(t2.globalIndex()); - } - } else { // for electron - if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t2.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { - fillTrackHistograms(t2); - acceptedNegTrackIds_per_collision.emplace_back(t2.globalIndex()); - } - } - } // end of ULS pairing - } else { // LS - for (auto& [t1, t2] : combinations(CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { - if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { - continue; - } - if (!isElectron(t1) || !isElectron(t2)) { - continue; - } - - ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); - ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), d_bz); - fRegistry.fill(HIST("Pair/") + HIST(dileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); - fRegistry.fill(HIST("Pair/") + HIST(dileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); - } // end of LS pairing - } - } - - std::vector acceptedPosTrackIds_per_collision; - std::vector acceptedNegTrackIds_per_collision; - std::vector> stored_trackIds; - Filter trackFilter = minpt < o2::aod::track::pt && nabs(o2::aod::track::eta) < maxeta && o2::aod::track::itsChi2NCl < maxchi2its && ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) == true && nabs(o2::aod::track::dcaXY) < dca_xy_max && nabs(o2::aod::track::dcaZ) < dca_z_max; - using MyFilteredTracks = soa::Filtered; - Partition posTracks = o2::aod::track::signed1Pt > 0.f; - Partition negTracks = o2::aod::track::signed1Pt < 0.f; - - // ---------- for data ---------- - void processRec(MyCollisions const& collisions, aod::BCsWithTimestamps const&, MyFilteredTracks const& tracks, aod::V0PhotonsKF const& v0photons) - { - stored_trackIds.reserve(tracks.size()); - - for (const auto& collision : collisions) { - auto bc = collision.template foundBC_as(); - initCCDB(bc); - if (!collision.isSelected()) { - continue; - } - - const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); - const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); - acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); - - fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - if (fillLS) { - fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - } - - if ((v0photons_per_coll.size() >= 1 && acceptedPosTrackIds_per_collision.size() >= 1 && acceptedNegTrackIds_per_collision.size() >= 1) || (acceptedPosTrackIds_per_collision.size() >= 2 && acceptedNegTrackIds_per_collision.size() >= 2)) { - // LOGF(info, "v0photons_per_coll.size() = %d, acceptedPosTrackIds_per_collision.size() = %d, acceptedNegTrackIds_per_collision.size() = %d", v0photons_per_coll.size(), acceptedPosTrackIds_per_collision.size(), acceptedNegTrackIds_per_collision.size()); - for (const auto& posId : acceptedPosTrackIds_per_collision) { - const auto& pos = tracks.rawIteratorAt(posId); - fillTrackTable(collision, pos); - } - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - const auto& ele = tracks.rawIteratorAt(eleId); - fillTrackTable(collision, ele); - } - } - - acceptedPosTrackIds_per_collision.clear(); - acceptedPosTrackIds_per_collision.shrink_to_fit(); - acceptedNegTrackIds_per_collision.clear(); - acceptedNegTrackIds_per_collision.shrink_to_fit(); - } // end of collision loop - - stored_trackIds.clear(); - stored_trackIds.shrink_to_fit(); - } - PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec, "process reconstructed info only", true); // standalone - - void processRec_SWT(MyCollisionsWithSWT const& collisions, aod::BCsWithTimestamps const&, MyFilteredTracks const& tracks, aod::V0PhotonsKF const& v0photons) - { - stored_trackIds.reserve(tracks.size()); - - for (const auto& collision : collisions) { - auto bc = collision.template foundBC_as(); - initCCDB(bc); - if (!collision.isSelected()) { - continue; - } - - if (collision.swtaliastmp_raw() == 0) { - continue; - } - - const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); - const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); - acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); - - fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - if (fillLS) { - fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - } - - if ((v0photons_per_coll.size() >= 1 && acceptedPosTrackIds_per_collision.size() >= 1 && acceptedNegTrackIds_per_collision.size() >= 1) || (acceptedPosTrackIds_per_collision.size() >= 2 && acceptedNegTrackIds_per_collision.size() >= 2)) { - // LOGF(info, "v0photons_per_coll.size() = %d, acceptedPosTrackIds_per_collision.size() = %d, acceptedNegTrackIds_per_collision.size() = %d", v0photons_per_coll.size(), acceptedPosTrackIds_per_collision.size(), acceptedNegTrackIds_per_collision.size()); - for (const auto& posId : acceptedPosTrackIds_per_collision) { - const auto& pos = tracks.rawIteratorAt(posId); - fillTrackTable(collision, pos); - } - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - const auto& ele = tracks.rawIteratorAt(eleId); - fillTrackTable(collision, ele); - } - } - - acceptedPosTrackIds_per_collision.clear(); - acceptedPosTrackIds_per_collision.shrink_to_fit(); - acceptedNegTrackIds_per_collision.clear(); - acceptedNegTrackIds_per_collision.shrink_to_fit(); - } // end of collision loop - - stored_trackIds.clear(); - stored_trackIds.shrink_to_fit(); - } - PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec_SWT, "process reconstructed info with CEFP", false); // with cefp - - using MyFilteredTracksMC = soa::Filtered; - Partition posTracksMC = o2::aod::track::signed1Pt > 0.f; - Partition negTracksMC = o2::aod::track::signed1Pt < 0.f; - // ---------- for MC ---------- - void processMC(MyCollisionsMC const& collisions, aod::McCollisions const&, aod::BCsWithTimestamps const&, MyFilteredTracksMC const& tracks, aod::V0PhotonsKF const& v0photons) - { - stored_trackIds.reserve(tracks.size()); - - for (const auto& collision : collisions) { - auto bc = collision.template foundBC_as(); - initCCDB(bc); - if (!collision.has_mcCollision()) { - continue; - } - if (!collision.isSelected()) { - continue; - } - - const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); - const auto& posTracks_per_coll = posTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - const auto& negTracks_per_coll = negTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); - acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); - acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); - - fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS - if (fillLS) { - fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ - fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- - } - if ((v0photons_per_coll.size() >= 1 && acceptedPosTrackIds_per_collision.size() >= 1 && acceptedNegTrackIds_per_collision.size() >= 1) || (acceptedPosTrackIds_per_collision.size() >= 2 && acceptedNegTrackIds_per_collision.size() >= 2)) { - // LOGF(info, "v0photons_per_coll.size() = %d, acceptedPosTrackIds_per_collision.size() = %d, acceptedNegTrackIds_per_collision.size() = %d", v0photons_per_coll.size(), acceptedPosTrackIds_per_collision.size(), acceptedNegTrackIds_per_collision.size()); - for (const auto& posId : acceptedPosTrackIds_per_collision) { - const auto& pos = tracks.rawIteratorAt(posId); - fillTrackTable(collision, pos); - } - for (const auto& eleId : acceptedNegTrackIds_per_collision) { - const auto& ele = tracks.rawIteratorAt(eleId); - fillTrackTable(collision, ele); - } - } - - acceptedPosTrackIds_per_collision.clear(); - acceptedPosTrackIds_per_collision.shrink_to_fit(); - acceptedNegTrackIds_per_collision.clear(); - acceptedNegTrackIds_per_collision.shrink_to_fit(); - } // end of collision loop - - stored_trackIds.clear(); - stored_trackIds.shrink_to_fit(); - } - PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processMC, "process reconstructed and MC info ", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"skimmer-primary-electron-from-dalitzee"})}; + o2::pid::tof::TOFResponseImpl::metadataInfo.initMetadata(context); + + return WorkflowSpec{ + adaptAnalysisTask>(context, TaskName{"skimmer-primary-electron-from-dalitzee"})}; } diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h new file mode 100644 index 00000000000..148f1667dd5 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h @@ -0,0 +1,1134 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file skimmerPrimaryElectronFromDalitzEE.h +/// \brief write relevant information about primary electrons. +/// \author daiki.sekihata@cern.ch + +#ifndef PWGEM_PHOTONMESON_TABLEPRODUCER_SKIMMERPRIMARYELECTRONFROMDALITZEE_H_ +#define PWGEM_PHOTONMESON_TABLEPRODUCER_SKIMMERPRIMARYELECTRONFROMDALITZEE_H_ + +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/Core/PID/PIDTOFParamService.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// NOLINTBEGIN(google-global-names-in-headers) +using o2::framework::AxisSpec; +using o2::framework::Configurable; +using o2::framework::ConfigurableGroup; +using o2::framework::HistType; +using o2::framework::kTH1D; +using o2::framework::kTH1F; +using o2::framework::kTH2F; +using o2::framework::kTHnSparseF; +using o2::framework::Preslice; +using o2::framework::Produces; +// NOLINTEND(google-global-names-in-headers) + +using MyCollisions = o2::soa::Join; +using MyCollisionsWithSWT = o2::soa::Join; +using MyBCs = o2::soa::Join; + +using MyCollisionsMC = o2::soa::Join; +using MyTracks = o2::soa::Join; +using MyTrack = MyTracks::iterator; +using MyTracksMC = o2::soa::Join; +using MyTrackMC = MyTracksMC::iterator; + +namespace o2::aod +{ +namespace pwgem::pm::recalculatedtofpid +{ +DECLARE_SOA_COLUMN(BetaRecalculated, betaRecalculated, float); +DECLARE_SOA_COLUMN(TOFNSigmaElRecalculated, tofNSigmaElRecalculated, float); +} // namespace pwgem::pm::recalculatedtofpid + +DECLARE_SOA_TABLE(EMTOFNSigmas, "AOD", "EMTOFNSIGMA", // make std::map in your tasks later. // Don't store this table in the derived data. + o2::aod::emprimaryelectron::CollisionId, o2::aod::emprimaryelectron::TrackId, + o2::aod::pwgem::pm::recalculatedtofpid::BetaRecalculated, o2::aod::pwgem::pm::recalculatedtofpid::TOFNSigmaElRecalculated); + +using EMTOFNSigma = EMTOFNSigmas::iterator; +} // namespace o2::aod + +template +struct skimmerPrimaryElectronFromDalitzEE { + + o2::framework::SliceCache cache; + Preslice perCol = o2::aod::track::collisionId; + o2::framework::PresliceOptional perTracksCollision = o2::aod::track::collisionId; + Preslice perCol_pcm = o2::aod::v0photonkf::collisionId; // o2::aod::V0PhotonsKF + Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + Produces emprimaryelectrons; + Produces emprimaryelectronsDeDxMC; + o2::framework::Service mTOFResponse{}; + + Produces emtofs; + + enum class enumFillingMode { + SingleTrack = 1, + EpEmPairs = 2, + EpEmPairsAndPhoton = 3 + }; + + enum RejectReason { + kAllTracks = 0, + kPassed, + kNoMcParticle, + kNotTrueElectron, + kTOFRequired, + kNoITS, + kITSChi2, + kITSNCls, + kITSNClsInnerBarrel, + kNoTPC, + kTPCChi2, + kTPCNClsFound, + kTPCCrossedRows, + kTPCCrossedRowsOverFindable, + kTPCSharedFraction, + kPropagation, + kDCAxyz, + kDCA3D, + kPtEta, + kITSClusterSize, + kITSsaPt, + kNotElectron, + kNRejectReasons + }; + static constexpr std::array RejectReasonLabels = {"all tracks", "accepted tracks", "no MC particle", "not true electron", "TOF required", "no ITS", "ITS chi2", "ITS Ncls", "ITS Ncls IB", "no TPC", "TPC chi2", "TPC Ncls found", "TPC crossed rows", "TPC Ncr/Nf", "TPC shared frac", "propagation", "DCAxy/z", "DCA 3D", "pt/eta", "ITS cluster size", "ITSsa pt", "not electron"}; + + // Configurables + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; + + // Operation and minimisation criteria + Configurable dBzInput{"dBzInput", -999, "bz field in kG, -999 is automatic"}; + Configurable min_ncluster_tpc{"min_ncluster_tpc", 0, "min ncluster tpc"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable mincrossedrows{"mincrossedrows", 70, "min. crossed rows"}; + Configurable min_tpc_cr_findable_ratio{"min_tpc_cr_findable_ratio", 0.8, "min. TPC Ncr/Nf ratio"}; + Configurable max_frac_shared_clusters_tpc{"max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; + Configurable min_ncluster_its{"min_ncluster_its", 4, "min ncluster its"}; + Configurable min_ncluster_itsib{"min_ncluster_itsib", 1, "min ncluster itsib"}; + Configurable minchi2tpc{"minchi2tpc", 0.0, "min. chi2/NclsTPC"}; + Configurable maxchi2tpc{"maxchi2tpc", 5.0, "max. chi2/NclsTPC"}; + Configurable minchi2its{"minchi2its", 0.0, "min. chi2/NclsITS"}; + Configurable maxchi2its{"maxchi2its", 36.0, "max. chi2/NclsITS"}; + Configurable minpt{"minpt", 0.05, "min pt for ITS-TPC track"}; + Configurable maxeta{"maxeta", 2.0, "max eta acceptance"}; + Configurable dca_xy_max{"dca_xy_max", 1, "max DCAxy in cm"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable dca_z_max{"dca_z_max", 1, "max DCAz in cm"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable dca_3d_sigma_max{"dca_3d_sigma_max", 2, "max DCA 3D in sigma"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable minTPCNsigmaEl{"minTPCNsigmaEl", -2.5, "min. TPC n sigma for electron inclusion"}; + Configurable maxTPCNsigmaEl{"maxTPCNsigmaEl", +3.5, "max. TPC n sigma for electron inclusion"}; + Configurable maxTPCNsigmaPi{"maxTPCNsigmaPi", 0.0, "max. TPC n sigma for pion exclusion"}; + Configurable minTPCNsigmaPi{"minTPCNsigmaPi", 0.0, "min. TPC n sigma for pion exclusion"}; + Configurable minTOFNsigmaEl{"minTOFNsigmaEl", -3.5, "min. TOF n sigma for electron inclusion"}; + Configurable maxTOFNsigmaEl{"maxTOFNsigmaEl", +3.5, "max. TOF n sigma for electron inclusion"}; + Configurable minTPCNsigmaKa{"minTPCNsigmaKa", -2.5, "min. TPC n sigma for kaon exclusion"}; + Configurable maxTPCNsigmaKa{"maxTPCNsigmaKa", 2.5, "max. TPC n sigma for kaon exclusion"}; + Configurable minTPCNsigmaPr{"minTPCNsigmaPr", -2.5, "min. TPC n sigma for proton exclusion"}; + Configurable maxTPCNsigmaPr{"maxTPCNsigmaPr", 2.5, "max. TPC n sigma for proton exclusion"}; + Configurable requireTOF{"requireTOF", false, "require TOF hit"}; + Configurable min_pin_for_pion_rejection{"min_pin_for_pion_rejection", 0.0, "pion rejection is applied above this pin"}; // this is used only in TOFreq + Configurable max_pin_for_pion_rejection{"max_pin_for_pion_rejection", 0.5, "pion rejection is applied below this pin"}; + Configurable minMee{"minMee", 0., "min. mee to store dalitz ee pairs"}; + Configurable maxMee{"maxMee", 0.5, "max. mee to store dalitz ee pairs"}; + Configurable minMeegamma{"minMeegamma", 0.3, "min. mee to store eegamma candidates"}; + Configurable maxMeegamma{"maxMeegamma", 0.8, "max. mee to store eegamma candidates"}; + Configurable photonsNeeded{"photonsNeeded", true, "require a minimum of one photons per collision"}; + Configurable fillLS{"fillLS", true, "flag to fill LS histograms for QA"}; + Configurable fillingMode{"fillingMode", 1, "Filling mode| 1: fill tracks without pair selection, 2: fill tracks from selected pairs, 3: fill tracks from selected pairs that can be combined with a photon"}; + Configurable fillWithEtaMassCut{"fillWithEtaMassCut", true, "only valid for fillingmode 3; true: filling tabled based on eta candidate selection with minMeegamma < M < maxMeegamma, false: fill identical to fillingmode 2"}; + Configurable includeITSsa{"includeITSsa", false, "Flag to include ITSsa tracks"}; + Configurable maxpt_itssa{"maxpt_itssa", 0.15, "max pt for ITSsa track"}; // o2-linter: disable=name/function-variable (renaming configs would mess up hyperloop) + Configurable maxMeanITSClusterSize{"maxMeanITSClusterSize", 16, "max x cos(lambda)"}; + Configurable slope{"slope", 0.0185, "slope for m vs. phiv"}; + Configurable intercept{"intercept", -0.0380, "intercept for m vs. phiv"}; + Configurable useTOFNSigmaDeltaBC{"useTOFNSigmaDeltaBC", false, "Flag to shift delta BC for TOF n sigma (only with TTCA)"}; + Configurable storeOnlyTrueElectronMC{"storeOnlyTrueElectronMC", false, "Flag to store only true electron in MC"}; + Configurable fillHistogramsBeforeSelection{"fillHistogramsBeforeSelection", false, "Flag to fill track histograms before track selections (only for fillingMode 1)"}; + + o2::framework::HistogramRegistry fRegistry{"output", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject, false, false}; + static constexpr std::array DileptonSigns = {"uls/", "lspp/", "lsmm/"}; + static constexpr std::array TrackDirs = {"Track/", "TrackTrueElec/", "TrackNoSel/", "TrackNoSelTrueElec/"}; + + int mRunNumber = 0; + float dBz = 0.; + o2::framework::Service ccdb{}; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + o2::dataformats::VertexBase mVtx; + + void init(o2::framework::InitContext& initContext) + { + // if (doprocessRec && doprocessRec_SWT) { + // LOGF(fatal, "Cannot enable doprocessRec and doprocessRec_SWT at the same time. Please choose one."); + // } + + mRunNumber = 0; + dBz = 0; + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + LOGF(info, "before TOF initSetup"); + mTOFResponse->initSetup(ccdb, initContext); + LOGF(info, "after TOF initSetup"); + + fRegistry.add("Track/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, o2::constants::math::TwoPI}, {400, -2.0f, 2.0f}}, false); + fRegistry.add("Track/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{400, -20, 20}}, false); + fRegistry.add("Track/hRelDeltaPt", "pT resolution;p_{T} (GeV/c);#Deltap_{T}/p_{T}", kTH2F, {{1000, 0, 10}, {100, 0, 0.1}}, false); + fRegistry.add("Track/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -1.0f, 1.0f}, {200, -1.0f, 1.0f}}, false); + fRegistry.add("Track/hDCAxy_Pt", "DCA_{xy} vs. pT;p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); + fRegistry.add("Track/hDCAz_Pt", "DCA_{z} vs. pT;p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {{200, 0, 10}, {200, -1, 1}}, false); + fRegistry.add("Track/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); + fRegistry.add("Track/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); + fRegistry.add("Track/hDCAzRes_Pt", "DCA_{z} resolution vs. pT;p_{T} (GeV/c);DCA_{z} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); + + // TPC + fRegistry.add("Track/hNclsTPC", "number of TPC clusters", kTH1F, {{161, -0.5, 160.5}}, false); + fRegistry.add("Track/hNcrTPC", "number of TPC crossed rows", kTH1F, {{161, -0.5, 160.5}}, false); + fRegistry.add("Track/hChi2TPC", "chi2/number of TPC clusters", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Track/hTPCNcr2Nf", "TPC Ncr/Nfindable", kTH1F, {{200, 0, 2}}, false); + fRegistry.add("Track/hTPCNcls2Nf", "TPC Ncls/Nfindable", kTH1F, {{200, 0, 2}}, false); + fRegistry.add("Track/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); + fRegistry.add("Track/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + fRegistry.add("Track/hTPCdEdxMC", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + fRegistry.add("Track/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("Track/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + + // ITS + fRegistry.add("Track/hNclsITS", "number of ITS clusters", kTH1F, {{8, -0.5, 7.5}}, false); + fRegistry.add("Track/hChi2ITS", "chi2/number of ITS clusters", kTH1F, {{400, 0, 40}}, false); + fRegistry.add("Track/hITSClusterMap", "ITS cluster map", kTH1F, {{128, -0.5, 127.5}}, false); + fRegistry.add("Track/hMeanClusterSizeITS", "mean cluster size ITS;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + fRegistry.add("Track/hMeanClusterSizeITSib", "mean cluster size ITSib;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + fRegistry.add("Track/hMeanClusterSizeITSob", "mean cluster size ITSob;p_{pv} (GeV/c); #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {150, 0, 15}}, false); + + // TOF + fRegistry.add("Track/hChi2TOF", "chi2 of TOF", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("Track/hTOFbeta", "TOF beta;p_{pv} (GeV/c);#beta", kTH2F, {{1000, 0, 10}, {240, 0, 1.2}}, false); + fRegistry.add("Track/hTOFNsigmaEl", "TOF n sigma el;p_{pv} (GeV/c);n #sigma_{e}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("Track/hTOFNsigmaPi", "TOF n sigma pi;p_{pv} (GeV/c);n #sigma_{#pi}^{TOF}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + + fRegistry.add("Track/hTrackRejectReason", "track rejection reason vs. p_{T};;p_{T} (GeV/c)", kTH2F, {{static_cast(kNRejectReasons), -0.5, static_cast(kNRejectReasons) - 0.5}, {200, 0, 20}}, false); + auto hRejectReason = fRegistry.get(HIST("Track/hTrackRejectReason")); + for (int i = 0; i < kNRejectReasons; i++) { + hRejectReason->GetXaxis()->SetBinLabel(i + 1, RejectReasonLabels[i]); + } + + if (doprocessMC) { + LOG(info) << "adding TrackTrueElec folder"; + fRegistry.addClone("Track/", "TrackTrueElec/"); + } + + if (fillHistogramsBeforeSelection) { + fRegistry.addClone("Track/", "TrackNoSel/"); + if (doprocessMC) { + fRegistry.addClone("Track/", "TrackNoSelTrueElec/"); + } + } + + // pair + fRegistry.add("Pair/uls/hTrackMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hCheckEMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hMCutMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + fRegistry.add("Pair/uls/hMPhiCutMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {{100, 0, 0.1}, {200, 0, 2}}, false); + + fRegistry.add("Pair/uls/hTrackMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hCheckEMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hMCutMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + fRegistry.add("Pair/uls/hMPhiCutMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi_{V} (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{180, 0, o2::constants::math::PI}, {100, 0, 0.1}}, false); + + fRegistry.addClone("Pair/uls/", "Pair/lspp/"); + fRegistry.addClone("Pair/uls/", "Pair/lsmm/"); + } + + void initCCDB(MyBCs::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + // In case override, don't proceed, please - no CCDB access required + if (dBzInput > -990) { // o2-linter: disable=magic-number (check against some default number) + dBz = dBzInput; + o2::parameters::GRPMagField grpmag; + if (std::fabs(dBz) > 1e-5) { // o2-linter: disable=magic-number (check against some default number) + grpmag.setL3Current(30000.f / (dBz / 5.0f)); // o2-linter: disable=magic-number (override value) + } + o2::base::Propagator::initFieldFromGRP(&grpmag); + return; + } + + o2::base::Propagator::initFieldFromGRP(&bc.grpMagField()); + + auto run3grpTimestamp = bc.timestamp(); + + dBz = bc.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; + } + + template + void calculateTOFNSigmaWithReassociation(TCollisions const& collisions, TBCs const&, TTracks const& tracks, TTrackAssoc const& trackIndices) + { + if (useTOFNSigmaDeltaBC) { + if constexpr (withTTCA) { + for (const auto& collision : collisions) { + if (mapCollisionTime.find(collision.globalIndex()) == mapCollisionTime.end()) { + continue; + } + auto bcCollision = collision.template bc_as(); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + + if (track.hasTOF() && track.has_collision()) { // TTCA may use orphan tracks. + auto bcTrack = track.template collision_as().template bc_as(); + float tofNSigmaEl = mTOFResponse->nSigma(track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()), track.tofExpMom(), track.length(), track.p(), track.eta(), mapCollisionTime[collision.globalIndex()], mapCollisionTimeError[collision.globalIndex()]); + float beta = track.length() / (track.tofSignalInAnotherBC(bcTrack.globalBC(), bcCollision.globalBC()) - mapCollisionTime[collision.globalIndex()]) / (o2::constants::physics::LightSpeedCm2S * 1e-12); + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = tofNSigmaEl; + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = beta; + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } else { + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } + } // end of track loop + } // end of collision loop + } else { + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } + } // end of track loop + } // end of collision loop + } else { + if constexpr (withTTCA) { + for (const auto& collision : collisions) { + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + for (const auto& trackId : trackIdsThisCollision) { + auto track = trackId.template track_as(); + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } // end of track loop + } // end of collision loop + } else { + for (const auto& collision : collisions) { + auto tracks_per_coll = tracks.sliceBy(perCol, collision.globalIndex()); + for (const auto& track : tracks_per_coll) { + if (!track.hasITS() || !track.hasTPC()) { // apply only minimal cut + continue; + } + mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.tofNSigmaEl(); + mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())] = track.beta(); + emtofs(collision.globalIndex(), track.globalIndex(), mapTOFBetaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())], mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]); + } + } // end of track loop + } // end of collision loop + } + } + + template + bool checkTrack(TCollision const& collision, TTrack const& track, int* rejectReason = nullptr) + { + // Lambda function: Sets the reject reason to the correct value and returns false. + // Used for every return statement in the checkTrack function where false is returned + auto reject = [rejectReason](RejectReason reason) { + if (rejectReason != nullptr) { + *rejectReason = reason; + } + return false; + }; + + if constexpr (isMC) { + if (!track.has_mcParticle()) { + return reject(kNoMcParticle); + } + if (storeOnlyTrueElectronMC) { + const auto& mcParticle = track.template mcParticle_as(); + if (std::abs(mcParticle.pdgCode()) != PDG_t::kElectron) { + return reject(kNotTrueElectron); + } + } + } + + float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + if (requireTOF && !(track.hasTOF() && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl)) { + return reject(kTOFRequired); + } + + if (!track.hasITS()) { + return reject(kNoITS); + } + + if (track.itsChi2NCl() < minchi2its || maxchi2its < track.itsChi2NCl()) { + return reject(kITSChi2); + } + + if (track.itsNCls() < min_ncluster_its) { + return reject(kITSNCls); + } + if (track.itsNClsInnerBarrel() < min_ncluster_itsib) { + return reject(kITSNClsInnerBarrel); + } + + if (!includeITSsa && !track.hasTPC()) { + return reject(kNoTPC); + } + + if (track.hasTPC()) { + if (track.tpcChi2NCl() < minchi2tpc || maxchi2tpc < track.tpcChi2NCl()) { + return reject(kTPCChi2); + } + + if (track.tpcNClsFound() < min_ncluster_tpc) { + return reject(kTPCNClsFound); + } + + if (track.tpcNClsCrossedRows() < mincrossedrows) { + return reject(kTPCCrossedRows); + } + + if (track.tpcCrossedRowsOverFindableCls() < min_tpc_cr_findable_ratio) { + return reject(kTPCCrossedRowsOverFindable); + } + + if (track.tpcFractionSharedCls() > max_frac_shared_clusters_tpc) { + return reject(kTPCSharedFraction); + } + } + + o2::dataformats::DCA mDcaInfoCov; // for Track association + mDcaInfoCov.set(999, 999, 999, 999, 999); + auto trackParCov = getTrackParCov(track); + trackParCov.setPID(o2::track::PID::Electron); + mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); + mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + bool isPropOK = o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, trackParCov, 2.f, matCorr, &mDcaInfoCov); + if (!isPropOK) { + return reject(kPropagation); + } + float dcaXY = mDcaInfoCov.getY(); + float dcaZ = mDcaInfoCov.getZ(); + + if (std::fabs(dcaXY) > dca_xy_max || std::fabs(dcaZ) > dca_z_max) { + return reject(kDCAxyz); + } + + float dca3D = 999.f; + float det = trackParCov.getSigmaY2() * trackParCov.getSigmaZ2() - trackParCov.getSigmaZY() * trackParCov.getSigmaZY(); + if (det < 0) { + dca3D = 999.f; + } else { + float chi2 = (dcaXY * dcaXY * trackParCov.getSigmaZ2() + dcaZ * dcaZ * trackParCov.getSigmaY2() - 2. * dcaXY * dcaZ * trackParCov.getSigmaZY()) / det; + dca3D = std::sqrt(std::fabs(chi2) / 2.); + } + if (dca3D > dca_3d_sigma_max) { + return reject(kDCA3D); + } + + if (trackParCov.getPt() < minpt || std::fabs(trackParCov.getEta()) > maxeta) { + return reject(kPtEta); + } + + int total_cluster_size = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl++; + } + total_cluster_size += cluster_size_per_layer; + } + + if (maxMeanITSClusterSize < static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(trackParCov.getTgl()))) { + return reject(kITSClusterSize); + } + + if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { + return reject(kITSsaPt); + } + + // if ((track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) && maxpt_itssa < track.pt()) { + // return false; + // } + + return true; + } + + template + bool isElectron(TCollision const& collision, TTrack const& track) + { + if (includeITSsa && (track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF())) [[unlikely]] { + int total_cluster_size = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + int cluster_size_per_layer = track.itsClsSizeInLayer(layer); + if (cluster_size_per_layer > 0) { + nl++; + } + total_cluster_size += cluster_size_per_layer; + } + + return (maxMeanITSClusterSize > static_cast(total_cluster_size) / static_cast(nl) * std::cos(std::atan(track.tgl()))); + } + return isElectron_TPChadrej(collision, track) || isElectron_TOFreq(collision, track); + } + + template + bool isElectron_TPChadrej(TCollision const& collision, TTrack const& track) + { + float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + + if (track.tpcNSigmaEl() < minTPCNsigmaEl || maxTPCNsigmaEl < track.tpcNSigmaEl()) { + return false; + } + if (minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < maxTPCNsigmaPi && track.tpcInnerParam() < max_pin_for_pion_rejection) { + return false; + } + if (minTPCNsigmaKa < track.tpcNSigmaKa() && track.tpcNSigmaKa() < maxTPCNsigmaKa) { + return false; + } + if (minTPCNsigmaPr < track.tpcNSigmaPr() && track.tpcNSigmaPr() < maxTPCNsigmaPr) { + return false; + } + if (track.hasTOF() && (maxTOFNsigmaEl < std::fabs(tofNSigmaEl))) { + return false; + } + return true; + } + + template + bool isElectron_TOFreq(TCollision const& collision, TTrack const& track) + { + float tofNSigmaEl = mapTOFNsigmaReassociated[std::make_pair(collision.globalIndex(), track.globalIndex())]; + + if (minTPCNsigmaPi < track.tpcNSigmaPi() && track.tpcNSigmaPi() < maxTPCNsigmaPi && (min_pin_for_pion_rejection < track.tpcInnerParam() && track.tpcInnerParam() < max_pin_for_pion_rejection)) { + return false; + } + return minTPCNsigmaEl < track.tpcNSigmaEl() && track.tpcNSigmaEl() < maxTPCNsigmaEl && std::fabs(tofNSigmaEl) < maxTOFNsigmaEl; + } + + template + void fillTrackInfo(TCollision const& collision, TTracks const& tracks) + { + + for (const auto& track : tracks) { + + if (fillHistogramsBeforeSelection) { + fillTrackHistograms(track); + } + + fRegistry.fill(HIST("Track/hTrackRejectReason"), kAllTracks, track.pt()); + int rejectReason = kPassed; + if (checkTrack(collision, track, &rejectReason) && !isElectron(collision, track)) { + rejectReason = kNotElectron; + } + fRegistry.fill(HIST("Track/hTrackRejectReason"), rejectReason, track.pt()); + if constexpr (isMC) { + if (isTruePrimaryElectron(track)) { + fRegistry.fill(HIST("TrackTrueElec/hTrackRejectReason"), kAllTracks, track.pt()); + fRegistry.fill(HIST("TrackTrueElec/hTrackRejectReason"), rejectReason, track.pt()); + } + } + if (rejectReason != kPassed) { + continue; + } + + fillTrackHistograms(track); + fillTrackTable(collision, track); + } + } + + template + void fillPairInfo(TCollision const& collision, TTracks1 const& tracks1, TTracks2 const& tracks2) + { + if constexpr (pairtype == 0) { // ULS + if (fillHistogramsBeforeSelection) { // we cannot fill the track histograms in the pair loop as they would get filled multiple times. Hence we loop over all tracks here + for (const auto& track : tracks1) { + fillTrackHistograms(track); + } + for (const auto& track : tracks2) { + fillTrackHistograms(track); + } + } + + for (const auto& [t1, t2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { + continue; + } + + if (!isElectron(collision, t1) || !isElectron(collision, t2)) { + continue; + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), dBz); + + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); + + // to do: test if minMee > Mpion show effect on S/B + if (v12.M() < minMee || v12.M() > maxMee) { // don't store //|| v12.M() > maxMee + continue; + } + + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMCutMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMCutMvsPhiV"), phiv, v12.M()); + + if (v12.M() < slope * phiv + intercept) { + continue; + } + + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMPhiCutMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMPhiCutMvsPhiV"), phiv, v12.M()); + + if (t1.sign() > 0) { // for positron + if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t1.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { + fillTrackHistograms(t1); + acceptedPosTrackIds_per_collision.emplace_back(t1.globalIndex()); + } + } else { // for electron + if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t1.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { + fillTrackHistograms(t1); + acceptedNegTrackIds_per_collision.emplace_back(t1.globalIndex()); + } + } + + if (t2.sign() > 0) { // for positron + if (std::find(acceptedPosTrackIds_per_collision.begin(), acceptedPosTrackIds_per_collision.end(), t2.globalIndex()) == acceptedPosTrackIds_per_collision.end()) { + fillTrackHistograms(t2); + acceptedPosTrackIds_per_collision.emplace_back(t2.globalIndex()); + } + } else { // for electron + if (std::find(acceptedNegTrackIds_per_collision.begin(), acceptedNegTrackIds_per_collision.end(), t2.globalIndex()) == acceptedNegTrackIds_per_collision.end()) { + fillTrackHistograms(t2); + acceptedNegTrackIds_per_collision.emplace_back(t2.globalIndex()); + } + } + } // end of ULS pairing + } else { // LS + for (auto& [t1, t2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(tracks1, tracks2))) { + if (!checkTrack(collision, t1) || !checkTrack(collision, t2)) { + continue; + } + if (!isElectron(collision, t1) || !isElectron(collision, t2)) { + continue; + } + + ROOT::Math::PtEtaPhiMVector v1(t1.pt(), t1.eta(), t1.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v2(t2.pt(), t2.eta(), t2.phi(), o2::constants::physics::MassElectron); + ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), dBz); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPt"), v12.M(), v12.Pt()); + fRegistry.fill(HIST("Pair/") + HIST(DileptonSigns[pairtype]) + HIST("hMvsPhiV"), phiv, v12.M()); + } // end of LS pairing + } + } + + template + bool isTruePrimaryElectron(TTrack const& track) + { + if (!track.has_mcParticle()) { + return false; + } + const auto mcParticle = track.template mcParticle_as(); + return std::abs(mcParticle.pdgCode()) == PDG_t::kElectron && mcParticle.isPhysicalPrimary(); // o2-linter: disable=pdg/explicit-code (electron) + } + + template + void fillTrackHistograms(TTrack const& track) + { + constexpr int kDirAllTracks = beforeSelection ? 2 : 0; + fillTrackHistogramsImpl(track); + if constexpr (isMC) { + if (isTruePrimaryElectron(track)) { + fillTrackHistogramsImpl(track); + } + } + } + + template + void fillTrackHistogramsImpl(TTrack const& track) + { + float mcTunedTPCSignal = 0.f; + if constexpr (isMC) { + mcTunedTPCSignal = track.mcTunedTPCSignal(); + } + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hPt"), track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hEtaPhi"), track.phi(), track.eta()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hQoverPt"), track.sign() / track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hRelDeltaPt"), track.pt(), track.sigma1Pt() * track.pt()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyz"), track.dcaXY(), track.dcaZ()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxy_Pt"), track.pt(), track.dcaXY()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAz_Pt"), track.pt(), track.dcaZ()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyzSigma"), track.dcaXY() / std::sqrt(track.cYY()), track.dcaZ() / std::sqrt(track.cZZ())); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAxyRes_Pt"), track.pt(), std::sqrt(track.cYY()) * 1e+4); // convert cm to um + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hDCAzRes_Pt"), track.pt(), std::sqrt(track.cZZ()) * 1e+4); // convert cm to um + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNclsTPC"), track.tpcNClsFound()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNcrTPC"), track.tpcNClsCrossedRows()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2TPC"), track.tpcChi2NCl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNcr2Nf"), track.tpcCrossedRowsOverFindableCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNcls2Nf"), track.tpcFoundOverFindableCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNclsShared"), track.pt(), track.tpcFractionSharedCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCdEdx"), track.tpcInnerParam(), track.tpcSignal()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCdEdxMC"), track.tpcInnerParam(), mcTunedTPCSignal); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNsigmaEl"), track.tpcInnerParam(), track.tpcNSigmaEl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTPCNsigmaPi"), track.tpcInnerParam(), track.tpcNSigmaPi()); + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2TOF"), track.tofChi2()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFbeta"), track.p(), track.beta()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFNsigmaEl"), track.p(), track.tofNSigmaEl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hTOFNsigmaPi"), track.p(), track.tofNSigmaPi()); + + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hNclsITS"), track.itsNCls()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hChi2ITS"), track.itsChi2NCl()); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hITSClusterMap"), track.itsClusterMap()); + + int totalClusterSize = 0, nl = 0; + for (unsigned int layer = 0; layer < 7; layer++) { // o2-linter: disable=magic-number (number of ITS layers) + int clusterSizePerLayer = track.itsClsSizeInLayer(layer); + if (clusterSizePerLayer > 0) { + nl++; + } + totalClusterSize += clusterSizePerLayer; + } + + int totalClusterSizeIb = 0, nl_ib = 0; + for (unsigned int layer = 0; layer < 3; layer++) { // o2-linter: disable=magic-number (number of inner barrel layers) + int clusterSizePerLayer = track.itsClsSizeInLayer(layer); + if (clusterSizePerLayer > 0) { + nl_ib++; + } + totalClusterSizeIb += clusterSizePerLayer; + } + + int totalClusterSizeOb = 0, nlOb = 0; + for (unsigned int layer = 3; layer < 7; layer++) { // o2-linter: disable=magic-number (number of outer barrel layers) + int clusterSizePerLayer = track.itsClsSizeInLayer(layer); + if (clusterSizePerLayer > 0) { + nlOb++; + } + totalClusterSizeOb += clusterSizePerLayer; + } + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITS"), track.p(), static_cast(totalClusterSize) / static_cast(nl) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITSib"), track.p(), static_cast(totalClusterSizeIb) / static_cast(nl_ib) * std::cos(std::atan(track.tgl()))); + fRegistry.fill(HIST(TrackDirs[trackType]) + HIST("hMeanClusterSizeITSob"), track.p(), static_cast(totalClusterSizeOb) / static_cast(nlOb) * std::cos(std::atan(track.tgl()))); + } + + template + void fillTrackTable(TCollision const& collision, TTrack const& track) + { + emprimaryelectrons(collision.globalIndex(), track.globalIndex(), track.sign(), + track.pt(), track.eta(), track.phi(), track.dcaXY(), track.dcaZ(), track.cYY(), track.cZY(), track.cZZ(), + track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), + track.tpcChi2NCl(), track.tpcInnerParam(), + track.tpcSignal(), track.tpcNSigmaEl(), track.tpcNSigmaPi(), + track.beta(), track.tofNSigmaEl(), + track.itsClusterSizes(), track.itsChi2NCl(), track.tofChi2(), track.detectorMap()); + + if constexpr (isMC) { + emprimaryelectronsDeDxMC(track.mcTunedTPCSignal()); + } + } + + std::vector acceptedPosTrackIds_per_collision; + std::vector acceptedNegTrackIds_per_collision; + // Filter trackFilter = minpt < o2::aod::track::pt && nabs(o2::aod::track::eta) < maxeta && o2::aod::track::itsChi2NCl < maxchi2its && ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) == true && nabs(o2::aod::track::dcaXY) < dca_xy_max && nabs(o2::aod::track::dcaZ) < dca_z_max; + // Filter trackFilter + // using MyFilteredTracks = soa::Filtered; + o2::framework::Partition posTracks = o2::aod::track::signed1Pt > 0.f; + o2::framework::Partition negTracks = o2::aod::track::signed1Pt < 0.f; + + std::unordered_map mapCollisionTime; + std::unordered_map mapCollisionTimeError; + + std::map, float> mapTOFNsigmaReassociated; // map pair(collisionId, trackId) -> tof n sigma + std::map, float> mapTOFBetaReassociated; // map pair(collisionId, trackId) -> tof beta + + // ---------- for data ---------- + void processRec(MyCollisions const& collisions, MyBCs const& bcs, MyTracks const& tracks, TV0PhotonsKF const& v0photons, o2::aod::TrackAssoc const& trackIndices) + { + initCCDB(bcs.iteratorAt(0)); + mTOFResponse->processSetup(bcs.iteratorAt(0)); + + for (const auto& track : tracks) { + mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); + mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); + } + calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); + + for (const auto& collision : collisions) { + if (!collision.isSelected()) { + continue; + } + + auto varFillingMode = static_cast(fillingMode.value); + + const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + if (photonsNeeded && v0photons_per_coll.size() == 0) { + continue; + } + + const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); + acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); + + auto iterEp = tracks.begin(); + auto iterEm = tracks.begin(); + auto iterTrack = tracks.begin(); + auto iterPhoton = v0photons.begin(); + + switch (varFillingMode) { + case enumFillingMode::SingleTrack: + fillTrackInfo(collision, slicedTracks); + break; + case enumFillingMode::EpEmPairs: + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty())) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } + break; + case enumFillingMode::EpEmPairsAndPhoton: + std::set tracksToFill; + + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if (v0photons_per_coll.size() >= 1 && ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()))) { + if (!fillWithEtaMassCut) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } else { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + ROOT::Math::PtEtaPhiMVector vEp(iterEp.pt(), iterEp.eta(), iterEp.phi(), o2::constants::physics::MassElectron); + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + ROOT::Math::PtEtaPhiMVector vEm(iterEm.pt(), iterEm.eta(), iterEm.phi(), o2::constants::physics::MassElectron); + for (const auto& photonId : v0photons_per_coll) { + iterPhoton.setCursor(photonId.globalIndex()); + ROOT::Math::PtEtaPhiMVector vPhoton(iterPhoton.pt(), iterPhoton.eta(), iterPhoton.phi(), o2::constants::physics::MassPhoton); + ROOT::Math::PtEtaPhiMVector vTotal = vEp + vEm + vPhoton; + + if (vTotal.M() < minMeegamma || vTotal.M() > maxMeegamma) { // don't store + continue; + } + tracksToFill.insert(posId); // store independently + tracksToFill.insert(eleId); + } + } + } + } + } + if (fillWithEtaMassCut) { + for (const auto& trackId : tracksToFill) { + iterTrack.setCursor(trackId); + fillTrackTable(collision, iterTrack); + } + } + break; + } + + acceptedPosTrackIds_per_collision.clear(); + acceptedNegTrackIds_per_collision.clear(); + } // end of collision loop + mapCollisionTime.clear(); + mapCollisionTimeError.clear(); + mapTOFNsigmaReassociated.clear(); + mapTOFBetaReassociated.clear(); + } + PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec, "process reconstructed info only", false); // standalone + + // void processRec_SWT(MyCollisionsWithSWT const& collisions, MyBCs const& bcs, MyTracks const& tracks, aod::V0PhotonsKF const& v0photons, aod::TrackAssoc const& trackIndices) + // { + // initCCDB(bcs.iteratorAt(0)); + + // mTOFResponse->processSetup(bcs.iteratorAt(0)); + // for (const auto& track : tracks) { + // mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); + // mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); + // } + // calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); + + // for (const auto& collision : collisions) { + // if (!collision.isSelected()) { + // continue; + // } + + // if (collision.swtaliastmp_raw() == 0) { + // continue; + // } + + // const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + // const auto& posTracks_per_coll = posTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + // const auto& negTracks_per_coll = negTracks->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + // const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + // acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); + // acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); + + // if(!fillWithPairs){ + // fillTrackInfo(collision, slicedTracks); + // }else{ + // fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + // if (fillLS) { + // fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + // fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + // } + + // if ((v0photons_per_coll.size() >= 1 && !acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()) || (acceptedPosTrackIds_per_collision.size() >= 2 && acceptedNegTrackIds_per_collision.size() >= 2)) { + // // LOGF(info, "v0photons_per_coll.size() = %d, acceptedPosTrackIds_per_collision.size() = %d, acceptedNegTrackIds_per_collision.size() = %d", v0photons_per_coll.size(), acceptedPosTrackIds_per_collision.size(), acceptedNegTrackIds_per_collision.size()); + // for (const auto& posId : acceptedPosTrackIds_per_collision) { + // const auto& pos = tracks.rawIteratorAt(posId); + // fillTrackTable(collision, pos); + // } + // for (const auto& eleId : acceptedNegTrackIds_per_collision) { + // const auto& ele = tracks.rawIteratorAt(eleId); + // fillTrackTable(collision, ele); + // } + // } + // } + + // acceptedPosTrackIds_per_collision.clear(); + // acceptedNegTrackIds_per_collision.clear(); + + // } // end of collision loop + // } + // PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processRec_SWT, "process reconstructed info with CEFP", false); // with cefp + + // using MyFilteredTracksMC = soa::Filtered; + o2::framework::Partition posTracksMC = o2::aod::track::signed1Pt > 0.f; + o2::framework::Partition negTracksMC = o2::aod::track::signed1Pt < 0.f; + // ---------- for MC ---------- + void processMC(MyCollisionsMC const& collisions, o2::aod::McCollisions const&, MyBCs const& bcs, MyTracksMC const& tracks, TV0PhotonsKF const& v0photons, o2::aod::TrackAssoc const& trackIndices, o2::aod::McParticles const&) + { + uint64_t nCollisSel = 0; + uint64_t nNoMcColl = 0; + uint64_t nProcessedCollisions = 0; + uint64_t nCheckTrack = 0; + + initCCDB(bcs.iteratorAt(0)); + + mTOFResponse->processSetup(bcs.iteratorAt(0)); + for (const auto& track : tracks) { + mapCollisionTime.try_emplace(track.collisionId(), track.tofEvTime()); + mapCollisionTimeError.try_emplace(track.collisionId(), track.tofEvTimeErr()); + } + calculateTOFNSigmaWithReassociation(collisions, bcs, tracks, trackIndices); + + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + nNoMcColl++; + continue; + } + + if (!collision.isSelected()) { + nCollisSel++; + continue; + } + nProcessedCollisions++; + + auto varFillingMode = static_cast(fillingMode.value); + + const auto& v0photons_per_coll = v0photons.sliceBy(perCol_pcm, collision.globalIndex()); + if (photonsNeeded && v0photons_per_coll.size() == 0) { + continue; + } + + const auto& posTracks_per_coll = posTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& negTracks_per_coll = negTracksMC->sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), cache); + const auto& slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + acceptedPosTrackIds_per_collision.reserve(posTracks_per_coll.size()); + acceptedNegTrackIds_per_collision.reserve(negTracks_per_coll.size()); + + auto iterEp = tracks.begin(); + auto iterEm = tracks.begin(); + auto iterTrack = tracks.begin(); + auto iterPhoton = v0photons.begin(); + + switch (varFillingMode) { + case enumFillingMode::SingleTrack: + fillTrackInfo(collision, slicedTracks); + break; + case enumFillingMode::EpEmPairs: + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty())) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } + break; + case enumFillingMode::EpEmPairsAndPhoton: + std::set tracksToFill; + + fillPairInfo(collision, posTracks_per_coll, negTracks_per_coll); // ULS + if (fillLS) { + fillPairInfo(collision, posTracks_per_coll, posTracks_per_coll); // LS++ + fillPairInfo(collision, negTracks_per_coll, negTracks_per_coll); // LS-- + } + if (v0photons_per_coll.size() >= 1 && ((!acceptedPosTrackIds_per_collision.empty() && !acceptedNegTrackIds_per_collision.empty()))) { + if (!fillWithEtaMassCut) { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + fillTrackTable(collision, iterEp); + } + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + fillTrackTable(collision, iterEm); + } + } else { + for (const auto& posId : acceptedPosTrackIds_per_collision) { + iterEp.setCursor(posId); + ROOT::Math::PtEtaPhiMVector vEp(iterEp.pt(), iterEp.eta(), iterEp.phi(), o2::constants::physics::MassElectron); + for (const auto& eleId : acceptedNegTrackIds_per_collision) { + iterEm.setCursor(eleId); + ROOT::Math::PtEtaPhiMVector vEm(iterEm.pt(), iterEm.eta(), iterEm.phi(), o2::constants::physics::MassElectron); + for (const auto& photonId : v0photons_per_coll) { + iterPhoton.setCursor(photonId.globalIndex()); + ROOT::Math::PtEtaPhiMVector vPhoton(iterPhoton.pt(), iterPhoton.eta(), iterPhoton.phi(), o2::constants::physics::MassPhoton); + ROOT::Math::PtEtaPhiMVector vTotal = vEp + vEm + vPhoton; + + if (vTotal.M() < minMeegamma || vTotal.M() > maxMeegamma) { // don't store + continue; + } + tracksToFill.insert(posId); // store independently + tracksToFill.insert(eleId); + } + } + } + } + } + if (fillWithEtaMassCut) { + for (const auto& trackId : tracksToFill) { + iterTrack.setCursor(trackId); + fillTrackTable(collision, iterTrack); + } + } + break; + } + + acceptedPosTrackIds_per_collision.clear(); + acceptedNegTrackIds_per_collision.clear(); + } // end of collision loop + mapCollisionTime.clear(); + mapCollisionTimeError.clear(); + mapTOFNsigmaReassociated.clear(); + mapTOFBetaReassociated.clear(); + + LOG(info) << "Total collisions: " << collisions.size(); + LOG(info) << "No MC collision: " << nNoMcColl; + LOG(info) << "Rejected by selection: " << nCollisSel; + LOG(info) << "Processed collisions: " << nProcessedCollisions; + LOG(info) << "Invalid track: " << nCheckTrack; + } + PROCESS_SWITCH(skimmerPrimaryElectronFromDalitzEE, processMC, "process reconstructed and MC info ", true); +}; + +#endif // PWGEM_PHOTONMESON_TABLEPRODUCER_SKIMMERPRIMARYELECTRONFROMDALITZEE_H_ diff --git a/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx new file mode 100644 index 00000000000..32ba2e5f287 --- /dev/null +++ b/PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEETmpTable.cxx @@ -0,0 +1,34 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file skimmerPrimaryElectronFromDalitzEETmpTable.cxx +/// \brief write electrons for Dalitz into a temporary table that is then used for event skimming +/// \author joshua.konig@cern.ch + +#include "PWGEM/PhotonMeson/TableProducer/skimmerPrimaryElectronFromDalitzEE.h" + +#include "PID/PIDTOFParamService.h" + +#include +#include +#include + +using namespace o2::framework; +using namespace o2::aod; +using namespace o2::framework::expressions; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + o2::pid::tof::TOFResponseImpl::metadataInfo.initMetadata(context); + + return WorkflowSpec{ + adaptAnalysisTask>(context, TaskName{"skimmer-primary-electron-from-dalitzee-tmptable"})}; +} diff --git a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt index fe155e91fe1..cd9513dcbe3 100644 --- a/PWGEM/PhotonMeson/Tasks/CMakeLists.txt +++ b/PWGEM/PhotonMeson/Tasks/CMakeLists.txt @@ -11,16 +11,6 @@ add_subdirectory(Converters) -o2physics_add_dpl_workflow(gammaconversions - SOURCES gammaConversions.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(gammaconversionstruthonlymc - SOURCES gammaConversionsTruthOnlyMc.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(emcal-pi0-qc SOURCES emcalPi0Qc.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore @@ -41,11 +31,6 @@ o2physics_add_dpl_workflow(pcm-qc PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(pcm-qc-mc - SOURCES pcmQCMC.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(dalitz-ee-qc SOURCES dalitzEEQC.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore O2Physics::MLCore O2Physics::PWGEMDileptonCore @@ -131,26 +116,26 @@ o2physics_add_dpl_workflow(pi0eta-to-gammagamma-mc-emcemc PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(pi0eta-to-gammagamma-mc-pcmemc + SOURCES Pi0EtaToGammaGammaMCPCMEMC.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(tag-and-probe SOURCES TagAndProbe.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore O2Physics::MLCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(material-budget - SOURCES MaterialBudget.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore - COMPONENT_NAME Analysis) - -o2physics_add_dpl_workflow(material-budget-mc - SOURCES MaterialBudgetMC.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore - COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(check-mc-v0 SOURCES CheckMCV0.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-photon-flow + SOURCES taskPhotonFlow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(pi0-flow-emc SOURCES taskPi0FlowEMC.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore @@ -200,3 +185,43 @@ o2physics_add_dpl_workflow(photonhbt SOURCES photonhbt.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(emcal-photon-mc-task + SOURCES emcalPhotonMcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(emcal-mc-sanity-check + SOURCES emcalMcSanityCheck.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(emcal-mc-task + SOURCES emcalMcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(event-normalization-task + SOURCES eventNormalizationTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pair-qc-task + SOURCES pairQcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-photon-mc-task + SOURCES twoPhotonMcTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(dilepton-effi-for-dalitz-skimmer + SOURCES dileptonEffiForDalitzSkimmer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(meson-signal-loss + SOURCES mesonSignalLoss.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGEM/PhotonMeson/Tasks/CheckMCV0.cxx b/PWGEM/PhotonMeson/Tasks/CheckMCV0.cxx index 3caa5e5b5aa..035cf6b905e 100644 --- a/PWGEM/PhotonMeson/Tasks/CheckMCV0.cxx +++ b/PWGEM/PhotonMeson/Tasks/CheckMCV0.cxx @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -169,7 +170,7 @@ struct CheckMCV0 { Configurable mLUTPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; Configurable mCCDBUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Service mCCDB; + Service mCCDB{}; int mRunNumber{-1}; o2::base::MatLayerCylSet* mLUT{nullptr}; o2::parameters::GRPMagField* mGRPMagField{nullptr}; @@ -177,7 +178,6 @@ struct CheckMCV0 { // params std::array mcPosXYZEtaTglPtProp{}; std::array mcEleXYZEtaTglPtProp{}; - std::array mcMotherXYZEtaTglPtProp{}; // Track Types static constexpr std::array v0Types{"ITSTPC_ITSTPC/", "TPConly_TPConly/", "ITSonly_ITSonly/", "ITSTPC_TPConly/", "ITSTPC_ITSonly/", "TPConly_ITSonly/"}; @@ -515,7 +515,7 @@ struct CheckMCV0 { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"check-mc-v0"})}; + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"check-mc-v0"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/Converters/electronFromDalitzConverter1.cxx b/PWGEM/PhotonMeson/Tasks/Converters/electronFromDalitzConverter1.cxx index cd54adedfb3..de371f4db9a 100644 --- a/PWGEM/PhotonMeson/Tasks/Converters/electronFromDalitzConverter1.cxx +++ b/PWGEM/PhotonMeson/Tasks/Converters/electronFromDalitzConverter1.cxx @@ -31,6 +31,7 @@ struct electronFromDalitzConverter1 { void process(aod::EMPrimaryElectronsFromDalitz_000 const& tracks) { + electron_001.reserve(tracks.size()); for (const auto& track : tracks) { electron_001(track.collisionId(), track.trackId(), diff --git a/PWGEM/PhotonMeson/Tasks/Converters/pcmConverter1.cxx b/PWGEM/PhotonMeson/Tasks/Converters/pcmConverter1.cxx index a8277d122db..35f3c5c24d3 100644 --- a/PWGEM/PhotonMeson/Tasks/Converters/pcmConverter1.cxx +++ b/PWGEM/PhotonMeson/Tasks/Converters/pcmConverter1.cxx @@ -32,6 +32,7 @@ struct pcmConverter1 { void process(aod::V0PhotonsKF_000 const& v0s, aod::V0Legs_000 const& v0legs) { + v0photon_001.reserve(v0s.size()); for (auto& v0 : v0s) { v0photon_001( v0.collisionId(), @@ -56,6 +57,7 @@ struct pcmConverter1 { v0.chiSquareNDF()); } // end of v0 loop + v0leg_001.reserve(v0legs.size()); for (auto& v0leg : v0legs) { v0leg_001( v0leg.collisionId(), diff --git a/PWGEM/PhotonMeson/Tasks/Converters/skimEmcClusterConverter.cxx b/PWGEM/PhotonMeson/Tasks/Converters/skimEmcClusterConverter.cxx index d1809162bbc..11721c5d12c 100644 --- a/PWGEM/PhotonMeson/Tasks/Converters/skimEmcClusterConverter.cxx +++ b/PWGEM/PhotonMeson/Tasks/Converters/skimEmcClusterConverter.cxx @@ -41,6 +41,7 @@ struct SkimEmcClusterConverter { { std::vector vDummy = {}; std::vector vPhi, vEta, vPt, vP; + tableGammaEMCReco001.reserve(emcClusters.size()); for (const auto& emcCluster : emcClusters) { // using convertSpanToVector is just a temporal solution, since right now tables return gsl::span // while filling a table needs std::span which can not be transformed. So going over std::vector diff --git a/PWGEM/PhotonMeson/Tasks/HeavyNeutralMeson.cxx b/PWGEM/PhotonMeson/Tasks/HeavyNeutralMeson.cxx index a92e1888049..be4f24a77e5 100644 --- a/PWGEM/PhotonMeson/Tasks/HeavyNeutralMeson.cxx +++ b/PWGEM/PhotonMeson/Tasks/HeavyNeutralMeson.cxx @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -90,10 +91,10 @@ enum PIDLimits { kTPCMin, const std::vector speciesName{"pion"}; // ToDo include charged pions const std::vector pTCutsName{"Pt min", "Pt max", "P TOF thres"}; const std::vector pidCutsName{"TPC min", "TPC max", "TPCTOF max", "ITS min", "ITS max"}; -const float pidcutsTable[kNTracksPID][kNPIDLimits]{{-4.f, 4.f, 4.f, -99.f, 99.f}}; -const float ptcutsTable[kNTracksPID][3]{{0.35f, 6.f, 0.75f}}; -const float nClusterMinTPC[1][kNTracksPID]{{80.0f}}; -const float nClusterMinITS[1][kNTracksPID]{{4}}; +constexpr std::array, kNTracksPID> pidcutsTable{{{-4.f, 4.f, 4.f, -99.f, 99.f}}}; +constexpr std::array, kNTracksPID> ptcutsTable{{{0.35f, 6.f, 0.75f}}}; +constexpr std::array, 1> nClusterMinTPC{{{80.0f}}}; +constexpr std::array, 1> nClusterMinITS{{{4.f}}}; } // namespace hnm @@ -111,19 +112,19 @@ struct HeavyNeutralMeson { Configurable confEvtRequireSel8{"confEvtRequireSel8", false, "Evt sel: check for offline selection (sel8)"}; // ---> Track selection - Configurable> cfgPtCuts{"cfgPtCuts", {hnm::ptcutsTable[0], 1, 3, hnm::speciesName, hnm::pTCutsName}, "Track pT selections"}; + Configurable> cfgPtCuts{"cfgPtCuts", {hnm::ptcutsTable[0].data(), 1, 3, hnm::speciesName, hnm::pTCutsName}, "Track pT selections"}; Configurable cfgTrkEta{"cfgTrkEta", 0.9, "Eta"}; - Configurable> cfgTPCNClustersMin{"cfgTPCNClustersMin", {hnm::nClusterMinTPC[0], 1, 1, std::vector{"TPCNClusMin"}, hnm::speciesName}, "Mininum of TPC Clusters"}; + Configurable> cfgTPCNClustersMin{"cfgTPCNClustersMin", {hnm::nClusterMinTPC[0].data(), 1, 1, std::vector{"TPCNClusMin"}, hnm::speciesName}, "Mininum of TPC Clusters"}; Configurable cfgTrkTPCfCls{"cfgTrkTPCfCls", 0.83, "Minimum fraction of crossed rows over findable clusters"}; Configurable cfgTrkTPCcRowsMin{"cfgTrkTPCcRowsMin", 70, "Minimum number of crossed TPC rows"}; Configurable cfgTrkTPCsClsSharedFrac{"cfgTrkTPCsClsSharedFrac", 1.f, "Fraction of shared TPC clusters"}; - Configurable> cfgTrkITSnclsMin{"cfgTrkITSnclsMin", {hnm::nClusterMinITS[0], 1, 1, std::vector{"Cut"}, hnm::speciesName}, "Minimum number of ITS clusters"}; + Configurable> cfgTrkITSnclsMin{"cfgTrkITSnclsMin", {hnm::nClusterMinITS[0].data(), 1, 1, std::vector{"Cut"}, hnm::speciesName}, "Minimum number of ITS clusters"}; Configurable cfgTrkDCAxyMax{"cfgTrkDCAxyMax", 0.15, "Maximum DCA_xy"}; Configurable cfgTrkDCAzMax{"cfgTrkDCAzMax", 0.3, "Maximum DCA_z"}; Configurable cfgTrkMaxChi2PerClusterTPC{"cfgTrkMaxChi2PerClusterTPC", 4.0f, "Minimal track selection: max allowed chi2 per TPC cluster"}; // 4.0 is default of global tracks on 20.01.2023 Configurable cfgTrkMaxChi2PerClusterITS{"cfgTrkMaxChi2PerClusterITS", 36.0f, "Minimal track selection: max allowed chi2 per ITS cluster"}; // 36.0 is default of global tracks on 20.01.2023 - Configurable> cfgPIDCuts{"cfgPIDCuts", {hnm::pidcutsTable[0], 1, hnm::kNPIDLimits, hnm::speciesName, hnm::pidCutsName}, "Femtopartner PID nsigma selections"}; // PID selections + Configurable> cfgPIDCuts{"cfgPIDCuts", {hnm::pidcutsTable[0].data(), 1, hnm::kNPIDLimits, hnm::speciesName, hnm::pidCutsName}, "Femtopartner PID nsigma selections"}; // PID selections // ---> Configurables to allow for a shift in eta/phi of EMCal clusters to better align with extrapolated TPC tracks Configurable cfgDoEMCShift{"cfgDoEMCShift", false, "Apply SM-wise shift in eta and phi to EMCal clusters to align with TPC tracks"}; @@ -137,9 +138,9 @@ struct HeavyNeutralMeson { Configurable cfgHNMMassCorrection{"cfgHNMMassCorrection", 1, "Use GG PDG mass to correct HNM mass (0 = off, 1 = subDeltaPi0, 2 = subLambda)"}; // ---> Mass windows for the selection of heavy neutral mesons (also based on mass of their light neutral meson decay daughter) - static constexpr float DefaultMassWindows[2][4] = {{0., 0.4, 0.6, 1.}, {0.4, 0.8, 0.8, 1.2}}; - Configurable> cfgMassWindowOmega{"cfgMassWindowOmega", {DefaultMassWindows[0], 4, {"pi0_min", "pi0_max", "omega_min", "omega_max"}}, "Mass window for selected omegas and their decay pi0"}; - Configurable> cfgMassWindowEtaPrime{"cfgMassWindowEtaPrime", {DefaultMassWindows[1], 4, {"eta_min", "eta_max", "etaprime_min", "etaprime_max"}}, "Mass window for selected eta' and their decay eta"}; + static constexpr std::array, 2> DefaultMassWindows{{{0., 0.4, 0.6, 1.}, {0.4, 0.8, 0.8, 1.2}}}; + Configurable> cfgMassWindowOmega{"cfgMassWindowOmega", {DefaultMassWindows[0].data(), 4, {"pi0_min", "pi0_max", "omega_min", "omega_max"}}, "Mass window for selected omegas and their decay pi0"}; + Configurable> cfgMassWindowEtaPrime{"cfgMassWindowEtaPrime", {DefaultMassWindows[1].data(), 4, {"eta_min", "eta_max", "etaprime_min", "etaprime_max"}}, "Mass window for selected eta' and their decay eta"}; Configurable cfgMaxMultiplicity{"cfgMaxMultiplicity", 5000, "Maximum number of tracks in a collision (can be used to increase the S/B -> Very experimental)"}; Configurable cfgMinGGPtOverHNMPt{"cfgMinGGPtOverHNMPt", 0., "Minimum ratio of the pT of the gamma gamma pair over the pT of the HNM (can be used to increase the S/B)"}; @@ -147,9 +148,9 @@ struct HeavyNeutralMeson { HistogramRegistry mHistManager{"HeavyNeutralMesonHistograms", {}, OutputObjHandlingPolicy::AnalysisObject}; // Prepare vectors for different species - std::vector vGGs; - std::vector vHNMs; - std::vector etaPrimeEMC, etaPrimePCM, omegaEMC, omegaPCM, proton, antiproton, deuteron, antideuteron, pion, antipion; + std::vector mvGGs; + std::vector mvHNMs; + std::vector etaPrimeEMC, etaPrimePCM, omegaEMC, omegaPCM, pion, antipion; float mMassProton = constants::physics::MassProton; float mMassDeuteron = constants::physics::MassDeuteron; float mMassOmega = 0.782; @@ -162,30 +163,42 @@ struct HeavyNeutralMeson { template bool isSelectedTrack(T const& track, hnm::TracksPID partSpecies) { - if (track.pt() < cfgPtCuts->get(partSpecies, "Pt min")) + if (track.pt() < cfgPtCuts->get(partSpecies, "Pt min")) { return false; - if (track.pt() > cfgPtCuts->get(partSpecies, "Pt max")) + } + if (track.pt() > cfgPtCuts->get(partSpecies, "Pt max")) { return false; - if (std::abs(track.eta()) > cfgTrkEta) + } + if (std::abs(track.eta()) > cfgTrkEta) { return false; - if (track.tpcNClsFound() < cfgTPCNClustersMin->get("TPCNClusMin", partSpecies)) + } + if (track.tpcNClsFound() < cfgTPCNClustersMin->get("TPCNClusMin", partSpecies)) { return false; - if (track.tpcCrossedRowsOverFindableCls() < cfgTrkTPCfCls) + } + if (track.tpcCrossedRowsOverFindableCls() < cfgTrkTPCfCls) { return false; - if (track.tpcNClsCrossedRows() < cfgTrkTPCcRowsMin) + } + if (track.tpcNClsCrossedRows() < cfgTrkTPCcRowsMin) { return false; - if (track.tpcFractionSharedCls() > cfgTrkTPCsClsSharedFrac) + } + if (track.tpcFractionSharedCls() > cfgTrkTPCsClsSharedFrac) { return false; - if (track.itsNCls() < cfgTrkITSnclsMin->get(static_cast(0), partSpecies)) + } + if (track.itsNCls() < cfgTrkITSnclsMin->get(static_cast(0), partSpecies)) { return false; - if (std::abs(track.dcaXY()) > cfgTrkDCAxyMax) + } + if (std::abs(track.dcaXY()) > cfgTrkDCAxyMax) { return false; - if (std::abs(track.dcaZ()) > cfgTrkDCAzMax) + } + if (std::abs(track.dcaZ()) > cfgTrkDCAzMax) { return false; - if (track.tpcChi2NCl() > cfgTrkMaxChi2PerClusterTPC) + } + if (track.tpcChi2NCl() > cfgTrkMaxChi2PerClusterTPC) { return false; - if (track.itsChi2NCl() > cfgTrkMaxChi2PerClusterITS) + } + if (track.itsChi2NCl() > cfgTrkMaxChi2PerClusterITS) { return false; + } return true; } @@ -237,8 +250,9 @@ struct HeavyNeutralMeson { mHistManager.add("Event/nClustersVsV0s", "Number of clusters and V0s in the collision;#bf{#it{N}_{clusters}};#bf{#it{N}_{V0s}}", HistType::kTH2F, {{26, -0.5, 25.5}, {26, -0.5, 25.5}}); mHistManager.add("Event/nEMCalEvents", "Number of collisions with a certain combination of EMCal triggers;;#bf{#it{N}_{collisions}}", HistType::kTH1F, {{5, -0.5, 4.5}}); std::vector nEventTitles = {"Cells & kTVXinEMC", "Cells & L0", "Cells & !kTVXinEMC & !L0", "!Cells & kTVXinEMC", "!Cells & L0"}; - for (size_t iBin = 0; iBin < nEventTitles.size(); iBin++) + for (size_t iBin = 0; iBin < nEventTitles.size(); iBin++) { mHistManager.get(HIST("Event/nEMCalEvents"))->GetXaxis()->SetBinLabel(iBin + 1, nEventTitles[iBin].data()); + } mHistManager.add("Event/fMultiplicityBefore", "Multiplicity of all processed events;#bf{#it{N}_{tracks}};#bf{#it{N}_{collisions}}", HistType::kTH1F, {{500, 0, 500}}); mHistManager.add("Event/fMultiplicityAfter", "Multiplicity after event cuts;#bf{#it{N}_{tracks}};#bf{#it{N}_{collisions}}", HistType::kTH1F, {{500, 0, 500}}); mHistManager.add("Event/fZvtxBefore", "Zvtx of all processed events;#bf{z_{vtx} (cm)};#bf{#it{N}_{collisions}}", HistType::kTH1F, {{500, -15, 15}}); @@ -265,8 +279,8 @@ struct HeavyNeutralMeson { mHistManager.add("TrackCuts/TPCSignal/fTPCSignalAnti", "TPCSignalP;#bf{#it{p} (GeV/#it{c})};dE/dx", {HistType::kTH2F, {{500, 0.0f, 6.0f}, {2000, -100.f, 500.f}}}); const int nTrackSpecies = 2; // x2 because of anti particles - const char* particleSpecies[nTrackSpecies] = {"Pion", "AntiPion"}; - const char* particleSpeciesLatex[nTrackSpecies] = {"#pi^{+}", "#pi^{-}"}; + std::array particleSpecies = {"Pion", "AntiPion"}; + std::array particleSpeciesLatex = {"#pi^{+}", "#pi^{-}"}; for (int iParticle = 0; iParticle < nTrackSpecies; iParticle++) { mHistManager.add(Form("TrackCuts/TracksBefore/fMomCorrelationAfterCuts%s", particleSpecies[iParticle]), "fMomCorrelation;#bf{#it{p} (GeV/#it{c})};#bf{#it{p}_{TPC} (GeV/#it{c})}", {HistType::kTH2F, {{500, 0.0f, 20.0f}, {500, 0.0f, 20.0f}}}); @@ -346,8 +360,9 @@ struct HeavyNeutralMeson { mHistManager.fill(HIST("Event/fZvtxBefore"), collision.posZ()); // Ensure evts are consistent with Sel8 and Vtx-z selection - if (!isSelectedEvent(collision)) + if (!isSelectedEvent(collision)) { return; + } // QA accepted evts mHistManager.fill(HIST("Event/fMultiplicityAfter"), collision.multNTracksPV()); @@ -356,7 +371,7 @@ struct HeavyNeutralMeson { // clean vecs pion.clear(); antipion.clear(); - vHNMs.clear(); + mvHNMs.clear(); // vGGs vector is cleared in reconstructGGs. // ---------------------------------> EMCal event QA <---------------------------------- @@ -366,16 +381,21 @@ struct HeavyNeutralMeson { bool iskTVXinEMC = collision.foundBC_as().alias_bit(kTVXinEMC); bool isL0Triggered = collision.foundBC_as().alias_bit(kEMC7) || collision.foundBC_as().alias_bit(kEG1) || collision.foundBC_as().alias_bit(kEG2); - if (bcHasEMCCells && iskTVXinEMC) + if (bcHasEMCCells && iskTVXinEMC) { mHistManager.fill(HIST("Event/nEMCalEvents"), 0); - if (bcHasEMCCells && isL0Triggered) + } + if (bcHasEMCCells && isL0Triggered) { mHistManager.fill(HIST("Event/nEMCalEvents"), 1); - if (bcHasEMCCells && !iskTVXinEMC && !isL0Triggered) + } + if (bcHasEMCCells && !iskTVXinEMC && !isL0Triggered) { mHistManager.fill(HIST("Event/nEMCalEvents"), 2); - if (!bcHasEMCCells && iskTVXinEMC) + } + if (!bcHasEMCCells && iskTVXinEMC) { mHistManager.fill(HIST("Event/nEMCalEvents"), 3); - if (!bcHasEMCCells && isL0Triggered) + } + if (!bcHasEMCCells && isL0Triggered) { mHistManager.fill(HIST("Event/nEMCalEvents"), 4); + } // --------------------------------> Process Photons <---------------------------------- // - Slice clusters and V0s by collision ID to get the ones in this collision @@ -388,9 +408,9 @@ struct HeavyNeutralMeson { std::vector vGammas; hnmutilities::storeGammasInVector(clustersInThisCollision, v0sInThisCollision, vGammas, emcEtaShift, emcPhiShift); - hnmutilities::reconstructGGs(vGammas, vGGs); + hnmutilities::reconstructGGs(vGammas, mvGGs); vGammas.clear(); - processGGs(vGGs); + processGGs(mvGGs); // ------------------------------> Loop over all tracks <------------------------------- // - Sort them into vectors based on PID ((anti)protons, (anti)deuterons, (anti)pions) @@ -482,7 +502,7 @@ struct HeavyNeutralMeson { for (const auto& posPion : pion) { for (const auto& negPion : antipion) { ROOT::Math::PtEtaPhiMVector vecPiPlPiMi = posPion + negPion; - hnmutilities::reconstructHeavyNeutralMesons(vecPiPlPiMi, vGGs, vHNMs); + hnmutilities::reconstructHeavyNeutralMesons(vecPiPlPiMi, mvGGs, mvHNMs); mHistManager.fill(HIST("HNM/Before/PiPlPiMi/fInvMassVsPt"), vecPiPlPiMi.M(), vecPiPlPiMi.pt()); mHistManager.fill(HIST("HNM/Before/PiPlPiMi/fEta"), vecPiPlPiMi.eta()); @@ -503,7 +523,7 @@ struct HeavyNeutralMeson { // ---------------------------> Process HNM candidates <-------------------------------- // - Fill invMassVsPt histograms separated into HNM types (based on GG mass) and gamma reco method // ------------------------------------------------------------------------------------- - processHNMs(vHNMs); + processHNMs(mvHNMs); } /// \brief Loop over the GG candidates, fill the mass/pt histograms and set the isPi0/isEta flags based on the reconstructed mass @@ -607,4 +627,7 @@ struct HeavyNeutralMeson { } }; -WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } +WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/OmegaMesonEMC.cxx b/PWGEM/PhotonMeson/Tasks/OmegaMesonEMC.cxx index 587546ff802..7fd90cde096 100644 --- a/PWGEM/PhotonMeson/Tasks/OmegaMesonEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/OmegaMesonEMC.cxx @@ -8,13 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// + /// \file OmegaMesonEMC.cxx -/// /// \brief This code loops over collisions to reconstruct heavy mesons (omega or eta') using EMCal clusters -/// /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) - Goethe University Frankfurt -/// #include "PWGEM/PhotonMeson/Utils/HNMUtilities.h" #include "PWGJE/DataModel/EMCALMatchedCollisions.h" @@ -29,6 +26,7 @@ #include #include +#include #include #include #include @@ -75,7 +73,7 @@ enum TrackCuts { kpT, const std::vector chargedPionMinMaxName{"Min", "Max"}; const std::vector chargedPionCutsName{"pT", "eta", "TPC sigma"}; -const float chargedPionCutsTable[kTrackCuts][2]{{0.35f, 20.f}, {-.8f, .8f}, {-4.f, 4.f}}; +[[maybe_unused]] const std::array, kTrackCuts> chargedPionCutsTable{{{0.35f, 20.f}, {-.8f, .8f}, {-4.f, 4.f}}}; } // namespace hnm @@ -97,7 +95,7 @@ struct OmegaMesonEMC { Configurable confEvtRequireNoSameBunchPileUp{"confEvtRequireNoSameBunchPileUp", false, "Evt sel: check for no same bunch pile-up"}; // ---> Track selection - Configurable> cfgChargedPionCuts{"cfgChargedPionCuts", {hnm::chargedPionCutsTable[0], 3, 2, hnm::chargedPionCutsName, hnm::chargedPionMinMaxName}, "Charged pion track cuts"}; + Configurable> cfgChargedPionCuts{"cfgChargedPionCuts", {hnm::chargedPionCutsTable[0].data(), 3, 2, hnm::chargedPionCutsName, hnm::chargedPionMinMaxName}, "Charged pion track cuts"}; Configurable cfgTPCNClustersMin{"cfgTPCNClustersMin", 80, "Mininum of TPC Clusters"}; Configurable cfgTrkTPCfCls{"cfgTrkTPCfCls", 0.83, "Minimum fraction of crossed rows over findable clusters"}; Configurable cfgTrkTPCcRowsMin{"cfgTrkTPCcRowsMin", 70, "Minimum number of crossed TPC rows"}; @@ -120,8 +118,8 @@ struct OmegaMesonEMC { Configurable cfgHNMMassCorrection{"cfgHNMMassCorrection", 1, "Use GG PDG mass to correct HNM mass (0 = off, 1 = subDeltaPi0, 2 = subLambda)"}; // ---> Mass windows for the selection of heavy neutral mesons (also based on mass of their light neutral meson decay daughter) - static constexpr float DefaultMassWindows[2] = {0.11, 0.16}; - Configurable> cfgMassWindowPi0{"cfgMassWindowPi0", {DefaultMassWindows, 2, {"min", "max"}}, "Mass window for selected decay pi0"}; + static constexpr std::array DefaultMassWindows{0.11, 0.16}; + Configurable> cfgMassWindowPi0{"cfgMassWindowPi0", {DefaultMassWindows.data(), 2, {"min", "max"}}, "Mass window for selected decay pi0"}; Configurable cfgMaxMultiplicity{"cfgMaxMultiplicity", 5000, "Maximum number of tracks in a collision (can be used to increase the S/B -> Very experimental)"}; Configurable cfgMinGGPtOverHNMPt{"cfgMinGGPtOverHNMPt", 0., "Minimum ratio of the pT of the gamma gamma pair over the pT of the HNM (can be used to increase the S/B)"}; @@ -142,16 +140,21 @@ struct OmegaMesonEMC { template bool isSelectedTrack(T const& track) { - if (track.tpcNClsFound() < cfgTPCNClustersMin) + if (track.tpcNClsFound() < cfgTPCNClustersMin) { return false; - if (track.tpcCrossedRowsOverFindableCls() < cfgTrkTPCfCls) + } + if (track.tpcCrossedRowsOverFindableCls() < cfgTrkTPCfCls) { return false; - if (track.tpcNClsCrossedRows() < cfgTrkTPCcRowsMin) + } + if (track.tpcNClsCrossedRows() < cfgTrkTPCcRowsMin) { return false; - if (track.tpcFractionSharedCls() > cfgTrkTPCsClsSharedFrac) + } + if (track.tpcFractionSharedCls() > cfgTrkTPCsClsSharedFrac) { return false; - if (track.itsNCls() < cfgTrkITSnclsMin) + } + if (track.itsNCls() < cfgTrkITSnclsMin) { return false; + } return true; } @@ -168,8 +171,9 @@ struct OmegaMesonEMC { { mHistManager.add("Event/nEMCalEvents", "Number of collisions with a certain combination of EMCal triggers;;#bf{#it{N}_{collisions}}", HistType::kTH1F, {{5, -0.5, 4.5}}); std::vector nEventTitles = {"Cells & kTVXinEMC", "Cells & L0", "Cells & !kTVXinEMC & !L0", "!Cells & kTVXinEMC", "!Cells & L0"}; - for (size_t iBin = 0; iBin < nEventTitles.size(); iBin++) + for (size_t iBin = 0; iBin < nEventTitles.size(); iBin++) { mHistManager.get(HIST("Event/nEMCalEvents"))->GetXaxis()->SetBinLabel(iBin + 1, nEventTitles[iBin].data()); + } mHistManager.add("Event/fMultiplicity", "Multiplicity after event cuts;#bf{#it{N}_{tracks}};#bf{#it{N}_{collisions}}", HistType::kTH1F, {{500, 0, 500}}); mHistManager.add("Event/fZvtx", "Zvtx after event cuts;#bf{z_{vtx} (cm)};#bf{#it{N}_{collisions}}", HistType::kTH1F, {{300, -15, 15}}); @@ -177,8 +181,8 @@ struct OmegaMesonEMC { mHistManager.add("GG/invMassVsPt_selected", "Invariant mass and pT of gg candidates;#bf{#it{M}^{#gamma#gamma} (GeV/#it{c}^{2})};#bf{#it{p}_{T}^{#gamma#gamma} (GeV/#it{c})}", HistType::kTH2F, {{400, 0., 0.8}, {250, 0., 25.}}); const int nTrackSpecies = 2; // x2 because of anti particles - const char* particleSpecies[nTrackSpecies] = {"Pion", "AntiPion"}; - const char* particleSpeciesLatex[nTrackSpecies] = {"#pi^{+}", "#pi^{-}"}; + std::array particleSpecies = {"Pion", "AntiPion"}; + std::array particleSpeciesLatex = {"#pi^{+}", "#pi^{-}"}; for (int iParticle = 0; iParticle < nTrackSpecies; iParticle++) { mHistManager.add(Form("TrackCuts/%s/fPt", particleSpecies[iParticle]), Form("%s transverse momentum;#bf{#it{p}_{T}^{%s} (GeV/#it{c})};#bf{#it{N}^{%s}}", particleSpecies[iParticle], particleSpeciesLatex[iParticle], particleSpeciesLatex[iParticle]), HistType::kTH1F, {{500, 0, 10}}); @@ -230,27 +234,37 @@ struct OmegaMesonEMC { bool iskTVXinEMC = foundBC.alias_bit(kTVXinEMC); bool isL0Triggered = foundBC.alias_bit(kEMC7) || foundBC.alias_bit(kDMC7) || foundBC.alias_bit(kEG1) || foundBC.alias_bit(kEG2); - if (confEvtRequireSel8 && !collision.sel8()) + if (confEvtRequireSel8 && !collision.sel8()) { return; // Skip this collision if sel8 trigger bit is not set - if (confEvtRequirekTVXinEMC && !iskTVXinEMC) + } + if (confEvtRequirekTVXinEMC && !iskTVXinEMC) { return; // Skip this collision if kTVXinEMC trigger bit is not set - if (confEvtRequireL0 && !isL0Triggered) + } + if (confEvtRequireL0 && !isL0Triggered) { return; // Skip this collision if L0 trigger bit is not set - if (confEvtRequireGoodZVertex && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + } + if (confEvtRequireGoodZVertex && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return; // Skip this collision if good z-vertex condition is not met - if (confEvtRequireNoSameBunchPileUp && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + } + if (confEvtRequireNoSameBunchPileUp && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return; // Skip this collision if no same bunch pileup condition is not met + } - if (bcHasEMCCells && iskTVXinEMC) + if (bcHasEMCCells && iskTVXinEMC) { mHistManager.fill(HIST("Event/nEMCalEvents"), 0); - if (bcHasEMCCells && isL0Triggered) + } + if (bcHasEMCCells && isL0Triggered) { mHistManager.fill(HIST("Event/nEMCalEvents"), 1); - if (bcHasEMCCells && !iskTVXinEMC && !isL0Triggered) + } + if (bcHasEMCCells && !iskTVXinEMC && !isL0Triggered) { mHistManager.fill(HIST("Event/nEMCalEvents"), 2); - if (!bcHasEMCCells && iskTVXinEMC) + } + if (!bcHasEMCCells && iskTVXinEMC) { mHistManager.fill(HIST("Event/nEMCalEvents"), 3); - if (!bcHasEMCCells && isL0Triggered) + } + if (!bcHasEMCCells && isL0Triggered) { mHistManager.fill(HIST("Event/nEMCalEvents"), 4); + } mHistManager.fill(HIST("Event/fMultiplicity"), collision.multNTracksPV()); mHistManager.fill(HIST("Event/fZvtx"), collision.posZ()); @@ -285,8 +299,9 @@ struct OmegaMesonEMC { // - Fill QA histograms for all tracks and per particle species // ------------------------------------------------------------------------------------- for (const auto& track : tracks) { - if (!isSelectedTrack(track)) - continue; // Skip tracks that do not pass the selection criteria + if (!isSelectedTrack(track)) { + continue; // Skip tracks that do not pass the selection criteria + } if (track.sign() > 0) { // Positive charge -> Particles pion.emplace_back(track.pt(), track.eta(), track.phi(), constants::physics::MassPionCharged); @@ -355,4 +370,7 @@ struct OmegaMesonEMC { } }; -WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"omega-meson-emc"})}; } +WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"omega-meson-emc"})}; +} diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaEMCEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaEMCEMC.cxx index a57661b5805..b5a82a7ac55 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaEMCEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaEMCEMC.cxx @@ -29,9 +29,9 @@ using namespace o2::aod::pwgem::photonmeson::photonpair; using MyEMCClusters = soa::Join; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-emcemc"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-emcemc"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCEMCEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCEMCEMC.cxx index da93889b9db..9c4398e75a3 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCEMCEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCEMCEMC.cxx @@ -27,11 +27,11 @@ using namespace o2::aod; using namespace o2::framework; using namespace o2::aod::pwgem::photonmeson::photonpair; -using MyEMCClusters = soa::Join; +using MyEMCClusters = soa::Join; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-mc-emcemc"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-mc-emcemc"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMDalitzEE.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMDalitzEE.cxx index 573b8d0224e..67e7e83ad21 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMDalitzEE.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMDalitzEE.cxx @@ -17,7 +17,7 @@ #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" -#include +#include #include #include @@ -30,9 +30,9 @@ using MyV0Photons = soa::Filtered; using MyMCElectrons = soa::Filtered>; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-mc-pcmdalitzee"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-mc-pcmdalitzee"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx new file mode 100644 index 00000000000..6bd33481ce2 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMEMC.cxx @@ -0,0 +1,39 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file Pi0EtaToGammaGammaMCPCMEMC.cxx +/// \brief This code loops over photons and makes pairs for neutral mesons analyses in MC for PCM-EMC. +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/PhotonMeson/Core/Pi0EtaToGammaGammaMC.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/PairUtilities.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::aod::pwgem::photonmeson::photonpair; + +using MyV0Photons = o2::soa::Filtered>; +using MyMCV0Legs = soa::Join; +using MyEMCClusters = soa::Join; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-mc-pcmemc"}), + }; +} diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCM.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCM.cxx index 9ec33da1389..5b6ec38bfe8 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCM.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCM.cxx @@ -18,7 +18,7 @@ #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" -#include +#include #include #include @@ -30,9 +30,9 @@ using namespace o2::aod::pwgem::photonmeson::photonpair; using MyV0Photons = o2::soa::Filtered>; using MyMCV0Legs = soa::Join; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-mc-pcmpcm"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-mc-pcmpcm"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCMML.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCMML.cxx index c5b06741e75..45c9fd499af 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCMML.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaMCPCMPCMML.cxx @@ -17,7 +17,7 @@ #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" -#include +#include #include #include @@ -26,12 +26,12 @@ using namespace o2::aod; using namespace o2::framework; using namespace o2::aod::pwgem::photonmeson::photonpair; -using MyV0Photons = o2::soa::Filtered>; +using MyV0Photons = o2::soa::Filtered>; using MyMCV0Legs = soa::Join; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-mc-pcmpcm"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-mc-pcmpcm"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMDalitzEE.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMDalitzEE.cxx index 810be128a00..dcaed33ccec 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMDalitzEE.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMDalitzEE.cxx @@ -17,7 +17,7 @@ #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" -#include +#include #include #include @@ -30,9 +30,9 @@ using namespace o2::aod::pwgem::photonmeson::photonpair; using MyV0Photons = Filtered>; using MyPrimaryElectrons = Filtered>; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-pcmdalitzee"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-pcmdalitzee"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx index 678d0f21f47..b076a94d3db 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMEMC.cxx @@ -14,6 +14,7 @@ /// \author M. Hemmer, marvin.hemmer@cern.ch #include "PWGEM/PhotonMeson/Core/Pi0EtaToGammaGamma.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" @@ -27,12 +28,12 @@ using namespace o2::aod; using namespace o2::framework; using namespace o2::aod::pwgem::photonmeson::photonpair; -using MyV0Photons = Filtered>; -using MyEMCClusters = soa::Join; +using MyV0Photons = Filtered>; +using MyEMCClusters = soa::Join; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-pcmemc"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-pcmemc"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCM.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCM.cxx index a19c2ac5be0..03fa790e8fb 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCM.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCM.cxx @@ -29,9 +29,9 @@ using namespace o2::aod::pwgem::photonmeson::photonpair; using MyV0Photons = o2::soa::Filtered>; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-pcmpcm"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-pcmpcm"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCMML.cxx b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCMML.cxx index 2d0fa9f2120..4901f219908 100644 --- a/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCMML.cxx +++ b/PWGEM/PhotonMeson/Tasks/Pi0EtaToGammaGammaPCMPCMML.cxx @@ -28,9 +28,9 @@ using namespace o2::aod::pwgem::photonmeson::photonpair; using MyV0Photons = o2::soa::Filtered>; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask>(cfgc, TaskName{"pi0eta-to-gammagamma-pcmpcm"}), + adaptAnalysisTask>(context, TaskName{"pi0eta-to-gammagamma-pcmpcm"}), }; } diff --git a/PWGEM/PhotonMeson/Tasks/SinglePhoton.cxx b/PWGEM/PhotonMeson/Tasks/SinglePhoton.cxx index 62d2e47a234..26815f26ad5 100644 --- a/PWGEM/PhotonMeson/Tasks/SinglePhoton.cxx +++ b/PWGEM/PhotonMeson/Tasks/SinglePhoton.cxx @@ -28,6 +28,7 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" +#include #include #include #include @@ -42,7 +43,7 @@ #include #include #include -#include +#include #include #include #include @@ -92,7 +93,7 @@ struct SinglePhoton { Configurable fConfigEMEventCut{"cfgEMEventCut", "minbias", "em event cut"}; // only 1 event cut per wagon EMPhotonEventCut fEMEventCut; - static constexpr std::string_view event_types[2] = {"before", "after"}; + static constexpr std::array event_types = {"before", "after"}; OutputObj fOutputEvent{"Event"}; OutputObj fOutputPhoton{"Photon"}; // single photon @@ -108,12 +109,6 @@ struct SinglePhoton { if (context.mOptions.get("processPCM")) { fDetNames.push_back("PCM"); } - // if (context.mOptions.get("processPHOS")) { - // fDetNames.push_back("PHOS"); - // } - // if (context.mOptions.get("processEMC")) { - // fDetNames.push_back("EMC"); - // } DefinePCMCuts(); DefinePHOSCuts(); @@ -122,19 +117,19 @@ struct SinglePhoton { TString ev_cut_name = fConfigEMEventCut.value; fEMEventCut = *eventcuts::GetCut(ev_cut_name.Data()); - fOutputEvent.setObject(reinterpret_cast(fMainList->FindObject("Event"))); - fOutputPhoton.setObject(reinterpret_cast(fMainList->FindObject("Photon"))); + fOutputEvent.setObject(dynamic_cast(fMainList->FindObject("Event"))); + fOutputPhoton.setObject(dynamic_cast(fMainList->FindObject("Photon"))); } template - void add_histograms(THashList* list_photon, const std::string detname, TCuts1 const& cuts1) + void add_histograms(THashList* list_photon, std::string const& detname, TCuts1 const& cuts1) { for (auto& cut1 : cuts1) { - std::string cutname1 = cut1.GetName(); + std::string const& cutname1 = cut1.getName(); - THashList* list_photon_subsys = reinterpret_cast(list_photon->FindObject(detname.data())); + auto* list_photon_subsys = dynamic_cast(list_photon->FindObject(detname.data())); o2::aod::pwgem::photon::histogram::AddHistClass(list_photon_subsys, cutname1.data()); - THashList* list_photon_subsys_cut = reinterpret_cast(list_photon_subsys->FindObject(cutname1.data())); + auto* list_photon_subsys_cut = dynamic_cast(list_photon_subsys->FindObject(cutname1.data())); if (cfgDoFlow) { o2::aod::pwgem::photon::histogram::DefineHistograms(list_photon_subsys_cut, "singlephoton", "qvector"); } else { @@ -144,7 +139,7 @@ struct SinglePhoton { } // end of cut1 loop } - static constexpr std::string_view detnames[3] = {"PCM", "PHOS", "EMC"}; + static constexpr std::array detnames = {"PCM", "PHOS", "EMC"}; void addhistograms() { fMainList->SetOwner(true); @@ -152,18 +147,18 @@ struct SinglePhoton { // create sub lists first. o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Event"); - THashList* list_ev = reinterpret_cast(fMainList->FindObject("Event")); + auto* list_ev = dynamic_cast(fMainList->FindObject("Event")); o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Photon"); - THashList* list_photon = reinterpret_cast(fMainList->FindObject("Photon")); + auto* list_photon = dynamic_cast(fMainList->FindObject("Photon")); - for (auto& detname : fDetNames) { + for (const auto& detname : fDetNames) { LOGF(info, "Enabled detector = %s", detname.data()); o2::aod::pwgem::photon::histogram::AddHistClass(list_ev, detname.data()); - THashList* list_ev_det = reinterpret_cast(list_ev->FindObject(detname.data())); + auto* list_ev_det = dynamic_cast(list_ev->FindObject(detname.data())); for (const auto& evtype : event_types) { - THashList* list_ev_det_type = reinterpret_cast(o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_det, evtype.data())); + THashList* list_ev_det_type = o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_det, evtype.data()); if (cfgDoFlow) { o2::aod::pwgem::photon::histogram::DefineHistograms(list_ev_det_type, "Event", "qvector"); } else { @@ -188,55 +183,64 @@ struct SinglePhoton { void DefinePCMCuts() { - TString cutNamesStr = fConfigPCMCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - fPCMCuts.push_back(*pcmcuts::GetCut(cutname)); - } + if (fConfigPCMCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigPCMCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add PCM cut : %s", cutname); + fPCMCuts.push_back(*pcmcuts::GetCut(cutname)); } LOGF(info, "Number of PCM cuts = %d", fPCMCuts.size()); } void DefinePHOSCuts() { - TString cutNamesStr = fConfigPHOSCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - fPHOSCuts.push_back(*phoscuts::GetCut(cutname)); - } + if (fConfigPHOSCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigPHOSCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add PHOS cut : %s", cutname); + fPHOSCuts.push_back(*phoscuts::GetCut(cutname)); } LOGF(info, "Number of PHOS cuts = %d", fPHOSCuts.size()); } void DefineEMCCuts() { - TString cutNamesStr = fConfigEMCCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - if (std::strcmp(cutname, "custom") == 0) { - EMCPhotonCut* custom_cut = new EMCPhotonCut(cutname, cutname); - custom_cut->SetMinE(EMC_minE); - custom_cut->SetMinNCell(EMC_minNCell); - custom_cut->SetM02Range(EMC_minM02, EMC_maxM02); - custom_cut->SetTimeRange(EMC_minTime, EMC_maxTime); - - custom_cut->SetTrackMatchingEtaParams(EMC_TM_Eta->at(0), EMC_TM_Eta->at(1), EMC_TM_Eta->at(2)); - custom_cut->SetTrackMatchingPhiParams(EMC_TM_Phi->at(0), EMC_TM_Phi->at(1), EMC_TM_Phi->at(2)); - - custom_cut->SetMinEoverP(EMC_Eoverp); - custom_cut->SetUseExoticCut(EMC_UseExoticCut); - fEMCCuts.push_back(*custom_cut); - } else { - fEMCCuts.push_back(*emccuts::GetCut(cutname)); - } + if (fConfigEMCCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigEMCCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add EMC cut : %s", cutname); + if (std::strcmp(cutname, "custom") == 0) { + auto* custom_cut = new EMCPhotonCut(cutname, cutname); + custom_cut->SetMinE(EMC_minE); + custom_cut->SetMinNCell(EMC_minNCell); + custom_cut->SetM02Range(EMC_minM02, EMC_maxM02); + custom_cut->SetTimeRange(EMC_minTime, EMC_maxTime); + + custom_cut->SetTrackMatchingEtaParams(EMC_TM_Eta->at(0), EMC_TM_Eta->at(1), EMC_TM_Eta->at(2)); + custom_cut->SetTrackMatchingPhiParams(EMC_TM_Phi->at(0), EMC_TM_Phi->at(1), EMC_TM_Phi->at(2)); + + custom_cut->SetMinEoverP(EMC_Eoverp); + custom_cut->SetUseExoticCut(EMC_UseExoticCut); + fEMCCuts.push_back(*custom_cut); + } else { + fEMCCuts.push_back(*emccuts::GetCut(cutname)); } } LOGF(info, "Number of EMCal cuts = %d", fEMCCuts.size()); @@ -265,9 +269,9 @@ struct SinglePhoton { template void FillPhoton(TEvents const& collisions, TPhotons1 const& photons1, TPreslice1 const& perCollision1, TCuts1 const& cuts1, TV0Legs const&) { - THashList* list_ev_before = static_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[0].data())); - THashList* list_ev_after = static_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[1].data())); - THashList* list_photon_det = static_cast(fMainList->FindObject("Photon")->FindObject(detnames[photontype].data())); + auto* list_ev_before = dynamic_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[0].data())); + auto* list_ev_after = dynamic_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[1].data())); + auto* list_photon_det = dynamic_cast(fMainList->FindObject("Photon")->FindObject(detnames[photontype].data())); for (auto& collision : collisions) { if (photontype == EMDetType::kPHOS && !collision.alias_bit(triggerAliases::kTVXinPHOS)) { @@ -277,7 +281,7 @@ struct SinglePhoton { continue; } - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -297,8 +301,8 @@ struct SinglePhoton { } else { o2::aod::pwgem::photon::histogram::FillHistClass(list_ev_after, "", collision); } - reinterpret_cast(list_ev_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); - reinterpret_cast(list_ev_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); std::array q2ft0m = {collision.q2xft0m(), collision.q2yft0m()}; std::array q2ft0a = {collision.q2xft0a(), collision.q2yft0a()}; std::array q2ft0c = {collision.q2xft0c(), collision.q2yft0c()}; @@ -306,7 +310,7 @@ struct SinglePhoton { auto photons1_coll = photons1.sliceBy(perCollision1, collision.globalIndex()); for (auto& cut : cuts1) { - THashList* list_photon_det_cut = static_cast(list_photon_det->FindObject(cut.GetName())); + auto* list_photon_det_cut = dynamic_cast(list_photon_det->FindObject(cut.getName().c_str())); for (auto& photon : photons1_coll) { if (!IsSelected(photon, cut)) { continue; @@ -316,16 +320,16 @@ struct SinglePhoton { } if (cfgDoFlow) { std::array u2_photon = {std::cos(2 * photon.phi()), std::sin(2 * photon.phi())}; - reinterpret_cast(list_photon_det_cut->FindObject("hPt_SPQ2FT0M"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2ft0m)); - reinterpret_cast(list_photon_det_cut->FindObject("hPt_SPQ2FT0A"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2ft0a)); - reinterpret_cast(list_photon_det_cut->FindObject("hPt_SPQ2FT0C"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2ft0c)); - // reinterpret_cast(list_photon_det_cut->FindObject("hPt_SPQ2FV0A"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2fv0a)); + dynamic_cast(list_photon_det_cut->FindObject("hPt_SPQ2FT0M"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2ft0m)); + dynamic_cast(list_photon_det_cut->FindObject("hPt_SPQ2FT0A"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2ft0a)); + dynamic_cast(list_photon_det_cut->FindObject("hPt_SPQ2FT0C"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2ft0c)); + // dynamic_cast(list_photon_det_cut->FindObject("hPt_SPQ2FV0A"))->Fill(photon.pt(), RecoDecay::dotProd(u2_photon, q2fv0a)); } else { - reinterpret_cast(list_photon_det_cut->FindObject("hPt"))->Fill(photon.pt()); + dynamic_cast(list_photon_det_cut->FindObject("hPt"))->Fill(photon.pt()); } - reinterpret_cast(list_photon_det_cut->FindObject("hY"))->Fill(photon.eta()); - reinterpret_cast(list_photon_det_cut->FindObject("hPhi"))->Fill(photon.phi()); + dynamic_cast(list_photon_det_cut->FindObject("hY"))->Fill(photon.eta()); + dynamic_cast(list_photon_det_cut->FindObject("hPhi"))->Fill(photon.phi()); } // end of photon loop } // end of cut loop } // end of collision loop @@ -356,8 +360,8 @@ struct SinglePhoton { PROCESS_SWITCH(SinglePhoton, processDummy, "Dummy function", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"single-photon"})}; + adaptAnalysisTask(context, TaskName{"single-photon"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/SinglePhotonMC.cxx b/PWGEM/PhotonMeson/Tasks/SinglePhotonMC.cxx index e8e083dca28..d5619fd4edb 100644 --- a/PWGEM/PhotonMeson/Tasks/SinglePhotonMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/SinglePhotonMC.cxx @@ -17,9 +17,7 @@ #include "PWGEM/Dilepton/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Core/CutsLibrary.h" -#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" #include "PWGEM/PhotonMeson/Core/HistogramsLibrary.h" -#include "PWGEM/PhotonMeson/Core/PHOSPhotonCut.h" #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" @@ -28,6 +26,7 @@ #include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/Centrality.h" +#include #include #include #include @@ -38,8 +37,7 @@ #include #include -#include -#include +#include #include #include #include @@ -77,24 +75,10 @@ struct SinglePhotonMC { Configurable margin_z_mc{"margin_z_mc", 7.0, "margin for z cut in cm for MC"}; Configurable fConfigPCMCuts{"cfgPCMCuts", "analysis,qc,nocut", "Comma separated list of V0 photon cuts"}; - // Configurable fConfigPHOSCuts{"cfgPHOSCuts", "test02,test03", "Comma separated list of PHOS photon cuts"}; - // Configurable fConfigEMCCuts{"fConfigEMCCuts", "custom,standard,nocut", "Comma separated list of EMCal photon cuts"}; - - //// Configurable for EMCal cuts - // Configurable EMC_minTime{"EMC_minTime", -20., "Minimum cluster time for EMCal time cut"}; - // Configurable EMC_maxTime{"EMC_maxTime", +25., "Maximum cluster time for EMCal time cut"}; - // Configurable EMC_minM02{"EMC_minM02", 0.1, "Minimum M02 for EMCal M02 cut"}; - // Configurable EMC_maxM02{"EMC_maxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; - // Configurable EMC_minE{"EMC_minE", 0.7, "Minimum cluster energy for EMCal energy cut"}; - // Configurable EMC_minNCell{"EMC_minNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; - // Configurable> EMC_TM_Eta{"EMC_TM_Eta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; - // Configurable> EMC_TM_Phi{"EMC_TM_Phi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; - // Configurable EMC_Eoverp{"EMC_Eoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"}; - // Configurable EMC_UseExoticCut{"EMC_UseExoticCut", true, "FLag to use the EMCal exotic cluster cut"}; Configurable fConfigEMEventCut{"cfgEMEventCut", "minbias", "em event cut"}; // only 1 event cut per wagon EMPhotonEventCut fEMEventCut; - static constexpr std::string_view event_types[2] = {"before", "after"}; + static constexpr std::array event_types = {"before", "after"}; OutputObj fOutputEvent{"Event"}; OutputObj fOutputPhoton{"Photon"}; // single photon @@ -102,8 +86,8 @@ struct SinglePhotonMC { THashList* fMainList = new THashList(); std::vector fPCMCuts; - std::vector fPHOSCuts; - std::vector fEMCCuts; + // std::vector fPHOSCuts; + // std::vector fEMCCuts; std::vector fDetNames; void init(InitContext& context) @@ -111,39 +95,31 @@ struct SinglePhotonMC { if (context.mOptions.get("processPCM")) { fDetNames.push_back("PCM"); } - // if (context.mOptions.get("processPHOS")) { - // fDetNames.push_back("PHOS"); - // } - // if (context.mOptions.get("processEMC")) { - // fDetNames.push_back("EMC"); - // } DefinePCMCuts(); - // DefinePHOSCuts(); - // DefineEMCCuts(); addhistograms(); TString ev_cut_name = fConfigEMEventCut.value; fEMEventCut = *eventcuts::GetCut(ev_cut_name.Data()); - fOutputEvent.setObject(reinterpret_cast(fMainList->FindObject("Event"))); - fOutputPhoton.setObject(reinterpret_cast(fMainList->FindObject("Photon"))); - fOutputGen.setObject(reinterpret_cast(fMainList->FindObject("Generated"))); + fOutputEvent.setObject(dynamic_cast(fMainList->FindObject("Event"))); + fOutputPhoton.setObject(dynamic_cast(fMainList->FindObject("Photon"))); + fOutputGen.setObject(dynamic_cast(fMainList->FindObject("Generated"))); } template - void add_photon_histograms(THashList* list_photon, const std::string detname, TCuts1 const& cuts1) + void add_photon_histograms(THashList* list_photon, std::string const& detname, TCuts1 const& cuts1) { for (auto& cut1 : cuts1) { std::string cutname1 = cut1.GetName(); - THashList* list_photon_subsys = reinterpret_cast(list_photon->FindObject(detname.data())); + auto* list_photon_subsys = dynamic_cast(list_photon->FindObject(detname.data())); o2::aod::pwgem::photon::histogram::AddHistClass(list_photon_subsys, cutname1.data()); - THashList* list_photon_subsys_cut = reinterpret_cast(list_photon_subsys->FindObject(cutname1.data())); + auto* list_photon_subsys_cut = dynamic_cast(list_photon_subsys->FindObject(cutname1.data())); o2::aod::pwgem::photon::histogram::DefineHistograms(list_photon_subsys_cut, "singlephoton", "mc"); } // end of cut1 loop } - static constexpr std::string_view detnames[3] = {"PCM", "PHOS", "EMC"}; + static constexpr std::array detnames = {"PCM", "PHOS", "EMC"}; void addhistograms() { fMainList->SetOwner(true); @@ -151,22 +127,22 @@ struct SinglePhotonMC { // create sub lists first. o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Event"); - THashList* list_ev = reinterpret_cast(fMainList->FindObject("Event")); + auto* list_ev = dynamic_cast(fMainList->FindObject("Event")); o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Photon"); - THashList* list_photon = reinterpret_cast(fMainList->FindObject("Photon")); + auto* list_photon = dynamic_cast(fMainList->FindObject("Photon")); o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Generated"); - THashList* list_gen = reinterpret_cast(fMainList->FindObject("Generated")); + auto* list_gen = dynamic_cast(fMainList->FindObject("Generated")); o2::aod::pwgem::photon::histogram::DefineHistograms(list_gen, "Generated", "Photon"); - for (auto& detname : fDetNames) { + for (const auto& detname : fDetNames) { LOGF(info, "Enabled detector = %s", detname.data()); o2::aod::pwgem::photon::histogram::AddHistClass(list_ev, detname.data()); - THashList* list_ev_det = reinterpret_cast(list_ev->FindObject(detname.data())); + auto* list_ev_det = dynamic_cast(list_ev->FindObject(detname.data())); for (const auto& evtype : event_types) { - THashList* list_ev_det_type = reinterpret_cast(o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_det, evtype.data())); + THashList* list_ev_det_type = o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_det, evtype.data()); o2::aod::pwgem::photon::histogram::DefineHistograms(list_ev_det_type, "Event", evtype.data()); } @@ -175,77 +151,27 @@ struct SinglePhotonMC { if (detname == "PCM") { add_photon_histograms(list_photon, detname, fPCMCuts); } - // if (detname == "PHOS") { - // add_photon_histograms(list_photon, detname, fPHOSCuts); - // } - // if (detname == "EMC") { - // add_photon_histograms(list_photon, detname, fEMCCuts); - // } - } // end of detector name loop } void DefinePCMCuts() { - TString cutNamesStr = fConfigPCMCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - fPCMCuts.push_back(*pcmcuts::GetCut(cutname)); - } + if (fConfigPCMCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigPCMCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add tag cut : %s", cutname); + fPCMCuts.push_back(*pcmcuts::GetCut(cutname)); } LOGF(info, "Number of PCM cuts = %d", fPCMCuts.size()); } - // void DefinePHOSCuts() - // { - // TString cutNamesStr = fConfigPHOSCuts.value; - // if (!cutNamesStr.IsNull()) { - // std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - // for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - // const char* cutname = objArray->At(icut)->GetName(); - // LOGF(info, "add cut : %s", cutname); - // fPHOSCuts.push_back(*phoscuts::GetCut(cutname)); - // } - // } - // LOGF(info, "Number of PHOS cuts = %d", fPHOSCuts.size()); - // } - // - // void DefineEMCCuts() - // { - // - // TString cutNamesStr = fConfigEMCCuts.value; - // if (!cutNamesStr.IsNull()) { - // std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - // for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - // const char* cutname = objArray->At(icut)->GetName(); - // LOGF(info, "add cut : %s", cutname); - // if (std::strcmp(cutname, "custom") == 0) { - // EMCPhotonCut* custom_cut = new EMCPhotonCut(cutname, cutname); - // custom_cut->SetMinE(EMC_minE); - // custom_cut->SetMinNCell(EMC_minNCell); - // custom_cut->SetM02Range(EMC_minM02, EMC_maxM02); - // custom_cut->SetTimeRange(EMC_minTime, EMC_maxTime); - // - // custom_cut->SetTrackMatchingEtaParams(EMC_TM_Eta->at(0), EMC_TM_Eta->at(1), EMC_TM_Eta->at(2)); - // custom_cut->SetTrackMatchingPhiParams(EMC_TM_Phi->at(0), EMC_TM_Phi->at(1), EMC_TM_Phi->at(2)); - // - // custom_cut->SetMinEoverP(EMC_Eoverp); - // custom_cut->SetUseExoticCut(EMC_UseExoticCut); - // fEMCCuts.push_back(*custom_cut); - // } else { - // fEMCCuts.push_back(*emccuts::GetCut(cutname)); - // } - // } - // } - // LOGF(info, "Number of EMCal cuts = %d", fEMCCuts.size()); - // } - Preslice perCollision = aod::v0photonkf::pmeventId; - // Preslice perCollision_phos = aod::skimmedcluster::collisionId; - // Preslice perCollision_emc = aod::skimmedcluster::collisionId; template bool IsSelected(TG1 const& g1, TCut1 const& cut1) @@ -266,9 +192,9 @@ struct SinglePhotonMC { template void FillTruePhoton(TEvents const& collisions, TPhotons1 const& photons1, TPreslice1 const& perCollision1, TCuts1 const& cuts1, TV0Legs const&, TMCParticles const& mcparticles, TMCEvents const&) { - THashList* list_ev_before = static_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[0].data())); - THashList* list_ev_after = static_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[1].data())); - THashList* list_photon_det = static_cast(fMainList->FindObject("Photon")->FindObject(detnames[photontype].data())); + auto* list_ev_before = dynamic_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[0].data())); + auto* list_ev_after = dynamic_cast(fMainList->FindObject("Event")->FindObject(detnames[photontype].data())->FindObject(event_types[1].data())); + auto* list_photon_det = dynamic_cast(fMainList->FindObject("Photon")->FindObject(detnames[photontype].data())); for (auto& collision : collisions) { if (photontype == EMDetType::kPHOS && !collision.alias_bit(triggerAliases::kTVXinPHOS)) { @@ -278,7 +204,7 @@ struct SinglePhotonMC { continue; } - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities{collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -288,12 +214,12 @@ struct SinglePhotonMC { continue; } o2::aod::pwgem::photon::histogram::FillHistClass(list_ev_after, "", collision); - reinterpret_cast(list_ev_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); - reinterpret_cast(list_ev_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); auto photons1_coll = photons1.sliceBy(perCollision1, collision.globalIndex()); for (auto& cut : cuts1) { - THashList* list_photon_det_cut = static_cast(list_photon_det->FindObject(cut.GetName())); + auto* list_photon_det_cut = dynamic_cast(list_photon_det->FindObject(cut.getName().c_str())); for (auto& photon : photons1_coll) { if (!IsSelected(photon, cut)) { continue; @@ -301,9 +227,9 @@ struct SinglePhotonMC { if (abs(photon.eta()) > maxY) { continue; } - reinterpret_cast(list_photon_det_cut->FindObject("hPt"))->Fill(photon.pt()); - reinterpret_cast(list_photon_det_cut->FindObject("hY"))->Fill(photon.eta()); - reinterpret_cast(list_photon_det_cut->FindObject("hPhi"))->Fill(photon.phi()); + dynamic_cast(list_photon_det_cut->FindObject("hPt"))->Fill(photon.pt()); + dynamic_cast(list_photon_det_cut->FindObject("hY"))->Fill(photon.eta()); + dynamic_cast(list_photon_det_cut->FindObject("hPhi"))->Fill(photon.phi()); int photonid = -1; if constexpr (photontype == EMDetType::kPCM) { @@ -328,17 +254,17 @@ struct SinglePhotonMC { continue; } } - reinterpret_cast(list_photon_det_cut->FindObject("hPt_Photon_Primary"))->Fill(photon.pt()); - reinterpret_cast(list_photon_det_cut->FindObject("hY_Photon_Primary"))->Fill(photon.eta()); - reinterpret_cast(list_photon_det_cut->FindObject("hPhi_Photon_Primary"))->Fill(photon.phi()); + dynamic_cast(list_photon_det_cut->FindObject("hPt_Photon_Primary"))->Fill(photon.pt()); + dynamic_cast(list_photon_det_cut->FindObject("hY_Photon_Primary"))->Fill(photon.eta()); + dynamic_cast(list_photon_det_cut->FindObject("hPhi_Photon_Primary"))->Fill(photon.phi()); } else if (IsFromWD(mcphoton.emmcevent(), mcphoton, mcparticles) > 0) { - reinterpret_cast(list_photon_det_cut->FindObject("hPt_Photon_FromWD"))->Fill(photon.pt()); - reinterpret_cast(list_photon_det_cut->FindObject("hY_Photon_FromWD"))->Fill(photon.eta()); - reinterpret_cast(list_photon_det_cut->FindObject("hPhi_Photon_FromWD"))->Fill(photon.phi()); + dynamic_cast(list_photon_det_cut->FindObject("hPt_Photon_FromWD"))->Fill(photon.pt()); + dynamic_cast(list_photon_det_cut->FindObject("hY_Photon_FromWD"))->Fill(photon.eta()); + dynamic_cast(list_photon_det_cut->FindObject("hPhi_Photon_FromWD"))->Fill(photon.phi()); } else { - reinterpret_cast(list_photon_det_cut->FindObject("hPt_Photon_hs"))->Fill(photon.pt()); - reinterpret_cast(list_photon_det_cut->FindObject("hY_Photon_hs"))->Fill(photon.eta()); - reinterpret_cast(list_photon_det_cut->FindObject("hPhi_Photon_hs"))->Fill(photon.phi()); + dynamic_cast(list_photon_det_cut->FindObject("hPt_Photon_hs"))->Fill(photon.pt()); + dynamic_cast(list_photon_det_cut->FindObject("hY_Photon_hs"))->Fill(photon.eta()); + dynamic_cast(list_photon_det_cut->FindObject("hPhi_Photon_hs"))->Fill(photon.phi()); } } // end of photon loop @@ -369,29 +295,29 @@ struct SinglePhotonMC { // loop over mc stack and fill histograms for pure MC truth signals // all MC tracks which belong to the MC event corresponding to the current reconstructed event for (auto& collision : grouped_collisions) { - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities{collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } auto mccollision = collision.emmcevent(); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(1.0); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_before"))->Fill(mccollision.posZ()); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(1.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_before"))->Fill(mccollision.posZ()); if (!collision.sel8()) { continue; } - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(2.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(2.0); if (collision.numContrib() < 0.5) { continue; } - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(3.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(3.0); if (abs(collision.posZ()) > 10.0) { continue; } - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(4.0); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_after"))->Fill(mccollision.posZ()); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hCollisionCounter"))->Fill(4.0); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hZvtx_after"))->Fill(mccollision.posZ()); auto mctracks_coll = mcparticles.sliceBy(perMcCollision, collision.emmceventId()); for (auto& mctrack : mctracks_coll) { @@ -400,9 +326,9 @@ struct SinglePhotonMC { } if (abs(mctrack.pdgCode()) == 22 && (mctrack.isPhysicalPrimary() || mctrack.producedByGenerator())) { - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hPt_Photon"))->Fill(mctrack.pt()); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hY_Photon"))->Fill(mctrack.y()); - reinterpret_cast(fMainList->FindObject("Generated")->FindObject("hPhi_Photon"))->Fill(mctrack.phi()); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hPt_Photon"))->Fill(mctrack.pt()); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hY_Photon"))->Fill(mctrack.y()); + dynamic_cast(fMainList->FindObject("Generated")->FindObject("hPhi_Photon"))->Fill(mctrack.phi()); } } } @@ -416,8 +342,8 @@ struct SinglePhotonMC { PROCESS_SWITCH(SinglePhotonMC, processDummy, "Dummy function", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"single-photon-mc"})}; + adaptAnalysisTask(context, TaskName{"single-photon-mc"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx b/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx index 818eb101285..f43011ad92c 100644 --- a/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx +++ b/PWGEM/PhotonMeson/Tasks/TagAndProbe.cxx @@ -31,6 +31,7 @@ #include "Common/DataModel/EventSelection.h" #include +#include #include #include #include @@ -48,17 +49,15 @@ #include #include +#include #include #include #include -#include #include #include #include #include -#include - using namespace o2; using namespace o2::aod; using namespace o2::framework; @@ -89,7 +88,7 @@ struct TagAndProbe { Configurable fConfigEMEventCut{"cfgEMEventCut", "minbias", "em event cut"}; // only 1 event cut per wagon EMPhotonEventCut fEMEventCut; - static constexpr std::string_view event_types[2] = {"before", "after"}; + static constexpr std::array event_types = {"before", "after"}; OutputObj fOutputEvent{"Event"}; OutputObj fOutputPair{"Pair"}; // 2-photon pair @@ -126,48 +125,49 @@ struct TagAndProbe { TString ev_cut_name = fConfigEMEventCut.value; fEMEventCut = *eventcuts::GetCut(ev_cut_name.Data()); - fOutputEvent.setObject(reinterpret_cast(fMainList->FindObject("Event"))); - fOutputPair.setObject(reinterpret_cast(fMainList->FindObject("Pair"))); + fOutputEvent.setObject(dynamic_cast(fMainList->FindObject("Event"))); + fOutputPair.setObject(dynamic_cast(fMainList->FindObject("Pair"))); } template - void add_pair_histograms(THashList* list_pair, const std::string pairname, TTagCut const& tagcut, TProbeCuts const& probecuts, TPairCuts const& paircuts) + void add_pair_histograms(THashList* list_pair, const std::string& pairname, TTagCut const& tagcut, TProbeCuts const& probecuts, TPairCuts const& paircuts) { - std::string cutname1 = tagcut.GetName(); + const std::string& cutname1 = tagcut.getName(); for (auto& cut2 : probecuts) { - std::string cutname2 = cut2.GetName(); - std::string photon_cut_name = cutname1 + "_" + cutname2; + const std::string& cutname2 = cut2.getName(); + std::string photon_cut_name = cutname1; + photon_cut_name.append("_").append(cutname2); THashList* list_pair_subsys_photoncut = o2::aod::pwgem::photon::histogram::AddHistClass(list_pair, photon_cut_name.data()); for (auto& cut3 : paircuts) { - std::string pair_cut_name = cut3.GetName(); + std::string const& pair_cut_name = cut3.getName(); o2::aod::pwgem::photon::histogram::AddHistClass(list_pair_subsys_photoncut, pair_cut_name.data()); - THashList* list_pair_subsys_paircut = reinterpret_cast(list_pair_subsys_photoncut->FindObject(pair_cut_name.data())); + auto* list_pair_subsys_paircut = dynamic_cast(list_pair_subsys_photoncut->FindObject(pair_cut_name.data())); o2::aod::pwgem::photon::histogram::DefineHistograms(list_pair_subsys_paircut, "tag_and_probe", pairname.data()); } // end of cut3 loop pair cut } // end of cut2 loop } - static constexpr std::string_view pairnames[6] = {"PCMPCM", "PHOSPHOS", "EMCEMC", "PCMPHOS", "PCMEMC", "PHOSEMC"}; + static constexpr std::array pairnames = {"PCMPCM", "PHOSPHOS", "EMCEMC", "PCMPHOS", "PCMEMC", "PHOSEMC"}; void addhistograms() { fMainList->SetOwner(true); fMainList->SetName("fMainList"); o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Event"); - THashList* list_ev = reinterpret_cast(fMainList->FindObject("Event")); + auto* list_ev = dynamic_cast(fMainList->FindObject("Event")); o2::aod::pwgem::photon::histogram::AddHistClass(fMainList, "Pair"); - THashList* list_pair = reinterpret_cast(fMainList->FindObject("Pair")); + auto* list_pair = dynamic_cast(fMainList->FindObject("Pair")); // create sub lists first. - for (auto& pairname : fPairNames) { + for (const auto& pairname : fPairNames) { LOGF(info, "Enabled pairs = %s", pairname.data()); - THashList* list_ev_pair = reinterpret_cast(o2::aod::pwgem::photon::histogram::AddHistClass(list_ev, pairname.data())); + THashList* list_ev_pair = o2::aod::pwgem::photon::histogram::AddHistClass(list_ev, pairname.data()); for (const auto& evtype : event_types) { - THashList* list_ev_type = reinterpret_cast(o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_pair, evtype.data())); + THashList* list_ev_type = o2::aod::pwgem::photon::histogram::AddHistClass(list_ev_pair, evtype.data()); o2::aod::pwgem::photon::histogram::DefineHistograms(list_ev_type, "Event", evtype.data()); } @@ -254,12 +254,12 @@ struct TagAndProbe { template void SameEventPairing(TEvents const& collisions, TPhotons1 const& photons1, TPhotons2 const& photons2, TPreslice1 const& perCollision1, TPreslice2 const& perCollision2, TTagCut const& tagcut, TProbeCuts const& probecuts, TPairCuts const& paircuts, TLegs const& /*legs*/) { - THashList* list_ev_pair_before = static_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[0].data())); - THashList* list_ev_pair_after = static_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[1].data())); - THashList* list_pair_ss = static_cast(fMainList->FindObject("Pair")->FindObject(pairnames[pairtype].data())); + auto* list_ev_pair_before = dynamic_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[0].data())); + auto* list_ev_pair_after = dynamic_cast(fMainList->FindObject("Event")->FindObject(pairnames[pairtype].data())->FindObject(event_types[1].data())); + auto* list_pair_ss = dynamic_cast(fMainList->FindObject("Pair")->FindObject(pairnames[pairtype].data())); for (auto& collision : collisions) { - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -276,8 +276,8 @@ struct TagAndProbe { continue; } o2::aod::pwgem::photon::histogram::FillHistClass(list_ev_pair_after, "", collision); - reinterpret_cast(list_ev_pair_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); - reinterpret_cast(list_ev_pair_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_pair_before->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); + dynamic_cast(list_ev_pair_after->FindObject("hCollisionCounter"))->Fill("accepted", 1.f); auto photons1_coll = photons1.sliceBy(perCollision1, collision.globalIndex()); auto photons2_coll = photons2.sliceBy(perCollision2, collision.globalIndex()); @@ -315,7 +315,7 @@ struct TagAndProbe { if (abs(v12.Rapidity()) > maxY) { continue; } - reinterpret_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.GetName(), probecut.GetName()))->FindObject(paircut.GetName())->FindObject("hMggPt_Probe_Same"))->Fill(v12.M(), v2.Pt()); + dynamic_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.getName().c_str(), probecut.getName().c_str()))->FindObject(paircut.getName().c_str())->FindObject("hMggPt_Probe_Same"))->Fill(v12.M(), v2.Pt()); if constexpr (pairtype == PairType::kPCMPCM) { if (!probecut.template IsSelected(g2)) { @@ -331,7 +331,7 @@ struct TagAndProbe { } } - reinterpret_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.GetName(), probecut.GetName()))->FindObject(paircut.GetName())->FindObject("hMggPt_PassingProbe_Same"))->Fill(v12.M(), v2.Pt()); + dynamic_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.getName().c_str(), probecut.getName().c_str()))->FindObject(paircut.getName().c_str())->FindObject("hMggPt_PassingProbe_Same"))->Fill(v12.M(), v2.Pt()); if constexpr (pairtype == PairType::kEMCEMC) { RotationBackground(v12, v1, v2, photons2_coll, g1.globalIndex(), g2.globalIndex(), probecut, paircut); @@ -349,19 +349,19 @@ struct TagAndProbe { using BinningType_M = ColumnBinningPolicy; using BinningType_A = ColumnBinningPolicy; using BinningType_C = ColumnBinningPolicy; - BinningType_M colBinning_M{{ConfVtxBins, ConfCentBins}, true}; - BinningType_A colBinning_A{{ConfVtxBins, ConfCentBins}, true}; - BinningType_C colBinning_C{{ConfVtxBins, ConfCentBins}, true}; + BinningType_M colBinning_M{{ConfVtxBins, ConfCentBins}}; + BinningType_A colBinning_A{{ConfVtxBins, ConfCentBins}}; + BinningType_C colBinning_C{{ConfVtxBins, ConfCentBins}}; template void MixedEventPairing(TEvents const& collisions, TPhotons1 const& photons1, TPhotons2 const& photons2, TPreslice1 const& perCollision1, TPreslice2 const& perCollision2, TTagCut const& tagcut, TProbeCuts const& probecuts, TPairCuts const& paircuts, TLegs const& /*legs*/, TMixedBinning const& colBinning) { - THashList* list_pair_ss = static_cast(fMainList->FindObject("Pair")->FindObject(pairnames[pairtype].data())); + auto* list_pair_ss = dynamic_cast(fMainList->FindObject("Pair")->FindObject(pairnames[pairtype].data())); for (auto& [collision1, collision2] : soa::selfCombinations(colBinning, ndepth, -1, collisions, collisions)) { // internally, CombinationsStrictlyUpperIndexPolicy(collisions, collisions) is called. - const float centralities1[3] = {collision1.centFT0M(), collision1.centFT0A(), collision1.centFT0C()}; - const float centralities2[3] = {collision2.centFT0M(), collision2.centFT0A(), collision2.centFT0C()}; + const std::array centralities1 = {collision1.centFT0M(), collision1.centFT0A(), collision1.centFT0C()}; + const std::array centralities2 = {collision2.centFT0M(), collision2.centFT0A(), collision2.centFT0C()}; if (centralities1[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities1[cfgCentEstimator]) { continue; @@ -406,7 +406,7 @@ struct TagAndProbe { if (abs(v12.Rapidity()) > maxY) { continue; } - reinterpret_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.GetName(), probecut.GetName()))->FindObject(paircut.GetName())->FindObject("hMggPt_Probe_Mixed"))->Fill(v12.M(), v2.Pt()); + dynamic_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.getName().c_str(), probecut.getName().c_str()))->FindObject(paircut.getName().c_str())->FindObject("hMggPt_Probe_Mixed"))->Fill(v12.M(), v2.Pt()); if constexpr (pairtype == PairType::kPCMPCM) { if (!probecut.template IsSelected(g2)) { @@ -422,7 +422,7 @@ struct TagAndProbe { } } - reinterpret_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.GetName(), probecut.GetName()))->FindObject(paircut.GetName())->FindObject("hMggPt_PassingProbe_Mixed"))->Fill(v12.M(), v2.Pt()); + dynamic_cast(list_pair_ss->FindObject(Form("%s_%s", tagcut.getName().c_str(), probecut.getName().c_str()))->FindObject(paircut.getName().c_str())->FindObject("hMggPt_PassingProbe_Mixed"))->Fill(v12.M(), v2.Pt()); } // end of probe cut loop } // end of pair cut loop @@ -484,10 +484,10 @@ struct TagAndProbe { // LOG(info) << "openingAngle2_2 = " << openingAngle2_2; if (openingAngle1 > minOpenAngle) { - reinterpret_cast(fMainList->FindObject("Pair")->FindObject("EMCEMC")->FindObject(Form("%s_%s", cut.GetName(), cut.GetName()))->FindObject(paircut.GetName())->FindObject("hMggPt_Same_RotatedBkg"))->Fill(mother1.M(), mother1.Pt()); + dynamic_cast(fMainList->FindObject("Pair")->FindObject("EMCEMC")->FindObject(Form("%s_%s", cut.getName().c_str(), cut.getName().c_str()))->FindObject(paircut.getName().c_str())->FindObject("hMggPt_Same_RotatedBkg"))->Fill(mother1.M(), mother1.Pt()); } if (openingAngle2 > minOpenAngle) { - reinterpret_cast(fMainList->FindObject("Pair")->FindObject("EMCEMC")->FindObject(Form("%s_%s", cut.GetName(), cut.GetName()))->FindObject(paircut.GetName())->FindObject("hMggPt_Same_RotatedBkg"))->Fill(mother2.M(), mother2.Pt()); + dynamic_cast(fMainList->FindObject("Pair")->FindObject("EMCEMC")->FindObject(Form("%s_%s", cut.getName().c_str(), cut.getName().c_str()))->FindObject(paircut.getName().c_str())->FindObject("hMggPt_Same_RotatedBkg"))->Fill(mother2.M(), mother2.Pt()); } } } @@ -529,8 +529,8 @@ struct TagAndProbe { PROCESS_SWITCH(TagAndProbe, processDummy, "Dummy function", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"tag-and-probe"})}; + adaptAnalysisTask(context, TaskName{"tag-and-probe"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx index 12b03c22cf4..b2395f2fe65 100644 --- a/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx +++ b/PWGEM/PhotonMeson/Tasks/calibTaskEmc.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -73,7 +74,7 @@ using namespace o2::aod::pwgem::photon; enum QvecEstimator { FT0M = 0, - FT0A = 1, + FT0A, FT0C, TPCPos, TPCNeg, @@ -83,7 +84,7 @@ enum QvecEstimator { enum CentralityEstimator { None = 0, - CFT0A = 1, + CFT0A, CFT0C, CFT0M, NCentralityEstimators @@ -228,7 +229,7 @@ struct CalibTaskEmc { } correctionConfig; SliceCache cache; - o2::framework::Service ccdb; + o2::framework::Service ccdb{}; int runNow = 0; int runBefore = -1; @@ -246,8 +247,8 @@ struct CalibTaskEmc { HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - o2::emcal::Geometry* emcalGeom; - o2::emcal::BadChannelMap* mBadChannels; + o2::emcal::Geometry* emcalGeom{}; + o2::emcal::BadChannelMap* mBadChannels{}; // Constants for eta and phi ranges for the look up table static constexpr double EtaMin = -0.75, etaMax = 0.75; static constexpr int NBinsEta = 150; // 150 bins for eta @@ -255,7 +256,7 @@ struct CalibTaskEmc { static constexpr double PhiMin = 1.35, phiMax = 5.75; static constexpr int NBinsPhi = 440; // (440 bins = 0.01 step size covering most regions) - std::array lookupTable1D; + std::array lookupTable1D{}; // Usage when cfgEnableNonLin is enabled uint8_t nColl = 1; @@ -446,7 +447,7 @@ struct CalibTaskEmc { template void fillThn(const float mass, const float pt, const float cent) { - static constexpr std::string_view HistTypes[3] = {"hSparsePi0", "hSparseBkgRot", "hSparseBkgMix"}; + static constexpr std::array HistTypes = {"hSparsePi0", "hSparseBkgRot", "hSparseBkgMix"}; registry.fill(HIST(HistTypes[histType]), mass, pt, cent); } @@ -457,7 +458,7 @@ struct CalibTaskEmc { template void fillOpeningAngleHisto(const float openingAngle, const float pt, const float cent) { - static constexpr std::string_view HistTypes[3] = {"hOpeningAngleSE", "hOpeningAngleRot", "hOpeningAngleME"}; + static constexpr std::array HistTypes = {"hOpeningAngleSE", "hOpeningAngleRot", "hOpeningAngleME"}; registry.fill(HIST(HistTypes[histType]), openingAngle, pt, cent); } @@ -504,9 +505,7 @@ struct CalibTaskEmc { int iRowLast = 24; if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_HALF) { iRowLast /= 2; // 2/3 sm case - } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_THIRD) { - iRowLast /= 3; // 1/3 sm case - } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::DCAL_EXT) { + } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_THIRD || emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::DCAL_EXT) { iRowLast /= 3; // 1/3 sm case } @@ -579,7 +578,6 @@ struct CalibTaskEmc { float massCand = meson.M(); fillThn(massCand, photonCalibValue, cent); - return; } /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is @@ -706,7 +704,7 @@ struct CalibTaskEmc { fEMCCut.AreSelectedRunning(flags, clusters, matchedPrims, matchedSeconds); using BinningType = ColumnBinningPolicy; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto clustersTuple = std::make_tuple(clusters); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clustersTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -716,7 +714,7 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange() && c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange() && c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { // occupancy selection continue; } @@ -739,7 +737,7 @@ struct CalibTaskEmc { continue; } // Cut edge clusters away, similar to rotation method to ensure same acceptance is used - if (cfgDistanceToEdge.value) { + if (cfgDistanceToEdge.value > 0) { if (checkEtaPhi1D(g1.eta(), RecoDecay::constrainAngle(g1.phi())) >= cfgEMCalMapLevelBackground.value) { continue; } @@ -819,7 +817,7 @@ struct CalibTaskEmc { } // Cut edge clusters away, similar to rotation method to ensure same acceptance is used - if (cfgDistanceToEdge.value) { + if (cfgDistanceToEdge.value > 0) { if (checkEtaPhi1D(g1.eta(), RecoDecay::constrainAngle(g1.phi())) >= cfgEMCalMapLevelSameEvent.value) { continue; } @@ -868,7 +866,7 @@ struct CalibTaskEmc { return; } using BinningTypeMixed = ColumnBinningPolicy; - BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto associatedTables = std::make_tuple(clusters, pcmPhotons); @@ -889,7 +887,7 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange() && c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange() && c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { // occupancy selection continue; } @@ -908,7 +906,7 @@ struct CalibTaskEmc { continue; } // Cut edge clusters away, similar to rotation method to ensure same acceptance is used - if (cfgDistanceToEdge.value) { + if (cfgDistanceToEdge.value > 0) { if (checkEtaPhi1D(g1.eta(), RecoDecay::constrainAngle(g1.phi())) >= cfgEMCalMapLevelBackground.value) { continue; } @@ -1022,7 +1020,7 @@ struct CalibTaskEmc { return; } using BinningType = ColumnBinningPolicy; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; auto pcmPhotonTuple = std::make_tuple(pcmPhotons); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, pcmPhotonTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -1035,7 +1033,7 @@ struct CalibTaskEmc { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange() && c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange() && c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { // occupancy selection continue; } @@ -1092,7 +1090,7 @@ struct CalibTaskEmc { }; // End struct CalibTaskEmc -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/compconvbuilder.cxx b/PWGEM/PhotonMeson/Tasks/compconvbuilder.cxx index d7046d8832f..f1e3d11878a 100644 --- a/PWGEM/PhotonMeson/Tasks/compconvbuilder.cxx +++ b/PWGEM/PhotonMeson/Tasks/compconvbuilder.cxx @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -83,11 +84,11 @@ struct Compconvbuilder { EMOnly = 2, LFOnly = 3, Common = 4, - NConversionBuilder + NConversionBuilder = 5 }; - static constexpr std::string_view kConversionBuilder[NConversionBuilder] = {"EMBuilder/", "LFBuilder/", "EMOnly/", "LFOnly/", "Common/"}; - static constexpr std::string_view kEventTypes[2] = {"before/", "after/"}; + static constexpr std::array kConversionBuilder = {"EMBuilder/", "LFBuilder/", "EMOnly/", "LFOnly/", "Common/"}; + static constexpr std::array kEventTypes = {"before/", "after/"}; EMPhotonEventCut fEMEventCut; struct : ConfigurableGroup { @@ -765,9 +766,7 @@ struct Compconvbuilder { registry.fill(HIST("truePhotons/Sparse_Converted"), d1.vx(), mc.y(), d1.vz(), r, mc.phi(), mc.eta(), mc.pt()); - int id1 = mc2trk.count(d1.globalIndex()) ? mc2trk[d1.globalIndex()] : -1; - int id2 = mc2trk.count(d2.globalIndex()) ? mc2trk[d2.globalIndex()] : -1; - if (id1 < 0 || id2 < 0) { + if (!mc2trk.contains(d1.globalIndex()) || !mc2trk.contains(d2.globalIndex())) { continue; } } @@ -795,8 +794,9 @@ struct Compconvbuilder { } for (const auto& collision : collisions) { - if (!fEMEventCut.IsSelected(collision)) + if (!fEMEventCut.IsSelected(collision)) { continue; + } fillEventInfo<1, EMBuilder>(collision); @@ -818,22 +818,25 @@ struct Compconvbuilder { .emmcparticle_as(); int pid = FindCommonMotherFrom2Prongs(posmc, negmc, kPositron, kElectron, kGamma, mcparticles); - if (pid >= 0) + if (pid >= 0) { table[pid].emIt = it; + } } for (LFIt it = lfSlice.begin(); it != lfSlice.end(); ++it) { int posTrackIndex = it.posTrackId(); auto negTrackIndex = it.negTrackId(); - if (!trackToMcLabel.count(posTrackIndex) || !trackToMcLabel.count(negTrackIndex)) + if (!trackToMcLabel.contains(posTrackIndex) || !trackToMcLabel.contains(negTrackIndex)) { continue; + } auto posmc = mcparticles.iteratorAt(trackToMcLabel[posTrackIndex]); auto negmc = mcparticles.iteratorAt(trackToMcLabel[negTrackIndex]); int pid = FindCommonMotherFrom2Prongs(posmc, negmc, kPositron, kElectron, kGamma, mcparticles); - if (pid >= 0) + if (pid >= 0) { table[pid].lfIt = it; + } } for (auto const& [pid, entry] : table) { @@ -872,7 +875,7 @@ struct Compconvbuilder { PROCESS_SWITCH(Compconvbuilder, processConvV0s, "Process generated converted V0s", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfg) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfg)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx b/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx index c3f41633d9b..43f82e92041 100644 --- a/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/dalitzEEQC.cxx @@ -8,11 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -// ======================== -// -// This code runs loop over dalitz ee table for dalitz QC. -// Please write to: daiki.sekihata@cern.ch + +/// \file dalitzEEQC.cxx +/// \brief This code runs loop over dalitz ee table for dalitz QC +/// \author Daiki Sekihata, daiki.sekihata@cern.ch #include "PWGEM/Dilepton/Utils/PairUtilities.h" #include "PWGEM/PhotonMeson/Core/DalitzEECut.h" @@ -27,6 +26,7 @@ #include #include +#include #include #include #include @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -47,14 +48,13 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include #include #include #include #include -#include - using namespace o2; using namespace o2::aod; using namespace o2::framework; @@ -131,12 +131,12 @@ struct DalitzEEQC { } dileptoncuts; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; - int mRunNumber; - float d_bz; + Service ccdb{}; + int mRunNumber = 0; + float d_bz = 0; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; + static constexpr std::array event_cut_types = {"before/", "after/"}; void init(InitContext& /*context*/) { @@ -153,7 +153,7 @@ struct DalitzEEQC { ccdb->setFatalWhenNull(false); } - template + template void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { @@ -172,10 +172,11 @@ struct DalitzEEQC { } auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) + o2::parameters::GRPObject* grpo = nullptr; + o2::parameters::GRPMagField* grpmag = nullptr; + if (!skipGRPOquery) { grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); + } if (grpo) { // Fetch magnetic field from ccdb for current collision d_bz = grpo->getNominalL3Field(); @@ -192,7 +193,7 @@ struct DalitzEEQC { mRunNumber = collision.runNumber(); } - ~DalitzEEQC() {} + ~DalitzEEQC() = default; void addhistograms() { @@ -204,11 +205,11 @@ struct DalitzEEQC { const AxisSpec axis_pt{200, 0, 2, "p_{T,ee} (GeV/c)"}; fRegistry.add("Pair/same/hMvsPt", "m_{ee} vs. p_{T,ee};m_{ee} (GeV/c^{2});p_{T,ee} (GeV/c)", kTH2F, {axis_mass, axis_pt}, true); - fRegistry.add("Pair/same/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{90, 0, M_PI}, {100, 0.0f, 0.1f}}, true); + fRegistry.add("Pair/same/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{90, 0, o2::constants::math::PI}, {100, 0.0f, 0.1f}}, true); fRegistry.add("Track/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); fRegistry.add("Track/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{400, -20, 20}}, false); - fRegistry.add("Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {40, -2.0f, 2.0f}}, false); + fRegistry.add("Track/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, o2::constants::math::TwoPI}, {40, -2.0f, 2.0f}}, false); fRegistry.add("Track/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -0.1f, 0.1f}, {200, -0.1f, 0.1f}}, false); fRegistry.add("Track/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); fRegistry.add("Track/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); @@ -280,7 +281,7 @@ struct DalitzEEQC { fDileptonCut.SetTOFNsigmaElRange(dileptoncuts.cfg_min_TOFNsigmaEl, dileptoncuts.cfg_max_TOFNsigmaEl); } - template + template bool fillPairInfo(TCollision const&, TTrack1 const& t1, TTrack2 const& t2) { if (t1.trackId() == t2.trackId()) { // this is protection against pairing identical 2 tracks. This happens, when TTCA is used. TTCA can assign a track to several possible collisions. @@ -319,7 +320,7 @@ struct DalitzEEQC { return true; } - template + template void fillTrackInfo(TTrack const& track) { fRegistry.fill(HIST("Track/hPt"), track.pt()); @@ -363,7 +364,7 @@ struct DalitzEEQC { { for (auto& collision : collisions) { initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -372,7 +373,7 @@ struct DalitzEEQC { if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { + if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { continue; } o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision); @@ -398,8 +399,8 @@ struct DalitzEEQC { PROCESS_SWITCH(DalitzEEQC, processDummy, "Dummy function", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"dalitz-ee-qc"})}; + adaptAnalysisTask(context, TaskName{"dalitz-ee-qc"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx b/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx index 084b866da50..c0dd75f3545 100644 --- a/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/dalitzEEQCMC.cxx @@ -9,10 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// ======================== -// -// This code runs loop over dalitz ee table for dalitz QC. -// Please write to: daiki.sekihata@cern.ch + +/// \file dalitzEEQC.cxx +/// \brief This code runs loop over dalitz ee table for dalitz QC in MC +/// \author Daiki Sekihata, daiki.sekihata@cern.ch #include "PWGEM/Dilepton/Utils/MCUtilities.h" #include "PWGEM/Dilepton/Utils/PairUtilities.h" @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -48,14 +50,15 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include #include #include +#include #include #include #include -#include - using namespace o2; using namespace o2::aod; using namespace o2::framework; @@ -134,9 +137,9 @@ struct DalitzEEQCMC { } dileptoncuts; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; - int mRunNumber; - float d_bz; + Service ccdb{}; + int mRunNumber = 0; + float d_bz = 0; struct : ConfigurableGroup { std::string prefix = "mctrackcut_group"; @@ -146,10 +149,10 @@ struct DalitzEEQCMC { } mctrackcuts; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_cut_types[2] = {"before/", "after/"}; - static constexpr std::string_view track_types[2] = {"primary/", "secondary/"}; + static constexpr std::array event_cut_types = {"before/", "after/"}; + static constexpr std::array track_types = {"primary/", "secondary/"}; - ~DalitzEEQCMC() {} + ~DalitzEEQCMC() = default; void addhistograms() { @@ -169,7 +172,7 @@ struct DalitzEEQCMC { // reconstructed pair info fRegistry.add("Pair/sm/Photon/hMvsPt", "m_{ee} vs. p_{T,ee} ULS", kTH2F, {axis_mass, axis_pt}, true); - fRegistry.add("Pair/sm/Photon/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{90, 0, M_PI}, {100, 0.0f, 0.1f}}, false); + fRegistry.add("Pair/sm/Photon/hMvsPhiV", "m_{ee} vs. #varphi_{V};#varphi (rad.);m_{ee} (GeV/c^{2})", kTH2F, {{90, 0, o2::constants::math::PI}, {100, 0.0f, 0.1f}}, false); fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Pi0/"); fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/Eta/"); fRegistry.addClone("Pair/sm/Photon/", "Pair/sm/EtaPrime/"); @@ -180,7 +183,7 @@ struct DalitzEEQCMC { // track info fRegistry.add("Track/primary/hPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); fRegistry.add("Track/primary/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{400, -20, 20}}, false); - fRegistry.add("Track/primary/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, 2 * M_PI}, {40, -2.0f, 2.0f}}, false); + fRegistry.add("Track/primary/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{180, 0, o2::constants::math::TwoPI}, {40, -2.0f, 2.0f}}, false); fRegistry.add("Track/primary/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -0.1f, 0.1f}, {200, -0.1f, 0.1f}}, false); fRegistry.add("Track/primary/hDCAxyzSigma", "DCA xy vs. z;DCA_{xy} (#sigma);DCA_{z} (#sigma)", kTH2F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}}, false); fRegistry.add("Track/primary/hDCAxyRes_Pt", "DCA_{xy} resolution vs. pT;p_{T} (GeV/c);DCA_{xy} resolution (#mum)", kTH2F, {{200, 0, 10}, {500, 0., 500}}, false); @@ -225,7 +228,7 @@ struct DalitzEEQCMC { ccdb->setFatalWhenNull(false); } - template + template void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { @@ -244,10 +247,11 @@ struct DalitzEEQCMC { } auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) + o2::parameters::GRPObject* grpo = nullptr; + o2::parameters::GRPMagField* grpmag = nullptr; + if (!skipGRPOquery) { grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); + } if (grpo) { // Fetch magnetic field from ccdb for current collision d_bz = grpo->getNominalL3Field(); @@ -310,10 +314,10 @@ struct DalitzEEQCMC { fDileptonCut.SetTOFNsigmaElRange(dileptoncuts.cfg_min_TOFNsigmaEl, dileptoncuts.cfg_max_TOFNsigmaEl); } - template + template int FindLF(TTrack const& posmc, TTrack const& elemc, TMCParticles const& mcparticles) { - int arr[] = { + std::array arr = { FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 22, mcparticles), FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 111, mcparticles), FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 221, mcparticles), @@ -323,22 +327,16 @@ struct DalitzEEQCMC { FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 333, mcparticles), FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 443, mcparticles), FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 100443, mcparticles)}; - int size = sizeof(arr) / sizeof(*arr); - int max = *std::max_element(arr, arr + size); - return max; + return *std::ranges::max_element(arr); } - template + template bool isInAcceptance(T const& t1) { - if ((mctrackcuts.min_mcPt < t1.pt() && t1.pt() < mctrackcuts.max_mcPt) && std::fabs(t1.eta()) < mctrackcuts.max_mcEta) { - return true; - } else { - return false; - } + return ((mctrackcuts.min_mcPt < t1.pt() && t1.pt() < mctrackcuts.max_mcPt) && std::fabs(t1.eta()) < mctrackcuts.max_mcEta); } - template + template bool fillTruePairInfo(TCollision const& collision, TTrack1 const& t1, TTrack2 const& t2, TMCParticles const& mcparticles) { if (!fDileptonCut.IsSelectedTrack(t1) || !fDileptonCut.IsSelectedTrack(t2)) { @@ -471,7 +469,7 @@ struct DalitzEEQCMC { return true; } - template + template void fillTrackInfo(TTrack const& track) { auto mctrack = track.template emmcparticle_as(); @@ -521,7 +519,7 @@ struct DalitzEEQCMC { for (auto& collision : collisions) { initCCDB(collision); - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -530,7 +528,7 @@ struct DalitzEEQCMC { if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { + if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { continue; } o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision); @@ -561,7 +559,7 @@ struct DalitzEEQCMC { // all MC tracks which belong to the MC event corresponding to the current reconstructed event for (auto& collision : collisions) { - float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -569,7 +567,7 @@ struct DalitzEEQCMC { if (!fEMEventCut.IsSelected(collision)) { continue; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { + if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { continue; } auto mccollision = collision.emmcevent_as(); @@ -640,8 +638,8 @@ struct DalitzEEQCMC { PROCESS_SWITCH(DalitzEEQCMC, processDummy, "Dummy function", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"dalitz-ee-qc-mc"})}; + adaptAnalysisTask(context, TaskName{"dalitz-ee-qc-mc"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx b/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx new file mode 100644 index 00000000000..58a62d358f1 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/dileptonEffiForDalitzSkimmer.cxx @@ -0,0 +1,446 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file dielptonEffi.cxx +/// \brief calculating dilepton efficiency for dalitz analyses +/// \author Laura Gans-Bartl, laura.gans-bartl@cern.ch + +#include "PWGEM/Dilepton/DataModel/dileptonTables.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" + +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::soa; +using namespace o2::framework; + +using MyEMPrimaryElectrons = soa::Join; +using MyEMPrimaryElectron = MyEMPrimaryElectrons::iterator; + +using MyTracks = soa::Join; +using MyTrack = MyTracks::iterator; +using MyTracksMC = soa::Join; +using MyTrackMC = MyTracksMC::iterator; + +using MyMCV0Legs = soa::Join; +using MyMCV0Leg = MyMCV0Legs::iterator; + +using MyV0Photons = soa::Join; +using MyV0Photon = MyV0Photons::iterator; + +using MyCollisionsMC = soa::Join; +using MyCollisionMC = MyCollisionsMC::iterator; + +struct DileptonEffiForDalitzSkimmer { + SliceCache cache; + PresliceOptional perCollision = aod::emprimaryelectronda::pmeventId; + PresliceOptional perTracksCollision = aod::track::collisionId; + PresliceOptional perDileptonMcCollision = aod::mcparticle::mcCollisionId; + Preslice perCollisionV0 = aod::v0photonkf::pmeventId; + + HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + bool isAmbiguousTrack(const auto& track, const aod::AmbiguousTracks& ambiguousTracks) + { + for (const auto& ambiguousTrack : ambiguousTracks) { + if (ambiguousTrack.trackId() == track.globalIndex()) { + return true; + } + } + return false; + } + + void init(o2::framework::InitContext&) + { + fRegistry.add("hPtSglEpEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEpEMPrimTruePtRec", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmEMPrimTruePtRec", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglPipEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglPimEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEpMCPart", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmMCPart", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSgltpcNSigmaEl", "pT;p_{T} (GeV/c); tpcNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSgltpcNSigmaPi", "pT;p_{T} (GeV/c); tpcNSigmaPi", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSgltofNSigmaEl", "pT;p_{T} (GeV/c); tofNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + + fRegistry.add("hPtSglTracktpcNSigmaEl", "pT;p_{T} (GeV/c); tpcNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSglTracktpcNSigmaPi", "pT;p_{T} (GeV/c); tpcNSigmaPi", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + fRegistry.add("hPtSglTracktofNSigmaEl", "pT;p_{T} (GeV/c); tofNSigmaEl", kTH2F, {{1000, 0.0f, 10}, {200, -5.f, 5}}, false); + + fRegistry.add("hPtMcEtaGeneral", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtMcEtaDalitz", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtEPrimMulti", ";p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hEPrimNumOccurence", ";p_{T} (GeV/c)", kTH1F, {{11, -0.5f, 10.5}}, false); + + fRegistry.add("hPtTrackMulti", ";p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hTrackNumOccurence", ";p_{T} (GeV/c)", kTH1F, {{11, -0.5f, 10.5}}, false); + + fRegistry.add("hPtSglEpTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEmFromTrueEtaGen", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtGammaFromTrueEtaGen", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglEmFromTrueEtaRecoRecoPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglEmFromTrueEtaRecoGenPt", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtGammaFromTrueEtaReco", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hPtSglPipTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("hPtSglPimTrackEMPrimTrue", "pT;p_{T} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + + fRegistry.add("hMcPartsPerColl", " ;N_{tracks}/collision; N", kTH1F, {{400, -0.5, 400.5}}, false); + + fRegistry.add("hCheckAsso", "pT;p_{T} (GeV/c)", kTH1F, {{2, -0.5f, 1.5}}, false); + } + void processReco(aod::PMEvents const& collisions, MyEMPrimaryElectrons const& emprimaryelectrons, aod::EMMCParticles_001 const& emmcparticle) // aod::EMEvents_005, aod::EMMCParticles_001 + { + LOG(info) << "started process"; + if (emprimaryelectrons.size() <= 0 || emmcparticle.size() <= 0) { + return; + } + LOG(info) << "size emprimaryelectrons: " << emprimaryelectrons.size(); + + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgPip = -211; + const int pdgPim = 211; + auto iterMC = emmcparticle.begin(); + std::unordered_map mapMultCount; + + //**Single electrons and positrons**/ + for (auto const& collision : collisions) { + auto slicedParticles = emprimaryelectrons.sliceBy(perCollision, collision.globalIndex()); + + for (const auto& ePrimary : slicedParticles) { + if (!std::isfinite(ePrimary.pt())) { // || std::abs(ePrimary.eta()) > 0.8 + continue; + } + + fRegistry.fill(HIST("hPtSgltpcNSigmaEl"), ePrimary.pt(), ePrimary.tpcNSigmaEl()); + fRegistry.fill(HIST("hPtSgltpcNSigmaPi"), ePrimary.pt(), ePrimary.tpcNSigmaPi()); + fRegistry.fill(HIST("hPtSgltofNSigmaEl"), ePrimary.pt(), ePrimary.tofNSigmaEl()); + + const auto mcId = ePrimary.emmcparticleId(); + + if (mcId < 0 || mcId >= emmcparticle.size()) { + continue; + } + iterMC.setCursor(mcId); + mapMultCount[static_cast(iterMC.globalIndex())]++; + + if (!iterMC.isPhysicalPrimary() || fabs(iterMC.y()) > 0.8) { + continue; + } + + if (iterMC.pdgCode() == pdgEp) { + fRegistry.fill(HIST("hPtSglEpEMPrimTrue"), iterMC.pt()); + fRegistry.fill(HIST("hPtSglEpEMPrimTruePtRec"), ePrimary.pt()); + } + if (iterMC.pdgCode() == pdgEm) { + fRegistry.fill(HIST("hPtSglEmEMPrimTrue"), iterMC.pt()); + fRegistry.fill(HIST("hPtSglEmEMPrimTruePtRec"), ePrimary.pt()); + } + if (iterMC.pdgCode() == pdgPip) { + fRegistry.fill(HIST("hPtSglPipEMPrimTrue"), iterMC.pt()); + } + if (iterMC.pdgCode() == pdgPim) { + fRegistry.fill(HIST("hPtSglPimEMPrimTrue"), iterMC.pt()); + } + } + } + // uint32_t maxNumOccurrences = 0; + // uint32_t mcIndexWithMaxTracks = 0; + // uint32_t numOkMc = 0; + // uint32_t numNotOkMc = 0; + + // for (const auto& [mcGlobalIndex, numOccurrences] : mapMultCount) { + // if (numOccurrences > 1) { + // iterMC.setCursor(mcGlobalIndex); + // fRegistry.fill(HIST("hPtEPrimMulti"), iterMC.pt()); + // fRegistry.fill(HIST("hEPrimNumOccurence"), numOccurrences); + // numNotOkMc++; + // } else { + // numOkMc++; + // } + // if (numOccurrences > maxNumOccurrences) { + // maxNumOccurrences = numOccurrences; + // mcIndexWithMaxTracks = mcGlobalIndex; + // } + // } + // LOG(info) << "numOkMc: " << numOkMc; + // LOG(info) << "numNotOkMc: " << numNotOkMc; + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processReco, "process for reco table emprimary", false); + + void processTracks(aod::Collisions const& collisions, aod::McParticles const& mcParticles, MyTracksMC const& tracks) + { + if (tracks.size() <= 0 || mcParticles.size() <= 0) { + return; + } + LOG(info) << "size tracks: " << tracks.size(); + + ////**Analysing tracks **/ + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgPip = -211; + const int pdgPim = 211; + auto iterMCTrack = mcParticles.begin(); + + std::unordered_map mapMultCount; + ////******Tracks before process function******//// + for (auto const& collision : collisions) { + auto slicedTracks = tracks.sliceBy(perTracksCollision, collision.globalIndex()); + for (const auto& track : slicedTracks) { + if (!track.has_mcParticle() || !std::isfinite(track.pt())) { //|| std::abs(track.eta()) > 0.8 + continue; + } + + fRegistry.fill(HIST("hPtSglTracktpcNSigmaEl"), track.pt(), track.tpcNSigmaEl()); + fRegistry.fill(HIST("hPtSglTracktpcNSigmaPi"), track.pt(), track.tpcNSigmaPi()); + fRegistry.fill(HIST("hPtSglTracktofNSigmaEl"), track.pt(), track.tofNSigmaEl()); + + const auto mcId = track.mcParticleId(); + + if (mcId < 0 || mcId >= mcParticles.size()) { + continue; + } + + iterMCTrack.setCursor(mcId); + mapMultCount[static_cast(iterMCTrack.globalIndex())]++; + + if (!iterMCTrack.isPhysicalPrimary() || fabs(iterMCTrack.y()) > 0.8) { + continue; + } + + if (iterMCTrack.pdgCode() == pdgEp) { + fRegistry.fill(HIST("hPtSglEpTrackEMPrimTrue"), iterMCTrack.pt()); + } + + if (iterMCTrack.pdgCode() == pdgEm) { + fRegistry.fill(HIST("hPtSglEmTrackEMPrimTrue"), iterMCTrack.pt()); + } + if (iterMCTrack.pdgCode() == pdgPip) { + fRegistry.fill(HIST("hPtSglPipTrackEMPrimTrue"), iterMCTrack.pt()); + } + if (iterMCTrack.pdgCode() == pdgPim) { + fRegistry.fill(HIST("hPtSglPimTrackEMPrimTrue"), iterMCTrack.pt()); + } + } + } + // uint32_t maxNumOccurrences = 0; + // uint64_t mcIndexWithMaxTracks = 0; + // uint32_t numOkMc = 0; + // uint64_t numNotOkMc = 0; + + // for (const auto& [mcGlobalIndex, numOccurrences] : mapMultCount) { + // if (numOccurrences > 1) { + // iterMCTrack.setCursor(mcGlobalIndex); + // // LOG(info) << "Track | Global Index: " << iterMCTrack.globalIndex(); + // fRegistry.fill(HIST("hPtTrackMulti"), iterMCTrack.pt()); + // fRegistry.fill(HIST("hTrackNumOccurence"), numOccurrences); + // numNotOkMc++; + // } else { + // numOkMc++; + // } + // if (numOccurrences > maxNumOccurrences) { + // maxNumOccurrences = numOccurrences; + // mcIndexWithMaxTracks = mcGlobalIndex; + // } + // } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processTracks, "process for reco MyTracks", true); + + using FilteredMyCollisionsMC = soa::Filtered; + void processGens(aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, aod::PMEvents const& pmcollisions, MyEMPrimaryElectrons const& emprimaryelectrons, aod::EMMCParticles_001 const& emmcparticle) + { + LOG(info) << "started process MC"; + if (mcParticles.size() <= 0 || emprimaryelectrons.size() <= 0 || emmcparticle.size() <= 0) { + return; + } + LOG(info) << " size mcParticles: " << mcParticles.size(); + auto iterChild = mcParticles.begin(); + auto iterMom = mcParticles.begin(); + + const int pdgEp = -11; + const int pdgEm = 11; + const int pdgEta = 221; + const int pdgGamma = 22; + + ////******generated Dileptons******//// + std::vector vecEpMCParticles; + vecEpMCParticles.reserve(1e5); + std::vector vecEmMCParticles; + vecEpMCParticles.reserve(1e5); + + std::vector vecEmMCPartPt; + vecEmMCPartPt.reserve(1e5); + + //**Single electrons and positrons**// + for (auto const& mcCollision : mcCollisions) { + auto slicedMcParticles = mcParticles.sliceBy(perDileptonMcCollision, mcCollision.globalIndex()); + fRegistry.fill(HIST("hMcPartsPerColl"), slicedMcParticles.size()); + for (const auto& mcParticle : slicedMcParticles) { + if (!mcParticle.isPhysicalPrimary() || fabs(mcParticle.y()) > 0.8) { + continue; + } + vecEmMCPartPt.push_back(mcParticle.pt()); + + // const float dx = mcParticle.vx(); //- mcCollision.posX() + // const float dy = mcParticle.vy(); + // const float dz = mcParticle.vz(); + + // const float prodDist = sqrt(dx * dx + dy * dy); //+ dz * dz + + if (mcParticle.pdgCode() == pdgEp) { + if (!std::isfinite(mcParticle.pt())) { + continue; + } + fRegistry.fill(HIST("hPtSglEpMCPart"), mcParticle.pt()); + vecEpMCParticles.push_back(mcParticle.globalIndex()); + } + if (mcParticle.pdgCode() == pdgEm) { + if (!std::isfinite(mcParticle.pt())) { + continue; + } + fRegistry.fill(HIST("hPtSglEmMCPart"), mcParticle.pt()); + vecEmMCParticles.push_back(mcParticle.globalIndex()); + } + + ////******Investigating Eta Daltz Decay******//// + if (!mcParticle.has_mothers()) { + continue; + } + if (mcParticle.mothersIds()[0] < 0 || mcParticle.mothersIds()[0] >= mcParticles.size()) { + continue; + } + iterMom.setCursor(mcParticle.mothersIds()[0]); + if (iterMom.pdgCode() != pdgEta) { + continue; + } + fRegistry.fill(HIST("hPtMcEtaGeneral"), iterMom.pt()); + + if (!iterMom.has_daughters()) { + continue; + } + bool childEp = false, childEm = false, childG = false; + double ptEmFromDalitzEta = -1; + double ptGammaFromDalitzEta = -1; + for (int childId = iterMom.daughtersIds()[0]; childId <= iterMom.daughtersIds()[1]; ++childId) { + iterChild.setCursor(childId); + if (iterChild.pdgCode() == pdgEp) { + childEp = true; + } + if (iterChild.pdgCode() == pdgEm) { + childEm = true; + ptEmFromDalitzEta = iterChild.pt(); + } + if (iterChild.pdgCode() == pdgGamma) { + childG = true; + ptGammaFromDalitzEta = iterChild.pt(); + } + } + if (childEp && childEm && childG) { + fRegistry.fill(HIST("hPtMcEtaDalitz"), iterMom.pt()); + fRegistry.fill(HIST("hPtSglEmFromTrueEtaGen"), ptEmFromDalitzEta); + fRegistry.fill(HIST("hPtGammaFromTrueEtaGen"), ptGammaFromDalitzEta); + } + } + if (vecEpMCParticles.empty() || vecEmMCParticles.empty()) { + continue; + } + } + + // Reco loop Elektronen + auto iterMC = emmcparticle.begin(); + auto iterMomE = emmcparticle.begin(); + for (auto const& pmcollision : pmcollisions) { + auto slicedParticles = emprimaryelectrons.sliceBy(perCollision, pmcollision.globalIndex()); + + for (const auto& ePrimary : slicedParticles) { + if (!std::isfinite(ePrimary.pt()) || fabs(iterMC.y()) > 0.8) { // || std::abs(ePrimary.eta()) > 0.8 + continue; + } + const auto mcId = ePrimary.emmcparticleId(); + iterMC.setCursor(mcId); + + if (!iterMC.isPhysicalPrimary() || fabs(iterMC.y()) > 0.8) { + continue; + } + + if (iterMC.pdgCode() != pdgEm) { + continue; + } + + if (!iterMC.has_mothers()) { + continue; + } + iterMomE.setCursor(iterMC.mothersIds()[0]); + if (iterMomE.pdgCode() != pdgEta) { + continue; + } + fRegistry.fill(HIST("hPtSglEmFromTrueEtaRecoRecoPt"), ePrimary.pt()); + fRegistry.fill(HIST("hPtSglEmFromTrueEtaRecoGenPt"), iterMC.pt()); // to do!!!!!!!!!!!!!!! + } + } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processGens, "process for generated (MCParticles)", false); + + void processCheckAssociation(MyEMPrimaryElectrons const& emprimaryelectrons, MyTracksMC const& tracks) + { + auto iterTrack = tracks.begin(); + LOG(info) << "start process Association"; + for (const auto& ePrimary : emprimaryelectrons) { + int idEm = ePrimary.emmcparticleId(); + iterTrack.setCursor(ePrimary.trackId()); + int idTrack = iterTrack.mcParticleId(); + + if (idEm == idTrack) { + fRegistry.fill(HIST("hCheckAsso"), 1); + } else { + fRegistry.fill(HIST("hCheckAsso"), 0); + } + } + } + PROCESS_SWITCH(DileptonEffiForDalitzSkimmer, processCheckAssociation, "checks if McParticle association is valid", false); +}; + +WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"dilepton-effi-for-dalitz-skimmer"})}; +} diff --git a/PWGEM/PhotonMeson/Tasks/emcalBcWiseGammaGamma.cxx b/PWGEM/PhotonMeson/Tasks/emcalBcWiseGammaGamma.cxx index 1bc980ed4bc..a189ecc8466 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalBcWiseGammaGamma.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalBcWiseGammaGamma.cxx @@ -8,12 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -/// + /// \file emcalBcWiseGammaGamma.cxx /// \brief Task that extracts pi0s and eta mesons from BC wise derived data of EMCal clusters /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) Goethe University Frankfurt -/// #include "PWGEM/PhotonMeson/DataModel/bcWiseTables.h" @@ -22,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -35,11 +34,12 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include -#include +#include #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -78,7 +78,7 @@ struct EmcalBcWiseGammaGamma { Filter m02Filter = (aod::bcwisecluster::storedNCells == 1 || (aod::bcwisecluster::storedM02 > cfgMinM02 && aod::bcwisecluster::storedM02 < cfgMaxM02)); Filter timeFilter = (aod::bcwisecluster::storedTime > cfgMinTime && aod::bcwisecluster::storedTime < cfgMaxTime); - emcal::Geometry* emcalGeom; + emcal::Geometry* emcalGeom = nullptr; void init(InitContext const&) { @@ -86,10 +86,10 @@ struct EmcalBcWiseGammaGamma { const int nEventBins = 6; mHistManager.add("Event/nBCs", "Number of BCs;;#bf{FT0M centrality (%)};#bf{#it{N}_{BC}}", HistType::kTH2F, {{nEventBins, -0.5, 5.5}, cfgCentralityBinning}); mHistManager.add("Event/nCollisions", "Number of Collisions (BCs x P(mu));;#bf{FT0M centrality (%)};#bf{#it{N}_{coll}}", HistType::kTH2F, {{nEventBins, -0.5, 5.5}, cfgCentralityBinning}); - const TString binLabels[nEventBins] = {"All", "FT0", "TVX", "kTVXinEMC", "Cell", "Cluster"}; + const std::array binLabels = {"All", "FT0", "TVX", "kTVXinEMC", "Cell", "Cluster"}; for (int iBin = 0; iBin < nEventBins; iBin++) { - mHistManager.get(HIST("Event/nBCs"))->GetXaxis()->SetBinLabel(iBin + 1, binLabels[iBin]); - mHistManager.get(HIST("Event/nCollisions"))->GetXaxis()->SetBinLabel(iBin + 1, binLabels[iBin]); + mHistManager.get(HIST("Event/nBCs"))->GetXaxis()->SetBinLabel(iBin + 1, binLabels[iBin].data()); + mHistManager.get(HIST("Event/nCollisions"))->GetXaxis()->SetBinLabel(iBin + 1, binLabels[iBin].data()); } mHistManager.add("Event/nCollPerBC", "Number of collisions per BC;#bf{#it{N}_{coll}};#bf{FT0M centrality (%)};#bf{#it{N}_{BC}}", HistType::kTH2F, {{5, -0.5, 4.5}, cfgCentralityBinning}); @@ -137,21 +137,24 @@ struct EmcalBcWiseGammaGamma { float getCentrality(const auto& bc) { - if (cfgCentralityEstimator == 0) + if (cfgCentralityEstimator == 0) { return bc.ft0cCentrality(); - else if (cfgCentralityEstimator == 1) + } + if (cfgCentralityEstimator == 1) { return bc.ft0mCentrality(); - else - throw std::runtime_error("Unknown centrality estimator selected"); + } + throw std::runtime_error("Unknown centrality estimator selected"); } /// \brief returns if cluster is too close to edge of EMCal bool isTooCloseToEdge(const int cellID, const int DistanceToBorder = 1) { - if (DistanceToBorder <= 0) + if (DistanceToBorder <= 0) { return false; - if (cellID < 0) + } + if (cellID < 0) { return true; + } // check distance to border in case the cell is okay auto [iSupMod, iMod, iPhi, iEta] = emcalGeom->GetCellIndex(cellID); @@ -218,20 +221,24 @@ struct EmcalBcWiseGammaGamma { ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - if (std::fabs(v12.Rapidity()) > cfgRapidityCut) + if (std::fabs(v12.Rapidity()) > cfgRapidityCut) { continue; + } float openingAngle12 = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); - if (openingAngle12 < cfgMinOpenAngle) + if (openingAngle12 < cfgMinOpenAngle) { continue; + } mHistManager.fill(HIST("GG/invMassVsPt"), v12.M(), v12.Pt(), getCentrality(bc)); - if (clusters.size() < 3) + if (clusters.size() < 3) { continue; + } - if (bc.globalIndex() % cfgBGEventDownsampling != 0) + if (bc.globalIndex() % cfgBGEventDownsampling != 0) { continue; + } // "else: Calculate background" @@ -243,21 +250,24 @@ struct EmcalBcWiseGammaGamma { try { int iCellID = emcalGeom->GetAbsCellIdFromEtaPhi(vi.Eta(), vi.Phi()); - if (isTooCloseToEdge(iCellID, cfgDistanceToEdge)) + if (isTooCloseToEdge(iCellID, cfgDistanceToEdge)) { continue; - } catch (o2::emcal::InvalidPositionException& e) { + } + } catch (o2::emcal::InvalidPositionException const& e) { continue; } for (const auto& g3 : clusters) { - if (g3.globalIndex() == g1.globalIndex() || g3.globalIndex() == g2.globalIndex()) + if (g3.globalIndex() == g1.globalIndex() || g3.globalIndex() == g2.globalIndex()) { continue; + } ROOT::Math::PtEtaPhiMVector v3(g3.pt(), g3.eta(), g3.phi(), 0.); float openingAnglei3 = std::acos(vi.Vect().Dot(v3.Vect()) / (vi.P() * v3.P())); - if (openingAnglei3 < cfgMinOpenAngle) + if (openingAnglei3 < cfgMinOpenAngle) { continue; + } ROOT::Math::PtEtaPhiMVector vBG = v3 + vi; @@ -269,57 +279,67 @@ struct EmcalBcWiseGammaGamma { void reconstructTrueMesons(const auto& clusters, const auto& mcPi0s, const auto& mcEtas, const auto& bc) { for (const auto& [g1, g2] : soa::combinations(soa::CombinationsStrictlyUpperIndexPolicy(clusters, clusters))) { - if (g1.mesonID() != g2.mesonID() || g1.mesonID() == -1) + if (g1.mesonID() != g2.mesonID() || g1.mesonID() == -1) { continue; + } ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector v12 = v1 + v2; - if (std::fabs(v12.Rapidity()) > cfgRapidityCut) + if (std::fabs(v12.Rapidity()) > cfgRapidityCut) { continue; + } float openingAngle12 = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); - if (openingAngle12 < cfgMinOpenAngle) + if (openingAngle12 < cfgMinOpenAngle) { continue; + } if (!g1.isEta()) { const auto& mcPi0 = mcPi0s.iteratorAt(g1.mesonID() - mcPi0s.offset()); mHistManager.fill(HIST("True/pi0_PtRecVsPtTrue"), v12.Pt(), mcPi0.pt(), getCentrality(bc)); - if (mcPi0.isPrimary()) + if (mcPi0.isPrimary()) { mHistManager.fill(HIST("True/pi0_invMassVsPt_Primary"), v12.M(), v12.Pt(), getCentrality(bc)); - else if (mcPi0.isFromWD()) + } else if (mcPi0.isFromWD()) { mHistManager.fill(HIST("True/pi0_invMassVsPt_Secondary"), v12.M(), v12.Pt(), getCentrality(bc)); - else + } else { mHistManager.fill(HIST("True/pi0_invMassVsPt_HadronicShower"), v12.M(), v12.Pt(), getCentrality(bc)); + } } else { const auto& mcEta = mcEtas.iteratorAt(g1.mesonID() - mcEtas.offset()); mHistManager.fill(HIST("True/eta_PtRecVsPtTrue"), v12.Pt(), mcEta.pt(), getCentrality(bc)); - if (mcEta.isPrimary()) + if (mcEta.isPrimary()) { mHistManager.fill(HIST("True/eta_invMassVsPt_Primary"), v12.M(), v12.Pt(), getCentrality(bc)); - else if (mcEta.isFromWD()) + } else if (mcEta.isFromWD()) { mHistManager.fill(HIST("True/eta_invMassVsPt_Secondary"), v12.M(), v12.Pt(), getCentrality(bc)); - else + } else { mHistManager.fill(HIST("True/eta_invMassVsPt_HadronicShower"), v12.M(), v12.Pt(), getCentrality(bc)); + } } } } bool isBCSelected(const auto& bc, const auto& collisions) { - if (cfgRequirekTVXinEMC && !bc.haskTVXinEMC()) + if (cfgRequirekTVXinEMC && !bc.haskTVXinEMC()) { return false; - if (cfgRequireEMCCell && !bc.hasEMCCell()) + } + if (cfgRequireEMCCell && !bc.hasEMCCell()) { return false; - if (cfgSelectOnlyUniqueAmbiguous == 1 && collisions.size() != 1) + } + if (cfgSelectOnlyUniqueAmbiguous == 1 && collisions.size() != 1) { return false; - if (cfgSelectOnlyUniqueAmbiguous == 2 && collisions.size() == 1) + } + if (cfgSelectOnlyUniqueAmbiguous == 2 && collisions.size() == 1) { return false; - if (cfgMinTimeSinceSOF > bc.timeSinceSOF() / 60 || cfgMaxTimeSinceSOF < bc.timeSinceSOF() / 60) + } + if (cfgMinTimeSinceSOF > bc.timeSinceSOF() / 60 || cfgMaxTimeSinceSOF < bc.timeSinceSOF() / 60) { return false; + } return true; } @@ -328,35 +348,44 @@ struct EmcalBcWiseGammaGamma { for (const auto& mcPi0 : mcPi0s) { if (mcPi0.isPrimary()) { mHistManager.fill(HIST("Generated/pi0_AllBCs"), mcPi0.pt(), getCentrality(bc)); - if (bc.hasFT0()) + if (bc.hasFT0()) { mHistManager.fill(HIST("Generated/pi0_FT0"), mcPi0.pt(), getCentrality(bc)); - if (bc.hasTVX()) + } + if (bc.hasTVX()) { mHistManager.fill(HIST("Generated/pi0_TVX"), mcPi0.pt(), getCentrality(bc)); - if (bc.haskTVXinEMC()) + } + if (bc.haskTVXinEMC()) { mHistManager.fill(HIST("Generated/pi0_kTVXinEMC"), mcPi0.pt(), getCentrality(bc)); - if (mcPi0.isAccepted() && bc.haskTVXinEMC()) + } + if (mcPi0.isAccepted() && bc.haskTVXinEMC()) { mHistManager.fill(HIST("Accepted/pi0_kTVXinEMC"), mcPi0.pt(), getCentrality(bc)); + } } } for (const auto& mcEta : mcEtas) { if (mcEta.isPrimary()) { mHistManager.fill(HIST("Generated/eta_AllBCs"), mcEta.pt(), getCentrality(bc)); - if (bc.hasFT0()) + if (bc.hasFT0()) { mHistManager.fill(HIST("Generated/eta_FT0"), mcEta.pt(), getCentrality(bc)); - if (bc.hasTVX()) + } + if (bc.hasTVX()) { mHistManager.fill(HIST("Generated/eta_TVX"), mcEta.pt(), getCentrality(bc)); - if (bc.haskTVXinEMC()) + } + if (bc.haskTVXinEMC()) { mHistManager.fill(HIST("Generated/eta_kTVXinEMC"), mcEta.pt(), getCentrality(bc)); - if (mcEta.isAccepted() && bc.haskTVXinEMC()) + } + if (mcEta.isAccepted() && bc.haskTVXinEMC()) { mHistManager.fill(HIST("Accepted/eta_kTVXinEMC"), mcEta.pt(), getCentrality(bc)); + } } } } void process(aod::BCWiseBCs::iterator const& bc, aod::BCWiseCollisions const& collisions, SelectedClusters const& clusters) { - if (!isBCSelected(bc, collisions)) + if (!isBCSelected(bc, collisions)) { return; + } fillEventHists(bc, collisions, clusters); @@ -367,20 +396,23 @@ struct EmcalBcWiseGammaGamma { void processMCInfo(aod::BCWiseBCs::iterator const& bc, aod::BCWiseCollisions const& collisions, SelectedMCClusters const& clusters, aod::BCWiseMCPi0s const& mcPi0s, aod::BCWiseMCEtas const& mcEtas) { - if (!cfgIsMC) + if (!cfgIsMC) { LOG(fatal) << "MC processing is not enabled, but the task is running on MC data. Please set cfgIsMC to true."; + } fillGeneratedMesonHists(mcPi0s, mcEtas, bc); // Fill before BC selection to also store pi0s and eta mesons in BCs that were not triggered - if (!isBCSelected(bc, collisions)) + if (!isBCSelected(bc, collisions)) { return; + } - for (const auto& cluster : clusters) + for (const auto& cluster : clusters) { mHistManager.fill(HIST("True/clusterERecVsETrue"), cluster.e(), cluster.trueE(), getCentrality(bc)); + } reconstructTrueMesons(clusters, mcPi0s, mcEtas, bc); } PROCESS_SWITCH(EmcalBcWiseGammaGamma, processMCInfo, "Run true and gen", false); }; -WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } +WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) { return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/emcalMcSanityCheck.cxx b/PWGEM/PhotonMeson/Tasks/emcalMcSanityCheck.cxx new file mode 100644 index 00000000000..ce546e6a843 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/emcalMcSanityCheck.cxx @@ -0,0 +1,288 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file emcalMcSanityCheck.cxx +/// \brief Task to test MC info for EMCal during different stages of out workflow +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/emcalHistoDefinitions.h" +#include "PWGJE/DataModel/EMCALClusters.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +constexpr float PointOnePercent = 0.001f; +constexpr float OnePercent = 0.01f; +constexpr float TenPercent = 0.1f; +constexpr float NinetyPercent = 0.9f; +constexpr float OneHundredTenPercent = 1.1f; + +// LSB from packing in MinClusters is 1 MeV +constexpr float EnergyPackingLsb = 1.f / o2::aod::emcdownscaling::downscalingFactors[o2::aod::emcdownscaling::kEnergy]; + +// Rounding error from packing should be half the LSB so 0.5 MeV +constexpr float EnergyComparisonEpsilon = 0.5f * EnergyPackingLsb; + +// uint16_t saturates at 65535 so max value of energy for MinClusters should be 65.535 GeV +constexpr float EnergyPackingSaturationValue = static_cast(std::numeric_limits::max()) * EnergyPackingLsb; + +enum class EnergyComparisonStatus : uint8_t { + Match = 0, // agrees within half a packing LSB + Mismatch = 1, // disagrees beyond epsilon, and not explained by saturation + Saturated = 2 // reference energy exceeded the uint16_t packing range +}; + +enum class McParticleComparisonStatus : uint8_t { + Match = 0, // same particle species, energy within epsilon, cluster fraction same + DiffSpecies = 1, // not the same species + DiffEnergy = 2, // energy not within epsilon + DiffFraction = 3 // cluster fraction not within epsilon +}; + +enum class EnergyRatioStatus : uint8_t { + PointOnePercent = 0, + OnePercent, + TenPercent, + NinetyPercent, + Ok, + OneHundredTenPercent +}; + +struct EmcalMcSanityCheck { + + using BeforeSkimmerCluster = soa::Join; + using AfterSkimmerCluster = soa::Join; + using AfterAssociateCluster = soa::Join; + using BeforeAfterAssociateCluster = soa::Join; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + void init(InitContext&) + { + const AxisSpec axisParticleClusterFracRatioStatus{6, -0.5, 5.5}; + + auto hParticleClusterFracRatioStatus = registry.add("BeforeSkimmer/hParticleClusterFracRatioStatus", "hParticleClusterFracRatioStatus;;counts", HistType::kTH1D, {axisParticleClusterFracRatioStatus}); + hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(1, "#it{E}_{part}/(frac#it{E}_{clus}) < 0.001"); + hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(2, "0.001 #leq #it{E}_{part}/(frac#it{E}_{clus}) < 0.01"); + hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(3, "0.01 #leq #it{E}_{part}/(frac#it{E}_{clus}) < 0.1"); + hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(4, "0.1 #leq #it{E}_{part}/(frac#it{E}_{clus}) < 0.9"); + hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(5, "0.9 #leq #it{E}_{part}/(frac#it{E}_{clus}) < 1.1"); + hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(6, "#it{E}_{part}/(frac#it{E}_{clus}) > 1.1"); + + if (doprocessAfterSkimmer) { + registry.addClone("BeforeSkimmer/", "AfterSkimmer/"); + } + + if (doprocessAfterAssociation) { + registry.addClone("BeforeSkimmer/", "AfterAssociate/"); + } + + if (doprocessCompareBeforeAfterAssociate) { + auto hMcParticleStatus = registry.add("hMcParticleStatus", "hMcParticleStatus;;counts", HistType::kTH1D, {{4, -0.5, 3.5}}); + hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast(McParticleComparisonStatus::Match) + 1, "Match"); + hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast(McParticleComparisonStatus::DiffSpecies) + 1, "DiffSpecies"); + hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast(McParticleComparisonStatus::DiffEnergy) + 1, "DiffEnergy"); + hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast(McParticleComparisonStatus::DiffFraction) + 1, "DiffFraction"); + } + }; // end init + + EnergyRatioStatus getEnergyRatioStatus(float clusterE, float mcParticleE, float frac) + { + float ratio = mcParticleE / (clusterE * frac); + // the most likely outcome due to hadrons not depositing their full energy + if (ratio > OneHundredTenPercent) [[likely]] { + return EnergyRatioStatus::OneHundredTenPercent; + } + if (NinetyPercent <= ratio && ratio < OneHundredTenPercent) { + return EnergyRatioStatus::Ok; + } + if (TenPercent <= ratio && ratio < NinetyPercent) { + return EnergyRatioStatus::NinetyPercent; + } + if (OnePercent <= ratio && ratio < TenPercent) [[unlikely]] { + return EnergyRatioStatus::TenPercent; + } + if (PointOnePercent <= ratio && ratio < OnePercent) [[unlikely]] { + return EnergyRatioStatus::OnePercent; + } + // everything else now has to be below 0.1% + return EnergyRatioStatus::PointOnePercent; + } + + // referenceEnergy: full-precision energy (e.g. BeforeSkimmer's cluster.energy()). + // compressedEnergy: energy unpacked from a uint16_t-packed table (e.g. MinClusters' cluster.e()). + EnergyComparisonStatus compareClusterEnergy(float referenceEnergy, float compressedEnergy) + { + if (referenceEnergy > EnergyPackingSaturationValue) { + return EnergyComparisonStatus::Saturated; + } + return std::abs(referenceEnergy - compressedEnergy) <= EnergyComparisonEpsilon + ? EnergyComparisonStatus::Match + : EnergyComparisonStatus::Mismatch; + } + + void processBeforeSkimmer(BeforeSkimmerCluster const& clusters, McParticles_001 const& mcParticles) + { + if (clusters.size() == 0 || mcParticles.size() == 0) { + return; + } + auto mcParticle = mcParticles.begin(); + + for (const auto& cluster : clusters) { + if (!cluster.has_mcParticle()) { + continue; + } + const auto& ids = cluster.mcParticleIds(); + const auto& fracs = cluster.amplitudeA(); + for (std::size_t i = 0; i < ids.size(); ++i) { + const auto& id = ids[i]; + const auto& frac = fracs[i]; + mcParticle.setCursor(id); + registry.fill(HIST("BeforeSkimmer/hParticleClusterFracRatioStatus"), static_cast(getEnergyRatioStatus(cluster.energy(), mcParticle.e(), frac))); + } + } + } + PROCESS_SWITCH(EmcalMcSanityCheck, processBeforeSkimmer, "Process EMCal cluster information before the skimmerGammaCalo", true); + + void processAfterSkimmer(AfterSkimmerCluster const& clusters, McParticles_001 const& mcParticles) + { + if (clusters.size() == 0 || mcParticles.size() == 0) { + return; + } + auto mcParticle = mcParticles.begin(); + + for (const auto& cluster : clusters) { + if (!cluster.has_mcParticle()) { + continue; + } + const auto& ids = cluster.mcParticleIds(); + const auto& fracs = cluster.amplitude(); + for (std::size_t i = 0; i < ids.size(); ++i) { + const auto& id = ids[i]; + const auto& frac = fracs[i]; + mcParticle.setCursor(id); + registry.fill(HIST("AfterSkimmer/hParticleClusterFracRatioStatus"), static_cast(getEnergyRatioStatus(cluster.e(), mcParticle.e(), frac))); + } + } + } + PROCESS_SWITCH(EmcalMcSanityCheck, processAfterSkimmer, "Process EMCal cluster information after the skimmerGammaCalo", true); + + void processAfterAssociation(AfterAssociateCluster const& clusters, EMMCParticles_001 const& mcParticles) + { + if (clusters.size() == 0 || mcParticles.size() == 0) { + return; + } + auto mcParticle = mcParticles.begin(); + + for (const auto& cluster : clusters) { + if (!cluster.has_emmcparticle()) { + continue; + } + const auto& ids = cluster.emmcparticleIds(); + const auto& fracs = cluster.amplitude(); + for (std::size_t i = 0; i < ids.size(); ++i) { + const auto& id = ids[i]; + const auto& frac = fracs[i]; + mcParticle.setCursor(id); + registry.fill(HIST("AfterAssociate/hParticleClusterFracRatioStatus"), static_cast(getEnergyRatioStatus(cluster.e(), mcParticle.e(), frac))); + } + } + } + PROCESS_SWITCH(EmcalMcSanityCheck, processAfterAssociation, "Process EMCal cluster information before the associateMCinfoPhoton", false); + + void processCompareBeforeAfterAssociate(MinClusters const& clusters, + EMCClusterMCLabels_001 const& beforeLabels, + EMEMCClusterMCLabels_001 const& afterLabels, + McParticles_001 const& mcParticles, + EMMCParticles_001 const& emMcParticles) + { + if (clusters.size() == 0 || mcParticles.size() == 0 || emMcParticles.size() == 0) { + return; + } + + auto mcParticle = mcParticles.begin(); + auto emMcParticle = emMcParticles.begin(); + + for (int64_t i = 0; i < clusters.size(); ++i) { + auto before = beforeLabels.iteratorAt(i); + auto after = afterLabels.iteratorAt(i); + + if (!after.has_emmcparticle() || !before.has_mcParticle()) { + continue; + } + + if (after.emmcparticleIds().size() != before.mcParticleIds().size()) { + LOG(fatal) << "Number of EmMcParticles and McParticles does not match (" << after.emmcparticleIds().size() << " vs " << before.mcParticleIds().size() << ")"; + } + for (size_t id = 0; id < after.emmcparticleIds().size(); ++id) { + bool isSameSpecies = true; + bool isSameEnergy = true; + bool hasSameFraction = true; + + mcParticle.setCursor(before.mcParticleIds()[id]); + emMcParticle.setCursor(after.emmcparticleIds()[id]); + + if (mcParticle.pdgCode() != emMcParticle.pdgCode()) { + isSameSpecies = false; + } + if (mcParticle.e() != emMcParticle.e()) { + isSameEnergy = false; + } + if (before.amplitude()[id] != after.amplitude()[id]) { + hasSameFraction = false; + } + + if (isSameSpecies && isSameEnergy && hasSameFraction) { + registry.fill(HIST("hMcParticleStatus"), 0); + } + if (!isSameSpecies) { + registry.fill(HIST("hMcParticleStatus"), 1); + } + if (!isSameEnergy) { + registry.fill(HIST("hMcParticleStatus"), 2); + } + if (!hasSameFraction) { + registry.fill(HIST("hMcParticleStatus"), 3); + } + } + } + } + PROCESS_SWITCH(EmcalMcSanityCheck, processCompareBeforeAfterAssociate, "Process and compare EMCal cluster and McParticle information before and after the associateMCinfoPhoton", false); +}; // End struct EmcalMcSanityCheck + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx b/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx new file mode 100644 index 00000000000..073479c2c9e --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/emcalMcTask.cxx @@ -0,0 +1,371 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file emcalMcTask.cxx +/// \brief Analysis task for to obtain cluster properties from MC identified particles like photons and electrons +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; + +namespace o2::em::emcal::mc +{ + +enum ParticleType : int { + kPhoton = 0, + kElectron, + kPositron, + kPi0, + kEta, + kOmega, + kPion, + kKaon, + kOther, + kNParticleTypes +}; +} // namespace o2::em::emcal::mc + +enum CentralityEstimator { + None = 0, + CFT0A = 1, + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 +}; + +enum class MapLevel { + kGood = 1, + kNoBad = 2, + kInEMC = 3, + kAll = 4 +}; + +struct EmcalMcTask { + // configurable axis + ConfigurableAxis thnConfigAxisE{"thnConfigAxisE", {400, 0., 20.}, "energy axis"}; + ConfigurableAxis thnConfigAxisEtaDiff{"thnConfigAxisEtaDiff", {300, -1., 2.}, "(eta rec - eta true)"}; + ConfigurableAxis thnConfigAxisPhiDiff{"thnConfigAxisPhiDiff", {300, -1., 2.}, "(phi rec - phi true"}; + ConfigurableAxis thnConfigAxisM02{"thnConfigAxisM02", {100, 0.0, 1.0}, "m02 mass axis"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {20, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "FLag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } emccuts; + + SliceCache cache; + + using EMCalPhotons = soa::Join; + + using Colls = soa::Join; + + using McColls = o2::soa::Join; + using McParticles = EMMCParticles; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + int mRunNumber{-1}; + float dBz{0.f}; + + static constexpr std::array(o2::em::emcal::mc::ParticleType::kNParticleTypes)> kSubDirs = { + "photon/", "electron/", "positron/", "pi0/", + "eta/", "omega/", "pion/", "kaon/", "other/"}; + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void init(InitContext&) + { + mRunNumber = 0; + dBz = 0; + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + + const AxisSpec thnAxisERec{thnConfigAxisE, "#it{E}_{Rec} (GeV)"}; + + const AxisSpec thnAxisM02{thnConfigAxisM02, "#it{M}_{02}"}; + + const AxisSpec thnAxisEtaDiff{thnConfigAxisEtaDiff, "#it{#eta}_{Rec} - #it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiDiff{thnConfigAxisPhiDiff, "#it{#varphi}_{Rec} - #it{#varphi}_{Gen}"}; + + AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init + if (useCent.value) { + // PbPb: use centrality + thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"}; + } else { + // pp: use multiplicity + thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"}; + } + + registry.add("photon/hM02", "cluster m02 vs energy vs cent/mult", HistType::kTH3F, {thnAxisM02, thnAxisERec, thnAxisCentOrMult}); + registry.add("photon/hEtaRel", "relative #eta vs energy vs cent/mult", HistType::kTH3F, {thnAxisEtaDiff, thnAxisERec, thnAxisCentOrMult}); + registry.add("photon/hPhiRel", "relative #varphi vs energy vs cent/mult", HistType::kTH3F, {thnAxisPhiDiff, thnAxisERec, thnAxisCentOrMult}); + + registry.addClone("photon/", "electron/"); + registry.addClone("photon/", "positron/"); + registry.addClone("photon/", "pi0/"); + registry.addClone("photon/", "eta/"); + registry.addClone("photon/", "omega/"); + registry.addClone("photon/", "pion/"); + registry.addClone("photon/", "kaon/"); + registry.addClone("photon/", "other/"); + + }; // end init + + template + float getCentralityOrMultiplicity(TCollision const& collision) + { + if (useCent.value) { + return getCentrality(collision); + } + // pp: use raw FT0C multiplicity + return collision.multFT0C(); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (eventcuts.centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + // occupancy selection + return false; + } + float centOrMult = getCentralityOrMultiplicity(collision); + if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { + // event selection + return false; + } + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); + registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted + registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + } + return true; + } + + // One templated fill function instead of 9 copy-pasted blocks + template + void fillClusterHistos(TCluster const& cluster, TMC const& mcPart, float centOrMult) + { + static constexpr std::string_view SubDir = kSubDirs[type]; + + registry.fill(HIST(SubDir) + HIST("hM02"), cluster.m02(), cluster.e(), centOrMult); + registry.fill(HIST(SubDir) + HIST("hEtaRel"), cluster.eta() - mcPart.eta(), cluster.e(), centOrMult); + registry.fill(HIST(SubDir) + HIST("hPhiRel"), cluster.phi() - mcPart.phi(), cluster.e(), centOrMult); + } + + // PCM-EMCal same event + void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, EMMCParticles const& mcParticles) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + EMBitFlags emcFlags(clusters.size()); + if (clusters.size() > 0) { + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + } + + // create iterators for photon mc particles + auto mcPhoton1 = mcParticles.begin(); + + for (const auto& collision : collisions) { + isFullEventSelected(collision, true); + + float centOrMult = getCentralityOrMultiplicity(collision); + + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + for (const auto& photonEMC : photonsEMCPerCollision) { + if (!(emcFlags.test(photonEMC.globalIndex()))) { + continue; + } + if (photonEMC.emmcparticleIds().empty()) { + // this is a cluster with just noise, skip + continue; + } + // we only want to look at the largest contribution + mcPhoton1.setCursor(photonEMC.emmcparticleIds()[0]); + + if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kGamma) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else if (mcPhoton1.pdgCode() == PDG_t::kElectron) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else if (mcPhoton1.pdgCode() == PDG_t::kPositron) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kPi0) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else if (std::abs(mcPhoton1.pdgCode()) == o2::constants::physics::Pdg::kEta) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else if (std::abs(mcPhoton1.pdgCode()) == o2::constants::physics::Pdg::kOmega) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kPiPlus) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kKPlus) { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } else { + fillClusterHistos(photonEMC, mcPhoton1, centOrMult); + } + } // for (const auto& photonEMC : photonsEMCPerCollision) { + } + } + PROCESS_SWITCH(EmcalMcTask, processEmcal, "Process for emcal", true); + +}; // End struct EmcalMcTask + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx new file mode 100644 index 00000000000..9d233f75ca0 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/emcalPhotonMcTask.cxx @@ -0,0 +1,1031 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file emcalPhotonMcTask.cxx +/// \brief Analysis task for to analyse photon clusters and differences to conversion clusters in MC +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCConversionCandidate.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/EmMlResponseEMCConversion.h" +#include "PWGEM/PhotonMeson/DataModel/ConversionMl.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Utils/ParticleOrigin.h" + +#include "Common/Core/RecoDecay.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; +using namespace o2::constants::physics; +using namespace o2::aod::pwgem::photonmeson::utils::mcutil; +using namespace o2::analysis::em; + +constexpr float MinAmpThreshold = 0.2f; // Minimum cluster amplitude threshold to count as significant +constexpr float MinAmpFraction = 0.6f; // Minimum fraction of particle energy that it needs to deposit in cluster to count as significant +enum CentralityEstimator { + None = 0, + CFT0A, + CFT0C, + CFT0M, + NCentralityEstimators +}; + +enum class TruthClass { + Conversion, // two electron legs, same conversion vertex + GammaGammaSamePi0, // both clusters are DIRECT daughter photons of the same generator-level meson + GammaGammaAnnihilation, // two photons from the same e+e- annihilation vertex + BSPhotonElectron, // a bremsstrahlung photon paired with the specific lepton that radiated it + PhotonComptonElectronPair, // a Compton-scattered photon paired with its own recoil electron + ElectronPairSamePi0, // two lepton clusters, directly from same meson + CrossConvertedSiblings, // two lepton clusters, same meson cross-converted siblings + DalitzDecaySiblings, // two lepton clusters, same meson from Dalitz + SplitPhotonCluster, // one physical photon shower reconstructed as two clusters + SplitLeptonCluster, // one physical lepton shower reconstructed as two clusters + IndirGammaGammaSamePi0, // both clusters are indirect daughter photons of the same generator-level meson + SameMesonIndirect, // both clusters trace to the SAME generator-level meson, but at least one path passes through extra generations (e.g. a further conversion/BS/scatter) before reaching the cluster -- NOT a clean two-body relationship + Background, // no common meson ancestor at all -- genuinely uncorrelated combinatorics + NClasses +}; + +enum class ClusterTruthClass { + Photon = 0, // cluster is a true, unconverted photon + Conversion, // cluster is a leg of a true conversion + Pi0Conversion, // ...and that conversion's photon itself came from pi0/eta (excludes same-gamma-pair case) + Pi0, // cluster comes from the pi0/eta decay chain (not via the conversion-leg path above) + Background, // cluster is flagged as both meson-chain AND conversion -- ambiguous/contradictory + NClasses +}; + +enum class TagDecision { + NotTagged = 0, + Tagged, + NTags +}; + +struct PendingConvPair { + int64_t idx1 = 0; + int64_t idx2 = 0; +}; + +struct ClusterMcInfo { + bool isLepton = false; // is cluster from a lepton + bool isPhoton = false; // is cluster from a photon + bool isMergedConv = false; // is cluster from a merged conversion + LeptonOrigin leptonOrigin = LeptonOrigin::Other; // origin of lepton in case isLepton == true + PhotonOrigin photonOrigin = PhotonOrigin::Other; // origin of photon in case isPhoton == true + int photonMotherId = -1; // for leptons: the photon this lepton traces to + int mesonId = -1; // for Decay photons: the pi0/eta/omega/etaprime id | for leptons from decay photons: the pi0/eta/omega/etaprime id + int hardPartonId = -1; // for Direct photons: the quark/gluon id + int decayPhotonId = -1; // unified: the photon (self, if isPhoton; or photonMotherId, if isLepton) whose immediate mother is a meson. -1 if not applicable. + float purity = 0.f; // fraction of energy the main particle contributed to the cluster + float radius = 0.f; // radius in xy from where the main contributor to the cluster originated +}; + +template +ClusterMcInfo classifyCluster(const TCluster& g, TIter& mcCluster, TIter& mcClusterLooper, TIter& mcClusterLooper2, McParticles const& mcParticles) +{ + static const std::array kMesonPdgs{PDG_t::kPi0, Pdg::kEta, Pdg::kOmega, Pdg::kEtaPrime}; + ClusterMcInfo info; + mcCluster.setCursor(g.emmcparticleIds()[0]); + info.radius = std::hypot(mcCluster.vx(), mcCluster.vy()); + float leadingAmplitude = g.amplitude()[0]; + info.purity = leadingAmplitude; + info.mesonId = o2::aod::pwgem::photonmeson::utils::mcutil::GetMesonInChain(mcCluster, mcParticles, kMesonPdgs); + if (std::abs(mcCluster.pdgCode()) == PDG_t::kElectron) { + info.isLepton = true; + info.leptonOrigin = getLeptonOriginType(mcCluster, mcClusterLooper2, kMesonPdgs); + mcClusterLooper.setCursor(g.emmcparticleIds()[0]); + if (info.leptonOrigin == LeptonOrigin::Conversion) { + if (!mcCluster.has_mothers()) [[unlikely]] { + // conersion with no mother does not make any sense + info.leptonOrigin = LeptonOrigin::Other; + } else { + info.photonMotherId = mcCluster.mothersIds()[0]; + // since this is a real conversion where both daughters exist, check if the other conversion leg entered this cluster as well + for (size_t i = 1; i < g.emmcparticleIds().size(); ++i) { + mcClusterLooper.setCursor(g.emmcparticleIds()[i]); + if (std::abs(mcClusterLooper.pdgCode()) == PDG_t::kElectron && mcClusterLooper.pdgCode() == -1 * mcCluster.pdgCode()) { + mcClusterLooper2.setCursor(mcClusterLooper.globalIndex()); + int32_t otherConvMotherId = getMotherIndexFromChain(mcClusterLooper2, PDG_t::kGamma); + if (otherConvMotherId == info.photonMotherId) { + float energyFraction = (g.amplitude()[i] * g.e()) / mcClusterLooper.e(); + if (energyFraction >= MinAmpFraction && g.amplitude()[i] > MinAmpThreshold) { + info.isMergedConv = true; + } + // we found the sibling leg no need to search further + break; + } // if (otherConvMotherId == info.photonMotherId) + } // if (std::abs(mcClusterLooper.pdgCode()) == PDG_t::kElectron && mcClusterLooper.pdgCode() == -1 * mcCluster.pdgCode()) + } // end of loop over other cluster contributions + } // particle has mothers + } // if(info.leptonOrigin == LeptonOrigin::Conversion) + if (info.photonMotherId >= 0) { + mcClusterLooper.setCursor(info.photonMotherId); + info.photonOrigin = getPhotonOriginType(mcClusterLooper, mcClusterLooper2, kMesonPdgs, info.hardPartonId); + if (info.photonOrigin == PhotonOrigin::Decay) { + info.decayPhotonId = info.photonMotherId; + info.mesonId = o2::aod::pwgem::photonmeson::utils::mcutil::GetMesonInChain(mcClusterLooper, mcParticles, kMesonPdgs); + } + } + } + if (std::abs(mcCluster.pdgCode()) == PDG_t::kGamma) { + info.isPhoton = true; + info.photonOrigin = getPhotonOriginType(mcCluster, mcClusterLooper, kMesonPdgs, info.hardPartonId); + if (info.photonOrigin == PhotonOrigin::Decay) { + info.mesonId = o2::aod::pwgem::photonmeson::utils::mcutil::GetMesonInChain(mcCluster, mcParticles, kMesonPdgs); + } + } + return info; +} + +struct EmcalPhotonMcTask { + static constexpr float EMCALRadius = 440.f; + static constexpr float PhiVUndefined = -999.f; + static constexpr float Epsilon = 1.e-6f; + + static constexpr uint32_t MlModelRow = 0; + static constexpr uint32_t MlPositiveClassCol = 1; + + static constexpr std::array, 1> DefaultCutsMl{{{0.0, 0.25}}}; + + static constexpr std::array(TruthClass::NClasses)> kTruthClassNames = { + "Conversion", "GammaGammaSamePi0", "GammaGammaAnnihilation", "BSPhotonElectron", + "PhotonComptonElectronPair", "ElectronPairSamePi0", "CrossConvertedSiblings", "DalitzDecaySiblings", + "SplitPhotonCluster", "SplitLeptonCluster", "IndirGammaGammaSamePi0", "SameMesonIndirect", + "Background"}; + + Produces convTagCandidates; + + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable writeTable{"writeTable", true, "write table for ML."}; + Configurable> classPrescale{"classPrescale", + { + 1, // Conversion + 1, // GammaGammaSamePi0 + 1, // GammaGammaAnnihilation + 1, // BSPhotonElectron + 1, // PhotonComptonElectronPair + 1, // ElectronPairSamePi0 + 1, // CrossConvertedSiblings + 1, // DalitzDecaySiblings + 1, // SplitPhotonCluster + 1, // SplitLeptonCluster + 1, // IndirGammaGammaSamePi0 + 1, // SameMesonIndirect + 5000, // Background + }, + "prescale factor per TruthClass, indexed 0..12 matching the enum order"}; + + Configurable bkgPrescaleSeed{"bkgPrescaleSeed", 42, "seed for the background-prescale RNG"}; + + // configurable axis + ConfigurableAxis thnConfigAxisInvMass{"thnConfigAxisInvMass", {400, 0.0, 0.8}, "invariant mass axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {400, 0., 20.}, "pT axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {20, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; + ConfigurableAxis thnConfigAxisDeltaEta{"thnConfigAxisDeltaEta", {100, -1, 1}, "delta eta axis"}; + ConfigurableAxis thnConfigAxisDeltaPhi{"thnConfigAxisDeltaPhi", {100, -1, 1}, "delta phi axis"}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; + + struct : ConfigurableGroup { + std::string prefix = "conversiontagging"; + Configurable maxMinv{"maxMinv", 0.1f, "maximum invariant mass for tagging conversions."}; + Configurable maxDeltaEta{"maxDeltaEta", 0.05f, "maximum delta eta between two clusters for tagging them as conversions."}; + Configurable maxDeltaPhi{"maxDeltaPhi", 0.1f, "maximum delta phi between two clusters for tagging them as conversions."}; + Configurable minrConv{"minrConv", 370.f, "minimum conversion Radius of two clusters for tagging them as conversions."}; + Configurable maxrConv{"maxrConv", 430.f, "maximum conversion Radius of two clusters for tagging them as conversions."}; + } conversiontaggingcuts; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "FLag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } emccuts; + + struct : ConfigurableGroup { + std::string prefix = "mesonConfig"; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } mesonConfig; + + struct : ConfigurableGroup { + std::string prefix = "mlConfig"; + Configurable useMlTagging{"useMlTagging", false, "use ML score instead of box cut for conversion tagging"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "load ML model from CCDB"}; + Configurable enableOptimization{"enableOptimization", false, "enable the MlResponse optimizations."}; + Configurable nThreads{"nThreads", 1, "number of threads for the ML model"}; + Configurable mlBatchFlushSize{"mlBatchFlushSize", 20000, "Flush ML batch after this many pending pairs."}; + Configurable> mlInputFeatures{"mlInputFeatures", {"minv", "deltaEta", "deltaR", "phiv", "rConv", "totE", "e2", "e1", "deltaPhi", "harmonicEt", "m021", "m022", "time1", "time2", "ncell1", "ncell2"}, "input feature names -- content and order must match the Python training FEATURES list"}; + Configurable mlModelPathLocal{"mlModelPathLocal", "/data/mhemmer/O2ML/code/conversion_tagging_bdt_conversion_splits_brems.onnx", "local ONNX model path"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/m/mhemmer/EM/ML/"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"conversion_tagging_bdt_conversion_splits_brems.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable mlThreshold{"mlThreshold", 0.5f, "positive-class score threshold for tagging"}; + Configurable> cutsMl{"cutsMl", {DefaultCutsMl[0].data(), 1, 2, {"pT bin 0"}, { + "score photon pairs", + "score conversion pairs", + }}, + "ML selections per pT bin"}; + } mlConfig; + + SliceCache cache; + + using EMCalPhotons = soa::Join; + + using Colls = soa::Join; + + using McColls = o2::soa::Join; + using McParticles = EMMCParticles; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + int8_t bTruthLabel{}; + + o2::ccdb::CcdbApi ccdbApi; + int mRunNumber{0}; + float dBz{0.f}; + + std::mt19937 mRandGen; + std::uniform_int_distribution mPrescaleDist; + + o2::analysis::em::emcconv::EmMlResponseEMCConversion mMlResponse; + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void init(InitContext&) + { + mRunNumber = 0; + dBz = 0; + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + + const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; + const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + + const AxisSpec thnAxisrConvRec{1000, 0, 500, "#it{R}_{rec}"}; + const AxisSpec thnAxisrConvGen{1000, 0, 500, "#it{R}_{gen}"}; + + const AxisSpec thnAxisDeltaEta{thnConfigAxisDeltaEta, "#Delta#it{eta}"}; + const AxisSpec thnAxisDeltaPhi{thnConfigAxisDeltaPhi, "#Delta#it{#varphi} (rad)"}; + + const AxisSpec thnAxisTagging{static_cast(TagDecision::NTags), -0.5, static_cast(TagDecision::NTags) - 0.5, ""}; + const AxisSpec thnAxisClasses{static_cast(TruthClass::NClasses), -0.5, static_cast(TruthClass::NClasses) - 0.5, ""}; + + const AxisSpec thnAxisM02{100, 0., 1.0, "#it{M}_{02}"}; + + AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init + if (useCent.value) { + // PbPb: use centrality + thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"}; + } else { + // pp: use multiplicity + thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"}; + } + + auto hTruthLabel = registry.add("hTruthLabel", "Truth label distribution;;Counts", HistType::kTH2D, {{static_cast(TruthClass::NClasses), -0.5, static_cast(TruthClass::NClasses) - 0.5}, thnAxisPtRec}); + + // set bin labels once at init, so histogram is human-readable without decoding the enum + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::Conversion) + 1, "Conversion"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::GammaGammaSamePi0) + 1, "GammaGammaSamePi0"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::GammaGammaAnnihilation) + 1, "GammaGammaAnnihilation"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::BSPhotonElectron) + 1, "BSPhotonElectron"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::PhotonComptonElectronPair) + 1, "PhotonComptonElectronPair"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::ElectronPairSamePi0) + 1, "ElectronPairSamePi0"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::CrossConvertedSiblings) + 1, "CrossConvertedSiblings"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::DalitzDecaySiblings) + 1, "DalitzDecaySiblings"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::SplitPhotonCluster) + 1, "SplitPhotonCluster"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::SplitLeptonCluster) + 1, "SplitLeptonCluster"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::IndirGammaGammaSamePi0) + 1, "IndirGammaGammaSamePi0"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::SameMesonIndirect) + 1, "SameMesonIndirect"); + hTruthLabel->GetXaxis()->SetBinLabel(static_cast(TruthClass::Background) + 1, "Background"); + + // auto hConfusionMatrixConversionTagging = registry.add("EMCal/ConfusionMatrixConversionTagging", "Confusion matrix for conversion tagging", HistType::kTH2D, {thnAxisTagging, thnAxisClasses}); + // hConfusionMatrixConversionTagging->GetXaxis()->SetBinLabel(1, "not tagged"); + // hConfusionMatrixConversionTagging->GetXaxis()->SetBinLabel(2, "tagged"); + // hConfusionMatrixConversionTagging->GetYaxis()->SetBinLabel(1, "true conv."); + // hConfusionMatrixConversionTagging->GetYaxis()->SetBinLabel(2, "true #pi^{0}"); + // hConfusionMatrixConversionTagging->GetYaxis()->SetBinLabel(3, "true #pi^{0} conv."); + // hConfusionMatrixConversionTagging->GetYaxis()->SetBinLabel(4, "background"); + // hConfusionMatrixConversionTagging->GetYaxis()->SetBinLabel(5, "#gamma"); + + auto hPhotonProcess = registry.add("Photon/hProcess", "Production process type", HistType::kTH1D, {{kMaxMCProcess, -0.5, kMaxMCProcess - 0.5}}); + for (int i = 0; i < kMaxMCProcess; ++i) { + hPhotonProcess->GetXaxis()->SetBinLabel(i + 1, TMCProcessName[i]); + } + registry.addClone("Photon/", "Electron/"); + registry.addClone("Photon/", "Positron/"); + + registry.add("Photon/Direct/M02", "M02 distribution;;counts", HistType::kTH2D, {thnAxisM02, thnAxisPtRec}); + + auto hDirectPhotonTags = registry.add("Photon/Direct/Tagging", "Tagging distribution;;counts", HistType::kTH1D, {{3, -0.5, 2.5}}); + hDirectPhotonTags->GetXaxis()->SetBinLabel(1, "Not tagged"); + hDirectPhotonTags->GetXaxis()->SetBinLabel(2, "tagged simple"); + hDirectPhotonTags->GetXaxis()->SetBinLabel(3, "tagged double"); + + registry.addClone("Photon/Direct/", "Photon/Decay/"); + registry.addClone("Photon/Direct/", "Photon/Bremsstrahlung/"); + registry.addClone("Photon/Direct/", "Photon/Annihilation/"); + registry.addClone("Photon/Direct/", "Photon/Hadronic/"); + + registry.addClone("Photon/Direct/", "Lepton/Conversion/"); + registry.addClone("Photon/Direct/", "Lepton/Compton/"); + registry.addClone("Photon/Direct/", "Lepton/PhotoElectric/"); + registry.addClone("Photon/Direct/", "Lepton/DeltaRay/"); + registry.addClone("Photon/Direct/", "Lepton/DirectMesonDecay/"); + + if (mlConfig.useMlTagging.value) { + registry.add("hMlScore", "BDT score;;Counts", HistType::kTH1D, {{100, 0, 1}}); + } + + mRandGen.seed(bkgPrescaleSeed.value); + + if (classPrescale.value.size() != kTruthClassNames.size()) { + LOG(fatal) << "classPrescale has " << classPrescale.value.size() << " entries, " + << "but TruthClass has " << kTruthClassNames.size() << " members -- update classPrescale!"; + } + + LOG(info) << "=== classPrescale configuration ==="; + for (size_t i = 0; i < kTruthClassNames.size(); ++i) { + LOG(info) << " [" << i << "] " << kTruthClassNames[i] << " -> prescale = " << classPrescale.value[i]; + } + + }; // end init + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + + auto timestamp = collision.timestamp(); + // Fetch magnetic field from ccdb for current collision + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << timestamp << " with magnetic field of " << dBz << " kZG"; + + if (mlConfig.useMlTagging.value) { + // single bin, full range -- see earlier discussion: pT/energy-binned + // thresholds are a straightforward future extension of this same + // machinery if ever needed, not used right now + std::vector binsLimits = {0., 1000.}; + std::vector cutDir = { + static_cast(o2::cuts_ml::CutDirection::CutNot), // class 0 (negative-class prob) -- no cut + static_cast(o2::cuts_ml::CutDirection::CutSmaller), // class 1 (positive-class prob) -- reject if score < threshold + }; + + mMlResponse.configure(binsLimits, mlConfig.cutsMl, cutDir, /*nClasses=*/2); + mMlResponse.cacheInputFeaturesIndices(mlConfig.mlInputFeatures.value); + if (mlConfig.loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + mMlResponse.setModelPathsCCDB(mlConfig.onnxFileNames, ccdbApi, mlConfig.modelPathsCCDB.value, timestamp); + } else { + mMlResponse.setModelPathsLocal({mlConfig.mlModelPathLocal.value}); + } + mMlResponse.init(mlConfig.enableOptimization, mlConfig.nThreads); + + LOG(info) << "ML conversion tagging enabled -- model: " << mlConfig.mlModelPathLocal.value + << ", threshold: " << mlConfig.mlThreshold.value; + } + } + + template + float getCentralityOrMultiplicity(TCollision const& collision) + { + if (useCent.value) { + return getCentrality(collision); + } + // pp: use raw FT0C multiplicity + return collision.multFT0C(); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (eventcuts.centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \param fillHisto flag to enable filling of histograms + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + // occupancy selection + return false; + } + float centOrMult = getCentralityOrMultiplicity(collision); + if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { + // event selection + return false; + } + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); + registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted + registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + } + return true; + } + + // EMCal same event + void processEmcal(Colls const& collisions, McColls const&, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, EMMCParticles const& mcParticles) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + if (collisions.size() <= 0) { + return; + } + + auto cluster1 = clusters.begin(); + auto cluster2 = clusters.begin(); + + std::vector wasMeassured(mcParticles.size(), false); + EMBitFlags emcFlagsFromTrueMeson(clusters.size()); + EMBitFlags emcFlagsFromTrueMesonSameGamma(clusters.size()); + EMBitFlags emcFlagsFromTrueConversion(clusters.size()); + EMBitFlags emcFlagsTagging(clusters.size()); + EMBitFlags emcFlagsMlTagging(clusters.size()); + EMBitFlags emcFlagsMlTaggingDoublePass(clusters.size()); + EMBitFlags emcFlags(clusters.size()); + + std::vector bestPositiveScore(clusters.size(), 0); + std::vector bestNegativeScore(clusters.size(), 0); + + std::vector mMlInputBuffer; + + std::vector pending; + std::vector mlBatchBuffer; + + auto flushMlBatch = [&]() { + if (pending.empty()) { + return; + } + auto scores = mMlResponse.getModelOutputBatched(mlBatchBuffer, /*nModel=*/0, pending.size()); + for (std::size_t i = 0; i < pending.size(); ++i) { + const auto& pp = pending[i]; + cluster1.setCursor(pp.idx1); + cluster2.setCursor(pp.idx2); + const float negScore = scores[i * 2 + 0]; + const float posScore = scores[i * 2 + 1]; + + for (const auto& idx : {pp.idx1, pp.idx2}) { + if (posScore > bestPositiveScore[idx]) { + bestPositiveScore[idx] = posScore; + } + if (negScore > bestNegativeScore[idx]) { + bestNegativeScore[idx] = negScore; + } + } + const bool isTagged = posScore >= mlConfig.cutsMl->get(MlModelRow, MlPositiveClassCol); // mirrors the CutSmaller-on-class-1 logic + if (isTagged) { + emcFlagsMlTagging.set(pp.idx1); + emcFlagsMlTagging.set(pp.idx2); + } + registry.fill(HIST("hMlScore"), posScore); + + // --- classification/table-fill exactly as before (lines 706-806), using pp.vMeson, pp.deltaEta, pp.deltaPhi, pp.phiV, pp.harmonicET, g1, g2 --- + } + mlBatchBuffer.clear(); + pending.clear(); + }; + + if (clusters.size() > 0) { + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + } + auto mcCluster1 = mcParticles.begin(); // iterator of MC particle of highest contributor for first cluster + auto mcCluster2 = mcParticles.begin(); // iterator of MC particle of highest contributor for second cluster + auto mcClusterLooper = mcParticles.begin(); // iterator of MC particle of other contributor for both clusters + auto mcClusterLooper2 = mcParticles.begin(); // iterator of MC particle of other contributor for both clusters + auto mcPhoton1 = mcParticles.begin(); // iterator of MC particle of highest contributor for first cluster that is photon + auto mcPhoton2 = mcParticles.begin(); // iterator of MC particle of highest contributor for second cluster that is photon + auto mcMother = mcParticles.begin(); // iterator of MC particle of combined mother of both clusters + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + + float centOrMult = getCentralityOrMultiplicity(collision); + + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + if (mlConfig.useMlTagging.value) { + pending.reserve(photonsEMCPerCollision.size()); // rough upper bound + mlBatchBuffer.reserve(photonsEMCPerCollision.size() * mlConfig.mlInputFeatures.value.size()); + } + + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsEMCPerCollision, photonsEMCPerCollision))) { + if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + + if (g1.emmcparticleIds().empty() || g2.emmcparticleIds().empty()) { + // there is a cluster which is just noise, skip + continue; + } + + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + + // z-axis unit vector (beam/field direction) + ROOT::Math::XYZVector p1 = v1.Vect(); + ROOT::Math::XYZVector p2 = v2.Vect(); + ROOT::Math::XYZVector pSum = p1 + p2; + ROOT::Math::XYZVector zAxis(0, 0, 1); + + ROOT::Math::XYZVector u = pSum.Unit(); + ROOT::Math::XYZVector nDecay = p1.Cross(p2); // normal to the plane containing p1, p2 + ROOT::Math::XYZVector nRef = u.Cross(zAxis); // normal to the plane containing u and z + + float phiV = PhiVUndefined; // sentinel for degenerate geometry + if (nDecay.R() > Epsilon && nRef.R() > Epsilon) { + float cosPhiV = static_cast(nDecay.Unit().Dot(nRef.Unit())); + cosPhiV = std::clamp(cosPhiV, -1.f, 1.f); + phiV = std::acos(cosPhiV); + } + + const float deltaPhi = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); + const float deltaEta = v1.Eta() - v2.Eta(); + const float eT1 = v1.Et(); + const float eT2 = v2.Et(); + const float harmonicET = (eT1 * eT2) / (eT1 + eT2); + + // calculate the conversion radius in cm. The formula expects radii in m, B field in T and energies in GeV + const float rConv = 100.f * (EMCALRadius / 100.f - std::fabs(deltaPhi) / (0.15f * (std::fabs(dBz) / 10.f)) * harmonicET); + + // tag conversion pairs + if (conversiontaggingcuts.maxMinv >= vMeson.M() && conversiontaggingcuts.maxDeltaEta >= std::fabs(deltaEta) && conversiontaggingcuts.minrConv <= rConv && rConv <= conversiontaggingcuts.maxrConv && conversiontaggingcuts.maxDeltaPhi >= std::fabs(deltaPhi)) { + emcFlagsTagging.set(g1.globalIndex()); + emcFlagsTagging.set(g2.globalIndex()); + } + + if (mlConfig.useMlTagging.value) { + o2::analysis::em::EMCConversionCandidate candidate{ + .mMinv = static_cast(vMeson.M()), .mDeltaEta = deltaEta, .mDeltaR = std::hypot(deltaEta, deltaPhi), .mPhiv = phiV, .mRConv = rConv, .mTotE = (g2.e() + g1.e()), .mE2 = g2.e(), .mE1 = g1.e(), .mDeltaPhi = deltaPhi, .mHarmonicEt = harmonicET, .mM021 = g1.m02(), .mM022 = g2.m02(), .mTime1 = g1.time(), .mTime2 = g2.time(), .mNcell1 = g1.nCells(), .mNcell2 = g2.nCells()}; + mMlResponse.getInputFeatures(candidate, mMlInputBuffer); + mlBatchBuffer.insert(mlBatchBuffer.end(), mMlInputBuffer.begin(), mMlInputBuffer.end()); + pending.push_back({g1.globalIndex(), g2.globalIndex()}); + if (pending.size() >= mlConfig.mlBatchFlushSize.value) { + flushMlBatch(); + } + } + + // set MC particle cursors to the largest cluster contributor + mcCluster1.setCursor(g1.emmcparticleIds()[0]); + mcCluster2.setCursor(g2.emmcparticleIds()[0]); + + auto c1 = classifyCluster(g1, mcCluster1, mcClusterLooper, mcClusterLooper2, mcParticles); + auto c2 = classifyCluster(g2, mcCluster2, mcClusterLooper, mcClusterLooper2, mcParticles); + + const bool areFromSamePi0 = c1.mesonId >= 0 && c1.mesonId == c2.mesonId; + bool areConversionLegs = false; + bool areSplitPhotonCluster = false; + bool areSplitLeptonCluster = false; + + // split-cluster check MUST run first and take priority over everything else -- + // if both clusters share the same dominant MC particle, this is one physical + // shower reconstructed as two clusters, not a genuine pair of anything. + const bool isSameDominantParticle = (g1.emmcparticleIds()[0] == g2.emmcparticleIds()[0]); + if (isSameDominantParticle) { + if (c1.isPhoton) { + areSplitPhotonCluster = true; + } else if (c1.isLepton) { + areSplitLeptonCluster = true; + } + } + + // if they are not a split cluster check for proper conversion pair + if (!isSameDominantParticle && c1.leptonOrigin == LeptonOrigin::Conversion && c2.leptonOrigin == LeptonOrigin::Conversion && c1.photonMotherId == c2.photonMotherId) { + emcFlagsFromTrueConversion.set(g1.globalIndex()); + emcFlagsFromTrueConversion.set(g2.globalIndex()); + areConversionLegs = true; + } + + // if they are not a split cluster check for neutral meson connection + if (!isSameDominantParticle && c1.decayPhotonId >= 0 && c2.decayPhotonId >= 0) { + mcPhoton1.setCursor(c1.decayPhotonId); + mcPhoton2.setCursor(c2.decayPhotonId); + mcMother.setCursor(mcPhoton1.mothersIds()[0]); + if (mcMother.producedByGenerator()) { + if (c1.mesonId == c2.mesonId) { + // bremsstrahlung: one side is a photon born from the other side's lepton lineage + const bool photonIsBS = (c1.isPhoton && c1.photonOrigin == PhotonOrigin::Bremsstrahlung) || (c2.isPhoton && c2.photonOrigin == PhotonOrigin::Bremsstrahlung); + if (photonIsBS && ((c1.isLepton && c2.isPhoton) || (c2.isLepton && c1.isPhoton))) { + // nothing + } else { + emcFlagsFromTrueMesonSameGamma.set(g1.globalIndex()); + emcFlagsFromTrueMesonSameGamma.set(g2.globalIndex()); + } + } else if (mcPhoton1.mothersIds()[0] == mcPhoton2.mothersIds()[0]) { + emcFlagsFromTrueMeson.set(g1.globalIndex()); + emcFlagsFromTrueMeson.set(g2.globalIndex()); + } + } + } + + bTruthLabel = static_cast(TruthClass::Background); + if (!mcCluster1.has_mothers() || !mcCluster2.has_mothers()) { + registry.fill(HIST("hTruthLabel"), bTruthLabel, vMeson.Pt()); + + // final tree values plus filling + const int prescale = classPrescale.value[static_cast(bTruthLabel)]; + const bool keepThisRow = (prescale <= 1) || (std::uniform_int_distribution(0, prescale - 1)(mRandGen) == 0); + if (writeTable.value && keepThisRow) { + convTagCandidates(collision.globalIndex(), vMeson.M(), harmonicET, deltaEta, deltaPhi, phiV, g1.e(), g2.e(), g1.m02(), g2.m02(), g1.time(), g2.time(), g1.nCells(), g2.nCells(), c1.purity, c2.purity, bTruthLabel, centOrMult); + } + continue; + } + if (areSplitPhotonCluster) { + bTruthLabel = static_cast(TruthClass::SplitPhotonCluster); + } else if (areSplitLeptonCluster) { + bTruthLabel = static_cast(TruthClass::SplitLeptonCluster); + } else if (areConversionLegs) { + bTruthLabel = static_cast(TruthClass::Conversion); + } else if (c1.photonOrigin == PhotonOrigin::Annihilation && c2.photonOrigin == PhotonOrigin::Annihilation && mcCluster1.mothersIds()[0] == mcCluster2.mothersIds()[0]) { + bTruthLabel = static_cast(TruthClass::GammaGammaAnnihilation); + } else if ((c1.photonOrigin == PhotonOrigin::Bremsstrahlung && mcCluster1.mothersIds()[0] == mcCluster2.globalIndex()) || (c2.photonOrigin == PhotonOrigin::Bremsstrahlung && mcCluster2.mothersIds()[0] == mcCluster1.globalIndex())) { + bTruthLabel = static_cast(TruthClass::BSPhotonElectron); + } else if ((c1.leptonOrigin == LeptonOrigin::Compton && mcCluster1.mothersIds()[0] == mcCluster2.globalIndex()) || (c2.leptonOrigin == LeptonOrigin::Compton && mcCluster2.mothersIds()[0] == mcCluster1.globalIndex())) { + bTruthLabel = static_cast(TruthClass::PhotonComptonElectronPair); + } else if (c1.leptonOrigin == LeptonOrigin::DirectMesonDecay && c2.leptonOrigin == LeptonOrigin::DirectMesonDecay && mcCluster1.mothersIds()[0] == mcCluster2.mothersIds()[0]) { // both cluster are leptons that come from the same meson decay + mcMother.setCursor(mcCluster1.mothersIds()[0]); + if (mcMother.daughtersIds().size() == 2) { // o2-linter: disable=magic-number (number of daughters) + bTruthLabel = static_cast(TruthClass::ElectronPairSamePi0); + } else if (mcMother.daughtersIds().size() == 3) { // o2-linter: disable=magic-number (number of daughters) + bTruthLabel = static_cast(TruthClass::DalitzDecaySiblings); + } + } else if (c1.leptonOrigin == LeptonOrigin::Conversion && c2.leptonOrigin == LeptonOrigin::Conversion && mcCluster1.mothersIds()[0] != mcCluster2.mothersIds()[0] && areFromSamePi0) { // both cluster are leptons that come from different conversions that come from the same meson + bTruthLabel = static_cast(TruthClass::CrossConvertedSiblings); + } else if (c1.photonOrigin == PhotonOrigin::Decay && c2.photonOrigin == PhotonOrigin::Decay && areFromSamePi0) { // both clusters are photons from decay from the same meson + bTruthLabel = static_cast(TruthClass::GammaGammaSamePi0); + } else if (c1.isPhoton && c2.isPhoton && areFromSamePi0) { + bTruthLabel = static_cast(TruthClass::IndirGammaGammaSamePi0); + } else if (areFromSamePi0) { // both cluster do not fit into one of the categories above, but they share a common meson ancestry + bTruthLabel = static_cast(TruthClass::SameMesonIndirect); + } + registry.fill(HIST("hTruthLabel"), bTruthLabel, vMeson.Pt()); + + // final tree values plus filling + const int prescale = classPrescale.value[static_cast(bTruthLabel)]; + const bool keepThisRow = (prescale <= 1) || (std::uniform_int_distribution(0, prescale - 1)(mRandGen) == 0); + if (writeTable.value && keepThisRow) { + convTagCandidates(collision.globalIndex(), vMeson.M(), harmonicET, deltaEta, deltaPhi, phiV, g1.e(), g2.e(), g1.m02(), g2.m02(), g1.time(), g2.time(), g1.nCells(), g2.nCells(), c1.purity, c2.purity, bTruthLabel, centOrMult); + } + } // pair loop + + // Tagging part for the double pass using ML + for (const auto& cluster : photonsEMCPerCollision) { + if (!emcFlags.test(cluster.globalIndex())) { + continue; + } + const float bestPos = bestPositiveScore[cluster.globalIndex()]; + const float bestNeg = bestNegativeScore[cluster.globalIndex()]; + + // compare best positive vs best negative case. Only when the best positive is higher than the best negative actually mark this cluster as conversion + if (bestPos >= mlConfig.mlThreshold.value && bestPos > bestNeg) { + emcFlagsMlTaggingDoublePass.set(cluster.globalIndex()); + } + } // end of loop over cluster + } // collision loop + + auto collision = collisions.begin(); + + for (const auto& cluster : clusters) { + if (!(emcFlags.test(cluster.globalIndex()))) { + continue; + } + if (cluster.emmcparticleIds().empty()) { + // there is a cluster which is just noise, skip + continue; + } + if (cluster.pmeventId() > collision.globalIndex()) { + collision.setCursor(cluster.pmeventId()); + } + if (!isFullEventSelected(collision, false)) { + continue; + } + + ClusterMcInfo clusterMcInfo = classifyCluster(cluster, mcCluster1, mcClusterLooper, mcClusterLooper2, mcParticles); + const PhotonOrigin photonOrigin = clusterMcInfo.photonOrigin; + const LeptonOrigin leptonOrigin = clusterMcInfo.leptonOrigin; + const bool simpleTagged = !emcFlagsMlTagging.test(cluster.globalIndex()); + const bool doubleTagged = !emcFlagsMlTaggingDoublePass.test(cluster.globalIndex()); + const bool notTagged = !simpleTagged; + switch (photonOrigin) { + case PhotonOrigin::Direct: + registry.fill(HIST("Photon/Direct/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Photon/Direct/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Photon/Direct/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Photon/Direct/Tagging"), 2); + } + } + break; + case PhotonOrigin::Decay: + registry.fill(HIST("Photon/Decay/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Photon/Decay/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Photon/Decay/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Photon/Decay/Tagging"), 2); + } + } + break; + case PhotonOrigin::Bremsstrahlung: + registry.fill(HIST("Photon/Bremsstrahlung/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Photon/Bremsstrahlung/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Photon/Bremsstrahlung/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Photon/Bremsstrahlung/Tagging"), 2); + } + } + break; + case PhotonOrigin::Annihilation: + registry.fill(HIST("Photon/Annihilation/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Photon/Annihilation/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Photon/Annihilation/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Photon/Annihilation/Tagging"), 2); + } + } + break; + case PhotonOrigin::Hadronic: + registry.fill(HIST("Photon/Hadronic/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Photon/Hadronic/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Photon/Hadronic/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Photon/Hadronic/Tagging"), 2); + } + } + break; + case PhotonOrigin::Other: + default: + break; + } + switch (leptonOrigin) { + case LeptonOrigin::Conversion: + registry.fill(HIST("Lepton/Conversion/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Lepton/Conversion/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Lepton/Conversion/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Lepton/Conversion/Tagging"), 2); + } + } + break; + case LeptonOrigin::Compton: + registry.fill(HIST("Lepton/Compton/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Lepton/Compton/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Lepton/Compton/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Lepton/Compton/Tagging"), 2); + } + } + break; + case LeptonOrigin::PhotoElectric: + registry.fill(HIST("Lepton/PhotoElectric/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Lepton/PhotoElectric/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Lepton/PhotoElectric/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Lepton/PhotoElectric/Tagging"), 2); + } + } + break; + case LeptonOrigin::DeltaRay: + registry.fill(HIST("Lepton/DeltaRay/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Lepton/DeltaRay/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Lepton/DeltaRay/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Lepton/DeltaRay/Tagging"), 2); + } + } + break; + case LeptonOrigin::DirectMesonDecay: + registry.fill(HIST("Lepton/DirectMesonDecay/M02"), cluster.m02(), cluster.pt()); + if (notTagged) { + registry.fill(HIST("Lepton/DirectMesonDecay/Tagging"), 0); + } else { + if (simpleTagged) { + registry.fill(HIST("Lepton/DirectMesonDecay/Tagging"), 1); + } + if (doubleTagged) { + registry.fill(HIST("Lepton/DirectMesonDecay/Tagging"), 2); + } + } + break; + case LeptonOrigin::Other: + default: + break; + } + } // end of loop over cluster + } + PROCESS_SWITCH(EmcalPhotonMcTask, processEmcal, "Process for pcm and emcal photons", true); + +}; // End struct EmcalPhotonMcTask + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/emcalPi0Qc.cxx b/PWGEM/PhotonMeson/Tasks/emcalPi0Qc.cxx index 04f1f09b6e7..4bd7e5fe9e0 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalPi0Qc.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalPi0Qc.cxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,7 @@ #include #include -#include +#include #include #include #include @@ -68,33 +69,33 @@ using MyBCs = o2::soa::Join; struct Photon { Photon(float eta_tmp, float phi_tmp, float energy_tmp, int clusid = 0, uint8_t sm_tmp = 0) + : eta(eta_tmp), + phi(phi_tmp), + energy(energy_tmp), + theta(2.f * std::atan2(std::exp(-eta), 1)), + px(energy * std::sin(theta) * std::cos(phi)), + py(energy * std::sin(theta) * std::sin(phi)), + pz(energy * std::cos(theta)), + pt(std::sqrt(px * px + py * py)), + photon(px, py, pz, energy), + id(clusid), + sm(sm_tmp), + onDCal(phi < 6 && phi > 4) // o2-linter: disable=magic-number (DCal phi range) { - eta = eta_tmp; - phi = phi_tmp; - energy = energy_tmp; - theta = 2 * std::atan2(std::exp(-eta), 1); - px = energy * std::sin(theta) * std::cos(phi); - py = energy * std::sin(theta) * std::sin(phi); - pz = energy * std::cos(theta); - pt = std::sqrt(px * px + py * py); - photon.SetPxPyPzE(px, py, pz, energy); - id = clusid; - sm = sm_tmp; - onDCal = (phi < 6 && phi > 4); } - ROOT::Math::PxPyPzEVector photon; - float pt; - float px; - float py; - float pz; float eta; float phi; float energy; float theta; + float px; + float py; + float pz; + float pt; + ROOT::Math::PxPyPzEVector photon; int id; uint8_t sm; - bool onDCal; // Checks whether photon is in phi region of the DCal, otherwise: EMCal + bool onDCal{phi < 6 && phi > 4}; // Checks whether photon is in phi region of the DCal, otherwise: EMCal }; enum SubGeneratorId { @@ -103,29 +104,27 @@ enum SubGeneratorId { }; struct Meson { - Meson(Photon p1, Photon p2) : pgamma1(p1), - pgamma2(p2) + Meson(Photon const& p1, Photon const& p2) : pgamma1(p1), pgamma2(p2), pMeson(p1.photon + p2.photon) { - pMeson = p1.photon + p2.photon; } Photon pgamma1; Photon pgamma2; ROOT::Math::PxPyPzEVector pMeson; - float getMass() const { return pMeson.M(); } - float getPt() const { return pMeson.Pt(); } - float getOpeningAngle() const + [[nodiscard]] float getMass() const { return pMeson.M(); } + [[nodiscard]] float getPt() const { return pMeson.Pt(); } + [[nodiscard]] float getOpeningAngle() const { float cosAngle = pgamma1.photon.Vect().Dot(pgamma2.photon.Vect()) / (pgamma1.photon.P() * pgamma2.photon.P()); float angle = std::acos(std::clamp(cosAngle, -1.0f, 1.0f)); return angle; } - ROOT::Math::PxPyPzEVector getMathVector() const { return pMeson; } + [[nodiscard]] ROOT::Math::PxPyPzEVector getMathVector() const { return pMeson; } }; struct EventMixVec { - void addEvent(std::vector vecGamma) + void addEvent(std::vector const& vecGamma) { if (vecEvtMix.size() < nEVtMixSize) { vecEvtMix.push_back(vecGamma); @@ -147,9 +146,9 @@ struct EmcalPi0Qc { // configurable parameters // TODO adapt mDoEventSel switch to also allow selection of other triggers (e.g. EMC7) - Configurable mDoEventSel{"mDoEventSel", 0, "demand kINT7"}; - Configurable mRequireCaloReadout{"mRequireCaloReadout", 0, "require kTVXinEMC"}; - Configurable mRequireEMCalCells{"mRequireEMCalCells", 0, "require at least one EMC cell in each collision"}; + Configurable mDoEventSel{"mDoEventSel", false, "demand kINT7"}; + Configurable mRequireCaloReadout{"mRequireCaloReadout", false, "require kTVXinEMC"}; + Configurable mRequireEMCalCells{"mRequireEMCalCells", false, "require at least one EMC cell in each collision"}; Configurable mVetoBCID{"mVetoBCID", "", "BC ID(s) to be excluded, this should be used as an alternative to the event selection"}; Configurable mSelectBCID{"mSelectBCID", "all", "BC ID(s) to be included, this should be used as an alternative to the event selection"}; Configurable mVertexCut{"mVertexCut", -1, "apply z-vertex cut with value in cm"}; @@ -161,8 +160,8 @@ struct EmcalPi0Qc { Configurable mMinNCellsCut{"mMinNCellsCut", 1, "apply min cluster number of cell cut"}; Configurable mMinOpenAngleCut{"mMinOpenAngleCut", 0.0202, "apply min opening angle cut"}; Configurable mClusterDefinition{"mClusterDefinition", "kV3Default", "cluster definition to be selected, e.g. V3Default"}; - Configurable mSplitEMCalDCal{"mSplitEMCalDCal", 0, "Create and fill inv mass histograms for photons on EMCal and DCal individually"}; - Configurable mDoSumw2{"mDoSumw2", 1, "enable Sumw2 for all histograms"}; + Configurable mSplitEMCalDCal{"mSplitEMCalDCal", false, "Create and fill inv mass histograms for photons on EMCal and DCal individually"}; + Configurable mDoSumw2{"mDoSumw2", true, "enable Sumw2 for all histograms"}; std::vector mVetoBCIDs; std::vector mSelectBCIDs; @@ -295,10 +294,10 @@ struct EmcalPi0Qc { mHistManager.add(Form("mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM%d", ism), Form("invariant mass vs collision timestamp in Supermodule %d", ism), HistType::kTH2F, {invmassAxis, collisionTimeAxis}, mDoSumw2.value); } - if (mVetoBCID->length()) { + if (!mVetoBCID->empty()) { std::stringstream parser(mVetoBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Veto BCID " << bcid; @@ -308,7 +307,7 @@ struct EmcalPi0Qc { if (mSelectBCID.value != "all") { std::stringstream parser(mSelectBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Select BCID " << bcid; @@ -326,9 +325,9 @@ struct EmcalPi0Qc { template void supermoduleHistHelperPhoton(float time, float m02, int NCell, float timeSinceSOR) { - static constexpr std::string_view ClusterTimeHistSM[20] = {"clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM0", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM1", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM2", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM3", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM4", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM5", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM6", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM7", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM8", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM9", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM10", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM11", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM12", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM13", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM14", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM15", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM16", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM17", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM18", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM19"}; - static constexpr std::string_view ClusterNcellHistSM[20] = {"clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM0", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM1", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM2", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM3", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM4", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM5", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM6", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM7", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM8", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM9", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM10", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM11", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM12", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM13", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM14", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM15", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM16", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM17", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM18", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM19"}; - static constexpr std::string_view ClusterM02HistSM[20] = {"clusterM02VsTimeStamp/clusterM02VsTimeStampSM0", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM1", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM2", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM3", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM4", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM5", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM6", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM7", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM8", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM9", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM10", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM11", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM12", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM13", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM14", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM15", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM16", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM17", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM18", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM19"}; + static constexpr std::array ClusterTimeHistSM = {"clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM0", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM1", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM2", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM3", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM4", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM5", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM6", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM7", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM8", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM9", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM10", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM11", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM12", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM13", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM14", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM15", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM16", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM17", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM18", "clusterTimeVsTimeStamp/clusterTimeVsTimeStampSM19"}; + static constexpr std::array ClusterNcellHistSM = {"clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM0", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM1", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM2", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM3", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM4", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM5", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM6", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM7", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM8", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM9", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM10", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM11", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM12", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM13", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM14", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM15", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM16", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM17", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM18", "clusterNcellVsTimeStamp/clusterNCellVsTimeStampSM19"}; + static constexpr std::array ClusterM02HistSM = {"clusterM02VsTimeStamp/clusterM02VsTimeStampSM0", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM1", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM2", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM3", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM4", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM5", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM6", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM7", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM8", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM9", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM10", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM11", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM12", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM13", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM14", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM15", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM16", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM17", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM18", "clusterM02VsTimeStamp/clusterM02VsTimeStampSM19"}; mHistManager.fill(HIST(ClusterTimeHistSM[supermoduleID]), time, timeSinceSOR, mWeight); mHistManager.fill(HIST(ClusterNcellHistSM[supermoduleID]), NCell, timeSinceSOR, mWeight); mHistManager.fill(HIST(ClusterM02HistSM[supermoduleID]), m02, timeSinceSOR, mWeight); @@ -337,7 +336,7 @@ struct EmcalPi0Qc { template void supermoduleHistHelperMeson(float minv, float timeSinceSOR) { - static constexpr std::string_view MesonInvMassHistSM[20] = {"mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM0", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM1", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM2", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM3", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM4", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM5", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM6", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM7", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM8", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM9", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM10", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM11", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM12", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM13", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM14", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM15", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM16", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM17", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM18", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM19"}; + static constexpr std::array MesonInvMassHistSM = {"mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM0", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM1", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM2", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM3", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM4", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM5", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM6", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM7", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM8", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM9", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM10", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM11", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM12", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM13", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM14", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM15", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM16", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM17", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM18", "mesonInvMassVsTimeStamp/mesonInvMassVsTimeStampSM19"}; mHistManager.fill(HIST(MesonInvMassHistSM[supermoduleID]), minv, timeSinceSOR, mWeight); } @@ -490,7 +489,7 @@ struct EmcalPi0Qc { auto cellIter = cells.begin(); auto bcIter = bcs.begin(); - int runNumber = bcIter.runNumber(); + int runNumber = 0; std::unordered_map cellGlobalBCs; // Build map of number of cells for corrected BCs using global BCs // used later in the determination whether a BC has EMC cell content (for speed reason) @@ -607,7 +606,7 @@ struct EmcalPi0Qc { { auto cellIter = cells.begin(); auto bcIter = bcs.begin(); - int runNumber = bcIter.runNumber(); + int runNumber = 0; std::unordered_map cellGlobalBCs; // Build map of number of cells for corrected BCs using global BCs // used later in the determination whether a BC has EMC cell content (for speed reason) @@ -747,7 +746,7 @@ struct EmcalPi0Qc { LOG(info) << "Event rejected because of veto BCID " << eventIR.bc; return; } - if (mSelectBCIDs.size() && (std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), eventIR.bc) == mSelectBCIDs.end())) { + if (!mSelectBCIDs.empty() && (std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), eventIR.bc) == mSelectBCIDs.end())) { return; } mHistManager.fill(HIST("eventBCSelected"), eventIR.bc); @@ -802,8 +801,9 @@ struct EmcalPi0Qc { fillClusterQAHistos(cluster); - if (clusterRejectedByCut(cluster)) + if (clusterRejectedByCut(cluster)) { continue; + } fillClusterQAHistos(cluster); @@ -818,15 +818,15 @@ struct EmcalPi0Qc { { // In this implementation the cluster properties are directly loaded from the flat table, // in the future one should consider using the AnalysisCluster object to work with after loading. - static constexpr std::string_view ClusterQAHistEnergy[2] = {"ClustersBeforeCuts/clusterE", "ClustersAfterCuts/clusterE"}; - static constexpr std::string_view ClusterQAHistEnergySimpleBinning[2] = {"ClustersBeforeCuts/clusterE_SimpleBinning", "ClustersAfterCuts/clusterE_SimpleBinning"}; - static constexpr std::string_view ClusterQAHistTime[2] = {"ClustersBeforeCuts/clusterTime", "ClustersAfterCuts/clusterTime"}; - static constexpr std::string_view ClusterQAHistEtaPhi[2] = {"ClustersBeforeCuts/clusterEtaPhi", "ClustersAfterCuts/clusterEtaPhi"}; - static constexpr std::string_view ClusterQAHistM02[2] = {"ClustersBeforeCuts/clusterM02", "ClustersAfterCuts/clusterM02"}; - static constexpr std::string_view ClusterQAHistM20[2] = {"ClustersBeforeCuts/clusterM20", "ClustersAfterCuts/clusterM20"}; - static constexpr std::string_view ClusterQAHistNLM[2] = {"ClustersBeforeCuts/clusterNLM", "ClustersAfterCuts/clusterNLM"}; - static constexpr std::string_view ClusterQAHistNCells[2] = {"ClustersBeforeCuts/clusterNCells", "ClustersAfterCuts/clusterNCells"}; - static constexpr std::string_view ClusterQAHistDistanceToBadChannel[2] = {"ClustersBeforeCuts/clusterDistanceToBadChannel", "ClustersAfterCuts/clusterDistanceToBadChannel"}; + static constexpr std::array ClusterQAHistEnergy = {"ClustersBeforeCuts/clusterE", "ClustersAfterCuts/clusterE"}; + static constexpr std::array ClusterQAHistEnergySimpleBinning = {"ClustersBeforeCuts/clusterE_SimpleBinning", "ClustersAfterCuts/clusterE_SimpleBinning"}; + static constexpr std::array ClusterQAHistTime = {"ClustersBeforeCuts/clusterTime", "ClustersAfterCuts/clusterTime"}; + static constexpr std::array ClusterQAHistEtaPhi = {"ClustersBeforeCuts/clusterEtaPhi", "ClustersAfterCuts/clusterEtaPhi"}; + static constexpr std::array ClusterQAHistM02 = {"ClustersBeforeCuts/clusterM02", "ClustersAfterCuts/clusterM02"}; + static constexpr std::array ClusterQAHistM20 = {"ClustersBeforeCuts/clusterM20", "ClustersAfterCuts/clusterM20"}; + static constexpr std::array ClusterQAHistNLM = {"ClustersBeforeCuts/clusterNLM", "ClustersAfterCuts/clusterNLM"}; + static constexpr std::array ClusterQAHistNCells = {"ClustersBeforeCuts/clusterNCells", "ClustersAfterCuts/clusterNCells"}; + static constexpr std::array ClusterQAHistDistanceToBadChannel = {"ClustersBeforeCuts/clusterDistanceToBadChannel", "ClustersAfterCuts/clusterDistanceToBadChannel"}; mHistManager.fill(HIST(ClusterQAHistEnergy[BeforeCuts]), cluster.energy(), mWeight); mHistManager.fill(HIST(ClusterQAHistEnergySimpleBinning[BeforeCuts]), cluster.energy(), mWeight); mHistManager.fill(HIST(ClusterQAHistTime[BeforeCuts]), cluster.time(), mWeight); @@ -871,8 +871,9 @@ struct EmcalPi0Qc { LOG(debug) << "processMesons " << mPhotons.size(); // if less then 2 clusters are found, skip event - if (mPhotons.size() < 2) + if (mPhotons.size() < 2) { return; + } // loop over all photon combinations and build meson candidates for (unsigned int ig1 = 0; ig1 < mPhotons.size(); ++ig1) { @@ -932,7 +933,6 @@ struct EmcalPi0Qc { lvRotationPion = meson.getMathVector(); // calculate rotation axis and matrix - lvRotationPion = lvRotationPhoton1 + lvRotationPhoton2; ROOT::Math::AxisAngle rotationAxis(lvRotationPion.Vect(), rotationAngle); ROOT::Math::Rotation3D rotationMatrix(rotationAxis); @@ -972,7 +972,7 @@ struct EmcalPi0Qc { } } - void calculateMixedBack(Photon gamma) + void calculateMixedBack(Photon const& gamma) { for (unsigned int i = 0; i < evtMix.vecEvtMix.size(); ++i) { for (unsigned int ig1 = 0; ig1 < evtMix.vecEvtMix[i].size(); ++ig1) { @@ -1004,7 +1004,7 @@ struct EmcalPi0Qc { result.emplace_back(0.10 * i); } else if (i < 140) { result.emplace_back(10. + 0.25 * (i - 100)); - } else if (i < 180) { + } else if (i < 179) { result.emplace_back(20. + 1.00 * (i - 140)); } else { result.emplace_back(maxPt); @@ -1014,10 +1014,10 @@ struct EmcalPi0Qc { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc, TaskName{"EmcalPi0QcAssociate"}, SetDefaultProcesses{{{"processCollision", true}, {"processCollisionMC", false}, {"processAmbiguous", false}}}), // o2-linter: disable=name/o2-task (adapted multiple times) - adaptAnalysisTask(cfgc, TaskName{"EmcalPi0QcAmbiguous"}, SetDefaultProcesses{{{"processCollision", false}, {"processCollisionMC", false}, {"processAmbiguous", true}}})}; // o2-linter: disable=name/o2-task (adapted multiple times) + adaptAnalysisTask(context, TaskName{"EmcalPi0QcAssociate"}, SetDefaultProcesses{{{"processCollision", true}, {"processCollisionMC", false}, {"processAmbiguous", false}}}), // o2-linter: disable=name/o2-task (adapted multiple times) + adaptAnalysisTask(context, TaskName{"EmcalPi0QcAmbiguous"}, SetDefaultProcesses{{{"processCollision", false}, {"processCollisionMC", false}, {"processAmbiguous", true}}})}; // o2-linter: disable=name/o2-task (adapted multiple times) return workflow; } diff --git a/PWGEM/PhotonMeson/Tasks/emcalQC.cxx b/PWGEM/PhotonMeson/Tasks/emcalQC.cxx index 39af167b327..def4442d5d5 100644 --- a/PWGEM/PhotonMeson/Tasks/emcalQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/emcalQC.cxx @@ -25,8 +25,10 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" +#include #include #include +#include #include #include #include @@ -97,6 +99,12 @@ struct EmcalQC { Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; } emccuts; + struct : ConfigurableGroup { + std::string prefix = "axis_group"; + ConfigurableAxis thnConfigAxisE{"thnConfigAxisE", {100, 0., 20.}, "energy axis for photon candidates"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {100, 0., 20.}, "pT axis for matched tracks"}; + } axisGroup; + void defineEMEventCut() { fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); @@ -146,6 +154,11 @@ struct EmcalQC { defineEMCCut(); defineEMEventCut(); + const AxisSpec thnAxisE{axisGroup.thnConfigAxisE, "#it{E}} (GeV)"}; + const AxisSpec thnAxisPt{axisGroup.thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thAxisdEta{200, -0.06, 0.06, "#Delta#eta"}; + const AxisSpec thAxisdPhi{200, -0.06, 0.06, "#Delta#varphi (rad)"}; + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); auto hEMCCollisionCounter = fRegistry.add("Event/hEMCCollisionCounter", "Number of collisions after event cuts", HistType::kTH1D, {{7, 0.5, 7.5}}, false); hEMCCollisionCounter->GetXaxis()->SetBinLabel(1, "all"); @@ -156,6 +169,18 @@ struct EmcalQC { hEMCCollisionCounter->GetXaxis()->SetBinLabel(6, "+unique"); // TVX with z < 10cm and Sel8 and good z xertex and unique (only collision in the BC) hEMCCollisionCounter->GetXaxis()->SetBinLabel(7, "+EMC readout"); // TVX with z < 10cm and Sel8 and good z xertex and unique (only collision in the BC) and kTVXinEMC o2::aod::pwgem::photonmeson::utils::clusterhistogram::addClusterHistograms(&fRegistry, cfgDo2DQA); + + if (doprocessQCTM) { + fRegistry.add("Cluster/hDeltaEtaPhiAllPrimTracks", "#Delta#eta #Delta#varphi distribution of all matched prim. tracks", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisE}, false); + fRegistry.add("Cluster/hDeltaEtaPhiClosestPrimTracks", "#Delta#eta #Delta#varphi distribution of the Closest matched prim. track", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisE}, false); + fRegistry.add("Cluster/hDeltaEtaPhiAllSecTracks", "#Delta#eta #Delta#varphi distribution of all matched sec. tracks", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisE}, false); + fRegistry.add("Cluster/hDeltaEtaPhiClosestSecTracks", "#Delta#eta #Delta#varphi distribution of the Closest matched sec. track", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisE}, false); + + fRegistry.add("Cluster/hDeltaEtaPhiAllPrimTracksTrackPt", "#Delta#eta #Delta#varphi distribution of all matched prim. tracks", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisPt}, false); + fRegistry.add("Cluster/hDeltaEtaPhiClosestPrimTracksTrackPt", "#Delta#eta #Delta#varphi distribution of the Closest matched prim. track", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisPt}, false); + fRegistry.add("Cluster/hDeltaEtaPhiAllSecTracksTrackPt", "#Delta#eta #Delta#varphi distribution of all matched sec. tracks", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisPt}, false); + fRegistry.add("Cluster/hDeltaEtaPhiClosestSecTracksTrackPt", "#Delta#eta #Delta#varphi distribution of the Closest matched sec. track", HistType::kTH3D, {thAxisdEta, thAxisdPhi, thnAxisPt}, false); + } } bool isEventGood(MyCollision const& collision) @@ -189,7 +214,7 @@ struct EmcalQC { if (!fEMEventCut.IsSelected(collision)) { return false; } - if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { + if (!(eventcuts.cfgOccupancyMin <= collision.trackOccupancyInTimeRange()) || !(collision.trackOccupancyInTimeRange() < eventcuts.cfgOccupancyMax)) { return false; } @@ -216,11 +241,11 @@ struct EmcalQC { // Define two boleans to see, whether the cluster "survives" the EMC cluster cuts to later check, whether the cuts in this task align with the ones in EMCPhotonCut.h: bool survivesIsSelectedEMCalCuts = true; // Survives "manual" cuts listed in this task - bool survivesIsSelectedCuts = false; + bool survivesIsSelectedCuts = true; survivesIsSelectedCuts = fEMCCut.IsSelected(cluster); for (int icut = 0; icut < static_cast(EMCPhotonCut::EMCPhotonCuts::kNCuts); icut++) { // Loop through different cut observables - EMCPhotonCut::EMCPhotonCuts specificcut = static_cast(icut); + auto specificcut = static_cast(icut); if (!fEMCCut.IsSelectedEMCal(specificcut, cluster)) { // Check whether cluster passes this cluster requirement, if not, fill why in the next row fRegistry.fill(HIST("Cluster/hClusterQualityCuts"), icut + 1, cluster.e(), collision.weight()); survivesIsSelectedEMCalCuts = false; @@ -240,12 +265,10 @@ struct EmcalQC { } } fRegistry.fill(HIST("Cluster/after/hNgamma"), ngAfter, collision.weight()); - - return; } template - void doClusterQA(MyCollision const& collision, TClusters const& clusters, TMatchedTracks const& primTracks, TMatchedSecondaries const& secTracks) + void doClusterQAWithTM(MyCollision const& collision, TClusters const& clusters, TMatchedTracks const& primTracks, TMatchedSecondaries const& secTracks) { fRegistry.fill(HIST("Cluster/before/hNgamma"), clusters.size(), collision.weight()); int ngAfter = 0; @@ -263,11 +286,11 @@ struct EmcalQC { // Define two boleans to see, whether the cluster "survives" the EMC cluster cuts to later check, whether the cuts in this task align with the ones in EMCPhotonCut.h: bool survivesIsSelectedEMCalCuts = true; // Survives "manual" cuts listed in this task - bool survivesIsSelectedCuts = false; + bool survivesIsSelectedCuts = true; survivesIsSelectedCuts = fEMCCut.IsSelected(cluster, primTracksPerCluster, secTracksPerCluster); for (int icut = 0; icut < static_cast(EMCPhotonCut::EMCPhotonCuts::kNCuts); icut++) { // Loop through different cut observables - EMCPhotonCut::EMCPhotonCuts specificcut = static_cast(icut); + auto specificcut = static_cast(icut); if (specificcut == EMCPhotonCut::EMCPhotonCuts::kTM || specificcut == EMCPhotonCut::EMCPhotonCuts::kSecondaryTM) { // will do track matching cuts extra later or never depending on chosen process function continue; @@ -277,6 +300,24 @@ struct EmcalQC { survivesIsSelectedEMCalCuts = false; } } + if (primTracksPerCluster.size() > 0) { + const auto closestPrimTrack = primTracksPerCluster.begin(); + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiClosestPrimTracks"), closestPrimTrack.deltaEta(), closestPrimTrack.deltaPhi(), cluster.e()); + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiClosestPrimTracksTrackPt"), closestPrimTrack.deltaEta(), closestPrimTrack.deltaPhi(), closestPrimTrack.trackPt()); + } + for (const auto& matchedPrimTrack : primTracksPerCluster) { + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiAllPrimTracks"), matchedPrimTrack.deltaEta(), matchedPrimTrack.deltaPhi(), cluster.e()); + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiAllPrimTracksTrackPt"), matchedPrimTrack.deltaEta(), matchedPrimTrack.deltaPhi(), matchedPrimTrack.trackPt()); + } + if (secTracksPerCluster.size() > 0) { + const auto closestSecTrack = secTracksPerCluster.begin(); + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiClosestSecTracks"), closestSecTrack.deltaEta(), closestSecTrack.deltaPhi(), cluster.e()); + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiClosestSecTracksTrackPt"), closestSecTrack.deltaEta(), closestSecTrack.deltaPhi(), closestSecTrack.trackPt()); + } + for (const auto& matchedSecTrack : secTracksPerCluster) { + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiAllSecTracks"), matchedSecTrack.deltaEta(), matchedSecTrack.deltaPhi(), cluster.e()); + fRegistry.fill(HIST("Cluster/hDeltaEtaPhiAllSecTracksTrackPt"), matchedSecTrack.deltaEta(), matchedSecTrack.deltaPhi(), matchedSecTrack.trackPt()); + } if (!fEMCCut.IsSelectedEMCal(EMCPhotonCut::EMCPhotonCuts::kTM, cluster, primTracksPerCluster)) { // Check whether cluster passes this cluster requirement, if not, fill why in the next row fRegistry.fill(HIST("Cluster/hClusterQualityCuts"), static_cast(EMCPhotonCut::EMCPhotonCuts::kTM) + 1, cluster.e(), collision.weight()); @@ -300,8 +341,6 @@ struct EmcalQC { } } fRegistry.fill(HIST("Cluster/after/hNgamma"), ngAfter, collision.weight()); - - return; } void processQC(MyCollisions const& collisions, EMCalPhotons const& clusters) @@ -327,7 +366,7 @@ struct EmcalQC { } auto clustersPerColl = clusters.sliceBy(perCollisionEMC, collision.collisionId()); - doClusterQA(collision, clustersPerColl, matchedPrims, matchedSeconds); + doClusterQAWithTM(collision, clustersPerColl, matchedPrims, matchedSeconds); } // end of collision loop } // end of process @@ -339,7 +378,7 @@ struct EmcalQC { PROCESS_SWITCH(EmcalQC, processDummy, "Dummy function", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx b/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx new file mode 100644 index 00000000000..5ac8343abf8 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/eventNormalizationTask.cxx @@ -0,0 +1,70 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file eventNormalizationTask.cxx +/// \brief task to produce histogram with event counter information +/// \author Joshua Konig, joshua.konig@cern.ch + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct EventNormalizationTask { + + HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + void init(InitContext& /*context*/) + { + // Names derived from EventAcceptance bits in EventTables.h + + fRegistry.add("EventNormalization/hEventCounter", "Event Counter;category;#it{N}_{evt}", kTH1D, {{static_cast(o2::aod::pmevsel::vecEventAccBitNames.size()), -0.5, -0.5 + static_cast(o2::aod::pmevsel::vecEventAccBitNames.size())}}, false); + for (size_t i = 0; i < o2::aod::pmevsel::vecEventAccBitNames.size(); ++i) { + fRegistry.get(HIST("EventNormalization/hEventCounter"))->GetXaxis()->SetBinLabel(i + 1, o2::aod::pmevsel::vecEventAccBitNames[i].c_str()); + } + } + + // Process function only takes the entries and fills them into the histogram. This is done per DF and not per collision + void processNorm(o2::aod::PMEvSelBit const& evSelBit) + { + + auto vecEvSelBits = evSelBit.eventSelectionBit(); + for (size_t i = 0; i < vecEvSelBits.size(); ++i) { + fRegistry.fill(HIST("EventNormalization/hEventCounter"), i, vecEvSelBits[i]); + } + } + + PROCESS_SWITCH(EventNormalizationTask, processNorm, "run event normalization task", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{ + adaptAnalysisTask(context, TaskName{"event-normalization-task"})}; +} diff --git a/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx b/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx index fc390c4ce65..6a0ca911f01 100644 --- a/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx +++ b/PWGEM/PhotonMeson/Tasks/mcGeneratorStudies.cxx @@ -8,23 +8,25 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// + /// \file mcGeneratorStudies.cxx -/// /// \brief Task that produces the generated pT spectrum of a given particle for MC studies based on on the fly MC simulations -/// /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) - Goethe University Frankfurt -/// -#include +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" + #include #include #include +#include #include #include #include #include +#include +#include + #include using namespace o2; @@ -34,25 +36,67 @@ struct MCGeneratorStudies { HistogramRegistry mHistManager{"MCGeneratorStudyHistograms"}; Configurable cfgSelectedParticleCode{"cfgSelectedParticleCode", 111, "PDG code of the particle to be investigated"}; Configurable cfgMaxZVertex{"cfgMaxZVertex", 10, "Maximum absolute z-vertex distance (cm)"}; - Configurable cfgRapidityCut{"cfgRapidityCut", 0.8, "Maximum absolute rapditity of selected generated particles"}; - ConfigurableAxis cfgMultiplicityBinning{"cfgMultiplicityBinning", {1000, 0, 10000}, "Binning used for the binning of the number of particles in the event"}; + Configurable cfgRapidityCut{"cfgRapidityCut", 0.8, "Maximum absolute rapidity of selected generated particles"}; expressions::Filter zVertexFilter = aod::mccollision::posZ < cfgMaxZVertex && aod::mccollision::posZ > -cfgMaxZVertex; void init(InitContext const&) { - mHistManager.add("Multiplicity", "Number of generated particles per MC collision;#bf{#it{N} (Multiplicity)};#bf{#it{N}_{MC collisions}}", HistType::kTH1F, {cfgMultiplicityBinning}); - mHistManager.add("YieldVsMultiplicity", "pT of selected particles in all MC collisions vs number of all generated particles in event;#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N} (Multiplicity)};#bf{#it{N}}", HistType::kTH2F, {{200, 0, 20}, cfgMultiplicityBinning}); + mHistManager.add("Yield", Form("Yield of primary selected particles in all MC collisions (PDG code %d);#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N}}", cfgSelectedParticleCode.value), HistType::kTH1D, {{250, 0, 25}}); + mHistManager.add("FullYield", Form("Yield of selected particles in all MC collisions (PDG code %d);#bf{#it{p}_{T} (GeV/#it{c})};#bf{#it{N}}", cfgSelectedParticleCode.value), HistType::kTH1D, {{250, 0, 25}}); + mHistManager.add("MCGenXSec", "Generated cross section = GetBinContent(1)/GetBinContent(2)", HistType::kTH1D, {{2, 0, 2}}); + auto hX = mHistManager.get(HIST("MCGenXSec")); + hX->GetXaxis()->SetBinLabel(1, "#sigma_{gen} (b)"); + hX->GetXaxis()->SetBinLabel(2, "nStreams"); + hX->SetBinContent(2, 1); // Unity to enable normalization by number of streams + hX->SetBinError(2, 0); + mHistManager.add("RapidityCut", "#Delta y = GetBinContent(1)/GetBinContent(2)", HistType::kTH1D, {{2, 0, 2}}); + auto hY = mHistManager.get(HIST("RapidityCut")); + hY->GetXaxis()->SetBinLabel(1, "2#Delta y"); + hY->GetXaxis()->SetBinLabel(2, "nStreams"); + hY->SetBinContent(1, 2 * cfgRapidityCut); + hY->SetBinContent(2, 1); // Unity to enable normalization by number of streams + hY->SetBinError(2, 0); + mHistManager.add("EventWeight", "MC collision weight;Event weight;#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{1000, 0, 10}}); + mHistManager.add("MCGenXSecDistribution", "Generated cross section distribution;#sigma_{gen} (b);#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{20000, 0, 2}}); + mHistManager.add("MCGenXSecErrDistribution", "Generated cross section error distribution;#sigma_{gen}^{err} (b);#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{20000, 0, 2}}); + mHistManager.add("EventWeightSum", "Sum of event weights", HistType::kTH1D, {{2, 0., 2.}}, true); + auto hE = mHistManager.get(HIST("EventWeightSum")); + hE->GetXaxis()->SetBinLabel(1, "#Sigma w"); + hE->GetXaxis()->SetBinLabel(2, "N_{MC Collisions}"); + mHistManager.add("zVertex", "zVertex position;#bf{z (cm)};#bf{#it{N}_{MC Collisions}}", HistType::kTH1D, {{500, -25., 25.}}, true); } - void process(soa::Filtered::iterator const&, aod::McParticles const& mcParticles) + void process(soa::Filtered::iterator const& mcCollision, aod::McParticles const& mcParticles, aod::HepMCXSections const& crossSections) { - int nParticles = mcParticles.size(); - mHistManager.fill(HIST("Multiplicity"), nParticles); + const double w = mcCollision.weight(); + mHistManager.fill(HIST("EventWeight"), w); + mHistManager.fill(HIST("EventWeightSum"), 0.5, w); // Fill with event weight (used for normalization) + mHistManager.fill(HIST("EventWeightSum"), 1.5, 1); // Fill with number of MC collisions + mHistManager.fill(HIST("zVertex"), mcCollision.posZ(), w); + if (crossSections.size() == 1) { + auto const xs = crossSections.begin(); + const double sigmaGen = xs.xsectGen() * 1E-12; // From pb to b + const double sigmaGenErr = xs.xsectErr() * 1E-12; // From pb to b + mHistManager.fill(HIST("MCGenXSecDistribution"), sigmaGen); + mHistManager.fill(HIST("MCGenXSecErrDistribution"), sigmaGenErr); + + auto hX = mHistManager.get(HIST("MCGenXSec")); + hX->SetBinContent(1, sigmaGen); // Set bin content to latest generated cross section and uncertainty + hX->SetBinError(1, sigmaGenErr); + } else { + LOG(warn) << "Multiple cross sections found: " << crossSections.size(); + } for (auto& mcParticle : mcParticles) { - if (mcParticle.pdgCode() == cfgSelectedParticleCode && std::abs(mcParticle.y()) < cfgRapidityCut) - mHistManager.fill(HIST("YieldVsMultiplicity"), mcParticle.pt(), nParticles); + if (mcParticle.pdgCode() == cfgSelectedParticleCode && std::abs(mcParticle.y()) < cfgRapidityCut) { + mHistManager.fill(HIST("FullYield"), mcParticle.pt(), w); + bool isPrimary = mcParticle.isPhysicalPrimary() || mcParticle.producedByGenerator(); + bool isFromWD = (aod::pwgem::photonmeson::utils::mcutil::IsFromWD(mcCollision, mcParticle, mcParticles)) > 0; + if (isPrimary && !isFromWD) { + mHistManager.fill(HIST("Yield"), mcParticle.pt(), w); + } + } } } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"mc-generator-studies"})}; } +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{adaptAnalysisTask(context, TaskName{"mc-generator-studies"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx new file mode 100644 index 00000000000..172e093488a --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/mesonSignalLoss.cxx @@ -0,0 +1,436 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// ======================== +// +// Calculate signal and event loss for neutral mesons and photons +// Joshua König, joshua.konig@cern.ch + +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/TriggerAliases.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct MesonSignalLoss { + + using MyBCs = soa::Join; + + using RecoCollisions = soa::Join; + using EventAccBits = o2::aod::pmevsel::EventAcceptanceBits; + + enum MultiplicityEstimator { kCentralBarrel, + kFT0A, + kFT0C }; + + // stage kStageHasReco + k means "passed event cut k of EventAccBits" + enum Stage { kStageAllMC = 0, + kStageTVX, + kStageHasReco }; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + Preslice mcCollisionsPerBC = aod::mccollision::bcId; + Preslice particlesPerMCCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted recoCollisionsPerMCCollision = aod::mccollisionlabel::mcCollisionId; + + struct : ConfigurableGroup { + std::string prefix = "axisConfig"; + ConfigurableAxis cfgPtAxis{"cfgPtAxis", {400, 0.f, 20.f}, "pT axis for generated particles"}; + ConfigurableAxis cfgMultiplicityAxis{"cfgMultiplicityAxis", {200, -0.5f, 199.5f}, "axis for true charged-particle multiplicity"}; + } axisConfig; + + struct : ConfigurableGroup { + std::string prefix = "particleConfig"; + Configurable cfgRapidity{"cfgRapidity", 0.8, "rapidity selection"}; + Configurable cfgSecondaryDecayLength{"cfgSecondaryDecayLength", 1.f, "Minimum ancestor decay length (cm) to classify a particle as secondary"}; + Configurable cfgEnableMultiplicityHistograms{"cfgEnableMultiplicityHistograms", false, "enable generated-particle and event-stage histograms versus true charged-particle multiplicity"}; + Configurable cfgMultiplicityEstimator{"cfgMultiplicityEstimator", "centralBarrel", "eta range for true multiplicity: centralBarrel, FT0A, or FT0C"}; + } particleConfig; + + struct : ConfigurableGroup { + std::string prefix = "rctConfig"; + Configurable cfgRequireGoodRCT{"cfgRequireGoodRCT", false, "require good detector flag in run condtion table"}; + Configurable cfgRCTLabel{"cfgRCTLabel", "CBT_hadronPID", "select 1 [CBT, CBT_hadronPID, CBT_muon_glo] see O2Physics/Common/CCDB/RCTSelectionFlags.h"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "set ZDC flag for PbPb"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } rctConfig; + + struct : ConfigurableGroup { + std::string prefix = "eventCuts"; + Configurable cfgZvtxMin{"cfgZvtxMin", -1e+10, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", 1e+10, "max. Zvtx"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", false, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. track occupancy"}; + Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. track occupancy"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2, "min. occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. occupancy"}; + Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "require no collision in time range standard"}; + Configurable cfgRequireTVXinEMC{"cfgRequireTVXinEMC", false, "require kTVXinEMC (only for EMC analyses)"}; + Configurable cfgRequireSel8{"cfgRequireSel8", false, "require sel8 condition"}; + } eventCuts; + + Service pdgDB{}; + o2::aod::rctsel::RCTFlagsChecker rctChecker; + MultiplicityEstimator multiplicityEstimator{kCentralBarrel}; + + /// \brief Creates the histograms used for signal-loss and event-selection analysis. + void Inithistograms() + { + registry.add("hBCCounter", "hBCCounter;;#it{N}_{BC}", kTH1F, {{3, -0.5, 3 - 0.5}}); + auto h = registry.get(HIST("hBCCounter")); + h->GetXaxis()->SetBinLabel(1, "All BCs"); + h->GetXaxis()->SetBinLabel(2, "kTVX fired"); + h->GetXaxis()->SetBinLabel(3, "kTVX fired && has MC collision"); + + registry.add("hMCCollStage", "hMCCollStage;;#it{N}_{MC coll}", kTH1F, {{20, -0.5, 20 - 0.5}}); + auto hStage = registry.get(HIST("hMCCollStage")); + hStage->GetXaxis()->SetBinLabel(kStageAllMC + 1, "all MC coll"); + hStage->GetXaxis()->SetBinLabel(kStageTVX + 1, "kTVX fired"); + hStage->GetXaxis()->SetBinLabel(kStageHasReco + 1, "has reco coll"); + for (int cut = 1; cut < EventAccBits::kSize; ++cut) { + hStage->GetXaxis()->SetBinLabel(kStageHasReco + cut + 1, o2::aod::pmevsel::vecEventAccBitNames[cut].c_str()); + } + + registry.add("QC/hNRecoPerMCColl", "reco collisions per MC collision;N_{reco};N_{MC coll}", kTH1F, {{10, -0.5, 9.5}}); + registry.add("QC/hNSelectedRecoPerMCColl", "selected reco collisions per MC collision;N_{sel};N_{MC coll}", kTH1F, {{10, -0.5, 9.5}}); + registry.add("QC/hNContribVsOrder", "N contributors;order in matched list;N_{contrib}", kTH2F, {{10, -0.5, 9.5}, {200, 0, 200}}); + + const AxisSpec ptAxis{axisConfig.cfgPtAxis, "p_{T} (GeV/c)"}; + const AxisSpec multAxis{axisConfig.cfgMultiplicityAxis, "N_{ch}^{true}"}; + registry.add("Generated/Pi0/hGeneratedParticlePt", "Generated Pi0;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Eta/hGeneratedParticlePt", "Generated Eta;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Gamma/hGeneratedParticlePt", "Generated Gamma;p_{T} (GeV/c);selection stage", kTH2F, {ptAxis, {20, -0.5, 20 - 0.5}}); + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.add("Generated/Multiplicity/hMCCollStage", "MC collision stages vs. true multiplicity;N_{ch}^{true};selection stage", kTH2F, {multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Pi0/hGeneratedParticlePt", "Generated Pi0;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Eta/hGeneratedParticlePt", "Generated Eta;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + registry.add("Generated/Multiplicity/Gamma/hGeneratedParticlePt", "Generated Gamma;p_{T} (GeV/c);N_{ch}^{true};selection stage", kTH3F, {ptAxis, multAxis, {20, -0.5, 20 - 0.5}}); + } + } + + /// \brief Configures the multiplicity estimator and initializes histograms. + /// \param[in] context Workflow initialization context. + void init([[maybe_unused]] InitContext& context) + { + // Init multiplicity estimator (only relevant for multiplicity dependent analyses) + if (particleConfig.cfgMultiplicityEstimator.value == "centralBarrel") { + multiplicityEstimator = kCentralBarrel; + } else if (particleConfig.cfgMultiplicityEstimator.value == "FT0A") { + multiplicityEstimator = kFT0A; + } else if (particleConfig.cfgMultiplicityEstimator.value == "FT0C") { + multiplicityEstimator = kFT0C; + } else if (particleConfig.cfgEnableMultiplicityHistograms) { + LOGF(fatal, "Unknown cfgMultiplicityEstimator '%s'; use centralBarrel, FT0A, or FT0C", particleConfig.cfgMultiplicityEstimator.value.c_str()); + } + if (rctConfig.cfgRequireGoodRCT) { + rctChecker.init(rctConfig.cfgRCTLabel.value, rctConfig.cfgCheckZDC.value, rctConfig.cfgTreatLimitedAcceptanceAsBad.value); + } + Inithistograms(); + } + + /// \brief Checks whether eta lies in the configured multiplicity-estimator range. + /// \param[in] eta Pseudorapidity to check. + /// \return True if eta is inside the configured range, otherwise false. + bool isInMultiplicityEtaRange(float eta) const + { + bool decision = false; + if (multiplicityEstimator == kCentralBarrel) { + decision = std::abs(eta) < 0.8f; // o2-linter: disable=magic-number (rapidity range of central barrel) + } else if (multiplicityEstimator == kFT0A) { + decision = 3.5f < eta && eta < 4.9f; // o2-linter: disable=magic-number (rapidity range of FT0) + } else if (multiplicityEstimator == kFT0C) { + decision = -3.3f < eta && eta < -2.1f; // o2-linter: disable=magic-number (rapidity range of FT0) + } + return decision; + } + + /// \brief Counts charged physical-primary particles in the configured eta range. + /// \param[in] particles Particles to include in the multiplicity count. + /// \return Number of charged physical-primary particles in range. + template + int countTrueMultiplicity(TParticles const& particles) const + { + int multiplicity = 0; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || !isInMultiplicityEtaRange(particle.eta())) { + continue; + } + const auto* pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + if (pdgParticle && std::abs(pdgParticle->Charge()) >= 1.) { // o2-linter: disable=magic-number (check that particle has electric charge) + ++multiplicity; + } + } + return multiplicity; + } + + /// \brief Determines whether a particle has a secondary-production origin. + /// \param[in] particle Particle whose origin is checked. + /// \param[in] mcParticles MC-particle table used to trace the particle ancestry. + /// \return True if the particle is classified as secondary, otherwise false. + bool isSecondary(aod::McParticle const& particle, aod::McParticles const& mcParticles) const + { + std::vector particlesToCheck{particle.globalIndex()}; + std::unordered_set checkedParticles; + auto currentParticle = mcParticles.begin(); + auto mother = mcParticles.begin(); + + while (!particlesToCheck.empty()) { + const auto particleIndex = particlesToCheck.back(); + particlesToCheck.pop_back(); + if (particleIndex < 0 || particleIndex >= mcParticles.size() || !checkedParticles.insert(particleIndex).second) { + continue; + } + + currentParticle.setCursor(particleIndex); + const auto process = currentParticle.getProcess(); + const bool fromDecay = currentParticle.producedByGenerator() || process == TMCProcess::kPDecay || process == TMCProcess::kPRadDecay; + if (!currentParticle.producedByGenerator() && !fromDecay) { + return true; + } + + for (const auto motherIndex : currentParticle.mothersIds()) { + if (motherIndex < 0 || motherIndex >= mcParticles.size()) { + continue; + } + mother.setCursor(motherIndex); + const float decayLength = std::sqrt(std::pow(currentParticle.vx() - mother.vx(), 2) + std::pow(currentParticle.vy() - mother.vy(), 2) + std::pow(currentParticle.vz() - mother.vz(), 2)); + if (fromDecay && decayLength > particleConfig.cfgSecondaryDecayLength.value) { + return true; + } + particlesToCheck.push_back(motherIndex); + } + } + return false; + } + + /// \brief Fills generated-particle histograms for the requested selection stages. + /// \param[in] particlesInMCCollision Particles associated with the MC collision. + /// \param[in] mcParticles MC-particle table used to inspect particle ancestry. + /// \param[in] selectionStage First selection stage to fill. + /// \param[in] selectionStageMax Last selection stage to fill. + /// \param[in] trueMultiplicity True charged-particle multiplicity of the collision. + template + void fillGeneratedParticles(TParticles const& particlesInMCCollision, aod::McParticles const& mcParticles, int selectionStage, int selectionStageMax, int trueMultiplicity) + { + if (selectionStageMax < selectionStage) { + selectionStageMax = selectionStage; + } + for (const auto& particle : particlesInMCCollision) { + if (std::abs(particle.y()) > particleConfig.cfgRapidity) { + continue; + } + if (isSecondary(particle, mcParticles)) { + continue; + } + for (int iSel = selectionStage; iSel <= selectionStageMax; ++iSel) { + switch (particle.pdgCode()) { + case PDG_t::kPi0: + registry.fill(HIST("Generated/Pi0/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Pi0/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + case o2::constants::physics::Pdg::kEta: + registry.fill(HIST("Generated/Eta/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Eta/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + case PDG_t::kGamma: + registry.fill(HIST("Generated/Gamma/hGeneratedParticlePt"), particle.pt(), iSel); // also add rapdidity?? + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/Gamma/hGeneratedParticlePt"), particle.pt(), trueMultiplicity, iSel); + } + break; + default: + break; + } + } + } + } + + /// \brief Returns the event-acceptance bit for the first failed collision selection. + /// \param[in] collision Reconstructed collision to evaluate. + /// \return Event-acceptance bit identifying the first failed cut, or kSize if accepted. + template + int isSelectedEvent(TCollision const& collision) + { + if (!collision.has_mcCollision()) { + return EventAccBits::kHasMCColl; + } + + if (collision.posZ() < eventCuts.cfgZvtxMin || eventCuts.cfgZvtxMax < collision.posZ()) { + return EventAccBits::kGoodZVtx; + } + + if (eventCuts.cfgRequireFT0AND && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return EventAccBits::kIsFT0AND; + } + + if (eventCuts.cfgRequireNoTFB && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return EventAccBits::kNoTFB; + } + + if (eventCuts.cfgRequireNoITSROFB && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return EventAccBits::kITSROFB; + } + + if (eventCuts.cfgRequireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return EventAccBits::kNoSameBunchPileUp; + } + + if (eventCuts.cfgRequireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return EventAccBits::kGoodZVtxFTOPV; + } + + if (eventCuts.cfgRequireNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return EventAccBits::kNoCollInTimeRange; + } + + if (!(eventCuts.cfgTrackOccupancyMin <= collision.trackOccupancyInTimeRange() && collision.trackOccupancyInTimeRange() < eventCuts.cfgTrackOccupancyMax)) { + return EventAccBits::kGoodTrackOccupancy; + } + + if (!(eventCuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventCuts.cfgFT0COccupancyMax)) { + return EventAccBits::kGoodFT0Occupancy; + } + + if (eventCuts.cfgRequireTVXinEMC && !collision.alias_bit(triggerAliases::kTVXinEMC)) { + return EventAccBits::kTVXInEMC; + } + + // if constexpr (std::is_same_v, MyCollisions_Cent::iterator>) { + // std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + // if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { + // return false; + // } + // } + + if (rctConfig.cfgRequireGoodRCT && !rctChecker.checkTable(collision)) { + return EventAccBits::kGoodRCT; + } + + if (eventCuts.cfgRequireSel8 && !collision.sel8()) { + return EventAccBits::kGoodSel8; + } + + return EventAccBits::kSize; + } + + /// \brief Processes MC bunch crossings and fills generated signal-loss histograms. + /// \param[in] bcs Bunch crossings to process. + /// \param[in] collisions Reconstructed collisions and their MC labels. + /// \param[in] mcCollisions MC collisions to process. + /// \param[in] mcParticles MC particles used for multiplicity and ancestry checks. + void processMBMC(MyBCs const& bcs, RecoCollisions const& collisions, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles) + { + // loop over all bcs + for (const auto& bc : bcs) { + registry.fill(HIST("hBCCounter"), 0); + + // Check if TVX trigger fired in that bc + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + registry.fill(HIST("hBCCounter"), 1); + } + + auto mcCollisionsInBC = mcCollisions.sliceBy(mcCollisionsPerBC, bc.globalIndex()); + // check if has at least one mc collision + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX) && mcCollisionsInBC.size() > 0) { + registry.fill(HIST("hBCCounter"), 2); + } + + // loop over all mc collisions in the current bc (should be only 1 mc collision in most cases) + for (const auto& mcCollision : mcCollisionsInBC) { + int lastStage = kStageAllMC; + auto particlesInMCCollision = mcParticles.sliceBy(particlesPerMCCollision, mcCollision.globalIndex()); + const int trueMultiplicity = particleConfig.cfgEnableMultiplicityHistograms ? countTrueMultiplicity(particlesInMCCollision) : 0; + + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + lastStage = kStageTVX; + + auto matchedRecoCollisions = collisions.sliceBy(recoCollisionsPerMCCollision, mcCollision.globalIndex()); + registry.fill(HIST("QC/hNRecoPerMCColl"), matchedRecoCollisions.size()); + + // if at least one reco collision is found for the current mc collision, loop over all reco collisions and check if the collision is selected + if (matchedRecoCollisions.size() > 0) { + + int bestReject = EventAccBits::kHasMCColl; + int nSelected = 0; + int order = 0; + for (const auto& recColl : matchedRecoCollisions) { + // this function returns if the collision is accepted or not. If not, the reason is encoded in the return value + const int reject = isSelectedEvent(recColl); + // furthest-passing matched collision decides, so split vertices are not double counted + bestReject = std::max(bestReject, reject); + if (reject == EventAccBits::kSize) { + ++nSelected; + } + // Fill how many contributers to the primary vertex are in the current collision (QA info) + registry.fill(HIST("QC/hNContribVsOrder"), order++, recColl.numContrib()); + } + // Fill number of collisions that are assigned to the current mc collision + registry.fill(HIST("QC/hNSelectedRecoPerMCColl"), nSelected); + // this variable is used to keep track at which stage the collision was rejected or if it was accepted. It is used later for filling the generated meson distributions + lastStage = kStageHasReco + (bestReject - 1); + } + } + // Fill the particles of interested as a function of the reject reason (lastStage) + fillGeneratedParticles(particlesInMCCollision, mcParticles, kStageAllMC, lastStage, trueMultiplicity); + for (int stage = kStageAllMC; stage <= lastStage; ++stage) { + registry.fill(HIST("hMCCollStage"), stage); + if (particleConfig.cfgEnableMultiplicityHistograms) { + registry.fill(HIST("Generated/Multiplicity/hMCCollStage"), trueMultiplicity, stage); + } + } + } + } + } + PROCESS_SWITCH(MesonSignalLoss, processMBMC, "Process MC bunch crossings for signal and event loss", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx new file mode 100644 index 00000000000..d2d05d32ae5 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/pairQcTask.cxx @@ -0,0 +1,1460 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pairQCTask.cxx +/// \brief This task looks at the pair of V0 candidates. +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/AnalyticV0.h" +#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Utils/PairUtilities.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep +#include +#include // IWYU pragma: keep +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::pwgem::photonmeson; +namespace mcutil = o2::aod::pwgem::photonmeson::utils::mcutil; +namespace pairutil = o2::aod::pwgem::photonmeson::utils::pairutil; + +// ─── Event Information Tables ──────────────────────────────────────────── + +using MyCollisions = soa::Join; +using MyCollisionsMC = soa::Join; + +// ─── Photon Tables ──────────────────────────────────────────── + +using MyV0Photons = soa::Join; + +// ─── Assoc. Track Tables ──────────────────────────────────────────── + +using MyMCV0Legs = soa::Join; + +struct PairQCTask { + + static constexpr float KMinMagnitude = 1e-12f; + static constexpr float KMinCosine = 1e-12f; + static constexpr float KMinSigma = 1e-9f; + static constexpr float KMe = 0.000510999f; // electron mass, GeV/c^2 + + struct LegLite { + float pt{0.f}, eta{0.f}, phi{0.f}; + float nsigEl{0.f}, dcaZ{0.f}; + float nClsFindable{0.f}, nClsITS{0.f}; + bool timed{false}; // ITS, TRD or TOF: the track has its own time + bool foreign{false}; + int64_t trackId{-1}; + }; + + struct PhotonCand { + int64_t gi{-1}; + float fPt{0.f}, fEta{0.f}, fPhi{0.f}; + float fVx{0.f}, fVy{0.f}, fVz{0.f}; // conversion point, global (cm) + float fVtxZ{0.f}; // primary vertex z of the collision + float fDcaXYToPV{0.f}, fDcaZToPV{0.f}; + float psipair{0.f}, phiv{0.f}, chi2{0.f}, cospa{0.f}, pca{0.f}; + std::array leg{}; // 0 = e+, 1 = e- + pairutil::V0PhotonLegCounts legCounts{}; + pairutil::DedupCand dedupCand{}; + bool dedupRejected{false}; + AnalyticV0 own{}; // the photon rebuilt from its own legs (WP-B, WP-F) + mcutil::PhotonMCInfo mc{}; + [[nodiscard]] float pt() const { return fPt; } + [[nodiscard]] float eta() const { return fEta; } + [[nodiscard]] float phi() const { return fPhi; } + [[nodiscard]] float vx() const { return fVx; } + [[nodiscard]] float vy() const { return fVy; } + [[nodiscard]] float vz() const { return fVz; } + [[nodiscard]] float rConv() const { return std::hypot(fVx, fVy); } + [[nodiscard]] int nTimedLegs() const { return (leg[0].timed ? 1 : 0) + (leg[1].timed ? 1 : 0); } + [[nodiscard]] int nForeignLegs() const { return (leg[0].foreign ? 1 : 0) + (leg[1].foreign ? 1 : 0); } + [[nodiscard]] bool sharesTrackWith(PhotonCand const& o) const + { + return leg[0].trackId == o.leg[0].trackId || leg[0].trackId == o.leg[1].trackId || + leg[1].trackId == o.leg[0].trackId || leg[1].trackId == o.leg[1].trackId; + } + [[nodiscard]] LegParam legParam(int il) const + { + LegParam l; + l.xyz = {fVx, fVy, fVz - fVtxZ}; + const float ptL = leg[static_cast(il)].pt; + l.mom = {ptL * std::cos(leg[static_cast(il)].phi), ptL * std::sin(leg[static_cast(il)].phi), ptL * std::sinh(leg[static_cast(il)].eta)}; + l.charge = (il == 0) ? +1 : -1; + return l; + } + }; + + // Pair observables from the two conversion points and momenta. + struct PairObs { + ROOT::Math::PtEtaPhiMVector v1, v2; + float deltaR{0.f}, deltaZ{0.f}, deltaR3D{0.f}; + float opa{0.f}, drOverCosOA{0.f}; + float deta{0.f}, dphi{0.f}; + pairutil::PairQ q{}; + bool valid{true}; + }; + + struct CrossObs { + std::array fit{}; + std::array ana{}; + int nAltValidFit{0}, nAltValidAna{0}; + float maxDcaFit{999.f}, maxDcaXYFit{999.f}, maxDcaZFit{999.f}; + float maxDcaAna{999.f}, maxDcaXYAna{999.f}, maxDcaZAna{999.f}; + std::array mee{999.f, 999.f}; + float meeOverQ{999.f}; + std::array ownMee{999.f, 999.f}; + float ownMeeMax{999.f}, deltaMee{-999.f}, m4{999.f}; + bool evaluated{false}; + }; + + Service ccdb{}; + Configurable cfgCcdbUrl{"cfgCcdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; + Configurable cfgBzOverrideT{"cfgBzOverrideT", -999.f, "Bz in Tesla; used instead of CCDB if > -100"}; + + struct : ConfigurableGroup { + std::string prefix = "eventcut_group"; + Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; + Configurable cfgZvtxMax{"cfgZvtxMax", +10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", true, "require no TF border"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", true, "require no ITS ROF border"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC"}; + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx FT0 vs PV"}; + Configurable cfgTrackOccupancyMin{"cfgTrackOccupancyMin", -2, "min. track occupancy"}; + Configurable cfgTrackOccupancyMax{"cfgTrackOccupancyMax", 1000000000, "max. track occupancy"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -2.f, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000.f, "max. FT0C occupancy"}; + Configurable cfgRequireNoCollInTimeRangeStandard{"cfgRequireNoCollInTimeRangeStandard", false, "no coll in time range std"}; + Configurable cfgRequireNoCollInTimeRangeStrict{"cfgRequireNoCollInTimeRangeStrict", false, "no coll in time range strict"}; + Configurable cfgRequireNoCollInITSROFStandard{"cfgRequireNoCollInITSROFStandard", false, "no coll in ITS ROF std"}; + Configurable cfgRequireNoCollInITSROFStrict{"cfgRequireNoCollInITSROFStrict", false, "no coll in ITS ROF strict"}; + Configurable cfgRequireNoHighMultCollInPrevRof{"cfgRequireNoHighMultCollInPrevRof", false, "no HM coll in prev ROF"}; + Configurable cfgRequireGoodITSLayer3{"cfgRequireGoodITSLayer3", false, "ITS layer 3 OK"}; + Configurable cfgRequireGoodITSLayer0123{"cfgRequireGoodITSLayer0123", false, "ITS layers 0-3 OK"}; + Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "all ITS layers OK"}; + } eventcuts; + + struct : ConfigurableGroup { + std::string prefix = "pcmcut_group"; + Configurable cfgRequireV0WithITSTPC{"cfgRequireV0WithITSTPC", false, "select V0s with ITS-TPC tracks"}; + Configurable cfgRequireV0WithITSOnly{"cfgRequireV0WithITSOnly", false, "select V0s with ITS-only tracks"}; + Configurable cfgRequireV0WithTPCOnly{"cfgRequireV0WithTPCOnly", false, "select V0s with TPC-only tracks"}; + Configurable cfgMinPtV0{"cfgMinPtV0", 0.1, "min pT for V0 photons at PV"}; + Configurable cfgMaxEtaV0{"cfgMaxEtaV0", 0.8, "max eta for V0 photons at PV"}; + Configurable cfgMinV0Radius{"cfgMinV0Radius", 16.0, "min V0 radius"}; + Configurable cfgMaxV0Radius{"cfgMaxV0Radius", 90.0, "max V0 radius"}; + Configurable cfgMaxAlphaAP{"cfgMaxAlphaAP", 0.95, "max alpha for AP cut"}; + Configurable cfgMaxQtAP{"cfgMaxQtAP", 0.01, "max qT for AP cut"}; + Configurable cfgMinCosPA{"cfgMinCosPA", 0.997, "min V0 CosPA"}; + Configurable cfgMaxPCA{"cfgMaxPCA", 3.0, "max distance between 2 legs"}; + Configurable cfgMaxChi2KF{"cfgMaxChi2KF", 1e+10, "max chi2/ndf with KF"}; + Configurable cfgRejectV0OnITSIB{"cfgRejectV0OnITSIB", true, "reject V0s on ITSib"}; + Configurable cfgDisableITSOnlyTrack{"cfgDisableITSOnlyTrack", false, "disable ITS-only tracks"}; + Configurable cfgDisableTPCOnlyTrack{"cfgDisableTPCOnlyTrack", false, "disable TPC-only tracks"}; + Configurable cfgMinNClusterTPC{"cfgMinNClusterTPC", 70, "min ncluster TPC"}; + Configurable cfgMinNCrossedRows{"cfgMinNCrossedRows", 70, "min crossed rows"}; + Configurable cfgMaxFracSharedClustersTPC{"cfgMaxFracSharedClustersTPC", 999.f, "max fraction of shared TPC clusters"}; + Configurable cfgMaxChi2TPC{"cfgMaxChi2TPC", 4.0, "max chi2/NclsTPC"}; + Configurable cfgMaxChi2ITS{"cfgMaxChi2ITS", 36.0, "max chi2/NclsITS"}; + Configurable cfgMinTPCNsigmaEl{"cfgMinTPCNsigmaEl", -3.5, "min TPC nsigma electron"}; + Configurable cfgMaxTPCNsigmaEl{"cfgMaxTPCNsigmaEl", +3.5, "max TPC nsigma electron"}; + } pcmcuts; + + struct : ConfigurableGroup { + std::string prefix = "centralitySelection_group"; + Configurable cfgCentMin{"cfgCentMin", -1, "min. centrality"}; + Configurable cfgCentMax{"cfgCentMax", 999, "max. centrality"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; + } centralitySelection; + + struct : ConfigurableGroup { + std::string prefix = "ggpaircut_group"; + Configurable cfgMinDRCosOA{"cfgMinDRCosOA", -1.f, "min. dr/cosOA; <0 = disabled"}; + Configurable cfgDoRCut{"cfgDoRCut", false, "apply |R1-R2| > cfgMinDeltaR cut"}; + Configurable cfgMinDeltaR{"cfgMinDeltaR", 0.f, "minimum |R1-R2| (cm)"}; + Configurable cfgDoZCut{"cfgDoZCut", false, "apply |DeltaZ| > cfgMinDeltaZ cut"}; + Configurable cfgMinDeltaZ{"cfgMinDeltaZ", 0.f, "minimum |DeltaZ| (cm)"}; + Configurable cfgDoEllipseCut{"cfgDoEllipseCut", false, "reject pairs inside ellipse in DeltaEta-DeltaPhi"}; + Configurable cfgEllipseSigEta{"cfgEllipseSigEta", 0.1f, "sigma_eta for ellipse cut"}; + Configurable cfgEllipseSigPhi{"cfgEllipseSigPhi", 0.1f, "sigma_phi for ellipse cut"}; + Configurable cfgEllipseR2{"cfgEllipseR2", 1.0f, "R^2 threshold: reject if ellipse value < R^2"}; + Configurable cfgMaxAsymmetry{"cfgMaxAsymmetry", -1.f, "max |p_{T, 1} - p_{T, 2}|/(p_{T, 1} + p_{T, 2}) asymmetry cut"}; + Configurable cfgMaxDcaZToPV{"cfgMaxDcaZToPV", 999.f, "max |DCAz| of both V0 photons to the primary vertex (cm); 999 = off"}; + Configurable cfgMaxDcaXYToPV{"cfgMaxDcaXYToPV", 999.f, "max |DCAxy| of both V0 photons to the primary vertex (cm); 999 = off"}; + } ggpaircuts; + + struct : ConfigurableGroup { + std::string prefix = "dedup_group"; + Configurable cfgDoDedup{"cfgDoDedup", false, "REMOVE duplicate-like candidates before pairing (as photonhbt would); the QA is filled either way"}; + Configurable cfgDupMaxLegDEta{"cfgDupMaxLegDEta", 0.01f, "leg identical if |dEta| below this"}; + Configurable cfgDupMaxLegDPhi{"cfgDupMaxLegDPhi", 0.01f, "leg identical if |dPhi| below this (rad)"}; + Configurable cfgDupMaxLegPtAsym{"cfgDupMaxLegPtAsym", 0.05f, "leg identical if |pt1-pt2|/(pt1+pt2) below this"}; + Configurable cfgDupMaxLegDeDxAsym{"cfgDupMaxLegDeDxAsym", 0.05f, "leg identical if |dEdx1-dEdx2|/(dEdx1+dEdx2) below this; skipped if a leg has no TPC"}; + Configurable cfgDupRequireBothLegs{"cfgDupRequireBothLegs", false, "true: both same-charge leg pairs must be identical; false: one is enough"}; + Configurable cfgDupMaxDVtx3D{"cfgDupMaxDVtx3D", 999.f, "additionally require the 3D conversion-point distance below this (cm); 999 = off"}; + } dedup; + + struct : ConfigurableGroup { + std::string prefix = "crosspair_group"; + Configurable cfgDoCrossPairCut{"cfgDoCrossPairCut", false, "apply the crossed-pair veto (DCAFitter alternatives) as the last pair cut, as photonhbt does"}; + Configurable cfgCrossVetoMode{"cfgCrossVetoMode", 1, "veto criterion: 1 = geometric AND-chain (cfgAlt*), 2 = maxAltDca alone (cfgCrossMinMaxAltDca), 3 = 1 OR 2"}; + Configurable cfgCrossMinMaxAltDca{"cfgCrossMinMaxAltDca", 4.9f, "veto mode 2/3: reject when maxAltDca is BELOW this (cm); 0 disables"}; + Configurable cfgAltMaxQinv{"cfgAltMaxQinv", 0.15f, "evaluate the alternative pairings only below this q_inv"}; + Configurable cfgAltMaxDca{"cfgAltMaxDca", 1.5f, "alternative pairing photon-like if its legs approach closer than this at their PCA (cm) ..."}; + Configurable cfgAltMinR{"cfgAltMinR", 1.f, "... and the PCA lies at a radius above this (cm) ..."}; + Configurable cfgAltMaxR{"cfgAltMaxR", 90.f, "... and below this (cm) ..."}; + Configurable cfgAltMinCosPA{"cfgAltMinCosPA", 0.98f, "... and cosPA above this ..."}; + Configurable cfgAltMaxDcaXY{"cfgAltMaxDcaXY", 3.f, "... and DCAxy of the photon line to the vertex below this (cm) ..."}; + Configurable cfgAltMaxDcaZ{"cfgAltMaxDcaZ", 999.f, "... and DCAz below this (cm); 999 = not used"}; + Configurable cfgAltMaxMee{"cfgAltMaxMee", 0.1f, "... and its m_ee below this (GeV/c^2)"}; + Configurable cfgAltRequireBoth{"cfgAltRequireBoth", true, "true: veto only if BOTH alternative pairings are photon-like; false: one is enough"}; + } crosspair; + + struct : ConfigurableGroup { + std::string prefix = "qaflags_group"; + Configurable cfgMaxQinvForQA{"cfgMaxQinvForQA", 0.3f, "pair QA only below this q_inv (GeV/c); the cheap gate runs before any boost"}; + Configurable cfgMaxQinvForMCQA{"cfgMaxQinvForMCQA", 0.3f, "MC pair diagnostics (ancestry, true conversions) only below this q_inv"}; + Configurable cfgDoCrossPairQA{"cfgDoCrossPairQA", true, "WP-F: evaluate the alternative pairings (two DCAFitter fits AND two analytic builds per pair below cfgAltMaxQinv) and fill Pair/CrossPair/*"}; + Configurable cfgDoAnalyticV0QA{"cfgDoAnalyticV0QA", true, "WP-B: rebuild every selected photon from its own legs and fill the residuals against the builder (Photon/AnalyticV0/*)"}; + Configurable cfgDoAncestry{"cfgDoAncestry", true, "WP-E (MC): census of the four legs, common ancestor, true conversion points (Pair/MC/Ancestry/*)"}; + Configurable cfgAncestorMaxGen{"cfgAncestorMaxGen", 4, "how many MC generations to walk up when looking for a common ancestor"}; + Configurable cfgFillLegSimilarity{"cfgFillLegSimilarity", true, "like-sign leg similarity sparse (duplicate diagnostic)"}; + Configurable cfgFillTypeSparses{"cfgFillTypeSparses", true, "MC: per truth type also the (dEta, dPhi, q_inv) and (dR, dZ, q_inv) sparses (large)"}; + Configurable cfgScoreWeight{"cfgScoreWeight", 0.5f, "builder score weight w for the analytic V0 (as mixing_group.cfgScoreWeight of photonhbt)"}; + Configurable cfgMaxQinvForSparses{"cfgMaxQinvForSparses", 0.1f, "the sparses with 4 or more axes carry the COARSE q axis (confQBinsCoarse) and are filled only below this q_inv; keep it equal to the upper edge of confQBinsCoarse"}; + } qaflags; + + ConfigurableAxis confQBins{"confQBins", {60, 0, +0.3f}, "q bins"}; + ConfigurableAxis confQBinsCoarse{"confQBinsCoarse", {VARIABLE_WIDTH, 0.0, 0.01, 0.02, 0.03, 0.05, 0.1}, "coarse q bins for the sparses with 4 or more axes (merging size)"}; + ConfigurableAxis confKtBins{"confKtBins", {VARIABLE_WIDTH, 0.0, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75}, "kT bins"}; + + AxisSpec axisQinv{confQBins, "q_{inv} (GeV/c)"}; + AxisSpec axisQinvCoarse{confQBinsCoarse, "q_{inv} (GeV/c), coarse"}; + AxisSpec axisKt{confKtBins, "k_{T} (GeV/c)"}; + AxisSpec axisDeltaEta{90, -1.6f, +1.6f, "#Delta#eta"}; + AxisSpec axisDeltaPhi{90, -o2::constants::math::PI, +o2::constants::math::PI, "#Delta#phi (rad)"}; + AxisSpec axisDeltaR{90, 0.f, 30.f, "|R_{1}-R_{2}| (cm)"}; + AxisSpec axisDeltaZ{200, -100.f, 100.f, "#Delta z (cm)"}; + AxisSpec axisDeltaR3D{100, 0.f, 100.f, "|#vec{r}_{1}-#vec{r}_{2}| (cm)"}; + AxisSpec axisTruthType{{0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5}, "truth type (1=TrueTrueDistinct,2=TrueTrueSamePhoton,3=SharedMcLeg,4=TrueFake,5=FakeFake,6=Pi0Daughters)"}; + AxisSpec axisMethod{2, -0.5f, 1.5f, "0 = DCAFitter, 1 = analytic"}; + [[nodiscard]] static AxisSpec makeAxisEnum(int nClasses, const char* title) + { + return AxisSpec{nClasses, -0.5f, static_cast(nClasses) - 0.5f, title}; + } + + static constexpr int kNPhotonSources = 6; + static constexpr const char* kPhotonSourceTitle = "parent photon source (0 direct, 1 #pi^{0}, 2 #eta, 3 other hadron, 4 electron = bremsstrahlung, 5 no photon parent)"; + template + [[nodiscard]] static int photonSource(TMCParticles const& mcParticles, int photonId) + { + if (photonId < 0) { + return 5; // o2-linter: disable=magic-number (source code) + } + const auto photon = mcParticles.iteratorAt(photonId); + if (!photon.has_mothers()) { + return 0; + } + const int motherPdg = std::abs(mcParticles.iteratorAt(photon.mothersIds()[0]).pdgCode()); + if (motherPdg == PDG_t::kPi0) { + return 1; + } + if (motherPdg == o2::constants::physics::Pdg::kEta) { + return 2; // o2-linter: disable=magic-number (source code) + } + if (motherPdg == PDG_t::kElectron) { + return 4; // o2-linter: disable=magic-number (source code) + } + return 3; // o2-linter: disable=magic-number (source code) + } + + // Track-class combination of the two legs of a V0, unordered: + // 0 ITSTPC+ITSTPC, 1 ITSTPC+ITSonly, 2 ITSTPC+TPConly, 3 ITSonly+ITSonly, 4 ITSonly+TPConly, 5 TPConly+TPConly + static constexpr int kNV0Classes = 6; + static constexpr const char* kV0ClassTitle = "V0 leg classes (0 ITSTPC+ITSTPC, 1 ITSTPC+ITSonly, 2 ITSTPC+TPConly, 3 ITSonly+ITSonly, 4 ITSonly+TPConly, 5 TPConly+TPConly)"; + [[nodiscard]] static int v0LegClass(pairutil::V0PhotonLegCounts const& c) + { + if (c.nITSTPC == 2) { // o2-linter: disable=magic-number (two legs) + return 0; + } + if (c.nITSTPC == 1) { + return (c.nITSOnly == 1) ? 1 : 2; // o2-linter: disable=magic-number (class code) + } + if (c.nITSOnly == 2) { // o2-linter: disable=magic-number (two legs) + return 3; // o2-linter: disable=magic-number (class code) + } + return (c.nITSOnly == 1) ? 4 : 5; // o2-linter: disable=magic-number (class code) + } + + HistogramRegistry fRegistry{"pairQC", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + EMPhotonEventCut fEMEventCut; + V0PhotonCut fV0PhotonCut; + bool isMC = false; + int mRunNumber{0}; + float mBzT{0.f}; + PhotonLikeCuts mAltCuts{}; + pairutil::DedupConfig mDedupCfg{}; + + SliceCache cache; + Preslice perCollisionPCM = aod::v0photonkf::pmeventId; + + Filter collisionFilterCentrality = + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < centralitySelection.cfgCentMax) || + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < centralitySelection.cfgCentMax) || + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < centralitySelection.cfgCentMax); + Filter collisionFilterOccupancyTrack = + eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && + o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; + Filter collisionFilterOccupancyFT0c = + eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && + o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; + using FilteredMyCollisions = soa::Filtered; + using FilteredMyMCCollisions = soa::Filtered; + + void init(InitContext& context) + { + isMC = context.mOptions.get("processMC"); + defineEMEventCut(); + definePCMCut(); + mAltCuts.maxDca = crosspair.cfgAltMaxDca.value; + mAltCuts.minR = crosspair.cfgAltMinR.value; + mAltCuts.maxR = crosspair.cfgAltMaxR.value; + mAltCuts.minCosPA = crosspair.cfgAltMinCosPA.value; + mAltCuts.maxMee = crosspair.cfgAltMaxMee.value; + mAltCuts.maxDcaXY = crosspair.cfgAltMaxDcaXY.value; + mAltCuts.maxDcaZ = crosspair.cfgAltMaxDcaZ.value; + mDedupCfg.maxLegDEta = dedup.cfgDupMaxLegDEta.value; + mDedupCfg.maxLegDPhi = dedup.cfgDupMaxLegDPhi.value; + mDedupCfg.maxLegPtAsym = dedup.cfgDupMaxLegPtAsym.value; + mDedupCfg.maxLegDeDxAsym = dedup.cfgDupMaxLegDeDxAsym.value; + mDedupCfg.maxDVtx3D = dedup.cfgDupMaxDVtx3D.value; + mDedupCfg.requireBothLegs = dedup.cfgDupRequireBothLegs.value; + ccdb->setURL(cfgCcdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fRegistry.add("Photon/hPtEtaPhi", "selected V0 photons;p_{T} (GeV/c);#eta;#varphi (rad)", kTH3F, {{100, 0.f, 2.f}, {40, -0.8f, 0.8f}, {72, 0.f, 6.2832f}}, true); // o2-linter: disable=magic-number (axis definition) + fRegistry.add("Photon/hNPhotonsPerEvent", "selected V0 photons per event;N_{#gamma};events", kTH1F, {{21, -0.5f, 20.5f}}, true); + fRegistry.add("Photon/hV0LegClass", "leg classes of the selected V0s;;V0s", kTH1F, {makeAxisEnum(kNV0Classes, kV0ClassTitle)}, true); + fRegistry.add("Photon/hV0LegClass_vs_Pt", "leg classes of the selected V0s vs p_{T};V0 leg class;p_{T} (GeV/c)", kTH2F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), {40, 0.f, 2.f}}, true); + fRegistry.add("Photon/hV0LegClass_vs_R", "leg classes of the selected V0s vs R_{conv};V0 leg class;R_{conv} (cm)", kTH2F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), {50, 0.f, 100.f}}, true); + addAnalyticV0Histograms(); + addPairQAHistograms(); + addDedupHistograms(); + addCrossPairHistograms(); + if (isMC) { + addPairTruthHistograms(); // WP-C + addAncestryHistograms(); // WP-E + } + LOGF(info, "pairQC: isMC = %d, pair QA below q_inv = %.2f, cross-pair QA %s (below %.3f), analytic V0 QA %s, ancestry %s, dedup %s", + isMC, qaflags.cfgMaxQinvForQA.value, qaflags.cfgDoCrossPairQA.value ? "on" : "off", crosspair.cfgAltMaxQinv.value, qaflags.cfgDoAnalyticV0QA.value ? "on" : "off", qaflags.cfgDoAncestry.value ? "on" : "off", dedup.cfgDoDedup.value ? "ON" : "off"); + } + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + if (cfgBzOverrideT.value > -100.f) { // o2-linter: disable=magic-number (override sentinel) + mBzT = cfgBzOverrideT.value; + return; + } + auto grpmag = ccdb->getForRun("GLO/Config/GRPMagField", mRunNumber); + mBzT = 0.1f * static_cast(grpmag->getNominalL3Field()); // o2-linter: disable=magic-number (kGauss -> Tesla) + LOGF(info, "pairQC: run %d, Bz = %.2f T", mRunNumber, mBzT); + } + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); + fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); + fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); + fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); + fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); + fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); + fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); + fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + fV0PhotonCut.SetV0PtRange(pcmcuts.cfgMinPtV0, 1e10f); + fV0PhotonCut.SetV0EtaRange(-pcmcuts.cfgMaxEtaV0, +pcmcuts.cfgMaxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.cfgMinCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.cfgMaxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfgMaxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.cfgMinV0Radius, pcmcuts.cfgMaxV0Radius); + fV0PhotonCut.SetAPRange(pcmcuts.cfgMaxAlphaAP, pcmcuts.cfgMaxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.cfgRejectV0OnITSIB); + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.cfgMinNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.cfgMinNCrossedRows); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.cfgMaxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.0, pcmcuts.cfgMaxChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.cfgMinTPCNsigmaEl, pcmcuts.cfgMaxTPCNsigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(-1e+10, pcmcuts.cfgMaxChi2ITS); + fV0PhotonCut.SetDisableITSonly(pcmcuts.cfgDisableITSOnlyTrack); + fV0PhotonCut.SetDisableTPConly(pcmcuts.cfgDisableTPCOnlyTrack); + fV0PhotonCut.SetNClustersITS(0, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(0.0, 16.0); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.cfgRequireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.cfgRequireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.cfgRequireV0WithTPCOnly); + } + + void addAnalyticV0Histograms() + { + if (!qaflags.cfgDoAnalyticV0QA.value) { + return; + } + const AxisSpec axR{50, 0.f, 100.f, "R_{conv}^{builder} (cm)"}; + const AxisSpec axTruth{3, -1.5f, 1.5f, "-1 = data / no label, 0 = fake V0, 1 = true photon"}; + fRegistry.add("Photon/AnalyticV0/hOk", "analytic rebuild succeeded;ok;photons", kTH1F, {{2, -0.5f, 1.5f}}, true); + fRegistry.add("Photon/AnalyticV0/hdR_R_Truth", "R_{ana} - R_{builder};R_{ana} - R_{builder} (cm);R_{conv}^{builder} (cm);truth", kTH3F, {{200, -10.f, 10.f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdZ_R_Truth", "z_{ana} - z_{builder};z_{ana} - z_{builder} (cm);R_{conv}^{builder} (cm);truth", kTH3F, {{200, -10.f, 10.f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdPhiConv_R_Truth", "#varphi_{ana} - #varphi_{builder} of the conversion point;#Delta#varphi (rad);R_{conv}^{builder} (cm);truth", kTH3F, {{200, -0.2f, 0.2f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdPtRel_Pt_Truth", "(p_{T,ana} - p_{T,builder}) / p_{T,builder};relative p_{T} difference;p_{T}^{builder} (GeV/c);truth", kTH3F, {{200, -0.5f, 0.5f}, {40, 0.f, 2.f}, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hdCosPA_Truth", "cosPA_{ana} - cosPA_{builder};#Delta cosPA;truth", kTH2F, {{200, -0.02f, 0.02f}, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hPcaAna_PcaBuilder_Truth", "PCA of the two legs;PCA_{ana} (cm);PCA_{builder} (cm);truth", kTH3F, {{60, 0.f, 6.f}, {60, 0.f, 6.f}, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hScore_R_Truth", "builder score of the own analytic V0;score;R_{conv}^{builder} (cm);truth", kTH3F, {{200, 0.f, 30.f}, axR, axTruth}, true); + fRegistry.add("Photon/AnalyticV0/hPhotonLike_Truth", "own analytic V0 passes the cfgAlt* cuts;photon-like;truth", kTH2F, {{2, -0.5f, 1.5f}, axTruth}, true); + } + + void addPairQAHistograms() + { + const AxisSpec axDrCos{100, 0.f, 200.f, "|#vec{r}_{1}-#vec{r}_{2}| / cos(#alpha/2) (cm)"}; + const AxisSpec axOpa{100, 0.f, 0.5f, "opening angle of the conversion points (rad)"}; + const AxisSpec axNTimed{5, -0.5f, 4.5f, "legs with a time (ITS, TRD or TOF) among the four"}; + const AxisSpec axNForeign{5, -0.5f, 4.5f, "legs whose collisionId differs from their V0"}; + const AxisSpec axMaxDcaZ{50, 0.f, 10.f, "max |DCAz| of the two V0 photons to the PV (cm)"}; + const AxisSpec axMaxDcaXY{30, 0.f, 3.f, "max |DCAxy| of the two V0 photons to the PV (cm)"}; + const std::string base = "Pair/QA/Before/"; + fRegistry.add((base + "hDEtaDPhi_Qinv").c_str(), "pair (#Delta#eta,#Delta#phi,q_{inv});#Delta#eta;#Delta#phi (rad);q_{inv} (GeV/c)", kTH3F, {axisDeltaEta, axisDeltaPhi, axisQinv}, true); + fRegistry.add((base + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv};q_{inv} (GeV/c);|R_{1}-R_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR}, true); + fRegistry.add((base + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv};q_{inv} (GeV/c);#Delta z (cm)", kTH2F, {axisQinv, axisDeltaZ}, true); + fRegistry.add((base + "hDeltaR3DVsQinv").c_str(), "|#vec{r}_{1}-#vec{r}_{2}| vs q_{inv};q_{inv} (GeV/c);|#vec{r}_{1}-#vec{r}_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR3D}, true); + fRegistry.add((base + "hDrOverCosOAVsQinv").c_str(), "dr/cosOA vs q_{inv};q_{inv} (GeV/c);|#vec{r}_{1}-#vec{r}_{2}| / cos(#alpha/2) (cm)", kTH2F, {axisQinv, axDrCos}, true); + fRegistry.add((base + "hOpeningAngleVsQinv").c_str(), "opening angle of the conversion points vs q_{inv};q_{inv} (GeV/c);#alpha (rad)", kTH2F, {axisQinv, axOpa}, true); + fRegistry.add((base + "hQinv_NTimed_NForeign").c_str(), "pairs by leg collision association;q_{inv} (GeV/c);legs with a time;legs with foreign collisionId", kTH3F, {axisQinv, axNTimed, axNForeign}, true); + fRegistry.add((base + "hQinv_MaxDcaZ_NTimed").c_str(), "pairs by V0 pointing to the primary vertex, z;q_{inv} (GeV/c);max |DCAz| (cm);legs with a time", kTH3F, {axisQinv, axMaxDcaZ, axNTimed}, true); + fRegistry.add((base + "hQinv_MaxDcaXY_NTimed").c_str(), "pairs by V0 pointing to the primary vertex, xy;q_{inv} (GeV/c);max |DCAxy| (cm);legs with a time", kTH3F, {axisQinv, axMaxDcaXY, axNTimed}, true); + fRegistry.add((base + "hRin_Rout_OuterSofter_Qinv").c_str(), "ordered conversion radii;R_{in} (cm);R_{out} (cm);outer photon softer (0/1);q_{inv} (GeV/c)", kTHnSparseF, {{50, 0.f, 100.f}, {50, 0.f, 100.f}, {2, -0.5f, 1.5f}, axisQinvCoarse}, true); + fRegistry.add((base + "hLegStart_dNFindable_dNits_Qinv").c_str(), "leg start-point asymmetry per V0;|#Delta N_{findable}^{TPC}| (pos-neg);|#Delta N_{cls}^{ITS}|;q_{inv} (GeV/c)", kTH3F, {{40, -0.5f, 39.5f}, {8, -0.5f, 7.5f}, axisQinv}, true); + fRegistry.add((base + "hV0_Eta_Phi_R_Qinv").c_str(), "eta, phi and conversion radius of each V0 of a pair;#eta;#varphi (rad);R_{conv} (cm);q_{inv} (GeV/c)", kTHnSparseF, {{20, -1.f, 1.f}, {72, 0.f, o2::constants::math::TwoPI}, {25, 0.f, 100.f}, axisQinvCoarse}, true); + fRegistry.add((base + "hPhi_lowerPtV0").c_str(), "azimuthal angle of the lower-p_{T} V0;#phi (rad);pairs", kTH1F, {{90, 0.f, o2::constants::math::TwoPI}}, true); + const AxisSpec axPairEta{20, -1.f, 1.f, "#eta_{pair}"}; + const AxisSpec axPairPhi{72, 0.f, o2::constants::math::TwoPI, "#varphi_{pair} (rad)"}; + const AxisSpec axDeltaRAng{100, 0.f, 0.5f, "#DeltaR = #sqrt{#Delta#eta^{2} + #Delta#varphi^{2}}"}; + fRegistry.add((base + "hDEta_DPhi_PairEta_Qinv").c_str(), "#Delta#eta, #Delta#varphi vs pair #eta;#Delta#eta;#Delta#phi (rad);#eta_{pair};q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaEta, axisDeltaPhi, axPairEta, axisQinvCoarse}, true); + fRegistry.add((base + "hDEta_DPhi_PairPhi_Qinv").c_str(), "#Delta#eta, #Delta#varphi vs pair #varphi;#Delta#eta;#Delta#phi (rad);#varphi_{pair} (rad);q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaEta, axisDeltaPhi, axPairPhi, axisQinvCoarse}, true); + fRegistry.add((base + "hDPhi_Phi1_Phi2_Qinv").c_str(), "#Delta#varphi vs the azimuth of each V0;#Delta#phi (rad);#varphi_{1} (rad);#varphi_{2} (rad);q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaPhi, axPairPhi, axPairPhi, axisQinvCoarse}, true); + fRegistry.add((base + "hDEta_Eta1_Eta2_Qinv").c_str(), "#Delta#eta vs the pseudorapidity of each V0;#Delta#eta;#eta_{1};#eta_{2};q_{inv} (GeV/c)", kTHnSparseF, {axisDeltaEta, axPairEta, axPairEta, axisQinvCoarse}, true); + fRegistry.add((base + "hDeltaRAngVsQinv").c_str(), "#DeltaR of the pair vs q_{inv};q_{inv} (GeV/c);#DeltaR", kTH2F, {axisQinv, axDeltaRAng}, true); + fRegistry.add((base + "hDeltaRAng_PairEta_PairPhi").c_str(), "#DeltaR of the pair vs where it sits;#DeltaR;#eta_{pair};#varphi_{pair} (rad)", kTH3F, {axDeltaRAng, axPairEta, axPairPhi}, true); + fRegistry.add((base + "hV0Class1_V0Class2_Qinv").c_str(), "leg classes of the two V0s of a pair (lower class first);V0 leg class 1;V0 leg class 2;q_{inv} (GeV/c)", kTH3F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), makeAxisEnum(kNV0Classes, kV0ClassTitle), axisQinv}, true); + fRegistry.add((base + "hNLegsITSTPC_NLegsTPConly_Qinv").c_str(), "legs of the four by class;N_{ITS-TPC legs} (0-4);N_{TPC-only legs} (0-4);q_{inv} (GeV/c)", kTH3F, {{5, -0.5f, 4.5f}, {5, -0.5f, 4.5f}, axisQinv}, true); + fRegistry.addClone("Pair/QA/Before/", "Pair/QA/AfterPairCuts/"); + + if (qaflags.cfgFillLegSimilarity.value) { + fRegistry.add("Pair/LegSimilarity/hdEta_dPhi_ptRatio_dNsig_Qinv", "like-sign leg similarity;#Delta#eta(LS legs);#Delta#varphi(LS legs) (rad);|p_{T,1}-p_{T,2}|/(p_{T,1}+p_{T,2});|#Delta n#sigma_{e}^{TPC}|;q_{inv} (GeV/c)", + kTHnSparseF, {{100, -0.1f, 0.1f}, {100, -0.1f, 0.1f}, {50, 0.f, 1.f}, {40, 0.f, 8.f}, axisQinvCoarse}, true); // o2-linter: disable=magic-number (axis definition) + } + } + + void addDedupHistograms() + { + const AxisSpec axDEta{100, 0.f, 0.5f, "|#Delta#eta| (candidate pair)"}; + const AxisSpec axDPhi{100, 0.f, 0.5f, "|#Delta#varphi| (rad)"}; + const AxisSpec axPtAsym{50, 0.f, 1.f, "|p_{T,1}-p_{T,2}|/(p_{T,1}+p_{T,2})"}; + const AxisSpec axDR{100, 0.f, 20.f, "|R_{1}-R_{2}| (cm)"}; + fRegistry.add("Dedup/hdEta_dPhi_ptAsym", "distance between two V0 candidates of the same collision;|#Delta#eta|;|#Delta#varphi| (rad);p_{T} asymmetry", kTH3F, {axDEta, axDPhi, axPtAsym}, true); + fRegistry.add("Dedup/hdR_ptAsym", "distance between two V0 candidates of the same collision;|R_{1}-R_{2}| (cm);p_{T} asymmetry", kTH2F, {axDR, axPtAsym}, true); + fRegistry.add("Dedup/hNDupPartners_vs_Pt", "duplicate partners per candidate (configured tolerances);N_{partners};p_{T,#gamma} (GeV/c)", kTH2F, {{6, -0.5f, 5.5f}, {50, 0.f, 2.f}}, true); + fRegistry.add("Dedup/hNCandBeforeAfter", "selected candidates per collision;0 = before dedup, 1 = after dedup;N_{cand}", kTH2F, {{2, -0.5f, 1.5f}, {21, -0.5f, 20.5f}}, true); + const AxisSpec axLegTruth{5, -0.5f, 4.5f, "0=diffReco/diffMC, 1=diffReco/sameMC, 2=sameReco/sameMC, 3=sameReco/diffMC, 4=noMC"}; + fRegistry.add("Dedup/hLeg_dEta_dPhi_Truth", "same-charge legs of two candidates;|#Delta#eta|;|#Delta#varphi| (rad);track-level truth", kTH3F, {{50, 0.f, 0.05f}, {50, 0.f, 0.05f}, axLegTruth}, true); + fRegistry.add("Dedup/hLeg_ptAsym_dEdxAsym_Truth", "same-charge legs of two candidates;p_{T} asym.;dE/dx asym.;track-level truth", kTH3F, {{50, 0.f, 0.5f}, {50, 0.f, 0.5f}, axLegTruth}, true); + fRegistry.add("Dedup/hLeg_fShared_Truth", "same-charge legs of two candidates;max f_{shared};track-level truth", kTH2F, {{20, 0.f, 1.f}, axLegTruth}, true); + if (!isMC) { + return; + } + const AxisSpec axDup = makeAxisEnum(static_cast(mcutil::DupClass::NClasses), "dup class (0 unknown, 1 same MC photon, 2 shared MC leg, 3 cross-built, 4 distinct true, 5 involves fake)"); + const AxisSpec axFlag{2, -0.5f, 1.5f, "flagged by the criterion"}; + fRegistry.add("Dedup/MC/hNCandPerMcPhoton", "V0 candidates per MC photon;N_{cand};MC photons", kTH1F, {{6, 0.5f, 6.5f}}, true); + fRegistry.add("Dedup/MC/hNCandPerMcPhoton_vs_Pt", "V0 candidates per MC photon vs p_{T};N_{cand};p_{T,#gamma}^{reco} (GeV/c)", kTH2F, {{6, 0.5f, 6.5f}, {50, 0.f, 2.f}}, true); + fRegistry.add("Dedup/MC/hDupClass", "MC class of each candidate pair of a collision;dup class;pairs", kTH1F, {axDup}, true); + fRegistry.add("Dedup/MC/hDupClass_vs_Flagged", "MC class vs dedup decision;dup class;flagged", kTH2F, {axDup, axFlag}, true); + fRegistry.add("Dedup/MC/hDupClass_dEta_dPhi", "candidate distance by MC class;dup class;|#Delta#eta|;|#Delta#varphi| (rad)", kTH3F, {axDup, axDEta, axDPhi}, true); + fRegistry.add("Dedup/MC/hDupClass_ptAsym_dR", "candidate distance by MC class;dup class;p_{T} asymmetry;|R_{1}-R_{2}| (cm)", kTH3F, {axDup, axPtAsym, axDR}, true); + fRegistry.add("Dedup/MC/hSamePhotonDuplicateType", "same MC photon: split pattern;0 = no split reco leg, 1 = split e^{+}, 2 = split e^{-}, 3 = both;pairs", kTH1F, {{4, -0.5f, 3.5f}}, true); + fRegistry.add("Dedup/MC/hSamePhotonDuplicateType_vs_Flagged", "same MC photon: split pattern vs dedup decision;split pattern;flagged", kTH2F, {{4, -0.5f, 3.5f}, axFlag}, true); + fRegistry.add("Dedup/MC/hSplitLegPairClass", "pair class for pairs containing a split reco leg;dup class;pairs", kTH1F, {axDup}, true); + fRegistry.add("Dedup/MC/hSplitLegV0Type", "V0 truth of both candidates with a split leg;0 = same true photon, 1 = one true + one fake, 2 = both fake, 3 = other;pairs", kTH1F, {{4, -0.5f, 3.5f}}, true); + fRegistry.add("Dedup/MC/hSplitLeg_ptAsym_dEdxAsym_DupClass", "split legs: distance vs pair class;p_{T} asym.;dE/dx asym.;dup class", kTH3F, {{50, 0.f, 0.5f}, {50, 0.f, 0.5f}, axDup}, true); + fRegistry.add("Dedup/MC/hSplitLeg_fShared_DupClass", "split legs: shared clusters vs pair class;max f_{shared};dup class", kTH2F, {{20, 0.f, 1.f}, axDup}, true); + fRegistry.add("Dedup/MC/hDupTrackClassMatrix", "true duplicates: ITS-TPC legs A vs B;n_{ITS-TPC}(A);n_{ITS-TPC}(B)", kTH2F, {{3, -0.5f, 2.5f}, {3, -0.5f, 2.5f}}, true); + fRegistry.add("Dedup/MC/hDupDeltaRconv", "true duplicates: radius difference;|R_{1}-R_{2}| (cm);pairs", kTH1F, {{200, 0.f, 20.f}}, true); + fRegistry.add("Dedup/MC/hChoiceOutcome", "dedup choice for flagged pairs;0 true kept/fake dropped, 1 fake kept/true dropped, 2 both true same photon, 3 both fake, 4 both true distinct;flagged pairs", kTH1F, {{5, -0.5f, 4.5f}}, true); + fRegistry.add("Dedup/MC/hLegTrackTruthClass", "track id vs MC id for same-charge legs;0 diff reco/diff MC, 1 diff reco/same MC, 2 same reco/same MC, 3 same reco/diff MC, 4 no MC;leg pairs", kTH1F, {{5, -0.5f, 4.5f}}, true); + fRegistry.add("Dedup/MC/hLegTrackTruthClass_vs_Flagged", "track-level truth vs leg flagged;class;leg flagged", kTH2F, {{5, -0.5f, 4.5f}, axFlag}, true); + fRegistry.add("Pair/MC/hTruthType_Qinv_DedupFlag", "would the dedup remove this pair?;truth type;q_{inv} (GeV/c);0 = stays, 1 = removed", kTH3F, {axisTruthType, axisQinv, axFlag}, true); + } + + void addCrossPairHistograms() + { + auto h = fRegistry.add("Pair/CrossPair/hVetoCounter", "crossed-pair veto (DCAFitter, cfgCrossVetoMode);;pairs", kTH1D, {{2, -0.5f, 1.5f}}, true); + h->GetXaxis()->SetBinLabel(1, "evaluated"); + h->GetXaxis()->SetBinLabel(2, "vetoed"); + if (!qaflags.cfgDoCrossPairQA.value) { + return; + } + const AxisSpec axNAlt{3, -0.5f, 2.5f, "N photon-like alternative pairings"}; + const AxisSpec axDca{60, 0.f, 12.f, "max DCA of the two alternative pairings (cm)"}; + const AxisSpec axDcaXY{60, 0.f, 12.f, "max DCAxy to the vertex of the two alternative pairings (cm)"}; + const AxisSpec axDcaZ{60, 0.f, 12.f, "max DCAz to the vertex of the two alternative pairings (cm)"}; + for (const auto& m : {std::string("Fit"), std::string("Ana")}) { + fRegistry.add(("Pair/CrossPair/hQinv_NAltValid_MaxAltDca_" + m).c_str(), ("alternative pairings of the four legs (" + m + ");q_{inv} (GeV/c);N photon-like;max DCA (cm)").c_str(), kTH3F, {axisQinv, axNAlt, axDca}, true); + fRegistry.add(("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaXY_" + m).c_str(), ("alternative pairings, xy (" + m + ");q_{inv} (GeV/c);N photon-like;max DCAxy (cm)").c_str(), kTH3F, {axisQinv, axNAlt, axDcaXY}, true); + fRegistry.add(("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaZ_" + m).c_str(), ("alternative pairings, z (" + m + ");q_{inv} (GeV/c);N photon-like;max DCAz (cm)").c_str(), kTH3F, {axisQinv, axNAlt, axDcaZ}, true); + } + fRegistry.add("Pair/CrossPair/hAltDca_Fit_vs_Ana", "alternative pairing: DCA of the legs, DCAFitter vs analytic;DCA_{fit} (cm);DCA_{ana} (cm)", kTH2F, {{120, 0.f, 12.f}, {120, 0.f, 12.f}}, true); + fRegistry.add("Pair/CrossPair/hAltCosPA_Fit_vs_Ana", "alternative pairing: cosPA, DCAFitter vs analytic;cosPA_{fit};cosPA_{ana}", kTH2F, {{100, 0.9f, 1.f}, {100, 0.9f, 1.f}}, true); + fRegistry.add("Pair/CrossPair/hNAltValid_Fit_vs_Ana", "N photon-like alternatives, DCAFitter vs analytic;N_{fit};N_{ana}", kTH2F, {axNAlt, axNAlt}, true); + fRegistry.add("Pair/CrossPair/hMeeRatio_dR_Qinv", "crossed-hypothesis mass;min(m_{ee}^{cross})/q_{inv};|R_{1}-R_{2}| (cm);q_{inv} (GeV/c)", kTH3F, {{100, 0.f, 2.f}, {80, 0.f, 80.f}, axisQinv}, true); + fRegistry.add("Pair/CrossPair/hRepairDeltaMee_OwnMee_Qinv", "re-pairing test;max(m_{ee}^{own}) - max(m_{ee}^{re-paired}) (GeV/c^{2});max(m_{ee}^{own}) (GeV/c^{2});q_{inv} (GeV/c)", kTH3F, {{120, -0.06f, 0.06f}, {60, 0.f, 0.06f}, axisQinv}, true); + fRegistry.add("Pair/CrossPair/hMee1_Mee2_M4_Qinv", "pair Dalitz plane;m_{ee}(#gamma_{1}) (GeV/c^{2});m_{ee}(#gamma_{2}) (GeV/c^{2});M(4 legs) (GeV/c^{2});q_{inv} (GeV/c)", kTHnSparseF, {{30, 0.f, 0.06f}, {30, 0.f, 0.06f}, {75, 0.f, 0.3f}, axisQinvCoarse}, true); + if (!isMC) { + return; + } + for (const auto& m : {std::string("Fit"), std::string("Ana")}) { + fRegistry.add(("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDca_" + m + "_Type").c_str(), ("alternative pairings per truth type (" + m + ");q_{inv} (GeV/c);N photon-like;max DCA (cm);truth type").c_str(), kTHnSparseF, {axisQinv, axNAlt, axDca, axisTruthType}, true); + fRegistry.add(("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDcaZ_" + m + "_Type").c_str(), ("alternative pairings per truth type, z (" + m + ");q_{inv} (GeV/c);N photon-like;max DCAz (cm);truth type").c_str(), kTHnSparseF, {axisQinv, axNAlt, axDcaZ, axisTruthType}, true); + } + fRegistry.add("Pair/MC/CrossPair/hMeeRatio_Qinv_Type", "crossed-pair hypothesis per truth type;min(m_{ee}^{cross})/q_{inv};q_{inv} (GeV/c);truth type", kTH3F, {{100, 0.f, 2.f}, axisQinv, axisTruthType}, true); + fRegistry.add("Pair/MC/CrossPair/hMee1_Mee2_M4_Qinv_Type", "pair Dalitz plane per truth type;m_{ee}(#gamma_{1});m_{ee}(#gamma_{2});M(4 legs);q_{inv} (GeV/c);truth type", kTHnSparseF, {{30, 0.f, 0.06f}, {30, 0.f, 0.06f}, {75, 0.f, 0.3f}, axisQinvCoarse, axisTruthType}, true); + fRegistry.add("Pair/MC/CrossPair/hQinv_MaxDcaZ_Type", "V0 pointing per truth type, z;q_{inv} (GeV/c);max |DCAz| (cm);truth type", kTH3F, {axisQinv, {50, 0.f, 10.f}, axisTruthType}, true); + fRegistry.add("Pair/MC/CrossPair/hQinv_MaxDcaXY_Type", "V0 pointing per truth type, xy;q_{inv} (GeV/c);max |DCAxy| (cm);truth type", kTH3F, {axisQinv, {30, 0.f, 3.f}, axisTruthType}, true); + const AxisSpec axEmu{4, -0.5f, 3.5f, "0 = not evaluated, 1 = kept, 2 = swapped, 3 = cross not photon-like"}; + fRegistry.add("Pair/MC/CrossPair/hEmulation_vs_Truth", "cross-pairing emulation (analytic score) vs truth;q_{inv} (GeV/c);truth type;outcome", kTH3F, {axisQinv, axisTruthType, axEmu}, true); + fRegistry.add("Pair/MC/CrossPair/hEmuDeltaScore_vs_Truth", "S_{cross} - S_{true} vs truth;q_{inv} (GeV/c);truth type;#DeltaS", kTH3F, {axisQinv, axisTruthType, {200, -20.f, 20.f}}, true); + fRegistry.add("Pair/MC/CrossPair/hFakeSubtype_Emu_Qinv", "fake subtype vs emulation outcome;fake subtype (0 ordinary, 1 full cross, 2 half cross);outcome;q_{inv} (GeV/c)", kTH3F, {{3, -0.5f, 2.5f}, axEmu, axisQinv}, true); + } + + void addPairTruthHistograms() + { + fRegistry.add("Pair/MC/hTruthTypeVsQinv", "truth type vs q_{inv};q_{inv} (GeV/c);truth type", kTH2F, {axisQinv, axisTruthType}, true); + fRegistry.add("Pair/MC/hTruthTypeVsKt", "truth type vs k_{T};k_{T} (GeV/c);truth type", kTH2F, {axisKt, axisTruthType}, true); + fRegistry.add("Pair/MC/hFakeSubtypeVsQinv", "fake pair subtype vs q_{inv};q_{inv} (GeV/c);0=ordinary, 1=full cross, 2=half cross", kTH2F, {axisQinv, {3, -0.5f, 2.5f}}, true); + fRegistry.add("Pair/MC/hV0Class1_V0Class2_Qinv_Type", "leg classes of the two V0s of a pair vs truth type;V0 leg class 1;V0 leg class 2;q_{inv} (GeV/c);truth type", kTHnSparseF, {makeAxisEnum(kNV0Classes, kV0ClassTitle), makeAxisEnum(kNV0Classes, kV0ClassTitle), axisQinv, axisTruthType}, true); + fRegistry.add("Pair/MC/hV0Class_IsTrue_Qinv", "per V0: leg class vs V0 truth;V0 leg class;V0 is a true photon;q_{inv} (GeV/c)", kTH3F, {makeAxisEnum(kNV0Classes, kV0ClassTitle), {2, -0.5f, 1.5f}, axisQinv}, true); + static constexpr std::array kTypes = {"TrueTrueDistinct/", "TrueTrueSamePhoton/", "SharedMcLeg/", "TrueFake/", "FakeFake/", "Pi0Daughters/"}; + for (const auto& label : kTypes) { + const std::string base = std::string("Pair/MC/") + std::string(label); + fRegistry.add((base + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv};q_{inv} (GeV/c);|R_{1}-R_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR}, true); + fRegistry.add((base + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv};q_{inv} (GeV/c);#Delta z (cm)", kTH2F, {axisQinv, axisDeltaZ}, true); + fRegistry.add((base + "hDeltaR3DVsQinv").c_str(), "|#vec{r}_{1}-#vec{r}_{2}| vs q_{inv};q_{inv} (GeV/c);|#vec{r}_{1}-#vec{r}_{2}| (cm)", kTH2F, {axisQinv, axisDeltaR3D}, true); + fRegistry.add((base + "hDEtaDPhi").c_str(), "#Delta#eta vs #Delta#phi;#Delta#eta;#Delta#phi (rad)", kTH2F, {axisDeltaEta, axisDeltaPhi}, true); + if (qaflags.cfgFillTypeSparses.value) { + fRegistry.add((base + "hDEtaDPhi_Qinv").c_str(), "#Delta#eta, #Delta#phi, q_{inv};#Delta#eta;#Delta#phi (rad);q_{inv} (GeV/c)", kTH3F, {axisDeltaEta, axisDeltaPhi, axisQinv}, true); + fRegistry.add((base + "hDeltaR_DeltaZ_Qinv").c_str(), "|R_{1}-R_{2}|, #Delta z, q_{inv};|R_{1}-R_{2}| (cm);#Delta z (cm);q_{inv} (GeV/c)", kTH3F, {axisDeltaR, axisDeltaZ, axisQinv}, true); + } + } + fRegistry.add("Pair/MC/hRreco_minus_Rmc", "conversion radius resolution by V0 truth;R_{reco} - R_{MC}(leg) (cm);R_{MC}(leg) (cm);V0 is a true photon", kTH3F, {{120, -30.f, 30.f}, {50, 0.f, 100.f}, {2, -0.5f, 1.5f}}, true); + fRegistry.add("Pair/MC/hPsiPair_PhiV_Qinv_Type_IsTrue", "per-photon conversion topology;|#psi_{pair}| (rad);#varphi_{V} (rad);q_{inv} (GeV/c);truth type;is true photon", kTHnSparseF, {{90, 0.f, o2::constants::math::PIHalf}, {90, 0.f, o2::constants::math::PI}, axisQinvCoarse, axisTruthType, {2, -0.5f, 1.5f}}, true); + fRegistry.add("Pair/MC/hMcEventPattern_NOwn_Qinv_Type", "MC event of the four legs;0 all same, 1 two consistent V0 from different events, 2 a V0 mixes events;N legs from own event;q_{inv} (GeV/c);truth type", kTHnSparseF, {{3, -0.5f, 2.5f}, {5, -0.5f, 4.5f}, axisQinv, axisTruthType}, true); + } + + void addAncestryHistograms() + { + if (!qaflags.cfgDoAncestry.value) { + return; + } + const AxisSpec axAnc = makeAxisEnum(static_cast(mcutil::AncestorKind::NClasses), "ancestor kind (0 none, 1 string/parton, 2 beam/nucleus, 3 #pi^{0}, 4 #eta, 5 other meson, 6 baryon, 7 photon, 8 electron, 9 other)"); + const AxisSpec axReco = makeAxisEnum(static_cast(mcutil::V0RecoStatus::NClasses), "V0 reco status (0 no label, 1 no conv leg, 2 one conv leg, 3 cross-built, 4 true photon)"); + const AxisSpec axSplit{6, -0.5f, 5.5f, "split code (2 n_{consistent} + shared)"}; + const AxisSpec axSame{2, -0.5f, 1.5f, "0 = different origin particles, 1 = the same"}; + const AxisSpec axSrc = makeAxisEnum(kNPhotonSources, kPhotonSourceTitle); + const AxisSpec axQtrue{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; + const std::string base = "Pair/MC/Ancestry/"; + + fRegistry.add((base + "hCensus_Qinv_Type").c_str(), "census of the four legs: code = (legs from a conversion) + 5 (distinct parent photons);census code;q_{inv} (GeV/c);truth type", kTH3F, {{25, -0.5f, 24.5f}, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hSplit_Qinv_Type").c_str(), "split code of the four legs;split code;q_{inv} (GeV/c);truth type", kTH3F, {axSplit, axisQinvCoarse, axisTruthType}, true); + + fRegistry.add((base + "hQtrue_Qreco_Split").c_str(), "pairs with exactly two parent photons: true q_{inv} of those two vs reconstructed, by pair class;q_{inv}^{true} (GeV/c);q_{inv}^{reco} (GeV/c);split code", kTH3F, {axQtrue, axisQinvCoarse, axSplit}, true); + fRegistry.add((base + "hAncKind_Qinv_Type").c_str(), "common ancestor of the two parent photons;common ancestor;q_{inv} (GeV/c);truth type", kTH3F, {axAnc, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hAnc1_Anc2_Same").c_str(), "origin particle of each parent photon (walk stops before beam/string);origin of photon A;origin of photon B;same origin particle?", kTH3F, {axAnc, axAnc, axSame}, true); + fRegistry.add((base + "hSame_Qinv_Type").c_str(), "the two parent photons come from the same origin particle;same origin?;q_{inv} (GeV/c);truth type", kTH3F, {axSame, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hSrc1_Src2_Type").c_str(), "pairs with exactly two parent photons: source of each (lower code first);source of photon A;source of photon B;truth type", kTH3F, {axSrc, axSrc, axisTruthType}, true); + fRegistry.add((base + "hTrueConv_d3D_Qinv_Type").c_str(), "true conversion points of the two parent photons;|#vec{r}_{A}-#vec{r}_{B}|^{true} (cm);q_{inv} (GeV/c);truth type", kTH3F, {{50, 0.f, 50.f}, axisQinvCoarse, axisTruthType}, true); + fRegistry.add((base + "hTrueConv_dR_dZ_Type").c_str(), "true conversion points of the two parent photons;|R_{A}-R_{B}|^{true} (cm);|z_{A}-z_{B}|^{true} (cm);truth type", kTH3F, {{50, 0.f, 25.f}, {50, 0.f, 25.f}, axisTruthType}, true); + fRegistry.add((base + "hRMeanTrue_DRTrue_Split").c_str(), "per pair: mean and difference of the two true conversion radii, by pair class;(R_{A}+R_{B})/2 (cm);|R_{A}-R_{B}| (cm);split code", kTH3F, {{100, 0.f, 100.f}, {50, 0.f, 25.f}, axSplit}, true); + fRegistry.add((base + "hTrueConv_dTheta_Qtrue_Type").c_str(), "true opening angle and q of the two parent photons;#Delta#theta_{#gamma#gamma}^{true} (mrad);q_{inv}^{true} (GeV/c);truth type", kTH3F, {{50, 0.f, 50.f}, axQtrue, axisTruthType}, true); + fRegistry.add((base + "hTrueConv_DeltaQ_Qtrue_Type").c_str(), "q_{inv}^{reco} - q_{inv}^{true} per pair;q_{inv}^{reco} - q_{inv}^{true} (GeV/c);q_{inv}^{true} (GeV/c);truth type", kTH3F, {{140, -0.05f, 0.30f}, axQtrue, axisTruthType}, true); + + fRegistry.add((base + "hEta_Phi_Reco").c_str(), "per V0 candidate: eta, phi and reco status;#eta;#varphi (rad);V0 reco status", kTH3F, {{20, -1.f, 1.f}, {72, 0.f, o2::constants::math::TwoPI}, axReco}, true); + fRegistry.add((base + "hR_Reco_Qinv").c_str(), "per V0 candidate: conversion radius and reco status;R_{conv} (cm);V0 reco status;q_{inv} (GeV/c)", kTH3F, {{25, 0.f, 100.f}, axReco, axisQinvCoarse}, true); + fRegistry.add((base + "hVx_Vy_Split").c_str(), "reconstructed conversion point of each V0, labelled by the class of its PAIR;V_{x} (cm);V_{y} (cm);split code", kTH3F, {{200, -100.f, 100.f}, {200, -100.f, 100.f}, axSplit}, true); + fRegistry.add((base + "hVxTrue_VyTrue_Split").c_str(), "true conversion point (production vertex of the e^{+} leg), by pair class;V_{x}^{true} (cm);V_{y}^{true} (cm);split code", kTH3F, {{200, -100.f, 100.f}, {200, -100.f, 100.f}, axSplit}, true); + fRegistry.add((base + "hRTruePos_RTrueNeg_Reco").c_str(), "true conversion radius of each leg, by reco status;R^{true}(e^{+}) (cm);R^{true}(e^{-}) (cm);V0 reco status", kTH3F, {{100, 0.f, 100.f}, {100, 0.f, 100.f}, axReco}, true); + fRegistry.add((base + "hRTrueMean_RReco_Reco").c_str(), "mean true conversion radius of the two legs vs reconstructed V0 radius;(R^{true}(e^{+}) + R^{true}(e^{-}))/2 (cm);R^{reco} (cm);V0 reco status", kTH3F, {{100, 0.f, 100.f}, {100, 0.f, 100.f}, axReco}, true); + fRegistry.add((base + "hSrcPos_SrcNeg_Reco").c_str(), "per V0 candidate: parent photon sources and reco status;source of the e^{+} parent;source of the e^{-} parent;V0 reco status", kTH3F, {axSrc, axSrc, axReco}, true); + fRegistry.add((base + "hSrcPos_SrcNeg_Type").c_str(), "per V0 candidate: parent photon sources and the truth type of its pair;source of the e^{+} parent;source of the e^{-} parent;truth type", kTH3F, {axSrc, axSrc, axisTruthType}, true); + fRegistry.add((base + "hAncPos_AncNeg_Reco").c_str(), "per V0: origin particle of each leg's parent photon;origin of the e^{+} parent;origin of the e^{-} parent;V0 reco status", kTH3F, {axAnc, axAnc, axReco}, true); + fRegistry.add((base + "hAncSame_Reco").c_str(), "per V0: the two legs' parent photons have the same origin particle;same origin?;V0 reco status", kTH2F, {axSame, axReco}, true); + std::vector psiEdges; + constexpr int kPsiFine = 60, kPsiCoarse = 13; + for (int i = 0; i <= kPsiFine; ++i) { + psiEdges.push_back(0.005 * i); // o2-linter: disable=magic-number (5 mrad steps) + } + for (int i = 1; i <= kPsiCoarse; ++i) { + psiEdges.push_back(0.30 + 0.1 * i); // o2-linter: disable=magic-number (coarse part up to pi/2) + } + std::vector chi2Edges; + constexpr int kChi2Fine = 50, kChi2Coarse = 20; + for (int i = 0; i <= kChi2Fine; ++i) { + chi2Edges.push_back(0.2 * i); // o2-linter: disable=magic-number (0.2 steps to 10) + } + for (int i = 1; i <= kChi2Coarse; ++i) { + chi2Edges.push_back(10. + 2.0 * i); // o2-linter: disable=magic-number (coarse part to 50) + } + fRegistry.add((base + "hPsiPair_Chi2_Split").c_str(), "per V0: the Run-2 cut plane, by pair class;|#psi_{pair}| (rad);#chi^{2}/ndf (KF);split code", kTH3F, {{psiEdges, "|#psi_{pair}| (rad)"}, {chi2Edges, "#chi^{2}/ndf (KF)"}, axSplit}, true); + fRegistry.add((base + "hPsiPair_R_Split").c_str(), "per V0: |psi_pair| vs conversion radius, by pair class;|#psi_{pair}| (rad);R_{conv}^{reco} (cm);split code", kTH3F, {{psiEdges, "|#psi_{pair}| (rad)"}, {10, 0.f, 100.f}, axSplit}, true); + } + + [[nodiscard]] inline bool passAsymmetryCut(float pt1, float pt2) const + { + if (ggpaircuts.cfgMaxAsymmetry.value < 0.f) { + return true; + } + const float sum = pt1 + pt2; + if (sum < KMinSigma) { + return false; + } + return std::fabs(pt1 - pt2) / sum < ggpaircuts.cfgMaxAsymmetry.value; + } + + [[nodiscard]] inline bool isInsideEllipse(float deta, float dphi) const + { + if (!ggpaircuts.cfgDoEllipseCut.value) { + return false; + } + const float sE = ggpaircuts.cfgEllipseSigEta.value; + const float sP = ggpaircuts.cfgEllipseSigPhi.value; + if (sE < KMinSigma || sP < KMinSigma) { + return false; + } + return (deta / sE) * (deta / sE) + (dphi / sP) * (dphi / sP) < ggpaircuts.cfgEllipseR2.value; + } + + [[nodiscard]] inline bool passRZCut(float deltaR, float deltaZ) const + { + if (ggpaircuts.cfgDoRCut.value && deltaR < ggpaircuts.cfgMinDeltaR.value) { + return false; + } + if (ggpaircuts.cfgDoZCut.value && std::fabs(deltaZ) < ggpaircuts.cfgMinDeltaZ.value) { + return false; + } + return true; + } + + [[nodiscard]] bool passPairCuts(PairObs const& obs, PhotonCand const& a, PhotonCand const& b) const + { + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA.value) { + return false; + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + return false; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { + return false; + } + return std::max(std::fabs(a.fDcaZToPV), std::fabs(b.fDcaZToPV)) < ggpaircuts.cfgMaxDcaZToPV.value && + std::max(std::fabs(a.fDcaXYToPV), std::fabs(b.fDcaXYToPV)) < ggpaircuts.cfgMaxDcaXYToPV.value; + } + + [[nodiscard]] PairObs buildPairObs(PhotonCand const& g1, PhotonCand const& g2) const + { + PairObs o; + const float dx = g1.fVx - g2.fVx, dy = g1.fVy - g2.fVy, dz = g1.fVz - g2.fVz; + o.deltaR = std::fabs(g1.rConv() - g2.rConv()); + o.deltaZ = dz; + o.deltaR3D = std::sqrt(dx * dx + dy * dy + dz * dz); + const ROOT::Math::XYZVector cp1(g1.fVx, g1.fVy, g1.fVz), cp2(g2.fVx, g2.fVy, g2.fVz); + const float mag1 = std::sqrt(cp1.Mag2()), mag2 = std::sqrt(cp2.Mag2()); + if (mag1 < KMinMagnitude || mag2 < KMinMagnitude) { + o.valid = false; + return o; + } + const float cosPA = std::clamp(static_cast(cp1.Dot(cp2) / (mag1 * mag2)), -1.f, 1.f); + o.opa = std::acos(cosPA); + const float cosHalf = std::cos(0.5f * o.opa); + o.drOverCosOA = (std::fabs(cosHalf) < KMinCosine) ? 1e12f : (o.deltaR3D / cosHalf); // o2-linter: disable=magic-number (sentinel for a degenerate opening angle) + o.v1 = ROOT::Math::PtEtaPhiMVector(g1.fPt, g1.fEta, g1.fPhi, 0.f); + o.v2 = ROOT::Math::PtEtaPhiMVector(g2.fPt, g2.fEta, g2.fPhi, 0.f); + o.deta = g1.fEta - g2.fEta; + o.dphi = RecoDecay::constrainAngle(g1.fPhi - g2.fPhi, -o2::constants::math::PI); + o.q = pairutil::computePairQ(o.v1, o.v2); + return o; + } + + [[nodiscard]] CrossObs computeCrossObs(PhotonCand const& a, PhotonCand const& b, float qinv) const + { + CrossObs c; + if (qinv < crosspair.cfgAltMaxQinv.value) { + const float bzkG = 10.f * mBzT; // o2-linter: disable=magic-number (Tesla -> kGauss) + c.fit[0] = evaluateAltPairingFitter(a.legParam(0), b.legParam(1), mBzT); + c.fit[1] = evaluateAltPairingFitter(b.legParam(0), a.legParam(1), mBzT); + c.ana[0] = buildAnalyticV0(a.legParam(0), b.legParam(1), bzkG, qaflags.cfgScoreWeight.value); + c.ana[1] = buildAnalyticV0(b.legParam(0), a.legParam(1), bzkG, qaflags.cfgScoreWeight.value); + c.nAltValidFit = (fitIsPhotonLike(c.fit[0]) ? 1 : 0) + (fitIsPhotonLike(c.fit[1]) ? 1 : 0); + c.nAltValidAna = (isPhotonLike(c.ana[0], mAltCuts) ? 1 : 0) + (isPhotonLike(c.ana[1], mAltCuts) ? 1 : 0); + c.maxDcaFit = std::max(c.fit[0].dca, c.fit[1].dca); + c.maxDcaXYFit = std::max(c.fit[0].dcaxy, c.fit[1].dcaxy); + c.maxDcaZFit = std::max(c.fit[0].dcaz, c.fit[1].dcaz); + c.maxDcaAna = std::max(c.ana[0].pca, c.ana[1].pca); + c.maxDcaXYAna = std::max(c.ana[0].dcaxy, c.ana[1].dcaxy); + c.maxDcaZAna = std::max(c.ana[0].dcaz, c.ana[1].dcaz); + c.evaluated = true; + } + auto legVec = [](PhotonCand const& p, int i) { + const auto& l = p.leg[static_cast(i)]; + return ROOT::Math::PtEtaPhiMVector(l.pt, l.eta, l.phi, KMe); + }; + c.mee[0] = static_cast((legVec(a, 0) + legVec(b, 1)).M()); + c.mee[1] = static_cast((legVec(b, 0) + legVec(a, 1)).M()); + if (qinv > 0.f) { + c.meeOverQ = std::min(c.mee[0], c.mee[1]) / qinv; + } + c.ownMee[0] = static_cast((legVec(a, 0) + legVec(a, 1)).M()); + c.ownMee[1] = static_cast((legVec(b, 0) + legVec(b, 1)).M()); + c.ownMeeMax = std::max(c.ownMee[0], c.ownMee[1]); + c.deltaMee = c.ownMeeMax - std::max(c.mee[0], c.mee[1]); + c.m4 = static_cast((legVec(a, 0) + legVec(a, 1) + legVec(b, 0) + legVec(b, 1)).M()); + return c; + } + + [[nodiscard]] inline bool fitIsPhotonLike(AltPairing const& r) const + { + return r.fitted && r.dca < mAltCuts.maxDca && r.rxy > mAltCuts.minR && r.rxy < mAltCuts.maxR && + r.cospa > mAltCuts.minCosPA && r.mee < mAltCuts.maxMee && r.dcaxy < mAltCuts.maxDcaXY && r.dcaz < mAltCuts.maxDcaZ; + } + + [[nodiscard]] inline bool passCrossPairVeto(CrossObs const& c) const + { + const int need = crosspair.cfgAltRequireBoth.value ? 2 : 1; + const bool passGeom = c.nAltValidFit < need; + const bool passDca = (crosspair.cfgCrossMinMaxAltDca.value <= 0.f) || (c.maxDcaFit > crosspair.cfgCrossMinMaxAltDca.value); + switch (crosspair.cfgCrossVetoMode.value) { + case 2: // o2-linter: disable=magic-number (veto mode) + return passDca; + case 3: // o2-linter: disable=magic-number (veto mode) + return passGeom && passDca; + default: + return passGeom; + } + } + struct Emulation { + int outcome{0}; + float deltaScore{0.f}; + }; + [[nodiscard]] Emulation emulate(CrossObs const& c, PhotonCand const& a, PhotonCand const& b) const + { + Emulation e; + if (!c.evaluated || !a.own.ok || !b.own.ok) { + return e; + } + if (c.nAltValidAna < 2) { // o2-linter: disable=magic-number (both cross candidates must be photon-like) + e.outcome = 3; // o2-linter: disable=magic-number (outcome code) + return e; + } + e.deltaScore = (c.ana[0].score + c.ana[1].score) - (a.own.score + b.own.score); + e.outcome = (e.deltaScore < 0.f) ? 2 : 1; // o2-linter: disable=magic-number (outcome code) + return e; + } + + template + void fillPairQA(PairObs const& obs, PhotonCand const& a, PhotonCand const& b) + { + constexpr auto dir = (step == 0) ? "Pair/QA/Before/" : "Pair/QA/AfterPairCuts/"; + const float qinv = obs.q.qinv; + fRegistry.fill(HIST(dir) + HIST("hDEtaDPhi_Qinv"), obs.deta, obs.dphi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDeltaRVsQinv"), qinv, obs.deltaR); + fRegistry.fill(HIST(dir) + HIST("hDeltaZVsQinv"), qinv, obs.deltaZ); + fRegistry.fill(HIST(dir) + HIST("hDeltaR3DVsQinv"), qinv, obs.deltaR3D); + fRegistry.fill(HIST(dir) + HIST("hDrOverCosOAVsQinv"), qinv, std::min(obs.drOverCosOA, 199.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST(dir) + HIST("hOpeningAngleVsQinv"), qinv, obs.opa); + const int nTimed = a.nTimedLegs() + b.nTimedLegs(); + fRegistry.fill(HIST(dir) + HIST("hQinv_NTimed_NForeign"), qinv, static_cast(nTimed), static_cast(a.nForeignLegs() + b.nForeignLegs())); + fRegistry.fill(HIST(dir) + HIST("hQinv_MaxDcaZ_NTimed"), qinv, std::min(std::max(std::fabs(a.fDcaZToPV), std::fabs(b.fDcaZToPV)), 9.9f), static_cast(nTimed)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST(dir) + HIST("hQinv_MaxDcaXY_NTimed"), qinv, std::min(std::max(std::fabs(a.fDcaXYToPV), std::fabs(b.fDcaXYToPV)), 2.9f), static_cast(nTimed)); // o2-linter: disable=magic-number (clamp to the axis) + const float ra = a.rConv(), rb = b.rConv(); + const bool aInner = ra <= rb; + const float rIn = aInner ? ra : rb, rOut = aInner ? rb : ra; + const bool outerSofter = aInner ? (b.fPt < a.fPt) : (a.fPt < b.fPt); + const bool inSparseWindow = qinv < qaflags.cfgMaxQinvForSparses.value; + if (inSparseWindow) { + fRegistry.fill(HIST(dir) + HIST("hRin_Rout_OuterSofter_Qinv"), rIn, rOut, outerSofter ? 1.f : 0.f, qinv); + } + for (const PhotonCand& p : {std::cref(a), std::cref(b)}) { + fRegistry.fill(HIST(dir) + HIST("hLegStart_dNFindable_dNits_Qinv"), std::fabs(p.leg[0].nClsFindable - p.leg[1].nClsFindable), std::fabs(p.leg[0].nClsITS - p.leg[1].nClsITS), qinv); + if (inSparseWindow) { + fRegistry.fill(HIST(dir) + HIST("hV0_Eta_Phi_R_Qinv"), p.fEta, p.fPhi, std::min(p.rConv(), 99.9f), qinv); // o2-linter: disable=magic-number (clamp to the axis) + } + } + fRegistry.fill(HIST(dir) + HIST("hPhi_lowerPtV0"), (a.fPt < b.fPt) ? a.fPhi : b.fPhi); + const float pairEta = 0.5f * (a.fEta + b.fEta); + const float pairPhi = RecoDecay::constrainAngle(static_cast((obs.v1 + obs.v2).Phi()), 0.f); + const float deltaRAng = std::hypot(obs.deta, obs.dphi); + if (inSparseWindow) { + fRegistry.fill(HIST(dir) + HIST("hDEta_DPhi_PairEta_Qinv"), obs.deta, obs.dphi, pairEta, qinv); + fRegistry.fill(HIST(dir) + HIST("hDEta_DPhi_PairPhi_Qinv"), obs.deta, obs.dphi, pairPhi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDPhi_Phi1_Phi2_Qinv"), obs.dphi, a.fPhi, b.fPhi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDEta_Eta1_Eta2_Qinv"), obs.deta, a.fEta, b.fEta, qinv); + } + fRegistry.fill(HIST(dir) + HIST("hDeltaRAngVsQinv"), qinv, std::min(deltaRAng, 0.499f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST(dir) + HIST("hDeltaRAng_PairEta_PairPhi"), std::min(deltaRAng, 0.499f), pairEta, pairPhi); // o2-linter: disable=magic-number (clamp to the axis) + const int c1 = v0LegClass(a.legCounts), c2 = v0LegClass(b.legCounts); + fRegistry.fill(HIST(dir) + HIST("hV0Class1_V0Class2_Qinv"), static_cast(std::min(c1, c2)), static_cast(std::max(c1, c2)), qinv); + fRegistry.fill(HIST(dir) + HIST("hNLegsITSTPC_NLegsTPConly_Qinv"), static_cast(a.legCounts.nITSTPC + b.legCounts.nITSTPC), static_cast(a.legCounts.nTPCOnly + b.legCounts.nTPCOnly), qinv); + } + + void fillLegSimilarity(PhotonCand const& a, PhotonCand const& b, float qinv) + { + if (!qaflags.cfgFillLegSimilarity.value || qinv >= qaflags.cfgMaxQinvForSparses.value) { + return; + } + for (int il = 0; il < 2; ++il) { // o2-linter: disable=magic-number (e+ with e+, e- with e-) + const auto& la = a.leg[static_cast(il)]; + const auto& lb = b.leg[static_cast(il)]; + const float ptSum = la.pt + lb.pt; + const float ptRatio = (ptSum > KMinSigma) ? std::fabs(la.pt - lb.pt) / ptSum : 1.f; + fRegistry.fill(HIST("Pair/LegSimilarity/hdEta_dPhi_ptRatio_dNsig_Qinv"), la.eta - lb.eta, + RecoDecay::constrainAngle(la.phi - lb.phi, -o2::constants::math::PI), ptRatio, + std::min(std::fabs(la.nsigEl - lb.nsigEl), 7.99f), qinv); // o2-linter: disable=magic-number (clamp to the axis) + } + } + + template + void fillAnalyticV0QA(PhotonCand const& p) + { + if (!qaflags.cfgDoAnalyticV0QA.value) { + return; + } + fRegistry.fill(HIST("Photon/AnalyticV0/hOk"), p.own.ok ? 1.f : 0.f); + if (!p.own.ok) { + return; + } + float truth = -1.f; + if constexpr (IsMC) { + if (p.mc.hasBothLabels()) { + truth = p.mc.isTruePhoton ? 1.f : 0.f; + } + } + const float rB = p.rConv(); + const float xA = p.own.vtx[0], yA = p.own.vtx[1], zA = p.own.vtx[2] + p.fVtxZ; + fRegistry.fill(HIST("Photon/AnalyticV0/hdR_R_Truth"), std::hypot(xA, yA) - rB, rB, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdZ_R_Truth"), zA - p.fVz, rB, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdPhiConv_R_Truth"), RecoDecay::constrainAngle(std::atan2(yA, xA) - std::atan2(p.fVy, p.fVx), -o2::constants::math::PI), rB, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdPtRel_Pt_Truth"), (p.fPt > KMinSigma) ? (p.own.pt() - p.fPt) / p.fPt : 0.f, p.fPt, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hdCosPA_Truth"), p.own.cospa - p.cospa, truth); + fRegistry.fill(HIST("Photon/AnalyticV0/hPcaAna_PcaBuilder_Truth"), std::min(p.own.pca, 5.99f), std::min(p.pca, 5.99f), truth); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Photon/AnalyticV0/hScore_R_Truth"), std::min(p.own.score, 29.9f), rB, truth); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Photon/AnalyticV0/hPhotonLike_Truth"), isPhotonLike(p.own, mAltCuts) ? 1.f : 0.f, truth); + } + template + void runDedupQA(std::vector& cands) + { + const auto n = static_cast(cands.size()); + std::vector nDupPartners(cands.size(), 0); + for (int i = 0; i < n; ++i) { + for (int j = i + 1; j < n; ++j) { + auto& a = cands[static_cast(i)]; + auto& b = cands[static_cast(j)]; + const float dEta = std::fabs(a.fEta - b.fEta); + const float dPhi = std::fabs(RecoDecay::constrainAngle(a.fPhi - b.fPhi, -o2::constants::math::PI)); + const float ptSum = a.fPt + b.fPt; + const float ptAsym = (ptSum > KMinSigma) ? std::fabs(a.fPt - b.fPt) / ptSum : 0.f; + const float dR = std::fabs(a.rConv() - b.rConv()); + const bool flagged = pairutil::isDuplicatePair(a.dedupCand, b.dedupCand, mDedupCfg); + fRegistry.fill(HIST("Dedup/hdEta_dPhi_ptAsym"), dEta, dPhi, ptAsym); + fRegistry.fill(HIST("Dedup/hdR_ptAsym"), dR, ptAsym); + for (int il = 0; il < 2; ++il) { // o2-linter: disable=magic-number (e+ and e- legs) + const auto ld = pairutil::legDistance(a.dedupCand, b.dedupCand, il); + float truth = 4.f; // o2-linter: disable=magic-number (no / invalid MC info) + if constexpr (IsMC) { + const int ida = (il == 0) ? a.mc.mcPosId : a.mc.mcNegId; + const int idb = (il == 0) ? b.mc.mcPosId : b.mc.mcNegId; + const int idbX = (il == 0) ? b.mc.mcNegId : b.mc.mcPosId; + if (ida >= 0 && (idb >= 0 || idbX >= 0)) { + const bool sameRecoTrack = (a.leg[static_cast(il)].trackId == b.leg[static_cast(il)].trackId) || + (a.leg[static_cast(il)].trackId == b.leg[static_cast(1 - il)].trackId); + const bool sameMC = (ida == idb) || (ida == idbX); + // 0 diff reco/diff MC, 1 diff reco/same MC, 2 same reco/same MC, 3 same reco/diff MC + if (!sameRecoTrack) { + truth = sameMC ? 1.f : 0.f; + } else { + truth = sameMC ? 2.f : 3.f; // o2-linter: disable=magic-number (class codes, see booking) + } + } + const bool legFlagged = pairutil::legsIdentical(a.dedupCand, b.dedupCand, il, mDedupCfg); + fRegistry.fill(HIST("Dedup/MC/hLegTrackTruthClass"), truth); + fRegistry.fill(HIST("Dedup/MC/hLegTrackTruthClass_vs_Flagged"), truth, legFlagged ? 1.f : 0.f); + } + fRegistry.fill(HIST("Dedup/hLeg_dEta_dPhi_Truth"), ld.dEta, ld.dPhi, truth); + fRegistry.fill(HIST("Dedup/hLeg_ptAsym_dEdxAsym_Truth"), ld.ptAsym, ld.dedxAsym, truth); + fRegistry.fill(HIST("Dedup/hLeg_fShared_Truth"), ld.fShared, truth); + } + if constexpr (IsMC) { + const auto dupClass = static_cast(static_cast(mcutil::classifyDupPair(a.mc, b.mc))); + fRegistry.fill(HIST("Dedup/MC/hDupClass"), dupClass); + fRegistry.fill(HIST("Dedup/MC/hDupClass_vs_Flagged"), dupClass, flagged ? 1.f : 0.f); + fRegistry.fill(HIST("Dedup/MC/hDupClass_dEta_dPhi"), dupClass, dEta, dPhi); + fRegistry.fill(HIST("Dedup/MC/hDupClass_ptAsym_dR"), dupClass, ptAsym, dR); + const std::array split = { + a.leg[0].trackId != b.leg[0].trackId && a.mc.mcPosId >= 0 && a.mc.mcPosId == b.mc.mcPosId, + a.leg[1].trackId != b.leg[1].trackId && a.mc.mcNegId >= 0 && a.mc.mcNegId == b.mc.mcNegId}; + if (split[0] || split[1]) { + fRegistry.fill(HIST("Dedup/MC/hSplitLegPairClass"), dupClass); + float v0Type = 3.f; // o2-linter: disable=magic-number (other) + if (a.mc.isTruePhoton && b.mc.isTruePhoton && a.mc.posPhotonId == b.mc.posPhotonId) { + v0Type = 0.f; + } else if (a.mc.isTruePhoton != b.mc.isTruePhoton) { + v0Type = 1.f; + } else if (!a.mc.isTruePhoton && !b.mc.isTruePhoton) { + v0Type = 2.f; // o2-linter: disable=magic-number (both fake) + } + fRegistry.fill(HIST("Dedup/MC/hSplitLegV0Type"), v0Type); + for (int il = 0; il < 2; ++il) { // o2-linter: disable=magic-number (two legs) + if (!split[static_cast(il)]) { + continue; + } + const auto ldSplit = pairutil::legDistance(a.dedupCand, b.dedupCand, il); + fRegistry.fill(HIST("Dedup/MC/hSplitLeg_ptAsym_dEdxAsym_DupClass"), ldSplit.ptAsym, ldSplit.dedxAsym, dupClass); + fRegistry.fill(HIST("Dedup/MC/hSplitLeg_fShared_DupClass"), ldSplit.fShared, dupClass); + } + } + if (mcutil::classifyDupPair(a.mc, b.mc) == mcutil::DupClass::SameMcPhoton) { + // 0 no split leg, 1 split e+, 2 split e-, 3 both + const float splitType = static_cast((split[0] ? 1 : 0) + (split[1] ? 2 : 0)); // o2-linter: disable=magic-number (bit code, see booking) + fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType"), splitType); + fRegistry.fill(HIST("Dedup/MC/hSamePhotonDuplicateType_vs_Flagged"), splitType, flagged ? 1.f : 0.f); + fRegistry.fill(HIST("Dedup/MC/hDupTrackClassMatrix"), static_cast(a.dedupCand.nITSTPC), static_cast(b.dedupCand.nITSTPC)); + fRegistry.fill(HIST("Dedup/MC/hDupDeltaRconv"), dR); + } + } + if (flagged) { + ++nDupPartners[static_cast(i)]; + ++nDupPartners[static_cast(j)]; + const bool keepA = pairutil::isBetterCand(a.dedupCand, b.dedupCand); + (keepA ? b : a).dedupRejected = true; + if constexpr (IsMC) { + const auto& kept = keepA ? a : b; + const auto& dropped = keepA ? b : a; + float outcome = 4.f; // o2-linter: disable=magic-number (both true, distinct photons) + if (kept.mc.isTruePhoton && !dropped.mc.isTruePhoton) { + outcome = 0.f; + } else if (!kept.mc.isTruePhoton && dropped.mc.isTruePhoton) { + outcome = 1.f; + } else if (kept.mc.isTruePhoton && dropped.mc.isTruePhoton && kept.mc.posPhotonId == dropped.mc.posPhotonId) { + outcome = 2.f; // o2-linter: disable=magic-number (both true, same photon) + } else if (!kept.mc.isTruePhoton && !dropped.mc.isTruePhoton) { + outcome = 3.f; // o2-linter: disable=magic-number (both fake) + } + fRegistry.fill(HIST("Dedup/MC/hChoiceOutcome"), outcome); + } + } + } + } + if constexpr (IsMC) { + std::unordered_map> perMcPhoton; // photon id -> (count, reco pT of the last candidate) + for (const auto& c : cands) { + if (c.mc.isTruePhoton) { + auto& entry = perMcPhoton[c.mc.posPhotonId]; + ++entry.first; + entry.second = c.fPt; + } + } + for (const auto& [mcId, entry] : perMcPhoton) { + const auto nCand = static_cast(std::min(entry.first, 6)); // o2-linter: disable=magic-number (last bin) + fRegistry.fill(HIST("Dedup/MC/hNCandPerMcPhoton"), nCand); + fRegistry.fill(HIST("Dedup/MC/hNCandPerMcPhoton_vs_Pt"), nCand, entry.second); + } + } + int nRejected = 0; + for (int i = 0; i < n; ++i) { + fRegistry.fill(HIST("Dedup/hNDupPartners_vs_Pt"), static_cast(std::min(nDupPartners[static_cast(i)], 5)), cands[static_cast(i)].fPt); // o2-linter: disable=magic-number (last bin) + nRejected += cands[static_cast(i)].dedupRejected ? 1 : 0; + } + fRegistry.fill(HIST("Dedup/hNCandBeforeAfter"), 0.f, static_cast(n)); + fRegistry.fill(HIST("Dedup/hNCandBeforeAfter"), 1.f, static_cast(n - nRejected)); + } + + template + void fillCrossPairQA(CrossObs const& c, PairObs const& obs, float truthAxis, PhotonCand const& a, PhotonCand const& b) + { + const float qinv = obs.q.qinv; + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDca_Fit"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaFit, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDca_Ana"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaAna, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaXY_Fit"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaXYFit, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaXY_Ana"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaXYAna, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Fit"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaZFit, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Ana"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaZAna, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + if (c.evaluated) { + for (int k = 0; k < 2; ++k) { // o2-linter: disable=magic-number (the two alternative pairings) + if (c.fit[static_cast(k)].fitted && c.ana[static_cast(k)].ok) { + fRegistry.fill(HIST("Pair/CrossPair/hAltDca_Fit_vs_Ana"), std::min(c.fit[static_cast(k)].dca, 11.9f), std::min(c.ana[static_cast(k)].pca, 11.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/CrossPair/hAltCosPA_Fit_vs_Ana"), c.fit[static_cast(k)].cospa, c.ana[static_cast(k)].cospa); + } + } + fRegistry.fill(HIST("Pair/CrossPair/hNAltValid_Fit_vs_Ana"), static_cast(c.nAltValidFit), static_cast(c.nAltValidAna)); + } + if (c.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) + fRegistry.fill(HIST("Pair/CrossPair/hMeeRatio_dR_Qinv"), c.meeOverQ, obs.deltaR, qinv); + } + fRegistry.fill(HIST("Pair/CrossPair/hRepairDeltaMee_OwnMee_Qinv"), c.deltaMee, c.ownMeeMax, qinv); + if (qinv < qaflags.cfgMaxQinvForSparses.value) { + fRegistry.fill(HIST("Pair/CrossPair/hMee1_Mee2_M4_Qinv"), c.ownMee[0], c.ownMee[1], c.m4, qinv); + } + if constexpr (IsMC) { + if (truthAxis < 0.f) { + return; + } + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDca_Fit_Type"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaFit, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDca_Ana_Type"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaAna, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Fit_Type"), qinv, static_cast(c.nAltValidFit), std::min(c.maxDcaZFit, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_NAltValid_MaxAltDcaZ_Ana_Type"), qinv, static_cast(c.nAltValidAna), std::min(c.maxDcaZAna, 11.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + if (c.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) + fRegistry.fill(HIST("Pair/MC/CrossPair/hMeeRatio_Qinv_Type"), c.meeOverQ, qinv, truthAxis); + } + if (qinv < qaflags.cfgMaxQinvForSparses.value) { + fRegistry.fill(HIST("Pair/MC/CrossPair/hMee1_Mee2_M4_Qinv_Type"), c.ownMee[0], c.ownMee[1], c.m4, qinv, truthAxis); + } + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_MaxDcaZ_Type"), qinv, std::min(std::max(std::fabs(a.fDcaZToPV), std::fabs(b.fDcaZToPV)), 9.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/CrossPair/hQinv_MaxDcaXY_Type"), qinv, std::min(std::max(std::fabs(a.fDcaXYToPV), std::fabs(b.fDcaXYToPV)), 2.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + const Emulation emu = emulate(c, a, b); + fRegistry.fill(HIST("Pair/MC/CrossPair/hEmulation_vs_Truth"), qinv, truthAxis, static_cast(emu.outcome)); + if (emu.outcome >= 1) { + fRegistry.fill(HIST("Pair/MC/CrossPair/hEmuDeltaScore_vs_Truth"), qinv, truthAxis, std::clamp(emu.deltaScore, -19.99f, 19.99f)); // o2-linter: disable=magic-number (clamp to the axis) + } + fRegistry.fill(HIST("Pair/MC/CrossPair/hFakeSubtype_Emu_Qinv"), static_cast(mcutil::classifyFakeSubtype(a.mc, b.mc)), static_cast(emu.outcome), qinv); + } + } + + template + void fillTypeObservables(PairObs const& obs) + { + constexpr auto dir = + (T == mcutil::PairTruthType::TrueTrueDistinct) ? "Pair/MC/TrueTrueDistinct/" + : (T == mcutil::PairTruthType::TrueTrueSamePhoton) ? "Pair/MC/TrueTrueSamePhoton/" + : (T == mcutil::PairTruthType::SharedMcLeg) ? "Pair/MC/SharedMcLeg/" + : (T == mcutil::PairTruthType::TrueFake) ? "Pair/MC/TrueFake/" + : (T == mcutil::PairTruthType::FakeFake) ? "Pair/MC/FakeFake/" + : "Pair/MC/Pi0Daughters/"; + const float qinv = obs.q.qinv; + fRegistry.fill(HIST(dir) + HIST("hDeltaRVsQinv"), qinv, obs.deltaR); + fRegistry.fill(HIST(dir) + HIST("hDeltaZVsQinv"), qinv, obs.deltaZ); + fRegistry.fill(HIST(dir) + HIST("hDeltaR3DVsQinv"), qinv, obs.deltaR3D); + fRegistry.fill(HIST(dir) + HIST("hDEtaDPhi"), obs.deta, obs.dphi); + if (qaflags.cfgFillTypeSparses.value) { + fRegistry.fill(HIST(dir) + HIST("hDEtaDPhi_Qinv"), obs.deta, obs.dphi, qinv); + fRegistry.fill(HIST(dir) + HIST("hDeltaR_DeltaZ_Qinv"), obs.deltaR, obs.deltaZ, qinv); + } + } + + void fillTypeObservables(mcutil::PairTruthType t, PairObs const& obs) + { + switch (t) { + case mcutil::PairTruthType::TrueTrueDistinct: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::TrueTrueSamePhoton: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::SharedMcLeg: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::TrueFake: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::FakeFake: + fillTypeObservables(obs); + break; + case mcutil::PairTruthType::Pi0Daughters: + fillTypeObservables(obs); + break; + default: + break; + } + } + + template + void fillAncestry(TMCParticles const& mcParticles, PhotonCand const& a, PhotonCand const& b, PairObs const& obs, float truthAxis) + { + if (!qaflags.cfgDoAncestry.value) { + return; + } + const float qinv = obs.q.qinv; + if (qinv >= qaflags.cfgMaxQinvForSparses.value) { + return; + } + const int maxGen = qaflags.cfgAncestorMaxGen.value; + const auto census = mcutil::censusMothers(a.mc, b.mc); + const auto splitAxis = static_cast(census.splitCode()); + const auto censusCode = static_cast(census.nConvLegs + 5 * census.nMothers); // o2-linter: disable=magic-number (code = legs + 5 mothers) + fRegistry.fill(HIST("Pair/MC/Ancestry/hCensus_Qinv_Type"), censusCode, qinv, truthAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSplit_Qinv_Type"), splitAxis, qinv, truthAxis); + + if (census.nMothers == 2) { // o2-linter: disable=magic-number (two parent photons) + const int idA = census.distinct[0], idB = census.distinct[1]; + const float qTrue = mcutil::trueQinvOfPhotons(mcParticles, idA, idB); + const auto ancKind = static_cast(static_cast(mcutil::commonAncestorKind(mcParticles, idA, idB, maxGen))); + fRegistry.fill(HIST("Pair/MC/Ancestry/hQtrue_Qreco_Split"), qTrue, qinv, splitAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hAncKind_Qinv_Type"), ancKind, qinv, truthAxis); + const int origA = mcutil::originParticleId(mcParticles, idA), origB = mcutil::originParticleId(mcParticles, idB); + const auto kindA = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(origA).pdgCode()))); + const auto kindB = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(origB).pdgCode()))); + const float same = (origA == origB) ? 1.f : 0.f; + fRegistry.fill(HIST("Pair/MC/Ancestry/hAnc1_Anc2_Same"), std::min(kindA, kindB), std::max(kindA, kindB), same); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSame_Qinv_Type"), same, qinv, truthAxis); + const int srcA = photonSource(mcParticles, idA), srcB = photonSource(mcParticles, idB); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSrc1_Src2_Type"), static_cast(std::min(srcA, srcB)), static_cast(std::max(srcA, srcB)), truthAxis); + std::array cA{}, cB{}; + if (mcutil::conversionPoint(mcParticles, idA, cA) && mcutil::conversionPoint(mcParticles, idB, cB)) { + const float rA = std::hypot(cA[0], cA[1]), rB = std::hypot(cB[0], cB[1]); + const float d3 = std::hypot(cA[0] - cB[0], cA[1] - cB[1], cA[2] - cB[2]); + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_d3D_Qinv_Type"), std::min(d3, 49.9f), qinv, truthAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_dR_dZ_Type"), std::min(std::fabs(rA - rB), 24.9f), std::min(std::fabs(cA[2] - cB[2]), 24.9f), truthAxis); // o2-linter: disable=magic-number (clamp to the axes) + fRegistry.fill(HIST("Pair/MC/Ancestry/hRMeanTrue_DRTrue_Split"), 0.5f * (rA + rB), std::min(std::fabs(rA - rB), 24.9f), splitAxis); // o2-linter: disable=magic-number (clamp to the axis) + } + const float theta = mcutil::trueOpeningAngle(mcParticles, idA, idB); + if (theta >= 0.f) { + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_dTheta_Qtrue_Type"), std::min(theta * 1000.f, 49.9f), qTrue, truthAxis); // o2-linter: disable=magic-number (rad -> mrad, clamp) + } + fRegistry.fill(HIST("Pair/MC/Ancestry/hTrueConv_DeltaQ_Qtrue_Type"), qinv - qTrue, qTrue, truthAxis); + } + + // per V0 candidate + for (const PhotonCand& p : {std::cref(a), std::cref(b)}) { + const auto reco = static_cast(static_cast(mcutil::classifyV0Reco(p.mc))); + const float rReco = p.rConv(); + { + const auto srcPos = static_cast(photonSource(mcParticles, p.mc.posPhotonId)); + const auto srcNeg = static_cast(photonSource(mcParticles, p.mc.negPhotonId)); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSrcPos_SrcNeg_Reco"), srcPos, srcNeg, reco); + fRegistry.fill(HIST("Pair/MC/Ancestry/hSrcPos_SrcNeg_Type"), srcPos, srcNeg, truthAxis); + } + fRegistry.fill(HIST("Pair/MC/Ancestry/hEta_Phi_Reco"), p.fEta, p.fPhi, reco); + fRegistry.fill(HIST("Pair/MC/Ancestry/hR_Reco_Qinv"), std::min(rReco, 99.9f), reco, qinv); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/Ancestry/hVx_Vy_Split"), p.fVx, p.fVy, splitAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hPsiPair_Chi2_Split"), std::fabs(p.psipair), std::min(p.chi2, 49.9f), splitAxis); // o2-linter: disable=magic-number (clamp to the axis) + fRegistry.fill(HIST("Pair/MC/Ancestry/hPsiPair_R_Split"), std::fabs(p.psipair), std::min(rReco, 99.9f), splitAxis); // o2-linter: disable=magic-number (clamp to the axis) + if (p.mc.mcPosId >= 0 && p.mc.mcNegId >= 0) { + const auto ePos = mcParticles.iteratorAt(p.mc.mcPosId); + const auto eNeg = mcParticles.iteratorAt(p.mc.mcNegId); + const float rPos = std::hypot(static_cast(ePos.vx()), static_cast(ePos.vy())); + const float rNeg = std::hypot(static_cast(eNeg.vx()), static_cast(eNeg.vy())); + fRegistry.fill(HIST("Pair/MC/Ancestry/hVxTrue_VyTrue_Split"), static_cast(ePos.vx()), static_cast(ePos.vy()), splitAxis); + fRegistry.fill(HIST("Pair/MC/Ancestry/hRTruePos_RTrueNeg_Reco"), std::min(rPos, 99.9f), std::min(rNeg, 99.9f), reco); // o2-linter: disable=magic-number (clamp to the axes) + fRegistry.fill(HIST("Pair/MC/Ancestry/hRTrueMean_RReco_Reco"), std::min(0.5f * (rPos + rNeg), 99.9f), std::min(rReco, 99.9f), reco); // o2-linter: disable=magic-number (clamp to the axes) + } + if (p.mc.posPhotonId >= 0 && p.mc.negPhotonId >= 0) { + const int oPos = mcutil::originParticleId(mcParticles, p.mc.posPhotonId); + const int oNeg = mcutil::originParticleId(mcParticles, p.mc.negPhotonId); + const auto kPos = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(oPos).pdgCode()))); + const auto kNeg = static_cast(static_cast(mcutil::ancestorKindOfPdg(mcParticles.iteratorAt(oNeg).pdgCode()))); + fRegistry.fill(HIST("Pair/MC/Ancestry/hAncPos_AncNeg_Reco"), kPos, kNeg, reco); + fRegistry.fill(HIST("Pair/MC/Ancestry/hAncSame_Reco"), (oPos == oNeg) ? 1.f : 0.f, reco); + } + } + } + + template + void collectPhotons(TPhotons const& photonsColl, TMCParticles const& mcParticles, float vtxZ, std::vector& cands) + { + cands.clear(); + cands.reserve(photonsColl.size()); + for (const auto& g : photonsColl) { + if (!fV0PhotonCut.template IsSelected(g)) { + continue; + } + const auto pos = g.template posTrack_as(); + const auto ele = g.template negTrack_as(); + PhotonCand c; + c.gi = g.globalIndex(); + c.fPt = g.pt(); + c.fEta = g.eta(); + c.fPhi = g.phi(); + c.fVx = g.vx(); + c.fVy = g.vy(); + c.fVz = g.vz(); + c.fVtxZ = vtxZ; + c.fDcaXYToPV = static_cast(g.dcaXYtopv()); + c.fDcaZToPV = static_cast(g.dcaZtopv()); + c.psipair = static_cast(g.psipair()); + c.phiv = static_cast(g.phiv()); + c.chi2 = static_cast(g.chiSquareNDF()); + c.cospa = static_cast(g.cospa()); + c.pca = static_cast(g.pca()); + auto fillLeg = [&](LegLite& l, auto const& t) { + l.pt = static_cast(t.pt()); + l.eta = static_cast(t.eta()); + l.phi = static_cast(t.phi()); + l.nsigEl = static_cast(t.tpcNSigmaEl()); + l.dcaZ = static_cast(t.dcaZ()); + l.nClsFindable = static_cast(t.tpcNClsFindable()); + l.nClsITS = static_cast(t.itsNCls()); + l.timed = t.hasITS() || t.hasTRD() || t.hasTOF(); + l.foreign = (t.collisionId() != g.collisionId()); + l.trackId = t.trackId(); + }; + fillLeg(c.leg[0], pos); + fillLeg(c.leg[1], ele); + c.legCounts = pairutil::getV0PhotonLegCounts(pos, ele); + c.dedupCand = pairutil::makeDedupCand(g, pos, ele); + if (qaflags.cfgDoAnalyticV0QA.value || qaflags.cfgDoCrossPairQA.value) { + c.own = buildAnalyticV0(c.legParam(0), c.legParam(1), 10.f * mBzT, qaflags.cfgScoreWeight.value); // o2-linter: disable=magic-number (Tesla -> kGauss) + } + if constexpr (IsMC) { + c.mc = mcutil::makePhotonMCInfo(g, mcParticles); + } + cands.push_back(c); + } + } + + template + void runPairQa(TCollision const& collision, std::vector const& cands, TMCParticles const& mcParticles) + { + const int evOwn = [&]() { + if constexpr (IsMC) { + return collision.has_emmcevent() ? static_cast(collision.emmceventId()) : -1; + } else { + return -1; + } + }(); + for (size_t i = 0; i < cands.size(); ++i) { + for (size_t j = i + 1; j < cands.size(); ++j) { + const auto& g1 = cands[i]; + const auto& g2 = cands[j]; + if (g1.sharesTrackWith(g2)) { + continue; + } + const bool dedupWouldRemove = g1.dedupRejected || g2.dedupRejected; + if (dedup.cfgDoDedup.value && dedupWouldRemove) { + continue; + } + if (!passAsymmetryCut(g1.fPt, g2.fPt)) { + continue; + } + if (!pairutil::pairBelowQmax(g1.fPt, g1.fEta, g1.fPhi, g2.fPt, g2.fEta, g2.fPhi, qaflags.cfgMaxQinvForQA.value)) { + continue; + } + const PairObs obs = buildPairObs(g1, g2); + if (!obs.valid) { + continue; + } + const float qinv = obs.q.qinv; + + float truthAxis = -1.f; + mcutil::PairTruthType truthType = mcutil::PairTruthType::Unknown; + if constexpr (IsMC) { + if (g1.mc.hasBothLabels() && g2.mc.hasBothLabels()) { + truthType = mcutil::pairTruthType(g1.mc, g2.mc, mcParticles); + truthAxis = static_cast(static_cast(truthType)); + } + } + + fillPairQA<0>(obs, g1, g2); + fillLegSimilarity(g1, g2, qinv); + + if (!passPairCuts(obs, g1, g2)) { + continue; + } + CrossObs cross; + if (qaflags.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + cross = computeCrossObs(g1, g2, qinv); + if (qaflags.cfgDoCrossPairQA.value) { + fillCrossPairQA(cross, obs, truthAxis, g1, g2); + } + const bool vetoed = !passCrossPairVeto(cross); + fRegistry.fill(HIST("Pair/CrossPair/hVetoCounter"), vetoed ? 1.f : 0.f); + if (crosspair.cfgDoCrossPairCut.value && vetoed) { + continue; + } + } + + fillPairQA<1>(obs, g1, g2); + + if constexpr (IsMC) { + if (truthAxis < 0.f) { + continue; + } + fRegistry.fill(HIST("Pair/MC/hTruthTypeVsQinv"), qinv, truthAxis); + fRegistry.fill(HIST("Pair/MC/hTruthTypeVsKt"), obs.q.kt, truthAxis); + fRegistry.fill(HIST("Pair/MC/hTruthType_Qinv_DedupFlag"), truthAxis, qinv, dedupWouldRemove ? 1.f : 0.f); + if (truthType == mcutil::PairTruthType::FakeFake || truthType == mcutil::PairTruthType::TrueFake) { + fRegistry.fill(HIST("Pair/MC/hFakeSubtypeVsQinv"), qinv, static_cast(mcutil::classifyFakeSubtype(g1.mc, g2.mc))); + } + fillTypeObservables(truthType, obs); + { + const int c1 = v0LegClass(g1.legCounts), c2 = v0LegClass(g2.legCounts); + fRegistry.fill(HIST("Pair/MC/hV0Class1_V0Class2_Qinv_Type"), static_cast(std::min(c1, c2)), static_cast(std::max(c1, c2)), qinv, truthAxis); + fRegistry.fill(HIST("Pair/MC/hV0Class_IsTrue_Qinv"), static_cast(c1), g1.mc.isTruePhoton ? 1.f : 0.f, qinv); + fRegistry.fill(HIST("Pair/MC/hV0Class_IsTrue_Qinv"), static_cast(c2), g2.mc.isTruePhoton ? 1.f : 0.f, qinv); + } + for (const PhotonCand& p : {std::cref(g1), std::cref(g2)}) { + if (qinv < qaflags.cfgMaxQinvForSparses.value) { + fRegistry.fill(HIST("Pair/MC/hPsiPair_PhiV_Qinv_Type_IsTrue"), std::fabs(p.psipair), p.phiv, qinv, truthAxis, p.mc.isTruePhoton ? 1.f : 0.f); + } + const float rReco = p.rConv(); + const std::array mcLegIds{p.mc.mcPosId, p.mc.mcNegId}; + for (const auto& mcLegId : mcLegIds) { + const auto legPart = mcParticles.iteratorAt(mcLegId); + const float rMc = std::hypot(static_cast(legPart.vx()), static_cast(legPart.vy())); + fRegistry.fill(HIST("Pair/MC/hRreco_minus_Rmc"), rReco - rMc, rMc, p.mc.isTruePhoton ? 1.f : 0.f); + } + } + { + const std::array legIds = {g1.mc.mcPosId, g1.mc.mcNegId, g2.mc.mcPosId, g2.mc.mcNegId}; + std::array ev{}; + int nOwn = 0; + for (size_t il = 0; il < legIds.size(); ++il) { + ev[il] = static_cast(mcParticles.iteratorAt(legIds[il]).emmceventId()); + nOwn += (ev[il] == evOwn) ? 1 : 0; + } + const float pattern = (ev[0] != ev[1] || ev[2] != ev[3]) ? 2.f : (ev[0] != ev[2]) ? 1.f + : 0.f; // o2-linter: disable=magic-number (pattern codes, see booking) + fRegistry.fill(HIST("Pair/MC/hMcEventPattern_NOwn_Qinv_Type"), pattern, static_cast(nOwn), qinv, truthAxis); + } + if (qinv < qaflags.cfgMaxQinvForMCQA.value) { + fillAncestry(mcParticles, g1, g2, obs, truthAxis); + } + } + } + } + } + + template + void runCollisions(TCollisions const& collisions, TPhotons const& photons, TLegs const& /*legs*/, TMCParticles const& mcParticles) + { + std::vector cands; + for (const auto& collision : collisions) { + initCCDB(collision); + const std::array cent = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (cent[centralitySelection.cfgCentEstimator] < centralitySelection.cfgCentMin || + centralitySelection.cfgCentMax < cent[centralitySelection.cfgCentEstimator]) { + continue; + } + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); + fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) + fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // o2-linter: disable=magic-number (counter bin, as in photonhbt) + auto photonsColl = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + collectPhotons(photonsColl, mcParticles, collision.posZ(), cands); + fRegistry.fill(HIST("Photon/hNPhotonsPerEvent"), static_cast(std::min(cands.size(), static_cast(20)))); // o2-linter: disable=magic-number (last bin) + for (const auto& c : cands) { + fRegistry.fill(HIST("Photon/hPtEtaPhi"), c.fPt, c.fEta, c.fPhi); + const auto cls = static_cast(v0LegClass(c.legCounts)); + fRegistry.fill(HIST("Photon/hV0LegClass"), cls); + fRegistry.fill(HIST("Photon/hV0LegClass_vs_Pt"), cls, c.fPt); + fRegistry.fill(HIST("Photon/hV0LegClass_vs_R"), cls, std::min(c.rConv(), 99.9f)); // o2-linter: disable=magic-number (clamp to the axis) + fillAnalyticV0QA(c); + } + runDedupQA(cands); + if (cands.size() < 2) { // o2-linter: disable=magic-number (suplicate number) + continue; + } + runPairQa(collision, cands, mcParticles); + } + } + + void processReco(FilteredMyCollisions const& collisions, MyV0Photons const& v0photons, aod::V0Legs const& v0legs) + { + runCollisions(collisions, v0photons, v0legs, v0legs /* placeholder, unused in data */); + } + PROCESS_SWITCH(PairQCTask, processReco, "pair QC on reconstructed data", true); + + void processMC(FilteredMyMCCollisions const& collisions, MyV0Photons const& v0photons, MyMCV0Legs const& v0legs, + aod::EMMCParticles const& mcParticles, aod::EMMCEvents const& /*mcEvents*/) + { + runCollisions(collisions, v0photons, v0legs, mcParticles); + } + PROCESS_SWITCH(PairQCTask, processMC, "pair QC on MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/pcmQC.cxx b/PWGEM/PhotonMeson/Tasks/pcmQC.cxx index 9530c9401da..112a686abc9 100644 --- a/PWGEM/PhotonMeson/Tasks/pcmQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/pcmQC.cxx @@ -9,17 +9,41 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file MaterialBudget.cxx -/// \brief This code runs loop over v0 photons for PCM QC. -/// \author Daiki Sekihata, daiki.sekihata@cern.ch +/// \file pcmQC.cxx +/// \brief Quality, purity, material and reconstruction monitoring of the selected V0 photon candidates +/// +/// - processQC: kinematics, conversion-point and leg-track QA of the +/// reconstructed photons. Material-budget configurables +/// add conversion-point maps, restricted to selectable +/// detector regions for material studies. +/// - processQCML: the same QC with the ML-based photon selection (BDT +/// models from CCDB) instead of the classical V0 cuts +/// - processPCMQCMC: MC-truth of the QC observables for the purity +/// assessment, plus resolution assessment. +/// Configurables activate collision-association studies +/// and the same regional material-budget mode as in +/// processQC, there with per-region resolution on top. +/// - processPCMQCMCML: the same MC QC with the ML-based photon selection +/// - processGen: generator-level distributions as the denominator +/// reference (requires the binned generated-pT derived +/// data as input). +/// - processRecoQA: runs on AO2Ds, before any skimming: track-level QA +/// plus a truth-based check of the SVertexer candidate +/// building and deduplication, separating genuine +/// reconstruction quality from reconstruction artifacts. +/// +/// \author Daiki Sekihata and author Stefanie Mrozinski +#include "PWGEM/Dilepton/Utils/MCUtilities.h" #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/Core/V0PhotonCandidate.h" #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -27,24 +51,29 @@ #include #include #include -#include +#include #include #include #include +#include #include #include #include #include #include #include -#include #include +#include +#include +#include #include +#include // for the concept std::ranges::contiguous_range #include #include #include +#include #include using namespace o2; @@ -53,6 +82,8 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photon; +using namespace o2::aod::pwgem::photonmeson::utils::mcutil; +using namespace o2::aod::pwgem::dilepton::utils::mcutil; using MyCollisions = soa::Join; using MyCollision = MyCollisions::iterator; @@ -63,14 +94,24 @@ using MyV0Photon = MyV0Photons::iterator; using MyV0PhotonsML = soa::Join; using MyV0PhotonML = MyV0PhotonsML::iterator; +// MC Joins +using MyCollisionsMC = soa::Join; +using MyCollisionMC = MyCollisionsMC::iterator; + +using MyMCCollisions = soa::Join; +using MyMCCollision = MyMCCollisions::iterator; + +using MyMCV0Legs = soa::Join; +using MyMCV0Leg = MyMCV0Legs::iterator; + +// AO2D level (processRecoQA): tracks before any skimming, with their MC labels +using MyTracksMC = soa::Join; + struct PCMQC { Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; Configurable cfgCentMin{"cfgCentMin", 0, "min. centrality"}; Configurable cfgCentMax{"cfgCentMax", 999.f, "max. centrality"}; Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; EMPhotonEventCut fEMEventCut; @@ -96,59 +137,113 @@ struct PCMQC { } eventcuts; V0PhotonCut fV0PhotonCut; + // v0 photon cuts + struct : ConfigurableGroup { + std::string prefix = "v0cut_group"; + Configurable cfgRequireV0WithITSTPC{"cfgRequireV0WithITSTPC", false, "flag to select V0s with ITS-TPC matched tracks"}; + Configurable cfgRequireV0WithITSonly{"cfgRequireV0WithITSonly", false, "flag to select V0s with ITSonly tracks"}; + Configurable cfgRequireV0WithTPConly{"cfgRequireV0WithTPConly", false, "flag to select V0s with TPConly tracks"}; + Configurable cfgMinPtV0{"cfgMinPtV0", 0.1, "min pT for v0 photons at PV"}; + Configurable cfgMaxPtV0{"cfgMaxPtV0", 1e+10, "max pT for v0 photons at PV"}; + Configurable cfgMinEtaV0{"cfgMinEtaV0", -0.8, "min eta for v0 photons at PV"}; + Configurable cfgMaxEtaV0{"cfgMaxEtaV0", +0.8, "max eta for v0 photons at PV"}; + Configurable cfgMinV0Radius{"cfgMinV0Radius", 4.0, "min v0 radius"}; + Configurable cfgMaxV0Radius{"cfgMaxV0Radius", 90.0, "max v0 radius"}; + Configurable cfgMidLV0Radius{"cfgMidLV0Radius", -1.0, "middle low v0 radius for rejection if >0"}; + Configurable cfgMidHV0Radius{"cfgMidHV0Radius", -1.0, "middle high v0 radius for rejection if >0"}; + Configurable cfgMaxAlphaAP{"cfgMaxAlphaAP", 0.95, "max alpha for AP cut"}; + Configurable cfgMaxQtAP{"cfgMaxQtAP", 0.01, "max qT for AP cut"}; + Configurable cfgMinV0CosPA{"cfgMinV0CosPA", 0.999, "min V0 CosPA"}; + Configurable cfgMaxPCA{"cfgMaxPCA", 1.5, "max distance btween 2 legs"}; + Configurable cfgMaxChi2KF{"cfgMaxChi2KF", 1e+10, "max chi2/ndf with KF"}; + Configurable cfgRejectV0OnITSib{"cfgRejectV0OnITSib", true, "flag to reject V0s on ITSib"}; + } v0cuts; + + // single track cuts + struct : ConfigurableGroup { + std::string prefix = "trackcut_group"; + Configurable cfgMinNClustersTPC{"cfgMinNClustersTPC", 0, "min ncluster tpc"}; + Configurable cfgMinNCrossedRows{"cfgMinNCrossedRows", 40, "min crossed rows"}; + Configurable cfgMaxFracSharedClustersTPC{"cfgMaxFracSharedClustersTPC", 999.f, "max fraction of shared clusters in TPC"}; + Configurable cfgMaxChi2TPC{"cfgMaxChi2TPC", 4.0, "max chi2/NclsTPC"}; + Configurable cfgMaxChi2ITS{"cfgMaxChi2ITS", 36.0, "max chi2/NclsITS"}; + Configurable cfgMinTPCNsigmaEl{"cfgMinTPCNsigmaEl", -3.0, "min. TPC n sigma for electron"}; + Configurable cfgMaxTPCNsigmaEl{"cfgMaxTPCNsigmaEl", +3.0, "max. TPC n sigma for electron"}; + Configurable cfgDisableITSonlyTracks{"cfgDisableITSonlyTracks", false, "disable ITSonly tracks in V0 legs"}; + Configurable cfgDisableTPConlyTracks{"cfgDisableTPConlyTracks", false, "disable TPConly tracks in V0 legs"}; + Configurable cfgDoDEdxPostCalibration{"cfgDoDEdxPostCalibration", false, "flag to enable dEdx post calibration"}; + } trackcuts; + + // pT-dependent loss QA + struct : ConfigurableGroup { + std::string prefix = "qaSettings_group"; + Configurable cfgDoPtDependentLossQA{"cfgDoPtDependentLossQA", false, "fill the cut variables vs. pT before AND after the V0 selection - the after/before ratio shows which candidates the cuts remove"}; + Configurable cfgDoKappaAnalysis{"cfgDoKappaAnalysis", false, "fill histograms with kappa vs pT vs cent."}; + } qaSettingsGroup; + + // PCM ML inference + struct : ConfigurableGroup { + std::string prefix = "mlcut_group"; + Configurable cfgApplyPCMMl{"cfgApplyPCMMl", false, "Flag to apply ML selections"}; + Configurable cfgUse2DBinning{"cfgUse2DBinning", false, "Flag to enable/disable 2D binning for ML application"}; + Configurable cfgLoadModelsFromCCDB{"cfgLoadModelsFromCCDB", true, "Flag to enable or disable the loading of models from CCDB"}; + Configurable cfgTimestampCCDB{"cfgTimestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable cfgNClassesPCMMl{"cfgNClassesPCMMl", static_cast(o2::analysis::em_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable> cfgCutDirPCMMl{"cfgCutDirPCMMl", std::vector{o2::analysis::em_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cfgNamesInputFeatures{"cfgNamesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + Configurable> cfgModelPathsCCDB{"cfgModelPathsCCDB", std::vector{"path_ccdb/BDT_PCM/"}, "Paths of models on CCDB"}; + Configurable> cfgOnnxFileNames{"cfgOnnxFileNames", std::vector{"ModelHandler_onnx_PCM.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable> cfgLabelsBinsPCMMl{"cfgLabelsBinsPCMMl", std::vector{"bin 0", "bin 1"}, "Labels for bins"}; + Configurable> cfgLabelsCutScoresPCMMl{"cfgLabelsCutScoresPCMMl", std::vector{o2::analysis::em_cuts_ml::labelsCutScore}, "Labels for cut scores"}; + Configurable> cfgBinsPtPCMMl{"cfgBinsPtPCMMl", std::vector{0.0, +1e+10}, "pT bin limits for ML application"}; + Configurable> cfgBinsCentPCMMl{"cfgBinsCentPCMMl", std::vector{o2::analysis::em_cuts_ml::vecBinsCent}, "Centrality bin limits for ML application"}; + Configurable> cfgCutsPCMMlFlat{"cfgCutsPCMMlFlat", {0.5}, "Flattened ML cuts: [bin0_score0, bin0_score1, ..., binN_scoreM]"}; + } mlcuts; + + struct : ConfigurableGroup { + std::string prefix = "mcAnalysisModeSettings_group"; + Configurable cfgDoDetailedResolution{"cfgDoDetailedResolution", false, "purpose of the configurable is to choose if we have more THnSparses included in order to see if some regions have better or worse resolution"}; + Configurable cfgDoCollisionAssociationQA{"cfgDoCollisionAssociationQA", false, "include the QA Plots which give an idea how photon distribution changes with different subclasses or what the effect of wrong collision association is"}; + Configurable cfgRequireTrueAssociation{"cfgRequireTrueAssociation", false, "flag to require true mc collision association for the truth-classified QC observables"}; + } mcAnalysisModeSettings; + + struct : ConfigurableGroup { + std::string prefix = "materialBudgetSettings_group"; + Configurable cfgDoMaterialDistribution{"cfgDoMaterialDistribution", false, "add THnSparses of the conversion-point distribution for the material budget study"}; + Configurable cfgDoWiresDetail{"cfgDoWiresDetail", false, "add detailed conversion-point histograms around the wire region"}; + Configurable cfgDoMFTDetail{"cfgDoMFTDetail", false, "add detailed conversion-point histograms around the MFT region"}; + Configurable cfgDoITSDetail{"cfgDoITSDetail", false, "add detailed conversion-point histograms around the ITS layers"}; + Configurable cfgDoTPCInnerBarrelDetail{"cfgDoTPCInnerBarrelDetail", false, "add detailed conversion-point histograms around the TPC inner barrel"}; + } materialBudgetSettingsGroup; + + struct : ConfigurableGroup { + std::string prefix = "genSettings_group"; + Configurable cfgMaxRGen{"cfgMaxRGen", 90.f, "maximum conversion radius for generated photons (fiducial cut, matches the reco acceptance)"}; + Configurable cfgMarginZMC{"cfgMarginZMC", 7.0, "margin for z cut in cm for MC"}; + } genSettingsGroup; + struct : ConfigurableGroup { - std::string prefix = "pcmcut_group"; - Configurable cfg_require_v0_with_itstpc{"cfg_require_v0_with_itstpc", false, "flag to select V0s with ITS-TPC matched tracks"}; - Configurable cfg_require_v0_with_itsonly{"cfg_require_v0_with_itsonly", false, "flag to select V0s with ITSonly tracks"}; - Configurable cfg_require_v0_with_tpconly{"cfg_require_v0_with_tpconly", false, "flag to select V0s with TPConly tracks"}; - Configurable cfg_min_pt_v0{"cfg_min_pt_v0", 0.1, "min pT for v0 photons at PV"}; - Configurable cfg_max_pt_v0{"cfg_max_pt_v0", 1e+10, "max pT for v0 photons at PV"}; - Configurable cfg_min_eta_v0{"cfg_min_eta_v0", -0.8, "min eta for v0 photons at PV"}; - Configurable cfg_max_eta_v0{"cfg_max_eta_v0", +0.8, "max eta for v0 photons at PV"}; - Configurable cfg_min_v0radius{"cfg_min_v0radius", 4.0, "min v0 radius"}; - Configurable cfg_max_v0radius{"cfg_max_v0radius", 90.0, "max v0 radius"}; - Configurable cfg_max_alpha_ap{"cfg_max_alpha_ap", 0.95, "max alpha for AP cut"}; - Configurable cfg_max_qt_ap{"cfg_max_qt_ap", 0.01, "max qT for AP cut"}; - Configurable cfg_min_cospa{"cfg_min_cospa", 0.999, "min V0 CosPA"}; - Configurable cfg_max_pca{"cfg_max_pca", 1.5, "max distance btween 2 legs"}; - Configurable cfg_max_chi2kf{"cfg_max_chi2kf", 1e+10, "max chi2/ndf with KF"}; - Configurable cfg_reject_v0_on_itsib{"cfg_reject_v0_on_itsib", true, "flag to reject V0s on ITSib"}; - Configurable cfg_min_ncluster_tpc{"cfg_min_ncluster_tpc", 0, "min ncluster tpc"}; - Configurable cfg_min_ncrossedrows{"cfg_min_ncrossedrows", 40, "min ncrossed rows"}; - Configurable cfg_max_frac_shared_clusters_tpc{"cfg_max_frac_shared_clusters_tpc", 999.f, "max fraction of shared clusters in TPC"}; - Configurable cfg_max_chi2tpc{"cfg_max_chi2tpc", 4.0, "max chi2/NclsTPC"}; - Configurable cfg_max_chi2its{"cfg_max_chi2its", 36.0, "max chi2/NclsITS"}; - Configurable cfg_min_TPCNsigmaEl{"cfg_min_TPCNsigmaEl", -3.0, "min. TPC n sigma for electron"}; - Configurable cfg_max_TPCNsigmaEl{"cfg_max_TPCNsigmaEl", +3.0, "max. TPC n sigma for electron"}; - Configurable cfg_disable_itsonly_track{"cfg_disable_itsonly_track", false, "flag to disable ITSonly tracks"}; - Configurable cfg_disable_tpconly_track{"cfg_disable_tpconly_track", false, "flag to disable TPConly tracks"}; - Configurable cfg_dEdx_postcalibration{"cfg_dEdx_postcalibration", false, "flag to enable dEdx post calibration"}; - // for ML cuts - Configurable cfg_apply_ml_cuts{"cfg_apply_ml", false, "flag to apply ML cut"}; - Configurable cfg_use_2d_binning{"cfg_use_2d_binning", false, "flag to use 2D binning (pT, cent)"}; - Configurable cfg_load_ml_models_from_ccdb{"cfg_load_ml_models_from_ccdb", true, "flag to load ML models from CCDB"}; - Configurable cfg_timestamp_ccdb{"cfg_timestamp_ccdb", -1, "timestamp for CCDB"}; - Configurable cfg_nclasses_ml{"cfg_nclasses_ml", static_cast(o2::analysis::em_cuts_ml::NCutScores), "number of classes for ML"}; - Configurable> cfg_cut_dir_ml{"cfg_cut_dir_ml", std::vector{o2::analysis::em_cuts_ml::vecCutDir}, "cut direction for ML"}; - Configurable> cfg_input_feature_names{"cfg_input_feature_names", std::vector{"feature1", "feature2"}, "input feature names for ML models"}; - Configurable> cfg_model_paths_ccdb{"cfg_model_paths_ccdb", std::vector{"path_ccdb/BDT_PCM/"}, "CCDB paths for ML models"}; - Configurable> cfg_onnx_file_names{"cfg_onnx_file_names", std::vector{"ModelHandler_onnx_PCM.onnx"}, "ONNX file names for ML models"}; - Configurable> cfg_labels_bins_ml{"cfg_labels_bins_ml", std::vector{"bin 0", "bin 1"}, "Labels for bins"}; - Configurable> cfg_labels_cut_scores_ml{"cfg_labels_cut_scores_ml", std::vector{o2::analysis::em_cuts_ml::labelsCutScore}, "Labels for cut scores"}; - Configurable> cfg_bins_pt_ml{"cfg_bins_pt_ml", std::vector{0.0, +1e+10}, "pT bin limits for ML application"}; - Configurable> cfg_bins_cent_ml{"cfg_bins_cent_ml", std::vector{o2::analysis::em_cuts_ml::vecBinsCent}, "centrality bins for ML"}; - Configurable> cfg_cuts_ml_flat{"cfg_cuts_ml_flat", {0.5}, "Flattened ML cuts: [bin0_score0, bin0_score1, ..., binN_scoreM]"}; - } pcmcuts; + std::string prefix = "recoQASettings_group"; + Configurable cfgDoDetailedTrackQA{"cfgDoDetailedTrackQA", false, "add track-quality histograms of surviving vs. lost conversion legs"}; + Configurable cfgRMinGen{"cfgRMinGen", 1.f, "min true conversion radius (cm)"}; + Configurable cfgRMaxGen{"cfgRMaxGen", 90.f, "max true conversion radius (cm)"}; + Configurable cfgLegEtaMax{"cfgLegEtaMax", 0.9f, "max |eta| of true MC legs"}; + Configurable cfgPhotonEtaMax{"cfgPhotonEtaMax", 0.9f, "max |eta| of true MC photon"}; + Configurable cfgPhotonPtMin{"cfgPhotonPtMin", 0.1f, "min pT of true MC photon"}; + } recoQASettingsGroup; o2::ccdb::CcdbApi ccdbApi; - o2::framework::Service ccdb; - int mRunNumber; - float d_bz; - static constexpr std::string_view event_types[2] = {"before/", "after/"}; + o2::framework::Service ccdb{}; + int mRunNumber = 0; + float d_bz = 0; + static constexpr std::array event_types = {"before/", "after/"}; + static constexpr std::array mcphoton_types = {"primary/", "fromWD/", "fromHS/", "fromPi0Dalitz/", "fromEtaDalitz/"}; HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - void init(InitContext&) + void init(InitContext& context) { + + (void)context; addhistograms(); DefineEMEventCut(); DefinePCMCut(); @@ -162,44 +257,35 @@ struct PCMQC { ccdb->setFatalWhenNull(false); } - template + template void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { return; } + mRunNumber = collision.runNumber(); // In case override, don't proceed, please - no CCDB access required - if (d_bz_input > -990) { + if (d_bz_input > -990) { // o2-linter: disable=magic-number (override value) d_bz = d_bz_input; o2::parameters::GRPMagField grpmag; - if (std::fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + if (std::fabs(d_bz) > 1e-5) { // o2-linter: disable=magic-number (override value) + grpmag.setL3Current(30000.f / (d_bz / 5.0f)); // o2-linter: disable=magic-number (override value) } - mRunNumber = collision.runNumber(); return; } - auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + // Fetch magnetic field from ccdb for current collision + // d_bz = collision.grpMagField().getNominalL3Field(); + // LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << d_bz << " kZG"; + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", collision.runNumber()); + if (grpmag == nullptr) { + LOGF(fatal, "Could not retrieve GRPMagField for run %d", collision.runNumber()); + return; } + d_bz = grpmag->getNominalL3Field(); + LOG(info) << "Retrieved GRP for run " << collision.runNumber() << " with magnetic field of " << d_bz << " kZG"; fV0PhotonCut.SetD_Bz(d_bz); - mRunNumber = collision.runNumber(); } void addhistograms() @@ -240,6 +326,11 @@ struct PCMQC { fRegistry.add("V0/hDCAxyz", "DCA to PV;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -5.f, +5.f}, {200, -5.f, +5.f}}, false); fRegistry.add("V0/hDCAz_Pt", "DCA_{z} to PV vs. p_{T};DCA_{z} (cm);p_{T} (GeV/c)", kTH2F, {{200, -5.f, +5.f}, {2000, 0.0f, 20}}, false); fRegistry.add("V0/hAPplot", "AP plot;#alpha;q_{T} (GeV/c)", kTH2F, {{200, -1.0f, +1.0f}, {250, 0.0f, 0.25f}}, false); + fRegistry.add("V0/hRxyVsPt", "conversion radius vs. pT;p_{T,#gamma} (GeV/c);R_{xy} (cm)", kTH2F, {{100, 0, 10}, {200, 0, 100}}, false); + fRegistry.add("V0/hEtaVsPt", "#eta vs. pT;p_{T,#gamma} (GeV/c);#eta", kTH2F, {{100, 0, 10}, {200, -1.0f, 1.0f}}, false); + fRegistry.add("V0/hPhiVsPt", "#varphi vs. pT;p_{T,#gamma} (GeV/c);#varphi (rad.)", kTH2F, {{100, 0, 10}, {90, 0, o2::constants::math::TwoPI}}, false); + fRegistry.add("V0/hsAlphaQtPt", "Armenteros vs. pT;#alpha;q_{T} (GeV/c);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{100, -1.0f, +1.0f}, {125, 0.0f, 0.25f}, {100, 0, 10}}, false); + fRegistry.add("V0/hPsiPairVsPt", "#psi_{pair} vs. pT;p_{T,#gamma} (GeV/c);#psi_{pair} (rad.)", kTH2F, {{100, 0, 10}, {200, -0.5f, +0.5f}}, false); fRegistry.add("V0/hMassGamma", "hMassGamma;R_{xy} (cm);m_{ee} (GeV/c^{2})", kTH2F, {{200, 0.0f, 100.0f}, {100, 0.0f, 0.1f}}, false); fRegistry.add("V0/hKFChi2vsM", "KF chi2 vs. m_{ee};m_{ee} (GeV/c^{2});KF chi2/NDF", kTH2F, {{100, 0.0f, 0.1f}, {100, 0.f, 100.0f}}, false); fRegistry.add("V0/hKFChi2vsR", "KF chi2 vs. conversion point in XY;R_{xy} (cm);KF chi2/NDF", kTH2F, {{200, 0.0f, 100.0f}, {100, 0.f, 100.0f}}, false); @@ -249,12 +340,12 @@ struct PCMQC { fRegistry.add("V0/hsConvPoint", "photon conversion point;r_{xy} (cm);#varphi (rad.);#eta;", kTHnSparseF, {{100, 0.0f, 100}, {90, 0, o2::constants::math::TwoPI}, {80, -2, +2}}, false); fRegistry.add("V0/hNgamma", "Number of #gamma candidates per collision", kTH1F, {{101, -0.5f, 100.5f}}); - if (pcmcuts.cfg_apply_ml_cuts) { - if (pcmcuts.cfg_nclasses_ml == 2) { + if (mlcuts.cfgApplyPCMMl) { + if (mlcuts.cfgNClassesPCMMl == 2) { // o2-linter: disable=magic-number (BDT class) fRegistry.add("V0/hBDTBackgroundScoreVsPt", "BDT background score vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); fRegistry.add("V0/hBDTSignalScoreVsPt", "BDT signal score vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); fRegistry.add("V0/hPhiVPsi", "#varphi vs. #psi angle;#psi (rad.); #varphi (rad.)", kTH2F, {{200, -o2::constants::math::PI, o2::constants::math::PI}, {200, 0, o2::constants::math::TwoPI}}, false); - } else if (pcmcuts.cfg_nclasses_ml == 3) { + } else if (mlcuts.cfgNClassesPCMMl == 3) { // o2-linter: disable=magic-number (BDT class) fRegistry.add("V0/hBDTBackgroundScoreVsPt", "BDT background score vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); fRegistry.add("V0/hBDTPrimaryPhotonScoreVsPt", "BDT primary photon score vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); fRegistry.add("V0/hBDTSecondaryPhotonScoreVsPt", "BDT secondary photon score vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{1000, 0.0f, 20.0f}, {1000, 0.0f, 1.0f}}}); @@ -276,6 +367,7 @@ struct PCMQC { fRegistry.add("V0Leg/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); fRegistry.add("V0Leg/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); fRegistry.add("V0Leg/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("V0Leg/hTPCNsigmaElVsEta", "TPC n sigma el vs. eta;#eta;n #sigma_{e}^{TPC}", kTH2F, {{40, -1.0f, 1.0f}, {100, -5, +5}}, false); // eta-dependence = dE/dx calibration check (TOF/ITS n#sigma not stored in the V0Leg skim) fRegistry.add("V0Leg/hTPCNcr2Nf", "TPC Ncr/Nfindable", kTH1F, {{200, 0, 2}}, false); fRegistry.add("V0Leg/hTPCNcls2Nf", "TPC Ncls/Nfindable", kTH1F, {{200, 0, 2}}, false); fRegistry.add("V0Leg/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); @@ -283,13 +375,206 @@ struct PCMQC { fRegistry.add("V0Leg/hChi2ITS", "chi2/number of ITS clusters", kTH1F, {{100, 0, 10}}, false); fRegistry.add("V0Leg/hITSClusterMap", "ITS cluster map", kTH1F, {{128, -0.5, 127.5}}, false); fRegistry.add("V0Leg/hMeanClusterSizeITS", "mean cluster size ITS; on ITS #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {160, 0, 16}}, false); - if (pcmcuts.cfg_dEdx_postcalibration) { + if (trackcuts.cfgDoDEdxPostCalibration) { fRegistry.add("V0Leg/hPvsConvPointvsTPCNsigmaElvsEta_Pos", "momentum of pos leg vs. conversion point of V0 vs. TPC n sigma pos vs. eta of pos leg; p (GeV/c); r_{xy} (cm); n #sigma_{e}^{TPC}; #eta", kTHnSparseF, {{200, 0, 20}, {100, 0, 100}, {500, -5, 5}, {200, -1, +1}}, false); fRegistry.add("V0Leg/hPvsConvPointvsTPCNsigmaElvsEta_Ele", "momentum of neg leg vs. conversion point of V0 vs. TPC n sigma el vs. eta of neg leg; p (GeV/c); r_{xy} (cm); n #sigma_{e}^{TPC}; #eta", kTHnSparseF, {{200, 0, 20}, {100, 0, 100}, {500, -5, 5}, {200, -1, +1}}, false); } - // fRegistry.add("V0Leg/hXY", "X vs. Y;X (cm);Y (cm)", kTH2F, {{100, 0, 100}, {80, -20, 20}}, false); - // fRegistry.add("V0Leg/hZX", "Z vs. X;Z (cm);X (cm)", kTH2F, {{200, -100, 100}, {100, 0, 100}}, false); - // fRegistry.add("V0Leg/hZY", "Z vs. Y;Z (cm);Y (cm)", kTH2F, {{200, -100, 100}, {80, -20, 20}}, false); + + if (materialBudgetSettingsGroup.cfgDoMaterialDistribution) { + fRegistry.add("MaterialBudget/hs", "conversion point;z (cm);r_{xy} (cm);#eta;#varphi (rad.);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{200, -100, 100}, {100, 0, 100}, {80, -2, +2}, {90, 0, o2::constants::math::TwoPI}, {100, 0, 10}}, false); + } + if (materialBudgetSettingsGroup.cfgDoWiresDetail) { + fRegistry.add("MaterialBudget/Wires/hsLeft", "conversion point near left wire;x (cm);y (cm);z (cm);#varphi (rad.);r_{xy} (cm);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{200, -20, 20}, {200, -20, 20}, {80, -20, 20}, {40, 3.15, 3.4}, {80, 0, 20}, {100, 0, 10}}, false); + fRegistry.add("MaterialBudget/Wires/hsRight", "conversion point near right wire;x (cm);y (cm);z (cm);#varphi (rad.);r_{xy} (cm);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{200, -20, 20}, {200, -20, 20}, {80, -20, 20}, {40, 6.00, 6.15}, {80, 0, 20}, {100, 0, 10}}, false); + } + if (materialBudgetSettingsGroup.cfgDoITSDetail) { + fRegistry.add("MaterialBudget/ITS/hs", "conversion point around the ITS layers;x (cm);y (cm);z (cm);#varphi (rad.);r_{xy} (cm);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{160, -40, 40}, {160, -40, 40}, {80, -40, 40}, {90, 0, o2::constants::math::TwoPI}, {150, 0, 60}, {100, 0, 10}}, false); + } + if (materialBudgetSettingsGroup.cfgDoMFTDetail) { + fRegistry.add("MaterialBudget/MFT/hs", "conversion point around the MFT region;x (cm);y (cm);z (cm);#varphi (rad.);r_{xy} (cm);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{160, -80, 80}, {160, -80, 80}, {40, -40, 40}, {90, 0, o2::constants::math::TwoPI}, {40, 40, 60}, {100, 0, 10}}, false); + } + if (materialBudgetSettingsGroup.cfgDoTPCInnerBarrelDetail) { + fRegistry.add("MaterialBudget/TPC/hs", "conversion point around the TPC inner barrel;x (cm);y (cm);z (cm);#varphi (rad.);r_{xy} (cm);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{160, -90, 90}, {160, -90, 90}, {80, -40, 40}, {90, 0, o2::constants::math::TwoPI}, {80, 60, 80}, {100, 0, 10}}, false); + } + + if (doprocessGen) { + std::vector ptbins; + ptbins.reserve(72); + for (int i = 0; i < 2; i++) { // o2-linter: disable=magic-number (binning) + ptbins.emplace_back(0.05 * (i - 0) + 0.0); // from 0 to 0.05 GeV/c, every 0.05 GeV/c + } + for (int i = 2; i < 51; i++) { // o2-linter: disable=magic-number (binning) + ptbins.emplace_back(0.1 * (i - 2) + 0.1); // from 0.1 to 4.9 GeV/c, every 0.1 GeV/c + } + for (int i = 51; i < 61; i++) { // o2-linter: disable=magic-number (binning) + ptbins.emplace_back(0.5 * (i - 51) + 5.0); // from 5 to 9.5 GeV/c, every 0.5 GeV/c + } + for (int i = 61; i < 72; i++) { // o2-linter: disable=magic-number (binning) + ptbins.emplace_back(1.0 * (i - 61) + 10.0); // from 10 to 20 GeV/c, every 1 GeV/c + } + const AxisSpec axisPtGen{ptbins, "p_{T,#gamma} (GeV/c)"}; + const AxisSpec axisRapidityGen{{0.0, +0.8, +0.9}, "rapidity |y_{#gamma}|"}; + + fRegistry.add("Generated/hPt", "pT;p_{T} (GeV/c)", kTH1D, {axisPtGen}, true); + fRegistry.add("Generated/hPtY", "Generated info", kTH2D, {axisPtGen, axisRapidityGen}, true); + fRegistry.add("Generated/hPt_ConversionPhoton", "converted photon pT;p_{T} (GeV/c)", kTH1D, {axisPtGen}, true); + fRegistry.add("Generated/hY_ConversionPhoton", "converted photon y;rapidity y", kTH1F, {{40, -2.0f, 2.0f}}, true); + fRegistry.add("Generated/hPhi_ConversionPhoton", "converted photon #varphi;#varphi (rad.)", kTH1F, {{180, 0, o2::constants::math::TwoPI}}, true); + fRegistry.add("Generated/hXY", "conversion point in XY MC;V_{x} (cm);V_{y} (cm)", kTH2F, {{800, -100.0f, 100.0f}, {800, -100.0f, 100.0f}}, true); + fRegistry.add("Generated/hRZ", "conversion point in RZ MC;V_{z} (cm);R_{xy} (cm)", kTH2F, {{400, -100.0f, 100.0f}, {400, 0.f, 100.0f}}, true); + fRegistry.add("Generated/hRPhi", "conversion point of #varphi vs. R_{xy} MC;#varphi (rad.);R_{xy} (cm);N_{e}", kTH2F, {{360, 0.0f, o2::constants::math::TwoPI}, {400, 0, 100}}, true); + fRegistry.add("Generated/hsConvPoint", "photon conversion point;r_{xy} (cm);#varphi (rad.);#eta;", kTHnSparseF, {{100, 0.0f, 100}, {90, 0, o2::constants::math::TwoPI}, {80, -2, +2}}, true); + fRegistry.add("Generated/hR_ConversionPhoton_wideR", "converted photon R_{xy} up to the EMCal, before fiducial cuts;R_{xy} (cm);N_{#gamma}", kTH1F, {{250, 0.0f, 500.0f}}, true); + fRegistry.add("Generated/hRZ_wideR", "conversion point in RZ up to the EMCal, before fiducial cuts;V_{z} (cm);R_{xy} (cm)", kTH2F, {{200, -400.0f, 400.0f}, {250, 0.0f, 500.0f}}, true); + fRegistry.add("Generated/hsRZPhi_wideR", "conversion point up to the EMCal, before fiducial cuts;V_{z} (cm);#varphi (rad.);R_{xy} (cm)", kTHnSparseF, {{200, -400.0f, 400.0f}, {360, 0.0f, o2::constants::math::TwoPI}, {250, 0.0f, 500.0f}}, true); + if (mcAnalysisModeSettings.cfgDoDetailedResolution) { + fRegistry.add("Generated/hPtEtaPhi", "Photon pt vs eta, and phi;p_{T, gen} (GeV/c);#eta;#phi", kTHnSparseF, {{200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + } + } + + if (doprocessPCMQCMC || doprocessPCMQCMCML) { + fRegistry.add("V0/primary/hPt", "pT;p_{T,#gamma} (GeV/c)", kTH1F, {{2000, 0.0f, 20}}, false); + fRegistry.add("V0/primary/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{90, 0, o2::constants::math::TwoPI}, {200, -1.0f, 1.0f}}, false); + fRegistry.add("V0/primary/hXY", "conversion point in XY;V_{x} (cm);V_{y} (cm)", kTH2F, {{400, -100.0f, 100.0f}, {400, -100.0f, 100.0f}}, false); + fRegistry.add("V0/primary/hRZ", "conversion point in RZ;Z (cm);R_{xy} (cm)", kTH2F, {{200, -100, 100}, {200, 0.0f, 100.0f}}, false); + fRegistry.add("V0/primary/hCosPA", "V0CosPA;cosine pointing angle in 3D", kTH1F, {{100, 0.99f, 1.0f}}, false); + fRegistry.add("V0/primary/hCosPAXY", "V0CosPA;cosine pointing angle in XY", kTH1F, {{100, 0.99f, 1.0f}}, false); + fRegistry.add("V0/primary/hCosPARZ", "V0CosPA;cosine pointing angle in RZ", kTH1F, {{100, 0.99f, 1.0f}}, false); + fRegistry.add("V0/primary/hPCA", "distance between 2 legs;PCA (cm)", kTH1F, {{500, 0.0f, 5.0f}}, false); + fRegistry.add("V0/primary/hDCAxyz", "DCA to PV;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -5.f, +5.f}, {200, -5.f, +5.f}}, false); + fRegistry.add("V0/primary/hDCAz_Pt", "DCA_{z} to PV vs. p_{T};DCA_{z} (cm);p_{T} (GeV/c)", kTH2F, {{200, -5.f, +5.f}, {2000, 0.0f, 20}}, false); + fRegistry.add("V0/primary/hAPplot", "AP plot;#alpha;q_{T} (GeV/c)", kTH2F, {{200, -1.0f, +1.0f}, {250, 0.0f, 0.25f}}, false); + fRegistry.add("V0/primary/hRxyVsPt", "conversion radius vs. pT;p_{T,#gamma} (GeV/c);R_{xy} (cm)", kTH2F, {{100, 0, 10}, {200, 0, 100}}, false); + fRegistry.add("V0/primary/hEtaVsPt", "#eta vs. pT;p_{T,#gamma} (GeV/c);#eta", kTH2F, {{100, 0, 10}, {200, -1.0f, 1.0f}}, false); + fRegistry.add("V0/primary/hPhiVsPt", "#varphi vs. pT;p_{T,#gamma} (GeV/c);#varphi (rad.)", kTH2F, {{100, 0, 10}, {90, 0, o2::constants::math::TwoPI}}, false); + fRegistry.add("V0/primary/hsAlphaQtPt", "Armenteros vs. pT;#alpha;q_{T} (GeV/c);p_{T,#gamma} (GeV/c)", kTHnSparseF, {{100, -1.0f, +1.0f}, {125, 0.0f, 0.25f}, {100, 0, 10}}, false); + fRegistry.add("V0/primary/hPsiPairVsPt", "#psi_{pair} vs. pT;p_{T,#gamma} (GeV/c);#psi_{pair} (rad.)", kTH2F, {{100, 0, 10}, {200, -0.5f, +0.5f}}, false); + fRegistry.add("V0/primary/hMassGamma", "hMassGamma;R_{xy} (cm);m_{ee} (GeV/c^{2})", kTH2F, {{200, 0.0f, 100.0f}, {100, 0.0f, 0.1f}}, false); + fRegistry.add("V0/primary/hKFChi2vsM", "KF chi2 vs. m_{ee};m_{ee} (GeV/c^{2});KF chi2/NDF", kTH2F, {{100, 0.0f, 0.1f}, {100, 0.f, 100.0f}}, false); + fRegistry.add("V0/primary/hKFChi2vsR", "KF chi2 vs. conversion point in XY;R_{xy} (cm);KF chi2/NDF", kTH2F, {{200, 0.0f, 100.0f}, {100, 0.f, 100.0f}}, false); + fRegistry.add("V0/primary/hKFChi2vsX", "KF chi2 vs. conversion point in X;X (cm);KF chi2/NDF", kTH2F, {{200, -100.0f, 100.0f}, {100, 0.f, 100.0f}}, false); + fRegistry.add("V0/primary/hKFChi2vsY", "KF chi2 vs. conversion point in Y;Y (cm);KF chi2/NDF", kTH2F, {{200, -100.0f, 100.0f}, {100, 0.f, 100.0f}}, false); + fRegistry.add("V0/primary/hKFChi2vsZ", "KF chi2 vs. conversion point in Z;Z (cm);KF chi2/NDF", kTH2F, {{200, -100.0f, 100.0f}, {100, 0.f, 100.0f}}, false); + fRegistry.add("V0/primary/hNgamma", "Number of true #gamma per collision;N_{#gamma} per event;Number of events", kTH1F, {{101, -0.5f, 100.5f}}); + fRegistry.add("V0/primary/hConvPoint_diffX", "conversion point diff X MC;X_{MC} (cm);X_{rec} - X_{MC} (cm)", kTH2F, {{200, -100, +100}, {100, -50.0f, 50.0f}}, true); + fRegistry.add("V0/primary/hConvPoint_diffY", "conversion point diff Y MC;Y_{MC} (cm);Y_{rec} - Y_{MC} (cm)", kTH2F, {{200, -100, +100}, {100, -50.0f, 50.0f}}, true); + fRegistry.add("V0/primary/hConvPoint_diffZ", "conversion point diff Z MC;Z_{MC} (cm);Z_{rec} - Z_{MC} (cm)", kTH2F, {{200, -100, +100}, {100, -50.0f, 50.0f}}, true); + fRegistry.add("V0/primary/hPtGen_DeltaPtOverPtGen", "photon p_{T} resolution;p_{T}^{gen} (GeV/c);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", kTH2F, {{1000, 0, 10}, {200, -1.0f, 1.0f}}, true); + fRegistry.add("V0/primary/hPtGen_DeltaEta", "photon #eta resolution;p_{T}^{gen} (GeV/c);#eta^{rec} - #eta^{gen}", kTH2F, {{200, 0, 20}, {199, -0.099, 0.099}}, true); + fRegistry.add("V0/primary/hPtGen_DeltaPhi", "photon #varphi resolution;p_{T}^{gen} (GeV/c);#varphi^{rec} - #varphi^{gen} (rad.)", kTH2F, {{200, 0, 20}, {199, -0.099, 0.099}}, true); + fRegistry.add("V0/primary/hRxyGen_DeltaPtOverPtGen", "photon p_{T} resolution; R_{xy}^{gen} (cm);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", kTH2F, {{100, 0, 100}, {200, -1.0f, 1.0f}}, true); + fRegistry.add("V0/primary/hRxyGen_DeltaEta", "photon #eta resolution;R_{xy}^{gen} (cm);#eta^{rec} - #eta^{gen}", kTH2F, {{100, 0, 100}, {100, -0.5f, 0.5f}}, true); + fRegistry.add("V0/primary/hRxyGen_DeltaPhi", "photon #varphi resolution;R_{xy}^{gen} (cm);#varphi^{rec} - #varphi^{gen} (rad.)", kTH2F, {{100, 0, 100}, {100, -0.5f, 0.5f}}, true); + fRegistry.add("V0/primary/hRxyGen_DeltaR", "photon R_{xy} resolution;R_{xy}^{gen} (cm);R_{xy}^{rec} - R_{xy}^{gen} (cm)", kTH2F, {{100, 0, 100}, {100, 0, 100}}, true); + fRegistry.add("V0/primary/hXY_MC", "X vs. Y of true photon conversion point.;X (cm);Y (cm)", kTH2F, {{400, -100.0f, +100}, {400, -100, +100}}, true); + fRegistry.add("V0/primary/hRZ_MC", "R vs. Z of true photon conversion point;Z (cm);R_{xy} (cm)", kTH2F, {{200, -100.0f, +100}, {200, 0, 100}}, true); + fRegistry.add("V0/primary/hsConvPoint", "photon conversion point;r_{xy} (cm);#varphi (rad.);#eta;", kTHnSparseF, {{100, 0.0f, 100}, {90, 0, o2::constants::math::TwoPI}, {80, -2, +2}}, false); + if (mcAnalysisModeSettings.cfgDoDetailedResolution) { + fRegistry.add("V0/primary/hEtaPhiResol", "Photon eta-phi resolution;p_{T} (GeV/c);#eta-diff;#phi-diff", kTH3F, {{200, 0., 20.}, {99, -0.049, 0.049}, {99, -0.049, 0.049}}, false); + fRegistry.add("V0/primary/hPtResolPtEtaPhi", "Photon resolution vs. pt, eta, and phi;p_{T_rec} - p_{T true} / p_{T true};p_{T} (GeV/c);#eta;#phi", kTHnSparseF, {{199, -0.995, 0.995}, {200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + fRegistry.add("V0/primary/hMomResolPtEtaPhi", "Photon momentum resolution vs. p, eta, and phi;p_{rec} - p_{true} / p_{true};p_{T} (GeV/c);#eta;#phi", kTHnSparseF, {{199, -0.995, 0.995}, {200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + fRegistry.add("V0/primary/hPtEtaPhi", "pt, eta, and phi;p_{T} (GeV/c);#eta;#phi", kTHnSparseF, {{200, 0., 20.}, {18, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}}, false); + } + if (mlcuts.cfgApplyPCMMl) { + if (mlcuts.cfgNClassesPCMMl == 2) { // o2-linter: disable=magic-number (BDT group) + fRegistry.add("V0/primary/hBDTBackgroundScoreVsPt", "BDT background score vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{200, 0.0f, 20.0f}, {100, 0.0f, 1.0f}}}); + fRegistry.add("V0/primary/hBDTSignalScoreVsPt", "BDT signal score vs pT; pT (GeV/c); BDT signal score", {HistType::kTH2F, {{200, 0.0f, 20.0f}, {100, 0.0f, 1.0f}}}); + } else if (mlcuts.cfgNClassesPCMMl == 3) { // o2-linter: disable=magic-number (BDT group) + fRegistry.add("V0/primary/hBDTBackgroundScoreVsPt", "BDT background score vs pT; pT (GeV/c); BDT background score", {HistType::kTH2F, {{200, 0.0f, 20.0f}, {100, 0.0f, 1.0f}}}); + fRegistry.add("V0/primary/hBDTPrimaryPhotonScoreVsPt", "BDT primary photon score vs pT; pT (GeV/c); BDT primary photon score", {HistType::kTH2F, {{200, 0.0f, 20.0f}, {100, 0.0f, 1.0f}}}); + fRegistry.add("V0/primary/hBDTSecondaryPhotonScoreVsPt", "BDT secondary photon score vs pT; pT (GeV/c); BDT secondary photon score", {HistType::kTH2F, {{200, 0.0f, 20.0f}, {100, 0.0f, 1.0f}}}); + } else { + fRegistry.add("V0/primary/hBDTScoreVsPt", "BDT score vs pT; pT (GeV/c); BDT score", {HistType::kTH2F, {{200, 0.0f, 20.0f}, {100, 0.0f, 1.0f}}}); + } + fRegistry.add("V0/primary/hPhiVPsi", "#varphi vs. #psi angle;#psi (rad.); #varphi (rad.)", kTH2F, {{200, -o2::constants::math::PI, o2::constants::math::PI}, {200, 0, o2::constants::math::TwoPI}}, false); + } + fRegistry.addClone("V0/primary/", "V0/fromWD/"); // from weak decay + fRegistry.addClone("V0/primary/", "V0/fromHS/"); // from hadronic shower in detector materials + fRegistry.addClone("V0/primary/", "V0/fromPi0Dalitz/"); // misidentified dielectron from pi0 dalitz decay + fRegistry.addClone("V0/primary/", "V0/fromEtaDalitz/"); // misidentified dielectron from eta dalitz decay + fRegistry.addClone("V0/primary/hPt", "V0/candidate/hPt"); // only for purity + fRegistry.addClone("V0/primary/hEtaPhi", "V0/candidate/hEtaPhi"); // only for purity + + fRegistry.add("V0Leg/primary/hPt", "pT;p_{T,e} (GeV/c)", kTH1F, {{1000, 0.0f, 10}}, false); + fRegistry.add("V0Leg/primary/hQoverPt", "q/pT;q/p_{T} (GeV/c)^{-1}", kTH1F, {{1000, -50, 50}}, false); + fRegistry.add("V0Leg/primary/hEtaPhi", "#eta vs. #varphi;#varphi (rad.);#eta", kTH2F, {{90, 0, o2::constants::math::TwoPI}, {200, -1.0f, 1.0f}}, false); + fRegistry.add("V0Leg/primary/hDCAxyz", "DCA xy vs. z;DCA_{xy} (cm);DCA_{z} (cm)", kTH2F, {{200, -50.0f, 50.0f}, {200, -50.0f, 50.0f}}, false); + fRegistry.add("V0Leg/primary/hNclsTPC", "number of TPC clusters", kTH1F, {{161, -0.5, 160.5}}, false); + fRegistry.add("V0Leg/primary/hNcrTPC", "number of TPC crossed rows", kTH1F, {{161, -0.5, 160.5}}, false); + fRegistry.add("V0Leg/primary/hChi2TPC", "chi2/number of TPC clusters", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("V0Leg/primary/hTPCdEdx", "TPC dE/dx;p_{in} (GeV/c);TPC dE/dx (a.u.)", kTH2F, {{1000, 0, 10}, {200, 0, 200}}, false); + fRegistry.add("V0Leg/primary/hTPCNsigmaEl", "TPC n sigma el;p_{in} (GeV/c);n #sigma_{e}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("V0Leg/primary/hTPCNsigmaPi", "TPC n sigma pi;p_{in} (GeV/c);n #sigma_{#pi}^{TPC}", kTH2F, {{1000, 0, 10}, {100, -5, +5}}, false); + fRegistry.add("V0Leg/primary/hTPCNsigmaElVsEta", "TPC n sigma el vs. eta;#eta;n #sigma_{e}^{TPC}", kTH2F, {{40, -1.0f, 1.0f}, {100, -5, +5}}, false); + fRegistry.add("V0Leg/primary/hTPCNcr2Nf", "TPC Ncr/Nfindable", kTH1F, {{200, 0, 2}}, false); + fRegistry.add("V0Leg/primary/hTPCNcls2Nf", "TPC Ncls/Nfindable", kTH1F, {{200, 0, 2}}, false); + fRegistry.add("V0Leg/primary/hTPCNclsShared", "TPC Ncls shared/Ncls;p_{T} (GeV/c);N_{cls}^{shared}/N_{cls} in TPC", kTH2F, {{1000, 0, 10}, {100, 0, 1}}, false); + fRegistry.add("V0Leg/primary/hNclsITS", "number of ITS clusters", kTH1F, {{8, -0.5, 7.5}}, false); + fRegistry.add("V0Leg/primary/hChi2ITS", "chi2/number of ITS clusters", kTH1F, {{100, 0, 10}}, false); + fRegistry.add("V0Leg/primary/hITSClusterMap", "ITS cluster map", kTH1F, {{128, -0.5, 127.5}}, false); + fRegistry.add("V0Leg/primary/hMeanClusterSizeITS", "mean cluster size ITS; on ITS #times cos(#lambda)", kTH2F, {{1000, 0, 10}, {160, 0, 16}}, false); + fRegistry.add("V0Leg/primary/hPtGen_DeltaPtOverPtGen", "electron p_{T} resolution;p_{T}^{gen} (GeV/c);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", kTH2F, {{1000, 0, 10}, {200, -1.0f, 1.0f}}, true); + fRegistry.add("V0Leg/primary/hPtGen_DeltaEta", "electron #eta resolution;p_{T}^{gen} (GeV/c);#eta^{rec} - #eta^{gen}", kTH2F, {{1000, 0, 10}, {100, -0.5f, 0.5f}}, true); + fRegistry.add("V0Leg/primary/hPtGen_DeltaPhi", "electron #varphi resolution;p_{T}^{gen} (GeV/c);#varphi^{rec} - #varphi^{gen} (rad.)", kTH2F, {{1000, 0, 10}, {100, -0.5f, 0.5f}}, true); + fRegistry.add("V0Leg/primary/hRxyGen_DeltaPtOverPtGen", "electron p_{T} resolution; R_{xy}^{gen} (cm);(p_{T}^{rec} - p_{T}^{gen})/p_{T}^{gen}", kTH2F, {{100, 0, 100}, {200, -1.0f, 1.0f}}, true); + fRegistry.add("V0Leg/primary/hRxyGen_DeltaEta", "electron #eta resolution;R_{xy}^{gen} (cm);#eta^{rec} - #eta^{gen}", kTH2F, {{100, 0, 100}, {100, -0.5f, 0.5f}}, true); + fRegistry.add("V0Leg/primary/hRxyGen_DeltaPhi", "electron #varphi resolution;R_{xy}^{gen} (cm);#varphi^{rec} - #varphi^{gen} (rad.)", kTH2F, {{100, 0, 100}, {100, -0.5f, 0.5f}}, true); + fRegistry.addClone("V0Leg/primary/", "V0Leg/fromWD/"); + fRegistry.addClone("V0Leg/primary/", "V0Leg/fromHS/"); + fRegistry.addClone("V0Leg/primary/", "V0Leg/fromPi0Dalitz/"); + fRegistry.addClone("V0Leg/primary/", "V0Leg/fromEtaDalitz/"); + fRegistry.addClone("V0Leg/primary/hPt", "V0Leg/candidate/hPt"); // only for purity + fRegistry.addClone("V0Leg/primary/hEtaPhi", "V0Leg/candidate/hEtaPhi"); // only for purity + + // collision-association QA: + if (mcAnalysisModeSettings.cfgDoCollisionAssociationQA) { + fRegistry.add("CollisionAssociation/hDeltaCollId", "MC-event index difference;#Delta coll ID (rec - true);N_{#gamma}", kTH1F, {{21, -10.5, 10.5}}, true); + fRegistry.add("CollisionAssociation/RightCollisions/hs", "correctly associated;R_{rec} (cm);p_{T} (GeV/c);#Delta p_{T}/p_{T,gen};#Delta z (cm);#Delta R (cm);#Delta coll ID", kTHnSparseF, {{100, 0, 100}, {200, 0, 10}, {200, -1, 1}, {200, -20, 20}, {200, -8, 8}, {21, -10.5, 10.5}}, true); + fRegistry.add("CollisionAssociation/WrongCollisions/hs", "wrongly associated;R_{rec} (cm);p_{T} (GeV/c);#Delta p_{T}/p_{T,gen};#Delta z (cm);#Delta R (cm);#Delta coll ID", kTHnSparseF, {{100, 0, 100}, {200, 0, 10}, {200, -1, 1}, {200, -20, 20}, {200, -8, 8}, {21, -10.5, 10.5}}, true); + } + } + + // pT-dependent loss QA: + if (qaSettingsGroup.cfgDoPtDependentLossQA) { + fRegistry.add("V0/LossQA/before/hCosPAvsPt", "cosPA vs. pT;p_{T,#gamma} (GeV/c);cosine pointing angle", kTH2F, {{100, 0, 10}, {100, 0.99, 1.0}}, false); + fRegistry.add("V0/LossQA/before/hPCAvsPt", "PCA vs. pT;p_{T,#gamma} (GeV/c);PCA (cm)", kTH2F, {{100, 0, 10}, {500, 0, 5}}, false); + fRegistry.add("V0/LossQA/before/hQtAPvsPt", "AP q_{T} vs. pT;p_{T,#gamma} (GeV/c);q_{T} (GeV/c)", kTH2F, {{100, 0, 10}, {250, 0, 0.25}}, false); + fRegistry.add("V0/LossQA/before/hRxyVsPt", "conversion radius vs. pT;p_{T,#gamma} (GeV/c);R_{xy} (cm)", kTH2F, {{100, 0, 10}, {200, 0, 100}}, false); + fRegistry.add("V0/LossQA/before/hMeeVsPt", "m_{ee} vs. pT;p_{T,#gamma} (GeV/c);m_{ee} (GeV/c^{2})", kTH2F, {{100, 0, 10}, {100, 0, 0.1}}, false); + fRegistry.addClone("V0/LossQA/before/", "V0/LossQA/after/"); + } + + if (doprocessRecoQA) { + fRegistry.add("RecoQA/0_converted/hs", "true converted photons;p_{T,#gamma} (GeV/c);#eta;#varphi (rad.);R_{conv}^{true} (cm)", kTHnSparseF, {{100, 0, 10}, {36, -0.9, 0.9}, {36, 0, o2::constants::math::TwoPI}, {100, 0, 100}}, true); + fRegistry.addClone("RecoQA/0_converted/hs", "RecoQA/1_bothLegsTracked/hs"); + fRegistry.addClone("RecoQA/0_converted/hs", "RecoQA/2_bothLegsInV0Legs/hs"); + fRegistry.addClone("RecoQA/0_converted/hs", "RecoQA/3_photonInV0PhotonsKF/hs"); + fRegistry.add("RecoQA/hStageVsPt", "reconstruction stage vs. pT;p_{T,#gamma} (GeV/c);stage (0=converted,1=tracked,2=V0Legs,3=V0PhotonsKF)", kTH2F, {{100, 0, 10}, {4, -0.5, 3.5}}, true); + fRegistry.add("RecoQA/hMissingLeg_PtRconv", "legs tracked but lost before V0Legs;p_{T,leg}^{true} (GeV/c);R_{conv}^{true} (cm)", kTH2F, {{100, 0, 1}, {100, 0, 100}}, true); + fRegistry.add("RecoQA/Duplicates/hNTracksPerTrueLeg", "reconstructed tracks per true conversion leg;N_{tracks} per true leg;N_{legs}", kTH1F, {{6, -0.5, 5.5}}, true); + fRegistry.add("RecoQA/Duplicates/hNV0LegsPerTrueLeg", "V0Leg entries per true conversion leg;N_{V0Legs} per true leg;N_{legs}", kTH1F, {{6, -0.5, 5.5}}, true); + fRegistry.add("RecoQA/Duplicates/hNV0CandsPerPhoton_Rconv", "correctly built V0 candidates per true photon;N_{cand} per true #gamma;R_{conv}^{true} (cm)", kTH2F, {{6, -0.5, 5.5}, {100, 0, 100}}, true); + if (recoQASettingsGroup.cfgDoDetailedTrackQA) { + fRegistry.add("RecoQA/LegQuality/survived/hNcrTPC", "TPC crossed rows;TPC crossed rows", kTH1F, {{161, -0.5, 160.5}}, true); + fRegistry.add("RecoQA/LegQuality/survived/hChi2TPC", "chi2 per TPC cluster;#chi^{2}/N_{cls}^{TPC}", kTH1F, {{100, 0, 10}}, true); + fRegistry.add("RecoQA/LegQuality/survived/hSharedFracTPC", "TPC shared-cluster fraction;N_{cls}^{shared}/N_{cls}", kTH1F, {{110, 0, 1.1}}, true); + fRegistry.add("RecoQA/LegQuality/survived/hNclsTPC", "number of TPC clusters;N_{cls}^{TPC}", kTH1F, {{161, -0.5, 160.5}}, true); + fRegistry.addClone("RecoQA/LegQuality/survived/", "RecoQA/LegQuality/lost/"); + } + } + + if (qaSettingsGroup.cfgDoKappaAnalysis.value) { + if (doprocessQC || doprocessQCML) { + fRegistry.add("Kappa/Rec", ";#Kappa;#it{p}_{T} (GeV/#it{c});cent (%)", kTH3D, {{200, -10.f, 10.f}, {100, 0., 10.}, {20, 0., 100.}}, true); + } + if (doprocessPCMQCMC || doprocessPCMQCMCML) { + fRegistry.add("Kappa/MC/v0", "v0 ee pairs;#Kappa;#it{p}_{T} (GeV/#it{c});cent (%)", kTH3D, {{200, -10.f, 10.f}, {100, 0., 10.}, {20, 0., 100.}}, true); + fRegistry.add("Kappa/MC/ee", "ee pairs not from v0;#Kappa;#it{p}_{T} (GeV/#it{c});cent (%)", kTH3D, {{200, -10.f, 10.f}, {100, 0., 10.}, {20, 0., 100.}}, true); + fRegistry.add("Kappa/MC/pie", "pion + e pairs;#Kappa;#it{p}_{T} (GeV/#it{c});cent (%)", kTH3D, {{200, -10.f, 10.f}, {100, 0., 10.}, {20, 0., 100.}}, true); + fRegistry.add("Kappa/MC/pipi", "pion pion pairs;#Kappa;#it{p}_{T} (GeV/#it{c});cent (%)", kTH3D, {{200, -10.f, 10.f}, {100, 0., 10.}, {20, 0., 100.}}, true); + fRegistry.add("Kappa/MC/other", "other pairs;#Kappa;#it{p}_{T} (GeV/#it{c});cent (%)", kTH3D, {{200, -10.f, 10.f}, {100, 0., 10.}, {20, 0., 100.}}, true); + } + } } void DefineEMEventCut() @@ -315,58 +600,57 @@ struct PCMQC { fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); // for v0 - fV0PhotonCut.SetV0PtRange(pcmcuts.cfg_min_pt_v0, pcmcuts.cfg_max_pt_v0); - fV0PhotonCut.SetV0EtaRange(pcmcuts.cfg_min_eta_v0, pcmcuts.cfg_max_eta_v0); - fV0PhotonCut.SetMinCosPA(pcmcuts.cfg_min_cospa); - fV0PhotonCut.SetMaxPCA(pcmcuts.cfg_max_pca); - fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfg_max_chi2kf); - fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius); - fV0PhotonCut.SetAPRange(pcmcuts.cfg_max_alpha_ap, pcmcuts.cfg_max_qt_ap); - fV0PhotonCut.RejectITSib(pcmcuts.cfg_reject_v0_on_itsib); + fV0PhotonCut.SetV0PtRange(v0cuts.cfgMinPtV0, v0cuts.cfgMaxPtV0); + fV0PhotonCut.SetV0EtaRange(v0cuts.cfgMinEtaV0, v0cuts.cfgMaxEtaV0); + fV0PhotonCut.SetMinCosPA(v0cuts.cfgMinV0CosPA); + fV0PhotonCut.SetMaxPCA(v0cuts.cfgMaxPCA); + fV0PhotonCut.SetMaxChi2KF(v0cuts.cfgMaxChi2KF); + fV0PhotonCut.SetRxyRange(v0cuts.cfgMinV0Radius, v0cuts.cfgMaxV0Radius, v0cuts.cfgMidLV0Radius, v0cuts.cfgMidHV0Radius); + fV0PhotonCut.SetAPRange(v0cuts.cfgMaxAlphaAP, v0cuts.cfgMaxQtAP); + fV0PhotonCut.RejectITSib(v0cuts.cfgRejectV0OnITSib); // for track - fV0PhotonCut.SetMinNClustersTPC(pcmcuts.cfg_min_ncluster_tpc); - fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.cfg_min_ncrossedrows); + fV0PhotonCut.SetMinNClustersTPC(trackcuts.cfgMinNClustersTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(trackcuts.cfgMinNCrossedRows); fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); - fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.cfg_max_frac_shared_clusters_tpc); - fV0PhotonCut.SetChi2PerClusterTPC(0.0, pcmcuts.cfg_max_chi2tpc); - fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.cfg_min_TPCNsigmaEl, pcmcuts.cfg_max_TPCNsigmaEl); - fV0PhotonCut.SetChi2PerClusterITS(-1e+10, pcmcuts.cfg_max_chi2its); + fV0PhotonCut.SetMaxFracSharedClustersTPC(trackcuts.cfgMaxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.0, trackcuts.cfgMaxChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(trackcuts.cfgMinTPCNsigmaEl, trackcuts.cfgMaxTPCNsigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(-1e+10, trackcuts.cfgMaxChi2ITS); fV0PhotonCut.SetNClustersITS(0, 7); fV0PhotonCut.SetMeanClusterSizeITSob(0.0, 16.0); - fV0PhotonCut.SetDisableITSonly(pcmcuts.cfg_disable_itsonly_track); - fV0PhotonCut.SetDisableTPConly(pcmcuts.cfg_disable_tpconly_track); - fV0PhotonCut.SetRequireITSTPC(pcmcuts.cfg_require_v0_with_itstpc); - fV0PhotonCut.SetRequireITSonly(pcmcuts.cfg_require_v0_with_itsonly); - fV0PhotonCut.SetRequireTPConly(pcmcuts.cfg_require_v0_with_tpconly); + fV0PhotonCut.SetDisableITSonly(trackcuts.cfgDisableITSonlyTracks); + fV0PhotonCut.SetDisableTPConly(trackcuts.cfgDisableTPConlyTracks); + fV0PhotonCut.SetRequireITSTPC(v0cuts.cfgRequireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(v0cuts.cfgRequireV0WithITSonly); + fV0PhotonCut.SetRequireTPConly(v0cuts.cfgRequireV0WithTPConly); // for ML - fV0PhotonCut.SetApplyMlCuts(pcmcuts.cfg_apply_ml_cuts); - fV0PhotonCut.SetUse2DBinning(pcmcuts.cfg_use_2d_binning); - fV0PhotonCut.SetLoadMlModelsFromCCDB(pcmcuts.cfg_load_ml_models_from_ccdb); - fV0PhotonCut.SetNClassesMl(pcmcuts.cfg_nclasses_ml); - fV0PhotonCut.SetMlTimestampCCDB(pcmcuts.cfg_timestamp_ccdb); + fV0PhotonCut.SetApplyMlCuts(mlcuts.cfgApplyPCMMl); + fV0PhotonCut.SetUse2DBinning(mlcuts.cfgUse2DBinning); + fV0PhotonCut.SetLoadMlModelsFromCCDB(mlcuts.cfgLoadModelsFromCCDB); + fV0PhotonCut.SetNClassesMl(mlcuts.cfgNClassesPCMMl); + fV0PhotonCut.SetMlTimestampCCDB(mlcuts.cfgTimestampCCDB); fV0PhotonCut.SetCcdbUrl(ccdburl); - CentType mCentralityTypeMlEnum; - mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); + auto mCentralityTypeMlEnum = static_cast(cfgCentEstimator.value); fV0PhotonCut.SetCentralityTypeMl(mCentralityTypeMlEnum); - fV0PhotonCut.SetCutDirMl(pcmcuts.cfg_cut_dir_ml); - fV0PhotonCut.SetMlModelPathsCCDB(pcmcuts.cfg_model_paths_ccdb); - fV0PhotonCut.SetMlOnnxFileNames(pcmcuts.cfg_onnx_file_names); - fV0PhotonCut.SetBinsPtMl(pcmcuts.cfg_bins_pt_ml); - fV0PhotonCut.SetBinsCentMl(pcmcuts.cfg_bins_cent_ml); - fV0PhotonCut.SetCutsMl(pcmcuts.cfg_cuts_ml_flat); - fV0PhotonCut.SetNamesInputFeatures(pcmcuts.cfg_input_feature_names); - fV0PhotonCut.SetLabelsBinsMl(pcmcuts.cfg_labels_bins_ml); - fV0PhotonCut.SetLabelsCutScoresMl(pcmcuts.cfg_labels_cut_scores_ml); - fV0PhotonCut.SetD_Bz(0.0f); // dummy value -> only for psi_pair calculation - - if (pcmcuts.cfg_apply_ml_cuts) { + fV0PhotonCut.SetCutDirMl(mlcuts.cfgCutDirPCMMl); + fV0PhotonCut.SetMlModelPathsCCDB(mlcuts.cfgModelPathsCCDB); + fV0PhotonCut.SetMlOnnxFileNames(mlcuts.cfgOnnxFileNames); + fV0PhotonCut.SetBinsPtMl(mlcuts.cfgBinsPtPCMMl); + fV0PhotonCut.SetBinsCentMl(mlcuts.cfgBinsCentPCMMl); + fV0PhotonCut.SetCutsMl(mlcuts.cfgCutsPCMMlFlat); + fV0PhotonCut.SetNamesInputFeatures(mlcuts.cfgNamesInputFeatures); + fV0PhotonCut.SetLabelsBinsMl(mlcuts.cfgLabelsBinsPCMMl); + fV0PhotonCut.SetLabelsCutScoresMl(mlcuts.cfgLabelsCutScoresPCMMl); + fV0PhotonCut.SetD_Bz(0.0f); + + if (mlcuts.cfgApplyPCMMl) { fV0PhotonCut.initV0MlModels(ccdbApi); } } - template + template void fillEventInfo(TCollision const& collision, const float /*weight*/ = 1.f) { fRegistry.fill(HIST("Event/") + HIST(event_types[ev_id]) + HIST("hCollisionCounter"), 1.0); @@ -391,7 +675,7 @@ struct PCMQC { if (collision.sel8()) { fRegistry.fill(HIST("Event/") + HIST(event_types[ev_id]) + HIST("hCollisionCounter"), 8.0); } - if (std::fabs(collision.posZ()) < 10.0) { + if (std::fabs(collision.posZ()) < 10.0) { // o2-linter: disable=magic-number (vertex cut) fRegistry.fill(HIST("Event/") + HIST(event_types[ev_id]) + HIST("hCollisionCounter"), 9.0); } fRegistry.fill(HIST("Event/") + HIST(event_types[ev_id]) + HIST("hZvtx"), collision.posZ()); @@ -406,7 +690,7 @@ struct PCMQC { fRegistry.fill(HIST("Event/") + HIST(event_types[ev_id]) + HIST("hMultFT0MvsMultNTracksPV"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksPV()); } - template + template void fillV0Info(TV0 const& v0) { fRegistry.fill(HIST("V0/hPt"), v0.pt()); @@ -420,6 +704,13 @@ struct PCMQC { fRegistry.fill(HIST("V0/hDCAxyz"), v0.dcaXYtopv(), v0.dcaZtopv()); fRegistry.fill(HIST("V0/hDCAz_Pt"), v0.dcaZtopv(), v0.pt()); fRegistry.fill(HIST("V0/hAPplot"), v0.alpha(), v0.qtarm()); + fRegistry.fill(HIST("V0/hRxyVsPt"), v0.pt(), v0.v0radius()); + fRegistry.fill(HIST("V0/hEtaVsPt"), v0.pt(), v0.eta()); + fRegistry.fill(HIST("V0/hPhiVsPt"), v0.pt(), v0.phi()); + fRegistry.fill(HIST("V0/hsAlphaQtPt"), v0.alpha(), v0.qtarm(), v0.pt()); + if constexpr (requires { v0.psipair(); }) { + fRegistry.fill(HIST("V0/hPsiPairVsPt"), v0.pt(), v0.psipair()); + } fRegistry.fill(HIST("V0/hMassGamma"), v0.v0radius(), v0.mGamma()); fRegistry.fill(HIST("V0/hKFChi2vsM"), v0.mGamma(), v0.chiSquareNDF()); fRegistry.fill(HIST("V0/hKFChi2vsR"), v0.v0radius(), v0.chiSquareNDF()); @@ -428,12 +719,12 @@ struct PCMQC { fRegistry.fill(HIST("V0/hKFChi2vsZ"), v0.vz(), v0.chiSquareNDF()); float phi_cp = std::atan2(v0.vy(), v0.vx()); - o2::math_utils::bringTo02Pi(phi_cp); + RecoDecay::constrainAngle(phi_cp); float eta_cp = std::atanh(v0.vz() / std::sqrt(std::pow(v0.vx(), 2) + std::pow(v0.vy(), 2) + std::pow(v0.vz(), 2))); fRegistry.fill(HIST("V0/hsConvPoint"), v0.v0radius(), phi_cp, eta_cp); // BDT response histogram can be filled here when apply BDT is true - if (pcmcuts.cfg_apply_ml_cuts) { + if (mlcuts.cfgApplyPCMMl) { const std::span& bdtValue = fV0PhotonCut.getBDTValue(); float psipair = 999.f; float phiv = 999.f; @@ -442,20 +733,20 @@ struct PCMQC { phiv = v0.phiv(); } fRegistry.fill(HIST("V0/hPhiVPsi"), psipair, phiv); - if (pcmcuts.cfg_nclasses_ml == 2 && bdtValue.size() == 2) { + if (mlcuts.cfgNClassesPCMMl == 2 && bdtValue.size() == 2) { // o2-linter: disable=magic-number (BDT) fRegistry.fill(HIST("V0/hBDTBackgroundScoreVsPt"), v0.pt(), bdtValue[0]); fRegistry.fill(HIST("V0/hBDTSignalScoreVsPt"), v0.pt(), bdtValue[1]); - } else if (pcmcuts.cfg_nclasses_ml == 3 && bdtValue.size() == 3) { + } else if (mlcuts.cfgNClassesPCMMl == 3 && bdtValue.size() == 3) { // o2-linter: disable=magic-number (BDT) fRegistry.fill(HIST("V0/hBDTBackgroundScoreVsPt"), v0.pt(), bdtValue[0]); fRegistry.fill(HIST("V0/hBDTPrimaryPhotonScoreVsPt"), v0.pt(), bdtValue[1]); fRegistry.fill(HIST("V0/hBDTSecondaryPhotonScoreVsPt"), v0.pt(), bdtValue[2]); } else if (bdtValue.size() == 1) { - fRegistry.fill(HIST("V0/hBDTCutVsPt"), v0.pt(), bdtValue[0]); + fRegistry.fill(HIST("V0/hBDTScoreVsPt"), v0.pt(), bdtValue[0]); } } } - template + template void fillV0LegInfo(TLeg const& leg) { fRegistry.fill(HIST("V0Leg/hPt"), leg.pt()); @@ -477,24 +768,101 @@ struct PCMQC { fRegistry.fill(HIST("V0Leg/hTPCdEdx"), leg.tpcInnerParam(), leg.tpcSignal()); fRegistry.fill(HIST("V0Leg/hTPCNsigmaEl"), leg.tpcInnerParam(), leg.tpcNSigmaEl()); fRegistry.fill(HIST("V0Leg/hTPCNsigmaPi"), leg.tpcInnerParam(), leg.tpcNSigmaPi()); - // fRegistry.fill(HIST("V0Leg/hXY"), leg.x(), leg.y()); - // fRegistry.fill(HIST("V0Leg/hZX"), leg.z(), leg.x()); - // fRegistry.fill(HIST("V0Leg/hZY"), leg.z(), leg.y()); + fRegistry.fill(HIST("V0Leg/hTPCNsigmaElVsEta"), leg.eta(), leg.tpcNSigmaEl()); } - Preslice perCollisionV0 = aod::v0photonkf::pmeventId; - Preslice perCollisionV0ML = aod::v0photonkf::pmeventId; + template + void fillLossQAInfo(TV0 const& v0) + { + if (!qaSettingsGroup.cfgDoPtDependentLossQA) { + return; + } + fRegistry.fill(HIST("V0/LossQA/") + HIST(event_types[ev_id]) + HIST("hCosPAvsPt"), v0.pt(), v0.cospa()); + fRegistry.fill(HIST("V0/LossQA/") + HIST(event_types[ev_id]) + HIST("hPCAvsPt"), v0.pt(), v0.pca()); + fRegistry.fill(HIST("V0/LossQA/") + HIST(event_types[ev_id]) + HIST("hQtAPvsPt"), v0.pt(), v0.qtarm()); + fRegistry.fill(HIST("V0/LossQA/") + HIST(event_types[ev_id]) + HIST("hRxyVsPt"), v0.pt(), v0.v0radius()); + fRegistry.fill(HIST("V0/LossQA/") + HIST(event_types[ev_id]) + HIST("hMeeVsPt"), v0.pt(), v0.mGamma()); + } + + template + void fillMaterialBudgetInfo(TV0 const& v0) + { + if (materialBudgetSettingsGroup.cfgDoMaterialDistribution) { + fRegistry.fill(HIST("MaterialBudget/hs"), v0.vz(), v0.v0radius(), v0.eta(), v0.phi(), v0.pt()); + } + if (materialBudgetSettingsGroup.cfgDoWiresDetail && v0.v0radius() < 20.f && std::fabs(v0.vz()) < 20.f) { // o2-linter: disable=magic-number (region window = booking range) + if (3.15f < v0.phi() && v0.phi() < 3.4f) { // o2-linter: disable=magic-number (region window = booking range) + fRegistry.fill(HIST("MaterialBudget/Wires/hsLeft"), v0.vx(), v0.vy(), v0.vz(), v0.phi(), v0.v0radius(), v0.pt()); + } else if (6.00f < v0.phi() && v0.phi() < 6.15f) { // o2-linter: disable=magic-number (region window = booking range) + fRegistry.fill(HIST("MaterialBudget/Wires/hsRight"), v0.vx(), v0.vy(), v0.vz(), v0.phi(), v0.v0radius(), v0.pt()); + } + } + if (materialBudgetSettingsGroup.cfgDoITSDetail && v0.v0radius() < 60.f && std::fabs(v0.vz()) < 40.f) { // o2-linter: disable=magic-number (region window = booking range) + fRegistry.fill(HIST("MaterialBudget/ITS/hs"), v0.vx(), v0.vy(), v0.vz(), v0.phi(), v0.v0radius(), v0.pt()); + } + if (materialBudgetSettingsGroup.cfgDoMFTDetail && 40.f < v0.v0radius() && v0.v0radius() < 60.f && std::fabs(v0.vz()) < 40.f) { // o2-linter: disable=magic-number (region window = booking range) + fRegistry.fill(HIST("MaterialBudget/MFT/hs"), v0.vx(), v0.vy(), v0.vz(), v0.phi(), v0.v0radius(), v0.pt()); + } + if (materialBudgetSettingsGroup.cfgDoTPCInnerBarrelDetail && 60.f < v0.v0radius() && v0.v0radius() < 80.f && std::fabs(v0.vz()) < 40.f) { // o2-linter: disable=magic-number (region window = booking range) + fRegistry.fill(HIST("MaterialBudget/TPC/hs"), v0.vx(), v0.vy(), v0.vz(), v0.phi(), v0.v0radius(), v0.pt()); + } + } + + //_______________________________________________________________________ + template + void fillKappaRec(TLeg const& pos, TLeg const& ele, const float pt, const float cent) + { + if (!qaSettingsGroup.cfgDoKappaAnalysis.value) { + return; + } + const float kappa = getV0Kappa(pos, ele); + fRegistry.fill(HIST("Kappa/Rec"), kappa, pt, cent); + } + + //_______________________________________________________________________ + template + void fillKappaMC(TLeg const& pos, TLeg const& ele, TMCParticle const& posmc, TMCParticle const& elemc, const int photonid, const float pt, const float cent) + { + if (!qaSettingsGroup.cfgDoKappaAnalysis.value) { + return; + } + + const float kappa = getV0Kappa(pos, ele); + + const int pdgPos = std::abs(posmc.pdgCode()); + const int pdgEle = std::abs(elemc.pdgCode()); + + const bool posIsElectron = (pdgPos == PDG_t::kElectron); + const bool eleIsElectron = (pdgEle == PDG_t::kElectron); + const bool posIsPion = (pdgPos == PDG_t::kPiPlus); + const bool eleIsPion = (pdgEle == PDG_t::kPiPlus); + + if (photonid > 0) { + fRegistry.fill(HIST("Kappa/MC/v0"), kappa, pt, cent); + } else if (posIsElectron && eleIsElectron) { + fRegistry.fill(HIST("Kappa/MC/ee"), kappa, pt, cent); + } else if ((posIsPion && eleIsElectron) || (posIsElectron && eleIsPion)) { + fRegistry.fill(HIST("Kappa/MC/pie"), kappa, pt, cent); + } else if (posIsPion && eleIsPion) { + fRegistry.fill(HIST("Kappa/MC/pipi"), kappa, pt, cent); + } else { + fRegistry.fill(HIST("Kappa/MC/other"), kappa, pt, cent); + } + } + + Preslice perCollisionV0 = aod::v0photonkf::pmeventId; Filter collisionFilter_centrality = (cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < cfgCentMax) || (cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < cfgCentMax); Filter collisionFilter_occupancy_track = eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; Filter collisionFilter_occupancy_ft0c = eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; using FilteredMyCollisions = soa::Filtered; + using FilteredMyCollisionsMC = soa::Filtered; // same filters, they act column-wise - template - void process(FilteredMyCollisions const& collisions, TV0Photon const& v0photons, aod::V0Legs const&, TPerCollision const& perCollision) + template + void processRec(FilteredMyCollisions const& collisions, TV0Photon const& v0photons, aod::V0Legs const&) { for (const auto& collision : collisions) { initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -509,19 +877,22 @@ struct PCMQC { fV0PhotonCut.SetCentrality(centralities[cfgCentEstimator]); int nv0 = 0; - auto v0photons_coll = v0photons.sliceBy(perCollision, collision.globalIndex()); + auto v0photons_coll = v0photons.sliceBy(perCollisionV0, collision.globalIndex()); for (const auto& v0 : v0photons_coll) { auto pos = v0.template posTrack_as(); auto ele = v0.template negTrack_as(); - + fillLossQAInfo<0>(v0); // all candidates offered to the selection if (!fV0PhotonCut.IsSelected(v0)) { continue; } + fillLossQAInfo<1>(v0); fillV0Info(v0); - for (auto& leg : {pos, ele}) { + fillKappaRec(pos, ele, v0.pt(), centralities[cfgCentEstimator]); + fillMaterialBudgetInfo(v0); + for (const auto& leg : {pos, ele}) { fillV0LegInfo(leg); } - if (pcmcuts.cfg_dEdx_postcalibration) { + if (trackcuts.cfgDoDEdxPostCalibration) { fRegistry.fill(HIST("V0Leg/hPvsConvPointvsTPCNsigmaElvsEta_Pos"), pos.p(), v0.v0radius(), pos.tpcNSigmaEl(), pos.eta()); fRegistry.fill(HIST("V0Leg/hPvsConvPointvsTPCNsigmaElvsEta_Ele"), ele.p(), v0.v0radius(), ele.tpcNSigmaEl(), ele.eta()); } @@ -532,23 +903,526 @@ struct PCMQC { } void processQC(FilteredMyCollisions const& collisions, MyV0Photons const& v0photons, aod::V0Legs const& v0legs) { - process(collisions, v0photons, v0legs, perCollisionV0); + processRec(collisions, v0photons, v0legs); } // end of process void processQCML(FilteredMyCollisions const& collisions, MyV0PhotonsML const& v0photonsML, aod::V0Legs const& v0legs) { - process(collisions, v0photonsML, v0legs, perCollisionV0ML); + processRec(collisions, v0photonsML, v0legs); } // end of ML process - void processDummy(MyCollisions const&) {} + template + void fillV0InfoMC(TV0 const& v0, TMCV0 const& mcphoton, TMCLeg const& mcleg) + { + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPt"), v0.pt()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hEtaPhi"), v0.phi(), v0.eta()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hXY"), v0.vx(), v0.vy()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRZ"), v0.vz(), v0.v0radius()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hCosPA"), v0.cospa()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hCosPAXY"), v0.cospaXY()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hCosPARZ"), v0.cospaRZ()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPCA"), v0.pca()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hDCAxyz"), v0.dcaXYtopv(), v0.dcaZtopv()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hDCAz_Pt"), v0.dcaZtopv(), v0.pt()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hAPplot"), v0.alpha(), v0.qtarm()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRxyVsPt"), v0.pt(), v0.v0radius()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hEtaVsPt"), v0.pt(), v0.eta()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPhiVsPt"), v0.pt(), v0.phi()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hsAlphaQtPt"), v0.alpha(), v0.qtarm(), v0.pt()); + if constexpr (requires { v0.psipair(); }) { // only the ML join carries psi_pair + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPsiPairVsPt"), v0.pt(), v0.psipair()); + } + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hMassGamma"), v0.v0radius(), v0.mGamma()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hKFChi2vsM"), v0.mGamma(), v0.chiSquareNDF()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hKFChi2vsR"), v0.v0radius(), v0.chiSquareNDF()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hKFChi2vsX"), v0.vx(), v0.chiSquareNDF()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hKFChi2vsY"), v0.vy(), v0.chiSquareNDF()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hKFChi2vsZ"), v0.vz(), v0.chiSquareNDF()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPtGen_DeltaPtOverPtGen"), mcphoton.pt(), (v0.pt() - mcphoton.pt()) / mcphoton.pt()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPtGen_DeltaEta"), mcphoton.pt(), v0.eta() - mcphoton.eta()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPtGen_DeltaPhi"), mcphoton.pt(), v0.phi() - mcphoton.phi()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRxyGen_DeltaPtOverPtGen"), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2)), (v0.pt() - mcphoton.pt()) / mcphoton.pt()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRxyGen_DeltaEta"), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2)), v0.eta() - mcphoton.eta()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRxyGen_DeltaPhi"), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2)), v0.phi() - mcphoton.phi()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRxyGen_DeltaR"), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2)), v0.v0radius() - std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2))); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hConvPoint_diffX"), mcleg.vx(), v0.vx() - mcleg.vx()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hConvPoint_diffY"), mcleg.vy(), v0.vy() - mcleg.vy()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hConvPoint_diffZ"), mcleg.vz(), v0.vz() - mcleg.vz()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hXY_MC"), mcleg.vx(), mcleg.vy()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hRZ_MC"), mcleg.vz(), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2))); + + if (mcAnalysisModeSettings.cfgDoDetailedResolution) { + float resolPt = (v0.pt() - mcphoton.pt()) / mcphoton.pt(); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPtResolPtEtaPhi"), resolPt, mcphoton.pt(), mcphoton.eta(), mcphoton.phi()); + float resolMom = (v0.p() - mcphoton.p()) / mcphoton.p(); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hMomResolPtEtaPhi"), resolMom, mcphoton.pt(), mcphoton.eta(), mcphoton.phi()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPtEtaPhi"), mcphoton.pt(), mcphoton.eta(), mcphoton.phi()); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hEtaPhiResol"), mcphoton.pt(), v0.eta() - mcphoton.eta(), v0.phi() - mcphoton.phi()); + } + + float phi_cp = std::atan2(v0.vy(), v0.vx()); + RecoDecay::constrainAngle(phi_cp); + float eta_cp = std::atanh(v0.vz() / std::sqrt(std::pow(v0.vx(), 2) + std::pow(v0.vy(), 2) + std::pow(v0.vz(), 2))); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hsConvPoint"), v0.v0radius(), phi_cp, eta_cp); + + if (mlcuts.cfgApplyPCMMl) { + const std::span& bdtValue = fV0PhotonCut.getBDTValue(); + float psipair = 999.f; + float phiv = 999.f; + if constexpr (requires { v0.psipair(); v0.phiv(); }) { + psipair = v0.psipair(); + phiv = v0.phiv(); + } + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hPhiVPsi"), psipair, phiv); + if (mlcuts.cfgNClassesPCMMl == 2 && bdtValue.size() == 2) { // o2-linter: disable=magic-number (BDT) + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hBDTBackgroundScoreVsPt"), v0.pt(), bdtValue[0]); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hBDTSignalScoreVsPt"), v0.pt(), bdtValue[1]); + } else if (mlcuts.cfgNClassesPCMMl == 3 && bdtValue.size() == 3) { // o2-linter: disable=magic-number (BDT) + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hBDTBackgroundScoreVsPt"), v0.pt(), bdtValue[0]); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hBDTPrimaryPhotonScoreVsPt"), v0.pt(), bdtValue[1]); + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hBDTSecondaryPhotonScoreVsPt"), v0.pt(), bdtValue[2]); + } else if (bdtValue.size() == 1) { // o2-linter: disable=magic-number (BDT) + fRegistry.fill(HIST("V0/") + HIST(mcphoton_types[mctype]) + HIST("hBDTScoreVsPt"), v0.pt(), bdtValue[0]); + } + } + } + + template + void fillV0LegInfoMC(TLeg const& leg) + { + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hPt"), leg.pt()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hQoverPt"), leg.sign() / leg.pt()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hEtaPhi"), leg.phi(), leg.eta()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hDCAxyz"), leg.dcaXY(), leg.dcaZ()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hNclsITS"), leg.itsNCls()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hNclsTPC"), leg.tpcNClsFound()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hNcrTPC"), leg.tpcNClsCrossedRows()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hTPCNcr2Nf"), leg.tpcCrossedRowsOverFindableCls()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hTPCNcls2Nf"), leg.tpcFoundOverFindableCls()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hTPCNclsShared"), leg.pt(), leg.tpcFractionSharedCls()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hChi2TPC"), leg.tpcChi2NCl()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hChi2ITS"), leg.itsChi2NCl()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hITSClusterMap"), leg.itsClusterMap()); + if (leg.hasITS()) { + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hMeanClusterSizeITS"), leg.p(), leg.meanClusterSizeITS() * std::cos(std::atan(leg.tgl()))); + } + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hTPCdEdx"), leg.tpcInnerParam(), leg.tpcSignal()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hTPCNsigmaEl"), leg.tpcInnerParam(), leg.tpcNSigmaEl()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hTPCNsigmaPi"), leg.tpcInnerParam(), leg.tpcNSigmaPi()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hTPCNsigmaElVsEta"), leg.eta(), leg.tpcNSigmaEl()); + auto mcleg = leg.template emmcparticle_as(); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hPtGen_DeltaPtOverPtGen"), mcleg.pt(), (leg.pt() - mcleg.pt()) / mcleg.pt()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hPtGen_DeltaEta"), mcleg.pt(), leg.eta() - mcleg.eta()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hPtGen_DeltaPhi"), mcleg.pt(), leg.phi() - mcleg.phi()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hRxyGen_DeltaPtOverPtGen"), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2)), (leg.pt() - mcleg.pt()) / mcleg.pt()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hRxyGen_DeltaEta"), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2)), leg.eta() - mcleg.eta()); + fRegistry.fill(HIST("V0Leg/") + HIST(mcphoton_types[mctype]) + HIST("hRxyGen_DeltaPhi"), std::sqrt(std::pow(mcleg.vx(), 2) + std::pow(mcleg.vy(), 2)), leg.phi() - mcleg.phi()); + } + + template + void processMC(FilteredMyCollisionsMC const& collisions, TV0Photons const& v0photons, aod::EMMCParticles const& mcparticles, MyMCV0Legs const&, aod::EMMCEvents const&) + { + for (const auto& collision : collisions) { + initCCDB(collision); + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { + continue; + } + + fillEventInfo<0>(collision); + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + fillEventInfo<1>(collision); + fRegistry.fill(HIST("Event/before/hCollisionCounter"), 10.0); // accepted + fRegistry.fill(HIST("Event/after/hCollisionCounter"), 10.0); // accepted + + fV0PhotonCut.SetCentrality(centralities[cfgCentEstimator]); // set centrality for BDT response + auto v0photons_coll = v0photons.sliceBy(perCollisionV0, collision.globalIndex()); + int ng_primary = 0, ng_wd = 0, ng_hs = 0, nee_pi0 = 0, nee_eta = 0; + for (const auto& v0 : v0photons_coll) { + auto pos = v0.template posTrack_as(); + auto ele = v0.template negTrack_as(); + auto posmc = pos.template emmcparticle_as(); + auto elemc = ele.template emmcparticle_as(); + + fillLossQAInfo<0>(v0); // all candidates offered to the selection + if (!fV0PhotonCut.IsSelected(v0)) { + continue; + } + fillLossQAInfo<1>(v0); + + fillMaterialBudgetInfo(v0); + + fRegistry.fill(HIST("V0/candidate/hPt"), v0.pt()); + fRegistry.fill(HIST("V0/candidate/hEtaPhi"), v0.phi(), v0.eta()); + for (const auto& leg : {pos, ele}) { + fRegistry.fill(HIST("V0Leg/candidate/hPt"), leg.pt()); + fRegistry.fill(HIST("V0Leg/candidate/hEtaPhi"), leg.phi(), leg.eta()); + } + + int photonid = FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 22, mcparticles); + int pi0id = FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 111, mcparticles); // pi0 dalitz decay + int etaid = FindCommonMotherFrom2Prongs(posmc, elemc, -11, 11, 221, mcparticles); // eta dalitz decay + + fillKappaMC(pos, ele, posmc, elemc, photonid, v0.pt(), centralities[cfgCentEstimator]); + + if (photonid < 0 && pi0id < 0 && etaid < 0) { + continue; + } + + if (photonid > 0) { + auto mcphoton = mcparticles.iteratorAt(photonid); + + if (mcAnalysisModeSettings.cfgDoCollisionAssociationQA) { + const int deltaCol = static_cast(collision.emmceventId()) - static_cast(mcphoton.emmceventId()); + const float rGen = std::sqrt(std::pow(elemc.vx(), 2) + std::pow(elemc.vy(), 2)); + const float deltaR = v0.v0radius() - rGen; + const float deltaZ = v0.vz() - elemc.vz(); + const float relPtRes = mcphoton.pt() > 0.f ? (v0.pt() - mcphoton.pt()) / mcphoton.pt() : 0.f; + fRegistry.fill(HIST("CollisionAssociation/hDeltaCollId"), deltaCol); + if (deltaCol == 0) { + fRegistry.fill(HIST("CollisionAssociation/RightCollisions/hs"), v0.v0radius(), v0.pt(), relPtRes, deltaZ, deltaR, deltaCol); + } else { + fRegistry.fill(HIST("CollisionAssociation/WrongCollisions/hs"), v0.v0radius(), v0.pt(), relPtRes, deltaZ, deltaR, deltaCol); + } + } + + if (mcAnalysisModeSettings.cfgRequireTrueAssociation && (mcphoton.emmceventId() != collision.emmceventId())) { + continue; + } + + if (mcphoton.isPhysicalPrimary() || mcphoton.producedByGenerator()) { + fillV0InfoMC<0>(v0, mcphoton, elemc); + for (const auto& leg : {pos, ele}) { + fillV0LegInfoMC<0>(leg); + } + ng_primary++; + } else if (IsFromWD(mcphoton.template emmcevent_as(), mcphoton, mcparticles) > 0) { + fillV0InfoMC<1>(v0, mcphoton, elemc); + for (const auto& leg : {pos, ele}) { + fillV0LegInfoMC<1>(leg); + } + ng_wd++; + } else { + fillV0InfoMC<2>(v0, mcphoton, elemc); + for (const auto& leg : {pos, ele}) { + fillV0LegInfoMC<2>(leg); + } + ng_hs++; + } + } else if (pi0id > 0) { + auto mcpi0 = mcparticles.iteratorAt(pi0id); + if (mcAnalysisModeSettings.cfgRequireTrueAssociation && (mcpi0.emmceventId() != collision.emmceventId())) { + continue; + } + if (mcpi0.isPhysicalPrimary() || mcpi0.producedByGenerator()) { + fillV0InfoMC<3>(v0, mcpi0, elemc); + for (const auto& leg : {pos, ele}) { + fillV0LegInfoMC<3>(leg); + } + nee_pi0++; + } + } else if (etaid > 0) { + auto mceta = mcparticles.iteratorAt(etaid); + if (mcAnalysisModeSettings.cfgRequireTrueAssociation && (mceta.emmceventId() != collision.emmceventId())) { + continue; + } + if (mceta.isPhysicalPrimary() || mceta.producedByGenerator()) { + fillV0InfoMC<4>(v0, mceta, elemc); + for (const auto& leg : {pos, ele}) { + fillV0LegInfoMC<4>(leg); + } + nee_eta++; + } + } + } // end of v0 loop + fRegistry.fill(HIST("V0/primary/hNgamma"), ng_primary); + fRegistry.fill(HIST("V0/fromWD/hNgamma"), ng_wd); + fRegistry.fill(HIST("V0/fromHS/hNgamma"), ng_hs); + fRegistry.fill(HIST("V0/fromPi0Dalitz/hNgamma"), nee_pi0); + fRegistry.fill(HIST("V0/fromEtaDalitz/hNgamma"), nee_eta); + } // end of collision loop + } // end of MC process + + void processPCMQCMC(FilteredMyCollisionsMC const& collisions, MyV0Photons const& v0photons, aod::EMMCParticles const& mcparticles, MyMCV0Legs const& mcv0legs, aod::EMMCEvents const& mcevents) + { + processMC(collisions, v0photons, mcparticles, mcv0legs, mcevents); + } // end of MC QC process + + void processPCMQCMCML(FilteredMyCollisionsMC const& collisions, MyV0PhotonsML const& v0photonsML, aod::EMMCParticles const& mcparticles, MyMCV0Legs const& mcv0legs, aod::EMMCEvents const& mcevents) + { + processMC(collisions, v0photonsML, mcparticles, mcv0legs, mcevents); + } // end of MC QC process with ML cuts + + template + void fillBinnedData(TBinnedData const& binned_data, const float weight = 1.f) + { + int xbin = 0, ybin = 0, zbin = 0; + auto hPtY = fRegistry.get(HIST("Generated/hPtY")); // 2D + auto hPt = fRegistry.get(HIST("Generated/hPt")); // 1D + + for (int ibin = 0; ibin < hPtY->GetNcells(); ibin++) { + int nentry = binned_data[ibin]; + hPtY->GetBinXYZ(ibin, xbin, ybin, zbin); + float pt = hPtY->GetXaxis()->GetBinCenter(xbin); + float y = hPtY->GetYaxis()->GetBinCenter(ybin); + if (y > v0cuts.cfgMaxEtaV0) { + continue; + } + + for (int j = 0; j < nentry; j++) { + hPtY->Fill(pt, y, weight); + hPt->Fill(pt, weight); + } + } + } + + PresliceUnsorted perMcCollision = aod::emmcparticle::emmceventId; + void processGen(FilteredMyCollisionsMC const& collisions, MyMCCollisions const&, aod::EMMCParticles const& mcparticles) + { + + for (const auto& collision : collisions) { + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { + continue; + } + if (!fEMEventCut.IsSelected(collision)) { + continue; + } + + auto mccollision = collision.template emmcevent_as(); + auto binned_data_gamma_gen = mccollision.generatedGamma(); + fillBinnedData(binned_data_gamma_gen, 1.f); + + auto mctracks_coll = mcparticles.sliceBy(perMcCollision, mccollision.globalIndex()); + for (const auto& mctrack : mctracks_coll) { + if (std::fabs(mctrack.y()) > v0cuts.cfgMaxEtaV0) { + continue; + } + if (std::abs(mctrack.pdgCode()) != PDG_t::kGamma || !(mctrack.isPhysicalPrimary() || mctrack.producedByGenerator())) { + continue; + } + + if (mcAnalysisModeSettings.cfgDoDetailedResolution) { + fRegistry.fill(HIST("Generated/hPtEtaPhi"), mctrack.pt(), mctrack.eta(), mctrack.phi()); // for all generated photons before any kinematic cut + } + if (mctrack.daughtersIds().empty()) { + continue; + } + auto daughter = mcparticles.iteratorAt(mctrack.daughtersIds()[0]); // choose ele or pos. + float rxy_gen_e = std::sqrt(std::pow(daughter.vx(), 2) + std::pow(daughter.vy(), 2)); + float phi_cp = std::atan2(daughter.vy(), daughter.vx()); + RecoDecay::constrainAngle(phi_cp); + float eta_cp = std::atanh(daughter.vz() / std::sqrt(std::pow(daughter.vx(), 2) + std::pow(daughter.vy(), 2) + std::pow(daughter.vz(), 2))); + + fRegistry.fill(HIST("Generated/hR_ConversionPhoton_wideR"), rxy_gen_e); + fRegistry.fill(HIST("Generated/hRZ_wideR"), daughter.vz(), rxy_gen_e); + fRegistry.fill(HIST("Generated/hsRZPhi_wideR"), daughter.vz(), phi_cp, rxy_gen_e); + if (rxy_gen_e < std::fabs(daughter.vz()) * std::tan(2 * std::atan(std::exp(-v0cuts.cfgMaxEtaV0))) - genSettingsGroup.cfgMarginZMC) { + continue; + } + if (rxy_gen_e > genSettingsGroup.cfgMaxRGen) { + continue; + } + + fRegistry.fill(HIST("Generated/hPt_ConversionPhoton"), mctrack.pt()); + fRegistry.fill(HIST("Generated/hY_ConversionPhoton"), mctrack.y()); + fRegistry.fill(HIST("Generated/hPhi_ConversionPhoton"), mctrack.phi()); + fRegistry.fill(HIST("Generated/hsConvPoint"), rxy_gen_e, phi_cp, eta_cp); + fRegistry.fill(HIST("Generated/hXY"), daughter.vx(), daughter.vy()); + fRegistry.fill(HIST("Generated/hRZ"), daughter.vz(), rxy_gen_e); + fRegistry.fill(HIST("Generated/hRPhi"), phi_cp, rxy_gen_e); + } // end of mctrack loop per collision + } // end of collision loop + } + + template + void fillLegQualityRecoQA(TTrack const& track) + { + if constexpr (survived) { + fRegistry.fill(HIST("RecoQA/LegQuality/survived/hNcrTPC"), track.tpcNClsCrossedRows()); + fRegistry.fill(HIST("RecoQA/LegQuality/survived/hChi2TPC"), track.tpcChi2NCl()); + fRegistry.fill(HIST("RecoQA/LegQuality/survived/hSharedFracTPC"), track.tpcFractionSharedCls()); + fRegistry.fill(HIST("RecoQA/LegQuality/survived/hNclsTPC"), track.tpcNClsFound()); + } else { + fRegistry.fill(HIST("RecoQA/LegQuality/lost/hNcrTPC"), track.tpcNClsCrossedRows()); + fRegistry.fill(HIST("RecoQA/LegQuality/lost/hChi2TPC"), track.tpcChi2NCl()); + fRegistry.fill(HIST("RecoQA/LegQuality/lost/hSharedFracTPC"), track.tpcFractionSharedCls()); + fRegistry.fill(HIST("RecoQA/LegQuality/lost/hNclsTPC"), track.tpcNClsFound()); + } + } + + Preslice perMcCollisionTruth = aod::mcparticle::mcCollisionId; + void processRecoQA(aod::McCollisions const& mcCollisions, aod::McParticles const& mcparticles, MyTracksMC const& tracks, aod::V0Legs const& v0legs, aod::V0PhotonsKF const& v0photonsKF) + { + std::unordered_map nTracksOfMcId; + std::unordered_map mcIdToBestTrack; + std::unordered_map mcIdBestCrossedRows; + nTracksOfMcId.reserve(tracks.size()); + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const int mcId = track.mcParticleId(); + nTracksOfMcId[mcId]++; + const float ncr = track.tpcNClsCrossedRows(); + auto it = mcIdBestCrossedRows.find(mcId); + if (it == mcIdBestCrossedRows.end() || ncr > it->second) { + mcIdBestCrossedRows[mcId] = ncr; + mcIdToBestTrack[mcId] = track.globalIndex(); + } + } + + std::unordered_map nV0LegsOfMcId; + nV0LegsOfMcId.reserve(v0legs.size()); + for (const auto& leg : v0legs) { + const int trackId = leg.trackId(); + if (trackId < 0 || trackId >= static_cast(tracks.size())) { + continue; + } + const auto track = tracks.iteratorAt(trackId); + if (track.has_mcParticle()) { + nV0LegsOfMcId[track.mcParticleId()]++; + } + } + auto legMcMotherId = [&](auto const& leg) -> int { + const int trackId = leg.trackId(); + if (trackId < 0 || trackId >= static_cast(tracks.size())) { + return -1; + } + const auto track = tracks.iteratorAt(trackId); + if (!track.has_mcParticle()) { + return -1; + } + const auto mc = track.mcParticle(); + if (!mc.has_mothers()) { + return -1; + } + return mc.mothersIds()[0]; + }; + std::unordered_map nV0CandsOfMcPhoton; + for (const auto& v0 : v0photonsKF) { + const int legPosIdx = v0.posTrackId(); + const int legNegIdx = v0.negTrackId(); + if (legPosIdx < 0 || legPosIdx >= static_cast(v0legs.size()) || legNegIdx < 0 || legNegIdx >= static_cast(v0legs.size())) { + continue; + } + const int motherPos = legMcMotherId(v0legs.iteratorAt(legPosIdx)); + const int motherNeg = legMcMotherId(v0legs.iteratorAt(legNegIdx)); + if (motherPos >= 0 && motherPos == motherNeg) { + nV0CandsOfMcPhoton[motherPos]++; + } + } + for (const auto& mcCollision : mcCollisions) { + const auto mcparticles_coll = mcparticles.sliceBy(perMcCollisionTruth, mcCollision.globalIndex()); + for (const auto& mctrack : mcparticles_coll) { + if (mctrack.pdgCode() != PDG_t::kGamma || !mctrack.isPhysicalPrimary()) { + continue; + } + if (std::fabs(mctrack.eta()) > recoQASettingsGroup.cfgPhotonEtaMax || mctrack.pt() < recoQASettingsGroup.cfgPhotonPtMin) { + continue; + } + if (!mctrack.has_daughters()) { + continue; + } + int idPos = -1, idNeg = -1; + for (const auto& daughterId : mctrack.daughtersIds()) { + if (daughterId < 0 || daughterId >= static_cast(mcparticles.size())) { + continue; + } + const auto daughter = mcparticles.iteratorAt(daughterId); + if (daughter.pdgCode() == -PDG_t::kElectron) { + idPos = daughterId; + } else if (daughter.pdgCode() == PDG_t::kElectron) { + idNeg = daughterId; + } + } + if (idPos < 0 || idNeg < 0) { + continue; // not an e+e- conversion + } + const auto daughterPos = mcparticles.iteratorAt(idPos); + const auto daughterNeg = mcparticles.iteratorAt(idNeg); + const float rConv = std::sqrt(std::pow(daughterPos.vx(), 2) + std::pow(daughterPos.vy(), 2)); + if (rConv < recoQASettingsGroup.cfgRMinGen || rConv > recoQASettingsGroup.cfgRMaxGen) { + continue; + } + if (std::fabs(daughterPos.eta()) > recoQASettingsGroup.cfgLegEtaMax || std::fabs(daughterNeg.eta()) > recoQASettingsGroup.cfgLegEtaMax) { + continue; + } + + fRegistry.fill(HIST("RecoQA/0_converted/hs"), mctrack.pt(), mctrack.eta(), mctrack.phi(), rConv); + fRegistry.fill(HIST("RecoQA/hStageVsPt"), mctrack.pt(), 0.f); + + auto countIn = [](std::unordered_map const& m, int key) -> int { + const auto it = m.find(key); + return (it == m.end()) ? 0 : it->second; + }; + const int nTrkPos = countIn(nTracksOfMcId, idPos); + const int nTrkNeg = countIn(nTracksOfMcId, idNeg); + const int nLegPos = countIn(nV0LegsOfMcId, idPos); + const int nLegNeg = countIn(nV0LegsOfMcId, idNeg); + const bool posTracked = nTrkPos > 0; + const bool negTracked = nTrkNeg > 0; + const bool posInV0Legs = nLegPos > 0; + const bool negInV0Legs = nLegNeg > 0; + + fRegistry.fill(HIST("RecoQA/Duplicates/hNTracksPerTrueLeg"), nTrkPos); + fRegistry.fill(HIST("RecoQA/Duplicates/hNTracksPerTrueLeg"), nTrkNeg); + fRegistry.fill(HIST("RecoQA/Duplicates/hNV0LegsPerTrueLeg"), nLegPos); + fRegistry.fill(HIST("RecoQA/Duplicates/hNV0LegsPerTrueLeg"), nLegNeg); + fRegistry.fill(HIST("RecoQA/Duplicates/hNV0CandsPerPhoton_Rconv"), countIn(nV0CandsOfMcPhoton, static_cast(mctrack.globalIndex())), rConv); + + if (posTracked && negTracked) { + fRegistry.fill(HIST("RecoQA/1_bothLegsTracked/hs"), mctrack.pt(), mctrack.eta(), mctrack.phi(), rConv); + fRegistry.fill(HIST("RecoQA/hStageVsPt"), mctrack.pt(), 1.f); + } + if (posInV0Legs && negInV0Legs) { + fRegistry.fill(HIST("RecoQA/2_bothLegsInV0Legs/hs"), mctrack.pt(), mctrack.eta(), mctrack.phi(), rConv); + fRegistry.fill(HIST("RecoQA/hStageVsPt"), mctrack.pt(), 2.f); + } + if (countIn(nV0CandsOfMcPhoton, static_cast(mctrack.globalIndex())) > 0) { + fRegistry.fill(HIST("RecoQA/3_photonInV0PhotonsKF/hs"), mctrack.pt(), mctrack.eta(), mctrack.phi(), rConv); + fRegistry.fill(HIST("RecoQA/hStageVsPt"), mctrack.pt(), 3.f); + } + if (posTracked && !posInV0Legs) { + fRegistry.fill(HIST("RecoQA/hMissingLeg_PtRconv"), daughterPos.pt(), rConv); + } + if (negTracked && !negInV0Legs) { + fRegistry.fill(HIST("RecoQA/hMissingLeg_PtRconv"), daughterNeg.pt(), rConv); + } + + if (recoQASettingsGroup.cfgDoDetailedTrackQA) { + for (const auto& legMcId : {idPos, idNeg}) { + if (countIn(nTracksOfMcId, legMcId) == 0) { + continue; + } + const auto itTrack = mcIdToBestTrack.find(legMcId); + if (itTrack == mcIdToBestTrack.end() || itTrack->second < 0 || itTrack->second >= static_cast(tracks.size())) { + continue; + } + const auto track = tracks.iteratorAt(itTrack->second); + if (countIn(nV0LegsOfMcId, legMcId) > 0) { + fillLegQualityRecoQA(track); + } else { + fillLegQualityRecoQA(track); + } + } + } + } // end of mc particle loop per true event + } // end of true event loop + } // end of reco QA process PROCESS_SWITCH(PCMQC, processQC, "run PCM QC", true); PROCESS_SWITCH(PCMQC, processQCML, "run PCM QC with ML", false); - PROCESS_SWITCH(PCMQC, processDummy, "Dummy function", false); + PROCESS_SWITCH(PCMQC, processPCMQCMC, "run PCM QC in MC", false); + PROCESS_SWITCH(PCMQC, processPCMQCMCML, "run PCM QC in MC with ML cuts", false); + PROCESS_SWITCH(PCMQC, processGen, "run generated information", false); + PROCESS_SWITCH(PCMQC, processRecoQA, "run single-photon reconstruction QA on AO2Ds", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"pcm-qc"})}; + adaptAnalysisTask(context, TaskName{"pcm-qc"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/phosQC.cxx b/PWGEM/PhotonMeson/Tasks/phosQC.cxx index 56c7adb50b6..b4302803248 100644 --- a/PWGEM/PhotonMeson/Tasks/phosQC.cxx +++ b/PWGEM/PhotonMeson/Tasks/phosQC.cxx @@ -30,10 +30,9 @@ #include #include -#include #include -#include +#include #include #include #include @@ -72,28 +71,31 @@ struct phosQC { THashList* list_cluster = reinterpret_cast(fMainList->FindObject("Cluster")); for (const auto& cut : fPHOSCuts) { - const char* cutname = cut.GetName(); - o2::aod::pwgem::photon::histogram::AddHistClass(list_cluster, cutname); + std::string cutname = cut.getName(); + o2::aod::pwgem::photon::histogram::AddHistClass(list_cluster, cutname.c_str()); } // for Clusters for (auto& cut : fPHOSCuts) { - std::string_view cutname = cut.GetName(); - THashList* list = reinterpret_cast(fMainList->FindObject("Cluster")->FindObject(cutname.data())); + const std::string& cutname = cut.getName(); + THashList* list = reinterpret_cast(fMainList->FindObject("Cluster")->FindObject(cutname.c_str())); o2::aod::pwgem::photon::histogram::DefineHistograms(list, "Cluster", "PHOS"); } } void DefineCuts() { - TString cutNamesStr = fConfigPHOSCuts.value; - if (!cutNamesStr.IsNull()) { - std::unique_ptr objArray(cutNamesStr.Tokenize(",")); - for (int icut = 0; icut < objArray->GetEntries(); ++icut) { - const char* cutname = objArray->At(icut)->GetName(); - LOGF(info, "add cut : %s", cutname); - fPHOSCuts.push_back(*phoscuts::GetCut(cutname)); - } + if (fConfigPHOSCuts.value.empty()) { + return; + } + + std::string_view namesView(fConfigPHOSCuts.value); + + for (auto name : namesView | std::views::split(',')) { + std::string cutString(name.begin(), name.end()); + const char* cutname = cutString.c_str(); + LOGF(info, "add PHOS cut : %s", cutname); + fPHOSCuts.push_back(*phoscuts::GetCut(cutname)); } LOGF(info, "Number of PHOS cuts = %d", fPHOSCuts.size()); } @@ -140,7 +142,7 @@ struct phosQC { auto clusters_per_coll = clusters.sliceBy(perCollision, collision.collisionId()); for (const auto& cut : fPHOSCuts) { - THashList* list_cluster_cut = static_cast(list_cluster->FindObject(cut.GetName())); + THashList* list_cluster_cut = static_cast(list_cluster->FindObject(cut.getName().c_str())); int ng = 0; for (auto& cluster : clusters_per_coll) { @@ -149,7 +151,7 @@ struct phosQC { ng++; } } // end of v0 loop - reinterpret_cast(fMainList->FindObject("Cluster")->FindObject(cut.GetName())->FindObject("hNgamma"))->Fill(ng); + reinterpret_cast(fMainList->FindObject("Cluster")->FindObject(cut.getName().c_str())->FindObject("hNgamma"))->Fill(ng); } // end of cut loop } // end of collision loop } // end of process diff --git a/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx b/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx index a9b4d5ed81d..a51c8533f95 100644 --- a/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonResoTask.cxx @@ -28,12 +28,12 @@ #include #include -#include -#include #include #include +#include #include #include +#include #include #include #include @@ -49,6 +49,7 @@ #include #include +#include #include #include #include @@ -63,9 +64,9 @@ using namespace o2::aod::pwgem::photon; enum CentralityEstimator { None = 0, CFT0A = 1, - CFT0C, - CFT0M, - NCentralityEstimators + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 }; enum class MapLevel { @@ -76,11 +77,7 @@ enum class MapLevel { }; struct PhotonResoTask { - static constexpr float MinEnergy = 0.7f; - o2::framework::Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - o2::framework::Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - o2::framework::Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; o2::framework::Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; // configurable axis @@ -92,7 +89,7 @@ struct PhotonResoTask { ConfigurableAxis thnConfigAxisPhiRelative{"thnConfigAxisPhiRelative", {300, -1., 2.}, "(phi rec - phi true) / phi true axis"}; ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {20, 0., 100.}, "centrality axis for the current event"}; ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; - Configurable useCent{"useCent", 0, "flag to enable usage of centrality instead of multiplicity as axis."}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; EMPhotonEventCut fEMEventCut; struct : ConfigurableGroup { @@ -180,12 +177,12 @@ struct PhotonResoTask { SliceCache cache; - using EMCalPhotons = soa::Join; + using EMCalPhotons = soa::Join; using PcmPhotons = soa::Join; using PcmMcLegs = soa::Join; - using Colls = soa::Join; + using Colls = soa::Join; using McColls = o2::soa::Join; using McParticles = EMMCParticles; @@ -197,7 +194,7 @@ struct PhotonResoTask { HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - o2::framework::Service ccdb; + o2::framework::Service ccdb{}; int mRunNumber{-1}; float dBz{0.f}; @@ -360,27 +357,11 @@ struct PhotonResoTask { if (mRunNumber == collision.runNumber()) { return; } + mRunNumber = collision.runNumber(); - auto run3GrpTimestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = nullptr; - o2::parameters::GRPMagField* grpmag = nullptr; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3GrpTimestamp); - if (grpo) { - // Fetch magnetic field from ccdb for current collision - dBz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3GrpTimestamp << " with magnetic field of " << dBz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3GrpTimestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3GrpTimestamp; - } - // Fetch magnetic field from ccdb for current collision - dBz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3GrpTimestamp << " with magnetic field of " << dBz << " kZG"; - } + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << dBz << " kZG"; fV0PhotonCut.SetD_Bz(dBz); - mRunNumber = collision.runNumber(); } template @@ -489,7 +470,7 @@ struct PhotonResoTask { if (!(emcFlags.test(photonEMC.globalIndex()))) { continue; } - if (photonEMC.emmcparticleIds().size() <= 0) { + if (photonEMC.emmcparticleIds().empty()) { // this is a cluster with just noise, skip continue; } @@ -508,6 +489,7 @@ struct PhotonResoTask { int32_t mcConvLegMotherId = -1; bool hasBothLegs = false; if (mcPhoton1MotherId == -1) { + // mother was not a photon so skip this cluster continue; } // mcPhoton1 now points to a photon that produced the e+/e- that was the largest contributor in the cluster @@ -599,7 +581,7 @@ struct PhotonResoTask { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); } - if (g1.emmcparticleIds().size() <= 0 || g2.emmcparticleIds().size() <= 0) { + if (g1.emmcparticleIds().empty() || g2.emmcparticleIds().empty()) { // there is a cluster which is just noise, skip continue; } @@ -639,7 +621,7 @@ struct PhotonResoTask { }; // End struct PhotonResoTask -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx index 0abc24b3cad..23cb1db4a70 100644 --- a/PWGEM/PhotonMeson/Tasks/photonhbt.cxx +++ b/PWGEM/PhotonMeson/Tasks/photonhbt.cxx @@ -14,38 +14,53 @@ /// \author Daiki Sekihata, daiki.sekihata@cern.ch /// Stefanie Mrozinski, stefanie.mrozinski@cern.ch -#include "PWGEM/Dilepton/Utils/EMTrack.h" #include "PWGEM/Dilepton/Utils/EventMixingHandler.h" #include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" #include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" #include "PWGEM/PhotonMeson/DataModel/EventTables.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" +#include "PWGEM/PhotonMeson/Utils/PairUtilities.h" #include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include #include +#include +#include +#include +#include #include #include +#include #include +#include #include #include #include #include +#include #include #include #include +#include #include -#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include // IWYU pragma: keep #include -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include // IWYU pragma: keep #include +#include +#include #include #include +#include + #include #include #include @@ -55,7 +70,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -65,163 +82,285 @@ #include #include -/// Single-photon track-type combo. -enum class V0Combo : int { - Inclusive = 0, - ItstpcItstpc = 1, - ItstpcTpconly = 2, - TpconlyTpconly = 3, -}; - -/// Photon-pair track-type combo. -enum class PairCombo : int { - Inclusive = 0, - IiXIi = 1, - IiXIt = 2, - IiXTt = 3, - ItXIt = 4, - ItXTt = 5, - TtXTt = 6, -}; - using namespace o2; using namespace o2::aod; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::dilepton::utils; +namespace pairutil = o2::aod::pwgem::photonmeson::utils::pairutil; +using namespace o2::pwgem::photonmeson; + +// ─── Event Information Tables ──────────────────────────────────────────── -using MyCollisions = soa::Join; -using MyCollisionsMC = soa::Join; +using MyCollisions = soa::Join; +using MyCollisionsMC = soa::Join; using MyCollision = MyCollisions::iterator; +// ─── Photon Tables ──────────────────────────────────────────── + using MyV0Photons = soa::Join; using MyV0Photon = MyV0Photons::iterator; +// ─── Assoc. Track Tables ──────────────────────────────────────────── + using MyMCV0Legs = soa::Join; using MyMCV0Leg = MyMCV0Legs::iterator; -// ─── MC truth classification types ──────────────────────────────────────────── - -struct PhotonMCInfo { - bool hasMC = false; - bool sameMother = false; - bool isTruePhoton = false; - int mcPosId = -1; - int mcNegId = -1; - int motherId = -1; - int motherPdg = 0; - bool isPhysicalPrimary = false; +static constexpr float kMinMagnitude = 1e-12f; +static constexpr float kMinCosine = 1e-12f; +static constexpr float kMinSigma = 1e-9f; + +static constexpr std::array kPhiStarRadiiM = { + 0.85f, 0.95f, 1.05f, 1.15f, 1.25f, 1.35f, 1.45f, 1.55f, 1.65f, + 1.75f, 1.85f, 1.95f, 2.05f, 2.15f, 2.25f, 2.35f, 2.45f}; +static constexpr float kPhiStarInvalid = -999.f; +static constexpr float kCmPerM = 100.f; + +struct LegHelix { + float phiStar = kPhiStarInvalid; // azimuth of the track at that radius + float sxy = -1.f; + int status = 3; // 0 ok, 1 below R_conv, 2 out of reach,3 bad input }; -enum class PairTruthType : uint8_t { - Unknown = 0, - TrueTrueDistinct, - TrueTrueSamePhoton, - SharedMcLeg, - TrueFake, - FakeFake, - Pi0Daughters, -}; +[[nodiscard]] static LegHelix legHelixAt(float vxM, float vyM, float phiMom, float pt, int charge, float bzT, float radiusM) +{ + LegHelix out; + if (pt < kMinSigma || std::fabs(bzT) < kMinSigma) { + out.status = 3; // o2-linter: disable=magic-number (status code) + return out; + } + const float rStart = std::hypot(vxM, vyM); + if (rStart < kMinSigma) { + out.status = 3; // o2-linter: disable=magic-number (status code) + return out; + } + if (radiusM < rStart) { + out.status = 1; + return out; + } + const float rho = pt / (0.3f * std::fabs(bzT)); // curvature radius, m + const float sgn = (bzT >= 0.f ? 1.f : -1.f) * static_cast(charge); + const float cx = vxM + sgn * rho * std::sin(phiMom); + const float cy = vyM - sgn * rho * std::cos(phiMom); + const float d = std::hypot(cx, cy); + if (d < kMinSigma) { + out.status = 3; // o2-linter: disable=magic-number (status code) + return out; + } + if (radiusM > d + rho || radiusM < std::fabs(d - rho)) { + out.status = 2; // o2-linter: disable=magic-number (status code) + return out; + } + const float phiC = std::atan2(cy, cx); + auto clamp1 = [](float x) { return std::max(-1.f, std::min(1.f, x)); }; + auto alphaAt = [&](float r) { return std::acos(clamp1((r * r + d * d - rho * rho) / (2.f * r * d))); }; + auto betaAt = [&](float r) { return std::acos(clamp1((d * d + rho * rho - r * r) / (2.f * d * rho))); }; + + const float phiVtx = std::atan2(vyM, vxM); + const float a0 = alphaAt(rStart); + const float resPlus = std::fabs(RecoDecay::constrainAngle(phiC + a0 - phiVtx, -o2::constants::math::PI)); + const float resMinus = std::fabs(RecoDecay::constrainAngle(phiC - a0 - phiVtx, -o2::constants::math::PI)); + const float branch = (resPlus < resMinus) ? 1.f : -1.f; + + out.phiStar = RecoDecay::constrainAngle(phiC + branch * alphaAt(radiusM), 0.f); + out.sxy = rho * std::fabs(betaAt(radiusM) - betaAt(rStart)); + out.status = 0; + return out; +} -static constexpr float kMinMagnitude = 1e-12f; -static constexpr float kMinCosine = 1e-12f; -static constexpr float kMinSigma = 1e-9; +[[nodiscard]] inline float selfTestLegHelix() +{ + float worst = 0.f; + constexpr float eps = 1.e-5f; -struct Photonhbt { + for (const auto& bz : {0.5f, -0.5f}) { + for (const auto& q : {+1, -1}) { + for (const auto& pt : {0.1f, 0.2f, 0.5f}) { + for (const auto& r : {0.85f, 1.45f, 2.45f}) { + for (const auto& phi : {0.f, 1.f, -2.f}) { + const auto h = legHelixAt(eps * std::cos(phi), eps * std::sin(phi), phi, pt, q, bz, r); - template - static inline V0Combo classifyV0Combo(TGamma const& g) - { - const auto pos = g.template posTrack_as(); - const auto neg = g.template negTrack_as(); - const bool posII = pos.hasITS() && pos.hasTPC(); - const bool posTPC = !pos.hasITS() && pos.hasTPC(); - const bool negII = neg.hasITS() && neg.hasTPC(); - const bool negTPC = !neg.hasITS() && neg.hasTPC(); - if (posII && negII) - return V0Combo::ItstpcItstpc; - if ((posII && negTPC) || (posTPC && negII)) - return V0Combo::ItstpcTpconly; - if (posTPC && negTPC) - return V0Combo::TpconlyTpconly; - return V0Combo::Inclusive; - } + if (h.status != 0) { + continue; + } + const float arg = + -0.3f * bz * static_cast(q) * r / (2.f * pt); - static inline PairCombo classifyPairCombo(V0Combo c1, V0Combo c2) - { - const int i1 = static_cast(c1); - const int i2 = static_cast(c2); - if (i1 <= 0 || i2 <= 0) - return PairCombo::Inclusive; - const int lo = std::min(i1, i2); - const int hi = std::max(i1, i2); - static constexpr std::array, 4> kTable = { - {{0, 0, 0, 0}, {0, 1, 2, 3}, {0, 2, 4, 5}, {0, 3, 5, 6}}}; - return static_cast(kTable[lo][hi]); + if (std::fabs(arg) >= 1.f) { + continue; + } + const float referencePhi = + RecoDecay::constrainAngle(phi + std::asin(arg), 0.f); + + worst = std::max( + worst, + std::fabs(RecoDecay::constrainAngle( + h.phiStar - referencePhi, + -o2::constants::math::PI))); + } + } + } + } } - // ─── Configurables ──────────────────────────────────────────────────────── + return worst; +} - ConfigurableAxis confQBins{"confQBins", {60, 0, +0.3f}, "q bins for output histograms"}; - ConfigurableAxis confKtBins{"confKtBins", {VARIABLE_WIDTH, 0.0, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75}, "kT bins"}; - ConfigurableAxis confPtBins{"confPtBins", {100, 0.f, 2.f}, "pT bins (GeV/c)"}; - ConfigurableAxis confEtaBins{"confEtaBins", {80, -0.8f, 0.8f}, "eta bins"}; - ConfigurableAxis confPhiBins{"confPhiBins", {90, 0.f, o2::constants::math::TwoPI}, "phi bins (rad)"}; - ConfigurableAxis confDeltaEtaBins{"confDeltaEtaBins", {180, -1.6f, +1.6f}, "Delta-eta bins"}; - ConfigurableAxis confDeltaPhiBins{"confDeltaPhiBins", {180, -o2::constants::math::PI, o2::constants::math::PI}, "Delta-phi bins (rad)"}; - ConfigurableAxis confEllipseValBins{"confEllipseValBins", {200, 0.f, 10.f}, "ellipse value bins"}; - ConfigurableAxis confCosThetaBins{"confCosThetaBins", {100, 0.f, 1.f}, "cos(theta*) bins"}; - ConfigurableAxis confOpeningAngleBins{"confOpeningAngleBins", {100, 0.f, o2::constants::math::PI}, "opening angle bins (rad)"}; - ConfigurableAxis confRBins{"confRBins", {100, 0.f, 100.f}, "conversion radius bins (cm)"}; - ConfigurableAxis confDeltaRBins{"confDeltaRBins", {120, 0.f, 30.f}, "|R1-R2| bins (cm)"}; - ConfigurableAxis confDeltaR3DBins{"confDeltaR3DBins", {100, 0.f, 100.f}, "3D distance between conversion points (cm)"}; - ConfigurableAxis confDeltaRxyBins{"confDeltaRxyBins", {100, 0.f, 100.f}, "xy distance between conversion points (cm)"}; - ConfigurableAxis confZConvBins{"confZConvBins", {200, -100.f, 100.f}, "conversion z (cm)"}; - ConfigurableAxis confDeltaZBins{"confDeltaZBins", {200, -100.f, 100.f}, "#Deltaz bins (cm)"}; - ConfigurableAxis confOccupancyQA{"confOccupancyQA", {100, 0.f, 50000.f}, "occupancy"}; - ConfigurableAxis confCentQABins{"confCentQABins", {110, 0.f, 110.f}, "centrality (%)"}; +struct Photonhbt { - // ─── Axis specs ──────────────────────────────────────────────────────────── - - const AxisSpec axisKt{confKtBins, "k_{T} (GeV/c)"}; - const AxisSpec axisQinv{confQBins, "q_{inv} (GeV/c)"}; - const AxisSpec axisQabsLcms{confQBins, "|#bf{q}|^{LCMS} (GeV/c)"}; - const AxisSpec axisQout{confQBins, "q_{out} (GeV/c)"}; - const AxisSpec axisQside{confQBins, "q_{side} (GeV/c)"}; - const AxisSpec axisQlong{confQBins, "q_{long} (GeV/c)"}; - const AxisSpec axisPt{confPtBins, "p_{T} (GeV/c)"}; - const AxisSpec axisEta{confEtaBins, "#eta"}; - const AxisSpec axisPhi{confPhiBins, "#phi (rad)"}; - const AxisSpec axisDeltaEta{confDeltaEtaBins, "#Delta#eta"}; - const AxisSpec axisDeltaPhi{confDeltaPhiBins, "#Delta#phi (rad)"}; - const AxisSpec axisEllipseVal{confEllipseValBins, "(#Delta#eta/#sigma_{#eta})^{2}+(#Delta#phi/#sigma_{#phi})^{2}"}; - const AxisSpec axisCosTheta{confCosThetaBins, "cos(#theta*)"}; - const AxisSpec axisOpeningAngle{confOpeningAngleBins, "Opening angle (rad)"}; - const AxisSpec axisR{confRBins, "R_{conv} (cm)"}; - const AxisSpec axisDeltaR{confDeltaRBins, "|R_{1}-R_{2}| (cm)"}; - const AxisSpec axisDeltaR3D{confDeltaR3DBins, "|#vec{r}_{1}-#vec{r}_{2}| (cm)"}; - const AxisSpec axisDeltaRxy{confDeltaRxyBins, "#Delta r_{xy} (cm)"}; - const AxisSpec axisZConv{confZConvBins, "z_{conv} (cm)"}; - const AxisSpec axisDeltaZ{confDeltaZBins, "#Delta z (cm)"}; - const AxisSpec axisOccupancy{confOccupancyQA, "occupancy"}; - const AxisSpec axisCentQA{confCentQABins, "centrality (%)"}; + /*************************************************/ + // TYPES + /*************************************************/ - // ─── Configurables: QA flags ─────────────────────────────────────────────── + struct PairQAObservables { + ROOT::Math::PtEtaPhiMVector v1, v2, k12; + float x1 = 0.f, y1 = 0.f, z1 = 0.f, x2 = 0.f, y2 = 0.f, z2 = 0.f; + float r1 = 0.f, r2 = 0.f, dx = 0.f, dy = 0.f, dz = 0.f; + float deltaR = 0.f, deltaZ = 0.f, deltaRxy = 0.f, deltaR3D = 0.f; + float opa = 0.f, cosOA = 0.f, drOverCosOA = 0.f; + float deta = 0.f, dphi = 0.f, pairEta = 0.f, pairPhi = 0.f; + float kt = 0.f, qinv = 0.f, cosTheta = 0.f, openingAngle = 0.f; + bool valid = true; + }; + + struct LegPairObservables { + float dEtaPP = 0.f, dPhiPP = 0.f, dRPP = 0.f; + float dEtaNN = 0.f, dPhiNN = 0.f, dRNN = 0.f; + float dEtaPN12 = 0.f, dPhiPN12 = 0.f, dRPN12 = 0.f; + float dEtaNP12 = 0.f, dPhiNP12 = 0.f, dRNP12 = 0.f; + float minDR_LS = 0.f; + float minLegPt_PP = 0.f, maxLegPt_PP = 0.f; + float minLegPt_NN = 0.f, maxLegPt_NN = 0.f; + float ptG1 = 0.f; + float ptG2 = 0.f; + }; + + struct PairSep { + float dMin3D{999.f}, dRPhiAtMin3D{999.f}, dZAtMin3D{999.f}; + float dMinRPhi{999.f}, dZAtMinRPhi{999.f}; + std::array fCloseRPhiThr{}; + std::array nCloseRPhiThr{}; + float fCloseRPhi{0.f}; // == fCloseRPhiThr[3] + int nCommon{0}; + int nSameSide{0}; // usable radii on the SAME TPC side + int criticalCharge{-1}; // 0 = e+e+, 1 = e-e-, chosen by d_3D + int rphiCharge{-1}; // chosen by d_rphi alone + std::array dMin3DPerCharge{999.f, 999.f}; + std::array fCloseRPhiPerCharge{0.f, 0.f}; + std::array dMinRPhiPerCharge{999.f, 999.f}; + std::array nSameSidePerCharge{}; + std::array, 2> nCloseRPhiThrPerCharge{}; + std::array, 2> fCloseRPhiThrPerCharge{}; + float dMinRPhiAll{999.f}, dPairMinAll{999.f}; + float dPairMin{999.f}; + float dRPhiAtDPairMin{999.f}, dZAtDPairMin{999.f}; + float rAtDPairMin{-1.f}, absZAtDPairMin{-1.f}, driftLen{-1.f}; + int nCloseScaled{0}; + std::array dPairMinPerCharge{999.f, 999.f}; + float dZGlobalAtDPairMin{999.f}; + float dZSignedLocalAtDPairMin{999.f}, dZSignedGlobalAtDPairMin{999.f}; + float dVtxZ{0.f}; + float fCloseScaled{0.f}; + float driftOldAtDPairMin{-1.f}; // kept only to show the difference + bool sameSideAtDPairMin{true}; + int nLegsITSTPC{0}; + std::array nPoints{}; + std::array, 2> ptDRPhi{}, ptDZ{}, ptDZSgnLocal{}, ptR{}, ptDrift{}, ptSameSide{}; + }; + struct CrossObs { + // m(e+e-) of the two leg-swapped combinations + std::array mee{999.f, 999.f}; + float meeOverQ{999.f}; + }; + + struct TruthGamma { + int id = -1, posId = -1, negId = -1; + float eta = 0.f, phi = 0.f, pt = 0.f; + float rTrue = -1.f; + float legDRtrue = -1.f; + float legDEta = 0.f; + float legDPhi = 0.f; + float alphaTrue = 0.f; + std::array legPtTrue{}, legEtaTrue{}, legPhiTrue{}; + }; + + struct PhotonWithLegs { + float fPt{0}, fEta{0}, fPhi{0}; + float fVx{0}, fVy{0}, fVz{0}; + float fVtxZ{0}; + std::array fLegPt{}, fLegEta{}, fLegPhi{}; + std::array, 2> fLegPhiStar{}; + std::array, 2> fLegSxy{}; // transverse arc length since conversion (m) + [[nodiscard]] float pt() const { return fPt; } + [[nodiscard]] float eta() const { return fEta; } + [[nodiscard]] float phi() const { return fPhi; } + [[nodiscard]] float vx() const { return fVx; } + [[nodiscard]] float vy() const { return fVy; } + [[nodiscard]] float vz() const { return fVz; } + [[nodiscard]] float legEta(int i) const { return fLegEta[i]; } + [[nodiscard]] float legPhi(int i) const { return fLegPhi[i]; } + [[nodiscard]] float legPt(int i) const { return fLegPt[i]; } + std::array fLegNsigEl{}; // TPC electron nsigma per leg, for the mixed-event leg similarity + [[nodiscard]] float legNsigEl(int i) const { return fLegNsigEl[i]; } + int fNITSTPC{0}; + pairutil::V0PhotonLegCounts fLegCounts{}; + [[nodiscard]] pairutil::V0PhotonLegCounts const& legCounts() const { return fLegCounts; } + int fIsTruePhoton{-1}; + }; + + struct PhotonMCInfo { + bool hasMC = false; + bool sameMother = false; + bool isTruePhoton = false; + int mcPosId = -1; + int mcNegId = -1; + int motherId = -1; + int motherPdg = 0; + bool isPhysicalPrimary = false; + int posMotherId = -1; + int negMotherId = -1; + bool posMotherIsPhoton = false; + bool negMotherIsPhoton = false; + }; + + enum class PairTruthType : uint8_t { + Unknown = 0, + TrueTrueDistinct, + TrueTrueSamePhoton, + SharedMcLeg, + TrueFake, + FakeFake, + Pi0Daughters, + }; + + /*************************************************/ + // CONFIGURABLES + /*************************************************/ + + Service ccdb{}; + Configurable cfgCcdbUrl{"cfgCcdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; + Configurable cfgBzOverrideT{"cfgBzOverrideT", -999.f, "Bz in Tesla; used instead of CCDB if > -100"}; + float mBzT{0.f}; // signed, Tesla + float mVDriftCmPerNs{0.f}; // TPC drift velocity from CCDB; 0 = unavailable + + // ─── Configurables: QA flags ─────────────────────────────────────────────── struct : ConfigurableGroup { std::string prefix = "qaflags_group"; Configurable doPairQa{"doPairQa", true, "fill pair QA histograms at each cut step"}; + Configurable doLegPairQA{"doLegPairQA", true, "fill and book leg diagnostics"}; Configurable doSinglePhotonQa{"doSinglePhotonQa", true, "fill single-photon QA histograms (pT, eta, phi)"}; - Configurable cfgMaxQinvForQA{"cfgMaxQinvForQA", 0.1f, "fill per-step pair QA histograms only when q_inv < this value. Set <= 0 to disable."}; - Configurable cfgMaxQinvForFullRange{"cfgMaxQinvForFullRange", 0.3f, "fill full-range histograms only when q_inv < this value. Set <= 0 to disable."}; - Configurable cfgMaxQinvForMCQA{"cfgMaxQinvForMCQA", 0.3f, - "fill MC truth 1D histograms (hQinv, hKt, hDeltaEta, ...) only when q_inv < this value. " - "hDEtaDPhi is always filled (needs full sample). Set <= 0 to disable. Default 0.6 cuts " - "most combinatorics while covering well beyond the CF range for systematics."}; + Configurable cfgMaxQinvForQA{"cfgMaxQinvForQA", 0.1f, "fill per-step pair QA histograms only when q_inv < this value"}; + Configurable cfgMaxQinvForFullRange{"cfgMaxQinvForFullRange", 0.3f, "fill full-range histograms only when q_inv < this value"}; + Configurable cfgMaxQinvForMCQA{"cfgMaxQinvForMCQA", 0.3f, "fill MC truth 1D histograms only when q_inv < this value"}; + Configurable cfgQaLevel{"cfgQaLevel", 2, "QA: 0 no QA, 1 standard, 2 full diagnostics with leg information etc."}; + Configurable cfgFillDRDZSparse{"cfgFillDRDZSparse", true, "book/fill the FullRange |R1-R2|-Deltaz-qinv sparse (large; needs cfgQaLevel>=2)"}; + Configurable cfgMaxQinvForProcessing{"cfgMaxQinvForProcessing", 0.5, "skip mixed pairs with q_inv above this before building observables"}; + Configurable cfgFillLegPairSparses{"cfgFillLegPairSparses", false, "book/fill the 4D leg-pair QA sparses (very large)"}; + Configurable cfgFillLegSimilaritySparse{"cfgFillLegSimilaritySparse", true, "book/fill the like-sign leg similarity sparse (duplicate diagnostic: DeltaEta, DeltaPhi, pT ratio, dNsigma_e vs qinv)"}; + Configurable cfgFillLegSimilarityMixSparse{"cfgFillLegSimilarityMixSparse", false, "book/fill the MIXED-EVENT leg similarity sparse (kinematic baseline for the SE-ME duplicate excess; needs cfgFillLegSimilaritySparse)"}; + Configurable cfgFillR1R2Sparse{"cfgFillR1R2Sparse", true, "book/fill the FullRange (R1, R2, q_inv) sparse: fix one conversion radius, see where the partner sits, per q_inv"}; } qaflags; // ─── HBT analysis mode ─────────────────────────────────────────────────────────── @@ -229,9 +368,45 @@ struct Photonhbt { std::string prefix = "hbtanalysis_group"; Configurable cfgDo3D{"cfgDo3D", false, "enable 3D (qout,qside,qlong) analysis"}; Configurable cfgDo2D{"cfgDo2D", false, "enable 2D (qout,qinv) projection (requires cfgDo3D)"}; + Configurable cfgDo2DSideLong{"cfgDo2DSideLong", false, "additionally book/fill CF_2D_Side and CF_2D_Long (qside/qlong vs qinv; two extra 5D sparses -> costs fill time and merge size; requires cfgDo2D)"}; Configurable cfgUseLCMS{"cfgUseLCMS", false, "measure 1D relative momentum in LCMS"}; + Configurable cfgDoQinvGate3D{"cfgDoQinvGate3D", false, "book/fill CF_3D_Qinv: 3D LCMS CF with a COARSE qinv axis (edges = candidate gate values)"}; + ConfigurableAxis confMultNTracksBins{"confMultNTracksBins", {VARIABLE_WIDTH, 0., 100., 200., 300., 400., 500., 600, 700, 800, 900, 1000., 1100., 1200., 1300., 1400., 1500., 1600., 1700., 1800., 1900., 2000., 2100., 2200., 2300., 2400., 2500., 2600., 2700., 2800., 2900., 3000., 4000., 5000.}, "N_{tracks}^{PV} |#eta|<1 bins for CF sparses"}; + ConfigurableAxis confMultFT0MBins{"confMultFT0MBins", {VARIABLE_WIDTH, 0., 25., 50., 100., 200., 400., 800., 2000., 2500., 3000., 3500., 4000., 5000., 6000., 7000., 8000., 9000., 10000., 15000., 40000., 100000., 250000.}, "FT0M amplitude bins for CF sparses"}; } hbtanalysis; + // ----- Photon Leg Classification + + Configurable cfgDoPhotonClassPairCut{"cfgDoPhotonClassPairCut", false, "apply photon-class pair selection A x B (leg track composition)"}; + + struct : ConfigurableGroup { + std::string prefix = "photonclassA_group"; + Configurable cfgMinNLegsITSTPC{"cfgMinNLegsITSTPC", 0, "min number of ITS-TPC legs (0-2)"}; + Configurable cfgMaxNLegsITSTPC{"cfgMaxNLegsITSTPC", 2, "max number of ITS-TPC legs (0-2)"}; + Configurable cfgMinNLegsITSOnly{"cfgMinNLegsITSOnly", 0, "min number of ITS-only legs (0-2)"}; + Configurable cfgMaxNLegsITSOnly{"cfgMaxNLegsITSOnly", 2, "max number of ITS-only legs (0-2)"}; + Configurable cfgMinNLegsTPCOnly{"cfgMinNLegsTPCOnly", 0, "min number of TPC-only legs (0-2)"}; + Configurable cfgMaxNLegsTPCOnly{"cfgMaxNLegsTPCOnly", 2, "max number of TPC-only legs (0-2)"}; + Configurable cfgMinNLegsTRD{"cfgMinNLegsTRD", 0, "min number of legs with TRD (0-2)"}; + Configurable cfgMaxNLegsTRD{"cfgMaxNLegsTRD", 2, "max number of legs with TRD (0-2)"}; + Configurable cfgMinNLegsTOF{"cfgMinNLegsTOF", 0, "min number of legs with TOF (0-2)"}; + Configurable cfgMaxNLegsTOF{"cfgMaxNLegsTOF", 2, "max number of legs with TOF (0-2)"}; + } photonclassA; + + struct : ConfigurableGroup { + std::string prefix = "photonclassB_group"; + Configurable cfgMinNLegsITSTPC{"cfgMinNLegsITSTPC", 0, "min number of ITS-TPC legs (0-2)"}; + Configurable cfgMaxNLegsITSTPC{"cfgMaxNLegsITSTPC", 2, "max number of ITS-TPC legs (0-2)"}; + Configurable cfgMinNLegsITSOnly{"cfgMinNLegsITSOnly", 0, "min number of ITS-only legs (0-2)"}; + Configurable cfgMaxNLegsITSOnly{"cfgMaxNLegsITSOnly", 2, "max number of ITS-only legs (0-2)"}; + Configurable cfgMinNLegsTPCOnly{"cfgMinNLegsTPCOnly", 0, "min number of TPC-only legs (0-2)"}; + Configurable cfgMaxNLegsTPCOnly{"cfgMaxNLegsTPCOnly", 2, "max number of TPC-only legs (0-2)"}; + Configurable cfgMinNLegsTRD{"cfgMinNLegsTRD", 0, "min number of legs with TRD (0-2)"}; + Configurable cfgMaxNLegsTRD{"cfgMaxNLegsTRD", 2, "max number of legs with TRD (0-2)"}; + Configurable cfgMinNLegsTOF{"cfgMinNLegsTOF", 0, "min number of legs with TOF (0-2)"}; + Configurable cfgMaxNLegsTOF{"cfgMaxNLegsTOF", 2, "max number of legs with TOF (0-2)"}; + } photonclassB; + // ─── Event mixing ───────────────────────────────────────────────────────────── struct : ConfigurableGroup { std::string prefix = "mixing_group"; @@ -241,7 +416,6 @@ struct Photonhbt { Configurable cfgEP2EstimatorForMix{"cfgEP2EstimatorForMix", 3, "FT0M:0, FT0A:1, FT0C:2, FV0A:3, BTot:4, BPos:5, BNeg:6"}; Configurable cfgOccupancyEstimator{"cfgOccupancyEstimator", 0, "FT0C:0, Track:1"}; Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; - ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis confCentBins{"confCentBins", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f, 999.f}, "Mixing bins - centrality"}; ConfigurableAxis confEPBinsBins{"confEPBinsBins", {16, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}, "Mixing bins - EP angle"}; @@ -266,6 +440,8 @@ struct Photonhbt { "min pT for true photons in truth-efficiency loop (GeV/c); " "0 = fall back to pcmcuts.cfgMinPtV0"}; Configurable cfgMCMinLegPt{"cfgMCMinLegPt", 0.0f, "min pT for true e^{+}/e^{-} legs in truth-efficiency loop (GeV/c);"}; + Configurable cfgMCAODBzkG{"cfgMCAODBzkG", -5.f, "B field (kG, signed) for analytic V0 kinematics in processMCAOD"}; + Configurable cfgMCAODScoreWeight{"cfgMCAODScoreWeight", 0.5f, "score weight w: w*cosPA-term + (1-w)*pca-term, as in the builder"}; } mctruth; struct : ConfigurableGroup { @@ -292,13 +468,41 @@ struct Photonhbt { Configurable cfgDoZCut{"cfgDoZCut", false, "apply |DeltaZ| > cfgMinDeltaZ cut"}; Configurable cfgMinDeltaZ{"cfgMinDeltaZ", 0.f, "minimum |DeltaZ| (cm)"}; Configurable cfgDoEllipseCut{"cfgDoEllipseCut", false, "reject pairs inside ellipse in DeltaEta-DeltaPhi"}; - Configurable cfgEllipseSigEta{"cfgEllipseSigEta", 0.02f, "sigma_eta for ellipse cut"}; - Configurable cfgEllipseSigPhi{"cfgEllipseSigPhi", 0.02f, "sigma_phi for ellipse cut"}; + Configurable cfgEllipseSigEta{"cfgEllipseSigEta", 0.1f, "sigma_eta for ellipse cut"}; + Configurable cfgEllipseSigPhi{"cfgEllipseSigPhi", 0.1f, "sigma_phi for ellipse cut"}; Configurable cfgEllipseR2{"cfgEllipseR2", 1.0f, "R^2 threshold: reject if ellipse value < R^2"}; Configurable cfgMaxAsymmetry{"cfgMaxAsymmetry", -1.f, "max |p_{T, 1} - p_{T, 2}|/(p_{T, 1} + p_{T, 2}) asymmetry cut"}; } ggpaircuts; - EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "pairsep_group"; + Configurable cfgDoPairSepQA{"cfgDoPairSepQA", true, "fill SE/ME same-sign leg separation histograms"}; + Configurable cfgPairSepMaxQinv{"cfgPairSepMaxQinv", 0.5f, "fill PairSep histograms only for qinv < this value (<=0: no gate)"}; + Configurable cfgSigRPhi{"cfgSigRPhi", 1.f, "rphi scale of the pair-merging metric (cm)"}; + Configurable cfgSigZ{"cfgSigZ", 1.f, "z scale of the pair-merging metric (cm)"}; + Configurable cfgSigZNs{"cfgSigZNs", -1.f, "z scale in ns of drift time; <=0 keeps cfgSigZ in cm"}; + Configurable cfgDPairClose{"cfgDPairClose", 1.f, "a sampled radius counts as close when D_pair < this"}; + Configurable> cfgCloseThrCm{"cfgCloseThrCm", {0.5f, 1.0f, 1.5f, 2.0f}, "four d_rphi thresholds (cm) for N_close/f_close"}; + Configurable cfgDoPairMergeCut{"cfgDoPairMergeCut", false, "apply the pair-merging cut to SE AND ME"}; + Configurable cfgDPairCut{"cfgDPairCut", 1.f, "reject pairs with min D_pair below this (needs cfgDoPairMergeCut)"}; + Configurable cfgNCloseCut{"cfgNCloseCut", 0, "reject pairs with at least this many close radii; 0 disables"}; + Configurable cfgTpcHalfLengthCm{"cfgTpcHalfLengthCm", 250.f, "TPC half length, used for drift length = halfLength - |z|"}; + Configurable cfgRegionEdge1{"cfgRegionEdge1", 132.f, "radius (cm) separating region 0 from 1 (approx. IROC / OROC1)"}; + Configurable cfgRegionEdge2{"cfgRegionEdge2", 171.f, "radius (cm) separating region 1 from 2"}; + Configurable cfgRegionEdge3{"cfgRegionEdge3", 208.f, "radius (cm) separating region 2 from 3"}; + Configurable cfgCondQinvMin{"cfgCondQinvMin", 0.10f, "control window for the (d_rphi, d_z) calibration map: min qinv"}; + Configurable cfgCondQinvMax{"cfgCondQinvMax", 0.30f, "control window for the (d_rphi, d_z) calibration map: max qinv"}; + Configurable cfgFillCalibSparse{"cfgFillCalibSparse", true, "fill the per-radius calibration sparse (d_rphi, d_z, region, drift, qinv)"}; + } pairsep; + + struct : ConfigurableGroup { + std::string prefix = "crosspair_group"; + Configurable cfgDoCrossPairQA{"cfgDoCrossPairQA", false, "fill crossed-hypothesis QA (mee_cross, dist_cross vs qinv) for pairs passing all other pair cuts; SE and ME"}; + Configurable cfgDoCrossPairCut{"cfgDoCrossPairCut", false, "reject pairs with a photon-like crossed combination; applied to SE AND ME"}; + Configurable cfgCrossMaxMeeRatio{"cfgCrossMaxMeeRatio", 0.2f, "veto: min(m_ee^cross)/q_inv below this; genuine pairs sit near 0.5, leg swaps near 0"}; + Configurable cfgCrossMaxQinvQA{"cfgCrossMaxQinvQA", 0.3f, "fill crossed QA sparses only below this qinv"}; + } crosspair; + struct : ConfigurableGroup { std::string prefix = "eventcut_group"; Configurable cfgZvtxMin{"cfgZvtxMin", -10.f, "min. Zvtx"}; @@ -324,7 +528,6 @@ struct Photonhbt { Configurable cfgRequireGoodITSLayersAll{"cfgRequireGoodITSLayersAll", false, "all ITS layers OK"}; } eventcuts; - V0PhotonCut fV0PhotonCut; struct : ConfigurableGroup { std::string prefix = "pcmcut_group"; Configurable cfgRequireV0WithITSTPC{"cfgRequireV0WithITSTPC", false, "select V0s with ITS-TPC tracks"}; @@ -351,52 +554,327 @@ struct Photonhbt { Configurable cfgMaxTPCNsigmaEl{"cfgMaxTPCNsigmaEl", +3.5, "max TPC nsigma electron"}; } pcmcuts; - ~Photonhbt() - { - delete emh1; - emh1 = nullptr; - delete emh2; - emh2 = nullptr; - mapMixedEventIdToGlobalBC.clear(); - usedPhotonIdsPerCol.clear(); - truthGammaPool.clear(); - } + ConfigurableAxis confQBins{"confQBins", {60, 0, +0.3f}, "q bins for output histograms"}; + ConfigurableAxis confKtBins{"confKtBins", {VARIABLE_WIDTH, 0.0, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75}, "kT bins"}; + ConfigurableAxis confPtBins{"confPtBins", {100, 0.f, 2.f}, "pT bins (GeV/c)"}; + ConfigurableAxis confEtaBins{"confEtaBins", {80, -0.8f, 0.8f}, "eta bins"}; + ConfigurableAxis confPhiBins{"confPhiBins", {90, 0.f, o2::constants::math::TwoPI}, "phi bins (rad)"}; + ConfigurableAxis confDeltaEtaBins{"confDeltaEtaBins", {360, -1.6f, +1.6f}, "Delta-eta bins"}; + ConfigurableAxis confDeltaPhiBins{"confDeltaPhiBins", {360, -o2::constants::math::PI, o2::constants::math::PI}, "Delta-phi bins (rad)"}; + ConfigurableAxis confEllipseValBins{"confEllipseValBins", {100, 0.f, 10.f}, "ellipse value bins"}; + ConfigurableAxis confCosThetaBins{"confCosThetaBins", {100, 0.f, 1.f}, "cos(theta*) bins"}; + ConfigurableAxis confOpeningAngleBins{"confOpeningAngleBins", {100, 0.f, o2::constants::math::PI}, "opening angle bins (rad)"}; + ConfigurableAxis confRBins{"confRBins", {100, 0.f, 100.f}, "conversion radius bins (cm)"}; + ConfigurableAxis confDeltaRBins{"confDeltaRBins", {90, 0.f, 30.f}, "|R1-R2| bins (cm)"}; + ConfigurableAxis confDeltaR3DBins{"confDeltaR3DBins", {100, 0.f, 100.f}, "3D distance between conversion points (cm)"}; + ConfigurableAxis confDeltaRxyBins{"confDeltaRxyBins", {100, 0.f, 100.f}, "xy distance between conversion points (cm)"}; + ConfigurableAxis confZConvBins{"confZConvBins", {200, -100.f, 100.f}, "conversion z (cm)"}; + ConfigurableAxis confDeltaZBins{"confDeltaZBins", {200, -100.f, 100.f}, "#Deltaz bins (cm)"}; + ConfigurableAxis confOccupancyQA{"confOccupancyQA", {100, 0.f, 50000.f}, "occupancy"}; + ConfigurableAxis confCentQABins{"confCentQABins", {110, 0.f, 110.f}, "centrality (%)"}; + ConfigurableAxis confLegPtBins{"confLegPtBins", {40, 0.f, 2.f}, "leg p_{T} bins (GeV/c)"}; + ConfigurableAxis confLegDRBins{"confLegDRBins", {200, 0.f, 1.0f}, "#DeltaR_{legs} bins"}; + + /*************************************************/ + // AXIS SPECS + /*************************************************/ + + AxisSpec axisKt{confKtBins, "k_{T} (GeV/c)"}; + AxisSpec axisQinv{confQBins, "q_{inv} (GeV/c)"}; + AxisSpec axisQabsLcms{confQBins, "|#bf{q}|^{LCMS} (GeV/c)"}; + AxisSpec axisQout{confQBins, "q_{out} (GeV/c)"}; + AxisSpec axisQside{confQBins, "q_{side} (GeV/c)"}; + AxisSpec axisQlong{confQBins, "q_{long} (GeV/c)"}; + AxisSpec axisPt{confPtBins, "p_{T} (GeV/c)"}; + AxisSpec axisEta{confEtaBins, "#eta"}; + AxisSpec axisPhi{confPhiBins, "#phi (rad)"}; + AxisSpec axisDeltaEta{confDeltaEtaBins, "#Delta#eta"}; + AxisSpec axisDeltaPhi{confDeltaPhiBins, "#Delta#phi (rad)"}; + AxisSpec axisEllipseVal{confEllipseValBins, "(#Delta#eta/#sigma_{#eta})^{2}+(#Delta#phi/#sigma_{#phi})^{2}"}; + AxisSpec axisCosTheta{confCosThetaBins, "cos(#theta*)"}; + AxisSpec axisOpeningAngle{confOpeningAngleBins, "Opening angle (rad)"}; + AxisSpec axisR{confRBins, "R_{conv} (cm)"}; + AxisSpec axisDeltaR{confDeltaRBins, "|R_{1}-R_{2}| (cm)"}; + AxisSpec axisDeltaR3D{confDeltaR3DBins, "|#vec{r}_{1}-#vec{r}_{2}| (cm)"}; + AxisSpec axisDeltaRxy{confDeltaRxyBins, "#Delta r_{xy} (cm)"}; + AxisSpec axisZConv{confZConvBins, "z_{conv} (cm)"}; + AxisSpec axisDeltaZ{confDeltaZBins, "#Delta z (cm)"}; + AxisSpec axisOccupancy{confOccupancyQA, "occupancy"}; + AxisSpec axisCentQA{confCentQABins, "centrality (%)"}; + [[nodiscard]] AxisSpec makeAxisMultNTracks() const { return AxisSpec{hbtanalysis.confMultNTracksBins, "N_{tracks}^{PV}, |#eta| < 1"}; } + [[nodiscard]] AxisSpec makeAxisMultFT0M() const { return AxisSpec{hbtanalysis.confMultFT0MBins, "mult. FT0M (amplitude)"}; } + AxisSpec axisLegPt{confLegPtBins, "p_{T,leg} (GeV/c)"}; + AxisSpec axisLegDR{confLegDRBins, "#DeltaR_{legs}"}; + + /*************************************************/ + // MAIN SPECS + /*************************************************/ HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - HistogramRegistry fRegistryPairQA{"outputPairQA", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - HistogramRegistry fRegistryPairMC{"outputPairMC", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - HistogramRegistry fRegistryMC{"outputMC", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - static constexpr std::string_view event_pair_types[2] = {"same/", "mix/"}; + HistogramRegistry fRegistryCF{"cf", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + HistogramRegistry fRegistryPairQA{"pairQA", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + HistogramRegistry fRegistryPairMC{"pairMC", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + HistogramRegistry fRegistryTruthMC{"truthMC", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + EMPhotonEventCut fEMEventCut; + V0PhotonCut fV0PhotonCut; std::mt19937 engine; std::uniform_int_distribution dist01; int mRunNumber{0}; + bool isMC = false; + int ndf = 0; + pairutil::V0PhotonClassSelection mPhotonClassSelA{}; + pairutil::V0PhotonClassSelection mPhotonClassSelB{}; std::vector ztxBinEdges; std::vector centBinEdges; std::vector epBinEgdes; std::vector occBinEdges; + using MyEMH = o2::aod::pwgem::dilepton::utils::EventMixingHandler, std::pair, PhotonWithLegs>; + std::shared_ptr emh1; + std::shared_ptr emh2; + std::unordered_set usedPhotonIdsPerCol; + std::map, uint64_t> mapMixedEventIdToGlobalBC; + std::map, std::deque>> truthGammaPool; + + SliceCache cache; + Preslice perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceUnsorted perCollisionV0Legs = aod::v0leg::collisionId; + PresliceUnsorted perMCCollisionEMMCParts = aod::emmcparticle::emmceventId; + + Filter collisionFilterCentrality = + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < centralitySelection.cfgCentMax) || + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < centralitySelection.cfgCentMax) || + (centralitySelection.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < centralitySelection.cfgCentMax); + Filter collisionFilterOccupancyTrack = + eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && + o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; + Filter collisionFilterOccupancyFT0c = + eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && + o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; + + using FilteredMyCollisions = soa::Filtered; + using FilteredMyMCCollisions = soa::Filtered; + + /*************************************************/ + // INITS + /*************************************************/ + + struct QaGates { + bool pairQa{true}; + bool singlePhotonQa{true}; + bool legPairQa{true}; + bool pairSepQa{true}; + bool fillDRDZSparse{true}; + bool fillR1R2Sparse{true}; + bool pairSepAfterCut{true}; + } mQa; + + void init(InitContext& context) + { + isMC = context.mOptions.get("processMC"); + if (context.mOptions.get("processMCAOD")) { + addMCAODHistograms(); + } + if (pairsep.cfgCloseThrCm.value.size() != 4) { // o2-linter: disable=magic-number (four thresholds) + LOGF(fatal, "cfgCloseThrCm must contain exactly four thresholds, got %zu", + pairsep.cfgCloseThrCm.value.size()); + } + if (pairsep.cfgCloseThrCm.value.front() <= 0.f || + !std::is_sorted(pairsep.cfgCloseThrCm.value.begin(), pairsep.cfgCloseThrCm.value.end())) { + LOGF(fatal, "cfgCloseThrCm must be positive and sorted in ascending order"); + } + const int qaLevel = qaflags.cfgQaLevel.value; + mQa.pairQa = qaflags.doPairQa.value && qaLevel >= 1; // o2-linter: disable=magic-number (QA set-up) + mQa.singlePhotonQa = qaflags.doSinglePhotonQa.value && qaLevel >= 1; // o2-linter: disable=magic-number (QA set-up) + mQa.legPairQa = qaflags.doLegPairQA.value && qaLevel >= 2; // o2-linter: disable=magic-number (QA set-up) + mQa.pairSepQa = pairsep.cfgDoPairSepQA.value && qaLevel >= 2; // o2-linter: disable=magic-number (QA set-up) + mQa.fillDRDZSparse = qaflags.cfgFillDRDZSparse.value && qaLevel >= 2; // o2-linter: disable=magic-number (QA set-up) + mQa.fillR1R2Sparse = qaflags.cfgFillR1R2Sparse.value && qaLevel >= 2; // o2-linter: disable=magic-number (QA set-up) + // an AfterCut copy only differs from Before when a pair cut is active + mQa.pairSepAfterCut = + mQa.pairSepQa && + (ggpaircuts.cfgDoEllipseCut.value || ggpaircuts.cfgDoRCut.value || + ggpaircuts.cfgDoZCut.value || ggpaircuts.cfgMinDRCosOA.value > 0.f || + ggpaircuts.cfgMaxAsymmetry.value >= 0.f || pairsep.cfgDoPairMergeCut.value || + crosspair.cfgDoCrossPairCut.value || cfgDoPhotonClassPairCut.value); + LOGF(info, "photonhbt QA level %d -> pairQA %d, singlePhotonQA %d, legPairQA %d, pairSep %d, dRdZ sparse %d, R1R2 sparse %d, pairSep afterCut %d", + qaLevel, mQa.pairQa, mQa.singlePhotonQa, mQa.legPairQa, mQa.pairSepQa, mQa.fillDRDZSparse, mQa.fillR1R2Sparse, mQa.pairSepAfterCut); + mRunNumber = 0; + parseBins(mixing.confVtxBins, ztxBinEdges); + parseBins(mixing.confCentBins, centBinEdges); + parseBins(mixing.confEPBinsBins, epBinEgdes); + parseBins(mixing.confOccupancyBins, occBinEdges); + emh1 = std::make_shared(mixing.ndepth); + emh2 = std::make_shared(mixing.ndepth); + DefineEMEventCut(); + DefinePCMCut(); + mPhotonClassSelA = pairutil::buildV0PhotonClassSelection(photonclassA); + mPhotonClassSelB = pairutil::buildV0PhotonClassSelection(photonclassB); + addhistograms(); + std::random_device seedGen; + engine = std::mt19937(seedGen()); + dist01 = std::uniform_int_distribution(0, 1); + ccdb->setURL(cfgCcdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + const float worst = selfTestLegHelix(); + constexpr float kSelfTestTol = 1.e-4f; + if (worst < kSelfTestTol) { + LOGF(info, "photonhbt: leg-helix origin-limit self test PASSED (max |dphi| = %.2e rad)", worst); + } else { + LOGF(fatal, + "photonhbt: leg-helix origin-limit self test FAILED, max |dphi| = %.3e rad. " + "The helix-centre sign convention or the branch choice is wrong; every " + "leg-separation variable would be mirrored. Refusing to run.", + worst); + } + LOGF(info, "photonhbt: leg propagation sampled at %zu radii, %.0f-%.0f cm in %.0f cm steps", + kPhiStarRadiiM.size(), kPhiStarRadiiM.front() * kCmPerM, kPhiStarRadiiM.back() * kCmPerM, + (kPhiStarRadiiM.size() > 1) ? (kPhiStarRadiiM[1] - kPhiStarRadiiM[0]) * kCmPerM : 0.f); + LOGF(info, "photonhbt: PairSep QA %s", pairsep.cfgDoPairSepQA.value ? "on" : "off"); + LOGF(info, "photonhbt r-phi closeness thresholds: %.2f, %.2f, %.2f, %.2f cm", + pairsep.cfgCloseThrCm.value[0], pairsep.cfgCloseThrCm.value[1], + pairsep.cfgCloseThrCm.value[2], pairsep.cfgCloseThrCm.value[3]); + LOGF(info, "photonhbt pair merging: metric sigma_rphi = %.2f cm, sigma_z = %.2f cm (or %.1f ns if > 0), close at D_pair < %.2f", + pairsep.cfgSigRPhi.value, pairsep.cfgSigZ.value, pairsep.cfgSigZNs.value, pairsep.cfgDPairClose.value); + LOGF(info, + "photonhbt pair separation: all merging observables (d_rphi^min, D_pair^min, " + "N_close, f_close) use SAME-SIDE radii only; the pure r-phi quantities are " + "selected by d_rphi, not by d_3D"); + LOGF(info, + "photonhbt pair separation: d_z is vertex-LOCAL (each leg relative to its own primary vertex); " + "drift length uses global z. Check PairSep/mix/hDzLocal_vs_dVtxZ -- it must NOT be diagonal."); + LOGF(info, "photonhbt pair merging cut: %s, D_pair > %.2f, N_close < %d (0 = off); control window %.2f < q_inv < %.2f", + pairsep.cfgDoPairMergeCut.value ? "ON" : "off", pairsep.cfgDPairCut.value, + pairsep.cfgNCloseCut.value, pairsep.cfgCondQinvMin.value, pairsep.cfgCondQinvMax.value); + LOGF(info, "photonhbt: crossed-pair QA %s, cut %s (min(m_ee)/q_inv > %.2f)", + crosspair.cfgDoCrossPairQA.value ? "on" : "off", + crosspair.cfgDoCrossPairCut.value ? "ON" : "off", crosspair.cfgCrossMaxMeeRatio.value); + LOGF(info, "photonhbt: q_inv gates -- QA < %.3f, FullRange < %.3f, PairSep < %.3f", + qaflags.cfgMaxQinvForQA.value, qaflags.cfgMaxQinvForFullRange.value, + pairsep.cfgPairSepMaxQinv.value); + LOGF(info, + "photonhbt: check PairSep/hLegPropStatus after the run -- if 'propagated' is " + "not the dominant bin, the separation variables rest on a minority of legs"); + } + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + + auto vd = ccdb->getForRun("TPC/Calib/VDriftTgl", mRunNumber); + if (vd != nullptr) { + mVDriftCmPerNs = vd->refVDrift * vd->corrFact * 1e-3f; // o2-linter: disable=magic-number (cm/us -> cm/ns) + LOGF(info, "photonhbt: run %d, TPC vdrift = %.6f cm/ns (%.4f cm/us); 1 cm in z = %.0f ns", + mRunNumber, mVDriftCmPerNs, 1e3f * mVDriftCmPerNs, + (mVDriftCmPerNs > 0.f) ? 1.f / mVDriftCmPerNs : -1.f); + } else { + mVDriftCmPerNs = 0.f; + LOGF(warn, "photonhbt: no TPC VDrift object for run %d -- the z scale stays in cm", mRunNumber); + } + + if (cfgBzOverrideT.value > -100.f) { // o2-linter: disable=magic-number (number in case B-field is overridden in case not fetched from CCDB) + mBzT = cfgBzOverrideT.value; + return; + } + auto grpmag = ccdb->getForRun("GLO/Config/GRPMagField", mRunNumber); + mBzT = 0.1f * static_cast(grpmag->getNominalL3Field()); + LOGF(info, "photonhbt: run %d, Bz = %.2f T", mRunNumber, mBzT); + } + + void DefineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); + fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); + fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); + fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); + fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); + fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); + fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); + fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + } + + void DefinePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + fV0PhotonCut.SetV0PtRange(pcmcuts.cfgMinPtV0, 1e10f); + fV0PhotonCut.SetV0EtaRange(-pcmcuts.cfgMaxEtaV0, +pcmcuts.cfgMaxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.cfgMinCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.cfgMaxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfgMaxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.cfgMinV0Radius, pcmcuts.cfgMaxV0Radius); + fV0PhotonCut.SetAPRange(pcmcuts.cfgMaxAlphaAP, pcmcuts.cfgMaxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.cfgRejectV0OnITSIB); + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.cfgMinNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.cfgMinNCrossedRows); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.cfgMaxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.0, pcmcuts.cfgMaxChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.cfgMinTPCNsigmaEl, pcmcuts.cfgMaxTPCNsigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(-1e+10, pcmcuts.cfgMaxChi2ITS); + fV0PhotonCut.SetDisableITSonly(pcmcuts.cfgDisableITSOnlyTrack); + fV0PhotonCut.SetDisableTPConly(pcmcuts.cfgDisableTPCOnlyTrack); + fV0PhotonCut.SetNClustersITS(0, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(0.0, 16.0); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.cfgRequireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.cfgRequireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.cfgRequireV0WithTPCOnly); + } + + /*************************************************/ + // HELPERS + /*************************************************/ + inline bool isInsideEllipse(float deta, float dphi) const { - if (!ggpaircuts.cfgDoEllipseCut.value) + if (!ggpaircuts.cfgDoEllipseCut.value) { return false; + } const float sE = ggpaircuts.cfgEllipseSigEta.value; const float sP = ggpaircuts.cfgEllipseSigPhi.value; - if (sE < kMinSigma || sP < kMinSigma) + if (sE < kMinSigma || sP < kMinSigma) { return false; + } return (deta / sE) * (deta / sE) + (dphi / sP) * (dphi / sP) < ggpaircuts.cfgEllipseR2.value; } inline bool passRZCut(float deltaR, float deltaZ) const { - if (ggpaircuts.cfgDoRCut.value && deltaR < ggpaircuts.cfgMinDeltaR.value) + if (ggpaircuts.cfgDoRCut.value && deltaR < ggpaircuts.cfgMinDeltaR.value) { return false; - if (ggpaircuts.cfgDoZCut.value && std::fabs(deltaZ) < ggpaircuts.cfgMinDeltaZ.value) + } + if (ggpaircuts.cfgDoZCut.value && std::fabs(deltaZ) < ggpaircuts.cfgMinDeltaZ.value) { return false; + } return true; } + [[nodiscard]] inline bool passPhotonClassPairCut(pairutil::V0PhotonLegCounts const& c1, + pairutil::V0PhotonLegCounts const& c2) const + { + if (!cfgDoPhotonClassPairCut.value) { + return true; + } + return pairutil::isPairPhotonClassSelected(c1, c2, mPhotonClassSelA, mPhotonClassSelB); + } + inline bool passAsymmetryCut(float pt1, float pt2) const { if (ggpaircuts.cfgMaxAsymmetry.value < 0.f) { @@ -427,171 +905,551 @@ struct Photonhbt { const float limit = qaflags.cfgMaxQinvForMCQA.value; return (limit <= 0.f) || (qinv < limit); } + /*************************************************/ + // HISTOGRAM BOOKING + /*************************************************/ - static inline float computeCosTheta(const ROOT::Math::PtEtaPhiMVector& v1, - const ROOT::Math::PtEtaPhiMVector& v2) + void addhistograms() { - ROOT::Math::PxPyPzEVector p1(v1), p2(v2); - ROOT::Math::PxPyPzEVector pair = p1 + p2; - ROOT::Math::Boost boost(-pair.BoostToCM()); - ROOT::Math::PxPyPzEVector p1cm = boost(p1); - ROOT::Math::XYZVector pairDir(pair.Px(), pair.Py(), pair.Pz()); - ROOT::Math::XYZVector p1cmDir(p1cm.Px(), p1cm.Py(), p1cm.Pz()); - if (pairDir.R() < kMinSigma || p1cmDir.R() < kMinSigma) - return -1.f; - return static_cast(pairDir.Unit().Dot(p1cmDir.Unit())); + addEventHistograms(); + addPairCFHistograms(); + addPairSepHistograms(); + addCrossPairHistograms(); + addSinglePhotonQAHistograms(); + addPairQAHistograms(); + + if (isMC) { + addPairMCHistograms(); + if (mQa.legPairQa) { + addLegPairMCHistograms(); + } + addTruthMCHistograms(); + } } - static void parseBins(const ConfigurableAxis& cfg, std::vector& edges) + void addMCAODHistograms() { - if (cfg.value[0] == VARIABLE_WIDTH) { - edges = std::vector(cfg.value.begin(), cfg.value.end()); - edges.erase(edges.begin()); - } else { - const int n = static_cast(cfg.value[0]); - const float xmin = static_cast(cfg.value[1]); - const float xmax = static_cast(cfg.value[2]); - edges.resize(n + 1); - for (int i = 0; i <= n; ++i) - edges[i] = xmin + (xmax - xmin) / n * i; - } - } + fRegistryTruthMC.add("MCAOD/hPhotonStage", "conversion photons;0=converted, 1=both legs tracked, 2=both legs in same collision, 3=V0 matched;counts", kTH1F, {{4, -0.5f, 3.5f}}, true); + fRegistryTruthMC.add("MCAOD/hPhotonStageVsPt", "conversion photon stage vs p_{T};0=converted, 1=both legs tracked, 2=both legs in same collision, 3=V0 matched;p_{T,#gamma} (GeV/c)", kTH2F, {{4, -0.5f, 3.5f}, {100, 0.f, 10.f}}, true); + fRegistryTruthMC.add("MCAOD/hV0Type", "matched V0, raw v0Type bitmap;v0Type;counts", kTH1F, {{8, -0.5f, 7.5f}}, true); + fRegistryTruthMC.add("MCAOD/hV0MatchMultiplicity", "SVertexer V0 candidates per matched truth photon;N V0;counts", kTH1F, {{10, 0.5f, 10.5f}}, true); + fRegistryTruthMC.add("MCAOD/hNTrackedColls", "collisions in which BOTH legs of a photon are tracked;N collisions;counts", kTH1F, {{5, 0.5f, 5.5f}}, true); + fRegistryTruthMC.add("MCAOD/hV0CollMatch", "matched V0 vs leg collision;0=V0 in a leg collision, 1=V0 in a DIFFERENT collision;counts", kTH1F, {{2, -0.5f, 1.5f}}, true); - static int clampBin(int b, int nmax) { return std::clamp(b, 0, nmax); } + const AxisSpec axStage{4, -0.5f, 3.5f, "pair stage"}; + const AxisSpec axSplit{2, -0.5f, 1.5f, "0=track-level split, 1=V0-level split"}; + const AxisSpec axType{8, -0.5f, 7.5f, "v0Type (raw)"}; - static int binOf(const std::vector& edges, float val) - { - const int b = static_cast( - std::lower_bound(edges.begin(), edges.end(), val) - edges.begin()) - - 1; - return clampBin(b, static_cast(edges.size()) - 2); // - } + fRegistryTruthMC.add("MCAOD/hPairStage", "pair step histogram;pair stage;counts", kTH1F, {{4, -0.5f, 3.5f}}, true); + fRegistryTruthMC.add("MCAOD/hPairStageVsKt", "pair stage vs k_{T};pair stage;k_{T} (GeV/c)", kTH2F, {{4, -0.5f, 3.5f}, {75, 0.f, 0.75f}}, true); + fRegistryTruthMC.add("MCAOD/hSparsePairStage", "pair step histogram", kTHnSparseF, {axStage, axisDeltaEta, axisDeltaPhi, axisQinv}, true); + fRegistryTruthMC.add("MCAOD/hSparseDRStage", "pair step histogram", kTHnSparseF, {axStage, axisDeltaR, axisQinv}, true); - template - static constexpr const char* qaPrefix() - { - if constexpr (ev_id == 0) { - if constexpr (step_id == 0) - return "Pair/same/QA/Before/"; - if constexpr (step_id == 1) - return "Pair/same/QA/AfterDRCosOA/"; - if constexpr (step_id == 2) // o2-linter: disable=magic-number (just counting the step of a cut) - return "Pair/same/QA/AfterRZ/"; - return "Pair/same/QA/AfterEllipse/"; - } else { - if constexpr (step_id == 0) - return "Pair/mix/QA/Before/"; - if constexpr (step_id == 1) - return "Pair/mix/QA/AfterDRCosOA/"; - if constexpr (step_id == 2) // o2-linter: disable=magic-number (just counting the step of a cut) - return "Pair/mix/QA/AfterRZ/"; - return "Pair/mix/QA/AfterEllipse/"; + // both photons reconstructed, but not in one event + fRegistryTruthMC.add("MCAOD/hPairSplit", "split pairs;split mode;counts", kTH1F, {{2, -0.5f, 1.5f}}, true); + fRegistryTruthMC.add("MCAOD/hSparsePairSplit", "split pairs", kTHnSparseF, {axSplit, axisDeltaEta, axisDeltaPhi, axisQinv}, true); + + fRegistryTruthMC.add("MCAOD/hSparsePairV0Type", "stage-3 pairs, raw V0 types", kTHnSparseF, {axisDeltaEta, axisDeltaPhi, axisQinv, axType, axType}, true); + + const AxisSpec axDedup{8, -0.5f, 7.5f, "0=keep-all, 1=per-coll oracle, 2=per-coll fake-first, 3=global oracle, 4=global fake-first, 5=per-coll score, 6=global score, 7=pairwise"}; + + fRegistryTruthMC.add("MCAOD/hSparsePairDedup", "pairs with both photons surviving in one common collision, per dedup scenario", kTHnSparseF, {axDedup, axisDeltaEta, axisDeltaPhi, axisQinv}, true); + fRegistryTruthMC.add("MCAOD/hDedupNCand", "surviving V0 candidates;scenario;0=true, 1=cross-leg fake, 2=other fake", kTH2F, {axDedup, {3, -0.5f, 2.5f}}, true); + + const AxisSpec axClass{3, -0.5f, 2.5f, "0=true, 1=cross-leg fake, 2=other fake"}; + + for (const auto& reg : {std::string(""), std::string("_Rgt35")}) { + fRegistryTruthMC.add(("MCAOD/hAnaScore" + reg).c_str(), "analytic builder score;class;score", kTH2F, {axClass, {200, 0.f, 30.f}}, true); + fRegistryTruthMC.add(("MCAOD/hAnaPCA" + reg).c_str(), "analytic PCA;class;PCA (cm)", kTH2F, {axClass, {150, 0.f, 30.f}}, true); + fRegistryTruthMC.add(("MCAOD/hAnaCosPA" + reg).c_str(), "analytic cosPA;class;cosPA", kTH2F, {axClass, {200, 0.9f, 1.f}}, true); } + + fRegistryTruthMC.add("MCAOD/hDeltaS2x2_vs_Qinv", "2x2 score ambiguity;#DeltaS;q_{inv}^{true} (GeV/c)", kTH2F, {{100, 0.f, 10.f}, {60, 0.f, 0.3f}}, true); + fRegistryTruthMC.add("MCAOD/hDeltaS2x2_CrossWins", "2x2 groups where CROSS scores better;#DeltaS;q_{inv}^{true} (GeV/c)", kTH2F, {{100, 0.f, 10.f}, {60, 0.f, 0.3f}}, true); } - template - static constexpr const char* fullRangePrefix() + // ─── Event histograms (fRegistry) ───────────────────────────────────────── + void addEventHistograms() { - if constexpr (ev_id == 0) - return "Pair/same/FullRange/"; - return "Pair/mix/FullRange/"; + static constexpr std::array det = {"FT0M", "FT0A", "FT0C", "BTot", "BPos", "BNeg"}; + + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); + fRegistry.add("Event/before/hEP2_CentFT0C_forMix", + Form("2nd harmonics EP for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", det[mixing.cfgEP2EstimatorForMix].data()), + kTH2D, {{110, 0, 110}, {180, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}}, false); + fRegistry.add("Event/after/hEP2_CentFT0C_forMix", + Form("2nd harmonics EP for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", det[mixing.cfgEP2EstimatorForMix].data()), + kTH2D, {{110, 0, 110}, {180, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}}, false); } - void init(InitContext& /*context*/) + // ─── CF: final correlation-function output (fRegistryCF) ────────────────── + void addPairCFHistograms() { - mRunNumber = 0; - parseBins(mixing.confVtxBins, ztxBinEdges); - parseBins(mixing.confCentBins, centBinEdges); - parseBins(mixing.confEPBinsBins, epBinEgdes); - parseBins(mixing.confOccupancyBins, occBinEdges); - emh1 = new MyEMH(mixing.ndepth); - emh2 = new MyEMH(mixing.ndepth); - o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&fRegistry); - DefineEMEventCut(); - DefinePCMCut(); - addhistograms(); - std::random_device seedGen; - engine = std::mt19937(seedGen()); - dist01 = std::uniform_int_distribution(0, 1); - fRegistry.add("Pair/mix/hDiffBC", - "diff. global BC in mixed event;|BC_{current}-BC_{mixed}|", - kTH1D, {{10001, -0.5, 10000.5}}, true); + const AxisSpec axisMultNTracks = makeAxisMultNTracks(); + const AxisSpec axisMultFT0M = makeAxisMultFT0M(); + if (hbtanalysis.cfgDo3D) { + fRegistryCF.add("Pair/same/CF_3D", "diphoton correlation 3D LCMS", kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt}, true); + if (hbtanalysis.cfgDo2D) { + fRegistryCF.add("Pair/same/CF_2D", "diphoton correlation 2D (qout,qinv)", kTHnSparseD, {axisQout, axisQinv, axisMultNTracks, axisMultFT0M, axisKt}, true); + if (hbtanalysis.cfgDo2DSideLong) { + fRegistryCF.add("Pair/same/CF_2D_Side", "diphoton correlation 2D (qside,qinv)", kTHnSparseD, {axisQside, axisQinv, axisMultNTracks, axisMultFT0M, axisKt}, true); + fRegistryCF.add("Pair/same/CF_2D_Long", "diphoton correlation 2D (qlong,qinv)", kTHnSparseD, {axisQlong, axisQinv, axisMultNTracks, axisMultFT0M, axisKt}, true); + } + } + if (hbtanalysis.cfgDoQinvGate3D) { + const AxisSpec axisQinvGate{{0.0, 0.01, 0.02, 0.03, 0.05, 0.30}, "q_{inv} (GeV/c)"}; + fRegistryCF.add("Pair/same/CF_3D_Qinv", "diphoton correlation 3D LCMS + qinv gate axis", + kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt, axisQinvGate}, true); + } + } else { + fRegistryCF.add("Pair/same/CF_1D", hbtanalysis.cfgUseLCMS ? "diphoton correlation 1D LCMS" : "diphoton correlation 1D (qinv)", kTH2D, {hbtanalysis.cfgUseLCMS ? axisQabsLcms : axisQinv, axisKt}, true); + } + fRegistryCF.add("Pair/same/hSparse_DEtaDPhi_qinv_kT", + "pair (#Delta#eta,#Delta#phi,q_{inv},k_{T}) for efficiency reweighting;" + "#Delta#eta;#Delta#phi (rad);q_{inv} (GeV/c);k_{T} (GeV/c)", + kTHnSparseF, {axisDeltaEta, axisDeltaPhi, axisQinv, axisKt}, true); + fRegistryCF.add("Pair/same/hPhi_lowerPtV0", "azimuthal angle of lower-p_{T} V0 in pair;#phi (rad);counts", kTH1D, {axisPhi}, true); + addFullRangeHistograms("Pair/same/FullRange/"); + + fRegistryCF.add("Pair/same/CF_QLcms_Qinv", "diphoton CF |q|_{LCMS} vs. q_{inv}", kTHnSparseD, {axisQabsLcms, axisQinv, axisKt}, true); - // Print histogram counts and memory estimates for all registries - // LOGF(info, "=== photonhbt histogram summary ==="); - // fRegistry.print(); - // fRegistryPairQA.print(); - // fRegistryPairMC.print(); - // fRegistryMC.print(); - // LOGF(info, "==================================="); + fRegistryCF.addClone("Pair/same/", "Pair/mix/"); + + fRegistryCF.add("Pair/mix/hDiffBC", "diff. global BC in mixed event;|BC_{current}-BC_{mixed}|", kTH1D, {{10001, -0.5, 10000.5}}, true); } + // ─── CF: PairSep ──────────────────────── + void addPairSepHistograms() + { - template - void initCCDB(TCollision const& collision) + auto hs = fRegistryCF.add("PairSep/hLegPropStatus", "leg propagation;;legs", kTH1D, {{4, -0.5f, 3.5f}}, true); + hs->GetXaxis()->SetBinLabel(1, "propagated"); + hs->GetXaxis()->SetBinLabel(2, "below R_{conv}"); + hs->GetXaxis()->SetBinLabel(3, "out of reach"); + hs->GetXaxis()->SetBinLabel(4, "bad input"); + // low-pT legs curl up early, so N_valid differs between pairs + fRegistryCF.add("PairSep/hPropStatus_vs_LegPt", "propagation vs leg p_{T};status;p_{T}^{leg} (GeV/c)", + kTH2F, {{4, -0.5f, 3.5f}, {40, 0.f, 2.f}}, true); + fRegistryCF.add("PairSep/hPropStatus_vs_Rconv", "propagation vs conversion radius;status;R_{conv} (cm)", + kTH2F, {{4, -0.5f, 3.5f}, {50, 0.f, 100.f}}, true); + + const AxisSpec axisQcal4{{0.00, 0.01, 0.02, 0.04, 0.06, 0.10, 0.15, 0.20, 0.30, 0.50}}; + + // variable width: fine below 10 cm, coarse above + std::vector sepEdges; + for (int i = 0; i <= 20; ++i) { // o2-linter: disable=magic-number (axis definition) + sepEdges.push_back(0.25 * i); // 0 .. 5 cm in 0.25 cm + } + for (int i = 1; i <= 10; ++i) { // o2-linter: disable=magic-number (axis definition) + sepEdges.push_back(5.0 + 0.5 * i); // 5 .. 10 cm in 0.5 cm + } + for (int i = 1; i <= 10; ++i) { // o2-linter: disable=magic-number (axis definition) + sepEdges.push_back(10.0 + 1.0 * i); // 10 .. 20 cm in 1 cm + } + for (int i = 1; i <= 6; ++i) { // o2-linter: disable=magic-number (axis definition) + sepEdges.push_back(20.0 + 5.0 * i); // 20 .. 50 cm in 5 cm + } + const AxisSpec axisSep{sepEdges}; + + std::vector dirs = {"PairSep/same/", "PairSep/mix/"}; + if (mQa.pairSepAfterCut) { + dirs.push_back("PairSep/sameAfterCut/"); + dirs.push_back("PairSep/mixAfterCut/"); + } + + for (const auto& sm : dirs) { + fRegistryCF.add((sm + "hSparse_dRPhiAtMin3D_dZAtMin3D_Qinv").c_str(), "components at the closest 3D approach;d_{r#varphi} @ d^{min}_{3D} (cm);d_{z} @ d^{min}_{3D} (cm);q_{inv} (GeV/c)", + kTHnSparseF, {axisSep, axisSep, {50, 0.f, 0.5f}}, true); + fRegistryCF.add((sm + "hSparse_dMin3D_fClose_Qinv").c_str(), "closest approach vs how long they stay close;d^{min}_{3D} (cm);f_{close}^{r#varphi}(2 cm);q_{inv} (GeV/c)", + kTHnSparseF, {axisSep, {18, -0.028f, 1.028f}, {50, 0.f, 0.5f}}, true); + fRegistryCF.add((sm + "hSparse_dRPhiMin_fClose_Thr_Qinv").c_str(), "pure r#varphi;d_{r#varphi}^{min} (cm);f_{close}^{r#varphi};threshold index;q_{inv} (GeV/c)", + kTHnSparseF, {{40, 0.f, 10.f}, {18, -0.028f, 1.028f}, {4, -0.5f, 3.5f}, axisQcal4}, true); + fRegistryCF.add((sm + "hSparse_DPair_NClose_Qinv").c_str(), "scaled pair distance;D_{pair}^{min};N_{close};q_{inv} (GeV/c)", + kTHnSparseF, {{40, 0.f, 10.f}, {18, -0.5f, 17.5f}, {50, 0.f, 0.5f}}, true); + fRegistryCF.add((sm + "hSparse_dRPhi_dZ_Cond").c_str(), "control window;d_{r#varphi} @ D^{min}_{pair} (cm);d_{z} @ D^{min}_{pair} (cm);3#upoint region + drift bin", + kTHnSparseF, {axisSep, axisSep, {12, -0.5f, 11.5f}}, true); + fRegistryCF.add((sm + "hDzLocal_vs_dVtxZ").c_str(), "vertex-local;#Deltaz_{vtx} (cm);d_{z} @ D^{min}_{pair} (cm)", kTH2F, + {{80, -4.f, 4.f}, {100, 0.f, 20.f}}, true); + fRegistryCF.add((sm + "hDzGlobal_vs_dVtxZ").c_str(), "global z;#Deltaz_{vtx} (cm);|z_{1}-z_{2}| @ D^{min}_{pair} (cm)", kTH2F, + {{80, -4.f, 4.f}, {100, 0.f, 20.f}}, true); + fRegistryCF.add((sm + "hDzLocalSigned_vs_dVtxZ").c_str(), "vertex-local, signed;#Deltaz_{vtx} (cm);d_{z}^{signed} (cm)", kTH2F, + {{80, -4.f, 4.f}, {200, -20.f, 20.f}}, true); + fRegistryCF.add((sm + "hDzGlobalSigned_vs_dVtxZ").c_str(), "global z, signed;#Deltaz_{vtx} (cm);z_{1}-z_{2} (cm)", kTH2F, + {{80, -4.f, 4.f}, {200, -20.f, 20.f}}, true); + fRegistryCF.add((sm + "hDriftOld_vs_DriftNew").c_str(), "drift length, same side;L from mean z (cm);L from the two legs (cm)", kTH2F, + {{50, 0.f, 250.f}, {50, 0.f, 250.f}}, true); + fRegistryCF.add((sm + "hDriftOld_vs_DriftNew_opp").c_str(), "drift length, opposite side;L from mean z (cm);L from the two legs (cm)", kTH2F, + {{50, 0.f, 250.f}, {50, 0.f, 250.f}}, true); + fRegistryCF.add((sm + "hZ1_vs_Z2").c_str(), "leg z at D^{min}_{pair};z_{1} (cm);z_{2} (cm)", kTH2F, + {{100, -250.f, 250.f}, {100, -250.f, 250.f}}, true); + auto hss = fRegistryCF.add((sm + "hSameSide_vs_Qinv").c_str(), "TPC side;;q_{inv} (GeV/c)", kTH2F, {{2, -0.5f, 1.5f}, {50, 0.f, 0.5f}}, true); + hss->GetXaxis()->SetBinLabel(1, "opposite side"); + hss->GetXaxis()->SetBinLabel(2, "same side"); + fRegistryCF.add((sm + "hNCommon_vs_Qinv").c_str(), "usable radii;N_{valid};q_{inv} (GeV/c)", kTH2F, {{18, -0.5f, 17.5f}, {50, 0.f, 0.5f}}, true); + fRegistryCF.add((sm + "hNSameSide_vs_Qinv").c_str(), "usable radii on the same TPC side;N_{same side};q_{inv} (GeV/c)", kTH2F, {{18, -0.5f, 17.5f}, {50, 0.f, 0.5f}}, true); + fRegistryCF.add((sm + "hSparse_FClose_NClose_Qinv").c_str(), "scaled closeness;f_{close}^{D};N_{close}^{D};q_{inv} (GeV/c)", kTHnSparseF, {{18, -0.028f, 1.028f}, {18, -0.5f, 17.5f}, {50, 0.f, 0.5f}}, true); + if (pairsep.cfgFillCalibSparse.value) { + const AxisSpec axisQcal{{0.00, 0.01, 0.02, 0.04, 0.06, 0.10, 0.15, 0.20, 0.30, 0.50}}; + fRegistryCF.add((sm + "hSparse_Calib_dRPhi_dZ_R_L_C_Q").c_str(), "per-radius calibration;d_{r#varphi} (cm);d_{z} (cm);TPC region;drift bin;charge (0 = e^{+}e^{+});q_{inv} (GeV/c)", + kTHnSparseF, {{32, 0.f, 8.f}, {32, 0.f, 8.f}, {4, -0.5f, 3.5f}, {3, -0.5f, 2.5f}, {2, -0.5f, 1.5f}, axisQcal}, true); + fRegistryCF.add((sm + "hSparse_dRPhi_dZ_LegClass_L_Q").c_str(), "by leg type;d_{r#varphi} (cm);d_{z} (cm);leg class (0: <2, 1: #geq2 ITS-TPC legs);drift bin;q_{inv} (GeV/c)", + kTHnSparseF, {{32, 0.f, 8.f}, {32, 0.f, 8.f}, {2, -0.5f, 1.5f}, {3, -0.5f, 2.5f}, axisQcal}, true); + + fRegistryCF.add((sm + "hSparse_DzSignedLocal_dVtxZ_R").c_str(), "vertex QA, all radii;d_{z}^{signed,local} (cm);#Deltaz_{vtx} (cm);r (cm)", + kTHnSparseF, {{80, -20.f, 20.f}, {20, -4.f, 4.f}, {17, 82.5f, 252.5f}}, true); + fRegistryCF.add((sm + "hSparse_Calib_OppositeSide").c_str(), "opposite-side points;d_{r#varphi} (cm);d_{z} (cm)", kTH2F, + {{32, 0.f, 8.f}, {32, 0.f, 8.f}}, true); + } + fRegistryCF.add((sm + "hSparse_fCloseRPhi_dR_Qinv").c_str(), "fraction of TPC radii with transverse leg distance < 2 cm;f_{close}^{r#varphi}(2 cm);|R_{1}-R_{2}| (cm);q_{inv} (GeV/c)", + kTHnSparseF, {{18, -0.028f, 1.028f}, {90, 0.f, 180.f}, {50, 0.f, 0.5f}}, true); + fRegistryCF.add((sm + "hDRPhiMin_All_vs_SameSide").c_str(), "r#varphi minimum;d_{r#varphi}^{min}, all radii (cm);d_{r#varphi}^{min}, same side (cm)", + kTH2F, {{40, 0.f, 10.f}, {40, 0.f, 10.f}}, true); + fRegistryCF.add((sm + "hDMin3D_PP_vs_NN").c_str(), "d^{min}_{3D};e^{+}e^{+} (cm);e^{-}e^{-} (cm)", kTH2F, + {axisSep, axisSep}, true); + fRegistryCF.add((sm + "hFClose_PP_vs_NN").c_str(), "f_{close}^{r#varphi}(2 cm);e^{+}e^{+};e^{-}e^{-}", kTH2F, + {{18, -0.028f, 1.028f}, {18, -0.028f, 1.028f}}, true); + } + } + + void addCrossPairHistograms() { - if (mRunNumber == collision.runNumber()) + if (!(crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value)) { return; - mRunNumber = collision.runNumber(); + } + for (const auto& sm : {std::string("Pair/same/CrossPair/"), std::string("Pair/mix/CrossPair/")}) { + fRegistryCF.add((sm + "hSparse_MeeRatio_dR_Qinv").c_str(), + "crossed-hypothesis QA;min(m_{ee}^{cross})/q_{inv};|R_{1}-R_{2}| (cm);q_{inv} (GeV/c)", + kTHnSparseF, {{100, 0.f, 2.f}, {80, 0.f, 80.f}, {60, 0.f, 0.3f}}, true); + auto h = fRegistryCF.add((sm + "hVetoCounter").c_str(), "crossed-pair veto;;pairs", kTH1D, {{2, -0.5f, 1.5f}}, true); + h->GetXaxis()->SetBinLabel(1, "evaluated"); + h->GetXaxis()->SetBinLabel(2, "vetoed"); + } } - struct PairQAObservables { - ROOT::Math::PtEtaPhiMVector v1, v2, k12; - float x1 = 0.f, y1 = 0.f, z1 = 0.f, x2 = 0.f, y2 = 0.f, z2 = 0.f; - float r1 = 0.f, r2 = 0.f, dx = 0.f, dy = 0.f, dz = 0.f; - float deltaR = 0.f, deltaZ = 0.f, deltaRxy = 0.f, deltaR3D = 0.f; - float opa = 0.f, cosOA = 0.f, drOverCosOA = 0.f; - float deta = 0.f, dphi = 0.f, pairEta = 0.f, pairPhi = 0.f; - float kt = 0.f, qinv = 0.f, cosTheta = 0.f, openingAngle = 0.f; - bool valid = true; - }; + // ─── pairQA: single-photon QA per cut step ───────────────────────────────── + void addSinglePhotonQAHistograms() + { + static constexpr std::array kStepDirs = {"Before/", "AfterPairCuts/"}; + for (const auto& step : kStepDirs) { + addSinglePhotonQAHistogramsForStep(std::string("SinglePhoton/") + step); + } + } - struct TruthGamma { - int id = -1, posId = -1, negId = -1; - float eta = 0.f, phi = 0.f, pt = 0.f; - float rTrue = -1.f; - float legDRtrue = -1.f; - float legDEta = 0.f; // ← neu - float legDPhi = 0.f; // ← neu - float alphaTrue = 0.f; - }; + // ─── pairQA: pair-level QA per cut step ──────────────────────────────────── + void addPairQAHistograms() + { + static constexpr std::array kStepDirs = {"Before/", "AfterPairCuts/"}; + for (const auto& step : kStepDirs) { + addQAHistogramsForStep(std::string("Pair/same/QA/") + step); + } - std::map, std::deque>> truthGammaPool; + if (qaflags.cfgFillLegSimilaritySparse.value) { + const AxisSpec axisLSDEta{100, -0.1f, 0.1f, "#Delta#eta(LS legs)"}; + const AxisSpec axisLSDPhi{100, -0.1f, 0.1f, "#Delta#varphi(LS legs) (rad)"}; + const AxisSpec axisLSPtRatio{50, 0.f, 1.f, "|p_{T,1}-p_{T,2}|/(p_{T,1}+p_{T,2})"}; + const AxisSpec axisLSDNsig{40, 0.f, 8.f, "|#Delta n#sigma_{e}^{TPC}|"}; + fRegistryPairQA.add("Pair/same/LegSimilarity/hSparse_dEta_dPhi_ptRatio_dNsig_Qinv", "like-sign leg similarity;#Delta#eta(LS legs);#Delta#varphi(LS legs) (rad);|p_{T,1}-p_{T,2}|/(p_{T,1}+p_{T,2});|#Delta n#sigma_{e}^{TPC}|;q_{inv} (GeV/c)", kTHnSparseF, {axisLSDEta, axisLSDPhi, axisLSPtRatio, axisLSDNsig, axisQinv}, true); + if (qaflags.cfgFillLegSimilarityMixSparse.value) { + fRegistryPairQA.addClone("Pair/same/LegSimilarity/", "Pair/mix/LegSimilarity/"); + } + } + fRegistryPairQA.addClone("Pair/same/QA/", "Pair/mix/QA/"); + if (mQa.legPairQa) { + addLegPairQAForStep("Pair/same/QA/Before/"); + addLegPairQAForStep("Pair/same/QA/AfterPairCuts/"); + } + } - void addSinglePhotonQAHistogramsForStep(const std::string& path) + void addPairMCHistograms() { - fRegistryPairQA.add((path + "hEtaVsPhiPt").c_str(), "acceptance;#phi (rad);#eta", kTH3D, {axisPhi, axisEta, axisPt}, true); - fRegistryPairQA.add((path + "hRVsZConvPt").c_str(), "R_{conv} vs z_{conv};z_{conv} (cm);R_{conv} (cm)", kTH3D, {axisZConv, axisR, axisPt}, true); + const AxisSpec axisMultNTracks = makeAxisMultNTracks(); + const AxisSpec axisMultFT0M = makeAxisMultFT0M(); + const AxisSpec axisTruthType{{0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5}, + "truth type (1=TrueTrueDistinct,2=TrueTrueSamePhoton,3=SharedMcLeg," + "4=TrueFake,5=FakeFake,6=Pi0Daughters)"}; + const AxisSpec axisDeltaEtaMC{90, -1.6f, +1.6f, "#Delta#eta"}; + const AxisSpec axisDeltaPhiMC{90, -o2::constants::math::PI, +o2::constants::math::PI, "#Delta#phi (rad)"}; + + static constexpr std::array kTypes = { + "TrueTrueDistinct/", "TrueTrueSamePhoton/", "SharedMcLeg/", + "TrueFake/", "FakeFake/", "Pi0Daughters/"}; + + // ─── Per truth type: CF + observables ──────────────────────────────── + for (const auto& label : kTypes) { + const std::string base = std::string("Pair/same/MC/") + std::string(label); + + // CF + if (hbtanalysis.cfgDo3D) { + fRegistryPairMC.add((base + "CF_3D").c_str(), "MC CF 3D LCMS", kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt}, true); + if (hbtanalysis.cfgDo2D) { + fRegistryPairMC.add((base + "CF_2D").c_str(), "MC CF 2D", kTHnSparseD, {axisQout, axisQinv, axisMultNTracks, axisMultFT0M, axisKt}, true); + } + } else { + fRegistryPairMC.add((base + "CF_1D").c_str(), hbtanalysis.cfgUseLCMS ? "MC CF 1D LCMS" : "MC CF 1D (qinv)", kTH2D, {hbtanalysis.cfgUseLCMS ? axisQabsLcms : axisQinv, axisKt}, true); + } + fRegistryPairMC.add((base + "CF_QLcms_Qinv").c_str(), "MC CF |q|_{LCMS} vs. q_{inv}", kTHnSparseD, {axisQabsLcms, axisQinv, axisKt}, true); + + // 1D observables + fRegistryPairMC.add((base + "hQinv").c_str(), "q_{inv};q_{inv} (GeV/c);counts", kTH1D, {axisQinv}, true); + fRegistryPairMC.add((base + "hDeltaR").c_str(), "|R_{1}-R_{2}|;|R_{1}-R_{2}| (cm);counts", kTH1D, {axisDeltaR}, true); + fRegistryPairMC.add((base + "hDeltaZ").c_str(), "#Delta z;#Delta z (cm);counts", kTH1D, {axisDeltaZ}, true); + fRegistryPairMC.add((base + "hDeltaR3D").c_str(), "#Delta r_{3D};#Delta r_{3D} (cm);counts", kTH1D, {axisDeltaR3D}, true); + + // 2D observables + fRegistryPairMC.add((base + "hDEtaDPhi").c_str(), "#Delta#eta vs #Delta#phi", kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + fRegistryPairMC.add((base + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv}", kTH2D, {axisQinv, axisDeltaR}, true); + fRegistryPairMC.add((base + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv}", kTH2D, {axisQinv, axisDeltaZ}, true); + fRegistryPairMC.add((base + "hDeltaR3DVsQinv").c_str(), "#Delta r_{3D} vs q_{inv}", kTH2D, {axisQinv, axisDeltaR3D}, true); + + // Sparses + fRegistryPairMC.add((base + "hSparse_DEtaDPhi_kT").c_str(), + "#Delta#eta,#Delta#phi,k_{T};#Delta#eta;#Delta#phi (rad);k_{T} (GeV/c)", + kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisKt}, true); + + // Sparses + const bool addDEtaDPhiVsQinv = + (label == "TrueTrueDistinct/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueDistinct.value + : (label == "TrueTrueSamePhoton/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueSamePhoton.value + : (label == "SharedMcLeg/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvSharedMcLeg.value + : (label == "TrueFake/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueFake.value + : (label == "FakeFake/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvFakeFake.value + : mctruthSparse.cfgFillDEtaDPhiVsQinvPi0Daughters.value; + if (addDEtaDPhiVsQinv) { + fRegistryPairMC.add((base + "hDEtaDPhiVsQinv").c_str(), + "#Delta#eta vs #Delta#phi vs q_{inv}", kTHnSparseD, + {axisDeltaEtaMC, axisDeltaPhiMC, axisQinv}, true); + } + + const bool addDRDZQinv = + (label == "TrueTrueDistinct/") ? mctruthSparse.cfgFillDRDZQinvTrueTrueDistinct.value + : (label == "TrueTrueSamePhoton/") ? mctruthSparse.cfgFillDRDZQinvTrueTrueSamePhoton.value + : (label == "SharedMcLeg/") ? mctruthSparse.cfgFillDRDZQinvSharedMcLeg.value + : (label == "TrueFake/") ? mctruthSparse.cfgFillDRDZQinvTrueFake.value + : (label == "FakeFake/") ? mctruthSparse.cfgFillDRDZQinvFakeFake.value + : mctruthSparse.cfgFillDRDZQinvPi0Daughters.value; + if (addDRDZQinv) { + fRegistryPairMC.add((base + "hSparseDeltaRDeltaZQinv").c_str(), + "|R_{1}-R_{2}|,#Delta z,q_{inv}", kTHnSparseD, + {axisDeltaR, axisDeltaZ, axisQinv}, true); + } + } + + const AxisSpec axisPsiPair{90, 0.f, o2::constants::math::PIHalf, "|#psi_{pair}| (rad)"}; + const AxisSpec axisPhiV{90, 0.f, o2::constants::math::PI, "#varphi_{V} (rad)"}; + const AxisSpec axisIsTruePhoton{2, -0.5f, 1.5f, "photon is true (0/1)"}; + + fRegistryPairMC.add("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type", + "per-photon conversion topology;|#psi_{pair}| (rad);#varphi_{V} (rad);" + "q_{inv} (GeV/c);truth type;is true photon", + kTHnSparseF, {axisPsiPair, axisPhiV, axisQinv, axisTruthType, axisIsTruePhoton}, true); + + const AxisSpec axisMeeRatio{100, 0.f, 2.f, "min(m_{ee}^{cross})/q_{inv}"}; + fRegistryPairMC.add("Pair/same/MC/hSparse_MeeRatio_Qinv_Type", + "crossed-pair hypothesis per truth type;" + "min(m_{ee}^{cross})/q_{inv};q_{inv} (GeV/c);truth type", + kTHnSparseF, {axisMeeRatio, axisQinv, axisTruthType}, true); + + // ─── Cross-type summary ────────────────────────────────────────────── + fRegistryPairMC.add("Pair/same/MC/hTruthTypeVsQinv", "truth type vs q_{inv};q_{inv} (GeV/c);truth type", kTH2D, {axisQinv, axisTruthType}, true); + fRegistryPairMC.add("Pair/same/MC/hTruthTypeVsKt", "truth type vs k_{T};k_{T} (GeV/c);truth type", kTH2D, {axisKt, axisTruthType}, true); + + fRegistryPairMC.add("Pair/mix/MC/hQinv_TrueTrue", "mixed pairs, both true;q_{inv} (GeV/c);counts", kTH1D, {axisQinv}, true); + fRegistryPairMC.add("Pair/mix/MC/hQinv_TrueFake", "mixed pairs, one fake;q_{inv} (GeV/c);counts", kTH1D, {axisQinv}, true); + fRegistryPairMC.add("Pair/mix/MC/hQinv_FakeFake", "mixed pairs, both fake;q_{inv} (GeV/c);counts", kTH1D, {axisQinv}, true); + + for (const auto& label : {std::string("TrueTrueDistinct/"), std::string("TrueFake/"), std::string("FakeFake/")}) { + const std::string mixBase = "Pair/mix/MC/" + label; + if (hbtanalysis.cfgDo3D) { + fRegistryPairMC.add((mixBase + "CF_3D").c_str(), "MC mixed CF 3D LCMS", kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt}, true); + if (hbtanalysis.cfgDo2D) { + fRegistryPairMC.add((mixBase + "CF_2D").c_str(), "MC mixed CF 2D", kTHnSparseD, {axisQout, axisQinv, axisMultNTracks, axisMultFT0M, axisKt}, true); + } + } else { + fRegistryPairMC.add((mixBase + "CF_1D").c_str(), hbtanalysis.cfgUseLCMS ? "MC mixed CF 1D LCMS" : "MC mixed CF 1D (qinv)", kTH2D, {hbtanalysis.cfgUseLCMS ? axisQabsLcms : axisQinv, axisKt}, true); + } + fRegistryPairMC.add((mixBase + "CF_QLcms_Qinv").c_str(), "MC mixed CF |q|_{LCMS} vs. q_{inv}", kTHnSparseD, {axisQabsLcms, axisQinv, axisKt}, true); + } + + const AxisSpec axisLegOrigin{6, -0.5f, 5.5f, "0=conv e, 1=#pi^{0} Dalitz e, 2=#eta Dalitz e, 3=primary e, 4=other e, 5=NOT an electron"}; + for (const auto& cls : {std::string("truePhotons/"), std::string("fakePhotons/")}) { + const std::string spBase = "SinglePhotonMC/" + cls; + fRegistryPairMC.add((spBase + "hEtaPhi").c_str(), "#eta vs #varphi;#eta;#varphi (rad)", kTH2F, {axisEta, axisPhi}, true); + fRegistryPairMC.add((spBase + "hRZ").c_str(), "conversion point;z_{conv} (cm);R_{conv} (cm)", kTH2F, {axisZConv, axisR}, true); + fRegistryPairMC.add((spBase + "hEtaPt").c_str(), "#eta vs p_{T};#eta;p_{T} (GeV/c)", kTH2F, {axisEta, axisPt}, true); + fRegistryPairMC.add((spBase + "hLegOriginMatrix").c_str(), "pos-leg origin vs neg-leg origin;pos leg;neg leg", kTH2F, {axisLegOrigin, axisLegOrigin}, true); + } + + const AxisSpec axisFakeSubtype{3, -0.5f, 2.5f, "0=ordinary, 1=swap doublet, 2=partial swap"}; + fRegistryPairMC.add("Pair/same/MC/hFakeSubtypeVsQinv", "fake pair subtype vs q_{inv};q_{inv} (GeV/c);subtype", kTH2D, {axisQinv, axisFakeSubtype}, true); + fRegistryPairMC.add("Pair/same/MC/hDEtaDPhi_truePairs", + "true reco pairs (TrueTrueDistinct+SamePhoton+Pi0);" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", + kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + fRegistryPairMC.add("Pair/same/MC/hDEtaDPhi_fakePairs", + "fake reco pairs (FakeFake+TrueFake+SharedMcLeg);" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", + kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_kT_truePairs", + "true pairs: #Delta#eta,#Delta#phi,k_{T};" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", + kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisKt}, true); + fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_kT_fakePairs", + "fake pairs: #Delta#eta,#Delta#phi,k_{T};" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", + kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisKt}, true); + fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_qinv_truePairs", + "true pairs: #Delta#eta,#Delta#phi,q_{inv};" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);q_{inv} (GeV/c)", + kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisQinv}, true); + fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_qinv_fakePairs", + "fake pairs: #Delta#eta,#Delta#phi,q_{inv};" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);q_{inv} (GeV/c)", + kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisQinv}, true); + + if (hbtanalysis.cfgDo3D) { + fRegistryPairMC.add("Pair/same/MC/NoLabel/CF_3D", "missing MC label - CF 3D LCMS", kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt}, true); + if (hbtanalysis.cfgDo2D) { + fRegistryPairMC.add("Pair/same/MC/NoLabel/CF_2D", "missing MC label - CF 2D", kTHnSparseD, {axisQout, axisQinv, axisMultNTracks, axisMultFT0M, axisKt}, true); + } + } else { + fRegistryPairMC.add("Pair/same/MC/NoLabel/CF_1D", hbtanalysis.cfgUseLCMS ? "missing MC label - CF 1D LCMS" : "missing MC label - CF 1D (qinv)", kTH2D, {hbtanalysis.cfgUseLCMS ? axisQabsLcms : axisQinv, axisKt}, true); + } + fRegistryPairMC.add("Pair/same/MC/NoLabel/hDEtaDPhi", + "missing MC label: #Delta#eta vs #Delta#phi;" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", + kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + fRegistryPairMC.add("Pair/same/MC/NoLabel/hQinv", "missing MC label: q_{inv};q_{inv} (GeV/c);counts", kTH1D, {axisQinv}, true); + fRegistryPairMC.add("Pair/same/MC/NoLabel/hKt", "missing MC label: k_{T};k_{T} (GeV/c);counts", kTH1D, {axisKt}, true); } - void addFullRangeHistograms(const std::string& path) + void addTruthMCHistograms() { - fRegistry.add((path + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv};q_{inv} (GeV/c);|R_{1}-R_{2}| (cm)", kTH2D, {axisQinv, axisDeltaR}, true); - fRegistry.add((path + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv};q_{inv} (GeV/c);#Delta z (cm)", kTH2D, {axisQinv, axisDeltaZ}, true); - fRegistry.add((path + "hDeltaR3DVsQinv").c_str(), "#Delta r_{3D} vs q_{inv};q_{inv} (GeV/c);#Delta r_{3D} (cm)", kTH2D, {axisQinv, axisDeltaR3D}, true); - fRegistry.add((path + "hQinvVsCent").c_str(), "q_{inv} vs centrality;centrality (%);q_{inv} (GeV/c)", kTH2D, {axisCentQA, axisQinv}, true); - fRegistry.add((path + "hQinvVsOccupancy").c_str(), "q_{inv} vs occupancy;occupancy;q_{inv} (GeV/c)", kTH2D, {axisOccupancy, axisQinv}, true); - fRegistry.add((path + "hSparseDeltaRDeltaZQinv").c_str(), "|R_{1}-R_{2}|,#Delta z,q_{inv}", kTHnSparseD, {axisDeltaR, axisDeltaZ, axisQinv}, true); - fRegistry.add((path + "hDeltaRCosOAVsQinv").c_str(), "#Delta r/cos(#theta_{op}/2) vs q_{inv};q_{inv} (GeV/c);#Delta r/cos(#theta_{op}/2) (cm)", kTH2D, {axisQinv, {100, 0, 100}}, true); + const AxisSpec axQinvMC{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; + const AxisSpec axRconv{180, 0.f, 90.f, "R_{conv}^{true} (cm)"}; + const AxisSpec axAlpha{100, -1.f, 1.f, "#alpha^{true}"}; + const AxisSpec axLegDR{100, 0.f, 0.3f, "leg #Delta R^{true}"}; + + // ─── Truth-level CF + if (mctruth.cfgDoTruthMix.value) { + fRegistryTruthMC.add("MC/TruthCF/hQinvVsKt_same", "truth-level same-event CF;k_{T} (GeV/c);q_{inv}^{true} (GeV/c)", kTH2D, {axisKt, axQinvMC}, true); + fRegistryTruthMC.add("MC/TruthCF/hDEtaDPhi_same", + "truth-level same-event #Delta#eta vs #Delta#phi;" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", + kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + fRegistryTruthMC.add("MC/TruthCF/hQinvVsKt_mix", "truth-level mixed-event CF;k_{T} (GeV/c);q_{inv}^{true} (GeV/c)", kTH2D, {axisKt, axQinvMC}, true); + fRegistryTruthMC.add("MC/TruthCF/hDEtaDPhi_mix", + "truth-level mixed-event #Delta#eta vs #Delta#phi;" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", + kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + } + + auto addStageHistos = [&](const char* suffix, const char* title, auto const&... axes) { + for (const auto& stage : {"truthConverted", "all4LegsThisColl", + "bothPhotonsBuilt", "bothPhotonsSelected"}) { + const std::string name = std::string("MC/TruthAO2D/") + suffix + std::string("_") + stage; + const std::string ttl = std::string(title) + std::string(" [") + stage + "]"; + fRegistryTruthMC.add(name.c_str(), ttl.c_str(), kTHnSparseD, {axes...}, true); + } + }; + auto addStageHistos2D = [&](const char* suffix, const char* title, auto const& ax1, auto const& ax2) { + for (const auto& stage : {"truthConverted", "all4LegsThisColl", + "bothPhotonsBuilt", "bothPhotonsSelected"}) { + const std::string name = std::string("MC/TruthAO2D/") + suffix + std::string("_") + stage; + fRegistryTruthMC.add(name.c_str(), title, kTH2D, {ax1, ax2}, true); + } + }; + + addStageHistos("hSparse_DEtaDPhi_qinv", + "#Delta#eta,#Delta#phi,q_{inv}^{true};" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);q_{inv}^{true} (GeV/c)", + axisDeltaEta, axisDeltaPhi, axQinvMC); + addStageHistos("hSparse_DEtaDPhi_kT", + "#Delta#eta,#Delta#phi,k_{T};" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", + axisDeltaEta, axisDeltaPhi, axisKt); + addStageHistos2D("hQinvVsKt", "q_{inv}^{true} vs k_{T};k_{T} (GeV/c);q_{inv}^{true} (GeV/c)", axisKt, axQinvMC); + addStageHistos2D("hDEtaDPhi", "#Delta#eta vs #Delta#phi;#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", axisDeltaEta, axisDeltaPhi); + + // ─── Rconv waterfall + fRegistryTruthMC.add("MC/TruthAO2D/hRconv1_vs_Rconv2_truthConverted", "denominator: R_{conv,1} vs R_{conv,2};R_{conv,1}^{true} (cm);R_{conv,2}^{true} (cm)", kTH2D, {axRconv, axRconv}, true); + fRegistryTruthMC.add("MC/TruthAO2D/hRconv1_vs_Rconv2_bothPhotonsSelected", "numerator: R_{conv,1} vs R_{conv,2};R_{conv,1}^{true} (cm);R_{conv,2}^{true} (cm)", kTH2D, {axRconv, axRconv}, true); + fRegistryTruthMC.add("MC/TruthAO2D/hMinRconv_vs_kT_truthConverted", "denominator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", kTH2D, {axisKt, axRconv}, true); + fRegistryTruthMC.add("MC/TruthAO2D/hMinRconv_vs_kT_bothPhotonsSelected", "numerator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", kTH2D, {axisKt, axRconv}, true); + + // ─── Stage summary + fRegistryTruthMC.add("MC/TruthAO2D/hStage_vs_kT", "efficiency waterfall;k_{T} (GeV/c);stage (0=converted,1=all4legs,2=bothBuilt,3=bothSel)", kTH2D, {axisKt, AxisSpec{4, -0.5f, 3.5f, "stage"}}, true); + fRegistryTruthMC.add("MC/TruthAO2D/hStageConsistency", "stage consistency (expect all at 0);N(V0 built but legs not found);counts", kTH1D, {AxisSpec{20, -0.5f, 19.5f, "N_{bad}"}}, true); + + // ─── Single-leg diagnostics ────────────────────────────────────────── + fRegistryTruthMC.add("MC/LegDiag/hLegDRtrue_vs_pt_legFound", "leg found: #Delta R^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#Delta R_{e^{+}e^{-}}^{true}", kTH2D, {axisPt, axLegDR}, true); + fRegistryTruthMC.add("MC/LegDiag/hLegDRtrue_vs_pt_legMissing", "leg missing: #Delta R^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#Delta R_{e^{+}e^{-}}^{true}", kTH2D, {axisPt, axLegDR}, true); + fRegistryTruthMC.add("MC/LegDiag/hLegDEta_legFound_vs_pt", "leg found: |#Delta#eta| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#eta_{e^{+}e^{-}}|", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#eta_{legs}|"}}, true); + fRegistryTruthMC.add("MC/LegDiag/hLegDEta_legMissing_vs_pt", "leg missing: |#Delta#eta| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#eta_{e^{+}e^{-}}|", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#eta_{legs}|"}}, true); + fRegistryTruthMC.add("MC/LegDiag/hLegDPhi_legFound_vs_pt", "leg found: |#Delta#phi| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#phi_{e^{+}e^{-}}| (rad)", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#phi_{legs}|"}}, true); + fRegistryTruthMC.add("MC/LegDiag/hLegDPhi_legMissing_vs_pt", "leg missing: |#Delta#phi| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#phi_{e^{+}e^{-}}| (rad)", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#phi_{legs}|"}}, true); + fRegistryTruthMC.add("MC/LegDiag/hAlphaTrue_legFound_vs_pt", "leg found: #alpha^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#alpha^{true}", kTH2D, {axisPt, axAlpha}, true); + fRegistryTruthMC.add("MC/LegDiag/hAlphaTrue_legMissing_vs_pt", "leg missing: #alpha^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#alpha^{true}", kTH2D, {axisPt, axAlpha}, true); + fRegistryTruthMC.add("MC/LegDiag/hAlpha_vs_legDR_legMissing", "leg missing: #alpha^{true} vs #Delta R^{true};#Delta R_{e^{+}e^{-}}^{true};#alpha^{true}", kTH2D, {axLegDR, axAlpha}, true); + + // ─── Pair-level leg diagnostics ────────────────────────────────────── + fRegistryTruthMC.add("MC/LegDiag/hNLegsPair_vs_kT", "N legs found per pair vs k_{T};k_{T} (GeV/c);N_{legs found} (0-4)", kTH2D, {axisKt, AxisSpec{5, -0.5f, 4.5f, "N_{legs found}"}}, true); + fRegistryTruthMC.add("MC/LegDiag/hMissingLegPt_vs_kT", "missing leg p_{T}^{true} vs pair k_{T};k_{T} (GeV/c);p_{T,leg}^{true} (GeV/c)", kTH2D, {axisKt, AxisSpec{100, 0.f, 0.5f, "p_{T,leg}^{true} (GeV/c)"}}, true); + fRegistryTruthMC.add("MC/LegDiag/hMissingLegRconv_vs_kT", "missing leg R_{conv}^{true} vs pair k_{T};k_{T} (GeV/c);R_{conv}^{true} (cm)", kTH2D, {axisKt, axisR}, true); + + // ─── Cross-built V0 pairs ──────────────────────────────────────────── + fRegistryTruthMC.add("MC/PairCrossBuild/hSparse_DEtaDPhi_kT", + "cross-built V0 pairs: #Delta#eta,#Delta#phi,k_{T};" + "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", + kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisKt}, true); + fRegistryTruthMC.add("MC/PairCrossBuild/hStageOut_vs_kT", + "cross-built pairs: N correctly built vs k_{T};" + "k_{T} (GeV/c);N photons correctly built (0/1/2)", + kTH2D, {axisKt, AxisSpec{3, -0.5f, 2.5f, "N correctly built"}}, true); } - template - inline void fillFullRangeQA(PairQAObservables const& obs, float cent, float occupancy) + void addLegPairMCHistograms() { - constexpr auto base = fullRangePrefix(); - fRegistry.fill(HIST(base) + HIST("hDeltaRVsQinv"), obs.qinv, obs.deltaR); - fRegistry.fill(HIST(base) + HIST("hDeltaZVsQinv"), obs.qinv, obs.deltaZ); - fRegistry.fill(HIST(base) + HIST("hDeltaR3DVsQinv"), obs.qinv, obs.deltaR3D); - fRegistry.fill(HIST(base) + HIST("hQinvVsCent"), cent, obs.qinv); - fRegistry.fill(HIST(base) + HIST("hQinvVsOccupancy"), occupancy, obs.qinv); - fRegistry.fill(HIST(base) + HIST("hSparseDeltaRDeltaZQinv"), obs.deltaR, obs.deltaZ, obs.qinv); + static constexpr std::array kTypes = { + "TrueTrueDistinct/", "TrueTrueSamePhoton/", "SharedMcLeg/", + "TrueFake/", "FakeFake/", "Pi0Daughters/"}; + + for (const auto& label : kTypes) { + const std::string base = std::string("Pair/same/MC/") + std::string(label) + "LegLS/"; + fRegistryPairMC.add((base + "hSparse_DR_kT_minLegPt_PP").c_str(), "e^{+}e^{+} #DeltaR,k_{T},min(p_{T,leg})", kTHnSparseF, {axisLegDR, axisKt, axisLegPt}, false); + fRegistryPairMC.add((base + "hSparse_DR_kT_minLegPt_NN").c_str(), "e^{-}e^{-} #DeltaR,k_{T},min(p_{T,leg})", kTHnSparseF, {axisLegDR, axisKt, axisLegPt}, false); + if (qaflags.cfgFillLegPairSparses.value) { + const AxisSpec axDE{80, -0.8f, 0.8f, "#Delta#eta"}, axDP{80, -0.8f, 0.8f, "#Delta#phi (rad)"}, axP{20, 0.f, 2.f, "p_{T} (GeV/c)"}; + fRegistryPairMC.add((base + "hSparse_DEtaDPhi_PtG1_PtG2_PP").c_str(), "e^{+}e^{+} #Delta#eta,#Delta#phi,p_{T,#gamma 1},p_{T,#gamma 2}", kTHnSparseF, {axDE, axDP, axP, axP}, false); + fRegistryPairMC.add((base + "hSparse_DEtaDPhi_PtG1_PtG2_NN").c_str(), "e^{-}e^{-} #Delta#eta,#Delta#phi,p_{T,#gamma 1},p_{T,#gamma 2}", kTHnSparseF, {axDE, axDP, axP, axP}, false); + } + } + + const AxisSpec axisTruthType{{0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5}, "truth type"}; + fRegistryPairMC.add("Pair/same/MC/hMinDR_LS_vs_Kt_TruthType", "min(#DeltaR_{LS}) vs k_{T} vs truth type", kTHnSparseF, {axisLegDR, axisKt, axisTruthType}, false); } - template - inline void fillFullRangeDeltaRCosOA(float qinv, float drOverCosOA) + void addSinglePhotonQAHistogramsForStep(const std::string& path) { - constexpr auto base = fullRangePrefix(); - fRegistry.fill(HIST(base) + HIST("hDeltaRCosOAVsQinv"), qinv, drOverCosOA); + fRegistryPairQA.add((path + "hEtaVsPhiPt").c_str(), "acceptance;#phi (rad);#eta", kTH3D, {axisPhi, axisEta, axisPt}, true); + fRegistryPairQA.add((path + "hRVsZConvPt").c_str(), "R_{conv} vs z_{conv};z_{conv} (cm);R_{conv} (cm)", kTH3D, {axisZConv, axisR, axisPt}, true); + } + + void addFullRangeHistograms(const std::string& path) + { + fRegistryCF.add((path + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv};q_{inv} (GeV/c);|R_{1}-R_{2}| (cm)", kTH2D, {axisQinv, axisDeltaR}, true); + fRegistryCF.add((path + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv};q_{inv} (GeV/c);#Delta z (cm)", kTH2D, {axisQinv, axisDeltaZ}, true); + fRegistryCF.add((path + "hDeltaR3DVsQinv").c_str(), "#Delta r_{3D} vs q_{inv};q_{inv} (GeV/c);#Delta r_{3D} (cm)", kTH2D, {axisQinv, axisDeltaR3D}, true); + fRegistryCF.add((path + "hQinvVsCent").c_str(), "q_{inv} vs centrality;centrality (%);q_{inv} (GeV/c)", kTH2D, {axisCentQA, axisQinv}, true); + fRegistryCF.add((path + "hQinvVsOccupancy").c_str(), "q_{inv} vs occupancy;occupancy;q_{inv} (GeV/c)", kTH2D, {axisOccupancy, axisQinv}, true); + if (mQa.fillDRDZSparse) { + fRegistryCF.add((path + "hSparseDeltaRDeltaZQinv").c_str(), "|R_{1}-R_{2}|,#Delta z,q_{inv}", kTHnSparseD, {axisDeltaR, axisDeltaZ, axisQinv}, true); + } + // partner radius at fixed R1, differentially in q_inv + if (mQa.fillR1R2Sparse) { + fRegistryCF.add((path + "hSparseR1R2Qinv").c_str(), "R_{1},R_{2},q_{inv};R_{1} (cm);R_{2} (cm);q_{inv} (GeV/c)", kTHnSparseD, {axisR, axisR, axisQinv}, true); + } + fRegistryCF.add((path + "hDeltaRCosOAVsQinv").c_str(), "#Delta r/cos(#theta_{op}/2) vs q_{inv};q_{inv} (GeV/c);#Delta r/cos(#theta_{op}/2) (cm)", kTH2D, {axisQinv, {100, 0, 100}}, true); } + void addQAHistogramsForStep(const std::string& path) { // Ellipse @@ -620,135 +1478,601 @@ struct Photonhbt { fRegistryPairQA.add((path + "hSparseDeltaRDeltaZKt").c_str(), "|R_{1}-R_{2}|,#Delta z,k_{T}", kTHnSparseD, {axisDeltaR, axisDeltaZ, axisKt}, true); } - void addhistograms() + void addLegPairQAForStep(const std::string& path) { - static constexpr std::string_view det[6] = {"FT0M", "FT0A", "FT0C", "BTot", "BPos", "BNeg"}; - fRegistry.add("Event/before/hEP2_CentFT0C_forMix", - Form("2nd harmonics EP for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", det[mixing.cfgEP2EstimatorForMix].data()), - kTH2D, {{110, 0, 110}, {180, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}}, false); - fRegistry.add("Event/after/hEP2_CentFT0C_forMix", - Form("2nd harmonics EP for mix;centrality FT0C (%%);#Psi_{2}^{%s} (rad.)", det[mixing.cfgEP2EstimatorForMix].data()), - kTH2D, {{110, 0, 110}, {180, -o2::constants::math::PIHalf, +o2::constants::math::PIHalf}}, false); + // local, not members: each member costs one of the 99 decomposable slots + const AxisSpec axisLegDEtaCoarse{80, -0.8f, 0.8f, "#Delta#eta"}; + const AxisSpec axisLegDPhiCoarse{80, -0.8f, 0.8f, "#Delta#phi (rad)"}; + const AxisSpec axisLegPtCoarse{20, 0.f, 2.f, "p_{T}^{leg} (GeV/c)"}; + const AxisSpec axisPtCoarse{20, 0.f, 2.f, "p_{T} (GeV/c)"}; + // LS positive + fRegistryPairQA.add((path + "LegLS/hDEtaDPhi_PP").c_str(), "e^{+}e^{+} #Delta#eta vs #Delta#phi;#Delta#eta;#Delta#phi (rad)", kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + if (qaflags.cfgFillLegPairSparses.value) { + fRegistryPairQA.add((path + "LegLS/hSparse_DEtaDPhi_kT_minLegPt_PP").c_str(), "e^{+}e^{+} #Delta#eta,#Delta#phi,k_{T},min(p_{T,leg})", kTHnSparseF, {axisLegDEtaCoarse, axisLegDPhiCoarse, axisKt, axisLegPtCoarse}, true); + } + fRegistryPairQA.add((path + "LegLS/hDR_PP").c_str(), "e^{+}e^{+} #DeltaR;#DeltaR;counts", kTH1D, {axisLegDR}, true); + fRegistryPairQA.add((path + "LegLS/hLegPtLow_vs_LegPtHigh_PP").c_str(), "e^{+}e^{+} p_{T,low} vs p_{T,high};p_{T,low}^{leg};p_{T,high}^{leg}", kTH2D, {axisLegPt, axisLegPt}, true); + fRegistryPairQA.add((path + "LegLS/hDR_PP_vs_minLegPt").c_str(), "e^{+}e^{+} #DeltaR vs min(p_{T,leg});#DeltaR;min(p_{T,leg}) (GeV/c)", kTH2D, {axisLegDR, axisLegPt}, true); + + // LS negative + fRegistryPairQA.add((path + "LegLS/hDEtaDPhi_NN").c_str(), "e^{-}e^{-} #Delta#eta vs #Delta#phi;#Delta#eta;#Delta#phi (rad)", kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + if (qaflags.cfgFillLegPairSparses.value) { + fRegistryPairQA.add((path + "LegLS/hSparse_DEtaDPhi_kT_minLegPt_NN").c_str(), "e^{-}e^{-} #Delta#eta,#Delta#phi,k_{T},min(p_{T,leg})", kTHnSparseF, {axisLegDEtaCoarse, axisLegDPhiCoarse, axisKt, axisLegPtCoarse}, true); + } + fRegistryPairQA.add((path + "LegLS/hDR_NN").c_str(), "e^{-}e^{-} #DeltaR;#DeltaR;counts", kTH1D, {axisLegDR}, true); + fRegistryPairQA.add((path + "LegLS/hLegPtLow_vs_LegPtHigh_NN").c_str(), "e^{-}e^{-} p_{T,low} vs p_{T,high};p_{T,low}^{leg};p_{T,high}^{leg}", kTH2D, {axisLegPt, axisLegPt}, true); + fRegistryPairQA.add((path + "LegLS/hDR_NN_vs_minLegPt").c_str(), "e^{-}e^{-} #DeltaR vs min(p_{T,leg});#DeltaR;min(p_{T,leg}) (GeV/c)", kTH2D, {axisLegDR, axisLegPt}, true); + + fRegistryPairQA.add((path + "LegLS/hMinDR_LS_vs_Kt").c_str(), "min(#DeltaR_{LS}) vs k_{T};#DeltaR_{LS}^{min};k_{T} (GeV/c)", kTH2D, {axisLegDR, axisKt}, true); + + // US cross + fRegistryPairQA.add((path + "LegUScross/hDEtaDPhi_PN12").c_str(), "pos1-ele2 #Delta#eta vs #Delta#phi;#Delta#eta;#Delta#phi (rad)", kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + fRegistryPairQA.add((path + "LegUScross/hDEtaDPhi_NP12").c_str(), "ele1-pos2 #Delta#eta vs #Delta#phi;#Delta#eta;#Delta#phi (rad)", kTH2D, {axisDeltaEta, axisDeltaPhi}, true); + + // 4D Photon-pT diagnostics + if (qaflags.cfgFillLegPairSparses.value) { + fRegistryPairQA.add((path + "LegLS/hSparse_DEtaDPhi_PtG1_PtG2_PP").c_str(), + "e^{+}e^{+} #Delta#eta,#Delta#phi,p_{T,#gamma 1},p_{T,#gamma 2};" + "#Delta#eta;#Delta#phi (rad);p_{T,#gamma 1} (GeV/c);p_{T,#gamma 2} (GeV/c)", + kTHnSparseF, {axisLegDEtaCoarse, axisLegDPhiCoarse, axisPtCoarse, axisPtCoarse}, true); + fRegistryPairQA.add((path + "LegLS/hSparse_DEtaDPhi_PtG1_PtG2_NN").c_str(), + "e^{-}e^{-} #Delta#eta,#Delta#phi,p_{T,#gamma 1},p_{T,#gamma 2};" + "#Delta#eta;#Delta#phi (rad);p_{T,#gamma 1} (GeV/c);p_{T,#gamma 2} (GeV/c)", + kTHnSparseF, {axisLegDEtaCoarse, axisLegDPhiCoarse, axisPtCoarse, axisPtCoarse}, true); + } - addSinglePhotonQAHistogramsForStep("SinglePhoton/Before/"); - addSinglePhotonQAHistogramsForStep("SinglePhoton/AfterDRCosOA/"); - addSinglePhotonQAHistogramsForStep("SinglePhoton/AfterRZ/"); - addSinglePhotonQAHistogramsForStep("SinglePhoton/AfterEllipse/"); + // Photon-pT Scatter + fRegistryPairQA.add((path + "LegLS/hPtG1_vs_PtG2").c_str(), "p_{T,#gamma 1} vs p_{T,#gamma 2};p_{T,#gamma 1} (GeV/c);p_{T,#gamma 2} (GeV/c)", kTH2D, {axisPt, axisPt}, true); + } - if (hbtanalysis.cfgDo3D) { - fRegistry.add("Pair/same/CF_3D", "diphoton correlation 3D LCMS", kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt}, true); - if (hbtanalysis.cfgDo2D) - fRegistry.add("Pair/same/CF_2D", "diphoton correlation 2D (qout,qinv)", kTHnSparseD, {axisQout, axisQinv, axisKt}, true); + static inline float computeCosTheta(const ROOT::Math::PtEtaPhiMVector& v1, + const ROOT::Math::PtEtaPhiMVector& v2) + { + ROOT::Math::PxPyPzEVector p1(v1), p2(v2); + ROOT::Math::PxPyPzEVector pair = p1 + p2; + ROOT::Math::Boost boost(-pair.BoostToCM()); + ROOT::Math::PxPyPzEVector p1cm = boost(p1); + ROOT::Math::XYZVector pairDir(pair.Px(), pair.Py(), pair.Pz()); + ROOT::Math::XYZVector p1cmDir(p1cm.Px(), p1cm.Py(), p1cm.Pz()); + if (pairDir.R() < kMinSigma || p1cmDir.R() < kMinSigma) { + return -1.f; + } + return static_cast(pairDir.Unit().Dot(p1cmDir.Unit())); + } + + static void parseBins(const ConfigurableAxis& cfg, std::vector& edges) + { + if (cfg.value[0] == VARIABLE_WIDTH) { + edges = std::vector(cfg.value.begin(), cfg.value.end()); + edges.erase(edges.begin()); } else { - if (hbtanalysis.cfgUseLCMS) - fRegistry.add("Pair/same/CF_1D", "diphoton correlation 1D LCMS", kTH2D, {axisQabsLcms, axisKt}, true); - else - fRegistry.add("Pair/same/CF_1D", "diphoton correlation 1D (qinv)", kTH2D, {axisQinv, axisKt}, true); + const auto n = static_cast(cfg.value[0]); + const auto xmin = static_cast(cfg.value[1]); + const auto xmax = static_cast(cfg.value[2]); + edges.resize(n + 1); + for (int i = 0; i <= n; ++i) { + edges[i] = xmin + (xmax - xmin) / n * i; + } } + } - fRegistry.add("Pair/same/hDeltaRCosOA", "distance between 2 conversion points / cos(#theta_{op}/2);#Delta r / cos(#theta_{op}/2) (cm);counts", kTH1D, {{100, 0, 100}}, true); - fRegistry.add("Pair/same/hSparse_DEtaDPhi_kT", - "same-event (#Delta#eta,#Delta#phi,q_{inv},k_{T}) for efficiency reweighting;" - "#Delta#eta;#Delta#phi (rad);q_{inv} (GeV/c);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisKt}, true); + static int clampBin(int b, int nmax) { return std::clamp(b, 0, nmax); } - fRegistry.add("Pair/same/hPhi_lowerPtV0", "azimuthal angle of lower-p_{T} V0 in pair;#phi (rad);counts", kTH1D, {axisPhi}, true); - fRegistry.add("Pair/same/hSparse_DEtaDPhi_qinv_kT", "azimuthal angle of lower-p_{T} V0 in pair;#phi (rad);counts", kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisQinv, axisKt}, true); + static int binOf(const std::vector& edges, float val) + { + const auto pos = static_cast(std::lower_bound(edges.begin(), edges.end(), val) - edges.begin()); + return clampBin(pos - 1, static_cast(edges.size()) - 2); + } - addQAHistogramsForStep("Pair/same/QA/Before/"); - addQAHistogramsForStep("Pair/same/QA/AfterDRCosOA/"); - addQAHistogramsForStep("Pair/same/QA/AfterRZ/"); - addQAHistogramsForStep("Pair/same/QA/AfterEllipse/"); + template + static constexpr const char* qaPrefix() + { + if constexpr (ev_id == 0) { + return (step_id == 0) ? "Pair/same/QA/Before/" : "Pair/same/QA/AfterPairCuts/"; + } else { + return (step_id == 0) ? "Pair/mix/QA/Before/" : "Pair/mix/QA/AfterPairCuts/"; + } + } - addMCHistograms(); - fRegistryPairQA.addClone("Pair/same/QA/", "Pair/mix/QA/"); - fRegistryPairMC.addClone("Pair/same/MC/", "Pair/mix/MC/"); - addFullRangeHistograms("Pair/same/FullRange/"); - fRegistry.addClone("Pair/same/", "Pair/mix/"); - - fRegistry.add("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_sameSideV0_sameSideLegs", - "both V0 same #eta-side, all legs same side;" - "#Delta#eta;#Delta#phi (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisKt}, true); - fRegistry.add("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_sameSideV0_mixedLegs", - "both V0 same #eta-side, legs mixed sides;" - "#Delta#eta;#Delta#phi (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisKt}, true); - fRegistry.add("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_diffSideV0_matchingLegs", - "V0 on opposite #eta-sides, legs match their V0 side;" - "#Delta#eta;#Delta#phi (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisKt}, true); - fRegistry.add("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_diffSideV0_crossedLegs", - "V0 on opposite #eta-sides, legs cross their V0 side;" - "#Delta#eta;#Delta#phi (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisKt}, true); + template + static constexpr const char* pairSepPrefix() + { + if constexpr (!after_cut) { + return (ev_id == 0) ? "PairSep/same/" : "PairSep/mix/"; + } else { + return (ev_id == 0) ? "PairSep/sameAfterCut/" : "PairSep/mixAfterCut/"; + } } - void DefineEMEventCut() + template + static constexpr const char* fullRangePrefix() { - fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); - fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); - fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); - fEMEventCut.SetZvtxRange(eventcuts.cfgZvtxMin, eventcuts.cfgZvtxMax); - fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); - fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); - fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); - fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); - fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); - fEMEventCut.SetRequireNoCollInTimeRangeStandard(eventcuts.cfgRequireNoCollInTimeRangeStandard); - fEMEventCut.SetRequireNoCollInTimeRangeStrict(eventcuts.cfgRequireNoCollInTimeRangeStrict); - fEMEventCut.SetRequireNoCollInITSROFStandard(eventcuts.cfgRequireNoCollInITSROFStandard); - fEMEventCut.SetRequireNoCollInITSROFStrict(eventcuts.cfgRequireNoCollInITSROFStrict); - fEMEventCut.SetRequireNoHighMultCollInPrevRof(eventcuts.cfgRequireNoHighMultCollInPrevRof); - fEMEventCut.SetRequireGoodITSLayer3(eventcuts.cfgRequireGoodITSLayer3); - fEMEventCut.SetRequireGoodITSLayer0123(eventcuts.cfgRequireGoodITSLayer0123); - fEMEventCut.SetRequireGoodITSLayersAll(eventcuts.cfgRequireGoodITSLayersAll); + if constexpr (ev_id == 0) { + return "Pair/same/FullRange/"; + } + return "Pair/mix/FullRange/"; } - void DefinePCMCut() + template + static constexpr const char* mcDirPrefix() { - fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); - fV0PhotonCut.SetV0PtRange(pcmcuts.cfgMinPtV0, 1e10f); - fV0PhotonCut.SetV0EtaRange(-pcmcuts.cfgMaxEtaV0, +pcmcuts.cfgMaxEtaV0); - fV0PhotonCut.SetMinCosPA(pcmcuts.cfgMinCosPA); - fV0PhotonCut.SetMaxPCA(pcmcuts.cfgMaxPCA); - fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfgMaxChi2KF); - fV0PhotonCut.SetRxyRange(pcmcuts.cfgMinV0Radius, pcmcuts.cfgMaxV0Radius); - fV0PhotonCut.SetAPRange(pcmcuts.cfgMaxAlphaAP, pcmcuts.cfgMaxQtAP); - fV0PhotonCut.RejectITSib(pcmcuts.cfgRejectV0OnITSIB); - fV0PhotonCut.SetMinNClustersTPC(pcmcuts.cfgMinNClusterTPC); - fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.cfgMinNCrossedRows); - fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(0.8); - fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.cfgMaxFracSharedClustersTPC); - fV0PhotonCut.SetChi2PerClusterTPC(0.0, pcmcuts.cfgMaxChi2TPC); - fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.cfgMinTPCNsigmaEl, pcmcuts.cfgMaxTPCNsigmaEl); - fV0PhotonCut.SetChi2PerClusterITS(-1e+10, pcmcuts.cfgMaxChi2ITS); - fV0PhotonCut.SetDisableITSonly(pcmcuts.cfgDisableITSOnlyTrack); - fV0PhotonCut.SetDisableTPConly(pcmcuts.cfgDisableTPCOnlyTrack); - fV0PhotonCut.SetNClustersITS(0, 7); - fV0PhotonCut.SetMeanClusterSizeITSob(0.0, 16.0); - fV0PhotonCut.SetRequireITSTPC(pcmcuts.cfgRequireV0WithITSTPC); - fV0PhotonCut.SetRequireITSonly(pcmcuts.cfgRequireV0WithITSOnly); - fV0PhotonCut.SetRequireTPConly(pcmcuts.cfgRequireV0WithTPCOnly); + if constexpr (!IsMix) { + if constexpr (TruthT == PairTruthType::TrueTrueDistinct) { + return "Pair/same/MC/TrueTrueDistinct/"; + } else if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) { + return "Pair/same/MC/TrueTrueSamePhoton/"; + } else if constexpr (TruthT == PairTruthType::SharedMcLeg) { + return "Pair/same/MC/SharedMcLeg/"; + } else if constexpr (TruthT == PairTruthType::TrueFake) { + return "Pair/same/MC/TrueFake/"; + } else if constexpr (TruthT == PairTruthType::FakeFake) { + return "Pair/same/MC/FakeFake/"; + } else { + return "Pair/same/MC/Pi0Daughters/"; + } + } else { + if constexpr (TruthT == PairTruthType::TrueTrueDistinct) { + return "Pair/mix/MC/TrueTrueDistinct/"; + + } else if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) { + return "Pair/mix/MC/TrueTrueSamePhoton/"; + } else if constexpr (TruthT == PairTruthType::SharedMcLeg) { + return "Pair/mix/MC/SharedMcLeg/"; + } else if constexpr (TruthT == PairTruthType::TrueFake) { + return "Pair/mix/MC/TrueFake/"; + + } else if constexpr (TruthT == PairTruthType::FakeFake) { + return "Pair/mix/MC/FakeFake/"; + } else { + return "Pair/mix/MC/Pi0Daughters/"; + } + } + } + + template + static LegPairObservables buildLegPairObservables(TGamma const& g1, TGamma const& g2, + TLeg const& pos1, TLeg const& ele1, + TLeg const& pos2, TLeg const& ele2) + { + LegPairObservables o{}; + auto comp = [](auto const& a, auto const& b, float& de, float& dp, float& dr) { + de = a.eta() - b.eta(); + dp = RecoDecay::constrainAngle(a.phi() - b.phi(), -o2::constants::math::PI); + dr = std::sqrt(de * de + dp * dp); + }; + comp(pos1, pos2, o.dEtaPP, o.dPhiPP, o.dRPP); + comp(ele1, ele2, o.dEtaNN, o.dPhiNN, o.dRNN); + comp(pos1, ele2, o.dEtaPN12, o.dPhiPN12, o.dRPN12); + comp(ele1, pos2, o.dEtaNP12, o.dPhiNP12, o.dRNP12); + o.minDR_LS = std::min(o.dRPP, o.dRNN); + + o.minLegPt_PP = std::min(static_cast(pos1.pt()), static_cast(pos2.pt())); + o.maxLegPt_PP = std::max(static_cast(pos1.pt()), static_cast(pos2.pt())); + o.minLegPt_NN = std::min(static_cast(ele1.pt()), static_cast(ele2.pt())); + o.maxLegPt_NN = std::max(static_cast(ele1.pt()), static_cast(ele2.pt())); + + o.ptG1 = static_cast(g1.pt()); + o.ptG2 = static_cast(g2.pt()); + return o; + } + + // not const: fills the propagation-status histogram + template + [[nodiscard]] PhotonWithLegs makePhotonWithLegs(TGamma const& g, TLeg const& pos, TLeg const& ele, float vtxZ) + { + PhotonWithLegs p; + p.fVtxZ = vtxZ; + p.fPt = g.pt(); + p.fEta = g.eta(); + p.fPhi = g.phi(); + p.fVx = g.vx(); + p.fVy = g.vy(); + p.fVz = g.vz(); + p.fLegPt = {static_cast(pos.pt()), static_cast(ele.pt())}; + p.fLegEta = {static_cast(pos.eta()), static_cast(ele.eta())}; + p.fLegPhi = {static_cast(pos.phi()), static_cast(ele.phi())}; + p.fLegNsigEl = {static_cast(pos.tpcNSigmaEl()), static_cast(ele.tpcNSigmaEl())}; + p.fLegCounts = pairutil::getV0PhotonLegCounts(pos, ele); + p.fNITSTPC = p.fLegCounts.nITSTPC; + // legHelixAt itself rejects radii below the conversion point + for (size_t ir = 0; ir < kPhiStarRadiiM.size(); ++ir) { + for (int il = 0; il < 2; ++il) { // o2-linter: disable=magic-number (two legs) + const auto h = legHelixAt(p.fVx / kCmPerM, p.fVy / kCmPerM, p.fLegPhi[il], p.fLegPt[il], + (il == 0) ? +1 : -1, mBzT, kPhiStarRadiiM[ir]); + p.fLegPhiStar[il][ir] = h.phiStar; + p.fLegSxy[il][ir] = h.sxy; + fRegistryCF.fill(HIST("PairSep/hLegPropStatus"), static_cast(h.status)); + fRegistryCF.fill(HIST("PairSep/hPropStatus_vs_LegPt"), static_cast(h.status), p.fLegPt[il]); + fRegistryCF.fill(HIST("PairSep/hPropStatus_vs_Rconv"), static_cast(h.status), std::hypot(p.fVx, p.fVy)); + } + } + return p; + } + + [[nodiscard]] PairSep computePairSep(PhotonWithLegs const& a, PhotonWithLegs const& b) const + { + PairSep s; + s.dVtxZ = a.fVtxZ - b.fVtxZ; + s.nLegsITSTPC = a.fNITSTPC + b.fNITSTPC; + + const float sigR = std::max(pairsep.cfgSigRPhi.value, kMinSigma); + const float sigZcm = (pairsep.cfgSigZNs.value > 0.f && mVDriftCmPerNs > 0.f) + ? pairsep.cfgSigZNs.value * mVDriftCmPerNs + : pairsep.cfgSigZ.value; + const float sigZ = std::max(sigZcm, kMinSigma); + const float dClose = pairsep.cfgDPairClose.value; + for (int i = 0; i < 2; ++i) { // o2-linter: disable=magic-number (PP and NN) + int nHere = 0, nSame = 0, nCloseD = 0; + std::array nCloseThr{}; + float best3 = 999.f, bestR = 999.f, bestD = 999.f; + float bestRAll = 999.f, bestDAll = 999.f; + float rphiAt3 = 999.f, zAt3 = 999.f, zAtR = 999.f; + float rphiAtD = 999.f, zAtD = 999.f, rAtD = -1.f, absZAtD = -1.f; + float zGlobAtD = 999.f, zSgnLocAtD = 999.f, zSgnGlobAtD = 999.f; + float driftAtD = -1.f, driftOldAtD = -1.f; + for (size_t ir = 0; ir < kPhiStarRadiiM.size(); ++ir) { + const float p1 = a.fLegPhiStar[i][ir], p2 = b.fLegPhiStar[i][ir]; + if (p1 < -100.f || p2 < -100.f) { // o2-linter: disable=magic-number (invalid phi*) + continue; + } + const float rCm = kPhiStarRadiiM[ir] * kCmPerM; + const float dphi = RecoDecay::constrainAngle(p1 - p2, -o2::constants::math::PI); + const float dRPhi = 2.f * rCm * std::fabs(std::sin(0.5f * dphi)); + const float z1 = a.fVz + a.fLegSxy[i][ir] * kCmPerM * std::sinh(a.fLegEta[i]); + const float z2 = b.fVz + b.fLegSxy[i][ir] * kCmPerM * std::sinh(b.fLegEta[i]); + const float dZSignedLocal = (z1 - a.fVtxZ) - (z2 - b.fVtxZ); + const float dZSignedGlobal = z1 - z2; + const float dZ = std::fabs(dZSignedLocal); + const float dZGlobal = std::fabs(dZSignedGlobal); + const float d3 = std::hypot(dRPhi, dZ); + const float zMax = pairsep.cfgTpcHalfLengthCm.value; + const float l1 = std::max(0.f, zMax - std::fabs(z1)); + const float l2 = std::max(0.f, zMax - std::fabs(z2)); + const float driftHere = 0.5f * (l1 + l2); + const float driftOldHere = std::max(0.f, zMax - 0.5f * std::fabs(z1 + z2)); + const bool sameSide = (z1 * z2 >= 0.f); + const float dPair = std::hypot(dRPhi / sigR, dZ / sigZ); + ++nHere; + s.ptR[i][nHere - 1] = rCm; + s.ptDRPhi[i][nHere - 1] = dRPhi; + s.ptDZ[i][nHere - 1] = dZ; + s.ptDZSgnLocal[i][nHere - 1] = dZSignedLocal; + s.ptDrift[i][nHere - 1] = driftHere; + s.ptSameSide[i][nHere - 1] = sameSide ? 1.f : 0.f; + bestRAll = std::min(bestRAll, dRPhi); + bestDAll = std::min(bestDAll, dPair); + if (d3 < best3) { + best3 = d3; + rphiAt3 = dRPhi; + zAt3 = dZ; + } + if (!sameSide) { + continue; + } + ++nSame; + for (std::size_t t = 0; t < s.nCloseRPhiThr.size(); ++t) { + if (dRPhi < pairsep.cfgCloseThrCm.value[t]) { + ++nCloseThr[t]; + } + } + if (dPair < dClose) { + ++nCloseD; + } + if (dPair < bestD) { + bestD = dPair; + rphiAtD = dRPhi; + zAtD = dZ; + zGlobAtD = dZGlobal; + zSgnLocAtD = dZSignedLocal; + zSgnGlobAtD = dZSignedGlobal; + rAtD = rCm; + absZAtD = 0.5f * std::fabs(z1 + z2); + driftAtD = driftHere; + driftOldAtD = driftOldHere; + } + if (dRPhi < bestR) { + bestR = dRPhi; + zAtR = dZ; + } + } + if (nHere == 0) { + continue; + } + s.nPoints[i] = nHere; + s.nSameSidePerCharge[i] = nSame; + s.dMin3DPerCharge[i] = best3; + s.dPairMinPerCharge[i] = bestD; + s.dMinRPhiPerCharge[i] = bestR; + s.dMinRPhiAll = std::min(s.dMinRPhiAll, bestRAll); + s.dPairMinAll = std::min(s.dPairMinAll, bestDAll); + const float invS = (nSame > 0) ? 1.f / static_cast(nSame) : 0.f; + for (std::size_t t = 0; t < nCloseThr.size(); ++t) { + s.nCloseRPhiThrPerCharge[i][t] = nCloseThr[t]; + s.fCloseRPhiThrPerCharge[i][t] = static_cast(nCloseThr[t]) * invS; + } + s.fCloseRPhiPerCharge[i] = s.fCloseRPhiThrPerCharge[i][3]; // o2-linter: disable=magic-number (last threshold) + if (bestD < s.dPairMin) { + s.dPairMin = bestD; + s.dRPhiAtDPairMin = rphiAtD; + s.dZAtDPairMin = zAtD; + s.dZGlobalAtDPairMin = zGlobAtD; + s.dZSignedLocalAtDPairMin = zSgnLocAtD; + s.dZSignedGlobalAtDPairMin = zSgnGlobAtD; + s.rAtDPairMin = rAtD; + s.absZAtDPairMin = absZAtD; + s.driftLen = driftAtD; + s.driftOldAtDPairMin = driftOldAtD; + s.nCloseScaled = nCloseD; + s.fCloseScaled = static_cast(nCloseD) * invS; + } + if (best3 < s.dMin3D) { + s.dMin3D = best3; + s.dRPhiAtMin3D = rphiAt3; + s.dZAtMin3D = zAt3; + s.criticalCharge = i; + } + if (bestR < s.dMinRPhi) { + s.dMinRPhi = bestR; + s.dZAtMinRPhi = zAtR; + s.nCloseRPhiThr = s.nCloseRPhiThrPerCharge[i]; + s.fCloseRPhiThr = s.fCloseRPhiThrPerCharge[i]; + s.fCloseRPhi = s.fCloseRPhiThr[3]; // o2-linter: disable=magic-number (last threshold) + s.nSameSide = nSame; + s.rphiCharge = i; + } + } + s.sameSideAtDPairMin = (s.nSameSidePerCharge[0] + s.nSameSidePerCharge[1]) > 0; + s.nCommon = (s.rphiCharge >= 0) ? s.nPoints[s.rphiCharge] + : std::max(s.nPoints[0], s.nPoints[1]); + return s; + } + + template + [[nodiscard]] inline bool passFastQinvGate(TG1 const& g1, TG2 const& g2) const + { + const float qMax = qaflags.cfgMaxQinvForProcessing.value; + if (qMax > 1e9f) { // o2-linter: disable=magic-number (skip if non-sensical value is chosen) + return true; + } + const float dEta = g1.eta() - g2.eta(); + const float dPhi = RecoDecay::constrainAngle(g1.phi() - g2.phi(), -o2::constants::math::PI); + const float q2 = 2.f * g1.pt() * g2.pt() * (std::cosh(dEta) - std::cos(dPhi)); + return q2 <= qMax * qMax; + } + + [[nodiscard]] inline bool passPairMergeCut(PairSep const& s) const + { + if (!pairsep.cfgDoPairMergeCut.value) { + return true; + } + if (s.nCommon <= 0) { // nothing was propagated: no statement possible + return true; + } + if (s.dPairMin < pairsep.cfgDPairCut.value) { + return false; + } + const int nCut = pairsep.cfgNCloseCut.value; + // dipping close once is survivable, running alongside is not + return nCut <= 0 || s.nCloseScaled < nCut; + } + + [[nodiscard]] inline int tpcRegion(float rCm) const + { + if (rCm < pairsep.cfgRegionEdge1.value) { + return 0; + } + if (rCm < pairsep.cfgRegionEdge2.value) { + return 1; + } + if (rCm < pairsep.cfgRegionEdge3.value) { + return 2; // o2-linter: disable=magic-number (region index) + } + return 3; // o2-linter: disable=magic-number (region index) + } + + [[nodiscard]] inline int driftBin(float driftLen) const + { + const float third = pairsep.cfgTpcHalfLengthCm.value / 3.f; // o2-linter: disable=magic-number (three drift slices) + if (driftLen < third) { + return 0; + } + if (driftLen < 2.f * third) { // o2-linter: disable=magic-number (three drift slices) + return 1; + } + return 2; // o2-linter: disable=magic-number (three drift slices) + } + + [[nodiscard]] CrossObs computeCrossObs(PhotonWithLegs const& a, PhotonWithLegs const& b, float qinv) const + { + constexpr float kMe = 0.000510999f; // electron mass, GeV/c^2 + CrossObs c; + auto legVec = [](PhotonWithLegs const& p, int i) { + return ROOT::Math::PtEtaPhiMVector(p.fLegPt[i], p.fLegEta[i], p.fLegPhi[i], kMe); + }; + // Combo 0: a's e+ with b's e-. Combo 1: b's e+ with a's e-. + for (int combo = 0; combo < 2; ++combo) { // o2-linter: disable=magic-number (the two leg-swap combinations) + PhotonWithLegs const& pPos = (combo == 0) ? a : b; + PhotonWithLegs const& pEle = (combo == 0) ? b : a; + c.mee[combo] = static_cast((legVec(pPos, 0) + legVec(pEle, 1)).M()); + } + if (qinv > 0.f) { + c.meeOverQ = std::min(c.mee[0], c.mee[1]) / qinv; + } + return c; + } + + [[nodiscard]] inline bool passCrossPairVeto(CrossObs const& c) const + { + if (!crosspair.cfgDoCrossPairCut.value) { + return true; + } + return c.meeOverQ > crosspair.cfgCrossMaxMeeRatio.value; + } + + template + inline void fillCrossPair(CrossObs const& c, float qinv, float deltaR, bool vetoed) + { + constexpr auto dir = (ev_id == 0) ? "Pair/same/CrossPair/" : "Pair/mix/CrossPair/"; + fRegistryCF.fill(HIST(dir) + HIST("hVetoCounter"), vetoed ? 1.0 : 0.0); + if (qinv > crosspair.cfgCrossMaxQinvQA.value) { + return; + } + fRegistryCF.fill(HIST(dir) + HIST("hSparse_MeeRatio_dR_Qinv"), c.meeOverQ, deltaR, qinv); + } + + /*************************************************/ + // FILL HELPERS + /*************************************************/ + template + void fillLegSimilarityValues(float etaA, float phiA, float ptA, float nsigA, + float etaB, float phiB, float ptB, float nsigB, float qinv) + { + if (!qaflags.cfgFillLegSimilaritySparse.value || qinv > 0.3f) { // o2-linter: disable=magic-number (sparse axis range) + return; + } + if constexpr (ev_id == 1) { + if (!qaflags.cfgFillLegSimilarityMixSparse.value) { + return; // mixed-event sparse not booked + } + } + const float deta = etaA - etaB; + const float dphi = RecoDecay::constrainAngle(phiA - phiB, -o2::constants::math::PI); + if (std::fabs(deta) > 0.1f || std::fabs(dphi) > 0.1f) { // o2-linter: disable=magic-number (sparse axis range) + return; + } + const float ptSum = ptA + ptB; + const float ptRatio = (ptSum > 0.f) ? std::fabs(ptA - ptB) / ptSum : 0.f; + const float dNsigma = std::fabs(nsigA - nsigB); + if constexpr (ev_id == 0) { + fRegistryPairQA.fill(HIST("Pair/same/LegSimilarity/hSparse_dEta_dPhi_ptRatio_dNsig_Qinv"), + deta, dphi, ptRatio, std::min(dNsigma, 7.99f), qinv); // o2-linter: disable=magic-number (clamp to axis) + } else { + fRegistryPairQA.fill(HIST("Pair/mix/LegSimilarity/hSparse_dEta_dPhi_ptRatio_dNsig_Qinv"), + deta, dphi, ptRatio, std::min(dNsigma, 7.99f), qinv); // o2-linter: disable=magic-number (clamp to axis) + } + } + + template + void fillLegSimilarity(TLegA const& legA, TLegB const& legB, float qinv) + { + fillLegSimilarityValues<0>(legA.eta(), legA.phi(), legA.pt(), legA.tpcNSigmaEl(), + legB.eta(), legB.phi(), legB.pt(), legB.tpcNSigmaEl(), qinv); + } + + void fillLegSimilarityMix(PhotonWithLegs const& a, PhotonWithLegs const& b, float qinv) + { + fillLegSimilarityValues<1>(a.legEta(0), a.legPhi(0), a.legPt(0), a.legNsigEl(0), + b.legEta(0), b.legPhi(0), b.legPt(0), b.legNsigEl(0), qinv); + fillLegSimilarityValues<1>(a.legEta(1), a.legPhi(1), a.legPt(1), a.legNsigEl(1), + b.legEta(1), b.legPhi(1), b.legPt(1), b.legNsigEl(1), qinv); + } + + template + inline void fillFullRangeQA(PairQAObservables const& obs, float cent, float occupancy) + { + constexpr auto base = fullRangePrefix(); + fRegistryCF.fill(HIST(base) + HIST("hDeltaRVsQinv"), obs.qinv, obs.deltaR); + fRegistryCF.fill(HIST(base) + HIST("hDeltaZVsQinv"), obs.qinv, obs.deltaZ); + fRegistryCF.fill(HIST(base) + HIST("hDeltaR3DVsQinv"), obs.qinv, obs.deltaR3D); + fRegistryCF.fill(HIST(base) + HIST("hQinvVsCent"), cent, obs.qinv); + fRegistryCF.fill(HIST(base) + HIST("hQinvVsOccupancy"), occupancy, obs.qinv); + if (mQa.fillDRDZSparse) { + fRegistryCF.fill(HIST(base) + HIST("hSparseDeltaRDeltaZQinv"), obs.deltaR, obs.deltaZ, obs.qinv); + } + if (mQa.fillR1R2Sparse) { + // both orderings: the pair is unordered + fRegistryCF.fill(HIST(base) + HIST("hSparseR1R2Qinv"), obs.r1, obs.r2, obs.qinv); + fRegistryCF.fill(HIST(base) + HIST("hSparseR1R2Qinv"), obs.r2, obs.r1, obs.qinv); + } + } + + template + inline void fillPairSep(PairSep const& s, PairQAObservables const& obs) + { + if (!mQa.pairSepQa) { + return; + } + // the AfterCut directories only exist when a pair cut is active + if constexpr (after_cut) { + if (!mQa.pairSepAfterCut) { + return; + } + } + const float limit = pairsep.cfgPairSepMaxQinv.value; + if (limit > 0.f && obs.qinv >= limit) { + return; + } + constexpr auto dir = pairSepPrefix(); + if (s.nCommon > 0) { + fRegistryCF.fill(HIST(dir) + HIST("hSparse_dRPhiAtMin3D_dZAtMin3D_Qinv"), s.dRPhiAtMin3D, s.dZAtMin3D, obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hSparse_dMin3D_fClose_Qinv"), s.dMin3D, s.fCloseRPhi, obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hSparse_fCloseRPhi_dR_Qinv"), s.fCloseRPhi, obs.deltaR, obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hDRPhiMin_All_vs_SameSide"), s.dMinRPhiAll, s.dMinRPhi); + fRegistryCF.fill(HIST(dir) + HIST("hDMin3D_PP_vs_NN"), s.dMin3DPerCharge[0], s.dMin3DPerCharge[1]); + fRegistryCF.fill(HIST(dir) + HIST("hFClose_PP_vs_NN"), s.fCloseRPhiPerCharge[0], s.fCloseRPhiPerCharge[1]); + for (std::size_t t = 0; t < s.fCloseRPhiThr.size(); ++t) { + fRegistryCF.fill(HIST(dir) + HIST("hSparse_dRPhiMin_fClose_Thr_Qinv"), s.dMinRPhi, + s.fCloseRPhiThr[t], static_cast(t), obs.qinv); + } + fRegistryCF.fill(HIST(dir) + HIST("hSparse_DPair_NClose_Qinv"), s.dPairMin, static_cast(s.nCloseScaled), obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hDzLocal_vs_dVtxZ"), s.dVtxZ, s.dZAtDPairMin); + fRegistryCF.fill(HIST(dir) + HIST("hDzGlobal_vs_dVtxZ"), s.dVtxZ, s.dZGlobalAtDPairMin); + fRegistryCF.fill(HIST(dir) + HIST("hDzLocalSigned_vs_dVtxZ"), s.dVtxZ, s.dZSignedLocalAtDPairMin); + fRegistryCF.fill(HIST(dir) + HIST("hDzGlobalSigned_vs_dVtxZ"), s.dVtxZ, s.dZSignedGlobalAtDPairMin); + if (s.sameSideAtDPairMin) { + fRegistryCF.fill(HIST(dir) + HIST("hDriftOld_vs_DriftNew"), s.driftOldAtDPairMin, s.driftLen); + } else { + fRegistryCF.fill(HIST(dir) + HIST("hDriftOld_vs_DriftNew_opp"), s.driftOldAtDPairMin, s.driftLen); + } + fRegistryCF.fill(HIST(dir) + HIST("hSameSide_vs_Qinv"), s.sameSideAtDPairMin ? 1.f : 0.f, obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hNCommon_vs_Qinv"), static_cast(s.nCommon), obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hNSameSide_vs_Qinv"), static_cast(s.nSameSide), obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hSparse_FClose_NClose_Qinv"), s.fCloseScaled, static_cast(s.nCloseScaled), obs.qinv); + if (pairsep.cfgFillCalibSparse.value) { + for (int ic = 0; ic < 2; ++ic) { // o2-linter: disable=magic-number (PP and NN) + for (int k = 0; k < s.nPoints[ic]; ++k) { + fRegistryCF.fill(HIST(dir) + HIST("hSparse_DzSignedLocal_dVtxZ_R"), + s.ptDZSgnLocal[ic][k], s.dVtxZ, s.ptR[ic][k]); + if (s.ptSameSide[ic][k] < 0.5f) { // o2-linter: disable=magic-number (indication of side) + fRegistryCF.fill(HIST(dir) + HIST("hSparse_Calib_OppositeSide"), s.ptDRPhi[ic][k], s.ptDZ[ic][k]); + continue; // cannot merge: different endcap + } + fRegistryCF.fill(HIST(dir) + HIST("hSparse_Calib_dRPhi_dZ_R_L_C_Q"), s.ptDRPhi[ic][k], s.ptDZ[ic][k], + static_cast(tpcRegion(s.ptR[ic][k])), static_cast(driftBin(s.ptDrift[ic][k])), + static_cast(ic), obs.qinv); + fRegistryCF.fill(HIST(dir) + HIST("hSparse_dRPhi_dZ_LegClass_L_Q"), s.ptDRPhi[ic][k], s.ptDZ[ic][k], + (s.nLegsITSTPC >= 2) ? 1.f : 0.f, // o2-linter: disable=magic-number (two leg classes) + static_cast(driftBin(s.ptDrift[ic][k])), obs.qinv); + } + } + } + if (obs.qinv >= pairsep.cfgCondQinvMin.value && obs.qinv < pairsep.cfgCondQinvMax.value && + s.rAtDPairMin > 0.f) { + const auto cond = static_cast(3 * tpcRegion(s.rAtDPairMin) + driftBin(s.driftLen)); // o2-linter: disable=magic-number (three drift bins per region) + fRegistryCF.fill(HIST(dir) + HIST("hSparse_dRPhi_dZ_Cond"), s.dRPhiAtDPairMin, s.dZAtDPairMin, cond); + } + } + } + + template + inline void fillFullRangeDeltaRCosOA(float qinv, float drOverCosOA) + { + constexpr auto base = fullRangePrefix(); + fRegistryCF.fill(HIST(base) + HIST("hDeltaRCosOAVsQinv"), qinv, drOverCosOA); } template static constexpr const char* singlePhotonQAPrefix() { - if constexpr (step_id == 0) - return "SinglePhoton/Before/"; - if constexpr (step_id == 1) - return "SinglePhoton/AfterDRCosOA/"; - if constexpr (step_id == 2) // o2-linter: disable=magic-number (just counting the step of a cut) - return "SinglePhoton/AfterRZ/"; - return "SinglePhoton/AfterEllipse/"; + return (step_id == 0) ? "SinglePhoton/Before/" : "SinglePhoton/AfterPairCuts/"; } template inline void fillSinglePhotonQAStep(TPhoton const& g) { - if (!qaflags.doSinglePhotonQa) + if (!mQa.singlePhotonQa) { return; + } constexpr auto base = singlePhotonQAPrefix(); const float r = std::sqrt(g.vx() * g.vx() + g.vy() * g.vy()); fRegistryPairQA.fill(HIST(base) + HIST("hEtaVsPhiPt"), g.phi(), g.eta(), g.pt()); @@ -756,11 +2080,14 @@ struct Photonhbt { } template - void fillPairHistogram(TCollision const& /*collision*/, - ROOT::Math::PtEtaPhiMVector v1, - ROOT::Math::PtEtaPhiMVector v2, + void fillPairHistogram(TCollision const& collision, + ROOT::Math::PtEtaPhiMVector const& v1, + ROOT::Math::PtEtaPhiMVector const& v2, float weight = 1.f) { + + const float multNTracks = collision.multNTracksPVeta1(); + const float multFT0M = collision.multFT0M(); float rndm = std::pow(-1, dist01(engine) % 2); auto k12 = 0.5 * (v1 + v2); float kt = k12.Pt(); @@ -778,31 +2105,63 @@ struct Photonhbt { float qside_lcms = q3_lcms.Dot(uv_side); float qlong_lcms = q3_lcms.Dot(uv_long); if (hbtanalysis.cfgDo3D) { - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("CF_3D"), - std::fabs(qout_lcms), std::fabs(qside_lcms), std::fabs(qlong_lcms), kt, weight); - if (hbtanalysis.cfgDo2D) - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("CF_2D"), - std::fabs(qout_lcms), std::fabs(qinv), kt, weight); + if constexpr (ev_id == 0) { + fRegistryCF.fill(HIST("Pair/same/CF_3D"), std::fabs(qout_lcms), std::fabs(qside_lcms), std::fabs(qlong_lcms), kt, weight); + if (hbtanalysis.cfgDo2D) { + fRegistryCF.fill(HIST("Pair/same/CF_2D"), std::fabs(qout_lcms), std::fabs(qinv), multNTracks, multFT0M, kt, weight); + if (hbtanalysis.cfgDo2DSideLong) { + fRegistryCF.fill(HIST("Pair/same/CF_2D_Side"), std::fabs(qside_lcms), std::fabs(qinv), multNTracks, multFT0M, kt, weight); + fRegistryCF.fill(HIST("Pair/same/CF_2D_Long"), std::fabs(qlong_lcms), std::fabs(qinv), multNTracks, multFT0M, kt, weight); + } + } + if (hbtanalysis.cfgDoQinvGate3D) { + fRegistryCF.fill(HIST("Pair/same/CF_3D_Qinv"), std::fabs(qout_lcms), std::fabs(qside_lcms), + std::fabs(qlong_lcms), kt, std::fabs(qinv), weight); + } + } else { + fRegistryCF.fill(HIST("Pair/mix/CF_3D"), std::fabs(qout_lcms), std::fabs(qside_lcms), std::fabs(qlong_lcms), kt, weight); + if (hbtanalysis.cfgDo2D) { + fRegistryCF.fill(HIST("Pair/mix/CF_2D"), std::fabs(qout_lcms), std::fabs(qinv), multNTracks, multFT0M, kt, weight); + if (hbtanalysis.cfgDo2DSideLong) { + fRegistryCF.fill(HIST("Pair/mix/CF_2D_Side"), std::fabs(qside_lcms), std::fabs(qinv), multNTracks, multFT0M, kt, weight); + fRegistryCF.fill(HIST("Pair/mix/CF_2D_Long"), std::fabs(qlong_lcms), std::fabs(qinv), multNTracks, multFT0M, kt, weight); + } + } + if (hbtanalysis.cfgDoQinvGate3D) { + fRegistryCF.fill(HIST("Pair/mix/CF_3D_Qinv"), std::fabs(qout_lcms), std::fabs(qside_lcms), + std::fabs(qlong_lcms), kt, std::fabs(qinv), weight); + } + } + } else { + if constexpr (ev_id == 0) { + fRegistryCF.fill(HIST("Pair/same/CF_1D"), hbtanalysis.cfgUseLCMS ? qabs_lcms : qinv, kt, weight); + } else { + fRegistryCF.fill(HIST("Pair/mix/CF_1D"), hbtanalysis.cfgUseLCMS ? qabs_lcms : qinv, kt, weight); + } + } + if constexpr (ev_id == 0) { + fRegistryCF.fill(HIST("Pair/same/CF_QLcms_Qinv"), qabs_lcms, std::fabs(qinv), kt, weight); } else { - fRegistry.fill(HIST("Pair/") + HIST(event_pair_types[ev_id]) + HIST("CF_1D"), - hbtanalysis.cfgUseLCMS ? qabs_lcms : qinv, kt, weight); + fRegistryCF.fill(HIST("Pair/mix/CF_QLcms_Qinv"), qabs_lcms, std::fabs(qinv), kt, weight); } float deta_pair = v1.Eta() - v2.Eta(); float dphi_pair = v1.Phi() - v2.Phi(); dphi_pair = RecoDecay::constrainAngle(dphi_pair, -o2::constants::math::PI); if constexpr (ev_id == 0) { - fRegistry.fill(HIST("Pair/same/hSparse_DEtaDPhi_qinv_kT"), deta_pair, dphi_pair, qinv, kt, weight); + fRegistryCF.fill(HIST("Pair/same/hSparse_DEtaDPhi_qinv_kT"), deta_pair, dphi_pair, qinv, kt, weight); } else { - fRegistry.fill(HIST("Pair/mix/hSparse_DEtaDPhi_qinv_kT"), deta_pair, dphi_pair, qinv, kt, weight); + fRegistryCF.fill(HIST("Pair/mix/hSparse_DEtaDPhi_qinv_kT"), deta_pair, dphi_pair, qinv, kt, weight); } } template - void fillPairHistogramMC(TCollision const& /*collision*/, - ROOT::Math::PtEtaPhiMVector v1, - ROOT::Math::PtEtaPhiMVector v2, + void fillPairHistogramMC(TCollision const& collision, + ROOT::Math::PtEtaPhiMVector const& v1, + ROOT::Math::PtEtaPhiMVector const& v2, float weight = 1.f) { + const float multNTracks = collision.multNTracksPVeta1(); + const float multFT0M = collision.multFT0M(); float rndm = std::pow(-1, dist01(engine) % 2); auto k12 = 0.5 * (v1 + v2); float kt = k12.Pt(); @@ -819,103 +2178,25 @@ struct Photonhbt { float qout_lcms = q3_lcms.Dot(uv_out); float qside_lcms = q3_lcms.Dot(uv_side); float qlong_lcms = q3_lcms.Dot(uv_long); - constexpr auto mcDir = []() constexpr -> const char* { - if constexpr (ev_id == 0) { - if constexpr (TruthT == PairTruthType::TrueTrueDistinct) - return "Pair/same/MC/TrueTrueDistinct/"; - if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) - return "Pair/same/MC/TrueTrueSamePhoton/"; - if constexpr (TruthT == PairTruthType::SharedMcLeg) - return "Pair/same/MC/SharedMcLeg/"; - if constexpr (TruthT == PairTruthType::TrueFake) - return "Pair/same/MC/TrueFake/"; - if constexpr (TruthT == PairTruthType::FakeFake) - return "Pair/same/MC/FakeFake/"; - return "Pair/same/MC/Pi0Daughters/"; - } else { - if constexpr (TruthT == PairTruthType::TrueTrueDistinct) - return "Pair/mix/MC/TrueTrueDistinct/"; - if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) - return "Pair/mix/MC/TrueTrueSamePhoton/"; - if constexpr (TruthT == PairTruthType::SharedMcLeg) - return "Pair/mix/MC/SharedMcLeg/"; - if constexpr (TruthT == PairTruthType::TrueFake) - return "Pair/mix/MC/TrueFake/"; - if constexpr (TruthT == PairTruthType::FakeFake) - return "Pair/mix/MC/FakeFake/"; - return "Pair/mix/MC/Pi0Daughters/"; - } - }(); + constexpr auto mcDir = mcDirPrefix(); if (hbtanalysis.cfgDo3D) { fRegistryPairMC.fill(HIST(mcDir) + HIST("CF_3D"), std::fabs(qout_lcms), std::fabs(qside_lcms), std::fabs(qlong_lcms), kt, weight); - if (hbtanalysis.cfgDo2D) - fRegistryPairMC.fill(HIST(mcDir) + HIST("CF_2D"), std::fabs(qout_lcms), std::fabs(qinv), kt, weight); + if (hbtanalysis.cfgDo2D) { + fRegistryPairMC.fill(HIST(mcDir) + HIST("CF_2D"), std::fabs(qout_lcms), std::fabs(qinv), multNTracks, multFT0M, kt, weight); + } } else { fRegistryPairMC.fill(HIST(mcDir) + HIST("CF_1D"), hbtanalysis.cfgUseLCMS ? qabs_lcms : qinv, kt, weight); } - float deta_pair = v1.Eta() - v2.Eta(); - float dphi_pair = v1.Phi() - v2.Phi(); - dphi_pair = RecoDecay::constrainAngle(dphi_pair, -o2::constants::math::PI); - if constexpr (ev_id == 0) { - fRegistry.fill(HIST("Pair/same/hSparse_DEtaDPhi_qinv_kT"), deta_pair, dphi_pair, qinv, kt, weight); - } else { - fRegistry.fill(HIST("Pair/mix/hSparse_DEtaDPhi_qinv_kT"), deta_pair, dphi_pair, qinv, kt, weight); - } - } - - template - PairQAObservables buildPairQAObservables(TG1 const& g1, TG2 const& g2) - { - PairQAObservables o{}; - o.x1 = g1.vx(); - o.y1 = g1.vy(); - o.z1 = g1.vz(); - o.x2 = g2.vx(); - o.y2 = g2.vy(); - o.z2 = g2.vz(); - o.r1 = std::sqrt(o.x1 * o.x1 + o.y1 * o.y1); - o.r2 = std::sqrt(o.x2 * o.x2 + o.y2 * o.y2); - o.dx = o.x1 - o.x2; - o.dy = o.y1 - o.y2; - o.dz = o.z1 - o.z2; - o.deltaR = std::fabs(o.r1 - o.r2); - o.deltaZ = o.dz; - o.deltaRxy = std::sqrt(o.dx * o.dx + o.dy * o.dy); - o.deltaR3D = std::sqrt(o.dx * o.dx + o.dy * o.dy + o.dz * o.dz); - ROOT::Math::XYZVector cp1(o.x1, o.y1, o.z1), cp2(o.x2, o.y2, o.z2); - const float mag1 = std::sqrt(cp1.Mag2()), mag2 = std::sqrt(cp2.Mag2()); - if (mag1 < kMinMagnitude || mag2 < kMinMagnitude) { - o.valid = false; - return o; - } - float cosPA = static_cast(cp1.Dot(cp2) / (mag1 * mag2)); - cosPA = std::clamp(cosPA, -1.f, 1.f); - o.opa = std::acos(cosPA); - o2::math_utils::bringTo02Pi(o.opa); - if (o.opa > o2::constants::math::PI) - o.opa -= o2::constants::math::PI; - o.cosOA = std::cos(o.opa / 2.f); - o.drOverCosOA = (std::fabs(o.cosOA) < kMinCosine) ? 1e12f : (o.deltaR3D / o.cosOA); - o.v1 = ROOT::Math::PtEtaPhiMVector(g1.pt(), g1.eta(), g1.phi(), 0.f); - o.v2 = ROOT::Math::PtEtaPhiMVector(g2.pt(), g2.eta(), g2.phi(), 0.f); - o.k12 = 0.5f * (o.v1 + o.v2); - o.deta = g1.eta() - g2.eta(); - o.dphi = RecoDecay::constrainAngle(g1.phi() - g2.phi(), -o2::constants::math::PI); - o.pairEta = 0.5f * (g1.eta() + g2.eta()); - o.pairPhi = RecoDecay::constrainAngle(o.k12.Phi(), 0.f); - o.kt = o.k12.Pt(); - o.qinv = std::fabs((o.v1 - o.v2).M()); - o.cosTheta = std::fabs(computeCosTheta(o.v1, o.v2)); - o.openingAngle = o.opa; - return o; + fRegistryPairMC.fill(HIST(mcDir) + HIST("CF_QLcms_Qinv"), qabs_lcms, std::fabs(qinv), kt, weight); } template inline void fillPairQAStep(PairQAObservables const& o, float /*cent*/, float /*occupancy*/) { - if (!qaflags.doPairQa) + if (!mQa.pairQa) { return; + } constexpr auto base = qaPrefix(); ///// Delta Eta QA @@ -945,395 +2226,91 @@ struct Photonhbt { fRegistryPairQA.fill(HIST(base) + HIST("hDeltaR3DKt"), o.deltaR3D, o.kt); const float sE = ggpaircuts.cfgEllipseSigEta.value, sP = ggpaircuts.cfgEllipseSigPhi.value; - if (sE > kMinSigma && sP > kMinSigma) + if (sE > kMinSigma && sP > kMinSigma) { fRegistryPairQA.fill(HIST(base) + HIST("hEllipseVal"), (o.deta / sE) * (o.deta / sE) + (o.dphi / sP) * (o.dphi / sP)); - } - - template - static PhotonMCInfo buildPhotonMCInfo(TPhoton const& g, TMCParticles const& mcParticles) - { - PhotonMCInfo info{}; - const auto pos = g.template posTrack_as(); - const auto neg = g.template negTrack_as(); - if (!pos.has_emmcparticle() || !neg.has_emmcparticle()) - return info; - info.hasMC = true; - info.mcPosId = pos.emmcparticleId(); - info.mcNegId = neg.emmcparticleId(); - const auto mcPos = pos.template emmcparticle_as(); - const auto mcNeg = neg.template emmcparticle_as(); - if (!mcPos.has_mothers() || !mcNeg.has_mothers()) - return info; - const int mothIdPos = mcPos.mothersIds()[0], mothIdNeg = mcNeg.mothersIds()[0]; - if (mothIdPos != mothIdNeg) - return info; - info.sameMother = true; - info.motherId = mothIdPos; - const auto mother = mcParticles.iteratorAt(mothIdPos); - info.motherPdg = mother.pdgCode(); - info.isTruePhoton = (info.motherPdg == kGamma); - info.isPhysicalPrimary = mother.isPhysicalPrimary(); - return info; - } - - static PairTruthType classifyPairTruth(PhotonMCInfo const& m1, PhotonMCInfo const& m2) - { - const bool t1 = m1.hasMC && m1.sameMother && m1.isTruePhoton; - const bool t2 = m2.hasMC && m2.sameMother && m2.isTruePhoton; - if (m1.hasMC && m2.hasMC) { - if ((m1.mcPosId >= 0 && (m1.mcPosId == m2.mcPosId || m1.mcPosId == m2.mcNegId)) || - (m1.mcNegId >= 0 && (m1.mcNegId == m2.mcPosId || m1.mcNegId == m2.mcNegId))) - return PairTruthType::SharedMcLeg; - } - if (!t1 && !t2) - return PairTruthType::FakeFake; - if (t1 != t2) - return PairTruthType::TrueFake; - if (m1.motherId >= 0 && m1.motherId == m2.motherId) - return PairTruthType::TrueTrueSamePhoton; - return PairTruthType::TrueTrueDistinct; - } - - enum class EtaTopology : uint8_t { - SameSideV0SameSideLegs = 0, ///< both V0 eta same sign; all 4 legs same sign - SameSideV0MixedLegs = 1, ///< both V0 eta same sign; legs not all on that sign - DiffSideV0MatchingLegs = 2, ///< V0s on opposite sides; each V0's legs match its own side - DiffSideV0CrossedLegs = 3, ///< V0s on opposite sides; legs do NOT match their V0's side - }; - - /// Classify the eta-side topology of a photon pair from the 6 raw eta values. - static EtaTopology classifyEtaTopology(float v1eta, float v2eta, - float pos1eta, float neg1eta, - float pos2eta, float neg2eta) - { - const bool v1pos = v1eta >= 0.f; - const bool v2pos = v2eta >= 0.f; - if (v1pos == v2pos) { // same-side V0s - const bool allSame = - ((pos1eta >= 0.f) == v1pos) && ((neg1eta >= 0.f) == v1pos) && - ((pos2eta >= 0.f) == v1pos) && ((neg2eta >= 0.f) == v1pos); - return allSame ? EtaTopology::SameSideV0SameSideLegs - : EtaTopology::SameSideV0MixedLegs; - } - // different-side V0s - const bool v1match = ((pos1eta >= 0.f) == v1pos) && ((neg1eta >= 0.f) == v1pos); - const bool v2match = ((pos2eta >= 0.f) == v2pos) && ((neg2eta >= 0.f) == v2pos); - return (v1match && v2match) ? EtaTopology::DiffSideV0MatchingLegs - : EtaTopology::DiffSideV0CrossedLegs; - } - - inline void fillEtaTopologyHisto(EtaTopology topo, float deta, float dphi, float kt) - { - switch (topo) { - case EtaTopology::SameSideV0SameSideLegs: - fRegistry.fill(HIST("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_sameSideV0_sameSideLegs"), - deta, dphi, kt); - break; - case EtaTopology::SameSideV0MixedLegs: - fRegistry.fill(HIST("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_sameSideV0_mixedLegs"), - deta, dphi, kt); - break; - case EtaTopology::DiffSideV0MatchingLegs: - fRegistry.fill(HIST("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_diffSideV0_matchingLegs"), - deta, dphi, kt); - break; - case EtaTopology::DiffSideV0CrossedLegs: - fRegistry.fill(HIST("Pair/same/EtaTopology/hSparse_DEtaDPhi_kT_diffSideV0_crossedLegs"), - deta, dphi, kt); - break; } } - template - static bool isPi0DaughterPair(PhotonMCInfo const& m1, PhotonMCInfo const& m2, - TMCParticles const& mcParticles) - { - if (!m1.isTruePhoton || !m2.isTruePhoton || m1.motherId < 0 || m2.motherId < 0) - return false; - const auto ph1 = mcParticles.iteratorAt(m1.motherId); - const auto ph2 = mcParticles.iteratorAt(m2.motherId); - if (!ph1.has_mothers() || !ph2.has_mothers()) - return false; - const int gm1 = ph1.mothersIds()[0], gm2 = ph2.mothersIds()[0]; - if (gm1 != gm2) - return false; - return (std::abs(mcParticles.iteratorAt(gm1).pdgCode()) == kPi0); - } - - static constexpr std::string_view pairTruthLabel(PairTruthType t) + template + inline void fillLegPairQAStep(LegPairObservables const& lo, float kt) { - switch (t) { - case PairTruthType::TrueTrueDistinct: - return "TrueTrueDistinct/"; - case PairTruthType::TrueTrueSamePhoton: - return "TrueTrueSamePhoton/"; - case PairTruthType::SharedMcLeg: - return "SharedMcLeg/"; - case PairTruthType::TrueFake: - return "TrueFake/"; - case PairTruthType::FakeFake: - return "FakeFake/"; - case PairTruthType::Pi0Daughters: - return "Pi0Daughters/"; - default: - return "Unknown/"; + if (!mQa.legPairQa) { + return; } - } - void addMCHistograms() - { - const AxisSpec axisTruthType{{0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5}, - "truth type (1=TrueTrueDistinct,2=TrueTrueSamePhoton,3=SharedMcLeg," - "4=TrueFake,5=FakeFake,6=Pi0Daughters)"}; - const AxisSpec axisDeltaEtaMC{90, -1.6f, +1.6f, "#Delta#eta"}; - const AxisSpec axisDeltaPhiMC{90, -o2::constants::math::PI, +o2::constants::math::PI, "#Delta#phi (rad)"}; - const AxisSpec axQinvMC{60, 0.f, 0.3f, "q_{inv}^{true} (GeV/c)"}; - const AxisSpec axRconv{180, 0.f, 90.f, "R_{conv}^{true} (cm)"}; - const AxisSpec axAlpha{100, -1.f, 1.f, "#alpha^{true}"}; - const AxisSpec axLegDR{100, 0.f, 0.3f, "leg #Delta R^{true}"}; - - // fRegistryPairMC — reco-level pair histograms, per MC truth type - - // Per-type CF + observables - static constexpr std::array kTypes = { - "TrueTrueDistinct/", "TrueTrueSamePhoton/", "SharedMcLeg/", - "TrueFake/", "FakeFake/", "Pi0Daughters/"}; - - for (const auto& label : kTypes) { - const std::string base = std::string("Pair/same/MC/") + std::string(label); - - // CF - if (hbtanalysis.cfgDo3D) { - fRegistryPairMC.add((base + "CF_3D").c_str(), "MC CF 3D LCMS", - kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt}, true); - if (hbtanalysis.cfgDo2D) - fRegistryPairMC.add((base + "CF_2D").c_str(), "MC CF 2D", - kTHnSparseD, {axisQout, axisQinv, axisKt}, true); - } else { - fRegistryPairMC.add((base + "CF_1D").c_str(), - hbtanalysis.cfgUseLCMS ? "MC CF 1D LCMS" : "MC CF 1D (qinv)", - kTH2D, {hbtanalysis.cfgUseLCMS ? axisQabsLcms : axisQinv, axisKt}, true); - } - - // 1D observables - fRegistryPairMC.add((base + "hQinv").c_str(), "q_{inv};q_{inv} (GeV/c);counts", kTH1D, {axisQinv}, true); - fRegistryPairMC.add((base + "hDeltaR").c_str(), "|R_{1}-R_{2}|;|R_{1}-R_{2}| (cm);counts", kTH1D, {axisDeltaR}, true); - fRegistryPairMC.add((base + "hDeltaZ").c_str(), "#Delta z;#Delta z (cm);counts", kTH1D, {axisDeltaZ}, true); - fRegistryPairMC.add((base + "hDeltaR3D").c_str(), "#Delta r_{3D};#Delta r_{3D} (cm);counts", kTH1D, {axisDeltaR3D}, true); - - // 2D observables - fRegistryPairMC.add((base + "hDEtaDPhi").c_str(), "#Delta#eta vs #Delta#phi", kTH2D, {axisDeltaEta, axisDeltaPhi}, true); - fRegistryPairMC.add((base + "hDeltaRVsQinv").c_str(), "|R_{1}-R_{2}| vs q_{inv}", kTH2D, {axisQinv, axisDeltaR}, true); - fRegistryPairMC.add((base + "hDeltaZVsQinv").c_str(), "#Delta z vs q_{inv}", kTH2D, {axisQinv, axisDeltaZ}, true); - fRegistryPairMC.add((base + "hDeltaR3DVsQinv").c_str(), "#Delta r_{3D} vs q_{inv}", kTH2D, {axisQinv, axisDeltaR3D}, true); - - // Sparse (conditional) - fRegistryPairMC.add((base + "hSparse_DEtaDPhi_kT").c_str(), - "#Delta#eta,#Delta#phi,k_{T};#Delta#eta;#Delta#phi (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisKt}, true); - - const bool addDEtaDPhiVsQinv = - (label == "TrueTrueDistinct/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueDistinct.value - : (label == "TrueTrueSamePhoton/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueSamePhoton.value - : (label == "SharedMcLeg/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvSharedMcLeg.value - : (label == "TrueFake/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueFake.value - : (label == "FakeFake/") ? mctruthSparse.cfgFillDEtaDPhiVsQinvFakeFake.value - : mctruthSparse.cfgFillDEtaDPhiVsQinvPi0Daughters.value; - if (addDEtaDPhiVsQinv) - fRegistryPairMC.add((base + "hDEtaDPhiVsQinv").c_str(), - "#Delta#eta vs #Delta#phi vs q_{inv}", kTHnSparseD, - {axisDeltaEtaMC, axisDeltaPhiMC, axisQinv}, true); + constexpr auto base = qaPrefix<0, step_id>(); - const bool addDRDZQinv = - (label == "TrueTrueDistinct/") ? mctruthSparse.cfgFillDRDZQinvTrueTrueDistinct.value - : (label == "TrueTrueSamePhoton/") ? mctruthSparse.cfgFillDRDZQinvTrueTrueSamePhoton.value - : (label == "SharedMcLeg/") ? mctruthSparse.cfgFillDRDZQinvSharedMcLeg.value - : (label == "TrueFake/") ? mctruthSparse.cfgFillDRDZQinvTrueFake.value - : (label == "FakeFake/") ? mctruthSparse.cfgFillDRDZQinvFakeFake.value - : mctruthSparse.cfgFillDRDZQinvPi0Daughters.value; - if (addDRDZQinv) - fRegistryPairMC.add((base + "hSparseDeltaRDeltaZQinv").c_str(), - "|R_{1}-R_{2}|,#Delta z,q_{inv}", kTHnSparseD, - {axisDeltaR, axisDeltaZ, axisQinv}, true); + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hDEtaDPhi_PP"), lo.dEtaPP, lo.dPhiPP); + if (qaflags.cfgFillLegPairSparses.value) { + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hSparse_DEtaDPhi_kT_minLegPt_PP"), lo.dEtaPP, lo.dPhiPP, kt, lo.minLegPt_PP); } + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hDR_PP"), lo.dRPP); + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hLegPtLow_vs_LegPtHigh_PP"), lo.minLegPt_PP, lo.maxLegPt_PP); + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hDR_PP_vs_minLegPt"), lo.dRPP, lo.minLegPt_PP); - fRegistryPairMC.add("Pair/same/MC/hTruthTypeVsQinv", - "truth type vs q_{inv};q_{inv} (GeV/c);truth type", kTH2D, {axisQinv, axisTruthType}, true); - fRegistryPairMC.add("Pair/same/MC/hTruthTypeVsKt", - "truth type vs k_{T};k_{T} (GeV/c);truth type", kTH2D, {axisKt, axisTruthType}, true); - - fRegistryPairMC.add("Pair/same/MC/hDEtaDPhi_truePairs", - "true reco pairs (TrueTrueDistinct+SamePhoton+Pi0);" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", - kTH2D, {axisDeltaEta, axisDeltaPhi}, true); - fRegistryPairMC.add("Pair/same/MC/hDEtaDPhi_fakePairs", - "fake reco pairs (FakeFake+TrueFake+SharedMcLeg);" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", - kTH2D, {axisDeltaEta, axisDeltaPhi}, true); - fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_kT_truePairs", - "true pairs: #Delta#eta,#Delta#phi,k_{T};" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisKt}, true); - fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_kT_fakePairs", - "fake pairs: #Delta#eta,#Delta#phi,k_{T};" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisKt}, true); - fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_qinv_truePairs", - "true pairs: #Delta#eta,#Delta#phi,q_{inv};" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);q_{inv} (GeV/c)", - kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisQinv}, true); - fRegistryPairMC.add("Pair/same/MC/hSparse_DEtaDPhi_qinv_fakePairs", - "fake pairs: #Delta#eta,#Delta#phi,q_{inv};" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);q_{inv} (GeV/c)", - kTHnSparseD, {axisDeltaEtaMC, axisDeltaPhiMC, axisQinv}, true); - - // ─── Pairs with missing MC label ───────────────────────────────────────── - if (hbtanalysis.cfgDo3D) { - fRegistryPairMC.add("Pair/same/MC/NoLabel/CF_3D", - "missing MC label — CF 3D LCMS", kTHnSparseD, {axisQout, axisQside, axisQlong, axisKt}, true); - if (hbtanalysis.cfgDo2D) - fRegistryPairMC.add("Pair/same/MC/NoLabel/CF_2D", - "missing MC label — CF 2D", kTHnSparseD, {axisQout, axisQinv, axisKt}, true); - } else { - fRegistryPairMC.add("Pair/same/MC/NoLabel/CF_1D", - hbtanalysis.cfgUseLCMS ? "missing MC label — CF 1D LCMS" : "missing MC label — CF 1D (qinv)", - kTH2D, {hbtanalysis.cfgUseLCMS ? axisQabsLcms : axisQinv, axisKt}, true); + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hDEtaDPhi_NN"), lo.dEtaNN, lo.dPhiNN); + if (qaflags.cfgFillLegPairSparses.value) { + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hSparse_DEtaDPhi_kT_minLegPt_NN"), lo.dEtaNN, lo.dPhiNN, kt, lo.minLegPt_NN); } - fRegistryPairMC.add("Pair/same/MC/NoLabel/hDEtaDPhi", - "missing MC label: #Delta#eta vs #Delta#phi;" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", - kTH2D, {axisDeltaEta, axisDeltaPhi}, true); - fRegistryPairMC.add("Pair/same/MC/NoLabel/hQinv", - "missing MC label: q_{inv};q_{inv} (GeV/c);counts", kTH1D, {axisQinv}, true); - fRegistryPairMC.add("Pair/same/MC/NoLabel/hKt", - "missing MC label: k_{T};k_{T} (GeV/c);counts", kTH1D, {axisKt}, true); - - // fRegistryMC — truth-level histograms + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hDR_NN"), lo.dRNN); + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hLegPtLow_vs_LegPtHigh_NN"), lo.minLegPt_NN, lo.maxLegPt_NN); + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hDR_NN_vs_minLegPt"), lo.dRNN, lo.minLegPt_NN); - // ─── Truth-level CF - fRegistryMC.add("MC/TruthCF/hQinvVsKt_same", "truth-level same-event CF;k_{T} (GeV/c);q_{inv}^{true} (GeV/c)", kTH2D, {axisKt, axQinvMC}, true); - fRegistryMC.add("MC/TruthCF/hDEtaDPhi_same", - "truth-level same-event #Delta#eta vs #Delta#phi;" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", - kTH2D, {axisDeltaEta, axisDeltaPhi}, true); - fRegistryMC.add("MC/TruthCF/hQinvVsKt_mix", "truth-level mixed-event CF;k_{T} (GeV/c);q_{inv}^{true} (GeV/c)", kTH2D, {axisKt, axQinvMC}, true); - fRegistryMC.add("MC/TruthCF/hDEtaDPhi_mix", - "truth-level mixed-event #Delta#eta vs #Delta#phi;" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", - kTH2D, {axisDeltaEta, axisDeltaPhi}, true); - - auto addStageHistos = [&](const char* suffix, const char* title, auto... axes) { - for (const auto& stage : {"truthConverted", "all4LegsThisColl", - "bothPhotonsBuilt", "bothPhotonsSelected"}) { - const std::string name = std::string("MC/TruthAO2D/") + suffix + std::string("_") + stage; - const std::string ttl = std::string(title) + std::string(" [") + stage + "]"; - fRegistryMC.add(name.c_str(), ttl.c_str(), kTHnD, {axes...}, true); - } - }; - auto addStageHistos2D = [&](const char* suffix, const char* title, auto ax1, auto ax2) { - for (const auto& stage : {"truthConverted", "all4LegsThisColl", - "bothPhotonsBuilt", "bothPhotonsSelected"}) { - const std::string name = std::string("MC/TruthAO2D/") + suffix + std::string("_") + stage; - fRegistryMC.add(name.c_str(), title, kTH2D, {ax1, ax2}, true); - } - }; + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hMinDR_LS_vs_Kt"), lo.minDR_LS, kt); - addStageHistos("hSparse_DEtaDPhi_qinv", - "#Delta#eta,#Delta#phi,q_{inv}^{true};" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);q_{inv}^{true} (GeV/c)", - axisDeltaEta, axisDeltaPhi, axQinvMC); + fRegistryPairQA.fill(HIST(base) + HIST("LegUScross/hDEtaDPhi_PN12"), lo.dEtaPN12, lo.dPhiPN12); + fRegistryPairQA.fill(HIST(base) + HIST("LegUScross/hDEtaDPhi_NP12"), lo.dEtaNP12, lo.dPhiNP12); - addStageHistos("hSparse_DEtaDPhi_kT", - "#Delta#eta,#Delta#phi,k_{T};" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", - axisDeltaEta, axisDeltaPhi, axisKt); + if (qaflags.cfgFillLegPairSparses.value) { + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hSparse_DEtaDPhi_PtG1_PtG2_PP"), lo.dEtaPP, lo.dPhiPP, lo.ptG1, lo.ptG2); + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hSparse_DEtaDPhi_PtG1_PtG2_NN"), lo.dEtaNN, lo.dPhiNN, lo.ptG1, lo.ptG2); + } + fRegistryPairQA.fill(HIST(base) + HIST("LegLS/hPtG1_vs_PtG2"), lo.ptG1, lo.ptG2); + } + + template + inline void fillLegPairMCTyped(LegPairObservables const& lo, float kt) + { + constexpr auto base = mcDirPrefix(); + fRegistryPairMC.fill(HIST(base) + HIST("LegLS/hSparse_DR_kT_minLegPt_PP"), lo.dRPP, kt, lo.minLegPt_PP); + fRegistryPairMC.fill(HIST(base) + HIST("LegLS/hSparse_DR_kT_minLegPt_NN"), lo.dRNN, kt, lo.minLegPt_NN); + if (qaflags.cfgFillLegPairSparses.value) { + fRegistryPairMC.fill(HIST(base) + HIST("LegLS/hSparse_DEtaDPhi_PtG1_PtG2_PP"), lo.dEtaPP, lo.dPhiPP, lo.ptG1, lo.ptG2); + fRegistryPairMC.fill(HIST(base) + HIST("LegLS/hSparse_DEtaDPhi_PtG1_PtG2_NN"), lo.dEtaNN, lo.dPhiNN, lo.ptG1, lo.ptG2); + } + } - addStageHistos2D("hQinvVsKt", - "q_{inv}^{true} vs k_{T};k_{T} (GeV/c);q_{inv}^{true} (GeV/c)", - axisKt, axQinvMC); - - addStageHistos2D("hDEtaDPhi", - "#Delta#eta vs #Delta#phi;#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad)", - axisDeltaEta, axisDeltaPhi); - - // Rconv waterfall (nur converted + selected) - fRegistryMC.add("MC/TruthAO2D/hRconv1_vs_Rconv2_truthConverted", - "denominator: R_{conv,1} vs R_{conv,2};R_{conv,1}^{true} (cm);R_{conv,2}^{true} (cm)", - kTH2D, {axRconv, axRconv}, true); - fRegistryMC.add("MC/TruthAO2D/hRconv1_vs_Rconv2_bothPhotonsSelected", - "numerator: R_{conv,1} vs R_{conv,2};R_{conv,1}^{true} (cm);R_{conv,2}^{true} (cm)", - kTH2D, {axRconv, axRconv}, true); - fRegistryMC.add("MC/TruthAO2D/hMinRconv_vs_kT_truthConverted", - "denominator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", - kTH2D, {axisKt, axRconv}, true); - fRegistryMC.add("MC/TruthAO2D/hMinRconv_vs_kT_bothPhotonsSelected", - "numerator: min(R_{conv}) vs k_{T};k_{T} (GeV/c);min(R_{conv}^{true}) (cm)", - kTH2D, {axisKt, axRconv}, true); - - // Stage waterfall summary + consistency - fRegistryMC.add("MC/TruthAO2D/hStage_vs_kT", - "efficiency waterfall;k_{T} (GeV/c);stage (0=converted,1=all4legs,2=bothBuilt,3=bothSel)", - kTH2D, {axisKt, AxisSpec{4, -0.5f, 3.5f, "stage"}}, true); - fRegistryMC.add("MC/TruthAO2D/hStageConsistency", - "stage consistency (expect all at 0);N(V0 built but legs not found);counts", - kTH1D, {AxisSpec{20, -0.5f, 19.5f, "N_{bad}"}}, true); - - // ─── Single-leg diagnostics ─────────────────────────────────────────────── - fRegistryMC.add("MC/LegDiag/hLegDRtrue_vs_pt_legFound", "leg found: #Delta R^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#Delta R_{e^{+}e^{-}}^{true}", kTH2D, {axisPt, axLegDR}, true); - fRegistryMC.add("MC/LegDiag/hLegDRtrue_vs_pt_legMissing", "leg missing: #Delta R^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#Delta R_{e^{+}e^{-}}^{true}", kTH2D, {axisPt, axLegDR}, true); - fRegistryMC.add("MC/LegDiag/hLegDEta_legFound_vs_pt", "leg found: |#Delta#eta| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#eta_{e^{+}e^{-}}|", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#eta_{legs}|"}}, true); - fRegistryMC.add("MC/LegDiag/hLegDEta_legMissing_vs_pt", "leg missing: |#Delta#eta| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#eta_{e^{+}e^{-}}|", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#eta_{legs}|"}}, true); - fRegistryMC.add("MC/LegDiag/hLegDPhi_legFound_vs_pt", "leg found: |#Delta#phi| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#phi_{e^{+}e^{-}}| (rad)", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#phi_{legs}|"}}, true); - fRegistryMC.add("MC/LegDiag/hLegDPhi_legMissing_vs_pt", "leg missing: |#Delta#phi| vs p_{T};p_{T,#gamma}^{true} (GeV/c);|#Delta#phi_{e^{+}e^{-}}| (rad)", kTH2D, {axisPt, AxisSpec{100, 0.f, 0.5f, "|#Delta#phi_{legs}|"}}, true); - fRegistryMC.add("MC/LegDiag/hAlphaTrue_legFound_vs_pt", "leg found: #alpha^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#alpha^{true}", kTH2D, {axisPt, axAlpha}, true); - fRegistryMC.add("MC/LegDiag/hAlphaTrue_legMissing_vs_pt", "leg missing: #alpha^{true} vs p_{T};p_{T,#gamma}^{true} (GeV/c);#alpha^{true}", kTH2D, {axisPt, axAlpha}, true); - fRegistryMC.add("MC/LegDiag/hAlpha_vs_legDR_legMissing", "leg missing: #alpha^{true} vs #Delta R^{true};#Delta R_{e^{+}e^{-}}^{true};#alpha^{true}", kTH2D, {axLegDR, axAlpha}, true); - - // ─── Pair-level leg diagnostics ─────────────────────────────────────────── - fRegistryMC.add("MC/LegDiag/hNLegsPair_vs_kT", "N legs found per pair vs k_{T};k_{T} (GeV/c);N_{legs found} (0-4)", kTH2D, {axisKt, AxisSpec{5, -0.5f, 4.5f, "N_{legs found}"}}, true); - fRegistryMC.add("MC/LegDiag/hMissingLegPt_vs_kT", "missing leg p_{T}^{true} vs pair k_{T};k_{T} (GeV/c);p_{T,leg}^{true} (GeV/c)", kTH2D, {axisKt, AxisSpec{100, 0.f, 0.5f, "p_{T,leg}^{true} (GeV/c)"}}, true); - fRegistryMC.add("MC/LegDiag/hMissingLegRconv_vs_kT", "missing leg R_{conv}^{true} vs pair k_{T};k_{T} (GeV/c);R_{conv}^{true} (cm)", kTH2D, {axisKt, axisR}, true); - - // ─── Cross-built V0 pairs ───────────────────────────────────────────────── - fRegistryMC.add("MC/PairCrossBuild/hSparse_DEtaDPhi_kT", - "cross-built V0 pairs: #Delta#eta,#Delta#phi,k_{T};" - "#Delta#eta_{#gamma#gamma};#Delta#phi_{#gamma#gamma} (rad);k_{T} (GeV/c)", - kTHnSparseD, {axisDeltaEta, axisDeltaPhi, axisKt}, true); - fRegistryMC.add("MC/PairCrossBuild/hStageOut_vs_kT", - "cross-built pairs: N correctly built vs k_{T};" - "k_{T} (GeV/c);N photons correctly built (0/1/2)", - kTH2D, {axisKt, AxisSpec{3, -0.5f, 2.5f, "N correctly built"}}, true); + inline void fillLegPairMC(PairTruthType t, LegPairObservables const& lo, float kt) + { + switch (t) { + case PairTruthType::TrueTrueDistinct: + fillLegPairMCTyped(lo, kt); + break; + case PairTruthType::TrueTrueSamePhoton: + fillLegPairMCTyped(lo, kt); + break; + case PairTruthType::SharedMcLeg: + fillLegPairMCTyped(lo, kt); + break; + case PairTruthType::TrueFake: + fillLegPairMCTyped(lo, kt); + break; + case PairTruthType::FakeFake: + fillLegPairMCTyped(lo, kt); + break; + case PairTruthType::Pi0Daughters: + fillLegPairMCTyped(lo, kt); + break; + default: + break; + } + fRegistryPairMC.fill(HIST("Pair/same/MC/hMinDR_LS_vs_Kt_TruthType"), + lo.minDR_LS, kt, static_cast(t)); } template inline void fillMCPairQATyped(PairQAObservables const& obs, bool doSparse, bool doMCQA) { - constexpr auto base = []() constexpr -> const char* { - if constexpr (!IsMix) { - if constexpr (TruthT == PairTruthType::TrueTrueDistinct) - return "Pair/same/MC/TrueTrueDistinct/"; - if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) - return "Pair/same/MC/TrueTrueSamePhoton/"; - if constexpr (TruthT == PairTruthType::SharedMcLeg) - return "Pair/same/MC/SharedMcLeg/"; - if constexpr (TruthT == PairTruthType::TrueFake) - return "Pair/same/MC/TrueFake/"; - if constexpr (TruthT == PairTruthType::FakeFake) - return "Pair/same/MC/FakeFake/"; - return "Pair/same/MC/Pi0Daughters/"; - } else { - if constexpr (TruthT == PairTruthType::TrueTrueDistinct) - return "Pair/mix/MC/TrueTrueDistinct/"; - if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) - return "Pair/mix/MC/TrueTrueSamePhoton/"; - if constexpr (TruthT == PairTruthType::SharedMcLeg) - return "Pair/mix/MC/SharedMcLeg/"; - if constexpr (TruthT == PairTruthType::TrueFake) - return "Pair/mix/MC/TrueFake/"; - if constexpr (TruthT == PairTruthType::FakeFake) - return "Pair/mix/MC/FakeFake/"; - return "Pair/mix/MC/Pi0Daughters/"; - } - }(); + constexpr auto base = mcDirPrefix(); if (doMCQA) { fRegistryPairMC.fill(HIST(base) + HIST("hDEtaDPhi"), obs.deta, obs.dphi); fRegistryPairMC.fill(HIST(base) + HIST("hQinv"), obs.qinv); @@ -1341,8 +2318,9 @@ struct Photonhbt { fRegistryPairMC.fill(HIST(base) + HIST("hDeltaZ"), obs.deltaZ); fRegistryPairMC.fill(HIST(base) + HIST("hDeltaR3D"), obs.deltaR3D); } - if (doSparse) + if (doSparse) { fRegistryPairMC.fill(HIST(base) + HIST("hSparse_DEtaDPhi_kT"), obs.deta, obs.dphi, obs.kt); + } constexpr auto summaryDir = IsMix ? "Pair/mix/MC/" : "Pair/same/MC/"; if (doMCQA) { fRegistryPairMC.fill(HIST(summaryDir) + HIST("hTruthTypeVsQinv"), obs.qinv, static_cast(TruthT)); @@ -1380,50 +2358,33 @@ struct Photonhbt { template inline void fillMCPairQAFullRangeTyped(PairQAObservables const& obs) { - constexpr auto base = []() constexpr -> const char* { - if constexpr (!IsMix) { - if constexpr (TruthT == PairTruthType::TrueTrueDistinct) - return "Pair/same/MC/TrueTrueDistinct/"; - if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) - return "Pair/same/MC/TrueTrueSamePhoton/"; - if constexpr (TruthT == PairTruthType::SharedMcLeg) - return "Pair/same/MC/SharedMcLeg/"; - if constexpr (TruthT == PairTruthType::TrueFake) - return "Pair/same/MC/TrueFake/"; - if constexpr (TruthT == PairTruthType::FakeFake) - return "Pair/same/MC/FakeFake/"; - return "Pair/same/MC/Pi0Daughters/"; - } else { - if constexpr (TruthT == PairTruthType::TrueTrueDistinct) - return "Pair/mix/MC/TrueTrueDistinct/"; - if constexpr (TruthT == PairTruthType::TrueTrueSamePhoton) - return "Pair/mix/MC/TrueTrueSamePhoton/"; - if constexpr (TruthT == PairTruthType::SharedMcLeg) - return "Pair/mix/MC/SharedMcLeg/"; - if constexpr (TruthT == PairTruthType::TrueFake) - return "Pair/mix/MC/TrueFake/"; - if constexpr (TruthT == PairTruthType::FakeFake) - return "Pair/mix/MC/FakeFake/"; - return "Pair/mix/MC/Pi0Daughters/"; - } - }(); + constexpr auto base = mcDirPrefix(); fRegistryPairMC.fill(HIST(base) + HIST("hDeltaRVsQinv"), obs.qinv, obs.deltaR); fRegistryPairMC.fill(HIST(base) + HIST("hDeltaZVsQinv"), obs.qinv, obs.deltaZ); fRegistryPairMC.fill(HIST(base) + HIST("hDeltaR3DVsQinv"), obs.qinv, obs.deltaR3D); - const bool fillDRDZ = ((TruthT == PairTruthType::TrueTrueDistinct) ? mctruthSparse.cfgFillDRDZQinvTrueTrueDistinct.value : (TruthT == PairTruthType::TrueTrueSamePhoton) ? mctruthSparse.cfgFillDRDZQinvTrueTrueSamePhoton.value - : (TruthT == PairTruthType::SharedMcLeg) ? mctruthSparse.cfgFillDRDZQinvSharedMcLeg.value - : (TruthT == PairTruthType::TrueFake) ? mctruthSparse.cfgFillDRDZQinvTrueFake.value - : (TruthT == PairTruthType::FakeFake) ? mctruthSparse.cfgFillDRDZQinvFakeFake.value - : mctruthSparse.cfgFillDRDZQinvPi0Daughters.value); - if (fillDRDZ) + + const bool fillDRDZ = + (TruthT == PairTruthType::TrueTrueDistinct) ? mctruthSparse.cfgFillDRDZQinvTrueTrueDistinct.value + : (TruthT == PairTruthType::TrueTrueSamePhoton) ? mctruthSparse.cfgFillDRDZQinvTrueTrueSamePhoton.value + : (TruthT == PairTruthType::SharedMcLeg) ? mctruthSparse.cfgFillDRDZQinvSharedMcLeg.value + : (TruthT == PairTruthType::TrueFake) ? mctruthSparse.cfgFillDRDZQinvTrueFake.value + : (TruthT == PairTruthType::FakeFake) ? mctruthSparse.cfgFillDRDZQinvFakeFake.value + : mctruthSparse.cfgFillDRDZQinvPi0Daughters.value; + + if (fillDRDZ) { fRegistryPairMC.fill(HIST(base) + HIST("hSparseDeltaRDeltaZQinv"), obs.deltaR, obs.deltaZ, obs.qinv); - const bool enabled = ((TruthT == PairTruthType::TrueTrueDistinct) ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueDistinct.value : (TruthT == PairTruthType::TrueTrueSamePhoton) ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueSamePhoton.value - : (TruthT == PairTruthType::SharedMcLeg) ? mctruthSparse.cfgFillDEtaDPhiVsQinvSharedMcLeg.value - : (TruthT == PairTruthType::TrueFake) ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueFake.value - : (TruthT == PairTruthType::FakeFake) ? mctruthSparse.cfgFillDEtaDPhiVsQinvFakeFake.value - : mctruthSparse.cfgFillDEtaDPhiVsQinvPi0Daughters.value); - if (enabled) + } + + const bool enabled = + (TruthT == PairTruthType::TrueTrueDistinct) ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueDistinct.value + : (TruthT == PairTruthType::TrueTrueSamePhoton) ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueTrueSamePhoton.value + : (TruthT == PairTruthType::SharedMcLeg) ? mctruthSparse.cfgFillDEtaDPhiVsQinvSharedMcLeg.value + : (TruthT == PairTruthType::TrueFake) ? mctruthSparse.cfgFillDEtaDPhiVsQinvTrueFake.value + : (TruthT == PairTruthType::FakeFake) ? mctruthSparse.cfgFillDEtaDPhiVsQinvFakeFake.value + : mctruthSparse.cfgFillDEtaDPhiVsQinvPi0Daughters.value; + if (enabled) { fRegistryPairMC.fill(HIST(base) + HIST("hDEtaDPhiVsQinv"), obs.deta, obs.dphi, obs.qinv); + } } template @@ -1452,11 +2413,14 @@ struct Photonhbt { break; } } + template - void fillPairHistogramNoLabel(TCollision const& /*collision*/, - ROOT::Math::PtEtaPhiMVector v1, - ROOT::Math::PtEtaPhiMVector v2) + void fillPairHistogramNoLabel(TCollision const& collision, + ROOT::Math::PtEtaPhiMVector const& v1, + ROOT::Math::PtEtaPhiMVector const& v2) { + const float multNTracks = collision.multNTracksPVeta1(); + const float multFT0M = collision.multFT0M(); float rndm = std::pow(-1, dist01(engine) % 2); auto k12 = 0.5 * (v1 + v2); float kt = k12.Pt(); @@ -1474,15 +2438,203 @@ struct Photonhbt { float qside_lcms = q3_lcms.Dot(uv_side); float qlong_lcms = q3_lcms.Dot(uv_long); if (hbtanalysis.cfgDo3D) { - fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/CF_3D"), - std::fabs(qout_lcms), std::fabs(qside_lcms), std::fabs(qlong_lcms), kt); - if (hbtanalysis.cfgDo2D) - fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/CF_2D"), std::fabs(qout_lcms), std::fabs(qinv), kt); + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/CF_3D"), std::fabs(qout_lcms), std::fabs(qside_lcms), std::fabs(qlong_lcms), kt); + if (hbtanalysis.cfgDo2D) { + fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/CF_2D"), std::fabs(qout_lcms), std::fabs(qinv), multNTracks, multFT0M, kt); + } } else { fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/CF_1D"), hbtanalysis.cfgUseLCMS ? qabs_lcms : qinv, kt); } } + /*************************************************/ + // BUILDERS + /*************************************************/ + + template + PairQAObservables buildPairQAObservables(TG1 const& g1, TG2 const& g2) + { + PairQAObservables o{}; + o.x1 = g1.vx(); + o.y1 = g1.vy(); + o.z1 = g1.vz(); + o.x2 = g2.vx(); + o.y2 = g2.vy(); + o.z2 = g2.vz(); + o.r1 = std::sqrt(o.x1 * o.x1 + o.y1 * o.y1); + o.r2 = std::sqrt(o.x2 * o.x2 + o.y2 * o.y2); + o.dx = o.x1 - o.x2; + o.dy = o.y1 - o.y2; + o.dz = o.z1 - o.z2; + o.deltaR = std::fabs(o.r1 - o.r2); + o.deltaZ = o.dz; + o.deltaRxy = std::sqrt(o.dx * o.dx + o.dy * o.dy); + o.deltaR3D = std::sqrt(o.dx * o.dx + o.dy * o.dy + o.dz * o.dz); + ROOT::Math::XYZVector cp1(o.x1, o.y1, o.z1), cp2(o.x2, o.y2, o.z2); + const float mag1 = std::sqrt(cp1.Mag2()), mag2 = std::sqrt(cp2.Mag2()); + if (mag1 < kMinMagnitude || mag2 < kMinMagnitude) { + o.valid = false; + return o; + } + auto cosPA = static_cast(cp1.Dot(cp2) / (mag1 * mag2)); + cosPA = std::clamp(cosPA, -1.f, 1.f); + o.opa = std::acos(cosPA); + o2::math_utils::bringTo02Pi(o.opa); + if (o.opa > o2::constants::math::PI) { + o.opa = 2.f * o2::constants::math::PI - o.opa; + } + o.cosOA = std::cos(o.opa / 2.f); + o.drOverCosOA = (std::fabs(o.cosOA) < kMinCosine) ? 1e12f : (o.deltaR3D / o.cosOA); + o.v1 = ROOT::Math::PtEtaPhiMVector(g1.pt(), g1.eta(), g1.phi(), 0.f); + o.v2 = ROOT::Math::PtEtaPhiMVector(g2.pt(), g2.eta(), g2.phi(), 0.f); + o.k12 = 0.5f * (o.v1 + o.v2); + o.deta = g1.eta() - g2.eta(); + o.dphi = RecoDecay::constrainAngle(g1.phi() - g2.phi(), -o2::constants::math::PI); + o.pairEta = 0.5f * (g1.eta() + g2.eta()); + o.pairPhi = RecoDecay::constrainAngle(o.k12.Phi(), 0.f); + o.kt = o.k12.Pt(); + o.qinv = std::fabs((o.v1 - o.v2).M()); + o.cosTheta = std::fabs(computeCosTheta(o.v1, o.v2)); + o.openingAngle = o.opa; + return o; + } + + template + static PhotonMCInfo buildPhotonMCInfo(TPhoton const& g, TMCParticles const& mcParticles) + { + PhotonMCInfo info{}; + const auto pos = g.template posTrack_as(); + const auto neg = g.template negTrack_as(); + if (!pos.has_emmcparticle() || !neg.has_emmcparticle()) { + return info; + } + info.hasMC = true; + info.mcPosId = pos.emmcparticleId(); + info.mcNegId = neg.emmcparticleId(); + const auto mcPos = pos.template emmcparticle_as(); + const auto mcNeg = neg.template emmcparticle_as(); + if (!mcPos.has_mothers() || !mcNeg.has_mothers()) { + return info; + } + const int mothIdPos = mcPos.mothersIds()[0], mothIdNeg = mcNeg.mothersIds()[0]; + info.posMotherId = mothIdPos; + info.negMotherId = mothIdNeg; + info.posMotherIsPhoton = (mcParticles.iteratorAt(mothIdPos).pdgCode() == kGamma); + info.negMotherIsPhoton = (mcParticles.iteratorAt(mothIdNeg).pdgCode() == kGamma); + if (mothIdPos != mothIdNeg) { + return info; + } + info.sameMother = true; + info.motherId = mothIdPos; + const auto mother = mcParticles.iteratorAt(mothIdPos); + info.motherPdg = mother.pdgCode(); + info.isTruePhoton = (info.motherPdg == kGamma); + info.isPhysicalPrimary = mother.isPhysicalPrimary(); + return info; + } + + static PairTruthType classifyPairTruth(PhotonMCInfo const& m1, PhotonMCInfo const& m2) + { + const bool t1 = m1.hasMC && m1.sameMother && m1.isTruePhoton; + const bool t2 = m2.hasMC && m2.sameMother && m2.isTruePhoton; + if (m1.hasMC && m2.hasMC) { + if ((m1.mcPosId >= 0 && (m1.mcPosId == m2.mcPosId || m1.mcPosId == m2.mcNegId)) || + (m1.mcNegId >= 0 && (m1.mcNegId == m2.mcPosId || m1.mcNegId == m2.mcNegId))) { + return PairTruthType::SharedMcLeg; + } + } + if (!t1 && !t2) { + return PairTruthType::FakeFake; + } + if (t1 != t2) { + return PairTruthType::TrueFake; + } + if (m1.motherId >= 0 && m1.motherId == m2.motherId) { + return PairTruthType::TrueTrueSamePhoton; + } + return PairTruthType::TrueTrueDistinct; + } + + // 0 = electron from a photon conversion, 1 = pi0 Dalitz electron, 2 = eta Dalitz electron, 3 = primary electron, 4 = other electron, 5 = not an electron at all (-> PID contamination) + template + static int classifyLegOrigin(TMCParticles const& mcParticles, int legMcId) + { + constexpr int kNotElectron = 5; + if (legMcId < 0) { + return kNotElectron; + } + const auto particle = mcParticles.iteratorAt(legMcId); + if (std::abs(particle.pdgCode()) != PDG_t::kElectron) { + return kNotElectron; + } + if (!particle.has_mothers()) { + return 3; // primary electron + } + const int motherPdg = mcParticles.iteratorAt(particle.mothersIds()[0]).pdgCode(); + if (motherPdg == kGamma) { + return 0; + } + if (motherPdg == kPi0) { + return 1; + } + if (motherPdg == o2::constants::physics::Pdg::kEta) { + return 2; + } + return 4; + } + + static int classifyFakeSubtype(PhotonMCInfo const& m1, PhotonMCInfo const& m2) + { + const bool cross12 = m1.posMotherId >= 0 && m1.posMotherId == m2.negMotherId && m1.posMotherIsPhoton; + const bool cross21 = m2.posMotherId >= 0 && m2.posMotherId == m1.negMotherId && m2.posMotherIsPhoton; + if (cross12 && cross21) { + return 1; + } + return (cross12 || cross21) ? 2 : 0; + } + + template + static bool isPi0DaughterPair(PhotonMCInfo const& m1, PhotonMCInfo const& m2, + TMCParticles const& mcParticles) + { + if (!m1.isTruePhoton || !m2.isTruePhoton || m1.motherId < 0 || m2.motherId < 0) { + return false; + } + const auto ph1 = mcParticles.iteratorAt(m1.motherId); + const auto ph2 = mcParticles.iteratorAt(m2.motherId); + if (!ph1.has_mothers() || !ph2.has_mothers()) { + return false; + } + const int gm1 = ph1.mothersIds()[0], gm2 = ph2.mothersIds()[0]; + if (gm1 != gm2) { + return false; + } + return (std::abs(mcParticles.iteratorAt(gm1).pdgCode()) == kPi0); + } + + static constexpr std::string_view pairTruthLabel(PairTruthType t) + { + switch (t) { + case PairTruthType::TrueTrueDistinct: + return "TrueTrueDistinct/"; + case PairTruthType::TrueTrueSamePhoton: + return "TrueTrueSamePhoton/"; + case PairTruthType::SharedMcLeg: + return "SharedMcLeg/"; + case PairTruthType::TrueFake: + return "TrueFake/"; + case PairTruthType::FakeFake: + return "FakeFake/"; + case PairTruthType::Pi0Daughters: + return "Pi0Daughters/"; + default: + return "Unknown/"; + } + } + /*************************************************/ + // MAIN LOOP FOR PAIRING + /*************************************************/ + template cent = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (cent[mixing.cfgCentEstimator] < centralitySelection.cfgCentMin || centralitySelection.cfgCentMax < cent[mixing.cfgCentEstimator]) { continue; + } const std::array epArr = {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2fv0a(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg()}; const float ep2 = epArr[mixing.cfgEP2EstimatorForMix]; fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); - if (!fEMEventCut.IsSelected(collision)) + if (!fEMEventCut.IsSelected(collision)) { continue; + } o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); @@ -1521,133 +2675,188 @@ struct Photonhbt { auto keyDFCollision = std::make_pair(ndf, collision.globalIndex()); auto photons1Coll = photons1.sliceBy(perCollision1, collision.globalIndex()); auto photons2Coll = photons2.sliceBy(perCollision2, collision.globalIndex()); - if (qaflags.doSinglePhotonQa) - for (const auto& g : photons1Coll) - if (cut1.template IsSelected(g)) + if (mQa.singlePhotonQa) { + for (const auto& g : photons1Coll) { + if (cut1.template IsSelected(g)) { fillSinglePhotonQAStep<0>(g); - std::unordered_set idsAfterDR, idsAfterRZ, idsAfterEllipse; + } + } + } + std::unordered_set idsAfterPairCuts; for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photons1Coll, photons2Coll))) { if (!cut1.template IsSelected(g1) || - !cut2.template IsSelected(g2)) + !cut2.template IsSelected(g2)) { continue; + } const auto pos1 = g1.template posTrack_as(), ele1 = g1.template negTrack_as(); const auto pos2 = g2.template posTrack_as(), ele2 = g2.template negTrack_as(); if (pos1.trackId() == pos2.trackId() || pos1.trackId() == ele2.trackId() || - ele1.trackId() == pos2.trackId() || ele1.trackId() == ele2.trackId()) + ele1.trackId() == pos2.trackId() || ele1.trackId() == ele2.trackId()) { continue; - if (!passAsymmetryCut(g1.pt(), g2.pt())) + } + if (!passAsymmetryCut(g1.pt(), g2.pt())) { continue; + } auto obs = buildPairQAObservables(g1, g2); - - if (!obs.valid) + if (!obs.valid) { continue; + } const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); - if (doQA) + const auto legObs = buildLegPairObservables(g1, g2, pos1, ele1, pos2, ele2); + const auto pwl1 = makePhotonWithLegs(g1, pos1, ele1, collision.posZ()); + const auto pwl2 = makePhotonWithLegs(g2, pos2, ele2, collision.posZ()); + const auto sep = computePairSep(pwl1, pwl2); + + // ───before pair cuts ───────────────────────────────────── + fillPairSep<0>(sep, obs); + if (doQA) { fillPairQAStep<0, 0>(obs, centForQA, occupancy); - if (doFR) + fillLegPairQAStep<0>(legObs, obs.kt); + } + if (doFR) { fillFullRangeDeltaRCosOA<0>(obs.qinv, obs.drOverCosOA); - fRegistry.fill(HIST("Pair/same/hDeltaRCosOA"), obs.drOverCosOA); - if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) + } + + fillLegSimilarity(pos1, pos2, obs.qinv); + fillLegSimilarity(ele1, ele2, obs.qinv); + + // ─── Pair cuts ──────────────────────────────────────────────────── + if (!passPhotonClassPairCut(pwl1.legCounts(), pwl2.legCounts())) { continue; - idsAfterDR.insert(g1.globalIndex()); - idsAfterDR.insert(g2.globalIndex()); - if (doQA) - fillPairQAStep<0, 1>(obs, centForQA, occupancy); - if (!passRZCut(obs.deltaR, obs.deltaZ)) + } + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { + continue; + } + if (!passPairMergeCut(sep)) { continue; - idsAfterRZ.insert(g1.globalIndex()); - idsAfterRZ.insert(g2.globalIndex()); - if (doQA) - fillPairQAStep<0, 2>(obs, centForQA, occupancy); - if (isInsideEllipse(obs.deta, obs.dphi)) + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + continue; + } + + if (isInsideEllipse(obs.deta, obs.dphi)) { continue; - idsAfterEllipse.insert(g1.globalIndex()); - idsAfterEllipse.insert(g2.globalIndex()); - if (doQA) - fillPairQAStep<0, 3>(obs, centForQA, occupancy); - if (doFR) + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair<0>(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + + // ───after pair cuts ────────────────────────────────────── + fillPairSep<0, true>(sep, obs); + idsAfterPairCuts.insert(g1.globalIndex()); + idsAfterPairCuts.insert(g2.globalIndex()); + if (doQA) { + fillPairQAStep<0, 1>(obs, centForQA, occupancy); + fillLegPairQAStep<1>(legObs, obs.kt); + } + if (doFR) { fillFullRangeQA<0>(obs, centForQA, occupancy); + } fillPairHistogram<0>(collision, obs.v1, obs.v2, 1.f); ndiphoton++; - fRegistry.fill(HIST("Pair/same/hPhi_lowerPtV0"), - (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); - - fillEtaTopologyHisto( - classifyEtaTopology(g1.eta(), g2.eta(), - pos1.eta(), ele1.eta(), - pos2.eta(), ele2.eta()), - obs.deta, obs.dphi, obs.kt); - auto addToPool = [&](auto const& g) { + fRegistryCF.fill(HIST("Pair/same/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + + auto addToPool = [&](auto const& g, PhotonWithLegs const& p) { if (usedPhotonIdsPerCol.insert(g.globalIndex()).second) { - EMPair gtmp(g.pt(), g.eta(), g.phi(), 0.f); - gtmp.setConversionPointXYZ(g.vx(), g.vy(), g.vz()); - emh1->AddTrackToEventPool(keyDFCollision, gtmp); + emh1->AddTrackToEventPool(keyDFCollision, p); } }; - addToPool(g1); - addToPool(g2); + addToPool(g1, pwl1); + addToPool(g2, pwl2); } - if (qaflags.doSinglePhotonQa) { + if (mQa.singlePhotonQa) { for (const auto& g : photons1Coll) { if (cut1.template IsSelected(g)) { - const int gid = g.globalIndex(); - if (idsAfterDR.count(gid)) { + if (idsAfterPairCuts.contains(g.globalIndex())) { fillSinglePhotonQAStep<1>(g); } - if (idsAfterRZ.count(gid)) { - fillSinglePhotonQAStep<2>(g); - } - if (idsAfterEllipse.count(gid)) { - fillSinglePhotonQAStep<3>(g); - } } } } usedPhotonIdsPerCol.clear(); - if (!mixing.cfgDoMix || ndiphoton == 0) + if (!mixing.cfgDoMix || ndiphoton == 0) { continue; - auto selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); - auto poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + } + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); for (const auto& mixID : poolIDs) { - if (mixID.second == collision.globalIndex() && mixID.first == ndf) + if (mixID.second == collision.globalIndex() && mixID.first == ndf) { continue; + } const uint64_t bcMix = mapMixedEventIdToGlobalBC[mixID]; const uint64_t diffBC = std::max(collision.globalBC(), bcMix) - std::min(collision.globalBC(), bcMix); - fRegistry.fill(HIST("Pair/mix/hDiffBC"), diffBC); - if (diffBC < mixing.ndiffBCMix) + fRegistryCF.fill(HIST("Pair/mix/hDiffBC"), diffBC); + if (diffBC < mixing.ndiffBCMix) { continue; - auto poolPhotons = emh1->GetTracksPerCollision(mixID); - for (const auto& g1 : selectedPhotons) + } + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); + for (const auto& g1 : selectedPhotons) { for (const auto& g2 : poolPhotons) { - if (!passAsymmetryCut(g1.pt(), g2.pt())) + if (!passAsymmetryCut(g1.pt(), g2.pt())) { + continue; + } + if (!passFastQinvGate(g1, g2)) { continue; + } auto obs = buildPairQAObservables(g1, g2); - if (!obs.valid) + if (!obs.valid) { continue; + } const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); - if (doQA) + const auto sep = computePairSep(g1, g2); + + // ───before pair cuts ───────────────────────────────── + fillPairSep<1>(sep, obs); + if (doQA) { fillPairQAStep<1, 0>(obs, centForQA, occupancy); - if (doFR) + } + if (doFR) { fillFullRangeDeltaRCosOA<1>(obs.qinv, obs.drOverCosOA); - fRegistry.fill(HIST("Pair/mix/hDeltaRCosOA"), obs.drOverCosOA); - if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) + } + + fillLegSimilarityMix(g1, g2, obs.qinv); + + // ─── Pair cuts ──────────────────────────────────────────────── + if (!passPhotonClassPairCut(g1.legCounts(), g2.legCounts())) { continue; - if (doQA) - fillPairQAStep<1, 1>(obs, centForQA, occupancy); - if (!passRZCut(obs.deltaR, obs.deltaZ)) + } + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { + continue; + } + if (!passPairMergeCut(sep)) { + continue; + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { continue; - if (doQA) - fillPairQAStep<1, 2>(obs, centForQA, occupancy); - if (isInsideEllipse(obs.deta, obs.dphi)) + } + if (isInsideEllipse(obs.deta, obs.dphi)) { continue; - if (doQA) - fillPairQAStep<1, 3>(obs, centForQA, occupancy); - if (doFR) + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + const auto cross = computeCrossObs(g1, g2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + + fillPairSep<1, true>(sep, obs); + if (doQA) { + fillPairQAStep<1, 1>(obs, centForQA, occupancy); + } + if (doFR) { fillFullRangeQA<1>(obs, centForQA, occupancy); + } fillPairHistogram<1>(collision, obs.v1, obs.v2, 1.f); - fRegistry.fill(HIST("Pair/mix/hPhi_lowerPtV0"), - (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + fRegistryCF.fill(HIST("Pair/mix/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); } + } } if (ndiphoton > 0) { emh1->AddCollisionIdAtLast(keyBin, keyDFCollision); @@ -1657,6 +2866,10 @@ struct Photonhbt { } } + /*************************************************/ + // MAIN LOOP FOR PAIRING MC INFORMATION + /*************************************************/ + template @@ -1667,16 +2880,18 @@ struct Photonhbt { for (const auto& collision : collisions) { initCCDB(collision); int ndiphoton = 0; - const float cent[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; - if (cent[mixing.cfgCentEstimator] < centralitySelection.cfgCentMin || centralitySelection.cfgCentMax < cent[mixing.cfgCentEstimator]) + const std::array cent = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + if (cent[mixing.cfgCentEstimator] < centralitySelection.cfgCentMin || centralitySelection.cfgCentMax < cent[mixing.cfgCentEstimator]) { continue; + } const std::array epArr = {collision.ep2ft0m(), collision.ep2ft0a(), collision.ep2ft0c(), collision.ep2fv0a(), collision.ep2btot(), collision.ep2bpos(), collision.ep2bneg()}; const float ep2 = epArr[mixing.cfgEP2EstimatorForMix]; fRegistry.fill(HIST("Event/before/hEP2_CentFT0C_forMix"), collision.centFT0C(), ep2); o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&fRegistry, collision, 1.f); - if (!fEMEventCut.IsSelected(collision)) + if (!fEMEventCut.IsSelected(collision)) { continue; + } o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&fRegistry, collision, 1.f); fRegistry.fill(HIST("Event/before/hCollisionCounter"), 12.0); fRegistry.fill(HIST("Event/after/hCollisionCounter"), 12.0); @@ -1690,65 +2905,115 @@ struct Photonhbt { auto keyBin = std::make_tuple(zbin, centbin, epbin, occbin); auto keyDFCollision = std::make_pair(ndf, collision.globalIndex()); auto photonsColl = photons.sliceBy(perCollision, collision.globalIndex()); - if (qaflags.doSinglePhotonQa) - for (const auto& g : photonsColl) - if (cut.template IsSelected(g)) + if (mQa.singlePhotonQa) { + for (const auto& g : photonsColl) { + if (cut.template IsSelected(g)) { fillSinglePhotonQAStep<0>(g); - std::unordered_set idsAfterDR, idsAfterRZ, idsAfterEllipse; + const auto mcg = buildPhotonMCInfo(g, mcParticles); + if (mcg.hasMC) { + const float rConvQA = std::hypot(g.vx(), g.vy()); + const auto originPos = static_cast(classifyLegOrigin(mcParticles, mcg.mcPosId)); + const auto originNeg = static_cast(classifyLegOrigin(mcParticles, mcg.mcNegId)); + if (mcg.sameMother && mcg.isTruePhoton) { + fRegistryPairMC.fill(HIST("SinglePhotonMC/truePhotons/hEtaPhi"), g.eta(), g.phi()); + fRegistryPairMC.fill(HIST("SinglePhotonMC/truePhotons/hRZ"), g.vz(), rConvQA); + fRegistryPairMC.fill(HIST("SinglePhotonMC/truePhotons/hEtaPt"), g.eta(), g.pt()); + fRegistryPairMC.fill(HIST("SinglePhotonMC/truePhotons/hLegOriginMatrix"), originPos, originNeg); + } else { + fRegistryPairMC.fill(HIST("SinglePhotonMC/fakePhotons/hEtaPhi"), g.eta(), g.phi()); + fRegistryPairMC.fill(HIST("SinglePhotonMC/fakePhotons/hRZ"), g.vz(), rConvQA); + fRegistryPairMC.fill(HIST("SinglePhotonMC/fakePhotons/hEtaPt"), g.eta(), g.pt()); + fRegistryPairMC.fill(HIST("SinglePhotonMC/fakePhotons/hLegOriginMatrix"), originPos, originNeg); + } + } + } + } + } + std::unordered_set idsAfterPairCuts; for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsColl, photonsColl))) { - if (!cut.template IsSelected(g1) || !cut.template IsSelected(g2)) + if (!cut.template IsSelected(g1) || !cut.template IsSelected(g2)) { continue; + } const auto pos1 = g1.template posTrack_as(), ele1 = g1.template negTrack_as(); const auto pos2 = g2.template posTrack_as(), ele2 = g2.template negTrack_as(); if (pos1.trackId() == pos2.trackId() || pos1.trackId() == ele2.trackId() || - ele1.trackId() == pos2.trackId() || ele1.trackId() == ele2.trackId()) + ele1.trackId() == pos2.trackId() || ele1.trackId() == ele2.trackId()) { continue; + } const auto mc1 = buildPhotonMCInfo(g1, mcParticles); const auto mc2 = buildPhotonMCInfo(g2, mcParticles); auto truthType = classifyPairTruth(mc1, mc2); - if (truthType == PairTruthType::TrueTrueDistinct && isPi0DaughterPair(mc1, mc2, mcParticles)) + if (truthType == PairTruthType::TrueTrueDistinct && isPi0DaughterPair(mc1, mc2, mcParticles)) { truthType = PairTruthType::Pi0Daughters; - if (!passAsymmetryCut(g1.pt(), g2.pt())) + } + if (!passAsymmetryCut(g1.pt(), g2.pt())) { continue; + } auto obs = buildPairQAObservables(g1, g2); - if (!obs.valid) + if (!obs.valid) { continue; + } const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); - if (doQA) + const auto legObs = buildLegPairObservables(g1, g2, pos1, ele1, pos2, ele2); + auto pwl1 = makePhotonWithLegs(g1, pos1, ele1, collision.posZ()); + auto pwl2 = makePhotonWithLegs(g2, pos2, ele2, collision.posZ()); + pwl1.fIsTruePhoton = (mc1.sameMother && mc1.isTruePhoton) ? 1 : 0; + pwl2.fIsTruePhoton = (mc2.sameMother && mc2.isTruePhoton) ? 1 : 0; + const auto sep = computePairSep(pwl1, pwl2); + + // ──before pair cuts ───────────────────────────────────── + fillPairSep<0>(sep, obs); + if (doQA) { fillPairQAStep<0, 0>(obs, centForQA, occupancy); - if (doFR) + fillLegPairQAStep<0>(legObs, obs.kt); + } + if (doFR) { fillFullRangeDeltaRCosOA<0>(obs.qinv, obs.drOverCosOA); - fRegistry.fill(HIST("Pair/same/hDeltaRCosOA"), obs.drOverCosOA); - if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) + } + + fillLegSimilarity(pos1, pos2, obs.qinv); + fillLegSimilarity(ele1, ele2, obs.qinv); + + // ─── Pair cuts ──────────────────────────────────────────────────── + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { continue; - idsAfterDR.insert(g1.globalIndex()); - idsAfterDR.insert(g2.globalIndex()); - if (doQA) - fillPairQAStep<0, 1>(obs, centForQA, occupancy); - if (!passRZCut(obs.deltaR, obs.deltaZ)) + } + + if (!passPairMergeCut(sep)) { continue; - idsAfterRZ.insert(g1.globalIndex()); - idsAfterRZ.insert(g2.globalIndex()); - if (doQA) - fillPairQAStep<0, 2>(obs, centForQA, occupancy); - if (isInsideEllipse(obs.deta, obs.dphi)) + } + + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + continue; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { continue; - idsAfterEllipse.insert(g1.globalIndex()); - idsAfterEllipse.insert(g2.globalIndex()); - if (doQA) - fillPairQAStep<0, 3>(obs, centForQA, occupancy); - if (doFR) + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + const auto cross = computeCrossObs(pwl1, pwl2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair<0>(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + + // ─── after pair cuts ────────────────────────────────────── + fillPairSep<0, true>(sep, obs); + idsAfterPairCuts.insert(g1.globalIndex()); + idsAfterPairCuts.insert(g2.globalIndex()); + if (doQA) { + fillPairQAStep<0, 1>(obs, centForQA, occupancy); + fillLegPairQAStep<1>(legObs, obs.kt); + } + if (doFR) { fillFullRangeQA<0>(obs, centForQA, occupancy); + } fillPairHistogram<0>(collision, obs.v1, obs.v2, 1.f); - fRegistry.fill(HIST("Pair/same/hPhi_lowerPtV0"), - (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); - - fillEtaTopologyHisto( - classifyEtaTopology(g1.eta(), g2.eta(), - pos1.eta(), ele1.eta(), - pos2.eta(), ele2.eta()), - obs.deta, obs.dphi, obs.kt); + fRegistryCF.fill(HIST("Pair/same/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); ndiphoton++; + + // ─── MC truth classification ────────────────────────────────────── if (!mc1.hasMC || !mc2.hasMC) { fillPairHistogramNoLabel(collision, obs.v1, obs.v2); fRegistryPairMC.fill(HIST("Pair/same/MC/NoLabel/hDEtaDPhi"), obs.deta, obs.dphi); @@ -1757,8 +3022,12 @@ struct Photonhbt { } else { const bool doMCQA = passQinvMCQAGate(obs.qinv); fillMCPairQA(truthType, obs, doQA, doMCQA); - if (doFR) + if (mQa.legPairQa) { + fillLegPairMC(truthType, legObs, obs.kt); + } + if (doFR) { fillMCPairQAFullRange(truthType, obs); + } const bool isTruePair = (truthType == PairTruthType::TrueTrueDistinct || truthType == PairTruthType::TrueTrueSamePhoton || truthType == PairTruthType::Pi0Daughters); @@ -1776,6 +3045,18 @@ struct Photonhbt { } } + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g1.psipair()), g1.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc1.sameMother && mc1.isTruePhoton) ? 1.f : 0.f); + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_PsiPair_PhiV_Qinv_Type"), std::fabs(g2.psipair()), g2.phiv(), obs.qinv, static_cast(static_cast(truthType)), (mc2.sameMother && mc2.isTruePhoton) ? 1.f : 0.f); + if (truthType == PairTruthType::FakeFake || truthType == PairTruthType::TrueFake) { + fRegistryPairMC.fill(HIST("Pair/same/MC/hFakeSubtypeVsQinv"), obs.qinv, + static_cast(classifyFakeSubtype(mc1, mc2))); + } + + const auto crossMC = computeCrossObs(pwl1, pwl2, obs.qinv); + if (crossMC.meeOverQ < 900.f) { // o2-linter: disable=magic-number (qinv was zero, skip) + fRegistryPairMC.fill(HIST("Pair/same/MC/hSparse_MeeRatio_Qinv_Type"), crossMC.meeOverQ, obs.qinv, static_cast(static_cast(truthType))); + } + switch (truthType) { case PairTruthType::TrueTrueDistinct: fillPairHistogramMC<0, PairTruthType::TrueTrueDistinct>(collision, obs.v1, obs.v2); @@ -1800,78 +3081,113 @@ struct Photonhbt { } } - auto addToPool = [&](auto const& g) { + auto addToPool = [&](auto const& g, PhotonWithLegs const& p) { if (usedPhotonIdsPerCol.insert(g.globalIndex()).second) { - EMPair gtmp(g.pt(), g.eta(), g.phi(), 0.f); - gtmp.setConversionPointXYZ(g.vx(), g.vy(), g.vz()); - emh1->AddTrackToEventPool(keyDFCollision, gtmp); + emh1->AddTrackToEventPool(keyDFCollision, p); } }; - addToPool(g1); - addToPool(g2); + addToPool(g1, pwl1); + addToPool(g2, pwl2); } - if (qaflags.doSinglePhotonQa) { + if (mQa.singlePhotonQa) { for (const auto& g : photonsColl) { if (cut.template IsSelected(g)) { - const int gid = g.globalIndex(); - if (idsAfterDR.count(gid)) { + if (idsAfterPairCuts.contains(g.globalIndex())) { fillSinglePhotonQAStep<1>(g); } - if (idsAfterRZ.count(gid)) { - fillSinglePhotonQAStep<2>(g); - } - if (idsAfterEllipse.count(gid)) { - fillSinglePhotonQAStep<3>(g); - } } } } usedPhotonIdsPerCol.clear(); - if (!mixing.cfgDoMix || ndiphoton == 0) + if (!mixing.cfgDoMix || ndiphoton == 0) { continue; - auto selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); - auto poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); + } + const auto& selectedPhotons = emh1->GetTracksPerCollision(keyDFCollision); + const auto& poolIDs = emh1->GetCollisionIdsFromEventPool(keyBin); for (const auto& mixID : poolIDs) { - if (mixID.second == collision.globalIndex() && mixID.first == ndf) + if (mixID.second == collision.globalIndex() && mixID.first == ndf) { continue; + } const uint64_t bcMix = mapMixedEventIdToGlobalBC[mixID]; const uint64_t diffBC = std::max(collision.globalBC(), bcMix) - std::min(collision.globalBC(), bcMix); - fRegistry.fill(HIST("Pair/mix/hDiffBC"), diffBC); - if (diffBC < mixing.ndiffBCMix) + fRegistryCF.fill(HIST("Pair/mix/hDiffBC"), diffBC); + if (diffBC < mixing.ndiffBCMix) { continue; - auto poolPhotons = emh1->GetTracksPerCollision(mixID); - for (const auto& g1 : selectedPhotons) + } + const auto& poolPhotons = emh1->GetTracksPerCollision(mixID); + for (const auto& g1 : selectedPhotons) { for (const auto& g2 : poolPhotons) { - if (!passAsymmetryCut(g1.pt(), g2.pt())) + if (!passAsymmetryCut(g1.pt(), g2.pt())) { continue; + } + if (!passFastQinvGate(g1, g2)) { + continue; + } auto obs = buildPairQAObservables(g1, g2); - - if (!obs.valid) + if (!obs.valid) { continue; + } const bool doQA = passQinvQAGate(obs.qinv), doFR = passQinvFullRangeGate(obs.qinv); - if (doQA) + const auto sep = computePairSep(g1, g2); + + // ───before pair cuts ───────────────────────────────── + fillPairSep<1>(sep, obs); + if (doQA) { fillPairQAStep<1, 0>(obs, centForQA, occupancy); - if (doFR) + } + if (doFR) { fillFullRangeDeltaRCosOA<1>(obs.qinv, obs.drOverCosOA); - fRegistry.fill(HIST("Pair/mix/hDeltaRCosOA"), obs.drOverCosOA); - if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) + } + + fillLegSimilarityMix(g1, g2, obs.qinv); + + // ─── Pair cuts ──────────────────────────────────────────────── + if (obs.drOverCosOA < ggpaircuts.cfgMinDRCosOA) { continue; - if (doQA) - fillPairQAStep<1, 1>(obs, centForQA, occupancy); - if (!passRZCut(obs.deltaR, obs.deltaZ)) + } + if (!passPairMergeCut(sep)) { continue; - if (doQA) - fillPairQAStep<1, 2>(obs, centForQA, occupancy); - if (isInsideEllipse(obs.deta, obs.dphi)) + } + if (!passRZCut(obs.deltaR, obs.deltaZ)) { + continue; + } + if (isInsideEllipse(obs.deta, obs.dphi)) { continue; - if (doQA) - fillPairQAStep<1, 3>(obs, centForQA, occupancy); - if (doFR) + } + if (crosspair.cfgDoCrossPairQA.value || crosspair.cfgDoCrossPairCut.value) { + const auto cross = computeCrossObs(g1, g2, obs.qinv); + const bool vetoed = !passCrossPairVeto(cross); + fillCrossPair<1>(cross, obs.qinv, obs.deltaR, vetoed); + if (vetoed) { + continue; + } + } + + // ─after pair cuts ────────────────────────────────── + fillPairSep<1, true>(sep, obs); + if (doQA) { + fillPairQAStep<1, 1>(obs, centForQA, occupancy); + } + if (doFR) { fillFullRangeQA<1>(obs, centForQA, occupancy); + } fillPairHistogram<1>(collision, obs.v1, obs.v2, 1.f); - fRegistry.fill(HIST("Pair/mix/hPhi_lowerPtV0"), - (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); + if (g1.fIsTruePhoton >= 0 && g2.fIsTruePhoton >= 0) { + const int nTrue = g1.fIsTruePhoton + g2.fIsTruePhoton; + if (nTrue == 2) { // o2-linter: disable=magic-number (both pairs are true) + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_TrueTrue"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::TrueTrueDistinct>(collision, obs.v1, obs.v2); + } else if (nTrue == 1) { + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_TrueFake"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::TrueFake>(collision, obs.v1, obs.v2); + } else { + fRegistryPairMC.fill(HIST("Pair/mix/MC/hQinv_FakeFake"), obs.qinv); + fillPairHistogramMC<1, PairTruthType::FakeFake>(collision, obs.v1, obs.v2); + } + } + fRegistryCF.fill(HIST("Pair/mix/hPhi_lowerPtV0"), (g1.pt() < g2.pt()) ? g1.phi() : g2.phi()); } + } } if (ndiphoton > 0) { emh1->AddCollisionIdAtLast(keyBin, keyDFCollision); @@ -1900,19 +3216,24 @@ struct Photonhbt { std::unordered_set mcIdsWithAnyV0Leg; mcIdsWithAnyV0Leg.reserve(v0legs.size() * 2); for (const auto& leg : v0legs) { - if (leg.has_emmcparticle()) + if (leg.has_emmcparticle()) { mcIdsWithAnyV0Leg.insert(leg.emmcparticleId()); + } } for (const auto& collision : collisions) { - if (!fEMEventCut.IsSelected(collision)) + initCCDB(collision); + if (!fEMEventCut.IsSelected(collision)) { continue; - const float cent[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + } + const std::array cent = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (cent[mixing.cfgCentEstimator] < centralitySelection.cfgCentMin || - centralitySelection.cfgCentMax < cent[mixing.cfgCentEstimator]) + centralitySelection.cfgCentMax < cent[mixing.cfgCentEstimator]) { continue; - if (!collision.has_emmcevent()) + } + if (!collision.has_emmcevent()) { continue; + } const int64_t thisCollisionId = collision.globalIndex(); const int mcEventId = collision.template emmcevent_as().globalIndex(); @@ -1923,14 +3244,18 @@ struct Photonhbt { std::unordered_set legIdsThisCollision; legIdsThisCollision.reserve(legsColl.size()); - for (const auto& leg : legsColl) - if (leg.has_emmcparticle()) + for (const auto& leg : legsColl) { + if (leg.has_emmcparticle()) { legIdsThisCollision.insert(leg.emmcparticleId()); + } + } std::unordered_map> crossBuildMap; struct PhotonRecoInfo { bool hasV0 = false, passesCut = false; + bool hasCounts = false; + pairutil::V0PhotonLegCounts legCounts{}; }; std::unordered_map gammaRecoMap; gammaRecoMap.reserve(recoPhotonsColl.size()); @@ -1938,14 +3263,17 @@ struct Photonhbt { for (const auto& g : recoPhotonsColl) { const auto pos = g.template posTrack_as(); const auto neg = g.template negTrack_as(); - if (pos.collisionId() != thisCollisionId || neg.collisionId() != thisCollisionId) + if (pos.collisionId() != thisCollisionId || neg.collisionId() != thisCollisionId) { continue; - if (!pos.has_emmcparticle() || !neg.has_emmcparticle()) + } + if (!pos.has_emmcparticle() || !neg.has_emmcparticle()) { continue; + } const auto mcPos = pos.template emmcparticle_as(); const auto mcNeg = neg.template emmcparticle_as(); - if (!mcPos.has_mothers() || !mcNeg.has_mothers()) + if (!mcPos.has_mothers() || !mcNeg.has_mothers()) { continue; + } const int posMotherId = mcPos.mothersIds()[0], negMotherId = mcNeg.mothersIds()[0]; if (posMotherId != negMotherId) { const auto posMother = emmcParticles.iteratorAt(posMotherId); @@ -1957,12 +3285,17 @@ struct Photonhbt { continue; } const int gammaId = posMotherId; - if (emmcParticles.iteratorAt(gammaId).pdgCode() != kGamma) + if (emmcParticles.iteratorAt(gammaId).pdgCode() != kGamma) { continue; + } const bool passes = cut.template IsSelected, TLegs>(g); auto& info = gammaRecoMap[gammaId]; info.hasV0 = true; info.passesCut = info.passesCut || passes; + if (passes && !info.hasCounts) { + info.legCounts = pairutil::getV0PhotonLegCounts(pos, neg); + info.hasCounts = true; + } } // ─── Build true gamma list ──────────────────────────────────────────────── @@ -1970,19 +3303,24 @@ struct Photonhbt { trueGammas.reserve(32); for (const auto& g : emmcPartsColl) { - if (g.pdgCode() != kGamma) + if (g.pdgCode() != kGamma) { continue; - if (!g.isPhysicalPrimary() && !g.producedByGenerator()) + } + if (!g.isPhysicalPrimary() && !g.producedByGenerator()) { continue; - if (std::fabs(g.eta()) > pcmcuts.cfgMaxEtaV0.value) + } + if (std::fabs(g.eta()) > pcmcuts.cfgMaxEtaV0.value) { continue; + } const float mcV0PtMin = (mctruth.cfgMCMinV0Pt.value > 0.f) ? mctruth.cfgMCMinV0Pt.value : pcmcuts.cfgMinPtV0.value; - if (g.pt() < mcV0PtMin) + if (g.pt() < mcV0PtMin) { continue; - if (!g.has_daughters()) + } + if (!g.has_daughters()) { continue; + } int posId = -1, negId = -1; float rTrue = -1.f; @@ -1991,10 +3329,10 @@ struct Photonhbt { continue; } const auto d = emmcParticles.iteratorAt(dId); - if (d.pdgCode() == kElectron) { + if (d.pdgCode() == kPositron) { posId = dId; rTrue = std::sqrt(d.vx() * d.vx() + d.vy() * d.vy()); - } else if (d.pdgCode() == kPositron) { + } else if (d.pdgCode() == kElectron) { negId = dId; } } @@ -2007,16 +3345,17 @@ struct Photonhbt { if (mctruth.cfgMCMinLegPt.value > 0.f && (static_cast(mcPosE.pt()) < mctruth.cfgMCMinLegPt.value || - static_cast(mcNegE.pt()) < mctruth.cfgMCMinLegPt.value)) + static_cast(mcNegE.pt()) < mctruth.cfgMCMinLegPt.value)) { continue; + } - const float deTrE = static_cast(mcPosE.eta() - mcNegE.eta()); - const float dpTrE = wrapPhi(static_cast(mcPosE.phi() - mcNegE.phi())); - const float legDRt = std::sqrt(deTrE * deTrE + dpTrE * dpTrE); + const auto deTrE = static_cast(mcPosE.eta() - mcNegE.eta()); + const auto dpTrE = wrapPhi(static_cast(mcPosE.phi() - mcNegE.phi())); + const auto legDRt = std::sqrt(deTrE * deTrE + dpTrE * dpTrE); - const float pxG = static_cast(g.px()), pyG = static_cast(g.py()), - pzG = static_cast(g.pz()); - const float magG = std::sqrt(pxG * pxG + pyG * pyG + pzG * pzG); + const auto pxG = static_cast(g.px()), pyG = static_cast(g.py()), + pzG = static_cast(g.pz()); + const auto magG = std::sqrt(pxG * pxG + pyG * pyG + pzG * pzG); float alphaTrue = 0.f; if (magG > kMinSigma) { const float ux = pxG / magG, uy = pyG / magG, uz = pzG / magG; @@ -2027,16 +3366,27 @@ struct Photonhbt { static_cast(mcNegE.py()) * uy + static_cast(mcNegE.pz()) * uz; const float sumPL = pLpos + pLneg; - if (std::fabs(sumPL) > kMinSigma) + if (std::fabs(sumPL) > kMinSigma) { alphaTrue = (pLpos - pLneg) / sumPL; + } } - trueGammas.push_back({static_cast(g.globalIndex()), posId, negId, - static_cast(g.eta()), static_cast(g.phi()), - static_cast(g.pt()), rTrue, legDRt, - deTrE, - dpTrE, - alphaTrue}); + TruthGamma tg; + tg.id = static_cast(g.globalIndex()); + tg.posId = posId; + tg.negId = negId; + tg.eta = static_cast(g.eta()); + tg.phi = static_cast(g.phi()); + tg.pt = static_cast(g.pt()); + tg.rTrue = rTrue; + tg.legDRtrue = legDRt; + tg.legDEta = deTrE; + tg.legDPhi = dpTrE; + tg.alphaTrue = alphaTrue; + tg.legPtTrue = {static_cast(mcPosE.pt()), static_cast(mcNegE.pt())}; + tg.legEtaTrue = {static_cast(mcPosE.eta()), static_cast(mcNegE.eta())}; + tg.legPhiTrue = {static_cast(mcPosE.phi()), static_cast(mcNegE.phi())}; + trueGammas.push_back(tg); } { @@ -2044,39 +3394,42 @@ struct Photonhbt { for (const auto& tg : trueGammas) { const auto it = gammaRecoMap.find(tg.id); if (it != gammaRecoMap.end() && it->second.hasV0 && - (mcIdsWithAnyV0Leg.count(tg.posId) == 0 || mcIdsWithAnyV0Leg.count(tg.negId) == 0)) + (!mcIdsWithAnyV0Leg.contains(tg.posId) || !mcIdsWithAnyV0Leg.contains(tg.negId))) { ++nBad; + } } - fRegistryMC.fill(HIST("MC/TruthAO2D/hStageConsistency"), static_cast(nBad)); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hStageConsistency"), static_cast(nBad)); } for (const auto& tg : trueGammas) { - const bool posFound = legIdsThisCollision.count(tg.posId) > 0; - const bool negFound = legIdsThisCollision.count(tg.negId) > 0; + const bool posFound = legIdsThisCollision.contains(tg.posId) > 0; + const bool negFound = legIdsThisCollision.contains(tg.negId) > 0; const bool bothFound = posFound && negFound; for (const auto& [legId, legFound] : std::initializer_list>{{tg.posId, posFound}, {tg.negId, negFound}}) { - if (legId < 0) + if (legId < 0) { continue; + } + if (legFound) { - fRegistryMC.fill(HIST("MC/LegDiag/hLegDRtrue_vs_pt_legFound"), tg.pt, tg.legDRtrue); - fRegistryMC.fill(HIST("MC/LegDiag/hLegDEta_legFound_vs_pt"), tg.pt, std::fabs(tg.legDEta)); - fRegistryMC.fill(HIST("MC/LegDiag/hLegDPhi_legFound_vs_pt"), tg.pt, std::fabs(tg.legDPhi)); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hLegDRtrue_vs_pt_legFound"), tg.pt, tg.legDRtrue); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hLegDEta_legFound_vs_pt"), tg.pt, std::fabs(tg.legDEta)); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hLegDPhi_legFound_vs_pt"), tg.pt, std::fabs(tg.legDPhi)); } else { - fRegistryMC.fill(HIST("MC/LegDiag/hLegDRtrue_vs_pt_legMissing"), tg.pt, tg.legDRtrue); - fRegistryMC.fill(HIST("MC/LegDiag/hLegDEta_legMissing_vs_pt"), tg.pt, std::fabs(tg.legDEta)); - fRegistryMC.fill(HIST("MC/LegDiag/hLegDPhi_legMissing_vs_pt"), tg.pt, std::fabs(tg.legDPhi)); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hLegDRtrue_vs_pt_legMissing"), tg.pt, tg.legDRtrue); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hLegDEta_legMissing_vs_pt"), tg.pt, std::fabs(tg.legDEta)); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hLegDPhi_legMissing_vs_pt"), tg.pt, std::fabs(tg.legDPhi)); } } // ─── Armenteros-α diagnostics per photon ───────────────────────────── if (bothFound) { - fRegistryMC.fill(HIST("MC/LegDiag/hAlphaTrue_legFound_vs_pt"), tg.pt, tg.alphaTrue); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hAlphaTrue_legFound_vs_pt"), tg.pt, tg.alphaTrue); } else { // At least one leg missing - fRegistryMC.fill(HIST("MC/LegDiag/hAlphaTrue_legMissing_vs_pt"), tg.pt, tg.alphaTrue); - fRegistryMC.fill(HIST("MC/LegDiag/hAlpha_vs_legDR_legMissing"), tg.legDRtrue, tg.alphaTrue); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hAlphaTrue_legMissing_vs_pt"), tg.pt, tg.alphaTrue); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hAlpha_vs_legDR_legMissing"), tg.legDRtrue, tg.alphaTrue); } } @@ -2091,99 +3444,109 @@ struct Photonhbt { const float px2 = g2.pt * std::cos(g2.phi), py2 = g2.pt * std::sin(g2.phi); const float kt = 0.5f * std::sqrt((px1 + px2) * (px1 + px2) + (py1 + py2) * (py1 + py2)); - if (mctruth.cfgMCMinKt > 0.f && kt < mctruth.cfgMCMinKt) + if (mctruth.cfgMCMinKt > 0.f && kt < mctruth.cfgMCMinKt) { continue; - if (mctruth.cfgMCMaxKt > 0.f && kt > mctruth.cfgMCMaxKt) + } + if (mctruth.cfgMCMaxKt > 0.f && kt > mctruth.cfgMCMaxKt) { continue; + } const float e1 = g1.pt * std::cosh(g1.eta), e2 = g2.pt * std::cosh(g2.eta); - const float dot = e1 * e2 - (px1 * px2 + py1 * py2 + - g1.pt * std::sinh(g1.eta) * g2.pt * std::sinh(g2.eta)); + const float dot = e1 * e2 - (px1 * px2 + py1 * py2 + g1.pt * std::sinh(g1.eta) * g2.pt * std::sinh(g2.eta)); const float qinv_true = std::sqrt(std::max(0.f, 2.f * dot)); - if (mctruth.cfgMCMaxQinv > 0.f && qinv_true > mctruth.cfgMCMaxQinv) + if (mctruth.cfgMCMaxQinv > 0.f && qinv_true > mctruth.cfgMCMaxQinv) { continue; + } auto it1 = gammaRecoMap.find(g1.id), it2 = gammaRecoMap.find(g2.id); const bool g1Built = (it1 != gammaRecoMap.end()) && it1->second.hasV0; const bool g2Built = (it2 != gammaRecoMap.end()) && it2->second.hasV0; const bool g1Sel = (it1 != gammaRecoMap.end()) && it1->second.passesCut; const bool g2Sel = (it2 != gammaRecoMap.end()) && it2->second.passesCut; + const bool pairClassSel = !cfgDoPhotonClassPairCut.value || (g1Sel && g2Sel && pairutil::isPairPhotonClassSelected(it1->second.legCounts, it2->second.legCounts, mPhotonClassSelA, mPhotonClassSelB)); const bool pairAll4LegsThisColl = - legIdsThisCollision.count(g1.posId) > 0 && legIdsThisCollision.count(g1.negId) > 0 && - legIdsThisCollision.count(g2.posId) > 0 && legIdsThisCollision.count(g2.negId) > 0; + legIdsThisCollision.contains(g1.posId) > 0 && legIdsThisCollision.contains(g1.negId) > 0 && + legIdsThisCollision.contains(g2.posId) > 0 && legIdsThisCollision.contains(g2.negId) > 0; - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_truthConverted"), deta, dphi, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_truthConverted"), deta, dphi, kt); - fRegistryMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_truthConverted"), kt, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_truthConverted"), deta, dphi); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_truthConverted"), deta, dphi, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_truthConverted"), deta, dphi, kt); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_truthConverted"), kt, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_truthConverted"), deta, dphi); if (pairAll4LegsThisColl) { - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_all4LegsThisColl"), deta, dphi, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_all4LegsThisColl"), deta, dphi, kt); - fRegistryMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_all4LegsThisColl"), kt, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_all4LegsThisColl"), deta, dphi); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_all4LegsThisColl"), deta, dphi, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_all4LegsThisColl"), deta, dphi, kt); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_all4LegsThisColl"), kt, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_all4LegsThisColl"), deta, dphi); } if (g1Built && g2Built) { - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_bothPhotonsBuilt"), deta, dphi, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_bothPhotonsBuilt"), deta, dphi, kt); - fRegistryMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_bothPhotonsBuilt"), kt, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_bothPhotonsBuilt"), deta, dphi); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_bothPhotonsBuilt"), deta, dphi, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_bothPhotonsBuilt"), deta, dphi, kt); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_bothPhotonsBuilt"), kt, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_bothPhotonsBuilt"), deta, dphi); } - if (g1Sel && g2Sel) { - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_bothPhotonsSelected"), deta, dphi, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_bothPhotonsSelected"), deta, dphi, kt); - fRegistryMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_bothPhotonsSelected"), kt, qinv_true); - fRegistryMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_bothPhotonsSelected"), deta, dphi); + if (g1Sel && g2Sel && pairClassSel) { + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_qinv_bothPhotonsSelected"), deta, dphi, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hSparse_DEtaDPhi_kT_bothPhotonsSelected"), deta, dphi, kt); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hQinvVsKt_bothPhotonsSelected"), kt, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hDEtaDPhi_bothPhotonsSelected"), deta, dphi); } if (g1.rTrue >= 0.f && g2.rTrue >= 0.f) { - fRegistryMC.fill(HIST("MC/TruthAO2D/hRconv1_vs_Rconv2_truthConverted"), g1.rTrue, g2.rTrue); - if (g1Sel && g2Sel) - fRegistryMC.fill(HIST("MC/TruthAO2D/hRconv1_vs_Rconv2_bothPhotonsSelected"), g1.rTrue, g2.rTrue); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hRconv1_vs_Rconv2_truthConverted"), g1.rTrue, g2.rTrue); + if (g1Sel && g2Sel && pairClassSel) { + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hRconv1_vs_Rconv2_bothPhotonsSelected"), g1.rTrue, g2.rTrue); + } } const float minRconv = (g1.rTrue >= 0.f && g2.rTrue >= 0.f) ? std::min(g1.rTrue, g2.rTrue) : (g1.rTrue >= 0.f ? g1.rTrue : g2.rTrue); if (minRconv >= 0.f) { - fRegistryMC.fill(HIST("MC/TruthAO2D/hMinRconv_vs_kT_truthConverted"), kt, minRconv); - if (g1Sel && g2Sel) - fRegistryMC.fill(HIST("MC/TruthAO2D/hMinRconv_vs_kT_bothPhotonsSelected"), kt, minRconv); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hMinRconv_vs_kT_truthConverted"), kt, minRconv); + if (g1Sel && g2Sel && pairClassSel) { + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hMinRconv_vs_kT_bothPhotonsSelected"), kt, minRconv); + } } - fRegistryMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 0.f); - if (pairAll4LegsThisColl) - fRegistryMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 1.f); - if (g1Built && g2Built) - fRegistryMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 2.f); - if (g1Sel && g2Sel) - fRegistryMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 3.f); + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 0.f); + if (pairAll4LegsThisColl) { + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 1.f); + } + if (g1Built && g2Built) { + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 2.f); + } + if (g1Sel && g2Sel && pairClassSel) { + fRegistryTruthMC.fill(HIST("MC/TruthAO2D/hStage_vs_kT"), kt, 3.f); + } const auto itCB = crossBuildMap.find(g1.id); - if (itCB != crossBuildMap.end() && itCB->second.count(g2.id) > 0) { - fRegistryMC.fill(HIST("MC/PairCrossBuild/hSparse_DEtaDPhi_kT"), deta, dphi, kt); - fRegistryMC.fill(HIST("MC/PairCrossBuild/hStageOut_vs_kT"), - kt, static_cast((g1Built ? 1 : 0) + (g2Built ? 1 : 0))); + if (itCB != crossBuildMap.end() && itCB->second.contains(g2.id)) { + fRegistryTruthMC.fill(HIST("MC/PairCrossBuild/hSparse_DEtaDPhi_kT"), deta, dphi, kt); + fRegistryTruthMC.fill(HIST("MC/PairCrossBuild/hStageOut_vs_kT"), + kt, static_cast((g1Built ? 1 : 0) + (g2Built ? 1 : 0))); } const int nLegsThisColl = - (legIdsThisCollision.count(g1.posId) > 0 ? 1 : 0) + - (legIdsThisCollision.count(g1.negId) > 0 ? 1 : 0) + - (legIdsThisCollision.count(g2.posId) > 0 ? 1 : 0) + - (legIdsThisCollision.count(g2.negId) > 0 ? 1 : 0); - fRegistryMC.fill(HIST("MC/LegDiag/hNLegsPair_vs_kT"), kt, static_cast(nLegsThisColl)); + (legIdsThisCollision.contains(g1.posId) > 0 ? 1 : 0) + + (legIdsThisCollision.contains(g1.negId) > 0 ? 1 : 0) + + (legIdsThisCollision.contains(g2.posId) > 0 ? 1 : 0) + + (legIdsThisCollision.contains(g2.negId) > 0 ? 1 : 0); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hNLegsPair_vs_kT"), kt, static_cast(nLegsThisColl)); for (const auto& [tgRef, legId] : std::initializer_list, int>>{ {std::cref(g1), g1.posId}, {std::cref(g1), g1.negId}, {std::cref(g2), g2.posId}, {std::cref(g2), g2.negId}}) { - if (legId < 0 || legIdsThisCollision.count(legId) > 0) + if (legId < 0 || legIdsThisCollision.contains(legId) > 0) { continue; + } const auto& tg_parent = tgRef.get(); - const float legPtTrue = static_cast(emmcParticles.iteratorAt(legId).pt()); - fRegistryMC.fill(HIST("MC/LegDiag/hMissingLegPt_vs_kT"), kt, legPtTrue); - if (tg_parent.rTrue >= 0.f) - fRegistryMC.fill(HIST("MC/LegDiag/hMissingLegRconv_vs_kT"), kt, tg_parent.rTrue); + const auto legPtTrue = static_cast(emmcParticles.iteratorAt(legId).pt()); + fRegistryTruthMC.fill(HIST("MC/LegDiag/hMissingLegPt_vs_kT"), kt, legPtTrue); + if (tg_parent.rTrue >= 0.f) { + fRegistryTruthMC.fill(HIST("MC/LegDiag/hMissingLegRconv_vs_kT"), kt, tg_parent.rTrue); + } } } } @@ -2194,8 +3557,9 @@ struct Photonhbt { for (size_t j = i + 1; j < trueGammas.size(); ++j) { const auto& g1 = trueGammas[i]; const auto& g2 = trueGammas[j]; - if (!passAsymmetryCut(g1.pt, g2.pt)) + if (!passAsymmetryCut(g1.pt, g2.pt)) { continue; + } const float deta = g1.eta - g2.eta; const float dphi = wrapPhi(g1.phi - g2.phi); const float px1 = g1.pt * std::cos(g1.phi), py1 = g1.pt * std::sin(g1.phi); @@ -2205,8 +3569,8 @@ struct Photonhbt { const float dot = e1 * e2 - (px1 * px2 + py1 * py2 + g1.pt * std::sinh(g1.eta) * g2.pt * std::sinh(g2.eta)); const float qinv_true = std::sqrt(std::max(0.f, 2.f * dot)); - fRegistryMC.fill(HIST("MC/TruthCF/hQinvVsKt_same"), kt, qinv_true); - fRegistryMC.fill(HIST("MC/TruthCF/hDEtaDPhi_same"), deta, dphi); + fRegistryTruthMC.fill(HIST("MC/TruthCF/hQinvVsKt_same"), kt, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthCF/hDEtaDPhi_same"), deta, dphi); } } @@ -2223,12 +3587,13 @@ struct Photonhbt { binOf(epBinEgdes, ep2), binOf(occBinEdges, occupancy)); - if (truthGammaPool.count(keyBin)) { + if (truthGammaPool.contains(keyBin)) { for (const auto& poolEvent : truthGammaPool[keyBin]) { for (const auto& g1 : trueGammas) { for (const auto& g2 : poolEvent) { - if (!passAsymmetryCut(g1.pt, g2.pt)) + if (!passAsymmetryCut(g1.pt, g2.pt)) { continue; + } const float deta = g1.eta - g2.eta; const float dphi = wrapPhi(g1.phi - g2.phi); const float px1 = g1.pt * std::cos(g1.phi), py1 = g1.pt * std::sin(g1.phi); @@ -2238,8 +3603,8 @@ struct Photonhbt { const float dot = e1 * e2 - (px1 * px2 + py1 * py2 + g1.pt * std::sinh(g1.eta) * g2.pt * std::sinh(g2.eta)); const float qinv_true = std::sqrt(std::max(0.f, 2.f * dot)); - fRegistryMC.fill(HIST("MC/TruthCF/hQinvVsKt_mix"), kt, qinv_true); - fRegistryMC.fill(HIST("MC/TruthCF/hDEtaDPhi_mix"), deta, dphi); + fRegistryTruthMC.fill(HIST("MC/TruthCF/hQinvVsKt_mix"), kt, qinv_true); + fRegistryTruthMC.fill(HIST("MC/TruthCF/hDEtaDPhi_mix"), deta, dphi); } } } @@ -2248,41 +3613,17 @@ struct Photonhbt { if (!trueGammas.empty()) { auto& poolBin = truthGammaPool[keyBin]; poolBin.push_back(trueGammas); - if (static_cast(poolBin.size()) > mctruth.cfgTruthMixDepth.value) + if (static_cast(poolBin.size()) > mctruth.cfgTruthMixDepth.value) { poolBin.pop_front(); + } } } // end cfgDoTruthMix } // end collision loop } // end runTruthEfficiency - using MyEMH = o2::aod::pwgem::dilepton::utils::EventMixingHandler< - std::tuple, std::pair, EMPair>; - - MyEMH* emh1 = nullptr; - MyEMH* emh2 = nullptr; - std::unordered_set usedPhotonIdsPerCol; - std::map, uint64_t> mapMixedEventIdToGlobalBC; - - SliceCache cache; - Preslice perCollisionPCM = aod::v0photonkf::pmeventId; - PresliceUnsorted perCollisionV0Legs = aod::v0leg::collisionId; - PresliceUnsorted perMCCollisionEMMCParts = aod::emmcparticle::emmceventId; - - Filter collisionFilterCentrality = - (centralitySelection.cfgCentMin < o2::aod::cent::centFT0M && o2::aod::cent::centFT0M < centralitySelection.cfgCentMax) || - (centralitySelection.cfgCentMin < o2::aod::cent::centFT0A && o2::aod::cent::centFT0A < centralitySelection.cfgCentMax) || - (centralitySelection.cfgCentMin < o2::aod::cent::centFT0C && o2::aod::cent::centFT0C < centralitySelection.cfgCentMax); - Filter collisionFilterOccupancyTrack = - eventcuts.cfgTrackOccupancyMin <= o2::aod::evsel::trackOccupancyInTimeRange && - o2::aod::evsel::trackOccupancyInTimeRange < eventcuts.cfgTrackOccupancyMax; - Filter collisionFilterOccupancyFT0c = - eventcuts.cfgFT0COccupancyMin <= o2::aod::evsel::ft0cOccupancyInTimeRange && - o2::aod::evsel::ft0cOccupancyInTimeRange < eventcuts.cfgFT0COccupancyMax; - - using FilteredMyCollisions = soa::Filtered; - using FilteredMyMCCollisions = soa::Filtered; - - int ndf = 0; + /*************************************************/ + // PROCESS FUNCTIONS + /*************************************************/ void processAnalysis(FilteredMyCollisions const& collisions, MyV0Photons const& v0photons, @@ -2309,9 +3650,564 @@ struct Photonhbt { ndf++; } PROCESS_SWITCH(Photonhbt, processMC, "MC CF + truth efficiency maps for CF correction", false); + + void processMCAOD(aod::Collisions const& collisions, + aod::V0s const& v0s, + soa::Join const& tracks, + aod::McParticles const& mcparticles) + { + + const float kRMinConv = pcmcuts.cfgMinV0Radius.value; + const float kRMaxConv = pcmcuts.cfgMaxV0Radius.value; + + std::unordered_map>> tracksOfMc; + for (const auto& t : tracks) { + if (!t.has_mcParticle()) { + continue; + } + tracksOfMc[t.mcParticleId()].emplace_back(t.globalIndex(), t.collisionId()); + } + + struct V0Lite { + int64_t gi = -1; + int collisionId = -1; + int posTrackId = -1; + int negTrackId = -1; + uint8_t typeRaw = 0; + float pca = 999.f; + float cospa = -1.f; + float score = 999.f; + float convR = -1.f; + }; + + const float bzkG = mctruth.cfgMCAODBzkG.value; + const float wScore = mctruth.cfgMCAODScoreWeight.value; + auto anaV0 = [&](int posId, int negId, int collisionId, float& pcaOut, float& cospaOut, float& convROut) -> float { + pcaOut = 999.f; + cospaOut = -1.f; + convROut = -1.f; + if (std::fabs(bzkG) < 0.1f || collisionId < 0) { // o2-linter: disable=magic-number (check if number is too small) + return 999.f; + } + const auto posTrk = tracks.rawIteratorAt(posId); + const auto negTrk = tracks.rawIteratorAt(negId); + auto pTrackC = getTrackParCov(posTrk); + auto nTrackC = getTrackParCov(negTrk); + const o2::track::TrackAuxPar h1(pTrackC, bzkG); + const o2::track::TrackAuxPar h2(nTrackC, bzkG); + const float dcx = h2.xC - h1.xC; + const float dcy = h2.yC - h1.yC; + const float d = std::hypot(dcx, dcy); + if (d < 1e-3f) { // o2-linter: disable=magic-number (check if number is too small) + return 999.f; + } + const float ux = dcx / d; + const float uy = dcy / d; + const float p1x = h1.xC + h1.rC * ux, p1y = h1.yC + h1.rC * uy; + const float p2x = h2.xC - h2.rC * ux, p2y = h2.yC - h2.rC * uy; + const float pcaXY = std::fabs(d - h1.rC - h2.rC); + const float convX = 0.5f * (p1x + p2x); + const float convY = 0.5f * (p1y + p2y); + const o2::math_utils::Point3D convXY{convX, convY, 0.f}; + const auto dcaP = CalculateDCAFast(pTrackC, convXY, bzkG); + const auto dcaN = CalculateDCAFast(nTrackC, convXY, bzkG); + const float z1 = dcaP[1]; + const float z2 = dcaN[1]; + const float pca3D = std::hypot(pcaXY, z1 - z2); + const float convZ = 0.5f * (z1 + z2); + auto arcTo = [](o2::track::TrackAuxPar const& h, float xTrk, float yTrk, float px_, float py_) { + const float th0 = std::atan2(yTrk - h.yC, xTrk - h.xC); + const float thv = std::atan2(py_ - h.yC, px_ - h.xC); + const auto dth = RecoDecay::constrainAngle(thv - th0, -o2::constants::math::PI); + return std::fabs(h.rC * dth); + }; + const auto posGlo = pTrackC.getXYZGlo(); + const auto negGlo = nTrackC.getXYZGlo(); + const auto pP = getPropMomentumFromTrackHelix(arcTo(h1, posGlo.X(), posGlo.Y(), p1x, p1y), posTrk, h1, bzkG / 10.f); + const auto pN = getPropMomentumFromTrackHelix(arcTo(h2, negGlo.X(), negGlo.Y(), p2x, p2y), negTrk, h2, bzkG / 10.f); + // pointing angle w.r.t. the V0's collision vertex + const auto col = collisions.rawIteratorAt(collisionId); + const float fx = convX - col.posX(); + const float fy = convY - col.posY(); + const float fz = convZ - col.posZ(); + const float gx = pP[0] + pN[0], gy = pP[1] + pN[1], gz = pP[2] + pN[2]; + const float fn = std::sqrt(fx * fx + fy * fy + fz * fz); + const float gn = std::sqrt(gx * gx + gy * gy + gz * gz); + if (fn < 1e-3f || gn < 1e-6f) { // o2-linter: disable=magic-number (check if number is too small) + return 999.f; + } + const float cospa = std::clamp((fx * gx + fy * gy + fz * gz) / (fn * gn), -1.f, 1.f); + pcaOut = pca3D; + cospaOut = cospa; + convROut = std::hypot(convX, convY); + return wScore * 60.f * std::acos(cospa) + (1.f - wScore) * pca3D / 3.f; // getScoreV0 + }; + std::vector allCands; + allCands.reserve(v0s.size()); + std::unordered_map> v0sByPosTrack; + for (const auto& v0 : v0s) { + if (v0.v0Type() == 0) { + continue; + } + if (v0.posTrackId() < 0 || v0.negTrackId() < 0 || + v0.posTrackId() >= static_cast(tracks.size()) || + v0.negTrackId() >= static_cast(tracks.size())) { + continue; + } + V0Lite v; + v.gi = v0.globalIndex(); + v.collisionId = v0.collisionId(); + v.posTrackId = v0.posTrackId(); + v.negTrackId = v0.negTrackId(); + v.typeRaw = v0.v0Type(); + v.score = anaV0(v.posTrackId, v.negTrackId, v.collisionId, v.pca, v.cospa, v.convR); + v0sByPosTrack[v.posTrackId].push_back(static_cast(allCands.size())); + allCands.push_back(v); + } + + struct MatchInfo { + int collisionId = -1; + int posTrackId = -1, negTrackId = -1; + uint8_t typeRaw = 0; + }; + struct GammaLite { + int64_t mcId = -1; + float eta = 0.f, phi = 0.f; + float px = 0.f, py = 0.f, pz = 0.f; + float rConv = 0.f; + bool tracked = false; // both legs have tracks (anywhere) + std::vector trackedColls; + std::vector matches; + }; + + std::map> gammasByMcColl; + for (const auto& mc : mcparticles) { + if (mc.pdgCode() != PDG_t::kGamma || !mc.isPhysicalPrimary() || !mc.has_daughters()) { + continue; + } + + const float mcV0PtMin = (mctruth.cfgMCMinV0Pt.value > 0.f) + ? mctruth.cfgMCMinV0Pt.value + : pcmcuts.cfgMinPtV0.value; + if (std::fabs(mc.eta()) > pcmcuts.cfgMaxEtaV0.value || mc.pt() < mcV0PtMin) { + continue; + } + int posId = -1, negId = -1; + for (const auto& dId : mc.daughtersIds()) { + if (dId < 0) { + continue; + } + const auto d = mcparticles.iteratorAt(dId); + if (d.pdgCode() == PDG_t::kPositron) { + posId = dId; + } else if (d.pdgCode() == PDG_t::kElectron) { + negId = dId; + } + } + if (posId < 0 || negId < 0) { + continue; // not a conversion + } + const auto dPos = mcparticles.iteratorAt(posId); + const auto dNeg = mcparticles.iteratorAt(negId); + const float rConv = std::hypot(dPos.vx(), dPos.vy()); + if (rConv < kRMinConv || rConv > kRMaxConv) { + continue; + } + + if (std::fabs(dPos.eta()) > pcmcuts.cfgMaxEtaV0.value || + std::fabs(dNeg.eta()) > pcmcuts.cfgMaxEtaV0.value) { + continue; + } + if (mctruth.cfgMCMinLegPt.value > 0.f && + (dPos.pt() < mctruth.cfgMCMinLegPt.value || dNeg.pt() < mctruth.cfgMCMinLegPt.value)) { + continue; + } + + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStage"), 0.f); + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStageVsPt"), 0.f, mc.pt()); + GammaLite g; + g.mcId = mc.globalIndex(); + g.eta = mc.eta(); + g.phi = mc.phi(); + g.px = mc.px(); + g.py = mc.py(); + g.pz = mc.pz(); + g.rConv = rConv; + + const auto itPos = tracksOfMc.find(posId); + const auto itNeg = tracksOfMc.find(negId); + if (itPos != tracksOfMc.end() && itNeg != tracksOfMc.end()) { + g.tracked = true; + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStage"), 1.f); + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStageVsPt"), 1.f, mc.pt()); + + std::set commonColls; + for (const auto& [posGi, posCol] : itPos->second) { + for (const auto& [negGi, negCol] : itNeg->second) { + if (posCol >= 0 && posCol == negCol) { + commonColls.insert(posCol); + } + } + } + if (!commonColls.empty()) { + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStage"), 2.f); + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStageVsPt"), 2.f, mc.pt()); + fRegistryTruthMC.fill(HIST("MCAOD/hNTrackedColls"), static_cast(commonColls.size())); + g.trackedColls.assign(commonColls.begin(), commonColls.end()); + + const V0Lite* firstMatch = nullptr; + int nMatches = 0; + for (const auto& [posGi, posCol] : itPos->second) { + const auto itV = v0sByPosTrack.find(static_cast(posGi)); + if (itV == v0sByPosTrack.end()) { + continue; + } + for (const int& iv : itV->second) { + const auto& v = allCands[iv]; + bool negOk = false; + for (const auto& [negGi, negCol] : itNeg->second) { + if (static_cast(negGi) == v.negTrackId) { + negOk = true; + break; + } + } + if (!negOk) { + continue; + } + ++nMatches; + if (firstMatch == nullptr) { + firstMatch = &v; + } + const bool inLegColl = commonColls.contains(v.collisionId); + fRegistryTruthMC.fill(HIST("MCAOD/hV0CollMatch"), inLegColl ? 0.f : 1.f); + + bool knownColl = false; + for (const auto& m : g.matches) { + if (m.collisionId == v.collisionId) { + knownColl = true; // keep only the first V0 per collision + break; + } + } + if (!knownColl) { + g.matches.push_back({v.collisionId, v.posTrackId, v.negTrackId, v.typeRaw}); + fRegistryTruthMC.fill(HIST("MCAOD/hV0Type"), static_cast(v.typeRaw)); + } + } + } + if (nMatches > 0) { + fRegistryTruthMC.fill(HIST("MCAOD/hV0MatchMultiplicity"), static_cast(nMatches)); + } + if (!g.matches.empty()) { + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStage"), 3.f); + fRegistryTruthMC.fill(HIST("MCAOD/hPhotonStageVsPt"), 3.f, mc.pt()); + } + } + } + gammasByMcColl[mc.mcCollisionId()].push_back(g); + } + + constexpr int kNDedup = 8; + const int nC = static_cast(allCands.size()); + std::vector candMother(nC, -1); + std::vector candClass(nC, 2); + { + auto photonMotherOf = [&](int trackId) -> int64_t { + const auto t = tracks.rawIteratorAt(trackId); + if (!t.has_mcParticle()) { + return -1; + } + const auto p = mcparticles.iteratorAt(t.mcParticleId()); + if (!p.has_mothers()) { + return -1; + } + const auto& mids = p.mothersIds(); + if (mids.empty() || mids[0] < 0) { + return -1; + } + return (mcparticles.iteratorAt(mids[0]).pdgCode() == PDG_t::kGamma) ? static_cast(mids[0]) : -1; + }; + + for (int i = 0; i < nC; ++i) { + const int64_t mp = photonMotherOf(allCands[i].posTrackId); + const int64_t me = photonMotherOf(allCands[i].negTrackId); + if (mp >= 0 && mp == me) { + candClass[i] = 0; + candMother[i] = mp; + } else if (mp >= 0 && me >= 0) { + candClass[i] = 1; + } + const auto cls = static_cast(candClass[i]); + fRegistryTruthMC.fill(HIST("MCAOD/hAnaScore"), cls, std::min(allCands[i].score, 29.9f)); + fRegistryTruthMC.fill(HIST("MCAOD/hAnaPCA"), cls, std::min(allCands[i].pca, 29.9f)); + fRegistryTruthMC.fill(HIST("MCAOD/hAnaCosPA"), cls, allCands[i].cospa); + if (allCands[i].convR > 35.f) { // o2-linter: disable=magic-number (radius) + fRegistryTruthMC.fill(HIST("MCAOD/hAnaScore_Rgt35"), cls, std::min(allCands[i].score, 29.9f)); + fRegistryTruthMC.fill(HIST("MCAOD/hAnaPCA_Rgt35"), cls, std::min(allCands[i].pca, 29.9f)); + fRegistryTruthMC.fill(HIST("MCAOD/hAnaCosPA_Rgt35"), cls, allCands[i].cospa); + } + } + } + + { + auto findCand = [&](int pos, int neg, int coll) -> int { + const auto it = v0sByPosTrack.find(pos); + if (it == v0sByPosTrack.end()) { + return -1; + } + for (const int& idx : it->second) { + if (allCands[idx].negTrackId == neg && allCands[idx].collisionId == coll) { + return idx; + } + } + return -1; + }; + auto qinvTrue = [&](int64_t ma, int64_t mb) -> float { + const auto pa = mcparticles.iteratorAt(ma); + const auto pb = mcparticles.iteratorAt(mb); + // photons: E = |p| + const float e1 = std::hypot(pa.px(), pa.py(), pa.pz()); + const float e2 = std::hypot(pb.px(), pb.py(), pb.pz()); + return std::sqrt(std::max(0.f, 2.f * (e1 * e2 - pa.px() * pb.px() - + pa.py() * pb.py() - pa.pz() * pb.pz()))); + }; + std::unordered_map> trueByColl; + for (int i = 0; i < nC; ++i) { + if (candClass[i] == 0) { + trueByColl[allCands[i].collisionId].push_back(i); + } + } + for (const auto& [coll, trues] : trueByColl) { + for (size_t a = 0; a < trues.size(); ++a) { + for (size_t b = a + 1; b < trues.size(); ++b) { + const int i = trues[a], j = trues[b]; + if (candMother[i] == candMother[j]) { + continue; // same photon twice + } + const int x1 = findCand(allCands[i].posTrackId, allCands[j].negTrackId, coll); + const int x2 = findCand(allCands[j].posTrackId, allCands[i].negTrackId, coll); + if (x1 < 0 || x2 < 0) { + continue; // no complete alternative matching + } + const float sTrue = allCands[i].score + allCands[j].score; + const float sCross = allCands[x1].score + allCands[x2].score; + const float deltaS = std::fabs(sTrue - sCross); + const float q = qinvTrue(candMother[i], candMother[j]); + fRegistryTruthMC.fill(HIST("MCAOD/hDeltaS2x2_vs_Qinv"), std::min(deltaS, 9.99f), q); + if (sCross < sTrue) { // smaller score = better in this builder + fRegistryTruthMC.fill(HIST("MCAOD/hDeltaS2x2_CrossWins"), std::min(deltaS, 9.99f), q); + } + } + } + } + } + std::array, kNDedup> dedupAlive; + for (auto& a : dedupAlive) { // o2-linter: disable=const-ref-in-for-loop (assign modifies the elements) + a.assign(nC, 0); + } + std::fill(dedupAlive[0].begin(), dedupAlive[0].end(), 1); + { + + auto runGreedy = [&](int scenario, int ordering, bool perCollision) { + std::vector order(nC); + for (int i = 0; i < nC; ++i) { + order[i] = i; + } + std::sort(order.begin(), order.end(), [&](int a, int b) { + if (ordering == 2) { // o2-linter: disable=magic-number (check if number is too small) + if (allCands[a].score != allCands[b].score) { + return allCands[a].score < allCands[b].score; + } + return allCands[a].gi < allCands[b].gi; + } + const bool truthFirst = (ordering == 0); + const int ka = (candClass[a] == 0) ? (truthFirst ? 0 : 1) : (truthFirst ? 1 : 0); + const int kb = (candClass[b] == 0) ? (truthFirst ? 0 : 1) : (truthFirst ? 1 : 0); + if (ka != kb) { + return ka < kb; + } + return allCands[a].gi < allCands[b].gi; + }); + std::set> usedLegs; + for (const int& i : order) { + const int c = perCollision ? allCands[i].collisionId : -1; + if (usedLegs.contains({c, allCands[i].posTrackId}) || + usedLegs.contains({c, allCands[i].negTrackId})) { + continue; // a leg is already owned by an earlier (preferred) candidate + } + usedLegs.insert({c, allCands[i].posTrackId}); + usedLegs.insert({c, allCands[i].negTrackId}); + dedupAlive[scenario][i] = 1; + } + }; + runGreedy(1, 0, true); // per-collision + runGreedy(2, 1, true); // per-collision fake-first + runGreedy(3, 0, false); // global (legs blocked across collisions) + runGreedy(4, 1, false); // global fake-first + runGreedy(5, 2, true); // per-collision by analytic score + runGreedy(6, 2, false); // builder greedy structure + } + { + std::unordered_map> byNegTrack; + for (int i = 0; i < nC; ++i) { + byNegTrack[allCands[i].negTrackId].push_back(i); + } + std::set> storedLegPairs; + for (int i = 0; i < nC; ++i) { + bool accept = true; + auto compare = [&](int j) { + if (!accept || allCands[i].gi == allCands[j].gi) { + return; // skip exactly the same v0 + } + if (allCands[i].collisionId != allCands[j].collisionId && + allCands[i].posTrackId == allCands[j].posTrackId && + allCands[i].negTrackId == allCands[j].negTrackId && + allCands[i].cospa < allCands[j].cospa) { + accept = false; + return; + } + if ((allCands[i].posTrackId == allCands[j].posTrackId || + allCands[i].negTrackId == allCands[j].negTrackId) && + allCands[i].pca > allCands[j].pca) { + accept = false; // shares a leg with a closer candidate + } + }; + for (const int& j : v0sByPosTrack[allCands[i].posTrackId]) { + compare(j); + } + if (accept) { + for (const int& j : byNegTrack[allCands[i].negTrackId]) { + compare(j); + } + } + if (accept && storedLegPairs.insert({allCands[i].posTrackId, allCands[i].negTrackId}).second) { + dedupAlive[7][i] = 1; + } + } + } + for (int p = 0; p < kNDedup; ++p) { + for (int i = 0; i < nC; ++i) { + if (dedupAlive[p][i] != 0) { + fRegistryTruthMC.fill(HIST("MCAOD/hDedupNCand"), static_cast(p), static_cast(candClass[i])); + } + } + } + std::unordered_map, kNDedup>> motherColls; + for (int i = 0; i < nC; ++i) { + if (candClass[i] != 0) { + continue; + } + auto& arr = motherColls[candMother[i]]; + for (int p = 0; p < kNDedup; ++p) { + if (dedupAlive[p][i] != 0) { + arr[p].push_back(allCands[i].collisionId); + } + } + } + + const float maxQ = mctruth.cfgMCMaxQinv.value > 0.f ? mctruth.cfgMCMaxQinv.value : 0.3f; + for (const auto& [mcCol, gammas] : gammasByMcColl) { + for (size_t i = 0; i < gammas.size(); ++i) { + for (size_t j = i + 1; j < gammas.size(); ++j) { + const auto& a = gammas[i]; + const auto& b = gammas[j]; + const float e1 = std::hypot(a.px, a.py, a.pz); + const float e2 = std::hypot(b.px, b.py, b.pz); + const float qinv = std::sqrt(std::max(0.f, 2.f * (e1 * e2 - a.px * b.px - a.py * b.py - a.pz * b.pz))); + if (qinv > maxQ) { + continue; // the cost gate + } + const float dEta = a.eta - b.eta; + const float dPhi = RecoDecay::constrainAngle(a.phi - b.phi, -o2::constants::math::PI); + const float dR = std::fabs(a.rConv - b.rConv); + const float kt = 0.5f * std::hypot(a.px + b.px, a.py + b.py); + auto fillStage = [&](float s) { + fRegistryTruthMC.fill(HIST("MCAOD/hPairStage"), s); + fRegistryTruthMC.fill(HIST("MCAOD/hPairStageVsKt"), s, kt); + fRegistryTruthMC.fill(HIST("MCAOD/hSparsePairStage"), s, dEta, dPhi, qinv); + fRegistryTruthMC.fill(HIST("MCAOD/hSparseDRStage"), s, dR, qinv); + }; + auto fillSplit = [&](float m) { + fRegistryTruthMC.fill(HIST("MCAOD/hPairSplit"), m); + fRegistryTruthMC.fill(HIST("MCAOD/hSparsePairSplit"), m, dEta, dPhi, qinv); + }; + + fillStage(0.f); + { + const auto itA = motherColls.find(a.mcId); + const auto itB = motherColls.find(b.mcId); + if (itA != motherColls.end() && itB != motherColls.end()) { + for (int p = 0; p < kNDedup; ++p) { + bool together = false; + for (const int& ca : itA->second[p]) { + for (const int& cb : itB->second[p]) { + if (ca == cb) { + together = true; + } + } + } + if (together) { + fRegistryTruthMC.fill(HIST("MCAOD/hSparsePairDedup"), static_cast(p), dEta, dPhi, qinv); + } + } + } + } + + if (!a.tracked || !b.tracked) { + continue; + } + // all 4 legs tracked (somewhere) + fillStage(1.f); + + // all 4 legs in ONE collision + bool commonTracked = false; + for (const int& ca : a.trackedColls) { + for (const int& cb : b.trackedColls) { + if (ca == cb) { + commonTracked = true; + } + } + } + if (!commonTracked) { + if (!a.trackedColls.empty() && !b.trackedColls.empty()) { + fillSplit(0.f); // each photon complete SOMEWHERE — pair split at track level + } + continue; + } + fillStage(2.f); + + // both tracks matched to V0s in ONE collision + const MatchInfo* ma = nullptr; + const MatchInfo* mb = nullptr; + for (const auto& x : a.matches) { + for (const auto& y : b.matches) { + if (x.collisionId == y.collisionId && ma == nullptr) { + ma = &x; + mb = &y; + } + } + } + if (ma == nullptr) { + if (!a.matches.empty() && !b.matches.empty()) { + fillSplit(1.f); // both photons became V0s but in diff. events + } + continue; + } + if (ma->posTrackId == mb->posTrackId || ma->negTrackId == mb->negTrackId) { + continue; // duplicates + } + fillStage(3.f); + const auto t1 = static_cast(std::min(ma->typeRaw, mb->typeRaw)); + const auto t2 = static_cast(std::max(ma->typeRaw, mb->typeRaw)); + fRegistryTruthMC.fill(HIST("MCAOD/hSparsePairV0Type"), dEta, dPhi, qinv, t1, t2); + } + } + } + } + + PROCESS_SWITCH(Photonhbt, processMCAOD, "truth/SVertexer pair QA directly on AO2Ds", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/prefilterPhoton.cxx b/PWGEM/PhotonMeson/Tasks/prefilterPhoton.cxx index 4072a74e096..8c1b1ed5bcb 100644 --- a/PWGEM/PhotonMeson/Tasks/prefilterPhoton.cxx +++ b/PWGEM/PhotonMeson/Tasks/prefilterPhoton.cxx @@ -29,10 +29,10 @@ #include #include #include -#include #include #include #include +#include #include #include #include @@ -47,6 +47,7 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include #include #include @@ -60,7 +61,7 @@ using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::pwgem::photonmeson::photonpair; -using MyCollisions = soa::Join; +using MyCollisions = soa::Join; using MyCollision = MyCollisions::iterator; using MyV0Photons = soa::Join; @@ -75,9 +76,6 @@ struct prefilterPhoton { // Configurables Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable skipGRPOquery{"skipGRPOquery", true, "skip grpo query"}; Configurable d_bz_input{"d_bz_input", -999, "bz field in kG, -999 is automatic"}; Configurable cfgCentEstimator{"cfgCentEstimator", 2, "FT0M:0, FT0A:1, FT0C:2"}; @@ -110,6 +108,8 @@ struct prefilterPhoton { Configurable cfg_max_eta_v0{"cfg_max_eta_v0", +0.9, "max eta for v0 photons at PV"}; Configurable cfg_min_v0radius{"cfg_min_v0radius", 4.0, "min v0 radius"}; Configurable cfg_max_v0radius{"cfg_max_v0radius", 90.0, "max v0 radius"}; + Configurable cfg_midL_v0radius{"cfg_midL_v0radius", -1.0, "middle low v0 radius for rejection if >0"}; + Configurable cfg_midH_v0radius{"cfg_midH_v0radius", -1.0, "middle high v0 radius for rejection if >0"}; Configurable cfg_max_alpha_ap{"cfg_max_alpha_ap", 0.95, "max alpha for AP cut"}; Configurable cfg_max_qt_ap{"cfg_max_qt_ap", 0.01, "max qT for AP cut"}; Configurable cfg_min_cospa{"cfg_min_cospa", 0.99, "min V0 CosPA"}; @@ -178,9 +178,9 @@ struct prefilterPhoton { HistogramRegistry fRegistry{"output", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; o2::ccdb::CcdbApi ccdbApi; - Service ccdb; - int mRunNumber; - float d_bz; + Service ccdb{}; + int mRunNumber = 0; + float dBz = 0; void init(InitContext& /*context*/) { @@ -189,7 +189,7 @@ struct prefilterPhoton { addhistograms(); mRunNumber = 0; - d_bz = 0; + dBz = 0; ccdb->setURL(ccdburl); ccdb->setCaching(true); @@ -197,7 +197,7 @@ struct prefilterPhoton { ccdb->setFatalWhenNull(false); } - ~prefilterPhoton() {} + ~prefilterPhoton() = default; template void initCCDB(TCollision const& collision) @@ -205,37 +205,21 @@ struct prefilterPhoton { if (mRunNumber == collision.runNumber()) { return; } + mRunNumber = collision.runNumber(); // In case override, don't proceed, please - no CCDB access required if (d_bz_input > -990) { - d_bz = d_bz_input; + dBz = d_bz_input; o2::parameters::GRPMagField grpmag; - if (fabs(d_bz) > 1e-5) { - grpmag.setL3Current(30000.f / (d_bz / 5.0f)); + if (fabs(dBz) > 1e-5) { + grpmag.setL3Current(30000.f / (dBz / 5.0f)); } - mRunNumber = collision.runNumber(); - return; } auto run3grp_timestamp = collision.timestamp(); - o2::parameters::GRPObject* grpo = 0x0; - o2::parameters::GRPMagField* grpmag = 0x0; - if (!skipGRPOquery) - grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); - if (grpo) { - // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; - } - // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; - } - mRunNumber = collision.runNumber(); + // Fetch magnetic field from ccdb for current collision + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << dBz << " kZG"; } void addhistograms() @@ -274,7 +258,7 @@ struct prefilterPhoton { fV0PhotonCut.SetMinCosPA(pcmcuts.cfg_min_cospa); fV0PhotonCut.SetMaxPCA(pcmcuts.cfg_max_pca); fV0PhotonCut.SetMaxChi2KF(pcmcuts.cfg_max_chi2kf); - fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius); + fV0PhotonCut.SetRxyRange(pcmcuts.cfg_min_v0radius, pcmcuts.cfg_max_v0radius, pcmcuts.cfg_midL_v0radius, pcmcuts.cfg_midH_v0radius); fV0PhotonCut.SetAPRange(pcmcuts.cfg_max_alpha_ap, pcmcuts.cfg_max_qt_ap); fV0PhotonCut.RejectITSib(pcmcuts.cfg_reject_v0_on_itsib); @@ -348,7 +332,7 @@ struct prefilterPhoton { if constexpr (pairtype == PairType::kPCMPCM) { for (const auto& collision : collisions) { initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; bool is_cent_ok = true; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { is_cent_ok = false; @@ -383,7 +367,7 @@ struct prefilterPhoton { } else if constexpr (pairtype == PairType::kPCMDalitzEE) { for (const auto& collision : collisions) { initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; bool is_cent_ok = true; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { is_cent_ok = false; @@ -446,7 +430,7 @@ struct prefilterPhoton { ROOT::Math::PtEtaPhiMVector v_pos(pos2.pt(), pos2.eta(), pos2.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v_ele(ele2.pt(), ele2.eta(), ele2.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v_ee = v_pos + v_ele; - if (!(dileptoncuts.cfg_min_mee < v_ee.M() && v_ee.M() < dileptoncuts.cfg_max_mee)) { + if (!(dileptoncuts.cfg_min_mee < v_ee.M()) || !(v_ee.M() < dileptoncuts.cfg_max_mee)) { continue; } ROOT::Math::PtEtaPhiMVector veeg = v_gamma + v_pos + v_ele; @@ -472,7 +456,7 @@ struct prefilterPhoton { ROOT::Math::PtEtaPhiMVector v_pos(pos2.pt(), pos2.eta(), pos2.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v_ele(ele2.pt(), ele2.eta(), ele2.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v_ee = v_pos + v_ele; - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(pos2.px(), pos2.py(), pos2.pz(), ele2.px(), ele2.py(), ele2.pz(), pos2.sign(), ele2.sign(), d_bz); + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(pos2.px(), pos2.py(), pos2.pz(), ele2.px(), ele2.py(), ele2.pz(), pos2.sign(), ele2.sign(), dBz); fRegistry.fill(HIST("Pair/PCMDalitzEE/before/hMvsPhiV"), phiv, v_ee.M()); if (v_ee.M() < phiv * dileptoncuts.cfg_phiv_slope + dileptoncuts.cfg_phiv_intercept) { @@ -499,7 +483,7 @@ struct prefilterPhoton { // check pfb. if constexpr (pairtype == PairType::kPCMPCM) { for (auto& collision : collisions) { - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -528,7 +512,7 @@ struct prefilterPhoton { } else if constexpr (pairtype == PairType::kPCMDalitzEE) { for (auto& collision : collisions) { initCCDB(collision); - const float centralities[3] = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; + const std::array centralities = {collision.centFT0M(), collision.centFT0A(), collision.centFT0C()}; if (centralities[cfgCentEstimator] < cfgCentMin || cfgCentMax < centralities[cfgCentEstimator]) { continue; } @@ -581,8 +565,8 @@ struct prefilterPhoton { ROOT::Math::PtEtaPhiMVector v_pos(pos2.pt(), pos2.eta(), pos2.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v_ele(ele2.pt(), ele2.eta(), ele2.phi(), o2::constants::physics::MassElectron); ROOT::Math::PtEtaPhiMVector v_ee = v_pos + v_ele; - float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(pos2.px(), pos2.py(), pos2.pz(), ele2.px(), ele2.py(), ele2.pz(), pos2.sign(), ele2.sign(), d_bz); - if (!(dileptoncuts.cfg_min_mee < v_ee.M() && v_ee.M() < dileptoncuts.cfg_max_mee)) { + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(pos2.px(), pos2.py(), pos2.pz(), ele2.px(), ele2.py(), ele2.pz(), pos2.sign(), ele2.sign(), dBz); + if (!(dileptoncuts.cfg_min_mee < v_ee.M()) || !(v_ee.M() < dileptoncuts.cfg_max_mee)) { continue; } ROOT::Math::PtEtaPhiMVector veeg = v_gamma + v_pos + v_ele; @@ -641,7 +625,7 @@ struct prefilterPhoton { PROCESS_SWITCH(prefilterPhoton, processDummyElectron, "dummy for electrons", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"prefilter-photon"})}; + return WorkflowSpec{adaptAnalysisTask(context, TaskName{"prefilter-photon"})}; } diff --git a/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx b/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx index f7c5484084c..6c4b0ecaed8 100644 --- a/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskFlowReso.cxx @@ -22,8 +22,9 @@ #include "Common/Core/RecoDecay.h" #include -#include +#include #include +#include #include #include #include @@ -50,7 +51,7 @@ using namespace o2::aod::pwgem::photon; enum QvecEstimator { FT0M = 0, - FT0A = 1, + FT0A, FT0C, TPCPos, TPCNeg, @@ -60,7 +61,7 @@ enum QvecEstimator { enum CentralityEstimator { None = 0, - CFT0A = 1, + CFT0A, CFT0C, CFT0M, NCentralityEstimators @@ -164,15 +165,22 @@ struct TaskFlowReso { registry.add("spReso/hSpResoFT0cTPCtot", "hSpResoFT0cTPCtot; centrality; Q_{FT0c} #bullet Q_{TPCtot}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFT0aTPCpos", "hSpResoFT0aTPCpos; centrality; Q_{FT0a} #bullet Q_{TPCpos}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFT0aTPCneg", "hSpResoFT0aTPCneg; centrality; Q_{FT0a} #bullet Q_{TPCneg}", HistType::kTProfile, {thnAxisCent}); - registry.add("spReso/hSpResoFT0aTPCtot", "hSpResoFT0aTPCtot; centrality; Q_{FT0m} #bullet Q_{TPCtot}", HistType::kTProfile, {thnAxisCent}); + registry.add("spReso/hSpResoFT0aTPCtot", "hSpResoFT0aTPCtot; centrality; Q_{FT0a} #bullet Q_{TPCtot}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFT0mTPCpos", "hSpResoFT0mTPCpos; centrality; Q_{FT0m} #bullet Q_{TPCpos}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFT0mTPCneg", "hSpResoFT0mTPCneg; centrality; Q_{FT0m} #bullet Q_{TPCneg}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFT0mTPCtot", "hSpResoFT0mTPCtot; centrality; Q_{FT0m} #bullet Q_{TPCtot}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoTPCposTPCneg", "hSpResoTPCposTPCneg; centrality; Q_{TPCpos} #bullet Q_{TPCneg}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFV0aFT0c", "hSpResoFV0aFT0c; centrality; Q_{FV0a} #bullet Q_{FT0c}", HistType::kTProfile, {thnAxisCent}); + registry.add("spReso/hSpResoFV0aFT0a", "hSpResoFV0aFT0a; centrality; Q_{FV0a} #bullet Q_{FT0a}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFV0aTPCpos", "hSpResoFV0aTPCpos; centrality; Q_{FV0a} #bullet Q_{TPCpos}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFV0aTPCneg", "hSpResoFV0aTPCneg; centrality; Q_{FV0a} #bullet Q_{TPCneg}", HistType::kTProfile, {thnAxisCent}); registry.add("spReso/hSpResoFV0aTPCtot", "hSpResoFV0aTPCtot; centrality; Q_{FV0a} #bullet Q_{TPCtot}", HistType::kTProfile, {thnAxisCent}); + + // auto correlations + registry.add("spReso/hSpResoFT0cFT0c", "hSpResoFT0cFT0c; centrality; Q_{FT0c} #bullet Q_{FT0c}", HistType::kTProfile, {thnAxisCent}); + registry.add("spReso/hSpResoFT0aFT0a", "hSpResoFT0aFT0a; centrality; Q_{FT0a} #bullet Q_{FT0a}", HistType::kTProfile, {thnAxisCent}); + registry.add("spReso/hSpResoFT0mFT0m", "hSpResoFT0mFT0m; centrality; Q_{FT0m} #bullet Q_{FT0m}", HistType::kTProfile, {thnAxisCent}); + registry.add("spReso/hSpResoFV0aFV0a", "hSpResoFV0aFV0a; centrality; Q_{FV0a} #bullet Q_{FV0a}", HistType::kTProfile, {thnAxisCent}); } if (saveEpResoHisto.value) { @@ -375,7 +383,7 @@ struct TaskFlowReso { // no selection on the centrality is applied on purpose to allow for the resolution study in post-processing return; } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange()) || !(collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { return; } float cent = getCentrality(collision); @@ -451,9 +459,15 @@ struct TaskFlowReso { registry.fill(HIST("spReso/hSpResoFT0mTPCtot"), centrality, xQVecFT0m * xQVecBTot + yQVecFT0m * yQVecBTot); registry.fill(HIST("spReso/hSpResoTPCposTPCneg"), centrality, xQVecBPos * xQVecBNeg + yQVecBPos * yQVecBNeg); registry.fill(HIST("spReso/hSpResoFV0aFT0c"), centrality, xQVecFV0a * xQVecFT0c + yQVecFV0a * yQVecFT0c); + registry.fill(HIST("spReso/hSpResoFV0aFT0a"), centrality, xQVecFV0a * xQVecFT0a + yQVecFV0a * yQVecFT0a); registry.fill(HIST("spReso/hSpResoFV0aTPCpos"), centrality, xQVecFV0a * xQVecBPos + yQVecFV0a * yQVecBPos); registry.fill(HIST("spReso/hSpResoFV0aTPCneg"), centrality, xQVecFV0a * xQVecBNeg + yQVecFV0a * yQVecBNeg); registry.fill(HIST("spReso/hSpResoFV0aTPCtot"), centrality, xQVecFV0a * xQVecBTot + yQVecFV0a * yQVecBTot); + // auto-correlations for forward detectors, this is needed for combining subevents! + registry.fill(HIST("spReso/hSpResoFT0cFT0c"), centrality, xQVecFT0c * xQVecFT0c + yQVecFT0c * yQVecFT0c); + registry.fill(HIST("spReso/hSpResoFT0aFT0a"), centrality, xQVecFT0a * xQVecFT0a + yQVecFT0a * yQVecFT0a); + registry.fill(HIST("spReso/hSpResoFT0mFT0m"), centrality, xQVecFT0m * xQVecFT0m + yQVecFT0m * yQVecFT0m); + registry.fill(HIST("spReso/hSpResoFV0aFV0a"), centrality, xQVecFV0a * xQVecFV0a + yQVecFV0a * yQVecFV0a); } if (saveEpResoHisto) { @@ -466,8 +480,8 @@ struct TaskFlowReso { float epFV0a = epHelper.GetEventPlane(xQVecFV0a, yQVecFV0a, harmonic); registry.fill(HIST("hEventPlaneAngleFT0M"), centrality, epFT0m); - registry.fill(HIST("hEventPlaneAngleFT0A"), centrality, epFT0c); - registry.fill(HIST("hEventPlaneAngleFT0C"), centrality, epFT0a); + registry.fill(HIST("hEventPlaneAngleFT0A"), centrality, epFT0a); + registry.fill(HIST("hEventPlaneAngleFT0C"), centrality, epFT0c); registry.fill(HIST("hEventPlaneAngleTPCpos"), centrality, epBPoss); registry.fill(HIST("hEventPlaneAngleTPCneg"), centrality, epBNegs); registry.fill(HIST("hEventPlaneAngleFV0A"), centrality, epFV0a); @@ -517,7 +531,7 @@ struct TaskFlowReso { }; // End struct TaskFlowReso -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx b/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx new file mode 100644 index 00000000000..41f47171448 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/taskPhotonFlow.cxx @@ -0,0 +1,783 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskPhotonFlow.cxx +/// \brief Analysis task for photon flow with PCM or EMCal +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" + +#include "Common/Core/EventPlaneHelper.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; + +enum QvecEstimator { + FT0M = 0, + FT0A = 1, + FT0C = 2, + TPCPos = 3, + TPCNeg = 4, + TPCTot = 5, + FV0A = 6 +}; + +enum CentralityEstimator { + None = 0, + CFT0A = 1, + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 +}; + +enum Harmonics { + kNone = 0, + kDirect = 1, + kElliptic = 2, + kTriangluar = 3, + kQuadrangular = 4, + kPentagonal = 5, + kHexagonal = 6, + kHeptagonal = 7, + kOctagonal = 8 +}; + +enum class MapLevel { + kGood = 1, + kNoBad = 2, + kInEMC = 3, + kAll = 4 +}; + +struct TaskPhotonFlow { + // configurable for flow + Configurable harmonic{"harmonic", 2, "harmonic number"}; + Configurable qvecDetector{"qvecDetector", 0, "Detector for Q vector estimation (FT0M: 0, FT0A: 1, FT0C: 2, TPC Pos: 3, TPC Neg: 4, TPC Tot: 5, FV0A: 6)"}; + Configurable qvecSubADetector{"qvecSubADetector", 3, "Sub A Detector for Q vector estimation for resolution (FT0M: 0, FT0A: 1, FT0C: 2, TPC Pos: 3, TPC Neg: 4, TPC Tot: 5, FV0A: 6)"}; + Configurable qvecSubBDetector{"qvecSubBDetector", 4, "Sub B Detector for Q vector estimation for resolution (FT0M: 0, FT0A: 1, FT0C: 2, TPC Pos: 3, TPC Neg: 4, TPC Tot: 5, FV0A: 6)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgEMCalMapLevelSameEvent{"cfgEMCalMapLevelSameEvent", 4, "Different levels of correction for the same event, the smaller number includes the level of the higher number (4: none, 3: only inside EMCal, 2: remove edges, 1: exclude bad channels)"}; + Configurable cfgDistanceToEdge{"cfgDistanceToEdge", 1, "Distance to edge in cells required for rotated cluster to be accepted"}; + Configurable cfgMaxQVector{"cfgMaxQVector", 20.f, "Maximum allowed absolute QVector value."}; + + // configurable axis + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {100, 0., 20.}, "pT axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {20, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisCosDeltaPhi{"thnConfigAxisCosDeltaPhi", {8, -1., 1.}, "cos(delta phi) axis for the current event"}; + ConfigurableAxis thnConfigAxisM02{"thnConfigAxisM02", {200, 0., 5.}, "M02 axis for the EMCal cluster"}; + ConfigurableAxis thnConfigAxisKappa{"thnConfigAxisKappa", {200, -10., 10.}, "Kappa axis for the EMCal cluster"}; + ConfigurableAxis thnConfigAxisEnergyCalib{"thnConfigAxisEnergyCalib", {200, 0., 20.}, "energy axis for the emcal clusters for the calibration process"}; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "FLag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + Configurable useEMCal{"useEMCal", false, "flag to use EMCal clusters"}; + Configurable useDCal{"useDCal", false, "flag to use DCal clusters"}; + } emccuts; + + V0PhotonCut fV0PhotonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "PCMcuts"; + o2::framework::Configurable requireV0WithITSTPC{"requireV0WithITSTPC", false, "flag to enforce V0s have ITS and TPC"}; + o2::framework::Configurable requireV0WithITSOnly{"requireV0WithITSOnly", false, "flag to select V0s with ITSonly tracks"}; + o2::framework::Configurable requireV0WithTPCOnly{"requireV0WithTPCOnly", false, "flag to select V0s with TPConly tracks"}; + o2::framework::Configurable minPtV0{"minPtV0", 0.1, "min pT for v0 photons at PV"}; + o2::framework::Configurable maxPtV0{"maxPtV0", 1e+10, "max pT for v0 photons at PV"}; + o2::framework::Configurable minEtaV0{"minEtaV0", -0.8, "min eta for v0 photons at PV"}; + o2::framework::Configurable maxEtaV0{"maxEtaV0", 0.8, "max eta for v0 photons at PV"}; + o2::framework::Configurable minRV0{"minRV0", 4.0, "min v0 radius"}; + o2::framework::Configurable maxRV0{"maxRV0", 90.0, "max v0 radius"}; + o2::framework::Configurable maxAlphaAP{"maxAlphaAP", 0.95, "max alpha for AP cut"}; + o2::framework::Configurable maxQtAP{"maxQtAP", 0.01, "max qT for AP cut"}; + o2::framework::Configurable minCosPA{"minCosPA", 0.999, "min V0 CosPA"}; + o2::framework::Configurable maxPCA{"maxPCA", 1.5, "max distance btween 2 legs"}; + o2::framework::Configurable maxChi2KF{"maxChi2KF", 1.e+10f, "max chi2/ndf with KF"}; + o2::framework::Configurable rejectV0onITSib{"rejectV0onITSib", true, "flag to reject V0s on ITSib"}; + o2::framework::Configurable applyPrefilter{"applyPrefilter", false, "flag to apply prefilter to V0"}; + + o2::framework::Configurable minNClusterTPC{"minNClusterTPC", 0, "min NCluster TPC"}; + o2::framework::Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 40, "min ncrossed rows in TPC"}; + o2::framework::Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "min fraction of crossed rows over findable clusters in TPC"}; + o2::framework::Configurable maxFracSharedClustersTPC{"maxFracSharedClustersTPC", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable minNClusterITS{"minNClusterITS", 0, "min NCluster ITS"}; + o2::framework::Configurable minMeanClusterSizeITSob{"minMeanClusterSizeITSob", 0.f, "min ITSob"}; + o2::framework::Configurable maxMeanClusterSizeITSob{"maxMeanClusterSizeITSob", 16.f, "max ITSob"}; + o2::framework::Configurable macChi2TPC{"macChi2TPC", 4.f, "max chi2/NclsTPC"}; + o2::framework::Configurable macChi2ITS{"macChi2ITS", 36.f, "max chi2/NclsITS"}; + o2::framework::Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -3.0f, "min. TPC n sigma for electron"}; + o2::framework::Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +3.0f, "max. TPC n sigma for electron"}; + o2::framework::Configurable disableITSOnly{"disableITSOnly", false, "flag to disable ITSonly tracks"}; + o2::framework::Configurable disableTPCOnly{"disableTPCOnly", false, "flag to disable TPConly tracks"}; + o2::framework::Configurable doQA{"doQA", false, "flag to set QA flag."}; + } pcmcuts; + + struct : ConfigurableGroup { + std::string prefix = "correctionConfig"; + Configurable cfgSpresoPath{"cfgSpresoPath", "Users/m/mhemmer/EM/Flow/Resolution", "Path to SP resolution file"}; + Configurable cfgApplySPresolution{"cfgApplySPresolution", false, "Apply resolution correction"}; + } correctionConfig; + + SliceCache cache; + EventPlaneHelper epHelper; + o2::framework::Service ccdb{}; + int runNow = 0; + int runBefore = -1; + + static constexpr float MaxPhiEMCal = 3.9f; // exatly the middle between EMCal and DCal + static constexpr uint16_t MaxPhiEMCalUint = static_cast(39000u); // exatly the middle between EMCal and DCal but as uint16_t that is used for storing phi values in derived data + + using EMCalPhotons = soa::Join; + using PCMPhotons = soa::Join; + using Colls = soa::Join; + + Partition emcalPhotons = aod::mincluster::storedPhi < MaxPhiEMCalUint; + Partition dcalPhotons = aod::mincluster::storedPhi >= MaxPhiEMCalUint; + + static constexpr std::size_t NQVecEntries = 6; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + o2::emcal::Geometry* emcalGeom = nullptr; + TH1D* h1SPResolution = nullptr; + // Constants for eta and phi ranges for the look up table + static constexpr double EtaMin = -0.75, etaMax = 0.75; + static constexpr int NBinsEta = 150; // 150 bins for eta + + static constexpr double PhiMin = 1.35, phiMax = 5.75; + static constexpr int NBinsPhi = 440; // (440 bins = 0.01 step size covering most regions) + + std::array lookupTable1D{}; + float epsilon = 1.e-8; + + // To access the 1D array + static inline int getIndex(int iEta, int iPhi) + { + return iEta * NBinsPhi + iPhi; + } + + // Function to access the lookup table + inline int8_t checkEtaPhi1D(double eta, double phi) + { + if (eta < EtaMin || eta > etaMax || phi < PhiMin || phi > phiMax) { + return 3; // Out of bounds + } + + // Compute indices directly + int iEta = static_cast((eta - EtaMin) / ((etaMax - EtaMin) / NBinsEta)); + int iPhi = static_cast((phi - PhiMin) / ((phiMax - PhiMin) / NBinsPhi)); + + return lookupTable1D[getIndex(iEta, iPhi)]; + } + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + + // for v0 + fV0PhotonCut.SetV0PtRange(pcmcuts.minPtV0, pcmcuts.maxPtV0); + fV0PhotonCut.SetV0EtaRange(pcmcuts.minEtaV0, pcmcuts.maxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.minCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.maxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.maxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.minRV0, pcmcuts.maxRV0); + fV0PhotonCut.SetAPRange(pcmcuts.maxAlphaAP, pcmcuts.maxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.rejectV0onITSib); + + // for track + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.minNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.minNCrossedRowsTPC); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.minNCrossedRowsOverFindableClustersTPC); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.maxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.f, pcmcuts.macChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.minTPCNSigmaEl, pcmcuts.maxTPCNSigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(0.f, pcmcuts.macChi2ITS); + fV0PhotonCut.SetNClustersITS(pcmcuts.minNClusterITS, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(pcmcuts.minMeanClusterSizeITSob, pcmcuts.maxMeanClusterSizeITSob); + fV0PhotonCut.SetDisableITSonly(pcmcuts.disableITSOnly); + fV0PhotonCut.SetDisableTPConly(pcmcuts.disableTPCOnly); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.requireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.requireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.requireV0WithTPCOnly); + + fV0PhotonCut.setDoQA(pcmcuts.doQA.value); + } + + void init(InitContext&) + { + if (harmonic != kElliptic && harmonic != kTriangluar) { + LOG(info) << "Harmonic was set to " << harmonic << " but can only be 2 or 3!"; + } + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + definePCMCut(); + fV0PhotonCut.addQAHistograms(®istry); + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality (%)"}; + const AxisSpec thnAxisCosDeltaPhi{thnConfigAxisCosDeltaPhi, std::format("cos({}(#varphi - #Psi_{{sub}}))", harmonic.value)}; + const AxisSpec thnAxisM02{thnConfigAxisM02, "M_{02}"}; + const AxisSpec thnAxisKappa{thnConfigAxisKappa, "#Kappa"}; + const AxisSpec thAxisClusterEnergy{thnConfigAxisPt, "#it{E} (GeV)"}; + const AxisSpec thAxisEnergyCalib{thnConfigAxisEnergyCalib, "#it{E}_{clus} (GeV)"}; + const AxisSpec thAxisEnergy{1000, 0., 100., "#it{E}_{clus} (GeV)"}; + const AxisSpec thAxisEta{320, -0.8, 0.8, "#eta"}; + const AxisSpec thAxisPhi{500, 0, 2 * 3.14159, "phi"}; + + if (emccuts.cfgEnableQA.value) { + registry.add("clusterQA/hEClusterBefore", "Histo for cluster energy before cuts", HistType::kTH1D, {thAxisClusterEnergy}); + registry.add("clusterQA/hEClusterAfter", "Histo for cluster energy after cuts", HistType::kTH1D, {thAxisClusterEnergy}); + registry.add("clusterQA/hClusterEtaPhiBefore", "hClusterEtaPhiBefore", HistType::kTH2D, {thAxisPhi, thAxisEta}); + registry.add("clusterQA/hClusterEtaPhiAfter", "hClusterEtaPhiAfter", HistType::kTH2D, {thAxisPhi, thAxisEta}); + } + + if (doprocessEMC) { + registry.add("EMCal/hSparsePhotonFlow", " vs M_{02} vs p_T vs cent", HistType::kTProfile3D, {thnAxisM02, thnAxisPt, thnAxisCent}); + registry.add("EMCal/hSparsePhoton", "M_{02} vs p_T vs cent", HistType::kTH3D, {thnAxisM02, thnAxisPt, thnAxisCent}); + } + if (doprocessPCM) { + registry.add("PCM/hSparsePhotonFlow", " vs M_{02} vs p_T vs cent", HistType::kTProfile3D, {thnAxisM02, thnAxisPt, thnAxisCent}); + registry.add("PCM/hSparsePhoton", "M_{02} vs p_T vs cent", HistType::kTH3D, {thnAxisM02, thnAxisPt, thnAxisCent}); + } + + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + LOG(info) << "thnConfigAxisPt.value[1] = " << thnConfigAxisPt.value[1] << " thnConfigAxisPt.value.back() = " << thnConfigAxisPt.value.back(); + + }; // end init + + /// \brief Check whether a photon (by its phi) falls in the EMCal or DCal acceptance + /// \param phi azimuthal angle of the photon + static bool isEMCalRegion(float phi) + { + return phi < MaxPhiEMCal; + } + + /// Change radians to degree + /// \param angle in radians + /// \return angle in degree + static constexpr float getAngleDegree(float angle) + { + return angle * o2::constants::math::Rad2Deg; + } + + /// Compute the delta psi in the range [0, pi/harmonic] + /// \param psi1 is the first angle + /// \param psi2 is the second angle + float getDeltaPsiInRange(float psi1, float psi2) + { + float deltaPsi = psi1 - psi2; + return RecoDecay::constrainAngle(deltaPsi, 0.f, harmonic); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// Get all used Q vector + /// \param collision is the collision with the Q vector information + template + std::array getAllQvec(TCollision const& collision) + { + // Retrieve the Q vectors using the helper function for each detector + auto [xQVecMain, yQVecMain] = getQvec(collision, qvecDetector); + auto [xQVecSubA, yQVecSubA] = getQvec(collision, qvecSubADetector); + auto [xQVecSubB, yQVecSubB] = getQvec(collision, qvecSubBDetector); + + return {xQVecMain, yQVecMain, xQVecSubA, yQVecSubA, xQVecSubB, yQVecSubB}; + } + + /// Get the Q vector + /// \param collision is the collision with the Q vector information + template + std::pair getQvec(TCollision const& collision, int detector) + { + float xQVec = -999.f; + float yQVec = -999.f; + + switch (detector) { + case QvecEstimator::FT0M: + if (harmonic == kElliptic) { + xQVec = collision.q2xft0m(); + yQVec = collision.q2yft0m(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xft0m(); + yQVec = collision.q3yft0m(); + } + break; + case QvecEstimator::FT0A: + if (harmonic == kElliptic) { + xQVec = collision.q2xft0a(); + yQVec = collision.q2yft0a(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xft0a(); + yQVec = collision.q3yft0a(); + } + break; + case QvecEstimator::FT0C: + if (harmonic == kElliptic) { + xQVec = collision.q2xft0c(); + yQVec = collision.q2yft0c(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xft0c(); + yQVec = collision.q3yft0c(); + } + break; + case QvecEstimator::TPCPos: + if (harmonic == kElliptic) { + xQVec = collision.q2xbpos(); + yQVec = collision.q2ybpos(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xbpos(); + yQVec = collision.q3ybpos(); + } + break; + case QvecEstimator::TPCNeg: + if (harmonic == kElliptic) { + xQVec = collision.q2xbneg(); + yQVec = collision.q2ybneg(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xbneg(); + yQVec = collision.q3ybneg(); + } + break; + case QvecEstimator::TPCTot: + if (harmonic == kElliptic) { + xQVec = collision.q2xbtot(); + yQVec = collision.q2ybtot(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xbtot(); + yQVec = collision.q3ybtot(); + } + break; + case QvecEstimator::FV0A: + if (harmonic == kElliptic) { + xQVec = collision.q2xfv0a(); + yQVec = collision.q2yfv0a(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xfv0a(); + yQVec = collision.q3yfv0a(); + } + break; + default: + LOG(warning) << "Q vector estimator not valid. Falling back to FT0M"; + if (harmonic == kElliptic) { + xQVec = collision.q2xft0m(); + yQVec = collision.q2yft0m(); + } else if (harmonic == kTriangluar) { + xQVec = collision.q3xft0m(); + yQVec = collision.q3yft0m(); + } + break; + } + return {xQVec, yQVec}; + } + + /// Check if the QVector values are within reasonable range + /// \param collision is the collision with the Q vector information + bool isQvecGood(std::array const& QVecs) + { + bool isgood = true; + for (const auto& QVec : QVecs) { + if (std::fabs(QVec) > cfgMaxQVector) { + isgood = false; + break; + } + } + return isgood; + } + + bool isTooCloseToEdge(const int cellID, const int DistanceToBorder = 1) + { + if (DistanceToBorder <= 0) { + return false; + } + if (cellID < 0) { + return true; + } + + int iBadCell = -1; + + // check distance to border in case the cell is okay + auto [iSupMod, iMod, iPhi, iEta] = emcalGeom->GetCellIndex(cellID); + auto [irow, icol] = emcalGeom->GetCellPhiEtaIndexInSModule(iSupMod, iMod, iPhi, iEta); + + // Check rows/phi + int iRowLast = 24; + if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_HALF) { + iRowLast /= 2; // 2/3 sm case + } else if ((emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_THIRD) || + (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::DCAL_EXT)) { + iRowLast /= 3; // 1/3 sm case + } + + if (irow < DistanceToBorder || (iRowLast - irow) <= DistanceToBorder) { + iBadCell = 1; + } + + if (iBadCell > 0) { + return true; + } + return false; + } + + template + bool isCellMasked(int cellID, TCollision const& collision) + { + bool masked = false; + auto maskStatus = collision.badChannelMap().getChannelStatus(cellID); + masked = (maskStatus != o2::emcal::BadChannelMap::MaskType_t::GOOD_CELL); + return masked; + } + + template + float getV0Kappa(TLeg const& pos, TLeg const& ele) + { + float kappa = (std::fabs(pos.tpcNSigmaEl()) + std::fabs(ele.tpcNSigmaEl())) / 2.f + (2.f * pos.tpcNSigmaEl() + ele.tpcNSigmaEl()); + return kappa; + } + + template + void initCCDB(TCollision const& collision) + { + // Load EMCal geometry + emcalGeom = o2::emcal::Geometry::GetInstanceFromRunNumber(collision.runNumber()); + lookupTable1D.fill(-1); + double binWidthEta = (etaMax - EtaMin) / NBinsEta; + double binWidthPhi = (phiMax - PhiMin) / NBinsPhi; + + if (cfgEMCalMapLevelSameEvent >= static_cast(MapLevel::kAll)) { + // in this case we do not want to check the clusters, so just say thery are all good. + lookupTable1D.fill(0); // good + } else { + for (int iEta = 0; iEta < NBinsEta; ++iEta) { + double etaCenter = EtaMin + (iEta + 0.5) * binWidthEta; + for (int iPhi = 0; iPhi < NBinsPhi; ++iPhi) { + double phiCenter = PhiMin + (iPhi + 0.5) * binWidthPhi; + try { + // Get the cell ID + int cellID = emcalGeom->GetAbsCellIdFromEtaPhi(etaCenter, phiCenter); + + // Check conditions for the cell + if (isTooCloseToEdge(cellID, 1)) { + lookupTable1D[getIndex(iEta, iPhi)] = 2; // Edge + } else if (isCellMasked(cellID, collision)) { + lookupTable1D[getIndex(iEta, iPhi)] = 1; // Bad + } else { + lookupTable1D[getIndex(iEta, iPhi)] = 0; // Good + } + } catch (o2::emcal::InvalidPositionException& e) { + lookupTable1D[getIndex(iEta, iPhi)] = 3; // Outside geometry + } + } + } + } + if (correctionConfig.cfgApplySPresolution.value) { + h1SPResolution = ccdb->getForTimeStamp(correctionConfig.cfgSpresoPath.value, collision.timestamp()); + } + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + // occupancy selection + return false; + } + float cent = getCentrality(collision); + if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { + // event selection + return false; + } + if (!isQvecGood(getAllQvec(collision))) { + // selection based on QVector + return false; + } + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); + registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted + registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + } + return true; + } + + void processEMC(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + + for (const auto& collision : collisions) { + float cent = getCentrality(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + runNow = collision.runNumber(); + if (runNow != runBefore) { + initCCDB(collision); + runBefore = runNow; + } + auto photonsPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + for (const auto& photon : photonsPerCollision) { + if (emccuts.cfgEnableQA.value) { + registry.fill(HIST("clusterQA/hEClusterBefore"), photon.corrE()); // before cuts + registry.fill(HIST("clusterQA/hClusterEtaPhiBefore"), photon.phi(), photon.eta()); // before cuts + } + if (!(emcFlags.test(photon.globalIndex()))) { + continue; + } + if ((emccuts.useEMCal.value && !emccuts.useDCal.value && (!isEMCalRegion(photon.phi()))) || (!emccuts.useEMCal.value && emccuts.useDCal.value && (isEMCalRegion(photon.phi())))) { + continue; + } + if (cfgDistanceToEdge.value > 0 && (checkEtaPhi1D(photon.eta(), RecoDecay::constrainAngle(photon.phi())) >= cfgEMCalMapLevelSameEvent.value)) { + continue; + } + if (emccuts.cfgEnableQA.value) { + registry.fill(HIST("clusterQA/hEClusterAfter"), photon.corrE()); // accepted after cuts + registry.fill(HIST("clusterQA/hClusterEtaPhiAfter"), photon.phi(), photon.eta()); // after cuts + } + + auto [xQVec, yQVec] = getQvec(collision, qvecDetector); + + float phiCand = photon.phi(); + + float cosNPhi = std::cos(harmonic * phiCand); + float sinNPhi = std::sin(harmonic * phiCand); + float scalprodCand = cosNPhi * xQVec + sinNPhi * yQVec; + + if (correctionConfig.cfgApplySPresolution.value) { + scalprodCand = scalprodCand / h1SPResolution->GetBinContent(h1SPResolution->FindBin(cent + epsilon)); + } + registry.fill(HIST("EMCal/hSparsePhotonFlow"), photon.m02(), photon.corrPt(), cent, scalprodCand); + registry.fill(HIST("EMCal/hSparsePhoton"), photon.m02(), photon.corrPt(), cent); + } // end of loop over single cluster + } // end of loop over collisions + } // processEMC + PROCESS_SWITCH(TaskPhotonFlow, processEMC, "Process single EMCal clusters as function of M02", false); + + void processPCM(Colls const& collisions, PCMPhotons const& photons, aod::V0Legs const& legs) + { + if (photons.size() <= 0 || legs.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto ele = legs.begin(); + auto pos = legs.begin(); + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + for (const auto& collision : collisions) { + + if (!isFullEventSelected(collision, true)) { + continue; + } + + float cent = getCentrality(collision); + + runNow = collision.runNumber(); + if (runNow != runBefore) { + initCCDB(collision); + runBefore = runNow; + } + + auto photonsPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + for (const auto& photon : photonsPerCollision) { + if (!(v0flags.test(photon.globalIndex()))) { + continue; + } + ele.setCursor(photon.negTrackId()); + pos.setCursor(photon.posTrackId()); + + auto [xQVec, yQVec] = getQvec(collision, qvecDetector); + + float phiCand = photon.phi(); + + float cosNPhi = std::cos(harmonic * phiCand); + float sinNPhi = std::sin(harmonic * phiCand); + float scalprodCand = cosNPhi * xQVec + sinNPhi * yQVec; + + if (correctionConfig.cfgApplySPresolution.value) { + scalprodCand = scalprodCand / h1SPResolution->GetBinContent(h1SPResolution->FindBin(cent + epsilon)); + } + + float kappa = getV0Kappa(pos, ele); + registry.fill(HIST("PCM/hSparsePhotonFlow"), kappa, photon.corrPt(), cent, scalprodCand); + registry.fill(HIST("PCM/hSparsePhoton"), kappa, photon.corrPt(), cent); + } + } // end of loop over collisions + } + PROCESS_SWITCH(TaskPhotonFlow, processPCM, "Process PCM Pi0 candidates", true); + +}; // End struct TaskPhotonFlow + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx index a6a6375cb4c..230ad975aec 100644 --- a/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx +++ b/PWGEM/PhotonMeson/Tasks/taskPi0FlowEMC.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -76,19 +77,19 @@ using namespace o2::aod::pwgem::photon; enum QvecEstimator { FT0M = 0, FT0A = 1, - FT0C, - TPCPos, - TPCNeg, - TPCTot, - FV0A + FT0C = 2, + TPCPos = 3, + TPCNeg = 4, + TPCTot = 5, + FV0A = 6 }; enum CentralityEstimator { None = 0, CFT0A = 1, - CFT0C, - CFT0M, - NCentralityEstimators + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 }; enum Harmonics { @@ -121,10 +122,8 @@ struct TaskPi0FlowEMC { Configurable cfgEMCalMapLevelBackground{"cfgEMCalMapLevelBackground", 4, "Different levels of correction for the background, the smaller number includes the level of the higher number (4: none, 3: only inside EMCal, 2: remove edges, 1: exclude bad channels)"}; Configurable cfgEMCalMapLevelSameEvent{"cfgEMCalMapLevelSameEvent", 4, "Different levels of correction for the same event, the smaller number includes the level of the higher number (4: none, 3: only inside EMCal, 2: remove edges, 1: exclude bad channels)"}; Configurable cfgDistanceToEdge{"cfgDistanceToEdge", 1, "Distance to edge in cells required for rotated cluster to be accepted"}; - Configurable cfgDoM02{"cfgDoM02", false, "Flag to enable flow vs M02 for single photons"}; Configurable cfgDoPlaneQA{"cfgDoPlaneQA", false, "Flag to enable QA plots comparing in and out of plane"}; Configurable cfgMaxQVector{"cfgMaxQVector", 20.f, "Maximum allowed absolute QVector value."}; - Configurable cfgMaxAsymmetry{"cfgMaxAsymmetry", 0.1f, "Maximum allowed asymmetry for photon pairs used in calibration."}; // configurable axis ConfigurableAxis thnConfigAxisInvMass{"thnConfigAxisInvMass", {400, 0.0, 0.8}, "invariant mass axis for the neutral meson"}; @@ -174,6 +173,9 @@ struct TaskPi0FlowEMC { Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + Configurable useEMCal{"useEMCal", false, "flag to use EMCal clusters"}; + Configurable useDCal{"useDCal", false, "flag to use DCal clusters"}; + Configurable useCrosspairs{"useCrosspairs", true, "flag to allow pairing of EMCal with DCal clusters. If this is set, useEMCal and useDCal are ignored!"}; } emccuts; V0PhotonCut fV0PhotonCut; @@ -217,16 +219,20 @@ struct TaskPi0FlowEMC { Configurable minOpenAngle{"minOpenAngle", 0.0202, "apply min opening angle. Default value one EMCal cell"}; Configurable enableTanThetadPhi{"enableTanThetadPhi", false, "flag to turn cut opening angle in delta theta delta phi on/off"}; Configurable minTanThetadPhi{"minTanThetadPhi", 4., "apply min opening angle in delta theta delta phi to cut on late conversion"}; - Configurable maxEnergyAsymmetry{"maxEnergyAsymmetry", 1., "apply max energy asymmetry for meson candidate"}; Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; ConfigurableAxis thConfigAxisTanThetaPhi{"thConfigAxisTanThetaPhi", {180, -90.f, 90.f}, ""}; + Configurable cfgPairBkgMeson{"cfgPairBkgMeson", 0, "meson for the mass binning of the pair background QA THn (0: pi0, 1: eta)"}; + ConfigurableAxis thnConfigAxisPairBkgMassPi0{"thnConfigAxisPairBkgMassPi0", {VARIABLE_WIDTH, 0.0f, 0.07f, 0.10f, 0.17f, 0.30f, 0.50f}, "invariant mass axis (sidebands and peak) of the pair background QA THn for the pi0"}; + ConfigurableAxis thnConfigAxisPairBkgMassEta{"thnConfigAxisPairBkgMassEta", {VARIABLE_WIDTH, 0.30f, 0.45f, 0.50f, 0.60f, 0.70f, 0.80f}, "invariant mass axis (sidebands and peak) of the pair background QA THn for the eta"}; + ConfigurableAxis thnConfigAxisPairBkgPt{"thnConfigAxisPairBkgPt", {VARIABLE_WIDTH, 0.f, 1.f, 2.f, 3.f, 5.f, 8.f, 20.f}, "pT axis of the pair background QA THn"}; + ConfigurableAxis thnConfigAxisPairBkgCent{"thnConfigAxisPairBkgCent", {VARIABLE_WIDTH, 0.f, 10.f, 30.f, 50.f, 90.f}, "centrality axis of the pair background QA THn"}; } mesonConfig; struct : ConfigurableGroup { std::string prefix = "mixingConfig"; ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgCentBins{"cfgCentBins", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Mixing bins - centrality"}; - ConfigurableAxis cfgEPBins{"cfgEPBins", {8, o2::constants::math::PIHalf, o2::constants::math::PIHalf}, "Mixing bins - event plane angle"}; + ConfigurableAxis cfgEPBins{"cfgEPBins", {8, -o2::constants::math::PIHalf, o2::constants::math::PIHalf}, "Mixing bins - event plane angle"}; ConfigurableAxis cfgOccupancyBins{"cfgOccupancyBins", {VARIABLE_WIDTH, 0, 100, 500, 1000, 2000}, "Mixing bins - occupancy"}; Configurable cfgMixingDepth{"cfgMixingDepth", 2, "Mixing depth"}; } mixingConfig; @@ -235,44 +241,51 @@ struct TaskPi0FlowEMC { std::string prefix = "rotationConfig"; Configurable cfgDoRotation{"cfgDoRotation", false, "Flag to enable rotation background method."}; Configurable cfgDownsampling{"cfgDownsampling", 1, "Calculate rotation background only for every collision."}; - Configurable cfgRotAngle{"cfgRotAngle", std::move(const_cast(o2::constants::math::PIHalf)), "Angle used for the rotation method."}; + Configurable cfgRotAngle{"cfgRotAngle", static_cast(o2::constants::math::PIHalf), "Angle used for the rotation method."}; Configurable cfgUseWeights{"cfgUseWeights", false, "Flag to enable weights for rotation background method."}; } rotationConfig; struct : ConfigurableGroup { std::string prefix = "correctionConfig"; Configurable cfgSpresoPath{"cfgSpresoPath", "Users/m/mhemmer/EM/Flow/Resolution", "Path to SP resolution file"}; - Configurable cfgApplySPresolution{"cfgApplySPresolution", 0, "Apply resolution correction"}; - Configurable doEMCalCalib{"doEMCalCalib", 0, "Produce output for EMCal calibration"}; + Configurable cfgApplySPresolution{"cfgApplySPresolution", false, "Apply resolution correction"}; + Configurable doEMCalCalib{"doEMCalCalib", false, "Produce output for EMCal calibration"}; Configurable cfgEnableNonLin{"cfgEnableNonLin", false, "flag to turn extra non linear energy calibration on/off"}; + Configurable cfgEmcalEffRadius{"cfgEmcalEffRadius", 430.f, "effective EMCal radius (cm) used for mixed-event vertex-swap correction"}; + Configurable cfgCorrectMixedVtxEta{"cfgCorrectMixedVtxEta", true, "re-project cluster2 eta onto collision1's vertex in mixed event"}; } correctionConfig; SliceCache cache; EventPlaneHelper epHelper; - o2::framework::Service ccdb; + o2::framework::Service ccdb{}; int runNow = 0; int runBefore = -1; + static constexpr float MaxPhiEMCal = 3.9f; // exatly the middle between EMCal and DCal + static constexpr uint16_t MaxPhiEMCalUint = static_cast(39000u); // exatly the middle between EMCal and DCal but as uint16_t that is used for storing phi values in derived data + // Filter clusterFilter = aod::skimmedcluster::time >= emccuts.cfgEMCminTime && aod::skimmedcluster::time <= emccuts.cfgEMCmaxTime && aod::skimmedcluster::m02 >= emccuts.cfgEMCminM02 && aod::skimmedcluster::m02 <= emccuts.cfgEMCmaxM02 && aod::skimmedcluster::e >= emccuts.cfgEMCminE; Filter collisionFilter = (nabs(aod::collision::posZ) <= eventcuts.cfgZvtxMax) && (aod::evsel::ft0cOccupancyInTimeRange <= eventcuts.cfgFT0COccupancyMax) && (aod::evsel::ft0cOccupancyInTimeRange >= eventcuts.cfgFT0COccupancyMin); // using FilteredEMCalPhotons = soa::Filtered>; using EMCalPhotons = soa::Join; using PCMPhotons = soa::Join; - using FilteredCollsWithQvecs = soa::Filtered>; - using CollsWithQvecs = soa::Join; - using Colls = soa::Join; + using FilteredCollsWithQvecs = soa::Filtered>; + using Colls = soa::Join; + + Partition emcalPhotons = aod::mincluster::storedPhi < MaxPhiEMCalUint; + Partition dcalPhotons = aod::mincluster::storedPhi >= MaxPhiEMCalUint; static constexpr std::size_t NQVecEntries = 6; - PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; - PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; - o2::emcal::Geometry* emcalGeom; - o2::emcal::BadChannelMap* mBadChannels; + o2::emcal::Geometry* emcalGeom = nullptr; + o2::emcal::BadChannelMap* mBadChannels = nullptr; TH1D* h1SPResolution = nullptr; // Constants for eta and phi ranges for the look up table static constexpr double EtaMin = -0.75, etaMax = 0.75; @@ -281,7 +294,7 @@ struct TaskPi0FlowEMC { static constexpr double PhiMin = 1.35, phiMax = 5.75; static constexpr int NBinsPhi = 440; // (440 bins = 0.01 step size covering most regions) - std::array lookupTable1D; + std::array lookupTable1D{}; float epsilon = 1.e-8; // static constexpr @@ -409,18 +422,27 @@ struct TaskPi0FlowEMC { const AxisSpec thnAxisMixingCent{mixingConfig.cfgCentBins, "Centrality (%)"}; const AxisSpec thnAxisMixingEP{mixingConfig.cfgEPBins, Form("cos(%d#varphi)", harmonic.value)}; - registry.add("hSparsePi0Flow", " vs m_{inv} vs p_T vs cent for same event", HistType::kTProfile3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); - registry.add("hSparsePi0", "m_{inv} vs p_T vs cent for same event", HistType::kTH3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); + const AxisSpec thnQaAxisMinv{16, 0, 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; // bin witdh 0.05 GeV/c^2 + const AxisSpec thnQaAxisPt{10, 0, 20., "#it{p}_{T} (GeV/#it{c})"}; // bin width 2 GeV/c + const AxisSpec thnQaAxisDeltaEta{20, -1.6, 1.6, "|#Delta#eta|"}; // bin witdh 0.16 + const AxisSpec thnQaAxisDeltaPhi{24, 0, o2::constants::math::PI, "#Delta#varphi"}; // bin width 7.5 degrees in rad + const AxisSpec thnQaAxisCent{10, 0, 100, "Centrality (%)"}; // bin width 10% + + if (!doprocessM02) { + registry.add("hSparsePi0Flow", " vs m_{inv} vs p_T vs cent for same event", HistType::kTProfile3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); + registry.add("hSparsePi0", "m_{inv} vs p_T vs cent for same event", HistType::kTH3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); - registry.add("hSparseBkgMixFlow", " vs m_{inv} vs p_T vs cent for mixed event", HistType::kTProfile3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); - registry.add("hSparseBkgMix", "m_{inv} vs p_T vs cent for mixed event", HistType::kTH3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); + registry.add("hSparseBkgMixFlow", " vs m_{inv} vs p_T vs cent for mixed event", HistType::kTProfile3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); + registry.add("hSparseBkgMix", "m_{inv} vs p_T vs cent for mixed event", HistType::kTH3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); + + registry.add("h3DMixingCount", "THn Event Mixing QA", HistType::kTH3D, {thnAxisMixingVtx, thnAxisMixingCent, thnAxisMixingEP}); + } if (rotationConfig.cfgDoRotation.value) { registry.add("hSparseBkgRotFlow", " vs m_{inv} vs p_T vs cent for rotation background", HistType::kTProfile3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); registry.add("hSparseBkgRot", "m_{inv} vs p_T vs cent for rotation background", HistType::kTH3D, {thnAxisInvMass, thnAxisPt, thnAxisCent}); } - registry.add("h3DMixingCount", "THn Event Mixing QA", HistType::kTH3D, {thnAxisMixingVtx, thnAxisMixingCent, thnAxisMixingEP}); if (cfgDoPlaneQA.value) { registry.add("hSparsePi0FlowPlane", "THn for SP", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisPt, thnAxisCent, thnAxisCosDeltaPhi}); } @@ -458,6 +480,16 @@ struct TaskPi0FlowEMC { registry.add("mesonQA/hInvMassPtMixed", "Histo for inv pair mass vs pt for mixed event", HistType::kTH2D, {thnAxisInvMass, thnAxisPt}); registry.add("mesonQA/hTanThetaPhiMixed", "Histo for identification of conversion cluster for mixed event", HistType::kTH2D, {thnAxisInvMass, thAxisTanThetaPhi}); registry.add("mesonQA/hAlphaPtMixed", "Histo of meson asymmetry vs pT for mixed event", HistType::kTH2D, {thAxisAlpha, thnAxisPt}); + const bool isPCMEMC = doprocessEMCalPCMC.value || doprocessEMCalPCMMixed.value; + const AxisSpec thnAxisPairBkgMass{mesonConfig.cfgPairBkgMeson.value == 0 ? mesonConfig.thnConfigAxisPairBkgMassPi0 : mesonConfig.thnConfigAxisPairBkgMassEta, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPairBkgPt{mesonConfig.thnConfigAxisPairBkgPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPairBkgCent{mesonConfig.thnConfigAxisPairBkgCent, "Centrality (%)"}; + const AxisSpec thnAxisPairBkgAlpha = isPCMEMC ? AxisSpec{40, -1.f, 1.f, "#alpha = (#it{E}_{PCM} - #it{E}_{EMC}) / (#it{E}_{PCM} + #it{E}_{EMC})"} : AxisSpec{20, 0.f, 1.f, "#alpha = |#it{E}_{1} - #it{E}_{2}| / (#it{E}_{1} + #it{E}_{2})"}; + const AxisSpec thnAxisPairBkgDirection{36, -o2::constants::math::PIHalf, o2::constants::math::PIHalf, "atan2(#Delta#eta, #Delta#varphi) (rad)"}; + registry.add("mesonQA/hPairBkgSE", "#it{m}_{inv} vs #it{p}_{T} vs cent vs #alpha vs pair direction for same event", HistType::kTHnF, {thnAxisPairBkgMass, thnAxisPairBkgPt, thnAxisPairBkgCent, thnAxisPairBkgAlpha, thnAxisPairBkgDirection}); + registry.add("mesonQA/hPairBkgME", "#it{m}_{inv} vs #it{p}_{T} vs cent vs #alpha vs pair direction for mixed event", HistType::kTHnF, {thnAxisPairBkgMass, thnAxisPairBkgPt, thnAxisPairBkgCent, thnAxisPairBkgAlpha, thnAxisPairBkgDirection}); + registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for same event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); + registry.add("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME", "#it{m}_{inv} vs #it{p}_{T} vs #Delta#eta vs #Delta#varphi vs cent for mixed event", HistType::kTHnF, {thnQaAxisMinv, thnQaAxisPt, thnQaAxisDeltaEta, thnQaAxisDeltaPhi, thnQaAxisCent}); } if (correctionConfig.doEMCalCalib.value) { @@ -465,9 +497,9 @@ struct TaskPi0FlowEMC { registry.add("hSparseCalibBack", "THn for Calib background", HistType::kTHnSparseF, {thnAxisInvMass, thAxisEnergyCalib, thnAxisCent}); } - if (cfgDoM02.value) { - registry.add("p3DM02Flow", " vs M_{02} vs p_T vs cent", HistType::kTProfile3D, {thnAxisM02, thnAxisPt, thnAxisCent}); - registry.add("h3DSparsePi0", "M_{02} vs p_T vs cent", HistType::kTH3D, {thnAxisM02, thnAxisPt, thnAxisCent}); + if (doprocessM02) { + registry.add("hSparsePhotonFlow", " vs M_{02} vs p_T vs cent", HistType::kTProfile3D, {thnAxisM02, thnAxisPt, thnAxisCent}); + registry.add("hSparsePhoton", "M_{02} vs p_T vs cent", HistType::kTH3D, {thnAxisM02, thnAxisPt, thnAxisCent}); } ccdb->setURL(ccdbUrl); @@ -480,6 +512,13 @@ struct TaskPi0FlowEMC { }; // end init + /// \brief Check whether a photon (by its phi) falls in the EMCal or DCal acceptance + /// \param phi azimuthal angle of the photon + static bool isEMCalRegion(float phi) + { + return phi < MaxPhiEMCal; + } + /// Change radians to degree /// \param angle in radians /// \return angle in degree @@ -505,12 +544,56 @@ struct TaskPi0FlowEMC { template void fillThn(const float mass, const float pt, const float cent, const float sp) { - static constexpr std::string_view FlowHistTypes[3] = {"hSparsePi0Flow", "hSparseBkgRotFlow", "hSparseBkgMixFlow"}; - static constexpr std::string_view HistTypes[3] = {"hSparsePi0", "hSparseBkgRot", "hSparseBkgMix"}; + static constexpr std::array FlowHistTypes = {"hSparsePi0Flow", "hSparseBkgRotFlow", "hSparseBkgMixFlow"}; + static constexpr std::array HistTypes = {"hSparsePi0", "hSparseBkgRot", "hSparseBkgMix"}; registry.fill(HIST(FlowHistTypes[histType]), mass, pt, cent, sp); registry.fill(HIST(HistTypes[histType]), mass, pt, cent); } + /// \brief Fill the pair background QA THn (m_inv, pT, cent, alpha, direction) + /// The direction is folded such that dPhi is in [0, pi] and dEta keeps its sign, identical for same and mixed event. + /// \tparam isMixed fill the mixed instead of the same event histogram + /// \tparam isSignedAlpha use the signed asymmetry (E1 - E2) / (E1 + E2), for PCM-EMC photon1 has to be the PCM photon + /// \param photon1 first photon + /// \param photon2 second photon + /// \param meson pair of photon1 and photon2 + /// \param cent centrality of the (first) collision + template + void fillPairBkg(ROOT::Math::PtEtaPhiMVector const& photon1, ROOT::Math::PtEtaPhiMVector const& photon2, ROOT::Math::PtEtaPhiMVector const& meson, const float cent) + { + if (!mesonConfig.cfgEnableQA.value) { + return; + } + float alpha = (photon1.E() - photon2.E()) / (photon1.E() + photon2.E()); + if constexpr (!isSignedAlpha) { + alpha = std::fabs(alpha); + } + float dPhi = RecoDecay::constrainAngle(photon1.Phi() - photon2.Phi(), -o2::constants::math::PI); // wrap to [-pi, pi) + float dEta = photon1.Eta() - photon2.Eta(); + if (dPhi < 0.f) { // swap the labels: flip BOTH signs + dPhi = -dPhi; + dEta = -dEta; + } + const float direction = std::atan2(dEta, dPhi); // in [-pi/2, pi/2] since dPhi >= 0 + if constexpr (isMixed) { + registry.fill(HIST("mesonQA/hPairBkgME"), meson.M(), meson.Pt(), cent, alpha, direction); + } else { + registry.fill(HIST("mesonQA/hPairBkgSE"), meson.M(), meson.Pt(), cent, alpha, direction); + } + } + + /// \brief eta a cluster would have if its own vertex vzOld is swapped for vzNew, + /// assuming a nominal cylindrical EMCal surface at transverse radius emcalR (cm). + /// phi is untouched: transverse vertex spread is negligible next to emcalR. + /// \param etaOld old eta value + /// \param vzOld old primary vertex z position + /// \param vzNew new primary vertex z position + /// \param emcalR radius of the EMCal + static float correctEtaForVertexShift(float etaOld, float vzOld, float vzNew, float emcalR) + { + return std::asinh(std::sinh(etaOld) + (vzOld - vzNew) / emcalR); + } + /// Get the centrality /// \param collision is the collision with the centrality information template @@ -667,9 +750,8 @@ struct TaskPi0FlowEMC { int iRowLast = 24; if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_HALF) { iRowLast /= 2; // 2/3 sm case - } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_THIRD) { - iRowLast /= 3; // 1/3 sm case - } else if (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::DCAL_EXT) { + } else if ((emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::EMCAL_THIRD) || + (emcalGeom->GetSMType(iSupMod) == o2::emcal::EMCALSMType::DCAL_EXT)) { iRowLast /= 3; // 1/3 sm case } @@ -683,13 +765,12 @@ struct TaskPi0FlowEMC { return false; } - bool isCellMasked(int cellID) + template + bool isCellMasked(int cellID, TCollision const& collision) { bool masked = false; - if (mBadChannels) { - auto maskStatus = mBadChannels->getChannelStatus(cellID); - masked = (maskStatus != o2::emcal::BadChannelMap::MaskType_t::GOOD_CELL); - } + auto maskStatus = collision.badChannelMap().getChannelStatus(cellID); + masked = (maskStatus != o2::emcal::BadChannelMap::MaskType_t::GOOD_CELL); return masked; } @@ -698,8 +779,6 @@ struct TaskPi0FlowEMC { { // Load EMCal geometry emcalGeom = o2::emcal::Geometry::GetInstanceFromRunNumber(collision.runNumber()); - // Load Bad Channel map - mBadChannels = ccdb->getForTimeStamp("EMC/Calib/BadChannelMap", collision.timestamp()); lookupTable1D.fill(-1); double binWidthEta = (etaMax - EtaMin) / NBinsEta; double binWidthPhi = (phiMax - PhiMin) / NBinsPhi; @@ -719,7 +798,7 @@ struct TaskPi0FlowEMC { // Check conditions for the cell if (isTooCloseToEdge(cellID, 1)) { lookupTable1D[getIndex(iEta, iPhi)] = 2; // Edge - } else if (isCellMasked(cellID)) { + } else if (isCellMasked(cellID, collision)) { lookupTable1D[getIndex(iEta, iPhi)] = 1; // Bad } else { lookupTable1D[getIndex(iEta, iPhi)] = 0; // Good @@ -803,7 +882,7 @@ struct TaskPi0FlowEMC { if (mesonConfig.enableTanThetadPhi.value) { float dTheta = photon1.Theta() - photon3.Theta(); float dPhi = photon1.Phi() - photon3.Phi(); - if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { + if (mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { continue; } } @@ -825,7 +904,7 @@ struct TaskPi0FlowEMC { if (mesonConfig.enableTanThetadPhi.value) { float dTheta = photon2.Theta() - photon3.Theta(); float dPhi = photon2.Phi() - photon3.Phi(); - if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { + if (mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { continue; } } @@ -835,7 +914,6 @@ struct TaskPi0FlowEMC { } // end of loop over third photon } } - return; } /// \brief Calculate background using rotation background method @@ -910,7 +988,7 @@ struct TaskPi0FlowEMC { if (mesonConfig.enableTanThetadPhi.value) { float dTheta = photonPCM.Theta() - photon3.Theta(); float dPhi = photonPCM.Phi() - photon3.Phi(); - if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { + if (mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { continue; } } @@ -942,7 +1020,7 @@ struct TaskPi0FlowEMC { if (mesonConfig.enableTanThetadPhi.value) { float dTheta = photonEMC.Theta() - photon3.Theta(); float dPhi = photonEMC.Phi() - photon3.Phi(); - if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { + if (mesonConfig.minTanThetadPhi <= std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { continue; } } @@ -951,7 +1029,6 @@ struct TaskPi0FlowEMC { } } // end of loop over PCM photons } // if(iCellIDphotonEMC > -1) - return; } /// Compute the scalar product @@ -982,7 +1059,6 @@ struct TaskPi0FlowEMC { } fillThn(massCand, ptCand, cent, scalprodCand); - return; } /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is @@ -1042,6 +1118,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); @@ -1050,7 +1132,7 @@ struct TaskPi0FlowEMC { continue; } if (rotationConfig.cfgDoRotation.value && nColl % rotationConfig.cfgDownsampling == 0) { - rotationBackground(vMeson, v1, v2, photons1, g1.globalIndex(), g2.globalIndex(), collision); + rotationBackground(vMeson, v1, v2, photons1, g1.globalIndex(), g2.globalIndex(), collision); } if (thnConfigAxisInvMass.value[1] > vMeson.M() || thnConfigAxisInvMass.value.back() < vMeson.M()) { registry.fill(HIST("hMesonCuts"), 3); @@ -1064,18 +1146,20 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); continue; } registry.fill(HIST("hMesonCuts"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(collision)); runFlowAnalysis<0>(collision, vMeson); } } // Pi0 from EMCal - void processEMCal(CollsWithQvecs const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) + void processEMCal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) { if (clusters.size() <= 0) { LOG(info) << "Skipping DF because there are not photons!"; @@ -1085,7 +1169,6 @@ struct TaskPi0FlowEMC { fEMCCut.AreSelectedRunning(flags, clusters, matchedPrims, matchedSeconds, ®istry); for (const auto& collision : collisions) { - if (!isFullEventSelected(collision, true)) { continue; } @@ -1096,6 +1179,8 @@ struct TaskPi0FlowEMC { } auto photonsPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + auto emcalPhotonsPerCollision = emcalPhotons.sliceBy(perCollisionEMC, collision.globalIndex()); + auto dcalPhotonsPerCollision = dcalPhotons.sliceBy(perCollisionEMC, collision.globalIndex()); if (emccuts.cfgEnableQA.value) { for (const auto& photon : photonsPerCollision) { @@ -1104,14 +1189,22 @@ struct TaskPi0FlowEMC { if (!(fEMCCut.IsSelected(photon))) { continue; } - if (cfgDistanceToEdge.value && (checkEtaPhi1D(photon.eta(), RecoDecay::constrainAngle(photon.phi())) >= cfgEMCalMapLevelSameEvent.value)) { + if (cfgDistanceToEdge.value > 0 && (checkEtaPhi1D(photon.eta(), RecoDecay::constrainAngle(photon.phi())) >= cfgEMCalMapLevelSameEvent.value)) { continue; } registry.fill(HIST("clusterQA/hEClusterAfter"), photon.corrE()); // accepted after cuts registry.fill(HIST("clusterQA/hClusterEtaPhiAfter"), photon.phi(), photon.eta()); // after cuts } } - runPairingLoop(collision, photonsPerCollision, photonsPerCollision, flags, flags); + if (emccuts.useEMCal.value && !emccuts.useCrosspairs.value) { + runPairingLoop(collision, emcalPhotonsPerCollision, emcalPhotonsPerCollision, flags, flags); + } + if (emccuts.useDCal.value && !emccuts.useCrosspairs.value) { + runPairingLoop(collision, dcalPhotonsPerCollision, dcalPhotonsPerCollision, flags, flags); + } + if (emccuts.useCrosspairs.value) { + runPairingLoop(collision, photonsPerCollision, photonsPerCollision, flags, flags); + } if (rotationConfig.cfgDoRotation.value) { if (nColl % rotationConfig.cfgDownsampling == 0) { nColl = 1; // reset counter @@ -1134,7 +1227,7 @@ struct TaskPi0FlowEMC { fEMCCut.AreSelectedRunning(flags, clusters, matchedPrims, matchedSeconds); using BinningType = ColumnBinningPolicy>; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto clustersTuple = std::make_tuple(clusters); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clustersTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -1144,7 +1237,7 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange() && c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange() && c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { // occupancy selection continue; } @@ -1166,8 +1259,15 @@ struct TaskPi0FlowEMC { if (!(flags.test(g1.globalIndex())) || !(flags.test(g2.globalIndex()))) { continue; } + if (emccuts.useEMCal.value && !emccuts.useCrosspairs.value && (!isEMCalRegion(g1.phi()) || !isEMCalRegion(g2.phi()))) { + continue; + } + if (emccuts.useDCal.value && !emccuts.useCrosspairs.value && (isEMCalRegion(g1.phi()) || isEMCalRegion(g2.phi()))) { + continue; + } + // Cut edge clusters away, similar to rotation method to ensure same acceptance is used - if (cfgDistanceToEdge.value) { + if (cfgDistanceToEdge.value > 0) { if (checkEtaPhi1D(g1.eta(), RecoDecay::constrainAngle(g1.phi())) >= cfgEMCalMapLevelBackground.value) { continue; } @@ -1176,11 +1276,23 @@ struct TaskPi0FlowEMC { } } ROOT::Math::PtEtaPhiMVector v1(g1.corrPt(), g1.eta(), g1.phi(), 0.); - ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); + + // changing the eta position of cluster 2 from collision 2 to match the z-vertex position of collision 1 + float eta2 = g2.eta(); + if (correctionConfig.cfgCorrectMixedVtxEta.value) { + eta2 = correctEtaForVertexShift(g2.eta(), c2.posZ(), c1.posZ(), correctionConfig.cfgEmcalEffRadius.value); + } + ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), eta2, g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1200,12 +1312,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); continue; } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(c1)); runFlowAnalysis<2>(c1, vMeson); } } @@ -1213,7 +1327,7 @@ struct TaskPi0FlowEMC { PROCESS_SWITCH(TaskPi0FlowEMC, processEMCalMixed, "Process EMCal Pi0 mixed event candidates", false); // PCM-EMCal same event - void processEMCalPCMC(CollsWithQvecs const& collisions, EMCalPhotons const& clusters, PCMPhotons const& photons, aod::V0Legs const&, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) + void processEMCalPCMC(Colls const& collisions, EMCalPhotons const& clusters, PCMPhotons const& photons, aod::V0Legs const&, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) { if (clusters.size() <= 0 && photons.size() <= 0) { LOG(info) << "Skipping DF because there are not photons!"; @@ -1258,7 +1372,7 @@ struct TaskPi0FlowEMC { } // Cut edge clusters away, similar to rotation method to ensure same acceptance is used - if (cfgDistanceToEdge.value) { + if (cfgDistanceToEdge.value > 0) { if (checkEtaPhi1D(g1.eta(), RecoDecay::constrainAngle(g1.phi())) >= cfgEMCalMapLevelSameEvent.value) { continue; } @@ -1272,6 +1386,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); if (openingAngle <= mesonConfig.minOpenAngle) { @@ -1293,11 +1413,13 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhi"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCuts"), 5); continue; } + fillPairBkg(v2, v1, vMeson, getCentrality(collision)); // v2 is the PCM photon runFlowAnalysis<0>(collision, vMeson); } } @@ -1305,7 +1427,7 @@ struct TaskPi0FlowEMC { PROCESS_SWITCH(TaskPi0FlowEMC, processEMCalPCMC, "Process neutral meson flow using PCM-EMC same event", false); // PCM-EMCal mixed event - void processEMCalPCMMixed(CollsWithQvecs const& collisions, EMCalPhotons const& clusters, PCMPhotons const& pcmPhotons, aod::V0Legs const&, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) + void processEMCalPCMMixed(Colls const& collisions, EMCalPhotons const& clusters, PCMPhotons const& pcmPhotons, aod::V0Legs const&, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) { if (clusters.size() <= 0 && pcmPhotons.size() <= 0) { LOG(info) << "Skipping DF because there are not photons!"; @@ -1313,11 +1435,11 @@ struct TaskPi0FlowEMC { } using BinningTypeMixed = ColumnBinningPolicy>; - BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningTypeMixed binningOnPositions{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto associatedTables = std::make_tuple(clusters, pcmPhotons); - Pair pairPCMEMC{binningOnPositions, mixingConfig.cfgMixingDepth, -1, collisions, associatedTables, &cache}; // indicates that mixingConfig.cfgMixingDepth events should be mixed and under/overflow (-1) to be ignored + Pair pairPCMEMC{binningOnPositions, mixingConfig.cfgMixingDepth, -1, collisions, associatedTables, &cache}; // indicates that mixingConfig.cfgMixingDepth events should be mixed and under/overflow (-1) to be ignored EMBitFlags emcFlags(clusters.size()); if (clusters.size() > 0) { @@ -1334,7 +1456,7 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange() && c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange() && c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { // occupancy selection continue; } @@ -1342,6 +1464,10 @@ struct TaskPi0FlowEMC { // event selection continue; } + if (!isQvecGood(getAllQvec(c1)) || !isQvecGood(getAllQvec(c2))) { + // selection based on QVector + continue; + } runNow = c1.runNumber(); if (runNow != runBefore) { initCCDB(c1); @@ -1353,7 +1479,7 @@ struct TaskPi0FlowEMC { continue; } // Cut edge clusters away, similar to rotation method to ensure same acceptance is used - if (cfgDistanceToEdge.value) { + if (cfgDistanceToEdge.value > 0) { if (checkEtaPhi1D(g1.eta(), RecoDecay::constrainAngle(g1.phi())) >= cfgEMCalMapLevelBackground.value) { continue; } @@ -1364,6 +1490,12 @@ struct TaskPi0FlowEMC { float dTheta = v1.Theta() - v2.Theta(); float dPhi = v1.Phi() - v2.Phi(); + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1383,12 +1515,14 @@ struct TaskPi0FlowEMC { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hTanThetaPhiMixed"), vMeson.M(), getAngleDegree(std::atan(dTheta / dPhi))); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } if (mesonConfig.enableTanThetadPhi.value && mesonConfig.minTanThetadPhi > std::fabs(getAngleDegree(std::atan(dTheta / dPhi)))) { registry.fill(HIST("hMesonCutsMixed"), 5); continue; } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v2, v1, vMeson, getCentrality(c1)); // v2 is the PCM photon runFlowAnalysis<2>(c1, vMeson); } } @@ -1396,7 +1530,7 @@ struct TaskPi0FlowEMC { PROCESS_SWITCH(TaskPi0FlowEMC, processEMCalPCMMixed, "Process neutral meson flow using PCM-EMC mixed event", false); // Pi0 from EMCal - void processM02(CollsWithQvecs const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) + void processM02(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds) { if (clusters.size() <= 0) { LOG(info) << "Skipping DF because there are not photons!"; @@ -1406,22 +1540,8 @@ struct TaskPi0FlowEMC { fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); for (const auto& collision : collisions) { - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); - if (!(fEMEventCut.IsSelected(collision))) { - // general event selection - continue; - } - if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { - // occupancy selection - continue; - } float cent = getCentrality(collision); - if (cent < eventcuts.cfgMinCent || cent > eventcuts.cfgMaxCent) { - // event selection - continue; - } - if (!isQvecGood(getAllQvec(collision))) { - // selection based on QVector + if (!isFullEventSelected(collision, true)) { continue; } runNow = collision.runNumber(); @@ -1429,27 +1549,22 @@ struct TaskPi0FlowEMC { initCCDB(collision); runBefore = runNow; } - o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); - registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted - registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted auto photonsPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); for (const auto& photon : photonsPerCollision) { if (emccuts.cfgEnableQA.value) { - registry.fill(HIST("clusterQA/hEClusterBefore"), photon.e()); // before cuts + registry.fill(HIST("clusterQA/hEClusterBefore"), photon.corrE()); // before cuts registry.fill(HIST("clusterQA/hClusterEtaPhiBefore"), photon.phi(), photon.eta()); // before cuts } - auto matchedPrimsPerCluster = matchedPrims.sliceBy(perEMCClusterMT, photon.globalIndex()); - auto matchedSecondsPerCluster = matchedSeconds.sliceBy(perEMCClusterMS, photon.globalIndex()); if (!(emcFlags.test(photon.globalIndex()))) { continue; } - if (cfgDistanceToEdge.value && (checkEtaPhi1D(photon.eta(), RecoDecay::constrainAngle(photon.phi())) >= cfgEMCalMapLevelSameEvent.value)) { + if (cfgDistanceToEdge.value > 0 && (checkEtaPhi1D(photon.eta(), RecoDecay::constrainAngle(photon.phi())) >= cfgEMCalMapLevelSameEvent.value)) { continue; } if (emccuts.cfgEnableQA.value) { - registry.fill(HIST("clusterQA/hEClusterAfter"), photon.e()); // accepted after cuts + registry.fill(HIST("clusterQA/hEClusterAfter"), photon.corrE()); // accepted after cuts registry.fill(HIST("clusterQA/hClusterEtaPhiAfter"), photon.phi(), photon.eta()); // after cuts } @@ -1464,18 +1579,15 @@ struct TaskPi0FlowEMC { if (correctionConfig.cfgApplySPresolution.value) { scalprodCand = scalprodCand / h1SPResolution->GetBinContent(h1SPResolution->FindBin(cent + epsilon)); } - if (cfgDoM02.value) { - registry.fill(HIST("p3DM02Flow"), photon.m02(), photon.pt(), cent, scalprodCand); - registry.fill(HIST("h3DSparsePi0"), photon.m02(), photon.pt(), cent); - } - return; + registry.fill(HIST("hSparsePhotonFlow"), photon.m02(), photon.corrPt(), cent, scalprodCand); + registry.fill(HIST("hSparsePhoton"), photon.m02(), photon.corrPt(), cent); } // end of loop over single cluster } // end of loop over collisions } // processM02 PROCESS_SWITCH(TaskPi0FlowEMC, processM02, "Process single EMCal clusters as function of M02", false); // Pi0 from EMCal - void processPCM(CollsWithQvecs const& collisions, PCMPhotons const& photons, aod::V0Legs const&) + void processPCM(Colls const& collisions, PCMPhotons const& photons, aod::V0Legs const&) { if (photons.size() <= 0) { LOG(info) << "Skipping DF because there are not photons!"; @@ -1500,10 +1612,16 @@ struct TaskPi0FlowEMC { continue; } - ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); - ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v1(g1.corrPt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCuts"), 1); @@ -1522,8 +1640,10 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPt"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentSE"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(collision)); } registry.fill(HIST("hMesonCuts"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(collision)); runFlowAnalysis<0>(collision, vMeson); } } // end of loop over collisions @@ -1540,7 +1660,7 @@ struct TaskPi0FlowEMC { } using BinningType = ColumnBinningPolicy>; - BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}, true}; + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins, mixingConfig.cfgEPBins}}; auto pcmPhotonTuple = std::make_tuple(pcmPhotons); SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, pcmPhotonTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -1553,7 +1673,7 @@ struct TaskPi0FlowEMC { // general event selection continue; } - if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange() && c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange() && c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + if (!(eventcuts.cfgFT0COccupancyMin <= c1.ft0cOccupancyInTimeRange()) || !(c1.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax) || !(eventcuts.cfgFT0COccupancyMin <= c2.ft0cOccupancyInTimeRange()) || !(c2.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { // occupancy selection continue; } @@ -1575,6 +1695,12 @@ struct TaskPi0FlowEMC { ROOT::Math::PtEtaPhiMVector v2(g2.corrPt(), g2.eta(), g2.phi(), 0.); ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float dPhiWrapped = RecoDecay::constrainAngle(v1.Phi() - v2.Phi(), -o2::constants::math::PI); // wrap to [-π, π) + float dEta = v1.Eta() - v2.Eta(); + if (dPhiWrapped < 0.) { // swap the labels: flip BOTH signs + dPhiWrapped = -dPhiWrapped; + dEta = -dEta; + } float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); registry.fill(HIST("hMesonCutsMixed"), 1); @@ -1593,8 +1719,10 @@ struct TaskPi0FlowEMC { if (mesonConfig.cfgEnableQA.value) { registry.fill(HIST("mesonQA/hInvMassPtMixed"), vMeson.M(), vMeson.Pt()); registry.fill(HIST("mesonQA/hAlphaPtMixed"), (v1.E() - v2.E()) / (v1.E() + v2.E()), vMeson.Pt()); + registry.fill(HIST("mesonQA/hDeltaEtaDeltaPhiMinvPtCentME"), vMeson.M(), vMeson.Pt(), dEta, dPhiWrapped, getCentrality(c1)); } registry.fill(HIST("hMesonCutsMixed"), 6); + fillPairBkg(v1, v2, vMeson, getCentrality(c1)); runFlowAnalysis<2>(c1, vMeson); } } @@ -1603,7 +1731,7 @@ struct TaskPi0FlowEMC { }; // End struct TaskPi0FlowEMC -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/testTaskEmc.cxx b/PWGEM/PhotonMeson/Tasks/testTaskEmc.cxx index c10b30fc3a6..ad2b5594c15 100644 --- a/PWGEM/PhotonMeson/Tasks/testTaskEmc.cxx +++ b/PWGEM/PhotonMeson/Tasks/testTaskEmc.cxx @@ -172,7 +172,7 @@ struct TestTaskEmc { PROCESS_SWITCH(TestTaskEmc, processEMCalCalib, "Process EMCal calibration same event", true); }; // End struct TestTaskEmc -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx new file mode 100644 index 00000000000..348f54a7868 --- /dev/null +++ b/PWGEM/PhotonMeson/Tasks/twoPhotonMcTask.cxx @@ -0,0 +1,1574 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file twoPhotonMcTask.cxx +/// \brief Analysis task to obtain the invariant mass distributions for different MC truth classes +/// \author M. Hemmer, marvin.hemmer@cern.ch + +#include "PWGEM/Dilepton/Utils/MCUtilities.h" +#include "PWGEM/PhotonMeson/Core/EMBitFlags.h" +#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" +#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h" +#include "PWGEM/PhotonMeson/Core/V0PhotonCut.h" +#include "PWGEM/PhotonMeson/DataModel/EventTables.h" +#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h" +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" +#include "PWGEM/PhotonMeson/Utils/EventHistograms.h" +#include "PWGEM/PhotonMeson/Utils/MCUtilities.h" + +#include "Common/DataModel/Centrality.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::aod::pwgem::photon; +using namespace o2::aod::pwgem::photonmeson::utils::mcutil; + +static constexpr int kMaxChainLength = 16; +using Chain = std::array; // filled with -1 after the last ancestor + +/// \brief MC truth of a single photon candidate, ordered from best to worst such that std::max gives the worse flag of a pair +enum class PhotonTruth : uint8_t { + kTruePhoton = 0, // PCM: both legs from the same photon, EMC: leading contributor is a photon (or e+-, converted in front of the EMCal) + kNonPhoton, // PCM: both legs have the same direct mother which is not a photon (K0S, Lambda, pi0 Dalitz), EMC: leading contributor is not a photon + kFake, // PCM: leg without MC label or legs without common direct mother, EMC: cluster without MC label + kNTruth +}; + +/// \brief Class of the closest common ancestor of the two photon candidates of a pair +enum class PairBucket : uint8_t { + kNone = 0, // no common ancestor + kSameParticle, // one particle seen twice: the common ancestor is one of the two candidates themselves (EMC conversion split, Dalitz), + // or a photon/electron whose electromagnetic shower made both candidates (e.g. bremsstrahlung of both conversion legs) + kSignalPi0, // pi0 is the direct mother of both + kSignalEta, // eta is the direct mother of both + kCrossPi0, // pi0 further up in the chain: the pi0 has no hadronic decays, so these are pi0 decays with an EM interaction in between (conversion, bremsstrahlung, shower in front of the EMCal) + kCrossEta, // eta further up in the chain: hadronic decays (eta -> 3 pi0, photons of different pi0) and eta decays with an EM interaction in between + kCrossOmega, // omega + kCrossK0S, // K0S + kCrossEtaPrime, // eta' + kCrossOtherHadron, // any other hadron (including the rare Sigma0) + kPartonString, // quark, gluon, diquark or string/cluster: jet-like + kNBuckets +}; + +/// \brief Origin of a photon candidate for the pi0 prefilter study: first particle above the EM steps (conversion, bremsstrahlung, shower) +enum class PhotonOrigin : uint8_t { + kFromPi0 = 0, // incl. photons of pi0 from eta -> 3 pi0 + kFromEta, + kElse +}; + +/// \brief Class of a photon pair for the pi0 prefilter study +enum class PrefilterPairClass : uint8_t { + kSameParticle = 0, // one particle seen twice (same particle bucket), never counted as pi0/eta pair + kSamePi0, // both from the same pi0: pi0 signal + kTwoPi0, // both from pi0, but from two different pi0 + kSameEta, // both from the same eta: eta signal + kTwoEta, // both from eta, but from two different eta + kPi0Eta, + kPi0Else, + kEtaElse, + kBothElse, + kNClasses +}; + +/// \brief What the leading contributor of the EMC cluster is in a PCM-EMC same particle pair +enum class SameParticleOrigin : uint8_t { + kLegElectron = 0, // the e+- that was reconstructed as one of the V0 legs, should have been removed by track matching + kBremsFromLeg, // a photon radiated by one of the V0 leg e+-, cannot be removed by track matching + kOtherElectron, // any other e+- + kOther, // anything else + kNOrigins +}; + +/// \brief Why the V0 leg did not veto the EMC cluster, from the tracks matched to the cluster +enum class LegMatchStatus : uint8_t { + kNoMatchStored = 0, // no matched track stored for this cluster at all + kNoCompatibleMatch, // matched tracks stored, but none with the momentum of the leg + kOutsideWindow, // leg found, but outside the track matching window of the analysis + kInsideWindowEoverP, // leg found inside the window, but E/p above the cut (primaries only) + kInsideWindow, // leg found inside the window: the cluster should have been vetoed + kNotGlobalTrack, // primaries only: the leg is not a global track, so it is no candidate of the primary matching + kNStatus +}; + +/// \brief Species of the closest common ancestor of an "other hadron" pair +enum class OtherHadronSpecies : uint8_t { + kChargedPion = 0, + kChargedKaon, + kK0L, + kRho, + kKstar, + kPhi, + kA1, // a1(1260) + kExcitedK, // K1(1270), K1(1400), K0*(1430), K2*(1430) + kOpenCharm, // D, D*, Ds, ... (one c quark) + kCharmonium, // J/psi, psi(2S), chi_c, ... + kOpenBeauty, // B, B*, Bs, Bc, ... (one b quark) + kBottomonium, // Upsilon, chi_b, ... + kOtherMeson, // incl. f0, f2, b, h resonances + kProton, + kNeutron, + kDelta, + kLambda, + kSigma, + kXi, + kOtherBaryon, + kOther, + kNSpecies +}; + +/// \brief How the particles directly below the common ancestor were produced, i.e. how the ancestor split into the two branches. +/// Ordered such that std::max of the two branches gives the material interaction if one branch comes from it. +enum class SplitProduction : uint8_t { + kGeneratorDecay = 0, // produced by the event generator: decay in the generator (resonances, Sigma0, ...) + kTransportDecay, // produced by the transport code in a decay (weak decays of K+-, K0L, Lambda, Xi, ...) + kHadronicInteraction, // produced by the transport code in a hadronic interaction in the detector material + kOtherProcess, // produced by the transport code in any other process + kNProduction +}; + +enum CentralityEstimator { + None = 0, + CFT0A = 1, + CFT0C = 2, + CFT0M = 3, + NCentralityEstimators = 4 +}; + +struct TwoPhotonMcTask { + // configurable axis + ConfigurableAxis thnConfigAxisInvMass{"thnConfigAxisInvMass", {400, 0.0, 0.8}, "invariant mass axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {100, 0., 20.}, "pT axis for the neutral meson"}; + ConfigurableAxis thnConfigAxisERelative{"thnConfigAxisERelative", {600, -1., 5.}, "(E rec - E true) / E true axis"}; + ConfigurableAxis thnConfigAxisPRelative{"thnConfigAxisPRelative", {300, -1., 2.}, "(P rec - P true) / P true axis"}; + ConfigurableAxis thnConfigAxisEtaRelative{"thnConfigAxisEtaRelative", {300, -1., 2.}, "(eta rec - eta true) / eta true axis"}; + ConfigurableAxis thnConfigAxisPhiRelative{"thnConfigAxisPhiRelative", {300, -1., 2.}, "(phi rec - phi true) / phi true axis"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {10, 0., 100.}, "centrality axis for the current event"}; + ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"}; + Configurable useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."}; + + EMPhotonEventCut fEMEventCut; + struct : ConfigurableGroup { + std::string prefix = "eventcuts"; + Configurable cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"}; + Configurable cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"}; + Configurable cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"}; + Configurable cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"}; + Configurable cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV. + Configurable cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"}; + Configurable cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"}; + Configurable cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"}; + Configurable cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"}; + Configurable cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"}; + Configurable cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"}; + Configurable cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"}; + Configurable onlyKeepWeightedEvents{"onlyKeepWeightedEvents", false, "flag to keep only weighted events (for JJ MCs) and remove all MB events (with weight = 1)"}; + } eventcuts; + + EMCPhotonCut fEMCCut; + struct : ConfigurableGroup { + std::string prefix = "emccuts"; + Configurable clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"}; + Configurable cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"}; + Configurable cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"}; + Configurable cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"}; + Configurable cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"}; + Configurable cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"}; + Configurable cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"}; + Configurable> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"}; + Configurable cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"}; + Configurable cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "FLag to use the EMCal exotic cluster cut"}; + Configurable cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"}; + Configurable emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + } emccuts; + + V0PhotonCut fV0PhotonCut; + struct : o2::framework::ConfigurableGroup { + std::string prefix = "PCMcuts"; + o2::framework::Configurable requireV0WithITSTPC{"requireV0WithITSTPC", false, "flag to enforce V0s have ITS and TPC"}; + o2::framework::Configurable requireV0WithITSOnly{"requireV0WithITSOnly", false, "flag to select V0s with ITSonly tracks"}; + o2::framework::Configurable requireV0WithTPCOnly{"requireV0WithTPCOnly", false, "flag to select V0s with TPConly tracks"}; + o2::framework::Configurable minPtV0{"minPtV0", 0.1, "min pT for v0 photons at PV"}; + o2::framework::Configurable maxPtV0{"maxPtV0", 1e+10, "max pT for v0 photons at PV"}; + o2::framework::Configurable minEtaV0{"minEtaV0", -0.8, "min eta for v0 photons at PV"}; + o2::framework::Configurable maxEtaV0{"maxEtaV0", 0.8, "max eta for v0 photons at PV"}; + o2::framework::Configurable minRV0{"minRV0", 4.0, "min v0 radius"}; + o2::framework::Configurable maxRV0{"maxRV0", 90.0, "max v0 radius"}; + o2::framework::Configurable maxAlphaAP{"maxAlphaAP", 0.95, "max alpha for AP cut"}; + o2::framework::Configurable maxQtAP{"maxQtAP", 0.01, "max qT for AP cut"}; + o2::framework::Configurable minCosPA{"minCosPA", 0.999, "min V0 CosPA"}; + o2::framework::Configurable maxPCA{"maxPCA", 1.5, "max distance btween 2 legs"}; + o2::framework::Configurable maxChi2KF{"maxChi2KF", 1.e+10f, "max chi2/ndf with KF"}; + o2::framework::Configurable rejectV0onITSib{"rejectV0onITSib", true, "flag to reject V0s on ITSib"}; + o2::framework::Configurable applyPrefilter{"applyPrefilter", false, "flag to apply prefilter to V0"}; + + o2::framework::Configurable minNClusterTPC{"minNClusterTPC", 0, "min NCluster TPC"}; + o2::framework::Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 40, "min ncrossed rows in TPC"}; + o2::framework::Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "min fraction of crossed rows over findable clusters in TPC"}; + o2::framework::Configurable maxFracSharedClustersTPC{"maxFracSharedClustersTPC", 999.f, "max fraction of shared clusters in TPC"}; + o2::framework::Configurable minNClusterITS{"minNClusterITS", 0, "min NCluster ITS"}; + o2::framework::Configurable minMeanClusterSizeITSob{"minMeanClusterSizeITSob", 0.f, "min ITSob"}; + o2::framework::Configurable maxMeanClusterSizeITSob{"maxMeanClusterSizeITSob", 16.f, "max ITSob"}; + o2::framework::Configurable macChi2TPC{"macChi2TPC", 4.f, "max chi2/NclsTPC"}; + o2::framework::Configurable macChi2ITS{"macChi2ITS", 36.f, "max chi2/NclsITS"}; + o2::framework::Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -3.0f, "min. TPC n sigma for electron"}; + o2::framework::Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +3.0f, "max. TPC n sigma for electron"}; + o2::framework::Configurable disableITSOnly{"disableITSOnly", false, "flag to disable ITSonly tracks"}; + o2::framework::Configurable disableTPCOnly{"disableTPCOnly", false, "flag to disable TPConly tracks"}; + o2::framework::Configurable doQA{"doQA", false, "flag to set QA flag."}; + } pcmcuts; + + struct : ConfigurableGroup { + std::string prefix = "mesonConfig"; + Configurable minOpenAngle{"minOpenAngle", 0.0202, "apply min opening angle. Default value one EMCal cell"}; + Configurable cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"}; + Configurable prefilterMassMin{"prefilterMassMin", 0.105f, "pi0 prefilter study: lower edge of the pi0 mass window (GeV/c2)"}; + Configurable prefilterMassMax{"prefilterMassMax", 0.165f, "pi0 prefilter study: upper edge of the pi0 mass window (GeV/c2)"}; + Configurable prefilterAllPairTypes{"prefilterAllPairTypes", false, "pi0 prefilter study, processPcmEmcal: also tag with PCM-PCM and EMC-EMC pairs and fill hPrefilterAll (for PCM-PCM / EMC-EMC use processPcmPrefilterAll / processEmcalPrefilterAll)"}; + ConfigurableAxis thnConfigAxisPrefilterMass{"thnConfigAxisPrefilterMass", {200, 0., 0.8}, "pi0 prefilter study: invariant mass axis"}; + ConfigurableAxis thnConfigAxisPrefilterPt{"thnConfigAxisPrefilterPt", {VARIABLE_WIDTH, 0., 1., 1.5, 2., 2.5, 3., 4., 5., 6., 8., 10., 12., 15., 20.}, "pi0 prefilter study: pT axis"}; + } mesonConfig; + + // same mixing as taskPi0FlowEMC, but without event plane bins: the derived MC has no Q vectors + struct : ConfigurableGroup { + std::string prefix = "mixingConfig"; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis cfgCentBins{"cfgCentBins", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.f}, "Mixing bins - centrality"}; + Configurable cfgMixingDepth{"cfgMixingDepth", 2, "Mixing depth"}; + Configurable cfgCorrectMixedVtxEta{"cfgCorrectMixedVtxEta", true, "EMC-EMC: re-project the eta of cluster 2 onto the vertex of collision 1 in the mixed event"}; + Configurable cfgEmcalEffRadius{"cfgEmcalEffRadius", 430.f, "effective EMCal radius (cm) used for the mixed event vertex correction"}; + } mixingConfig; + + using BinningType = ColumnBinningPolicy; + + SliceCache cache; + + using EMCalPhotons = soa::Join; + using PcmPhotons = soa::Join; + + using PcmMcLegs = soa::Join; + + using Colls = soa::Join; + + using McColls = o2::soa::Join; + using McParticles = EMMCParticles; + + PresliceOptional perCollisionEMC = o2::aod::emccluster::pmeventId; + PresliceOptional perCollisionPCM = aod::v0photonkf::pmeventId; + PresliceOptional perEMCClusterMT = o2::aod::mintm::minClusterId; + PresliceOptional perEMCClusterMS = o2::aod::mintm::minClusterId; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + + int mRunNumber{-1}; + float dBz{0.f}; + + static constexpr int PhotonPCM = 0; + static constexpr int PhotonEMC = 1; + static constexpr int PairPCMPCM = 0; + static constexpr int PairEMCEMC = 1; + static constexpr int PairPCMEMC = 2; + + // ancestor chains and truth flags, filled once per DF, index = globalIndex of the V0 photon / cluster + std::vector mChainsPCM; + std::vector mChainsEMC; + std::vector mTruthPCM; + std::vector mTruthEMC; + // pi0 prefilter study: photon forms a pair inside the pi0 mass window with another photon of its event, index = globalIndex + std::vector mPrefilterPCM; + std::vector mPrefilterEMC; + // the same, tagged with all three pair types (PCM-PCM, EMC-EMC, PCM-EMC) + std::vector mPrefilterAllPCM; + std::vector mPrefilterAllEMC; + + void defineEMEventCut() + { + fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut"); + fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8); + fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND); + fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax); + fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB); + fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB); + fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup); + fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC); + fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV); + fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB); + fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered); + } + + void defineEMCCut() + { + fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut"); + + fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2)); + fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2)); + + fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2)); + fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2)); + fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp); + + fEMCCut.SetMinE(emccuts.cfgEMCminE); + fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell); + fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02); + fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime); + fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut); + fEMCCut.SetClusterizer(emccuts.clusterDefinition); + fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM + fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM + fEMCCut.SetDoQA(emccuts.cfgEnableQA.value); + } + + void definePCMCut() + { + fV0PhotonCut = V0PhotonCut("fV0PhotonCut", "fV0PhotonCut"); + + // for v0 + fV0PhotonCut.SetV0PtRange(pcmcuts.minPtV0, pcmcuts.maxPtV0); + fV0PhotonCut.SetV0EtaRange(pcmcuts.minEtaV0, pcmcuts.maxEtaV0); + fV0PhotonCut.SetMinCosPA(pcmcuts.minCosPA); + fV0PhotonCut.SetMaxPCA(pcmcuts.maxPCA); + fV0PhotonCut.SetMaxChi2KF(pcmcuts.maxChi2KF); + fV0PhotonCut.SetRxyRange(pcmcuts.minRV0, pcmcuts.maxRV0); + fV0PhotonCut.SetAPRange(pcmcuts.maxAlphaAP, pcmcuts.maxQtAP); + fV0PhotonCut.RejectITSib(pcmcuts.rejectV0onITSib); + + // for track + fV0PhotonCut.SetMinNClustersTPC(pcmcuts.minNClusterTPC); + fV0PhotonCut.SetMinNCrossedRowsTPC(pcmcuts.minNCrossedRowsTPC); + fV0PhotonCut.SetMinNCrossedRowsOverFindableClustersTPC(pcmcuts.minNCrossedRowsOverFindableClustersTPC); + fV0PhotonCut.SetMaxFracSharedClustersTPC(pcmcuts.maxFracSharedClustersTPC); + fV0PhotonCut.SetChi2PerClusterTPC(0.f, pcmcuts.macChi2TPC); + fV0PhotonCut.SetTPCNsigmaElRange(pcmcuts.minTPCNSigmaEl, pcmcuts.maxTPCNSigmaEl); + fV0PhotonCut.SetChi2PerClusterITS(0.f, pcmcuts.macChi2ITS); + fV0PhotonCut.SetNClustersITS(pcmcuts.minNClusterITS, 7); + fV0PhotonCut.SetMeanClusterSizeITSob(pcmcuts.minMeanClusterSizeITSob, pcmcuts.maxMeanClusterSizeITSob); + fV0PhotonCut.SetDisableITSonly(pcmcuts.disableITSOnly); + fV0PhotonCut.SetDisableTPConly(pcmcuts.disableTPCOnly); + fV0PhotonCut.SetRequireITSTPC(pcmcuts.requireV0WithITSTPC); + fV0PhotonCut.SetRequireITSonly(pcmcuts.requireV0WithITSOnly); + fV0PhotonCut.SetRequireTPConly(pcmcuts.requireV0WithTPCOnly); + + fV0PhotonCut.setDoQA(pcmcuts.doQA.value); + } + + void init(InitContext&) + { + if ((doprocessPcm.value && doprocessPcmPrefilterAll.value) || (doprocessEmcal.value && doprocessEmcalPrefilterAll.value)) { + LOG(fatal) << "processPcm / processEmcal and their PrefilterAll variants fill the same histograms, enable only one of each"; + } + mRunNumber = 0; + dBz = 0; + + defineEMEventCut(); + defineEMCCut(); + fEMCCut.addQAHistograms(®istry); + definePCMCut(); + fV0PhotonCut.addQAHistograms(®istry); + o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(®istry); + + const AxisSpec thnAxisPtGen{thnConfigAxisPt, "#it{p}_{T,Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPtRec{thnConfigAxisPt, "#it{p}_{T,Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPGen{thnConfigAxisPt, "#it{p}_{Gen} (GeV/#it{c})"}; + const AxisSpec thnAxisPRec{thnConfigAxisPt, "#it{p}_{Rec} (GeV/#it{c})"}; + const AxisSpec thnAxisPRelative{thnConfigAxisPRelative, "#it{p}_{Rec} - #it{p}_{Gen} / #it{p}_{Gen}"}; + const AxisSpec thnAxisEGen{thnConfigAxisPt, "#it{E}_{Gen} (GeV)"}; + const AxisSpec thnAxisERec{thnConfigAxisPt, "#it{E}_{Rec} (GeV)"}; + const AxisSpec thnAxisERelative{thnConfigAxisERelative, "#it{E}_{Rec} - #it{E}_{Gen} / #it{E}_{Gen}"}; + const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + + const AxisSpec thnAxisEtaRelative{thnConfigAxisEtaRelative, "#it{#eta}_{Rec} - #it{#eta}_{Gen} / #it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiRelative{thnConfigAxisPhiRelative, "#it{#varphi}_{Rec} - #it{#varphi}_{Gen} / #it{#varphi}_{Gen}"}; + + const AxisSpec thnAxisEtaGen{280, -0.7, 0.7, "#it{#eta}_{Gen}"}; + const AxisSpec thnAxisPhiGen{360, 0., o2::constants::math::TwoPI, "#it{#varphi}_{Gen} (rad)"}; + + const AxisSpec thnAxisPairTruth{static_cast(PhotonTruth::kNTruth), -0.5, static_cast(PhotonTruth::kNTruth) - 0.5, "pair truth (worse photon)"}; + const AxisSpec thnAxisPairBucket{static_cast(PairBucket::kNBuckets), -0.5, static_cast(PairBucket::kNBuckets) - 0.5, "closest common ancestor"}; + + AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init + if (useCent.value) { + // PbPb: use centrality + thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"}; + } else { + // pp: use multiplicity + thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"}; + } + + auto hPairAncestor = registry.add("PCM/hPairAncestor", "minv vs pT vs cent vs pair truth vs closest common ancestor", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth, thnAxisPairBucket}); + static constexpr std::array(PhotonTruth::kNTruth)> TruthLabels = {"true #gamma", "non #gamma", "fake"}; + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hPairAncestor->GetAxis(3)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + static constexpr std::array(PairBucket::kNBuckets)> BucketLabels = {"none", "same particle", "#pi^{0} direct", "#eta direct", "#pi^{0} cross", "#eta cross", "#omega", "K^{0}_{S}", "#eta'", "other hadron", "parton/string"}; + for (std::size_t i = 0; i < BucketLabels.size(); ++i) { + hPairAncestor->GetAxis(4)->SetBinLabel(static_cast(i) + 1, BucketLabels[i].data()); + } + // "other hadron" pairs: which hadron is the common ancestor, and did it split in a decay or a material interaction + const AxisSpec axisOHMass{160, 0., 0.8, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec axisOHPt{20, 0., 20., "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisOHSpecies{static_cast(OtherHadronSpecies::kNSpecies), -0.5, static_cast(OtherHadronSpecies::kNSpecies) - 0.5, "common ancestor"}; + const AxisSpec axisOHProduction{static_cast(SplitProduction::kNProduction), -0.5, static_cast(SplitProduction::kNProduction) - 0.5, "split of the common ancestor"}; + auto hOtherHadron = registry.add("PCM/OtherHadron/hAncestor", "other hadron pairs: minv vs pT vs ancestor species vs split production vs pair truth", HistType::kTHnF, {axisOHMass, axisOHPt, axisOHSpecies, axisOHProduction, thnAxisPairTruth}); + static constexpr std::array(OtherHadronSpecies::kNSpecies)> SpeciesLabels = {"#pi^{#pm}", "K^{#pm}", "K^{0}_{L}", "#rho", "K^{*}", "#phi", "a_{1}", "K_{1}, K^{*}_{0,2}", "open charm", "c#bar{c}", "open beauty", "b#bar{b}", "other meson", "p", "n", "#Delta", "#Lambda", "#Sigma", "#Xi", "other baryon", "other"}; + for (std::size_t i = 0; i < SpeciesLabels.size(); ++i) { + hOtherHadron->GetAxis(2)->SetBinLabel(static_cast(i) + 1, SpeciesLabels[i].data()); + } + static constexpr std::array(SplitProduction::kNProduction)> ProductionLabels = {"generator decay", "transport decay", "hadronic interaction", "other process"}; + for (std::size_t i = 0; i < ProductionLabels.size(); ++i) { + hOtherHadron->GetAxis(3)->SetBinLabel(static_cast(i) + 1, ProductionLabels[i].data()); + } + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hOtherHadron->GetAxis(4)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + registry.add("PCM/OtherHadron/hAncestorPdg", "other hadron pairs: |PDG| of the common ancestor;|PDG| (last bin: #geq 9999);pairs", HistType::kTH1D, {{10000, -0.5, 9999.5}}); + + // pi0 prefilter study for the eta: photon origins of the pair vs number of photons tagged by a pi0 mass window prefilter + const AxisSpec axisPrefilterMass{mesonConfig.thnConfigAxisPrefilterMass, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec axisPrefilterPt{mesonConfig.thnConfigAxisPrefilterPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisPrefilterClass{static_cast(PrefilterPairClass::kNClasses), -0.5, static_cast(PrefilterPairClass::kNClasses) - 0.5, "photon origins"}; + const AxisSpec axisPrefilterTagged{3, -0.5, 2.5, "photons tagged by the #pi^{0} prefilter"}; + auto hPrefilter = registry.add("PCM/hPrefilter", "minv vs pT vs cent vs photon origins vs photons tagged by the pi0 prefilter", HistType::kTHnF, {axisPrefilterMass, axisPrefilterPt, thnAxisCentOrMult, axisPrefilterClass, axisPrefilterTagged}); + static constexpr std::array(PrefilterPairClass::kNClasses)> PrefilterClassLabels = {"same particle", "same #pi^{0}", "two #pi^{0}", "same #eta", "two #eta", "#pi^{0} + #eta", "#pi^{0} + else", "#eta + else", "both else"}; + for (std::size_t i = 0; i < PrefilterClassLabels.size(); ++i) { + hPrefilter->GetAxis(3)->SetBinLabel(static_cast(i) + 1, PrefilterClassLabels[i].data()); + } + static constexpr std::array TaggedLabels = {"0", "1", "2"}; + for (std::size_t i = 0; i < TaggedLabels.size(); ++i) { + hPrefilter->GetAxis(4)->SetBinLabel(static_cast(i) + 1, TaggedLabels[i].data()); + } + // same, but the photons are tagged with all three pair types, only booked if a process function fills it + if (doprocessPcmPrefilterAll.value || doprocessEmcalPrefilterAll.value || (doprocessPcmEmcal.value && mesonConfig.prefilterAllPairTypes.value)) { + registry.addClone("PCM/hPrefilter", "PCM/hPrefilterAll"); + } + + // mixed event, same axes as hPairAncestor without the bucket (pairs from different events share no ancestor) + auto hMixed = registry.add("PCM/hMixed", "minv vs pT vs cent vs pair truth, mixed event", HistType::kTHnF, {thnAxisInvMass, thnAxisPtRec, thnAxisCentOrMult, thnAxisPairTruth}); + for (std::size_t i = 0; i < TruthLabels.size(); ++i) { + hMixed->GetAxis(3)->SetBinLabel(static_cast(i) + 1, TruthLabels[i].data()); + } + registry.addClone("PCM/", "EMC/"); + registry.addClone("PCM/", "PCMEMC/"); + + // ancestor chain QA + registry.add("QA/PCM/hChainLength", "length of the ancestor chain;chain length;counts", HistType::kTH1D, {{kMaxChainLength, 0.5, kMaxChainLength + 0.5}}); + auto hChainEnd = registry.add("QA/PCM/hChainEnd", "reason the ancestor chain ended;;counts", HistType::kTH1D, {{4, -0.5, 3.5}}); + hChainEnd->GetXaxis()->SetBinLabel(1, "parton-like"); + hChainEnd->GetXaxis()->SetBinLabel(2, "beam nucleus"); + hChainEnd->GetXaxis()->SetBinLabel(3, "no mothers"); + hChainEnd->GetXaxis()->SetBinLabel(4, "truncated"); + auto hChainEndKind = registry.add("QA/PCM/hChainEndKind", "kind of the last chain element;;counts", HistType::kTH1D, {{static_cast(AncestorKind::NClasses), -0.5, static_cast(AncestorKind::NClasses) - 0.5}}); + static constexpr std::array(AncestorKind::NClasses)> KindLabels = {"none", "string/parton", "beam/nucleus", "#pi^{0}", "#eta", "other meson", "baryon", "#gamma", "e^{#pm}", "other"}; + for (std::size_t i = 0; i < KindLabels.size(); ++i) { + hChainEndKind->GetXaxis()->SetBinLabel(static_cast(i) + 1, KindLabels[i].data()); + } + registry.add("QA/PCM/hChainEndPdg", "|PDG| of the last chain element;|PDG| (last bin: #geq 9999, incl. nuclei);counts", HistType::kTH1D, {{10000, -0.5, 9999.5}}); + registry.addClone("QA/PCM/", "QA/EMC/"); + + // origin of PCM-EMC same particle pairs and why track matching did not remove them + if (doprocessPcmEmcal.value) { + const AxisSpec axisOrigin{static_cast(SameParticleOrigin::kNOrigins), -0.5, static_cast(SameParticleOrigin::kNOrigins) - 0.5, "leading contributor of the cluster"}; + const AxisSpec axisStatus{static_cast(LegMatchStatus::kNStatus), -0.5, static_cast(LegMatchStatus::kNStatus) - 0.5, "V0 leg among the matched tracks"}; + const AxisSpec axisPtLeg{40, 0., 20., "#it{p}_{T,leg} (GeV/#it{c})"}; + const AxisSpec axisDeltaEta{100, -0.1, 0.1, "#Delta#eta (cluster - track)"}; + const AxisSpec axisDeltaPhi{100, -0.1, 0.1, "#Delta#varphi (cluster - track)"}; + const AxisSpec axisDp{200, -1., 1., "(#it{p}_{match} - #it{p}_{leg}) / #it{p}_{leg}"}; + static constexpr std::array(SameParticleOrigin::kNOrigins)> OriginLabels = {"leg e^{#pm}", "brems #gamma of leg", "other e^{#pm}", "other"}; + static constexpr std::array(LegMatchStatus::kNStatus)> StatusLabels = {"no match stored", "no match with leg #it{p}", "outside window", "in window, #it{E}/#it{p} #geq cut", "in window", "not a global track"}; + + auto hOrigin = registry.add("PCMEMC/SameParticle/hOrigin", "same particle pairs: leading contributor of the cluster", HistType::kTH3F, {thnAxisInvMass, thnAxisPtRec, axisOrigin}); + for (std::size_t i = 0; i < OriginLabels.size(); ++i) { + hOrigin->GetZaxis()->SetBinLabel(static_cast(i) + 1, OriginLabels[i].data()); + } + // labels make the axis extendable, and TH1Merger::LabelMerge in ROOT 6.36 reads the z labels from the y axis: + // merged TH3 content would end up in a new, unlabelled z bin. With a fixed axis the merge goes bin by bin. + hOrigin->GetZaxis()->SetCanExtend(false); + registry.add("PCMEMC/SameParticle/hLegEoverP", "cluster from a V0 leg: #it{E}_{cls}/#it{p}_{leg}", HistType::kTH2F, {{200, 0., 4., "#it{E}_{cls}/#it{p}_{leg}"}, axisPtLeg}); + for (const auto& [type, title] : {std::pair{"Prim", "primary"}, {"Sec", "secondary"}}) { + auto hStatus = registry.add(std::format("PCMEMC/SameParticle/hLeg{}Status", type.data()).data(), std::format("cluster from a V0 leg: leg among the matched {} tracks", title.data()).data(), HistType::kTH2F, {axisStatus, axisPtLeg}); + for (std::size_t i = 0; i < StatusLabels.size(); ++i) { + hStatus->GetXaxis()->SetBinLabel(static_cast(i) + 1, StatusLabels[i].data()); + } + registry.add(std::format("PCMEMC/SameParticle/hLeg{}Dp", type.data()).data(), std::format("cluster from a V0 leg: closest {} match in #it{{p}}", title.data()).data(), HistType::kTH1F, {axisDp}); + registry.add(std::format("PCMEMC/SameParticle/hLeg{}Residual", type.data()).data(), std::format("cluster from a V0 leg: residual of the {} match", title.data()).data(), HistType::kTH3F, {axisDeltaEta, axisDeltaPhi, axisPtLeg}); + } + } + + auto hMesonCuts = registry.add("hMesonCuts", "hMesonCuts;;Counts", kTH1D, {{6, 0.5, 6.5}}, false); + hMesonCuts->GetXaxis()->SetBinLabel(1, "in"); + hMesonCuts->GetXaxis()->SetBinLabel(2, "opening angle"); + hMesonCuts->GetXaxis()->SetBinLabel(3, "#it{M}_{#gamma#gamma}"); + hMesonCuts->GetXaxis()->SetBinLabel(4, "#it{p}_{T}"); + hMesonCuts->GetXaxis()->SetBinLabel(5, "conversion cut"); + hMesonCuts->GetXaxis()->SetBinLabel(6, "out"); + if (mesonConfig.cfgEnableQA.value) { + registry.add("mesonQA/hInvMassPt", "Histo for inv pair mass vs pt", HistType::kTH2D, {thnAxisInvMass, thnAxisPtRec}); + } + }; // end init + + template + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber()) { + return; + } + mRunNumber = collision.runNumber(); + + dBz = collision.grpMagField().getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << collision.timestamp() << " with magnetic field of " << dBz << " kZG"; + fV0PhotonCut.SetD_Bz(dBz); + } + + template + float getCentralityOrMultiplicity(TCollision const& collision) + { + if (useCent.value) { + return getCentrality(collision); + } + // pp: use raw FT0C multiplicity + return collision.multFT0C(); + } + + /// Get the centrality + /// \param collision is the collision with the centrality information + template + float getCentrality(TCollision const& collision) + { + float cent = -999.; + switch (eventcuts.centEstimator) { + case CentralityEstimator::CFT0M: + cent = collision.centFT0M(); + break; + case CentralityEstimator::CFT0A: + cent = collision.centFT0A(); + break; + case CentralityEstimator::CFT0C: + cent = collision.centFT0C(); + break; + default: + LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C"; + cent = collision.centFT0C(); + break; + } + return cent; + } + + /// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is + /// \param collision collision that will be checked + /// \return true if collision survives all checks, otherwise false + template + bool isFullEventSelected(TCollision const& collision, bool fillHisto = false) + { + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(®istry, collision); + } + if (!(fEMEventCut.IsSelected(collision))) { + // general event selection + return false; + } + if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) { + // occupancy selection + return false; + } + float centOrMult = getCentralityOrMultiplicity(collision); + if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) { + // event selection + return false; + } + if (fillHisto) { + o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(®istry, collision); + registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted + registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted + } + return true; + } + + /// \brief Reset the per DF cache of one photon type + /// \param chains ancestor chains, set to -1 (not {}, which gives 0, a valid index) + /// \param truth truth flags, set to kFake + /// \param size size of the full photon table + static void resetCache(std::vector& chains, std::vector& truth, const std::size_t size) + { + Chain empty; + empty.fill(-1); + chains.assign(size, empty); + truth.assign(size, PhotonTruth::kFake); + } + + /// \brief Fill the ancestor chain QA + /// \param mcIter MC particle iterator, has to sit where getAncestorChain left it (last chain element or the nucleus that ended the chain) + /// \param length length of the chain as returned by getAncestorChain + template + void fillChainQA(TMCIter const& mcIter, const int length) + { + static constexpr std::array HistLength = {"QA/PCM/hChainLength", "QA/EMC/hChainLength"}; + static constexpr std::array HistEnd = {"QA/PCM/hChainEnd", "QA/EMC/hChainEnd"}; + static constexpr std::array HistEndKind = {"QA/PCM/hChainEndKind", "QA/EMC/hChainEndKind"}; + static constexpr std::array HistEndPdg = {"QA/PCM/hChainEndPdg", "QA/EMC/hChainEndPdg"}; + if (length <= 0) { + return; + } + static constexpr int MaxPdgBin = 9999; // nuclei and other large codes go to the last bin + const int lastPdg = mcIter.pdgCode(); + int endReason = 3; // chain is full without reaching a parton, a nucleus or a particle without mothers + if (isPartonLike(lastPdg)) { + endReason = 0; + } else if (isNucleus(lastPdg)) { + endReason = 1; + } else if (!mcIter.has_mothers()) { + endReason = 2; + } + registry.fill(HIST(HistLength[photonType]), length); + registry.fill(HIST(HistEnd[photonType]), endReason); + registry.fill(HIST(HistEndKind[photonType]), static_cast(ancestorKindOfPdg(lastPdg))); + registry.fill(HIST(HistEndPdg[photonType]), std::min(std::abs(lastPdg), MaxPdgBin)); + } + + /// \brief Fill ancestor chains and truth flags of all EMC clusters of the DF + /// \param clusters full (unfiltered) cluster table + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \param doChainQA fill the ancestor chain QA (off for the mixed event, so chains are not counted twice) + template + void fillEmcCache(TClusters const& clusters, TMCIter& mcIter, const bool doChainQA = true) + { + for (const auto& cluster : clusters) { + const auto mcIds = cluster.emmcparticleIds(); + if (mcIds.empty() || mcIds[0] < 0) { + continue; // noise cluster without MC label: kFake, chain stays -1 + } + auto& chain = mChainsEMC[cluster.globalIndex()]; + const int length = getAncestorChain(mcIter, mcIds[0], chain); // leading contributor + if (doChainQA) { + fillChainQA(mcIter, length); + } + if (length <= 0) { + continue; // leading contributor is a nucleus: kFake, chain stays -1 + } + + // e+- whose mother is a photon (conversion in front of the EMCal): start the chain at the photon, + // otherwise the direct mother check of the pair classification fails for converted photons + mcIter.setCursor(chain[0]); + if (length > 1 && std::abs(mcIter.pdgCode()) == PDG_t::kElectron) { + mcIter.setCursor(chain[1]); + if (mcIter.pdgCode() == PDG_t::kGamma) { + std::shift_left(chain.begin(), chain.end(), 1); + chain.back() = -1; + } + } + mcIter.setCursor(chain[0]); + mTruthEMC[cluster.globalIndex()] = mcIter.pdgCode() == PDG_t::kGamma ? PhotonTruth::kTruePhoton : PhotonTruth::kNonPhoton; + } + } + + /// \brief Fill ancestor chains and truth flags of all V0 photons of the DF + /// \param photons full (unfiltered) V0 photon table + /// \param legs full V0 leg table with MC labels + /// \param mcParticles full MC particle table the leg labels point into + /// \param doChainQA fill the ancestor chain QA (off for the mixed event, so chains are not counted twice) + template + void fillPcmCache(TPhotons const& photons, TLegs const& legs, TMCParticles const& mcParticles, const bool doChainQA = true) + { + auto mcIter = mcParticles.begin(); + auto posLeg = legs.begin(); + auto negLeg = legs.begin(); + auto posMc = mcParticles.begin(); + auto negMc = mcParticles.begin(); + + Chain chainPos{}; + Chain chainNeg{}; + + for (const auto& photon : photons) { + posLeg.setCursor(photon.posTrackId()); + negLeg.setCursor(photon.negTrackId()); + if (posLeg.emmcparticleId() < 0 || negLeg.emmcparticleId() < 0) { + continue; // leg without MC label: kFake, chain stays -1 + } + posMc.setCursor(posLeg.emmcparticleId()); + negMc.setCursor(negLeg.emmcparticleId()); + + auto& chain = mChainsPCM[photon.globalIndex()]; + auto& truth = mTruthPCM[photon.globalIndex()]; + const int photonId = o2::aod::pwgem::dilepton::utils::mcutil::FindCommonMotherFrom2Prongs(posMc, negMc, PDG_t::kPositron, PDG_t::kElectron, PDG_t::kGamma, mcParticles); + if (photonId >= 0) { + truth = PhotonTruth::kTruePhoton; + const int length = getAncestorChain(mcIter, photonId, chain); + if (doChainQA) { + fillChainQA(mcIter, length); + } + continue; + } + + // not a true conversion: K0S/Lambda V0, Dalitz pair or combinatorial fake + const int lengthPos = getAncestorChain(mcIter, posLeg.emmcparticleId(), chainPos); + if (doChainQA) { + fillChainQA(mcIter, lengthPos); // the kept chain is a tail of this one, so it ends the same way + } + getAncestorChain(mcIter, negLeg.emmcparticleId(), chainNeg); + const int ancestor = getFirstCommonAncestor(chainPos, chainNeg); + if (ancestor < 0) { + continue; // no common ancestor: kFake, chain stays -1 + } + const bool isDirectMother = chainPos[1] == ancestor && chainNeg[1] == ancestor; + truth = isDirectMother ? PhotonTruth::kNonPhoton : PhotonTruth::kFake; + const auto start = std::ranges::find(chainPos, ancestor); + std::ranges::fill(std::copy(start, chainPos.end(), chain.begin()), chain.end(), -1); + } + } + + /// \brief Classify a pair by the closest common ancestor of the two candidates + /// \param chain1 ancestor chain of the first candidate + /// \param chain2 ancestor chain of the second candidate + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + PairBucket classifyPair(Chain const& chain1, Chain const& chain2, TMCIter& mcIter) + { + const int ancestor = getFirstCommonAncestor(chain1, chain2); + if (ancestor < 0) { + return PairBucket::kNone; + } + if (ancestor == chain1[0] || ancestor == chain2[0]) { + return PairBucket::kSameParticle; + } + mcIter.setCursor(ancestor); + const int absPdg = std::abs(mcIter.pdgCode()); + if (absPdg == PDG_t::kGamma || absPdg == PDG_t::kElectron) { + return PairBucket::kSameParticle; // both candidates come from the electromagnetic shower of one photon or electron + } + const bool isDirectMother = chain1[1] == ancestor && chain2[1] == ancestor; + if (isDirectMother && absPdg == PDG_t::kPi0) { + return PairBucket::kSignalPi0; + } + if (isDirectMother && absPdg == o2::constants::physics::Pdg::kEta) { + return PairBucket::kSignalEta; + } + switch (absPdg) { + case PDG_t::kPi0: + return PairBucket::kCrossPi0; + case o2::constants::physics::Pdg::kEta: + return PairBucket::kCrossEta; + case o2::constants::physics::Pdg::kOmega: + return PairBucket::kCrossOmega; + case PDG_t::kK0Short: + return PairBucket::kCrossK0S; + case o2::constants::physics::Pdg::kEtaPrime: + return PairBucket::kCrossEtaPrime; + default: + break; + } + if (isPartonLike(absPdg)) { + return PairBucket::kPartonString; + } + return PairBucket::kCrossOtherHadron; + } + + /// \brief Whether the track of a V0 leg passes the global track selection used by the primary EMCal track matching. + /// Rebuilt from the leg columns of the derived data, following getGlobalTrackSelectionRun3ITSMatch(Run3ITSibAny): + /// the golden chi2 requirement cannot be checked here, and pT/eta are the ones of the leg at the V0 vertex. + /// \param leg V0 leg + template + static bool isLikelyGlobalTrack(TLeg const& leg) + { + constexpr float MinPt = 0.1f, MaxEta = 0.8f; + constexpr int MinCrossedRows = 70; + constexpr float MinCrossedRowsOverFindable = 0.8f, MaxChi2TPC = 4.f, MaxChi2ITS = 36.f, MaxDcaZ = 2.f; + const float pt = leg.pt(); + if (!leg.hasITS() || !leg.hasTPC() || pt < MinPt || std::fabs(leg.eta()) > MaxEta) { + return false; + } + if (leg.itsNClsInnerBarrel() < 1 || leg.itsChi2NCl() > MaxChi2ITS) { // Run3ITSibAny: at least one hit in ITS layers 0-2 + return false; + } + if (leg.tpcNClsCrossedRows() < MinCrossedRows || leg.tpcCrossedRowsOverFindableCls() < MinCrossedRowsOverFindable || leg.tpcChi2NCl() > MaxChi2TPC) { + return false; + } + const float maxDcaXY = 0.0105f + 0.035f / std::pow(pt, 1.1f); // o2-linter: disable=magic-number (global track selection) + return std::fabs(leg.dcaXY()) <= maxDcaXY && std::fabs(leg.dcaZ()) <= MaxDcaZ; + } + + /// \brief Look for the V0 leg among the tracks matched to an EMC cluster and classify why it did not veto the cluster. + /// The matched track tables store no track index, so the leg is identified by its momentum. + /// \tparam isSecondary matches are secondaries (V0 legs, no E/p condition) instead of primaries + /// \param matches tracks matched to this cluster + /// \param eCluster cluster energy + /// \param pLeg momentum of the V0 leg + /// \param ptLeg transverse momentum of the V0 leg + template + void fillLegMatch(TMatches const& matches, const float eCluster, const float pLeg, const float ptLeg) + { + static constexpr std::array HistStatus = {"PCMEMC/SameParticle/hLegPrimStatus", "PCMEMC/SameParticle/hLegSecStatus"}; + static constexpr std::array HistDp = {"PCMEMC/SameParticle/hLegPrimDp", "PCMEMC/SameParticle/hLegSecDp"}; + static constexpr std::array HistResidual = {"PCMEMC/SameParticle/hLegPrimResidual", "PCMEMC/SameParticle/hLegSecResidual"}; + static constexpr float MaxRelDp = 0.1f; // max |p_match - p_leg| / p_leg to identify the match as the leg + + LegMatchStatus status = LegMatchStatus::kNoMatchStored; + if (matches.size() > 0) { + float bestDp = 1e9f; + float dEta = 0.f, dPhi = 0.f, trackPt = 0.f, trackP = 0.f; + for (const auto& match : matches) { + const float dp = (match.trackP() - pLeg) / pLeg; + if (std::fabs(dp) < std::fabs(bestDp)) { + bestDp = dp; + dEta = match.deltaEta(); + dPhi = match.deltaPhi(); + trackPt = match.trackPt(); + trackP = match.trackP(); + } + } + registry.fill(HIST(HistDp[isSecondary]), bestDp); + if (std::fabs(bestDp) > MaxRelDp) { + status = LegMatchStatus::kNoCompatibleMatch; + } else { + registry.fill(HIST(HistResidual[isSecondary]), dEta, dPhi, ptLeg); + // same windows as EMCPhotonCut::checkTrackMatching + bool isInWindow = false; + if constexpr (isSecondary) { + isInWindow = std::fabs(dEta) <= fEMCCut.GetSecTrackMatchingEta(trackPt) && std::fabs(dPhi) <= fEMCCut.GetSecTrackMatchingPhi(trackPt); + } else { + isInWindow = std::fabs(dEta) <= fEMCCut.GetTrackMatchingEta(trackPt) && std::fabs(dPhi) <= fEMCCut.GetTrackMatchingPhi(trackPt); + } + if (!isInWindow) { + status = LegMatchStatus::kOutsideWindow; + } else if (!isSecondary && eCluster / trackP >= emccuts.cfgEMCEoverp) { + status = LegMatchStatus::kInsideWindowEoverP; + } else { + status = LegMatchStatus::kInsideWindow; + } + } + } + registry.fill(HIST(HistStatus[isSecondary]), static_cast(status), ptLeg); + } + + /// \brief Find out where a PCM-EMC same particle pair comes from: what made the EMC cluster and why track matching did not remove it + /// \param pcm V0 photon + /// \param emc EMC cluster + /// \param legs full V0 leg table with MC labels + /// \param matchedPrims full table of primary tracks matched to clusters + /// \param matchedSeconds full table of secondary tracks (V0 legs) matched to clusters + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + void diagnoseSameParticle(TPCM const& pcm, TEMC const& emc, TLegs const& legs, TMatchedPrims const& matchedPrims, TMatchedSecs const& matchedSeconds, TMCIter& mcIter) + { + const auto mcIds = emc.emmcparticleIds(); + if (mcIds.empty() || mcIds[0] < 0) { + return; + } + const int lead = mcIds[0]; // leading contributor, before the conversion shift of the chain + const auto posLeg = legs.iteratorAt(pcm.posTrackId()); + const auto negLeg = legs.iteratorAt(pcm.negTrackId()); + const std::array legMcIds = {posLeg.emmcparticleId(), negLeg.emmcparticleId()}; + + SameParticleOrigin origin = SameParticleOrigin::kOther; + int legIndex = -1; + if (lead == legMcIds[0] || lead == legMcIds[1]) { + origin = SameParticleOrigin::kLegElectron; + legIndex = lead == legMcIds[0] ? 0 : 1; + } else { + mcIter.setCursor(lead); + const int absPdg = std::abs(mcIter.pdgCode()); + const int motherId = mcIter.has_mothers() ? mcIter.mothersIds()[0] : -1; + if (absPdg == PDG_t::kGamma && motherId >= 0 && (motherId == legMcIds[0] || motherId == legMcIds[1])) { + origin = SameParticleOrigin::kBremsFromLeg; + } else if (absPdg == PDG_t::kElectron) { + origin = SameParticleOrigin::kOtherElectron; + } + } + + ROOT::Math::PtEtaPhiMVector v1(pcm.pt(), pcm.eta(), pcm.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(emc.pt(), emc.eta(), emc.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + registry.fill(HIST("PCMEMC/SameParticle/hOrigin"), vMeson.M(), vMeson.Pt(), static_cast(origin)); + if (origin != SameParticleOrigin::kLegElectron) { + return; + } + + const auto& leg = legIndex == 0 ? posLeg : negLeg; + const float pLeg = std::hypot(leg.px(), leg.py(), leg.pz()); + const float ptLeg = std::hypot(leg.px(), leg.py()); + if (pLeg <= 0.f) { + return; + } + registry.fill(HIST("PCMEMC/SameParticle/hLegEoverP"), emc.e() / pLeg, ptLeg); + // the primary matching only uses global tracks: other legs are no candidate there and get their own status + if (isLikelyGlobalTrack(leg)) { + fillLegMatch(matchedPrims.sliceBy(perEMCClusterMT, emc.globalIndex()), emc.e(), pLeg, ptLeg); + } else { + registry.fill(HIST("PCMEMC/SameParticle/hLegPrimStatus"), static_cast(LegMatchStatus::kNotGlobalTrack), ptLeg); + } + fillLegMatch(matchedSeconds.sliceBy(perEMCClusterMS, emc.globalIndex()), emc.e(), pLeg, ptLeg); + } + + /// \brief Species class of the common ancestor of an "other hadron" pair + /// \param pdg PDG code of the ancestor + static OtherHadronSpecies otherHadronSpecies(const int pdg) + { + const int absPdg = std::abs(pdg); + switch (absPdg) { + case PDG_t::kPiPlus: + return OtherHadronSpecies::kChargedPion; + case PDG_t::kKPlus: + return OtherHadronSpecies::kChargedKaon; + case PDG_t::kK0Long: + return OtherHadronSpecies::kK0L; + case PDG_t::kRho770_0: + case PDG_t::kRho770Plus: + return OtherHadronSpecies::kRho; + case o2::constants::physics::Pdg::kK0Star892: + case o2::constants::physics::Pdg::kKPlusStar892: + return OtherHadronSpecies::kKstar; + case o2::constants::physics::Pdg::kPhi: + return OtherHadronSpecies::kPhi; + case PDG_t::kProton: + return OtherHadronSpecies::kProton; + case PDG_t::kNeutron: + return OtherHadronSpecies::kNeutron; + case 1114: // o2-linter: disable=pdg/explicit-code (Delta-, no named constant) + case 2114: // o2-linter: disable=pdg/explicit-code (Delta0, no named constant) + case 2214: // o2-linter: disable=pdg/explicit-code (Delta+, no named constant) + case 2224: // o2-linter: disable=pdg/explicit-code (Delta++, no named constant) + return OtherHadronSpecies::kDelta; + case PDG_t::kLambda0: + return OtherHadronSpecies::kLambda; + case PDG_t::kSigmaMinus: + case PDG_t::kSigma0: + case PDG_t::kSigmaPlus: + return OtherHadronSpecies::kSigma; + case PDG_t::kXiMinus: + case 3322: // o2-linter: disable=pdg/explicit-code (Xi0, no named constant) + return OtherHadronSpecies::kXi; + default: + break; + } + constexpr int MinHadronPdg = 100; + constexpr int MaxHadronPdg = 10000000; + if (absPdg < MinHadronPdg || absPdg >= MaxHadronPdg) { + return OtherHadronSpecies::kOther; + } + // PDG numbering: mesons have no third quark, i.e. the thousands digit is 0 (also for 9000000 type resonances) + if ((absPdg / 1000) % 10 != 0) { // o2-linter: disable=magic-number (PDG digit) + return OtherHadronSpecies::kOtherBaryon; + } + // mesons: hundreds digit = heavier quark, tens digit = lighter quark, last digit = 2J+1 + // (also covers excited states with a radial/orbital prefix, e.g. 10441, 20443, 100553) + const int heavyQuark = (absPdg / 100) % 10; // o2-linter: disable=magic-number (PDG digit) + const int lightQuark = (absPdg / 10) % 10; // o2-linter: disable=magic-number (PDG digit) + constexpr int Strange = 3, Charm = 4, Beauty = 5; + constexpr int ExcitedPrefix = 10000; + constexpr int SpinTwo = 5; // 2J+1 for J = 2 + if (heavyQuark == Charm) { + return lightQuark == Charm ? OtherHadronSpecies::kCharmonium : OtherHadronSpecies::kOpenCharm; + } + if (heavyQuark == Beauty) { + return lightQuark == Beauty ? OtherHadronSpecies::kBottomonium : OtherHadronSpecies::kOpenBeauty; + } + if (absPdg == 20213 || absPdg == 20113) { // o2-linter: disable=pdg/explicit-code (a1(1260), no named constant) + return OtherHadronSpecies::kA1; + } + if (heavyQuark == Strange && lightQuark < Strange && (absPdg >= ExcitedPrefix || absPdg % 10 == SpinTwo)) { // o2-linter: disable=magic-number (PDG digit) + return OtherHadronSpecies::kExcitedK; + } + return OtherHadronSpecies::kOtherMeson; + } + + /// \brief How an MC particle was produced + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \param id index of the particle + template + static SplitProduction productionOf(TMCIter& mcIter, const int id) + { + mcIter.setCursor(id); + if (mcIter.producedByGenerator()) { + return SplitProduction::kGeneratorDecay; + } + switch (mcIter.getProcess()) { + case TMCProcess::kPDecay: + return SplitProduction::kTransportDecay; + case TMCProcess::kPHadronic: + case TMCProcess::kPHInhelastic: + case TMCProcess::kPNuclearAbsorption: + case TMCProcess::kPNCapture: + case TMCProcess::kPPbarAnnihilation: + case TMCProcess::kPNbarAnnihilation: + case TMCProcess::kPEvaporation: + case TMCProcess::kPNuclearFission: + case TMCProcess::kPPhotoNuclear: + case TMCProcess::kPPhotonInhelastic: + return SplitProduction::kHadronicInteraction; + default: + return SplitProduction::kOtherProcess; + } + } + + /// \brief Fill which hadron is the common ancestor of an "other hadron" pair and how it split into the two branches + /// \param chain1 ancestor chain of the first candidate + /// \param chain2 ancestor chain of the second candidate + /// \param pairTruth truth flag of the pair + /// \param vMeson pair four vector + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + template + void diagnoseOtherHadron(Chain const& chain1, Chain const& chain2, const PhotonTruth pairTruth, ROOT::Math::PtEtaPhiMVector const& vMeson, TMCIter& mcIter) + { + static constexpr std::array HistAncestor = {"PCM/OtherHadron/hAncestor", "EMC/OtherHadron/hAncestor", "PCMEMC/OtherHadron/hAncestor"}; + static constexpr std::array HistAncestorPdg = {"PCM/OtherHadron/hAncestorPdg", "EMC/OtherHadron/hAncestorPdg", "PCMEMC/OtherHadron/hAncestorPdg"}; + static constexpr int MaxPdgBin = 9999; + + const int ancestor = getFirstCommonAncestor(chain1, chain2); + const auto pos1 = std::distance(chain1.begin(), std::ranges::find(chain1, ancestor)); + const auto pos2 = std::distance(chain2.begin(), std::ranges::find(chain2, ancestor)); + if (ancestor < 0 || pos1 < 1 || pos2 < 1 || pos1 >= std::ssize(chain1) || pos2 >= std::ssize(chain2)) { + return; // not possible for "other hadron" (the ancestor is neither of the candidates), protection only + } + // the particle directly below the ancestor in each branch tells how the ancestor split + const SplitProduction production = std::max(productionOf(mcIter, chain1[pos1 - 1]), productionOf(mcIter, chain2[pos2 - 1])); + mcIter.setCursor(ancestor); + const int pdg = mcIter.pdgCode(); + registry.fill(HIST(HistAncestor[pairType]), vMeson.M(), vMeson.Pt(), static_cast(otherHadronSpecies(pdg)), static_cast(production), static_cast(pairTruth)); + registry.fill(HIST(HistAncestorPdg[pairType]), std::min(std::abs(pdg), MaxPdgBin)); + } + + /// \brief Origin of a photon candidate: walk up its chain through the EM steps (gamma, e+-), the first other particle decides + /// \param chain ancestor chain of the candidate + /// \param mcIter iterator of the full MC particle table, WILL BE CHANGED + /// \return origin and the index of the pi0/eta (-1 for else) + template + static std::pair photonOrigin(Chain const& chain, TMCIter& mcIter) + { + for (const auto& id : chain) { + if (id < 0) { + break; + } + mcIter.setCursor(id); + const int absPdg = std::abs(mcIter.pdgCode()); + if (absPdg == PDG_t::kGamma || absPdg == PDG_t::kElectron) { + continue; // EM step: conversion, bremsstrahlung, shower + } + if (absPdg == PDG_t::kPi0) { + return {PhotonOrigin::kFromPi0, id}; + } + if (absPdg == o2::constants::physics::Pdg::kEta) { + return {PhotonOrigin::kFromEta, id}; + } + break; + } + return {PhotonOrigin::kElse, -1}; + } + + /// \brief Class of a photon pair for the pi0 prefilter study + /// \param bucket closest common ancestor bucket of the pair: same particle pairs get their own class + template + static PrefilterPairClass prefilterClassOf(const PairBucket bucket, Chain const& chain1, Chain const& chain2, TMCIter& mcIter) + { + if (bucket == PairBucket::kSameParticle) { + return PrefilterPairClass::kSameParticle; + } + const auto [origin1, id1] = photonOrigin(chain1, mcIter); + const auto [origin2, id2] = photonOrigin(chain2, mcIter); + const int nPi0 = (origin1 == PhotonOrigin::kFromPi0 ? 1 : 0) + (origin2 == PhotonOrigin::kFromPi0 ? 1 : 0); + const int nEta = (origin1 == PhotonOrigin::kFromEta ? 1 : 0) + (origin2 == PhotonOrigin::kFromEta ? 1 : 0); + constexpr int Both = 2; + if (nPi0 == Both) { + return id1 == id2 ? PrefilterPairClass::kSamePi0 : PrefilterPairClass::kTwoPi0; + } + if (nEta == Both) { + return id1 == id2 ? PrefilterPairClass::kSameEta : PrefilterPairClass::kTwoEta; + } + if (nPi0 == 1 && nEta == 1) { + return PrefilterPairClass::kPi0Eta; + } + if (nPi0 == 1) { + return PrefilterPairClass::kPi0Else; + } + return nEta == 1 ? PrefilterPairClass::kEtaElse : PrefilterPairClass::kBothElse; + } + + /// \brief Whether a photon pair passes the pair cuts of fillPair and lies inside the pi0 window of the prefilter + template + bool isInPrefilterWindow(TPhoton1 const& g1, TPhoton2 const& g2) + { + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + const float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + if (openingAngle <= mesonConfig.minOpenAngle) { + return false; + } + const float mass = (v1 + v2).M(); + return mesonConfig.prefilterMassMin <= mass && mass <= mesonConfig.prefilterMassMax; + } + + /// \brief pi0 prefilter: tag both photons of every same-kind pair inside the pi0 window (PCM-PCM or EMC-EMC) + /// \param tags tag vector of the photon type, index = globalIndex + template + void tagSameKind(TPhotons const& photonsPerCollision, EMBitFlags const& flags, std::vector& tags) + { + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (flags.test(g1.globalIndex()) && flags.test(g2.globalIndex()) && isInPrefilterWindow(g1, g2)) { + tags[g1.globalIndex()] = 1; + tags[g2.globalIndex()] = 1; + } + } + } + + /// \brief pi0 prefilter: tag both photons of every PCM-EMC pair inside the pi0 window + template + void tagPcmEmc(TPcmPhotons const& pcmPerCollision, TEmcPhotons const& emcPerCollision, EMBitFlags const& v0flags, EMBitFlags const& emcFlags, std::vector& tagsPCM, std::vector& tagsEMC) + { + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(pcmPerCollision, emcPerCollision))) { + if (v0flags.test(g1.globalIndex()) && emcFlags.test(g2.globalIndex()) && isInPrefilterWindow(g1, g2)) { + tagsPCM[g1.globalIndex()] = 1; + tagsEMC[g2.globalIndex()] = 1; + } + } + } + + /// \brief pi0 prefilter with all three pair types: fill mPrefilterAllPCM / mPrefilterAllEMC for one collision + template + void tagAllPairTypes(TPcmPhotons const& pcmPerCollision, TEmcPhotons const& emcPerCollision, EMBitFlags const& v0flags, EMBitFlags const& emcFlags) + { + tagSameKind(pcmPerCollision, v0flags, mPrefilterAllPCM); + tagSameKind(emcPerCollision, emcFlags, mPrefilterAllEMC); + tagPcmEmc(pcmPerCollision, emcPerCollision, v0flags, emcFlags, mPrefilterAllPCM, mPrefilterAllEMC); + } + + /// \brief Apply the pair cuts and fill the pair ancestor histogram + /// \param nTagged number of photons of this pair tagged by the pi0 prefilter with the pair type of the process function + /// \param nTaggedAll number of photons of this pair tagged by the pi0 prefilter with all three pair types, -1: not computed + /// \return bucket of the pair, std::nullopt if the pair was rejected + template + std::optional fillPair(TPhoton1 const& g1, TPhoton2 const& g2, Chain const& chain1, Chain const& chain2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult, const int nTagged, const int nTaggedAll, TMCIter& mcIter) + { + static constexpr std::array HistPairAncestor = {"PCM/hPairAncestor", "EMC/hPairAncestor", "PCMEMC/hPairAncestor"}; + static constexpr std::array HistPrefilter = {"PCM/hPrefilter", "EMC/hPrefilter", "PCMEMC/hPrefilter"}; + static constexpr std::array HistPrefilterAll = {"PCM/hPrefilterAll", "EMC/hPrefilterAll", "PCMEMC/hPrefilterAll"}; + + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), g2.eta(), g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + + registry.fill(HIST("hMesonCuts"), 1); + if (openingAngle <= mesonConfig.minOpenAngle) { + registry.fill(HIST("hMesonCuts"), 2); + return std::nullopt; + } + registry.fill(HIST("hMesonCuts"), 6); + + const PhotonTruth pairTruth = std::max(truth1, truth2); + const PairBucket bucket = classifyPair(chain1, chain2, mcIter); + registry.fill(HIST(HistPairAncestor[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(pairTruth), static_cast(bucket)); + const int prefilterClass = static_cast(prefilterClassOf(bucket, chain1, chain2, mcIter)); + registry.fill(HIST(HistPrefilter[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, prefilterClass, nTagged); + if (nTaggedAll >= 0) { + registry.fill(HIST(HistPrefilterAll[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, prefilterClass, nTaggedAll); + } + if (bucket == PairBucket::kCrossOtherHadron) { + diagnoseOtherHadron(chain1, chain2, pairTruth, vMeson, mcIter); + } + if (mesonConfig.cfgEnableQA.value) { + registry.fill(HIST("mesonQA/hInvMassPt"), vMeson.M(), vMeson.Pt()); + } + return bucket; + } + + /// \brief eta a cluster would have if its own vertex vzOld is swapped for vzNew, same as in taskPi0FlowEMC + /// \param etaOld old eta value + /// \param vzOld old primary vertex z position + /// \param vzNew new primary vertex z position + /// \param emcalR radius of the EMCal + static float correctEtaForVertexShift(float etaOld, float vzOld, float vzNew, float emcalR) + { + return std::asinh(std::sinh(etaOld) + (vzOld - vzNew) / emcalR); + } + + /// \brief Apply the pair cuts to a mixed event pair and fill the mixed event histogram + /// \param eta2 eta of the second photon, possibly re-projected onto the vertex of the first collision + template + void fillMixedPair(TPhoton1 const& g1, TPhoton2 const& g2, const float eta2, const PhotonTruth truth1, const PhotonTruth truth2, const float centOrMult) + { + static constexpr std::array HistMixed = {"PCM/hMixed", "EMC/hMixed", "PCMEMC/hMixed"}; + + ROOT::Math::PtEtaPhiMVector v1(g1.pt(), g1.eta(), g1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector v2(g2.pt(), eta2, g2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vMeson = v1 + v2; + float openingAngle = std::acos(v1.Vect().Dot(v2.Vect()) / (v1.P() * v2.P())); + if (openingAngle <= mesonConfig.minOpenAngle) { + return; + } + registry.fill(HIST(HistMixed[pairType]), vMeson.M(), vMeson.Pt(), centOrMult, static_cast(std::max(truth1, truth2))); + } + + /// \brief Size of a table that is only subscribed in some process functions, nullptr otherwise + template + static std::size_t optionalTableSize(TTable const& table) + { + if constexpr (std::is_same_v) { + return 0; + } else { + return table.size(); + } + } + + /// \brief PCM-PCM same event + /// \tparam AllPairTypes also tag the photons for the pi0 prefilter with EMC-EMC and PCM-EMC pairs (needs the EMC tables, nullptr otherwise) + template + void runPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles, TClusters const& clusters, TMatchedPrims const& matchedPrims, TMatchedSeconds const& matchedSeconds) + { + if (photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsPCM, mTruthPCM, photons.size()); + fillPcmCache(photons, legs, mcParticles); + mPrefilterPCM.assign(photons.size(), 0); + + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + + // EMC clusters only for the prefilter tagging: no chains, no cut QA + EMBitFlags emcFlags(optionalTableSize(clusters)); + if constexpr (AllPairTypes) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + } + + auto mcIter = mcParticles.begin(); + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagSameKind(photonsPerCollision, v0flags, mPrefilterPCM); + if constexpr (AllPairTypes) { + tagAllPairTypes(photonsPerCollision, clusters.sliceBy(perCollisionEMC, collision.globalIndex()), v0flags, emcFlags); + } + + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (!(v0flags.test(g1.globalIndex())) || !(v0flags.test(g2.globalIndex()))) { + continue; + } + const int nTagged = mPrefilterPCM[g1.globalIndex()] + mPrefilterPCM[g2.globalIndex()]; + int nTaggedAll = -1; + if constexpr (AllPairTypes) { + nTaggedAll = mPrefilterAllPCM[g1.globalIndex()] + mPrefilterAllPCM[g2.globalIndex()]; + } + fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsPCM[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); + } + } + } + + /// \brief EMC-EMC same event + /// \tparam AllPairTypes also tag the photons for the pi0 prefilter with PCM-PCM and PCM-EMC pairs (needs the PCM table, nullptr otherwise) + template + void runEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles, TPcmPhotons const& photons) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillEmcCache(clusters, mcIter); + mPrefilterEMC.assign(clusters.size(), 0); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + + // PCM photons only for the prefilter tagging: no chains, no cut QA + EMBitFlags v0flags(optionalTableSize(photons)); + if constexpr (AllPairTypes) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + } + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagSameKind(photonsPerCollision, emcFlags, mPrefilterEMC); + if constexpr (AllPairTypes) { + tagAllPairTypes(photons.sliceBy(perCollisionPCM, collision.globalIndex()), photonsPerCollision, v0flags, emcFlags); + } + + for (const auto& [g1, g2] : combinations(CombinationsStrictlyUpperIndexPolicy(photonsPerCollision, photonsPerCollision))) { + if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + const int nTagged = mPrefilterEMC[g1.globalIndex()] + mPrefilterEMC[g2.globalIndex()]; + int nTaggedAll = -1; + if constexpr (AllPairTypes) { + nTaggedAll = mPrefilterAllEMC[g1.globalIndex()] + mPrefilterAllEMC[g2.globalIndex()]; + } + fillPair(g1, g2, mChainsEMC[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); + } + } + } + + // PCM-PCM same event + void processPcm(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + { + runPcm(collisions, photons, legs, mcParticles, nullptr, nullptr, nullptr); + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcm, "Process for pcm photons", false); + + // PCM-PCM same event, pi0 prefilter study also with the photons tagged by all three pair types (needs the EMC tables) + void processPcmPrefilterAll(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + runPcm(collisions, photons, legs, mcParticles, clusters, matchedPrims, matchedSeconds); + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmPrefilterAll, "Process for pcm photons, pi0 prefilter also with all pair types (instead of processPcm)", false); + + // EMCal-EMCal same event + void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + runEmcal(collisions, clusters, matchedPrims, matchedSeconds, mcParticles, nullptr); + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcal, "Process for emcal photons", false); + + // EMCal-EMCal same event, pi0 prefilter study also with the photons tagged by all three pair types (needs the PCM tables) + void processEmcalPrefilterAll(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, PcmPhotons const& photons, PcmMcLegs const&, McParticles const& mcParticles) + { + runEmcal(collisions, clusters, matchedPrims, matchedSeconds, mcParticles, photons); + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcalPrefilterAll, "Process for emcal photons, pi0 prefilter also with all pair types (instead of processEmcal)", false); + + // PCM-EMCal same event + void processPcmEmcal(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0 || photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // none of the photon tables is filtered, so its size is the size of the full table the globalIndex points into + resetCache(mChainsPCM, mTruthPCM, photons.size()); + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillPcmCache(photons, legs, mcParticles); + fillEmcCache(clusters, mcIter); + mPrefilterPCM.assign(photons.size(), 0); + mPrefilterEMC.assign(clusters.size(), 0); + const bool prefilterAll = mesonConfig.prefilterAllPairTypes.value; + if (prefilterAll) { + mPrefilterAllPCM.assign(photons.size(), 0); + mPrefilterAllEMC.assign(clusters.size(), 0); + } + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, ®istry); + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons, ®istry); + + for (const auto& collision : collisions) { + initCCDB(collision); + if (!isFullEventSelected(collision, true)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(collision); + auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex()); + auto photonsPCMPerCollision = photons.sliceBy(perCollisionPCM, collision.globalIndex()); + + // pi0 prefilter: tag both photons of every pair inside the pi0 window, with the pair type of this process function + tagPcmEmc(photonsPCMPerCollision, photonsEMCPerCollision, v0flags, emcFlags, mPrefilterPCM, mPrefilterEMC); + if (prefilterAll) { + tagAllPairTypes(photonsPCMPerCollision, photonsEMCPerCollision, v0flags, emcFlags); + } + + // PCM - EMC loop, two different tables: all combinations + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photonsPCMPerCollision, photonsEMCPerCollision))) { + if (!(v0flags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + const int nTagged = mPrefilterPCM[g1.globalIndex()] + mPrefilterEMC[g2.globalIndex()]; + const int nTaggedAll = prefilterAll ? mPrefilterAllPCM[g1.globalIndex()] + mPrefilterAllEMC[g2.globalIndex()] : -1; + const auto bucket = fillPair(g1, g2, mChainsPCM[g1.globalIndex()], mChainsEMC[g2.globalIndex()], mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult, nTagged, nTaggedAll, mcIter); + if (bucket == PairBucket::kSameParticle) { + diagnoseSameParticle(g1, g2, legs, matchedPrims, matchedSeconds, mcIter); + } + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmEmcal, "Process for pcm and emcal photons", true); + + // PCM-PCM mixed event + void processPcmMixed(Colls const& collisions, PcmPhotons const& photons, PcmMcLegs const& legs, McParticles const& mcParticles) + { + if (photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsPCM, mTruthPCM, photons.size()); + fillPcmCache(photons, legs, mcParticles, false); + + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto photonTuple = std::make_tuple(photons); + SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, photonTuple, &cache}; + + for (const auto& [c1, photons1, c2, photons2] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photons1, photons2))) { + if (!(v0flags.test(g1.globalIndex())) || !(v0flags.test(g2.globalIndex()))) { + continue; + } + fillMixedPair(g1, g2, g2.eta(), mTruthPCM[g1.globalIndex()], mTruthPCM[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmMixed, "Process for pcm photons, mixed event", false); + + // EMCal-EMCal mixed event + void processEmcalMixed(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillEmcCache(clusters, mcIter, false); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto clusterTuple = std::make_tuple(clusters); + SameKindPair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, clusterTuple, &cache}; + + for (const auto& [c1, clusters1, c2, clusters2] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(clusters1, clusters2))) { + if (!(emcFlags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + // cluster 2 seen from the vertex of collision 1, as in taskPi0FlowEMC + const float eta2 = mixingConfig.cfgCorrectMixedVtxEta.value ? correctEtaForVertexShift(g2.eta(), c2.posZ(), c1.posZ(), mixingConfig.cfgEmcalEffRadius.value) : g2.eta(); + fillMixedPair(g1, g2, eta2, mTruthEMC[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processEmcalMixed, "Process for emcal photons, mixed event", false); + + // PCM-EMCal mixed event + void processPcmEmcalMixed(Colls const& collisions, EMCalPhotons const& clusters, PcmPhotons const& photons, PcmMcLegs const& legs, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, McParticles const& mcParticles) + { + if (clusters.size() <= 0 || photons.size() <= 0) { + LOG(info) << "Skipping DF because there are not photons!"; + return; + } + auto mcIter = mcParticles.begin(); + // the mixed pairs are built inside the DF, so the per DF cache is valid for them + resetCache(mChainsPCM, mTruthPCM, photons.size()); + resetCache(mChainsEMC, mTruthEMC, clusters.size()); + fillPcmCache(photons, legs, mcParticles, false); + fillEmcCache(clusters, mcIter, false); + + EMBitFlags emcFlags(clusters.size()); + fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds); + EMBitFlags v0flags(photons.size()); + fV0PhotonCut.AreSelectedRunning(v0flags, photons); + + BinningType binningMixedEvent{{mixingConfig.cfgVtxBins, mixingConfig.cfgCentBins}}; + auto associatedTables = std::make_tuple(photons, clusters); + Pair pair{binningMixedEvent, mixingConfig.cfgMixingDepth, -1, collisions, associatedTables, &cache}; + + for (const auto& [c1, photonsPCM, c2, photonsEMC] : pair) { + if (!isFullEventSelected(c1) || !isFullEventSelected(c2)) { + continue; + } + const float centOrMult = getCentralityOrMultiplicity(c1); + for (const auto& [g1, g2] : combinations(CombinationsFullIndexPolicy(photonsPCM, photonsEMC))) { + if (!(v0flags.test(g1.globalIndex())) || !(emcFlags.test(g2.globalIndex()))) { + continue; + } + fillMixedPair(g1, g2, g2.eta(), mTruthPCM[g1.globalIndex()], mTruthEMC[g2.globalIndex()], centOrMult); + } + } + } + PROCESS_SWITCH(TwoPhotonMcTask, processPcmEmcalMixed, "Process for pcm and emcal photons, mixed event", false); + +}; // End struct TwoPhotonMcTask + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGEM/PhotonMeson/Utils/AnalyticV0.h b/PWGEM/PhotonMeson/Utils/AnalyticV0.h new file mode 100644 index 00000000000..ede542509cf --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/AnalyticV0.h @@ -0,0 +1,342 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AnalyticV0.h +/// \brief Build a photon V0 candidate from two legs without a vertex fitter. +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_ANALYTICV0_H_ +#define PWGEM_PHOTONMESON_UTILS_ANALYTICV0_H_ + +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" // CalculateDCAFast, getPropMomentumFromTrackHelix (soa version) + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +struct LegParam { + std::array xyz{}; // conversion point, vertex-relative (cm) + std::array mom{}; // momentum at the conversion point (GeV/c) + int charge{0}; // +1 e+, -1 e- +}; + +inline o2::track::TrackParCov trackFromLeg(LegParam const& leg) +{ + std::array cov{}; // o2-linter: disable=magic-number (lab-frame covariance has 21 elements) + cov[0] = cov[2] = cov[5] = 0.01f; // o2-linter: disable=magic-number (placeholder sigma_xyz = 0.1 cm) + cov[9] = cov[14] = cov[20] = 1e-4f; // o2-linter: disable=magic-number (placeholder sigma_p = 0.01 GeV/c) + return {leg.xyz, leg.mom, cov, leg.charge, true}; +} + +inline std::array getPropMomentumFromTrackHelix(const float s, const o2::track::TrackParCov& track, + const o2::track::TrackAuxPar& trHelix, const float bz, float addPhi = 0.f) +{ + const float pt = track.getPt(); + const float phi0 = track.getPhi(); // global azimuth + const float dphi = -static_cast(track.getSign()) * 0.3f * bz * s / 100.f / pt; // o2-linter: disable=magic-number (0.3 GeV/(T m), s in cm) + const float dotProd = std::cos(phi0 + addPhi) * pt * trHelix.xC + std::sin(phi0 + addPhi) * pt * trHelix.yC; + if (dotProd < 0.f) { + addPhi -= o2::constants::math::PI; + } + const auto phi = RecoDecay::constrainAngle(phi0 + dphi + addPhi); + return {std::cos(phi) * pt, std::sin(phi) * pt, track.getTgl() * pt}; +} + +struct AnalyticV0 { + bool ok{false}; + std::array vtx{}; // conversion point (cm), vertex-relative + std::array mom{}; // photon momentum at the vertex (GeV/c) + float pca{999.f}; // distance of the two legs at the vertex (cm) + float cospa{-2.f}; // pointing of mom from the origin (= primary vertex) + float mee{-1.f}; // m(e+e-) at the vertex (GeV/c^2) + float dcaxy{999.f}; // line through vtx along mom: distance to the origin in xy (cm) + float dcaz{999.f}; // line through vtx along mom in z at the xy-closest point (cm) + float score{999.f}; // the builder's getScoreV0 + std::array, 2> legMom{}; // e+ and e- momentum at the vertex (GeV/c) + void armenteros(float& alpha, float& qt) const + { + const float pMag = std::sqrt(mom[0] * mom[0] + mom[1] * mom[1] + mom[2] * mom[2]); + if (pMag < 1e-9f) { // o2-linter: disable=magic-number (degenerate) + alpha = 2.f; + qt = 999.f; + return; + } + const auto& pp = legMom[0]; + const auto& pn = legMom[1]; + const float plP = (pp[0] * mom[0] + pp[1] * mom[1] + pp[2] * mom[2]) / pMag; + const float plN = (pn[0] * mom[0] + pn[1] * mom[1] + pn[2] * mom[2]) / pMag; + const float pP2 = pp[0] * pp[0] + pp[1] * pp[1] + pp[2] * pp[2]; + alpha = (plP + plN > 1e-9f) ? (plP - plN) / (plP + plN) : 2.f; // o2-linter: disable=magic-number (degenerate) + qt = std::sqrt(std::max(0.f, pP2 - plP * plP)); + } + [[nodiscard]] float rxy() const { return std::hypot(vtx[0], vtx[1]); } + [[nodiscard]] float pt() const { return std::hypot(mom[0], mom[1]); } + [[nodiscard]] float eta() const + { + const float p = std::sqrt(mom[0] * mom[0] + mom[1] * mom[1] + mom[2] * mom[2]); + return (p > mom[2]) ? 0.5f * std::log((p + mom[2]) / (p - mom[2])) : 0.f; + } + [[nodiscard]] float phi() const { return RecoDecay::constrainAngle(std::atan2(mom[1], mom[0])); } +}; + +inline float analyticScoreV0(float cospa, float pca, float w) +{ + return w * 60.f * std::acos(cospa) + (1.f - w) * pca / 3.f; // o2-linter: disable=magic-number (getScoreV0 of the builder) +} + +inline AnalyticV0 buildAnalyticV0(LegParam const& pos, LegParam const& neg, float bzkG, float scoreWeight) +{ + constexpr float kMe = 0.000510999f; // electron mass, GeV/c^2 + constexpr float kTiny = 1e-6f; + AnalyticV0 v; + if (std::fabs(bzkG) < 0.1f) { // o2-linter: disable=magic-number (no field) + return v; + } + const auto tP = trackFromLeg(pos); + const auto tN = trackFromLeg(neg); + const o2::track::TrackAuxPar h1(tP, bzkG); + const o2::track::TrackAuxPar h2(tN, bzkG); + + // 1. xy + const float dcx = h2.xC - h1.xC, dcy = h2.yC - h1.yC; + const float d = std::hypot(dcx, dcy); + if (d < 1e-3f) { // o2-linter: disable=magic-number (concentric circles) + return v; + } + const float ux = dcx / d, uy = dcy / d; + const float r1 = h1.rC, r2 = h2.rC; + + int nCand = 0; + std::array c1x{}, c1y{}, c2x{}, c2y{}; + float pcaXY = 0.f; + if (d > r1 + r2) { // external gap: tangent points along the centre line + c1x[0] = h1.xC + r1 * ux; + c1y[0] = h1.yC + r1 * uy; + c2x[0] = h2.xC - r2 * ux; + c2y[0] = h2.yC - r2 * uy; + pcaXY = d - r1 - r2; + nCand = 1; + } else if (d < std::fabs(r1 - r2)) { // one circle inside the other: closest points on the far side + const float sgn = (r1 > r2) ? 1.f : -1.f; + c1x[0] = h1.xC + sgn * r1 * ux; + c1y[0] = h1.yC + sgn * r1 * uy; + c2x[0] = h2.xC + sgn * r2 * ux; + c2y[0] = h2.yC + sgn * r2 * uy; + pcaXY = std::fabs(r1 - r2) - d; + nCand = 1; + } else { // the circles cross: two points with xy distance 0 + const float a = (r1 * r1 - r2 * r2 + d * d) / (2.f * d); + const float hh = std::sqrt(std::max(0.f, r1 * r1 - a * a)); + const float bx = h1.xC + a * ux, by = h1.yC + a * uy; + c1x[0] = c2x[0] = bx - hh * uy; + c1y[0] = c2y[0] = by + hh * ux; + c1x[1] = c2x[1] = bx + hh * uy; + c1y[1] = c2y[1] = by - hh * ux; + pcaXY = 0.f; + nCand = 2; + } + + int best = 0; + float z1 = 0.f, z2 = 0.f, dzBest = 1e9f; + for (int ic = 0; ic < nCand; ++ic) { + const o2::math_utils::Point3D xy{0.5f * (c1x[ic] + c2x[ic]), 0.5f * (c1y[ic] + c2y[ic]), 0.f}; + const auto dcaP = CalculateDCAFast(tP, xy, bzkG); + const auto dcaN = CalculateDCAFast(tN, xy, bzkG); + if (std::fabs(dcaP[1] - dcaN[1]) < dzBest) { + dzBest = std::fabs(dcaP[1] - dcaN[1]); + z1 = dcaP[1]; + z2 = dcaN[1]; + best = ic; + } + } + const float p1x = c1x[best], p1y = c1y[best]; + const float p2x = c2x[best], p2y = c2y[best]; + const float convX = 0.5f * (p1x + p2x), convY = 0.5f * (p1y + p2y); + const float convZ = 0.5f * (z1 + z2); + v.vtx = {convX, convY, convZ}; + v.pca = std::hypot(pcaXY, z1 - z2); + + auto arcTo = [](o2::track::TrackAuxPar const& h, float xTrk, float yTrk, float px_, float py_) { + const float th0 = std::atan2(yTrk - h.yC, xTrk - h.xC); + const float thv = std::atan2(py_ - h.yC, px_ - h.xC); + const auto dth = RecoDecay::constrainAngle(thv - th0, -o2::constants::math::PI); + return std::fabs(h.rC * dth); + }; + const auto gP = tP.getXYZGlo(); + const auto gN = tN.getXYZGlo(); + const float bzT = bzkG / 10.f; // o2-linter: disable=magic-number (kGauss -> Tesla) + const auto pP = getPropMomentumFromTrackHelix(arcTo(h1, gP.X(), gP.Y(), p1x, p1y), tP, h1, bzT); + const auto pN = getPropMomentumFromTrackHelix(arcTo(h2, gN.X(), gN.Y(), p2x, p2y), tN, h2, bzT); + v.mom = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + v.legMom = {pP, pN}; + + const float pMag = std::sqrt(v.mom[0] * v.mom[0] + v.mom[1] * v.mom[1] + v.mom[2] * v.mom[2]); + const float vMag = std::sqrt(convX * convX + convY * convY + convZ * convZ); + if (pMag < kTiny || vMag < 1e-3f) { // o2-linter: disable=magic-number (degenerate vertex) + return v; + } + v.cospa = std::clamp((convX * v.mom[0] + convY * v.mom[1] + convZ * v.mom[2]) / (pMag * vMag), -1.f, 1.f); + const float ptSum = std::hypot(v.mom[0], v.mom[1]); + if (ptSum > kTiny) { + v.dcaxy = std::fabs(convX * v.mom[1] - convY * v.mom[0]) / ptSum; + const float t = -(convX * v.mom[0] + convY * v.mom[1]) / (ptSum * ptSum); + v.dcaz = std::fabs(convZ + t * v.mom[2]); + } + const float e0 = std::sqrt(pP[0] * pP[0] + pP[1] * pP[1] + pP[2] * pP[2] + kMe * kMe); + const float e1 = std::sqrt(pN[0] * pN[0] + pN[1] * pN[1] + pN[2] * pN[2] + kMe * kMe); + v.mee = std::sqrt(std::max(0.f, (e0 + e1) * (e0 + e1) - pMag * pMag)); + v.score = analyticScoreV0(v.cospa, v.pca, scoreWeight); + v.ok = true; + return v; +} + +struct PhotonLikeCuts { + float maxDca{1.5f}, minR{1.f}, maxR{90.f}, minCosPA{0.98f}, maxMee{0.1f}, maxDcaXY{3.f}, maxDcaZ{999.f}; // o2-linter: disable=magic-number (defaults = crosspair_group) +}; + +inline bool isPhotonLike(AnalyticV0 const& v, PhotonLikeCuts const& c) +{ + return v.ok && v.pca < c.maxDca && v.rxy() > c.minR && v.rxy() < c.maxR && + v.cospa > c.minCosPA && v.mee < c.maxMee && v.dcaxy < c.maxDcaXY && v.dcaz < c.maxDcaZ; +} + +struct AltPairing { + bool fitted{false}; + float dca{999.f}, rxy{-1.f}, cospa{-2.f}, mee{-1.f}, dcaz{999.f}, dcaxy{999.f}; +}; + +//_______________________________________________________________________ +/// \brief Build the candidate with the DCAFitter +/// \param pos e+ leg +/// \param neg e- leg +/// \param bzT field in Tesla +/// \param scoreWeight w of analyticScoreV0 +/// \return the candidate; ok = false when the fitter does not converge +inline AnalyticV0 buildFitterV0(o2::track::TrackParCov tPos, o2::track::TrackParCov tEle, float bzT, float scoreWeight) +{ + constexpr float kMe = 0.000510999f; // electron mass, GeV/c^2 + constexpr float kTiny = 1e-12f; + static o2::vertexing::DCAFitterN<2> fitter; + AnalyticV0 v; + if (std::fabs(bzT) < 1e-9f) { // o2-linter: disable=magic-number (no field) + return v; + } + fitter.setBz(10.f * bzT); // o2-linter: disable=magic-number (Tesla -> kGauss) + fitter.setPropagateToPCA(true); + fitter.setMaxR(200.f); // o2-linter: disable=magic-number (fitter range, cm) + fitter.setMaxDZIni(8.f); // o2-linter: disable=magic-number (as the SVertexer photon tune) + fitter.setMaxDXYIni(8.f); // o2-linter: disable=magic-number (as the SVertexer photon tune) + fitter.setMaxChi2(1e9f); // o2-linter: disable=magic-number (no chi2 cut; the distance is judged afterwards) + fitter.setMinParamChange(1e-3f); // o2-linter: disable=magic-number (fitter convergence) + fitter.setMinRelChi2Change(0.9f); // o2-linter: disable=magic-number (fitter convergence) + fitter.setUseAbsDCA(true); + fitter.setCollinear(true); + if (fitter.process(tPos, tEle) == 0) { + return v; + } + if (!fitter.isPropagateTracksToVertexDone(0) && !fitter.propagateTracksToVertex(0)) { + return v; + } + const auto& vtx = fitter.getPCACandidatePos(0); + std::array x0{}, x1{}, p0{}, p1{}; + fitter.getTrack(0, 0).getXYZGlo(x0); + fitter.getTrack(1, 0).getXYZGlo(x1); + if (!fitter.getTrack(0, 0).getPxPyPzGlo(p0) || !fitter.getTrack(1, 0).getPxPyPzGlo(p1)) { + return v; + } + v.vtx = {static_cast(vtx[0]), static_cast(vtx[1]), static_cast(vtx[2])}; + v.mom = {p0[0] + p1[0], p0[1] + p1[1], p0[2] + p1[2]}; + v.legMom = {p0, p1}; + v.pca = std::sqrt((x0[0] - x1[0]) * (x0[0] - x1[0]) + (x0[1] - x1[1]) * (x0[1] - x1[1]) + (x0[2] - x1[2]) * (x0[2] - x1[2])); + const float pMag = std::sqrt(v.mom[0] * v.mom[0] + v.mom[1] * v.mom[1] + v.mom[2] * v.mom[2]); + const float vMag = std::sqrt(v.vtx[0] * v.vtx[0] + v.vtx[1] * v.vtx[1] + v.vtx[2] * v.vtx[2]); + if (pMag < kTiny || vMag < kTiny) { + return v; + } + v.cospa = std::clamp((v.vtx[0] * v.mom[0] + v.vtx[1] * v.mom[1] + v.vtx[2] * v.mom[2]) / (pMag * vMag), -1.f, 1.f); + const float ptSum = std::hypot(v.mom[0], v.mom[1]); + if (ptSum > kTiny) { + v.dcaxy = std::fabs(v.vtx[0] * v.mom[1] - v.vtx[1] * v.mom[0]) / ptSum; + const float t = -(v.vtx[0] * v.mom[0] + v.vtx[1] * v.mom[1]) / (ptSum * ptSum); + v.dcaz = std::fabs(v.vtx[2] + t * v.mom[2]); + } + const float e0 = std::sqrt(p0[0] * p0[0] + p0[1] * p0[1] + p0[2] * p0[2] + kMe * kMe); + const float e1 = std::sqrt(p1[0] * p1[0] + p1[1] * p1[1] + p1[2] * p1[2] + kMe * kMe); + v.mee = std::sqrt(std::max(0.f, (e0 + e1) * (e0 + e1) - pMag * pMag)); + v.score = analyticScoreV0(v.cospa, v.pca, scoreWeight); + v.ok = true; + return v; +} + +//_______________________________________________________________________ +/// \brief buildFitterV0 for two legs given as (point, momentum, charge) +inline AnalyticV0 buildFitterV0(LegParam const& pos, LegParam const& neg, float bzT, float scoreWeight) +{ + constexpr float kFitterMaxR = 200.f; // o2-linter: disable=magic-number (the fitter's own setMaxR) + const AnalyticV0 seed = buildAnalyticV0(pos, neg, 10.f * bzT, scoreWeight); // o2-linter: disable=magic-number (Tesla -> kGauss) + if (!seed.ok || seed.rxy() > kFitterMaxR) { + return AnalyticV0{}; + } + return buildFitterV0(trackFromLeg(pos), trackFromLeg(neg), bzT, scoreWeight); +} + +//_______________________________________________________________________ +/// \brief Move a track in the global frame +/// \param t the track; its local frame is rotated by alpha, so a global shift enters x and y rotated +/// \param dx shift in global x (cm) +/// \param dy shift in global y (cm) +/// \param dz shift in global z (cm) +inline void translateTrackParCov(o2::track::TrackParCov& t, float dx, float dy, float dz) +{ + const float ca = std::cos(t.getAlpha()), sa = std::sin(t.getAlpha()); + t.setX(t.getX() + ca * dx + sa * dy); + t.setY(t.getY() - sa * dx + ca * dy); + t.setZ(t.getZ() + dz); +} + +//_______________________________________________________________________ +/// \brief The DCAFitter alternative pairing +/// \param pos e+ leg +/// \param neg e- leg +/// \param bzT field in Tesla +/// \return AltPairing; fitted = false when the fitter does not converge +inline AltPairing evaluateAltPairingFitter(LegParam const& pos, LegParam const& neg, float bzT) +{ + const AnalyticV0 v = buildFitterV0(pos, neg, bzT, 0.5f); // o2-linter: disable=magic-number (score not used here) + AltPairing r; + if (!v.ok) { + return r; + } + r.fitted = true; + r.dca = v.pca; + r.rxy = v.rxy(); + r.cospa = v.cospa; + r.mee = v.mee; + r.dcaxy = v.dcaxy; + r.dcaz = v.dcaz; + return r; +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_ANALYTICV0_H_ diff --git a/PWGEM/PhotonMeson/Utils/ClusterHistograms.h b/PWGEM/PhotonMeson/Utils/ClusterHistograms.h index 6e51d09ab40..73aece33282 100644 --- a/PWGEM/PhotonMeson/Utils/ClusterHistograms.h +++ b/PWGEM/PhotonMeson/Utils/ClusterHistograms.h @@ -19,12 +19,13 @@ #include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h" #include -#include +#include #include #include #include +#include #include #include @@ -112,7 +113,7 @@ inline void fillClusterHistograms(o2::framework::HistogramRegistry* fRegistry, T const auto eta = cluster.eta(); const auto phi = cluster.phi(); - static constexpr std::string_view ClusterTypes[2] = {"Cluster/before/", "Cluster/after/"}; + static constexpr std::array ClusterTypes = {"Cluster/before/", "Cluster/after/"}; if constexpr (HasTrackMatching) { for (size_t iTrack = 0; iTrack < cluster.deltaEta().size(); ++iTrack) { @@ -129,7 +130,7 @@ inline void fillClusterHistograms(o2::framework::HistogramRegistry* fRegistry, T } } - if constexpr (HasPrimaries && IsTrackContainer) { + if constexpr (HasPrimaries && IsFullTrackContainer) { for (const auto& primTrack : primTracks) { if (do2DQA) { fillTrackQA2D(fRegistry, HIST(ClusterTypes[cls_id]), e, primTrack.deltaEta(), primTrack.deltaPhi(), primTrack.trackP(), primTrack.trackPt(), weight); @@ -139,7 +140,7 @@ inline void fillClusterHistograms(o2::framework::HistogramRegistry* fRegistry, T } } - if constexpr (HasSecondaries && IsTrackContainer) { + if constexpr (HasSecondaries && IsFullTrackContainer) { for (const auto& secTrack : secTracks) { const auto trackP = secTrack.trackP(); if (do2DQA) { @@ -165,11 +166,11 @@ inline void fillClusterHistograms(o2::framework::HistogramRegistry* fRegistry, T // primary matched tracks are stored directly inside cluster fRegistry->fill(HIST(ClusterTypes[cls_id]) + HIST("hNTracks"), cluster.deltaEta().size(), weight); - } else if constexpr (HasPrimaries && IsTrackContainer) { + } else if constexpr (HasPrimaries && IsFullTrackContainer) { // primary matched tracks are stored as their own table fRegistry->fill(HIST(ClusterTypes[cls_id]) + HIST("hNTracks"), primTracks.size(), weight); } - if constexpr (HasSecondaries && IsTrackContainer) { + if constexpr (HasSecondaries && IsFullTrackContainer) { // secondary matched tracks are stored as their own table fRegistry->fill(HIST(ClusterTypes[cls_id]) + HIST("hNSecTracks"), secTracks.size(), weight); } diff --git a/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h b/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h new file mode 100644 index 00000000000..2749319def2 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/ConversionPointProfile.h @@ -0,0 +1,177 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ConversionPointProfile.h +/// \brief radius-profiled conversion point of a photon conversion (alternative to the helix touching point) +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ +#define PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +constexpr int NProfPar = 4; // (rphi, z, phi, tgl) +constexpr int NProfR = 75; +constexpr float ProfRMin = 10.f; // trial radii 10 .. 84 cm + +/// \brief chi2 of a leg pair vs the trial radius, its minimum and class +struct RadiusProfile { + std::array chi2{}; + std::array ok{}; + int status = 4; + int iMin = -1; + float rMin = -1.f; + float chi2Min = -1.f; + float halfWidth = -1.f; + std::array xyz{-999.f, -999.f, -999.f}; +}; + +/// \brief parameters and covariance of one leg at every trial radius +struct LegScan { + std::array ok{}; + std::array, NProfR> m{}; // phi of the point, z, phi of the direction, tgl + std::array, NProfR> c{}; // lower triangle of the (rphi, z, phi, tgl) covariance + std::array, NProfR> pos{}; // global point +}; + +//_______________________________________________________________________ +/// \brief propagate one leg inward from its IU to a radius and keep its parameters there +/// \param trackIU leg at its innermost update +/// \param bz magnetic field +/// \param matCorr material correction of the propagation +/// \param out output: parameters and covariance per radius, ok = false outside the reach of the leg +inline void cpScanLeg(o2::track::TrackParCov const& trackIU, const float bz, const o2::base::Propagator::MatCorrType matCorr, LegScan& out) +{ + auto trk = trackIU; + const auto iu = trk.getXYZGlo(); + const float rIU = std::hypot(iu.X(), iu.Y()); + for (int k = NProfR - 1; k >= 0; --k) { + const float r = ProfRMin + k; + out.ok[k] = false; + if (r >= rIU - 0.1f) { // o2-linter: disable=magic-number (margin) + continue; + } + float xl = 0.f; + if (!trk.getXatLabR(r, xl, bz, o2::track::DirInward)) { + break; + } + if (!o2::base::Propagator::Instance()->propagateToX(trk, xl, bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorr)) { + break; + } + const auto g = trk.getXYZGlo(); + const float snp = trk.getSnp(); + const float f = 1.f / std::sqrt(std::max(1.f - snp * snp, 1e-4f)); // o2-linter: disable=magic-number (floor) + out.m[k] = {std::atan2(g.Y(), g.X()), trk.getZ(), trk.getAlpha() + std::asin(std::clamp(snp, -1.f, 1.f)), trk.getTgl()}; + out.c[k] = {trk.getSigmaY2(), trk.getSigmaZY(), trk.getSigmaZ2(), trk.getSigmaSnpY() * f, trk.getSigmaSnpZ() * f, trk.getSigmaSnp2() * f * f, trk.getSigmaTglY(), trk.getSigmaTglZ(), trk.getSigmaTglSnp() * f, trk.getSigmaTgl2()}; + out.pos[k] = {g.X(), g.Y(), g.Z()}; + out.ok[k] = true; + } +} + +inline void cpProfileFinish(LegScan const& p, LegScan const& n, RadiusProfile& out); + +//_______________________________________________________________________ +/// \brief chi2 of a common point and a common direction of two legs at every trial radius, then minimum and class +/// \param p scan of the positive leg +/// \param n scan of the negative leg +/// \param zSig2Pos extra z variance of the positive leg (cm^2, legs without ITS) +/// \param zSig2Ele extra z variance of the negative leg (cm^2, legs without ITS) +/// \param out output: profile, minimum, half width and class +inline void cpRadiusProfile(LegScan const& p, LegScan const& n, const float zSig2Pos, const float zSig2Ele, RadiusProfile& out) +{ + out = RadiusProfile{}; + for (int k = 0; k < NProfR; ++k) { + out.ok[k] = p.ok[k] && n.ok[k]; + if (!out.ok[k]) { + continue; + } + const float r = ProfRMin + k; + ROOT::Math::SVector d; + d[0] = r * RecoDecay::constrainAngle(p.m[k][0] - n.m[k][0], -o2::constants::math::PI); + d[1] = p.m[k][1] - n.m[k][1]; + d[2] = RecoDecay::constrainAngle(p.m[k][2] - n.m[k][2], -o2::constants::math::PI); + d[3] = p.m[k][3] - n.m[k][3]; + ROOT::Math::SMatrix> cov; + int idx = 0; + for (int i = 0; i < NProfPar; ++i) { + for (int j = 0; j <= i; ++j) { + cov(i, j) = p.c[k][idx] + n.c[k][idx]; + ++idx; + } + } + cov(1, 1) += zSig2Pos + zSig2Ele; + if (!cov.Invert()) { + out.ok[k] = false; + continue; + } + out.chi2[k] = static_cast(ROOT::Math::Similarity(d, cov)); + if (out.iMin < 0 || out.chi2[k] < out.chi2Min) { + out.iMin = k; + out.chi2Min = out.chi2[k]; + } + } + cpProfileFinish(p, n, out); +} +//_______________________________________________________________________ +/// \brief minimum, half width of the dchi2 < 1 region, other minima and class of a filled profile +/// \param p scan of the positive leg (point at the minimum) +/// \param n scan of the negative leg (point at the minimum) +/// \param out profile, completed in place +inline void cpProfileFinish(LegScan const& p, LegScan const& n, RadiusProfile& out) +{ + if (out.iMin < 0) { + return; + } + out.rMin = ProfRMin + out.iMin; + out.xyz = {0.5f * (p.pos[out.iMin][0] + n.pos[out.iMin][0]), 0.5f * (p.pos[out.iMin][1] + n.pos[out.iMin][1]), 0.5f * (p.pos[out.iMin][2] + n.pos[out.iMin][2])}; + // valid range, dchi2 < 1 region around the minimum, other minima + int kLo = out.iMin; + int kHi = kLo; + while (kLo - 1 >= 0 && out.ok[kLo - 1] && out.chi2[kLo - 1] - out.chi2Min < 1.f) { + --kLo; + } + while (kHi + 1 < NProfR && out.ok[kHi + 1] && out.chi2[kHi + 1] - out.chi2Min < 1.f) { + ++kHi; + } + out.halfWidth = 0.5f * (kHi - kLo) + 0.5f; + const bool edge = (out.iMin == 0 || !out.ok[out.iMin - 1]) || (out.iMin == NProfR - 1 || !out.ok[out.iMin + 1]); + bool other = false; + for (int k = 1; k < NProfR - 1 && !other; ++k) { + if (k >= kLo && k <= kHi) { + continue; + } + if (out.ok[k] && out.ok[k - 1] && out.ok[k + 1] && out.chi2[k] <= out.chi2[k - 1] && out.chi2[k] <= out.chi2[k + 1] && out.chi2[k] - out.chi2Min < 1.f) { + other = true; + } + } + out.status = edge ? 3 : (other ? 2 : (out.halfWidth > 5.f ? 1 : 0)); // o2-linter: disable=magic-number (broad: half width above 5 cm) +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_CONVERSIONPOINTPROFILE_H_ diff --git a/PWGEM/PhotonMeson/Utils/EMPhoton.h b/PWGEM/PhotonMeson/Utils/EMPhoton.h index cc50f02855e..fb24ab96b52 100644 --- a/PWGEM/PhotonMeson/Utils/EMPhoton.h +++ b/PWGEM/PhotonMeson/Utils/EMPhoton.h @@ -15,41 +15,48 @@ #ifndef PWGEM_PHOTONMESON_UTILS_EMPHOTON_H_ #define PWGEM_PHOTONMESON_UTILS_EMPHOTON_H_ -#include +#include "PWGEM/PhotonMeson/Utils/PairUtilities.h" -#include +#include namespace o2::aod::pwgem::photonmeson::utils { class EMPhoton { public: - EMPhoton(float pt, float eta, float phi, float mass) + EMPhoton(float pt, float eta, float phi, float mass) : fPt(pt), fEta(eta), fPhi(phi), fMass(mass) { - fPt = pt; - fEta = eta; - fPhi = phi; - fMass = mass; } - ~EMPhoton() {} + ~EMPhoton() = default; + + [[nodiscard]] float pt() const { return fPt; } + [[nodiscard]] float eta() const { return fEta; } + [[nodiscard]] float phi() const { return fPhi; } - float pt() const { return fPt; } - float eta() const { return fEta; } - float phi() const { return fPhi; } + [[nodiscard]] float p() const { return fPt * std::cosh(fEta); } + [[nodiscard]] float px() const { return fPt * std::cos(fPhi); } + [[nodiscard]] float py() const { return fPt * std::sin(fPhi); } + [[nodiscard]] float pz() const { return fPt * std::sinh(fEta); } + [[nodiscard]] float mass() const { return fMass; } + [[nodiscard]] float rapidity() const { return std::log((std::sqrt(std::pow(fMass, 2) + std::pow(fPt * std::cosh(fEta), 2)) + fPt * std::sinh(fEta)) / std::sqrt(std::pow(fMass, 2) + std::pow(fPt, 2))); } - float p() const { return fPt * std::cosh(fEta); } - float px() const { return fPt * std::cos(fPhi); } - float py() const { return fPt * std::sin(fPhi); } - float pz() const { return fPt * std::sinh(fEta); } - float mass() const { return fMass; } - float rapidity() const { return std::log((std::sqrt(std::pow(fMass, 2) + std::pow(fPt * std::cosh(fEta), 2)) + fPt * std::sinh(fEta)) / std::sqrt(std::pow(fMass, 2) + std::pow(fPt, 2))); } + // optional leg track-composition info for pair-level photon-class selections (PCM only) + void setLegCounts(o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonLegCounts const& c) + { + fLegCounts = c; + fHasLegCounts = true; + } + [[nodiscard]] o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonLegCounts const& legCounts() const { return fLegCounts; } + [[nodiscard]] bool hasLegCounts() const { return fHasLegCounts; } protected: - float fPt; - float fEta; - float fPhi; - float fMass; + float fPt{0.f}; + float fEta{0.f}; + float fPhi{0.f}; + float fMass{0.f}; + o2::aod::pwgem::photonmeson::utils::pairutil::V0PhotonLegCounts fLegCounts{}; + bool fHasLegCounts{false}; }; } // namespace o2::aod::pwgem::photonmeson::utils diff --git a/PWGEM/PhotonMeson/Utils/EventHistograms.h b/PWGEM/PhotonMeson/Utils/EventHistograms.h index 6991e399625..f9f9fcdec0b 100644 --- a/PWGEM/PhotonMeson/Utils/EventHistograms.h +++ b/PWGEM/PhotonMeson/Utils/EventHistograms.h @@ -19,12 +19,13 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/TriggerAliases.h" -#include +#include #include #include #include +#include #include namespace o2::aod::pwgem::photonmeson::utils::eventhistogram @@ -46,34 +47,13 @@ inline void addEventHistograms(o2::framework::HistogramRegistry* fRegistry, bool hCollisionCounter->GetXaxis()->SetBinLabel(11, "EMC L0 Triggered"); hCollisionCounter->GetXaxis()->SetBinLabel(12, "accepted"); - const o2::framework::AxisSpec axis_cent_ft0m{{0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110}, - "centrality FT0M (%)"}; - - const o2::framework::AxisSpec axis_cent_ft0a{{0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110}, - "centrality FT0A (%)"}; - - const o2::framework::AxisSpec axis_cent_ft0c{{0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110}, - "centrality FT0C (%)"}; - fRegistry->add("Event/before/hZvtx", "vertex z; Z_{vtx} (cm)", o2::framework::HistType::kTH1D, {{220, -11, +11}}, useWeight); fRegistry->add("Event/before/hMultNTracksPV", "hMultNTracksPV; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); fRegistry->add("Event/before/hMultNTracksPVeta1", "hMultNTracksPVeta1; N_{track} to PV", o2::framework::HistType::kTH1F, {{6001, -0.5, 6000.5}}, useWeight); fRegistry->add("Event/before/hMultFT0", "hMultFT0;mult. FT0A;mult. FT0C", o2::framework::HistType::kTH2F, {{200, 0, 200000}, {60, 0, 60000}}, useWeight); - fRegistry->add("Event/before/hCentFT0A", "hCentFT0A;centrality FT0A (%)", o2::framework::HistType::kTH1F, {{axis_cent_ft0a}}, useWeight); - fRegistry->add("Event/before/hCentFT0C", "hCentFT0C;centrality FT0C (%)", o2::framework::HistType::kTH1F, {{axis_cent_ft0c}}, useWeight); - fRegistry->add("Event/before/hCentFT0M", "hCentFT0M;centrality FT0M (%)", o2::framework::HistType::kTH1F, {{axis_cent_ft0m}}, useWeight); + fRegistry->add("Event/before/hCentFT0A", "hCentFT0A;centrality FT0A (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0C", "hCentFT0C;centrality FT0C (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); + fRegistry->add("Event/before/hCentFT0M", "hCentFT0M;centrality FT0M (%)", o2::framework::HistType::kTH1F, {{100, 0., 100.}}, useWeight); fRegistry->add("Event/before/hCentFT0CvsMultNTracksPV", "hCentFT0CvsMultNTracksPV;centrality FT0C (%);N_{track} to PV", o2::framework::HistType::kTH2F, {{110, 0, 110}, {500, 0, 5000}}, useWeight); fRegistry->add("Event/before/hMultFT0CvsMultNTracksPV", "hMultFT0CvsMultNTracksPV;mult. FT0C;N_{track} to PV", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 5000}}, useWeight); fRegistry->add("Event/before/hMultFT0CvsOccupancy", "hMultFT0CvsOccupancy;mult. FT0C;N_{track} in time range", o2::framework::HistType::kTH2F, {{60, 0, 60000}, {500, 0, 10000}}, useWeight); @@ -84,7 +64,7 @@ template void fillEventInfo(o2::framework::HistogramRegistry* fRegistry, TCollision const& collision, float weight = 1.) { const float maxZ = 10.f; - static constexpr std::string_view EventTypes[2] = {"before/", "after/"}; + static constexpr std::array EventTypes = {"before/", "after/"}; fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 1.0, weight); if (collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { fRegistry->fill(HIST("Event/") + HIST(EventTypes[ev_id]) + HIST("hCollisionCounter"), 2.0, weight); diff --git a/PWGEM/PhotonMeson/Utils/HNMUtilities.h b/PWGEM/PhotonMeson/Utils/HNMUtilities.h index 956529610a0..d8c7698e185 100644 --- a/PWGEM/PhotonMeson/Utils/HNMUtilities.h +++ b/PWGEM/PhotonMeson/Utils/HNMUtilities.h @@ -10,9 +10,7 @@ // or submit itself to any jurisdiction. /// /// \file HNMUtilities.h -/// /// \brief This code provides helper functions for the reconstruction of heavy neutral mesons (omega and eta meson) via their three pion decay -/// /// \author Nicolas Strangmann (nicolas.strangmann@cern.ch) - Goethe University Frankfurt /// @@ -23,7 +21,7 @@ #include "PWGEM/PhotonMeson/Utils/PairUtilities.h" #include -#include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) @@ -68,9 +66,8 @@ struct Photon { // -------> Struct to store gamma gamma pairs (pi0 or eta meson candidates) struct GammaGammaPair { - GammaGammaPair(Photon p1, Photon p2) : p1(p1), p2(p2) + GammaGammaPair(Photon const& p1, Photon const& p2) : p1(p1), p2(p2), vGG(p1.photon + p2.photon) { - vGG = p1.photon + p2.photon; } Photon p1, p2; ROOT::Math::PxPyPzEVector vGG; @@ -80,8 +77,8 @@ struct GammaGammaPair { ushort reconstructionType = photonpair::kNpair; void setReconstructionType(ushort type) { reconstructionType = type; } - float m() const { return vGG.M(); } - float pT() const { return vGG.Pt(); } + [[nodiscard]] float m() const { return vGG.M(); } + [[nodiscard]] float pT() const { return vGG.Pt(); } }; // -------> Enum to specify how the heavy neutral meson mass should be corrected based on the PDG mass of its light neutral meson decay daughter @@ -100,7 +97,7 @@ struct HeavyNeutralMeson { GammaGammaPair* gg = nullptr; ROOT::Math::PxPyPzEVector vHeavyNeutralMeson; - float m(int massCorrectionType) const + [[nodiscard]] float m(int massCorrectionType) const { float massHNM = vHeavyNeutralMeson.M(); switch (massCorrectionType) { @@ -118,30 +115,32 @@ struct HeavyNeutralMeson { } return massHNM; } - float pT() const { return vHeavyNeutralMeson.Pt(); } - float eta() const { return vHeavyNeutralMeson.Eta(); } - float phi() const { return vHeavyNeutralMeson.Phi(); } + [[nodiscard]] float pT() const { return vHeavyNeutralMeson.Pt(); } + [[nodiscard]] float eta() const { return vHeavyNeutralMeson.Eta(); } + [[nodiscard]] float phi() const { return vHeavyNeutralMeson.Phi(); } }; const int nSMEdges = 9; -inline float smPhiEdges[nSMEdges] = {1.75, 2.1, 2.45, 2.8, 3.14, 4., 4.89, 5.24, 5.58}; +inline const std::array smPhiEdges = {1.75, 2.1, 2.45, 2.8, 3.14, 4., 4.89, 5.24, 5.58}; inline int getSMNumber(float eta, float phi) { int smNumber = 0; for (int iPhiInterval = 0; iPhiInterval < nSMEdges; iPhiInterval++) { - if (phi > smPhiEdges[iPhiInterval]) + if (phi > smPhiEdges[iPhiInterval]) { smNumber = 2 * (iPhiInterval + 1); + } } - if (eta < 0) + if (eta < 0) { smNumber += 1; + } return smNumber; } /// \brief Store photons from EMC clusters and V0s in a vector and possibly add a eta and phi offset for alignment of EMCal clusters template -void storeGammasInVector(C clusters, V v0s, std::vector& vPhotons, std::array EMCEtaShift, std::array EMCPhiShift) +void storeGammasInVector(C clusters, V v0s, std::vector& vPhotons, std::array const& EMCEtaShift, std::array const& EMCPhiShift) { vPhotons.clear(); for (const auto& cluster : clusters) { @@ -154,13 +153,14 @@ void storeGammasInVector(C clusters, V v0s, std::vector& vPhotons, std:: vPhotons.push_back(Photon::fromEtaPhiEnergy(eta, phi, cluster.e())); } - for (const auto& v0 : v0s) + for (const auto& v0 : v0s) { vPhotons.push_back(Photon::fromPxPyPz(v0.px(), v0.py(), v0.pz())); + } } /// \brief Store photons from EMC clusters in a vector and possibly add a eta and phi offset for alignment of EMCal clusters -template -void storeGammasInVector(C clusters, std::vector& vPhotons, std::array EMCEtaShift, std::array EMCPhiShift) +template +void storeGammasInVector(Clusters clusters, std::vector& vPhotons, std::array const& EMCEtaShift, std::array const& EMCPhiShift) { vPhotons.clear(); for (const auto& cluster : clusters) { @@ -175,20 +175,20 @@ void storeGammasInVector(C clusters, std::vector& vPhotons, std::array vPhotons, std::vector& vGGs) +inline void reconstructGGs(std::vector const& vPhotons, std::vector& vGGs) { vGGs.clear(); // loop over all photon combinations and build meson candidates for (unsigned int ig1 = 0; ig1 < vPhotons.size(); ++ig1) { for (unsigned int ig2 = ig1 + 1; ig2 < vPhotons.size(); ++ig2) { GammaGammaPair lightMeson(vPhotons[ig1], vPhotons[ig2]); // build lightMeson from photons - if (vPhotons[ig1].isFromConversion && vPhotons[ig2].isFromConversion) + if (vPhotons[ig1].isFromConversion && vPhotons[ig2].isFromConversion) { lightMeson.setReconstructionType(photonpair::kPCMPCM); - else if (!vPhotons[ig1].isFromConversion && !vPhotons[ig2].isFromConversion) + } else if (!vPhotons[ig1].isFromConversion && !vPhotons[ig2].isFromConversion) { lightMeson.setReconstructionType(photonpair::kEMCEMC); - else + } else { lightMeson.setReconstructionType(photonpair::kPCMEMC); - + } vGGs.push_back(lightMeson); } } diff --git a/PWGEM/PhotonMeson/Utils/MCUtilities.h b/PWGEM/PhotonMeson/Utils/MCUtilities.h index 90568398c63..1326f513aef 100644 --- a/PWGEM/PhotonMeson/Utils/MCUtilities.h +++ b/PWGEM/PhotonMeson/Utils/MCUtilities.h @@ -16,27 +16,39 @@ #ifndef PWGEM_PHOTONMESON_UTILS_MCUTILITIES_H_ #define PWGEM_PHOTONMESON_UTILS_MCUTILITIES_H_ +#include "PWGEM/PhotonMeson/Utils/ParticleOrigin.h" + +#include #include +#include +#include #include #include +#include #include +#include #include +#include +#include +#include #include //_______________________________________________________________________ namespace o2::aod::pwgem::photonmeson::utils::mcutil { + +template +concept is_unfiltered_iterator = o2::soa::is_iterator && !o2::soa::has_filtered_policy; + +constexpr float kVertexEps = 1e-4f; // cm + template bool IsPhysicalPrimary(TTrack const& mctrack) { // This is to check mctrack is ALICE physical primary. - if (mctrack.isPhysicalPrimary() || mctrack.producedByGenerator()) { - return true; - } else { - return false; - } + return (mctrack.isPhysicalPrimary() || mctrack.producedByGenerator()); } //_______________________________________________________________________ template @@ -90,10 +102,11 @@ int IsXFromY(T const& mctrack, TMCs const& mcTracks, const int pdgX, const int p } return -1; } + //_______________________________________________________________________ // Go up the decay chain of a mcparticle looking for a mother with the given pdg codes, if found return this mothers daughter // E.g. Find the gamma that was created in a pi0 or eta decay -template +template int FindMotherInChain(T const& mcparticle, TMCs const& mcparticles, TTargetPDGs const& motherpdgs, const int Depth = 50) // o2-linter: disable=pdg/explicit-code (false positive) { if (!mcparticle.has_mothers() || Depth < 1) { @@ -104,10 +117,54 @@ int FindMotherInChain(T const& mcparticle, TMCs const& mcparticles, TTargetPDGs auto mother = mcparticles.iteratorAt(motherid); if (std::find(motherpdgs.begin(), motherpdgs.end(), mother.pdgCode()) != motherpdgs.end()) { return mcparticle.globalIndex(); // The mother has the required pdg code, so return its daughters global mc particle code. - } else { - return FindMotherInChain(mother, mcparticles, motherpdgs, Depth - 1); } + return FindMotherInChain(mother, mcparticles, motherpdgs, Depth - 1); } + +//_______________________________________________________________________ +/// \brief Go up the decay chain of a mcparticle looking for a mother with the given pdg codes, +/// and return that MOTHER's own index (unlike FindMotherInChain, which returns its daughter). +/// Two different particles that share the same meson ancestor will resolve to the same value here. +/// \param mcparticle iterator of McParticles +/// \param mcparticles table of McParticles +/// \param motherpdgs ranges of mother PDG values to compare against +/// \param Depth how many links should this go up +template +int GetMesonInChain(T const& mcparticle, TMCs const& mcparticles, TTargetPDGs const& motherpdgs, const int Depth = 15) +{ + int decayChildIdx = FindMotherInChain(mcparticle, mcparticles, motherpdgs, Depth); + if (decayChildIdx < 0) { + return -1; + } + auto decayChild = mcparticles.iteratorAt(decayChildIdx); + return decayChild.mothersIds()[0]; // the meson itself, not its daughter +} + +//_______________________________________________________________________ +/// \brief Go up the decay chain of a mcparticle looking for a mother with the given pdg codes, if found return this mothers daughter +/// E.g. Find the gamma that was created in a pi0 or eta decay +/// \param mcIter iterator of mcparticle -- WILL BE MODIFIED/CONSUMED by this function +/// \param motherPdgs target mother PDG values +/// \param depth how many steps in the chain this check should go maximum before failing +template +int findMotherInChain(T& mcIter, TTargetPDGs const& motherPdgs, const int depth = 50) +{ + int currentIndex = mcIter.globalIndex(); // the node whose immediate mother we're about to test + + for (int d = 0; d < depth; ++d) { + if (!mcIter.has_mothers()) { + return -1; + } + const int motherId = mcIter.mothersIds()[0]; + mcIter.setCursor(motherId); + if (std::find(motherPdgs.begin(), motherPdgs.end(), mcIter.pdgCode()) != motherPdgs.end()) { + return currentIndex; // mother matches -- return the node directly below it + } + currentIndex = motherId; // no match -- this mother becomes "current" for the next step up + } + return -1; +} + //_______________________________________________________________________ template int IsEleFromPC(T const& mctrack, TMCs const& mcTracks) @@ -132,7 +189,7 @@ int IsEleFromPC(T const& mctrack, TMCs const& mcTracks) return -1; } //_______________________________________________________________________ -template +template bool IsInAcceptanceNonDerived(TMCParticle const& mcparticle, TMCParticles const& mcparticles, TTargetPDGs target_pdgs, const float ymin, const float ymax, const float phimin, const float phimax) { // contents in vector of daughter ID is different. @@ -177,13 +234,10 @@ bool IsInAcceptanceNonDerived(TMCParticle const& mcparticle, TMCParticles const& bool is_equal = std::equal(pdgs.cbegin(), pdgs.cend(), target_pdgs.cbegin()); pdgs.clear(); pdgs.shrink_to_fit(); - if (!is_equal) { - return false; // garantee daughter is in acceptance. - } - return true; + return is_equal; } //_______________________________________________________________________ -template +template bool IsInAcceptance(TMCParticle const& mcparticle, TMCParticles const& mcparticles, TTargetPDGs target_pdgs, const float ymin, const float ymax, const float phimin, const float phimax) { if (mcparticle.y() < ymin || ymax < mcparticle.y()) { @@ -225,10 +279,7 @@ bool IsInAcceptance(TMCParticle const& mcparticle, TMCParticles const& mcparticl bool is_equal = std::equal(pdgs.cbegin(), pdgs.cend(), target_pdgs.cbegin()); pdgs.clear(); pdgs.shrink_to_fit(); - if (!is_equal) { - return false; // garantee daughter is in acceptance. - } - return true; + return is_equal; } //_______________________________________________________________________ template @@ -298,35 +349,733 @@ bool isMotherPDG(T& mcparticle, const int motherPDG, const int depth = 10) // o2 mcparticle.setCursor(motherid); if (mcparticle.pdgCode() == motherPDG) { return true; // The mother has the required pdg code, so return its daughters global mc particle code. - } else { - return isMotherPDG(mcparticle, motherPDG, depth - 1); } + return isMotherPDG(mcparticle, motherPDG, depth - 1); } //_______________________________________________________________________ -/// \brief Go up the decay chain of a mcparticle looking for a mother with the given pdg codes, if found return id else -1 +/// \brief Go up the decay chain of a mcparticle looking for a mother with the given pdg codes, if found return true else false /// E.g. if electron cluster is coming from a photon return true, if primary electron return false -/// \param mcparticle iterator of mxparticle, WILL BE CHANGED by this function! +/// \param mcparticle iterator of mcparticle, NOT modified by this function +/// \param mcparticleWorking a second iterator of the SAME table, used as scratch space to walk up the chain -- caller must supply this so the function doesn't construct its own /// \param motherPDG target mother PDG value /// \param depth how many steps in the chain this check should go maximum before failing template -int32_t getMotherIndexFromChain(T& mcparticle, const int motherPDG, const int depth = 10) // o2-linter: disable=pdg/explicit-code (false positive) +bool isMotherPDG(const T& mcparticle, T& mcparticleWorking, const int motherPDG, const int depth = 10) // o2-linter: disable=pdg/explicit-code (false positive) { if (!mcparticle.has_mothers() || depth < 1) { + return false; + } + + int motherid = mcparticle.mothersIds()[0]; + mcparticleWorking.setCursor(motherid); + if (mcparticleWorking.pdgCode() == motherPDG) { + return true; // The mother has the required pdg code. + } + return isMotherPDG(mcparticleWorking, mcparticleWorking, motherPDG, depth - 1); +} + +//_______________________________________________________________________ +/// \brief Check if given particle is from Bremsstrahlung +/// \param mcCursor iterator of mcparticle +/// \param iter shared iterator used to walk to the mother +template +bool isFromBremsstrahlung(TIter const& mcCursor, TIter& iter, int& motherId) +{ + if (!mcCursor.has_mothers()) { + return false; + } + if (mcCursor.pdgCode() != PDG_t::kGamma) { + return false; // only a photon can itself be a bremsstrahlung emission + } + if (mcCursor.mothersIds().size() != 1) { + return false; // mother can be only a single lepton, otherwise it might be e+e- annihilation or something else + } + motherId = mcCursor.mothersIds()[0]; + iter.setCursor(motherId); + return std::abs(iter.pdgCode()) == PDG_t::kElectron; +} + +//_______________________________________________________________________ +/// \brief Go up the decay chain of a mcparticle looking for a mother with the given pdg codes, if found return id else -1 +/// E.g. if electron cluster is coming from a photon return true, if primary electron return false +/// \param mcParticle iterator of mxparticle, WILL BE CHANGED by this function! +/// \param motherPDG target mother PDG value +/// \param depth how many steps in the chain this check should go maximum before failing +template +int32_t getMotherIndexFromChain(T& mcParticle, const int motherPDG, const int depth = 10) +{ + for (int d = 0; d < depth; ++d) { + if (!mcParticle.has_mothers()) { + return -1; + } + const int32_t motherid = mcParticle.mothersIds()[0]; + mcParticle.setCursor(motherid); + if (mcParticle.pdgCode() == motherPDG) { + return motherid; + } + } + return -1; +} + +//_______________________________________________________________________ +/// \brief Obtains given photon mcpartlices origin type +/// \param mcPhoton mcparticle iterator of photon +/// \param iter mcparticle iterator used to walk to the mother +/// \param motherDPGs list of pdg values of mothers that would be excepted for PhotonOrigin::Decay +/// \return given photon mcpartlices origin type +template +o2::analysis::em::PhotonOrigin getPhotonOriginType(TIter const& mcPhoton, TIter& mcIter, TTargetPDGs const& motherPdgs, int& motherId) +{ + switch (mcPhoton.getProcess()) { + case TMCProcess::kPBrem: + return o2::analysis::em::PhotonOrigin::Bremsstrahlung; + case TMCProcess::kPAnnihilation: + return o2::analysis::em::PhotonOrigin::Annihilation; + case TMCProcess::kPHadronic: + return o2::analysis::em::PhotonOrigin::Hadronic; + default: + break; + } + if (!mcPhoton.has_mothers()) { + return o2::analysis::em::PhotonOrigin::Other; + } + mcIter.setCursor(mcPhoton.globalIndex()); + motherId = findMotherInChain(mcIter, motherPdgs); + if (motherId >= 0) { + return o2::analysis::em::PhotonOrigin::Decay; + } + motherId = mcPhoton.mothersIds()[0]; + mcIter.setCursor(motherId); + if ((std::abs(mcIter.pdgCode()) >= PDG_t::kDown && std::abs(mcIter.pdgCode()) <= PDG_t::kTop) || std::abs(mcIter.pdgCode()) == PDG_t::kGluon) { + return o2::analysis::em::PhotonOrigin::Direct; + } + return o2::analysis::em::PhotonOrigin::Other; +} + +//_______________________________________________________________________ +/// \brief Obtains given lepton mcpartlices origin type +/// \param mcLepton mcparticle iterator of lepton +/// \param iter mcparticle iterator used to walk to the mother +/// \param motherDPGs list of pdg values of mothers that would be excepted for PhotonOrigin::Decay +/// \return given lepton mcpartlices origin type +template +o2::analysis::em::LeptonOrigin getLeptonOriginType(TIter const& mcLepton, TIter& mcIter, TTargetPDGs const& motherPdgs) +{ + switch (mcLepton.getProcess()) { + case TMCProcess::kPPair: + return o2::analysis::em::LeptonOrigin::Conversion; + case TMCProcess::kPCompton: + return o2::analysis::em::LeptonOrigin::Compton; + case TMCProcess::kPPhotoelectric: + return o2::analysis::em::LeptonOrigin::PhotoElectric; + case TMCProcess::kPDeltaRay: + return o2::analysis::em::LeptonOrigin::DeltaRay; + case TMCProcess::kPDecay: { + if (!mcLepton.has_mothers()) { + return o2::analysis::em::LeptonOrigin::Other; + } + mcIter.setCursor(mcLepton.mothersIds()[0]); + if (std::find(motherPdgs.begin(), motherPdgs.end(), mcIter.pdgCode()) != motherPdgs.end()) { + return o2::analysis::em::LeptonOrigin::DirectMesonDecay; + } + return o2::analysis::em::LeptonOrigin::Other; // decay, but not from your target meson list + } + default: + return o2::analysis::em::LeptonOrigin::Other; + } +} + +//_______________________________________________________________________ +/// \brief Photon mother of a conversion electron, addressed by MC index +/// \param mcParticles table of MC particles (aod::McParticles or aod::EMMCParticles) +/// \param mcId index of the MC particle behind a track or V0 leg, -1 if the track carries no label +/// \return index of the mother photon if the particle is a transport electron with a photon as first mother, else -1 +template +int photonMotherId(TMCParticles const& mcParticles, const int mcId) +{ + if (mcId < 0) { return -1; } + return IsEleFromPC(mcParticles.iteratorAt(mcId), mcParticles); +} - int32_t motherid = mcparticle.mothersIds()[0]; - mcparticle.setCursor(motherid); - if (mcparticle.pdgCode() == motherPDG) { - return motherid; // The mother has the required pdg code, so return its daughters global mc particle code. - } else { - return getMotherIndexFromChain(mcparticle, motherPDG, depth - 1); +//_______________________________________________________________________ +/// \brief MC leg information for a V0 candidate +struct PhotonMCInfo { + int mcPosId = -1; /// MC particle of the positive leg, -1 = no label + int mcNegId = -1; /// MC particle of the negative leg, -1 = no label + int posPhotonId = -1; /// photon mother of the positive leg (photonMotherId), -1 = not a conversion electron + int negPhotonId = -1; /// same for the negative leg + int motherId = -1; /// common first mother of both legs, whatever its species, -1 = none + int motherPdg = 0; /// PDG code of that common mother + bool isTruePhoton = false; /// both legs are conversion electrons of the SAME photon + bool isPhysicalPrimary = false; /// the common mother is a physical primary + [[nodiscard]] bool hasBothLabels() const { return mcPosId >= 0 && mcNegId >= 0; } + [[nodiscard]] bool bothLegsFromPhotons() const { return posPhotonId >= 0 && negPhotonId >= 0; } +}; + +//_______________________________________________________________________ +/// \brief Fills PhotonMCInfo from the two leg labels +/// \param mcParticles table of MC particles +/// \param mcPosId MC index of the positive leg, -1 if unlabelled +/// \param mcNegId MC index of the negative leg, -1 if unlabelled +/// \return truth record of the candidate; with one unlabelled leg only that leg's fields stay at -1 +template +PhotonMCInfo makePhotonMCInfo(TMCParticles const& mcParticles, const int mcPosId, const int mcNegId) +{ + PhotonMCInfo info; + info.mcPosId = mcPosId; + info.mcNegId = mcNegId; + info.posPhotonId = photonMotherId(mcParticles, mcPosId); + info.negPhotonId = photonMotherId(mcParticles, mcNegId); + info.isTruePhoton = (info.posPhotonId >= 0) && (info.posPhotonId == info.negPhotonId); + if (!info.hasBothLabels()) { + return info; + } + const auto mcPos = mcParticles.iteratorAt(mcPosId); + const auto mcNeg = mcParticles.iteratorAt(mcNegId); + const int motherPos = mcPos.has_mothers() ? mcPos.mothersIds()[0] : -1; + const int motherNeg = mcNeg.has_mothers() ? mcNeg.mothersIds()[0] : -1; + if (motherPos < 0 || motherPos != motherNeg) { + return info; // no common valid mother } + const auto mother = mcParticles.iteratorAt(motherPos); + info.motherId = motherPos; + info.motherPdg = mother.pdgCode(); + info.isPhysicalPrimary = mother.isPhysicalPrimary(); + return info; } //_______________________________________________________________________ -} // namespace o2::aod::pwgem::photonmeson::utils::mcutil +/// \brief Fills PhotonMCInfo for a V0 photon of the skimmed tables +/// \tparam TLegs leg table joined with the MC labels (e.g. soa::Join) +/// \param v0 iterator of the V0 photon +/// \param mcParticles table of aod::EMMCParticles +/// \return truth record of this V0 +template +PhotonMCInfo makePhotonMCInfo(TV0 const& v0, TMCParticles const& mcParticles) +{ + const auto pos = v0.template posTrack_as(); + const auto neg = v0.template negTrack_as(); + return makePhotonMCInfo(mcParticles, + pos.has_emmcparticle() ? static_cast(pos.emmcparticleId()) : -1, + neg.has_emmcparticle() ? static_cast(neg.emmcparticleId()) : -1); +} + +//_______________________________________________________________________ +/// \brief Truth class of a pair of two V0 candidates +enum class PairTruthType : int { + Unknown = 0, + TrueTrueDistinct, /// two different true photons + TrueTrueSamePhoton, /// the same true photon twice (duplicate V0) + SharedMcLeg, /// the two V0s contain the same MC particle as a leg + TrueFake, /// one true photon, one fake + FakeFake, /// two fakes + Pi0Daughters, /// two true photons from the same pi0 + NClasses +}; + +//_______________________________________________________________________ +/// \brief Truth class of a pair from the two V0 records, without the pi0 check +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \return PairTruthType; TrueTrueSamePhoton has priority over SharedMcLeg, the shared-leg test runs across charges +[[nodiscard]] inline PairTruthType classifyPairTruth(PhotonMCInfo const& m1, PhotonMCInfo const& m2) +{ + if (m1.isTruePhoton && m2.isTruePhoton && m1.posPhotonId == m2.posPhotonId) { + return PairTruthType::TrueTrueSamePhoton; + } + const bool sharedLeg = + (m1.mcPosId >= 0 && (m1.mcPosId == m2.mcPosId || m1.mcPosId == m2.mcNegId)) || + (m1.mcNegId >= 0 && (m1.mcNegId == m2.mcPosId || m1.mcNegId == m2.mcNegId)); + if (sharedLeg) { + return PairTruthType::SharedMcLeg; + } + if (!m1.isTruePhoton && !m2.isTruePhoton) { + return PairTruthType::FakeFake; + } + if (m1.isTruePhoton != m2.isTruePhoton) { + return PairTruthType::TrueFake; + } + return PairTruthType::TrueTrueDistinct; +} + +//_______________________________________________________________________ +/// \brief Are two true photons daughters of the same pi0 +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \param mcParticles table of MC particles +/// \return true if both V0s are true photons and IsXFromY finds the same pi0 as first mother of both photons +template +bool isPi0DaughterPair(PhotonMCInfo const& m1, PhotonMCInfo const& m2, TMCParticles const& mcParticles) +{ + if (!m1.isTruePhoton || !m2.isTruePhoton) { + return false; + } + const int pi0Of1 = IsXFromY(mcParticles.iteratorAt(m1.posPhotonId), mcParticles, PDG_t::kGamma, PDG_t::kPi0); + const int pi0Of2 = IsXFromY(mcParticles.iteratorAt(m2.posPhotonId), mcParticles, PDG_t::kGamma, PDG_t::kPi0); + return pi0Of1 >= 0 && pi0Of1 == pi0Of2; +} + +//_______________________________________________________________________ +/// \brief Full truth class of a pair, as the HBT task bins it +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \param mcParticles table of MC particles +/// \return classifyPairTruth, with TrueTrueDistinct promoted to Pi0Daughters where isPi0DaughterPair holds +template +PairTruthType pairTruthType(PhotonMCInfo const& m1, PhotonMCInfo const& m2, TMCParticles const& mcParticles) +{ + const PairTruthType t = classifyPairTruth(m1, m2); + if (t == PairTruthType::TrueTrueDistinct && isPi0DaughterPair(m1, m2, mcParticles)) { + return PairTruthType::Pi0Daughters; + } + return t; +} + +//_______________________________________________________________________ +/// \brief Classifies the V0 cross-wise built candidates. The crossed pairs are evaluted whether it is from the same photon or not. +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \return 1 if both directions cross (full 2x2 cross: same two photons, legs swapped), 2 if only one direction crosses, 0 otherwise +[[nodiscard]] inline int classifyFakeSubtype(PhotonMCInfo const& m1, PhotonMCInfo const& m2) +{ + const bool cross12 = m1.posPhotonId >= 0 && m1.posPhotonId == m2.negPhotonId; + const bool cross21 = m2.posPhotonId >= 0 && m2.posPhotonId == m1.negPhotonId; + if (cross12 && cross21) { + return 1; + } + return (cross12 || cross21) ? 2 : 0; // o2-linter: disable=magic-number (class code, see brief) +} + //_______________________________________________________________________ +/// \brief Particle a mother chain ends at +enum class AncestorKind : int { + NoneFound = 0, /// no common ancestor within the search depth + StringOrCluster, /// PYTHIA string or cluster, or a bare parton (quark, gluon) + BeamOrNucleus, /// beam particle, nucleus, nucleon or diquark remnant + Pi0, + Eta, + OtherMeson, + Baryon, + Photon, // the ancestor is a photon: one particle descends from the other + Electron, + Other, + NClasses +}; + +//_______________________________________________________________________ +/// \brief Maps a PDG code onto AncestorKind +/// \param pdgIn PDG code, the sign is ignored +/// \return AncestorKind of the code; ranges of codes, not single particles +inline AncestorKind ancestorKindOfPdg(int pdgIn) +{ + const int pdg = std::abs(pdgIn); + constexpr int kStringLo = 91, kStringHi = 94; + constexpr int kDiquarkLo = 1103, kDiquarkHi = 5503; + constexpr int kNucleusOffset = 1000000000; + constexpr int kMesonHi = 999; + constexpr int kBaryonHi = 9999; + constexpr int kQuarkHi = 8, kGluon = 21; + if ((pdg >= kStringLo && pdg <= kStringHi) || (pdg >= 1 && pdg <= kQuarkHi) || pdg == kGluon) { + return AncestorKind::StringOrCluster; + } + if (pdg >= kNucleusOffset || (pdg >= kDiquarkLo && pdg <= kDiquarkHi)) { + return AncestorKind::BeamOrNucleus; + } + if (pdg == PDG_t::kPi0) { + return AncestorKind::Pi0; + } + if (pdg == o2::constants::physics::Pdg::kEta) { + return AncestorKind::Eta; + } + if (pdg == PDG_t::kGamma) { + return AncestorKind::Photon; + } + if (pdg == PDG_t::kElectron) { + return AncestorKind::Electron; + } + if (pdg == PDG_t::kProton || pdg == PDG_t::kNeutron) { + return AncestorKind::BeamOrNucleus; + } + if (pdg > 0 && pdg <= kMesonHi) { + return AncestorKind::OtherMeson; + } + if (pdg > kMesonHi && pdg <= kBaryonHi) { + return AncestorKind::Baryon; + } + return AncestorKind::Other; +} + +//_______________________________________________________________________ +/// \brief The particle a chain originates from, stopping before the beam +/// \param mcParticles table of MC particles +/// \param id index of the particle to start from +/// \param maxGen safety cap against self-referencing mother links, not a physics limit +/// \return index of the origin particle, or -1 if id was invalid +template +int originParticleId(TMCParticles const& mcParticles, int id, int maxGen = 50) +{ + if (id < 0) { + return -1; + } + int cur = id; + for (int gen = 0; gen < maxGen; ++gen) { + const auto p = mcParticles.iteratorAt(cur); + if (!p.has_mothers()) { + return cur; + } + const int mother = p.mothersIds()[0]; + if (mother < 0 || mother == cur) { + return cur; + } + const AncestorKind kindOfMother = ancestorKindOfPdg(mcParticles.iteratorAt(mother).pdgCode()); + if (kindOfMother == AncestorKind::BeamOrNucleus || kindOfMother == AncestorKind::StringOrCluster) { + return cur; + } + cur = mother; + } + return cur; +} + +//_______________________________________________________________________ +/// \brief Chains meetup like in findCommonAncestor +struct CommonAncestor { + int id{-1}; /// index of the lowest common ancestor, -1 if none was found + int gen1{-1}; /// generations from the first particle up to it (0 = it IS that particle) + int gen2{-1}; /// the same for the second particle +}; + +//_______________________________________________________________________ +/// \brief Lowest common ancestor of two MC particles +/// \param mcParticles table of MC particles +/// \param id1 index of the first particle +/// \param id2 index of the second particle +/// \param maxGen how many generations to walk on each side +/// \param stopAtBeam treat beam, nucleus and string as "no mother" +/// \return CommonAncestor with id = -1 if the chains do not meet within maxGen +template +CommonAncestor findCommonAncestor(TMCParticles const& mcParticles, int id1, int id2, + int maxGen = 4, bool stopAtBeam = false) +{ + CommonAncestor out; + if (id1 < 0 || id2 < 0) { + return out; + } + auto motherOf = [&mcParticles, stopAtBeam](int id) -> int { + const auto particle = mcParticles.iteratorAt(id); + if (!particle.has_mothers()) { + return -1; + } + const int mother = particle.mothersIds()[0]; + if (mother < 0 || mother == id) { + return -1; + } + if (stopAtBeam) { + const AncestorKind kind = ancestorKindOfPdg(mcParticles.iteratorAt(mother).pdgCode()); + if (kind == AncestorKind::BeamOrNucleus || kind == AncestorKind::StringOrCluster) { + return -1; + } + } + return mother; + }; + std::vector chain1; + chain1.reserve(static_cast(maxGen) + 1); + for (int id = id1, gen = 0; id >= 0 && gen <= maxGen; ++gen) { + chain1.push_back(id); + id = motherOf(id); + } + for (int id = id2, gen = 0; id >= 0 && gen <= maxGen; ++gen) { + const auto it = std::find(chain1.begin(), chain1.end(), id); + if (it != chain1.end()) { + out.id = id; + out.gen1 = static_cast(std::distance(chain1.begin(), it)); + out.gen2 = gen; + return out; + } + id = motherOf(id); + } + return out; +} + +//_______________________________________________________________________ +/// \brief Class of the lowest common ancestor of two MC particles +/// \param mcParticles table of MC particles +/// \param id1 index of the first particle +/// \param id2 index of the second particle +/// \param maxGen how many generations to walk on each side +/// \return AncestorKind of the common ancestor, AncestorKind::NoneFound when the chains do not meet within maxGen +template +AncestorKind commonAncestorKind(TMCParticles const& mcParticles, int id1, int id2, int maxGen = 4) +{ + const CommonAncestor anc = findCommonAncestor(mcParticles, id1, id2, maxGen, false); + if (anc.id < 0) { + return AncestorKind::NoneFound; + } + return ancestorKindOfPdg(mcParticles.iteratorAt(anc.id).pdgCode()); +} + +//_______________________________________________________________________ +/// \brief Distinct parents for mc truth +/// V0_a = (e+_1, e-_1), V0_b = (e+_2, e-_2) N=2, consistent=2, shared=no signal +/// V0_a = (e+_1, e-_2), V0_b = (e+_2, e-_1) N=2, consistent=0, shared=yes leg swap +/// V0_a = (e+_1, e-_1), V0_b = (e+_2, e-_3) N=3, consistent=1, shared=no one real + one swap +/// V0_a = (e+_1, e-_2), V0_b = (e+_3, e-_4) N=4, consistent=0, shared=no pure combinatorics +/// V0_a = (e+_1, e-_1), V0_b = (e+_1',e-_1') N=1, consistent=2, shared=yes duplicate (split track) +/// V0_a = (e+_1, X), V0_b = (Y, e-_1) N=1, consistent=0, shared=yes one photon, two foreign tracks +struct MotherCensus { + int nConvLegs = 0; /// legs whose mother is a photon (0-4) + int nMothers = 0; /// distinct parent photons among them (0-4) + int nConsistent = 0; /// V0s whose two legs share the same parent photon (0-2) + bool shared = false; /// one parent photon appears in BOTH V0s + std::array distinct{{-1, -1, -1, -1}}; + [[nodiscard]] int splitCode() const { return 2 * nConsistent + (shared ? 1 : 0); } // o2-linter: disable=magic-number (two values per nConsistent) +}; + +//_______________________________________________________________________ +/// \brief Census of the four legs of a photon pair +/// \param motherPhotonIds the parent photon of each leg, in the order (V0 A e+, V0 A e-, V0 B e+, V0 B e-); +/// \return MotherCensus of the four legs +inline MotherCensus censusMothers(std::array const& motherPhotonIds) +{ + constexpr int kNLegs = 4; + const auto& mp = motherPhotonIds; + MotherCensus c; + for (int i = 0; i < kNLegs; ++i) { + const auto ui = static_cast(i); + if (mp[ui] < 0) { + continue; + } + c.nConvLegs++; + bool seen = false; + for (int j = 0; j < i; ++j) { // only the first appearance counts as a new mother + seen = seen || (mp[static_cast(j)] == mp[ui]); + } + if (!seen) { + c.distinct[static_cast(c.nMothers)] = mp[ui]; // nMothers < 4: at most four legs + c.nMothers++; + } + } + c.nConsistent = ((mp[0] >= 0 && mp[0] == mp[1]) ? 1 : 0) + ((mp[2] >= 0 && mp[2] == mp[3]) ? 1 : 0); + for (int i = 0; i < 2; ++i) { // o2-linter: disable=magic-number (the two legs of the first V0) + for (int j = 2; j < kNLegs; ++j) { + c.shared = c.shared || (mp[static_cast(i)] >= 0 && mp[static_cast(i)] == mp[static_cast(j)]); + } + } + return c; +} + +//_______________________________________________________________________ +/// \brief Amount of V0 mothers per legs. +/// \param m1 truth record of the first V0 +/// \param m2 truth record of the second V0 +/// \return censusMothers of (m1 e+, m1 e-, m2 e+, m2 e-) +[[nodiscard]] inline MotherCensus censusMothers(PhotonMCInfo const& m1, PhotonMCInfo const& m2) +{ + return censusMothers({m1.posPhotonId, m1.negPhotonId, m2.posPhotonId, m2.negPhotonId}); +} + +//_______________________________________________________________________ +/// \brief Analysis for two photons based on their MC track label content. +enum class DupClass : int { + Unknown = 0, /// at least one candidate without both labels + SameMcPhoton, /// a duplicate + SharedMcLeg, /// share an MC particle as a leg, but different mother photons + CrossBuilt, /// legs of two true photons built crosswise (swap) + DistinctTrue, /// two different, cleanly reconstructed photons + InvolvesFake, /// at least one is not a true photon, no swap + NClasses +}; + //_______________________________________________________________________ +/// \brief Duplicate class of two candidates from their truth records +/// \param a truth record of the first candidate +/// \param b truth record of the second candidate +/// \return DupClass; the shared-leg test runs before the label requirement, a V0 with one labelled leg can still share it +[[nodiscard]] inline DupClass classifyDupPair(PhotonMCInfo const& a, PhotonMCInfo const& b) +{ + const bool sharedLeg = + (a.mcPosId >= 0 && (a.mcPosId == b.mcPosId || a.mcPosId == b.mcNegId)) || + (a.mcNegId >= 0 && (a.mcNegId == b.mcPosId || a.mcNegId == b.mcNegId)); + if (!a.hasBothLabels() || !b.hasBothLabels()) { + return sharedLeg ? DupClass::SharedMcLeg : DupClass::Unknown; + } + if (a.isTruePhoton && b.isTruePhoton && a.posPhotonId == b.posPhotonId) { + return DupClass::SameMcPhoton; + } + if (sharedLeg) { + return DupClass::SharedMcLeg; + } + if (classifyFakeSubtype(a, b) != 0) { + return DupClass::CrossBuilt; + } + if (a.isTruePhoton && b.isTruePhoton) { + return DupClass::DistinctTrue; + } + return DupClass::InvolvesFake; +} + +//_______________________________________________________________________ +/// \brief How one V0 candidate was built, by MC truth +enum class V0RecoStatus : int { + NoLabel = 0, ///< at least one leg without MC label + NoConvLeg, ///< no leg is a conversion electron + OneConvLeg, ///< one conversion electron, one foreign track + CrossBuilt, ///< two conversion electrons of DIFFERENT photons + TruePhoton, ///< two conversion electrons of the same photon + NClasses +}; + +//_______________________________________________________________________ +/// \brief Reco status of a candidate from its truth record +/// \param m truth record of the candidate +/// \return V0RecoStatus +[[nodiscard]] inline V0RecoStatus classifyV0Reco(PhotonMCInfo const& m) +{ + if (!m.hasBothLabels()) { + return V0RecoStatus::NoLabel; + } + if (m.isTruePhoton) { + return V0RecoStatus::TruePhoton; + } + if (m.bothLegsFromPhotons()) { + return V0RecoStatus::CrossBuilt; + } + return (m.posPhotonId >= 0 || m.negPhotonId >= 0) ? V0RecoStatus::OneConvLeg : V0RecoStatus::NoConvLeg; +} + +//_______________________________________________________________________ +/// \brief True conversion point of a photon: the production vertex of its first e+/e- daughter +/// \param mcParticles table of MC particles +/// \param photonId index of the photon +/// \param xyz output, (vx, vy, vz) of the conversion (cm) +/// \return true if the photon has an electron daughter, false otherwise (xyz untouched) +template +bool conversionPoint(TMCParticles const& mcParticles, const int photonId, std::array& xyz) +{ + if (photonId < 0) { + return false; + } + const auto photon = mcParticles.iteratorAt(photonId); + if (!photon.has_daughters()) { + return false; + } + for (const auto& dId : photon.daughtersIds()) { + if (dId < 0) { + continue; + } + const auto d = mcParticles.iteratorAt(dId); + if (std::abs(d.pdgCode()) == PDG_t::kElectron) { + xyz = {static_cast(d.vx()), static_cast(d.vy()), static_cast(d.vz())}; + return true; + } + } + return false; +} + +//_______________________________________________________________________ +/// \brief True q_inv of two MC photons (massless) +/// \param mcParticles table of MC particles +/// \param id1 index of the first photon +/// \param id2 index of the second photon +/// \return q_inv = sqrt(2 (E1 E2 - p1.p2)) in GeV/c, -1 if an index is invalid +template +float trueQinvOfPhotons(TMCParticles const& mcParticles, const int id1, const int id2) +{ + if (id1 < 0 || id2 < 0) { + return -1.f; + } + const auto pa = mcParticles.iteratorAt(id1); + const auto pb = mcParticles.iteratorAt(id2); + const float e1 = std::hypot(pa.px(), pa.py(), pa.pz()); + const float e2 = std::hypot(pb.px(), pb.py(), pb.pz()); + return std::sqrt(std::max(0.f, 2.f * (e1 * e2 - pa.px() * pb.px() - pa.py() * pb.py() - pa.pz() * pb.pz()))); +} + +//_______________________________________________________________________ +/// \brief True opening angle of two MC photons +/// \param mcParticles table of MC particles +/// \param id1 index of the first photon +/// \param id2 index of the second photon +/// \return angle between the two momenta in rad, -1 if an index is invalid +template +float trueOpeningAngle(TMCParticles const& mcParticles, const int id1, const int id2) +{ + if (id1 < 0 || id2 < 0) { + return -1.f; + } + const auto pa = mcParticles.iteratorAt(id1); + const auto pb = mcParticles.iteratorAt(id2); + const float n1 = std::hypot(pa.px(), pa.py(), pa.pz()); + const float n2 = std::hypot(pb.px(), pb.py(), pb.pz()); + if (n1 < 1e-9f || n2 < 1e-9f) { // o2-linter: disable=magic-number (zero momentum guard) + return -1.f; + } + const float c = (pa.px() * pb.px() + pa.py() * pb.py() + pa.pz() * pb.pz()) / (n1 * n2); + return std::acos(std::clamp(c, -1.f, 1.f)); +} + +//_______________________________________________________________________ +/// \brief Whether the PDG code is a quark, gluon, diquark or string/cluster, i.e. where the decay chain ends +[[nodiscard]] inline bool isPartonLike(int pdg) +{ + const int absPdg = std::abs(pdg); + const bool isDiquark = absPdg > 1000 && absPdg < 6000 && (absPdg / 10) % 10 == 0; // o2-linter: disable=magic-number + return (absPdg >= PDG_t::kDown && absPdg <= PDG_t::kTop) || absPdg == PDG_t::kGluon || absPdg == 91 || absPdg == 92 || isDiquark; // o2-linter: disable=magic-number +} + +//_______________________________________________________________________ +/// \brief Whether the PDG code is a nucleus (10LZZZAAAI), e.g. one of the colliding beam nuclei +[[nodiscard]] inline bool isNucleus(int pdg) +{ + constexpr int NucleusOffset = 1000000000; + return std::abs(pdg) >= NucleusOffset; +} + +//_______________________________________________________________________ +/// \brief Fill the ancestor chain of a particle, starting with the particle itself. +/// Ends with the first parton-like ancestor (included), a particle without mothers, or when chain is full. +/// A nucleus (the colliding beam nuclei) is never part of the chain, otherwise all particles of an event would share it. +/// Unused entries of chain are set to -1. +/// \param mcParticle iterator of the full (unsliced) MC particle table. !WILL BE CHANGED! +/// Afterwards it points to the last chain element, or to the nucleus that ended the chain. +/// \param startId global index of the particle of interest +/// \param chain output, global indices, closest first +/// \return number of filled entries +template +int getAncestorChain(TMCParticle& mcParticle, int startId, std::span chain) +{ + int length = 0; + int id = startId; + while (id >= 0 && length < std::ssize(chain)) { + mcParticle.setCursor(id); + if (isNucleus(mcParticle.pdgCode())) { + break; // colliding nuclei are not an ancestor + } + chain[length++] = id; + if (isPartonLike(mcParticle.pdgCode()) || !mcParticle.has_mothers()) { + break; + } + id = mcParticle.mothersIds()[0]; + } + std::ranges::fill(chain.subspan(length), -1); + return length; +} + +//_______________________________________________________________________ +/// \brief Find the closest common ancestor of two ancestor chains +/// \param chain1 ancestor chain of first particle (particle itself first, may be padded with -1) +/// \param chain2 ancestor chain of second particle (particle itself first, may be padded with -1) +/// \return global index of the closest common ancestor, -1 if none found +[[nodiscard]] inline int getFirstCommonAncestor(std::span chain1, std::span chain2) +{ + chain1 = chain1.first(std::ranges::distance(chain1.begin(), std::ranges::find(chain1, -1))); + chain2 = chain2.first(std::ranges::distance(chain2.begin(), std::ranges::find(chain2, -1))); + const auto it = std::ranges::find_first_of(chain1, chain2); + return it != chain1.end() ? *it : -1; +} + +} // namespace o2::aod::pwgem::photonmeson::utils::mcutil + #endif // PWGEM_PHOTONMESON_UTILS_MCUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/NMHistograms.h b/PWGEM/PhotonMeson/Utils/NMHistograms.h index edca1f8c3ed..d7c87380417 100644 --- a/PWGEM/PhotonMeson/Utils/NMHistograms.h +++ b/PWGEM/PhotonMeson/Utils/NMHistograms.h @@ -18,7 +18,9 @@ #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" -#include +#include +#include +#include #include #include @@ -27,14 +29,31 @@ #include #include +#include #include namespace o2::aod::pwgem::photonmeson::utils::nmhistogram { -inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool isMC, const char* pairname = "#gamma#gamma") +/// \brief Add the pair histograms +/// \param isMC book the MC truth histograms (only mass and pT axes) instead of the same/mixed event histograms +/// \param pairname name of the pair used in the axis titles +/// \param sparseFullAxes data only: add EP, occupancy and z-vertex axes to the same/mixed event histograms +/// \param occBins data only: occupancy bin edges +/// \param epBins data only: event plane bin edges +/// \param vtxBins data only: z-vertex bin edges +inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, + bool isMC, + const char* pairname = "#gamma#gamma", + bool sparseFullAxes = false, + std::vector const& occBins = {0, 500, 1000, 2000, 3000, 5000, 10000}, + std::vector const& epBins = {-o2::constants::math::PIHalf, -o2::constants::math::PIQuarter, 0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf}, + std::vector const& vtxBins = {-10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, + const int nBinsMass = 400, + const std::array massBinRange = {0., 0.8}) { // !!Don't change pt,eta,y binning. These binnings have to be consistent with binned data at skimming.!! std::vector ptbins; + ptbins.reserve(72); for (int i = 0; i < 2; i++) { // o2-linter: disable=magic-number (just numbers for binning) ptbins.emplace_back(0.05 * (i - 0) + 0.0); // from 0 to 0.05 GeV/c, every 0.05 GeV/c } @@ -48,7 +67,10 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is ptbins.emplace_back(1.0 * (i - 61) + 10.0); // from 10 to 20 GeV/c, every 1 GeV/c } const o2::framework::AxisSpec axis_pt{ptbins, Form("p_{T,%s} (GeV/c)", pairname)}; - const o2::framework::AxisSpec axis_mass{400, 0, 0.8, Form("m_{%s} (GeV/c^{2})", pairname)}; + const o2::framework::AxisSpec axis_mass{nBinsMass, massBinRange[0], massBinRange[1], Form("m_{%s} (GeV/c^{2})", pairname)}; + const o2::framework::AxisSpec axis_occ{occBins, "occupancy"}; + const o2::framework::AxisSpec axis_ep{epBins, "#Psi_{2} (rad)"}; + const o2::framework::AxisSpec axis_vtx{vtxBins, "Z_{vtx} (cm)"}; if (isMC) { fRegistry->add("Pair/Pi0/hs_Primary", "rec. true pi0", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); @@ -72,7 +94,15 @@ inline void addNMHistograms(o2::framework::HistogramRegistry* fRegistry, bool is fRegistry->get(HIST("Generated/Eta/hPtY"))->SetXTitle("p_{T} (GeV/c)"); fRegistry->get(HIST("Generated/Eta/hPtY"))->SetYTitle("rapidity |y|"); } else { - fRegistry->add("Pair/same/hs", "diphoton", o2::framework::HistType::kTHnSparseD, {axis_mass, axis_pt}, true); + if (sparseFullAxes) { + fRegistry->add("Pair/same/hs", "diphoton", + o2::framework::HistType::kTHnSparseD, + {axis_mass, axis_pt, axis_ep, axis_occ, axis_vtx}, true); + } else { + fRegistry->add("Pair/same/hs", "diphoton", + o2::framework::HistType::kTHnSparseD, + {axis_mass, axis_pt}, true); + } fRegistry->addClone("Pair/same/", "Pair/mix/"); } } @@ -84,12 +114,12 @@ void fillTruePairInfo(o2::framework::HistogramRegistry* fRegistry, TDiphoton con float weight = eventWeight; int motherid_strhad = o2::aod::pwgem::photonmeson::utils::mcutil::IsFromWD(mcparticle.template emmcevent_as(), mcparticle, mcparticles); switch (pdg) { - case kPi0: { + case PDG_t::kPi0: { if (mcparticle.isPhysicalPrimary() || mcparticle.producedByGenerator()) { fRegistry->fill(HIST("Pair/Pi0/hs_Primary"), v12.M(), v12.Pt(), weight); } else if (motherid_strhad > 0) { auto str_had = mcparticles.iteratorAt(motherid_strhad); - if (std::abs(str_had.pdgCode()) == kK0Short && f1fd_k0s_to_pi0 != nullptr) { + if (std::abs(str_had.pdgCode()) == PDG_t::kK0Short && f1fd_k0s_to_pi0 != nullptr) { weight *= f1fd_k0s_to_pi0->Eval(str_had.pt()); } fRegistry->fill(HIST("Pair/Pi0/hs_FromWD"), v12.M(), v12.Pt(), weight); @@ -98,7 +128,7 @@ void fillTruePairInfo(o2::framework::HistogramRegistry* fRegistry, TDiphoton con } break; } - case 221: { + case constants::physics::kEta: { if (mcparticle.isPhysicalPrimary() || mcparticle.producedByGenerator()) { fRegistry->fill(HIST("Pair/Eta/hs_Primary"), v12.M(), v12.Pt(), weight); } else if (motherid_strhad > 0) { diff --git a/PWGEM/PhotonMeson/Utils/PCMUtilities.h b/PWGEM/PhotonMeson/Utils/PCMUtilities.h index 269db4f46fb..f8f6daed325 100644 --- a/PWGEM/PhotonMeson/Utils/PCMUtilities.h +++ b/PWGEM/PhotonMeson/Utils/PCMUtilities.h @@ -21,28 +21,45 @@ #include #include +#include +#include +#include #include +#include #include -#include +#include // IWYU pragma: keep (for rotate) +#include +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector2Dfwd.h) +#include +#include + +#include #include #include - -#include +#include //_______________________________________________________________________ +/// \brief Armenteros-Podolanski cut of photon conversions: (alpha, qT) inside the ellipse with half axes alpha_max and qt_max +/// \param alpha Armenteros alpha +/// \param qt Armenteros qT (GeV/c) +/// \param alpha_max half axis in alpha +/// \param qt_max half axis in qT (GeV/c) +/// \return true if inside the ellipse inline bool checkAP(const float alpha, const float qt, const float alpha_max = 0.95, const float qt_max = 0.05) { float ellipse = std::pow(alpha / alpha_max, 2) + std::pow(qt / qt_max, 2); - if (ellipse < 1.0) { - return true; - } else { - return false; - } + return (ellipse < 1.0); } //_______________________________________________________________________ -inline float v0_alpha(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) +/// \brief Armenteros alpha of a V0: (pL+ - pL-) / (pL+ + pL-), pL = momentum of a leg along the V0 momentum +/// \param pxpos px of the positive leg (GeV/c), same for pypos, pzpos +/// \param pxneg px of the negative leg (GeV/c), same for pyneg, pzneg +/// \return alpha +inline float v0Alpha(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { float momTot = RecoDecay::p(pxpos + pxneg, pypos + pyneg, pzpos + pzneg); float lQlNeg = RecoDecay::dotProd(std::array{pxneg, pyneg, pzneg}, std::array{pxpos + pxneg, pypos + pyneg, pzpos + pzneg}) / momTot; @@ -50,15 +67,26 @@ inline float v0_alpha(float pxpos, float pypos, float pzpos, float pxneg, float return (lQlPos - lQlNeg) / (lQlPos + lQlNeg); // longitudinal momentum asymmetry of v0 } //_______________________________________________________________________ -inline float v0_qt(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) +/// \brief Armenteros qT of a V0: momentum of the negative leg transverse to the V0 momentum +/// \param pxpos px of the positive leg (GeV/c), same for pypos, pzpos +/// \param pxneg px of the negative leg (GeV/c), same for pyneg, pzneg +/// \return qT (GeV/c) +inline float v0Qt(float pxpos, float pypos, float pzpos, float pxneg, float pyneg, float pzneg) { float momTot = RecoDecay::p2(pxpos + pxneg, pypos + pyneg, pzpos + pzneg); float dp = RecoDecay::dotProd(std::array{pxneg, pyneg, pzneg}, std::array{pxpos + pxneg, pypos + pyneg, pzpos + pzneg}); return std::sqrt(RecoDecay::p2(pxneg, pyneg, pzneg) - dp * dp / momTot); // qt of v0 } //_______________________________________________________________________ +/// \brief conversion point of two legs (Run 2 recipe): xy = touching point of the two helix circles (centres weighted with the +/// radius of the other circle), z = radius-weighted mean of the leg z after propagating each leg to its point closest to it +/// \param prop propagator (also gives the nominal field) +/// \param trackPosInformation positive leg +/// \param trackNegInformation negative leg +/// \param xyz output: conversion point (cm) +/// \param matCorr material correction of the propagation template -inline void Vtx_recalculationParCov(o2::base::Propagator* prop, const o2::track::TrackParametrizationWithError& trackPosInformation, const o2::track::TrackParametrizationWithError& trackNegInformation, float xyz[3], o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) +inline void Vtx_recalculationParCov(o2::base::Propagator* prop, const o2::track::TrackParametrizationWithError& trackPosInformation, const o2::track::TrackParametrizationWithError& trackNegInformation, std::array& xyz, o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) { float bz = prop->getNominalBz(); @@ -82,21 +110,21 @@ inline void Vtx_recalculationParCov(o2::base::Propagator* prop, const o2::track: float vertexXNeg = helixNeg.xC + helixNeg.rC * std::cos(alphaNeg); float vertexYNeg = helixNeg.yC + helixNeg.rC * std::sin(alphaNeg); - TVector2 vertexPos(vertexXPos, vertexYPos); - TVector2 vertexNeg(vertexXNeg, vertexYNeg); + ROOT::Math::XYVector vertexPos(vertexXPos, vertexYPos); + ROOT::Math::XYVector vertexNeg(vertexXNeg, vertexYNeg); // Convert to local coordinate system - TVector2 vertexPosRot = vertexPos.Rotate(-trackPosInformationCopy.getAlpha()); - TVector2 vertexNegRot = vertexNeg.Rotate(-trackNegInformationCopy.getAlpha()); + vertexPos.Rotate(-trackPosInformationCopy.getAlpha()); + vertexNeg.Rotate(-trackNegInformationCopy.getAlpha()); prop->propagateToX(trackPosInformationCopy, - vertexPosRot.X(), + vertexPos.X(), bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, matCorr); prop->propagateToX(trackNegInformationCopy, - vertexNegRot.X(), + vertexNeg.X(), bz, o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, @@ -104,90 +132,314 @@ inline void Vtx_recalculationParCov(o2::base::Propagator* prop, const o2::track: xyz[2] = (trackPosInformationCopy.getZ() * helixNeg.rC + trackNegInformationCopy.getZ() * helixPos.rC) / (helixPos.rC + helixNeg.rC); } + +//_______________________________________________________________________ +/// \brief Calculate DCA for tracks using the track helix and the inclination angl tan(lambda) +/// \param trk track parameterization to obtain helix parameters +/// \param vtx primary vertex position +/// \param magField magnetic field strenght of L3 +/// \return DCAxy, DCAz +template +std::array CalculateDCAFast(const o2::track::TrackParametrizationWithError& trk, const o2::math_utils::Point3D& vtx, const float magField) +{ + + std::array dca{}; + + // obtain circle from track in x-y plane + const o2::track::TrackAuxPar helixPos(trk, magField); + + // obtain position in global coordinates and tan(lambda) + const auto posTrack = trk.getXYZGlo(); + const float trX = posTrack.X(); + const float trY = posTrack.Y(); + const float trZ = posTrack.Z(); + const float tangentLambda = trk.getTgl(); + + // Calculate DCAxy + // Use distance in x and y between circle center and vtx, afterwards subtract radius of circle + const float distX = helixPos.xC - vtx.X(); + const float distY = helixPos.yC - vtx.Y(); + const float trackCircCenter = std::sqrt(distX * distX + distY * distY); + dca[0] = helixPos.rC - trackCircCenter; + + // Calculate DCAz + // First step: Calculate arc lenght of circle between current position and primary vertex in x-y + const float theta0 = std::atan2(trY - helixPos.yC, trX - helixPos.xC); + const float thetav = std::atan2(vtx.Y() - helixPos.yC, vtx.X() - helixPos.xC); + + // Make sure angle is between -pi and pi + const auto dtheta = RecoDecay::constrainAngle(thetav - theta0, -o2::constants::math::PI); + + // arc-lenght along helix + const float arcLenght = std::fabs(helixPos.rC * dtheta); + + // get global z-position at DCA + const float ZPosGlo = trZ - arcLenght * tangentLambda; + + // DCA calculated from + dca[1] = ZPosGlo - vtx.Z(); + + return dca; +} + +//_______________________________________________________________________ +/// \brief Calculate the track momentum at a different place of the track Helix. +/// \param s arc-lenght where track should be propagated to +/// \param track particle track +/// \param trHelix track helix param. for circle approximation +/// \param bz magnetic field strenght of L3 +/// \param addPhi optional additional rotation of the track in phi-direction +/// \return track momentum vector at new position +template +inline std::array getPropMomentumFromTrackHelix(const float s, const TTrack& track, const o2::track::TrackAuxPar& trHelix, const float bz, float addPhi = 0.f) +{ + + // Calculate the change in phi considering the track radius and the track arc lenght s + const float dphi = -track.sign() * 0.3f * bz * s / 100. / track.pt(); // s in cm + const float dotProd = std::cos(track.phi() + addPhi) * track.pt() * trHelix.xC + std::sin(track.phi() + addPhi) * track.pt() * trHelix.yC; + if (dotProd < 0) { + addPhi -= o2::constants::math::PI; + } + + // Calculate the phi at the secondary vertex + const auto phi = RecoDecay::constrainAngle(track.phi() + dphi + addPhi); + + // Calculate px,y,z at the new propagated vertex + std::array trackP{}; + trackP[0] = std::cos(phi) * track.pt(); + trackP[1] = std::sin(phi) * track.pt(); + trackP[2] = track.tgl() * track.pt(); + + return trackP; +} + //_______________________________________________________________________ -template -inline void Vtx_recalculation(o2::base::Propagator* prop, T1 lTrackPos, T2 lTrackNeg, float xyz[3], o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) +/// \brief conversion point of two legs from their track rows (Vtx_recalculationParCov) +/// \param prop propagator (also gives the nominal field) +/// \param lTrackPos positive leg +/// \param lTrackNeg negative leg +/// \param xyz output: conversion point (cm) +/// \param matCorr material correction of the propagation +template +inline void Vtx_recalculation(o2::base::Propagator* prop, T1 lTrackPos, T2 lTrackNeg, std::array& xyz, o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE) { + // o2::track::TrackParametrizationWithError = TrackParCov, I use the full version to have control over the data type o2::track::TrackParametrizationWithError trackPosInformation = getTrackParCov(lTrackPos); // first get an object that stores Track information (positive) o2::track::TrackParametrizationWithError trackNegInformation = getTrackParCov(lTrackNeg); // first get an object that stores Track information (negative) Vtx_recalculationParCov(prop, trackPosInformation, trackNegInformation, xyz, matCorr); } + //_______________________________________________________________________ -// template -// float getPtResolution(TV0 const& v0) -// { -// float px = v0.px(); -// float py = v0.py(); -// float pt = v0.pt(); -// float px_err = std::sqrt(std::fabs(v0.sigmaPx2())); -// float py_err = std::sqrt(std::fabs(v0.sigmaPy2())); -// float pxy_err = v0.sigmaPxPy(); -// return std::sqrt(std::pow(px / pt * px_err, 2) + std::pow(py / pt * py_err, 2) + 2.f * px / pt * py / pt * pxy_err); -// } -// //_______________________________________________________________________ -// template -// float getPhiResolution(TV0 const& v0) -// { -// float px = v0.px(); -// float py = v0.py(); -// float pt = v0.pt(); -// float px_err = std::sqrt(std::fabs(v0.sigmaPx2())); -// float py_err = std::sqrt(std::fabs(v0.sigmaPy2())); -// float pxy_err = v0.sigmaPxPy(); -// return std::sqrt(std::pow(px / pt / pt * py_err, 2) + std::pow(py / pt / pt * px_err, 2) - 2.f * px / pt / pt * py / pt / pt * pxy_err); -// } -// //_______________________________________________________________________ -// template -// float getThetaResolution(TV0 const& v0) -// { -// float px = v0.px(); -// float py = v0.py(); -// float pz = v0.pz(); -// float pt = v0.pt(); -// float p = v0.p(); -// float px_err = std::sqrt(std::fabs(v0.sigmaPx2())); -// float py_err = std::sqrt(std::fabs(v0.sigmaPy2())); -// float pz_err = std::sqrt(std::fabs(v0.sigmaPz2())); -// float pxy_err = v0.sigmaPxPy(); -// float pyz_err = v0.sigmaPyPz(); -// float pzx_err = v0.sigmaPzPx(); -// return std::sqrt(std::pow(pz * pz / p / p, 2) * (std::pow(px / pz / pt * px_err, 2) + std::pow(py / pz / pt * py_err, 2) + std::pow(pt / pz / pz * pz_err, 2) + 2.f * (px * py / pz / pz / pt / pt * pxy_err - py / pz / pz / pz * pyz_err - px / pz / pz / pz * pzx_err))); -// } -// //_______________________________________________________________________ -// template -// float getEtaResolution(TV0 const& v0) -// { -// float px = v0.px(); -// float py = v0.py(); -// float pz = v0.pz(); -// float pt = v0.pt(); -// float p = v0.p(); -// float px_err = std::sqrt(std::fabs(v0.sigmaPx2())); -// float py_err = std::sqrt(std::fabs(v0.sigmaPy2())); -// float pz_err = std::sqrt(std::fabs(v0.sigmaPz2())); -// float pxy_err = v0.sigmaPxPy(); -// float pyz_err = v0.sigmaPyPz(); -// float pzx_err = v0.sigmaPzPx(); -// return std::sqrt(std::pow(1.f / p / pt / pt, 2) * (std::pow(pz * px * px_err, 2) + std::pow(pz * py * py_err, 2) + std::pow(pt * pt * pz_err, 2) + 2.f * (pz * pz * px * py * pxy_err - pt * pt * py * pz * pyz_err - pt * pt * pz * px * pzx_err))); -// } -// //_______________________________________________________________________ -// template -// float getPResolution(TV0 const& v0) -// { -// float px = v0.px(); -// float py = v0.py(); -// float pz = v0.pz(); -// float p = v0.p(); -// float px_err = std::sqrt(std::fabs(v0.sigmaPx2())); -// float py_err = std::sqrt(std::fabs(v0.sigmaPy2())); -// float pz_err = std::sqrt(std::fabs(v0.sigmaPz2())); -// float pxy_err = v0.sigmaPxPy(); -// float pyz_err = v0.sigmaPyPz(); -// float pzx_err = v0.sigmaPzPx(); -// return std::sqrt(std::pow(1.f / p, 2) * (std::pow(px * px_err, 2) + std::pow(py * py_err, 2) + std::pow(pz * pz_err, 2) + 2.f * (px * py * pxy_err + py * pz * pyz_err + pz * px * pzx_err))); -// } +/// \brief Function to calculate a score based on cosPA and PCA. The smaller the score the better the V0 Candidate +/// \param cosPA cosine of pointing angle +/// \param pca point of closest approach in cm +/// \param weight how much is cosPA weighted (for pca its 1-weight). Weight has to be between 0 and 1 +/// \return final score +inline float getScoreV0(float cosPA, float pca, float weight) +{ + float cosScore = 60 * std::acos(cosPA); // pointing angle in degrees, the smaller the better + float pcaScore = pca / 3.f; // assume pca is between 0 and 3 + float wCos = weight; + float wPca = 1.f - wCos; // random values for now + float score = wCos * cosScore + wPca * pcaScore; + return score; +} //_______________________________________________________________________ +/// \brief truth class of a V0 candidate, from the mothers of its two legs +enum V0TruthClass { + kV0True = 0, // both legs come from the SAME true photon + kV0CrossLegFake = 1, // legs come from TWO different true photons + kV0OtherFake = 2 // at least one leg is not a photon conversion leg +}; +//_______________________________________________________________________ +/// \brief global index of the photon a track descends from +/// \param track track with an MC label +/// \param mcparticles the full McParticles table +/// \return index of the mother if it is a photon, -1 otherwise +template +inline int64_t getPhotonMotherId(TTrack const& track, TMCParticles const& mcparticles) +{ + if (!track.has_mcParticle()) { + return -1; + } + const auto mcp = mcparticles.iteratorAt(track.mcParticleId()); + if (!mcp.has_mothers()) { + return -1; + } + const auto& mothers = mcp.mothersIds(); + if (mothers.empty() || mothers[0] < 0) { + return -1; + } + return (mcparticles.iteratorAt(mothers[0]).pdgCode() == PDG_t::kGamma) ? static_cast(mothers[0]) : -1; +} + //_______________________________________________________________________ +/// \brief classify a V0 candidate by the photon mothers of its two legs +/// \param motherPos photon mother of the positive leg, -1 if none +/// \param motherEle photon mother of the negative leg, -1 if none +/// \return kV0True, kV0CrossLegFake or kV0OtherFake +template +inline V0TruthClass classifyV0Truth(TTrack const& pos, TTrack const& ele, TMCParticles const& mcparticles, + int64_t& motherPos, int64_t& motherEle) +{ + motherPos = getPhotonMotherId(pos, mcparticles); + motherEle = getPhotonMotherId(ele, mcparticles); + if (motherPos >= 0 && motherPos == motherEle) { + return kV0True; + } + if (motherPos >= 0 && motherEle >= 0) { + return kV0CrossLegFake; + } + return kV0OtherFake; +} + +//_______________________________________________________________________ +/// \brief electron-PID compatibility of the two legs: mean |n sigma_e| + 2 * (n sigma_e+ + n sigma_e-) in the TPC +/// \param pos positive leg +/// \param ele negative leg +/// \return kappa +template +float getV0Kappa(TLeg const& pos, TLeg const& ele) +{ + float kappa = (std::fabs(pos.tpcNSigmaEl()) + std::fabs(ele.tpcNSigmaEl())) / 2.f + (2.f * (pos.tpcNSigmaEl() + ele.tpcNSigmaEl())); + return kappa; +} + +//_______________________________________________________________________ +/// \brief gap between the helix circles of two legs in xy +/// \param cPos circle of the positive leg +/// \param cEle circle of the negative leg +/// \return distance of the centres minus the sum of the radii (cm); < 0: the circles overlap +inline float circleGap(o2::track::TrackAuxPar const& cPos, o2::track::TrackAuxPar const& cEle) +{ + return std::hypot(cEle.xC - cPos.xC, cEle.yC - cPos.yC) - (cPos.rC + cEle.rC); +} + +/// \brief return value of tpcDriftSide if the side cannot be determined +constexpr int TPCSideUndetermined = 2; + +//_______________________________________________________________________ +/// \brief TPC side in which a track drifted, from the TPC side flags, else from z, tgl and the time asymmetry +/// \param track track row (flags, z, tgl, tpcDeltaTFwd, tpcDeltaTBwd) +/// \return 1 A side, -1 C side, 0 both sides (crosses the central electrode), TPCSideUndetermined +template +inline int tpcDriftSide(TTrack const& track) +{ + constexpr float MaxCECrossingDeltaTNS = 1000.f; + const auto sides = track.flags() & (o2::aod::track::TPCSideA | o2::aod::track::TPCSideC); + if (sides == o2::aod::track::TPCSideA) { + return 1; + } + if (sides == o2::aod::track::TPCSideC) { + return -1; + } + if (sides != 0) { + return 0; + } + const float dFwd = track.tpcDeltaTFwd(); + const float dBwd = track.tpcDeltaTBwd(); + if (dFwd == dBwd && dFwd < MaxCECrossingDeltaTNS) { + return 0; + } + const bool zPositive = track.z() > 0.f; + const bool tglPositive = track.tgl() > 0.f; + if (zPositive == tglPositive) { + return tglPositive ? 1 : -1; + } + if (dBwd == 0.f && dFwd > 0.f) { + return zPositive ? 1 : -1; + } + if (dBwd > 0.f) { + return tglPositive ? 1 : -1; + } + return TPCSideUndetermined; +} + +/// \brief z and momentum of a track helix at a point of its circle (helixPointAt) +struct HelixPoint { + bool ok = false; + bool inward = false; + float z = 0.f; + std::array p{}; +}; + +//_______________________________________________________________________ +/// \brief z and momentum of a track helix at an xy point of its circle +/// \param par track parameters +/// \param xP x of the point on the circle (cm) +/// \param yP y of the point on the circle (cm) +/// \param xB x of the beam position (cm) +/// \param yB y of the beam position (cm) +/// \param bz magnetic field (kG) +/// \return ok = false for a degenerate circle; inward = the track moves towards the beam at the point (more than 10 cm from +/// the beam), then the momentum is reversed; z and momentum at the point +inline HelixPoint helixPointAt(o2::track::TrackPar const& par, const float xP, const float yP, const float xB, const float yB, const float bz) +{ + HelixPoint h; + const o2::track::TrackAuxPar c(par, bz); + std::array r{}, p{}; + par.getXYZGlo(r); + par.getPxPyPzGlo(p); + const float pt = std::hypot(p[0], p[1]); + if (!(c.rC > 0.f) || !(pt > 0.f)) { + return h; + } + const float sense = (r[0] - c.xC) * p[1] - (r[1] - c.yC) * p[0] > 0.f ? 1.f : -1.f; + float back = RecoDecay::constrainAngle(sense * (std::atan2(r[1] - c.yC, r[0] - c.xC) - std::atan2(yP - c.yC, xP - c.xC)), 0.f); + const float tx = -sense * (yP - c.yC) / c.rC; + const float ty = sense * (xP - c.xC) / c.rC; + constexpr float MinRadiusOrientation = 10.f; + h.inward = std::hypot(xP - xB, yP - yB) > MinRadiusOrientation && tx * (xP - xB) + ty * (yP - yB) < 0.f; + if (!h.inward) { + if (back > o2::constants::math::PI && (r[0] - xB) * p[0] + (r[1] - yB) * p[1] > 0.f) { + back -= o2::constants::math::TwoPI; // o2-linter: disable=two-pi-add-subtract (signed arc length to a point behind the track, not an angle range) + } + h.z = r[2] - par.getTgl() * c.rC * back; + h.p = {pt * tx, pt * ty, pt * par.getTgl()}; + } else { + h.z = r[2] + par.getTgl() * c.rC * (back > 0.f ? o2::constants::math::TwoPI - back : 0.f); + h.p = {-pt * tx, -pt * ty, -pt * par.getTgl()}; + } + h.ok = true; + return h; +} + +//_______________________________________________________________________ +/// \brief compatibility of two track pieces with one track +/// \param trA first track +/// \param trB second track +/// \param bz magnetic field (kG) +/// \return chi2 of (y, snp, tgl, q/pt) at a common x with the summed covariances (4 dof); -1 if the propagation or the inversion fails +template +inline float splitPieceChi2(TTrack const& trA, TTrack const& trB, const float bz) +{ + auto a = getTrackParCov(trA); + auto b = getTrackParCov(trB); + if (!a.rotate(b.getAlpha()) || !a.propagateTo(b.getX(), bz)) { + a = getTrackParCov(trA); + b = getTrackParCov(trB); + if (!b.rotate(a.getAlpha()) || !b.propagateTo(a.getX(), bz)) { + return -1.f; + } + } + ROOT::Math::SMatrix> c; + c(0, 0) = a.getSigmaY2() + b.getSigmaY2(); + c(1, 0) = a.getSigmaSnpY() + b.getSigmaSnpY(); + c(1, 1) = a.getSigmaSnp2() + b.getSigmaSnp2(); + c(2, 0) = a.getSigmaTglY() + b.getSigmaTglY(); + c(2, 1) = a.getSigmaTglSnp() + b.getSigmaTglSnp(); + c(2, 2) = a.getSigmaTgl2() + b.getSigmaTgl2(); + c(3, 0) = a.getSigma1PtY() + b.getSigma1PtY(); + c(3, 1) = a.getSigma1PtSnp() + b.getSigma1PtSnp(); + c(3, 2) = a.getSigma1PtTgl() + b.getSigma1PtTgl(); + c(3, 3) = a.getSigma1Pt2() + b.getSigma1Pt2(); + if (!c.Invert()) { + return -1.f; + } + const ROOT::Math::SVector d(a.getY() - b.getY(), a.getSnp() - b.getSnp(), a.getTgl() - b.getTgl(), a.getQ2Pt() - b.getQ2Pt()); + return static_cast(ROOT::Math::Similarity(c, d)); +} + #endif // PWGEM_PHOTONMESON_UTILS_PCMUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/PairUtilities.h b/PWGEM/PhotonMeson/Utils/PairUtilities.h index 8a1ff20de88..87c45f4ad99 100644 --- a/PWGEM/PhotonMeson/Utils/PairUtilities.h +++ b/PWGEM/PhotonMeson/Utils/PairUtilities.h @@ -18,9 +18,19 @@ #include "Common/Core/RecoDecay.h" #include -#include +#include +#include +#include // IWYU pragma: keep +#include +#include // IWYU pragma: keep +#include + +#include +#include #include +#include +#include namespace o2::aod::pwgem::photonmeson::utils::pairutil { @@ -37,14 +47,14 @@ namespace o2::aod::pwgem::photonmeson::photonpair { enum PairType { kPCMPCM = 0, - kPHOSPHOS = 1, - kEMCEMC = 2, - kPCMPHOS = 3, - kPCMEMC = 4, - kPCMDalitzEE = 5, - kPCMDalitzMuMu = 6, - kPHOSEMC = 7, - kEEEE = 8, // dielectron-dielectron + kPHOSPHOS, + kEMCEMC, + kPCMPHOS, + kPCMEMC, + kPCMDalitzEE, + kPCMDalitzMuMu, + kPHOSEMC, + kEEEE, // dielectron-dielectron kNpair, }; @@ -69,4 +79,309 @@ bool DoesV0LegMatchWithCluster(TV0Leg const& v0leg, TCluster const& cluster, con } } // namespace o2::aod::pwgem::photonmeson::photonpair +namespace o2::aod::pwgem::photonmeson::utils::pairutil +{ +// ─── photon-class selection by leg track composition ────────────────────── + +struct V0PhotonLegCounts { + int nITSTPC{0}; + int nITSOnly{0}; + int nTPCOnly{0}; + int nTRD{0}; + int nTOF{0}; +}; + +template +V0PhotonLegCounts getV0PhotonLegCounts(TLeg const& pos, TLeg const& ele) +{ + V0PhotonLegCounts c; + auto countLeg = [&c](auto const& l) { + const bool its = l.hasITS(); + const bool tpc = l.hasTPC(); + if (its && tpc) { + c.nITSTPC++; + } else if (its) { + c.nITSOnly++; + } else { + c.nTPCOnly++; + } + if (l.hasTRD()) { + c.nTRD++; + } + if (l.hasTOF()) { + c.nTOF++; + } + }; + countLeg(pos); + countLeg(ele); + return c; +} + +struct V0PhotonClassSelection { + int minITSTPC{0}, maxITSTPC{2}; + int minITSOnly{0}, maxITSOnly{2}; + int minTPCOnly{0}, maxTPCOnly{2}; + int minTRD{0}, maxTRD{2}; + int minTOF{0}, maxTOF{2}; + + [[nodiscard]] bool isSelected(V0PhotonLegCounts const& c) const + { + return c.nITSTPC >= minITSTPC && c.nITSTPC <= maxITSTPC && + c.nITSOnly >= minITSOnly && c.nITSOnly <= maxITSOnly && + c.nTPCOnly >= minTPCOnly && c.nTPCOnly <= maxTPCOnly && + c.nTRD >= minTRD && c.nTRD <= maxTRD && + c.nTOF >= minTOF && c.nTOF <= maxTOF; + } +}; + +inline bool isPairPhotonClassSelected(V0PhotonLegCounts const& c1, V0PhotonLegCounts const& c2, + V0PhotonClassSelection const& selA, V0PhotonClassSelection const& selB) +{ + return (selA.isSelected(c1) && selB.isSelected(c2)) || + (selA.isSelected(c2) && selB.isSelected(c1)); +} + +template +V0PhotonClassSelection buildV0PhotonClassSelection(TGroup const& g) +{ + V0PhotonClassSelection s; + s.minITSTPC = g.cfgMinNLegsITSTPC.value; + s.maxITSTPC = g.cfgMaxNLegsITSTPC.value; + s.minITSOnly = g.cfgMinNLegsITSOnly.value; + s.maxITSOnly = g.cfgMaxNLegsITSOnly.value; + s.minTPCOnly = g.cfgMinNLegsTPCOnly.value; + s.maxTPCOnly = g.cfgMaxNLegsTPCOnly.value; + s.minTRD = g.cfgMinNLegsTRD.value; + s.maxTRD = g.cfgMaxNLegsTRD.value; + s.minTOF = g.cfgMinNLegsTOF.value; + s.maxTOF = g.cfgMaxNLegsTOF.value; + return s; +} + +//_______________________________________________________________________ +/// \brief Pair observables of a photon pair: kT, q_inv and the LCMS decomposition +struct PairQ { + float kt{0.f}; + float qinv{0.f}; + float qabsLcms{0.f}; + float qout{0.f}, qside{0.f}, qlong{0.f}; +}; + +//_______________________________________________________________________ +/// \brief Cheap q_inv gate before any boost or sparse fill +/// \param pt1 pT of the first photon +/// \param eta1 eta of the first photon +/// \param phi1 phi of the first photon +/// \param pt2 pT of the second photon +/// \param eta2 eta of the second photon +/// \param phi2 phi of the second photon +/// \param qMax upper limit on q_inv +/// \return true if q_inv <= qMax, using q_inv^2 = 2 pT1 pT2 (cosh(deta) - cos(dphi)), exact for massless particles +[[nodiscard]] inline bool pairBelowQmax(float pt1, float eta1, float phi1, + float pt2, float eta2, float phi2, float qMax) +{ + const float q2 = 2.f * pt1 * pt2 * (std::cosh(eta1 - eta2) - std::cos(phi1 - phi2)); + return q2 <= qMax * qMax; +} + +//_______________________________________________________________________ +/// \brief Computes kT, q_inv and (q_out, q_side, q_long) in the LCMS +/// \param v1 four-vector of the first photon +/// \param v2 four-vector of the second photon +/// \return PairQ; +inline PairQ computePairQ(ROOT::Math::PtEtaPhiMVector const& v1, ROOT::Math::PtEtaPhiMVector const& v2) +{ + PairQ r; + const auto k12 = 0.5 * (v1 + v2); + r.kt = static_cast(k12.Pt()); + r.qinv = std::fabs(static_cast((v1 - v2).M())); + const double ktMag = k12.Pt(); + const ROOT::Math::XYZVector uvOut = (ktMag > 1e-9) // o2-linter: disable=magic-number (guard against a zero-pT pair) + ? ROOT::Math::XYZVector(k12.Px() / ktMag, k12.Py() / ktMag, 0.) + : ROOT::Math::XYZVector(1., 0., 0.); + const ROOT::Math::XYZVector uvLong(0., 0., 1.); + const ROOT::Math::XYZVector uvSide = uvOut.Cross(uvLong); + const ROOT::Math::PxPyPzEVector v1c(v1), v2c(v2); + const double betaZ = (v1 + v2).Beta() * std::cos((v1 + v2).Theta()); + const ROOT::Math::Boost bstZ(0., 0., -betaZ); + const auto q3Lcms = bstZ(v1c - v2c).Vect(); + r.qabsLcms = static_cast(q3Lcms.R()); + r.qout = std::fabs(static_cast(q3Lcms.Dot(uvOut))); + r.qside = std::fabs(static_cast(q3Lcms.Dot(uvSide))); + r.qlong = std::fabs(static_cast(q3Lcms.Dot(uvLong))); + return r; +} + +//_______________________________________________________________________ +/// \brief computePairQ for anything with pt(), eta() and phi(): V0 photons, MC photons, truth records +/// \param a first photon-like object +/// \param b second photon-like object +/// \return PairQ of the massless pair +template +PairQ computePairQ(TA const& a, TB const& b) +{ + return computePairQ(ROOT::Math::PtEtaPhiMVector(a.pt(), a.eta(), a.phi(), 0.f), + ROOT::Math::PtEtaPhiMVector(b.pt(), b.eta(), b.phi(), 0.f)); +} + +//_______________________________________________________________________ +/// \brief One leg of a V0 candidat +struct DedupLeg { + float pt{0.f}, eta{0.f}, phi{0.f}; + float dedx{0.f}; /// TPC dE/dx + float fracShared{0.f}; /// fraction of shared TPC clusters + bool hasTpc{false}; + int64_t trackId{-1}; +}; + +//_______________________________________________________________________ +/// \brief A selected V0 candidate +struct DedupCand { + int64_t gi{-1}; /// globalIndex of the V0 + float eta{0.f}, phi{0.f}, pt{0.f}, rConv{0.f}, chi2{1e9f}; + float vx{0.f}, vy{0.f}, vz{0.f}; + int nITSTPC{0}; + std::array leg{}; /// 0 = e+, 1 = e- +}; + +//_______________________________________________________________________ +/// \brief Tolerances of the duplicate test +struct DedupConfig { + float maxLegDEta{0.01f}; + float maxLegDPhi{0.01f}; + float maxLegPtAsym{0.05f}; + float maxLegDeDxAsym{0.05f}; + float maxDVtx3D{999.f}; + bool requireBothLegs{false}; +}; + +//_______________________________________________________________________ +/// \brief Distance of two same-charge legs of two candidates +struct LegDistance { + float dEta{0.f}, dPhi{0.f}, ptAsym{1.f}, dedxAsym{1.f}, fShared{0.f}; + bool dedxValid{false}; +}; + +//_______________________________________________________________________ +/// \brief Fills DedupLeg from a V0 leg +/// \param leg V0 leg iterator (aod::V0Legs) +/// \return the leg reduced to the duplicate-test variables +template +DedupLeg makeDedupLeg(TLeg const& leg) +{ + DedupLeg l; + l.pt = static_cast(leg.pt()); + l.eta = static_cast(leg.eta()); + l.phi = static_cast(leg.phi()); + l.hasTpc = leg.hasTPC(); + l.dedx = static_cast(leg.tpcSignal()); + l.fracShared = static_cast(leg.tpcFractionSharedCls()); + l.trackId = leg.trackId(); + return l; +} + +//_______________________________________________________________________ +/// \brief Fills DedupCand from a V0 photon and its two legs +/// \param v0 V0 photon iterator +/// \param pos positive leg +/// \param ele negative leg +/// \return the candidate reduced to what defines its identity +template +DedupCand makeDedupCand(TV0 const& v0, TLeg const& pos, TLeg const& ele) +{ + DedupCand c; + c.gi = v0.globalIndex(); + c.eta = v0.eta(); + c.phi = v0.phi(); + c.pt = v0.pt(); + c.rConv = std::hypot(v0.vx(), v0.vy()); + c.chi2 = v0.chiSquareNDF(); + c.vx = v0.vx(); + c.vy = v0.vy(); + c.vz = v0.vz(); + c.nITSTPC = getV0PhotonLegCounts(pos, ele).nITSTPC; + c.leg = {makeDedupLeg(pos), makeDedupLeg(ele)}; + return c; +} + +//_______________________________________________________________________ +/// \brief Distance of two same-charge legs +/// \param a first candidate +/// \param b second candidate +/// \param il 0 = e+ with e+, 1 = e- with e- +/// \return LegDistance; dedxValid is false when a leg has no TPC +[[nodiscard]] inline LegDistance legDistance(DedupCand const& a, DedupCand const& b, int il) +{ + constexpr float kMinSigma = 1e-9f; + const auto& la = a.leg[static_cast(il)]; + const auto& lb = b.leg[static_cast(il)]; + LegDistance d; + d.dEta = std::fabs(la.eta - lb.eta); + d.dPhi = std::fabs(RecoDecay::constrainAngle(la.phi - lb.phi, -o2::constants::math::PI)); + const float ptSum = la.pt + lb.pt; + d.ptAsym = (ptSum > kMinSigma) ? std::fabs(la.pt - lb.pt) / ptSum : 1.f; + d.dedxValid = la.hasTpc && lb.hasTpc; + const float dedxSum = la.dedx + lb.dedx; + d.dedxAsym = (d.dedxValid && dedxSum > kMinSigma) ? std::fabs(la.dedx - lb.dedx) / dedxSum : 1.f; + d.fShared = std::max(la.fracShared, lb.fracShared); + return d; +} + +//_______________________________________________________________________ +/// \brief Test of track identity on reco level: same-charge legs physically the same particle +/// \param a first candidate +/// \param b second candidate +/// \param il 0 = e+, 1 = e- +/// \param cfg tolerances +/// \return true if the two legs agree within all tolerances +[[nodiscard]] inline bool legsIdentical(DedupCand const& a, DedupCand const& b, int il, DedupConfig const& cfg) +{ + if (a.leg[static_cast(il)].trackId == b.leg[static_cast(il)].trackId) { + return false; + } + const auto d = legDistance(a, b, il); + if (d.dEta > cfg.maxLegDEta || d.dPhi > cfg.maxLegDPhi) { + return false; + } + if (d.ptAsym > cfg.maxLegPtAsym) { + return false; + } + if (d.dedxValid && d.dedxAsym > cfg.maxLegDeDxAsym) { + return false; + } + return true; +} + +//_______________________________________________________________________ +/// \brief Test: Duplicate stores +/// \param a first candidate +/// \param b second candidate +/// \param cfg tolerances +/// \return true if at least one same-charge leg pair is identical (both, with cfg.requireBothLegs) and the conversion points are within cfg.maxDVtx3D +[[nodiscard]] inline bool isDuplicatePair(DedupCand const& a, DedupCand const& b, DedupConfig const& cfg) +{ + const float dVtx = std::hypot(a.vx - b.vx, a.vy - b.vy, a.vz - b.vz); + if (dVtx > cfg.maxDVtx3D) { + return false; + } + const bool posSame = legsIdentical(a, b, 0, cfg); + const bool negSame = legsIdentical(a, b, 1, cfg); + return cfg.requireBothLegs ? (posSame && negSame) : (posSame || negSame); +} + +//_______________________________________________________________________ +/// \brief Quality order among duplicates +/// \param a first candidate +/// \param b second candidate +/// \return true if a should be kept over b +[[nodiscard]] inline bool isBetterCand(DedupCand const& a, DedupCand const& b) +{ + if (a.nITSTPC != b.nITSTPC) { + return a.nITSTPC > b.nITSTPC; + } + return a.chi2 < b.chi2; +} + +} // namespace o2::aod::pwgem::photonmeson::utils::pairutil + #endif // PWGEM_PHOTONMESON_UTILS_PAIRUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/ParticleOrigin.h b/PWGEM/PhotonMeson/Utils/ParticleOrigin.h new file mode 100644 index 00000000000..86e0c3b1470 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/ParticleOrigin.h @@ -0,0 +1,46 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ParticleOrigin.h +/// \brief commonly used enums for particle origins. +/// \author marvin.hemmer@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_PARTICLEORIGIN_H_ +#define PWGEM_PHOTONMESON_UTILS_PARTICLEORIGIN_H_ + +#include + +namespace o2::analysis::em +{ + +// Classifies the production history of a lepton +enum class LeptonOrigin : uint8_t { + Conversion = 0, + Compton = 1, + PhotoElectric = 2, + DeltaRay = 3, + DirectMesonDecay = 4, + Other = 5 +}; + +// Classifies the production history of a photon +enum class PhotonOrigin : uint8_t { + Direct = 0, // direct photons from quarks or gluons from inital scattering + Decay = 1, // decay photons from + Bremsstrahlung = 2, // photon from bremsstrahlung + Annihilation = 3, // e+e- -> gammagamma + Hadronic = 4, // hadronic interactions like charged pions with material + Other = 5 // anything else +}; + +} // namespace o2::analysis::em + +#endif // PWGEM_PHOTONMESON_UTILS_PARTICLEORIGIN_H_ diff --git a/PWGEM/PhotonMeson/Utils/TrackSelection.h b/PWGEM/PhotonMeson/Utils/TrackSelection.h index 941a7ea2bc0..0b9b70b0090 100644 --- a/PWGEM/PhotonMeson/Utils/TrackSelection.h +++ b/PWGEM/PhotonMeson/Utils/TrackSelection.h @@ -16,7 +16,7 @@ #ifndef PWGEM_PHOTONMESON_UTILS_TRACKSELECTION_H_ #define PWGEM_PHOTONMESON_UTILS_TRACKSELECTION_H_ -#include +#include #include diff --git a/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h b/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h new file mode 100644 index 00000000000..757d86b5a86 --- /dev/null +++ b/PWGEM/PhotonMeson/Utils/V0DedupUtilities.h @@ -0,0 +1,309 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file V0DedupUtilities.h +/// \brief helpers of the photon conversion builder: conflict-group solver of the deduplication and the geometry of the photon finder +/// \author Stefanie Mrozinski, stefanie.mrozinski@cern.ch + +#ifndef PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ +#define PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ + +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::pwgem::photonmeson +{ + +//_______________________________________________________________________ +/// \brief q_inv of two massless particles (photons) +/// \param px1 px of the first photon (GeV/c), same for py1, pz1 +/// \param px2 px of the second photon (GeV/c), same for py2, pz2 +/// \return sqrt(2 (|p1| |p2| - p1 . p2)) (GeV/c) +inline float qInv(const float px1, const float py1, const float pz1, const float px2, const float py2, const float pz2) +{ + return std::sqrt(std::max(0.f, 2.f * (std::hypot(px1, py1, pz1) * std::hypot(px2, py2, pz2) - px1 * px2 - py1 * py2 - pz1 * pz2))); +} + +/// \brief result of solveConflictGroup +struct ConflictGroupSolution { + uint32_t bestMask = 0; + int bestCount = 0; + float bestScore = 0.f; + std::vector bestWithCount; // per candidate: size of the best subset that contains it + std::vector bestWithScore; // per candidate: score sum of that subset +}; + +//_______________________________________________________________________ +/// \brief best leg-disjoint subset of a conflict group of the deduplication (mode 2), exact enumeration of all subsets +/// \param legMask per candidate: bit mask of its legs (group-local bits); at most 16 candidates +/// \param score per candidate: dedup score, lower = better +/// \param s0 objective: <= 0 maximum number of candidates, then minimum score sum; > 0 maximum sum of max(s0 - score, 1e-3), +/// ties by the score sum +/// \param memberBest also find, per candidate, the best subset that contains it (MC diagnostics) +/// \return mask of the best subset, its size and score sum, and the per-candidate results +inline ConflictGroupSolution solveConflictGroup(std::vector const& legMask, std::vector const& score, const float s0, const bool memberBest) +{ + const int nMembers = static_cast(legMask.size()); + const bool weighted = s0 > 0.f; + constexpr float MinWeight = 1e-3f; + constexpr float WeightTolerance = 1e-6f; + ConflictGroupSolution sol; + sol.bestWithCount.assign(nMembers, -1); + sol.bestWithScore.assign(nMembers, 0.f); + float bestWeight = -1.f; + for (uint32_t mask = 1; mask < (1u << nMembers); ++mask) { + uint64_t legs = 0; + float scoreSum = 0.f; + float weightSum = 0.f; + int count = 0; + bool disjoint = true; + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if ((legs & legMask[k]) != 0) { + disjoint = false; + break; + } + legs |= legMask[k]; + scoreSum += score[k]; + weightSum += std::max(s0 - score[k], MinWeight); + ++count; + } + if (!disjoint) { + continue; + } + const bool better = weighted ? (weightSum > bestWeight + WeightTolerance || (std::fabs(weightSum - bestWeight) <= WeightTolerance && scoreSum < sol.bestScore)) + : (count > sol.bestCount || (count == sol.bestCount && scoreSum < sol.bestScore)); + if (better) { + sol.bestCount = count; + sol.bestScore = scoreSum; + bestWeight = weightSum; + sol.bestMask = mask; + } + if (memberBest) { + for (int k = 0; k < nMembers; ++k) { + if ((mask & (1u << k)) == 0) { + continue; + } + if (count > sol.bestWithCount[k] || (count == sol.bestWithCount[k] && scoreSum < sol.bestWithScore[k])) { + sol.bestWithCount[k] = count; + sol.bestWithScore[k] = scoreSum; + } + } + } + } + return sol; +} + +/// \brief steps of the photon finder (pairing of free legs at the touching point of their helix circles); evalFinderPair returns the last step passed +enum FinderStep { + kFindTglWindow = 0, + kFindTime, + kFindGap, + kFindRTouch, + kFindPointing, + kFindAlongMotion, + kFindZMatch, + kFindCollision, + kNFindStep +}; + +/// \brief collision for the photon finder: time (ns, relative to the first BC of the DF), index, vertex (cm) +struct FinderCollision { + double t = 0.; + int64_t id = -1; + float x = 0.f, y = 0.f, z = 0.f; +}; + +/// \brief leg for the photon finder: track parameters, helix circle, TPC drift side and the time window (ns) of the track +struct FinderLeg { + int64_t id = -1; + float tgl = 0.f; + bool noITS = false; + bool movable = false; + int side = 0; + double t = 0., tMin = 0., tMax = 0.; + o2::track::TrackPar par; + o2::track::TrackAuxPar circle; +}; + +/// \brief cuts of the photon finder (cm) and the magnetic field (kG) +struct FinderCuts { + float maxDeltaTgl = 0.f; + float maxCircleGap = 0.f; + float minRTPConly = 0.f; + float maxRReqITS = 0.f; + float rMargin = 0.f; + float maxR = 0.f; + float minCosXY = 0.f; + float maxDeltaZ = 0.f; + float maxDz = 0.f; + float bz = 0.f; +}; + +//_______________________________________________________________________ +/// \brief geometric test of one leg pair of the photon finder: tgl, time windows, circle gap, touching radius, xy pointing to the +/// beam, direction of motion, z match at the touching point (incl. the drift of TPC-only legs), collision by z +/// \param p positive leg +/// \param e negative leg +/// \param colls collisions of the DF, sorted by time +/// \param cuts cuts of the finder +/// \param xB x of the beam position (cm) +/// \param yB y of the beam position (cm) +/// \param vDrift TPC drift velocity (cm/ns) +/// \param colId output: index of the matched collision, -1 if none +/// \return last FinderStep passed, -1 if the tgl window fails; kFindCollision if a collision matches +inline int evalFinderPair(FinderLeg const& p, FinderLeg const& e, std::vector const& colls, FinderCuts const& cuts, const float xB, const float yB, const double vDrift, int64_t& colId) +{ + colId = -1; + if (std::fabs(p.tgl - e.tgl) > cuts.maxDeltaTgl) { + return -1; + } + const double tLo = std::max(p.tMin, e.tMin); + const double tHi = std::min(p.tMax, e.tMax); + if (tLo > tHi) { + return kFindTglWindow; + } + const auto& cp = p.circle; + const auto& ce = e.circle; + const float dxC = ce.xC - cp.xC; + const float dyC = ce.yC - cp.yC; + const float dC = std::hypot(dxC, dyC); + if (std::fabs(circleGap(cp, ce)) > cuts.maxCircleGap) { + return kFindTime; + } + const float xT = (cp.xC * ce.rC + ce.xC * cp.rC) / (cp.rC + ce.rC); + const float yT = (cp.yC * ce.rC + ce.yC * cp.rC) / (cp.rC + ce.rC); + const float rTouch = std::hypot(xT, yT); + const float rMin = (p.noITS && e.noITS ? cuts.minRTPConly : cuts.maxRReqITS) - cuts.rMargin; + if (rTouch > cuts.maxR || rTouch < rMin) { + return kFindGap; + } + const float dB = std::hypot(xT - xB, yT - yB); + if (!(dC > 0.f) || !(dB > 0.f) || std::fabs((xT - xB) * (-dyC / dC) + (yT - yB) * (dxC / dC)) / dB < cuts.minCosXY) { + return kFindRTouch; + } + auto nearest = [&](o2::track::TrackAuxPar const& c, float& x, float& y) { + const float d = std::max(std::hypot(xT - c.xC, yT - c.yC), 1e-6f); + x = c.xC + c.rC * (xT - c.xC) / d; + y = c.yC + c.rC * (yT - c.yC) / d; + }; + float xP = 0.f, yP = 0.f, xE = 0.f, yE = 0.f; + nearest(cp, xP, yP); + nearest(ce, xE, yE); + const auto hp = helixPointAt(p.par, xP, yP, xB, yB, cuts.bz); + const auto he = helixPointAt(e.par, xE, yE, xB, yB, cuts.bz); + if (!hp.ok || !he.ok || hp.inward || he.inward) { + return kFindPointing; + } + const double b = vDrift * (p.side - e.side); + const double a = (hp.z - p.side * vDrift * p.t) - (he.z - e.side * vDrift * e.t); + const double tBest = b == 0. ? tLo : std::clamp(-a / b, tLo, tHi); + if (std::fabs(a + b * tBest) > cuts.maxDeltaZ) { + return kFindAlongMotion; + } + const float ptG = std::hypot(hp.p[0] + he.p[0], hp.p[1] + he.p[1]); + const float tglG = ptG > 0.f ? (hp.p[2] + he.p[2]) / ptG : 0.f; + float best = std::numeric_limits::max(); + for (auto it = std::lower_bound(colls.begin(), colls.end(), tLo, [](const FinderCollision& c, double t) { return c.t < t; }); it != colls.end() && it->t <= tHi; ++it) { + const auto zPos = static_cast(hp.z + p.side * vDrift * (it->t - p.t)); + const auto zEle = static_cast(he.z + e.side * vDrift * (it->t - e.t)); + const float deltaZ = zPos - zEle; + const float dz = (zPos * ce.rC + zEle * cp.rC) / (cp.rC + ce.rC) - std::hypot(xT - it->x, yT - it->y) * tglG - it->z; + const float dist = std::hypot(dz, deltaZ); + if (dist < best && std::fabs(dz) < cuts.maxDz && std::fabs(deltaZ) < cuts.maxDeltaZ) { + best = dist; + colId = it->id; + } + } + return colId >= 0 ? kFindCollision : kFindZMatch; +} + +/// \brief input features of the finder purity score (naive-Bayes log-likelihood ratio, finderScore_v1, PbPb MC LHC26a5a) +enum FinderScoreFeature { + kScoreCosPA = 0, // 3D cosine of the pointing angle + kScorePCA, // distance of the legs at the conversion point (cm) + kScoreMee, // e+e- mass at the conversion point (GeV/c^2) + kScoreGap, // helix-circle gap of the legs (cm) + kScoreDTgl, // |tgl+ - tgl-| + kScorePsiPair, + kScoreMaxNSigmaEl, // max(|n sigma_e|) of the legs in the TPC + kScoreMinNSigmaPi, // min(|n sigma_pi|) of the legs in the TPC + kNScoreFeatures +}; + +/// \brief log-likelihood-ratio table of one feature: nBins uniform bins in [lo, hi), llr[0] underflow, llr[nBins + 1] overflow +/// (same bin lookup as TH1::FindBin) +struct FinderScoreTable { + int nBins; + double lo; + double hi; + const double* llr; +}; + +/// \brief log(p_true / p_fake) per bin of each feature (finderScore_v1) +inline constexpr std::array FinderLlrCosPA = {-0.42326231102291223, -3.0746401250262112, -2.902679468526141, -2.7737498628462491, -2.8653323764415854, -2.9977154358984874, -2.879287982624958, -2.9449559801189884, -2.8305718792936898, -2.7610493575753212, -2.6743115383729115, -2.739759837578176, -2.7445126982363175, -2.6010956990444813, -2.5377329273302194, -2.4413963521466235, -2.5007549142569676, -2.5484647707016048, -2.3339023465737707, -2.3706555897531327, -2.3350661347670365, -2.3987685637953868, -2.2562477335729185, -2.2430310349096305, -2.1429211398936303, -2.1233771903398493, -2.0022839108013391, -1.9558000169723206, -1.8729479034124974, -1.9065302845458256, -1.8542708039089619, -1.7230031706190478, -1.6490950919627059, -1.633800343346663, -1.5180730364736743, -1.389137677593709, -1.3181114339137541, -1.2615432549457208, -1.1021895055813662, -0.97913284177589832, -0.97669632321874755, -0.81070582095170196, -0.70030094035730539, -0.55130110346066707, -0.34392181466201621, -0.16444806113591265, 0.025741391801742678, 0.22392951134446418, 0.48449501950084528, 0.74029958080748082, 1.3544181524605163, 2.1416870464386246}; +inline constexpr std::array FinderLlrPca = {-0.42328563731423596, 0.74173300442294698, 0.60064005460866021, 0.48774214670698213, 0.43172212684872524, 0.34949695425445604, 0.2876013953788461, 0.13114334603545452, -0.013681195276045386, -0.10436433242742214, -0.27099684361934573, -0.38200720615175005, -0.47403001970996045, -0.57728096929145511, -0.65985610396473016, -0.75403841222733481, -0.76619973024014831, -0.76182156236311549, -0.84274340404963699, -0.93501651159467125, -0.99602554213200045, -1.029448537116822, -1.1240438482723172, -1.124554873172243, -1.2439733445571921, -1.2077440689826793, -1.3081144381420802, -1.3626265850153199, -1.3892300573742717, -1.3978783977548588, -1.4463232066948644, -0.42328563731423596}; +inline constexpr std::array FinderLlrMee = {-0.42329146929478079, 0.82940375143002754, 0.6225570701387575, 0.30541176737301567, -0.042962035242895512, -0.43745174446394713, -0.78901712806679281, -1.1834130413160895, -1.5486037315484942, -1.8446313857300292, -2.2387380284795446, -2.1811493868471543, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079, -0.42329146929478079}; +inline constexpr std::array FinderLlrGap = {-0.42329730143842487, -3.0127420263468552, -2.6803080781356927, -2.2310834132674926, -1.852433867664681, -1.4426351719772665, -1.1194240909923954, -0.72769983474283484, -0.24592785900768224, 0.40475570320106047, 1.4082550958014817, 1.1535415604659081, 0.43945228763843225, 0.14326171246634631, -0.19523294768817565, -0.39258756076294099, -0.41539680606824775, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487, -0.42329730143842487}; +inline constexpr std::array FinderLlrDTgl = {-0.42330313374517559, 0.84513809550928687, 0.46735826574627454, 0.13082343003051436, -0.12182979016333238, -0.33846868362980387, -0.52696466906551476, -0.72450796222957359, -0.89155673129371438, -1.0310133083500654, -1.2327015991375558, -1.3726345840013925, -1.4519781151564646, -1.7004785855213946, -1.7559986132128569, -1.818203125662764, -0.42330313374517559}; +inline constexpr std::array FinderLlrPsiPair = {-0.42330196727077668, 1.5226081817845367, 0.42399589311642699, 1.6969615689293145, 1.6135799599902634, 0.44573587975283285, 0.50095693425255516, 0.43535965176674207, 0.0028547913604746104, -0.0059523726814127804, 0.61779960880440121, 0.82263503615419142, 0.81841116503800626, 0.99808371366038406, 0.57522686284035063, 0.80047346435133904, 2.521137011895664, 2.2392858597546761}; +inline constexpr std::array FinderLlrMaxNSigmaEl = {-0.42331129924865812, 0.58307082727859216, 0.52708190318359982, 0.41704446150850366, 0.16536503067868297, -0.38368187287820599, -1.4189814933894278, -3.0185512358113016, -5.190101150118414, -0.42331129924865812}; +inline constexpr std::array FinderLlrMinNSigmaPi = {-0.42330896621503916, -0.9609371208084968, -0.46431089172823747, -0.06050992822674324, 0.16077238768804142, 0.3920347340880036, 0.64255032840474002, 0.82818496003043784, 0.96447986209716363, 1.0700938177484094, 1.1082284119787249, 1.017209914244056}; + +/// \brief binning and LLR table of each feature, in the order of FinderScoreFeature +inline constexpr std::array FinderScoreTables = {{ + {50, 0.99000000953674316, 1, FinderLlrCosPA.data()}, + {30, 0, 3, FinderLlrPca.data()}, + {25, 0, 0.05000000074505806, FinderLlrMee.data()}, + {20, -5, 5, FinderLlrGap.data()}, + {15, 0, 0.15000000596046448, FinderLlrDTgl.data()}, + {16, -1.6000000238418579, 1.6000000238418579, FinderLlrPsiPair.data()}, + {8, 0, 4, FinderLlrMaxNSigmaEl.data()}, + {10, 0, 10, FinderLlrMinNSigmaPi.data()}, +}}; + +//_______________________________________________________________________ +/// \brief purity score of a finder candidate: log(p_true / p_fake) summed over the 8 features (naive Bayes) +/// \param x feature values in the order of FinderScoreFeature +/// \return score; > 0: more likely a true photon +inline float finderPurityScore(std::array const& x) +{ + float score = 0.f; + for (int i = 0; i < kNScoreFeatures; ++i) { + const auto& t = FinderScoreTables[i]; + const double v = x[i]; + int bin = 0; + if (v < t.lo) { + bin = 0; + } else if (!(v < t.hi)) { + bin = t.nBins + 1; + } else { + bin = 1 + static_cast(t.nBins * (v - t.lo) / (t.hi - t.lo)); + } + score += t.llr[bin]; + } + return score; +} + +} // namespace o2::pwgem::photonmeson + +#endif // PWGEM_PHOTONMESON_UTILS_V0DEDUPUTILITIES_H_ diff --git a/PWGEM/PhotonMeson/Utils/gammaConvDefinitions.h b/PWGEM/PhotonMeson/Utils/gammaConvDefinitions.h index 0ef33364c80..b5a7be0d04a 100644 --- a/PWGEM/PhotonMeson/Utils/gammaConvDefinitions.h +++ b/PWGEM/PhotonMeson/Utils/gammaConvDefinitions.h @@ -35,7 +35,7 @@ o2::framework::AxisSpec const gAxis_TPCdEdxSig{401, -10.025f, 10.025f}; o2::framework::AxisSpec const gAxis_radRes{800, -o2::constants::math::PI, o2::constants::math::PI}; o2::framework::AxisSpec const gAxis_xyz{2400, -300.f, 300.f}; o2::framework::AxisSpec const gAxis_chi2{501, -1.f, 500.f}; -o2::framework::AxisSpec gAxis_pT_log{800, 0.01f, 25.f}; +o2::framework::AxisSpec const gAxis_pT_log{800, 0.01f, 25.f}; o2::framework::HistogramSpec const gHistoSpec_hCollisionZ_all_MCTrue{"hCollisionZ_all_MCTrue", "hCollisionZ_all_MCTrue;z (cm);counts", {o2::framework::HistType::kTH1F, {gAxis_zColl}}}; o2::framework::HistogramSpec const gHistoSpec_hCollisionZ_MCTrue{"hCollisionZ_MCTrue", "hCollisionZ_MCTrue;z (cm);counts", {o2::framework::HistType::kTH1F, {gAxis_zColl}}}; diff --git a/PWGEM/Tasks/phosCalibration.cxx b/PWGEM/Tasks/phosCalibration.cxx index 5e62addf7dc..0f244127a0e 100644 --- a/PWGEM/Tasks/phosCalibration.cxx +++ b/PWGEM/Tasks/phosCalibration.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include using namespace o2; diff --git a/PWGEM/Tasks/phosCluQA.cxx b/PWGEM/Tasks/phosCluQA.cxx index 62d171a7690..94c332a2208 100644 --- a/PWGEM/Tasks/phosCluQA.cxx +++ b/PWGEM/Tasks/phosCluQA.cxx @@ -730,7 +730,7 @@ struct phosCluQA { // If several collisions appear in BC, choose one with largers number of contributors std::map colMap; - for (auto cl : colls) { + for (const auto& cl : colls) { auto colbc = colMap.find(cl.bc_as().globalBC()); if (colbc == colMap.end()) { // single collision per BC colMap[cl.bc_as().globalBC()] = 1; diff --git a/PWGEM/Tasks/phosNbar.cxx b/PWGEM/Tasks/phosNbar.cxx index 56447345bc0..8b0f99c8120 100644 --- a/PWGEM/Tasks/phosNbar.cxx +++ b/PWGEM/Tasks/phosNbar.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -40,8 +41,6 @@ #include -#include - #include #include #include @@ -249,7 +248,7 @@ struct phosNbar { if constexpr (isMC) { // check current collision Id for clusters int cluMcBCId = -1; - for (auto clu : clusters) { + for (const auto& clu : clusters) { auto mcList = clu.labels(); // const std::vector int nParents = mcList.size(); for (int iParent = 0; iParent < nParents; iParent++) { // Not found nbar parent yiet @@ -266,7 +265,7 @@ struct phosNbar { } // Scan MC particles in current MC event if (mcParticles->begin() != mcParticles->end()) { - for (auto part : *mcParticles) { + for (const auto& part : *mcParticles) { if (part.mcCollision().bcId() != cluMcBCId) { continue; if (part.pdgCode() == -3112) { // Sigma+ @@ -385,7 +384,7 @@ struct phosNbar { return; // do not fill Mixed, do not update stack of events } for (auto tr : piEvent) { - for (auto nbarMixEv : mixNbarEvts[mixIndex]) { + for (const auto& nbarMixEv : mixNbarEvts[mixIndex]) { for (auto nbar : nbarMixEv) { double dca = 999.; switch (mPairingMethod) { @@ -418,7 +417,7 @@ struct phosNbar { } } } - for (auto trMixEvent : mixTrackEvts[mixIndex]) { + for (const auto& trMixEvent : mixTrackEvts[mixIndex]) { for (auto tr : trMixEvent) { for (auto nbar : nbarEvent) { double dca = 999.; diff --git a/PWGEM/Tasks/phosTrigQA.cxx b/PWGEM/Tasks/phosTrigQA.cxx index 6cea670f118..c7e351672ab 100644 --- a/PWGEM/Tasks/phosTrigQA.cxx +++ b/PWGEM/Tasks/phosTrigQA.cxx @@ -181,7 +181,7 @@ struct phosTrigQA { // If several collisions appear in BC, choose one with largers number of contributors std::map colMap; - for (auto cl : colls) { + for (const auto& cl : colls) { auto colbc = colMap.find(cl.bc().globalBC()); if (colbc == colMap.end()) { // single collision per BC colMap[cl.bc().globalBC()] = 1; diff --git a/PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h b/PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h new file mode 100644 index 00000000000..ddf7bacc33d --- /dev/null +++ b/PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h @@ -0,0 +1,208 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CandidateReconstructionTables.h +/// \brief Definitions of tables produced by candidate reconstruction workflows +/// +/// \author Gian Michele Innocenti , CERN +/// \author Vít Kučera , CERN + +#ifndef PWGHF_ALICE3_DATAMODEL_CANDIDATERECONSTRUCTIONTABLES_H_ +#define PWGHF_ALICE3_DATAMODEL_CANDIDATERECONSTRUCTIONTABLES_H_ + +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" + +#include +#include +#include + +namespace o2::aod +{ +// specific chic candidate properties +namespace hf_cand_chic +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand2Prong, "_0"); // Jpsi index +// DECLARE_SOA_INDEX_COLUMN_FULL(Prong1, prong1, int, ECALs, "_1"); +DECLARE_SOA_COLUMN(JpsiToMuMuMass, jpsiToMuMuMass, float); // Jpsi mass +} // namespace hf_cand_chic + +// declare dedicated chi_c candidate table +DECLARE_SOA_TABLE(HfCandChicBase, "AOD", "HFCANDCHICBASE", + // general columns + HFCAND_COLUMNS, + // 2-prong specific columns + hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, + hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, + hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, + hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + hf_cand_chic::Prong0Id, /*hf_cand_chic::Prong1Id,*/ + hf_track_index::HFflag, hf_cand_chic::JpsiToMuMuMass, + /* dynamic columns */ + hf_cand_2prong::M, + hf_cand_2prong::M2, + /* prong 2 */ + // hf_cand::PtProng1, + // hf_cand::Pt2Prong1, + // hf_cand::PVectorProng1, + /* dynamic columns that use candidate momentum components */ + hf_cand::Pt, + hf_cand::Pt2, + hf_cand::P, + hf_cand::P2, + hf_cand::PVector, + hf_cand::Cpa, + hf_cand::CpaXY, + hf_cand::Ct, + hf_cand::ImpactParameterXY, + hf_cand_2prong::MaxNormalisedDeltaIP, + hf_cand::Eta, + hf_cand::Phi, + hf_cand::Y, + hf_cand::E, + hf_cand::E2); + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(HfCandChicExt, HfCandChicBase, "HFCANDCHICEXT", + hf_cand_2prong::Px, hf_cand_2prong::Py, hf_cand_2prong::Pz); + +using HfCandChic = HfCandChicExt; + +// table with results of reconstruction level MC matching +DECLARE_SOA_TABLE(HfCandChicMcRec, "AOD", "HFCANDCHICMCREC", //! + hf_cand_mc_flag::FlagMcMatchRec, + hf_cand_mc_flag::OriginMcRec, + hf_cand_mc_flag::FlagMcDecayChanRec); + +// table with results of generator level MC matching +DECLARE_SOA_TABLE(HfCandChicMcGen, "AOD", "HFCANDCHICMCGEN", //! + hf_cand_mc_flag::FlagMcMatchGen, + hf_cand_mc_flag::OriginMcGen, + hf_cand_mc_flag::FlagMcDecayChanGen); + +namespace hf_cand_x +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand2Prong, "_0"); // Jpsi index +} // namespace hf_cand_x + +// declare dedicated X candidate table +DECLARE_SOA_TABLE(HfCandXBase, "AOD", "HFCANDXBASE", + // general columns + HFCAND_COLUMNS, + // 3-prong specific columns + hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, + hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, + hf_cand::PxProng2, hf_cand::PyProng2, hf_cand::PzProng2, + hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ImpactParameter2, + hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, hf_cand::ErrorImpactParameter2, + hf_cand_x::Prong0Id, hf_track_index::Prong1Id, hf_track_index::Prong2Id, // note the difference between Jpsi and pion indices + hf_track_index::HFflag, + /* dynamic columns */ + hf_cand_3prong::M, + hf_cand_3prong::M2, + /* prong 2 */ + hf_cand::PtProng2, + hf_cand::Pt2Prong2, + hf_cand::PVectorProng2, + /* dynamic columns that use candidate momentum components */ + hf_cand::Pt, + hf_cand::Pt2, + hf_cand::P, + hf_cand::P2, + hf_cand::PVector, + hf_cand::Cpa, + hf_cand::CpaXY, + hf_cand::Ct, + hf_cand::ImpactParameterXY, + hf_cand_3prong::MaxNormalisedDeltaIP, + hf_cand::Eta, + hf_cand::Phi, + hf_cand::Y, + hf_cand::E, + hf_cand::E2); + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(HfCandXExt, HfCandXBase, "HFCANDXEXT", + hf_cand_3prong::Px, hf_cand_3prong::Py, hf_cand_3prong::Pz); + +using HfCandX = HfCandXExt; + +// table with results of reconstruction level MC matching +DECLARE_SOA_TABLE(HfCandXMcRec, "AOD", "HFCANDXMCREC", //! + hf_cand_mc_flag::FlagMcMatchRec, + hf_cand_mc_flag::OriginMcRec, + hf_cand_mc_flag::FlagMcDecayChanRec); + +// table with results of generator level MC matching +DECLARE_SOA_TABLE(HfCandXMcGen, "AOD", "HFCANDXMCGEN", //! + hf_cand_mc_flag::FlagMcMatchGen, + hf_cand_mc_flag::OriginMcGen, + hf_cand_mc_flag::FlagMcDecayChanGen); + +// specific Xicc candidate properties +namespace hf_cand_xicc +{ +DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand3Prong, "_0"); // Xic index +} // namespace hf_cand_xicc + +// declare dedicated Xicc candidate table +DECLARE_SOA_TABLE(HfCandXiccBase, "AOD", "HFCANDXICCBASE", + // general columns + HFCAND_COLUMNS, + // 2-prong specific columns + hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, + hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, + hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, + hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + hf_cand_xicc::Prong0Id, hf_track_index::Prong1Id, + hf_track_index::HFflag, + /* dynamic columns */ + hf_cand_2prong::M, + hf_cand_2prong::M2, + hf_cand_2prong::ImpactParameterProduct, + /* dynamic columns that use candidate momentum components */ + hf_cand::Pt, + hf_cand::Pt2, + hf_cand::P, + hf_cand::P2, + hf_cand::PVector, + hf_cand::Cpa, + hf_cand::CpaXY, + hf_cand::Ct, + hf_cand::ImpactParameterXY, + hf_cand_2prong::MaxNormalisedDeltaIP, + hf_cand::Eta, + hf_cand::Phi, + hf_cand::Y, + hf_cand::E, + hf_cand::E2); + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(HfCandXiccExt, HfCandXiccBase, "HFCANDXICCEXT", + hf_cand_2prong::Px, hf_cand_2prong::Py, hf_cand_2prong::Pz); + +using HfCandXicc = HfCandXiccExt; + +// table with results of reconstruction level MC matching +DECLARE_SOA_TABLE(HfCandXiccMcRec, "AOD", "HFCANDXICCMCREC", //! + hf_cand_mc_flag::FlagMcMatchRec, + hf_cand_mc_flag::OriginMcRec, + hf_cand_mc_flag::DebugMcRec); + +// table with results of generator level MC matching +DECLARE_SOA_TABLE(HfCandXiccMcGen, "AOD", "HFCANDXICCMCGEN", //! + hf_cand_mc_flag::FlagMcMatchGen, + hf_cand_mc_flag::OriginMcGen); + +} // namespace o2::aod + +#endif // PWGHF_ALICE3_DATAMODEL_CANDIDATERECONSTRUCTIONTABLES_H_ diff --git a/PWGHF/ALICE3/DataModel/CandidateSelectionTables.h b/PWGHF/ALICE3/DataModel/CandidateSelectionTables.h new file mode 100644 index 00000000000..f2d45eb55ad --- /dev/null +++ b/PWGHF/ALICE3/DataModel/CandidateSelectionTables.h @@ -0,0 +1,166 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CandidateSelectionTables.h +/// \brief Definitions of tables produced by candidate selectors +/// +/// \author Nima Zardoshti , CERN + +#ifndef PWGHF_ALICE3_DATAMODEL_CANDIDATESELECTIONTABLES_H_ +#define PWGHF_ALICE3_DATAMODEL_CANDIDATESELECTIONTABLES_H_ + +#include + +namespace o2::aod +{ +namespace hf_sel_candidate_d0_parametrized_pid +{ +DECLARE_SOA_COLUMN(IsSelD0NoPid, isSelD0NoPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0PerfectPid, isSelD0PerfectPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0, isSelD0, int); //! +DECLARE_SOA_COLUMN(IsSelD0barNoPid, isSelD0barNoPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0barPerfectPid, isSelD0barPerfectPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0bar, isSelD0bar, int); //! +} // namespace hf_sel_candidate_d0_parametrized_pid + +DECLARE_SOA_TABLE(HfSelD0ParametrizedPid, "AOD", "HFSELD0P", //! + hf_sel_candidate_d0_parametrized_pid::IsSelD0NoPid, + hf_sel_candidate_d0_parametrized_pid::IsSelD0PerfectPid, + hf_sel_candidate_d0_parametrized_pid::IsSelD0, + hf_sel_candidate_d0_parametrized_pid::IsSelD0barNoPid, + hf_sel_candidate_d0_parametrized_pid::IsSelD0barPerfectPid, + hf_sel_candidate_d0_parametrized_pid::IsSelD0bar); + +namespace hf_sel_candidate_d0_alice3_barrel +{ +DECLARE_SOA_COLUMN(IsSelHfFlag, isSelHfFlag, int); //! +DECLARE_SOA_COLUMN(IsSelD0NoPid, isSelD0NoPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0PerfectPid, isSelD0PerfectPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0TofPid, isSelD0TofPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0RichPid, isSelD0RichPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0TofPlusRichPid, isSelD0TofPlusRichPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0barTofPlusRichPid, isSelD0barTofPlusRichPid, int); //! +} // namespace hf_sel_candidate_d0_alice3_barrel + +DECLARE_SOA_TABLE(HfSelD0Alice3Barrel, "AOD", "HFSELD0A3B", //! + hf_sel_candidate_d0_alice3_barrel::IsSelHfFlag, + hf_sel_candidate_d0_alice3_barrel::IsSelD0NoPid, + hf_sel_candidate_d0_alice3_barrel::IsSelD0PerfectPid, + hf_sel_candidate_d0_alice3_barrel::IsSelD0TofPid, + hf_sel_candidate_d0_alice3_barrel::IsSelD0RichPid, + hf_sel_candidate_d0_alice3_barrel::IsSelD0TofPlusRichPid, + hf_sel_candidate_d0_alice3_barrel::IsSelD0barTofPlusRichPid); + +namespace hf_sel_candidate_d0_alice3_forward +{ +DECLARE_SOA_COLUMN(IsSelHfFlag, isSelHfFlag, int); //! +DECLARE_SOA_COLUMN(IsSelD0FNoPid, isSelD0FNoPid, int); //! +DECLARE_SOA_COLUMN(IsSelD0FRichPid, isSelD0FRichPid, int); //! +} // namespace hf_sel_candidate_d0_alice3_forward + +DECLARE_SOA_TABLE(HfSelD0Alice3Forward, "AOD", "HFSELD0A3F", //! + hf_sel_candidate_d0_alice3_forward::IsSelHfFlag, + hf_sel_candidate_d0_alice3_forward::IsSelD0FNoPid, + hf_sel_candidate_d0_alice3_forward::IsSelD0FRichPid); + +namespace hf_sel_candidate_lc_alice3 +{ +DECLARE_SOA_COLUMN(IsSelLcToPKPiNoPid, isSelLcToPKPiNoPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPKPiPerfectPid, isSelLcToPKPiPerfectPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPKPiTofPid, isSelLcToPKPiTofPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPKPiTofPlusRichPid, isSelLcToPKPiTofPlusRichPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPiKPNoPid, isSelLcToPiKPNoPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPiKPPerfectPid, isSelLcToPiKPPerfectPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPiKPTofPid, isSelLcToPiKPTofPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPiKPTofPlusRichPid, isSelLcToPiKPTofPlusRichPid, int); //! +} // namespace hf_sel_candidate_lc_alice3 + +DECLARE_SOA_TABLE(HfSelLcAlice3, "AOD", "HFSELLCA3B", //! + hf_sel_candidate_lc_alice3::IsSelLcToPKPiNoPid, + hf_sel_candidate_lc_alice3::IsSelLcToPKPiPerfectPid, + hf_sel_candidate_lc_alice3::IsSelLcToPKPiTofPid, + hf_sel_candidate_lc_alice3::IsSelLcToPKPiTofPlusRichPid, + hf_sel_candidate_lc_alice3::IsSelLcToPiKPNoPid, + hf_sel_candidate_lc_alice3::IsSelLcToPiKPPerfectPid, + hf_sel_candidate_lc_alice3::IsSelLcToPiKPTofPid, + hf_sel_candidate_lc_alice3::IsSelLcToPiKPTofPlusRichPid); + +namespace hf_sel_candidate_lc_parametrized_pid +{ +DECLARE_SOA_COLUMN(IsSelLcToPKPiNoPid, isSelLcToPKPiNoPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPKPiPerfectPid, isSelLcToPKPiPerfectPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPKPi, isSelLcToPKPi, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPiKPNoPid, isSelLcToPiKPNoPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPiKPPerfectPid, isSelLcToPiKPPerfectPid, int); //! +DECLARE_SOA_COLUMN(IsSelLcToPiKP, isSelLcToPiKP, int); //! +} // namespace hf_sel_candidate_lc_parametrized_pid + +DECLARE_SOA_TABLE(HfSelLcParametrizedPid, "AOD", "HFSELLCP", //! + hf_sel_candidate_lc_parametrized_pid::IsSelLcToPKPiNoPid, + hf_sel_candidate_lc_parametrized_pid::IsSelLcToPKPiPerfectPid, + hf_sel_candidate_lc_parametrized_pid::IsSelLcToPKPi, + hf_sel_candidate_lc_parametrized_pid::IsSelLcToPiKPNoPid, + hf_sel_candidate_lc_parametrized_pid::IsSelLcToPiKPPerfectPid, + hf_sel_candidate_lc_parametrized_pid::IsSelLcToPiKP); + +namespace hf_sel_candidate_jpsi +{ +DECLARE_SOA_COLUMN(IsSelJpsiToEE, isSelJpsiToEE, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToMuMu, isSelJpsiToMuMu, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToEETopol, isSelJpsiToEETopol, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToEETpc, isSelJpsiToEETpc, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToEETof, isSelJpsiToEETof, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToEERich, isSelJpsiToEERich, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToEETofRich, isSelJpsiToEETofRich, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToMuMuTopol, isSelJpsiToMuMuTopol, int); //! +DECLARE_SOA_COLUMN(IsSelJpsiToMuMuMid, isSelJpsiToMuMuMid, int); //! +} // namespace hf_sel_candidate_jpsi + +DECLARE_SOA_TABLE(HfSelJpsi, "AOD", "HFSELJPSI", //! + hf_sel_candidate_jpsi::IsSelJpsiToEE, + hf_sel_candidate_jpsi::IsSelJpsiToMuMu, + hf_sel_candidate_jpsi::IsSelJpsiToEETopol, + hf_sel_candidate_jpsi::IsSelJpsiToEETpc, + hf_sel_candidate_jpsi::IsSelJpsiToEETof, + hf_sel_candidate_jpsi::IsSelJpsiToEERich, + hf_sel_candidate_jpsi::IsSelJpsiToEETofRich, + hf_sel_candidate_jpsi::IsSelJpsiToMuMuTopol, + hf_sel_candidate_jpsi::IsSelJpsiToMuMuMid); + +namespace hf_sel_candidate_x +{ +DECLARE_SOA_COLUMN(IsSelXToJpsiToEEPiPi, isSelXToJpsiToEEPiPi, int); //! +DECLARE_SOA_COLUMN(IsSelXToJpsiToMuMuPiPi, isSelXToJpsiToMuMuPiPi, int); //! +} // namespace hf_sel_candidate_x + +DECLARE_SOA_TABLE(HfSelXToJpsiPiPi, "AOD", "HFSELX", //! + hf_sel_candidate_x::IsSelXToJpsiToEEPiPi, hf_sel_candidate_x::IsSelXToJpsiToMuMuPiPi); + +namespace hf_sel_candidate_chic +{ +DECLARE_SOA_COLUMN(IsSelChicToJpsiToEEGamma, isSelChicToJpsiToEEGamma, int); //! +DECLARE_SOA_COLUMN(IsSelChicToJpsiToMuMuGamma, isSelChicToJpsiToMuMuGamma, int); //! +} // namespace hf_sel_candidate_chic + +DECLARE_SOA_TABLE(HfSelChicToJpsiGamma, "AOD", "HFSELCHIC", //! + hf_sel_candidate_chic::IsSelChicToJpsiToEEGamma, hf_sel_candidate_chic::IsSelChicToJpsiToMuMuGamma); + +namespace hf_sel_candidate_xicc +{ +DECLARE_SOA_COLUMN(IsSelXiccToPKPiPi, isSelXiccToPKPiPi, int); //! +} // namespace hf_sel_candidate_xicc + +DECLARE_SOA_TABLE(HfSelXiccToPKPiPi, "AOD", "HFSELXICC", //! + hf_sel_candidate_xicc::IsSelXiccToPKPiPi); + +} // namespace o2::aod + +#endif // PWGHF_ALICE3_DATAMODEL_CANDIDATESELECTIONTABLES_H_ diff --git a/PWGHF/ALICE3/TableProducer/CMakeLists.txt b/PWGHF/ALICE3/TableProducer/CMakeLists.txt index 5bbc40353ee..4aba14d7e30 100644 --- a/PWGHF/ALICE3/TableProducer/CMakeLists.txt +++ b/PWGHF/ALICE3/TableProducer/CMakeLists.txt @@ -13,16 +13,19 @@ o2physics_add_dpl_workflow(candidate-creator-chic SOURCES candidateCreatorChic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-x SOURCES candidateCreatorX.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xicc SOURCES candidateCreatorXicc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) @@ -30,46 +33,55 @@ o2physics_add_dpl_workflow(candidate-creator-xicc o2physics_add_dpl_workflow(candidate-selector-chic-to-jpsi-gamma SOURCES candidateSelectorChicToJpsiGamma.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-alice3-barrel SOURCES candidateSelectorD0Alice3Barrel.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-alice3-forward SOURCES candidateSelectorD0Alice3Forward.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0-parametrized-pid SOURCES candidateSelectorD0ParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-jpsi SOURCES candidateSelectorJpsi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-alice3 SOURCES candidateSelectorLcAlice3.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-parametrized-pid SOURCES candidateSelectorLcParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-x-to-jpsi-pi-pi SOURCES candidateSelectorXToJpsiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xicc-to-p-k-pi-pi SOURCES candidateSelectorXiccToPKPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -77,15 +89,18 @@ o2physics_add_dpl_workflow(candidate-selector-xicc-to-p-k-pi-pi o2physics_add_dpl_workflow(tree-creator-chic-to-jpsi-gamma SOURCES treeCreatorChicToJpsiGamma.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-x-to-jpsi-pi-pi SOURCES treeCreatorXToJpsiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xicc-to-p-k-pi-pi SOURCES treeCreatorXiccToPKPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/ALICE3/TableProducer/candidateCreatorChic.cxx b/PWGHF/ALICE3/TableProducer/candidateCreatorChic.cxx index b03c9bf6952..ee9d90c178f 100644 --- a/PWGHF/ALICE3/TableProducer/candidateCreatorChic.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateCreatorChic.cxx @@ -15,11 +15,13 @@ /// /// \author Alessandro De Falco , Cagliari University +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" +#include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" -#include "ALICE3/DataModel/ECAL.h" #include "Common/Core/trackUtilities.h" #include diff --git a/PWGHF/ALICE3/TableProducer/candidateCreatorX.cxx b/PWGHF/ALICE3/TableProducer/candidateCreatorX.cxx index f0dfb8d761c..daf0e2fe7b2 100644 --- a/PWGHF/ALICE3/TableProducer/candidateCreatorX.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateCreatorX.cxx @@ -16,6 +16,8 @@ /// \author Rik Spijkers , Utrecht University /// \author Luca Micheletti , INFN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateCreatorXicc.cxx b/PWGHF/ALICE3/TableProducer/candidateCreatorXicc.cxx index a605acdc7fc..2d30f704d67 100644 --- a/PWGHF/ALICE3/TableProducer/candidateCreatorXicc.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateCreatorXicc.cxx @@ -18,6 +18,8 @@ /// \author Mattia Faggin , University and INFN PADOVA #include "PWGHF/ALICE3/Core/DecayChannelsLegacy.h" +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorChicToJpsiGamma.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorChicToJpsiGamma.cxx index 0c4b37e21c4..2096b646f17 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorChicToJpsiGamma.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorChicToJpsiGamma.cxx @@ -15,6 +15,8 @@ /// /// \author Alessandro De Falco , Università/INFN Cagliari +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Barrel.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Barrel.cxx index 5fb93e341f5..6a49ab90852 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Barrel.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Barrel.cxx @@ -15,6 +15,8 @@ /// \author Nima Zardoshti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Forward.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Forward.cxx index ab2e05c16e8..e9f2645ce0d 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Forward.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorD0Alice3Forward.cxx @@ -15,6 +15,8 @@ /// \author Nima Zardoshti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorD0ParametrizedPid.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorD0ParametrizedPid.cxx index 474d0eda059..31216646ba3 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorD0ParametrizedPid.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorD0ParametrizedPid.cxx @@ -15,6 +15,8 @@ /// \author Nima Zardoshti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorJpsi.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorJpsi.cxx index 40f8aee3e01..9b2db0530af 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorJpsi.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorJpsi.cxx @@ -16,6 +16,8 @@ /// \author Nima Zardoshti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorLcAlice3.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorLcAlice3.cxx index 1844f315f28..904daf44b45 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorLcAlice3.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorLcAlice3.cxx @@ -16,6 +16,8 @@ /// \author Nima Zardoshti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorLcParametrizedPid.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorLcParametrizedPid.cxx index 5fbde221894..c494536e169 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorLcParametrizedPid.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorLcParametrizedPid.cxx @@ -16,6 +16,8 @@ /// \author Nima Zardoshti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorXToJpsiPiPi.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorXToJpsiPiPi.cxx index 69ef001ab57..d24b5d80bde 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorXToJpsiPiPi.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorXToJpsiPiPi.cxx @@ -16,6 +16,8 @@ /// \author Rik Spijkers , Utrecht University /// \author Luca Micheletti , INFN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/candidateSelectorXiccToPKPiPi.cxx b/PWGHF/ALICE3/TableProducer/candidateSelectorXiccToPKPiPi.cxx index 12a5bc3477f..0754aa27539 100644 --- a/PWGHF/ALICE3/TableProducer/candidateSelectorXiccToPKPiPi.cxx +++ b/PWGHF/ALICE3/TableProducer/candidateSelectorXiccToPKPiPi.cxx @@ -15,6 +15,8 @@ /// \author Gian Michele Innocenti , CERN #include "PWGHF/ALICE3/Core/DecayChannelsLegacy.h" +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/AliasTables.h" diff --git a/PWGHF/ALICE3/TableProducer/treeCreatorChicToJpsiGamma.cxx b/PWGHF/ALICE3/TableProducer/treeCreatorChicToJpsiGamma.cxx index 589d889fdcb..a7d6dca0f42 100644 --- a/PWGHF/ALICE3/TableProducer/treeCreatorChicToJpsiGamma.cxx +++ b/PWGHF/ALICE3/TableProducer/treeCreatorChicToJpsiGamma.cxx @@ -18,6 +18,8 @@ /// \author Alessandro De Falco , Università/INFN Cagliari /// \author Luca Micheletti , INFN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/treeCreatorXToJpsiPiPi.cxx b/PWGHF/ALICE3/TableProducer/treeCreatorXToJpsiPiPi.cxx index 2a9c9c49faf..a92171bb28c 100644 --- a/PWGHF/ALICE3/TableProducer/treeCreatorXToJpsiPiPi.cxx +++ b/PWGHF/ALICE3/TableProducer/treeCreatorXToJpsiPiPi.cxx @@ -17,6 +17,8 @@ /// /// \author Luca Micheletti , INFN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/TableProducer/treeCreatorXiccToPKPiPi.cxx b/PWGHF/ALICE3/TableProducer/treeCreatorXiccToPKPiPi.cxx index 305ec024513..582967fffe4 100644 --- a/PWGHF/ALICE3/TableProducer/treeCreatorXiccToPKPiPi.cxx +++ b/PWGHF/ALICE3/TableProducer/treeCreatorXiccToPKPiPi.cxx @@ -18,6 +18,8 @@ /// \author Jinjoo Seo , Inha University #include "PWGHF/ALICE3/Core/DecayChannelsLegacy.h" +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/Tasks/CMakeLists.txt b/PWGHF/ALICE3/Tasks/CMakeLists.txt index c94c68a9587..3394d304c58 100644 --- a/PWGHF/ALICE3/Tasks/CMakeLists.txt +++ b/PWGHF/ALICE3/Tasks/CMakeLists.txt @@ -11,36 +11,43 @@ o2physics_add_dpl_workflow(task-chic SOURCES taskChic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-alice3-barrel SOURCES taskD0Alice3Barrel.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-alice3-forward SOURCES taskD0Alice3Forward.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-d0-parametrized-pid SOURCES taskD0ParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-jpsi SOURCES taskJpsi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-alice3 SOURCES taskLcAlice3.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-parametrized-pid SOURCES taskLcParametrizedPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -51,10 +58,12 @@ o2physics_add_dpl_workflow(task-qa-pid-rejection o2physics_add_dpl_workflow(task-x SOURCES taskX.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xicc SOURCES taskXicc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/ALICE3/Tasks/taskChic.cxx b/PWGHF/ALICE3/Tasks/taskChic.cxx index f6ed3fbabe0..84e749c0dc8 100644 --- a/PWGHF/ALICE3/Tasks/taskChic.cxx +++ b/PWGHF/ALICE3/Tasks/taskChic.cxx @@ -15,6 +15,8 @@ /// \author Gian Michele Innocenti , CERN /// \author Alessandro De Falco , Cagliari University +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskD0Alice3Barrel.cxx b/PWGHF/ALICE3/Tasks/taskD0Alice3Barrel.cxx index a6a79fa2e23..1aa6989dec6 100644 --- a/PWGHF/ALICE3/Tasks/taskD0Alice3Barrel.cxx +++ b/PWGHF/ALICE3/Tasks/taskD0Alice3Barrel.cxx @@ -15,6 +15,8 @@ /// \author Gian Michele Innocenti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskD0Alice3Forward.cxx b/PWGHF/ALICE3/Tasks/taskD0Alice3Forward.cxx index ac528208830..8064b70756f 100644 --- a/PWGHF/ALICE3/Tasks/taskD0Alice3Forward.cxx +++ b/PWGHF/ALICE3/Tasks/taskD0Alice3Forward.cxx @@ -15,6 +15,8 @@ /// \author Gian Michele Innocenti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskD0ParametrizedPid.cxx b/PWGHF/ALICE3/Tasks/taskD0ParametrizedPid.cxx index 23d160348d8..5132bfc55e2 100644 --- a/PWGHF/ALICE3/Tasks/taskD0ParametrizedPid.cxx +++ b/PWGHF/ALICE3/Tasks/taskD0ParametrizedPid.cxx @@ -15,6 +15,8 @@ /// \author Gian Michele Innocenti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskJpsi.cxx b/PWGHF/ALICE3/Tasks/taskJpsi.cxx index 9390751b7ee..ea5c2abf616 100644 --- a/PWGHF/ALICE3/Tasks/taskJpsi.cxx +++ b/PWGHF/ALICE3/Tasks/taskJpsi.cxx @@ -16,6 +16,8 @@ /// \author Vít Kučera , CERN /// \author Biao Zhang , CCNU +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskLcAlice3.cxx b/PWGHF/ALICE3/Tasks/taskLcAlice3.cxx index 703ba304092..c39455ca5d8 100644 --- a/PWGHF/ALICE3/Tasks/taskLcAlice3.cxx +++ b/PWGHF/ALICE3/Tasks/taskLcAlice3.cxx @@ -15,6 +15,8 @@ /// \author Gian Michele Innocenti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskLcParametrizedPid.cxx b/PWGHF/ALICE3/Tasks/taskLcParametrizedPid.cxx index ff8e2aad4a4..21e97ad4355 100644 --- a/PWGHF/ALICE3/Tasks/taskLcParametrizedPid.cxx +++ b/PWGHF/ALICE3/Tasks/taskLcParametrizedPid.cxx @@ -15,6 +15,8 @@ /// \author Gian Michele Innocenti , CERN /// \author Vít Kučera , CERN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskX.cxx b/PWGHF/ALICE3/Tasks/taskX.cxx index 2391634f8ce..3cde2020102 100644 --- a/PWGHF/ALICE3/Tasks/taskX.cxx +++ b/PWGHF/ALICE3/Tasks/taskX.cxx @@ -16,6 +16,8 @@ /// \author Rik Spijkers , Utrecht University /// \author Luca Micheletti , INFN +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" diff --git a/PWGHF/ALICE3/Tasks/taskXicc.cxx b/PWGHF/ALICE3/Tasks/taskXicc.cxx index f75ca850c81..3a00bc928bd 100644 --- a/PWGHF/ALICE3/Tasks/taskXicc.cxx +++ b/PWGHF/ALICE3/Tasks/taskXicc.cxx @@ -17,6 +17,8 @@ /// \author Jinjoo Seo , Inha University #include "PWGHF/ALICE3/Core/DecayChannelsLegacy.h" +#include "PWGHF/ALICE3/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/AliasTables.h" diff --git a/PWGHF/CMakeLists.txt b/PWGHF/CMakeLists.txt index 5bb211e6f56..add3997939e 100644 --- a/PWGHF/CMakeLists.txt +++ b/PWGHF/CMakeLists.txt @@ -9,7 +9,7 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -# add_subdirectory(Core) +add_subdirectory(Core) # add_subdirectory(DataModel) add_subdirectory(TableProducer) add_subdirectory(Tasks) diff --git a/PWGHF/Core/CMakeLists.txt b/PWGHF/Core/CMakeLists.txt new file mode 100644 index 00000000000..bd5c35a3f9b --- /dev/null +++ b/PWGHF/Core/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_library(HfMlCore + SOURCES HfMlResponse.cxx + PUBLIC_LINK_LIBRARIES O2Physics::MLCore) + +# Precompiled header shared across PWGHF. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# The D2H reduced-data-model workflows share this carrier too. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(HfCandidatesPCH OBJECT hfCandidatesPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET HfCandidatesPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(HfCandidatesPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(HfCandidatesPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGHF/DataModel/CandidateReconstructionTables.h"]] # 78%, 2479 lines + [["PWGHF/DataModel/CandidateSelectionTables.h"]] # 72% + [["Common/Core/RecoDecay.h"]] # 65%, 1523 lines + ) +endif() diff --git a/PWGHF/Core/DecayChannels.h b/PWGHF/Core/DecayChannels.h index 929079b9b9f..1def8cb21e0 100644 --- a/PWGHF/Core/DecayChannels.h +++ b/PWGHF/Core/DecayChannels.h @@ -92,8 +92,10 @@ enum DecayChannelMain : HfDecayChannel { XicToPKPi = 21, // p K− π+ XicToPKK = 22, // p K− K+ XicToSPiPi = 23, // Σ+ π− π+ + // cd+ + CDeuteronToDeKPi = 24, // de K− π+ // - NChannelsMain = XicToSPiPi // last channel + NChannelsMain = CDeuteronToDeKPi // last channel }; /// @brief 3-prong candidates: resonant channels enum DecayChannelResonant : HfDecayChannel { @@ -131,8 +133,11 @@ enum DecayChannelResonant : HfDecayChannel { // Ξc+ XicToPKstar0 = 27, // p anti-K*0(892) XicToPPhi = 28, // p φ - // - NChannelsResonant = XicToPPhi // last channel + // cd+ + CDeuteronToDeKstar0 = 30, + CDeuteronToNeDeltaplusK = 31, + CDeuteronToNeL1520Pi = 32, + NChannelsResonant = CDeuteronToNeL1520Pi // last channel }; } // namespace hf_cand_3prong @@ -200,17 +205,32 @@ enum DecayChannelResonant : HfDecayChannel { /// @brief beauty candidates: beauty to J/ψ decay channels enum DecayChannelToJpsiMain : HfDecayChannel { // B0 - B0ToJpsiPiK = 1, // J/ψ π− K+ + B0ToJpsiPiKToMuMuPiK = 1, // J/ψ(->μ+μ−) π− K+ + B0ToJpsiPiKToEEPiK = 6, // J/ψ(->e+e−) π− K+ + B0ToJpsiXToMuMuX = 7, // J/ψ(->μ+μ−) X (partly/mis reconstructed) + B0ToJpsiXToEEX = 8, // J/ψ(->e+e−) X (partly/mis reconstructed) // Bs0 - BsToJpsiKK = 2, // J/ψ K+ K− + BsToJpsiKKToMuMuKK = 2, // J/ψ(->μ+μ−) K+ K− + BsToJpsiKKToEEKK = 9, // J/ψ(->e+e−) K+ K− + BsToJpsiXToMuMuX = 10, // J/ψ(->μ+μ−) X (partly/mis reconstructed) + BsToJpsiXToEEX = 11, // J/ψ(->e+e−) X (partly/mis reconstructed) // Λb0 - LbToJpsiPK = 3, // J/ψ p K− + LbToJpsiPKToMuMuPK = 3, // J/ψ(->μ+μ−) p K− + LbToJpsiPKToEEPK = 12, // J/ψ(->e+e−) p K− + LbToJpsiXToMuMuX = 13, // J/ψ(->μ+μ−) X (partly/mis reconstructed) + LbToJpsiXToEEX = 14, // J/ψ(->e+e−) X (partly/mis reconstructed) // B+ - BplusToJpsiK = 4, // J/ψ K+ + BplusToJpsiKToMuMuK = 4, // J/ψ(->μ+μ−) K+ + BplusToJpsiKToEEK = 15, // J/ψ(->e+e−) K+ + BplusToJpsiXToMuMuX = 16, // J/ψ(->μ+μ−) X (partly/mis reconstructed) + BplusToJpsiXToEEX = 17, // J/ψ(->e+e−) X (partly/mis reconstructed) // Bc+ - BcToJpsiPi = 5, // J/ψ π+ + BcToJpsiPiToMuMuPi = 5, // J/ψ(->μ+μ−) π+ + BcToJpsiPiToEEPi = 18, // J/ψ(->e+e−) π+ + BcToJpsiXToMuMuX = 19, // J/ψ(->μ+μ−) X (partly/mis reconstructed) + BcToJpsiXToEEX = 20, // J/ψ(->e+e−) X (partly/mis reconstructed) // - NChannelsToJpsiMain = BcToJpsiPi // last channel + NChannelsToJpsiMain = BcToJpsiXToEEX // last channel }; /// @brief beauty candidates: beauty to J/ψ resonant decay channels enum DecayChannelToJpsiResonant : HfDecayChannel { diff --git a/PWGHF/Core/HfHelper.h b/PWGHF/Core/HfHelper.h index 3eb3e8dd0b9..155dfda35f2 100644 --- a/PWGHF/Core/HfHelper.h +++ b/PWGHF/Core/HfHelper.h @@ -39,6 +39,8 @@ concept IsB0ToDstarPiChannel = requires(T candidate) { }; struct HfHelper { + HfHelper() = delete; // Prevent instances. + // 2-prong // D0(bar) → π± K∓ @@ -172,9 +174,18 @@ struct HfHelper { } template - static auto invMassBplusToJpsiK(const T& candidate) + static auto invMassBplusToJpsiK(const T& candidate, const bool useJpsiPdgMass) { - return candidate.m(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus}); + auto const pVecMuPos = candidate.pVectorProng0(); + auto const pVecMuNeg = candidate.pVectorProng1(); + auto const pVecKa = candidate.pVectorProng2(); + if (useJpsiPdgMass) { + return RecoDecay::m(std::array{RecoDecay::pVec(pVecMuPos, pVecMuNeg), pVecKa}, + std::array{o2::constants::physics::MassJPsi, o2::constants::physics::MassKPlus}); + } + // Do not use PDG mass for J/psi + return RecoDecay::m(std::array{pVecMuPos, pVecMuNeg, pVecKa}, + std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus}); } template @@ -262,6 +273,30 @@ struct HfHelper { return RecoDecay::m(std::array{candidate.pVectorProng1(), candidate.pVectorProng2()}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); } + template + static auto massKFirstPiPairDsToKKPi(const T& candidate) + { + return RecoDecay::m(std::array{candidate.pVectorProng0(), candidate.pVectorProng2()}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + } + + template + static auto massKFirstPiPairDsToPiKK(const T& candidate) + { + return RecoDecay::m(std::array{candidate.pVectorProng0(), candidate.pVectorProng2()}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + } + + template + static auto massKSecondPiPairDsToKKPi(const T& candidate) + { + return RecoDecay::m(std::array{candidate.pVectorProng1(), candidate.pVectorProng2()}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + } + + template + static auto massKSecondPiPairDsToPiKK(const T& candidate) + { + return RecoDecay::m(std::array{candidate.pVectorProng0(), candidate.pVectorProng1()}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + } + template static auto deltaMassPhiDsToKKPi(const T& candidate) { @@ -651,6 +686,38 @@ struct HfHelper { return candidate.m(std::array{o2::constants::physics::MassD0, o2::constants::physics::MassPiPlus, o2::constants::physics::MassPiPlus}); } + template + static auto invMassB0ToJpsiK0Star(const T& candidate, const bool useJpsiPdgMass, const bool useK0StarPdgMass, bool k0StarToKPi = true) + { + auto const pVecMuPos = candidate.pVectorProng0(); + auto const pVecMuNeg = candidate.pVectorProng1(); + auto const pVecLfTrack0 = candidate.pVectorProng2(); + auto const pVecLfTrack1 = candidate.pVectorProng3(); + if (useJpsiPdgMass && useK0StarPdgMass) { + return RecoDecay::m(std::array{RecoDecay::pVec(pVecMuPos, pVecMuNeg), RecoDecay::pVec(pVecLfTrack0, pVecLfTrack1)}, + std::array{o2::constants::physics::MassJPsi, o2::constants::physics::MassK0Star892}); + } + if (useJpsiPdgMass && !useK0StarPdgMass) { + if (k0StarToKPi) { + return RecoDecay::m(std::array{RecoDecay::pVec(pVecMuPos, pVecMuNeg), pVecLfTrack0, pVecLfTrack1}, + std::array{o2::constants::physics::MassJPsi, o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + } + return RecoDecay::m(std::array{RecoDecay::pVec(pVecMuPos, pVecMuNeg), pVecLfTrack0, pVecLfTrack1}, + std::array{o2::constants::physics::MassJPsi, o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + } + if (!useJpsiPdgMass && useK0StarPdgMass) { + return RecoDecay::m(std::array{pVecMuPos, pVecMuNeg, RecoDecay::pVec(pVecLfTrack0, pVecLfTrack1)}, + std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassK0Star892}); + } + // Do not use PDG mass for either J/psi or K*0 + if (k0StarToKPi) { + return RecoDecay::m(std::array{pVecMuPos, pVecMuNeg, pVecLfTrack0, pVecLfTrack1}, + std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + } + return RecoDecay::m(std::array{pVecMuPos, pVecMuNeg, pVecLfTrack0, pVecLfTrack1}, + std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + } + template static auto cosThetaStarB0(const T& candidate) { @@ -684,9 +751,27 @@ struct HfHelper { } template - static auto invMassBsToJpsiPhi(const T& candidate) + static auto invMassBsToJpsiPhi(const T& candidate, const bool useJpsiPdgMass, const bool usePhiPdgMass) { - return candidate.m(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); + auto const pVecMuPos = candidate.pVectorProng0(); + auto const pVecMuNeg = candidate.pVectorProng1(); + auto const pVecKa0 = candidate.pVectorProng2(); + auto const pVecKa1 = candidate.pVectorProng3(); + if (useJpsiPdgMass && usePhiPdgMass) { + return RecoDecay::m(std::array{RecoDecay::pVec(pVecMuPos, pVecMuNeg), RecoDecay::pVec(pVecKa0, pVecKa1)}, + std::array{o2::constants::physics::MassJPsi, o2::constants::physics::MassPhi}); + } + if (useJpsiPdgMass && !usePhiPdgMass) { + return RecoDecay::m(std::array{RecoDecay::pVec(pVecMuPos, pVecMuNeg), pVecKa0, pVecKa1}, + std::array{o2::constants::physics::MassJPsi, o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); + } + if (!useJpsiPdgMass && usePhiPdgMass) { + return RecoDecay::m(std::array{pVecMuPos, pVecMuNeg, RecoDecay::pVec(pVecKa0, pVecKa1)}, + std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassPhi}); + } + // Do not use PDG mass for either J/psi or phi + return RecoDecay::m(std::array{pVecMuPos, pVecMuNeg, pVecKa0, pVecKa1}, + std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); } template @@ -885,6 +970,122 @@ struct HfHelper { return true; } + // Apply topological cuts as defined in SelectorCuts.h + /// \param candB0 B0 candidate + /// \param cuts B0 candidate selection per pT bin + /// \param binsPt pT bin limits + /// \param useJpsiPdgMass Use PDG mass for J/psi when calculating B0 candidate mass + /// \param useK0StarPdgMass Use PDG mass for K*0 when calculating B0 candidate mass + /// \return true if candidate passes all selections + template + static bool selectionB0ToJpsiK0StarTopol(const T1& candB0, const T2& cuts, const T3& binsPt, const bool useJpsiPdgMass, const bool useK0StarPdgMass, const bool k0StarToKPi = true) + { + auto ptCandB0 = candB0.pt(); + auto mCandB0 = invMassB0ToJpsiK0Star(candB0, useJpsiPdgMass, useK0StarPdgMass, k0StarToKPi); + auto const pVecMu0 = candB0.pVectorProng0(); + auto const pVecMu1 = candB0.pVectorProng1(); + auto const pVecLfDau0 = candB0.pVectorProng2(); + auto const pVecLfDau1 = candB0.pVectorProng3(); + // K*0 invariant mass for the chosen daughter-mass hypothesis: K*0 → K+ π- or K*0bar → π+ K- + auto mCandK0Star = k0StarToKPi + ? RecoDecay::m(std::array{pVecLfDau0, pVecLfDau1}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}) + : RecoDecay::m(std::array{pVecLfDau0, pVecLfDau1}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); + auto ptK0Star = RecoDecay::pt(pVecLfDau0, pVecLfDau1); + auto candJpsi = candB0.template jpsi_as(); + float pseudoPropDecLen = candB0.decayLengthXY() * mCandB0 / ptCandB0; + + int binPt = o2::analysis::findBin(binsPt, ptCandB0); + if (binPt == -1) { + return false; + } + + // B0 mass cut + if (std::abs(mCandB0 - o2::constants::physics::MassB0) > cuts->get(binPt, "m")) { + return false; + } + + // K*0 pt + if (ptK0Star < cuts->get(binPt, "pT K*0")) { + return false; + } + + // J/Psi pt + if (ptJpsi < cuts->get(binPt, "pT J/Psi")) { + return false; + } + + // K*0 mass + if (std::abs(mCandK0Star - o2::constants::physics::MassK0Star892) > cuts->get(binPt, "DeltaM K*0")) { + return false; + } + + // J/Psi mass + if (std::abs(candJpsi.m() - o2::constants::physics::MassJPsi) > cuts->get(binPt, "DeltaM J/Psi")) { + return false; + } + + // d0(J/Psi)xd0(K*0) + if (candB0.impactParameterProduct() > cuts->get(binPt, "B Imp. Par. Product")) { + return false; + } + + // B0 Decay length + if (candB0.decayLength() < cuts->get(binPt, "B decLen")) { + return false; + } + + // B0 Decay length XY + if (candB0.decayLengthXY() < cuts->get(binPt, "B decLenXY")) { + return false; + } + + // B0 CPA cut + if (candB0.cpa() < cuts->get(binPt, "CPA")) { + return false; + } + + // B0 CPAXY cut + if (candB0.cpaXY() < cuts->get(binPt, "CPAXY")) { + return false; + } + + // d0 of K*0 daughters + if (std::abs(candB0.impactParameter2()) < cuts->get(binPt, "d0 K*0 daughters") || + std::abs(candB0.impactParameter3()) < cuts->get(binPt, "d0 K*0 daughters")) { + return false; + } + + // d0 of J/Psi + if (std::abs(candB0.impactParameter0()) < cuts->get(binPt, "d0 J/Psi daughters") || + std::abs(candB0.impactParameter1()) < cuts->get(binPt, "d0 J/Psi daughters")) { + return false; + } + + // B pseudoproper decay length + if (pseudoPropDecLen < cuts->get(binPt, "B pseudoprop. decLen")) { + return false; + } + + return true; + } + + /// Apply PID selection + /// \param pidTrack PID status of track (prong of B0 candidate) + /// \param acceptPIDNotApplicable switch to accept Status::NotApplicable + /// \return true if prong of B0 candidate passes all selections + static bool selectionB0ToJpsiK0StarPid(const int pidTrack, const bool acceptPIDNotApplicable) + { + if (!acceptPIDNotApplicable && pidTrack != TrackSelectorPID::Accepted) { + return false; + } + if (acceptPIDNotApplicable && pidTrack == TrackSelectorPID::Rejected) { + return false; + } + + return true; + } + /// Apply PID selection /// \param pidTrackPi PID status of trackPi (prong1 of B+ candidate) /// \param acceptPIDNotApplicable switch to accept Status::NotApplicable @@ -905,15 +1106,19 @@ struct HfHelper { /// \param candBp B+ candidate /// \param cuts B+ candidate selection per pT bin /// \param binsPt pT bin limits + /// \param useJpsiPdgMass Use PDG mass for J/psi when calculating B+ candidate mass /// \return true if candidate passes all selections - template - static bool selectionBplusToJpsiKTopol(const T1& candBp, const T2& cuts, const T3& binsPt) + template + static bool selectionBplusToJpsiKTopol(const T1& candBp, const T2& cuts, const T3& binsPt, const bool useJpsiPdgMass) { auto ptCandBp = candBp.pt(); - auto mCandBp = invMassBplusToJpsiK(candBp); - auto ptJpsi = RecoDecay::pt(candBp.pxProng0(), candBp.pyProng0()); - auto ptKa = RecoDecay::pt(candBp.pxProng1(), candBp.pyProng1()); - auto candJpsi = candBp.jpsi(); + auto mCandBp = invMassBplusToJpsiK(candBp, useJpsiPdgMass); + auto const pVecMu0 = candBp.pVectorProng0(); + auto const pVecMu1 = candBp.pVectorProng1(); + auto const pVecKa = candBp.pVectorProng2(); + auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); + auto ptKa = RecoDecay::pt(pVecKa); + auto candJpsi = candBp.template jpsi_as(); float pseudoPropDecLen = candBp.decayLengthXY() * mCandBp / ptCandBp; int binPt = o2::analysis::findBin(binsPt, ptCandBp); @@ -937,7 +1142,7 @@ struct HfHelper { } // J/Psi mass - if (std::abs(candJpsi.m() - o2::constants::physics::MassJPsi) < cuts->get(binPt, "DeltaM J/Psi")) { + if (std::abs(candJpsi.m() - o2::constants::physics::MassJPsi) > cuts->get(binPt, "DeltaM J/Psi")) { return false; } @@ -967,12 +1172,13 @@ struct HfHelper { } // d0 of K - if (std::abs(candBp.impactParameter1()) < cuts->get(binPt, "d0 K")) { + if (std::abs(candBp.impactParameter2()) < cuts->get(binPt, "d0 K")) { return false; } - // d0 of J/Psi - if (std::abs(candBp.impactParameter0()) < cuts->get(binPt, "d0 J/Psi")) { + // d0 of J/Psi daughters + if (std::abs(candBp.impactParameter0()) < cuts->get(binPt, "d0 J/Psi daughters") || + std::abs(candBp.impactParameter1()) < cuts->get(binPt, "d0 J/Psi daughters")) { return false; } @@ -1088,21 +1294,24 @@ struct HfHelper { // Apply topological cuts as defined in SelectorCuts.h /// \param candBs Bs candidate - /// \param candKa0 kaon candidate 0 (phi daughter) - /// \param candKa1 kaon candidate 1 (phi daughter) /// \param cuts Bs candidate selection per pT bin /// \param binsPt pT bin limits + /// \param useJpsiPdgMass Use PDG mass for J/psi when calculating Bs candidate mass + /// \param usePhiPdgMass Use PDG mass for phi when calculating Bs candidate mass /// \return true if candidate passes all selections - template - static bool selectionBsToJpsiPhiTopol(const T1& candBs, const T2& candKa0, const T3& candKa1, const T4& cuts, const T5& binsPt) + template + static bool selectionBsToJpsiPhiTopol(const T1& candBs, const T2& cuts, const T3& binsPt, const bool useJpsiPdgMass, const bool usePhiPdgMass) { auto ptCandBs = candBs.pt(); - auto mCandBs = invMassBsToJpsiPhi(candBs); - std::array pVecKa0 = candKa0.pVector(); - std::array pVecKa1 = candKa1.pVector(); + auto mCandBs = invMassBsToJpsiPhi(candBs, useJpsiPdgMass, usePhiPdgMass); + auto const pVecMu0 = candBs.pVectorProng0(); + auto const pVecMu1 = candBs.pVectorProng1(); + auto const pVecKa0 = candBs.pVectorProng2(); + auto const pVecKa1 = candBs.pVectorProng3(); auto mCandPhi = RecoDecay::m(std::array{pVecKa0, pVecKa1}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); - auto ptJpsi = RecoDecay::pt(candBs.pxProng0(), candBs.pyProng0()); - auto candJpsi = candBs.jpsi(); + auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); + auto ptPhi = RecoDecay::pt(pVecKa0, pVecKa1); + auto candJpsi = candBs.template jpsi_as(); float pseudoPropDecLen = candBs.decayLengthXY() * mCandBs / ptCandBs; int binPt = o2::analysis::findBin(binsPt, ptCandBs); @@ -1111,13 +1320,12 @@ struct HfHelper { } // Bs mass cut - if (std::abs(mCandBs - o2::constants::physics::MassBPlus) > cuts->get(binPt, "m")) { + if (std::abs(mCandBs - o2::constants::physics::MassBS) > cuts->get(binPt, "m")) { return false; } - // kaon pt - if (candKa0.pt() < cuts->get(binPt, "pT K") && - candKa1.pt() < cuts->get(binPt, "pT K")) { + // phi pt + if (ptPhi < cuts->get(binPt, "pT phi")) { return false; } @@ -1127,12 +1335,12 @@ struct HfHelper { } // phi mass - if (std::abs(mCandPhi - o2::constants::physics::MassPhi) < cuts->get(binPt, "DeltaM phi")) { + if (std::abs(mCandPhi - o2::constants::physics::MassPhi) > cuts->get(binPt, "DeltaM phi")) { return false; } // J/Psi mass - if (std::abs(candJpsi.m() - o2::constants::physics::MassJPsi) < cuts->get(binPt, "DeltaM J/Psi")) { + if (std::abs(candJpsi.m() - o2::constants::physics::MassJPsi) > cuts->get(binPt, "DeltaM J/Psi")) { return false; } @@ -1162,12 +1370,14 @@ struct HfHelper { } // d0 of phi - if (std::abs(candBs.impactParameter1()) < cuts->get(binPt, "d0 phi")) { + if ((std::abs(candBs.impactParameter2()) < cuts->get(binPt, "d0 phi daughters")) || + (std::abs(candBs.impactParameter3()) < cuts->get(binPt, "d0 phi daughters"))) { return false; } // d0 of J/Psi - if (std::abs(candBs.impactParameter0()) < cuts->get(binPt, "d0 J/Psi")) { + if (std::abs(candBs.impactParameter0()) < cuts->get(binPt, "d0 J/Psi daughters") || + std::abs(candBs.impactParameter1()) < cuts->get(binPt, "d0 J/Psi daughters")) { return false; } @@ -1180,15 +1390,15 @@ struct HfHelper { } /// Apply PID selection - /// \param pidTrackKa PID status of trackKa (prong1 of B+ candidate) + /// \param pidTrack PID status of track (prong of Bs candidate) /// \param acceptPIDNotApplicable switch to accept Status::NotApplicable - /// \return true if prong1 of B+ candidate passes all selections - static bool selectionBsToJpsiPhiPid(const int pidTrackKa, const bool acceptPIDNotApplicable) + /// \return true if prong1 of Bs candidate passes all selections + static bool selectionBsToJpsiPhiPid(const int pidTrack, const bool acceptPIDNotApplicable) { - if (!acceptPIDNotApplicable && pidTrackKa != TrackSelectorPID::Accepted) { + if (!acceptPIDNotApplicable && pidTrack != TrackSelectorPID::Accepted) { return false; } - if (acceptPIDNotApplicable && pidTrackKa == TrackSelectorPID::Rejected) { + if (acceptPIDNotApplicable && pidTrack == TrackSelectorPID::Rejected) { return false; } diff --git a/PWGHF/Core/HfMlResponse.cxx b/PWGHF/Core/HfMlResponse.cxx new file mode 100644 index 00000000000..d0413658618 --- /dev/null +++ b/PWGHF/Core/HfMlResponse.cxx @@ -0,0 +1,17 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HfMlResponse.cxx +/// \brief Helper file to provide compilation command for headers. +/// +/// \author Vít Kučera , Inha University + +#include "HfMlResponse.h" // IWYU pragma: keep diff --git a/PWGHF/Core/HfMlResponseB0ToDPi.h b/PWGHF/Core/HfMlResponseB0ToDPi.h index 354d295f7a6..475fc8f4127 100644 --- a/PWGHF/Core/HfMlResponseB0ToDPi.h +++ b/PWGHF/Core/HfMlResponseB0ToDPi.h @@ -30,10 +30,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_B0(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesB0ToDPi::FEATURE) \ - } +#define FILL_MAP_B0(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesB0ToDPi::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -90,6 +89,14 @@ enum class InputFeaturesB0ToDPi : uint8_t { decayLengthXYNormalised, cpa, cpaXY, + decayLengthDToPv, + decayLengthDToPvXY, + decayLengthDToB, + decayLengthDToBXY, + cpaDToPv, + cpaDToPvXY, + cpaDToB, + cpaDToBXY, maxNormalisedDeltaIP, prong0MlScoreBkg, prong0MlScorePrompt, @@ -139,6 +146,14 @@ class HfMlResponseB0ToDPi : public HfMlResponse CHECK_AND_FILL_VEC_B0(decayLengthXYNormalised); CHECK_AND_FILL_VEC_B0(cpa); CHECK_AND_FILL_VEC_B0(cpaXY); + CHECK_AND_FILL_VEC_B0(decayLengthDToPv); + CHECK_AND_FILL_VEC_B0(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_B0(decayLengthDToB); + CHECK_AND_FILL_VEC_B0(decayLengthDToBXY); + CHECK_AND_FILL_VEC_B0(cpaDToPv); + CHECK_AND_FILL_VEC_B0(cpaDToPvXY); + CHECK_AND_FILL_VEC_B0(cpaDToB); + CHECK_AND_FILL_VEC_B0(cpaDToBXY); CHECK_AND_FILL_VEC_B0(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_B0_FULL(prongBachPi, tpcNSigmaPi1, tpcNSigmaPi); @@ -202,6 +217,14 @@ class HfMlResponseB0ToDPi : public HfMlResponse CHECK_AND_FILL_VEC_B0(decayLengthXYNormalised); CHECK_AND_FILL_VEC_B0(cpa); CHECK_AND_FILL_VEC_B0(cpaXY); + CHECK_AND_FILL_VEC_B0(decayLengthDToPv); + CHECK_AND_FILL_VEC_B0(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_B0(decayLengthDToB); + CHECK_AND_FILL_VEC_B0(decayLengthDToBXY); + CHECK_AND_FILL_VEC_B0(cpaDToPv); + CHECK_AND_FILL_VEC_B0(cpaDToPvXY); + CHECK_AND_FILL_VEC_B0(cpaDToB); + CHECK_AND_FILL_VEC_B0(cpaDToBXY); CHECK_AND_FILL_VEC_B0(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_B0_FULL(prongBachPi, tpcNSigmaPiBachPi, tpcNSigmaPi); @@ -257,6 +280,14 @@ class HfMlResponseB0ToDPi : public HfMlResponse FILL_MAP_B0(decayLengthXYNormalised), FILL_MAP_B0(cpa), FILL_MAP_B0(cpaXY), + FILL_MAP_B0(decayLengthDToPv), + FILL_MAP_B0(decayLengthDToPvXY), + FILL_MAP_B0(decayLengthDToB), + FILL_MAP_B0(decayLengthDToBXY), + FILL_MAP_B0(cpaDToPv), + FILL_MAP_B0(cpaDToPvXY), + FILL_MAP_B0(cpaDToB), + FILL_MAP_B0(cpaDToBXY), FILL_MAP_B0(maxNormalisedDeltaIP), FILL_MAP_B0(prong0MlScoreBkg), FILL_MAP_B0(prong0MlScorePrompt), diff --git a/PWGHF/Core/HfMlResponseB0ToJpsiK0StarReduced.h b/PWGHF/Core/HfMlResponseB0ToJpsiK0StarReduced.h new file mode 100644 index 00000000000..a8cf0fa4752 --- /dev/null +++ b/PWGHF/Core/HfMlResponseB0ToJpsiK0StarReduced.h @@ -0,0 +1,224 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HfMlResponseB0ToJpsiK0StarReduced.h +/// \brief Class to compute the ML response for B0 → J/Psi K*0 analysis selections in the reduced format +/// \author Fabrizio Chinu , Università degli Studi and INFN Torino + +#ifndef PWGHF_CORE_HFMLRESPONSEB0TOJPSIK0STARREDUCED_H_ +#define PWGHF_CORE_HFMLRESPONSEB0TOJPSIK0STARREDUCED_H_ + +#include "PWGHF/Core/HfMlResponse.h" +#include "PWGHF/D2H/Utils/utilsRedDataFormat.h" + +#include "Tools/ML/MlResponse.h" + +#include + +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_B0(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesB0ToJpsiK0StarReduced::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER from OBJECT +#define CHECK_AND_FILL_VEC_B0_FULL(OBJECT, FEATURE, GETTER) \ + case static_cast(InputFeaturesB0ToJpsiK0StarReduced::FEATURE): { \ + inputFeatures.emplace_back(OBJECT.GETTER()); \ + break; \ + } + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the GETTER function taking OBJECT in argument +#define CHECK_AND_FILL_VEC_B0_FUNC(OBJECT, FEATURE, GETTER) \ + case static_cast(InputFeaturesB0ToJpsiK0StarReduced::FEATURE): { \ + inputFeatures.emplace_back(GETTER(OBJECT)); \ + break; \ + } + +// Specific case of CHECK_AND_FILL_VEC_B0_FULL(OBJECT, FEATURE, GETTER) +// where OBJECT is named candidate and FEATURE = GETTER +#define CHECK_AND_FILL_VEC_B0(GETTER) \ + case static_cast(InputFeaturesB0ToJpsiK0StarReduced::GETTER): { \ + inputFeatures.emplace_back(candidate.GETTER()); \ + break; \ + } + +// Specific case of CHECK_AND_FILL_VEC_B0_FULL(OBJECT, FEATURE, GETTER) +// where OBJECT is named candidate and FEATURE = GETTER and mass hypotheses are needed +#define CHECK_AND_FILL_VEC_B0_WITH_MASS_HYPO(GETTER) \ + case static_cast(InputFeaturesB0ToJpsiK0StarReduced::GETTER): { \ + std::array massesKPi{ \ + o2::constants::physics::MassMuon, \ + o2::constants::physics::MassMuon, \ + o2::constants::physics::MassKPlus, \ + o2::constants::physics::MassPiPlus}; \ + std::array massesPiK{ \ + o2::constants::physics::MassMuon, \ + o2::constants::physics::MassMuon, \ + o2::constants::physics::MassPiPlus, \ + o2::constants::physics::MassKPlus}; \ + if (caseK0StarToKPi) { \ + inputFeatures.emplace_back(candidate.GETTER(massesKPi)); \ + } else { \ + inputFeatures.emplace_back(candidate.GETTER(massesPiK)); \ + } \ + break; \ + } + +namespace o2::analysis +{ + +enum class InputFeaturesB0ToJpsiK0StarReduced : uint8_t { + ptProng0 = 0, + ptProng1, + impactParameter0, + impactParameter1, + impactParameter2, + impactParameter3, + impactParameterProduct, + impactParameterProductJpsi, + impactParameterProductK0Star, + chi2PCA, + decayLength, + decayLengthXY, + decayLengthNormalised, + decayLengthXYNormalised, + cpa, + cpaXY, + maxNormalisedDeltaIP, + ctXY, + tpcNSigmaKa0, + tofNSigmaKa0, + tpcTofNSigmaKa0, + tpcNSigmaKa1, + tofNSigmaKa1, + tpcTofNSigmaKa1 +}; + +template +class HfMlResponseB0ToJpsiK0StarReduced : public HfMlResponse +{ + public: + /// Default constructor + HfMlResponseB0ToJpsiK0StarReduced() = default; + /// Default destructor + virtual ~HfMlResponseB0ToJpsiK0StarReduced() = default; + + /// Method to get the input features vector needed for ML inference + /// \param candidate is the B0 candidate + /// \param prong1 is the candidate's prong1 + /// \param prong2 is the candidate's prong2 + /// \param caseK0StarToKPi whether we have K*0 → K+ Pi- or K*0bar → K- Pi+ + /// \return inputFeatures vector + template + std::vector getInputFeatures(T1 const& candidate, + T2 const& prong1, + T3 const& prong2, + bool const caseK0StarToKPi) + { + std::vector inputFeatures; + + for (const auto& idx : MlResponse::mCachedIndices) { + switch (idx) { + CHECK_AND_FILL_VEC_B0(ptProng0); + CHECK_AND_FILL_VEC_B0(ptProng1); + CHECK_AND_FILL_VEC_B0(impactParameter0); + CHECK_AND_FILL_VEC_B0(impactParameter1); + CHECK_AND_FILL_VEC_B0(impactParameter2); + CHECK_AND_FILL_VEC_B0(impactParameter3); + CHECK_AND_FILL_VEC_B0(impactParameterProduct); + CHECK_AND_FILL_VEC_B0(impactParameterProductJpsi); + CHECK_AND_FILL_VEC_B0(impactParameterProductK0Star); + CHECK_AND_FILL_VEC_B0(chi2PCA); + CHECK_AND_FILL_VEC_B0(decayLength); + CHECK_AND_FILL_VEC_B0(decayLengthXY); + CHECK_AND_FILL_VEC_B0(decayLengthNormalised); + CHECK_AND_FILL_VEC_B0(decayLengthXYNormalised); + CHECK_AND_FILL_VEC_B0(cpa); + CHECK_AND_FILL_VEC_B0(cpaXY); + CHECK_AND_FILL_VEC_B0(maxNormalisedDeltaIP); + CHECK_AND_FILL_VEC_B0_WITH_MASS_HYPO(ctXY); + // TPC PID variable + CHECK_AND_FILL_VEC_B0_FULL(prong1, tpcNSigmaKa0, tpcNSigmaKa); + // TOF PID variable + CHECK_AND_FILL_VEC_B0_FULL(prong1, tofNSigmaKa0, tofNSigmaKa); + // Combined PID variables + CHECK_AND_FILL_VEC_B0_FUNC(prong1, tpcTofNSigmaKa0, o2::pid_tpc_tof_utils::getTpcTofNSigmaKa1); + // TPC PID variable + CHECK_AND_FILL_VEC_B0_FULL(prong2, tpcNSigmaKa1, tpcNSigmaKa); + // TOF PID variable + CHECK_AND_FILL_VEC_B0_FULL(prong2, tofNSigmaKa1, tofNSigmaKa); + // Combined PID variables + CHECK_AND_FILL_VEC_B0_FUNC(prong2, tpcTofNSigmaKa1, o2::pid_tpc_tof_utils::getTpcTofNSigmaKa1); + } + } + + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + MlResponse::mAvailableInputFeatures = { + FILL_MAP_B0(ptProng0), + FILL_MAP_B0(ptProng1), + FILL_MAP_B0(impactParameter0), + FILL_MAP_B0(impactParameter1), + FILL_MAP_B0(impactParameter2), + FILL_MAP_B0(impactParameter3), + FILL_MAP_B0(impactParameterProduct), + FILL_MAP_B0(impactParameterProductJpsi), + FILL_MAP_B0(impactParameterProductK0Star), + FILL_MAP_B0(chi2PCA), + FILL_MAP_B0(decayLength), + FILL_MAP_B0(decayLengthXY), + FILL_MAP_B0(decayLengthNormalised), + FILL_MAP_B0(decayLengthXYNormalised), + FILL_MAP_B0(cpa), + FILL_MAP_B0(cpaXY), + FILL_MAP_B0(maxNormalisedDeltaIP), + FILL_MAP_B0(ctXY), + // TPC PID variable + FILL_MAP_B0(tpcNSigmaKa0), + // TOF PID variable + FILL_MAP_B0(tofNSigmaKa0), + // Combined PID variable + FILL_MAP_B0(tpcTofNSigmaKa0), + // TPC PID variable + FILL_MAP_B0(tpcNSigmaKa1), + // TOF PID variable + FILL_MAP_B0(tofNSigmaKa1), + // Combined PID variable + FILL_MAP_B0(tpcTofNSigmaKa1)}; + } +}; + +} // namespace o2::analysis + +#undef FILL_MAP_B0 +#undef CHECK_AND_FILL_VEC_B0_FULL +#undef CHECK_AND_FILL_VEC_B0_FUNC +#undef CHECK_AND_FILL_VEC_B0 +#undef CHECK_AND_FILL_VEC_B0_WITH_MASS_HYPO + +#endif // PWGHF_CORE_HFMLRESPONSEB0TOJPSIK0STARREDUCED_H_ diff --git a/PWGHF/Core/HfMlResponseBplusToD0Pi.h b/PWGHF/Core/HfMlResponseBplusToD0Pi.h index 9593101719e..3ca6e41e53e 100644 --- a/PWGHF/Core/HfMlResponseBplusToD0Pi.h +++ b/PWGHF/Core/HfMlResponseBplusToD0Pi.h @@ -29,10 +29,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BPLUS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBplusToD0Pi::FEATURE) \ - } +#define FILL_MAP_BPLUS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBplusToD0Pi::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -89,6 +88,14 @@ enum class InputFeaturesBplusToD0Pi : uint8_t { decayLengthXYNormalised, cpa, cpaXY, + decayLengthDToPv, + decayLengthDToPvXY, + decayLengthDToB, + decayLengthDToBXY, + cpaDToPv, + cpaDToPvXY, + cpaDToB, + cpaDToBXY, maxNormalisedDeltaIP, prong0MlProbBkg, prong0MlProbPrompt, @@ -134,6 +141,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); CHECK_AND_FILL_VEC_D0_INDEX(prong0MlProbBkg, mlProbD0, mlProbD0bar, 0); CHECK_AND_FILL_VEC_D0_INDEX(prong0MlProbPrompt, mlProbD0, mlProbD0bar, 1); @@ -159,6 +174,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_BPLUS_FULL(prong1, tpcNSigmaPi1, tpcNSigmaPi); @@ -190,6 +213,14 @@ class HfMlResponseBplusToD0Pi : public HfMlResponse FILL_MAP_BPLUS(decayLengthXYNormalised), FILL_MAP_BPLUS(cpa), FILL_MAP_BPLUS(cpaXY), + FILL_MAP_BPLUS(decayLengthDToPv), + FILL_MAP_BPLUS(decayLengthDToPvXY), + FILL_MAP_BPLUS(decayLengthDToB), + FILL_MAP_BPLUS(decayLengthDToBXY), + FILL_MAP_BPLUS(cpaDToPv), + FILL_MAP_BPLUS(cpaDToPvXY), + FILL_MAP_BPLUS(cpaDToB), + FILL_MAP_BPLUS(cpaDToBXY), FILL_MAP_BPLUS(maxNormalisedDeltaIP), FILL_MAP_BPLUS(prong0MlProbBkg), FILL_MAP_BPLUS(prong0MlProbPrompt), diff --git a/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h b/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h index 93e2ea67eb4..43052a86ccf 100644 --- a/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h +++ b/PWGHF/Core/HfMlResponseBplusToD0PiReduced.h @@ -27,10 +27,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BPLUS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBplusToD0PiReduced::FEATURE) \ - } +#define FILL_MAP_BPLUS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBplusToD0PiReduced::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -76,6 +75,14 @@ enum class InputFeaturesBplusToD0PiReduced : uint8_t { decayLengthXYNormalised, cpa, cpaXY, + decayLengthDToPv, + decayLengthDToPvXY, + decayLengthDToB, + decayLengthDToBXY, + cpaDToPv, + cpaDToPvXY, + cpaDToB, + cpaDToBXY, maxNormalisedDeltaIP, prong0MlScoreBkg, prong0MlScorePrompt, @@ -119,6 +126,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); CHECK_AND_FILL_VEC_BPLUS(prong0MlScoreBkg); CHECK_AND_FILL_VEC_BPLUS(prong0MlScorePrompt); @@ -144,6 +159,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse CHECK_AND_FILL_VEC_BPLUS(decayLengthXYNormalised); CHECK_AND_FILL_VEC_BPLUS(cpa); CHECK_AND_FILL_VEC_BPLUS(cpaXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPv); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToB); + CHECK_AND_FILL_VEC_BPLUS(decayLengthDToBXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPv); + CHECK_AND_FILL_VEC_BPLUS(cpaDToPvXY); + CHECK_AND_FILL_VEC_BPLUS(cpaDToB); + CHECK_AND_FILL_VEC_BPLUS(cpaDToBXY); CHECK_AND_FILL_VEC_BPLUS(maxNormalisedDeltaIP); // TPC PID variable CHECK_AND_FILL_VEC_BPLUS_FULL(prong1, tpcNSigmaPi1, tpcNSigmaPi); @@ -175,6 +198,14 @@ class HfMlResponseBplusToD0PiReduced : public HfMlResponse FILL_MAP_BPLUS(decayLengthXYNormalised), FILL_MAP_BPLUS(cpa), FILL_MAP_BPLUS(cpaXY), + FILL_MAP_BPLUS(decayLengthDToPv), + FILL_MAP_BPLUS(decayLengthDToPvXY), + FILL_MAP_BPLUS(decayLengthDToB), + FILL_MAP_BPLUS(decayLengthDToBXY), + FILL_MAP_BPLUS(cpaDToPv), + FILL_MAP_BPLUS(cpaDToPvXY), + FILL_MAP_BPLUS(cpaDToB), + FILL_MAP_BPLUS(cpaDToBXY), FILL_MAP_BPLUS(maxNormalisedDeltaIP), FILL_MAP_BPLUS(prong0MlScoreBkg), FILL_MAP_BPLUS(prong0MlScorePrompt), diff --git a/PWGHF/Core/HfMlResponseBplusToJpsiKReduced.h b/PWGHF/Core/HfMlResponseBplusToJpsiKReduced.h index 85830fcb55b..cab5d8e3141 100644 --- a/PWGHF/Core/HfMlResponseBplusToJpsiKReduced.h +++ b/PWGHF/Core/HfMlResponseBplusToJpsiKReduced.h @@ -23,16 +23,16 @@ #include +#include #include #include // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BPLUS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBplusToJpsiKReduced::FEATURE) \ - } +#define FILL_MAP_BPLUS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBplusToJpsiKReduced::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE diff --git a/PWGHF/Core/HfMlResponseBsToJpsiPhiReduced.h b/PWGHF/Core/HfMlResponseBsToJpsiPhiReduced.h index ee9fb5956c7..5e189aa68ba 100644 --- a/PWGHF/Core/HfMlResponseBsToJpsiPhiReduced.h +++ b/PWGHF/Core/HfMlResponseBsToJpsiPhiReduced.h @@ -23,16 +23,16 @@ #include +#include #include #include // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BS(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBsToJpsiPhiReduced::FEATURE) \ - } +#define FILL_MAP_BS(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBsToJpsiPhiReduced::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -112,6 +112,7 @@ class HfMlResponseBsToJpsiPhiReduced : public HfMlResponse /// Method to get the input features vector needed for ML inference /// \param candidate is the Bs candidate /// \param prong1 is the candidate's prong1 + /// \param prong2 is the candidate's prong2 /// \return inputFeatures vector template std::vector getInputFeatures(T1 const& candidate, diff --git a/PWGHF/Core/HfMlResponseD0ToKPi.h b/PWGHF/Core/HfMlResponseD0ToKPi.h index b0c6eaccdd3..bebc1f20247 100644 --- a/PWGHF/Core/HfMlResponseD0ToKPi.h +++ b/PWGHF/Core/HfMlResponseD0ToKPi.h @@ -225,8 +225,8 @@ class HfMlResponseD0ToKPi : public HfMlResponse CHECK_AND_FILL_VEC_D0_SIGNED(candidate, nSigTpcTofKaExpKa, tpcTofNSigmaKa1, tpcTofNSigmaKa0); CHECK_AND_FILL_VEC_D0_ML(candidate, bdtOutputBkg, mlProbD0, mlProbD0bar, 0); - CHECK_AND_FILL_VEC_D0_ML(candidate, bdtOutputNonPrompt, mlProbD0, mlProbD0bar, 1); - CHECK_AND_FILL_VEC_D0_ML(candidate, bdtOutputPrompt, mlProbD0, mlProbD0bar, 2); + CHECK_AND_FILL_VEC_D0_ML(candidate, bdtOutputPrompt, mlProbD0, mlProbD0bar, 1); + CHECK_AND_FILL_VEC_D0_ML(candidate, bdtOutputNonPrompt, mlProbD0, mlProbD0bar, 2); CHECK_AND_FILL_VEC_D0(maxNormalisedDeltaIP); CHECK_AND_FILL_VEC_D0_FULL(candidate, impactParameterProduct, impactParameterProduct); diff --git a/PWGHF/Core/HfMlResponseHfTracks.h b/PWGHF/Core/HfMlResponseHfTracks.h new file mode 100644 index 00000000000..4b6a9df281f --- /dev/null +++ b/PWGHF/Core/HfMlResponseHfTracks.h @@ -0,0 +1,225 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HfMlResponseHfTracks.h +/// \brief Class to compute the ML response for single-track selection of HF daughters +/// \author Fabrizio Chinu , Universita and INFN Torino + +#ifndef PWGHF_CORE_HFMLRESPONSEHFTRACKS_H_ +#define PWGHF_CORE_HFMLRESPONSEHFTRACKS_H_ + +#include "PWGHF/Core/HfMlResponse.h" + +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_HF_TRACK(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesTracks::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +#define CHECK_AND_FILL_VEC_HF_TRACK(FEATURE) \ + case static_cast(InputFeaturesTracks::FEATURE): \ + inputFeatures.emplace_back(track.FEATURE); \ + break; + +namespace o2::analysis +{ + +/// Output classes of the track-level HF model. +/// The ordering must match the one used at training time. +enum class HfTrackMlClass : uint8_t { + Background = 0, ///< track is not a daughter of a D+ → πKπ / Ds → φπ → KKπ decay + Pion, ///< track is the π± of a D+ → πKπ / Ds → φπ → KKπ decay + Kaon, ///< track is the K∓ of a D+ → πKπ / Ds → φπ → KKπ decay + NClasses +}; + +enum class InputFeaturesTracks : uint8_t { + // kinematics + pt = 0, + eta, + // impact parameter and its resolution + dcaXY, + dcaZ, + sigmaDcaXY, + sigmaDcaZ, + normDcaXY, + normDcaZ, + // track parameters + signed1Pt, + tgl, + sign, + isPvContributor, + // ITS quality + itsNCls, + itsNClsInnerBarrel, + itsChi2NCl, + // TPC quality + tpcNClsFound, + tpcNClsCrossedRows, + tpcCrossedRowsOverFindableCls, + tpcChi2NCl, + tpcFractionSharedCls, + // TPC PID + tpcNSigmaPi, + tpcNSigmaKa, + // event + centrality +}; + +/// Plain container with every quantity the model can be fed. +/// It is filled once per (track, collision) association by the task and then reduced to the +/// configured subset of features by getInputFeatures(). +/// Everything is a float so that the training tree and the inference input are bit-identical. +struct HfTrackMlFeatures { + // kinematics + float pt{0.f}; + float eta{0.f}; + // impact parameter and its resolution + float dcaXY{0.f}; + float dcaZ{0.f}; + float sigmaDcaXY{0.f}; + float sigmaDcaZ{0.f}; + float normDcaXY{0.f}; + float normDcaZ{0.f}; + // track parameters + float signed1Pt{0.f}; + float tgl{0.f}; + float sign{0.f}; + float isPvContributor{0.f}; + // ITS quality + float itsNCls{0.f}; + float itsNClsInnerBarrel{0.f}; + float itsChi2NCl{0.f}; + // TPC quality + float tpcNClsFound{0.f}; + float tpcNClsCrossedRows{0.f}; + float tpcCrossedRowsOverFindableCls{0.f}; + float tpcChi2NCl{0.f}; + float tpcFractionSharedCls{0.f}; + // TPC PID + float tpcNSigmaPi{0.f}; + float tpcNSigmaKa{0.f}; + // event + float centrality{0.f}; +}; + +template +class HfMlResponseHfTracks : public HfMlResponse +{ + public: + /// Default constructor + HfMlResponseHfTracks() = default; + /// Default destructor + virtual ~HfMlResponseHfTracks() = default; + + /// Index of the model to be used for a given value of the binning variable (the track pT). + /// Needed because the task applies its own per-class thresholds instead of the single-decision + /// logic of MlResponse::isSelectedMl, whose private findBin is not reachable from here. + /// Follows the same convention as the base class: mBinsLimits stores the bin edges. + /// \param value is the value of the binning variable + /// \return index of the model to be used, -1 if the value is outside the configured range + int getModelBin(float value) const + { + const auto& binsLimits = MlResponse::mBinsLimits; + const auto valueDouble = static_cast(value); + if (binsLimits.empty() || valueDouble < binsLimits.front() || valueDouble >= binsLimits.back()) { + return -1; + } + return std::distance(binsLimits.begin(), std::upper_bound(binsLimits.begin(), binsLimits.end(), valueDouble)) - 1; + } + + /// Method to get the input features vector needed for ML inference + /// \param track is the container with all the candidate track quantities + /// \return inputFeatures vector, in the order configured via cacheInputFeaturesIndices + std::vector getInputFeatures(HfTrackMlFeatures const& track) + { + std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); + + for (const auto& idx : MlResponse::mCachedIndices) { + switch (idx) { + CHECK_AND_FILL_VEC_HF_TRACK(pt); + CHECK_AND_FILL_VEC_HF_TRACK(eta); + CHECK_AND_FILL_VEC_HF_TRACK(dcaXY); + CHECK_AND_FILL_VEC_HF_TRACK(dcaZ); + CHECK_AND_FILL_VEC_HF_TRACK(sigmaDcaXY); + CHECK_AND_FILL_VEC_HF_TRACK(sigmaDcaZ); + CHECK_AND_FILL_VEC_HF_TRACK(normDcaXY); + CHECK_AND_FILL_VEC_HF_TRACK(normDcaZ); + CHECK_AND_FILL_VEC_HF_TRACK(signed1Pt); + CHECK_AND_FILL_VEC_HF_TRACK(tgl); + CHECK_AND_FILL_VEC_HF_TRACK(sign); + CHECK_AND_FILL_VEC_HF_TRACK(isPvContributor); + CHECK_AND_FILL_VEC_HF_TRACK(itsNCls); + CHECK_AND_FILL_VEC_HF_TRACK(itsNClsInnerBarrel); + CHECK_AND_FILL_VEC_HF_TRACK(itsChi2NCl); + CHECK_AND_FILL_VEC_HF_TRACK(tpcNClsFound); + CHECK_AND_FILL_VEC_HF_TRACK(tpcNClsCrossedRows); + CHECK_AND_FILL_VEC_HF_TRACK(tpcCrossedRowsOverFindableCls); + CHECK_AND_FILL_VEC_HF_TRACK(tpcChi2NCl); + CHECK_AND_FILL_VEC_HF_TRACK(tpcFractionSharedCls); + CHECK_AND_FILL_VEC_HF_TRACK(tpcNSigmaPi); + CHECK_AND_FILL_VEC_HF_TRACK(tpcNSigmaKa); + CHECK_AND_FILL_VEC_HF_TRACK(centrality); + } + } + + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + MlResponse::mAvailableInputFeatures = { + FILL_MAP_HF_TRACK(pt), + FILL_MAP_HF_TRACK(eta), + FILL_MAP_HF_TRACK(dcaXY), + FILL_MAP_HF_TRACK(dcaZ), + FILL_MAP_HF_TRACK(sigmaDcaXY), + FILL_MAP_HF_TRACK(sigmaDcaZ), + FILL_MAP_HF_TRACK(normDcaXY), + FILL_MAP_HF_TRACK(normDcaZ), + FILL_MAP_HF_TRACK(signed1Pt), + FILL_MAP_HF_TRACK(tgl), + FILL_MAP_HF_TRACK(sign), + FILL_MAP_HF_TRACK(isPvContributor), + FILL_MAP_HF_TRACK(itsNCls), + FILL_MAP_HF_TRACK(itsNClsInnerBarrel), + FILL_MAP_HF_TRACK(itsChi2NCl), + FILL_MAP_HF_TRACK(tpcNClsFound), + FILL_MAP_HF_TRACK(tpcNClsCrossedRows), + FILL_MAP_HF_TRACK(tpcCrossedRowsOverFindableCls), + FILL_MAP_HF_TRACK(tpcChi2NCl), + FILL_MAP_HF_TRACK(tpcFractionSharedCls), + FILL_MAP_HF_TRACK(tpcNSigmaPi), + FILL_MAP_HF_TRACK(tpcNSigmaKa), + FILL_MAP_HF_TRACK(centrality)}; + } +}; + +} // namespace o2::analysis + +#undef FILL_MAP_HF_TRACK +#undef CHECK_AND_FILL_VEC_HF_TRACK + +#endif // PWGHF_CORE_HFMLRESPONSEHFTRACKS_H_ diff --git a/PWGHF/Core/HfMlResponseOmegacToOmegaPi.h b/PWGHF/Core/HfMlResponseOmegacToOmegaPi.h index d3afc762d83..13e4731f491 100644 --- a/PWGHF/Core/HfMlResponseOmegacToOmegaPi.h +++ b/PWGHF/Core/HfMlResponseOmegacToOmegaPi.h @@ -74,6 +74,7 @@ enum class InputFeaturesOmegacToOmegaPi : uint8_t { dcaCascDau, cosPaCascToOmegac, decayLenXYCasc, + decayLenXYOmegac, ldlOmegac, chi2NdfTopoCascToOmegac, chi2NdfTopoCascToPv, @@ -120,6 +121,7 @@ class HfMlResponseOmegacToOmegaPi : public HfMlResponse CHECK_AND_FILL_VEC_OMEGAC0(dcaCascDau); CHECK_AND_FILL_VEC_OMEGAC0(cosPaCascToOmegac); CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYCasc); + CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYOmegac); CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, ldlOmegac, omegacldl); CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToOmegac, chi2TopoCascToOmegac); CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToPv, chi2TopoCascToPv); @@ -156,6 +158,7 @@ class HfMlResponseOmegacToOmegaPi : public HfMlResponse FILL_MAP_OMEGAC0(dcaCascDau), FILL_MAP_OMEGAC0(cosPaCascToOmegac), FILL_MAP_OMEGAC0(decayLenXYCasc), + FILL_MAP_OMEGAC0(decayLenXYOmegac), FILL_MAP_OMEGAC0(ldlOmegac), FILL_MAP_OMEGAC0(chi2NdfTopoCascToOmegac), FILL_MAP_OMEGAC0(chi2NdfTopoCascToPv), diff --git a/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h new file mode 100644 index 00000000000..2c3fd60bcae --- /dev/null +++ b/PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h @@ -0,0 +1,237 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file HfMlResponseOmegacToOmegaPiQa.h +/// \brief Class to compute the ML response for Ωc0 → Ω- π+ (and charge-conjugate) analysis selections +/// \author Yunfan Liu , China University of Geosciences +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#ifndef PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ +#define PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ + +#include "PWGHF/Core/HfMlResponse.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" + +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include + +// Fill the map of available input features +// the key is the feature's name (std::string) +// the value is the corresponding value in EnumInputFeatures +#define FILL_MAP_OMEGAC0(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesOmegacToOmegaPi::FEATURE)} + +// Check if the index of mCachedIndices (index associated to a FEATURE) +// matches the entry in EnumInputFeatures associated to this FEATURE +// if so, the inputFeatures vector is filled with the FEATURE's value +// by calling the corresponding GETTER from OBJECT +#define CHECK_AND_FILL_VEC_OMEGAC0_FULL(OBJECT, FEATURE, GETTER) \ + case static_cast(InputFeaturesOmegacToOmegaPi::FEATURE): { \ + inputFeatures.emplace_back(OBJECT.GETTER()); \ + break; \ + } + +// Specific case of CHECK_AND_FILL_VEC_OMEGAC0_FULL(OBJECT, FEATURE, GETTER) +// where OBJECT is named candidate and FEATURE = GETTER +#define CHECK_AND_FILL_VEC_OMEGAC0(GETTER) \ + case static_cast(InputFeaturesOmegacToOmegaPi::GETTER): { \ + inputFeatures.emplace_back(candidate.GETTER()); \ + break; \ + } + +namespace o2::analysis +{ +enum class InputFeaturesOmegacToOmegaPi : uint8_t { + + // Common variables + dcaCharmBaryonDau = 0, + dcaCascDau, + nSigmaTPCPiFromV0, + nSigmaTPCPiFromOmegac, + nSigmaTPCKaFromCasc, + + // DCAFitter variables + impactParBachFromCharmBaryonXY, + impactParBachFromCharmBaryonZ, + impactParCascXY, + impactParCascZ, + cosPACharmBaryon, + cosPACasc, + normDecayLenCharmBar, + + // KFParticle variables + cosPaOmegacToPv, + kfDcaXYPiFromOmegac, + chi2TopoPiFromOmegacToPv, + invMassCascade, + massCascChi2OverNdf, + kfDcaXYCascToPv, + cosPaCascToPv, + cosThetaStarPiFromOmegac, + chi2NdfTopoOmegacToPv, + ldlCasc, + cosPaCascToOmegac, + decayLenXYCasc, + decayLenXYOmegac, + ldlOmegac, + chi2NdfTopoCascToOmegac, + chi2NdfTopoCascToPv, + chi2GeoOmegac, + chi2GeoCasc, +}; + +template +class HfMlResponseOmegacToOmegaPi : public HfMlResponse +{ + public: + /// Default constructor + HfMlResponseOmegacToOmegaPi() = default; + /// Default destructor + virtual ~HfMlResponseOmegacToOmegaPi() = default; + + /// Method to get the input features vector needed for ML inference + /// \param candidate is the OMEGAC0 candidate + /// \param lamProngPi is the candidate's lamProngPi + /// \param cascProng is the bachelor track from the Omega decay + /// \param charmBaryonProng is the bachelor pion from the OmegaC decay + /// \return inputFeatures vector + template + std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& cascProng, T3 const& charmBaryonProng) + { + std::vector inputFeatures; + + for (const auto& idx : MlResponse::mCachedIndices) { + + // Variables common to DCAFitter and KFParticle + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0(dcaCharmBaryonDau); + CHECK_AND_FILL_VEC_OMEGAC0(dcaCascDau); + // TPC PID variables + CHECK_AND_FILL_VEC_OMEGAC0_FULL(lamProngPi, nSigmaTPCPiFromV0, tpcNSigmaPi); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(cascProng, nSigmaTPCKaFromCasc, tpcNSigmaKa); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(charmBaryonProng, nSigmaTPCPiFromOmegac, tpcNSigmaPi); + } + + // DCAFitter variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::DcaFitter) { + + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0(impactParBachFromCharmBaryonXY); + CHECK_AND_FILL_VEC_OMEGAC0(impactParBachFromCharmBaryonZ); + CHECK_AND_FILL_VEC_OMEGAC0(impactParCascXY); + CHECK_AND_FILL_VEC_OMEGAC0(impactParCascZ); + CHECK_AND_FILL_VEC_OMEGAC0(cosPACharmBaryon); + CHECK_AND_FILL_VEC_OMEGAC0(cosPACasc); + CHECK_AND_FILL_VEC_OMEGAC0(normDecayLenCharmBar); + } + } + + // KFParticle variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::KfParticle) { + + switch (idx) { + + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, cosPaOmegacToPv, cosPACharmBaryon); + CHECK_AND_FILL_VEC_OMEGAC0(kfDcaXYPiFromOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(chi2TopoPiFromOmegacToPv); + CHECK_AND_FILL_VEC_OMEGAC0(invMassCascade); + CHECK_AND_FILL_VEC_OMEGAC0(massCascChi2OverNdf); + CHECK_AND_FILL_VEC_OMEGAC0(kfDcaXYCascToPv); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, cosPaCascToPv, cosPACasc); + CHECK_AND_FILL_VEC_OMEGAC0(cosThetaStarPiFromOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoOmegacToPv, chi2TopoOmegacToPv); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, ldlCasc, cascldl); + CHECK_AND_FILL_VEC_OMEGAC0(cosPaCascToOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYCasc); + CHECK_AND_FILL_VEC_OMEGAC0(decayLenXYOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, ldlOmegac, omegacldl); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToOmegac, chi2TopoCascToOmegac); + CHECK_AND_FILL_VEC_OMEGAC0_FULL(candidate, chi2NdfTopoCascToPv, chi2TopoCascToPv); + CHECK_AND_FILL_VEC_OMEGAC0(chi2GeoOmegac); + CHECK_AND_FILL_VEC_OMEGAC0(chi2GeoCasc); + } + } + } + + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() + { + // Common variables + MlResponse::mAvailableInputFeatures = { + + FILL_MAP_OMEGAC0(dcaCharmBaryonDau), + FILL_MAP_OMEGAC0(dcaCascDau), + FILL_MAP_OMEGAC0(nSigmaTPCPiFromV0), + FILL_MAP_OMEGAC0(nSigmaTPCPiFromOmegac), + FILL_MAP_OMEGAC0(nSigmaTPCKaFromCasc)}; + + // DCAFitter variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::DcaFitter) { + + std::map mapDcaFeatures{ + + FILL_MAP_OMEGAC0(impactParBachFromCharmBaryonXY), + FILL_MAP_OMEGAC0(impactParBachFromCharmBaryonZ), + FILL_MAP_OMEGAC0(impactParCascXY), + FILL_MAP_OMEGAC0(impactParCascZ), + FILL_MAP_OMEGAC0(cosPACharmBaryon), + FILL_MAP_OMEGAC0(cosPACasc), + FILL_MAP_OMEGAC0(normDecayLenCharmBar)}; + + MlResponse::mAvailableInputFeatures.insert(mapDcaFeatures.begin(), mapDcaFeatures.end()); + } + + // KFParticle variables + if constexpr (reconstructionType == aod::hf_cand_casc_lf::ConstructMethod::KfParticle) { + + std::map mapKfFeatures{ + + FILL_MAP_OMEGAC0(cosPaOmegacToPv), + FILL_MAP_OMEGAC0(kfDcaXYPiFromOmegac), + FILL_MAP_OMEGAC0(chi2TopoPiFromOmegacToPv), + FILL_MAP_OMEGAC0(invMassCascade), + FILL_MAP_OMEGAC0(massCascChi2OverNdf), + FILL_MAP_OMEGAC0(kfDcaXYCascToPv), + FILL_MAP_OMEGAC0(cosPaCascToPv), + FILL_MAP_OMEGAC0(cosThetaStarPiFromOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoOmegacToPv), + FILL_MAP_OMEGAC0(ldlCasc), + FILL_MAP_OMEGAC0(cosPaCascToOmegac), + FILL_MAP_OMEGAC0(decayLenXYCasc), + FILL_MAP_OMEGAC0(decayLenXYOmegac), + FILL_MAP_OMEGAC0(ldlOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoCascToOmegac), + FILL_MAP_OMEGAC0(chi2NdfTopoCascToPv), + FILL_MAP_OMEGAC0(chi2GeoOmegac), + FILL_MAP_OMEGAC0(chi2GeoCasc)}; + + MlResponse::mAvailableInputFeatures.insert(mapKfFeatures.begin(), mapKfFeatures.end()); + } + } +}; + +} // namespace o2::analysis + +#undef FILL_MAP_OMEGAC0 +#undef CHECK_AND_FILL_VEC_OMEGAC0_FULL +#undef CHECK_AND_FILL_VEC_OMEGAC0 +#endif // PWGHF_CORE_HFMLRESPONSEOMEGACTOOMEGAPIQA_H_ diff --git a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h index 3fbb6cf4052..2a16648320b 100644 --- a/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h +++ b/PWGHF/Core/HfMlResponseXic0ToXiPiKf.h @@ -18,26 +18,20 @@ #include "PWGHF/Core/HfMlResponse.h" +#include "Common/Core/RecoDecay.h" #include "Tools/ML/MlResponse.h" #include #include -// Fill the map of available input features -// the key is the feature's name (std::string) -// the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_XIC0TOXIPIKF(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE) \ - } - +// Feature names are registered explicitly with the training-tree branch names. // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE // if so, the inputFeatures vector is filled with the FEATURE's value // by calling the corresponding GETTER from OBJECT #define CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(OBJECT, FEATURE, GETTER) \ case static_cast(InputFeaturesXic0ToXiPiKf::FEATURE): { \ - inputFeatures.emplace_back(OBJECT.GETTER()); \ + inputFeatures.emplace_back((OBJECT).GETTER()); \ break; \ } @@ -48,10 +42,20 @@ break; \ } +#define CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE(FEATURE, VALUE) \ + case static_cast(InputFeaturesXic0ToXiPiKf::FEATURE): { \ + inputFeatures.emplace_back(VALUE); \ + break; \ + } + +#define FILL_MAP_XIC0TOXIPIKF_AS(NAME, FEATURE) \ + {NAME, static_cast(InputFeaturesXic0ToXiPiKf::FEATURE)} + namespace o2::analysis { enum class InputFeaturesXic0ToXiPiKf : uint8_t { + // Internal feature identifiers; configuration names are registered below. tpcNSigmaPiFromLambda, tpcNSigmaPiFromCasc, tpcNSigmaPiFromCharmBaryon, @@ -65,7 +69,27 @@ enum class InputFeaturesXic0ToXiPiKf : uint8_t { chi2TopoCascToPv, chi2TopoCascToXic, cosPaCascToXic, - decayLenXYCasc + decayLenXYCasc, + // Remaining features used by the local PbPb Transformer. + decayLenXYXic, + chi2TopoXicToPv, + chi2TopoPiFromXicToPv, + kfptPiFromXic, + cosPaCascToPv, + dcaXYToPvCascDau, + decayLenXYLambda, + v0Chi2OverNdf, + chi2TopoV0ToPv, + chi2TopoV0ToCasc, + cosPaV0ToPv, + cosPaV0ToCasc, + dcaV0Dau, + dcaXYToPvV0Dau0, + dcaXYToPvV0Dau1, + tpcNSigmaPrFromLambda, + chi2GeoXic, + cTauXic, + chi2GeoCasc }; template @@ -81,34 +105,53 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse /// \param candidate is the Xic0 candidate /// \return inputFeatures vector template - // std::vector getInputFeatures(T1 const& candidate) - std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& cascProngPi, T3 const& charmBaryonProngPi) + std::vector getInputFeatures(T1 const& candidate, T2 const& lamProngPi, T2 const& lamProngPr, + T2 const& cascProngPi, T3 const& charmBaryonProngPi) { std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); for (const auto& idx : MlResponse::mCachedIndices) { switch (idx) { - // PID variables - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPi, tpcNSigmaPiFromLambda, tpcNSigmaPi); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(cascProngPi, tpcNSigmaPiFromCasc, tpcNSigmaPi); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(charmBaryonProngPi, tpcNSigmaPiFromCharmBaryon, tpcNSigmaPi); - // DCA - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCascDau); + // Xic candidate topology. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYXic); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(xicChi2OverNdf); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoXicToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCharmBaryonDau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2GeoXic); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cTauXic); + // Bachelor pion from Xic. CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYPiFromXic); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYCascToPv); - // Chi2Geo + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoPiFromXicToPv); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE(kfptPiFromXic, RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon())); + // Cascade. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYCasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascChi2OverNdf); - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(xicChi2OverNdf); - // ldl - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascldl); - // Chi2Topo + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2GeoCasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToPv); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoCascToXic); - // CosPa + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(candidate, cosPaCascToPv, cosPACasc); CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cosPaCascToXic); - // Decay length - CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaCascDau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(kfDcaXYCascToPv); + // Pion from cascade. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(cascProngPi, tpcNSigmaPiFromCasc, tpcNSigmaPi); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvCascDau); + // Lambda and its charged daughters. The selector passes sign-corrected prongs. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(decayLenXYLambda); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(v0Chi2OverNdf); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoV0ToPv); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(chi2TopoV0ToCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(candidate, cosPaV0ToPv, cosPAV0); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cosPaV0ToCasc); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaV0Dau); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvV0Dau0); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(dcaXYToPvV0Dau1); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPr, tpcNSigmaPrFromLambda, tpcNSigmaPr); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(lamProngPi, tpcNSigmaPiFromLambda, tpcNSigmaPi); + // Additional supported features. + CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL(charmBaryonProngPi, tpcNSigmaPiFromCharmBaryon, tpcNSigmaPi); + CHECK_AND_FILL_VEC_XIC0TOXIPIKF(cascldl); } } @@ -120,28 +163,48 @@ class HfMlResponseXic0ToXiPiKf : public HfMlResponse void setAvailableInputFeatures() { MlResponse::mAvailableInputFeatures = { - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromLambda), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCasc), - FILL_MAP_XIC0TOXIPIKF(tpcNSigmaPiFromCharmBaryon), - FILL_MAP_XIC0TOXIPIKF(dcaCascDau), - FILL_MAP_XIC0TOXIPIKF(dcaCharmBaryonDau), - FILL_MAP_XIC0TOXIPIKF(kfDcaXYPiFromXic), - FILL_MAP_XIC0TOXIPIKF(kfDcaXYCascToPv), - FILL_MAP_XIC0TOXIPIKF(cascChi2OverNdf), - FILL_MAP_XIC0TOXIPIKF(xicChi2OverNdf), - FILL_MAP_XIC0TOXIPIKF(cascldl), - FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToPv), - FILL_MAP_XIC0TOXIPIKF(chi2TopoCascToXic), - FILL_MAP_XIC0TOXIPIKF(cosPaCascToXic), - FILL_MAP_XIC0TOXIPIKF(decayLenXYCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYXic", decayLenXYXic), + FILL_MAP_XIC0TOXIPIKF_AS("fXicChi2OverNdf", xicChi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoXicToPv", chi2TopoXicToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaCharmBaryonDau", dcaCharmBaryonDau), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2GeoXic", chi2GeoXic), + FILL_MAP_XIC0TOXIPIKF_AS("fCtXic", cTauXic), + FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYPiFromXic", kfDcaXYPiFromXic), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoPiFromXicToPv", chi2TopoPiFromXicToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fKfptPiFromXic", kfptPiFromXic), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYCasc", decayLenXYCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fCascChi2OverNdf", cascChi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2GeoCasc", chi2GeoCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToPv", chi2TopoCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoCascToXic", chi2TopoCascToXic), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToPv", cosPaCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaCascToXic", cosPaCascToXic), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaCascDau", dcaCascDau), + FILL_MAP_XIC0TOXIPIKF_AS("fKfDcaXYCascToPv", kfDcaXYCascToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromCasc", tpcNSigmaPiFromCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvCascDau", dcaXYToPvCascDau), + FILL_MAP_XIC0TOXIPIKF_AS("fDecayLenXYLambda", decayLenXYLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fV0Chi2OverNdf", v0Chi2OverNdf), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoV0ToPv", chi2TopoV0ToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fChi2TopoV0ToCasc", chi2TopoV0ToCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaV0ToPv", cosPaV0ToPv), + FILL_MAP_XIC0TOXIPIKF_AS("fCosPaV0ToCasc", cosPaV0ToCasc), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaV0Dau", dcaV0Dau), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau0", dcaXYToPvV0Dau0), + FILL_MAP_XIC0TOXIPIKF_AS("fDcaXYToPvV0Dau1", dcaXYToPvV0Dau1), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPrFromLambda", tpcNSigmaPrFromLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromLambda", tpcNSigmaPiFromLambda), + FILL_MAP_XIC0TOXIPIKF_AS("fTpcNSigmaPiFromCharmBaryon", tpcNSigmaPiFromCharmBaryon), + FILL_MAP_XIC0TOXIPIKF_AS("fCascldl", cascldl), }; } }; } // namespace o2::analysis -#undef FILL_MAP_XIC0TOXIPIKF +#undef FILL_MAP_XIC0TOXIPIKF_AS #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF_FULL #undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF +#undef CHECK_AND_FILL_VEC_XIC0TOXIPIKF_VALUE #endif // PWGHF_CORE_HFMLRESPONSEXIC0TOXIPIKF_H_ diff --git a/PWGHF/Core/SelectorCuts.h b/PWGHF/Core/SelectorCuts.h index 17a223bcd20..37dd3c656c7 100644 --- a/PWGHF/Core/SelectorCuts.h +++ b/PWGHF/Core/SelectorCuts.h @@ -232,7 +232,7 @@ static const std::vector labelsCutVar = {"massMin", "massMax", "cos namespace hf_cuts_presel_3prong { static constexpr int NBinsPt = 2; -static constexpr int NCutVars = 4; +static constexpr int NCutVars = 5; // default values for the pT bin edges (can be used to configure histogram axis) // common for any 3-prong candidate // offset by 1 from the bin numbers in cuts array @@ -243,20 +243,21 @@ constexpr double BinsPt[NBinsPt + 1] = { const auto vecBinsPt = std::vector{BinsPt, BinsPt + NBinsPt + 1}; // default values for the cuts -constexpr double Cuts[NBinsPt][NCutVars] = {{1.75, 2.05, 0.7, 0.02}, /* 1 < pt < 5 */ - {1.75, 2.05, 0.5, 0.02}}; /* 5 < pt < 1000 */ +constexpr double Cuts[NBinsPt][NCutVars] = {{1.75, 2.05, 0.7, 0.02, 0.3}, /* 1 < pt < 5 */ + {1.75, 2.05, 0.5, 0.02, 0.3}}; /* 5 < pt < 1000 */ // row labels static const std::vector labelsPt{}; // column labels -static const std::vector labelsCutVar = {"massMin", "massMax", "cosp", "decL"}; +static const std::vector labelsCutVar = {"massMin", "massMax", "cosp", "decL", "ptProngMin"}; + } // namespace hf_cuts_presel_3prong namespace hf_cuts_presel_ds { static constexpr int NBinsPt = 2; -static constexpr int NCutVars = 5; +static constexpr int NCutVars = 6; // default values for the pT bin edges (can be used to configure histogram axis) // offset by 1 from the bin numbers in cuts array constexpr double BinsPt[NBinsPt + 1] = { @@ -266,14 +267,14 @@ constexpr double BinsPt[NBinsPt + 1] = { const auto vecBinsPt = std::vector{BinsPt, BinsPt + NBinsPt + 1}; // default values for the cuts -constexpr double Cuts[NBinsPt][NCutVars] = {{1.70, 2.15, 0.7, 0.02, 0.02}, /* 1 < pt < 5 */ - {1.70, 2.15, 0.5, 0.02, 0.02}}; /* 5 < pt < 1000 */ +constexpr double Cuts[NBinsPt][NCutVars] = {{1.70, 2.15, 0.7, 0.02, 0.3, 0.02}, /* 1 < pt < 5 */ + {1.70, 2.15, 0.5, 0.02, 0.3, 0.02}}; /* 5 < pt < 1000 */ // row labels static const std::vector labelsPt{}; // column labels -static const std::vector labelsCutVar = {"massMin", "massMax", "cosp", "decL", "deltaMassKK"}; +static const std::vector labelsCutVar = {"massMin", "massMax", "cosp", "decL", "ptProngMin", "deltaMassKK"}; } // namespace hf_cuts_presel_ds namespace hf_cuts_presel_dstar @@ -553,32 +554,42 @@ static const std::vector labelsCutKfVar = {"kfChi2PrimPr", "kfChi2P namespace hf_cuts_lc_to_k0s_p { -static constexpr int NBinsPt = 8; +static constexpr int NBinsPt = 13; static constexpr int NCutVars = 9; // default values for the pT bin edges (can be used to configure histogram axis) // offset by 1 from the bin numbers in cuts array constexpr double BinsPt[NBinsPt + 1] = { - 1., - 2., - 3., - 4., - 5., - 6., - 8., - 12., - 24.}; + 0.0, + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 6.0, + 7.0, + 8.0, + 10.0, + 12.0, + 16.0, + 24.0, + 36.0}; const auto vecBinsPt = std::vector{BinsPt, BinsPt + NBinsPt + 1}; // default values for the cuts // mLc(GeV) mK0s(GeV) mLambdas(GeV) mGammas(GeV) ptp ptK0sdau ptK0s d0p d0K0 -constexpr double Cuts[NBinsPt][NCutVars] = {{0.4, 0.008, 0.005, 0.1, 0.5, 0.3, 0.6, 0.05, 999999.}, // 1 < pt < 2 +constexpr double Cuts[NBinsPt][NCutVars] = {{0.4, 0.008, 0.005, 0.1, 0.5, 0.3, 0.6, 0.05, 999999.}, // 0 < pt < 1 + {0.4, 0.008, 0.005, 0.1, 0.5, 0.3, 0.6, 0.05, 999999.}, // 1 < pt < 2 {0.4, 0.008, 0.005, 0.1, 0.5, 0.4, 1.3, 0.05, 999999.}, // 2 < pt < 3 {0.4, 0.009, 0.005, 0.1, 0.6, 0.4, 1.3, 0.05, 999999.}, // 3 < pt < 4 - {0.4, 0.011, 0.005, 0.1, 0.6, 0.4, 1.4, 0.05, 999999.}, // 4 < pt < 5 - {0.4, 0.013, 0.005, 0.1, 0.6, 0.4, 1.4, 0.06, 999999.}, // 5 < pt < 6 - {0.4, 0.013, 0.005, 0.1, 0.9, 0.4, 1.6, 0.09, 999999.}, // 6 < pt < 8 - {0.4, 0.016, 0.005, 0.1, 0.9, 0.4, 1.7, 0.10, 999999.}, // 8 < pt < 12 - {0.4, 0.019, 0.005, 0.1, 1.0, 0.4, 1.9, 0.20, 999999.}}; // 12 < pt < 24 + {0.4, 0.009, 0.005, 0.1, 0.6, 0.4, 1.3, 0.05, 999999.}, // 4 < pt < 5 + {0.4, 0.011, 0.005, 0.1, 0.6, 0.4, 1.4, 0.05, 999999.}, // 5 < pt < 6 + {0.4, 0.013, 0.005, 0.1, 0.6, 0.4, 1.4, 0.06, 999999.}, // 6 < pt < 7 + {0.4, 0.013, 0.005, 0.1, 0.6, 0.4, 1.4, 0.06, 999999.}, // 7 < pt < 8 + {0.4, 0.013, 0.005, 0.1, 0.6, 0.4, 1.4, 0.06, 999999.}, // 8 < pt < 10 + {0.4, 0.013, 0.005, 0.1, 0.9, 0.4, 1.6, 0.09, 999999.}, // 10 < pt < 12 + {0.4, 0.016, 0.005, 0.1, 0.9, 0.4, 1.7, 0.10, 999999.}, // 12 < pt < 16 + {0.4, 0.016, 0.005, 0.1, 0.9, 0.4, 1.7, 0.10, 999999.}, // 16 < pt < 24 + {0.4, 0.019, 0.005, 0.1, 1.0, 0.4, 1.9, 0.20, 999999.}}; // 24 < pt < 36 // row labels static const std::vector labelsPt = { @@ -589,7 +600,12 @@ static const std::vector labelsPt = { "pT bin 4", "pT bin 5", "pT bin 6", - "pT bin 7"}; + "pT bin 7", + "pT bin 8", + "pT bin 9", + "pT bin 10", + "pT bin 11", + "pT bin 12"}; // column labels static const std::vector labelsCutVar = {"mLc", "mK0s", "mLambda", "mGamma", "ptBach", "ptV0Dau", "ptV0", "d0Bach", "d0V0"}; @@ -1130,6 +1146,61 @@ static const std::vector labelsPt = { // column labels static const std::vector labelsCutVar = {"m", "CPA", "Chi2PCA", "d0 D", "d0 Pi", "pT D", "pT Pi", "B0 decLen", "B0 decLenXY", "Imp. Par. Product", "DeltaMD", "Cos ThetaStar"}; } // namespace hf_cuts_b0_to_d_pi +namespace hf_cuts_b0_to_jpsi_k0star +{ +static constexpr int NBinsPt = 12; +static constexpr int NCutVars = 13; +// default values for the pT bin edges (can be used to configure histogram axis) +// offset by 1 from the bin numbers in cuts array +constexpr double BinsPt[NBinsPt + 1] = { + 0, + 0.5, + 1.0, + 2.0, + 3.0, + 4.0, + 5.0, + 7.0, + 10.0, + 13.0, + 16.0, + 20.0, + 24.0}; + +const auto vecBinsPt = std::vector{BinsPt, BinsPt + NBinsPt + 1}; + +// default values for the cuts +// DeltaM CPA CPAXY d0JpsiDau d0K*0Dau pTJpsi pTK*0 BDecayLength BDecayLengthXY BIPProd DeltaMJpsi DeltaMK*0 PseudopropDL +constexpr double Cuts[NBinsPt][NCutVars] = {{1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 0 < pt < 0.5 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 0.5 < pt < 1 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 1 < pt < 2 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 2 < pt < 3 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 3 < pt < 4 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 4 < pt < 5 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 5 < pt < 7 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 7 < pt < 10 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 10 < pt < 13 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 13 < pt < 16 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}, /* 16 < pt < 20 */ + {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.15, 0.}}; /* 20 < pt < 24 */ +// row labels +static const std::vector labelsPt = { + "pT bin 0", + "pT bin 1", + "pT bin 2", + "pT bin 3", + "pT bin 4", + "pT bin 5", + "pT bin 6", + "pT bin 7", + "pT bin 8", + "pT bin 9", + "pT bin 10", + "pT bin 11"}; + +// column labels +static const std::vector labelsCutVar = {"m", "CPA", "CPAXY", "d0 J/Psi daughters", "d0 K*0 daughters", "pT J/Psi", "pT K*0", "B decLen", "B decLenXY", "B Imp. Par. Product", "DeltaM J/Psi", "DeltaM K*0", "B pseudoprop. decLen"}; +} // namespace hf_cuts_b0_to_jpsi_k0star namespace hf_cuts_bs_to_ds_pi { @@ -1206,7 +1277,7 @@ constexpr double BinsPt[NBinsPt + 1] = { const auto vecBinsPt = std::vector{BinsPt, BinsPt + NBinsPt + 1}; // default values for the cuts -// DeltaM CPA d0Jpsi d0K pTJpsi pTK BDecayLength BDecayLengthXY BIPProd DeltaMJpsi JpsiIPProd +// DeltaM CPA CPAXY d0Jpsi d0K pTJpsi pTK BDecayLength BDecayLengthXY BIPProd DeltaMJpsi DeltaMPhi PseudopropDL constexpr double Cuts[NBinsPt][NCutVars] = {{1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.02, 0.}, /* 0 < pt < 0.5 */ {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.02, 0.}, /* 0.5 < pt < 1 */ {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.02, 0.}, /* 1 < pt < 2 */ @@ -1235,7 +1306,7 @@ static const std::vector labelsPt = { "pT bin 11"}; // column labels -static const std::vector labelsCutVar = {"m", "CPA", "CPAXY", "d0 J/Psi", "d0 phi", "pT J/Psi", "pT K", "B decLen", "B decLenXY", "B Imp. Par. Product", "DeltaM J/Psi", "DeltaM phi", "B pseudoprop. decLen"}; +static const std::vector labelsCutVar = {"m", "CPA", "CPAXY", "d0 J/Psi daughters", "d0 phi daughters", "pT J/Psi", "pT phi", "B decLen", "B decLenXY", "B Imp. Par. Product", "DeltaM J/Psi", "DeltaM phi", "B pseudoprop. decLen"}; } // namespace hf_cuts_bs_to_jpsi_phi namespace hf_cuts_bplus_to_d0_pi @@ -1318,7 +1389,7 @@ constexpr double BinsPt[NBinsPt + 1] = { const auto vecBinsPt = std::vector{BinsPt, BinsPt + NBinsPt + 1}; // default values for the cuts -// DeltaM CPA d0Jpsi d0K pTJpsi pTK BDecayLength BDecayLengthXY BIPProd DeltaMJpsi JpsiIPProd +// DeltaM CPA d0Jpsi d0K pTJpsi pTK BDecayLength BDecayLengthXY BIPProd DeltaMJpsi PseudopropDL constexpr double Cuts[NBinsPt][NCutVars] = {{1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.}, /* 0 < pt < 0.5 */ {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.}, /* 0.5 < pt < 1 */ {1., 0.8, 0.8, 0.01, 0.01, 1.0, 0.15, 0.05, 0.05, 0., 0.1, 0.}, /* 1 < pt < 2 */ @@ -1347,7 +1418,7 @@ static const std::vector labelsPt = { "pT bin 11"}; // column labels -static const std::vector labelsCutVar = {"m", "CPA", "CPAXY", "d0 J/Psi", "d0 K", "pT J/Psi", "pT K", "B decLen", "B decLenXY", "B Imp. Par. Product", "DeltaM J/Psi", "B pseudoprop. decLen"}; +static const std::vector labelsCutVar = {"m", "CPA", "CPAXY", "d0 J/Psi daughters", "d0 K", "pT J/Psi", "pT K", "B decLen", "B decLenXY", "B Imp. Par. Product", "DeltaM J/Psi", "B pseudoprop. decLen"}; } // namespace hf_cuts_bplus_to_jpsi_k namespace hf_cuts_lb_to_lc_pi diff --git a/PWGHF/Core/hfCandidatesPCH.cxx b/PWGHF/Core/hfCandidatesPCH.cxx new file mode 100644 index 00000000000..95f76a2131e --- /dev/null +++ b/PWGHF/Core/hfCandidatesPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the HfCandidatesPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGHF/D2H/CMakeLists.txt b/PWGHF/D2H/CMakeLists.txt index a842726759f..a40839d4f22 100644 --- a/PWGHF/D2H/CMakeLists.txt +++ b/PWGHF/D2H/CMakeLists.txt @@ -9,5 +9,6 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +add_subdirectory(Core) add_subdirectory(TableProducer) add_subdirectory(Tasks) diff --git a/PWGHF/D2H/Core/CMakeLists.txt b/PWGHF/D2H/Core/CMakeLists.txt new file mode 100644 index 00000000000..813de3e001e --- /dev/null +++ b/PWGHF/D2H/Core/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_library(HfDataCreationCharmReso + SOURCES DataCreationCharmReso.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore KFParticle::KFParticle) diff --git a/PWGHF/D2H/Core/DataCreationCharmReso.cxx b/PWGHF/D2H/Core/DataCreationCharmReso.cxx new file mode 100644 index 00000000000..b6e4345ebc3 --- /dev/null +++ b/PWGHF/D2H/Core/DataCreationCharmReso.cxx @@ -0,0 +1,17 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file DataCreationCharmReso.cxx +/// \brief Helper file to provide compilation command for headers. +/// +/// \author Vít Kučera , Inha University + +#include "DataCreationCharmReso.h" // IWYU pragma: keep diff --git a/PWGHF/D2H/Core/DataCreationCharmReso.h b/PWGHF/D2H/Core/DataCreationCharmReso.h index 01092564ff8..09904380a89 100644 --- a/PWGHF/D2H/Core/DataCreationCharmReso.h +++ b/PWGHF/D2H/Core/DataCreationCharmReso.h @@ -19,7 +19,7 @@ #define PWGHF_D2H_CORE_DATACREATIONCHARMRESO_H_ #ifndef HomogeneousField -#define HomogeneousField // needed for KFParticle::SetField(magneticField); +#define HomogeneousField // o2-linter: disable=name/macro (needed for KFParticle::SetField(magneticField);) #endif #include "PWGHF/Core/CentralityEstimation.h" @@ -27,7 +27,6 @@ #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Utils/utilsEvSelHf.h" #include "PWGHF/Utils/utilsMcMatching.h" - // #include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" #include "PWGEM/PhotonMeson/Utils/TrackSelection.h" @@ -63,11 +62,10 @@ #include #include #include +#include #include -namespace o2::analysis -{ -namespace hf_charm_reso +namespace o2::analysis::hf_charm_reso { // event types @@ -226,7 +224,7 @@ template void addHistograms(o2::framework::HistogramRegistry& registry) { constexpr uint8_t NumBinsEvents = EventType::NEventType; - std::string labels[NumBinsEvents]; + std::array labels; labels[EventType::Processed] = "processed"; labels[EventType::NoDV0Selected] = "without DV0 pairs"; labels[EventType::DV0Selected] = "with DV0 pairs"; @@ -542,7 +540,7 @@ bool buildAndSelectGamma(const Coll& collision, const std::array& dDaugh return false; } - std::array dcaInfo; + std::array dcaInfo{}; auto trackParPos = getTrackParCov(trackPos); if (o2::pwgem::photonmeson::isTPConlyTrack(trackPos) && !vDriftMgr->moveTPCTrack(collision, trackPos, trackParPos)) { LOGP(error, "failed correction for positive tpc track"); @@ -568,7 +566,7 @@ bool buildAndSelectGamma(const Coll& collision, const std::array& dDaugh return false; } - float gammaVtx[3] = {0.f, 0.f, 0.f}; + std::array gammaVtx = {0.f, 0.f, 0.f}; Vtx_recalculationParCov(o2::base::Propagator::Instance(), trackParPropPos, trackParPropNeg, gammaVtx, matCorr); float radiusXy = std::hypot(gammaVtx[0], gammaVtx[1]); const float maxX{83.1f}; // max X for track IU @@ -585,17 +583,17 @@ bool buildAndSelectGamma(const Coll& collision, const std::array& dDaugh KFPTrack kfpTrackNeg = createKFPTrackFromTrackParCov(trackParPropNeg, trackNeg.sign(), trackNeg.tpcNClsFound(), trackNeg.tpcChi2NCl()); KFParticle kfPartPos(kfpTrackPos, kPositron); KFParticle kfPartNeg(kfpTrackNeg, kElectron); - const KFParticle* gammaDaughters[2] = {&kfPartPos, &kfPartNeg}; + std::array gammaDaughters = {&kfPartPos, &kfPartNeg}; KFParticle gamma; gamma.SetConstructMethod(2); - gamma.Construct(gammaDaughters, 2); + gamma.Construct(gammaDaughters.data(), 2); KFPVertex kfpVertex = createKFPVertexFromCollision(collision); KFParticle KFPV(kfpVertex); // Transport the gamma to the recalculated decay vertex KFParticle gammaDecayVtx = gamma; // with respect to (0,0,0) - gammaDecayVtx.TransportToPoint(gammaVtx); + gammaDecayVtx.TransportToPoint(gammaVtx.data()); v0.cosPA = cpaFromKF(gammaDecayVtx, KFPV); if (v0.cosPA < cfgGammaCuts.cosPa.value) { return false; @@ -634,10 +632,10 @@ bool buildAndSelectGamma(const Coll& collision, const std::array& dDaugh return false; } - KFParticle kfPartDecayVtxPos = kfPartPos; // Don't set Primary Vertex - KFParticle kfPartDecayVtxNeg = kfPartNeg; // Don't set Primary Vertex - kfPartDecayVtxPos.TransportToPoint(gammaVtx); // Don't set Primary Vertex - kfPartDecayVtxNeg.TransportToPoint(gammaVtx); // Don't set Primary Vertex + KFParticle kfPartDecayVtxPos = kfPartPos; // Don't set Primary Vertex + KFParticle kfPartDecayVtxNeg = kfPartNeg; // Don't set Primary Vertex + kfPartDecayVtxPos.TransportToPoint(gammaVtx.data()); // Don't set Primary Vertex + kfPartDecayVtxNeg.TransportToPoint(gammaVtx.data()); // Don't set Primary Vertex v0.dcaDau = kfPartDecayVtxPos.GetDistanceFromParticle(kfPartDecayVtxNeg); v0.momPos = RecoDecay::pVec(std::array{kfPartDecayVtxPos.GetPx(), kfPartDecayVtxPos.GetPy(), kfPartDecayVtxPos.GetPz()}); v0.momNeg = RecoDecay::pVec(std::array{kfPartDecayVtxNeg.GetPx(), kfPartDecayVtxNeg.GetPy(), kfPartDecayVtxNeg.GetPz()}); @@ -2013,7 +2011,6 @@ void runMcGen(McParticles const& mcParticles, } } } -} // namespace hf_charm_reso -} // namespace o2::analysis +} // namespace o2::analysis::hf_charm_reso #endif // PWGHF_D2H_CORE_DATACREATIONCHARMRESO_H_ diff --git a/PWGHF/D2H/DataModel/ReducedDataModel.h b/PWGHF/D2H/DataModel/ReducedDataModel.h index 09f8bcd2d1b..62e52032538 100644 --- a/PWGHF/D2H/DataModel/ReducedDataModel.h +++ b/PWGHF/D2H/DataModel/ReducedDataModel.h @@ -247,6 +247,14 @@ DECLARE_SOA_COLUMN(TOFNSigmaKaProng2, tofNSigmaKaProng2, float); //! NsigmaTOFKa DECLARE_SOA_COLUMN(TOFNSigmaPrProng0, tofNSigmaPrProng0, float); //! NsigmaTOFPr for prong0, o2-linter: disable=name/o2-column (written to disk) DECLARE_SOA_COLUMN(TOFNSigmaPrProng1, tofNSigmaPrProng1, float); //! NsigmaTOFPr for prong1, o2-linter: disable=name/o2-column (written to disk) DECLARE_SOA_COLUMN(TOFNSigmaPrProng2, tofNSigmaPrProng2, float); //! NsigmaTOFPr for prong2, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TPCNSigmaMuPos, tpcNSigmaMuPos, float); //! NsigmaTPCMu for positive J/psi daughter, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TPCNSigmaMuNeg, tpcNSigmaMuNeg, float); //! NsigmaTPCMu for negative J/psi daughter, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TPCNSigmaElPos, tpcNSigmaElPos, float); //! NsigmaTPCEl for positive J/psi daughter, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TPCNSigmaElNeg, tpcNSigmaElNeg, float); //! NsigmaTPCEl for negative J/psi daughter, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TOFNSigmaMuPos, tofNSigmaMuPos, float); //! NsigmaTPCMu for positive J/psi daughter, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TOFNSigmaMuNeg, tofNSigmaMuNeg, float); //! NsigmaTPCMu for negative J/psi daughter, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TOFNSigmaElPos, tofNSigmaElPos, float); //! NsigmaTPCEl for positive J/psi daughter, o2-linter: disable=name/o2-column (written to disk) +DECLARE_SOA_COLUMN(TOFNSigmaElNeg, tofNSigmaElNeg, float); //! NsigmaTPCEl for negative J/psi daughter, o2-linter: disable=name/o2-column (written to disk) // dynamic columns DECLARE_SOA_DYNAMIC_COLUMN(TPCTOFNSigmaPi, tpcTofNSigmaPi, //! Combination of NsigmaTPC and NsigmaTOF, o2-linter: disable=name/o2-column (written to disk) [](float tpcNSigmaPi, float tofNSigmaPi) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaPi, tofNSigmaPi); }); @@ -272,6 +280,14 @@ DECLARE_SOA_DYNAMIC_COLUMN(TPCTOFNSigmaPrProng1, tpcTofNSigmaPrProng1, //! Combi [](float tpcNSigmaPr, float tofNSigmaPr) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaPr, tofNSigmaPr); }); DECLARE_SOA_DYNAMIC_COLUMN(TPCTOFNSigmaPrProng2, tpcTofNSigmaPrProng2, //! Combination of NsigmaTPC and NsigmaTOF, o2-linter: disable=name/o2-column (written to disk) [](float tpcNSigmaPr, float tofNSigmaPr) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaPr, tofNSigmaPr); }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCTOFNSigmaMuPos, tpcTofNSigmaMuPos, //! Combination of NsigmaTPC and NsigmaTOF, o2-linter: disable=name/o2-column (written to disk) + [](float tpcNSigmaMu, float tofNSigmaMu) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaMu, tofNSigmaMu); }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCTOFNSigmaMuNeg, tpcTofNSigmaMuNeg, //! Combination of NsigmaTPC and NsigmaTOF, o2-linter: disable=name/o2-column (written to disk) + [](float tpcNSigmaMu, float tofNSigmaMu) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaMu, tofNSigmaMu); }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCTOFNSigmaElPos, tpcTofNSigmaElPos, //! Combination of NsigmaTPC and NsigmaTOF, o2-linter: disable=name/o2-column (written to disk) + [](float tpcNSigmaEl, float tofNSigmaEl) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaEl, tofNSigmaEl); }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCTOFNSigmaElNeg, tpcTofNSigmaElNeg, //! Combination of NsigmaTPC and NsigmaTOF, o2-linter: disable=name/o2-column (written to disk) + [](float tpcNSigmaEl, float tofNSigmaEl) -> float { return pid_tpc_tof_utils::combineNSigma(tpcNSigmaEl, tofNSigmaEl); }); } // namespace hf_track_pid_reduced // CAREFUL: need to follow convention [Name = Description + 's'] in DECLARE_SOA_TABLE(Name, "AOD", Description) @@ -327,8 +343,9 @@ DECLARE_SOA_TABLE(HfRedBach0Bases, "AOD", "HFREDBACH0BASE", //! Table with track hf_track_pid_reduced::TPCTOFNSigmaPr, aod::track::Px, aod::track::Py, - aod::track::Pz, - aod::track::PVector); + aod::track::Pz, + aod::track::PVector, + aod::track::Sign); DECLARE_SOA_TABLE(HfRedBach0Cov, "AOD", "HFREDBACH0COV", //! Table with track covariance information for reduced workflow soa::Index<>, @@ -359,8 +376,9 @@ DECLARE_SOA_TABLE(HfRedBach1Bases, "AOD", "HFREDBACH1BASE", //! Table with track hf_track_pid_reduced::TPCTOFNSigmaPr, aod::track::Px, aod::track::Py, - aod::track::Pz, - aod::track::PVector); + aod::track::Pz, + aod::track::PVector, + aod::track::Sign); DECLARE_SOA_TABLE(HfRedBach1Cov, "AOD", "HFREDBACH1COV", //! Table with track covariance information for reduced workflow soa::Index<>, @@ -666,6 +684,20 @@ DECLARE_SOA_TABLE(HfRedJpsiCov, "AOD", "HFREDJPSICOV", //! Table with J/Psi cand hf_jpsi_cand_reduced::C1PtTglDauPos, hf_jpsi_cand_reduced::C1PtTglDauNeg, hf_jpsi_cand_reduced::C1Pt21Pt2DauPos, hf_jpsi_cand_reduced::C1Pt21Pt2DauNeg); +DECLARE_SOA_TABLE(HfRedJpsiDauPid, "AOD", "HFREDJPSIDAUPID", //! Table with J/Psi daughter PID information (muon, electron) for reduced workflow + hf_track_pid_reduced::TPCNSigmaMuPos, + hf_track_pid_reduced::TPCNSigmaMuNeg, + hf_track_pid_reduced::TPCNSigmaElPos, + hf_track_pid_reduced::TPCNSigmaElNeg, + hf_track_pid_reduced::TOFNSigmaMuPos, + hf_track_pid_reduced::TOFNSigmaMuNeg, + hf_track_pid_reduced::TOFNSigmaElPos, + hf_track_pid_reduced::TOFNSigmaElNeg, + hf_track_pid_reduced::TPCTOFNSigmaMuPos, + hf_track_pid_reduced::TPCTOFNSigmaMuNeg, + hf_track_pid_reduced::TPCTOFNSigmaElPos, + hf_track_pid_reduced::TPCTOFNSigmaElNeg); + DECLARE_SOA_TABLE(HfRedPidDau0s_000, "AOD", "HFREDPIDDAU0", //! hf_track_pid_reduced::TPCNSigmaPiProng0, hf_track_pid_reduced::TOFNSigmaPiProng0, @@ -749,14 +781,17 @@ using HfRedPidDau2 = HfRedPidDau2s::iterator; // Beauty candidates prongs namespace hf_cand_b0_reduced { -DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfRed3Prongs, "_0"); //! Prong0 index -DECLARE_SOA_INDEX_COLUMN_FULL(Prong1, prong1, int, HfRedTrackBases, "_1"); //! Prong1 index -DECLARE_SOA_INDEX_COLUMN_FULL(ProngD0, prongD0, int, HfRed2Prongs, "_0"); //! ProngD0 index -DECLARE_SOA_INDEX_COLUMN_FULL(ProngBachPi, prongBachPi, int, HfRedTrackBases, "_1"); //! ProngBachPi index -DECLARE_SOA_INDEX_COLUMN_FULL(ProngSoftPi, prongSoftPi, int, HfRedSoftPiBases, "_2"); //! ProngSoftPi index -DECLARE_SOA_COLUMN(Prong0MlScoreBkg, prong0MlScoreBkg, float); //! Bkg ML score of the D daughter -DECLARE_SOA_COLUMN(Prong0MlScorePrompt, prong0MlScorePrompt, float); //! Prompt ML score of the D daughter -DECLARE_SOA_COLUMN(Prong0MlScoreNonprompt, prong0MlScoreNonprompt, float); //! Nonprompt ML score of the D daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfRed3Prongs, "_0"); //! Prong0 index +DECLARE_SOA_INDEX_COLUMN_FULL(Prong1, prong1, int, HfRedTrackBases, "_1"); //! Prong1 index +DECLARE_SOA_INDEX_COLUMN_FULL(ProngD0, prongD0, int, HfRed2Prongs, "_0"); //! ProngD0 index +DECLARE_SOA_INDEX_COLUMN_FULL(ProngBachPi, prongBachPi, int, HfRedTrackBases, "_1"); //! ProngBachPi index +DECLARE_SOA_INDEX_COLUMN_FULL(ProngSoftPi, prongSoftPi, int, HfRedSoftPiBases, "_2"); //! ProngSoftPi index +DECLARE_SOA_INDEX_COLUMN_FULL(Jpsi, jpsi, int, HfRedJpsis, "_0"); //! J/Psi index +DECLARE_SOA_INDEX_COLUMN_FULL(Prong0K0Star, prong0K0Star, int, HfRedBach0Bases, "_0"); //! K*0 index +DECLARE_SOA_INDEX_COLUMN_FULL(Prong1K0Star, prong1K0Star, int, HfRedBach1Bases, "_0"); //! K*0 index +DECLARE_SOA_COLUMN(Prong0MlScoreBkg, prong0MlScoreBkg, float); //! Bkg ML score of the D daughter +DECLARE_SOA_COLUMN(Prong0MlScorePrompt, prong0MlScorePrompt, float); //! Prompt ML score of the D daughter +DECLARE_SOA_COLUMN(Prong0MlScoreNonprompt, prong0MlScoreNonprompt, float); //! Nonprompt ML score of the D daughter } // namespace hf_cand_b0_reduced DECLARE_SOA_TABLE(HfRedB0Prongs, "AOD", "HFREDB0PRONG", //! Table with B0 daughter indices @@ -765,6 +800,9 @@ DECLARE_SOA_TABLE(HfRedB0Prongs, "AOD", "HFREDB0PRONG", //! Table with B0 daught DECLARE_SOA_TABLE(HfRedB0ProngDStars, "AOD", "HFREDB0PRONGDST", //! Table with B0 daughter indices hf_cand_b0_reduced::ProngD0Id, hf_cand_b0_reduced::ProngBachPiId, hf_cand_b0_reduced::ProngSoftPiId); +DECLARE_SOA_TABLE(HfRedB02JpsiDaus, "AOD", "HFREDB02JPSIDAU", + hf_cand_b0_reduced::JpsiId, hf_cand_b0_reduced::Prong0K0StarId, hf_cand_b0_reduced::Prong1K0StarId); + DECLARE_SOA_TABLE(HfRedB0DpMls, "AOD", "HFREDB0DPML", //! Table with ML scores for the D+ daughter hf_cand_b0_reduced::Prong0MlScoreBkg, hf_cand_b0_reduced::Prong0MlScorePrompt, @@ -773,6 +811,7 @@ DECLARE_SOA_TABLE(HfRedB0DpMls, "AOD", "HFREDB0DPML", //! Table with ML scores f using HfRedCandB0 = soa::Join; using HfRedCandB0DStar = soa::Join; +using HfRedCandB0ToJpsiK0Star = soa::Join; namespace hf_cand_bplus_reduced { @@ -904,6 +943,16 @@ DECLARE_SOA_TABLE(HfMcRecRedDStarPis, "AOD", "HFMCRECREDDSTPI", //! Table with r hf_cand_mc_flag::DebugMcRec, hf_b0_mc::PtMother); +DECLARE_SOA_TABLE(HfMcRecRedJPK0ss, "AOD", "HFMCRECREDJPK0S", //! Table with reconstructed MC information on J/Psi/K*0(<-B0) pairs for reduced workflow + hf_cand_b0_reduced::JpsiId, + hf_cand_b0_reduced::Prong0K0StarId, + hf_cand_b0_reduced::Prong1K0StarId, + hf_cand_mc_flag::FlagMcMatchRec, + hf_cand_mc_flag::FlagMcDecayChanRec, + hf_cand_mc_flag::FlagWrongCollision, + hf_cand_mc_flag::DebugMcRec, + hf_b0_mc::PtMother); + // Table with same size as HFCANDB0 DECLARE_SOA_TABLE(HfMcRecRedB0s, "AOD", "HFMCRECREDB0", //! Reconstruction-level MC information on B0 candidates for reduced workflow hf_cand_mc_flag::FlagMcMatchRec, @@ -941,14 +990,18 @@ DECLARE_SOA_TABLE(HfMcGenRedB0s, "AOD", "HFMCGENREDB0", //! Generation-level MC // so we can use them in the B0 part namespace hf_cand_b0_config { -DECLARE_SOA_COLUMN(MySelectionFlagD, mySelectionFlagD, int8_t); //! Flag to filter selected D+ mesons -DECLARE_SOA_COLUMN(MyInvMassWindowDPi, myInvMassWindowDPi, float); //! Half-width of the B0 invariant-mass window in GeV/c2 +DECLARE_SOA_COLUMN(MySelectionFlagD, mySelectionFlagD, int8_t); //! Flag to filter selected D+ mesons +DECLARE_SOA_COLUMN(MyInvMassWindowDPi, myInvMassWindowDPi, float); //! Half-width of the B0 invariant-mass window in GeV/c2 +DECLARE_SOA_COLUMN(MyInvMassWindowJpsiK0Star, myInvMassWindowJpsiK0Star, float); //! Half-width of the B0 invariant-mass window in GeV/c2 } // namespace hf_cand_b0_config DECLARE_SOA_TABLE(HfCandB0Configs, "AOD", "HFCANDB0CONFIG", //! Table with configurables information for reduced workflow hf_cand_b0_config::MySelectionFlagD, hf_cand_b0_config::MyInvMassWindowDPi); +DECLARE_SOA_TABLE(HfCfgB0ToJpsis, "AOD", "HFCFGB0TOJPSI", //! Table with configurables information for reduced workflow + hf_cand_b0_config::MyInvMassWindowJpsiK0Star); + namespace hf_bplus_mc { // MC Rec @@ -1046,7 +1099,7 @@ DECLARE_SOA_TABLE(HfCandBpConfigs, "AOD", "HFCANDBPCONFIG", //! Table with confi hf_cand_bplus_config::MySelectionFlagD0bar, hf_cand_bplus_config::MyInvMassWindowD0Pi); -DECLARE_SOA_TABLE(HfCfgBpToJpsi, "AOD", "HFCFGBPTOJPSI", //! Table with configurables information for reduced workflow +DECLARE_SOA_TABLE(HfCfgBpToJpsis, "AOD", "HFCFGBPTOJPSI", //! Table with configurables information for reduced workflow hf_cand_bplus_config::MyInvMassWindowJpsiK); namespace hf_bs_mc diff --git a/PWGHF/D2H/Macros/HFInvMassFitter.cxx b/PWGHF/D2H/Macros/HFInvMassFitter.cxx index e56c9b87a28..1d25c5a3778 100644 --- a/PWGHF/D2H/Macros/HFInvMassFitter.cxx +++ b/PWGHF/D2H/Macros/HFInvMassFitter.cxx @@ -19,9 +19,12 @@ /// \author Oleksii Lubynets /// \author Phil Stahlhut +// o2-linter: disable=name/workflow-file (not an O2Physics workflow) + #include "HFInvMassFitter.h" #include +#include #include #include #include @@ -37,8 +40,12 @@ #include #include #include +#include #include +#include // IWYU pragma: keep (do not replace with TMatrixDSymfwd.h) +#include #include +#include #include #include #include @@ -46,12 +53,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include using namespace RooFit; @@ -60,7 +69,8 @@ HFInvMassFitter::HFInvMassFitter(TH1* histoToFit, double minValue, double maxValue, int fitTypeBkg, - int fitTypeSgn) : mHistoInvMass(nullptr), + int fitTypeSgn, + int randomSeed) : mHistoInvMass(histoToFit), mFitOption("L,E"), mMinMass(minValue), mMaxMass(maxValue), @@ -86,6 +96,7 @@ HFInvMassFitter::HFInvMassFitter(TH1* histoToFit, mFixedSigma(false), mFixedSigmaDoubleGaus(false), mBoundSigma(false), + mFixedDscbTailParams(false), mSigmaValue(0.012), mParamSgn(0.1), mFracDoubleGaus(0.2), @@ -106,6 +117,18 @@ HFInvMassFitter::HFInvMassFitter(TH1* histoToFit, mChiSquareOverNdfTotal(0), mChiSquareOverNdfBkg(0), mFixReflOverSgn(false), + mDscbAlphaLInitialValue(1.5), + mDscbAlphaLLowLimit(0.1), + mDscbAlphaLUpLimit(5.0), + mDscbAlphaRInitialValue(1.5), + mDscbAlphaRLowLimit(0.1), + mDscbAlphaRUpLimit(5.0), + mDscbNLInitialValue(2.0), + mDscbNLLowLimit(0.5), + mDscbNLUpLimit(50.), + mDscbNRInitialValue(2.0), + mDscbNRLowLimit(0.5), + mDscbNRUpLimit(50.), mRooMeanSgn(nullptr), mRooSigmaSgn(nullptr), mRooSecSigmaSgn(nullptr), @@ -116,32 +139,44 @@ HFInvMassFitter::HFInvMassFitter(TH1* histoToFit, mRooNSgn(nullptr), mRooNBkg(nullptr), mRooNRefl(nullptr), + mRooDscbAlphaL(nullptr), + mRooDscbAlphaR(nullptr), + mRooDscbNL(nullptr), + mRooDscbNR(nullptr), mTotalPdf(nullptr), mInvMassFrame(nullptr), mReflFrame(nullptr), mReflOnlyFrame(nullptr), mResidualFrame(nullptr), + mResidualHist(nullptr), mRatioFrame(nullptr), - mResidualFrameForCalculation(nullptr), mWorkspace(nullptr), - mIntegralHisto(0), mIntegralBkg(0), mIntegralSgn(0), mHistoTemplateRefl(nullptr), mDrawBgPrefit(false), - mHighlightPeakRegion(false) + mHighlightPeakRegion(false), + mRandomSeed(randomSeed), + mRandomGen(nullptr), + mFitStatus(-999), + mCovQual(-999), + mEdm(-999.), + mMinNll(-999.), + mSgnGlobalCorrelCoeff(-999.), + mCovCorrMatrix(nullptr) { // standard constructor - mHistoInvMass = histoToFit; - mHistoInvMass->SetName("mHistoInvMass"); mHistoInvMass->SetDirectory(nullptr); + if (mRandomSeed >= 0) { + mRandomGen = new TRandom3(); + mRandomGen->SetSeed(mRandomSeed); + } } HFInvMassFitter::~HFInvMassFitter() { /// destructor - delete mHistoInvMass; delete mHistoTemplateRefl; delete mRooMeanSgn; delete mRooSigmaSgn; @@ -163,7 +198,6 @@ HFInvMassFitter::~HFInvMassFitter() void HFInvMassFitter::doFit() { - mIntegralHisto = mHistoInvMass->Integral(mHistoInvMass->FindBin(mMinMass), mHistoInvMass->FindBin(mMaxMass)); mWorkspace = new RooWorkspace("mWorkspace"); fillWorkspace(*mWorkspace); RooRealVar* mass = mWorkspace->var("mass"); @@ -172,67 +206,68 @@ void HFInvMassFitter::doFit() if (mTypeOfBkgPdf == NoBkg) { // MC mass->setRange("signal", mMass - 3. * mSigmaSgn, mMass + 3. * mSigmaSgn); } else { + mass->setRange("SBL", mMinMass, mMass - mNSigmaForSidebands * mSigmaSgn); if (mTypeOfSgnPdf == GausSec) { // Second Peak fit range - mass->setRange("SBL", mMinMass, mMass - mNSigmaForSidebands * mSigmaSgn); mass->setRange("SBR", mMass + mNSigmaForSidebands * mSigmaSgn, mSecMass - mNSigmaForSidebands * mSecSigma); mass->setRange("SEC", mSecMass + mNSigmaForSidebands * mSecSigma, mMaxMass); - mass->setRange("signal", mSecMass - mNSigmaForSidebands * mSecSigma, mSecMass + mNSigmaForSidebands * mSecSigma); + mass->setRange("signal", mSecMass - mNSigmaForSgn * mSecSigma, mSecMass + mNSigmaForSgn * mSecSigma); } else { // Single Peak fit range - mass->setRange("SBL", mMinMass, mMass - mNSigmaForSidebands * mSigmaSgn); mass->setRange("SBR", mMass + mNSigmaForSidebands * mSigmaSgn, mMaxMass); mass->setRange("signal", mMass - mNSigmaForSgn * mSigmaSgn, mMass + mNSigmaForSgn * mSigmaSgn); } } - mass->setRange("bkg", mMass - 4 * mSigmaSgn, mMass + 4 * mSigmaSgn); + mass->setRange("bkg", mMass - mNSigmaForSgn * mSigmaSgn, mMass + mNSigmaForSgn * mSigmaSgn); mass->setRange("full", mMinMass, mMaxMass); mInvMassFrame = mass->frame(Title(Form("%s", mHistoInvMass->GetTitle()))); // define the frame to plot dataHistogram.plotOn(mInvMassFrame, Name("data_c")); // plot data histogram on the frame - // define number of background and background fit function - mRooNBkg = new RooRealVar("mRooNBkg", "number of background", 0.3 * mIntegralHisto, 0., 1.2 * mIntegralHisto); // background yield - RooAbsPdf* bkgPdf = createBackgroundFitFunction(mWorkspace); // Create background pdf - RooAbsPdf* sgnPdf = createSignalFitFunction(mWorkspace); // Create signal pdf + TH1* histoInvMassSB = dynamic_cast(mHistoInvMass->Clone()); + cutRangesFromHisto(histoInvMassSB, {"SBL", "SBR"}); + RooDataHist sbHistogram("sbHistogram", "sb", *mass, Import(*histoInvMassSB)); + RooAbsPdf* bkgPdf = createBackgroundFitFunction(mWorkspace); // Create background pdf + RooAbsPdf* sgnPdf = createSignalFitFunction(mWorkspace); // Create signal pdf + + const double integralHisto = integrateHistoInvMassOverWorkspaceRanges({"full"}); // fit MC or Data - if (mTypeOfBkgPdf == NoBkg) { // MC - mRooNSgn = new RooRealVar("mRooNSig", "number of signal", 0.3 * mIntegralHisto, 0., 1.2 * mIntegralHisto); // signal yield - mTotalPdf = new RooAddPdf("mMCFunc", "MC fit function", RooArgList(*sgnPdf), RooArgList(*mRooNSgn)); // create total pdf + RooFitResult* fitResult{nullptr}; + if (mTypeOfBkgPdf == NoBkg) { // MC + const ParameterRanges rooNSgnParamRanges{0.5 * integralHisto, 1.5 * integralHisto, integralHisto}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + mRooNSgn = new RooRealVar("mRooNSig", "number of signal", randomizeInitialParameter(rooNSgnParamRanges), rooNSgnParamRanges.lower, rooNSgnParamRanges.upper); // signal yield + mTotalPdf = new RooAddPdf("mMCFunc", "MC fit function", RooArgList(*sgnPdf), RooArgList(*mRooNSgn)); // create total pdf if (strcmp(mFitOption.c_str(), "Chi2") == 0) { - mTotalPdf->chi2FitTo(dataHistogram, Range("signal")); + fitResult = mTotalPdf->chi2FitTo(dataHistogram, Range("full"), Save()); } else { - mTotalPdf->fitTo(dataHistogram, Range("signal")); + fitResult = mTotalPdf->fitTo(dataHistogram, Range("full"), Save()); } RooAbsReal* signalIntegralMc = mTotalPdf->createIntegral(*mass, NormSet(*mass), Range("signal")); // sig yield from fit mIntegralSgn = signalIntegralMc->getValV(); - calculateSignal(mRawYield, mRawYieldErr); // calculate signal and signal error + calculateSignal(mRawYield, mRawYieldErr); // calculate signal and signal error + countSignal(mRawYieldCounted, mRawYieldCountedErr); mTotalPdf->plotOn(mInvMassFrame, Name("Tot_c")); // plot total function // Fit to data ratio mRatioFrame = mass->frame(Title(Form("%s", mHistoInvMass->GetTitle()))); calculateFitToDataRatio(); } else { // data + const double integralSidebands = integrateHistoInvMassOverWorkspaceRanges({"SBL", "SBR"}); + const ParameterRanges rooNBkgParamRanges{0.5 * integralSidebands, 1.5 * integralSidebands, integralSidebands}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + mRooNBkg = new RooRealVar("mRooNBkg", "number of background", randomizeInitialParameter(rooNBkgParamRanges), rooNBkgParamRanges.lower, rooNBkgParamRanges.upper); // background yield mBkgPdf = new RooAddPdf("mBkgPdf", "background fit function", RooArgList(*bkgPdf), RooArgList(*mRooNBkg)); + std::string sbRanges{"SBL,SBR"}; if (mTypeOfSgnPdf == GausSec) { // two peak fit - if (strcmp(mFitOption.c_str(), "Chi2") == 0) { - mBkgPdf->chi2FitTo(dataHistogram, Range("SBL,SBR,SEC"), Save()); - } else { - mBkgPdf->fitTo(dataHistogram, Range("SBL,SBR,SEC"), Save()); - } - } else { // single peak fit - if (strcmp(mFitOption.c_str(), "Chi2") == 0) { - mBkgPdf->chi2FitTo(dataHistogram, Range("SBL,SBR"), Save()); - } else { - mBkgPdf->fitTo(dataHistogram, Range("SBL,SBR"), Save()); - } + sbRanges.append(",SEC"); } + mBkgPdf->chi2FitTo(sbHistogram, DataError(RooAbsData::SumW2), Save()); + // define the frame to evaluate background sidebands chi2 (bg pdf needs to be plotted within sideband ranges) RooPlot* frameTemporary = mass->frame(Title(Form("%s_temp", mHistoInvMass->GetTitle()))); dataHistogram.plotOn(frameTemporary, Name("data_for_bkgchi2")); - mBkgPdf->plotOn(frameTemporary, Range("SBL", true), Name("Bkg_sidebands")); + mBkgPdf->plotOn(frameTemporary, Range(sbRanges.c_str()), Name("Bkg_sidebands")); mChiSquareOverNdfBkg = frameTemporary->chiSquare("Bkg_sidebands", "data_for_bkgchi2"); // calculate reduced chi2 / NDF of background sidebands (pre-fit) delete frameTemporary; if (mDrawBgPrefit) { - RooAbsPdf* bkgPdfPrefit = dynamic_cast(mBkgPdf->Clone()); - bkgPdfPrefit->plotOn(mInvMassFrame, Range("full"), Name("Bkg_c_prefit"), LineColor(kGray)); + auto* bkgPdfPrefit = dynamic_cast(mBkgPdf->Clone()); + bkgPdfPrefit->plotOn(mInvMassFrame, Range("full"), Normalization(mRooNBkg->getVal(), RooAbsReal::NumEvent), Name("Bkg_c_prefit"), LineColor(kGray)); delete bkgPdfPrefit; } @@ -243,20 +278,23 @@ void HFInvMassFitter::doFit() checkForSignal(estimatedSignal); // SIG's absolute integral in "bkg" range calculateBackground(mBkgYield, mBkgYieldErr); // BG's absolute integral in "bkg" range - mRooNSgn = new RooRealVar("mNSgn", "number of signal", 0.3 * estimatedSignal, 0., 1.2 * estimatedSignal); // estimated signal yield + const ParameterRanges rooNSgnParamRanges{0.1 * estimatedSignal, 10 * estimatedSignal, estimatedSignal}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + mRooNSgn = new RooRealVar("mRooNSig", "number of signal", randomizeInitialParameter(rooNSgnParamRanges), rooNSgnParamRanges.lower, rooNSgnParamRanges.upper); // estimated signal yield if (mFixedRawYield > 0) { mRooNSgn->setVal(mFixedRawYield); // fixed signal yield mRooNSgn->setConstant(true); } mSgnPdf = new RooAddPdf("mSgnPdf", "signal fit function", RooArgList(*sgnPdf), RooArgList(*mRooNSgn)); // create reflection template and fit to reflection + RooAbsPdf* reflPdf{nullptr}; if (mHistoTemplateRefl != nullptr) { - RooAbsPdf* reflPdf = createReflectionFitFunction(mWorkspace); // create reflection pdf + reflPdf = createReflectionFitFunction(mWorkspace); // create reflection pdf RooDataHist reflHistogram("reflHistogram", "refl for fit", *mass, Import(*mHistoTemplateRefl)); mReflFrame = mass->frame(); mReflOnlyFrame = mass->frame(Title(Form("%s", mHistoTemplateRefl->GetTitle()))); reflHistogram.plotOn(mReflOnlyFrame); - mRooNRefl = new RooRealVar("mNRefl", "number of reflection", 0.5 * mHistoTemplateRefl->Integral(), 0, mHistoTemplateRefl->Integral()); + const ParameterRanges rooNReflParamRanges{0., mHistoTemplateRefl->Integral(), 0.5 * mHistoTemplateRefl->Integral()}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + mRooNRefl = new RooRealVar("mNRefl", "number of reflection", randomizeInitialParameter(rooNReflParamRanges), rooNReflParamRanges.lower, rooNReflParamRanges.upper); RooAddPdf reflFuncTemp("reflFuncTemp", "template reflection fit function", RooArgList(*reflPdf), RooArgList(*mRooNRefl)); if (strcmp(mFitOption.c_str(), "Chi2") == 0) { reflFuncTemp.chi2FitTo(reflHistogram); @@ -269,42 +307,33 @@ void HFInvMassFitter::doFit() mRooNRefl->setConstant(true); setReflFuncFixed(); // fix reflection pdf parameter mTotalPdf = new RooAddPdf("mTotalPdf", "background + signal + reflection fit function", RooArgList(*bkgPdf, *sgnPdf, *reflPdf), RooArgList(*mRooNBkg, *mRooNSgn, *mRooNRefl)); - if (strcmp(mFitOption.c_str(), "Chi2") == 0) { - mTotalPdf->chi2FitTo(dataHistogram); - } else { - mTotalPdf->fitTo(dataHistogram); - } - mTotalPdf->plotOn(mInvMassFrame, Name("Tot_c")); + } else { + mTotalPdf = new RooAddPdf("mTotalPdf", "background + signal pdf", RooArgList(*bkgPdf, *sgnPdf), RooArgList(*mRooNBkg, *mRooNSgn)); + } + + if (strcmp(mFitOption.c_str(), "Chi2") == 0) { + fitResult = mTotalPdf->chi2FitTo(dataHistogram, Save()); + } else { + fitResult = mTotalPdf->fitTo(dataHistogram, Save()); + } + + plotBkg(mTotalPdf); + mTotalPdf->plotOn(mInvMassFrame, Name("Tot_c"), LineColor(kBlue)); + if (mHistoTemplateRefl != nullptr) { mReflPdf = new RooAddPdf("mReflPdf", "reflection fit function", RooArgList(*reflPdf), RooArgList(*mRooNRefl)); RooAddPdf const reflBkgPdf("reflBkgPdf", "reflBkgPdf", RooArgList(*bkgPdf, *reflPdf), RooArgList(*mRooNBkg, *mRooNRefl)); reflBkgPdf.plotOn(mInvMassFrame, Normalization(1.0, RooAbsReal::RelativeExpected), LineStyle(7), LineColor(kRed + 1), Name("ReflBkg_c")); - plotBkg(mTotalPdf); // plot bkg pdf in total pdf - plotRefl(mTotalPdf); // plot reflection in total pdf - mChiSquareOverNdfTotal = mInvMassFrame->chiSquare("Tot_c", "data_c"); // calculate reduced chi2 / NDF - - // plot residual distribution - RooHist* residualHistogram = mInvMassFrame->residHist("data_c", "ReflBkg_c"); - mResidualFrame = mass->frame(Title("Residual Distribution")); - mResidualFrame->addPlotable(residualHistogram, "p"); - mSgnPdf->plotOn(mResidualFrame, Normalization(1.0, RooAbsReal::RelativeExpected), LineColor(kBlue)); + plotRefl(mTotalPdf); // plot reflection in total pdf } else { - mTotalPdf = new RooAddPdf("mTotalPdf", "background + signal pdf", RooArgList(*bkgPdf, *sgnPdf), RooArgList(*mRooNBkg, *mRooNSgn)); - if (strcmp(mFitOption.c_str(), "Chi2") == 0) { - mTotalPdf->chi2FitTo(dataHistogram); - } else { - mTotalPdf->fitTo(dataHistogram); - } - plotBkg(mTotalPdf); - mTotalPdf->plotOn(mInvMassFrame, Name("Tot_c"), LineColor(kBlue)); mSgnPdf->plotOn(mInvMassFrame, Normalization(1.0, RooAbsReal::RelativeExpected), DrawOption("F"), FillColor(TColor::GetColorTransparent(kBlue, 0.2)), VLines()); - mChiSquareOverNdfTotal = mInvMassFrame->chiSquare("Tot_c", "data_c"); // calculate reduced chi2 / DNF - - // plot residual distribution - mResidualFrame = mass->frame(Title("Residual Distribution")); - RooHist* residualHistogram = mInvMassFrame->residHist("data_c", "Bkg_c"); - mResidualFrame->addPlotable(residualHistogram, "P"); - mSgnPdf->plotOn(mResidualFrame, Normalization(1.0, RooAbsReal::RelativeExpected), LineColor(kBlue)); } + mChiSquareOverNdfTotal = mInvMassFrame->chiSquare("Tot_c", "data_c"); // calculate reduced chi2 / NDF + // plot residual distribution + mResidualFrame = mass->frame(Title(Form("%s", mHistoInvMass->GetTitle()))); + mResidualHist = mInvMassFrame->residHist("data_c", mHistoTemplateRefl ? "ReflBkg_c" : "Bkg_c"); + mResidualFrame->addPlotable(mResidualHist, "P"); + mSgnPdf->plotOn(mResidualFrame, Normalization(1.0, RooAbsReal::RelativeExpected), LineColor(kBlue)); + mass->setRange("bkgForSignificance", mRooMeanSgn->getVal() - mNSigmaForSgn * mRooSecSigmaSgn->getVal(), mRooMeanSgn->getVal() + mNSigmaForSgn * mRooSecSigmaSgn->getVal()); bkgIntegral = mBkgPdf->createIntegral(*mass, NormSet(*mass), Range("bkgForSignificance")); mIntegralBkg = bkgIntegral->getValV(); @@ -319,6 +348,12 @@ void HFInvMassFitter::doFit() mRatioFrame = mass->frame(Title(Form("%s", mHistoInvMass->GetTitle()))); calculateFitToDataRatio(); } + mFitStatus = fitResult->status(); + mCovQual = fitResult->covQual(); + mEdm = fitResult->edm(); + mMinNll = fitResult->minNll(); + mSgnGlobalCorrelCoeff = fitResult->globalCorr("mRooNSig"); + mCovCorrMatrix = fillCovCorrMatrix(fitResult); } void HFInvMassFitter::fillWorkspace(RooWorkspace& workspace) const @@ -326,35 +361,42 @@ void HFInvMassFitter::fillWorkspace(RooWorkspace& workspace) const // Declare observable variable RooRealVar mass("mass", "mass", mMinMass, mMaxMass, "GeV/c^{2}"); // bkg expo - RooRealVar tau("tau", "tau", -1, -5., 5.); + const ParameterRanges tauParamRanges{-5., 5., -1., 0.1}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar tau("tau", "tau", randomizeInitialParameter(tauParamRanges), tauParamRanges.lower, tauParamRanges.upper); RooAbsPdf* bkgFuncExpo = new RooExponential("bkgFuncExpo", "background fit function", mass, tau); workspace.import(*bkgFuncExpo); delete bkgFuncExpo; // bkg poly1 - RooRealVar const polyParam0("polyParam0", "Parameter of Poly function", 0.5, -5., 5.); - RooRealVar const polyParam1("polyParam1", "Parameter of Poly function", 0.2, -5., 5.); + const ParameterRanges polyParam0ParamRanges{-5., 5., 0.5, 0.1}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyParam0("polyParam0", "Parameter of Poly function", randomizeInitialParameter(polyParam0ParamRanges), polyParam0ParamRanges.lower, polyParam0ParamRanges.upper); + const ParameterRanges polyParam1ParamRanges{-5., 5., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyParam1("polyParam1", "Parameter of Poly function", randomizeInitialParameter(polyParam1ParamRanges), polyParam1ParamRanges.lower, polyParam1ParamRanges.upper); RooAbsPdf* bkgFuncPoly1 = new RooPolynomial("bkgFuncPoly1", "background fit function", mass, RooArgSet(polyParam0, polyParam1)); workspace.import(*bkgFuncPoly1); delete bkgFuncPoly1; // bkg poly2 - RooRealVar const polyParam2("polyParam2", "Parameter of Poly function", 0.2, -5., 5.); + const ParameterRanges polyParam2ParamRanges{-5., 5., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyParam2("polyParam2", "Parameter of Poly function", randomizeInitialParameter(polyParam2ParamRanges), polyParam2ParamRanges.lower, polyParam2ParamRanges.upper); RooAbsPdf* bkgFuncPoly2 = new RooPolynomial("bkgFuncPoly2", "background fit function", mass, RooArgSet(polyParam0, polyParam1, polyParam2)); workspace.import(*bkgFuncPoly2); delete bkgFuncPoly2; // bkg poly3 - RooRealVar const polyParam3("polyParam3", "Parameter of Poly function", 0.2, -1., 1.); + const ParameterRanges polyParam3ParamRanges{-1., 1., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyParam3("polyParam3", "Parameter of Poly function", randomizeInitialParameter(polyParam3ParamRanges), polyParam3ParamRanges.lower, polyParam3ParamRanges.upper); RooAbsPdf* bkgFuncPoly3 = new RooPolynomial("bkgFuncPoly3", "background pdf", mass, RooArgSet(polyParam0, polyParam1, polyParam2, polyParam3)); workspace.import(*bkgFuncPoly3); delete bkgFuncPoly3; // bkg power law RooRealVar const powParam1("powParam1", "Parameter of Pow function", TDatabasePDG::Instance()->GetParticle("pi+")->Mass()); - RooRealVar const powParam2("powParam2", "Parameter of Pow function", 1., -10, 10); + const ParameterRanges powParam2ParamRanges{-10., 10., 1., 0.2}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const powParam2("powParam2", "Parameter of Pow function", randomizeInitialParameter(powParam2ParamRanges), powParam2ParamRanges.lower, powParam2ParamRanges.upper); RooAbsPdf* bkgFuncPow = new RooGenericPdf("bkgFuncPow", "bkgFuncPow", "(mass-powParam1)^powParam2", RooArgSet(mass, powParam1, powParam2)); workspace.import(*bkgFuncPow); delete bkgFuncPow; // pow * exp RooRealVar const powExpoParam1("powExpoParam1", "Parameter of PowExpo function", 1. / 2.); - RooRealVar const powExpoParam2("powExpoParam2", "Parameter of PowExpo function", 1, -10, 10); + const ParameterRanges powExpoParam2ParamRanges{-10., 10., 1., 0.2}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const powExpoParam2("powExpoParam2", "Parameter of PowExpo function", randomizeInitialParameter(powExpoParam2ParamRanges), powExpoParam2ParamRanges.lower, powExpoParam2ParamRanges.upper); RooRealVar massPi("massPi", "mass of pion", TDatabasePDG::Instance()->GetParticle("pi+")->Mass()); RooFormulaVar powExpoParam3("powExpoParam3", "powExpoParam1 + 1", RooArgList(powExpoParam1)); RooFormulaVar powExpoParam4("powExpoParam4", "1./powExpoParam2", RooArgList(powExpoParam2)); @@ -482,18 +524,48 @@ void HFInvMassFitter::fillWorkspace(RooWorkspace& workspace) const RooAbsPdf* reflFuncDoubleGaus = new RooAddPdf("reflFuncDoubleGaus", "reflection pdf", RooArgList(gausRefl1, gausRefl2), fracRefl); workspace.import(*reflFuncDoubleGaus); delete reflFuncDoubleGaus; + // signal DSCB pdf + const ParameterRanges dscbAlphaLParamRanges{mDscbAlphaLLowLimit, mDscbAlphaLUpLimit, mDscbAlphaLInitialValue}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + const ParameterRanges dscbNLParamRanges{mDscbNLLowLimit, mDscbNLUpLimit, mDscbNLInitialValue}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + const ParameterRanges dscbAlphaRParamRanges{mDscbAlphaRLowLimit, mDscbAlphaRUpLimit, mDscbAlphaRInitialValue}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + const ParameterRanges dscbNRParamRanges{mDscbNRLowLimit, mDscbNRUpLimit, mDscbNRInitialValue}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar alphaL("alphaL", "left tail alpha", randomizeInitialParameter(dscbAlphaLParamRanges), dscbAlphaLParamRanges.lower, dscbAlphaLParamRanges.upper); + RooRealVar nL("nL", "left tail n", randomizeInitialParameter(dscbNLParamRanges), dscbNLParamRanges.lower, dscbNLParamRanges.upper); + RooRealVar alphaR("alphaR", "right tail alpha", randomizeInitialParameter(dscbAlphaRParamRanges), dscbAlphaRParamRanges.lower, dscbAlphaRParamRanges.upper); + RooRealVar nR("nR", "right tail n", randomizeInitialParameter(dscbNRParamRanges), dscbNRParamRanges.lower, dscbNRParamRanges.upper); + if (mFixedDscbTailParams) { + printf("Fixing DSCB tail parameters to initial values.\n"); + alphaL.setVal(mDscbAlphaLInitialValue); + alphaL.setConstant(true); + alphaR.setVal(mDscbAlphaRInitialValue); + alphaR.setConstant(true); + nL.setVal(mDscbNLInitialValue); + nL.setConstant(true); + nR.setVal(mDscbNRInitialValue); + nR.setConstant(true); + } + RooAbsPdf* sgnFuncDSCB = new RooCrystalBall("sgnFuncDSCB", "double sided crystal ball signal", mass, mean, sigma, alphaL, nL, alphaR, nR); + workspace.import(*sgnFuncDSCB); + delete sgnFuncDSCB; // reflection poly3 - RooRealVar const polyReflParam0("polyReflParam0", "polyReflParam0", 0.5, -1., 1.); - RooRealVar const polyReflParam1("polyReflParam1", "polyReflParam1", 0.2, -1., 1.); - RooRealVar const polyReflParam2("polyReflParam2", "polyReflParam2", 0.2, -1., 1.); - RooRealVar const polyReflParam3("polyReflParam3", "polyReflParam3", 0.2, -1., 1.); + const ParameterRanges polyReflParam0ParamRanges{-1., 1., 0.5, 0.1}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyReflParam0("polyReflParam0", "polyReflParam0", randomizeInitialParameter(polyReflParam0ParamRanges), polyReflParam0ParamRanges.lower, polyReflParam0ParamRanges.upper); + const ParameterRanges polyReflParam1ParamRanges{-1., 1., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyReflParam1("polyReflParam1", "polyReflParam1", randomizeInitialParameter(polyReflParam1ParamRanges), polyReflParam1ParamRanges.lower, polyReflParam1ParamRanges.upper); + const ParameterRanges polyReflParam2ParamRanges{-1., 1., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyReflParam2("polyReflParam2", "polyReflParam2", randomizeInitialParameter(polyReflParam2ParamRanges), polyReflParam2ParamRanges.lower, polyReflParam2ParamRanges.upper); + const ParameterRanges polyReflParam3ParamRanges{-1., 1., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyReflParam3("polyReflParam3", "polyReflParam3", randomizeInitialParameter(polyReflParam3ParamRanges), polyReflParam3ParamRanges.lower, polyReflParam3ParamRanges.upper); RooAbsPdf* reflFuncPoly3 = new RooPolynomial("reflFuncPoly3", "reflection PDF", mass, RooArgSet(polyReflParam0, polyReflParam1, polyReflParam2, polyReflParam3)); workspace.import(*reflFuncPoly3); delete reflFuncPoly3; // reflection poly6 - RooRealVar const polyReflParam4("polyReflParam4", "polyReflParam4", 0.2, -1., 1.); - RooRealVar const polyReflParam5("polyReflParam5", "polyReflParam5", 0.2, -1., 1.); - RooRealVar const polyReflParam6("polyReflParam6", "polyReflParam6", 0.2, -1., 1.); + const ParameterRanges polyReflParam4ParamRanges{-1., 1., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyReflParam4("polyReflParam4", "polyReflParam4", randomizeInitialParameter(polyReflParam4ParamRanges), polyReflParam4ParamRanges.lower, polyReflParam4ParamRanges.upper); + const ParameterRanges polyReflParam5ParamRanges{-1., 1., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyReflParam5("polyReflParam5", "polyReflParam5", randomizeInitialParameter(polyReflParam5ParamRanges), polyReflParam5ParamRanges.lower, polyReflParam5ParamRanges.upper); + const ParameterRanges polyReflParam6ParamRanges{-1., 1., 0.2, 0.05}; // NOLINT(modernize-use-designated-initializers): c++17 compatibility + RooRealVar const polyReflParam6("polyReflParam6", "polyReflParam6", randomizeInitialParameter(polyReflParam6ParamRanges), polyReflParam6ParamRanges.lower, polyReflParam6ParamRanges.upper); RooAbsPdf* reflFuncPoly6 = new RooPolynomial("reflFuncPoly6", "reflection pdf", mass, RooArgSet(polyReflParam0, polyReflParam1, polyReflParam2, polyReflParam3, polyReflParam4, polyReflParam5, polyReflParam6)); workspace.import(*reflFuncPoly6); delete reflFuncPoly6; @@ -515,9 +587,9 @@ void HFInvMassFitter::drawFit(TVirtualPad* pad, const std::vector& textFitMetrics->AddText(Form("S = %.0f #pm %.0f ", mRawYield, mRawYieldErr)); textFitMetrics->AddText(Form("S_{count} = %.0f #pm %.0f ", mRawYieldCounted, mRawYieldCountedErr)); if (mTypeOfBkgPdf != NoBkg) { - textFitMetrics->AddText(Form("B (%.1f#sigma) = %.0f #pm %.0f", mNSigmaForSidebands, mBkgYield, mBkgYieldErr)); - textFitMetrics->AddText(Form("S/B (%.1f#sigma) = %.4g ", mNSigmaForSidebands, mRawYield / mBkgYield)); - textFitMetrics->AddText(Form("Significance (%.1f#sigma) = %.1f #pm %.1f ", mNSigmaForSidebands, mSignificance, mSignificanceErr)); + textFitMetrics->AddText(Form("B (%.1f#sigma) = %.0f #pm %.0f", mNSigmaForSgn, mBkgYield, mBkgYieldErr)); + textFitMetrics->AddText(Form("S/B (%.1f#sigma) = %.4g ", mNSigmaForSgn, mRawYield / mBkgYield)); + textFitMetrics->AddText(Form("Significance (%.1f#sigma) = %.1f #pm %.1f ", mNSigmaForSgn, mSignificance, mSignificanceErr)); textFitMetrics->AddText(Form("#chi^{2} / ndf = %.3f", mChiSquareOverNdfTotal)); } if (mReflPdf != nullptr) { @@ -544,16 +616,17 @@ void HFInvMassFitter::drawFit(TVirtualPad* pad, const std::vector& const std::string fixMeanStatus = mFixedMean ? "fixed" : "free"; const std::string fixSigmaStatus = mFixedSigma ? "fixed" : "free"; const std::string fixSigmaDoubleGausStatus = mFixedSigmaDoubleGaus ? "fixed" : "free"; - textSignalPar->AddText(Form("mean(%s) = %.3f #pm %.3f", fixMeanStatus.c_str(), mRooMeanSgn->getVal(), mRooMeanSgn->getError())); - textSignalPar->AddText(Form("sigma(%s) = %.3f #pm %.3f", fixSigmaStatus.c_str(), mRooSigmaSgn->getVal(), mRooSigmaSgn->getError())); + textSignalPar->AddText(Form("#mu(%s) = %.3f #pm %.3f", fixMeanStatus.c_str(), mRooMeanSgn->getVal(), mRooMeanSgn->getError())); + textSignalPar->AddText(Form("#sigma(%s) = %.3f #pm %.3f", fixSigmaStatus.c_str(), mRooSigmaSgn->getVal(), mRooSigmaSgn->getError())); if (mTypeOfSgnPdf == DoubleGaus) { - textSignalPar->AddText(Form("sigma 2(%s) = %.3f #pm %.3f", fixSigmaDoubleGausStatus.c_str(), mRooSecSigmaSgn->getVal(), mRooSecSigmaSgn->getError())); + textSignalPar->AddText(Form("#sigma_{2}(%s) = %.3f #pm %.3f", fixSigmaDoubleGausStatus.c_str(), mRooSecSigmaSgn->getVal(), mRooSecSigmaSgn->getError())); } mInvMassFrame->addObject(textSignalPar); } mInvMassFrame->GetXaxis()->SetTitleOffset(1.2); mInvMassFrame->GetYaxis()->SetTitleOffset(1.8); gPad->SetLeftMargin(0.15); + gPad->SetRightMargin(0.02); mInvMassFrame->GetYaxis()->SetTitle(Form("%s", mHistoInvMass->GetYaxis()->GetTitle())); mInvMassFrame->GetXaxis()->SetTitle(Form("%s", mHistoInvMass->GetXaxis()->GetTitle())); mInvMassFrame->Draw(); @@ -566,20 +639,27 @@ void HFInvMassFitter::drawFit(TVirtualPad* pad, const std::vector& // draw residual distribution on canvas void HFInvMassFitter::drawResidual(TVirtualPad* pad) { + if (mResidualFrame == nullptr) { + printf("Warning HFInvMassFitter::drawResidual(): mResidualFrame == nullptr and will not be drawn\n"); + return; + } pad->cd(); - mResidualFrame->GetYaxis()->SetTitle(""); + mResidualFrame->GetYaxis()->SetTitle(mHistoInvMass->GetYaxis()->GetTitle()); auto* textInfo = new TPaveText(0.12, 0.65, 0.47, .89, "NDC"); textInfo->SetBorderSize(0); textInfo->SetFillStyle(0); textInfo->SetTextColor(kBlue); textInfo->AddText(Form("S = %.0f #pm %.0f ", mRawYield, mRawYieldErr)); textInfo->AddText(Form("S_{count} = %.0f #pm %.0f ", mRawYieldCounted, mRawYieldCountedErr)); - textInfo->AddText(Form("mean = %.3f #pm %.3f", mRooMeanSgn->getVal(), mRooMeanSgn->getError())); - textInfo->AddText(Form("sigma = %.3f #pm %.3f", mRooSigmaSgn->getVal(), mRooSigmaSgn->getError())); + textInfo->AddText(Form("#mu = %.3f #pm %.3f", mRooMeanSgn->getVal(), mRooMeanSgn->getError())); + textInfo->AddText(Form("#sigma = %.3f #pm %.3f", mRooSigmaSgn->getVal(), mRooSigmaSgn->getError())); if (mTypeOfSgnPdf == DoubleGaus) { - textInfo->AddText(Form("sigma 2 = %.3f #pm %.3f", mRooSecSigmaSgn->getVal(), mRooSecSigmaSgn->getError())); + textInfo->AddText(Form("#sigma_{2} = %.3f #pm %.3f", mRooSecSigmaSgn->getVal(), mRooSecSigmaSgn->getError())); } mResidualFrame->addObject(textInfo); + gPad->SetLeftMargin(0.15); + gPad->SetRightMargin(0.02); + mResidualFrame->GetYaxis()->SetTitleOffset(1.8); mResidualFrame->Draw(); highlightPeakRegion(mResidualFrame); } @@ -587,6 +667,10 @@ void HFInvMassFitter::drawResidual(TVirtualPad* pad) // draw ratio on canvas void HFInvMassFitter::drawRatio(TVirtualPad* pad) { + if (mRatioFrame == nullptr) { + printf("Warning HFInvMassFitter::drawRatio(): mRatioFrame == nullptr and will not be drawn\n"); + return; + } pad->cd(); mRatioFrame->GetXaxis()->SetTitleOffset(1.2); mRatioFrame->GetYaxis()->SetTitleOffset(1.5); @@ -598,6 +682,8 @@ void HFInvMassFitter::drawRatio(TVirtualPad* pad) line->SetLineStyle(2); line->SetLineWidth(2); mRatioFrame->addObject(line); + gPad->SetLeftMargin(0.15); + gPad->SetRightMargin(0.02); mRatioFrame->Draw(); highlightPeakRegion(mRatioFrame); } @@ -633,31 +719,34 @@ void HFInvMassFitter::drawReflection(TVirtualPad* pad) } // calculate signal yield via bin counting -void HFInvMassFitter::countSignal(double& signal, double& signalErr) const +void HFInvMassFitter::countSignal(double& signal, double& errSignal) const { - const double mean = mRooMeanSgn->getVal(); - const double sigma = mRooSecSigmaSgn->getVal(); - const double minForSgn = mean - mNSigmaForSidebands * sigma; - const double maxForSgn = mean + mNSigmaForSidebands * sigma; - const int binForMinSgn = mHistoInvMass->FindBin(minForSgn); - const int binForMaxSgn = mHistoInvMass->FindBin(maxForSgn); - const double binForMinSgnUpperEdge = mHistoInvMass->GetBinLowEdge(binForMinSgn + 1); - const double binForMaxSgnLowerEdge = mHistoInvMass->GetBinLowEdge(binForMaxSgn); - const double binForMinSgnFraction = (binForMinSgnUpperEdge - minForSgn) / mHistoInvMass->GetBinWidth(binForMinSgn); - const double binForMaxSgnFraction = (maxForSgn - binForMaxSgnLowerEdge) / mHistoInvMass->GetBinWidth(binForMaxSgn); - - double sum = 0; - sum += mHistoInvMass->GetBinContent(binForMinSgn) * binForMinSgnFraction; + const auto& histoForCounting = mTypeOfBkgPdf == NoBkg ? mInvMassFrame->getHist("data_c") : mResidualHist; + + const double binWidth = histoForCounting->GetX()[1] - histoForCounting->GetX()[0]; + const double firstBinLowEdge = histoForCounting->GetX()[0] - binWidth / 2; + const auto [minForSgn, maxForSgn] = getRangesOfSignal(); + const int binForMinSgn = static_cast((minForSgn - firstBinLowEdge) / binWidth) + 1; + const int binForMaxSgn = static_cast((maxForSgn - firstBinLowEdge) / binWidth) + 1; + const double binForMinSgnUpperEdge = firstBinLowEdge + (binForMinSgn - 1) * binWidth + binWidth; + const double binForMaxSgnLowerEdge = firstBinLowEdge + (binForMaxSgn - 1) * binWidth; + const double binForMinSgnFraction = (binForMinSgnUpperEdge - minForSgn) / binWidth; + const double binForMaxSgnFraction = (maxForSgn - binForMaxSgnLowerEdge) / binWidth; + + auto square = [](double value) { return value * value; }; + + double sumValues{}, sumErrorsSquare{}; + sumValues += histoForCounting->GetY()[binForMinSgn - 1] * binForMinSgnFraction; + sumErrorsSquare += square(histoForCounting->GetErrorY(binForMinSgn - 1) * binForMinSgnFraction); for (int iBin = binForMinSgn + 1; iBin <= binForMaxSgn - 1; iBin++) { - sum += mHistoInvMass->GetBinContent(iBin); + sumValues += histoForCounting->GetY()[iBin - 1]; + sumErrorsSquare += square(histoForCounting->GetErrorY(iBin - 1)); } - sum += mHistoInvMass->GetBinContent(binForMaxSgn) * binForMaxSgnFraction; - - double bkg{}, errBkg{}; - calculateBackground(bkg, errBkg); + sumValues += histoForCounting->GetY()[binForMaxSgn - 1] * binForMaxSgnFraction; + sumErrorsSquare += square(histoForCounting->GetErrorY(binForMaxSgn - 1) * binForMaxSgnFraction); - signal = sum - bkg; - signalErr = std::sqrt(sum + errBkg * errBkg); // sum error squared is equal to sum + signal = sumValues; + errSignal = std::sqrt(sumErrorsSquare); } // calculate signal yield @@ -670,8 +759,14 @@ void HFInvMassFitter::calculateSignal(double& signal, double& errSignal) const // calculate background yield void HFInvMassFitter::calculateBackground(double& bkg, double& errBkg) const { - bkg = mRooNBkg->getVal() * mIntegralBkg; - errBkg = mRooNBkg->getError() * mIntegralBkg; + if (mTypeOfBkgPdf == NoBkg) { + bkg = 0.; + errBkg = 0.; + return; + } + const double bgCoefficient = mTypeOfSgnPdf == DoubleSidedCrystalBall ? 1. : mIntegralBkg; + bkg = mRooNBkg->getVal() * bgCoefficient; + errBkg = mRooNBkg->getError() * bgCoefficient; } // calculate significance @@ -691,18 +786,21 @@ void HFInvMassFitter::calculateSignificance(double& significance, double& errSig // estimate Signal void HFInvMassFitter::checkForSignal(double& estimatedSignal) { - double const minForSgn = mMass - 4 * mSigmaSgn; - double const maxForSgn = mMass + 4 * mSigmaSgn; - int const binForMinSgn = mHistoInvMass->FindBin(minForSgn); - int const binForMaxSgn = mHistoInvMass->FindBin(maxForSgn); + const double integralHisto = integrateHistoInvMassOverWorkspaceRanges({"full"}); + const double bkg = mRooNBkg->getVal(); + estimatedSignal = integralHisto - bkg; +} - double sum = 0; - for (int i = binForMinSgn; i <= binForMaxSgn; i++) { - sum += mHistoInvMass->GetBinContent(i); +// Estimate ranges where signal is located to be used in countSignal() and checkForSignal() +// It is mu +- n*sigma for Gaussian, and the entire mInv histogram for DoubleSidedCrystalBall (due to heavy tails) +std::pair HFInvMassFitter::getRangesOfSignal() const +{ + if (mTypeOfSgnPdf == DoubleSidedCrystalBall) { + return std::make_pair(mMinMass, mMaxMass); } - double bkg{}, errBkg{}; - calculateBackground(bkg, errBkg); - estimatedSignal = sum - bkg; + const double mean = mRooMeanSgn->getVal(); + const double sigma = mRooSecSigmaSgn->getVal(); + return std::make_pair(mean - mNSigmaForSgn * sigma, mean + mNSigmaForSgn * sigma); } // Create Background Fit Function @@ -751,6 +849,16 @@ RooAbsPdf* HFInvMassFitter::createSignalFitFunction(RooWorkspace* workspace) mRooSecSigmaSgn = workspace->var("sigmaSec"); mRooMeanSgn = workspace->var("meanSec"); } break; + case DoubleSidedCrystalBall: { + sgnPdf = workspace->pdf("sgnFuncDSCB"); + mRooSigmaSgn = workspace->var("sigma"); + mRooSecSigmaSgn = workspace->var("sigma"); + mRooMeanSgn = workspace->var("mean"); + mRooDscbAlphaL = workspace->var("alphaL"); + mRooDscbNL = workspace->var("nL"); + mRooDscbAlphaR = workspace->var("alphaR"); + mRooDscbNR = workspace->var("nR"); + } break; default: break; } @@ -804,7 +912,7 @@ void HFInvMassFitter::calculateFitToDataRatio() const return; } - RooHist* ratioHist = new RooHist(); + auto* ratioHist = new RooHist(); for (int i = 0; i < dataHist->GetN(); ++i) { double x{}, dataY{}, dataErr{}; @@ -991,3 +1099,130 @@ void HFInvMassFitter::setTemplateReflections(TH1* histoRefl) mHistoTemplateRefl = histoRefl; mHistoTemplateRefl->SetName("mHistoTemplateRefl"); } + +void HFInvMassFitter::setFixDscbAlphaL(double alphaL) +{ + if (mTypeOfSgnPdf != DoubleSidedCrystalBall) { + printf("Fit type should be DSCB!\n"); + } + mFixedDscbTailParams = kTRUE; + mDscbAlphaLInitialValue = alphaL; +} +void HFInvMassFitter::setFixDscbAlphaR(double alphaR) +{ + if (mTypeOfSgnPdf != DoubleSidedCrystalBall) { + printf("Fit type should be DSCB!\n"); + } + mFixedDscbTailParams = kTRUE; + mDscbAlphaRInitialValue = alphaR; +} +void HFInvMassFitter::setFixDscbNL(double nL) +{ + if (mTypeOfSgnPdf != DoubleSidedCrystalBall) { + printf("Fit type should be DSCB!\n"); + } + mFixedDscbTailParams = kTRUE; + mDscbNLInitialValue = nL; +} +void HFInvMassFitter::setFixDscbNR(double nR) +{ + if (mTypeOfSgnPdf != DoubleSidedCrystalBall) { + printf("Fit type should be DSCB!\n"); + } + mFixedDscbTailParams = kTRUE; + mDscbNRInitialValue = nR; +} + +double HFInvMassFitter::randomizeInitialParameter(const ParameterRanges& parameterRanges) const +{ + constexpr double DefaultSigmaFraction{10.}; + constexpr int MaximalNumberOfIterations{20}; + + if (mRandomSeed < 0) { + return parameterRanges.initial; + } + const auto sigma = parameterRanges.sigma < 0 ? (parameterRanges.upper - parameterRanges.lower) / DefaultSigmaFraction : parameterRanges.sigma; + + double result{}; + int nIter{0}; + do { + result = mRandomGen->Gaus(parameterRanges.initial, sigma); + ++nIter; + if (nIter > MaximalNumberOfIterations) { + std::array errorMessage{}; + std::snprintf(errorMessage.data(), errorMessage.size(), "randomizeInitialParameter() - long while loop with lower = %f upper = %f initial = %f sigma = %f\n", parameterRanges.lower, parameterRanges.upper, parameterRanges.initial, sigma); + throw std::runtime_error(errorMessage.data()); + } + } while (result < parameterRanges.lower || result > parameterRanges.upper); + + return result; +} + +double HFInvMassFitter::integrateHistoInvMassOverWorkspaceRanges(const std::vector& ranges) const +{ + double sumEntries{0.}; + double sumLengths{0.}; + for (const auto& range : ranges) { + const auto [lo, hi] = mWorkspace->var("mass")->getRange(range.c_str()); + const auto binLo = mHistoInvMass->FindBin(lo); + const auto binHi = mHistoInvMass->FindBin(hi); + sumEntries += mHistoInvMass->Integral(binLo + 1, binHi - 1); + sumEntries += mHistoInvMass->GetBinContent(binLo) * (mHistoInvMass->GetBinLowEdge(binLo + 1) - lo) / mHistoInvMass->GetBinWidth(binLo); + sumEntries += mHistoInvMass->GetBinContent(binHi) * (hi - mHistoInvMass->GetBinLowEdge(binHi)) / mHistoInvMass->GetBinWidth(binHi); + sumLengths += (hi - lo); + } + const auto [fullLo, fullHi] = mWorkspace->var("mass")->getRange("full"); + const double fullLength = fullHi - fullLo; + + return sumEntries / sumLengths * fullLength; +} + +void HFInvMassFitter::cutRangesFromHisto(TH1* histo, const std::vector& ranges) const +{ + auto* mass = mWorkspace->var("mass"); + + for (int iBin = 1, nBins = histo->GetNbinsX(); iBin <= nBins; ++iBin) { + const double binLow = histo->GetBinLowEdge(iBin); + const double binHigh = binLow + histo->GetBinWidth(iBin); + + bool overlapsAnyRange = false; + for (const auto& range : ranges) { + const double rangeMin = mass->getMin(range.c_str()); + const double rangeMax = mass->getMax(range.c_str()); + if (std::max(binLow, rangeMin) <= std::min(binHigh, rangeMax)) { + overlapsAnyRange = true; + break; + } + } + + if (!overlapsAnyRange) { + histo->SetBinContent(iBin, 0.); + histo->SetBinError(iBin, 1.e9); + } + } +} + +TH2* HFInvMassFitter::fillCovCorrMatrix(const RooFitResult* fitResult) +{ + const RooArgList& pars = fitResult->floatParsFinal(); + const int nPars = static_cast(pars.size()); + + TH2* hMatrix = new TH2D("covCorrMatrix", "covariance (upper left + diagonal) and correlation (lower right) matrix", nPars, 0, nPars, nPars, 0, nPars); + for (int iPar = 0; iPar < nPars; ++iPar) { + hMatrix->GetXaxis()->SetBinLabel(iPar + 1, pars[iPar].GetName()); + hMatrix->GetYaxis()->SetBinLabel(iPar + 1, pars[iPar].GetName()); + } + + const TMatrixDSym& covMatrix = fitResult->covarianceMatrix(); + const TMatrixDSym& corrMatrix = fitResult->correlationMatrix(); + + for (int iPar = 0; iPar < nPars; ++iPar) { + hMatrix->SetBinContent(iPar + 1, iPar + 1, covMatrix(iPar, iPar)); + for (int jPar = iPar + 1; jPar < nPars; ++jPar) { + hMatrix->SetBinContent(iPar + 1, jPar + 1, covMatrix(iPar, jPar)); + hMatrix->SetBinContent(jPar + 1, iPar + 1, corrMatrix(iPar, jPar)); + } + } + + return hMatrix; +} diff --git a/PWGHF/D2H/Macros/HFInvMassFitter.h b/PWGHF/D2H/Macros/HFInvMassFitter.h index 28ee858bba6..be5c95be829 100644 --- a/PWGHF/D2H/Macros/HFInvMassFitter.h +++ b/PWGHF/D2H/Macros/HFInvMassFitter.h @@ -22,12 +22,17 @@ #ifndef PWGHF_D2H_MACROS_HFINVMASSFITTER_H_ #define PWGHF_D2H_MACROS_HFINVMASSFITTER_H_ +#include +#include +#include #include #include #include #include #include +#include #include +#include #include #include @@ -35,47 +40,57 @@ #include #include +#include #include class HFInvMassFitter : public TNamed { + private: + struct ParameterRanges { + double lower{}; + double upper{}; + double initial{}; + double sigma{-999.}; + }; + public: enum TypeOfBkgPdf { Expo = 0, - Poly1 = 1, - Poly2 = 2, - Pow = 3, - PowExpo = 4, - Poly3 = 5, - NoBkg = 6, + Poly1, // 1 + Poly2, // 2 + Pow, // 3 + PowExpo, // 4 + Poly3, // 5 + NoBkg, // 6 NTypesOfBkgPdf }; std::array namesOfBkgPdf{"bkgFuncExpo", "bkgFuncPoly1", "bkgFuncPoly2", "bkgFuncPow", "bkgFuncPowExpo", "bkgFuncPoly3"}; enum TypeOfSgnPdf { SingleGaus = 0, - DoubleGaus = 1, - DoubleGausSigmaRatioPar = 2, - GausSec = 3, + DoubleGaus, // 1 + DoubleGausSigmaRatioPar, // 2 + GausSec, // 3 + DoubleSidedCrystalBall, // 4 NTypesOfSgnPdf }; enum TypeOfReflPdf { SingleGausRefl = 0, - DoubleGausRefl = 1, - Poly3Refl = 2, - Poly6Refl = 3, + DoubleGausRefl, // 1 + Poly3Refl, // 2 + Poly6Refl, // 3 NTypesOfReflPdf }; std::array namesOfReflPdf{"reflFuncGaus", "reflFuncDoubleGaus", "reflFuncPoly3", "reflFuncPoly6"}; HFInvMassFitter() = delete; - HFInvMassFitter(TH1* histoToFit, double minValue, double maxValue, int fitTypeBkg = Expo, int fitTypeSgn = SingleGaus); + HFInvMassFitter(TH1* histoToFit, double minValue, double maxValue, int fitTypeBkg = Expo, int fitTypeSgn = SingleGaus, int randomSeed = -1); ~HFInvMassFitter() override; void setHistogramForFit(TH1* histoToFit); void setUseLikelihoodFit() { mFitOption = "L,E"; } void setUseChi2Fit() { mFitOption = "Chi2"; } void setFitOption(const std::string& opt) { mFitOption = opt; } - RooAbsPdf* createBackgroundFitFunction(RooWorkspace* w1) const; - RooAbsPdf* createSignalFitFunction(RooWorkspace* w1); - RooAbsPdf* createReflectionFitFunction(RooWorkspace* w1) const; + RooAbsPdf* createBackgroundFitFunction(RooWorkspace* workspace) const; + RooAbsPdf* createSignalFitFunction(RooWorkspace* workspace); + RooAbsPdf* createReflectionFitFunction(RooWorkspace* workspace) const; void setFitRange(double minValue, double maxValue); void setFitFunctions(int fitTypeBkg, int fitTypeSgn); @@ -97,8 +112,25 @@ class HFInvMassFitter : public TNamed void setFixRatioToGausSigma(double sigmaFrac); void setFixSignalYield(double yield) { mFixedRawYield = yield; } void setNumberOfSigmaForSidebands(double numberOfSigma) { mNSigmaForSidebands = numberOfSigma; } - void plotBkg(RooAbsPdf* mFunc, Color_t color = kRed); - void plotRefl(RooAbsPdf* mFunc); + void setNumberOfSigmaForSignal(double numberOfSigma) { mNSigmaForSgn = numberOfSigma; } + void setFixDscbAlphaL(double alphaL); + void setFixDscbAlphaR(double alphaR); + void setFixDscbNL(double nL); + void setFixDscbNR(double nR); + void setDscbAlphaLInitialValue(double value) { mDscbAlphaLInitialValue = value; } + void setDscbAlphaLLowLimit(double value) { mDscbAlphaLLowLimit = value; } + void setDscbAlphaLUpLimit(double value) { mDscbAlphaLUpLimit = value; } + void setDscbAlphaRInitialValue(double value) { mDscbAlphaRInitialValue = value; } + void setDscbAlphaRLowLimit(double value) { mDscbAlphaRLowLimit = value; } + void setDscbAlphaRUpLimit(double value) { mDscbAlphaRUpLimit = value; } + void setDscbNLInitialValue(double value) { mDscbNLInitialValue = value; } + void setDscbNLLowLimit(double value) { mDscbNLLowLimit = value; } + void setDscbNLUpLimit(double value) { mDscbNLUpLimit = value; } + void setDscbNRInitialValue(double value) { mDscbNRInitialValue = value; } + void setDscbNRLowLimit(double value) { mDscbNRLowLimit = value; } + void setDscbNRUpLimit(double value) { mDscbNRUpLimit = value; } + void plotBkg(RooAbsPdf* pdf, Color_t color = kRed); + void plotRefl(RooAbsPdf* pdf); void setReflFuncFixed(); void doFit(); void setInitialReflOverSgn(double reflOverSgn) { mReflOverSgn = reflOverSgn; } @@ -120,27 +152,46 @@ class HFInvMassFitter : public TNamed [[nodiscard]] double getMeanUncertainty() const { return mRooMeanSgn->getError(); } [[nodiscard]] double getSigma() const { return mRooSigmaSgn->getVal(); } [[nodiscard]] double getSigmaUncertainty() const { return mRooSigmaSgn->getError(); } + [[nodiscard]] double getDscbAlphaL() const { return mRooDscbAlphaL ? mRooDscbAlphaL->getVal() : 0.; } + [[nodiscard]] double getDscbAlphaR() const { return mRooDscbAlphaR ? mRooDscbAlphaR->getVal() : 0.; } + [[nodiscard]] double getDscbNL() const { return mRooDscbNL ? mRooDscbNL->getVal() : 0.; } + [[nodiscard]] double getDscbNR() const { return mRooDscbNR ? mRooDscbNR->getVal() : 0.; } + [[nodiscard]] double getDscbAlphaLUncertainty() const { return mRooDscbAlphaL ? mRooDscbAlphaL->getError() : 0.; } + [[nodiscard]] double getDscbAlphaRUncertainty() const { return mRooDscbAlphaR ? mRooDscbAlphaR->getError() : 0.; } + [[nodiscard]] double getDscbNLUncertainty() const { return mRooDscbNL ? mRooDscbNL->getError() : 0.; } + [[nodiscard]] double getDscbNRUncertainty() const { return mRooDscbNR ? mRooDscbNR->getError() : 0.; } [[nodiscard]] double getSecSigma() const { return mRooSecSigmaSgn->getVal(); } [[nodiscard]] double getSecSigmaUncertainty() const { return mRooSecSigmaSgn->getError(); } [[nodiscard]] double getFracDoubleGaus() const { return mRooFracDoubleGaus->getVal(); } [[nodiscard]] double getFracDoubleGausUncertainty() const { return mRooFracDoubleGaus->getError(); } [[nodiscard]] double getReflOverSig() const { return mReflPdf != nullptr ? mReflOverSgn : 0.; } - void calculateSignal(double& signal, double& signalErr) const; - void countSignal(double& signal, double& signalErr) const; - void calculateBackground(double& bkg, double& bkgErr) const; - void calculateSignificance(double& significance, double& significanceErr) const; + [[nodiscard]] int getFitStatus() const { return mFitStatus; } + [[nodiscard]] int getCovQual() const { return mCovQual; } + [[nodiscard]] double getEDM() const { return mEdm; } + [[nodiscard]] double getMinNll() const { return mMinNll; } + [[nodiscard]] double getSgnGlobalCorrelCoeff() const { return mSgnGlobalCorrelCoeff; } + [[nodiscard]] TH2* getCovCorrMatrix() const { return mCovCorrMatrix; } + void calculateSignal(double& signal, double& errSignal) const; + void countSignal(double& signal, double& errSignal) const; + void calculateBackground(double& bkg, double& errBkg) const; + void calculateSignificance(double& significance, double& errSignificance) const; void checkForSignal(double& estimatedSignal); void calculateFitToDataRatio() const; - void drawFit(TVirtualPad* c, const std::vector& plotLabels, bool writeParInfo = true); - void drawResidual(TVirtualPad* c); - void drawRatio(TVirtualPad* c); - void drawReflection(TVirtualPad* c); + void drawFit(TVirtualPad* pad, const std::vector& plotLabels, bool writeParInfo = true); + void drawResidual(TVirtualPad* pad); + void drawRatio(TVirtualPad* pad); + void drawReflection(TVirtualPad* pad); private: HFInvMassFitter(const HFInvMassFitter& source); HFInvMassFitter& operator=(const HFInvMassFitter& source); void fillWorkspace(RooWorkspace& w) const; void highlightPeakRegion(const RooPlot* plot, Color_t color = kGray + 1, Width_t width = 1, Style_t style = 2) const; + [[nodiscard]] double randomizeInitialParameter(const ParameterRanges& parameterRanges) const; + [[nodiscard]] std::pair getRangesOfSignal() const; + [[nodiscard]] double integrateHistoInvMassOverWorkspaceRanges(const std::vector& ranges) const; + void cutRangesFromHisto(TH1* histo, const std::vector& ranges) const; + static TH2* fillCovCorrMatrix(const RooFitResult* fitResult); TH1* mHistoInvMass; // histogram to fit std::string mFitOption; @@ -168,6 +219,7 @@ class HFInvMassFitter : public TNamed bool mFixedSigma; /// fix sigma or not bool mFixedSigmaDoubleGaus; /// fix sigma of 2gaussian or not bool mBoundSigma; /// set bound sigma or not + bool mFixedDscbTailParams; /// switch for fix double sided Crystal Ball tail parameters double mSigmaValue; /// value of sigma double mParamSgn; /// +/- range variation of bound Sigma of gaussian in % double mFracDoubleGaus; /// initialization for fraction of 2nd gaussian in case of k2Gaus or k2GausSigmaRatioPar @@ -188,6 +240,18 @@ class HFInvMassFitter : public TNamed double mChiSquareOverNdfTotal; /// chi2/ndf of the total fit double mChiSquareOverNdfBkg; /// chi2/ndf of the background (sidebands) pre-fit bool mFixReflOverSgn; /// switch for fix refl/signal + double mDscbAlphaLInitialValue; /// double sided Crystal Ball alpha left initial value + double mDscbAlphaLLowLimit; /// double sided Crystal Ball alpha left lower limit + double mDscbAlphaLUpLimit; /// double sided Crystal Ball alpha left upper limit + double mDscbAlphaRInitialValue; /// double sided Crystal Ball alpha right initial value + double mDscbAlphaRLowLimit; /// double sided Crystal Ball alpha right lower limit + double mDscbAlphaRUpLimit; /// double sided Crystal Ball alpha right upper limit + double mDscbNLInitialValue; /// double sided Crystal Ball n left initial value + double mDscbNLLowLimit; /// double sided Crystal Ball n left lower limit + double mDscbNLUpLimit; /// double sided Crystal Ball n left upper limit + double mDscbNRInitialValue; /// double sided Crystal Ball n right initial value + double mDscbNRLowLimit; /// double sided Crystal Ball n right lower limit + double mDscbNRUpLimit; /// double sided Crystal Ball n right upper limit RooRealVar* mRooMeanSgn; /// mean for gaussian of signal RooRealVar* mRooSigmaSgn; /// sigma for gaussian of signal RooRealVar* mRooSecSigmaSgn; /// second sigma for composite gaussian of signal @@ -198,20 +262,31 @@ class HFInvMassFitter : public TNamed RooRealVar* mRooNSgn; /// total Signal fit function integral RooRealVar* mRooNBkg; /// total background fit function integral RooRealVar* mRooNRefl; /// total reflection fit function integral + RooRealVar* mRooDscbAlphaL; /// double sided Crystal Ball alpha left + RooRealVar* mRooDscbAlphaR; /// double sided Crystal Ball alpha right + RooRealVar* mRooDscbNL; /// double sided Crystal Ball n left + RooRealVar* mRooDscbNR; /// double sided Crystal Ball n right RooAbsPdf* mTotalPdf; /// total fit function RooPlot* mInvMassFrame; /// frame of mass RooPlot* mReflFrame; /// reflection frame RooPlot* mReflOnlyFrame; /// reflection frame plot on reflection only RooPlot* mResidualFrame; /// residual frame + RooHist* mResidualHist; /// residual histogram RooPlot* mRatioFrame; /// fit/data ratio frame - RooPlot* mResidualFrameForCalculation; - RooWorkspace* mWorkspace; /// workspace - double mIntegralHisto; /// integral of histogram to fit - double mIntegralBkg; /// integral of background fit function - double mIntegralSgn; /// integral of signal fit function - TH1* mHistoTemplateRefl; /// reflection histogram - bool mDrawBgPrefit; /// draw background after fitting the sidebands - bool mHighlightPeakRegion; /// draw vertical lines showing the peak region (usually +- 3 sigma) + RooWorkspace* mWorkspace; /// workspace + double mIntegralBkg; /// integral of background fit function + double mIntegralSgn; /// integral of signal fit function + TH1* mHistoTemplateRefl; /// reflection histogram + bool mDrawBgPrefit; /// draw background after fitting the sidebands + bool mHighlightPeakRegion; /// draw vertical lines showing the peak region (usually +- 3 sigma) + int mRandomSeed; /// seed for random engine for fit's initial parameters randomization + TRandom3* mRandomGen; /// engine for fit's initial parameters randomization + int mFitStatus; /// fit result status, see https://root-forum.cern.ch/t/meaning-of-values-returned-by-roofitresult-status/16355/2 + int mCovQual; /// fit result covariance matrix quality, see https://root.cern.ch/doc/v620/Minuit2Minimizer_8cxx_source.html#l01121 + double mEdm; /// fit quality metrics: Estimated Distance to Minimum + double mMinNll; /// fit quality metrics: minimum negative log-likelihood (NLL) value achieved at the best-fit parameter values + double mSgnGlobalCorrelCoeff; /// global correlation coefficient of mRooNSgn with other fit parameters + TH2* mCovCorrMatrix; /// covariance (upper left + diagonal) and correlation (lower right) matrix of free fit parameters ClassDefOverride(HFInvMassFitter, 1); }; diff --git a/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h b/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h index cc755de478f..f5ddd506b55 100644 --- a/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h +++ b/PWGHF/D2H/Macros/HFInvMassFitterLinkDef.h @@ -18,8 +18,7 @@ /// \author Biao Zhang /// \author Oleksii Lubynets -#ifndef PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ -#define PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ +// NOLINT(build/header_guard) #ifdef __CINT__ #pragma link off all globals; @@ -27,5 +26,3 @@ #pragma link off all functions; #pragma link C++ class HFInvMassFitter + ; #endif - -#endif // PWGHF_D2H_MACROS_HFINVMASSFITTERLINKDEF_H_ diff --git a/PWGHF/D2H/Macros/compute_fraction_cutvar.py b/PWGHF/D2H/Macros/compute_fraction_cutvar.py index 0099185472b..d3b3b87111d 100644 --- a/PWGHF/D2H/Macros/compute_fraction_cutvar.py +++ b/PWGHF/D2H/Macros/compute_fraction_cutvar.py @@ -11,16 +11,42 @@ import json import os import sys +from enum import IntEnum, auto import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error -sys.path.insert(0, '..') -from cut_variation import CutVarMinimiser + +sys.path.insert(0, "..") +from cut_variation import CutVarMinimiser, MinimisationStatus from style_formatter import set_object_style # pylint: disable=no-member,too-many-locals,too-many-statements +class PlotType(IntEnum): + Rawy = 0 + Eff = auto() + Frac = auto() + Cov = auto() + Unc = auto() + RelUnc = auto() + N = auto() + + +class ObjectToSave(IntEnum): + Canvas = 0 + RawYield = auto() + Uncertainty = auto() + Efficiency = auto() + Fraction = auto() + CorrectedYield = auto() + CorrelationMatrix = auto() + Covariance = auto() + CorrectedFraction = auto() + MinimisationStatus = auto() + N = auto() + + def main(config): """ Main function @@ -32,13 +58,17 @@ def main(config): with open(config, encoding="utf8") as fil: cfg = json.load(fil) + zero_eff_unc = cfg.get("zero_eff_unc", False) + effp_shift_nsigma = cfg.get("effp_shift_nsigma", 0.0) + effnp_shift_nsigma = cfg.get("effnp_shift_nsigma", 0.0) + hist_rawy, hist_effp, hist_effnp = ([] for _ in range(3)) for filename_rawy, filename_eff in zip(cfg["rawyields"]["inputfiles"], cfg["efficiencies"]["inputfiles"]): infile_rawy = ROOT.TFile.Open(os.path.join(cfg["rawyields"]["inputdir"], filename_rawy)) hist_rawy_name = cfg["rawyields"]["histoname"] hist_rawy.append(infile_rawy.Get(hist_rawy_name)) if not isinstance(hist_rawy[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with raw yield \"{hist_rawy_name}\" is absent. Exit.\33[0m") + sys.exit(f'\33[31mFatal error: Histogram with raw yield "{hist_rawy_name}" is absent. Exit.\33[0m') hist_rawy[-1].SetDirectory(0) infile_rawy.Close() @@ -48,30 +78,68 @@ def main(config): hist_effp.append(infile_eff.Get(hist_effp_name)) hist_effnp.append(infile_eff.Get(hist_effnp_name)) if not isinstance(hist_effp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with efficiency for prompt \"{hist_effp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with efficiency for prompt "{hist_effp_name}" is absent. Exit.\33[0m' + ) if not isinstance(hist_effnp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with efficiency for nonprompt \"{hist_effnp}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with efficiency for nonprompt "{hist_effnp}" is absent. Exit.\33[0m' + ) hist_effp[-1].SetDirectory(0) hist_effnp[-1].SetDirectory(0) + for i_bin in range(1, hist_effp[-1].GetNbinsX() + 1): + hist_effp[-1].SetBinContent( + i_bin, hist_effp[-1].GetBinContent(i_bin) + effp_shift_nsigma * hist_effp[-1].GetBinError(i_bin) + ) + hist_effnp[-1].SetBinContent( + i_bin, hist_effnp[-1].GetBinContent(i_bin) + effnp_shift_nsigma * hist_effnp[-1].GetBinError(i_bin) + ) + if zero_eff_unc: + hist_effp[-1].SetBinError(i_bin, 0.0) + hist_effnp[-1].SetBinError(i_bin, 0.0) infile_eff.Close() pt_bin_to_process = cfg.get("pt_bin_to_process", -1) if not isinstance(pt_bin_to_process, int): sys.exit("\33[31mFatal error: pt_bin_to_process must be an integer value. Exit.") if (pt_bin_to_process != -1 and pt_bin_to_process < 1) or pt_bin_to_process > hist_rawy[0].GetNbinsX(): - sys.exit("\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit.") - - is_draw_title_rawy = cfg.get("is_draw_title", {}).get("rawy", True) - is_draw_title_eff = cfg.get("is_draw_title", {}).get("eff", False) - is_draw_title_frac = cfg.get("is_draw_title", {}).get("frac", False) - is_draw_title_cov = cfg.get("is_draw_title", {}).get("cov", False) - is_draw_title_unc = cfg.get("is_draw_title", {}).get("unc", True) + sys.exit( + "\33[31mFatal error: pt_bin_to_process must be a positive value up to number of bins in raw yield histogram. Exit." + ) - is_save_canvas_as_macro_rawy = cfg.get("is_save_canvas_as_macro", {}).get("rawy", False) - is_save_canvas_as_macro_eff = cfg.get("is_save_canvas_as_macro", {}).get("eff", False) - is_save_canvas_as_macro_frac = cfg.get("is_save_canvas_as_macro", {}).get("frac", False) - is_save_canvas_as_macro_cov = cfg.get("is_save_canvas_as_macro", {}).get("cov", False) - is_save_canvas_as_macro_unc = cfg.get("is_save_canvas_as_macro", {}).get("unc", False) + is_draw_title = [False] * PlotType.N + is_draw_title[PlotType.Rawy] = cfg.get("is_draw_title", {}).get("rawy", True) + is_draw_title[PlotType.Eff] = cfg.get("is_draw_title", {}).get("eff", False) + is_draw_title[PlotType.Frac] = cfg.get("is_draw_title", {}).get("frac", False) + is_draw_title[PlotType.Cov] = cfg.get("is_draw_title", {}).get("cov", False) + is_draw_title[PlotType.Unc] = cfg.get("is_draw_title", {}).get("unc", True) + is_draw_title[PlotType.RelUnc] = cfg.get("is_draw_title", {}).get("relunc", True) + + is_save_canvas_as_macro = [False] * PlotType.N + is_save_canvas_as_macro[PlotType.Rawy] = cfg.get("is_save_canvas_as_macro", {}).get("rawy", False) + is_save_canvas_as_macro[PlotType.Eff] = cfg.get("is_save_canvas_as_macro", {}).get("eff", False) + is_save_canvas_as_macro[PlotType.Frac] = cfg.get("is_save_canvas_as_macro", {}).get("frac", False) + is_save_canvas_as_macro[PlotType.Cov] = cfg.get("is_save_canvas_as_macro", {}).get("cov", False) + is_save_canvas_as_macro[PlotType.Unc] = cfg.get("is_save_canvas_as_macro", {}).get("unc", False) + is_save_canvas_as_macro[PlotType.RelUnc] = cfg.get("is_save_canvas_as_macro", {}).get("relunc", False) + + is_save_to_root_file = [False] * ObjectToSave.N + is_save_to_root_file[ObjectToSave.Canvas] = cfg.get("is_save_to_root_file", {}).get("canvas", True) + is_save_to_root_file[ObjectToSave.RawYield] = cfg.get("is_save_to_root_file", {}).get("raw_yield", True) + is_save_to_root_file[ObjectToSave.Uncertainty] = cfg.get("is_save_to_root_file", {}).get("uncertainty", True) + is_save_to_root_file[ObjectToSave.Efficiency] = cfg.get("is_save_to_root_file", {}).get("efficiency", True) + is_save_to_root_file[ObjectToSave.Fraction] = cfg.get("is_save_to_root_file", {}).get("fraction", True) + is_save_to_root_file[ObjectToSave.CorrectedYield] = cfg.get("is_save_to_root_file", {}).get("corrected_yield", True) + is_save_to_root_file[ObjectToSave.CorrelationMatrix] = cfg.get("is_save_to_root_file", {}).get( + "correlation_matrix", True + ) + is_save_to_root_file[ObjectToSave.Covariance] = cfg.get("is_save_to_root_file", {}).get("covariance", True) + is_save_to_root_file[ObjectToSave.CorrectedFraction] = cfg.get("is_save_to_root_file", {}).get( + "corrected_fraction", True + ) + is_save_to_root_file[ObjectToSave.MinimisationStatus] = cfg.get("is_save_to_root_file", {}).get( + "minimisation_status", True + ) if cfg["central_efficiency"]["computerawfrac"]: infile_name = os.path.join(cfg["central_efficiency"]["inputdir"], cfg["central_efficiency"]["inputfile"]) @@ -79,11 +147,15 @@ def main(config): hist_central_effp_name = cfg["central_efficiency"]["histonames"]["prompt"] hist_central_effp = infile_central_eff.Get(hist_central_effp_name) if not isinstance(hist_central_effp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with central efficiency for prompt \"{hist_central_effp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with central efficiency for prompt "{hist_central_effp_name}" is absent. Exit.\33[0m' + ) hist_central_effnp_name = cfg["central_efficiency"]["histonames"]["nonprompt"] hist_central_effnp = infile_central_eff.Get(hist_central_effnp_name) if not isinstance(hist_central_effnp[-1], ROOT.TH1): - sys.exit(f"\33[31mFatal error: Histogram with central efficiency for nonprompt \"{hist_central_effnp_name}\" is absent. Exit.\33[0m") + sys.exit( + f'\33[31mFatal error: Histogram with central efficiency for nonprompt "{hist_central_effnp_name}" is absent. Exit.\33[0m' + ) hist_central_effp.SetDirectory(0) hist_central_effnp.SetDirectory(0) infile_central_eff.Close() @@ -100,7 +172,19 @@ def main(config): hist_covariance_npnp = hist_rawy[0].Clone("hCovNonPromptNonPrompt") hist_corrfrac_prompt = hist_rawy[0].Clone("hCorrFracPrompt") hist_corrfrac_nonprompt = hist_rawy[0].Clone("hCorrFracNonPrompt") - for histo in hist_corry_prompt, hist_corry_nonprompt, hist_covariance_pnp, hist_covariance_pp, hist_covariance_npnp, hist_corrfrac_prompt, hist_corrfrac_nonprompt: + hist_minimisation_status = hist_rawy[0].Clone("hMinimizationStatus") + hist_red_chi2 = hist_rawy[0].Clone("hChi2OverNdf") + for histo in ( + hist_corry_prompt, + hist_corry_nonprompt, + hist_covariance_pnp, + hist_covariance_pp, + hist_covariance_npnp, + hist_corrfrac_prompt, + hist_corrfrac_nonprompt, + hist_minimisation_status, + hist_red_chi2, + ): histo.Reset() hist_corry_prompt.GetYaxis().SetTitle("corrected yields prompt") hist_corry_nonprompt.GetYaxis().SetTitle("corrected yields non-prompt") @@ -109,6 +193,13 @@ def main(config): hist_covariance_npnp.GetYaxis().SetTitle("#sigma(non-prompt, non-prompt)") hist_corrfrac_prompt.GetYaxis().SetTitle("corrected fraction prompt") hist_corrfrac_nonprompt.GetYaxis().SetTitle("corrected fraction non-prompt") + hist_minimisation_status.GetYaxis().SetTitle("minimisation status") + hist_red_chi2.GetYaxis().SetTitle("#chi^{2}/ndf") + hist_minimisation_status_title = "" + for min_status in MinimisationStatus: + hist_minimisation_status_title += str(min_status.value) + " = " + min_status.name + ", " + hist_minimisation_status_title = hist_minimisation_status_title[:-2] + hist_minimisation_status.SetTitle(hist_minimisation_status_title) set_object_style( hist_corry_prompt, color=ROOT.kRed + 1, @@ -124,6 +215,8 @@ def main(config): set_object_style(hist_covariance_pnp) set_object_style(hist_covariance_pp) set_object_style(hist_covariance_npnp) + set_object_style(hist_minimisation_status) + set_object_style(hist_red_chi2) set_object_style( hist_corrfrac_prompt, color=ROOT.kRed + 1, @@ -161,16 +254,17 @@ def main(config): if pt_bin_to_process != -1: pt_bin_to_process_name_suffix = "_bin_" + str(pt_bin_to_process) - output_name_template = cfg['output']['file'].replace(".root", "") + pt_bin_to_process_name_suffix + ".root" + output_name_template = cfg["output"]["file"].replace(".root", "") + pt_bin_to_process_name_suffix + ".root" output = ROOT.TFile(os.path.join(cfg["output"]["directory"], output_name_template), "recreate") n_sets = len(hist_rawy) pt_axis_title = hist_rawy[0].GetXaxis().GetTitle() for ipt in range(hist_rawy[0].GetNbinsX()): - if pt_bin_to_process !=-1 and ipt+1 != pt_bin_to_process: + if pt_bin_to_process != -1 and ipt + 1 != pt_bin_to_process: continue + all_vectors_monotonous = MinimisationStatus.Success pt_min = hist_rawy[0].GetXaxis().GetBinLowEdge(ipt + 1) pt_max = hist_rawy[0].GetXaxis().GetBinUpEdge(ipt + 1) - print(f"\n\nINFO: processing pt range {ipt+1} from {pt_min} to {pt_max} {pt_axis_title}") + print(f"\n\nINFO: processing pt range {ipt + 1} from {pt_min} to {pt_max} {pt_axis_title}") rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp = (np.zeros(n_sets) for _ in range(6)) for iset, (hrawy, heffp, heffnp) in enumerate(zip(hist_rawy, hist_effp, hist_effnp)): @@ -183,16 +277,30 @@ def main(config): if cfg["minimisation"]["correlated"]: if not (np.all(rawy[1:] > rawy[:-1]) or np.all(rawy[1:] < rawy[:-1])): - print("\0\33[33mWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\0\33[0m") + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print( + "\0\33[33m\nWARNING! main(): the raw yield vector is not monotonous. Check the input for stability.\n\0\33[0m" + ) print(f"raw yield vector elements = {rawy}\n") if not (np.all(unc_rawy[1:] > unc_rawy[:-1]) or np.all(unc_rawy[1:] < unc_rawy[:-1])): - print("\0\33[33mWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\0\33[0m") + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print( + "\0\33[33m\nWARNING! main(): the raw yield uncertainties vector is not monotonous. Check the input for stability.\n\0\33[0m" + ) print(f"raw yield uncertainties vector elements = {unc_rawy}\n") + if not (np.all(effp[1:] > effp[:-1]) or np.all(effp[1:] < effp[:-1])): + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print("\0\33[33m\nWARNING! the prompt efficiency vector is not monotonous. Check the input.\0\33[0m") + print(f"prompt efficiency vector elements = {effp}\n") + if not (np.all(effnp[1:] > effnp[:-1]) or np.all(effnp[1:] < effnp[:-1])): + all_vectors_monotonous = MinimisationStatus.MonotonyViolation + print("\0\33[33m\nWARNING! the nonprompt efficiency vector is not monotonous. Check the input.\0\33[0m") + print(f"nonprompt efficiency vector elements = {effnp}\n") minimiser = CutVarMinimiser(rawy, effp, effnp, unc_rawy, unc_effp, unc_effnp) status = minimiser.minimise_system(cfg["minimisation"]["correlated"]) - if status: + if status != MinimisationStatus.Fail: hist_corry_prompt.SetBinContent(ipt + 1, minimiser.get_prompt_yield_and_error()[0]) hist_corry_prompt.SetBinError(ipt + 1, minimiser.get_prompt_yield_and_error()[1]) hist_corry_nonprompt.SetBinContent(ipt + 1, minimiser.get_nonprompt_yield_and_error()[0]) @@ -209,6 +317,8 @@ def main(config): hist_corrfrac_prompt.SetBinError(ipt + 1, corr_frac_prompt[1]) hist_corrfrac_nonprompt.SetBinContent(ipt + 1, corr_frac_nonprompt[0]) hist_corrfrac_nonprompt.SetBinError(ipt + 1, corr_frac_nonprompt[1]) + hist_minimisation_status.SetBinContent(ipt + 1, max(status, all_vectors_monotonous)) + hist_red_chi2.SetBinContent(ipt + 1, minimiser.get_red_chi2()) if cfg["central_efficiency"]["computerawfrac"]: raw_frac_prompt = minimiser.get_raw_prompt_fraction( hist_central_effp.GetBinContent(ipt + 1), hist_central_effnp.GetBinContent(ipt + 1) @@ -221,57 +331,85 @@ def main(config): hist_frac_raw_nonprompt.SetBinContent(ipt + 1, raw_frac_nonprompt[0]) hist_frac_raw_nonprompt.SetBinError(ipt + 1, raw_frac_nonprompt[1]) - hist_bin_title = f"bin # {ipt+1}; {pt_axis_title}#in ({pt_min}; {pt_max})" + hist_bin_title = f"bin # {ipt + 1}; {pt_axis_title}#in ({pt_min}; {pt_max})" - hist_bin_title_rawy = hist_bin_title if is_draw_title_rawy else "" - canv_rawy, histos_rawy, leg_r = minimiser.plot_result(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rawy) + hist_bin_title_rawy = hist_bin_title if is_draw_title[PlotType.Rawy] else "" + canv_rawy, histos_rawy, _leg_r = minimiser.plot_result(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rawy) output.cd() - canv_rawy.Write() - for _, hist in histos_rawy.items(): - hist.Write() - if (is_save_canvas_as_macro_rawy): - canv_rawy.SaveAs(f"canv_rawy_{ipt+1}.C") - - hist_bin_title_unc = hist_bin_title if is_draw_title_unc else "" - canv_unc, histos_unc, leg_unc = minimiser.plot_uncertainties(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc) + if is_save_to_root_file[ObjectToSave.Canvas]: + canv_rawy.Write() + if is_save_to_root_file[ObjectToSave.RawYield]: + for hist in histos_rawy.values(): + hist.Write() + if is_save_canvas_as_macro[PlotType.Rawy]: + canv_rawy.SaveAs(f"canv_rawy_{ipt + 1}.C") + + hist_bin_title_unc = hist_bin_title if is_draw_title[PlotType.Unc] else "" + canv_unc, histos_unc, _leg_unc = minimiser.plot_uncertainties( + f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_unc + ) output.cd() - canv_unc.Write() - for _, hist in histos_unc.items(): - hist.Write() - if (is_save_canvas_as_macro_unc): - canv_unc.SaveAs(f"canv_unc_{ipt+1}.C") - - hist_bin_title_eff = hist_bin_title if is_draw_title_eff else "" - canv_eff, histos_eff, leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff) + if is_save_to_root_file[ObjectToSave.Canvas]: + canv_unc.Write() + if is_save_to_root_file[ObjectToSave.Uncertainty]: + for hist in histos_unc.values(): + hist.Write() + if is_save_canvas_as_macro[PlotType.Unc]: + canv_unc.SaveAs(f"canv_unc_{ipt + 1}.C") + + hist_bin_title_rel_unc = hist_bin_title if is_draw_title[PlotType.RelUnc] else "" + canv_rel_unc, histos_rel_unc, _leg_rel_unc = minimiser.plot_relative_uncertainties( + f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_rel_unc + ) output.cd() - canv_eff.Write() - for _, hist in histos_eff.items(): - hist.Write() - if (is_save_canvas_as_macro_eff): - canv_eff.SaveAs(f"canv_eff_{ipt+1}.C") - - hist_bin_title_frac = hist_bin_title if is_draw_title_frac else "" - canv_frac, histos_frac, leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac) + if is_save_to_root_file[ObjectToSave.Canvas]: + canv_rel_unc.Write() + if is_save_to_root_file[ObjectToSave.Uncertainty]: + for hist in histos_rel_unc.values(): + hist.Write() + if is_save_canvas_as_macro[PlotType.RelUnc]: + canv_rel_unc.SaveAs(f"canv_rel_unc_{ipt + 1}.C") + + hist_bin_title_eff = hist_bin_title if is_draw_title[PlotType.Eff] else "" + canv_eff, histos_eff, _leg_e = minimiser.plot_efficiencies(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_eff) output.cd() - canv_frac.Write() - for _, hist in histos_frac.items(): - hist.Write() - if (is_save_canvas_as_macro_frac): - canv_frac.SaveAs(f"canv_frac_{ipt+1}.C") - - hist_bin_title_cov = hist_bin_title if is_draw_title_cov else "" + if is_save_to_root_file[ObjectToSave.Canvas]: + canv_eff.Write() + if is_save_to_root_file[ObjectToSave.Efficiency]: + for hist in histos_eff.values(): + hist.Write() + if is_save_canvas_as_macro[PlotType.Eff]: + canv_eff.SaveAs(f"canv_eff_{ipt + 1}.C") + + hist_bin_title_frac = hist_bin_title if is_draw_title[PlotType.Frac] else "" + canv_frac, histos_frac, _leg_f = minimiser.plot_fractions(f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_frac) + output.cd() + if is_save_to_root_file[ObjectToSave.Canvas]: + canv_frac.Write() + if is_save_to_root_file[ObjectToSave.Fraction]: + for hist in histos_frac.values(): + hist.Write() + if is_save_canvas_as_macro[PlotType.Frac]: + canv_frac.SaveAs(f"canv_frac_{ipt + 1}.C") + + hist_bin_title_cov = hist_bin_title if is_draw_title[PlotType.Cov] else "" canv_cov, histo_cov = minimiser.plot_cov_matrix(True, f"_pt_{pt_min}_to_{pt_max}", hist_bin_title_cov) output.cd() - canv_cov.Write() - histo_cov.Write() - if (is_save_canvas_as_macro_cov): - canv_cov.SaveAs(f"canv_cov_{ipt+1}.C") + if is_save_to_root_file[ObjectToSave.Canvas]: + canv_cov.Write() + if is_save_to_root_file[ObjectToSave.CorrelationMatrix]: + histo_cov.Write() + if is_save_canvas_as_macro[PlotType.Cov]: + canv_cov.SaveAs(f"canv_cov_{ipt + 1}.C") else: print(f"Minimization for pT {pt_min}, {pt_max} not successful") + hist_minimisation_status.SetBinContent(ipt + 1, MinimisationStatus.Fail) canv_rawy = ROOT.TCanvas("c_rawy_minimization_error", "Minimization error", 500, 500) canv_eff = ROOT.TCanvas("c_eff_minimization_error", "Minimization error", 500, 500) canv_frac = ROOT.TCanvas("c_frac_minimization_error", "Minimization error", 500, 500) canv_cov = ROOT.TCanvas("c_conv_minimization_error", "Minimization error", 500, 500) + canv_unc = ROOT.TCanvas("c_unc_minimization_error", "Minimization error", 500, 500) + canv_rel_unc = ROOT.TCanvas("c_rel_unc_minimization_error", "Minimization error", 500, 500) canv_combined = ROOT.TCanvas(f"canv_combined_{ipt}", "", 1000, 1000) canv_combined.Divide(2, 2) @@ -284,13 +422,14 @@ def main(config): canv_combined.cd(4) canv_cov.DrawClonePad() - output_name_template = output_name_template.replace('.root', '.pdf') + output_name_template = output_name_template.replace(".root", ".pdf") output_name_rawy_pdf = f"Distr_{output_name_template}" output_name_eff_pdf = f"Eff_{output_name_template}" output_name_frac_pdf = f"Frac_{output_name_template}" output_name_covmat_pdf = f"CovMatrix_{output_name_template}" output_name_unc_pdf = f"Unc_{output_name_template}" + output_name_rel_unc_pdf = f"RelUnc_{output_name_template}" output_name_pdf = f"{output_name_template}" if hist_rawy[0].GetNbinsX() == 1 or pt_bin_to_process != -1: @@ -307,15 +446,22 @@ def main(config): canv_cov.Print(f"{os.path.join(cfg['output']['directory'], output_name_covmat_pdf)}{print_bracket}") canv_combined.Print(f"{os.path.join(cfg['output']['directory'], output_name_pdf)}{print_bracket}") canv_unc.Print(f"{os.path.join(cfg['output']['directory'], output_name_unc_pdf)}{print_bracket}") + canv_rel_unc.Print(f"{os.path.join(cfg['output']['directory'], output_name_rel_unc_pdf)}{print_bracket}") output.cd() - hist_corry_prompt.Write() - hist_corry_nonprompt.Write() - hist_covariance_pnp.Write() - hist_covariance_pp.Write() - hist_covariance_npnp.Write() - hist_corrfrac_prompt.Write() - hist_corrfrac_nonprompt.Write() + if is_save_to_root_file[ObjectToSave.CorrectedYield]: + hist_corry_prompt.Write() + hist_corry_nonprompt.Write() + if is_save_to_root_file[ObjectToSave.Covariance]: + hist_covariance_pnp.Write() + hist_covariance_pp.Write() + hist_covariance_npnp.Write() + if is_save_to_root_file[ObjectToSave.CorrectedFraction]: + hist_corrfrac_prompt.Write() + hist_corrfrac_nonprompt.Write() + if is_save_to_root_file[ObjectToSave.MinimisationStatus]: + hist_minimisation_status.Write() + hist_red_chi2.Write() if cfg["central_efficiency"]["computerawfrac"]: hist_frac_raw_prompt.Write() hist_frac_raw_nonprompt.Write() diff --git a/PWGHF/D2H/Macros/config_cutvar_example.json b/PWGHF/D2H/Macros/config_cutvar_example.json index e1b768b807d..d645db1a6d6 100644 --- a/PWGHF/D2H/Macros/config_cutvar_example.json +++ b/PWGHF/D2H/Macros/config_cutvar_example.json @@ -62,14 +62,28 @@ "frac": false, "eff": false, "cov": false, - "unc": true + "unc": true, + "relunc": true }, "is_save_canvas_as_macro": { "rawy": false, "frac": false, "eff": false, "cov": false, - "unc": false + "unc": false, + "relunc": false + }, + "is_save_to_root_file": { + "canvas": true, + "raw_yield": true, + "uncertainty": true, + "efficiency": true, + "fraction": true, + "corrected_yield": true, + "correlation_matrix": true, + "covariance": true, + "corrected_fraction": true, + "minimisation_status": true }, "central_efficiency": { "computerawfrac": true, @@ -83,5 +97,8 @@ "output": { "directory": ".", "file": "CutVarDplus_pp13TeV_MB.root" - } + }, + "zero_eff_unc": false, + "effp_shift_nsigma": 0, + "effnp_shift_nsigma": 0 } diff --git a/PWGHF/D2H/Macros/config_massfitter.json b/PWGHF/D2H/Macros/config_massfitter.json index 25dc9d50bcc..0baf94cabad 100644 --- a/PWGHF/D2H/Macros/config_massfitter.json +++ b/PWGHF/D2H/Macros/config_massfitter.json @@ -101,21 +101,24 @@ 0.9, 1.6 ], - "MassMin": [ + "MassMin": 2.12, + "_MassMin": [ 2.12, 2.12, 2.12, 2.12, 2.12 ], - "MassMax": [ + "MassMax": 2.42, + "_MassMax": [ 2.42, 2.42, 2.42, 2.42, 2.42 ], - "Rebin": [ + "Rebin": 4, + "_Rebin": [ 4, 4, 4, @@ -128,7 +131,8 @@ "true: likelihood fit", "false: chi2 fit" ], - "BkgFunc": [ + "BkgFunc": 2, + "_BkgFunc": [ 2, 2, 2, @@ -144,7 +148,8 @@ "5 for Poly3", "6 for NoBkg" ], - "SgnFunc": [ + "SgnFunc": 0, + "_SgnFunc": [ 0, 0, 0, @@ -155,8 +160,12 @@ "0 for SingleGaus", "1 for DoubleGaus", "2 for DoubleGausSigmaRatioPar", - "3 for GausSec" + "3 for GausSec", + "4 for DoubleSidedCrystalBall" ], + "NSigmaForSideband": 3, + "NSigmaForSignal": 3, "DrawBgPrefit": true, - "HighlightPeakRegion": true + "HighlightPeakRegion": true, + "RandomSeed": -1 } diff --git a/PWGHF/D2H/Macros/cut_variation.py b/PWGHF/D2H/Macros/cut_variation.py index f9b49f15710..a361da090d6 100644 --- a/PWGHF/D2H/Macros/cut_variation.py +++ b/PWGHF/D2H/Macros/cut_variation.py @@ -8,13 +8,22 @@ """ import sys +from enum import IntEnum, auto import numpy as np # pylint: disable=import-error import ROOT # pylint: disable=import-error -sys.path.insert(0, '..') + +sys.path.insert(0, "..") from style_formatter import set_global_style, set_object_style +class MinimisationStatus(IntEnum): + Undefined = 0 + Success = auto() + MonotonyViolation = auto() + Fail = auto() + + # pylint: disable=too-many-instance-attributes class CutVarMinimiser: """ @@ -40,13 +49,26 @@ class CutVarMinimiser: def __init__( # pylint: disable=too-many-arguments self, - raw_yields=np.zeros(0), - eff_prompt=np.zeros(0), - eff_nonprompt=np.zeros(0), - unc_raw_yields=np.zeros(0), - unc_eff_prompt=np.zeros(0), - unc_eff_nonprompt=np.zeros(0), + raw_yields=None, + eff_prompt=None, + eff_nonprompt=None, + unc_raw_yields=None, + unc_eff_prompt=None, + unc_eff_nonprompt=None, ): + if raw_yields is None: + raw_yields = np.zeros(0) + if eff_prompt is None: + eff_prompt = np.zeros(0) + if eff_nonprompt is None: + eff_nonprompt = np.zeros(0) + if unc_raw_yields is None: + unc_raw_yields = np.zeros(0) + if unc_eff_prompt is None: + unc_eff_prompt = np.zeros(0) + if unc_eff_nonprompt is None: + unc_eff_nonprompt = np.zeros(0) + self.raw_yields = raw_yields self.eff_prompt = eff_prompt self.eff_nonprompt = eff_nonprompt @@ -155,23 +177,15 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) ) if correlated and unc_row > 0 and unc_col > 0: - if unc_row < unc_col: - rho = unc_row / unc_col - else: - rho = unc_col / unc_row + self.m_cov_sets[i_row, i_col] = min(unc_row**2, unc_col**2) else: - if i_row == i_col: - rho = 1.0 - else: - rho = 0.0 - cov_row_col = rho * unc_row * unc_col - self.m_cov_sets[i_row, i_col] = cov_row_col + self.m_cov_sets[i_row, i_col] = unc_row**2 if i_row == i_col else 0.0 self.m_cov_sets = np.matrix(self.m_cov_sets) try: self.m_weights = np.linalg.inv(np.linalg.cholesky(self.m_cov_sets)) except np.linalg.LinAlgError: - return False + return MinimisationStatus.Fail self.m_weights = self.m_weights.T * self.m_weights m_eff_tr = self.m_eff.T @@ -179,7 +193,7 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) try: self.m_covariance = np.linalg.inv(np.linalg.cholesky(self.m_covariance)) except np.linalg.LinAlgError: - return False + return MinimisationStatus.Fail self.m_covariance = self.m_covariance.T * self.m_covariance self.m_corr_yields = self.m_covariance * (m_eff_tr * self.m_weights) * self.m_rawy @@ -195,11 +209,17 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) m_corr_yields_old = np.copy(self.m_corr_yields) - print(f"INFO: number of processed iterations = {iteration+1}\n") + print(f"INFO: number of processed iterations = {iteration + 1}\n") + minimisation_status = MinimisationStatus.Success if correlated: m_cov_sets_diag = np.diag(self.m_cov_sets) - if not (np.all(m_cov_sets_diag[1:] > m_cov_sets_diag[:-1]) or np.all(m_cov_sets_diag[1:] < m_cov_sets_diag[:-1])): - print("\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m") + if not ( + np.all(m_cov_sets_diag[1:] > m_cov_sets_diag[:-1]) or np.all(m_cov_sets_diag[1:] < m_cov_sets_diag[:-1]) + ): + minimisation_status = MinimisationStatus.MonotonyViolation + print( + "\033[33mWARNING! minimise_system(): the residual vector uncertainties elements are not monotonous. Check the input for stability.\033[0m" + ) print(f"residual vector uncertainties elements = {np.sqrt(m_cov_sets_diag)}\n") # chi2 @@ -229,7 +249,7 @@ def minimise_system(self, correlated=True, precision=1.0e-8, max_iterations=100) self.unc_frac_prompt[i_set] = unc_fp self.unc_frac_nonprompt[i_set] = unc_fnp - return True + return minimisation_status def get_red_chi2(self): """ @@ -335,8 +355,7 @@ def get_raw_prompt_fraction(self, effacc_p, effacc_np): return f_p, f_p_unc - def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np): + def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np): """ Helper function to get the raw prompt fraction given the efficiencies @@ -370,11 +389,7 @@ def get_raw_prompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, # derivatives of prompt fraction wrt corr yields d_p = (effacc_p * (rawy_p + rawy_np) - effacc_p**2 * corry_p) / (rawy_p + rawy_np) ** 2 d_np = -effacc_np * rawy_p / (rawy_p + rawy_np) ** 2 - f_p_unc = np.sqrt( - d_p**2 * unc_corry_p**2 - + d_np**2 * unc_corry_np**2 - + 2 * d_p * d_np * cov_p_np - ) + f_p_unc = np.sqrt(d_p**2 * unc_corry_p**2 + d_np**2 * unc_corry_np**2 + 2 * d_p * d_np * cov_p_np) return f_p, f_p_unc @@ -401,8 +416,9 @@ def get_raw_nonprompt_fraction(self, effacc_p, effacc_np): return f_np, f_np_unc - def get_raw_nonprompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np): + def get_raw_nonprompt_fraction_ext( + self, corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np + ): """ Helper function to get the raw non-prompt fraction given the efficiencies @@ -430,8 +446,9 @@ def get_raw_nonprompt_fraction_ext(self, corry_p, corry_np, unc_corry_p, """ - f_p, f_np_unc = self.get_raw_prompt_fraction_ext(corry_p, corry_np, unc_corry_p, - unc_corry_np, cov_p_np, effacc_p, effacc_np) + f_p, f_np_unc = self.get_raw_prompt_fraction_ext( + corry_p, corry_np, unc_corry_p, unc_corry_np, cov_p_np, effacc_p, effacc_np + ) f_np = 1 - f_p return f_np, f_np_unc @@ -572,7 +589,8 @@ def plot_result(self, suffix="", title=""): hist_raw_yield_sum.Draw("histsame") tex = ROOT.TLatex() tex.SetTextSize(0.04) - tex.DrawLatexNDC(0.05, 0.95, title) + tex.SetTextAlign(31) + tex.DrawLatexNDC(0.95, 0.95, title) canvas.Modified() canvas.Update() @@ -611,7 +629,7 @@ def plot_cov_matrix(self, correlated=True, suffix="", title=""): padleftmargin=0.14, padbottommargin=0.12, padrightmargin=0.12, - padtopmargin = 0.075, + padtopmargin=0.075, palette=ROOT.kRainBow, ) @@ -628,22 +646,17 @@ def plot_cov_matrix(self, correlated=True, suffix="", title=""): for i_row, unc_row in enumerate(self.unc_raw_yields): for i_col, unc_col in enumerate(self.unc_raw_yields): if correlated and unc_row > 0 and unc_col > 0: - if unc_row < unc_col: - rho = unc_row / unc_col - else: - rho = unc_col / unc_row + rho = min(unc_row / unc_col, unc_col / unc_row) else: - if i_row == i_col: - rho = 1.0 - else: - rho = 0.0 + rho = 1.0 if i_row == i_col else 0.0 hist_corr_matrix.SetBinContent(i_row + 1, i_col + 1, rho) canvas = ROOT.TCanvas(f"cCorrMatrixCutSets{suffix}", "", 500, 500) hist_corr_matrix.Draw("colz") tex = ROOT.TLatex() tex.SetTextSize(0.04) - tex.DrawLatexNDC(0.05, 0.95, title) + tex.SetTextAlign(31) + tex.DrawLatexNDC(0.95, 0.95, title) canvas.Modified() canvas.Update() @@ -671,7 +684,7 @@ def plot_efficiencies(self, suffix="", title=""): """ suffix = suffix.replace(".", "_") - set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin = 0.075) + set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin=0.075) hist_eff_prompt = ROOT.TH1F( f"hEffPromptVsCut{suffix}", @@ -735,7 +748,8 @@ def plot_efficiencies(self, suffix="", title=""): leg.Draw() tex = ROOT.TLatex() tex.SetTextSize(0.04) - tex.DrawLatexNDC(0.05, 0.95, title) + tex.SetTextAlign(31) + tex.DrawLatexNDC(0.95, 0.95, title) canvas.Modified() canvas.Update() @@ -763,7 +777,7 @@ def plot_fractions(self, suffix="", title=""): """ suffix = suffix.replace(".", "_") - set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin = 0.075) + set_global_style(padleftmargin=0.14, padbottommargin=0.12, titleoffset=1.2, padtopmargin=0.075) hist_f_prompt = ROOT.TH1F( f"hFracPromptVsCut{suffix}", @@ -820,7 +834,8 @@ def plot_fractions(self, suffix="", title=""): leg.Draw() tex = ROOT.TLatex() tex.SetTextSize(0.04) - tex.DrawLatexNDC(0.05, 0.95, title) + tex.SetTextAlign(31) + tex.DrawLatexNDC(0.95, 0.95, title) canvas.Modified() canvas.Update() @@ -873,7 +888,7 @@ def plot_uncertainties(self, suffix="", title=""): for i_bin, (unc_rawy, unc_res) in enumerate(zip(self.unc_raw_yields, m_cov_sets_diag)): hist_raw_yield_unc.SetBinContent(i_bin + 1, unc_rawy) - hist_residual_unc.SetBinContent(i_bin+1, unc_res) + hist_residual_unc.SetBinContent(i_bin + 1, unc_res) set_object_style(hist_raw_yield_unc, color=ROOT.kRed + 1, fillstyle=0) set_object_style(hist_residual_unc, color=ROOT.kAzure + 4, fillstyle=0) @@ -897,7 +912,8 @@ def plot_uncertainties(self, suffix="", title=""): hist_residual_unc.Draw("histsame") tex = ROOT.TLatex() tex.SetTextSize(0.04) - tex.DrawLatexNDC(0.05, 0.95, title) + tex.SetTextAlign(31) + tex.DrawLatexNDC(0.95, 0.95, title) canvas.Modified() canvas.Update() @@ -907,3 +923,110 @@ def plot_uncertainties(self, suffix="", title=""): } return canvas, histos, leg + + # pylint: disable=no-member + def plot_relative_uncertainties(self, suffix="", title=""): + """ + Helper function to plot uncertainties as a function of cut set + + Parameters + ----------------------------------------------------- + - suffix: str + suffix to be added in the name of the output objects + - title: str + title to be written at the top margin of the output objects + + Returns + ----------------------------------------------------- + - canvas: ROOT.TCanvas + canvas with plot + - histos: dict + dictionary of ROOT.TH1F with relative uncertainties distributions + for raw yield and efficiencies + - leg: ROOT.TLegend + needed otherwise it is destroyed + """ + suffix = suffix.replace(".", "_") + + set_global_style(padleftmargin=0.16, padbottommargin=0.12, padtopmargin=0.075, titleoffsety=1.6) + + hist_raw_yield_rel_unc = ROOT.TH1F( + f"hRawYieldRelUncVsCut{suffix}", + ";cut set;relative unc.", + self.n_sets, + -0.5, + self.n_sets - 0.5, + ) + + hist_eff_prompt_rel_unc = ROOT.TH1F( + f"hEffPromptRelUncVsCut{suffix}", + ";cut set;relative unc.", + self.n_sets, + -0.5, + self.n_sets - 0.5, + ) + + hist_eff_nonprompt_rel_unc = ROOT.TH1F( + f"hEffNonPromptRelUncVsCut{suffix}", + ";cut set;relative unc.", + self.n_sets, + -0.5, + self.n_sets - 0.5, + ) + + for i_bin, (unc_rawy, rawy, unc_eff_prompt, eff_prompt, unc_eff_nonprompt, eff_nonprompt) in enumerate( + zip( + self.unc_raw_yields, + self.raw_yields, + self.unc_eff_prompt, + self.eff_prompt, + self.unc_eff_nonprompt, + self.eff_nonprompt, + ) + ): + hist_raw_yield_rel_unc.SetBinContent(i_bin + 1, unc_rawy / rawy) + hist_eff_prompt_rel_unc.SetBinContent(i_bin + 1, unc_eff_prompt / eff_prompt) + hist_eff_nonprompt_rel_unc.SetBinContent(i_bin + 1, unc_eff_nonprompt / eff_nonprompt) + + set_object_style(hist_raw_yield_rel_unc, color=ROOT.kBlack, fillstyle=0) + set_object_style(hist_eff_prompt_rel_unc, color=ROOT.kRed + 1, fillstyle=0) + set_object_style(hist_eff_nonprompt_rel_unc, color=ROOT.kAzure + 4, fillstyle=0) + + canvas = ROOT.TCanvas(f"cRelUncVsCut{suffix}", "", 500, 500) + canvas.DrawFrame( + -0.5, + 0.0, + self.n_sets - 0.5, + max( + hist_raw_yield_rel_unc.GetMaximum(), + hist_eff_prompt_rel_unc.GetMaximum(), + hist_eff_nonprompt_rel_unc.GetMaximum(), + ) + * 1.2, + ";cut set;relative unc.", + ) + leg = ROOT.TLegend(0.2, 0.75, 0.4, 0.85) + leg.SetBorderSize(0) + leg.SetFillStyle(0) + leg.SetTextSize(0.04) + leg.AddEntry(hist_raw_yield_rel_unc, "raw yield", "l") + leg.AddEntry(hist_eff_prompt_rel_unc, "efficiency prompt", "l") + leg.AddEntry(hist_eff_nonprompt_rel_unc, "efficiency nonprompt", "l") + leg.Draw() + hist_raw_yield_rel_unc.Draw("histsame") + hist_eff_prompt_rel_unc.Draw("histsame") + hist_eff_nonprompt_rel_unc.Draw("histsame") + tex = ROOT.TLatex() + tex.SetTextSize(0.04) + tex.SetTextAlign(31) + tex.DrawLatexNDC(0.95, 0.95, title) + canvas.Modified() + canvas.Update() + + histos = { + "rawy": hist_raw_yield_rel_unc, + "prompt": hist_eff_prompt_rel_unc, + "nonprompt": hist_eff_nonprompt_rel_unc, + } + + return canvas, histos, leg diff --git a/PWGHF/D2H/Macros/runMassFitter.C b/PWGHF/D2H/Macros/runMassFitter.C index 5937d12e0ba..531b0515679 100644 --- a/PWGHF/D2H/Macros/runMassFitter.C +++ b/PWGHF/D2H/Macros/runMassFitter.C @@ -37,8 +37,11 @@ #include #include +#include +#include #include #include +#include #include #include #include @@ -66,7 +69,12 @@ T readJsonField(const Document& config, const std::string& fieldName); template void readJsonVector(std::vector& vec, const Document& config, const std::string& fieldName, bool isRequired = false); -void divideCanvas(TCanvas* c, int nSliceVarBins); +template +void readJsonVectorFlexible(std::vector& vec, const Document& config, int nHistograms, const std::string& fieldName, bool isRequired = false); + +void readJsonVectorFromHisto(std::vector& vec, const Document& config, const std::string& fileNameFieldName, const std::string& histoNameFieldName); + +void divideCanvas(TCanvas* c, int nHistograms); void setHistoStyle(TH1* histo, Color_t color = kBlack, Size_t markerSize = 1); @@ -79,25 +87,31 @@ void runMassFitter(const std::string& configFileName) } Document config; - char readBuffer[65536]; - FileReadStream is(configFile, readBuffer, sizeof(readBuffer)); + std::array readBuffer{}; + FileReadStream is(configFile, readBuffer.data(), readBuffer.size()); config.ParseStream(is); fclose(configFile); - bool const isMc = readJsonField(config, "IsMC"); - bool const writeSignalPar = readJsonField(config, "WriteSignalPar", true); - std::string const particleName = readJsonField(config, "Particle"); - std::string const collisionSystem = readJsonField(config, "CollisionSystem", ""); - std::string const inputFileName = readJsonField(config, "InFileName"); - std::string const reflFileName = readJsonField(config, "ReflFileName", ""); + if (config.HasParseError()) { + throw std::runtime_error("ERROR: Parsing the configuration json file failed. Check the config for correct formatting"); + } + + auto const isMc = readJsonField(config, "IsMC"); + auto const writeSignalPar = readJsonField(config, "WriteSignalPar", true); + auto const particleName = readJsonField(config, "Particle"); + auto const collisionSystem = readJsonField(config, "CollisionSystem", ""); + auto const inputFileName = readJsonField(config, "InFileName"); + auto const reflFileName = readJsonField(config, "ReflFileName", ""); TString outputFileName = readJsonField(config, "OutFileName", "mInvFit.root"); std::vector inputHistoName; std::vector promptHistoName; std::vector fdHistoName; + std::vector signalHistoName; std::vector reflHistoName; std::vector promptSecPeakHistoName; std::vector fdSecPeakHistoName; + std::vector signalSecPeakHistoName; std::vector sliceVarMin; std::vector sliceVarMax; std::vector massMin; @@ -109,29 +123,48 @@ void runMassFitter(const std::string& configFileName) std::vector nRebin; std::vector bkgFunc; std::vector sgnFunc; - - readJsonVector(inputHistoName, config, "InputHistoName", true); + std::vector dscbAlphaLInitial; + std::vector dscbAlphaLLower; + std::vector dscbAlphaLUpper; + std::vector dscbAlphaRInitial; + std::vector dscbAlphaRLower; + std::vector dscbAlphaRUpper; + std::vector dscbNLInitial; + std::vector dscbNLLower; + std::vector dscbNLUpper; + std::vector dscbNRInitial; + std::vector dscbNRLower; + std::vector dscbNRUpper; + + readJsonVector(inputHistoName, config, "InputHistoName"); readJsonVector(promptHistoName, config, "PromptHistoName"); readJsonVector(fdHistoName, config, "FDHistoName"); + readJsonVector(signalHistoName, config, "SignalHistoName"); + const std::array possibleInputHistogramSizes{inputHistoName.size(), promptHistoName.size(), fdHistoName.size(), signalHistoName.size()}; + const int nHistograms = static_cast(*std::max_element(possibleInputHistogramSizes.begin(), possibleInputHistogramSizes.end())); + readJsonVector(reflHistoName, config, "ReflHistoName"); readJsonVector(promptSecPeakHistoName, config, "PromptSecPeakHistoName"); readJsonVector(fdSecPeakHistoName, config, "FDSecPeakHistoName"); + readJsonVector(signalSecPeakHistoName, config, "SignalSecPeakHistoName"); - const bool fixMean = readJsonField(config, "FixMean", false); - const std::string meanFile = readJsonField(config, "MeanFile", ""); - readJsonVector(fixMeanManual, config, "FixMeanManual"); + const auto fixMean = readJsonField(config, "FixMean", false); + const auto meanFile = readJsonField(config, "MeanFile", ""); + readJsonVectorFlexible(fixMeanManual, config, nHistograms, "FixMeanManual"); - const bool fixSigma = readJsonField(config, "FixSigma", false); - const std::string sigmaFile = readJsonField(config, "SigmaFile", ""); - readJsonVector(fixSigmaManual, config, "FixSigmaManual"); + const auto fixSigma = readJsonField(config, "FixSigma", false); + const auto sigmaFile = readJsonField(config, "SigmaFile", ""); + readJsonVectorFlexible(fixSigmaManual, config, nHistograms, "FixSigmaManual"); - const bool fixSecondSigma = readJsonField(config, "FixSecondSigma", false); - const std::string secondSigmaFile = readJsonField(config, "SecondSigmaFile", ""); - readJsonVector(fixSecondSigmaManual, config, "FixSecondSigmaManual"); + const auto fixSecondSigma = readJsonField(config, "FixSecondSigma", false); + const auto secondSigmaFile = readJsonField(config, "SecondSigmaFile", ""); + readJsonVectorFlexible(fixSecondSigmaManual, config, nHistograms, "FixSecondSigmaManual"); - const bool fixFracDoubleGaus = readJsonField(config, "FixFracDoubleGaus", false); - const std::string fracDoubleGausFile = readJsonField(config, "FracDoubleGausFile", ""); - readJsonVector(fixFracDoubleGausManual, config, "FixFracDoubleGausManual"); + const auto fixFracDoubleGaus = readJsonField(config, "FixFracDoubleGaus", false); + const auto fracDoubleGausFile = readJsonField(config, "FracDoubleGausFile", ""); + readJsonVectorFlexible(fixFracDoubleGausManual, config, nHistograms, "FixFracDoubleGausManual"); + + const auto fixDscbTailParams = readJsonField(config, "FixDscbTailParams", false); const TString sliceVarName = readJsonField(config, "SliceVarName"); const TString sliceVarUnit = readJsonField(config, "SliceVarUnit"); @@ -139,26 +172,53 @@ void runMassFitter(const std::string& configFileName) readJsonVector(sliceVarMin, config, "SliceVarMin", true); readJsonVector(sliceVarMax, config, "SliceVarMax", true); - readJsonVector(massMin, config, "MassMin", true); - readJsonVector(massMax, config, "MassMax", true); - - readJsonVector(nRebin, config, "Rebin", true); - - bool const includeSecPeak = readJsonField(config, "InclSecPeak", false); - bool const useLikelihood = readJsonField(config, "UseLikelihood"); - - readJsonVector(bkgFunc, config, "BkgFunc", true); - readJsonVector(sgnFunc, config, "SgnFunc", true); - - const bool enableRefl = readJsonField(config, "EnableRefl", false); - const bool drawBgPrefit = readJsonField(config, "DrawBgPrefit", true); - const bool highlightPeakRegion = readJsonField(config, "HighlightPeakRegion", true); - - const int nSliceVarBins = static_cast(sliceVarMin.size()); - std::vector sliceVarLimits(nSliceVarBins + 1); - - auto checkVectorSize = [&](const auto& vec, const std::string& name = "", const bool isEmptyOk = false) { - if (vec.size() != static_cast(nSliceVarBins)) { + readJsonVectorFlexible(massMin, config, nHistograms, "MassMin", true); + readJsonVectorFlexible(massMax, config, nHistograms, "MassMax", true); + + readJsonVectorFlexible(nRebin, config, nHistograms, "Rebin", true); + + auto const includeSecPeak = readJsonField(config, "InclSecPeak", false); + auto const useLikelihood = readJsonField(config, "UseLikelihood"); + + readJsonVectorFlexible(bkgFunc, config, nHistograms, "BkgFunc", true); + readJsonVectorFlexible(sgnFunc, config, nHistograms, "SgnFunc", true); + + const auto enableRefl = readJsonField(config, "EnableRefl", false); + const auto drawBgPrefit = readJsonField(config, "DrawBgPrefit", true); + const auto highlightPeakRegion = readJsonField(config, "HighlightPeakRegion", true); + const auto randomSeed = readJsonField(config, "RandomSeed", -1); + const auto nSigmaForSideband = readJsonField(config, "NSigmaForSideband", 3.); + const auto nSigmaForSignal = readJsonField(config, "NSigmaForSignal", 3.); + + readJsonVector(dscbAlphaLInitial, config, "DscbAlphaLInitial"); + readJsonVector(dscbAlphaLLower, config, "DscbAlphaLLower"); + readJsonVector(dscbAlphaLUpper, config, "DscbAlphaLUpper"); + readJsonVector(dscbAlphaRInitial, config, "DscbAlphaRInitial"); + readJsonVector(dscbAlphaRLower, config, "DscbAlphaRLower"); + readJsonVector(dscbAlphaRUpper, config, "DscbAlphaRUpper"); + readJsonVector(dscbNLInitial, config, "DscbNLInitial"); + readJsonVector(dscbNLLower, config, "DscbNLLower"); + readJsonVector(dscbNLUpper, config, "DscbNLUpper"); + readJsonVector(dscbNRInitial, config, "DscbNRInitial"); + readJsonVector(dscbNRLower, config, "DscbNRLower"); + readJsonVector(dscbNRUpper, config, "DscbNRUpper"); + readJsonVectorFromHisto(dscbAlphaLInitial, config, "DscbParametersFile", "DscbAlphaLInitialHisto"); + readJsonVectorFromHisto(dscbAlphaLLower, config, "DscbParametersFile", "DscbAlphaLLowerHisto"); + readJsonVectorFromHisto(dscbAlphaLUpper, config, "DscbParametersFile", "DscbAlphaLUpperHisto"); + readJsonVectorFromHisto(dscbAlphaRInitial, config, "DscbParametersFile", "DscbAlphaRInitialHisto"); + readJsonVectorFromHisto(dscbAlphaRLower, config, "DscbParametersFile", "DscbAlphaRLowerHisto"); + readJsonVectorFromHisto(dscbAlphaRUpper, config, "DscbParametersFile", "DscbAlphaRUpperHisto"); + readJsonVectorFromHisto(dscbNLInitial, config, "DscbParametersFile", "DscbNLInitialHisto"); + readJsonVectorFromHisto(dscbNLLower, config, "DscbParametersFile", "DscbNLLowerHisto"); + readJsonVectorFromHisto(dscbNLUpper, config, "DscbParametersFile", "DscbNLUpperHisto"); + readJsonVectorFromHisto(dscbNRInitial, config, "DscbParametersFile", "DscbNRInitialHisto"); + readJsonVectorFromHisto(dscbNRLower, config, "DscbParametersFile", "DscbNRLowerHisto"); + readJsonVectorFromHisto(dscbNRUpper, config, "DscbParametersFile", "DscbNRUpperHisto"); + + std::vector sliceVarLimits(nHistograms + 1); + + auto checkVectorSize = [&](const auto& vec, const std::string& name, const bool isEmptyOk = false) { + if (vec.size() != static_cast(nHistograms)) { if (isEmptyOk && vec.empty()) { return; } @@ -169,9 +229,11 @@ void runMassFitter(const std::string& configFileName) checkVectorSize(inputHistoName, "inputHistoName", true); checkVectorSize(promptHistoName, "promptHistoName", true); checkVectorSize(fdHistoName, "fdHistoName", true); + checkVectorSize(signalHistoName, "signalHistoName", true); checkVectorSize(reflHistoName, "reflHistoName", true); checkVectorSize(promptSecPeakHistoName, "promptSecPeakHistoName", true); checkVectorSize(fdSecPeakHistoName, "fdSecPeakHistoName", true); + checkVectorSize(signalSecPeakHistoName, "signalSecPeakHistoName", true); checkVectorSize(sliceVarMin, "sliceVarMin"); checkVectorSize(sliceVarMax, "sliceVarMax"); checkVectorSize(massMin, "massMin"); @@ -183,8 +245,36 @@ void runMassFitter(const std::string& configFileName) checkVectorSize(nRebin, "nRebin"); checkVectorSize(bkgFunc, "bkgFunc"); checkVectorSize(sgnFunc, "sgnFunc"); + checkVectorSize(dscbAlphaLInitial, "dscbAlphaLInitial", true); + checkVectorSize(dscbAlphaLLower, "dscbAlphaLLower", true); + checkVectorSize(dscbAlphaLUpper, "dscbAlphaLUpper", true); + checkVectorSize(dscbAlphaRInitial, "dscbAlphaRInitial", true); + checkVectorSize(dscbAlphaRLower, "dscbAlphaRLower", true); + checkVectorSize(dscbAlphaRUpper, "dscbAlphaRUpper", true); + checkVectorSize(dscbNLInitial, "dscbNLInitial", true); + checkVectorSize(dscbNLLower, "dscbNLLower", true); + checkVectorSize(dscbNLUpper, "dscbNLUpper", true); + checkVectorSize(dscbNRInitial, "dscbNRInitial", true); + checkVectorSize(dscbNRLower, "dscbNRLower", true); + checkVectorSize(dscbNRUpper, "dscbNRUpper", true); + + auto checkVectorSizeMcHistograms = [](const auto& vecSignal, const auto& vecPrompt, const auto& vecFd) { + const auto signalSize = vecSignal.size(); + const auto promptSize = vecPrompt.size(); + const auto fdSize = vecFd.size(); + if (!((signalSize > 0 && promptSize == 0 && fdSize == 0) || (signalSize == 0 && promptSize > 0 && fdSize > 0))) { + throw std::runtime_error("ERROR: either signal histogram must be provided or both prompt and fd, but not all three. Exit"); + } + }; + + if ((!isMc && enableRefl) || isMc) { + checkVectorSizeMcHistograms(signalHistoName, promptHistoName, fdHistoName); + } + if (isMc && includeSecPeak) { + checkVectorSizeMcHistograms(signalSecPeakHistoName, promptSecPeakHistoName, fdSecPeakHistoName); + } - for (int iSliceVar = 0; iSliceVar < nSliceVarBins; iSliceVar++) { + for (int iSliceVar = 0; iSliceVar < nHistograms; iSliceVar++) { sliceVarLimits[iSliceVar] = sliceVarMin[iSliceVar]; if (bkgFunc[iSliceVar] < 0 || bkgFunc[iSliceVar] >= HFInvMassFitter::NTypesOfBkgPdf) { @@ -197,7 +287,7 @@ void runMassFitter(const std::string& configFileName) throw std::runtime_error("ERROR: only SingleGaus, DoubleGaus and DoubleGausSigmaRatioPar signal functions supported! Exit"); } } - sliceVarLimits[nSliceVarBins] = sliceVarMax[nSliceVarBins - 1]; + sliceVarLimits[nHistograms] = sliceVarMax[nHistograms - 1]; struct DecayInfo { std::string decayProducts; @@ -207,6 +297,7 @@ void runMassFitter(const std::string& configFileName) }; std::map particles{ + // NOLINTBEGIN(modernize-use-designated-initializers): c++17 compatibility {"Dplus", {"K#pi#pi", "D+", "D^{+}", "K^{-}#pi^{+}#pi^{+}"}}, {"D0", {"K#pi", "D0", "D^{0}", "K^{-}#pi^{+}"}}, {"Ds", {"KK#pi", "D_s+", "D_{s}^{+}", "K^{-}K^{+}#pi^{+}"}}, @@ -214,37 +305,51 @@ void runMassFitter(const std::string& configFileName) {"LcToPK0s", {"pK^{0}_{s}", "Lambda_c+", "#Lambda_{c}^{+}", "pK^{0}_{s}"}}, {"Dstar", {"D^{0}pi^{+}", "D*+", "D^{*+}", "D^{0}#pi^{+}"}}, {"XicToXiPiPi", {"#Xi#pi#pi", "Xi_c+", "#Xi_{c}^{+}", "#Xi^{-}#pi^{+}#pi^{+}"}}}; - if (particles.find(particleName.c_str()) == particles.end()) { + // NOLINTEND(modernize-use-designated-initializers): c++17 compatibility + if (particles.find(particleName) == particles.end()) { // NOLINT(readability-container-contains): c++17 compatibility throw std::runtime_error("ERROR: only Dplus, D0, Ds, LcToPKPi, LcToPK0s, Dstar and XicToXiPiPi particles supported! Exit"); } const auto& particle = particles[particleName.c_str()]; const std::string massAxisTitle = "#it{M}(" + particle.decayProducts + ") (GeV/#it{c}^{2})"; const double massPDG = TDatabasePDG::Instance()->GetParticle(particle.pdgName.c_str())->Mass(); - const std::vector plotLabels = {(particle.decayFormulaLhs + " #rightarrow " + particle.decayFormulaRhs + " + c.c.").c_str(), collisionSystem}; + const std::vector plotLabels = {particle.decayFormulaLhs + " #rightarrow " + particle.decayFormulaRhs + " + c.c.", collisionSystem}; // load inv-mass histograms auto* inputFile = openFileWithNullptrCheck(inputFileName); TFile* inputFileRefl = enableRefl ? openFileWithNullptrCheck(reflFileName) : nullptr; - std::vector hMassSgn(nSliceVarBins); - std::vector hMassRefl(nSliceVarBins); - std::vector hMass(nSliceVarBins); + std::vector hMassSgn(nHistograms); + std::vector hMassRefl(nHistograms); + std::vector hMass(nHistograms); + std::vector hCovCorr(nHistograms); - for (int iSliceVar = 0; iSliceVar < nSliceVarBins; iSliceVar++) { + for (int iSliceVar = 0; iSliceVar < nHistograms; iSliceVar++) { if (!isMc) { hMass[iSliceVar] = getObjectWithNullPtrCheck(inputFile, inputHistoName[iSliceVar]); if (enableRefl) { hMassRefl[iSliceVar] = getObjectWithNullPtrCheck(inputFileRefl, reflHistoName[iSliceVar]); - hMassSgn[iSliceVar] = getObjectWithNullPtrCheck(inputFileRefl, fdHistoName[iSliceVar]); - hMassSgn[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFileRefl, promptHistoName[iSliceVar])); + if (!signalHistoName.empty()) { + hMassSgn[iSliceVar] = getObjectWithNullPtrCheck(inputFileRefl, signalHistoName[iSliceVar]); + } else { + hMassSgn[iSliceVar] = getObjectWithNullPtrCheck(inputFileRefl, fdHistoName[iSliceVar]); + hMassSgn[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFileRefl, promptHistoName[iSliceVar])); + } } } else { - hMass[iSliceVar] = getObjectWithNullPtrCheck(inputFile, promptHistoName[iSliceVar]); - hMass[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFile, fdHistoName[iSliceVar])); + if (!signalHistoName.empty()) { + hMass[iSliceVar] = getObjectWithNullPtrCheck(inputFile, signalHistoName[iSliceVar]); + } else { + hMass[iSliceVar] = getObjectWithNullPtrCheck(inputFile, promptHistoName[iSliceVar]); + hMass[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFile, fdHistoName[iSliceVar])); + } if (includeSecPeak) { - hMass[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFile, promptSecPeakHistoName[iSliceVar])); - hMass[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFile, fdSecPeakHistoName[iSliceVar])); + if (!signalHistoName.empty()) { + hMass[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFile, signalSecPeakHistoName[iSliceVar])); + } else { + hMass[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFile, promptSecPeakHistoName[iSliceVar])); + hMass[iSliceVar]->Add(getObjectWithNullPtrCheck(inputFile, fdSecPeakHistoName[iSliceVar])); + } } } hMass[iSliceVar]->SetDirectory(nullptr); @@ -255,18 +360,22 @@ void runMassFitter(const std::string& configFileName) } // define output histos - auto* hRawYieldsSignal = new TH1D("hRawYieldsSignal", ";" + sliceVarName + "(" + sliceVarUnit + ");raw yield", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsSignalCounted = new TH1D("hRawYieldsSignalCounted", ";" + sliceVarName + "(" + sliceVarUnit + ");raw yield via bin count", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsBkg = new TH1D("hRawYieldsBkg", ";" + sliceVarName + "(" + sliceVarUnit + ");Background (3#sigma)", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsSgnOverBkg = new TH1D("hRawYieldsSgnOverBkg", ";" + sliceVarName + "(" + sliceVarUnit + ");S/B (3#sigma)", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsSignificance = new TH1D("hRawYieldsSignificance", ";" + sliceVarName + "(" + sliceVarUnit + ");significance (3#sigma)", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsChiSquareBkg = new TH1D("hRawYieldsChiSquareBkg", ";" + sliceVarName + "(" + sliceVarUnit + ");#chi^{2}/#it{ndf}", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsChiSquareTotal = new TH1D("hRawYieldsChiSquareTotal", ";" + sliceVarName + "(" + sliceVarUnit + ");#chi^{2}/#it{ndf}", nSliceVarBins, sliceVarLimits.data()); - auto* hReflectionOverSignal = new TH1D("hReflectionOverSignal", ";" + sliceVarName + "(" + sliceVarUnit + ");Refl/Signal", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsMean = new TH1D("hRawYieldsMean", ";" + sliceVarName + "(" + sliceVarUnit + ");mean (GeV/#it{c}^{2})", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsSigma = new TH1D("hRawYieldsSigma", ";" + sliceVarName + "(" + sliceVarUnit + ");width (GeV/#it{c}^{2})", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsSecSigma = new TH1D("hRawYieldsSecSigma", ";" + sliceVarName + "(" + sliceVarUnit + ");width (GeV/#it{c}^{2})", nSliceVarBins, sliceVarLimits.data()); - auto* hRawYieldsFracDoubleGaus = new TH1D("hRawYieldsFracDoubleGaus", ";" + sliceVarName + "(" + sliceVarUnit + ");fraction of double gaussian", nSliceVarBins, sliceVarLimits.data()); + auto* hRawYieldsSignal = new TH1D("hRawYieldsSignal", ";" + sliceVarName + " (" + sliceVarUnit + ");raw yield", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsSignalCounted = new TH1D("hRawYieldsSignalCounted", ";" + sliceVarName + " (" + sliceVarUnit + ");raw yield via bin count", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsBkg = new TH1D("hRawYieldsBkg", ";" + sliceVarName + " (" + sliceVarUnit + ");Background (3#sigma)", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsSgnOverBkg = new TH1D("hRawYieldsSgnOverBkg", ";" + sliceVarName + " (" + sliceVarUnit + ");S/B (3#sigma)", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsSignificance = new TH1D("hRawYieldsSignificance", ";" + sliceVarName + " (" + sliceVarUnit + ");significance (3#sigma)", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsChiSquareBkg = new TH1D("hRawYieldsChiSquareBkg", ";" + sliceVarName + " (" + sliceVarUnit + ");#chi^{2}/#it{ndf}", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsChiSquareTotal = new TH1D("hRawYieldsChiSquareTotal", ";" + sliceVarName + " (" + sliceVarUnit + ");#chi^{2}/#it{ndf}", nHistograms, sliceVarLimits.data()); + auto* hReflectionOverSignal = new TH1D("hReflectionOverSignal", ";" + sliceVarName + " (" + sliceVarUnit + ");Refl/Signal", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsMean = new TH1D("hRawYieldsMean", ";" + sliceVarName + " (" + sliceVarUnit + ");mean (GeV/#it{c}^{2})", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsSigma = new TH1D("hRawYieldsSigma", ";" + sliceVarName + " (" + sliceVarUnit + ");width (GeV/#it{c}^{2})", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsSecSigma = new TH1D("hRawYieldsSecSigma", ";" + sliceVarName + " (" + sliceVarUnit + ");width (GeV/#it{c}^{2})", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsFracDoubleGaus = new TH1D("hRawYieldsFracDoubleGaus", ";" + sliceVarName + " (" + sliceVarUnit + ");fraction of double gaussian", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsDscbAlphaL = new TH1D("hRawYieldsDscbAlphaL", ";" + sliceVarName + " (" + sliceVarUnit + ");#alpha_{L}", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsDscbAlphaR = new TH1D("hRawYieldsDscbAlphaR", ";" + sliceVarName + " (" + sliceVarUnit + ");#alpha_{R}", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsDscbNL = new TH1D("hRawYieldsDscbNL", ";" + sliceVarName + " (" + sliceVarUnit + ");n_{L}", nHistograms, sliceVarLimits.data()); + auto* hRawYieldsDscbNR = new TH1D("hRawYieldsDscbNR", ";" + sliceVarName + " (" + sliceVarUnit + ");n_{R}", nHistograms, sliceVarLimits.data()); enum { ConfigMassMin = 1, @@ -275,17 +384,34 @@ void runMassFitter(const std::string& configFileName) ConfigFixSigma, ConfigBkgFunc, ConfigSgnFunc, + ConfigRandomSeed, NConfigsToSave }; - auto* hFitConfig = new TH2F("hfitConfig", "Fit Configurations", NConfigsToSave - 1, 0, NConfigsToSave - 1, nSliceVarBins, sliceVarLimits.data()); - const char* hFitConfigXLabel[NConfigsToSave - 1] = {"mass min", "mass max", "rebin num", "fix sigma", "bkg func", "sgn func"}; - hFitConfig->SetStats(false); + enum { + FitResultStatus = 1, + FitResultCovQual, + FitResultEdm, + FitResultMinNll, + FitResultNSgnGCC, + NFitResultsToSave + }; + auto* hFitConfig = new TH2F("hFitConfig", "Fit Configurations", NConfigsToSave - 1, 0, NConfigsToSave - 1, nHistograms, sliceVarLimits.data()); + constexpr std::array HFitConfigXLabel = {"mass min", "mass max", "rebin num", "fix sigma", "bkg func", "sgn func", "rnd seed"}; + auto* hFitResult = new TH2F("hFitResult", "Fit Result", NFitResultsToSave - 1, 0, NFitResultsToSave - 1, nHistograms, sliceVarLimits.data()); + constexpr std::array HFitResultXLabel = {"status", "cov qual", "edm", "minNLL", "N Sig GCC"}; for (int i = 0; i < NConfigsToSave - 1; i++) { - hFitConfig->GetXaxis()->SetBinLabel(i + 1, hFitConfigXLabel[i]); + hFitConfig->GetXaxis()->SetBinLabel(i + 1, HFitConfigXLabel[i]); + } + for (int i = 0; i < NFitResultsToSave - 1; i++) { + hFitResult->GetXaxis()->SetBinLabel(i + 1, HFitResultXLabel[i]); + } + for (const auto& h : {hFitConfig, hFitResult}) { + h->SetStats(false); + h->LabelsDeflate("X"); + h->LabelsDeflate("Y"); + h->LabelsOption("v"); + h->GetYaxis()->SetTitle(sliceVarName + " (" + sliceVarUnit + ")"); } - hFitConfig->LabelsDeflate("X"); - hFitConfig->LabelsDeflate("Y"); - hFitConfig->LabelsOption("v"); setHistoStyle(hRawYieldsSignal); setHistoStyle(hRawYieldsSignalCounted); @@ -299,8 +425,12 @@ void runMassFitter(const std::string& configFileName) setHistoStyle(hRawYieldsSigma); setHistoStyle(hRawYieldsSecSigma); setHistoStyle(hRawYieldsFracDoubleGaus); + setHistoStyle(hRawYieldsDscbAlphaL); + setHistoStyle(hRawYieldsDscbAlphaR); + setHistoStyle(hRawYieldsDscbNL); + setHistoStyle(hRawYieldsDscbNR); - auto getHistToFix = [&nSliceVarBins](bool const& isFix, std::vector const& fixManual, std::string const& fixFileName, std::string const& var) -> TH1* { + auto getHistToFix = [&nHistograms](bool const& isFix, std::vector const& fixManual, std::string const& fixFileName, std::string const& var) -> TH1* { TH1* histToFix = nullptr; if (isFix) { if (fixManual.empty()) { @@ -308,7 +438,7 @@ void runMassFitter(const std::string& configFileName) const std::string histName = "hRawYields" + var; histToFix = getObjectWithNullPtrCheck(fixInputFile, histName); histToFix->SetDirectory(nullptr); - if (histToFix->GetNbinsX() != nSliceVarBins) { + if (histToFix->GetNbinsX() != nHistograms) { throw std::runtime_error("Different number of bins for this analysis and histo for fixed " + var); } fixInputFile->Close(); @@ -322,36 +452,38 @@ void runMassFitter(const std::string& configFileName) TH1* hSecondSigmaToFix = getHistToFix(fixSecondSigma, fixSecondSigmaManual, secondSigmaFile, "SecSigma"); TH1* hFracDoubleGausToFix = getHistToFix(fixFracDoubleGaus, fixFracDoubleGausManual, fracDoubleGausFile, "FracDoubleGaus"); - int canvasSize[2] = {1920, 1080}; - if (nSliceVarBins == 1) { + std::array canvasSize = {1920, 1080}; + if (nHistograms == 1) { canvasSize[0] = 500; canvasSize[1] = 500; } int constexpr NCanvasesMax = 20; // do not put more than 20 bins per canvas to make them visible - const int nCanvases = std::ceil(static_cast(nSliceVarBins) / NCanvasesMax); + const int nCanvases = (nHistograms + NCanvasesMax - 1) / NCanvasesMax; + const int nPads = (nCanvases == 1) ? nHistograms : NCanvasesMax; std::vector canvasMass(nCanvases); std::vector canvasResiduals(nCanvases); std::vector canvasRatio(nCanvases); std::vector canvasRefl(nCanvases); - for (int iCanvas = 0; iCanvas < nCanvases; iCanvas++) { - const int nPads = (nCanvases == 1) ? nSliceVarBins : NCanvasesMax; - canvasMass[iCanvas] = new TCanvas(Form("canvasMass%d", iCanvas), Form("canvasMass%d", iCanvas), canvasSize[0], canvasSize[1]); - divideCanvas(canvasMass[iCanvas], nPads); - - canvasResiduals[iCanvas] = new TCanvas(Form("canvasResiduals%d", iCanvas), Form("canvasResiduals%d", iCanvas), canvasSize[0], canvasSize[1]); - divideCanvas(canvasResiduals[iCanvas], nPads); - - canvasRatio[iCanvas] = new TCanvas(Form("canvasRatio%d", iCanvas), Form("canvasRatio%d", iCanvas), canvasSize[0], canvasSize[1]); - divideCanvas(canvasRatio[iCanvas], nPads); - if (enableRefl) { - canvasRefl[iCanvas] = new TCanvas(Form("canvasRefl%d", iCanvas), Form("canvasRefl%d", iCanvas), canvasSize[0], canvasSize[1]); - divideCanvas(canvasRefl[iCanvas], nPads); + std::vector*> canvasTypes{&canvasMass, &canvasResiduals, &canvasRatio}; + std::vector canvasTypeNames{"canvasMass", "canvasResiduals", "canvasRatio"}; + if (enableRefl) { + canvasTypes.push_back(&canvasRefl); + canvasTypeNames.push_back("canvasRefl"); + } + for (int iCanvasType = 0, nCanvasTypeNames = static_cast(canvasTypes.size()); iCanvasType < nCanvasTypeNames; ++iCanvasType) { + const auto canvasTypeName = canvasTypeNames[iCanvasType]; + for (int iCanvas = 0; iCanvas < nCanvases; iCanvas++) { + auto& canvas = (*canvasTypes[iCanvasType])[iCanvas]; + const auto canvasName = Form("%s%d", canvasTypeName, iCanvas); + canvas = new TCanvas(canvasName, canvasName, canvasSize[0], canvasSize[1]); + canvas->SetTicks(1, 1); + divideCanvas(canvas, nPads); } } - for (int iSliceVar = 0; iSliceVar < nSliceVarBins; iSliceVar++) { + for (int iSliceVar = 0; iSliceVar < nHistograms; iSliceVar++) { const int iCanvas = std::floor(static_cast(iSliceVar) / NCanvasesMax); hMass[iSliceVar]->Rebin(nRebin[iSliceVar]); @@ -360,17 +492,30 @@ void runMassFitter(const std::string& configFileName) hMass[iSliceVar]->SetTitle(Form("%s;%s;Counts per %0.1f MeV/#it{c}^{2}", ptTitle.Data(), massAxisTitle.c_str(), hMass[iSliceVar]->GetBinWidth(1) * 1000)); - hMass[iSliceVar]->SetName(Form("MassForFit%d", iSliceVar)); + hMass[iSliceVar]->SetName(Form("hMassForFit%d", iSliceVar + 1)); if (enableRefl) { hMassRefl[iSliceVar]->Rebin(nRebin[iSliceVar]); hMassSgn[iSliceVar]->Rebin(nRebin[iSliceVar]); } + const auto hMassLo = hMass[iSliceVar]->GetXaxis()->GetXmin(); + if (massMin[iSliceVar] < hMassLo) { + printf("Warning! massMin[%d] is less than hMass[%d] left edge (%f vs %f) and will be assigned the value of the latter\n", iSliceVar, iSliceVar, massMin[iSliceVar], hMassLo); + massMin[iSliceVar] = hMassLo; + } + const auto hMassUp = hMass[iSliceVar]->GetXaxis()->GetXmax(); + if (massMax[iSliceVar] > hMassUp) { + printf("Warning! massMax[%d] is greater than hMass[%d] right edge (%f vs %f) and will be assigned the value of the latter\n", iSliceVar, iSliceVar, massMax[iSliceVar], hMassUp); + massMax[iSliceVar] = hMassUp; + } + double reflOverSgn = 0; - HFInvMassFitter* massFitter = new HFInvMassFitter(hMass[iSliceVar], massMin[iSliceVar], massMax[iSliceVar], bkgFunc[iSliceVar], sgnFunc[iSliceVar]); + auto* massFitter = new HFInvMassFitter(hMass[iSliceVar], massMin[iSliceVar], massMax[iSliceVar], bkgFunc[iSliceVar], sgnFunc[iSliceVar], randomSeed); massFitter->setDrawBgPrefit(drawBgPrefit); + massFitter->setNumberOfSigmaForSidebands(nSigmaForSideband); + massFitter->setNumberOfSigmaForSignal(nSigmaForSignal); massFitter->setHighlightPeakRegion(highlightPeakRegion); massFitter->setInitialGaussianMean(massPDG); massFitter->setParticlePdgMass(massPDG); @@ -381,23 +526,27 @@ void runMassFitter(const std::string& configFileName) massFitter->setUseChi2Fit(); } - auto setFixedValue = [&iSliceVar](bool const& isFix, std::vector const& fixManual, const TH1* histToFix, std::function setFunc, std::string const& var) -> void { + auto setFixedValue = [&iSliceVar, massFitter](bool const& isFix, std::vector const& fixManual, const TH1* histToFix, void (HFInvMassFitter::*setter)(double), std::string const& var) -> void { if (isFix) { if (fixManual.empty() && histToFix == nullptr) { throw std::runtime_error("Histogram to fix " + var + " is null while isFix==true and fixManual is empty"); } const auto valueToFix = fixManual.empty() ? histToFix->GetBinContent(iSliceVar + 1) : fixManual[iSliceVar]; - setFunc(valueToFix); + (massFitter->*setter)(valueToFix); printf("*****************************\n"); printf("FIXED %s: %f\n", var.data(), valueToFix); printf("*****************************\n"); } }; - setFixedValue(fixMean, fixMeanManual, hMeanToFix, std::bind(&HFInvMassFitter::setFixGaussianMean, massFitter, std::placeholders::_1), "MEAN"); - setFixedValue(fixSigma, fixSigmaManual, hSigmaToFix, std::bind(&HFInvMassFitter::setFixGaussianSigma, massFitter, std::placeholders::_1), "SIGMA"); - setFixedValue(fixSecondSigma, fixSecondSigmaManual, hSecondSigmaToFix, std::bind(&HFInvMassFitter::setFixSecondGaussianSigma, massFitter, std::placeholders::_1), "SECOND SIGMA"); - setFixedValue(fixFracDoubleGaus, fixFracDoubleGausManual, hFracDoubleGausToFix, std::bind(&HFInvMassFitter::setFixFrac2Gaus, massFitter, std::placeholders::_1), "FRAC DOUBLE GAUS"); + setFixedValue(fixMean, fixMeanManual, hMeanToFix, &HFInvMassFitter::setFixGaussianMean, "MEAN"); + setFixedValue(fixSigma, fixSigmaManual, hSigmaToFix, &HFInvMassFitter::setFixGaussianSigma, "SIGMA"); + setFixedValue(fixSecondSigma, fixSecondSigmaManual, hSecondSigmaToFix, &HFInvMassFitter::setFixSecondGaussianSigma, "SECOND SIGMA"); + setFixedValue(fixFracDoubleGaus, fixFracDoubleGausManual, hFracDoubleGausToFix, &HFInvMassFitter::setFixFrac2Gaus, "FRAC DOUBLE GAUS"); + setFixedValue(fixDscbTailParams, dscbAlphaLInitial, nullptr, &HFInvMassFitter::setFixDscbAlphaL, "DSCB ALPHA LEFT"); + setFixedValue(fixDscbTailParams, dscbAlphaRInitial, nullptr, &HFInvMassFitter::setFixDscbAlphaR, "DSCB ALPHA RIGHT"); + setFixedValue(fixDscbTailParams, dscbNLInitial, nullptr, &HFInvMassFitter::setFixDscbNL, "DSCB N LEFT"); + setFixedValue(fixDscbTailParams, dscbNRInitial, nullptr, &HFInvMassFitter::setFixDscbNR, "DSCB N RIGHT"); if (!isMc && enableRefl) { reflOverSgn = hMassSgn[iSliceVar]->Integral(hMassSgn[iSliceVar]->FindBin(massMin[iSliceVar] * 1.0001), hMassSgn[iSliceVar]->FindBin(massMax[iSliceVar] * 0.999)); @@ -406,10 +555,32 @@ void runMassFitter(const std::string& configFileName) massFitter->setTemplateReflections(hMassRefl[iSliceVar]); } - massFitter->doFit(); + auto setDscbParameter = [&](const std::vector& vec, void (HFInvMassFitter::*setter)(double)) { + if (static_cast(vec.size()) == nHistograms) { + (massFitter->*setter)(vec[iSliceVar]); + } + }; + setDscbParameter(dscbAlphaLInitial, &HFInvMassFitter::setDscbAlphaLInitialValue); + setDscbParameter(dscbAlphaLLower, &HFInvMassFitter::setDscbAlphaLLowLimit); + setDscbParameter(dscbAlphaLUpper, &HFInvMassFitter::setDscbAlphaLUpLimit); + setDscbParameter(dscbAlphaRInitial, &HFInvMassFitter::setDscbAlphaRInitialValue); + setDscbParameter(dscbAlphaRLower, &HFInvMassFitter::setDscbAlphaRLowLimit); + setDscbParameter(dscbAlphaRUpper, &HFInvMassFitter::setDscbAlphaRUpLimit); + setDscbParameter(dscbNLInitial, &HFInvMassFitter::setDscbNLInitialValue); + setDscbParameter(dscbNLLower, &HFInvMassFitter::setDscbNLLowLimit); + setDscbParameter(dscbNLUpper, &HFInvMassFitter::setDscbNLUpLimit); + setDscbParameter(dscbNRInitial, &HFInvMassFitter::setDscbNRInitialValue); + setDscbParameter(dscbNRLower, &HFInvMassFitter::setDscbNRLowLimit); + setDscbParameter(dscbNRUpper, &HFInvMassFitter::setDscbNRUpLimit); + + try { + massFitter->doFit(); + } catch (const std::exception& e) { + printf("Warinig! Exception \"%s\" caught while doing fit of the histogram no. %d. The fitting process will be continued without it.\n", e.what(), iSliceVar); + } - auto drawOnCanvas = [&](std::vector& canvas, std::function drawer) { - if (nSliceVarBins > 1) { + auto drawOnCanvas = [&](std::vector& canvas, const std::function& drawer) { + if (nHistograms > 1) { canvas[iCanvas]->cd(iSliceVar - NCanvasesMax * iCanvas + 1); } else { canvas[iCanvas]->cd(); @@ -442,6 +613,14 @@ void runMassFitter(const std::string& configFileName) const double meanErr = massFitter->getMeanUncertainty(); const double sigma = massFitter->getSigma(); const double sigmaErr = massFitter->getSigmaUncertainty(); + const double dscbAlphaL = massFitter->getDscbAlphaL(); + const double dscbAlphaR = massFitter->getDscbAlphaR(); + const double dscbNL = massFitter->getDscbNL(); + const double dscbNR = massFitter->getDscbNR(); + const double dscbAlphaLErr = massFitter->getDscbAlphaLUncertainty(); + const double dscbAlphaRErr = massFitter->getDscbAlphaRUncertainty(); + const double dscbNErrL = massFitter->getDscbNLUncertainty(); + const double dscbNErrR = massFitter->getDscbNRUncertainty(); hRawYieldsSignal->SetBinContent(iSliceVar + 1, rawYield); hRawYieldsSignal->SetBinError(iSliceVar + 1, rawYieldErr); @@ -462,6 +641,14 @@ void runMassFitter(const std::string& configFileName) hRawYieldsSigma->SetBinContent(iSliceVar + 1, sigma); hRawYieldsSigma->SetBinError(iSliceVar + 1, sigmaErr); hReflectionOverSignal->SetBinContent(iSliceVar + 1, reflOverSgn); + hRawYieldsDscbAlphaL->SetBinContent(iSliceVar + 1, dscbAlphaL); + hRawYieldsDscbAlphaL->SetBinError(iSliceVar + 1, dscbAlphaLErr); + hRawYieldsDscbAlphaR->SetBinContent(iSliceVar + 1, dscbAlphaR); + hRawYieldsDscbAlphaR->SetBinError(iSliceVar + 1, dscbAlphaRErr); + hRawYieldsDscbNL->SetBinContent(iSliceVar + 1, dscbNL); + hRawYieldsDscbNL->SetBinError(iSliceVar + 1, dscbNErrL); + hRawYieldsDscbNR->SetBinContent(iSliceVar + 1, dscbNR); + hRawYieldsDscbNR->SetBinError(iSliceVar + 1, dscbNErrR); if (sgnFunc[iSliceVar] != HFInvMassFitter::SingleGaus) { // TODO foresee DSCB and Voigt cases const double secSigma = massFitter->getSecSigma(); @@ -485,6 +672,15 @@ void runMassFitter(const std::string& configFileName) } hFitConfig->SetBinContent(ConfigBkgFunc, iSliceVar + 1, bkgFunc[iSliceVar]); hFitConfig->SetBinContent(ConfigSgnFunc, iSliceVar + 1, sgnFunc[iSliceVar]); + hFitConfig->SetBinContent(ConfigRandomSeed, iSliceVar + 1, randomSeed); + + hFitResult->SetBinContent(FitResultStatus, iSliceVar + 1, massFitter->getFitStatus()); + hFitResult->SetBinContent(FitResultCovQual, iSliceVar + 1, massFitter->getCovQual()); + hFitResult->SetBinContent(FitResultEdm, iSliceVar + 1, massFitter->getEDM()); + hFitResult->SetBinContent(FitResultMinNll, iSliceVar + 1, massFitter->getMinNll()); + hFitResult->SetBinContent(FitResultNSgnGCC, iSliceVar + 1, massFitter->getSgnGlobalCorrelCoeff()); + + hCovCorr[iSliceVar] = massFitter->getCovCorrMatrix(); } // save output histograms @@ -500,9 +696,19 @@ void runMassFitter(const std::string& configFileName) } } - for (int iSliceVar = 0; iSliceVar < nSliceVarBins; iSliceVar++) { + for (int iSliceVar = 0; iSliceVar < nHistograms; iSliceVar++) { + if (iSliceVar == 0) { + outputFile.mkdir("MassHistograms"); + outputFile.mkdir("CovCorrMatrices"); + } + outputFile.cd("MassHistograms"); hMass[iSliceVar]->Write(); + outputFile.cd("CovCorrMatrices"); + if (hCovCorr[iSliceVar] != nullptr) { + hCovCorr[iSliceVar]->Write(Form("hCovCorrMatrix%d", iSliceVar + 1)); + } } + outputFile.cd(); hRawYieldsSignal->Write(); hRawYieldsSignalCounted->Write(); hRawYieldsBkg->Write(); @@ -517,7 +723,14 @@ void runMassFitter(const std::string& configFileName) if (enableRefl) { hReflectionOverSignal->Write(); } + if (std::find(sgnFunc.begin(), sgnFunc.end(), HFInvMassFitter::DoubleSidedCrystalBall) != sgnFunc.end()) { + hRawYieldsDscbAlphaL->Write(); + hRawYieldsDscbAlphaR->Write(); + hRawYieldsDscbNL->Write(); + hRawYieldsDscbNR->Write(); + } hFitConfig->Write(); + hFitResult->Write(); outputFile.Close(); @@ -548,10 +761,10 @@ void setHistoStyle(TH1* histo, Color_t color, Size_t markerSize) histo->SetLineColor(color); } -void divideCanvas(TCanvas* canvas, int nSliceVarBins) +void divideCanvas(TCanvas* canvas, int nHistograms) { - int nCols = std::ceil(std::sqrt(nSliceVarBins)); - int nRows = std::ceil(static_cast(nSliceVarBins) / nCols); + int nCols = std::ceil(std::sqrt(nHistograms)); + int nRows = std::ceil(static_cast(nHistograms) / nCols); canvas->Divide(nCols, nRows); } @@ -629,6 +842,47 @@ void readJsonVector(std::vector& vec, const Document& config, const std::stri } } +template +void readJsonVectorFlexible(std::vector& vec, const Document& config, int nHistograms, const std::string& fieldName, bool isRequired) +{ + if constexpr (!(std::is_same_v, int> || std::is_same_v, double>)) { + static_assert(AlwaysFalse, "readJsonVectorFlexible(): unsupported type!"); + } + if (!vec.empty()) { + throw std::runtime_error("readJsonVectorFlexible(): vector is not empty!"); + } + if (!config.HasMember(fieldName.c_str())) { + if (isRequired) { + throw std::runtime_error("readJsonVectorFlexible(): missing required field " + fieldName); + } + return; + } + if (config[fieldName.c_str()].IsArray()) { + readJsonVector(vec, config, fieldName); + } else { + const T value = readJsonField(config, fieldName); + vec.assign(nHistograms, value); + } +} + +void readJsonVectorFromHisto(std::vector& vec, const Document& config, const std::string& fileNameFieldName, const std::string& histoNameFieldName) +{ + if (!vec.empty()) { + throw std::runtime_error("readJsonVectorFromHisto(): vector is not empty!"); + } + const auto fileName = readJsonField(config, fileNameFieldName, ""); + const auto histoName = readJsonField(config, histoNameFieldName, ""); + if (fileName.empty() || histoName.empty()) { + return; + } + TFile* inputFile = openFileWithNullptrCheck(fileName); + TH1* histo = getObjectWithNullPtrCheck(inputFile, histoName); + for (int iBin = 1; iBin <= histo->GetNbinsX(); iBin++) { + vec.push_back(histo->GetBinContent(iBin)); + } + inputFile->Close(); +} + int main(int argc, const char* argv[]) { if (argc == 1) { @@ -638,7 +892,11 @@ int main(int argc, const char* argv[]) const std::string configFileName = argv[1]; - runMassFitter(configFileName); - - return 0; + try { + runMassFitter(configFileName); + return 0; + } catch (const std::exception& e) { + printf("Error: Exception \"%s\" caught during runMassFitter() call. Exit.\n", e.what()); + return 1; + } } diff --git a/PWGHF/D2H/TableProducer/CMakeLists.txt b/PWGHF/D2H/TableProducer/CMakeLists.txt index 674bbeeb25c..ebc99c83782 100644 --- a/PWGHF/D2H/TableProducer/CMakeLists.txt +++ b/PWGHF/D2H/TableProducer/CMakeLists.txt @@ -63,6 +63,13 @@ o2physics_add_dpl_workflow(candidate-selector-lb-to-lc-pi-reduced PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) +# Track selectors + +o2physics_add_dpl_workflow(ml-based-track-selector + SOURCES mlBasedTrackSelector.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsVertexing O2::DCAFitter O2Physics::AnalysisCCDB O2Physics::MLCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + # Data creators o2physics_add_dpl_workflow(data-creator-charm-had-pi-reduced @@ -106,3 +113,11 @@ o2physics_add_dpl_workflow(converter-reduced-3-prongs-ml SOURCES converterReducedHadronDausPid.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +# Tree creator + +o2physics_add_dpl_workflow(tree-creator-dstar-spin-align-mixing + SOURCES treeCreatorDstarSpinAlignMixing.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx index 613bae38b31..81a8e3d79b0 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorB0Reduced.cxx @@ -65,6 +65,7 @@ struct HfCandidateCreatorB0Reduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowDPiTolerance{"invMassWindowDPiTolerance", 0.01, "invariant-mass window tolerance for DPi pair preselections (GeV/c2)"}; @@ -94,21 +95,27 @@ struct HfCandidateCreatorB0Reduced { } // Initialize fitter - df2.setPropagateToPCA(propagateToPCA); - df2.setMaxR(maxR); - df2.setMaxDZIni(maxDZIni); - df2.setMinParamChange(minParamChange); - df2.setMinRelChi2Change(minRelChi2Change); - df2.setUseAbsDCA(useAbsDCA); - df2.setWeightedFinalPCA(useWeightedFinalPCA); - - df3.setPropagateToPCA(propagateToPCA); - df3.setMaxR(maxR); - df3.setMaxDZIni(maxDZIni); - df3.setMinParamChange(minParamChange); - df3.setMinRelChi2Change(minRelChi2Change); - df3.setUseAbsDCA(useAbsDCA); - df3.setWeightedFinalPCA(useWeightedFinalPCA); + if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl) { + df2.setPropagateToPCA(propagateToPCA); + df2.setMaxR(maxR); + df2.setMaxDZIni(maxDZIni); + df2.setMinParamChange(minParamChange); + df2.setMinRelChi2Change(minRelChi2Change); + df2.setUseAbsDCA(useAbsDCA); + df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); + } + + if (doprocessDataDstarPi || doprocessDataDstarPiWithDmesMl) { + df3.setPropagateToPCA(propagateToPCA); + df3.setMaxR(maxR); + df3.setMaxDZIni(maxDZIni); + df3.setMinParamChange(minParamChange); + df3.setMinRelChi2Change(minRelChi2Change); + df3.setUseAbsDCA(useAbsDCA); + df3.setWeightedFinalPCA(useWeightedFinalPCA); + df3.setMaxChi2(maxChi2); + } // histograms if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl) { @@ -225,7 +232,8 @@ struct HfCandidateCreatorB0Reduced { pVecSoftPion[0], pVecSoftPion[1], pVecSoftPion[2], pVecBachPion[0], pVecBachPion[1], pVecBachPion[2], dcaD.getY(), dcaSoftPion.getY(), dcaBachPion.getY(), - std::sqrt(dcaD.getSigmaY2()), std::sqrt(dcaSoftPion.getSigmaY2()), std::sqrt(dcaBachPion.getSigmaY2())); + std::sqrt(dcaD.getSigmaY2()), std::sqrt(dcaSoftPion.getSigmaY2()), std::sqrt(dcaBachPion.getSigmaY2()), + candD.xSecondaryVertex(), candD.ySecondaryVertex(), candD.zSecondaryVertex()); rowCandidateProngsDStar(candD.globalIndex(), trackBachPion.globalIndex(), trackSoftPion.globalIndex()); @@ -327,7 +335,8 @@ struct HfCandidateCreatorB0Reduced { pVecD[0], pVecD[1], pVecD[2], pVecPion[0], pVecPion[1], pVecPion[2], dcaD.getY(), dcaPion.getY(), - std::sqrt(dcaD.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2())); + std::sqrt(dcaD.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2()), + candD.xSecondaryVertex(), candD.ySecondaryVertex(), candD.zSecondaryVertex()); rowCandidateProngs(candD.globalIndex(), trackPion.globalIndex()); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx index b3777ae9012..25430a1ce18 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBToJpsiReduced.cxx @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include @@ -46,6 +46,7 @@ #include #include #include +#include // std::move using namespace o2; using namespace o2::aod; @@ -63,6 +64,8 @@ enum DecayChannel : uint8_t { struct HfCandidateCreatorBToJpsiReduced { Produces rowCandidateBpBase; // table defined in CandidateReconstructionTables.h Produces rowCandidateBpProngs; // table defined in ReducedDataModel.h + Produces rowCandidateB0Base; // table defined in CandidateReconstructionTables.h + Produces rowCandidateB0Prongs; // table defined in ReducedDataModel.h Produces rowCandidateBsBase; // table defined in CandidateReconstructionTables.h Produces rowCandidateBsProngs; // table defined in ReducedDataModel.h @@ -74,15 +77,16 @@ struct HfCandidateCreatorBToJpsiReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B+ is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2JPsiVtx{"maxChi2JPsiVtx", 1e9, "maximum value of chi2 for JPsi vertex computed with DCAFitter"}; + Configurable maxChi2BhadVtx{"maxChi2BhadVtx", 0.9, "maximum value of chi2 for B-hadron vertex computed with DCAFitter"}; - Configurable runJpsiToee{"runJpsiToee", false, "Run analysis for J/Psi to ee (debug)"}; // selection Configurable invMassWindowJpsiHadTolerance{"invMassWindowJpsiHadTolerance", 0.01, "invariant-mass window tolerance for J/Psi K pair preselections (GeV/c2)"}; - float myInvMassWindowJpsiK{1.}, myInvMassWindowJpsiPhi{1.}; // variable that will store the value of invMassWindowJpsiK (defined in dataCreatorJpsiKReduced.cxx) - double massBplus{0.}, massBs{0.}; + float myInvMassWindowJpsiK{1.}, myInvMassWindowJpsiK0Star{1.}, myInvMassWindowJpsiPhi{1.}; // variable that will store the value of invMassWindowJpsiK (defined in dataCreatorJpsiKReduced.cxx) + double massBplus{o2::constants::physics::MassBPlus}, massB0{o2::constants::physics::MassB0}, massBs{o2::constants::physics::MassBS}; double bz{0.}; - o2::vertexing::DCAFitterN<2> df2; // fitter for B vertex (2-prong vertex fitter) + o2::vertexing::DCAFitterN<2> df2; // fitter for JPsi vertex (2-prong vertex fitter) o2::vertexing::DCAFitterN<3> df3; // fitter for B vertex (3-prong vertex fitter) o2::vertexing::DCAFitterN<4> df4; // fitter for B vertex (4-prong vertex fitter) @@ -98,41 +102,44 @@ struct HfCandidateCreatorBToJpsiReduced { void init(InitContext const&) { - // invariant-mass window cut - massBplus = o2::constants::physics::MassBPlus; - massBs = o2::constants::physics::MassBS; - // Initialize fitters df2.setPropagateToPCA(propagateToPCA); df2.setMaxR(maxR); df2.setMaxDZIni(maxDZIni); df2.setMinParamChange(minParamChange); df2.setMinRelChi2Change(minRelChi2Change); + df2.setMaxChi2(maxChi2JPsiVtx); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); df2.setMatCorrType(noMatCorr); - df3.setPropagateToPCA(propagateToPCA); - df3.setMaxR(maxR); - df3.setMaxDZIni(maxDZIni); - df3.setMinParamChange(minParamChange); - df3.setMinRelChi2Change(minRelChi2Change); - df3.setUseAbsDCA(useAbsDCA); - df3.setWeightedFinalPCA(useWeightedFinalPCA); - df3.setMatCorrType(noMatCorr); - - df4.setPropagateToPCA(propagateToPCA); - df4.setMaxR(maxR); - df4.setMaxDZIni(maxDZIni); - df4.setMinParamChange(minParamChange); - df4.setMinRelChi2Change(minRelChi2Change); - df4.setUseAbsDCA(useAbsDCA); - df4.setWeightedFinalPCA(useWeightedFinalPCA); - df4.setMatCorrType(noMatCorr); + if (doprocessDataBplus) { + df3.setPropagateToPCA(propagateToPCA); + df3.setMaxR(maxR); + df3.setMaxDZIni(maxDZIni); + df3.setMinParamChange(minParamChange); + df3.setMinRelChi2Change(minRelChi2Change); + df3.setMaxChi2(maxChi2BhadVtx); + df3.setUseAbsDCA(useAbsDCA); + df3.setWeightedFinalPCA(useWeightedFinalPCA); + df3.setMatCorrType(noMatCorr); + } else { + df4.setPropagateToPCA(propagateToPCA); + df4.setMaxR(maxR); + df4.setMaxDZIni(maxDZIni); + df4.setMinParamChange(minParamChange); + df4.setMinRelChi2Change(minRelChi2Change); + df3.setMaxChi2(maxChi2BhadVtx); + df4.setUseAbsDCA(useAbsDCA); + df4.setWeightedFinalPCA(useWeightedFinalPCA); + df4.setMatCorrType(noMatCorr); + } // histograms registry.add("hMassJpsi", "J/Psi mass;#it{M}_{#mu#mu} (GeV/#it{c}^{2});Counts", {HistType::kTH1F, {{600, 2.5, 3.7, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassBplusToJpsiK", "2-prong candidates;inv. mass (B^{+} #rightarrow #overline{D^{0}}#pi^{#plus} #rightarrow #pi^{#minus}K^{#plus}#pi^{#plus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); + registry.add("hMassB0ToJpsiK0Star", "2-prong candidates;inv. mass (B^{0} #rightarrow J/ψ K^{*0}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 4., 6.}}}); + registry.add("hMassBsToJpsiPhi", "2-prong candidates;inv. mass (B^{0}_{s} #rightarrow J/ψ #phi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 4., 6.}}}); registry.add("hCovPVXX", "2-prong candidates;XX element of cov. matrix of prim. vtx. position (cm^{2});entries", {HistType::kTH1F, {{100, 0., 1.e-4}}}); registry.add("hCovSVXX", "2-prong candidates;XX element of cov. matrix of sec. vtx. position (cm^{2});entries", {HistType::kTH1F, {{100, 0., 0.2}}}); registry.add("hEvents", "Events;;entries", HistType::kTH1F, {{1, 0.5, 1.5}}); @@ -164,18 +171,36 @@ struct HfCandidateCreatorBToJpsiReduced { // Set the magnetic field from ccdb bz = collision.bz(); df2.setBz(bz); - df3.setBz(bz); - df4.setBz(bz); + if constexpr (DecChannel == DecayChannel::BplusToJpsiK) { + df3.setBz(bz); + } else { + df4.setBz(bz); + } for (const auto& candJpsi : candsJpsiThisColl) { - o2::track::TrackParametrizationWithError trackPosParCov( - candJpsi.xDauPos(), candJpsi.alphaDauPos(), {candJpsi.yDauPos(), candJpsi.zDauPos(), candJpsi.snpDauPos(), candJpsi.tglDauPos(), candJpsi.signed1PtDauPos()}, 1 /*Charge*/, 1 /*Muon*/); - o2::track::TrackParametrizationWithError trackNegParCov( - candJpsi.xDauNeg(), candJpsi.alphaDauNeg(), {candJpsi.yDauNeg(), candJpsi.zDauNeg(), candJpsi.snpDauNeg(), candJpsi.tglDauNeg(), candJpsi.signed1PtDauNeg()}, -1 /*Charge*/, 1 /*Muon*/); + + std::array parsTrackPos = {candJpsi.yDauPos(), candJpsi.zDauPos(), candJpsi.snpDauPos(), candJpsi.tglDauPos(), candJpsi.signed1PtDauPos()}; + std::array parsTrackNeg = {candJpsi.yDauNeg(), candJpsi.zDauNeg(), candJpsi.snpDauNeg(), candJpsi.tglDauNeg(), candJpsi.signed1PtDauNeg()}; + + std::array covTrackPos = {candJpsi.cYYDauPos(), candJpsi.cZYDauPos(), candJpsi.cZZDauPos(), + candJpsi.cSnpYDauPos(), candJpsi.cSnpZDauPos(), + candJpsi.cSnpSnpDauPos(), candJpsi.cTglYDauPos(), candJpsi.cTglZDauPos(), + candJpsi.cTglSnpDauPos(), candJpsi.cTglTglDauPos(), + candJpsi.c1PtYDauPos(), candJpsi.c1PtZDauPos(), candJpsi.c1PtSnpDauPos(), + candJpsi.c1PtTglDauPos(), candJpsi.c1Pt21Pt2DauPos()}; + + std::array covTrackNeg = {candJpsi.cYYDauNeg(), candJpsi.cZYDauNeg(), candJpsi.cZZDauNeg(), + candJpsi.cSnpYDauNeg(), candJpsi.cSnpZDauNeg(), + candJpsi.cSnpSnpDauNeg(), candJpsi.cTglYDauNeg(), candJpsi.cTglZDauNeg(), + candJpsi.cTglSnpDauNeg(), candJpsi.cTglTglDauNeg(), + candJpsi.c1PtYDauNeg(), candJpsi.c1PtZDauNeg(), candJpsi.c1PtSnpDauNeg(), + candJpsi.c1PtTglDauNeg(), candJpsi.c1Pt21Pt2DauNeg()}; + + o2::track::TrackParametrizationWithError trackPosParCov(candJpsi.xDauPos(), candJpsi.alphaDauPos(), std::move(parsTrackPos), std::move(covTrackPos)); + o2::track::TrackParametrizationWithError trackNegParCov(candJpsi.xDauNeg(), candJpsi.alphaDauNeg(), std::move(parsTrackNeg), std::move(covTrackNeg)); // --------------------------------- // reconstruct J/Psi candidate - o2::track::TrackParCov trackParCovJpsi{}; std::array pVecJpsi{}; registry.fill(HIST("hFitCandidatesJpsi"), SVFitting::BeforeFit); try { @@ -196,15 +221,9 @@ struct HfCandidateCreatorBToJpsiReduced { df2.getTrack(0).getPxPyPzGlo(pVecDauPos); df2.getTrack(1).getPxPyPzGlo(pVecDauNeg); pVecJpsi = RecoDecay::pVec(pVecDauPos, pVecDauNeg); - trackParCovJpsi = df2.createParentTrackParCov(); - trackParCovJpsi.setAbsCharge(0); // to be sure float invMassJpsi{0.f}; - if (runJpsiToee) { - invMassJpsi = RecoDecay::m2(std::array{pVecDauPos, pVecDauNeg}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - } else { - invMassJpsi = RecoDecay::m2(std::array{pVecDauPos, pVecDauNeg}, std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon}); - } + invMassJpsi = RecoDecay::m2(std::array{pVecDauPos, pVecDauNeg}, std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon}); invMassJpsi = std::sqrt(invMassJpsi); registry.fill(HIST("hMassJpsi"), invMassJpsi); @@ -277,8 +296,91 @@ struct HfCandidateCreatorBToJpsiReduced { std::sqrt(dcaDauPos.getSigmaY2()), std::sqrt(dcaDauNeg.getSigmaY2()), std::sqrt(dcaKaon.getSigmaY2())); rowCandidateBpProngs(candJpsi.globalIndex(), trackLf0.globalIndex()); + } else if constexpr (DecChannel == DecayChannel::B0ToJpsiK0Star) { + for (const auto& trackLf1 : tracksLfDau1ThisCollision) { + if (trackLf0.sign() == trackLf1.sign()) { + continue; // require opposite-sign tracks for K*0 reconstruction + } + // this track is among daughters + if (trackLf1.trackId() == candJpsi.prongPosId() || trackLf1.trackId() == candJpsi.prongNegId()) { + continue; + } + auto trackParCovLf1 = getTrackParCov(trackLf1); + std::array pVecTrackLf1 = trackLf1.pVector(); + + // --------------------------------- + // reconstruct the 4-prong B0 vertex + hCandidatesB->Fill(SVFitting::BeforeFit); + try { + if (df4.process(trackPosParCov, trackNegParCov, trackParCovLf0, trackParCovLf1) == 0) { + continue; + } + } catch (const std::runtime_error& error) { + LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; + hCandidatesB->Fill(SVFitting::Fail); + continue; + } + hCandidatesB->Fill(SVFitting::FitOk); + // passed B0 reconstruction + + // calculate relevant properties + const auto& secondaryVertexB0 = df4.getPCACandidate(); + auto chi2PCA = df4.getChi2AtPCACandidate(); + auto covMatrixPCA = df4.calcPCACovMatrixFlat(); + registry.fill(HIST("hCovSVXX"), covMatrixPCA[0]); + registry.fill(HIST("hCovPVXX"), covMatrixPV[0]); + + // get JPsi daughters and K tracks (propagated to the B0 vertex if propagateToPCA==true) + // track.getPxPyPzGlo(pVec) modifies pVec of track + df4.getTrack(0).getPxPyPzGlo(pVecDauPos); // momentum of Jpsi at the B0 vertex + df4.getTrack(1).getPxPyPzGlo(pVecDauNeg); // momentum of Jpsi at the B0 vertex + df4.getTrack(2).getPxPyPzGlo(pVecTrackLf0); // momentum of K at the B0 vertex + df4.getTrack(3).getPxPyPzGlo(pVecTrackLf1); // momentum of K at the B0 vertex + + // compute invariant mass square and apply selection + auto invMass2JpsiPiK = RecoDecay::m2(std::array{pVecDauPos, pVecDauNeg, pVecTrackLf0, pVecTrackLf1}, std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + auto invMass2JpsiKPi = RecoDecay::m2(std::array{pVecDauPos, pVecDauNeg, pVecTrackLf0, pVecTrackLf1}, std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + bool isK0StarPiK = invMass2JpsiPiK > invMass2JpsiHadMin && invMass2JpsiPiK < invMass2JpsiHadMax; + bool isK0StarKPi = invMass2JpsiKPi > invMass2JpsiHadMin && invMass2JpsiKPi < invMass2JpsiHadMax; + + if (!isK0StarPiK && !isK0StarKPi) { + continue; + } + registry.fill(HIST("hMassB0ToJpsiK0Star"), isK0StarPiK ? std::sqrt(invMass2JpsiPiK) : std::sqrt(invMass2JpsiKPi)); + + // compute impact parameters of Jpsi and K*0 daughters + o2::dataformats::DCA dcaDauPos, dcaDauNeg, dcaTrackLf0, dcaTrackLf1; + trackPosParCov.propagateToDCA(primaryVertex, bz, &dcaDauPos); + trackNegParCov.propagateToDCA(primaryVertex, bz, &dcaDauNeg); + trackParCovLf0.propagateToDCA(primaryVertex, bz, &dcaTrackLf0); + trackParCovLf1.propagateToDCA(primaryVertex, bz, &dcaTrackLf1); + + // get uncertainty of the decay length + double phi{}, theta{}; + // getPointDirection modifies phi and theta + getPointDirection(std::array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertexB0, phi, theta); + auto errorDecayLength = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, theta) + getRotatedCovMatrixXX(covMatrixPCA, phi, theta)); + auto errorDecayLengthXY = std::sqrt(getRotatedCovMatrixXX(covMatrixPV, phi, 0.) + getRotatedCovMatrixXX(covMatrixPCA, phi, 0.)); + + // fill the candidate table for the B0 here: + rowCandidateB0Base(collision.globalIndex(), + collision.posX(), collision.posY(), collision.posZ(), + secondaryVertexB0[0], secondaryVertexB0[1], secondaryVertexB0[2], + errorDecayLength, errorDecayLengthXY, + chi2PCA, + pVecDauPos[0], pVecDauPos[1], pVecDauPos[2], + pVecDauNeg[0], pVecDauNeg[1], pVecDauNeg[2], + pVecTrackLf0[0], pVecTrackLf0[1], pVecTrackLf0[2], + pVecTrackLf1[0], pVecTrackLf1[1], pVecTrackLf1[2], + dcaDauPos.getY(), dcaDauNeg.getY(), dcaTrackLf0.getY(), dcaTrackLf1.getY(), + std::sqrt(dcaDauPos.getSigmaY2()), std::sqrt(dcaDauNeg.getSigmaY2()), std::sqrt(dcaTrackLf0.getSigmaY2()), std::sqrt(dcaTrackLf1.getSigmaY2())); + rowCandidateB0Prongs(candJpsi.globalIndex(), trackLf0.globalIndex(), trackLf1.globalIndex()); + } } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { for (const auto& trackLf1 : tracksLfDau1ThisCollision) { + if (trackLf0.sign() == trackLf1.sign()) { + continue; // require opposite-sign tracks for phi reconstruction + } // this track is among daughters if (trackLf1.trackId() == candJpsi.prongPosId() || trackLf1.trackId() == candJpsi.prongNegId()) { continue; @@ -310,17 +412,17 @@ struct HfCandidateCreatorBToJpsiReduced { // get JPsi daughters and K tracks (propagated to the Bs vertex if propagateToPCA==true) // track.getPxPyPzGlo(pVec) modifies pVec of track - df4.getTrack(0).getPxPyPzGlo(pVecDauPos); // momentum of Jpsi at the B+ vertex - df4.getTrack(1).getPxPyPzGlo(pVecDauNeg); // momentum of Jpsi at the B+ vertex - df4.getTrack(2).getPxPyPzGlo(pVecTrackLf0); // momentum of K at the B+ vertex - df4.getTrack(3).getPxPyPzGlo(pVecTrackLf1); // momentum of K at the B+ vertex + df4.getTrack(0).getPxPyPzGlo(pVecDauPos); // momentum of Jpsi at the Bs vertex + df4.getTrack(1).getPxPyPzGlo(pVecDauNeg); // momentum of Jpsi at the Bs vertex + df4.getTrack(2).getPxPyPzGlo(pVecTrackLf0); // momentum of K at the Bs vertex + df4.getTrack(3).getPxPyPzGlo(pVecTrackLf1); // momentum of K at the Bs vertex // compute invariant mass square and apply selection auto invMass2JpsiPhi = RecoDecay::m2(std::array{pVecDauPos, pVecDauNeg, pVecTrackLf0, pVecTrackLf1}, std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); if ((invMass2JpsiPhi < invMass2JpsiHadMin) || (invMass2JpsiPhi > invMass2JpsiHadMax)) { continue; } - registry.fill(HIST("hMassBplusToJpsiK"), std::sqrt(invMass2JpsiPhi)); + registry.fill(HIST("hMassBsToJpsiPhi"), std::sqrt(invMass2JpsiPhi)); // compute impact parameters of Jpsi and K o2::dataformats::DCA dcaDauPos, dcaDauNeg, dcaTrackLf0, dcaTrackLf1; @@ -343,9 +445,9 @@ struct HfCandidateCreatorBToJpsiReduced { errorDecayLength, errorDecayLengthXY, chi2PCA, pVecDauPos[0], pVecDauPos[1], pVecDauPos[2], - pVecDauNeg[0], pVecDauNeg[1], pVecDauPos[2], + pVecDauNeg[0], pVecDauNeg[1], pVecDauNeg[2], pVecTrackLf0[0], pVecTrackLf0[1], pVecTrackLf0[2], - pVecTrackLf1[0], pVecTrackLf1[1], pVecTrackLf0[2], + pVecTrackLf1[0], pVecTrackLf1[1], pVecTrackLf1[2], dcaDauPos.getY(), dcaDauNeg.getY(), dcaTrackLf0.getY(), dcaTrackLf1.getY(), std::sqrt(dcaDauPos.getSigmaY2()), std::sqrt(dcaDauNeg.getSigmaY2()), std::sqrt(dcaTrackLf0.getSigmaY2()), std::sqrt(dcaTrackLf1.getSigmaY2())); rowCandidateBsProngs(candJpsi.globalIndex(), trackLf0.globalIndex(), trackLf1.globalIndex()); @@ -359,7 +461,7 @@ struct HfCandidateCreatorBToJpsiReduced { soa::Join const& candsJpsi, soa::Join const& tracksKaon, aod::HfOrigColCounts const& collisionsCounter, - aod::HfCfgBpToJpsi const& configs) + aod::HfCfgBpToJpsis const& configs) { // Jpsi K invariant-mass window cut for (const auto& config : configs) { @@ -383,6 +485,36 @@ struct HfCandidateCreatorBToJpsiReduced { } // processDataBplus PROCESS_SWITCH(HfCandidateCreatorBToJpsiReduced, processDataBplus, "Process data for B+", true); + void processDataB0(HfRedCollisionsWithExtras const& collisions, + soa::Join const& candsJpsi, + soa::Join const& tracksLfDau0, + soa::Join const& tracksLfDau1, + aod::HfOrigColCounts const& collisionsCounter, + aod::HfCfgB0ToJpsis const& configs) + { + // Jpsi K*0 invariant-mass window cut + for (const auto& config : configs) { + myInvMassWindowJpsiK0Star = config.myInvMassWindowJpsiK0Star(); + } + // invMassWindowJpsiHadTolerance is used to apply a slightly tighter cut than in JpsiK pair preselection + // to avoid accepting JpsiK pairs that were not formed in JpsiK pair creator + double const invMass2JpsiKMin = (massB0 - myInvMassWindowJpsiK0Star + invMassWindowJpsiHadTolerance) * (massB0 - myInvMassWindowJpsiK0Star + invMassWindowJpsiHadTolerance); + double const invMass2JpsiKMax = (massB0 + myInvMassWindowJpsiK0Star - invMassWindowJpsiHadTolerance) * (massB0 + myInvMassWindowJpsiK0Star - invMassWindowJpsiHadTolerance); + + for (const auto& collisionCounter : collisionsCounter) { + registry.fill(HIST("hEvents"), 1, collisionCounter.originalCollisionCount()); + } + + for (const auto& collision : collisions) { + auto thisCollId = collision.globalIndex(); + auto candsJpsiThisColl = candsJpsi.sliceBy(candsJpsiPerCollision, thisCollId); + auto tracksLf0ThisCollision = tracksLfDau0.sliceBy(tracksLf0PerCollision, thisCollId); + auto tracksLf1ThisCollision = tracksLfDau1.sliceBy(tracksLf1PerCollision, thisCollId); + runCandidateCreation(collision, candsJpsiThisColl, tracksLf0ThisCollision, tracksLf1ThisCollision, invMass2JpsiKMin, invMass2JpsiKMax); + } + } // processDataB0 + PROCESS_SWITCH(HfCandidateCreatorBToJpsiReduced, processDataB0, "Process data for B0", false); + void processDataBs(HfRedCollisionsWithExtras const& collisions, soa::Join const& candsJpsi, soa::Join const& tracksLfDau0, @@ -390,7 +522,7 @@ struct HfCandidateCreatorBToJpsiReduced { aod::HfOrigColCounts const& collisionsCounter, aod::HfCfgBsToJpsis const& configs) { - // Jpsi K invariant-mass window cut + // Jpsi phi invariant-mass window cut for (const auto& config : configs) { myInvMassWindowJpsiPhi = config.myInvMassWindowJpsiPhi(); } @@ -417,8 +549,10 @@ struct HfCandidateCreatorBToJpsiReduced { /// Extends the table base with expression columns and performs MC matching. struct HfCandidateCreatorBToJpsiReducedExpressions { Spawns rowCandidateBPlus; + Spawns rowCandidateB0; Spawns rowCandidateBs; Produces rowBplusMcRec; + Produces rowB0McRec; Produces rowBsMcRec; /// Fill candidate information at MC reconstruction level @@ -441,6 +575,18 @@ struct HfCandidateCreatorBToJpsiReducedExpressions { if (!filledMcInfo) { // protection to get same size tables in case something went wrong: we created a candidate that was not preselected in the Jpsi-K creator rowBplusMcRec(0, -1, -1, -1, -1.f); } + } else if constexpr (DecChannel == DecayChannel::B0ToJpsiK0Star) { + for (const auto& rowJpsiHadMcRec : rowsJpsiHadMcRec) { + if ((rowJpsiHadMcRec.jpsiId() != candB.jpsiId()) || (rowJpsiHadMcRec.prong0K0StarId() != candB.prong0K0StarId()) || (rowJpsiHadMcRec.prong1K0StarId() != candB.prong1K0StarId())) { + continue; + } + rowB0McRec(rowJpsiHadMcRec.flagMcMatchRec(), rowJpsiHadMcRec.flagMcDecayChanRec(), rowJpsiHadMcRec.flagWrongCollision(), rowJpsiHadMcRec.debugMcRec(), rowJpsiHadMcRec.ptMother()); + filledMcInfo = true; + break; + } + if (!filledMcInfo) { // protection to get same size tables in case something went wrong: we created a candidate that was not preselected in the Jpsi-K creator + rowB0McRec(0, -1, -1, -1, -1.f); + } } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { for (const auto& rowJpsiHadMcRec : rowsJpsiHadMcRec) { if ((rowJpsiHadMcRec.jpsiId() != candB.jpsiId()) || (rowJpsiHadMcRec.prong0PhiId() != candB.prong0PhiId()) || (rowJpsiHadMcRec.prong1PhiId() != candB.prong1PhiId())) { @@ -461,13 +607,19 @@ struct HfCandidateCreatorBToJpsiReducedExpressions { { fillMcRec(rowsJpsiKMcRec, candsBplus); } - PROCESS_SWITCH(HfCandidateCreatorBToJpsiReducedExpressions, processMcBPlus, "Process MC", false); + PROCESS_SWITCH(HfCandidateCreatorBToJpsiReducedExpressions, processMcBPlus, "Process MC for B+", false); + + void processMcB0(HfMcRecRedJPK0ss const& rowsJpsiK0StarMcRec, HfRedB02JpsiDaus const& b0) + { + fillMcRec(rowsJpsiK0StarMcRec, b0); + } + PROCESS_SWITCH(HfCandidateCreatorBToJpsiReducedExpressions, processMcB0, "Process MC for B0", false); void processMcBs(HfMcRecRedJPPhis const& rowsJpsiPhiMcRec, HfRedBs2JpsiDaus const& bs) { fillMcRec(rowsJpsiPhiMcRec, bs); } - PROCESS_SWITCH(HfCandidateCreatorBToJpsiReducedExpressions, processMcBs, "Process MC", false); + PROCESS_SWITCH(HfCandidateCreatorBToJpsiReducedExpressions, processMcBs, "Process MC for Bs", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx index 2ef89ce34c5..0278f36fd50 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBplusReduced.cxx @@ -62,6 +62,7 @@ struct HfCandidateCreatorBplusReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B+ is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowD0PiTolerance{"invMassWindowD0PiTolerance", 0.01, "invariant-mass window tolerance for D0Pi pair preselections (GeV/c2)"}; @@ -101,6 +102,7 @@ struct HfCandidateCreatorBplusReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassBplusToD0Pi", "2-prong candidates;inv. mass (B^{+} #rightarrow #overline{D^{0}}#pi^{#plus} #rightarrow #pi^{#minus}K^{#plus}#pi^{#plus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); @@ -198,7 +200,8 @@ struct HfCandidateCreatorBplusReduced { pVecD0[0], pVecD0[1], pVecD0[2], pVecPion[0], pVecPion[1], pVecPion[2], dcaD0.getY(), dcaPion.getY(), - std::sqrt(dcaD0.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2())); + std::sqrt(dcaD0.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2()), + candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()); rowCandidateProngs(candD0.globalIndex(), trackPion.globalIndex()); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx index 8bed227e12d..a46d274b8d5 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorBsReduced.cxx @@ -62,6 +62,7 @@ struct HfCandidateCreatorBsReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowDPiTolerance{"invMassWindowDPiTolerance", 0.01, "invariant-mass window tolerance for DsPi pair preselections (GeV/c2)"}; @@ -97,6 +98,7 @@ struct HfCandidateCreatorBsReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassBsToDsPi", "2-prong candidates;inv. mass (B^{0}_{s} #rightarrow D_{s}^{#minus}#pi^{#plus} #rightarrow #K^{#minus}K^{#plus}#pi^{#minus}#pi^{#plus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); diff --git a/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx index b1affb1cbac..bd7e4f64cc4 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorCharmResoReduced.cxx @@ -765,7 +765,7 @@ struct HfCandidateCreatorCharmResoReduced { V0TrRedTable const& candsV0Tr) { using BinningType = ColumnBinningPolicy; - BinningType const corrBinning{{cfgMixedEvent.zPoolBins, cfgMixedEvent.multPoolBins}, true}; + BinningType const corrBinning{{cfgMixedEvent.zPoolBins, cfgMixedEvent.multPoolBins}}; auto bachTuple = std::make_tuple(candsD, candsV0Tr); Pair const pairs{corrBinning, cfgMixedEvent.numberEventsMixed, cfgMixedEvent.numberEventsToSkip, collisions, bachTuple, &cache}; for (const auto& [collision1, bachDs, collision2, bachV0Trs] : pairs) { diff --git a/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx b/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx index 1b43d988285..64692dc72eb 100644 --- a/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateCreatorLbReduced.cxx @@ -64,6 +64,7 @@ struct HfCandidateCreatorLbReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any Lb is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; // selection Configurable invMassWindowLcPiTolerance{"invMassWindowLcPiTolerance", 0.01, "invariant-mass window tolerance for LcPi pair preselections (GeV/c2)"}; @@ -96,6 +97,7 @@ struct HfCandidateCreatorLbReduced { df2.setMinRelChi2Change(minRelChi2Change); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); + df2.setMaxChi2(maxChi2); // histograms registry.add("hMassLambdaB0ToLcPi", "2-prong candidates;inv. mass (#Lambda_{b}^{0} #rightarrow #Lambda_{c}^{#plus}#pi^{#minus} #rightarrow pK^{#minus}#pi^{#plus}#pi^{#minus}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{500, 3., 8.}}}); diff --git a/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx index 3913e7f0fca..93ec3498cd0 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorB0ToDPiReduced.cxx @@ -20,11 +20,13 @@ #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" #include "PWGHF/Utils/utilsPid.h" #include "Common/Core/TrackSelectorPID.h" #include +#include #include #include #include @@ -92,6 +94,7 @@ struct HfCandidateSelectorB0ToDPiReduced { Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_B0ToDPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable useDplusTriggerMassCut{"useDplusTriggerMassCut", false, "Flag to enable parametrize pT differential mass cut for triggered data"}; // variable that will store the value of selectionFlagD (defined in dataCreatorDplusPiReduced.cxx) int mySelectionFlagD = -1; @@ -101,6 +104,7 @@ struct HfCandidateSelectorB0ToDPiReduced { o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; + HfTrigger3ProngCuts hfTriggerCuts; using TracksBachPion = soa::Join; using TracksSoftPions = soa::Join; @@ -118,6 +122,10 @@ struct HfCandidateSelectorB0ToDPiReduced { LOGP(fatal, "Invalid PID option in configurable, please set 0 (no PID), 1 (TPC or TOF), or 2 (TPC and TOF)"); } + if (useDplusTriggerMassCut && (doprocessSelectionDstarPi || doprocessSelectionDstarPiWithDmesMl)) { + LOGP(fatal, "useDplusTriggerMassCut has no effect in the D*-pi channel. Please set it to false."); + } + if (pionPidMethod != PidMethod::NoPid) { selectorPion.setRangePtTpc(ptPidTpcMin, ptPidTpcMax); selectorPion.setRangeNSigmaTpc(-nSigmaTpcMax, nSigmaTpcMax); @@ -136,7 +144,7 @@ struct HfCandidateSelectorB0ToDPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -190,6 +198,28 @@ struct HfCandidateSelectorB0ToDPiReduced { return candidate.template prong1_as(); } + /// Utility function to apply the pT-differential mass cut for triggered data + /// on the D meson daughter of the B0 candidate + /// \param candidate is the B0 candidate + /// \return true if the D daughter passes the mass selection + /// \note the cut is parametrised for the D+ meson, hence it is not applied in the D*-pi channel + template + bool isDmesInMassRange(const T1& /*candidate*/) + { + return true; + } + + /// Utility function to apply the pT-differential mass cut for triggered data + /// on the D- daughter of the B0 candidate in the D-pi decay channel + /// \param candidate is the B0 candidate + /// \return true if the D- daughter passes the mass selection + template + bool isDmesInMassRange(const T1& candidate) + { + const auto candD = candidate.template prong0_as(); + return isCandidateInMassRange(candD.invMassHypo0(), o2::constants::physics::MassDPlus, candidate.ptProng0(), hfTriggerCuts); + } + /// Method to get the input features vector needed for ML inference /// \param candB0 is the B0 candidate /// \param prongBachPi is the candidate's bachelor pion prong @@ -238,6 +268,16 @@ struct HfCandidateSelectorB0ToDPiReduced { continue; } + // pT-differential mass cut on the D daughter for triggered data + if (useDplusTriggerMassCut && !isDmesInMassRange(hfCandB0)) { + hfSelB0ToDPiCandidate(statusB0ToDPi); + if (applyB0Ml) { + hfMlB0ToDPiCandidate(outputMlNotPreselected); + } + // LOGF(info, "B0 candidate selection failed at D-meson mass selection"); + continue; + } + if constexpr (WithDmesMl) { // we include it in the topological selections if (!HfHelper::selectionDmesMlScoresForBReduced(hfCandB0, cutsDmesMl, binsPtDmesMl)) { hfSelB0ToDPiCandidate(statusB0ToDPi); @@ -298,7 +338,8 @@ struct HfCandidateSelectorB0ToDPiReduced { void processSelectionDplusPi(HfRedCandB0 const& hfCandsB0, TracksBachPion const& pionTracks, - HfCandB0Configs const& configs) + HfCandB0Configs const& configs, + HfRed3Prongs const& /*candsD*/) { runSelection(hfCandsB0, pionTracks, configs); } // processSelectionDplusPi @@ -307,7 +348,8 @@ struct HfCandidateSelectorB0ToDPiReduced { void processSelectionDplusPiWithDmesMl(soa::Join const& hfCandsB0, TracksBachPion const& pionTracks, - HfCandB0Configs const& configs) + HfCandB0Configs const& configs, + HfRed3Prongs const& /*candsD*/) { runSelection(hfCandsB0, pionTracks, configs); } // processSelectionDplusPiWithDmesMl diff --git a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx index e9088c68e93..5673db5971b 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorBplusToD0PiReduced.cxx @@ -19,11 +19,13 @@ #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" #include "PWGHF/Utils/utilsPid.h" #include "Common/Core/TrackSelectorPID.h" #include +#include #include #include #include @@ -91,6 +93,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_BPLUSToD0Pi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable useD0TriggerMassCut{"useD0TriggerMassCut", false, "Flag to enable parametrize pT differential mass cut for triggered data"}; // variable that will store the value of selectionFlagD (defined in dataCreatorD0PiReduced.cxx) int mySelectionFlagD0 = -1; int mySelectionFlagD0bar = -1; @@ -101,6 +104,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; + HfTrigger2ProngCuts hfTriggerCuts; using TracksPion = soa::Join; @@ -135,7 +139,7 @@ struct HfCandidateSelectorBplusToD0PiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -154,6 +158,19 @@ struct HfCandidateSelectorBplusToD0PiReduced { } } + /// Utility function to apply the pT-differential mass cut for triggered data + /// on the D0 daughter of the B+ candidate + /// \param candidate is the B+ candidate + /// \param trackPi is the bachelor pion of the B+ candidate + /// \return true if the D0 daughter passes the mass selection + template + bool isDmesInMassRange(const T1& candidate, TracksPion::iterator const& trackPi) + { + const auto candD0 = candidate.template prong0_as(); + const auto invMassD0 = (trackPi.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); + return isCandidateInMassRange(invMassD0, o2::constants::physics::MassD0, candidate.ptProng0(), hfTriggerCuts); + } + /// Main function to perform B+ candidate creation /// \param withDmesMl is the flag to use the table with ML scores for the D- daughter (only possible if present in the derived data) /// \param hfCandsBp B+ candidates @@ -189,6 +206,18 @@ struct HfCandidateSelectorBplusToD0PiReduced { continue; } + auto trackPi = hfCandBp.template prong1_as(); + + // pT-differential mass cut on the D0 daughter for triggered data + if (useD0TriggerMassCut && !isDmesInMassRange(hfCandBp, trackPi)) { + hfSelBplusToD0PiCandidate(statusBplus); + if (applyBplusMl) { + hfMlBplusToD0PiCandidate(outputMlNotPreselected); + } + // LOGF(info, "B+ candidate selection failed at D0-meson mass selection"); + continue; + } + if constexpr (WithDmesMl) { // we include it in the topological selections if (!HfHelper::selectionDmesMlScoresForBReduced(hfCandBp, cutsDmesMl, binsPtDmesMl)) { hfSelBplusToD0PiCandidate(statusBplus); @@ -206,7 +235,6 @@ struct HfCandidateSelectorBplusToD0PiReduced { } // track-level PID selection - auto trackPi = hfCandBp.template prong1_as(); if (pionPidMethod == PidMethod::TpcOrTof || pionPidMethod == PidMethod::TpcAndTof) { int pidTrackPi{TrackSelectorPID::Status::NotApplicable}; if (pionPidMethod == PidMethod::TpcOrTof) { @@ -250,7 +278,8 @@ struct HfCandidateSelectorBplusToD0PiReduced { void processSelection(HfRedCandBplus const& hfCandsBp, TracksPion const& pionTracks, - HfCandBpConfigs const& configs) + HfCandBpConfigs const& configs, + HfRed2Prongs const& /*candsD*/) { runSelection(hfCandsBp, pionTracks, configs); } // processSelection @@ -259,7 +288,8 @@ struct HfCandidateSelectorBplusToD0PiReduced { void processSelectionWithDmesMl(soa::Join const& hfCandsBp, TracksPion const& pionTracks, - HfCandBpConfigs const& configs) + HfCandBpConfigs const& configs, + HfRed2Prongs const& /*candsD*/) { runSelection(hfCandsBp, pionTracks, configs); } // processSelectionWithDmesMl diff --git a/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx index 988df046c06..7a9e71e26bc 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorBsToDsPiReduced.cxx @@ -131,7 +131,7 @@ struct HfCandidateSelectorBsToDsPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx b/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx index 705f23ae97e..80b38931c13 100644 --- a/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/candidateSelectorLbToLcPiReduced.cxx @@ -132,7 +132,7 @@ struct HfCandidateSelectorLbToLcPiReduced { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx index 880fd3f62e5..0080ceb0715 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmHadPiReduced.cxx @@ -37,6 +37,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/Qvectors.h" @@ -176,6 +177,7 @@ struct HfDataCreatorCharmHadPiReduced { Configurable invMassWindowCharmHadPi{"invMassWindowCharmHadPi", 0.3, "invariant-mass window for CharmHad-Pi pair preselections (GeV/c2)"}; // MC extra Configurable checkDecayTypeMc{"checkDecayTypeMc", false, "flag to enable MC checks on decay type"}; + Configurable nMcLoops{"nMcLoops", 0, "number of extra loops over generated particles per MC collision"}; } configs; // vertexing struct : o2::framework::ConfigurableGroup { @@ -185,7 +187,8 @@ struct HfDataCreatorCharmHadPiReduced { Configurable maxR{"maxR", 200., "reject PCA's above this radius"}; Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; - Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old < this"}; + Configurable maxChi2{"maxChi2", 1.e9, "maximum chi2 for vertex reconstruction"}; } vertexConfigurations; // selection struct : o2::framework::ConfigurableGroup { @@ -195,6 +198,12 @@ struct HfDataCreatorCharmHadPiReduced { Configurable> binsPtPion{"binsPtPion", std::vector{hf_cuts_single_track::vecBinsPtTrack}, "track pT bin limits for pion DCA XY pT-dependent cut"}; Configurable> cutsTrackPionDCA{"cutsTrackPionDCA", {hf_cuts_single_track::CutsTrack[0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track selections per pT bin for pions"}; } trackPionConfigurations; + // B hadron selection + struct : o2::framework::ConfigurableGroup { + Configurable cpaMin{"cpaMin", 0., "Minimum cosine of pointing angle for B candidates"}; + Configurable decLenMin{"decLenMin", 0., "Minimum decay length for B candidates"}; + Configurable impParProductMax{"impParProductMax", 10., "Maximum product of the impact parameters of the B daughters (cm^2)"}; + } bhadronConfigurations; // HF flags struct : o2::framework::ConfigurableGroup { Configurable selectionFlagDplus{"selectionFlagDplus", 7, "Selection Flag for D+"}; @@ -244,7 +253,7 @@ struct HfDataCreatorCharmHadPiReduced { using CandsDstarFiltered = soa::Filtered>; using CandsDstarFilteredWithMl = soa::Filtered>; - using CollisionsWCent = soa::Join; + using CollisionsWCent = soa::Join; using CollisionsWCentAndMcLabels = soa::Join; using CollisionsWCentAndQvectors = soa::Join; using BCsInfo = soa::Join; @@ -271,7 +280,7 @@ struct HfDataCreatorCharmHadPiReduced { PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; } preslices; - std::shared_ptr hCandidatesD0, hCandidatesDPlus, hCandidatesDs, hCandidatesLc, hCandidatesD0FromDstar; + std::shared_ptr hCandidatesD0, hCandidatesDPlus, hCandidatesDs, hCandidatesLc, hCandidatesD0FromDstar, hCandidatesBHadron; HistogramRegistry registry{"registry"}; OutputObj zorroSummary{"zorroSummary"}; @@ -310,6 +319,16 @@ struct HfDataCreatorCharmHadPiReduced { invMass2ChHadPiMax = (massB + configs.invMassWindowCharmHadPi) * (massB + configs.invMassWindowCharmHadPi); // Initialize fitter + // D0 + B hadrons + df2.setPropagateToPCA(vertexConfigurations.propagateToPCA); + df2.setMaxR(vertexConfigurations.maxR); + df2.setMaxDZIni(vertexConfigurations.maxDZIni); + df2.setMinParamChange(vertexConfigurations.minParamChange); + df2.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); + df2.setUseAbsDCA(vertexConfigurations.useAbsDCA); + df2.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); + df2.setMaxChi2(vertexConfigurations.maxChi2); + df2.setMatCorrType(noMatCorr); if (doprocessDplusPiData || doprocessDplusPiDataWithMl || doprocessDplusPiDataWithQvec || doprocessDplusPiDataWithMlAndQvec || doprocessDplusPiMc || doprocessDplusPiMcWithMl || doprocessDsPiData || doprocessDsPiDataWithMl || doprocessDsPiDataWithQvec || doprocessDsPiDataWithMlAndQvec || doprocessDsPiMc || doprocessDsPiMcWithMl || doprocessLcPiData || doprocessLcPiDataWithMl || doprocessLcPiMc || doprocessLcPiMcWithMl) { @@ -320,17 +339,8 @@ struct HfDataCreatorCharmHadPiReduced { df3.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); df3.setUseAbsDCA(vertexConfigurations.useAbsDCA); df3.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); + df3.setMaxChi2(vertexConfigurations.maxChi2); df3.setMatCorrType(noMatCorr); - } else if (doprocessD0PiData || doprocessD0PiDataWithMl || doprocessD0PiDataWithQvec || doprocessD0PiDataWithMlAndQvec || doprocessD0PiMc || doprocessD0PiMcWithMl || - doprocessDstarPiData || doprocessDstarPiDataWithMl || doprocessDstarPiDataWithQvec || doprocessDstarPiDataWithMlAndQvec || doprocessDstarPiMc || doprocessDstarPiMcWithMl) { - df2.setPropagateToPCA(vertexConfigurations.propagateToPCA); - df2.setMaxR(vertexConfigurations.maxR); - df2.setMaxDZIni(vertexConfigurations.maxDZIni); - df2.setMinParamChange(vertexConfigurations.minParamChange); - df2.setMinRelChi2Change(vertexConfigurations.minRelChi2Change); - df2.setUseAbsDCA(vertexConfigurations.useAbsDCA); - df2.setWeightedFinalPCA(vertexConfigurations.useWeightedFinalPCA); - df2.setMatCorrType(noMatCorr); } // Configure CCDB access @@ -388,12 +398,17 @@ struct HfDataCreatorCharmHadPiReduced { hCandidatesDs = registry.add("hCandidatesDs", "Ds candidate counter", {HistType::kTH1D, {axisCands}}); hCandidatesLc = registry.add("hCandidatesLc", "Lc candidate counter", {HistType::kTH1D, {axisCands}}); hCandidatesD0FromDstar = registry.add("hCandidatesD0FromDstar", "D0 from D* candidate counter", {HistType::kTH1D, {axisCands}}); + hCandidatesBHadron = registry.add("hCandidatesBHadron", "B hadron candidate counter", {HistType::kTH1D, {axisCands}}); + if (configs.nMcLoops > 0) { + registry.add("hNMatchedMcLoops", "matches found in extra MC loops;matches per MC collision;entries", {HistType::kTH1D, {{100, -0.5, 99.5}}}); + } setLabelHistoCands(hCandidatesD0); setLabelHistoCands(hCandidatesDPlus); setLabelHistoCands(hCandidatesDs); setLabelHistoCands(hCandidatesLc); setLabelHistoCands(hCandidatesD0FromDstar); + setLabelHistoCands(hCandidatesBHadron); // init HF event selection helper hfEvSel.init(registry, &zorroSummary); @@ -488,7 +503,7 @@ struct HfDataCreatorCharmHadPiReduced { const PParticles& particlesMc, const std::vector& vecDaughtersB, int& indexHfCandCharm, - std::map selectedTracksPion, + const std::map& selectedTracksPion, const int64_t indexCollisionMaxNumContrib) { @@ -619,7 +634,7 @@ struct HfDataCreatorCharmHadPiReduced { } tables.rowHfDPiMcCheckReduced(pdgCodeBeautyMother, pdgCodeCharmMother, pdgCodeProng0, pdgCodeProng1, pdgCodeProng2, pdgCodeProng3); } - tables.rowHfDPiMcRecReduced(indexHfCandCharm, selectedTracksPion[vecDaughtersB.back().globalIndex()], flag, flagWrongCollision, debug, motherPt); + tables.rowHfDPiMcRecReduced(indexHfCandCharm, selectedTracksPion.at(vecDaughtersB.back().globalIndex()), flag, flagWrongCollision, debug, motherPt); } else if constexpr (DecChannel == DecayChannel::BsToDsminusPi) { // Bs → Ds- π+ → (K- K+ π-) π+ auto indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kBS, std::array{-kKPlus, +kKPlus, -kPiPlus, +kPiPlus}, true, &sign, 3); @@ -774,7 +789,7 @@ struct HfDataCreatorCharmHadPiReduced { } tables.rowHfDsPiMcCheckReduced(pdgCodeBeautyMother, pdgCodeCharmMother, pdgCodeProng0, pdgCodeProng1, pdgCodeProng2, pdgCodeProng3); } - tables.rowHfDsPiMcRecReduced(indexHfCandCharm, selectedTracksPion[vecDaughtersB.back().globalIndex()], flag, flagWrongCollision, debug, motherPt); + tables.rowHfDsPiMcRecReduced(indexHfCandCharm, selectedTracksPion.at(vecDaughtersB.back().globalIndex()), flag, flagWrongCollision, debug, motherPt); } else if constexpr (DecChannel == DecayChannel::BplusToD0barPi) { // B+ → D0(bar) π+ → (K+ π-) π+ auto indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2]}, Pdg::kBPlus, std::array{+kPiPlus, +kKPlus, -kPiPlus}, true, &sign, 2); @@ -871,7 +886,7 @@ struct HfDataCreatorCharmHadPiReduced { } tables.rowHfD0PiMcCheckReduced(pdgCodeBeautyMother, pdgCodeCharmMother, pdgCodeProng0, pdgCodeProng1, pdgCodeProng2); } - tables.rowHfD0PiMcRecReduced(indexHfCandCharm, selectedTracksPion[vecDaughtersB.back().globalIndex()], flag, flagWrongCollision, debug, motherPt); + tables.rowHfD0PiMcRecReduced(indexHfCandCharm, selectedTracksPion.at(vecDaughtersB.back().globalIndex()), flag, flagWrongCollision, debug, motherPt); } else if constexpr (DecChannel == DecayChannel::LbToLcplusPi) { // Lb → Lc+ π- → (p K- π+) π- auto indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kLambdaB0, std::array{+kProton, -kKPlus, +kPiPlus, -kPiPlus}, true, &sign, 3); @@ -975,7 +990,7 @@ struct HfDataCreatorCharmHadPiReduced { } tables.rowHfLcPiMcCheckReduced(pdgCodeBeautyMother, pdgCodeCharmMother, pdgCodeProng0, pdgCodeProng1, pdgCodeProng2, pdgCodeProng3); } - tables.rowHfLcPiMcRecReduced(indexHfCandCharm, selectedTracksPion[vecDaughtersB.back().globalIndex()], flag, flagWrongCollision, debug, motherPt); + tables.rowHfLcPiMcRecReduced(indexHfCandCharm, selectedTracksPion.at(vecDaughtersB.back().globalIndex()), flag, flagWrongCollision, debug, motherPt); } else if constexpr (DecChannel == DecayChannel::B0ToDstarPi) { // B0 → D*+ π- → (D0 π+) π- → (K- π+ π+) π- auto indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kB0, std::array{+kKPlus, -kPiPlus, -kPiPlus, +kPiPlus}, true, &sign, 4); @@ -1012,8 +1027,34 @@ struct HfDataCreatorCharmHadPiReduced { checkWrongCollision(particleMother, collision, indexCollisionMaxNumContrib, flagWrongCollision); } } - tables.rowHfDStarPiMcRecReduced(indexHfCandCharm, selectedTracksPion[vecDaughtersB.back().globalIndex()], flag, flagWrongCollision, debug, motherPt); + tables.rowHfDStarPiMcRecReduced(indexHfCandCharm, selectedTracksPion.at(vecDaughtersB.back().globalIndex()), flag, flagWrongCollision, debug, motherPt); + } + } + + /// B hadron preselections + /// \param momentum is the B meson momentum + /// \param secondaryVertex is the reconstructed secondary vertex + /// \param collision is the reconstructed collision + /// \param impParProduct is the product of the impact parameters of the B daughters + template + bool isBHadronSelected(const T1& momentum, const T2& secondaryVertex, const T3& collision, const float impParProduct) + { + // B candidate CPA + if (RecoDecay::cpa(std::array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex, momentum) < bhadronConfigurations.cpaMin) { + return false; } + + // B candidate decay length + if (RecoDecay::distance(std::array{collision.posX(), collision.posY(), collision.posZ()}, secondaryVertex) < bhadronConfigurations.decLenMin) { + return false; + } + + // B candidate product of daughter impact parameters + if (impParProduct > bhadronConfigurations.impParProductMax) { + return false; + } + + return true; } template @@ -1306,12 +1347,36 @@ struct HfDataCreatorCharmHadPiReduced { } registry.fill(HIST("hPtPion"), trackParCovPion.getPt()); + // compute invariant mass square and apply selection auto invMass2DPi = RecoDecay::m2(std::array{pVecCharm, pVecPion}, std::array{massC, MassPiPlus}); if ((invMass2DPi < invMass2ChHadPiMin) || (invMass2DPi > invMass2ChHadPiMax)) { continue; } + std::array pVecBHad{}, pVecDHad{}, pVecDau{}; + + hCandidatesBHadron->Fill(SVFitting::BeforeFit); + try { + if (df2.process(trackParCovCharmHad, trackParCovPion) == 0) { + continue; + } + } catch (const std::runtime_error& error) { + LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; + hCandidatesBHadron->Fill(SVFitting::Fail); + continue; + } + hCandidatesBHadron->Fill(SVFitting::FitOk); + auto secondaryVertexBHad = df2.getPCACandidate(); + df2.getTrack(0).getPxPyPzGlo(pVecDHad); + df2.getTrack(1).getPxPyPzGlo(pVecDau); + pVecBHad = RecoDecay::pVec(pVecDHad, pVecDau); + o2::track::TrackParCov trackParCovCharmHadAtPv = trackParCovCharmHad; + std::array dcaCharmHad{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParCovCharmHadAtPv, 2.f, noMatCorr, &dcaCharmHad); + if (!isBHadronSelected(pVecBHad, secondaryVertexBHad, collision, dcaCharmHad[0] * dcaPion[0])) { + continue; + } // fill Pion tracks table // if information on track already stored, go to next track if (!selectedTracksPion.count(trackPion.globalIndex())) { @@ -1379,7 +1444,10 @@ struct HfDataCreatorCharmHadPiReduced { if constexpr (WithMl) { std::array mlScores = {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; if constexpr (DecChannel == DecayChannel::B0ToDminusPi) { - tables.hfCand3ProngMl(candC.mlProbDplusToPiKPi()[0], candC.mlProbDplusToPiKPi()[1], candC.mlProbDplusToPiKPi()[2], -1., -1., -1.); + if (candC.mlProbDplusToPiKPi().size() == NSizeMLScore) { + std::copy(candC.mlProbDplusToPiKPi().begin(), candC.mlProbDplusToPiKPi().end(), mlScores.begin()); + } + tables.hfCand3ProngMl(mlScores[0], mlScores[1], mlScores[2], -1., -1., -1.); } else if constexpr (DecChannel == DecayChannel::BsToDsminusPi) { if (candC.mlProbDsToKKPi().size() == NSizeMLScore) { std::copy(candC.mlProbDsToKKPi().begin(), candC.mlProbDsToKKPi().end(), mlScores.begin()); @@ -1444,7 +1512,7 @@ struct HfDataCreatorCharmHadPiReduced { tables.hfCandPidProng1(candC.nSigTpcPi1(), candC.nSigTofPi1(), candC.nSigTpcKa1(), candC.nSigTofKa1(), nSigmaTpcPr1, nSigmaTofPr1, charmHadDauTracks[1].hasTOF(), charmHadDauTracks[1].hasTPC()); // Soft pion tables - auto trackSoftPion = charmHadDauTracks.back(); + const auto& trackSoftPion = charmHadDauTracks.back(); auto trackParCovSoftPion = getTrackParCov(trackSoftPion); std::array dcaSoftPion{trackSoftPion.dcaXY(), trackSoftPion.dcaZ()}; std::array pVecSoftPion = trackSoftPion.pVector(); @@ -1742,6 +1810,20 @@ struct HfDataCreatorCharmHadPiReduced { ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); } } // gen + + // extra loops over generated particles (increases CPU time) + if (configs.nMcLoops > 0) { + int nMatched{0}; + for (int iLoop = 0; iLoop < configs.nMcLoops; ++iLoop) { + for (const auto& particle : mcParticlesPerMcColl) { + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kB0, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBPlus, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBS, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + nMatched += RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kLambdaB0, std::array{-static_cast(Pdg::kDPlus), +kPiPlus}, true); + } + } + registry.fill(HIST("hNMatchedMcLoops"), nMatched); + } } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmResoToD0Reduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmResoToD0Reduced.cxx index 6526044e6d2..c993fbda899 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmResoToD0Reduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmResoToD0Reduced.cxx @@ -28,6 +28,7 @@ #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -133,7 +134,7 @@ struct HfDataCreatorCharmResoToD0Reduced { using TracksIUWithPIDAndMC = soa::Join; // Collisions MC using BCsInfo = soa::Join; - using McCollisionsNoCents = soa::Join; + using McCollisionsNoCents = soa::Join; Filter filterSelectD0Candidates = (aod::hf_sel_candidate_d0::isSelD0 >= cfgDmesCuts.selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= cfgDmesCuts.selectionFlagD0Bar); @@ -193,7 +194,7 @@ struct HfDataCreatorCharmResoToD0Reduced { // Process functions // No ML // Data - void processD0V0(soa::Join const& collisions, + void processD0V0(soa::Join const& collisions, CandsD0Filtered const& candsD0, aod::V0s const& v0s, TracksIUWithPID const& tracksIU, @@ -221,14 +222,14 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0V0, "Process D0 candidates paired with V0s", false); - void processD0Track(soa::Join const& collisions, + void processD0Track(soa::Join const& collisions, CandsD0Filtered const& candsD0, aod::TrackAssoc const& trackIndices, TracksWithPID const& tracks, @@ -256,14 +257,14 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0Track, "Process D0 candidates paired with Tracks", false); - void processD0V0AndTrack(soa::Join const& collisions, + void processD0V0AndTrack(soa::Join const& collisions, CandsD0Filtered const& candsD0, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -294,7 +295,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto candsDThisColl = candsD0.sliceBy(candsD0PerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); @@ -303,7 +304,7 @@ struct HfDataCreatorCharmResoToD0Reduced { // ML // Data - void processD0V0WithMl(soa::Join const& collisions, + void processD0V0WithMl(soa::Join const& collisions, CandsD0FilteredWithMl const& candsD0, aod::V0s const& v0s, TracksIUWithPID const& tracksIU, @@ -331,14 +332,14 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD2PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD2PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0V0WithMl, "Process D0 candidates paired with V0s with ML info", false); - void processD0GammaWithMl(soa::Join const& collisions, + void processD0GammaWithMl(soa::Join const& collisions, CandsD0FilteredWithMl const& candsD0, aod::V0s const& v0s, TracksIUWithElPID const& tracksIU, @@ -366,14 +367,14 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD2PrMl, &vDriftMgr); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD2PrMl, &vDriftMgr); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0GammaWithMl, "Process D0 candidates paired with gammas with ML info", false); - void processD0TrackWithMl(soa::Join const& collisions, + void processD0TrackWithMl(soa::Join const& collisions, CandsD0FilteredWithMl const& candsD0, aod::TrackAssoc const& trackIndices, TracksWithPID const& tracks, @@ -401,14 +402,14 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollisionWithMl, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, hfCandD2PrMl); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, hfCandD2PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0TrackWithMl, "Process D0 candidates paired with Tracks with ML info", false); - void processD0V0AndTrackWithMl(soa::Join const& collisions, + void processD0V0AndTrackWithMl(soa::Join const& collisions, CandsD0FilteredWithMl const& candsD0, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -439,7 +440,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto candsDThisColl = candsD0.sliceBy(candsD0PerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, hfCandD2PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, hfCandD2PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); @@ -448,7 +449,7 @@ struct HfDataCreatorCharmResoToD0Reduced { // MC // No ML - void processD0V0MC(soa::Join const& collisions, + void processD0V0MC(soa::Join const& collisions, CandsD0FilteredWithMc const& candsD0, aod::V0s const& v0s, TracksIUWithPIDAndMC const& tracksIU, @@ -479,7 +480,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, rowHf2PrV0McRecReduced, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, rowHf2PrV0McRecReduced, dummyTable, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -487,7 +488,7 @@ struct HfDataCreatorCharmResoToD0Reduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0V0MC, "Process D0 candidates paired with V0s with MC matching", false); - void processD0TrackMC(soa::Join const& collisions, + void processD0TrackMC(soa::Join const& collisions, CandsD0FilteredWithMc const& candsD0, TracksWithPIDAndMC const& tracks, aod::TrackAssoc const& trackIndices, @@ -518,7 +519,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf2PrTrkMcRecReduced, dummyTable); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf2PrTrkMcRecReduced, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -526,7 +527,7 @@ struct HfDataCreatorCharmResoToD0Reduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0TrackMC, "Process D0 candidates paired with tracks with MC matching", false); - void processD0V0AndTrackMC(soa::Join const& collisions, + void processD0V0AndTrackMC(soa::Join const& collisions, CandsD0FilteredWithMc const& candsD0, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -560,7 +561,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto candsDThisColl = candsD0.sliceBy(candsD0PerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, rowHf2PrV0McRecReduced, rowHf2PrTrkMcRecReduced, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, rowHf2PrV0McRecReduced, rowHf2PrTrkMcRecReduced, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -569,7 +570,7 @@ struct HfDataCreatorCharmResoToD0Reduced { PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0V0AndTrackMC, "Process D0 candidates paired with V0s and tracks with MC matching", false); // ML - void processD0V0MCWithMl(soa::Join const& collisions, + void processD0V0MCWithMl(soa::Join const& collisions, CandsD0FilteredWithMlAndMc const& candsD0, aod::V0s const& v0s, TracksIUWithPIDAndMC const& tracksIU, @@ -600,7 +601,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, rowHf2PrV0McRecReduced, dummyTable, hfCandD2PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, dummyTable, rowHf2PrV0McRecReduced, dummyTable, hfCandD2PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -608,7 +609,7 @@ struct HfDataCreatorCharmResoToD0Reduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0V0MCWithMl, "Process D0 candidates paired with V0s with MC matching and with ML info", false); - void processD0TrackMCWithMl(soa::Join const& collisions, + void processD0TrackMCWithMl(soa::Join const& collisions, CandsD0FilteredWithMlAndMc const& candsD0, TracksWithPIDAndMC const& tracks, aod::TrackAssoc const& trackIndices, @@ -639,7 +640,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsD0.sliceBy(candsD0PerCollisionWithMl, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf2PrTrkMcRecReduced, hfCandD2PrMl); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf2PrTrkMcRecReduced, hfCandD2PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -647,7 +648,7 @@ struct HfDataCreatorCharmResoToD0Reduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToD0Reduced, processD0TrackMCWithMl, "Process D0 candidates paired with tracks with MC matching and with ML info", false); - void processD0V0AndTrackMCWithMl(soa::Join const& collisions, + void processD0V0AndTrackMCWithMl(soa::Join const& collisions, CandsD0FilteredWithMlAndMc const& candsD0, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -680,7 +681,7 @@ struct HfDataCreatorCharmResoToD0Reduced { auto candsDThisColl = candsD0.sliceBy(candsD0PerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, rowHf2PrV0McRecReduced, rowHf2PrTrkMcRecReduced, hfCandD2PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgGammaCuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD2Pr, hfCandV0, hfTrackNoParam, rowHf2PrV0McRecReduced, rowHf2PrTrkMcRecReduced, hfCandD2PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDplusReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDplusReduced.cxx index 71c1f6b49cc..79505392ace 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDplusReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDplusReduced.cxx @@ -27,6 +27,7 @@ #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -128,7 +129,7 @@ struct HfDataCreatorCharmResoToDplusReduced { using TracksIUWithPIDAndMC = soa::Join; // Collisions MC using BCsInfo = soa::Join; - using McCollisionsNoCents = soa::Join; + using McCollisionsNoCents = soa::Join; Filter filterSelectDplus = (aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= cfgDmesCuts.selectionFlagDplus); @@ -187,7 +188,7 @@ struct HfDataCreatorCharmResoToDplusReduced { // Process functions // No ML // Data - void processDplusV0(soa::Join const& collisions, + void processDplusV0(soa::Join const& collisions, CandsDplusFiltered const& candsDplus, aod::V0s const& v0s, TracksIUWithPID const& tracksIU, @@ -215,14 +216,14 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusV0, "Process Dplus candidates paired with V0s", false); - void processDplusTrack(soa::Join const& collisions, + void processDplusTrack(soa::Join const& collisions, CandsDplusFiltered const& candsDplus, aod::TrackAssoc const& trackIndices, TracksWithPID const& tracks, @@ -250,14 +251,14 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusTrack, "Process Dplus candidates paired with Tracks", false); - void processDplusV0AndTrack(soa::Join const& collisions, + void processDplusV0AndTrack(soa::Join const& collisions, CandsDplusFiltered const& candsDplus, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -288,7 +289,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); @@ -297,7 +298,7 @@ struct HfDataCreatorCharmResoToDplusReduced { // ML // Data - void processDplusV0WithMl(soa::Join const& collisions, + void processDplusV0WithMl(soa::Join const& collisions, CandsDplusFilteredWithMl const& candsDplus, aod::V0s const& v0s, TracksIUWithPID const& tracksIU, @@ -325,14 +326,14 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD3PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusV0WithMl, "Process Dplus candidates paired with V0s with ML info", false); - void processDplusTrackWithMl(soa::Join const& collisions, + void processDplusTrackWithMl(soa::Join const& collisions, CandsDplusFilteredWithMl const& candsDplus, aod::TrackAssoc const& trackIndices, TracksWithPID const& tracks, @@ -360,14 +361,14 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollisionWithMl, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusTrackWithMl, "Process Dplus candidates paired with Tracks with ML info", false); - void processDplusV0AndTrackWithMl(soa::Join const& collisions, + void processDplusV0AndTrackWithMl(soa::Join const& collisions, CandsDplusFilteredWithMl const& candsDplus, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -398,7 +399,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); @@ -406,7 +407,7 @@ struct HfDataCreatorCharmResoToDplusReduced { PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusV0AndTrackWithMl, "Process Dplus candidates paired with V0s and Tracks with ML info", false); // Dplus - void processDplusV0MC(soa::Join const& collisions, + void processDplusV0MC(soa::Join const& collisions, CandsDplusFilteredWithMc const& candsDplus, aod::V0s const& v0s, TracksIUWithPIDAndMC const& tracksIU, @@ -437,7 +438,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, rowHf3PrV0McRecReduced, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, rowHf3PrV0McRecReduced, dummyTable, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -445,7 +446,7 @@ struct HfDataCreatorCharmResoToDplusReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusV0MC, "Process Dplus candidates paired with V0s with MC matching", false); - void processDplusTrackMC(soa::Join const& collisions, + void processDplusTrackMC(soa::Join const& collisions, CandsDplusFilteredWithMc const& candsDplus, TracksWithPIDAndMC const& tracks, aod::TrackAssoc const& trackIndices, @@ -476,7 +477,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf3PrTrkMcRecReduced, dummyTable); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf3PrTrkMcRecReduced, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -484,7 +485,7 @@ struct HfDataCreatorCharmResoToDplusReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusTrackMC, "Process Dplus candidates paired with tracks with MC matching", false); - void processDplusV0AndTrackMC(soa::Join const& collisions, + void processDplusV0AndTrackMC(soa::Join const& collisions, CandsDplusFilteredWithMc const& candsDplus, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -518,7 +519,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, rowHf3PrV0McRecReduced, rowHf3PrTrkMcRecReduced, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, rowHf3PrV0McRecReduced, rowHf3PrTrkMcRecReduced, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -527,7 +528,7 @@ struct HfDataCreatorCharmResoToDplusReduced { PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusV0AndTrackMC, "Process Dplus candidates paired with V0s and tracks with MC matching", false); // ML - void processDplusV0MCWithMl(soa::Join const& collisions, + void processDplusV0MCWithMl(soa::Join const& collisions, CandsDplusFilteredWithMlAndMc const& candsDplus, aod::V0s const& v0s, TracksIUWithPIDAndMC const& tracksIU, @@ -558,7 +559,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, rowHf3PrV0McRecReduced, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, dummyTable, rowHf3PrV0McRecReduced, dummyTable, hfCandD3PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -566,7 +567,7 @@ struct HfDataCreatorCharmResoToDplusReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusV0MCWithMl, "Process Dplus candidates paired with V0s with MC matching and with ML info", false); - void processDplusTrackMCWithMl(soa::Join const& collisions, + void processDplusTrackMCWithMl(soa::Join const& collisions, CandsDplusFilteredWithMlAndMc const& candsDplus, TracksWithPIDAndMC const& tracks, aod::TrackAssoc const& trackIndices, @@ -597,7 +598,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollisionWithMl, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf3PrTrkMcRecReduced, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, dummyTable, hfTrackNoParam, dummyTable, rowHf3PrTrkMcRecReduced, hfCandD3PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -605,7 +606,7 @@ struct HfDataCreatorCharmResoToDplusReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDplusReduced, processDplusTrackMCWithMl, "Process Dplus candidates paired with tracks with MC matching and with ML info", false); - void processDplusV0AndTrackMCWithMl(soa::Join const& collisions, + void processDplusV0AndTrackMCWithMl(soa::Join const& collisions, CandsDplusFilteredWithMlAndMc const& candsDplus, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -638,7 +639,7 @@ struct HfDataCreatorCharmResoToDplusReduced { auto candsDThisColl = candsDplus.sliceBy(candsDplusPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, rowHf3PrV0McRecReduced, rowHf3PrTrkMcRecReduced, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandD3Pr, hfCandV0, hfTrackNoParam, rowHf3PrV0McRecReduced, rowHf3PrTrkMcRecReduced, hfCandD3PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions diff --git a/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDstarReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDstarReduced.cxx index d8939b6a765..d3777632e78 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDstarReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorCharmResoToDstarReduced.cxx @@ -27,6 +27,7 @@ #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -127,7 +128,7 @@ struct HfDataCreatorCharmResoToDstarReduced { using TracksIUWithPIDAndMC = soa::Join; // Collisions MC using BCsInfo = soa::Join; - using McCollisionsNoCents = soa::Join; + using McCollisionsNoCents = soa::Join; Filter filterSelectedCandDstar = (aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == cfgDmesCuts.selectionFlagDstarToD0Pi); @@ -186,7 +187,7 @@ struct HfDataCreatorCharmResoToDstarReduced { // Process functions // No ML // Data - void processDstarV0(soa::Join const& collisions, + void processDstarV0(soa::Join const& collisions, CandsDstarFiltered const& candsDstar, aod::V0s const& v0s, TracksIUWithPID const& tracksIU, @@ -215,14 +216,14 @@ struct HfDataCreatorCharmResoToDstarReduced { auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarV0, "Process Dstar candidates paired with V0s", true); - void processDstarTrack(soa::Join const& collisions, + void processDstarTrack(soa::Join const& collisions, CandsDstarFiltered const& candsDstar, aod::TrackAssoc const& trackIndices, TracksWithPID const& tracks, @@ -251,14 +252,14 @@ struct HfDataCreatorCharmResoToDstarReduced { auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarTrack, "Process Dstar candidates paired with Tracks", false); - void processDstarV0AndTrack(soa::Join const& collisions, + void processDstarV0AndTrack(soa::Join const& collisions, CandsDstarFiltered const& candsDstar, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -289,7 +290,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, dummyTable); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); @@ -298,7 +299,7 @@ struct HfDataCreatorCharmResoToDstarReduced { // ML // Data - void processDstarV0WithMl(soa::Join const& collisions, + void processDstarV0WithMl(soa::Join const& collisions, CandsDstarFilteredWithMl const& candsDstar, aod::V0s const& v0s, TracksIUWithPID const& tracksIU, @@ -326,14 +327,14 @@ struct HfDataCreatorCharmResoToDstarReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, dummyTable, dummyTable, hfCandD3PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarV0WithMl, "Process Dstar candidates paired with V0s with ML info", false); - void processDstarTrackWithMl(soa::Join const& collisions, + void processDstarTrackWithMl(soa::Join const& collisions, CandsDstarFilteredWithMl const& candsDstar, aod::TrackAssoc const& trackIndices, TracksWithPID const& tracks, @@ -361,14 +362,14 @@ struct HfDataCreatorCharmResoToDstarReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollisionWithMl, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarTrackWithMl, "Process Dstar candidates paired with Tracks with ML info", false); - void processDstarV0AndTrackWithMl(soa::Join const& collisions, + void processDstarV0AndTrackWithMl(soa::Join const& collisions, CandsDstarFilteredWithMl const& candsDstar, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -399,7 +400,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, nullptr, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, dummyTable, dummyTable, hfCandD3PrMl); } // handle normalization by the right number of collisions hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); @@ -408,7 +409,7 @@ struct HfDataCreatorCharmResoToDstarReduced { // MC // No ML - void processDstarV0MC(soa::Join const& collisions, + void processDstarV0MC(soa::Join const& collisions, CandsDstarFilteredWithMc const& candsDstar, aod::V0s const& v0s, TracksIUWithPIDAndMC const& tracksIU, @@ -439,7 +440,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, rowHfDstarV0McRecReduced, dummyTable, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, rowHfDstarV0McRecReduced, dummyTable, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -447,7 +448,7 @@ struct HfDataCreatorCharmResoToDstarReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarV0MC, "Process Dstar candidates paired with V0s with MC matching", false); - void processDstarTrackMC(soa::Join const& collisions, + void processDstarTrackMC(soa::Join const& collisions, CandsDstarFilteredWithMc const& candsDstar, TracksWithPIDAndMC const& tracks, aod::TrackAssoc const& trackIndices, @@ -478,7 +479,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, rowHfDstarTrkMcRecReduced, dummyTable); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, rowHfDstarTrkMcRecReduced, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -486,7 +487,7 @@ struct HfDataCreatorCharmResoToDstarReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarTrackMC, "Process Dstar candidates paired with tracks with MC matching", false); - void processDstarV0AndTrackMC(soa::Join const& collisions, + void processDstarV0AndTrackMC(soa::Join const& collisions, CandsDstarFilteredWithMc const& candsDstar, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -520,7 +521,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollision, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, rowHfDstarV0McRecReduced, rowHfDstarTrkMcRecReduced, dummyTable); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, rowHfDstarV0McRecReduced, rowHfDstarTrkMcRecReduced, dummyTable); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -529,7 +530,7 @@ struct HfDataCreatorCharmResoToDstarReduced { PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarV0AndTrackMC, "Process Dstar candidates paired with V0s and tracks with MC matching", false); // ML - void processDstarV0MCWithMl(soa::Join const& collisions, + void processDstarV0MCWithMl(soa::Join const& collisions, CandsDstarFilteredWithMlAndMc const& candsDstar, aod::V0s const& v0s, TracksIUWithPIDAndMC const& tracksIU, @@ -560,7 +561,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, rowHfDstarV0McRecReduced, dummyTable, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, nullptr, tracksIU, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, dummyTable, rowHfDstarV0McRecReduced, dummyTable, hfCandD3PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -568,7 +569,7 @@ struct HfDataCreatorCharmResoToDstarReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarV0MCWithMl, "Process Dstar candidates paired with V0s with MC matching and with ML info", false); - void processDstarTrackMCWithMl(soa::Join const& collisions, + void processDstarTrackMCWithMl(soa::Join const& collisions, CandsDstarFilteredWithMlAndMc const& candsDstar, TracksWithPIDAndMC const& tracks, aod::TrackAssoc const& trackIndices, @@ -599,7 +600,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto thisCollId = collision.globalIndex(); auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollisionWithMl, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, rowHfDstarTrkMcRecReduced, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, nullptr, trackIdsThisColl, tracks, tracks, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, dummyTable, hfTrackNoParam, dummyTable, rowHfDstarTrkMcRecReduced, hfCandD3PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions @@ -607,7 +608,7 @@ struct HfDataCreatorCharmResoToDstarReduced { } PROCESS_SWITCH(HfDataCreatorCharmResoToDstarReduced, processDstarTrackMCWithMl, "Process Dstar candidates paired with tracks with MC matching and with ML info", false); - void processDstarV0AndTrackMCWithMl(soa::Join const& collisions, + void processDstarV0AndTrackMCWithMl(soa::Join const& collisions, CandsDstarFilteredWithMlAndMc const& candsDstar, aod::V0s const& v0s, aod::TrackAssoc const& trackIndices, @@ -640,7 +641,7 @@ struct HfDataCreatorCharmResoToDstarReduced { auto candsDThisColl = candsDstar.sliceBy(candsDstarPerCollisionWithMl, thisCollId); auto v0sThisColl = v0s.sliceBy(candsV0PerCollision, thisCollId); auto trackIdsThisColl = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); - runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, rowHfDstarV0McRecReduced, rowHfDstarTrkMcRecReduced, hfCandD3PrMl); + runDataCreation>(collision, candsDThisColl, v0sThisColl, trackIdsThisColl, tracks, tracksIU, particlesMc, hfRejMap, bz, pdg, registry, matCorr, fitter, cfgDmesCuts, cfgSingleTrackCuts, cfgV0Cuts, cfgV0Cuts, cfgQaPlots, rejectPairsWithCommonDaughter, hfReducedCollision, hfCandDstar, hfCandV0, hfTrackNoParam, rowHfDstarV0McRecReduced, rowHfDstarTrkMcRecReduced, hfCandD3PrMl); } runMcGen(particlesMc, mcParticlesPerMcCollision, collInfos, colPerMcCollision, mcCollisions, hfEvSelMc, rejectCollisionsWithBadEvSel, registry, pdg, rowHfResoMcGenReduced, bcs); // handle normalization by the right number of collisions diff --git a/PWGHF/D2H/TableProducer/dataCreatorHiddenCharmReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorHiddenCharmReduced.cxx index ef0171a7fd9..dc71ed37260 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorHiddenCharmReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorHiddenCharmReduced.cxx @@ -27,6 +27,7 @@ #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -76,9 +77,10 @@ struct HfDataCreatorHiddenCharmReduced { Produces hfTrackLite; struct : ConfigurableGroup { - // track quality Configurable fillHistograms{"fillHistograms", true, "Fill proton QA histograms"}; + Configurable fillSparses{"fillSparses", false, "Flag to enable sparse filling"}; Configurable selectProtons{"selectProtons", true, "Select protons"}; + // track quality Configurable itsNClsMin{"itsNClsMin", 5, "Minimum number of ITS clusters"}; Configurable tpcNClsFoundMin{"tpcNClsFoundMin", 50, "Minimum number of found TPC clusters"}; Configurable tpcNClsCrossedRowsMin{"tpcNClsCrossedRowsMin", 80, "Minimum number of crossed TPC rows"}; @@ -90,6 +92,7 @@ struct HfDataCreatorHiddenCharmReduced { Configurable> binsPtTrack{"binsPtTrack", std::vector{hf_cuts_single_track::vecBinsPtTrack}, "Track pT bin limits for DCA cuts"}; Configurable> cutsTrack{"cutsTrack", {hf_cuts_single_track::CutsTrack[0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track DCA selections per pT bin"}; // DCA + Configurable applyPtDependentDcaTrackSelection{"applyPtDependentDcaTrackSelection", false, "Flag to enable pT-dependent DCAxy and DCAz selection on single tracks"}; Configurable> paramsDCAxyPtDep{"paramsDCAxyPtDep", std::vector{0.0010, 0.0080, 0.73}, "Parameters for pT-dependent DCAxy cut: p0, p1, p2 for cut = p0 + p1/pt^p2"}; Configurable> paramsDCAzPtDep{"paramsDCAzPtDep", std::vector{-0.0044, 0.0152, 0.47}, "Parameters for pT-dependent DCAz cut: p0, p1, p2 for cut = p0 + p1/pt^p2"}; // PID @@ -145,19 +148,32 @@ struct HfDataCreatorHiddenCharmReduced { if (config.fillHistograms) { const AxisSpec axisPt{360, 0., 36., "#it{p}_{T}^{proton} (GeV/#it{c})"}; + const AxisSpec axisPtEtaC{100, 0., 10., "#it{p}_{T}^{#eta_{c}} (GeV/#it{c})"}; const AxisSpec axisEta{100, -1., 1., "#eta"}; const AxisSpec axisDca{400, -2., 2., "DCA_{xy} to primary vertex (cm)"}; - const AxisSpec axisNSigma{100, -5., 5., "n#sigma"}; + const AxisSpec axisNSigmaTPC{100, -5., 5., "n#sigma_{TPC}"}; + const AxisSpec axisNSigmaTOF{100, -5., 5., "n#sigma_{TOF}"}; + const AxisSpec axisMass{100, 2.85, 3.25, "p#overline{p} invariant mass (GeV/#it{c}^{2})"}; registry.add("hPzVtx", "Z position of primary vertex for selected tracks;z_{vtx} (cm);entries", {HistType::kTH1D, {AxisSpec{200, -20., 20., "z_{vtx} (cm)"}}}); registry.add("hPtNoCuts", "All associated tracks;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPt}}); registry.add("hPtCutsProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPt}}); registry.add("hEtaCutsProton", "Selected proton tracks;#eta;entries", {HistType::kTH1D, {axisEta}}); registry.add("hDCAToPrimXYVsPtCutsProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});DCA_{xy} to primary vertex (cm)", {HistType::kTH2D, {axisPt, axisDca}}); - registry.add("hNSigmaTPCProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});n#sigma_{TPC}", {HistType::kTH2D, {axisPt, axisNSigma}}); - registry.add("hNSigmaTOFProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});n#sigma_{TOF}", {HistType::kTH2D, {axisPt, axisNSigma}}); - registry.add("hInvMass", "Invariant mass of selected proton with all other tracks in the event;#it{p}_{T}^{proton} (GeV/#it{c});invariant mass with other tracks (GeV/#it{c}^{2})", {HistType::kTH2D, {axisPt, AxisSpec{100, 2.85, 3.25, "invariant mass with other tracks (GeV/#it{c}^{2})"}}}); + registry.add("hNSigmaTPCProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});n#sigma_{TPC}^{p}", {HistType::kTH2D, {axisPt, axisNSigmaTPC}}); + registry.add("hNSigmaTOFProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});n#sigma_{TOF}^{p}", {HistType::kTH2D, {axisPt, axisNSigmaTOF}}); + registry.add("hNSigmaTPCKaon", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); n#sigma_{TPC}^{K}", {HistType::kTH2D, {axisPt, axisNSigmaTPC}}); + registry.add("hNSigmaTOFKaon", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); n#sigma_{TOF}^{K}", {HistType::kTH2D, {axisPt, axisNSigmaTOF}}); + registry.add("hNSigmaTPCPion", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); n#sigma_{TPC}^{#pi}", {HistType::kTH2D, {axisPt, axisNSigmaTPC}}); + registry.add("hNSigmaTOFPion", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); n#sigma_{TOF}^{#pi}", {HistType::kTH2D, {axisPt, axisNSigmaTOF}}); + registry.add("hTpcNClsFound", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); TpcNClsFound", {HistType::kTH2D, {axisPt, {200, 0., 200, "TpcNClsFound"}}}); + registry.add("hTpcNClsCrossedRows", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); TpcNClsCrossedRows", {HistType::kTH2D, {axisPt, {200, 0., 200, "TpcNClsCrossedRows"}}}); + registry.add("hTpcChi2NCl", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); TpcChi2NCl", {HistType::kTH2D, {axisPt, {10, 0., 10, "TpcChi2NCl"}}}); + registry.add("hItsNCls", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});ItsNCls", {HistType::kTH2D, {axisPt, {7, 0, 7, "ItsNCls"}}}); + registry.add("hItsChi2NCl", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c}); ItsChi2NCl", {HistType::kTH2D, {axisPt, {10, 0., 10, "ItsChi2NCl"}}}); + registry.add("hInvMass", "Invariant mass of selected protons pairs;#it{p}_{T}^{#eta_{c}} (GeV/#it{c});", {HistType::kTH2D, {axisPtEtaC, axisMass}}); registry.add("hDeDxTPCProton", "Selected proton tracks;#it{p}_{T}^{track} (GeV/#it{c});TPC dE/dx (a.u.)", {HistType::kTH2D, {axisPt, AxisSpec{100, 0., 200., "TPC dE/dx (a.u.)"}}}); + registry.add("hMassPtCutVars", "Charmonia candidates;#it{M} (p#overline{p}) (GeV/#it{c}^{2});#it{p}_{T}(cc) (GeV/#it{c}); p_{T}^{p}; n#sigma_{TPC}^{p}; n#sigma_{TOF}^{p}; dca_{xy}^{p}; p_{T}^{#overline{p}}; n#sigma_{TPC}^{#overline{p}}; n#sigma_{TOF}^{#overline{p}}; dca_{xy}^{#overline{p}}", {HistType::kTHnSparseF, {axisMass, axisPtEtaC, axisPt, axisNSigmaTPC, axisNSigmaTOF, axisDca, axisPt, axisNSigmaTPC, axisNSigmaTOF, axisDca}}); } // init HF event selection helper @@ -266,13 +282,15 @@ struct HfDataCreatorHiddenCharmReduced { if (!isSelectedTrackDca(config.binsPtTrack, config.cutsTrack, track.pt(), track.dcaXY(), track.dcaZ())) { return false; } - if (dcaSigma(track.pt(), config.paramsDCAxyPtDep.value[0], config.paramsDCAxyPtDep.value[1], config.paramsDCAxyPtDep.value[2]) > std::abs(dcaXY) || dcaSigma(track.pt(), config.paramsDCAzPtDep.value[0], config.paramsDCAzPtDep.value[1], config.paramsDCAzPtDep.value[2]) > std::abs(dcaZ)) { - return false; + if (config.applyPtDependentDcaTrackSelection.value) { + if (std::abs(dcaXY) > dcaSigma(track.pt(), config.paramsDCAxyPtDep.value[0], config.paramsDCAxyPtDep.value[1], config.paramsDCAxyPtDep.value[2]) || std::abs(dcaZ) > dcaSigma(track.pt(), config.paramsDCAzPtDep.value[0], config.paramsDCAzPtDep.value[1], config.paramsDCAzPtDep.value[2])) { + return false; + } } return isSelectedPid(track); } - void processEtaCTrack(soa::Join const& collisions, + void processEtaCTrack(soa::Join const& collisions, aod::TrackAssoc const& trackIndices, TracksWithPID const& tracks, aod::BCsWithTimestamps const&) @@ -304,37 +322,60 @@ struct HfDataCreatorHiddenCharmReduced { if (!isSelectedTrack(trk)) { continue; } - std::array pVecProton{trk.pVector()}; - hfTrackLite(trk.globalIndex(), collision.globalIndex(), pVecProton[0], pVecProton[1], pVecProton[2], trk.sign(), static_cast(TrackType::Proton)); - selectedTrackIds.push_back(trk.globalIndex()); + selectedTrackIds.push_back(trkId.trackId()); + if (config.fillHistograms) { registry.fill(HIST("hPtCutsProton"), trk.pt()); registry.fill(HIST("hEtaCutsProton"), trk.eta()); registry.fill(HIST("hDCAToPrimXYVsPtCutsProton"), trk.pt(), trk.dcaXY()); registry.fill(HIST("hNSigmaTPCProton"), trk.pt(), trk.tpcNSigmaPr()); - registry.fill(HIST("hDeDxTPCProton"), trk.pt(), trk.tpcSignal()); + registry.fill(HIST("hNSigmaTPCPion"), trk.pt(), trk.tpcNSigmaPi()); + registry.fill(HIST("hNSigmaTPCKaon"), trk.pt(), trk.tpcNSigmaKa()); if (trk.hasTOF()) { registry.fill(HIST("hNSigmaTOFProton"), trk.pt(), trk.tofNSigmaPr()); + registry.fill(HIST("hNSigmaTOFPion"), trk.pt(), trk.tofNSigmaPi()); + registry.fill(HIST("hNSigmaTOFKaon"), trk.pt(), trk.tofNSigmaKa()); } + registry.fill(HIST("hTpcNClsFound"), trk.pt(), trk.tpcNClsFound()); + registry.fill(HIST("hTpcNClsCrossedRows"), trk.pt(), trk.tpcNClsCrossedRows()); + registry.fill(HIST("hTpcChi2NCl"), trk.pt(), trk.tpcChi2NCl()); + registry.fill(HIST("hItsNCls"), trk.pt(), trk.itsNCls()); + registry.fill(HIST("hItsChi2NCl"), trk.pt(), trk.itsChi2NCl()); + registry.fill(HIST("hDeDxTPCProton"), trk.pt(), trk.tpcSignal()); } } + // skip event if not at least a pair of tracks skimming selection if (selectedTrackIds.size() < NDaughtersCharmMeson) { continue; } hfReducedCollision(collision.posX(), collision.posY(), collision.posZ(), collision.numContrib(), hfRejMap, bz); - for (size_t i = 0; i < selectedTrackIds.size(); ++i) { - auto t1 = tracks.rawIteratorAt(selectedTrackIds[i]); - std::array pVec1{t1.pVector()}; - for (size_t j = i + 1; j < selectedTrackIds.size(); ++j) { - auto t2 = tracks.rawIteratorAt(selectedTrackIds[j]); - if (t1.sign() * t2.sign() > 0) { - continue; - } - std::array pVec2{t2.pVector()}; - float invMass = RecoDecay::m(std::array{pVec1, pVec2}, std::array{o2::constants::physics::MassProton, o2::constants::physics::MassProton}); - float ptEtac = RecoDecay::pt(RecoDecay::sumOfVec(pVec1, pVec2)); - if (config.fillHistograms) { + + auto reducedEventId = hfReducedCollision.lastIndex(); + for (const auto& trkId : selectedTrackIds) { + auto track = tracks.rawIteratorAt(trkId); + std::array pVec{track.pVector()}; + hfTrackLite(track.globalIndex(), reducedEventId, pVec[0], pVec[1], pVec[2], track.sign(), static_cast(TrackType::Proton)); + } + + if (config.fillHistograms.value) { + for (size_t i = 0; i < selectedTrackIds.size(); ++i) { + auto t1 = tracks.rawIteratorAt(selectedTrackIds[i]); + std::array pVec1{t1.pVector()}; + for (size_t j = i + 1; j < selectedTrackIds.size(); ++j) { + auto t2 = tracks.rawIteratorAt(selectedTrackIds[j]); + if (t1.sign() * t2.sign() > 0) + continue; + std::array pVec2{t2.pVector()}; + float invMass = RecoDecay::m(std::array{pVec1, pVec2}, std::array{o2::constants::physics::MassProton, o2::constants::physics::MassProton}); + float ptEtac = RecoDecay::pt(RecoDecay::sumOfVec(pVec1, pVec2)); registry.fill(HIST("hInvMass"), ptEtac, invMass); + if (config.fillSparses.value) { + if (t1.sign() > 0) { + registry.fill(HIST("hMassPtCutVars"), invMass, ptEtac, t1.pt(), t1.tpcNSigmaPr(), t1.tofNSigmaPr(), t1.dcaXY(), t2.pt(), t2.tpcNSigmaPr(), t2.tofNSigmaPr(), t2.dcaXY()); + } else { + registry.fill(HIST("hMassPtCutVars"), invMass, ptEtac, t2.pt(), t2.tpcNSigmaPr(), t2.tofNSigmaPr(), t2.dcaXY(), t1.pt(), t1.tpcNSigmaPr(), t1.tofNSigmaPr(), t1.dcaXY()); + } + } } } } diff --git a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx index c6ab8136c26..49ca42dcee4 100644 --- a/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx +++ b/PWGHF/D2H/TableProducer/dataCreatorJpsiHadReduced.cxx @@ -34,6 +34,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -59,12 +60,14 @@ #include #include #include -#include #include #include #include +#include + +#include #include #include #include @@ -102,6 +105,18 @@ enum WrongCollisionType : uint8_t { SplitCollision, }; +std::map const partlyRecoDecayMapMuMu = { + {Pdg::kB0, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::B0ToJpsiXToMuMuX}, + {Pdg::kBPlus, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BplusToJpsiXToMuMuX}, + {Pdg::kBS, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BsToJpsiXToMuMuX}, + {Pdg::kLambdaB0, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::LbToJpsiXToMuMuX}}; + +std::map const partlyRecoDecayMapEE = { + {Pdg::kB0, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::B0ToJpsiXToEEX}, + {Pdg::kBPlus, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BplusToJpsiXToEEX}, + {Pdg::kBS, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::BsToJpsiXToEEX}, + {Pdg::kLambdaB0, hf_decay::hf_cand_beauty::DecayChannelToJpsiMain::LbToJpsiXToEEX}}; + /// Creation of Jpsi-Had pairs for Beauty hadrons struct HfDataCreatorJpsiHadReduced { // Produces AOD tables to store track information @@ -114,6 +129,7 @@ struct HfDataCreatorJpsiHadReduced { // J/Psi related tables Produces hfJpsi; Produces hfRedJpsiCov; + Produces hfRedJpsiDauPid; // Ka bachelor related tables Produces hfTrackLfDau0; Produces hfTrackCovLfDau0; @@ -121,11 +137,14 @@ struct HfDataCreatorJpsiHadReduced { Produces hfTrackCovLfDau1; // MC related tables Produces rowHfJpsiKMcRecReduced; + Produces rowHfJpsiK0StarMcRecReduced; Produces rowHfJpsiPhiMcRecReduced; Produces rowHfBpMcGenReduced; + Produces rowHfB0McGenReduced; Produces rowHfBsMcGenReduced; - Produces rowCandidateConfigBplus; + Produces rowCandidateConfigBplus; + Produces rowCandidateConfigB0; Produces rowCandidateConfigBs; Configurable skipRejectedCollisions{"skipRejectedCollisions", true, "skips collisions rejected by the event selection, instead of flagging only"}; @@ -136,37 +155,15 @@ struct HfDataCreatorJpsiHadReduced { Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any B0 is smaller than this"}; Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations is chi2/chi2old > this"}; + Configurable maxChi2JPsiVtx{"maxChi2JPsiVtx", 1e9, "maximum value of chi2 for JPsi vertex computed with DCAFitter"}; + Configurable maxChi2BhadVtx{"maxChi2BhadVtx", 0.9, "maximum value of chi2 for B-hadron vertex computed with DCAFitter"}; - Configurable runJpsiToee{"runJpsiToee", false, "Run analysis for J/Psi to ee (debug)"}; struct : o2::framework::ConfigurableGroup { // TPC PID Configurable ptPidTpcMin{"ptPidTpcMin", 0.15, "Lower bound of track pT for TPC PID"}; Configurable ptPidTpcMax{"ptPidTpcMax", 5., "Upper bound of track pT for TPC PID"}; - Configurable nSigmaTpcElMax{"nSigmaTpcElMax", 4., "Electron nsigma cut on TPC only"}; - Configurable nSigmaTpcPiMin{"nSigmaTpcPiMin", 2.5, "Pion nsigma cut on TPC only"}; - Configurable nSigmaTpcPiMax{"nSigmaTpcPiMax", 99., "Pion nsigma cut on TPC only"}; - Configurable nSigmaTpcPrMin{"nSigmaTpcPrMin", -99., "Proton nsigma cut on TPC only"}; - Configurable nSigmaTpcPrMax{"nSigmaTpcPrMax", 99, "Proton nsigma cut on TPC only"}; - Configurable nSigmaTpcElCombinedMax{"nSigmaTpcElCombinedMax", 4., "Electron Nsigma cut on TPC combined with TOF"}; - Configurable nSigmaTpcPiCombinedMin{"nSigmaTpcPiCombinedMin", 2.5, "Pion Nsigma cut on TPC combined with TOF"}; - Configurable nSigmaTpcPiCombinedMax{"nSigmaTpcPiCombinedMax", 99., "Pion Nsigma cut on TPC combined with TOF"}; - Configurable nSigmaTpcPrCombinedMin{"nSigmaTpcPrCombinedMin", -99., "Proton Nsigma cut on TPC combined with TOF"}; - Configurable nSigmaTpcPrCombinedMax{"nSigmaTpcPrCombinedMax", 99, "Proton Nsigma cut on TPC combined with TOF"}; - // TOF PID - Configurable ptPidTofMin{"ptPidTofMin", 0.15, "Lower bound of track pT for TOF PID"}; - Configurable ptPidTofMax{"ptPidTofMax", 5., "Upper bound of track pT for TOF PID"}; - Configurable nSigmaTofElMax{"nSigmaTofElMax", 4., "Electron nsigma cut on TPC only"}; - Configurable nSigmaTofPiMin{"nSigmaTofPiMin", -99, "Pion nsigma cut on TPC only"}; - Configurable nSigmaTofPiMax{"nSigmaTofPiMax", 99., "Pion nsigma cut on TPC only"}; - Configurable nSigmaTofPrMin{"nSigmaTofPrMin", -99, "Proton nsigma cut on TPC only"}; - Configurable nSigmaTofPrMax{"nSigmaTofPrMax", 99., "Proton nsigma cut on TPC only"}; - Configurable nSigmaTofElCombinedMax{"nSigmaTofElCombinedMax", 4., "Electron Nsigma cut on TOF combined with TPC"}; - Configurable nSigmaTofPiCombinedMin{"nSigmaTofPiCombinedMin", 2.5, "Pion Nsigma cut on TOF combined with TPC"}; - Configurable nSigmaTofPiCombinedMax{"nSigmaTofPiCombinedMax", 99., "Pion Nsigma cut on TOF combined with TPC"}; - Configurable nSigmaTofPrCombinedMin{"nSigmaTofPrCombinedMin", -99., "Proton Nsigma cut on TOF combined with TPC"}; - Configurable nSigmaTofPrCombinedMax{"nSigmaTofPrCombinedMax", 99, "Proton Nsigma cut on TOF combined with TPC"}; - // AND logic for TOF+TPC PID (as in Run2) - Configurable usePidTpcAndTof{"usePidTpcAndTof", true, "Use AND logic for TPC and TOF PID"}; + Configurable nSigmaTpcElMinForVeto{"nSigmaTpcElMinForVeto", -1., "Electron nsigma cut on TPC only for veto (electrons within min and max are discarded)"}; + Configurable nSigmaTpcElMaxForVeto{"nSigmaTpcElMaxForVeto", 1., "Electron nsigma cut on TPC only for veto (electrons within min and max are discarded)"}; } selectionsPid; Configurable ptJpsiMin{"ptJpsiMin", 0., "Lower bound of J/Psi pT"}; Configurable ptJpsiMax{"ptJpsiMax", 50., "Upper bound of J/Psi pT"}; @@ -174,21 +171,21 @@ struct HfDataCreatorJpsiHadReduced { Configurable ptTrackMin{"ptTrackMin", 0.5, "minimum bachelor track pT threshold (GeV/c)"}; Configurable absEtaTrackMax{"absEtaTrackMax", 0.8, "maximum bachelor track absolute eta threshold"}; Configurable> binsPtTrack{"binsPtTrack", std::vector{hf_cuts_single_track::vecBinsPtTrack}, "track pT bin limits for bachelor track DCA XY pT-dependent cut"}; - Configurable> cutsTrackDCA{"cutsTrackDCA", {hf_cuts_single_track::CutsTrack[0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track selections per pT bin for bachelor track"}; + Configurable> cutsTrackDCA{"cutsTrackDCA", {&hf_cuts_single_track::CutsTrack[0][0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track selections per pT bin for bachelor track"}; // topological/kinematic cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_jpsi_to_mu_mu::vecBinsPt}, "J/Psi pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_jpsi_to_mu_mu::Cuts[0], hf_cuts_jpsi_to_mu_mu::NBinsPt, hf_cuts_jpsi_to_mu_mu::NCutVars, hf_cuts_jpsi_to_mu_mu::labelsPt, hf_cuts_jpsi_to_mu_mu::labelsCutVar}, "J/Psi candidate selection per pT bin"}; + Configurable> cuts{"cuts", {&hf_cuts_jpsi_to_mu_mu::Cuts[0][0], hf_cuts_jpsi_to_mu_mu::NBinsPt, hf_cuts_jpsi_to_mu_mu::NCutVars, hf_cuts_jpsi_to_mu_mu::labelsPt, hf_cuts_jpsi_to_mu_mu::labelsCutVar}, "J/Psi candidate selection per pT bin"}; Configurable invMassWindowJpsiHad{"invMassWindowJpsiHad", 0.3, "invariant-mass window for Jpsi-Had pair preselections (GeV/c2)"}; Configurable deltaMPhiMax{"deltaMPhiMax", 0.02, "invariant-mass window for phi preselections (GeV/c2) (only for Bs->J/PsiPhi)"}; + Configurable deltaMK0StarMax{"deltaMK0StarMax", 0.15, "invariant-mass window for K*0 preselections (GeV/c2) (only for B0->J/PsiK0*)"}; Configurable cpaMin{"cpaMin", 0., "Minimum cosine of pointing angle for B candidates"}; Configurable decLenMin{"decLenMin", 0., "Minimum decay length for B candidates"}; + Configurable checkDecayTypeMc{"checkDecayTypeMc", false, "flag to enable MC checks on decay type"}; // magnetic field setting from CCDB Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; - TrackSelectorPi selectorPion; - TrackSelectorPr selectorProton; TrackSelectorEl selectorElectron; // CCDB service @@ -196,10 +193,10 @@ struct HfDataCreatorJpsiHadReduced { // O2DatabasePDG service Service pdg{}; - using TracksPid = soa::Join; + using TracksPid = soa::Join; using TracksPidWithSel = soa::Join; using TracksPidWithSelAndMc = soa::Join; - using CollisionsWCMcLabels = soa::Join; + using CollisionsWCMcLabels = soa::Join; using BCsInfo = soa::Join; Preslice candsJpsiPerCollision = aod::track_association::collisionId; @@ -212,6 +209,7 @@ struct HfDataCreatorJpsiHadReduced { double bz{0.}; double invMass2JpsiHadMin{}, invMass2JpsiHadMax{}; bool isHfCandBhadConfigFilled = false; + static constexpr int nProngsJpsi = 2; o2::hf_evsel::HfEventSelection hfEvSel; o2::hf_evsel::HfEventSelectionMc hfEvSelMc; @@ -225,72 +223,74 @@ struct HfDataCreatorJpsiHadReduced { void init(InitContext& initContext) { - selectorPion.setRangePtTpc(selectionsPid.ptPidTpcMin, selectionsPid.ptPidTpcMax); - selectorPion.setRangeNSigmaTpc(-selectionsPid.nSigmaTpcPiMax, selectionsPid.nSigmaTpcPiMax); - selectorPion.setRangeNSigmaTpcCondTof(-selectionsPid.nSigmaTpcPiCombinedMax, selectionsPid.nSigmaTpcPiCombinedMax); - selectorPion.setRangePtTof(selectionsPid.ptPidTofMin, selectionsPid.ptPidTofMax); - selectorPion.setRangeNSigmaTof(-selectionsPid.nSigmaTofPiMax, selectionsPid.nSigmaTofPiMax); - selectorPion.setRangeNSigmaTofCondTpc(-selectionsPid.nSigmaTofPiCombinedMax, selectionsPid.nSigmaTofPiCombinedMax); - - selectorProton.setRangePtTpc(selectionsPid.ptPidTpcMin, selectionsPid.ptPidTpcMax); - selectorProton.setRangeNSigmaTpc(-selectionsPid.nSigmaTpcPrMax, selectionsPid.nSigmaTpcPrMax); - selectorProton.setRangeNSigmaTpcCondTof(-selectionsPid.nSigmaTpcPrCombinedMax, selectionsPid.nSigmaTpcPrCombinedMax); - selectorProton.setRangePtTof(selectionsPid.ptPidTofMin, selectionsPid.ptPidTofMax); - selectorProton.setRangeNSigmaTof(-selectionsPid.nSigmaTofPrMax, selectionsPid.nSigmaTofPrMax); - selectorProton.setRangeNSigmaTofCondTpc(-selectionsPid.nSigmaTofPrCombinedMax, selectionsPid.nSigmaTofPrCombinedMax); - selectorElectron.setRangePtTpc(selectionsPid.ptPidTpcMin, selectionsPid.ptPidTpcMax); - selectorElectron.setRangeNSigmaTpc(-selectionsPid.nSigmaTpcElMax, selectionsPid.nSigmaTpcElMax); - selectorElectron.setRangeNSigmaTpcCondTof(-selectionsPid.nSigmaTofElCombinedMax, selectionsPid.nSigmaTofElCombinedMax); - selectorElectron.setRangePtTof(selectionsPid.ptPidTofMin, selectionsPid.ptPidTofMax); - selectorElectron.setRangeNSigmaTof(-selectionsPid.nSigmaTofElMax, selectionsPid.nSigmaTofElMax); - selectorElectron.setRangeNSigmaTofCondTpc(-selectionsPid.nSigmaTofElCombinedMax, selectionsPid.nSigmaTofElCombinedMax); - - std::array doProcess = {doprocessJpsiKData, doprocessJpsiKMc, doprocessJpsiPhiData, doprocessJpsiPhiMc}; - if (std::accumulate(doProcess.begin(), doProcess.end(), 0) != 1) { - LOGP(fatal, "One and only one process function can be enabled at a time, please fix your configuration!"); + selectorElectron.setRangeNSigmaTpc(selectionsPid.nSigmaTpcElMinForVeto, selectionsPid.nSigmaTpcElMaxForVeto); + + if (doprocessJpsiKData && doprocessJpsiKMc) { + LOGP(fatal, "Cannot enable both J/Psi-K data and MC processing at the same time, please fix your configuration!"); + } + if (doprocessJpsiK0StarData && doprocessJpsiK0StarMc) { + LOGP(fatal, "Cannot enable both J/Psi-K*0 data and MC processing at the same time, please fix your configuration!"); + } + if (doprocessJpsiPhiData && doprocessJpsiPhiMc) { + LOGP(fatal, "Cannot enable both J/Psi-Phi data and MC processing at the same time, please fix your configuration!"); + } + + std::array doProcess = {doprocessJpsiKData, doprocessJpsiKMc, doprocessJpsiK0StarData, doprocessJpsiK0StarMc, doprocessJpsiPhiData, doprocessJpsiPhiMc}; + if (std::accumulate(doProcess.begin(), doProcess.end(), 0) == 0) { + LOGP(fatal, "Enable at least one process function, please fix your configuration!"); } // Set up the histogram registry constexpr int NumBinsSelections = 2 + aod::SelectionStep::RecoPID; - std::string labels[NumBinsSelections]; + std::array labels; labels[0] = "No selection"; labels[1 + aod::SelectionStep::RecoSkims] = "Skims selection"; labels[1 + aod::SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {NumBinsSelections, 0.5, NumBinsSelections + 0.5, ""}; - registry.add("hSelectionsJpsi", "J/Psi selection;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelectionsJpsi", "J/Psi selection;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < NumBinsSelections; ++iBin) { registry.get(HIST("hSelectionsJpsi"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } constexpr int NumBinsEvents = NEvent; - std::string labelsEvents[NumBinsEvents]; + std::array labelsEvents; labelsEvents[Event::Processed] = "processed"; labelsEvents[Event::NoCharmHadPiSelected] = "without CharmHad-Pi pairs"; labelsEvents[Event::CharmHadPiSelected] = "with CharmHad-Pi pairs"; static const AxisSpec axisEvents = {NumBinsEvents, 0.5, NumBinsEvents + 0.5, ""}; - registry.add("hEvents", "Events;;entries", HistType::kTH1F, {axisEvents}); + registry.add("hEvents", "Events;;entries", HistType::kTH1D, {axisEvents}); for (int iBin = 0; iBin < NumBinsEvents; iBin++) { registry.get(HIST("hEvents"))->GetXaxis()->SetBinLabel(iBin + 1, labelsEvents[iBin].data()); } - registry.add("hMassJpsi", "J/Psi mass;#it{M}_{#mu#mu} (GeV/#it{c}^{2});Counts", {HistType::kTH1F, {{600, 2.8, 3.4, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtJpsi", "J/Psi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {{(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hCpaJpsi", "J/Psi cos#theta_{p};J/Psi cos#theta_{p};Counts", {HistType::kTH1F, {{200, -1., 1, "J/Psi cos#theta_{p}"}}}); + registry.add("hMassJpsi", "J/Psi mass;#it{M}_{#mu#mu} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{600, 2.8, 3.4, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hPtJpsi", "J/Psi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hCpaJpsi", "J/Psi cos#theta_{p};J/Psi cos#theta_{p};Counts", {HistType::kTH1D, {{200, -1., 1, "J/Psi cos#theta_{p}"}}}); std::shared_ptr hFitCandidatesJpsi = registry.add("hFitCandidatesJpsi", "Jpsi candidate counter", {HistType::kTH1D, {axisCands}}); std::shared_ptr hFitCandidatesBPlus = registry.add("hFitCandidatesBPlus", "hFitCandidatesBPlus candidate counter", {HistType::kTH1D, {axisCands}}); + std::shared_ptr hFitCandidatesB0 = registry.add("hFitCandidatesB0", "hFitCandidatesB0 candidate counter", {HistType::kTH1D, {axisCands}}); std::shared_ptr hFitCandidatesBS = registry.add("hFitCandidatesBS", "hFitCandidatesBS candidate counter", {HistType::kTH1D, {axisCands}}); setLabelHistoCands(hFitCandidatesJpsi); setLabelHistoCands(hFitCandidatesBPlus); + setLabelHistoCands(hFitCandidatesB0); setLabelHistoCands(hFitCandidatesBS); if (doprocessJpsiKData || doprocessJpsiKMc) { - registry.add("hPtKaon", "Kaon #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {{100, 0., 10.}}}); - registry.add("hMassJpsiKaon", "J/Psi Kaon mass;#it{M}_{J/#PsiK} (GeV/#it{c}^{2});Counts", {HistType::kTH1F, {{800, 4.9, 5.7}}}); - } else if (doprocessJpsiPhiData || doprocessJpsiPhiMc) { - registry.add("hPtPhi", "Phi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1F, {{100, 0., 10.}}}); - registry.add("hMassPhi", "Phi mass;#it{M}_{KK} (GeV/#it{c}^{2});Counts", {HistType::kTH1F, {{200, 0.9, 1.2}}}); - registry.add("hMassJpsiPhi", "J/Psi Phi mass;#it{M}_{J/#Psi#phi} (GeV/#it{c}^{2});Counts", {HistType::kTH1F, {{800, 4.9, 5.7}}}); + registry.add("hPtKaon", "Kaon #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{100, 0., 10.}}}); + registry.add("hMassJpsiKaon", "J/Psi Kaon mass;#it{M}_{J/#PsiK} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{800, 4.9, 5.7}}}); + } + if (doprocessJpsiK0StarData || doprocessJpsiK0StarMc) { + registry.add("hPtK0Star", "K*0 #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{100, 0., 10.}}}); + registry.add("hMassK0Star", "K*0 mass;#it{M}_{#piK} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{400, 0.6, 1.2}}}); + registry.add("hMassJpsiK0Star", "J/Psi K*0 mass;#it{M}_{J/#PsiK*0} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{800, 4.9, 5.7}}}); + std::shared_ptr hFitCandidatesK0Star = registry.add("hFitCandidatesK0Star", "K*0 candidate counter", {HistType::kTH1D, {axisCands}}); + setLabelHistoCands(hFitCandidatesK0Star); + } + if (doprocessJpsiPhiData || doprocessJpsiPhiMc) { + registry.add("hPtPhi", "Phi #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", {HistType::kTH1D, {{100, 0., 10.}}}); + registry.add("hMassPhi", "Phi mass;#it{M}_{KK} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{400, 0.9, 1.2}}}); + registry.add("hMassJpsiPhi", "J/Psi Phi mass;#it{M}_{J/#Psi#phi} (GeV/#it{c}^{2});Counts", {HistType::kTH1D, {{800, 4.9, 5.7}}}); std::shared_ptr hFitCandidatesPhi = registry.add("hFitCandidatesPhi", "Phi candidate counter", {HistType::kTH1D, {axisCands}}); setLabelHistoCands(hFitCandidatesPhi); } @@ -300,27 +300,32 @@ struct HfDataCreatorJpsiHadReduced { df2.setMaxDZIni(maxDZIni); df2.setMinParamChange(minParamChange); df2.setMinRelChi2Change(minRelChi2Change); + df2.setMaxChi2(maxChi2JPsiVtx); df2.setUseAbsDCA(useAbsDCA); df2.setWeightedFinalPCA(useWeightedFinalPCA); df2.setMatCorrType(noMatCorr); - df3.setPropagateToPCA(propagateToPCA); - df3.setMaxR(maxR); - df3.setMaxDZIni(maxDZIni); - df3.setMinParamChange(minParamChange); - df3.setMinRelChi2Change(minRelChi2Change); - df3.setUseAbsDCA(useAbsDCA); - df3.setWeightedFinalPCA(useWeightedFinalPCA); - df3.setMatCorrType(noMatCorr); - - df4.setPropagateToPCA(propagateToPCA); - df4.setMaxR(maxR); - df4.setMaxDZIni(maxDZIni); - df4.setMinParamChange(minParamChange); - df4.setMinRelChi2Change(minRelChi2Change); - df4.setUseAbsDCA(useAbsDCA); - df4.setWeightedFinalPCA(useWeightedFinalPCA); - df4.setMatCorrType(noMatCorr); + if (doprocessJpsiKData || doprocessJpsiKMc) { + df3.setPropagateToPCA(propagateToPCA); + df3.setMaxR(maxR); + df3.setMaxDZIni(maxDZIni); + df3.setMinParamChange(minParamChange); + df3.setMinRelChi2Change(minRelChi2Change); + df3.setMaxChi2(maxChi2BhadVtx); + df3.setUseAbsDCA(useAbsDCA); + df3.setWeightedFinalPCA(useWeightedFinalPCA); + df3.setMatCorrType(noMatCorr); + } else { + df4.setPropagateToPCA(propagateToPCA); + df4.setMaxR(maxR); + df4.setMaxDZIni(maxDZIni); + df4.setMinParamChange(minParamChange); + df4.setMinRelChi2Change(minRelChi2Change); + df4.setMaxChi2(maxChi2BhadVtx); + df4.setUseAbsDCA(useAbsDCA); + df4.setWeightedFinalPCA(useWeightedFinalPCA); + df4.setMatCorrType(noMatCorr); + } // Configure CCDB access ccdb->setURL(ccdbUrl); @@ -331,6 +336,9 @@ struct HfDataCreatorJpsiHadReduced { if (doprocessJpsiKData || doprocessJpsiKMc) { invMass2JpsiHadMin = (MassBPlus - invMassWindowJpsiHad) * (MassBPlus - invMassWindowJpsiHad); invMass2JpsiHadMax = (MassBPlus + invMassWindowJpsiHad) * (MassBPlus + invMassWindowJpsiHad); + } else if (doprocessJpsiK0StarData || doprocessJpsiK0StarMc) { + invMass2JpsiHadMin = (MassB0 - invMassWindowJpsiHad) * (MassB0 - invMassWindowJpsiHad); + invMass2JpsiHadMax = (MassB0 + invMassWindowJpsiHad) * (MassB0 + invMassWindowJpsiHad); } else if (doprocessJpsiPhiData || doprocessJpsiPhiMc) { invMass2JpsiHadMin = (MassBS - invMassWindowJpsiHad) * (MassBS - invMassWindowJpsiHad); invMass2JpsiHadMax = (MassBS + invMassWindowJpsiHad) * (MassBS + invMassWindowJpsiHad); @@ -359,7 +367,7 @@ struct HfDataCreatorJpsiHadReduced { bool selectionTopol(const T1& candidate, const T2& trackPos, const T2& trackNeg) { auto candpT = candidate.pt(); - auto candInvMass = runJpsiToee ? HfHelper::invMassJpsiToEE(candidate) : HfHelper::invMassJpsiToMuMu(candidate); + auto candInvMass = HfHelper::invMassJpsiToMuMu(candidate); auto pseudoPropDecLen = candidate.decayLengthXY() * candInvMass / candpT; auto pTBin = findBin(binsPt, candpT); if (pTBin == -1) { @@ -444,26 +452,11 @@ struct HfDataCreatorJpsiHadReduced { template bool isSelectedJpsiDauPid(const T1& track) { - int pidPion = -1; - int pidProton = -1; int pidElectron = -1; - if (selectionsPid.usePidTpcAndTof) { - pidPion = selectorPion.statusTpcAndTof(track, track.tpcNSigmaPi(), track.tofNSigmaPi()); - pidProton = selectorProton.statusTpcAndTof(track, track.tpcNSigmaPr(), track.tofNSigmaPr()); - pidElectron = selectorElectron.statusTpcAndTof(track, track.tpcNSigmaEl(), track.tofNSigmaEl()); - } else { - pidPion = selectorPion.statusTpcOrTof(track, track.tpcNSigmaPi(), track.tofNSigmaPi()); - pidProton = selectorProton.statusTpcOrTof(track, track.tpcNSigmaPr(), track.tofNSigmaPr()); - pidElectron = selectorElectron.statusTpcOrTof(track, track.tpcNSigmaEl(), track.tofNSigmaEl()); - } + pidElectron = selectorElectron.statusTpc(track, track.tpcNSigmaEl()); - if (pidPion == TrackSelectorPID::Rejected || - pidProton == TrackSelectorPID::Rejected || - pidElectron == TrackSelectorPID::Rejected) { - return false; - } - return true; + return pidElectron != TrackSelectorPID::Accepted; } /// B meson preselections @@ -530,23 +523,25 @@ struct HfDataCreatorJpsiHadReduced { if constexpr (DecChannel == DecayChannel::BplusToJpsiK) { // B+ → J/Psi K+ → (µ+µ-) K+ int indexRec = -1; - if (!runJpsiToee) { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2]}, Pdg::kBPlus, std::array{-kMuonMinus, +kMuonMinus, +kKPlus}, true, &sign, 3); - } else { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2]}, Pdg::kBPlus, std::array{-kElectron, +kElectron, +kKPlus}, true, &sign, 3); - } + // We set acceptIncompleteReco to true to accept the case of bremsstrahlung + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2]}, Pdg::kBPlus, std::array{-kMuonMinus, +kMuonMinus, +kKPlus}, true, &sign, 2); if (indexRec > -1) { - // J/Psi → µ+µ- - if (!runJpsiToee) { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, Pdg::kJPsi, std::array{-kMuonMinus, +kMuonMinus}, true); - } else { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, Pdg::kJPsi, std::array{-kElectron, +kElectron}, true); - } - if (indexRec > -1) { - flag = sign * o2::hf_decay::hf_cand_beauty::BplusToJpsiK; - } else { - debug = 1; - LOGF(debug, "B+ decays in the expected final state but the condition on the intermediate state is not fulfilled"); + const auto& bParticle = particlesMc.rawIteratorAt(indexRec); + std::vector dauIndices{}; + const int8_t signB = sign; + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBPlus, std::array{+kJPsi, signB * kKPlus}, false, nullptr, 1, &dauIndices)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); + + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (µ+µ-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauIndices{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauIndices.push_back(dau.pdgCode()); + } + if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + flag = signB * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK; + } } auto indexMother = RecoDecay::getMother(particlesMc, vecDaughtersB.back().template mcParticle_as(), Pdg::kBPlus, true); @@ -556,34 +551,186 @@ struct HfDataCreatorJpsiHadReduced { checkWrongCollision(particleMother, collision, indexCollisionMaxNumContrib, flagWrongCollision); } } - rowHfJpsiKMcRecReduced(indexHfCandJpsi, selectedTracksBach[0][vecDaughtersB.back().globalIndex()], flag, channel, flagWrongCollision, debug, motherPt); - } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { - // Bs → J/Psi phi → (µ+µ-) (K+K-) - int indexRec = -1; - if (!runJpsiToee) { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kBS, std::array{-kMuonMinus, +kMuonMinus, +kKPlus, -kKPlus}, true, &sign, 4); - } else { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kBS, std::array{-kElectron, +kElectron, +kKPlus, -kKPlus}, true, &sign, 4); + + // additional checks for correlated backgrounds + if (checkDecayTypeMc) { + // B+ → J/Psi K+ → (e+e-) K+ + if (!flag) { + // We set acceptIncompleteReco to true to accept the case of bremsstrahlung + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2]}, Pdg::kBPlus, std::array{-kElectron, +kElectron, +kKPlus}, true, &sign, 2); + if (indexRec > -1) { + const auto& bParticle = particlesMc.rawIteratorAt(indexRec); + std::vector dauIndices{}; + const int8_t signB = sign; + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBPlus, std::array{+kJPsi, signB * kKPlus}, false, nullptr, 1, &dauIndices)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); + + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauIndices{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauIndices.push_back(dau.pdgCode()); + } + if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + flag = signB * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToEEK; + } + } + } + } + + // Partly reconstructed decays, i.e. the 3 prongs have a common b-hadron ancestor + // convention: final state particles are prong0,1,2 + if (!flag) { + // b-hadron hypothesis + std::array const bHadronMotherHypos = {Pdg::kB0, Pdg::kBPlus, Pdg::kBS, Pdg::kLambdaB0}; + + for (const auto& bHadronMotherHypo : bHadronMotherHypos) { + for (const auto& jpsiDau : std::array{kMuonMinus, kElectron}) { + auto indexRecB = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, bHadronMotherHypo, std::array{-jpsiDau, +jpsiDau}, true, &sign, 2); + auto signB = sign; + auto indexRecJPsi = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, Pdg::kJPsi, std::array{-jpsiDau, +jpsiDau}, true, nullptr, 1); + + // check that the other prongs come from the same b-hadron + int const index2Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[2].mcParticle(), bHadronMotherHypo, true); + if (indexRecB > -1 && indexRecJPsi > -1 && index2Mother > -1 && index2Mother == indexRecB) { + flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu.at(std::abs(bHadronMotherHypo)) : signB * partlyRecoDecayMapEE.at(std::abs(bHadronMotherHypo)); + break; + } + } + if (flag) { + break; + } + } + } } + rowHfJpsiKMcRecReduced(indexHfCandJpsi, selectedTracksBach[0][vecDaughtersB[2].globalIndex()], flag, channel, flagWrongCollision, debug, motherPt); + } else if constexpr (DecChannel == DecayChannel::B0ToJpsiK0Star) { + // B0 → J/Psi K0* → (µ+µ-) (K+pi-) + int indexRec = -1; + // We set acceptIncompleteReco to true to accept the case of bremsstrahlung + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kB0, std::array{-kMuonMinus, +kMuonMinus, +kKPlus, -kPiPlus}, true, &sign, 3); if (indexRec > -1) { - // J/Psi → µ+µ- - if (!runJpsiToee) { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, Pdg::kJPsi, std::array{-kMuonMinus, +kMuonMinus}, true, &sign, 1); - } else { - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, Pdg::kJPsi, std::array{-kElectron, +kElectron}, true, &sign, 1); + const auto& bParticle = particlesMc.rawIteratorAt(indexRec); + std::vector dauIndices{}; + const int8_t signB = sign; + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); + + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauIndices{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauIndices.push_back(dau.pdgCode()); + } + if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + flag = signB * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK; + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kK0Star892, std::array{+kKPlus, -kPiPlus}, true, &sign, 1); + if (indexRec > -1) { + channel = o2::hf_decay::hf_cand_beauty::B0ToJpsiKstar0; + } + } + } + + auto indexMother = RecoDecay::getMother(particlesMc, vecDaughtersB.back().template mcParticle_as(), Pdg::kB0, true); + if (indexMother >= 0) { + auto particleMother = particlesMc.rawIteratorAt(indexMother); + motherPt = particleMother.pt(); + checkWrongCollision(particleMother, collision, indexCollisionMaxNumContrib, flagWrongCollision); } - if (indexRec > -1) { - flag = sign * o2::hf_decay::hf_cand_beauty::BsToJpsiKK; - indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kPhi, std::array{-kKPlus, +kKPlus}, true, &sign, 1); + } + + // additional checks for correlated backgrounds + if (checkDecayTypeMc) { + // B0 → J/Psi K*0 → (e+e-) K+pi- + if (!flag) { + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kB0, std::array{-kElectron, +kElectron, +kKPlus, -kPiPlus}, true, &sign, 3); if (indexRec > -1) { - channel = o2::hf_decay::hf_cand_beauty::BsToJpsiPhi; - } else { - debug = 1; - LOGF(debug, "Bs decays in the expected final state but the condition on the phi intermediate state is not fulfilled"); + const auto& bParticle = particlesMc.rawIteratorAt(indexRec); + std::vector dauIndices{}; + const int8_t signB = sign; + + // Since J/Psi is self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kB0, std::array{+kJPsi, signB * Pdg::kK0Star892}, false, nullptr, 1, &dauIndices)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); + + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauIndices{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauIndices.push_back(dau.pdgCode()); + } + if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + flag = signB * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToEEPiK; + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kK0Star892, std::array{+kKPlus, -kPiPlus}, true, &sign, 1); + if (indexRec > -1) { + channel = o2::hf_decay::hf_cand_beauty::B0ToJpsiKstar0; + } + } + } + + auto indexMother = RecoDecay::getMother(particlesMc, vecDaughtersB.back().template mcParticle_as(), Pdg::kB0, true); + if (indexMother >= 0) { + auto particleMother = particlesMc.rawIteratorAt(indexMother); + motherPt = particleMother.pt(); + checkWrongCollision(particleMother, collision, indexCollisionMaxNumContrib, flagWrongCollision); + } + } + } + + // Partly reconstructed decays, i.e. the 4 prongs have a common b-hadron ancestor + // convention: final state particles are prong0,1,2,3 + if (!flag) { + // b-hadron hypothesis + std::array const bHadronMotherHypos = {Pdg::kB0, Pdg::kBPlus, Pdg::kBS, Pdg::kLambdaB0}; + + for (const auto& bHadronMotherHypo : bHadronMotherHypos) { + for (const auto& jpsiDau : std::array{kMuonMinus, kElectron}) { + auto indexRecB = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, bHadronMotherHypo, std::array{-jpsiDau, +jpsiDau}, true, &sign, 2); + auto signB = sign; + auto indexRecJPsi = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, Pdg::kJPsi, std::array{-jpsiDau, +jpsiDau}, true, nullptr, 1); + // check that the other prongs come from the same b-hadron + int const index2Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[2].mcParticle(), bHadronMotherHypo, true); + int const index3Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[3].mcParticle(), bHadronMotherHypo, true); + if (indexRecB > -1 && indexRecJPsi > -1 && index2Mother > -1 && index3Mother > -1 && index2Mother == indexRecB && index3Mother == indexRecB) { + flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu.at(std::abs(bHadronMotherHypo)) : signB * partlyRecoDecayMapEE.at(std::abs(bHadronMotherHypo)); + break; + } + } + if (flag) { + break; + } + } + } + } + rowHfJpsiK0StarMcRecReduced(indexHfCandJpsi, selectedTracksBach[0][vecDaughtersB[2].globalIndex()], selectedTracksBach[1][vecDaughtersB[3].globalIndex()], flag, channel, flagWrongCollision, debug, motherPt); + } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { + // Bs → J/Psi phi → (µ+µ-) (K+K-) + int indexRec = -1; + // We set acceptIncompleteReco to true to accept the case of bremsstrahlung + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kBS, std::array{-kMuonMinus, +kMuonMinus, +kKPlus, -kKPlus}, true, &sign, 3); + if (indexRec > -1) { + const auto& bParticle = particlesMc.rawIteratorAt(indexRec); + std::vector dauIndices{}; + const int8_t signB = sign; + // Since J/Psi and phi are self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); + + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauIndices{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauIndices.push_back(dau.pdgCode()); + } + if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + flag = signB * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK; + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kPhi, std::array{+kKPlus, -kKPlus}, true, &sign, 1); + if (indexRec > -1) { + channel = o2::hf_decay::hf_cand_beauty::BsToJpsiPhi; + } } - } else { - debug = 1; - LOGF(debug, "Bs decays in the expected final state but the condition on the J/Psi intermediate state is not fulfilled"); } auto indexMother = RecoDecay::getMother(particlesMc, vecDaughtersB.back().template mcParticle_as(), Pdg::kBS, true); @@ -593,7 +740,72 @@ struct HfDataCreatorJpsiHadReduced { checkWrongCollision(particleMother, collision, indexCollisionMaxNumContrib, flagWrongCollision); } } - rowHfJpsiPhiMcRecReduced(indexHfCandJpsi, selectedTracksBach[0][vecDaughtersB.back().globalIndex()], selectedTracksBach[1][vecDaughtersB.back().globalIndex()], flag, channel, flagWrongCollision, debug, motherPt); + + // additional checks for correlated backgrounds + if (checkDecayTypeMc) { + // Bs → J/Psi phi → (e+e-) K+K- + if (!flag) { + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1], vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kBS, std::array{-kElectron, +kElectron, +kKPlus, -kKPlus}, true, &sign, 3); + if (indexRec > -1) { + const auto& bParticle = particlesMc.rawIteratorAt(indexRec); + std::vector dauIndices{}; + const int8_t signB = sign; + + // Since J/Psi and phi are self-conjugate, we need to set acceptAntiParticles to false + if (RecoDecay::isMatchedMCGen(particlesMc, bParticle, signB * Pdg::kBS, std::array{+kJPsi, +Pdg::kPhi}, false, nullptr, 1, &dauIndices)) { + const auto& jpsiPart = particlesMc.rawIteratorAt(dauIndices[0]); + + // Now we check that subtracting gammas from bremsstrahlung, the number of daughters of the J/Psi is 2 (e+e-) + // In EVTGEN, bremmstralhung photons are considered as daughters of the J/Psi + std::vector jpsiDauIndices{}; + for (const auto& dau : jpsiPart.template daughters_as()) { + jpsiDauIndices.push_back(dau.pdgCode()); + } + if (jpsiDauIndices.size() - std::count(jpsiDauIndices.begin(), jpsiDauIndices.end(), kGamma) == nProngsJpsi) { + flag = signB * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToEEKK; + indexRec = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[2], vecDaughtersB[3]}, Pdg::kPhi, std::array{+kKPlus, -kKPlus}, true, &sign, 1); + if (indexRec > -1) { + channel = o2::hf_decay::hf_cand_beauty::BsToJpsiPhi; + } + } + } + + auto indexMother = RecoDecay::getMother(particlesMc, vecDaughtersB.back().template mcParticle_as(), Pdg::kBS, true); + if (indexMother >= 0) { + auto particleMother = particlesMc.rawIteratorAt(indexMother); + motherPt = particleMother.pt(); + checkWrongCollision(particleMother, collision, indexCollisionMaxNumContrib, flagWrongCollision); + } + } + } + + // Partly reconstructed decays, i.e. the 4 prongs have a common b-hadron ancestor + // convention: final state particles are prong0,1,2,3 + if (!flag) { + // b-hadron hypothesis + std::array const bHadronMotherHypos = {Pdg::kB0, Pdg::kBPlus, Pdg::kBS, Pdg::kLambdaB0}; + + for (const auto& bHadronMotherHypo : bHadronMotherHypos) { + for (const auto& jpsiDau : std::array{kMuonMinus, kElectron}) { + auto indexRecB = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, bHadronMotherHypo, std::array{-jpsiDau, +jpsiDau}, true, &sign, 2); + auto signB = sign; + auto indexRecJPsi = RecoDecay::getMatchedMCRec(particlesMc, std::array{vecDaughtersB[0], vecDaughtersB[1]}, Pdg::kJPsi, std::array{-jpsiDau, +jpsiDau}, true, nullptr, 1); + + // check that the other prongs come from the same b-hadron + int const index2Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[2].mcParticle(), bHadronMotherHypo, true); + int const index3Mother = RecoDecay::getMother(particlesMc, vecDaughtersB[3].mcParticle(), bHadronMotherHypo, true); + if (indexRecB > -1 && indexRecJPsi > -1 && index2Mother > -1 && index3Mother > -1 && index2Mother == indexRecB && index3Mother == indexRecB) { + flag = jpsiDau == kMuonMinus ? signB * partlyRecoDecayMapMuMu.at(std::abs(bHadronMotherHypo)) : signB * partlyRecoDecayMapEE.at(std::abs(bHadronMotherHypo)); + break; + } + } + if (flag) { + break; + } + } + } + } + rowHfJpsiPhiMcRecReduced(indexHfCandJpsi, selectedTracksBach[0][vecDaughtersB[2].globalIndex()], selectedTracksBach[1][vecDaughtersB[3].globalIndex()], flag, channel, flagWrongCollision, debug, motherPt); } } @@ -639,20 +851,13 @@ struct HfDataCreatorJpsiHadReduced { // Match J/Psi -> µ+µ- auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); // Printf("Checking J/Psi -> µ+µ-"); - if (!runJpsiToee) { - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { - flag = sign * o2::hf_decay::hf_cand_beauty::BplusToJpsiK; - } - } else { // debug - // Printf("Checking J/Psi -> e+e-"); - if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kElectron, +kElectron}, true)) { - flag = sign * o2::hf_decay::hf_cand_beauty::BplusToJpsiK; - } + if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + flag = sign * o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK; } } // save information for B+ task - if (std::abs(flag) != o2::hf_decay::hf_cand_beauty::BplusToJpsiK) { + if (std::abs(flag) != o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK) { continue; } @@ -673,6 +878,44 @@ struct HfDataCreatorJpsiHadReduced { rowHfBpMcGenReduced(flag, channel, ptParticle, yParticle, etaParticle, ptProngs[0], yProngs[0], etaProngs[0], ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); + } else if constexpr (DecChannel == DecayChannel::B0ToJpsiK0Star) { + // B0 → J/Psi K*0 → (µ+µ-) (K+pi-) + if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kB0, std::array{static_cast(Pdg::kJPsi), +kKPlus, -kPiPlus}, true, &sign, 2)) { + // Match J/Psi -> µ+µ- and K*0 -> K+pi- + auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); + auto candK0StarMC = particlesMc.rawIteratorAt(particle.daughtersIds().back()); + // Printf("Checking J/Psi -> µ+µ- and K*0 -> K+pi-"); + if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + flag = sign * o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK; + } + // Check K*0 -> K+pi- + if (RecoDecay::isMatchedMCGen(particlesMc, candK0StarMC, static_cast(Pdg::kK0Star892), std::array{+kKPlus, -kPiPlus}, true)) { + channel = o2::hf_decay::hf_cand_beauty::B0ToJpsiKstar0; + } + } + + // save information for B0 task + if (std::abs(flag) != o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK) { + continue; + } + + auto ptParticle = particle.pt(); + auto yParticle = RecoDecay::y(particle.pVector(), MassB0); + auto etaParticle = particle.eta(); + + std::array ptProngs{}; + std::array yProngs{}; + std::array etaProngs{}; + int counter = 0; + for (const auto& daught : particle.daughters_as()) { + ptProngs[counter] = daught.pt(); + etaProngs[counter] = daught.eta(); + yProngs[counter] = RecoDecay::y(daught.pVector(), pdg->Mass(daught.pdgCode())); + counter++; + } + rowHfB0McGenReduced(flag, channel, ptParticle, yParticle, etaParticle, + ptProngs[0], yProngs[0], etaProngs[0], + ptProngs[1], yProngs[1], etaProngs[1], hfRejMap, centFT0C, centFT0M); } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { // Bs → J/Psi phi → (µ+µ-) (K+K-) if (RecoDecay::isMatchedMCGen(particlesMc, particle, Pdg::kBS, std::array{static_cast(Pdg::kJPsi), +kKPlus, -kKPlus}, true, &sign, 2)) { @@ -680,10 +923,8 @@ struct HfDataCreatorJpsiHadReduced { auto candJpsiMC = particlesMc.rawIteratorAt(particle.daughtersIds().front()); auto candPhiMC = particlesMc.rawIteratorAt(particle.daughtersIds().back()); // Printf("Checking J/Psi -> µ+µ- and phi -> K+K-"); - if (runJpsiToee && RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kElectron, +kElectron}, true)) { - flag = sign * o2::hf_decay::hf_cand_beauty::BsToJpsiKK; - } else if (!runJpsiToee && RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { - flag = sign * o2::hf_decay::hf_cand_beauty::BsToJpsiKK; + if (RecoDecay::isMatchedMCGen(particlesMc, candJpsiMC, static_cast(Pdg::kJPsi), std::array{-kMuonMinus, +kMuonMinus}, true)) { + flag = sign * o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK; } // Check phi -> K+K- if (RecoDecay::isMatchedMCGen(particlesMc, candPhiMC, static_cast(Pdg::kPhi), std::array{-kKPlus, +kKPlus}, true)) { @@ -692,12 +933,12 @@ struct HfDataCreatorJpsiHadReduced { } // save information for Bs task - if (std::abs(flag) != o2::hf_decay::hf_cand_beauty::BsToJpsiKK) { + if (std::abs(flag) != o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK) { continue; } auto ptParticle = particle.pt(); - auto yParticle = RecoDecay::y(particle.pVector(), MassBPlus); + auto yParticle = RecoDecay::y(particle.pVector(), MassBS); auto etaParticle = particle.eta(); std::array ptProngs{}; @@ -766,8 +1007,11 @@ struct HfDataCreatorJpsiHadReduced { runNumber = bc.runNumber(); } df2.setBz(bz); - df3.setBz(bz); - df4.setBz(bz); + if constexpr (DecChannel == DecayChannel::BplusToJpsiK) { + df3.setBz(bz); + } else { + df4.setBz(bz); + } auto thisCollId = collision.globalIndex(); // looping over 2-prong candidates @@ -776,7 +1020,7 @@ struct HfDataCreatorJpsiHadReduced { // Apply the selections on the J/Psi candidates registry.fill(HIST("hSelectionsJpsi"), 1, candidate.pt()); - if (!(candidate.hfflag() & (1 << (runJpsiToee ? aod::hf_cand_2prong::DecayType::JpsiToEE : aod::hf_cand_2prong::DecayType::JpsiToMuMu)))) { + if (!TESTBIT(candidate.hfflag(), aod::hf_cand_2prong::DecayType::JpsiToMuMu)) { continue; } registry.fill(HIST("hSelectionsJpsi"), 2 + aod::SelectionStep::RecoSkims, candidate.pt()); @@ -829,7 +1073,7 @@ struct HfDataCreatorJpsiHadReduced { registry.fill(HIST("hSelectionsJpsi"), 2 + aod::SelectionStep::RecoPID, candidate.pt()); int const indexHfCandJpsi = hfJpsi.lastIndex() + 1; - float const invMassJpsi = runJpsiToee ? HfHelper::invMassJpsiToEE(candidate) : HfHelper::invMassJpsiToMuMu(candidate); + float invMassJpsi = HfHelper::invMassJpsiToMuMu(candidate); registry.fill(HIST("hMassJpsi"), invMassJpsi); registry.fill(HIST("hPtJpsi"), candidate.pt()); registry.fill(HIST("hCpaJpsi"), candidate.cpa()); @@ -870,7 +1114,6 @@ struct HfDataCreatorJpsiHadReduced { } registry.fill(HIST("hFitCandidatesBPlus"), SVFitting::FitOk); - o2::track::TrackParCov trackParCovBPlus{}; std::array pVecBPlus{}, pVec0{}, pVec1{}, pVec2{}; auto secondaryVertexBPlus = df3.getPCACandidate(); @@ -879,14 +1122,12 @@ struct HfDataCreatorJpsiHadReduced { df3.getTrack(2).getPxPyPzGlo(pVec2); pVecBPlus = RecoDecay::pVec(pVec0, pVec1, pVec2); pVecJpsi = RecoDecay::pVec(pVec0, pVec1); - trackParCovBPlus = df3.createParentTrackParCov(); - trackParCovBPlus.setAbsCharge(0); // to be sure if (!isBSelected(pVecBPlus, secondaryVertexBPlus, collision)) { continue; } // compute invariant mass square and apply selection - invMass2JpsiHad = RecoDecay::m2(std::array{pVecJpsi, pVecBach}, std::array{MassJPsi, MassKPlus}); + invMass2JpsiHad = RecoDecay::m2(std::array{pVecJpsi, pVec2}, std::array{MassJPsi, MassKPlus}); if ((invMass2JpsiHad < invMass2JpsiHadMin) || (invMass2JpsiHad > invMass2JpsiHadMax)) { continue; } @@ -894,7 +1135,7 @@ struct HfDataCreatorJpsiHadReduced { // fill Kaon tracks table // if information on track already stored, go to next track - if (!selectedTracksBach.count(trackBach.globalIndex())) { + if (!selectedTracksBach.contains(trackBach.globalIndex())) { hfTrackLfDau0(trackBach.globalIndex(), indexHfReducedCollision, trackParCovBach.getX(), trackParCovBach.getAlpha(), trackParCovBach.getY(), trackParCovBach.getZ(), trackParCovBach.getSnp(), @@ -926,9 +1167,144 @@ struct HfDataCreatorJpsiHadReduced { fillMcRecoInfo(collision, particlesMc, beautyHadDauTracks, indexHfCandJpsi, std::array, 2>{selectedTracksBach}, indexCollisionMaxNumContrib); } fillHfCandJpsi = true; + } else if constexpr (DecChannel == DecayChannel::B0ToJpsiK0Star) { + for (auto trackBachId2 = trackId + 1; trackBachId2 != trackIndices.end(); ++trackBachId2) { + auto trackBach2 = trackBachId2.template track_as(); + if (trackBach.sign() == trackBach2.sign()) { + continue; + } + auto trackBach2ParCov = getTrackParCov(trackBach2); + + std::array dcaBach2{trackBach2.dcaXY(), trackBach2.dcaZ()}; + std::array pVecBach2 = trackBach2.pVector(); + if (trackBach2.collisionId() != thisCollId) { + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackBach2ParCov, 2.f, noMatCorr, &dcaBach2); + getPxPyPz(trackBach2ParCov, pVecBach2); + } + + // apply selections on bachelor tracks + if (!isTrackSelected(trackBach2, trackBach2ParCov, dcaBach2, jPsiDauTracks)) { + continue; + } + std::array pVec2{trackBach.pVector()}, pVec3{trackBach2.pVector()}; + auto invMassPiK = RecoDecay::m(std::array{pVec2, pVec3}, std::array{MassPiPlus, MassKPlus}); + auto invMassKPi = RecoDecay::m(std::array{pVec2, pVec3}, std::array{MassKPlus, MassPiPlus}); + bool isK0StarPiK = std::abs(invMassPiK - MassK0Star892) < deltaMK0StarMax; + bool isK0StarKPi = std::abs(invMassKPi - MassK0Star892) < deltaMK0StarMax; + + if (!isK0StarPiK && !isK0StarKPi) { + continue; + } + + // --------------------------------- + // reconstruct B0 candidate secondary vertex + + registry.fill(HIST("hFitCandidatesB0"), SVFitting::BeforeFit); + try { + if (df4.process(trackPosParCov, trackNegParCov, trackParCovBach, trackBach2ParCov) == 0) { + continue; + } + } catch (const std::runtime_error& error) { + LOG(info) << "Run time error found: " << error.what() << ". DCAFitterN cannot work, skipping the candidate."; + registry.fill(HIST("hFitCandidatesB0"), SVFitting::Fail); + continue; + } + registry.fill(HIST("hFitCandidatesB0"), SVFitting::FitOk); + + std::array pVecB0{}, pVec0{}, pVec1{}, pVecK0Star{}; + + auto secondaryVertexB0 = df4.getPCACandidate(); + df4.getTrack(0).getPxPyPzGlo(pVec0); + df4.getTrack(1).getPxPyPzGlo(pVec1); + df4.getTrack(2).getPxPyPzGlo(pVec2); + df4.getTrack(3).getPxPyPzGlo(pVec3); + pVecB0 = RecoDecay::pVec(pVec0, pVec1, pVec2, pVec3); + pVecJpsi = RecoDecay::pVec(pVec0, pVec1); + pVecK0Star = RecoDecay::pVec(pVec2, pVec3); + + if (!isBSelected(pVecB0, secondaryVertexB0, collision)) { + continue; + } + + registry.fill(HIST("hPtK0Star"), RecoDecay::pt(pVecBach, pVecBach2)); + registry.fill(HIST("hMassK0Star"), RecoDecay::m(std::array{pVecBach, pVecBach2}, isK0StarPiK ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus})); + invMass2JpsiHad = RecoDecay::m2(std::array{pVecJpsi, pVecK0Star}, std::array{MassJPsi, MassK0Star892}); + if ((invMass2JpsiHad < invMass2JpsiHadMin) || (invMass2JpsiHad > invMass2JpsiHadMax)) { + continue; + } + registry.fill(HIST("hMassJpsiK0Star"), std::sqrt(invMass2JpsiHad)); + + // fill daughter tracks table (positive daughter as Dau0, negative daughter as Dau1) + const auto& posDauTrack = trackBach.sign() > 0 ? trackBach : trackBach2; + const auto& negDauTrack = trackBach.sign() > 0 ? trackBach2 : trackBach; + const auto& posDauTrackParCov = trackBach.sign() > 0 ? trackParCovBach : trackBach2ParCov; + const auto& negDauTrackParCov = trackBach.sign() > 0 ? trackBach2ParCov : trackParCovBach; + + // if information on track already stored, go to next track + if (!selectedTracksBach.contains(posDauTrack.globalIndex())) { + hfTrackLfDau0(posDauTrack.globalIndex(), indexHfReducedCollision, + posDauTrackParCov.getX(), posDauTrackParCov.getAlpha(), + posDauTrackParCov.getY(), posDauTrackParCov.getZ(), posDauTrackParCov.getSnp(), + posDauTrackParCov.getTgl(), posDauTrackParCov.getQ2Pt(), + posDauTrack.itsNCls(), posDauTrack.tpcNClsCrossedRows(), posDauTrack.tpcChi2NCl(), posDauTrack.itsChi2NCl(), + posDauTrack.hasTPC(), posDauTrack.hasTOF(), + posDauTrack.tpcNSigmaPi(), posDauTrack.tofNSigmaPi(), + posDauTrack.tpcNSigmaKa(), posDauTrack.tofNSigmaKa(), + posDauTrack.tpcNSigmaPr(), posDauTrack.tofNSigmaPr()); + hfTrackCovLfDau0(posDauTrackParCov.getSigmaY2(), posDauTrackParCov.getSigmaZY(), posDauTrackParCov.getSigmaZ2(), + posDauTrackParCov.getSigmaSnpY(), posDauTrackParCov.getSigmaSnpZ(), + posDauTrackParCov.getSigmaSnp2(), posDauTrackParCov.getSigmaTglY(), posDauTrackParCov.getSigmaTglZ(), + posDauTrackParCov.getSigmaTglSnp(), posDauTrackParCov.getSigmaTgl2(), + posDauTrackParCov.getSigma1PtY(), posDauTrackParCov.getSigma1PtZ(), posDauTrackParCov.getSigma1PtSnp(), + posDauTrackParCov.getSigma1PtTgl(), posDauTrackParCov.getSigma1Pt2()); + // add trackBach.globalIndex() to a list + // to keep memory of the pions filled in the table and avoid refilling them if they are paired to another Jpsi candidate + // and keep track of their index in hfTrackLfDau0 for McRec purposes + selectedTracksBach[posDauTrack.globalIndex()] = hfTrackLfDau0.lastIndex(); + } + + // fill daughter tracks table + // if information on track already stored, go to next track + if (!selectedTracksBach2.contains(negDauTrack.globalIndex())) { + hfTrackLfDau1(negDauTrack.globalIndex(), indexHfReducedCollision, + negDauTrackParCov.getX(), negDauTrackParCov.getAlpha(), + negDauTrackParCov.getY(), negDauTrackParCov.getZ(), negDauTrackParCov.getSnp(), + negDauTrackParCov.getTgl(), negDauTrackParCov.getQ2Pt(), + negDauTrack.itsNCls(), negDauTrack.tpcNClsCrossedRows(), negDauTrack.tpcChi2NCl(), negDauTrack.itsChi2NCl(), + negDauTrack.hasTPC(), negDauTrack.hasTOF(), + negDauTrack.tpcNSigmaPi(), negDauTrack.tofNSigmaPi(), + negDauTrack.tpcNSigmaKa(), negDauTrack.tofNSigmaKa(), + negDauTrack.tpcNSigmaPr(), negDauTrack.tofNSigmaPr()); + hfTrackCovLfDau1(negDauTrackParCov.getSigmaY2(), negDauTrackParCov.getSigmaZY(), negDauTrackParCov.getSigmaZ2(), + negDauTrackParCov.getSigmaSnpY(), negDauTrackParCov.getSigmaSnpZ(), + negDauTrackParCov.getSigmaSnp2(), negDauTrackParCov.getSigmaTglY(), negDauTrackParCov.getSigmaTglZ(), + negDauTrackParCov.getSigmaTglSnp(), negDauTrackParCov.getSigmaTgl2(), + negDauTrackParCov.getSigma1PtY(), negDauTrackParCov.getSigma1PtZ(), negDauTrackParCov.getSigma1PtSnp(), + negDauTrackParCov.getSigma1PtTgl(), negDauTrackParCov.getSigma1Pt2()); + // add negDauTrack.globalIndex() to a list + // to keep memory of the pions filled in the table and avoid refilling them if they are paired to another Jpsi candidate + // and keep track of their index in hfTrackLfDau1 for McRec purposes + selectedTracksBach2[negDauTrack.globalIndex()] = hfTrackLfDau1.lastIndex(); + } + + if constexpr (DoMc) { + std::vector beautyHadDauTracks{}; + beautyHadDauTracks.reserve(jPsiDauTracks.size()); + for (const auto& track : jPsiDauTracks) { + beautyHadDauTracks.push_back(track); + } + beautyHadDauTracks.push_back(posDauTrack); + beautyHadDauTracks.push_back(negDauTrack); + fillMcRecoInfo(collision, particlesMc, beautyHadDauTracks, indexHfCandJpsi, std::array, 2>{selectedTracksBach, selectedTracksBach2}, indexCollisionMaxNumContrib); + } + fillHfCandJpsi = true; + } } else if constexpr (DecChannel == DecayChannel::BsToJpsiPhi) { for (auto trackBachId2 = trackId + 1; trackBachId2 != trackIndices.end(); ++trackBachId2) { auto trackBach2 = trackBachId2.template track_as(); + if (trackBach.sign() == trackBach2.sign()) { + continue; + } auto trackBach2ParCov = getTrackParCov(trackBach2); std::array dcaBach2{trackBach2.dcaXY(), trackBach2.dcaZ()}; @@ -964,7 +1340,6 @@ struct HfDataCreatorJpsiHadReduced { } registry.fill(HIST("hFitCandidatesBS"), SVFitting::FitOk); - o2::track::TrackParCov trackParCovBS{}; std::array pVecBS{}, pVec0{}, pVec1{}, pVecPhi{}; auto secondaryVertexBS = df4.getPCACandidate(); @@ -975,8 +1350,6 @@ struct HfDataCreatorJpsiHadReduced { pVecBS = RecoDecay::pVec(pVec0, pVec1, pVec2, pVec3); pVecJpsi = RecoDecay::pVec(pVec0, pVec1); pVecPhi = RecoDecay::pVec(pVec2, pVec3); - trackParCovBS = df4.createParentTrackParCov(); - trackParCovBS.setAbsCharge(0); // to be sure if (!isBSelected(pVecBS, secondaryVertexBS, collision)) { continue; @@ -990,52 +1363,57 @@ struct HfDataCreatorJpsiHadReduced { } registry.fill(HIST("hMassJpsiPhi"), std::sqrt(invMass2JpsiHad)); - // fill daughter tracks table + // fill daughter tracks table (positive daughter as Dau0, negative daughter as Dau1) + const auto& posDauTrack = trackBach.sign() > 0 ? trackBach : trackBach2; + const auto& negDauTrack = trackBach.sign() > 0 ? trackBach2 : trackBach; + const auto& posDauTrackParCov = trackBach.sign() > 0 ? trackParCovBach : trackBach2ParCov; + const auto& negDauTrackParCov = trackBach.sign() > 0 ? trackBach2ParCov : trackParCovBach; + // if information on track already stored, go to next track - if (!selectedTracksBach.count(trackBach.globalIndex())) { - hfTrackLfDau0(trackBach.globalIndex(), indexHfReducedCollision, - trackParCovBach.getX(), trackParCovBach.getAlpha(), - trackParCovBach.getY(), trackParCovBach.getZ(), trackParCovBach.getSnp(), - trackParCovBach.getTgl(), trackParCovBach.getQ2Pt(), - trackBach.itsNCls(), trackBach.tpcNClsCrossedRows(), trackBach.tpcChi2NCl(), trackBach.itsChi2NCl(), - trackBach.hasTPC(), trackBach.hasTOF(), - trackBach.tpcNSigmaPi(), trackBach.tofNSigmaPi(), - trackBach.tpcNSigmaKa(), trackBach.tofNSigmaKa(), - trackBach.tpcNSigmaPr(), trackBach.tofNSigmaPr()); - hfTrackCovLfDau0(trackParCovBach.getSigmaY2(), trackParCovBach.getSigmaZY(), trackParCovBach.getSigmaZ2(), - trackParCovBach.getSigmaSnpY(), trackParCovBach.getSigmaSnpZ(), - trackParCovBach.getSigmaSnp2(), trackParCovBach.getSigmaTglY(), trackParCovBach.getSigmaTglZ(), - trackParCovBach.getSigmaTglSnp(), trackParCovBach.getSigmaTgl2(), - trackParCovBach.getSigma1PtY(), trackParCovBach.getSigma1PtZ(), trackParCovBach.getSigma1PtSnp(), - trackParCovBach.getSigma1PtTgl(), trackParCovBach.getSigma1Pt2()); - // add trackBach.globalIndex() to a list + if (!selectedTracksBach.contains(posDauTrack.globalIndex())) { + hfTrackLfDau0(posDauTrack.globalIndex(), indexHfReducedCollision, + posDauTrackParCov.getX(), posDauTrackParCov.getAlpha(), + posDauTrackParCov.getY(), posDauTrackParCov.getZ(), posDauTrackParCov.getSnp(), + posDauTrackParCov.getTgl(), posDauTrackParCov.getQ2Pt(), + posDauTrack.itsNCls(), posDauTrack.tpcNClsCrossedRows(), posDauTrack.tpcChi2NCl(), posDauTrack.itsChi2NCl(), + posDauTrack.hasTPC(), posDauTrack.hasTOF(), + posDauTrack.tpcNSigmaPi(), posDauTrack.tofNSigmaPi(), + posDauTrack.tpcNSigmaKa(), posDauTrack.tofNSigmaKa(), + posDauTrack.tpcNSigmaPr(), posDauTrack.tofNSigmaPr()); + hfTrackCovLfDau0(posDauTrackParCov.getSigmaY2(), posDauTrackParCov.getSigmaZY(), posDauTrackParCov.getSigmaZ2(), + posDauTrackParCov.getSigmaSnpY(), posDauTrackParCov.getSigmaSnpZ(), + posDauTrackParCov.getSigmaSnp2(), posDauTrackParCov.getSigmaTglY(), posDauTrackParCov.getSigmaTglZ(), + posDauTrackParCov.getSigmaTglSnp(), posDauTrackParCov.getSigmaTgl2(), + posDauTrackParCov.getSigma1PtY(), posDauTrackParCov.getSigma1PtZ(), posDauTrackParCov.getSigma1PtSnp(), + posDauTrackParCov.getSigma1PtTgl(), posDauTrackParCov.getSigma1Pt2()); + // add posDauTrack.globalIndex() to a list // to keep memory of the pions filled in the table and avoid refilling them if they are paired to another Jpsi candidate // and keep track of their index in hfTrackLfDau0 for McRec purposes - selectedTracksBach[trackBach.globalIndex()] = hfTrackLfDau0.lastIndex(); + selectedTracksBach[posDauTrack.globalIndex()] = hfTrackLfDau0.lastIndex(); } // fill daughter tracks table // if information on track already stored, go to next track - if (!selectedTracksBach2.count(trackBach2.globalIndex())) { - hfTrackLfDau1(trackBach2.globalIndex(), indexHfReducedCollision, - trackBach2ParCov.getX(), trackBach2ParCov.getAlpha(), - trackBach2ParCov.getY(), trackBach2ParCov.getZ(), trackBach2ParCov.getSnp(), - trackBach2ParCov.getTgl(), trackBach2ParCov.getQ2Pt(), - trackBach2.itsNCls(), trackBach2.tpcNClsCrossedRows(), trackBach2.tpcChi2NCl(), trackBach2.itsChi2NCl(), - trackBach2.hasTPC(), trackBach2.hasTOF(), - trackBach2.tpcNSigmaPi(), trackBach2.tofNSigmaPi(), - trackBach2.tpcNSigmaKa(), trackBach2.tofNSigmaKa(), - trackBach2.tpcNSigmaPr(), trackBach2.tofNSigmaPr()); - hfTrackCovLfDau1(trackBach2ParCov.getSigmaY2(), trackBach2ParCov.getSigmaZY(), trackBach2ParCov.getSigmaZ2(), - trackBach2ParCov.getSigmaSnpY(), trackBach2ParCov.getSigmaSnpZ(), - trackBach2ParCov.getSigmaSnp2(), trackBach2ParCov.getSigmaTglY(), trackBach2ParCov.getSigmaTglZ(), - trackBach2ParCov.getSigmaTglSnp(), trackBach2ParCov.getSigmaTgl2(), - trackBach2ParCov.getSigma1PtY(), trackBach2ParCov.getSigma1PtZ(), trackBach2ParCov.getSigma1PtSnp(), - trackBach2ParCov.getSigma1PtTgl(), trackBach2ParCov.getSigma1Pt2()); - // add trackBach2.globalIndex() to a list + if (!selectedTracksBach2.contains(negDauTrack.globalIndex())) { + hfTrackLfDau1(negDauTrack.globalIndex(), indexHfReducedCollision, + negDauTrackParCov.getX(), negDauTrackParCov.getAlpha(), + negDauTrackParCov.getY(), negDauTrackParCov.getZ(), negDauTrackParCov.getSnp(), + negDauTrackParCov.getTgl(), negDauTrackParCov.getQ2Pt(), + negDauTrack.itsNCls(), negDauTrack.tpcNClsCrossedRows(), negDauTrack.tpcChi2NCl(), negDauTrack.itsChi2NCl(), + negDauTrack.hasTPC(), negDauTrack.hasTOF(), + negDauTrack.tpcNSigmaPi(), negDauTrack.tofNSigmaPi(), + negDauTrack.tpcNSigmaKa(), negDauTrack.tofNSigmaKa(), + negDauTrack.tpcNSigmaPr(), negDauTrack.tofNSigmaPr()); + hfTrackCovLfDau1(negDauTrackParCov.getSigmaY2(), negDauTrackParCov.getSigmaZY(), negDauTrackParCov.getSigmaZ2(), + negDauTrackParCov.getSigmaSnpY(), negDauTrackParCov.getSigmaSnpZ(), + negDauTrackParCov.getSigmaSnp2(), negDauTrackParCov.getSigmaTglY(), negDauTrackParCov.getSigmaTglZ(), + negDauTrackParCov.getSigmaTglSnp(), negDauTrackParCov.getSigmaTgl2(), + negDauTrackParCov.getSigma1PtY(), negDauTrackParCov.getSigma1PtZ(), negDauTrackParCov.getSigma1PtSnp(), + negDauTrackParCov.getSigma1PtTgl(), negDauTrackParCov.getSigma1Pt2()); + // add negDauTrack.globalIndex() to a list // to keep memory of the pions filled in the table and avoid refilling them if they are paired to another Jpsi candidate // and keep track of their index in hfTrackLfDau1 for McRec purposes - selectedTracksBach2[trackBach2.globalIndex()] = hfTrackLfDau1.lastIndex(); + selectedTracksBach2[negDauTrack.globalIndex()] = hfTrackLfDau1.lastIndex(); } if constexpr (DoMc) { @@ -1044,20 +1422,16 @@ struct HfDataCreatorJpsiHadReduced { for (const auto& track : jPsiDauTracks) { beautyHadDauTracks.push_back(track); } - beautyHadDauTracks.push_back(trackBach); - fillMcRecoInfo(collision, particlesMc, beautyHadDauTracks, indexHfCandJpsi, std::array, 2>{selectedTracksBach, selectedTracksBach2}, indexCollisionMaxNumContrib); + beautyHadDauTracks.push_back(posDauTrack); + beautyHadDauTracks.push_back(negDauTrack); + fillMcRecoInfo(collision, particlesMc, beautyHadDauTracks, indexHfCandJpsi, std::array, 2>{selectedTracksBach, selectedTracksBach2}, indexCollisionMaxNumContrib); } fillHfCandJpsi = true; } } } // kaon loop if (fillHfCandJpsi) { // fill Jpsi table only once per Jpsi candidate - double invMassJpsi{0.}; - if (runJpsiToee) { - invMassJpsi = HfHelper::invMassJpsiToEE(candidate); - } else { - invMassJpsi = HfHelper::invMassJpsiToMuMu(candidate); - } + invMassJpsi = HfHelper::invMassJpsiToMuMu(candidate); hfJpsi(trackPos.globalIndex(), trackNeg.globalIndex(), indexHfReducedCollision, candidate.xSecondaryVertex(), candidate.ySecondaryVertex(), candidate.zSecondaryVertex(), @@ -1086,6 +1460,10 @@ struct HfDataCreatorJpsiHadReduced { trackPosParCov.getSigma1PtSnp(), trackNegParCov.getSigma1PtSnp(), trackPosParCov.getSigma1PtTgl(), trackNegParCov.getSigma1PtTgl(), trackPosParCov.getSigma1Pt2(), trackNegParCov.getSigma1Pt2()); + hfRedJpsiDauPid(trackPos.tpcNSigmaMu(), trackNeg.tpcNSigmaMu(), + trackPos.tpcNSigmaEl(), trackNeg.tpcNSigmaEl(), + trackPos.tofNSigmaMu(), trackNeg.tofNSigmaMu(), + trackPos.tofNSigmaEl(), trackNeg.tofNSigmaEl()); fillHfReducedCollision = true; } } // candsJpsi loop @@ -1110,7 +1488,7 @@ struct HfDataCreatorJpsiHadReduced { // } } - void processJpsiKData(soa::Join const& collisions, + void processJpsiKData(soa::Join const& collisions, aod::HfCand2ProngWPid const& candsJpsi, aod::TrackAssoc const& trackIndices, TracksPidWithSel const& tracks, @@ -1138,13 +1516,13 @@ struct HfDataCreatorJpsiHadReduced { } PROCESS_SWITCH(HfDataCreatorJpsiHadReduced, processJpsiKData, "Process J/Psi K without MC info", true); - void processJpsiPhiData(soa::Join const& collisions, + void processJpsiPhiData(soa::Join const& collisions, aod::HfCand2ProngWPid const& candsJpsi, aod::TrackAssoc const& trackIndices, TracksPidWithSel const& tracks, aod::BCsWithTimestamps const& bcs) { - // store configurables needed for B0 workflow + // store configurables needed for Bs workflow if (!isHfCandBhadConfigFilled) { rowCandidateConfigBs(invMassWindowJpsiHad.value); isHfCandBhadConfigFilled = true; @@ -1166,6 +1544,34 @@ struct HfDataCreatorJpsiHadReduced { } PROCESS_SWITCH(HfDataCreatorJpsiHadReduced, processJpsiPhiData, "Process J/Psi phi without MC info", false); + void processJpsiK0StarData(soa::Join const& collisions, + aod::HfCand2ProngWPid const& candsJpsi, + aod::TrackAssoc const& trackIndices, + TracksPidWithSel const& tracks, + aod::BCsWithTimestamps const& bcs) + { + // store configurables needed for B0 workflow + if (!isHfCandBhadConfigFilled) { + rowCandidateConfigB0(invMassWindowJpsiHad.value); + isHfCandBhadConfigFilled = true; + } + + int zvtxColl{0}; + int sel8Coll{0}; + int zvtxAndSel8Coll{0}; + int zvtxAndSel8CollAndSoftTrig{0}; + int allSelColl{0}; + for (const auto& collision : collisions) { + auto thisCollId = collision.globalIndex(); + auto candsJpsiThisColl = candsJpsi.sliceBy(candsJpsiPerCollision, thisCollId); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); + runDataCreation(collision, candsJpsiThisColl, trackIdsThisCollision, tracks, tracks, -1, bcs, zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); + } + // handle normalization by the right number of collisions + hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); + } + PROCESS_SWITCH(HfDataCreatorJpsiHadReduced, processJpsiK0StarData, "Process J/Psi K*0 without MC info", false); + void processJpsiKMc(CollisionsWCMcLabels const& collisions, aod::HfCand2ProngWPid const& candsJpsi, aod::TrackAssoc const& trackIndices, @@ -1201,6 +1607,41 @@ struct HfDataCreatorJpsiHadReduced { } PROCESS_SWITCH(HfDataCreatorJpsiHadReduced, processJpsiKMc, "Process J/Psi K with MC info", false); + void processJpsiK0StarMc(CollisionsWCMcLabels const& collisions, + aod::HfCand2ProngWPid const& candsJpsi, + aod::TrackAssoc const& trackIndices, + TracksPidWithSelAndMc const& tracks, + aod::McParticles const& particlesMc, + BCsInfo const& bcs, + McCollisions const& mcCollisions) + { + // store configurables needed for B+ workflow + if (!isHfCandBhadConfigFilled) { + rowCandidateConfigB0(invMassWindowJpsiHad.value); + isHfCandBhadConfigFilled = true; + } + + int zvtxColl{0}; + int sel8Coll{0}; + int zvtxAndSel8Coll{0}; + int zvtxAndSel8CollAndSoftTrig{0}; + int allSelColl{0}; + for (const auto& collision : collisions) { + auto thisCollId = collision.globalIndex(); + auto candsJpsiThisColl = candsJpsi.sliceBy(candsJpsiPerCollision, thisCollId); + auto trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); + auto collsSameMcCollision = collisions.sliceBy(colPerMcCollision, collision.mcCollisionId()); + int64_t const indexCollisionMaxNumContrib = getIndexCollisionMaxNumContrib(collsSameMcCollision); + runDataCreation(collision, candsJpsiThisColl, trackIdsThisCollision, tracks, particlesMc, indexCollisionMaxNumContrib, bcs, zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); + } + // handle normalization by the right number of collisions + hfCollisionCounter(collisions.tableSize(), zvtxColl, sel8Coll, zvtxAndSel8Coll, zvtxAndSel8CollAndSoftTrig, allSelColl); + for (const auto& mcCollision : mcCollisions) { + runMcGen(mcCollision, particlesMc, collisions, bcs); + } + } + PROCESS_SWITCH(HfDataCreatorJpsiHadReduced, processJpsiK0StarMc, "Process J/Psi K0* with MC info", false); + void processJpsiPhiMc(CollisionsWCMcLabels const& collisions, aod::HfCand2ProngWPid const& candsJpsi, aod::TrackAssoc const& trackIndices, diff --git a/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx new file mode 100644 index 00000000000..5961630d3d2 --- /dev/null +++ b/PWGHF/D2H/TableProducer/mlBasedTrackSelector.cxx @@ -0,0 +1,1361 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file mlBasedTrackSelector.cxx +/// \brief D± → π± K∓ π± and D_s± → K± K∓ π± skimming with track-level ML +/// TEST ONLY, PLEASE DO NOT USE FOR ANALYSIS +/// \author Fabrizio Chinu , Universita and INFN Torino + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/HfMlResponseHfTracks.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" +#include "PWGHF/Utils/utilsBfieldCCDB.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/CCDB/TriggerAliases.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::analysis; +using namespace o2::hf_evsel; +using namespace o2::aod; +using namespace o2::hf_centrality; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +/// Bit positions of aod::HfSelTrack::isSelProng (only Cand3Prong is ever set here). +enum CandidateType { + Cand2Prong = 0, + Cand3Prong, + CandV0bachelor, + CandDstar, + CandCascadeBachelor, + NCandidateTypes +}; + +/// The two 3-prong channels handled by this task. +enum Channel3Prong { + ChannelDplusToPiKPi = 0, + ChannelDsToKKPi, + NChannels3Prong +}; + +constexpr std::array DecayTypeOfChannel{ + hf_cand_3prong::DecayType::DplusToPiKPi, + hf_cand_3prong::DecayType::DsToKKPi}; + +/// Bit positions of aod::HfSelTrack::isIdentifiedPid filled by this task: the decision of the +/// track-level model, one bit per (channel, role). A track can be pion-like for one channel and +/// kaon-like for the other, so the four bits are independent. +enum TrackMlRole { + RolePionDplus = 0, + RoleKaonDplus, + RolePionDs, + RoleKaonDs, + NTrackMlRoles +}; + +constexpr std::array RolePionOfChannel{RolePionDplus, RolePionDs}; +constexpr std::array RoleKaonOfChannel{RoleKaonDplus, RoleKaonDs}; + +constexpr int NProngs = 3; // prongs of the candidates built here +constexpr int NMassHypos = 2; // mass hypotheses per channel, i.e. the two orderings of the same-sign pair + +/// Whether each prong is a kaon, per channel and mass hypothesis. +constexpr std::array, NMassHypos>, NChannels3Prong> IsKaonProng{{ + {{{{false, true, false}}, {{false, true, false}}}}, + {{{{true, true, false}}, {{false, true, true}}}}, +}}; + +constexpr double PtMaxModel = 1.e10; + +constexpr uint32_t MaskSameSignAny = (1u << RolePionDplus) | (1u << RolePionDs) | (1u << RoleKaonDs); +constexpr uint32_t MaskOppSignAny = (1u << RoleKaonDplus) | (1u << RoleKaonDs); + +/// Where the combinatorics spends its time, accumulated in seconds into hTiming. +enum TimingStep { + TimeLoopTotal = 0, ///< everything inside the triple loop + TimeFit2Prong, ///< the 2-track vertex used to skip pairs early + TimeFit3Prong, ///< the 3-track vertex of the surviving triplets + TimeCacheFill, ///< building the per-collision prong cache, propagation included + TimeCachePropagate, ///< only the re-propagation inside that build + NTimingSteps +}; + +/// How often each stage of the triple loop runs, accumulated into hLoopCounters. +enum LoopCounter { + CountPairsSeen = 0, ///< (same-sign, opposite-sign) pairs reached + CountPairsCandidateOk, ///< ... of those, surviving the ML pair test + CountFit2Prong, ///< ... of those, handed to the 2-prong fitter + CountPairsRejected2P, ///< ... of those, dropped by the 2-track vertex + CountTripletsEnumerated, ///< triplets built from the surviving pairs + CountTripletsWritten, ///< ... of those, written to the skim + CountTracksCached, ///< associations put into the prong cache + CountTracksPropagated, ///< ... of those, needing a re-propagation to this collision + NLoopCounters +}; + +/// Where the ML track selection spends its time, accumulated in seconds into its own hTiming. +enum TrackTimingStep { + TrackTimeTotal = 0, ///< the whole per-collision loop: slicing, features, models, table filling + TrackTimeFeatures, ///< building the input features, re-propagation included + TrackTimeModelDplus, ///< evaluating the D+ model: input vector, ONNX call, thresholds + TrackTimeModelDs, ///< evaluating the Ds model + NTrackTimingSteps +}; + +inline std::chrono::steady_clock::time_point tickIf(const bool enabled) +{ + return enabled ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; +} +inline double elapsedSeconds(std::chrono::steady_clock::time_point const& start) +{ + return std::chrono::duration(std::chrono::steady_clock::now() - start).count(); +} + +/// Event selection +struct HfTrackSelectorTagSelCollisions { + Produces rowSelectedCollision; + + Configurable fillHistograms{"fillHistograms", true, "fill histograms"}; + Configurable triggerClassName{"triggerClassName", "kINT7", "Run 2 trigger class, only for Run 2 converted data"}; + HfEventSelection hfEvSel; // event selection and monitoring + Service ccdb{}; // needed for evSelection + + // QA histos + HistogramRegistry registry{"registry"}; + OutputObj zorroSummary{"zorroSummary"}; + + void init(InitContext const&) + { + const std::array doProcess = {doprocessTrigAndCentFT0ASel, doprocessTrigAndCentFT0CSel, doprocessTrigAndCentFT0MSel, doprocessTrigAndCentFV0ASel, doprocessTrigSel, doprocessNoTrigSel, doprocessUpcSel}; + if (std::count(doProcess.begin(), doProcess.end(), true) != 1) { + LOGP(fatal, "One and only one process function for collision selection can be enabled at a time!"); + } + + // set numerical value of the Run 2 trigger class + auto* const triggerAlias = std::find(aliasLabels.begin(), aliasLabels.end(), triggerClassName.value.data()); + if (triggerAlias != aliasLabels.end()) { + hfEvSel.triggerClass.value = std::distance(aliasLabels.begin(), triggerAlias); + } + + hfEvSel.init(registry, &zorroSummary); // collision monitoring + if (fillHistograms) { + if (doprocessTrigAndCentFT0ASel || doprocessTrigAndCentFT0CSel || doprocessTrigAndCentFT0MSel || doprocessTrigAndCentFV0ASel) { + const AxisSpec axisCentrality{200, 0., 100., "centrality percentile"}; + registry.add("hCentralitySelected", "Centrality percentile of selected events in the centrality interval; centrality percentile;entries", {HistType::kTH1D, {axisCentrality}}); + registry.add("hCentralityRejected", "Centrality percentile of selected events outside the centrality interval; centrality percentile;entries", {HistType::kTH1D, {axisCentrality}}); + } + } + } + + /// Collision selection + /// \param collision collision table with + template + void selectCollision(const Col& collision, + const BCsType& bcs) + { + float centrality{-1.f}; + o2::hf_evsel::HfCollisionRejectionMask rejectionMask{}; + + if constexpr (ApplyUpcSel) { + rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc( + collision, centrality, ccdb, registry, bcs); + } else { + rejectionMask = hfEvSel.getHfCollisionRejectionMask( + collision, centrality, ccdb, registry); + } + + if (fillHistograms) { + hfEvSel.fillHistograms(collision, rejectionMask, centrality); + // additional centrality histos + if constexpr (CentEstimator != o2::hf_centrality::None) { + if (rejectionMask == 0) { + registry.fill(HIST("hCentralitySelected"), centrality); + } else if (rejectionMask == BIT(EventRejection::Centrality)) { // rejected by centrality only + registry.fill(HIST("hCentralityRejected"), centrality); + } + } + } + + // fill table row + rowSelectedCollision(rejectionMask); + } + + /// Event selection with trigger and FT0A centrality selection + void processTrigAndCentFT0ASel(soa::Join::iterator const& collision, + aod::BcFullInfos const& bcs) + { + selectCollision(collision, bcs); + } + PROCESS_SWITCH(HfTrackSelectorTagSelCollisions, processTrigAndCentFT0ASel, "Use trigger and centrality selection with FT0A", false); + + /// Event selection with trigger and FT0C centrality selection + void processTrigAndCentFT0CSel(soa::Join::iterator const& collision, + aod::BcFullInfos const& bcs) + { + selectCollision(collision, bcs); + } + PROCESS_SWITCH(HfTrackSelectorTagSelCollisions, processTrigAndCentFT0CSel, "Use trigger and centrality selection with FT0C", false); + + /// Event selection with trigger and FT0M centrality selection + void processTrigAndCentFT0MSel(soa::Join::iterator const& collision, + aod::BcFullInfos const& bcs) + { + selectCollision(collision, bcs); + } + PROCESS_SWITCH(HfTrackSelectorTagSelCollisions, processTrigAndCentFT0MSel, "Use trigger and centrality selection with FT0M", false); + + /// Event selection with trigger and FV0A centrality selection + void processTrigAndCentFV0ASel(soa::Join::iterator const& collision, + aod::BcFullInfos const& bcs) + { + selectCollision(collision, bcs); + } + PROCESS_SWITCH(HfTrackSelectorTagSelCollisions, processTrigAndCentFV0ASel, "Use trigger and centrality selection with FV0A", false); + + /// Event selection with trigger selection + void processTrigSel(soa::Join::iterator const& collision, + aod::BcFullInfos const& bcs) + { + selectCollision(collision, bcs); + } + PROCESS_SWITCH(HfTrackSelectorTagSelCollisions, processTrigSel, "Use trigger selection", false); + + /// Event selection without trigger selection + void processNoTrigSel(soa::Join::iterator const& collision, + aod::BcFullInfos const& bcs) + { + selectCollision(collision, bcs); + } + PROCESS_SWITCH(HfTrackSelectorTagSelCollisions, processNoTrigSel, "Do not use trigger selection", true); + + /// Event selection with UPC + void processUpcSel(soa::Join::iterator const& collision, + aod::BcFullInfos const& bcs, + aod::FT0s const& /*ft0s*/, + aod::FV0As const& /*fv0as*/, + aod::FDDs const& /*fdds*/, + aod::Zdcs const& /*zdcs*/) + { + selectCollision(collision, bcs); + } + PROCESS_SWITCH(HfTrackSelectorTagSelCollisions, processUpcSel, "Use UPC event selection", false); +}; + +/// Track selection, ML based +struct HfTrackSelectorTagSelTracks { + Produces rowSelectedTrack; + + struct : ConfigurableGroup { + Configurable testAcknowledgement{"testAcknowledgement", false, "test acknowledgement"}; + Configurable fillHistograms{"fillHistograms", true, "fill histograms"}; + Configurable ptMinTrack{"ptMinTrack", 0.3f, "min. track pT entering the charm combinatorics"}; + Configurable etaMaxTrack{"etaMaxTrack", 0.8f, "max. track eta entering the charm combinatorics"}; + Configurable enableTiming{"enableTiming", false, "fill hTiming with the CPU of the feature building and of each model evaluation (adds two clock reads per call)"}; + // D+ model + Configurable applyMlDplus{"applyMlDplus", true, "evaluate the D+ track model"}; + Configurable onnxFileNameDplus{"onnxFileNameDplus", "ModelHandler_DplusTracks.onnx", "ONNX file name of the D+ track model"}; + Configurable> inputFeaturesDplus{"inputFeaturesDplus", std::vector{"pt", "eta", "dcaXY", "dcaZ", "sigmaDcaXY", "sigmaDcaZ", "normDcaXY", "normDcaZ", "signed1Pt", "tgl", "sign", "isPvContributor", "itsNCls", "itsNClsInnerBarrel", "itsChi2NCl", "tpcNClsFound", "tpcCrossedRowsOverFindableCls", "tpcChi2NCl", "tpcFractionSharedCls", "tpcNSigmaPi", "tpcNSigmaKa"}, "input features of the D+ model, in the order it expects"}; + Configurable thresholdScorePionDplus{"thresholdScorePionDplus", 0., "min. D+ pion-class score"}; + Configurable thresholdScoreKaonDplus{"thresholdScoreKaonDplus", 0., "min. D+ kaon-class score"}; + // Ds model + Configurable applyMlDs{"applyMlDs", true, "evaluate the Ds track model"}; + Configurable onnxFileNameDs{"onnxFileNameDs", "ModelHandler_DsTracks.onnx", "ONNX file name of the Ds track model"}; + Configurable> inputFeaturesDs{"inputFeaturesDs", std::vector{"pt", "eta", "dcaXY", "dcaZ", "sigmaDcaXY", "sigmaDcaZ", "normDcaXY", "normDcaZ", "signed1Pt", "tgl", "sign", "isPvContributor", "itsNCls", "itsNClsInnerBarrel", "itsChi2NCl", "tpcNClsFound", "tpcCrossedRowsOverFindableCls", "tpcChi2NCl", "tpcFractionSharedCls", "tpcNSigmaPi", "tpcNSigmaKa"}, "input features of the Ds model, in the order it expects"}; + Configurable thresholdScorePionDs{"thresholdScorePionDs", 0., "min. Ds pion-class score"}; + Configurable thresholdScoreKaonDs{"thresholdScoreKaonDs", 0., "min. Ds kaon-class score"}; + // ONNX runtime + Configurable loadModelsFromCcdb{"loadModelsFromCcdb", false, "load the ONNX models from CCDB instead of a local path"}; + Configurable mlModelPathCcdbDplus{"mlModelPathCcdbDplus", "path/to/ml/models/Dplus", "CCDB path of the D+ track model"}; + Configurable mlModelPathCcdbDs{"mlModelPathCcdbDs", "path/to/ml/models/Ds", "CCDB path of the Ds track model"}; + Configurable timestampCcdbForMlModels{"timestampCcdbForMlModels", -1, "timestamp of the ONNX files to be queried in CCDB"}; + Configurable enableOnnxOptimizations{"enableOnnxOptimizations", true, "enable the ONNX graph optimisations"}; + Configurable onnxThreads{"onnxThreads", 1, "number of threads used by the ONNX runtime (0 = let onnxruntime decide)"}; + // CCDB + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + } config; + + /// One track-level model: the response, its score thresholds and whether it runs. + struct HfTrackModel { + HfMlResponseHfTracks response{}; + double thresholdPion{0.}; + double thresholdKaon{0.}; + bool enabled{false}; + }; + std::array models; + + Service ccdb{}; + o2::ccdb::CcdbApi ccdbApi; + o2::base::MatLayerCylSet* lut{}; + o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + int runNumber{}; + + // running variables for the re-propagation of tracks to a non-default collision + o2::dataformats::DCA dcaInfoCov; + o2::dataformats::VertexBase vtx; + + using TracksWithSelAndPid = soa::Join; + using CollisionsWithSel = soa::Join; + using CollisionsWithCentFT0CAndSel = soa::Join; + + Preslice trackIndicesPerCollision = aod::track_association::collisionId; + + // per-data-frame accumulators for hTiming, filled into it at the end of each process call + std::array timing{}; + bool doTiming{false}; ///< cached config.enableTiming: read once, used on the hot path + + HistogramRegistry registry{"registry"}; + + /// Add this data frame's accumulated seconds to hTiming and reset the accumulators. + void flushTiming() + { + if (doTiming) { + // Fill(bin, weight) so the bins accumulate seconds over the whole run + for (int iStep = 0; iStep < NTrackTimingSteps; iStep++) { + registry.fill(HIST("hTiming"), iStep, timing[iStep]); + } + } + timing.fill(0.); + } + + /// Configure one model from its configurables. + /// \param model is the model to configure + /// \param onnxFile is the ONNX file name + /// \param ccdbPath is the CCDB path of the model, used if the models are loaded from CCDB + /// \param features are the input feature names, in the order the model expects + /// \param thrPion, thrKaon are the score thresholds + /// \param name is used in the log message + void configureModel(HfTrackModel& model, + std::string const& onnxFile, + std::string const& ccdbPath, + std::vector const& features, + const double thrPion, + const double thrKaon, + const char* name) + { + model.thresholdPion = thrPion; + model.thresholdKaon = thrKaon; + + const std::vector binsPtSingle{0., PtMaxModel}; + const std::vector onnxFiles{onnxFile}; + const auto nOut = static_cast(HfTrackMlClass::NClasses); + const std::vector cutDir(nOut, o2::cuts_ml::CutDirection::CutNot); + const std::vector dummyCutValues(nOut, 0.); + const o2::framework::LabeledArray dummyCuts{dummyCutValues.data(), 1u, static_cast(nOut), {}, {}}; + + model.response.configure(binsPtSingle, dummyCuts, cutDir, static_cast(nOut)); + if (config.loadModelsFromCcdb) { + ccdbApi.init(config.ccdbUrl); + model.response.setModelPathsCCDB(onnxFiles, ccdbApi, std::vector{ccdbPath}, config.timestampCcdbForMlModels); + } else { + model.response.setModelPathsLocal(onnxFiles); + } + model.response.cacheInputFeaturesIndices(features); + model.response.init(config.enableOnnxOptimizations, config.onnxThreads); + model.enabled = true; + LOGP(info, "{}: configured from {} with {} input features and {} output classes (pion score > {}, kaon score > {})", + name, config.loadModelsFromCcdb ? "CCDB " + ccdbPath : onnxFile, features.size(), nOut, thrPion, thrKaon); + } + + void init(InitContext const&) + { + if (!config.testAcknowledgement) { + LOGF(fatal, "ml-based-track-selector is for test only, please do not use for analysis. If you are aware of what you are doing, set testAcknowledgement to true in the configuration."); + } + + const std::array doProcess = {doprocessTracks, doprocessTracksWithCentFT0C}; + if (std::count(doProcess.begin(), doProcess.end(), true) != 1) { + LOGP(fatal, "One and only one process function of HfTrackSelectorTagSelTracks can be enabled at a time!"); + } + + if (config.applyMlDplus) { + configureModel(models[ChannelDplusToPiKPi], config.onnxFileNameDplus, config.mlModelPathCcdbDplus, config.inputFeaturesDplus, + config.thresholdScorePionDplus, config.thresholdScoreKaonDplus, "D+ track model"); + } + if (config.applyMlDs) { + configureModel(models[ChannelDsToKKPi], config.onnxFileNameDs, config.mlModelPathCcdbDs, config.inputFeaturesDs, + config.thresholdScorePionDs, config.thresholdScoreKaonDs, "Ds track model"); + } + if (!models[ChannelDplusToPiKPi].enabled && !models[ChannelDsToKKPi].enabled) { + LOGP(fatal, "At least one of the D+ and Ds track models must be enabled!"); + } + + ccdb->setURL(config.ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(config.ccdbPathLut)); + runNumber = 0; + + if (config.fillHistograms) { + const AxisSpec axisPtProng{360, 0., 36., "#it{p}_{T}^{track} (GeV/#it{c})"}; + const AxisSpec axisScore{100, 0., 1., "ML score"}; + const AxisSpec axisEta{100, -1., 1., "#it{#eta}"}; + + registry.add("hPtNoCuts", "all track associations;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPtProng}}); + registry.add("hPtQuality", "track associations passing the pT floor and the quality cut, scored;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPtProng}}); + registry.add("hPtQualityRejColl", "track associations passing the pT floor and the quality cut, not scored: collision fails the HF event selection;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPtProng}}); + registry.add("hPtSelected", "track associations selected by at least one model;#it{p}_{T}^{track} (GeV/#it{c});entries", {HistType::kTH1D, {axisPtProng}}); + registry.add("hEtaSelected", "track associations selected by at least one model;#it{#eta};entries", {HistType::kTH1D, {axisEta}}); + registry.add("hScorePionDplus", "D^{#plus} pion-class score;#it{p}_{T}^{track} (GeV/#it{c});score;entries", {HistType::kTH2D, {axisPtProng, axisScore}}); + registry.add("hScoreKaonDplus", "D^{#plus} kaon-class score;#it{p}_{T}^{track} (GeV/#it{c});score;entries", {HistType::kTH2D, {axisPtProng, axisScore}}); + registry.add("hScorePionDs", "D_{s}^{#plus} pion-class score;#it{p}_{T}^{track} (GeV/#it{c});score;entries", {HistType::kTH2D, {axisPtProng, axisScore}}); + registry.add("hScoreKaonDs", "D_{s}^{#plus} kaon-class score;#it{p}_{T}^{track} (GeV/#it{c});score;entries", {HistType::kTH2D, {axisPtProng, axisScore}}); + // seconds, accumulated over the run via Fill(bin, weight); per-call cost = bin / hPtQuality entries + auto hTiming = registry.add("hTiming", "CPU of the ML track selection;;seconds", {HistType::kTH1D, {{NTrackTimingSteps, -0.5f, static_cast(NTrackTimingSteps) - 0.5f}}}); + hTiming->GetXaxis()->SetBinLabel(TrackTimeTotal + 1, "track loop total"); + hTiming->GetXaxis()->SetBinLabel(TrackTimeFeatures + 1, "features"); + hTiming->GetXaxis()->SetBinLabel(TrackTimeModelDplus + 1, "D+ model"); + hTiming->GetXaxis()->SetBinLabel(TrackTimeModelDs + 1, "Ds model"); + } + doTiming = config.enableTiming && config.fillHistograms; + } + + /// Fill the container of ML input features for one (track, collision) association. + /// The DCA and its resolution are recomputed by propagating to the collision under study + /// whenever that is not the track's own collision. + /// \param collision is the collision the track is associated to + /// \param track is the track + /// \param centrality is the centrality percentile of the collision + /// \param features is the container to be filled + template + void fillFeatures(TCollision const& collision, TTrack const& track, float centrality, HfTrackMlFeatures& features) + { + float trackPt = track.pt(); + float trackEta = track.eta(); + float dcaXY = track.dcaXY(); + float dcaZ = track.dcaZ(); + float sigmaDcaXY = std::sqrt(std::abs(track.sigmaDcaXY2())); + float sigmaDcaZ = std::sqrt(std::abs(track.sigmaDcaZ2())); + + if (track.collisionId() != collision.globalIndex()) { + // not the default collision of this track: re-propagate to get DCA and its covariance + auto trackParCov = getTrackParCov(track); + dcaInfoCov.set(999.f, 999.f, 999.f, 999.f, 999.f); + vtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); + vtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + if (o2::base::Propagator::Instance()->propagateToDCABxByBz(vtx, trackParCov, 2.f, noMatCorr, &dcaInfoCov)) { + trackPt = trackParCov.getPt(); + trackEta = trackParCov.getEta(); + dcaXY = dcaInfoCov.getY(); + dcaZ = dcaInfoCov.getZ(); + sigmaDcaXY = std::sqrt(std::abs(dcaInfoCov.getSigmaY2())); + sigmaDcaZ = std::sqrt(std::abs(dcaInfoCov.getSigmaZ2())); + } + } + + constexpr float MinSigmaDca = 1.e-6f; // guard against a null resolution before dividing + + features.pt = trackPt; + features.eta = trackEta; + features.dcaXY = dcaXY; + features.dcaZ = dcaZ; + features.sigmaDcaXY = sigmaDcaXY; + features.sigmaDcaZ = sigmaDcaZ; + features.normDcaXY = dcaXY / std::max(sigmaDcaXY, MinSigmaDca); + features.normDcaZ = dcaZ / std::max(sigmaDcaZ, MinSigmaDca); + features.signed1Pt = track.signed1Pt(); + features.tgl = track.tgl(); + features.sign = static_cast(track.sign()); + features.isPvContributor = static_cast(track.isPVContributor()); + features.itsNCls = static_cast(track.itsNCls()); + features.itsNClsInnerBarrel = static_cast(track.itsNClsInnerBarrel()); + features.itsChi2NCl = track.itsChi2NCl(); + features.tpcNClsFound = static_cast(track.tpcNClsFound()); + features.tpcNClsCrossedRows = static_cast(track.tpcNClsCrossedRows()); + features.tpcCrossedRowsOverFindableCls = track.tpcCrossedRowsOverFindableCls(); + features.tpcChi2NCl = track.tpcChi2NCl(); + features.tpcFractionSharedCls = track.tpcFractionSharedCls(); + features.tpcNSigmaPi = track.tpcNSigmaPi(); + features.tpcNSigmaKa = track.tpcNSigmaKa(); + features.centrality = centrality; + } + + /// Evaluate one model and turn its output into the two role decisions. + /// \param model is the model to evaluate + /// \param features are the track features + /// \param scores is filled with (background, pion-role, kaon-role); -1 if not evaluated + /// \param isSelPion, isSelKaon are filled with the threshold decisions + /// \return true if the model was actually evaluated + bool evaluateModel(HfTrackModel& model, HfTrackMlFeatures const& features, + std::array& scores, bool& isSelPion, bool& isSelKaon) + { + scores = {-1.f, -1.f, -1.f}; + isSelPion = false; + isSelKaon = false; + if (!model.enabled) { + return false; + } + auto inputFeatures = model.response.getInputFeatures(features); + // one model for the whole pT range, so the bin index is always 0 + const auto output = model.response.getModelOutput(inputFeatures, 0); + if (output.size() != scores.size()) { + LOGP(fatal, "the model emitted {} scores, expected {}: only 3-class (bkg/pi/K) models are supported", output.size(), scores.size()); + } + std::copy(output.begin(), output.end(), scores.begin()); + + isSelPion = scores[1] > model.thresholdPion; + isSelKaon = scores[2] > model.thresholdKaon; + return true; + } + + /// Selection tag for tracks + /// \param collision is the collision iterator + /// \param trackIndicesCollision are the track indices associated to this collision + /// \param centrality is the centrality percentile of the collision + /// \param isCollisionSelected is whether the collision passes the HF event selection + template + void runTagSelTracks(TCollision const& collision, + TTracks const& /*tracks*/, + GroupedTrackIndices const& trackIndicesCollision, + float centrality, + const bool isCollisionSelected) + { + const bool scoreCollision = isCollisionSelected; + + for (const auto& trackId : trackIndicesCollision) { + const auto track = trackId.template track_as(); + + // One row per track association, always: aod::HfSelTrack is joined with aod::TrackAssoc + // downstream, so the two tables have to stay index-aligned. Rejected associations are + // written with an empty selection mask, not skipped. + uint32_t statusProng{0}; + uint32_t isIdentifiedPid{0}; + + if (config.fillHistograms) { + registry.fill(HIST("hPtNoCuts"), track.pt()); + } + + const bool passesQuality = track.pt() >= config.ptMinTrack && std::abs(track.eta()) <= config.etaMaxTrack && track.isGlobalTrackWoDCA(); + if (passesQuality && !scoreCollision && config.fillHistograms) { + registry.fill(HIST("hPtQualityRejColl"), track.pt()); + } + + if (passesQuality && scoreCollision) { + if (config.fillHistograms) { + registry.fill(HIST("hPtQuality"), track.pt()); + } + + HfTrackMlFeatures features{}; + auto tStart = tickIf(doTiming); + fillFeatures(collision, track, centrality, features); + if (doTiming) { + timing[TrackTimeFeatures] += elapsedSeconds(tStart); + } + + std::array scores{}; + bool isSelPion{false}; + bool isSelKaon{false}; + + tStart = tickIf(doTiming); + const bool evaluatedDplus = evaluateModel(models[ChannelDplusToPiKPi], features, scores, isSelPion, isSelKaon); + if (doTiming) { + timing[TrackTimeModelDplus] += elapsedSeconds(tStart); + } + if (evaluatedDplus) { + if (isSelPion) { + SETBIT(isIdentifiedPid, RolePionDplus); + } + if (isSelKaon) { + SETBIT(isIdentifiedPid, RoleKaonDplus); + } + if (config.fillHistograms) { + registry.fill(HIST("hScorePionDplus"), features.pt, scores[1]); + registry.fill(HIST("hScoreKaonDplus"), features.pt, scores[2]); + } + } + + tStart = tickIf(doTiming); + const bool evaluatedDs = evaluateModel(models[ChannelDsToKKPi], features, scores, isSelPion, isSelKaon); + if (doTiming) { + timing[TrackTimeModelDs] += elapsedSeconds(tStart); + } + if (evaluatedDs) { + if (isSelPion) { + SETBIT(isIdentifiedPid, RolePionDs); + } + if (isSelKaon) { + SETBIT(isIdentifiedPid, RoleKaonDs); + } + if (config.fillHistograms) { + registry.fill(HIST("hScorePionDs"), features.pt, scores[1]); + registry.fill(HIST("hScoreKaonDs"), features.pt, scores[2]); + } + } + + // the track enters the combinatorics if it can play any role in any of the two channels + if (isIdentifiedPid != 0u) { + SETBIT(statusProng, CandidateType::Cand3Prong); + if (config.fillHistograms) { + registry.fill(HIST("hPtSelected"), features.pt); + registry.fill(HIST("hEtaSelected"), features.eta); + } + } + } + + const bool isPositive = track.sign() > 0; + rowSelectedTrack(statusProng, isIdentifiedPid, isPositive); + } + } + + /// Set the magnetic field and the material LUT for the current collision, needed by the + /// re-propagation of tracks associated to a collision that is not their own. + template + void initMagneticField(TCollision const& collision) + { + const auto bc = collision.template bc_as(); + initCCDB(bc, runNumber, ccdb, config.ccdbPathGrpMag, lut, false); + } + + void processTracks(CollisionsWithSel const& collisions, + aod::TrackAssoc const& trackIndices, + TracksWithSelAndPid const& tracks, + aod::BCsWithTimestamps const&) + { + rowSelectedTrack.reserve(trackIndices.size()); + const auto tStart = tickIf(doTiming); + for (const auto& collision : collisions) { + initMagneticField(collision); + const auto groupedTrackIndices = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + runTagSelTracks(collision, tracks, groupedTrackIndices, -1.f, collision.whyRejectColl() == 0); + } + if (doTiming) { + timing[TrackTimeTotal] += elapsedSeconds(tStart); + } + flushTiming(); + } + PROCESS_SWITCH(HfTrackSelectorTagSelTracks, processTracks, "Process tracks without centrality", true); + + void processTracksWithCentFT0C(CollisionsWithCentFT0CAndSel const& collisions, + aod::TrackAssoc const& trackIndices, + TracksWithSelAndPid const& tracks, + aod::BCsWithTimestamps const&) + { + rowSelectedTrack.reserve(trackIndices.size()); + const auto tStart = tickIf(doTiming); + for (const auto& collision : collisions) { + initMagneticField(collision); + const auto groupedTrackIndices = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + runTagSelTracks(collision, tracks, groupedTrackIndices, collision.centFT0C(), collision.whyRejectColl() == 0); + } + if (doTiming) { + timing[TrackTimeTotal] += elapsedSeconds(tStart); + } + flushTiming(); + } + PROCESS_SWITCH(HfTrackSelectorTagSelTracks, processTracksWithCentFT0C, "Process tracks with FT0C centrality as ML input feature", false); +}; + +/// Pre-selection of 3-prong secondary vertices +struct HfMlBasedTrackSelector { + Produces rowTrackIndexProng3; + + struct : ConfigurableGroup { + Configurable fillHistograms{"fillHistograms", true, "fill histograms"}; + // preselection + Configurable ptTolerance{"ptTolerance", 0.1, "pT tolerance in GeV/c for applying preselections before vertex reconstruction"}; + // preselection of 3-prongs using the decay length computed only with the first two tracks + Configurable minTwoTrackDecayLengthFor3Prongs{"minTwoTrackDecayLengthFor3Prongs", 0., "Minimum decay length computed with 2 tracks for 3-prongs to speedup combinatorial"}; + Configurable maxTwoTrackChi2PcaFor3Prongs{"maxTwoTrackChi2PcaFor3Prongs", 1.e10, "Maximum chi2 pca computed with 2 tracks for 3-prongs to speedup combinatorial"}; + Configurable enableTiming{"enableTiming", false, "fill hTiming/hLoopCounters with the CPU and rates of the triple loop (adds two clock reads per fit)"}; + // vertexing + Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; + Configurable useAbsDCA{"useAbsDCA", false, "Minimise abs. distance rather than chi2"}; + Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; + Configurable maxR{"maxR", 200., "reject PCA's above this radius"}; + Configurable maxDZIni{"maxDZIni", 4., "reject (if>0) PCA candidate if tracks DZ exceeds threshold"}; + Configurable minParamChange{"minParamChange", 1.e-3, "stop iterations if largest change of any X is smaller than this"}; + Configurable minRelChi2Change{"minRelChi2Change", 0.9, "stop iterations if chi2/chi2old > this"}; + // CCDB + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + + // D+ cuts + Configurable> binsPtDplusToPiKPi{"binsPtDplusToPiKPi", std::vector{hf_cuts_presel_3prong::vecBinsPt}, "pT bin limits for D+->piKpi pT-dependent cuts"}; + Configurable> cutsDplusToPiKPi{"cutsDplusToPiKPi", {hf_cuts_presel_3prong::Cuts[0], hf_cuts_presel_3prong::NBinsPt, hf_cuts_presel_3prong::NCutVars, hf_cuts_presel_3prong::labelsPt, hf_cuts_presel_3prong::labelsCutVar}, "D+->piKpi selections per pT bin"}; + // Ds+ cuts + Configurable> binsPtDsToKKPi{"binsPtDsToKKPi", std::vector{hf_cuts_presel_ds::vecBinsPt}, "pT bin limits for Ds+->KKPi pT-dependent cuts"}; + Configurable> cutsDsToKKPi{"cutsDsToKKPi", {hf_cuts_presel_ds::Cuts[0], hf_cuts_presel_ds::NBinsPt, hf_cuts_presel_ds::NCutVars, hf_cuts_presel_ds::labelsPt, hf_cuts_presel_ds::labelsCutVar}, "Ds+->KKPi selections per pT bin"}; + } config; + + SliceCache cache; + o2::vertexing::DCAFitterN<2> df2; // 2-prong vertex fitter, only used for the 3-prong speed-up + o2::vertexing::DCAFitterN<3> df3; // 3-prong vertex fitter + Service ccdb{}; + o2::base::MatLayerCylSet* lut{}; + o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + int runNumber{}; + + // masses of the two mass hypotheses of each channel, in the (same-sign, opposite-sign, + // same-sign) prong ordering used by the combinatorics + std::array, NMassHypos>, NChannels3Prong> arrMass3Prong{}; + // cuts and pT binning, one entry per channel + std::array, NChannels3Prong> cut3Prong{}; + std::array, NChannels3Prong> binsPt3Prong{}; + + /// One track of the collision under study, already propagated to that collision's PV. + struct HfProngCandidate { + o2::track::TrackParCov parCov; + std::array pVec{}; + uint32_t mask{}; ///< aod::HfSelTrack::isIdentifiedPid + int64_t globalIndex{}; ///< index in the track table, written to the skim + }; + + /// Cache of propagated tracks for one collision, split by charge. + struct HfProngCache { + std::vector prongs; + std::vector sameSign; ///< can sit in a same-sign slot: pi(D+), pi(Ds) or K(Ds) + std::vector oppSign; ///< can sit in the opposite-sign slot: K(D+) or K(Ds) + + void clear() + { + prongs.clear(); + sameSign.clear(); + oppSign.clear(); + } + }; + HfProngCache cachePos; + HfProngCache cacheNeg; + + // per-collision accumulators for hTiming / hLoopCounters, reset at each collision + std::array timing{}; + std::array counters{}; + bool doTiming{false}; ///< cached config.enableTiming: read once, used on the hot path + + using SelectedCollisions = soa::Filtered>; + using FilteredTrackAssocSel = soa::Filtered>; + + // filter collisions + Filter filterSelectCollisions = (aod::hf_sel_collision::whyRejectColl == static_cast(0)); + // filter track indices + Filter filterSelectTrackIds = ((aod::hf_sel_track::isSelProng & static_cast(BIT(CandidateType::Cand3Prong))) != 0u); + + Preslice trackIndicesPerCollision = aod::track_association::collisionId; + + Partition positiveFor3Prongs = aod::hf_sel_track::isPositive == true && ((aod::hf_sel_track::isSelProng & static_cast(BIT(CandidateType::Cand3Prong))) != 0u); + Partition negativeFor3Prongs = aod::hf_sel_track::isPositive == false && ((aod::hf_sel_track::isSelProng & static_cast(BIT(CandidateType::Cand3Prong))) != 0u); + + HistogramRegistry registry{"registry"}; + + void init(InitContext const&) + { + if (!doprocess3Prongs) { + return; + } + doTiming = config.enableTiming && config.fillHistograms; + + arrMass3Prong[ChannelDplusToPiKPi] = std::array{std::array{MassPiPlus, MassKPlus, MassPiPlus}, + std::array{MassPiPlus, MassKPlus, MassPiPlus}}; + + arrMass3Prong[ChannelDsToKKPi] = std::array{std::array{MassKPlus, MassKPlus, MassPiPlus}, + std::array{MassPiPlus, MassKPlus, MassKPlus}}; + + // cuts retrieved by json, in the order of Channel3Prong + cut3Prong = {config.cutsDplusToPiKPi, config.cutsDsToKKPi}; + binsPt3Prong = {config.binsPtDplusToPiKPi, config.binsPtDsToKKPi}; + + df2.setPropagateToPCA(config.propagateToPCA); + df2.setMaxR(config.maxR); + df2.setMaxDZIni(config.maxDZIni); + df2.setMinParamChange(config.minParamChange); + df2.setMinRelChi2Change(config.minRelChi2Change); + df2.setUseAbsDCA(config.useAbsDCA); + df2.setWeightedFinalPCA(config.useWeightedFinalPCA); + + df3.setPropagateToPCA(config.propagateToPCA); + df3.setMaxR(config.maxR); + df3.setMaxDZIni(config.maxDZIni); + df3.setMinParamChange(config.minParamChange); + df3.setMinRelChi2Change(config.minRelChi2Change); + df3.setUseAbsDCA(config.useAbsDCA); + df3.setWeightedFinalPCA(config.useWeightedFinalPCA); + + ccdb->setURL(config.ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(config.ccdbPathLut)); + runNumber = 0; + + if (config.fillHistograms) { + const AxisSpec axisNumTracks{500, -0.5f, 499.5f, "Number of tracks"}; + const AxisSpec axisNumCands{1000, -0.5f, 999.5f, "Number of candidates"}; + registry.add("hNTracks", "Number of selected tracks;# of selected tracks;entries", {HistType::kTH1D, {axisNumTracks}}); + // seconds and raw counts, accumulated over the run via Fill(bin, weight) + auto hTiming = registry.add("hTiming", "CPU inside the triple loop;;seconds", {HistType::kTH1D, {{NTimingSteps, -0.5f, static_cast(NTimingSteps) - 0.5f}}}); + hTiming->GetXaxis()->SetBinLabel(TimeLoopTotal + 1, "triple loop total"); + hTiming->GetXaxis()->SetBinLabel(TimeFit2Prong + 1, "2-prong vertex fit"); + hTiming->GetXaxis()->SetBinLabel(TimeFit3Prong + 1, "3-prong vertex fit"); + hTiming->GetXaxis()->SetBinLabel(TimeCacheFill + 1, "prong cache fill"); + hTiming->GetXaxis()->SetBinLabel(TimeCachePropagate + 1, "cache re-propagation"); + auto hLoopCounters = registry.add("hLoopCounters", "Triple loop stages;;entries", {HistType::kTH1D, {{NLoopCounters, -0.5f, static_cast(NLoopCounters) - 0.5f}}}); + hLoopCounters->GetXaxis()->SetBinLabel(CountPairsSeen + 1, "pairs seen"); + hLoopCounters->GetXaxis()->SetBinLabel(CountPairsCandidateOk + 1, "pairs passing ML roles"); + hLoopCounters->GetXaxis()->SetBinLabel(CountFit2Prong + 1, "2-prong fits"); + hLoopCounters->GetXaxis()->SetBinLabel(CountPairsRejected2P + 1, "pairs rejected by 2-prong"); + hLoopCounters->GetXaxis()->SetBinLabel(CountTripletsEnumerated + 1, "triplets enumerated"); + hLoopCounters->GetXaxis()->SetBinLabel(CountTripletsWritten + 1, "triplets written"); + hLoopCounters->GetXaxis()->SetBinLabel(CountTracksCached + 1, "tracks cached"); + hLoopCounters->GetXaxis()->SetBinLabel(CountTracksPropagated + 1, "tracks re-propagated"); + registry.add("hVtx3ProngX", "3-prong candidates;#it{x}_{sec. vtx.} (cm);entries", {HistType::kTH1D, {{1000, -2., 2.}}}); + registry.add("hVtx3ProngY", "3-prong candidates;#it{y}_{sec. vtx.} (cm);entries", {HistType::kTH1D, {{1000, -2., 2.}}}); + registry.add("hVtx3ProngZ", "3-prong candidates;#it{z}_{sec. vtx.} (cm);entries", {HistType::kTH1D, {{1000, -20., 20.}}}); + registry.add("hNCand3Prong", "3-prong candidates preselected;# of candidates;entries", {HistType::kTH1D, {axisNumCands}}); + registry.add("hNCand3ProngVsNTracks", "3-prong candidates preselected;# of selected tracks;# of candidates;entries", {HistType::kTH2D, {axisNumTracks, axisNumCands}}); + registry.add("hMassDPlusToPiKPi", "D^{#plus} candidates;inv. mass (#pi K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); + registry.add("hMassDsToKKPi", "D_{s}^{#plus} candidates;inv. mass (K K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); + } + } + + /// Check whether the two prongs can satisfy any mass hypothesis of either channel, based on their ML role masks. + /// \param maskOpp is the isIdentifiedPid mask of the opposite-sign prong + /// \param maskSame is the isIdentifiedPid mask of the first same-sign prong + static bool isCandidateAllowed(const uint32_t maskOpp, const uint32_t maskSame) + { + const bool okDplus = TESTBIT(maskOpp, RoleKaonDplus) && TESTBIT(maskSame, RolePionDplus); + const bool okDs = TESTBIT(maskOpp, RoleKaonDs) && (TESTBIT(maskSame, RoleKaonDs) || TESTBIT(maskSame, RolePionDs)); + return okDplus || okDs; + } + + /// Check whether the three ML masks can satisfy any mass hypothesis of either channel + /// \param maskOpp is the mask of the opposite-sign prong (prong 1) + /// \param mask1, mask2 are the masks of the same-sign prongs (prongs 0 and 2) + static bool tripletRoleOk(const uint32_t maskOpp, const uint32_t mask1, const uint32_t mask2) + { + const std::array masks{mask1, maskOpp, mask2}; + for (int iChannel = 0; iChannel < NChannels3Prong; iChannel++) { + for (int iHypo = 0; iHypo < NMassHypos; iHypo++) { + bool isHypoAlive = true; + for (int iProng = 0; iProng < NProngs; iProng++) { + const int role = IsKaonProng[iChannel][iHypo][iProng] ? RoleKaonOfChannel[iChannel] : RolePionOfChannel[iChannel]; + if (!TESTBIT(masks[iProng], role)) { + isHypoAlive = false; + break; + } + } + if (isHypoAlive) { + return true; + } + } + } + return false; + } + + /// Require the ML role of each prong to match the mass hypothesis under test. + /// The opposite-sign prong is the kaon in both channels; the same-sign pair is (pi, pi) for the + /// D+ and (K, pi) or (pi, K) for the Ds, which is exactly what the two mass hypotheses encode. + /// \param isIdentifiedPid0,1,2 are the aod::HfSelTrack::isIdentifiedPid masks of the prongs, in + /// the (same-sign, opposite-sign, same-sign) ordering + /// \param whichHypo information of the mass hypotheses that are still alive + /// \param isSelected is a bitmap with selection outcome + void applyMlRoleSelection(const uint32_t isIdentifiedPid0, const uint32_t isIdentifiedPid1, const uint32_t isIdentifiedPid2, + std::array& whichHypo, auto& isSelected) + { + const std::array isIdentifiedPid{isIdentifiedPid0, isIdentifiedPid1, isIdentifiedPid2}; + + for (int iChannel = 0; iChannel < NChannels3Prong; iChannel++) { + for (int iHypo = 0; iHypo < NMassHypos; iHypo++) { + for (int iProng = 0; iProng < NProngs; iProng++) { + const int role = IsKaonProng[iChannel][iHypo][iProng] ? RoleKaonOfChannel[iChannel] : RolePionOfChannel[iChannel]; + if (!TESTBIT(isIdentifiedPid[iProng], role)) { + CLRBIT(whichHypo[iChannel], iHypo); + break; + } + } + } + if (whichHypo[iChannel] == 0) { + CLRBIT(isSelected, iChannel); + } + } + } + + /// Method to perform selections on difference from nominal mass for phi decay + /// \param binPt pt bin for the cuts + /// \param pVecTrack0 is the momentum array of the first daughter track + /// \param pVecTrack1 is the momentum array of the second daughter track + /// \param pVecTrack2 is the momentum array of the third daughter track + /// \param whichHypo information of the mass hypoteses that were selected + /// \param isSelected is a bitmap with selection outcome + template + void applyPreselectionPhiDecay(const int binPt, T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, + std::array& whichHypo, auto& isSelected) + { + const double deltaMassMax = cut3Prong[ChannelDsToKKPi].get(binPt, 5u); // 5u == "deltaMassKK" + if (TESTBIT(whichHypo[ChannelDsToKKPi], 0)) { + const double mass2PhiKKPi = RecoDecay::m2(std::array{pVecTrack0, pVecTrack1}, std::array{arrMass3Prong[ChannelDsToKKPi][0][0], arrMass3Prong[ChannelDsToKKPi][0][1]}); + if (mass2PhiKKPi > (MassPhi + deltaMassMax) * (MassPhi + deltaMassMax) || (deltaMassMax < MassPhi && mass2PhiKKPi < (MassPhi - deltaMassMax) * (MassPhi - deltaMassMax))) { + CLRBIT(whichHypo[ChannelDsToKKPi], 0); + } + } + if (TESTBIT(whichHypo[ChannelDsToKKPi], 1)) { + const double mass2PhiPiKK = RecoDecay::m2(std::array{pVecTrack1, pVecTrack2}, std::array{arrMass3Prong[ChannelDsToKKPi][1][1], arrMass3Prong[ChannelDsToKKPi][1][2]}); + if (mass2PhiPiKK > (MassPhi + deltaMassMax) * (MassPhi + deltaMassMax) || (deltaMassMax < MassPhi && mass2PhiPiKK < (MassPhi - deltaMassMax) * (MassPhi - deltaMassMax))) { + CLRBIT(whichHypo[ChannelDsToKKPi], 1); + } + } + if (whichHypo[ChannelDsToKKPi] == 0) { + CLRBIT(isSelected, ChannelDsToKKPi); + } + } + + /// Method to perform selections for 3-prong candidates before vertex reconstruction + /// \param pVecTrack0 is the momentum array of the first daughter track + /// \param pVecTrack1 is the momentum array of the second daughter track + /// \param pVecTrack2 is the momentum array of the third daughter track + /// \param whichHypo information of the mass hypotheses that were selected + /// \param isSelected is a bitmap with selection outcome + template + void applyPreselection3Prong(T2 const& pVecTrack0, T2 const& pVecTrack1, T2 const& pVecTrack2, + std::array& whichHypo, auto& isSelected) + { + const auto pt = RecoDecay::pt(pVecTrack0, pVecTrack1, pVecTrack2) + config.ptTolerance; // add tolerance because of no reco decay vertex + + for (int iChannel = 0; iChannel < NChannels3Prong; iChannel++) { + if (!TESTBIT(isSelected, iChannel)) { + whichHypo[iChannel] = 0; + continue; + } + + // pT + const auto binPt = findBin(&binsPt3Prong[iChannel], pt); + // return immediately if it is outside the defined pT bins + if (binPt == -1) { + CLRBIT(isSelected, iChannel); + whichHypo[iChannel] = 0; + continue; + } + + // invariant mass + const double minMass = cut3Prong[iChannel].get(binPt, 0u); + const double maxMass = cut3Prong[iChannel].get(binPt, 1u); + if (minMass >= 0. && maxMass > 0.) { + std::array massHypos = {0., 0.}; + const std::array arrMom{pVecTrack0, pVecTrack1, pVecTrack2}; + const double min2 = minMass * minMass; + const double max2 = maxMass * maxMass; + massHypos[0] = RecoDecay::m2(arrMom, arrMass3Prong[iChannel][0]); + massHypos[1] = (iChannel != ChannelDplusToPiKPi) ? RecoDecay::m2(arrMom, arrMass3Prong[iChannel][1]) : massHypos[0]; + if (massHypos[0] < min2 || massHypos[0] >= max2) { + CLRBIT(whichHypo[iChannel], 0); + } + if (massHypos[1] < min2 || massHypos[1] >= max2) { + CLRBIT(whichHypo[iChannel], 1); + } + if (whichHypo[iChannel] == 0) { + CLRBIT(isSelected, iChannel); + continue; + } + } + + // prong pT + const auto ptProngMin = cut3Prong[iChannel].get(binPt, 4u); // 4u == "ptProngMin" + const auto pt2ProngMin = ptProngMin * ptProngMin; + if (RecoDecay::pt2(pVecTrack0) < pt2ProngMin || RecoDecay::pt2(pVecTrack1) < pt2ProngMin || RecoDecay::pt2(pVecTrack2) < pt2ProngMin) { + CLRBIT(isSelected, iChannel); + continue; + } + + if (iChannel == ChannelDsToKKPi) { + applyPreselectionPhiDecay(binPt, pVecTrack0, pVecTrack1, pVecTrack2, whichHypo, isSelected); + } + } + } + + /// Method to perform selections for a 2-track vertex used to speed up the 3-prong combinatorics + /// \param secVtx is the secondary vertex + /// \param primVtx is the primary vertex + /// \param dcaFitter is the DCAFitter used for the 2-track vertex + /// \returns true if the candidate is selected + template + bool isTwoTrackVertexSelectedFor3Prongs(const T1& secVtx, const T2& primVtx, const T3& dcaFitter) + { + if (dcaFitter.getChi2AtPCACandidate() > config.maxTwoTrackChi2PcaFor3Prongs) { + return false; + } + const auto decLen = RecoDecay::distance(primVtx, secVtx); + return static_cast(decLen >= config.minTwoTrackDecayLengthFor3Prongs); + } + + /// Method to perform selections for 3-prong candidates after vertex reconstruction + /// \param pVecCand is the array for the candidate momentum after reconstruction of secondary vertex + /// \param secVtx is the secondary vertex + /// \param primVtx is the primary vertex + /// \param isSelected is a bitmap with selection outcome + template + void applySelection3Prong(const T1& pVecCand, const T2& secVtx, const T3& primVtx, auto& isSelected) + { + if (isSelected == 0) { + return; + } + + const auto pt = RecoDecay::pt(pVecCand); + for (int iChannel = 0; iChannel < NChannels3Prong; iChannel++) { + if (!TESTBIT(isSelected, iChannel)) { + continue; + } + + // pT + const auto binPt = findBin(&binsPt3Prong[iChannel], pt); + if (binPt == -1) { // cut if it is outside the defined pT bins + CLRBIT(isSelected, iChannel); + continue; + } + + // cos of pointing angle + const auto cpa = RecoDecay::cpa(primVtx, secVtx, pVecCand); + if (cpa < cut3Prong[iChannel].get(binPt, 2u)) { // 2u == "cosp" + CLRBIT(isSelected, iChannel); + continue; + } + + // decay length + const auto decayLength = RecoDecay::distance(primVtx, secVtx); + if (decayLength < cut3Prong[iChannel].get(binPt, 3u)) { // 3u == "decL" + CLRBIT(isSelected, iChannel); + } + } + } + + /// Translate the compact per-channel selection bitmap into the hfflag written to aod::Hf3Prongs, + /// which uses the hf_cand_3prong::DecayType bit positions. + uint8_t hfFlagOfSelection(const uint32_t isSelected) const + { + uint8_t hfFlag{0}; + for (int iChannel = 0; iChannel < NChannels3Prong; iChannel++) { + if (TESTBIT(isSelected, iChannel)) { + SETBIT(hfFlag, DecayTypeOfChannel[iChannel]); + } + } + return hfFlag; + } + + /// Reconstruct and preselect one 3-prong candidate, and fill its table row if it survives. + /// \param collision is the collision the candidate belongs to + /// \param trackParVar0,1,2 are the track parametrisations, in the (same-sign, opposite-sign, + /// same-sign) prong ordering + /// \param pVecTrack0,1,2 are the corresponding momenta at the primary vertex + /// \param isIdentifiedPid0,1,2 are the corresponding ML role masks + /// \param globalIndex0,1,2 are the corresponding track global indices + template + void processTriplet(SelectedCollisions::iterator const& collision, + TTrackParVar const& trackParVar0, TTrackParVar const& trackParVar1, TTrackParVar const& trackParVar2, + std::array const& pVecTrack0, std::array const& pVecTrack1, std::array const& pVecTrack2, + const uint32_t isIdentifiedPid0, const uint32_t isIdentifiedPid1, const uint32_t isIdentifiedPid2, + const int64_t globalIndex0, const int64_t globalIndex1, const int64_t globalIndex2) + { + uint32_t isSelected3ProngCand = BIT(NChannels3Prong) - 1; + std::array whichHypo3Prong{}; + whichHypo3Prong.fill(BIT(NMassHypos) - 1); // all mass hypotheses alive + + applyMlRoleSelection(isIdentifiedPid0, isIdentifiedPid1, isIdentifiedPid2, whichHypo3Prong, isSelected3ProngCand); + if (isSelected3ProngCand == 0) { + return; + } + + applyPreselection3Prong(pVecTrack0, pVecTrack1, pVecTrack2, whichHypo3Prong, isSelected3ProngCand); + if (isSelected3ProngCand == 0) { + return; + } + + // reconstruct the 3-prong secondary vertex + const auto tStart3Prong = tickIf(doTiming); + int nVtxFrom3ProngFitter = 0; + try { + nVtxFrom3ProngFitter = df3.process(trackParVar0, trackParVar1, trackParVar2); + } catch (...) { + nVtxFrom3ProngFitter = 0; + } + if (doTiming) { + timing[TimeFit3Prong] += elapsedSeconds(tStart3Prong); + } + if (nVtxFrom3ProngFitter == 0) { + return; + } + + // get secondary vertex + const auto& secondaryVertex3 = df3.getPCACandidate(); + // get track momenta + std::array pvec0{}; + std::array pvec1{}; + std::array pvec2{}; + df3.getTrack(0).getPxPyPzGlo(pvec0); + df3.getTrack(1).getPxPyPzGlo(pvec1); + df3.getTrack(2).getPxPyPzGlo(pvec2); + const auto pVecCandProng3 = RecoDecay::pVec(pvec0, pvec1, pvec2); + + // 3-prong selections after secondary vertex + const std::array pvCoord{collision.posX(), collision.posY(), collision.posZ()}; + applySelection3Prong(pVecCandProng3, secondaryVertex3, pvCoord, isSelected3ProngCand); + if (isSelected3ProngCand == 0) { + return; + } + + // fill table row + rowTrackIndexProng3(collision.globalIndex(), globalIndex0, globalIndex1, globalIndex2, hfFlagOfSelection(isSelected3ProngCand)); + + // fill histograms + if (config.fillHistograms) { + registry.fill(HIST("hVtx3ProngX"), secondaryVertex3[0]); + registry.fill(HIST("hVtx3ProngY"), secondaryVertex3[1]); + registry.fill(HIST("hVtx3ProngZ"), secondaryVertex3[2]); + const std::array arr3Mom{pvec0, pvec1, pvec2}; + for (int iChannel = 0; iChannel < NChannels3Prong; iChannel++) { + if (!TESTBIT(isSelected3ProngCand, iChannel)) { + continue; + } + if (TESTBIT(whichHypo3Prong[iChannel], 0)) { + const auto mass3Prong = RecoDecay::m(arr3Mom, arrMass3Prong[iChannel][0]); + if (iChannel == ChannelDplusToPiKPi) { + registry.fill(HIST("hMassDPlusToPiKPi"), mass3Prong); + } else { + registry.fill(HIST("hMassDsToKKPi"), mass3Prong); + } + } + // the two D+ hypotheses are identical, only the Ds has a second one worth filling + if (iChannel == ChannelDsToKKPi && TESTBIT(whichHypo3Prong[iChannel], 1)) { + registry.fill(HIST("hMassDsToKKPi"), RecoDecay::m(arr3Mom, arrMass3Prong[iChannel][1])); + } + } + } + } + + /// Build the per-collision cache of prongs propagated to the collision's primary vertex. + /// \param collision is the collision under study + /// \param trackIndices are the track associations of this collision + /// \param cacheThis is the prong cache to be filled + template + void fillProngCache(SelectedCollisions::iterator const& collision, + TTrackIndices const& trackIndices, HfProngCache& cacheThis) + { + const auto tStartCache = tickIf(doTiming); + const auto thisCollId = collision.globalIndex(); + for (const auto& trackIndex : trackIndices) { + const auto track = trackIndex.template track_as(); + HfProngCandidate prong{getTrackParCov(track), track.pVector(), trackIndex.isIdentifiedPid(), track.globalIndex()}; + ++counters[CountTracksCached]; + if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it + const auto tStartPropagate = tickIf(doTiming); + ++counters[CountTracksPropagated]; + std::array dcaInfo{track.dcaXY(), track.dcaZ()}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, prong.parCov, 2.f, noMatCorr, &dcaInfo); + getPxPyPz(prong.parCov, prong.pVec); + if (doTiming) { + timing[TimeCachePropagate] += elapsedSeconds(tStartPropagate); + } + } + const int index = static_cast(cacheThis.prongs.size()); + cacheThis.prongs.push_back(prong); + if ((prong.mask & MaskSameSignAny) != 0u) { + cacheThis.sameSign.push_back(index); + } + if ((prong.mask & MaskOppSignAny) != 0u) { + cacheThis.oppSign.push_back(index); + } + } + if (doTiming) { + timing[TimeCacheFill] += elapsedSeconds(tStartCache); + } + } + + /// Run the combinatorial loop over the two same-sign prongs and the opposite-sign prong. + /// \param collision is the collision under study + /// \param sameSignCache holds the prongs of the charge the candidate carries; its sameSign + /// list supplies the two same-sign slots + /// \param oppSignCache holds the opposite charge; its oppSign list supplies the kaon slot + void runCombinatorics(SelectedCollisions::iterator const& collision, + HfProngCache const& sameSignCache, HfProngCache const& oppSignCache) + { + const std::vector& sameSignList = sameSignCache.sameSign; + const std::vector& oppSignList = oppSignCache.oppSign; + constexpr std::size_t NSameSignProngs = NProngs - 1; // two of the three prongs carry the candidate charge + if (sameSignList.size() < NSameSignProngs || oppSignList.empty()) { + return; + } + const auto tStartLoop = tickIf(doTiming); + const std::array pvCoord2Prong{collision.posX(), collision.posY(), collision.posZ()}; + const bool useTwoTrackVertex = config.minTwoTrackDecayLengthFor3Prongs > 0.f || config.maxTwoTrackChi2PcaFor3Prongs < 1.e9f; // o2-linter: disable="magic-number" (default maxTwoTrackChi2PcaFor3Prongs is 1.e10) + + for (const int iOpp : oppSignList) { // o2-linter: disable=const-ref-in-for-loop (int elements) + const auto& prongOpp = oppSignCache.prongs[iOpp]; + + for (std::size_t i1 = 0; i1 + 1 < sameSignList.size(); ++i1) { + const auto& prong1 = sameSignCache.prongs[sameSignList[i1]]; + ++counters[CountPairsSeen]; + if (!isCandidateAllowed(prongOpp.mask, prong1.mask)) { + continue; + } + ++counters[CountPairsCandidateOk]; + + // optional 2-track vertex to skip the pair early + if (useTwoTrackVertex) { + const auto tStart2Prong = tickIf(doTiming); + ++counters[CountFit2Prong]; + int nVtxFrom2ProngFitter = 0; + try { + nVtxFrom2ProngFitter = df2.process(prong1.parCov, prongOpp.parCov); + } catch (...) { + } + const bool pairOk = (nVtxFrom2ProngFitter != 0) && + isTwoTrackVertexSelectedFor3Prongs(df2.getPCACandidate(), pvCoord2Prong, df2); + if (doTiming) { + timing[TimeFit2Prong] += elapsedSeconds(tStart2Prong); + } + if (!pairOk) { + ++counters[CountPairsRejected2P]; + continue; + } + } + + for (std::size_t i2 = i1 + 1; i2 < sameSignList.size(); ++i2) { + const auto& prong2 = sameSignCache.prongs[sameSignList[i2]]; + if (!tripletRoleOk(prongOpp.mask, prong1.mask, prong2.mask)) { + continue; + } + ++counters[CountTripletsEnumerated]; + + processTriplet(collision, prong1.parCov, prongOpp.parCov, prong2.parCov, + prong1.pVec, prongOpp.pVec, prong2.pVec, + prong1.mask, prongOpp.mask, prong2.mask, + prong1.globalIndex, prongOpp.globalIndex, prong2.globalIndex); + } + } + } + if (doTiming) { + timing[TimeLoopTotal] += elapsedSeconds(tStartLoop); + } + } + + void process3Prongs(SelectedCollisions const& collisions, + aod::BCsWithTimestamps const&, + FilteredTrackAssocSel const&, + aod::TracksWCovDca const& /*tracks*/) + { + for (const auto& collision : collisions) { + // set the magnetic field from CCDB + const auto bc = collision.bc_as(); + initCCDB(bc, runNumber, ccdb, config.ccdbPathGrpMag, lut, false); + df2.setBz(o2::base::Propagator::Instance()->getNominalBz()); + df3.setBz(o2::base::Propagator::Instance()->getNominalBz()); + + // used to calculate number of candidates per event + auto nCand3 = rowTrackIndexProng3.lastIndex(); + + timing.fill(0.); + counters.fill(0); + + const auto thisCollId = collision.globalIndex(); + + const auto allPos = positiveFor3Prongs->sliceByCached(aod::track::collisionId, thisCollId, cache); + const auto allNeg = negativeFor3Prongs->sliceByCached(aod::track::collisionId, thisCollId, cache); + const int nTracks = allPos.size() + allNeg.size(); + + // We fill the prong cache for each collision, so that the prongs are propagated to the PV only once. + cachePos.clear(); + cacheNeg.clear(); + fillProngCache(collision, allPos, cachePos); + fillProngCache(collision, allNeg, cacheNeg); + + // D+ -> pi+ K- pi+ / Ds -> K+ K- pi+ and their charge conjugates: the same-sign pair carries + // the charge of the candidate, the opposite-sign prong is the kaon in both channels + runCombinatorics(collision, cachePos, cacheNeg); + runCombinatorics(collision, cacheNeg, cachePos); + + nCand3 = rowTrackIndexProng3.lastIndex() - nCand3; // number of 3-prong candidates in this collision + counters[CountTripletsWritten] = nCand3; + + if (config.fillHistograms) { + // Fill(bin, weight) so the bins accumulate seconds and counts over the whole run + for (int iStep = 0; iStep < NTimingSteps; iStep++) { + registry.fill(HIST("hTiming"), iStep, timing[iStep]); + } + for (int iStep = 0; iStep < NLoopCounters; iStep++) { + registry.fill(HIST("hLoopCounters"), iStep, static_cast(counters[iStep])); + } + registry.fill(HIST("hNTracks"), nTracks); + registry.fill(HIST("hNCand3Prong"), nCand3); + registry.fill(HIST("hNCand3ProngVsNTracks"), nTracks, nCand3); + } + } + } + PROCESS_SWITCH(HfMlBasedTrackSelector, process3Prongs, "Process 3-prong skim", true); + + void processNo3Prongs(SelectedCollisions const&) + { + // dummy + } + PROCESS_SWITCH(HfMlBasedTrackSelector, processNo3Prongs, "Do not process 3-prongs", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec workflow{}; + workflow.push_back(adaptAnalysisTask(cfgc)); + workflow.push_back(adaptAnalysisTask(cfgc)); + workflow.push_back(adaptAnalysisTask(cfgc)); + return workflow; +} diff --git a/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx b/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx new file mode 100644 index 00000000000..e89d601aa3b --- /dev/null +++ b/PWGHF/D2H/TableProducer/treeCreatorDstarSpinAlignMixing.cxx @@ -0,0 +1,236 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file treeCreatorDstarSpinAlignMixing.cxx +/// \brief Writer of D*+ → D0 ( → π+ K-) π+ candidates in the form of flat tables to be stored in TTrees. +/// Intended for Mix-candidate analysis in spin alignment measurement. +/// Serving as a correction for detector acceptance and reconstruction efficiency. +/// +/// \author Mingze li , CCNU/UniTo + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/D2H/Utils/utilsFlow.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Qvectors.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_occupancy; +using namespace o2::analysis::hf_flow_utils; + +namespace o2::aod +{ +namespace mixing_dstar +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +// D0 related variables +DECLARE_SOA_COLUMN(MD0, mD0, float); +// DECLARE_SOA_COLUMN(PtD0, ptD0, float); +// DECLARE_SOA_COLUMN(PD0, pD0, float); +// DECLARE_SOA_COLUMN(EtaD0, etaD0, float); +// DECLARE_SOA_COLUMN(PhiD0, phiD0, float); +// DECLARE_SOA_COLUMN(YD0, yD0, float); +// soft pion related variables +DECLARE_SOA_COLUMN(PtSoftPi, ptSoftPi, float); +// DECLARE_SOA_COLUMN(PSoftPi, pSoftPi, float); +DECLARE_SOA_COLUMN(EtaSoftPi, etaSoftPi, float); +DECLARE_SOA_COLUMN(PhiSoftPi, phiSoftPi, float); +// DECLARE_SOA_COLUMN(YSoftPi, ySoftPi, float); +// Dstar related variables +DECLARE_SOA_COLUMN(M, m, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +// DECLARE_SOA_COLUMN(P, p, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(MlProbDstarToD0PiBkg, mlProbDstarToD0PiBkg, float); +DECLARE_SOA_COLUMN(MlProbDstarToD0PiPrompt, mlProbDstarToD0PiPrompt, float); +DECLARE_SOA_COLUMN(MlProbDstarToD0PiNonPrompt, mlProbDstarToD0PiNonPrompt, float); +// Events +DECLARE_SOA_COLUMN(ZVtx, zVtx, float); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(NPvContrib, nPvContrib, uint16_t); +DECLARE_SOA_COLUMN(Occupancy, occupancy, int); +DECLARE_SOA_COLUMN(XQVec, xQVec, float); +DECLARE_SOA_COLUMN(YQVec, yQVec, float); +DECLARE_SOA_COLUMN(GIndexCol, gIndexCol, int); +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, int64_t); +// Tracks +DECLARE_SOA_COLUMN(MinAbsEtaTrack, minAbsEtaTrack, float); +DECLARE_SOA_COLUMN(MinNumItsCls, minNumItsCls, uint8_t); +DECLARE_SOA_COLUMN(MinNumTpcCls, minNumTpcCls, uint8_t); +} // namespace mixing_dstar + +DECLARE_SOA_TABLE(HfCandDstMix, "AOD", "HFCANDDSTMIX", + mixing_dstar::MD0, + mixing_dstar::PtSoftPi, + mixing_dstar::EtaSoftPi, + mixing_dstar::PhiSoftPi, + mixing_dstar::M, + mixing_dstar::Pt, + mixing_dstar::Eta, + mixing_dstar::Phi, + mixing_dstar::Y, + mixing_dstar::MlProbDstarToD0PiBkg, + mixing_dstar::MlProbDstarToD0PiPrompt, + mixing_dstar::MlProbDstarToD0PiNonPrompt, + mixing_dstar::ZVtx, + mixing_dstar::Centrality, + mixing_dstar::NPvContrib, + mixing_dstar::Occupancy, + mixing_dstar::XQVec, + mixing_dstar::YQVec, + mixing_dstar::MinAbsEtaTrack, + mixing_dstar::MinNumItsCls, + mixing_dstar::MinNumTpcCls, + mixing_dstar::GIndexCol, + mixing_dstar::TimeStamp); +} // namespace o2::aod + +/// Writes the full information in an output TTree +struct HfTreeCreatorDstarSpinAlignMixing { + Produces rowCandidateMix; + + Configurable qVecDetector{"qVecDetector", 2, "Detector for Q vector estimation (FV0A: 0, FT0M: 1, FT0C: 2)"}; + Configurable centEstimator{"centEstimator", 2, "Centrality estimator ((None: 0, FT0C: 2, FT0M: 3))"}; + Configurable occEstimator{"occEstimator", 2, "If enabled, replace number of PV contributors with occupancy estimation (0: don't use, 1: ITS, 2: FT0C)"}; + + using CollsWithQVecs = soa::Join; + using TracksWithExtra = soa::Join; + using CandDstarWSelFlag = soa::Join; + using FilteredCandDstarWSelFlagAndMl = soa::Filtered>; + + Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + + Preslice dstarWithMlPerCollision = aod::hf_cand::collisionId; + + void init(InitContext const&) + { + } + + /// prongTracks is the vector of daughter tracks + /// etaMin is the minimum eta + /// nItsClsMin is the minumum number of clusters in ITS + /// nTpcClsMin is the minumum number of clusters in TPC + template + void getTrackingInfos(std::vector const& prongTracks, float& etaMin, int& nItsClsMin, int& nTpcClsMin) + { + etaMin = 10.f; + nItsClsMin = 10; + nTpcClsMin = 1000; + + for (const auto& track : prongTracks) { + if (std::abs(track.eta()) < etaMin) { + etaMin = std::abs(track.eta()); + } + if (track.itsNCls() < nItsClsMin) { + nItsClsMin = track.itsNCls(); + } + if (track.tpcNClsCrossedRows() < nTpcClsMin) { + nTpcClsMin = track.tpcNClsCrossedRows(); + } + } + } + + template + void fillCandidateTable(CollType const& collision, const T& candidate, Trk const& /*tracks*/, BcType const& /*bcs*/) + { + const auto bc = collision.template bc_as(); + const int64_t timeStamp = bc.timestamp(); + + float massD0{-1.f}; + float massDStar{-1.f}; + float etaSoftPi{-1.f}; + float phiSoftPi{-1.f}; + if (candidate.signSoftPi() > 0) { + massD0 = candidate.invMassD0(); + massDStar = candidate.invMassDstar(); + } else { + massD0 = candidate.invMassD0Bar(); + massDStar = candidate.invMassAntiDstar(); + } + etaSoftPi = RecoDecay::eta(std::array{candidate.pxSoftPi(), candidate.pySoftPi(), candidate.pzSoftPi()}); + phiSoftPi = RecoDecay::phi(std::array{candidate.pxSoftPi(), candidate.pySoftPi(), candidate.pzSoftPi()}); + + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + + auto trackProng0 = candidate.template prong0_as(); + auto trackProng1 = candidate.template prong1_as(); + auto trackProng2 = candidate.template prongPi_as(); + getTrackingInfos(std::vector{trackProng0, trackProng1, trackProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + std::array const Qvector = getQvec(collision, qVecDetector.value); + + rowCandidateMix( + massD0, + candidate.ptSoftPi(), + etaSoftPi, + phiSoftPi, + massDStar, + candidate.pt(), + candidate.eta(), + candidate.phi(), + candidate.y(constants::physics::MassDStar), + candidate.mlProbDstarToD0Pi()[0], + candidate.mlProbDstarToD0Pi()[1], + candidate.mlProbDstarToD0Pi()[2], + collision.posZ(), + getCentralityColl(collision, centEstimator), + collision.numContrib(), + getOccupancyColl(collision, occEstimator), + Qvector[0], + Qvector[1], + absEtaTrackMin, + numItsClsMin, + numTpcClsMin, + collision.globalIndex(), + timeStamp); + } + + void processData(CollsWithQVecs const& collisions, + FilteredCandDstarWSelFlagAndMl const& dstarCandidates, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& bcWithTimeStamps) + { + for (const auto& collision : collisions) { + auto thisCollId = collision.globalIndex(); + auto groupedDstarCandidates = dstarCandidates.sliceBy(dstarWithMlPerCollision, thisCollId); + for (const auto& dstarCandidate : groupedDstarCandidates) { + fillCandidateTable(collision, dstarCandidate, tracks, bcWithTimeStamps); + } + } + } + PROCESS_SWITCH(HfTreeCreatorDstarSpinAlignMixing, processData, "Process data", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/CMakeLists.txt b/PWGHF/D2H/Tasks/CMakeLists.txt index 608145a8e51..a3726135da8 100644 --- a/PWGHF/D2H/Tasks/CMakeLists.txt +++ b/PWGHF/D2H/Tasks/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(task-b0 SOURCES taskB0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -19,8 +20,14 @@ o2physics_add_dpl_workflow(task-b0-reduced PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-b0-to-jpsi-k0-star-reduced + SOURCES taskB0ToJpsiK0StarReduced.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(task-bplus SOURCES taskBplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -46,16 +53,19 @@ o2physics_add_dpl_workflow(task-bs-to-jpsi-phi-reduced o2physics_add_dpl_workflow(task-bs SOURCES taskBs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-cd SOURCES taskCd.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-charm-polarisation SOURCES taskCharmPolarisation.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -71,36 +81,49 @@ o2physics_add_dpl_workflow(task-charm-reso-to-d-trk-reduced o2physics_add_dpl_workflow(task-d0 SOURCES taskD0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-directed-flow-charm-hadrons SOURCES taskDirectedFlowCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-dplus SOURCES taskDplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-ds SOURCES taskDs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-dstar-to-d0-pi SOURCES taskDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-flattenicity-d0-lc + SOURCES taskFlattenicityD0Lc.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::SGCutParHolder O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(task-flow-charm-hadrons SOURCES taskFlowCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lb SOURCES taskLb.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -111,46 +134,73 @@ o2physics_add_dpl_workflow(task-lb-reduced o2physics_add_dpl_workflow(task-lc SOURCES taskLc.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(task-upc-lc + SOURCES taskUpcLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lc-to-k0s-p SOURCES taskLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(task-net-charm-fluctuations + SOURCES taskNetCharmFluctuations.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(task-omegac0-to-omega-pi SOURCES taskOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(task-omegac0-to-omega-pi-qa + SOURCES taskOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-pt-fluc-charm-hadrons SOURCES taskPtFlucCharmHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-sigmac SOURCES taskSigmac.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-sigmac-to-cascade SOURCES taskSigmacToCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic SOURCES taskXic.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic-to-xi-pi-pi SOURCES taskXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-xic0-to-xi-pi SOURCES taskXic0ToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -158,3 +208,8 @@ o2physics_add_dpl_workflow(task-hidden-charm SOURCES taskHiddenCharm.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(task-deuteron-from-lb + SOURCES taskDeuteronFromLb.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGHF/D2H/Tasks/taskB0.cxx b/PWGHF/D2H/Tasks/taskB0.cxx index ca89758c2b9..b55eaedad4d 100644 --- a/PWGHF/D2H/Tasks/taskB0.cxx +++ b/PWGHF/D2H/Tasks/taskB0.cxx @@ -79,7 +79,7 @@ struct HfTaskB0 { void init(InitContext const&) { static const AxisSpec axisMassB0 = {300, 4.5, 6.0, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; registry.add("hMass", "B^{0} candidates;inv. mass D^{#minus}#pi^{#plus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisMassB0, axisPt}}); registry.add("hDecLength", "B^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, axisPt}}); diff --git a/PWGHF/D2H/Tasks/taskB0Reduced.cxx b/PWGHF/D2H/Tasks/taskB0Reduced.cxx index 16634591c5f..9ea6b4696bc 100644 --- a/PWGHF/D2H/Tasks/taskB0Reduced.cxx +++ b/PWGHF/D2H/Tasks/taskB0Reduced.cxx @@ -10,13 +10,16 @@ // or submit itself to any jurisdiction. /// \file taskB0Reduced.cxx -/// \brief B0 → D- π+ → (π- K+ π-) π+ analysis task +/// \brief B0 → D-/D*- π+ → (π- K+ π-) π+ analysis task /// /// \author Alexandre Bigot , IPHC Strasbourg /// \author Fabrizio Grosa , CERN +/// \author Fabrizio Chinu , Universita and INFN Torino +/// \author Marcello Di Costanzo , Polytechnic University of Turin and INFN #include "PWGHF/Core/HfHelper.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" +#include "PWGHF/D2H/Utils/utilsFlow.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" @@ -38,6 +41,7 @@ #include #include +#include #include #include #include @@ -47,6 +51,7 @@ using namespace o2::aod; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::analysis::hf_flow_utils; namespace o2::aod { @@ -90,11 +95,19 @@ DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); DECLARE_SOA_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, float); //! Normalised transverse decay length of candidate DECLARE_SOA_COLUMN(DecayLengthD, decayLengthD, float); //! Decay length of D-meson daughter candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXYD, decayLengthXYD, float); //! Transverse decay length of D-meson daughter candidate (cm) +DECLARE_SOA_COLUMN(DecayLengthDToB, decayLengthDToB, float); //! Decay length of D-meson daughter candidate from B decay vertex (cm) +DECLARE_SOA_COLUMN(DecayLengthXYDToB, decayLengthXYDToB, float); //! Transverse decay length of D-meson daughter candidate from B decay vertex (cm) DECLARE_SOA_COLUMN(ImpactParameterD, impactParameterD, float); //! Impact parameter product of D-meson daughter candidate DECLARE_SOA_COLUMN(ImpactParameterBach, impactParameterBach, float); //! Impact parameter product of bachelor pion DECLARE_SOA_COLUMN(ImpactParameterProduct, impactParameterProduct, float); //! Impact parameter product of daughters DECLARE_SOA_COLUMN(Cpa, cpa, float); //! Cosine pointing angle of candidate DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! Cosine pointing angle of candidate in transverse plane +DECLARE_SOA_COLUMN(CpaD, cpaD, float); //! Cosine pointing angle of D candidate to PV +DECLARE_SOA_COLUMN(CpaDXY, cpaDXY, float); //! Cosine pointing angle of D candidate to PV in transverse plane +DECLARE_SOA_COLUMN(CpaDToB, cpaDToB, float); //! Cosine pointing angle of D candidate to B decay vertex +DECLARE_SOA_COLUMN(CpaDToBXY, cpaDToBXY, float); //! Cosine pointing angle of D candidate to B decay vertex in transverse plane +DECLARE_SOA_COLUMN(ScalarProd, scalarProd, float); //! Scalar product of B candidate +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality of the collision DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs DECLARE_SOA_COLUMN(MlScoreSig, mlScoreSig, float); //! ML score for signal class DECLARE_SOA_COLUMN(FlagWrongCollision, flagWrongCollision, int8_t); //! Flag for association with wrong collision @@ -118,11 +131,19 @@ DECLARE_SOA_TABLE(HfRedCandB0Lites, "AOD", "HFREDCANDB0LITE", //! Table with som hf_cand_b0_lite::MaxNormalisedDeltaIP, hf_cand_b0_lite::MlScoreSig, hf_sel_candidate_b0::IsSelB0ToDPi, + hf_cand_b0_lite::ScalarProd, + hf_cand_b0_lite::Centrality, // D meson features hf_cand_b0_lite::MD, hf_cand_b0_lite::PtD, hf_cand_b0_lite::DecayLengthD, hf_cand_b0_lite::DecayLengthXYD, + hf_cand_b0_lite::DecayLengthDToB, + hf_cand_b0_lite::DecayLengthXYDToB, + hf_cand_b0_lite::CpaD, + hf_cand_b0_lite::CpaDXY, + hf_cand_b0_lite::CpaDToB, + hf_cand_b0_lite::CpaDToBXY, hf_cand_b0_lite::ImpactParameterD, hf_cand_b0_lite::PtDmesProngMin, hf_cand_b0_lite::AbsEtaDmesProngMin, @@ -189,7 +210,12 @@ struct HfTaskB0Reduced { Configurable fillBackground{"fillBackground", false, "Flag to enable filling of background histograms/sparses/tree (only MC)"}; Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; + Configurable centralityEstimator{"centralityEstimator", 1, "Centrality estimator (FT0M: 0, FT0C: 1)"}; + Configurable harmonic{"harmonic", 2, "harmonic number"}; + Configurable qVecDetector{"qVecDetector", 3, "Detector for Q vector estimation (FT0M: 1, FT0A: 2, FT0C: 3, TPC Pos: 4, TPC Neg: 5, TPC Tot: 6)"}; + using CollisionsWithCents = soa::Join; + using CollisionsWithCentsQvecs = soa::Join; using TracksBachPions = soa::Join; using CandsDplus = soa::Join; using CandsDstar = soa::Join; @@ -201,8 +227,8 @@ struct HfTaskB0Reduced { void init(InitContext&) { - std::array processFuncData{doprocessDataDplusPi, doprocessDataDplusPiWithDmesMl, doprocessDataDplusPiWithB0Ml, - doprocessDataDstarPi, doprocessDataDstarPiWithDmesMl}; + std::array processFuncData{doprocessDataDplusPi, doprocessDataDplusPiWithDmesMl, doprocessDataDplusPiWithDmesMlScalarProd, doprocessDataDplusPiWithB0Ml, doprocessDataDplusPiWithB0MlScalarProd, + doprocessDataDstarPi, doprocessDataDstarPiWithDmesMl, doprocessDataDstarPiWithDmesMlScalarProd}; if ((std::accumulate(processFuncData.begin(), processFuncData.end(), 0)) > 1) { LOGP(fatal, "Only one process function for data can be enabled at a time."); } @@ -229,10 +255,9 @@ struct HfTaskB0Reduced { const AxisSpec axisPtPi{100, 0.f, 10.f}; const AxisSpec axisPtSoftPi{100, 0.f, 1.f}; - std::array processFuncDplusPi = {doprocessDataDplusPi, doprocessDataDplusPiWithDmesMl, doprocessDataDplusPiWithB0Ml, - doprocessMcDplusPi, doprocessMcDplusPiWithDecayTypeCheck, doprocessMcDplusPiWithDmesMl, - doprocessMcDplusPiWithDmesMlAndDecayTypeCheck, doprocessMcDplusPiWithB0Ml, - doprocessMcDplusPiWithB0MlAndDecayTypeCheck}; + std::array processFuncDplusPi = {doprocessDataDplusPi, doprocessDataDplusPiWithDmesMl, doprocessDataDplusPiWithDmesMlScalarProd, doprocessDataDplusPiWithB0Ml, doprocessDataDplusPiWithB0MlScalarProd, + doprocessMcDplusPi, doprocessMcDplusPiWithDecayTypeCheck, doprocessMcDplusPiWithDmesMl, doprocessMcDplusPiWithDmesMlAndDecayTypeCheck, + doprocessMcDplusPiWithB0Ml, doprocessMcDplusPiWithB0MlAndDecayTypeCheck}; const AxisSpec axisMass = ((std::accumulate(processFuncDplusPi.begin(), processFuncDplusPi.end(), 0)) > 0) ? axisMassDminus : axisMassDeltaMassDStar; std::string dMesSpecie; if ((std::accumulate(processFuncDplusPi.begin(), processFuncDplusPi.end(), 0)) > 0) { @@ -241,7 +266,8 @@ struct HfTaskB0Reduced { dMesSpecie += "D^{0}#pi^{#minus}"; } - if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl || doprocessDataDplusPiWithB0Ml || doprocessDataDstarPi || doprocessDataDstarPiWithDmesMl) { + if (doprocessDataDplusPi || doprocessDataDplusPiWithDmesMl || doprocessDataDplusPiWithB0Ml || doprocessDataDstarPi || doprocessDataDstarPiWithDmesMl || + doprocessDataDplusPiWithDmesMlScalarProd || doprocessDataDplusPiWithB0MlScalarProd || doprocessDataDstarPiWithDmesMlScalarProd) { if (fillHistograms) { registry.add("hMass", "B^{0} candidates;#it{p}_{T}(B^{0}) (GeV/#it{c});#it{M} (D#pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {axisPtB0, axisMassB0}}); registry.add("hDecLength", "B^{0} candidates;#it{p}_{T}(B^{0}) (GeV/#it{c});B^{0} candidate decay length (cm);entries", {HistType::kTH2F, {axisPtB0, axisDecayLength}}); @@ -271,19 +297,19 @@ struct HfTaskB0Reduced { registry.add("hDcaProng2", "B^{0} candidates;#it{p}_{T}(B^{0}) (GeV/#it{c});prong 2 (#pi^{#plus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {axisPtB0, axisDca}}); } // ML scores of D- daughter - if (doprocessDataDplusPiWithDmesMl || doprocessDataDstarPiWithDmesMl) { + if (doprocessDataDplusPiWithDmesMl || doprocessDataDplusPiWithDmesMlScalarProd || doprocessDataDstarPiWithDmesMl || doprocessDataDstarPiWithDmesMlScalarProd) { registry.add("hMlScoreBkgD", Form("B^{0} candidates;#it{p}_{T}(%s) (GeV/#it{c});prong0, %s ML background score;entries", dMesSpecie.c_str(), dMesSpecie.c_str()), {HistType::kTH2F, {axisPtDminus, axisMlScore}}); registry.add("hMlScorePromptD", Form("B^{0} candidates;#it{p}_{T}(%s) (GeV/#it{c});prong0, %s ML prompt score;entries", dMesSpecie.c_str(), dMesSpecie.c_str()), {HistType::kTH2F, {axisPtDminus, axisMlScore}}); registry.add("hMlScoreNonPromptD", Form("B^{0} candidates;#it{p}_{T}(%s) (GeV/#it{c});prong0, %s ML nonprompt score;entries", dMesSpecie.c_str(), dMesSpecie.c_str()), {HistType::kTH2F, {axisPtDminus, axisMlScore}}); } // ML scores of B0 candidate - if (doprocessDataDplusPiWithB0Ml) { + if (doprocessDataDplusPiWithB0Ml || doprocessDataDplusPiWithB0MlScalarProd) { registry.add("hMlScoreSigB0", "B^{0} candidates;#it{p}_{T}(B^{0}) (GeV/#it{c});prong0, B^{0} ML signal score;entries", {HistType::kTH2F, {axisPtB0, axisMlScore}}); } } if (fillSparses) { - if (!(doprocessDataDplusPiWithDmesMl || doprocessDataDplusPiWithB0Ml || doprocessDataDstarPiWithDmesMl)) { + if (!(doprocessDataDplusPiWithDmesMl || doprocessDataDplusPiWithDmesMlScalarProd || doprocessDataDplusPiWithB0Ml || doprocessDataDplusPiWithB0MlScalarProd || doprocessDataDstarPiWithDmesMl || doprocessDataDstarPiWithDmesMlScalarProd)) { if ((std::accumulate(processFuncDplusPi.begin(), processFuncDplusPi.end(), 0)) > 0) { registry.add("hMassPtCutVars", "B^{0} candidates;#it{M} (D#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{0}) (GeV/#it{c});B^{0} candidate decay length (cm);B^{0} candidate norm. decay length XY (cm);B^{0} candidate impact parameter product (cm);B^{0} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(%s) (GeV/#it{c});%s candidate decay length (cm);%s candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassB0, axisPtB0, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMass, axisPtDminus, axisDecayLength, axisCosp}}); } else { @@ -444,10 +470,13 @@ struct HfTaskB0Reduced { /// \param withDecayTypeCheck is the flag to enable MC with decay type check /// \param withDmesMl is the flag to enable the filling with ML scores for the D- daughter /// \param withB0Ml is the flag to enable the filling with ML scores for the B0 candidate + /// \param withScalarProd is the flag to enable the filling with scalar product + /// \param collision is the collision /// \param candidate is the B0 candidate /// \param candidatesD is the table with D- candidates - template - void fillCandDStarPi(Cand const& candidate, + template + void fillCandDStarPi(Collision const& collision, + Cand const& candidate, SoftPions const& softPions, CandsDmes const&) { @@ -588,11 +617,18 @@ struct HfTaskB0Reduced { float prong0MlScorePrompt = -1.; float prong0MlScoreNonprompt = -1.; float const candidateMlScoreSig = -1; + float scalarProd = -1; if constexpr (WithDmesMl) { prong0MlScoreBkg = candidate.prong0MlScoreBkg(); prong0MlScorePrompt = candidate.prong0MlScorePrompt(); prong0MlScoreNonprompt = candidate.prong0MlScoreNonprompt(); } + if constexpr (WithScalarProd) { + auto qVecs = getQvec(collision, qVecDetector.value); + float xQVec = qVecs[0]; + float yQVec = qVecs[1]; + scalarProd = std::cos(harmonic * candidate.phi()) * xQVec + std::sin(harmonic * candidate.phi()) * yQVec; + } auto prongBachPi = candidate.template prongBachPi_as(); float ptMother = -1.; @@ -618,11 +654,19 @@ struct HfTaskB0Reduced { candidate.maxNormalisedDeltaIP(), candidateMlScoreSig, candidate.isSelB0ToDPi(), + scalarProd, + centralityEstimator == 0 ? collision.centFT0M() : collision.centFT0C(), // D-meson features invMassD, ptD, - decLenD, - decLenXyD, + candidate.decayLengthDToPv(), + candidate.decayLengthDToPvXY(), + candidate.decayLengthDToB(), + candidate.decayLengthDToBXY(), + candidate.cpaDToPv(), + candidate.cpaDToPvXY(), + candidate.cpaDToB(), + candidate.cpaDToBXY(), candidate.impactParameter0(), candD.ptProngMin(), candD.absEtaProngMin(), @@ -682,10 +726,13 @@ struct HfTaskB0Reduced { /// \param withDecayTypeCheck is the flag to enable MC with decay type check /// \param withDmesMl is the flag to enable the filling with ML scores for the D- daughter /// \param withB0Ml is the flag to enable the filling with ML scores for the B0 candidate + /// \param withScalarProd is the flag to enable the filling with scalar product + /// \param collision is the collision /// \param candidate is the B0 candidate /// \param candidatesD is the table with D- candidates - template - void fillCand(Cand const& candidate, + template + void fillCand(Collision const& collision, + Cand const& candidate, CandsDmes const&) { auto ptCandB0 = candidate.pt(); @@ -842,6 +889,7 @@ struct HfTaskB0Reduced { float prong0MlScorePrompt = -1.; float prong0MlScoreNonprompt = -1.; float candidateMlScoreSig = -1; + float scalarProd = -1; if constexpr (WithDmesMl) { prong0MlScoreBkg = candidate.prong0MlScoreBkg(); prong0MlScorePrompt = candidate.prong0MlScorePrompt(); @@ -850,6 +898,12 @@ struct HfTaskB0Reduced { if constexpr (WithB0Ml) { candidateMlScoreSig = candidate.mlProbB0ToDPi(); } + if constexpr (WithScalarProd) { + auto qVecs = getQvec(collision, qVecDetector.value); + float xQVec = qVecs[0]; + float yQVec = qVecs[1]; + scalarProd = std::cos(harmonic * candidate.phi()) * xQVec + std::sin(harmonic * candidate.phi()) * yQVec; + } auto prong1 = candidate.template prong1_as(); float ptMother = -1.; @@ -875,11 +929,19 @@ struct HfTaskB0Reduced { candidate.maxNormalisedDeltaIP(), candidateMlScoreSig, candidate.isSelB0ToDPi(), + scalarProd, + centralityEstimator == 0 ? collision.centFT0M() : collision.centFT0C(), // D-meson features invMassD, ptD, - decLenD, - decLenXyD, + candidate.decayLengthDToPv(), + candidate.decayLengthDToPvXY(), + candidate.decayLengthDToB(), + candidate.decayLengthDToBXY(), + candidate.cpaDToPv(), + candidate.cpaDToPvXY(), + candidate.cpaDToB(), + candidate.cpaDToBXY(), candidate.impactParameter0(), candD.ptProngMin(), candD.absEtaProngMin(), @@ -973,7 +1035,8 @@ struct HfTaskB0Reduced { } // Process functions - void processDataDplusPi(soa::Filtered> const& candidates, + void processDataDplusPi(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsDplus const& candidatesD, TracksBachPions const&) { @@ -981,12 +1044,13 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataDplusPi PROCESS_SWITCH(HfTaskB0Reduced, processDataDplusPi, "Process data without ML scores for B0 and Dplus daughter", true); - void processDataDplusPiWithDmesMl(soa::Filtered> const& candidates, + void processDataDplusPiWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsDplus const& candidatesD, TracksBachPions const&) { @@ -994,12 +1058,27 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataDplusPiWithDmesMl PROCESS_SWITCH(HfTaskB0Reduced, processDataDplusPiWithDmesMl, "Process data with(out) ML scores for Dplus daughter (B0)", false); - void processDataDplusPiWithB0Ml(soa::Filtered> const& candidates, + void processDataDplusPiWithDmesMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsDplus const& candidatesD, + TracksBachPions const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataDplusPiWithDmesMlScalarProd + PROCESS_SWITCH(HfTaskB0Reduced, processDataDplusPiWithDmesMlScalarProd, "Process data with(out) ML scores for Dplus daughter (B0) and scalar products", false); + + void processDataDplusPiWithB0Ml(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsDplus const& candidatesD, TracksBachPions const&) { @@ -1007,13 +1086,27 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataDplusPiWithB0Ml PROCESS_SWITCH(HfTaskB0Reduced, processDataDplusPiWithB0Ml, "Process data with(out) ML scores for B0 (Dplus daughter)", false); - // Process functions - void processDataDstarPi(soa::Filtered> const& candidates, + void processDataDplusPiWithB0MlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsDplus const& candidatesD, + TracksBachPions const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataDplusPiWithB0MlScalarProd + PROCESS_SWITCH(HfTaskB0Reduced, processDataDplusPiWithB0MlScalarProd, "Process data with(out) ML scores for B0 (Dplus daughter) and scalar products", false); + + void processDataDstarPi(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsDstar const& candidatesD, TracksSoftPions const& softPions, TracksBachPions const&) @@ -1022,12 +1115,13 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCandDStarPi(candidate, softPions, candidatesD); + fillCandDStarPi(collisions.rawIteratorAt(candidate.collisionId()), candidate, softPions, candidatesD); } // candidate loop } // processDataDstarPi PROCESS_SWITCH(HfTaskB0Reduced, processDataDstarPi, "Process data without ML scores for B0 and Dstar daughter", false); - void processDataDstarPiWithDmesMl(soa::Filtered> const& candidates, + void processDataDstarPiWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsDstar const& candidatesD, TracksSoftPions const& softPions, TracksBachPions const&) @@ -1036,12 +1130,28 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCandDStarPi(candidate, softPions, candidatesD); + fillCandDStarPi(collisions.rawIteratorAt(candidate.collisionId()), candidate, softPions, candidatesD); } // candidate loop } // processDataDstarPiWithDmesMl PROCESS_SWITCH(HfTaskB0Reduced, processDataDstarPiWithDmesMl, "Process data with(out) ML scores for Dstar daughter (B0)", false); - void processMcDplusPi(soa::Filtered> const& candidates, + void processDataDstarPiWithDmesMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsDstar const& candidatesD, + TracksSoftPions const& softPions, + TracksBachPions const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { + continue; + } + fillCandDStarPi(collisions.rawIteratorAt(candidate.collisionId()), candidate, softPions, candidatesD); + } // candidate loop + } // processDataDstarPiWithDmesMlScalarProd + PROCESS_SWITCH(HfTaskB0Reduced, processDataDstarPiWithDmesMlScalarProd, "Process data with(out) ML scores for Dstar daughter (B0) and scalar products", false); + + void processMcDplusPi(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDplus const& candidatesD, TracksBachPions const&) @@ -1051,7 +1161,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -1061,7 +1171,8 @@ struct HfTaskB0Reduced { } // processMcDplusPi PROCESS_SWITCH(HfTaskB0Reduced, processMcDplusPi, "Process MC without ML scores for B0 and Dplus daughter", false); - void processMcDplusPiWithDecayTypeCheck(soa::Filtered> const& candidates, + void processMcDplusPiWithDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDplus const& candidatesD, TracksBachPions const&) @@ -1071,7 +1182,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -1081,7 +1192,8 @@ struct HfTaskB0Reduced { } // processMcDplusPi PROCESS_SWITCH(HfTaskB0Reduced, processMcDplusPiWithDecayTypeCheck, "Process MC with decay type check and without ML scores for B0 and Dplus daughter", false); - void processMcDplusPiWithDmesMl(soa::Filtered> const& candidates, + void processMcDplusPiWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDplus const& candidatesD, TracksBachPions const&) @@ -1091,7 +1203,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -1101,7 +1213,8 @@ struct HfTaskB0Reduced { } // processMcDplusPiWithDmesMl PROCESS_SWITCH(HfTaskB0Reduced, processMcDplusPiWithDmesMl, "Process MC with(out) ML scores for Dplus daughter (B0)", false); - void processMcDplusPiWithDmesMlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcDplusPiWithDmesMlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDplus const& candidatesD, TracksBachPions const&) @@ -1111,7 +1224,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -1121,7 +1234,8 @@ struct HfTaskB0Reduced { } // processMcDplusPi PROCESS_SWITCH(HfTaskB0Reduced, processMcDplusPiWithDmesMlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B0 (Dplus daughter)", false); - void processMcDplusPiWithB0Ml(soa::Filtered> const& candidates, + void processMcDplusPiWithB0Ml(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDplus const& candidatesD, TracksBachPions const&) @@ -1131,7 +1245,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -1141,7 +1255,8 @@ struct HfTaskB0Reduced { } // processMcDplusPiWithB0Ml PROCESS_SWITCH(HfTaskB0Reduced, processMcDplusPiWithB0Ml, "Process MC with(out) ML scores for B0 (Dplus daughter)", false); - void processMcDplusPiWithB0MlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcDplusPiWithB0MlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDplus const& candidatesD, TracksBachPions const&) @@ -1151,7 +1266,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -1161,7 +1276,8 @@ struct HfTaskB0Reduced { } // processMcDplusPi PROCESS_SWITCH(HfTaskB0Reduced, processMcDplusPiWithB0MlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B0 (Dplus daughter)", false); - void processMcDstarPi(soa::Filtered> const& candidates, + void processMcDstarPi(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDstar const& candidatesD, TracksSoftPions const& softPions, @@ -1172,7 +1288,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCandDStarPi(candidate, softPions, candidatesD); + fillCandDStarPi(collisions.rawIteratorAt(candidate.collisionId()), candidate, softPions, candidatesD); } // rec // MC gen. level @@ -1182,7 +1298,8 @@ struct HfTaskB0Reduced { } // processMcDstarPi PROCESS_SWITCH(HfTaskB0Reduced, processMcDstarPi, "Process MC without ML scores for B0 and Dstar daughter", false); - void processMcDstarPiWithDmesMl(soa::Filtered> const& candidates, + void processMcDstarPiWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedB0s const& mcParticles, CandsDstar const& candidatesD, TracksSoftPions const& softPions, @@ -1193,7 +1310,7 @@ struct HfTaskB0Reduced { if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { continue; } - fillCandDStarPi(candidate, softPions, candidatesD); + fillCandDStarPi(collisions.rawIteratorAt(candidate.collisionId()), candidate, softPions, candidatesD); } // rec // MC gen. level diff --git a/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx new file mode 100644 index 00000000000..9431ca25590 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskB0ToJpsiK0StarReduced.cxx @@ -0,0 +1,764 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskB0ToJpsiK0StarReduced.cxx +/// \brief B0 → Jpsi K*0 → (µ+ µ-) (K+pi-) analysis task +/// +/// \author Fabrizio Chinu , Università degli Studi and INFN Torino +/// \author Fabrizio Grosa , CERN + +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/HfMlResponseB0ToJpsiK0StarReduced.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/D2H/DataModel/ReducedDataModel.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsPid.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TrackSelectorPID.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::analysis; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::pid_tpc_tof_utils; + +namespace o2::aod +{ +namespace hf_cand_b0tojpsik0star_lite +{ +DECLARE_SOA_COLUMN(PtJpsi, ptJpsi, float); //! Transverse momentum of Jpsi daughter candidate (GeV/c) +DECLARE_SOA_COLUMN(PtJpsiDauPos, ptJpsiDauPos, float); //! Transverse momentum of Jpsi positive daughter candidate (GeV/c) +DECLARE_SOA_COLUMN(PtJpsiDauNeg, ptJpsiDauNeg, float); //! Transverse momentum of Jpsi negative daughter candidate (GeV/c) +DECLARE_SOA_COLUMN(PtBach0, ptBach0, float); //! Transverse momentum of bachelor kaon(<- K*0) (GeV/c) +DECLARE_SOA_COLUMN(PtBach1, ptBach1, float); //! Transverse momentum of bachelor kaon(<- K*0) (GeV/c) +DECLARE_SOA_COLUMN(ItsNClsJpsiDauPos, itsNClsJpsiDauPos, int); //! Number of clusters in ITS +DECLARE_SOA_COLUMN(TpcNClsCrossedRowsJpsiDauPos, tpcNClsCrossedRowsJpsiDauPos, int); //! Number of TPC crossed rows +DECLARE_SOA_COLUMN(ItsChi2NClJpsiDauPos, itsChi2NClJpsiDauPos, float); //! ITS chi2 / Number of clusters +DECLARE_SOA_COLUMN(TpcChi2NClJpsiDauPos, tpcChi2NClJpsiDauPos, float); //! TPC chi2 / Number of clusters +DECLARE_SOA_COLUMN(AbsEtaJpsiDauPos, absEtaJpsiDauPos, float); //! |eta| +DECLARE_SOA_COLUMN(ItsNClsJpsiDauNeg, itsNClsJpsiDauNeg, int); //! Number of clusters in ITS +DECLARE_SOA_COLUMN(TpcNClsCrossedRowsJpsiDauNeg, tpcNClsCrossedRowsJpsiDauNeg, int); //! Number of TPC crossed rows +DECLARE_SOA_COLUMN(ItsChi2NClJpsiDauNeg, itsChi2NClJpsiDauNeg, float); //! ITS chi2 / Number of clusters +DECLARE_SOA_COLUMN(TpcChi2NClJpsiDauNeg, tpcChi2NClJpsiDauNeg, float); //! TPC chi2 / Number of clusters +DECLARE_SOA_COLUMN(AbsEtaJpsiDauNeg, absEtaJpsiDauNeg, float); //! |eta| +DECLARE_SOA_COLUMN(ItsNClsLfTrack0, itsNClsLfTrack0, int); //! Number of clusters in ITS +DECLARE_SOA_COLUMN(TpcNClsCrossedRowsLfTrack0, tpcNClsCrossedRowsLfTrack0, int); //! Number of TPC crossed rows +DECLARE_SOA_COLUMN(ItsChi2NClLfTrack0, itsChi2NClLfTrack0, float); //! ITS chi2 / Number of clusters +DECLARE_SOA_COLUMN(TpcChi2NClLfTrack0, tpcChi2NClLfTrack0, float); //! TPC chi2 / Number of clusters +DECLARE_SOA_COLUMN(AbsEtaLfTrack0, absEtaLfTrack0, float); //! |eta| +DECLARE_SOA_COLUMN(ItsNClsLfTrack1, itsNClsLfTrack1, int); //! Number of clusters in ITS +DECLARE_SOA_COLUMN(TpcNClsCrossedRowsLfTrack1, tpcNClsCrossedRowsLfTrack1, int); //! Number of TPC crossed rows +DECLARE_SOA_COLUMN(ItsChi2NClLfTrack1, itsChi2NClLfTrack1, float); //! ITS chi2 / Number of clusters +DECLARE_SOA_COLUMN(TpcChi2NClLfTrack1, tpcChi2NClLfTrack1, float); //! TPC chi2 / Number of clusters +DECLARE_SOA_COLUMN(AbsEtaLfTrack1, absEtaLfTrack1, float); //! |eta| +DECLARE_SOA_COLUMN(MJpsi, mJpsi, float); //! Invariant mass of Jpsi daughter candidates (GeV/c) +DECLARE_SOA_COLUMN(MK0Star, mK0Star, float); //! Invariant mass of K*0 daughter candidates (GeV/c) +DECLARE_SOA_COLUMN(M, m, float); //! Invariant mass of candidate particle (GeV/c2) +DECLARE_SOA_COLUMN(Pt, pt, float); //! Transverse momentum of candidate (GeV/c) +DECLARE_SOA_COLUMN(PtGen, ptGen, float); //! Transverse momentum of candidate (GeV/c) +DECLARE_SOA_COLUMN(P, p, float); //! Momentum of candidate (GeV/c) +DECLARE_SOA_COLUMN(Y, y, float); //! Rapidity of candidate +DECLARE_SOA_COLUMN(Eta, eta, float); //! Pseudorapidity of candidate +DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuth angle of candidate +DECLARE_SOA_COLUMN(E, e, float); //! Energy of candidate (GeV) +DECLARE_SOA_COLUMN(NSigTpcKaBachelor0, nSigTpcKaBachelor0, float); //! TPC Nsigma separation for bachelor 0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaBachelor0, nSigTofKaBachelor0, float); //! TOF Nsigma separation for bachelor 0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaBachelor0, nSigTpcTofKaBachelor0, float); //! Combined TPC and TOF Nsigma separation for bachelor 0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcKaBachelor1, nSigTpcKaBachelor1, float); //! TPC Nsigma separation for bachelor 1 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaBachelor1, nSigTofKaBachelor1, float); //! TOF Nsigma separation for bachelor 1 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaBachelor1, nSigTpcTofKaBachelor1, float); //! Combined TPC and TOF Nsigma separation for bachelor 1 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPiBachelor0, nSigTpcPiBachelor0, float); //! TPC Nsigma separation for bachelor 0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPiBachelor0, nSigTofPiBachelor0, float); //! TOF Nsigma separation for bachelor 0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiBachelor0, nSigTpcTofPiBachelor0, float); //! Combined TPC and TOF Nsigma separation for bachelor 0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPiBachelor1, nSigTpcPiBachelor1, float); //! TPC Nsigma separation for bachelor 1 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPiBachelor1, nSigTofPiBachelor1, float); //! TOF Nsigma separation for bachelor 1 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiBachelor1, nSigTpcTofPiBachelor1, float); //! Combined TPC and TOF Nsigma separation for bachelor 1 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcMuJpsiDauPos, nSigTpcMuJpsiDauPos, float); //! TPC Nsigma separation for Jpsi DauPos with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofMuJpsiDauPos, nSigTofMuJpsiDauPos, float); //! TOF Nsigma separation for Jpsi DauPos with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofMuJpsiDauPos, nSigTpcTofMuJpsiDauPos, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong0 with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcMuJpsiDauNeg, nSigTpcMuJpsiDauNeg, float); //! TPC Nsigma separation for Jpsi DauNeg with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofMuJpsiDauNeg, nSigTofMuJpsiDauNeg, float); //! TOF Nsigma separation for Jpsi DauNeg with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofMuJpsiDauNeg, nSigTpcTofMuJpsiDauNeg, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong1 with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcElJpsiDauPos, nSigTpcElJpsiDauPos, float); //! TPC Nsigma separation for Jpsi DauPos with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTofElJpsiDauPos, nSigTofElJpsiDauPos, float); //! TOF Nsigma separation for Jpsi DauPos with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofElJpsiDauPos, nSigTpcTofElJpsiDauPos, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong0 with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcElJpsiDauNeg, nSigTpcElJpsiDauNeg, float); //! TPC Nsigma separation for Jpsi DauNeg with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTofElJpsiDauNeg, nSigTofElJpsiDauNeg, float); //! TOF Nsigma separation for Jpsi DauNeg with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofElJpsiDauNeg, nSigTpcTofElJpsiDauNeg, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong1 with electron mass hypothesis +DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) +DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) +DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate +DECLARE_SOA_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, float); //! Normalised transverse decay length of candidate +DECLARE_SOA_COLUMN(CtXY, ctXY, float); //! Pseudo-proper decay length of candidate particle +DECLARE_SOA_COLUMN(ImpactParameterProduct, impactParameterProduct, float); //! Impact parameter product of B daughters +DECLARE_SOA_COLUMN(ImpactParameterProductJpsi, impactParameterProductJpsi, float); //! Impact parameter product of Jpsi daughters +DECLARE_SOA_COLUMN(ImpactParameterProductK0Star, impactParameterProductK0Star, float); //! Impact parameter product of K*0 daughters +DECLARE_SOA_COLUMN(ImpactParameterJpsiDauPos, impactParameterJpsiDauPos, float); //! Impact parameter of Jpsi daughter candidate +DECLARE_SOA_COLUMN(ImpactParameterJpsiDauNeg, impactParameterJpsiDauNeg, float); //! Impact parameter of Jpsi daughter candidate +DECLARE_SOA_COLUMN(ImpactParameterLfTrack0, impactParameterLfTrack0, float); //! Impact parameter of K*0 daughter candidate +DECLARE_SOA_COLUMN(ImpactParameterLfTrack1, impactParameterLfTrack1, float); //! Impact parameter of K*0 daughter candidate +DECLARE_SOA_COLUMN(Cpa, cpa, float); //! Cosine pointing angle of candidate +DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! Cosine pointing angle of candidate in transverse plane +DECLARE_SOA_COLUMN(CpaJpsi, cpaJpsi, float); //! Cosine pointing angle of Jpsi daughter candidate +DECLARE_SOA_COLUMN(CpaXYJpsi, cpaXYJpsi, float); //! Cosine pointing angle in transverse plane of Jpsi daughter candidate +DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs +DECLARE_SOA_COLUMN(MlScoreSig, mlScoreSig, float); //! ML score for signal class +DECLARE_SOA_COLUMN(IsSelB0KPi, isSelB0KPi, int8_t); //! Selection-step bitmask passed under the KPi K*0 mass hypothesis (see SelectionStep) +DECLARE_SOA_COLUMN(IsSelB0PiK, isSelB0PiK, int8_t); //! Selection-step bitmask passed under the PiK K*0 mass hypothesis (see SelectionStep) +DECLARE_SOA_COLUMN(FlagWrongCollision, flagWrongCollision, int8_t); //! Flag for association with wrong collision +} // namespace hf_cand_b0tojpsik0star_lite + +DECLARE_SOA_TABLE(HfRedCandB0Lites, "AOD", "HFREDCANDB0LITE", //! Table with some B0 properties + hf_cand_b0tojpsik0star_lite::M, + hf_cand_b0tojpsik0star_lite::Pt, + hf_cand_b0tojpsik0star_lite::Eta, + hf_cand_b0tojpsik0star_lite::Phi, + hf_cand_b0tojpsik0star_lite::Y, + hf_cand_b0tojpsik0star_lite::Cpa, + hf_cand_b0tojpsik0star_lite::CpaXY, + hf_cand_b0tojpsik0star_lite::CpaJpsi, + hf_cand::Chi2PCA, + hf_cand_b0tojpsik0star_lite::DecayLength, + hf_cand_b0tojpsik0star_lite::DecayLengthXY, + hf_cand_b0tojpsik0star_lite::DecayLengthNormalised, + hf_cand_b0tojpsik0star_lite::DecayLengthXYNormalised, + hf_cand_b0tojpsik0star_lite::CtXY, + hf_cand_b0tojpsik0star_lite::ImpactParameterProduct, + hf_cand_b0tojpsik0star_lite::ImpactParameterProductJpsi, + hf_cand_b0tojpsik0star_lite::ImpactParameterProductK0Star, + hf_cand_b0tojpsik0star_lite::MaxNormalisedDeltaIP, + hf_cand_b0tojpsik0star_lite::MlScoreSig, + hf_cand_b0tojpsik0star_lite::IsSelB0KPi, + hf_cand_b0tojpsik0star_lite::IsSelB0PiK, + // Jpsi meson features + hf_cand_b0tojpsik0star_lite::MJpsi, + hf_cand_b0tojpsik0star_lite::PtJpsi, + hf_cand_b0tojpsik0star_lite::MK0Star, + hf_cand_b0tojpsik0star_lite::ImpactParameterJpsiDauPos, + hf_cand_b0tojpsik0star_lite::ImpactParameterJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::ImpactParameterLfTrack0, + hf_cand_b0tojpsik0star_lite::ImpactParameterLfTrack1, + // Jpsi daughter features + hf_cand_b0tojpsik0star_lite::PtJpsiDauPos, + hf_cand_b0tojpsik0star_lite::ItsNClsJpsiDauPos, + hf_cand_b0tojpsik0star_lite::TpcNClsCrossedRowsJpsiDauPos, + hf_cand_b0tojpsik0star_lite::ItsChi2NClJpsiDauPos, + hf_cand_b0tojpsik0star_lite::TpcChi2NClJpsiDauPos, + hf_cand_b0tojpsik0star_lite::AbsEtaJpsiDauPos, + hf_cand_b0tojpsik0star_lite::PtJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::ItsNClsJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::TpcNClsCrossedRowsJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::ItsChi2NClJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::TpcChi2NClJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::AbsEtaJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::NSigTpcMuJpsiDauPos, + hf_cand_b0tojpsik0star_lite::NSigTofMuJpsiDauPos, + hf_cand_b0tojpsik0star_lite::NSigTpcTofMuJpsiDauPos, + hf_cand_b0tojpsik0star_lite::NSigTpcMuJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::NSigTofMuJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::NSigTpcTofMuJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::NSigTpcElJpsiDauPos, + hf_cand_b0tojpsik0star_lite::NSigTofElJpsiDauPos, + hf_cand_b0tojpsik0star_lite::NSigTpcTofElJpsiDauPos, + hf_cand_b0tojpsik0star_lite::NSigTpcElJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::NSigTofElJpsiDauNeg, + hf_cand_b0tojpsik0star_lite::NSigTpcTofElJpsiDauNeg, + // K0* features + hf_cand_b0tojpsik0star_lite::PtBach0, + hf_cand_b0tojpsik0star_lite::ItsNClsLfTrack0, + hf_cand_b0tojpsik0star_lite::TpcNClsCrossedRowsLfTrack0, + hf_cand_b0tojpsik0star_lite::ItsChi2NClLfTrack0, + hf_cand_b0tojpsik0star_lite::TpcChi2NClLfTrack0, + hf_cand_b0tojpsik0star_lite::AbsEtaLfTrack0, + hf_cand_b0tojpsik0star_lite::NSigTpcKaBachelor0, + hf_cand_b0tojpsik0star_lite::NSigTofKaBachelor0, + hf_cand_b0tojpsik0star_lite::NSigTpcTofKaBachelor0, + hf_cand_b0tojpsik0star_lite::NSigTpcPiBachelor0, + hf_cand_b0tojpsik0star_lite::NSigTofPiBachelor0, + hf_cand_b0tojpsik0star_lite::NSigTpcTofPiBachelor0, + hf_cand_b0tojpsik0star_lite::PtBach1, + hf_cand_b0tojpsik0star_lite::ItsNClsLfTrack1, + hf_cand_b0tojpsik0star_lite::TpcNClsCrossedRowsLfTrack1, + hf_cand_b0tojpsik0star_lite::ItsChi2NClLfTrack1, + hf_cand_b0tojpsik0star_lite::TpcChi2NClLfTrack1, + hf_cand_b0tojpsik0star_lite::AbsEtaLfTrack1, + hf_cand_b0tojpsik0star_lite::NSigTpcKaBachelor1, + hf_cand_b0tojpsik0star_lite::NSigTofKaBachelor1, + hf_cand_b0tojpsik0star_lite::NSigTpcTofKaBachelor1, + hf_cand_b0tojpsik0star_lite::NSigTpcPiBachelor1, + hf_cand_b0tojpsik0star_lite::NSigTofPiBachelor1, + hf_cand_b0tojpsik0star_lite::NSigTpcTofPiBachelor1, + // MC truth + hf_cand_mc_flag::FlagMcMatchRec, + hf_cand_mc_flag::FlagMcDecayChanRec, + hf_cand_mc_flag::OriginMcRec, + hf_cand_b0tojpsik0star_lite::FlagWrongCollision, + hf_cand_b0tojpsik0star_lite::PtGen); + +} // namespace o2::aod + +// string definitions, used for histogram axis labels +const TString stringPt = "#it{p}_{T} (GeV/#it{c})"; +const TString stringPtJpsi = "#it{p}_{T}(Jpsi) (GeV/#it{c});"; +const TString b0CandTitle = "B^{0} candidates;"; +const TString entries = "entries"; +const TString b0CandMatch = "B^{0} candidates (matched);"; +const TString b0CandUnmatch = "B^{0} candidates (unmatched);"; +const TString mcParticleMatched = "MC particles (matched);"; + +/// B0 analysis task +struct HfTaskB0ToJpsiK0StarReduced { + Produces hfRedCandB0Lite; + // Produces hfRedB0McCheck; + + Configurable selectionFlagB0{"selectionFlagB0", 1, "Selection Flag for B0"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable etaTrackMax{"etaTrackMax", 0.8, "max. track pseudo-rapidity"}; + Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track transverse momentum"}; + Configurable fillBackground{"fillBackground", false, "Flag to enable filling of background histograms/sparses/tree (only MC)"}; + Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; + Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; + Configurable useJpsiPdgMass{"useJpsiPdgMass", true, "Whether to use J/Psi PDG mass for B+ candidate mass evaluation or to use the invariant mass of the two prongs"}; + Configurable useK0StarPdgMass{"useK0StarPdgMass", true, "Whether to use K*0 PDG mass for B+ candidate mass evaluation or to use the invariant mass of the two prongs"}; + // topological cuts + Configurable> binsPt{"binsPt", std::vector{hf_cuts_b0_to_jpsi_k0star::vecBinsPt}, "pT bin limits"}; + Configurable> cuts{"cuts", {hf_cuts_b0_to_jpsi_k0star::Cuts[0], hf_cuts_b0_to_jpsi_k0star::NBinsPt, hf_cuts_b0_to_jpsi_k0star::NCutVars, hf_cuts_b0_to_jpsi_k0star::labelsPt, hf_cuts_b0_to_jpsi_k0star::labelsCutVar}, "B0 candidate selection per pT bin"}; + // Enable PID + Configurable kaonPidMethod{"kaonPidMethod", PidMethod::TpcOrTof, "PID selection method for the bachelor kaon (PidMethod::NoPid: none, PidMethod::TpcOrTof: TPC or TOF, PidMethod::TpcAndTof: TPC and TOF)"}; + Configurable pionPidMethod{"pionPidMethod", PidMethod::TpcOrTof, "PID selection method for the bachelor pion (PidMethod::NoPid: none, PidMethod::TpcOrTof: TPC or TOF, PidMethod::TpcAndTof: TPC and TOF)"}; + Configurable acceptPIDNotApplicable{"acceptPIDNotApplicable", true, "Switch to accept Status::NotApplicable [(NotApplicable for one detector) and (NotApplicable or Conditional for the other)] in PID selection"}; + // TPC PID + Configurable ptPidTpcMin{"ptPidTpcMin", 0.15, "Lower bound of track pT for TPC PID"}; + Configurable ptPidTpcMax{"ptPidTpcMax", 20., "Upper bound of track pT for TPC PID"}; + Configurable nSigmaTpcMax{"nSigmaTpcMax", 5., "Nsigma cut on TPC only"}; + Configurable nSigmaTpcCombinedMax{"nSigmaTpcCombinedMax", 5., "Nsigma cut on TPC combined with TOF"}; + // TOF PID + Configurable ptPidTofMin{"ptPidTofMin", 0.15, "Lower bound of track pT for TOF PID"}; + Configurable ptPidTofMax{"ptPidTofMax", 20., "Upper bound of track pT for TOF PID"}; + Configurable nSigmaTofMax{"nSigmaTofMax", 5., "Nsigma cut on TOF only"}; + Configurable nSigmaTofCombinedMax{"nSigmaTofCombinedMax", 5., "Nsigma cut on TOF combined with TPC"}; + // B0 ML inference + Configurable> binsPtB0Ml{"binsPtB0Ml", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirB0Ml{"cutDirB0Ml", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsB0Ml{"cutsB0Ml", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesB0Ml{"nClassesB0Ml", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + // CCDB configuration + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"path_ccdb/BDT_BS/"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_BSToJpsiPhi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + + TrackSelectorKa selectorKaon; + TrackSelectorPi selectorPion; + o2::analysis::HfMlResponseB0ToJpsiK0StarReduced hfMlResponse; + o2::ccdb::CcdbApi ccdbApi; + + using TracksKaon = soa::Join; + using JPsis = soa::Join; + std::vector outputMl; + + // Filter filterSelectCandidates = (aod::hf_sel_candidate_bplus::isSelBsToJpsiPi >= selectionFlagBs); + + HistogramRegistry registry{"registry"}; + + void init(InitContext&) + { + std::array processFuncData{doprocessData, doprocessDataWithB0Ml}; + if ((std::accumulate(processFuncData.begin(), processFuncData.end(), 0)) > 1) { + LOGP(fatal, "Only one process function for data can be enabled at a time."); + } + std::array processFuncMc{doprocessMc, doprocessMcWithB0Ml}; + if ((std::accumulate(processFuncMc.begin(), processFuncMc.end(), 0)) > 1) { + LOGP(fatal, "Only one process function for MC can be enabled at a time."); + } + + if (kaonPidMethod < 0 || kaonPidMethod >= PidMethod::NPidMethods || pionPidMethod < 0 || pionPidMethod >= PidMethod::NPidMethods) { + LOGP(fatal, "Invalid PID option in configurable, please set 0 (no PID), 1 (TPC or TOF), or 2 (TPC and TOF)"); + } + + if (kaonPidMethod != PidMethod::NoPid) { + selectorKaon.setRangePtTpc(ptPidTpcMin, ptPidTpcMax); + selectorKaon.setRangeNSigmaTpc(-nSigmaTpcMax, nSigmaTpcMax); + selectorKaon.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedMax, nSigmaTpcCombinedMax); + selectorKaon.setRangePtTof(ptPidTofMin, ptPidTofMax); + selectorKaon.setRangeNSigmaTof(-nSigmaTofMax, nSigmaTofMax); + selectorKaon.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedMax, nSigmaTofCombinedMax); + } + + if (pionPidMethod != PidMethod::NoPid) { + selectorPion.setRangePtTpc(ptPidTpcMin, ptPidTpcMax); + selectorPion.setRangeNSigmaTpc(-nSigmaTpcMax, nSigmaTpcMax); + selectorPion.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedMax, nSigmaTpcCombinedMax); + selectorPion.setRangePtTof(ptPidTofMin, ptPidTofMax); + selectorPion.setRangeNSigmaTof(-nSigmaTofMax, nSigmaTofMax); + selectorPion.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedMax, nSigmaTofCombinedMax); + } + + const AxisSpec axisMassB0{150, 4.5, 6.0}; + const AxisSpec axisMassJpsi{600, 2.8f, 3.4f}; + const AxisSpec axisMassK0Star{200, 0.9f, 1.1f}; + const AxisSpec axisPtProng{100, 0., 10.}; + const AxisSpec axisImpactPar{200, -0.05, 0.05}; + const AxisSpec axisPtJpsi{100, 0., 50.}; + const AxisSpec axisRapidity{100, -2., 2.}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{0}} (GeV/#it{c})"}; + const AxisSpec axisPtK0Star{100, 0.f, 10.f}; + + registry.add("hMass", b0CandTitle + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); + registry.add("hMassJpsi", b0CandTitle + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); + registry.add("hMassK0Star", b0CandTitle + "inv. mass K^{+}#pi^{#minus} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassK0Star, axisPtK0Star}}); + + // histograms processMC + if (doprocessMc || doprocessMcWithB0Ml) { + registry.add("hPtJpsiGen", mcParticleMatched + "J/#Psi #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); + registry.add("hPtK0StarGen", mcParticleMatched + "K*^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} " + stringPt, {HistType::kTH2F, {axisPtK0Star, axisPtB}}); + registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B^{0} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); + registry.add("hMassRecSig", b0CandMatch + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2}); B^{0} " + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); + registry.add("hMassJpsiRecSig", b0CandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); + registry.add("hMassK0StarRecSig", b0CandMatch + "inv. mass K^{+}#pi^{#minus} (GeV/#it{c}^{2}); K*^{0} " + stringPt, {HistType::kTH2F, {axisMassK0Star, axisPtK0Star}}); + registry.add("hMassRecBg", b0CandUnmatch + "inv. mass J/#Psi K*^{0} (GeV/#it{c}^{2}); B^{0} " + stringPt, {HistType::kTH2F, {axisMassB0, axisPtB}}); + registry.add("hMassJpsiRecBg", b0CandUnmatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); + registry.add("hMassK0StarRecBg", b0CandUnmatch + "inv. mass K^{+}#pi^{#minus} (GeV/#it{c}^{2}); K*^{0} " + stringPt, {HistType::kTH2F, {axisMassK0Star, axisPtK0Star}}); + } + + if (doprocessDataWithB0Ml || doprocessMcWithB0Ml) { + hfMlResponse.configure(binsPtB0Ml, cutsB0Ml, cutDirB0Ml, nClassesB0Ml); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponse.setModelPathsLocal(onnxFileNames); + } + hfMlResponse.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponse.init(); + } + } + + /// Selection of B0 daughter in geometrical acceptance + /// \param etaProng is the pseudorapidity of B0 prong + /// \param ptProng is the pT of B0 prong + /// \return true if prong is in geometrical acceptance + template + bool isProngInAcceptance(const T& etaProng, const T& ptProng) + { + return std::abs(etaProng) <= etaTrackMax && ptProng >= ptTrackMin; + } + + /// Calculate pseudorapidity from track tan(lambda) + /// \param tgl is the track tangent of the dip angle + /// \return pseudorapidity + float absEta(float tgl) + { + return std::abs(std::log(std::tan(o2::constants::math::PIQuarter - 0.5f * std::atan(tgl)))); + } + + /// Fill candidate information at reconstruction level + /// \param doMc is the flag to enable the filling with MC information + /// \param withB0Ml is the flag to enable the filling with ML scores for the B0 candidate + /// \param candidate is the B0 candidate + /// \param candidatesJpsi is the table with Jpsi candidates + template + void fillCand(Cand const& candidate, + JPsis const& /*candidatesJpsi*/, + aod::HfRedBach0Tracks const&, + aod::HfRedBach1Tracks const&) + { + auto ptCandB0 = candidate.pt(); + auto invMassB0KPi = HfHelper::invMassB0ToJpsiK0Star(candidate, useJpsiPdgMass, useK0StarPdgMass, true); + auto invMassB0PiK = HfHelper::invMassB0ToJpsiK0Star(candidate, useJpsiPdgMass, useK0StarPdgMass, false); + auto candJpsi = candidate.template jpsi_as(); + auto candLfDau0 = candidate.template prong0K0Star_as(); + auto candLfDau1 = candidate.template prong1K0Star_as(); + auto const pVecMu0 = candidate.pVectorProng0(); + auto const pVecMu1 = candidate.pVectorProng1(); + auto const pVecLfDau0 = candidate.pVectorProng2(); + auto const pVecLfDau1 = candidate.pVectorProng3(); + auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); + auto ptMuPos = RecoDecay::pt(pVecMu0); + auto ptMuNeg = RecoDecay::pt(pVecMu1); + auto ptK0Star = RecoDecay::pt(pVecLfDau0, pVecLfDau1); + auto invMassJpsi = RecoDecay::m(std::array{pVecMu0, pVecMu1}, std::array{o2::constants::physics::MassMuonPlus, o2::constants::physics::MassMuonMinus}); + auto invMassK0StarKPi = RecoDecay::m(std::array{pVecLfDau0, pVecLfDau1}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + auto invMassK0StarPiK = RecoDecay::m(std::array{pVecLfDau0, pVecLfDau1}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + + int8_t flagMcMatchRec{0}, flagMcDecayChanRec{0}, flagWrongCollision{0}; + bool isSignal = false; + if constexpr (DoMc) { + flagMcMatchRec = candidate.flagMcMatchRec(); + flagMcDecayChanRec = candidate.flagMcDecayChanRec(); + flagWrongCollision = candidate.flagWrongCollision(); + isSignal = std::abs(flagMcMatchRec) == o2::hf_decay::hf_cand_beauty::B0ToJpsiPiKToMuMuPiK && + flagMcDecayChanRec == o2::hf_decay::hf_cand_beauty::B0ToJpsiKstar0; + } + + int8_t statusB0KPi{0}; + int8_t statusB0PiK{0}; + SETBIT(statusB0KPi, SelectionStep::RecoSkims); + SETBIT(statusB0PiK, SelectionStep::RecoSkims); + + // topological selection for the two K*0 mass hypotheses + bool selKPi = HfHelper::selectionB0ToJpsiK0StarTopol(candidate, cuts, binsPt, useJpsiPdgMass, useK0StarPdgMass, true); + bool selPiK = HfHelper::selectionB0ToJpsiK0StarTopol(candidate, cuts, binsPt, useJpsiPdgMass, useK0StarPdgMass, false); + + if (!selKPi && !selPiK && selectionFlagB0 >= BIT(SelectionStep::RecoTopol) * 2 - 1) { + return; + } + if (selKPi) { + SETBIT(statusB0KPi, SelectionStep::RecoTopol); + } + if (selPiK) { + SETBIT(statusB0PiK, SelectionStep::RecoTopol); + } + + // track-level PID selection + // KPi: LfTrack0 as K and LfTrack1 as π ; PiK: LfTrack0 as π and LfTrack1 as K + if (kaonPidMethod != PidMethod::NoPid || pionPidMethod != PidMethod::NoPid) { + int pidKa0{TrackSelectorPID::Status::NotApplicable}; + int pidKa1{TrackSelectorPID::Status::NotApplicable}; + int pidPi0{TrackSelectorPID::Status::NotApplicable}; + int pidPi1{TrackSelectorPID::Status::NotApplicable}; + if (kaonPidMethod == PidMethod::TpcOrTof) { + pidKa0 = selectorKaon.statusTpcOrTof(candLfDau0); + pidKa1 = selectorKaon.statusTpcOrTof(candLfDau1); + } else if (kaonPidMethod == PidMethod::TpcAndTof) { + pidKa0 = selectorKaon.statusTpcAndTof(candLfDau0); + pidKa1 = selectorKaon.statusTpcAndTof(candLfDau1); + } + if (pionPidMethod == PidMethod::TpcOrTof) { + pidPi0 = selectorPion.statusTpcOrTof(candLfDau0); + pidPi1 = selectorPion.statusTpcOrTof(candLfDau1); + } else if (pionPidMethod == PidMethod::TpcAndTof) { + pidPi0 = selectorPion.statusTpcAndTof(candLfDau0); + pidPi1 = selectorPion.statusTpcAndTof(candLfDau1); + } + bool const pidKPi = HfHelper::selectionB0ToJpsiK0StarPid(pidKa0, acceptPIDNotApplicable.value) && + HfHelper::selectionB0ToJpsiK0StarPid(pidPi1, acceptPIDNotApplicable.value); + bool const pidPiK = HfHelper::selectionB0ToJpsiK0StarPid(pidPi0, acceptPIDNotApplicable.value) && + HfHelper::selectionB0ToJpsiK0StarPid(pidKa1, acceptPIDNotApplicable.value); + selKPi = selKPi && pidKPi; + selPiK = selPiK && pidPiK; + if (!selKPi && !selPiK && selectionFlagB0 >= BIT(SelectionStep::RecoPID) * 2 - 1) { + return; + } + if (selKPi) { + SETBIT(statusB0KPi, SelectionStep::RecoPID); + } + if (selPiK) { + SETBIT(statusB0PiK, SelectionStep::RecoPID); + } + } + + // ML selection, evaluated for each hypothesis that survived topological + PID selection + float mlScoreSigKPi = -1; + float mlScoreSigPiK = -1; + if constexpr (WithB0Ml) { + if (selKPi) { + std::vector inputFeaturesKPi = hfMlResponse.getInputFeatures(candidate, candLfDau0, candLfDau1, true); + outputMl.clear(); + selKPi = hfMlResponse.isSelectedMl(inputFeaturesKPi, ptCandB0, outputMl); + if (selKPi) { + mlScoreSigKPi = outputMl[1]; + SETBIT(statusB0KPi, SelectionStep::RecoMl); + } + } + if (selPiK) { + std::vector inputFeaturesPiK = hfMlResponse.getInputFeatures(candidate, candLfDau0, candLfDau1, false); + outputMl.clear(); + selPiK = hfMlResponse.isSelectedMl(inputFeaturesPiK, ptCandB0, outputMl); + if (selPiK) { + mlScoreSigPiK = outputMl[1]; + SETBIT(statusB0PiK, SelectionStep::RecoMl); + } + } + if (!selKPi && !selPiK && selectionFlagB0 >= BIT(SelectionStep::RecoMl) * 2 - 1) { + return; + } + } + + registry.fill(HIST("hMassJpsi"), invMassJpsi, ptJpsi); + if constexpr (DoMc) { + if (isSignal) { + registry.fill(HIST("hMassJpsiRecSig"), invMassJpsi, ptJpsi); + } else if (fillBackground) { + registry.fill(HIST("hMassJpsiRecBg"), invMassJpsi, ptJpsi); + } + } + if (selKPi) { + registry.fill(HIST("hMass"), invMassB0KPi, ptCandB0); + registry.fill(HIST("hMassK0Star"), invMassK0StarKPi, ptK0Star); + } + if (selPiK) { + registry.fill(HIST("hMass"), invMassB0PiK, ptCandB0); + registry.fill(HIST("hMassK0Star"), invMassK0StarPiK, ptK0Star); + } + if constexpr (DoMc) { + if (isSignal) { + if (selKPi) { + registry.fill(HIST("hMassRecSig"), invMassB0KPi, ptCandB0); + registry.fill(HIST("hMassK0StarRecSig"), invMassK0StarKPi, ptK0Star); + } + if (selPiK) { + registry.fill(HIST("hMassRecSig"), invMassB0PiK, ptCandB0); + registry.fill(HIST("hMassK0StarRecSig"), invMassK0StarPiK, ptK0Star); + } + } else if (fillBackground) { + if (selKPi) { + registry.fill(HIST("hMassRecBg"), invMassB0KPi, ptCandB0); + registry.fill(HIST("hMassK0StarRecBg"), invMassK0StarKPi, ptK0Star); + } + if (selPiK) { + registry.fill(HIST("hMassRecBg"), invMassB0PiK, ptCandB0); + registry.fill(HIST("hMassK0StarRecBg"), invMassK0StarPiK, ptK0Star); + } + } + } + + // downsampling for ML trainings + float const pseudoRndm = ptJpsi * 1000. - static_cast(ptJpsi * 1000); + if (ptCandB0 < ptMaxForDownSample && pseudoRndm >= downSampleBkgFactor) { + return; + } + + float ptMother = -1.; + if constexpr (DoMc) { + ptMother = candidate.ptMother(); + } + + auto fillTable = [&](bool isSelKPi) { + auto ctXY = candidate.ctXY(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassKPlus, o2::constants::physics::MassPiPlus}); + auto ctXYBar = candidate.ctXY(std::array{o2::constants::physics::MassMuon, o2::constants::physics::MassMuon, o2::constants::physics::MassPiPlus, o2::constants::physics::MassKPlus}); + hfRedCandB0Lite( + // B0 - meson features + isSelKPi ? invMassB0KPi : invMassB0PiK, + ptCandB0, + candidate.eta(), + candidate.phi(), + HfHelper::yB0(candidate), + candidate.cpa(), + candidate.cpaXY(), + candidate.cpaJpsi(), + candidate.chi2PCA(), + candidate.decayLength(), + candidate.decayLengthXY(), + candidate.decayLengthNormalised(), + candidate.decayLengthXYNormalised(), + isSelKPi ? ctXY : ctXYBar, + candidate.impactParameterProduct(), + candidate.impactParameterProductJpsi(), + candidate.impactParameterProductK0Star(), + candidate.maxNormalisedDeltaIP(), + isSelKPi ? mlScoreSigKPi : mlScoreSigPiK, + isSelKPi ? statusB0KPi : -1, + isSelKPi ? -1 : statusB0PiK, + // J/Psi features + invMassJpsi, + ptJpsi, + isSelKPi ? invMassK0StarKPi : invMassK0StarPiK, + candidate.impactParameter0(), + candidate.impactParameter1(), + candidate.impactParameter2(), + candidate.impactParameter3(), + ptMuPos, + candJpsi.itsNClsDauPos(), + candJpsi.tpcNClsCrossedRowsDauPos(), + candJpsi.itsChi2NClDauPos(), + candJpsi.tpcChi2NClDauPos(), + absEta(candJpsi.tglDauPos()), + ptMuNeg, + candJpsi.itsNClsDauNeg(), + candJpsi.tpcNClsCrossedRowsDauNeg(), + candJpsi.itsChi2NClDauNeg(), + candJpsi.tpcChi2NClDauNeg(), + absEta(candJpsi.tglDauNeg()), + candJpsi.tpcNSigmaMuPos(), + candJpsi.tofNSigmaMuPos(), + candJpsi.tpcTofNSigmaMuPos(), + candJpsi.tpcNSigmaMuNeg(), + candJpsi.tofNSigmaMuNeg(), + candJpsi.tpcTofNSigmaMuNeg(), + candJpsi.tpcNSigmaElPos(), + candJpsi.tofNSigmaElPos(), + candJpsi.tpcTofNSigmaElPos(), + candJpsi.tpcNSigmaElNeg(), + candJpsi.tofNSigmaElNeg(), + candJpsi.tpcTofNSigmaElNeg(), + // K*0 daughter features + candLfDau0.pt(), + candLfDau0.itsNCls(), + candLfDau0.tpcNClsCrossedRows(), + candLfDau0.itsChi2NCl(), + candLfDau0.tpcChi2NCl(), + absEta(candLfDau0.tgl()), + candLfDau0.tpcNSigmaKa(), + candLfDau0.tofNSigmaKa(), + candLfDau0.tpcTofNSigmaKa(), + candLfDau0.tpcNSigmaPi(), + candLfDau0.tofNSigmaPi(), + candLfDau0.tpcTofNSigmaPi(), + candLfDau1.pt(), + candLfDau1.itsNCls(), + candLfDau1.tpcNClsCrossedRows(), + candLfDau1.itsChi2NCl(), + candLfDau1.tpcChi2NCl(), + absEta(candLfDau1.tgl()), + candLfDau1.tpcNSigmaKa(), + candLfDau1.tofNSigmaKa(), + candLfDau1.tpcTofNSigmaKa(), + candLfDau1.tpcNSigmaPi(), + candLfDau1.tofNSigmaPi(), + candLfDau1.tpcTofNSigmaPi(), + // MC truth + flagMcMatchRec, + flagMcDecayChanRec, + isSignal, + flagWrongCollision, + ptMother); + }; + + if (statusB0KPi >= selectionFlagB0) { + fillTable(true); + } + if (statusB0PiK >= selectionFlagB0) { + fillTable(false); + } + } + + /// Fill particle histograms (gen MC truth) + void fillCandMcGen(aod::HfMcGenRedB0s::iterator const& particle) + { + auto ptParticle = particle.ptTrack(); + auto yParticle = particle.yTrack(); + if (yCandGenMax >= 0. && std::abs(yParticle) > yCandGenMax) { + return; + } + std::array ptProngs = {particle.ptProng0(), particle.ptProng1()}; + std::array etaProngs = {particle.etaProng0(), particle.etaProng1()}; + bool const prongsInAcc = isProngInAcceptance(etaProngs[0], ptProngs[0]) && isProngInAcceptance(etaProngs[1], ptProngs[1]); + + registry.fill(HIST("hPtJpsiGen"), ptProngs[0], ptParticle); + registry.fill(HIST("hPtK0StarGen"), ptProngs[1], ptParticle); + + // generated B0 with daughters in geometrical acceptance + if (prongsInAcc) { + registry.fill(HIST("hYGenWithProngsInAcceptance"), ptParticle, yParticle); + } + } + + // Process functions + void processData(aod::HfRedCandB0ToJpsiK0Star const& candidates, + JPsis const& candidatesJpsi, + aod::HfRedBach0Tracks const& kaon0Tracks, + aod::HfRedBach1Tracks const& kaon1Tracks) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { + continue; + } + fillCand(candidate, candidatesJpsi, kaon0Tracks, kaon1Tracks); + } // candidate loop + } // processData + PROCESS_SWITCH(HfTaskB0ToJpsiK0StarReduced, processData, "Process data without ML for B0", true); + + void processDataWithB0Ml(aod::HfRedCandB0ToJpsiK0Star const& candidates, + JPsis const& candidatesJpsi, + aod::HfRedBach0Tracks const& kaon0Tracks, + aod::HfRedBach1Tracks const& kaon1Tracks) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { + continue; + } + fillCand(candidate, candidatesJpsi, kaon0Tracks, kaon1Tracks); + } // candidate loop + } // processDataWithB0Ml + PROCESS_SWITCH(HfTaskB0ToJpsiK0StarReduced, processDataWithB0Ml, "Process data with ML for B0", false); + + void processMc(soa::Join const& candidates, + aod::HfMcGenRedB0s const& mcParticles, + JPsis const& candidatesJpsi, + aod::HfRedBach0Tracks const& kaon0Tracks, + aod::HfRedBach1Tracks const& kaon1Tracks) + { + // MC rec + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { + continue; + } + fillCand(candidate, candidatesJpsi, kaon0Tracks, kaon1Tracks); + } // rec + + // MC gen. level + for (const auto& particle : mcParticles) { + fillCandMcGen(particle); + } // gen + } // processMc + PROCESS_SWITCH(HfTaskB0ToJpsiK0StarReduced, processMc, "Process MC without ML for B0", false); + + void processMcWithB0Ml(soa::Join const& candidates, + aod::HfMcGenRedB0s const& mcParticles, + JPsis const& candidatesJpsi, + aod::HfRedBach0Tracks const& kaon0Tracks, + aod::HfRedBach1Tracks const& kaon1Tracks) + { + // MC rec + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yB0(candidate)) > yCandRecoMax) { + continue; + } + fillCand(candidate, candidatesJpsi, kaon0Tracks, kaon1Tracks); + } // rec + + // MC gen. level + for (const auto& particle : mcParticles) { + fillCandMcGen(particle); + } // gen + } // processMcWithB0Ml + PROCESS_SWITCH(HfTaskB0ToJpsiK0StarReduced, processMcWithB0Ml, "Process MC with ML for B0", false); + +}; // struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/taskBplus.cxx b/PWGHF/D2H/Tasks/taskBplus.cxx index f3223f55eba..98d12dc90f7 100644 --- a/PWGHF/D2H/Tasks/taskBplus.cxx +++ b/PWGHF/D2H/Tasks/taskBplus.cxx @@ -98,7 +98,7 @@ struct HfTaskBplus { const AxisSpec axisNormDecLength{40, 0., 20}; const AxisSpec axisEta{100, -2., 2.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; registry.add("hMass", bPlusCandTitle + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMass, axisPtB}}); registry.add("hDecLength", bPlusCandTitle + "decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); @@ -127,8 +127,8 @@ struct HfTaskBplus { registry.add("hEtaProng1Gen", mcParticleMatched + "prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); registry.add("hPtGenWithRapidityBelowHalf", "MC particles (generated - |#it{y}^{gen}|<0.5);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{300, 0., 30.}}}); registry.add("hPtGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // histograms MC: Reco Level registry.add("hCPARecSig", bPlusCandMatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCPA, axisPtB}}); registry.add("hCPARecBg", bPlusCandUnmatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCPA, axisPtB}}); diff --git a/PWGHF/D2H/Tasks/taskBplusReduced.cxx b/PWGHF/D2H/Tasks/taskBplusReduced.cxx index 5913760dcea..190918846e4 100644 --- a/PWGHF/D2H/Tasks/taskBplusReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusReduced.cxx @@ -15,8 +15,8 @@ /// \author Antonio Palasciano , Università degli Studi di Bari & INFN, Sezione di Bari #include "PWGHF/Core/HfHelper.h" -#include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/D2H/DataModel/ReducedDataModel.h" +#include "PWGHF/D2H/Utils/utilsFlow.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" @@ -38,15 +38,16 @@ #include #include +#include #include #include -#include using namespace o2; using namespace o2::aod; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::analysis::hf_flow_utils; namespace o2::aod { @@ -87,19 +88,26 @@ DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); DECLARE_SOA_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, float); //! Normalised transverse decay length of candidate DECLARE_SOA_COLUMN(DecayLengthD, decayLengthD, float); //! Decay length of D-meson daughter candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXYD, decayLengthXYD, float); //! Transverse decay length of D-meson daughter candidate (cm) +DECLARE_SOA_COLUMN(DecayLengthDToB, decayLengthDToB, float); //! Decay length of D-meson daughter candidate from B decay vertex (cm) +DECLARE_SOA_COLUMN(DecayLengthXYDToB, decayLengthXYDToB, float); //! Transverse decay length of D-meson daughter candidate from B decay vertex (cm) DECLARE_SOA_COLUMN(ImpactParameterD, impactParameterD, float); //! Impact parameter product of D-meson daughter candidate DECLARE_SOA_COLUMN(ImpactParameterBach, impactParameterBach, float); //! Impact parameter product of bachelor pion DECLARE_SOA_COLUMN(ImpactParameterProduct, impactParameterProduct, float); //! Impact parameter product of daughters DECLARE_SOA_COLUMN(Cpa, cpa, float); //! Cosine pointing angle of candidate DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! Cosine pointing angle of candidate in transverse plane -DECLARE_SOA_COLUMN(CpaD, cpaD, float); //! Cosine pointing angle of D-meson daughter candidate -DECLARE_SOA_COLUMN(CpaXYD, cpaXYD, float); //! Cosine pointing angle in transverse plane of D-meson daughter candidate +DECLARE_SOA_COLUMN(CpaD, cpaD, float); //! Cosine pointing angle of D candidate to PV +DECLARE_SOA_COLUMN(CpaDXY, cpaDXY, float); //! Cosine pointing angle of D candidate to PV in transverse plane +DECLARE_SOA_COLUMN(CpaDToB, cpaDToB, float); //! Cosine pointing angle of D candidate to B decay vertex +DECLARE_SOA_COLUMN(CpaDToBXY, cpaDToBXY, float); //! Cosine pointing angle of D candidate to B decay vertex in transverse plane +DECLARE_SOA_COLUMN(ScalarProd, scalarProd, float); //! Scalar product of B candidate +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality of the collision DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs DECLARE_SOA_COLUMN(MlScoreSig, mlScoreSig, float); //! ML score for signal class DECLARE_SOA_COLUMN(FlagWrongCollision, flagWrongCollision, int8_t); //! Flag for association with wrong collision } // namespace hf_cand_bplus_lite DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with some B+ properties + // B meson features hf_cand_bplus_lite::M, hf_cand_bplus_lite::Pt, hf_cand_bplus_lite::Eta, @@ -116,14 +124,20 @@ DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with som hf_cand_bplus_lite::MaxNormalisedDeltaIP, hf_cand_bplus_lite::MlScoreSig, hf_sel_candidate_bplus::IsSelBplusToD0Pi, + hf_cand_bplus_lite::ScalarProd, + hf_cand_bplus_lite::Centrality, // D meson features hf_cand_bplus_lite::MD, hf_cand_bplus_lite::PtD, hf_cand_bplus_lite::DecayLengthD, hf_cand_bplus_lite::DecayLengthXYD, - hf_cand_bplus_lite::ImpactParameterD, + hf_cand_bplus_lite::DecayLengthDToB, + hf_cand_bplus_lite::DecayLengthXYDToB, hf_cand_bplus_lite::CpaD, - hf_cand_bplus_lite::CpaXYD, + hf_cand_bplus_lite::CpaDXY, + hf_cand_bplus_lite::CpaDToB, + hf_cand_bplus_lite::CpaDToBXY, + hf_cand_bplus_lite::ImpactParameterD, hf_cand_bplus_lite::PtDmesProngMin, hf_cand_bplus_lite::AbsEtaDmesProngMin, hf_cand_bplus_lite::ItsNClsDmesProngMin, @@ -170,13 +184,14 @@ DECLARE_SOA_TABLE(HfRedBpMcCheck, "AOD", "HFREDBPMCCHECK", //! Table with MC dec } // namespace o2::aod // string definitions, used for histogram axis labels -const TString stringPt = "#it{p}_{T} (GeV/#it{c})"; -const TString stringPtD = "#it{p}_{T}(D0) (GeV/#it{c});"; -const TString bPlusCandTitle = "B+ candidates;"; +const TString stringPt = "#it{p}_{T}(B^{+}) (GeV/#it{c});"; +const TString stringPtGen = "#it{p}_{T}^{gen}(B^{+}) (GeV/#it{c});"; +const TString stringPtD = "#it{p}_{T}(D^{0}) (GeV/#it{c});"; +const TString bPlusCandTitle = "B^{+} candidates;"; const TString entries = "entries"; -const TString bPlusCandMatch = "B+ candidates (matched);"; -const TString bPlusCandUnmatch = "B+ candidates (unmatched);"; -const TString mcParticleMatched = "MC particles (matched);"; +const TString bPlusCandMatch = "B^{+} candidates (matched);"; +const TString bPlusCandUnmatch = "B^{+} candidates (unmatched);"; +const TString bPlusPartGen = "B^{+} particles (generated);"; /// B+ analysis task struct HfTaskBplusReduced { @@ -184,18 +199,22 @@ struct HfTaskBplusReduced { Produces hfRedBpMcCheck; Configurable selectionFlagBplus{"selectionFlagBplus", 1, "Selection Flag for Bplus"}; - Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; - Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; - Configurable etaTrackMax{"etaTrackMax", 0.8, "max. track pseudo-rapidity"}; - Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track transverse momentum"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable etaTrackMax{"etaTrackMax", 0.8, "max. track pseudo-rapidity for acceptance calculation"}; + Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track transverse momentum for acceptance calculation"}; Configurable fillHistograms{"fillHistograms", true, "Flag to enable histogram filling"}; Configurable fillSparses{"fillSparses", false, "Flag to enable sparse filling"}; Configurable fillTree{"fillTree", false, "Flag to enable tree filling"}; Configurable fillBackground{"fillBackground", false, "Flag to enable filling of background histograms/sparses/tree (only MC)"}; Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - Configurable> binsPt{"binsPt", std::vector{hf_cuts_bplus_to_d0_pi::vecBinsPt}, "pT bin limits"}; + Configurable centralityEstimator{"centralityEstimator", 1, "Centrality estimator (FT0M: 0, FT0C: 1)"}; + Configurable harmonic{"harmonic", 2, "harmonic number"}; + Configurable qVecDetector{"qVecDetector", 3, "Detector for Q vector estimation (FT0M: 1, FT0A: 2, FT0C: 3, TPC Pos: 4, TPC Neg: 5, TPC Tot: 6)"}; + using CollisionsWithCents = soa::Join; + using CollisionsWithCentsQvecs = soa::Join; using TracksPion = soa::Join; using CandsD0 = soa::Join; @@ -205,7 +224,7 @@ struct HfTaskBplusReduced { void init(InitContext&) { - std::array processFuncData{doprocessData, doprocessDataWithDmesMl, doprocessDataWithBplusMl}; + std::array processFuncData{doprocessData, doprocessDataWithDmesMl, doprocessDataWithDmesMlScalarProd, doprocessDataWithBplusMl, doprocessDataWithBplusMlScalarProd}; if ((std::accumulate(processFuncData.begin(), processFuncData.end(), 0)) > 1) { LOGP(fatal, "Only one process function for data can be enabled at a time."); } @@ -215,131 +234,125 @@ struct HfTaskBplusReduced { } const AxisSpec axisMlScore{100, 0.f, 1.f}; - const AxisSpec axisMassBplus{150, 4.5, 6.0}; + const AxisSpec axisMassBplus{300, 4.5f, 6.0f}; const AxisSpec axisMassD0{300, 1.75f, 2.05f}; - const AxisSpec axisCpa{120, -1.1, 1.1}; - const AxisSpec axisCpaD{101, 0.9, 1.01}; - const AxisSpec axisPtProng{100, 0., 10.}; - const AxisSpec axisD0Prong{200, -0.05, 0.05}; - const AxisSpec axisImpParProd{200, -0.001, 0.001}; - const AxisSpec axisDecLength{100, 0., 0.5}; - const AxisSpec axisNormDecLength{40, 0., 20}; - const AxisSpec axisEta{100, -2., 2.}; - const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtD0{100, 0., 50.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisDecayLength{200, 0.f, 0.4f}; + const AxisSpec axisNormDecayLength{100, 0.f, 50.f}; + const AxisSpec axisDca{100, -0.05f, 0.05f}; + const AxisSpec axisCosp{110, 0.f, 1.1f}; + const AxisSpec axisEta{30, -1.5f, 1.5f}; + const AxisSpec axisImpParProd{100, -1.e-3, 1.e-3}; + const AxisSpec axisPtBplus{100, 0.f, 50.f}; + const AxisSpec axisPtD0{100, 0.f, 50.f}; const AxisSpec axisPtPi{100, 0.f, 10.f}; - if (doprocessData || doprocessDataWithDmesMl || doprocessDataWithBplusMl) { + if (doprocessData || doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd || doprocessDataWithBplusMl || doprocessDataWithBplusMlScalarProd) { if (fillHistograms) { - registry.add("hMass", bPlusCandTitle + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hPtCand", bPlusCandTitle + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{1000, 0., 50.}}}); - registry.add("hPtProng0", bPlusCandTitle + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1", bPlusCandTitle + "prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hDecLength", bPlusCandTitle + "decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXy", bPlusCandTitle + "decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXy", bPlusCandTitle + "Norm. decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0Prong0", bPlusCandTitle + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1", bPlusCandTitle + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hCpa", bPlusCandTitle + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXy", bPlusCandTitle + "candidate cosine of pointing angle xy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEta", bPlusCandTitle + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidity", bPlusCandTitle + "candidate #it{y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hd0d0", bPlusCandTitle + "candidate product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hInvMassD0", bPlusCandTitle + "prong0, D0 inv. mass (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassD0, axisPtD0}}); - registry.add("hDecLengthD0", bPlusCandTitle + "D^{0} candidate decay length (cm);#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisDecLength, axisPtD0}}); - registry.add("hDecLengthXyD0", bPlusCandTitle + "decay length XY (cm);#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisDecLength, axisPtD0}}); - registry.add("hCpaD0", bPlusCandTitle + "D^{0} candidate cos(#vartheta_{P});#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisCpaD, axisPtD0}}); - registry.add("hCpaXyD0", bPlusCandTitle + "D^{0} candidate cos(#vartheta_{P}^{XY});#it{p}_{T}(D^{0}) (GeV/#it{c});entries", {HistType::kTH2F, {axisCpaD, axisPtD0}}); + registry.add("hMass", bPlusCandTitle + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLength", bPlusCandTitle + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXy", bPlusCandTitle + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXy", bPlusCandTitle + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0", bPlusCandTitle + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1", bPlusCandTitle + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCosp", bPlusCandTitle + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXy", bPlusCandTitle + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEta", bPlusCandTitle + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidity", bPlusCandTitle + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProd", bPlusCandTitle + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0", bPlusCandTitle + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1", bPlusCandTitle + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassD", bPlusCandTitle + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthD", bPlusCandTitle + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyD", bPlusCandTitle + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospD", bPlusCandTitle + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyD", bPlusCandTitle + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); // ML scores of D0 daughter - if (doprocessDataWithDmesMl) { - registry.add("hMlScoreBkgD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptD", bPlusCandTitle + "#it{p}_{T}(D0) (GeV/#it{c});prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + if (doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd) { + registry.add("hMlScoreBkgD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptD", bPlusCandTitle + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); } // ML scores of B+ candidate - if (doprocessDataWithBplusMl) { - registry.add("hMlScoreSigBplus", bPlusCandTitle + "#it{p}_{T} (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + if (doprocessDataWithBplusMl || doprocessDataWithBplusMlScalarProd) { + registry.add("hMlScoreSigBplus", bPlusCandTitle + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); } } if (fillSparses) { - if (!(doprocessDataWithDmesMl || doprocessDataWithBplusMl)) { - registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D0#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + if (!(doprocessDataWithDmesMl || doprocessDataWithDmesMlScalarProd)) { + registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); } else { - registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D0#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVars", bPlusCandTitle + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); } } } - // histograms processMC if (doprocessMc || doprocessMcWithDecayTypeCheck || doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck || doprocessMcWithBplusMl || doprocessMcWithBplusMlAndDecayTypeCheck) { if (fillHistograms) { - // Gen Level - registry.add("hEtaGen", mcParticleMatched + "candidate #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hYGen", mcParticleMatched + "candidate #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProng0Gen", mcParticleMatched + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1Gen", mcParticleMatched + "prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hYProng0Gen", mcParticleMatched + "prong 0 #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hYProng1Gen", mcParticleMatched + "prong 1 #it{y}^{gen};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hEtaProng0Gen", mcParticleMatched + "prong 0 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hEtaProng1Gen", mcParticleMatched + "prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hPtProngsVsPtBGen", mcParticleMatched + "prong 0 #it{p}_{T}^{gen} (GeV/#it{c});prong 1 #it{p}_{T}^{gen} (GeV/#it{c});" + stringPt, {HistType::kTH3F, {axisPtProng, axisPtProng, axisPtB}}); - registry.add("hYProngsVsBplusGen", mcParticleMatched + "prong 0 #it{y}^{gen};prong 1 #it{y}^{gen};" + stringPt, {HistType::kTH3F, {axisRapidity, axisRapidity, axisPtB}}); - registry.add("hEtaProngsVsBplusGen", mcParticleMatched + "prong 0 #it{#eta}^{gen};prong 1 #it{#eta}^{gen};" + stringPt, {HistType::kTH3F, {axisEta, axisEta, axisPtB}}); - registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);B^{+} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - // Reco Level - Signal - registry.add("hPtRecSig", bPlusCandMatch + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hCpaRecSig", bPlusCandMatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXyRecSig", bPlusCandMatch + "candidate CpaXy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEtaRecSig", bPlusCandMatch + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidityRecSig", bPlusCandMatch + "candidate #it{y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProng0RecSig", bPlusCandMatch + "prong 0 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1RecSig", bPlusCandMatch + "prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hMassRecSig", bPlusCandMatch + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hd0Prong0RecSig", bPlusCandMatch + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1RecSig", bPlusCandMatch + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hDecLengthRecSig", bPlusCandMatch + "candidate decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXyRecSig", bPlusCandMatch + "candidate decay length xy (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXyRecSig", bPlusCandMatch + "candidate normalized decay length (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0d0RecSig", bPlusCandMatch + "product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hCpaD0RecSig", bPlusCandMatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hCpaXyD0RecSig", bPlusCandMatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hDecLengthD0RecSig", bPlusCandMatch + "prong0 D^{0} decay length (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); - registry.add("hDecLengthXyD0RecSig", bPlusCandMatch + "prong0 D^{0} decay length XY (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); + // gen histos + registry.add("hEtaGen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYGen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);" + stringPtGen + "#it{y}^{gen}(B^{+});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hPtProng0Gen", bPlusPartGen + stringPtGen + "#it{p}_{T}^{gen}(D^{0}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1Gen", bPlusPartGen + stringPtGen + "#it{p}_{T}^{gen}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hYProng0Gen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(D^{0});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hYProng1Gen", bPlusPartGen + stringPtGen + "#it{y}^{gen}(#pi^{#plus});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hEtaProng0Gen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(D^{0});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hEtaProng1Gen", bPlusPartGen + stringPtGen + "#it{#eta}^{gen}(#pi^{#plus});" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + + // reco histos + // signal + registry.add("hMassRecSig", bPlusCandMatch + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLengthRecSig", bPlusCandMatch + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXyRecSig", bPlusCandMatch + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXyRecSig", bPlusCandMatch + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0RecSig", bPlusCandMatch + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1RecSig", bPlusCandMatch + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCospRecSig", bPlusCandMatch + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXyRecSig", bPlusCandMatch + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEtaRecSig", bPlusCandMatch + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidityRecSig", bPlusCandMatch + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProdRecSig", bPlusCandMatch + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0RecSig", bPlusCandMatch + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1RecSig", bPlusCandMatch + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassDRecSig", bPlusCandMatch + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyDRecSig", bPlusCandMatch + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyDRecSig", bPlusCandMatch + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + // background if (fillBackground) { - registry.add("hPtRecBg", bPlusCandUnmatch + "candidate #it{p}_{T} (GeV/#it{c});" + entries, {HistType::kTH1F, {{300, 0., 30.}}}); - registry.add("hCpaRecBg", bPlusCandUnmatch + "candidate cosine of pointing angle;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hCpaXyRecBg", bPlusCandUnmatch + "candidate CpaXy;" + stringPt, {HistType::kTH2F, {axisCpa, axisPtB}}); - registry.add("hEtaRecBg", bPlusCandUnmatch + "candidate #it{#eta};" + stringPt, {HistType::kTH2F, {axisEta, axisPtB}}); - registry.add("hRapidityRecBg", bPlusCandUnmatch + "candidate #it{#y};" + stringPt, {HistType::kTH2F, {axisRapidity, axisPtB}}); - registry.add("hPtProngsVsBplusRecBg", bPlusCandUnmatch + "prong 0 #it{p}_{T} (GeV/#it{c});prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH3F, {axisPtProng, axisPtProng, axisPtB}}); - registry.add("hPtProng0RecBg", bPlusCandUnmatch + "prong 0 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hPtProng1RecBg", bPlusCandUnmatch + "prong 1 #it{p}_{T} (GeV/#it{c});" + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); - registry.add("hMassRecBg", bPlusCandUnmatch + "inv. mass #bar{D^{0}}#pi^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); - registry.add("hd0Prong0RecBg", bPlusCandUnmatch + "prong 0 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hd0Prong1RecBg", bPlusCandUnmatch + "prong 1 DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisD0Prong, axisPtB}}); - registry.add("hDecLengthRecBg", bPlusCandUnmatch + "candidate decay length (cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hDecLengthXyRecBg", bPlusCandUnmatch + "candidate decay length xy(cm);" + stringPt, {HistType::kTH2F, {axisDecLength, axisPtB}}); - registry.add("hNormDecLengthXyRecBg", bPlusCandUnmatch + "candidate normalized decay length (cm);" + stringPt, {HistType::kTH2F, {axisNormDecLength, axisPtB}}); - registry.add("hd0d0RecBg", bPlusCandUnmatch + "product of DCAxy to prim. vertex (cm^{2});" + stringPt, {HistType::kTH2F, {axisImpParProd, axisPtB}}); - registry.add("hCpaD0RecBg", bPlusCandUnmatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hCpaXyD0RecBg", bPlusCandUnmatch + "prong0 (D^{0}) cosine of pointing angle;" + stringPtD + entries, {HistType::kTH2F, {{220, 0., 1.1}, {120, 0., 60.}}}); - registry.add("hDecLengthD0RecBg", bPlusCandUnmatch + "prong0 D^{0} decay length (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); - registry.add("hDecLengthXyD0RecBg", bPlusCandUnmatch + "prong0 D^{0} decay length XY (cm);" + stringPtD + entries, {HistType::kTH2F, {{100, 0., 0.5}, {120, 0., 60.}}}); + registry.add("hMassRecBg", bPlusCandUnmatch + stringPt + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtBplus, axisMassBplus}}); + registry.add("hDecLengthRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hDecLengthXyRecBg", bPlusCandUnmatch + stringPt + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDecayLength}}); + registry.add("hNormDecLengthXyRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate norm. decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisNormDecayLength}}); + registry.add("hDcaProng0RecBg", bPlusCandUnmatch + stringPt + "prong 0 (D^{0}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hDcaProng1RecBg", bPlusCandUnmatch + stringPt + "prong 1 (#pi^{#plus}) DCAxy to prim. vertex (cm);" + entries, {HistType::kTH2F, {axisPtBplus, axisDca}}); + registry.add("hCospRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hCospXyRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtBplus, axisCosp}}); + registry.add("hEtaRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate #it{#eta};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hRapidityRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate #it{y};" + entries, {HistType::kTH2F, {axisPtBplus, axisEta}}); + registry.add("hImpParProdRecBg", bPlusCandUnmatch + stringPt + "B^{+} candidate impact parameter product;" + entries, {HistType::kTH2F, {axisPtBplus, axisImpParProd}}); + registry.add("hPtProng0RecBg", bPlusCandUnmatch + stringPt + stringPtD + entries, {HistType::kTH2F, {axisPtBplus, axisPtD0}}); + registry.add("hPtProng1RecBg", bPlusCandUnmatch + stringPt + "#it{p}_{T}(#pi^{#plus}) (GeV/#it{c});" + entries, {HistType::kTH2F, {axisPtBplus, axisPtPi}}); + registry.add("hInvMassDRecBg", bPlusCandUnmatch + stringPtD + "prong0, #it{M}(K#pi) (GeV/#it{c}^{2});" + entries, {HistType::kTH2F, {axisPtD0, axisMassD0}}); + registry.add("hDecLengthDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate decay length (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hDecLengthXyDRecBg", bPlusCandUnmatch + stringPtD + "decay length XY (cm);" + entries, {HistType::kTH2F, {axisPtD0, axisDecayLength}}); + registry.add("hCospDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate cos(#vartheta_{P});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); + registry.add("hCospXyDRecBg", bPlusCandUnmatch + stringPtD + "D^{0} candidate cos(#vartheta_{P}^{XY});" + entries, {HistType::kTH2F, {axisPtD0, axisCosp}}); } // MC checks if (doprocessMcWithDecayTypeCheck || doprocessMcWithDmesMlAndDecayTypeCheck || doprocessMcWithBplusMlAndDecayTypeCheck) { constexpr uint8_t kNBinsDecayTypeMc = hf_cand_bplus::DecayTypeMc::NDecayTypeMc; TString labels[kNBinsDecayTypeMc]; labels[hf_cand_bplus::DecayTypeMc::BplusToD0PiToKPiPi] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) #pi^{#plus}"; - labels[hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) #K^{#plus}"; + labels[hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK] = "B^{+} #rightarrow (#overline{D^{0}} #rightarrow K^{#plus} #pi^{#minus}) K^{#plus}"; labels[hf_cand_bplus::DecayTypeMc::PartlyRecoDecay] = "Partly reconstructed decay channel"; labels[hf_cand_bplus::DecayTypeMc::OtherDecay] = "Other decays"; static const AxisSpec axisDecayType = {kNBinsDecayTypeMc, 0.5, kNBinsDecayTypeMc + 0.5, ""}; - registry.add("hDecayTypeMc", "DecayType", {HistType::kTH3F, {axisDecayType, axisMassBplus, axisPtB}}); + registry.add("hDecayTypeMc", "DecayType", {HistType::kTH3F, {axisDecayType, axisMassBplus, axisPtBplus}}); for (uint8_t iBin = 0; iBin < kNBinsDecayTypeMc; ++iBin) { registry.get(HIST("hDecayTypeMc"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin]); } @@ -347,36 +360,37 @@ struct HfTaskBplusReduced { // ML scores of D0 daughter if (doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck) { // signal - registry.add("hMlScoreBkgDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreBkgDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptDRecSig", bPlusCandMatch + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); // background - registry.add("hMlScoreBkgDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML background score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScorePromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML prompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); - registry.add("hMlScoreNonPromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D0 ML nonprompt score;entries", {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreBkgDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML background score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScorePromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML prompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); + registry.add("hMlScoreNonPromptDRecBg", bPlusCandUnmatch + stringPtD + "prong0, D^{0} ML nonprompt score;" + entries, {HistType::kTH2F, {axisPtD0, axisMlScore}}); } // ML scores of B+ candidate if (doprocessMcWithBplusMl || doprocessMcWithBplusMlAndDecayTypeCheck) { // signal - registry.add("hMlScoreSigBplusRecSig", bPlusCandMatch + "#it{p}_{T}(B^{+}) (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + registry.add("hMlScoreSigBplusRecSig", bPlusCandMatch + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); // background - registry.add("hMlScoreSigBplusRecBg", bPlusCandUnmatch + "#it{p}_{T}(B^{+}) (GeV/#it{c});prong0, B^{+} ML signal score;entries", {HistType::kTH2F, {axisPtB, axisMlScore}}); + registry.add("hMlScoreSigBplusRecBg", bPlusCandUnmatch + stringPt + "prong0, B^{+} ML signal score;" + entries, {HistType::kTH2F, {axisPtBplus, axisMlScore}}); } } if (fillSparses) { // gen sparses - registry.add("hPtYGenSig", "B^{+} particles (generated);#it{p}_{T}(B^{+}) (GeV/#it{c});#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtB, axisEta}}); - registry.add("hPtYWithProngsInAccepanceGenSig", "B^{+} particles (generated-daughters in acceptance);#it{p}_{T}(B^{+}) (GeV/#it{c});#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtB, axisEta}}); + registry.add("hPtYGenSig", bPlusPartGen + stringPt + "#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtBplus, axisEta}}); + registry.add("hPtYWithProngsInAccepanceGenSig", "B^{+} particles (generated-daughters in acceptance);" + stringPt + "#it{y}(B^{+})", {HistType::kTHnSparseF, {axisPtBplus, axisEta}}); + // reco sparses - if (!doprocessDataWithDmesMl) { - registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + if (!(doprocessMcWithDmesMl || doprocessMcWithDmesMlAndDecayTypeCheck)) { + registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); if (fillBackground) { - registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate decay length (cm);D0 candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisDecLength, axisCpa}}); + registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate decay length (cm);D^{0} candidate cos(#vartheta_{P})", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisDecayLength, axisCosp}}); } } else { - registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVarsRecSig", bPlusCandMatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); if (fillBackground) { - registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});#it{p}_{T}(B^{+}) (GeV/#it{c});B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});#it{p}_{T}(D0) (GeV/#it{c});D0 candidate ML score bkg;D0 candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtB, axisDecLength, axisNormDecLength, axisImpParProd, axisCpa, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); + registry.add("hMassPtCutVarsRecBg", bPlusCandUnmatch + "#it{M} (D^{0}#pi) (GeV/#it{c}^{2});" + stringPt + "B^{+} candidate decay length (cm);B^{+} candidate norm. decay length XY (cm);B^{+} candidate impact parameter product (cm);B^{+} candidate cos(#vartheta_{P});#it{M} (K#pi) (GeV/#it{c}^{2});" + stringPtD + "D^{0} candidate ML score bkg;D^{0} candidate ML score nonprompt", {HistType::kTHnSparseF, {axisMassBplus, axisPtBplus, axisDecayLength, axisNormDecayLength, axisImpParProd, axisCosp, axisMassD0, axisPtD0, axisMlScore, axisMlScore}}); } } } @@ -398,19 +412,21 @@ struct HfTaskBplusReduced { /// \param withDecayTypeCheck is the flag to enable MC with decay type check /// \param withDmesMl is the flag to enable the filling with ML scores for the D0 daughter /// \param withBplusMl is the flag to enable the filling with ML scores for the B+ candidate + /// \param withScalarProd is the flag to enable the filling with scalar product + /// \param collision is the collision /// \param candidate is the B+ candidate /// \param candidatesD is the table with D0 candidates - template - void fillCand(Cand const& candidate, - CandsDmes const& /*candidatesD*/, - TracksPion const&) + template + void fillCand(Collision const& collision, + Cand const& candidate, + CandsDmes const&) { auto ptCandBplus = candidate.pt(); auto invMassBplus = HfHelper::invMassBplusToD0Pi(candidate); auto candD0 = candidate.template prong0_as(); - auto candPi = candidate.template prong1_as(); + auto prong1 = candidate.template prong1_as(); auto ptD0 = candidate.ptProng0(); - auto invMassD0 = (candPi.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); + auto invMassD0 = (prong1.signed1Pt() < 0) ? candD0.invMassHypo0() : candD0.invMassHypo1(); std::array const posPv{candidate.posX(), candidate.posY(), candidate.posZ()}; std::array const posSvD{candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()}; std::array const momD{candD0.pVector()}; @@ -431,26 +447,24 @@ struct HfTaskBplusReduced { if (fillHistograms) { if constexpr (DoMc) { if (isSignal) { - registry.fill(HIST("hMassRecSig"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtRecSig"), ptCandBplus); - registry.fill(HIST("hPtProng0RecSig"), ptD0, ptCandBplus); - registry.fill(HIST("hPtProng1RecSig"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hCpaRecSig"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXyRecSig"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEtaRecSig"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidityRecSig"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hDecLengthRecSig"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXyRecSig"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXyRecSig"), candidate.decayLengthXYNormalised(), ptCandBplus); - registry.fill(HIST("hd0Prong0RecSig"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1RecSig"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hd0d0RecSig"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hPtProng0RecSig"), candidate.ptProng0(), ptCandBplus); - registry.fill(HIST("hPtProng1RecSig"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hDecLengthD0RecSig"), decLenD0, candidate.ptProng0()); - registry.fill(HIST("hDecLengthXyD0RecSig"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaD0RecSig"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyD0RecSig"), cpaXyD0, ptD0); + registry.fill(HIST("hMassRecSig"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0RecSig"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1RecSig"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProdRecSig"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLengthRecSig"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXyRecSig"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXyRecSig"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0RecSig"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1RecSig"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCospRecSig"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXyRecSig"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEtaRecSig"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidityRecSig"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassDRecSig"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthDRecSig"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyDRecSig"), ptD0, decLenXyD0); + registry.fill(HIST("hCospDRecSig"), ptD0, cpaD0); + registry.fill(HIST("hCospXyDRecSig"), ptD0, cpaXyD0); if constexpr (WithDecayTypeCheck) { registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::BplusToD0PiToKPiPi, invMassBplus, ptCandBplus); } @@ -463,26 +477,24 @@ struct HfTaskBplusReduced { registry.fill(HIST("hMlScoreSigBplusRecSig"), ptCandBplus, candidate.mlProbBplusToD0Pi()); } } else if (fillBackground) { - registry.fill(HIST("hMassRecBg"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtRecBg"), ptCandBplus); - registry.fill(HIST("hPtProng0RecBg"), candidate.ptProng0(), ptCandBplus); - registry.fill(HIST("hPtProng1RecBg"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hCpaRecBg"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXyRecBg"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEtaRecBg"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidityRecBg"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hDecLengthRecBg"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXyRecBg"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXyRecBg"), candidate.decayLengthXYNormalised(), ptCandBplus); - registry.fill(HIST("hd0Prong0RecBg"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1RecBg"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hd0d0RecBg"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hDecLengthD0RecBg"), decLenD0, candidate.ptProng0()); - registry.fill(HIST("hInvMassDRecBg"), invMassD0, ptD0); - registry.fill(HIST("hDecLengthDRecBg"), decLenD0, ptD0); - registry.fill(HIST("hDecLengthXyDRecBg"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaDRecBg"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyDRecBg"), cpaXyD0, ptD0); + registry.fill(HIST("hMassRecBg"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0RecBg"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1RecBg"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProdRecBg"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLengthRecBg"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXyRecBg"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXyRecBg"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0RecBg"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1RecBg"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCospRecBg"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXyRecBg"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEtaRecBg"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidityRecBg"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassDRecBg"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthDRecBg"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyDRecBg"), ptD0, decLenXyD0); + registry.fill(HIST("hCospDRecBg"), ptD0, cpaD0); + registry.fill(HIST("hCospXyDRecBg"), ptD0, cpaXyD0); if constexpr (WithDmesMl) { registry.fill(HIST("hMlScoreBkgDRecBg"), ptD0, candidate.prong0MlScoreBkg()); registry.fill(HIST("hMlScorePromptDRecBg"), ptD0, candidate.prong0MlScorePrompt()); @@ -492,7 +504,7 @@ struct HfTaskBplusReduced { registry.fill(HIST("hMlScoreSigBplusRecBg"), ptCandBplus, candidate.mlProbBplusToD0Pi()); } } else if constexpr (WithDecayTypeCheck) { - if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK)) { // Partly reconstructed decay channel + if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK)) { // B+ → D0bar K+ → (K+ π-) K+ registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::BplusToD0KToKPiK, invMassBplus, ptCandBplus); } else if (TESTBIT(flagMcMatchRec, hf_cand_bplus::DecayTypeMc::PartlyRecoDecay)) { // Partly reconstructed decay channel registry.fill(HIST("hDecayTypeMc"), 1 + hf_cand_bplus::DecayTypeMc::PartlyRecoDecay, invMassBplus, ptCandBplus); @@ -501,25 +513,25 @@ struct HfTaskBplusReduced { } } } else { - registry.fill(HIST("hMass"), invMassBplus, ptCandBplus); - registry.fill(HIST("hPtCand"), ptCandBplus); - registry.fill(HIST("hPtProng0"), ptD0, ptCandBplus); - registry.fill(HIST("hPtProng1"), candidate.ptProng1(), ptCandBplus); - registry.fill(HIST("hd0d0"), candidate.impactParameterProduct(), ptCandBplus); - registry.fill(HIST("hDecLength"), candidate.decayLength(), ptCandBplus); - registry.fill(HIST("hDecLengthXy"), candidate.decayLengthXY(), ptCandBplus); - registry.fill(HIST("hNormDecLengthXy"), candidate.decayLengthXY() / candidate.errorDecayLengthXY(), ptCandBplus); - registry.fill(HIST("hd0Prong0"), candidate.impactParameter0(), ptCandBplus); - registry.fill(HIST("hd0Prong1"), candidate.impactParameter1(), ptCandBplus); - registry.fill(HIST("hCpa"), candidate.cpa(), ptCandBplus); - registry.fill(HIST("hCpaXy"), candidate.cpaXY(), ptCandBplus); - registry.fill(HIST("hEta"), candidate.eta(), ptCandBplus); - registry.fill(HIST("hRapidity"), HfHelper::yBplus(candidate), ptCandBplus); - registry.fill(HIST("hInvMassD0"), invMassD0, ptCandBplus); - registry.fill(HIST("hDecLengthD0"), decLenD0, ptD0); - registry.fill(HIST("hDecLengthXyD0"), decLenXyD0, ptD0); - registry.fill(HIST("hCpaD0"), cpaD0, ptD0); - registry.fill(HIST("hCpaXyD0"), cpaXyD0, ptD0); + registry.fill(HIST("hMass"), ptCandBplus, invMassBplus); + registry.fill(HIST("hPtProng0"), ptCandBplus, candidate.ptProng0()); + registry.fill(HIST("hPtProng1"), ptCandBplus, candidate.ptProng1()); + registry.fill(HIST("hImpParProd"), ptCandBplus, candidate.impactParameterProduct()); + registry.fill(HIST("hDecLength"), ptCandBplus, candidate.decayLength()); + registry.fill(HIST("hDecLengthXy"), ptCandBplus, candidate.decayLengthXY()); + registry.fill(HIST("hNormDecLengthXy"), ptCandBplus, candidate.decayLengthXY() / candidate.errorDecayLengthXY()); + registry.fill(HIST("hDcaProng0"), ptCandBplus, candidate.impactParameter0()); + registry.fill(HIST("hDcaProng1"), ptCandBplus, candidate.impactParameter1()); + registry.fill(HIST("hCosp"), ptCandBplus, candidate.cpa()); + registry.fill(HIST("hCospXy"), ptCandBplus, candidate.cpaXY()); + registry.fill(HIST("hEta"), ptCandBplus, candidate.eta()); + registry.fill(HIST("hRapidity"), ptCandBplus, HfHelper::yBplus(candidate)); + registry.fill(HIST("hInvMassD"), ptD0, invMassD0); + registry.fill(HIST("hDecLengthD"), ptD0, decLenD0); + registry.fill(HIST("hDecLengthXyD"), ptD0, decLenXyD0); + registry.fill(HIST("hCospD"), ptD0, cpaD0); + registry.fill(HIST("hCospXyD"), ptD0, cpaXyD0); + if constexpr (WithDmesMl) { registry.fill(HIST("hMlScoreBkgD"), ptD0, candidate.prong0MlScoreBkg()); registry.fill(HIST("hMlScorePromptD"), ptD0, candidate.prong0MlScorePrompt()); @@ -555,11 +567,12 @@ struct HfTaskBplusReduced { } if (fillTree) { float const pseudoRndm = ptD0 * 1000. - static_cast(ptD0 * 1000); - if (ptCandBplus >= ptMaxForDownSample || pseudoRndm < downSampleBkgFactor) { + if (flagMcMatchRec != 0 || (((DoMc && fillBackground) || !DoMc) && (ptCandBplus >= ptMaxForDownSample || pseudoRndm < downSampleBkgFactor))) { float prong0MlScoreBkg = -1.; float prong0MlScorePrompt = -1.; float prong0MlScoreNonprompt = -1.; float candidateMlScoreSig = -1; + float scalarProd = -1; if constexpr (WithDmesMl) { prong0MlScoreBkg = candidate.prong0MlScoreBkg(); prong0MlScorePrompt = candidate.prong0MlScorePrompt(); @@ -568,7 +581,12 @@ struct HfTaskBplusReduced { if constexpr (WithBplusMl) { candidateMlScoreSig = candidate.mlProbBplusToD0Pi(); } - auto prong1 = candidate.template prong1_as(); + if constexpr (WithScalarProd) { + auto qVecs = getQvec(collision, qVecDetector.value); + float xQVec = qVecs[0]; + float yQVec = qVecs[1]; + scalarProd = std::cos(harmonic * candidate.phi()) * xQVec + std::sin(harmonic * candidate.phi()) * yQVec; + } float tpcNSigmaPi, tofNSigmaPi, tpcTofNSigmaPi, tpcNSigmaKa, tofNSigmaKa, tpcTofNSigmaKa; if (prong1.signed1Pt() < 0) { tpcNSigmaPi = candD0.tpcNSigmaPiProng1(); @@ -592,7 +610,7 @@ struct HfTaskBplusReduced { } hfRedCandBpLite( - // B+ - meson features + // B-meson features invMassBplus, ptCandBplus, candidate.eta(), @@ -609,14 +627,20 @@ struct HfTaskBplusReduced { candidate.maxNormalisedDeltaIP(), candidateMlScoreSig, candidate.isSelBplusToD0Pi(), + scalarProd, + centralityEstimator == 0 ? collision.centFT0M() : collision.centFT0C(), // D-meson features invMassD0, ptD0, - decLenD0, - decLenXyD0, + candidate.decayLengthDToPv(), + candidate.decayLengthDToPvXY(), + candidate.decayLengthDToB(), + candidate.decayLengthDToBXY(), + candidate.cpaDToPv(), + candidate.cpaDToPvXY(), + candidate.cpaDToB(), + candidate.cpaDToBXY(), candidate.impactParameter0(), - cpaD0, - cpaXyD0, candD0.ptProngMin(), candD0.absEtaProngMin(), candD0.itsNClsProngMin(), @@ -705,56 +729,88 @@ struct HfTaskBplusReduced { } // Process functions - void processData(soa::Filtered> const& candidates, + void processData(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processData - PROCESS_SWITCH(HfTaskBplusReduced, processData, "Process data without ML scores for D0 daughter", true); + PROCESS_SWITCH(HfTaskBplusReduced, processData, "Process data without ML scores for B+ and D0 daughter", true); - void processDataWithDmesMl(soa::Filtered> const& candidates, + void processDataWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataWithDmesMl - PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMl, "Process data with ML scores for D0 daughter", false); + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMl, "Process data with(out) ML scores for D0 daughter (B+)", false); - void processDataWithBplusMl(soa::Filtered> const& candidates, + void processDataWithDmesMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsD0 const& candidatesD, + TracksPion const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataWithDmesMlScalarProd + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithDmesMlScalarProd, "Process data with(out) ML scores for D0 daughter (B+) and scalar products", false); + + void processDataWithBplusMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // candidate loop } // processDataWithBplusMl PROCESS_SWITCH(HfTaskBplusReduced, processDataWithBplusMl, "Process data with(out) ML scores for B+ (D0 daughter)", false); - void processMc(soa::Filtered> const& candidates, + void processDataWithBplusMlScalarProd(CollisionsWithCentsQvecs const& collisions, + soa::Filtered> const& candidates, + CandsD0 const& candidatesD, + TracksPion const&) + { + for (const auto& candidate : candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { + continue; + } + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); + } // candidate loop + } // processDataWithBplusMlScalarProd + PROCESS_SWITCH(HfTaskBplusReduced, processDataWithBplusMlScalarProd, "Process data with(out) ML scores for B+ (D0 daughter) and scalar products", false); + + void processMc(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -764,37 +820,39 @@ struct HfTaskBplusReduced { } // processMc PROCESS_SWITCH(HfTaskBplusReduced, processMc, "Process MC without ML scores for B+ and D0 daughter", false); - void processMcWithDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc - PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDecayTypeCheck, "Process MC with decay type check and without ML scores for B+ and D daughter", false); + } // processMcWithDecayTypeCheck + PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDecayTypeCheck, "Process MC with decay type check and without ML scores for B+ and D0 daughter", false); - void processMcWithDmesMl(soa::Filtered> const& candidates, + void processMcWithDmesMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -804,37 +862,39 @@ struct HfTaskBplusReduced { } // processMcWithDmesMl PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDmesMl, "Process MC with(out) ML scores for D0 daughter (B+)", false); - void processMcWithDmesMlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithDmesMlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc + } // processMcWithDmesMlAndDecayTypeCheck PROCESS_SWITCH(HfTaskBplusReduced, processMcWithDmesMlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B+ (D0 daughter)", false); - void processMcWithBplusMl(soa::Filtered> const& candidates, + void processMcWithBplusMl(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level @@ -844,24 +904,25 @@ struct HfTaskBplusReduced { } // processMcWithBplusMl PROCESS_SWITCH(HfTaskBplusReduced, processMcWithBplusMl, "Process MC with(out) ML scores for B+ (D0 daughter)", false); - void processMcWithBplusMlAndDecayTypeCheck(soa::Filtered> const& candidates, + void processMcWithBplusMlAndDecayTypeCheck(CollisionsWithCents const& collisions, + soa::Filtered> const& candidates, aod::HfMcGenRedBps const& mcParticles, CandsD0 const& candidatesD, - TracksPion const& pionTracks) + TracksPion const&) { // MC rec for (const auto& candidate : candidates) { if (yCandRecoMax >= 0. && std::abs(HfHelper::yBplus(candidate)) > yCandRecoMax) { continue; } - fillCand(candidate, candidatesD, pionTracks); + fillCand(collisions.rawIteratorAt(candidate.collisionId()), candidate, candidatesD); } // rec // MC gen. level for (const auto& particle : mcParticles) { fillCandMcGen(particle); } // gen - } // processMc + } // processMcWithBplusMlAndDecayTypeCheck PROCESS_SWITCH(HfTaskBplusReduced, processMcWithBplusMlAndDecayTypeCheck, "Process MC with decay type check and with(out) ML scores for B+ (D0 daughter)", false); }; // struct diff --git a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx index 31fae6a389d..6f1ea153985 100644 --- a/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBplusToJpsiKReduced.cxx @@ -24,6 +24,7 @@ #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/Utils/utilsPid.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/TrackSelectorPID.h" #include @@ -64,6 +65,8 @@ namespace o2::aod namespace hf_cand_bplustojpsik_lite { DECLARE_SOA_COLUMN(PtJpsi, ptJpsi, float); //! Transverse momentum of Jpsi daughter candidate (GeV/c) +DECLARE_SOA_COLUMN(PtJpsiDauPos, ptJpsiDauPos, float); //! Transverse momentum of Jpsi positive daughter candidate (GeV/c) +DECLARE_SOA_COLUMN(PtJpsiDauNeg, ptJpsiDauNeg, float); //! Transverse momentum of Jpsi negative daughter candidate (GeV/c) DECLARE_SOA_COLUMN(PtBach, ptBach, float); //! Transverse momentum of bachelor kaon (GeV/c) DECLARE_SOA_COLUMN(ItsNClsJpsiDauPos, itsNClsJpsiDauPos, int); //! Number of clusters in ITS DECLARE_SOA_COLUMN(TpcNClsCrossedRowsJpsiDauPos, tpcNClsCrossedRowsJpsiDauPos, int); //! Number of TPC crossed rows @@ -98,6 +101,12 @@ DECLARE_SOA_COLUMN(NSigTpcTofMuJpsiDauPos, nSigTpcTofMuJpsiDauPos, float); DECLARE_SOA_COLUMN(NSigTpcMuJpsiDauNeg, nSigTpcMuJpsiDauNeg, float); //! TPC Nsigma separation for Jpsi DauNeg with muon mass hypothesis DECLARE_SOA_COLUMN(NSigTofMuJpsiDauNeg, nSigTofMuJpsiDauNeg, float); //! TOF Nsigma separation for Jpsi DauNeg with muon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofMuJpsiDauNeg, nSigTpcTofMuJpsiDauNeg, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong1 with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcElJpsiDauPos, nSigTpcElJpsiDauPos, float); //! TPC Nsigma separation for Jpsi DauPos with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTofElJpsiDauPos, nSigTofElJpsiDauPos, float); //! TOF Nsigma separation for Jpsi DauPos with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofElJpsiDauPos, nSigTpcTofElJpsiDauPos, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong0 with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcElJpsiDauNeg, nSigTpcElJpsiDauNeg, float); //! TPC Nsigma separation for Jpsi DauNeg with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTofElJpsiDauNeg, nSigTofElJpsiDauNeg, float); //! TOF Nsigma separation for Jpsi DauNeg with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofElJpsiDauNeg, nSigTpcTofElJpsiDauNeg, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong1 with electron mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -125,6 +134,7 @@ DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with som hf_cand_bplustojpsik_lite::Y, hf_cand_bplustojpsik_lite::Cpa, hf_cand_bplustojpsik_lite::CpaXY, + hf_cand_bplustojpsik_lite::CpaJpsi, hf_cand::Chi2PCA, hf_cand_bplustojpsik_lite::DecayLength, hf_cand_bplustojpsik_lite::DecayLengthXY, @@ -141,20 +151,34 @@ DECLARE_SOA_TABLE(HfRedCandBpLites, "AOD", "HFREDCANDBPLITE", //! Table with som hf_cand_bplustojpsik_lite::PtJpsi, hf_cand_bplustojpsik_lite::ImpactParameterJpsiDauPos, hf_cand_bplustojpsik_lite::ImpactParameterJpsiDauNeg, - hf_cand_bplustojpsik_lite::ImpactParameterLfTrack0, // Jpsi daughter features + hf_cand_bplustojpsik_lite::PtJpsiDauPos, hf_cand_bplustojpsik_lite::ItsNClsJpsiDauPos, hf_cand_bplustojpsik_lite::TpcNClsCrossedRowsJpsiDauPos, hf_cand_bplustojpsik_lite::ItsChi2NClJpsiDauPos, hf_cand_bplustojpsik_lite::TpcChi2NClJpsiDauPos, hf_cand_bplustojpsik_lite::AbsEtaJpsiDauPos, + hf_cand_bplustojpsik_lite::PtJpsiDauNeg, hf_cand_bplustojpsik_lite::ItsNClsJpsiDauNeg, hf_cand_bplustojpsik_lite::TpcNClsCrossedRowsJpsiDauNeg, hf_cand_bplustojpsik_lite::ItsChi2NClJpsiDauNeg, hf_cand_bplustojpsik_lite::TpcChi2NClJpsiDauNeg, hf_cand_bplustojpsik_lite::AbsEtaJpsiDauNeg, + hf_cand_bplustojpsik_lite::NSigTpcMuJpsiDauPos, + hf_cand_bplustojpsik_lite::NSigTofMuJpsiDauPos, + hf_cand_bplustojpsik_lite::NSigTpcTofMuJpsiDauPos, + hf_cand_bplustojpsik_lite::NSigTpcMuJpsiDauNeg, + hf_cand_bplustojpsik_lite::NSigTofMuJpsiDauNeg, + hf_cand_bplustojpsik_lite::NSigTpcTofMuJpsiDauNeg, + hf_cand_bplustojpsik_lite::NSigTpcElJpsiDauPos, + hf_cand_bplustojpsik_lite::NSigTofElJpsiDauPos, + hf_cand_bplustojpsik_lite::NSigTpcTofElJpsiDauPos, + hf_cand_bplustojpsik_lite::NSigTpcElJpsiDauNeg, + hf_cand_bplustojpsik_lite::NSigTofElJpsiDauNeg, + hf_cand_bplustojpsik_lite::NSigTpcTofElJpsiDauNeg, // kaon features hf_cand_bplustojpsik_lite::PtBach, + hf_cand_bplustojpsik_lite::ImpactParameterLfTrack0, hf_cand_bplustojpsik_lite::ItsNClsLfTrack0, hf_cand_bplustojpsik_lite::TpcNClsCrossedRowsLfTrack0, hf_cand_bplustojpsik_lite::ItsChi2NClLfTrack0, @@ -207,6 +231,7 @@ struct HfTaskBplusToJpsiKReduced { Configurable fillBackground{"fillBackground", false, "Flag to enable filling of background histograms/sparses/tree (only MC)"}; Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; + Configurable useJpsiPdgMass{"useJpsiPdgMass", true, "Whether to use J/Psi PDG mass for B+ candidate mass evaluation or to use the invariant mass of the two prongs"}; // topological cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_bplus_to_jpsi_k::vecBinsPt}, "pT bin limits"}; Configurable> cuts{"cuts", {hf_cuts_bplus_to_jpsi_k::Cuts[0], hf_cuts_bplus_to_jpsi_k::NBinsPt, hf_cuts_bplus_to_jpsi_k::NCutVars, hf_cuts_bplus_to_jpsi_k::labelsPt, hf_cuts_bplus_to_jpsi_k::labelsCutVar}, "B+ candidate selection per pT bin"}; @@ -241,6 +266,7 @@ struct HfTaskBplusToJpsiKReduced { o2::ccdb::CcdbApi ccdbApi; using TracksKaon = soa::Join; + using JPsis = soa::Join; std::vector outputMl; // Filter filterSelectCandidates = (aod::hf_sel_candidate_bplus::isSelBplusToJpsiPi >= selectionFlagBplus); @@ -277,7 +303,7 @@ struct HfTaskBplusToJpsiKReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B^{+}} (GeV/#it{c})"}; const AxisSpec axisPtKa{100, 0.f, 10.f}; registry.add("hMass", bPlusCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBplus, axisPtB}}); @@ -335,15 +361,19 @@ struct HfTaskBplusToJpsiKReduced { /// \param candidatesJpsi is the table with Jpsi candidates template void fillCand(Cand const& candidate, - aod::HfRedJpsis const& /*candidatesJpsi*/, + JPsis const& /*candidatesJpsi*/, aod::HfRedBach0Tracks const&) { auto ptCandBplus = candidate.pt(); - auto invMassBplus = HfHelper::invMassBplusToJpsiK(candidate); - auto candJpsi = candidate.template jpsi_as(); + auto invMassBplus = HfHelper::invMassBplusToJpsiK(candidate, useJpsiPdgMass); + auto candJpsi = candidate.template jpsi_as(); auto candKa = candidate.template bachKa_as(); - auto ptJpsi = candidate.ptProng0(); - auto invMassJpsi = candJpsi.m(); + auto const pVecMu0 = candidate.pVectorProng0(); + auto const pVecMu1 = candidate.pVectorProng1(); + auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); + auto ptMuPos = RecoDecay::pt(pVecMu0); + auto ptMuNeg = RecoDecay::pt(pVecMu1); + auto invMassJpsi = RecoDecay::m(std::array{pVecMu0, pVecMu1}, std::array{o2::constants::physics::MassMuonPlus, o2::constants::physics::MassMuonMinus}); uint8_t statusBplus = 0; int8_t flagMcMatchRec{0}, flagMcDecayChanRec{0}, flagWrongCollision{0}; @@ -352,11 +382,11 @@ struct HfTaskBplusToJpsiKReduced { flagMcMatchRec = candidate.flagMcMatchRec(); flagMcDecayChanRec = candidate.flagMcDecayChanRec(); flagWrongCollision = candidate.flagWrongCollision(); - isSignal = std::abs(flagMcMatchRec) == o2::hf_decay::hf_cand_beauty::BplusToJpsiK; + isSignal = std::abs(flagMcMatchRec) == o2::hf_decay::hf_cand_beauty::BplusToJpsiKToMuMuK; } SETBIT(statusBplus, SelectionStep::RecoSkims); - if (HfHelper::selectionBplusToJpsiKTopol(candidate, cuts, binsPt)) { + if (HfHelper::selectionBplusToJpsiKTopol(candidate, cuts, binsPt, useJpsiPdgMass)) { SETBIT(statusBplus, SelectionStep::RecoTopol); } else if (selectionFlagBplus >= BIT(SelectionStep::RecoTopol) * 2 - 1) { return; @@ -391,17 +421,17 @@ struct HfTaskBplusToJpsiKReduced { } registry.fill(HIST("hMass"), invMassBplus, ptCandBplus); - registry.fill(HIST("hMassJpsi"), invMassJpsi, candidate.ptProng0()); - registry.fill(HIST("hd0K"), candidate.impactParameter1(), candidate.ptProng1()); + registry.fill(HIST("hMassJpsi"), invMassJpsi, ptJpsi); + registry.fill(HIST("hd0K"), candidate.impactParameter2(), candidate.ptProng2()); if constexpr (DoMc) { if (isSignal) { registry.fill(HIST("hMassRecSig"), invMassBplus, ptCandBplus); - registry.fill(HIST("hMassJpsiRecSig"), invMassJpsi, candidate.ptProng0()); - registry.fill(HIST("hd0KRecSig"), candidate.impactParameter1(), candidate.ptProng1()); + registry.fill(HIST("hMassJpsiRecSig"), invMassJpsi, ptJpsi); + registry.fill(HIST("hd0KRecSig"), candidate.impactParameter2(), candidate.ptProng2()); } else if (fillBackground) { registry.fill(HIST("hMassRecBg"), invMassBplus, ptCandBplus); - registry.fill(HIST("hMassJpsiRecBg"), invMassJpsi, candidate.ptProng0()); - registry.fill(HIST("hd0KRecBg"), candidate.impactParameter1(), candidate.ptProng1()); + registry.fill(HIST("hMassJpsiRecBg"), invMassJpsi, ptJpsi); + registry.fill(HIST("hd0KRecBg"), candidate.impactParameter2(), candidate.ptProng2()); } } @@ -421,6 +451,7 @@ struct HfTaskBplusToJpsiKReduced { HfHelper::yBplus(candidate), candidate.cpa(), candidate.cpaXY(), + candidate.cpaJpsi(), candidate.chi2PCA(), candidate.decayLength(), candidate.decayLengthXY(), @@ -436,19 +467,33 @@ struct HfTaskBplusToJpsiKReduced { ptJpsi, candidate.impactParameter0(), candidate.impactParameter1(), - candidate.impactParameter2(), + ptMuPos, candJpsi.itsNClsDauPos(), candJpsi.tpcNClsCrossedRowsDauPos(), candJpsi.itsChi2NClDauPos(), candJpsi.tpcChi2NClDauPos(), absEta(candJpsi.tglDauPos()), + ptMuNeg, candJpsi.itsNClsDauNeg(), candJpsi.tpcNClsCrossedRowsDauNeg(), candJpsi.itsChi2NClDauNeg(), candJpsi.tpcChi2NClDauNeg(), absEta(candJpsi.tglDauNeg()), + candJpsi.tpcNSigmaMuPos(), + candJpsi.tofNSigmaMuPos(), + candJpsi.tpcTofNSigmaMuPos(), + candJpsi.tpcNSigmaMuNeg(), + candJpsi.tofNSigmaMuNeg(), + candJpsi.tpcTofNSigmaMuNeg(), + candJpsi.tpcNSigmaElPos(), + candJpsi.tofNSigmaElPos(), + candJpsi.tpcTofNSigmaElPos(), + candJpsi.tpcNSigmaElNeg(), + candJpsi.tofNSigmaElNeg(), + candJpsi.tpcTofNSigmaElNeg(), // kaon features - candidate.ptProng1(), + candidate.ptProng2(), + candidate.impactParameter2(), candKa.itsNCls(), candKa.tpcNClsCrossedRows(), candKa.itsChi2NCl(), @@ -489,7 +534,7 @@ struct HfTaskBplusToJpsiKReduced { // Process functions void processData(aod::HfRedCandBplusToJpsiK const& candidates, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaonTracks) { for (const auto& candidate : candidates) { @@ -502,7 +547,7 @@ struct HfTaskBplusToJpsiKReduced { PROCESS_SWITCH(HfTaskBplusToJpsiKReduced, processData, "Process data without ML for B+", true); void processDataWithBplusMl(aod::HfRedCandBplusToJpsiK const& candidates, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaonTracks) { for (const auto& candidate : candidates) { @@ -516,7 +561,7 @@ struct HfTaskBplusToJpsiKReduced { void processMc(soa::Join const& candidates, aod::HfMcGenRedBps const& mcParticles, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaonTracks) { // MC rec @@ -536,7 +581,7 @@ struct HfTaskBplusToJpsiKReduced { void processMcWithBplusMl(soa::Join const& candidates, aod::HfMcGenRedBps const& mcParticles, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaonTracks) { // MC rec diff --git a/PWGHF/D2H/Tasks/taskBs.cxx b/PWGHF/D2H/Tasks/taskBs.cxx index 46b4dacec14..b6635bf670a 100644 --- a/PWGHF/D2H/Tasks/taskBs.cxx +++ b/PWGHF/D2H/Tasks/taskBs.cxx @@ -79,7 +79,7 @@ struct HfTaskBs { void init(InitContext const&) { static const AxisSpec axisMassBs = {300, 4.5, 6.0, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; registry.add("hEta", "B^{0}_{s} candidates;B^{0}_{s} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, axisPt}}); registry.add("hRapidity", "B^{0}_{s} candidates;B^{0}_{s} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, axisPt}}); diff --git a/PWGHF/D2H/Tasks/taskBsReduced.cxx b/PWGHF/D2H/Tasks/taskBsReduced.cxx index 703a527ffac..d304fe782c4 100644 --- a/PWGHF/D2H/Tasks/taskBsReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsReduced.cxx @@ -79,12 +79,18 @@ DECLARE_SOA_COLUMN(TpcChi2NClDmesProngMax, tpcChi2NClDmesProngMax, float); DECLARE_SOA_COLUMN(NSigTpcPiDmesProng0, nSigTpcPiDmesProng0, float); //! TPC Nsigma separation for D-meson prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiDmesProng0, nSigTofPiDmesProng0, float); //! TOF Nsigma separation for D-meson prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng0, nSigTpcTofPiDmesProng0, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcKaDmesProng0, nSigTpcKaDmesProng0, float); //! TPC Nsigma separation for D-meson prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaDmesProng0, nSigTofKaDmesProng0, float); //! TOF Nsigma separation for D-meson prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng0, nSigTpcTofKaDmesProng0, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcKaDmesProng1, nSigTpcKaDmesProng1, float); //! TPC Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofKaDmesProng1, nSigTofKaDmesProng1, float); //! TOF Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng1, nSigTpcTofKaDmesProng1, float); //! Combined TPC and TOF Nsigma separation for D-meson prong1 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcPiDmesProng2, nSigTpcPiDmesProng2, float); //! TPC Nsigma separation for D-meson prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiDmesProng2, nSigTofPiDmesProng2, float); //! TOF Nsigma separation for D-meson prong2 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng2, nSigTpcTofPiDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiDmesProng2, nSigTpcTofPiDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong2 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcKaDmesProng2, nSigTpcKaDmesProng2, float); //! TPC Nsigma separation for D-meson prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTofKaDmesProng2, nSigTofKaDmesProng2, float); //! TOF Nsigma separation for D-meson prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofKaDmesProng2, nSigTpcTofKaDmesProng2, float); //! Combined TPC and TOF Nsigma separation for D-meson prong2 with kaon mass hypothesis // pion features DECLARE_SOA_COLUMN(PtProng1, ptProng1, float); //! Transverse momentum of prong1 (GeV/c) DECLARE_SOA_COLUMN(AbsEtaProng1, absEtaProng1, float); //! Absolute pseudorapidity of Prong1 @@ -135,12 +141,18 @@ DECLARE_SOA_TABLE(HfRedCandBsLites, "AOD", "HFREDCANDBSLITE", //! Table with som hf_cand_bs_lite::NSigTpcPiDmesProng0, hf_cand_bs_lite::NSigTofPiDmesProng0, hf_cand_bs_lite::NSigTpcTofPiDmesProng0, + hf_cand_bs_lite::NSigTpcKaDmesProng0, + hf_cand_bs_lite::NSigTofKaDmesProng0, + hf_cand_bs_lite::NSigTpcTofKaDmesProng0, hf_cand_bs_lite::NSigTpcKaDmesProng1, hf_cand_bs_lite::NSigTofKaDmesProng1, hf_cand_bs_lite::NSigTpcTofKaDmesProng1, hf_cand_bs_lite::NSigTpcPiDmesProng2, hf_cand_bs_lite::NSigTofPiDmesProng2, hf_cand_bs_lite::NSigTpcTofPiDmesProng2, + hf_cand_bs_lite::NSigTpcKaDmesProng2, + hf_cand_bs_lite::NSigTofKaDmesProng2, + hf_cand_bs_lite::NSigTpcTofKaDmesProng2, hf_cand_bs_reduced::Prong0MlScoreBkg, hf_cand_bs_reduced::Prong0MlScorePrompt, hf_cand_bs_reduced::Prong0MlScoreNonprompt, @@ -596,9 +608,15 @@ struct HfTaskBsReduced { candDs.tpcNSigmaPiProng0(), candDs.tofNSigmaPiProng0(), candDs.tpcTofNSigmaPiProng0(), + candDs.tpcNSigmaKaProng0(), + candDs.tofNSigmaKaProng0(), + candDs.tpcTofNSigmaKaProng0(), candDs.tpcNSigmaKaProng1(), candDs.tofNSigmaKaProng1(), candDs.tpcTofNSigmaKaProng1(), + candDs.tpcNSigmaPiProng2(), + candDs.tofNSigmaPiProng2(), + candDs.tpcTofNSigmaPiProng2(), candDs.tpcNSigmaKaProng2(), candDs.tofNSigmaKaProng2(), candDs.tpcTofNSigmaKaProng2(), diff --git a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx index ab44c6f7dbe..86d6a7cfbab 100644 --- a/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx +++ b/PWGHF/D2H/Tasks/taskBsToJpsiPhiReduced.cxx @@ -65,6 +65,8 @@ namespace o2::aod namespace hf_cand_bstojpsiphi_lite { DECLARE_SOA_COLUMN(PtJpsi, ptJpsi, float); //! Transverse momentum of Jpsi daughter candidate (GeV/c) +DECLARE_SOA_COLUMN(PtJpsiDauPos, ptJpsiDauPos, float); //! Transverse momentum of Jpsi positive daughter candidate (GeV/c) +DECLARE_SOA_COLUMN(PtJpsiDauNeg, ptJpsiDauNeg, float); //! Transverse momentum of Jpsi negative daughter candidate (GeV/c) DECLARE_SOA_COLUMN(PtBach0, ptBach0, float); //! Transverse momentum of bachelor kaon(<- phi) (GeV/c) DECLARE_SOA_COLUMN(PtBach1, ptBach1, float); //! Transverse momentum of bachelor kaon(<- phi) (GeV/c) DECLARE_SOA_COLUMN(ItsNClsJpsiDauPos, itsNClsJpsiDauPos, int); //! Number of clusters in ITS @@ -109,6 +111,12 @@ DECLARE_SOA_COLUMN(NSigTpcTofMuJpsiDauPos, nSigTpcTofMuJpsiDauPos, float); DECLARE_SOA_COLUMN(NSigTpcMuJpsiDauNeg, nSigTpcMuJpsiDauNeg, float); //! TPC Nsigma separation for Jpsi DauNeg with muon mass hypothesis DECLARE_SOA_COLUMN(NSigTofMuJpsiDauNeg, nSigTofMuJpsiDauNeg, float); //! TOF Nsigma separation for Jpsi DauNeg with muon mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofMuJpsiDauNeg, nSigTpcTofMuJpsiDauNeg, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong1 with muon mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcElJpsiDauPos, nSigTpcElJpsiDauPos, float); //! TPC Nsigma separation for Jpsi DauPos with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTofElJpsiDauPos, nSigTofElJpsiDauPos, float); //! TOF Nsigma separation for Jpsi DauPos with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofElJpsiDauPos, nSigTpcTofElJpsiDauPos, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong0 with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcElJpsiDauNeg, nSigTpcElJpsiDauNeg, float); //! TPC Nsigma separation for Jpsi DauNeg with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTofElJpsiDauNeg, nSigTofElJpsiDauNeg, float); //! TOF Nsigma separation for Jpsi DauNeg with electron mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofElJpsiDauNeg, nSigTpcTofElJpsiDauNeg, float); //! Combined TPC and TOF Nsigma separation for Jpsi prong1 with electron mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -138,6 +146,7 @@ DECLARE_SOA_TABLE(HfRedCandBsLites, "AOD", "HFREDCANDBSLITE", //! Table with som hf_cand_bstojpsiphi_lite::Y, hf_cand_bstojpsiphi_lite::Cpa, hf_cand_bstojpsiphi_lite::CpaXY, + hf_cand_bstojpsiphi_lite::CpaJpsi, hf_cand::Chi2PCA, hf_cand_bstojpsiphi_lite::DecayLength, hf_cand_bstojpsiphi_lite::DecayLengthXY, @@ -159,17 +168,31 @@ DECLARE_SOA_TABLE(HfRedCandBsLites, "AOD", "HFREDCANDBSLITE", //! Table with som hf_cand_bstojpsiphi_lite::ImpactParameterLfTrack0, hf_cand_bstojpsiphi_lite::ImpactParameterLfTrack1, // Jpsi daughter features + hf_cand_bstojpsiphi_lite::PtJpsiDauPos, hf_cand_bstojpsiphi_lite::ItsNClsJpsiDauPos, hf_cand_bstojpsiphi_lite::TpcNClsCrossedRowsJpsiDauPos, hf_cand_bstojpsiphi_lite::ItsChi2NClJpsiDauPos, hf_cand_bstojpsiphi_lite::TpcChi2NClJpsiDauPos, hf_cand_bstojpsiphi_lite::AbsEtaJpsiDauPos, + hf_cand_bstojpsiphi_lite::PtJpsiDauNeg, hf_cand_bstojpsiphi_lite::ItsNClsJpsiDauNeg, hf_cand_bstojpsiphi_lite::TpcNClsCrossedRowsJpsiDauNeg, hf_cand_bstojpsiphi_lite::ItsChi2NClJpsiDauNeg, hf_cand_bstojpsiphi_lite::TpcChi2NClJpsiDauNeg, hf_cand_bstojpsiphi_lite::AbsEtaJpsiDauNeg, - // kaon features + hf_cand_bstojpsiphi_lite::NSigTpcMuJpsiDauPos, + hf_cand_bstojpsiphi_lite::NSigTofMuJpsiDauPos, + hf_cand_bstojpsiphi_lite::NSigTpcTofMuJpsiDauPos, + hf_cand_bstojpsiphi_lite::NSigTpcMuJpsiDauNeg, + hf_cand_bstojpsiphi_lite::NSigTofMuJpsiDauNeg, + hf_cand_bstojpsiphi_lite::NSigTpcTofMuJpsiDauNeg, + hf_cand_bstojpsiphi_lite::NSigTpcElJpsiDauPos, + hf_cand_bstojpsiphi_lite::NSigTofElJpsiDauPos, + hf_cand_bstojpsiphi_lite::NSigTpcTofElJpsiDauPos, + hf_cand_bstojpsiphi_lite::NSigTpcElJpsiDauNeg, + hf_cand_bstojpsiphi_lite::NSigTofElJpsiDauNeg, + hf_cand_bstojpsiphi_lite::NSigTpcTofElJpsiDauNeg, + // phi features hf_cand_bstojpsiphi_lite::PtBach0, hf_cand_bstojpsiphi_lite::ItsNClsLfTrack0, hf_cand_bstojpsiphi_lite::TpcNClsCrossedRowsLfTrack0, @@ -213,10 +236,10 @@ DECLARE_SOA_TABLE(HfRedCandBsLites, "AOD", "HFREDCANDBSLITE", //! Table with som // string definitions, used for histogram axis labels const TString stringPt = "#it{p}_{T} (GeV/#it{c})"; const TString stringPtJpsi = "#it{p}_{T}(Jpsi) (GeV/#it{c});"; -const TString bSCandTitle = "B_{s}^{0} candidates;"; +const TString bsCandTitle = "B_{s}^{0} candidates;"; const TString entries = "entries"; -const TString bSCandMatch = "B_{s}^{0} candidates (matched);"; -const TString bSCandUnmatch = "B_{s}^{0} candidates (unmatched);"; +const TString bsCandMatch = "B_{s}^{0} candidates (matched);"; +const TString bsCandUnmatch = "B_{s}^{0} candidates (unmatched);"; const TString mcParticleMatched = "MC particles (matched);"; /// Bs analysis task @@ -232,6 +255,8 @@ struct HfTaskBsToJpsiPhiReduced { Configurable fillBackground{"fillBackground", false, "Flag to enable filling of background histograms/sparses/tree (only MC)"}; Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; + Configurable useJpsiPdgMass{"useJpsiPdgMass", true, "Whether to use J/Psi PDG mass for B+ candidate mass evaluation or to use the invariant mass of the two prongs"}; + Configurable usePhiPdgMass{"usePhiPdgMass", true, "Whether to use Phi PDG mass for B+ candidate mass evaluation or to use the invariant mass of the two prongs"}; // topological cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_bs_to_jpsi_phi::vecBinsPt}, "pT bin limits"}; Configurable> cuts{"cuts", {hf_cuts_bs_to_jpsi_phi::Cuts[0], hf_cuts_bs_to_jpsi_phi::NBinsPt, hf_cuts_bs_to_jpsi_phi::NCutVars, hf_cuts_bs_to_jpsi_phi::labelsPt, hf_cuts_bs_to_jpsi_phi::labelsCutVar}, "Bs candidate selection per pT bin"}; @@ -266,6 +291,7 @@ struct HfTaskBsToJpsiPhiReduced { o2::ccdb::CcdbApi ccdbApi; using TracksKaon = soa::Join; + using JPsis = soa::Join; std::vector outputMl; // Filter filterSelectCandidates = (aod::hf_sel_candidate_bplus::isSelBsToJpsiPi >= selectionFlagBs); @@ -303,14 +329,12 @@ struct HfTaskBsToJpsiPhiReduced { const AxisSpec axisImpactPar{200, -0.05, 0.05}; const AxisSpec axisPtJpsi{100, 0., 50.}; const AxisSpec axisRapidity{100, -2., 2.}; - const AxisSpec axisPtB{(std::vector)binsPt, "#it{p}_{T}^{B_{s}^{0}} (GeV/#it{c})"}; - const AxisSpec axisPtKa{100, 0.f, 10.f}; + const AxisSpec axisPtB{std::vector(binsPt), "#it{p}_{T}^{B_{s}^{0}} (GeV/#it{c})"}; const AxisSpec axisPtPhi{100, 0.f, 10.f}; - registry.add("hMass", bSCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); - registry.add("hMassJpsi", bSCandTitle + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); - registry.add("hMassPhi", bSCandTitle + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); - registry.add("hd0K", bSCandTitle + "Kaon DCAxy to prim. vertex (cm);" + stringPt, {HistType::kTH2F, {axisImpactPar, axisPtKa}}); + registry.add("hMass", bsCandTitle + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); + registry.add("hMassJpsi", bsCandTitle + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); + registry.add("hMassPhi", bsCandTitle + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2});" + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); // histograms processMC if (doprocessMc || doprocessMcWithBsMl) { @@ -318,14 +342,12 @@ struct HfTaskBsToJpsiPhiReduced { registry.add("hPtPhiGen", mcParticleMatched + "#phi #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hPtKGen", mcParticleMatched + "Kaon #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisPtProng, axisPtB}}); registry.add("hYGenWithProngsInAcceptance", mcParticleMatched + "B_{s}^{0} #it{p}_{T}^{gen} (GeV/#it{c}); B_{s}^{0} #it{y}", {HistType::kTH2F, {axisPtProng, axisRapidity}}); - registry.add("hMassRecSig", bSCandMatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); - registry.add("hMassJpsiRecSig", bSCandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); - registry.add("hMassPhiRecSig", bSCandMatch + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2}); #phi " + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); - registry.add("hd0KRecSig", bSCandMatch + "Kaon DCAxy to prim. vertex (cm); K^{+} " + stringPt, {HistType::kTH2F, {axisImpactPar, axisPtKa}}); - registry.add("hMassRecBg", bSCandUnmatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); - registry.add("hMassJpsiRecBg", bSCandUnmatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); - registry.add("hMassPhiRecBg", bSCandMatch + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2}); #phi " + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); - registry.add("hd0KRecBg", bSCandMatch + "Kaon DCAxy to prim. vertex (cm); K^{+} " + stringPt, {HistType::kTH2F, {axisImpactPar, axisPtKa}}); + registry.add("hMassRecSig", bsCandMatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); + registry.add("hMassJpsiRecSig", bsCandMatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); + registry.add("hMassPhiRecSig", bsCandMatch + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2}); #phi " + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); + registry.add("hMassRecBg", bsCandUnmatch + "inv. mass J/#Psi K^{+} (GeV/#it{c}^{2}); B_{s}^{0} " + stringPt, {HistType::kTH2F, {axisMassBs, axisPtB}}); + registry.add("hMassJpsiRecBg", bsCandUnmatch + "inv. mass #mu^{+}#mu^{#minus} (GeV/#it{c}^{2}); J/#Psi " + stringPt, {HistType::kTH2F, {axisMassJpsi, axisPtJpsi}}); + registry.add("hMassPhiRecBg", bsCandUnmatch + "inv. mass K^{+}K^{#minus} (GeV/#it{c}^{2}); #phi " + stringPt, {HistType::kTH2F, {axisMassPhi, axisPtPhi}}); } if (doprocessDataWithBsMl || doprocessMcWithBsMl) { @@ -366,19 +388,24 @@ struct HfTaskBsToJpsiPhiReduced { /// \param candidatesJpsi is the table with Jpsi candidates template void fillCand(Cand const& candidate, - aod::HfRedJpsis const& /*candidatesJpsi*/, + JPsis const& /*candidatesJpsi*/, aod::HfRedBach0Tracks const&, aod::HfRedBach1Tracks const&) { auto ptCandBs = candidate.pt(); - auto invMassBs = HfHelper::invMassBsToJpsiPhi(candidate); - auto candJpsi = candidate.template jpsi_as(); + auto invMassBs = HfHelper::invMassBsToJpsiPhi(candidate, useJpsiPdgMass, usePhiPdgMass); + auto candJpsi = candidate.template jpsi_as(); auto candKa0 = candidate.template prong0Phi_as(); auto candKa1 = candidate.template prong1Phi_as(); - std::array const pVecKa0 = {candKa0.px(), candKa0.py(), candKa0.pz()}; - std::array const pVecKa1 = {candKa1.px(), candKa1.py(), candKa1.pz()}; - auto ptJpsi = candidate.ptProng0(); - auto invMassJpsi = candJpsi.m(); + auto const pVecMu0 = candidate.pVectorProng0(); + auto const pVecMu1 = candidate.pVectorProng1(); + auto const pVecKa0 = candidate.pVectorProng2(); + auto const pVecKa1 = candidate.pVectorProng3(); + auto ptJpsi = RecoDecay::pt(pVecMu0, pVecMu1); + auto ptMuPos = RecoDecay::pt(pVecMu0); + auto ptMuNeg = RecoDecay::pt(pVecMu1); + auto ptPhi = RecoDecay::pt(pVecKa0, pVecKa1); + auto invMassJpsi = RecoDecay::m(std::array{pVecMu0, pVecMu1}, std::array{o2::constants::physics::MassMuonPlus, o2::constants::physics::MassMuonMinus}); auto invMassPhi = RecoDecay::m(std::array{pVecKa0, pVecKa1}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassKPlus}); uint8_t statusBs = 0; @@ -388,12 +415,12 @@ struct HfTaskBsToJpsiPhiReduced { flagMcMatchRec = candidate.flagMcMatchRec(); flagMcDecayChanRec = candidate.flagMcDecayChanRec(); flagWrongCollision = candidate.flagWrongCollision(); - isSignal = flagMcMatchRec == o2::hf_decay::hf_cand_beauty::BsToJpsiKK && + isSignal = std::abs(flagMcMatchRec) == o2::hf_decay::hf_cand_beauty::BsToJpsiKKToMuMuKK && flagMcDecayChanRec == o2::hf_decay::hf_cand_beauty::BsToJpsiPhi; } SETBIT(statusBs, SelectionStep::RecoSkims); - if (HfHelper::selectionBsToJpsiPhiTopol(candidate, candKa0, candKa1, cuts, binsPt)) { + if (HfHelper::selectionBsToJpsiPhiTopol(candidate, cuts, binsPt, useJpsiPdgMass, usePhiPdgMass)) { SETBIT(statusBs, SelectionStep::RecoTopol); } else if (selectionFlagBs >= BIT(SelectionStep::RecoTopol) * 2 - 1) { return; @@ -432,18 +459,15 @@ struct HfTaskBsToJpsiPhiReduced { } registry.fill(HIST("hMass"), invMassBs, ptCandBs); - registry.fill(HIST("hMassJpsi"), invMassJpsi, candidate.ptProng0()); - registry.fill(HIST("hMassPhi"), invMassPhi, candidate.ptProng0()); - registry.fill(HIST("hd0K"), candidate.impactParameter1(), candidate.ptProng1()); + registry.fill(HIST("hMassJpsi"), invMassJpsi, ptJpsi); + registry.fill(HIST("hMassPhi"), invMassPhi, ptPhi); if constexpr (DoMc) { if (isSignal) { registry.fill(HIST("hMassRecSig"), invMassBs, ptCandBs); - registry.fill(HIST("hMassJpsiRecSig"), invMassJpsi, candidate.ptProng0()); - registry.fill(HIST("hd0KRecSig"), candidate.impactParameter1(), candidate.ptProng1()); + registry.fill(HIST("hMassJpsiRecSig"), invMassJpsi, ptJpsi); } else if (fillBackground) { registry.fill(HIST("hMassRecBg"), invMassBs, ptCandBs); - registry.fill(HIST("hMassJpsiRecBg"), invMassJpsi, candidate.ptProng0()); - registry.fill(HIST("hd0KRecBg"), candidate.impactParameter1(), candidate.ptProng1()); + registry.fill(HIST("hMassJpsiRecBg"), invMassJpsi, ptJpsi); } } @@ -463,6 +487,7 @@ struct HfTaskBsToJpsiPhiReduced { HfHelper::yBs(candidate), candidate.cpa(), candidate.cpaXY(), + candidate.cpaJpsi(), candidate.chi2PCA(), candidate.decayLength(), candidate.decayLengthXY(), @@ -482,16 +507,30 @@ struct HfTaskBsToJpsiPhiReduced { candidate.impactParameter1(), candidate.impactParameter2(), candidate.impactParameter3(), + ptMuPos, candJpsi.itsNClsDauPos(), candJpsi.tpcNClsCrossedRowsDauPos(), candJpsi.itsChi2NClDauPos(), candJpsi.tpcChi2NClDauPos(), absEta(candJpsi.tglDauPos()), + ptMuNeg, candJpsi.itsNClsDauNeg(), candJpsi.tpcNClsCrossedRowsDauNeg(), candJpsi.itsChi2NClDauNeg(), candJpsi.tpcChi2NClDauNeg(), absEta(candJpsi.tglDauNeg()), + candJpsi.tpcNSigmaMuPos(), + candJpsi.tofNSigmaMuPos(), + candJpsi.tpcTofNSigmaMuPos(), + candJpsi.tpcNSigmaMuNeg(), + candJpsi.tofNSigmaMuNeg(), + candJpsi.tpcTofNSigmaMuNeg(), + candJpsi.tpcNSigmaElPos(), + candJpsi.tofNSigmaElPos(), + candJpsi.tpcTofNSigmaElPos(), + candJpsi.tpcNSigmaElNeg(), + candJpsi.tofNSigmaElNeg(), + candJpsi.tpcTofNSigmaElNeg(), // kaon features candKa0.pt(), candKa0.itsNCls(), @@ -544,7 +583,7 @@ struct HfTaskBsToJpsiPhiReduced { // Process functions void processData(aod::HfRedCandBsToJpsiPhi const& candidates, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaon0Tracks, aod::HfRedBach1Tracks const& kaon1Tracks) { @@ -558,7 +597,7 @@ struct HfTaskBsToJpsiPhiReduced { PROCESS_SWITCH(HfTaskBsToJpsiPhiReduced, processData, "Process data without ML for Bs", true); void processDataWithBsMl(aod::HfRedCandBsToJpsiPhi const& candidates, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaon0Tracks, aod::HfRedBach1Tracks const& kaon1Tracks) { @@ -573,7 +612,7 @@ struct HfTaskBsToJpsiPhiReduced { void processMc(soa::Join const& candidates, aod::HfMcGenRedBss const& mcParticles, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaon0Tracks, aod::HfRedBach1Tracks const& kaon1Tracks) { @@ -594,7 +633,7 @@ struct HfTaskBsToJpsiPhiReduced { void processMcWithBsMl(soa::Join const& candidates, aod::HfMcGenRedBss const& mcParticles, - aod::HfRedJpsis const& candidatesJpsi, + JPsis const& candidatesJpsi, aod::HfRedBach0Tracks const& kaon0Tracks, aod::HfRedBach1Tracks const& kaon1Tracks) { diff --git a/PWGHF/D2H/Tasks/taskCd.cxx b/PWGHF/D2H/Tasks/taskCd.cxx index af4e5960cd9..6e35eb255c3 100644 --- a/PWGHF/D2H/Tasks/taskCd.cxx +++ b/PWGHF/D2H/Tasks/taskCd.cxx @@ -10,23 +10,27 @@ // or submit itself to any jurisdiction. /// \file taskCd.cxx -/// \brief Cd± → d± K∓ π± analysis task +/// \brief cd± → d± K∓ π± analysis task /// \author Biao Zhang , Heidelberg Universiity #include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include @@ -40,6 +44,7 @@ #include #include +#include #include @@ -48,6 +53,7 @@ #include #include #include +#include #include // std::vector using namespace o2; @@ -63,45 +69,73 @@ namespace o2::aod namespace full { // Candidate kinematics -DECLARE_SOA_COLUMN(MassCd, massCd, float); //! Invariant mass of cd candidate (GeV/c^2) -DECLARE_SOA_COLUMN(MassLc, massLc, float); //! Invariant mass of lc candidate (GeV/c^2) -DECLARE_SOA_COLUMN(Pt, pt, float); //! Transverse momentum of candidate (GeV/c) -DECLARE_SOA_COLUMN(Eta, eta, float); //! eta of candidate (GeV/c) -DECLARE_SOA_COLUMN(Phi, phi, float); //! phi of candidate (GeV/c) -DECLARE_SOA_COLUMN(PtProng0, ptProng0, float); //! Transverse momentum of prong 0 (GeV/c) -DECLARE_SOA_COLUMN(PtProng1, ptProng1, float); //! Transverse momentum of prong 1 (GeV/c) -DECLARE_SOA_COLUMN(PtProng2, ptProng2, float); //! Transverse momentum of prong 2 (GeV/c) -DECLARE_SOA_COLUMN(ImpactParameter0, impactParameter0, float); //! Impact parameter (DCA to PV) of prong 0 (cm) -DECLARE_SOA_COLUMN(ImpactParameter1, impactParameter1, float); //! Impact parameter (DCA to PV) of prong 1 (cm) -DECLARE_SOA_COLUMN(ImpactParameter2, impactParameter2, float); //! Impact parameter (DCA to PV) of prong 2 (cm) -DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length (3D) of candidate (cm) -DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Decay length in transverse plane (cm) -DECLARE_SOA_COLUMN(Cpa, cpa, float); //! Cosine of pointing angle (3D) -DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! Cosine of pointing angle in XY plane -DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); //! chi2PCA -DECLARE_SOA_COLUMN(NSigmaTpcDe, nSigmaTpcDe, float); //! TPC nσ for deuteron hypothesis -DECLARE_SOA_COLUMN(NSigmaTpcPr, nSigmaTpcPr, float); //! TPC nσ for proton hypothesis -DECLARE_SOA_COLUMN(NSigmaTpcKa, nSigmaTpcKa, float); //! TPC nσ for kaon hypothesis -DECLARE_SOA_COLUMN(NSigmaTpcPi, nSigmaTpcPi, float); //! TPC nσ for pion hypothesis -DECLARE_SOA_COLUMN(NSigmaItsDe, nSigmaItsDe, float); //! ITS nσ for deuteron hypothesis -DECLARE_SOA_COLUMN(NSigmaTofDe, nSigmaTofDe, float); //! TOF nσ for deuteron hypothesis -DECLARE_SOA_COLUMN(NSigmaTofKa, nSigmaTofKa, float); //! TOF nσ for kaon hypothesis -DECLARE_SOA_COLUMN(NSigmaTofPi, nSigmaTofPi, float); //! TOF nσ for pion hypothesis -DECLARE_SOA_COLUMN(NItsClusters, nItsClusters, float); //! Number of ITS clusters used in the track fit -DECLARE_SOA_COLUMN(NItsNClusterSize, nItsNClusterSize, float); //! Number of ITS clusters size used in the track fit -DECLARE_SOA_COLUMN(NTpcClusters, nTpcClusters, float); //! Number of TPC clusters used in the track fit -DECLARE_SOA_COLUMN(NTpcSignalsDe, nTpcSignalsDe, float); //! Number of TPC signas for deuteron -DECLARE_SOA_COLUMN(NTpcSignalsPi, nTpcSignalsPi, float); //! Number of TPC signas for pion -DECLARE_SOA_COLUMN(NTpcSignalsKa, nTpcSignalsKa, float); //! Number of TPC signas for kaon -DECLARE_SOA_COLUMN(NItsSignalsDe, nItsSignalsDe, float); //! Number of ITS signas -DECLARE_SOA_COLUMN(CandidateSelFlag, candidateSelFlag, int8_t); //! Candidates falg -DECLARE_SOA_COLUMN(Cent, cent, float); //! Centrality -DECLARE_SOA_COLUMN(GIndexCol, gIndexCol, int); //! Global index for the collisionAdd commentMore actions -DECLARE_SOA_COLUMN(TimeStamp, timeStamp, int64_t); //! Timestamp for the collision +DECLARE_SOA_COLUMN(MassCd, massCd, float); //! Invariant mass of cd candidate (GeV/c^2) +DECLARE_SOA_COLUMN(MassLc, massLc, float); //! Invariant mass under the p K pi hypothesis (GeV/c^2) +DECLARE_SOA_COLUMN(Pt, pt, float); //! Transverse momentum of candidate (GeV/c) +DECLARE_SOA_COLUMN(Eta, eta, float); //! eta of candidate (GeV/c) +DECLARE_SOA_COLUMN(Phi, phi, float); //! phi of candidate (GeV/c) +DECLARE_SOA_COLUMN(Y, y, float); //! rapidity of generated particle +DECLARE_SOA_COLUMN(PtProng0, ptProng0, float); //! Transverse momentum of prong 0 (GeV/c) +DECLARE_SOA_COLUMN(PxProng0, pxProng0, float); //! Px of prong 0 (GeV/c) +DECLARE_SOA_COLUMN(PyProng0, pyProng0, float); //! Py of prong 0 (GeV/c) +DECLARE_SOA_COLUMN(PzProng0, pzProng0, float); //! Pz of prong 0 (GeV/c) +DECLARE_SOA_COLUMN(PtProng1, ptProng1, float); //! Transverse momentum of prong 1 (GeV/c) +DECLARE_SOA_COLUMN(PxProng1, pxProng1, float); //! Px of prong 1 (GeV/c) +DECLARE_SOA_COLUMN(PyProng1, pyProng1, float); //! Py of prong 1 (GeV/c) +DECLARE_SOA_COLUMN(PzProng1, pzProng1, float); //! Pz of prong 1 (GeV/c) +DECLARE_SOA_COLUMN(PtProng2, ptProng2, float); //! Transverse momentum of prong 2 (GeV/c) +DECLARE_SOA_COLUMN(PxProng2, pxProng2, float); //! Px of prong 2 (GeV/c) +DECLARE_SOA_COLUMN(PyProng2, pyProng2, float); //! Py of prong 2 (GeV/c) +DECLARE_SOA_COLUMN(PzProng2, pzProng2, float); //! Pz of prong 2 (GeV/c) +DECLARE_SOA_COLUMN(ImpactParameter0, impactParameter0, float); //! Impact parameter (DCA to PV) of prong 0 (cm) +DECLARE_SOA_COLUMN(ImpactParameter1, impactParameter1, float); //! Impact parameter (DCA to PV) of prong 1 (cm) +DECLARE_SOA_COLUMN(ImpactParameter2, impactParameter2, float); //! Impact parameter (DCA to PV) of prong 2 (cm) +DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length (3D) of candidate (cm) +DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Decay length in transverse plane (cm) +DECLARE_SOA_COLUMN(Cpa, cpa, float); //! Cosine of pointing angle (3D) +DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! Cosine of pointing angle in XY plane +DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); //! chi2PCA +DECLARE_SOA_COLUMN(NSigmaTpcDe, nSigmaTpcDe, float); //! TPC nσ for deuteron hypothesis +DECLARE_SOA_COLUMN(NSigmaTpcPr, nSigmaTpcPr, float); //! TPC nσ for proton hypothesis +DECLARE_SOA_COLUMN(NSigmaTpcKa, nSigmaTpcKa, float); //! TPC nσ for kaon hypothesis +DECLARE_SOA_COLUMN(NSigmaTpcPi, nSigmaTpcPi, float); //! TPC nσ for pion hypothesis +DECLARE_SOA_COLUMN(NSigmaItsDe, nSigmaItsDe, float); //! ITS nσ for deuteron hypothesis +DECLARE_SOA_COLUMN(NSigmaTofDe, nSigmaTofDe, float); //! TOF nσ for deuteron hypothesis +DECLARE_SOA_COLUMN(TofBetaDe, tofBetaDe, float); //! TOF beta for deuteron candidate +DECLARE_SOA_COLUMN(TpcInnerParamDe, tpcInnerParamDe, float); //! TPC inner-wall momentum for deuteron candidate (GeV/c) +DECLARE_SOA_COLUMN(TofExpMomDe, tofExpMomDe, float); //! TOF expected momentum for deuteron candidate (GeV/c) +DECLARE_SOA_COLUMN(NSigmaTofKa, nSigmaTofKa, float); //! TOF nσ for kaon hypothesis +DECLARE_SOA_COLUMN(NSigmaTofPi, nSigmaTofPi, float); //! TOF nσ for pion hypothesis +DECLARE_SOA_COLUMN(NItsClusters, nItsClusters, float); //! Number of ITS clusters used in the track fit +DECLARE_SOA_COLUMN(NItsNClusterSize, nItsNClusterSize, float); //! Number of ITS clusters size used in the track fit +DECLARE_SOA_COLUMN(NTpcClusters, nTpcClusters, float); //! Number of TPC clusters used in the track fit +DECLARE_SOA_COLUMN(TpcNClsFoundDe, tpcNClsFoundDe, uint16_t); //! Number of found TPC clusters for the selected deuteron prong +DECLARE_SOA_COLUMN(NTpcSignalsDe, nTpcSignalsDe, float); //! Number of TPC signas for deuteron +DECLARE_SOA_COLUMN(NTpcSignalsPi, nTpcSignalsPi, float); //! Number of TPC signas for pion +DECLARE_SOA_COLUMN(NTpcSignalsKa, nTpcSignalsKa, float); //! Number of TPC signas for kaon +DECLARE_SOA_COLUMN(NItsSignalsDe, nItsSignalsDe, float); //! Number of ITS signas +DECLARE_SOA_COLUMN(CandidateSelFlag, candidateSelFlag, int8_t); //! Candidates falg +DECLARE_SOA_COLUMN(CandidateSign, candidateSign, int8_t); //! Candidates sign +DECLARE_SOA_COLUMN(FlagMc, flagMc, int8_t); //! Main MC decay-channel flag; 0 for unmatched candidates +DECLARE_SOA_COLUMN(IsCandidateSwapped, isCandidateSwapped, int8_t); //! MC-matched prong permutation; -1 for data +DECLARE_SOA_COLUMN(HypothesisMask, hypothesisMask, uint8_t); //! Bit 0: DeKPi selected; bit 1: PiKDe selected +DECLARE_SOA_COLUMN(CandidateGlobalIndex, candidateGlobalIndex, int64_t); //! Input candidate index, shared by rows from the same candidate +DECLARE_SOA_COLUMN(OriginMcRec, originMcRec, int8_t); //! MC origin for reconstructed candidates +DECLARE_SOA_COLUMN(FlagMcDecayChanRec, flagMcDecayChanRec, int8_t); //! Resonant MC decay channel for reconstructed candidates +DECLARE_SOA_COLUMN(OriginMcGen, originMcGen, int8_t); //! MC origin for generated particles +DECLARE_SOA_COLUMN(FlagMcDecayChanGen, flagMcDecayChanGen, int8_t); //! Resonant MC decay channel for generated candidates +DECLARE_SOA_COLUMN(CtGen, ctGen, float); //! Generated ct computed wrt to c-deuteron production vertex, which can be either PV (prompt) or B-hadron decay vertex (non-prompt) +DECLARE_SOA_COLUMN(CtRec, ctRec, float); //! Reconstructed ct computed wrt to PV +DECLARE_SOA_COLUMN(NumPvContributors, numPvContributors, uint16_t); //! Number of contributors to the primary vertex +DECLARE_SOA_COLUMN(Cent, cent, float); //! Centrality +DECLARE_SOA_COLUMN(VtxZ, vtxZ, float); //! Vertex Z +DECLARE_SOA_COLUMN(GIndexCol, gIndexCol, int); //! Global index for the collision +DECLARE_SOA_COLUMN(McCollisionId, mcCollisionId, int); //! Global index for the MC collision +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, int64_t); //! Timestamp for the collision } // namespace full -// Full table: include ALL columns declared above -DECLARE_SOA_TABLE(HfCandCd, "AOD", "HFCANDCD", +// Lite table +DECLARE_SOA_TABLE(HfCandCdLite, "AOD", "HFCANDCDLITE", full::MassCd, full::MassLc, full::Pt, @@ -120,33 +154,120 @@ DECLARE_SOA_TABLE(HfCandCd, "AOD", "HFCANDCD", full::NSigmaTpcPr, full::NSigmaItsDe, full::NSigmaTofDe, - full::NItsNClusterSize, - full::NTpcSignalsDe, - full::NTpcSignalsPi, - full::NTpcSignalsKa, + full::TofBetaDe, + full::TpcInnerParamDe, + full::TofExpMomDe, + full::CtRec, full::CandidateSelFlag, + full::CandidateSign, + full::FlagMc, + full::IsCandidateSwapped, + full::HypothesisMask, + full::CandidateGlobalIndex, + full::OriginMcRec, + full::FlagMcDecayChanRec, + full::CtGen, full::Cent); + +// full table for local Rotation & Event Mixing +DECLARE_SOA_TABLE(HfCandCdFull, "AOD", "HFCANDCDFULL", + full::PxProng0, + full::PyProng0, + full::PzProng0, + full::PxProng1, + full::PyProng1, + full::PzProng1, + full::PxProng2, + full::PyProng2, + full::PzProng2, + full::ImpactParameter0, + full::ImpactParameter1, + full::ImpactParameter2, + full::DecayLength, + full::Cpa, + full::Chi2PCA, + full::NSigmaTpcDe, + full::NSigmaTpcPr, + full::NSigmaItsDe, + full::NSigmaTofDe, + full::TofBetaDe, + full::TpcInnerParamDe, + full::TofExpMomDe, + full::NSigmaTpcPi, + full::NSigmaTofPi, + full::NSigmaTpcKa, + full::NSigmaTofKa, + full::CtRec, + full::CandidateSelFlag, + full::CandidateSign, + full::FlagMc, + full::IsCandidateSwapped, + full::HypothesisMask, + full::CandidateGlobalIndex, + full::OriginMcRec, + full::FlagMcDecayChanRec, + full::CtGen, + full::NumPvContributors, + full::Cent, + full::VtxZ, + full::GIndexCol, + full::TimeStamp, + full::TpcNClsFoundDe); + +DECLARE_SOA_TABLE(HfCandCdGen, "AOD", "HFCANDCDGEN", + full::Pt, + full::Eta, + full::Phi, + full::Y, + full::FlagMc, + full::OriginMcGen, + full::FlagMcDecayChanGen, + full::CtGen, + full::NumPvContributors, + full::Cent, + full::VtxZ, + full::McCollisionId); } // namespace o2::aod struct HfTaskCd { - Produces rowCandCd; + Produces rowCandCdLite; + Produces rowCandCdFull; + Produces rowCandCdGen; + Configurable selectionFlagCd{"selectionFlagCd", 1, "Selection Flag for Cd"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; Configurable> binsPt{"binsPt", std::vector{hf_cuts_cd_to_de_k_pi::vecBinsPt}, "pT bin limits"}; Configurable fillTHn{"fillTHn", false, "fill THn"}; - Configurable fillTree{"fillTree", false, "Flag to fill candiates tree"}; + Configurable fillCandLiteTree{"fillCandLiteTree", false, "Flag to fill candiates lite tree"}; + Configurable fillCandFullTree{"fillCandFullTree", false, "Flag to fill candiates full tree"}; + Configurable cfgUseTofPidForDeuteron{"cfgUseTofPidForDeuteron", false, "Use TOF PID for deuteron candidates"}; + Configurable cfgCutOnDeuteronDcaOrdering{"cfgCutOnDeuteronDcaOrdering", false, "Require deuteron DCA to be smaller than kaon and pion DCAs"}; + Configurable cfgMinDeuteronDcaPreselection{"cfgMinDeuteronDcaPreselection", 0.004, "Minimum deuteron DCA for preselection (cm)"}; + Configurable cfgMaxDeuteronTofPidPreselection{"cfgMaxDeuteronTofPidPreselection", 5, "Maximum |nSigma TOF| for deuteron preselection"}; + Configurable fillMcCorrelatedBackgrounds{"fillMcCorrelatedBackgrounds", false, "Store selected MC candidates matched to a supported decay channel other than c-deuteron"}; + Configurable fillMcCombinatorialBackground{"fillMcCombinatorialBackground", false, "Store selected MC candidates with flagMcMatchRec equal to zero"}; + Configurable acceptCandidatesWithoutCdFlag{"acceptCandidatesWithoutCdFlag", false, "Accept selected candidates without the Cd skim bit; intended for MC reflection studies"}; SliceCache cache; - using CollisionsWEvSel = soa::Join; - using CollisionsWithEvSelFT0C = soa::Join; - using CollisionsWithEvSelFT0M = soa::Join; + using CollisionsWEvSel = soa::Join; + using CollisionsMc = soa::Join; + using CollisionsWithEvSelFT0C = soa::Join; + using CollisionsMcWithEvSelFT0C = soa::Join; + using CollisionsWithEvSelFT0M = soa::Join; + using CollisionsMcWithEvSelFT0M = soa::Join; using CdCandidates = soa::Filtered>; - using HFTracks = soa::Join; + using CdCandidatesMc = soa::Filtered>; + using McParticles3ProngMatched = soa::Join; + using HFTracks = soa::Join; + using HFTracksMc = soa::Join; Filter filterSelectCandidates = aod::hf_sel_candidate_cd::isSelCdToDeKPi >= selectionFlagCd || aod::hf_sel_candidate_cd::isSelCdToPiKDe >= selectionFlagCd; Preslice candCdPerCollision = aod::hf_cand::collisionId; + PresliceUnsorted colPerMcCollision = aod::mcparticle::mcCollisionId; ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {72, 0, 36}, ""}; ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {400, 2.4, 4.4}, ""}; @@ -155,45 +276,106 @@ struct HfTaskCd { ConfigurableAxis thnConfigAxisDecLength{"thnConfigAxisDecLength", {10, 0, 0.05}, ""}; ConfigurableAxis thnConfigAxisCPA{"thnConfigAxisCPA", {20, 0.8, 1}, ""}; ConfigurableAxis thnConfigAxisCentrality{"thnConfigAxisCentrality", {100, 0, 100}, ""}; + ConfigurableAxis thnAxisRapidity{"thnAxisRapidity", {20, -1, 1}, "Cand. rapidity bins"}; + ConfigurableAxis thnConfigAxisGenPtB{"thnConfigAxisGenPtB", {1000, 0, 100}, "Gen Pt B"}; + ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Number of PV contributors"}; + ConfigurableAxis thnConfigAxisCt{"thnConfigAxisCt", {500, 0., 5000.}, ""}; + + static constexpr std::array SignalFolders = {"signal", "prompt", "nonprompt"}; + static constexpr std::array SignalSuffixes = {"", "Prompt", "NonPrompt"}; + static constexpr float CmToMum = 1.e4f; + + enum SignalClasses : int { + Signal = 0, + Prompt, + NonPrompt + }; HistogramRegistry registry{ "registry", {/// mass candidate - {"Data/hMass", "3-prong candidates;inv. mass (de K #pi) (GeV/#it{c}^{2})", {HistType::kTH1F, {{400, 2.4, 4.4}}}}, + {"Data/hMass", "3-prong candidates;inv. mass (de K #pi) (GeV/#it{c}^{2})", {HistType::kTH1D, {{400, 2.4, 4.4}}}}, /// pT - {"Data/hPt", "3-prong candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}}, - {"Data/hPtProng0", "3-prong candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}}, - {"Data/hPtProng1", "3-prong candidates;prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}}, - {"Data/hPtProng2", "3-prong candidates;prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}}, + {"Data/hPt", "3-prong candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1D, {{360, 0., 36.}}}}, + {"Data/hPtProng0", "3-prong candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1D, {{360, 0., 36.}}}}, + {"Data/hPtProng1", "3-prong candidates;prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1D, {{360, 0., 36.}}}}, + {"Data/hPtProng2", "3-prong candidates;prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1D, {{360, 0., 36.}}}}, /// DCAxy to prim. vertex prongs - {"Data/hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}}, - {"Data/hd0Prong1", "3-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}}, - {"Data/hd0Prong2", "3-prong candidates;prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}}, + {"Data/hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1D, {{600, -0.4, 0.4}}}}, + {"Data/hd0Prong1", "3-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1D, {{600, -0.4, 0.4}}}}, + {"Data/hd0Prong2", "3-prong candidates;prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1D, {{600, -0.4, 0.4}}}}, /// decay length candidate - {"Data/hDecLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}}, + {"Data/hDecLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH1D, {{400, 0., 1.}}}}, /// decay length xy candidate - {"Data/hDecLengthxy", "3-prong candidates;decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}}, + {"Data/hDecLengthxy", "3-prong candidates;decay length xy (cm);entries", {HistType::kTH1D, {{400, 0., 1.}}}}, /// cosine of pointing angle - {"Data/hCPA", "3-prong candidates;cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"Data/hCPA", "3-prong candidates;cosine of pointing angle;entries", {HistType::kTH1D, {{110, -1.1, 1.1}}}}, /// cosine of pointing angle xy - {"Data/hCPAxy", "3-prong candidates;cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}}, + {"Data/hCPAxy", "3-prong candidates;cosine of pointing angle xy;entries", {HistType::kTH1D, {{110, -1.1, 1.1}}}}, /// Chi 2 PCA to sec. vertex - {"Data/hDca2", "3-prong candidates;prong Chi2PCA to sec. vertex (cm);entries", {HistType::kTH1F, {{400, 0., 20.}}}}, + {"Data/hDca2", "3-prong candidates;prong Chi2PCA to sec. vertex (cm);entries", {HistType::kTH1D, {{400, 0., 20.}}}}, /// eta - {"Data/hEta", "3-prong candidates;#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + {"Data/hEta", "3-prong candidates;#it{#eta};entries", {HistType::kTH1D, {{100, -2., 2.}}}}, /// phi - {"Data/hPhi", "3-prong candidates;#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}}}}; + {"Data/hPhi", "3-prong candidates;#it{#Phi};entries", {HistType::kTH1D, {{100, 0., 6.3}}}}}}; HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext&) { - std::array doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M}; + std::array doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M}; if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); } + const bool isData = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M; + + auto addHistogramsRec = [&](const std::string& histoName, const std::string& xAxisTitle, const std::string& yAxisTitle, const HistogramConfigSpec& configSpec) { + if (!isData) { + registry.add(("MC/reconstructed/signal/" + histoName + "RecSig").c_str(), ("3-prong candidates (matched);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); + registry.add(("MC/reconstructed/prompt/" + histoName + "RecSigPrompt").c_str(), ("3-prong candidates (matched, prompt);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); + registry.add(("MC/reconstructed/nonprompt/" + histoName + "RecSigNonPrompt").c_str(), ("3-prong candidates (matched, non-prompt);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); + } + }; + + auto addHistogramsGen = [&](const std::string& histoName, const std::string& xAxisTitle, const std::string& yAxisTitle, const HistogramConfigSpec& configSpec) { + if (!isData) { + registry.add(("MC/generated/signal/" + histoName + "Gen").c_str(), ("MC particles (matched);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); + registry.add(("MC/generated/prompt/" + histoName + "GenPrompt").c_str(), ("MC particles (matched, prompt);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); + registry.add(("MC/generated/nonprompt/" + histoName + "GenNonPrompt").c_str(), ("MC particles (matched, non-prompt);" + xAxisTitle + ";" + yAxisTitle).c_str(), configSpec); + } + }; + + addHistogramsRec("hMass", "inv. mass (de K #pi) (GeV/#it{c}^{2})", "", {HistType::kTH1D, {{400, 2.4, 4.4}}}); + addHistogramsRec("hPt", "#it{p}_{T}^{rec.} (GeV/#it{c})", "entries", {HistType::kTH1D, {{360, 0., 36.}}}); + addHistogramsGen("hPt", "#it{p}_{T}^{gen.} (GeV/#it{c})", "entries", {HistType::kTH1D, {{360, 0., 36.}}}); + if (!isData) { + registry.add("MC/generated/signal/hPtGenSig", "3-prong candidates (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1D, {{360, 0., 36.}}}); + registry.add("MC/reconstructed/allCandidates/hFlagMcMatchRec", "Selected 3-prong candidates;MC decay-channel flag;entries", {HistType::kTH1D, {{49, -24.5, 24.5}}}); + registry.add("MC/reconstructed/allCandidates/hMassCdVsFlagMcMatchRec", "Selected 3-prong candidates;inv. mass (d K #pi) (GeV/#it{c}^{2});MC decay-channel flag", {HistType::kTH2F, {{400, 2.4, 4.4}, {49, -24.5, 24.5}}}); + registry.add("MC/reconstructed/allCandidates/hMassPKPiVsFlagMcMatchRec", "Selected 3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2});MC decay-channel flag", {HistType::kTH2F, {{400, 1.6, 3.2}, {49, -24.5, 24.5}}}); + } + addHistogramsRec("hPtProng0", "prong 0 #it{p}_{T} (GeV/#it{c})", "entries", {HistType::kTH1D, {{360, 0., 36.}}}); + addHistogramsRec("hPtProng1", "prong 1 #it{p}_{T} (GeV/#it{c})", "entries", {HistType::kTH1D, {{360, 0., 36.}}}); + addHistogramsRec("hPtProng2", "prong 2 #it{p}_{T} (GeV/#it{c})", "entries", {HistType::kTH1D, {{360, 0., 36.}}}); + addHistogramsRec("hd0Prong0", "prong 0 DCAxy to prim. vertex (cm)", "entries", {HistType::kTH1D, {{600, -0.4, 0.4}}}); + addHistogramsRec("hd0Prong1", "prong 1 DCAxy to prim. vertex (cm)", "entries", {HistType::kTH1D, {{600, -0.4, 0.4}}}); + addHistogramsRec("hd0Prong2", "prong 2 DCAxy to prim. vertex (cm)", "entries", {HistType::kTH1D, {{600, -0.4, 0.4}}}); + addHistogramsRec("hDecLength", "decay length (cm)", "entries", {HistType::kTH1D, {{400, 0., 1.}}}); + addHistogramsRec("hDecLengthxy", "decay length xy (cm)", "entries", {HistType::kTH1D, {{400, 0., 1.}}}); + addHistogramsRec("hCPA", "cosine of pointing angle", "entries", {HistType::kTH1D, {{110, -1.1, 1.1}}}); + addHistogramsRec("hCPAxy", "cosine of pointing angle xy", "entries", {HistType::kTH1D, {{110, -1.1, 1.1}}}); + addHistogramsRec("hDca2", "prong Chi2PCA to sec. vertex (cm)", "entries", {HistType::kTH1D, {{400, 0., 20.}}}); + addHistogramsRec("hEta", "#it{#eta}", "entries", {HistType::kTH1D, {{100, -2., 2.}}}); + addHistogramsGen("hEta", "#it{#eta}", "entries", {HistType::kTH1D, {{100, -2., 2.}}}); + addHistogramsGen("hY", "#it{y}", "entries", {HistType::kTH1D, {{100, -2., 2.}}}); + addHistogramsRec("hPhi", "#it{#Phi}", "entries", {HistType::kTH1D, {{100, 0., 6.3}}}); + addHistogramsGen("hPhi", "#it{#Phi}", "entries", {HistType::kTH1D, {{100, 0., 6.3}}}); + /// mass candidate - registry.add("Data/hMassVsPtVsNPvContributors", "3-prong candidates;inv. mass (de K #pi) (GeV/#it{c}^{2}); p_{T}; Number of PV contributors", {HistType::kTH3F, {{400, 2.4, 4.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}, {500, 0., 5000.}}}); + if (isData) { + registry.add("Data/hMassVsPtVsNPvContributors", "3-prong candidates;inv. mass (de K #pi) (GeV/#it{c}^{2}); p_{T}; Number of PV contributors", {HistType::kTH3F, {{400, 2.4, 4.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}, {500, 0., 5000.}}}); + } + addHistogramsRec("hMassVsPt", "inv. mass (de K #pi) (GeV/#it{c}^{2})", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 2.4, 4.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("Data/hMassVsPt", "3-prong candidates;inv. mass (de K #pi) (GeV/#it{c}^{2}); p_{T}", {HistType::kTH2F, {{400, 2.4, 4.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); /// DCAxy to prim. vertex prongs registry.add("Data/hd0VsPtProng0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{600, -0.4, 0.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -211,8 +393,11 @@ struct HfTaskCd { registry.add("Data/hDca2VsPt", "3-prong candidates;prong Chi2PCA to sec. vertex (cm);entries", {HistType::kTH2F, {{400, 0., 20.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); /// eta registry.add("Data/hEtaVsPt", "3-prong candidates;candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsGen("hEtaVsPt", "#it{#eta}", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsGen("hYVsPt", "#it{y}", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); /// phi registry.add("Data/hPhiVsPt", "3-prong candidates;candidate #it{#Phi};entries", {HistType::kTH2F, {{100, 0., 6.3}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsGen("hPhiVsPt", "#it{#Phi}", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); /// selection status registry.add("hSelectionStatus", "3-prong candidates;selection status;entries", {HistType::kTH2F, {{5, -0.5, 4.5}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); /// impact parameter error @@ -231,6 +416,16 @@ struct HfTaskCd { registry.add("Data/hNsigmaTOFPiVsP", "Pion;#it{p} (GeV/#it{c});n#sigma^{TOF}_{pi};", {HistType::kTH2F, {{200, -10.f, 10.f}, {200, -6.f, 6.f}}}); registry.add("Data/hNsigmaTPCKaVsP", "Kaon;#it{p} (GeV/#it{c}); n#sigma^{TPC}_{Kaon}", {HistType::kTH2F, {{200, -10.f, 10.f}, {200, -6.f, 6.f}}}); registry.add("Data/hNsigmaTOFKaVsP", "Kaon;#it{p} (GeV/#it{c}); n#sigma^{TOF}_{Kaon}", {HistType::kTH2F, {{200, -10.f, 10.f}, {200, -6.f, 6.f}}}); + addHistogramsRec("hd0VsPtProng0", "prong 0 DCAxy to prim. vertex (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hd0VsPtProng1", "prong 1 DCAxy to prim. vertex (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hd0VsPtProng2", "prong 2 DCAxy to prim. vertex (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hDecLengthVsPt", "decay length (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hDecLengthxyVsPt", "decay length xy (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hCPAVsPt", "cosine of pointing angle", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hCPAxyVsPt", "cosine of pointing angle xy", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hDca2VsPt", "prong Chi2PCA to sec. vertex (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 20.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hEtaVsPt", "candidate #it{#eta}", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + addHistogramsRec("hPhiVsPt", "candidate #it{#Phi}", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); if (fillTHn) { const AxisSpec thnAxisMass{thnConfigAxisMass, "inv. mass (de K #pi) (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T}(C_{d}^{+}) (GeV/#it{c})"}; @@ -241,9 +436,17 @@ struct HfTaskCd { const AxisSpec thnAxisDecLength{thnConfigAxisDecLength, "decay length (cm)"}; const AxisSpec thnAxisCPA{thnConfigAxisCPA, "cosine of pointing angle"}; const AxisSpec thnAxisCentrality{thnConfigAxisCentrality, "centrality (FT0C)"}; + const AxisSpec thnAxisY{thnAxisRapidity, "rapidity"}; + const AxisSpec thnAxisPtB{thnConfigAxisGenPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; + const AxisSpec thnAxisCt{thnConfigAxisCt, "#it{ct} (#mum)"}; + const AxisSpec thnAxisTracklets{thnConfigAxisNumPvContr, "Number of PV contributors"}; std::vector axesStd{thnAxisMass, thnAxisPt, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisCentrality}; - registry.add("hnCdVars", "THn for Reconstructed Cd candidates for data", HistType::kTHnSparseF, axesStd); + std::vector axesGen{thnAxisPt, thnAxisCentrality, thnAxisY, thnAxisTracklets, thnAxisCt, thnAxisPtB}; + registry.add("hnCdVars", isData ? "THn for Reconstructed Cd candidates for data" : "THn for Reconstructed Cd candidates for MC", HistType::kTHnSparseF, axesStd); + if (!isData) { + registry.add("hnCdVarsGen", "THn for Generated Cd", HistType::kTHnSparseF, axesGen); + } } } @@ -265,6 +468,282 @@ struct HfTaskCd { return (static_cast(sumClusterSizes) / numLayers) * cosLamnda; }; + /// Helper function for filling MC reconstructed histograms for prompt, nonprompt and common signal + template + void fillHistogramsRecSig(CandidateType const& candidate) + { + const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as(); + const auto pdgCodeProng0 = std::abs(mcParticleProng0.pdgCode()); + if ((candidate.isSelCdToDeKPi() >= selectionFlagCd) && pdgCodeProng0 == o2::constants::physics::Pdg::kDeuteron) { + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hMassRecSig") + HIST(SignalSuffixes[SignalType]), HfHelper::invMassCdToDeKPi(candidate)); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hMassVsPtRecSig") + HIST(SignalSuffixes[SignalType]), HfHelper::invMassCdToDeKPi(candidate), candidate.pt()); + } + if ((candidate.isSelCdToPiKDe() >= selectionFlagCd) && pdgCodeProng0 == kPiPlus) { + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hMassRecSig") + HIST(SignalSuffixes[SignalType]), HfHelper::invMassCdToPiKDe(candidate)); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hMassVsPtRecSig") + HIST(SignalSuffixes[SignalType]), HfHelper::invMassCdToPiKDe(candidate), candidate.pt()); + } + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hPtProng0RecSig") + HIST(SignalSuffixes[SignalType]), candidate.ptProng0()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hPtProng1RecSig") + HIST(SignalSuffixes[SignalType]), candidate.ptProng1()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hPtProng2RecSig") + HIST(SignalSuffixes[SignalType]), candidate.ptProng2()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hd0Prong0RecSig") + HIST(SignalSuffixes[SignalType]), candidate.impactParameter0()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hd0Prong1RecSig") + HIST(SignalSuffixes[SignalType]), candidate.impactParameter1()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hd0Prong2RecSig") + HIST(SignalSuffixes[SignalType]), candidate.impactParameter2()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hd0VsPtProng0RecSig") + HIST(SignalSuffixes[SignalType]), candidate.impactParameter0(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hd0VsPtProng1RecSig") + HIST(SignalSuffixes[SignalType]), candidate.impactParameter1(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hd0VsPtProng2RecSig") + HIST(SignalSuffixes[SignalType]), candidate.impactParameter2(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthRecSig") + HIST(SignalSuffixes[SignalType]), candidate.decayLength()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthVsPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.decayLength(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthxyRecSig") + HIST(SignalSuffixes[SignalType]), candidate.decayLengthXY()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthxyVsPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.decayLengthXY(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hCPARecSig") + HIST(SignalSuffixes[SignalType]), candidate.cpa()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hCPAVsPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.cpa(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hCPAxyRecSig") + HIST(SignalSuffixes[SignalType]), candidate.cpaXY()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hCPAxyVsPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.cpaXY(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hDca2RecSig") + HIST(SignalSuffixes[SignalType]), candidate.chi2PCA()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hDca2VsPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.chi2PCA(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaRecSig") + HIST(SignalSuffixes[SignalType]), candidate.eta()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaVsPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.eta(), candidate.pt()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hPhiRecSig") + HIST(SignalSuffixes[SignalType]), candidate.phi()); + registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hPhiVsPtRecSig") + HIST(SignalSuffixes[SignalType]), candidate.phi(), candidate.pt()); + } + + template + void fillHistosMcRec(CollType const& collision, CandCdMcRec const& candidates, CandCdMcGen const& mcParticles, TrackWithItsType const& tracksWithItsPid, BcType const& /*bcs*/) + { + const auto thisCollId = collision.globalIndex(); + const auto& groupedCdCandidates = candidates.sliceBy(candCdPerCollision, thisCollId); + const auto bc = collision.template bc_as(); + const int64_t timeStamp = bc.timestamp(); + + for (const auto& candidate : groupedCdCandidates) { + const int absFlagMc = std::abs(candidate.flagMcMatchRec()); + const bool isTrueCd = absFlagMc == hf_decay::hf_cand_3prong::DecayChannelMain::CDeuteronToDeKPi; + const bool keepCorrelatedBackground = fillMcCorrelatedBackgrounds && absFlagMc != 0 && !isTrueCd; + const bool keepCombinatorialBackground = fillMcCombinatorialBackground && absFlagMc == 0; + if (!isTrueCd && !keepCorrelatedBackground && !keepCombinatorialBackground) { + continue; + } + if (!acceptCandidatesWithoutCdFlag && !TESTBIT(candidate.hfflag(), aod::hf_cand_3prong::DecayType::CdToDeKPi)) { + continue; + } + const auto yCd = RecoDecay::y(candidate.pVector(), o2::constants::physics::MassCDeuteron); + if (yCandRecoMax >= 0. && std::abs(yCd) > yCandRecoMax) { + continue; + } + + registry.fill(HIST("MC/reconstructed/allCandidates/hFlagMcMatchRec"), candidate.flagMcMatchRec()); + if (candidate.isSelCdToDeKPi() >= selectionFlagCd) { + registry.fill(HIST("MC/reconstructed/allCandidates/hMassCdVsFlagMcMatchRec"), HfHelper::invMassCdToDeKPi(candidate), candidate.flagMcMatchRec()); + registry.fill(HIST("MC/reconstructed/allCandidates/hMassPKPiVsFlagMcMatchRec"), HfHelper::invMassLcToPKPi(candidate), candidate.flagMcMatchRec()); + } + if (candidate.isSelCdToPiKDe() >= selectionFlagCd) { + registry.fill(HIST("MC/reconstructed/allCandidates/hMassCdVsFlagMcMatchRec"), HfHelper::invMassCdToPiKDe(candidate), candidate.flagMcMatchRec()); + registry.fill(HIST("MC/reconstructed/allCandidates/hMassPKPiVsFlagMcMatchRec"), HfHelper::invMassLcToPiKP(candidate), candidate.flagMcMatchRec()); + } + + float ctGen{-1.f}, ptGen{-1.f}; + int pdgCodeProng0{0}; + int8_t isCandidateSwapped{-1}; + if (isTrueCd) { + const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as(); + pdgCodeProng0 = std::abs(mcParticleProng0.pdgCode()); + isCandidateSwapped = static_cast(pdgCodeProng0 == kPiPlus); + const auto indexMother = RecoDecay::getMother(mcParticles, mcParticleProng0, o2::constants::physics::Pdg::kCDeuteron, true); + const auto particleMother = mcParticles.rawIteratorAt(indexMother); + ctGen = RecoDecay::ct(std::array{particleMother.px(), particleMother.py(), particleMother.pz()}, RecoDecay::distance(std::array{particleMother.vx(), particleMother.vy(), particleMother.vz()}, std::array{mcParticleProng0.vx(), mcParticleProng0.vy(), mcParticleProng0.vz()}), o2::constants::physics::MassCDeuteron) * CmToMum; + ptGen = particleMother.pt(); + } else if (absFlagMc != 0) { + isCandidateSwapped = candidate.isCandidateSwapped(); + } + + if (fillCandLiteTree || fillCandFullTree) { + const bool selDeKPi = (candidate.isSelCdToDeKPi() >= selectionFlagCd); + const bool selPiKDe = (candidate.isSelCdToPiKDe() >= selectionFlagCd); + const uint8_t hypothesisMask = static_cast((selDeKPi ? 0x1 : 0x0) | + (selPiKDe ? 0x2 : 0x0)); + const int64_t candidateGlobalIndex = candidate.globalIndex(); + auto prong0 = candidate.template prong0_as(); + auto prong1 = candidate.template prong1_as(); + auto prong2 = candidate.template prong2_as(); + auto prong0Its = tracksWithItsPid.iteratorAt(candidate.prong0Id() - tracksWithItsPid.offset()); + auto prong2Its = tracksWithItsPid.iteratorAt(candidate.prong2Id() - tracksWithItsPid.offset()); + + auto writeMcHypothesis = [&](bool isDeKPi) { + const float invMassCd = isDeKPi ? HfHelper::invMassCdToDeKPi(candidate) : HfHelper::invMassCdToPiKDe(candidate); + const float invMassLc = isDeKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + const int candFlag = isDeKPi ? 1 : -1; + const int candSign = static_cast(-prong1.sign()); + const float nSigmaTpcDe = isDeKPi ? candidate.nSigTpcDe0() : candidate.nSigTpcDe2(); + const float nSigmaTpcPr = isDeKPi ? candidate.nSigTpcPr0() : candidate.nSigTpcPr2(); + const float nSigmaTofDe = isDeKPi ? candidate.nSigTofDe0() : candidate.nSigTofDe2(); + const float nSigmaTpcPi = isDeKPi ? candidate.nSigTpcPi2() : candidate.nSigTpcPi0(); + const float nSigmaTofPi = isDeKPi ? candidate.nSigTofPi2() : candidate.nSigTofPi0(); + const float nSigmaItsDe = isDeKPi ? prong0Its.itsNSigmaDe() : prong2Its.itsNSigmaDe(); + const float nSigmaTpcKa = candidate.nSigTpcKa1(); + const float nSigmaTofKa = candidate.nSigTofKa1(); + const auto& deuteronProng = isDeKPi ? prong0 : prong2; + const float tofBetaDe = (deuteronProng.hasTOF() && deuteronProng.beta() > 0.f) ? deuteronProng.beta() : -999.f; + const float tpcInnerParamDe = deuteronProng.tpcInnerParam(); + const float tofExpMomDe = deuteronProng.hasTOF() ? deuteronProng.tofExpMom() : -999.f; + const float dcaDeuteron = isDeKPi ? candidate.impactParameter0() : candidate.impactParameter2(); + const float dcaKaon = candidate.impactParameter1(); + const float dcaPion = isDeKPi ? candidate.impactParameter2() : candidate.impactParameter0(); + + if (cfgUseTofPidForDeuteron && std::abs(nSigmaTofDe) > cfgMaxDeuteronTofPidPreselection) { + return; + } + if (std::abs(dcaDeuteron) < cfgMinDeuteronDcaPreselection) { + return; + } + if (cfgCutOnDeuteronDcaOrdering && (std::abs(dcaDeuteron) > std::abs(dcaKaon) || std::abs(dcaDeuteron) > std::abs(dcaPion))) { + return; + } + + if (fillCandLiteTree) { + rowCandCdLite( + invMassCd, invMassLc, candidate.pt(), candidate.eta(), candidate.phi(), + candidate.ptProng0(), candidate.ptProng1(), candidate.ptProng2(), + candidate.impactParameter0(), candidate.impactParameter1(), candidate.impactParameter2(), + candidate.decayLength(), candidate.cpa(), candidate.chi2PCA(), + nSigmaTpcDe, nSigmaTpcPr, nSigmaItsDe, nSigmaTofDe, + tofBetaDe, tpcInnerParamDe, tofExpMomDe, + candidate.ct(o2::constants::physics::MassCDeuteron) * CmToMum, + candFlag, candSign, candidate.flagMcMatchRec(), isCandidateSwapped, hypothesisMask, candidateGlobalIndex, candidate.originMcRec(), + candidate.flagMcDecayChanRec(), ctGen, o2::hf_centrality::getCentralityColl(collision)); + } + + if (fillCandFullTree) { + rowCandCdFull( + candidate.pxProng0(), candidate.pyProng0(), candidate.pzProng0(), + candidate.pxProng1(), candidate.pyProng1(), candidate.pzProng1(), + candidate.pxProng2(), candidate.pyProng2(), candidate.pzProng2(), + candidate.impactParameter0(), candidate.impactParameter1(), candidate.impactParameter2(), + candidate.decayLength(), candidate.cpa(), candidate.chi2PCA(), + nSigmaTpcDe, nSigmaTpcPr, nSigmaItsDe, nSigmaTofDe, + tofBetaDe, tpcInnerParamDe, tofExpMomDe, + nSigmaTpcPi, nSigmaTofPi, nSigmaTpcKa, nSigmaTofKa, + candidate.ct(o2::constants::physics::MassCDeuteron) * CmToMum, + candFlag, candSign, candidate.flagMcMatchRec(), isCandidateSwapped, hypothesisMask, candidateGlobalIndex, candidate.originMcRec(), + candidate.flagMcDecayChanRec(), ctGen, collision.numContrib(), o2::hf_centrality::getCentralityColl(collision), + collision.posZ(), collision.globalIndex(), timeStamp, deuteronProng.tpcNClsFound()); + } + }; + + if (isTrueCd) { + // For signal MC, retain only the hypothesis that matches the true deuteron prong. + if (selDeKPi && pdgCodeProng0 == o2::constants::physics::Pdg::kDeuteron) { + writeMcHypothesis(true); + } + if (selPiKDe && pdgCodeProng0 == kPiPlus) { + writeMcHypothesis(false); + } + } else { + // Reflection and combinatorial candidates have no true deuteron prong. + // Retain every Cd hypothesis accepted by the selector and distinguish them with CandidateSelFlag. + if (selDeKPi) { + writeMcHypothesis(true); + } + if (selPiKDe) { + writeMcHypothesis(false); + } + } + } + + if (!isTrueCd) { + continue; + } + + registry.fill(HIST("MC/generated/signal/hPtGenSig"), ptGen); + + fillHistogramsRecSig(candidate); + if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { + fillHistogramsRecSig(candidate); + } else if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { + fillHistogramsRecSig(candidate); + } + + if (fillTHn) { + const float cent = o2::hf_centrality::getCentralityColl(collision); + auto fillTHnRecSig = [&](bool isDeKPi) { + const auto massCd = isDeKPi ? HfHelper::invMassCdToDeKPi(candidate) : HfHelper::invMassCdToPiKDe(candidate); + std::vector valuesToFill{massCd, candidate.pt(), candidate.ptProng0(), candidate.ptProng1(), candidate.ptProng2(), candidate.chi2PCA(), candidate.decayLength(), candidate.cpa(), cent}; + registry.get(HIST("hnCdVars"))->Fill(valuesToFill.data()); + }; + if ((candidate.isSelCdToDeKPi() >= selectionFlagCd) && pdgCodeProng0 == o2::constants::physics::Pdg::kDeuteron) { + fillTHnRecSig(true); + } + if ((candidate.isSelCdToPiKDe() >= selectionFlagCd) && pdgCodeProng0 == kPiPlus) { + fillTHnRecSig(false); + } + } + } + } + + template + void fillHistogramsGen(ParticleType const& particle, float yGen) + { + registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hPtGen") + HIST(SignalSuffixes[SignalType]), particle.pt()); + registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaGen") + HIST(SignalSuffixes[SignalType]), particle.eta()); + registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hYGen") + HIST(SignalSuffixes[SignalType]), yGen); + registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hPhiGen") + HIST(SignalSuffixes[SignalType]), particle.phi()); + registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaVsPtGen") + HIST(SignalSuffixes[SignalType]), particle.eta(), particle.pt()); + registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hYVsPtGen") + HIST(SignalSuffixes[SignalType]), yGen, particle.pt()); + registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hPhiVsPtGen") + HIST(SignalSuffixes[SignalType]), particle.phi(), particle.pt()); + } + + template + void fillHistosMcGen(CandCdMcGen const& mcParticles, Coll const& recoCollisions) + { + for (const auto& particle : mcParticles) { + if (std::abs(particle.flagMcMatchGen()) != hf_decay::hf_cand_3prong::DecayChannelMain::CDeuteronToDeKPi) { + continue; + } + const auto yGen = RecoDecay::y(particle.pVector(), o2::constants::physics::MassCDeuteron); + if (yCandGenMax >= 0. && std::abs(yGen) > yCandGenMax) { + continue; + } + const auto& recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollision, particle.mcCollision().globalIndex()); + unsigned int numPvContributors = 0; + float vtxZ = -999.f; + for (const auto& recCol : recoCollsPerMcColl) { + numPvContributors = recCol.numContrib() > numPvContributors ? recCol.numContrib() : numPvContributors; + vtxZ = recCol.posZ(); + } + const float cent = o2::hf_centrality::getCentralityGenColl(recoCollsPerMcColl); + const bool isPrompt = particle.originMcGen() == 0; + const float ptGenB = isPrompt ? -1.f : mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + const auto firstDau = particle.template daughters_as().begin(); + const float ctGen = RecoDecay::ct(std::array{particle.px(), particle.py(), particle.pz()}, RecoDecay::distance(std::array{particle.vx(), particle.vy(), particle.vz()}, std::array{firstDau.vx(), firstDau.vy(), firstDau.vz()}), o2::constants::physics::MassCDeuteron) * CmToMum; + + fillHistogramsGen(particle, yGen); + if (isPrompt) { + fillHistogramsGen(particle, yGen); + } else if (particle.originMcGen() == RecoDecay::OriginType::NonPrompt) { + fillHistogramsGen(particle, yGen); + } + + if (fillTHn) { + std::vector valuesToFill{particle.pt(), cent, yGen, static_cast(numPvContributors), ctGen, ptGenB}; + registry.get(HIST("hnCdVarsGen"))->Fill(valuesToFill.data()); + } + + rowCandCdGen( + particle.pt(), + particle.eta(), + particle.phi(), + yGen, + particle.flagMcMatchGen(), + particle.originMcGen(), + particle.flagMcDecayChanGen(), + ctGen, + numPvContributors, + cent, + vtxZ, + particle.mcCollision().globalIndex()); + } + } + /// Fill histograms for real data template void fillHistosData(CollType const& collision, CandType const& candidates, TrackType const& /*tracks*/, TrackWithItsType const& tracksWithItsPid, BcType const& /*bcs*/) @@ -272,8 +751,8 @@ struct HfTaskCd { auto thisCollId = collision.globalIndex(); auto groupedCdCandidates = candidates.sliceBy(candCdPerCollision, thisCollId); auto numPvContributors = collision.numContrib(); - // auto bc = collision.template bc_as(); - // int64_t timeStamp = bc.timestamp(); + auto bc = collision.template bc_as(); + int64_t timeStamp = bc.timestamp(); for (const auto& candidate : groupedCdCandidates) { if (!TESTBIT(candidate.hfflag(), aod::hf_cand_3prong::DecayType::CdToDeKPi)) { @@ -291,17 +770,6 @@ struct HfTaskCd { const auto chi2PCA = candidate.chi2PCA(); const auto cpa = candidate.cpa(); const auto cpaXY = candidate.cpaXY(); - float invMassCd = 0.f; - float invMassLc = 0.f; - if (candidate.isSelCdToDeKPi() >= selectionFlagCd) { - invMassCd = HfHelper::invMassCdToDeKPi(candidate); - invMassLc = HfHelper::invMassLcToPKPi(candidate); - } - if (candidate.isSelCdToPiKDe() >= selectionFlagCd) { - invMassCd = HfHelper::invMassCdToPiKDe(candidate); - invMassLc = HfHelper::invMassLcToPiKP(candidate); - } - if (candidate.isSelCdToDeKPi() >= selectionFlagCd) { registry.fill(HIST("Data/hMass"), HfHelper::invMassCdToDeKPi(candidate)); registry.fill(HIST("Data/hMassVsPtVsNPvContributors"), HfHelper::invMassCdToDeKPi(candidate), pt, numPvContributors); @@ -358,108 +826,98 @@ struct HfTaskCd { } } - if (fillTree) { - int candFlag = -999; - - float nSigmaTpcDe = 0.f, nSigmaTpcKa = 0.f, nSigmaTpcPi = 0.f, nSigmaTpcPr = 0.f; - float nSigmaItsDe = 0.f; - float nSigmaTofDe = 0.f, nSigmaTofKa = 0.f, nSigmaTofPi = 0.f; - - int itsNClusterSizeDe = 0; - - float tpcSignalsDe = 0.f; - float tpcSignalsPi = 0.f; - float tpcSignalsKa = 0.f; - - float itsSignalsDe = 0.f; - - float pSignedDe = -999.f; - float pSignedPi = -999.f; - - nSigmaTpcKa = candidate.nSigTpcKa1(); - nSigmaTofKa = candidate.nSigTofKa1(); - - const bool selDeKPi = (candidate.isSelCdToDeKPi() >= 1); - const bool selPiKDe = (candidate.isSelCdToPiKDe() >= 1); - + if (fillCandLiteTree || fillCandFullTree) { + const bool selDeKPi = (candidate.isSelCdToDeKPi() >= selectionFlagCd); + const bool selPiKDe = (candidate.isSelCdToPiKDe() >= selectionFlagCd); + const uint8_t hypothesisMask = static_cast((selDeKPi ? 0x1 : 0x0) | + (selPiKDe ? 0x2 : 0x0)); + const int64_t candidateGlobalIndex = candidate.globalIndex(); auto prong0 = candidate.template prong0_as(); auto prong1 = candidate.template prong1_as(); auto prong2 = candidate.template prong2_as(); - auto prong0Its = tracksWithItsPid.iteratorAt(candidate.prong0Id() - tracksWithItsPid.offset()); auto prong2Its = tracksWithItsPid.iteratorAt(candidate.prong2Id() - tracksWithItsPid.offset()); - tpcSignalsKa = prong1.tpcSignal(); + auto writeDataHypothesis = [&](bool isDeKPi) { + const float invMassCd = isDeKPi ? HfHelper::invMassCdToDeKPi(candidate) : HfHelper::invMassCdToPiKDe(candidate); + const float invMassLc = isDeKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + const int candFlag = isDeKPi ? 1 : -1; + const int candSign = static_cast(-prong1.sign()); + const float nSigmaTpcDe = isDeKPi ? candidate.nSigTpcDe0() : candidate.nSigTpcDe2(); + const float nSigmaTpcPr = isDeKPi ? candidate.nSigTpcPr0() : candidate.nSigTpcPr2(); + const float nSigmaTofDe = isDeKPi ? candidate.nSigTofDe0() : candidate.nSigTofDe2(); + const float nSigmaTpcPi = isDeKPi ? candidate.nSigTpcPi2() : candidate.nSigTpcPi0(); + const float nSigmaTofPi = isDeKPi ? candidate.nSigTofPi2() : candidate.nSigTofPi0(); + const float nSigmaItsDe = isDeKPi ? prong0Its.itsNSigmaDe() : prong2Its.itsNSigmaDe(); + const float nSigmaTpcKa = candidate.nSigTpcKa1(); + const float nSigmaTofKa = candidate.nSigTofKa1(); + const auto& deuteronProng = isDeKPi ? prong0 : prong2; + const auto& pionProng = isDeKPi ? prong2 : prong0; + const float tofBetaDe = (deuteronProng.hasTOF() && deuteronProng.beta() > 0.f) ? deuteronProng.beta() : -999.f; + const float tpcInnerParamDe = deuteronProng.tpcInnerParam(); + const float tofExpMomDe = deuteronProng.hasTOF() ? deuteronProng.tofExpMom() : -999.f; + const float dcaDeuteron = isDeKPi ? candidate.impactParameter0() : candidate.impactParameter2(); + const float dcaKaon = candidate.impactParameter1(); + const float dcaPion = isDeKPi ? candidate.impactParameter2() : candidate.impactParameter0(); + const float pSignedDe = deuteronProng.tpcInnerParam() * deuteronProng.sign(); + const float pSignedPi = pionProng.tpcInnerParam() * pionProng.sign(); + + // Fill PID QA per retained mass hypothesis. + registry.fill(HIST("Data/hNsigmaTPCDeVsP"), pSignedDe, nSigmaTpcDe); + registry.fill(HIST("Data/hNsigmaTPCPrVsP"), pSignedDe, nSigmaTpcPr); + registry.fill(HIST("Data/hNsigmaTOFDeVsP"), pSignedDe, nSigmaTofDe); + registry.fill(HIST("Data/hNsigmaITSDeVsP"), pSignedDe, nSigmaItsDe); + registry.fill(HIST("Data/hTPCSignalDeVsP"), pSignedDe, deuteronProng.tpcSignal()); + registry.fill(HIST("Data/hTPCSignalPiVsP"), pSignedPi, pionProng.tpcSignal()); + registry.fill(HIST("Data/hTPCSignalKaVsP"), prong1.tpcInnerParam() * prong1.sign(), prong1.tpcSignal()); + registry.fill(HIST("Data/hITSSignalDeVsP"), pSignedDe, itsSignal(deuteronProng)); + registry.fill(HIST("Data/hNsigmaTPCPiVsP"), pSignedPi, nSigmaTpcPi); + registry.fill(HIST("Data/hNsigmaTOFPiVsP"), pSignedPi, nSigmaTofPi); + registry.fill(HIST("Data/hNsigmaTPCKaVsP"), prong1.tpcInnerParam() * prong1.sign(), nSigmaTpcKa); + registry.fill(HIST("Data/hNsigmaTOFKaVsP"), prong1.tpcInnerParam() * prong1.sign(), nSigmaTofKa); + if (cfgUseTofPidForDeuteron && std::abs(nSigmaTofDe) > cfgMaxDeuteronTofPidPreselection) { + return; + } + if (std::abs(dcaDeuteron) < cfgMinDeuteronDcaPreselection) { + return; + } + if (cfgCutOnDeuteronDcaOrdering && (std::abs(dcaDeuteron) > std::abs(dcaKaon) || std::abs(dcaDeuteron) > std::abs(dcaPion))) { + return; + } + + if (fillCandLiteTree) { + rowCandCdLite( + invMassCd, invMassLc, pt, eta, phi, ptProng0, ptProng1, ptProng2, + candidate.impactParameter0(), candidate.impactParameter1(), candidate.impactParameter2(), + decayLength, cpa, chi2PCA, nSigmaTpcDe, nSigmaTpcPr, nSigmaItsDe, nSigmaTofDe, + tofBetaDe, tpcInnerParamDe, tofExpMomDe, + candidate.ct(o2::constants::physics::MassCDeuteron), + candFlag, candSign, 0, -1, hypothesisMask, candidateGlobalIndex, 0, -1, -1.f, cent); + } + + if (fillCandFullTree) { + rowCandCdFull( + candidate.pxProng0(), candidate.pyProng0(), candidate.pzProng0(), + candidate.pxProng1(), candidate.pyProng1(), candidate.pzProng1(), + candidate.pxProng2(), candidate.pyProng2(), candidate.pzProng2(), + candidate.impactParameter0(), candidate.impactParameter1(), candidate.impactParameter2(), + decayLength, cpa, chi2PCA, nSigmaTpcDe, nSigmaTpcPr, nSigmaItsDe, nSigmaTofDe, + tofBetaDe, tpcInnerParamDe, tofExpMomDe, + nSigmaTpcPi, nSigmaTofPi, nSigmaTpcKa, nSigmaTofKa, + candidate.ct(o2::constants::physics::MassCDeuteron), + candFlag, candSign, 0, -1, hypothesisMask, candidateGlobalIndex, 0, 0, -1.f, collision.numContrib(), cent, + collision.posZ(), collision.globalIndex(), timeStamp, deuteronProng.tpcNClsFound()); + } + }; + + // Data have no truth information: retain every selected hypothesis. if (selDeKPi) { - candFlag = 1; - pSignedDe = prong0.tpcInnerParam() * prong0.sign(); - pSignedPi = prong2.tpcInnerParam() * prong2.sign(); - nSigmaTpcDe = candidate.nSigTpcDe0(); - nSigmaTpcPr = candidate.nSigTpcPr0(); - nSigmaTofDe = candidate.nSigTofDe0(); - nSigmaTpcPi = candidate.nSigTpcPi2(); - nSigmaTofPi = candidate.nSigTofPi2(); - nSigmaItsDe = prong0Its.itsNSigmaDe(); - itsNClusterSizeDe = prong0.itsClusterSizes(); - tpcSignalsDe = prong0.tpcSignal(); - tpcSignalsPi = prong2.tpcSignal(); - itsSignalsDe = itsSignal(prong0); - } else if (selPiKDe) { - candFlag = -1; - pSignedDe = prong2.tpcInnerParam() * prong2.sign(); - pSignedPi = prong0.tpcInnerParam() * prong0.sign(); - nSigmaTpcDe = candidate.nSigTpcDe2(); - nSigmaTpcPr = candidate.nSigTpcPr2(); - nSigmaTofDe = candidate.nSigTofDe2(); - nSigmaTpcPi = candidate.nSigTpcPi0(); - nSigmaTofPi = candidate.nSigTofPi0(); - nSigmaItsDe = prong2Its.itsNSigmaDe(); - itsNClusterSizeDe = prong2.itsClusterSizes(); - tpcSignalsDe = prong2.tpcSignal(); - tpcSignalsPi = prong0.tpcSignal(); - itsSignalsDe = itsSignal(prong2); + writeDataHypothesis(true); + } + if (selPiKDe) { + writeDataHypothesis(false); } - - // PID QA - registry.fill(HIST("Data/hNsigmaTPCDeVsP"), pSignedDe, nSigmaTpcDe); - registry.fill(HIST("Data/hNsigmaTPCPrVsP"), pSignedDe, nSigmaTpcPr); - registry.fill(HIST("Data/hNsigmaTOFDeVsP"), pSignedDe, nSigmaTofDe); - registry.fill(HIST("Data/hNsigmaITSDeVsP"), pSignedDe, nSigmaItsDe); - registry.fill(HIST("Data/hTPCSignalDeVsP"), pSignedDe, tpcSignalsDe); - registry.fill(HIST("Data/hTPCSignalPiVsP"), pSignedPi, tpcSignalsPi); - registry.fill(HIST("Data/hTPCSignalKaVsP"), prong1.tpcInnerParam() * prong1.sign(), tpcSignalsKa); - registry.fill(HIST("Data/hITSSignalDeVsP"), pSignedDe, itsSignalsDe); - registry.fill(HIST("Data/hNsigmaTPCPiVsP"), pSignedPi, nSigmaTpcPi); - registry.fill(HIST("Data/hNsigmaTOFPiVsP"), pSignedPi, nSigmaTofPi); - registry.fill(HIST("Data/hNsigmaTPCKaVsP"), prong1.tpcInnerParam() * prong1.sign(), nSigmaTpcKa); - registry.fill(HIST("Data/hNsigmaTOFKaVsP"), prong1.tpcInnerParam() * prong1.sign(), nSigmaTofKa); - - rowCandCd( - invMassCd, - invMassLc, - pt, - eta, - phi, - ptProng0, - ptProng1, - ptProng2, - candidate.impactParameter0(), - candidate.impactParameter1(), - candidate.impactParameter2(), - decayLength, - cpa, - chi2PCA, - nSigmaTpcDe, - nSigmaTpcPr, - nSigmaItsDe, - nSigmaTofDe, - itsNClusterSizeDe, - tpcSignalsDe, - tpcSignalsPi, - tpcSignalsKa, - candFlag, - cent); } } } @@ -477,6 +935,19 @@ struct HfTaskCd { } } + template + void runAnalysisPerCollisionMc(CollType const& collisions, + CandType const& candidates, + CandCdMcGen const& mcParticles, + TrackWithItsType const& tracksWithItsPid, + BcType const& bcs) + { + for (const auto& collision : collisions) { + fillHistosMcRec(collision, candidates, mcParticles, tracksWithItsPid, bcs); + } + fillHistosMcGen(mcParticles, collisions); + } + void processDataStd(CollisionsWEvSel const& collisions, CdCandidates const& selectedCdCandidates, HFTracks const& tracks, @@ -509,6 +980,42 @@ struct HfTaskCd { runAnalysisPerCollisionData(collisions, selectedCdCandidates, tracks, tracksWithItsPid, bcWithTimeStamps); } PROCESS_SWITCH(HfTaskCd, processDataStdWithFT0M, "Process real data with the standard method and with FT0M centrality", false); + + void processMcStd(CollisionsMc const& collisions, + CdCandidatesMc const& selectedCdCandidatesMc, + McParticles3ProngMatched const& mcParticles, + aod::McCollisions const&, + HFTracksMc const& tracks, + aod::BCsWithTimestamps const& bcWithTimeStamps) + { + auto tracksWithItsPid = soa::Attach(tracks); + runAnalysisPerCollisionMc(collisions, selectedCdCandidatesMc, mcParticles, tracksWithItsPid, bcWithTimeStamps); + } + PROCESS_SWITCH(HfTaskCd, processMcStd, "Process MC with the standard method", false); + + void processMcStdWithFT0C(CollisionsMcWithEvSelFT0C const& collisions, + CdCandidatesMc const& selectedCdCandidatesMc, + McParticles3ProngMatched const& mcParticles, + aod::McCollisions const&, + HFTracksMc const& tracks, + aod::BCsWithTimestamps const& bcWithTimeStamps) + { + auto tracksWithItsPid = soa::Attach(tracks); + runAnalysisPerCollisionMc(collisions, selectedCdCandidatesMc, mcParticles, tracksWithItsPid, bcWithTimeStamps); + } + PROCESS_SWITCH(HfTaskCd, processMcStdWithFT0C, "Process MC with the standard method with FT0C centrality", false); + + void processMcStdWithFT0M(CollisionsMcWithEvSelFT0M const& collisions, + CdCandidatesMc const& selectedCdCandidatesMc, + McParticles3ProngMatched const& mcParticles, + aod::McCollisions const&, + HFTracksMc const& tracks, + aod::BCsWithTimestamps const& bcWithTimeStamps) + { + auto tracksWithItsPid = soa::Attach(tracks); + runAnalysisPerCollisionMc(collisions, selectedCdCandidatesMc, mcParticles, tracksWithItsPid, bcWithTimeStamps); + } + PROCESS_SWITCH(HfTaskCd, processMcStdWithFT0M, "Process MC with the standard method with FT0M centrality", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx b/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx index 5ab8dc2d1a6..0691e76b104 100644 --- a/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx +++ b/PWGHF/D2H/Tasks/taskCharmPolarisation.cxx @@ -29,6 +29,7 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/Qvectors.h" #include @@ -226,9 +227,9 @@ struct HfTaskCharmPolarisation { HfEventSelection hfEvSel; // event selection and monitoring o2::framework::Service ccdb{}; - using CollisionsWithMcLabels = soa::SmallGroups>; - using CollisionsWithMcLabelsAndCent = soa::SmallGroups>; - using CollsWithQVecs = soa::Join; + using CollisionsWithMcLabels = soa::SmallGroups>; + using CollisionsWithMcLabelsAndCent = soa::SmallGroups>; + using CollsWithQVecs = soa::Join; using TracksWithMcLabels = soa::Join; using TracksWithExtra = soa::Join; @@ -794,8 +795,8 @@ struct HfTaskCharmPolarisation { LOGP(fatal, "THnSparse with cosThStar w.r.t. event plane axis is not supported for pp analysis, please check the configuration!"); } else if (activateTHnSparseCosThStarEP) { std::vector hEPaxes = {thnAxisInvMass, thnAxisPt, thnAxisNumPvContributors, thnAxisY}; + hEPaxes.insert(hEPaxes.end(), {thnAxisInvMassD0, thnAxisCosThetaStarEP}); if (doprocessDstarInPbPb || doprocessDstarWithMlInPbPb) { - hEPaxes.insert(hEPaxes.end(), {thnAxisInvMassD0, thnAxisCosThetaStarEP}); if (doprocessDstarWithMlInPbPb) { hEPaxes.insert(hEPaxes.end(), {thnAxisMlBkg, thnAxisMlNonPrompt}); } @@ -1626,6 +1627,9 @@ struct HfTaskCharmPolarisation { } } else { registry.fill(HIST("hEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, outputMl[0], /*outputMl[1],*/ outputMl[2], absEtaMin, numItsClsMin, numTpcClsMin, centrality); + if (activateTHnSparsePhiAndCosSqStarEP && ptCharmHad > ptMinCosPhiCosSqTheta) { + registry.fill(HIST("hCosPhiCosSqThetaEP"), invMassCharmHad, ptCharmHad, std::abs(rapCharmHad), std::cos(2 * phiEuler), cosThetaStar * cosThetaStar, outputMl[0], /*outputMl[1],*/ outputMl[2], centrality); + } } } else { if (nBkgRotations > 0) { @@ -1635,6 +1639,9 @@ struct HfTaskCharmPolarisation { } } else { registry.fill(HIST("hEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, outputMl[0], /*outputMl[1],*/ outputMl[2], centrality); + if (activateTHnSparsePhiAndCosSqStarEP && ptCharmHad > ptMinCosPhiCosSqTheta) { + registry.fill(HIST("hCosPhiCosSqThetaEP"), invMassCharmHad, ptCharmHad, std::abs(rapCharmHad), std::cos(2 * phiEuler), cosThetaStar * cosThetaStar, outputMl[0], /*outputMl[1],*/ outputMl[2], centrality); + } } } } @@ -1690,6 +1697,40 @@ struct HfTaskCharmPolarisation { } } } + } else { + if (origin == RecoDecay::OriginType::Prompt) { // prompt + if constexpr (Channel == charm_polarisation::DecayChannel::DstarToDzeroPi) { // D*+ + if (!isPartRecoDstar) { + if (activateTrackingSys) { + registry.fill(HIST("hRecoPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, absEtaMin, numItsClsMin, numTpcClsMin, nMuons, centrality); + } else { + registry.fill(HIST("hRecoPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, nMuons, centrality); + } + } else { + if (activateTrackingSys) { + registry.fill(HIST("hPartRecoPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, absEtaMin, numItsClsMin, numTpcClsMin, nMuons, centrality); + } else { + registry.fill(HIST("hPartRecoPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, nMuons, centrality); + } + } + } + } else { // non-prompt + if constexpr (Channel == charm_polarisation::DecayChannel::DstarToDzeroPi) { // D*+ + if (!isPartRecoDstar) { + if (activateTrackingSys) { + registry.fill(HIST("hRecoNonPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, absEtaMin, numItsClsMin, numTpcClsMin, nMuons, ptBhadMother, centrality); + } else { + registry.fill(HIST("hRecoNonPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, absEtaMin, nMuons, ptBhadMother, centrality); + } + } else { + if (activateTrackingSys) { + registry.fill(HIST("hPartRecoNonPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, absEtaMin, numItsClsMin, numTpcClsMin, nMuons, ptBhadMother, centrality); + } else { + registry.fill(HIST("hPartRecoNonPromptEP"), invMassCharmHad, ptCharmHad, numPvContributors, std::abs(rapCharmHad), invMassD0, cosThetaStar, nMuons, ptBhadMother, centrality); + } + } + } + } } } } @@ -1939,7 +1980,7 @@ struct HfTaskCharmPolarisation { template bool runPolarisationAnalysis(Cand const& candidate, int bkgRotationId, T numPvContributors, Part const& particles, Trk const& /*tracks*/, float centrality = -999.f, QVecs const* qVecs = nullptr) { - if constexpr (WithEp) { + if constexpr (WithEp && !DoMc) { assert(qVecs && "EP analysis requested but qVecs == nullptr"); assert(centrality && "EP analysis requested but centrality == nullptr"); } @@ -2298,22 +2339,20 @@ struct HfTaskCharmPolarisation { } } - if constexpr (WithEp) { - if (!DoMc) { - /// EP analysis + if (activateTHnSparseCosThStarEP) { + if constexpr (WithEp && !DoMc) { + // EP analysis for data float const xQvec = (*qVecs).at(0); float const yQvec = (*qVecs).at(1); ROOT::Math::XYZVector const qVecNorm = ROOT::Math::XYZVector(yQvec, -xQvec, 0.f); - ROOT::Math::XYZVector const qVec = ROOT::Math::XYZVector(xQvec, yQvec, 0.); - - if (activateTHnSparseCosThStarEP) { - // EP - ROOT::Math::XYZVector threeVecDauCMXY = ROOT::Math::XYZVector(threeVecDauCM.X(), threeVecDauCM.Y(), 0.); - float const phiEP = qVec.Dot(threeVecDauCMXY) / std::sqrt(threeVecDauCMXY.Mag2()) / std::sqrt(qVec.Mag2()); - float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(qVecNorm.Mag2()); - fillRecoHistos(invMassCharmHadForSparse, ptCharmHad, numPvContributors, rapidity, invMassD0, invMassKPiLc, cosThetaStarEP, phiEP, outputMl, isRotatedCandidate, origin, ptBhadMother, resoChannelLc, absEtaTrackMin, numItsClsMin, numTpcClsMin, charge, nMuons, partRecoDstar, centrality); - } - } else { + float const phiStarBeam = std::atan2(threeVecDauCM.Y(), threeVecDauCM.X()); + float const psiAngle = epHelper.GetEventPlane(xQvec, yQvec, 2); + float const deltaPhiStarEP = RecoDecay::constrainAngle(phiStarBeam - psiAngle, 0., 2); + float const cosThetaStarEP = qVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(qVecNorm.Mag2()); + fillRecoHistos(invMassCharmHadForSparse, ptCharmHad, numPvContributors, rapidity, invMassD0, invMassKPiLc, cosThetaStarEP, deltaPhiStarEP, outputMl, isRotatedCandidate, origin, ptBhadMother, resoChannelLc, absEtaTrackMin, numItsClsMin, numTpcClsMin, charge, nMuons, partRecoDstar, centrality); + } + if (DoMc) { + // EP analysis for MC double const deltaPhi = sampleDeltaPhi(ptCharmHad); double psi = candidate.phi() - deltaPhi; ROOT::Math::XYZVector qVecNorm = ROOT::Math::XYZVector(-std::sin(psi), std::cos(psi), 0.f); diff --git a/PWGHF/D2H/Tasks/taskCharmResoToDTrkReduced.cxx b/PWGHF/D2H/Tasks/taskCharmResoToDTrkReduced.cxx index 356e36e6894..07e56a5cfab 100644 --- a/PWGHF/D2H/Tasks/taskCharmResoToDTrkReduced.cxx +++ b/PWGHF/D2H/Tasks/taskCharmResoToDTrkReduced.cxx @@ -412,9 +412,11 @@ struct HfTaskCharmResoToDTrkReduced { std::array ptProngs = {particle.ptProng0(), particle.ptProng1()}; std::array etaProngs = {particle.etaProng0(), particle.etaProng1()}; bool const prongsInAcc = isProngInAcceptance(etaProngs[0], ptProngs[0]) && isProngInAcceptance(etaProngs[1], ptProngs[1]); - if (Channel == DecayChannel::D0Kplus && - !hf_decay::hf_cand_reso::particlesToD0Kplus.contains(static_cast(std::abs(flag)))) { - continue; + if constexpr (Channel == DecayChannel::D0Kplus) { + if (fillOnlySignal && + !hf_decay::hf_cand_reso::particlesToD0Kplus.contains(static_cast(std::abs(flag)))) { + continue; + } } registry.fill(HIST("hYGenAll"), ptParticle, yParticle); if (yCandGenMax >= 0. && std::abs(yParticle) > yCandGenMax) { diff --git a/PWGHF/D2H/Tasks/taskD0.cxx b/PWGHF/D2H/Tasks/taskD0.cxx index 088c3847b90..59778af1fa0 100644 --- a/PWGHF/D2H/Tasks/taskD0.cxx +++ b/PWGHF/D2H/Tasks/taskD0.cxx @@ -26,12 +26,14 @@ #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/Utils/utilsEvSelHf.h" #include "PWGHF/Utils/utilsUpcHf.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGUD/Core/UPCHelpers.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -57,6 +59,7 @@ #include #include #include +#include #include using namespace o2; @@ -81,6 +84,12 @@ enum CandTypeSel { }; } // namespace struct HfTaskD0 { + enum RecoEventStatus { + NoRecoCollision = 0, + NoSelectedRecoCollision, + SelectedRecoCollision + }; + Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; @@ -91,6 +100,7 @@ struct HfTaskD0 { Configurable selectionPid{"selectionPid", 1, "Selection Flag for reco PID candidates"}; Configurable> binsPt{"binsPt", std::vector{hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits"}; Configurable centEstimator{"centEstimator", 0, "Centrality estimation (None: 0, FT0C: 2, FT0M: 3)"}; + Configurable fillEventStatus{"fillEventStatus", false, "Use generated centrality in hSparseAcc and append reco-event status (requires storeCentrality)"}; Configurable occEstimator{"occEstimator", 0, "Occupancy estimation (None: 0, ITS: 1, FT0C: 2)"}; Configurable storeCentrality{"storeCentrality", false, "Flag to store centrality information"}; Configurable storeOccupancyAndIR{"storeOccupancyAndIR", false, "Flag to store occupancy information and interaction rate"}; @@ -120,10 +130,11 @@ struct HfTaskD0 { using D0CandidatesMlKF = soa::Filtered>; using D0CandidatesMlMcKF = soa::Filtered>; - using Collisions = soa::Join; - using CollisionsCent = soa::Join; - using CollisionsWithMcLabels = soa::Join; - using CollisionsWithMcLabelsCent = soa::Join; + using Collisions = soa::Join; + using CollisionsCent = soa::Join; + using CollisionsWithMcLabels = soa::Join; + using CollisionsWithMcLabelsCent = soa::Join; + using McCollisionsWithCentrality = soa::Join; using TracksSelQuality = soa::Join; using TracksWPid = soa::Join; // using TracksWithExtra = o2::soa::Join; @@ -261,7 +272,7 @@ struct HfTaskD0 { if ((storeZdcEnergy || storeZdcTime) && !(doprocessDataWithDCAFitterNWithUpc || doprocessDataWithDCAFitterNMlWithUpc)) { LOGP(fatal, "Can't enable the storeZdcEnergy and storeZdcTime without UPC process"); } - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0., 5.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassVsPhi", "2-prong candidates vs phi;inv. mass (#pi K) (GeV/#it{c}^{2});phi (rad);entries", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {32, 0, o2::constants::math::TwoPI}}}); registry.add("hDecLength", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -330,13 +341,22 @@ struct HfTaskD0 { std::vector axesAcc = {thnAxisGenPtD, thnAxisGenPtB, thnAxisY, thnAxisOrigin, thnAxisNumPvContr}; if (storeCentrality) { - axesAcc.push_back(thnAxisCent); + axesAcc.emplace_back(thnConfigAxisCent, fillEventStatus ? "Generated centrality (%)" : "Centrality"); } // interaction rate only store in Data and MC Reco. Level if (storeOccupancyAndIR) { axesAcc.push_back(thnAxisOccupancy); } + if (fillEventStatus) { + if (!storeCentrality) { + LOGP(fatal, "fillEventStatus requires storeCentrality=true."); + } + if (centEstimator != CentralityEstimator::FT0C && centEstimator != CentralityEstimator::FT0M) { + LOGP(fatal, "centEstimator must be FT0C (2) or FT0M (3)."); + } + axesAcc.emplace_back(3, -0.5, 2.5, "Reco event status (0: no reco, 1: none selected, 2: selected)"); + } registry.add("hSparseAcc", "Thn for generated D0 from charm and beauty", HistType::kTHnSparseD, axesAcc); registry.get(HIST("hSparseAcc"))->Sumw2(); } @@ -345,10 +365,10 @@ struct HfTaskD0 { if (doprocessMcWithDCAFitterN || doprocessMcWithDCAFitterNCent || doprocessMcWithKFParticle || doprocessMcWithDCAFitterNMl || doprocessMcWithDCAFitterNMlCent || doprocessMcWithKFParticleMl) { axes.push_back(thnAxisPtB); axes.push_back(thnAxisOrigin); - axes.push_back(thnAxisNumPvContr); } if (storeCentrality) { axes.push_back(thnAxisCent); + axes.push_back(thnAxisNumPvContr); } if (storeOccupancyAndIR) { axes.push_back(thnAxisOccupancy); @@ -365,12 +385,12 @@ struct HfTaskD0 { // Insert ML scores after pt (position 2) to match taskDplus structure: [mass, pt, mlScores, ...] if (doprocessDataWithDCAFitterNMlWithUpc) { - axes.insert(axes.begin() + 2, thnAxisPromptScore); axes.insert(axes.begin() + 2, thnAxisNonPromptScore); + axes.insert(axes.begin() + 2, thnAxisPromptScore); axes.insert(axes.begin() + 2, thnAxisBkgScore); } else { - axes.insert(axes.begin(), thnAxisPromptScore); axes.insert(axes.begin(), thnAxisNonPromptScore); + axes.insert(axes.begin(), thnAxisPromptScore); axes.insert(axes.begin(), thnAxisBkgScore); } } @@ -483,7 +503,11 @@ struct HfTaskD0 { registry.add("QAtracks/hDCAxy_GapC", "Gap C; DCA xy", {HistType::kTH1F, {{400, -2, 2.}}}); registry.add("QAtracks/hDCAz_GapC", "Gap C; DCA z", {HistType::kTH1F, {{400, -4, 4.}}}); - hfEvSel.addHistograms(registry); + if (fillEventStatus && (doprocessMcWithDCAFitterN || doprocessMcWithDCAFitterNCent || doprocessMcWithKFParticle || doprocessMcWithDCAFitterNMl || doprocessMcWithDCAFitterNMlCent || doprocessMcWithKFParticleMl)) { + hfEvSel.init(registry); + } else { + hfEvSel.addHistograms(registry); + } ccdb->setURL(ccdbUrl); ccdb->setCaching(true); @@ -556,10 +580,12 @@ struct HfTaskD0 { float cent{-1.f}; float occ{-1.f}; float ir{-1.f}; + int numPvContributors = -1; if (storeCentrality || storeOccupancyAndIR) { auto collision = candidate.template collision_as(); if (storeCentrality && centEstimator != CentralityEstimator::None) { cent = getCentralityColl(collision, centEstimator); + numPvContributors = collision.numContrib(); } if (storeOccupancyAndIR && occEstimator != OccupancyEstimator::None) { occ = o2::hf_occupancy::getOccupancyColl(collision, occEstimator); @@ -575,21 +601,21 @@ struct HfTaskD0 { if constexpr (ApplyMl) { if (storeCentrality && storeOccupancyAndIR) { if (candidate.isSelD0() >= selectionFlagD0) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent, occ, ir); - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent, numPvContributors, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent, numPvContributors, occ, ir); } if (candidate.isSelD0bar() >= selectionFlagD0bar) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent, occ, ir); - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent, numPvContributors, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent, numPvContributors, occ, ir); } } else if (storeCentrality && !storeOccupancyAndIR) { if (candidate.isSelD0() >= selectionFlagD0) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent); - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent, numPvContributors); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent, numPvContributors); } if (candidate.isSelD0bar() >= selectionFlagD0bar) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent); - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent, numPvContributors); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent, numPvContributors); } } else if (!storeCentrality && storeOccupancyAndIR) { if (candidate.isSelD0() >= selectionFlagD0) { @@ -622,21 +648,21 @@ struct HfTaskD0 { } else { if (storeCentrality && storeOccupancyAndIR) { if (candidate.isSelD0() >= selectionFlagD0) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent, occ, ir); - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent, numPvContributors, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent, numPvContributors, occ, ir); } if (candidate.isSelD0bar() >= selectionFlagD0bar) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent, occ, ir); - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent, numPvContributors, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent, numPvContributors, occ, ir); } } else if (storeCentrality && !storeOccupancyAndIR) { if (candidate.isSelD0() >= selectionFlagD0) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent); - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), SigD0, cent, numPvContributors); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0, cent, numPvContributors); } if (candidate.isSelD0bar() >= selectionFlagD0bar) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent); - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), SigD0bar, cent, numPvContributors); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar, cent, numPvContributors); } } else if (!storeCentrality && storeOccupancyAndIR) { if (candidate.isSelD0() >= selectionFlagD0) { @@ -967,7 +993,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const&, CollType const& collisions, - aod::McCollisions const&, + McCollisionsWithCentrality const&, BCsType const&) { // MC rec. @@ -1122,27 +1148,27 @@ struct HfTaskD0 { registry.fill(HIST("hMassSigD0"), massD0, ptCandidate, rapidityCandidate); if constexpr (ApplyMl) { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), occ, ir); } else if (storeTrackQuality) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, minItsClustersOfProngs, minTpcCrossedRowsOfProngs); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), minItsClustersOfProngs, minTpcCrossedRowsOfProngs); } else { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); } } else { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), occ, ir); } else if (storeTrackQuality) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, minItsClustersOfProngs, minTpcCrossedRowsOfProngs); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), minItsClustersOfProngs, minTpcCrossedRowsOfProngs); } else { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); } } } else { @@ -1164,27 +1190,27 @@ struct HfTaskD0 { registry.fill(HIST("hMassReflBkgD0"), massD0, ptCandidate, rapidityCandidate); if constexpr (ApplyMl) { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), occ, ir); } else if (storeTrackQuality) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, minItsClustersOfProngs, minTpcCrossedRowsOfProngs); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), minItsClustersOfProngs, minTpcCrossedRowsOfProngs); } else { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0()[0], candidate.mlProbD0()[1], candidate.mlProbD0()[2], massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); } } else { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), occ, ir); } else if (storeTrackQuality) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, minItsClustersOfProngs, minTpcCrossedRowsOfProngs); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), minItsClustersOfProngs, minTpcCrossedRowsOfProngs); } else { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0, ptCandidate, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); } } } @@ -1196,27 +1222,27 @@ struct HfTaskD0 { registry.fill(HIST("hMassSigD0bar"), massD0bar, ptCandidate, rapidityCandidate); if constexpr (ApplyMl) { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), occ, ir); } else if (storeTrackQuality) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, minItsClustersOfProngs, minTpcCrossedRowsOfProngs); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), minItsClustersOfProngs, minTpcCrossedRowsOfProngs); } else { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec()); } } else { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), occ, ir); } else if (storeTrackQuality) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, minItsClustersOfProngs, minTpcCrossedRowsOfProngs); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), minItsClustersOfProngs, minTpcCrossedRowsOfProngs); } else { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec()); } } } else { @@ -1225,9 +1251,9 @@ struct HfTaskD0 { registry.fill(HIST("hMassReflBkgD0bar"), massD0bar, ptCandidate, rapidityCandidate); if constexpr (ApplyMl) { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { registry.fill(HIST("hBdtScoreVsMassVsPtVsPtBVsYVsOriginVsD0Type"), candidate.mlProbD0bar()[0], candidate.mlProbD0bar()[1], candidate.mlProbD0bar()[2], massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); } else if (storeTrackQuality) { @@ -1237,22 +1263,46 @@ struct HfTaskD0 { } } else { if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors, occ, ir); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, cent); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), cent, numPvContributors); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, occ, ir); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), occ, ir); } else if (storeTrackQuality) { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors, minItsClustersOfProngs, minTpcCrossedRowsOfProngs); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), minItsClustersOfProngs, minTpcCrossedRowsOfProngs); } else { - registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec(), numPvContributors); + registry.fill(HIST("hMassVsPtVsPtBVsYVsOriginVsD0Type"), massD0bar, ptCandidate, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec()); } } } } } } - // MC gen. + // Classify reco associations once per collision, not once per generated particle. + // The task's hfEvSel.* configuration must match the candidate creator's cuts. + std::unordered_map recoEventStatus; + if (fillEventStatus) { + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + float unusedCentrality{-1.f}; + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, unusedCentrality, ccdb, registry); + bool selected = rejectionMask == 0; + if constexpr (std::is_same_v) { + if (centEstimator != CentralityEstimator::None) { + const auto recoCentrality = getCentralityColl(collision, centEstimator); + selected = selected && recoCentrality >= hfEvSel.centralityMin && recoCentrality <= hfEvSel.centralityMax; + } + } + auto& status = recoEventStatus[collision.mcCollisionId()]; + const int currentStatus = selected ? SelectedRecoCollision : NoSelectedRecoCollision; + status = std::max(status, currentStatus); + } + } + + // MC gen. Each particle is filled once, even for split or unreconstructed events. + // Upstream event rejection can clear flagMcMatchGen: this is not an unfiltered truth sample. for (const auto& particle : mcParticles) { if (std::abs(particle.flagMcMatchGen()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { if (yCandGenMax >= 0. && std::abs(RecoDecay::y(particle.pVector(), o2::constants::physics::MassD0)) > yCandGenMax) { @@ -1264,11 +1314,13 @@ struct HfTaskD0 { registry.fill(HIST("hPtGen"), ptGen); registry.fill(HIST("hPtVsYGen"), ptGen, yGen); + int eventStatus = NoRecoCollision; + unsigned maxNumContrib = 0; float cent{-1.f}; float occ{-1.f}; if constexpr (std::is_same_v) { - const auto& recoCollsPerMcCollCent = collisions.sliceBy(colPerMcCollisionCent, particle.mcCollision().globalIndex()); + const auto& recoCollsPerMcCollCent = collisions.sliceBy(colPerMcCollisionCent, particle.mcCollisionId()); for (const auto& recCol : recoCollsPerMcCollCent) { maxNumContrib = recCol.numContrib() > maxNumContrib ? recCol.numContrib() : maxNumContrib; } @@ -1279,24 +1331,39 @@ struct HfTaskD0 { occ = o2::hf_occupancy::getOccupancyGenColl(recoCollsPerMcCollCent, occEstimator); } } else { - const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, particle.mcCollision().globalIndex()); + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, particle.mcCollisionId()); for (const auto& recCol : recoCollsPerMcColl) { maxNumContrib = recCol.numContrib() > maxNumContrib ? recCol.numContrib() : maxNumContrib; } } + if (fillEventStatus) { + const auto mcCollision = particle.template mcCollision_as(); + const auto eventEntry = recoEventStatus.find(mcCollision.globalIndex()); + eventStatus = eventEntry == recoEventStatus.end() ? NoRecoCollision : eventEntry->second; + // Replace the existing centrality coordinate, including zero-reco events. + cent = centEstimator == CentralityEstimator::FT0C ? mcCollision.centFT0C() : mcCollision.centFT0M(); + } + const auto fillGeneratedSparse = [&](auto... coordinates) { + if (fillEventStatus) { + registry.fill(HIST("hSparseAcc"), coordinates..., eventStatus); + } else { + registry.fill(HIST("hSparseAcc"), coordinates...); + } + }; + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hPtGenPrompt"), ptGen); registry.fill(HIST("hYGenPrompt"), yGen); registry.fill(HIST("hPtVsYGenPrompt"), ptGen, yGen); if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib, cent, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib, cent, occ); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib, cent); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib, cent); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib, occ); } else { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 1, maxNumContrib); + fillGeneratedSparse(ptGen, ptGenB, yGen, 1, maxNumContrib); } } else { ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); @@ -1304,13 +1371,13 @@ struct HfTaskD0 { registry.fill(HIST("hYGenNonPrompt"), yGen); registry.fill(HIST("hPtVsYGenNonPrompt"), ptGen, yGen); if (storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib, cent, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib, cent, occ); } else if (storeCentrality && !storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib, cent); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib, cent); } else if (!storeCentrality && storeOccupancyAndIR) { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib, occ); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib, occ); } else { - registry.fill(HIST("hSparseAcc"), ptGen, ptGenB, yGen, 2, maxNumContrib); + fillGeneratedSparse(ptGen, ptGenB, yGen, 2, maxNumContrib); } } registry.fill(HIST("hEtaGen"), particle.eta()); @@ -1322,7 +1389,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1333,7 +1400,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabelsCent const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1344,7 +1411,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMcKF, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1356,7 +1423,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMlMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1367,7 +1434,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabelsCent const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMlMc, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1378,7 +1445,7 @@ struct HfTaskD0 { soa::Join const& mcParticles, TracksSelQuality const& tracks, CollisionsWithMcLabels const& collisions, - aod::McCollisions const& mcCollisions, + McCollisionsWithCentrality const& mcCollisions, aod::BcFullInfos const& bcs) { processMc(selectedD0CandidatesMlMcKF, mcParticles, tracks, collisions, mcCollisions, bcs); @@ -1386,7 +1453,7 @@ struct HfTaskD0 { PROCESS_SWITCH(HfTaskD0, processMcWithKFParticleMl, "Process MC with KFParticle and ML selections", false); // TODO: add the processMcWithKFParticleMlCent - void processDataWithDCAFitterNWithUpc(soa::Join const& collisions, + void processDataWithDCAFitterNWithUpc(soa::Join const& collisions, aod::BcFullInfos const& bcs, D0Candidates const&, aod::TracksWExtra const&, @@ -1401,7 +1468,7 @@ struct HfTaskD0 { } PROCESS_SWITCH(HfTaskD0, processDataWithDCAFitterNWithUpc, "Process real data with DCAFitterN w/o ML with UPC", false); - void processDataWithDCAFitterNMlWithUpc(soa::Join const& collisions, + void processDataWithDCAFitterNMlWithUpc(soa::Join const& collisions, aod::BcFullInfos const& bcs, D0CandidatesMl const&, aod::TracksWExtra const&, diff --git a/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx new file mode 100644 index 00000000000..28106a38b6e --- /dev/null +++ b/PWGHF/D2H/Tasks/taskDeuteronFromLb.cxx @@ -0,0 +1,808 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file taskDeuteronFromLb.cxx +/// \brief A filter task for non prompt deuterons from beauty-hadron decays +/// \author Marta Razza +/// \author Francesca Ercolessi +/// \since May 25, 2026 + +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct HfTaskDeuteronFromLb { + + Zorro zorro; + o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + + Configurable cutzvertex{"cutzvertex", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable applySkimming{"applySkimming", false, "Skimmed dataset processing"}; + Configurable cfgSkimming{"cfgSkimming", "fH2fromLb", "Configurable for skimming"}; + Configurable sel8{"sel8", true, "sel8 event selection"}; + Configurable cfgEta{"cfgEta", 0.8f, "Track eta selection"}; + Configurable cfgTPCNclsFound{"cfgTPCNclsFound", 100, "Minimum TPC clusters found"}; + Configurable cfgTPCChi2Ncl{"cfgTPCChi2Ncl", 4.0f, "Maximum TPC chi2 per N clusters"}; + Configurable cfgITSChi2Ncl{"cfgITSChi2Ncl", 36.0f, "Maximum ITS chi2 per N clusters"}; + Configurable cfgITScls{"cfgITScls", 2, "Minimum ITS clusters"}; + Configurable cfgMaxPt{"cfgMaxPt", 5.0f, "Maximum pT cut"}; + Configurable cfgMinPt{"cfgMinPt", 0.5f, "Minimum pT cut"}; + Configurable cfgTPCNsigma{"cfgTPCNsigma", 3.0f, "TPC n sigma for deuteron PID"}; + Configurable cfgTofNsigma{"cfgTofNsigma", 3.0f, "TOF n sigma for deuteron PID"}; + Configurable cfgITSNsigma{"cfgITSNsigma", -2.0f, "ITS n sigma for deuteron PID"}; + Configurable cfgDCAmin{"cfgDCAmin", 0.05f, "Minimum DCA for deuteron PID"}; + Configurable cfgDCAmax{"cfgDCAmax", 1000.0f, "Maximum DCA for deuteron PID"}; + Configurable rapidityCut{"rapidityCut", 0.5f, "Rapidity cut"}; + Configurable applyRapidityCut{"applyRapidityCut", true, "Apply the rapidity cut |y| < rapidityCut (reco and gen)"}; + Configurable ptThresholdforPID{"ptThresholdforPID", 1.0f, "pT threshold for PID, below TPC+ITS PID is used, above TPC+TOF PID is used and VETO on TPC for e,pi,K,p is applied"}; + Configurable ptThresholdforPID2{"ptThresholdforPID2", 2.2f, "pT threshold above which TPC+TOF PID with TOF veto on pi,K,p is applied (TPC veto below)"}; + // PDG codes + Configurable pdgCodeBeautyMeson{"pdgCodeBeautyMeson", -521, "PDG code of the beauty meson mother particle (default: B-)"}; + Configurable pdgCodeBeautyBaryon{"pdgCodeBeautyBaryon", -5122, "PDG code of the beauty baryon mother particle (default: anti-Lambda_b)"}; + Configurable pdgCodeDaughter{"pdgCodeDaughter", -1000010020, "PDG code of the daughter particle (default: anti-deuteron)"}; + Configurable pdgCodeHypertriton{"pdgCodeHypertriton", -1010010030, "PDG code of the hypertriton mother particle (default: anti-hypertriton)"}; + // Veto switches: if true, tracks compatible with that species are rejected + Configurable applyVetoElTPC{"applyVetoElTPC", true, "Apply TPC veto on electrons"}; + Configurable applyVetoPiTPC{"applyVetoPiTPC", true, "Apply TPC veto on pions"}; + Configurable applyVetoKaTPC{"applyVetoKaTPC", true, "Apply TPC veto on kaons"}; + Configurable applyVetoPrTPC{"applyVetoPrTPC", true, "Apply TPC veto on protons"}; + Configurable applyVetoPiTOF{"applyVetoPiTOF", true, "Apply TOF veto on pions"}; + Configurable applyVetoKaTOF{"applyVetoKaTOF", true, "Apply TOF veto on kaons"}; + Configurable applyVetoPrTOF{"applyVetoPrTOF", true, "Apply TOF veto on protons"}; + + int mRunNumber = 0; + int mCurrentRun = -1; + + framework::Service ccdb{}; + + using CollisionCandidates = o2::soa::Join; + using TrackCandidates = o2::soa::Join; + using MCTrackCandidates = o2::soa::Join; + using MCCollisionCandidates = o2::soa::Join; + + Preslice trackIndicesPerCollision = o2::aod::track_association::collisionId; + Preslice mcParticlesPerCollision = o2::aod::mcparticle::mcCollisionId; + + ConfigurableAxis ptAxis{"ptAxis", {100, 0.f, 10.f}, "p_{T} GeV/c"}; + ConfigurableAxis nSigmaAxis{"nSigmaAxis", {200, -10.f, 10.f}, "nSigma"}; + ConfigurableAxis dcaXyAxis{"dcaXyAxis", {4000, -2.0f, 2.0f}, "DCA xy (cm)"}; + ConfigurableAxis dcaZAxis{"dcaZAxis", {4000, -2.0f, 2.0f}, "DCA z (cm)"}; + ConfigurableAxis ctauHypertritonAxis{"ctauHypertritonAxis", {100, 0.f, 30.f}, "c#tau (cm)"}; + ConfigurableAxis etaAxis{"etaAxis", {20, -1.f, 1.f}, "#eta"}; + ConfigurableAxis rapidityAxis{"rapidityAxis", {20, -1.f, 1.f}, "y"}; + + HistogramRegistry qaHistos{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + OutputObj zorroSummary{"zorroSummary"}; + OutputObj hProcessedEvents{TH1D("hProcessedEvents", "Event filtered;; Number of events", 4, 0., 4.)}; + + bool isSelectedMother(int pdgCode) + { + return pdgCode == pdgCodeBeautyMeson || pdgCode == pdgCodeBeautyBaryon || pdgCode == pdgCodeHypertriton; + } + + double getMotherMass(int pdgCode) + { + if (pdgCode == pdgCodeBeautyMeson) { + return o2::constants::physics::MassBPlus; + } + + if (pdgCode == pdgCodeBeautyBaryon) { + return o2::constants::physics::MassLambdaB0; + } + if (pdgCode == pdgCodeHypertriton) { + return o2::constants::physics::MassHyperTriton; + } + return -1.; + } + + void init(framework::InitContext&) + { + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + if (applySkimming) { + zorroSummary.setObject(zorro.getZorroSummary()); + } + + qaHistos.add("Data/hDCAxyVsPt", "DCAxy #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("Data/hDCAzVsPt", "DCAz #bar{d} vs p_{T}", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("Data/hnSigmaTPCVsPtFinalSel", "n#sigma TPC vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtFinalSel", "n#sigma TOF vs p_{T} for #bar{d} hypothesis; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/ptAntiDeuteron", "ptAntiDeuteron", {HistType::kTH1F, {ptAxis}}); + qaHistos.add("Data/etaAntideuteron", "etaAntideuteron", {HistType::kTH1F, {{100, -1.0f, 1.0f, "eta #bar{d}"}}}); + qaHistos.add("Data/rapidityAntiDeuteron", "rapidityAntiDeuteron;y #bar{d};Counts", {HistType::kTH1F, {rapidityAxis}}); + qaHistos.add("Data/hVtxZ", "Z-Vertex distribution after selection;Z (cm)", HistType::kTH1F, {{100, -50, 50}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityNoVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTOFVsPtPurityVeto", "n#sigma TOF vs p_{T} for #bar{d} hypothesis for Data-driven purity check with TPC veto for e,pi,K,p below the threshold and TOF veto for pi,K,p above the threshold; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaTPCVsPtPurity", "n#sigma TPC vs p_{T} for #bar{d} hypothesis for Data-driven purity check; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + qaHistos.add("Data/hnSigmaITSVsPt", "n#sigma ITS vs p_{T} for #bar{d} hypothesis, used to clean up the TPC/TOF purity sample; p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {ptAxis, nSigmaAxis}}); + // MC generated-level histograms + qaHistos.add("MCGen/ptGeneratedBminus", "p_{T} generated B^{-};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/ptGeneratedAntiLambdaB", "p_{T} generated #bar{#Lambda}_{b};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/hMotherPdgCode", "PDG code of mother, gen level;PDG code;Counts", HistType::kTH1I, {{12000, -6000, 6000}}); + qaHistos.add("MCGen/ctauBminus", "ctau of B^{-}, gen level;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); + qaHistos.add("MCGen/ctauAntiLambdaB", "ctau of #bar{#Lambda}_{b}, gen level;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); + qaHistos.add("MCGen/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/ptAntiDeuteronAll", "p_{T} #bar{d} all gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/hEvents", "Events MC gen;;Counts", HistType::kTH1F, {{2, 0., 2.}}); + qaHistos.add("MCGen/ptGeneratedAntiHypertriton", "p_{T} generated ^{3}_{#bar{#Lambda}}#bar{H};p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, gen level;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); + qaHistos.add("MCGen/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromBminus", "#bar{d} from B^{-} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromAntiLambdaB", "#bar{d} from #bar{#Lambda}_{b} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronFromAntiHypertriton", "#bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/hPtEtaAntiDeuteronPrimary", "#bar{d} primary gen;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromBminus", "y #bar{d} from B^{-} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCGen/rapidityAntiDeuteronPrimary", "y #bar{d} primary gen;y;Counts", HistType::kTH1F, {rapidityAxis}); + // MC reco/MC-anchored histograms + qaHistos.add("MCReco/hDCAxyVsPtDeFromBminus", "DCAxy #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaB", "DCAxy #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertriton", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDePrimary", "DCAxy #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromBminus", "DCAz #bar{d} from B^{-} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaB", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertriton", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDePrimary", "DCAz #bar{d} primary vs p_{T};p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromBminusSameMC", "DCAxy #bar{d} from B^{-} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC", "DCAxy #bar{d} from #bar{#Lambda}_{b} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC", "DCAxy #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAxyVsPtDePrimarySameMC", "DCAxy #bar{d} primary vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{xy} (cm)", {HistType::kTH2D, {ptAxis, dcaXyAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromBminusSameMC", "DCAz #bar{d} from B^{-} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiLambdaBSameMC", "DCAz #bar{d} from #bar{#Lambda}_{b} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDeFromAntiHypertritonSameMC", "DCAz #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/hDCAzVsPtDePrimarySameMC", "DCAz #bar{d} primary vs p_{T}, same MC collision;p_{T} (GeV/c);DCA_{z} (cm)", {HistType::kTH2D, {ptAxis, dcaZAxis}}); + qaHistos.add("MCReco/ptAntiDeuteronFromBminusSameMC", "p_{T} #bar{d} from B^{-} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaBSameMC", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertritonSameMC", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPrimarySameMC", "p_{T} #bar{d} primary reco/MC anchored, same MC collision;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromBminus", "p_{T} #bar{d} from B^{-} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPrimary", "p_{T} #bar{d} primary reco/MC anchored;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/hEvents", "Events MC reco;;Counts", HistType::kTH1F, {{3, 0., 3.}}); + qaHistos.add("MCReco/hMotherPdgCode", "PDG code of mother, reco/MC anchored;PDG code;Counts", HistType::kTH1I, {{12000, -6000, 6000}}); + qaHistos.add("MCReco/ctauBminus", "ctau of B^{-}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); + qaHistos.add("MCReco/ctauAntiLambdaB", "ctau of #bar{#Lambda}_{b}, reco/MC anchored;ctau (#mu m);Counts", HistType::kTH1F, {{25, 0., 2000.f}}); + qaHistos.add("MCReco/ctauAntiHypertriton", "ctau of ^{3}_{#bar{#Lambda}}#bar{H}, reco/MC anchored;c#tau (cm);Counts", HistType::kTH1F, {ctauHypertritonAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPIDSelected", "p_{T} #bar{d} reco/MC anchored PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronTPCOnlyPIDSelected", "p_{T} #bar{d} reco/MC anchored TPC only PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored TPC only PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected", "p_{T} #bar{d} reco/MC anchored TPC or TPC+TOF PID selection;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue", "p_{T} #bar{d} reco/MC anchored TPC or TPC+TOF PID and PDG;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromBminus", "p_{T} #bar{d} from B^{-} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiLambdaB", "p_{T} #bar{d} from #bar{#Lambda}_{b} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenFromAntiHypertriton", "p_{T} #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + qaHistos.add("MCReco/ptAntiDeuteronGenPrimary", "p_{T} #bar{d} primary gen in reco events;p_{T} (GeV/c);Counts", HistType::kTH1F, {ptAxis}); + // MC reco: eta and rapidity of reconstructed tracks passing the final selection + qaHistos.add("MCReco/etaAntiDeuteronFromBminus", "#eta #bar{d} from B^{-} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiLambdaB", "#eta #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiHypertriton", "#eta #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronPrimary", "#eta #bar{d} primary reco/MC anchored;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromBminus", "y #bar{d} from B^{-} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronPrimary", "y #bar{d} primary reco/MC anchored;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromBminusSameMC", "#eta #bar{d} from B^{-} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiLambdaBSameMC", "#eta #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronFromAntiHypertritonSameMC", "#eta #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/etaAntiDeuteronPrimarySameMC", "#eta #bar{d} primary reco/MC anchored, same MC collision;#eta;Counts", HistType::kTH1F, {etaAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromBminusSameMC", "y #bar{d} from B^{-} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiLambdaBSameMC", "y #bar{d} from #bar{#Lambda}_{b} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronFromAntiHypertritonSameMC", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronPrimarySameMC", "y #bar{d} primary reco/MC anchored, same MC collision;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromBminus", "#bar{d} from B^{-} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromAntiLambdaB", "#bar{d} from #bar{#Lambda}_{b} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenFromAntiHypertriton", "#bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/hPtEtaAntiDeuteronGenPrimary", "#bar{d} primary gen in reco events;p_{T} (GeV/c);#eta", {HistType::kTH2D, {ptAxis, etaAxis}}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromBminus", "y #bar{d} from B^{-} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromAntiLambdaB", "y #bar{d} from #bar{#Lambda}_{b} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenFromAntiHypertriton", "y #bar{d} from ^{3}_{#bar{#Lambda}}#bar{H} gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + qaHistos.add("MCReco/rapidityAntiDeuteronGenPrimary", "y #bar{d} primary gen in reco events;y;Counts", HistType::kTH1F, {rapidityAxis}); + + hProcessedEvents->GetXaxis()->SetBinLabel(1, "Events processed"); + hProcessedEvents->GetXaxis()->SetBinLabel(2, "ZORRO"); + hProcessedEvents->GetXaxis()->SetBinLabel(3, "sel8"); + hProcessedEvents->GetXaxis()->SetBinLabel(4, "z-vertex"); + auto hEvents = qaHistos.get(HIST("MCReco/hEvents")); + hEvents->GetXaxis()->SetBinLabel(1, "All"); + hEvents->GetXaxis()->SetBinLabel(2, "sel8"); + hEvents->GetXaxis()->SetBinLabel(3, "z-vertex"); + auto hEventsGen = qaHistos.get(HIST("MCGen/hEvents")); + hEventsGen->GetXaxis()->SetBinLabel(1, "All"); + hEventsGen->GetXaxis()->SetBinLabel(2, "z-vertex"); + + qaHistos.add("MCReco/hMotherCategory", "Mother of #bar{d};;Counts", HistType::kTH1F, {{5, 0., 5.}}); + auto hMotherCategory = qaHistos.get(HIST("MCReco/hMotherCategory")); + hMotherCategory->GetXaxis()->SetBinLabel(1, "B^{-}"); + hMotherCategory->GetXaxis()->SetBinLabel(2, "#bar{#Lambda}_{b}"); + hMotherCategory->GetXaxis()->SetBinLabel(3, "^{3}_{#bar{#Lambda}}#bar{H}"); + hMotherCategory->GetXaxis()->SetBinLabel(4, "primary"); + hMotherCategory->GetXaxis()->SetBinLabel(5, "other"); + } + + void initCCDB(o2::aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + if (applySkimming) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), cfgSkimming.value); + zorro.populateHistRegistry(registry, bc.runNumber()); + } + mRunNumber = bc.runNumber(); + } + + template + bool passedSingleTrackSelection(const T1& track) + { + if (std::abs(track.eta()) > cfgEta) { + return false; + } + if (applyRapidityCut && std::abs(track.rapidity(o2::constants::physics::MassDeuteron)) > rapidityCut) { + return false; + } + if (!track.hasITS()) { + return false; + } + if (!track.hasTPC()) { + return false; + } + if (track.tpcNClsFound() < cfgTPCNclsFound) { + return false; + } + if (track.tpcChi2NCl() > cfgTPCChi2Ncl) { + return false; + } + if (track.itsChi2NCl() > cfgITSChi2Ncl) { + return false; + } + if (track.itsNCls() < cfgITScls) { + return false; + } + if (track.pt() > cfgMaxPt) { + return false; + } + if (track.pt() < cfgMinPt) { + return false; + } + if (track.sign() > 0) { + return false; + } + + return true; + } + + void processData(CollisionCandidates const& collisions, + o2::aod::TrackAssoc const& trackIndices, + TrackCandidates const& tracks, + o2::aod::BCsWithTimestamps const&) + { + auto tracksWithItsPid = o2::soa::Attach(tracks); + for (const auto& collision : collisions) { + if (mCurrentRun != collision.bc_as().runNumber()) { + auto* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", collision.bc_as().timestamp()); + o2::base::Propagator::initFieldFromGRP(grpo); + mCurrentRun = collision.bc_as().runNumber(); + } + + const auto& bc = collision.bc_as(); + initCCDB(bc); + hProcessedEvents->Fill(0.5); + if (applySkimming) { + if (!zorro.isSelected(bc.globalBC())) { + continue; + } + } + hProcessedEvents->Fill(1.5); + if (sel8 && !collision.sel8()) { + continue; + } + hProcessedEvents->Fill(2.5); + if (std::abs(collision.posZ()) > cutzvertex) { + continue; + } + hProcessedEvents->Fill(3.5); + qaHistos.fill(HIST("Data/hVtxZ"), collision.posZ()); + + const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + + for (const auto& trackId : trackIdsThisCollision) { + const auto& track = tracksWithItsPid.rawIteratorAt(trackId.trackId()); + std::array dca{track.dcaXY(), track.dcaZ()}; + + if (track.collisionId() != collision.globalIndex()) { + auto trackPar = getTrackParCov(track); + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackPar, 2.f, noMatCorr, &dca); + } + + if (std::abs(dca[0]) < cfgDCAmin || std::abs(dca[0]) > cfgDCAmax) { + continue; + } + + if (!passedSingleTrackSelection(track)) { + continue; + } + + const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; + const bool isTOFDe = std::abs(track.tofNSigmaDe()) < cfgTofNsigma; + const bool isITSDe = track.itsNSigmaDe() > cfgITSNsigma; + + qaHistos.fill(HIST("Data/hnSigmaITSVsPt"), track.pt(), track.itsNSigmaDe()); + + const bool isElectronForTPC = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma; + const bool isPionForTPC = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma; + const bool isKaonForTPC = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma; + const bool isProtonForTPC = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma; + const bool isPionForTOF = std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isKaonForTOF = std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isProtonForTOF = std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + + const bool isNotElectronForTPC = !applyVetoElTPC || !isElectronForTPC; + const bool isNotPionForTPC = !applyVetoPiTPC || !isPionForTPC; + const bool isNotKaonForTPC = !applyVetoKaTPC || !isKaonForTPC; + const bool isNotProtonForTPC = !applyVetoPrTPC || !isProtonForTPC; + const bool isNotPionForTOF = !applyVetoPiTOF || !isPionForTOF; + const bool isNotKaonForTOF = !applyVetoKaTOF || !isKaonForTOF; + const bool isNotProtonForTOF = !applyVetoPrTOF || !isProtonForTOF; + + const bool isTPCandVetoTPC = isTPCDe && isNotElectronForTPC && isNotPionForTPC && isNotKaonForTPC && isNotProtonForTPC; + const bool isTPCandVetoTOF = isTPCDe && isNotPionForTOF && isNotKaonForTOF && isNotProtonForTOF; + + const bool isLowPt = track.pt() < ptThresholdforPID; + const bool isMidPt = track.pt() >= ptThresholdforPID && track.pt() < ptThresholdforPID2; + const bool isHighPt = track.pt() >= ptThresholdforPID2; + + bool passVeto = false; + if (isMidPt) { + passVeto = isTPCandVetoTPC; // veto TPC for e,pi,K,p + } else if (isHighPt) { + passVeto = isTPCandVetoTOF; // veto TOF for pi,K,p + } + + // pT < 1.0 GeV/c: purity TPC using ITS + if (isLowPt && isITSDe) { + qaHistos.fill(HIST("Data/hnSigmaTPCVsPtPurity"), track.pt(), track.tpcNSigmaDe()); + } + // pT >= 1.0 GeV/c: purity TOF with and without vetoes + if (!isLowPt && isTPCDe && track.hasTOF()) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityNoVeto"), track.pt(), track.tofNSigmaDe()); + if (passVeto) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtPurityVeto"), track.pt(), track.tofNSigmaDe()); + } + } + + const bool passFinalSel = (isLowPt && isTPCDe && isITSDe) || (!isLowPt && passVeto && isTOFDe && track.hasTOF()); + + if (passFinalSel) { + qaHistos.fill(HIST("Data/ptAntiDeuteron"), track.pt()); + qaHistos.fill(HIST("Data/etaAntideuteron"), track.eta()); + qaHistos.fill(HIST("Data/rapidityAntiDeuteron"), track.rapidity(o2::constants::physics::MassDeuteron)); + qaHistos.fill(HIST("Data/hDCAxyVsPt"), track.pt(), dca[0]); + qaHistos.fill(HIST("Data/hDCAzVsPt"), track.pt(), dca[1]); + qaHistos.fill(HIST("Data/hnSigmaTPCVsPtFinalSel"), track.pt(), track.tpcNSigmaDe()); + if (!isLowPt) { + qaHistos.fill(HIST("Data/hnSigmaTOFVsPtFinalSel"), track.pt(), track.tofNSigmaDe()); + } + } + } + } + } + PROCESS_SWITCH(HfTaskDeuteronFromLb, processData, "processData", true); + + void processMC(MCCollisionCandidates const& collisions, + o2::aod::TrackAssoc const& trackIndices, + MCTrackCandidates const& tracks, + o2::aod::McParticles const& mcParticles, + o2::aod::BCsWithTimestamps const&) + { + auto tracksWithItsPid = o2::soa::Attach(tracks); + tracksWithItsPid.bindExternalIndices(&mcParticles); + + for (const auto& collision : collisions) { + const auto& bc = collision.bc_as(); + if (mCurrentRun != bc.runNumber()) { + auto* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + o2::base::Propagator::initFieldFromGRP(grpo); + mCurrentRun = bc.runNumber(); + } + + qaHistos.fill(HIST("MCReco/hEvents"), 0.5); + if (sel8 && !collision.sel8()) { + continue; + } + qaHistos.fill(HIST("MCReco/hEvents"), 1.5); + if (std::abs(collision.posZ()) > cutzvertex) { + continue; + } + qaHistos.fill(HIST("MCReco/hEvents"), 2.5); + + if (collision.has_mcCollision()) { + const auto mcParticlesThisEvent = mcParticles.sliceBy(mcParticlesPerCollision, collision.mcCollisionId()); + + for (const auto& mcParticle : mcParticlesThisEvent) { + + if (mcParticle.pdgCode() != pdgCodeDaughter) { + continue; + } + if (std::abs(mcParticle.eta()) > cfgEta || (applyRapidityCut && std::abs(mcParticle.y()) > rapidityCut)) { + continue; + } + if (mcParticle.pt() < cfgMinPt || mcParticle.pt() > cfgMaxPt) { + continue; + } + bool fromMother = false; + for (const auto& mom : mcParticle.mothers_as()) { + if (mom.pdgCode() == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromBminus"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromBminus"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromBminus"), mcParticle.y()); + fromMother = true; + break; + } + if (mom.pdgCode() == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiLambdaB"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromAntiLambdaB"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromAntiLambdaB"), mcParticle.y()); + fromMother = true; + break; + } + if (mom.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenFromAntiHypertriton"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenFromAntiHypertriton"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenFromAntiHypertriton"), mcParticle.y()); + fromMother = true; + break; + } + } + if (!fromMother && mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronGenPrimary"), mcParticle.pt()); + qaHistos.fill(HIST("MCReco/hPtEtaAntiDeuteronGenPrimary"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronGenPrimary"), mcParticle.y()); + } + } + } + + const auto& trackIdsThisCollision = trackIndices.sliceBy(trackIndicesPerCollision, collision.globalIndex()); + + for (const auto& trackId : trackIdsThisCollision) { + const auto& track = tracksWithItsPid.rawIteratorAt(trackId.trackId()); + std::array dca{track.dcaXY(), track.dcaZ()}; + + if (track.collisionId() != collision.globalIndex()) { + auto trackPar = getTrackParCov(track); + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackPar, 2.f, noMatCorr, &dca); + } + + if (std::abs(dca[0]) < cfgDCAmin || std::abs(dca[0]) > cfgDCAmax) { + continue; + } + if (!passedSingleTrackSelection(track)) { + continue; + } + + if (!track.has_mcParticle()) { + continue; + } + + auto mcParticle = track.mcParticle(); + const bool isSameMcCollision = collision.has_mcCollision() && (mcParticle.mcCollisionId() == collision.mcCollisionId()); + const bool isTrueDeuteron = (mcParticle.pdgCode() == pdgCodeDaughter); + const bool isTPCDe = std::abs(track.tpcNSigmaDe()) < cfgTPCNsigma; + const bool isTOFDe = std::abs(track.tofNSigmaDe()) < cfgTofNsigma; + const bool isITSDe = track.itsNSigmaDe() > cfgITSNsigma; + + const bool selTpcOrTpcTof = isTPCDe && (!track.hasTOF() || isTOFDe); + + if (isTPCDe) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCOnlyPIDSelectedTrue"), track.pt()); + } + } + + if (selTpcOrTpcTof) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronTPCorTPCTOFPIDSelectedTrue"), track.pt()); + } + } + // Purity study: TPC && TOF + if (isTPCDe && isTOFDe && track.hasTOF()) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelected"), track.pt()); + if (isTrueDeuteron) { + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPIDSelectedTrue"), track.pt()); + } + } + + const bool isElectronForTPC = std::abs(track.tpcNSigmaEl()) < cfgTPCNsigma; + const bool isPionForTPC = std::abs(track.tpcNSigmaPi()) < cfgTPCNsigma; + const bool isKaonForTPC = std::abs(track.tpcNSigmaKa()) < cfgTPCNsigma; + const bool isProtonForTPC = std::abs(track.tpcNSigmaPr()) < cfgTPCNsigma; + const bool isPionForTOF = std::abs(track.tofNSigmaPi()) < cfgTofNsigma; + const bool isKaonForTOF = std::abs(track.tofNSigmaKa()) < cfgTofNsigma; + const bool isProtonForTOF = std::abs(track.tofNSigmaPr()) < cfgTofNsigma; + + const bool isNotElectronForTPC = !applyVetoElTPC || !isElectronForTPC; + const bool isNotPionForTPC = !applyVetoPiTPC || !isPionForTPC; + const bool isNotKaonForTPC = !applyVetoKaTPC || !isKaonForTPC; + const bool isNotProtonForTPC = !applyVetoPrTPC || !isProtonForTPC; + const bool isNotPionForTOF = !applyVetoPiTOF || !isPionForTOF; + const bool isNotKaonForTOF = !applyVetoKaTOF || !isKaonForTOF; + const bool isNotProtonForTOF = !applyVetoPrTOF || !isProtonForTOF; + + const bool isTPCandVetoTPC = isTPCDe && isNotElectronForTPC && isNotPionForTPC && isNotKaonForTPC && isNotProtonForTPC; + const bool isTPCandVetoTOF = isTPCDe && isNotPionForTOF && isNotKaonForTOF && isNotProtonForTOF; + + const bool isLowPt = track.pt() < ptThresholdforPID; + const bool isMidPt = track.pt() >= ptThresholdforPID && track.pt() < ptThresholdforPID2; + const bool isHighPt = track.pt() >= ptThresholdforPID2; + + bool passVeto = false; + if (isMidPt) { + passVeto = isTPCandVetoTPC; // veto TPC for e,pi,K,p + } else if (isHighPt) { + passVeto = isTPCandVetoTOF; // veto TOF for pi,K,p + } + + if (isTrueDeuteron) { + int motherPdg = 0; + for (const auto& mom : mcParticle.mothers_as()) { + qaHistos.fill(HIST("MCReco/hMotherPdgCode"), mom.pdgCode()); + if (mom.pdgCode() == pdgCodeBeautyMeson || mom.pdgCode() == pdgCodeBeautyBaryon || mom.pdgCode() == pdgCodeHypertriton) { + motherPdg = mom.pdgCode(); + double dx = mcParticle.vx() - mom.vx(); + double dy = mcParticle.vy() - mom.vy(); + double dz = mcParticle.vz() - mom.vz(); + + double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); + double massBeauty = getMotherMass(mom.pdgCode()); + + if (massBeauty > 0.) { + double ctauBeauty = flightDistance / mom.p() * massBeauty; + + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/ctauBminus"), ctauBeauty * 1.e4); + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/ctauAntiLambdaB"), ctauBeauty * 1.e4); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/ctauAntiHypertriton"), ctauBeauty); // cm + } + } + break; + } + } + + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 0.5); + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 1.5); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 2.5); + } else if (mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 3.5); + } else { + qaHistos.fill(HIST("MCReco/hMotherCategory"), 4.5); + } + // same final selection as in data + const bool passFinalSel = (isLowPt && isTPCDe && isITSDe) || (!isLowPt && passVeto && isTOFDe && track.hasTOF()); + if (passFinalSel) { + const float rapidity = track.rapidity(o2::constants::physics::MassDeuteron); + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminus"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminus"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminus"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromBminus"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromBminus"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromBminusSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromBminusSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromBminusSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromBminusSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromBminusSameMC"), rapidity); + } + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaB"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaB"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaB"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiLambdaB"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiLambdaB"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiLambdaBSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiLambdaBSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiLambdaBSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiLambdaBSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiLambdaBSameMC"), rapidity); + } + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertriton"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertriton"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertriton"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiHypertriton"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiHypertriton"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDeFromAntiHypertritonSameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDeFromAntiHypertritonSameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronFromAntiHypertritonSameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronFromAntiHypertritonSameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronFromAntiHypertritonSameMC"), rapidity); + } + } else if (mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimary"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimary"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimary"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronPrimary"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronPrimary"), rapidity); + if (isSameMcCollision) { + qaHistos.fill(HIST("MCReco/hDCAxyVsPtDePrimarySameMC"), track.pt(), dca[0]); + qaHistos.fill(HIST("MCReco/hDCAzVsPtDePrimarySameMC"), track.pt(), dca[1]); + qaHistos.fill(HIST("MCReco/ptAntiDeuteronPrimarySameMC"), track.pt()); + qaHistos.fill(HIST("MCReco/etaAntiDeuteronPrimarySameMC"), track.eta()); + qaHistos.fill(HIST("MCReco/rapidityAntiDeuteronPrimarySameMC"), rapidity); + } + } + } + } + } + } + } + PROCESS_SWITCH(HfTaskDeuteronFromLb, processMC, "processMC", false); + + void processGen(o2::aod::McCollision const& collision, o2::aod::McParticles const& mcParticles) + { + qaHistos.fill(HIST("MCGen/hEvents"), 0.5); + if (std::abs(collision.posZ()) > cutzvertex) { + return; + } + qaHistos.fill(HIST("MCGen/hEvents"), 1.5); + + for (const auto& mcParticle : mcParticles) { + if (isSelectedMother(mcParticle.pdgCode())) { + if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCGen/ptGeneratedBminus"), mcParticle.pt()); + } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCGen/ptGeneratedAntiLambdaB"), mcParticle.pt()); + } else if (mcParticle.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ptGeneratedAntiHypertriton"), mcParticle.pt()); + } + if (mcParticle.has_daughters()) { + + for (const auto& daughter : mcParticle.daughters_as()) { + + double dx = daughter.vx() - mcParticle.vx(); + double dy = daughter.vy() - mcParticle.vy(); + double dz = daughter.vz() - mcParticle.vz(); + double flightDistance = std::sqrt(dx * dx + dy * dy + dz * dz); + double massBeauty = getMotherMass(mcParticle.pdgCode()); + if (massBeauty > 0.) { + double ctauBeauty = flightDistance / mcParticle.p() * massBeauty; + + if (mcParticle.pdgCode() == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCGen/ctauBminus"), ctauBeauty * 1.e4); + } else if (mcParticle.pdgCode() == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCGen/ctauAntiLambdaB"), ctauBeauty * 1.e4); + } else if (mcParticle.pdgCode() == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ctauAntiHypertriton"), ctauBeauty); // cm + } + } + break; + } + } + } + + if (mcParticle.pdgCode() == pdgCodeDaughter) { + + if (std::abs(mcParticle.eta()) > cfgEta) { + continue; + } + + if (applyRapidityCut && std::abs(mcParticle.y()) > rapidityCut) { + continue; + } + + if (mcParticle.pt() < cfgMinPt || mcParticle.pt() > cfgMaxPt) { + continue; + } + + qaHistos.fill(HIST("MCGen/ptAntiDeuteronAll"), mcParticle.pt()); + + int motherPdg = 0; + if (mcParticle.has_mothers()) { + for (const auto& mom : mcParticle.mothers_as()) { + + qaHistos.fill(HIST("MCGen/hMotherPdgCode"), mom.pdgCode()); + + if (mom.pdgCode() == pdgCodeBeautyMeson) { + motherPdg = mom.pdgCode(); + break; + } + + if (mom.pdgCode() == pdgCodeBeautyBaryon) { + motherPdg = mom.pdgCode(); + break; + } + + if (mom.pdgCode() == pdgCodeHypertriton) { + motherPdg = mom.pdgCode(); + break; + } + } + } + + if (motherPdg == pdgCodeBeautyMeson) { + qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromBminus"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromBminus"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromBminus"), mcParticle.y()); + } else if (motherPdg == pdgCodeBeautyBaryon) { + qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiLambdaB"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromAntiLambdaB"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromAntiLambdaB"), mcParticle.y()); + } else if (motherPdg == pdgCodeHypertriton) { + qaHistos.fill(HIST("MCGen/ptAntiDeuteronFromAntiHypertriton"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronFromAntiHypertriton"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronFromAntiHypertriton"), mcParticle.y()); + } else if (mcParticle.isPhysicalPrimary()) { + qaHistos.fill(HIST("MCGen/ptAntiDeuteronPrimary"), mcParticle.pt()); + qaHistos.fill(HIST("MCGen/hPtEtaAntiDeuteronPrimary"), mcParticle.pt(), mcParticle.eta()); + qaHistos.fill(HIST("MCGen/rapidityAntiDeuteronPrimary"), mcParticle.y()); + } + } + } + } + + PROCESS_SWITCH(HfTaskDeuteronFromLb, processGen, "processGen", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/taskDplus.cxx b/PWGHF/D2H/Tasks/taskDplus.cxx index fb4102b48a1..4d8f0aafdab 100644 --- a/PWGHF/D2H/Tasks/taskDplus.cxx +++ b/PWGHF/D2H/Tasks/taskDplus.cxx @@ -35,6 +35,7 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include #include @@ -100,8 +101,8 @@ struct HfTaskDplus { using CandDplusMcRecoWithMl = soa::Filtered>; using CandDplusMcGen = soa::Join; - using CollisionsCent = soa::Join; - using McRecoCollisionsCent = soa::Join; + using CollisionsCent = soa::Join; + using McRecoCollisionsCent = soa::Join; Filter filterDplusFlag = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_3prong::DecayType::DplusToPiKPi))) != static_cast(0); @@ -121,12 +122,13 @@ struct HfTaskDplus { Partition recoBkgCandidates = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) && aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlagDplus; Partition recoBkgCandidatesWithMl = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) && aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlagDplus; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {50, 0, 25}, "Cand. pt bins"}; ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {600, 1.67, 2.27}, "Cand. mass bins"}; ConfigurableAxis thnConfigAxisY{"thnConfigAxisY", {40, -1, 1}, "Cand. rapidity bins"}; ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {110, 0., 110.}, "axis for centrality"}; ConfigurableAxis thnConfigAxisOccupancy{"thnConfigAxisOccupancy", {14, 0, 14000}, "axis for occupancy"}; ConfigurableAxis thnConfigAxisIR{"thnConfigAxisIR", {5000, 0, 500}, "Interaction rate (kHz)"}; - ConfigurableAxis thnConfigAxisPvContributors{"thnConfigAxisPvContributors", {100, 0., 100.}, "axis for PV contributors"}; + ConfigurableAxis thnConfigAxisPvContributors{"thnConfigAxisPvContributors", {200, 0., 200.}, "axis for PV contributors"}; ConfigurableAxis thnConfigAxisPtBHad{"thnConfigAxisPtBHad", {25, 0., 50}, "axis for pt of B hadron decayed into D candidate"}; ConfigurableAxis thnConfigAxisFlagBHad{"thnConfigAxisFlagBHad", {5, 0., 5}, "axis for PDG of B hadron"}; ConfigurableAxis thnConfigAxisMlScore0{"thnConfigAxisMlScore0", {100, 0., 1.}, "axis for ML output score 0"}; @@ -156,13 +158,12 @@ struct HfTaskDplus { if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { LOGP(fatal, "Only one process function should be enabled! Please check your configuration!"); } - auto vbins = static_cast>(binsPt); - AxisSpec const thnAxisPt = {vbins, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec const thnAxisMass = {thnConfigAxisMass, "inv. mass (K#pi#pi) (GeV/#it{c}^{2})"}; - AxisSpec const thnAxisY = {thnConfigAxisY, "y"}; - AxisSpec const thnAxisMlScore0 = {thnConfigAxisMlScore0, "Score 0"}; - AxisSpec const thnAxisMlScore1 = {thnConfigAxisMlScore1, "Score 1"}; - AxisSpec const thnAxisMlScore2 = {thnConfigAxisMlScore2, "Score 2"}; + AxisSpec const thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec const thnAxisMass{thnConfigAxisMass, "inv. mass (K#pi#pi) (GeV/#it{c}^{2})"}; + AxisSpec const thnAxisY{thnConfigAxisY, "y"}; + AxisSpec const thnAxisMlScore0{thnConfigAxisMlScore0, "Score 0"}; + AxisSpec const thnAxisMlScore1{thnConfigAxisMlScore1, "Score 1"}; + AxisSpec const thnAxisMlScore2{thnConfigAxisMlScore2, "Score 2"}; AxisSpec const thnAxisPtBHad{thnConfigAxisPtBHad, "#it{p}_{T,B} (GeV/#it{c})"}; AxisSpec const thnAxisFlagBHad{thnConfigAxisFlagBHad, "B Hadron flag"}; AxisSpec const thnAxisCent{thnConfigAxisCent, "Centrality"}; @@ -178,43 +179,43 @@ struct HfTaskDplus { AxisSpec const thnAxisZNA{thnConfigAxisZN, "ZNA energy"}; AxisSpec const thnAxisZNC{thnConfigAxisZN, "ZNC energy"}; - registry.add("hMass", "3-prong candidates;inv. mass (#pi K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{350, 1.7, 2.05}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hEta", "3-prong candidates;candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hCt", "3-prong candidates;proper lifetime (D^{#pm}) * #it{c} (cm);entries", {HistType::kTH2F, {{120, -20., 100.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecayLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecayLengthXY", "3-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hNormalisedDecayLengthXY", "3-prong candidates;norm. decay length xy;entries", {HistType::kTH2F, {{80, 0., 80.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hCPA", "3-prong candidates;cos. pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hCPAxy", "3-prong candidates;cos. pointing angle xy;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hImpactParameterXY", "3-prong candidates;impact parameter xy (cm);entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hMaxNormalisedDeltaIP", "3-prong candidates;norm. IP;entries", {HistType::kTH2F, {{200, -20., 20.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hImpactParameterProngSqSum", "3-prong candidates;squared sum of prong imp. par. (cm^{2});entries", {HistType::kTH2F, {{100, 0., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecayLengthError", "3-prong candidates;decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecayLengthXYError", "3-prong candidates;decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hImpactParameterError", "3-prong candidates;impact parameter error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong1", "3-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong2", "3-prong candidates;prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtRecSig", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtRecSigPrompt", "3-prong candidates (matched, prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtRecSigNonPrompt", "3-prong candidates (matched, non-prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtRecBg", "3-prong candidates (unmatched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtGen", "MC particles (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtGenSig", "MC particles (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtGenPrompt", "MC particles (matched, prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtGenNonPrompt", "MC particles (matched, non-prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hPtVsYRecSig_RecoPID", "3-prong candidates (RecoPID - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSigPromptRecoPID", "3-prong candidates (RecoPID - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSigNonPromptRecoPID", "3-prong candidates (RecoPID - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSigRecoTopol", "3-prong candidates (RecoTopol - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSigPromptRecoTopol", "3-prong candidates (RecoTopol - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSigNonPromptRecoTopol", "3-prong candidates (RecoTopol - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSig_RecoSkim", "3-prong candidates (RecoSkim - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSigPrompt_RecoSkim", "3-prong candidates (RecoSkim - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYRecSigNonPrompt_RecoSkim", "3-prong candidates (RecoSkim - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYGen", "MC particles (matched);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYGenPrompt", "MC particles (matched, prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); - registry.add("hPtVsYGenNonPrompt", "MC particles (matched, non-prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, -5., 5.}}}); + registry.add("hMass", "3-prong candidates;inv. mass (#pi K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{350, 1.7, 2.05}, thnAxisPt}}); + registry.add("hEta", "3-prong candidates;candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, thnAxisPt}}); + registry.add("hCt", "3-prong candidates;proper lifetime (D^{#pm}) * #it{c} (cm);entries", {HistType::kTH2F, {{120, -20., 100.}, thnAxisPt}}); + registry.add("hDecayLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 2.}, thnAxisPt}}); + registry.add("hDecayLengthXY", "3-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 2.}, thnAxisPt}}); + registry.add("hNormalisedDecayLengthXY", "3-prong candidates;norm. decay length xy;entries", {HistType::kTH2F, {{80, 0., 80.}, thnAxisPt}}); + registry.add("hCPA", "3-prong candidates;cos. pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, thnAxisPt}}); + registry.add("hCPAxy", "3-prong candidates;cos. pointing angle xy;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, thnAxisPt}}); + registry.add("hImpactParameterXY", "3-prong candidates;impact parameter xy (cm);entries", {HistType::kTH2F, {{200, -1., 1.}, thnAxisPt}}); + registry.add("hMaxNormalisedDeltaIP", "3-prong candidates;norm. IP;entries", {HistType::kTH2F, {{200, -20., 20.}, thnAxisPt}}); + registry.add("hImpactParameterProngSqSum", "3-prong candidates;squared sum of prong imp. par. (cm^{2});entries", {HistType::kTH2F, {{100, 0., 1.}, thnAxisPt}}); + registry.add("hDecayLengthError", "3-prong candidates;decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, thnAxisPt}}); + registry.add("hDecayLengthXYError", "3-prong candidates;decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, thnAxisPt}}); + registry.add("hImpactParameterError", "3-prong candidates;impact parameter error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, thnAxisPt}}); + registry.add("hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, thnAxisPt}}); + registry.add("hd0Prong1", "3-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, thnAxisPt}}); + registry.add("hd0Prong2", "3-prong candidates;prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, thnAxisPt}}); + registry.add("hPtRecSig", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtRecSigPrompt", "3-prong candidates (matched, prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtRecSigNonPrompt", "3-prong candidates (matched, non-prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtRecBg", "3-prong candidates (unmatched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtGen", "MC particles (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtGenSig", "MC particles (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtGenPrompt", "MC particles (matched, prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtGenNonPrompt", "MC particles (matched, non-prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {thnAxisPt}}); + registry.add("hPtVsYRecSig_RecoPID", "3-prong candidates (RecoPID - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSigPromptRecoPID", "3-prong candidates (RecoPID - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSigNonPromptRecoPID", "3-prong candidates (RecoPID - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSigRecoTopol", "3-prong candidates (RecoTopol - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSigPromptRecoTopol", "3-prong candidates (RecoTopol - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSigNonPromptRecoTopol", "3-prong candidates (RecoTopol - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSig_RecoSkim", "3-prong candidates (RecoSkim - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSigPrompt_RecoSkim", "3-prong candidates (RecoSkim - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYRecSigNonPrompt_RecoSkim", "3-prong candidates (RecoSkim - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYGen", "MC particles (matched);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYGenPrompt", "MC particles (matched, prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); + registry.add("hPtVsYGenNonPrompt", "MC particles (matched, non-prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {thnAxisPt, {100, -5., 5.}}}); if (doprocessDataWithMl || doprocessData || doprocessDataWithMlWithUpc || doprocessDataWithUpc) { std::vector axes = {thnAxisMass, thnAxisPt}; @@ -233,6 +234,9 @@ struct HfTaskDplus { if (storeIR) { axes.push_back(thnAxisIR); } + if (storePvContributors) { + axes.push_back(thnAxisPvContributors); + } if (doprocessDataWithMlWithUpc || doprocessDataWithUpc) { axes.push_back(thnAxisGapType); axes.push_back(thnAxisFT0A); @@ -410,10 +414,16 @@ struct HfTaskDplus { } } } else { // Data - if (storeCentrality && storeOccupancy) { + if (storeCentrality && storeOccupancy && storePvContributors) { + registry.fill(HIST("hSparseMass"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality, occupancy, numPvContributors); + } else if (storeCentrality && storeOccupancy && !storePvContributors) { registry.fill(HIST("hSparseMass"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality, occupancy); + } else if (storeCentrality && !storeOccupancy && storePvContributors) { + registry.fill(HIST("hSparseMass"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality, numPvContributors); } else if (storeCentrality && !storeOccupancy) { registry.fill(HIST("hSparseMass"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], centrality); + } else if (!storeCentrality && storeOccupancy && storePvContributors) { + registry.fill(HIST("hSparseMass"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], occupancy, numPvContributors); } else if (!storeCentrality && storeOccupancy) { registry.fill(HIST("hSparseMass"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2], occupancy); } else if (!storeCentrality && !storeOccupancy && storePvContributors) { @@ -559,7 +569,7 @@ struct HfTaskDplus { continue; } - if (storeCentrality || storeOccupancy) { + if (storeCentrality || storeOccupancy || storePvContributors) { auto collision = candidate.template collision_as(); if (storeCentrality && centEstimator != CentralityEstimator::None) { cent = getCentralityColl(collision, centEstimator); @@ -771,7 +781,8 @@ struct HfTaskDplus { int const nAxesCent = storeCentrality ? 1 : 0; // centrality if storeCentrality int const nAxesOcc = storeOccupancy ? 1 : 0; // occupancy if storeOccupancy int const nAxesIR = storeIR ? 1 : 0; // IR if storeIR - int const nAxesTotal = NAxesBase + NAxesMl + nAxesCent + nAxesOcc + nAxesIR; + int const nAxesPv = storePvContributors ? 1 : 0; + int const nAxesTotal = NAxesBase + NAxesMl + nAxesCent + nAxesOcc + nAxesIR + nAxesPv; std::vector valuesToFill; valuesToFill.reserve(nAxesTotal); @@ -797,6 +808,9 @@ struct HfTaskDplus { if (storeIR) { valuesToFill.push_back(ir); } + if (storePvContributors) { + valuesToFill.push_back(static_cast(collision.numContrib())); + } valuesToFill.push_back(static_cast(gap)); valuesToFill.push_back(static_cast(fitInfo.ampFT0A)); valuesToFill.push_back(static_cast(fitInfo.ampFT0C)); @@ -851,7 +865,7 @@ struct HfTaskDplus { } PROCESS_SWITCH(HfTaskDplus, processMcWithMl, "Process MC with ML", false); - void processDataWithUpc(soa::Join const& collisions, + void processDataWithUpc(soa::Join const& collisions, aod::BcFullInfos const& bcs, CandDplusData const& selectedDplusCandidates, aod::Tracks const&, @@ -864,7 +878,7 @@ struct HfTaskDplus { } PROCESS_SWITCH(HfTaskDplus, processDataWithUpc, "Process real data w/o ML with UPC", false); - void processDataWithMlWithUpc(soa::Join const& collisions, + void processDataWithMlWithUpc(soa::Join const& collisions, aod::BcFullInfos const& bcs, CandDplusDataWithMl const& selectedDplusCandidatesMl, aod::Tracks const&, diff --git a/PWGHF/D2H/Tasks/taskDs.cxx b/PWGHF/D2H/Tasks/taskDs.cxx index 0f163718eda..7e79fe0f3ad 100644 --- a/PWGHF/D2H/Tasks/taskDs.cxx +++ b/PWGHF/D2H/Tasks/taskDs.cxx @@ -98,7 +98,10 @@ namespace o2::aod namespace hf_cand_ds_mini { DECLARE_SOA_COLUMN(Pt, pt, float); //! Transverse momentum of D-meson candidate (GeV/c) -DECLARE_SOA_COLUMN(M, m, float); //! Invariant mass of D-meson candidates (GeV/c) +DECLARE_SOA_COLUMN(M, m, float); //! Invariant mass of D-meson candidates (GeV/c2) +DECLARE_SOA_COLUMN(MassKaKa, massKaKa, float); //! Invariant mass of kaon pairs (GeV/c2) +DECLARE_SOA_COLUMN(MassKaFirstPi, massKaFirstPi, float); //! Invariant mass of kaon(first) pion (GeV/c2) +DECLARE_SOA_COLUMN(MassKaSecondPi, massKaSecondPi, float); //! Invariant mass of kaon(second) pion (GeV/c2) DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality of collision DECLARE_SOA_COLUMN(ImpactParameter, impactParameter, float); //! Impact parameter of D-meson candidate DECLARE_SOA_COLUMN(ImpactParameterMc, impactParameterMc, float); //! Generated impact parameter of D-meson candidate @@ -114,6 +117,11 @@ DECLARE_SOA_TABLE(HfCandDsMinis, "AOD", "HFDSMINI", //! Table with few Ds proper hf_cand_ds_mini::Pt, hf_cand_ds_mini::Centrality); +DECLARE_SOA_TABLE(HfCandDsMassMinis, "AOD", "HFDSMASSMINI", //! Table with Ds 2-prong masses + hf_cand_ds_mini::MassKaKa, + hf_cand_ds_mini::MassKaFirstPi, + hf_cand_ds_mini::MassKaSecondPi); + DECLARE_SOA_TABLE(HfCandDsDlMinis, "AOD", "HFDSDLMINI", //! Table with decay length Ds properties hf_cand_ds_mini::DecayLength, hf_cand_ds_mini::DecayLengthXY, @@ -142,6 +150,7 @@ concept HasDsMlInfo = requires(T candidate) { /// Ds± analysis task struct HfTaskDs { Produces hfCandDsMinis; + Produces hfCandDsMassMinis; Produces hfCandDsDlMinis; Produces hfCandDsD0Minis; Produces hfCandDsMcMinis; @@ -183,13 +192,13 @@ struct HfTaskDs { template using MemberFunctionPointer = bool (HfTaskDs::*)(const CandDs&); - using CollisionsWithFT0C = soa::Join; - using CollisionsWithFT0M = soa::Join; + using CollisionsWithFT0C = soa::Join; + using CollisionsWithFT0M = soa::Join; using CollisionsWithNTracksPV = soa::Join; - using CollisionsMc = soa::Join; - using CollisionsMcWithFT0C = soa::Join; - using CollisionsMcWithFT0M = soa::Join; + using CollisionsMc = soa::Join; + using CollisionsMcWithFT0C = soa::Join; + using CollisionsMcWithFT0M = soa::Join; using CollisionsMcWithNTracksPV = soa::Join; using CandDsData = soa::Filtered>; @@ -592,10 +601,22 @@ struct HfTaskDs { template void fillMiniTrees(const Cand& candidate, FinalState finalState) { - auto mass = finalState == FinalState::KKPi ? HfHelper::invMassDsToKKPi(candidate) : HfHelper::invMassDsToPiKK(candidate); + float mass{-1.f}, massKaKa{-1.f}, massKaFirstPi{-1.f}, massKaSecondPi{-1.f}; + if (finalState == FinalState::KKPi) { + mass = HfHelper::invMassDsToKKPi(candidate); + massKaKa = HfHelper::massKKPairDsToKKPi(candidate); + massKaFirstPi = HfHelper::massKFirstPiPairDsToKKPi(candidate); + massKaSecondPi = HfHelper::massKSecondPiPairDsToKKPi(candidate); + } else { + mass = HfHelper::invMassDsToPiKK(candidate); + massKaKa = HfHelper::massKKPairDsToPiKK(candidate); + massKaFirstPi = HfHelper::massKFirstPiPairDsToPiKK(candidate); + massKaSecondPi = HfHelper::massKSecondPiPairDsToPiKK(candidate); + } auto pt = candidate.pt(); hfCandDsMinis(mass, pt, evaluateCentralityCand(candidate)); + hfCandDsMassMinis(massKaKa, massKaFirstPi, massKaSecondPi); if (miniTrees.extendWithDecayLength) { hfCandDsDlMinis(candidate.decayLength(), candidate.decayLengthXY(), candidate.decayLengthNormalised(), candidate.decayLengthXYNormalised()); } @@ -607,10 +628,22 @@ struct HfTaskDs { template void fillMiniTreesMc(const Cand& candidate, DataType dataType, FinalState finalState, const CandDsMcGen& mcParticles, int indexMother) { - auto mass = finalState == FinalState::KKPi ? HfHelper::invMassDsToKKPi(candidate) : HfHelper::invMassDsToPiKK(candidate); + float mass{-1.f}, massKaKa{-1.f}, massKaFirstPi{-1.f}, massKaSecondPi{-1.f}; + if (finalState == FinalState::KKPi) { + mass = HfHelper::invMassDsToKKPi(candidate); + massKaKa = HfHelper::massKKPairDsToKKPi(candidate); + massKaFirstPi = HfHelper::massKFirstPiPairDsToKKPi(candidate); + massKaSecondPi = HfHelper::massKSecondPiPairDsToKKPi(candidate); + } else { + mass = HfHelper::invMassDsToPiKK(candidate); + massKaKa = HfHelper::massKKPairDsToPiKK(candidate); + massKaFirstPi = HfHelper::massKFirstPiPairDsToPiKK(candidate); + massKaSecondPi = HfHelper::massKSecondPiPairDsToPiKK(candidate); + } auto pt = candidate.pt(); hfCandDsMinis(mass, pt, evaluateCentralityCand(candidate)); + hfCandDsMassMinis(massKaKa, massKaFirstPi, massKaSecondPi); if (miniTrees.extendWithDecayLength) { hfCandDsDlMinis(candidate.decayLength(), candidate.decayLengthXY(), candidate.decayLengthNormalised(), candidate.decayLengthXYNormalised()); } @@ -1089,7 +1122,7 @@ struct HfTaskDs { } PROCESS_SWITCH(HfTaskDs, processDataWithCentNTracksPV, "Process data w/o ML information on Ds, with information on centrality from NTracksPV", false); - void processData(soa::Join const& collisions, + void processData(soa::Join const& collisions, CandDsData const& candsDs, aod::BCs const&, aod::Tracks const&) @@ -1125,7 +1158,7 @@ struct HfTaskDs { } PROCESS_SWITCH(HfTaskDs, processDataWithMlAndCentNTracksPV, "Process data with ML information on Ds, with information on centrality", false); - void processDataWithMl(soa::Join const& collisions, + void processDataWithMl(soa::Join const& collisions, CandDsDataWithMl const& candsDs, aod::BCs const&, aod::Tracks const&) diff --git a/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx b/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx index 64dae9f433c..03c39466c18 100644 --- a/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx +++ b/PWGHF/D2H/Tasks/taskDstarToD0Pi.cxx @@ -63,6 +63,7 @@ struct HfTaskDstarToD0Pi { Configurable studyD0ToPiKPi0{"studyD0ToPiKPi0", false, "Flag to study D*->D0(piKpi0)pi channel"}; Configurable ptShapeStudy{"ptShapeStudy", false, "Flag to enable pT shape study"}; Configurable useWeightOnline{"useWeightOnline", false, "Flag to enable use of weights for pT shape study online"}; + Configurable studySoftPiFraction{"studySoftPiFraction", false, "Flag to enable study of soft pion fraction, currently implemented for ML-based analysis only"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -82,7 +83,7 @@ struct HfTaskDstarToD0Pi { std::vector hWeights; int const nWeights = 2; // prompt and non-prompt weights std::vector const weightHistNames = {"promptWeightVsPt", "nonPromptWeightVsPt"}; - enum weightType { + enum WeightType { Prompt = 0, NonPrompt = 1 }; @@ -136,7 +137,7 @@ struct HfTaskDstarToD0Pi { if ((doprocessDataWoML && doprocessDataWML) || (doprocessMcWoMl && doprocessMcWML) || (doprocessDataWoML && doprocessMcWML) || (doprocessDataWML && doprocessMcWoMl)) { LOGP(fatal, "Only Without-ML or With-ML functions should be enabled at a time! Please check your configuration!"); } - auto vecPtBins = (std::vector)ptBins; + auto vecPtBins = std::vector(ptBins); AxisSpec const axisImpactParam = {binningImpactParam, "impact parameter (cm)"}; AxisSpec const axisDecayLength = {binningDecayLength, " decay length (cm)"}; @@ -368,27 +369,27 @@ struct HfTaskDstarToD0Pi { // Ensure hWeights is properly sized hWeights.resize(nWeights); // prompt and non-prompt - hWeights[weightType::Prompt] = dynamic_cast(weightFile->Get(weightHistNames[weightType::Prompt].c_str())); - hWeights[weightType::NonPrompt] = dynamic_cast(weightFile->Get(weightHistNames[weightType::NonPrompt].c_str())); - if (hWeights[weightType::Prompt] == nullptr) { - LOGF(fatal, "Histogram %s not found in weight file!", weightHistNames[weightType::Prompt].c_str()); + hWeights[WeightType::Prompt] = dynamic_cast(weightFile->Get(weightHistNames[WeightType::Prompt].c_str())); + hWeights[WeightType::NonPrompt] = dynamic_cast(weightFile->Get(weightHistNames[WeightType::NonPrompt].c_str())); + if (hWeights[WeightType::Prompt] == nullptr) { + LOGF(fatal, "Histogram %s not found in weight file!", weightHistNames[WeightType::Prompt].c_str()); return; } - if (hWeights[weightType::NonPrompt] == nullptr) { - LOGF(fatal, "Histogram %s not found in weight file!", weightHistNames[weightType::NonPrompt].c_str()); + if (hWeights[WeightType::NonPrompt] == nullptr) { + LOGF(fatal, "Histogram %s not found in weight file!", weightHistNames[WeightType::NonPrompt].c_str()); return; } // checking if bin wdith of weight histograms are not finner than pT axis of Dstar - if (hWeights[weightType::Prompt]->GetXaxis()->GetBinWidth(1) >= vecPtBins[1] - vecPtBins[0]) { + if (hWeights[WeightType::Prompt]->GetXaxis()->GetBinWidth(1) >= vecPtBins[1] - vecPtBins[0]) { LOGF(fatal, "Bin width of weight histogram should be finer than pT axis of Dstar!"); return; } - if (hWeights[weightType::NonPrompt]->GetXaxis()->GetBinWidth(1) >= vecPtBins[1] - vecPtBins[0]) { + if (hWeights[WeightType::NonPrompt]->GetXaxis()->GetBinWidth(1) >= vecPtBins[1] - vecPtBins[0]) { LOGF(fatal, "Bin width of weight histogram should be finer than pT axis of Dstar!"); return; } - hWeights[weightType::Prompt]->SetDirectory(nullptr); - hWeights[weightType::NonPrompt]->SetDirectory(nullptr); + hWeights[WeightType::Prompt]->SetDirectory(nullptr); + hWeights[WeightType::NonPrompt]->SetDirectory(nullptr); weightFile->Close(); delete weightFile; @@ -397,6 +398,10 @@ struct HfTaskDstarToD0Pi { return; } } + + if (studySoftPiFraction && doprocessDataWML) { + registry.add("SoftPiFraction/hPtSoftPiVsPtDtstarVsCentVsBDTScore", "Pt of Soft Pi vs Pt of D* vs Centrality vs BDT Score", {HistType::kTHnSparseF, {{100, 0.1, 1.0}, {vecPtBins, "#it{p}_{T} of Soft Pi (GeV/#it{c})"}, {axisCentrality}, {axisBDTScoreBackground}, {axisBDTScorePrompt}, {axisBDTScoreNonPrompt}}}, true); + } } // Comparator function to sort based on the second argument of a tuple @@ -413,7 +418,7 @@ struct HfTaskDstarToD0Pi { /// @param selectedCands selected candidates with selection flag /// @param preslice preslice to slice template - void runTaskDstar(CollisionsWCent const& cols, T1 selectedCands, T2 preslice) + void runTaskDstar(CollisionsWCent const& cols, const T1& selectedCands, const T2& preslice) { for (const auto& col : cols) { auto nPVContributors = col.numContrib(); @@ -475,6 +480,9 @@ struct HfTaskDstarToD0Pi { if constexpr (ApplyMl) { auto mlBdtScore = candDstar.mlProbDstarToD0Pi(); registry.fill(HIST("Yield/hDeltaInvMassVsPtVsCentVsBDTScore"), deltaMDstar, candDstar.pt(), centrality, mlBdtScore[0], mlBdtScore[1], mlBdtScore[2], invD0); + if (studySoftPiFraction) { + registry.fill(HIST("SoftPiFraction/hPtSoftPiVsPtDtstarVsCentVsBDTScore"), candDstar.ptSoftPi(), candDstar.pt(), centrality, mlBdtScore[0], mlBdtScore[1], mlBdtScore[2]); + } } if (doprocessDataWoML) { @@ -526,11 +534,11 @@ struct HfTaskDstarToD0Pi { /// @tparam T1 type of the candidate table /// @tparam applyMl a boolean to apply ML or not /// @param candsMcRecSel reconstructed candidates with selection flag - /// @param rowsMcPartilces generated particles table + // /// @param rowsMcPartilces generated particles table template - void runMcRecTaskDstar(T1 const& candsMcRecSel, CandDstarMcGen const& rowsMcPartilces) + void runMcRecTaskDstar(T1 const& candsMcRecSel /*, CandDstarMcGen const& rowsMcPartilces*/) { - int8_t signDstar = 0; + // int8_t signDstar = 0; // MC at Reconstruction level for (const auto& candDstarMcRec : candsMcRecSel) { auto ptDstarRecSig = candDstarMcRec.pt(); @@ -543,10 +551,16 @@ struct HfTaskDstarToD0Pi { auto nPVContributors = collision.numContrib(); // number of PV contributors if (std::abs(candDstarMcRec.flagMcMatchRec()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) { // if MC matching is successful at Reconstruction Level // get MC Mother particle - auto prong0 = candDstarMcRec.template prong0_as(); - auto indexMother = RecoDecay::getMother(rowsMcPartilces, prong0.template mcParticle_as(), o2::constants::physics::Pdg::kDStar, true, &signDstar, 2); - auto particleMother = rowsMcPartilces.rawIteratorAt(indexMother); // What is difference between rawIterator() or iteratorAt() methods? - auto ptMother = particleMother.pt(); + // auto prong0 = candDstarMcRec.template prong0_as(); + // auto indexMother = RecoDecay::getMother(rowsMcPartilces, prong0.template mcParticle_as(), o2::constants::physics::Pdg::kDStar, true, &signDstar, 2); + // auto particleMother = rowsMcPartilces.rawIteratorAt(indexMother); // What is difference between rawIterator() or iteratorAt() methods? + // auto ptMother = particleMother.pt(); + auto ptMother = candDstarMcRec.ptBhadMotherPart(); + int const pdgBhadMother = candDstarMcRec.pdgBhadMotherPart(); + // For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia. + if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::NonPrompt && (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557)) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header) + continue; + } if (qaEnabled) { registry.fill(HIST("QA/hPtSkimDstarGenSig"), ptMother); // generator level pt registry.fill(HIST("QA/hPtVsCentSkimDstarGenSig"), ptMother, centrality); @@ -568,8 +582,8 @@ struct HfTaskDstarToD0Pi { LOGF(fatal, "Weight histograms are not initialized or empty. Check CCDB path or weight file."); return; } - ptShapeWeightValues[weightType::Prompt] = hWeights[weightType::Prompt]->GetBinContent(hWeights[weightType::Prompt]->FindBin(ptDstarRecSig)); - ptShapeWeightValues[weightType::NonPrompt] = hWeights[weightType::NonPrompt]->GetBinContent(hWeights[weightType::NonPrompt]->FindBin(ptMother)); + ptShapeWeightValues[WeightType::Prompt] = hWeights[WeightType::Prompt]->GetBinContent(hWeights[WeightType::Prompt]->FindBin(ptDstarRecSig)); + ptShapeWeightValues[WeightType::NonPrompt] = hWeights[WeightType::NonPrompt]->GetBinContent(hWeights[WeightType::NonPrompt]->FindBin(ptMother)); } if (qaEnabled) { @@ -581,9 +595,9 @@ struct HfTaskDstarToD0Pi { auto bdtScore = candDstarMcRec.mlProbDstarToD0Pi(); registry.fill(HIST("Efficiency/hPtVsCentVsBDTScoreVsPvContribRecSig"), ptDstarRecSig, centrality, bdtScore[0], bdtScore[1], bdtScore[2], nPVContributors, weightValue); if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("Efficiency/hPtPromptVsCentVsBDTScorePvContribRecSig"), ptDstarRecSig, centrality, bdtScore[0], bdtScore[1], bdtScore[2], nPVContributors, ptShapeWeightValues[weightType::Prompt]); + registry.fill(HIST("Efficiency/hPtPromptVsCentVsBDTScorePvContribRecSig"), ptDstarRecSig, centrality, bdtScore[0], bdtScore[1], bdtScore[2], nPVContributors, ptShapeWeightValues[WeightType::Prompt]); } else if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("Efficiency/hPtNonPrompRectVsCentVsBDTScorePvContribRecSig"), ptDstarRecSig, centrality, bdtScore[0], bdtScore[1], bdtScore[2], nPVContributors, ptShapeWeightValues[weightType::NonPrompt]); + registry.fill(HIST("Efficiency/hPtNonPrompRectVsCentVsBDTScorePvContribRecSig"), ptDstarRecSig, centrality, bdtScore[0], bdtScore[1], bdtScore[2], nPVContributors, ptShapeWeightValues[WeightType::NonPrompt]); } if (ptShapeStudy && !useWeightOnline) { if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { @@ -596,9 +610,9 @@ struct HfTaskDstarToD0Pi { auto bdtScore = candDstarMcRec.mlProbDstarToD0Pi(); registry.fill(HIST("Efficiency/hPtVsBDTScoreRecSig"), ptDstarRecSig, bdtScore[0], bdtScore[1], bdtScore[2], weightValue); if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("Efficiency/hPtPromptVsBDTScoreRecSig"), ptDstarRecSig, bdtScore[0], bdtScore[1], bdtScore[2], ptShapeWeightValues[weightType::Prompt]); + registry.fill(HIST("Efficiency/hPtPromptVsBDTScoreRecSig"), ptDstarRecSig, bdtScore[0], bdtScore[1], bdtScore[2], ptShapeWeightValues[WeightType::Prompt]); } else if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("Efficiency/hPtNonPromptVsBDTScoreRecSig"), ptDstarRecSig, bdtScore[0], bdtScore[1], bdtScore[2], ptShapeWeightValues[weightType::NonPrompt]); + registry.fill(HIST("Efficiency/hPtNonPromptVsBDTScoreRecSig"), ptDstarRecSig, bdtScore[0], bdtScore[1], bdtScore[2], ptShapeWeightValues[WeightType::NonPrompt]); } if (ptShapeStudy && !useWeightOnline) { if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { @@ -612,9 +626,9 @@ struct HfTaskDstarToD0Pi { if (isCentStudy) { registry.fill(HIST("Efficiency/hPtVsCentVsPvContribRecSig"), ptDstarRecSig, centrality, nPVContributors, weightValue); if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("Efficiency/hPtPromptVsCentVsPvContribRecSig"), ptDstarRecSig, centrality, nPVContributors, ptShapeWeightValues[weightType::Prompt]); + registry.fill(HIST("Efficiency/hPtPromptVsCentVsPvContribRecSig"), ptDstarRecSig, centrality, nPVContributors, ptShapeWeightValues[WeightType::Prompt]); } else if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("Efficiency/hPtNonPromptVsCentVsPvContribRecSig"), ptDstarRecSig, centrality, nPVContributors, ptShapeWeightValues[weightType::NonPrompt]); + registry.fill(HIST("Efficiency/hPtNonPromptVsCentVsPvContribRecSig"), ptDstarRecSig, centrality, nPVContributors, ptShapeWeightValues[WeightType::NonPrompt]); } if (ptShapeStudy && !useWeightOnline) { if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { @@ -626,9 +640,9 @@ struct HfTaskDstarToD0Pi { } else { registry.fill(HIST("Efficiency/hPtVsPvContribRecSig"), ptDstarRecSig, nPVContributors, weightValue); if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("Efficiency/hPtPromptVsPvContribRecSig"), ptDstarRecSig, nPVContributors, ptShapeWeightValues[weightType::Prompt]); + registry.fill(HIST("Efficiency/hPtPromptVsPvContribRecSig"), ptDstarRecSig, nPVContributors, ptShapeWeightValues[WeightType::Prompt]); } else if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("Efficiency/hPtNonPromptVsPvContribRecSig"), ptDstarRecSig, nPVContributors, ptShapeWeightValues[weightType::NonPrompt]); + registry.fill(HIST("Efficiency/hPtNonPromptVsPvContribRecSig"), ptDstarRecSig, nPVContributors, ptShapeWeightValues[WeightType::NonPrompt]); } if (ptShapeStudy && !useWeightOnline) { if (candDstarMcRec.originMcRec() == RecoDecay::OriginType::Prompt) { @@ -737,6 +751,12 @@ struct HfTaskDstarToD0Pi { auto idxBhadMotherPart = mcParticle.idxBhadMotherPart(); auto bMother = rowsMcPartilces.rawIteratorAt(idxBhadMotherPart); auto ptBMother = bMother.pt(); + int const pdgBhadMother = std::abs(bMother.pdgCode()); + + // For unknown reasons there are charm hadrons coming directly from beauty diquarks without an intermediate B-hadron which have an unreasonable correlation between the pT of the charm hadron and the beauty mother. We also remove charm hadrons from quarkonia. + if (mcParticle.originMcGen() == RecoDecay::OriginType::NonPrompt && (pdgBhadMother == 5101 || pdgBhadMother == 5103 || pdgBhadMother == 5201 || pdgBhadMother == 5203 || pdgBhadMother == 5301 || pdgBhadMother == 5303 || pdgBhadMother == 5401 || pdgBhadMother == 5403 || pdgBhadMother == 5503 || pdgBhadMother == 553 || pdgBhadMother == 555 || pdgBhadMother == 557)) { // o2-linter: disable=pdg/explicit-code, magic-number (constants not in the PDG header) + continue; + } auto yGen = RecoDecay::y(mcParticle.pVector(), o2::constants::physics::MassDStar); if (yCandDstarGenMax >= 0. && std::abs(yGen) > yCandDstarGenMax) { @@ -782,8 +802,8 @@ struct HfTaskDstarToD0Pi { LOGF(fatal, "Weight histograms are not initialized or empty. Check CCDB path or weight file."); return; } - ptShapeWeightValues[weightType::Prompt] = hWeights[weightType::Prompt]->GetBinContent(hWeights[weightType::Prompt]->FindBin(ptGen)); - ptShapeWeightValues[weightType::NonPrompt] = hWeights[weightType::NonPrompt]->GetBinContent(hWeights[weightType::NonPrompt]->FindBin(ptBMother)); + ptShapeWeightValues[WeightType::Prompt] = hWeights[WeightType::Prompt]->GetBinContent(hWeights[WeightType::Prompt]->FindBin(ptGen)); + ptShapeWeightValues[WeightType::NonPrompt] = hWeights[WeightType::NonPrompt]->GetBinContent(hWeights[WeightType::NonPrompt]->FindBin(ptBMother)); } registry.fill(HIST("Efficiency/hPtVsYDstarGen"), ptGen, yGen, weightValue); @@ -798,20 +818,20 @@ struct HfTaskDstarToD0Pi { // Prompt if (mcParticle.originMcGen() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("Efficiency/hPtVsYPromptDstarGen"), ptGen, yGen, ptShapeWeightValues[weightType::Prompt]); + registry.fill(HIST("Efficiency/hPtVsYPromptDstarGen"), ptGen, yGen, ptShapeWeightValues[WeightType::Prompt]); if (isCentStudy) { - registry.fill(HIST("Efficiency/hPtPromptVsCentVsPvContribGen"), ptGen, centFT0MGen, pvContributors, ptShapeWeightValues[weightType::Prompt]); + registry.fill(HIST("Efficiency/hPtPromptVsCentVsPvContribGen"), ptGen, centFT0MGen, pvContributors, ptShapeWeightValues[WeightType::Prompt]); } else { - registry.fill(HIST("Efficiency/hPtPromptVsGen"), ptGen, ptShapeWeightValues[weightType::Prompt]); + registry.fill(HIST("Efficiency/hPtPromptVsGen"), ptGen, ptShapeWeightValues[WeightType::Prompt]); } // Non-Prompt } else if (mcParticle.originMcGen() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("Efficiency/hPtVsYNonPromptDstarGen"), ptGen, yGen, ptShapeWeightValues[weightType::NonPrompt]); + registry.fill(HIST("Efficiency/hPtVsYNonPromptDstarGen"), ptGen, yGen, ptShapeWeightValues[WeightType::NonPrompt]); if (isCentStudy) { - registry.fill(HIST("Efficiency/hPtNonPromptVsCentVsPvContribGen"), ptGen, centFT0MGen, pvContributors, ptShapeWeightValues[weightType::NonPrompt]); + registry.fill(HIST("Efficiency/hPtNonPromptVsCentVsPvContribGen"), ptGen, centFT0MGen, pvContributors, ptShapeWeightValues[WeightType::NonPrompt]); } else { - registry.fill(HIST("Efficiency/hPtNonPromptVsGen"), ptGen, ptShapeWeightValues[weightType::NonPrompt]); + registry.fill(HIST("Efficiency/hPtNonPromptVsGen"), ptGen, ptShapeWeightValues[WeightType::NonPrompt]); } } if (ptShapeStudy && !useWeightOnline) { @@ -857,7 +877,7 @@ struct HfTaskDstarToD0Pi { aod::TracksWMc const&) { rowsSelectedCandDstarMcRec.bindExternalIndices(&collisions); - runMcRecTaskDstar>(rowsSelectedCandDstarMcRec, rowsMcPartilces); + runMcRecTaskDstar>(rowsSelectedCandDstarMcRec /*, rowsMcPartilces*/); runMcGenTaskDstar(collisions, rowsMcPartilces); } PROCESS_SWITCH(HfTaskDstarToD0Pi, processMcWoMl, "Process MC Data without ML", false); @@ -868,7 +888,7 @@ struct HfTaskDstarToD0Pi { aod::TracksWMc const&) { rowsSelectedCandDstarMcRecWMl.bindExternalIndices(&collisions); - runMcRecTaskDstar>(rowsSelectedCandDstarMcRecWMl, rowsMcPartilces); + runMcRecTaskDstar>(rowsSelectedCandDstarMcRecWMl /*, rowsMcPartilces*/); runMcGenTaskDstar(collisions, rowsMcPartilces); } PROCESS_SWITCH(HfTaskDstarToD0Pi, processMcWML, "Process MC Data with ML", false); diff --git a/PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx b/PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx new file mode 100644 index 00000000000..dc18bbd9087 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskFlattenicityD0Lc.cxx @@ -0,0 +1,1128 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file taskFlattenicityD0Lc.cxx +/// \brief Analysis of D0/Lambda_c yield as a function of flattenicity +/// \author Laszlo Gyulai, laszlo.gyulai@cern.ch + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::analysis; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_evsel; + +namespace +{ +enum CandTypeSel { + SigD0 = 0, // Signal D0 + SigD0bar, // Signal D0bar + ReflectedD0, // Reflected D0 + ReflectedD0bar, // Reflected D0bar + PureSigD0, // Signal D0 exclude Reflected D0bar + PureSigD0bar // Signal D0bar exclude Reflected D0 +}; +} // namespace + +static const int nCellsFV0 = 48; +static const int nInnerCellsFV0 = 32; +std::array rhoLatticeFV0{}; +std::array rhoLatticeFV0Calibrated{}; +std::array calib = {1.01697, 1.122, 1.03854, 1.108, 1.11634, 1.14971, 1.19321, 1.06866, 0.954675, 0.952695, 0.969853, 0.957557, 0.989784, 1.01549, 1.02182, 0.976005, 1.01865, 1.06871, 1.06264, 1.02969, 1.07378, 1.06622, 1.15057, 1.0433, 0.83654, 0.847178, 0.890027, 0.920814, 0.888271, 1.04662, 0.8869, 0.856348, 0.863181, 0.906312, 0.902166, 1.00122, 1.03303, 0.887866, 0.892437, 0.906278, 0.884976, 0.864251, 0.917221, 1.10618, 1.04028, 0.893184, 0.915734, 0.892676}; +std::map channelsToRings = {{0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 7}, {5, 6}, {6, 5}, {7, 4}, {8, 8}, {9, 9}, {10, 10}, {11, 11}, {12, 15}, {13, 14}, {14, 13}, {15, 12}, {16, 16}, {17, 17}, {18, 18}, {19, 19}, {20, 23}, {21, 22}, {22, 21}, {23, 20}, {24, 24}, {25, 25}, {26, 26}, {27, 27}, {28, 31}, {29, 30}, {30, 29}, {31, 28}, {32, 32}, {33, 34}, {34, 36}, {35, 38}, {36, 47}, {37, 45}, {38, 43}, {39, 41}, {40, 33}, {41, 35}, {42, 37}, {43, 39}, {44, 46}, {45, 44}, {46, 42}, {47, 40}}; + +struct HfTaskFlattenicityD0Lc { + Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; + Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; + Configurable selectionFlagLc{"selectionFlagLc", 1, "Selection Flag for Lc"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable selectionTopol{"selectionTopol", 1, "Selection Flag for topologically selected candidates"}; + Configurable selectionCand{"selectionCand", 1, "Selection Flag for conj. topol. selected candidates"}; + Configurable selectionPid{"selectionPid", 1, "Selection Flag for reco PID candidates"}; + Configurable> binsPt{"binsPt", std::vector{hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable selectionFlagHf{"selectionFlagHf", 1, "Selection Flag for HF flagged candidates"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; + + HfEventSelection hfEvSel; + Service ccdb{}; + + using Collisions = soa::Join; + using CollisionsWithMcLabels = soa::Join; + + using TracksWPid = soa::Join; + using TracksSelQuality = soa::Join; + + using D0Candidates = soa::Filtered>; + using D0CandidatesMc = soa::Filtered>; + + using LcCandidates = soa::Filtered>; + using LcCandidatesMc = soa::Filtered>; + + Filter filterD0 = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_2prong::DecayType::D0ToPiK))) != static_cast(0); + Filter filterLc = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlagLc || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlagLc; + + Preslice candD0PerCollision = aod::hf_cand::collisionId; + Preslice candLcPerCollision = aod::hf_cand::collisionId; + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted colPerMcCollisionLc = aod::mcparticle::mcCollisionId; + + Partition selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + Partition selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isRecoHfFlag >= selectionFlagHf; + + ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {120, 1.5848, 2.1848}, "Cand. inv-mass bins"}; + ConfigurableAxis thnConfigAxisFlat{"thnConfigAxisFlat", {100, 0, 1}, "Flattenicity in event associated with candidate"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {500, 0, 50}, "Cand. pT bins"}; + ConfigurableAxis thnConfigAxisY{"thnConfigAxisY", {20, -1, 1}, "Cand. rapidity bins"}; + ConfigurableAxis thnConfigAxisCandType{"thnConfigAxisCandType", {6, -0.5, 5.5}, "D0 type"}; + ConfigurableAxis thnConfigAxisPtB{"thnConfigAxisPtB", {1000, 0, 100}, "Cand. beauty mother pTB bins"}; + ConfigurableAxis thnConfigAxisOrigin{"thnConfigAxisOrigin", {3, -0.5, 2.5}, "Cand. origin type"}; + ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Number of PV contributors"}; + ConfigurableAxis thnConfigAxisGenPtD{"thnConfigAxisGenPtD", {500, 0, 50}, "Gen Pt D"}; + ConfigurableAxis thnConfigAxisGenPtB{"thnConfigAxisGenPtB", {1000, 0, 100}, "Gen Pt B"}; + ConfigurableAxis thnConfigAxisMassLc{"thnConfigAxisMassLc", {300, 1.98, 2.58}, ""}; + ConfigurableAxis thnConfigAxisCanType{"thnConfigAxisCanType", {5, 0., 5.}, ""}; + + HistogramRegistry registry{"registry"}; + + void init(InitContext const&) + { + hfEvSel.addHistograms(registry); + registry.add("Flattenicity", "Number of events; 1-#rho; Counter", {kTH1F, {{100, 0, 1}}}); + registry.add("Flattenicity_calibrated", "Number of events; 1-#rho; Counter", {kTH1F, {{100, 0, 1}}}); + + auto vbins = binsPt.value; + + const AxisSpec thnAxisMass{thnConfigAxisMass, "inv. mass (#pi K) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisFlat{thnConfigAxisFlat, "#it{#rho}"}; + const AxisSpec thnAxisY{thnConfigAxisY, "y"}; + const AxisSpec thnAxisCandType{thnConfigAxisCandType, "D0 type"}; + const AxisSpec thnAxisPtB{thnConfigAxisPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; + const AxisSpec thnAxisOrigin{thnConfigAxisOrigin, "Origin"}; + const AxisSpec thnAxisGenPtD{thnConfigAxisGenPtD, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisGenPtB{thnConfigAxisGenPtB, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisMassLc{thnConfigAxisMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisCanType{thnConfigAxisCanType, "candidates type"}; + + std::vector axesD0 = {thnAxisMass, thnAxisPt, thnAxisFlat, thnAxisY, thnAxisCandType}; + std::vector axesD0Gen = {thnAxisGenPtD, thnAxisFlat, thnAxisGenPtB, thnAxisY, thnAxisOrigin}; + std::vector axesLcData = {thnAxisMassLc, thnAxisPt, thnAxisFlat}; + std::vector axesLcMc = {thnAxisMassLc, thnAxisPt, thnAxisFlat, thnAxisPtB, thnAxisCanType}; + std::vector axesGenLc = {thnAxisPt, thnAxisFlat, thnAxisPtB, thnAxisCanType}; + + if (doprocessData) { + registry.add("Data/D0/hMass", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0., 5.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hMassFinerBinning", "2-prong candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hMassVsPhi", "2-prong candidates vs phi;inv. mass (#pi K) (GeV/#it{c}^{2});phi (rad);entries", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {32, 0, o2::constants::math::TwoPI}}}); + registry.add("Data/D0/hPtCand", "2-prong candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/D0/hPtProng0", "2-prong candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/D0/hPtProng1", "2-prong candidates;prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/D0/hDecLength", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLengthxy", "2-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLenErr", "2-prong candidates;decay length error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLenXYErr", "2-prong candidates;decay length xy error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hNormalisedDecLength", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{800, 0., 40.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hNormalisedDecLengthxy", "2-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{800, 0., 40.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong0", "2-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong1", "2-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0ErrProng0", "2-prong candidates;prong 0 DCAxy to prim. vertex error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0ErrProng1", "2-prong candidates;prong 1 DCAxy to prim. vertex error (cm);entries", {HistType::kTH2F, {{800, 0., 0.2}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0d0", "2-prong candidates;product of DCAxy to prim. vertex (cm^{2});entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCTS", "2-prong candidates;cos #it{#theta}* (D^{0});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCt", "2-prong candidates;proper lifetime (D^{0}) * #it{c} (cm);entries", {HistType::kTH2F, {{120, -20., 100.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCPA", "2-prong candidates;cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hEta", "2-prong candidates;candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hSelectionStatus", "2-prong candidates;selection status;entries", {HistType::kTH2F, {{5, -0.5, 4.5}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLengthFinerBinning", "2-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{400, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hDecLengthxyFinerBinning", "2-prong candidates;decay length xy (cm);entries", {HistType::kTH2F, {{400, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong0FinerBinning", "2-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0Prong1FinerBinning", "2-prong candidates;prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hd0d0FinerBinning", "2-prong candidates;product of DCAxy to prim. vertex (cm^{2});entries", {HistType::kTH2F, {{500, -0.1, 0.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCTSFinerBinning", "2-prong candidates;cos #it{#theta}* (D^{0});entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCtFinerBinning", "2-prong candidates;proper lifetime (D^{0}) * #it{c} (cm);entries", {HistType::kTH2F, {{500, -0., 100.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCPAFinerBinning", "2-prong candidates;cosine of pointing angle;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/D0/hCPAXYFinerBinning", "2-prong candidates;cosine of pointing angle xy;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("Data/D0/hMassVsPtVsFlatVsYVsD0Type", "Thn for D0 candidates", HistType::kTHnSparseD, axesD0); + + registry.add("Data/Lc/hMass", ";inv. mass (p K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{600, 1.98, 2.58}}}); + registry.add("Data/Lc/hMassVsPt", "Inv.mass vs pt;inv. mass (p K #pi) (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, 1.98, 2.58}, {vbins}}}); + registry.add("Data/Lc/hPt", "#pT of candidates;it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hPtProng0", "pT of prong 0;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hPtProng1", "pT of prong 1;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hPtProng2", "pT of prong 2;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("Data/Lc/hd0Prong0", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("Data/Lc/hd0Prong1", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("Data/Lc/hd0Prong2", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("Data/Lc/hd0VsPtProng0", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("Data/Lc/hd0VsPtProng1", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("Data/Lc/hd0VsPtProng2", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("Data/Lc/hDecLength", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("Data/Lc/hDecLengthVsPt", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("Data/Lc/hDecLengthxy", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("Data/Lc/hDecLengthxyVsPt", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("Data/Lc/hCt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("Data/Lc/hCtVsPt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("Data/Lc/hCPA", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("Data/Lc/hCPAVsPt", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("Data/Lc/hCPAxy", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("Data/Lc/hCPAxyVsPt", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("Data/Lc/hEta", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("Data/Lc/hEtaVsPt", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("Data/Lc/hPhi", "#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("Data/Lc/hPhiVsPt", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("Data/Lc/hSelectionStatus", "3-prong candidates;selection status;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{5, -0.5, 4.5}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("Data/Lc/hImpParErrProng0VsPt", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("Data/Lc/hImpParErrProng1VsPt", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("Data/Lc/hImpParErrProng2VsPt", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("Data/Lc/hDecLenErrVsPt", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + + registry.add("Data/Lc/hLcVsPtVsFlat", "THn for Reconstructed Lambdac candidates for data", HistType::kTHnSparseF, axesLcData); + } + + if (doprocessMCD0) { + registry.add("MC/D0/hPtGenSig", "2-prong candidates (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYRecSigRecoHFFlag", "2-prong candidates (RecoHFFlag - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtGenVsPtRecSig", "2-prong candidates (matched);#it{p}_{T}^{gen.} (GeV/#it{c});#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH2F, {{360, 0., 36.}, {360, 0., 36.}}}); + registry.add("MC/D0/hYGenVsYRecSig", "2-prong candidates (matched);#it{y}^{gen.} ;#it{y}^{rec.} ;entries", {HistType::kTH2F, {{300, -1.5, 1.5}, {300, -1.5, 1.5}}}); + registry.add("MC/D0/hMassVsPtGenVsPtRecSig", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2});#it{p}_{T}^{gen.} (GeV/#it{c});#it{p}_{T}^{rec.} (GeV/#it{c})", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {150, 0., 30.}}}); + registry.add("MC/D0/hCPARecSig", "2-prong candidates (matched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/D0/hEtaRecSig", "2-prong candidates (matched);#it{#eta};entries", {HistType::kTH1F, {{100, -5., 5.}}}); + registry.add("MC/D0/hPtRecBg", "2-prong candidates (unmatched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hCPARecBg", "2-prong candidates (unmatched);cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/D0/hEtaRecBg", "2-prong candidates (unmatched);#it{#eta};entries", {HistType::kTH1F, {{100, -5., 5.}}}); + registry.add("MC/D0/hMassSigBkgD0", "2-prong candidates (not checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtProng0Sig", "prong0 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hPtProng1Sig", "prong1 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthSig", "2-prong candidates (matched);decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthXYSig", "2-prong candidates (matched);decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthSig", "2-prong candidates (matched);normalised decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthXYSig", "2-prong candidates (matched);normalised decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong0Sig", "2-prong candidates (matched);prong 0 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong1Sig", "2-prong candidates (matched);prong 1 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0d0Sig", "2-prong candidates (matched);product of DCAxy to prim. vertex (cm^{2}); #it{y}", {HistType::kTH2F, {{500, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCTSSig", "2-prong candidates (matched);cos #it{#theta}* (D^{0}); #it{y}", {HistType::kTH2F, {{110, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCtSig", "2-prong candidates (matched);proper lifetime (D^{0}) * #it{c} (cm); #it{y}", {HistType::kTH2F, {{120, -20., 100.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPASig", "2-prong candidates (matched);cosine of pointing angle; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPAxySig", "2-prong candidates (matched);cosine of pointing angle xy; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong0VsPtSig", "2-prong candidates;prong 0 DCAxy to prim. vertex (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hd0Prong1VsPtSig", "2-prong candidates;prong 1 DCAxy to prim. vertex (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{500, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hd0d0VsPtSig", "2-prong candidates;product of DCAxy to prim. vertex (cm^{2}) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{500, -0.1, 0.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hCTSVsPtSig", "2-prong candidates;cos #it{#theta}* (D^{0}) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hCPAVsPtSig", "2-prong candidates;cosine of pointing angle vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hCPAXYVsPtSig", "2-prong candidates;cosine of pointing angle xy vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{200, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hNormalisedDecLengthxyVsPtSig", "2-prong candidates;decay length xy (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{800, 0., 40.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hDecLengthVsPtSig", "2-prong candidates;decay length (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hDecLengthxyVsPtSig", "2-prong candidates;decay length xy (cm) vs #it{p}_{T} for signal;entries", {HistType::kTH2F, {{800, 0., 4.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/D0/hMassSigD0", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtProng0Bkg", "prong0 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hPtProng1Bkg", "prong1 pt (matched); #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthBkg", "2-prong candidates (checked);decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hDecLengthXYBkg", "2-prong candidates (checked);decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 2.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthBkg", "2-prong candidates (checked);normalised decay length (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hNormalisedDecLengthXYBkg", "2-prong candidates (checked);normalised decay length xy (cm); #it{y}", {HistType::kTH2F, {{200, 0., 10.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong0Bkg", "2-prong candidates (checked);prong 0 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0Prong1Bkg", "2-prong candidates (checked);prong 1 DCAxy to prim. vertex (cm); #it{y}", {HistType::kTH2F, {{100, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hd0d0Bkg", "2-prong candidates (checked);product of DCAxy to prim. vertex (cm^{2}); #it{y}", {HistType::kTH2F, {{500, -1., 1.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCTSBkg", "2-prong candidates (checked);cos #it{#theta}* (D^{0}); #it{y}", {HistType::kTH2F, {{110, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCtBkg", "2-prong candidates (checked);proper lifetime (D^{0}) * #it{c} (cm); #it{y}", {HistType::kTH2F, {{120, -20., 100.}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPABkg", "2-prong candidates (checked);cosine of pointing angle; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hCPAxyBkg", "2-prong candidates (checked);cosine of pointing angle xy; #it{y}", {HistType::kTH2F, {{440, -1.1, 1.1}, {10, -5., 5.}}}); + registry.add("MC/D0/hMassBkgD0", "2-prong candidates (checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassReflBkgD0", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassSigBkgD0bar", "2-prong candidates (not checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassSigD0bar", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hMassReflBkgD0bar", "2-prong candidates (matched);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtGen", "MC particles (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYGen", "2-prong candidates (matched);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtGenPrompt", "MC particles (matched, prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hYGenPrompt", "MC particles (matched, prompt);#it{y}^{gen.};entries", {HistType::kTH1F, {{300, -1.5, 1.5}}}); + registry.add("MC/D0/hPtVsYGenPrompt", "2-prong candidates (matched, prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtGenNonPrompt", "MC particles (matched, non-prompt);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hYGenNonPrompt", "MC particles (matched, non-prompt);#it{y}^{gen.};entries", {HistType::kTH1F, {{300, -1.5, 1.5}}}); + registry.add("MC/D0/hPtVsYGenNonPrompt", "2-prong candidates (matched, non-prompt);#it{p}_{T}^{gen.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hEtaGen", "MC particles (matched);#it{#eta};entries", {HistType::kTH1F, {{100, -5., 5.}}}); + registry.add("MC/D0/hMassBkgD0bar", "2-prong candidates (checked);#it{m}_{inv} (GeV/#it{c}^{2}); #it{p}_{T}; #it{y}", {HistType::kTH3F, {{120, 1.5848, 2.1848}, {150, 0., 30.}, {20, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigRecoTopol", "2-prong candidates (RecoTopol - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigRecoCand", "2-prong candidates (RecoCand - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSig_RecoPID", "2-prong candidates (RecoPID - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigReco", "2-prong candidates (Reco - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtRecSig", "2-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoHFFlag", "2-prong candidates (RecoHFFlag - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoTopol", "2-prong candidates (RecoTopol - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoCand", "2-prong candidates (RecoCand - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptRecoPID", "2-prong candidates (RecoPID - matched, prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigPromptReco", "2-prong candidates (Reco - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtRecSigPrompt", "2-prong candidates (matched, prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoHFFlag", "2-prong candidates (RecoHFFlag - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoTopol", "2-prong candidates (RecoTopol - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoCand", "2-prong candidates (RecoCand - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptRecoPID", "2-prong candidates (RecoPID - matched, non-prompt);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtVsYRecSigNonPromptReco", "2-prong candidates (Reco - matched);#it{p}_{T}^{rec.}; #it{y}", {HistType::kTH2F, {{360, 0., 36.}, {100, -5., 5.}}}); + registry.add("MC/D0/hPtRecSigNonPrompt", "2-prong candidates (matched, non-prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + + axesD0.push_back(thnAxisPtB); + axesD0.push_back(thnAxisOrigin); + registry.add("MC/D0/hMassVsPtVsFlatVsYVsD0Type", "Thn for D0 candidates", HistType::kTHnSparseD, axesD0); + registry.add("MC/D0/hD0Gen", "Thn for generated D0 from charm and beauty", HistType::kTHnSparseD, axesD0Gen); + } + + if (doprocessMCLc) { + registry.add("MC/Lc/hPtGenSig", "3-prong candidates (matched);#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hMassRecSig", "3-prong candidates (matched);inv. mass (p K #pi) (GeV/#it{c}^{2});", {HistType::kTH1F, {{600, 1.98, 2.58}}}); + registry.add("MC/Lc/hMassVsPtRecSig", "3-prong candidates (matched);inv. mass (p K #pi) (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, 1.98, 2.58}, {vbins}}}); + registry.add("MC/Lc/hPtRecSig", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng0RecSig", ";prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng1RecSig", ";prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng2RecSig", ";prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hd0Prong0RecSig", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong1RecSig", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong2RecSig", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0VsPtProng0RecSig", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng1RecSig", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng2RecSig", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hDecLengthRecSig", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthVsPtRecSig", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLengthxyRecSig", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthxyVsPtRecSig", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hCtRecSig", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("MC/Lc/hCtVsPtRecSig", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("MC/Lc/hCPARecSig", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAVsPtRecSig", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hCPAxyRecSig", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAxyVsPtRecSig", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hEtaRecSig", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hEtaVsPtRecSig", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiRecSig", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hPhiVsPtRecSig", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng0VsPtRecSig", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng1VsPtRecSig", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng2VsPtRecSig", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLenErrVsPtRecSig", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hPtRecSigPrompt", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng0RecSigPrompt", ";prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng1RecSigPrompt", ";prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng2RecSigPrompt", ";prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hd0Prong0RecSigPrompt", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong1RecSigPrompt", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong2RecSigPrompt", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0VsPtProng0RecSigPrompt", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng1RecSigPrompt", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng2RecSigPrompt", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hDecLengthRecSigPrompt", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthVsPtRecSigPrompt", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLengthxyRecSigPrompt", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthxyVsPtRecSigPrompt", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hCtRecSigPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("MC/Lc/hCtVsPtRecSigPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("MC/Lc/hCPARecSigPrompt", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAVsPtRecSigPrompt", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hCPAxyRecSigPrompt", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAxyVsPtRecSigPrompt", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hEtaRecSigPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hEtaVsPtRecSigPrompt", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiRecSigPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hPhiVsPtRecSigPrompt", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng0VsPtRecSigPrompt", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng1VsPtRecSigPrompt", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng2VsPtRecSigPrompt", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLenErrVsPtRecSigPrompt", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hPtRecSigNonPrompt", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng0RecSigNonPrompt", ";prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng1RecSigNonPrompt", ";prong 1 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hPtProng2RecSigNonPrompt", ";prong 2 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hd0Prong0RecSigNonPrompt", ";prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong1RecSigNonPrompt", ";prong 1 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0Prong2RecSigNonPrompt", ";prong 2 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {{600, -0.4, 0.4}}}); + registry.add("MC/Lc/hd0VsPtProng0RecSigNonPrompt", ";prong 0 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng1RecSigNonPrompt", ";prong 1 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hd0VsPtProng2RecSigNonPrompt", ";prong 2 DCAxy to prim. vertex (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{600, -0.4, 0.4}, {vbins}}}); + registry.add("MC/Lc/hDecLengthRecSigNonPrompt", ";decay length (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthVsPtRecSigNonPrompt", ";decay length (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLengthxyRecSigNonPrompt", ";decay length xy (cm);entries", {HistType::kTH1F, {{400, 0., 1.}}}); + registry.add("MC/Lc/hDecLengthxyVsPtRecSigNonPrompt", ";decay length xy (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hCtRecSigNonPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + registry.add("MC/Lc/hCtVsPtRecSigNonPrompt", ";proper lifetime (#Lambda_{c}) * #it{c} (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + registry.add("MC/Lc/hCPARecSigNonPrompt", ";cosine of pointing angle;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAVsPtRecSigNonPrompt", ";cosine of pointing angle;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hCPAxyRecSigNonPrompt", ";cosine of pointing angle xy;entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); + registry.add("MC/Lc/hCPAxyVsPtRecSigNonPrompt", ";cosine of pointing angle xy;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); + registry.add("MC/Lc/hEtaRecSigNonPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hEtaVsPtRecSigNonPrompt", ";candidate #it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiRecSigNonPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hPhiVsPtRecSigNonPrompt", ";candidate #it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng0VsPtRecSigNonPrompt", ";prong 0 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng1VsPtRecSigNonPrompt", ";prong 1 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hImpParErrProng2VsPtRecSigNonPrompt", ";prong 2 impact parameter error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -1., 1.}, {vbins}}}); + registry.add("MC/Lc/hDecLenErrVsPtRecSigNonPrompt", ";decay length error (cm);#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); + registry.add("MC/Lc/hPtGen", ";#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hEtaGen", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hYGen", ";#it{y};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hPhiGen", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hEtaVsPtGen", ";#it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hYVsPtGen", ";#it{y};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiVsPtGen", ";#it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hPtGenPrompt", ";#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hEtaGenPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hYGenPrompt", ";#it{y};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hPhiGenPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hEtaVsPtGenPrompt", ";#it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hYVsPtGenPrompt", ";#it{y};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiVsPtGenPrompt", ";#it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + registry.add("MC/Lc/hPtGenNonPrompt", ";#it{p}_{T}^{gen.} (GeV/#it{c});entries", {HistType::kTH1F, {{360, 0., 36.}}}); + registry.add("MC/Lc/hEtaGenNonPrompt", ";#it{#eta};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hYGenNonPrompt", ";#it{y};entries", {HistType::kTH1F, {{100, -2., 2.}}}); + registry.add("MC/Lc/hPhiGenNonPrompt", ";#it{#Phi};entries", {HistType::kTH1F, {{100, 0., 6.3}}}); + registry.add("MC/Lc/hEtaVsPtGenNonPrompt", ";#it{#eta};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hYVsPtGenNonPrompt", ";#it{y};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, -2., 2.}, {vbins}}}); + registry.add("MC/Lc/hPhiVsPtGenNonPrompt", ";#it{#Phi};#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 6.3}, {vbins}}}); + + registry.add("MC/Lc/hnLcVars", "THn for Reconstructed Lambdac candidates for MC", HistType::kTHnSparseF, axesLcMc); + registry.add("MC/Lc/hnLcVarsGen", "THn for Generated Lambdac", HistType::kTHnSparseF, axesGenLc); + } + + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } + + void processData(Collisions const& collisions, + D0Candidates const&, + LcCandidates const& selectedLcCandidates, + aod::TracksWExtra const&, + aod::BcFullInfos const& bcs, + aod::FV0As const&, + TracksWPid const& tracks) + { + runAnalysisData(collisions, selectedD0Candidates, selectedLcCandidates, bcs, tracks); + } + PROCESS_SWITCH(HfTaskFlattenicityD0Lc, processData, "Process data", true); + + void processMCD0(D0CandidatesMc const&, + soa::Join const& mcParticles2prong, + TracksSelQuality const& tracks, + CollisionsWithMcLabels const& collisions, + aod::McCollisions const& mcCollisions, + aod::FV0As const&, + aod::BcFullInfos const& bcs) + { + runAnalysisMCD0(selectedD0CandidatesMc, mcParticles2prong, tracks, collisions, mcCollisions, bcs); + } + PROCESS_SWITCH(HfTaskFlattenicityD0Lc, processMCD0, "Process MC D0 with DCAFitter", false); + + void processMCLc(soa::Join const& mcParticles3prong, + TracksSelQuality const& tracks, + CollisionsWithMcLabels const& collisions, + aod::McCollisions const& mcCollisions, + aod::FV0As const&, + LcCandidatesMc const& selectedLcCandidatesMc, + aod::BcFullInfos const& bcs) + { + runAnalysisMCLc(selectedLcCandidatesMc, mcParticles3prong, tracks, collisions, mcCollisions, bcs); + } + PROCESS_SWITCH(HfTaskFlattenicityD0Lc, processMCLc, "Process MC Lc with DCAFitter", false); + + template + void runAnalysisData(CollType const& collisions, + CandTypeD0 const& candidatesD0, + CandTypeLc const& candidatesLc, + aod::BcFullInfos const&, + TracksWPid const&) + { + for (const auto& collision : collisions) { + + float centrality{-1.f}; + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + hfEvSel.fillHistograms(collision, rejectionMask, centrality); + if (rejectionMask != 0) { + continue; + } + + const float flat = fillFlat(collision); + const auto thisCollId = collision.globalIndex(); + + // D0 + const auto& groupedD0Candidates = candidatesD0.sliceBy(candD0PerCollision, thisCollId); + for (const auto& candidate : groupedD0Candidates) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yD0(candidate)) > yCandRecoMax) { + continue; + } + + const float massD0 = HfHelper::invMassD0ToPiK(candidate); + const float massD0bar = HfHelper::invMassD0barToKPi(candidate); + const auto ptCandidate = candidate.pt(); + + registry.fill(HIST("Data/D0/hPtCand"), ptCandidate); + registry.fill(HIST("Data/D0/hPtProng0"), candidate.ptProng0()); + registry.fill(HIST("Data/D0/hPtProng1"), candidate.ptProng1()); + registry.fill(HIST("Data/D0/hDecLength"), candidate.decayLength(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLengthxy"), candidate.decayLengthXY(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLenErr"), candidate.errorDecayLength(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLenXYErr"), candidate.errorDecayLengthXY(), ptCandidate); + registry.fill(HIST("Data/D0/hNormalisedDecLength"), candidate.decayLengthNormalised(), ptCandidate); + registry.fill(HIST("Data/D0/hNormalisedDecLengthxy"), candidate.decayLengthXYNormalised(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong0"), candidate.impactParameter0(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong1"), candidate.impactParameter1(), ptCandidate); + registry.fill(HIST("Data/D0/hd0ErrProng0"), candidate.errorImpactParameter0(), ptCandidate); + registry.fill(HIST("Data/D0/hd0ErrProng1"), candidate.errorImpactParameter1(), ptCandidate); + registry.fill(HIST("Data/D0/hd0d0"), candidate.impactParameterProduct(), ptCandidate); + registry.fill(HIST("Data/D0/hCTS"), HfHelper::cosThetaStarD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCt"), HfHelper::ctD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCPA"), candidate.cpa(), ptCandidate); + registry.fill(HIST("Data/D0/hEta"), candidate.eta(), ptCandidate); + registry.fill(HIST("Data/D0/hSelectionStatus"), candidate.isSelD0() + (candidate.isSelD0bar() * 2), ptCandidate); + registry.fill(HIST("Data/D0/hDecLengthFinerBinning"), candidate.decayLength(), ptCandidate); + registry.fill(HIST("Data/D0/hDecLengthxyFinerBinning"), candidate.decayLengthXY(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong0FinerBinning"), candidate.impactParameter0(), ptCandidate); + registry.fill(HIST("Data/D0/hd0Prong1FinerBinning"), candidate.impactParameter1(), ptCandidate); + registry.fill(HIST("Data/D0/hd0d0FinerBinning"), candidate.impactParameterProduct(), ptCandidate); + registry.fill(HIST("Data/D0/hCTSFinerBinning"), HfHelper::cosThetaStarD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCtFinerBinning"), HfHelper::ctD0(candidate), ptCandidate); + registry.fill(HIST("Data/D0/hCPAFinerBinning"), candidate.cpa(), ptCandidate); + registry.fill(HIST("Data/D0/hCPAXYFinerBinning"), candidate.cpaXY(), ptCandidate); + + if (candidate.isSelD0() >= selectionFlagD0) { + registry.fill(HIST("Data/D0/hMass"), massD0, ptCandidate); + registry.fill(HIST("Data/D0/hMassFinerBinning"), massD0, ptCandidate); + registry.fill(HIST("Data/D0/hMassVsPhi"), massD0, ptCandidate, candidate.phi()); + + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, HfHelper::yD0(candidate), SigD0); + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, HfHelper::yD0(candidate), candidate.isSelD0bar() ? ReflectedD0 : PureSigD0); + } + if (candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("Data/D0/hMass"), massD0bar, ptCandidate); + registry.fill(HIST("Data/D0/hMassFinerBinning"), massD0bar, ptCandidate); + registry.fill(HIST("Data/D0/hMassVsPhi"), massD0bar, ptCandidate, candidate.phi()); + + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, HfHelper::yD0(candidate), SigD0bar); + registry.fill(HIST("Data/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, HfHelper::yD0(candidate), candidate.isSelD0() ? ReflectedD0bar : PureSigD0bar); + } + } + + // Lc + const auto& groupedLcCandidates = candidatesLc.sliceBy(candLcPerCollision, thisCollId); + + for (const auto& candidate : groupedLcCandidates) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { + continue; + } + const auto pt = candidate.pt(); + const auto ptProng0 = candidate.ptProng0(); + const auto ptProng1 = candidate.ptProng1(); + const auto ptProng2 = candidate.ptProng2(); + const auto decayLength = candidate.decayLength(); + const auto decayLengthXY = candidate.decayLengthXY(); + const auto cpa = candidate.cpa(); + const auto cpaXY = candidate.cpaXY(); + + if (candidate.isSelLcToPKPi() >= selectionFlagLc) { + registry.fill(HIST("Data/Lc/hMass"), HfHelper::invMassLcToPKPi(candidate)); + registry.fill(HIST("Data/Lc/hMassVsPt"), HfHelper::invMassLcToPKPi(candidate), pt); + } + if (candidate.isSelLcToPiKP() >= selectionFlagLc) { + registry.fill(HIST("Data/Lc/hMass"), HfHelper::invMassLcToPiKP(candidate)); + registry.fill(HIST("Data/Lc/hMassVsPt"), HfHelper::invMassLcToPiKP(candidate), pt); + } + registry.fill(HIST("Data/Lc/hPt"), pt); + registry.fill(HIST("Data/Lc/hPtProng0"), ptProng0); + registry.fill(HIST("Data/Lc/hPtProng1"), ptProng1); + registry.fill(HIST("Data/Lc/hPtProng2"), ptProng2); + registry.fill(HIST("Data/Lc/hd0Prong0"), candidate.impactParameter0()); + registry.fill(HIST("Data/Lc/hd0Prong1"), candidate.impactParameter1()); + registry.fill(HIST("Data/Lc/hd0Prong2"), candidate.impactParameter2()); + registry.fill(HIST("Data/Lc/hd0VsPtProng0"), candidate.impactParameter0(), pt); + registry.fill(HIST("Data/Lc/hd0VsPtProng1"), candidate.impactParameter1(), pt); + registry.fill(HIST("Data/Lc/hd0VsPtProng2"), candidate.impactParameter2(), pt); + registry.fill(HIST("Data/Lc/hDecLength"), decayLength); + registry.fill(HIST("Data/Lc/hDecLengthVsPt"), decayLength, pt); + registry.fill(HIST("Data/Lc/hDecLengthxy"), decayLengthXY); + registry.fill(HIST("Data/Lc/hDecLengthxyVsPt"), decayLengthXY, pt); + registry.fill(HIST("Data/Lc/hCt"), HfHelper::ctLc(candidate)); + registry.fill(HIST("Data/Lc/hCtVsPt"), HfHelper::ctLc(candidate), pt); + registry.fill(HIST("Data/Lc/hCPA"), cpa); + registry.fill(HIST("Data/Lc/hCPAVsPt"), cpa, pt); + registry.fill(HIST("Data/Lc/hCPAxy"), cpaXY); + registry.fill(HIST("Data/Lc/hCPAxyVsPt"), cpaXY, pt); + registry.fill(HIST("Data/Lc/hEta"), candidate.eta()); + registry.fill(HIST("Data/Lc/hEtaVsPt"), candidate.eta(), pt); + registry.fill(HIST("Data/Lc/hPhi"), candidate.phi()); + registry.fill(HIST("Data/Lc/hPhiVsPt"), candidate.phi(), pt); + registry.fill(HIST("Data/Lc/hSelectionStatus"), candidate.isSelLcToPKPi(), pt); + registry.fill(HIST("Data/Lc/hSelectionStatus"), candidate.isSelLcToPiKP(), pt); + registry.fill(HIST("Data/Lc/hImpParErrProng0VsPt"), candidate.errorImpactParameter0(), pt); + registry.fill(HIST("Data/Lc/hImpParErrProng1VsPt"), candidate.errorImpactParameter1(), pt); + registry.fill(HIST("Data/Lc/hImpParErrProng2VsPt"), candidate.errorImpactParameter2(), pt); + registry.fill(HIST("Data/Lc/hDecLenErrVsPt"), candidate.errorDecayLength(), pt); + + auto fillTHnData = [&](bool isPKPi) { + const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + std::vector valuesToFill; + valuesToFill.reserve(registry.get(HIST("Data/Lc/hLcVsPtVsFlat"))->GetNdimensions()); + valuesToFill.insert(valuesToFill.end(), {massLc, pt, flat}); + + registry.get(HIST("Data/Lc/hLcVsPtVsFlat"))->Fill(valuesToFill.data()); + }; + + if (candidate.isSelLcToPKPi() >= selectionFlagLc) { + fillTHnData(true); + } + if (candidate.isSelLcToPiKP() >= selectionFlagLc) { + fillTHnData(false); + } + } + } + } + + template + void runAnalysisMCD0(CandTypeD0 const& candidatesD0, + soa::Join const& mcParticles2prong, + TracksSelQuality const&, + CollType const& collisions, + aod::McCollisions const&, + aod::BcFullInfos const&) + { + // MC rec. + for (const auto& candidate : candidatesD0) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_2prong::DecayType::D0ToPiK)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yD0(candidate)) > yCandRecoMax) { + continue; + } + + auto collision = candidate.template collision_as(); + + const float flat = fillFlat(collision); + + float massD0{0.f}, massD0bar{0.f}; + massD0 = HfHelper::invMassD0ToPiK(candidate); + massD0bar = HfHelper::invMassD0barToKPi(candidate); + + auto trackPos = candidate.template prong0_as(); + if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + auto indexMother = RecoDecay::getMother(mcParticles2prong, trackPos.template mcParticle_as>(), o2::constants::physics::Pdg::kD0, true); + auto particleMother = mcParticles2prong.rawIteratorAt(indexMother); + auto ptGen = particleMother.pt(); + auto yGen = RecoDecay::y(particleMother.pVector(), o2::constants::physics::MassD0); + registry.fill(HIST("MC/D0/hPtGenSig"), ptGen); + auto ptRec = candidate.pt(); + auto yRec = HfHelper::yD0(candidate); + if (candidate.isRecoHfFlag() >= selectionFlagHf) { + registry.fill(HIST("MC/D0/hPtVsYRecSigRecoHFFlag"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtGenVsPtRecSig"), ptGen, ptRec); + registry.fill(HIST("MC/D0/hYGenVsYRecSig"), yGen, yRec); + if (candidate.isSelD0() >= selectionFlagD0) { + registry.fill(HIST("MC/D0/hMassVsPtGenVsPtRecSig"), massD0, ptGen, ptRec); + } + if (candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hMassVsPtGenVsPtRecSig"), massD0bar, ptGen, ptRec); + } + } + + if (candidate.isRecoTopol() >= selectionTopol) { + registry.fill(HIST("MC/D0/hPtVsYRecSigRecoTopol"), ptRec, yRec); + } + if (candidate.isRecoCand() >= selectionCand) { + registry.fill(HIST("MC/D0/hPtVsYRecSigRecoCand"), ptRec, yRec); + } + if (candidate.isRecoPid() >= selectionPid) { + registry.fill(HIST("MC/D0/hPtVsYRecSig_RecoPID"), ptRec, yRec); + } + if (candidate.isSelD0() >= selectionFlagD0 || candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hPtVsYRecSigReco"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtRecSig"), ptRec); + } + if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { + if (candidate.isRecoHfFlag() >= selectionFlagHf) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoHFFlag"), ptRec, yRec); + } + if (candidate.isRecoTopol() >= selectionTopol) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoTopol"), ptRec, yRec); + } + if (candidate.isRecoCand() >= selectionCand) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoCand"), ptRec, yRec); + } + if (candidate.isRecoPid() >= selectionPid) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptRecoPID"), ptRec, yRec); + } + if (candidate.isSelD0() >= selectionFlagD0 || candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hPtVsYRecSigPromptReco"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtRecSigPrompt"), ptRec); + } + } else { + if (candidate.isRecoHfFlag() >= selectionFlagHf) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoHFFlag"), ptRec, yRec); + } + if (candidate.isRecoTopol() >= selectionTopol) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoTopol"), ptRec, yRec); + } + if (candidate.isRecoCand() >= selectionCand) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoCand"), ptRec, yRec); + } + if (candidate.isRecoPid() >= selectionPid) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptRecoPID"), ptRec, yRec); + } + if (candidate.isSelD0() >= selectionFlagD0 || candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hPtVsYRecSigNonPromptReco"), ptRec, yRec); + registry.fill(HIST("MC/D0/hPtRecSigNonPrompt"), ptRec); + } + } + registry.fill(HIST("MC/D0/hCPARecSig"), candidate.cpa()); + registry.fill(HIST("MC/D0/hEtaRecSig"), candidate.eta()); + } else { + registry.fill(HIST("MC/D0/hPtRecBg"), candidate.pt()); + registry.fill(HIST("MC/D0/hCPARecBg"), candidate.cpa()); + registry.fill(HIST("MC/D0/hEtaRecBg"), candidate.eta()); + } + auto ptCandidate = candidate.pt(); + auto ptProng0 = candidate.ptProng0(); + auto ptProng1 = candidate.ptProng1(); + auto rapidityCandidate = HfHelper::yD0(candidate); + auto declengthCandidate = candidate.decayLength(); + auto declengthxyCandidate = candidate.decayLengthXY(); + auto normaliseddeclengthCandidate = candidate.decayLengthNormalised(); + auto normaliseddeclengthxyCandidate = candidate.decayLengthXYNormalised(); + auto d0Prong0 = candidate.impactParameter0(); + auto d0Prong1 = candidate.impactParameter1(); + auto d0d0Candidate = candidate.impactParameterProduct(); + auto ctsCandidate = HfHelper::cosThetaStarD0(candidate); + auto ctCandidate = HfHelper::ctD0(candidate); + auto cpaCandidate = candidate.cpa(); + auto cpaxyCandidate = candidate.cpaXY(); + if (candidate.isSelD0() >= selectionFlagD0) { + registry.fill(HIST("MC/D0/hMassSigBkgD0"), massD0, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hPtProng0Sig"), ptProng0, rapidityCandidate); + registry.fill(HIST("MC/D0/hPtProng1Sig"), ptProng1, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthSig"), declengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthXYSig"), declengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthSig"), normaliseddeclengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthXYSig"), normaliseddeclengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong0Sig"), d0Prong0, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong1Sig"), d0Prong1, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0d0Sig"), d0d0Candidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCTSSig"), ctsCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCtSig"), ctCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPASig"), cpaCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPAxySig"), cpaxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong0VsPtSig"), d0Prong0, ptCandidate); + registry.fill(HIST("MC/D0/hd0Prong1VsPtSig"), d0Prong1, ptCandidate); + registry.fill(HIST("MC/D0/hd0d0VsPtSig"), d0d0Candidate, ptCandidate); + registry.fill(HIST("MC/D0/hCTSVsPtSig"), ctsCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hCPAVsPtSig"), cpaCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hCPAXYVsPtSig"), cpaxyCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthxyVsPtSig"), normaliseddeclengthxyCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hDecLengthVsPtSig"), declengthCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hDecLengthxyVsPtSig"), declengthxyCandidate, ptCandidate); + registry.fill(HIST("MC/D0/hMassSigD0"), massD0, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, rapidityCandidate, SigD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } else { + registry.fill(HIST("MC/D0/hPtProng0Bkg"), ptProng0, rapidityCandidate); + registry.fill(HIST("MC/D0/hPtProng1Bkg"), ptProng1, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthBkg"), declengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hDecLengthXYBkg"), declengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthBkg"), normaliseddeclengthCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hNormalisedDecLengthXYBkg"), normaliseddeclengthxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong0Bkg"), d0Prong0, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0Prong1Bkg"), d0Prong1, rapidityCandidate); + registry.fill(HIST("MC/D0/hd0d0Bkg"), d0d0Candidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCTSBkg"), ctsCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCtBkg"), ctCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPABkg"), cpaCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hCPAxyBkg"), cpaxyCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassBkgD0"), massD0, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hMassReflBkgD0"), massD0, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0, ptCandidate, flat, rapidityCandidate, ReflectedD0, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } + } + } + if (candidate.isSelD0bar() >= selectionFlagD0bar) { + registry.fill(HIST("MC/D0/hMassSigBkgD0bar"), massD0bar, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hMassSigD0bar"), massD0bar, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, rapidityCandidate, SigD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } else { + registry.fill(HIST("MC/D0/hMassBkgD0bar"), massD0bar, ptCandidate, rapidityCandidate); + if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + registry.fill(HIST("MC/D0/hMassReflBkgD0bar"), massD0bar, ptCandidate, rapidityCandidate); + registry.fill(HIST("MC/D0/hMassVsPtVsFlatVsYVsD0Type"), massD0bar, ptCandidate, flat, rapidityCandidate, ReflectedD0bar, candidate.ptBhadMotherPart(), candidate.originMcRec()); + } + } + } + } + + // MC gen. + for (const auto& particle : mcParticles2prong) { + if (std::abs(particle.flagMcMatchGen()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + if (yCandGenMax >= 0. && std::abs(RecoDecay::y(particle.pVector(), o2::constants::physics::MassD0)) > yCandGenMax) { + continue; + } + + float flat{-1.f}; + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, particle.mcCollision().globalIndex()); + for (const auto& recCol : recoCollsPerMcColl) { + flat = fillFlat(recCol); + } + + float ptGenB = -1; + auto ptGen = particle.pt(); + auto yGen = RecoDecay::y(particle.pVector(), o2::constants::physics::MassD0); + registry.fill(HIST("MC/D0/hPtGen"), ptGen); + registry.fill(HIST("MC/D0/hPtVsYGen"), ptGen, yGen); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("MC/D0/hPtGenPrompt"), ptGen); + registry.fill(HIST("MC/D0/hYGenPrompt"), yGen); + registry.fill(HIST("MC/D0/hPtVsYGenPrompt"), ptGen, yGen); + registry.fill(HIST("MC/D0/hD0Gen"), ptGen, flat, ptGenB, yGen, 1); + } else { + ptGenB = mcParticles2prong.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("MC/D0/hPtGenNonPrompt"), ptGen); + registry.fill(HIST("MC/D0/hYGenNonPrompt"), yGen); + registry.fill(HIST("MC/D0/hPtVsYGenNonPrompt"), ptGen, yGen); + registry.fill(HIST("MC/D0/hD0Gen"), ptGen, flat, ptGenB, yGen, 2); + } + registry.fill(HIST("MC/D0/hEtaGen"), particle.eta()); + } + } + } + + template + void runAnalysisMCLc(CandTypeLc const& candidatesLc, + soa::Join const& mcParticles3prong, + TracksSelQuality const&, + CollType const& collisions, + aod::McCollisions const&, + aod::BcFullInfos const&) + { + for (const auto& collision : collisions) { + // MC Rec. + const auto thisCollId = collision.globalIndex(); + const auto& groupedLcCandidates = candidatesLc.sliceBy(candLcPerCollision, thisCollId); + + const float flat = fillFlat(collision); + + for (const auto& candidate : groupedLcCandidates) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { + continue; + } + + if (std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as>(); + const auto pdgCodeProng0 = std::abs(mcParticleProng0.pdgCode()); + const auto indexMother = RecoDecay::getMother(mcParticles3prong, mcParticleProng0, o2::constants::physics::Pdg::kLambdaCPlus, true); + const auto particleMother = mcParticles3prong.rawIteratorAt(indexMother); + registry.fill(HIST("MC/Lc/hPtGenSig"), particleMother.pt()); + + const auto pt = candidate.pt(); + const auto originType = candidate.originMcRec(); + const auto ptRecB = candidate.ptBhadMotherPart(); + + if ((candidate.isSelLcToPKPi() >= selectionFlagLc) && pdgCodeProng0 == kProton) { + registry.fill(HIST("MC/Lc/hMassRecSig"), HfHelper::invMassLcToPKPi(candidate)); + registry.fill(HIST("MC/Lc/hMassVsPtRecSig"), HfHelper::invMassLcToPKPi(candidate), candidate.pt()); + } + if ((candidate.isSelLcToPiKP() >= selectionFlagLc) && pdgCodeProng0 == kPiPlus) { + registry.fill(HIST("MC/Lc/hMassRecSig"), HfHelper::invMassLcToPiKP(candidate)); + registry.fill(HIST("MC/Lc/hMassVsPtRecSig"), HfHelper::invMassLcToPiKP(candidate), candidate.pt()); + } + registry.fill(HIST("MC/Lc/hPtRecSig"), candidate.pt()); + registry.fill(HIST("MC/Lc/hPtProng0RecSig"), candidate.ptProng0()); + registry.fill(HIST("MC/Lc/hPtProng1RecSig"), candidate.ptProng1()); + registry.fill(HIST("MC/Lc/hPtProng2RecSig"), candidate.ptProng2()); + registry.fill(HIST("MC/Lc/hd0Prong0RecSig"), candidate.impactParameter0()); + registry.fill(HIST("MC/Lc/hd0Prong1RecSig"), candidate.impactParameter1()); + registry.fill(HIST("MC/Lc/hd0Prong2RecSig"), candidate.impactParameter2()); + registry.fill(HIST("MC/Lc/hd0VsPtProng0RecSig"), candidate.impactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng1RecSig"), candidate.impactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng2RecSig"), candidate.impactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthRecSig"), candidate.decayLength()); + registry.fill(HIST("MC/Lc/hDecLengthVsPtRecSig"), candidate.decayLength(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthxyRecSig"), candidate.decayLengthXY()); + registry.fill(HIST("MC/Lc/hDecLengthxyVsPtRecSig"), candidate.decayLengthXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCtRecSig"), HfHelper::ctLc(candidate)); + registry.fill(HIST("MC/Lc/hCtVsPtRecSig"), HfHelper::ctLc(candidate), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPARecSig"), candidate.cpa()); + registry.fill(HIST("MC/Lc/hCPAVsPtRecSig"), candidate.cpa(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPAxyRecSig"), candidate.cpaXY()); + registry.fill(HIST("MC/Lc/hCPAxyVsPtRecSig"), candidate.cpaXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hEtaRecSig"), candidate.eta()); + registry.fill(HIST("MC/Lc/hEtaVsPtRecSig"), candidate.eta(), candidate.pt()); + registry.fill(HIST("MC/Lc/hPhiRecSig"), candidate.phi()); + registry.fill(HIST("MC/Lc/hPhiVsPtRecSig"), candidate.phi(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng0VsPtRecSig"), candidate.errorImpactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng1VsPtRecSig"), candidate.errorImpactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng2VsPtRecSig"), candidate.errorImpactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLenErrVsPtRecSig"), candidate.errorDecayLength(), candidate.pt()); + + if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("MC/Lc/hPtRecSigPrompt"), candidate.pt()); + registry.fill(HIST("MC/Lc/hPtProng0RecSigPrompt"), candidate.ptProng0()); + registry.fill(HIST("MC/Lc/hPtProng1RecSigPrompt"), candidate.ptProng1()); + registry.fill(HIST("MC/Lc/hPtProng2RecSigPrompt"), candidate.ptProng2()); + registry.fill(HIST("MC/Lc/hd0Prong0RecSigPrompt"), candidate.impactParameter0()); + registry.fill(HIST("MC/Lc/hd0Prong1RecSigPrompt"), candidate.impactParameter1()); + registry.fill(HIST("MC/Lc/hd0Prong2RecSigPrompt"), candidate.impactParameter2()); + registry.fill(HIST("MC/Lc/hd0VsPtProng0RecSigPrompt"), candidate.impactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng1RecSigPrompt"), candidate.impactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng2RecSigPrompt"), candidate.impactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthRecSigPrompt"), candidate.decayLength()); + registry.fill(HIST("MC/Lc/hDecLengthVsPtRecSigPrompt"), candidate.decayLength(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthxyRecSigPrompt"), candidate.decayLengthXY()); + registry.fill(HIST("MC/Lc/hDecLengthxyVsPtRecSigPrompt"), candidate.decayLengthXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCtRecSigPrompt"), HfHelper::ctLc(candidate)); + registry.fill(HIST("MC/Lc/hCtVsPtRecSigPrompt"), HfHelper::ctLc(candidate), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPARecSigPrompt"), candidate.cpa()); + registry.fill(HIST("MC/Lc/hCPAVsPtRecSigPrompt"), candidate.cpa(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPAxyRecSigPrompt"), candidate.cpaXY()); + registry.fill(HIST("MC/Lc/hCPAxyVsPtRecSigPrompt"), candidate.cpaXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hEtaRecSigPrompt"), candidate.eta()); + registry.fill(HIST("MC/Lc/hEtaVsPtRecSigPrompt"), candidate.eta(), candidate.pt()); + registry.fill(HIST("MC/Lc/hPhiRecSigPrompt"), candidate.phi()); + registry.fill(HIST("MC/Lc/hPhiVsPtRecSigPrompt"), candidate.phi(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng0VsPtRecSigPrompt"), candidate.errorImpactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng1VsPtRecSigPrompt"), candidate.errorImpactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng2VsPtRecSigPrompt"), candidate.errorImpactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLenErrVsPtRecSigPrompt"), candidate.errorDecayLength(), candidate.pt()); + } else if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("MC/Lc/hPtRecSigNonPrompt"), candidate.pt()); + registry.fill(HIST("MC/Lc/hPtProng0RecSigNonPrompt"), candidate.ptProng0()); + registry.fill(HIST("MC/Lc/hPtProng1RecSigNonPrompt"), candidate.ptProng1()); + registry.fill(HIST("MC/Lc/hPtProng2RecSigNonPrompt"), candidate.ptProng2()); + registry.fill(HIST("MC/Lc/hd0Prong0RecSigNonPrompt"), candidate.impactParameter0()); + registry.fill(HIST("MC/Lc/hd0Prong1RecSigNonPrompt"), candidate.impactParameter1()); + registry.fill(HIST("MC/Lc/hd0Prong2RecSigNonPrompt"), candidate.impactParameter2()); + registry.fill(HIST("MC/Lc/hd0VsPtProng0RecSigNonPrompt"), candidate.impactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng1RecSigNonPrompt"), candidate.impactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hd0VsPtProng2RecSigNonPrompt"), candidate.impactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthRecSigNonPrompt"), candidate.decayLength()); + registry.fill(HIST("MC/Lc/hDecLengthVsPtRecSigNonPrompt"), candidate.decayLength(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLengthxyRecSigNonPrompt"), candidate.decayLengthXY()); + registry.fill(HIST("MC/Lc/hDecLengthxyVsPtRecSigNonPrompt"), candidate.decayLengthXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCtRecSigNonPrompt"), HfHelper::ctLc(candidate)); + registry.fill(HIST("MC/Lc/hCtVsPtRecSigNonPrompt"), HfHelper::ctLc(candidate), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPARecSigNonPrompt"), candidate.cpa()); + registry.fill(HIST("MC/Lc/hCPAVsPtRecSigNonPrompt"), candidate.cpa(), candidate.pt()); + registry.fill(HIST("MC/Lc/hCPAxyRecSigNonPrompt"), candidate.cpaXY()); + registry.fill(HIST("MC/Lc/hCPAxyVsPtRecSigNonPrompt"), candidate.cpaXY(), candidate.pt()); + registry.fill(HIST("MC/Lc/hEtaRecSigNonPrompt"), candidate.eta()); + registry.fill(HIST("MC/Lc/hEtaVsPtRecSigNonPrompt"), candidate.eta(), candidate.pt()); + registry.fill(HIST("MC/Lc/hPhiRecSigNonPrompt"), candidate.phi()); + registry.fill(HIST("MC/Lc/hPhiVsPtRecSigNonPrompt"), candidate.phi(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng0VsPtRecSigNonPrompt"), candidate.errorImpactParameter0(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng1VsPtRecSigNonPrompt"), candidate.errorImpactParameter1(), candidate.pt()); + registry.fill(HIST("MC/Lc/hImpParErrProng2VsPtRecSigNonPrompt"), candidate.errorImpactParameter2(), candidate.pt()); + registry.fill(HIST("MC/Lc/hDecLenErrVsPtRecSigNonPrompt"), candidate.errorDecayLength(), candidate.pt()); + } + + if ((candidate.isSelLcToPKPi() >= selectionFlagLc) && pdgCodeProng0 == kProton) { + const auto massLc = HfHelper::invMassLcToPKPi(candidate); + std::vector valuesToFill; + valuesToFill.reserve(registry.get(HIST("MC/Lc/hnLcVars"))->GetNdimensions()); + valuesToFill.insert(valuesToFill.end(), {massLc, pt, flat, ptRecB, static_cast(originType)}); + registry.get(HIST("MC/Lc/hnLcVars"))->Fill(valuesToFill.data()); + } + if ((candidate.isSelLcToPiKP() >= selectionFlagLc) && pdgCodeProng0 == kPiPlus) { + const auto massLc = HfHelper::invMassLcToPiKP(candidate); + std::vector valuesToFill; + valuesToFill.reserve(registry.get(HIST("MC/Lc/hnLcVars"))->GetNdimensions()); + valuesToFill.insert(valuesToFill.end(), {massLc, pt, flat, ptRecB, static_cast(originType)}); + registry.get(HIST("MC/Lc/hnLcVars"))->Fill(valuesToFill.data()); + } + } + } + } + + // MC gen. + for (const auto& particle : mcParticles3prong) { + if (std::abs(particle.flagMcMatchGen()) == hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + auto yGen = RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus); + if (yCandGenMax >= 0. && std::abs(yGen) > yCandGenMax) { + continue; + } + + float flat{-1.f}; + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollisionLc, particle.mcCollision().globalIndex()); + for (const auto& recCol : recoCollsPerMcColl) { + flat = fillFlat(recCol); + } + + const auto ptGen = particle.pt(); + const auto originType = particle.originMcGen(); + float ptGenB = -1.; + + registry.fill(HIST("MC/Lc/hPtGen"), particle.pt()); + registry.fill(HIST("MC/Lc/hEtaGen"), particle.eta()); + registry.fill(HIST("MC/Lc/hYGen"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)); + registry.fill(HIST("MC/Lc/hPhiGen"), particle.phi()); + registry.fill(HIST("MC/Lc/hEtaVsPtGen"), particle.eta(), particle.pt()); + registry.fill(HIST("MC/Lc/hYVsPtGen"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus), particle.pt()); + registry.fill(HIST("MC/Lc/hPhiVsPtGen"), particle.phi(), particle.pt()); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + ptGenB = -1.; + std::vector valuesToFill{ptGen, flat, ptGenB, static_cast(originType)}; + registry.get(HIST("MC/Lc/hnLcVarsGen"))->Fill(valuesToFill.data()); + + registry.fill(HIST("MC/Lc/hPtGenPrompt"), particle.pt()); + registry.fill(HIST("MC/Lc/hEtaGenPrompt"), particle.eta()); + registry.fill(HIST("MC/Lc/hYGenPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)); + registry.fill(HIST("MC/Lc/hPhiGenPrompt"), particle.phi()); + registry.fill(HIST("MC/Lc/hEtaVsPtGenPrompt"), particle.eta(), particle.pt()); + registry.fill(HIST("MC/Lc/hYVsPtGenPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus), particle.pt()); + registry.fill(HIST("MC/Lc/hPhiVsPtGenPrompt"), particle.phi(), particle.pt()); + } else if (particle.originMcGen() == RecoDecay::OriginType::NonPrompt) { + ptGenB = mcParticles3prong.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + std::vector valuesToFill{ptGen, flat, ptGenB, static_cast(originType)}; + registry.get(HIST("MC/Lc/hnLcVarsGen"))->Fill(valuesToFill.data()); + + registry.fill(HIST("MC/Lc/hPtGenNonPrompt"), particle.pt()); + registry.fill(HIST("MC/Lc/hEtaGenNonPrompt"), particle.eta()); + registry.fill(HIST("MC/Lc/hYGenNonPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)); + registry.fill(HIST("MC/Lc/hPhiGenNonPrompt"), particle.phi()); + registry.fill(HIST("MC/Lc/hEtaVsPtGenNonPrompt"), particle.eta(), particle.pt()); + registry.fill(HIST("MC/Lc/hYVsPtGenNonPrompt"), RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus), particle.pt()); + registry.fill(HIST("MC/Lc/hPhiVsPtGenNonPrompt"), particle.phi(), particle.pt()); + } + } + } + } + + template + float fillFlat(CollType const& collision) + { + rhoLatticeFV0.fill(0); + rhoLatticeFV0Calibrated.fill(0); + float flat = 9999; + if (collision.has_foundFV0()) { + auto fv0 = collision.foundFV0(); + std::bitset<8> fV0Triggers = fv0.triggerMask(); + bool isOkFV0OrA = fV0Triggers[o2::fit::Triggers::bitA]; + if (isOkFV0OrA) { + for (std::size_t ich = 0; ich < fv0.channel().size(); ich++) { + float amplCh = fv0.amplitude()[ich]; + float amplChCalibrated = fv0.amplitude()[ich]; + int chv0 = fv0.channel()[ich]; + int chv0phi = channelsToRings.at(chv0); + if (amplCh > 0.0) { + if (chv0phi > 0.0) { + amplChCalibrated *= calib[chv0phi]; + if (chv0 < nInnerCellsFV0) { + rhoLatticeFV0[chv0phi] += amplCh; + rhoLatticeFV0Calibrated[chv0phi] += amplChCalibrated; + } else { + rhoLatticeFV0[chv0phi] += amplCh / 2.; + rhoLatticeFV0Calibrated[chv0phi] += amplChCalibrated / 2.; + } + } + } + } + float flattenicityFV0 = calcFlatenicity(rhoLatticeFV0); + float flattenicityFV0Calibrated = calcFlatenicity(rhoLatticeFV0Calibrated); + if constexpr (fillHist) { + registry.fill(HIST("Flattenicity_calibrated"), 1 - flattenicityFV0Calibrated); + registry.fill(HIST("Flattenicity"), 1 - flattenicityFV0); + } + flat = 1. - flattenicityFV0; + } + } + return flat; + } + + template + float calcFlatenicity(std::array const& signals) + { + static_assert(S != 0); + + int entries = signals.size(); + float flat{-1}; + float mRho{0}; + for (int iCell = 0; iCell < entries; ++iCell) { + if (signals[iCell] > 0.0) { + mRho += 1.0 * signals[iCell]; + } + } + mRho /= (1.0 * entries); + if (mRho <= 0) { + return -1; + } + float sRhoTmp{0}; + float sRho{0}; + for (int iCell = 0; iCell < entries; ++iCell) { + if (signals[iCell] > 0.0) { + sRhoTmp += std::pow(1.0 * signals[iCell] - mRho, 2); + } + } + sRhoTmp /= (1.0 * entries * entries); + sRho = std::sqrt(sRhoTmp); + if (mRho > 0.0) { + flat = sRho / mRho; + } else { + flat = -1; + } + return flat; + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx index 78462e10a73..c7b099f64ea 100644 --- a/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx +++ b/PWGHF/D2H/Tasks/taskFlowCharmHadrons.cxx @@ -28,6 +28,7 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/Qvectors.h" #include @@ -52,6 +53,7 @@ #include #include #include +#include #include #include @@ -75,6 +77,8 @@ DECLARE_SOA_COLUMN(MlScore0, mlScore0, float); //! ML score of the first con DECLARE_SOA_COLUMN(MlScore1, mlScore1, float); //! ML score of the second configured index DECLARE_SOA_COLUMN(ScalarProd, scalarProd, float); //! Scalar product DECLARE_SOA_COLUMN(Cent, cent, float); //! Centrality +DECLARE_SOA_COLUMN(PvContrib, pvContrib, float); //! Number of primary vertex contributors +DECLARE_SOA_COLUMN(RedQVec, redQVec, float); //! Reduced Q-vector } // namespace full DECLARE_SOA_TABLE(HfCandMPtInfos, "AOD", "HFCANDMPTINFO", full::M, @@ -89,6 +93,22 @@ DECLARE_SOA_TABLE(HfCandFlowInfos, "AOD", "HFCANDFLOWINFO", full::MlScore1, full::ScalarProd, full::Cent); +DECLARE_SOA_TABLE(HfCandFlowPvs, "AOD", "HFCANDFLOWPV", + full::M, + full::Pt, + full::MlScore0, + full::MlScore1, + full::ScalarProd, + full::Cent, + full::PvContrib); +DECLARE_SOA_TABLE(HfCandFlowEses, "AOD", "HFCANDFLOWESE", + full::M, + full::Pt, + full::MlScore0, + full::MlScore1, + full::ScalarProd, + full::Cent, + full::RedQVec); } // namespace o2::aod enum DecayChannel { DplusToPiKPi = 0, @@ -100,23 +120,37 @@ enum DecayChannel { DplusToPiKPi = 0, LcToPiKP, XicToPKPi, XicToPiKP, - Xic0ToXiPi + Xic0ToXiPi, + DstarToD0Pi +}; + +enum RunMode { + kSP = 0, + kEsE, + kSPEsE, }; struct HfTaskFlowCharmHadrons { Produces rowCandMassPtMl; Produces rowCandMassPtMlSpCent; + Produces rowCandFlowEsE; + Produces rowCandMassPtMlSpCentPv; Configurable harmonic{"harmonic", 2, "harmonic number"}; Configurable qVecDetector{"qVecDetector", 3, "Detector for Q vector estimation (FV0A: 0, FT0M: 1, FT0A: 2, FT0C: 3, TPC Pos: 4, TPC Neg: 5, TPC Tot: 6)"}; + Configurable qVecRedDetector{"qVecRedDetector", 6, "Detector for Q vector estimation (FT0C: 3, TPC Pos: 4, TPC Neg: 5, TPC Tot: 6)"}; Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4, NTracksPV: 5, FT0CVariant2: 6)"}; Configurable selectionFlag{"selectionFlag", 1, "Selection Flag for hadron (e.g. 1 for skimming, 3 for topo. and kine., 7 for PID)"}; Configurable centralityMin{"centralityMin", 0., "Minimum centrality accepted in SP/EP computation (not applied in resolution process)"}; Configurable centralityMax{"centralityMax", 100., "Maximum centrality accepted in SP/EP computation (not applied in resolution process)"}; Configurable storeEP{"storeEP", false, "Flag to store EP-related axis"}; Configurable storeMl{"storeMl", false, "Flag to store ML scores"}; + Configurable storeSPQVec{"storeSPQVec", true, "Flag to store the Q-vectors for SP"}; + Configurable storeRedQVec{"storeRedQVec", false, "Flag to store reduced Q-vectors for ESE"}; + Configurable useRedQVecDaugsSubtr{"useRedQVecDaugsSubtr", true, "Flag to subtract daughter tracks from reduced Q-vectors for ESE"}; Configurable fillMassPtMlTree{"fillMassPtMlTree", false, "Flag to fill mass, pt and ML scores tree"}; Configurable fillMassPtMlSpCentTree{"fillMassPtMlSpCentTree", false, "Flag to fill mass, pt, ML scores, SP and centrality tree"}; + Configurable fillMassPtMlSpCentPvTree{"fillMassPtMlSpCentPvTree", false, "Flag to fill mass, pt, ML scores, SP, centrality, PV contrib tree"}; Configurable fillSparse{"fillSparse", true, "Flag to fill sparse"}; Configurable downSampleFactor{"downSampleFactor", 1., "Fraction of candidates to keep in TTree"}; Configurable ptDownSampleMax{"ptDownSampleMax", 10., "Maximum pt for the application of the downsampling factor"}; @@ -129,75 +163,76 @@ struct HfTaskFlowCharmHadrons { Configurable occEstimator{"occEstimator", 0, "Occupancy estimation (0: None, 1: ITS, 2: FT0C)"}; Configurable saveEpResoHisto{"saveEpResoHisto", false, "Flag to save event plane resolution histogram"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbResoHistPath{"ccdbResoHistPath", "", "path to the resolution histogram in the ccdb"}; + Configurable useOnlineResoCorrection{"useOnlineResoCorrection", false, "True to fill candidate histograms with SP / R"}; + Configurable resoTripletPath{"resoTripletPath", "FT0c_FV0a_TPCtot", "Detector resolution triplet for online correction"}; Configurable> classMl{"classMl", {0, 2}, "Indexes of BDT scores to be stored. Two indexes max."}; EventPlaneHelper epHelper; HfEventSelection hfEvSel; // event selection and monitoring o2::framework::Service ccdb{}; SliceCache cache; + HfQVectorResoHelper qVecResoHelper; + int lastRunNumber{-1}; + float scalarProdReso{-1.f}; // resolution value for online correction using CandDsDataWMl = soa::Filtered>; - using CandDsData = soa::Filtered>; using CandDplusDataWMl = soa::Filtered>; - using CandDplusData = soa::Filtered>; - using CandLcData = soa::Filtered>; using CandLcDataWMl = soa::Filtered>; - using CandXicData = soa::Filtered>; using CandXicDataWMl = soa::Filtered>; using CandXic0Data = soa::Filtered>; using CandXic0DataWMl = soa::Filtered>; using CandD0DataWMl = soa::Filtered>; - using CandD0Data = soa::Filtered>; - using CollsWithQvecs = soa::Join; + using CandDstarDataWMl = soa::Filtered>; + using CollsWithSPQvecs = soa::Join; + using CollsWithEsEQvecs = soa::Join; + using CollsWithSPEsEQvecs = soa::Join; using TracksWithExtra = soa::Join; Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; + Filter filterSelectDstarCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Filter filterSelectXicCandidates = aod::hf_sel_candidate_xic::isSelXicToPKPi >= selectionFlag || aod::hf_sel_candidate_xic::isSelXicToPiKP >= selectionFlag; Filter filterSelectXic0Candidates = aod::hf_sel_toxipi::resultSelections == true; - Partition selectedDsToKKPi = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag; - Partition selectedDsToPiKK = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Partition selectedDsToKKPiWMl = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag; Partition selectedDsToPiKKWMl = aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; - Partition selectedD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; - Partition selectedD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; Partition selectedD0ToPiKWMl = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; Partition selectedD0ToKPiWMl = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; - Partition selectedLcToPKPi = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; - Partition selectedLcToPiKP = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; + Partition selectedDstarToD0PiWMl = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Partition selectedLcToPKPiWMl = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; Partition selectedLcToPiKPWMl = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; - Partition selectedXicToPKPi = aod::hf_sel_candidate_xic::isSelXicToPKPi >= selectionFlag; - Partition selectedXicToPiKP = aod::hf_sel_candidate_xic::isSelXicToPiKP >= selectionFlag; Partition selectedXicToPKPiWMl = aod::hf_sel_candidate_xic::isSelXicToPKPi >= selectionFlag; Partition selectedXicToPiKPWMl = aod::hf_sel_candidate_xic::isSelXicToPiKP >= selectionFlag; Partition selectedXic0 = aod::hf_sel_toxipi::resultSelections == true; Partition selectedXic0WMl = aod::hf_sel_toxipi::resultSelections == true; - ConfigurableAxis thnConfigAxisInvMass{"thnConfigAxisInvMass", {100, 1.78, 2.05}, ""}; - ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {10, 0., 10.}, ""}; - ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {10000, 0., 100.}, ""}; - ConfigurableAxis thnConfigAxisCosNPhi{"thnConfigAxisCosNPhi", {100, -1., 1.}, ""}; - ConfigurableAxis thnConfigAxisPsi{"thnConfigAxisPsi", {6000, 0, constants::math::TwoPI}, ""}; - ConfigurableAxis thnConfigAxisCosDeltaPhi{"thnConfigAxisCosDeltaPhi", {100, -1., 1.}, ""}; - ConfigurableAxis thnConfigAxisScalarProd{"thnConfigAxisScalarProd", {100, 0., 1.}, ""}; - ConfigurableAxis thnConfigAxisMlOne{"thnConfigAxisMlOne", {1000, 0., 1.}, ""}; - ConfigurableAxis thnConfigAxisMlTwo{"thnConfigAxisMlTwo", {1000, 0., 1.}, ""}; - ConfigurableAxis thnConfigAxisOccupancyITS{"thnConfigAxisOccupancyITS", {14, 0, 14000}, ""}; - ConfigurableAxis thnConfigAxisOccupancyFT0C{"thnConfigAxisOccupancyFT0C", {14, 0, 140000}, ""}; - ConfigurableAxis thnConfigAxisNoSameBunchPileup{"thnConfigAxisNoSameBunchPileup", {2, 0, 2}, ""}; - ConfigurableAxis thnConfigAxisOccupancy{"thnConfigAxisOccupancy", {2, 0, 2}, ""}; - ConfigurableAxis thnConfigAxisNoCollInTimeRangeNarrow{"thnConfigAxisNoCollInTimeRangeNarrow", {2, 0, 2}, ""}; - ConfigurableAxis thnConfigAxisNoCollInTimeRangeStandard{"thnConfigAxisNoCollInTimeRangeStandard", {2, 0, 2}, ""}; - ConfigurableAxis thnConfigAxisNoCollInRofStandard{"thnConfigAxisNoCollInRofStandard", {2, 0, 2}, ""}; - ConfigurableAxis thnConfigAxisResoFT0cFV0a{"thnConfigAxisResoFT0cFV0a", {160, -8, 8}, ""}; - ConfigurableAxis thnConfigAxisResoFT0cTPCtot{"thnConfigAxisResoFT0cTPCtot", {160, -8, 8}, ""}; - ConfigurableAxis thnConfigAxisResoFV0aTPCtot{"thnConfigAxisResoFV0aTPCtot", {160, -8, 8}, ""}; - ConfigurableAxis thnConfigAxisCandidateEta{"thnConfigAxisCandidateEta", {100, -5, 5}, ""}; - ConfigurableAxis thnConfigAxisSign{"thnConfigAxisSign", {6, -3.0, 3.0}, ""}; + struct : o2::framework::ConfigurableGroup { + ConfigurableAxis thnConfigAxisInvMass{"thnConfigAxisInvMass", {100, 1.78, 2.05}, ""}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {10, 0., 10.}, ""}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {10000, 0., 100.}, ""}; + ConfigurableAxis thnConfigAxisCosNPhi{"thnConfigAxisCosNPhi", {100, -1., 1.}, ""}; + ConfigurableAxis thnConfigAxisPsi{"thnConfigAxisPsi", {6000, 0, constants::math::TwoPI}, ""}; + ConfigurableAxis thnConfigAxisCosDeltaPhi{"thnConfigAxisCosDeltaPhi", {100, -1., 1.}, ""}; + ConfigurableAxis thnConfigAxisScalarProd{"thnConfigAxisScalarProd", {100, 0., 1.}, ""}; + ConfigurableAxis thnConfigAxisMlOne{"thnConfigAxisMlOne", {1000, 0., 1.}, ""}; + ConfigurableAxis thnConfigAxisMlTwo{"thnConfigAxisMlTwo", {1000, 0., 1.}, ""}; + ConfigurableAxis thnConfigAxisOccupancyITS{"thnConfigAxisOccupancyITS", {14, 0, 14000}, ""}; + ConfigurableAxis thnConfigAxisOccupancyFT0C{"thnConfigAxisOccupancyFT0C", {14, 0, 140000}, ""}; + ConfigurableAxis thnConfigAxisNoSameBunchPileup{"thnConfigAxisNoSameBunchPileup", {2, 0, 2}, ""}; + ConfigurableAxis thnConfigAxisOccupancy{"thnConfigAxisOccupancy", {2, 0, 2}, ""}; + ConfigurableAxis thnConfigAxisNoCollInTimeRangeNarrow{"thnConfigAxisNoCollInTimeRangeNarrow", {2, 0, 2}, ""}; + ConfigurableAxis thnConfigAxisNoCollInTimeRangeStandard{"thnConfigAxisNoCollInTimeRangeStandard", {2, 0, 2}, ""}; + ConfigurableAxis thnConfigAxisNoCollInRofStandard{"thnConfigAxisNoCollInRofStandard", {2, 0, 2}, ""}; + ConfigurableAxis thnConfigAxisResoFT0cFV0a{"thnConfigAxisResoFT0cFV0a", {160, -8, 8}, ""}; + ConfigurableAxis thnConfigAxisResoFT0cTPCtot{"thnConfigAxisResoFT0cTPCtot", {160, -8, 8}, ""}; + ConfigurableAxis thnConfigAxisResoFV0aTPCtot{"thnConfigAxisResoFV0aTPCtot", {160, -8, 8}, ""}; + ConfigurableAxis thnConfigAxisCandidateEta{"thnConfigAxisCandidateEta", {100, -5, 5}, ""}; + ConfigurableAxis thnConfigAxisSign{"thnConfigAxisSign", {6, -3.0, 3.0}, ""}; + ConfigurableAxis thnConfigAxisRedQVec{"thnConfigAxisRedQVec", {1000, 0, 100}, ""}; + } thnAxesCfgs; HistogramRegistry registry{"registry", {}}; @@ -206,31 +241,41 @@ struct HfTaskFlowCharmHadrons { if (storeResoOccu && occEstimator == 0) { LOGP(fatal, "Occupancy estimation must be enabled to store resolution THnSparse! Please check your configuration!"); } - const AxisSpec thnAxisInvMass{thnConfigAxisInvMass, "Inv. mass (GeV/#it{c}^{2})"}; - const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality"}; - const AxisSpec thnAxisCosNPhi{thnConfigAxisCosNPhi, Form("cos(%d#varphi)", harmonic.value)}; - const AxisSpec thnAxisSinNPhi{thnConfigAxisCosNPhi, Form("sin(%d#varphi)", harmonic.value)}; - const AxisSpec thnAxisPsi{thnConfigAxisPsi, Form("#Psi_{%d}", harmonic.value)}; - const AxisSpec thnAxisCosDeltaPhi{thnConfigAxisCosDeltaPhi, Form("cos(%d(#varphi - #Psi_{sub}))", harmonic.value)}; - const AxisSpec thnAxisScalarProd{thnConfigAxisScalarProd, "SP"}; - const AxisSpec thnAxisMlOne{thnConfigAxisMlOne, "Bkg score"}; - const AxisSpec thnAxisMlTwo{thnConfigAxisMlTwo, "FD score"}; - const AxisSpec thnAxisOccupancyITS{thnConfigAxisOccupancyITS, "OccupancyITS"}; - const AxisSpec thnAxisOccupancyFT0C{thnConfigAxisOccupancyFT0C, "OccupancyFT0C"}; - const AxisSpec thnAxisCandEta{thnConfigAxisCandidateEta, "#eta"}; - const AxisSpec thnAxisSign{thnConfigAxisSign, "Sign"}; - const AxisSpec thnAxisNoSameBunchPileup{thnConfigAxisNoSameBunchPileup, "NoSameBunchPileup"}; - const AxisSpec thnAxisOccupancy{thnConfigAxisOccupancy, "Occupancy"}; - const AxisSpec thnAxisNoCollInTimeRangeNarrow{thnConfigAxisNoCollInTimeRangeNarrow, "NoCollInTimeRangeNarrow"}; - const AxisSpec thnAxisNoCollInTimeRangeStandard{thnConfigAxisNoCollInTimeRangeStandard, "NoCollInTimeRangeStandard"}; - const AxisSpec thnAxisNoCollInRofStandard{thnConfigAxisNoCollInRofStandard, "NoCollInRofStandard"}; + if (storeRedQVec && !doprocessD0EsEMl && !doprocessD0SPEsEMl && !doprocessResolutionSPEsE) { + LOGP(fatal, "EsE q-vectors require dedicated process functions. Please check your configuration!"); + } + if (storeSPQVec && doprocessD0EsEMl) { + LOGP(fatal, "Scalar product tables not available for this process function. Please check your configuration!"); + } + const AxisSpec thnAxisInvMass{thnAxesCfgs.thnConfigAxisInvMass, "Inv. mass (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{thnAxesCfgs.thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisCent{thnAxesCfgs.thnConfigAxisCent, "Centrality"}; + const AxisSpec thnAxisCosNPhi{thnAxesCfgs.thnConfigAxisCosNPhi, Form("cos(%d#varphi)", harmonic.value)}; + const AxisSpec thnAxisSinNPhi{thnAxesCfgs.thnConfigAxisCosNPhi, Form("sin(%d#varphi)", harmonic.value)}; + const AxisSpec thnAxisPsi{thnAxesCfgs.thnConfigAxisPsi, Form("#Psi_{%d}", harmonic.value)}; + const AxisSpec thnAxisCosDeltaPhi{thnAxesCfgs.thnConfigAxisCosDeltaPhi, Form("cos(%d(#varphi - #Psi_{sub}))", harmonic.value)}; + const AxisSpec thnAxisScalarProd{thnAxesCfgs.thnConfigAxisScalarProd, "SP"}; + const AxisSpec thnAxisMlOne{thnAxesCfgs.thnConfigAxisMlOne, "Bkg score"}; + const AxisSpec thnAxisMlTwo{thnAxesCfgs.thnConfigAxisMlTwo, "FD score"}; + const AxisSpec thnAxisOccupancyITS{thnAxesCfgs.thnConfigAxisOccupancyITS, "OccupancyITS"}; + const AxisSpec thnAxisOccupancyFT0C{thnAxesCfgs.thnConfigAxisOccupancyFT0C, "OccupancyFT0C"}; + const AxisSpec thnAxisCandEta{thnAxesCfgs.thnConfigAxisCandidateEta, "#eta"}; + const AxisSpec thnAxisSign{thnAxesCfgs.thnConfigAxisSign, "Sign"}; + const AxisSpec thnAxisNoSameBunchPileup{thnAxesCfgs.thnConfigAxisNoSameBunchPileup, "NoSameBunchPileup"}; + const AxisSpec thnAxisOccupancy{thnAxesCfgs.thnConfigAxisOccupancy, "Occupancy"}; + const AxisSpec thnAxisNoCollInTimeRangeNarrow{thnAxesCfgs.thnConfigAxisNoCollInTimeRangeNarrow, "NoCollInTimeRangeNarrow"}; + const AxisSpec thnAxisNoCollInTimeRangeStandard{thnAxesCfgs.thnConfigAxisNoCollInTimeRangeStandard, "NoCollInTimeRangeStandard"}; + const AxisSpec thnAxisNoCollInRofStandard{thnAxesCfgs.thnConfigAxisNoCollInRofStandard, "NoCollInRofStandard"}; + const AxisSpec thnAxisRedQVec{thnAxesCfgs.thnConfigAxisRedQVec, "Reduced Q-vector"}; // TODO: currently only the Q vector of FT0c FV0a and TPCtot are considered - const AxisSpec thnAxisResoFT0cFV0a{thnConfigAxisResoFT0cFV0a, "Q_{FT0c} #bullet Q_{FV0a}"}; - const AxisSpec thnAxisResoFT0cTPCtot{thnConfigAxisResoFT0cTPCtot, "Q_{FT0c} #bullet Q_{TPCtot}"}; - const AxisSpec thnAxisResoFV0aTPCtot{thnConfigAxisResoFV0aTPCtot, "Q_{FV0a} #bullet Q_{TPCtot}"}; + const AxisSpec thnAxisResoFT0cFV0a{thnAxesCfgs.thnConfigAxisResoFT0cFV0a, "Q_{FT0c} #bullet Q_{FV0a}"}; + const AxisSpec thnAxisResoFT0cTPCtot{thnAxesCfgs.thnConfigAxisResoFT0cTPCtot, "Q_{FT0c} #bullet Q_{TPCtot}"}; + const AxisSpec thnAxisResoFV0aTPCtot{thnAxesCfgs.thnConfigAxisResoFV0aTPCtot, "Q_{FV0a} #bullet Q_{TPCtot}"}; - std::vector axes = {thnAxisInvMass, thnAxisPt, thnAxisCent, thnAxisScalarProd}; + std::vector axes = {thnAxisInvMass, thnAxisPt, thnAxisCent}; + if (storeSPQVec) { + axes.insert(axes.end(), {thnAxisScalarProd}); + } if (storeEP) { axes.insert(axes.end(), {thnAxisCosNPhi, thnAxisSinNPhi, thnAxisCosDeltaPhi}); } @@ -252,6 +297,9 @@ struct HfTaskFlowCharmHadrons { thnAxisNoCollInTimeRangeNarrow, thnAxisNoCollInTimeRangeStandard, thnAxisNoCollInRofStandard}); } } + if (storeRedQVec) { + axes.insert(axes.end(), {thnAxisRedQVec}); + } registry.add("hSparseFlowCharm", "THn for SP", HistType::kTHnSparseF, axes); registry.add("hCentEventWithCand", "Centrality distributions with charm candidates;Cent;entries", HistType::kTH1F, {{100, 0.f, 100.f}}); registry.add("hCentEventWithCandInSigRegion", "Centrality distributions with charm candidates in signal range;Cent;entries", HistType::kTH1F, {{100, 0.f, 100.f}}); @@ -269,24 +317,35 @@ struct HfTaskFlowCharmHadrons { registry.add("ep/hSparseEp", "THn for Event Plane distirbution", {HistType::kTHnSparseF, {thnAxisCent, thnAxisPsi, thnAxisCosNPhi, thnAxisSinNPhi}}); } - if (doprocessResolution) { // enable resolution histograms only for resolution process - registry.add("spReso/hSpResoFT0cFT0a", "hSpResoFT0cFT0a; centrality; Q_{FT0c} #bullet Q_{FT0a}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0cFV0a", "hSpResoFT0cFV0a; centrality; Q_{FT0c} #bullet Q_{FV0a}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0cTPCpos", "hSpResoFT0cTPCpos; centrality; Q_{FT0c} #bullet Q_{TPCpos}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0cTPCneg", "hSpResoFT0cTPCneg; centrality; Q_{FT0c} #bullet Q_{TPCneg}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0cTPCtot", "hSpResoFT0cTPCtot; centrality; Q_{FT0c} #bullet Q_{TPCtot}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0aFV0a", "hSpResoFT0aFV0a; centrality; Q_{FT0a} #bullet Q_{FV0a}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0aTPCpos", "hSpResoFT0aTPCpos; centrality; Q_{FT0a} #bullet Q_{TPCpos}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0aTPCneg", "hSpResoFT0aTPCneg; centrality; Q_{FT0a} #bullet Q_{TPCneg}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0aTPCtot", "hSpResoFT0aTPCtot; centrality; Q_{FT0m} #bullet Q_{TPCtot}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0mFV0a", "hSpResoFT0mFV0a; centrality; Q_{FT0m} #bullet Q_{FV0a}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0mTPCpos", "hSpResoFT0mTPCpos; centrality; Q_{FT0m} #bullet Q_{TPCpos}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0mTPCneg", "hSpResoFT0mTPCneg; centrality; Q_{FT0m} #bullet Q_{TPCneg}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFT0mTPCtot", "hSpResoFT0mTPCtot; centrality; Q_{FV0a} #bullet Q_{TPCtot}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFV0aTPCpos", "hSpResoFV0aTPCpos; centrality; Q_{FV0a} #bullet Q_{TPCpos}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFV0aTPCneg", "hSpResoFV0aTPCneg; centrality; Q_{FV0a} #bullet Q_{TPCneg}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoFV0aTPCtot", "hSpResoFV0aTPCtot; centrality; Q_{FV0a} #bullet Q_{TPCtot}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); - registry.add("spReso/hSpResoTPCposTPCneg", "hSpResoTPCposTPCneg; centrality; Q_{TPCpos} #bullet Q_{TPCneg}", {HistType::kTH2F, {thnAxisCent, thnAxisScalarProd}}); + if (doprocessResolutionSP || doprocessResolutionSPEsE) { // enable resolution histograms only for resolution process + registry.add("spReso/hSpResoFT0cFT0a", "hSpResoFT0cFT0a; centrality; Q_{FT0c} #bullet Q_{FT0a}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0cFV0a", "hSpResoFT0cFV0a; centrality; Q_{FT0c} #bullet Q_{FV0a}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0cTPCpos", "hSpResoFT0cTPCpos; centrality; Q_{FT0c} #bullet Q_{TPCpos}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0cTPCneg", "hSpResoFT0cTPCneg; centrality; Q_{FT0c} #bullet Q_{TPCneg}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0cTPCtot", "hSpResoFT0cTPCtot; centrality; Q_{FT0c} #bullet Q_{TPCtot}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0aFV0a", "hSpResoFT0aFV0a; centrality; Q_{FT0a} #bullet Q_{FV0a}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0aTPCpos", "hSpResoFT0aTPCpos; centrality; Q_{FT0a} #bullet Q_{TPCpos}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0aTPCneg", "hSpResoFT0aTPCneg; centrality; Q_{FT0a} #bullet Q_{TPCneg}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0aTPCtot", "hSpResoFT0aTPCtot; centrality; Q_{FT0m} #bullet Q_{TPCtot}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0mFV0a", "hSpResoFT0mFV0a; centrality; Q_{FT0m} #bullet Q_{FV0a}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0mTPCpos", "hSpResoFT0mTPCpos; centrality; Q_{FT0m} #bullet Q_{TPCpos}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0mTPCneg", "hSpResoFT0mTPCneg; centrality; Q_{FT0m} #bullet Q_{TPCneg}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFT0mTPCtot", "hSpResoFT0mTPCtot; centrality; Q_{FV0a} #bullet Q_{TPCtot}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFV0aTPCpos", "hSpResoFV0aTPCpos; centrality; Q_{FV0a} #bullet Q_{TPCpos}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFV0aTPCneg", "hSpResoFV0aTPCneg; centrality; Q_{FV0a} #bullet Q_{TPCneg}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoFV0aTPCtot", "hSpResoFV0aTPCtot; centrality; Q_{FV0a} #bullet Q_{TPCtot}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + registry.add("spReso/hSpResoTPCposTPCneg", "hSpResoTPCposTPCneg; centrality; Q_{TPCpos} #bullet Q_{TPCneg}; |q_{2}|", {HistType::kTH3F, {thnAxisCent, thnAxisScalarProd, thnAxisRedQVec}}); + + if (doprocessResolutionSPEsE) { + registry.add("redQVecs/hSparseRedQVecs", "hSpResoRedQVec; centrality; Q_{red} #bullet Q_{TPCtot}", {HistType::kTHnSparseF, {thnAxisCent, thnAxisRedQVec, thnAxisRedQVec, thnAxisRedQVec, thnAxisRedQVec}}); + registry.add("redQVecs/hRedQVecFT0C", "hRedQVecFT0C; centrality; q_{FT0c}", {HistType::kTH2F, {thnAxisCent, thnAxisRedQVec}}); + registry.add("redQVecs/hRedQVecFT0A", "hRedQVecFT0A; centrality; q_{FT0a}", {HistType::kTH2F, {thnAxisCent, thnAxisRedQVec}}); + registry.add("redQVecs/hRedQVecFT0M", "hRedQVecFT0M; centrality; q_{FT0m}", {HistType::kTH2F, {thnAxisCent, thnAxisRedQVec}}); + registry.add("redQVecs/hRedQVecFV0A", "hRedQVecFV0A; centrality; q_{FV0a}", {HistType::kTH2F, {thnAxisCent, thnAxisRedQVec}}); + registry.add("redQVecs/hRedQVecTpcPos", "hRedQVecTpcPos; centrality; q_{TPCpos}", {HistType::kTH2F, {thnAxisCent, thnAxisRedQVec}}); + registry.add("redQVecs/hRedQVecTpcNeg", "hRedQVecTpcNeg; centrality; q_{TPCneg}", {HistType::kTH2F, {thnAxisCent, thnAxisRedQVec}}); + registry.add("redQVecs/hRedQVecTpcAll", "hRedQVecTpcAll; centrality; q_{TPCall}", {HistType::kTH2F, {thnAxisCent, thnAxisRedQVec}}); + } if (saveEpResoHisto) { registry.add("epReso/hEpResoFT0cFT0a", "hEpResoFT0cFT0a; centrality; #Delta#Psi_{sub}", {HistType::kTH2F, {thnAxisCent, thnAxisCosNPhi}}); @@ -337,17 +396,17 @@ struct HfTaskFlowCharmHadrons { std::vector& tracksQx, std::vector& tracksQy, const float amplQVec, - QvecEstimator qVecDetector) + QvecEstimator qVecDet) { auto addProngIfInSubevent = [&](float px, float py, float pz) { const std::array pVec{px, py, pz}; const float eta = RecoDecay::eta(pVec); // only subtract daughters that actually contributed to THIS subevent Q - if (qVecDetector == QvecEstimator::TPCPos && eta <= 0.f) { + if (qVecDet == QvecEstimator::TPCPos && eta <= 0.f) { return; } - if (qVecDetector == QvecEstimator::TPCNeg && eta >= 0.f) { + if (qVecDet == QvecEstimator::TPCNeg && eta >= 0.f) { return; } // for TPCTot: no early return, all prongs contribute @@ -363,8 +422,10 @@ struct HfTaskFlowCharmHadrons { addProngIfInSubevent(cand.pxProng0(), cand.pyProng0(), cand.pzProng0()); addProngIfInSubevent(cand.pxProng1(), cand.pyProng1(), cand.pzProng1()); - // 3-prong channels - if constexpr (Channel != DecayChannel::D0ToPiK && Channel != DecayChannel::D0ToKPi) { + if constexpr (Channel == DecayChannel::DstarToD0Pi) { + addProngIfInSubevent(cand.pxSoftPi(), cand.pySoftPi(), cand.pzSoftPi()); + } else if constexpr (Channel != DecayChannel::D0ToPiK && Channel != DecayChannel::D0ToKPi) { + // Other 3-prong channels addProngIfInSubevent(cand.pxProng2(), cand.pyProng2(), cand.pzProng2()); } } @@ -379,16 +440,16 @@ struct HfTaskFlowCharmHadrons { std::vector& tracksQx, std::vector& tracksQy, float amplQVec, - QvecEstimator qVecDetector) + QvecEstimator qVecDet) { auto addProngIfInSubevent = [&](float px, float py, float pz) { const std::array pVec{px, py, pz}; const float eta = RecoDecay::eta(pVec); - if (qVecDetector == QvecEstimator::TPCPos && eta <= 0.f) { + if (qVecDet == QvecEstimator::TPCPos && eta <= 0.f) { return; } - if (qVecDetector == QvecEstimator::TPCNeg && eta >= 0.f) { + if (qVecDet == QvecEstimator::TPCNeg && eta >= 0.f) { return; } @@ -430,6 +491,7 @@ struct HfTaskFlowCharmHadrons { /// \param outputMl are the ML scores /// \param occupancy is the occupancy of the collision using the track estimator /// \param hfevselflag flag of the collision associated to utilsEvSelHf.h + /// \param redQVec is the reduced Q vector for EsE void fillThn(const float mass, const float pt, const float eta, @@ -441,7 +503,8 @@ struct HfTaskFlowCharmHadrons { const float sp, const std::vector& outputMl, const float occupancy, - const o2::hf_evsel::HfCollisionRejectionMask hfevselflag) + const o2::hf_evsel::HfCollisionRejectionMask hfevselflag, + const float redQVec) { auto hSparse = registry.get(HIST("hSparseFlowCharm")); const int ndim = hSparse->GetNdimensions(); @@ -452,7 +515,9 @@ struct HfTaskFlowCharmHadrons { values.push_back(mass); values.push_back(pt); values.push_back(cent); - values.push_back(sp); + if (storeSPQVec) { + values.push_back(sp); + } if (storeEP) { values.push_back(cosNPhi); @@ -480,6 +545,9 @@ struct HfTaskFlowCharmHadrons { values.push_back(evtSelFlags[3]); values.push_back(evtSelFlags[4]); } + if (storeRedQVec) { + values.push_back(redQVec); + } if (static_cast(values.size()) != ndim) { LOGF(fatal, @@ -487,7 +555,6 @@ struct HfTaskFlowCharmHadrons { "does not match THnSparse dimensionality (%d).", static_cast(values.size()), ndim); } - hSparse->Fill(values.data()); } @@ -496,26 +563,29 @@ struct HfTaskFlowCharmHadrons { /// \param bc is the bunch crossing with timestamp information /// \param centrality is the collision centrality /// \return true if the collision is selected, false otherwise - template - bool isCollSelected(CollsWithQvecs::iterator const& collision, + template + bool isCollSelected(TCollision const& collision, aod::BCsWithTimestamps const&, float& centrality) { - const auto occupancy = o2::hf_occupancy::getOccupancyColl(collision, occEstimator); + float occupancy{-999.f}; + if (occEstimator != 0) { + occupancy = o2::hf_occupancy::getOccupancyColl(collision, occEstimator); + registry.fill(HIST("trackOccVsFT0COcc"), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); + } const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); centrality = o2::hf_centrality::getCentralityColl(collision, CentEstimator); /// monitor the satisfied event selections hfEvSel.fillHistograms(collision, rejectionMask, centrality, occupancy); - registry.fill(HIST("trackOccVsFT0COcc"), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); return rejectionMask == 0; } /// Compute the scalar product /// \param collision is the collision with the Q vector information and event plane /// \param candidates are the selected candidates - template - void runFlowAnalysis(CollsWithQvecs::iterator const& collision, + template + void runFlowAnalysis(TCollision const& collision, T1 const& candidates, Trk const& /*tracks*/) { @@ -538,51 +608,70 @@ struct HfTaskFlowCharmHadrons { float const sigmaMDplus = 0.015; // used 15 MeV as the D+ average peak width in run3 float const nSigmaMass = 2.5; - std::array qVecs = getQvec(collision, qVecDetector.value); + std::array qVecs{-999.f, -999.f, -999.f}; + if constexpr (StoreInfo == RunMode::kSP || StoreInfo == RunMode::kSPEsE) { + qVecs = getQvec(collision, qVecDetector.value); + } float xQVec = qVecs[0]; float yQVec = qVecs[1]; float const amplQVec = qVecs[2]; float const evtPl = epHelper.GetEventPlane(xQVec, yQVec, harmonic); + + float redQVec{-999.f}; + std::array qVecRedComps{-999.f, -999.f, -999.f}; + if constexpr (StoreInfo == RunMode::kEsE || StoreInfo == RunMode::kSPEsE) { + qVecRedComps = getEseQvec(collision, qVecRedDetector.value); + } + float const xRedQVec = qVecRedComps[0]; + float const yRedQVec = qVecRedComps[1]; + float const amplRedQVec = qVecRedComps[2]; + + if (useOnlineResoCorrection) { + // Access BC information through the collision's BC index. + const auto bc = collision.template bc_as(); + // Per-run update + if (bc.runNumber() != lastRunNumber) { + LOG(info) << "Run number changed from " << lastRunNumber << " to " << bc.runNumber() << ". Updating resolution values."; + qVecResoHelper.update(ccdb, ccdbResoHistPath, resoTripletPath.value, bc.timestamp(), bc.runNumber()); + lastRunNumber = bc.runNumber(); + } + scalarProdReso = qVecResoHelper[static_cast(cent - 0.5f)]; // Cent is given in X.5f format + } + for (const auto& candidate : candidates) { float massCand = 0.; float signCand = 0.; std::vector outputMl = {-999., -999.}; - if constexpr (std::is_same_v || std::is_same_v) { + if constexpr (std::is_same_v) { switch (Channel) { case DecayChannel::DsToKKPi: massCand = HfHelper::invMassDsToKKPi(candidate); signCand = 1; - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; } break; case DecayChannel::DsToPiKK: massCand = HfHelper::invMassDsToPiKK(candidate); signCand = -1; - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; } break; default: break; } - } else if constexpr (std::is_same_v || std::is_same_v) { + } else if constexpr (std::is_same_v) { massCand = HfHelper::invMassDplusToPiKPi(candidate); if (std::abs(massCand - MassDPlus) < nSigmaMass * sigmaMDplus) { hasCandInMassWin = true; } auto trackprong0 = candidate.template prong0_as(); signCand = trackprong0.sign(); - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } - } else if constexpr (std::is_same_v || std::is_same_v) { + } else if constexpr (std::is_same_v) { switch (Channel) { case DecayChannel::D0ToPiK: signCand = candidate.isSelD0bar() ? 3 : 1; // 3: reflected D0bar, 1: pure D0 excluding reflected D0bar @@ -590,10 +679,8 @@ struct HfTaskFlowCharmHadrons { if (std::abs(massCand - MassD0) < nSigmaMass * sigmaMD0) { hasCandInMassWin = true; } - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; } break; case DecayChannel::D0ToKPi: @@ -602,56 +689,53 @@ struct HfTaskFlowCharmHadrons { hasCandInMassWin = true; } signCand = candidate.isSelD0() ? 3 : 2; // 3: reflected D0, 2: pure D0bar excluding reflected D0 - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; } break; default: break; } - } else if constexpr (std::is_same_v || std::is_same_v) { + } else if constexpr (std::is_same_v) { + // Use the soft-pion charge to select the D0 or D0bar mass hypothesis. + signCand = candidate.signSoftPi(); + massCand = signCand > 0 ? candidate.invMassDstar() - candidate.invMassD0() : candidate.invMassAntiDstar() - candidate.invMassD0Bar(); + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDstarToD0Pi()[classMl->at(iclass)]; + } + } else if constexpr (std::is_same_v) { switch (Channel) { case DecayChannel::LcToPKPi: massCand = HfHelper::invMassLcToPKPi(candidate); signCand = 1; - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl->at(iclass)]; } break; case DecayChannel::LcToPiKP: massCand = HfHelper::invMassLcToPiKP(candidate); signCand = -1; - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbLcToPiKP()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPiKP()[classMl->at(iclass)]; } break; default: break; } - } else if constexpr (std::is_same_v || std::is_same_v) { + } else if constexpr (std::is_same_v) { switch (Channel) { case DecayChannel::XicToPKPi: massCand = HfHelper::invMassXicToPKPi(candidate); signCand = 1; - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbXicToPKPi()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbXicToPKPi()[classMl->at(iclass)]; } break; case DecayChannel::XicToPiKP: massCand = HfHelper::invMassXicToPiKP(candidate); signCand = -1; - if constexpr (std::is_same_v) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = candidate.mlProbXicToPiKP()[classMl->at(iclass)]; - } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbXicToPiKP()[classMl->at(iclass)]; } break; default: @@ -681,10 +765,16 @@ struct HfTaskFlowCharmHadrons { etaCand = candidate.eta(); } + float const cosNPhi = std::cos(harmonic * phiCand); + float const sinNPhi = std::sin(harmonic * phiCand); + float const cosDeltaPhi = std::cos(harmonic * (phiCand - evtPl)); + // If TPC is used for the SP estimation, the tracks of the hadron candidate must be removed from the corresponding TPC Q vector to avoid self-correlations - if (qVecDetector == QvecEstimator::TPCNeg || - qVecDetector == QvecEstimator::TPCPos || - qVecDetector == QvecEstimator::TPCTot) { + bool subtractDaugsFromQVec = (qVecDetector == QvecEstimator::TPCNeg || + qVecDetector == QvecEstimator::TPCPos || + qVecDetector == QvecEstimator::TPCTot); + float scalprodCand{-999.f}; + if (subtractDaugsFromQVec) { std::vector tracksQx; std::vector tracksQy; @@ -697,18 +787,18 @@ struct HfTaskFlowCharmHadrons { } // subtract daughters' contribution from the (normalized) Q-vector - for (std::size_t iTrack = 0; iTrack < tracksQx.size(); ++iTrack) { - xQVec -= tracksQx[iTrack]; - yQVec -= tracksQy[iTrack]; - } + float xQVecDaugSubtr = xQVec - std::accumulate(tracksQx.begin(), tracksQx.end(), 0.0); + float yQVecDaugSubtr = yQVec - std::accumulate(tracksQy.begin(), tracksQy.end(), 0.0); + scalprodCand = cosNPhi * xQVecDaugSubtr + sinNPhi * yQVecDaugSubtr; + } else { + scalprodCand = cosNPhi * xQVec + sinNPhi * yQVec; + } + float lowestReso = 1e-5f; + if (useOnlineResoCorrection && scalarProdReso > lowestReso) { + scalprodCand /= scalarProdReso; } - float const cosNPhi = std::cos(harmonic * phiCand); - float const sinNPhi = std::sin(harmonic * phiCand); - float const scalprodCand = cosNPhi * xQVec + sinNPhi * yQVec; - float const cosDeltaPhi = std::cos(harmonic * (phiCand - evtPl)); - - if (fillMassPtMlTree || fillMassPtMlSpCentTree) { + if (fillMassPtMlTree || fillMassPtMlSpCentTree || fillMassPtMlSpCentPvTree) { if (downSampleFactor < 1.) { float const pseudoRndm = ptCand * 1000. - static_cast(ptCand * 1000); if (ptCand < ptDownSampleMax && pseudoRndm >= downSampleFactor) { @@ -721,9 +811,40 @@ struct HfTaskFlowCharmHadrons { if (fillMassPtMlSpCentTree) { rowCandMassPtMlSpCent(massCand, ptCand, outputMl[0], outputMl[1], scalprodCand, cent); } + if (fillMassPtMlSpCentPvTree) { + rowCandMassPtMlSpCentPv(massCand, ptCand, outputMl[0], outputMl[1], scalprodCand, cent, collision.numContrib()); + } + } + + bool subtractDaugsFromRedQVec = storeRedQVec && useRedQVecDaugsSubtr && + (qVecRedDetector == QvecEstimator::TPCNeg || + qVecRedDetector == QvecEstimator::TPCPos || + qVecRedDetector == QvecEstimator::TPCTot); + if (subtractDaugsFromRedQVec) { + std::vector tracksRedQx; + std::vector tracksRedQy; + + // IMPORTANT: use the amplitude of the reduced Q-vector to build this Q-vector + if constexpr (std::is_same_v || std::is_same_v) { + getQvecXic0Tracks(candidate, tracksRedQx, tracksRedQy, std::sqrt(amplRedQVec), static_cast(qVecRedDetector.value)); + } else { + getQvecDtracks(candidate, tracksRedQx, tracksRedQy, std::sqrt(amplRedQVec), static_cast(qVecRedDetector.value)); + } + + // subtract daughters' contribution from the (normalized) Q-vector + const float redQVecXDaugSubtr = xRedQVec - std::accumulate(tracksRedQx.begin(), tracksRedQx.end(), 0.0); + const float redQVecYDaugSubtr = yRedQVec - std::accumulate(tracksRedQy.begin(), tracksRedQy.end(), 0.0); + // Correct for track multiplicity + redQVec = std::hypot(redQVecXDaugSubtr, redQVecYDaugSubtr) * std::sqrt(amplRedQVec) / std::sqrt(amplRedQVec - tracksRedQx.size()); + } else { + redQVec = std::hypot(xRedQVec, yRedQVec); + } + if (storeRedQVec) { + rowCandFlowEsE(massCand, ptCand, outputMl[0], outputMl[1], scalprodCand, cent, redQVec); } if (fillSparse) { - fillThn(massCand, ptCand, etaCand, signCand, cent, cosNPhi, sinNPhi, cosDeltaPhi, scalprodCand, outputMl, occupancy, hfevflag); + fillThn(massCand, ptCand, etaCand, signCand, cent, cosNPhi, sinNPhi, + cosDeltaPhi, scalprodCand, outputMl, occupancy, hfevflag, redQVec); } } if (hasCandInMassWin) { @@ -732,142 +853,177 @@ struct HfTaskFlowCharmHadrons { } // Ds with ML - void processDsMl(CollsWithQvecs::iterator const& collision, + void processDsMl(CollsWithSPQvecs::iterator const& collision, CandDsDataWMl const&, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsDsToKKPiWMl, tracks); - runFlowAnalysis(collision, candsDsToPiKKWMl, tracks); + runFlowAnalysis(collision, candsDsToKKPiWMl, tracks); + runFlowAnalysis(collision, candsDsToPiKKWMl, tracks); } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDsMl, "Process Ds candidates with ML", false); - // Ds with rectangular cuts - void processDs(CollsWithQvecs::iterator const& collision, - CandDsData const& /*candidatesDs*/, - TracksWithExtra const& tracks) + // Ds with ML EsE + void processDsEsEMl(CollsWithEsEQvecs::iterator const& collision, + CandDsDataWMl const& /*candidatesDs*/, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) + { + auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + runFlowAnalysis(collision, candsDsToKKPiWMl, tracks); + runFlowAnalysis(collision, candsDsToPiKKWMl, tracks); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDsEsEMl, "Process Ds candidates with ML for EsE information", false); + + // Ds with ML EsE and SP + void processDsSPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, + CandDsDataWMl const& /*candidatesDs*/, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { - auto candsDsToKKPi = selectedDsToKKPi->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - auto candsDsToPiKK = selectedDsToPiKK->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsDsToKKPi, tracks); - runFlowAnalysis(collision, candsDsToPiKK, tracks); + auto candsDsToKKPiWMl = selectedDsToKKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + auto candsDsToPiKKWMl = selectedDsToPiKKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + runFlowAnalysis(collision, candsDsToKKPiWMl, tracks); + runFlowAnalysis(collision, candsDsToPiKKWMl, tracks); } - PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDs, "Process Ds candidates", false); + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDsSPEsEMl, "Process Ds candidates with ML for SP and EsE information", false); // Dplus with ML - void processDplusMl(CollsWithQvecs::iterator const& collision, + void processDplusMl(CollsWithSPQvecs::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { - runFlowAnalysis(collision, candidatesDplus, tracks); + runFlowAnalysis(collision, candidatesDplus, tracks); } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDplusMl, "Process Dplus candidates with ML", false); - // Dplus with rectangular cuts - void processDplus(CollsWithQvecs::iterator const& collision, - CandDplusData const& candidatesDplus, - TracksWithExtra const& tracks) + // Dplus with ML EsE + void processDplusEsEMl(CollsWithEsEQvecs::iterator const& collision, + CandDplusDataWMl const& candidatesDplus, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) + { + runFlowAnalysis(collision, candidatesDplus, tracks); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDplusEsEMl, "Process Dplus candidates with ML for EsE information", false); + + // Dplus with ML SP and EsE + void processDplusSPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, + CandDplusDataWMl const& candidatesDplus, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { - runFlowAnalysis(collision, candidatesDplus, tracks); + runFlowAnalysis(collision, candidatesDplus, tracks); } - PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDplus, "Process Dplus candidates", true); + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDplusSPEsEMl, "Process Dplus candidates with ML for SP and EsE information", false); // D0 with ML - void processD0Ml(CollsWithQvecs::iterator const& collision, + void processD0Ml(CollsWithSPQvecs::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsD0ToPiKWMl, tracks); - runFlowAnalysis(collision, candsD0ToKPiWMl, tracks); + runFlowAnalysis(collision, candsD0ToPiKWMl, tracks); + runFlowAnalysis(collision, candsD0ToKPiWMl, tracks); } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processD0Ml, "Process D0 candidates with ML", false); - // D0 with rectangular cuts - void processD0(CollsWithQvecs::iterator const& collision, - CandD0Data const& /*candidatesD0*/, - TracksWithExtra const& tracks) + // D0 with ML + void processD0EsEMl(CollsWithEsEQvecs::iterator const& collision, + CandD0DataWMl const& /*candidatesD0*/, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) + { + auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + runFlowAnalysis(collision, candsD0ToPiKWMl, tracks); + runFlowAnalysis(collision, candsD0ToKPiWMl, tracks); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processD0EsEMl, "Process D0 candidates with ML for EsE information", false); + + // D0 with ML + void processD0SPEsEMl(CollsWithSPEsEQvecs::iterator const& collision, + CandD0DataWMl const& /*candidatesD0*/, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) + { + auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + runFlowAnalysis(collision, candsD0ToPiKWMl, tracks); + runFlowAnalysis(collision, candsD0ToKPiWMl, tracks); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processD0SPEsEMl, "Process D0 candidates with ML for SP and EsE information", false); + + // Dstar with ML + void processDstarMl(CollsWithSPQvecs::iterator const& collision, + CandDstarDataWMl const& candidatesDstar, + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { - auto candsD0ToPiK = selectedD0ToPiK->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - auto candsD0ToKPi = selectedD0ToKPi->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsD0ToPiK, tracks); - runFlowAnalysis(collision, candsD0ToKPi, tracks); + runFlowAnalysis(collision, candidatesDstar, tracks); } - PROCESS_SWITCH(HfTaskFlowCharmHadrons, processD0, "Process D0 candidates", false); + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processDstarMl, "Process Dstar candidates with ML", false); // Lc with ML - void processLcMl(CollsWithQvecs::iterator const& collision, + void processLcMl(CollsWithSPQvecs::iterator const& collision, CandLcDataWMl const& /*candidatesLc*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsLcToPKPiWMl = selectedLcToPKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsLcToPiKPWMl = selectedLcToPiKPWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsLcToPKPiWMl, tracks); - runFlowAnalysis(collision, candsLcToPiKPWMl, tracks); + runFlowAnalysis(collision, candsLcToPKPiWMl, tracks); + runFlowAnalysis(collision, candsLcToPiKPWMl, tracks); } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processLcMl, "Process Lc candidates with ML", false); - // Lc with rectangular cuts - void processLc(CollsWithQvecs::iterator const& collision, - CandLcData const& /*candidatesLc*/, - TracksWithExtra const& tracks) - { - auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - auto candsLcToPiKP = selectedLcToPiKP->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsLcToPKPi, tracks); - runFlowAnalysis(collision, candsLcToPiKP, tracks); - } - PROCESS_SWITCH(HfTaskFlowCharmHadrons, processLc, "Process Lc candidates", false); - // Xic with ML - void processXicMl(CollsWithQvecs::iterator const& collision, + void processXicMl(CollsWithSPQvecs::iterator const& collision, CandXicDataWMl const& /*candidatesXic*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXicToPKPiWMl = selectedXicToPKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsXicToPiKPWMl = selectedXicToPiKPWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsXicToPKPiWMl, tracks); - runFlowAnalysis(collision, candsXicToPiKPWMl, tracks); + runFlowAnalysis(collision, candsXicToPKPiWMl, tracks); + runFlowAnalysis(collision, candsXicToPiKPWMl, tracks); } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processXicMl, "Process Xic candidates with ML", false); - // Xic with rectangular cuts - void processXic(CollsWithQvecs::iterator const& collision, - CandXicData const& /*candidatesXic*/, - TracksWithExtra const& tracks) - { - auto candsXicToPKPi = selectedXicToPKPi->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - auto candsXicToPiKP = selectedXicToPiKP->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsXicToPKPi, tracks); - runFlowAnalysis(collision, candsXicToPiKP, tracks); - } - PROCESS_SWITCH(HfTaskFlowCharmHadrons, processXic, "Process Xic candidates", false); - // Xic0 with ML - void processXic0Ml(CollsWithQvecs::iterator const& collision, + void processXic0Ml(CollsWithSPQvecs::iterator const& collision, CandXic0DataWMl const& /*candidatesXic0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXic0WMl = selectedXic0WMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsXic0WMl, tracks); + runFlowAnalysis(collision, candsXic0WMl, tracks); } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processXic0Ml, "Process Xic0 candidates with ML", false); // Xic0 - void processXic0(CollsWithQvecs::iterator const& collision, + void processXic0(CollsWithSPQvecs::iterator const& collision, CandXic0Data const& /*candidatesXic0*/, - TracksWithExtra const& tracks) + TracksWithExtra const& tracks, + aod::BCsWithTimestamps const& /*bcs*/) { auto candsXic0 = selectedXic0->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - runFlowAnalysis(collision, candsXic0, tracks); + runFlowAnalysis(collision, candsXic0, tracks); } PROCESS_SWITCH(HfTaskFlowCharmHadrons, processXic0, "Process Xic0 candidates", false); - // Resolution - void processResolution(CollsWithQvecs::iterator const& collision, - aod::BCsWithTimestamps const& bcs) + /// Compute resolution + /// \param collision is the collision with the Q vector information + /// \param bcs is used for the event selection based on centrality estimators with time information + template + void fillResolution(TCollision const& collision, + aod::BCsWithTimestamps const& bcs) { float centrality{-1.f}; float const xQVecFT0a = collision.qvecFT0ARe(); @@ -878,22 +1034,25 @@ struct HfTaskFlowCharmHadrons { float const yQVecFT0m = collision.qvecFT0MIm(); float const xQVecFV0a = collision.qvecFV0ARe(); float const yQVecFV0a = collision.qvecFV0AIm(); - float const xQVecBPos = collision.qvecBPosRe(); - float const yQVecBPos = collision.qvecBPosIm(); - float const xQVecBNeg = collision.qvecBNegRe(); - float const yQVecBNeg = collision.qvecBNegIm(); - float const xQVecBTot = collision.qvecBTotRe(); - float const yQVecBTot = collision.qvecBTotIm(); + float const xQVecTPCPos = collision.qvecBPosRe(); + float const yQVecTPCPos = collision.qvecBPosIm(); + float const xQVecTPCNeg = collision.qvecBNegRe(); + float const yQVecTPCNeg = collision.qvecBNegIm(); + float const xQVecTPCAll = collision.qvecBTotRe(); + float const yQVecTPCAll = collision.qvecBTotIm(); centrality = o2::hf_centrality::getCentralityColl(collision, centEstimator); if (storeResoOccu) { - const auto occupancy = o2::hf_occupancy::getOccupancyColl(collision, occEstimator); - registry.fill(HIST("trackOccVsFT0COcc"), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); + float occupancy{-999.f}; + if (occEstimator != 0) { + occupancy = o2::hf_occupancy::getOccupancyColl(collision, occEstimator); + registry.fill(HIST("trackOccVsFT0COcc"), collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange()); + } const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); std::vector evtSelFlags = getEventSelectionFlags(rejectionMask); registry.fill(HIST("spReso/hSparseReso"), centrality, xQVecFT0c * xQVecFV0a + yQVecFT0c * yQVecFV0a, - xQVecFT0c * xQVecBTot + yQVecFT0c * yQVecBTot, - xQVecFV0a * xQVecBTot + yQVecFV0a * yQVecBTot, + xQVecFT0c * xQVecTPCAll + yQVecFT0c * yQVecTPCAll, + xQVecFV0a * xQVecTPCAll + yQVecFV0a * yQVecTPCAll, occupancy, evtSelFlags[0], evtSelFlags[1], evtSelFlags[2], evtSelFlags[3], evtSelFlags[4]); } @@ -901,38 +1060,61 @@ struct HfTaskFlowCharmHadrons { registry.fill(HIST("hSparseCentEstimators"), o2::hf_centrality::getCentralityColl(collision, centEstimatorsForSparse->at(0)), o2::hf_centrality::getCentralityColl(collision, centEstimatorsForSparse->at(1)), o2::hf_centrality::getCentralityColl(collision, centEstimatorsForSparse->at(2)), o2::hf_centrality::getCentralityColl(collision, centEstimatorsForSparse->at(3))); } + if constexpr (HasRedQVecs) { + registry.fill(HIST("redQVecs/hSparseRedQVecs"), centrality, + std::hypot(collision.eseQvecFT0CRe(), collision.eseQvecFT0CIm()), + std::hypot(collision.eseQvecTPCposRe(), collision.eseQvecTPCposIm()), + std::hypot(collision.eseQvecTPCnegRe(), collision.eseQvecTPCnegIm()), + std::hypot(collision.eseQvecTPCallRe(), collision.eseQvecTPCallIm())); + } if (!isCollSelected(collision, bcs, centrality)) { // no selection on the centrality is applied, but on event selection flags return; } - registry.fill(HIST("spReso/hSpResoFT0cFT0a"), centrality, xQVecFT0c * xQVecFT0a + yQVecFT0c * yQVecFT0a); - registry.fill(HIST("spReso/hSpResoFT0cFV0a"), centrality, xQVecFT0c * xQVecFV0a + yQVecFT0c * yQVecFV0a); - registry.fill(HIST("spReso/hSpResoFT0cTPCpos"), centrality, xQVecFT0c * xQVecBPos + yQVecFT0c * yQVecBPos); - registry.fill(HIST("spReso/hSpResoFT0cTPCneg"), centrality, xQVecFT0c * xQVecBNeg + yQVecFT0c * yQVecBNeg); - registry.fill(HIST("spReso/hSpResoFT0cTPCtot"), centrality, xQVecFT0c * xQVecBTot + yQVecFT0c * yQVecBTot); - registry.fill(HIST("spReso/hSpResoFT0aFV0a"), centrality, xQVecFT0a * xQVecFV0a + yQVecFT0a * yQVecFV0a); - registry.fill(HIST("spReso/hSpResoFT0aTPCpos"), centrality, xQVecFT0a * xQVecBPos + yQVecFT0a * yQVecBPos); - registry.fill(HIST("spReso/hSpResoFT0aTPCneg"), centrality, xQVecFT0a * xQVecBNeg + yQVecFT0a * yQVecBNeg); - registry.fill(HIST("spReso/hSpResoFT0aTPCtot"), centrality, xQVecFT0a * xQVecBTot + yQVecFT0a * yQVecBTot); - registry.fill(HIST("spReso/hSpResoFT0mFV0a"), centrality, xQVecFT0m * xQVecFV0a + yQVecFT0m * yQVecFV0a); - registry.fill(HIST("spReso/hSpResoFT0mTPCpos"), centrality, xQVecFT0m * xQVecBPos + yQVecFT0m * yQVecBPos); - registry.fill(HIST("spReso/hSpResoFT0mTPCneg"), centrality, xQVecFT0m * xQVecBNeg + yQVecFT0m * yQVecBNeg); - registry.fill(HIST("spReso/hSpResoFT0mTPCtot"), centrality, xQVecFT0m * xQVecBTot + yQVecFT0m * yQVecBTot); - registry.fill(HIST("spReso/hSpResoFV0aTPCpos"), centrality, xQVecFV0a * xQVecBPos + yQVecFV0a * yQVecBPos); - registry.fill(HIST("spReso/hSpResoFV0aTPCneg"), centrality, xQVecFV0a * xQVecBNeg + yQVecFV0a * yQVecBNeg); - registry.fill(HIST("spReso/hSpResoFV0aTPCtot"), centrality, xQVecFV0a * xQVecBTot + yQVecFV0a * yQVecBTot); - registry.fill(HIST("spReso/hSpResoTPCposTPCneg"), centrality, xQVecBPos * xQVecBNeg + yQVecBPos * yQVecBNeg); - + float redQVec{0.f}; + std::array qVecRedComps{0.f, 0.f, 0.f}; + if constexpr (HasRedQVecs) { + qVecRedComps = getEseQvec(collision, qVecRedDetector.value); + } + redQVec = std::hypot(qVecRedComps[0], qVecRedComps[1]); + + registry.fill(HIST("spReso/hSpResoFT0cFT0a"), centrality, xQVecFT0c * xQVecFT0a + yQVecFT0c * yQVecFT0a, redQVec); + registry.fill(HIST("spReso/hSpResoFT0cFV0a"), centrality, xQVecFT0c * xQVecFV0a + yQVecFT0c * yQVecFV0a, redQVec); + registry.fill(HIST("spReso/hSpResoFT0cTPCpos"), centrality, xQVecFT0c * xQVecTPCPos + yQVecFT0c * yQVecTPCPos, redQVec); + registry.fill(HIST("spReso/hSpResoFT0cTPCneg"), centrality, xQVecFT0c * xQVecTPCNeg + yQVecFT0c * yQVecTPCNeg, redQVec); + registry.fill(HIST("spReso/hSpResoFT0cTPCtot"), centrality, xQVecFT0c * xQVecTPCAll + yQVecFT0c * yQVecTPCAll, redQVec); + registry.fill(HIST("spReso/hSpResoFT0aFV0a"), centrality, xQVecFT0a * xQVecFV0a + yQVecFT0a * yQVecFV0a, redQVec); + registry.fill(HIST("spReso/hSpResoFT0aTPCpos"), centrality, xQVecFT0a * xQVecTPCPos + yQVecFT0a * yQVecTPCPos, redQVec); + registry.fill(HIST("spReso/hSpResoFT0aTPCneg"), centrality, xQVecFT0a * xQVecTPCNeg + yQVecFT0a * yQVecTPCNeg, redQVec); + registry.fill(HIST("spReso/hSpResoFT0aTPCtot"), centrality, xQVecFT0a * xQVecTPCAll + yQVecFT0a * yQVecTPCAll, redQVec); + registry.fill(HIST("spReso/hSpResoFT0mFV0a"), centrality, xQVecFT0m * xQVecFV0a + yQVecFT0m * yQVecFV0a, redQVec); + registry.fill(HIST("spReso/hSpResoFT0mTPCpos"), centrality, xQVecFT0m * xQVecTPCPos + yQVecFT0m * yQVecTPCPos, redQVec); + registry.fill(HIST("spReso/hSpResoFT0mTPCneg"), centrality, xQVecFT0m * xQVecTPCNeg + yQVecFT0m * yQVecTPCNeg, redQVec); + registry.fill(HIST("spReso/hSpResoFT0mTPCtot"), centrality, xQVecFT0m * xQVecTPCAll + yQVecFT0m * yQVecTPCAll, redQVec); + registry.fill(HIST("spReso/hSpResoFV0aTPCpos"), centrality, xQVecFV0a * xQVecTPCPos + yQVecFV0a * yQVecTPCPos, redQVec); + registry.fill(HIST("spReso/hSpResoFV0aTPCneg"), centrality, xQVecFV0a * xQVecTPCNeg + yQVecFV0a * yQVecTPCNeg, redQVec); + registry.fill(HIST("spReso/hSpResoFV0aTPCtot"), centrality, xQVecFV0a * xQVecTPCAll + yQVecFV0a * yQVecTPCAll, redQVec); + registry.fill(HIST("spReso/hSpResoTPCposTPCneg"), centrality, xQVecTPCPos * xQVecTPCNeg + yQVecTPCPos * yQVecTPCNeg, redQVec); + + if constexpr (HasRedQVecs) { + registry.fill(HIST("redQVecs/hRedQVecFT0C"), centrality, std::hypot(collision.eseQvecFT0CRe(), collision.eseQvecFT0CIm())); + registry.fill(HIST("redQVecs/hRedQVecFT0M"), centrality, std::hypot(collision.eseQvecFT0MRe(), collision.eseQvecFT0MIm())); + registry.fill(HIST("redQVecs/hRedQVecFT0A"), centrality, std::hypot(collision.eseQvecFT0ARe(), collision.eseQvecFT0AIm())); + registry.fill(HIST("redQVecs/hRedQVecFV0A"), centrality, std::hypot(collision.eseQvecFV0ARe(), collision.eseQvecFV0AIm())); + registry.fill(HIST("redQVecs/hRedQVecTpcPos"), centrality, std::hypot(collision.eseQvecTPCposRe(), collision.eseQvecTPCposIm())); + registry.fill(HIST("redQVecs/hRedQVecTpcNeg"), centrality, std::hypot(collision.eseQvecTPCnegRe(), collision.eseQvecTPCnegIm())); + registry.fill(HIST("redQVecs/hRedQVecTpcAll"), centrality, std::hypot(collision.eseQvecTPCallRe(), collision.eseQvecTPCallIm())); + } if (saveEpResoHisto) { float const epFT0a = epHelper.GetEventPlane(xQVecFT0a, yQVecFT0a, harmonic); float const epFT0c = epHelper.GetEventPlane(xQVecFT0c, yQVecFT0c, harmonic); float const epFT0m = epHelper.GetEventPlane(xQVecFT0m, yQVecFT0m, harmonic); float const epFV0a = epHelper.GetEventPlane(xQVecFV0a, yQVecFV0a, harmonic); - float const epBPoss = epHelper.GetEventPlane(xQVecBPos, yQVecBPos, harmonic); - float const epBNegs = epHelper.GetEventPlane(xQVecBNeg, yQVecBNeg, harmonic); - float const epBTots = epHelper.GetEventPlane(xQVecBTot, yQVecBTot, harmonic); + float const epBPoss = epHelper.GetEventPlane(xQVecTPCPos, yQVecTPCPos, harmonic); + float const epBNegs = epHelper.GetEventPlane(xQVecTPCNeg, yQVecTPCNeg, harmonic); + float const epBTots = epHelper.GetEventPlane(xQVecTPCAll, yQVecTPCAll, harmonic); registry.fill(HIST("epReso/hEpResoFT0cFT0a"), centrality, std::cos(harmonic * getDeltaPsiInRange(epFT0c, epFT0a, harmonic))); registry.fill(HIST("epReso/hEpResoFT0cFV0a"), centrality, std::cos(harmonic * getDeltaPsiInRange(epFT0c, epFV0a, harmonic))); @@ -960,7 +1142,20 @@ struct HfTaskFlowCharmHadrons { std::sin(harmonic * epHelper.GetEventPlane(xQVecFT0c, yQVecFT0c, harmonic))); } } - PROCESS_SWITCH(HfTaskFlowCharmHadrons, processResolution, "Process resolution", false); + + void processResolutionSP(CollsWithSPQvecs::iterator const& collision, + aod::BCsWithTimestamps const& bcs) + { + fillResolution(collision, bcs); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processResolutionSP, "Process SP resolution", false); + + void processResolutionSPEsE(CollsWithSPEsEQvecs::iterator const& collision, + aod::BCsWithTimestamps const& bcs) + { + fillResolution(collision, bcs); + } + PROCESS_SWITCH(HfTaskFlowCharmHadrons, processResolutionSPEsE, "Process SP and EsE resolution", false); }; // End struct HfTaskFlowCharmHadrons diff --git a/PWGHF/D2H/Tasks/taskHiddenCharm.cxx b/PWGHF/D2H/Tasks/taskHiddenCharm.cxx index 70fa6f717f2..4ca56330285 100644 --- a/PWGHF/D2H/Tasks/taskHiddenCharm.cxx +++ b/PWGHF/D2H/Tasks/taskHiddenCharm.cxx @@ -52,8 +52,8 @@ struct HfTaskHiddenCharm { Configurable centEstimator{"centEstimator", 0, "Centrality estimation (None: 0, FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4, NTracksPV: 5, FT0CVariant2: 6)"}; Configurable centralityMin{"centralityMin", 0.f, "Minimum accepted centrality"}; Configurable centralityMax{"centralityMax", 100.f, "Maximum accepted centrality"}; - Configurable fillOnlyUnlikeSign{"fillOnlyUnlikeSign", true, "Fill only unlike-sign proton pairs"}; - Configurable fillOnlyLikeSign{"fillOnlyLikeSign", true, "Fill only like-sign proton pairs"}; + Configurable fillUnlikeSign{"fillUnlikeSign", true, "Fill unlike-sign proton pairs"}; + Configurable fillLikeSign{"fillLikeSign", true, "Fill like-sign proton pairs"}; SliceCache cache; @@ -94,7 +94,7 @@ struct HfTaskHiddenCharm { void fillEtac(TCollisions const& collision, TProtonIds const& protonIds) { - float cent{-1.f}; + float cent{0.f}; if constexpr (CentEstimator != o2::hf_centrality::CentralityEstimator::None) { cent = o2::hf_centrality::getCentralityColl(collision, centEstimator); if (cent < centralityMin || cent >= centralityMax) { @@ -102,26 +102,32 @@ struct HfTaskHiddenCharm { } } - for (const auto& proton1 : protonIds) { - for (const auto& proton2 : protonIds) { - if (proton1.trackId() >= proton2.trackId()) { - continue; // avoid double counting and self-pairs - } + for (auto it1 = protonIds.begin(); it1 != protonIds.end(); ++it1) { + for (auto it2 = it1 + 1; it2 != protonIds.end(); ++it2) { + + const auto& proton1 = *it1; + const auto& proton2 = *it2; const int sign = (proton1.sign() * proton2.sign() > 0) ? 1 : -1; - if ((sign == 1 && !fillOnlyLikeSign) || (sign == -1 && !fillOnlyUnlikeSign)) { + + if (sign == 1 && !fillLikeSign) continue; - } + if (sign == -1 && !fillUnlikeSign) + continue; + std::array pVec1{proton1.px(), proton1.py(), proton1.pz()}; std::array pVec2{proton2.px(), proton2.py(), proton2.pz()}; + float invMass = RecoDecay::m(std::array{pVec1, pVec2}, std::array{o2::constants::physics::MassProton, o2::constants::physics::MassProton}); - float ptEtac = RecoDecay::pt(RecoDecay::sumOfVec(pVec1, pVec2)); - registry.fill(HIST("hSparseHiddenCharm"), invMass, ptEtac, sign, cent); + float pt = RecoDecay::pt(RecoDecay::sumOfVec(pVec1, pVec2)); + + registry.fill(HIST("hSparseHiddenCharm"), invMass, pt, sign, cent); if (sign == 1) { - registry.fill(HIST("hPtVsInvMassLikeSign"), invMass, ptEtac); - } else if (sign == -1) { - registry.fill(HIST("hPtVsInvMassUnlikeSign"), invMass, ptEtac); - registry.fill(HIST("hPtVsInvMassAllSign"), invMass, ptEtac); + registry.fill(HIST("hPtVsInvMassLikeSign"), invMass, pt); + } else { + registry.fill(HIST("hPtVsInvMassUnlikeSign"), invMass, pt); } + + registry.fill(HIST("hPtVsInvMassAllSign"), invMass, pt); } } } diff --git a/PWGHF/D2H/Tasks/taskLb.cxx b/PWGHF/D2H/Tasks/taskLb.cxx index 9a605393658..6ad885313c6 100644 --- a/PWGHF/D2H/Tasks/taskLb.cxx +++ b/PWGHF/D2H/Tasks/taskLb.cxx @@ -121,65 +121,65 @@ struct HfTaskLb { void init(InitContext&) { - registry.add("hMass", "#Lambda_{b}^{0} candidates;inv. mass #Lambda_{c}^{#plus}#pi^{#minus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLength", "#Lambda_{b}^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLengthXY", "#Lambda_{b}^{0} candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong0", "#Lambda_{b}^{0} candidates;prong 0 (#Lambda_{c}^{#plus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hd0Prong1", "#Lambda_{b}^{0} candidates;prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hCPA", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hEta", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hRapidity", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hImpParErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter error (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLenErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hDecLenXYErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hIPProd", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter product;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("hInvMassLc", "#Lambda_{b}^{0} candidates;prong0, #Lambda_{c}^{+} inv. mass (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0, 5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hMass", "#Lambda_{b}^{0} candidates;inv. mass #Lambda_{c}^{#plus}#pi^{#minus} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLength", "#Lambda_{b}^{0} candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLengthXY", "#Lambda_{b}^{0} candidates;decay length xy (cm);entries", {HistType::kTH2F, {{200, 0., 0.4}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hd0Prong0", "#Lambda_{b}^{0} candidates;prong 0 (#Lambda_{c}^{#plus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hd0Prong1", "#Lambda_{b}^{0} candidates;prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hCPA", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hEta", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hRapidity", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hImpParErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter error (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLenErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hDecLenXYErr", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate decay length xy error (cm);entries", {HistType::kTH2F, {{100, 0., 1.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hIPProd", "#Lambda_{b}^{0} candidates;#Lambda_{b}^{0} candidate impact parameter product;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hInvMassLc", "#Lambda_{b}^{0} candidates;prong0, #Lambda_{c}^{+} inv. mass (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{500, 0, 5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // Now add MC histograms - registry.add("MC/hEtaGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hYProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaProng0Gen", "MC particles (matched);prong 0 (#Lambda_{b}^{0}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPARecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPARecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPAxyRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPAxyRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPALcRecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hCPALcRecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hEtaRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hRapidityRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hRapidityRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#y};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - - registry.add("MC/hPtProng0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hPtProng1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hMassRecSig", "#Lambda_{b}^{0} candidates (matched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.00}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hMassRecBg", "#Lambda_{b}^{0} candidates (unmatched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.0}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hd0Prong1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthXYRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length xy (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthXYRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length xy(cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthLcRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthLcRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthNormRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hDecLengthNormRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hImpParProdLbRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hImpParProdLbRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - - registry.add("MC/hChi2PCARecSig", "#Lambda_{b}^{0} candidates (matched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hChi2PCARecBg", "#Lambda_{b}^{0} candidates (unmatched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hThetaStarRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/hThetaStarRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYGen", "MC particles (matched);#Lambda_{b}^{0} candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{p}_{T}^{gen} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYProng0Gen", "MC particles (matched);prong 0 (#Lambda_{c}^{+}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hYProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaProng0Gen", "MC particles (matched);prong 0 (#Lambda_{b}^{0}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaProng1Gen", "MC particles (matched);prong 1 (#pi^{-}) #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2, 2}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPARecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPARecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPAxyRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPAxyRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate CPAxy;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPALcRecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hCPALcRecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) cosine of pointing angle;entries", {HistType::kTH2F, {{220, 0., 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hEtaRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#eta};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hRapidityRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate #it{y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hRapidityRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate #it{#y};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("MC/hPtProng0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hPtProng1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH2F, {{100, 0., 10.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hMassRecSig", "#Lambda_{b}^{0} candidates (matched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.00}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hMassRecBg", "#Lambda_{b}^{0} candidates (unmatched);inv. mass #Lambda_{c}^{+}#pi^{+} (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{300, 4.0, 7.0}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong0RecSig", "#Lambda_{b}^{0} candidates (matched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong1RecSig", "#Lambda_{b}^{0} candidates (matched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong0RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 0 (#Lambda_{c}^{+}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hd0Prong1RecBg", "#Lambda_{b}^{0} candidates (unmatched);prong 1 (#pi^{#minus}) DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{200, -0.05, 0.05}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthXYRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length xy (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthXYRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length xy(cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthLcRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthLcRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthNormRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hDecLengthNormRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate decay length (cm);entries", {HistType::kTH2F, {{100, 0., 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hImpParProdLbRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hImpParProdLbRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} candidate impact parameter product ;entries", {HistType::kTH2F, {{100, -0.5, 0.5}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + + registry.add("MC/hChi2PCARecSig", "#Lambda_{b}^{0} candidates (matched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hChi2PCARecBg", "#Lambda_{b}^{0} candidates (unmatched);sum of distances of the secondary vertex to its prongs;entries", {HistType::kTH2F, {{240, -0.01, 0.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hThetaStarRecSig", "#Lambda_{b}^{0} candidates (matched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/hThetaStarRecBg", "#Lambda_{b}^{0} candidates (unmatched);#Lambda_{b}^{0} #cos(#theta^{*});entries", {HistType::kTH2F, {{110, -1.1, 1.1}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); } void processData(aod::Collisions::iterator const& collision, diff --git a/PWGHF/D2H/Tasks/taskLbReduced.cxx b/PWGHF/D2H/Tasks/taskLbReduced.cxx index fe43e8d2c6e..da430368482 100644 --- a/PWGHF/D2H/Tasks/taskLbReduced.cxx +++ b/PWGHF/D2H/Tasks/taskLbReduced.cxx @@ -73,6 +73,9 @@ DECLARE_SOA_COLUMN(E, e, float); DECLARE_SOA_COLUMN(NSigTpcPiBachelor, nSigTpcPiBachelor, float); //! TPC Nsigma separation for bachelor with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiBachelor, nSigTofPiBachelor, float); //! TOF Nsigma separation for bachelor with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiBachelor, nSigTpcTofPiBachelor, float); //! Combined TPC and TOF Nsigma separation for bachelor with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPiLcProng0, nSigTpcPiLcProng0, float); //! TPC Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPiLcProng0, nSigTofPiLcProng0, float); //! TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPiLcProng0, nSigTpcTofPiLcProng0, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcPrLcProng0, nSigTpcPrLcProng0, float); //! TPC Nsigma separation for Lc-baryon prong0 with proton mass hypothesis DECLARE_SOA_COLUMN(NSigTofPrLcProng0, nSigTofPrLcProng0, float); //! TOF Nsigma separation for Lc-baryon prong0 with proton mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPrLcProng0, nSigTpcTofPrLcProng0, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with proton mass hypothesis @@ -82,6 +85,9 @@ DECLARE_SOA_COLUMN(NSigTpcTofKaLcProng1, nSigTpcTofKaLcProng1, float); DECLARE_SOA_COLUMN(NSigTpcPiLcProng2, nSigTpcPiLcProng2, float); //! TPC Nsigma separation for Lc-baryon prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofPiLcProng2, nSigTofPiLcProng2, float); //! TOF Nsigma separation for Lc-baryon prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcTofPiLcProng2, nSigTpcTofPiLcProng2, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcPrLcProng2, nSigTpcPrLcProng2, float); //! TPC Nsigma separation for Lc-baryon prong2 with proton mass hypothesis +DECLARE_SOA_COLUMN(NSigTofPrLcProng2, nSigTofPrLcProng2, float); //! TOF Nsigma separation for Lc-baryon prong2 with proton mass hypothesis +DECLARE_SOA_COLUMN(NSigTpcTofPrLcProng2, nSigTpcTofPrLcProng2, float); //! Combined TPC and TOF Nsigma separation for Lc-baryon prong2 with proton mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -127,6 +133,9 @@ DECLARE_SOA_TABLE(HfRedCandLbLites, "AOD", "HFREDCANDLBLITE", //! Table with som hf_cand_lb_lite::ItsNClsLcProngMin, hf_cand_lb_lite::TpcNClsCrossedRowsLcProngMin, hf_cand_lb_lite::TpcChi2NClLcProngMax, + hf_cand_lb_lite::NSigTpcPiLcProng0, + hf_cand_lb_lite::NSigTofPiLcProng0, + hf_cand_lb_lite::NSigTpcTofPiLcProng0, hf_cand_lb_lite::NSigTpcPrLcProng0, hf_cand_lb_lite::NSigTofPrLcProng0, hf_cand_lb_lite::NSigTpcTofPrLcProng0, @@ -136,6 +145,9 @@ DECLARE_SOA_TABLE(HfRedCandLbLites, "AOD", "HFREDCANDLBLITE", //! Table with som hf_cand_lb_lite::NSigTpcPiLcProng2, hf_cand_lb_lite::NSigTofPiLcProng2, hf_cand_lb_lite::NSigTpcTofPiLcProng2, + hf_cand_lb_lite::NSigTpcPrLcProng2, + hf_cand_lb_lite::NSigTofPrLcProng2, + hf_cand_lb_lite::NSigTpcTofPrLcProng2, hf_cand_lb_reduced::Prong0MlScoreBkg, hf_cand_lb_reduced::Prong0MlScorePrompt, hf_cand_lb_reduced::Prong0MlScoreNonprompt, @@ -587,6 +599,9 @@ struct HfTaskLbReduced { candLc.itsNClsProngMin(), candLc.tpcNClsCrossedRowsProngMin(), candLc.tpcChi2NClProngMax(), + candLc.tpcNSigmaPiProng0(), + candLc.tofNSigmaPiProng0(), + candLc.tpcTofNSigmaPiProng0(), candLc.tpcNSigmaPrProng0(), candLc.tofNSigmaPrProng0(), candLc.tpcTofNSigmaPrProng0(), @@ -596,6 +611,9 @@ struct HfTaskLbReduced { candLc.tpcNSigmaPiProng2(), candLc.tofNSigmaPiProng2(), candLc.tpcTofNSigmaPiProng2(), + candLc.tpcNSigmaPrProng2(), + candLc.tofNSigmaPrProng2(), + candLc.tpcTofNSigmaPrProng2(), prong0MlScoreBkg, prong0MlScorePrompt, prong0MlScoreNonprompt, diff --git a/PWGHF/D2H/Tasks/taskLc.cxx b/PWGHF/D2H/Tasks/taskLc.cxx index e7144cf505d..68bc5266735 100644 --- a/PWGHF/D2H/Tasks/taskLc.cxx +++ b/PWGHF/D2H/Tasks/taskLc.cxx @@ -17,7 +17,6 @@ /// \author Vít Kučera , CERN /// \author Annalena Kalteyer , GSI Darmstadt /// \author Biao Zhang , Heidelberg University -/// \author Ran Tu , Fudan University /// \author Oleksii Lubynets , Heidelberg University, GSI Darmstadt #include "PWGHF/Core/CentralityEstimation.h" @@ -29,9 +28,6 @@ #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/Utils/utilsEvSelHf.h" -#include "PWGHF/Utils/utilsUpcHf.h" -#include "PWGUD/Core/SGSelector.h" -#include "PWGUD/Core/UPCHelpers.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" @@ -49,7 +45,6 @@ #include #include #include -#include #include #include @@ -69,7 +64,7 @@ using namespace o2::framework::expressions; using namespace o2::hf_centrality; using namespace o2::hf_occupancy; using namespace o2::hf_evsel; -using namespace o2::analysis::hf_upc; +using namespace o2::constants::physics; /// Λc± → p± K∓ π± analysis task struct HfTaskLc { @@ -79,26 +74,24 @@ struct HfTaskLc { Configurable> binsPt{"binsPt", std::vector{hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits"}; // ThnSparse for ML outputScores and Vars Configurable fillTHn{"fillTHn", false, "fill THn"}; - Configurable fillUPCTHnLite{"fillUPCTHnLite", false, "fill THn"}; Configurable storeOccupancy{"storeOccupancy", true, "Flag to store occupancy information"}; Configurable occEstimator{"occEstimator", 2, "Occupancy estimation (None: 0, ITS: 1, FT0C: 2)"}; - Configurable storeProperLifetime{"storeProperLifetime", false, "Flag to store proper lifetime"}; + Configurable storeProperDecayTime{"storeProperDecayTime", false, "Flag to store proper decay time"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; - HfEventSelection hfEvSel; // event selection and monitoring - HfUpcGapThresholds upcThresholds; // UPC gap determination thresholds + HfEventSelection hfEvSel; // event selection and monitoring SliceCache cache; Service ccdb{}; - using Collisions = soa::Join; - using CollisionsMc = soa::Join; - using CollisionsWithFT0C = soa::Join; - using CollisionsMcWithFT0C = soa::Join; - using CollisionsWithFT0M = soa::Join; - using CollisionsMcWithFT0M = soa::Join; + using Collisions = soa::Join; + using CollisionsMc = soa::Join; + using CollisionsWithFT0C = soa::Join; + using CollisionsMcWithFT0C = soa::Join; + using CollisionsWithFT0M = soa::Join; + using CollisionsMcWithFT0M = soa::Join; using LcCandidates = soa::Filtered>; using LcCandidatesMl = soa::Filtered>; @@ -126,21 +119,8 @@ struct HfTaskLc { ConfigurableAxis thnConfigAxisGenPtB{"thnConfigAxisGenPtB", {1000, 0, 100}, "Gen Pt B"}; ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Number of PV contributors"}; ConfigurableAxis thnConfigAxisOccupancy{"thnConfigAxisOccupancy", {14, 0, 14000}, "axis for centrality"}; - ConfigurableAxis thnConfigAxisProperLifetime{"thnConfigAxisProperLifetime", {200, 0, 2}, "Proper lifetime, ps"}; - ConfigurableAxis thnConfigAxisGapType{"thnConfigAxisGapType", {7, -1.5, 5.5}, "axis for UPC gap type (see TrueGap enum in o2::aod::sgselector)"}; - ConfigurableAxis thnConfigAxisFV0A{"thnConfigAxisFV0A", {1001, -1.5, 999.5}, "axis for FV0-A amplitude (a.u.)"}; - ConfigurableAxis thnConfigAxisFT0{"thnConfigAxisFT0", {1001, -1.5, 999.5}, "axis for FT0 amplitude (a.u.)"}; - ConfigurableAxis thnConfigAxisZN{"thnConfigAxisZN", {510, -1.5, 49.5}, "axis for ZN energy (a.u.)"}; - ConfigurableAxis thnConfigAxisZNTime{"thnConfigAxisZNTime", {200, -10, 10}, "axis for ZN energy (a.u.)"}; + ConfigurableAxis thnConfigAxisProperDecayTime{"thnConfigAxisProperDecayTime", {200, 0, 2}, "Proper decay time, ps"}; HistogramRegistry registry{"registry", {}}; - HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - // Factors for conversion between units - constexpr static float CtToProperLifetimePs = 1.f / o2::constants::physics::LightSpeedCm2PS; - constexpr static float NanoToPico = 1000.f; - // Names of folders and suffixes for MC signal histograms - constexpr static std::string_view SignalFolders[] = {"signal", "prompt", "nonprompt"}; - constexpr static std::string_view SignalSuffixes[] = {"", "Prompt", "NonPrompt"}; enum MlClasses : int { MlClassBackground = 0, @@ -152,18 +132,24 @@ struct HfTaskLc { enum SignalClasses : int { Signal = 0, Prompt, - NonPrompt + NonPrompt, + NumberOfSignalClasses }; + // Factor for conversion between units + constexpr static float CtToProperDecayTimePs = 1.f / o2::constants::physics::LightSpeedCm2PS; + // Names of folders and suffixes for MC signal histograms + constexpr static std::array SignalFolders = {"signal", "prompt", "nonprompt"}; + constexpr static std::array SignalSuffixes = {"", "Prompt", "NonPrompt"}; + void init(InitContext&) { - const std::array doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, doprocessDataWithMl, doprocessDataWithMlWithFT0C, doprocessDataWithMlWithFT0M, doprocessDataWithMlWithUpc, doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M, doprocessMcWithMl, doprocessMcWithMlWithFT0C, doprocessMcWithMlWithFT0M, doprocessDataStdWithUpc}; + const std::array doprocess{doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, doprocessDataWithMl, doprocessDataWithMlWithFT0C, doprocessDataWithMlWithFT0M, doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M, doprocessMcWithMl, doprocessMcWithMlWithFT0C, doprocessMcWithMlWithFT0M}; if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); } - const bool isData = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M || doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M || doprocessDataWithMlWithUpc; - const bool isUpc = doprocessDataWithMlWithUpc || doprocessDataStdWithUpc; + const bool isData = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M || doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M; auto addHistogramsRec = [&](const std::string& histoName, const std::string& xAxisTitle, const std::string& yAxisTitle, const HistogramConfigSpec& configSpec) { if (isData) { @@ -202,8 +188,8 @@ struct HfTaskLc { addHistogramsRec("hDecLength", "decay length (cm)", "entries", {HistType::kTH1F, {{400, 0., 1.}}}); /// decay length xy candidate addHistogramsRec("hDecLengthxy", "decay length xy (cm)", "entries", {HistType::kTH1F, {{400, 0., 1.}}}); - /// proper lifetime - addHistogramsRec("hCt", "proper lifetime (#Lambda_{c}) * #it{c} (cm)", "entries", {HistType::kTH1F, {{100, 0., 0.2}}}); + /// proper decay time + addHistogramsRec("hCt", "proper decay time (#Lambda_{c}) * #it{c} (cm)", "entries", {HistType::kTH1F, {{100, 0., 0.2}}}); /// cosine of pointing angle addHistogramsRec("hCPA", "cosine of pointing angle", "entries", {HistType::kTH1F, {{110, -1.1, 1.1}}}); /// cosine of pointing angle xy @@ -218,7 +204,7 @@ struct HfTaskLc { addHistogramsRec("hPhi", "#it{#Phi}", "entries", {HistType::kTH1F, {{100, 0., 6.3}}}); addHistogramsGen("hPhi", "#it{#Phi}", "entries", {HistType::kTH1F, {{100, 0., 6.3}}}); - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); /// mass candidate if (isData) { registry.add("Data/hMassVsPtVsNPvContributors", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2}); p_{T}; Number of PV contributors", {HistType::kTH3F, {{600, 1.98, 2.58}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {5000, 0., 10000.}}}); @@ -235,8 +221,8 @@ struct HfTaskLc { /// decay length xy candidate addHistogramsRec("hDecLengthxyVsPt", "decay length xy (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{400, 0., 1.}, {vbins}}}); - /// proper lifetime - addHistogramsRec("hCtVsPt", "proper lifetime (#Lambda_{c}) * #it{c} (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); + /// proper decay time + addHistogramsRec("hCtVsPt", "proper decay time (#Lambda_{c}) * #it{c} (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 0.2}, {vbins}}}); /// cosine of pointing angle addHistogramsRec("hCPAVsPt", "cosine of pointing angle", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{110, -1.1, 1.1}, {vbins}}}); @@ -266,14 +252,6 @@ struct HfTaskLc { /// decay length error addHistogramsRec("hDecLenErrVsPt", "decay length error (cm)", "#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 1.}, {vbins}}}); - if (isUpc) { - qaRegistry.add("Data/fitInfo/ampFT0A_vs_ampFT0C", "FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)", {HistType::kTH2F, {{500, 0., 500}, {500, 0., 500}}}); - qaRegistry.add("Data/zdc/energyZNA_vs_energyZNC", "ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)", {HistType::kTH2F, {{100, 0., 10}, {100, 0., 10}}}); - qaRegistry.add("Data/zdc/timeZNA_vs_timeZNC", "ZNA vs ZNC time;ZNA Time;ZNC time", {HistType::kTH2F, {{200, -10., 10}, {200, -10., 10}}}); - qaRegistry.add("Data/hUpcGapAfterSelection", "UPC gap type after selection;Gap side;Counts", {HistType::kTH1F, {{7, -1.5, 5.5}}}); - qaRegistry.add("Data/hUpcMulti", "Multiplicity of UPC events;Multiplicity;Counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); - qaRegistry.add("Data/hUpcVtz", "Vertex Z position of UPC events;Vz (cm);Counts", {HistType::kTH1F, {{200, -10., 10.}}}); - } if (fillTHn) { const AxisSpec thnAxisMass{thnConfigAxisMass, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T}(#Lambda_{c}^{+}) (GeV/#it{c})"}; @@ -292,40 +270,28 @@ struct HfTaskLc { const AxisSpec thnAxisPtB{thnConfigAxisGenPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; const AxisSpec thnAxisTracklets{thnConfigAxisNumPvContr, "Number of PV contributors"}; const AxisSpec thnAxisOccupancy{thnConfigAxisOccupancy, "Occupancy"}; - const AxisSpec thnAxisProperLifetime{thnConfigAxisProperLifetime, "T_{proper} (ps)"}; - const AxisSpec thnAxisFV0A{thnConfigAxisFV0A, "FV0-A amplitude"}; - const AxisSpec thnAxisFT0A{thnConfigAxisFT0, "FT0-A amplitude"}; - const AxisSpec thnAxisFT0C{thnConfigAxisFT0, "FT0-C amplitude"}; - const AxisSpec thnAxisZNA{thnConfigAxisZN, "ZNA energy"}; - const AxisSpec thnAxisZNC{thnConfigAxisZN, "ZNC energy"}; - const AxisSpec thnAxisZNATime{thnConfigAxisZNTime, "ZNA time"}; - const AxisSpec thnAxisZNCTime{thnConfigAxisZNTime, "ZNC time"}; - - bool const isDataWithMl = doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M || doprocessDataWithMlWithUpc; + const AxisSpec thnAxisProperDecayTime{thnConfigAxisProperDecayTime, "#it{t}_{proper} (ps)"}; + const AxisSpec thnAxisProperDecayTimeGen{thnConfigAxisProperDecayTime, "#it{t}_{proper, gen} (ps)"}; + + bool const isDataWithMl = doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M; bool const isMcWithMl = doprocessMcWithMl || doprocessMcWithMlWithFT0C || doprocessMcWithMlWithFT0M; - bool const isDataStd = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M || doprocessDataStdWithUpc; + bool const isDataStd = doprocessDataStd || doprocessDataStdWithFT0C || doprocessDataStdWithFT0M; bool const isMcStd = doprocessMcStd || doprocessMcStdWithFT0C || doprocessMcStdWithFT0M; - std::vector axesStd, axesWithBdt, axesGen, axesUpc, axesUpcWithBdt; + std::vector axesStd, axesWithBdt, axesGen; - if (isDataStd && !isUpc) { + if (isDataStd) { axesStd = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets}; } - if (isDataStd && isUpc) { - axesUpc = {thnAxisMass, thnAxisPt, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets, thnAxisFV0A, thnAxisFT0A, thnAxisFT0C, thnAxisZNA, thnAxisZNC, thnAxisZNATime, thnAxisZNCTime}; - } if (isMcStd) { axesStd = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisPtProng0, thnAxisPtProng1, thnAxisPtProng2, thnAxisChi2PCA, thnAxisDecLength, thnAxisCPA, thnAxisTracklets, thnAxisPtB, thnAxisCanType}; } if (isMcStd || isMcWithMl) { axesGen = {thnAxisPt, thnAxisCentrality, thnAxisY, thnAxisTracklets, thnAxisPtB, thnAxisCanType}; } - if (isDataWithMl && !isUpc) { + if (isDataWithMl) { axesWithBdt = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets}; } - if (isDataWithMl && isUpc) { - axesUpcWithBdt = {thnAxisMass, thnAxisPt, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets, thnAxisFV0A, thnAxisFT0A, thnAxisFT0C, thnAxisZNA, thnAxisZNC, thnAxisZNATime, thnAxisZNCTime}; - } if (isMcWithMl) { axesWithBdt = {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcPrompt, thnAxisBdtScoreLcNonPrompt, thnAxisTracklets, thnAxisPtB, thnAxisCanType}; } @@ -337,20 +303,17 @@ struct HfTaskLc { } } } - if (storeProperLifetime) { + if (storeProperDecayTime) { for (const auto& axes : std::array*, 3>{&axesWithBdt, &axesStd, &axesGen}) { if (!axes->empty()) { - axes->push_back(thnAxisProperLifetime); + axes->push_back(thnAxisProperDecayTime); + if (!isData && axes != &axesGen) { + axes->push_back(thnAxisProperDecayTimeGen); + } } } } - if (isUpc) { - if (isDataStd) { - registry.add("hnLcUpcVars", "THn for Lambdac candidates for Data in UPC", HistType::kTHnSparseF, axesUpc); - } else if (isDataWithMl) { - registry.add("hnLcUpcVarsWithBdt", "THn for Lambdac candidates with BDT scores for data in UPC", HistType::kTHnSparseF, axesUpcWithBdt); - } - } else if (isDataWithMl) { + if (isDataWithMl) { registry.add("hnLcVarsWithBdt", "THn for Lambdac candidates with BDT scores for data with ML", HistType::kTHnSparseF, axesWithBdt); } else if (isMcWithMl) { registry.add("hnLcVarsWithBdt", "THn for Lambdac candidates with BDT scores for mc with ML", HistType::kTHnSparseF, axesWithBdt); @@ -363,10 +326,6 @@ struct HfTaskLc { } } - if (isUpc) { - hfEvSel.addHistograms(qaRegistry); // collision monitoring - } - ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -381,13 +340,34 @@ struct HfTaskLc { return o2::hf_centrality::getCentralityColl(collision); } + /// Evaluate decay time of generated particle + /// \param mcParticleProng0 one of generated particle's daughters + /// \param motherParticle generated particle + /// \return decay time in picoseconds. For nonprompt particles it is evaluated as if it was prompt (as it is calculated for data) + float evaluateMcGenDecayTime(const McParticles3ProngMatched::iterator& mcParticleProng0, const McParticles3ProngMatched::iterator& motherParticle) + { + const auto mcCollision = motherParticle.template mcCollision_as(); + const float pMother = motherParticle.p(); + const float pvX = mcCollision.posX(); + const float pvY = mcCollision.posY(); + const float pvZ = mcCollision.posZ(); + const float svX = mcParticleProng0.vx(); + const float svY = mcParticleProng0.vy(); + const float svZ = mcParticleProng0.vz(); + + const float decayLength = static_cast(RecoDecay::distance(std::array{svX, svY, svZ}, std::array{pvX, pvY, pvZ})); + const float properDecayTime = decayLength * static_cast(MassLambdaCPlus) / LightSpeedCm2PS / pMother; + + return properDecayTime; + } + /// Helper function for filling MC reconstructed histograms for prompt, nonpromt and common (signal) /// \param candidate is a reconstructed candidate /// \tparam SignalType is an enum defining which histogram in which folder (signal, prompt or nonpromt) to fill template void fillHistogramsRecSig(CandidateType const& candidate) { - const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as>(); + const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as(); const auto pdgCodeProng0 = std::abs(mcParticleProng0.pdgCode()); if ((candidate.isSelLcToPKPi() >= selectionFlagLc) && pdgCodeProng0 == kProton) { registry.fill(HIST("MC/reconstructed/") + HIST(SignalFolders[SignalType]) + HIST("/hMassRecSig") + HIST(SignalSuffixes[SignalType]), HfHelper::invMassLcToPKPi(candidate)); @@ -432,8 +412,8 @@ struct HfTaskLc { /// Fill MC histograms at reconstruction level /// \tparam FillMl switch to fill ML histograms - template - void fillHistosMcRec(CollType const& collision, CandLcMcRec const& candidates, CandLcMcGen const& mcParticles) + template + void fillHistosMcRec(CollType const& collision, CandLcMcRec const& candidates, McParticles3ProngMatched const& mcParticles) { const auto thisCollId = collision.globalIndex(); const auto& groupedLcCandidates = candidates.sliceBy(candLcPerCollision, thisCollId); @@ -450,7 +430,7 @@ struct HfTaskLc { if (std::abs(candidate.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { // Get the corresponding MC particle. - const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as>(); + const auto& mcParticleProng0 = candidate.template prong0_as().template mcParticle_as(); const auto pdgCodeProng0 = std::abs(mcParticleProng0.pdgCode()); const auto indexMother = RecoDecay::getMother(mcParticles, mcParticleProng0, o2::constants::physics::Pdg::kLambdaCPlus, true); const auto particleMother = mcParticles.rawIteratorAt(indexMother); @@ -467,6 +447,8 @@ struct HfTaskLc { const auto numPvContributors = collision.numContrib(); const auto ptRecB = candidate.ptBhadMotherPart(); + const float properDecayTimeGen = evaluateMcGenDecayTime(mcParticleProng0, particleMother); + /// MC reconstructed signal fillHistogramsRecSig(candidate); @@ -485,7 +467,7 @@ struct HfTaskLc { occ = o2::hf_occupancy::getOccupancyColl(collision, occEstimator); } double outputBkg(-1), outputPrompt(-1), outputFD(-1); - const float properLifetime = HfHelper::ctLc(candidate) * CtToProperLifetimePs; + const float properDecayTime = HfHelper::ctLc(candidate) * CtToProperDecayTimePs; auto fillTHnRecSig = [&](bool isPKPi) { const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); @@ -508,8 +490,9 @@ struct HfTaskLc { if (storeOccupancy && occEstimator != o2::hf_occupancy::OccupancyEstimator::None) { valuesToFill.push_back(occ); } - if (storeProperLifetime) { - valuesToFill.push_back(properLifetime); + if (storeProperDecayTime) { + valuesToFill.push_back(properDecayTime); + valuesToFill.push_back(properDecayTimeGen); } if constexpr (FillMl) { registry.get(HIST("hnLcVarsWithBdt"))->Fill(valuesToFill.data()); @@ -532,8 +515,8 @@ struct HfTaskLc { /// Helper function for filling MC generated histograms for prompt, nonpromt and common (signal) /// \param particle is a generated particle /// \tparam SignalType is an enum defining which histogram in which folder (signal, prompt or nonpromt) to fill - template - void fillHistogramsGen(ParticleType const& particle) + template + void fillHistogramsGen(McParticles3ProngMatched::iterator const& particle) { registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hPtGen") + HIST(SignalSuffixes[SignalType]), particle.pt()); registry.fill(HIST("MC/generated/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaGen") + HIST(SignalSuffixes[SignalType]), particle.eta()); @@ -545,8 +528,8 @@ struct HfTaskLc { } /// Fill MC histograms at generated level - template - void fillHistosMcGen(CandLcMcGen const& mcParticles, Coll const& recoCollisions) + template + void fillHistosMcGen(McParticles3ProngMatched const& mcParticles, Coll const& recoCollisions) { // MC gen. for (const auto& particle : mcParticles) { @@ -569,10 +552,9 @@ struct HfTaskLc { occ = o2::hf_occupancy::getOccupancyGenColl(recoCollsPerMcColl, occEstimator); } - const auto& mcDaughter0 = particle.template daughters_as>().begin(); - const float p2m = particle.p() / o2::constants::physics::MassLambdaCPlus; - const float gamma = std::sqrt(1 + p2m * p2m); // mother's particle Lorentz factor - const float properLifetime = mcDaughter0.vt() * NanoToPico / gamma; // from ns to ps * from lab time to proper time + const auto mcDaughter0 = particle.template daughters_as().begin(); + + const float properDecayTime = evaluateMcGenDecayTime(mcDaughter0, particle); fillHistogramsGen(particle); @@ -584,8 +566,8 @@ struct HfTaskLc { if (storeOccupancy && occEstimator != o2::hf_occupancy::OccupancyEstimator::None) { valuesToFill.push_back(occ); } - if (storeProperLifetime) { - valuesToFill.push_back(properLifetime); + if (storeProperDecayTime) { + valuesToFill.push_back(properDecayTime); } registry.get(HIST("hnLcVarsGen"))->Fill(valuesToFill.data()); } @@ -678,7 +660,7 @@ struct HfTaskLc { occ = o2::hf_occupancy::getOccupancyColl(collision, occEstimator); } double outputBkg(-1), outputPrompt(-1), outputFD(-1); - const float properLifetime = HfHelper::ctLc(candidate) * CtToProperLifetimePs; + const float properDecayTime = HfHelper::ctLc(candidate) * CtToProperDecayTimePs; auto fillTHnData = [&](bool isPKPi) { const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); @@ -701,8 +683,8 @@ struct HfTaskLc { if (storeOccupancy && occEstimator != o2::hf_occupancy::OccupancyEstimator::None) { valuesToFill.push_back(occ); } - if (storeProperLifetime) { - valuesToFill.push_back(properLifetime); + if (storeProperDecayTime) { + valuesToFill.push_back(properDecayTime); } if constexpr (FillMl) { registry.get(HIST("hnLcVarsWithBdt"))->Fill(valuesToFill.data()); @@ -732,133 +714,12 @@ struct HfTaskLc { } } - template - void runAnalysisPerCollisionDataWithUpc(CollType const& collisions, - CandType const& candidates, - BCsType const& bcs, - aod::FT0s const& ft0s, - aod::FV0As const& fv0as, - aod::FDDs const& fdds - - ) - { - for (const auto& collision : collisions) { - float centrality{-1.f}; - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc(collision, centrality, ccdb, qaRegistry, bcs); - if (rejectionMask != 0) { - /// at least one event selection not satisfied --> reject the candidate - continue; - } - const auto thisCollId = collision.globalIndex(); - const auto& groupedLcCandidates = candidates.sliceBy(candLcPerCollision, thisCollId); - const auto numPvContributors = collision.numContrib(); - const auto& bc = collision.template bc_as(); - - // Determine gap type using SGSelector with BC range checking - const auto gapResult = hf_upc::determineGapType(collision, bcs, upcThresholds); - const int gap = gapResult.value; - - // Use the BC with FIT activity if available from SGSelector - auto bcForUPC = bc; - if (gapResult.bc) { - bcForUPC = *(gapResult.bc); - } - - // Get FIT information from the UPC BC - upchelpers::FITInfo fitInfo{}; - udhelpers::getFITinfo(fitInfo, bcForUPC, bcs, ft0s, fv0as, fdds); - - // Get ZDC energies if available (extract once and reuse) - const bool hasZdc = bcForUPC.has_zdc(); - float zdcEnergyZNA = -1.f; - float zdcEnergyZNC = -1.f; - float zdcTimeZNA = -1.f; - float zdcTimeZNC = -1.f; - - if (hasZdc) { - const auto zdc = bcForUPC.zdc(); - zdcEnergyZNA = zdc.energyCommonZNA(); - zdcEnergyZNC = zdc.energyCommonZNC(); - zdcTimeZNA = zdc.timeZNA(); - zdcTimeZNC = zdc.timeZNC(); - qaRegistry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C); - qaRegistry.fill(HIST("Data/zdc/energyZNA_vs_energyZNC"), zdcEnergyZNA, zdcEnergyZNC); - qaRegistry.fill(HIST("Data/zdc/timeZNA_vs_timeZNC"), zdcTimeZNA, zdcTimeZNC); - qaRegistry.fill(HIST("Data/hUpcGapAfterSelection"), static_cast(gap)); - } - for (const auto& candidate : groupedLcCandidates) { - if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { - continue; - } - if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { - continue; - } - const auto pt = candidate.pt(); - const auto ptProng0 = candidate.ptProng0(); - const auto ptProng1 = candidate.ptProng1(); - const auto ptProng2 = candidate.ptProng2(); - const auto decayLength = candidate.decayLength(); - const auto chi2PCA = candidate.chi2PCA(); - const auto cpa = candidate.cpa(); - if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { - qaRegistry.fill(HIST("Data/hUpcMulti"), collision.multNTracksPV()); - qaRegistry.fill(HIST("Data/hUpcVtz"), collision.posZ()); - } - - if (fillTHn) { - double outputBkg(-1), outputPrompt(-1), outputFD(-1); - - auto fillTHnData = [&](bool isPKPi) { - const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); - - if constexpr (FillMl) { - const auto& mlProb = isPKPi ? candidate.mlProbLcToPKPi() : candidate.mlProbLcToPiKP(); - if (mlProb.size() == NumberOfMlClasses) { - outputBkg = mlProb[MlClassBackground]; /// bkg score - outputPrompt = mlProb[MlClassPrompt]; /// prompt score - outputFD = mlProb[MlClassNonPrompt]; /// non-prompt score - } - /// Fill the ML outputScores and variables of candidate - if (fillUPCTHnLite) { - if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { - std::vector valuesToFill{massLc, pt, outputBkg, outputPrompt, outputFD, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVarsWithBdt"))->Fill(valuesToFill.data()); - } - } else { - std::vector valuesToFill{massLc, pt, outputBkg, outputPrompt, outputFD, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVarsWithBdt"))->Fill(valuesToFill.data()); - } - - } else { - if (fillUPCTHnLite) { - if (gap == o2::aod::sgselector::TrueGap::SingleGapA || gap == o2::aod::sgselector::TrueGap::SingleGapC) { - std::vector valuesToFill{massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVars"))->Fill(valuesToFill.data()); - } - } else { - std::vector valuesToFill{massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, static_cast(numPvContributors), static_cast(fitInfo.ampFV0A), static_cast(fitInfo.ampFT0A), static_cast(fitInfo.ampFT0C), static_cast(zdcEnergyZNA), static_cast(zdcEnergyZNC), static_cast(zdcTimeZNA), static_cast(zdcTimeZNC)}; - registry.get(HIST("hnLcUpcVars"))->Fill(valuesToFill.data()); - } - } - }; - - if (candidate.isSelLcToPKPi() >= selectionFlagLc) { - fillTHnData(true); - } - if (candidate.isSelLcToPiKP() >= selectionFlagLc) { - fillTHnData(false); - } - } - } - } - } - /// Run the analysis on MC data /// \tparam FillMl switch to fill ML histograms - template + template void runAnalysisPerCollisionMc(CollType const& collisions, CandType const& candidates, - CandLcMcGen const& mcParticles) + McParticles3ProngMatched const& mcParticles) { for (const auto& collision : collisions) { // MC Rec. @@ -916,32 +777,6 @@ struct HfTaskLc { } PROCESS_SWITCH(HfTaskLc, processDataWithMlWithFT0M, "Process real data with the ML method and with FT0M centrality", false); - void processDataWithMlWithUpc(soa::Join const& collisions, - aod::BcFullInfos const& bcs, - LcCandidatesMl const& selectedLcCandidatesMl, - aod::Tracks const&, - aod::FT0s const& ft0s, - aod::FV0As const& fv0as, - aod::FDDs const& fdds, - aod::Zdcs const& /*zdcs*/) - { - runAnalysisPerCollisionDataWithUpc(collisions, selectedLcCandidatesMl, bcs, ft0s, fv0as, fdds); - } - PROCESS_SWITCH(HfTaskLc, processDataWithMlWithUpc, "Process real data with the ML method with UPC", false); - - void processDataStdWithUpc(soa::Join const& collisions, - aod::BcFullInfos const& bcs, - LcCandidatesMl const& selectedLcCandidatesMl, - aod::Tracks const&, - aod::FT0s const& ft0s, - aod::FV0As const& fv0as, - aod::FDDs const& fdds, - aod::Zdcs const& /*zdcs*/) - { - runAnalysisPerCollisionDataWithUpc(collisions, selectedLcCandidatesMl, bcs, ft0s, fv0as, fdds); - } - PROCESS_SWITCH(HfTaskLc, processDataStdWithUpc, "Process real data with the standard method with UPC", false); - void processMcStd(CollisionsMc const& collisions, LcCandidatesMc const& selectedLcCandidatesMc, McParticles3ProngMatched const& mcParticles, diff --git a/PWGHF/D2H/Tasks/taskLcToK0sP.cxx b/PWGHF/D2H/Tasks/taskLcToK0sP.cxx index 78fd2c5174a..349d5c80009 100644 --- a/PWGHF/D2H/Tasks/taskLcToK0sP.cxx +++ b/PWGHF/D2H/Tasks/taskLcToK0sP.cxx @@ -14,9 +14,11 @@ /// /// \author Chiara Zampolli, , CERN /// Paul Buehler, , Vienna +/// Xufei Xue, , CUG /// /// \note based on taskD0.cxx, taskLc.cxx +#include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/AliasTables.h" @@ -24,6 +26,9 @@ #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include #include @@ -35,31 +40,98 @@ #include #include +#include + +#include #include +#include +#include +#include #include using namespace o2; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::hf_centrality; /// LcToK0sp analysis task struct HfTaskLcToK0sP { + Configurable fillTHn{"fillTHn", true, "fill THnSparse"}; Configurable selectionFlagLcToK0sP{"selectionFlagLcToK0sP", 1, "Selection Flag for Lc"}; - Configurable selectionFlagLcbarToK0sP{"selectionFlagLcbarToK0sP", 1, "Selection Flag for Lcbar"}; Configurable etaCandMax{"etaCandMax", -1., "max. cand. pseudorapidity"}; Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen particle rapidity"}; Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; Configurable> binsPt{"binsPt", std::vector{hf_cuts_lc_to_k0s_p::vecBinsPt}, "pT bin limits"}; + SliceCache cache; + + using FilteredCandLcToPK0SWSelFlag = soa::Filtered>; + using FilteredCandLcToPK0SWSelFlagAndMl = soa::Filtered>; + using FilteredCandLcToPK0SWSelFlagAndMc = soa::Filtered>; + using FilteredCandLcToPK0SWSelFlagAndMcAndMl = soa::Filtered>; + using McParticlesCascadeMatched = soa::Join; + using TracksWPid = soa::Join; - Filter filterSelectCandidates = (aod::hf_sel_candidate_lc_to_k0s_p::isSelLcToK0sP >= selectionFlagLcToK0sP || aod::hf_sel_candidate_lc_to_k0s_p::isSelLcToK0sP >= selectionFlagLcbarToK0sP); + using Collisions = soa::Join; + using CollisionsMc = soa::Join; + using CollisionsWithFT0C = soa::Join; + using CollisionsMcWithFT0C = soa::Join; + using CollisionsWithFT0M = soa::Join; + using CollisionsMcWithFT0M = soa::Join; + + Filter filterSelectCandidates = aod::hf_sel_candidate_lc_to_k0s_p::isSelLcToK0sP >= selectionFlagLcToK0sP; + + Preslice lcToPK0SPerCollision = aod::hf_cand::collisionId; + PresliceUnsorted colPerMcCollision = aod::mcparticle::mcCollisionId; + + // THnSparse configurable axes + ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {300, 2.0, 2.6}, ""}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {360, 0., 36.}, ""}; + ConfigurableAxis thnConfigAxisBdtScoreBkg{"thnConfigAxisBdtScoreBkg", {100, 0., 1.}, ""}; + ConfigurableAxis thnConfigAxisBdtScorePrompt{"thnConfigAxisBdtScorePrompt", {100, 0., 1.}, ""}; + ConfigurableAxis thnConfigAxisBdtScoreNonPrompt{"thnConfigAxisBdtScoreNonPrompt", {100, 0., 1.}, ""}; + ConfigurableAxis thnConfigAxisPtB{"thnConfigAxisPtB", {3000, 0., 300.}, ""}; + ConfigurableAxis thnConfigAxisOrigin{"thnConfigAxisOrigin", {3, -0.5, 2.5}, ""}; + ConfigurableAxis thnConfigAxisCentrality{"thnConfigAxisCentrality", {100, 0., 100.}, ""}; + ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Number of PV contributors"}; + ConfigurableAxis thnConfigAxisY{"thnConfigAxisY", {20, -1., 1.}, "candidate rapidity"}; HistogramRegistry registry{"registry"}; - void init(InitContext& context) + // ML class indices + enum MlClasses : int { + MlClassBackground = 0, + MlClassPrompt, + MlClassNonPrompt, + NumberOfMlClasses + }; + + static constexpr int DecayChannelLcToK0sP = 1; + + // Names of folders and suffixes for MC signal histograms + constexpr static std::string_view SignalFolders[] = {"signal", "prompt", "nonprompt"}; + constexpr static std::string_view SignalSuffixes[] = {"", "Prompt", "NonPrompt"}; + + enum SignalClass : int { + Signal = 0, + Prompt, + NonPrompt + }; + + void init(InitContext&) { + // Check that only one process function is enabled + std::array processFlags{ + doprocessDataStd, doprocessDataStdWithFT0C, doprocessDataStdWithFT0M, + doprocessDataWithMl, doprocessDataWithMlWithFT0C, doprocessDataWithMlWithFT0M, + doprocessMcStd, doprocessMcStdWithFT0C, doprocessMcStdWithFT0M, + doprocessMcWithMl, doprocessMcWithMlWithFT0C, doprocessMcWithMlWithFT0M}; + if ((std::accumulate(processFlags.begin(), processFlags.end(), 0)) != 1) { + LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); + } + // axes AxisSpec const axisBinsPt = {binsPt, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec const axisPt = {300, 0.0f, 30.0f, "#it{p}_{T} (GeV/#it{c})"}; @@ -74,503 +146,717 @@ struct HfTaskLcToK0sP { AxisSpec const axisMassK0Short = {500, 0.4f, 0.6f, "#it{m}(K_{S}^{0}) (GeV/#it{c}^{2})"}; AxisSpec const axisMassLambda = {500, 1.0f, 1.2f, "#it{m}(#Lambda) (GeV/#it{c}^{2})"}; AxisSpec const axisMassGamma = {500, 0.0f, 0.4f, "#it{m}(#gamma) (GeV/#it{c}^{2})"}; - AxisSpec const axisCPACand = {110, -1.1f, 1.1f, "candiate cos pointing angle"}; + AxisSpec const axisCPACand = {110, -1.1f, 1.1f, "candidate cos pointing angle"}; AxisSpec const axisDecLength = {200, 0.f, 2.0f, "decay length (cm)"}; AxisSpec const axisProperLifetime = {100, 0.f, 0.2f, "#it{c#tau} (cm)"}; AxisSpec const axisProperLifetimeV0 = {1000, 0.f, 80.f, "#it{c#tau} (cm)"}; AxisSpec const axisNSigma = {100, -6.f, 6.f, "n#it{#sigma}_{p}"}; AxisSpec const axisPidP = {100, 0.f, 10.0f, "#it{p} (GeV/#it{c})"}; - // data - registry.add("hPtCand", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("hEtaCand", "cascade candidates;candidate #it{#eta};entries", {HistType::kTH1F, {axisEta}}); - registry.add("hEtaCandVsPtCand", "cascade candidates;candidate #it{#eta};p_{T}", {HistType::kTH2F, {axisEta, axisBinsPt}}); - registry.add("hPhiCand", "cascade candidates;candidate #it{#phi};entries", {HistType::kTH1F, {axisPhi}}); - registry.add("hPhiCandVsPtCand", "cascade candidates;candidate #it{#phi};p_{T}", {HistType::kTH2F, {axisPhi, axisBinsPt}}); - registry.add("hMass", "cascade candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassCand}}); - registry.add("hMassVsPtCand", "cascade candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassCand, axisBinsPt}}); - registry.add("hPtBach", "cascade candidates;bachelor #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("hPtBachVsPtCand", "cascade candidates;bachelor #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("hPtV0", "cascade candidates;v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("hPtV0VsPtCand", "cascade candidates;v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("hd0Bach", "cascade candidates;bachelor DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0}}); - registry.add("hd0BachVsPtCand", "cascade candidates;bachelor DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0, axisBinsPt}}); - registry.add("hd0V0", "cascade candidates;V0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0}}); - registry.add("hd0V0VsPtCand", "cascade candidates;V0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0, axisBinsPt}}); - registry.add("hd0V0pos", "cascade candidates;pos daugh v0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0V0Daughters}}); - registry.add("hd0V0posVsPtCand", "cascade candidates;pos daugh v0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0V0Daughters, axisBinsPt}}); - registry.add("hd0V0neg", "cascade candidates;neg daugh v0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0V0Daughters}}); - registry.add("hd0V0negVsPtCand", "cascade candidates;neg daugh v0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0V0Daughters, axisBinsPt}}); - registry.add("hPtV0pos", "cascade candidates;pos daugh v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("hPtV0posVsPtCand", "cascade candidates;pos daugh v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("hPtV0neg", "cascade candidates;neg daugh v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("hPtV0negVsPtCand", "cascade candidates;neg daugh v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("hV0CPA", "cascade candidates;v0 cosine of pointing angle;entries", {HistType::kTH1F, {axisV0CPA}}); - registry.add("hV0CPAVsPtCand", "cascade candidates;v0 cosine of pointing angle;p_{T}", {HistType::kTH2F, {axisV0CPA, axisBinsPt}}); - registry.add("hV0Radius", "cascade candidates;v0 radius (cm);entries", {HistType::kTH1F, {axisV0Radius}}); - registry.add("hV0RadiusVsPtCand", "cascade candidates;v0 radius (cm);p_{T}", {HistType::kTH2F, {axisV0Radius, axisBinsPt}}); - registry.add("hV0DCADaughters", "cascade candidates;v0 dca daughters (cm);entries", {HistType::kTH1F, {axisV0DCADaughters}}); - registry.add("hV0DCADaughtersVsPtCand", "cascade candidates;v0 dca daughters (cm);p_{T}", {HistType::kTH2F, {axisV0DCADaughters, axisBinsPt}}); - registry.add("hV0MK0Short", "cascade candidates;v0 mass K0s (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassK0Short}}); - registry.add("hV0MK0ShortVsPtCand", "cascade candidates;v0 mass K0s (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassK0Short, axisBinsPt}}); - registry.add("hV0MLambda", "cascade candidates;v0 mass Lambda (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassLambda}}); - registry.add("hV0MLambdaVsPtCand", "cascade candidates;v0 mass Lambda (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassLambda, axisBinsPt}}); - registry.add("hV0MAntiLambda", "cascade candidates;v0 mass AntiLambda (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassLambda}}); - registry.add("hV0MAntiLambdaVsPtCand", "cascade candidates;v0 mass AntiLambda (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassLambda, axisBinsPt}}); - registry.add("hV0MGamma", "cascade candidates;v0 mass Gamma (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassGamma}}); - registry.add("hV0MGammaVsPtCand", "cascade candidates;v0 mass Gamma (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassGamma, axisBinsPt}}); - registry.add("hCtV0K0Short", "cascade candidates;proper lifetime (V0) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetimeV0}}); - registry.add("hCtV0K0ShortVsPtCand", "cascade candidates;proper lifetime (V0) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetimeV0, axisBinsPt}}); - registry.add("hCtV0Lambda", "cascade candidates;proper lifetime (V0) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetimeV0}}); - registry.add("hCtV0LambdaVsPtCand", "cascade candidates;proper lifetime (V0) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetimeV0, axisBinsPt}}); - registry.add("hCPACand", "cascade candidates;cosine pointing angle;entries", {HistType::kTH1F, {axisCPACand}}); - registry.add("hCPACandVsPtCand", "cascade candidates;cosine pointing angle;p_{T}", {HistType::kTH2F, {axisCPACand, axisBinsPt}}); - registry.add("hCPAxyCand", "cascade candidates;cosine pointing angle xy;entries", {HistType::kTH1F, {axisCPACand}}); - registry.add("hCPAxyCandVsPtCand", "cascade candidates;cosine pointing angle xy;p_{T}", {HistType::kTH2F, {axisCPACand, axisBinsPt}}); - registry.add("hDecLengthCand", "cascade candidates;decay length (cm);entries", {HistType::kTH1F, {axisDecLength}}); - registry.add("hDecLengthCandVsPtCand", "cascade candidates;decay length (cm);p_{T}", {HistType::kTH2F, {axisDecLength, axisBinsPt}}); - registry.add("hDecLengthXYCand", "cascade candidates;decay length xy (cm);entries", {HistType::kTH1F, {axisDecLength}}); - registry.add("hDecLengthXYCandVsPtCand", "cascade candidates;decay length xy (cm);p_{T}", {HistType::kTH2F, {axisDecLength, axisBinsPt}}); - registry.add("hCtCand", "cascade candidates;proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetime}}); - registry.add("hCtCandVsPtCand", "cascade candidates;proper lifetime (#Lambda_{c}) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetime, axisBinsPt}}); - registry.add("hTPCNSigmaPrBach", "cascade candidates;n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); - registry.add("hPBachVsTPCNSigmaPrBach", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); - registry.add("hTOFNSigmaPrBach", "cascade candidates;n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); - registry.add("hPBachVsTOFNSigmaPrBach", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); + // data - helper lambda: adds 1D histogram and its VsPtCand 2D variant + auto addHistosData = [&](const std::string& name, const std::string& title, const AxisSpec& axis) { + registry.add(("Data/h" + name).c_str(), ("cascade candidates;" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("Data/h" + name + "VsPtCand").c_str(), ("cascade candidates;" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + }; + + addHistosData("PtCand", "candidate #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosData("EtaCand", "candidate #it{#eta}", axisEta); + addHistosData("PhiCand", "candidate #it{#phi}", axisPhi); + addHistosData("Mass", "inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2})", axisMassCand); + addHistosData("PtBach", "bachelor #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosData("PtV0", "v0 #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosData("d0Bach", "bachelor DCAxy to prim. vertex (cm)", axisd0); + addHistosData("d0V0", "V0 DCAxy to prim. vertex (cm)", axisd0); + addHistosData("d0V0pos", "pos daugh v0 DCAxy to prim. vertex (cm)", axisd0V0Daughters); + addHistosData("d0V0neg", "neg daugh v0 DCAxy to prim. vertex (cm)", axisd0V0Daughters); + addHistosData("PtV0pos", "pos daugh v0 #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosData("PtV0neg", "neg daugh v0 #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosData("V0CPA", "v0 cosine of pointing angle", axisV0CPA); + addHistosData("V0Radius", "V0 radius (cm)", axisV0Radius); + addHistosData("V0DCADaughters", "v0 dca daughters (cm)", axisV0DCADaughters); + addHistosData("V0MK0Short", "v0 mass K0s (GeV/#it{c}^{2})", axisMassK0Short); + addHistosData("V0MLambda", "v0 mass #Lambda (GeV/#it{c}^{2})", axisMassLambda); + addHistosData("V0MAntiLambda", "v0 mass Anti#Lambda (GeV/#it{c}^{2})", axisMassLambda); + addHistosData("V0MGamma", "v0 mass #gamma (GeV/#it{c}^{2})", axisMassGamma); + addHistosData("CtV0K0Short", "proper lifetime (V0) * #it{c} (cm)", axisProperLifetimeV0); + addHistosData("CtV0Lambda", "proper lifetime (V0) * #it{c} (cm)", axisProperLifetimeV0); + addHistosData("CPACand", "cosine pointing angle", axisCPACand); + addHistosData("CPAxyCand", "cosine pointing angle xy", axisCPACand); + addHistosData("DecLengthCand", "decay length (cm)", axisDecLength); + addHistosData("DecLengthXYCand", "decay length xy (cm)", axisDecLength); + addHistosData("CtCand", "proper lifetime (#Lambda_{c}) * #it{c} (cm)", axisProperLifetime); + + // PID histograms (non-standard axis pattern) + registry.add("Data/hTPCNSigmaPrBach", "cascade candidates;n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("Data/hPBachVsTPCNSigmaPrBach", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("Data/hTOFNSigmaPrBach", "cascade candidates;n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("Data/hPBachVsTOFNSigmaPrBach", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); // add MC histograms - if (context.mOptions.get("processMc")) { - registry.add("MC/Rec/hPtCandRecSig", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtCandRecSigPrompt", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtCandRecSigNonPrompt", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtCandRecBg", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hEtaCandRecSig", "cascade candidates;candidate #it{#eta};entries", {HistType::kTH1F, {axisEta}}); - registry.add("MC/Rec/hEtaCandVsPtCandRecSig", "cascade candidates;candidate #it{#eta};p_{T}", {HistType::kTH2F, {axisEta, axisBinsPt}}); - registry.add("MC/Rec/hEtaCandRecBg", "cascade candidates;candidate #it{#eta};entries", {HistType::kTH1F, {axisEta}}); - registry.add("MC/Rec/hEtaCandVsPtCandRecBg", "cascade candidates;candidate #it{#eta};p_{T}", {HistType::kTH2F, {axisEta, axisBinsPt}}); - registry.add("MC/Rec/hPhiCandRecSig", "cascade candidates;candidate #it{#phi};entries", {HistType::kTH1F, {axisPhi}}); - registry.add("MC/Rec/hPhiCandVsPtCandRecSig", "cascade candidates;candidate #it{#phi};p_{T}", {HistType::kTH2F, {axisPhi, axisBinsPt}}); - registry.add("MC/Rec/hPhiCandRecBg", "cascade candidates;candidate #it{#phi};entries", {HistType::kTH1F, {axisPhi}}); - registry.add("MC/Rec/hPhiCandVsPtCandRecBg", "cascade candidates;candidate #it{#phi};p_{T}", {HistType::kTH2F, {axisPhi, axisBinsPt}}); - registry.add("MC/Gen/hPtCandGen", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Gen/hPtCandGenPrompt", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Gen/hPtCandGenNonPrompt", "cascade candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Gen/hEtaCandGen", "cascade candidates;candidate #it{#eta};entries", {HistType::kTH1F, {axisEta}}); - registry.add("MC/Gen/hEtaCandVsPtCandGen", "cascade candidates;candidate #it{#eta};p_{T}", {HistType::kTH2F, {axisEta, axisBinsPt}}); - registry.add("MC/Gen/hPhiCandGen", "cascade candidates;candidate #it{#phi};entries", {HistType::kTH1F, {axisPhi}}); - registry.add("MC/Gen/hPhiCandVsPtCandGen", "cascade candidates;candidate #it{#phi};p_{T}", {HistType::kTH2F, {axisPhi, axisBinsPt}}); - registry.add("MC/Rec/hMassRecSig", "cascade candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});p_{T}", {HistType::kTH1F, {axisMassCand}}); - registry.add("MC/Rec/hMassVsPtCandRecSig", "cascade candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassCand, axisBinsPt}}); - registry.add("MC/Rec/hMassRecBg", "cascade candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});p_{T}", {HistType::kTH1F, {axisMassCand}}); - registry.add("MC/Rec/hMassVsPtCandRecBg", "cascade candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassCand, axisBinsPt}}); - registry.add("MC/Rec/hPtBachRecSig", "cascade candidates;bachelor #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtBachVsPtCandRecSig", "cascade candidates;bachelor #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hPtBachRecBg", "cascade candidates;bachelor #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtBachVsPtCandRecBg", "cascade candidates;bachelor #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hPtV0RecSig", "cascade candidates;v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtV0VsPtCandRecSig", "cascade candidates;v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hPtV0RecBg", "cascade candidates;v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtV0VsPtCandRecBg", "cascade candidates;v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hd0BachRecSig", "cascade candidates;bachelor DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0}}); - registry.add("MC/Rec/hd0BachVsPtCandRecSig", "cascade candidates;bachelor DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0, axisBinsPt}}); - registry.add("MC/Rec/hd0BachRecBg", "cascade candidates;bachelor DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0}}); - registry.add("MC/Rec/hd0BachVsPtCandRecBg", "cascade candidates;bachelor DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0, axisBinsPt}}); - registry.add("MC/Rec/hd0V0RecSig", "cascade candidates;V0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0}}); - registry.add("MC/Rec/hd0V0VsPtCandRecSig", "cascade candidates;V0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0, axisBinsPt}}); - registry.add("MC/Rec/hd0V0RecBg", "cascade candidates;V0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0}}); - registry.add("MC/Rec/hd0V0VsPtCandRecBg", "cascade candidates;V0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0, axisBinsPt}}); - registry.add("MC/Rec/hd0V0posRecSig", "cascade candidates;pos daugh v0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0V0Daughters}}); - registry.add("MC/Rec/hd0V0posVsPtCandRecSig", "cascade candidates;pos daugh v0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0V0Daughters, axisBinsPt}}); - registry.add("MC/Rec/hd0V0posRecBg", "cascade candidates;pos daugh v0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0V0Daughters}}); - registry.add("MC/Rec/hd0V0posVsPtCandRecBg", "cascade candidates;pos daugh v0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0V0Daughters, axisBinsPt}}); - registry.add("MC/Rec/hd0V0negRecSig", "cascade candidates;neg daugh v0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0V0Daughters}}); - registry.add("MC/Rec/hd0V0negVsPtCandRecSig", "cascade candidates;neg daugh v0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0V0Daughters, axisBinsPt}}); - registry.add("MC/Rec/hd0V0negRecBg", "cascade candidates;neg daugh v0 DCAxy to prim. vertex (cm);entries", {HistType::kTH1F, {axisd0V0Daughters}}); - registry.add("MC/Rec/hd0V0negVsPtCandRecBg", "cascade candidates;neg daugh v0 DCAxy to prim. vertex (cm);p_{T}", {HistType::kTH2F, {axisd0V0Daughters, axisBinsPt}}); - registry.add("MC/Rec/hPtV0posRecSig", "cascade candidates;pos daugh v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtV0posVsPtCandRecSig", "cascade candidates;pos daugh v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hPtV0posRecBg", "cascade candidates;pos daugh v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtV0posVsPtCandRecBg", "cascade candidates;pos daugh v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hPtV0negRecSig", "cascade candidates;neg daugh v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtV0negVsPtCandRecSig", "cascade candidates;neg daugh v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hPtV0negRecBg", "cascade candidates;neg daugh v0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); - registry.add("MC/Rec/hPtV0negVsPtCandRecBg", "cascade candidates;neg daugh v0 #it{p}_{T} (GeV/#it{c});p_{T}", {HistType::kTH2F, {axisPt, axisBinsPt}}); - registry.add("MC/Rec/hV0CPARecSig", "cascade candidates;v0 cosine of pointing angle;entries", {HistType::kTH1F, {axisV0CPA}}); - registry.add("MC/Rec/hV0CPAVsPtCandRecSig", "cascade candidates;v0 cosine of pointing angle;p_{T}", {HistType::kTH2F, {axisV0CPA, axisBinsPt}}); - registry.add("MC/Rec/hV0CPARecBg", "cascade candidates;v0 cosine of pointing angle;entries", {HistType::kTH1F, {axisV0CPA}}); - registry.add("MC/Rec/hV0CPAVsPtCandRecBg", "cascade candidates;v0 cosine of pointing angle;p_{T}", {HistType::kTH2F, {axisV0CPA, axisBinsPt}}); - registry.add("MC/Rec/hV0RadiusRecSig", "cascade candidates;v0 radius (cm);entries", {HistType::kTH1F, {axisV0Radius}}); - registry.add("MC/Rec/hV0RadiusVsPtCandRecSig", "cascade candidates;v0 radius (cm);p_{T}", {HistType::kTH2F, {axisV0Radius, axisBinsPt}}); - registry.add("MC/Rec/hV0RadiusRecBg", "cascade candidates;v0 radius (cm);entries", {HistType::kTH1F, {axisV0Radius}}); - registry.add("MC/Rec/hV0RadiusVsPtCandRecBg", "cascade candidates;v0 radius (cm);p_{T}", {HistType::kTH2F, {axisV0Radius, axisBinsPt}}); - registry.add("MC/Rec/hV0DCADaughtersRecSig", "cascade candidates;v0 dca daughters (cm);entries", {HistType::kTH1F, {axisV0DCADaughters}}); - registry.add("MC/Rec/hV0DCADaughtersVsPtCandRecSig", "cascade candidates;v0 dca daughters (cm);p_{T}", {HistType::kTH2F, {axisV0DCADaughters, axisBinsPt}}); - registry.add("MC/Rec/hV0DCADaughtersRecBg", "cascade candidates;v0 dca daughters (cm);entries", {HistType::kTH1F, {axisV0DCADaughters}}); - registry.add("MC/Rec/hV0DCADaughtersVsPtCandRecBg", "cascade candidates;v0 dca daughters (cm);p_{T}", {HistType::kTH2F, {axisV0DCADaughters, axisBinsPt}}); - registry.add("MC/Rec/hV0MK0ShortRecSig", "cascade candidates;v0 mass K0s (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassK0Short}}); - registry.add("MC/Rec/hV0MK0ShortVsPtCandRecSig", "cascade candidates;v0 mass K0s (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassK0Short, axisBinsPt}}); - registry.add("MC/Rec/hV0MK0ShortRecBg", "cascade candidates;v0 mass K0s (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassK0Short}}); - registry.add("MC/Rec/hV0MK0ShortVsPtCandRecBg", "cascade candidates;v0 mass K0s (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassK0Short, axisBinsPt}}); - registry.add("MC/Rec/hV0MLambdaRecSig", "cascade candidates;v0 mass Lambda (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassLambda}}); - registry.add("MC/Rec/hV0MLambdaVsPtCandRecSig", "cascade candidates;v0 mass Lambda (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassLambda, axisBinsPt}}); - registry.add("MC/Rec/hV0MLambdaRecBg", "cascade candidates;v0 mass Lambda (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassLambda}}); - registry.add("MC/Rec/hV0MLambdaVsPtCandRecBg", "cascade candidates;v0 mass Lambda (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassLambda, axisBinsPt}}); - registry.add("MC/Rec/hV0MAntiLambdaRecSig", "cascade candidates;v0 mass AntiLambda (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassLambda}}); - registry.add("MC/Rec/hV0MAntiLambdaVsPtCandRecSig", "cascade candidates;v0 mass AntiLambda (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassLambda, axisBinsPt}}); - registry.add("MC/Rec/hV0MAntiLambdaRecBg", "cascade candidates;v0 mass AntiLambda (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassLambda}}); - registry.add("MC/Rec/hV0MAntiLambdaVsPtCandRecBg", "cascade candidates;v0 mass AntiLambda (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassLambda, axisBinsPt}}); - registry.add("MC/Rec/hV0MGammaRecSig", "cascade candidates;v0 mass Gamma (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassGamma}}); - registry.add("MC/Rec/hV0MGammaVsPtCandRecSig", "cascade candidates;v0 mass Gamma (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassGamma, axisBinsPt}}); - registry.add("MC/Rec/hV0MGammaRecBg", "cascade candidates;v0 mass Gamma (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMassGamma}}); - registry.add("MC/Rec/hV0MGammaVsPtCandRecBg", "cascade candidates;v0 mass Gamma (GeV/#it{c}^{2});p_{T}", {HistType::kTH2F, {axisMassGamma, axisBinsPt}}); - registry.add("MC/Rec/hCtV0K0ShortRecSig", "cascade candidates;proper lifetime (V0) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetimeV0}}); - registry.add("MC/Rec/hCtV0K0ShortVsPtCandRecSig", "cascade candidates;proper lifetime (V0) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetimeV0, axisBinsPt}}); - registry.add("MC/Rec/hCtV0K0ShortRecBg", "cascade candidates;proper lifetime (V0) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetimeV0}}); - registry.add("MC/Rec/hCtV0K0ShortVsPtCandRecBg", "cascade candidates;proper lifetime (V0) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetimeV0, axisBinsPt}}); - registry.add("MC/Rec/hCtV0LambdaRecSig", "cascade candidates;proper lifetime (V0) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetimeV0}}); - registry.add("MC/Rec/hCtV0LambdaVsPtCandRecSig", "cascade candidates;proper lifetime (V0) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetimeV0, axisBinsPt}}); - registry.add("MC/Rec/hCtV0LambdaRecBg", "cascade candidates;proper lifetime (V0) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetimeV0}}); - registry.add("MC/Rec/hCtV0LambdaVsPtCandRecBg", "cascade candidates;proper lifetime (V0) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetimeV0, axisBinsPt}}); - registry.add("MC/Rec/hCPACandRecSig", "cascade candidates;cosine pointing angle;entries", {HistType::kTH1F, {axisCPACand}}); - registry.add("MC/Rec/hCPACandVsPtCandRecSig", "cascade candidates;cosine pointing angle;p_{T}", {HistType::kTH2F, {axisCPACand, axisBinsPt}}); - registry.add("MC/Rec/hCPACandRecBg", "cascade candidates;cosine pointing angle;entries", {HistType::kTH1F, {axisCPACand}}); - registry.add("MC/Rec/hCPACandVsPtCandRecBg", "cascade candidates;cosine pointing angle;p_{T}", {HistType::kTH2F, {axisCPACand, axisBinsPt}}); - registry.add("MC/Rec/hCPAxyCandRecSig", "cascade candidates;cosine pointing angle xy;entries", {HistType::kTH1F, {axisCPACand}}); - registry.add("MC/Rec/hCPAxyCandVsPtCandRecSig", "cascade candidates;cosine pointing angle xy;p_{T}", {HistType::kTH2F, {axisCPACand, axisBinsPt}}); - registry.add("MC/Rec/hCPAxyCandRecBg", "cascade candidates;cosine pointing angle xy;entries", {HistType::kTH1F, {axisCPACand}}); - registry.add("MC/Rec/hCPAxyCandVsPtCandRecBg", "cascade candidates;cosine pointing angle xy;p_{T}", {HistType::kTH2F, {axisCPACand, axisBinsPt}}); - registry.add("MC/Rec/hDecLengthCandRecSig", "cascade candidates;decay length (cm);entries", {HistType::kTH1F, {axisDecLength}}); - registry.add("MC/Rec/hDecLengthCandVsPtCandRecSig", "cascade candidates;decay length (cm);p_{T}", {HistType::kTH2F, {axisDecLength, axisBinsPt}}); - registry.add("MC/Rec/hDecLengthCandRecBg", "cascade candidates;decay length (cm);entries", {HistType::kTH1F, {axisDecLength}}); - registry.add("MC/Rec/hDecLengthCandVsPtCandRecBg", "cascade candidates;decay length (cm);p_{T}", {HistType::kTH2F, {axisDecLength, axisBinsPt}}); - registry.add("MC/Rec/hDecLengthXYCandRecSig", "cascade candidates;decay length xy (cm);entries", {HistType::kTH1F, {axisDecLength}}); - registry.add("MC/Rec/hDecLengthXYCandVsPtCandRecSig", "cascade candidates;decay length xy (cm);p_{T}", {HistType::kTH2F, {axisDecLength, axisBinsPt}}); - registry.add("MC/Rec/hDecLengthXYCandRecBg", "cascade candidates;decay length xy (cm);entries", {HistType::kTH1F, {axisDecLength}}); - registry.add("MC/Rec/hDecLengthXYCandVsPtCandRecBg", "cascade candidates;decay length xy (cm);p_{T}", {HistType::kTH2F, {axisDecLength, axisBinsPt}}); - registry.add("MC/Rec/hCtCandRecSig", "cascade candidates;proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetime}}); - registry.add("MC/Rec/hCtCandVsPtCandRecSig", "cascade candidates;proper lifetime (#Lambda_{c}) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetime, axisBinsPt}}); - registry.add("MC/Rec/hCtCandRecBg", "cascade candidates;proper lifetime (#Lambda_{c}) * #it{c} (cm);entries", {HistType::kTH1F, {axisProperLifetime}}); - registry.add("MC/Rec/hCtCandVsPtCandRecBg", "cascade candidates;proper lifetime (#Lambda_{c}) * #it{c} (cm);p_{T}", {HistType::kTH2F, {axisProperLifetime, axisBinsPt}}); - registry.add("MC/Rec/hTPCNSigmaPrBachRecSig", "cascade candidates;n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); - registry.add("MC/Rec/hPBachVsTPCNSigmaPrBachRecSig", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); - registry.add("MC/Rec/hTPCNSigmaPrBachRecBg", "cascade candidates;n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); - registry.add("MC/Rec/hPBachVsTPCNSigmaPrBachRecBg", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); - registry.add("MC/Rec/hTOFNSigmaPrBachRecSig", "cascade candidates;n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); - registry.add("MC/Rec/hPBachVsTOFNSigmaPrBachRecSig", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); - registry.add("MC/Rec/hTOFNSigmaPrBachRecBg", "cascade candidates;n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); - registry.add("MC/Rec/hPBachVsTOFNSigmaPrBachRecBg", "cascade candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); + bool const isMc = doprocessMcStd || doprocessMcStdWithFT0C || doprocessMcStdWithFT0M || + doprocessMcWithMl || doprocessMcWithMlWithFT0C || doprocessMcWithMlWithFT0M; + if (isMc) { + // MC Rec helper: signal/prompt/nonprompt + background + auto addHistosMcRec = [&](const std::string& name, const std::string& title, const AxisSpec& axis) { + registry.add(("MC/Rec/signal/h" + name + "RecSig").c_str(), ("cascade candidates (matched);" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("MC/Rec/signal/h" + name + "VsPtCandRecSig").c_str(), ("cascade candidates (matched);" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + registry.add(("MC/Rec/prompt/h" + name + "RecSigPrompt").c_str(), ("cascade candidates (matched, prompt);" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("MC/Rec/prompt/h" + name + "VsPtCandRecSigPrompt").c_str(), ("cascade candidates (matched, prompt);" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + registry.add(("MC/Rec/nonprompt/h" + name + "RecSigNonPrompt").c_str(), ("cascade candidates (matched, non-prompt);" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("MC/Rec/nonprompt/h" + name + "VsPtCandRecSigNonPrompt").c_str(), ("cascade candidates (matched, non-prompt);" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + registry.add(("MC/Rec/background/h" + name + "RecBg").c_str(), ("cascade candidates (unmatched);" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("MC/Rec/background/h" + name + "VsPtCandRecBg").c_str(), ("cascade candidates (unmatched);" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + }; + + // MC Gen helper: signal/prompt/nonprompt + auto addHistosMcGen = [&](const std::string& name, const std::string& title, const AxisSpec& axis) { + registry.add(("MC/Gen/signal/h" + name + "Gen").c_str(), ("MC particles (matched);" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("MC/Gen/signal/h" + name + "VsPtCandGen").c_str(), ("MC particles (matched);" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + registry.add(("MC/Gen/prompt/h" + name + "GenPrompt").c_str(), ("MC particles (matched, prompt);" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("MC/Gen/prompt/h" + name + "VsPtCandGenPrompt").c_str(), ("MC particles (matched, prompt);" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + registry.add(("MC/Gen/nonprompt/h" + name + "GenNonPrompt").c_str(), ("MC particles (matched, non-prompt);" + title + ";entries").c_str(), {HistType::kTH1F, {axis}}); + registry.add(("MC/Gen/nonprompt/h" + name + "VsPtCandGenNonPrompt").c_str(), ("MC particles (matched, non-prompt);" + title + ";p_{T}").c_str(), {HistType::kTH2F, {axis, axisBinsPt}}); + }; + + // MC Rec: PtCand has extra signal/prompt/nonprompt + background variants + registry.add("MC/Rec/signal/hPtCandRecSig", "cascade candidates (matched);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("MC/Rec/prompt/hPtCandRecSigPrompt", "cascade candidates (matched, prompt);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("MC/Rec/nonprompt/hPtCandRecSigNonPrompt", "cascade candidates (matched, non-prompt);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("MC/Rec/background/hPtCandRecBg", "cascade candidates (unmatched);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + + addHistosMcRec("EtaCand", "candidate #it{#eta}", axisEta); + addHistosMcRec("PhiCand", "candidate #it{#phi}", axisPhi); + addHistosMcRec("Mass", "inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2})", axisMassCand); + addHistosMcRec("PtBach", "bachelor #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosMcRec("PtV0", "v0 #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosMcRec("d0Bach", "bachelor DCAxy to prim. vertex (cm)", axisd0); + addHistosMcRec("d0V0", "V0 DCAxy to prim. vertex (cm)", axisd0); + addHistosMcRec("d0V0pos", "pos daugh v0 DCAxy to prim. vertex (cm)", axisd0V0Daughters); + addHistosMcRec("d0V0neg", "neg daugh v0 DCAxy to prim. vertex (cm)", axisd0V0Daughters); + addHistosMcRec("PtV0pos", "pos daugh v0 #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosMcRec("PtV0neg", "neg daugh v0 #it{p}_{T} (GeV/#it{c})", axisPt); + addHistosMcRec("V0CPA", "v0 cosine of pointing angle", axisV0CPA); + addHistosMcRec("V0Radius", "V0 radius (cm)", axisV0Radius); + addHistosMcRec("V0DCADaughters", "v0 dca daughters (cm)", axisV0DCADaughters); + addHistosMcRec("V0MK0Short", "v0 mass K0s (GeV/#it{c}^{2})", axisMassK0Short); + addHistosMcRec("V0MLambda", "v0 mass #Lambda (GeV/#it{c}^{2})", axisMassLambda); + addHistosMcRec("V0MAntiLambda", "v0 mass Anti#Lambda (GeV/#it{c}^{2})", axisMassLambda); + addHistosMcRec("V0MGamma", "v0 mass #gamma (GeV/#it{c}^{2})", axisMassGamma); + addHistosMcRec("CtV0K0Short", "proper lifetime (V0) * #it{c} (cm)", axisProperLifetimeV0); + addHistosMcRec("CtV0Lambda", "proper lifetime (V0) * #it{c} (cm)", axisProperLifetimeV0); + addHistosMcRec("CPACand", "cosine pointing angle", axisCPACand); + addHistosMcRec("CPAxyCand", "cosine pointing angle xy", axisCPACand); + addHistosMcRec("DecLengthCand", "decay length (cm)", axisDecLength); + addHistosMcRec("DecLengthXYCand", "decay length xy (cm)", axisDecLength); + addHistosMcRec("CtCand", "proper lifetime (#Lambda_{c}) * #it{c} (cm)", axisProperLifetime); + + // MC Rec PID + registry.add("MC/Rec/signal/hTPCNSigmaPrBachRecSig", "cascade candidates (matched);n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/signal/hPBachVsTPCNSigmaPrBachRecSig", "cascade candidates (matched);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("MC/Rec/signal/hTOFNSigmaPrBachRecSig", "cascade candidates (matched);n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/signal/hPBachVsTOFNSigmaPrBachRecSig", "cascade candidates (matched);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("MC/Rec/prompt/hTPCNSigmaPrBachRecSigPrompt", "cascade candidates (matched, prompt);n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/prompt/hPBachVsTPCNSigmaPrBachRecSigPrompt", "cascade candidates (matched, prompt);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("MC/Rec/prompt/hTOFNSigmaPrBachRecSigPrompt", "cascade candidates (matched, prompt);n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/prompt/hPBachVsTOFNSigmaPrBachRecSigPrompt", "cascade candidates (matched, prompt);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("MC/Rec/nonprompt/hTPCNSigmaPrBachRecSigNonPrompt", "cascade candidates (matched, non-prompt);n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/nonprompt/hPBachVsTPCNSigmaPrBachRecSigNonPrompt", "cascade candidates (matched, non-prompt);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("MC/Rec/nonprompt/hTOFNSigmaPrBachRecSigNonPrompt", "cascade candidates (matched, non-prompt);n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/nonprompt/hPBachVsTOFNSigmaPrBachRecSigNonPrompt", "cascade candidates (matched, non-prompt);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("MC/Rec/background/hTPCNSigmaPrBachRecBg", "cascade candidates (unmatched);n#it{#sigma}_{p} TPC;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/background/hPBachVsTPCNSigmaPrBachRecBg", "cascade candidates (unmatched);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TPC", {HistType::kTH2F, {axisPidP, axisNSigma}}); + registry.add("MC/Rec/background/hTOFNSigmaPrBachRecBg", "cascade candidates (unmatched);n#it{#sigma}_{p} TOF;entries", {HistType::kTH1F, {axisNSigma}}); + registry.add("MC/Rec/background/hPBachVsTOFNSigmaPrBachRecBg", "cascade candidates (unmatched);#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{p} TOF", {HistType::kTH2F, {axisPidP, axisNSigma}}); + + // MC Gen: PtCand with signal/prompt/nonprompt variants + registry.add("MC/Gen/signal/hPtCandGen", "MC particles (matched);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("MC/Gen/prompt/hPtCandGenPrompt", "MC particles (matched, prompt);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("MC/Gen/nonprompt/hPtCandGenNonPrompt", "MC particles (matched, non-prompt);candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + + addHistosMcGen("EtaCand", "candidate #it{#eta}", axisEta); + addHistosMcGen("PhiCand", "candidate #it{#phi}", axisPhi); + } + + // THnSparse for ML analysis + if (fillTHn) { + const AxisSpec thnAxisMass{thnConfigAxisMass, "inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisBdtScoreBkg{thnConfigAxisBdtScoreBkg, "BDT bkg score"}; + const AxisSpec thnAxisBdtScorePrompt{thnConfigAxisBdtScorePrompt, "BDT prompt score"}; + const AxisSpec thnAxisBdtScoreNonPrompt{thnConfigAxisBdtScoreNonPrompt, "BDT non-prompt score"}; + bool const isFT0C = doprocessDataStdWithFT0C || doprocessDataWithMlWithFT0C || doprocessMcStdWithFT0C || doprocessMcWithMlWithFT0C; + bool const isFT0M = doprocessDataStdWithFT0M || doprocessDataWithMlWithFT0M || doprocessMcStdWithFT0M || doprocessMcWithMlWithFT0M; + std::string centLabel = "centrality"; + if (isFT0C) { + centLabel = "centrality (FT0C)"; + } else if (isFT0M) { + centLabel = "centrality (FT0M)"; + } + const AxisSpec thnAxisCentrality{thnConfigAxisCentrality, centLabel}; + const AxisSpec thnAxisNumPvContr{thnConfigAxisNumPvContr, "Number of PV contributors"}; + const AxisSpec thnAxisPtB{thnConfigAxisPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; + const AxisSpec thnAxisOrigin{thnConfigAxisOrigin, "origin"}; + const AxisSpec thnAxisY{thnConfigAxisY, "rapidity"}; + + // Data with ML: {mass, pt, centrality, bkg, prompt, non-prompt, numPvContr} + if (doprocessDataWithMl || doprocessDataWithMlWithFT0C || doprocessDataWithMlWithFT0M) { + registry.add("hnLcK0sPDataWithBdt", "THn for Lc->K0sP data with BDT", HistType::kTHnSparseF, + {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreBkg, thnAxisBdtScorePrompt, thnAxisBdtScoreNonPrompt, thnAxisNumPvContr}); + } + + // MC Rec with ML: {mass, pt, centrality, bkg, prompt, non-prompt, numPvContr, origin} + if (doprocessMcWithMl || doprocessMcWithMlWithFT0C || doprocessMcWithMlWithFT0M) { + registry.add("hnLcK0sPRecMcWithBdt", "THn for Lc->K0sP MC rec with BDT", HistType::kTHnSparseF, + {thnAxisMass, thnAxisPt, thnAxisCentrality, thnAxisBdtScoreBkg, thnAxisBdtScorePrompt, thnAxisBdtScoreNonPrompt, thnAxisNumPvContr, thnAxisOrigin}); + } + + // MC Gen: {pt, centrality, rapidity, numPvContr, ptB, origin} + if (isMc) { + registry.add("hnLcK0sPGenMc", "THn for Lc->K0sP MC gen", HistType::kTHnSparseF, + {thnAxisPt, thnAxisCentrality, thnAxisY, thnAxisNumPvContr, thnAxisPtB, thnAxisOrigin}); + } + } + } + + /// Evaluate centrality/multiplicity percentile + /// \param collision is collision + /// \return centrality/multiplicity percentile of the collision + template + float evaluateCentralityColl(const Coll& collision) + { + return o2::hf_centrality::getCentralityColl(collision); + } + + /// Bundled candidate variables with conversion constructor + struct HfCandVars { + double ptCand{}, eta{}, phi{}, invMassLcToK0sP{}; + double ptProng0{}, ptProng1{}; + double impactParameter0{}, impactParameter1{}; + double dcaPosToPV{}, dcaNegToPV{}; + double ptV0Pos{}, ptV0Neg{}; + double v0CosPA{}, v0Radius{}, dcaV0Daughters{}; + double mK0Short{}, mLambda{}, mAntiLambda{}, mGamma{}; + double ctV0K0Short{}, ctV0Lambda{}; + double cpa{}, cpaXY{}; + double decayLength{}, decayLengthXY{}, ctLc{}; + + template + explicit HfCandVars(CandType const& candidate) + : ptCand(candidate.pt()), + eta(candidate.eta()), + phi(candidate.phi()), + invMassLcToK0sP(HfHelper::invMassLcToK0sP(candidate)), + ptProng0(candidate.ptProng0()), + ptProng1(candidate.ptProng1()), + impactParameter0(candidate.impactParameter0()), + impactParameter1(candidate.impactParameter1()), + dcaPosToPV(candidate.dcapostopv()), + dcaNegToPV(candidate.dcanegtopv()), + ptV0Pos(candidate.ptV0Pos()), + ptV0Neg(candidate.ptV0Neg()), + v0CosPA(candidate.v0cosPA()), + v0Radius(candidate.v0radius()), + dcaV0Daughters(candidate.dcaV0daughters()), + mK0Short(candidate.mK0Short()), + mLambda(candidate.mLambda()), + mAntiLambda(candidate.mAntiLambda()), + mGamma(candidate.mGamma()), + ctV0K0Short(HfHelper::ctV0K0s(candidate)), + ctV0Lambda(HfHelper::ctV0Lambda(candidate)), + cpa(candidate.cpa()), + cpaXY(candidate.cpaXY()), + decayLength(candidate.decayLength()), + decayLengthXY(candidate.decayLengthXY()), + ctLc(HfHelper::ctLc(candidate)) + { + } + }; + + /// Helper function to fill candidate histograms + /// \param candidate is the candidate + template + void fillCandHistograms(CandType const& candidate) + { + HfCandVars cand(candidate); + + registry.fill(HIST("Data/hPtCand"), cand.ptCand); + registry.fill(HIST("Data/hEtaCand"), cand.eta); + registry.fill(HIST("Data/hEtaCandVsPtCand"), cand.eta, cand.ptCand); + registry.fill(HIST("Data/hPhiCand"), cand.phi); + registry.fill(HIST("Data/hPhiCandVsPtCand"), cand.phi, cand.ptCand); + registry.fill(HIST("Data/hMass"), cand.invMassLcToK0sP); + registry.fill(HIST("Data/hMassVsPtCand"), cand.invMassLcToK0sP, cand.ptCand); + registry.fill(HIST("Data/hPtBach"), cand.ptProng0); + registry.fill(HIST("Data/hPtBachVsPtCand"), cand.ptProng0, cand.ptCand); + registry.fill(HIST("Data/hPtV0"), cand.ptProng1); + registry.fill(HIST("Data/hPtV0VsPtCand"), cand.ptProng1, cand.ptCand); + registry.fill(HIST("Data/hd0Bach"), cand.impactParameter0); + registry.fill(HIST("Data/hd0BachVsPtCand"), cand.impactParameter0, cand.ptCand); + registry.fill(HIST("Data/hd0V0"), cand.impactParameter1); + registry.fill(HIST("Data/hd0V0VsPtCand"), cand.impactParameter1, cand.ptCand); + registry.fill(HIST("Data/hd0V0pos"), cand.dcaPosToPV); + registry.fill(HIST("Data/hd0V0posVsPtCand"), cand.dcaPosToPV, cand.ptCand); + registry.fill(HIST("Data/hd0V0neg"), cand.dcaNegToPV); + registry.fill(HIST("Data/hd0V0negVsPtCand"), cand.dcaNegToPV, cand.ptCand); + registry.fill(HIST("Data/hPtV0pos"), cand.ptV0Pos); + registry.fill(HIST("Data/hPtV0posVsPtCand"), cand.ptV0Pos, cand.ptCand); + registry.fill(HIST("Data/hPtV0neg"), cand.ptV0Neg); + registry.fill(HIST("Data/hPtV0negVsPtCand"), cand.ptV0Neg, cand.ptCand); + registry.fill(HIST("Data/hV0CPA"), cand.v0CosPA); + registry.fill(HIST("Data/hV0CPAVsPtCand"), cand.v0CosPA, cand.ptCand); + registry.fill(HIST("Data/hV0Radius"), cand.v0Radius); + registry.fill(HIST("Data/hV0RadiusVsPtCand"), cand.v0Radius, cand.ptCand); + registry.fill(HIST("Data/hV0DCADaughters"), cand.dcaV0Daughters); + registry.fill(HIST("Data/hV0DCADaughtersVsPtCand"), cand.dcaV0Daughters, cand.ptCand); + registry.fill(HIST("Data/hV0MK0Short"), cand.mK0Short); + registry.fill(HIST("Data/hV0MK0ShortVsPtCand"), cand.mK0Short, cand.ptCand); + registry.fill(HIST("Data/hV0MLambda"), cand.mLambda); + registry.fill(HIST("Data/hV0MLambdaVsPtCand"), cand.mLambda, cand.ptCand); + registry.fill(HIST("Data/hV0MAntiLambda"), cand.mAntiLambda); + registry.fill(HIST("Data/hV0MAntiLambdaVsPtCand"), cand.mAntiLambda, cand.ptCand); + registry.fill(HIST("Data/hV0MGamma"), cand.mGamma); + registry.fill(HIST("Data/hV0MGammaVsPtCand"), cand.mGamma, cand.ptCand); + registry.fill(HIST("Data/hCtV0K0Short"), cand.ctV0K0Short); + registry.fill(HIST("Data/hCtV0K0ShortVsPtCand"), cand.ctV0K0Short, cand.ptCand); + registry.fill(HIST("Data/hCtV0Lambda"), cand.ctV0Lambda); + registry.fill(HIST("Data/hCtV0LambdaVsPtCand"), cand.ctV0Lambda, cand.ptCand); + registry.fill(HIST("Data/hCPACand"), cand.cpa); + registry.fill(HIST("Data/hCPACandVsPtCand"), cand.cpa, cand.ptCand); + registry.fill(HIST("Data/hCPAxyCand"), cand.cpaXY); + registry.fill(HIST("Data/hCPAxyCandVsPtCand"), cand.cpaXY, cand.ptCand); + registry.fill(HIST("Data/hDecLengthCand"), cand.decayLength); + registry.fill(HIST("Data/hDecLengthCandVsPtCand"), cand.decayLength, cand.ptCand); + registry.fill(HIST("Data/hDecLengthXYCand"), cand.decayLengthXY); + registry.fill(HIST("Data/hDecLengthXYCandVsPtCand"), cand.decayLengthXY, cand.ptCand); + registry.fill(HIST("Data/hCtCand"), cand.ctLc); + registry.fill(HIST("Data/hCtCandVsPtCand"), cand.ctLc, cand.ptCand); + } + + /// Data processing template + /// \tparam FillMl switch to fill ML histograms + template + void runAnalysisPerCollisionData(CollType const& collisions, CandType const& candidates) + { + for (const auto& collision : collisions) { + fillHistosData(collision, candidates); } } - void process(soa::Filtered> const& candidates, - TracksWPid const&) + /// Helper function to fill data histograms + /// \tparam FillMl switch to fill ML histograms + template + void fillHistosData(CollType const& collision, CandType const& candidates) { - for (const auto& candidate : candidates) { + const auto thisCollId = collision.globalIndex(); + const auto& groupedCandidates = candidates.sliceBy(lcToPK0SPerCollision, thisCollId); + + for (const auto& candidate : groupedCandidates) { if (etaCandMax >= 0. && std::abs(candidate.eta()) > etaCandMax) { continue; } if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { continue; } + if (candidate.isSelLcToK0sP() < selectionFlagLcToK0sP) { + continue; + } - auto ptCand = candidate.pt(); - auto eta = candidate.eta(); - auto phi = candidate.phi(); - auto invMassLcToK0sP = HfHelper::invMassLcToK0sP(candidate); - auto ptProng0 = candidate.ptProng0(); - auto ptProng1 = candidate.ptProng1(); - auto impactParameter0 = candidate.impactParameter0(); - auto impactParameter1 = candidate.impactParameter1(); - auto dcaPosToPV = candidate.dcapostopv(); - auto dcaNegToPV = candidate.dcanegtopv(); - auto ptV0Pos = candidate.ptV0Pos(); - auto ptV0Neg = candidate.ptV0Neg(); - auto v0CosPA = candidate.v0cosPA(); - auto v0Radius = candidate.v0radius(); - auto dcaV0Daughters = candidate.dcaV0daughters(); - auto mK0Short = candidate.mK0Short(); - auto mLambda = candidate.mLambda(); - auto mAntiLambda = candidate.mAntiLambda(); - auto mGamma = candidate.mGamma(); - auto ctV0K0Short = HfHelper::ctV0K0s(candidate); - auto ctV0Lambda = HfHelper::ctV0Lambda(candidate); - auto cpa = candidate.cpa(); - auto cpaXY = candidate.cpaXY(); - auto decayLength = candidate.decayLength(); - auto decayLengthXY = candidate.decayLengthXY(); - auto ctLc = HfHelper::ctLc(candidate); - - registry.fill(HIST("hPtCand"), ptCand); - registry.fill(HIST("hEtaCand"), eta); - registry.fill(HIST("hEtaCandVsPtCand"), eta, ptCand); - registry.fill(HIST("hPhiCand"), phi); - registry.fill(HIST("hPhiCandVsPtCand"), phi, ptCand); - registry.fill(HIST("hMass"), invMassLcToK0sP); - registry.fill(HIST("hMassVsPtCand"), invMassLcToK0sP, ptCand); - registry.fill(HIST("hPtBach"), ptProng0); - registry.fill(HIST("hPtBachVsPtCand"), ptProng0, ptCand); - registry.fill(HIST("hPtV0"), ptProng1); - registry.fill(HIST("hPtV0VsPtCand"), ptProng1, ptCand); - registry.fill(HIST("hd0Bach"), impactParameter0); - registry.fill(HIST("hd0BachVsPtCand"), impactParameter0, ptCand); - registry.fill(HIST("hd0V0"), impactParameter1); - registry.fill(HIST("hd0V0VsPtCand"), impactParameter1, ptCand); - registry.fill(HIST("hd0V0pos"), dcaPosToPV); - registry.fill(HIST("hd0V0posVsPtCand"), dcaPosToPV, ptCand); - registry.fill(HIST("hd0V0neg"), dcaNegToPV); - registry.fill(HIST("hd0V0negVsPtCand"), dcaNegToPV, ptCand); - registry.fill(HIST("hPtV0pos"), ptV0Pos); - registry.fill(HIST("hPtV0posVsPtCand"), ptV0Pos, ptCand); - registry.fill(HIST("hPtV0neg"), ptV0Neg); - registry.fill(HIST("hPtV0negVsPtCand"), ptV0Neg, ptCand); - registry.fill(HIST("hV0CPA"), v0CosPA); - registry.fill(HIST("hV0CPAVsPtCand"), v0CosPA, ptCand); - registry.fill(HIST("hV0Radius"), v0Radius); - registry.fill(HIST("hV0RadiusVsPtCand"), v0Radius, ptCand); - registry.fill(HIST("hV0DCADaughters"), dcaV0Daughters); - registry.fill(HIST("hV0DCADaughtersVsPtCand"), dcaV0Daughters, ptCand); - registry.fill(HIST("hV0MK0Short"), mK0Short); - registry.fill(HIST("hV0MK0ShortVsPtCand"), mK0Short, ptCand); - registry.fill(HIST("hV0MLambda"), mLambda); - registry.fill(HIST("hV0MLambdaVsPtCand"), mLambda, ptCand); - registry.fill(HIST("hV0MAntiLambda"), mAntiLambda); - registry.fill(HIST("hV0MAntiLambdaVsPtCand"), mAntiLambda, ptCand); - registry.fill(HIST("hV0MGamma"), mGamma); - registry.fill(HIST("hV0MGammaVsPtCand"), mGamma, ptCand); - registry.fill(HIST("hCtV0K0Short"), ctV0K0Short); - registry.fill(HIST("hCtV0K0ShortVsPtCand"), ctV0K0Short, ptCand); - registry.fill(HIST("hCtV0Lambda"), ctV0Lambda); - registry.fill(HIST("hCtV0LambdaVsPtCand"), ctV0Lambda, ptCand); - registry.fill(HIST("hCPACand"), cpa); - registry.fill(HIST("hCPACandVsPtCand"), cpa, ptCand); - registry.fill(HIST("hCPAxyCand"), cpaXY); - registry.fill(HIST("hCPAxyCandVsPtCand"), cpaXY, ptCand); - registry.fill(HIST("hDecLengthCand"), decayLength); - registry.fill(HIST("hDecLengthCandVsPtCand"), decayLength, ptCand); - registry.fill(HIST("hDecLengthXYCand"), decayLengthXY); - registry.fill(HIST("hDecLengthXYCandVsPtCand"), decayLengthXY, ptCand); - registry.fill(HIST("hCtCand"), ctLc); - registry.fill(HIST("hCtCandVsPtCand"), ctLc, ptCand); - - const auto& bach = candidate.prong0_as(); // bachelor track + fillCandHistograms(candidate); + + // PID histograms + const auto& bach = candidate.template prong0_as(); auto tpcNSigmaPr = bach.tpcNSigmaPr(); auto pBach = bach.p(); - registry.fill(HIST("hTPCNSigmaPrBach"), tpcNSigmaPr); - registry.fill(HIST("hPBachVsTPCNSigmaPrBach"), pBach, tpcNSigmaPr); + registry.fill(HIST("Data/hTPCNSigmaPrBach"), tpcNSigmaPr); + registry.fill(HIST("Data/hPBachVsTPCNSigmaPrBach"), pBach, tpcNSigmaPr); if (bach.hasTOF()) { - auto tofNSigmaPr = bach.tofNSigmaPr(); - registry.fill(HIST("hTOFNSigmaPrBach"), tofNSigmaPr); - registry.fill(HIST("hPBachVsTOFNSigmaPrBach"), pBach, tofNSigmaPr); + registry.fill(HIST("Data/hTOFNSigmaPrBach"), bach.tofNSigmaPr()); + registry.fill(HIST("Data/hPBachVsTOFNSigmaPrBach"), pBach, bach.tofNSigmaPr()); + } + + if (fillTHn && FillMl) { + if constexpr (CandType::template contains()) { + if (candidate.mlProbLcToK0sP().size() == NumberOfMlClasses) { + float ml0 = candidate.mlProbLcToK0sP()[MlClassBackground]; + float ml1 = candidate.mlProbLcToK0sP()[MlClassPrompt]; + float ml2 = candidate.mlProbLcToK0sP()[MlClassNonPrompt]; + if (ml0 >= 0.f && ml1 >= 0.f && ml2 >= 0.f) { + float cent = evaluateCentralityColl(collision); + int numPvContr = collision.numContrib(); + registry.get(HIST("hnLcK0sPDataWithBdt"))->Fill(HfHelper::invMassLcToK0sP(candidate), candidate.pt(), cent, ml0, ml1, ml2, static_cast(numPvContr)); + } + } + } } } } - void processMc(soa::Filtered> const& candidates, - soa::Join const& mcParticles, - aod::TracksWMc const&, - TracksWPid const&) + /// Fill MC reconstructed signal histograms + /// \tparam SignalType signal class (Signal, Prompt, NonPrompt) + template + void fillHistogramsRecSig(CandidateType const& candidate) { - // MC rec. - for (const auto& candidate : candidates) { + HfCandVars cand(candidate); + + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.eta); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaCandVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.eta, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPhiCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.phi); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPhiCandVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.phi, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hMassRecSig") + HIST(SignalSuffixes[SignalType]), cand.invMassLcToK0sP); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hMassVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.invMassLcToK0sP, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtBachRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptProng0); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtBachVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptProng0, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtV0RecSig") + HIST(SignalSuffixes[SignalType]), cand.ptProng1); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtV0VsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptProng1, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0BachRecSig") + HIST(SignalSuffixes[SignalType]), cand.impactParameter0); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0BachVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.impactParameter0, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0V0RecSig") + HIST(SignalSuffixes[SignalType]), cand.impactParameter1); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0V0VsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.impactParameter1, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0V0posRecSig") + HIST(SignalSuffixes[SignalType]), cand.dcaPosToPV); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0V0posVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.dcaPosToPV, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0V0negRecSig") + HIST(SignalSuffixes[SignalType]), cand.dcaNegToPV); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hd0V0negVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.dcaNegToPV, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtV0posRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptV0Pos); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtV0posVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptV0Pos, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtV0negRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptV0Neg); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPtV0negVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ptV0Neg, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0CPARecSig") + HIST(SignalSuffixes[SignalType]), cand.v0CosPA); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0CPAVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.v0CosPA, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0RadiusRecSig") + HIST(SignalSuffixes[SignalType]), cand.v0Radius); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0RadiusVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.v0Radius, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0DCADaughtersRecSig") + HIST(SignalSuffixes[SignalType]), cand.dcaV0Daughters); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0DCADaughtersVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.dcaV0Daughters, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MK0ShortRecSig") + HIST(SignalSuffixes[SignalType]), cand.mK0Short); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MK0ShortVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.mK0Short, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MLambdaRecSig") + HIST(SignalSuffixes[SignalType]), cand.mLambda); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MLambdaVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.mLambda, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MAntiLambdaRecSig") + HIST(SignalSuffixes[SignalType]), cand.mAntiLambda); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MAntiLambdaVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.mAntiLambda, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MGammaRecSig") + HIST(SignalSuffixes[SignalType]), cand.mGamma); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hV0MGammaVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.mGamma, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCtV0K0ShortRecSig") + HIST(SignalSuffixes[SignalType]), cand.ctV0K0Short); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCtV0K0ShortVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ctV0K0Short, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCtV0LambdaRecSig") + HIST(SignalSuffixes[SignalType]), cand.ctV0Lambda); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCtV0LambdaVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ctV0Lambda, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCPACandRecSig") + HIST(SignalSuffixes[SignalType]), cand.cpa); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCPACandVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.cpa, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCPAxyCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.cpaXY); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCPAxyCandVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.cpaXY, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.decayLength); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthCandVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.decayLength, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthXYCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.decayLengthXY); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hDecLengthXYCandVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.decayLengthXY, cand.ptCand); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ctLc); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hCtCandVsPtCandRecSig") + HIST(SignalSuffixes[SignalType]), cand.ctLc, cand.ptCand); + + // PID + const auto& bach = candidate.template prong0_as(); + auto tpcNSigmaPr = bach.tpcNSigmaPr(); + auto pBach = bach.p(); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hTPCNSigmaPrBachRecSig") + HIST(SignalSuffixes[SignalType]), tpcNSigmaPr); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPBachVsTPCNSigmaPrBachRecSig") + HIST(SignalSuffixes[SignalType]), pBach, tpcNSigmaPr); + if (bach.hasTOF()) { + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hTOFNSigmaPrBachRecSig") + HIST(SignalSuffixes[SignalType]), bach.tofNSigmaPr()); + registry.fill(HIST("MC/Rec/") + HIST(SignalFolders[SignalType]) + HIST("/hPBachVsTOFNSigmaPrBachRecSig") + HIST(SignalSuffixes[SignalType]), pBach, bach.tofNSigmaPr()); + } + } + + /// MC processing template + /// \tparam FillMl switch to fill ML histograms + template + void runAnalysisPerCollisionMc(CollType const& collisions, + CandType const& candidates, + CandMcGen const& mcParticles) + { + for (const auto& collision : collisions) { + fillHistosMcRec(collision, candidates, mcParticles); + } + fillHistosMcGen(mcParticles, collisions); + } + + /// Helper function to fill MC reconstructed histograms + /// \tparam FillMl switch to fill ML histograms + template + void fillHistosMcRec(CollType const& collision, CandType const& candidates, CandMcGen const&) + { + const auto thisCollId = collision.globalIndex(); + const auto& groupedCandidates = candidates.sliceBy(lcToPK0SPerCollision, thisCollId); + + for (const auto& candidate : groupedCandidates) { if (etaCandMax >= 0. && std::abs(candidate.eta()) > etaCandMax) { continue; } - if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { continue; } - auto ptCand = candidate.pt(); - auto eta = candidate.eta(); - auto phi = candidate.phi(); - auto invMassLcToK0sP = HfHelper::invMassLcToK0sP(candidate); - auto ptProng0 = candidate.ptProng0(); - auto ptProng1 = candidate.ptProng1(); - auto impactParameter0 = candidate.impactParameter0(); - auto impactParameter1 = candidate.impactParameter1(); - auto dcaPosToPV = candidate.dcapostopv(); - auto dcaNegToPV = candidate.dcanegtopv(); - auto ptV0Pos = candidate.ptV0Pos(); - auto ptV0Neg = candidate.ptV0Neg(); - auto v0CosPA = candidate.v0cosPA(); - auto v0Radius = candidate.v0radius(); - auto dcaV0Daughters = candidate.dcaV0daughters(); - auto mK0Short = candidate.mK0Short(); - auto mLambda = candidate.mLambda(); - auto mAntiLambda = candidate.mAntiLambda(); - auto mGamma = candidate.mGamma(); - auto ctV0K0Short = HfHelper::ctV0K0s(candidate); - auto ctV0Lambda = HfHelper::ctV0Lambda(candidate); - auto cpa = candidate.cpa(); - auto cpaXY = candidate.cpaXY(); - auto decayLength = candidate.decayLength(); - auto decayLengthXY = candidate.decayLengthXY(); - auto ctLc = HfHelper::ctLc(candidate); - - const auto& bach = candidate.prong0_as(); // bachelor track - auto tpcNSigmaPr = bach.tpcNSigmaPr(); - auto pBach = bach.p(); + if (std::abs(candidate.flagMcMatchRec()) == DecayChannelLcToK0sP) { + fillCandHistograms(candidate); + + // MC reconstructed signal + fillHistogramsRecSig(candidate); - if (std::abs(candidate.flagMcMatchRec()) == 1) { + // reconstructed signal prompt / nonprompt if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("MC/Rec/hPtCandRecSigPrompt"), ptCand); + fillHistogramsRecSig(candidate); } else if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("MC/Rec/hPtCandRecSigNonPrompt"), ptCand); + fillHistogramsRecSig(candidate); } - registry.fill(HIST("MC/Rec/hPtCandRecSig"), ptCand); - registry.fill(HIST("MC/Rec/hEtaCandRecSig"), eta); - registry.fill(HIST("MC/Rec/hEtaCandVsPtCandRecSig"), eta, ptCand); - registry.fill(HIST("MC/Rec/hPhiCandRecSig"), phi); - registry.fill(HIST("MC/Rec/hPhiCandVsPtCandRecSig"), phi, ptCand); - registry.fill(HIST("MC/Rec/hMassRecSig"), invMassLcToK0sP); - registry.fill(HIST("MC/Rec/hMassVsPtCandRecSig"), invMassLcToK0sP, ptCand); - registry.fill(HIST("MC/Rec/hPtBachRecSig"), ptProng0); - registry.fill(HIST("MC/Rec/hPtBachVsPtCandRecSig"), ptProng0, ptCand); - registry.fill(HIST("MC/Rec/hPtV0RecSig"), ptProng1); - registry.fill(HIST("MC/Rec/hPtV0VsPtCandRecSig"), ptProng1, ptCand); - registry.fill(HIST("MC/Rec/hd0BachRecSig"), impactParameter0); - registry.fill(HIST("MC/Rec/hd0BachVsPtCandRecSig"), impactParameter0, ptCand); - registry.fill(HIST("MC/Rec/hd0V0RecSig"), impactParameter1); - registry.fill(HIST("MC/Rec/hd0V0VsPtCandRecSig"), impactParameter1, ptCand); - registry.fill(HIST("MC/Rec/hd0V0posRecSig"), dcaPosToPV); - registry.fill(HIST("MC/Rec/hd0V0posVsPtCandRecSig"), dcaPosToPV, ptCand); - registry.fill(HIST("MC/Rec/hd0V0negRecSig"), dcaNegToPV); - registry.fill(HIST("MC/Rec/hd0V0negVsPtCandRecSig"), dcaNegToPV, ptCand); - registry.fill(HIST("MC/Rec/hPtV0posRecSig"), ptV0Pos); - registry.fill(HIST("MC/Rec/hPtV0posVsPtCandRecSig"), ptV0Pos, ptCand); - registry.fill(HIST("MC/Rec/hPtV0negRecSig"), ptV0Neg); - registry.fill(HIST("MC/Rec/hPtV0negVsPtCandRecSig"), ptV0Neg, ptCand); - registry.fill(HIST("MC/Rec/hV0CPARecSig"), v0CosPA); - registry.fill(HIST("MC/Rec/hV0CPAVsPtCandRecSig"), v0CosPA, ptCand); - registry.fill(HIST("MC/Rec/hV0RadiusRecSig"), v0Radius); - registry.fill(HIST("MC/Rec/hV0RadiusVsPtCandRecSig"), v0Radius, ptCand); - registry.fill(HIST("MC/Rec/hV0DCADaughtersRecSig"), dcaV0Daughters); - registry.fill(HIST("MC/Rec/hV0DCADaughtersVsPtCandRecSig"), dcaV0Daughters, ptCand); - registry.fill(HIST("MC/Rec/hV0MK0ShortRecSig"), mK0Short); - registry.fill(HIST("MC/Rec/hV0MK0ShortVsPtCandRecSig"), mK0Short, ptCand); - registry.fill(HIST("MC/Rec/hV0MLambdaRecSig"), mLambda); - registry.fill(HIST("MC/Rec/hV0MLambdaVsPtCandRecSig"), mLambda, ptCand); - registry.fill(HIST("MC/Rec/hV0MAntiLambdaRecSig"), mAntiLambda); - registry.fill(HIST("MC/Rec/hV0MAntiLambdaVsPtCandRecSig"), mAntiLambda, ptCand); - registry.fill(HIST("MC/Rec/hV0MGammaRecSig"), mGamma); - registry.fill(HIST("MC/Rec/hV0MGammaVsPtCandRecSig"), mGamma, ptCand); - registry.fill(HIST("MC/Rec/hCtV0K0ShortRecSig"), ctV0K0Short); - registry.fill(HIST("MC/Rec/hCtV0K0ShortVsPtCandRecSig"), ctV0K0Short, ptCand); - registry.fill(HIST("MC/Rec/hCtV0LambdaRecSig"), ctV0Lambda); - registry.fill(HIST("MC/Rec/hCtV0LambdaVsPtCandRecSig"), ctV0Lambda, ptCand); - registry.fill(HIST("MC/Rec/hCPACandRecSig"), cpa); - registry.fill(HIST("MC/Rec/hCPACandVsPtCandRecSig"), cpa, ptCand); - registry.fill(HIST("MC/Rec/hCPAxyCandRecSig"), cpaXY); - registry.fill(HIST("MC/Rec/hCPAxyCandVsPtCandRecSig"), cpaXY, ptCand); - registry.fill(HIST("MC/Rec/hDecLengthCandRecSig"), decayLength); - registry.fill(HIST("MC/Rec/hDecLengthCandVsPtCandRecSig"), decayLength, ptCand); - registry.fill(HIST("MC/Rec/hDecLengthXYCandRecSig"), decayLengthXY); - registry.fill(HIST("MC/Rec/hDecLengthXYCandVsPtCandRecSig"), decayLengthXY, ptCand); - registry.fill(HIST("MC/Rec/hCtCandRecSig"), ctLc); - registry.fill(HIST("MC/Rec/hCtCandVsPtCandRecSig"), ctLc, ptCand); - registry.fill(HIST("MC/Rec/hTPCNSigmaPrBachRecSig"), tpcNSigmaPr); - registry.fill(HIST("MC/Rec/hPBachVsTPCNSigmaPrBachRecSig"), pBach, tpcNSigmaPr); - if (bach.hasTOF()) { - auto tofNSigmaPr = bach.tofNSigmaPr(); - registry.fill(HIST("MC/Rec/hTOFNSigmaPrBachRecSig"), tofNSigmaPr); - registry.fill(HIST("MC/Rec/hPBachVsTOFNSigmaPrBachRecSig"), pBach, tofNSigmaPr); + + if (fillTHn && FillMl) { + float mass = HfHelper::invMassLcToK0sP(candidate); + float pt = candidate.pt(); + std::array mlScores{-1.f, -1.f, -1.f}; + if constexpr (CandType::template contains()) { + if (candidate.mlProbLcToK0sP().size() == NumberOfMlClasses) { + mlScores = {candidate.mlProbLcToK0sP()[MlClassBackground], + candidate.mlProbLcToK0sP()[MlClassPrompt], + candidate.mlProbLcToK0sP()[MlClassNonPrompt]}; + } + } + float cent = evaluateCentralityColl(collision); + int numPvContr = collision.numContrib(); + int8_t origin = candidate.originMcRec(); + registry.get(HIST("hnLcK0sPRecMcWithBdt"))->Fill(mass, pt, cent, mlScores[0], mlScores[1], mlScores[2], static_cast(numPvContr), origin); } } else { - registry.fill(HIST("MC/Rec/hPtCandRecBg"), ptCand); - registry.fill(HIST("MC/Rec/hEtaCandRecBg"), eta); - registry.fill(HIST("MC/Rec/hEtaCandVsPtCandRecBg"), eta, ptCand); - registry.fill(HIST("MC/Rec/hPhiCandRecBg"), phi); - registry.fill(HIST("MC/Rec/hPhiCandVsPtCandRecBg"), phi, ptCand); - registry.fill(HIST("MC/Rec/hMassRecBg"), invMassLcToK0sP); - registry.fill(HIST("MC/Rec/hMassVsPtCandRecBg"), invMassLcToK0sP, ptCand); - registry.fill(HIST("MC/Rec/hPtBachRecBg"), ptProng0); - registry.fill(HIST("MC/Rec/hPtBachVsPtCandRecBg"), ptProng0, ptCand); - registry.fill(HIST("MC/Rec/hPtV0RecBg"), ptProng1); - registry.fill(HIST("MC/Rec/hPtV0VsPtCandRecBg"), ptProng1, ptCand); - registry.fill(HIST("MC/Rec/hd0BachRecBg"), impactParameter0); - registry.fill(HIST("MC/Rec/hd0BachVsPtCandRecBg"), impactParameter0, ptCand); - registry.fill(HIST("MC/Rec/hd0V0RecBg"), impactParameter1); - registry.fill(HIST("MC/Rec/hd0V0VsPtCandRecBg"), impactParameter1, ptCand); - registry.fill(HIST("MC/Rec/hd0V0posRecBg"), dcaPosToPV); - registry.fill(HIST("MC/Rec/hd0V0posVsPtCandRecBg"), dcaPosToPV, ptCand); - registry.fill(HIST("MC/Rec/hd0V0negRecBg"), dcaNegToPV); - registry.fill(HIST("MC/Rec/hd0V0negVsPtCandRecBg"), dcaNegToPV, ptCand); - registry.fill(HIST("MC/Rec/hPtV0posRecBg"), ptV0Pos); - registry.fill(HIST("MC/Rec/hPtV0posVsPtCandRecBg"), ptV0Pos, ptCand); - registry.fill(HIST("MC/Rec/hPtV0negRecBg"), ptV0Neg); - registry.fill(HIST("MC/Rec/hPtV0negVsPtCandRecBg"), ptV0Neg, ptCand); - registry.fill(HIST("MC/Rec/hV0CPARecBg"), v0CosPA); - registry.fill(HIST("MC/Rec/hV0CPAVsPtCandRecBg"), v0CosPA, ptCand); - registry.fill(HIST("MC/Rec/hV0RadiusRecBg"), v0Radius); - registry.fill(HIST("MC/Rec/hV0RadiusVsPtCandRecBg"), v0Radius, ptCand); - registry.fill(HIST("MC/Rec/hV0DCADaughtersRecBg"), dcaV0Daughters); - registry.fill(HIST("MC/Rec/hV0DCADaughtersVsPtCandRecBg"), dcaV0Daughters, ptCand); - registry.fill(HIST("MC/Rec/hV0MK0ShortRecBg"), mK0Short); - registry.fill(HIST("MC/Rec/hV0MK0ShortVsPtCandRecBg"), mK0Short, ptCand); - registry.fill(HIST("MC/Rec/hV0MLambdaRecBg"), mLambda); - registry.fill(HIST("MC/Rec/hV0MLambdaVsPtCandRecBg"), mLambda, ptCand); - registry.fill(HIST("MC/Rec/hV0MAntiLambdaRecBg"), mAntiLambda); - registry.fill(HIST("MC/Rec/hV0MAntiLambdaVsPtCandRecBg"), mAntiLambda, ptCand); - registry.fill(HIST("MC/Rec/hV0MGammaRecBg"), mGamma); - registry.fill(HIST("MC/Rec/hV0MGammaVsPtCandRecBg"), mGamma, ptCand); - registry.fill(HIST("MC/Rec/hCtV0K0ShortRecBg"), ctV0K0Short); - registry.fill(HIST("MC/Rec/hCtV0K0ShortVsPtCandRecBg"), ctV0K0Short, ptCand); - registry.fill(HIST("MC/Rec/hCtV0LambdaRecBg"), ctV0Lambda); - registry.fill(HIST("MC/Rec/hCtV0LambdaVsPtCandRecBg"), ctV0Lambda, ptCand); - registry.fill(HIST("MC/Rec/hCPACandRecBg"), cpa); - registry.fill(HIST("MC/Rec/hCPACandVsPtCandRecBg"), cpa, ptCand); - registry.fill(HIST("MC/Rec/hCPAxyCandRecBg"), cpaXY); - registry.fill(HIST("MC/Rec/hCPAxyCandVsPtCandRecBg"), cpaXY, ptCand); - registry.fill(HIST("MC/Rec/hDecLengthCandRecBg"), decayLength); - registry.fill(HIST("MC/Rec/hDecLengthCandVsPtCandRecBg"), decayLength, ptCand); - registry.fill(HIST("MC/Rec/hDecLengthXYCandRecBg"), decayLengthXY); - registry.fill(HIST("MC/Rec/hDecLengthXYCandVsPtCandRecBg"), decayLengthXY, ptCand); - registry.fill(HIST("MC/Rec/hCtCandRecBg"), ctLc); - registry.fill(HIST("MC/Rec/hCtCandVsPtCandRecBg"), ctLc, ptCand); - registry.fill(HIST("MC/Rec/hTPCNSigmaPrBachRecBg"), tpcNSigmaPr); - registry.fill(HIST("MC/Rec/hPBachVsTPCNSigmaPrBachRecBg"), pBach, tpcNSigmaPr); + // MC reconstructed background + HfCandVars cand(candidate); + + registry.fill(HIST("MC/Rec/background/hPtCandRecBg"), cand.ptCand); + registry.fill(HIST("MC/Rec/background/hEtaCandRecBg"), cand.eta); + registry.fill(HIST("MC/Rec/background/hEtaCandVsPtCandRecBg"), cand.eta, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hPhiCandRecBg"), cand.phi); + registry.fill(HIST("MC/Rec/background/hPhiCandVsPtCandRecBg"), cand.phi, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hMassRecBg"), cand.invMassLcToK0sP); + registry.fill(HIST("MC/Rec/background/hMassVsPtCandRecBg"), cand.invMassLcToK0sP, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hPtBachRecBg"), cand.ptProng0); + registry.fill(HIST("MC/Rec/background/hPtBachVsPtCandRecBg"), cand.ptProng0, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hPtV0RecBg"), cand.ptProng1); + registry.fill(HIST("MC/Rec/background/hPtV0VsPtCandRecBg"), cand.ptProng1, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hd0BachRecBg"), cand.impactParameter0); + registry.fill(HIST("MC/Rec/background/hd0BachVsPtCandRecBg"), cand.impactParameter0, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hd0V0RecBg"), cand.impactParameter1); + registry.fill(HIST("MC/Rec/background/hd0V0VsPtCandRecBg"), cand.impactParameter1, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hd0V0posRecBg"), cand.dcaPosToPV); + registry.fill(HIST("MC/Rec/background/hd0V0posVsPtCandRecBg"), cand.dcaPosToPV, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hd0V0negRecBg"), cand.dcaNegToPV); + registry.fill(HIST("MC/Rec/background/hd0V0negVsPtCandRecBg"), cand.dcaNegToPV, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hPtV0posRecBg"), cand.ptV0Pos); + registry.fill(HIST("MC/Rec/background/hPtV0posVsPtCandRecBg"), cand.ptV0Pos, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hPtV0negRecBg"), cand.ptV0Neg); + registry.fill(HIST("MC/Rec/background/hPtV0negVsPtCandRecBg"), cand.ptV0Neg, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hV0CPARecBg"), cand.v0CosPA); + registry.fill(HIST("MC/Rec/background/hV0CPAVsPtCandRecBg"), cand.v0CosPA, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hV0RadiusRecBg"), cand.v0Radius); + registry.fill(HIST("MC/Rec/background/hV0RadiusVsPtCandRecBg"), cand.v0Radius, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hV0DCADaughtersRecBg"), cand.dcaV0Daughters); + registry.fill(HIST("MC/Rec/background/hV0DCADaughtersVsPtCandRecBg"), cand.dcaV0Daughters, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hV0MK0ShortRecBg"), cand.mK0Short); + registry.fill(HIST("MC/Rec/background/hV0MK0ShortVsPtCandRecBg"), cand.mK0Short, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hV0MLambdaRecBg"), cand.mLambda); + registry.fill(HIST("MC/Rec/background/hV0MLambdaVsPtCandRecBg"), cand.mLambda, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hV0MAntiLambdaRecBg"), cand.mAntiLambda); + registry.fill(HIST("MC/Rec/background/hV0MAntiLambdaVsPtCandRecBg"), cand.mAntiLambda, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hV0MGammaRecBg"), cand.mGamma); + registry.fill(HIST("MC/Rec/background/hV0MGammaVsPtCandRecBg"), cand.mGamma, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hCtV0K0ShortRecBg"), cand.ctV0K0Short); + registry.fill(HIST("MC/Rec/background/hCtV0K0ShortVsPtCandRecBg"), cand.ctV0K0Short, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hCtV0LambdaRecBg"), cand.ctV0Lambda); + registry.fill(HIST("MC/Rec/background/hCtV0LambdaVsPtCandRecBg"), cand.ctV0Lambda, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hCPACandRecBg"), cand.cpa); + registry.fill(HIST("MC/Rec/background/hCPACandVsPtCandRecBg"), cand.cpa, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hCPAxyCandRecBg"), cand.cpaXY); + registry.fill(HIST("MC/Rec/background/hCPAxyCandVsPtCandRecBg"), cand.cpaXY, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hDecLengthCandRecBg"), cand.decayLength); + registry.fill(HIST("MC/Rec/background/hDecLengthCandVsPtCandRecBg"), cand.decayLength, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hDecLengthXYCandRecBg"), cand.decayLengthXY); + registry.fill(HIST("MC/Rec/background/hDecLengthXYCandVsPtCandRecBg"), cand.decayLengthXY, cand.ptCand); + registry.fill(HIST("MC/Rec/background/hCtCandRecBg"), cand.ctLc); + registry.fill(HIST("MC/Rec/background/hCtCandVsPtCandRecBg"), cand.ctLc, cand.ptCand); + + // PID background + const auto& bach = candidate.template prong0_as(); + auto tpcNSigmaPr = bach.tpcNSigmaPr(); + auto pBach = bach.p(); + registry.fill(HIST("MC/Rec/background/hTPCNSigmaPrBachRecBg"), tpcNSigmaPr); + registry.fill(HIST("MC/Rec/background/hPBachVsTPCNSigmaPrBachRecBg"), pBach, tpcNSigmaPr); if (bach.hasTOF()) { - auto tofNSigmaPr = bach.tofNSigmaPr(); - registry.fill(HIST("MC/Rec/hTOFNSigmaPrBachRecBg"), tofNSigmaPr); - registry.fill(HIST("MC/Rec/hPBachVsTOFNSigmaPrBachRecBg"), pBach, tofNSigmaPr); + registry.fill(HIST("MC/Rec/background/hTOFNSigmaPrBachRecBg"), bach.tofNSigmaPr()); + registry.fill(HIST("MC/Rec/background/hPBachVsTOFNSigmaPrBachRecBg"), pBach, bach.tofNSigmaPr()); } } } - // MC gen. - for (const auto& particle : mcParticles) { - if (etaCandMax >= 0. && std::abs(particle.eta()) > etaCandMax) { - continue; - } + } - if (std::abs(particle.flagMcMatchGen()) == 1) { + /// Fill MC generated signal histograms + /// \tparam SignalType signal class (Signal, Prompt, NonPrompt) + template + void fillHistogramsGenSig(ParticleType const& particle) + { + registry.fill(HIST("MC/Gen/") + HIST(SignalFolders[SignalType]) + HIST("/hPtCandGen") + HIST(SignalSuffixes[SignalType]), particle.pt()); + registry.fill(HIST("MC/Gen/") + HIST(SignalFolders[SignalType]) + HIST("/hEtaCandGen") + HIST(SignalSuffixes[SignalType]), particle.eta()); + registry.fill(HIST("MC/Gen/") + HIST(SignalFolders[SignalType]) + HIST("/hPhiCandGen") + HIST(SignalSuffixes[SignalType]), particle.phi()); + } + /// Helper function to fill MC generated histograms + template + void fillHistosMcGen(CandMcGen const& mcParticles, Coll const& recoCollisions) + { + for (const auto& particle : mcParticles) { + if (std::abs(particle.flagMcMatchGen()) == DecayChannelLcToK0sP) { auto yGen = RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus); if (yCandGenMax >= 0. && std::abs(yGen) > yCandGenMax) { continue; } - auto ptCand = particle.pt(); - auto eta = particle.eta(); - auto phi = particle.phi(); - registry.fill(HIST("MC/Gen/hPtCandGen"), ptCand); - registry.fill(HIST("MC/Gen/hEtaCandGen"), eta); - registry.fill(HIST("MC/Gen/hEtaCandVsPtCandGen"), eta, ptCand); - registry.fill(HIST("MC/Gen/hPhiCandGen"), phi); - registry.fill(HIST("MC/Gen/hPhiCandVsPtCandGen"), phi, ptCand); - + fillHistogramsGenSig(particle); if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("MC/Gen/hPtCandGenPrompt"), ptCand); + fillHistogramsGenSig(particle); } else if (particle.originMcGen() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("MC/Gen/hPtCandGenNonPrompt"), ptCand); + fillHistogramsGenSig(particle); + } + + if (fillTHn) { + int8_t origin = particle.originMcGen(); + float ptB = (origin == RecoDecay::OriginType::NonPrompt) ? mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt() : -1.; + const auto& recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollision, particle.mcCollision().globalIndex()); + int numPvContr = 0; + for (const auto& recCol : recoCollsPerMcColl) { + numPvContr = recCol.numContrib() > numPvContr ? recCol.numContrib() : numPvContr; + } + float cent = o2::hf_centrality::getCentralityGenColl(recoCollsPerMcColl); + registry.get(HIST("hnLcK0sPGenMc"))->Fill(particle.pt(), cent, yGen, static_cast(numPvContr), ptB, origin); } } } } - PROCESS_SWITCH(HfTaskLcToK0sP, processMc, "Process MC data", false); + void processDataStd(Collisions const& collisions, + FilteredCandLcToPK0SWSelFlag const& candidates, + TracksWPid const&) + { + runAnalysisPerCollisionData(collisions, candidates); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processDataStd, "Process Data", false); + + void processMcStd(CollisionsMc const& collisions, + FilteredCandLcToPK0SWSelFlagAndMc const& candidates, + McParticlesCascadeMatched const& mcParticles, + aod::TracksWMc const&, + aod::McCollisions const&, + TracksWPid const&) + { + runAnalysisPerCollisionMc(collisions, candidates, mcParticles); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processMcStd, "Process MC data", false); + + // Data with ML + void processDataWithMl(Collisions const& collisions, + FilteredCandLcToPK0SWSelFlagAndMl const& candidates, + TracksWPid const&) + { + runAnalysisPerCollisionData(collisions, candidates); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processDataWithMl, "Process Data with ML", false); + + // MC Rec and Gen with ML + void processMcWithMl(CollisionsMc const& collisions, + FilteredCandLcToPK0SWSelFlagAndMcAndMl const& candidates, + McParticlesCascadeMatched const& mcParticles, + aod::TracksWMc const&, + aod::McCollisions const&, + TracksWPid const&) + { + runAnalysisPerCollisionMc(collisions, candidates, mcParticles); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processMcWithMl, "Process MC with ML", false); + + // Data with FT0C centrality + void processDataStdWithFT0C(CollisionsWithFT0C const& collisions, + FilteredCandLcToPK0SWSelFlag const& candidates, + TracksWPid const&) + { + runAnalysisPerCollisionData(collisions, candidates); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processDataStdWithFT0C, "Process Data with FT0C centrality", false); + + // Data with FT0M centrality + void processDataStdWithFT0M(CollisionsWithFT0M const& collisions, + FilteredCandLcToPK0SWSelFlag const& candidates, + TracksWPid const&) + { + runAnalysisPerCollisionData(collisions, candidates); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processDataStdWithFT0M, "Process Data with FT0M centrality", false); + + // Data with ML + FT0C centrality + void processDataWithMlWithFT0C(CollisionsWithFT0C const& collisions, + FilteredCandLcToPK0SWSelFlagAndMl const& candidates, + TracksWPid const&) + { + runAnalysisPerCollisionData(collisions, candidates); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processDataWithMlWithFT0C, "Process Data with ML and FT0C centrality", false); + + // Data with ML + FT0M centrality + void processDataWithMlWithFT0M(CollisionsWithFT0M const& collisions, + FilteredCandLcToPK0SWSelFlagAndMl const& candidates, + TracksWPid const&) + { + runAnalysisPerCollisionData(collisions, candidates); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processDataWithMlWithFT0M, "Process Data with ML and FT0M centrality", false); + + // MC Std with FT0C centrality + void processMcStdWithFT0C(CollisionsMcWithFT0C const& collisions, + FilteredCandLcToPK0SWSelFlagAndMc const& candidates, + McParticlesCascadeMatched const& mcParticles, + aod::TracksWMc const&, + aod::McCollisions const&, + TracksWPid const&) + { + runAnalysisPerCollisionMc(collisions, candidates, mcParticles); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processMcStdWithFT0C, "Process MC with FT0C centrality", false); + + // MC Std with FT0M centrality + void processMcStdWithFT0M(CollisionsMcWithFT0M const& collisions, + FilteredCandLcToPK0SWSelFlagAndMc const& candidates, + McParticlesCascadeMatched const& mcParticles, + aod::TracksWMc const&, + aod::McCollisions const&, + TracksWPid const&) + { + runAnalysisPerCollisionMc(collisions, candidates, mcParticles); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processMcStdWithFT0M, "Process MC with FT0M centrality", false); + + // MC with ML + FT0C centrality + void processMcWithMlWithFT0C(CollisionsMcWithFT0C const& collisions, + FilteredCandLcToPK0SWSelFlagAndMcAndMl const& candidates, + McParticlesCascadeMatched const& mcParticles, + aod::TracksWMc const&, + aod::McCollisions const&, + TracksWPid const&) + { + runAnalysisPerCollisionMc(collisions, candidates, mcParticles); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processMcWithMlWithFT0C, "Process MC with ML and FT0C centrality", false); + + // MC with ML + FT0M centrality + void processMcWithMlWithFT0M(CollisionsMcWithFT0M const& collisions, + FilteredCandLcToPK0SWSelFlagAndMcAndMl const& candidates, + McParticlesCascadeMatched const& mcParticles, + aod::TracksWMc const&, + aod::McCollisions const&, + TracksWPid const&) + { + runAnalysisPerCollisionMc(collisions, candidates, mcParticles); + } + PROCESS_SWITCH(HfTaskLcToK0sP, processMcWithMlWithFT0M, "Process MC with ML and FT0M centrality", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc), - }; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGHF/D2H/Tasks/taskNetCharmFluctuations.cxx b/PWGHF/D2H/Tasks/taskNetCharmFluctuations.cxx new file mode 100644 index 00000000000..37542fdc8f1 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskNetCharmFluctuations.cxx @@ -0,0 +1,451 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskNetCharmFluctuations.cxx +/// \brief Producer of per-candidate and per-event charm-counting tables for net-charm fluctuation studies +/// \author Biao Zhang , Heidelberg University +/// \author Fan Si , Heidelberg University + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_evsel; + +namespace o2::aod +{ +namespace eyefluc +{ +DECLARE_SOA_COLUMN(EventId, eventId, int); +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, int64_t); +DECLARE_SOA_COLUMN(CandId, candId, int); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); +DECLARE_SOA_COLUMN(MassD0, massD0, float); +DECLARE_SOA_COLUMN(MassD0bar, massD0bar, float); +DECLARE_SOA_COLUMN(MassDplus, massDplus, float); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(OmegaCharm, omegaCharm, float); +DECLARE_SOA_COLUMN(OmegaAntiCharm, omegaAntiCharm, float); +DECLARE_SOA_COLUMN(OmegaBkg, omegaBkg, float); +DECLARE_SOA_COLUMN(WCharm, wCharm, double); +DECLARE_SOA_COLUMN(WAntiCharm, wAntiCharm, double); +DECLARE_SOA_COLUMN(WBkg, wBkg, double); +} // namespace eyefluc + +DECLARE_SOA_TABLE(EyeFlucCharmD0Cands, "AOD", "EYEFCD0CAND", + eyefluc::EventId, + eyefluc::TimeStamp, + eyefluc::CandId, + eyefluc::Sign, + eyefluc::Pt, + eyefluc::Rapidity, + eyefluc::MassD0, + eyefluc::MassD0bar, + eyefluc::OmegaCharm, + eyefluc::OmegaAntiCharm, + eyefluc::OmegaBkg, + aod::hf_cand_mc_flag::FlagMcMatchRec, + aod::hf_cand_mc_flag::OriginMcRec); + +DECLARE_SOA_TABLE(EyeFlucCharmD0Events, "AOD", "EYEFCD0EVT", + eyefluc::EventId, + eyefluc::TimeStamp, + eyefluc::Centrality, + eyefluc::WCharm, + eyefluc::WAntiCharm, + eyefluc::WBkg); + +DECLARE_SOA_TABLE(EyeFlucCharmDplusCands, "AOD", "EYEFCDPCAND", + eyefluc::EventId, + eyefluc::TimeStamp, + eyefluc::CandId, + eyefluc::Sign, + eyefluc::Pt, + eyefluc::Rapidity, + eyefluc::MassDplus, + eyefluc::OmegaCharm, + eyefluc::OmegaAntiCharm, + eyefluc::OmegaBkg, + aod::hf_cand_mc_flag::FlagMcMatchRec, + aod::hf_cand_mc_flag::OriginMcRec); + +DECLARE_SOA_TABLE(EyeFlucCharmDplusEvents, "AOD", "EYEFCDPEVT", + eyefluc::EventId, + eyefluc::TimeStamp, + eyefluc::Centrality, + eyefluc::WCharm, + eyefluc::WAntiCharm, + eyefluc::WBkg); +} // namespace o2::aod + +enum EventQa : uint8_t { + All = 0, + RejHfEventSelection, + RejNoCharmCandidate, + CharmCandidateSelected, + EventWritten, + NEventQa +}; + +using CandD0Data = soa::Filtered>; +using CandD0McRec = soa::Filtered>; +using CandDplusData = soa::Filtered>; +using CandDplusMcRec = soa::Filtered>; +using CollData = soa::Join; + +struct HfTaskNetCharmFluctuations { + Produces outD0Cand; + Produces outD0Evt; + Produces outDplusCand; + Produces outDplusEvt; + + Configurable selectionFlagD0{"selectionFlagD0", 1, "Minimum D0 selection flag"}; + Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Minimum D0bar selection flag"}; + Configurable selectionFlagDplus{"selectionFlagDplus", 1, "Minimum Dplus selection flag"}; + Configurable centralityEstimator{"centralityEstimator", static_cast(CentralityEstimator::FT0C), "Centrality estimator used for output tables"}; + Configurable fillOmegaRaw{"fillOmegaRaw", true, "Fill omega sums with raw charm/anti-charm candidate counts"}; + + SliceCache cache; + HfEventSelection hfEvSel; + Service ccdb; + + Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlagDplus; + Partition selectedD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0; + Partition selectedD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + Partition selectedD0McToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0; + Partition selectedD0McToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + + HistogramRegistry registry{"registry"}; + + struct HfCandInfo { + int id = -1; + int8_t sign = 0; + float massD0 = -1.f; + float massD0bar = -1.f; + float massDplus = -1.f; + float pt = -1.f; + float rapidity = -999.f; + float omegaCharm = 0.f; + float omegaAntiCharm = 0.f; + float omegaBkg = 1.f; + int8_t flagMcMatchRec = -1; + int8_t originMcRec = -1; + }; + + void init(InitContext const&) + { + std::array processes = {doprocessD0, doprocessMcD0, doprocessDplus, doprocessMcDplus}; + const int nProcesses = std::accumulate(processes.begin(), processes.end(), 0); + if (nProcesses > 1) { + LOGP(fatal, "Only one process function should be enabled at a time, please check your configuration"); + } else if (nProcesses == 0) { + LOGP(fatal, "No process function enabled"); + } + + static constexpr std::array EventLabels = { + "All events", + "rejected by HF event selection", + "without charm candidates", + "with charm candidates", + "written events"}; + static constexpr double EventQaAxisMin = 0.5; + static constexpr double EventQaAxisMax = static_cast(EventQa::NEventQa) + EventQaAxisMin; + registry.add("hEventQa", "Event QA;;entries", {HistType::kTH1F, {{static_cast(EventQa::NEventQa), EventQaAxisMin, EventQaAxisMax}}}); + for (int iBin = 0; iBin < EventQa::NEventQa; ++iBin) { + registry.get(HIST("hEventQa"))->GetXaxis()->SetBinLabel(iBin + 1, EventLabels[iBin].data()); + } + registry.add("hMassVsPtD0", "D0 candidates;#it{M}_{#pi K} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 1.65, 2.15}, {200, 0., 50.}}}); + registry.add("hMassVsPtD0bar", "D0bar candidates;#it{M}_{K#pi} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 1.65, 2.15}, {200, 0., 50.}}}); + registry.add("hMassVsPtDplus", "Dplus candidates;#it{M}_{#pi K#pi} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{500, 1.65, 2.15}, {200, 0., 50.}}}); + registry.add("hCandidateCounter", "Candidate counter;candidate type;entries", {HistType::kTH1F, {{4, 0.5, 4.5}}}); + hfEvSel.init(registry); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } + + int64_t getTimeStamp(CollData::iterator const& collision) const + { + const auto bc = collision.template bc_as(); + return bc.timestamp(); + } + + float getCentrality(CollData::iterator const& collision) const + { + return getCentralityColl(collision, centralityEstimator.value); + } + + void setOmegaRaw(HfCandInfo& cand) const + { + if (!fillOmegaRaw.value) { + return; + } + cand.omegaBkg = 0.f; + if (cand.sign > 0) { + cand.omegaCharm = 1.f; + } else { + cand.omegaAntiCharm = 1.f; + } + } + + template + void setMcInfo(HfCandInfo& info, TCandidate const& cand) const + { + if constexpr (IsMc) { + info.flagMcMatchRec = cand.flagMcMatchRec(); + info.originMcRec = cand.originMcRec(); + } + } + + bool passEventSelection(CollData::iterator const& collision) + { + registry.fill(HIST("hEventQa"), 1 + EventQa::All); + float centrality = 0.f; + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + hfEvSel.fillHistograms(collision, rejectionMask, centrality); + if (rejectionMask != 0) { + registry.fill(HIST("hEventQa"), 1 + EventQa::RejHfEventSelection); + return false; + } + return true; + } + + void fillD0OutputTables(CollData::iterator const& collision, std::vector& acceptedCands) + { + const int eventId = collision.globalIndex(); + const int64_t timeStamp = getTimeStamp(collision); + const float centrality = getCentrality(collision); + + if (acceptedCands.empty()) { + outD0Evt(eventId, timeStamp, centrality, 0., 0., 0.); + registry.fill(HIST("hEventQa"), 1 + EventQa::RejNoCharmCandidate); + registry.fill(HIST("hEventQa"), 1 + EventQa::EventWritten); + return; + } + registry.fill(HIST("hEventQa"), 1 + EventQa::CharmCandidateSelected); + + double wCharm = 0.; + double wAntiCharm = 0.; + double wBkg = 0.; + + for (const auto& cand : acceptedCands) { + wCharm += cand.omegaCharm; + wAntiCharm += cand.omegaAntiCharm; + wBkg += cand.omegaBkg; + + outD0Cand(eventId, + timeStamp, + cand.id, + cand.sign, + cand.pt, + cand.rapidity, + cand.massD0, + cand.massD0bar, + cand.omegaCharm, + cand.omegaAntiCharm, + cand.omegaBkg, + cand.flagMcMatchRec, + cand.originMcRec); + } + + outD0Evt(eventId, timeStamp, centrality, wCharm, wAntiCharm, wBkg); + registry.fill(HIST("hEventQa"), 1 + EventQa::EventWritten); + } + + void fillDplusOutputTables(CollData::iterator const& collision, std::vector& acceptedCands) + { + const int eventId = collision.globalIndex(); + const int64_t timeStamp = getTimeStamp(collision); + const float centrality = getCentrality(collision); + + if (acceptedCands.empty()) { + outDplusEvt(eventId, timeStamp, centrality, 0., 0., 0.); + registry.fill(HIST("hEventQa"), 1 + EventQa::RejNoCharmCandidate); + registry.fill(HIST("hEventQa"), 1 + EventQa::EventWritten); + return; + } + registry.fill(HIST("hEventQa"), 1 + EventQa::CharmCandidateSelected); + + double wCharm = 0.; + double wAntiCharm = 0.; + double wBkg = 0.; + + for (const auto& cand : acceptedCands) { + wCharm += cand.omegaCharm; + wAntiCharm += cand.omegaAntiCharm; + wBkg += cand.omegaBkg; + + outDplusCand(eventId, + timeStamp, + cand.id, + cand.sign, + cand.pt, + cand.rapidity, + cand.massDplus, + cand.omegaCharm, + cand.omegaAntiCharm, + cand.omegaBkg, + cand.flagMcMatchRec, + cand.originMcRec); + } + + outDplusEvt(eventId, timeStamp, centrality, wCharm, wAntiCharm, wBkg); + registry.fill(HIST("hEventQa"), 1 + EventQa::EventWritten); + } + + template + void addD0Candidates(TCandidates const& candidates, std::vector& acceptedCands) + { + for (const auto& cand : candidates) { + const float massD0 = HfHelper::invMassD0ToPiK(cand); + const float massD0bar = HfHelper::invMassD0barToKPi(cand); + + HfCandInfo info; + info.id = cand.globalIndex(); + info.sign = Sign; + info.massD0 = massD0; + info.massD0bar = massD0bar; + info.pt = cand.pt(); + info.rapidity = HfHelper::yD0(cand); + setMcInfo(info, cand); + setOmegaRaw(info); + acceptedCands.push_back(info); + + if constexpr (Sign > 0) { + registry.fill(HIST("hMassVsPtD0"), massD0, cand.pt()); + registry.fill(HIST("hCandidateCounter"), 1.f); + } else { + registry.fill(HIST("hMassVsPtD0bar"), massD0bar, cand.pt()); + registry.fill(HIST("hCandidateCounter"), 2.f); + } + } + } + + void processD0(CollData::iterator const& collision, + aod::BCsWithTimestamps const&, + CandD0Data const&) + { + if (!passEventSelection(collision)) { + return; + } + + auto candsD0ToPiK = selectedD0ToPiK->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + auto candsD0ToKPi = selectedD0ToKPi->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + + std::vector acceptedCands; + addD0Candidates<+1>(candsD0ToPiK, acceptedCands); + addD0Candidates<-1>(candsD0ToKPi, acceptedCands); + fillD0OutputTables(collision, acceptedCands); + } + PROCESS_SWITCH(HfTaskNetCharmFluctuations, processD0, "Process D0 candidates", true); + + void processMcD0(CollData::iterator const& collision, + aod::BCsWithTimestamps const&, + CandD0McRec const&) + { + if (!passEventSelection(collision)) { + return; + } + + auto candsD0ToPiK = selectedD0McToPiK->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + auto candsD0ToKPi = selectedD0McToKPi->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + + std::vector acceptedCands; + addD0Candidates<+1, true>(candsD0ToPiK, acceptedCands); + addD0Candidates<-1, true>(candsD0ToKPi, acceptedCands); + fillD0OutputTables(collision, acceptedCands); + } + PROCESS_SWITCH(HfTaskNetCharmFluctuations, processMcD0, "Process MC D0 candidates", false); + + template + void runDplus(CollData::iterator const& collision, TCandidates const& candidatesDplus) + { + if (!passEventSelection(collision)) { + return; + } + + std::vector acceptedCands; + for (const auto& cand : candidatesDplus) { + const auto trackProng0 = cand.template prong0_as(); + const int8_t sign = trackProng0.sign() > 0 ? +1 : -1; + const float massDplus = HfHelper::invMassDplusToPiKPi(cand); + + HfCandInfo info; + info.id = cand.globalIndex(); + info.sign = sign; + info.massDplus = massDplus; + info.pt = cand.pt(); + info.rapidity = HfHelper::yDplus(cand); + setMcInfo(info, cand); + setOmegaRaw(info); + acceptedCands.push_back(info); + registry.fill(HIST("hMassVsPtDplus"), massDplus, cand.pt()); + registry.fill(HIST("hCandidateCounter"), sign > 0 ? 3.f : 4.f); + } + + fillDplusOutputTables(collision, acceptedCands); + } + + void processDplus(CollData::iterator const& collision, + aod::BCsWithTimestamps const&, + CandDplusData const& candidatesDplus, + aod::Tracks const&) + { + runDplus(collision, candidatesDplus); + } + PROCESS_SWITCH(HfTaskNetCharmFluctuations, processDplus, "Process Dplus candidates", false); + + void processMcDplus(CollData::iterator const& collision, + aod::BCsWithTimestamps const&, + CandDplusMcRec const& candidatesDplus, + aod::Tracks const&) + { + runDplus(collision, candidatesDplus); + } + PROCESS_SWITCH(HfTaskNetCharmFluctuations, processMcDplus, "Process MC Dplus candidates", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx b/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..90ebbb0d312 --- /dev/null +++ b/PWGHF/D2H/Tasks/taskOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,557 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskOmegac0ToOmegaPiQa.cxx +/// \brief OmegaC0 analysis task +/// \author Yunfan Liu , China University of Geosciences +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannelsLegacy.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +/// Omegac0 analysis task +struct HfTaskOmegac0ToOmegaPiQa { + + Configurable selectionFlagOmegac0{"selectionFlagOmegac0", true, "Select Omegac0 candidates"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "Max. gen particle rapidity"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "Max. cand. rapidity"}; + + SliceCache cache; + + using Omegac0Cands = soa::Filtered>; + using Omegac0CandsKF = soa::Filtered>; + using Omegac0CandsMc = soa::Filtered>; + using Omegac0CandsMcKF = soa::Filtered>; + using Omegac0CandsMl = soa::Filtered>; + using Omegac0CandsMlKF = soa::Filtered>; + using Omegac0CandsMlMc = soa::Filtered>; + using Omegac0CandsMlMcKF = soa::Filtered>; + + using Omegac0Gen = soa::Filtered>; + + using Collisions = soa::Join; + using CollisionsWithFT0C = soa::Join; + using CollisionsWithFT0M = soa::Join; + using CollisionsWithMcLabels = soa::Join; + + using McCollisionsWithFT0M = soa::Join; + + Filter filterOmegaCToOmegaPiFlag = (aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi))) != static_cast(0); + Filter filterOmegaCMatchedRec = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::OmegaczeroToOmegaPi)); + Filter filterOmegaCMatchedGen = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::OmegaczeroToOmegaPi)); + + Preslice candOmegacPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacKFPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacMlPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + Preslice candOmegacKFMlPerCollision = aod::hf_cand_xic0_omegac0::collisionId; + + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + + ConfigurableAxis thnConfigAxisMass{"thnConfigAxisMass", {700, 2.4, 3.1}, "Cand. inv. mass"}; + ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {500, 0, 50}, "Cand. pT"}; + ConfigurableAxis thnConfigAxisPtB{"thnConfigAxisPtB", {500, 0, 50}, "Cand. beauty mother pT"}; + ConfigurableAxis thnConfigAxisY{"thnConfigAxisY", {20, -1, 1}, "Cand. rapidity"}; + ConfigurableAxis thnConfigAxisOrigin{"thnConfigAxisOrigin", {3, -0.5, 2.5}, "Cand. origin"}; + ConfigurableAxis thnConfigAxisMatchFlag{"thnConfigAxisMatchFlag", {15, -7.5, 7.5}, "Cand. MC match flag"}; + ConfigurableAxis thnConfigAxisNumPvContr{"thnConfigAxisNumPvContr", {200, -0.5, 199.5}, "Coll. num. PV contributors"}; + ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {100, 0, 100}, "Coll. centrality precentile"}; + ConfigurableAxis thnConfigAxisPromptScore{"thnConfigAxisPromptScore", {100, 0, 1}, "Prompt score"}; + HistogramRegistry registry{"registry", {}}; + + void init(InitContext&) + { + std::array doprocess{doprocessDataDCAFitter, doprocessDataDCAFitterMl, doprocessDataDCAFitterFT0C, doprocessDataDCAFitterMlFT0C, + doprocessDataDCAFitterFT0M, doprocessDataDCAFitterMlFT0M, + doprocessDataKFParticle, doprocessDataKFParticleMl, doprocessDataKFParticleFT0C, doprocessDataKFParticleMlFT0C, + doprocessDataKFParticleFT0M, doprocessDataKFParticleMlFT0M, + doprocessMcDCAFitter, doprocessMcDCAFitterMl, + doprocessMcDCAFitterFT0M, doprocessMcDCAFitterMlFT0M, + doprocessMcKFParticle, doprocessMcKFParticleMl, + doprocessMcKFParticleFT0M, doprocessMcKFParticleMlFT0M}; + if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { + LOGP(fatal, "One and only one process function should be enabled at a time."); + } + + const AxisSpec thnAxisMass{thnConfigAxisMass, "Inv. mass (#Omega#pi) (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPt{thnConfigAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisPtB{thnConfigAxisPtB, "#it{p}_{T}^{B} (GeV/#it{c})"}; + const AxisSpec thnAxisY{thnConfigAxisY, "y"}; + const AxisSpec thnAxisOrigin{thnConfigAxisOrigin, "Origin"}; + const AxisSpec thnAxisMatchFlag{thnConfigAxisMatchFlag, "MC match flag"}; + const AxisSpec thnAxisNumPvContr{thnConfigAxisNumPvContr, "Number of primary vtx. contributors"}; + const AxisSpec thnAxisCent{thnConfigAxisCent, "Centrality percentile"}; + const AxisSpec thnAxisCentMc{thnConfigAxisCent, "Centrality percentile (from gen. MC info)"}; + const AxisSpec thnAxisPromptScore{thnConfigAxisPromptScore, "BDT score prompt"}; + + std::vector axes = {thnAxisMass, thnAxisPt, thnAxisY}; + std::vector axesMcGen = {thnAxisPt, thnAxisPtB, thnAxisY, thnAxisOrigin}; + + if (doprocessDataDCAFitterFT0C || doprocessDataDCAFitterMlFT0C || doprocessDataDCAFitterFT0M || doprocessDataDCAFitterMlFT0M || doprocessDataKFParticleFT0C || doprocessDataKFParticleMlFT0C || doprocessDataKFParticleFT0M || doprocessDataKFParticleMlFT0M) { + axes.push_back(thnAxisCent); + axes.emplace_back(thnConfigAxisNumPvContr); + } + + if (doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + axes.push_back(thnAxisCentMc); + axes.emplace_back(thnConfigAxisNumPvContr); + axesMcGen.push_back(thnAxisCentMc); + axesMcGen.emplace_back(thnConfigAxisNumPvContr); + } + + if (doprocessMcDCAFitter || doprocessMcDCAFitterMl || doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticle || doprocessMcKFParticleMl || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + registry.add("hMcGen", "Gen. #Omega_{c}^{0} from charm and beauty", HistType::kTHnSparseD, axesMcGen); + registry.get(HIST("hMcGen"))->Sumw2(); + + if (doprocessMcDCAFitterFT0M || doprocessMcDCAFitterMlFT0M || doprocessMcKFParticleFT0M || doprocessMcKFParticleMlFT0M) { + registry.add("hMcGenWithRecoColl", "Gen. #Omega_{c}^{0} from charm and beauty (associated to a reco collision)", HistType::kTHnSparseD, axesMcGen); + registry.add("hNumRecoCollPerMcColl", "Number of reco collisions associated to a mc collision;Num. reco. coll. per Mc coll.;", {HistType::kTH1D, {{10, -0.5, 9.5}}}); + registry.get(HIST("hMcGenWithRecoColl"))->Sumw2(); + } + + axes.push_back(thnAxisPtB); + axes.push_back(thnAxisOrigin); + axes.push_back(thnAxisMatchFlag); + } + + if (doprocessDataKFParticleMl || doprocessDataKFParticleMlFT0C || doprocessDataKFParticleMlFT0M || doprocessDataDCAFitterMl || doprocessDataDCAFitterMlFT0C || doprocessDataDCAFitterMlFT0M || doprocessMcKFParticleMl || doprocessMcKFParticleMlFT0M || doprocessMcDCAFitterMl || doprocessMcDCAFitterMlFT0M) { + axes.push_back(thnAxisPromptScore); + } + + registry.add("hReco", "Reco. #Omega_{c}^{0} candidates", HistType::kTHnSparseD, axes); + registry.get(HIST("hReco"))->Sumw2(); + } + + template + void processData(const CandType& candidates) + { + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac); + } + } + } + + template + void processDataCent(const CandType& candidate, CollType const& collision) + { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + return; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + return; + } + + auto numPvContributors = collision.numContrib(); + if constexpr (UseCentrality) { + float const cent = o2::hf_centrality::getCentralityColl(collision); + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, + cent, numPvContributors, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, + cent, numPvContributors); + } + } else { + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac); + } + } + } + + template + void processMc(const CandType& candidates, Omegac0Gen const& mcParticles) + { + // MC rec. + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec(), candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec()); + } + } + + // MC gen. + for (const auto& particle : mcParticles) { + if (yCandGenMax >= 0. && std::abs(particle.rapidityCharmBaryonGen()) > yCandGenMax) { + continue; + } + + auto ptGen = particle.pt(); + auto yGen = particle.rapidityCharmBaryonGen(); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGen"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGen"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt); + } + } + } + + template + void processMcCent(const CandType& candidates, Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, McCollisionWithCents const&) + { + // MC rec. + for (const auto& candidate : candidates) { + if (!(candidate.resultSelections() == true || (candidate.resultSelections() == false && !selectionFlagOmegac0))) { + continue; + } + + double yOmegac{0.}; + if constexpr (UseKfParticle) { + yOmegac = candidate.kfRapOmegac(); + } else { + yOmegac = RecoDecay::y(std::array{candidate.pxCharmBaryon(), candidate.pyCharmBaryon(), candidate.pzCharmBaryon()}, o2::constants::physics::MassOmegaC0); + } + + if (yCandRecoMax >= 0. && std::abs(yOmegac) > yCandRecoMax) { + continue; + } + + auto collision = candidate.template collision_as(); + uint16_t const numPvContributors = collision.numContrib(); + float const mcCent = o2::hf_centrality::getCentralityColl(collision.template mcCollision_as()); + + if constexpr (ApplyMl) { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, mcCent, numPvContributors, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec(), candidate.mlProbOmegac()[0]); + } else { + registry.fill(HIST("hReco"), candidate.invMassCharmBaryon(), candidate.ptCharmBaryon(), yOmegac, mcCent, numPvContributors, candidate.ptBhadMotherPart(), candidate.originMcRec(), candidate.flagMcMatchRec()); + } + } + + // MC gen. + for (const auto& particle : mcParticles) { + if (yCandGenMax >= 0. && std::abs(particle.rapidityCharmBaryonGen()) > yCandGenMax) { + continue; + } + + auto ptGen = particle.pt(); + auto yGen = particle.rapidityCharmBaryonGen(); + auto mcCollision = particle.template mcCollision_as(); + + int maxNumContrib = 0; + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, mcCollision.globalIndex()); + for (const auto& recCol : recoCollsPerMcColl) { + maxNumContrib = recCol.numContrib() > maxNumContrib ? recCol.numContrib() : maxNumContrib; + } + + float const mcCent = o2::hf_centrality::getCentralityColl(mcCollision); + + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGen"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt, mcCent, maxNumContrib); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGen"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt, mcCent, maxNumContrib); + } + + registry.fill(HIST("hNumRecoCollPerMcColl"), recoCollsPerMcColl.size()); + + // fill sparse only for gen particles associated to a reconstructed collision + if (recoCollsPerMcColl.size() >= 1) { + if (particle.originMcGen() == RecoDecay::OriginType::Prompt) { + registry.fill(HIST("hMcGenWithRecoColl"), ptGen, -1., yGen, RecoDecay::OriginType::Prompt, mcCent, maxNumContrib); + } else { + float const ptGenB = mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt(); + registry.fill(HIST("hMcGenWithRecoColl"), ptGen, ptGenB, yGen, RecoDecay::OriginType::NonPrompt, mcCent, maxNumContrib); + } + } + } + } + + /////////////////////////////////// + /// Data with DCAFitter // + /////////////////////////////////// + + void processDataDCAFitter(Omegac0Cands const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitter, "process data with DCAFitter", false); + + void processDataDCAFitterMl(Omegac0CandsMl const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMl, "process data with DCAFitter, ML selections", false); + + void processDataDCAFitterFT0C(Omegac0Cands const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterFT0C, "process data with DCAFitter, FT0C centrality", false); + + void processDataDCAFitterMlFT0C(Omegac0CandsMl const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMlFT0C, "process data with DCAFitter, ML selections, FT0C centrality", false); + + void processDataDCAFitterFT0M(Omegac0Cands const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterFT0M, "process data with DCAFitter, FT0M centrality", false); + + void processDataDCAFitterMlFT0M(Omegac0CandsMl const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataDCAFitterMlFT0M, "process data with DCAFitter, ML selections, FT0M centrality", false); + + //////////////////////////////////// + /// Data with KFParticle // + //////////////////////////////////// + + void processDataKFParticle(Omegac0CandsKF const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticle, "process data with KFParticle", false); + + void processDataKFParticleMl(Omegac0CandsMlKF const& candidates) + { + processData(candidates); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMl, "process data with KFParticle, ML selections", false); + + void processDataKFParticleFT0C(Omegac0CandsKF const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleFT0C, "process data with KFParticle, FT0C centrality", false); + + void processDataKFParticleMlFT0C(Omegac0CandsMlKF const& candidates, + CollisionsWithFT0C const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMlFT0C, "process data with KFParticle, ML selections, FT0C centrality", false); + + void processDataKFParticleFT0M(Omegac0CandsKF const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleFT0M, "process data with KFParticle, FT0M centrality", false); + + void processDataKFParticleMlFT0M(Omegac0CandsMlKF const& candidates, + CollisionsWithFT0M const& collisions) + { + for (const auto& collision : collisions) { + auto groupedOmegacCandidates = candidates.sliceBy(candOmegacKFMlPerCollision, collision.globalIndex()); + + for (const auto& candidate : groupedOmegacCandidates) { + processDataCent(candidate, collision); + } + } + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processDataKFParticleMlFT0M, "process data with KFParticle, ML selections, FT0M centrality", false); + + /////////////////////////////////// + /// MC with DCAFitter // + /////////////////////////////////// + + void processMcDCAFitter(Omegac0CandsMc const& omegaC0CandidatesMc, + Omegac0Gen const& mcParticles) + { + processMc(omegaC0CandidatesMc, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitter, "Process MC with DCAFitter", false); + + void processMcDCAFitterMl(Omegac0CandsMlMc const& omegac0CandidatesMlMc, + Omegac0Gen const& mcParticles) + { + processMc(omegac0CandidatesMlMc, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterMl, "Process MC with DCAFitter, ML selections", false); + + void processMcDCAFitterFT0M(Omegac0CandsMc const& omegaC0CandidatesMc, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegaC0CandidatesMc, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterFT0M, "Process MC with DCAFitter, FT0M centrality (from MC)", false); + + void processMcDCAFitterMlFT0M(Omegac0CandsMlMc const& omegac0CandidatesMlMc, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegac0CandidatesMlMc, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcDCAFitterMlFT0M, "Process MC with DCAFitter, ML selections, FT0M centrality (from MC)", false); + + //////////////////////////////////// + /// MC with KFParticle // + //////////////////////////////////// + + void processMcKFParticle(Omegac0CandsMcKF const& omegaC0CandidatesMcKF, + Omegac0Gen const& mcParticles) + { + processMc(omegaC0CandidatesMcKF, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticle, "Process MC with KFParticle", false); + + void processMcKFParticleMl(Omegac0CandsMlMcKF const& omegac0CandidatesMlMcKF, + Omegac0Gen const& mcParticles) + { + processMc(omegac0CandidatesMlMcKF, mcParticles); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleMl, "Process MC with KFParticle, ML selections", false); + + void processMcKFParticleFT0M(Omegac0CandsMcKF const& omegaC0CandidatesMcKF, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegaC0CandidatesMcKF, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleFT0M, "Process MC with KFParticle, FT0M centrality (from MC)", false); + + void processMcKFParticleMlFT0M(Omegac0CandsMlMcKF const& omegac0CandidatesMlMcKF, + Omegac0Gen const& mcParticles, + CollisionsWithMcLabels const& collisions, + McCollisionsWithFT0M const& mcCollisions) + { + processMcCent(omegac0CandidatesMlMcKF, mcParticles, collisions, mcCollisions); + } + PROCESS_SWITCH(HfTaskOmegac0ToOmegaPiQa, processMcKFParticleMlFT0M, "Process MC with KFParticle, ML selections, FT0M centrality (from MC)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/taskPtFlucCharmHadrons.cxx b/PWGHF/D2H/Tasks/taskPtFlucCharmHadrons.cxx index d0695616c84..e94dcab7a87 100644 --- a/PWGHF/D2H/Tasks/taskPtFlucCharmHadrons.cxx +++ b/PWGHF/D2H/Tasks/taskPtFlucCharmHadrons.cxx @@ -13,6 +13,8 @@ /// \brief Analysis task for charm hadron pt fluctuation /// /// \author Prottay Das, prottay.das@cern.ch +/// \author Wu Chuntai, UNIPD, CCNU, and INFN Padova +/// \author Andrea Rossi, INFN Padova #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/HfHelper.h" @@ -22,6 +24,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -37,6 +40,7 @@ #include #include +#include #include #include #include @@ -57,6 +61,12 @@ enum DecayChannel { DplusToPiKPi = 0, D0ToPiK, D0ToKPi }; +enum CandD0Type { PureD0 = 0, + PureD0bar, + ReflectedD0, + ReflectedD0bar +}; + struct HfTaskPtFlucCharmHadrons { Configurable centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"}; Configurable selectionFlag{"selectionFlag", 1, "Selection Flag for hadron (e.g. 1 for skimming, 3 for topo. and kine., 7 for PID)"}; @@ -64,6 +74,7 @@ struct HfTaskPtFlucCharmHadrons { Configurable centralityMax{"centralityMax", 100., "Maximum centrality accepted in SP computation"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable> classMl{"classMl", {0, 2}, "Indices of BDT scores to be stored. Two indexes max."}; + Configurable saveCharmBulkCorrelations{"saveCharmBulkCorrelations", false, "Flag to save charm-bulk correlations in THnSparse"}; Configurable cfgITScluster{"cfgITScluster", 5, "Number of ITS cluster"}; Configurable cfgITSbarrel{"cfgITSbarrel", 1, "Number of ITS inner barrel"}; @@ -77,11 +88,12 @@ struct HfTaskPtFlucCharmHadrons { Configurable etaAMax{"etaAMax", 0.0f, "A: negative eta max (0)"}; Configurable etaBMin{"etaBMin", 0.4f, "B: positive eta min (eta_min)"}; // or set from etaMinGap in code Configurable etaBMax{"etaBMax", 0.8f, "B: positive eta max"}; + Configurable etaTrkMax{"etaTrkMax", 0.8f, "Track eta max"}; Configurable ptTrkMin{"ptTrkMin", 0.2f, "Track pT min for (charged hadrons)"}; - Configurable ptTrkMax{"ptTrkMax", 2.0f, "Track pT max for (charged hadrons)"}; - Configurable ptTrkMinD{"ptTrkMinD", 0.2f, "Track pT min for D"}; - Configurable ptTrkMaxD{"ptTrkMaxD", 2.0f, "Track pT max for D"}; + Configurable ptTrkMax{"ptTrkMax", 5.0f, "Track pT max for (charged hadrons)"}; + Configurable ptCandMin{"ptCandMin", 0.2f, "Min pT for D candidates"}; + Configurable ptCandMax{"ptCandMax", 12.0f, "Max pT for D candidates"}; Configurable minNTrk{"minNTrk", 5, "Min charged tracks in each subevent to compute "}; // Use ML in denominator AND numerator consistently @@ -102,12 +114,12 @@ struct HfTaskPtFlucCharmHadrons { using CandDplusDataWMl = soa::Filtered>; using CandD0DataWMl = soa::Filtered>; - using Colls = soa::Join; + using Colls = soa::Join; using TracksWithExtra = soa::Join; Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; - + Filter filterSelectTracks = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); Partition selectedD0ToPiKWMl = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; Partition selectedD0ToKPiWMl = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; @@ -117,9 +129,16 @@ struct HfTaskPtFlucCharmHadrons { ConfigurableAxis axisInvMass{"axisInvMass", {100, 1.78, 2.05}, "Inv mass axis"}; ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.5, 8.0, 10.0}, "Candidate pT axis"}; ConfigurableAxis axisCent{"axisCent", {VARIABLE_WIDTH, 0.0, 10.0, 40.0, 80.0}, "Centrality axis"}; - ConfigurableAxis axisSign{"axisSign", {VARIABLE_WIDTH, -1.0, 1.0}, "Sign axis"}; - ConfigurableAxis thnConfigAxisMlOne{"thnConfigAxisMlOne", {1000, 0., 1.}, ""}; - ConfigurableAxis thnConfigAxisMlTwo{"thnConfigAxisMlTwo", {1000, 0., 1.}, ""}; + ConfigurableAxis axisSign{"axisSign", {VARIABLE_WIDTH, -1.0, 4.0}, "Sign axis"}; + ConfigurableAxis axisMlOne{"axisMlOne", {1000, 0., 1.}, "ML score 1 axis"}; + ConfigurableAxis axisMlTwo{"axisMlTwo", {100, 0., 1.}, "ML score 2 axis"}; + ConfigurableAxis axisCandEta{"axisCandEta", {16, -0.8, 0.8}, "Candidate eta axis"}; + ConfigurableAxis axisMPtTrkA{"axisMPtTrkA", {150, 0., 1.5}, "Mean pT of tracks in subevent A"}; + ConfigurableAxis axisMPtTrkB{"axisMPtTrkB", {150, 0., 1.5}, "Mean pT of tracks in subevent B"}; + ConfigurableAxis axisPtCandProduct{"axisPtCandProduct", {150, 0., 15}, "Candidate pT * mean pT of tracks (A and B) product"}; + ConfigurableAxis axisPtTrkProduct{"axisPtTrkProduct", {200, 0., 2.}, "Mean pT of tracks in A * Mean pT of tracks in B"}; + ConfigurableAxis axisNTrkA{"axisNTrkA", {2000, 0., 2000.}, "N_{tracks} in subevent A"}; + ConfigurableAxis axisNTrkB{"axisNTrkB", {2000, 0., 2000.}, "N_{tracks} in subevent B"}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -139,8 +158,15 @@ struct HfTaskPtFlucCharmHadrons { const AxisSpec aPt{axisPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec aCent{axisCent, "Centrality"}; const AxisSpec aSign{axisSign, "Sign"}; - const AxisSpec thnAxisMlOne{thnConfigAxisMlOne, "Bkg score"}; - const AxisSpec thnAxisMlTwo{thnConfigAxisMlTwo, "FD score"}; + const AxisSpec aMlOne{axisMlOne, "Bkg score"}; + const AxisSpec aMlTwo{axisMlTwo, "FD score"}; + const AxisSpec aCandEta{axisCandEta, "Eta"}; + const AxisSpec aMPtTrkA{axisMPtTrkA, "Mean pT of tracks in subevent A (GeV/#it{c})"}; + const AxisSpec aMPtTrkB{axisMPtTrkB, "Mean pT of tracks in subevent B (GeV/#it{c})"}; + const AxisSpec aPtCandProduct{axisPtCandProduct, "#it{p}_{T}^{cand} * <#it{p}_{T}>^{trks} (GeV^{2}/#it{c}^{2})"}; + const AxisSpec aPtTrkProduct{axisPtTrkProduct, "#it{p}_{T}^{trks}(A) * #it{p}_{T}^{trks}(B) (GeV^{2}/#it{c}^{2})"}; + const AxisSpec aNTrkA{axisNTrkA, "N_{tracks} in subevent A"}; + const AxisSpec aNTrkB{axisNTrkB, "N_{tracks} in subevent B"}; // Event-level accumulators (charged hadrons only!) registry.add("hEvents", "events vs cent", HistType::kTH1F, {aCent}, true); @@ -155,53 +181,104 @@ struct HfTaskPtFlucCharmHadrons { registry.add("pNB", " vs cent (charged hadrons)", HistType::kTProfile, {aCent}, true); // Candidate mass distributions - registry.add("hD_mass", "D candidate mass (weight=1)", HistType::kTHnSparseF, {aInvMass, aCent, aPt, aSign, thnAxisMlOne, thnAxisMlTwo}, true); - registry.add("hD_f", "hD_f", HistType::kTHnSparseF, {aInvMass, aCent, aPt, aSign, thnAxisMlOne, thnAxisMlTwo}, true); - registry.add("hD_f_wPtB", "hD_f_wPtB", HistType::kTHnSparseF, {aInvMass, aCent, aPt, aSign, thnAxisMlOne, thnAxisMlTwo}, true); + registry.add("hD_mass", "D candidate mass (weight=1)", HistType::kTHnSparseF, {aInvMass, aCent, aPt, aSign, aMlOne, aMlTwo}, true); + registry.add("hD_f", "hD_f", HistType::kTHnSparseF, {aInvMass, aCent, aPt, aSign, aMlOne, aMlTwo}, true); + registry.add("hD_f_wPtB", "hD_f_wPtB", HistType::kTHnSparseF, {aInvMass, aCent, aPt, aSign, aMlOne, aMlTwo}, true); + + // charm-bulk correlations (optional) + if (saveCharmBulkCorrelations) { + registry.add("hCharmBulkCorrelations", "Charm-bulk correlations", HistType::kTHnSparseF, {aInvMass, aCent, aPt, aSign, aMlOne, aMlTwo, aCandEta, aMPtTrkA, aMPtTrkB, aPtCandProduct, aNTrkA, aNTrkB}, true); + registry.add("hMeanPtTrkAllColls", "Mean pT of charged hadrons for all collisions", HistType::kTHnSparseF, {aCent, aMPtTrkA, aMPtTrkB, aPtTrkProduct, aNTrkA, aNTrkB}, true); + } + + hfEvSel.addHistograms(registry); // collision monitoring ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); }; // end init - /// Get the centrality - /// \param collision is the collision with the centrality information - double getCentrality(Colls::iterator const& collision) + // --------------------------------------------------------------------------- + // Helper functions + // --------------------------------------------------------------------------- + /// Check event selections for collision and fill event selection histograms, and revalculate centrality + /// \param collision is the collision + template + bool isSelectedHfCollision(Coll const& collision, float& cent) + { + o2::hf_evsel::HfCollisionRejectionMask collRejMask{}; + if (centEstimator == CentralityEstimator::FT0A) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + } else if (centEstimator == CentralityEstimator::FT0C) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + } else if (centEstimator == CentralityEstimator::FT0M) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + } else if (centEstimator == CentralityEstimator::FV0A) { + collRejMask = hfEvSel.getHfCollisionRejectionMask(collision, cent, ccdb, registry); + } else { + LOG(fatal) << "Centrality estimator not recognized for collision selection"; + } + hfEvSel.fillHistograms(collision, collRejMask, cent); + return collRejMask == 0; + } + + /// Get candidate mass + template + std::pair getCandMassAndSign(const CandT& cand) { - double cent = -999.; - switch (centEstimator) { - case CentralityEstimator::FV0A: - cent = collision.centFV0A(); - break; - case CentralityEstimator::FT0M: - cent = collision.centFT0M(); - break; - case CentralityEstimator::FT0A: - cent = collision.centFT0A(); - break; - case CentralityEstimator::FT0C: - cent = collision.centFT0C(); - break; - default: - LOG(warning) << "Centrality estimator not valid. Possible values are V0A, T0M, T0A, T0C. Fallback to V0A"; - cent = collision.centFV0A(); - break; + if constexpr (std::is_same_v) { + return {HfHelper::invMassDplusToPiKPi(cand), cand.template prong0_as().sign()}; } - return cent; + if constexpr (std::is_same_v) { + if (channel == DecayChannel::D0ToPiK) { + return {HfHelper::invMassD0ToPiK(cand), cand.isSelD0bar() ? CandD0Type::ReflectedD0bar : CandD0Type::PureD0}; + } + if (channel == DecayChannel::D0ToKPi) { + return {HfHelper::invMassD0barToKPi(cand), cand.isSelD0() ? CandD0Type::ReflectedD0 : CandD0Type::PureD0bar}; + } + } + return {0., 0.}; // default return value for unsupported types } template - bool selectionTrack(const T& candidate) const + bool selectionTrack(const T& track) const { - if (!(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > cfgITScluster.value && candidate.tpcNClsFound() > cfgTPCcluster.value && candidate.itsNClsInnerBarrel() >= cfgITSbarrel.value && std::abs(candidate.dcaXY()) < cfgCutDCAxy.value && std::abs(candidate.dcaZ()) < cfgCutDCAz.value)) { + if (!(track.isGlobalTrack() && track.isPVContributor() && track.itsNCls() > cfgITScluster.value && track.tpcNClsFound() > cfgTPCcluster.value && track.itsNClsInnerBarrel() >= cfgITSbarrel.value)) { return false; } return true; } - // ------------------------- + /// remove candidate daughters from the mean pT of tracks in A and B (if they are in the respective subevent) + template + std::pair removeDaughtersFromMeanPt(const CandT& cand, float rawMeanPt, int n, const std::vector& trkIDs) + { + int removedCount = 0; + float removedSumPt = 0.f; + auto removeDaug = [&](int daugID, float daugPt) { + if (std::binary_search(trkIDs.begin(), trkIDs.end(), daugID)) { + removedSumPt += daugPt; + ++removedCount; + } + }; + if constexpr (Channel == DecayChannel::DplusToPiKPi) { + removeDaug(cand.prong0Id(), cand.ptProng0()); + removeDaug(cand.prong1Id(), cand.ptProng1()); + removeDaug(cand.prong2Id(), cand.ptProng2()); + } else if constexpr (Channel == DecayChannel::D0ToPiK || Channel == DecayChannel::D0ToKPi) { + removeDaug(cand.prong0Id(), cand.ptProng0()); + removeDaug(cand.prong1Id(), cand.ptProng1()); + } + if (removedCount > 0) { + double totalSum = static_cast(rawMeanPt) * n; + return {static_cast((totalSum - removedSumPt) / (n - removedCount)), n - removedCount}; + } + return {rawMeanPt, n}; + } + + // --------------------------------------------------------------------------- // Event-wise mean pT from charged hadron tracks - // ------------------------- + // --------------------------------------------------------------------------- template std::pair computeMeanPtCharged(Trk const& tracks, float etaMin, float etaMax) const { @@ -217,9 +294,6 @@ struct HfTaskPtFlucCharmHadrons { continue; } float pt = trk.pt(); - if (pt < ptTrkMin.value || pt >= ptTrkMax.value) { - continue; - } sumPt += pt; ++n; } @@ -277,109 +351,153 @@ struct HfTaskPtFlucCharmHadrons { ml2 >= mlTwoMin.value && ml2 < mlTwoMax.value); } - /// Compute the scalar product - /// \param collision is the collision with the Q vector information and event plane - /// \param candidates are the selected candidates + /// Compute the mean pT of the event using charged tracks in two eta-separated subevents, and correlate with D candidates + /// \param collision is the collision with centrality and event selection + /// \param candidates are the D candidates to correlate with the mean pT of the event + /// \param tracks are the tracks used to compute the mean pT of the event template void runPtFlucAnalysis(Colls::iterator const& collision, T1 const& candidates, Trk const& tracks) { - float cent = getCentrality(collision); - if (cent < centralityMin || cent > centralityMax) { + float cent{0.f}; + if (!isSelectedHfCollision(collision, cent)) { return; } // Compute in two eta-separated subevents using CHARGED tracks only - auto [meanPtA, nA] = computeMeanPtCharged(tracks, etaAMin.value, etaAMax.value); - auto [meanPtB, nB] = computeMeanPtCharged(tracks, etaBMin.value, etaBMax.value); + auto [RawMeanPtA, nA] = computeMeanPtCharged(tracks, etaAMin.value, etaAMax.value); + auto [RawMeanPtB, nB] = computeMeanPtCharged(tracks, etaBMin.value, etaBMax.value); // QA registry.fill(HIST("pNA"), cent, static_cast(nA)); registry.fill(HIST("pNB"), cent, static_cast(nB)); - if (!std::isfinite(meanPtA) || !std::isfinite(meanPtB)) { + if (!std::isfinite(RawMeanPtA) || !std::isfinite(RawMeanPtB)) { return; } registry.fill(HIST("hEvents"), cent, 1.f); - registry.fill(HIST("pMeanPtA"), cent, meanPtA); - registry.fill(HIST("pMeanPtB"), cent, meanPtB); - registry.fill(HIST("pMeanPtAmeanPtB"), cent, meanPtA * meanPtB); - - int nDcandTotA = 0; + registry.fill(HIST("pMeanPtA"), cent, RawMeanPtA); + registry.fill(HIST("pMeanPtB"), cent, RawMeanPtB); + registry.fill(HIST("pMeanPtAmeanPtB"), cent, RawMeanPtA * RawMeanPtB); + if (saveCharmBulkCorrelations) { + registry.fill(HIST("hMeanPtTrkAllColls"), cent, RawMeanPtA, RawMeanPtB, RawMeanPtA * RawMeanPtB, nA, nB); - for (const auto& cand : candidates) { - if (!passCandInA(cand)) { - continue; - } + std::vector trkIDA; + std::vector trkIDB; - // compute ML exactly like numerator - const auto [ml1, ml2] = getMlScores(cand); + trkIDA.reserve(tracks.size() / 2); + trkIDB.reserve(tracks.size() / 2); - // apply same ML cut - if (!passMlCut(ml1, ml2)) { - continue; - } - float pt = cand.pt(); - if (pt < ptTrkMinD.value || pt >= ptTrkMaxD.value) { - continue; + // collect track IDs in A and B + for (const auto& trk : tracks) { + if (!selectionTrack(trk)) { + continue; + } + float eta = trk.eta(); + if (eta > etaAMin.value && eta < etaAMax.value) { + trkIDA.push_back(trk.globalIndex()); + } else if (eta > etaBMin.value && eta < etaBMax.value) { + trkIDB.push_back(trk.globalIndex()); + } } - ++nDcandTotA; - } + std::sort(trkIDA.begin(), trkIDA.end()); + std::sort(trkIDB.begin(), trkIDB.end()); - if (nDcandTotA <= 0) { - return; // cannot build fraction - } + for (const auto& cand : candidates) { + // apply ML selection + auto [ml1, ml2] = getMlScores(cand); + if (!passMlCut(ml1, ml2)) { + continue; + } - registry.fill(HIST("hEventsD"), cent, 1.f); - const float invND = 1.f / static_cast(nDcandTotA); + // remove the daughters from the mean pT of tracks and calculate pt product + float eta = cand.eta(); + float pt = cand.pt(); + + float candPtProduct{0.f}; + float meanPtA{0.f}; + float meanPtB{0.f}; + int nATrk{nA}; + int nBTrk{nB}; + if (eta > etaAMin.value && eta < etaAMax.value) { + std::tie(meanPtB, nBTrk) = removeDaughtersFromMeanPt(cand, RawMeanPtB, nB, trkIDB); + meanPtA = RawMeanPtA; // no need to remove daughters from A if candidate is in A + candPtProduct = pt * meanPtB; + } else if (eta > etaBMin.value && eta < etaBMax.value) { + std::tie(meanPtA, nATrk) = removeDaughtersFromMeanPt(cand, RawMeanPtA, nA, trkIDA); + meanPtB = RawMeanPtB; // no need to remove daughters from B if candidate is in B + candPtProduct = pt * meanPtA; + } - for (const auto& candidate : candidates) { - if (!passCandInA(candidate)) { - continue; - } + // get candidate mass and sign + auto [invMass, sign] = getCandMassAndSign(cand); - // ML first (same definition) - const auto [ml1, ml2] = getMlScores(candidate); - if (!passMlCut(ml1, ml2)) { - continue; + // fill charm-bulk correlation thnsparse + registry.fill(HIST("hCharmBulkCorrelations"), invMass, cent, pt, sign, ml1, ml2, eta, meanPtA, meanPtB, candPtProduct, nATrk, nBTrk); } + } else { + int nDcandTotA = 0; + for (const auto& cand : candidates) { + if (!passCandInA(cand)) { + continue; + } + + // compute ML exactly like numerator + const auto [ml1, ml2] = getMlScores(cand); - // compute mass - float massCand = 0.; - float signCand = 0.; - if constexpr (std::is_same_v) { - massCand = HfHelper::invMassDplusToPiKPi(candidate); - auto trackprong0 = candidate.template prong0_as(); - signCand = trackprong0.sign(); - } else { - if constexpr (Channel == DecayChannel::D0ToPiK) { - massCand = HfHelper::invMassD0ToPiK(candidate); - signCand = candidate.isSelD0bar() ? 3 : 1; // 3: reflected D0bar, 1: pure D0 excluding reflected D0bar - } else if constexpr (Channel == DecayChannel::D0ToKPi) { - massCand = HfHelper::invMassD0barToKPi(candidate); - signCand = candidate.isSelD0() ? 3 : 2; // 3: reflected D0, 2: pure D0bar excluding reflected D0 + // apply same ML cut + if (!passMlCut(ml1, ml2)) { + continue; } + float pt = cand.pt(); + if (pt < ptCandMin.value || pt >= ptCandMax.value) { + continue; + } + + ++nDcandTotA; } - const double ptCand = candidate.pt(); - if (ptCand < ptTrkMinD.value || ptCand >= ptTrkMaxD.value) { - continue; + if (nDcandTotA == 0) { + return; // cannot build fraction } - registry.fill(HIST("hD_mass"), massCand, cent, ptCand, signCand, ml1, ml2, 1.f); - registry.fill(HIST("hD_f"), massCand, cent, ptCand, signCand, ml1, ml2, invND); - registry.fill(HIST("hD_f_wPtB"), massCand, cent, ptCand, signCand, ml1, ml2, meanPtB * invND); + registry.fill(HIST("hEventsD"), cent, 1.f); + const float invND = 1.f / static_cast(nDcandTotA); + + for (const auto& candidate : candidates) { + if (!passCandInA(candidate)) { + continue; + } + + // ML first (same definition) + const auto [ml1, ml2] = getMlScores(candidate); + if (!passMlCut(ml1, ml2)) { + continue; + } + + // compute mass + auto [massCand, signCand] = getCandMassAndSign(candidate); + + const double ptCand = candidate.pt(); + if (ptCand < ptCandMin.value || ptCand >= ptCandMax.value) { + continue; + } + + registry.fill(HIST("hD_mass"), massCand, cent, ptCand, signCand, ml1, ml2, 1.f); + registry.fill(HIST("hD_f"), massCand, cent, ptCand, signCand, ml1, ml2, invND); + registry.fill(HIST("hD_f_wPtB"), massCand, cent, ptCand, signCand, ml1, ml2, RawMeanPtB * invND); + } } - } + } // end runPtFlucAnalysis // D0 with ML void processD0Ml(Colls::iterator const& collision, CandD0DataWMl const& /*candidatesD0*/, - TracksWithExtra const& tracks) + soa::Filtered const& tracks) { auto candsD0ToPiKWMl = selectedD0ToPiKWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); auto candsD0ToKPiWMl = selectedD0ToKPiWMl->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); @@ -391,7 +509,7 @@ struct HfTaskPtFlucCharmHadrons { // Dplus with ML void processDplusMl(Colls::iterator const& collision, CandDplusDataWMl const& candidatesDplus, - TracksWithExtra const& tracks) + soa::Filtered const& tracks) { runPtFlucAnalysis(collision, candidatesDplus, tracks); } diff --git a/PWGHF/D2H/Tasks/taskSigmac.cxx b/PWGHF/D2H/Tasks/taskSigmac.cxx index 8bfbff280cb..3f73c812615 100644 --- a/PWGHF/D2H/Tasks/taskSigmac.cxx +++ b/PWGHF/D2H/Tasks/taskSigmac.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include using namespace o2; @@ -67,11 +68,16 @@ struct HfTaskSigmac { Configurable addSoftPiDcaToSigmacSparse{"addSoftPiDcaToSigmacSparse", false, "enable the filling of soft-pion dcaXY, dcaZ in the Σc0,++ THnSparse"}; Configurable addMassDiffAbsLambdaCToSigmacSparse{"addMassDiffAbsLambdaCToSigmacSparse", false, "enable the filling of |M(pkpi, piKp) - M(LambdaC)| in the Σc0,++ THnSparse"}; Configurable deltaMassSigmacRecoMax{"deltaMassSigmacRecoMax", 1000, "Maximum allowed value for Sigmac deltaMass. Conceived to reduce the output size (i.e. reject background above a certain threshold)"}; + Configurable ptMinSc{"ptMinSc", -1.f, "Minimum accepted value for SigmaC-hadron pt (GeV/c)"}; + Configurable trackingInfo4SoftPiOnly{"trackingInfo4SoftPiOnly", false, "Flag to enable the storage of tracking info about the soft pion only"}; bool isMc{}; + bool storeTrackProp{}; static constexpr std::size_t NDaughters{2u}; using RecoLc = soa::Join; + using TracksWithExtra = soa::Join; + using TracksWMcWithExtra = soa::Join; /// THn for candidate Λc+ and Σc0,++ cut variation ConfigurableAxis thnConfigAxisPt{"thnConfigAxisPt", {16, 0, 16}, ""}; @@ -87,6 +93,9 @@ struct HfTaskSigmac { ConfigurableAxis thnConfigAxisBdtScoreLcNonPrompt{"thnConfigAxisBdtScoreLcNonPrompt", {100, 0., 1.}, ""}; ConfigurableAxis thnConfigAxisSoftPiAbsDca{"thnConfigAxisSoftPiAbsDca", {14, 0., 0.07}, ""}; ConfigurableAxis thnConfigAxisMassDiffAbsLambdaC{"thnConfigAxisMassDiffAbsLambdaC", {12, 0, 0.06}, ""}; + ConfigurableAxis thnConfigAxisAbsEtaTrackMin{"thnConfigAxisAbsEtaTrackMin", {3, 0.f, 0.3f}, "min |#it{#eta_{track}}|"}; + ConfigurableAxis thnConfigAxisNumItsClsMin{"thnConfigAxisNumItsClsMin", {4, 3.5f, 7.5f}, "min #it{N}_{cls ITS}"}; + ConfigurableAxis thnConfigAxisNumTpcClsMin{"thnConfigAxisNumTpcClsMin", {3, 79.5f, 140.5f}, "min #it{N}_{cls TPC}"}; /// analysis histograms HistogramRegistry registry{ @@ -136,27 +145,40 @@ struct HfTaskSigmac { void init(InitContext&) { - /// To be considered in the future, let's keep the possibility to run in MC also with "data-like" mode (just for TH1 objects) - // std::array processes {doprocessDataWoMl, doprocessDataWithMl, doprocessMcWoMl, doprocessMcWithMl}; - // if( std::accumulate(processes.begin(), processes.end(), 0) != 1 ) { - // LOG(fatal) << "One and only one process function must be enabled. Fix it!"; - // } - - // avoid 2 enabled process functions on data - if (doprocessDataWoMl && doprocessDataWithMl) { - LOG(fatal) << "processDataWoMl and processDataWithMl both enabled. Fix it!"; + // avoid more than one enabled process functions on data + std::array processesData = {doprocessDataWoMl, doprocessDataWithMl, doprocessDataWithMlWithTrackInfo}; + if (std::accumulate(processesData.begin(), processesData.end(), 0) > 1) { + LOG(fatal) << "one and only one process function in data can be enabled. Fix it!"; } - // avoid 2 enabled process functions on MC - if (doprocessMcWoMl && doprocessMcWithMl) { - LOG(fatal) << "processMcWoMl and processMcWithMl both enabled. Fix it!"; + // avoid more than one enabled process functions on MC + std::array processesMc = {doprocessMcWoMl, doprocessMcWithMl, doprocessMcWithMlWithTrackInfo}; + if (std::accumulate(processesMc.begin(), processesMc.end(), 0) > 1) { + LOG(fatal) << "one and only one process function in MC can be enabled. Fix it!"; } // avoid that in data no ML is used while in MC yes, and viceversa - if ((doprocessDataWithMl && doprocessMcWoMl) || (doprocessDataWoMl && doprocessMcWithMl)) { + if (((doprocessDataWithMl || doprocessDataWithMlWithTrackInfo) && doprocessMcWoMl) || (doprocessDataWoMl && (doprocessMcWithMl || doprocessMcWithMlWithTrackInfo))) { LOG(fatal) << "process functions with and w/o ML enabled not consistently between data and MC. Fix it! processDataWoMl: " << doprocessDataWoMl << "processDataWithMl: " << doprocessDataWithMl << "processMcWoMl: " << doprocessMcWoMl << "processMcWithMl: " << doprocessMcWithMl; } + // avoid that in data track info get stored while in MC not, and viceversa + if ((doprocessDataWithMlWithTrackInfo && doprocessMcWithMl) || (doprocessDataWithMl && doprocessMcWithMlWithTrackInfo)) { + LOG(fatal) << "process functions with and w/o storage of track information enabled not consistently between data and MC. Fix it! processDataWithMlWithTrackInfo: " << doprocessDataWithMlWithTrackInfo << "processDataWithMl: " << doprocessDataWithMl << "processMcWithMlWithTrackInfo: " << doprocessMcWithMlWithTrackInfo << "processMcWithMl: " << doprocessMcWithMl; + } + /// establish if the analysis is done on Data or MC - isMc = doprocessMcWoMl || doprocessMcWithMl; + isMc = doprocessMcWoMl || doprocessMcWithMl || doprocessMcWithMlWithTrackInfo; + + /// establish if the output THnSparse should include track information + storeTrackProp = doprocessDataWithMlWithTrackInfo || doprocessMcWithMlWithTrackInfo; + + LOG(info) << "### doprocessDataWoMl: " << doprocessDataWoMl; + LOG(info) << "### doprocessDataWithMl: " << doprocessDataWithMl; + LOG(info) << "### doprocessDataWithMlWithTrackInfo: " << doprocessDataWithMlWithTrackInfo; + LOG(info) << "### doprocessMcWoMl: " << doprocessMcWoMl; + LOG(info) << "### doprocessMcWithMl: " << doprocessMcWithMl; + LOG(info) << "### doprocessMcWithMlWithTrackInfo: " << doprocessMcWithMlWithTrackInfo; + LOG(info) << "+++ isMc: " << isMc; + LOG(info) << "+++ storeTrackProp: " << storeTrackProp; const AxisSpec thnAxisMassLambdaC{configAxisMassLambdaC, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPtLambdaC{thnConfigAxisPt, "#it{p}_{T}(#Lambda_{c}^{+}) (GeV/#it{c})"}; @@ -180,6 +202,9 @@ struct HfTaskSigmac { const AxisSpec thnAxisSigmaCParticleAntiparticle = {o2::aod::hf_cand_sigmac::Conjugated::NConjugated, -0.5f, +o2::aod::hf_cand_sigmac::Conjugated::NConjugated - 0.5f, "bin 1: particle, bin 2: antiparticle"}; const AxisSpec axisDeltaMassSigmaC{configAxisDeltaMassSigmaC, "#it{M}(pK#pi#pi) - #it{M}(pK#pi) (GeV/#it{c}^{2})"}; const AxisSpec thnAxisMassDiffAbsLambdaC{thnConfigAxisMassDiffAbsLambdaC, "|#it{M}(pK#pi or piK#pi) - #it{M}(#Lambda_{c})| (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisAbsEtaTrackMin{thnConfigAxisAbsEtaTrackMin, "min |#it{#eta_{track}}|"}; + const AxisSpec thnAxisNumItsClsMin{thnConfigAxisNumItsClsMin, "min #it{N}_{cls ITS}"}; + const AxisSpec thnAxisNumTpcClsMin{thnConfigAxisNumTpcClsMin, "min #it{N}_{cls TPC}"}; registry.add("Data/hDeltaMassSc0", "#Sigma_{c}^{0} candidates; #it{M}(pK#pi#pi) - #it{M}(pK#pi) (GeV/#it{c}^{2}); #it{p}_{T}(#Sigma_{c}^{0}) (GeV/#it{c});", {HistType::kTH2D, {axisDeltaMassSigmaC, {36, 0., 36.}}}); registry.add("Data/hDeltaMassScPlusPlus", "#Sigma_{c}^{++} candidates; #it{M}(pK#pi#pi) - #it{M}(pK#pi) (GeV/#it{c}^{2}); #it{p}_{T}(#Sigma_{c}^{++}) (GeV/#it{c});", {HistType::kTH2D, {axisDeltaMassSigmaC, {36, 0., 36.}}}); registry.add("Data/hDeltaMassSc0PlusPlus", "#Sigma_{c}^{0,++} candidates; #it{M}(pK#pi#pi) - #it{M}(pK#pi) (GeV/#it{c}^{2}); #it{p}_{T}(#Sigma_{c}^{0,++}) (GeV/#it{c});", {HistType::kTH2D, {axisDeltaMassSigmaC, {36, 0., 36.}}}); @@ -291,14 +316,22 @@ struct HfTaskSigmac { /// THn for candidate Λc+ and Σc0,++ cut variation if (enableTHnSc || enableTHnLc) { + + /// force the setting of a minimum pt value for SigmaC in case we want to store the track info, to reduce the output size + if (storeTrackProp && ptMinSc < 0.f) { + LOG(fatal) << "Storage of track properties enabled, but no constraints on minimum pt(Sc) are defined. Please specify the minimum pt for Sc candidates to be stored in the output THnSparse"; + } + std::vector axesLambdaCWithMl = {thnAxisPtLambdaC, thnAxisMassLambdaC, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcNonPrompt, thnAxisOriginMc, thnAxisChannel}; std::vector axesSigmaCWithMl = {thnAxisPtLambdaC, axisDeltaMassSigmaC, thnAxisBdtScoreLcBkg, thnAxisBdtScoreLcNonPrompt, thnAxisOriginMc, thnAxisChannel, thnAxisPtSigmaC, thnAxisChargeSigmaC}; std::vector axesLambdaCWoMl = {thnAxisPtLambdaC, thnAxisMassLambdaC, thnAxisDecLength, thnAxisDecLengthXY, thnAxisCPA, thnAxisCPAXY, thnAxisOriginMc, thnAxisChannel}; std::vector axesSigmaCWoMl = {thnAxisPtLambdaC, axisDeltaMassSigmaC, thnAxisDecLength, thnAxisDecLengthXY, thnAxisCPA, thnAxisCPAXY, thnAxisOriginMc, thnAxisChannel, thnAxisPtSigmaC, thnAxisChargeSigmaC}; if (isMc) { + /// MC analysis registry.add("MC/generated/hnLambdaCGen", "THn for Lambdac gen", HistType::kTHnSparseF, {thnAxisGenPtLambdaC, thnAxisGenPtLambdaCBMother, thnAxisOriginMc, thnAxisChannel}); registry.add("MC/generated/hnSigmaCGen", "THn for Sigmac gen", HistType::kTHnSparseF, {thnAxisGenPtSigmaC, thnAxisGenPtSigmaCBMother, thnAxisOriginMc, thnAxisChannel, thnAxisGenPtLambdaC, thnAxisChargeSigmaC, thnAxisGenSigmaCSpecies, thnAxisSigmaCParticleAntiparticle}); - if (doprocessMcWithMl) { + if (doprocessMcWithMl || doprocessMcWithMlWithTrackInfo) { + /// MC analysis with ML info axesLambdaCWithMl.push_back(thnAxisGenPtLambdaCBMother); axesSigmaCWithMl.push_back(thnAxisGenPtSigmaCBMother); axesSigmaCWithMl.push_back(thnAxisGenSigmaCSpecies); @@ -310,6 +343,14 @@ struct HfTaskSigmac { if (addMassDiffAbsLambdaCToSigmacSparse) { axesSigmaCWithMl.push_back(thnAxisMassDiffAbsLambdaC); } + if (storeTrackProp) { + axesLambdaCWithMl.push_back(thnAxisAbsEtaTrackMin); + axesSigmaCWithMl.push_back(thnAxisAbsEtaTrackMin); + axesLambdaCWithMl.push_back(thnAxisNumItsClsMin); + axesSigmaCWithMl.push_back(thnAxisNumItsClsMin); + axesLambdaCWithMl.push_back(thnAxisNumTpcClsMin); + axesSigmaCWithMl.push_back(thnAxisNumTpcClsMin); + } // enable THnSparse for Λc+ if (enableTHnLc) { registry.add("hnLambdaC", "THn for Lambdac", HistType::kTHnSparseF, axesLambdaCWithMl); @@ -319,6 +360,7 @@ struct HfTaskSigmac { registry.add("hnSigmaC", "THn for Sigmac", HistType::kTHnSparseF, axesSigmaCWithMl); } } else { + /// MC analysis w/o ML info axesLambdaCWoMl.push_back(thnAxisGenPtLambdaCBMother); axesSigmaCWoMl.push_back(thnAxisGenPtSigmaCBMother); axesSigmaCWoMl.push_back(thnAxisGenSigmaCSpecies); @@ -340,7 +382,9 @@ struct HfTaskSigmac { } } } else { - if (doprocessDataWithMl) { + /// data analysis + if (doprocessDataWithMl || doprocessDataWithMlWithTrackInfo) { + /// data analysis with ML info if (addSoftPiDcaToSigmacSparse) { axesSigmaCWithMl.push_back(thnAxisSoftPiAbsDcaXY); axesSigmaCWithMl.push_back(thnAxisSoftPiAbsDcaZ); @@ -348,6 +392,14 @@ struct HfTaskSigmac { if (addMassDiffAbsLambdaCToSigmacSparse) { axesSigmaCWithMl.push_back(thnAxisMassDiffAbsLambdaC); } + if (storeTrackProp) { + axesLambdaCWithMl.push_back(thnAxisAbsEtaTrackMin); + axesSigmaCWithMl.push_back(thnAxisAbsEtaTrackMin); + axesLambdaCWithMl.push_back(thnAxisNumItsClsMin); + axesSigmaCWithMl.push_back(thnAxisNumItsClsMin); + axesLambdaCWithMl.push_back(thnAxisNumTpcClsMin); + axesSigmaCWithMl.push_back(thnAxisNumTpcClsMin); + } // enable THnSparse for Λc+ if (enableTHnLc) { registry.add("hnLambdaC", "THn for Lambdac", HistType::kTHnSparseF, axesLambdaCWithMl); @@ -357,6 +409,7 @@ struct HfTaskSigmac { registry.add("hnSigmaC", "THn for Sigmac", HistType::kTHnSparseF, axesSigmaCWithMl); } } else { + /// data analysis w/o ML info if (addSoftPiDcaToSigmacSparse) { axesSigmaCWoMl.push_back(thnAxisSoftPiAbsDcaXY); axesSigmaCWoMl.push_back(thnAxisSoftPiAbsDcaZ); @@ -378,13 +431,38 @@ struct HfTaskSigmac { }; /// end init + /// @brief Function to retrieve information about track properties + /// @param prongTracks is the vector of daughter tracks + /// @param etaMin is the minimum eta + /// @param nItsClsMin is the minumum number of clusters in ITS + /// @param nTpcClsMin is the minumum number of clusters in TPC + template + void getTrackingInfo(std::vector const& prongTracks, float& etaMin, int& nItsClsMin, int& nTpcClsMin) + { + etaMin = 10.f; + nItsClsMin = 10; + nTpcClsMin = 1000; + + for (const auto& track : prongTracks) { + if (std::abs(track.eta()) < etaMin) { + etaMin = std::abs(track.eta()); + } + if (track.itsNCls() < nItsClsMin) { + nItsClsMin = track.itsNCls(); + } + if (track.tpcNClsCrossedRows() < nTpcClsMin) { + nTpcClsMin = track.tpcNClsCrossedRows(); + } + } + } + /// @brief function to fill the histograms needed in analysis (data) /// @param candidatesSc are the reconstructed candidate Σc0,++ /// @param - template + template void fillHistosData(aod::HfCandSc const& candidatesSc, CandsLc const& candidatesLc, - aod::Tracks const&) + TRK const&) { /// loop over the candidate Σc0,++ @@ -408,9 +486,14 @@ struct HfTaskSigmac { double ptSc(candSc.pt()), ptLc(candidateLc.pt()); double etaSc(candSc.eta()), etaLc(candidateLc.eta()); double phiSc(candSc.phi()), phiLc(candidateLc.phi()); - double ptSoftPi(candSc.prong1().pt()), etaSoftPi(candSc.prong1().eta()), phiSoftPi(candSc.prong1().phi()); + const auto& trackSoftPi = candSc.template prong1_as(); + double ptSoftPi(trackSoftPi.pt()), etaSoftPi(trackSoftPi.eta()), phiSoftPi(trackSoftPi.phi()); double decLengthLc(candidateLc.decayLength()), decLengthXYLc(candidateLc.decayLengthXY()); double cpaLc(candidateLc.cpa()), cpaXYLc(candidateLc.cpaXY()); + /// exclude already candidate Σc0,++ outside the accepted pt range + if (ptMinSc > 0.f && ptSc < ptMinSc) { + continue; + } /// candidate Λc+ → pK-π+ (and charge conjugate) within the range of M(pK-π+) chosen in the Σc0,++ builder if (TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PKPi)) { massSc = HfHelper::invMassScRecoLcToPKPi(candSc, candidateLc); @@ -485,27 +568,74 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPKPi()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPKPi()[2]; /// non-prompt score } + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + if (trackingInfo4SoftPiOnly) { + /// soft pion info only + getTrackingInfo(std::vector{trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// info from soft pion and also Lc daughters + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2, trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + } if (addSoftPiDcaToSigmacSparse) { /// dcaXY,Z of soft pion track stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ); + } } } else { /// dcaXY,Z of soft pion track not stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: none - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc)); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc)); + } } } } else { @@ -611,27 +741,74 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPiKP()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPiKP()[2]; /// non-prompt score } + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + if (trackingInfo4SoftPiOnly) { + /// soft pion info only + getTrackingInfo(std::vector{trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// info from soft pion and also Lc daughters + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2, trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + } if (addSoftPiDcaToSigmacSparse) { /// dcaXY,Z of soft pion track stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), softPiAbsDcaXY, softPiAbsDcaZ); + } } } else { /// dcaXY,Z of soft pion track not stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: none - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc)); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc), absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), 0, 0, ptSc, std::abs(chargeSc)); + } } } } else { @@ -685,7 +862,28 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPKPi()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPKPi()[2]; /// non-prompt score } - registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), 0, 0); + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), 0, 0, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info note stored + /// optional axes enabled: none + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), 0, 0); + } } else { /// fill w/o BDT information registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, decLengthLc, decLengthXYLc, cpaLc, cpaXYLc, 0, 0); @@ -701,7 +899,28 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPiKP()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPiKP()[2]; /// non-prompt score } - registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), 0, 0); + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), 0, 0, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info note stored + /// optional axes enabled: none + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), 0, 0); + } } else { /// fill w/o BDT information registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, decLengthLc, decLengthXYLc, cpaLc, cpaXYLc, 0, 0); @@ -734,13 +953,13 @@ struct HfTaskSigmac { /// @param candidatesSc are the reconstructed candidate Σc0,++ with MC info /// @param mcParticles are the generated particles with flags wheter they are Σc0,++ or not /// @param - template + template void fillHistosMc(soa::Join const& candidatesSc, soa::Join const& mcParticlesSc, soa::Join const& mcParticlesLc, aod::McParticles const& mcParticles, // this establishes the type of particle obtained with the .mcParticle() getter CandsLc const& candidatesLc, - aod::TracksWMc const&) + TRK const&) { /// loop over Sc generated particles @@ -786,6 +1005,10 @@ struct HfTaskSigmac { /// Get information about origin (prompt, non-prompt) /// Get information about decay Λc+ channel (direct, resonant) double ptGenSc(particle.pt()), etaGenSc(particle.eta()), phiGenSc(particle.phi()); + /// exclude already candidate Σc0,++ outside the accepted pt range + if (ptMinSc > 0.f && ptGenSc < ptMinSc) { + continue; + } double ptGenScBMother(-1.); auto arrayDaughtersIds = particle.daughtersIds(); if (arrayDaughtersIds.size() != NDaughters) { @@ -970,21 +1193,21 @@ struct HfTaskSigmac { int indexMcScRec = -1; if (isTrueSc0Reco) { // Σc0(2455) - indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaC0, true); + indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaC0, true); sigmacSpecies = o2::aod::hf_cand_sigmac::Sc2455; } else if (isTrueScStar0Reco) { // Σc0(2520) - indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaCStar0, true); + indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaCStar0, true); sigmacSpecies = o2::aod::hf_cand_sigmac::Sc2520; } auto particleSc = mcParticles.rawIteratorAt(indexMcScRec); // Get the corresponding MC particle for Lc - auto arrayDaughtersLc = std::array{candidateLc.template prong0_as(), candidateLc.template prong1_as(), candidateLc.template prong2_as()}; + auto arrayDaughtersLc = std::array{candidateLc.template prong0_as(), candidateLc.template prong1_as(), candidateLc.template prong2_as()}; int8_t sign = 0; int const indexMcLcRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughtersLc, o2::constants::physics::Pdg::kLambdaCPlus, std::array{+kProton, -kKPlus, +kPiPlus}, true, &sign, 2); auto particleLc = mcParticles.rawIteratorAt(indexMcLcRec); // Get the corresponding MC particle for soft pion - auto particleSoftPi = candSc.prong1_as().mcParticle(); + auto particleSoftPi = candSc.prong1_as().mcParticle(); // const int iscandidateLcpKpi = (candidateLc.isSelLcToPKPi() >= 1) && candSc.statusSpreadLcMinvPKPiFromPDG(); // Λc+ → pK-π+ and within the requested mass to build the Σc0,++ // const int iscandidateLcpiKp = (candidateLc.isSelLcToPiKP() >= 1) && candSc.statusSpreadLcMinvPiKPFromPDG(); // Λc+ → π+K-p and within the requested mass to build the Σc0,++ @@ -992,16 +1215,20 @@ struct HfTaskSigmac { double ptSc(candSc.pt()), ptLc(candidateLc.pt()); double etaSc(candSc.eta()), etaLc(candidateLc.eta()); double phiSc(candSc.phi()), phiLc(candidateLc.phi()); - double ptSoftPi(candSc.prong1_as().pt()), etaSoftPi(candSc.prong1_as().eta()), phiSoftPi(candSc.prong1_as().phi()); + double ptSoftPi(candSc.prong1_as().pt()), etaSoftPi(candSc.prong1_as().eta()), phiSoftPi(candSc.prong1_as().phi()); double ptGenSc(particleSc.pt()), ptGenLc(particleLc.pt()), ptGenSoftPi(particleSoftPi.pt()); double decLengthLc(candidateLc.decayLength()), decLengthXYLc(candidateLc.decayLengthXY()); double cpaLc(candidateLc.cpa()), cpaXYLc(candidateLc.cpaXY()); int const origin = candSc.originMcRec(); auto channel = candidateLc.flagMcDecayChanRec(); /// 0: direct; 1: Λc± → p± K*; 2: Λc± → Δ(1232)±± K∓; 3: Λc± → Λ(1520) π± channel = remapResoChannelLc(channel); + /// exclude already candidate Σc0,++ outside the accepted pt range + if (ptMinSc > 0.f && ptSc < ptMinSc) { + continue; + } /// candidate Λc+ → pK-π+ (and charge conjugate) within the range of M(pK-π+) chosen in the Σc0,++ builder - if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PKPi)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kProton) { + if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PKPi)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kProton) { massSc = HfHelper::invMassScRecoLcToPKPi(candSc, candidateLc); massLc = HfHelper::invMassLcToPKPi(candidateLc); massDiffAbsFromPdgLc = std::abs(o2::hf_sigmac_utils::massDiffFromPdgLcToPKPi(candidateLc)); @@ -1075,27 +1302,75 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPKPi()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPKPi()[2]; /// non-prompt score } + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + const auto& trackSoftPi = candSc.template prong1_as(); + if (trackingInfo4SoftPiOnly) { + /// soft pion info only + getTrackingInfo(std::vector{trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// info from soft pion and also Lc daughters + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2, trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + } if (addSoftPiDcaToSigmacSparse) { /// dcaXY,Z of soft pion track stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + } } } else { /// dcaXY,Z of soft pion track not stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: none - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + } } } } else { @@ -1128,7 +1403,7 @@ struct HfTaskSigmac { } /// end candidate Λc+ → pK-π+ (and charge conjugate) /// candidate Λc+ → π+K-p (and charge conjugate) within the range of M(π+K-p) chosen in the Σc0,++ builder - if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PiKP)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kPiPlus) { + if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PiKP)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kPiPlus) { massSc = HfHelper::invMassScRecoLcToPiKP(candSc, candidateLc); massLc = HfHelper::invMassLcToPiKP(candidateLc); massDiffAbsFromPdgLc = std::abs(o2::hf_sigmac_utils::massDiffFromPdgLcToPiKP(candidateLc)); @@ -1202,27 +1477,75 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPiKP()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPiKP()[2]; /// non-prompt score } + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + const auto& trackSoftPi = candSc.template prong1_as(); + if (trackingInfo4SoftPiOnly) { + /// soft pion info only + getTrackingInfo(std::vector{trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// info from soft pion and also Lc daughters + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2, trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + } if (addSoftPiDcaToSigmacSparse) { /// dcaXY,Z of soft pion track stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + } } } else { /// dcaXY,Z of soft pion track not stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: none - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + } } } } else { @@ -1261,21 +1584,21 @@ struct HfTaskSigmac { int indexMcScRec = -1; if (isTrueScPlusPlusReco) { // Σc0(2455) - indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaCPlusPlus, true); + indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaCPlusPlus, true); sigmacSpecies = o2::aod::hf_cand_sigmac::Sc2455; } else if (isTrueScStarPlusPlusReco) { // Σc0(2520) - indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaCStarPlusPlus, true); + indexMcScRec = RecoDecay::getMother(mcParticles, candSc.prong1_as().mcParticle(), o2::constants::physics::Pdg::kSigmaCStarPlusPlus, true); sigmacSpecies = o2::aod::hf_cand_sigmac::Sc2520; } auto particleSc = mcParticles.rawIteratorAt(indexMcScRec); // Get the corresponding MC particle for Lc - auto arrayDaughtersLc = std::array{candidateLc.template prong0_as(), candidateLc.template prong1_as(), candidateLc.template prong2_as()}; + auto arrayDaughtersLc = std::array{candidateLc.template prong0_as(), candidateLc.template prong1_as(), candidateLc.template prong2_as()}; int8_t sign = 0; int const indexMcLcRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughtersLc, o2::constants::physics::Pdg::kLambdaCPlus, std::array{+kProton, -kKPlus, +kPiPlus}, true, &sign, 2); auto particleLc = mcParticles.rawIteratorAt(indexMcLcRec); // Get the corresponding MC particle for soft pion - auto particleSoftPi = candSc.prong1_as().mcParticle(); + auto particleSoftPi = candSc.prong1_as().mcParticle(); // const int iscandidateLcpKpi = (candidateLc.isSelLcToPKPi() >= 1) && candSc.statusSpreadLcMinvPKPiFromPDG(); // Λc+ → pK-π+ and within the requested mass to build the Σc0,++ // const int iscandidateLcpiKp = (candidateLc.isSelLcToPiKP() >= 1) && candSc.statusSpreadLcMinvPiKPFromPDG(); // Λc+ → π+K-p and within the requested mass to build the Σc0,++ @@ -1283,7 +1606,7 @@ struct HfTaskSigmac { double ptSc(candSc.pt()), ptLc(candidateLc.pt()); double etaSc(candSc.eta()), etaLc(candidateLc.eta()); double phiSc(candSc.phi()), phiLc(candidateLc.phi()); - double ptSoftPi(candSc.prong1_as().pt()), etaSoftPi(candSc.prong1_as().eta()), phiSoftPi(candSc.prong1_as().phi()); + double ptSoftPi(candSc.prong1_as().pt()), etaSoftPi(candSc.prong1_as().eta()), phiSoftPi(candSc.prong1_as().phi()); double ptGenSc(particleSc.pt()), ptGenLc(particleLc.pt()), ptGenSoftPi(particleSoftPi.pt()); double decLengthLc(candidateLc.decayLength()), decLengthXYLc(candidateLc.decayLengthXY()); double cpaLc(candidateLc.cpa()), cpaXYLc(candidateLc.cpaXY()); @@ -1292,7 +1615,7 @@ struct HfTaskSigmac { channel = remapResoChannelLc(channel); /// candidate Λc+ → pK-π+ (and charge conjugate) within the range of M(pK-π+) chosen in the Σc0,++ builder - if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PKPi)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kProton) { + if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PKPi)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kProton) { massSc = HfHelper::invMassScRecoLcToPKPi(candSc, candidateLc); massLc = HfHelper::invMassLcToPKPi(candidateLc); massDiffAbsFromPdgLc = std::abs(o2::hf_sigmac_utils::massDiffFromPdgLcToPKPi(candidateLc)); @@ -1366,27 +1689,75 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPKPi()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPKPi()[2]; /// non-prompt score } + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + const auto& trackSoftPi = candSc.template prong1_as(); + if (trackingInfo4SoftPiOnly) { + /// soft pion info only + getTrackingInfo(std::vector{trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// info from soft pion and also Lc daughters + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2, trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + } if (addSoftPiDcaToSigmacSparse) { /// dcaXY,Z of soft pion track stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + } } } else { /// dcaXY,Z of soft pion track not stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: none - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + } } } } else { @@ -1419,7 +1790,7 @@ struct HfTaskSigmac { } /// end candidate Λc+ → pK-π+ (and charge conjugate) /// candidate Λc+ → π+K-p (and charge conjugate) within the range of M(π+K-p) chosen in the Σc0,++ builder - if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PiKP)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kPiPlus) { + if ((TESTBIT(isCandPKPiPiKP, o2::aod::hf_cand_sigmac::Decays::PiKP)) && std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()) == kPiPlus) { massSc = HfHelper::invMassScRecoLcToPiKP(candSc, candidateLc); massLc = HfHelper::invMassLcToPiKP(candidateLc); massDiffAbsFromPdgLc = std::abs(o2::hf_sigmac_utils::massDiffFromPdgLcToPiKP(candidateLc)); @@ -1491,27 +1862,75 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPiKP()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPiKP()[2]; /// non-prompt score } + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + const auto& trackSoftPi = candSc.template prong1_as(); + if (trackingInfo4SoftPiOnly) { + /// soft pion info only + getTrackingInfo(std::vector{trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// info from soft pion and also Lc daughters + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2, trackSoftPi}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + } if (addSoftPiDcaToSigmacSparse) { /// dcaXY,Z of soft pion track stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: dcaXY_softPi, dcaZ_softPi - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi, trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: dcaXY_softPi, dcaZ_softPi + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, softPiAbsDcaXY, softPiAbsDcaZ); + } } } else { /// dcaXY,Z of soft pion track not stored if (addMassDiffAbsLambdaCToSigmacSparse) { /// difference |candidate mass - Lc mass| stored - /// optional axes enabled: massDiffLc - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: massDiffLc + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, massDiffAbsFromPdgLc); + } } else { /// difference |candidate mass - Lc mass| not stored - /// optional axes enabled: none - registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnSigmaC"))->Fill(ptLc, deltaMass, outputMl.at(0), outputMl.at(1), origin, channel, ptSc, std::abs(chargeSc), candSc.ptBhadMotherPart(), sigmacSpecies, particleAntiparticle); + } } } } else { @@ -1562,8 +1981,8 @@ struct HfTaskSigmac { auto channel = candidateLc.flagMcDecayChanRec(); /// 0: direct; 1: Λc± → p± K*; 2: Λc± → Δ(1232)±± K∓; 3: Λc± → Λ(1520) π± channel = remapResoChannelLc(channel); int pdgAbs = -1; - if (candidateLc.template prong0_as().has_mcParticle()) { - pdgAbs = std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()); + if (candidateLc.template prong0_as().has_mcParticle()) { + pdgAbs = std::abs(candidateLc.template prong0_as().mcParticle().pdgCode()); } if (candidateLc.isSelLcToPKPi() >= 1 && pdgAbs == kProton) { massLc = HfHelper::invMassLcToPKPi(candidateLc); @@ -1575,7 +1994,28 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPKPi()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPKPi()[2]; /// non-prompt score } - registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), origin, channel, candidateLc.ptBhadMotherPart()); + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), origin, channel, candidateLc.ptBhadMotherPart(), absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), origin, channel, candidateLc.ptBhadMotherPart()); + } } else { /// fill w/o BDT information registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, decLengthLc, decLengthXYLc, cpaLc, cpaXYLc, origin, channel, candidateLc.ptBhadMotherPart()); @@ -1591,7 +2031,28 @@ struct HfTaskSigmac { outputMl.at(0) = candidateLc.mlProbLcToPiKP()[0]; /// bkg score outputMl.at(1) = candidateLc.mlProbLcToPiKP()[2]; /// non-prompt score } - registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), origin, channel, candidateLc.ptBhadMotherPart()); + /// track properties + float absEtaTrackMin{-1.f}; + int numItsClsMin{-1}, numTpcClsMin{-1}; + if constexpr (std::is_same_v) { + if (!storeTrackProp) { + /// this should never happen + LOG(fatal) << "getting track info but storeTrackProp=" << storeTrackProp << ". Not possible! Aborting ..."; + } + const auto& trackLcProng0 = candidateLc.template prong0_as(); + const auto& trackLcProng1 = candidateLc.template prong1_as(); + const auto& trackLcProng2 = candidateLc.template prong2_as(); + getTrackingInfo(std::vector{trackLcProng0, trackLcProng1, trackLcProng2}, absEtaTrackMin, numItsClsMin, numTpcClsMin); + } + if (storeTrackProp) { + /// track info stored + /// optional axes enabled: trackEtaMin, trackItsClstMin, trackTpcClstMin + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), origin, channel, candidateLc.ptBhadMotherPart(), absEtaTrackMin, numItsClsMin, numTpcClsMin); + } else { + /// track info not stored + /// optional axes enabled: none + registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, outputMl.at(0), outputMl.at(1), origin, channel, candidateLc.ptBhadMotherPart()); + } } else { /// fill w/o BDT information registry.get(HIST("hnLambdaC"))->Fill(ptLc, massLc, decLengthLc, decLengthXYLc, cpaLc, cpaXYLc, origin, channel, candidateLc.ptBhadMotherPart()); @@ -1621,6 +2082,15 @@ struct HfTaskSigmac { } PROCESS_SWITCH(HfTaskSigmac, processDataWithMl, "Process data with ML information on Lc", false); + /// @brief process function to fill the histograms needed in analysis with ML information (data), with specific settings to store single-track properties + void processDataWithMlWithTrackInfo(aod::HfCandSc const& candidatesSc, + soa::Join const& candidatesLc, + TracksWithExtra const& tracks) + { + fillHistosData(candidatesSc, candidatesLc, tracks); + } + PROCESS_SWITCH(HfTaskSigmac, processDataWithMlWithTrackInfo, "Process data with ML information on Lc, including also information about track properties", false); + /// @brief process function to fill the histograms needed in analysis w/o ML information (MC) void processMcWoMl(soa::Join const& candidatesSc, soa::Join const& mcParticlesSc, @@ -1644,6 +2114,18 @@ struct HfTaskSigmac { fillHistosMc(candidatesSc, mcParticlesSc, mcParticlesLc, mcParticles, candidatesLc, tracksWithMc); } PROCESS_SWITCH(HfTaskSigmac, processMcWithMl, "Process MC with ML information on Lc", false); + + /// @brief process function to fill the histograms needed in analysis with ML information (MC), with specific settings to store single-track properties + void processMcWithMlWithTrackInfo(soa::Join const& candidatesSc, + soa::Join const& mcParticlesSc, + soa::Join const& mcParticlesLc, + aod::McParticles const& mcParticles, // this establishes the type of particle obtained with the .mcParticle() getter + soa::Join const& candidatesLc, + TracksWMcWithExtra const& tracksWithMc) + { + fillHistosMc(candidatesSc, mcParticlesSc, mcParticlesLc, mcParticles, candidatesLc, tracksWithMc); + } + PROCESS_SWITCH(HfTaskSigmac, processMcWithMlWithTrackInfo, "Process MC with ML information on Lc, including also information about track properties", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/D2H/Tasks/taskUpcLc.cxx b/PWGHF/D2H/Tasks/taskUpcLc.cxx new file mode 100644 index 00000000000..9d8d28b9d8a --- /dev/null +++ b/PWGHF/D2H/Tasks/taskUpcLc.cxx @@ -0,0 +1,451 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskUpcLc.cxx +/// \brief Λc± → p± K∓ π± analysis task +/// \note Extended from taskLc +/// +/// \author Biao Zhang , Heidelberg University +/// \author Ran Tu , Fudan University + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGHF/Utils/utilsEvSelHf.h" +#include "PWGHF/Utils/utilsUpcHf.h" +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UPCHelpers.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include // std::vector + +using namespace o2; +using namespace o2::analysis; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::hf_centrality; +using namespace o2::hf_evsel; +using namespace o2::analysis::hf_upc; + +namespace o2::aod +{ +namespace full +{ +DECLARE_SOA_COLUMN(M, m, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(BkgScore, bkgScore, float); +DECLARE_SOA_COLUMN(PtProng0, ptProng0, float); +DECLARE_SOA_COLUMN(PtProng1, ptProng1, float); +DECLARE_SOA_COLUMN(PtProng2, ptProng2, float); +DECLARE_SOA_COLUMN(Chi2PCA, chi2PCA, float); +DECLARE_SOA_COLUMN(DecayLength, decayLength, float); +DECLARE_SOA_COLUMN(Cpa, cpa, float); +DECLARE_SOA_COLUMN(PvContributors, pvContributors, int); +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, int); +DECLARE_SOA_COLUMN(Vtz, vtz, float); +DECLARE_SOA_COLUMN(AmpFT0A, ampFT0A, float); +DECLARE_SOA_COLUMN(AmpFT0C, ampFT0C, float); +DECLARE_SOA_COLUMN(ZdcTimeZNA, zdcTimeZNA, float); +DECLARE_SOA_COLUMN(ZdcTimeZNC, zdcTimeZNC, float); +DECLARE_SOA_COLUMN(FlagMcMatchRec, flagMcMatchRec, int8_t); +DECLARE_SOA_COLUMN(OriginMcRec, originMcRec, int8_t); +DECLARE_SOA_COLUMN(PtBhadMotherPart, ptBhadMotherPart, float); +} // namespace full +DECLARE_SOA_TABLE(HfUpcQa, "AOD", "HFUPCQA", + full::PvContributors, + full::Multiplicity, + full::Vtz, + full::AmpFT0A, + full::AmpFT0C, + full::ZdcTimeZNA, + full::ZdcTimeZNC); + +DECLARE_SOA_TABLE(HfUpcLcBdtInfos, "AOD", "HFUPCLCBDTINFOS", + full::M, + full::Pt, + full::BkgScore); + +DECLARE_SOA_TABLE(HfUpcLcInfos, "AOD", "HFUPCLCINFOS", + full::M, + full::Pt, + full::PtProng0, + full::PtProng1, + full::PtProng2, + full::Chi2PCA, + full::DecayLength, + full::Cpa); +DECLARE_SOA_TABLE(HfUpcLcMcBdtInfos, "AOD", "HFUPCLCMCBDT", + full::M, + full::Pt, + full::BkgScore, + full::FlagMcMatchRec, + full::OriginMcRec, + full::PtBhadMotherPart); +DECLARE_SOA_TABLE(HfUpcLcMcInfos, "AOD", "HFUPCLCMCINFO", + full::M, + full::Pt, + full::PtProng0, + full::PtProng1, + full::PtProng2, + full::Chi2PCA, + full::DecayLength, + full::Cpa, + full::FlagMcMatchRec, + full::OriginMcRec, + full::PtBhadMotherPart); +DECLARE_SOA_TABLE(HfUpcLcMcGen, "AOD", "HFUPCLCMCGEN", + full::Pt, + hf_cand_mc_flag::FlagMcMatchGen, + hf_cand_mc_flag::OriginMcGen); +} // namespace o2::aod + +/// Λc± → p± K∓ π± analysis task +struct HfTaskUpcLc { + Produces rowCandUpcBdt; + Produces rowCandUpc; + Produces rowCandUpcMcBdt; + Produces rowCandUpcMc; + Produces rowLcMcGen; + Produces rowUpcQa; + + Configurable selectionFlagLc{"selectionFlagLc", 1, "Selection Flag for Lc"}; + Configurable yCandRecoMax{"yCandRecoMax", 0.8, "max. cand. rapidity"}; + Configurable> binsPt{"binsPt", std::vector{hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits"}; + Configurable fillTreeOnlySingleGap{"fillTreeOnlySingleGap", false, "Only fill the tree for candidates that pass the single-gap UPC events"}; + Configurable fillMcGenLcTree{"fillMcGenLcTree", false, "Fill the generated Lc to p K pi tree"}; + Configurable fillTreeUpcQa{"fillTreeUpcQa", false, "Fill Tree for UPC QA"}; + Configurable fillHistQa{"fillHistQa", false, "Fill histograms for UPC detector QA"}; + Configurable verticesWithUpc{"verticesWithUpc", false, "Consider vertices with UPC settings"}; + Configurable zdcTimeThreshold{"zdcTimeThreshold", 2., "Threshold for ZNA/ZNC time"}; + // CCDB configuration + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + + HfEventSelection hfEvSel; // event selection and monitoring + HfUpcGapThresholds upcThresholds; // UPC gap determination thresholds + SliceCache cache; + Service ccdb{}; + + using Collisions = soa::Join; + + using LcCandidates = soa::Filtered>; + using LcCandidatesMl = soa::Filtered>; + using LcCandidatesMc = soa::Filtered>; + using LcCandidatesMlMc = soa::Filtered>; + + Filter filterSelectCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlagLc || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlagLc; + Preslice candLcPerCollision = aod::hf_cand::collisionId; + PresliceUnsorted colPerMcCollision = aod::mcparticle::mcCollisionId; + + HistogramRegistry registry{"registry", {}}; + + enum MlClasses : int { + MlClassBackground = 0, + MlClassPrompt, + MlClassNonPrompt, + NumberOfMlClasses + }; + + void init(InitContext&) + { + const std::array doprocess{doprocessDataWithMlWithUpc, doprocessDataStdWithUpc, doprocessMcWithMlWithUpc, doprocessMcStdWithUpc}; + if ((std::accumulate(doprocess.begin(), doprocess.end(), 0)) != 1) { + LOGP(fatal, "no or more than one process function enabled! Please check your configuration!"); + } + + auto vbins = std::vector(binsPt); + registry.add("Data/fitInfo/ampFT0A_vs_ampFT0C", "FT0-A vs FT0-C amplitude;FT0-A amplitude (a.u.);FT0-C amplitude (a.u.)", {HistType::kTH2F, {{200, 0., 200}, {200, 0., 200}}}); + registry.add("Data/zdc/energyZNA_vs_energyZNC", "ZNA vs ZNC common energy;E_{ZNA}^{common} (a.u.);E_{ZNC}^{common} (a.u.)", {HistType::kTH2F, {{100, 0., 10}, {100, 0., 10}}}); + registry.add("Data/zdc/timeZNA_vs_timeZNC", "ZNA vs ZNC time;ZNA Time;ZNC time", {HistType::kTH2F, {{200, -10., 10}, {200, -10., 10}}}); + registry.add("Data/hUpcGapAfterSelection", "UPC gap type after selection;Gap side;Counts", {HistType::kTH1F, {{7, -1.5, 5.5}}}); + registry.add("Data/hUpcMulti", "Multiplicity of UPC events;Multiplicity;Counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); + registry.add("Data/hUpcVtz", "Vertex Z position of UPC events;Vz (cm);Counts", {HistType::kTH1F, {{200, -10., 10.}}}); + registry.add("Data/eta_vs_Multi_A", "Eta vs Multiplicity of Gap A Events;Eta;Multiplicity", {HistType::kTH2F, {{20, -1., 1.}, {200, -0.5, 199.5}}}); + registry.add("Data/eta_vs_Multi_C", "Eta vs Multiplicity of Gap C Events;Eta;Multiplicity", {HistType::kTH2F, {{20, -1., 1.}, {200, -0.5, 199.5}}}); + hfEvSel.addHistograms(registry); + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } + + /// Evaluate centrality/multiplicity percentile (centrality estimator is automatically selected based on the used table) + /// \param collision is collision + /// \return centrality/multiplicity percentile of the collision + template + float evaluateCentralityColl(const Coll& collision) + { + return o2::hf_centrality::getCentralityColl(collision); + } + + template + void runAnalysisPerCollisionWithUpc(CollType const& collisions, + CandType const& candidates, + BCsType const& bcs, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds, + soa::Join const* mcParticles = nullptr) + { + if constexpr (IsMc) { + if (fillMcGenLcTree) { + for (const auto& particle : *mcParticles) { + if (std::abs(particle.flagMcMatchGen()) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(RecoDecay::y(particle.pVector(), o2::constants::physics::MassLambdaCPlus)) > yCandRecoMax) { + continue; + } + rowLcMcGen(particle.pt(), particle.flagMcMatchGen(), particle.originMcGen()); + } + } + } + for (const auto& collision : collisions) { + float centrality{-1.f}; + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMaskWithUpc(collision, centrality, ccdb, registry, bcs); + if (rejectionMask != 0) { + /// at least one event selection not satisfied --> reject the candidate + continue; + } + const auto thisCollId = collision.globalIndex(); + const auto& groupedLcCandidates = candidates.sliceBy(candLcPerCollision, thisCollId); + if (!fillTreeUpcQa && !fillHistQa && groupedLcCandidates.size() == 0) { + continue; + } + const auto numPvContributors = collision.numContrib(); + const auto& bc = collision.template bc_as(); + + // Determine gap type using SGSelector with BC range checking + const auto gapResult = hf_upc::determineGapType(collision, bcs, upcThresholds); + const int gap = gapResult.value; + const int upcFlag = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) ? 1 : 0; + + // Use the BC with FIT activity if available from SGSelector + auto bcForUPC = bc; + if (gapResult.bc) { + bcForUPC = *(gapResult.bc); + } + + // Get FIT information from the UPC BC + upchelpers::FITInfo fitInfo{}; + udhelpers::getFITinfo(fitInfo, bcForUPC, bcs, ft0s, fv0as, fdds); + + // Get ZDC energies if available (extract once and reuse) + const bool hasZdc = bcForUPC.has_zdc(); + float zdcEnergyZNA = -1.f; + float zdcEnergyZNC = -1.f; + float zdcTimeZNA = -1.f; + float zdcTimeZNC = -1.f; + bool gapA0nXn = false; + bool gapCXn0n = false; + + if (verticesWithUpc && !upcFlag) { + continue; + } + + if (hasZdc) { + const auto zdc = bcForUPC.zdc(); + zdcEnergyZNA = zdc.energyCommonZNA(); + zdcEnergyZNC = zdc.energyCommonZNC(); + zdcTimeZNA = zdc.timeZNA(); + zdcTimeZNC = zdc.timeZNC(); + if (fillHistQa) { + registry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C); + registry.fill(HIST("Data/zdc/energyZNA_vs_energyZNC"), zdcEnergyZNA, zdcEnergyZNC); + registry.fill(HIST("Data/zdc/timeZNA_vs_timeZNC"), zdcTimeZNA, zdcTimeZNC); + } + registry.fill(HIST("Data/hUpcGapAfterSelection"), static_cast(gap)); + } + if constexpr (!IsMc) { + if (!hasZdc) { + continue; + } + } + if constexpr (IsMc) { + if (!hasZdc && fillHistQa) { + registry.fill(HIST("Data/fitInfo/ampFT0A_vs_ampFT0C"), fitInfo.ampFT0A, fitInfo.ampFT0C); + } + if (!hasZdc) { + registry.fill(HIST("Data/hUpcGapAfterSelection"), static_cast(gap)); + } + } + bool ignoreZdcTime = (zdcTimeThreshold < 0.f); + if constexpr (IsMc) { + ignoreZdcTime = ignoreZdcTime || !hasZdc; + } + const auto multNTracksPV = collision.multNTracksPV(); + const auto posZ = collision.posZ(); + if (gap == o2::aod::sgselector::TrueGap::SingleGapA && (ignoreZdcTime || (std::abs(zdcTimeZNA) > zdcTimeThreshold && std::abs(zdcTimeZNC) < zdcTimeThreshold))) { + gapA0nXn = true; + } + if (gap == o2::aod::sgselector::TrueGap::SingleGapC && (ignoreZdcTime || (std::abs(zdcTimeZNA) < zdcTimeThreshold && std::abs(zdcTimeZNC) > zdcTimeThreshold))) { + gapCXn0n = true; + } + if (fillTreeOnlySingleGap && !gapA0nXn && !gapCXn0n) { + continue; + } + registry.fill(HIST("Data/hUpcMulti"), multNTracksPV); + registry.fill(HIST("Data/hUpcVtz"), posZ); + + if (fillTreeUpcQa) { + rowUpcQa(numPvContributors, multNTracksPV, posZ, fitInfo.ampFT0A, fitInfo.ampFT0C, zdcTimeZNA, zdcTimeZNC); + } + + for (const auto& candidate : groupedLcCandidates) { + if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) { + continue; + } + if (yCandRecoMax >= 0. && std::abs(HfHelper::yLc(candidate)) > yCandRecoMax) { + continue; + } + const auto pt = candidate.pt(); + const auto ptProng0 = candidate.ptProng0(); + const auto ptProng1 = candidate.ptProng1(); + const auto ptProng2 = candidate.ptProng2(); + const auto decayLength = candidate.decayLength(); + const auto chi2PCA = candidate.chi2PCA(); + const auto cpa = candidate.cpa(); + const auto eta = candidate.eta(); + + double outputBkg(-1); + if (gapA0nXn) { + registry.fill(HIST("Data/eta_vs_Multi_A"), eta, multNTracksPV); + } + if (gapCXn0n) { + registry.fill(HIST("Data/eta_vs_Multi_C"), eta, multNTracksPV); + } + + auto fillTHnData = [&](bool isPKPi) { + const auto massLc = isPKPi ? HfHelper::invMassLcToPKPi(candidate) : HfHelper::invMassLcToPiKP(candidate); + if constexpr (IsMc) { + if constexpr (FillMl) { + const auto& mlProb = isPKPi ? candidate.mlProbLcToPKPi() : candidate.mlProbLcToPiKP(); + if (mlProb.size() == NumberOfMlClasses) { + outputBkg = mlProb[MlClassBackground]; /// bkg score + } + rowCandUpcMcBdt(massLc, pt, outputBkg, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.ptBhadMotherPart()); + } else { + rowCandUpcMc(massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.ptBhadMotherPart()); + } + return; + } + if constexpr (FillMl) { + const auto& mlProb = isPKPi ? candidate.mlProbLcToPKPi() : candidate.mlProbLcToPiKP(); + if (mlProb.size() == NumberOfMlClasses) { + outputBkg = mlProb[MlClassBackground]; /// bkg score + } + /// Fill the ML outputScores and variables of candidate + rowCandUpcBdt(massLc, pt, outputBkg); + + } else { + rowCandUpc(massLc, pt, ptProng0, ptProng1, ptProng2, chi2PCA, decayLength, cpa); + } + }; + + if (candidate.isSelLcToPKPi() >= selectionFlagLc) { + fillTHnData(true); + } + if (candidate.isSelLcToPiKP() >= selectionFlagLc) { + fillTHnData(false); + } + } + } + } + + void processDataWithMlWithUpc(soa::Join const& collisions, + aod::BcFullInfos const& bcs, + LcCandidatesMl const& selectedLcCandidatesMl, + aod::Tracks const&, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds, + aod::Zdcs const& /*zdcs*/) + { + runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidatesMl, bcs, ft0s, fv0as, fdds); + } + PROCESS_SWITCH(HfTaskUpcLc, processDataWithMlWithUpc, "Process real data with the ML method with UPC", false); + + void processDataStdWithUpc(soa::Join const& collisions, + aod::BcFullInfos const& bcs, + LcCandidates const& selectedLcCandidates, + aod::Tracks const&, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds, + aod::Zdcs const& /*zdcs*/) + { + runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidates, bcs, ft0s, fv0as, fdds); + } + PROCESS_SWITCH(HfTaskUpcLc, processDataStdWithUpc, "Process real data with the standard method with UPC", false); + + void processMcWithMlWithUpc(soa::Join const& collisions, + aod::BcFullInfos const& bcs, + LcCandidatesMlMc const& selectedLcCandidatesMlMc, + aod::McCollisions const&, + soa::Join const& mcParticles, + aod::TracksWMc const&, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds, + aod::Zdcs const& /*zdcs*/) + { + runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidatesMlMc, bcs, ft0s, fv0as, fdds, &mcParticles); + } + PROCESS_SWITCH(HfTaskUpcLc, processMcWithMlWithUpc, "Process MC with the ML method with UPC", false); + + void processMcStdWithUpc(soa::Join const& collisions, + aod::BcFullInfos const& bcs, + LcCandidatesMc const& selectedLcCandidatesMc, + aod::McCollisions const&, + soa::Join const& mcParticles, + aod::TracksWMc const&, + aod::FT0s const& ft0s, + aod::FV0As const& fv0as, + aod::FDDs const& fdds, + aod::Zdcs const& /*zdcs*/) + { + runAnalysisPerCollisionWithUpc(collisions, selectedLcCandidatesMc, bcs, ft0s, fv0as, fdds, &mcParticles); + } + PROCESS_SWITCH(HfTaskUpcLc, processMcStdWithUpc, "Process MC with the standard method with UPC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/D2H/Tasks/taskXic.cxx b/PWGHF/D2H/Tasks/taskXic.cxx index 46dfdd64521..e00f8af2153 100644 --- a/PWGHF/D2H/Tasks/taskXic.cxx +++ b/PWGHF/D2H/Tasks/taskXic.cxx @@ -125,7 +125,7 @@ struct HfTaskXic { AxisSpec const axisNSigmaPi = {100, -6.f, 6.f, "n#it{#sigma}_{#pi}"}; AxisSpec const axisNSigmaKa = {100, -6.f, 6.f, "n#it{#sigma}_{K}"}; - auto vbins = (std::vector)binsPt; // histo in pt bins + auto vbins = std::vector(binsPt); // histo in pt bins registry.add("Data/hMassVsPt", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2});;entries", {HistType::kTH2F, {{500, 2., 3.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("Data/hDecLength", "3-prong candidates;decay length (cm);;entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("Data/hDecLengthXY", "3-prong candidates;decay length XY (cm);;entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -175,8 +175,8 @@ struct HfTaskXic { registry.add("Data/hPVsTOFNSigmaKa_Prong2", "3-prong candidates;#it{p} bachelor (GeV/#it{c}) ;n#it{#sigma}_{K} TOF", {HistType::kTH2F, {axisPPid, axisNSigmaKa}}); // MC generated - registry.add("MC/generated/hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("MC/generated/hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/generated/hYGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{y}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("MC/generated/hEtaGenWithProngsInAcceptance", "MC particles (generated-daughters in acceptance);candidate #it{#eta}^{gen};entries", {HistType::kTH2F, {{100, -2., 2.}, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); // MC reconstructed:signal registry.add("MC/reconstructed/signal/hMassSig", "Invariant mass (matched);m (p K #pi) (GeV/#it{c}^{2});;entries", {HistType::kTH2F, {{500, 1.6, 3.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx b/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx index b6b709c7c55..880952751cf 100644 --- a/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx +++ b/PWGHF/D2H/Tasks/taskXicToXiPiPi.cxx @@ -110,7 +110,7 @@ struct HfTaskXicToXiPiPi { static const AxisSpec axisMassXic = {300, 1.8, 3.0, "inv. mass (GeV/#it{c}^{2})"}; static const AxisSpec axisMassXiRes = {300, 1.4, 2.7, "inv. mass (GeV/#it{c}^{2})"}; - static const AxisSpec axisPt = {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}; + static const AxisSpec axisPt = {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}; static const AxisSpec axisDecLength = {binsDecLength}; static const AxisSpec axisErrDecLength = {binsErrDecLength}; static const AxisSpec axisDCA = {binsDCA}; diff --git a/PWGHF/D2H/Utils/utilsFlow.h b/PWGHF/D2H/Utils/utilsFlow.h index 7efaedcdda3..70a4927c13b 100644 --- a/PWGHF/D2H/Utils/utilsFlow.h +++ b/PWGHF/D2H/Utils/utilsFlow.h @@ -12,20 +12,25 @@ /// \file utilsFlow.h /// \brief Utilities for flow analyses /// \author Fabrizio Grosa , CERN +/// \author Marcello Di Costanzo , Polytechnic University of Turin and INFN #ifndef PWGHF_D2H_UTILS_UTILSFLOW_H_ #define PWGHF_D2H_UTILS_UTILSFLOW_H_ #include "Common/Core/RecoDecay.h" +#include #include +#include #include #include +#include +#include +#include +#include -namespace o2::analysis -{ -namespace hf_flow_utils +namespace o2::analysis::hf_flow_utils { enum QvecEstimator { FV0A = 0, FT0M, @@ -35,6 +40,48 @@ enum QvecEstimator { FV0A = 0, TPCNeg, TPCTot }; +class HfQVectorResoHelper +{ + public: + HfQVectorResoHelper() = default; + + /// Call once per run / timestamp update + void update(o2::framework::Service& ccdb, + std::string const& basePath, + std::string const& tripletPath, + int64_t timestamp, + int runNumber) + { + if (mCurrentRun == runNumber && mResoValues != nullptr) { + return; + } + + std::string fullPath = basePath + "/" + tripletPath + "/"; + LOG(info) << "Fetching resolution array from CCDB path: " << fullPath << " for timestamp " << timestamp << " and run number " << runNumber; + + mResoValues = ccdb->getForTimeStamp>(fullPath, timestamp); + if (!mResoValues) { + LOGF(fatal, "Resolution array not found in CCDB at path: %s", fullPath.c_str()); + } + + mCurrentRun = runNumber; + } + + /// Get pointer to the full array + [[nodiscard]] const std::array* getResoValues() const { return mResoValues; } + + /// Direct array access with simple float return type + float operator[](size_t index) const + { + assert(mResoValues != nullptr && "CCDB resolution array accessed before initialization!"); + return (*mResoValues)[index]; + } + + private: + const std::array* mResoValues{nullptr}; + int64_t mCurrentRun{-1}; +}; + /// Compute the delta psi in the range [0, pi/harmonic] /// \param psi1 is the first angle /// \param psi2 is the second angle @@ -201,7 +248,98 @@ std::array getQvec(TCollision const& collision, const int qvecEst) } return std::array{-999.f, -999.f, -999.f}; } -} // namespace hf_flow_utils -} // namespace o2::analysis + +/// Get the EsE Q vector +template +concept HasEsEQvecFT0A = requires(T collision) { + collision.eseQvecFT0ARe(); + collision.eseQvecFT0AIm(); +}; + +template +concept HasEsEQvecFT0C = requires(T collision) { + collision.eseQvecFT0CRe(); + collision.eseQvecFT0CIm(); +}; + +template +concept HasEsEQvecFT0M = requires(T collision) { + collision.eseQvecFT0MRe(); + collision.eseQvecFT0MIm(); +}; + +template +concept HasEsEQvecFV0A = requires(T collision) { + collision.eseQvecFV0ARe(); + collision.eseQvecFV0AIm(); +}; + +template +concept HasEsEQvecTPCpos = requires(T collision) { + collision.eseQvecTPCposRe(); + collision.eseQvecTPCposIm(); +}; + +template +concept HasEsEQvecTPCneg = requires(T collision) { + collision.eseQvecTPCnegRe(); + collision.eseQvecTPCnegIm(); +}; + +template +concept HasEsEQvecTPCtot = requires(T collision) { + collision.eseQvecTPCallRe(); + collision.eseQvecTPCallIm(); +}; + +/// Get the Ese Q vector choosing your favourite estimator +/// \param collision is the collision with the Q vector information +/// \param qvecEst is the chosen Q-vector estimator +template +std::array getEseQvec(TCollision const& collision, const int qvecEst) +{ + switch (qvecEst) { + case QvecEstimator::FV0A: + if constexpr (HasEsEQvecFV0A) { + return std::array{collision.eseQvecFV0ARe(), collision.eseQvecFV0AIm(), collision.sumAmplFV0A()}; + } + break; + case QvecEstimator::FT0A: + if constexpr (HasEsEQvecFT0A) { + return std::array{collision.eseQvecFT0ARe(), collision.eseQvecFT0AIm(), collision.sumAmplFT0A()}; + } + break; + case QvecEstimator::FT0C: + if constexpr (HasEsEQvecFT0C) { + return std::array{collision.eseQvecFT0CRe(), collision.eseQvecFT0CIm(), collision.sumAmplFT0C()}; + } + break; + case QvecEstimator::FT0M: + if constexpr (HasEsEQvecFT0M) { + return std::array{collision.eseQvecFT0MRe(), collision.eseQvecFT0MIm(), collision.sumAmplFT0M()}; + } + break; + case QvecEstimator::TPCPos: + if constexpr (HasEsEQvecTPCpos) { + return std::array{collision.eseQvecTPCposRe(), collision.eseQvecTPCposIm(), static_cast(collision.nTrkTPCpos())}; + } + break; + case QvecEstimator::TPCNeg: + if constexpr (HasEsEQvecTPCneg) { + return std::array{collision.eseQvecTPCnegRe(), collision.eseQvecTPCnegIm(), static_cast(collision.nTrkTPCneg())}; + } + break; + case QvecEstimator::TPCTot: + if constexpr (HasEsEQvecTPCtot) { + return std::array{collision.eseQvecTPCallRe(), collision.eseQvecTPCallIm(), static_cast(collision.nTrkTPCall())}; + } + break; + default: + LOGP(fatal, "Q-vector estimator not valid. Please choose between FV0A, FT0M, FT0A, FT0C, TPCPos, TPCNeg, TPCTot"); + break; + } + return std::array{-999.f, -999.f, -999.f}; +} +} // namespace o2::analysis::hf_flow_utils #endif // PWGHF_D2H_UTILS_UTILSFLOW_H_ diff --git a/PWGHF/DataModel/CandidateReconstructionTables.h b/PWGHF/DataModel/CandidateReconstructionTables.h index 02c0e0b297b..6036b9238bc 100644 --- a/PWGHF/DataModel/CandidateReconstructionTables.h +++ b/PWGHF/DataModel/CandidateReconstructionTables.h @@ -23,7 +23,6 @@ // #include "PWGLF/DataModel/LFStrangenessTables.h" -#include "ALICE3/DataModel/ECAL.h" #include "Common/Core/RecoDecay.h" #include @@ -585,11 +584,34 @@ DECLARE_SOA_TABLE(HfCandCascadeMcGen, "AOD", "HFCANDCASCMCGEN", //! // specific BPlus candidate properties namespace hf_cand_bplus { +DECLARE_SOA_COLUMN(XSecondaryVertexD, xSecondaryVertexD, float); +DECLARE_SOA_COLUMN(YSecondaryVertexD, ySecondaryVertexD, float); +DECLARE_SOA_COLUMN(ZSecondaryVertexD, zSecondaryVertexD, float); DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand2Prong, "_0"); // D0 index DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProduct, impactParameterProduct, // Impact parameter product for B+ -> J/Psi K [](float px0, float py0, float pz0, float px1, float py1, float pz1, float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float impParK) -> float { return impParK * RecoDecay::impParXY(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, RecoDecay::pVec(std::array{px0, py0, pz0}, std::array{px1, py1, pz1})); }); DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProductJpsi, impactParameterProductJpsi, // J/Psi impact parameter for B+ -> J/Psi K [](float dcaDauPos, float dcaDauNeg) -> float { return dcaDauPos * dcaDauNeg; }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsi, cpaJpsi, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px0, float py0, float pz0, float px1, float py1, float pz1) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px0 + px1, py0 + py1, pz0 + pz1}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsiXY, cpaJpsiXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px0, float py0, float px1, float py1) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px0 + px1, py0 + py1}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPv, cpaDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPvXY, cpaDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToB, cpaDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToBXY, cpaDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPv, decayLengthDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPvXY, decayLengthDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToB, decayLengthDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToBXY, decayLengthDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}); }); enum DecayTypeMc : uint8_t { BplusToD0PiToKPiPi = 0, BplusToD0KToKPiK, @@ -613,7 +635,17 @@ DECLARE_SOA_TABLE(HfCandBplusBase, "AOD", "HFCANDBPLUSBASE", hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + // D meson decay vertex position + hf_cand_bplus::XSecondaryVertexD, hf_cand_bplus::YSecondaryVertexD, hf_cand_bplus::ZSecondaryVertexD, /* dynamic columns */ + hf_cand_bplus::CpaDToPv, + hf_cand_bplus::CpaDToPvXY, + hf_cand_bplus::CpaDToB, + hf_cand_bplus::CpaDToBXY, + hf_cand_bplus::DecayLengthDToPv, + hf_cand_bplus::DecayLengthDToPvXY, + hf_cand_bplus::DecayLengthDToB, + hf_cand_bplus::DecayLengthDToBXY, hf_cand_2prong::M, hf_cand_2prong::M2, hf_cand_2prong::ImpactParameterProduct, @@ -901,6 +933,8 @@ DECLARE_SOA_TABLE(HfCandBpJPBase, "AOD", "HFCANDBPJPBASE", hf_cand::PVector, hf_cand::Cpa, hf_cand::CpaXY, + hf_cand_bplus::CpaJpsi, + hf_cand_bplus::CpaJpsiXY, hf_cand::Ct, hf_cand::ImpactParameterXY, hf_cand_3prong::MaxNormalisedDeltaIP, @@ -927,120 +961,6 @@ enum ConstructMethod { DcaFitter = 0, KfParticle }; } // namespace hf_cand_casc_lf -namespace hf_cand_x -{ -DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand2Prong, "_0"); // Jpsi index -} // namespace hf_cand_x - -// declare dedicated X candidate table -DECLARE_SOA_TABLE(HfCandXBase, "AOD", "HFCANDXBASE", - // general columns - HFCAND_COLUMNS, - // 3-prong specific columns - hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, - hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, - hf_cand::PxProng2, hf_cand::PyProng2, hf_cand::PzProng2, - hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ImpactParameter2, - hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, hf_cand::ErrorImpactParameter2, - hf_cand_x::Prong0Id, hf_track_index::Prong1Id, hf_track_index::Prong2Id, // note the difference between Jpsi and pion indices - hf_track_index::HFflag, - /* dynamic columns */ - hf_cand_3prong::M, - hf_cand_3prong::M2, - /* prong 2 */ - hf_cand::PtProng2, - hf_cand::Pt2Prong2, - hf_cand::PVectorProng2, - /* dynamic columns that use candidate momentum components */ - hf_cand::Pt, - hf_cand::Pt2, - hf_cand::P, - hf_cand::P2, - hf_cand::PVector, - hf_cand::Cpa, - hf_cand::CpaXY, - hf_cand::Ct, - hf_cand::ImpactParameterXY, - hf_cand_3prong::MaxNormalisedDeltaIP, - hf_cand::Eta, - hf_cand::Phi, - hf_cand::Y, - hf_cand::E, - hf_cand::E2); - -// extended table with expression columns that can be used as arguments of dynamic columns -DECLARE_SOA_EXTENDED_TABLE_USER(HfCandXExt, HfCandXBase, "HFCANDXEXT", - hf_cand_3prong::Px, hf_cand_3prong::Py, hf_cand_3prong::Pz); - -using HfCandX = HfCandXExt; - -// table with results of reconstruction level MC matching -DECLARE_SOA_TABLE(HfCandXMcRec, "AOD", "HFCANDXMCREC", //! - hf_cand_mc_flag::FlagMcMatchRec, - hf_cand_mc_flag::OriginMcRec, - hf_cand_mc_flag::FlagMcDecayChanRec); - -// table with results of generator level MC matching -DECLARE_SOA_TABLE(HfCandXMcGen, "AOD", "HFCANDXMCGEN", //! - hf_cand_mc_flag::FlagMcMatchGen, - hf_cand_mc_flag::OriginMcGen, - hf_cand_mc_flag::FlagMcDecayChanGen); - -// specific Xicc candidate properties -namespace hf_cand_xicc -{ -DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand3Prong, "_0"); // Xic index -} // namespace hf_cand_xicc - -// declare dedicated Xicc candidate table -DECLARE_SOA_TABLE(HfCandXiccBase, "AOD", "HFCANDXICCBASE", - // general columns - HFCAND_COLUMNS, - // 2-prong specific columns - hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, - hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, - hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, - hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, - hf_cand_xicc::Prong0Id, hf_track_index::Prong1Id, - hf_track_index::HFflag, - /* dynamic columns */ - hf_cand_2prong::M, - hf_cand_2prong::M2, - hf_cand_2prong::ImpactParameterProduct, - /* dynamic columns that use candidate momentum components */ - hf_cand::Pt, - hf_cand::Pt2, - hf_cand::P, - hf_cand::P2, - hf_cand::PVector, - hf_cand::Cpa, - hf_cand::CpaXY, - hf_cand::Ct, - hf_cand::ImpactParameterXY, - hf_cand_2prong::MaxNormalisedDeltaIP, - hf_cand::Eta, - hf_cand::Phi, - hf_cand::Y, - hf_cand::E, - hf_cand::E2); - -// extended table with expression columns that can be used as arguments of dynamic columns -DECLARE_SOA_EXTENDED_TABLE_USER(HfCandXiccExt, HfCandXiccBase, "HFCANDXICCEXT", - hf_cand_2prong::Px, hf_cand_2prong::Py, hf_cand_2prong::Pz); - -using HfCandXicc = HfCandXiccExt; - -// table with results of reconstruction level MC matching -DECLARE_SOA_TABLE(HfCandXiccMcRec, "AOD", "HFCANDXICCMCREC", //! - hf_cand_mc_flag::FlagMcMatchRec, - hf_cand_mc_flag::OriginMcRec, - hf_cand_mc_flag::DebugMcRec); - -// table with results of generator level MC matching -DECLARE_SOA_TABLE(HfCandXiccMcGen, "AOD", "HFCANDXICCMCGEN", //! - hf_cand_mc_flag::FlagMcMatchGen, - hf_cand_mc_flag::OriginMcGen); - // specific Omegac and Xic to Xi Pi candidate properties namespace hf_cand_xic0_omegac0 { @@ -1302,6 +1222,9 @@ DECLARE_SOA_DYNAMIC_COLUMN(PtPiFromCharmBaryon, ptPiFromCharmBaryon, DECLARE_SOA_DYNAMIC_COLUMN(PtKaFromCasc, ptKaFromCasc, [](float px, float py) -> float { return RecoDecay::pt(px, py); }); +DECLARE_SOA_DYNAMIC_COLUMN(NormDecayLenCharmBar, normDecayLenCharmBar, + [](float decLen, float errDecLen) -> float { return decLen / errDecLen; }); + } // end of namespace hf_cand_xic0_omegac0 // declare dedicated Omegac and Xic to Xi Pi candidate table @@ -1368,7 +1291,8 @@ DECLARE_SOA_TABLE(HfCandToOmegaPi, "AOD", "HFCANDTOOMEGAPI", hf_cand_xic0_omegac0::DcaXYToPvV0Dau0, hf_cand_xic0_omegac0::DcaXYToPvV0Dau1, hf_cand_xic0_omegac0::DcaXYToPvCascDau, hf_cand_xic0_omegac0::DcaZToPvV0Dau0, hf_cand_xic0_omegac0::DcaZToPvV0Dau1, hf_cand_xic0_omegac0::DcaZToPvCascDau, hf_cand_xic0_omegac0::DcaCascDau, hf_cand_xic0_omegac0::DcaV0Dau, hf_cand_xic0_omegac0::DcaCharmBaryonDau, - hf_cand_xic0_omegac0::DecLenCharmBaryon, hf_cand_xic0_omegac0::DecLenCascade, hf_cand_xic0_omegac0::DecLenV0, hf_cand_xic0_omegac0::ErrorDecayLengthCharmBaryon, hf_cand_xic0_omegac0::ErrorDecayLengthXYCharmBaryon, hf_track_index::HFflag); + hf_cand_xic0_omegac0::DecLenCharmBaryon, hf_cand_xic0_omegac0::DecLenCascade, hf_cand_xic0_omegac0::DecLenV0, hf_cand_xic0_omegac0::ErrorDecayLengthCharmBaryon, hf_cand_xic0_omegac0::ErrorDecayLengthXYCharmBaryon, hf_track_index::HFflag, + hf_cand_xic0_omegac0::NormDecayLenCharmBar); DECLARE_SOA_TABLE(HfCandToOmegaK, "AOD", "HFCANDTOOMEGAK", o2::soa::Index<>, @@ -1565,7 +1489,9 @@ DECLARE_SOA_COLUMN(CpaLambda, cpaLambda, float); DECLARE_SOA_COLUMN(CpaXYLambda, cpaXYLambda, float); DECLARE_SOA_COLUMN(CpaLambdaToXi, cpaLambdaToXi, float); DECLARE_SOA_COLUMN(CpaXYLambdaToXi, cpaXYLambdaToXi, float); +DECLARE_SOA_COLUMN(RadiusLambda, radiusLambda, float); DECLARE_SOA_COLUMN(PBachelorPi, pBachelorPi, float); +DECLARE_SOA_COLUMN(PtBachelorPi, ptBachelorPi, float); DECLARE_SOA_COLUMN(PPiFromLambda, pPiFromLambda, float); DECLARE_SOA_COLUMN(PPrFromLambda, pPrFromLambda, float); DECLARE_SOA_COLUMN(DcaXiDaughters, dcaXiDaughters, float); @@ -1577,10 +1503,10 @@ DECLARE_SOA_COLUMN(DcaXYCascToPV, dcaXYCascToPV, float); DECLARE_SOA_COLUMN(DcaZCascToPV, dcaZCascToPV, float); // KF specific columns DECLARE_SOA_COLUMN(Chi2TopoXicPlusToPV, chi2TopoXicPlusToPV, float); -DECLARE_SOA_COLUMN(Chi2TopoXicPlusToPVBefConst, chi2TopoXicPlusToPVBefConst, float); -DECLARE_SOA_COLUMN(Chi2PrimXi, chi2PrimXi, float); -DECLARE_SOA_COLUMN(Chi2PrimPi0, chi2PrimPi0, float); -DECLARE_SOA_COLUMN(Chi2PrimPi1, chi2PrimPi1, float); +DECLARE_SOA_COLUMN(Chi2XicPlusToPV, chi2XicPlusToPV, float); +DECLARE_SOA_COLUMN(Chi2XiToPV, chi2XiToPV, float); +DECLARE_SOA_COLUMN(Chi2Pi0ToPV, chi2Pi0ToPV, float); +DECLARE_SOA_COLUMN(Chi2Pi1ToPV, chi2Pi1ToPV, float); DECLARE_SOA_COLUMN(Chi2DevPi0Pi1, chi2DevPi0Pi1, float); DECLARE_SOA_COLUMN(Chi2DevPi0Xi, chi2DevPi0Xi, float); DECLARE_SOA_COLUMN(Chi2DevPi1Xi, chi2DevPi1Xi, float); @@ -1607,6 +1533,7 @@ DECLARE_SOA_COLUMN(NSigTofPiFromLambda, nSigTofPiFromLambda, float); DECLARE_SOA_COLUMN(NSigTofPrFromLambda, nSigTofPrFromLambda, float); // MC DECLARE_SOA_COLUMN(DecayLengthMcGen, decayLengthMcGen, float); +DECLARE_SOA_COLUMN(MassMcGen, massMcGen, float); // Residuals and pulls DECLARE_SOA_COLUMN(PtResidual, ptResidual, float); DECLARE_SOA_COLUMN(PResidual, pResidual, float); @@ -1650,9 +1577,10 @@ DECLARE_SOA_TABLE(HfCandXicBase, "AOD", "HFCANDXICBASE", hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ImpactParameter2, hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, hf_cand::ErrorImpactParameter2, // cascade specific columns - hf_cand_xic_to_xi_pi_pi::PBachelorPi, hf_cand_xic_to_xi_pi_pi::PPiFromLambda, hf_cand_xic_to_xi_pi_pi::PPrFromLambda, + hf_cand_xic_to_xi_pi_pi::PBachelorPi, hf_cand_xic_to_xi_pi_pi::PPiFromLambda, hf_cand_xic_to_xi_pi_pi::PPrFromLambda, hf_cand_xic_to_xi_pi_pi::PtBachelorPi, hf_cand_xic_to_xi_pi_pi::CpaXi, hf_cand_xic_to_xi_pi_pi::CpaXYXi, hf_cand_xic_to_xi_pi_pi::CpaLambda, hf_cand_xic_to_xi_pi_pi::CpaXYLambda, hf_cand_xic_to_xi_pi_pi::CpaLambdaToXi, hf_cand_xic_to_xi_pi_pi::CpaXYLambdaToXi, hf_cand_xic_to_xi_pi_pi::InvMassXi, hf_cand_xic_to_xi_pi_pi::InvMassLambda, hf_cand_xic_to_xi_pi_pi::InvMassXiPi0, hf_cand_xic_to_xi_pi_pi::InvMassXiPi1, + hf_cand_xic_to_xi_pi_pi::RadiusLambda, // DCA hf_cand_xic_to_xi_pi_pi::DcaXiDaughters, hf_cand_xic_to_xi_pi_pi::DcaV0Daughters, hf_cand_xic_to_xi_pi_pi::DcaPosToPV, hf_cand_xic_to_xi_pi_pi::DcaNegToPV, hf_cand_xic_to_xi_pi_pi::DcaBachelorToPV, hf_cand_xic_to_xi_pi_pi::DcaXYCascToPV, hf_cand_xic_to_xi_pi_pi::DcaZCascToPV, // PID @@ -1695,8 +1623,8 @@ using HfCandXic = HfCandXicExt; DECLARE_SOA_TABLE(HfCandXicKF, "AOD", "HFCANDXICKF", hf_cand_xic_to_xi_pi_pi::KfDecayLength, hf_cand_xic_to_xi_pi_pi::KfDecayLengthNormalised, hf_cand_xic_to_xi_pi_pi::KfDecayLengthXY, hf_cand_xic_to_xi_pi_pi::KfDecayLengthXYNormalised, cascdata::KFCascadeChi2, cascdata::KFV0Chi2, - hf_cand_xic_to_xi_pi_pi::Chi2TopoXicPlusToPVBefConst, hf_cand_xic_to_xi_pi_pi::Chi2TopoXicPlusToPV, - hf_cand_xic_to_xi_pi_pi::Chi2PrimXi, hf_cand_xic_to_xi_pi_pi::Chi2PrimPi0, hf_cand_xic_to_xi_pi_pi::Chi2PrimPi1, + hf_cand_xic_to_xi_pi_pi::Chi2XicPlusToPV, hf_cand_xic_to_xi_pi_pi::Chi2TopoXicPlusToPV, + hf_cand_xic_to_xi_pi_pi::Chi2XiToPV, hf_cand_xic_to_xi_pi_pi::Chi2Pi0ToPV, hf_cand_xic_to_xi_pi_pi::Chi2Pi1ToPV, hf_cand_xic_to_xi_pi_pi::Chi2DevPi0Pi1, hf_cand_xic_to_xi_pi_pi::Chi2DevPi0Xi, hf_cand_xic_to_xi_pi_pi::Chi2DevPi1Xi, hf_cand_xic_to_xi_pi_pi::DcaPi0Pi1, hf_cand_xic_to_xi_pi_pi::DcaPi0Xi, hf_cand_xic_to_xi_pi_pi::DcaPi1Xi, hf_cand_xic_to_xi_pi_pi::DcaXYPi0Pi1, hf_cand_xic_to_xi_pi_pi::DcaXYPi0Xi, hf_cand_xic_to_xi_pi_pi::DcaXYPi1Xi); @@ -1711,7 +1639,8 @@ DECLARE_SOA_TABLE(HfCandXicMcGen, "AOD", "HFCANDXICMCGEN", hf_cand_mc_flag::FlagMcMatchGen, hf_cand_mc_flag::OriginMcGen, hf_cand_mc_flag::PdgBhadMotherPart, - hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen); + hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen, + hf_cand_xic_to_xi_pi_pi::MassMcGen); // table with residuals and pulls of PV DECLARE_SOA_TABLE(HfCandXicResid, "AOD", "HFCANDXICRESID", @@ -1731,67 +1660,6 @@ DECLARE_SOA_TABLE(HfCandXicResid, "AOD", "HFCANDXICRESID", hf_cand_xic_to_xi_pi_pi::YSvPull, hf_cand_xic_to_xi_pi_pi::ZSvPull); -// specific chic candidate properties -namespace hf_cand_chic -{ -DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand2Prong, "_0"); // Jpsi index -DECLARE_SOA_INDEX_COLUMN_FULL(Prong1, prong1, int, ECALs, "_1"); -DECLARE_SOA_COLUMN(JpsiToMuMuMass, jpsiToMuMuMass, float); // Jpsi mass -} // namespace hf_cand_chic - -// declare dedicated chi_c candidate table -DECLARE_SOA_TABLE(HfCandChicBase, "AOD", "HFCANDCHICBASE", - // general columns - HFCAND_COLUMNS, - // 2-prong specific columns - hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, - hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, - hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, - hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, - hf_cand_chic::Prong0Id, hf_cand_chic::Prong1Id, - hf_track_index::HFflag, hf_cand_chic::JpsiToMuMuMass, - /* dynamic columns */ - hf_cand_2prong::M, - hf_cand_2prong::M2, - /* prong 2 */ - // hf_cand::PtProng1, - // hf_cand::Pt2Prong1, - // hf_cand::PVectorProng1, - /* dynamic columns that use candidate momentum components */ - hf_cand::Pt, - hf_cand::Pt2, - hf_cand::P, - hf_cand::P2, - hf_cand::PVector, - hf_cand::Cpa, - hf_cand::CpaXY, - hf_cand::Ct, - hf_cand::ImpactParameterXY, - hf_cand_2prong::MaxNormalisedDeltaIP, - hf_cand::Eta, - hf_cand::Phi, - hf_cand::Y, - hf_cand::E, - hf_cand::E2); - -// extended table with expression columns that can be used as arguments of dynamic columns -DECLARE_SOA_EXTENDED_TABLE_USER(HfCandChicExt, HfCandChicBase, "HFCANDCHICEXT", - hf_cand_2prong::Px, hf_cand_2prong::Py, hf_cand_2prong::Pz); - -using HfCandChic = HfCandChicExt; - -// table with results of reconstruction level MC matching -DECLARE_SOA_TABLE(HfCandChicMcRec, "AOD", "HFCANDCHICMCREC", //! - hf_cand_mc_flag::FlagMcMatchRec, - hf_cand_mc_flag::OriginMcRec, - hf_cand_mc_flag::FlagMcDecayChanRec); - -// table with results of generator level MC matching -DECLARE_SOA_TABLE(HfCandChicMcGen, "AOD", "HFCANDCHICMCGEN", //! - hf_cand_mc_flag::FlagMcMatchGen, - hf_cand_mc_flag::OriginMcGen, - hf_cand_mc_flag::FlagMcDecayChanGen); - // specific Lb candidate properties namespace hf_cand_lb { @@ -1860,7 +1728,36 @@ DECLARE_SOA_TABLE(HfCandLbMcGen, "AOD", "HFCANDLBMCGEN", //! // specific B0 candidate properties namespace hf_cand_b0 { -DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand3Prong, "_0"); // D index +DECLARE_SOA_COLUMN(XSecondaryVertexD, xSecondaryVertexD, float); +DECLARE_SOA_COLUMN(YSecondaryVertexD, ySecondaryVertexD, float); +DECLARE_SOA_COLUMN(ZSecondaryVertexD, zSecondaryVertexD, float); +DECLARE_SOA_INDEX_COLUMN_FULL(Prong0, prong0, int, HfCand3Prong, "_0"); // D index +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProduct, impactParameterProduct, // Impact parameter product for B0 -> J/Psi K*0 + [](float pxJpsiDauPos, float pyJpsiDauPos, float pzJpsiDauPos, float pxJpsiDauNeg, float pyJpsiDauNeg, float pzJpsiDauNeg, float pxLfTrack0, float pyLfTrack0, float pzLfTrack0, float pxLfTrack1, float pyLfTrack1, float pzLfTrack1, float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS) -> float { + float impParJpsi = RecoDecay::impParXY(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, RecoDecay::pVec(std::array{pxJpsiDauPos, pyJpsiDauPos, pzJpsiDauPos}, std::array{pxJpsiDauNeg, pyJpsiDauNeg, pzJpsiDauNeg})); + float impParK0Star = RecoDecay::impParXY(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, RecoDecay::pVec(std::array{pxLfTrack0, pyLfTrack0, pzLfTrack0}, std::array{pxLfTrack1, pyLfTrack1, pzLfTrack1})); + return impParJpsi * impParK0Star; + }); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProductJpsi, impactParameterProductJpsi, // J/Psi impact parameter product for B0 -> J/Psi K*0 + [](float dcaDauPos, float dcaDauNeg) -> float { return dcaDauPos * dcaDauNeg; }); +DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterProductK0Star, impactParameterProductK0Star, // K*0 impact parameter product for B0 -> J/Psi K*0 + [](float dcaLfTrack0, float dcaLfTrack1) -> float { return dcaLfTrack0 * dcaLfTrack1; }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPv, cpaDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToPvXY, cpaDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToB, cpaDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaDToBXY, cpaDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS, float px, float py) -> float { return RecoDecay::cpaXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}, std::array{px, py}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPv, decayLengthDToPv, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToPvXY, decayLengthDToPvXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToB, decayLengthDToB, //! + [](float xVtxB, float yVtxB, float zVtxB, float xVtxS, float yVtxS, float zVtxS) -> float { return RecoDecay::distance(std::array{xVtxB, yVtxB, zVtxB}, std::array{xVtxS, yVtxS, zVtxS}); }); +DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthDToBXY, decayLengthDToBXY, //! + [](float xVtxB, float yVtxB, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxB, yVtxB}, std::array{xVtxS, yVtxS}); }); enum DecayTypeMc : uint8_t { B0ToDplusPiToPiKPiPi = 0, B0ToDsPiToKKPiPi, @@ -1881,7 +1778,17 @@ DECLARE_SOA_TABLE(HfCandB0Base, "AOD", "HFCANDB0BASE", hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + // D meson decay vertex position + hf_cand_b0::XSecondaryVertexD, hf_cand_b0::YSecondaryVertexD, hf_cand_b0::ZSecondaryVertexD, /* dynamic columns */ + hf_cand_b0::CpaDToPv, + hf_cand_b0::CpaDToPvXY, + hf_cand_b0::CpaDToB, + hf_cand_b0::CpaDToBXY, + hf_cand_b0::DecayLengthDToPv, + hf_cand_b0::DecayLengthDToPvXY, + hf_cand_b0::DecayLengthDToB, + hf_cand_b0::DecayLengthDToBXY, hf_cand_2prong::M, hf_cand_2prong::M2, hf_cand_2prong::ImpactParameterProduct, @@ -1918,7 +1825,17 @@ DECLARE_SOA_TABLE(HfCandB0DStar, "AOD", "HFCANDB0DSTAR", hf_cand::PxProng2, hf_cand::PyProng2, hf_cand::PzProng2, hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ImpactParameter2, hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, hf_cand::ErrorImpactParameter2, + // D meson decay vertex position + hf_cand_b0::XSecondaryVertexD, hf_cand_b0::YSecondaryVertexD, hf_cand_b0::ZSecondaryVertexD, /* dynamic columns */ + hf_cand_b0::CpaDToPv, + hf_cand_b0::CpaDToPvXY, + hf_cand_b0::CpaDToB, + hf_cand_b0::CpaDToBXY, + hf_cand_b0::DecayLengthDToPv, + hf_cand_b0::DecayLengthDToPvXY, + hf_cand_b0::DecayLengthDToB, + hf_cand_b0::DecayLengthDToBXY, hf_cand_3prong::M, hf_cand_3prong::M2, hf_cand_2prong::ImpactParameterProduct, @@ -2068,8 +1985,69 @@ DECLARE_SOA_DYNAMIC_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, //! [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float errDlxy, float pxM, float pyM, float ip0, float errIp0, float ip1, float errIp1, float ip2, float errIp2, float ip3, float errIp3, float px0, float py0, float px1, float py1, float px2, float py2, float px3, float py3) -> float { return RecoDecay::maxNormalisedDeltaIP(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, errDlxy, std::array{pxM, pyM}, std::array{ip0, ip1, ip2, ip3}, std::array{errIp0, errIp1, errIp2, errIp3}, std::array{std::array{px0, py0}, std::array{px1, py1}, std::array{px2, py2}, std::array{px3, py3}}); }); DECLARE_SOA_DYNAMIC_COLUMN(CtXY, ctXY, //! [](float px0, float py0, float pz0, float px1, float py1, float pz1, float px2, float py2, float pz2, float px3, float py3, float pz3, float xVtxP, float yVtxP, float xVtxS, float yVtxS, const std::array& m) -> float { return RecoDecay::ctXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{std::array{px0, py0, pz0}, std::array{px1, py1, pz1}, std::array{px2, py2, pz2}, std::array{px3, py3, pz3}}, m); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsi, cpaJpsi, //! + [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px0, float py0, float pz0, float px1, float py1, float pz1) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px0 + px1, py0 + py1, pz0 + pz1}); }); +DECLARE_SOA_DYNAMIC_COLUMN(CpaJpsiXY, cpaJpsiXY, //! + [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float px0, float py0, float px1, float py1) -> float { return RecoDecay::cpaXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}, std::array{px0 + px1, py0 + py1}); }); } // namespace hf_cand_4prong +// declare dedicated B0 -> J/Psi K*0 decay candidate table +// convention: prongs 0 and 1 should be J/Psi decay products, 2 and 3 should be K*0 decay products +DECLARE_SOA_TABLE(HfCandB0JPBase, "AOD", "HFCANDB0JPBASE", + // general columns + HFCAND_COLUMNS, + /* prong 2 */ hf_cand::ImpactParameterNormalised2, + hf_cand::PtProng2, + hf_cand::Pt2Prong2, + hf_cand::PVectorProng2, + /* prong 3 */ hf_cand::ImpactParameterNormalised3, + hf_cand::PtProng3, + hf_cand::Pt2Prong3, + hf_cand::PVectorProng3, + // 4-prong specific columns + o2::soa::Index<>, + hf_cand::PxProng0, hf_cand::PyProng0, hf_cand::PzProng0, + hf_cand::PxProng1, hf_cand::PyProng1, hf_cand::PzProng1, + hf_cand::PxProng2, hf_cand::PyProng2, hf_cand::PzProng2, + hf_cand::PxProng3, hf_cand::PyProng3, hf_cand::PzProng3, + hf_cand::ImpactParameter0, hf_cand::ImpactParameter1, hf_cand::ImpactParameter2, hf_cand::ImpactParameter3, + hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, hf_cand::ErrorImpactParameter2, hf_cand::ErrorImpactParameter3, + /* dynamic columns */ + hf_cand_4prong::M, + hf_cand_4prong::M2, + hf_cand_4prong::ImpactParameterProngSqSum, + hf_cand_b0::ImpactParameterProduct, + hf_cand_b0::ImpactParameterProductJpsi, + hf_cand_b0::ImpactParameterProductK0Star, + /* dynamic columns that use candidate momentum components */ + hf_cand::Pt, + hf_cand::Pt2, + hf_cand::P, + hf_cand::P2, + hf_cand::PVector, + hf_cand::Cpa, + hf_cand::CpaXY, + hf_cand_4prong::CpaJpsi, + hf_cand_4prong::CpaJpsiXY, + hf_cand::Ct, + hf_cand::ImpactParameterXY, + hf_cand_4prong::MaxNormalisedDeltaIP, + hf_cand::Eta, + hf_cand::Phi, + hf_cand::Y, + hf_cand::E, + hf_cand::E2, + hf_cand_4prong::CtXY); + +// extended table with expression columns that can be used as arguments of dynamic columns +DECLARE_SOA_EXTENDED_TABLE_USER(HfCandB0JPExt, HfCandB0JPBase, "HFCANDB0JPEXT", + hf_cand_4prong::Px, hf_cand_4prong::Py, hf_cand_4prong::Pz); + +DECLARE_SOA_TABLE(HfCandB0JPDaus, "AOD", "HFCANDB0JPDAUS", + hf_cand_b0::Prong0Id, hf_track_index::Prong1Id, hf_track_index::Prong2Id, hf_track_index::Prong3Id); + +using HfCandB0ToJpsi = soa::Join; + // declare dedicated Bs -> J/Psi phi decay candidate table // convention: prongs 0 and 1 should be J/Psi decay products, 2 and 3 should be phi decay products DECLARE_SOA_TABLE(HfCandBsJPBase, "AOD", "HFCANDBSJPBASE", @@ -2106,6 +2084,8 @@ DECLARE_SOA_TABLE(HfCandBsJPBase, "AOD", "HFCANDBSJPBASE", hf_cand::PVector, hf_cand::Cpa, hf_cand::CpaXY, + hf_cand_4prong::CpaJpsi, + hf_cand_4prong::CpaJpsiXY, hf_cand::Ct, hf_cand::ImpactParameterXY, hf_cand_4prong::MaxNormalisedDeltaIP, diff --git a/PWGHF/DataModel/CandidateSelectionTables.h b/PWGHF/DataModel/CandidateSelectionTables.h index a359794da92..0e06ab6797c 100644 --- a/PWGHF/DataModel/CandidateSelectionTables.h +++ b/PWGHF/DataModel/CandidateSelectionTables.h @@ -56,56 +56,6 @@ DECLARE_SOA_TABLE(HfMlD0, "AOD", "HFMLD0", //! hf_sel_candidate_d0::MlProbD0, hf_sel_candidate_d0::MlProbD0bar); -namespace hf_sel_candidate_d0_parametrized_pid -{ -DECLARE_SOA_COLUMN(IsSelD0NoPid, isSelD0NoPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0PerfectPid, isSelD0PerfectPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0, isSelD0, int); //! -DECLARE_SOA_COLUMN(IsSelD0barNoPid, isSelD0barNoPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0barPerfectPid, isSelD0barPerfectPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0bar, isSelD0bar, int); //! -} // namespace hf_sel_candidate_d0_parametrized_pid - -DECLARE_SOA_TABLE(HfSelD0ParametrizedPid, "AOD", "HFSELD0P", //! - hf_sel_candidate_d0_parametrized_pid::IsSelD0NoPid, - hf_sel_candidate_d0_parametrized_pid::IsSelD0PerfectPid, - hf_sel_candidate_d0_parametrized_pid::IsSelD0, - hf_sel_candidate_d0_parametrized_pid::IsSelD0barNoPid, - hf_sel_candidate_d0_parametrized_pid::IsSelD0barPerfectPid, - hf_sel_candidate_d0_parametrized_pid::IsSelD0bar); - -namespace hf_sel_candidate_d0_alice3_barrel -{ -DECLARE_SOA_COLUMN(IsSelHfFlag, isSelHfFlag, int); //! -DECLARE_SOA_COLUMN(IsSelD0NoPid, isSelD0NoPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0PerfectPid, isSelD0PerfectPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0TofPid, isSelD0TofPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0RichPid, isSelD0RichPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0TofPlusRichPid, isSelD0TofPlusRichPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0barTofPlusRichPid, isSelD0barTofPlusRichPid, int); //! -} // namespace hf_sel_candidate_d0_alice3_barrel - -DECLARE_SOA_TABLE(HfSelD0Alice3Barrel, "AOD", "HFSELD0A3B", //! - hf_sel_candidate_d0_alice3_barrel::IsSelHfFlag, - hf_sel_candidate_d0_alice3_barrel::IsSelD0NoPid, - hf_sel_candidate_d0_alice3_barrel::IsSelD0PerfectPid, - hf_sel_candidate_d0_alice3_barrel::IsSelD0TofPid, - hf_sel_candidate_d0_alice3_barrel::IsSelD0RichPid, - hf_sel_candidate_d0_alice3_barrel::IsSelD0TofPlusRichPid, - hf_sel_candidate_d0_alice3_barrel::IsSelD0barTofPlusRichPid); - -namespace hf_sel_candidate_d0_alice3_forward -{ -DECLARE_SOA_COLUMN(IsSelHfFlag, isSelHfFlag, int); //! -DECLARE_SOA_COLUMN(IsSelD0FNoPid, isSelD0FNoPid, int); //! -DECLARE_SOA_COLUMN(IsSelD0FRichPid, isSelD0FRichPid, int); //! -} // namespace hf_sel_candidate_d0_alice3_forward - -DECLARE_SOA_TABLE(HfSelD0Alice3Forward, "AOD", "HFSELD0A3F", //! - hf_sel_candidate_d0_alice3_forward::IsSelHfFlag, - hf_sel_candidate_d0_alice3_forward::IsSelD0FNoPid, - hf_sel_candidate_d0_alice3_forward::IsSelD0FRichPid); - namespace hf_sel_candidate_dplus { DECLARE_SOA_COLUMN(IsSelDplusToPiKPi, isSelDplusToPiKPi, int); //! @@ -188,70 +138,6 @@ DECLARE_SOA_COLUMN(IsSelChToPiKHe, isSelChToPiKHe, int); //! DECLARE_SOA_TABLE(HfSelCh, "AOD", "HFSELCH", //! hf_sel_candidate_ch::IsSelChToHeKPi, hf_sel_candidate_ch::IsSelChToPiKHe); -namespace hf_sel_candidate_lc_alice3 -{ -DECLARE_SOA_COLUMN(IsSelLcToPKPiNoPid, isSelLcToPKPiNoPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPKPiPerfectPid, isSelLcToPKPiPerfectPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPKPiTofPid, isSelLcToPKPiTofPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPKPiTofPlusRichPid, isSelLcToPKPiTofPlusRichPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPiKPNoPid, isSelLcToPiKPNoPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPiKPPerfectPid, isSelLcToPiKPPerfectPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPiKPTofPid, isSelLcToPiKPTofPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPiKPTofPlusRichPid, isSelLcToPiKPTofPlusRichPid, int); //! -} // namespace hf_sel_candidate_lc_alice3 - -DECLARE_SOA_TABLE(HfSelLcAlice3, "AOD", "HFSELLCA3B", //! - hf_sel_candidate_lc_alice3::IsSelLcToPKPiNoPid, - hf_sel_candidate_lc_alice3::IsSelLcToPKPiPerfectPid, - hf_sel_candidate_lc_alice3::IsSelLcToPKPiTofPid, - hf_sel_candidate_lc_alice3::IsSelLcToPKPiTofPlusRichPid, - hf_sel_candidate_lc_alice3::IsSelLcToPiKPNoPid, - hf_sel_candidate_lc_alice3::IsSelLcToPiKPPerfectPid, - hf_sel_candidate_lc_alice3::IsSelLcToPiKPTofPid, - hf_sel_candidate_lc_alice3::IsSelLcToPiKPTofPlusRichPid); - -namespace hf_sel_candidate_lc_parametrized_pid -{ -DECLARE_SOA_COLUMN(IsSelLcToPKPiNoPid, isSelLcToPKPiNoPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPKPiPerfectPid, isSelLcToPKPiPerfectPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPKPi, isSelLcToPKPi, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPiKPNoPid, isSelLcToPiKPNoPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPiKPPerfectPid, isSelLcToPiKPPerfectPid, int); //! -DECLARE_SOA_COLUMN(IsSelLcToPiKP, isSelLcToPiKP, int); //! -} // namespace hf_sel_candidate_lc_parametrized_pid - -DECLARE_SOA_TABLE(HfSelLcParametrizedPid, "AOD", "HFSELLCP", //! - hf_sel_candidate_lc_parametrized_pid::IsSelLcToPKPiNoPid, - hf_sel_candidate_lc_parametrized_pid::IsSelLcToPKPiPerfectPid, - hf_sel_candidate_lc_parametrized_pid::IsSelLcToPKPi, - hf_sel_candidate_lc_parametrized_pid::IsSelLcToPiKPNoPid, - hf_sel_candidate_lc_parametrized_pid::IsSelLcToPiKPPerfectPid, - hf_sel_candidate_lc_parametrized_pid::IsSelLcToPiKP); - -namespace hf_sel_candidate_jpsi -{ -DECLARE_SOA_COLUMN(IsSelJpsiToEE, isSelJpsiToEE, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToMuMu, isSelJpsiToMuMu, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToEETopol, isSelJpsiToEETopol, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToEETpc, isSelJpsiToEETpc, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToEETof, isSelJpsiToEETof, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToEERich, isSelJpsiToEERich, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToEETofRich, isSelJpsiToEETofRich, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToMuMuTopol, isSelJpsiToMuMuTopol, int); //! -DECLARE_SOA_COLUMN(IsSelJpsiToMuMuMid, isSelJpsiToMuMuMid, int); //! -} // namespace hf_sel_candidate_jpsi - -DECLARE_SOA_TABLE(HfSelJpsi, "AOD", "HFSELJPSI", //! - hf_sel_candidate_jpsi::IsSelJpsiToEE, - hf_sel_candidate_jpsi::IsSelJpsiToMuMu, - hf_sel_candidate_jpsi::IsSelJpsiToEETopol, - hf_sel_candidate_jpsi::IsSelJpsiToEETpc, - hf_sel_candidate_jpsi::IsSelJpsiToEETof, - hf_sel_candidate_jpsi::IsSelJpsiToEERich, - hf_sel_candidate_jpsi::IsSelJpsiToEETofRich, - hf_sel_candidate_jpsi::IsSelJpsiToMuMuTopol, - hf_sel_candidate_jpsi::IsSelJpsiToMuMuMid); - namespace hf_sel_candidate_lc_to_k0s_p { DECLARE_SOA_COLUMN(IsSelLcToK0sP, isSelLcToK0sP, int); @@ -315,24 +201,6 @@ DECLARE_SOA_TABLE(HfSelLbToLcPi, "AOD", "HFSELLB", //! DECLARE_SOA_TABLE(HfMlLbToLcPi, "AOD", "HFMLLB", //! hf_sel_candidate_lb::MlProbLbToLcPi); -namespace hf_sel_candidate_x -{ -DECLARE_SOA_COLUMN(IsSelXToJpsiToEEPiPi, isSelXToJpsiToEEPiPi, int); //! -DECLARE_SOA_COLUMN(IsSelXToJpsiToMuMuPiPi, isSelXToJpsiToMuMuPiPi, int); //! -} // namespace hf_sel_candidate_x - -DECLARE_SOA_TABLE(HfSelXToJpsiPiPi, "AOD", "HFSELX", //! - hf_sel_candidate_x::IsSelXToJpsiToEEPiPi, hf_sel_candidate_x::IsSelXToJpsiToMuMuPiPi); - -namespace hf_sel_candidate_chic -{ -DECLARE_SOA_COLUMN(IsSelChicToJpsiToEEGamma, isSelChicToJpsiToEEGamma, int); //! -DECLARE_SOA_COLUMN(IsSelChicToJpsiToMuMuGamma, isSelChicToJpsiToMuMuGamma, int); //! -} // namespace hf_sel_candidate_chic - -DECLARE_SOA_TABLE(HfSelChicToJpsiGamma, "AOD", "HFSELCHIC", //! - hf_sel_candidate_chic::IsSelChicToJpsiToEEGamma, hf_sel_candidate_chic::IsSelChicToJpsiToMuMuGamma); - namespace hf_sel_candidate_xic { // XicPlus to P K Pi @@ -363,14 +231,6 @@ DECLARE_SOA_TABLE(HfSelXicToXiPiPi, "AOD", "HFSELXICTOXI2PI", //! DECLARE_SOA_TABLE(HfMlXicToXiPiPi, "AOD", "HFMLXICTOXI2PI", //! hf_sel_candidate_xic::MlProbXicToXiPiPi); -namespace hf_sel_candidate_xicc -{ -DECLARE_SOA_COLUMN(IsSelXiccToPKPiPi, isSelXiccToPKPiPi, int); //! -} // namespace hf_sel_candidate_xicc - -DECLARE_SOA_TABLE(HfSelXiccToPKPiPi, "AOD", "HFSELXICC", //! - hf_sel_candidate_xicc::IsSelXiccToPKPiPi); - namespace hf_sel_toxipi { DECLARE_SOA_COLUMN(StatusPidLambda, statusPidLambda, bool); diff --git a/PWGHF/DataModel/DerivedTables.h b/PWGHF/DataModel/DerivedTables.h index 359a2320b3a..6d83b436957 100644 --- a/PWGHF/DataModel/DerivedTables.h +++ b/PWGHF/DataModel/DerivedTables.h @@ -314,10 +314,14 @@ DECLARE_TABLES_3P(Dstar, "DST", dstar, 6); DECLARE_TABLES_3P(Ds, "DS", ds, 9); DECLARE_TABLES_3P(LcToK0sP, "LCC", lc_to_k0s_p, 10); // Workaround for the existing B0 macro in termios.h +#ifdef B0 #pragma push_macro("B0") #undef B0 DECLARE_TABLES_4P(B0, "B0", b0, 7); #pragma pop_macro("B0") +#else +DECLARE_TABLES_4P(B0, "B0", b0, 7); +#endif DECLARE_TABLES_5P(XicToXiPiPi, "XICXPP", xic_to_xi_pi_pi, 8); // ================ diff --git a/PWGHF/HFC/DataModel/CorrelationTables.h b/PWGHF/HFC/DataModel/CorrelationTables.h index 62414101df2..fcea57be77c 100644 --- a/PWGHF/HFC/DataModel/CorrelationTables.h +++ b/PWGHF/HFC/DataModel/CorrelationTables.h @@ -51,6 +51,57 @@ DECLARE_SOA_TABLE(DDbarRecoInfo, "AOD", "DDBARRECOINFO", aod::hf_correlation_d_dbar::MDbar, aod::hf_correlation_d_dbar::SignalStatus); +// Table to store event by event D meson selection information for event mixing in D-hadron correlation analysis +namespace hf_d0_meson +{ +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(PtD, ptD, float); +DECLARE_SOA_COLUMN(MD, mD, float); +DECLARE_SOA_COLUMN(PoolBin, poolBin, int); +DECLARE_SOA_COLUMN(GIndexCol, gIndexCol, int); +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, int64_t); +DECLARE_SOA_COLUMN(SignalStatus, signalStatus, int); +} // namespace hf_d0_meson + +DECLARE_SOA_TABLE(D0, "AOD", "D0", + aod::hf_d0_meson::Phi, + aod::hf_d0_meson::Eta, + aod::hf_d0_meson::PtD, + aod::hf_d0_meson::MD, + aod::hf_d0_meson::PoolBin, + aod::hf_d0_meson::GIndexCol, + aod::hf_d0_meson::TimeStamp, + aod::hf_d0_meson::SignalStatus); + +// Table to store event-by-event associated tracks for offline D0-hadron event mixing +namespace hf_d0_assoc_tracks +{ +DECLARE_SOA_COLUMN(Phi, phi, float); //! Phi of hadron +DECLARE_SOA_COLUMN(Eta, eta, float); //! Eta of hadron +DECLARE_SOA_COLUMN(PtH, ptH, float); //! Transverse momentum of hadron +DECLARE_SOA_COLUMN(PoolBin, poolBin, int); //! Pool Bin of event defined using zvtx and multiplicity +DECLARE_SOA_COLUMN(GIndexCol, gIndexCol, int); //! Global index for the collision +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, int64_t); //! Timestamp for the collision +DECLARE_SOA_COLUMN(SoftPiStatus, softPiStatus, int); //! Soft-pion status for D0 offline mixing + +enum SoftPionStatus { + NotSoftPi = 0, + SoftPiD0 = 1, + SoftPiD0bar = 2, + SoftPiD0D0bar = 3 +}; +} // namespace hf_d0_assoc_tracks + +DECLARE_SOA_TABLE(D0Hadron, "AOD", "D0HADRON", + aod::hf_d0_assoc_tracks::Phi, + aod::hf_d0_assoc_tracks::Eta, + aod::hf_d0_assoc_tracks::PtH, + aod::hf_d0_assoc_tracks::PoolBin, + aod::hf_d0_assoc_tracks::GIndexCol, + aod::hf_d0_assoc_tracks::TimeStamp, + aod::hf_d0_assoc_tracks::SoftPiStatus); + // definition of columns and tables for D0-Hadron correlation pairs namespace hf_correlation_d0_hadron { @@ -61,10 +112,10 @@ DECLARE_SOA_COLUMN(PtHadron, ptHadron, float); //! T DECLARE_SOA_COLUMN(MD, mD, float); //! Invariant mass of D0 DECLARE_SOA_COLUMN(MDbar, mDbar, float); //! Invariant mass of D0bar DECLARE_SOA_COLUMN(MlScoreBkgD0, mlScoreBkgD0, float); //! ML background score for D0 selection -DECLARE_SOA_COLUMN(MlScoreNonPromptD0, mlScoreNonPromptD0, float); //! ML prompt score for D0 selection +DECLARE_SOA_COLUMN(MlScoreNonPromptD0, mlScoreNonPromptD0, float); //! ML non-prompt score for D0 selection DECLARE_SOA_COLUMN(MlScorePromptD0, mlScorePromptD0, float); //! ML prompt score for D0 selection DECLARE_SOA_COLUMN(MlScoreBkgD0bar, mlScoreBkgD0bar, float); //! ML background score for D0 selection -DECLARE_SOA_COLUMN(MlScoreNonPromptD0bar, mlScoreNonPromptD0bar, float); //! ML prompt score for D0 selection +DECLARE_SOA_COLUMN(MlScoreNonPromptD0bar, mlScoreNonPromptD0bar, float); //! ML non-prompt score for D0 selection DECLARE_SOA_COLUMN(MlScorePromptD0bar, mlScorePromptD0bar, float); //! ML prompt score for D0 selection DECLARE_SOA_COLUMN(SignalStatus, signalStatus, int); //! Tag for D0,D0bar DECLARE_SOA_COLUMN(PoolBin, poolBin, int); //! Pool Bin for the MixedEvent @@ -78,12 +129,12 @@ DECLARE_SOA_COLUMN(IsPrompt, isPrompt, bool); //! U DECLARE_SOA_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, bool); //! Used in MC-Rec, primary associated particles enum ParticleTypeData { - D0Only = 1, // Identified as D0 - D0barOnly, // Identified as D0bar - D0D0barBoth, // Identified as both D0 and D0bar - D0OnlySoftPi = 11, // Identified as D0 with soft pion - D0barOnlySoftPi, // Identified as D0bar with soft pion - D0D0barBothSoftPi // Identified as both D0 and D0bar with soft pion + D0Only = 1, // Identified as D0 + D0barOnly = 2, // Identified as D0bar + D0D0barBoth = 3, // Identified as both D0 and D0bar + D0OnlySoftPi = 11, // Identified as D0 with soft pion + D0barOnlySoftPi = 12, // Identified as D0bar with soft pion + D0D0barBothSoftPi = 13 // Identified as both D0 and D0bar with soft pion }; enum ParticleTypeMcRec { @@ -129,8 +180,10 @@ DECLARE_SOA_TABLE(D0CandRecoInfo, "AOD", "D0CANDRECOINFO", //! Ds candidates Rec aod::hf_correlation_d0_hadron::MDbar, aod::hf_correlation_d0_hadron::PtD, aod::hf_correlation_d0_hadron::MlScoreBkgD0, + aod::hf_correlation_d0_hadron::MlScoreNonPromptD0, aod::hf_correlation_d0_hadron::MlScorePromptD0, aod::hf_correlation_d0_hadron::MlScoreBkgD0bar, + aod::hf_correlation_d0_hadron::MlScoreNonPromptD0bar, aod::hf_correlation_d0_hadron::MlScorePromptD0bar); DECLARE_SOA_TABLE(D0CandGenInfo, "AOD", "D0CANDGENOINFO", //! Ds candidates Generated Information @@ -149,6 +202,7 @@ DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); //! D DECLARE_SOA_COLUMN(DeltaY, deltaY, float); //! DeltaY between Lc and Hadrons DECLARE_SOA_COLUMN(PtLc, ptLc, float); //! Transverse momentum of Lc DECLARE_SOA_COLUMN(PtHadron, ptHadron, float); //! Transverse momentum of Hadron +DECLARE_SOA_COLUMN(PtSoftPi, ptSoftPi, float); //! Transverse momentum of Soft Pion DECLARE_SOA_COLUMN(ChargeCand, chargeCand, int); //! store charge of Lc and Sc DECLARE_SOA_COLUMN(MLc, mLc, float); //! Invariant mass of Lc DECLARE_SOA_COLUMN(MlScoreBkg, mlScoreBkg, float); //! ML background score for Lc selection @@ -157,6 +211,8 @@ DECLARE_SOA_COLUMN(SignalStatus, signalStatus, int); //! T DECLARE_SOA_COLUMN(PoolBin, poolBin, int); //! Pool Bin for the MixedEvent DECLARE_SOA_COLUMN(TrackDcaXY, trackDcaXY, float); //! DCA xy of the track DECLARE_SOA_COLUMN(TrackDcaZ, trackDcaZ, float); //! DCA z of the track +DECLARE_SOA_COLUMN(SoftPiDcaXY, softPiDcaXY, float); //! DCA xy of the soft pion +DECLARE_SOA_COLUMN(SoftPiDcaZ, softPiDcaZ, float); //! DCA z of the soft pion DECLARE_SOA_COLUMN(TrackTPCNClsCrossedRows, trackTPCNClsCrossedRows, int); //! Number of crossed TPC Rows DECLARE_SOA_COLUMN(TrackOrigin, trackOrigin, int); //! Number of crossed TPC Rows DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); //! Used in MC-Rec, Lc Signal @@ -170,11 +226,27 @@ DECLARE_SOA_COLUMN(PiNsigmTPC, piNsigmTPC, float); //! A DECLARE_SOA_COLUMN(PrNsigmTOF, prNsigmTOF, float); //! Associated Particle TOF nSigma Proton DECLARE_SOA_COLUMN(KaNsigmTOF, kaNsigmTOF, float); //! Associated Particle TOF nSigma Kaon DECLARE_SOA_COLUMN(PiNsigmTOF, piNsigmTOF, float); //! Associated Particle TOF nSigma Pion +DECLARE_SOA_COLUMN(MCandHadron, mCandHadron, float); //! Invariant mass of Lc/Sc+Hadron combined system +DECLARE_SOA_COLUMN(PtCombined, ptCombined, float); //! Transverse momentum of combined Lc+Hadron system +DECLARE_SOA_COLUMN(MV0, mV0, float); //! Invariant mass of V0 +DECLARE_SOA_COLUMN(MV0Ref, mV0Ref, float); //! Invariant mass of reflected V0 } // namespace hf_correlation_lc_hadron DECLARE_SOA_TABLE(PtLcFromScHPair, "AOD", "PTLCSCHPAIR", //! Sc-->Lc pT for paired Sc-proton aod::hf_correlation_lc_hadron::PtLc); +DECLARE_SOA_TABLE(ScRecoInfo, "AOD", "SCRECOINFO", //! Sc reco info + aod::hf_correlation_lc_hadron::MLc, + aod::hf_correlation_lc_hadron::PtSoftPi, + aod::hf_correlation_lc_hadron::SoftPiDcaXY, + aod::hf_correlation_lc_hadron::SoftPiDcaZ); + +DECLARE_SOA_TABLE(ScHadronRecoInfo, "AOD", "SCHADRECOINFO", //! Sc reco info + aod::hf_correlation_lc_hadron::MLc, + aod::hf_correlation_lc_hadron::PtSoftPi, + aod::hf_correlation_lc_hadron::SoftPiDcaXY, + aod::hf_correlation_lc_hadron::SoftPiDcaZ); + DECLARE_SOA_TABLE(PtLcFromSc, "AOD", "PTLCSC", //! Sc-->Lc pT aod::hf_correlation_lc_hadron::PtLc); @@ -186,6 +258,13 @@ DECLARE_SOA_TABLE(LcHadronPair, "AOD", "LCHPAIR", //! Lc-Hadrons pairs Informati aod::hf_correlation_lc_hadron::PoolBin, aod::hf_correlation_lc_hadron::IsAutoCorrelated, aod::hf_correlation_lc_hadron::Cent); +DECLARE_SOA_TABLE(CandHadronInvMass, "AOD", "CANDHIMASS", //! Lc-Hadron mass + aod::hf_correlation_lc_hadron::MCandHadron, + aod::hf_correlation_lc_hadron::PtCombined); +DECLARE_SOA_TABLE(PairedV0InvMass, "AOD", "PAIRV0IMASS", //! invarient mass of v0 which paired with charm candidates + aod::hf_correlation_lc_hadron::MV0, aod::hf_correlation_lc_hadron::MV0Ref); +DECLARE_SOA_TABLE(V0InvMass, "AOD", "V0IMASS", //! invarient mass of v0 + aod::hf_correlation_lc_hadron::MV0, aod::hf_correlation_lc_hadron::MV0Ref); DECLARE_SOA_TABLE(LcHadronRecoInfo, "AOD", "LCHRECOINFO", //! Lc-Hadrons pairs Reconstructed Informations aod::hf_correlation_lc_hadron::MLc, @@ -239,6 +318,7 @@ DECLARE_SOA_TABLE(CandCharge, "AOD", "CANDCHARGE", // definition of columns and tables for Ds-Hadron correlation pairs namespace hf_correlation_ds_hadron { +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality of Collision DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); //! DeltaPhi between Ds and Hadrons DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); //! DeltaEta between Ds and Hadrons DECLARE_SOA_COLUMN(SignedPtD, signedPtD, float); //! Transverse momentum of Ds @@ -264,7 +344,8 @@ DECLARE_SOA_TABLE(DsHadronPair, "AOD", "DSHPAIR", //! Ds-Hadrons pairs Informati aod::hf_correlation_ds_hadron::SignedPtD, aod::hf_correlation_ds_hadron::SignedPtHadron, aod::hf_correlation_ds_hadron::PoolBin, - aod::hf_correlation_ds_hadron::NumPvContrib); + aod::hf_correlation_ds_hadron::NumPvContrib, + aod::hf_correlation_ds_hadron::Centrality); DECLARE_SOA_TABLE(DsHadronRecoInfo, "AOD", "DSHRECOINFO", //! Ds-Hadrons pairs Reconstructed Information aod::hf_correlation_ds_hadron::MD, @@ -285,7 +366,8 @@ DECLARE_SOA_TABLE(DsCandRecoInfo, "AOD", "DSCANDRECOINFO", //! Ds candidates Rec aod::hf_correlation_ds_hadron::SignedPtD, aod::hf_correlation_ds_hadron::MlScorePrompt, aod::hf_correlation_ds_hadron::MlScoreBkg, - aod::hf_correlation_ds_hadron::NumPvContrib); + aod::hf_correlation_ds_hadron::NumPvContrib, + aod::hf_correlation_ds_hadron::Centrality); DECLARE_SOA_TABLE(DsCandGenInfo, "AOD", "DSCANDGENOINFO", //! Ds candidates Generated Information aod::hf_correlation_ds_hadron::IsPrompt); @@ -415,6 +497,21 @@ DECLARE_SOA_TABLE(TrkRecInfoDplus, "AOD", "TRKRECINFODPLUS", //! Tracks Reconstr aod::hf_correlation_dplus_hadron::TrackDcaZ, aod::hf_correlation_dplus_hadron::TrackTPCNClsCrossedRows); +// definition of columns and tables for the beauty-mother information of the charm-hadron trigger (MC only), usable by any D-hadron correlation analysis +namespace hf_correlation_info_b_mother +{ +DECLARE_SOA_COLUMN(PtB, ptB, float); //! Generated pT of the beauty mother of the charm hadron (-1 if not from beauty) +DECLARE_SOA_COLUMN(DeltaPhiBtoD, deltaPhiBtoD, float); //! phi(charm hadron) - phi(B mother, generated) in [-pihalf, 3.*pihalf) (-999 if not from beauty) +} // namespace hf_correlation_info_b_mother + +DECLARE_SOA_TABLE(BMotherPairInfo, "AOD", "BMOTHERPAIRINFO", //! Charm-hadron-hadron pairs beauty-mother information, joinable with the pair table + aod::hf_correlation_info_b_mother::PtB, + aod::hf_correlation_info_b_mother::DeltaPhiBtoD); + +DECLARE_SOA_TABLE(BMotherCandInfo, "AOD", "BMOTHERCANDINFO", //! Charm-hadron candidates beauty-mother information, joinable with the candidate table + aod::hf_correlation_info_b_mother::PtB, + aod::hf_correlation_info_b_mother::DeltaPhiBtoD); + // definition of columns and tables for Dstar-Hadron correlation pair namespace hf_correlation_dstar_hadron { diff --git a/PWGHF/HFC/DataModel/DMesonPairsTables.h b/PWGHF/HFC/DataModel/DMesonPairsTables.h index 35e12760919..a5dccbaf0aa 100644 --- a/PWGHF/HFC/DataModel/DMesonPairsTables.h +++ b/PWGHF/HFC/DataModel/DMesonPairsTables.h @@ -32,6 +32,8 @@ DECLARE_SOA_COLUMN(PtCand1, ptCand1, float); //! Transverse momentum of first DECLARE_SOA_COLUMN(PtCand2, ptCand2, float); //! Transverse momentum of second candidate DECLARE_SOA_COLUMN(YCand1, yCand1, float); //! Rapidity of first candidate DECLARE_SOA_COLUMN(YCand2, yCand2, float); //! Rapidity of second candidate +DECLARE_SOA_COLUMN(EtaCand1, etaCand1, float); //! Azimuthal angle of first candidate +DECLARE_SOA_COLUMN(EtaCand2, etaCand2, float); //! Azimuthal angle of second candidate DECLARE_SOA_COLUMN(PhiCand1, phiCand1, float); //! Azimuthal angle of first candidate DECLARE_SOA_COLUMN(PhiCand2, phiCand2, float); //! Azimuthal angle of second candidate // Invariant mass @@ -55,12 +57,15 @@ DECLARE_SOA_COLUMN(MlProbD0barCand2, mlProbD0barCand2, std::vector); //! } // namespace hf_correlation_d_meson_pair // Definition of the D meson pair table. Contains the info needed at Data level. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_DMESON_PAIR_TABLE(_pair_type_, _marker_value_, _description_) \ DECLARE_SOA_TABLE(_pair_type_, "AOD", _description_, o2::soa::Marker<_marker_value_>, \ hf_correlation_d_meson_pair::PtCand1, \ hf_correlation_d_meson_pair::PtCand2, \ hf_correlation_d_meson_pair::YCand1, \ hf_correlation_d_meson_pair::YCand2, \ + hf_correlation_d_meson_pair::EtaCand1, \ + hf_correlation_d_meson_pair::EtaCand2, \ hf_correlation_d_meson_pair::PhiCand1, \ hf_correlation_d_meson_pair::PhiCand2, \ hf_correlation_d_meson_pair::MDCand1, \ @@ -71,6 +76,7 @@ DECLARE_SOA_COLUMN(MlProbD0barCand2, mlProbD0barCand2, std::vector); //! hf_correlation_d_meson_pair::CandidateType1, \ hf_correlation_d_meson_pair::CandidateType2); // Definition of the D meson pair table with info at MC level. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_DMESON_PAIR_MCINFO_TABLE(_pair_type_, _marker_value_, _description_) \ DECLARE_SOA_TABLE(_pair_type_, "AOD", _description_ "MCINFO", o2::soa::Marker<_marker_value_>, \ hf_correlation_d_meson_pair::Origin1, \ @@ -78,6 +84,7 @@ DECLARE_SOA_COLUMN(MlProbD0barCand2, mlProbD0barCand2, std::vector); //! hf_correlation_d_meson_pair::MatchedMc1, \ hf_correlation_d_meson_pair::MatchedMc2); // Definition of the table with the ML info of the D meson pair. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_DMESON_PAIR_MLINFO_TABLE(_pair_type_, _marker_value_, _description_) \ DECLARE_SOA_TABLE(_pair_type_, "AOD", _description_ "ML", o2::soa::Marker<_marker_value_>, \ hf_correlation_d_meson_pair::MlProbD0Cand1, \ @@ -93,6 +100,36 @@ DECLARE_DMESON_PAIR_MLINFO_TABLE(D0PairMl, 1, "D0PAIR"); //! D0 pairs ML Inf DECLARE_DMESON_PAIR_TABLE(D0PairMcGen, 2, "D0PAIRGEN"); //! D0 pairs MC Gen Kinematic Info DECLARE_DMESON_PAIR_MCINFO_TABLE(D0PairMcGenInfo, 2, "D0PAIRGEN"); //! D0 pairs MC Gen Info +namespace hf_correlation_d_meson_had +{ +// D candidate +DECLARE_SOA_COLUMN(PtD, ptD, float); //! Transverse momentum of the D meson +DECLARE_SOA_COLUMN(YD, yD, float); //! Rapidity of the D meson +DECLARE_SOA_COLUMN(EtaD, etaD, float); //! Pseudorapidity of the D meson +DECLARE_SOA_COLUMN(PhiD, phiD, float); //! Azimuthal angle of the D meson +DECLARE_SOA_COLUMN(MD, mD, float); //! Invariant mass of the D meson +DECLARE_SOA_COLUMN(DType, dType, uint8_t); //! D meson type (D0 or D0bar) +DECLARE_SOA_COLUMN(PoolBinD, poolBinD, int); //! Pool bin of the D meson +DECLARE_SOA_COLUMN(GIndexColD, gIndexColD, int); //! G-index column of the D meson +DECLARE_SOA_COLUMN(TimestampD, timestampD, int64_t); //! Timestamp of the D meson + +} // namespace hf_correlation_d_meson_had + +// Definition of the D meson table for D-had correlations. Contains the info needed at Data level. +#define DECLARE_DMESON_ME_TABLE(_cand_number_, _marker_value_, _description_) \ + DECLARE_SOA_TABLE(_cand_number_, "AOD", _description_, o2::soa::Marker<_marker_value_>, \ + hf_correlation_d_meson_had::PtD, \ + hf_correlation_d_meson_had::EtaD, \ + hf_correlation_d_meson_had::PhiD, \ + hf_correlation_d_meson_had::MD, \ + hf_correlation_d_meson_had::DType, \ + hf_correlation_d_meson_had::PoolBinD, \ + hf_correlation_d_meson_had::GIndexColD, \ + hf_correlation_d_meson_had::TimestampD); + +DECLARE_DMESON_ME_TABLE(DMesonInfoCand1, 1, "DMESCAND1"); //! D0 cand 1 info for event mixing +DECLARE_DMESON_ME_TABLE(DMesonInfoCand2, 2, "DMESCAND2"); //! D0 cand 2 info for event mixing + } // namespace o2::aod #endif // PWGHF_HFC_DATAMODEL_DMESONPAIRSTABLES_H_ diff --git a/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h b/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h index 8f8dbecf390..6a807a0db0c 100644 --- a/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h +++ b/PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h @@ -32,6 +32,7 @@ DECLARE_SOA_COLUMN(PosZ, posZ, float); //! Primary vertex z posi DECLARE_SOA_TABLE(HfcRedCollisions, "AOD", "HFCREDCOLLISION", //! Table with collision info soa::Index<>, aod::hf_collisions_reduced::Multiplicity, + aod::hf_collisions_reduced::Centrality, aod::hf_collisions_reduced::NumPvContrib, aod::hf_collisions_reduced::PosZ); @@ -46,33 +47,35 @@ using HfcRedCollision = HfcRedCollisions::iterator; namespace hf_candidate_reduced { -DECLARE_SOA_INDEX_COLUMN(HfcRedCollision, hfcRedCollision); //! ReducedCollision index -DECLARE_SOA_COLUMN(Prong0Id, prong0Id, int); //! Prong 0 index -DECLARE_SOA_COLUMN(Prong1Id, prong1Id, int); //! Prong 1 index -DECLARE_SOA_COLUMN(Prong2Id, prong2Id, int); //! Prong2 index -DECLARE_SOA_COLUMN(PhiCand, phiCand, float); //! Phi of the candidate -DECLARE_SOA_COLUMN(EtaCand, etaCand, float); //! Eta of the candidate -DECLARE_SOA_COLUMN(PtCand, ptCand, float); //! Pt of the candidate -DECLARE_SOA_COLUMN(InvMassDs, invMassDs, float); //! Invariant mass of Ds candidate -DECLARE_SOA_COLUMN(BdtScorePrompt, bdtScorePrompt, float); //! BDT output score for prompt hypothesis -DECLARE_SOA_COLUMN(BdtScoreBkg, bdtScoreBkg, float); //! BDT output score for background hypothesis +DECLARE_SOA_INDEX_COLUMN(HfcRedCollision, hfcRedCollision); //! ReducedCollision index +DECLARE_SOA_COLUMN(Prong0Id, prong0Id, int); //! Prong 0 index +DECLARE_SOA_COLUMN(Prong1Id, prong1Id, int); //! Prong 1 index +DECLARE_SOA_COLUMN(Prong2Id, prong2Id, int); //! Prong2 index +DECLARE_SOA_COLUMN(PhiCand, phiCand, float); //! Phi of the candidate +DECLARE_SOA_COLUMN(EtaCand, etaCand, float); //! Eta of the candidate +DECLARE_SOA_COLUMN(PtCand, ptCand, float); //! Pt of the candidate +DECLARE_SOA_COLUMN(InvMass, invMass, float); //! Invariant mass of charm candidate +DECLARE_SOA_COLUMN(BdtScorePrompt, bdtScorePrompt, float); //! BDT output score for prompt hypothesis +DECLARE_SOA_COLUMN(BdtScoreNonPrompt, bdtScoreNonPrompt, float); //! BDT output score for non-prompt hypothesis +DECLARE_SOA_COLUMN(BdtScoreBkg, bdtScoreBkg, float); //! BDT output score for background hypothesis } // namespace hf_candidate_reduced -DECLARE_SOA_TABLE(DsCandReduceds, "AOD", "DSCANDREDUCED", //! Table with Ds candidate info +DECLARE_SOA_TABLE(HcCandReduceds, "AOD", "HCCANDREDUCED", //! Table with charm hadron candidate info soa::Index<>, aod::hf_candidate_reduced::HfcRedCollisionId, aod::hf_candidate_reduced::PhiCand, aod::hf_candidate_reduced::EtaCand, aod::hf_candidate_reduced::PtCand, - aod::hf_candidate_reduced::InvMassDs, + aod::hf_candidate_reduced::InvMass, aod::hf_candidate_reduced::Prong0Id, aod::hf_candidate_reduced::Prong1Id, aod::hf_candidate_reduced::Prong2Id); -DECLARE_SOA_TABLE(DsCandSelInfos, "AOD", "DSCANDSELINFO", //! Table with Ds candidate selection info +DECLARE_SOA_TABLE(HcCandSelInfos, "AOD", "HCCANDSELINFO", //! Table with charm hadron candidate selection info soa::Index<>, aod::hf_candidate_reduced::HfcRedCollisionId, + aod::hf_candidate_reduced::BdtScoreBkg, aod::hf_candidate_reduced::BdtScorePrompt, - aod::hf_candidate_reduced::BdtScoreBkg); + aod::hf_candidate_reduced::BdtScoreNonPrompt); namespace hf_assoc_track_reduced { diff --git a/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h b/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h index 9c4484de852..42724605adf 100644 --- a/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h +++ b/PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h @@ -38,6 +38,8 @@ DECLARE_SOA_TABLE(HfCandDpMcEvs, "AOD", "HFCANDDPMCEV", namespace full { +DECLARE_SOA_INDEX_COLUMN(HfCandDpFullEv, hfCandDpFullEv); +DECLARE_SOA_INDEX_COLUMN(HfCandDpMcEv, hfCandDpMcEv); DECLARE_SOA_COLUMN(RSecondaryVertex, rSecondaryVertex, float); //! Radius of secondary vertex (cm) DECLARE_SOA_COLUMN(PtProng0, ptProng0, float); //! Transverse momentum of prong0 (GeV/c) DECLARE_SOA_COLUMN(PProng0, pProng0, float); //! Momentum of prong0 (GeV/c) @@ -57,8 +59,6 @@ DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); //! Azimuth angle of candidate DECLARE_SOA_COLUMN(E, e, float); //! Energy of candidate (GeV) DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Collision centrality -DECLARE_SOA_INDEX_COLUMN(HfCandDpMcEv, hfCandDpMcEv); //! The Mc collision index this MC particles belongs to -DECLARE_SOA_INDEX_COLUMN(HfCandDpFullEv, hfCandDpFullEv); //! The collision index this candidate belongs to DECLARE_SOA_COLUMN(NSigTpcPi0, nSigTpcPi0, float); //! TPC Nsigma separation for prong0 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTpcKa0, nSigTpcKa0, float); //! TPC Nsigma separation for prong0 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofPi0, nSigTofPi0, float); //! TOF Nsigma separation for prong0 with pion mass hypothesis @@ -71,12 +71,12 @@ DECLARE_SOA_COLUMN(NSigTpcPi2, nSigTpcPi2, float); DECLARE_SOA_COLUMN(NSigTpcKa2, nSigTpcKa2, float); //! TPC Nsigma separation for prong2 with kaon mass hypothesis DECLARE_SOA_COLUMN(NSigTofPi2, nSigTofPi2, float); //! TOF Nsigma separation for prong2 with pion mass hypothesis DECLARE_SOA_COLUMN(NSigTofKa2, nSigTofKa2, float); //! TOF Nsigma separation for prong2 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi0, nSigTpcTofPi0, float); //! TPC and TOF combined Nsigma separation for prong0 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa0, nSigTpcTofKa0, float); //! TPC and TOF combined Nsigma separation for prong0 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi1, nSigTpcTofPi1, float); //! TPC and TOF combined Nsigma separation for prong1 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa1, nSigTpcTofKa1, float); //! TPC and TOF combined Nsigma separation for prong1 with kaon mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofPi2, nSigTpcTofPi2, float); //! TPC and TOF combined Nsigma separation for prong2 with pion mass hypothesis -DECLARE_SOA_COLUMN(NSigTpcTofKa2, nSigTpcTofKa2, float); //! TPC and TOF combined Nsigma separation for prong2 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi0, tpcTofNSigmaPi0, float); //! TPC and TOF combined Nsigma separation for prong0 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa0, tpcTofNSigmaKa0, float); //! TPC and TOF combined Nsigma separation for prong0 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi1, tpcTofNSigmaPi1, float); //! TPC and TOF combined Nsigma separation for prong1 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa1, tpcTofNSigmaKa1, float); //! TPC and TOF combined Nsigma separation for prong1 with kaon mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaPi2, tpcTofNSigmaPi2, float); //! TPC and TOF combined Nsigma separation for prong2 with pion mass hypothesis +DECLARE_SOA_COLUMN(TpcTofNSigmaKa2, tpcTofNSigmaKa2, float); //! TPC and TOF combined Nsigma separation for prong2 with kaon mass hypothesis DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! Decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthXY, decayLengthXY, float); //! Transverse decay length of candidate (cm) DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! Normalised decay length of candidate @@ -96,7 +96,7 @@ DECLARE_SOA_TABLE(HfCandDpMls, "AOD", "HFCANDDPML", full::MlScore0, full::MlScore1) -DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", +DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", o2::soa::Index<>, full::CandidateSelFlag, full::M, full::Pt, @@ -107,7 +107,7 @@ DECLARE_SOA_TABLE(HfCandDpTinys, "AOD", "HFCANDDPTINY", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec) -DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", +DECLARE_SOA_TABLE(HfCandDpSimples, "AOD", "HFCANDDPSIMPLE", o2::soa::Index<>, hf_cand::Chi2PCA, full::DecayLength, full::DecayLengthXY, @@ -126,20 +126,20 @@ DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", full::NSigTpcKa0, full::NSigTofPi0, full::NSigTofKa0, - full::NSigTpcTofPi0, - full::NSigTpcTofKa0, + full::TpcTofNSigmaPi0, + full::TpcTofNSigmaKa0, full::NSigTpcPi1, full::NSigTpcKa1, full::NSigTofPi1, full::NSigTofKa1, - full::NSigTpcTofPi1, - full::NSigTpcTofKa1, + full::TpcTofNSigmaPi1, + full::TpcTofNSigmaKa1, full::NSigTpcPi2, full::NSigTpcKa2, full::NSigTofPi2, full::NSigTofKa2, - full::NSigTpcTofPi2, - full::NSigTpcTofKa2, + full::TpcTofNSigmaPi2, + full::TpcTofNSigmaKa2, full::CandidateSelFlag, full::M, full::Pt, @@ -155,7 +155,7 @@ DECLARE_SOA_TABLE(HfCandDpLites, "AOD", "HFCANDDPLITE", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec) -DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", +DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", o2::soa::Index<>, hf_cand::XSecondaryVertex, hf_cand::YSecondaryVertex, hf_cand::ZSecondaryVertex, @@ -201,20 +201,20 @@ DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", full::NSigTpcKa0, full::NSigTofPi0, full::NSigTofKa0, - full::NSigTpcTofPi0, - full::NSigTpcTofKa0, + full::TpcTofNSigmaPi0, + full::TpcTofNSigmaKa0, full::NSigTpcPi1, full::NSigTpcKa1, full::NSigTofPi1, full::NSigTofKa1, - full::NSigTpcTofPi1, - full::NSigTpcTofKa1, + full::TpcTofNSigmaPi1, + full::TpcTofNSigmaKa1, full::NSigTpcPi2, full::NSigTpcKa2, full::NSigTofPi2, full::NSigTofKa2, - full::NSigTpcTofPi2, - full::NSigTpcTofKa2, + full::TpcTofNSigmaPi2, + full::TpcTofNSigmaKa2, full::CandidateSelFlag, full::M, full::Pt, @@ -233,7 +233,7 @@ DECLARE_SOA_TABLE(HfCandDpFulls, "AOD", "HFCANDDPFULL", hf_cand_mc_flag::OriginMcRec, hf_cand_mc_flag::FlagMcDecayChanRec); -DECLARE_SOA_TABLE(HfCandDpMcPs, "AOD", "HFCANDDPMCP", +DECLARE_SOA_TABLE(HfCandDpMcPs, "AOD", "HFCANDDPMCP", o2::soa::Index<>, full::Pt, full::Eta, full::Phi, diff --git a/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx b/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx index f2566f4f9a6..fd5b2ca77bc 100644 --- a/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx +++ b/PWGHF/HFC/Macros/DhCorrelationExtraction.cxx @@ -201,7 +201,7 @@ Bool_t DhCorrelationExtraction::setDmesonSpecie(DmesonSpecie k) return kTRUE; } -Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t ptCandMax, Double_t ptHadMin, Double_t ptHadMax, TString codeName) +Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t ptCandMax, Double_t ptHadMin, Double_t ptHadMax, const TString& codeName) { if (fSubtractSoftPiME) { @@ -325,11 +325,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t normalizeMePlot(hMeSideb[iPool], hMeSidebSoftPi[iPool]); // Apply Event Mixing Correction - hCorrSign[iPool] = reinterpret_cast(hSeSign[iPool]->Clone(Form("hCorr_Sign_Pool%d", iPool))); + hCorrSign[iPool] = dynamic_cast(hSeSign[iPool]->Clone(Form("hCorr_Sign_Pool%d", iPool))); hCorrSign[iPool]->Sumw2(); hCorrSign[iPool]->Divide(hMeSign[iPool]); - hCorrSideb[iPool] = reinterpret_cast(hSeSideb[iPool]->Clone(Form("hCorr_Sideb_Pool%d", iPool))); + hCorrSideb[iPool] = dynamic_cast(hSeSideb[iPool]->Clone(Form("hCorr_Sideb_Pool%d", iPool))); hCorrSideb[iPool]->Sumw2(); hCorrSideb[iPool]->Divide(hMeSideb[iPool]); @@ -374,8 +374,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Pools integration if (iPool == 0) { - h2DSign = reinterpret_cast(hCorrSign[0]->Clone("h2D_Sign")); - h2DSideb = reinterpret_cast(hCorrSideb[0]->Clone("h2D_Sideb")); + h2DSign = dynamic_cast(hCorrSign[0]->Clone("h2D_Sign")); + h2DSideb = dynamic_cast(hCorrSideb[0]->Clone("h2D_Sideb")); h2DSign->Sumw2(); h2DSideb->Sumw2(); } else { @@ -425,7 +425,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Bkg subtraction (2D plot) auto* c2DSub = new TCanvas(Form("c2D_Subtr_IntPools_PtHAd%.0fto%.0f", ptHadMin, ptHadMax), Form("c2D_%s_Subtr_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1500, 800); - h2DSubtr = reinterpret_cast(h2DSign->Clone("h2D_Subtr")); + h2DSubtr = dynamic_cast(h2DSign->Clone("h2D_Subtr")); h2DSubtr->Sumw2(); h2DSubtr->Add(h2DSideb, -1); h2DSubtr->SetEntries(h2DSign->GetEntries() - h2DSideb->GetEntries()); @@ -443,7 +443,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DSideb->Scale(1. / h1DSideb->GetXaxis()->GetBinWidth(1)); // Bkg subtraction (1D plot) - h1DSubtr = reinterpret_cast(h1DSign->Clone("h1D_Subtr")); + h1DSubtr = dynamic_cast(h1DSign->Clone("h1D_Subtr")); h1DSubtr->Sumw2(); h1DSubtr->Add(h1DSideb, -1); h1DSubtr->SetEntries(h1DSign->GetEntries() - h1DSideb->GetEntries()); @@ -462,8 +462,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1D->SaveAs(Form("Output_CorrelationExtraction_%s_Root/h1D_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); if (fDebug >= 1) { - h1DSignNorm = reinterpret_cast(h1DSign->Clone("h1D_Sign_Norm")); - h1DSidebNorm = reinterpret_cast(h1DSideb->Clone("h1D_Sideb_Norm")); + h1DSignNorm = dynamic_cast(h1DSign->Clone("h1D_Sign_Norm")); + h1DSidebNorm = dynamic_cast(h1DSideb->Clone("h1D_Sideb_Norm")); h1DSignNorm->Scale(1. / (fSgnYieldNorm + fBkgYield)); // h1D_SidebNorm -> Scale(1./fBkgYield); h1DSidebNorm->Scale(1. / fBkgScaleFactor); @@ -488,7 +488,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c->SaveAs(Form("Output_CorrelationExtraction_%s_Root/ComparisonSignalSidebCorr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); } // Apply normalization to number of triggers - h1DSubtrNorm = reinterpret_cast(h1DSubtr->Clone("h1D_SubtrNorm")); + h1DSubtrNorm = dynamic_cast(h1DSubtr->Clone("h1D_SubtrNorm")); h1DSubtrNorm->Sumw2(); h1DSubtrNorm->Scale(1. / fSgnYieldNorm); h1DSubtrNorm->SetTitle("Signal region after sideb. subt. corr. - Normalized to # of triggers"); @@ -498,7 +498,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hModul = evaluateMcClosModulations(ptCandMin, ptCandMax, ptHadMin, ptHadMax); auto* c1DCorrBbias = new TCanvas(Form("c1D_corrBbias_IntPools_PtCand%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax), Form("c1D_corrBbias_%s_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1600, 500); c1DCorrBbias->cd(); - hBeforeModulCorr = reinterpret_cast(h1DSubtrNorm->Clone("hBeforeModulCorr")); + hBeforeModulCorr = dynamic_cast(h1DSubtrNorm->Clone("hBeforeModulCorr")); hBeforeModulCorr->SetLineColor(kViolet - 3); hBeforeModulCorr->GetYaxis()->SetRangeUser(0., 5.); hBeforeModulCorr->Draw(); @@ -508,10 +508,10 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1DCorrBbias->SaveAs(Form("Output_CorrelationExtraction_%s_Root/ComparisonCorrBiasBtoD_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); auto* file = new TFile(Form("Output_CorrelationExtraction_%s_Root/SystematicCorrBiasBtoD_%s_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax), "RECREATE"); // Open file in write mode - TH1D* h1DSubtrNormClone = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_Clone")); + TH1D* h1DSubtrNormClone = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_Clone")); h1DSubtrNormClone = reflectCorrHistogram(h1DSubtrNormClone); hBeforeModulCorr = reflectCorrHistogram(hBeforeModulCorr); - TH1D* hSystematicCorrBiasBtoD = reinterpret_cast(h1DSubtrNormClone->Clone("hSystematicCorrBiasBtoD")); + TH1D* hSystematicCorrBiasBtoD = dynamic_cast(h1DSubtrNormClone->Clone("hSystematicCorrBiasBtoD")); hSystematicCorrBiasBtoD->Add(h1DSubtrNormClone, hBeforeModulCorr, 1, -1); // Set bin contents to absolute values for (int i = 1; i <= hSystematicCorrBiasBtoD->GetNbinsX(); ++i) { @@ -533,7 +533,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DAllPartCorr->RebinX(fRebinAxisDeltaPhi); std::cout << "Secondary particle histogram rebinned" << std::endl; } - h1DSecPartFrac = reinterpret_cast(h1DPrimaryPartCorr->Clone(Form("hCorrRatio_PtD%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h1DSecPartFrac = dynamic_cast(h1DPrimaryPartCorr->Clone(Form("hCorrRatio_PtD%.0fto%.0f_PtHad%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); h1DSecPartFrac->Sumw2(); h1DSecPartFrac->Divide(h1DPrimaryPartCorr, h1DAllPartCorr, 1., 1., "B"); @@ -544,7 +544,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t c1D->SaveAs(Form("Output_CorrelationExtraction_%s_png/CorrPrimaryPartRatio_%s_Canvas_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f.png", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); c1D->SaveAs(Form("Output_CorrelationExtraction_%s_Root/CorrPrimaryPartRatio_%s_Canvas_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); - h1DSubtrNormSecPart = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_SecPart")); + h1DSubtrNormSecPart = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrNorm_SecPart")); h1DSubtrNormSecPart->Sumw2(); Int_t const nBinsPhi = h1DSubtrNormSecPart->GetNbinsX(); if (nBinsPhi != h1DSecPartFrac->GetNbinsX()) { @@ -562,7 +562,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t h1DFdTemplatePrompt->Scale(1. / h1DFdTemplatePrompt->GetXaxis()->GetBinWidth(1)); h1DFdTemplateNonPrompt->Scale(1. / h1DFdTemplateNonPrompt->GetXaxis()->GetBinWidth(1)); - h1DTemplateTotal = reinterpret_cast(h1DFdTemplatePrompt->Clone("h1D_TemplateTotal")); + h1DTemplateTotal = dynamic_cast(h1DFdTemplatePrompt->Clone("h1D_TemplateTotal")); h1DTemplateTotal->Sumw2(); h1DTemplateTotal->Scale(fdPromptFrac); h1DTemplateTotal->Add(h1DFdTemplateNonPrompt, 1 - fdPromptFrac); @@ -611,16 +611,16 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t std::cout << " " << std::endl; Double_t const baselinediff = baselineData - baselineFd; - TH1D* hBaselineDiff = reinterpret_cast(h1DFdTemplateNonPrompt->Clone("hBaselineDiff")); + TH1D* hBaselineDiff = dynamic_cast(h1DFdTemplateNonPrompt->Clone("hBaselineDiff")); for (int iBin = 0; iBin < hBaselineDiff->GetNbinsX(); iBin++) { hBaselineDiff->SetBinContent(iBin + 1, baselinediff); } h1DFdTemplateNonPrompt->Add(hBaselineDiff); h1DTemplateTotal->Add(hBaselineDiff); if (fSecPartContamination) { - h1DSubtrFdNorm = reinterpret_cast(h1DSubtrNormSecPart->Clone("h1D_SubtrFDNorm")); + h1DSubtrFdNorm = dynamic_cast(h1DSubtrNormSecPart->Clone("h1D_SubtrFDNorm")); } else { - h1DSubtrFdNorm = reinterpret_cast(h1DSubtrNorm->Clone("h1D_SubtrFDNorm")); + h1DSubtrFdNorm = dynamic_cast(h1DSubtrNorm->Clone("h1D_SubtrFDNorm")); } h1DFdTemplateNonPrompt->Scale(1 - fdPromptFrac); h1DSubtrFdNorm->Add(h1DFdTemplateNonPrompt, -1); @@ -667,11 +667,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t } if (fFDsubtraction) { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrFdNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrFdNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else if (fSecPartContamination) { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrNormSecPart->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrNormSecPart->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else { - fCorrectedCorrHisto = reinterpret_cast(h1DSubtrNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHisto = dynamic_cast(h1DSubtrNorm->Clone(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } std::cout << "Analysis steps completed - baseline subtraction missing" << std::endl; @@ -715,7 +715,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t // Baseline subtraction Double_t baselineData, baselineDataErr; - TH1D* hBaseline = reinterpret_cast(h1DSubtrNorm->Clone("hBaseline")); + TH1D* hBaseline = dynamic_cast(h1DSubtrNorm->Clone("hBaseline")); hBaseline->Sumw2(); if (fFDsubtraction) { baselineData = calculateBaseline(h1DSubtrFdNorm, kTRUE, kFALSE); // introduced kFALSE @@ -724,7 +724,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrFdNorm->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrFdNorm->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } else if (fSecPartContamination) { baselineData = calculateBaseline(h1DSubtrNormSecPart, kTRUE, kFALSE); @@ -733,7 +733,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrNormSecPart->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrNormSecPart->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } else { baselineData = calculateBaseline(h1DSubtrNorm, kTRUE, kFALSE); @@ -742,11 +742,11 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaseline->SetBinContent(iBin + 1, baselineData); hBaseline->SetBinError(iBin + 1, baselineDataErr); } - h1DBaselineSubtr = reinterpret_cast(h1DSubtrNorm->Clone("h1D_BaselineSubtr")); + h1DBaselineSubtr = dynamic_cast(h1DSubtrNorm->Clone("h1D_BaselineSubtr")); h1DBaselineSubtr->Add(hBaseline, -1.); } - fCorrectedCorrHistoBaselineSubtr = reinterpret_cast(h1DBaselineSubtr->Clone(Form("hCorrectedCorrBaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoBaselineSubtr = dynamic_cast(h1DBaselineSubtr->Clone(Form("hCorrectedCorrBaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); auto* cFinalBaselineSubtr = new TCanvas(Form("cFinal_BaselineSubtr_%.0fto%.0f", ptHadMin, ptHadMax), Form("cFinal_BaselineSubtr_%s_IntPools_PtAssoc%.0fto%.0f", fDmesonLabel.Data(), ptHadMin, ptHadMax), 100, 100, 1200, 700); h1DBaselineSubtr->SetMarkerColor(kOrange + 8); @@ -795,7 +795,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t cFinalReflected->SaveAs(Form("Output_CorrelationExtraction_%s_Root/AzimCorrDistr_Reflected_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); // Reflected histograms baseline subtracted - TH1D* hBaselineRefl = reinterpret_cast(h1DReflCorr->Clone("hBaseline_Refl")); + TH1D* hBaselineRefl = dynamic_cast(h1DReflCorr->Clone("hBaseline_Refl")); hBaselineRefl->Sumw2(); baselineData = calculateBaseline(h1DReflCorr, kFALSE, kTRUE); baselineDataErr = calculateBaselineError(h1DReflCorr, kFALSE, kTRUE); @@ -804,7 +804,7 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t hBaselineRefl->SetBinContent(iBin + 1, baselineData); hBaselineRefl->SetBinError(iBin + 1, baselineDataErr); } - h1DReflCorrBaselineSubtr = reinterpret_cast(h1DReflCorr->Clone("h1D_ReflCorr_BaselineSubtr")); + h1DReflCorrBaselineSubtr = dynamic_cast(h1DReflCorr->Clone("h1D_ReflCorr_BaselineSubtr")); h1DReflCorrBaselineSubtr->Sumw2(); h1DReflCorrBaselineSubtr->Add(hBaselineRefl, -1.); @@ -832,8 +832,8 @@ Bool_t DhCorrelationExtraction::extractCorrelations(Double_t ptCandMin, Double_t cFinalReflectedBaselineSubtr->SaveAs(Form("Output_CorrelationExtraction_%s_png/AzimCorrDistr_Reflected_BaselineSubtr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.png", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); cFinalReflectedBaselineSubtr->SaveAs(Form("Output_CorrelationExtraction_%s_Root/AzimCorrDistr_Reflected_BaselineSubtr_%s_Canvas_PtCand%.0fto%.0f_PoolInt_PtAssoc%.0fto%.0f.root", codeName.Data(), fDmesonLabel.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax)); - fCorrectedCorrHistoReflected = reinterpret_cast(h1DReflCorr->Clone(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); - fCorrectedCorrHistoReflectedBaselineSubtr = reinterpret_cast(h1DReflCorrBaselineSubtr->Clone(Form("hCorrectedCorrReflected_BaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoReflected = dynamic_cast(h1DReflCorr->Clone(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + fCorrectedCorrHistoReflectedBaselineSubtr = dynamic_cast(h1DReflCorrBaselineSubtr->Clone(Form("hCorrectedCorrReflected_BaselineSubtr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", ptCandMin, ptCandMax, ptHadMin, ptHadMax))); return kTRUE; } @@ -853,8 +853,8 @@ Bool_t DhCorrelationExtraction::readInputSeAndMe() return kFALSE; } - fDirSE = reinterpret_cast(fFileSE->Get(fDirNameSE.Data())); - fDirME = reinterpret_cast(fFileME->Get(fDirNameME.Data())); + fDirSE = dynamic_cast(fFileSE->Get(fDirNameSE.Data())); + fDirME = dynamic_cast(fFileME->Get(fDirNameME.Data())); std::cout << "===================== " << std::endl; std::cout << "Read inputs SE and ME" << std::endl; @@ -925,7 +925,7 @@ Bool_t DhCorrelationExtraction::readInputSecondaryPartContamination() return kFALSE; } - fDirSecPart = reinterpret_cast(fFileSecPart->Get(fDirSecPartName.Data())); + fDirSecPart = dynamic_cast(fFileSecPart->Get(fDirSecPartName.Data())); if (fDirSecPart == nullptr) { std::cout << "[ERROR] Directory " << fDirSecPart << " cannot be opened! check your file path!" << std::endl; @@ -957,10 +957,10 @@ TH1D* DhCorrelationExtraction::evaluateMcClosModulations(Double_t ptCandMin, Dou } // TODO: generalise this part - TH1D* hRecPrompt = reinterpret_cast(fFilePromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hRecNonPrompt = reinterpret_cast(fFileNonPromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hGenPrompt = reinterpret_cast(fFilePromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); - TH1D* hGenNonPrompt = reinterpret_cast(fFileNonPromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hRecPrompt = dynamic_cast(fFilePromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hRecNonPrompt = dynamic_cast(fFileNonPromptMc->Get(Form("h1D_Rec_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hGenPrompt = dynamic_cast(fFilePromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); + TH1D* hGenNonPrompt = dynamic_cast(fFileNonPromptMc->Get(Form("h1D_Gen_iPtD%d_iPtAssoc%d", fBinPtCand, fBinPtHad))); printf("[INFO] Bin cand %d - Bin had %d \n", fBinPtCand, fBinPtHad); @@ -974,10 +974,10 @@ TH1D* DhCorrelationExtraction::evaluateMcClosModulations(Double_t ptCandMin, Dou hGenPrompt->Sumw2(); hGenNonPrompt->Sumw2(); - TH1D* hRatioNonPrompt = reinterpret_cast(hRecNonPrompt->Clone("hRatioNonPrompt")); + TH1D* hRatioNonPrompt = dynamic_cast(hRecNonPrompt->Clone("hRatioNonPrompt")); hRatioNonPrompt->Sumw2(); hRatioNonPrompt->Divide(hRecNonPrompt, hGenNonPrompt, 1., 1., "B"); - hModul = reinterpret_cast(hRatioNonPrompt->Clone("hModul")); + hModul = dynamic_cast(hRatioNonPrompt->Clone("hModul")); TF1* funFit = new TF1("funFit", "[0]", TMath::Pi() * 3. / 8., TMath::Pi() * 3 / 2); hRatioNonPrompt->Fit(funFit, "R"); @@ -1026,51 +1026,51 @@ TH2D* DhCorrelationExtraction::getCorrelHisto(Int_t sEorMe, Int_t sorSb, Int_t p THnSparseD* hSparse = nullptr; if (sEorMe == kSE) { // Same Event if (sorSb == kSign) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSignalRegionName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSignalRegionName.Data())); } else if (!fSidebandDivided) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandsName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandsName.Data())); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirSE->Get(fSECorrelSidebandLeftName.Data())); hSparse->SetName("hSparse"); - auto* hSparseRightSideb = reinterpret_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); + auto* hSparseRightSideb = dynamic_cast(fDirSE->Get(fSECorrelSidebandRightName.Data())); hSparse->Add(hSparseRightSideb, 1.); } } } else { // Mixed Event if (sorSb == kSign) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSignalRegionName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSignalRegionName.Data())); } else if (!fSidebandDivided) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandsName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandsName.Data())); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); + hSparse = dynamic_cast(fDirME->Get(fMECorrelSidebandLeftName.Data())); hSparse->SetName("hSparse"); - auto* hSparseRightSideb = reinterpret_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); + auto* hSparseRightSideb = dynamic_cast(fDirME->Get(fMECorrelSidebandRightName.Data())); hSparse->Add(hSparseRightSideb, 1.); } } } /*else if (fSidebandDivided) { // Mixed Event - if (SorSB == kSign) { hSparse = reinterpret_cast fDirME -> Get(fMECorrelSignalRegionName.Data()); - } else if (!fSidebandDivided) { hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandsName.Data()); + if (SorSB == kSign) { hSparse = dynamic_cast fDirME -> Get(fMECorrelSignalRegionName.Data()); + } else if (!fSidebandDivided) { hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandsName.Data()); } else { if (fUseSidebLeft && !fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); } else if (!fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); } else if (fUseSidebLeft && fUseSidebRight) { - hSparse = reinterpret_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); + hSparse = dynamic_cast fDirME -> Get(fMECorrelSidebandLeftName.Data()); hSparse -> SetName("hSparse"); - THnSparseD *hSparseRightSideb = reinterpret_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); + THnSparseD *hSparseRightSideb = dynamic_cast fDirME -> Get(fMECorrelSidebandRightName.Data()); hSparse -> Add(hSparseRightSideb, 1.); } } @@ -1140,11 +1140,11 @@ void DhCorrelationExtraction::getSignalAndBackgroundForNorm(Double_t ptCandMin, { // using results obtained from HFInvariantMassFitter.cxx class - TH1F* hMassFitSgnYield = reinterpret_cast(fFileMass->Get(fMassHistoNameSgn.Data())); - TH1F* hMassFitBkgYield = reinterpret_cast(fFileMass->Get(fMassHistoNameBkg.Data())); - TH1F* hMassFitSBsYield = reinterpret_cast(fFileMass->Get(fMassHistoNameSBs.Data())); - TH1F* hMassFitSBLYield = reinterpret_cast(fFileMass->Get("hBackgroundSidebandLeft")); - TH1F* hMassFitSBRYield = reinterpret_cast(fFileMass->Get("hBackgroundSidebandRight")); + TH1F* hMassFitSgnYield = dynamic_cast(fFileMass->Get(fMassHistoNameSgn.Data())); + TH1F* hMassFitBkgYield = dynamic_cast(fFileMass->Get(fMassHistoNameBkg.Data())); + TH1F* hMassFitSBsYield = dynamic_cast(fFileMass->Get(fMassHistoNameSBs.Data())); + TH1F* hMassFitSBLYield = dynamic_cast(fFileMass->Get("hBackgroundSidebandLeft")); + TH1F* hMassFitSBRYield = dynamic_cast(fFileMass->Get("hBackgroundSidebandRight")); Int_t const ptCandBin = hMassFitSgnYield->FindBin(ptCandMin + 0.01); if (ptCandBin != hMassFitSgnYield->FindBin(ptCandMax - 0.01)) { @@ -1188,9 +1188,9 @@ TH2D* DhCorrelationExtraction::getFdTemplateHisto(Int_t promptOrFd, Double_t ptC TH2D* h2D = nullptr; // pointer to be returned if (promptOrFd == kPrompt) { - h2D = reinterpret_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplatePromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h2D = dynamic_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplatePromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } else { - h2D = reinterpret_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplateNonPromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); + h2D = dynamic_cast(fFileFDTemplate->Get(Form("%s%.0f_%.0f_ptassoc%.0f_%.0f", fHistoFDTemplateNonPromptName.Data(), ptCandMin, ptCandMax, ptHadMin, ptHadMax))); } Int_t binExtEtaMin = h2D->GetXaxis()->FindBin(fDeltaEtaMin + 0.000001); @@ -1222,9 +1222,9 @@ TH1D* DhCorrelationExtraction::getCorrelHistoSecondaryPart(Int_t partType, Doubl THnSparseD* hSparse = nullptr; if (partType == kPrimaryPart) { // primary particles - hSparse = reinterpret_cast(fDirSecPart->Get(fHistoPrimaryPartName.Data())); + hSparse = dynamic_cast(fDirSecPart->Get(fHistoPrimaryPartName.Data())); } else { // all selected particles - hSparse = reinterpret_cast(fDirSecPart->Get(fHistoAllPartName.Data())); + hSparse = dynamic_cast(fDirSecPart->Get(fHistoAllPartName.Data())); } Int_t const binExtPtCandMin = hSparse->GetAxis(2)->FindBin(ptCandMin + 0.01); // axis2: ptCand, the 0.01 to avoid bin edges! Int_t const binExtPtCandMax = hSparse->GetAxis(2)->FindBin(ptCandMax - 0.01); @@ -1274,7 +1274,7 @@ TH1D* DhCorrelationExtraction::reflectCorrHistogram(TH1D*& histo) TH1D* h1D = new TH1D("h1D_Reflected", "", nBinsPhiRefl, 0., TMath::Pi()); // pointer to be returned h1D->Sumw2(); - // TH1D* h1D = reinterpret_cast histo -> Clone("h1D_Reflected"); + // TH1D* h1D = dynamic_cast histo -> Clone("h1D_Reflected"); // h1D -> GetXaxis() -> SetRange(bin0Phi, binPiPhi); // reflection @@ -1331,7 +1331,7 @@ TH1D* DhCorrelationExtraction::reflectHistoRun2(TH1D* h, Double_t scale) Double_t DhCorrelationExtraction::getFdPromptFrac(Double_t ptCandMin, Double_t ptCandMax, Double_t /*ptHadMin*/, Double_t /*ptHadMax*/) { - TH1D* h1D = reinterpret_cast(fFileFDPromptFrac->Get(fHistoFDPromptFracName.Data())); + TH1D* h1D = dynamic_cast(fFileFDPromptFrac->Get(fHistoFDPromptFracName.Data())); Int_t const binPtCandMin = h1D->GetXaxis()->FindBin(ptCandMin + 0.01); Int_t const binPtCandMax = h1D->GetXaxis()->FindBin(ptCandMax - 0.01); @@ -1513,7 +1513,7 @@ Double_t DhCorrelationExtraction::calculateBaselineError(TH1D*& histo, Bool_t to return errBaseline; } -void DhCorrelationExtraction::setTH1HistoStyle(TH1D*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, +void DhCorrelationExtraction::setTH1HistoStyle(TH1D*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, Style_t markerStyle, Color_t markerColor, Double_t markerSize, Color_t lineColor, Int_t lineWidth, Float_t hTitleXaxisOffset, Float_t hTitleYaxisOffset, Float_t hTitleXaxisSize, Float_t hTitleYaxisSize, Float_t hLabelXaxisSize, Float_t hLabelYaxisSize, @@ -1538,7 +1538,7 @@ void DhCorrelationExtraction::setTH1HistoStyle(TH1D*& histo, TString hTitle, TSt histo->GetYaxis()->CenterTitle(centerYaxisTitle); } -void DhCorrelationExtraction::setTH2HistoStyle(TH2D*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, TString hZaxisTitle, +void DhCorrelationExtraction::setTH2HistoStyle(TH2D*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, const TString& hZaxisTitle, Float_t hTitleXaxisOffset, Float_t hTitleYaxisOffset, Float_t hTitleZaxisOffset, Float_t hTitleXaxisSize, Float_t hTitleYaxisSize, Float_t hTitleZaxisSize, Float_t hLabelXaxisSize, Float_t hLabelYaxisSize, Float_t hLabelZaxisSize, diff --git a/PWGHF/HFC/Macros/DhCorrelationExtraction.h b/PWGHF/HFC/Macros/DhCorrelationExtraction.h index aa3f2a52874..76372dfd81b 100644 --- a/PWGHF/HFC/Macros/DhCorrelationExtraction.h +++ b/PWGHF/HFC/Macros/DhCorrelationExtraction.h @@ -29,6 +29,8 @@ #include #include +#include + class DhCorrelationExtraction : public TObject { @@ -53,39 +55,39 @@ class DhCorrelationExtraction : public TObject /// Methods to set the input configuration // Input files, directories and histograms Bool_t setDmesonSpecie(DmesonSpecie k); - void setInputFilenameMass(TString filenameMass) { fFileNameMass = filenameMass; } - void setInputFilenameSe(TString filenameSE) { fFileNameSE = filenameSE; } - void setInputFilenameMe(TString filenameME) { fFileNameME = filenameME; } - void setInputFilenameSecPart(TString filenameSecPart) { fFileSecPartName = filenameSecPart; } + void setInputFilenameMass(TString filenameMass) { fFileNameMass = std::move(filenameMass); } + void setInputFilenameSe(TString filenameSE) { fFileNameSE = std::move(filenameSE); } + void setInputFilenameMe(TString filenameME) { fFileNameME = std::move(filenameME); } + void setInputFilenameSecPart(TString filenameSecPart) { fFileSecPartName = std::move(filenameSecPart); } void setInputFilenameBiasBtoD(TString filenamePromptMcRec, TString filenameNonPromptMcRec) { - fFilePromptMcRecName = filenamePromptMcRec; - fFileNonPromptMcRecName = filenameNonPromptMcRec; + fFilePromptMcRecName = std::move(filenamePromptMcRec); + fFileNonPromptMcRecName = std::move(filenameNonPromptMcRec); } - void setDirNameSe(TString dirNameSE) { fDirNameSE = dirNameSE; } - void setDirNameMe(TString dirNameME) { fDirNameME = dirNameME; } - void setDirNameSecPart(TString dirNameSecPart) { fDirSecPartName = dirNameSecPart; } - void setMassHistoNameSgn(TString massHistoNameSgn) { fMassHistoNameSgn = massHistoNameSgn; } - void setMassHistoNameBkg(TString massHistoNameBkg) { fMassHistoNameBkg = massHistoNameBkg; } - void setMassHistoNameSBs(TString massHistoNameSBs) { fMassHistoNameSBs = massHistoNameSBs; } - void setSeCorrelHistoSignalName(TString correlNameSigSE) { fSECorrelSignalRegionName = correlNameSigSE; } - void setSeCorrelHistoSidebandName(TString correlNameSbSE) { fSECorrelSidebandsName = correlNameSbSE; } - void setSeCorrelHistoSidebandLeftName(TString correlNameSbSE) { fSECorrelSidebandLeftName = correlNameSbSE; } - void setSeCorrelHistoSidebandRightName(TString correlNameSbSE) { fSECorrelSidebandRightName = correlNameSbSE; } - void setMeCorrelHistoSignalName(TString correlNameSigME) { fMECorrelSignalRegionName = correlNameSigME; } - void setMeCorrelHistoSidebandName(TString correlNameSbME) { fMECorrelSidebandsName = correlNameSbME; } - void setMeCorrelHistoSidebandLeftName(TString correlNameSbME) { fMECorrelSidebandLeftName = correlNameSbME; } - void setMeCorrelHistoSidebandRightName(TString correlNameSbME) { fMECorrelSidebandRightName = correlNameSbME; } + void setDirNameSe(TString dirNameSE) { fDirNameSE = std::move(dirNameSE); } + void setDirNameMe(TString dirNameME) { fDirNameME = std::move(dirNameME); } + void setDirNameSecPart(TString dirNameSecPart) { fDirSecPartName = std::move(dirNameSecPart); } + void setMassHistoNameSgn(TString massHistoNameSgn) { fMassHistoNameSgn = std::move(massHistoNameSgn); } + void setMassHistoNameBkg(TString massHistoNameBkg) { fMassHistoNameBkg = std::move(massHistoNameBkg); } + void setMassHistoNameSBs(TString massHistoNameSBs) { fMassHistoNameSBs = std::move(massHistoNameSBs); } + void setSeCorrelHistoSignalName(TString correlNameSigSE) { fSECorrelSignalRegionName = std::move(correlNameSigSE); } + void setSeCorrelHistoSidebandName(TString correlNameSbSE) { fSECorrelSidebandsName = std::move(correlNameSbSE); } + void setSeCorrelHistoSidebandLeftName(TString correlNameSbSE) { fSECorrelSidebandLeftName = std::move(correlNameSbSE); } + void setSeCorrelHistoSidebandRightName(TString correlNameSbSE) { fSECorrelSidebandRightName = std::move(correlNameSbSE); } + void setMeCorrelHistoSignalName(TString correlNameSigME) { fMECorrelSignalRegionName = std::move(correlNameSigME); } + void setMeCorrelHistoSidebandName(TString correlNameSbME) { fMECorrelSidebandsName = std::move(correlNameSbME); } + void setMeCorrelHistoSidebandLeftName(TString correlNameSbME) { fMECorrelSidebandLeftName = std::move(correlNameSbME); } + void setMeCorrelHistoSidebandRightName(TString correlNameSbME) { fMECorrelSidebandRightName = std::move(correlNameSbME); } void setHistoSecPartName(TString histoPrimaryPartName, TString histoAllPartName) { - fHistoPrimaryPartName = histoPrimaryPartName; - fHistoAllPartName = histoAllPartName; + fHistoPrimaryPartName = std::move(histoPrimaryPartName); + fHistoAllPartName = std::move(histoAllPartName); } - void setInputFilenameFdTemplate(TString filenameFDTemplate) { fFileFDTemplateName = filenameFDTemplate; } - void setInputFilenameFdPromptFrac(TString filenameFDPromptFrac) { fFileFDPromptFracName = filenameFDPromptFrac; } - void setInputHistoNameFdTemplatePrompt(TString hNameFDTemplatePrompt) { fHistoFDTemplatePromptName = hNameFDTemplatePrompt; } - void setInputHistoNameFdTemplateNonPrompt(TString hNameFDTemplateNonPrompt) { fHistoFDTemplateNonPromptName = hNameFDTemplateNonPrompt; } - void setInputHistoNameFdPromptFrac(TString hNameFDPromptFrac) { fHistoFDPromptFracName = hNameFDPromptFrac; } + void setInputFilenameFdTemplate(TString filenameFDTemplate) { fFileFDTemplateName = std::move(filenameFDTemplate); } + void setInputFilenameFdPromptFrac(TString filenameFDPromptFrac) { fFileFDPromptFracName = std::move(filenameFDPromptFrac); } + void setInputHistoNameFdTemplatePrompt(TString hNameFDTemplatePrompt) { fHistoFDTemplatePromptName = std::move(hNameFDTemplatePrompt); } + void setInputHistoNameFdTemplateNonPrompt(TString hNameFDTemplateNonPrompt) { fHistoFDTemplateNonPromptName = std::move(hNameFDTemplateNonPrompt); } + void setInputHistoNameFdPromptFrac(TString hNameFDPromptFrac) { fHistoFDPromptFracName = std::move(hNameFDPromptFrac); } // Input conditions: PtCand, PtHad, PoolBins void setNpools(Int_t npools) { fNpools = npools; } @@ -143,15 +145,15 @@ class DhCorrelationExtraction : public TObject Bool_t readInputInvMass(); Bool_t readInputFdSubtr(); Bool_t readInputSecondaryPartContamination(); - Bool_t extractCorrelations(Double_t ptCandMin, Double_t ptCandMax, Double_t ptHadMin, Double_t ptHadMax, TString codeName); + Bool_t extractCorrelations(Double_t ptCandMin, Double_t ptCandMax, Double_t ptHadMin, Double_t ptHadMax, const TString& codeName); TH1D* getCorrectedCorrHisto() { return fCorrectedCorrHisto; } TH1D* getCorrectedCorrHistoBaselineSubtr() { return fCorrectedCorrHistoBaselineSubtr; } TH1D* getCorrectedCorrHistoReflected() { return fCorrectedCorrHistoReflected; } TH1D* getCorrectedCorrHistoReflectedBaselineSubtr() { return fCorrectedCorrHistoReflectedBaselineSubtr; } /// Histogram style - void setTH1HistoStyle(TH1D*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, Style_t markerStyle = kFullCircle, Color_t markerColor = kRed + 1, Double_t markerSize = 1.4, Color_t lineColor = kRed + 1, Int_t lineWidth = 3, Float_t hTitleXaxisOffset = 1.0, Float_t hTitleYaxisOffset = 1.0, Float_t hTitleXaxisSize = 0.060, Float_t hTitleYaxisSize = 0.060, Float_t hLabelXaxisSize = 0.060, Float_t hLabelYaxisSize = 0.060, Bool_t centerXaxisTitle = false, Bool_t centerYaxisTitle = false); - void setTH2HistoStyle(TH2D*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, TString hZaxisTitle, Float_t hTitleXaxisOffset = 1.8, Float_t hTitleYaxisOffset = 1.8, Float_t hTitleZaxisOffset = 1.2, Float_t hTitleXaxisSize = 0.060, Float_t hTitleYaxisSize = 0.060, Float_t hTitleZaxisSize = 0.060, Float_t hLabelXaxisSize = 0.060, Float_t hLabelYaxisSize = 0.060, Float_t hLabelZaxisSize = 0.060, Bool_t centerXaxisTitle = true, Bool_t centerYaxisTitle = true); + void setTH1HistoStyle(TH1D*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, Style_t markerStyle = kFullCircle, Color_t markerColor = kRed + 1, Double_t markerSize = 1.4, Color_t lineColor = kRed + 1, Int_t lineWidth = 3, Float_t hTitleXaxisOffset = 1.0, Float_t hTitleYaxisOffset = 1.0, Float_t hTitleXaxisSize = 0.060, Float_t hTitleYaxisSize = 0.060, Float_t hLabelXaxisSize = 0.060, Float_t hLabelYaxisSize = 0.060, Bool_t centerXaxisTitle = false, Bool_t centerYaxisTitle = false); + void setTH2HistoStyle(TH2D*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, const TString& hZaxisTitle, Float_t hTitleXaxisOffset = 1.8, Float_t hTitleYaxisOffset = 1.8, Float_t hTitleZaxisOffset = 1.2, Float_t hTitleXaxisSize = 0.060, Float_t hTitleYaxisSize = 0.060, Float_t hTitleZaxisSize = 0.060, Float_t hLabelXaxisSize = 0.060, Float_t hLabelYaxisSize = 0.060, Float_t hLabelZaxisSize = 0.060, Bool_t centerXaxisTitle = true, Bool_t centerYaxisTitle = true); private: TFile* fFileMass; // File containing the mass histograms diff --git a/PWGHF/HFC/Macros/ExtractOutputCorrel.C b/PWGHF/HFC/Macros/ExtractOutputCorrel.C index db8dc9158d5..1a80656d58c 100644 --- a/PWGHF/HFC/Macros/ExtractOutputCorrel.C +++ b/PWGHF/HFC/Macros/ExtractOutputCorrel.C @@ -55,13 +55,13 @@ void parseStringArray(const Value& jsonArray, std::vector& output) } } -void setInputCorrelNames(DhCorrelationExtraction* plotter, TString pathFileSE, TString pathFileME, TString dirSE, TString dirME, TString histoNameCorrSignal, TString histoNameCorrSideba, TString histoNameCorrSidebaLeft, TString histoNameCorrSidebaRight); -void setInputHistoInvMassNames(DhCorrelationExtraction* plotter, TString pathFileMass, std::vector inputMassNames); -void setInputHistoFdSubtraction(DhCorrelationExtraction* plotter, TString pathFileFDTemplate, TString pathFileFDPromptFrac, TString histoNameFDTemplatePrompt, TString histoNameFDTemplateNonPrompt, TString histoNameRawFracPrompt); -void setInputHistoSecPart(DhCorrelationExtraction* plotter, TString pathFileSecPart, TString dirSecPartName, TString histoNamePrimaryPart, TString histoNameAllPart); -void setInputHistoBiasBtoD(DhCorrelationExtraction* plotter, TString pathfFilePromptMcRec, TString pathfFileNonPromptMcRec); +void setInputCorrelNames(DhCorrelationExtraction* plotter, const TString& pathFileSE, const TString& pathFileME, const TString& dirSE, const TString& dirME, const TString& histoNameCorrSignal, const TString& histoNameCorrSideba, const TString& histoNameCorrSidebaLeft, const TString& histoNameCorrSidebaRight); +void setInputHistoInvMassNames(DhCorrelationExtraction* plotter, const TString& pathFileMass, std::vector inputMassNames); +void setInputHistoFdSubtraction(DhCorrelationExtraction* plotter, const TString& pathFileFDTemplate, const TString& pathFileFDPromptFrac, const TString& histoNameFDTemplatePrompt, const TString& histoNameFDTemplateNonPrompt, const TString& histoNameRawFracPrompt); +void setInputHistoSecPart(DhCorrelationExtraction* plotter, const TString& pathFileSecPart, const TString& dirSecPartName, const TString& histoNamePrimaryPart, const TString& histoNameAllPart); +void setInputHistoBiasBtoD(DhCorrelationExtraction* plotter, const TString& pathfFilePromptMcRec, const TString& pathfFileNonPromptMcRec); -void extractOutputCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") +void extractOutputCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") { // gStyle -> SetOptStat(0); gStyle->SetPadLeftMargin(0.15); @@ -274,7 +274,7 @@ void extractOutputCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json outFileReflectedBaselineSubtr->Close(); } -void setInputCorrelNames(DhCorrelationExtraction* plotter, TString pathFileSE, TString pathFileME, TString dirSE, TString dirME, TString histoNameCorrSignal, TString histoNameCorrSideba, TString histoNameCorrSidebaLeft, TString histoNameCorrSidebaRight) +void setInputCorrelNames(DhCorrelationExtraction* plotter, const TString& pathFileSE, const TString& pathFileME, const TString& dirSE, const TString& dirME, const TString& histoNameCorrSignal, const TString& histoNameCorrSideba, const TString& histoNameCorrSidebaLeft, const TString& histoNameCorrSidebaRight) { // Ds paths @@ -292,7 +292,7 @@ void setInputCorrelNames(DhCorrelationExtraction* plotter, TString pathFileSE, T plotter->setMeCorrelHistoSidebandRightName(histoNameCorrSidebaRight.Data()); } -void setInputHistoInvMassNames(DhCorrelationExtraction* plotter, TString pathFileMass, std::vector inputMassNames) +void setInputHistoInvMassNames(DhCorrelationExtraction* plotter, const TString& pathFileMass, std::vector inputMassNames) { // to use if sgn and bkg extraction is done apart plotter->setInputFilenameMass(pathFileMass.Data()); @@ -301,7 +301,7 @@ void setInputHistoInvMassNames(DhCorrelationExtraction* plotter, TString pathFil plotter->setMassHistoNameSBs(inputMassNames[2].data()); } -void setInputHistoFdSubtraction(DhCorrelationExtraction* plotter, TString pathFileFDTemplate, TString pathFileFDPromptFrac, TString histoNameFDTemplatePrompt, TString histoNameFDTemplateNonPrompt, TString histoNameRawFracPrompt) +void setInputHistoFdSubtraction(DhCorrelationExtraction* plotter, const TString& pathFileFDTemplate, const TString& pathFileFDPromptFrac, const TString& histoNameFDTemplatePrompt, const TString& histoNameFDTemplateNonPrompt, const TString& histoNameRawFracPrompt) { plotter->setInputFilenameFdTemplate(pathFileFDTemplate.Data()); @@ -311,7 +311,7 @@ void setInputHistoFdSubtraction(DhCorrelationExtraction* plotter, TString pathFi plotter->setInputHistoNameFdPromptFrac(histoNameRawFracPrompt.Data()); } -void setInputHistoSecPart(DhCorrelationExtraction* plotter, TString pathFileSecPart, TString dirSecPartName, TString histoNamePrimaryPart, TString histoNameAllPart) +void setInputHistoSecPart(DhCorrelationExtraction* plotter, const TString& pathFileSecPart, const TString& dirSecPartName, const TString& histoNamePrimaryPart, const TString& histoNameAllPart) { plotter->setInputFilenameSecPart(pathFileSecPart.Data()); @@ -319,7 +319,7 @@ void setInputHistoSecPart(DhCorrelationExtraction* plotter, TString pathFileSecP plotter->setHistoSecPartName(histoNamePrimaryPart.Data(), histoNameAllPart.Data()); } -void setInputHistoBiasBtoD(DhCorrelationExtraction* plotter, TString pathfFilePromptMcRec, TString pathfFileNonPromptMcRec) +void setInputHistoBiasBtoD(DhCorrelationExtraction* plotter, const TString& pathfFilePromptMcRec, const TString& pathfFileNonPromptMcRec) { plotter->setInputFilenameBiasBtoD(pathfFilePromptMcRec.Data(), pathfFileNonPromptMcRec.Data()); diff --git a/PWGHF/HFC/Macros/FitCorrel.C b/PWGHF/HFC/Macros/FitCorrel.C index 72461f06dc3..e5b88cb5bd2 100644 --- a/PWGHF/HFC/Macros/FitCorrel.C +++ b/PWGHF/HFC/Macros/FitCorrel.C @@ -54,18 +54,18 @@ void readArray(const Value& jsonArray, vector& output) } } -void setTH1HistoStyle(TH1D*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, +void setTH1HistoStyle(TH1D*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, Style_t markerStyle, Color_t markerColor, Double_t markerSize, Color_t lineColor, Int_t lineWidth, Float_t hTitleXaxisOffset = 1.3, Float_t hTitleYaxisOffset = 1.3, Float_t hTitleXaxisSize = 0.045, Float_t hTitleYaxisSize = 0.045, Float_t hLabelXaxisSize = 0.045, Float_t hLabelYaxisSize = 0.045, Bool_t centerXaxisTitle = false, Bool_t centerYaxisTitle = false); -void setTH1HistoStyle(TH1F*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, +void setTH1HistoStyle(TH1F*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, Style_t markerStyle, Color_t markerColor, Double_t markerSize, Color_t lineColor, Int_t lineWidth, Float_t hTitleXaxisOffset = 1.3, Float_t hTitleYaxisOffset = 1.3, Float_t hTitleXaxisSize = 0.045, Float_t hTitleYaxisSize = 0.045, Float_t hLabelXaxisSize = 0.045, Float_t hLabelYaxisSize = 0.045, Bool_t centerXaxisTitle = false, Bool_t centerYaxisTitle = false); -void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") +void fitCorrelDs(const TString& cfgFileName = "config_CorrAnalysis.json") { gStyle->SetOptStat(0); gStyle->SetPadLeftMargin(0.2); @@ -110,10 +110,7 @@ void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") const int nBinsPtCand = binsPtCandIntervalsVec.size() - 1; const int nBinsPtHad = binsPtHadIntervals.size() - 1; - double binsPtCandIntervals[nBinsPtCand + 1]; - for (int i = 0; i < nBinsPtCand + 1; i++) { - binsPtCandIntervals[i] = binsPtCandIntervalsVec[i]; - } + std::vector binsPtCandIntervals{binsPtCandIntervalsVec}; const Value& fitFuncValue = config["FitFunction"]; readArray(fitFuncValue, fitFunc); @@ -122,17 +119,14 @@ void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") int const fixMean = config["FixMean"].GetInt(); int const nBaselinePoints = config["nBaselinePoints"].GetInt(); - vector pointsForBaselineVec; + std::vector pointsForBaselineVec; const Value& pointsForBaselineValue = config["binsForBaseline"]; readArray(pointsForBaselineValue, pointsForBaselineVec); if (pointsForBaselineVec.size() != nBaselinePoints) { cout << "ERROR: size of the vector pointsForBaseline is different from the number of nBaselinePoints" << endl; return; } - int pointsForBaseline[nBaselinePoints]; - for (int i = 0; i < nBaselinePoints; i++) { - pointsForBaseline[i] = pointsForBaselineVec[i]; - } + std::vector pointsForBaseline{pointsForBaselineVec}; std::cout << "=========================== " << std::endl; std::cout << "Input variables from config" << std::endl; @@ -193,16 +187,16 @@ void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { for (int iBinPtHad = 0; iBinPtHad < nBinsPtHad; iBinPtHad++) { if (isReflected) { - hCorrPhi[iBinPtCand][iBinPtHad] = reinterpret_cast(inFile->Get(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); + hCorrPhi[iBinPtCand][iBinPtHad] = dynamic_cast(inFile->Get(Form("hCorrectedCorrReflected_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); } else { - hCorrPhi[iBinPtCand][iBinPtHad] = reinterpret_cast(inFile->Get(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); + hCorrPhi[iBinPtCand][iBinPtHad] = dynamic_cast(inFile->Get(Form("hCorrectedCorr_PtCand%.0fto%.0f_PtAssoc%.0fto%.0f", binsPtCandIntervals[iBinPtCand], binsPtCandIntervals[iBinPtCand + 1], binsPtHadIntervals[iBinPtHad], binsPtHadIntervals[iBinPtHad + 1]))); } - corrFitter[iBinPtHad][iBinPtCand] = new DhCorrelationFitter(reinterpret_cast(hCorrPhi[iBinPtCand][iBinPtHad]), fMin, fMax); + corrFitter[iBinPtHad][iBinPtCand] = new DhCorrelationFitter(dynamic_cast(hCorrPhi[iBinPtCand][iBinPtHad]), fMin, fMax); // FIXME: invalid cast corrFitter[iBinPtHad][iBinPtCand]->setHistoIsReflected(refl); corrFitter[iBinPtHad][iBinPtCand]->setFixBaseline(fixBase); corrFitter[iBinPtHad][iBinPtCand]->setBaselineUpOrDown(shiftBaseUp, shiftBaseDown); - corrFitter[iBinPtHad][iBinPtCand]->setPointsForBaseline(nBaselinePoints, pointsForBaseline); + corrFitter[iBinPtHad][iBinPtCand]->setPointsForBaseline(nBaselinePoints, pointsForBaseline.data()); corrFitter[iBinPtHad][iBinPtCand]->setv2(v2AssocPart[iBinPtCand], v2Dmeson[iBinPtCand]); corrFitter[iBinPtHad][iBinPtCand]->setReflectedCorrHisto(isReflected); @@ -222,14 +216,14 @@ void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") canvasCorrPhi[iBinPtHad]->Divide(3, 2); } // histograms with fir parameters - hBaselin[iBinPtHad] = new TH1D(Form("hBaselin_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSYield[iBinPtHad] = new TH1D(Form("hNSYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSSigma[iBinPtHad] = new TH1D(Form("hNSSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASYield[iBinPtHad] = new TH1D(Form("hASYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASSigma[iBinPtHad] = new TH1D(Form("hASSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hBeta[iBinPtHad] = new TH1D(Form("hBeta_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hNSYieldBinCount[iBinPtHad] = new TH1D(Form("hNSYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); - hASYieldBinCount[iBinPtHad] = new TH1D(Form("hASYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals); + hBaselin[iBinPtHad] = new TH1D(Form("hBaselin_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSYield[iBinPtHad] = new TH1D(Form("hNSYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSSigma[iBinPtHad] = new TH1D(Form("hNSSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASYield[iBinPtHad] = new TH1D(Form("hASYield_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASSigma[iBinPtHad] = new TH1D(Form("hASSigma_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hBeta[iBinPtHad] = new TH1D(Form("hBeta_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hNSYieldBinCount[iBinPtHad] = new TH1D(Form("hNSYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); + hASYieldBinCount[iBinPtHad] = new TH1D(Form("hASYieldBinCount_PtBinAssoc%d", iBinPtHad + 1), "", nBinsPtCand, binsPtCandIntervals.data()); for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { setTH1HistoStyle(hCorrPhi[iBinPtCand][iBinPtHad], "", "#Delta#phi [rad]", "#frac{1}{N_{D_{s}}}#frac{dN^{assoc}}{d#Delta#phi} [rad^{-1}]", kFullCircle, kRed + 1, 1.4, kRed + 1, 3); @@ -244,8 +238,8 @@ void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") // draw systematic errors int const nBinsPhi = hCorrPhi[iBinPtCand][iBinPtHad]->GetNbinsX(); if (drawSystematicErrors) { - hSystematicErrors[iBinPtCand][iBinPtHad] = reinterpret_cast(inFileSystematicErrors->Get(Form("hSystematicErrorsMerged_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); - hSystematicErrorsPlot[iBinPtCand][iBinPtHad] = reinterpret_cast(hCorrPhi[iBinPtCand][iBinPtHad]->Clone(Form("hSystematicErrorsPlot_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); + hSystematicErrors[iBinPtCand][iBinPtHad] = dynamic_cast(inFileSystematicErrors->Get(Form("hSystematicErrorsMerged_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); + hSystematicErrorsPlot[iBinPtCand][iBinPtHad] = dynamic_cast(hCorrPhi[iBinPtCand][iBinPtHad]->Clone(Form("hSystematicErrorsPlot_PtBin%d_PtBinAssoc%d", iBinPtCand + 1, iBinPtHad + 1))); for (int iPhi = 0; iPhi < nBinsPhi; iPhi++) { if (sameSystematics) { hSystematicErrorsPlot[iBinPtCand][iBinPtHad]->SetBinError(iPhi + 1, hSystematicErrors[iBinPtCand][iBinPtHad]->GetBinContent(1) * hCorrPhi[iBinPtCand][iBinPtHad]->GetBinContent(iPhi + 1)); @@ -397,11 +391,11 @@ void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") hBaselin[iBinPtHad]->Draw(); if (drawSystematicErrors) { - TH1F* hBaselinSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsBaselinMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hNSYieldSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hNSSigmaSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hASYieldSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); - TH1F* hASSigmaSyst = reinterpret_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hBaselinSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsBaselinMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hNSYieldSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hNSSigmaSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsNSSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hASYieldSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASYieldMerged_PtBinAssoc%d", iBinPtHad + 1))); + TH1F* hASSigmaSyst = dynamic_cast(inFileFitSystematicErrors->Get(Form("hSystematicErrorsASSigmaMerged_PtBinAssoc%d", iBinPtHad + 1))); for (int iBinPtCand = 0; iBinPtCand < nBinsPtCand; iBinPtCand++) { hBaselinSyst->SetBinError(iBinPtCand + 1, hBaselinSyst->GetBinContent(iBinPtCand + 1) * hBaselin[iBinPtHad]->GetBinContent(iBinPtCand + 1)); @@ -456,7 +450,7 @@ void fitCorrelDs(const TString cfgFileName = "config_CorrAnalysis.json") } } -void setTH1HistoStyle(TH1D*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, +void setTH1HistoStyle(TH1D*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, Style_t markerStyle, Color_t markerColor, Double_t markerSize, Color_t lineColor, Int_t lineWidth, Float_t hTitleXaxisOffset, Float_t hTitleYaxisOffset, Float_t hTitleXaxisSize, Float_t hTitleYaxisSize, Float_t hLabelXaxisSize, Float_t hLabelYaxisSize, @@ -481,7 +475,7 @@ void setTH1HistoStyle(TH1D*& histo, TString hTitle, TString hXaxisTitle, TString histo->GetYaxis()->CenterTitle(centerYaxisTitle); } -void setTH1HistoStyle(TH1F*& histo, TString hTitle, TString hXaxisTitle, TString hYaxisTitle, +void setTH1HistoStyle(TH1F*& histo, const TString& hTitle, const TString& hXaxisTitle, const TString& hYaxisTitle, Style_t markerStyle, Color_t markerColor, Double_t markerSize, Color_t lineColor, Int_t lineWidth, Float_t hTitleXaxisOffset, Float_t hTitleYaxisOffset, Float_t hTitleXaxisSize, Float_t hTitleYaxisSize, Float_t hLabelXaxisSize, Float_t hLabelYaxisSize, diff --git a/PWGHF/HFC/TableProducer/CMakeLists.txt b/PWGHF/HFC/TableProducer/CMakeLists.txt index cee79035d78..87d3e12ea40 100644 --- a/PWGHF/HFC/TableProducer/CMakeLists.txt +++ b/PWGHF/HFC/TableProducer/CMakeLists.txt @@ -11,46 +11,55 @@ o2physics_add_dpl_workflow(correlator-d0-d0bar SOURCES correlatorD0D0bar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d0-d0bar-barrel-full-pid SOURCES correlatorD0D0barBarrelFullPid.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d0-hadrons SOURCES correlatorD0Hadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-d-meson-pairs SOURCES correlatorDMesonPairs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-dminus SOURCES correlatorDplusDminus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-dplus-reduced SOURCES correlatorDplusDplusReduced.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-dplus-hadrons SOURCES correlatorDplusHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-ds-hadrons SOURCES correlatorDsHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-correlations-reduced SOURCES derivedDataCreatorCorrelationsReduced.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -66,6 +75,7 @@ o2physics_add_dpl_workflow(correlator-ds-hadrons-reduced o2physics_add_dpl_workflow(correlator-dstar-hadrons SOURCES correlatorDstarHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -76,20 +86,36 @@ o2physics_add_dpl_workflow(correlator-hfe-hadrons o2physics_add_dpl_workflow(correlator-lc-hadrons SOURCES correlatorLcHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(correlator-lc-sc-hadrons SOURCES correlatorLcScHadrons.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(correlator-xic-hadrons + SOURCES correlatorXicHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-track-femto-dream SOURCES producerCharmHadronsTrackFemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(producer-charm-hadrons-v0-femto-dream SOURCES producerCharmHadronsV0FemtoDream.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(producer-charm-hadrons-charm-femto-dream + SOURCES producerCharmHadronsCharmFemtoDream.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx b/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx index dbef6c1ee60..ba37e499c1e 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0D0bar.cxx @@ -135,7 +135,7 @@ struct HfCorrelatorD0D0bar { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx b/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx index a7efa82e7bd..3f1289e4413 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0D0barBarrelFullPid.cxx @@ -14,12 +14,12 @@ /// /// \author Fabio Colamaria , INFN Bari +#include "PWGHF/ALICE3/DataModel/CandidateSelectionTables.h" #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" -#include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/Utils/utilsAnalysis.h" @@ -136,7 +136,7 @@ struct HfCorrelatorD0D0barBarrelFullPid { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "D0,D0bar candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0", "D0,D0bar candidates;inv. mass D0 only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassD0bar", "D0,D0bar candidates;inv. mass D0bar only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx index 85ec82b5ed9..cd7edd8130a 100644 --- a/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorD0Hadrons.cxx @@ -214,6 +214,8 @@ struct HfCorrelatorD0Hadrons { Produces entryD0CandRecoInfo; Produces entryD0CandGenInfo; Produces entryTrackRecoInfo; + Produces entryD0; + Produces entryD0Hadron; Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; @@ -237,6 +239,7 @@ struct HfCorrelatorD0Hadrons { Configurable storeAutoCorrelationFlag{"storeAutoCorrelationFlag", false, "Store flag that indicates if the track is paired to its D-meson mother instead of skipping it"}; Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; Configurable useCentrality{"useCentrality", false, "Flag for centrality dependent analyses"}; + Configurable enableCentralityQA{"enableCentralityQA", false, "Enable FT0M centrality vs multiplicity QA in processData"}; int leadingIndex = 0; double softPiMass = 0.14543; // pion mass + Q-value of the D*->D0pi decay @@ -247,7 +250,7 @@ struct HfCorrelatorD0Hadrons { Filter trackFilter = requireGlobalTrackWoDCAInFilter() && (nabs(aod::track::eta) < etaTrackMax) && (aod::track::pt > ptTrackMin) && (aod::track::pt < ptTrackMax) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); Filter d0Filter = (aod::hf_sel_candidate_d0::isSelD0 >= 1) || (aod::hf_sel_candidate_d0::isSelD0bar >= 1); Filter collisionFilterGen = aod::hf_selection_dmeson_collision::dmesonSel == true; - Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kD0) || ((aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary); + Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kD0) || ((aod::mcparticle::flags & static_cast(o2::aod::mcparticle::enums::PhysicalPrimary)) == static_cast(o2::aod::mcparticle::enums::PhysicalPrimary)); Preslice perCol = aod::hf_cand::collisionId; Preslice perCollisionID = aod::track::collisionId; @@ -264,6 +267,9 @@ struct HfCorrelatorD0Hadrons { ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "primary vertex z coordinate"}; ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; ConfigurableAxis binsCentFt0m{"binsCentFt0m", {100, 0., 100.}, "Centrality percentile (FT0M)"}; + ConfigurableAxis binsBdtScoreBkg{"binsBdtScoreBkg", {500, 0., 1.}, "Bdt score background"}; + ConfigurableAxis binsBdtScorePrompt{"binsBdtScorePrompt", {200, 0., 1.}, "Bdt score prompt"}; + ConfigurableAxis binsBdtScoreNonPrompt{"binsBdtScoreNonPrompt", {200, 0., 1.}, "Bdt score non-prompt"}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -273,8 +279,8 @@ struct HfCorrelatorD0Hadrons { AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; AxisSpec const axisRapidity = {100, -5., 5., "Rapidity"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsPosZ, "PosZ"}; @@ -283,11 +289,13 @@ struct HfCorrelatorD0Hadrons { AxisSpec const axisSignalStatus = {200, 0., 200., "Signal status"}; AxisSpec axisEvtCount = {1, -0.5, 0.5}; AxisSpec const axisTrkCount = {5, 0., 5.}; - AxisSpec axisBdtScoreBkg = {100, 0., 1., "Bdt score background"}; - AxisSpec axisBdtScorePrompt = {100, 0., 1., "Bdt score prompt"}; - AxisSpec axisBdtScoreNonPrompt = {100, 0., 1., "Bdt score Nonprompt"}; + AxisSpec axisBdtScoreBkg = {binsBdtScoreBkg, "Bdt score background"}; + AxisSpec axisBdtScorePrompt = {binsBdtScorePrompt, "Bdt score prompt"}; + AxisSpec axisBdtScoreNonPrompt = {binsBdtScoreNonPrompt, "Bdt score Nonprompt"}; AxisSpec axisOrigin = {10, 0., 10., "Candidate origin"}; AxisSpec axisCent = {binsCentFt0m, "Centrality"}; + AxisSpec const axisCandidateStatusMcRec = {64, -0.5, 63.5, "MC candidate status"}; + AxisSpec const axisRecoHypothesis = {4, -0.5, 3.5, "Reconstructed hypothesis"}; // Histograms for Data registry.add("hPtCand", "D0, D0bar candidates", {HistType::kTH1F, {axisPtD}}); @@ -306,6 +314,9 @@ struct HfCorrelatorD0Hadrons { registry.add("hMassD0bar1D", "D0bar candidates mass", {HistType::kTH1F, {axisMassD}}); registry.add("hMassD0VsPtVsCent", "D0 candidates;inv. mass (p K #pi) (GeV/#it{c}^{2});entries", {HistType::kTH3F, {{axisMassD}, {axisPtD}, {axisCent}}}); registry.add("hMLScoresVsMassVsPtVsEtaVsOriginVsCent", "D0, D0bar candidates BkgVspromptVsNonPromptVsMassVsPtVsEtaVsOrigin", {HistType::kTHnSparseD, {{axisBdtScoreBkg}, {axisBdtScorePrompt}, {axisBdtScoreNonPrompt}, {axisMassD}, {axisPtD}, {axisEta}, {axisOrigin}, {axisCent}}}); + registry.add("hTracksBeforeSoftMix", "Tracks before soft pion reject offline mixing", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hTracksAfterSoftMix", "Tracks after soft pion reject offline mixing", {HistType::kTH1F, {axisPtHadron}}); + // Histograms for MC Reco registry.add("hPtCandRec", "D0, D0bar candidates - MC reco", {HistType::kTH1F, {axisPtD}}); registry.add("hPtProng0Rec", "D0, D0bar candidates prong 0 - MC reco", {HistType::kTH1F, {axisPtD}}); @@ -317,9 +328,11 @@ struct HfCorrelatorD0Hadrons { registry.add("hYRec", "D0,D0bar candidates - MC reco", {HistType::kTH1F, {axisRapidity}}); registry.add("hMassD0RecSig", "D0 signal candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hMassD0RecRef", "D0 reflection candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); + registry.add("hMassD0RecRefAfterRejectBoth", "D0 reflection candidates after rejecting D0 and D0bar candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hMassD0RecBg", "D0 background candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hMassD0barRecSig", "D0bar signal candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hMassD0barRecRef", "D0bar reflection candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); + registry.add("hMassD0barRecRefAfterRejectBoth", "D0bar reflection candidates after rejecting D0 and D0bar candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hMassD0barRecBg", "D0bar background candidates massVsPt - MC reco", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hPtCandRecSigPrompt", "D0,Hadron candidates Prompt - MC Reco", {HistType::kTH1F, {axisPtD}}); registry.add("hPtVsMLScoresVsEtaRecSigPrompt", "Prompt D0-D0bar signal candidates MLVsPtVsEta - MC reco", {HistType::kTHnSparseD, {{axisBdtScoreBkg}, {axisBdtScorePrompt}, {axisBdtScoreNonPrompt}, {axisPtD}, {axisEta}}}); @@ -329,6 +342,11 @@ struct HfCorrelatorD0Hadrons { registry.add("hPtVsMultiplicityRecNonPrompt", "Multiplicity FT0M - MC Rec Non Prompt", {HistType::kTH2F, {{axisPtD}, {axisMultFT0M}}}); registry.add("hPtParticleAssocVsCandRec", "Associated Particle - MC reco", {HistType::kTH2F, {{axisPtHadron}, {axisPtD}}}); registry.add("hPtPrimaryParticleAssocVsCandRec", "Associated Particle - MC reco", {HistType::kTH2F, {{axisPtHadron}, {axisPtD}}}); + if (useCentrality) { + registry.add("hMLScoresVsMassVsPtVsEtaVsOriginVsCandidateStatusVsHypothesisVsCent", "MCRec template information with centrality", {HistType::kTHnSparseD, {{axisBdtScoreBkg}, {axisBdtScorePrompt}, {axisBdtScoreNonPrompt}, {axisMassD}, {axisPtD}, {axisEta}, {axisOrigin}, {axisCandidateStatusMcRec}, {axisRecoHypothesis}, {axisCent}}}); + } else { + registry.add("hMLScoresVsMassVsPtVsEtaVsOriginVsCandidateStatusVsHypothesis", "MCRec template information", {HistType::kTHnSparseD, {{axisBdtScoreBkg}, {axisBdtScorePrompt}, {axisBdtScoreNonPrompt}, {axisMassD}, {axisPtD}, {axisEta}, {axisOrigin}, {axisCandidateStatusMcRec}, {axisRecoHypothesis}}}); + } // Histograms for MC Gen registry.add("hEvtCountGen", "Event counter - MC gen", {HistType::kTH1F, {axisEvtCount}}); registry.add("hPtCandGen", "D0, D0bar candidates - MC gen", {HistType::kTH1F, {axisPtD}}); @@ -349,6 +367,9 @@ struct HfCorrelatorD0Hadrons { registry.get(HIST("hTrackCounter"))->GetXaxis()->SetBinLabel(5, "fake tracks"); registry.add("hZvtx", "z vertex", {HistType::kTH1F, {axisPosZ}}); registry.add("hMultFT0M", "Multiplicity FT0M", {HistType::kTH1F, {axisMultFT0M}}); + if (enableCentralityQA) { + registry.add("hCentFT0MVsMultFT0M", "Centrality FT0M vs multiplicity FT0M", {HistType::kTH2D, {axisMultFT0M, axisCent}}); + } registry.add("hCollisionPoolBin", "collision pool bin", {HistType::kTH1F, {axisPoolBin}}); registry.add("hD0PoolBin", "D0 selected in pool Bin", {HistType::kTH1F, {axisPoolBin}}); registry.add("hTracksPoolBin", "Particles associated pool bin", {HistType::kTH1F, {axisPoolBin}}); @@ -359,9 +380,15 @@ struct HfCorrelatorD0Hadrons { /// D0-h correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) void processData(SelectedCollisions::iterator const& collision, SelectedTracks const& tracks, - SelectedCandidatesDataMl const& candidates) + SelectedCandidatesDataMl const& candidates, + aod::BCsWithTimestamps const&) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; + + auto bc = collision.bc_as(); + int gCollisionId = collision.globalIndex(); + int64_t timeStamp = bc.timestamp(); + // find leading particle if (correlateD0WithLeadingParticle) { leadingIndex = findLeadingParticle(tracks, etaTrackMax.value); @@ -375,6 +402,9 @@ struct HfCorrelatorD0Hadrons { registry.fill(HIST("hCollisionPoolBin"), poolBin); registry.fill(HIST("hZvtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); + if (enableCentralityQA) { + registry.fill(HIST("hCentFT0MVsMultFT0M"), collision.multFT0M(), collision.centFT0M()); + } int nTracks = 0; if (collision.numContrib() > 1) { @@ -397,6 +427,30 @@ struct HfCorrelatorD0Hadrons { std::vector outputMlD0 = {-1., -1., -1.}; std::vector outputMlD0bar = {-1., -1., -1.}; + std::vector softPionTrackIdsForOfflineMixing; + std::vector softPionStatusesForOfflineMixing; + bool hasAcceptedD0ForOfflineMixing = false; + + auto addSoftPionTrackForOfflineMixing = [&softPionTrackIdsForOfflineMixing, &softPionStatusesForOfflineMixing](int64_t trackId, int softPiStatus) { + for (auto iTrack = 0u; iTrack < softPionTrackIdsForOfflineMixing.size(); ++iTrack) { + if (softPionTrackIdsForOfflineMixing[iTrack] == trackId) { + softPionStatusesForOfflineMixing[iTrack] |= softPiStatus; + return; + } + } + softPionTrackIdsForOfflineMixing.push_back(trackId); + softPionStatusesForOfflineMixing.push_back(softPiStatus); + }; + + auto getSoftPionStatusForOfflineMixing = [&softPionTrackIdsForOfflineMixing, &softPionStatusesForOfflineMixing](int64_t trackId) { + for (auto iTrack = 0u; iTrack < softPionTrackIdsForOfflineMixing.size(); ++iTrack) { + if (softPionTrackIdsForOfflineMixing[iTrack] == trackId) { + return softPionStatusesForOfflineMixing[iTrack]; + } + } + return static_cast(aod::hf_d0_assoc_tracks::NotSoftPi); + }; + for (const auto& candidate : candidates) { if (std::abs(HfHelper::yD0(candidate)) >= yCandMax || candidate.pt() <= ptCandMin || candidate.pt() >= ptTrackMax) { continue; @@ -420,6 +474,10 @@ struct HfCorrelatorD0Hadrons { const auto invMassD0 = HfHelper::invMassD0ToPiK(candidate); const auto invMassD0bar = HfHelper::invMassD0barToKPi(candidate); + if (candidate.isSelD0() >= selectionFlagD0 || candidate.isSelD0bar() >= selectionFlagD0bar) { + hasAcceptedD0ForOfflineMixing = true; + } + // ========================== Fill mass histo ================================ if (candidate.isSelD0() >= selectionFlagD0) { registry.fill(HIST("hMass"), invMassD0, candidate.pt(), efficiencyWeight); @@ -430,6 +488,7 @@ struct HfCorrelatorD0Hadrons { outputMlD0[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; } registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCent"), outputMlD0[0], outputMlD0[1], outputMlD0[2], invMassD0, candidate.pt(), candidate.eta(), (candidate.isSelD0bar() != 0) ? o2::aod::hf_correlation_d0_hadron::D0D0barBoth : o2::aod::hf_correlation_d0_hadron::D0Only, cent, efficiencyWeight); + entryD0(candidate.phi(), candidate.eta(), candidate.pt(), invMassD0, poolBin, gCollisionId, timeStamp, (candidate.isSelD0bar() != 0) ? o2::aod::hf_correlation_d0_hadron::D0D0barBoth : o2::aod::hf_correlation_d0_hadron::D0Only); } if (candidate.isSelD0bar() >= selectionFlagD0bar) { registry.fill(HIST("hMass"), invMassD0bar, candidate.pt(), efficiencyWeight); @@ -440,8 +499,9 @@ struct HfCorrelatorD0Hadrons { outputMlD0bar[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; } registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCent"), outputMlD0bar[0], outputMlD0bar[1], outputMlD0bar[2], invMassD0bar, candidate.pt(), candidate.eta(), (candidate.isSelD0() != 0) ? o2::aod::hf_correlation_d0_hadron::D0D0barBoth : o2::aod::hf_correlation_d0_hadron::D0barOnly, cent, efficiencyWeight); + entryD0(candidate.phi(), candidate.eta(), candidate.pt(), invMassD0bar, poolBin, gCollisionId, timeStamp, (candidate.isSelD0() != 0) ? o2::aod::hf_correlation_d0_hadron::D0D0barBoth : o2::aod::hf_correlation_d0_hadron::D0barOnly); } - entryD0CandRecoInfo(invMassD0, invMassD0bar, candidate.pt(), outputMlD0[0], outputMlD0[2], outputMlD0bar[0], outputMlD0bar[2]); + entryD0CandRecoInfo(invMassD0, invMassD0bar, candidate.pt(), outputMlD0[0], outputMlD0[1], outputMlD0[2], outputMlD0bar[0], outputMlD0bar[1], outputMlD0bar[2]); // ========================== Fill general histos ================================ registry.fill(HIST("hPtCand"), candidate.pt()); @@ -477,12 +537,14 @@ struct HfCorrelatorD0Hadrons { if (candidate.isSelD0() >= selectionFlagD0) { if ((std::abs(invMassDstar1 - invMassD0) - softPiMass) < ptSoftPionMax) { + addSoftPionTrackForOfflineMixing(track.globalIndex(), aod::hf_d0_assoc_tracks::SoftPiD0); continue; } } if (candidate.isSelD0bar() >= selectionFlagD0bar) { if ((std::abs(invMassDstar2 - invMassD0bar) - softPiMass) < ptSoftPionMax) { + addSoftPionTrackForOfflineMixing(track.globalIndex(), aod::hf_d0_assoc_tracks::SoftPiD0bar); continue; } } @@ -518,6 +580,20 @@ struct HfCorrelatorD0Hadrons { } // end inner loop (tracks) } // end outer loop + + // loop to save tables for offline event mixing + if (hasAcceptedD0ForOfflineMixing) { + for (const auto& track : tracks) { + const auto softPiStatus = getSoftPionStatusForOfflineMixing(track.globalIndex()); + + registry.fill(HIST("hTracksBeforeSoftMix"), track.pt()); + if (softPiStatus == aod::hf_d0_assoc_tracks::NotSoftPi) { + registry.fill(HIST("hTracksAfterSoftMix"), track.pt()); + } + + entryD0Hadron(track.phi(), track.eta(), track.pt(), poolBin, gCollisionId, timeStamp, softPiStatus); + } + } } PROCESS_SWITCH(HfCorrelatorD0Hadrons, processData, "Process data", false); @@ -526,9 +602,15 @@ struct HfCorrelatorD0Hadrons { void processMcRec(SelectedCollisions::iterator const& collision, SelectedTracksMcRec const& tracks, SelectedCandidatesMcRecMl const& candidates, - aod::McParticles const& mcParticles) + aod::McParticles const& mcParticles, + aod::BCsWithTimestamps const&) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; + + auto bc = collision.bc_as(); + int gCollisionId = collision.globalIndex(); + int64_t timeStamp = bc.timestamp(); + // find leading particle if (correlateD0WithLeadingParticle) { leadingIndex = findLeadingParticle(tracks, etaTrackMax.value); @@ -568,6 +650,30 @@ struct HfCorrelatorD0Hadrons { std::vector outputMlD0 = {-1., -1., -1.}; std::vector outputMlD0bar = {-1., -1., -1.}; + std::vector softPionTrackIdsForOfflineMixing; + std::vector softPionStatusesForOfflineMixing; + bool hasAcceptedD0ForOfflineMixing = false; + + auto addSoftPionTrackForOfflineMixing = [&softPionTrackIdsForOfflineMixing, &softPionStatusesForOfflineMixing](int64_t trackId, int softPiStatus) { + for (auto iTrack = 0u; iTrack < softPionTrackIdsForOfflineMixing.size(); ++iTrack) { + if (softPionTrackIdsForOfflineMixing[iTrack] == trackId) { + softPionStatusesForOfflineMixing[iTrack] |= softPiStatus; + return; + } + } + softPionTrackIdsForOfflineMixing.push_back(trackId); + softPionStatusesForOfflineMixing.push_back(softPiStatus); + }; + + auto getSoftPionStatusForOfflineMixing = [&softPionTrackIdsForOfflineMixing, &softPionStatusesForOfflineMixing](int64_t trackId) { + for (auto iTrack = 0u; iTrack < softPionTrackIdsForOfflineMixing.size(); ++iTrack) { + if (softPionTrackIdsForOfflineMixing[iTrack] == trackId) { + return softPionStatusesForOfflineMixing[iTrack]; + } + } + return static_cast(aod::hf_d0_assoc_tracks::NotSoftPi); + }; + for (const auto& candidate : candidates) { bool isD0Prompt = candidate.originMcRec() == RecoDecay::OriginType::Prompt; bool isD0NonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; @@ -589,6 +695,16 @@ struct HfCorrelatorD0Hadrons { const auto invMassD0 = HfHelper::invMassD0ToPiK(candidate); const auto invMassD0bar = HfHelper::invMassD0barToKPi(candidate); + const bool selectedD0 = candidate.isSelD0() >= selectionFlagD0; + const bool selectedD0bar = candidate.isSelD0bar() >= selectionFlagD0bar; + const int recoHypothesis = selectedD0 && selectedD0bar ? aod::hf_correlation_d0_hadron::ParticleTypeData::D0D0barBoth : selectedD0 ? aod::hf_correlation_d0_hadron::ParticleTypeData::D0Only + : selectedD0bar ? aod::hf_correlation_d0_hadron::ParticleTypeData::D0barOnly + : 0; + + if (selectedD0 || selectedD0bar) { + hasAcceptedD0ForOfflineMixing = true; + } + if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // fill per-candidate distributions from D0/D0bar true candidates registry.fill(HIST("hPtCandRec"), candidate.pt()); @@ -600,9 +716,13 @@ struct HfCorrelatorD0Hadrons { registry.fill(HIST("hSelectionStatusRec"), candidate.isSelD0bar() + (candidate.isSelD0() * 2)); } // fill invariant mass plots from D0/D0bar signal and background candidates - if (candidate.isSelD0() >= selectionFlagD0) { // only reco as D0 - if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // also matched as D0 + if (selectedD0) { // reconstructed under the D0 hypothesis + int candidateStatus = 0; + + if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + SETBIT(candidateStatus, aod::hf_correlation_d0_hadron::ParticleTypeMcRec::D0Sig); registry.fill(HIST("hMassD0RecSig"), invMassD0, candidate.pt(), efficiencyWeight); + if (isD0Prompt) { registry.fill(HIST("hPtCandRecSigPrompt"), candidate.pt()); registry.fill(HIST("hPtVsMultiplicityRecPrompt"), candidate.pt(), collision.multFT0M()); @@ -613,18 +733,41 @@ struct HfCorrelatorD0Hadrons { registry.fill(HIST("hPtVsMLScoresVsEtaRecSigNonPrompt"), outputMlD0[0], outputMlD0[1], outputMlD0[2], candidate.pt(), candidate.eta()); } } else if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + SETBIT(candidateStatus, aod::hf_correlation_d0_hadron::ParticleTypeMcRec::D0Ref); registry.fill(HIST("hMassD0RecRef"), invMassD0, candidate.pt(), efficiencyWeight); + + if (!selectedD0bar) { + registry.fill(HIST("hMassD0RecRefAfterRejectBoth"), invMassD0, candidate.pt(), efficiencyWeight); + } } else { + SETBIT(candidateStatus, aod::hf_correlation_d0_hadron::ParticleTypeMcRec::D0Bg); registry.fill(HIST("hMassD0RecBg"), invMassD0, candidate.pt(), efficiencyWeight); } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMlD0[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; } + registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCent"), outputMlD0[0], outputMlD0[1], outputMlD0[2], invMassD0, candidate.pt(), candidate.eta(), isD0Prompt, cent, efficiencyWeight); + + if (useCentrality) { + registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCandidateStatusVsHypothesisVsCent"), outputMlD0[0], outputMlD0[1], outputMlD0[2], invMassD0, candidate.pt(), candidate.eta(), isD0Prompt, + candidateStatus, recoHypothesis, cent, efficiencyWeight); + } else { + registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCandidateStatusVsHypothesis"), outputMlD0[0], outputMlD0[1], outputMlD0[2], invMassD0, candidate.pt(), candidate.eta(), isD0Prompt, + candidateStatus, recoHypothesis, efficiencyWeight); + } + + entryD0(candidate.phi(), candidate.eta(), candidate.pt(), invMassD0, poolBin, gCollisionId, timeStamp, selectedD0bar ? o2::aod::hf_correlation_d0_hadron::D0D0barBoth : o2::aod::hf_correlation_d0_hadron::D0Only); } - if (candidate.isSelD0bar() >= selectionFlagD0bar) { // only reco as D0bar - if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // also matched as D0bar + + if (selectedD0bar) { // reconstructed under the D0bar hypothesis + int candidateStatus = 0; + + if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + SETBIT(candidateStatus, aod::hf_correlation_d0_hadron::ParticleTypeMcRec::D0barSig); registry.fill(HIST("hMassD0barRecSig"), invMassD0bar, candidate.pt(), efficiencyWeight); + if (isD0Prompt) { registry.fill(HIST("hPtCandRecSigPrompt"), candidate.pt()); registry.fill(HIST("hPtVsMultiplicityRecPrompt"), candidate.pt(), collision.multFT0M()); @@ -635,16 +778,34 @@ struct HfCorrelatorD0Hadrons { registry.fill(HIST("hPtVsMLScoresVsEtaRecSigNonPrompt"), outputMlD0bar[0], outputMlD0bar[1], outputMlD0bar[2], candidate.pt(), candidate.eta()); } } else if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + SETBIT(candidateStatus, aod::hf_correlation_d0_hadron::ParticleTypeMcRec::D0barRef); registry.fill(HIST("hMassD0barRecRef"), invMassD0bar, candidate.pt(), efficiencyWeight); + + if (!selectedD0) { + registry.fill(HIST("hMassD0barRecRefAfterRejectBoth"), invMassD0bar, candidate.pt(), efficiencyWeight); + } } else { + SETBIT(candidateStatus, aod::hf_correlation_d0_hadron::ParticleTypeMcRec::D0barBg); registry.fill(HIST("hMassD0barRecBg"), invMassD0bar, candidate.pt(), efficiencyWeight); } + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMlD0bar[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; } + registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCent"), outputMlD0bar[0], outputMlD0bar[1], outputMlD0bar[2], invMassD0bar, candidate.pt(), candidate.eta(), isD0Prompt, cent, efficiencyWeight); + + if (useCentrality) { + registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCandidateStatusVsHypothesisVsCent"), outputMlD0bar[0], outputMlD0bar[1], outputMlD0bar[2], invMassD0bar, candidate.pt(), candidate.eta(), + isD0Prompt, candidateStatus, recoHypothesis, cent, efficiencyWeight); + } else { + registry.fill(HIST("hMLScoresVsMassVsPtVsEtaVsOriginVsCandidateStatusVsHypothesis"), outputMlD0bar[0], outputMlD0bar[1], outputMlD0bar[2], invMassD0bar, candidate.pt(), candidate.eta(), isD0Prompt, + candidateStatus, recoHypothesis, efficiencyWeight); + } + + entryD0(candidate.phi(), candidate.eta(), candidate.pt(), invMassD0bar, poolBin, gCollisionId, timeStamp, selectedD0 ? o2::aod::hf_correlation_d0_hadron::D0D0barBoth : o2::aod::hf_correlation_d0_hadron::D0barOnly); } - entryD0CandRecoInfo(invMassD0, invMassD0bar, candidate.pt(), outputMlD0[0], outputMlD0[2], outputMlD0bar[0], outputMlD0bar[2]); + entryD0CandRecoInfo(invMassD0, invMassD0bar, candidate.pt(), outputMlD0[0], outputMlD0[1], outputMlD0[2], outputMlD0bar[0], outputMlD0bar[1], outputMlD0bar[2]); entryD0CandGenInfo(isD0Prompt); // ===================== Define parameters for soft pion removal ======================== @@ -683,12 +844,14 @@ struct HfCorrelatorD0Hadrons { if (candidate.isSelD0() >= selectionFlagD0) { if ((std::abs(invMassDstar1 - invMassD0) - softPiMass) < ptSoftPionMax) { + addSoftPionTrackForOfflineMixing(track.globalIndex(), aod::hf_d0_assoc_tracks::SoftPiD0); continue; } } if (candidate.isSelD0bar() >= selectionFlagD0bar) { if ((std::abs(invMassDstar2 - invMassD0bar) - softPiMass) < ptSoftPionMax) { + addSoftPionTrackForOfflineMixing(track.globalIndex(), aod::hf_d0_assoc_tracks::SoftPiD0bar); continue; } } @@ -749,6 +912,20 @@ struct HfCorrelatorD0Hadrons { entryTrackRecoInfo(track.dcaXY(), track.dcaZ(), track.tpcNClsCrossedRows()); } // end inner loop (Tracks) } // end of outer loop (D0) + + // loop to save tables for offline event mixing + if (hasAcceptedD0ForOfflineMixing) { + for (const auto& track : tracks) { + const auto softPiStatus = getSoftPionStatusForOfflineMixing(track.globalIndex()); + + registry.fill(HIST("hTracksBeforeSoftMix"), track.pt()); + if (softPiStatus == aod::hf_d0_assoc_tracks::NotSoftPi) { + registry.fill(HIST("hTracksAfterSoftMix"), track.pt()); + } + + entryD0Hadron(track.phi(), track.eta(), track.pt(), poolBin, gCollisionId, timeStamp, softPiStatus); + } + } } PROCESS_SWITCH(HfCorrelatorD0Hadrons, processMcRec, "Process MC Reco mode", true); @@ -758,8 +935,10 @@ struct HfCorrelatorD0Hadrons { void processMcGen(SelectedCollisionsMcGen::iterator const& mcCollision, SelectedParticlesMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); + int gCollisionId = mcCollision.globalIndex(); + int64_t timeStamp = 0; registry.fill(HIST("hCollisionPoolBin"), poolBin); registry.fill(HIST("hEvtCountGen"), 0); // MC gen level @@ -771,6 +950,29 @@ struct HfCorrelatorD0Hadrons { bool isD0NonPrompt = false; int trackOrigin = -1; float cent = 100.; // Centrality Placeholder: will be updated later + std::vector softPionTrackIdsForOfflineMixing; + std::vector softPionStatusesForOfflineMixing; + bool hasAcceptedD0ForOfflineMixing = false; + + auto addSoftPionTrackForOfflineMixing = [&softPionTrackIdsForOfflineMixing, &softPionStatusesForOfflineMixing](int64_t trackId, int softPiStatus) { + for (auto iTrack = 0u; iTrack < softPionTrackIdsForOfflineMixing.size(); ++iTrack) { + if (softPionTrackIdsForOfflineMixing[iTrack] == trackId) { + softPionStatusesForOfflineMixing[iTrack] |= softPiStatus; + return; + } + } + softPionTrackIdsForOfflineMixing.push_back(trackId); + softPionStatusesForOfflineMixing.push_back(softPiStatus); + }; + + auto getSoftPionStatusForOfflineMixing = [&softPionTrackIdsForOfflineMixing, &softPionStatusesForOfflineMixing](int64_t trackId) { + for (auto iTrack = 0u; iTrack < softPionTrackIdsForOfflineMixing.size(); ++iTrack) { + if (softPionTrackIdsForOfflineMixing[iTrack] == trackId) { + return softPionStatusesForOfflineMixing[iTrack]; + } + } + return static_cast(aod::hf_d0_assoc_tracks::NotSoftPi); + }; for (const auto& particleTrigg : mcParticles) { if (std::abs(particleTrigg.pdgCode()) != Pdg::kD0) { @@ -784,6 +986,7 @@ struct HfCorrelatorD0Hadrons { if (ptCandMin >= 0. && particleTrigg.pt() < ptCandMin) { continue; } + hasAcceptedD0ForOfflineMixing = true; registry.fill(HIST("hD0PoolBin"), poolBin); registry.fill(HIST("hPtCandGen"), particleTrigg.pt()); @@ -803,6 +1006,7 @@ struct HfCorrelatorD0Hadrons { registry.fill(HIST("hPtCandGenNonPrompt"), particleTrigg.pt()); registry.fill(HIST("hPtVsEtaCandGenSigNonPrompt"), particleTrigg.pt(), particleTrigg.eta()); } + entryD0(particleTrigg.phi(), particleTrigg.eta(), particleTrigg.pt(), MassD0, poolBin, gCollisionId, timeStamp, (particleTrigg.pdgCode() == Pdg::kD0) ? o2::aod::hf_correlation_d0_hadron::D0Only : o2::aod::hf_correlation_d0_hadron::D0barOnly); // =============== D-h correlation dedicated section ===================== for (const auto& particleAssoc : mcParticles) { @@ -819,6 +1023,14 @@ struct HfCorrelatorD0Hadrons { if (!particleAssoc.isPhysicalPrimary()) { continue; } + + // Explicitly reject direct daughters of the current trigger D0/D0bar. + auto const motherIdxD0 = RecoDecay::getMother(mcParticles, particleAssoc, Pdg::kD0, true, nullptr, 1); + auto const motherIdxD0bar = RecoDecay::getMother(mcParticles, particleAssoc, -Pdg::kD0, true, nullptr, 1); + if (motherIdxD0 == particleTrigg.globalIndex() || motherIdxD0bar == particleTrigg.globalIndex()) { + continue; + } + // ==============================soft pion removal================================ registry.fill(HIST("hTrackCounter"), 1); // fill before soft pi removal // method used: indexMother = -1 by default if the mother doesn't match with given PID of the mother. We find mother of pion if it is D* and mother of D0 if it is D*. If they are both positive and they both match each other, then it is detected as a soft pion @@ -827,6 +1039,7 @@ struct HfCorrelatorD0Hadrons { auto indexMotherD0 = RecoDecay::getMother(mcParticles, particleTrigg, Pdg::kDStar, true, nullptr, 1); bool correlationStatus = false; if (std::abs(particleAssoc.pdgCode()) == kPiPlus && indexMotherPi >= 0 && indexMotherD0 >= 0 && indexMotherPi == indexMotherD0) { + addSoftPionTrackForOfflineMixing(particleAssoc.globalIndex(), (particleTrigg.pdgCode() == Pdg::kD0) ? aod::hf_d0_assoc_tracks::SoftPiD0 : aod::hf_d0_assoc_tracks::SoftPiD0bar); if (!storeAutoCorrelationFlag) { continue; } @@ -854,6 +1067,31 @@ struct HfCorrelatorD0Hadrons { } // end inner loop (Tracks) } } // end outer loop (D0) + + if (hasAcceptedD0ForOfflineMixing) { + for (const auto& particleAssoc : mcParticles) { + if (std::abs(particleAssoc.eta()) > etaTrackMax) { + continue; + } + if (particleAssoc.pt() < ptTrackMin) { + continue; + } + if ((std::abs(particleAssoc.pdgCode()) != kElectron) && (std::abs(particleAssoc.pdgCode()) != kMuonMinus) && (std::abs(particleAssoc.pdgCode()) != kPiPlus) && (std::abs(particleAssoc.pdgCode()) != kKPlus) && (std::abs(particleAssoc.pdgCode()) != kProton)) { + continue; + } + if (!particleAssoc.isPhysicalPrimary()) { + continue; + } + const auto softPiStatus = getSoftPionStatusForOfflineMixing(particleAssoc.globalIndex()); + + registry.fill(HIST("hTracksBeforeSoftMix"), particleAssoc.pt()); + if (softPiStatus == aod::hf_d0_assoc_tracks::NotSoftPi) { + registry.fill(HIST("hTracksAfterSoftMix"), particleAssoc.pt()); + } + + entryD0Hadron(particleAssoc.phi(), particleAssoc.eta(), particleAssoc.pt(), poolBin, gCollisionId, timeStamp, softPiStatus); + } + } } PROCESS_SWITCH(HfCorrelatorD0Hadrons, processMcGen, "Process MC Gen mode", false); @@ -864,7 +1102,7 @@ struct HfCorrelatorD0Hadrons { SelectedCandidatesDataMl const& candidates, SelectedTracks const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { registry.fill(HIST("hMultFT0M"), collision.multFT0M()); registry.fill(HIST("hZvtx"), collision.posZ()); @@ -953,7 +1191,7 @@ struct HfCorrelatorD0Hadrons { SelectedTracksMcRec const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(candidates, tracks); Pair const pairMcRec{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; bool isD0Prompt = false; @@ -1088,7 +1326,7 @@ struct HfCorrelatorD0Hadrons { void processMcGenMixedEvent(SelectedCollisionsMcGen const& collisions, SelectedParticlesMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; diff --git a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx index 77e43224d52..b6d40c9e764 100644 --- a/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDMesonPairs.cxx @@ -18,11 +18,17 @@ #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/HfMlResponseD0ToKPi.h" #include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/HFC/DataModel/DMesonPairsTables.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" #include #include @@ -32,7 +38,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -43,8 +51,13 @@ #include +#include +#include #include +#include #include +#include +#include #include using namespace o2; @@ -55,6 +68,8 @@ using namespace o2::framework::expressions; namespace { +using DecayChannel = o2::hf_decay::hf_cand_2prong::DecayChannelMain; + enum CandidateType { SelectedD = 0, // This particle is selected as a D SelectedDbar, // This particle is selected as a Dbar @@ -71,6 +86,53 @@ enum PairTypeOfSelMassSel { } // namespace using McParticlesPlus2Prong = soa::Join; +// definition of ME variables +using BinningType = ColumnBinningPolicy>; + +struct HfCorrelatorD0HadronsSelection { + Produces d0Sel; + + Configurable useSel8{"useSel8", true, "Flag for applying sel8 for collision selection"}; + Configurable selNoSameBunchPileUpColl{"selNoSameBunchPileUpColl", true, "Flag for rejecting the collisions associated with the same bunch crossing"}; + Configurable doSelD0Collision{"doSelD0Collision", true, "Select collisions with at least one D0"}; + Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; + Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable ptCandMin{"ptCandMin", 1., "min. cand. pT"}; + + SliceCache cache; + + using SelCollisions = soa::Join; + using CandidatesD0Data = soa::Join; + + Partition selectedD0Candidates = + aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || + aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + + // Process function to select collisions with at least one D0 candidate passing the selection criteria + void processD0SelectionData(SelCollisions::iterator const& collision, + CandidatesD0Data const&) + { + bool const isSel8 = !useSel8 || collision.sel8(); + bool const isNoSameBunchPileUp = !selNoSameBunchPileUpColl || + collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + bool isD0Found = !doSelD0Collision; + + if (doSelD0Collision) { + auto grouped = selectedD0Candidates.sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + for (const auto& candidate : grouped) { + if (std::abs(HfHelper::yD0(candidate)) > yCandMax || candidate.pt() < ptCandMin) { + continue; + } + isD0Found = true; + break; + } + } + + d0Sel(isD0Found && isSel8 && isNoSameBunchPileUp); + } + PROCESS_SWITCH(HfCorrelatorD0HadronsSelection, processD0SelectionData, "Process D0 Selection Data", true); +}; struct HfCorrelatorDMesonPairs { @@ -80,23 +142,26 @@ struct HfCorrelatorDMesonPairs { Produces entryD0PairMcGen; Produces entryD0PairMcGenInfo; + // Tables for event mixing + Produces entryDMesonInfoCand1; + Produces entryDMesonInfoCand2; + Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0"}; Configurable selectionFlagD0bar{"selectionFlagD0bar", 1, "Selection Flag for D0bar"}; - Configurable selectionFlagHf{"selectionFlagHf", 1, "Selection Flag for HF flagged candidates"}; - Configurable yCandMax{"yCandMax", 0.8, "maxmum |y| of D0 candidates"}; + Configurable yCandMax{"yCandMax", 0.8, "maximum |y| of D0 candidates"}; Configurable ptCandMin{"ptCandMin", -1., "minimum pT of D0 candidates"}; Configurable> binsPt{"binsPt", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for candidate mass plots"}; Configurable selectSignalRegionOnly{"selectSignalRegionOnly", false, "only use events close to PDG peak"}; Configurable massCut{"massCut", 0.05, "Maximum deviation from PDG peak allowed for signal region"}; - Configurable daughterTracksCutFlag{"daughterTracksCutFlag", false, "Flag to add cut on daughter tracks"}; Configurable removeAmbiguous{"removeAmbiguous", false, "Flag to remove ambiguous candidates"}; Configurable ptMaxRemoveAmbiguous{"ptMaxRemoveAmbiguous", 5.0, "Max. pT to remove the ambiguous candidates"}; + Configurable applyMixedEvent{"applyMixedEvent", false, "Flag to make the event mixing"}; // ML inference Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutsMl{"cutsMl", {static_cast(hf_cuts_ml::Cuts[0]), hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; @@ -108,7 +173,17 @@ struct HfCorrelatorDMesonPairs { Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; SliceCache cache; - Preslice perCol2Prong = aod::hf_cand::collisionId; + + // Table aliases. The partitions below must have exactly the same type as the + // candidate table taken by the process functions, otherwise they are never bound. + using SelCollisionsWithD0 = soa::Filtered>; + using CandidatesD0Data = soa::Join; + using CandidatesD0McRec = soa::Join; + + Filter collisionFilter = aod::hf_selection_dmeson_collision::dmesonSel == true; + + Partition selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; + Partition selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; o2::analysis::HfMlResponseD0ToKPi hfMlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -119,11 +194,6 @@ struct HfCorrelatorDMesonPairs { std::vector outputMlD0Cand2; std::vector outputMlD0barCand2; - // using TracksWPid = soa::Join; - - Partition> selectedD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0bar; - Partition> selectedD0CandidatesMc = aod::hf_sel_candidate_d0::isRecoHfFlag >= selectionFlagHf; - // ThnSparse for ML outputScores and Vars ConfigurableAxis thnConfigAxisBkgScore{"thnConfigAxisBkgScore", {100, 0, 1}, "Bkg score bins"}; ConfigurableAxis thnConfigAxisSignalScore{"thnConfigAxisSignalScore", {100, 0, 1}, "Signal score bins"}; @@ -141,6 +211,11 @@ struct HfCorrelatorDMesonPairs { HistogramConfigSpec hTH2Pid{HistType::kTH2F, {{500, 0., 10.}, {400, -20., 20.}}}; HistogramConfigSpec hTH3PtVsYVsNContrib{HistType::kTH3F, {{360, 0., 36.}, {20, -1., 1.}, {120, -0.5, 119.5}}}; + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; + ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; + ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; + std::unique_ptr corrBinning; + HistogramRegistry registry{ "registry", {{"hPtCand", "D meson candidates;candidate #it{p}_{T} (GeV/#it{c});entries", hTH1Pt}, @@ -158,7 +233,7 @@ struct HfCorrelatorDMesonPairs { {"hPhiMcGen", "D meson candidates MC Gen;candidate #it{#varphi};entries", hTH1Phi}, {"hPtVsYVsNContribMcGen", "D meson candidates MC Gen;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, {"hPtVsYVsNContribMcGenPrompt", "D meson candidates MC Gen Prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, - {"hPtVsYVsNContribMcGenNonPrompt", "D meson candidates MC Gen Prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, + {"hPtVsYVsNContribMcGenNonPrompt", "D meson candidates MC Gen Non-prompt;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, {"hNContribMcGen", "D meson candidates MC Gen;Number of PV contributors", hTH1NContrib}, // MC Rec plots {"hPtVsYVsNContribMcRec", "D meson candidates MC Rec;candidate #it{p}_{T} (GeV/#it{c});#it{y};Number of contributors", hTH3PtVsYVsNContrib}, @@ -175,7 +250,6 @@ struct HfCorrelatorDMesonPairs { void init(InitContext&) { - if (applyMl) { hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); if (loadModelsFromCCDB) { @@ -188,9 +262,9 @@ struct HfCorrelatorDMesonPairs { hfMlResponse.init(); } - auto vbins = (std::vector)binsPt; - constexpr int kNBinsSelStatus = 25; - std::string labels[kNBinsSelStatus]; + auto vbins = static_cast>(binsPt); + constexpr int NBinsSelStatus = 25; + std::array labels; labels[0] = "total # of Selected pairs"; // Cand1 analysis @@ -222,17 +296,17 @@ struct HfCorrelatorDMesonPairs { labels[23] = "# of True D+Dbar Pairs"; labels[24] = "# of True Dbar+D Pairs"; - AxisSpec const axisSelStatus = {kNBinsSelStatus, 0.5, kNBinsSelStatus + 0.5, ""}; + AxisSpec const axisSelStatus = {NBinsSelStatus, 0.5, NBinsSelStatus + 0.5, ""}; registry.add("hSelectionStatus", "D Meson candidates;selection status;entries", HistType::kTH1F, {axisSelStatus}); registry.add("hSelectionStatusMcGen", "D Meson candidates MC Gen;selection status;entries", HistType::kTH1F, {axisSelStatus}); - for (int iBin = 0; iBin < kNBinsSelStatus; iBin++) { + for (int iBin = 0; iBin < NBinsSelStatus; iBin++) { registry.get(HIST("hSelectionStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); registry.get(HIST("hSelectionStatusMcGen"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } - constexpr int kNBinsMatching = 8; - std::string labelsMatching[kNBinsMatching]; + constexpr int NBinsMatching = 8; + std::array labelsMatching; // Cand1 analysis labelsMatching[0] = "total # of Cand 1"; labelsMatching[1] = "# of matched D Cand 1"; @@ -244,17 +318,17 @@ struct HfCorrelatorDMesonPairs { labelsMatching[6] = "# of matched Dbar Cand 2"; labelsMatching[7] = "# of unmatched Cand 2"; - AxisSpec const axisMatching = {kNBinsMatching, 0.5, kNBinsMatching + 0.5, ""}; + AxisSpec const axisMatching = {NBinsMatching, 0.5, NBinsMatching + 0.5, ""}; registry.add("hMatchingMcRec", "D Meson candidates; MC matching status;entries", HistType::kTH1F, {axisMatching}); registry.add("hMatchingMcGen", "D Meson candidates; MC matching status;entries", HistType::kTH1F, {axisMatching}); - for (int iBin = 0; iBin < kNBinsMatching; iBin++) { + for (int iBin = 0; iBin < NBinsMatching; iBin++) { registry.get(HIST("hMatchingMcRec"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMatching[iBin].data()); registry.get(HIST("hMatchingMcGen"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMatching[iBin].data()); } - constexpr int kNBinsSinglePart = 6; - std::string labelsSinglePart[kNBinsSinglePart]; + constexpr int NBinsSinglePart = 6; + std::array labelsSinglePart; // Candidate analysis labelsSinglePart[0] = "total # of Candidates"; labelsSinglePart[1] = "# of selected D"; @@ -263,11 +337,11 @@ struct HfCorrelatorDMesonPairs { labelsSinglePart[4] = "# of true D"; labelsSinglePart[5] = "# of true Dbar"; - AxisSpec const axisSinglePart = {kNBinsSinglePart, 0.5, kNBinsSinglePart + 0.5, ""}; + AxisSpec const axisSinglePart = {NBinsSinglePart, 0.5, NBinsSinglePart + 0.5, ""}; registry.add("hStatusSinglePart", "D Meson candidates; MC matching status;entries", HistType::kTH1F, {axisSinglePart}); registry.add("hStatusSinglePartMcGen", "D Meson candidates; MC matching status;entries", HistType::kTH1F, {axisSinglePart}); - for (int iBin = 0; iBin < kNBinsSinglePart; iBin++) { + for (int iBin = 0; iBin < NBinsSinglePart; iBin++) { registry.get(HIST("hStatusSinglePart"))->GetXaxis()->SetBinLabel(iBin + 1, labelsSinglePart[iBin].data()); registry.get(HIST("hStatusSinglePartMcGen"))->GetXaxis()->SetBinLabel(iBin + 1, labelsSinglePart[iBin].data()); } @@ -284,6 +358,7 @@ struct HfCorrelatorDMesonPairs { registry.add("hInputCheckD0OrD0barMcGen", "Check on input D0 | D0bar meson candidates/event MC Gen", {HistType::kTH1F, {axisInputD0}}); registry.add("hMass", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hMassSig", "MC-matched D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecPrompt", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecNonPrompt", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassMcRecReflections", "D Meson pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -309,6 +384,93 @@ struct HfCorrelatorDMesonPairs { registry.add("hnDMeson", "Thn for D0 candidates", HistType::kTHnSparseD, axes); registry.get(HIST("hnDMeson"))->Sumw2(); } + + // Event mixing + AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; + corrBinning = std::make_unique(BinningType{{binsZVtx, binsMultiplicity}}); + + if (applyMixedEvent) { + registry.add("hMultFT0M", "multiplicity;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); + registry.add("hZvtx", "z vertex;z vertex;entries", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hD0Bin", "D0 selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); + registry.add("hD0Cand2Bin", "D0 selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{axisPoolBin}}}); + } + } + + /// ------- FUNCTION DEFINITIONS ------- + + /// Kinematic, signal-region and ambiguity selections of a candidate + /// \return true if the candidate passes the selections + template + bool passKinematicSelections(const T& candidate, const bool isDCand, const bool isDbarCand) + { + if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { + return false; + } + if (ptCandMin >= 0. && candidate.pt() < ptCandMin) { + return false; + } + bool const isSignalD0Cand = std::abs(HfHelper::invMassD0ToPiK(candidate) - MassD0) < massCut; + bool const isSignalD0barCand = std::abs(HfHelper::invMassD0barToKPi(candidate) - MassD0Bar) < massCut; + if (selectSignalRegionOnly && !(isSignalD0Cand || isSignalD0barCand)) { + return false; + } + // Remove ambiguous D0 candidates if flag is true + if (removeAmbiguous && (isDCand && isDbarCand) && candidate.pt() < ptMaxRemoveAmbiguous) { + return false; + } + return true; + } + + /// ML selections of a candidate + /// \return true if the candidate is selected by the ML model (as D0 and/or D0bar) + template + bool passMlSelections(const T& candidate, const bool isDCand, const bool isDbarCand, std::vector& outputMlD0Cand, std::vector& outputMlD0barCand) + { + bool isSelectedMlD0Cand = false; + bool isSelectedMlD0barCand = false; + + if (isDCand) { + std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate, o2::constants::physics::kD0); + isSelectedMlD0Cand = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate.pt(), outputMlD0Cand); + } + if (isDbarCand) { + std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate, o2::constants::physics::kD0Bar); + isSelectedMlD0barCand = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate.pt(), outputMlD0barCand); + } + + return isSelectedMlD0Cand || isSelectedMlD0barCand; + } + + /// Rapidity and pT selections of gen-level particles + template + bool passGenSelections(const T& particle) + { + return std::abs(particle.y()) <= yCandMax && (ptCandMin < 0. || particle.pt() >= ptCandMin); + } + + /// Sets bits to select pair types. + /// \param candidate is candidate + /// \return bitmap with type of pair (DD, DbarDbar, DDbar, DbarD) + uint8_t setPairType(const uint8_t candidateType1, const uint8_t candidateType2) + { + uint8_t pairType(0); + bool const isDCand1 = isD(candidateType1), isDCand2 = isD(candidateType2); + bool const isDbarCand1 = isDbar(candidateType1), isDbarCand2 = isDbar(candidateType2); + + if (isDCand1 && isDCand2) { + SETBIT(pairType, DD); + } + if (isDbarCand1 && isDbarCand2) { + SETBIT(pairType, DbarDbar); + } + if (isDCand1 && isDbarCand2) { + SETBIT(pairType, DDbar); + } + if (isDbarCand1 && isDCand2) { + SETBIT(pairType, DbarD); + } + return pairType; } /// Sets bits to select candidate type for D0 @@ -326,10 +488,10 @@ struct HfCorrelatorDMesonPairs { SETBIT(candidateType, SelectedDbar); } if constexpr (IsMcRec) { - if (candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // matched as D0 + if (candidate.flagMcMatchRec() == DecayChannel::D0ToPiK) { // matched as D0 SETBIT(candidateType, TrueD); } - if (candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { // matched as D0bar + if (candidate.flagMcMatchRec() == -DecayChannel::D0ToPiK) { // matched as D0bar SETBIT(candidateType, TrueDbar); } } @@ -353,37 +515,40 @@ struct HfCorrelatorDMesonPairs { } /// Check if the candidate is a D - /// \param candidate is candidate - /// \return true if is a D bool isD(const uint8_t candidateType) { return (TESTBIT(candidateType, SelectedD)); } /// Check if the candidate is a Dbar - /// \param candidate is candidate - /// \return true if is a Dbar bool isDbar(const uint8_t candidateType) { return (TESTBIT(candidateType, SelectedDbar)); } /// Check if the candidate is a true D - /// \param candidate is candidate - /// \return true if is a true D bool isTrueD(const uint8_t candidateType) { return (TESTBIT(candidateType, TrueD)); } /// Check if the candidate is a true Dbar - /// \param candidate is candidate - /// \return true if is a true Dbar bool isTrueDbar(const uint8_t candidateType) { return (TESTBIT(candidateType, TrueDbar)); } + /// Canonical pair ordering: D0 before D0bar; for equal flavours, higher pT first. + /// \return true if candidate 1 and candidate 2 should be exchanged + template + static bool shouldSwapOrder(const bool isD1, const bool isDbar1, const bool isD2, const bool isDbar2, const T& candidate1, const T& candidate2) + { + if (isD1 != isD2 || isDbar1 != isDbar2) { // different flavour content + return !isD1 && isD2; // Dbar(-only) first, D second -> swap + } + return candidate1.pt() < candidate2.pt(); + } + /// Fill PID related plots for D0 and D0bar /// \param candidate is candidate template @@ -408,51 +573,51 @@ struct HfCorrelatorDMesonPairs { } /// Fill counters for D0 and D0bar - /// \param selectedD0Candidates contains all D0 candidates - template - void getCountersPerEvent(const T& selectedD0Candidates) + /// \param d0Candidates contains the D0 candidates of the collision + template + void getCountersPerEvent(const T& d0Candidates) { int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; - for (const auto& candidate : selectedD0Candidates) { + for (const auto& candidate : d0Candidates) { // Get counters per event - bool const isSignalD0 = std::abs(HfHelper::invMassD0ToPiK(candidate) - MassD0) < massCut; - bool const isSignalD0bar = std::abs(HfHelper::invMassD0barToKPi(candidate) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0 || isSignalD0bar)) { - continue; - } - auto candidateType1 = assignCandidateTypeD0(candidate); // Candidate type attribution - registry.fill(HIST("hPtCand"), candidate.pt()); - if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate.pt() < ptCandMin) { + auto const candidateType = assignCandidateTypeD0(candidate); // Candidate type attribution + bool const isDCand = isD(candidateType); + bool const isDbarCand = isDbar(candidateType); + bool const isTrueDCand = isTrueD(candidateType); + bool const isTrueDbarCand = isTrueDbar(candidateType); + + if (!passKinematicSelections(candidate, isDCand, isDbarCand)) { continue; } - bool const isDCand1 = isD(candidateType1); - bool const isDbarCand1 = isDbar(candidateType1); - if (isDCand1) { + if (isDCand) { nDevent++; } - if (isDbarCand1) { + if (isDbarCand) { nDbarevent++; } - if (isDCand1 && isDbarCand1) { + if (isDCand && isDbarCand) { nDDbarevent++; } - if (isDCand1 || isDbarCand1) { + if (isDCand || isDbarCand) { nDorDbarevent++; } // Fill selection status single particle registry.fill(HIST("hStatusSinglePart"), 1); - if (isDCand1 && !isDbarCand1) { + if (isDCand && !isDbarCand) { registry.fill(HIST("hStatusSinglePart"), 2); - } else if (isDbarCand1 && !isDCand1) { + } else if (isDbarCand && !isDCand) { registry.fill(HIST("hStatusSinglePart"), 3); - } else if (isDCand1 && isDbarCand1) { + } else if (isDCand && isDbarCand) { registry.fill(HIST("hStatusSinglePart"), 4); } + // fill true info (available only in MC) + if (isTrueDCand) { + registry.fill(HIST("hStatusSinglePart"), 5); + } else if (isTrueDbarCand) { + registry.fill(HIST("hStatusSinglePart"), 6); + } } if (nDevent > 0) { registry.fill(HIST("hInputCheckD0"), nDevent); @@ -468,11 +633,12 @@ struct HfCorrelatorDMesonPairs { } } - /// Fill selection status histogram - void fillEntry(const bool& isDCand1, const bool& isDbarCand1, const bool& isDCand2, const bool& isDbarCand2, - const uint8_t& candidateType1, const uint8_t& candidateType2, float yCand1, float yCand2, float phiCand1, float phiCand2, - double ptCand1, double ptCand2, float massDCand1, float massDbarCand1, float massDCand2, float massDbarCand2) + void fillSelectionStatusHisto(const uint8_t candidateType1, const uint8_t candidateType2) { + bool const isDCand1 = isD(candidateType1), isDCand2 = isD(candidateType2); + bool const isDbarCand1 = isDbar(candidateType1), isDbarCand2 = isDbar(candidateType2); + bool const isTrueDCand1 = isTrueD(candidateType1), isTrueDCand2 = isTrueD(candidateType2); + bool const isTrueDbarCand1 = isTrueDbar(candidateType1), isTrueDbarCand2 = isTrueDbar(candidateType2); /// Fill information on the D candidates registry.fill(HIST("hSelectionStatus"), 2); @@ -494,311 +660,120 @@ struct HfCorrelatorDMesonPairs { } /// Collect information on the D pairs - uint8_t pairType(0); registry.fill(HIST("hSelectionStatus"), 1); if (isDCand1 && isDCand2) { - SETBIT(pairType, DD); registry.fill(HIST("hSelectionStatus"), 14); if ((!isDbarCand1) && (!isDbarCand2)) { registry.fill(HIST("hSelectionStatus"), 18); } } if (isDbarCand1 && isDbarCand2) { - SETBIT(pairType, DbarDbar); registry.fill(HIST("hSelectionStatus"), 15); if ((!isDCand1) && (!isDCand2)) { registry.fill(HIST("hSelectionStatus"), 19); } } if (isDCand1 && isDbarCand2) { - SETBIT(pairType, DDbar); registry.fill(HIST("hSelectionStatus"), 16); if ((!isDbarCand1) && (!isDCand2)) { registry.fill(HIST("hSelectionStatus"), 20); } } if (isDbarCand1 && isDCand2) { - SETBIT(pairType, DbarD); registry.fill(HIST("hSelectionStatus"), 17); if ((!isDCand1) && (!isDbarCand2)) { registry.fill(HIST("hSelectionStatus"), 21); } } - - entryD0Pair(ptCand1, ptCand2, yCand1, yCand2, phiCand1, phiCand2, massDCand1, massDbarCand1, massDCand2, massDbarCand2, pairType, candidateType1, candidateType2); - } - - void fillMcHistos(int8_t matchedRec1, int8_t matchedRec2, int8_t isTrueDCand1, int8_t isTrueDbarCand1, int8_t isTrueDCand2, int8_t isTrueDbarCand2) - { - // Fill hMatchingMcRec - Cand 1 - registry.fill(HIST("hMatchingMcRec"), 1); - if (matchedRec1 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 2); - } else if (matchedRec1 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 3); - } else if (matchedRec1 == 0) { - registry.fill(HIST("hMatchingMcRec"), 4); - } - // Fill hMatchingMcRec - Cand 2 - registry.fill(HIST("hMatchingMcRec"), 5); - if (matchedRec2 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 6); - } else if (matchedRec2 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcRec"), 7); - } else if (matchedRec2 == 0) { - registry.fill(HIST("hMatchingMcRec"), 8); - } // Fill True info - if (isTrueDCand1 != 0) { + if (isTrueDCand1) { registry.fill(HIST("hSelectionStatus"), 6); - } else if (isTrueDbarCand1 != 0) { + } else if (isTrueDbarCand1) { registry.fill(HIST("hSelectionStatus"), 7); } - if (isTrueDCand2 != 0) { + if (isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 12); - } else if (isTrueDbarCand2 != 0) { + } else if (isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 13); } - if ((isTrueDCand1 != 0) && (isTrueDCand2 != 0)) { + if (isTrueDCand1 && isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 22); - } else if ((isTrueDbarCand1 != 0) && (isTrueDbarCand2 != 0)) { + } else if (isTrueDbarCand1 && isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 23); - } else if ((isTrueDCand1 != 0) && (isTrueDbarCand2 != 0)) { + } else if (isTrueDCand1 && isTrueDbarCand2) { registry.fill(HIST("hSelectionStatus"), 24); - } else if ((isTrueDbarCand1 != 0) && (isTrueDCand2 != 0)) { + } else if (isTrueDbarCand1 && isTrueDCand2) { registry.fill(HIST("hSelectionStatus"), 25); } } - /// D0(bar)-D0(bar) correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) - void processData(aod::Collision const& collision, - soa::Join const& candidates, aod::Tracks const&) + template + void fillMatchingHisto(const int8_t matched1, const int8_t matched2) { - for (const auto& candidate : candidates) { - analysePid(candidate); + auto hist = IsGen ? registry.get(HIST("hMatchingMcGen")) : registry.get(HIST("hMatchingMcRec")); + hist->Fill(1); + if (matched1 == DecayChannel::D0ToPiK) { + hist->Fill(2); + } else if (matched1 == -DecayChannel::D0ToPiK) { + hist->Fill(3); + } else if (matched1 == 0) { + hist->Fill(4); } - auto selectedD0CandidatesGrouped = selectedD0Candidates->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - getCountersPerEvent(selectedD0CandidatesGrouped); - // protection against empty tables to be sliced - if (selectedD0Candidates.size() <= 1) { - return; + hist->Fill(5); + if (matched2 == DecayChannel::D0ToPiK) { + hist->Fill(6); + } else if (matched2 == -DecayChannel::D0ToPiK) { + hist->Fill(7); + } else if (matched2 == 0) { + hist->Fill(8); } - for (const auto& candidate1 : selectedD0CandidatesGrouped) { - - outputMlD0Cand1.clear(); - outputMlD0barCand1.clear(); - - if (std::abs(HfHelper::yD0(candidate1)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate1.pt() < ptCandMin) { - continue; - } - auto prong0Cand1 = candidate1.template prong0_as(); - auto prong1Cand1 = candidate1.template prong1_as(); - - bool const isSignalD0Cand1 = std::abs(HfHelper::invMassD0ToPiK(candidate1) - MassD0) < massCut; - bool const isSignalD0barCand1 = std::abs(HfHelper::invMassD0barToKPi(candidate1) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand1 || isSignalD0barCand1)) { - continue; - } - - auto candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution - bool const isDCand1 = isD(candidateType1); - bool const isDbarCand1 = isDbar(candidateType1); - - bool isSelectedMlD0Cand1 = false; - bool isSelectedMlD0barCand1 = false; - - if (applyMl) { - if (isDCand1) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0); - isSelectedMlD0Cand1 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate1.pt(), outputMlD0Cand1); - } - if (isDbarCand1) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand1 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate1.pt(), outputMlD0barCand1); - } - - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand1 && !isSelectedMlD0barCand1) { - continue; - } - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand1 && isDbarCand1) && candidate1.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - registry.fill(HIST("hPtProng0"), candidate1.ptProng0()); - registry.fill(HIST("hPtProng1"), candidate1.ptProng1()); - registry.fill(HIST("hEta"), candidate1.eta()); - registry.fill(HIST("hPhi"), candidate1.phi()); - registry.fill(HIST("hY"), candidate1.y(MassD0)); - registry.fill(HIST("hPtCandAfterCut"), candidate1.pt()); - registry.fill(HIST("hPVContrib"), collision.numContrib()); - - if (isDCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); - if (applyMl) { - registry.fill(HIST("hnDMesonMl"), outputMlD0Cand1[0], outputMlD0Cand1[1], HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } else { - registry.fill(HIST("hnDMeson"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } - } - if (isDbarCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); - if (applyMl) { - registry.fill(HIST("hnDMesonMl"), outputMlD0barCand1[0], outputMlD0barCand1[1], HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } else { - registry.fill(HIST("hnDMeson"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), 0, candidateType1); - } - } - - for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGrouped.end(); ++candidate2) { - - outputMlD0Cand2.clear(); - outputMlD0barCand2.clear(); - - if (std::abs(HfHelper::yD0(candidate2)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate2.pt() < ptCandMin) { - continue; - } - auto prong0Cand2 = candidate2.template prong0_as(); - auto prong1Cand2 = candidate2.template prong1_as(); - if (daughterTracksCutFlag && ((prong0Cand1 == prong0Cand2) || (prong1Cand1 == prong1Cand2) || (prong0Cand1 == prong1Cand2) || (prong1Cand1 == prong0Cand2))) { - continue; - } - - bool const isSignalD0Cand2 = std::abs(HfHelper::invMassD0ToPiK(candidate2) - MassD0) < massCut; - bool const isSignalD0barCand2 = std::abs(HfHelper::invMassD0barToKPi(candidate2) - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand2 || isSignalD0barCand2)) { - continue; - } - auto candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution - - bool const isDCand2 = isD(candidateType2); - bool const isDbarCand2 = isDbar(candidateType2); - - bool isSelectedMlD0Cand2 = false; - bool isSelectedMlD0barCand2 = false; - - if (applyMl) { - if (isDCand2) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0); - isSelectedMlD0Cand2 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate2.pt(), outputMlD0Cand2); - } - if (isDbarCand2) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand2 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate2.pt(), outputMlD0barCand2); - } - - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand2 && !isSelectedMlD0barCand2) { - continue; - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand2 && isDbarCand2) && candidate2.pt() < ptMaxRemoveAmbiguous) { - continue; - } - - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, HfHelper::yD0(candidate1), HfHelper::yD0(candidate2), - candidate1.phi(), candidate2.phi(), candidate1.pt(), candidate2.pt(), HfHelper::invMassD0ToPiK(candidate1), HfHelper::invMassD0barToKPi(candidate1), - HfHelper::invMassD0ToPiK(candidate2), HfHelper::invMassD0barToKPi(candidate2)); - - entryD0PairMl(outputMlD0Cand1, outputMlD0barCand1, outputMlD0Cand2, outputMlD0barCand2); - } else { - // Fill entries - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, HfHelper::yD0(candidate1), HfHelper::yD0(candidate2), candidate1.phi(), candidate2.phi(), - candidate1.pt(), candidate2.pt(), HfHelper::invMassD0ToPiK(candidate1), HfHelper::invMassD0barToKPi(candidate1), - HfHelper::invMassD0ToPiK(candidate2), HfHelper::invMassD0barToKPi(candidate2)); - } - } // end inner loop (Cand2) - } // end outer loop (Cand1) } - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processData, "Process data mode", true); - - void processMcRec(aod::Collision const& collision, soa::Join const& candidates, aod::Tracks const&) + /// D0(bar)-D0(bar) correlation pair builder, shared by the data and MC-reco modes + /// \param collision is the collision + /// \param selectedD0CandidatesGrouped is the per-collision slice of the selected D0 candidates + /// \param tracks are the (already filtered) tracks of the collision + /// \param timeStamp is the BC timestamp of the collision + template + void processImpl(TCollision const& collision, + TCandidates const& selectedD0CandidatesGrouped, + const int64_t timeStamp) { - for (const auto& candidate : candidates) { - analysePid(candidate); - } - auto selectedD0CandidatesGroupedMc = selectedD0CandidatesMc->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); - getCountersPerEvent(selectedD0CandidatesGroupedMc); - // protection against empty tables to be sliced - if (selectedD0CandidatesMc.size() <= 1) { - return; - } - for (const auto& candidate1 : selectedD0CandidatesGroupedMc) { + int const gCollisionId = collision.globalIndex(); + int const poolBin = corrBinning->getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); + + getCountersPerEvent(selectedD0CandidatesGrouped); + + for (const auto& candidate1 : selectedD0CandidatesGrouped) { + analysePid(candidate1); outputMlD0Cand1.clear(); outputMlD0barCand1.clear(); - auto ptCandidate1 = candidate1.pt(); - auto yCandidate1 = HfHelper::yD0(candidate1); - auto phiCandidate1 = candidate1.phi(); - float const massD0Cand1 = HfHelper::invMassD0ToPiK(candidate1); - float const massD0barCand1 = HfHelper::invMassD0barToKPi(candidate1); - auto prong0Cand1 = candidate1.template prong0_as(); - auto prong1Cand1 = candidate1.template prong1_as(); + auto const prong0Cand1 = candidate1.prong0Id(); + auto const prong1Cand1 = candidate1.prong1Id(); - if (std::abs(HfHelper::yD0(candidate1)) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && candidate1.pt() < ptCandMin) { - continue; + [[maybe_unused]] int8_t matchedRec1 = 0; + [[maybe_unused]] int8_t originRec1 = 0; + if constexpr (IsMcRec) { + matchedRec1 = candidate1.flagMcMatchRec(); + originRec1 = candidate1.originMcRec(); } - bool const isSignalD0Cand1 = std::abs(massD0Cand1 - MassD0) < massCut; - bool const isSignalD0barCand1 = std::abs(massD0barCand1 - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand1 || isSignalD0barCand1)) { - continue; - } - if (candidate1.isSelD0() < selectionFlagD0 && candidate1.isSelD0bar() < selectionFlagD0bar) { - continue; - } - - auto candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution + auto const candidateType1 = assignCandidateTypeD0(candidate1); // Candidate type attribution bool const isDCand1 = isD(candidateType1); bool const isDbarCand1 = isDbar(candidateType1); bool const isTrueDCand1 = isTrueD(candidateType1); bool const isTrueDbarCand1 = isTrueDbar(candidateType1); - int8_t matchedRec1 = candidate1.flagMcMatchRec(); - int8_t originRec1 = candidate1.originMcRec(); - - bool isSelectedMlD0Cand1 = false; - bool isSelectedMlD0barCand1 = false; - - if (applyMl) { - if (isDCand1) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0); - isSelectedMlD0Cand1 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate1.pt(), outputMlD0Cand1); - } - if (isDbarCand1) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate1, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand1 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate1.pt(), outputMlD0barCand1); - } - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand1 && !isSelectedMlD0barCand1) { - continue; - } - } + registry.fill(HIST("hPtCand"), candidate1.pt()); - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand1 && isDbarCand1) && candidate1.pt() < ptMaxRemoveAmbiguous) { + if (!passKinematicSelections(candidate1, isDCand1, isDbarCand1)) { continue; } - if (isTrueDCand1) { - registry.fill(HIST("hStatusSinglePart"), 5); - } else if (isTrueDbarCand1) { - registry.fill(HIST("hStatusSinglePart"), 6); + if (applyMl && !passMlSelections(candidate1, isDCand1, isDbarCand1, outputMlD0Cand1, outputMlD0barCand1)) { + continue; } registry.fill(HIST("hPtProng0"), candidate1.ptProng0()); @@ -807,15 +782,22 @@ struct HfCorrelatorDMesonPairs { registry.fill(HIST("hPhi"), candidate1.phi()); registry.fill(HIST("hY"), candidate1.y(MassD0)); registry.fill(HIST("hPtCandAfterCut"), candidate1.pt()); + registry.fill(HIST("hPVContrib"), collision.numContrib()); + if (applyMixedEvent) { + registry.fill(HIST("hD0Bin"), poolBin); + } if (isDCand1) { + registry.fill(HIST("hMass"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); + // Fill THnSparse for D0 candidates if (applyMl) { registry.fill(HIST("hnDMesonMl"), outputMlD0Cand1[0], outputMlD0Cand1[1], HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } else { registry.fill(HIST("hnDMeson"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } + // Fill MC-matched histograms for D0 candidates if (isTrueDCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); + registry.fill(HIST("hMassSig"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); registry.fill(HIST("hPtVsYVsNContribMcRec"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); registry.fill(HIST("hNContribMcRec"), collision.numContrib()); if (originRec1 == RecoDecay::Prompt) { @@ -828,148 +810,190 @@ struct HfCorrelatorDMesonPairs { } else if (isTrueDbarCand1) { registry.fill(HIST("hMassMcRecReflections"), HfHelper::invMassD0ToPiK(candidate1), candidate1.pt()); } + // Fill D0 table for offline event mixing + if (applyMixedEvent) { + entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0ToPiK(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); + } } if (isDbarCand1) { + registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + // Fill THnSparse for D0bar candidates if (applyMl) { registry.fill(HIST("hnDMesonMl"), outputMlD0barCand1[0], outputMlD0barCand1[1], HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } else { registry.fill(HIST("hnDMeson"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt(), candidate1.y(MassD0), collision.numContrib(), originRec1, candidateType1); } + // Fill MC-matched histograms for D0bar candidates if (isTrueDbarCand1) { - registry.fill(HIST("hMass"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hMassSig"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); registry.fill(HIST("hPtVsYVsNContribMcRec"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); registry.fill(HIST("hNContribMcRec"), collision.numContrib()); if (originRec1 == RecoDecay::Prompt) { registry.fill(HIST("hMassMcRecPrompt"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hPtVsYVsNContribMcRecPrompt"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); } else if (originRec1 == RecoDecay::NonPrompt) { registry.fill(HIST("hMassMcRecNonPrompt"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); + registry.fill(HIST("hPtVsYVsNContribMcRecNonPrompt"), candidate1.pt(), HfHelper::yD0(candidate1), collision.numContrib()); } } else if (isTrueDCand1) { registry.fill(HIST("hMassMcRecReflections"), HfHelper::invMassD0barToKPi(candidate1), candidate1.pt()); } + // Fill D0bar table for offline event mixing + if (applyMixedEvent) { + entryDMesonInfoCand1(candidate1.pt(), candidate1.eta(), candidate1.phi(), HfHelper::invMassD0barToKPi(candidate1), candidateType1, poolBin, gCollisionId, timeStamp); + } } - for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGroupedMc.end(); ++candidate2) { + // Loop on the second D0 candidate + for (auto candidate2 = candidate1 + 1; candidate2 != selectedD0CandidatesGrouped.end(); ++candidate2) { outputMlD0Cand2.clear(); outputMlD0barCand2.clear(); - auto ptCandidate2 = candidate2.pt(); - auto yCandidate2 = HfHelper::yD0(candidate2); - auto phiCandidate2 = candidate2.phi(); - float const massD0Cand2 = HfHelper::invMassD0ToPiK(candidate2); - float const massD0barCand2 = HfHelper::invMassD0barToKPi(candidate2); - auto prong0Cand2 = candidate2.template prong0_as(); - auto prong1Cand2 = candidate2.template prong1_as(); - - if (std::abs(HfHelper::yD0(candidate2)) > yCandMax) { + auto const prong0Cand2 = candidate2.prong0Id(); + auto const prong1Cand2 = candidate2.prong1Id(); + if ((prong0Cand1 == prong0Cand2) || (prong1Cand1 == prong1Cand2) || (prong0Cand1 == prong1Cand2) || (prong1Cand1 == prong0Cand2)) { continue; } - if (ptCandMin >= 0. && candidate2.pt() < ptCandMin) { - continue; - } - bool const isSignalD0Cand2 = std::abs(massD0Cand2 - MassD0) < massCut; - bool const isSignalD0barCand2 = std::abs(massD0barCand2 - MassD0Bar) < massCut; - if (selectSignalRegionOnly && !(isSignalD0Cand2 || isSignalD0barCand2)) { - continue; - } - if (candidate2.isSelD0() < selectionFlagD0 && candidate2.isSelD0bar() < selectionFlagD0bar) { - continue; - } - if (daughterTracksCutFlag && ((prong0Cand1 == prong0Cand2) || (prong1Cand1 == prong1Cand2) || (prong0Cand1 == prong1Cand2) || (prong1Cand1 == prong0Cand2))) { - continue; - } - auto candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution + auto const candidateType2 = assignCandidateTypeD0(candidate2); // Candidate type attribution bool const isDCand2 = isD(candidateType2); bool const isDbarCand2 = isDbar(candidateType2); - bool const isTrueDCand2 = isTrueD(candidateType2); - bool const isTrueDbarCand2 = isTrueDbar(candidateType2); - int8_t matchedRec2 = candidate2.flagMcMatchRec(); - int8_t originRec2 = candidate2.originMcRec(); + int8_t matchedRec2 = 0; + int8_t originRec2 = 0; + if constexpr (IsMcRec) { + matchedRec2 = candidate2.flagMcMatchRec(); + originRec2 = candidate2.originMcRec(); + } - bool isSelectedMlD0Cand2 = false; - bool isSelectedMlD0barCand2 = false; + if (!passKinematicSelections(candidate2, isDCand2, isDbarCand2)) { + continue; + } - if (applyMl) { + if (applyMl && !passMlSelections(candidate2, isDCand2, isDbarCand2, outputMlD0Cand2, outputMlD0barCand2)) { + continue; + } + + if (applyMixedEvent) { if (isDCand2) { - std::vector inputFeaturesD0 = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0); - isSelectedMlD0Cand2 = hfMlResponse.isSelectedMl(inputFeaturesD0, candidate2.pt(), outputMlD0Cand2); + entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0ToPiK(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); } if (isDbarCand2) { - std::vector inputFeaturesD0bar = hfMlResponse.getInputFeatures(candidate2, o2::constants::physics::kD0Bar); - isSelectedMlD0barCand2 = hfMlResponse.isSelectedMl(inputFeaturesD0bar, candidate2.pt(), outputMlD0barCand2); - } - - // Remove non-ML selected D0 candidates - if (!isSelectedMlD0Cand2 && !isSelectedMlD0barCand2) { - continue; - } - - // Remove ambiguous D0 candidates if flag is true - if (removeAmbiguous && (isDCand2 && isDbarCand2) && candidate2.pt() < ptMaxRemoveAmbiguous) { - continue; + entryDMesonInfoCand2(candidate2.pt(), candidate2.eta(), candidate2.phi(), HfHelper::invMassD0barToKPi(candidate2), candidateType2, poolBin, gCollisionId, timeStamp); } + registry.fill(HIST("hD0Cand2Bin"), poolBin); + } - // Fill tables - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, yCandidate1, yCandidate2, phiCandidate1, phiCandidate2, - ptCandidate1, ptCandidate2, massD0Cand1, massD0barCand1, massD0Cand2, massD0barCand2); - fillMcHistos(matchedRec1, matchedRec2, static_cast(isTrueDCand1), static_cast(isTrueDbarCand1), static_cast(isTrueDCand2), static_cast(isTrueDbarCand2)); - entryD0PairMcInfo(originRec1, originRec2, matchedRec1, matchedRec2); - entryD0PairMl(outputMlD0Cand1, outputMlD0barCand1, outputMlD0Cand2, outputMlD0barCand2); - - } else { - // Fill tables - fillEntry(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidateType1, candidateType2, yCandidate1, yCandidate2, phiCandidate1, phiCandidate2, - ptCandidate1, ptCandidate2, massD0Cand1, massD0barCand1, massD0Cand2, massD0barCand2); - fillMcHistos(matchedRec1, matchedRec2, static_cast(isTrueDCand1), static_cast(isTrueDbarCand1), static_cast(isTrueDCand2), static_cast(isTrueDbarCand2)); - entryD0PairMcInfo(originRec1, originRec2, matchedRec1, matchedRec2); + // Canonical ordering of the pair: D0 first (higher pT first for equal flavours) + bool const swapOrder = shouldSwapOrder(isDCand1, isDbarCand1, isDCand2, isDbarCand2, candidate1, candidate2); + uint8_t const type1 = swapOrder ? candidateType2 : candidateType1; + uint8_t const type2 = swapOrder ? candidateType1 : candidateType2; + auto const& first = swapOrder ? candidate2 : candidate1; + auto const& second = swapOrder ? candidate1 : candidate2; + uint8_t const pairType = setPairType(type1, type2); + + // Fill entries for D0-D0(bar) pairs and plot histos + fillSelectionStatusHisto(type1, type2); + entryD0Pair(first.pt(), second.pt(), HfHelper::yD0(first), HfHelper::yD0(second), first.eta(), second.eta(), + first.phi(), second.phi(), HfHelper::invMassD0ToPiK(first), HfHelper::invMassD0barToKPi(first), + HfHelper::invMassD0ToPiK(second), HfHelper::invMassD0barToKPi(second), pairType, type1, type2); + if (applyMl) { + entryD0PairMl(swapOrder ? outputMlD0Cand2 : outputMlD0Cand1, swapOrder ? outputMlD0barCand2 : outputMlD0barCand1, + swapOrder ? outputMlD0Cand1 : outputMlD0Cand2, swapOrder ? outputMlD0barCand1 : outputMlD0barCand2); + } + if constexpr (IsMcRec) { + fillMatchingHisto(swapOrder ? matchedRec2 : matchedRec1, swapOrder ? matchedRec1 : matchedRec2); + entryD0PairMcInfo(swapOrder ? originRec2 : originRec1, swapOrder ? originRec1 : originRec2, + swapOrder ? matchedRec2 : matchedRec1, swapOrder ? matchedRec1 : matchedRec2); } } // end inner loop (Cand2) } // end outer loop (Cand1) + + if (applyMixedEvent) { + registry.fill(HIST("hZvtx"), collision.posZ()); + registry.fill(HIST("hMultFT0M"), collision.multFT0M()); + } + } + + /// D0(bar)-D0(bar) correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) + void processData(SelCollisionsWithD0::iterator const& collision, + CandidatesD0Data const&, aod::Tracks const&, aod::BCsWithTimestamps const&) + { + auto const timeStamp = collision.bc_as().timestamp(); + auto grouped = selectedD0Candidates->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + processImpl(collision, grouped, timeStamp); } + PROCESS_SWITCH(HfCorrelatorDMesonPairs, processData, "Process data mode", true); - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcRec, "Process Mc reco mode", false); + /// D0(bar)-D0(bar) correlation pair builder - MC reco level (candidates matched to MC truth) + void processMcRec(SelCollisionsWithD0::iterator const& collision, + CandidatesD0McRec const&, aod::Tracks const&, aod::BCsWithTimestamps const&) + { + auto const timeStamp = collision.bc_as().timestamp(); + auto grouped = selectedD0CandidatesMc->sliceByCached(aod::hf_cand::collisionId, collision.globalIndex(), cache); + processImpl(collision, grouped, timeStamp); + } + PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcRec, "Process MC reco mode", false); + /// D0(bar)-D0(bar) correlation pair builder - MC generator level (particles flagged as D0 -> pi K) void processMcGen(aod::McCollision const&, soa::SmallGroups> const& collisions, McParticlesPlus2Prong const& mcParticles) { + // Take the associated reconstructed collision with the highest number of PV contributors int numPvContributorsGen{0}; - for (const auto& collision : collisions) { // loop over reco collisions associated to this gen collision + for (const auto& collision : collisions) { int const numPvContributors = collision.numContrib(); - - if (numPvContributors > numPvContributorsGen) { // we take the associated reconstructed collision with higher number of PV contributors + if (numPvContributors > numPvContributorsGen) { numPvContributorsGen = numPvContributors; } } - // Get counters per event - int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; + + // Select the D0(bar) -> K pi particles + std::vector selectedParticles; for (const auto& particle : mcParticles) { - // check if the particle is D0 or D0bar - if (std::abs(particle.pdgCode()) != Pdg::kD0) { + if (std::abs(particle.flagMcMatchGen()) != DecayChannel::D0ToPiK) { continue; } - if (std::abs(particle.y()) > yCandMax) { + registry.fill(HIST("hPtCandMcGen"), particle.pt()); + if (!passGenSelections(particle)) { continue; } - if (ptCandMin >= 0. && particle.pt() < ptCandMin) { - continue; - } - auto particleType = assignParticleTypeD0Gen(particle); // Candidate type attribution + selectedParticles.push_back(particle); + } + + // Single-particle histograms and per-event counters + int nDevent = 0, nDbarevent = 0, nDDbarevent = 0, nDorDbarevent = 0; + for (const auto& particle : selectedParticles) { + auto const particleType = assignParticleTypeD0Gen(particle); bool const isDParticle = isTrueD(particleType); bool const isDbarParticle = isTrueDbar(particleType); - if (isDParticle) { - nDevent++; - } - if (isDbarParticle) { - nDbarevent++; - } - if (isDParticle && isDbarParticle) { - nDDbarevent++; + nDevent += isDParticle; + nDbarevent += isDbarParticle; + nDDbarevent += (isDParticle && isDbarParticle); + nDorDbarevent += (isDParticle || isDbarParticle); + + registry.fill(HIST("hEtaMcGen"), particle.eta()); + registry.fill(HIST("hPhiMcGen"), particle.phi()); + registry.fill(HIST("hPtCandAfterCutMcGen"), particle.pt()); + + registry.fill(HIST("hStatusSinglePartMcGen"), 1); + if (isDParticle && !isDbarParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 2); + registry.fill(HIST("hStatusSinglePartMcGen"), 5); + } else if (isDbarParticle && !isDParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 3); + registry.fill(HIST("hStatusSinglePartMcGen"), 6); + } else if (isDParticle && isDbarParticle) { + registry.fill(HIST("hStatusSinglePartMcGen"), 4); } - if (isDParticle || isDbarParticle) { - nDorDbarevent++; + + registry.fill(HIST("hPtVsYVsNContribMcGen"), particle.pt(), particle.y(), numPvContributorsGen); + if (particle.originMcGen() == RecoDecay::Prompt) { + registry.fill(HIST("hPtVsYVsNContribMcGenPrompt"), particle.pt(), particle.y(), numPvContributorsGen); + } else if (particle.originMcGen() == RecoDecay::NonPrompt) { + registry.fill(HIST("hPtVsYVsNContribMcGenNonPrompt"), particle.pt(), particle.y(), numPvContributorsGen); } + registry.fill(HIST("hNContribMcGen"), numPvContributorsGen); } if (nDevent > 0) { registry.fill(HIST("hInputCheckD0McGen"), nDevent); @@ -984,142 +1008,68 @@ struct HfCorrelatorDMesonPairs { registry.fill(HIST("hInputCheckD0OrD0barMcGen"), nDorDbarevent); } - for (const auto& particle1 : mcParticles) { - // check if the particle is D0 or D0bar - if (std::abs(particle1.pdgCode()) != Pdg::kD0) { - continue; - } - registry.fill(HIST("hPtCandMcGen"), particle1.pt()); + // Pairs of selected particles + for (size_t i = 0; i < selectedParticles.size(); ++i) { + for (size_t j = i + 1; j < selectedParticles.size(); ++j) { + auto const& particle1 = selectedParticles[i]; + auto const& particle2 = selectedParticles[j]; - if (std::abs(particle1.y()) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && particle1.pt() < ptCandMin) { - continue; - } - registry.fill(HIST("hEtaMcGen"), particle1.eta()); - registry.fill(HIST("hPhiMcGen"), particle1.phi()); - registry.fill(HIST("hPtCandAfterCutMcGen"), particle1.pt()); - - auto particleType1 = assignParticleTypeD0Gen(particle1); // Candidate sign attribution - bool const isDParticle1 = isTrueD(particleType1); - bool const isDbarParticle1 = isTrueDbar(particleType1); - - // check if it's MC matched - int8_t matchedGen1 = particle1.flagMcMatchGen(); - // check origin - int8_t originGen1 = particle1.originMcGen(); - - // Fill selection status single particle - registry.fill(HIST("hStatusSinglePartMcGen"), 1); - if (isDParticle1 && !isDbarParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 2); - registry.fill(HIST("hStatusSinglePartMcGen"), 5); - } else if (isDbarParticle1 && !isDParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 3); - registry.fill(HIST("hStatusSinglePartMcGen"), 6); - } else if (isDParticle1 && isDbarParticle1) { - registry.fill(HIST("hStatusSinglePartMcGen"), 4); - } + auto const type1 = assignParticleTypeD0Gen(particle1); + auto const type2 = assignParticleTypeD0Gen(particle2); - registry.fill(HIST("hPtVsYVsNContribMcGen"), particle1.pt(), particle1.y(), numPvContributorsGen); - if (originGen1 == RecoDecay::Prompt) { - registry.fill(HIST("hPtVsYVsNContribMcGenPrompt"), particle1.pt(), particle1.y(), numPvContributorsGen); - } - if (originGen1 == RecoDecay::NonPrompt) { - registry.fill(HIST("hPtVsYVsNContribMcGenNonPrompt"), particle1.pt(), particle1.y(), numPvContributorsGen); - } - registry.fill(HIST("hNContribMcGen"), numPvContributorsGen); + // Canonical ordering (same as reco): D0 first, higher pT first for equal flavours + bool const swapOrder = shouldSwapOrder(isTrueD(type1), isTrueDbar(type1), isTrueD(type2), isTrueDbar(type2), particle1, particle2); + auto const& first = swapOrder ? particle2 : particle1; + auto const& second = swapOrder ? particle1 : particle2; + auto const typeFirst = swapOrder ? type2 : type1; + auto const typeSecond = swapOrder ? type1 : type2; + bool const isDFirst = isTrueD(typeFirst), isDbarFirst = isTrueDbar(typeFirst); + bool const isDSecond = isTrueD(typeSecond), isDbarSecond = isTrueDbar(typeSecond); - for (auto particle2 = particle1 + 1; particle2 != mcParticles.end(); ++particle2) { - // check if the particle is D0 or D0bar - if (std::abs(particle2.pdgCode()) != Pdg::kD0) { - continue; - } - if (std::abs(particle2.y()) > yCandMax) { - continue; - } - if (ptCandMin >= 0. && particle2.pt() < ptCandMin) { - continue; - } - // Candidate sign attribution. - auto particleType2 = assignParticleTypeD0Gen(particle2); - bool const isDParticle2 = isTrueD(particleType2); - bool const isDbarParticle2 = isTrueDbar(particleType2); - - // check if it's MC matched - int8_t matchedGen2 = particle2.flagMcMatchGen(); - // check origin - int8_t originGen2 = particle2.originMcGen(); - - // Fill hMatchingMcGen - Cand 1 - registry.fill(HIST("hMatchingMcGen"), 1); - if (matchedGen1 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 2); - } else if (matchedGen1 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 3); - } else if (matchedGen1 == 0) { - registry.fill(HIST("hMatchingMcGen"), 4); - } - // Fill hMatchingMcRec - Cand 2 - registry.fill(HIST("hMatchingMcGen"), 5); - if (matchedGen2 == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 6); - } else if (matchedGen2 == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - registry.fill(HIST("hMatchingMcGen"), 7); - } else if (matchedGen2 == 0) { - registry.fill(HIST("hMatchingMcGen"), 8); - } + fillMatchingHisto(first.flagMcMatchGen(), second.flagMcMatchGen()); - // Fill particle1's Selection Status + // Selection status registry.fill(HIST("hSelectionStatusMcGen"), 2); - if (isDParticle1 && !isDbarParticle1) { + if (isDFirst && !isDbarFirst) { registry.fill(HIST("hSelectionStatusMcGen"), 6); - } else if (isDbarParticle1 && !isDParticle1) { + } else if (isDbarFirst && !isDFirst) { registry.fill(HIST("hSelectionStatusMcGen"), 7); } - - // Fill particle2's Selection Status registry.fill(HIST("hSelectionStatusMcGen"), 8); - if (isDParticle2 && !isDbarParticle2) { + if (isDSecond && !isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 12); - } else if (isDbarParticle2 && !isDParticle2) { + } else if (isDbarSecond && !isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 13); } - // Fill pair Selection Status - uint8_t pairType(0); registry.fill(HIST("hSelectionStatusMcGen"), 1); - - if (isDParticle1 && isDParticle2) { - SETBIT(pairType, DD); + if (isDFirst && isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 22); } - if (isDbarParticle1 && isDbarParticle2) { - SETBIT(pairType, DbarDbar); + if (isDbarFirst && isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 23); } - if (isDParticle1 && isDbarParticle2) { - SETBIT(pairType, DDbar); + if (isDFirst && isDbarSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 24); } - if (isDbarParticle1 && isDParticle2) { - SETBIT(pairType, DbarD); + if (isDbarFirst && isDSecond) { registry.fill(HIST("hSelectionStatusMcGen"), 25); } - // Fill pair Selection Status - entryD0PairMcGen(particle1.pt(), particle2.pt(), particle1.y(), particle2.y(), particle1.phi(), particle2.phi(), MassD0, MassD0Bar, MassD0, MassD0Bar, pairType, particleType1, particleType2); - entryD0PairMcGenInfo(originGen1, originGen2, matchedGen1, matchedGen2); - - } // end inner loop - } // end outer loop + uint8_t const pairType = setPairType(typeFirst, typeSecond); + entryD0PairMcGen(first.pt(), second.pt(), first.y(), second.y(), first.eta(), second.eta(), first.phi(), second.phi(), + MassD0, MassD0Bar, MassD0, MassD0Bar, pairType, typeFirst, typeSecond); + entryD0PairMcGenInfo(first.originMcGen(), second.originMcGen(), first.flagMcMatchGen(), second.flagMcMatchGen()); + } + } } - PROCESS_SWITCH(HfCorrelatorDMesonPairs, processMcGen, "Process D0 Mc Gen mode", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + }; } diff --git a/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx b/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx index 14eed44af7f..897d192eaa4 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusDminus.cxx @@ -139,7 +139,7 @@ struct HfCorrelatorDplusDminus { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "Dplus,Dminus candidates;inv. mass (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassDplus", "Dplus,Dminus candidates;inv. mass Dplus only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hMassDminus", "Dplus,Dminus candidates;inv. mass Dminus only (#pi K) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{massAxisBins, massAxisMin, massAxisMax}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx index d58da7fccd6..d3530cfff96 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusDplusReduced.cxx @@ -20,6 +20,8 @@ #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" +#include "PWGHF/Core/HfMlResponseDplusToPiKPi.h" +#include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/HFC/DataModel/ReducedDMesonPairsTables.h" @@ -30,10 +32,12 @@ #include "Common/DataModel/Centrality.h" #include +#include #include #include #include #include +#include #include #include #include @@ -41,9 +45,12 @@ #include #include +#include +#include #include using namespace o2; +using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::hf_centrality; @@ -51,7 +58,7 @@ using namespace o2::hf_centrality; /// Writes the full information in an output TTree struct HfCorrelatorDplusDplusReduced { Produces rowCandidateFull; - Produces rowCandidateLite; + Produces rowCandidateLite; Produces rowCandidateTiny; Produces rowCandidateFullEvents; Produces rowCandidateMl; @@ -69,7 +76,33 @@ struct HfCorrelatorDplusDplusReduced { Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", true, "Enables processing of skimmed datasets"}; Configurable skipSingleD{"skipSingleD", true, "Skip collisions with one or less D candidates"}; - HfHelper hfHelper; + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable applySkimming{"applySkimming", false, "Flag to apply Skimming selections"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTDPlus"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_DPlusToKPiPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + + Configurable> cutPtSkimming{"cutPtSkimming", {1, 5, 1000}, "pT bin limits for Skimming application"}; + Configurable> massMin{"massMin", {1.7, 1.7}, "Mass minimal for the cut for each pt bin"}; + Configurable> massMax{"massMax", {2.0, 2.1}, "Mass maximal for the cut for each pt bin"}; + Configurable> cosThetaMin{"cosThetaMin", {0.96, 0.98}, "CosTheta minimal for the cut for each pt bin"}; + Configurable> decayLengthMin{"decayLengthMin", {0.02, 0.03}, "DecayLength minimal for the cut for each pt bin"}; + Configurable> nSigmaTpcMax{"nSigmaTpcMax", {3, 3}, "NsigmaTPC maximal for the cut for each pt bin"}; + Configurable> nSigmaTofMax{"nSigmaTofMax", {3, 3}, "NsigmaTOF maximal for the cut for each pt bin"}; + + Configurable> binsPtSkimming{"binsPtSkimming", {0}, "pT bin limits for Skimming application"}; + + HfMlResponseDplusToPiKPi hfMlResponse; + + std::vector outputMl; + o2::ccdb::CcdbApi ccdbApi; Service ccdb; @@ -102,6 +135,39 @@ struct HfCorrelatorDplusDplusReduced { if (cfgSkimmedProcessing) { zorroSummary.setObject(zorro.getZorroSummary()); } + + if (applyMl) { + hfMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponse.setModelPathsLocal(onnxFileNames); + } + hfMlResponse.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponse.init(); + } + } + + bool skimming(auto const& candidate) + { + if (candidate.pt() < cutPtSkimming.value.at(0) || candidate.pt() > cutPtSkimming.value.at(cutPtSkimming.value.size() - 1)) { + return false; + } + for (size_t i = 0; i < cutPtSkimming.value.size() - 1; i++) { + if (candidate.pt() <= cutPtSkimming.value.at(i + 1)) { + if (HfHelper::invMassDplusToPiKPi(candidate) < massMin.value.at(i) || + HfHelper::invMassDplusToPiKPi(candidate) > massMax.value.at(i) || + candidate.cpa() < cosThetaMin.value.at(i) || + candidate.decayLength() < decayLengthMin.value.at(i) || + std::abs(candidate.nSigTofKa1()) > nSigmaTofMax.value.at(i) || + std::abs(candidate.nSigTpcKa1()) > nSigmaTpcMax.value.at(i)) { + return false; + } + return true; + } + } + return false; } template @@ -127,14 +193,14 @@ struct HfCorrelatorDplusDplusReduced { channelMc = candidate.flagMcDecayChanRec(); } - std::vector outputMl = {-999., -999.}; + std::vector mlProb = {-999., -999.}; if constexpr (DoMl) { for (unsigned int iclass = 0; iclass < classMlIndexes->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; + mlProb[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; } rowCandidateMl( - outputMl[0], - outputMl[1]); + mlProb[0], + mlProb[1]); } float cent{-1.}; @@ -146,7 +212,7 @@ struct HfCorrelatorDplusDplusReduced { if (fillCandidateTinyTable) { rowCandidateTiny( candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.eta(), candidate.phi(), @@ -189,14 +255,14 @@ struct HfCorrelatorDplusDplusReduced { candidate.tpcTofNSigmaPi2(), candidate.tpcTofNSigmaKa2(), candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.cpa(), candidate.cpaXY(), candidate.maxNormalisedDeltaIP(), candidate.eta(), candidate.phi(), - hfHelper.yDplus(candidate), + HfHelper::yDplus(candidate), cent, localEvIdx, flagMc, @@ -264,17 +330,17 @@ struct HfCorrelatorDplusDplusReduced { candidate.tpcTofNSigmaPi2(), candidate.tpcTofNSigmaKa2(), candidate.isSelDplusToPiKPi(), - hfHelper.invMassDplusToPiKPi(candidate), + HfHelper::invMassDplusToPiKPi(candidate), sign * candidate.pt(), candidate.p(), candidate.cpa(), candidate.cpaXY(), candidate.maxNormalisedDeltaIP(), - hfHelper.ctDplus(candidate), + HfHelper::ctDplus(candidate), candidate.eta(), candidate.phi(), - hfHelper.yDplus(candidate), - hfHelper.eDplus(candidate), + HfHelper::yDplus(candidate), + HfHelper::eDplus(candidate), cent, localEvIdx, flagMc, @@ -291,7 +357,9 @@ struct HfCorrelatorDplusDplusReduced { static int lastRunNumber = -1; // reserve memory rowCandidateFullEvents.reserve(collisions.size()); - if (fillCandidateLiteTable) { + if (fillCandidateTinyTable) { + rowCandidateTiny.reserve(candidates.size()); + } else if (fillCandidateLiteTable) { rowCandidateLite.reserve(candidates.size()); } else { rowCandidateFull.reserve(candidates.size()); @@ -313,13 +381,31 @@ struct HfCorrelatorDplusDplusReduced { const auto colId = collision.globalIndex(); auto candidatesInThisCollision = candidates.sliceBy(tracksPerCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (candidatesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } fillEvent(collision); for (const auto& candidate : candidatesInThisCollision) { auto prongCandidate = candidate.prong1_as(); auto candidateSign = -prongCandidate.sign(); + + if (applySkimming && + !skimming(candidate)) { + continue; + } + + if (applyMl) { + std::vector inputFeatures = hfMlResponse.getInputFeatures(candidate); + bool const isSelectedMl = hfMlResponse.isSelectedMl(inputFeatures, + std::abs(candidate.pt()), + outputMl); + if (!isSelectedMl) { + continue; + } + } fillCandidateTable(candidate, rowCandidateFullEvents.lastIndex(), candidateSign); } } @@ -332,7 +418,9 @@ struct HfCorrelatorDplusDplusReduced { { // reserve memory rowCandidateFullEvents.reserve(collisions.size()); - if (fillCandidateLiteTable) { + if (fillCandidateTinyTable) { + rowCandidateTiny.reserve(candidates.size()); + } else if (fillCandidateLiteTable) { rowCandidateLite.reserve(candidates.size()); } else { rowCandidateFull.reserve(candidates.size()); @@ -341,13 +429,30 @@ struct HfCorrelatorDplusDplusReduced { for (const auto& collision : collisions) { // No skimming for MC data. No Zorro ! const auto colId = collision.globalIndex(); auto candidatesInThisCollision = candidates.sliceBy(tracksPerCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (candidatesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } fillEvent(collision); for (const auto& candidate : candidatesInThisCollision) { auto prongCandidate = candidate.prong1_as(); auto candidateSign = -prongCandidate.sign(); + + if (applySkimming && + !skimming(candidate)) { + continue; + } + if (applyMl) { + std::vector inputFeatures = hfMlResponse.getInputFeatures(candidate); + bool const isSelectedMl = hfMlResponse.isSelectedMl(inputFeatures, + std::abs(candidate.pt()), + outputMl); + if (!isSelectedMl) { + continue; + } + } fillCandidateTable(candidate, rowCandidateFullEvents.lastIndex(), candidateSign); } } @@ -364,9 +469,12 @@ struct HfCorrelatorDplusDplusReduced { for (const auto& mccollision : mccollisions) { // No skimming for MC data. No Zorro ! const auto colId = mccollision.globalIndex(); const auto particlesInThisCollision = mcparticles.sliceBy(mcParticlesPerMcCollision, colId); - if (skipSingleD) + if (skipSingleD) { if (particlesInThisCollision.size() < 2) // o2-linter: disable=magic-number (number of candidate must be larger than 1) + { continue; + } + } rowCandidateMcCollisions( mccollision.posX(), mccollision.posY(), diff --git a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx index 3f7f48f84d3..6f7c3f3c6a1 100644 --- a/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDplusHadrons.cxx @@ -21,6 +21,8 @@ #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/HFC/DataModel/CorrelationTables.h" +#include "PWGHF/HFC/DataModel/DerivedDataCorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsCorrelations.h" #include "PWGHF/Utils/utilsAnalysis.h" #include "Common/CCDB/EventSelectionParams.h" @@ -98,13 +100,12 @@ struct HfCorrelatorDplusHadronsDplusSelection { CandidatesDplusData const& candidates) { bool isSelColl = true; - bool isDplusFound = true; + bool isDplusFound = !doSelDplusCollision; bool isSel8 = true; bool isNosameBunchPileUp = true; if (doSelDplusCollision) { for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDplus(candidate)) > yCandMax || candidate.pt() < ptCandMin) { - isDplusFound = false; continue; } isDplusFound = true; @@ -126,7 +127,7 @@ struct HfCorrelatorDplusHadronsDplusSelection { CandidatesDplusMcRec const& candidates) { bool isSelColl = true; - bool isDplusFound = false; + bool isDplusFound = !doSelDplusCollision; bool isSel8 = true; bool isNosameBunchPileUp = true; if (doSelDplusCollision) { @@ -181,8 +182,17 @@ struct HfCorrelatorDplusHadrons { Produces entryTrackRecoInfo; Produces entryDplus; Produces entryHadron; + Produces collReduced; + Produces candReduced; + Produces candSelInfo; + Produces assocTrackReduced; + Produces assocTrackSelInfo; + Produces entryDplusHadronBMotherInfo; + Produces entryDplusCandBMotherInfo; + static constexpr std::size_t NDaughters{3u}; - static constexpr float EtaDaughtersMax = 0.8f; // Eta cut on daughters of D+ meson as Run2 + static constexpr float EtaDaughtersMax = 0.8f; // Eta cut on daughters of D+ meson as Run2 + static constexpr int8_t DepthMaxDplusDaughters{2}; // max. depth of the D+ daughters in the decay chain (resonant channels) Configurable selectionFlagDplus{"selectionFlagDplus", 7, "Selection Flag for Dplus"}; // 7 corresponds to topo+PID cuts Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; @@ -231,10 +241,13 @@ struct HfCorrelatorDplusHadrons { // filter on selection of Dplus meson and decay channel Dplus->KPiPi Filter dplusFilter = ((o2::aod::hf_track_index::hfflag & static_cast(1 << aod::hf_cand_3prong::DecayType::DplusToPiKPi)) != static_cast(0)) && aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlagDplus; Filter trackFilter = (nabs(aod::track::eta) < etaTrackMax) && (nabs(aod::track::pt) > ptTrackMin) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); + // Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == 411 || ((aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary); Preslice presliceMc{aod::mcparticle::mcCollisionId}; Preslice candMcGenPerMcCollision = o2::aod::mcparticle::mcCollisionId; + Preslice candsDplusPerCollision = aod::hf_cand::collisionId; + Preslice trackIndicesPerCollision = aod::track::collisionId; PresliceUnsorted> recoCollisionsPerMcCollision = o2::aod::mccollisionlabel::mcCollisionId; - // Filter particlesFilter = nabs(aod::mcparticle::pdgCode) == 411 || ((aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary); + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; ConfigurableAxis binsMultiplicityMc{"binsMultiplicityMc", {VARIABLE_WIDTH, 0.0f, 20.0f, 50.0f, 500.0f}, "Mixing bins - MC multiplicity"}; // In MCGen multiplicity is defined by counting tracks @@ -244,7 +257,8 @@ struct HfCorrelatorDplusHadrons { ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 6000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsMassD{"binsMassD", {200, 1.7, 2.10}, "inv. mass (#pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + ConfigurableAxis binsDcaXY{"binsDcaXY", {128, -0.2, 0.2}, "DCA xy"}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext&) @@ -252,8 +266,8 @@ struct HfCorrelatorDplusHadrons { AxisSpec axisMassD = {binsMassD, "inv. mass (pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsZVtx, "PosZ"}; @@ -261,6 +275,7 @@ struct HfCorrelatorDplusHadrons { AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; AxisSpec const axisStatus = {15, 0.5, 15.5, "Selection status"}; AxisSpec const axisRapidity = {100, -2, 2, "Rapidity"}; + AxisSpec const axisDcaXY = {binsDcaXY, "DCA xy"}; registry.add("hPtCand", "Dplus,Hadron candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtD}}); registry.add("hPtProng0", "Dplus,Hadron candidates;prong 0 #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtD}}); @@ -281,6 +296,7 @@ struct HfCorrelatorDplusHadrons { registry.add("hMassDplusData", "Dplus candidates;inv. mass (K^{-}#pi^{+}#pi^{+}) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{axisMassD}}}); registry.add("hDplusPoolBin", "D+ candidates pool bin", {HistType::kTH1F, {axisPoolBin}}); registry.add("hTracksPoolBin", "Particles associated pool bin", {HistType::kTH1F, {axisPoolBin}}); + registry.add("hDcaXYVsPt", "DCA xy vs pt", {HistType::kTH2F, {{axisDcaXY}, {axisPtHadron}}}); // Histograms for MC Reco analysis registry.add("hSelectionStatusMCRec", "Dplus,Hadron candidates - MC reco;selection status;entries", {HistType::kTH1F, {{4, -0.5, 3.5}}}); registry.add("hMCEvtCount", "Event counter - MC gen;;entries", {HistType::kTH1F, {{1, -0.5, 0.5}}}); @@ -317,7 +333,7 @@ struct HfCorrelatorDplusHadrons { registry.add("hPhiMcGen", "D+,Hadron particles - MC Gen", {HistType::kTH1F, {axisPhi}}); registry.add("hMultFT0AMcGen", "D+,Hadron multiplicity FT0A - MC Gen", {HistType::kTH1F, {axisMultiplicity}}); registry.add("hFakeCollision", "Fake collision counter", {HistType::kTH1F, {{1, -0.5, 0.5, "n fake coll"}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template @@ -389,6 +405,9 @@ struct HfCorrelatorDplusHadrons { // Count triggers registry.fill(HIST("hcountDplustriggersMCGen"), 0, particle.pt()); + // beauty-mother information + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcGen(particle, particlesToLoop); + for (const auto& particleAssoc : groupedMcParticles) { if (std::abs(particleAssoc.eta()) > etaTrackMax || @@ -435,6 +454,7 @@ struct HfCorrelatorDplusHadrons { entryDplusHadronGenInfo(isDplusPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); } } } @@ -528,6 +548,47 @@ struct HfCorrelatorDplusHadrons { } PROCESS_SWITCH(HfCorrelatorDplusHadrons, processData, "Process data", false); + void processDerivedDataDplus(SelCollisionsWithDplus const& collisions, + CandidatesDplusData const& candidates, + TracksData const& tracks) + { + + for (const auto& collision : collisions) { + auto thisCollId = collision.globalIndex(); + auto candsDplusThisColl = candidates.sliceBy(candsDplusPerCollision, thisCollId); + auto tracksThisColl = tracks.sliceBy(trackIndicesPerCollision, thisCollId); + + int indexHfcReducedCollision = collReduced.lastIndex() + 1; + + // Dplus candidates information stored + for (const auto& candidate : candsDplusThisColl) { + std::vector outputMl = {-1., -1., -1.}; + // candidate selected + if (candidate.isSelDplusToPiKPi() >= selectionFlagDplus) { + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; + } + candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt(), HfHelper::invMassDplusToPiKPi(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); + // Check compatibility with the candidate selector options: BdtScoreBkg, BdtScorePrompt, BdtScoreNonPrompt + candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[1], outputMl[2]); + } + } + + // tracks information + for (const auto& track : tracksThisColl) { + if (!track.isGlobalTrackWoDCA()) { + continue; + } + registry.fill(HIST("hDcaXYVsPt"), track.dcaXY(), track.pt()); + assocTrackReduced(indexHfcReducedCollision, track.globalIndex(), track.phi(), track.eta(), track.pt()); + assocTrackSelInfo(indexHfcReducedCollision, track.tpcNClsCrossedRows(), track.itsClusterMap(), track.itsNCls(), track.dcaXY(), track.dcaZ()); + } + + collReduced(collision.multFT0M(), 0.f, collision.numContrib(), collision.posZ()); + } + } + PROCESS_SWITCH(HfCorrelatorDplusHadrons, processDerivedDataDplus, "Process derived data D+", false); + /// Dplus-Hadron correlation pair builder - for MC reco-level analysis (candidates matched to true signal only, but also the various bkg sources are studied) void processMcRec(SelCollisionsWithDplus::iterator const& collision, TracksWithMc const& tracks, @@ -579,6 +640,8 @@ struct HfCorrelatorDplusHadrons { bool isDplusNonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; std::vector outputMl = {-1., -1., -1.}; + // beauty-mother information (generated B, reconstructed D+) + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcRec(candidate, mcParticles, Pdg::kDPlus, DepthMaxDplusDaughters); // fill invariant mass plots from Dplus signal and background candidates registry.fill(HIST("hMassDplusMcRec"), HfHelper::invMassDplusToPiKPi(candidate), efficiencyWeightD); @@ -611,6 +674,7 @@ struct HfCorrelatorDplusHadrons { registry.fill(HIST("hMassDplusMcRecSig"), HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), efficiencyWeightD); entryDplusCandRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), candidate.pt(), outputMl[0], outputMl[1], outputMl[2]); entryDplusCandGenInfo(isDplusPrompt); + entryDplusCandBMotherInfo(ptB, deltaPhiBtoD); } else { registry.fill(HIST("hPtCandMcRecBkg"), candidate.pt()); registry.fill(HIST("hEtaMcRecBkg"), candidate.eta()); @@ -639,6 +703,7 @@ struct HfCorrelatorDplusHadrons { track.pt(), poolBin); entryDplusHadronRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), isDplusSignal); entryDplusHadronMlInfo(outputMl[0], outputMl[1], outputMl[2]); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); if (track.has_mcParticle()) { auto mcParticle = track.template mcParticle_as(); isPhysicalPrimary = mcParticle.isPhysicalPrimary(); @@ -668,7 +733,7 @@ struct HfCorrelatorDplusHadrons { soa::Join const& collisions, CandDplusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; for (const auto& mcCollision : mcCollisions) { @@ -753,7 +818,7 @@ struct HfCorrelatorDplusHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{binsZVtx, binsMultiplicityMc}, true}; + BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDplus(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -778,11 +843,11 @@ struct HfCorrelatorDplusHadrons { } } auto tracksTuple = std::make_tuple(candidates, tracks); - Pair const pairMcRec{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair const pairMcRec{corrBinningMcRec, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcRec) { - int poolBin = corrBinning.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); - int const poolBinDplus = corrBinning.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); + int poolBin = corrBinningMcRec.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); + int const poolBinDplus = corrBinningMcRec.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); registry.fill(HIST("hMultFT0M"), c1.multFT0M()); registry.fill(HIST("hZVtx"), c1.posZ()); registry.fill(HIST("hTracksPoolBin"), poolBin); // note that the selections here are not yet applied @@ -814,6 +879,8 @@ struct HfCorrelatorDplusHadrons { poolBin); entryDplusHadronRecoInfo(HfHelper::invMassDplusToPiKPi(candidate), isDplusSignal); entryDplusHadronGenInfo(isDplusPrompt, isPhysicalPrimary, trackOrigin); + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcRec(candidate, mcParticles, Pdg::kDPlus, DepthMaxDplusDaughters); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMl->at(iclass)]; } @@ -827,7 +894,7 @@ struct HfCorrelatorDplusHadrons { void processMcGenMixedEvent(SelCollisionsWithDplusMc const& collisions, CandDplusMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { @@ -858,6 +925,8 @@ struct HfCorrelatorDplusHadrons { poolBin); entryDplusHadronRecoInfo(MassDPlus, true); entryDplusHadronGenInfo(isDplusPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); + const auto [ptB, deltaPhiBtoD] = hf_correlations::getBMotherInfoMcGen(candidate, mcParticles); + entryDplusHadronBMotherInfo(ptB, deltaPhiBtoD); } } } diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx index e2b85b70fc0..7404d0f6a61 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadrons.cxx @@ -28,6 +28,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -173,8 +174,8 @@ struct HfCorrelatorDsHadrons { Produces entryDsCandGenInfo; Produces entryTrackRecoInfo; Produces collReduced; - Produces candReduced; - Produces candSelInfo; + Produces candReduced; + Produces candSelInfo; Produces assocTrackReduced; Produces assocTrackSelInfo; Produces assocTrackPidInfo; @@ -216,7 +217,7 @@ struct HfCorrelatorDsHadrons { SliceCache cache; - using SelCollisionsWithDs = soa::Filtered>; // collisionFilter applied + using SelCollisionsWithDs = soa::Filtered>; // collisionFilter applied // using SelCollisionsWithDsWithMc = soa::Filtered>; // collisionFilter applied using SelCollisionsMc = soa::Join; using CandDsData = soa::Filtered>; // flagDsFilter applied @@ -254,8 +255,8 @@ struct HfCorrelatorDsHadrons { AxisSpec axisMassD = {binsMassD, "inv. mass (K^{#pm}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec const axisPosZ = {binsPosZ, "PosZ"}; @@ -442,7 +443,7 @@ struct HfCorrelatorDsHadrons { CandDsData const& candidates, MyTracksData const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; registry.fill(HIST("hZVtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); @@ -468,13 +469,13 @@ struct HfCorrelatorDsHadrons { for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; } - entryDsCandRecoInfo(HfHelper::invMassDsToKKPi(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib()); + entryDsCandRecoInfo(HfHelper::invMassDsToKKPi(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib(), collision.centFT0M()); } else if (candidate.isSelDsToPiKK() >= selectionFlagDs) { fillHistoPiKK(candidate, efficiencyWeightD); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; } - entryDsCandRecoInfo(HfHelper::invMassDsToPiKK(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib()); + entryDsCandRecoInfo(HfHelper::invMassDsToPiKK(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib(), collision.centFT0M()); } if (candidate.isSelDsToKKPi() >= selectionFlagDs && candidate.isSelDsToPiKK() >= selectionFlagDs) { registry.fill(HIST("hCountSelectionStatusDsToKKPiAndToPiKK"), 0.); @@ -498,7 +499,8 @@ struct HfCorrelatorDsHadrons { candidate.pt() * chargeDs, track.pt() * track.sign(), poolBin, - collision.numContrib()); + collision.numContrib(), + collision.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToKKPi(candidate), false, false); // entryDsHadronGenInfo(false, false, 0); entryDsHadronMlInfo(outputMl[0], outputMl[2]); @@ -509,7 +511,8 @@ struct HfCorrelatorDsHadrons { candidate.pt() * chargeDs, track.pt() * track.sign(), poolBin, - collision.numContrib()); + collision.numContrib(), + collision.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToPiKK(candidate), false, false); // entryDsHadronGenInfo(false, false, 0); entryDsHadronMlInfo(outputMl[0], outputMl[2]); @@ -526,7 +529,7 @@ struct HfCorrelatorDsHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; registry.fill(HIST("hZVtx"), collision.posZ()); registry.fill(HIST("hMultFT0M"), collision.multFT0M()); int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); @@ -569,7 +572,7 @@ struct HfCorrelatorDsHadrons { registry.fill(HIST("hMassDsMcRecSig"), HfHelper::invMassDsToKKPi(candidate), candidate.pt(), efficiencyWeightD); registry.fill(HIST("hMassDsVsPtMcRec"), HfHelper::invMassDsToKKPi(candidate), candidate.pt(), efficiencyWeightD); registry.fill(HIST("hSelectionStatusDsToKKPiMcRec"), candidate.isSelDsToKKPi()); - entryDsCandRecoInfo(HfHelper::invMassDsToKKPi(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib()); + entryDsCandRecoInfo(HfHelper::invMassDsToKKPi(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib(), collision.centFT0M()); entryDsCandGenInfo(isDsPrompt); } else if (candidate.isSelDsToPiKK() >= selectionFlagDs) { for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { @@ -579,7 +582,7 @@ struct HfCorrelatorDsHadrons { registry.fill(HIST("hMassDsMcRecSig"), HfHelper::invMassDsToPiKK(candidate), candidate.pt(), efficiencyWeightD); registry.fill(HIST("hMassDsVsPtMcRec"), HfHelper::invMassDsToPiKK(candidate), candidate.pt(), efficiencyWeightD); registry.fill(HIST("hSelectionStatusDsToPiKKMcRec"), candidate.isSelDsToPiKK()); - entryDsCandRecoInfo(HfHelper::invMassDsToPiKK(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib()); + entryDsCandRecoInfo(HfHelper::invMassDsToPiKK(candidate), candidate.pt() * chargeDs, outputMl[0], outputMl[2], collision.numContrib(), collision.centFT0M()); entryDsCandGenInfo(isDsPrompt); } } else { @@ -653,7 +656,8 @@ struct HfCorrelatorDsHadrons { candidate.pt() * chargeDs, track.pt() * track.sign(), poolBin, - collision.numContrib()); + collision.numContrib(), + collision.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToKKPi(candidate), isDsSignal, isDecayChan); entryDsHadronMlInfo(outputMl[0], outputMl[2]); isPhysicalPrimary = mcParticle.isPhysicalPrimary(); @@ -669,13 +673,13 @@ struct HfCorrelatorDsHadrons { entryTrackRecoInfo(track.dcaXY(), track.dcaZ(), track.tpcNClsCrossedRows()); } else if (isCorrectInvMassHypo && candidate.isSelDsToPiKK() >= selectionFlagDs) { // if (track.has_mcParticle()) { - auto mcParticle = track.template mcParticle_as(); entryDsHadronPair(getDeltaPhi(track.phi(), candidate.phi()), track.eta() - candidate.eta(), candidate.pt() * chargeDs, track.pt() * track.sign(), poolBin, - collision.numContrib()); + collision.numContrib(), + collision.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToPiKK(candidate), isDsSignal, isDecayChan); entryDsHadronMlInfo(outputMl[0], outputMl[2]); isPhysicalPrimary = mcParticle.isPhysicalPrimary(); @@ -701,7 +705,7 @@ struct HfCorrelatorDsHadrons { soa::Join const& collisions, CandDsMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}}; for (const auto& mcCollision : mcCollisions) { @@ -819,7 +823,8 @@ struct HfCorrelatorDsHadrons { particle.pt() * chargeDs, particleAssoc.pt() * chargeParticle, poolBin, - 0); + 0, + 0.f); // no multiplicity and centrality info at gen level entryDsHadronRecoInfo(MassDS, true, isDecayChan); entryDsHadronGenInfo(isDsPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); } // end loop generated particles @@ -853,13 +858,14 @@ struct HfCorrelatorDsHadrons { outputMl[iclass] = candidate.mlProbDsToKKPi()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt() * chargeDs, HfHelper::invMassDsToKKPi(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + // Check compatibility with the candidate selector options; candSelInfo considers BdtScoreBkg, BdtScorePrompt, BdtScoreNonPrompt + candSelInfo(indexHfcReducedCollision, outputMl[2], outputMl[0], outputMl[1]); // bkg, prompt, non-prompt (Ds convention: outputMl[0] = prompt, outputMl[2] = bkg) } else if (candidate.isSelDsToPiKK() >= selectionFlagDs) { for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { outputMl[iclass] = candidate.mlProbDsToPiKK()[classMl->at(iclass)]; } candReduced(indexHfcReducedCollision, candidate.phi(), candidate.eta(), candidate.pt() * chargeDs, HfHelper::invMassDsToPiKK(candidate), candidate.prong0Id(), candidate.prong1Id(), candidate.prong2Id()); - candSelInfo(indexHfcReducedCollision, outputMl[0], outputMl[2]); + candSelInfo(indexHfcReducedCollision, outputMl[2], outputMl[0], outputMl[1]); // bkg, prompt, non-prompt (Ds convention: outputMl[0] = prompt, outputMl[2] = bkg) } } @@ -902,7 +908,7 @@ struct HfCorrelatorDsHadrons { } } - collReduced(collision.multFT0M(), collision.numContrib(), collision.posZ()); + collReduced(collision.multFT0M(), collision.centFT0M(), collision.numContrib(), collision.posZ()); } } PROCESS_SWITCH(HfCorrelatorDsHadrons, processDerivedDataDs, "Process derived data Ds", false); @@ -912,7 +918,7 @@ struct HfCorrelatorDsHadrons { CandDsData const& candidates, MyTracksData const& tracks) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { registry.fill(HIST("hMultFT0M"), collision.multFT0M()); registry.fill(HIST("hZVtx"), collision.posZ()); @@ -952,7 +958,8 @@ struct HfCorrelatorDsHadrons { cand.pt() * chargeDs, pAssoc.pt() * pAssoc.sign(), poolBin, - c1.numContrib()); + c1.numContrib(), + c1.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToKKPi(cand), false, false); // entryDsHadronGenInfo(false, false, 0); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { @@ -967,7 +974,8 @@ struct HfCorrelatorDsHadrons { cand.pt() * chargeDs, pAssoc.pt() * pAssoc.sign(), poolBin, - c1.numContrib()); + c1.numContrib(), + c1.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToPiKK(cand), false, false); // entryDsHadronGenInfo(false, false, 0); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { @@ -986,7 +994,7 @@ struct HfCorrelatorDsHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yDs(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -1044,7 +1052,8 @@ struct HfCorrelatorDsHadrons { candidate.pt() * chargeDs, pAssoc.pt() * pAssoc.sign(), poolBin, - c1.numContrib()); + c1.numContrib(), + c1.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToKKPi(candidate), isDsSignal, isDecayChan); entryDsHadronGenInfo(isDsPrompt, isPhysicalPrimary, trackOrigin); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { @@ -1058,7 +1067,8 @@ struct HfCorrelatorDsHadrons { candidate.pt() * chargeDs, pAssoc.pt() * pAssoc.sign(), poolBin, - c1.numContrib()); + c1.numContrib(), + c1.centFT0M()); entryDsHadronRecoInfo(HfHelper::invMassDsToPiKK(candidate), isDsSignal, isDecayChan); entryDsHadronGenInfo(isDsPrompt, isPhysicalPrimary, trackOrigin); for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { @@ -1075,7 +1085,7 @@ struct HfCorrelatorDsHadrons { void processMcGenME(SelCollisionsMc const& collisions, CandDsMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { @@ -1128,7 +1138,8 @@ struct HfCorrelatorDsHadrons { candidate.pt() * chargeDs, particleAssoc.pt() * chargeParticle, poolBin, - 0); + 0, + 0.f); // no multiplicity and centrality info at gen level entryDsHadronRecoInfo(MassDS, true, true); entryDsHadronGenInfo(isDsPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); } diff --git a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx index f7aa4fbc31b..d72373f1b9b 100644 --- a/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDsHadronsReduced.cxx @@ -69,7 +69,7 @@ struct HfCorrelatorDsHadronsReduced { // Preslice tracksPerCol = aod::hf_assoc_track_reduced::hfcRedCollisionId; Preslice tracksPerCol = aod::hf_candidate_reduced::hfcRedCollisionId; - Preslice candPerCol = aod::hf_candidate_reduced::hfcRedCollisionId; + Preslice candPerCol = aod::hf_candidate_reduced::hfcRedCollisionId; ConfigurableAxis zPoolBins{"zPoolBins", {VARIABLE_WIDTH, -10.0, -2.5, 2.5, 10.0}, "z vertex position pools"}; ConfigurableAxis multPoolBins{"multPoolBins", {VARIABLE_WIDTH, 0., 900., 1800., 6000.}, "event multiplicity pools (FT0M)"}; @@ -88,8 +88,8 @@ struct HfCorrelatorDsHadronsReduced { AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; // Histograms for data analysis if (fillHistoData) { @@ -106,11 +106,11 @@ struct HfCorrelatorDsHadronsReduced { } void processDerivedData(aod::HfcRedCollisions const& collisions, - soa::Join const& candidates, + soa::Join const& candidates, soa::Join const& tracks) { - BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multiplicity())); @@ -126,7 +126,7 @@ struct HfCorrelatorDsHadronsReduced { registry.fill(HIST("hDsPoolBin"), poolBin); registry.fill(HIST("hPhiVsPtCand"), RecoDecay::constrainAngle(candidate.phiCand(), -PIHalf), candidate.ptCand()); registry.fill(HIST("hEtaVsPtCand"), candidate.etaCand(), candidate.ptCand()); - entryDsCandRecoInfo(candidate.invMassDs(), candidate.ptCand(), candidate.bdtScorePrompt(), candidate.bdtScoreBkg(), collision.numPvContrib()); + entryDsCandRecoInfo(candidate.invMass(), candidate.ptCand(), candidate.bdtScorePrompt(), candidate.bdtScoreBkg(), collision.numPvContrib(), collision.centrality()); for (const auto& track : tracksThisColl) { // Removing Ds daughters by checking track indices if ((candidate.prong0Id() == track.originTrackId()) || (candidate.prong1Id() == track.originTrackId()) || (candidate.prong2Id() == track.originTrackId())) { @@ -141,8 +141,9 @@ struct HfCorrelatorDsHadronsReduced { candidate.ptCand(), track.ptAssocTrack(), poolBin, - collision.numPvContrib()); - entryDsHadronRecoInfo(candidate.invMassDs(), false, false); + collision.numPvContrib(), + collision.centrality()); + entryDsHadronRecoInfo(candidate.invMass(), false, false); entryDsHadronMlInfo(candidate.bdtScorePrompt(), candidate.bdtScoreBkg()); entryTrackRecoInfo(track.dcaXY(), track.dcaZ(), track.nTpcCrossedRows()); } @@ -152,11 +153,11 @@ struct HfCorrelatorDsHadronsReduced { PROCESS_SWITCH(HfCorrelatorDsHadronsReduced, processDerivedData, "Process Derived Data", true); void processDerivedDataME(aod::HfcRedCollisions const& collisions, - aod::DsCandReduceds const& candidates, + aod::HcCandReduceds const& candidates, aod::AssocTrackReds const& tracks) { - BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}, true}; + BinningTypeDerived const corrBinning{{zPoolBins, multPoolBins}}; for (const auto& collision : collisions) { int const poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multiplicity())); @@ -182,7 +183,7 @@ struct HfCorrelatorDsHadronsReduced { auto tracksTuple = std::make_tuple(candidates, tracks); - Pair const pairData{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair const pairData{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairData) { if (tracks1.size() == 0) { @@ -204,8 +205,9 @@ struct HfCorrelatorDsHadronsReduced { cand.ptCand(), pAssoc.ptAssocTrack(), poolBin, - c1.numPvContrib()); - entryDsHadronRecoInfo(cand.invMassDs(), false, false); + c1.numPvContrib(), + c1.centrality()); + entryDsHadronRecoInfo(cand.invMass(), false, false); // entryDsHadronGenInfo(false, false, 0); } } diff --git a/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx index ff27fa3f3e9..aedb3fadca9 100644 --- a/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorDstarHadrons.cxx @@ -179,7 +179,7 @@ struct HfCorrelatorDstarHadrons { ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; - BinningType binningScheme{{binsZVtx, binsMultiplicity}, true}; + BinningType binningScheme{{binsZVtx, binsMultiplicity}}; // Eta Phi Axes ConfigurableAxis axisEta{"axisEta", {16, -1.0, 1.0}, "Eta Axis"}; ConfigurableAxis axisPhi{"axisPhi", {64, 0.0, 3.14}, "Phi Axis"}; @@ -203,7 +203,7 @@ struct HfCorrelatorDstarHadrons { invMassD0Particle = -999.0; binNumber = -2; - binningScheme = {{binsZVtx, binsMultiplicity}, true}; + binningScheme = {{binsZVtx, binsMultiplicity}}; registry.add("QA/hMultFT0M", "Multiplicity distribution in FT0M", {HistType::kTH1D, {axisSpecMultFT0M}}); registry.add("QA/hCandsPerCol", "Candidates per Collision", {HistType::kTH1D, {{100, 0.0, 100.0}}}); diff --git a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx index 2249a165d6c..50e99a602e1 100644 --- a/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx +++ b/PWGHF/HFC/TableProducer/correlatorFlowCharmHadronsReduced.cxx @@ -96,8 +96,12 @@ struct HfCorrelatorFlowCharmHadronsReduced { Configurable> bdtScore0PtMaxs{"bdtScore0PtMaxs", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential maximum score 0 for charm candidates"}; Configurable> bdtScore1PtMins{"bdtScore1PtMins", std::vector{0.1, 0.1, 0.1, 0.1, 0.1}, "pT-differential minimum score 1 for charm candidates"}; Configurable> binsPtAssoc{"binsPtAssoc", std::vector{0.3, 1., 2., 50.}, "pT bin limits for associated particles"}; - Configurable centralityMin{"centralityMin", 0, "min. centrality"}; - Configurable centralityMax{"centralityMax", 10., "max. centrality"}; + Configurable selCentrality{"selCentrality", false, "Use centrality to select events"}; + Configurable selMultiplicity{"selMultiplicity", false, "Use multiplicity to select events"}; + Configurable centMin{"centMin", 0, "min. centrality"}; + Configurable centMax{"centMax", 10., "max. centrality"}; + Configurable multMin{"multMin", 0, "min. multiplicity"}; + Configurable multMax{"multMax", 10., "max. multiplicity"}; Configurable deltaEtaAbsMin{"deltaEtaAbsMin", 0.5, "min. pair delta eta"}; Configurable deltaEtaAbsMax{"deltaEtaAbsMax", 2., "max. pair delta eta"}; Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. track DCA XY"}; @@ -106,7 +110,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { Configurable itsNClsMin{"itsNClsMin", 1, "min. ITS clusters"}; Configurable downSamplePairs{"downSamplePairs", 1., "Fraction of pairs to keep"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; - Configurable centMaxForDownSample{"centMaxForDownSample", 10., "Maximum centrality for the application of the downsampling factor"}; + Configurable centMultMaxForDownSample{"centMultMaxForDownSample", 10., "Maximum centrality for the application of the downsampling factor"}; SliceCache cache; @@ -131,10 +135,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "Primary vertex z coordinate"}; - ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "Eta bins"}; - ConfigurableAxis binsPhi{"binsPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Phi bins"}; ConfigurableAxis binsDeltaEta{"binsDeltaEta", {100, -2., 2.}, "Delta Eta bins"}; - ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -3., 3.}, "Delta Phi bins"}; + ConfigurableAxis binsDeltaPhi{"binsDeltaPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, "Delta Phi bins"}; ConfigurableAxis binsMlOne{"binsMlOne", {100, 0., 1.}, "ML score index 1 bins"}; ConfigurableAxis binsMlTwo{"binsMlTwo", {100, 0., 1.}, "ML score index 2 bins"}; @@ -142,6 +144,21 @@ struct HfCorrelatorFlowCharmHadronsReduced { void init(InitContext&) { + if (selCentrality && selMultiplicity) { + LOGP(fatal, "You cannot select events based on both centrality and multiplicity! Please check your configuration!"); + } + if (!selCentrality && !selMultiplicity) { + LOGP(fatal, "You must select events based on either centrality or multiplicity! Please check your configuration!"); + } + + // Fatal if selCentrality and process functions using multiplicity mixing, or selMultiplicity and process functions using centrality mixing + if (selCentrality && (doprocessSameEventCharmHadWMultMix || doprocessMixedEventCharmHadWMultMix || doprocessSameEventHadHadWMultMix || doprocessMixedEventHadHadWMultMix)) { + LOGP(fatal, "You cannot select events based on centrality and process functions using multiplicity mixing! Please check your configuration!"); + } + if (selMultiplicity && (doprocessSameEventCharmHadWCentMix || doprocessMixedEventCharmHadWCentMix || doprocessSameEventHadHadWCentMix || doprocessMixedEventHadHadWCentMix)) { + LOGP(fatal, "You cannot select events based on multiplicity and process functions using centrality mixing! Please check your configuration!"); + } + if ((doprocessSameEventCharmHadWCentMix && doprocessMixedEventCharmHadWMultMix) || (doprocessSameEventCharmHadWMultMix && doprocessMixedEventCharmHadWCentMix) || (doprocessSameEventHadHadWCentMix && doprocessMixedEventHadHadWMultMix) || @@ -168,8 +185,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { const AxisSpec axisPoolBin = {poolBins, 0., static_cast(poolBins), "PoolBin"}; const AxisSpec axisDeltaEta = {binsDeltaEta, "#Delta#it{#eta}"}; const AxisSpec axisDeltaPhi = {binsDeltaPhi, "#Delta#it{#varphi}"}; - const AxisSpec axisPtTrig = {(std::vector)binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {(std::vector)binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {std::vector(binsPtTrig), "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {std::vector(binsPtAssoc), "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisMlOne{binsMlOne, "bdtScore0"}; const AxisSpec axisMlTwo{binsMlTwo, "bdtScore1"}; @@ -185,6 +202,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { } registry.add("hZVtxPoolBinSE", "z vertex SE", {HistType::kTH2F, {{axisPosZ}, {axisPoolBin}}}); registry.add("hZVtxPoolBinME", "z vertex ME", {HistType::kTH2F, {{axisPosZ}, {axisPoolBin}}}); + registry.add("hCollisionsCentMultAll", "Collision centrality vs multiplicity", {HistType::kTH2F, {{axisCent}, {axisMultFT0M}}}); + registry.add("hCollisionsCentMultSel", "Collision centrality vs multiplicity", {HistType::kTH2F, {{axisCent}, {axisMultFT0M}}}); registry.add("hPoolBinTrigSE", "Trigger candidates pool bin SE", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinTrigME", "Trigger candidates pool bin ME", {HistType::kTH1F, {axisPoolBin}}); registry.add("hPoolBinAssocSE", "Associated particles pool bin SE", {HistType::kTH1F, {axisPoolBin}}); @@ -197,8 +216,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { } else if (doprocessMixedEventHadHadWCentMix || doprocessMixedEventHadHadWMultMix) { registry.add("hSparseCorrelationsMEHadHad", "THn for ME Had-Had correlations", HistType::kTHnSparseF, axes); } else { - axes.insert(axes.end(), {axisInvMass}); - // axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); + // axes.insert(axes.end(), {axisInvMass}); + axes.insert(axes.end(), {axisInvMass, axisMlOne, axisMlTwo}); if (doprocessSameEventCharmHadWCentMix || doprocessSameEventCharmHadWMultMix || doprocessSameEventCharmHadWCentMixBase) { registry.add("hSparseCorrelationsSECharmHad", "THn for SE Charm-Had correlations", HistType::kTHnSparseF, axes); } else if (doprocessMixedEventCharmHadWCentMix || doprocessMixedEventCharmHadWMultMix || doprocessMixedEventCharmHadWCentMixBase) { @@ -211,6 +230,18 @@ struct HfCorrelatorFlowCharmHadronsReduced { } } + template + bool isSelCollision(const TColl& collision) + { + bool isSel = false; + if (selCentrality) { + isSel = (collision.centrality() >= centMin && collision.centrality() <= centMax); + } else if (selMultiplicity) { + isSel = (collision.multiplicity() >= multMin && collision.multiplicity() <= multMax); + } + return isSel; + } + /// Get the binning pool associated to the collision /// \param collision is the collision /// \param binPolicy is the binning policy for the correlation @@ -262,12 +293,15 @@ struct HfCorrelatorFlowCharmHadronsReduced { template void fillSameEvent(TPair const& pair, TTrigCand const& trigCand, - TBinningType binPolicy) + const TBinningType& binPolicy) { auto collision = pair.template hfcRedCorrColl_as(); - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + registry.fill(HIST("hCollisionsCentMultAll"), collision.centrality(), collision.multiplicity()); + if (!isSelCollision(collision)) { return; } + registry.fill(HIST("hCollisionsCentMultSel"), collision.centrality(), collision.multiplicity()); + double const ptTrig = trigCand.ptTrig(); if constexpr (requires { trigCand.bdtScore0Trig(); }) { // ML selection on bkg score for Charm-Had case if (!isSelBdtScoreCut(trigCand, ptTrig)) { @@ -276,7 +310,8 @@ struct HfCorrelatorFlowCharmHadronsReduced { } if (downSamplePairs < 1.) { float const pseudoRndm = ptTrig * 1000. - static_cast(ptTrig * 1000); - if (ptTrig < ptMaxForDownSample && collision.centrality() < centMaxForDownSample && pseudoRndm >= downSamplePairs) { + float collCentMult = selCentrality ? collision.centrality() : collision.multiplicity(); + if (ptTrig < ptMaxForDownSample && collCentMult < centMultMaxForDownSample && pseudoRndm >= downSamplePairs) { return; } } @@ -298,7 +333,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { if constexpr (FillSparses) { if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases registry.fill(HIST("hSparseCorrelationsSECharmHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), - pair.deltaPhi(), trigCand.invMassTrig()); // , trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); + pair.deltaPhi(), trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); } else { registry.fill(HIST("hSparseCorrelationsSEHadHad"), poolBin, ptTrig, pair.ptAssoc(), pair.deltaEta(), pair.deltaPhi()); } @@ -311,16 +346,20 @@ struct HfCorrelatorFlowCharmHadronsReduced { /// \param binPolicy is the binning policy for the correlation template void fillMixedEvent(TPairs const& pairs, - TBinningType binPolicy) + const TBinningType& binPolicy) { for (const auto& [trigColl, trigCands, assocColl, assocTracks] : pairs) { if (trigCands.size() == 0 || assocTracks.size() == 0) { continue; } - if (trigColl.centrality() < centralityMin || trigColl.centrality() > centralityMax || - assocColl.centrality() < centralityMin || assocColl.centrality() > centralityMax) { + registry.fill(HIST("hCollisionsCentMultAll"), trigColl.centrality(), trigColl.multiplicity()); + registry.fill(HIST("hCollisionsCentMultAll"), assocColl.centrality(), assocColl.multiplicity()); + if (!isSelCollision(trigColl) || !isSelCollision(assocColl)) { continue; } + registry.fill(HIST("hCollisionsCentMultSel"), trigColl.centrality(), trigColl.multiplicity()); + registry.fill(HIST("hCollisionsCentMultSel"), assocColl.centrality(), assocColl.multiplicity()); + int const poolBinTrig = getPoolBin(trigColl, binPolicy); int const poolBinAssoc = getPoolBin(assocColl, binPolicy); if (poolBinAssoc != poolBinTrig) { @@ -345,8 +384,10 @@ struct HfCorrelatorFlowCharmHadronsReduced { double const ptAssoc = getPt(assocTrack); if (downSamplePairs < 1.) { float const pseudoRndm = ptAssoc * 1000. - static_cast(ptAssoc * 1000); - if (ptTrig < ptMaxForDownSample && trigColl.centrality() < centMaxForDownSample && - assocColl.centrality() < centMaxForDownSample && pseudoRndm >= downSamplePairs) { + float trigCollCentMult = selCentrality ? trigColl.centrality() : trigColl.multiplicity(); + float assocCollCentMult = selCentrality ? assocColl.centrality() : assocColl.multiplicity(); + if (ptTrig < ptMaxForDownSample && trigCollCentMult < centMultMaxForDownSample && + assocCollCentMult < centMultMaxForDownSample && pseudoRndm >= downSamplePairs) { continue; } } @@ -366,7 +407,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { if constexpr (FillSparses) { if constexpr (requires { trigCand.bdtScore0Trig(); }) { // Separate Charm-Had and Had-Had cases registry.fill(HIST("hSparseCorrelationsMECharmHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, - deltaPhi, trigCand.invMassTrig()); //, trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); + deltaPhi, trigCand.invMassTrig(), trigCand.bdtScore0Trig(), trigCand.bdtScore1Trig()); } else { registry.fill(HIST("hSparseCorrelationsMEHadHad"), poolBinTrig, ptTrig, ptAssoc, deltaEta, deltaPhi); } @@ -423,19 +464,22 @@ struct HfCorrelatorFlowCharmHadronsReduced { void doCorrelationsMixedEvent(const TCollision& collisions, const TTrigCand& trigCands, const TTrackAssoc& assocTracks, - TBinningType binPolicy) + const TBinningType& binPolicy) { auto tracksTuple = std::make_tuple(trigCands, assocTracks); Pair pairData{binPolicy, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairData) { + if (!isSelCollision(c1) || !isSelCollision(c2)) { + continue; + } if (tracks1.size() == 0) { continue; } - int poolBin = binPolicy.getBin({c2.posZ(), c2.centrality()}); - int poolBinTrigCand = binPolicy.getBin({c1.posZ(), c1.centrality()}); + int poolBin = binPolicy.getBin({c2.posZ(), selCentrality ? c2.centrality() : c2.multiplicity()}); + int poolBinTrigCand = binPolicy.getBin({c1.posZ(), selCentrality ? c1.centrality() : c1.multiplicity()}); if (poolBin != poolBinTrigCand) { LOGF(info, "Error, poolBins are different"); @@ -446,7 +490,6 @@ struct HfCorrelatorFlowCharmHadronsReduced { if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { continue; } - LOGF(info, "Mixed event tracks pair: (%d, %d) from events (%d, %d), track event: (%d, %d)", trigCand.index(), assTrk.index(), c1.index(), c2.index(), trigCand.hfcRedCorrCollId(), assTrk.hfcRedCorrCollId()); double deltaPhi = RecoDecay::constrainAngle(assTrk.phiAssoc() - trigCand.phiTrig(), -o2::constants::math::PIHalf); double deltaEta = assTrk.etaAssoc() - trigCand.etaTrig(); @@ -466,7 +509,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { aod::HfcRedTrigCharms const&, aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto trigCand = pair.template hfcRedTrigCharm_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZMult); @@ -476,13 +519,13 @@ struct HfCorrelatorFlowCharmHadronsReduced { fillSameEvent(pair, trigCand, binPolicyPosZMult); } } - PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", true); + PROCESS_SWITCH(HfCorrelatorFlowCharmHadronsReduced, processSameEventCharmHadWMultMix, "Process Same Event for Charm-Had with multiplicity pools", false); void processSameEventHadHadWMultMix(SameEvtPairsHadHad::iterator const& pair, aod::HfcRedTrigTracks const&, aod::HfcRedCorrColls const&) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto trigCand = pair.template hfcRedTrigTrack_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZMult); @@ -498,7 +541,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { aod::HfcRedTrigCharms const&, aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto trigCand = pair.template hfcRedTrigCharm_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZCent); @@ -514,7 +557,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { aod::HfcRedTrigTracks const&, aod::HfcRedCorrColls const&) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto trigCand = pair.template hfcRedTrigTrack_as(); if (fillSparses && fillTables) { fillSameEvent(pair, trigCand, binPolicyPosZCent); @@ -530,7 +573,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, pairsTuple, &cache}; if (fillSparses && fillTables) { @@ -547,7 +590,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto pairsTuple = std::make_tuple(candidates, tracks); Pair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, pairsTuple, &cache}; if (fillSparses && fillTables) { @@ -563,7 +606,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { void processMixedEventHadHadWCentMix(aod::HfcRedCorrColls const& collisions, AssocTracks const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZCent, numberEventsMixed, -1, collisions, tracksTuple, &cache}; if (fillSparses && fillTables) { @@ -579,7 +622,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { void processMixedEventHadHadWMultMix(aod::HfcRedCorrColls const& collisions, AssocTracks const& tracks) { - BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}, true}; + BinningMultPosZ binPolicyPosZMult{{zPoolBins, multPoolBins}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair const pairs{binPolicyPosZMult, numberEventsMixed, -1, collisions, tracksTuple, &cache}; if (fillSparses && fillTables) { @@ -595,9 +638,10 @@ struct HfCorrelatorFlowCharmHadronsReduced { void processCharmTriggers(aod::HfcRedTrigCharms const& trigCands, aod::HfcRedCorrColls const&) { + for (const auto& trigCand : trigCands) { auto collision = trigCand.template hfcRedCorrColl_as(); - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + if (!isSelCollision(collision)) { continue; } if (!isSelBdtScoreCut(trigCand, trigCand.ptTrig())) { @@ -612,10 +656,10 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, aod::HfcRedAssBases const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; for (const auto& collision : collisions) { - if (collision.centrality() < centralityMin || collision.centrality() > centralityMax) { + if (!isSelCollision(collision)) { continue; } int poolBin = binPolicyPosZCent.getBin({collision.posZ(), collision.centrality()}); @@ -633,7 +677,7 @@ struct HfCorrelatorFlowCharmHadronsReduced { TrigCharmCands const& candidates, aod::HfcRedAssBases const& tracks) { - BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}, true}; + BinningCentPosZ binPolicyPosZCent{{zPoolBins, centPoolBins}}; doCorrelationsMixedEvent(collisions, candidates, tracks, binPolicyPosZCent); } diff --git a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx index 544559ca89e..5b5722562d3 100644 --- a/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorHfeHadrons.cxx @@ -62,7 +62,7 @@ struct HfCorrelatorHfeHadrons { // Event Selection Configurable zPvPosMax{"zPvPosMax", 10., "Maximum z of the primary vertex (cm)"}; Configurable isRun3{"isRun3", true, "Data is from Run3 or Run2"}; - + Configurable skipNoEmcClusters{"skipNoEmcClusters", false, "Skip events with no EMCal clusters"}; Configurable numberEventsMixed{"numberEventsMixed", 5, "number of events mixed in ME process"}; Configurable invMassEEMax{"invMassEEMax", 0.14f, "max Invariant Mass for Photonic electron"}; // Associated Hadron selection @@ -91,7 +91,8 @@ struct HfCorrelatorHfeHadrons { // Electron hadron correlation condition Configurable ptCondition{"ptCondition", true, "Electron pT should be greater than associate particle pT"}; - Configurable eventFractionToAnalyze{"eventFractionToAnalyze", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; + Configurable electronEventFraction{"electronEventFraction", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; + Configurable hadronEventFraction{"hadronEventFraction", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; TRandom3 rnd{0}; @@ -121,7 +122,7 @@ struct HfCorrelatorHfeHadrons { ConfigurableAxis binsNSigma{"binsNSigma", {30, -15., 15.}, "#it{#sigma_{TPC}}"}; ConfigurableAxis binsMass{"binsMass", {100, 0.0, 2.0}, "Mass (GeV/#it{c}^{2}); entries"}; - BinningType corrBinning{{zPoolBins, multPoolBins}, true}; + BinningType corrBinning{{zPoolBins, multPoolBins}}; HistogramRegistry registry{ "registry", {}}; @@ -217,60 +218,69 @@ struct HfCorrelatorHfeHadrons { if (!(isRun3 ? collision.sel8() : (collision.sel7() && collision.alias_bit(kINT7)))) { return; } + int poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), collision.multFT0M())); auto bc = collision.template bc_as(); int gCollisionId = collision.globalIndex(); int64_t timeStamp = bc.timestamp(); - bool skipEventTableFilling = false; - if (eventFractionToAnalyze > 0) { - if (rnd.Uniform(0, 1) > eventFractionToAnalyze) { - skipEventTableFilling = true; + bool skipEventElectronTableFilling = false; + bool skipEventHadronTableFilling = false; + if (electronEventFraction > 0) { + if (rnd.Uniform(0, 1) > electronEventFraction) { + skipEventElectronTableFilling = true; } } - registry.fill(HIST("hNevents"), 1); + if (hadronEventFraction > 0) { + if (rnd.Uniform(0, 1) > hadronEventFraction) { + skipEventHadronTableFilling = true; + } + } - // Add hadron Table For Mix Event Electron Hadron correlation - if (!skipEventTableFilling) { - registry.fill(HIST("hZvertex"), collision.posZ()); - for (const auto& hTrack : tracks) { - if (!selAssoHadron(hTrack)) { - continue; - } + registry.fill(HIST("hZvertex"), collision.posZ()); + // fraction of event which used for Event mixing + if (!skipEventElectronTableFilling) { + registry.fill(HIST("hTracksBin"), poolBin); + } + if (!skipEventHadronTableFilling) { + registry.fill(HIST("hNevents"), 1); + } + for (const auto& hTrack : tracks) { + if (!selAssoHadron(hTrack)) { + continue; + } + if (!skipEventHadronTableFilling) { + registry.fill(HIST("hptHadron"), hTrack.pt()); - // Mc rec hadron efficiency - if constexpr (IsMc) { - if (hTrack.has_mcParticle()) { - auto mcParticle = hTrack.template mcParticle_as(); - if ((std::abs(mcParticle.pdgCode()) != kElectron) && (std::abs(mcParticle.pdgCode()) != kMuonMinus) && (std::abs(mcParticle.pdgCode()) != kPiPlus) && (std::abs(mcParticle.pdgCode()) != kKPlus) && (std::abs(mcParticle.pdgCode()) != kProton)) { - continue; - } + entryHadron(hTrack.phi(), hTrack.eta(), hTrack.pt(), poolBin, gCollisionId, timeStamp); + } + // Mc rec hadron efficiency + if constexpr (IsMc) { + if (hTrack.has_mcParticle()) { + auto mcParticle = hTrack.template mcParticle_as(); + if ((std::abs(mcParticle.pdgCode()) != kElectron) && (std::abs(mcParticle.pdgCode()) != kMuonMinus) && (std::abs(mcParticle.pdgCode()) != kPiPlus) && (std::abs(mcParticle.pdgCode()) != kKPlus) && (std::abs(mcParticle.pdgCode()) != kProton)) { + continue; + } - registry.fill(HIST("hptHadronMcRec"), hTrack.pt()); - if (mcParticle.isPhysicalPrimary()) { - - registry.fill(HIST("hptHadronMcRecPrimary"), hTrack.pt()); - - if (std::abs(mcParticle.pdgCode()) == kElectron) { - registry.fill(HIST("hMCRecptEleHadron"), hTrack.pt()); - } else if (std::abs(mcParticle.pdgCode()) == kMuonMinus) { - registry.fill(HIST("hMCRecptMuonHadron"), hTrack.pt()); - } else if (std::abs(mcParticle.pdgCode()) == kPiPlus) { - registry.fill(HIST("hMCRecptPionHadron"), hTrack.pt()); - } else if (std::abs(mcParticle.pdgCode()) == kKPlus) { - registry.fill(HIST("hMCRecptKaonHadron"), hTrack.pt()); - } else if (std::abs(mcParticle.pdgCode()) == kProton) { - registry.fill(HIST("hMCRecptProtonHadron"), hTrack.pt()); - } + registry.fill(HIST("hptHadronMcRec"), hTrack.pt()); + if (mcParticle.isPhysicalPrimary()) { + + registry.fill(HIST("hptHadronMcRecPrimary"), hTrack.pt()); + + if (std::abs(mcParticle.pdgCode()) == kElectron) { + registry.fill(HIST("hMCRecptEleHadron"), hTrack.pt()); + } else if (std::abs(mcParticle.pdgCode()) == kMuonMinus) { + registry.fill(HIST("hMCRecptMuonHadron"), hTrack.pt()); + } else if (std::abs(mcParticle.pdgCode()) == kPiPlus) { + registry.fill(HIST("hMCRecptPionHadron"), hTrack.pt()); + } else if (std::abs(mcParticle.pdgCode()) == kKPlus) { + registry.fill(HIST("hMCRecptKaonHadron"), hTrack.pt()); + } else if (std::abs(mcParticle.pdgCode()) == kProton) { + registry.fill(HIST("hMCRecptProtonHadron"), hTrack.pt()); } } } - registry.fill(HIST("hTracksBin"), poolBin); - - registry.fill(HIST("hptHadron"), hTrack.pt()); - - entryHadron(hTrack.phi(), hTrack.eta(), hTrack.pt(), poolBin, gCollisionId, timeStamp); } } @@ -279,7 +289,6 @@ struct HfCorrelatorHfeHadrons { double ptElectron = -999; double phiElectron = -999; double etaElectron = -999; - for (const auto& eTrack : electrons) { ptElectron = eTrack.ptTrack(); phiElectron = eTrack.phiTrack(); @@ -293,9 +302,7 @@ struct HfCorrelatorHfeHadrons { double etaHadron = -999; double phiHadron = -999; // EMCal electron - if (eTrack.isEmcal() && requireEmcal) { - acceptElectron = true; - } else if (!eTrack.isEmcal() && !requireEmcal) { + if (eTrack.isEmcal() == requireEmcal) { acceptElectron = true; } @@ -337,7 +344,7 @@ struct HfCorrelatorHfeHadrons { } } - if (!skipEventTableFilling) { + if (!skipEventElectronTableFilling) { registry.fill(HIST("hElectronBin"), poolBin); entryElectron(phiElectron, etaElectron, ptElectron, nElectronLS, nElectronUS, poolBin, gCollisionId, timeStamp); } @@ -377,6 +384,7 @@ struct HfCorrelatorHfeHadrons { ++nElHadUSCorr; } } + entryElectronHadronPair(deltaPhi, deltaEta, ptElectron, ptHadron, eTrack.eopEl(), eTrack.m02El(), eTrack.tpcNSigmaElTrack(), eTrack.tofNSigmaElTrack(), eTrack.tpcNClsCrRowsTrack(), eTrack.tpcCrRowsRatioTrack(), eTrack.itsChi2NClTrack(), eTrack.tpcChi2NClTrack(), eTrack.dcaXYTrack(), eTrack.dcaZTrack(), hTrack.tpcNClsCrossedRows(), hTrack.tpcCrossedRowsOverFindableCls(), hTrack.itsChi2NCl(), hTrack.tpcChi2NCl(), hTrack.dcaXY(), hTrack.dcaZ(), poolBin, nElHadLSCorr, nElHadUSCorr); } // end Hadron Track loop @@ -471,7 +479,7 @@ struct HfCorrelatorHfeHadrons { void processMcGen(McGenTableCollision const& mcCollision, aod::McParticles const& mcParticles, aod::HfMcGenSelEl const& electrons) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); for (const auto& particleMc : mcParticles) { @@ -590,7 +598,7 @@ struct HfCorrelatorHfeHadrons { void processMcGenMixedEvent(McGenTableCollisions const& mcCollision, aod::HfMcGenSelEl const& electrons, aod::McParticles const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}, true}; + BinningTypeMcGen const corrBinningMcGen{{zPoolBins, multPoolBinsMcGen}}; auto tracksTuple = std::make_tuple(electrons, mcParticles); Pair const pairMcGen{corrBinningMcGen, 5, -1, mcCollision, tracksTuple, &cache}; diff --git a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx index 15bc9ed7c79..25a7091e6b8 100644 --- a/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorLcHadrons.cxx @@ -282,7 +282,7 @@ struct HfCorrelatorLcHadrons { ConfigurableAxis binsMassLc{"binsMassLc", {200, 1.98, 2.58}, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsCentFt0m{"binsCentFt0m", {100, 0., 100.}, "Centrality percentile (FT0M)"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(InitContext&) @@ -290,8 +290,8 @@ struct HfCorrelatorLcHadrons { AxisSpec axisMassLc = {binsMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; AxisSpec const axisEta = {binsEta, "#it{eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtLc = {(std::vector)binsPtLc, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtLc = {std::vector(binsPtLc), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec axisPtTrack = {500, 0, 50, "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; @@ -362,7 +362,7 @@ struct HfCorrelatorLcHadrons { registry.add("hEtaMcGen", "Lc,Hadron particles - MC Gen", {HistType::kTH1D, {axisEta}}); registry.add("hPhiMcGen", "Lc,Hadron particles - MC Gen", {HistType::kTH1D, {axisPhi}}); registry.add("hMultFT0AMcGen", "Lc,Hadron multiplicity FT0A - MC Gen", {HistType::kTH1D, {axisMultiplicity}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } /// Lc-hadron correlation pair builder - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) @@ -696,7 +696,11 @@ struct HfCorrelatorLcHadrons { } //} } - float cent = 100.0; // will be updated later + + float cent = 0.; + if (useCentrality) { + cent = collision.centFT0M(); + } // Lc-Hadron correlation dedicated section // if the candidate is selected as Lc, search for Hadron ad evaluate correlations @@ -821,7 +825,7 @@ struct HfCorrelatorLcHadrons { int counterLcHadron = 0; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; int poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); @@ -890,7 +894,7 @@ struct HfCorrelatorLcHadrons { correlationStatus = true; } - if ((std::abs(particleAssoc.pdgCode()) != kElectron) && (std::abs(particleAssoc.pdgCode()) != kMuonMinus) && (std::abs(particleAssoc.pdgCode()) != kPiPlus) && (std::abs(particle.pdgCode()) != kKPlus) && (std::abs(particleAssoc.pdgCode()) != kProton)) { + if ((std::abs(particleAssoc.pdgCode()) != kElectron) && (std::abs(particleAssoc.pdgCode()) != kMuonMinus) && (std::abs(particleAssoc.pdgCode()) != kPiPlus) && (std::abs(particleAssoc.pdgCode()) != kKPlus) && (std::abs(particleAssoc.pdgCode()) != kProton)) { continue; } @@ -908,8 +912,8 @@ struct HfCorrelatorLcHadrons { } } - int8_t const chargeLc = pdg->GetParticle(particle.pdgCode())->Charge(); // Retrieve charge - int8_t const chargeAssoc = pdg->GetParticle(particleAssoc.pdgCode())->Charge(); // Retrieve charge + auto const chargeLc = static_cast(pdg->GetParticle(particle.pdgCode())->Charge()); // Retrieve charge + auto const chargeAssoc = static_cast(pdg->GetParticle(particleAssoc.pdgCode())->Charge()); // Retrieve charge int trackOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, particleAssoc, true); registry.fill(HIST("hPtParticleAssocMcGen"), particleAssoc.pt()); @@ -1007,7 +1011,7 @@ struct HfCorrelatorLcHadrons { TracksWithMc const& tracks, aod::McParticles const& mcParticles) { - BinningType const corrBinning{{binsZVtx, binsMultiplicityMc}, true}; + BinningType const corrBinningMcRec{{binsZVtx, binsMultiplicityMc}}; for (const auto& candidate : candidates) { if (std::abs(HfHelper::yLc(candidate)) > yCandMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { continue; @@ -1043,11 +1047,11 @@ struct HfCorrelatorLcHadrons { } } auto tracksTuple = std::make_tuple(candidates, tracks); - Pair const pairMcRec{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair const pairMcRec{corrBinningMcRec, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcRec) { - int poolBin = corrBinning.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); - int const poolBinLc = corrBinning.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); + int poolBin = corrBinningMcRec.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); + int const poolBinLc = corrBinningMcRec.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); registry.fill(HIST("hMultFT0M"), c1.multFT0M()); registry.fill(HIST("hZvtx"), c1.posZ()); registry.fill(HIST("hTracksPoolBin"), poolBin); @@ -1128,7 +1132,7 @@ struct HfCorrelatorLcHadrons { void processMcGenMixedEvent(SelCollisionsWithLcMc const& collisions, CandidatesLcMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { @@ -1153,9 +1157,9 @@ struct HfCorrelatorLcHadrons { if (pidTrkApplied && (std::abs(particleAssoc.pdgCode()) != kProton)) { continue; // proton PID } - int8_t const chargeLc = pdg->GetParticle(candidate.pdgCode())->Charge(); // Retrieve charge - int8_t const chargeAssoc = pdg->GetParticle(particleAssoc.pdgCode())->Charge(); // Retrieve charge - float cent = 100.0; // will be updated later + auto const chargeLc = static_cast(pdg->GetParticle(candidate.pdgCode())->Charge()); // Retrieve charge + auto const chargeAssoc = static_cast(pdg->GetParticle(particleAssoc.pdgCode())->Charge()); // Retrieve charge + float cent = 100.0; // will be updated later int trackOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, particleAssoc, true); bool isLcPrompt = candidate.originMcGen() == RecoDecay::OriginType::Prompt; diff --git a/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx index 144c344abbc..3b2b29b1f8b 100644 --- a/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx +++ b/PWGHF/HFC/TableProducer/correlatorLcScHadrons.cxx @@ -26,6 +26,7 @@ #include "PWGHF/HFC/DataModel/CorrelationTables.h" #include "PWGHF/HFC/Utils/utilsCorrelations.h" #include "PWGHF/Utils/utilsAnalysis.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" @@ -54,6 +55,8 @@ #include #include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include #include @@ -61,6 +64,7 @@ #include #include #include +#include #include using namespace o2; @@ -94,16 +98,9 @@ struct HfCorrelatorLcScHadronsSelection { Configurable ptCandMin{"ptCandMin", 1., "min. cand. pT"}; struct : ConfigurableGroup { - Configurable cfgV0radiusMin{"cfgV0radiusMin", 1.2, "minimum decay radius"}; - Configurable cfgDCAPosToPVMin{"cfgDCAPosToPVMin", 0.05, "minimum DCA to PV for positive track"}; - Configurable cfgDCANegToPVMin{"cfgDCANegToPVMin", 0.2, "minimum DCA to PV for negative track"}; - Configurable cfgV0CosPA{"cfgV0CosPA", 0.995, "minimum v0 cosine"}; - Configurable cfgDCAV0Dau{"cfgDCAV0Dau", 1.0, "maximum DCA between daughters"}; - Configurable cfgV0PtMin{"cfgV0PtMin", 0, "minimum pT for lambda"}; - Configurable cfgV0LifeTime{"cfgV0LifeTime", 30., "maximum lambda lifetime"}; - Configurable cfgPV{"cfgPV", 10., "maximum z-vertex"}; Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable cfgPV{"cfgPV", 10., "maximum z-vertex"}; } cfgV0; SliceCache cache; @@ -117,7 +114,7 @@ struct HfCorrelatorLcScHadronsSelection { // filter on selection of Lc and decay channel Lc->PKPi Filter lcFilter = ((o2::aod::hf_track_index::hfflag & static_cast(1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) != static_cast(0)) && (aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlagLc || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlagLc); - template + template void selectionCollision(CollType const& collision, CandType const& candidates) { bool isSelColl = true; @@ -146,6 +143,19 @@ struct HfCorrelatorLcScHadronsSelection { isCandFound = false; continue; } + if constexpr (IsMc) { + auto const mcFlag = std::abs(candidate.flagMcMatchRec()); + + // Cast enums to int to safely compare against the absolute integer flag + bool isSc0 = (mcFlag == static_cast(o2::hf_decay::hf_cand_sigmac::DecayChannelMain::Sc0ToPKPiPi)); + bool isScPlusPlus = (mcFlag == static_cast(o2::hf_decay::hf_cand_sigmac::DecayChannelMain::ScplusplusToPKPiPi)); + bool isLc = (mcFlag == static_cast(o2::hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi)); + + if (!(isSc0 || isScPlusPlus || isLc)) { + isCandFound = false; + continue; + } + } isCandFound = true; break; } @@ -184,39 +194,8 @@ struct HfCorrelatorLcScHadronsSelection { candSel(isCandFound); } - template - bool selectionV0(TCollision const& collision, V0 const& candidate) - { - if (candidate.v0radius() < cfgV0.cfgV0radiusMin) { - return false; - } - if (std::abs(candidate.dcapostopv()) < cfgV0.cfgDCAPosToPVMin) { - return false; - } - if (std::abs(candidate.dcanegtopv()) < cfgV0.cfgDCANegToPVMin) { - return false; - } - if (candidate.v0cosPA() < cfgV0.cfgV0CosPA) { - return false; - } - if (std::abs(candidate.dcaV0daughters()) > cfgV0.cfgDCAV0Dau) { - return false; - } - if (candidate.pt() < cfgV0.cfgV0PtMin) { - return false; - } - if (std::abs(candidate.yLambda()) > yCandMax) { - return false; - } - if (candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda > cfgV0.cfgV0LifeTime) { - return false; - } - - return true; - } - template - bool eventSelV0(TCollision collision) + bool eventSelV0(TCollision const& collision) { if (!collision.sel8()) { return 0; @@ -256,12 +235,7 @@ struct HfCorrelatorLcScHadronsSelection { candSel(isCandFound); return; } - for (const auto& v0 : V0s) { - if (selectionV0(collision, v0)) { - isCandFound = true; - break; - } - } + isCandFound = true; candSel(isCandFound); } PROCESS_SWITCH(HfCorrelatorLcScHadronsSelection, processV0Selection, "Process V0 Collision Selection for Data", true); @@ -269,28 +243,28 @@ struct HfCorrelatorLcScHadronsSelection { void processLcSelection(SelCollisions::iterator const& collision, CandsLcDataFiltered const& candidates) { - selectionCollision(collision, candidates); + selectionCollision(collision, candidates); } PROCESS_SWITCH(HfCorrelatorLcScHadronsSelection, processLcSelection, "Process Lc Collision Selection for Data and Mc", true); void processScSelection(SelCollisions::iterator const& collision, aod::HfCandSc const& candidates) { - selectionCollision(collision, candidates); + selectionCollision(collision, candidates); } PROCESS_SWITCH(HfCorrelatorLcScHadronsSelection, processScSelection, "Process Sc Collision Selection for Data and Mc", false); void processLcSelectionMcRec(SelCollisions::iterator const& collision, CandsLcMcRecFiltered const& candidates) { - selectionCollision(collision, candidates); + selectionCollision(collision, candidates); } PROCESS_SWITCH(HfCorrelatorLcScHadronsSelection, processLcSelectionMcRec, "Process Lc Selection McRec", false); void processScSelectionMcRec(SelCollisions::iterator const& collision, CandsScMcRec const& candidates) { - selectionCollision(collision, candidates); + selectionCollision(collision, candidates); } PROCESS_SWITCH(HfCorrelatorLcScHadronsSelection, processScSelectionMcRec, "Process Sc Selection McRec", false); @@ -317,8 +291,10 @@ struct HfCorrelatorLcScHadrons { Produces entryCandHadronPairY; Produces entryCandHadronPairTrkPID; Produces entryCandHadronRecoInfo; + Produces entryScHadronRecoInfoExt; Produces entryCandHadronMlInfo; - Produces entryCandCandRecoInfo; + Produces entryCandRecoInfo; + Produces entryScRecoInfoExt; Produces entryCandHadronGenInfo; Produces entryCandCandGenInfo; Produces entryTrackRecoInfo; @@ -327,72 +303,74 @@ struct HfCorrelatorLcScHadrons { Produces entryTrkPID; Produces entryPairCandCharge; Produces entryCandCharge; + Produces entryLcHadronInvMass; + Produces entryPairedV0InvMass; + Produces entryV0InvMass; - Configurable selectionFlagLc{"selectionFlagLc", 1, "Selection Flag for Lc"}; - Configurable numberEventsMixed{"numberEventsMixed", 5, "number of events mixed in ME process"}; - Configurable applyEfficiency{"applyEfficiency", 1, "Flag for applying Lc efficiency weights"}; - Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; - Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen. cand. rapidity"}; - Configurable etaTrackMax{"etaTrackMax", 0.8, "max. eta of tracks"}; - Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. DCAxy of tracks"}; - Configurable dcaZTrackMax{"dcaZTrackMax", 1., "max. DCAz of tracks"}; - Configurable ptCandMin{"ptCandMin", 1., "min. cand. pT"}; - Configurable ptCandMax{"ptCandMax", 50., "max. cand. pT"}; - Configurable ptTrackMin{"ptTrackMin", 0.3, "min. track pT"}; - Configurable ptTrackMax{"ptTrackMax", 50., "max. track pT"}; - Configurable multMin{"multMin", 0., "minimum multiplicity accepted"}; - Configurable multMax{"multMax", 10000., "maximum multiplicity accepted"}; - Configurable> classMl{"classMl", {0, 1, 2}, "Indexes of ML scores to be stored. Three indexes max."}; - Configurable> binsPtLc{"binsPtLc", std::vector{o2::analysis::hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits for candidate mass plots"}; - Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle"}; - Configurable> binsPtEfficiencyLc{"binsPtEfficiencyLc", std::vector{o2::analysis::hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits for efficiency"}; - Configurable> efficiencyLc{"efficiencyLc", {1., 1., 1., 1., 1., 1.}, "efficiency values for Lc"}; - Configurable storeAutoCorrelationFlag{"storeAutoCorrelationFlag", false, "Store flag that indicates if the track is paired to its Lc mother instead of skipping it"}; - Configurable correlateLcWithLeadingParticle{"correlateLcWithLeadingParticle", false, "Switch for correlation of Lc baryons with leading particle only"}; - Configurable pidTrkApplied{"pidTrkApplied", false, "Apply PID selection for associated tracks"}; - Configurable> trkPIDspecies{"trkPIDspecies", std::vector{o2::track::PID::Proton, o2::track::PID::Pion, o2::track::PID::Kaon}, "Trk sel: Particles species for PID, proton, pion, kaon"}; - Configurable> pidTPCMax{"pidTPCMax", std::vector{3., 0., 0.}, "maximum nSigma TPC"}; - Configurable> pidTOFMax{"pidTOFMax", std::vector{3., 0., 0.}, "maximum nSigma TOF"}; - Configurable tofPIDThreshold{"tofPIDThreshold", 0.75, "minimum pT after which TOF PID is applicable"}; - Configurable fillTrkPID{"fillTrkPID", false, "fill PID information for associated tracks"}; - Configurable forceTOF{"forceTOF", false, "fill PID information for associated tracks"}; - Configurable calTrkEff{"calTrkEff", false, "fill histograms to calculate efficiency"}; - Configurable isRecTrkPhyPrimary{"isRecTrkPhyPrimary", true, "Calculate the efficiency of reconstructed primary physical tracks"}; - Configurable calEffEventWithCand{"calEffEventWithCand", true, "Calculate the efficiency of Lc candidate"}; - Configurable eventFractionToAnalyze{"eventFractionToAnalyze", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; + SliceCache cache; + Service pdg{}; + + struct : ConfigurableGroup { + Configurable selectionFlagLc{"selectionFlagLc", 1, "Selection Flag for Lc"}; + Configurable numberEventsMixed{"numberEventsMixed", 5, "number of events mixed in ME process"}; + Configurable applyEfficiency{"applyEfficiency", 1, "Flag for applying Lc efficiency weights"}; + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen. cand. rapidity"}; + Configurable etaTrackMax{"etaTrackMax", 0.8, "max. eta of tracks"}; + Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. DCAxy of tracks"}; + Configurable dcaZTrackMax{"dcaZTrackMax", 1., "max. DCAz of tracks"}; + Configurable ptCandMin{"ptCandMin", 1., "min. cand. pT"}; + Configurable ptCandMax{"ptCandMax", 50., "max. cand. pT"}; + Configurable ptTrackMin{"ptTrackMin", 0.3, "min. track pT"}; + Configurable ptTrackMax{"ptTrackMax", 50., "max. track pT"}; + Configurable multMin{"multMin", 0., "minimum multiplicity accepted"}; + Configurable multMax{"multMax", 10000., "maximum multiplicity accepted"}; + Configurable> classMl{"classMl", {0, 1, 2}, "Indexes of ML scores to be stored. Three indexes max."}; + Configurable> binsPtLc{"binsPtLc", std::vector{o2::analysis::hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits for candidate mass plots"}; + Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle"}; + Configurable> binsPtEfficiencyLc{"binsPtEfficiencyLc", std::vector{o2::analysis::hf_cuts_lc_to_p_k_pi::vecBinsPt}, "pT bin limits for efficiency"}; + Configurable> efficiencyLc{"efficiencyLc", {1., 1., 1., 1., 1., 1.}, "efficiency values for Lc"}; + Configurable storeAutoCorrelationFlag{"storeAutoCorrelationFlag", false, "Store flag that indicates if the track is paired to its Lc mother instead of skipping it"}; + Configurable correlateLcWithLeadingParticle{"correlateLcWithLeadingParticle", false, "Switch for correlation of Lc baryons with leading particle only"}; + Configurable pidTrkApplied{"pidTrkApplied", false, "Apply PID selection for associated tracks"}; + Configurable> trkPIDspecies{"trkPIDspecies", std::vector{o2::track::PID::Proton, o2::track::PID::Pion, o2::track::PID::Kaon}, "Trk sel: Particles species for PID, proton, pion, kaon"}; + Configurable> pidTPCMax{"pidTPCMax", std::vector{3., 0., 0.}, "maximum nSigma TPC"}; + Configurable> pidTOFMax{"pidTOFMax", std::vector{3., 0., 0.}, "maximum nSigma TOF"}; + Configurable tofPIDThreshold{"tofPIDThreshold", 0.75, "minimum pT after which TOF PID is applicable"}; + Configurable fillTrkPID{"fillTrkPID", false, "fill PID information for associated tracks"}; + Configurable forceTOF{"forceTOF", false, "fill PID information for associated tracks"}; + Configurable calTrkEff{"calTrkEff", false, "fill histograms to calculate efficiency"}; + Configurable isRecTrkPhyPrimary{"isRecTrkPhyPrimary", true, "Calculate the efficiency of reconstructed primary physical tracks"}; + Configurable calEffEventWithCand{"calEffEventWithCand", true, "Calculate the efficiency of Lc candidate"}; + Configurable eventFractionToAnalyze{"eventFractionToAnalyze", -1, "Fraction of events to analyze (use only for ME offline on very large samples)"}; + } cfgCharmCand; struct : ConfigurableGroup { - Configurable cfgDaughPrPtMax{"cfgDaughPrPtMax", 5., "max. pT Daughter Proton"}; - Configurable cfgDaughPrPtMin{"cfgDaughPrPtMin", 0.3, "min. pT Daughter Proton"}; - Configurable cfgDaughPiPtMax{"cfgDaughPiPtMax", 10., "max. pT Daughter Pion"}; - Configurable cfgDaughPiPtMin{"cfgDaughPiPtMin", 0.3, "min. pT Daughter Pion"}; - Configurable cfgDaughPIDCutsTPCPr{"cfgDaughPIDCutsTPCPr", 3., "max. TPCnSigma Proton"}; - Configurable cfgDaughPIDCutsTPCPi{"cfgDaughPIDCutsTPCPi", 2., "max. TPCnSigma Pion"}; - Configurable cfgDaughPIDCutsTOFPi{"cfgDaughPIDCutsTOFPi", 2., "max. TOFnSigma Pion"}; + Configurable cfgV0DaughPrPtMax{"cfgV0DaughPrPtMax", 5., "max. pT Daughter Proton"}; + Configurable cfgV0DaughPrPtMin{"cfgV0DaughPrPtMin", 0.3, "min. pT Daughter Proton"}; + Configurable cfgV0DaughPiPtMax{"cfgV0DaughPiPtMax", 10., "max. pT Daughter Pion"}; + Configurable cfgV0DaughPiPtMin{"cfgV0DaughPiPtMin", 0.3, "min. pT Daughter Pion"}; + Configurable cfgV0DaughPIDCutsTPCPr{"cfgV0DaughPIDCutsTPCPr", 2.5, "max. TPCnSigma Proton"}; + Configurable cfgV0DaughPIDCutsTPCPi{"cfgV0DaughPIDCutsTPCPi", 2.5, "max. TPCnSigma Pion"}; + Configurable cfgV0DaughPIDCutsTOFPi{"cfgV0DaughPIDCutsTOFPi", 2.5, "max. TOFnSigma Pion"}; + Configurable cfgV0DaughPIDCutsTOFPr{"cfgV0DaughPIDCutsTOFPr", -2.5, "min. TOFnSigma Proton (put only negative value)"}; Configurable cfgHypMassWindow{"cfgHypMassWindow", 0.1, "single lambda mass selection"}; Configurable cfgIsCorrCollMatchV0{"cfgIsCorrCollMatchV0", true, "check if daughter and mother collision are same"}; + Configurable cfgCalDataDrivenEffPr{"cfgCalDataDrivenEffPr", false, "calculate data driven efficiency of proton using Lambda"}; + Configurable cfgV0radiusMin{"cfgV0radiusMin", 1.2, "minimum decay radius"}; + Configurable cfgDCAPosToPVMin{"cfgDCAPosToPVMin", 0.05, "minimum DCA to PV for positive track"}; + Configurable cfgDCANegToPVMin{"cfgDCANegToPVMin", 0.2, "minimum DCA to PV for negative track"}; + Configurable cfgV0CosPA{"cfgV0CosPA", 0.995, "minimum v0 cosine"}; + Configurable cfgDCAV0Dau{"cfgDCAV0Dau", 1.0, "maximum DCA between daughters"}; + Configurable cfgV0PtMin{"cfgV0PtMin", 0, "minimum pT for lambda"}; + Configurable cfgV0LifeTime{"cfgV0LifeTime", 30., "maximum lambda lifetime"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable cfgPV{"cfgPV", 10., "maximum z-vertex"}; + Configurable calEffV0{"calEffV0", false, "calculate lambda0 efficiency"}; + Configurable checkTOFForPion{"checkTOFForPion", false, "if True, TOF selection on pion V0 wil only be applied if TOF present"}; } cfgV0; - SliceCache cache; - Service pdg{}; - int8_t chargeCand = 3; - int8_t signSoftPion = 0; - int leadingIndex = 0; - int poolBin = 0; - int poolBinLc = 0; - bool correlationStatus = false; - bool isPrompt = false; - bool isNonPrompt = false; - bool isSignal = false; - static constexpr int8_t ChargeScPlusPlus{2}; - static constexpr int8_t ChargeZero{0}; - static constexpr int8_t AssignedChargeSc0{1}; // to distinguish sc0 from anti-sc0, charge set to +1 and -1 - - TRandom3* rnd = new TRandom3(0); - // std::vector outputMl = {-1., -1., -1.}; - std::vector outputMlPKPi = {-1., -1., -1.}; - std::vector outputMlPiKP = {-1., -1., -1.}; - // Event Mixing for the Data Mode // using SelCollisionsWithSc = soa::Join; using SelCollisions = soa::Filtered>; @@ -414,10 +392,13 @@ struct HfCorrelatorLcScHadrons { using TracksData = soa::Filtered>; // trackFilter applied using TracksWithMc = soa::Filtered>; // trackFilter applied + template + using HasStrangeTOFinV0 = decltype(std::declval().tofNSigmaLaPr()); + // Filters for ME Filter collisionFilter = aod::hf_selection_lc_collision::lcSel == true; - Filter lcFilter = ((o2::aod::hf_track_index::hfflag & static_cast(1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) != static_cast(0)) && (aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlagLc || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlagLc); - Filter trackFilter = (nabs(aod::track::eta) < etaTrackMax) && (nabs(aod::track::pt) > ptTrackMin) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); + Filter lcFilter = ((o2::aod::hf_track_index::hfflag & static_cast(1 << aod::hf_cand_3prong::DecayType::LcToPKPi)) != static_cast(0)) && (aod::hf_sel_candidate_lc::isSelLcToPKPi >= cfgCharmCand.selectionFlagLc || aod::hf_sel_candidate_lc::isSelLcToPiKP >= cfgCharmCand.selectionFlagLc); + Filter trackFilter = (nabs(aod::track::eta) < cfgCharmCand.etaTrackMax) && (nabs(aod::track::pt) > cfgCharmCand.ptTrackMin) && (nabs(aod::track::dcaXY) < cfgCharmCand.dcaXYTrackMax) && (nabs(aod::track::dcaZ) < cfgCharmCand.dcaZTrackMax); Preslice perTrueCollision = o2::aod::mcparticle::mcCollisionId; Preslice perCollisionID = aod::track::collisionId; @@ -436,16 +417,32 @@ struct HfCorrelatorLcScHadrons { ConfigurableAxis binsCandMass{"binsCandMass", {200, 1.98, 2.58}, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; ConfigurableAxis binsNSigmas{"binsNSigmas", {4000, -500., 500.}, "n#sigma"}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; - + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + int8_t chargeCand = 3; + int8_t signSoftPion = 0; + int leadingIndex = 0; + int poolBin = 0; + int poolBinLc = 0; + bool correlationStatus = false; + bool isPrompt = false; + bool isNonPrompt = false; + bool isSignal = false; + static constexpr int8_t ChargeScPlusPlus{2}; + static constexpr int8_t ChargeZero{0}; + static constexpr int8_t AssignedChargeSc0{1}; // to distinguish sc0 from anti-sc0, charge set to +1 and -1 + TRandom3 rnd{0}; + std::vector outputMlPKPi = {-1., -1., -1.}; + std::vector outputMlPiKP = {-1., -1., -1.}; + void init(InitContext&) { AxisSpec axisCandMass = {binsCandMass, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; AxisSpec const axisEta = {binsEta, "#it{eta}"}; AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; - AxisSpec axisPtLc = {static_cast>(binsPtLc), "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {static_cast>(binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtLc = {static_cast>(cfgCharmCand.binsPtLc), "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {static_cast>(cfgCharmCand.binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec axisPtTrack = {500, 0, 50, "#it{p}_{T} Hadron (GeV/#it{c})"}; AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; @@ -518,6 +515,12 @@ struct HfCorrelatorLcScHadrons { registry.add("hTPCnSigmaPr", "hTPCnSigmaPr", {HistType::kTH2F, {{axisPtHadron}, {axisNSigma}}}); registry.add("hTOFnSigmaPrPiKRej", "hTOFnSigmaPrPiKRej", {HistType::kTH2F, {{axisPtHadron}, {axisNSigma}}}); registry.add("hTPCnSigmaPrPiKRej", "hTPCnSigmaPrPiKRej", {HistType::kTH2F, {{axisPtHadron}, {axisNSigma}}}); + registry.add("hLcHadronInvMassVsPt", "Lc+Hadron invariant mass vs combined pT;m_{Lc+h} (GeV/c^2);p_{T,combined} (GeV/c)", {HistType::kTH2F, {{500, 2.0, 4.5}, {100, 0., 50.}}}); + registry.add("hLcLambdaInvMassVsPt", "Lc+Lambda invariant mass vs combined pT;m_{Lc+Lambda} (GeV/c^2);p_{T,combined} (GeV/c)", {HistType::kTH2F, {{500, 2.0, 5.0}, {100, 0., 50.}}}); + registry.add("hLcHadronInvMass", "Lc+Hadron invariant mass;m_{Lc+h} (GeV/c^2);entries", {HistType::kTH1F, {{500, 2.0, 4.5}}}); + registry.add("hLcLambdaInvMass", "Lc+Lambda invariant mass;m_{Lc+Lambda} (GeV/c^2);entries", {HistType::kTH1F, {{500, 2.5, 5.0}}}); + registry.add("hLcHadronPtCombined", "Combined pT (Lc+Hadron);p_{T,combined} (GeV/c);entries", {HistType::kTH1F, {{100, 0., 50.}}}); + registry.add("hLcLambdaPtCombined", "Combined pT (Lc+Lambda);p_{T,combined} (GeV/c);entries", {HistType::kTH1F, {{100, 0., 50.}}}); // Lambda V0 histograms registry.add("hEventLambdaV0", "Lambda, events", {HistType::kTH1F, {{2, 0, 2}}}); @@ -528,16 +531,19 @@ struct HfCorrelatorLcScHadrons { registry.add("hV0LambdaMcRec", "McRec V0 Lambda candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); registry.add("hV0LambdaReflMcRec", "McRec V0 Lambda reflected candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); registry.add("hV0LambdaPiKRejMcRec", "McRec V0 Lambda candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0PrimLambdaMcRec", "McRec V0 Lambda candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0PrimLambdaReflMcRec", "McRec V0 Lambda reflected candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); registry.add("hV0LambdaReflPiKRejMcRec", "McRec V0 Lambda reflected candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0PtPrimLambdaMcGen", "Mcgen V0 Lambda candidates;GeV/#it{c}", {HistType::kTH1F, {{axisPtV0}}}); - corrBinning = {{binsZVtx, binsMultiplicity}, true}; + corrBinning = {{binsZVtx, binsMultiplicity}}; } template void fillMlOutput(MlProbType const& mlProb, std::vector& outputMl) { - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMl[iclass] = mlProb[classMl->at(iclass)]; + for (unsigned int iclass = 0; iclass < cfgCharmCand.classMl->size(); iclass++) { + outputMl[iclass] = mlProb[cfgCharmCand.classMl->at(iclass)]; } }; @@ -560,123 +566,339 @@ struct HfCorrelatorLcScHadrons { return y; } - template - bool isSelectedV0Daughter(T const& track, int pid) + template + bool selectionV0(TCollision const& collision, V0 const& candidate) { - // if (!track.isGlobalTrackWoDCA()) - // return false; - if (std::abs(pid) == kProton && std::abs(track.tpcNSigmaPr()) > cfgV0.cfgDaughPIDCutsTPCPr) { + if (candidate.v0radius() < cfgV0.cfgV0radiusMin) { return false; } - if (std::abs(pid) == kPiPlus && (std::abs(track.tpcNSigmaPi()) > cfgV0.cfgDaughPIDCutsTPCPi || std::abs(track.tofNSigmaPi()) > cfgV0.cfgDaughPIDCutsTOFPi)) { + if (std::abs(candidate.dcapostopv()) < cfgV0.cfgDCAPosToPVMin) { return false; } - if (std::abs(track.eta()) > etaTrackMax) { + if (std::abs(candidate.dcanegtopv()) < cfgV0.cfgDCANegToPVMin) { return false; } - if (std::abs(pid) == kProton && track.pt() > cfgV0.cfgDaughPrPtMax) { + if (candidate.v0cosPA() < cfgV0.cfgV0CosPA) { return false; } - if (std::abs(pid) == kProton && track.pt() < cfgV0.cfgDaughPrPtMin) { + if (std::abs(candidate.dcaV0daughters()) > cfgV0.cfgDCAV0Dau) { return false; } - if (std::abs(pid) == kPiPlus && track.pt() > cfgV0.cfgDaughPiPtMax) { + if (candidate.pt() < cfgV0.cfgV0PtMin) { + return false; + } + if (std::abs(candidate.yLambda()) > cfgCharmCand.yCandMax) { return false; } - if (std::abs(pid) == kPiPlus && track.pt() < cfgV0.cfgDaughPiPtMin) { + if (candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda > cfgV0.cfgV0LifeTime) { return false; } return true; } - template - void fillV0Histograms(V0 const& v0s, TrackType const&) + template + bool isSelectedV0Daughter(Tracktype const& track, V0Type const& v0, int pid) { - for (const auto& v0 : v0s) { - auto posTrackV0 = v0.template posTrack_as(); - auto negTrackV0 = v0.template negTrack_as(); - if (cfgV0.cfgIsCorrCollMatchV0 && ((v0.collisionId() != posTrackV0.collisionId()) || (v0.collisionId() != negTrackV0.collisionId()))) { - continue; + if (std::abs(track.eta()) > cfgCharmCand.etaTrackMax) { + return false; + } + + // --------------------------------------------------------- + // 1. Proton PID Selection + // --------------------------------------------------------- + if (std::abs(pid) == kProton) { + bool hasTOFProton = (pid > 0) ? v0.positiveHasTOF() : v0.negativeHasTOF(); + bool passTOF = false; + + if (track.pt() > cfgV0.cfgV0DaughPrPtMax || track.pt() < cfgV0.cfgV0DaughPrPtMin) { + return false; } - if (std::abs(o2::constants::physics::MassLambda - v0.mLambda()) < cfgV0.cfgHypMassWindow) { - entryHadron(v0.mLambda(), posTrackV0.eta(), posTrackV0.pt() * posTrackV0.sign(), 0, 0, v0.pt()); - entryTrkPID(posTrackV0.tpcNSigmaPr(), posTrackV0.tpcNSigmaKa(), posTrackV0.tpcNSigmaPi(), posTrackV0.tofNSigmaPr(), posTrackV0.tofNSigmaKa(), posTrackV0.tofNSigmaPi()); + if (hasTOFProton && (track.pt() > cfgCharmCand.tofPIDThreshold)) { + if constexpr (std::experimental::is_detected::value) { + // pid > 0: Proton from Lambda (LaPr) + // pid < 0: Antiproton from Anti-Lambda (ALaPr) + double strangeTOF = (pid > 0) ? v0.tofNSigmaLaPr() : v0.tofNSigmaALaPr(); + passTOF = strangeTOF > cfgV0.cfgV0DaughPIDCutsTOFPr; - if (isSelectedV0Daughter(posTrackV0, kProton) && isSelectedV0Daughter(negTrackV0, kPiPlus)) { - registry.fill(HIST("hV0Lambda"), v0.mLambda(), v0.pt(), posTrackV0.pt()); - registry.fill(HIST("hV0LambdaRefl"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); + } else { + // if strange TOF is unavailable + passTOF = track.tofNSigmaPr() > cfgV0.cfgV0DaughPIDCutsTOFPr; + } + } - registry.fill(HIST("hTPCnSigmaPr"), posTrackV0.pt(), posTrackV0.tpcNSigmaPr()); - if (posTrackV0.hasTOF()) { - registry.fill(HIST("hTOFnSigmaPr"), posTrackV0.pt(), posTrackV0.tofNSigmaPr()); - } + if ((std::abs(track.tpcNSigmaPr()) > cfgV0.cfgV0DaughPIDCutsTPCPr) && !passTOF) { + return false; + } + } - if (passPIDSelection(posTrackV0, trkPIDspecies, pidTPCMax, pidTOFMax, tofPIDThreshold, forceTOF)) { - registry.fill(HIST("hV0LambdaPiKRej"), v0.mLambda(), v0.pt(), posTrackV0.pt()); - registry.fill(HIST("hV0LambdaReflPiKRej"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); + // --------------------------------------------------------- + // 2. Pion PID Selection + // --------------------------------------------------------- + if (std::abs(pid) == kPiPlus && cfgV0.checkTOFForPion) { + bool hasTOFPion = (pid < 0) ? v0.negativeHasTOF() : v0.positiveHasTOF(); + bool passTOF = false; - registry.fill(HIST("hTPCnSigmaPrPiKRej"), posTrackV0.pt(), posTrackV0.tpcNSigmaPr()); - if (posTrackV0.hasTOF()) { - registry.fill(HIST("hTOFnSigmaPrPiKRej"), posTrackV0.pt(), posTrackV0.tofNSigmaPr()); - } - } + if (track.pt() > cfgV0.cfgV0DaughPiPtMax || track.pt() < cfgV0.cfgV0DaughPiPtMin) { + return false; + } + + if (hasTOFPion && (track.pt() > cfgCharmCand.tofPIDThreshold)) { + if constexpr (std::experimental::is_detected::value) { + // A pion can belong to either a Lambda/Anti-Lambda decay or a K0s decay. + // We evaluate both applicable hypotheses based on charge sign and pick the best match. + double tofLa = (pid > 0) ? v0.tofNSigmaALaPi() : v0.tofNSigmaLaPi(); + + passTOF = std::abs(tofLa) > cfgV0.cfgV0DaughPIDCutsTOFPi; + } else { + // Fallback to standard track TOF + passTOF = std::abs(track.tofNSigmaPi()) > cfgV0.cfgV0DaughPIDCutsTOFPi; } } - if (std::abs(o2::constants::physics::MassLambda - v0.mAntiLambda()) < cfgV0.cfgHypMassWindow) { - entryHadron(v0.mAntiLambda(), negTrackV0.eta(), negTrackV0.pt() * negTrackV0.sign(), 0, 0, v0.pt()); - entryTrkPID(negTrackV0.tpcNSigmaPr(), negTrackV0.tpcNSigmaKa(), negTrackV0.tpcNSigmaPi(), negTrackV0.tofNSigmaPr(), negTrackV0.tofNSigmaKa(), negTrackV0.tofNSigmaPi()); - if (isSelectedV0Daughter(negTrackV0, kProton) && isSelectedV0Daughter(posTrackV0, kPiPlus)) { - registry.fill(HIST("hV0Lambda"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); - registry.fill(HIST("hV0LambdaRefl"), v0.mLambda(), v0.pt(), posTrackV0.pt()); + if ((std::abs(track.tpcNSigmaPi()) > cfgV0.cfgV0DaughPIDCutsTPCPi) && !passTOF) { + return false; + } + } + return true; + } - registry.fill(HIST("hTPCnSigmaPr"), negTrackV0.pt(), negTrackV0.tpcNSigmaPr()); - if (negTrackV0.hasTOF()) { - registry.fill(HIST("hTOFnSigmaPr"), negTrackV0.pt(), negTrackV0.tofNSigmaPr()); - } - if (passPIDSelection(negTrackV0, trkPIDspecies, pidTPCMax, pidTOFMax, tofPIDThreshold, forceTOF)) { - registry.fill(HIST("hV0LambdaPiKRej"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); - registry.fill(HIST("hV0LambdaReflPiKRej"), v0.mLambda(), v0.pt(), posTrackV0.pt()); + template + float calculateInvMass(T1 const& particle1, T2 const& particle2, float mass1, float mass2) + { + ROOT::Math::PtEtaPhiMVector vec1(particle1.pt(), particle1.eta(), particle1.phi(), mass1); + ROOT::Math::PtEtaPhiMVector vec2(particle2.pt(), particle2.eta(), particle2.phi(), mass2); + ROOT::Math::PtEtaPhiMVector combined = vec1 + vec2; + return combined.mass(); + } - registry.fill(HIST("hTPCnSigmaPrPiKRej"), negTrackV0.pt(), negTrackV0.tpcNSigmaPr()); - if (negTrackV0.hasTOF()) { - registry.fill(HIST("hTOFnSigmaPrPiKRej"), negTrackV0.pt(), negTrackV0.tofNSigmaPr()); - } - } + // Helper function to calculate combined pT + template + float calculateCombinedPt(T1 const& particle1, T2 const& particle2) + { + ROOT::Math::PtEtaPhiMVector vec1(particle1.pt(), particle1.eta(), particle1.phi(), 0.); + ROOT::Math::PtEtaPhiMVector vec2(particle2.pt(), particle2.eta(), particle2.phi(), 0.); + ROOT::Math::PtEtaPhiMVector combined = vec1 + vec2; + return combined.pt(); + } + + template + void fillV0HistogramsWithLc(CollisionType const& collision, V0 const& v0s, TrackType const& tracks, CandsLcType const& candidates, aod::McParticles const* mcParticles = nullptr) + { + + int nTracks = 0; + float efficiencyWeightCand = 1.; + int64_t timeStamp = 0; + bool skipMixedEventTableFilling = false; + float const multiplicityFT0M = collision.multFT0M(); + int gCollisionId = collision.globalIndex(); + if (candidates.size() == 0) { + return; + } + + if (cfgCharmCand.eventFractionToAnalyze > 0) { + if (rnd.Uniform(0, 1) > cfgCharmCand.eventFractionToAnalyze) { + skipMixedEventTableFilling = true; + } + } + + if constexpr (!IsMcRec) { + timeStamp = collision.template bc_as().timestamp(); + } + + poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), multiplicityFT0M)); + + // Count good tracks + if (collision.numContrib() > 1) { + for (const auto& track : tracks) { + if (std::abs(track.eta()) > cfgCharmCand.etaTrackMax || std::abs(track.dcaXY()) > cfgCharmCand.dcaXYTrackMax || std::abs(track.dcaZ()) > cfgCharmCand.dcaZTrackMax) { + continue; } + nTracks++; + } + } + + registry.fill(HIST("hMultiplicityPreSelection"), nTracks); + if (nTracks < cfgCharmCand.multMin || nTracks > cfgCharmCand.multMax) { + return; + } + registry.fill(HIST("hMultiplicity"), nTracks); + + int countCand = 1; + + // Lc-Lambda correlation (same event) + for (const auto& candidate : candidates) { + double yCand = HfHelper::yLc(candidate); + double ptCand = candidate.pt(); + double etaCand = candidate.eta(); + double phiCand = RecoDecay::constrainAngle(candidate.phi(), -PIHalf); + + if ((std::abs(yCand) > cfgCharmCand.yCandMax) || ptCand < cfgCharmCand.ptCandMin || ptCand > cfgCharmCand.ptCandMax) { + continue; + } + + registry.fill(HIST("hY"), yCand); + registry.fill(HIST("hPtCand"), ptCand); + registry.fill(HIST("hEta"), etaCand); + registry.fill(HIST("hPhi"), phiCand); + registry.fill(HIST("hCandBin"), poolBin); + + bool selLcPKPi = candidate.isSelLcToPKPi() >= cfgCharmCand.selectionFlagLc; + bool selLcPiKP = candidate.isSelLcToPiKP() >= cfgCharmCand.selectionFlagLc; + + if (!selLcPKPi && !selLcPiKP) { + continue; } + + if (cfgCharmCand.applyEfficiency) { + efficiencyWeightCand = 1. / cfgCharmCand.efficiencyLc->at(o2::analysis::findBin(cfgCharmCand.binsPtEfficiencyLc, ptCand)); + } + if constexpr (IsMcRec) { - if (!v0.has_mcParticle() || !posTrackV0.has_mcParticle() || !negTrackV0.has_mcParticle()) { + isPrompt = candidate.originMcRec() == RecoDecay::OriginType::Prompt; + isNonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; + isSignal = std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi; + + if (isSignal) { + registry.fill(HIST("hPtCandSig"), ptCand); + registry.fill(HIST("hEtaSig"), etaCand); + registry.fill(HIST("hPhiSig"), phiCand); + registry.fill(HIST("hYSig"), yCand); + } + } + + float massCandPKPi = -999.0; + float massCandPiKP = -999.0; + + if (selLcPKPi) { + massCandPKPi = HfHelper::invMassLcToPKPi(candidate); + const auto& probs = candidate.mlProbLcToPKPi(); + fillMlOutput(probs, outputMlPKPi); + + registry.fill(HIST("hMassLcVsPt"), massCandPKPi, ptCand, efficiencyWeightCand); + registry.fill(HIST("hMassScVsPtVsSign"), massCandPKPi, ptCand, chargeCand, efficiencyWeightCand); + registry.fill(HIST("hMassLcData"), massCandPKPi, efficiencyWeightCand); + registry.fill(HIST("hSelectionStatusLcToPKPi"), selLcPKPi); + if (isPrompt) { + registry.fill(HIST("hPtCandSigPrompt"), ptCand); + registry.fill(HIST("hPtVsMultiplicityMcRecPrompt"), ptCand, multiplicityFT0M); + } else if (isNonPrompt) { + registry.fill(HIST("hPtCandSigNonPrompt"), ptCand); + registry.fill(HIST("hPtVsMultiplicityMcRecNonPrompt"), ptCand, multiplicityFT0M); + } + + entryCandRecoInfo(massCandPKPi, ptCand, outputMlPKPi[0], outputMlPKPi[1], poolBin); + entryCandCandGenInfo(isPrompt); + if (!skipMixedEventTableFilling) { + entryCand(candidate.phi(), etaCand, ptCand, massCandPKPi, poolBin, gCollisionId, timeStamp); + entryCandCharge(chargeCand); + } + } + if (selLcPiKP) { + massCandPiKP = HfHelper::invMassLcToPiKP(candidate); + const auto& probs = candidate.mlProbLcToPiKP(); + fillMlOutput(probs, outputMlPiKP); + registry.fill(HIST("hMassLcVsPt"), massCandPiKP, ptCand, efficiencyWeightCand); + registry.fill(HIST("hMassScVsPtVsSign"), massCandPKPi, ptCand, chargeCand, efficiencyWeightCand); + registry.fill(HIST("hMassLcData"), massCandPiKP, efficiencyWeightCand); + registry.fill(HIST("hSelectionStatusLcToPiKP"), selLcPiKP); + if (isPrompt) { + registry.fill(HIST("hPtCandSigPrompt"), ptCand); + registry.fill(HIST("hPtVsMultiplicityMcRecPrompt"), ptCand, multiplicityFT0M); + } else if (isNonPrompt) { + registry.fill(HIST("hPtCandSigNonPrompt"), ptCand); + registry.fill(HIST("hPtVsMultiplicityMcRecNonPrompt"), ptCand, multiplicityFT0M); + } + entryCandRecoInfo(massCandPiKP, ptCand, outputMlPiKP[0], outputMlPiKP[1], poolBin); + entryCandCandGenInfo(isPrompt); + if (!skipMixedEventTableFilling) { + entryCand(candidate.phi(), etaCand, ptCand, massCandPiKP, poolBin, gCollisionId, timeStamp); + entryCandCharge(chargeCand); + } + } + + auto trackPos1 = candidate.template prong0_as(); + // auto trackPos2 = candidate.template prong2_as(); + chargeCand = trackPos1.sign(); + + registry.fill(HIST("hPtProng0"), candidate.ptProng0()); + registry.fill(HIST("hPtProng1"), candidate.ptProng1()); + registry.fill(HIST("hPtProng2"), candidate.ptProng2()); + + registry.fill(HIST("hCandBin"), poolBin); + + // Correlate Lc with all Lambda V0 in the same event + for (const auto& v0 : v0s) { + + const int v0Lambda = 1; + const int v0AntiLambda = -1; + + if (!selectionV0(collision, v0)) { continue; } - auto v0Mc = v0.mcParticle(); - auto posTrack = posTrackV0.mcParticle(); - auto negTrack = negTrackV0.mcParticle(); - - if (std::abs(v0Mc.pdgCode()) == kLambda0) { - if (std::abs(posTrack.pdgCode()) == kProton) { - registry.fill(HIST("hV0LambdaMcRec"), v0.mLambda(), v0.pt(), posTrackV0.pt()); - registry.fill(HIST("hV0LambdaReflMcRec"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); - - if (passPIDSelection(posTrackV0, trkPIDspecies, pidTPCMax, pidTOFMax, tofPIDThreshold, forceTOF)) { - registry.fill(HIST("hV0LambdaPiKRejMcRec"), v0.mLambda(), v0.pt(), posTrackV0.pt()); - registry.fill(HIST("hV0LambdaReflPiKRejMcRec"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); + + auto posTrackV0 = v0.template posTrack_as(); + auto negTrackV0 = v0.template negTrack_as(); + + if ((candidate.prong0Id() == posTrackV0.globalIndex()) || (candidate.prong1Id() == posTrackV0.globalIndex()) || (candidate.prong2Id() == posTrackV0.globalIndex()) || (candidate.prong0Id() == negTrackV0.globalIndex()) || (candidate.prong1Id() == negTrackV0.globalIndex()) || (candidate.prong2Id() == negTrackV0.globalIndex())) { + if (!cfgCharmCand.storeAutoCorrelationFlag) { + continue; + } + correlationStatus = true; + } + + if (cfgV0.cfgIsCorrCollMatchV0 && ((v0.collisionId() != posTrackV0.collisionId()) || (v0.collisionId() != negTrackV0.collisionId()))) { + continue; + } + + // Process Lambda (proton-pion) + if ((std::abs(o2::constants::physics::MassLambda - v0.mLambda()) < cfgV0.cfgHypMassWindow) && v0.alpha() > 0) { + if (isSelectedV0Daughter(posTrackV0, v0, kProton) && isSelectedV0Daughter(negTrackV0, v0, kPiMinus)) { + + if (selLcPKPi) { + fillCorrelationTable(cfgCharmCand.fillTrkPID, v0, candidate, outputMlPKPi, poolBin, correlationStatus, yCand, chargeCand, 0, massCandPKPi, *mcParticles); + } + if (selLcPiKP) { + fillCorrelationTable(cfgCharmCand.fillTrkPID, v0, candidate, outputMlPiKP, poolBin, correlationStatus, yCand, chargeCand, 0, massCandPiKP, *mcParticles); + } + + if (countCand == 1) { + if (!skipMixedEventTableFilling) { + entryHadron(v0.phi(), v0.eta(), v0.pt() * v0Lambda, poolBin, gCollisionId, timeStamp); + entryV0InvMass(v0.mLambda(), v0.mAntiLambda()); + registry.fill(HIST("hTracksBin"), poolBin); + } } } - if (std::abs(negTrack.pdgCode()) == kProton) { - registry.fill(HIST("hV0LambdaMcRec"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); - registry.fill(HIST("hV0LambdaReflMcRec"), v0.mLambda(), v0.pt(), posTrackV0.pt()); + } + + // Process anti-Lambda (anti-proton-pion) + if ((std::abs(o2::constants::physics::MassLambda - v0.mAntiLambda()) < cfgV0.cfgHypMassWindow) && v0.alpha() < 0) { + if (isSelectedV0Daughter(negTrackV0, v0, kProtonBar) && isSelectedV0Daughter(posTrackV0, v0, kPiPlus)) { - if (passPIDSelection(negTrackV0, trkPIDspecies, pidTPCMax, pidTOFMax, tofPIDThreshold, forceTOF)) { - registry.fill(HIST("hV0LambdaPiKRejMcRec"), v0.mAntiLambda(), v0.pt(), negTrackV0.pt()); - registry.fill(HIST("hV0LambdaReflPiKRejMcRec"), v0.mLambda(), v0.pt(), posTrackV0.pt()); + if (selLcPKPi) { + fillCorrelationTable(cfgCharmCand.fillTrkPID, v0, candidate, outputMlPKPi, poolBin, correlationStatus, yCand, chargeCand, 0, massCandPKPi, *mcParticles); + } + if (selLcPiKP) { + fillCorrelationTable(cfgCharmCand.fillTrkPID, v0, candidate, outputMlPiKP, poolBin, correlationStatus, yCand, chargeCand, 0, massCandPiKP, *mcParticles); + } + if (countCand == 1) { + if (!skipMixedEventTableFilling) { + entryHadron(v0.phi(), v0.eta(), v0.pt() * v0AntiLambda, poolBin, gCollisionId, timeStamp); + entryV0InvMass(v0.mAntiLambda(), v0.mLambda()); + registry.fill(HIST("hTracksBin"), poolBin); + } } } } - } - } + + } // end v0 loop + + countCand++; + } // end outer Lc loop + registry.fill(HIST("hZvtx"), collision.posZ()); + registry.fill(HIST("hMultFT0M"), multiplicityFT0M); } template @@ -693,14 +915,14 @@ struct HfCorrelatorLcScHadrons { } auto gentracks = mcParticles.sliceBy(perTrueCollision, mctrk.mcCollisionId()); for (const auto& track : gentracks) { - if (std::abs(track.eta()) > etaTrackMax || track.pt() < ptTrackMin || track.pt() > ptTrackMax) { + if (std::abs(track.eta()) > cfgCharmCand.etaTrackMax || track.pt() < cfgCharmCand.ptTrackMin || track.pt() > cfgCharmCand.ptTrackMax) { continue; } if ((std::abs(track.pdgCode()) != kElectron) && (std::abs(track.pdgCode()) != kMuonMinus) && (std::abs(track.pdgCode()) != kPiPlus) && (std::abs(track.pdgCode()) != kKPlus) && (std::abs(track.pdgCode()) != kProton)) { continue; } - if (pidTrkApplied && (std::abs(track.pdgCode()) != kProton)) { + if (cfgCharmCand.pidTrkApplied && (std::abs(track.pdgCode()) != kProton)) { continue; // proton PID } @@ -717,43 +939,242 @@ struct HfCorrelatorLcScHadrons { registry.fill(HIST("hPtTracksVsSignGen"), track.pt(), chargeTrack / (std::abs(chargeTrack))); } } - template - void fillCorrelationTable(bool trkPidFill, TrackType const& track, CandType const& candidate, + + // ======================================== + // Efficiency calculation block + // ======================================== + template + void fillEffV0(CollType const& col, + V0 const& v0s, + TrackType const&, + PartType const& mcParticles) + { + int countV0 = 1; + // Data-driven efficiency calculation for protons using Lambda + for (const auto& v0 : v0s) { + bool passV0Sel = selectionV0(col, v0); + + auto const& trackV0Pos = v0.template posTrack_as(); + auto const& trackV0Neg = v0.template negTrack_as(); + if (cfgV0.cfgIsCorrCollMatchV0 && ((v0.collisionId() != trackV0Pos.collisionId()) || (v0.collisionId() != trackV0Neg.collisionId()))) { + continue; + } + + // Process Lambda (proton + pion) + if (passV0Sel && std::abs(o2::constants::physics::MassLambda - v0.mLambda()) < cfgV0.cfgHypMassWindow && v0.alpha() > 0) { + entryHadron(v0.mLambda(), trackV0Pos.eta(), trackV0Pos.pt() * trackV0Pos.sign(), 0, 0, v0.pt()); + entryTrkPID(trackV0Pos.tpcNSigmaPr(), trackV0Pos.tpcNSigmaKa(), trackV0Pos.tpcNSigmaPi(), trackV0Pos.tofNSigmaPr(), trackV0Pos.tofNSigmaKa(), trackV0Pos.tofNSigmaPi()); + + if (isSelectedV0Daughter(trackV0Pos, v0, kProton) && isSelectedV0Daughter(trackV0Neg, v0, kPiMinus)) { + registry.fill(HIST("hV0Lambda"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hV0LambdaRefl"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hTPCnSigmaPr"), trackV0Pos.pt(), trackV0Pos.tpcNSigmaPr()); + + if (trackV0Pos.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPr"), trackV0Pos.pt(), trackV0Pos.tofNSigmaPr()); + } + + if (passPIDSelection(trackV0Pos, cfgCharmCand.trkPIDspecies, cfgCharmCand.pidTPCMax, cfgCharmCand.pidTOFMax, cfgCharmCand.tofPIDThreshold, cfgCharmCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRej"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hV0LambdaReflPiKRej"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hTPCnSigmaPrPiKRej"), trackV0Pos.pt(), trackV0Pos.tpcNSigmaPr()); + if (trackV0Pos.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPrPiKRej"), trackV0Pos.pt(), trackV0Pos.tofNSigmaPr()); + } + } + } + } + + if (passV0Sel && std::abs(o2::constants::physics::MassLambda - v0.mAntiLambda()) < cfgV0.cfgHypMassWindow && v0.alpha() < 0) { + entryHadron(v0.mAntiLambda(), trackV0Neg.eta(), trackV0Neg.pt() * trackV0Neg.sign(), 0, 0, v0.pt()); + entryTrkPID(trackV0Neg.tpcNSigmaPr(), trackV0Neg.tpcNSigmaKa(), trackV0Neg.tpcNSigmaPi(), trackV0Neg.tofNSigmaPr(), trackV0Neg.tofNSigmaKa(), trackV0Neg.tofNSigmaPi()); + + if (isSelectedV0Daughter(trackV0Neg, v0, kProtonBar) && isSelectedV0Daughter(trackV0Pos, v0, kPiPlus)) { + registry.fill(HIST("hV0Lambda"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hV0LambdaRefl"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hTPCnSigmaPr"), trackV0Neg.pt(), trackV0Neg.tpcNSigmaPr()); + + if (trackV0Neg.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPr"), trackV0Neg.pt(), trackV0Neg.tofNSigmaPr()); + } + + if (passPIDSelection(trackV0Neg, cfgCharmCand.trkPIDspecies, cfgCharmCand.pidTPCMax, cfgCharmCand.pidTOFMax, cfgCharmCand.tofPIDThreshold, cfgCharmCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRej"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hV0LambdaReflPiKRej"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hTPCnSigmaPrPiKRej"), trackV0Neg.pt(), trackV0Neg.tpcNSigmaPr()); + if (trackV0Neg.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPrPiKRej"), trackV0Neg.pt(), trackV0Neg.tofNSigmaPr()); + } + } + } + } + + // MC-Reco specific V0 matching + if constexpr (IsMc) { + if (!v0.has_mcParticle() || !trackV0Pos.has_mcParticle() || !trackV0Neg.has_mcParticle()) { + continue; + } + auto const& v0Mc = v0.mcParticle(); + auto const& partV0Pos = trackV0Pos.mcParticle(); + auto const& partV0Neg = trackV0Neg.mcParticle(); + + if (passV0Sel && v0Mc.pdgCode() == kLambda0 && v0.alpha() > 0) { + if (isSelectedV0Daughter(trackV0Pos, v0, kProton) && isSelectedV0Daughter(trackV0Neg, v0, kPiMinus)) { + registry.fill(HIST("hV0LambdaMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + registry.fill(HIST("hV0LambdaReflMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + if (cfgV0.calEffV0 && v0Mc.isPhysicalPrimary() && v0Mc.producedByGenerator()) { + registry.fill(HIST("hV0PrimLambdaMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + registry.fill(HIST("hV0PrimLambdaReflMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + } + + if (passPIDSelection(trackV0Pos, cfgCharmCand.trkPIDspecies, cfgCharmCand.pidTPCMax, cfgCharmCand.pidTOFMax, cfgCharmCand.tofPIDThreshold, cfgCharmCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRejMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + registry.fill(HIST("hV0LambdaReflPiKRejMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + } + } + } + if (passV0Sel && v0Mc.pdgCode() == kLambda0Bar && v0.alpha() < 0) { + if (isSelectedV0Daughter(trackV0Neg, v0, kProtonBar) && isSelectedV0Daughter(trackV0Pos, v0, kPiPlus)) { + registry.fill(HIST("hV0LambdaMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + registry.fill(HIST("hV0LambdaReflMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + + if (cfgV0.calEffV0 && v0Mc.isPhysicalPrimary() && v0Mc.producedByGenerator()) { + registry.fill(HIST("hV0PrimLambdaMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + registry.fill(HIST("hV0PrimLambdaReflMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + } + if (passPIDSelection(trackV0Neg, cfgCharmCand.trkPIDspecies, cfgCharmCand.pidTPCMax, cfgCharmCand.pidTOFMax, cfgCharmCand.tofPIDThreshold, cfgCharmCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRejMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + registry.fill(HIST("hV0LambdaReflPiKRejMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + } + } + } + if (cfgV0.calEffV0 && countV0 == 1) { + auto genPart = mcParticles.sliceBy(perTrueCollision, v0Mc.mcCollisionId()); + + for (const auto& particle : genPart) { + + if (std::abs(particle.pdgCode()) != kLambda0) { + continue; + } + + if (std::abs(particle.y()) > cfgCharmCand.yCandMax) { + continue; + } + if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) { + continue; + } + + auto daughterParts = particle.template daughters_as(); + const int8_t nDaughtersV0 = 2; + + if (daughterParts.size() != nDaughtersV0) { + continue; + } + + int8_t countPassedDaughter = 0; + for (const auto& currentDaughter : daughterParts) { + + if (std::abs(currentDaughter.eta()) > cfgCharmCand.etaTrackMax) { + continue; + } + + if (std::abs(currentDaughter.pdgCode()) == kProton) { + + if (currentDaughter.pt() > cfgV0.cfgV0DaughPrPtMax || currentDaughter.pt() < cfgV0.cfgV0DaughPrPtMin) { + continue; + } + + } else if (std::abs(currentDaughter.pdgCode()) == kPiPlus) { + if (currentDaughter.pt() > cfgV0.cfgV0DaughPiPtMax || currentDaughter.pt() < cfgV0.cfgV0DaughPiPtMin) { + continue; + } + + } else { + continue; + } + countPassedDaughter++; + } + if (countPassedDaughter == nDaughtersV0) { + registry.fill(HIST("hV0PtPrimLambdaMcGen"), particle.pt()); + } + } + } + countV0++; + } + } + } + + template + void fillCorrelationTable(bool trkPidFill, AssocType const& assoc, CandType const& candidate, const std::vector& outMl, int binPool, int8_t correlStatus, - double yCand, int signCand, float ptLcFromSc, McPart const& mcParticles) + double yCand, int signCand, float ptLcFromSc, double massCand, McPart const& mcParticles) { bool isPhysicalPrimary = false; int trackOrigin = -1; float const cent = 100.0; // will be updated later + double massCandHadron = -999.0; + double ptCombined = -999.0; + double yAssoc = -999.0; + int signAssoc = 0; + + if constexpr (LambdaPart == 1) { + massCandHadron = calculateInvMass(candidate, assoc, massCand, assoc.mLambda()); + entryPairedV0InvMass(assoc.mLambda(), assoc.mAntiLambda()); + signAssoc = 1; + yAssoc = assoc.yLambda(); + } else if constexpr (LambdaPart == -1) { + massCandHadron = calculateInvMass(candidate, assoc, massCand, assoc.mAntiLambda()); + entryPairedV0InvMass(assoc.mAntiLambda(), assoc.mLambda()); + signAssoc = -1; // Note: Ensure signAssoc vs assoc.signAssoc is consistent + yAssoc = assoc.yLambda(); + } else { + // The final 'else' does not need the 'constexpr' keyword; + // it is automatically part of the constexpr logic. + massCandHadron = calculateInvMass(candidate, assoc, massCand, MassProton); + signAssoc = assoc.sign(); + yAssoc = assoc.rapidity(MassProton); + } + + ptCombined = calculateCombinedPt(candidate, assoc); - entryCandHadronPair(getDeltaPhi(track.phi(), candidate.phi()), - track.eta() - candidate.eta(), + registry.fill(HIST("hLcHadronInvMassVsPt"), massCandHadron, ptCombined); + registry.fill(HIST("hLcHadronInvMass"), massCandHadron); + registry.fill(HIST("hLcHadronPtCombined"), ptCombined); + + entryCandHadronPair(getDeltaPhi(assoc.phi(), candidate.phi()), + assoc.eta() - candidate.eta(), candidate.pt(), - track.pt() * track.sign(), + assoc.pt() * signAssoc, binPool, correlStatus, cent); - entryCandHadronPairY(track.rapidity(MassProton) - yCand); + entryCandHadronPairY(yAssoc - yCand); entryCandHadronMlInfo(outMl[0], outMl[1]); entryPtLcFromScPair(ptLcFromSc); - entryTrackRecoInfo(track.dcaXY(), track.dcaZ(), track.tpcNClsCrossedRows()); + if constexpr (LambdaPart == 0) { + entryTrackRecoInfo(assoc.dcaXY(), assoc.dcaZ(), assoc.tpcNClsCrossedRows()); + } + entryCandHadronRecoInfo(massCand, false); + entryLcHadronInvMass(massCandHadron, ptCombined); entryPairCandCharge(signCand); - if (trkPidFill) { - entryCandHadronPairTrkPID(track.tpcNSigmaPr(), track.tpcNSigmaKa(), track.tpcNSigmaPi(), track.tofNSigmaPr(), track.tofNSigmaKa(), track.tofNSigmaPi()); + if constexpr (LambdaPart == 0) { + if (trkPidFill) { + entryCandHadronPairTrkPID(assoc.tpcNSigmaPr(), assoc.tpcNSigmaKa(), assoc.tpcNSigmaPi(), assoc.tofNSigmaPr(), assoc.tofNSigmaKa(), assoc.tofNSigmaPi()); + } } if constexpr (IsMcRec) { - if (track.has_mcParticle()) { - auto mcParticle = track.template mcParticle_as(); + if (assoc.has_mcParticle()) { + auto mcParticle = assoc.template mcParticle_as(); isPhysicalPrimary = mcParticle.isPhysicalPrimary(); trackOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, mcParticle, true); entryCandHadronGenInfo(isPrompt, isPhysicalPrimary, trackOrigin); } else { entryCandHadronGenInfo(isPrompt, false, 0); - registry.fill(HIST("hFakeTracksMcRec"), track.pt()); + registry.fill(HIST("hFakeTracksMcRec"), assoc.pt()); } - registry.fill(HIST("hPtParticleAssocVsCandMcRec"), track.pt(), candidate.pt()); + registry.fill(HIST("hPtParticleAssocVsCandMcRec"), assoc.pt(), candidate.pt()); if (isPhysicalPrimary) { - registry.fill(HIST("hPtPrimaryParticleAssocVsCandMcRec"), track.pt(), candidate.pt()); + registry.fill(HIST("hPtPrimaryParticleAssocVsCandMcRec"), assoc.pt(), candidate.pt()); } } } @@ -774,8 +1195,8 @@ struct HfCorrelatorLcScHadrons { return; } - if (eventFractionToAnalyze > 0) { - if (rnd->Uniform(0, 1) > eventFractionToAnalyze) { + if (cfgCharmCand.eventFractionToAnalyze > 0) { + if (rnd.Uniform(0, 1) > cfgCharmCand.eventFractionToAnalyze) { skipMixedEventTableFilling = true; } } @@ -785,14 +1206,14 @@ struct HfCorrelatorLcScHadrons { } poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), multiplicityFT0M)); - if (correlateLcWithLeadingParticle) { - leadingIndex = findLeadingParticle(tracks, etaTrackMax.value); + if (cfgCharmCand.correlateLcWithLeadingParticle) { + leadingIndex = findLeadingParticle(tracks, cfgCharmCand.etaTrackMax.value); } // Count good tracks if (collision.numContrib() > 1) { for (const auto& track : tracks) { - if (std::abs(track.eta()) > etaTrackMax || std::abs(track.dcaXY()) > dcaXYTrackMax || std::abs(track.dcaZ()) > dcaZTrackMax) { + if (std::abs(track.eta()) > cfgCharmCand.etaTrackMax || std::abs(track.dcaXY()) > cfgCharmCand.dcaXYTrackMax || std::abs(track.dcaZ()) > cfgCharmCand.dcaZTrackMax) { continue; } nTracks++; @@ -800,7 +1221,7 @@ struct HfCorrelatorLcScHadrons { } registry.fill(HIST("hMultiplicityPreSelection"), nTracks); - if (nTracks < multMin || nTracks > multMax) { + if (nTracks < cfgCharmCand.multMin || nTracks > cfgCharmCand.multMax) { return; } registry.fill(HIST("hMultiplicity"), nTracks); @@ -811,20 +1232,25 @@ struct HfCorrelatorLcScHadrons { double efficiencyWeightCand = 1.; double yCand = -999.0; double etaCand = -999.0; - double ptScProng0 = -999.0; + double ptScProng0 = 0.0; + double ptSoftPi = -999.0; + double massPKPiLcFromSc = -999.0; + double massPiKPLcFromSc = -999.0; double ptCand = -999.0; double phiCand = -999.0; double massCandPKPi = -999.0; double massCandPiKP = -999.0; bool selLcPKPi = false; bool selLcPiKP = false; + float softPiAbsDcaXY = -999.0; + float softPiAbsDcaZ = -999.0; yCand = estimateY(candidate); etaCand = candidate.eta(); ptCand = candidate.pt(); phiCand = RecoDecay::constrainAngle(candidate.phi(), -PIHalf); - if ((std::abs(yCand) > yCandMax) || ptCand < ptCandMin || ptCand > ptCandMax) { + if ((std::abs(yCand) > cfgCharmCand.yCandMax) || ptCand < cfgCharmCand.ptCandMin || ptCand > cfgCharmCand.ptCandMax) { continue; } @@ -834,8 +1260,8 @@ struct HfCorrelatorLcScHadrons { registry.fill(HIST("hPhi"), phiCand); registry.fill(HIST("hCandBin"), poolBin); - if (applyEfficiency) { - efficiencyWeightCand = 1. / efficiencyLc->at(o2::analysis::findBin(binsPtEfficiencyLc, ptCand)); + if (cfgCharmCand.applyEfficiency) { + efficiencyWeightCand = 1. / cfgCharmCand.efficiencyLc->at(o2::analysis::findBin(cfgCharmCand.binsPtEfficiencyLc, ptCand)); } if constexpr (IsMcRec) { @@ -847,17 +1273,21 @@ struct HfCorrelatorLcScHadrons { chargeCand = candidate.charge(); const auto& candidateLc = candidate.template prongLc_as(); ptScProng0 = candidateLc.pt(); - selLcPKPi = (candidateLc.isSelLcToPKPi() >= selectionFlagLc) && (candidate.statusSpreadLcMinvPKPiFromPDG()); - selLcPiKP = (candidateLc.isSelLcToPiKP() >= selectionFlagLc) && (candidate.statusSpreadLcMinvPiKPFromPDG()); + softPiAbsDcaXY = std::abs(candidate.softPiDcaXY()); + softPiAbsDcaZ = std::abs(candidate.softPiDcaZ()); + selLcPKPi = (candidateLc.isSelLcToPKPi() >= cfgCharmCand.selectionFlagLc) && (candidate.statusSpreadLcMinvPKPiFromPDG()); + selLcPiKP = (candidateLc.isSelLcToPiKP() >= cfgCharmCand.selectionFlagLc) && (candidate.statusSpreadLcMinvPiKPFromPDG()); if (selLcPKPi) { const auto& probs = candidateLc.mlProbLcToPKPi(); fillMlOutput(probs, outputMlPKPi); - massCandPKPi = std::abs(HfHelper::invMassScRecoLcToPKPi(candidate, candidateLc) - HfHelper::invMassLcToPKPi(candidateLc)); + massPKPiLcFromSc = HfHelper::invMassLcToPKPi(candidateLc); + massCandPKPi = std::abs(HfHelper::invMassScRecoLcToPKPi(candidate, candidateLc) - massPKPiLcFromSc); } if (selLcPiKP) { const auto& probs = candidateLc.mlProbLcToPiKP(); fillMlOutput(probs, outputMlPiKP); - massCandPiKP = std::abs(HfHelper::invMassScRecoLcToPiKP(candidate, candidateLc) - HfHelper::invMassLcToPiKP(candidateLc)); + massPiKPLcFromSc = HfHelper::invMassLcToPiKP(candidateLc); + massCandPiKP = std::abs(HfHelper::invMassScRecoLcToPiKP(candidate, candidateLc) - massPiKPLcFromSc); } if constexpr (IsMcRec) { // isSignal = @@ -870,12 +1300,14 @@ struct HfCorrelatorLcScHadrons { auto trackPos1 = candidateLc.template prong0_as(); auto trackPos2 = candidateLc.template prong2_as(); signSoftPion = candidate.template prong1_as().sign(); - if (calTrkEff && countCand == 1 && (isSignal || !calEffEventWithCand)) { + ptSoftPi = candidate.template prong1_as().pt(); + if (cfgCharmCand.calTrkEff && countCand == 1 && (isSignal || !cfgCharmCand.calEffEventWithCand)) { calculateTrkEff(trackPos1, trackPos2, *mcParticles); } registry.fill(HIST("hPtProng1"), candidate.template prong1_as().pt()); } else { signSoftPion = candidate.template prong1_as().sign(); + ptSoftPi = candidate.template prong1_as().pt(); registry.fill(HIST("hPtProng1"), candidate.prong1().pt()); } registry.fill(HIST("hPtProng0"), ptScProng0); @@ -885,8 +1317,8 @@ struct HfCorrelatorLcScHadrons { } } else { - selLcPKPi = candidate.isSelLcToPKPi() >= selectionFlagLc; - selLcPiKP = candidate.isSelLcToPiKP() >= selectionFlagLc; + selLcPKPi = candidate.isSelLcToPKPi() >= cfgCharmCand.selectionFlagLc; + selLcPiKP = candidate.isSelLcToPiKP() >= cfgCharmCand.selectionFlagLc; if (selLcPKPi) { const auto& probs = candidate.mlProbLcToPKPi(); fillMlOutput(probs, outputMlPKPi); @@ -902,7 +1334,7 @@ struct HfCorrelatorLcScHadrons { chargeCand = trackPos1.sign(); if constexpr (IsMcRec) { isSignal = std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi; - if (calTrkEff && countCand == 1 && (isSignal || !calEffEventWithCand)) { + if (cfgCharmCand.calTrkEff && countCand == 1 && (isSignal || !cfgCharmCand.calEffEventWithCand)) { calculateTrkEff(trackPos1, trackPos2, *mcParticles); } } @@ -913,7 +1345,7 @@ struct HfCorrelatorLcScHadrons { if (isSignal) { registry.fill(HIST("hPtCandSig"), ptCand); - registry.fill(HIST("hEtaSig"), ptCand); + registry.fill(HIST("hEtaSig"), etaCand); registry.fill(HIST("hPhiSig"), phiCand); registry.fill(HIST("hYSig"), yCand); } @@ -931,12 +1363,13 @@ struct HfCorrelatorLcScHadrons { registry.fill(HIST("hPtVsMultiplicityMcRecNonPrompt"), ptCand, multiplicityFT0M); } - entryCandCandRecoInfo(massCandPKPi, ptCand, outputMlPKPi[0], outputMlPKPi[1], poolBin); + entryCandRecoInfo(massCandPKPi, ptCand, outputMlPKPi[0], outputMlPKPi[1], poolBin); entryCandCandGenInfo(isPrompt); if (!skipMixedEventTableFilling) { entryCand(candidate.phi(), etaCand, ptCand, massCandPKPi, poolBin, gCollisionId, timeStamp); entryCandCharge(chargeCand); entryPtLcFromSc(ptScProng0); + entryScRecoInfoExt(massPKPiLcFromSc, ptSoftPi, softPiAbsDcaXY, softPiAbsDcaZ); } } @@ -952,12 +1385,13 @@ struct HfCorrelatorLcScHadrons { registry.fill(HIST("hPtCandSigNonPrompt"), ptCand); registry.fill(HIST("hPtVsMultiplicityMcRecNonPrompt"), ptCand, multiplicityFT0M); } - entryCandCandRecoInfo(massCandPiKP, ptCand, outputMlPiKP[0], outputMlPiKP[1], poolBin); + entryCandRecoInfo(massCandPiKP, ptCand, outputMlPiKP[0], outputMlPiKP[1], poolBin); entryCandCandGenInfo(isPrompt); if (!skipMixedEventTableFilling) { entryCand(candidate.phi(), etaCand, ptCand, massCandPiKP, poolBin, gCollisionId, timeStamp); entryCandCharge(chargeCand); entryPtLcFromSc(ptScProng0); + entryScRecoInfoExt(massPiKPLcFromSc, ptSoftPi, softPiAbsDcaXY, softPiAbsDcaZ); } } @@ -967,7 +1401,7 @@ struct HfCorrelatorLcScHadrons { // Remove Lc daughters by checking track indices if constexpr (!IsCandSc) { if ((candidate.prong0Id() == track.globalIndex()) || (candidate.prong1Id() == track.globalIndex()) || (candidate.prong2Id() == track.globalIndex())) { - if (!storeAutoCorrelationFlag) { + if (!cfgCharmCand.storeAutoCorrelationFlag) { continue; } correlationStatus = true; @@ -975,7 +1409,7 @@ struct HfCorrelatorLcScHadrons { } else { const auto& candidateLc = candidate.template prongLc_as(); if ((candidateLc.prong0Id() == track.globalIndex()) || (candidateLc.prong1Id() == track.globalIndex()) || (candidateLc.prong2Id() == track.globalIndex()) || (candidate.prong1Id() == track.globalIndex())) { - if (!storeAutoCorrelationFlag) { + if (!cfgCharmCand.storeAutoCorrelationFlag) { continue; } correlationStatus = true; @@ -984,20 +1418,20 @@ struct HfCorrelatorLcScHadrons { if (!track.isGlobalTrackWoDCA()) { continue; } - if (pidTrkApplied) { - if (!passPIDSelection(track, trkPIDspecies, pidTPCMax, pidTOFMax, tofPIDThreshold, forceTOF)) { + if (cfgCharmCand.pidTrkApplied) { + if (!passPIDSelection(track, cfgCharmCand.trkPIDspecies, cfgCharmCand.pidTPCMax, cfgCharmCand.pidTOFMax, cfgCharmCand.tofPIDThreshold, cfgCharmCand.forceTOF)) { continue; } } - if (correlateLcWithLeadingParticle) { + if (cfgCharmCand.correlateLcWithLeadingParticle) { if (track.globalIndex() != leadingIndex) { continue; } } if constexpr (IsMcRec) { - if (calTrkEff && countCand == 1 && (isSignal || !calEffEventWithCand) && track.has_mcParticle()) { + if (cfgCharmCand.calTrkEff && countCand == 1 && (isSignal || !cfgCharmCand.calEffEventWithCand) && track.has_mcParticle()) { auto mcParticle = track.template mcParticle_as(); - if (!mcParticle.isPhysicalPrimary() && isRecTrkPhyPrimary) { + if (!mcParticle.isPhysicalPrimary() && cfgCharmCand.isRecTrkPhyPrimary) { continue; } @@ -1014,18 +1448,18 @@ struct HfCorrelatorLcScHadrons { } if (selLcPKPi) { - fillCorrelationTable(fillTrkPID, track, candidate, outputMlPKPi, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, *mcParticles); - entryCandHadronRecoInfo(massCandPKPi, false); + fillCorrelationTable(cfgCharmCand.fillTrkPID, track, candidate, outputMlPKPi, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, massCandPKPi, *mcParticles); + entryScHadronRecoInfoExt(massPKPiLcFromSc, ptSoftPi, softPiAbsDcaXY, softPiAbsDcaZ); } if (selLcPiKP) { - fillCorrelationTable(fillTrkPID, track, candidate, outputMlPiKP, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, *mcParticles); - entryCandHadronRecoInfo(massCandPiKP, false); + fillCorrelationTable(cfgCharmCand.fillTrkPID, track, candidate, outputMlPiKP, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, massCandPiKP, *mcParticles); + entryScHadronRecoInfoExt(massPiKPLcFromSc, ptSoftPi, softPiAbsDcaXY, softPiAbsDcaZ); } if (countCand == 1) { if (!skipMixedEventTableFilling) { entryHadron(track.phi(), track.eta(), track.pt() * track.sign(), poolBin, gCollisionId, timeStamp); - if (fillTrkPID) { + if (cfgCharmCand.fillTrkPID) { entryTrkPID(track.tpcNSigmaPr(), track.tpcNSigmaKa(), track.tpcNSigmaPi(), track.tofNSigmaPr(), track.tofNSigmaKa(), track.tofNSigmaPi()); } registry.fill(HIST("hTracksBin"), poolBin); @@ -1059,7 +1493,7 @@ struct HfCorrelatorLcScHadrons { bool selLcPiKP = false; auto tracksTuple = std::make_tuple(candidates, tracks); - Pair const pairData{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair const pairData{corrBinning, cfgCharmCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairData) { poolBin = corrBinning.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); @@ -1082,8 +1516,8 @@ struct HfCorrelatorLcScHadrons { chargeCand = candidate.charge(); ptScProng0 = candidateLc.pt(); - selLcPKPi = (candidateLc.isSelLcToPKPi() >= selectionFlagLc) && (candidate.statusSpreadLcMinvPKPiFromPDG()); - selLcPiKP = (candidateLc.isSelLcToPiKP() >= selectionFlagLc) && (candidate.statusSpreadLcMinvPiKPFromPDG()); + selLcPKPi = (candidateLc.isSelLcToPKPi() >= cfgCharmCand.selectionFlagLc) && (candidate.statusSpreadLcMinvPKPiFromPDG()); + selLcPiKP = (candidateLc.isSelLcToPiKP() >= cfgCharmCand.selectionFlagLc) && (candidate.statusSpreadLcMinvPiKPFromPDG()); if (selLcPKPi) { const auto& probs = candidateLc.mlProbLcToPKPi(); fillMlOutput(probs, outputMlPKPi); @@ -1106,8 +1540,8 @@ struct HfCorrelatorLcScHadrons { chargeCand = (signSoftPion < ChargeZero) ? AssignedChargeSc0 : -AssignedChargeSc0; // to distingush sc0 from anti-sc0, charge set to +1 and -1 } } else { - selLcPKPi = candidate.isSelLcToPKPi() >= selectionFlagLc; - selLcPiKP = candidate.isSelLcToPiKP() >= selectionFlagLc; + selLcPKPi = candidate.isSelLcToPKPi() >= cfgCharmCand.selectionFlagLc; + selLcPiKP = candidate.isSelLcToPiKP() >= cfgCharmCand.selectionFlagLc; if (selLcPKPi) { const auto& probs = candidate.mlProbLcToPKPi(); fillMlOutput(probs, outputMlPKPi); @@ -1125,20 +1559,18 @@ struct HfCorrelatorLcScHadrons { } } - if (!assocParticle.isGlobalTrackWoDCA() || std::abs(yCand) > yCandMax) { + if (!assocParticle.isGlobalTrackWoDCA() || std::abs(yCand) > cfgCharmCand.yCandMax) { continue; } - if (pidTrkApplied) { - if (!passPIDSelection(assocParticle, trkPIDspecies, pidTPCMax, pidTOFMax, tofPIDThreshold, forceTOF)) { + if (cfgCharmCand.pidTrkApplied) { + if (!passPIDSelection(assocParticle, cfgCharmCand.trkPIDspecies, cfgCharmCand.pidTPCMax, cfgCharmCand.pidTOFMax, cfgCharmCand.tofPIDThreshold, cfgCharmCand.forceTOF)) { continue; } } if (selLcPKPi) { - fillCorrelationTable(fillTrkPID, assocParticle, candidate, outputMlPKPi, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, *mcParticles); - entryCandHadronRecoInfo(massCandPKPi, false); - + fillCorrelationTable(cfgCharmCand.fillTrkPID, assocParticle, candidate, outputMlPKPi, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, massCandPKPi, *mcParticles); if (isPrompt) { registry.fill(HIST("hPtCandMcRecSigPrompt"), ptCand); registry.fill(HIST("hPtVsMultiplicityMcRecPrompt"), ptCand, 0); @@ -1149,8 +1581,7 @@ struct HfCorrelatorLcScHadrons { } if (selLcPiKP) { - fillCorrelationTable(fillTrkPID, assocParticle, candidate, outputMlPiKP, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, *mcParticles); - entryCandHadronRecoInfo(massCandPiKP, false); + fillCorrelationTable(cfgCharmCand.fillTrkPID, assocParticle, candidate, outputMlPiKP, poolBin, correlationStatus, yCand, chargeCand, ptScProng0, massCandPiKP, *mcParticles); if (isPrompt) { registry.fill(HIST("hPtCandMcRecSigPrompt"), ptCand); @@ -1172,13 +1603,13 @@ struct HfCorrelatorLcScHadrons { static constexpr std::size_t PDGChargeScale{3u}; registry.fill(HIST("hMcEvtCount"), 0); - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); // find leading particle - if (correlateLcWithLeadingParticle) { - leadingIndex = findLeadingParticleMcGen(mcParticles, etaTrackMax.value, ptTrackMin.value); + if (cfgCharmCand.correlateLcWithLeadingParticle) { + leadingIndex = findLeadingParticleMcGen(mcParticles, cfgCharmCand.etaTrackMax.value, cfgCharmCand.ptTrackMin.value); } // Mc Gen level for (const auto& particle : mcParticles) { @@ -1190,7 +1621,7 @@ struct HfCorrelatorLcScHadrons { } double const yCand = RecoDecay::y(particle.pVector(), massCand); - if (std::abs(yCand) > yCandGenMax || particle.pt() < ptCandMin) { + if (std::abs(yCand) > cfgCharmCand.yCandGenMax || particle.pt() < cfgCharmCand.ptCandMin) { continue; } registry.fill(HIST("hCandBin"), poolBin); @@ -1199,7 +1630,7 @@ struct HfCorrelatorLcScHadrons { registry.fill(HIST("hPhiMcGen"), RecoDecay::constrainAngle(particle.phi(), -PIHalf)); registry.fill(HIST("hYMcGen"), yCand); - int8_t chargeCand = pdg->GetParticle(particle.pdgCode())->Charge() / PDGChargeScale; // Retrieve charge + auto chargeCand = pdg->GetParticle(particle.pdgCode())->Charge() / PDGChargeScale; // Retrieve charge if (chargeCand == ChargeZero) { chargeCand = (particle.pdgCode() > ChargeZero) ? AssignedChargeSc0 : -AssignedChargeSc0; // to distingush sc0 from anti-sc0, charge set to +1 and -1 } @@ -1248,12 +1679,12 @@ struct HfCorrelatorLcScHadrons { // if it's a Lc particle, search for Hadron and evalutate correlations registry.fill(HIST("hcountCandtriggersMcGen"), 0, particle.pt()); // to count trigger Lc for normalisation for (const auto& particleAssoc : mcParticles) { - if (std::abs(particleAssoc.eta()) > etaTrackMax || particleAssoc.pt() < ptTrackMin || particleAssoc.pt() > ptTrackMax) { + if (std::abs(particleAssoc.eta()) > cfgCharmCand.etaTrackMax || particleAssoc.pt() < cfgCharmCand.ptTrackMin || particleAssoc.pt() > cfgCharmCand.ptTrackMax) { continue; } if (std::find(prongsId.begin(), prongsId.end(), particleAssoc.globalIndex()) != prongsId.end()) { - if (!storeAutoCorrelationFlag) { + if (!cfgCharmCand.storeAutoCorrelationFlag) { continue; } correlationStatus = true; @@ -1263,7 +1694,7 @@ struct HfCorrelatorLcScHadrons { continue; } - if (pidTrkApplied && (std::abs(particleAssoc.pdgCode()) != kProton)) { + if (cfgCharmCand.pidTrkApplied && (std::abs(particleAssoc.pdgCode()) != kProton)) { continue; // proton PID } @@ -1271,7 +1702,7 @@ struct HfCorrelatorLcScHadrons { continue; } - if (correlateLcWithLeadingParticle) { + if (cfgCharmCand.correlateLcWithLeadingParticle) { if (particleAssoc.globalIndex() != leadingIndex) { continue; } @@ -1403,9 +1834,9 @@ struct HfCorrelatorLcScHadrons { void processMcGenMixedEvent(SelCollisionsMc const& collisions, CandidatesLcMcGen const& mcParticles) { - BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}, true}; + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; auto tracksTuple = std::make_tuple(mcParticles, mcParticles); - Pair const pairMcGen{corrBinningMcGen, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair const pairMcGen{corrBinningMcGen, cfgCharmCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { poolBin = corrBinningMcGen.getBin(std::make_tuple(c1.posZ(), c1.multMCFT0A())); for (const auto& [candidate, particleAssoc] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { @@ -1413,10 +1844,10 @@ struct HfCorrelatorLcScHadrons { continue; } double const yL = RecoDecay::y(candidate.pVector(), MassLambdaCPlus); - if (std::abs(yL) > yCandGenMax || candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { + if (std::abs(yL) > cfgCharmCand.yCandGenMax || candidate.pt() < cfgCharmCand.ptCandMin || candidate.pt() > cfgCharmCand.ptCandMax) { continue; } - if (std::abs(particleAssoc.eta()) > etaTrackMax || particleAssoc.pt() < ptTrackMin || particleAssoc.pt() > ptTrackMax) { + if (std::abs(particleAssoc.eta()) > cfgCharmCand.etaTrackMax || particleAssoc.pt() < cfgCharmCand.ptTrackMin || particleAssoc.pt() > cfgCharmCand.ptTrackMax) { continue; } if ((std::abs(particleAssoc.pdgCode()) != kElectron) && (std::abs(particleAssoc.pdgCode()) != kMuonMinus) && (std::abs(particleAssoc.pdgCode()) != kPiPlus) && (std::abs(particleAssoc.pdgCode()) != kKPlus) && (std::abs(particleAssoc.pdgCode()) != kProton)) { @@ -1425,11 +1856,11 @@ struct HfCorrelatorLcScHadrons { if (!particleAssoc.isPhysicalPrimary()) { continue; } - if (pidTrkApplied && (std::abs(particleAssoc.pdgCode()) != kProton)) { + if (cfgCharmCand.pidTrkApplied && (std::abs(particleAssoc.pdgCode()) != kProton)) { continue; // proton PID } - int8_t const chargeLc = pdg->GetParticle(candidate.pdgCode())->Charge(); // Retrieve charge - int8_t const chargeAssoc = pdg->GetParticle(particleAssoc.pdgCode())->Charge(); // Retrieve charge + auto const chargeLc = pdg->GetParticle(candidate.pdgCode())->Charge(); // Retrieve charge + auto const chargeAssoc = pdg->GetParticle(particleAssoc.pdgCode())->Charge(); // Retrieve charge float cent = 100.0; // will be updated later int trackOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, particleAssoc, true); @@ -1449,23 +1880,39 @@ struct HfCorrelatorLcScHadrons { } PROCESS_SWITCH(HfCorrelatorLcScHadrons, processMcGenMixedEvent, "Process Mixed Event McGen", false); - void processDataLambdaV0(SelCollisions::iterator const&, - TracksData const& tracks, aod::V0Datas const& v0s) + // Updated processDataLambdaV0 to include Lc candidates + void processDataLambdaV0(SelCollisions::iterator const& collision, + TracksData const& tracks, + soa::Join const& v0s, + CandsLcDataFiltered const& candidates, + aod::BCsWithTimestamps const&) + { + fillV0HistogramsWithLc(collision, v0s, tracks, candidates); + } + PROCESS_SWITCH(HfCorrelatorLcScHadrons, processDataLambdaV0, "Data process for v0 lambda with Lc", true); + + void processMcLambdaV0(SelCollisions::iterator const& collision, + TracksWithMc const& tracks, + soa::Join const& v0s, + CandsLcMcRecFiltered const& candidates, + aod::McParticles const& mcParticles) { - fillV0Histograms(v0s, tracks); + fillV0HistogramsWithLc(collision, v0s, tracks, candidates, &mcParticles); } - PROCESS_SWITCH(HfCorrelatorLcScHadrons, processDataLambdaV0, "Data process for v0 lambda", false); + PROCESS_SWITCH(HfCorrelatorLcScHadrons, processMcLambdaV0, "Mc process for v0 lambda with Lc", false); - void processMcLambdaV0(SelCollisions::iterator const&, - TracksWithMc const& tracks, soa::Join const& v0s, aod::McParticles const&) + void processLambda0EffCal(SelCollisions::iterator const& collision, + TracksWithMc const& tracks, + soa::Join const& v0s, + aod::McParticles const& mcParticles) { - fillV0Histograms(v0s, tracks); + fillEffV0(collision, v0s, tracks, mcParticles); } - PROCESS_SWITCH(HfCorrelatorLcScHadrons, processMcLambdaV0, "Mc process for v0 lambda", false); + PROCESS_SWITCH(HfCorrelatorLcScHadrons, processLambda0EffCal, "Mc process for lambda0", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context), + adaptAnalysisTask(context)}; } diff --git a/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx b/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx new file mode 100644 index 00000000000..5c0d11af9d5 --- /dev/null +++ b/PWGHF/HFC/TableProducer/correlatorXicHadrons.cxx @@ -0,0 +1,1937 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file correlatorXicHadrons.cxx +/// \brief Xic-Hadrons correlator task - data-like, Mc-Reco and Mc-Gen analyses +/// \author Ravindra Singh + +#include "PWGHF/Core/DecayChannelsLegacy.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/HFC/DataModel/CorrelationTables.h" +#include "PWGHF/HFC/Utils/utilsCorrelations.h" +#include "PWGHF/Utils/utilsAnalysis.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::analysis; +using namespace o2::constants::physics; +using namespace o2::constants::math; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::analysis::hf_correlations; + +// ============================================================================ +// ENUMS FOR MAGIC NUMBERS - REPLACES MAGIC NUMBERS WITH TYPED CONSTANTS +// ============================================================================ + +/// Enum for particle types (particle vs antiparticle) +enum ParticleType : int8_t { + Particle = 1, + AntiParticle = -1 +}; + +/// Enum for V0 lambda types +enum V0LambdaType : int8_t { + NotLambda = 0, + Lambda = 1, + AntiLambda = -1 +}; + +/// Enum for decay daughters count +enum XicDecayDaughtersCount : size_t { + Xic0DaughtersCount = 4u, + XicPlusDaughtersCount = 5u +}; + +namespace corr_particle_type +{ +enum Type : int8_t { + PhysicalPrimary = 0, + IdentifiedParticle = 1, + V0 = 2 +}; +} +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +double getDeltaPhi(double phiXic, double phiHadron) +{ + return RecoDecay::constrainAngle(phiHadron - phiXic, -PIHalf); +} + +// definition of ME variables +using BinningType = ColumnBinningPolicy>; +using BinningTypeMcGen = ColumnBinningPolicy; + +// ============================================================================ +// SELECTION STRUCT - FOR COLLISION SELECTION WITH XIC +// ============================================================================ + +struct HfCorrelatorXicHadronsSelection { + Produces candSel; // using LcSelection table to avoid duplication for similair table, name will be changed later + + Configurable useSel8{"useSel8", true, "Flag for applying sel8 for collision selection"}; + Configurable selNoSameBunchPileUpColl{"selNoSameBunchPileUpColl", true, "Flag for rejecting the collisions associated with the same bunch crossing"}; + Configurable doSelXicCollision{"doSelXicCollision", true, "Select collisions with at least one Xic"}; + Configurable selectionFlagXic{"selectionFlagXic", 1, "Selection Flag for Xic"}; + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable ptCandMin{"ptCandMin", 1., "min. cand. pT"}; + + struct : ConfigurableGroup { + Configurable cfgPV{"cfgPV", 10., "maximum z-vertex"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + } cfgV0; + + SliceCache cache; + + using SelCollisions = soa::Join; + // Xic+ tables + using CandsXicPlusDataFiltered = soa::Filtered>; + using CandsXicPlusMcRecFiltered = soa::Filtered>; + // Xic0 tables + using CandsXic0DataFiltered = soa::Filtered>; + using CandsXic0McRecFiltered = soa::Filtered>; + + // MC Gen + using CandidatesXicPlusMcGen = soa::Join; + using CandidatesXic0McGen = soa::Join; + + // filter on selection of Xic and decay channel Xic->PKPi + Filter xicPlusFilter = aod::hf_sel_candidate_xic::isSelXicToXiPiPi >= selectionFlagXic; + Filter xic0Filter = aod::hf_sel_toxipi::resultSelections == true; + + template + void selectionCollision(CollType const& collision, CandType const& candidates) + { + bool isSelColl = true; + bool isCandFound = false; + bool isSel8 = true; + bool isNosameBunchPileUp = true; + + if (doSelXicCollision) { + for (const auto& candidate : candidates) { + double massCand = -999.; + double ptCand = -999.; + double yCand = -999.; + + // For both XicPlus and Xic0 + if constexpr (IsXicPlus) { + massCand = o2::constants::physics::MassXiCPlus; + ptCand = candidate.pt(); + yCand = candidate.y(massCand); + } else { + massCand = o2::constants::physics::MassXiC0; + ptCand = RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()); + yCand = candidate.kfRapXic(); + } + + // Kinematic cuts + if (std::abs(yCand) > yCandMax || ptCand < ptCandMin) { + isCandFound = false; + continue; + } + + if constexpr (IsMc) { + auto const mcFlag = std::abs(candidate.flagMcMatchRec()); + + bool isSignal = (mcFlag == static_cast(o2::aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || (mcFlag == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::XiczeroToXiPi))); + + if (!isSignal) { + isCandFound = false; + continue; + } + } + // If it passed both Kinematic and MC checks + isCandFound = true; + break; + } + } + + // Collision-level cuts + if (useSel8) { + isSel8 = collision.sel8(); + } + if (selNoSameBunchPileUpColl) { + isNosameBunchPileUp = static_cast(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)); + } + + isSelColl = isCandFound && isSel8 && isNosameBunchPileUp; + candSel(isSelColl); + } + + template + void selectionCollisionMcGen(CandType const& mcParticles) + { + bool isCandFound = false; + for (const auto& particle : mcParticles) { + if (std::abs(particle.pdgCode()) != kXiCPlus && std::abs(particle.pdgCode()) != kXiC0) { + continue; + } + + double const massCand = (std::abs(particle.pdgCode()) == kXiC0) ? o2::constants::physics::MassXiC0 : o2::constants::physics::MassXiCPlus; + double const yCand = RecoDecay::y(particle.pVector(), massCand); + if (std::abs(yCand) > yCandMax || particle.pt() < ptCandMin) { + isCandFound = false; + continue; + } + + isCandFound = true; + break; + } + candSel(isCandFound); + } + + template + bool eventSelV0(TCollision const& col) + { + if (!col.sel8()) { + return false; + } + + if (!col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (!col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (std::abs(col.posZ()) > cfgV0.cfgPV) { + return false; + } + if (!col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (col.trackOccupancyInTimeRange() > cfgV0.cfgMaxOccupancy || col.trackOccupancyInTimeRange() < cfgV0.cfgMinOccupancy) { + return false; + } + + return true; + } // event selection V0 + + void processV0Selection(SelCollisions::iterator const& collision, + aod::V0Datas const& V0s) + { + bool isCandFound = false; + const int64_t nCandidatesV0Min = 1; + + if (!eventSelV0(collision)) { + candSel(isCandFound); + return; + } + if (V0s.size() < nCandidatesV0Min) { + candSel(isCandFound); + return; + } + isCandFound = true; + candSel(isCandFound); + } + PROCESS_SWITCH(HfCorrelatorXicHadronsSelection, processV0Selection, "Process V0 Collision Selection for Data", false); + + void processXicPlusSelection(SelCollisions::iterator const& collision, + CandsXicPlusDataFiltered const& candidates) + { + selectionCollision(collision, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadronsSelection, processXicPlusSelection, "Process XicPlus Collision Selection for Data", true); + + void processXic0Selection(SelCollisions::iterator const& collision, + CandsXic0DataFiltered const& candidates) + { + selectionCollision(collision, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadronsSelection, processXic0Selection, "Process Xic0 Collision Selection for Data", false); + + void processXicPlusSelectionMcRec(SelCollisions::iterator const& collision, + CandsXicPlusMcRecFiltered const& candidates) + { + selectionCollision(collision, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadronsSelection, processXicPlusSelectionMcRec, "Process XicPlus Selection McRec", false); + + void processXic0SelectionMcRec(SelCollisions::iterator const& collision, + CandsXic0McRecFiltered const& candidates) + { + selectionCollision(collision, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadronsSelection, processXic0SelectionMcRec, "Process Xic0 Selection McRec", false); + + void processXicPlusSelectionMcGen(aod::McCollision const&, + CandidatesXicPlusMcGen const& mcParticles) + { + selectionCollisionMcGen(mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadronsSelection, processXicPlusSelectionMcGen, "Process XicPlus Selection McGen", false); + + void processXic0SelectionMcGen(aod::McCollision const&, + CandidatesXic0McGen const& mcParticles) + { + selectionCollisionMcGen(mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadronsSelection, processXic0SelectionMcGen, "Process Xic0 Selection McGen", false); +}; + +// Xic-Hadron correlation pair builder +struct HfCorrelatorXicHadrons { + // using Lc correlation table to avoid duplication for similair tables, name will be changed later + Produces entryCandHadronPair; + Produces entryCandHadronPairY; + Produces entryCandHadronPairTrkPID; + Produces entryCandHadronRecoInfo; + Produces entryCandHadronMlInfo; + Produces entryCandHadronGenInfo; + Produces entryCandRecoInfo; + Produces entryCandCandGenInfo; + Produces entryTrackRecoInfo; + Produces entryCand; + Produces entryHadron; + Produces entryTrkPID; + Produces entryPairCandCharge; + Produces entryCandCharge; + Produces entryXicHadronInvMass; + Produces entryPairedV0InvMass; + Produces entryV0InvMass; + + struct : ConfigurableGroup { + Configurable selectionFlagXic{"selectionFlagXic", 1, "Selection flag for Xic"}; + Configurable numberEventsMixed{"numberEventsMixed", 5, "number of events mixed in ME process"}; + Configurable applyEfficiency{"applyEfficiency", 1, "Flag for applying Xic efficiency weights"}; + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable yCandGenMax{"yCandGenMax", 0.5, "max. gen. cand. rapidity"}; + Configurable etaTrackMax{"etaTrackMax", 0.8, "max. eta of tracks"}; + Configurable dcaXYTrackMax{"dcaXYTrackMax", 1., "max. DCAxy of tracks"}; + Configurable dcaZTrackMax{"dcaZTrackMax", 1., "max. DCAz of tracks"}; + Configurable ptCandMin{"ptCandMin", 1., "min. cand. pT"}; + Configurable ptCandMax{"ptCandMax", 50., "max. cand. pT"}; + Configurable ptTrackMin{"ptTrackMin", 0.3, "min. track pT"}; + Configurable ptTrackMax{"ptTrackMax", 50., "max. track pT"}; + Configurable multMin{"multMin", 0., "minimum multiplicity accepted"}; + Configurable multMax{"multMax", 10000., "maximum multiplicity accepted"}; + Configurable> classMl{"classMl", {0, 1, 2}, "Indexes of ML scores to be stored. Three indexes max."}; + Configurable> binsPtXic{"binsPtXic", std::vector{0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 50.0}, "pT bin limits for XicPlus mass plots"}; + Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle"}; + Configurable> binsPtEfficiencyXic{"binsPtEfficiencyXic", std::vector{0.0, 2.0, 4.0, 6.0, 8.0, 12.0, 50.0}, "pT bin limits for efficiency"}; + Configurable> efficiencyXic{"efficiencyXic", {1., 1., 1., 1., 1., 1.}, "efficiency values for Xic"}; + Configurable storeAutoCorrelationFlag{"storeAutoCorrelationFlag", false, "Store flag that indicates if the track is paired to its Xic mother instead of skipping it"}; + Configurable correlateXicWithLeadingParticle{"correlateXicWithLeadingParticle", false, "Switch for correlation of Xic baryons with leading particle only"}; + Configurable pidTrkApplied{"pidTrkApplied", false, "Apply PID selection for associated tracks"}; + Configurable> trkPIDspecies{"trkPIDspecies", std::vector{o2::track::PID::Proton, o2::track::PID::Pion, o2::track::PID::Kaon}, "Trk sel: Particles species for PID"}; + Configurable> pidTPCMax{"pidTPCMax", std::vector{3., 0., 0.}, "maximum nSigma TPC"}; + Configurable> pidTOFMax{"pidTOFMax", std::vector{3., 0., 0.}, "maximum nSigma TOF"}; + Configurable tofPIDThreshold{"tofPIDThreshold", 0.75, "minimum pT after which TOF PID is applicable"}; + Configurable fillTrkPID{"fillTrkPID", false, "fill PID information for associated tracks"}; + Configurable forceTOF{"forceTOF", false, "fill PID information for associated tracks"}; + Configurable calTrkEff{"calTrkEff", false, "fill histograms to calculate efficiency"}; + Configurable isRecTrkPhyPrimary{"isRecTrkPhyPrimary", true, "Calculate the efficiency of reconstructed primary physical tracks"}; + Configurable calEffEventWithCand{"calEffEventWithCand", true, "Calculate the efficiency of Xic candidate"}; + Configurable eventFractionToAnalyze{"eventFractionToAnalyze", -1, "Fraction of events to analyze"}; + Configurable particlePdg{"particlePdg", PDG_t::kProton, "PDG code of particle: kProton(2212), kPiPlus(211), kKPlus(321), kLambda0(3122)"}; + Configurable corrParticle{"corrParticle", corr_particle_type::IdentifiedParticle, "0: physical primary, 1: individual identified particle, 2: V0s"}; + } cfgXicCand; + + struct : ConfigurableGroup { + Configurable cfgV0DaughPrPtMax{"cfgV0DaughPrPtMax", 5., "max. pT Daughter Proton"}; + Configurable cfgV0DaughPrPtMin{"cfgV0DaughPrPtMin", 0.3, "min. pT Daughter Proton"}; + Configurable cfgV0DaughPiPtMax{"cfgV0DaughPiPtMax", 10., "max. pT Daughter Pion"}; + Configurable cfgV0DaughPiPtMin{"cfgV0DaughPiPtMin", 0.3, "min. pT Daughter Pion"}; + Configurable cfgV0DaughPIDCutsTPCPr{"cfgV0DaughPIDCutsTPCPr", 2.5, "max. TPCnSigma Proton"}; + Configurable cfgV0DaughPIDCutsTPCPi{"cfgV0DaughPIDCutsTPCPi", 2.5, "max. TPCnSigma Pion"}; + Configurable cfgV0DaughPIDCutsTOFPi{"cfgV0DaughPIDCutsTOFPi", 2.5, "max. TOFnSigma Pion"}; + Configurable cfgV0DaughPIDCutsTOFPr{"cfgV0DaughPIDCutsTOFPr", -2.5, "min. TOFnSigma Proton (put only negative value)"}; + Configurable cfgHypMassWindow{"cfgHypMassWindow", 0.1, "single lambda mass selection"}; + Configurable cfgIsCorrCollMatchV0{"cfgIsCorrCollMatchV0", true, "check if daughter and mother collision are same"}; + Configurable cfgCalDataDrivenEffPr{"cfgCalDataDrivenEffPr", false, "calculate data driven efficiency of proton using Lambda"}; + Configurable cfgV0radiusMin{"cfgV0radiusMin", 1.2, "minimum decay radius"}; + Configurable cfgDCAPosToPVMin{"cfgDCAPosToPVMin", 0.05, "minimum DCA to PV for positive track"}; + Configurable cfgDCANegToPVMin{"cfgDCANegToPVMin", 0.2, "minimum DCA to PV for negative track"}; + Configurable cfgV0CosPA{"cfgV0CosPA", 0.995, "minimum v0 cosine"}; + Configurable cfgDCAV0Dau{"cfgDCAV0Dau", 1.0, "maximum DCA between daughters"}; + Configurable cfgV0PtMin{"cfgV0PtMin", 0, "minimum pT for lambda"}; + Configurable cfgV0LifeTime{"cfgV0LifeTime", 30., "maximum lambda lifetime"}; + Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable cfgPV{"cfgPV", 10., "maximum z-vertex"}; + Configurable calEffV0{"calEffV0", false, "calculate lambda0 efficiency"}; + Configurable checkTOFForPion{"checkTOFForPion", false, "if True, TOF selection on pion V0 wil only be applied if TOF present"}; + } cfgV0; + + SliceCache cache; + Service pdg{}; + + // Event Mixing for the Data Mode + using SelCollisions = soa::Filtered>; + using SelCollisionsMc = soa::Filtered>; + + // XicPlus data + using CandsXicPlusData = soa::Join; + using CandsXicPlusDataFiltered = soa::Filtered; + using CandsXicPlusMcRec = soa::Join; + using CandsXicPlusMcRecFiltered = soa::Filtered; + + // Xic0 data + using CandsXic0Data = soa::Join; + using CandsXic0DataFiltered = soa::Filtered; + using CandsXic0McRec = soa::Join; + using CandsXic0McRecFiltered = soa::Filtered; + + // MC Gen + using CandidatesXicPlusMcGen = soa::Join; + using CandidatesXic0McGen = soa::Join; + + using McCollisionsSel = soa::Filtered>; + using McParticlesSel = soa::Filtered; + + // Tracks + using TracksData = soa::Filtered>; + using TracksWithMc = soa::Filtered>; + + template + using HasStrangeTOFinV0 = decltype(std::declval().tofNSigmaLaPr()); + + Filter collisionFilter = aod::hf_selection_lc_collision::lcSel == true; + Filter trackFilter = (nabs(aod::track::eta) < cfgXicCand.etaTrackMax) && (nabs(aod::track::pt) > cfgXicCand.ptTrackMin) && (nabs(aod::track::dcaXY) < cfgXicCand.dcaXYTrackMax) && (nabs(aod::track::dcaZ) < cfgXicCand.dcaZTrackMax); + Filter xicPlusFilter = aod::hf_sel_candidate_xic::isSelXicToXiPiPi >= cfgXicCand.selectionFlagXic; + Filter xic0Filter = aod::hf_sel_toxipi::resultSelections == true; + + Preslice perTrueCollision = o2::aod::mcparticle::mcCollisionId; + Preslice perCollisionID = aod::track::collisionId; + Preslice candXicPerCol = aod::hf_cand::collisionId; + Preslice candXic0PerCol = aod::hf_cand_xic0_omegac0::collisionId; + + // configurable axis definition + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.0f, 2000.0f, 6000.0f, 100000.0f}, "Mixing bins - multiplicity"}; + ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; + ConfigurableAxis binsMultiplicityMc{"binsMultiplicityMc", {VARIABLE_WIDTH, 0.0f, 20.0f, 50.0f, 500.0f}, "Mixing bins - MC multiplicity"}; + ConfigurableAxis binsBdtScore{"binsBdtScore", {100, 0., 1.}, "Bdt output scores"}; + ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "#it{#eta}"}; + ConfigurableAxis binsPhi{"binsPhi", {64, -PIHalf, 3. * PIHalf}, "#it{#varphi}"}; + ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; + ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 6000.}, "Multiplicity as FT0M signal amplitude"}; + ConfigurableAxis binsCandMassXicPlus{"binsCandMassXicPlus", {400, 2., 2.8}, "inv. mass (Xi pi pi) (GeV/#it{c}^{2})"}; + ConfigurableAxis binsCandMassXic0{"binsCandMassXic0", {200, 2.4, 2.8}, "inv. mass (Xi pi) (GeV/#it{c}^{2})"}; + ConfigurableAxis binsNSigmas{"binsNSigmas", {4000, -500., 500.}, "n#sigma"}; + + BinningType corrBinning{{binsZVtx, binsMultiplicity}}; + HistogramRegistry registry{"registry"}; + + int8_t chargeCand = 3; + int leadingIndex = 0; + int poolBin = 0; + int poolBinXic = 0; + bool correlationStatus = false; + bool isPrompt = false; + bool isNonPrompt = false; + bool isSignal = false; + TRandom3 rnd{0}; + std::vector outputMlXic = {-1., -1., -1.}; + + void init(InitContext&) + { + // XicPlus mass axes + AxisSpec axisCandMassXicPlus = {binsCandMassXicPlus, "inv. mass (Xi pi pi) (GeV/#it{c}^{2})"}; + // Xic0 mass axes + AxisSpec axisCandMassXic0 = {binsCandMassXic0, "inv. mass (Xi pi) (GeV/#it{c}^{2})"}; + + AxisSpec const axisEta = {binsEta, "#it{eta}"}; + AxisSpec const axisPhi = {binsPhi, "#it{#varphi}"}; + AxisSpec axisPtXic = {static_cast>(cfgXicCand.binsPtXic), "#it{p}_{T} Xic (GeV/#it{c})"}; + AxisSpec axisPtHadron = {static_cast>(cfgXicCand.binsPtHadron), "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec axisPtTrack = {500, 0, 50, "#it{p}_{T} Hadron (GeV/#it{c})"}; + AxisSpec const axisMultiplicity = {binsMultiplicity, "Multiplicity"}; + AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; + AxisSpec const axisPosZ = {binsZVtx, "PosZ"}; + AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; + AxisSpec const axisPoolBin = {binsPoolBin, "PoolBin"}; + AxisSpec const axisRapidity = {100, -2, 2, "Rapidity"}; + AxisSpec const axisNSigma = {binsNSigmas, "n#sigma"}; + AxisSpec axisSign = {5, -2.5, 2.5, "Sign"}; + AxisSpec axisPtV0 = {500, 0., 50.0, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisMassV0 = {300, 1.05f, 1.2f, "inv. mass (p #pi) (GeV/#it{c}^{2})"}; + + // XicPlus histograms + registry.add("hMassXicPlusVsPt", "XicPlus candidates;inv. mass (Xi pi pi) (GeV/#it{c}^{2});#it{p}_{T};entries", {HistType::kTH2F, {{axisCandMassXicPlus}, {axisPtXic}}}); + registry.add("hMassXicPlusData", "XicPlus candidates;inv. mass (Xi pi pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{axisCandMassXicPlus}}}); + + // Xic0 histograms + registry.add("hMassXic0VsPt", "Xic0 candidates;inv. mass (Xi pi) (GeV/#it{c}^{2});#it{p}_{T};entries", {HistType::kTH2F, {{axisCandMassXic0}, {axisPtXic}}}); + registry.add("hMassXic0Data", "Xic0 candidates;inv. mass (Xi pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{axisCandMassXic0}}}); + + // Common histograms + registry.add("hPtCandXic", "Xic0 candidates;candidate #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtXic}}); + registry.add("hEta", "Xic candidates;candidate #it{#eta};entries", {HistType::kTH1F, {axisEta}}); + registry.add("hPhi", "Xic candidates;candidate #it{#varphi};entries", {HistType::kTH1F, {axisPhi}}); + registry.add("hY", "Xic candidates;candidate #it{#y};entries", {HistType::kTH1F, {axisRapidity}}); + registry.add("hMultiplicityPreSelection", "multiplicity prior to selection;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); + registry.add("hMultiplicity", "multiplicity;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); + registry.add("hMultFT0M", "multiplicity;multiplicity;entries", {HistType::kTH1F, {{10000, 0., 10000.}}}); + registry.add("hZvtx", "z vertex;z vertex;entries", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hCandBin", "candidates in pool Bin;pool Bin;entries", {HistType::kTH1F, {{9, 0., 9.}}}); + registry.add("hTracksBin", "Tracks selected in pool Bin;pool Bin;entries", {HistType::kTH1F, {{9, 0., 9.}}}); + // registry.add("hMassXicVsPt", "Xic candidates;inv. mass (Xi pi pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{axisCandMass}, {axisPtXic}}}); + // registry.add("hMassXicPlusVsPtVsSign", "Xic candidates;inv. mass (#Xi #pi #pi) (GeV/#it{c}^{2});sign;entries", {HistType::kTH3F, {{axisCandMass}, {axisPtXic}, {axisSign}}}); + // registry.add("hMassXicData", "Xic candidates;inv. mass (Xi pi pi) (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{axisCandMass}}}); + registry.add("hXicPoolBin", "Xic candidates pool bin", {HistType::kTH1F, {axisPoolBin}}); + registry.add("hTracksPoolBin", "Particles associated pool bin", {HistType::kTH1F, {axisPoolBin}}); + // Histograms for MC Reco analysis + registry.add("hMcEvtCount", "Event counter - MC gen;;entries", {HistType::kTH1F, {{1, -0.5, 0.5}}}); + // registry.add("hMassXicMcRecBkg", "Xic background candidates - MC reco;inv. mass (Xi pi pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{axisCandMass}, {axisPtXic}}}); + registry.add("hPtCandSig", "Xic,Hadron candidates - MC Reco", {HistType::kTH1F, {axisPtXic}}); + registry.add("hPtCandSigPrompt", "Xic,Hadron candidates Prompt - MC Reco", {HistType::kTH1F, {axisPtXic}}); + registry.add("hPtCandSigNonPrompt", "Xic,Hadron candidates Non Prompt - MC Reco", {HistType::kTH1F, {axisPtXic}}); + // registry.add("hPtCandMcRecBkg", "Xic,Hadron candidates - MC Reco", {HistType::kTH1F, {axisPtXic}}); + registry.add("hEtaSig", "Xic,Hadron candidates - MC Reco", {HistType::kTH1F, {axisEta}}); + registry.add("hPhiSig", "Xic,Hadron candidates - MC Reco", {HistType::kTH1F, {axisPhi}}); + // registry.add("hY", "Xic,Hadron candidates;candidate #it{#y};entries", {HistType::kTH1F, {axisRapidity}}); + registry.add("hYSig", "Xic,Hadron candidates - MC reco;candidate #it{#y};entries", {HistType::kTH1F, {axisRapidity}}); + registry.add("hPtCandMcRecSigPrompt", "Xic,Hadron candidates Prompt - MC Reco", {HistType::kTH1F, {axisPtXic}}); + registry.add("hPtCandMcRecSigNonPrompt", "Xic,Hadron candidates Non Prompt - MC Reco", {HistType::kTH1F, {axisPtXic}}); + registry.add("hEtaMcRecBkg", "Xic,Hadron candidates - MC Reco", {HistType::kTH1F, {axisEta}}); + registry.add("hPhiMcRecBkg", "Xic,Hadron candidates - MC Reco", {HistType::kTH1F, {axisPhi}}); + registry.add("hYMcRecBkg", "Xic,Hadron candidates - MC reco;candidate #it{#y};entries", {HistType::kTH1F, {axisRapidity}}); + registry.add("hFakeTracksMcRec", "Fake tracks - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtParticleAssocVsCandMcRec", "Associated Particle - MC Rec", {HistType::kTH2F, {{axisPtHadron}, {axisPtXic}}}); + registry.add("hPtTracksVsSignRec", "Associated Particle - MC Rec", {HistType::kTH2F, {{axisPtTrack}, {axisSign}}}); + registry.add("hPtTracksVsSignRecTrue", "Associated Particle - MC Rec (True)", {HistType::kTH2F, {{axisPtTrack}, {axisSign}}}); + registry.add("hPtTracksVsSignGen", "Associated Particle - MC Gen", {HistType::kTH2F, {{axisPtTrack}, {axisSign}}}); + registry.add("hPtPrimaryParticleAssocVsCandMcRec", "Associated Particle - MC Rec", {HistType::kTH2F, {{axisPtHadron}, {axisPtXic}}}); + registry.add("hPtVsMultiplicityMcRecPrompt", "Multiplicity FT0M - MC Rec Prompt", {HistType::kTH2F, {{axisPtXic}, {axisMultFT0M}}}); + registry.add("hPtVsMultiplicityMcRecNonPrompt", "Multiplicity FT0M - MC Rec Non Prompt", {HistType::kTH2F, {{axisPtXic}, {axisMultFT0M}}}); + // Histograms for MC Gen analysis + registry.add("hcountCandtriggersMcGen", "Xic trigger particles - MC gen;;N of trigger Xic", {HistType::kTH2F, {{1, -0.5, 0.5}, {axisPtXic}}}); + registry.add("hcountCandHadronPerEvent", "Xic,Hadron particles - MC gen;Number per event;entries", {HistType::kTH1F, {{21, -0.5, 20.5}}}); + registry.add("hPtCandMcGen", "Xic,Hadron particles - MC gen;particle #it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtXic}}); + registry.add("hYMcGen", "Xic,Hadron candidates - MC gen;candidate #it{#y};entries", {HistType::kTH1F, {axisRapidity}}); + registry.add("hPtCandMcGenPrompt", "Xic,Hadron particles - MC Gen Prompt", {HistType::kTH1F, {axisPtXic}}); + registry.add("hPtCandVsChargeMcGenPrompt", "Charm Hadron particles - MC Gen Prompt", {HistType::kTH2F, {{axisPtXic}, {axisSign}}}); + registry.add("hPtCandMcGenNonPrompt", "Charm Hadron particles - MC Gen Non Prompt", {HistType::kTH1F, {axisPtXic}}); + registry.add("hPtCandVsChargeMcGenNonPrompt", "Xic,Hadron particles - MC Gen Non Prompt", {HistType::kTH2F, {{axisPtXic}, {axisSign}}}); + registry.add("hPtParticleAssocMcGen", "Associated Particle - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hEtaMcGen", "Xic,Hadron particles - MC Gen", {HistType::kTH1F, {axisEta}}); + registry.add("hPhiMcGen", "Xic,Hadron particles - MC Gen", {HistType::kTH1F, {axisPhi}}); + registry.add("hMultFT0AMcGen", "Xic,Hadron multiplicity FT0A - MC Gen", {HistType::kTH1F, {axisMultiplicity}}); + registry.add("hTOFnSigmaPr", "hTOFnSigmaPr", {HistType::kTH2F, {{axisPtHadron}, {axisNSigma}}}); + registry.add("hTPCnSigmaPr", "hTPCnSigmaPr", {HistType::kTH2F, {{axisPtHadron}, {axisNSigma}}}); + registry.add("hTOFnSigmaPrPiKRej", "hTOFnSigmaPrPiKRej", {HistType::kTH2F, {{axisPtHadron}, {axisNSigma}}}); + registry.add("hTPCnSigmaPrPiKRej", "hTPCnSigmaPrPiKRej", {HistType::kTH2F, {{axisPtHadron}, {axisNSigma}}}); + + // Xic-Hadron invariant mass histograms + registry.add("hXicHadronInvMassVsPt", "Xic+Hadron invariant mass vs combined pT;m_{Xic+h} (GeV/c^2);p_{T,combined} (GeV/c)", {HistType::kTH2F, {{500, 2.5, 5.0}, {100, 0., 50.}}}); + registry.add("hXicLambdaInvMassVsPt", "Xic+Lambda invariant mass vs combined pT;m_{Xic+Lambda} (GeV/c^2);p_{T,combined} (GeV/c)", {HistType::kTH2F, {{500, 3.0, 5.5}, {100, 0., 50.}}}); + registry.add("hXicHadronInvMass", "Xic+Hadron invariant mass;m_{Xic+h} (GeV/c^2);entries", {HistType::kTH1F, {{500, 2.5, 5.0}}}); + registry.add("hXicLambdaInvMass", "Xic+Lambda invariant mass;m_{Xic+Lambda} (GeV/c^2);entries", {HistType::kTH1F, {{500, 3.0, 5.5}}}); + registry.add("hXicHadronPtCombined", "Combined pT (Xic+Hadron);p_{T,combined} (GeV/c);entries", {HistType::kTH1F, {{100, 0., 50.}}}); + registry.add("hXicLambdaPtCombined", "Combined pT (Xic+Lambda);p_{T,combined} (GeV/c);entries", {HistType::kTH1F, {{100, 0., 50.}}}); + + // Lambda V0 histograms + registry.add("hEventLambdaV0", "Lambda, events", {HistType::kTH1F, {{2, 0, 2}}}); + registry.add("hV0Lambda", "V0 Lambda candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0LambdaRefl", "V0 Lambda reflected candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0LambdaPiKRej", "V0 Lambda candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0LambdaReflPiKRej", "V0 Lambda reflected candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0LambdaMcRec", "McRec V0 Lambda candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0LambdaReflMcRec", "McRec V0 Lambda reflected candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0PrimLambdaMcRec", "McRec V0 Lambda candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0PrimLambdaReflMcRec", "McRec V0 Lambda reflected candidates;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0LambdaPiKRejMcRec", "McRec V0 Lambda candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0LambdaReflPiKRejMcRec", "McRec V0 Lambda reflected candidates with #pi K rejection;inv. mass (p #pi) (GeV/#it{c}^{2});GeV/#it{c};GeV/#it{c}", {HistType::kTH3F, {{axisMassV0}, {axisPtV0}, {axisPtHadron}}}); + registry.add("hV0PtPrimLambdaMcGen", "Mcgen V0 Lambda candidates;GeV/#it{c}", {HistType::kTH1F, {{axisPtV0}}}); + + corrBinning = {{binsZVtx, binsMultiplicity}}; + } + + template + void fillMlOutput(MlProbType const& mlProb, std::vector& outputMl) + { + if (mlProb.size() == 0) { + return; + } + for (unsigned int iclass = 0; iclass < cfgXicCand.classMl->size() && iclass < outputMl.size(); iclass++) { + unsigned int targetIndex = cfgXicCand.classMl->at(iclass); + if (targetIndex < mlProb.size()) { + outputMl[iclass] = mlProb[targetIndex]; + } + } + } + + float getMassFromPdg(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kProton: + return MassProton; + case PDG_t::kPiPlus: + return MassPiPlus; + case PDG_t::kKPlus: + return MassKPlus; + case PDG_t::kLambda0: + return MassLambda; + default: + return 0.f; + } + } + + template + bool selectionV0(TCollision const& collision, V0 const& candidate) + { + if (candidate.v0radius() < cfgV0.cfgV0radiusMin) { + return false; + } + if (std::abs(candidate.dcapostopv()) < cfgV0.cfgDCAPosToPVMin) { + return false; + } + if (std::abs(candidate.dcanegtopv()) < cfgV0.cfgDCANegToPVMin) { + return false; + } + if (candidate.v0cosPA() < cfgV0.cfgV0CosPA) { + return false; + } + if (std::abs(candidate.dcaV0daughters()) > cfgV0.cfgDCAV0Dau) { + return false; + } + if (candidate.pt() < cfgV0.cfgV0PtMin) { + return false; + } + if (std::abs(candidate.yLambda()) > cfgXicCand.yCandMax) { + return false; + } + if (candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda > cfgV0.cfgV0LifeTime) { + return false; + } + + return true; + } + + template + bool isSelectedV0Daughter(Tracktype const& track, V0Type const& v0, int pid) + { + if (std::abs(track.eta()) > cfgXicCand.etaTrackMax) { + return false; + } + + // --------------------------------------------------------- + // 1. Proton PID Selection + // --------------------------------------------------------- + if (std::abs(pid) == kProton) { + bool hasTOFProton = (pid > 0) ? v0.positiveHasTOF() : v0.negativeHasTOF(); + bool passTOF = false; + + if (track.pt() > cfgV0.cfgV0DaughPrPtMax || track.pt() < cfgV0.cfgV0DaughPrPtMin) { + return false; + } + + if (hasTOFProton && (track.pt() > cfgXicCand.tofPIDThreshold)) { + if constexpr (std::experimental::is_detected::value) { + // pid > 0: Proton from Lambda (LaPr) + // pid < 0: Antiproton from Anti-Lambda (ALaPr) + double strangeTOF = (pid > 0) ? v0.tofNSigmaLaPr() : v0.tofNSigmaALaPr(); + passTOF = strangeTOF > cfgV0.cfgV0DaughPIDCutsTOFPr; + + } else { + // if strange TOF is unavailable + passTOF = track.tofNSigmaPr() > cfgV0.cfgV0DaughPIDCutsTOFPr; + } + } + + if ((std::abs(track.tpcNSigmaPr()) > cfgV0.cfgV0DaughPIDCutsTPCPr) && !passTOF) { + return false; + } + } + + // --------------------------------------------------------- + // 2. Pion PID Selection + // --------------------------------------------------------- + if (std::abs(pid) == kPiPlus && cfgV0.checkTOFForPion) { + bool hasTOFPion = (pid < 0) ? v0.negativeHasTOF() : v0.positiveHasTOF(); + bool passTOF = false; + + if (track.pt() > cfgV0.cfgV0DaughPiPtMax || track.pt() < cfgV0.cfgV0DaughPiPtMin) { + return false; + } + + if (hasTOFPion && (track.pt() > cfgXicCand.tofPIDThreshold)) { + if constexpr (std::experimental::is_detected::value) { + // A pion can belong to either a Lambda/Anti-Lambda decay or a K0s decay. + // We evaluate both applicable hypotheses based on charge sign and pick the best match. + double tofLa = (pid > 0) ? v0.tofNSigmaALaPi() : v0.tofNSigmaLaPi(); + + passTOF = std::abs(tofLa) > cfgV0.cfgV0DaughPIDCutsTOFPi; + } else { + // Fallback to standard track TOF + passTOF = std::abs(track.tofNSigmaPi()) > cfgV0.cfgV0DaughPIDCutsTOFPi; + } + } + + if ((std::abs(track.tpcNSigmaPi()) > cfgV0.cfgV0DaughPIDCutsTPCPi) && !passTOF) { + return false; + } + } + return true; + } + + // ======================================== + // Efficiency calculation block + // ======================================== + template + void fillEffV0(CollType const& col, + V0 const& v0s, + TrackType const&, + PartType const& mcParticles) + { + int countV0 = 1; + // Data-driven efficiency calculation for protons using Lambda + for (const auto& v0 : v0s) { + bool passV0Sel = selectionV0(col, v0); + + auto const& trackV0Pos = v0.template posTrack_as(); + auto const& trackV0Neg = v0.template negTrack_as(); + if (cfgV0.cfgIsCorrCollMatchV0 && ((v0.collisionId() != trackV0Pos.collisionId()) || (v0.collisionId() != trackV0Neg.collisionId()))) { + continue; + } + + // Process Lambda (proton + pion) + if (passV0Sel && std::abs(o2::constants::physics::MassLambda - v0.mLambda()) < cfgV0.cfgHypMassWindow && v0.alpha() > 0) { + entryHadron(v0.mLambda(), trackV0Pos.eta(), trackV0Pos.pt() * trackV0Pos.sign(), 0, 0, v0.pt()); + entryTrkPID(trackV0Pos.tpcNSigmaPr(), trackV0Pos.tpcNSigmaKa(), trackV0Pos.tpcNSigmaPi(), trackV0Pos.tofNSigmaPr(), trackV0Pos.tofNSigmaKa(), trackV0Pos.tofNSigmaPi()); + + if (isSelectedV0Daughter(trackV0Pos, v0, kProton) && isSelectedV0Daughter(trackV0Neg, v0, kPiMinus)) { + registry.fill(HIST("hV0Lambda"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hV0LambdaRefl"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hTPCnSigmaPr"), trackV0Pos.pt(), trackV0Pos.tpcNSigmaPr()); + + if (trackV0Pos.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPr"), trackV0Pos.pt(), trackV0Pos.tofNSigmaPr()); + } + + if (passPIDSelection(trackV0Pos, cfgXicCand.trkPIDspecies, cfgXicCand.pidTPCMax, cfgXicCand.pidTOFMax, cfgXicCand.tofPIDThreshold, cfgXicCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRej"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hV0LambdaReflPiKRej"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hTPCnSigmaPrPiKRej"), trackV0Pos.pt(), trackV0Pos.tpcNSigmaPr()); + if (trackV0Pos.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPrPiKRej"), trackV0Pos.pt(), trackV0Pos.tofNSigmaPr()); + } + } + } + } + + if (passV0Sel && std::abs(o2::constants::physics::MassLambda - v0.mAntiLambda()) < cfgV0.cfgHypMassWindow && v0.alpha() < 0) { + entryHadron(v0.mAntiLambda(), trackV0Neg.eta(), trackV0Neg.pt() * trackV0Neg.sign(), 0, 0, v0.pt()); + entryTrkPID(trackV0Neg.tpcNSigmaPr(), trackV0Neg.tpcNSigmaKa(), trackV0Neg.tpcNSigmaPi(), trackV0Neg.tofNSigmaPr(), trackV0Neg.tofNSigmaKa(), trackV0Neg.tofNSigmaPi()); + + if (isSelectedV0Daughter(trackV0Neg, v0, kProtonBar) && isSelectedV0Daughter(trackV0Pos, v0, kPiPlus)) { + registry.fill(HIST("hV0Lambda"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hV0LambdaRefl"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hTPCnSigmaPr"), trackV0Neg.pt(), trackV0Neg.tpcNSigmaPr()); + + if (trackV0Neg.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPr"), trackV0Neg.pt(), trackV0Neg.tofNSigmaPr()); + } + + if (passPIDSelection(trackV0Neg, cfgXicCand.trkPIDspecies, cfgXicCand.pidTPCMax, cfgXicCand.pidTOFMax, cfgXicCand.tofPIDThreshold, cfgXicCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRej"), v0.mAntiLambda(), v0.pt(), trackV0Neg.pt()); + registry.fill(HIST("hV0LambdaReflPiKRej"), v0.mLambda(), v0.pt(), trackV0Pos.pt()); + registry.fill(HIST("hTPCnSigmaPrPiKRej"), trackV0Neg.pt(), trackV0Neg.tpcNSigmaPr()); + if (trackV0Neg.hasTOF()) { + registry.fill(HIST("hTOFnSigmaPrPiKRej"), trackV0Neg.pt(), trackV0Neg.tofNSigmaPr()); + } + } + } + } + + // MC-Reco specific V0 matching + if constexpr (IsMc) { + if (!v0.has_mcParticle() || !trackV0Pos.has_mcParticle() || !trackV0Neg.has_mcParticle()) { + continue; + } + auto const& v0Mc = v0.mcParticle(); + auto const& partV0Pos = trackV0Pos.mcParticle(); + auto const& partV0Neg = trackV0Neg.mcParticle(); + + if (passV0Sel && v0Mc.pdgCode() == kLambda0 && v0.alpha() > 0) { + if (isSelectedV0Daughter(trackV0Pos, v0, kProton) && isSelectedV0Daughter(trackV0Neg, v0, kPiMinus)) { + registry.fill(HIST("hV0LambdaMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + registry.fill(HIST("hV0LambdaReflMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + if (cfgV0.calEffV0 && v0Mc.isPhysicalPrimary() && v0Mc.producedByGenerator()) { + registry.fill(HIST("hV0PrimLambdaMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + registry.fill(HIST("hV0PrimLambdaReflMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + } + + if (passPIDSelection(trackV0Pos, cfgXicCand.trkPIDspecies, cfgXicCand.pidTPCMax, cfgXicCand.pidTOFMax, cfgXicCand.tofPIDThreshold, cfgXicCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRejMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + registry.fill(HIST("hV0LambdaReflPiKRejMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + } + } + } + if (passV0Sel && v0Mc.pdgCode() == kLambda0Bar && v0.alpha() < 0) { + if (isSelectedV0Daughter(trackV0Neg, v0, kProtonBar) && isSelectedV0Daughter(trackV0Pos, v0, kPiPlus)) { + registry.fill(HIST("hV0LambdaMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + registry.fill(HIST("hV0LambdaReflMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + + if (cfgV0.calEffV0 && v0Mc.isPhysicalPrimary() && v0Mc.producedByGenerator()) { + registry.fill(HIST("hV0PrimLambdaMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + registry.fill(HIST("hV0PrimLambdaReflMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + } + if (passPIDSelection(trackV0Neg, cfgXicCand.trkPIDspecies, cfgXicCand.pidTPCMax, cfgXicCand.pidTOFMax, cfgXicCand.tofPIDThreshold, cfgXicCand.forceTOF)) { + registry.fill(HIST("hV0LambdaPiKRejMcRec"), v0.mAntiLambda(), v0.pt(), partV0Neg.pt()); + registry.fill(HIST("hV0LambdaReflPiKRejMcRec"), v0.mLambda(), v0.pt(), partV0Pos.pt()); + } + } + } + if (cfgV0.calEffV0 && countV0 == 1) { + auto genPart = mcParticles.sliceBy(perTrueCollision, v0Mc.mcCollisionId()); + + for (const auto& particle : genPart) { + + if (std::abs(particle.pdgCode()) != kLambda0) { + continue; + } + + if (std::abs(particle.y()) > cfgXicCand.yCandMax) { + continue; + } + if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) { + continue; + } + + auto daughterParts = particle.template daughters_as(); + const int8_t nDaughtersV0 = 2; + + if (daughterParts.size() != nDaughtersV0) { + continue; + } + + int8_t countPassedDaughter = 0; + for (const auto& currentDaughter : daughterParts) { + + if (std::abs(currentDaughter.eta()) > cfgXicCand.etaTrackMax) { + continue; + } + + if (std::abs(currentDaughter.pdgCode()) == kProton) { + + if (currentDaughter.pt() > cfgV0.cfgV0DaughPrPtMax || currentDaughter.pt() < cfgV0.cfgV0DaughPrPtMin) { + continue; + } + + } else if (std::abs(currentDaughter.pdgCode()) == kPiPlus) { + if (currentDaughter.pt() > cfgV0.cfgV0DaughPiPtMax || currentDaughter.pt() < cfgV0.cfgV0DaughPiPtMin) { + continue; + } + + } else { + continue; + } + countPassedDaughter++; + } + if (countPassedDaughter == nDaughtersV0) { + registry.fill(HIST("hV0PtPrimLambdaMcGen"), particle.pt()); + } + } + } + countV0++; + } + } + } + + // ============================================================================ + // SAME EVENT WITH V0 LAMBDA PROCESSING + // ============================================================================ + + template + void doSameEventWithV0(CollisionType const& collision, + V0 const& v0s, + TrackType const& tracks, + CandsXics const& candidates, + aod::McParticles const* mcParticles = nullptr) + { + + // Main Xic-Lambda correlation loop + int nTracks = 0; + float efficiencyWeightCand = 1.; + int64_t timeStamp = 0; + bool skipMixedEventTableFilling = false; + float const multiplicityFT0M = collision.multFT0M(); + int gCollisionId = collision.globalIndex(); + + if (candidates.size() == 0) { + return; + } + + if (cfgXicCand.eventFractionToAnalyze > 0) { + if (rnd.Uniform(0, 1) > cfgXicCand.eventFractionToAnalyze) { + skipMixedEventTableFilling = true; + } + } + + if constexpr (!IsMcRec) { + timeStamp = collision.template bc_as().timestamp(); + } + + poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), multiplicityFT0M)); + + // Count good tracks + if (collision.numContrib() > 1) { + for (const auto& track : tracks) { + if (std::abs(track.eta()) > cfgXicCand.etaTrackMax || std::abs(track.dcaXY()) > cfgXicCand.dcaXYTrackMax || std::abs(track.dcaZ()) > cfgXicCand.dcaZTrackMax) { + continue; + } + nTracks++; + } + } + + registry.fill(HIST("hMultiplicityPreSelection"), nTracks); + if (nTracks < cfgXicCand.multMin || nTracks > cfgXicCand.multMax) { + return; + } + registry.fill(HIST("hMultiplicity"), nTracks); + + int countCand = 1; + + // Xic-Lambda correlation (same event) + for (const auto& candidate : candidates) { + double massCand = -999.0; + double yCand = -999.0; + double ptCand = -999.0; + double absPtCand = -999.0; + double etaCand = -999.0; + double phiCand = -999.0; + bool selXicCand = false; + + // float xicType = getXicType(candidate); + + // Determine mass and rapidity based on Xic type + if constexpr (!IsXicPlus) { + massCand = candidate.invMassCharmBaryon(); + yCand = candidate.kfRapXic(); + ptCand = -RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()) * candidate.signDecay(); + etaCand = candidate.etaCharmBaryon(); + phiCand = RecoDecay::phi(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()); + selXicCand = candidate.resultSelections() >= cfgXicCand.selectionFlagXic; + const auto& probs = candidate.mlProbToXiPi(); + fillMlOutput(probs, outputMlXic); + } else { // XicPlus + massCand = candidate.invMassXicPlus(); + yCand = candidate.y(o2::constants::physics::MassXiCPlus); + ptCand = candidate.pt() * candidate.sign(); + etaCand = candidate.eta(); + phiCand = candidate.phi(); + selXicCand = candidate.isSelXicToXiPiPi() >= cfgXicCand.selectionFlagXic; + const auto& probs = candidate.mlProbXicToXiPiPi(); + fillMlOutput(probs, outputMlXic); + } + + absPtCand = std::abs(ptCand); + registry.fill(HIST("hPtCandXic"), absPtCand); + + phiCand = RecoDecay::constrainAngle(phiCand, -PIHalf); + + if ((std::abs(yCand) > cfgXicCand.yCandMax) || absPtCand < cfgXicCand.ptCandMin || absPtCand > cfgXicCand.ptCandMax) { + continue; + } + + registry.fill(HIST("hY"), yCand); + registry.fill(HIST("hEta"), etaCand); + registry.fill(HIST("hPhi"), phiCand); + registry.fill(HIST("hCandBin"), poolBin); + + if (cfgXicCand.applyEfficiency) { + efficiencyWeightCand = 1. / cfgXicCand.efficiencyXic->at(o2::analysis::findBin(cfgXicCand.binsPtEfficiencyXic, absPtCand)); + } + + if constexpr (IsMcRec) { + isPrompt = candidate.originMcRec() == RecoDecay::OriginType::Prompt; + isNonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; + + isSignal = IsXicPlus ? (std::abs(candidate.flagMcMatchRec()) == o2::aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) : (std::abs(candidate.flagMcMatchRec()) == static_cast(BIT(aod::hf_cand_xic0_omegac0::DecayType::XiczeroToXiPi))); + + if (isSignal) { + registry.fill(HIST("hPtCandSig"), absPtCand); + registry.fill(HIST("hEtaSig"), etaCand); + registry.fill(HIST("hPhiSig"), phiCand); + registry.fill(HIST("hYSig"), yCand); + } + } + + if (selXicCand) { + if (IsXicPlus) { + registry.fill(HIST("hMassXicPlusVsPt"), massCand, absPtCand, efficiencyWeightCand); + registry.fill(HIST("hMassXicPlusData"), massCand, efficiencyWeightCand); + } else { // Xic0 + registry.fill(HIST("hMassXic0VsPt"), massCand, absPtCand, efficiencyWeightCand); + registry.fill(HIST("hMassXic0Data"), massCand, efficiencyWeightCand); + } + + if (isPrompt) { + registry.fill(HIST("hPtCandSigPrompt"), absPtCand); + registry.fill(HIST("hPtVsMultiplicityMcRecPrompt"), absPtCand, multiplicityFT0M); + } else if (isNonPrompt) { + registry.fill(HIST("hPtCandSigNonPrompt"), absPtCand); + registry.fill(HIST("hPtVsMultiplicityMcRecNonPrompt"), absPtCand, multiplicityFT0M); + } + + entryCandRecoInfo(massCand, ptCand, outputMlXic[0], outputMlXic[1], poolBin); + entryCandCandGenInfo(isPrompt); + if (!skipMixedEventTableFilling) { + entryCand(phiCand, etaCand, ptCand, massCand, poolBin, gCollisionId, timeStamp); + // entryCandCharge(xicType); + } + } + + registry.fill(HIST("hCandBin"), poolBin); + + // Correlate Xic with all Lambda V0 in the same event + for (const auto& v0 : v0s) { + if (!selectionV0(collision, v0)) { + continue; + } + auto const& trackV0Pos = v0.template posTrack_as(); + auto const& trackV0Neg = v0.template negTrack_as(); + + if (cfgV0.cfgIsCorrCollMatchV0 && ((v0.collisionId() != trackV0Pos.collisionId()) || (v0.collisionId() != trackV0Neg.collisionId()))) { + continue; + } + + // Process Lambda (proton-pion) + if (std::abs(o2::constants::physics::MassLambda - v0.mLambda()) < cfgV0.cfgHypMassWindow && v0.alpha() > 0) { + if (isSelectedV0Daughter(trackV0Pos, v0, kProton) && isSelectedV0Daughter(trackV0Neg, v0, kPiMinus)) { + + if (selXicCand) { + fillCorrelationTable(cfgXicCand.fillTrkPID, v0, ptCand, etaCand, phiCand, outputMlXic, poolBin, correlationStatus, yCand, massCand, *mcParticles); + } + + if (countCand == 1) { + if (!skipMixedEventTableFilling) { + entryHadron(v0.phi(), v0.eta(), v0.pt() * static_cast(V0LambdaType::Lambda), poolBin, gCollisionId, timeStamp); + entryV0InvMass(v0.mLambda(), v0.mAntiLambda()); + registry.fill(HIST("hTracksBin"), poolBin); + } + } + } + } + + // Process anti-Lambda (anti-proton-pion) + if (std::abs(o2::constants::physics::MassLambda - v0.mAntiLambda()) < cfgV0.cfgHypMassWindow && v0.alpha() < 0) { + if (isSelectedV0Daughter(trackV0Neg, v0, kProtonBar) && isSelectedV0Daughter(trackV0Pos, v0, kPiPlus)) { + + if (selXicCand) { + fillCorrelationTable(cfgXicCand.fillTrkPID, v0, ptCand, etaCand, phiCand, outputMlXic, poolBin, correlationStatus, yCand, massCand, *mcParticles); + } + + if (countCand == 1) { + if (!skipMixedEventTableFilling) { + entryHadron(v0.phi(), v0.eta(), v0.pt() * static_cast(V0LambdaType::AntiLambda), poolBin, gCollisionId, timeStamp); + entryV0InvMass(v0.mAntiLambda(), v0.mLambda()); + registry.fill(HIST("hTracksBin"), poolBin); + } + } + } + } + + } // end v0 loop + + countCand++; + } // end outer Xic loop + registry.fill(HIST("hZvtx"), collision.posZ()); + registry.fill(HIST("hMultFT0M"), multiplicityFT0M); + } + + template + void calculateTrkEff(T1 const& trackPos1, T2 const& trackPos2, McPart const& mcParticles) + { + decltype(trackPos1.template mcParticle_as()) mctrk{}; + if (trackPos1.has_mcParticle()) { + mctrk = trackPos1.template mcParticle_as(); + } else if (trackPos2.has_mcParticle()) { + mctrk = trackPos2.template mcParticle_as(); + } else { + return; + } + auto gentracks = mcParticles.sliceBy(perTrueCollision, mctrk.mcCollisionId()); + for (const auto& track : gentracks) { + if (std::abs(track.eta()) > cfgXicCand.etaTrackMax || track.pt() < cfgXicCand.ptTrackMin || track.pt() > cfgXicCand.ptTrackMax) { + continue; + } + if ((std::abs(track.pdgCode()) != kElectron) && (std::abs(track.pdgCode()) != kMuonMinus) && (std::abs(track.pdgCode()) != kPiPlus) && (std::abs(track.pdgCode()) != kKPlus) && (std::abs(track.pdgCode()) != kProton)) { + continue; + } + if (cfgXicCand.pidTrkApplied && (std::abs(track.pdgCode()) != cfgXicCand.particlePdg)) { + continue; + } + + if (!track.isPhysicalPrimary()) { + continue; + } + + auto motherTrkGen = mcParticles.iteratorAt(track.mothersIds()[0]); + if (std::abs(motherTrkGen.pdgCode()) == kXiC0 || std::abs(motherTrkGen.pdgCode()) == kXiCPlus) { + continue; + } + + auto chargeTrack = pdg->GetParticle(track.pdgCode())->Charge(); + registry.fill(HIST("hPtTracksVsSignGen"), track.pt(), chargeTrack / (std::abs(chargeTrack))); + } + } + + template + void fillCorrelationTable(bool trkPidFill, AssocType const& assoc, float const candPt, float const candEta, float const candPhi, + const std::vector& outMl, int binPool, int8_t correlStatus, + double yCand, double candMass, McPart const& mcParticles) + { + bool isPhysicalPrimary = false; + int trackOrigin = -1; + float const cent = 100.f; + double massCandHadron = -999.0; + double ptCombined = -999.0; + double yAssoc = -999.0; + int signAssoc = 0; + auto pVec1 = RecoDecayPtEtaPhi::pVector(candPt, candEta, candPhi); + double massPart2 = -999.0; + std::array pVec2{assoc.px(), assoc.py(), assoc.pz()}; + std::array moms{pVec1, pVec2}; + + if constexpr (LambdaType == V0LambdaType::Lambda) { // Lambda + massPart2 = assoc.mLambda(); + entryPairedV0InvMass(assoc.mLambda(), assoc.mAntiLambda()); + signAssoc = V0LambdaType::Lambda; + yAssoc = assoc.yLambda(); + } else if constexpr (LambdaType == V0LambdaType::AntiLambda) { // AntiLambda + massPart2 = assoc.mAntiLambda(); + signAssoc = V0LambdaType::AntiLambda; + yAssoc = assoc.yLambda(); + entryPairedV0InvMass(assoc.mAntiLambda(), assoc.mLambda()); + } else { // Regular track + massPart2 = getMassFromPdg(cfgXicCand.particlePdg); + signAssoc = assoc.sign(); + yAssoc = assoc.rapidity(getMassFromPdg(cfgXicCand.particlePdg)); + } + + std::array masses{candMass, massPart2}; + ptCombined = RecoDecay::pt(pVec1, pVec2); + massCandHadron = RecoDecay::m(moms, masses); + + registry.fill(HIST("hXicHadronInvMassVsPt"), massCandHadron, ptCombined); + registry.fill(HIST("hXicHadronInvMass"), massCandHadron); + registry.fill(HIST("hXicHadronPtCombined"), ptCombined); + + entryCandHadronPair(getDeltaPhi(assoc.phi(), candPhi), + assoc.eta() - candEta, + candPt, + assoc.pt() * signAssoc, + binPool, + correlStatus, + cent); // will be added later if required + entryCandHadronPairY(yAssoc - yCand); + entryCandHadronMlInfo(outMl[0], outMl[1]); + entryCandHadronRecoInfo(candMass, false); + + if constexpr (LambdaType == V0LambdaType::NotLambda) { // Regular track + entryTrackRecoInfo(assoc.dcaXY(), assoc.dcaZ(), assoc.tpcNClsCrossedRows()); + if (trkPidFill) { + entryCandHadronPairTrkPID(assoc.tpcNSigmaPr(), assoc.tpcNSigmaKa(), assoc.tpcNSigmaPi(), assoc.tofNSigmaPr(), assoc.tofNSigmaKa(), assoc.tofNSigmaPi()); + } + } + entryXicHadronInvMass(massCandHadron, ptCombined); + + if constexpr (IsMcRec) { + if (assoc.has_mcParticle()) { + auto mcParticle = assoc.template mcParticle_as(); + isPhysicalPrimary = mcParticle.isPhysicalPrimary(); + trackOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, mcParticle, true); + entryCandHadronGenInfo(isPrompt, isPhysicalPrimary, trackOrigin); + } else { + entryCandHadronGenInfo(isPrompt, false, 0); + registry.fill(HIST("hFakeTracksMcRec"), assoc.pt()); + } + registry.fill(HIST("hPtParticleAssocVsCandMcRec"), assoc.pt(), candPt); + if (isPhysicalPrimary) { + registry.fill(HIST("hPtPrimaryParticleAssocVsCandMcRec"), assoc.pt(), candPt); + } + } + } + + // ============================================================================ + // SAME EVENT PROCESSING (WITH REGULAR HADRON TRACKS) + // ============================================================================ + + template + void doSameEvent(CollisionType const& collision, + TrackType const& tracks, + CandType const& candidates, + aod::McParticles const* mcParticles = nullptr) + { + int nTracks = 0; + int64_t timeStamp = 0; + bool skipMixedEventTableFilling = false; + float const multiplicityFT0M = collision.multFT0M(); + int gCollisionId = collision.globalIndex(); + + if (cfgXicCand.eventFractionToAnalyze > 0) { + if (rnd.Uniform(0, 1) > cfgXicCand.eventFractionToAnalyze) { + skipMixedEventTableFilling = true; + } + } + + if constexpr (!IsMcRec) { + timeStamp = collision.template bc_as().timestamp(); + } + + poolBin = corrBinning.getBin(std::make_tuple(collision.posZ(), multiplicityFT0M)); + + if (cfgXicCand.correlateXicWithLeadingParticle) { + leadingIndex = findLeadingParticle(tracks, cfgXicCand.etaTrackMax.value); + } + + if (collision.numContrib() > 1) { + for (const auto& track : tracks) { + if (std::abs(track.eta()) > cfgXicCand.etaTrackMax || std::abs(track.dcaXY()) > cfgXicCand.dcaXYTrackMax || std::abs(track.dcaZ()) > cfgXicCand.dcaZTrackMax) { + continue; + } + nTracks++; + } + } + + registry.fill(HIST("hMultiplicityPreSelection"), nTracks); + if (nTracks < cfgXicCand.multMin || nTracks > cfgXicCand.multMax) { + return; + } + registry.fill(HIST("hMultiplicity"), nTracks); + + int countCand = 1; + + for (const auto& candidate : candidates) { + double efficiencyWeightCand = 1.; + double yCand = -999.0; + double etaCand = -999.0; + double ptCand = -999.0; + double phiCand = -999.0; + double massCand = -999.0; + bool selXicCand = false; + bool isCandidateDaughter = true; + + if constexpr (!IsXicPlus) { + massCand = candidate.invMassCharmBaryon(); + yCand = candidate.kfRapXic(); // yCand = candidate.y(o2::constants::physics::MassXiC0); + ptCand = -RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()) * candidate.signDecay(); + etaCand = candidate.etaCharmBaryon(); + phiCand = RecoDecay::phi(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()); + selXicCand = candidate.resultSelections() >= cfgXicCand.selectionFlagXic; + const auto& probs = candidate.mlProbToXiPi(); + fillMlOutput(probs, outputMlXic); + } else { // XicPlus + + massCand = candidate.invMassXicPlus(); + yCand = candidate.y(o2::constants::physics::MassXiCPlus); + ptCand = candidate.pt() * candidate.sign(); + etaCand = candidate.eta(); + phiCand = candidate.phi(); + selXicCand = candidate.isSelXicToXiPiPi() >= cfgXicCand.selectionFlagXic; + const auto& probs = candidate.mlProbXicToXiPiPi(); + fillMlOutput(probs, outputMlXic); + } + + double absPtCand = std::abs(ptCand); + registry.fill(HIST("hPtCandXic"), absPtCand); + + if ((std::abs(yCand) > cfgXicCand.yCandMax) || absPtCand < cfgXicCand.ptCandMin || absPtCand > cfgXicCand.ptCandMax) { + continue; + } + + registry.fill(HIST("hY"), yCand); + registry.fill(HIST("hEta"), etaCand); + registry.fill(HIST("hPhi"), phiCand); + registry.fill(HIST("hCandBin"), poolBin); + + if (cfgXicCand.applyEfficiency) { + efficiencyWeightCand = 1. / cfgXicCand.efficiencyXic->at(o2::analysis::findBin(cfgXicCand.binsPtEfficiencyXic, absPtCand)); + } + + if constexpr (IsMcRec) { + isPrompt = candidate.originMcRec() == RecoDecay::OriginType::Prompt; + isNonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; + + isSignal = std::abs(candidate.flagMcMatchRec()) == o2::aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi; + // auto trackProng0 = candidate.template prong0_as(); + // auto trackProng1 = candidate.template prong1_as(); + if (cfgXicCand.calTrkEff && countCand == 1 && (isSignal || !cfgXicCand.calEffEventWithCand)) { + // calculateTrkEff can be added here if needed + } + } + + if (isSignal) { + registry.fill(HIST("hPtCandSig"), absPtCand); + registry.fill(HIST("hEtaSig"), etaCand); + registry.fill(HIST("hPhiSig"), phiCand); + registry.fill(HIST("hYSig"), yCand); + } + + if (selXicCand) { + if (IsXicPlus) { + registry.fill(HIST("hMassXicPlusVsPt"), massCand, absPtCand, efficiencyWeightCand); + registry.fill(HIST("hMassXicPlusData"), massCand, efficiencyWeightCand); + } else { // Xic0 + registry.fill(HIST("hMassXic0VsPt"), massCand, absPtCand, efficiencyWeightCand); + registry.fill(HIST("hMassXic0Data"), massCand, efficiencyWeightCand); + } + + if (isPrompt) { + registry.fill(HIST("hPtCandSigPrompt"), absPtCand); + registry.fill(HIST("hPtVsMultiplicityMcRecPrompt"), absPtCand, multiplicityFT0M); + } else if (isNonPrompt) { + registry.fill(HIST("hPtCandSigNonPrompt"), absPtCand); + registry.fill(HIST("hPtVsMultiplicityMcRecNonPrompt"), absPtCand, multiplicityFT0M); + } + + entryCandRecoInfo(massCand, ptCand, outputMlXic[0], outputMlXic[1], poolBin); + entryCandCandGenInfo(isPrompt); + if (!skipMixedEventTableFilling) { + entryCand(phiCand, etaCand, ptCand, massCand, poolBin, gCollisionId, timeStamp); + } + } + + registry.fill(HIST("hCandBin"), poolBin); + + // Correlation with hadrons + for (const auto& track : tracks) { + + isCandidateDaughter = (candidate.bachelorId() == track.globalIndex()) || (candidate.posTrackId() == track.globalIndex()) || (candidate.negTrackId() == track.globalIndex()); + + if constexpr (IsXicPlus) { + isCandidateDaughter = (candidate.pi0Id() == track.globalIndex()) || (candidate.pi1Id() == track.globalIndex()) || isCandidateDaughter; + } else { + isCandidateDaughter = (candidate.bachelorFromCharmBaryonId() == track.globalIndex()) || isCandidateDaughter; + } + + if (isCandidateDaughter && !cfgXicCand.storeAutoCorrelationFlag) { + continue; + } + if (isCandidateDaughter) { + correlationStatus = true; + } + if (!track.isGlobalTrackWoDCA()) { + continue; + } + + if (cfgXicCand.pidTrkApplied) { + if (!passPIDSelection(track, cfgXicCand.trkPIDspecies, cfgXicCand.pidTPCMax, cfgXicCand.pidTOFMax, cfgXicCand.tofPIDThreshold, cfgXicCand.forceTOF)) { + continue; + } + } + + if (cfgXicCand.correlateXicWithLeadingParticle) { + if (track.globalIndex() != leadingIndex) { + continue; + } + } + + if constexpr (IsMcRec) { + if (cfgXicCand.calTrkEff && countCand == 1 && (isSignal || !cfgXicCand.calEffEventWithCand) && track.has_mcParticle()) { + auto mcParticle = track.template mcParticle_as(); + if (!mcParticle.isPhysicalPrimary() && cfgXicCand.isRecTrkPhyPrimary) { + continue; + } + + auto motherTrk = mcParticles->iteratorAt(mcParticle.mothersIds()[0]); + if (std::abs(motherTrk.pdgCode()) == kXiCPlus || std::abs(motherTrk.pdgCode()) == kXiC0) { + continue; + } + + registry.fill(HIST("hPtTracksVsSignRec"), track.pt(), track.sign()); + if (std::abs(mcParticle.pdgCode()) == cfgXicCand.particlePdg) { + registry.fill(HIST("hPtTracksVsSignRecTrue"), track.pt(), track.sign()); + } + } else { + registry.fill(HIST("hFakeTracksMcRec"), track.pt()); + } + } + + if (selXicCand) { + fillCorrelationTable(cfgXicCand.fillTrkPID, track, ptCand, etaCand, phiCand, outputMlXic, poolBin, correlationStatus, yCand, massCand, *mcParticles); + } + + if (countCand == 1) { + if (!skipMixedEventTableFilling) { + entryHadron(track.phi(), track.eta(), track.pt() * track.sign(), poolBin, gCollisionId, timeStamp); + if (cfgXicCand.fillTrkPID) { + entryTrkPID(track.tpcNSigmaPr(), track.tpcNSigmaKa(), track.tpcNSigmaPi(), track.tofNSigmaPr(), track.tofNSigmaKa(), track.tofNSigmaPi()); + } + registry.fill(HIST("hTracksBin"), poolBin); + } + } + } // end Hadron Tracks loop + countCand++; + } // end outer Xic loop + registry.fill(HIST("hZvtx"), collision.posZ()); + registry.fill(HIST("hMultFT0M"), multiplicityFT0M); + } + // ============================================================================ + // MIXED EVENT PROCESSING + // ============================================================================ + + template + void doMixEvent(CollisionType const& collisions, + AssociateType const& tracks, + CandType const& candidates, + TrackType const&, + aod::McParticles const* mcParticles = nullptr) + { + if (candidates.size() == 0) { + return; + } + + double yCand = -999.; + double ptCand = -999.; + double phiCand = -999.; + double etaCand = -999.; + double massCand = -999.0; + bool selCand = false; + + auto tracksTuple = std::make_tuple(candidates, tracks); + Pair const pairData{corrBinning, cfgXicCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; + + for (const auto& [c1, tracks1, c2, tracks2] : pairData) { + poolBin = corrBinning.getBin(std::make_tuple(c2.posZ(), c2.multFT0M())); + poolBinXic = corrBinning.getBin(std::make_tuple(c1.posZ(), c1.multFT0M())); + registry.fill(HIST("hMultFT0M"), c1.multFT0M()); + registry.fill(HIST("hZvtx"), c1.posZ()); + registry.fill(HIST("hTracksPoolBin"), poolBin); + registry.fill(HIST("hXicPoolBin"), poolBinXic); + + for (const auto& [candidate, assocParticle] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if constexpr (!IsXicPlus) { + massCand = candidate.invMassCharmBaryon(); + yCand = candidate.kfRapXic(); + ptCand = -RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()) * candidate.signDecay(); + etaCand = candidate.etaCharmBaryon(); + phiCand = RecoDecay::phi(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()); + selCand = candidate.resultSelections() >= cfgXicCand.selectionFlagXic; + const auto& probs = candidate.mlProbToXiPi(); + fillMlOutput(probs, outputMlXic); + } else { + massCand = candidate.invMassXicPlus(); + yCand = candidate.y(o2::constants::physics::MassXiCPlus); + ptCand = candidate.pt() * candidate.sign(); + etaCand = candidate.eta(); + phiCand = candidate.phi(); + selCand = candidate.isSelXicToXiPiPi() >= cfgXicCand.selectionFlagXic; + const auto& probs = candidate.mlProbXicToXiPiPi(); + fillMlOutput(probs, outputMlXic); + } + + if (!selCand) { + continue; + } + if constexpr (IsMcRec) { + isPrompt = candidate.originMcRec() == RecoDecay::OriginType::Prompt; + isNonPrompt = candidate.originMcRec() == RecoDecay::OriginType::NonPrompt; + } + + if (std::abs(yCand) > cfgXicCand.yCandMax) { + continue; + } + + if constexpr (!isV0) { + if (cfgXicCand.corrParticle != corr_particle_type::V0 && !assocParticle.isGlobalTrackWoDCA()) { + continue; + } + if (cfgXicCand.pidTrkApplied) { + if (!passPIDSelection(assocParticle, cfgXicCand.trkPIDspecies, cfgXicCand.pidTPCMax, cfgXicCand.pidTOFMax, cfgXicCand.tofPIDThreshold, cfgXicCand.forceTOF)) { + continue; + } + } + } else { + + auto const& trackV0Pos = assocParticle.template posTrack_as(); + auto const& trackV0Neg = assocParticle.template negTrack_as(); + + if (std::abs(o2::constants::physics::MassLambda - assocParticle.mLambda()) < cfgV0.cfgHypMassWindow && assocParticle.alpha() > 0) { + if (isSelectedV0Daughter(trackV0Pos, assocParticle, kProton) && isSelectedV0Daughter(trackV0Neg, assocParticle, kPiPlus)) { + + fillCorrelationTable(cfgXicCand.fillTrkPID, assocParticle, ptCand, etaCand, phiCand, outputMlXic, poolBin, correlationStatus, yCand, massCand, *mcParticles); + } + } + + if (std::abs(o2::constants::physics::MassLambda - assocParticle.mAntiLambda()) < cfgV0.cfgHypMassWindow && assocParticle.alpha() < 0) { + if (isSelectedV0Daughter(trackV0Neg, assocParticle, -kProton) && isSelectedV0Daughter(trackV0Pos, assocParticle, -kPiPlus)) { + fillCorrelationTable(cfgXicCand.fillTrkPID, assocParticle, ptCand, etaCand, phiCand, outputMlXic, poolBin, correlationStatus, yCand, massCand, *mcParticles); + } + } + } + if (isPrompt) { + registry.fill(HIST("hPtCandMcRecSigPrompt"), ptCand); + registry.fill(HIST("hPtVsMultiplicityMcRecPrompt"), ptCand, 0); + } else if (isNonPrompt) { + registry.fill(HIST("hPtCandMcRecSigNonPrompt"), ptCand); + registry.fill(HIST("hPtVsMultiplicityMcRecNonPrompt"), ptCand, 0); + } + } + } + } + + // ============================================================================ + // MC GEN LEVEL PROCESSING + // ============================================================================ + + template + void doSameEventMcGen(CollisionType const& mcCollision, PartType const& mcParticles) + { + int counterCharmCand = 0; + int8_t candSign = 0; + + registry.fill(HIST("hMcEvtCount"), 0); + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; + poolBin = corrBinningMcGen.getBin(std::make_tuple(mcCollision.posZ(), mcCollision.multMCFT0A())); + registry.fill(HIST("hMultFT0AMcGen"), mcCollision.multMCFT0A()); + + // find leading particle + if (cfgXicCand.correlateXicWithLeadingParticle) { + leadingIndex = findLeadingParticleMcGen(mcParticles, cfgXicCand.etaTrackMax.value, cfgXicCand.ptTrackMin.value); + } + + // Mc Gen level + for (const auto& particle : mcParticles) { + if ((IsXicPlus && std::abs(particle.pdgCode()) != kXiCPlus) || (!IsXicPlus && std::abs(particle.pdgCode()) != kXiC0)) { + continue; + } + + if (particle.pdgCode() > 0) { + candSign = ParticleType::Particle; + } else { + candSign = ParticleType::AntiParticle; + } + + double const massCand = (std::abs(particle.pdgCode()) == kXiC0) ? o2::constants::physics::MassXiC0 : o2::constants::physics::MassXiCPlus; + double const yCand = RecoDecay::y(particle.pVector(), massCand); + + if (std::abs(yCand) > cfgXicCand.yCandGenMax || particle.pt() < cfgXicCand.ptCandMin) { + continue; + } + + registry.fill(HIST("hCandBin"), poolBin); + registry.fill(HIST("hPtCandMcGen"), particle.pt()); + registry.fill(HIST("hEtaMcGen"), particle.eta()); + registry.fill(HIST("hPhiMcGen"), RecoDecay::constrainAngle(particle.phi(), -PIHalf)); + registry.fill(HIST("hYMcGen"), yCand); + + isPrompt = particle.originMcGen() == RecoDecay::OriginType::Prompt; + isNonPrompt = particle.originMcGen() == RecoDecay::OriginType::NonPrompt; + + if (isPrompt) { + registry.fill(HIST("hPtCandMcGenPrompt"), particle.pt()); + registry.fill(HIST("hPtCandVsChargeMcGenPrompt"), particle.pt(), candSign); + } else if (isNonPrompt) { + registry.fill(HIST("hPtCandMcGenNonPrompt"), particle.pt()); + registry.fill(HIST("hPtCandVsChargeMcGenNonPrompt"), particle.pt(), candSign); + } + + // Xic Hadron correlation dedicated section + registry.fill(HIST("hcountCandtriggersMcGen"), 0, particle.pt()); + + std::vector listDaughters{}; + listDaughters.clear(); + const std::size_t nDaughtersExpected = IsXicPlus ? XicDecayDaughtersCount::XicPlusDaughtersCount : XicDecayDaughtersCount::Xic0DaughtersCount; + + if (IsXicPlus) { + // Final state: p, pi-, pi-, pi+, pi+ + std::array const arrDaughXicPlusPDG = {kProton, kPiMinus, kPiMinus, kPiPlus, kPiPlus}; + + // Pass -1 to automatically traverse the entire cascade down to the final state + RecoDecay::getDaughters(particle, &listDaughters, arrDaughXicPlusPDG); + } else { + // Final state: p, pi-, pi-, pi+ + std::array const arrDaughXic0PDG = {kProton, kPiMinus, kPiMinus, kPiPlus}; + + // Pass -1 to automatically traverse the entire cascade down to the final state + RecoDecay::getDaughters(particle, &listDaughters, arrDaughXic0PDG); + } + + int counterDaughters = 0; + std::vector prongsId(nDaughtersExpected); + if (listDaughters.size() == nDaughtersExpected) { + for (const auto& dauIdx : listDaughters) { + auto daughI = mcParticles.rawIteratorAt(dauIdx - mcParticles.offset()); + counterDaughters += 1; + prongsId[counterDaughters - 1] = daughI.globalIndex(); + } + } + counterCharmCand++; + + for (const auto& particleAssoc : mcParticles) { + if (std::abs(particleAssoc.eta()) > cfgXicCand.etaTrackMax || particleAssoc.pt() < cfgXicCand.ptTrackMin || particleAssoc.pt() > cfgXicCand.ptTrackMax) { + continue; + } + + if (cfgXicCand.corrParticle == corr_particle_type::IdentifiedParticle && ((std::abs(particleAssoc.pdgCode()) != kElectron) && (std::abs(particleAssoc.pdgCode()) != kMuonMinus) && (std::abs(particleAssoc.pdgCode()) != kPiPlus) && (std::abs(particleAssoc.pdgCode()) != kKPlus) && (std::abs(particleAssoc.pdgCode()) != kProton))) { + continue; + } + + if (cfgXicCand.corrParticle == corr_particle_type::V0 && (std::abs(particleAssoc.pdgCode()) != cfgXicCand.particlePdg)) { + continue; + } + + if (cfgXicCand.corrParticle == corr_particle_type::IdentifiedParticle && !particleAssoc.isPhysicalPrimary()) { + continue; + } + + if (cfgXicCand.correlateXicWithLeadingParticle) { + if (particleAssoc.globalIndex() != leadingIndex) { + continue; + } + } + + if (std::find(prongsId.begin(), prongsId.end(), particleAssoc.globalIndex()) != prongsId.end()) { + if (!cfgXicCand.storeAutoCorrelationFlag) { + continue; + } + correlationStatus = true; + } + + int trackOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, particleAssoc, true); + int8_t chargeAssoc = pdg->GetParticle(particleAssoc.pdgCode())->Charge(); + chargeAssoc = chargeAssoc / std::abs(chargeAssoc); + registry.fill(HIST("hPtParticleAssocMcGen"), particleAssoc.pt()); + float const cent = 100.0; + + entryCandHadronPair(getDeltaPhi(particleAssoc.phi(), particle.phi()), + particleAssoc.eta() - particle.eta(), + particle.pt() * candSign, + particleAssoc.pt() * chargeAssoc, + poolBin, + correlationStatus, + cent); + entryCandHadronPairY(RecoDecay::y(particleAssoc.pVector(), getMassFromPdg(particleAssoc.pdgCode())) - yCand); + entryCandHadronRecoInfo(massCand, true); + entryCandHadronGenInfo(isPrompt, particleAssoc.isPhysicalPrimary(), trackOrigin); + } // end inner loop + } // end outer loop + registry.fill(HIST("hcountCandHadronPerEvent"), counterCharmCand); + registry.fill(HIST("hZvtx"), mcCollision.posZ()); + } + + // ============================================================================ + // PROCESS FUNCTIONS + // ============================================================================ + + /// Data processing: XicPlus with regular hadron tracks + void processDataXicPlus(SelCollisions::iterator const& collision, + TracksData const& tracks, + CandsXicPlusDataFiltered const& candidates, + aod::BCsWithTimestamps const&) + { + + doSameEvent(collision, tracks, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataXicPlus, "Process data XicPlus", true); + + /// MC Reco processing: XicPlus with regular hadron tracks + void processMcRecXicPlus(SelCollisions::iterator const& collision, + TracksWithMc const& tracks, + CandsXicPlusMcRecFiltered const& candidates, + aod::McParticles const& mcParticles) + { + doSameEvent(collision, tracks, candidates, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecXicPlus, "Process Mc Reco mode Xic", false); + + /// Data processing: Xic0 with regular hadron tracks + void processDataXic0(SelCollisions::iterator const& collision, + TracksData const& tracks, + CandsXic0DataFiltered const& candidates, + aod::BCsWithTimestamps const&) + { + doSameEvent(collision, tracks, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataXic0, "Process data Xic0", false); + + /// MC Reco processing: Xic0 with regular hadron tracks + void processMcRecXic0(SelCollisions::iterator const& collision, + TracksWithMc const& tracks, + CandsXic0McRecFiltered const& candidates, + aod::McParticles const& mcParticles) + { + doSameEvent(collision, tracks, candidates, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecXic0, "Process Mc Reco mode Xic0", false); + + /// Data processing: XicPlus with V0 Lambda + void processDataXicPlusV0(SelCollisions::iterator const& collision, + TracksData const& tracks, + soa::Join const& v0s, + CandsXicPlusDataFiltered const& candidates, + aod::BCsWithTimestamps const&) + { + doSameEventWithV0(collision, v0s, tracks, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataXicPlusV0, "Data process for v0 lambda with Xic Plus", false); + + /// MC Reco processing: XicPlus with V0 Lambda + void processMcRecXicPlusV0(SelCollisions::iterator const& collision, + TracksWithMc const& tracks, + soa::Join const& v0s, + CandsXicPlusMcRecFiltered const& candidates, + aod::McParticles const& mcParticles) + { + doSameEventWithV0(collision, v0s, tracks, candidates, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecXicPlusV0, "Mc process for v0 lambda with Xic Plus", false); + + /// Data processing: Xic0 with V0 Lambda + void processDataXic0V0(SelCollisions::iterator const& collision, + TracksData const& tracks, + soa::Join const& v0s, + CandsXic0DataFiltered const& candidates, + aod::BCsWithTimestamps const&) + { + doSameEventWithV0(collision, v0s, tracks, candidates); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataXic0V0, "Data process for v0 lambda with Xic0", false); + + /// MC Reco processing: Xic0 with V0 Lambda + void processMcRecXic0V0(SelCollisions::iterator const& collision, + TracksWithMc const& tracks, + soa::Join const& v0s, + CandsXic0McRecFiltered const& candidates, + aod::McParticles const& mcParticles) + { + doSameEventWithV0(collision, v0s, tracks, candidates, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecXic0V0, "Mc process for v0 lambda with Xic0", false); + + /// MC Reco processing: Xic0 with V0 Lambda + void processV0McRec(SelCollisions::iterator const& collision, + TracksWithMc const& tracks, + soa::Join const& v0s, + aod::McParticles const& mcParticles) + { + fillEffV0(collision, v0s, tracks, mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processV0McRec, "Mc process for v0 lambda", false); + + // ============================================================================ + // MIXED EVENT PROCESS FUNCTIONS + // ============================================================================ + + /// Data Mixed Event: XicPlus with regular hadron tracks + void processDataMixedEventXicPlus(SelCollisions const& collisions, + CandsXicPlusDataFiltered const& candidates, + TracksData const& tracks) + { + doMixEvent(collisions, tracks, candidates, tracks); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataMixedEventXicPlus, "Process Mixed Event Data XicPlus", false); + + /// MC Reco Mixed Event: XicPlus with regular hadron tracks + void processMcRecMixedEventXicPlus(SelCollisions const& collisions, + CandsXicPlusMcRecFiltered const& candidates, + TracksWithMc const& tracks, + aod::McParticles const& mcParticles) + { + doMixEvent(collisions, tracks, candidates, tracks, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecMixedEventXicPlus, "Process Mixed Event McRec XicPlus", false); + + /// Data Mixed Event: Xic0 with regular hadron tracks + void processDataMixedEventXic0(SelCollisions const& collisions, + CandsXic0DataFiltered const& candidates, + TracksData const& tracks) + { + doMixEvent(collisions, tracks, candidates, tracks); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataMixedEventXic0, "Process Mixed Event Data Xic0", false); + + /// MC Reco Mixed Event: Xic0 with regular hadron tracks + void processMcRecMixedEventXic0(SelCollisions const& collisions, + CandsXic0McRecFiltered const& candidates, + TracksWithMc const& tracks, + aod::McParticles const& mcParticles) + { + doMixEvent(collisions, tracks, candidates, tracks, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecMixedEventXic0, "Process Mixed Event McRec Xic0", false); + + // ============================================================================ + // MIXED EVENT WITH V0 LAMBDA - NEW FUNCTIONS + // ============================================================================ + + /// Data Mixed Event: XicPlus with V0 Lambda + /// NOTE: V0 mixed events are more complex - need proper binning and collision matching + void processDataMixedEventXicPlusV0(SelCollisions const& collisions, + CandsXicPlusDataFiltered const& candidates, + soa::Join const& v0s, + TracksData const& tracks) + { + doMixEvent(collisions, v0s, candidates, tracks); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataMixedEventXicPlusV0, "Process Mixed Event Data XicPlus + V0Lambda", false); + + /// MC Reco Mixed Event: XicPlus with V0 Lambda + void processMcRecMixedEventXicPlusV0(SelCollisions const& collisions, + CandsXicPlusMcRecFiltered const& candidates, + soa::Join const& v0s, + TracksWithMc const& tracks, + aod::McParticles const& mcParticles) + { + doMixEvent(collisions, v0s, candidates, tracks, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecMixedEventXicPlusV0, "Process Mixed Event McRec XicPlus + V0Lambda", false); + + /// Data Mixed Event: Xic0 with V0 Lambda + void processDataMixedEventXic0V0(SelCollisions const& collisions, + CandsXic0DataFiltered const& candidates, + soa::Join const& v0s, + TracksData const& tracks) + { + doMixEvent(collisions, v0s, candidates, tracks); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processDataMixedEventXic0V0, "Process Mixed Event Data Xic0 + V0Lambda", false); + + /// MC Reco Mixed Event: Xic0 with V0 Lambda + void processMcRecMixedEventXic0V0(SelCollisions const& collisions, + CandsXic0McRecFiltered const& candidates, + soa::Join const& v0s, + TracksWithMc const& tracks, + aod::McParticles const& mcParticles) + { + doMixEvent(collisions, v0s, candidates, tracks, &mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcRecMixedEventXic0V0, "Process Mixed Event McRec Xic0 + V0Lambda", false); + + // ============================================================================ + // MC GEN LEVEL - SAME EVENT PROCESSING + // ============================================================================ + + /// MC Gen Same Event: XicPlus + void processMcGenXicPlus(SelCollisionsMc::iterator const& mcCollision, + CandidatesXicPlusMcGen const& mcParticles) + { + doSameEventMcGen<1>(mcCollision, mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcGenXicPlus, "Process Mc Gen XicPlus", false); + + /// MC Gen Same Event: Xic0 + void processMcGenXic0(SelCollisionsMc::iterator const& mcCollision, + CandidatesXic0McGen const& mcParticles) + { + doSameEventMcGen<0>(mcCollision, mcParticles); + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcGenXic0, "Process Mc Gen Xic0", false); + + void processMcGenMixedEvent(SelCollisionsMc const& collisions, + CandidatesXicPlusMcGen const& mcParticles) + { + BinningTypeMcGen const corrBinningMcGen{{binsZVtx, binsMultiplicityMc}}; + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + Pair const pairMcGen{corrBinningMcGen, cfgXicCand.numberEventsMixed, -1, collisions, tracksTuple, &cache}; + + for (const auto& [c1, tracks1, c2, tracks2] : pairMcGen) { + poolBin = corrBinningMcGen.getBin(std::make_tuple(c1.posZ(), c1.multMCFT0A())); + + for (const auto& [candidate, particleAssoc] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (std::abs(candidate.pdgCode()) != kXiCPlus) { + continue; + } + + double const massCand = (std::abs(candidate.pdgCode()) == kXiC0) ? o2::constants::physics::MassXiC0 : o2::constants::physics::MassXiCPlus; + double const yXic = RecoDecay::y(candidate.pVector(), massCand); + if (std::abs(yXic) > cfgXicCand.yCandGenMax || candidate.pt() < cfgXicCand.ptCandMin || candidate.pt() > cfgXicCand.ptCandMax) { + continue; + } + + if (std::abs(particleAssoc.eta()) > cfgXicCand.etaTrackMax || particleAssoc.pt() < cfgXicCand.ptTrackMin || particleAssoc.pt() > cfgXicCand.ptTrackMax) { + continue; + } + + if ((std::abs(particleAssoc.pdgCode()) != kElectron) && (std::abs(particleAssoc.pdgCode()) != kMuonMinus) && (std::abs(particleAssoc.pdgCode()) != kPiPlus) && (std::abs(particleAssoc.pdgCode()) != kKPlus) && (std::abs(particleAssoc.pdgCode()) != kProton)) { + continue; + } + + if (!particleAssoc.isPhysicalPrimary()) { + continue; + } + + if (cfgXicCand.pidTrkApplied && (std::abs(particleAssoc.pdgCode()) != cfgXicCand.particlePdg)) { + continue; + } + + // int8_t const chargeXic = pdg->GetParticle(candidate.pdgCode())->Charge(); + // float xicType = getXicTypeMC(candidate); + int8_t candSign = 0; + if (candidate.pdgCode() > 0) { + candSign = ParticleType::Particle; + } else { + candSign = ParticleType::AntiParticle; + } + + auto const chargeAssoc = pdg->GetParticle(particleAssoc.pdgCode())->Charge(); + float cent = 100.0; + + int trackOrigin = RecoDecay::getCharmHadronOrigin(mcParticles, particleAssoc, true); + bool isPromptXic = candidate.originMcGen() == RecoDecay::OriginType::Prompt; + + entryCandHadronPair(getDeltaPhi(particleAssoc.phi(), candidate.phi()), + particleAssoc.eta() - candidate.eta(), + candidate.pt() * candSign, + particleAssoc.pt() * chargeAssoc / std::abs(chargeAssoc), + poolBin, + correlationStatus, + cent); + entryCandHadronPairY(RecoDecay::y(particleAssoc.pVector(), getMassFromPdg(particleAssoc.pdgCode())) - yXic); // particleAssoc.rapidity(getMassFromPdg(particleAssoc.pdgCode())) + entryCandHadronRecoInfo(massCand, true); + entryCandHadronGenInfo(isPromptXic, particleAssoc.isPhysicalPrimary(), trackOrigin); + } + } + } + PROCESS_SWITCH(HfCorrelatorXicHadrons, processMcGenMixedEvent, "Process Mixed Event McGen", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context), + adaptAnalysisTask(context)}; +} diff --git a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx index 5636a0b8fa6..06ae7c8a758 100644 --- a/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx +++ b/PWGHF/HFC/TableProducer/derivedDataCreatorCorrelationsReduced.cxx @@ -65,6 +65,7 @@ enum CandidateType { D0ToPiK, D0ToKPi, LcToPKPi, + LcToPiKP, Hadron }; @@ -111,7 +112,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { double massCharm{0.}; TF1* funcDcaXYPtCutPrimTrk = nullptr; - using CollsWithCentMult = soa::Join; + using CollsWithCentMult = soa::Join; using CandDsData = soa::Filtered>; using CandDplusData = soa::Filtered>; using CandD0Data = soa::Filtered>; @@ -121,7 +122,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { Filter filterSelectDsCandidates = aod::hf_sel_candidate_ds::isSelDsToKKPi >= selectionFlag || aod::hf_sel_candidate_ds::isSelDsToPiKK >= selectionFlag; Filter filterSelectDplusCandidates = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlag; Filter filterSelectD0Candidates = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; - Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; + Filter filterSelectLcCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; Filter filterSelectTrkData = (nabs(aod::track::eta) < etaTrkMax) && (aod::track::pt > ptTrkMin) && (aod::track::pt < ptTrkMax) && (nabs(aod::track::dcaXY) < dcaXYTrkMax) && (nabs(aod::track::dcaZ) < dcaZTrkMax); Preslice candsDsPerColl = aod::hf_cand::collisionId; @@ -135,9 +136,10 @@ struct HfDerivedDataCreatorCorrelationsReduced { Partition selectedD0ToPiK = aod::hf_sel_candidate_d0::isSelD0 >= selectionFlag; Partition selectedD0ToKPi = aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlag; Partition selectedLcToPKPi = aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlag; + Partition selectedLcToPiKP = aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlag; ConfigurableAxis binsInvMass{"binsInvMass", {300, 1.6, 2.2}, ""}; - ConfigurableAxis binsMultFT0M{"binsMultFT0M", {100, 0., 10000.}, "Multiplicity as FT0M signal amplitude"}; + ConfigurableAxis binsMult{"binsMult", {100, 0., 10000.}, "Multiplicity bins"}; ConfigurableAxis binsCent{"binsCent", {100, 0., 100.}, "Centrality bins"}; ConfigurableAxis binsPosZ{"binsPosZ", {100, -10., 10.}, "Primary vertex z coordinate"}; ConfigurableAxis binsEta{"binsEta", {50, -2., 2.}, "Eta bins"}; @@ -172,12 +174,12 @@ struct HfDerivedDataCreatorCorrelationsReduced { ccdb->setLocalObjectValidityChecking(); const AxisSpec axisCent = {binsCent, "Centrality"}; - const AxisSpec axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; + const AxisSpec axisMult = {binsMult, "Multiplicity"}; const AxisSpec axisPosZ = {binsPosZ, "PosZ"}; const AxisSpec axisEta = {binsEta, "#it{#eta}"}; const AxisSpec axisPhi = {binsPhi, "#it{#varphi}"}; - const AxisSpec axisPtTrig = {(std::vector)binsPtTrig, "#it{p}_{T} Trig (GeV/#it{c})"}; - const AxisSpec axisPtAssoc = {(std::vector)binsPtAssoc, "#it{p}_{T} Assoc (GeV/#it{c})"}; + const AxisSpec axisPtTrig = {std::vector(binsPtTrig), "#it{p}_{T} Trig (GeV/#it{c})"}; + const AxisSpec axisPtAssoc = {std::vector(binsPtAssoc), "#it{p}_{T} Assoc (GeV/#it{c})"}; const AxisSpec axisDcaXY = {binsDca, "DCA XY (cm)"}; const AxisSpec axisDcaZ = {binsDca, "DCA Z (cm)"}; @@ -188,8 +190,10 @@ struct HfDerivedDataCreatorCorrelationsReduced { registry.add("hEtaVsPtTrigAssoc", "Associated particles etaVsPt", {HistType::kTH3F, {{axisEta}, {axisPtTrig}, {axisPtAssoc}}}); registry.add("hPhiVsPtAssoc", "Associated particles phiVsPt", {HistType::kTH2F, {{axisPhi}, {axisPtAssoc}}}); registry.add("hEtaVsPtAssoc", "Associated particles etaVsPt", {HistType::kTH2F, {{axisEta}, {axisPtAssoc}}}); - registry.add("hDcaXYVsPtAssoc", "Associated particles DCAxyVsPt", {HistType::kTH2F, {{axisDcaXY}, {axisPtAssoc}}}); - registry.add("hDcaZVsPtAssoc", "Associated particles DCAzVsPt", {HistType::kTH2F, {{axisDcaZ}, {axisPtAssoc}}}); + registry.add("hDcaXYVsPtVsCentAssoc", "Associated particles DCAxyVsPtVsCent", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaXY}, {axisCent}}}); + registry.add("hDcaZVsPtVsCentAssoc", "Associated particles DCAzVsPtVsCent", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaZ}, {axisCent}}}); + registry.add("hDcaXYVsPtVsMultAssoc", "Associated particles DCAxyVsPtVsMult", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaXY}, {axisMult}}}); + registry.add("hDcaZVsPtVsMultAssoc", "Associated particles DCAzVsPtVsMult", {HistType::kTH3F, {{axisPtAssoc}, {axisDcaZ}, {axisMult}}}); // Setup pt-dependent DCAxy cut function if (usePtDiffDcaXYCut) { @@ -222,6 +226,9 @@ struct HfDerivedDataCreatorCorrelationsReduced { if constexpr (CandType == CandidateType::LcToPKPi) { return HfHelper::invMassLcToPKPi(candidate); } + if constexpr (CandType == CandidateType::LcToPiKP) { + return HfHelper::invMassLcToPiKP(candidate); + } return -1.; } @@ -261,6 +268,11 @@ struct HfDerivedDataCreatorCorrelationsReduced { outputMl[iclass] = candidate.mlProbLcToPKPi()[classMl->at(iclass)]; } } + if constexpr (CandType == CandidateType::LcToPiKP) { + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + outputMl[iclass] = candidate.mlProbLcToPiKP()[classMl->at(iclass)]; + } + } return outputMl; } @@ -276,7 +288,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { y = HfHelper::yDplus(candidate); } else if constexpr (CandType == CandidateType::D0ToPiK || CandType == CandidateType::D0ToKPi) { y = HfHelper::yD0(candidate); - } else if constexpr (CandType == CandidateType::LcToPKPi) { + } else if constexpr (CandType == CandidateType::LcToPKPi || CandType == CandidateType::LcToPiKP) { y = HfHelper::yLc(candidate); } else { return true; @@ -349,11 +361,13 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Fill histograms and tables for same-event correlations /// \param trigCands are the trigger candidates /// \param assTrks are the associated tracks - /// \param collCentrality is the collision centrality + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity template void fillSameEvent(TTrigCands const& trigCands, TAssocTrks const& assTrks, - const float collCentrality) + const float cent, + const float mult) { for (const auto& trigCand : trigCands) { if (!cutCandRapidity(trigCand)) { @@ -384,7 +398,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } if (downSampleTrksFactor < 1.) { float const pseudoRndm = assTrkPt * 1000. - static_cast(assTrkPt * 1000); - if (assTrkPt < ptMaxForDownSample && collCentrality < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { + if (assTrkPt < ptMaxForDownSample && cent < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { continue; } } @@ -392,8 +406,10 @@ struct HfDerivedDataCreatorCorrelationsReduced { registry.fill(HIST("hEtaVsPtTrigAssoc"), assTrk.eta(), trigCandPt, assTrkPt); registry.fill(HIST("hPhiVsPtAssoc"), RecoDecay::constrainAngle(assTrk.phi(), -o2::constants::math::PIHalf), assTrkPt); registry.fill(HIST("hEtaVsPtAssoc"), assTrk.eta(), assTrkPt); - registry.fill(HIST("hDcaXYVsPtAssoc"), assTrk.dcaXY(), assTrkPt); - registry.fill(HIST("hDcaZVsPtAssoc"), assTrk.dcaZ(), assTrkPt); + registry.fill(HIST("hDcaXYVsPtVsCentAssoc"), assTrkPt, assTrk.dcaXY(), cent); + registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); + registry.fill(HIST("hDcaXYVsPtVsMultAssoc"), assTrkPt, assTrk.dcaXY(), mult); + registry.fill(HIST("hDcaZVsPtVsMultAssoc"), assTrkPt, assTrk.dcaZ(), mult); double deltaPhi = RecoDecay::constrainAngle(assTrk.phi() - trigCand.phi(), -o2::constants::math::PIHalf); rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); @@ -426,10 +442,12 @@ struct HfDerivedDataCreatorCorrelationsReduced { /// Fill track tables for mixed-event /// \param assTrks are the associated tracks - /// \param collCentrality is the collision centrality + /// \param cent is the collision centrality + /// \param mult is the collision multiplicity template void fillTrkMixedEvent(TAssocTrks const& assTrks, - const float collCentrality) + const float cent, + const float mult) { bool first = true; for (const auto& assTrk : assTrks) { @@ -445,15 +463,17 @@ struct HfDerivedDataCreatorCorrelationsReduced { } if (!first && downSampleTrksFactor < 1.) { // skip downsampling for the first track to avoid empty tables float const pseudoRndm = assTrkPt * 1000. - static_cast(assTrkPt * 1000); - if (assTrkPt < ptMaxForDownSample && collCentrality < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { + if (assTrkPt < ptMaxForDownSample && cent < centMaxForDownSample && pseudoRndm >= downSampleTrksFactor) { continue; } } first = false; registry.fill(HIST("hPhiVsPtAssoc"), RecoDecay::constrainAngle(assTrk.phi(), -o2::constants::math::PIHalf), assTrkPt); registry.fill(HIST("hEtaVsPtAssoc"), assTrk.eta(), assTrkPt); - registry.fill(HIST("hDcaXYVsPtAssoc"), assTrk.dcaXY(), assTrkPt); - registry.fill(HIST("hDcaZVsPtAssoc"), assTrk.dcaZ(), assTrkPt); + registry.fill(HIST("hDcaXYVsPtVsCentAssoc"), assTrkPt, assTrk.dcaXY(), cent); + registry.fill(HIST("hDcaZVsPtVsCentAssoc"), assTrkPt, assTrk.dcaZ(), cent); + registry.fill(HIST("hDcaXYVsPtVsMultAssoc"), assTrkPt, assTrk.dcaXY(), mult); + registry.fill(HIST("hDcaZVsPtVsMultAssoc"), assTrkPt, assTrk.dcaZ(), mult); rowAssocBases(rowCollisions.lastIndex(), assTrk.phi(), assTrk.eta(), assTrkPt); rowAssocTrkSels(assTrk.tpcNClsCrossedRows(), assTrk.itsClusterMap(), assTrk.itsNCls(), assTrk.dcaXY(), assTrk.dcaZ()); } @@ -472,7 +492,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDplus, tracks, cent); + fillSameEvent(candsDplus, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusSameEvent, "Process Same Event for Dplus candidates", true); @@ -490,7 +510,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsDplus); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDplusMixedEvent, "Process Mixed Event for Dplus candidates", false); @@ -509,8 +529,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsDsToPiKK, tracks, cent); - fillSameEvent(candsDsToKKPi, tracks, cent); + fillSameEvent(candsDsToPiKK, tracks, cent, mult); + fillSameEvent(candsDsToKKPi, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsSameEvent, "Process Same Event for Ds candidates", false); @@ -531,7 +551,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsDsToPiKK); fillCharmMixedEvent(candsDsToKKPi); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processDsMixedEvent, "Process Mixed Event for Ds candidates", false); @@ -550,8 +570,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsD0ToPiK, tracks, cent); - fillSameEvent(candsD0ToKPi, tracks, cent); + fillSameEvent(candsD0ToPiK, tracks, cent, mult); + fillSameEvent(candsD0ToKPi, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0SameEvent, "Process Same Event for D0 candidates", false); @@ -572,7 +592,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsD0ToPiK); fillCharmMixedEvent(candsD0ToKPi); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processD0MixedEvent, "Process Mixed Event for D0 candidates", false); @@ -582,7 +602,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { CandLcData const&) { auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - if (forceCharmInCollision && candsLcToPKPi.size() < 1) { + auto candsLcToPiKP = selectedLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { return; } float cent{-1.}, mult{-1.}; @@ -590,7 +611,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(candsLcToPKPi, tracks, cent); + fillSameEvent(candsLcToPKPi, tracks, cent, mult); + fillSameEvent(candsLcToPiKP, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcSameEvent, "Process Same Event for Lc candidates", false); @@ -600,7 +622,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { CandLcData const&) { auto candsLcToPKPi = selectedLcToPKPi->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); - if (forceCharmInCollision && candsLcToPKPi.size() < 1) { + auto candsLcToPiKP = selectedLcToPiKP->sliceByCached(aod::hf_cand::collisionId, coll.globalIndex(), cache); + if (forceCharmInCollision && candsLcToPKPi.size() < 1 && candsLcToPiKP.size() < 1) { return; } float cent{-1.}, mult{-1.}; @@ -609,7 +632,8 @@ struct HfDerivedDataCreatorCorrelationsReduced { } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); fillCharmMixedEvent(candsLcToPKPi); - fillTrkMixedEvent(tracks, cent); + fillCharmMixedEvent(candsLcToPiKP); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processLcMixedEvent, "Process Mixed Event for Lc candidates", false); @@ -622,7 +646,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillSameEvent(tracks, tracks, cent); + fillSameEvent(tracks, tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronSameEvent, "Process Same Event for hadron candidates", false); @@ -635,7 +659,7 @@ struct HfDerivedDataCreatorCorrelationsReduced { return; } rowCollisions(mult, coll.numContrib(), cent, coll.posZ()); - fillTrkMixedEvent(tracks, cent); + fillTrkMixedEvent(tracks, cent, mult); } PROCESS_SWITCH(HfDerivedDataCreatorCorrelationsReduced, processHadronHadronMixedEvent, "Process Mixed Event for hadron candidates", false); }; diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx new file mode 100644 index 00000000000..6ac7c687352 --- /dev/null +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsCharmFemtoDream.cxx @@ -0,0 +1,406 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file producerCharmHadronsCharmFemtoDream.cxx +/// \brief Produce reduced D-meson candidate tables for charm-charm femtoscopy +/// \author Biao Zhang, Heidelberg University, biao.zhang@cern.ch + +#include "PWGCF/DataModel/FemtoDerived.h" +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/HfMlResponseD0ToKPi.h" +#include "PWGHF/Core/HfMlResponseDstarToD0Pi.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsBfieldCCDB.h" +#include "PWGHF/Utils/utilsEvSelHf.h" + +#include "Common/Core/ZorroSummary.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::analysis; +using namespace o2::framework::expressions; + +struct HfProducerCharmHadronsCharmFemtoDream { + enum MlMode : uint8_t { + NoMl = 0, + FillMlFromSelector = 1, + FillMlFromNewBDT = 2 + }; + static constexpr std::size_t NMlClasses = 3; + static constexpr std::size_t NMlPtEdgesMin = 2; + static constexpr int ND0Hypotheses = 2; + // Each species needs its own model, feature order and pT-dependent cuts. + struct HfMlConfig : ConfigurableGroup { + std::string prefix; + Configurable mlApplicationMode{"mlApplicationMode", FillMlFromSelector, "0: no ML, 1: selector scores, 2: new BDT after selector"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for new BDT"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsDmesCutScore}, "New BDT cuts per pT bin: background, prompt, nonprompt"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Reject scores above (0), below (1), or do not cut (2)"}; + Configurable nClassesMl{"nClassesMl", int{hf_cuts_ml::NCutScores}, "Three output classes: background, prompt, nonprompt"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{}, "Ordered input feature names for new BDT"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{}, "Model files, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{}, "CCDB model paths, one per pT bin"}; + Configurable timestampCCDB{"timestampCCDB", -1, "Timestamp used to retrieve models"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Load new BDT from CCDB instead of local files"}; + }; + + Produces collisions; + Produces rowMasks; + Produces d0Rows; + Produces dstarRows; + + Configurable selectionFlagD0{"selectionFlagD0", 1, "Minimum D0 selector decision"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "Run 3 magnetic field"}; + HfMlConfig mlD0{.prefix = "mlD0"}, mlDstar{.prefix = "mlDstar"}; + + Service ccdb{}; + o2::ccdb::CcdbApi ccdbApi; + o2::hf_evsel::HfEventSelection hfEvSel; + o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; + o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; + + using Collisions = soa::Join; + using CollisionsWithFT0C = soa::Join; + using D0s = soa::Join; + using Dstars = soa::Join; + + using D0sMl = soa::Join; + using DstarsMl = soa::Join; + + Filter filterSelectCandidateD0 = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0 || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); + Filter filterSelectCandidateDstar = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; + + HistogramRegistry registry{"registry"}; + OutputObj zorroSummary{"zorroSummary"}; + + bool hasD0 = false; + bool hasDstar = false; + int runNumber = -1; + + template + void initMl(HfMlConfig const& cfg, Response& response, bool withMl) + { + if (cfg.mlApplicationMode.value < NoMl || cfg.mlApplicationMode.value > FillMlFromNewBDT) { + LOGP(fatal, "{}: invalid mlApplicationMode", cfg.prefix); + } + if (cfg.mlApplicationMode.value != FillMlFromNewBDT) { + return; + } + if (!withMl) { + LOGP(fatal, "{}: new BDT requires an Ml producer process", cfg.prefix); + } + auto const& edges = cfg.binsPtMl.value; + const bool areEdgesFinite = std::all_of(edges.begin(), edges.end(), [](double x) { return std::isfinite(x); }); + const bool areEdgesIncreasing = std::adjacent_find(edges.begin(), edges.end(), [](double a, double b) { return a >= b; }) == edges.end(); + if (edges.size() < NMlPtEdgesMin || !areEdgesFinite || !areEdgesIncreasing) { + LOGP(fatal, "{}: binsPtMl must be finite and strictly increasing", cfg.prefix); + } + const auto nBins = edges.size() - 1; + if (cfg.nClassesMl.value != NMlClasses || cfg.cutDirMl.value.size() != NMlClasses || cfg.cutsMl.value.rows() != nBins || cfg.cutsMl.value.cols() != NMlClasses || cfg.onnxFileNames.value.size() != nBins || cfg.namesInputFeatures.value.empty() || (cfg.loadModelsFromCCDB.value && cfg.modelPathsCCDB.value.size() != nBins)) { + LOGP(fatal, "{}: provide three classes, cuts/models for every pT bin, and input features", cfg.prefix); + } + for (const auto& direction : cfg.cutDirMl.value) { + if (direction < o2::cuts_ml::CutGreater || direction > o2::cuts_ml::CutNot) { + LOGP(fatal, "{}: invalid cutDirMl", cfg.prefix); + } + } + for (unsigned int bin = 0; bin < nBins; ++bin) { + for (unsigned int score = 0; score < NMlClasses; ++score) { + if (!std::isfinite(cfg.cutsMl.value.get(bin, score))) { + LOGP(fatal, "{}: cutsMl must be finite", cfg.prefix); + } + } + } + response.configure(edges, cfg.cutsMl.value, cfg.cutDirMl.value, cfg.nClassesMl.value); + response.cacheInputFeaturesIndices(cfg.namesInputFeatures.value); + if (cfg.loadModelsFromCCDB.value) { + ccdbApi.init(ccdbUrl.value); + response.setModelPathsCCDB(cfg.onnxFileNames.value, ccdbApi, cfg.modelPathsCCDB.value, cfg.timestampCCDB.value); + } else { + response.setModelPathsLocal(cfg.onnxFileNames.value); + } + response.init(); + } + + void init(InitContext const&) + { + const std::array processSwitches{doprocessD0D0, doprocessD0Dstar, doprocessD0D0Ml, doprocessD0DstarMl, doprocessD0D0WithFT0C, doprocessD0DstarWithFT0C, doprocessD0D0MlWithFT0C, doprocessD0DstarMlWithFT0C}; + if (std::count(processSwitches.begin(), processSwitches.end(), true) != 1) { + LOGP(fatal, "Enable exactly one charm-charm producer process"); + } + if (selectionFlagD0 < 1) { + LOGP(fatal, "selectionFlagD0 must be positive"); + } + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + const bool withMl = doprocessD0D0Ml || doprocessD0DstarMl || doprocessD0D0MlWithFT0C || doprocessD0DstarMlWithFT0C; + initMl(mlD0, hfMlResponseD0, withMl); + if (doprocessD0Dstar || doprocessD0DstarMl || doprocessD0DstarWithFT0C || doprocessD0DstarMlWithFT0C) { + initMl(mlDstar, hfMlResponseDstar, withMl); + } + hfEvSel.init(registry, &zorroSummary); + const AxisSpec eventStage{2, -0.5, 1.5, "stage (0=all,1=accepted)"}; + const AxisSpec candidateSpecies{4, -0.5, 3.5, "species (0=D0,1=D0bar,2=Dstar+,3=Dstar-)"}; + registry.add("events", "Events", kTH1F, {eventStage}); + registry.add("candidates", "Candidates", kTH1F, {candidateSpecies}); + } + + template + bool acceptCollision(CollType const& col) + { + registry.fill(HIST("events"), 0); + float cent = -1.f; // No centrality for pp MB. + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(col, cent, ccdb, registry); + hfEvSel.fillHistograms(col, rejectionMask, cent); + if (rejectionMask != 0) { + return false; + } + hasD0 = false; + hasDstar = false; + auto bc = col.template bc_as(); + initCCDB(bc, runNumber, ccdb, ccdbPathGrpMag.value, nullptr, false); + // Propagator field is in kG; FemtoDream uses Tesla. + const float fieldTesla = 0.1f * o2::base::Propagator::Instance()->getNominalBz(); + // MultV0M is the common reduced percentile column: FT0C or -1 (no + // estimator). + collisions(col.posZ(), cent, col.multNTracksPV(), 2.f, fieldTesla); + registry.fill(HIST("events"), 1); + // Keep all accepted collisions, including zero/single-candidate events. + return true; + } + + template + std::array readScores(Scores const& scores) + { + if (scores.size() != NMlClasses) { + LOGP(fatal, "Expected three selector ML scores: background, prompt, nonprompt"); + return {-1.f, -1.f, -1.f}; + } + for (const auto& score : scores) { + if (!std::isfinite(score)) { + LOGP(fatal, "Non-finite selector ML score"); + } + } + return {scores[0], scores[1], scores[2]}; + } + + template + void fillD0(CollType const& col, CandsType const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); + for (const auto& cand : candidates) { + auto p0 = cand.template prong0_as(); + auto p1 = cand.template prong1_as(); + // The OR filter accepts the row if either hypothesis passes. Write only + // the passing hypotheses, each with its own flavour and ML scores. + for (int hypothesis = 0; hypothesis < ND0Hypotheses; ++hypothesis) { + if ((hypothesis == 0 ? cand.isSelD0() : cand.isSelD0bar()) < selectionFlagD0) { + continue; + } + std::array scores{-1.f, -1.f, -1.f}; + if constexpr (WithMl) { + if (mlD0.mlApplicationMode != NoMl) { + scores = hypothesis == 0 ? readScores(cand.mlProbD0()) : readScores(cand.mlProbD0bar()); + } + if (mlD0.mlApplicationMode == FillMlFromNewBDT) { + // Do not call the ML response with an out-of-range model index. + if (!std::isfinite(cand.pt()) || cand.pt() < mlD0.binsPtMl.value.front() || cand.pt() >= mlD0.binsPtMl.value.back()) { + continue; + } + const int pdgCode = hypothesis == 0 ? o2::constants::physics::kD0 : -o2::constants::physics::kD0; + auto features = hfMlResponseD0.getInputFeatures(cand, pdgCode); + std::vector output; + if (!hfMlResponseD0.isSelectedMl(features, cand.pt(), output)) { + continue; // Reject this hypothesis only. + } + scores = readScores(output); + } + } + d0Rows(collisions.lastIndex(), timestamp, hypothesis == 0 ? 1 : -1, p0.globalIndex(), p1.globalIndex(), p0.pt(), p1.pt(), p0.eta(), p1.eta(), p0.phi(), p1.phi(), 1 << hypothesis, scores[0], scores[1], scores[2]); + hasD0 = true; + registry.fill(HIST("candidates"), hypothesis); + } + } + } + + template + void fillDstar(CollType const& col, CandsType const& candidates) + { + const auto timestamp = col.template bc_as().timestamp(); + for (const auto& cand : candidates) { + auto p0 = cand.template prong0_as(); + auto p1 = cand.template prong1_as(); + auto soft = cand.template prongPi_as(); + std::array scores{-1.f, -1.f, -1.f}; + if constexpr (WithMl) { + if (mlDstar.mlApplicationMode != NoMl) { + scores = readScores(cand.mlProbDstarToD0Pi()); + } + if (mlDstar.mlApplicationMode == FillMlFromNewBDT) { + if (!std::isfinite(cand.pt()) || cand.pt() < mlDstar.binsPtMl.value.front() || cand.pt() >= mlDstar.binsPtMl.value.back()) { + continue; + } + // Match the unswapped daughter convention used by D+track. + auto features = hfMlResponseDstar.getInputFeatures(cand, false); + std::vector output; + if (!hfMlResponseDstar.isSelectedMl(features, cand.pt(), output)) { + continue; + } + scores = readScores(output); + } + } + dstarRows(collisions.lastIndex(), timestamp, soft.sign(), p0.globalIndex(), p1.globalIndex(), soft.globalIndex(), p0.pt(), p1.pt(), soft.pt(), p0.eta(), p1.eta(), soft.eta(), p0.phi(), p1.phi(), soft.phi(), 1, scores[0], scores[1], scores[2]); + hasDstar = true; + registry.fill(HIST("candidates"), soft.sign() > 0 ? 2 : 3); + } + } + + void processD0D0(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0, "D0 only, data", true); + + void processD0Dstar(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0Dstar, "D0 and Dstar, data", false); + + void processD0D0Ml(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0Ml, "D0 with selector ML scores", false); + + void processD0DstarMl(Collisions::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMl, "D0 and Dstar with selector ML scores", false); + + void processD0D0WithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0WithFT0C, "D0 only, data with FT0C centrality", false); + + void processD0DstarWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarWithFT0C, "D0 and Dstar, data with FT0C centrality", false); + + void processD0D0MlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0D0MlWithFT0C, "D0 with selector ML scores and FT0C centrality", false); + + void processD0DstarMlWithFT0C(CollisionsWithFT0C::iterator const& col, + aod::BCsWithTimestamps const&, + aod::Tracks const&, + soa::Filtered const& d0s, + soa::Filtered const& dstars) + { + if (acceptCollision(col)) { + fillD0(col, d0s); + fillDstar(col, dstars); + rowMasks(hasD0 ? 1 : 0, hasDstar ? 1 : 0, 0); + } + } + PROCESS_SWITCH(HfProducerCharmHadronsCharmFemtoDream, processD0DstarMlWithFT0C, "D0 and Dstar with selector ML scores and FT0C centrality", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx index eb197529980..0b1102163c7 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsTrackFemtoDream.cxx @@ -21,10 +21,12 @@ #include "PWGCF/FemtoDream/Core/femtoDreamUtils.h" #include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/DecayChannels.h" +#include "PWGHF/Core/DecayChannelsLegacy.h" #include "PWGHF/Core/HfMlResponseD0ToKPi.h" #include "PWGHF/Core/HfMlResponseDplusToPiKPi.h" #include "PWGHF/Core/HfMlResponseDstarToD0Pi.h" #include "PWGHF/Core/HfMlResponseLcToPKPi.h" +#include "PWGHF/Core/HfMlResponseXicToXiPiPi.h" #include "PWGHF/Core/SelectorCuts.h" #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" @@ -32,6 +34,7 @@ #include "PWGHF/Utils/utilsBfieldCCDB.h" #include "PWGHF/Utils/utilsEvSelHf.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -98,7 +101,8 @@ enum MlMode : uint8_t { enum DecayChannel { DplusToPiKPi = 0, LcToPKPi, D0ToPiK, - DstarToD0Pi + DstarToD0Pi, + XicToXiPiPi }; enum class D0CandFlag : uint8_t { @@ -109,35 +113,38 @@ enum class D0CandFlag : uint8_t { struct HfProducerCharmHadronsTrackFemtoDream { - Produces outputCollision; - Produces rowMasks; - Produces rowCandCharm3Prong; - Produces rowCandCharm2Prong; - Produces rowCandCharmDstar; - Produces rowCandMcCharmHad; - Produces rowCandCharmHadGen; - Produces outputPartsIndex; - Produces outputPartsTime; - Produces outputMcCollision; - Produces outputCollsMcLabels; - Produces outputParts; - Produces outputPartsMc; - Produces outputDebugParts; - Produces outputPartsMcLabels; - Produces outputDebugPartsMc; - Produces outputPartsExtMcLabels; - - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"}; - Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; - Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; - - Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTLc"}, "Paths of models on CCDB"}; - Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_LcToPKPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; - Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; - Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; - - // Configurable isForceGRP{"isForceGRP", false, "Set true if the magnetic field configuration is not available in the usual CCDB directory (e.g. for Run 2 converted data or unanchorad Monte Carlo)"}; + struct : ProducesGroup { + Produces outputCollision; + Produces rowMasks; + Produces rowCandCharm3Prong; + Produces rowCandCharm3ProngXic; + Produces rowCandCharm3ProngXicQa; + Produces rowCandCharm2Prong; + Produces rowCandCharmDstar; + Produces rowCandMcCharmHad; + Produces rowCandCharmHadGen; + Produces outputPartsIndex; + Produces outputPartsTime; + Produces outputMcCollision; + Produces outputCollsMcLabels; + Produces outputParts; + Produces outputPartsMc; + Produces outputDebugParts; + Produces outputPartsMcLabels; + Produces outputDebugPartsMc; + Produces outputPartsExtMcLabels; + } tables; + + struct : ConfigurableGroup { + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTLc"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_LcToPKPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + } ccdbCfg; // ------------------------- // Kaon PID cut parameters @@ -157,37 +164,41 @@ struct HfProducerCharmHadronsTrackFemtoDream { Configurable nSigmaCombPiMax{"nSigmaCombPiMax", 6.f, "Kaon PID Method2: for p > pTrackTightMin require |nSigmaCombPi| < this"}; } kaonPidSel; - Configurable isDebug{"isDebug", true, "Enable Debug tables"}; - Configurable isRun3{"isRun3", true, "Running on Run3 or pilot"}; - - /// Charm hadron table - Configurable selectionFlagHadron{"selectionFlagHadron", 1, "Selection Flag for Charm Hadron: 1 for Lc, 7 for Dplus (Topologic and PID cuts)"}; - Configurable useCent{"useCent", false, "Enable centrality for Charm Hadron"}; - - Configurable trkPDGCode{"trkPDGCode", 2212, "PDG code of the selected track for Monte Carlo truth"}; - Configurable> trkCharge{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kSign, "trk"), std::vector{-1, 1}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kSign, "Track selection: ")}; - Configurable> trkDCAxyMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kDCAxyMax, "trk"), std::vector{0.1f, 3.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kDCAxyMax, "Track selection: ")}; - Configurable> trkDCAzMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kDCAzMax, "trk"), std::vector{0.2f, 3.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kDCAzMax, "Track selection: ")}; - Configurable> trkEta{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kEtaMax, "trk"), std::vector{0.8f, 0.7f, 0.9f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kEtaMax, "Track selection: ")}; - Configurable> trkPIDspecies{"trkPIDspecies", std::vector{o2::track::PID::Pion, o2::track::PID::Kaon, o2::track::PID::Proton, o2::track::PID::Deuteron}, "Trk sel: Particles species for PID"}; - Configurable> trkPIDnSigmaMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kPIDnSigmaMax, "trk"), std::vector{3.5f, 3.f, 2.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kPIDnSigmaMax, "Track selection: ")}; - Configurable trkPIDnSigmaOffsetTPC{"trkPIDnSigmaOffsetTPC", 0., "Offset for TPC nSigma because of bad calibration"}; - Configurable trkPIDnSigmaOffsetTOF{"trkPIDnSigmaOffsetTOF", 0., "Offset for TOF nSigma because of bad calibration"}; - Configurable> trkPtmax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kpTMax, "trk"), std::vector{5.4f, 5.6f, 5.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kpTMax, "Track selection: ")}; - Configurable> trkPtmin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kpTMin, "trk"), std::vector{0.5f, 0.4f, 0.6f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kpTMin, "Track selection: ")}; - Configurable> trkTPCcRowsMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCcRowsMin, "trk"), std::vector{70.f, 60.f, 80.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCcRowsMin, "Track selection: ")}; - Configurable> trkTPCfCls{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCfClsMin, "trk"), std::vector{0.7f, 0.83f, 0.9f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCfClsMin, "Track selection: ")}; - Configurable> trkTPCnclsMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCnClsMin, "trk"), std::vector{80.f, 70.f, 60.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCnClsMin, "Track selection: ")}; - Configurable> trkTPCsCls{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCsClsMax, "trk"), std::vector{0.1f, 160.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCsClsMax, "Track selection: ")}; - Configurable> trkITSnclsIbMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kITSnClsIbMin, "trk"), std::vector{-1.f, 1.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kITSnClsIbMin, "Track selection: ")}; - Configurable> trkITSnclsMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kITSnClsMin, "trk"), std::vector{-1.f, 2.f, 4.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kITSnClsMin, "Track selection: ")}; - // ML inference - Configurable applyMlMode{"applyMlMode", 1, "None: 0, BDT model from candidate selector: 1, New BDT model on Top of candidate selector: 2"}; - Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; - Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; - Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + struct : ConfigurableGroup { + Configurable isDebug{"isDebug", true, "Enable Debug tables"}; + Configurable isRun3{"isRun3", true, "Running on Run3 or pilot"}; + Configurable selectionFlagHadron{"selectionFlagHadron", 1, "Selection flag for charm hadrons; applied to D0, Dplus, Lc and Xic selector decisions"}; + Configurable useCent{"useCent", false, "Enable centrality for Charm Hadron"}; + } generalCfg; + + struct : ConfigurableGroup { + Configurable trkPDGCode{"trkPDGCode", 2212, "PDG code of the selected track for Monte Carlo truth"}; + Configurable> trkCharge{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kSign, "trk"), std::vector{-1, 1}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kSign, "Track selection: ")}; + Configurable> trkDCAxyMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kDCAxyMax, "trk"), std::vector{0.1f, 3.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kDCAxyMax, "Track selection: ")}; + Configurable> trkDCAzMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kDCAzMax, "trk"), std::vector{0.2f, 3.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kDCAzMax, "Track selection: ")}; + Configurable> trkEta{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kEtaMax, "trk"), std::vector{0.8f, 0.7f, 0.9f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kEtaMax, "Track selection: ")}; + Configurable> trkPIDspecies{"trkPIDspecies", std::vector{o2::track::PID::Pion, o2::track::PID::Kaon, o2::track::PID::Proton, o2::track::PID::Deuteron}, "Trk sel: Particles species for PID"}; + Configurable> trkPIDnSigmaMax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kPIDnSigmaMax, "trk"), std::vector{3.5f, 3.f, 2.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kPIDnSigmaMax, "Track selection: ")}; + Configurable trkPIDnSigmaOffsetTPC{"trkPIDnSigmaOffsetTPC", 0., "Offset for TPC nSigma because of bad calibration"}; + Configurable trkPIDnSigmaOffsetTOF{"trkPIDnSigmaOffsetTOF", 0., "Offset for TOF nSigma because of bad calibration"}; + Configurable> trkPtmax{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kpTMax, "trk"), std::vector{5.4f, 5.6f, 5.5f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kpTMax, "Track selection: ")}; + Configurable> trkPtmin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kpTMin, "trk"), std::vector{0.5f, 0.4f, 0.6f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kpTMin, "Track selection: ")}; + Configurable> trkTPCcRowsMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCcRowsMin, "trk"), std::vector{70.f, 60.f, 80.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCcRowsMin, "Track selection: ")}; + Configurable> trkTPCfCls{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCfClsMin, "trk"), std::vector{0.7f, 0.83f, 0.9f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCfClsMin, "Track selection: ")}; + Configurable> trkTPCnclsMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCnClsMin, "trk"), std::vector{80.f, 70.f, 60.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCnClsMin, "Track selection: ")}; + Configurable> trkTPCsCls{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kTPCsClsMax, "trk"), std::vector{0.1f, 160.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kTPCsClsMax, "Track selection: ")}; + Configurable> trkITSnclsIbMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kITSnClsIbMin, "trk"), std::vector{-1.f, 1.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kITSnClsIbMin, "Track selection: ")}; + Configurable> trkITSnclsMin{FemtoDreamTrackSelection::getSelectionName(femtoDreamTrackSelection::kITSnClsMin, "trk"), std::vector{-1.f, 2.f, 4.f}, FemtoDreamTrackSelection::getSelectionHelper(femtoDreamTrackSelection::kITSnClsMin, "Track selection: ")}; + } trackCfg; + + struct : ConfigurableGroup { + Configurable applyMlMode{"applyMlMode", 1, "None: 0, BDT model from candidate selector: 1, New BDT model on Top of candidate selector: 2"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + } mlCfg; FemtoDreamTrackSelection trackCuts; @@ -195,6 +206,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { o2::analysis::HfMlResponseDplusToPiKPi hfMlResponseDplus; o2::analysis::HfMlResponseD0ToKPi hfMlResponseD0; o2::analysis::HfMlResponseDstarToD0Pi hfMlResponseDstar; + o2::analysis::HfMlResponseXicToXiPiPi hfMlResponseXic; std::vector outputMlD0; std::vector outputMlD0bar; @@ -202,11 +214,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { std::vector outputMlDplus; std::vector outputMlPKPi; std::vector outputMlPiKP; + std::vector outputMlXic; o2::ccdb::CcdbApi ccdbApi; o2::hf_evsel::HfEventSelection hfEvSel; Service ccdb{}; /// Accessing the CCDB o2::base::MatLayerCylSet* lut{}; - // if (doPvRefit){ lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(ccdbPathLut));} //! may be it useful, will check later float magField{}; int runNumber{}; @@ -218,6 +230,10 @@ struct HfProducerCharmHadronsTrackFemtoDream { using CandidateDplusMc = soa::Join; using CandidateLc = soa::Join; using CandidateLcMc = soa::Join; + using CandidateXic = soa::Join; + using CandidateXicMc = soa::Join; + using CandidateXicKf = soa::Join; + using CandidateXicKfMc = soa::Join; using FemtoFullCollision = soa::Join::iterator; using FemtoFullCollisionMc = soa::Join::iterator; @@ -231,11 +247,13 @@ struct HfProducerCharmHadronsTrackFemtoDream { using Generated3ProngMc = soa::Join; using Generated2ProngMc = soa::Join; using GeneratedDstarMc = soa::Join; + using GeneratedXicMc = soa::Join; - Filter filterSelectCandidateD0 = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagHadron || aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagHadron); + Filter filterSelectCandidateD0 = (aod::hf_sel_candidate_d0::isSelD0 >= generalCfg.selectionFlagHadron || aod::hf_sel_candidate_d0::isSelD0bar >= generalCfg.selectionFlagHadron); Filter filterSelectCandidateDstar = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; - Filter filterSelectCandidateDplus = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= selectionFlagHadron; - Filter filterSelectCandidateLc = (aod::hf_sel_candidate_lc::isSelLcToPKPi >= selectionFlagHadron || aod::hf_sel_candidate_lc::isSelLcToPiKP >= selectionFlagHadron); + Filter filterSelectCandidateDplus = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= generalCfg.selectionFlagHadron; + Filter filterSelectCandidateLc = (aod::hf_sel_candidate_lc::isSelLcToPKPi >= generalCfg.selectionFlagHadron || aod::hf_sel_candidate_lc::isSelLcToPiKP >= generalCfg.selectionFlagHadron); + Filter filterSelectCandidateXic = aod::hf_sel_candidate_xic::isSelXicToXiPiPi >= generalCfg.selectionFlagHadron; HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry trackRegistry{"Tracks", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -243,8 +261,12 @@ struct HfProducerCharmHadronsTrackFemtoDream { void init(InitContext&) { - std::array processes = {doprocessDataDplusToPiKPi, doprocessMcDplusToPiKPi, doprocessDataDplusToPiKPiWithML, doprocessMcDplusToPiKPiWithML, doprocessMcDplusToPiKPiGen, - doprocessDataLcToPKPi, doprocessMcLcToPKPi, doprocessDataLcToPKPiWithML, doprocessMcLcToPKPiWithML, doprocessMcLcToPKPiGen, doprocessDataD0ToPiK, doprocessMcD0ToPiK, doprocessDataD0ToPiKWithML, doprocessMcD0ToPiKWithML, doprocessMcD0ToPiKGen, doprocessDataDstarToD0Pi, doprocessMcDstarToD0Pi, doprocessDataDstarToD0PiWithML, doprocessMcDstarToD0PiWithML, doprocessMcDstarToD0PiGen}; + std::array processes = {doprocessDataDplusToPiKPi, doprocessMcDplusToPiKPi, doprocessDataDplusToPiKPiWithML, doprocessMcDplusToPiKPiWithML, doprocessMcDplusToPiKPiGen, + doprocessDataLcToPKPi, doprocessMcLcToPKPi, doprocessDataLcToPKPiWithML, doprocessMcLcToPKPiWithML, doprocessMcLcToPKPiGen, + doprocessDataD0ToPiK, doprocessMcD0ToPiK, doprocessDataD0ToPiKWithML, doprocessMcD0ToPiKWithML, doprocessMcD0ToPiKGen, + doprocessDataDstarToD0Pi, doprocessMcDstarToD0Pi, doprocessDataDstarToD0PiWithML, doprocessMcDstarToD0PiWithML, doprocessMcDstarToD0PiGen, + doprocessDataXicToXiPiPi, doprocessDataXicToXiPiPiKf, doprocessDataXicToXiPiPiWithML, doprocessDataXicToXiPiPiWithMLKf, + doprocessMcXicToXiPiPi, doprocessMcXicToXiPiPiKf, doprocessMcXicToXiPiPiWithML, doprocessMcXicToXiPiPiWithMLKf, doprocessMcXicToXiPiPiGen}; if (std::accumulate(processes.begin(), processes.end(), 0) != 1) { LOGP(fatal, "One and only one process function must be enabled at a time."); } @@ -253,8 +275,8 @@ struct HfProducerCharmHadronsTrackFemtoDream { trackRegistry.add("AnalysisQA/CutCounter", "; Bit; Counter", kTH1F, {{cutBits + 1, -0.5, cutBits + 0.5}}); // event QA histograms - constexpr int kEventTypes = PairSelected + 1; - std::string labels[kEventTypes]; + constexpr int EventTypes = PairSelected + 1; + std::string labels[EventTypes]; labels[Event::All] = "All events"; labels[Event::RejEveSel] = "rejected by event selection"; labels[Event::RejNoTracksAndCharm] = "rejected by no tracks and charm"; @@ -262,33 +284,33 @@ struct HfProducerCharmHadronsTrackFemtoDream { labels[Event::CharmSelected] = "with charm hadrons "; labels[Event::PairSelected] = "with pairs"; - static const AxisSpec axisEvents = {kEventTypes, 0.5, kEventTypes + 0.5, ""}; + static const AxisSpec axisEvents = {EventTypes, 0.5, EventTypes + 0.5, ""}; qaRegistry.add("hEventQA", "Events;;entries", HistType::kTH1F, {axisEvents}); - for (int iBin = 0; iBin < kEventTypes; iBin++) { + for (int iBin = 0; iBin < EventTypes; iBin++) { qaRegistry.get(HIST("hEventQA"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } - trackCuts.setSelection(trkCharge, femtoDreamTrackSelection::kSign, femtoDreamSelection::kEqual); - trackCuts.setSelection(trkPtmin, femtoDreamTrackSelection::kpTMin, femtoDreamSelection::kLowerLimit); - trackCuts.setSelection(trkPtmax, femtoDreamTrackSelection::kpTMax, femtoDreamSelection::kUpperLimit); - trackCuts.setSelection(trkEta, femtoDreamTrackSelection::kEtaMax, femtoDreamSelection::kAbsUpperLimit); - trackCuts.setSelection(trkTPCnclsMin, femtoDreamTrackSelection::kTPCnClsMin, femtoDreamSelection::kLowerLimit); - trackCuts.setSelection(trkTPCfCls, femtoDreamTrackSelection::kTPCfClsMin, femtoDreamSelection::kLowerLimit); - trackCuts.setSelection(trkTPCcRowsMin, femtoDreamTrackSelection::kTPCcRowsMin, femtoDreamSelection::kLowerLimit); - trackCuts.setSelection(trkTPCsCls, femtoDreamTrackSelection::kTPCsClsMax, femtoDreamSelection::kUpperLimit); - trackCuts.setSelection(trkITSnclsMin, femtoDreamTrackSelection::kITSnClsMin, femtoDreamSelection::kLowerLimit); - trackCuts.setSelection(trkITSnclsIbMin, femtoDreamTrackSelection::kITSnClsIbMin, femtoDreamSelection::kLowerLimit); - trackCuts.setSelection(trkDCAxyMax, femtoDreamTrackSelection::kDCAxyMax, femtoDreamSelection::kAbsUpperLimit); - trackCuts.setSelection(trkDCAzMax, femtoDreamTrackSelection::kDCAzMax, femtoDreamSelection::kAbsUpperLimit); - trackCuts.setSelection(trkPIDnSigmaMax, femtoDreamTrackSelection::kPIDnSigmaMax, femtoDreamSelection::kAbsUpperLimit); - trackCuts.setPIDSpecies(trkPIDspecies); - trackCuts.setnSigmaPIDOffset(trkPIDnSigmaOffsetTPC, trkPIDnSigmaOffsetTOF); + trackCuts.setSelection(trackCfg.trkCharge, femtoDreamTrackSelection::kSign, femtoDreamSelection::kEqual); + trackCuts.setSelection(trackCfg.trkPtmin, femtoDreamTrackSelection::kpTMin, femtoDreamSelection::kLowerLimit); + trackCuts.setSelection(trackCfg.trkPtmax, femtoDreamTrackSelection::kpTMax, femtoDreamSelection::kUpperLimit); + trackCuts.setSelection(trackCfg.trkEta, femtoDreamTrackSelection::kEtaMax, femtoDreamSelection::kAbsUpperLimit); + trackCuts.setSelection(trackCfg.trkTPCnclsMin, femtoDreamTrackSelection::kTPCnClsMin, femtoDreamSelection::kLowerLimit); + trackCuts.setSelection(trackCfg.trkTPCfCls, femtoDreamTrackSelection::kTPCfClsMin, femtoDreamSelection::kLowerLimit); + trackCuts.setSelection(trackCfg.trkTPCcRowsMin, femtoDreamTrackSelection::kTPCcRowsMin, femtoDreamSelection::kLowerLimit); + trackCuts.setSelection(trackCfg.trkTPCsCls, femtoDreamTrackSelection::kTPCsClsMax, femtoDreamSelection::kUpperLimit); + trackCuts.setSelection(trackCfg.trkITSnclsMin, femtoDreamTrackSelection::kITSnClsMin, femtoDreamSelection::kLowerLimit); + trackCuts.setSelection(trackCfg.trkITSnclsIbMin, femtoDreamTrackSelection::kITSnClsIbMin, femtoDreamSelection::kLowerLimit); + trackCuts.setSelection(trackCfg.trkDCAxyMax, femtoDreamTrackSelection::kDCAxyMax, femtoDreamSelection::kAbsUpperLimit); + trackCuts.setSelection(trackCfg.trkDCAzMax, femtoDreamTrackSelection::kDCAzMax, femtoDreamSelection::kAbsUpperLimit); + trackCuts.setSelection(trackCfg.trkPIDnSigmaMax, femtoDreamTrackSelection::kPIDnSigmaMax, femtoDreamSelection::kAbsUpperLimit); + trackCuts.setPIDSpecies(trackCfg.trkPIDspecies); + trackCuts.setnSigmaPIDOffset(trackCfg.trkPIDnSigmaOffsetTPC, trackCfg.trkPIDnSigmaOffsetTOF); trackCuts.init(&qaRegistry, &trackRegistry); runNumber = 0; magField = 0.0; /// Initializing CCDB - ccdb->setURL(ccdbUrl); + ccdb->setURL(ccdbCfg.ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -301,25 +323,27 @@ struct HfProducerCharmHadronsTrackFemtoDream { bool useDplusMl = doprocessDataDplusToPiKPiWithML || doprocessMcDplusToPiKPiWithML; bool useD0Ml = doprocessDataD0ToPiKWithML || doprocessMcD0ToPiKWithML; bool useDstarMl = doprocessDataDstarToD0PiWithML || doprocessMcDstarToD0PiWithML; + bool useXicMl = doprocessDataXicToXiPiPiWithML || doprocessMcXicToXiPiPiWithML || doprocessDataXicToXiPiPiWithMLKf || doprocessMcXicToXiPiPiWithMLKf; - if (applyMlMode == FillMlFromNewBDT) { + if (mlCfg.applyMlMode == FillMlFromNewBDT) { auto setupFeatures = [&](auto& hfResponse, bool useMlFlag) { if (!useMlFlag) { return; } - hfResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); - hfResponse.cacheInputFeaturesIndices(namesInputFeatures); + hfResponse.configure(mlCfg.binsPtMl, mlCfg.cutsMl, mlCfg.cutDirMl, mlCfg.nClassesMl); + hfResponse.cacheInputFeaturesIndices(mlCfg.namesInputFeatures); }; setupFeatures(hfMlResponseLc, useLcMl); setupFeatures(hfMlResponseDplus, useDplusMl); setupFeatures(hfMlResponseD0, useD0Ml); setupFeatures(hfMlResponseDstar, useDstarMl); + setupFeatures(hfMlResponseXic, useXicMl); - const bool useAnyMl = useLcMl || useDplusMl || useD0Ml || useDstarMl; - if (loadModelsFromCCDB && useAnyMl) { - ccdbApi.init(ccdbUrl); + const bool useAnyMl = useLcMl || useDplusMl || useD0Ml || useDstarMl || useXicMl; + if (ccdbCfg.loadModelsFromCCDB && useAnyMl) { + ccdbApi.init(ccdbCfg.ccdbUrl); } auto initModel = [&](auto& hfResponse, bool useMlFlag) { @@ -327,10 +351,10 @@ struct HfProducerCharmHadronsTrackFemtoDream { return; } - if (loadModelsFromCCDB) { - hfResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + if (ccdbCfg.loadModelsFromCCDB) { + hfResponse.setModelPathsCCDB(ccdbCfg.onnxFileNames, ccdbApi, ccdbCfg.modelPathsCCDB, ccdbCfg.timestampCCDB); } else { - hfResponse.setModelPathsLocal(onnxFileNames); + hfResponse.setModelPathsLocal(ccdbCfg.onnxFileNames); } hfResponse.init(); }; @@ -339,6 +363,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { initModel(hfMlResponseDplus, useDplusMl); initModel(hfMlResponseD0, useD0Ml); initModel(hfMlResponseDstar, useDstarMl); + initModel(hfMlResponseXic, useXicMl); } } @@ -391,7 +416,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { if (std::abs(nSigmaCombKa) >= kaonPidSel.nSigmaCombKaTightMax) { isTrackKaonPidMethod2 = false; } - if (std::abs(nSigmaCombPi) >= kaonPidSel.nSigmaCombPiMax) { + if (std::abs(nSigmaCombPi) <= kaonPidSel.nSigmaCombPiMax) { isTrackKaonPidMethod2 = false; } } @@ -403,45 +428,67 @@ struct HfProducerCharmHadronsTrackFemtoDream { /// Function to retrieve the nominal magnetic field in kG (0.1T) and convert it directly to T void getMagneticFieldTesla(const aod::BCsWithTimestamps::iterator& bc) { - initCCDB(bc, runNumber, ccdb, !isRun3 ? ccdbPathGrp : ccdbPathGrpMag, lut, !isRun3); + initCCDB(bc, runNumber, ccdb, !generalCfg.isRun3 ? ccdbCfg.ccdbPathGrp : ccdbCfg.ccdbPathGrpMag, lut, !generalCfg.isRun3); } template void fillDebugParticle(ParticleType const& particle) { - outputDebugParts(particle.sign(), - (uint8_t)particle.tpcNClsFound(), - particle.tpcNClsFindable(), - (uint8_t)particle.tpcNClsCrossedRows(), - particle.tpcNClsShared(), - particle.tpcInnerParam(), - particle.itsNCls(), - particle.itsNClsInnerBarrel(), - particle.dcaXY(), - particle.dcaZ(), - particle.tpcSignal(), - -999., - particle.tpcNSigmaPi(), - particle.tpcNSigmaKa(), - particle.tpcNSigmaPr(), - particle.tpcNSigmaDe(), - -999., - -999., - -999., - particle.tofNSigmaPi(), - particle.tofNSigmaKa(), - particle.tofNSigmaPr(), - particle.tofNSigmaDe(), - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999., -999., - -999., -999., -999.); + tables.outputDebugParts(particle.sign(), + static_cast(particle.tpcNClsFound()), + particle.tpcNClsFindable(), + static_cast(particle.tpcNClsCrossedRows()), + particle.tpcNClsShared(), + particle.tpcInnerParam(), + particle.itsNCls(), + particle.itsNClsInnerBarrel(), + particle.dcaXY(), + particle.dcaZ(), + particle.tpcSignal(), + -999., + particle.tpcNSigmaPi(), + particle.tpcNSigmaKa(), + particle.tpcNSigmaPr(), + particle.tpcNSigmaDe(), + -999., + -999., + -999., + particle.tofNSigmaPi(), + particle.tofNSigmaKa(), + particle.tofNSigmaPr(), + particle.tofNSigmaDe(), + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999., -999., + -999., -999., -999.); + } + + /// Classify true kaons from the correct collision for the DCA templates. + template + static int getKaonOrigin(T const& particles, typename T::iterator const& particle) + { + using namespace o2::aod::femtodreamMCparticle; + constexpr int GenFromTransport = -1; + if (particle.getProcess() == TMCProcess::kPHInhelastic && particle.getGenStatusCode() == GenFromTransport) { + return kMaterial; + } + // HF decay products can be physical primaries. Check their ancestry first. + if (RecoDecay::getParticleOrigin(particles, particle) != RecoDecay::OriginType::None) { + return kSecondaryFromHF; + } + if (particle.isPhysicalPrimary()) { + return kPrimary; + } + if (particle.getProcess() == TMCProcess::kPDecay && particle.getGenStatusCode() == GenFromTransport && particle.has_mothers()) { + return kSecondaryFromLF; + } + return kElse; } template - void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype) + void fillMcParticle(CollisionType const& col, ParticleType const& particle, o2::aod::femtodreamparticle::ParticleType fdparttype, aod::McParticles const& mcParticles) { if (particle.has_mcParticle()) { // get corresponding MC particle and its info @@ -454,11 +501,16 @@ struct HfProducerCharmHadronsTrackFemtoDream { auto motherparticlesMc = particleMc.template mothers_as(); // check pdg code // if this fails, the particle is a fake - if (std::abs(pdgCode) == std::abs(trkPDGCode.value)) { + if (std::abs(pdgCode) == std::abs(trackCfg.trkPDGCode.value)) { // check first if particle is from pile up // check if the collision associated with the particle is the same as the analyzed collision by checking their Ids if ((col.has_mcCollision() && (particleMc.mcCollisionId() != col.mcCollisionId())) || !col.has_mcCollision()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kWrongCollision; + } else if (std::abs(pdgCode) == kKPlus) { + particleOrigin = getKaonOrigin(mcParticles, particleMc); + if (!motherparticlesMc.empty()) { + pdgCodeMother = motherparticlesMc.front().pdgCode(); + } // check if particle is primary } else if (particleMc.isPhysicalPrimary()) { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kPrimary; @@ -468,9 +520,9 @@ struct HfProducerCharmHadronsTrackFemtoDream { // list of mothers is not empty } else if (particleMc.getProcess() == TMCProcess::kPDecay && particleMc.getGenStatusCode() == GenFromTransport && !motherparticlesMc.empty()) { // get direct mother - auto motherparticleMc = motherparticlesMc.front(); + const auto& motherparticleMc = motherparticlesMc.front(); pdgCodeMother = motherparticleMc.pdgCode(); - particleOrigin = checkDaughterType(fdparttype, motherparticleMc.pdgCode()); + particleOrigin = checkDaughterType(fdparttype, motherparticleMc.pdgCode(), pdgCode); // check if particle is material // particle is from inelastic hadronic interaction -> getProcess() == 23 // particle is generated during transport -> getGenStatusCode() == -1 @@ -485,16 +537,16 @@ struct HfProducerCharmHadronsTrackFemtoDream { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kFake; } - outputPartsMc(particleOrigin, pdgCode, particleMc.pt(), particleMc.eta(), particleMc.phi()); - outputPartsMcLabels(outputPartsMc.lastIndex()); - if (isDebug) { - outputPartsExtMcLabels(outputPartsMc.lastIndex()); - outputDebugPartsMc(pdgCodeMother); + tables.outputPartsMc(particleOrigin, pdgCode, particleMc.pt(), particleMc.eta(), particleMc.phi()); + tables.outputPartsMcLabels(tables.outputPartsMc.lastIndex()); + if (generalCfg.isDebug) { + tables.outputPartsExtMcLabels(tables.outputPartsMc.lastIndex()); + tables.outputDebugPartsMc(pdgCodeMother); } } else { - outputPartsMcLabels(-1); - if (isDebug) { - outputPartsExtMcLabels(-1); + tables.outputPartsMcLabels(-1); + if (generalCfg.isDebug) { + tables.outputPartsExtMcLabels(-1); } } } @@ -504,15 +556,15 @@ struct HfProducerCharmHadronsTrackFemtoDream { { if (col.has_mcCollision()) { // auto genMCcol = col.template mcCollision_as(); - // outputMcCollision(genMCcol.multMCNParticlesEta08()); - outputCollsMcLabels(outputMcCollision.lastIndex()); + // tables.outputMcCollision(genMCcol.multMCNParticlesEta08()); + tables.outputCollsMcLabels(tables.outputMcCollision.lastIndex()); } else { - outputCollsMcLabels(-1); + tables.outputCollsMcLabels(-1); } } template - bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks) + bool fillTracksForCharmHadron(CollisionType const& col, TrackType const& tracks, aod::McParticles const* mcParticles = nullptr) { std::vector const childIDs = {0, 0}; // these IDs are necessary to keep track of the children @@ -532,55 +584,55 @@ struct HfProducerCharmHadronsTrackFemtoDream { auto bc = col.template bc_as(); int64_t timeStamp = bc.timestamp(); // track global index - outputPartsIndex(track.globalIndex()); - outputPartsTime(timeStamp); + tables.outputPartsIndex(track.globalIndex()); + tables.outputPartsTime(timeStamp); // now the table is filled - if (trkPDGCode == kKPlus) { + if (trackCfg.trkPDGCode == kKPlus) { const auto pidTrackPassBit = static_cast(isTrackKaonPidSelected(track)); - outputParts(outputCollision.lastIndex(), - track.pt(), - track.eta(), - track.phi(), - aod::femtodreamparticle::ParticleType::kTrack, - cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kCuts), - pidTrackPassBit, - track.dcaXY(), childIDs, 0, 0); + tables.outputParts(tables.outputCollision.lastIndex(), + track.pt(), + track.eta(), + track.phi(), + aod::femtodreamparticle::ParticleType::kTrack, + cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kCuts), + pidTrackPassBit, + track.dcaXY(), childIDs, 0, 0); } else { - outputParts(outputCollision.lastIndex(), - track.pt(), - track.eta(), - track.phi(), - aod::femtodreamparticle::ParticleType::kTrack, - cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kCuts), - cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kPID), - track.dcaXY(), childIDs, 0, 0); + tables.outputParts(tables.outputCollision.lastIndex(), + track.pt(), + track.eta(), + track.phi(), + aod::femtodreamparticle::ParticleType::kTrack, + cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kCuts), + cutContainer.at(femtoDreamTrackSelection::TrackContainerPosition::kPID), + track.dcaXY(), childIDs, 0, 0); } fIsTrackFilled = true; // tmpIDtrack.push_back(track.globalIndex()); - if (isDebug.value) { + if (generalCfg.isDebug.value) { fillDebugParticle(track); } if constexpr (IsMc) { - fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack); + fillMcParticle(col, track, o2::aod::femtodreamparticle::ParticleType::kTrack, *mcParticles); } } return fIsTrackFilled; } template - void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates) + void fillCharmHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, aod::McParticles const* mcParticles = nullptr) { const auto vtxZ = col.posZ(); const auto sizeCand = candidates.size(); const auto spher = 2.; // dummy value for the moment float mult = 0; int multNtr = 0; - if (isRun3) { - if (useCent) { + if (generalCfg.isRun3) { + if (generalCfg.useCent) { mult = col.centFT0M(); } else { mult = 0; @@ -608,19 +660,11 @@ struct HfProducerCharmHadronsTrackFemtoDream { return; } - outputCollision(vtxZ, mult, multNtr, spher, magField); + tables.outputCollision(vtxZ, mult, multNtr, spher, magField); if constexpr (IsMc) { fillMcCollision(col); } - // Filling candidate properties - if constexpr (Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::LcToPKPi) { - rowCandCharm3Prong.reserve(sizeCand); - } else if constexpr (Channel == DecayChannel::D0ToPiK) { - rowCandCharm2Prong.reserve(sizeCand); - } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { - rowCandCharmDstar.reserve(sizeCand); - } bool isTrackFilled = false; bool isSelectedMlLcToPKPi = true; bool isSelectedMlLcToPiKP = true; @@ -629,6 +673,14 @@ struct HfProducerCharmHadronsTrackFemtoDream { bool isSelectedMlD0barToKPi = true; bool isSelectedMlDstarToD0Pi = true; + if constexpr (Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::LcToPKPi) { + tables.rowCandCharm3Prong.reserve(tables.rowCandCharm3Prong.lastIndex() + sizeCand * 2 + 1); + } else if constexpr (Channel == DecayChannel::D0ToPiK) { + tables.rowCandCharm2Prong.reserve(tables.rowCandCharm2Prong.lastIndex() + sizeCand * 2 + 1); + } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { + tables.rowCandCharmDstar.reserve(tables.rowCandCharmDstar.lastIndex() + sizeCand + 1); + } + for (const auto& candidate : candidates) { outputMlD0 = {-1.0f, -1.0f, -1.0f}; outputMlD0bar = {-1.0f, -1.0f, -1.0f}; @@ -650,8 +702,8 @@ struct HfProducerCharmHadronsTrackFemtoDream { if (functionSelection >= 1) { if constexpr (Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::LcToPKPi) { auto trackPos2 = candidate.template prong2_as(); - rowCandCharm3Prong( - outputCollision.lastIndex(), + tables.rowCandCharm3Prong( + tables.outputCollision.lastIndex(), timeStamp, trackPos1.sign() + trackNeg.sign() + trackPos2.sign(), trackPos1.globalIndex(), @@ -682,8 +734,8 @@ struct HfProducerCharmHadronsTrackFemtoDream { } else { LOG(error) << "Unexpected candFlag = " << candFlag; } - rowCandCharm2Prong( - outputCollision.lastIndex(), + tables.rowCandCharm2Prong( + tables.outputCollision.lastIndex(), timeStamp, signD0, trackPos1.globalIndex(), @@ -700,8 +752,8 @@ struct HfProducerCharmHadronsTrackFemtoDream { bdtScoreFd); } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { auto trackPos2 = candidate.template prongPi_as(); - rowCandCharmDstar( - outputCollision.lastIndex(), + tables.rowCandCharmDstar( + tables.outputCollision.lastIndex(), timeStamp, candidate.template prongPi_as().sign(), trackPos1.globalIndex(), @@ -723,7 +775,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } if constexpr (IsMc) { - rowCandMcCharmHad( + tables.rowCandMcCharmHad( candidate.flagMcMatchRec(), candidate.originMcRec()); } @@ -734,13 +786,13 @@ struct HfProducerCharmHadronsTrackFemtoDream { if constexpr (UseCharmMl) { /// fill with ML information /// BDT index 0: bkg score; BDT index 1: prompt score; BDT index 2: non-prompt score - if (applyMlMode == FillMlFromSelector) { + if (mlCfg.applyMlMode == FillMlFromSelector) { if (candidate.mlProbDplusToPiKPi().size() > 0) { outputMlDplus.at(0) = candidate.mlProbDplusToPiKPi()[0]; /// bkg score outputMlDplus.at(1) = candidate.mlProbDplusToPiKPi()[1]; /// prompt score outputMlDplus.at(2) = candidate.mlProbDplusToPiKPi()[2]; /// non-prompt score } - } else if (applyMlMode == FillMlFromNewBDT) { + } else if (mlCfg.applyMlMode == FillMlFromNewBDT) { isSelectedMlDplusToPiKPi = false; if (candidate.mlProbDplusToPiKPi().size() > 0) { std::vector inputFeaturesDplusToPiKPi = hfMlResponseDplus.getInputFeatures(candidate); @@ -759,7 +811,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { if constexpr (UseCharmMl) { /// fill with ML information /// BDT index 0: bkg score; BDT index 1: prompt score; BDT index 2: non-prompt score - if (applyMlMode == FillMlFromSelector) { + if (mlCfg.applyMlMode == FillMlFromSelector) { if (candidate.mlProbLcToPKPi().size() > 0) { outputMlPKPi.at(0) = candidate.mlProbLcToPKPi()[0]; /// bkg score outputMlPKPi.at(1) = candidate.mlProbLcToPKPi()[1]; /// prompt score @@ -770,7 +822,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { outputMlPiKP.at(1) = candidate.mlProbLcToPiKP()[1]; /// prompt score outputMlPiKP.at(2) = candidate.mlProbLcToPiKP()[2]; /// non-prompt score } - } else if (applyMlMode == FillMlFromNewBDT) { + } else if (mlCfg.applyMlMode == FillMlFromNewBDT) { isSelectedMlLcToPKPi = false; isSelectedMlLcToPiKP = false; if (candidate.mlProbLcToPKPi().size() > 0) { @@ -795,7 +847,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { /// fill with ML information /// BDT index 0: bkg score; BDT index 1: prompt score; BDT index 2: non-prompt score - if (applyMlMode == FillMlFromSelector) { + if (mlCfg.applyMlMode == FillMlFromSelector) { if (candidate.mlProbD0().size() > 0) { outputMlD0.at(0) = candidate.mlProbD0()[0]; /// bkg score outputMlD0.at(1) = candidate.mlProbD0()[1]; /// prompt score @@ -807,7 +859,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { outputMlD0bar.at(2) = candidate.mlProbD0bar()[2]; /// non-prompt score } - } else if (applyMlMode == FillMlFromNewBDT) { + } else if (mlCfg.applyMlMode == FillMlFromNewBDT) { isSelectedMlD0ToPiK = false; isSelectedMlD0barToKPi = false; @@ -837,13 +889,13 @@ struct HfProducerCharmHadronsTrackFemtoDream { if constexpr (UseCharmMl) { /// fill with ML information /// BDT index 0: bkg score; BDT index 1: prompt score; BDT index 2: non-prompt score - if (applyMlMode == FillMlFromSelector) { + if (mlCfg.applyMlMode == FillMlFromSelector) { if (candidate.mlProbDstarToD0Pi().size() > 0) { outputMlDstar.at(0) = candidate.mlProbDstarToD0Pi()[0]; /// bkg score outputMlDstar.at(1) = candidate.mlProbDstarToD0Pi()[1]; /// prompt score outputMlDstar.at(2) = candidate.mlProbDstarToD0Pi()[2]; /// non-prompt score } - } else if (applyMlMode == FillMlFromNewBDT) { + } else if (mlCfg.applyMlMode == FillMlFromNewBDT) { isSelectedMlDstarToD0Pi = false; if (candidate.mlProbDstarToD0Pi().size() > 0) { std::vector inputFeaturesDstarToD0Pi = hfMlResponseDstar.getInputFeatures(candidate, false); @@ -859,7 +911,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { fillTable(2, candidate.isSelDstarToD0Pi(), outputMlDstar.at(0), outputMlDstar.at(1), outputMlDstar.at(2)); } } - isTrackFilled = fillTracksForCharmHadron(col, tracks); + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); aod::femtodreamcollision::BitMaskType bitTrack = 0; if (isTrackFilled) { @@ -877,9 +929,153 @@ struct HfProducerCharmHadronsTrackFemtoDream { qaRegistry.fill(HIST("hEventQA"), 1 + Event::PairSelected); } - rowMasks(bitTrack, - bitCand, - 0); + tables.rowMasks(bitTrack, + bitCand, + 0); + } + + template + void fillXicHadronTable(CollisionType const& col, TrackType const& tracks, CandType const& candidates, DaughterTrackType const&, aod::McParticles const* mcParticles = nullptr) + { + const auto vtxZ = col.posZ(); + const auto sizeCand = candidates.size(); + const auto spher = 2.f; + float mult = 0; + int multNtr = 0; + if (generalCfg.isRun3) { + mult = generalCfg.useCent ? col.centFT0M() : 0.f; + multNtr = col.multNTracksPV(); + } else { + mult = 1.f; + multNtr = col.multTracklets(); + } + + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(col, mult, ccdb, qaRegistry); + qaRegistry.fill(HIST("hEventQA"), 1 + Event::All); + hfEvSel.fillHistograms(col, rejectionMask, mult); + if (rejectionMask != 0) { + qaRegistry.fill(HIST("hEventQA"), 1 + Event::RejEveSel); + return; + } + + if (isNoSelectedTracks(col, tracks, trackCuts) && sizeCand <= 0) { + qaRegistry.fill(HIST("hEventQA"), 1 + Event::RejNoTracksAndCharm); + return; + } + + tables.outputCollision(vtxZ, mult, multNtr, spher, magField); + if constexpr (IsMc) { + fillMcCollision(col); + } + + tables.rowCandCharm3ProngXic.reserve(tables.rowCandCharm3ProngXic.lastIndex() + sizeCand + 1); + tables.rowCandCharm3ProngXicQa.reserve(tables.rowCandCharm3ProngXicQa.lastIndex() + sizeCand + 1); + + bool isTrackFilled = false; + int nSelectedXic = 0; + + for (const auto& candidate : candidates) { + if (candidate.isSelXicToXiPiPi() < generalCfg.selectionFlagHadron) { + continue; + } + + outputMlXic = {-1.f, -1.f, -1.f}; + bool isSelectedMlXicToXiPiPi = true; + if constexpr (UseCharmMl) { + if (mlCfg.applyMlMode == FillMlFromSelector) { + if (candidate.mlProbXicToXiPiPi().size() > 0) { + outputMlXic.at(0) = candidate.mlProbXicToXiPiPi()[0]; + outputMlXic.at(1) = candidate.mlProbXicToXiPiPi()[1]; + outputMlXic.at(2) = candidate.mlProbXicToXiPiPi()[2]; + } + } else if (mlCfg.applyMlMode == FillMlFromNewBDT) { + isSelectedMlXicToXiPiPi = false; + if (candidate.mlProbXicToXiPiPi().size() > 0) { + std::vector inputFeaturesXicToXiPiPi = hfMlResponseXic.getInputFeatures(candidate); + isSelectedMlXicToXiPiPi = hfMlResponseXic.isSelectedMl(inputFeaturesXicToXiPiPi, candidate.pt(), outputMlXic); + } + if (!isSelectedMlXicToXiPiPi) { + continue; + } + } else { + LOGF(fatal, "Please check your ML configuration."); + } + } + + auto bc = col.template bc_as(); + int64_t timeStamp = bc.timestamp(); + const auto eta0 = static_cast(RecoDecay::eta(std::array{candidate.pxProng0(), candidate.pyProng0(), candidate.pzProng0()})); + const auto eta1 = static_cast(RecoDecay::eta(std::array{candidate.pxProng1(), candidate.pyProng1(), candidate.pzProng1()})); + const auto eta2 = static_cast(RecoDecay::eta(std::array{candidate.pxProng2(), candidate.pyProng2(), candidate.pzProng2()})); + const auto phi0 = static_cast(RecoDecay::phi(candidate.pxProng0(), candidate.pyProng0())); + const auto phi1 = static_cast(RecoDecay::phi(candidate.pxProng1(), candidate.pyProng1())); + const auto phi2 = static_cast(RecoDecay::phi(candidate.pxProng2(), candidate.pyProng2())); + const auto cascBachelorTrack = candidate.template bachelor_as(); + const auto cascPosTrack = candidate.template posTrack_as(); + const auto cascNegTrack = candidate.template negTrack_as(); + + tables.rowCandCharm3ProngXic( + tables.outputCollision.lastIndex(), + timeStamp, + candidate.sign(), + candidate.cascadeId(), + candidate.pi0Id(), + candidate.pi1Id(), + candidate.bachelorId(), + candidate.posTrackId(), + candidate.negTrackId(), + candidate.ptProng0(), + candidate.ptProng1(), + candidate.ptProng2(), + eta0, + eta1, + eta2, + phi0, + phi1, + phi2, + 1 << 0, + outputMlXic.at(0), + outputMlXic.at(1), + outputMlXic.at(2)); + + tables.rowCandCharm3ProngXicQa( + cascBachelorTrack.pt(), + cascBachelorTrack.phi(), + cascBachelorTrack.eta(), + cascPosTrack.pt(), + cascPosTrack.phi(), + cascPosTrack.eta(), + cascNegTrack.pt(), + cascNegTrack.phi(), + cascNegTrack.eta()); + + ++nSelectedXic; + if constexpr (IsMc) { + tables.rowCandMcCharmHad( + candidate.flagMcMatchRec(), + candidate.originMcRec()); + } + } + + isTrackFilled = fillTracksForCharmHadron(col, tracks, mcParticles); + + aod::femtodreamcollision::BitMaskType bitTrack = 0; + if (isTrackFilled) { + bitTrack |= 1 << 0; + qaRegistry.fill(HIST("hEventQA"), 1 + Event::TrackSelected); + } + + aod::femtodreamcollision::BitMaskType bitCand = 0; + if (nSelectedXic > 0) { + bitCand |= 1 << 0; + qaRegistry.fill(HIST("hEventQA"), 1 + Event::CharmSelected); + } + + if (isTrackFilled && nSelectedXic > 0) { + qaRegistry.fill(HIST("hEventQA"), 1 + Event::PairSelected); + } + + tables.rowMasks(bitTrack, bitCand, 0); } // check if there is no selected track @@ -899,14 +1095,14 @@ struct HfProducerCharmHadronsTrackFemtoDream { } template - void fillCharmHadMcGen(ParticleType particles) + void fillCharmHadMcGen(const ParticleType& particles) { // Filling particle properties - rowCandCharmHadGen.reserve(particles.size()); + tables.rowCandCharmHadGen.reserve(particles.size() + 1); if constexpr (Channel == DecayChannel::DplusToPiKPi) { for (const auto& particle : particles) { if (std::abs(particle.flagMcMatchGen()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { - rowCandCharmHadGen( + tables.rowCandCharmHadGen( particle.mcCollisionId(), particle.flagMcMatchGen(), particle.originMcGen()); @@ -915,7 +1111,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } else if constexpr (Channel == DecayChannel::LcToPKPi) { for (const auto& particle : particles) { if (std::abs(particle.flagMcMatchGen()) == hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { - rowCandCharmHadGen( + tables.rowCandCharmHadGen( particle.mcCollisionId(), particle.flagMcMatchGen(), particle.originMcGen()); @@ -924,7 +1120,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } else if constexpr (Channel == DecayChannel::D0ToPiK) { for (const auto& particle : particles) { if (std::abs(particle.flagMcMatchGen()) == hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - rowCandCharmHadGen( + tables.rowCandCharmHadGen( particle.mcCollisionId(), particle.flagMcMatchGen(), particle.originMcGen()); @@ -933,7 +1129,7 @@ struct HfProducerCharmHadronsTrackFemtoDream { } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { for (const auto& particle : particles) { if (std::abs(particle.flagMcMatchGen()) == hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) { - rowCandCharmHadGen( + tables.rowCandCharmHadGen( particle.mcCollisionId(), particle.flagMcMatchGen(), particle.originMcGen()); @@ -942,6 +1138,21 @@ struct HfProducerCharmHadronsTrackFemtoDream { } } + void fillXicMcGen(GeneratedXicMc const& particles) + { + tables.rowCandCharmHadGen.reserve(particles.size() + 1); + for (const auto& particle : particles) { + const int absFlag = std::abs(static_cast(particle.flagMcMatchGen())); + if (absFlag == (1 << o2::aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) || + absFlag == (1 << o2::aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { + tables.rowCandCharmHadGen( + particle.mcCollisionId(), + particle.flagMcMatchGen(), + particle.originMcGen()); + } + } + } + /// D0ToPiK void processDataD0ToPiK(FemtoFullCollision const& col, aod::BCsWithTimestamps const&, @@ -969,25 +1180,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcD0ToPiK(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateD0Mc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiK, "Provide Mc for D0ToPiK", false); void processMcD0ToPiKWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcD0ToPiKWithML, "Provide Mc for D0ToPiK with ml", false); @@ -1024,25 +1235,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDstarToD0Pi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDstarMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0Pi, "Provide Mc for DstarToD0Pi", false); void processMcDstarToD0PiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDstarToD0PiWithML, "Provide Mc for DstarToD0Pi with ml", false); @@ -1081,25 +1292,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcDplusToPiKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateDplusMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPi, "Provide Mc for DplusToPiKPi", false); void processMcDplusToPiKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcDplusToPiKPiWithML, "Provide Mc for DplusToPiKPi with ml", false); @@ -1138,25 +1349,25 @@ struct HfProducerCharmHadronsTrackFemtoDream { void processMcLcToPKPi(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, CandidateLcMc const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPi, "Provide Mc for lctopkpi", false); void processMcLcToPKPiWithML(FemtoFullCollisionMc const& col, aod::BCsWithTimestamps const&, FemtoHFMcTracks const& tracks, - aod::McParticles const&, + aod::McParticles const& mcParticles, soa::Join const& candidates) { // get magnetic field for run getMagneticFieldTesla(col.bc_as()); - fillCharmHadronTable(col, tracks, candidates); + fillCharmHadronTable(col, tracks, candidates, &mcParticles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPiWithML, "Provide Mc for lctopkpi with ml", false); @@ -1166,6 +1377,105 @@ struct HfProducerCharmHadronsTrackFemtoDream { fillCharmHadMcGen(particles); } PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcLcToPKPiGen, "Provide Mc Generated lctopkpi", false); + + /// XicToXiPiPi + void processDataXicToXiPiPi(FemtoFullCollision const& col, + aod::BCsWithTimestamps const&, + FemtoHFTracks const& tracks, + aod::FullTracks const& daughterTracks, + soa::Filtered const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processDataXicToXiPiPi, "Data for XicToXiPiPi femto (DCAFitter; no HFCANDXICKF)", false); + + void processDataXicToXiPiPiKf(FemtoFullCollision const& col, + aod::BCsWithTimestamps const&, + FemtoHFTracks const& tracks, + aod::FullTracks const& daughterTracks, + soa::Filtered const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processDataXicToXiPiPiKf, "Data for XicToXiPiPi femto (KFParticle; requires HFCANDXICKF)", false); + + void processDataXicToXiPiPiWithML(FemtoFullCollision const& col, + aod::BCsWithTimestamps const&, + FemtoHFTracks const& tracks, + aod::FullTracks const& daughterTracks, + soa::Filtered> const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processDataXicToXiPiPiWithML, "Data for XicToXiPiPi femto with ML (DCAFitter)", false); + + void processDataXicToXiPiPiWithMLKf(FemtoFullCollision const& col, + aod::BCsWithTimestamps const&, + FemtoHFTracks const& tracks, + aod::FullTracks const& daughterTracks, + soa::Filtered> const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processDataXicToXiPiPiWithMLKf, "Data for XicToXiPiPi femto with ML (KFParticle)", false); + + void processMcXicToXiPiPi(FemtoFullCollisionMc const& col, + aod::BCsWithTimestamps const&, + FemtoHFMcTracks const& tracks, + aod::FullTracks const& daughterTracks, + aod::McParticles const& mcParticles, + soa::Filtered const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPi, "MC for XicToXiPiPi (DCAFitter)", false); + + void processMcXicToXiPiPiKf(FemtoFullCollisionMc const& col, + aod::BCsWithTimestamps const&, + FemtoHFMcTracks const& tracks, + aod::FullTracks const& daughterTracks, + aod::McParticles const& mcParticles, + soa::Filtered const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiKf, "MC for XicToXiPiPi (KFParticle)", false); + + void processMcXicToXiPiPiWithML(FemtoFullCollisionMc const& col, + aod::BCsWithTimestamps const&, + FemtoHFMcTracks const& tracks, + aod::FullTracks const& daughterTracks, + aod::McParticles const& mcParticles, + soa::Filtered> const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithML, "MC for XicToXiPiPi with ML (DCAFitter)", false); + + void processMcXicToXiPiPiWithMLKf(FemtoFullCollisionMc const& col, + aod::BCsWithTimestamps const&, + FemtoHFMcTracks const& tracks, + aod::FullTracks const& daughterTracks, + aod::McParticles const& mcParticles, + soa::Filtered> const& candidates) + { + getMagneticFieldTesla(col.bc_as()); + fillXicHadronTable(col, tracks, candidates, daughterTracks, &mcParticles); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiWithMLKf, "MC for XicToXiPiPi with ML (KFParticle)", false); + + void processMcXicToXiPiPiGen(GeneratedXicMc const& particles) + { + fillXicMcGen(particles); + } + PROCESS_SWITCH(HfProducerCharmHadronsTrackFemtoDream, processMcXicToXiPiPiGen, "MC generated XicToXiPiPi", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx index ebc38e73682..4b8594f8833 100644 --- a/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/TableProducer/producerCharmHadronsV0FemtoDream.cxx @@ -449,9 +449,9 @@ struct HfProducerCharmHadronsV0FemtoDream { { if constexpr (isTrackOrV0) { outputDebugParts(particle.sign(), - (uint8_t)particle.tpcNClsFound(), + static_cast(particle.tpcNClsFound()), particle.tpcNClsFindable(), - (uint8_t)particle.tpcNClsCrossedRows(), + static_cast(particle.tpcNClsCrossedRows()), particle.tpcNClsShared(), particle.tpcInnerParam(), particle.itsNCls(), @@ -506,6 +506,10 @@ struct HfProducerCharmHadronsV0FemtoDream { constexpr int GenFromTransport = -1; // -1 if a particle produced during transport // get list of mothers, but it could be empty (for example in case of injected light nuclei) auto motherparticlesMc = particleMc.template mothers_as(); + float ptPos{-1.f}, ptNeg{-1.f}; + float etaPos{-1.f}, etaNeg{-1.f}; + float phiPos{-1.f}, phiNeg{-1.f}; + int pdgCodePos{0}, pdgCodeNeg{0}; // check pdg code // if this fails, the particle is a fake if (std::abs(pdgCode) == std::abs(v0PDGCode.value)) { @@ -522,9 +526,9 @@ struct HfProducerCharmHadronsV0FemtoDream { // list of mothers is not empty } else if (particleMc.getProcess() == TMCProcess::kPDecay && particleMc.getGenStatusCode() == GenFromTransport && !motherparticlesMc.empty()) { // get direct mother - auto motherparticleMc = motherparticlesMc.front(); + const auto& motherparticleMc = motherparticlesMc.front(); pdgCodeMother = motherparticleMc.pdgCode(); - particleOrigin = checkDaughterType(fdparttype, motherparticleMc.pdgCode()); + particleOrigin = checkDaughterType(fdparttype, motherparticleMc.pdgCode(), pdgCode); // check if particle is material // particle is from inelastic hadronic interaction -> getProcess() == 23 // particle is generated during transport -> getGenStatusCode() == -1 @@ -534,11 +538,39 @@ struct HfProducerCharmHadronsV0FemtoDream { } else { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kElse; } + for (auto const& dauMc : particleMc.template daughters_as()) { + if (dauMc.pdgCode() > 0) { + ptPos = dauMc.pt(); + etaPos = dauMc.eta(); + phiPos = dauMc.phi(); + pdgCodePos = dauMc.pdgCode(); + } else { + ptNeg = dauMc.pt(); + etaNeg = dauMc.eta(); + phiNeg = dauMc.phi(); + pdgCodeNeg = dauMc.pdgCode(); + } + } // if pdg code is wrong, particle is fake } else { particleOrigin = aod::femtodreamMCparticle::ParticleOriginMCTruth::kFake; } + // positive daughter + outputPartsMc(particleOrigin, pdgCodePos, ptPos, etaPos, phiPos); + outputPartsMcLabels(outputPartsMc.lastIndex()); + if (isDebug) { + outputPartsExtMcLabels(outputPartsMc.lastIndex()); + outputDebugPartsMc(pdgCode); + } + // negative daughter + outputPartsMc(particleOrigin, pdgCodeNeg, ptNeg, etaNeg, phiNeg); + outputPartsMcLabels(outputPartsMc.lastIndex()); + if (isDebug) { + outputPartsExtMcLabels(outputPartsMc.lastIndex()); + outputDebugPartsMc(pdgCode); + } + // V0 outputPartsMc(particleOrigin, pdgCode, particleMc.pt(), particleMc.eta(), particleMc.phi()); outputPartsMcLabels(outputPartsMc.lastIndex()); if (isDebug) { @@ -546,9 +578,23 @@ struct HfProducerCharmHadronsV0FemtoDream { outputDebugPartsMc(pdgCodeMother); } } else { + // positive daughter + outputPartsMcLabels(-1); + if (isDebug) { + outputPartsExtMcLabels(-1); + outputDebugPartsMc(0); + } + // negative daughter outputPartsMcLabels(-1); if (isDebug) { outputPartsExtMcLabels(-1); + outputDebugPartsMc(0); + } + // V0 + outputPartsMcLabels(-1); + if (isDebug) { + outputPartsExtMcLabels(-1); + outputDebugPartsMc(0); } } } @@ -722,19 +768,19 @@ struct HfProducerCharmHadronsV0FemtoDream { return; } + if constexpr (Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::LcToPKPi) { + rowCandCharm3Prong.reserve(rowCandCharm3Prong.lastIndex() + sizeCand * 2 + 1); + } else if constexpr (Channel == DecayChannel::D0ToPiK) { + rowCandCharm2Prong.reserve(rowCandCharm2Prong.lastIndex() + sizeCand * 2 + 1); + } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { + rowCandCharmDstar.reserve(rowCandCharmDstar.lastIndex() + sizeCand + 1); + } + outputCollision(vtxZ, mult, multNtr, spher, magField); if constexpr (IsMc) { fillMcCollision(col); } - // Filling candidate properties - if constexpr (Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::LcToPKPi) { - rowCandCharm3Prong.reserve(sizeCand); - } else if constexpr (Channel == DecayChannel::D0ToPiK) { - rowCandCharm2Prong.reserve(sizeCand); - } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { - rowCandCharmDstar.reserve(sizeCand); - } bool isV0Filled = false; bool isSelectedMlLcToPKPi = true; bool isSelectedMlLcToPiKP = true; @@ -1129,10 +1175,10 @@ struct HfProducerCharmHadronsV0FemtoDream { } template - void fillCharmHadMcGen(ParticleType particles) + void fillCharmHadMcGen(const ParticleType& particles) { // Filling particle properties - rowCandCharmHadGen.reserve(particles.size()); + rowCandCharmHadGen.reserve(particles.size() + 1); if constexpr (Channel == DecayChannel::DplusToPiKPi) { for (const auto& particle : particles) { if (std::abs(particle.flagMcMatchGen()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { diff --git a/PWGHF/HFC/Tasks/CMakeLists.txt b/PWGHF/HFC/Tasks/CMakeLists.txt index 4fa2b6cd959..4551ccd2b0f 100644 --- a/PWGHF/HFC/Tasks/CMakeLists.txt +++ b/PWGHF/HFC/Tasks/CMakeLists.txt @@ -21,6 +21,7 @@ o2physics_add_dpl_workflow(task-charm-hadrons-v0-femto-dream o2physics_add_dpl_workflow(task-correlation-d0-hadrons SOURCES taskCorrelationD0Hadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -41,11 +42,13 @@ o2physics_add_dpl_workflow(task-correlation-dplus-dplus-reduced o2physics_add_dpl_workflow(task-correlation-dplus-hadrons SOURCES taskCorrelationDplusHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-correlation-ds-hadrons SOURCES taskCorrelationDsHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -61,12 +64,19 @@ o2physics_add_dpl_workflow(task-correlation-hfe-hadrons o2physics_add_dpl_workflow(task-correlation-lc-hadrons SOURCES taskCorrelationLcHadrons.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-flow SOURCES taskFlow.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGCFCore COMPONENT_NAME Analysis) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/PWGCF/Core) + +o2physics_add_dpl_workflow(task-charm-hadrons-charm-femto-dream + SOURCES taskCharmHadronsCharmFemtoDream.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx new file mode 100644 index 00000000000..74ab959c959 --- /dev/null +++ b/PWGHF/HFC/Tasks/taskCharmHadronsCharmFemtoDream.cxx @@ -0,0 +1,457 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file taskCharmHadronsCharmFemtoDream.cxx +/// \brief Build DD pair femtoscopy distributions +/// \author Biao Zhang, Heidelberg University, biao.zhang@cern.ch + +#include "PWGCF/DataModel/FemtoDerived.h" +#include "PWGCF/FemtoDream/Core/femtoDreamMath.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; +using namespace o2::analysis::femtoDream; + +struct HfTaskCharmHadronsCharmFemtoDream { + // Like/unlike sign refers to charm flavour; D0 is electrically neutral. + // Keep charge-conjugate channels separate so they can also be combined + // offline. + enum PairChannel : int { + D0D0LikeSign = 0, + D0barD0barLikeSign = 1, + D0D0barUnlikeSign = 2, + D0DstarPlusLikeSign = 3, + D0barDstarMinusLikeSign = 4, + D0DstarMinusUnlikeSign = 5, + D0barDstarPlusUnlikeSign = 6, + NPairChannels = 7 + }; + static constexpr int MixingBinPolicyMax = 2; + + Produces rowFemtoResultCharm2Prong; + Produces rowFemtoResultCharmDstar; + Produces rowFemtoResultColl; + + Configurable ptD0Min{"ptD0Min", 0.f, "Minimum D0 pT"}; + Configurable ptD0Max{"ptD0Max", 36.f, "Maximum D0 pT"}; + Configurable ptDstarMin{"ptDstarMin", 0.f, "Minimum Dstar pT"}; + Configurable ptDstarMax{"ptDstarMax", 36.f, "Maximum Dstar pT"}; + Configurable etaCandMax{"etaCandMax", 0.8f, "Maximum absolute candidate eta"}; + Configurable massD0Min{"massD0Min", 1.7f, "Minimum D0 mass (keep sidebands)"}; + Configurable massD0Max{"massD0Max", 2.0f, "Maximum D0 mass"}; + Configurable deltaMassDstarMin{"deltaMassDstarMin", MassPiPlus, "Minimum Dstar-D0 mass difference"}; + Configurable deltaMassDstarMax{"deltaMassDstarMax", 0.17f, "Maximum Dstar-D0 mass difference"}; + Configurable massD0DaughterMin{"massD0DaughterMin", 1.80f, "Minimum Dstar daughter D0 mass"}; + Configurable massD0DaughterMax{"massD0DaughterMax", 1.93f, "Maximum Dstar daughter D0 mass"}; + Configurable useMl{"useMl", false, "Require valid ML scores and apply score cuts"}; + Configurable bkgBdtD0Max{"bkgBdtD0Max", 1.f, "Maximum D0 background score"}; + Configurable promptBdtD0Min{"promptBdtD0Min", 0.f, "Minimum D0 prompt score"}; + Configurable bkgBdtDstarMax{"bkgBdtDstarMax", 1.f, "Maximum Dstar background score"}; + Configurable promptBdtDstarMin{"promptBdtDstarMin", 0.f, "Minimum Dstar prompt score"}; + Configurable charmHadCandSel{"charmHadCandSel", 1, "Minimum reduced charm candidate selection flag"}; + struct : ConfigurableGroup { + std::string prefix = "eventSel"; + Configurable useCentrality{"useCentrality", false, "Apply percentile selection (requires a centrality-enabled producer)"}; + Configurable multMin{"multMin", 0, "Minimum multiplicity (MultNtr)"}; + Configurable multMax{"multMax", 99999, "Maximum multiplicity (MultNtr)"}; + Configurable multPercentileMin{"multPercentileMin", 0.f, "Minimum multiplicity percentile"}; + Configurable multPercentileMax{"multPercentileMax", 100.f, "Maximum multiplicity percentile"}; + } eventSel; + + struct : ConfigurableGroup { + Configurable doMixEvent{"doMixEvent", true, "Enable mixed events"}; + Configurable mixingBinPolicy{"mixingBinPolicy", 0, "0: multiplicity, 1: percentile, 2: both"}; + Configurable mixingDepth{"mixingDepth", 5, "Number of neighbours for O2 event mixing"}; + } mixSetting; + SliceCache cache; + using FilteredCollisions = soa::Filtered>; + using FilteredCharmCand2Prongs = soa::Filtered; + using FilteredCharmCandDstars = soa::Filtered; + + Filter eventMultiplicity = aod::femtodreamcollision::multNtr >= eventSel.multMin && aod::femtodreamcollision::multNtr <= eventSel.multMax; + Filter eventMultiplicityPercentile = ifnode(eventSel.useCentrality, aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax, Node{LiteralNode{true}}); + Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmHadCandSel; + + // Full-table process inputs do not automatically register grouping caches. + // Register both keys explicitly for the sliceByCached calls below. + Preslice perCollisionD0 = aod::femtodreamparticle::fdCollisionId; + Preslice perCollisionDstar = aod::femtodreamparticle::fdCollisionId; + + Partition partitionCharmHadron2Prong = ifnode(useMl, aod::fdhf::bdtBkg <= bkgBdtD0Max && aod::fdhf::bdtPrompt >= promptBdtD0Min, Node{LiteralNode{true}}); + Partition partitionCharmHadronDstar = ifnode(useMl, aod::fdhf::bdtBkg <= bkgBdtDstarMax && aod::fdhf::bdtPrompt >= promptBdtDstarMin, Node{LiteralNode{true}}); + + ConfigurableAxis mixingBinMult{"mixingBinMult", {VARIABLE_WIDTH, 0.f, 20.f, 60.f, 200.f}, "Mixing bins - multiplicity"}; + ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.f, 100.f}, "Mixing bins - multiplicity percentile"}; + ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; + aod::femtodreamcollision::BitMaskType bitMask = 1 << 0; + HistogramRegistry registry{"registry"}; + + void init(InitContext const&) + { + if (doprocessD0D0 == doprocessD0Dstar) { + LOGP(fatal, "Enable exactly one charm-charm analysis process"); + } + if (mixSetting.mixingDepth < 0 || mixSetting.mixingBinPolicy < 0 || mixSetting.mixingBinPolicy > MixingBinPolicyMax || ptD0Min < 0 || ptD0Min >= ptD0Max || ptDstarMin < 0 || ptDstarMin >= ptDstarMax || etaCandMax <= 0 || massD0Min >= massD0Max || deltaMassDstarMin >= deltaMassDstarMax || massD0DaughterMin >= massD0DaughterMax || charmHadCandSel < 1 || eventSel.multMin > eventSel.multMax || eventSel.multPercentileMin > eventSel.multPercentileMax) { + LOGP(fatal, "Invalid charm-charm selection or mixing configuration"); + } + colBinningMult = {{mixingBinVztx, mixingBinMult}}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; + const AxisSpec kstar{400, 0., 2., "k* (GeV/c)"}; + const AxisSpec massD0{300, massD0Min.value, massD0Max.value, "M(Kpi) (GeV/c2)"}; + const AxisSpec deltaMass{310, deltaMassDstarMin.value, deltaMassDstarMax.value, "Delta M (GeV/c2)"}; + const AxisSpec pt{72, ptD0Min.value, ptD0Max.value, "D0 pT (GeV/c)"}; + const AxisSpec ptStar{72, ptDstarMin.value, ptDstarMax.value, "Dstar pT (GeV/c)"}; + const AxisSpec mult{mixingBinMult, "NTracksPV"}; + const AxisSpec channel{static_cast(NPairChannels), -0.5, static_cast(NPairChannels) - 0.5, "0=D0D0 LS,1=D0barD0bar LS,2=D0D0bar US,3=D0D*+ LS,4=D0barD*- LS,5=D0D*- US,6=D0barD*+ US"}; + registry.add("SE/D0D0", "Same event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("ME/D0D0", "Mixed event", kTHnSparseF, {kstar, massD0, massD0, pt, pt, mult, channel}); + registry.add("SE/D0Dstar", "Same event", kTHnSparseF, {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("ME/D0Dstar", "Mixed event", kTHnSparseF, {kstar, massD0, deltaMass, pt, ptStar, mult, channel}); + registry.add("QA/events", ";0=in mixing range,1=outside;events", kTH1F, {{2, -0.5, 1.5}}); + registry.add("MixingQA/hSECollisionBins", ";mixing bin;events", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0D0", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("MixingQA/hMECollisionBinsD0Dstar", ";mixing bin;event pairs", kTH1F, {{1000, -0.5, 999.5}}); + registry.add("QA/sharedDaughters", ";0=D0D0,1=D0Dstar;rejected pairs", kTH1F, {{2, -0.5, 1.5}}); + registry.add("QA/d0MassPt", ";M(Kpi);pT", kTH2F, {massD0, pt}); + registry.add("QA/dstarMassPt", ";Delta M;pT", kTH2F, {deltaMass, ptStar}); + } + + template + static bool sharesDaughter(D0Row const& a, OtherRow const& b) + { + for (const auto& id : std::array{a.prong0Id(), a.prong1Id()}) { + if (id < 0) { + continue; + } + if (id == b.prong0Id() || id == b.prong1Id()) { + return true; + } + if constexpr (IsDstar) { + if (id == b.prong2Id()) { + return true; + } + } + } + return false; + } + + // D0 charge denotes flavour, not electric charge. + template + static PairChannel pairChannel(D0Row const& d0, OtherRow const& other) + { + if constexpr (!IsDstar) { + return d0.charge() != other.charge() ? D0D0barUnlikeSign : (d0.charge() > 0 ? D0D0LikeSign : D0barD0barLikeSign); + } + return d0.charge() > 0 ? (other.charge() > 0 ? D0DstarPlusLikeSign : D0DstarMinusUnlikeSign) : (other.charge() < 0 ? D0barDstarMinusLikeSign : D0barDstarPlusUnlikeSign); + } + + // Canonical ordering keeps mass/pt axes independent of event ordering for + // D0D0. + template + static bool reverseD0Order(FirstRow const& first, SecondRow const& second) + { + if (first.charge() != second.charge()) { + return first.charge() < second.charge(); // D0 first for D0-D0bar + } + return first.pt() < second.pt(); // leading pT first for equal flavours + } + + template + void validateMlScores(CandsType const& candidates) + { + if (!useMl) { + return; + } + for (const auto& row : candidates) { + if (!std::isfinite(row.bdtBkg()) || !std::isfinite(row.bdtPrompt()) || row.bdtBkg() < 0.f || row.bdtPrompt() < 0.f) { + LOGP(fatal, "ML cuts requested on missing/invalid scores: use an ML producer process"); + } + } + } + + template + bool select(Row const& row, float& mass) + { + if (std::abs(row.charge()) != 1 || !std::isfinite(row.pt()) || !std::isfinite(row.eta()) || !std::isfinite(row.phi()) || std::abs(row.eta()) >= etaCandMax || row.pt() < (IsDstar ? ptDstarMin.value : ptD0Min.value) || row.pt() >= (IsDstar ? ptDstarMax.value : ptD0Max.value)) { + return false; + } + const std::array masses = row.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; + if constexpr (IsDstar) { + const float daughterMass = row.mDaughD0(masses); + if (!std::isfinite(daughterMass) || daughterMass < massD0DaughterMin || daughterMass >= massD0DaughterMax) { + return false; + } + mass = row.m(std::array{masses[0], masses[1], MassPiPlus}) - daughterMass; + } else { + mass = row.m(masses); + } + if (!std::isfinite(mass) || mass < (IsDstar ? deltaMassDstarMin.value : massD0Min.value) || mass >= (IsDstar ? deltaMassDstarMax.value : massD0Max.value)) { + return false; + } + if constexpr (IsDstar) { + if constexpr (FillQa) { + registry.fill(HIST("QA/dstarMassPt"), mass, row.pt()); + } + } else { + if constexpr (FillQa) { + registry.fill(HIST("QA/d0MassPt"), mass, row.pt()); + } + } + return true; + } + + template + void fillPair(FirstRow const& first, SecondRow const& second, float firstMass, float secondMass, float mult) + { + if constexpr (!Mixed) { + if (sharesDaughter(first, second)) { + registry.fill(HIST("QA/sharedDaughters"), IsDstar ? 1 : 0); + return; + } + } + const bool reverse = !IsDstar && reverseD0Order(first, second); + const float mass1 = reverse ? secondMass : firstMass; + const float mass2 = reverse ? firstMass : secondMass; + const float pt1 = reverse ? second.pt() : first.pt(); + const float pt2 = reverse ? first.pt() : second.pt(); + // Read kinematics directly from the candidate rows. Only D0D0 can reverse; + // its two nominal parent masses are equal. Never use Dstar delta mass here. + const float kstar = reverse ? FemtoDreamMath::getkstar(second, MassD0, first, MassD0) : FemtoDreamMath::getkstar(first, MassD0, second, IsDstar ? MassDStar : MassD0); + if (!std::isfinite(kstar)) { + return; + } + const int channel = pairChannel(first, second); + if constexpr (IsDstar && Mixed) { + registry.fill(HIST("ME/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else if constexpr (IsDstar) { + registry.fill(HIST("SE/D0Dstar"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else if constexpr (Mixed) { + registry.fill(HIST("ME/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } else { + registry.fill(HIST("SE/D0D0"), kstar, mass1, mass2, pt1, pt2, mult, channel); + } + } + + template + int mixingBin(CollType const& col) + { + switch (mixSetting.mixingBinPolicy) { + case aod::femtodreamcollision::kMult: + return colBinningMult.getBin({col.posZ(), col.multNtr()}); + case aod::femtodreamcollision::kMultPercentile: + return colBinningMultPercentile.getBin({col.posZ(), col.multV0M()}); + case aod::femtodreamcollision::kMultMultPercentile: + return colBinningMultMultPercentile.getBin({col.posZ(), col.multNtr(), col.multV0M()}); + default: + return -1; + } + } + + template + void doMixedEvent(FilteredCollisions const& cols, D0Table const& d0Rows, OtherTable const& otherRows, Policy const& policy) + { + Partition partitionMaskedCol1 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + // TrackOne denotes D0 presence, TrackTwo denotes Dstar presence. + Partition partitionMaskedCol2 = (aod::femtodreamcollision::bitmaskTrackOne & bitMask) == bitMask; + Partition partitionMaskedColDstar = (aod::femtodreamcollision::bitmaskTrackTwo & bitMask) == bitMask; + partitionMaskedCol1.bindTable(cols); + if constexpr (IsDstar) { + partitionMaskedColDstar.bindTable(cols); + } else { + partitionMaskedCol2.bindTable(cols); + } + auto const& secondCollisions = IsDstar ? *partitionMaskedColDstar.mFiltered : *partitionMaskedCol2.mFiltered; + for (const auto& [collision1, collision2] : combinations(soa::CombinationsBlockFullIndexPolicy(policy, mixSetting.mixingDepth, -1, *partitionMaskedCol1.mFiltered, secondCollisions))) { + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } + if constexpr (!IsDstar) { + // Full policy returns both orientations for identical inputs. Count + // once. + if (collision1.globalIndex() > collision2.globalIndex()) { + continue; + } + } + if (!std::isfinite(collision1.magField()) || collision1.magField() != collision2.magField()) { + continue; + } + if constexpr (IsDstar) { + registry.fill(HIST("MixingQA/hMECollisionBinsD0Dstar"), mixingBin(collision1)); + } else { + registry.fill(HIST("MixingQA/hMECollisionBinsD0D0"), mixingBin(collision1)); + } + auto first = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision1.globalIndex(), cache); + auto second = otherRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, collision2.globalIndex(), cache); + // Species roles remain fixed; the full policy supplies the reverse event + // orientation when eligible. Do not add a second manual reverse loop. + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(first, second))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, collision1.multNtr()); + } + } + } + } + + // Write candidates once per collision, independently of SE/ME pair counts. + // Reuse the same output tables and field conventions as the track/V0 tasks. + template + void fillTables(CollType const& col, D0Slice const& d0s, DstarSlice const& dstars) + { + int64_t timeStamp = -1; + bool hasCandidate = false; + auto recordTimeStamp = [&](int64_t value) { + if (hasCandidate && value != timeStamp) { + LOGP(fatal, "Inconsistent charm timestamps in one reduced collision"); + } + timeStamp = value; + hasCandidate = true; + }; + for (const auto& part : d0s) { + float mass{}; + if (!select(part, mass)) { + continue; + } + recordTimeStamp(part.timeStamp()); + rowFemtoResultCharm2Prong(col.globalIndex(), timeStamp, mass, part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + } + if constexpr (WithDstar) { + for (const auto& part : dstars) { + float mass{}; + if (!select(part, mass)) { + continue; + } + recordTimeStamp(part.timeStamp()); + const std::array daughterMasses = part.charge() > 0 ? std::array{MassPiPlus, MassKPlus} : std::array{MassKPlus, MassPiPlus}; + // CharmM is delta mass, CharmDaughM is M(Kpi), as in D+Track. + rowFemtoResultCharmDstar(col.globalIndex(), timeStamp, mass, part.mDaughD0(daughterMasses), part.pt(), part.eta(), part.phi(), part.prong0Id(), part.prong1Id(), part.prong2Id(), part.charge(), part.bdtBkg(), part.bdtPrompt(), part.bdtFD(), 0, 0); + } + } + // Retain D0-only/Dstar-only events even when no clean SE pair exists. + // MC fields above are zero for these data processes, as in existing tasks. + if (hasCandidate) { + rowFemtoResultColl(col.globalIndex(), timeStamp, col.posZ(), col.multNtr()); + } + } + + template + void runWithPolicy(FilteredCollisions const& cols, D0Table const& d0Rows, DstarTable const& dstarRows, Policy const& policy) + { + for (const auto& col : cols) { + auto d0s = d0Rows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + // Export before online mixing-bin cuts, so offline mixing can change bins. + if constexpr (WithDstar) { + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + fillTables(col, d0s, dstars); + } else { + fillTables(col, d0s, d0s); + } + const int bin = mixingBin(col); + if (bin < 0 || !std::isfinite(col.magField())) { + registry.fill(HIST("QA/events"), 1); + continue; + } + registry.fill(HIST("QA/events"), 0); + registry.fill(HIST("MixingQA/hSECollisionBins"), bin); + for (const auto& [row1, row2] : combinations(soa::CombinationsStrictlyUpperIndexPolicy(d0s, d0s))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, col.multNtr()); + } + } + if constexpr (WithDstar) { + auto dstars = dstarRows.sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + for (const auto& [row1, row2] : combinations(soa::CombinationsFullIndexPolicy(d0s, dstars))) { + float mass1{}, mass2{}; + if (select(row1, mass1) && select(row2, mass2)) { + fillPair(row1, row2, mass1, mass2, col.multNtr()); + } + } + } + } + if (mixSetting.doMixEvent && mixSetting.mixingDepth > 0) { + doMixedEvent(cols, d0Rows, d0Rows, policy); + if constexpr (WithDstar) { + doMixedEvent(cols, d0Rows, dstarRows, policy); + } + } + } + + template + void run(FilteredCollisions const& cols, FilteredCharmCand2Prongs const& d0Rows, DstarTable const& dstarRows) + { + validateMlScores(d0Rows); + partitionCharmHadron2Prong.bindTable(d0Rows); + if constexpr (WithDstar) { + validateMlScores(dstarRows); + partitionCharmHadronDstar.bindTable(dstarRows); + } + switch (mixSetting.mixingBinPolicy) { + case aod::femtodreamcollision::kMult: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMult); + break; + case aod::femtodreamcollision::kMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultPercentile); + break; + case aod::femtodreamcollision::kMultMultPercentile: + runWithPolicy(cols, partitionCharmHadron2Prong, partitionCharmHadronDstar, colBinningMultMultPercentile); + break; + default: + LOGP(fatal, "Invalid mixing binning policy"); + } + } + + void processD0D0(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s) + { + run(cols, d0s, d0s); + } + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0D0, "D0D0 data, SE and ME", true); + + void processD0Dstar(FilteredCollisions const& cols, + FilteredCharmCand2Prongs const& d0s, + FilteredCharmCandDstars const& dstars) + { + run(cols, d0s, dstars); + } + PROCESS_SWITCH(HfTaskCharmHadronsCharmFemtoDream, processD0Dstar, "D0D0 and D0Dstar data, SE and ME", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx index a0b6b1aed76..cfbd216ec22 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsTrackFemtoDream.cxx @@ -86,7 +86,8 @@ struct HfTaskCharmHadronsTrackFemtoDream { enum DecayChannel { DplusToPiKPi = 0, LcToPKPi, D0ToPiK, - DstarToD0Pi + DstarToD0Pi, + XicToXiPiPi }; constexpr static int OriginRecPrompt = 1; @@ -107,14 +108,14 @@ struct HfTaskCharmHadronsTrackFemtoDream { struct : ConfigurableGroup { Configurable charmHadBkgBDTmax{"charmHadBkgBDTmax", 1., "Maximum background bdt score for Charm Hadron (particle 2)"}; Configurable charmHadCandSel{"charmHadCandSel", 1, "candidate selection for charm hadron"}; - Configurable charmHadMcSel{"charmHadMcSel", DecayChannelMain::LcToPKPi, "charm hadron selection for mc, DplusToPiKPi = 1, LcToPKPi = 17"}; + Configurable charmHadMcSel{"charmHadMcSel", DecayChannelMain::LcToPKPi, "MC matching flag for the selected charm hadron decay channel"}; Configurable charmHadFdBDTmin{"charmHadFdBDTmin", 0., "Minimum feed-down bdt score Charm Hadron (particle 2)"}; Configurable charmHadFdBDTmax{"charmHadFdBDTmax", 1., "Maximum feed-down bdt score Charm Hadron (particle 2)"}; Configurable charmHadMaxInvMass{"charmHadMaxInvMass", 2.45, "Maximum invariant mass of Charm Hadron (particle 2)"}; Configurable charmHadMinInvMass{"charmHadMinInvMass", 2.15, "Minimum invariant mass of Charm Hadron (particle 2)"}; Configurable charmHadMinPt{"charmHadMinPt", 0., "Minimum pT of Charm Hadron (particle 2)"}; Configurable charmHadMaxPt{"charmHadMaxPt", 999., "Maximum pT of Charm Hadron (particle 2)"}; - Configurable charmHadPDGCode{"charmHadPDGCode", 4122, "PDG code of particle 2 Charm Hadron"}; + Configurable charmHadPDGCode{"charmHadPDGCode", Pdg::kLambdaCPlus, "PDG code of particle 2 Charm Hadron"}; Configurable charmHadPromptBDTmin{"charmHadPromptBDTmin", 0., "Minimum prompt bdt score Charm Hadron (particle 2)"}; Configurable charmHadPromptBDTmax{"charmHadPromptBDTmax", 1., "Maximum prompt bdt score Charm Hadron (particle 2)"}; } charmSel; @@ -150,7 +151,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { /// Particle 1 (track) struct : ConfigurableGroup { Configurable cutBitTrack1{"cutBitTrack1", 8188, "Particle 1 (Track) - Selection bit from cutCulator"}; - Configurable pdgCodeTrack1{"pdgCodeTrack1", 2212, "PDG code of Particle 1 (Track)"}; + Configurable pdgCodeTrack1{"pdgCodeTrack1", kProton, "PDG code of Particle 1 (Track)"}; Configurable pidThresTrack1{"pidThresTrack1", 0.75, "Momentum threshold for PID selection for particle 1 (Track)"}; Configurable tpcBitTrack1{"tpcBitTrack1", 4, "PID TPC bit from cutCulator for particle 1 (Track)"}; Configurable tpcTofBitTrack1{"tpcTofBitTrack1", 2, "PID TPCTOF bit from cutCulator for particle 1 (Track)"}; @@ -164,6 +165,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { using FilteredCharmCand3Prongs = soa::Filtered; using FilteredCharmCand3Prong = FilteredCharmCand3Prongs::iterator; + using FilteredCharmCand3ProngsXic = soa::Filtered; + using FilteredCharmCand3ProngXic = FilteredCharmCand3ProngsXic::iterator; + using FilteredCharmCand2Prongs = soa::Filtered; using FilteredCharmCand2Prong = FilteredCharmCand2Prongs::iterator; @@ -173,6 +177,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { using FilteredCharmMcCand3Prongs = soa::Filtered>; using FilteredCharmMcCand3Prong = FilteredCharmMcCand3Prongs::iterator; + using FilteredCharmMcCand3ProngsXic = soa::Filtered>; + using FilteredCharmMcCand3ProngXic = FilteredCharmMcCand3ProngsXic::iterator; + using FilteredCharmMcCand2Prongs = soa::Filtered>; using FilteredCharmMcCand2Prong = FilteredCharmMcCand2Prongs::iterator; @@ -185,7 +192,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { using FilteredMcColisions = soa::Filtered>; using FilteredMcColision = FilteredMcColisions::iterator; - using FilteredFDMcParts = soa::Filtered>; + using FilteredFDMcParts = soa::Filtered>; using FilteredFDMcPart = FilteredFDMcParts::iterator; using FilteredFDParticles = soa::Filtered>; @@ -195,32 +202,37 @@ struct HfTaskCharmHadronsTrackFemtoDream { Filter eventMultiplicityPercentile = aod::femtodreamcollision::multV0M >= eventSel.multPercentileMin && aod::femtodreamcollision::multV0M <= eventSel.multPercentileMax; Filter hfCandSelFilter = aod::fdhf::candidateSelFlag >= charmSel.charmHadCandSel; Filter hfMcSelFilter = (nabs(aod::fdhf::flagMc) == charmSel.charmHadMcSel); - Filter trackEtaFilterLow = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta < trackSel.etaTrack1Max, true); - Filter trackEtaFilterUp = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta > trackSel.etaTrack1Min, true); - Filter trackPtFilterLow = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt < trackSel.ptTrack1Max, true); - Filter trackPtFilterUp = ifnode(aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt > trackSel.ptTrack1Min, true); - - Preslice perCol = aod::femtodreamparticle::fdCollisionId; - Preslice perHf3ProngByCol = aod::femtodreamparticle::fdCollisionId; - Preslice perHf2ProngByCol = aod::femtodreamparticle::fdCollisionId; - Preslice perHfDstarByCol = aod::femtodreamparticle::fdCollisionId; + Filter trackEtaFilterLow = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta < trackSel.etaTrack1Max, true); + Filter trackEtaFilterUp = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::eta > trackSel.etaTrack1Min, true); + Filter trackPtFilterLow = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt < trackSel.ptTrack1Max, true); + Filter trackPtFilterUp = ifnode(aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack), aod::femtodreamparticle::pt > trackSel.ptTrack1Min, true); + + struct : PresliceGroup { + Preslice perCol = aod::femtodreamparticle::fdCollisionId; + Preslice perHf3ProngByCol = aod::femtodreamparticle::fdCollisionId; + Preslice perHf3ProngXicByCol = aod::femtodreamparticle::fdCollisionId; + Preslice perHf2ProngByCol = aod::femtodreamparticle::fdCollisionId; + Preslice perHfDstarByCol = aod::femtodreamparticle::fdCollisionId; + } preslices; /// Partition for particle 1 - Partition partitionTrk1 = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); - Partition partitionTrk1Ka = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); + Partition partitionTrk1 = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); + Partition partitionTrk1Ka = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); - Partition partitionMcTrk1 = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && + Partition partitionMcTrk1 = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && ifnode(aod::femtodreamparticle::pt * coshEta(aod::femtodreamparticle::eta) <= trackSel.pidThresTrack1, ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcBitTrack1), ncheckbit(aod::femtodreamparticle::pidcut, trackSel.tpcTofBitTrack1)); - Partition partitionMcTrk1Ka = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); + Partition partitionMcTrk1Ka = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && (ncheckbit(aod::femtodreamparticle::cut, trackSel.cutBitTrack1)) && (aod::femtodreamparticle::pidcut == PidTrackPass); /// Partition for particle 2 Partition partitionCharmHadron3Prong = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; + Partition partitionCharmHadron3ProngXic = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; Partition partitionCharmHadron2Prong = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; Partition partitionCharmHadronDstar = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; Partition partitionMcCharmHadron3Prong = aod::fdhf::originMcRec == OriginRecPrompt || aod::fdhf::originMcRec == OriginRecFD; + Partition partitionMcCharmHadron3ProngXic = aod::fdhf::originMcRec == OriginRecPrompt || aod::fdhf::originMcRec == OriginRecFD; Partition partitionMcCharmHadron2Prong = aod::fdhf::originMcRec == OriginRecPrompt || aod::fdhf::originMcRec == OriginRecFD; Partition partitionMcCharmHadronDstar = aod::fdhf::originMcRec == OriginRecPrompt || aod::fdhf::originMcRec == OriginRecFD; @@ -251,9 +263,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { ConfigurableAxis mixingBinMultPercentile{"mixingBinMultPercentile", {VARIABLE_WIDTH, 0.0f, 100.f}, "Mixing bins - multiplicity percentile"}; ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.0f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{mixingBinVztx, mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{mixingBinVztx, mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -282,25 +294,27 @@ struct HfTaskCharmHadronsTrackFemtoDream { HistogramRegistry registryMixQa{"registryMixQa"}; HistogramRegistry registryCharmHadronQa{"registryCharmHadronQa"}; - float massOne = o2::analysis::femtoDream::getMass(trackSel.pdgCodeTrack1); - float massTwo = o2::analysis::femtoDream::getMass(charmSel.charmHadPDGCode); - int8_t partSign = 0; + float massOne = 0.f; + float massTwo = 0.f; int64_t processType = 0; void init(InitContext& /*context*/) { - std::array processes = {doprocessDataLcTrk, doprocessDataDplusTrk, doprocessDataD0Trk, doprocessDataDstarTrk, doprocessMcLcTrk, doprocessMcDplusTrk, doprocessMcD0Trk, doprocessMcDstarTrk}; + std::array processes = {doprocessDataLcTrk, doprocessDataDplusTrk, doprocessDataD0Trk, doprocessDataDstarTrk, doprocessDataXicTrk, doprocessMcLcTrk, doprocessMcDplusTrk, doprocessMcD0Trk, doprocessMcDstarTrk, doprocessMcXicTrk}; if (std::accumulate(processes.begin(), processes.end(), 0) != 1) { LOGP(fatal, "One and only one process function must be enabled at a time."); } - bool process3Prong = doprocessDataLcTrk || doprocessDataDplusTrk || doprocessMcLcTrk || doprocessMcDplusTrk; + bool process3Prong = doprocessDataLcTrk || doprocessDataDplusTrk || doprocessDataXicTrk || doprocessMcLcTrk || doprocessMcDplusTrk || doprocessMcXicTrk; bool process2Prong = doprocessDataD0Trk || doprocessMcD0Trk; bool processDstar = doprocessDataDstarTrk || doprocessMcDstarTrk; + massOne = o2::analysis::femtoDream::getMass(trackSel.pdgCodeTrack1.value); + massTwo = o2::analysis::femtoDream::getMass(charmSel.charmHadPDGCode.value); + // setup columnpolicy for binning - colBinningMult = {{mixingBinVztx, mixingBinMult}, true}; - colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}, true}; + colBinningMult = {{mixingBinVztx, mixingBinMult}}; + colBinningMultPercentile = {{mixingBinVztx, mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{mixingBinVztx, mixingBinMult, mixingBinMultPercentile}}; eventHisto.init(®istry); allTrackHisto.init(®istry, binmultTempFit, binMulPercentile, binpTTrack, binEta, binPhi, binTempFitVarTrack, binNSigmaTPC, binNSigmaTOF, binNSigmaTPCTOF, binTPCClusters, dummy, dummy, isMc, trackSel.pdgCodeTrack1, true); selectedTrackHisto.init(®istry, binmultTempFit, binMulPercentile, binpTTrack, binEta, binPhi, binTempFitVarTrack, binNSigmaTPC, binNSigmaTOF, binNSigmaTPCTOF, binTPCClusters, dummy, dummy, isMc, trackSel.pdgCodeTrack1, true); @@ -312,7 +326,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { highkstarCut, smearingByOrigin, binInvMass); - sameEventCont.setPDGCodes(trackSel.pdgCodeTrack1, charmSel.charmHadPDGCode); + sameEventCont.setPDGCodes(trackSel.pdgCodeTrack1.value, charmSel.charmHadPDGCode.value); mixedEventCont.init(®istry, binkstar, binpTTrack, binkT, binmT, mixingBinMult, mixingBinMultPercentile, bin4Dkstar, bin4DmT, bin4DMult, bin4DmultPercentile, @@ -320,7 +334,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { highkstarCut, smearingByOrigin, binInvMass); - mixedEventCont.setPDGCodes(trackSel.pdgCodeTrack1, charmSel.charmHadPDGCode); + mixedEventCont.setPDGCodes(trackSel.pdgCodeTrack1.value, charmSel.charmHadPDGCode.value); registryMixQa.add("MixingQA/hSECollisionBins", "; bin; Entries", kTH1F, {{120, -0.5, 119.5}}); registryMixQa.add("MixingQA/hSECollisionPool", "; Vz (cm); Mul", kTH2F, {{100, -10, 10}, {200, 0, 200}}); registryMixQa.add("MixingQA/hMECollisionBins", "; bin; Entries", kTH1F, {{120, -0.5, 119.5}}); @@ -349,7 +363,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { } /// Compute the charm hadron candidates mass with the daughter masses - /// assumes the candidate is either a D+ or Λc+ or D0 or Dstar + /// assumes the candidate is either a D+ or Λc+ or D0 or Dstar or Ξc+ template float getCharmHadronMass(const Candidate& cand, bool ReturnDaughMass = false) { @@ -368,10 +382,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { if (cand.candidateSelFlag() == 1) { invMass = cand.m(std::array{MassPiPlus, MassKPlus}); return invMass; - } else { - invMass = cand.m(std::array{MassKPlus, MassPiPlus}); - return invMass; } + invMass = cand.m(std::array{MassKPlus, MassPiPlus}); + return invMass; } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { // D* → D0π (PDG: 413) float mDstar = 0.f; float mD0 = 0.f; @@ -384,9 +397,11 @@ struct HfTaskCharmHadronsTrackFemtoDream { } if (ReturnDaughMass) { return mD0; - } else { - return mDstar - mD0; } + return mDstar - mD0; + } else if constexpr (Channel == DecayChannel::XicToXiPiPi) { + invMass = cand.m(std::array{MassXiMinus, MassPiPlus, MassPiPlus}); + return invMass; } // Add more channels as needed return 0.f; @@ -433,7 +448,15 @@ struct HfTaskCharmHadronsTrackFemtoDream { return static_cast(RecoDecay::m(pVecCharmTrk, massCharmTrk)); } - // 3-prong:Λc → p K π, D+ → π K π + track + // Ξc⁺ → Ξ π π + track + if constexpr (Channel == DecayChannel::XicToXiPiPi) { + auto pVecProng2 = RecoDecayPtEtaPhi::pVector(cand.prong2Pt(), cand.prong2Eta(), cand.prong2Phi()); + const auto pVecCharmTrk = std::array{pVecProng0, pVecProng1, pVecProng2, pVecTrack}; + const std::array massCharmTrk{MassXiMinus, MassPiPlus, MassPiPlus, trackMassHyp}; + return static_cast(RecoDecay::m(pVecCharmTrk, massCharmTrk)); + } + + // 3-prong: Λc → p K π, D+ → π K π, D* → D0π + track if constexpr (Channel == DecayChannel::LcToPKPi || Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::DstarToD0Pi) { auto pVecProng2 = RecoDecayPtEtaPhi::pVector(cand.prong2Pt(), cand.prong2Eta(), cand.prong2Phi()); const auto pVecCharmTrk = std::array{pVecProng0, pVecProng1, pVecProng2, pVecTrack}; @@ -502,6 +525,19 @@ struct HfTaskCharmHadronsTrackFemtoDream { } } + if constexpr (Channel == DecayChannel::XicToXiPiPi) { + if (p1.trackId() == p2.prong1Id() || p1.trackId() == p2.prong2Id() || + p1.trackId() == p2.cascBachelorTrackId() || + p1.trackId() == p2.cascPosTrackId() || p1.trackId() == p2.cascNegTrackId()) { + continue; + } + if (pairQASetting.useCPR.value) { + if (pairCloseRejectionSE3Prong.isClosePair(p1, p2, parts, col.magField())) { + continue; + } + } + } + if constexpr (Channel == DecayChannel::DstarToD0Pi) { if (p1.trackId() == p2.prong0Id() || p1.trackId() == p2.prong1Id() || p1.trackId() == p2.prong2Id()) { continue; @@ -533,7 +569,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { float deltaInvMassPair = getCharmHadronTrackMass(p2, p1, trackSel.pdgCodeTrack1.value) - invMass; - // proton track charge + // associated track charge float chargeTrack = 0.; if ((p1.cut() & CutBitChargePositive) == CutBitChargePositive) { chargeTrack = PositiveCharge; @@ -583,7 +619,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { } template - void doMixedEvent(CollisionType const& cols, PartitionType1& charms, PartitionType2& trks, TableTracks const& parts, BinningType policy) + void doMixedEvent(CollisionType const& cols, PartitionType1& charms, PartitionType2& trks, TableTracks const& parts, BinningType const& policy) { processType = 2; // for mixed event // Mixed events that contain the pair of interest @@ -632,6 +668,14 @@ struct HfTaskCharmHadronsTrackFemtoDream { } } + if constexpr (Channel == DecayChannel::XicToXiPiPi) { + if (pairQASetting.useCPR.value) { + if (pairCloseRejectionME3Prong.isClosePair(p1, p2, parts, collision1.magField())) { + continue; + } + } + } + if constexpr (Channel == DecayChannel::DstarToD0Pi) { if (pairQASetting.useCPR.value) { @@ -662,7 +706,7 @@ struct HfTaskCharmHadronsTrackFemtoDream { float deltaInvMassPair = getCharmHadronTrackMass(p2, p1, trackSel.pdgCodeTrack1.value) - invMass; - // proton track charge + // associated track charge float chargeTrack = 0.; if ((p1.cut() & CutBitChargePositive) == CutBitChargePositive) { chargeTrack = PositiveCharge; @@ -724,6 +768,13 @@ struct HfTaskCharmHadronsTrackFemtoDream { timeStamp = part.timeStamp(); + int charmHadMc = 0; + int originType = 0; + if constexpr (IsMc) { + charmHadMc = part.flagMc(); + originType = part.originMcRec(); + } + if constexpr (Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::LcToPKPi) { rowFemtoResultCharm3Prong( @@ -739,7 +790,26 @@ struct HfTaskCharmHadronsTrackFemtoDream { part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD()); + part.bdtFD(), + charmHadMc, + originType); + } else if constexpr (Channel == DecayChannel::XicToXiPiPi) { + rowFemtoResultCharm3Prong( + col.globalIndex(), + timeStamp, + invMass, + part.pt(), + part.eta(), + part.phi(), + part.cascId(), + part.prong1Id(), + part.prong2Id(), + part.charge(), + part.bdtBkg(), + part.bdtPrompt(), + part.bdtFD(), + charmHadMc, + originType); } else if constexpr (Channel == DecayChannel::D0ToPiK) { rowFemtoResultCharm2Prong( col.globalIndex(), @@ -753,7 +823,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD()); + part.bdtFD(), + charmHadMc, + originType); } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { float invMassD0 = getCharmHadronMass(part, true); rowFemtoResultCharmDstar( @@ -770,7 +842,9 @@ struct HfTaskCharmHadronsTrackFemtoDream { part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD()); + part.bdtFD(), + charmHadMc, + originType); } } // ---- Fill Track Table ---- @@ -837,8 +911,10 @@ struct HfTaskCharmHadronsTrackFemtoDream { void processDataLcTrk(FilteredCollisions const& cols, FilteredFDParticles const& parts, - FilteredCharmCand3Prongs const&) + FilteredCharmCand3Prongs const& candidates) { + rowFemtoResultCharm3Prong.reserve(2 * candidates.size() + 1); + rowFemtoResultTrk.reserve(parts.size() + 1); for (const auto& col : cols) { eventHisto.fillQA(col); auto* partitionTrk1Selected = &partitionTrk1; @@ -849,23 +925,26 @@ struct HfTaskCharmHadronsTrackFemtoDream { auto sliceCharmHad = partitionCharmHadron3Prong->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); if (fillTableWithCharm.value && sliceCharmHad.size() == 0) { continue; - } else { - fillTables(col, sliceTrk1, sliceCharmHad); } + fillTables(col, sliceTrk1, sliceCharmHad); if (sliceCharmHad.size() > 0 && sliceTrk1.size() > 0) { doSameEvent(sliceCharmHad, sliceTrk1, parts, col); } } if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionTrk1Ka; + } switch (mixSetting.mixingBinPolicy) { case femtodreamcollision::kMult: - doMixedEvent(cols, partitionCharmHadron3Prong, partitionTrk1, parts, colBinningMult); + doMixedEvent(cols, partitionCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMult); break; case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionCharmHadron3Prong, partitionTrk1, parts, colBinningMultPercentile); + doMixedEvent(cols, partitionCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultPercentile); break; case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionCharmHadron3Prong, partitionTrk1, parts, colBinningMultMultPercentile); + doMixedEvent(cols, partitionCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultMultPercentile); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -876,8 +955,10 @@ struct HfTaskCharmHadronsTrackFemtoDream { void processDataDplusTrk(FilteredCollisions const& cols, FilteredFDParticles const& parts, - FilteredCharmCand3Prongs const&) + FilteredCharmCand3Prongs const& candidates) { + rowFemtoResultCharm3Prong.reserve(candidates.size() + 1); + rowFemtoResultTrk.reserve(parts.size() + 1); for (const auto& col : cols) { eventHisto.fillQA(col); auto* partitionTrk1Selected = &partitionTrk1; @@ -889,23 +970,26 @@ struct HfTaskCharmHadronsTrackFemtoDream { if (fillTableWithCharm.value && sliceCharmHad.size() == 0) { continue; - } else { - fillTables(col, sliceTrk1, sliceCharmHad); } + fillTables(col, sliceTrk1, sliceCharmHad); if (sliceCharmHad.size() > 0 && sliceTrk1.size() > 0) { doSameEvent(sliceCharmHad, sliceTrk1, parts, col); } } if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionTrk1Ka; + } switch (mixSetting.mixingBinPolicy) { case femtodreamcollision::kMult: - doMixedEvent(cols, partitionCharmHadron3Prong, partitionTrk1, parts, colBinningMult); + doMixedEvent(cols, partitionCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMult); break; case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionCharmHadron3Prong, partitionTrk1, parts, colBinningMultPercentile); + doMixedEvent(cols, partitionCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultPercentile); break; case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionCharmHadron3Prong, partitionTrk1, parts, colBinningMultMultPercentile); + doMixedEvent(cols, partitionCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultMultPercentile); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -916,8 +1000,10 @@ struct HfTaskCharmHadronsTrackFemtoDream { void processDataD0Trk(FilteredCollisions const& cols, FilteredFDParticles const& parts, - FilteredCharmCand2Prongs const&) + FilteredCharmCand2Prongs const& candidates) { + rowFemtoResultCharm2Prong.reserve(candidates.size() * 2 + 1); + rowFemtoResultTrk.reserve(parts.size() + 1); for (const auto& col : cols) { eventHisto.fillQA(col); auto* partitionTrk1Selected = &partitionTrk1; @@ -928,23 +1014,26 @@ struct HfTaskCharmHadronsTrackFemtoDream { auto sliceCharmHad = partitionCharmHadron2Prong->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); if (fillTableWithCharm.value && sliceCharmHad.size() == 0) { continue; - } else { - fillTables(col, sliceTrk1, sliceCharmHad); } + fillTables(col, sliceTrk1, sliceCharmHad); if (sliceCharmHad.size() > 0 && sliceTrk1.size() > 0) { doSameEvent(sliceCharmHad, sliceTrk1, parts, col); } } if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionTrk1Ka; + } switch (mixSetting.mixingBinPolicy) { case femtodreamcollision::kMult: - doMixedEvent(cols, partitionCharmHadron2Prong, partitionTrk1, parts, colBinningMult); + doMixedEvent(cols, partitionCharmHadron2Prong, *partitionTrk1Selected, parts, colBinningMult); break; case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionCharmHadron2Prong, partitionTrk1, parts, colBinningMultPercentile); + doMixedEvent(cols, partitionCharmHadron2Prong, *partitionTrk1Selected, parts, colBinningMultPercentile); break; case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionCharmHadron2Prong, partitionTrk1, parts, colBinningMultMultPercentile); + doMixedEvent(cols, partitionCharmHadron2Prong, *partitionTrk1Selected, parts, colBinningMultMultPercentile); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -955,8 +1044,10 @@ struct HfTaskCharmHadronsTrackFemtoDream { void processDataDstarTrk(FilteredCollisions const& cols, FilteredFDParticles const& parts, - FilteredCharmCandDstars const&) + FilteredCharmCandDstars const& candidates) { + rowFemtoResultCharmDstar.reserve(candidates.size() + 1); + rowFemtoResultTrk.reserve(parts.size() + 1); for (const auto& col : cols) { eventHisto.fillQA(col); auto* partitionTrk1Selected = &partitionTrk1; @@ -967,23 +1058,26 @@ struct HfTaskCharmHadronsTrackFemtoDream { auto sliceCharmHad = partitionCharmHadronDstar->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); if (fillTableWithCharm.value && sliceCharmHad.size() == 0) { continue; - } else { - fillTables(col, sliceTrk1, sliceCharmHad); } + fillTables(col, sliceTrk1, sliceCharmHad); if (sliceCharmHad.size() > 0 && sliceTrk1.size() > 0) { doSameEvent(sliceCharmHad, sliceTrk1, parts, col); } } if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionTrk1Ka; + } switch (mixSetting.mixingBinPolicy) { case femtodreamcollision::kMult: - doMixedEvent(cols, partitionCharmHadronDstar, partitionTrk1, parts, colBinningMult); + doMixedEvent(cols, partitionCharmHadronDstar, *partitionTrk1Selected, parts, colBinningMult); break; case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionCharmHadronDstar, partitionTrk1, parts, colBinningMultPercentile); + doMixedEvent(cols, partitionCharmHadronDstar, *partitionTrk1Selected, parts, colBinningMultPercentile); break; case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionCharmHadronDstar, partitionTrk1, parts, colBinningMultMultPercentile); + doMixedEvent(cols, partitionCharmHadronDstar, *partitionTrk1Selected, parts, colBinningMultMultPercentile); break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -992,6 +1086,48 @@ struct HfTaskCharmHadronsTrackFemtoDream { } PROCESS_SWITCH(HfTaskCharmHadronsTrackFemtoDream, processDataDstarTrk, "Enable processing DstarToD0Pi and Tracks correlation", false); + void processDataXicTrk(FilteredCollisions const& cols, + FilteredFDParticles const& parts, + FilteredCharmCand3ProngsXic const&) + { + for (const auto& col : cols) { + eventHisto.fillQA(col); + auto* partitionTrk1Selected = &partitionTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionTrk1Ka; + } + auto sliceTrk1 = partitionTrk1Selected->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + auto sliceCharmHad = partitionCharmHadron3ProngXic->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + if (fillTableWithCharm.value && sliceCharmHad.size() == 0) { + continue; + } + fillTables(col, sliceTrk1, sliceCharmHad); + if (sliceCharmHad.size() > 0 && sliceTrk1.size() > 0) { + doSameEvent(sliceCharmHad, sliceTrk1, parts, col); + } + } + if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionTrk1Ka; + } + switch (mixSetting.mixingBinPolicy) { + case femtodreamcollision::kMult: + doMixedEvent(cols, partitionCharmHadron3ProngXic, *partitionTrk1Selected, parts, colBinningMult); + break; + case femtodreamcollision::kMultPercentile: + doMixedEvent(cols, partitionCharmHadron3ProngXic, *partitionTrk1Selected, parts, colBinningMultPercentile); + break; + case femtodreamcollision::kMultMultPercentile: + doMixedEvent(cols, partitionCharmHadron3ProngXic, *partitionTrk1Selected, parts, colBinningMultMultPercentile); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + } + PROCESS_SWITCH(HfTaskCharmHadronsTrackFemtoDream, processDataXicTrk, "Enable processing XicToXiPiPi and Tracks correlation", false); + void processMcLcTrk(FilteredMcColisions const& cols, FilteredFDMcParts const& parts, o2::aod::FDMCParticles const&, @@ -1009,20 +1145,30 @@ struct HfTaskCharmHadronsTrackFemtoDream { if ((col.bitmaskTrackOne() & bitMask) != bitMask || (col.bitmaskTrackTwo() & bitMask) != bitMask) { continue; } + if (fillTableWithCharm.value && sliceMcCharmHad.size() == 0) { + continue; + } + fillTables(col, sliceMcTrk1, sliceMcCharmHad); doSameEvent(sliceMcCharmHad, sliceMcTrk1, parts, col); } - switch (mixSetting.mixingBinPolicy) { - case femtodreamcollision::kMult: - doMixedEvent(cols, partitionMcCharmHadron3Prong, partitionMcTrk1, parts, colBinningMult); - break; - case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionMcCharmHadron3Prong, partitionMcTrk1, parts, colBinningMultPercentile); - break; - case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionMcCharmHadron3Prong, partitionMcTrk1, parts, colBinningMultMultPercentile); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionMcTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionMcTrk1Ka; + } + switch (mixSetting.mixingBinPolicy) { + case femtodreamcollision::kMult: + doMixedEvent(cols, partitionMcCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMult); + break; + case femtodreamcollision::kMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultPercentile); + break; + case femtodreamcollision::kMultMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultMultPercentile); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } PROCESS_SWITCH(HfTaskCharmHadronsTrackFemtoDream, processMcLcTrk, "Enable processing LcToPKPi and Tracks correlation for Monte Carlo", false); @@ -1044,20 +1190,30 @@ struct HfTaskCharmHadronsTrackFemtoDream { if ((col.bitmaskTrackOne() & bitMask) != bitMask || (col.bitmaskTrackTwo() & bitMask) != bitMask) { continue; } + if (fillTableWithCharm.value && sliceMcCharmHad.size() == 0) { + continue; + } + fillTables(col, sliceMcTrk1, sliceMcCharmHad); doSameEvent(sliceMcCharmHad, sliceMcTrk1, parts, col); } - switch (mixSetting.mixingBinPolicy) { - case femtodreamcollision::kMult: - doMixedEvent(cols, partitionMcCharmHadron3Prong, partitionMcTrk1, parts, colBinningMult); - break; - case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionMcCharmHadron3Prong, partitionMcTrk1, parts, colBinningMultPercentile); - break; - case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionMcCharmHadron3Prong, partitionMcTrk1, parts, colBinningMultMultPercentile); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionMcTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionMcTrk1Ka; + } + switch (mixSetting.mixingBinPolicy) { + case femtodreamcollision::kMult: + doMixedEvent(cols, partitionMcCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMult); + break; + case femtodreamcollision::kMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultPercentile); + break; + case femtodreamcollision::kMultMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron3Prong, *partitionTrk1Selected, parts, colBinningMultMultPercentile); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } PROCESS_SWITCH(HfTaskCharmHadronsTrackFemtoDream, processMcDplusTrk, "Enable processing DplusToPiKPi and Tracks correlation for Monte Carlo", false); @@ -1079,20 +1235,30 @@ struct HfTaskCharmHadronsTrackFemtoDream { if ((col.bitmaskTrackOne() & bitMask) != bitMask || (col.bitmaskTrackTwo() & bitMask) != bitMask) { continue; } + if (fillTableWithCharm.value && sliceMcCharmHad.size() == 0) { + continue; + } + fillTables(col, sliceMcTrk1, sliceMcCharmHad); doSameEvent(sliceMcCharmHad, sliceMcTrk1, parts, col); } - switch (mixSetting.mixingBinPolicy) { - case femtodreamcollision::kMult: - doMixedEvent(cols, partitionMcCharmHadron2Prong, partitionMcTrk1, parts, colBinningMult); - break; - case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionMcCharmHadron2Prong, partitionMcTrk1, parts, colBinningMultPercentile); - break; - case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionMcCharmHadron2Prong, partitionMcTrk1, parts, colBinningMultMultPercentile); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionMcTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionMcTrk1Ka; + } + switch (mixSetting.mixingBinPolicy) { + case femtodreamcollision::kMult: + doMixedEvent(cols, partitionMcCharmHadron2Prong, *partitionTrk1Selected, parts, colBinningMult); + break; + case femtodreamcollision::kMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron2Prong, *partitionTrk1Selected, parts, colBinningMultPercentile); + break; + case femtodreamcollision::kMultMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron2Prong, *partitionTrk1Selected, parts, colBinningMultMultPercentile); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } PROCESS_SWITCH(HfTaskCharmHadronsTrackFemtoDream, processMcD0Trk, "Enable processing D0ToPiK and Tracks correlation for Monte Carlo", false); @@ -1114,23 +1280,78 @@ struct HfTaskCharmHadronsTrackFemtoDream { if ((col.bitmaskTrackOne() & bitMask) != bitMask || (col.bitmaskTrackTwo() & bitMask) != bitMask) { continue; } + if (fillTableWithCharm.value && sliceMcCharmHad.size() == 0) { + continue; + } + fillTables(col, sliceMcTrk1, sliceMcCharmHad); doSameEvent(sliceMcCharmHad, sliceMcTrk1, parts, col); } - switch (mixSetting.mixingBinPolicy) { - case femtodreamcollision::kMult: - doMixedEvent(cols, partitionMcCharmHadronDstar, partitionMcTrk1, parts, colBinningMult); - break; - case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, partitionMcCharmHadronDstar, partitionMcTrk1, parts, colBinningMultPercentile); - break; - case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, partitionMcCharmHadronDstar, partitionMcTrk1, parts, colBinningMultMultPercentile); - break; - default: - LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionMcTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionMcTrk1Ka; + } + switch (mixSetting.mixingBinPolicy) { + case femtodreamcollision::kMult: + doMixedEvent(cols, partitionMcCharmHadronDstar, *partitionTrk1Selected, parts, colBinningMult); + break; + case femtodreamcollision::kMultPercentile: + doMixedEvent(cols, partitionMcCharmHadronDstar, *partitionTrk1Selected, parts, colBinningMultPercentile); + break; + case femtodreamcollision::kMultMultPercentile: + doMixedEvent(cols, partitionMcCharmHadronDstar, *partitionTrk1Selected, parts, colBinningMultMultPercentile); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } } } PROCESS_SWITCH(HfTaskCharmHadronsTrackFemtoDream, processMcDstarTrk, "Enable processing DstarToD0Pi and Tracks correlation for Monte Carlo", false); + + void processMcXicTrk(FilteredMcColisions const& cols, + FilteredFDMcParts const& parts, + o2::aod::FDMCParticles const&, + o2::aod::FDExtMCParticles const&, + FilteredCharmMcCand3ProngsXic const&) + { + for (const auto& col : cols) { + eventHisto.fillQA(col); + auto* partitionTrk1Selected = &partitionMcTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionMcTrk1Ka; + } + auto sliceMcTrk1 = partitionTrk1Selected->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + auto sliceMcCharmHad = partitionMcCharmHadron3ProngXic->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + if ((col.bitmaskTrackOne() & bitMask) != bitMask || (col.bitmaskTrackTwo() & bitMask) != bitMask) { + continue; + } + if (fillTableWithCharm.value && sliceMcCharmHad.size() == 0) { + continue; + } + fillTables(col, sliceMcTrk1, sliceMcCharmHad); + doSameEvent(sliceMcCharmHad, sliceMcTrk1, parts, col); + } + if (mixSetting.doMixEvent) { + auto* partitionTrk1Selected = &partitionMcTrk1; + if (trackSel.pdgCodeTrack1.value == kKPlus) { + partitionTrk1Selected = &partitionMcTrk1Ka; + } + switch (mixSetting.mixingBinPolicy) { + case femtodreamcollision::kMult: + doMixedEvent(cols, partitionMcCharmHadron3ProngXic, *partitionTrk1Selected, parts, colBinningMult); + break; + case femtodreamcollision::kMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron3ProngXic, *partitionTrk1Selected, parts, colBinningMultPercentile); + break; + case femtodreamcollision::kMultMultPercentile: + doMixedEvent(cols, partitionMcCharmHadron3ProngXic, *partitionTrk1Selected, parts, colBinningMultMultPercentile); + break; + default: + LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; + } + } + } + PROCESS_SWITCH(HfTaskCharmHadronsTrackFemtoDream, processMcXicTrk, "Enable processing XicToXiPiPi and Tracks correlation for Monte Carlo", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx index 57f05fbea1a..29538febfce 100644 --- a/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx +++ b/PWGHF/HFC/Tasks/taskCharmHadronsV0FemtoDream.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file taskCharmHadronsTrackFemtoDream.cxx +/// \file taskCharmHadronsV0FemtoDream.cxx /// \brief Tasks that reads the V0 and CharmHadrons tables used for the pairing and builds pairs /// \author Biao Zhang, Heidelberg University, biao.zhang@cern.ch @@ -174,11 +174,11 @@ struct HfTaskCharmHadronsV0FemtoDream { using FilteredCollisions = soa::Filtered>; using FilteredCollision = FilteredCollisions::iterator; - using FilteredMcColisions = soa::Filtered>; - using FilteredMcColision = FilteredMcColisions::iterator; + using FilteredMcCollisions = soa::Filtered>; + using FilteredMcColision = FilteredMcCollisions::iterator; - using FilteredFDMcParts = soa::Filtered>; - using FilteredFDMcPart = FilteredFDMcParts::iterator; + using FDMcV0Particles = soa::Join; + using FDMcV0Particle = FDMcV0Particles::iterator; using FDV0Particles = soa::Join; using FDV0Particle = FDV0Particles::iterator; @@ -189,12 +189,13 @@ struct HfTaskCharmHadronsV0FemtoDream { Filter hfMcSelFilter = (nabs(aod::fdhf::flagMc) == charmSel.charmHadMcSel); Preslice perCol = aod::femtodreamparticle::fdCollisionId; + Preslice perColMc = aod::femtodreamparticle::fdCollisionId; Preslice perHf3ProngByCol = aod::femtodreamparticle::fdCollisionId; Preslice perHf2ProngByCol = aod::femtodreamparticle::fdCollisionId; Preslice perHfDstarByCol = aod::femtodreamparticle::fdCollisionId; - /// Partition for particle Lambda - Partition partitionLambda = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0)) && + /// Partitions for particle Lambda + Partition partitionLambda = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -205,8 +206,19 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); - /// Partition for particle K0Short - Partition partitionK0Short = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kV0K0Short)) && + Partition partitionMcLambda = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0)) && + ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && + (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && + (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && + (aod::femtodreamparticle::eta > v0Sel.etaV0Min) && + (aod::femtodreamparticle::eta < v0Sel.etaV0Max) && + (aod::femtodreamparticle::mLambda > v0Sel.invMassV0Min) && + (aod::femtodreamparticle::mLambda < v0Sel.invMassV0Max) && + (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && + (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); + + /// Partitions for particle K0Short + Partition partitionK0Short = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0K0Short)) && ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && @@ -217,6 +229,17 @@ struct HfTaskCharmHadronsV0FemtoDream { (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); + Partition partitionMcK0Short = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kV0K0Short)) && + ((aod::femtodreamparticle::cut & v0Sel.cutBit) == v0Sel.cutBit) && + (aod::femtodreamparticle::pt > v0Sel.ptV0Min) && + (aod::femtodreamparticle::pt < v0Sel.ptV0Max) && + (aod::femtodreamparticle::eta > v0Sel.etaV0Min) && + (aod::femtodreamparticle::eta < v0Sel.etaV0Max) && + (aod::femtodreamparticle::mLambda > v0Sel.invMassV0Min) && + (aod::femtodreamparticle::mLambda < v0Sel.invMassV0Max) && + (aod::femtodreamparticle::mAntiLambda > v0Sel.invMassAntiV0Min) && + (aod::femtodreamparticle::mAntiLambda < v0Sel.invMassAntiV0Max); + /// Partition for particle 2 Partition partitionCharmHadron3Prong = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; Partition partitionCharmHadron2Prong = aod::fdhf::bdtBkg < charmSel.charmHadBkgBDTmax && aod::fdhf::bdtFD < charmSel.charmHadFdBDTmax && aod::fdhf::bdtFD > charmSel.charmHadFdBDTmin&& aod::fdhf::bdtPrompt charmSel.charmHadPromptBDTmin; @@ -256,9 +279,9 @@ struct HfTaskCharmHadronsV0FemtoDream { ConfigurableAxis mixingBinVztx{"mixingBinVztx", {VARIABLE_WIDTH, -10.0f, -4.f, 0.f, 4.f, 10.f}, "Mixing bins - z-vertex"}; } AxisBinning; - ColumnBinningPolicy colBinningMult{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}, true}; - ColumnBinningPolicy colBinningMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}, true}; - ColumnBinningPolicy colBinningMultMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}, true}; + ColumnBinningPolicy colBinningMult{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}}; + ColumnBinningPolicy colBinningMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}}; + ColumnBinningPolicy colBinningMultMultPercentile{{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}}; FemtoDreamContainer sameEventCont; FemtoDreamContainer mixedEventCont; @@ -298,18 +321,18 @@ struct HfTaskCharmHadronsV0FemtoDream { void init(InitContext& /*context*/) { - std::array processes = {doprocessDataLcV0, doprocessDataDplusV0, doprocessDataD0V0, doprocessDataDstarV0}; + std::array processes = {doprocessDataLcV0, doprocessDataDplusV0, doprocessDataD0V0, doprocessDataDstarV0, doprocessMcDplusV0}; if (std::accumulate(processes.begin(), processes.end(), 0) != 1) { LOGP(fatal, "One and only one process function must be enabled at a time."); } - bool process3Prong = doprocessDataLcV0 || doprocessDataDplusV0; + bool process3Prong = doprocessDataLcV0 || doprocessDataDplusV0 || doprocessMcDplusV0; bool process2Prong = doprocessDataD0V0; bool processDstar = doprocessDataDstarV0; // setup columnpolicy for binning - colBinningMult = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}, true}; - colBinningMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}, true}; - colBinningMultMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}, true}; + colBinningMult = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult}}; + colBinningMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMultPercentile}}; + colBinningMultMultPercentile = {{AxisBinning.mixingBinVztx, AxisBinning.mixingBinMult, AxisBinning.mixingBinMultPercentile}}; eventHisto.init(®istry); v0HistoPartOne.init(®istry, AxisBinning.binmultTempFit, AxisBinning.dummy, AxisBinning.binpTV0, AxisBinning.binEta, AxisBinning.binPhi, AxisBinning.binTempFitVarV0, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.binInvMassV0, AxisBinning.binInvMassV0, isMc, v0Sel.pdgCodeV0, true); v0HistoPartOneSelected.init(®istry, AxisBinning.binmultTempFit, AxisBinning.dummy, AxisBinning.binpTV0, AxisBinning.binEta, AxisBinning.binPhi, AxisBinning.binTempFitVarV0, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.dummy, AxisBinning.binInvMassV0, AxisBinning.binInvMassV0, isMc, v0Sel.pdgCodeV0, true); @@ -381,10 +404,9 @@ struct HfTaskCharmHadronsV0FemtoDream { if (cand.candidateSelFlag() == 1) { invMass = cand.m(std::array{MassPiPlus, MassKPlus}); return invMass; - } else { - invMass = cand.m(std::array{MassKPlus, MassPiPlus}); - return invMass; } + invMass = cand.m(std::array{MassKPlus, MassPiPlus}); + return invMass; } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { // D* → D0π (PDG: 413) float mDstar = 0.f; float mD0 = 0.f; @@ -397,9 +419,8 @@ struct HfTaskCharmHadronsV0FemtoDream { } if (ReturnDaughMass) { return mD0; - } else { - return mDstar - mD0; } + return mDstar - mD0; } // Add more channels as needed return 0.f; @@ -417,11 +438,7 @@ struct HfTaskCharmHadronsV0FemtoDream { if (p1.childrenIds()[0] == p2.prong0Id() || p1.childrenIds()[0] == p2.prong1Id() || p1.childrenIds()[1] == p2.prong0Id() || p1.childrenIds()[1] == p2.prong1Id()) { continue; } - } else if constexpr (Channel == DecayChannel::LcToPKPi || Channel == DecayChannel::DplusToPiKPi) { - if (p1.childrenIds()[0] == p2.prong0Id() || p1.childrenIds()[0] == p2.prong1Id() || p1.childrenIds()[0] == p2.prong2Id() || p1.childrenIds()[1] == p2.prong0Id() || p1.childrenIds()[1] == p2.prong1Id() || p1.childrenIds()[1] == p2.prong2Id()) { - continue; - } - } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { + } else if constexpr (Channel == DecayChannel::LcToPKPi || Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::DstarToD0Pi) { if (p1.childrenIds()[0] == p2.prong0Id() || p1.childrenIds()[0] == p2.prong1Id() || p1.childrenIds()[0] == p2.prong2Id() || p1.childrenIds()[1] == p2.prong0Id() || p1.childrenIds()[1] == p2.prong1Id() || p1.childrenIds()[1] == p2.prong2Id()) { continue; } @@ -506,7 +523,7 @@ struct HfTaskCharmHadronsV0FemtoDream { } template - void doMixedEvent(CollisionType const& cols, PartitionType1& charms, PartitionType2& v0s, FDParticles const& /*femtoParts*/, BinningType policy) + void doMixedEvent(CollisionType const& cols, PartitionType1& charms, PartitionType2& v0s, FDParticles const& /*femtoParts*/, BinningType const& policy) { processType = 2; // for mixed event // Mixed events that contain the pair of interest @@ -610,6 +627,13 @@ struct HfTaskCharmHadronsV0FemtoDream { registryCharmHadronQa.fill(HIST("CharmHadronQA/hPtVsMass"), part.pt(), invMass); timeStamp = part.timeStamp(); + int charmHadMc = 0; + int originType = 0; + if constexpr (IsMc) { + charmHadMc = part.flagMc(); + originType = part.originMcRec(); + } + if constexpr (Channel == DecayChannel::DplusToPiKPi || Channel == DecayChannel::LcToPKPi) { rowFemtoResultCharm3Prong( @@ -625,7 +649,9 @@ struct HfTaskCharmHadronsV0FemtoDream { part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD()); + part.bdtFD(), + charmHadMc, + originType); } else if constexpr (Channel == DecayChannel::D0ToPiK) { rowFemtoResultCharm2Prong( col.globalIndex(), @@ -639,7 +665,9 @@ struct HfTaskCharmHadronsV0FemtoDream { part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD()); + part.bdtFD(), + charmHadMc, + originType); } else if constexpr (Channel == DecayChannel::DstarToD0Pi) { float invMassD0 = getCharmHadronMass(part, true); rowFemtoResultCharmDstar( @@ -656,7 +684,9 @@ struct HfTaskCharmHadronsV0FemtoDream { part.charge(), part.bdtBkg(), part.bdtPrompt(), - part.bdtFD()); + part.bdtFD(), + charmHadMc, + originType); } } @@ -721,9 +751,9 @@ struct HfTaskCharmHadronsV0FemtoDream { } } - template - void runMixing(FilteredCollisions const& cols, - FDV0Particles const& parts, + template + void runMixing(Cols const& cols, + V0Part const& parts, CharmPart& charmPart) { if (!mixSetting.doMixEvent) { @@ -732,13 +762,25 @@ struct HfTaskCharmHadronsV0FemtoDream { auto run = [&](auto& v0Part) { switch (mixSetting.mixingBinPolicy) { case femtodreamcollision::kMult: - doMixedEvent(cols, charmPart, v0Part, parts, colBinningMult); + if constexpr (IsMc) { + doMixedEvent(cols, charmPart, v0Part, parts, colBinningMult); + } else { + doMixedEvent(cols, charmPart, v0Part, parts, colBinningMult); + } break; case femtodreamcollision::kMultPercentile: - doMixedEvent(cols, charmPart, v0Part, parts, colBinningMultPercentile); + if constexpr (IsMc) { + doMixedEvent(cols, charmPart, v0Part, parts, colBinningMultPercentile); + } else { + doMixedEvent(cols, charmPart, v0Part, parts, colBinningMultPercentile); + } break; case femtodreamcollision::kMultMultPercentile: - doMixedEvent(cols, charmPart, v0Part, parts, colBinningMultMultPercentile); + if constexpr (IsMc) { + doMixedEvent(cols, charmPart, v0Part, parts, colBinningMultMultPercentile); + } else { + doMixedEvent(cols, charmPart, v0Part, parts, colBinningMultMultPercentile); + } break; default: LOG(fatal) << "Invalid binning policiy specifed. Breaking..."; @@ -746,9 +788,17 @@ struct HfTaskCharmHadronsV0FemtoDream { }; if (v0Sel.pdgCodeV0 == kLambda0) { - run(partitionLambda); + if constexpr (IsMc) { + run(partitionMcLambda); + } else { + run(partitionLambda); + } } else if (v0Sel.pdgCodeV0 == kK0Short) { - run(partitionK0Short); + if constexpr (IsMc) { + run(partitionMcK0Short); + } else { + run(partitionK0Short); + } } else { LOG(fatal) << "Unsupported V0 PDG: " << v0Sel.pdgCodeV0 << " (allowed: 3122, 310) for mixed-events"; } @@ -756,8 +806,11 @@ struct HfTaskCharmHadronsV0FemtoDream { void processDataLcV0(FilteredCollisions const& cols, FDV0Particles const& parts, - FilteredCharmCand3Prongs const&) + FilteredCharmCand3Prongs const& candidates) { + + rowFemtoResultCharm3Prong.reserve(candidates.size() + 1); + for (const auto& col : cols) { eventHisto.fillQA(col); auto sliceCharmHad = partitionCharmHadron3Prong->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); @@ -782,14 +835,16 @@ struct HfTaskCharmHadronsV0FemtoDream { LOG(fatal) << "Unsupported V0 PDG: " << v0Sel.pdgCodeV0 << " (allowed: 3122, 310) for same events"; } } - runMixing(cols, parts, partitionCharmHadron3Prong); + runMixing(cols, parts, partitionCharmHadron3Prong); } PROCESS_SWITCH(HfTaskCharmHadronsV0FemtoDream, processDataLcV0, "Enable processing LcToPKPi and V0 correlation", false); void processDataDplusV0(FilteredCollisions const& cols, FDV0Particles const& parts, - FilteredCharmCand3Prongs const&) + FilteredCharmCand3Prongs const& candidates) { + rowFemtoResultCharm3Prong.reserve(candidates.size() + 1); + for (const auto& col : cols) { eventHisto.fillQA(col); auto sliceCharmHad = partitionCharmHadron3Prong->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); @@ -814,14 +869,16 @@ struct HfTaskCharmHadronsV0FemtoDream { LOG(fatal) << "Unsupported V0 PDG: " << v0Sel.pdgCodeV0 << " (allowed: 3122, 310)"; } } - runMixing(cols, parts, partitionCharmHadron3Prong); + runMixing(cols, parts, partitionCharmHadron3Prong); } PROCESS_SWITCH(HfTaskCharmHadronsV0FemtoDream, processDataDplusV0, "Enable processing DplusToPiKPi and V0 correlation", false); void processDataD0V0(FilteredCollisions const& cols, FDV0Particles const& parts, - FilteredCharmCand2Prongs const&) + FilteredCharmCand2Prongs const& candidates) { + rowFemtoResultCharm2Prong.reserve(candidates.size() + 1); + for (const auto& col : cols) { eventHisto.fillQA(col); auto sliceCharmHad = partitionCharmHadron2Prong->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); @@ -846,14 +903,17 @@ struct HfTaskCharmHadronsV0FemtoDream { LOG(fatal) << "Unsupported V0 PDG: " << v0Sel.pdgCodeV0 << " (allowed: 3122, 310)"; } } - runMixing(cols, parts, partitionCharmHadron2Prong); + runMixing(cols, parts, partitionCharmHadron2Prong); } PROCESS_SWITCH(HfTaskCharmHadronsV0FemtoDream, processDataD0V0, "Enable processing D0ToPiK and V0 correlation", false); void processDataDstarV0(FilteredCollisions const& cols, FDV0Particles const& parts, - FilteredCharmCandDstars const&) + FilteredCharmCandDstars const& candidates) { + + rowFemtoResultCharmDstar.reserve(candidates.size() + 1); + for (const auto& col : cols) { eventHisto.fillQA(col); auto sliceCharmHad = partitionCharmHadronDstar->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); @@ -878,9 +938,43 @@ struct HfTaskCharmHadronsV0FemtoDream { LOG(fatal) << "Unsupported V0 PDG: " << v0Sel.pdgCodeV0 << " (allowed: 3122, 310)"; } } - runMixing(cols, parts, partitionCharmHadronDstar); + runMixing(cols, parts, partitionCharmHadronDstar); } PROCESS_SWITCH(HfTaskCharmHadronsV0FemtoDream, processDataDstarV0, "Enable processing DstarToD0Pi and V0 correlation", false); + + void processMcDplusV0(FilteredMcCollisions const& cols, + FDMcV0Particles const& parts, + o2::aod::FDMCParticles const&, + o2::aod::FDExtMCParticles const&, + FilteredCharmMcCand3Prongs const&) + { + for (const auto& col : cols) { + eventHisto.fillQA(col); + auto sliceMcCharmHad = partitionMcCharmHadron3Prong->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + if (fillTableWithCharm.value && sliceMcCharmHad.size() == 0) { + continue; + } + if (v0Sel.pdgCodeV0 == kLambda0) { + auto sliceMcV0 = partitionMcLambda->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + fillTables(col, sliceMcV0, sliceMcCharmHad, parts); + if (sliceMcCharmHad.size() > 0 && sliceMcV0.size() > 0) { + doSameEvent(sliceMcCharmHad, sliceMcV0, parts, col); + } + + } else if (v0Sel.pdgCodeV0 == kK0Short) { + auto sliceMcV0 = partitionMcK0Short->sliceByCached(aod::femtodreamparticle::fdCollisionId, col.globalIndex(), cache); + fillTables(col, sliceMcV0, sliceMcCharmHad, parts); + + if (sliceMcCharmHad.size() > 0 && sliceMcV0.size() > 0) { + doSameEvent(sliceMcCharmHad, sliceMcV0, parts, col); + } + } else { + LOG(fatal) << "Unsupported V0 PDG: " << v0Sel.pdgCodeV0 << " (allowed: 3122, 310)"; + } + } + runMixing(cols, parts, partitionMcCharmHadron3Prong); + } + PROCESS_SWITCH(HfTaskCharmHadronsV0FemtoDream, processMcDplusV0, "Enable processing DplusToPiKPi and V0 correlation for Monte Carlo", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx index 2d51bc7ece9..873c0bc052c 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationD0Hadrons.cxx @@ -16,6 +16,7 @@ /// \author Samrangy Sadhu , INFN Bari /// \author Swapnesh Santosh Khade , IIT Indore +#include "PWGHF/Core/CentralityEstimation.h" #include "PWGHF/Core/DecayChannels.h" #include "PWGHF/Core/HfHelper.h" #include "PWGHF/Core/SelectorCuts.h" @@ -28,6 +29,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -46,6 +48,7 @@ #include #include +#include #include @@ -57,38 +60,20 @@ using namespace o2::constants::physics; using namespace o2::constants::math; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::hf_centrality; using namespace o2::aod::hf_correlation_d0_hadron; using namespace o2::analysis::hf_correlations; -// definition of vectors for standard ptbin and invariant mass configurables -const int nPtBinsCorrelations = 12; -const double pTBinsCorrelations[nPtBinsCorrelations + 1] = {0., 1., 2., 3., 4., 5., 6., 7., 8., 12., 16., 24., 99.}; -const auto vecPtBinsCorrelations = std::vector{pTBinsCorrelations, pTBinsCorrelations + nPtBinsCorrelations + 1}; -const double signalRegionLeftDefault[nPtBinsCorrelations] = {1.7948, 1.8198, 1.8198, 1.8148, 1.8148, 1.8048, 1.8048, 1.7948, 1.7948, 1.7898, 1.7848, 1.7598}; -const double signalRegionRightDefault[nPtBinsCorrelations] = {1.9098, 1.8998, 1.9048, 1.9048, 1.9148, 1.9248, 1.9298, 1.9348, 1.9398, 1.9298, 1.9398, 1.9198}; -const double sidebandLeftInnerDefault[nPtBinsCorrelations] = {1.7398, 1.7748, 1.7798, 1.7698, 1.7648, 1.7448, 1.7448, 1.7198, 1.7198, 1.7198, 1.7048, 1.6798}; -const double sidebandLeftOuterDefault[nPtBinsCorrelations] = {1.6298, 1.6898, 1.6948, 1.6748, 1.6648, 1.6248, 1.6198, 1.5748, 1.5748, 1.5798, 1.5448, 1.5198}; -const double sidebandRightInnerDefault[nPtBinsCorrelations] = {1.9648, 1.9448, 1.9448, 1.9548, 1.9648, 1.9848, 1.9948, 2.0098, 2.0148, 1.9998, 2.0248, 1.9998}; -const double sidebandRightOuterDefault[nPtBinsCorrelations] = {2.0748, 2.0248, 2.0298, 2.0448, 2.0648, 2.1048, 2.1148, 2.1548, 2.1648, 2.1398, 2.1848, 2.1598}; -const auto vecsignalRegionLeft = std::vector{signalRegionLeftDefault, signalRegionLeftDefault + nPtBinsCorrelations}; -const auto vecsignalRegionRight = std::vector{signalRegionRightDefault, signalRegionRightDefault + nPtBinsCorrelations}; -const auto vecSidebandLeftInner = std::vector{sidebandLeftInnerDefault, sidebandLeftInnerDefault + nPtBinsCorrelations}; -const auto vecSidebandLeftOuter = std::vector{sidebandLeftOuterDefault, sidebandLeftOuterDefault + nPtBinsCorrelations}; -const auto vecSidebandRightInner = std::vector{sidebandRightInnerDefault, sidebandRightInnerDefault + nPtBinsCorrelations}; -const auto vecSidebandRightOuter = std::vector{sidebandRightOuterDefault, sidebandRightOuterDefault + nPtBinsCorrelations}; -const int nPtbinsPtEfficiencyD = o2::analysis::hf_cuts_d0_to_pi_k::NBinsPt; -const double efficiencyDmesonDefault[nPtbinsPtEfficiencyD] = {}; -const auto vecEfficiencyDmeson = std::vector{efficiencyDmesonDefault, efficiencyDmesonDefault + nPtbinsPtEfficiencyD}; - struct HfTaskCorrelationD0Hadrons { // pT ranges: the default values are those embedded in hf_cuts_d0_to_pi_k (i.e. the mass pT bins), but can be redefined via json files Configurable> binsPtD{"binsPtD", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for candidate mass plots and efficiency"}; Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle efficiency"}; - Configurable> binsCorrelations{"binsCorrelations", std::vector{vecPtBinsCorrelations}, "pT bin limits for correlation plots"}; + Configurable> binsCorrelations{"binsCorrelations", {0., 1., 2., 3., 4., 5., 6., 7., 8., 12., 16., 24., 99.}, "pT bin limits for correlation plots"}; + Configurable> binsPtMl{"binsPtMl", {0., 1., 2., 3., 4., 5., 6., 7., 8., 12., 16., 24., 99.}, "D0 candidate pT bin edges for the ML score thresholds"}; Configurable> binsPtEfficiencyD{"binsPtEfficiencyD", std::vector{o2::analysis::hf_cuts_d0_to_pi_k::vecBinsPt}, "pT bin limits for efficiency"}; Configurable> binsPtEfficiencyHad{"binsPtEfficiencyHad", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for associated particle efficiency"}; - Configurable> efficiencyDmeson{"efficiencyDmeson", std::vector{vecEfficiencyDmeson}, "Efficiency values for D meson specie under study"}; + Configurable> efficiencyDmeson{"efficiencyDmeson", std::vector(o2::analysis::hf_cuts_d0_to_pi_k::NBinsPt, 0.), "Efficiency values for D meson specie under study"}; Configurable> efficiencyHad{"efficiencyHad", {1., 1., 1., 1., 1., 1.}, "efficiency values for associated particles"}; Configurable applyEfficiency{"applyEfficiency", 1, "Flag for applying efficiency weights"}; Configurable> classMl{"classMl", {0, 1, 2}, "Indexes of ML scores to be stored. Three indexes max."}; @@ -96,6 +81,8 @@ struct HfTaskCorrelationD0Hadrons { Configurable> mlOutputBkgD0{"mlOutputBkgD0", {0.5, 0.5, 0.5, 0.5}, "Machine learning scores for bkg"}; Configurable> mlOutputPromptD0bar{"mlOutputPromptD0bar", {0.5, 0.5, 0.5, 0.5}, "Machine learning scores for prompt"}; Configurable> mlOutputBkgD0bar{"mlOutputBkgD0bar", {0.5, 0.5, 0.5, 0.5}, "Machine learning scores for bkg"}; + Configurable> mlOutputNonPromptD0{"mlOutputNonPromptD0", {0.5, 0.5, 0.5, 0.5}, "Maximum non-prompt ML score for D0"}; + Configurable> mlOutputNonPromptD0bar{"mlOutputNonPromptD0bar", {0.5, 0.5, 0.5, 0.5}, "Maximum non-prompt ML score for D0bar"}; Configurable ptCandMin{"ptCandMin", 1., "min. cand. pT"}; Configurable ptCandMax{"ptCandMax", 50., "max. cand pT"}; Configurable ptTrackMin{"ptTrackMin", 0.3, "min. track pT"}; @@ -110,17 +97,24 @@ struct HfTaskCorrelationD0Hadrons { Configurable selectionFlagD0{"selectionFlagD0", 1, "Selection Flag for D0 (bar)"}; Configurable selNoSameBunchPileUpColl{"selNoSameBunchPileUpColl", true, "Flag for rejecting the collisions associated with the same bunch crossing"}; Configurable useCentrality{"useCentrality", false, "Flag for centrality dependent analysis"}; - Configurable> signalRegionLeft{"signalRegionLeft", std::vector{vecsignalRegionLeft}, "Inner values of signal region vs pT"}; - Configurable> signalRegionRight{"signalRegionRight", std::vector{vecsignalRegionRight}, "Outer values of signal region vs pT"}; - Configurable> sidebandLeftInner{"sidebandLeftInner", std::vector{vecSidebandLeftInner}, "Inner values of left sideband vs pT"}; - Configurable> sidebandLeftOuter{"sidebandLeftOuter", std::vector{vecSidebandLeftOuter}, "Outer values of left sideband vs pT"}; - Configurable> sidebandRightInner{"sidebandRightInner", std::vector{vecSidebandRightInner}, "Inner values of right sideband vs pT"}; - Configurable> sidebandRightOuter{"sidebandRightOuter", std::vector{vecSidebandRightOuter}, "Outer values of right sideband vs pT"}; + Configurable useSel8ForEff{"useSel8ForEff", true, "Flag for applying sel8 for collision selection in efficiency"}; + Configurable removeCollWSplitVtx{"removeCollWSplitVtx", false, "Flag for rejecting MC collisions with more than one reconstructed collision"}; + Configurable centEstimator{"centEstimator", 3, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3, FV0A: 4)"}; + Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance in efficiency"}; + Configurable centralityMin{"centralityMin", 0., "min. centrality for efficiency"}; + Configurable centralityMax{"centralityMax", 100., "max. centrality for efficiency"}; + Configurable rejectD0D0barHypothesis{"rejectD0D0barHypothesis", true, "Reject candidates satisfying both D0 and D0bar hypothesis"}; + Configurable> signalRegionLeft{"signalRegionLeft", {1.7948, 1.8198, 1.8198, 1.8148, 1.8148, 1.8048, 1.8048, 1.7948, 1.7948, 1.7898, 1.7848, 1.7598}, "Inner values of signal region vs pT"}; + Configurable> signalRegionRight{"signalRegionRight", {1.9098, 1.8998, 1.9048, 1.9048, 1.9148, 1.9248, 1.9298, 1.9348, 1.9398, 1.9298, 1.9398, 1.9198}, "Outer values of signal region vs pT"}; + Configurable> sidebandLeftInner{"sidebandLeftInner", {1.7398, 1.7748, 1.7798, 1.7698, 1.7648, 1.7448, 1.7448, 1.7198, 1.7198, 1.7198, 1.7048, 1.6798}, "Inner values of left sideband vs pT"}; + Configurable> sidebandLeftOuter{"sidebandLeftOuter", {1.6298, 1.6898, 1.6948, 1.6748, 1.6648, 1.6248, 1.6198, 1.5748, 1.5748, 1.5798, 1.5448, 1.5198}, "Outer values of left sideband vs pT"}; + Configurable> sidebandRightInner{"sidebandRightInner", {1.9648, 1.9448, 1.9448, 1.9548, 1.9648, 1.9848, 1.9948, 2.0098, 2.0148, 1.9998, 2.0248, 1.9998}, "Inner values of right sideband vs pT"}; + Configurable> sidebandRightOuter{"sidebandRightOuter", {2.0748, 2.0248, 2.0298, 2.0448, 2.0648, 2.1048, 2.1148, 2.1548, 2.1648, 2.1398, 2.1848, 2.1598}, "Outer values of right sideband vs pT"}; Configurable isTowardTransverseAway{"isTowardTransverseAway", false, "Divide into three regions: toward, transverse, and away"}; Configurable leadingParticlePtMin{"leadingParticlePtMin", 0., "Min for leading particle pt"}; enum CandidateStep { kCandidateStepMcGenAll = 0, - kCandidateStepMcGenD0ToPiKPi, + kCandidateStepMcGenD0ToKPi, kCandidateStepMcCandInAcceptance, kCandidateStepMcDaughtersInAcceptance, kCandidateStepMcReco, @@ -132,10 +126,18 @@ struct HfTaskCorrelationD0Hadrons { using CandD0McReco = soa::Filtered>; using CandD0McGen = soa::Join; using TracksWithMc = soa::Filtered>; // trackFilter applied + using CollisionsWithCent = soa::Join; + using McCollisionsWithMult = soa::Join; Filter d0Filter = (aod::hf_sel_candidate_d0::isSelD0 >= selectionFlagD0) || (aod::hf_sel_candidate_d0::isSelD0bar >= selectionFlagD0); Filter trackFilter = (nabs(aod::track::eta) < etaTrackMax) && (aod::track::pt > ptTrackMin) && (aod::track::pt < ptTrackMax) && (nabs(aod::track::dcaXY) < dcaXYTrackMax) && (nabs(aod::track::dcaZ) < dcaZTrackMax); + Preslice perCollisionCand = o2::aod::hf_cand::collisionId; + Preslice perCollisionCandMc = o2::aod::mcparticle::mcCollisionId; + Preslice perCollisionTrack = o2::aod::track::collisionId; + Preslice perCollisionTrackMc = o2::aod::mcparticle::mcCollisionId; + PresliceUnsorted collPerCollMc = o2::aod::mccollisionlabel::mcCollisionId; + ConfigurableAxis binsMassD{"binsMassD", {200, 1.3848, 2.3848}, "inv. mass (#pi K) (GeV/#it{c}^{2});entries"}; ConfigurableAxis binsBdtScore{"binsBdtScore", {100, 0., 1.}, "Bdt output scores"}; ConfigurableAxis binsEta{"binsEta", {100, -2., 2.}, "#it{#eta}"}; @@ -153,8 +155,8 @@ struct HfTaskCorrelationD0Hadrons { AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec const axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; @@ -167,6 +169,7 @@ struct HfTaskCorrelationD0Hadrons { registry.add("hBdtScorePrompt", "D0 BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); registry.add("hBdtScoreBkg", "D0 BDT bkg score", {HistType::kTH1F, {axisBdtScore}}); + registry.add("hBdtScoreNonPrompt", "D0 BDT non-prompt score", {HistType::kTH1F, {axisBdtScore}}); registry.add("hMassD0VsPt", "D0 candidates massVsPt", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hMassD0VsPtWoEff", "D0 candidates massVsPt without efficiency", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); @@ -204,8 +207,8 @@ struct HfTaskCorrelationD0Hadrons { registry.add("hDeltaEtaPtIntSignalRegionRecSig", "D0-h deltaEta MC reco signal region, signal", {HistType::kTH1F, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSignalRegionRecSig", "D0-h deltaPhi MC reco signal region, signal", {HistType::kTH1F, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSignalRegionRecSig", "D0-h deltaPhi vs deltaEta MC reco signal region, signal", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegionRecSig", "D0-h correlations MC reco signal region, signal", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtPhysicalPrimaryRecSig", "D0-h correlations signal region (only true primary particles) MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegionRecSig", "D0-h correlations MC reco signal region, signal", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}, {axisCentFT0M}}}); + registry.add("hCorrel2DVsPtPhysicalPrimaryRecSig", "D0-h correlations signal region (only true primary particles) MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisD0Prompt}, {axisPoolBin}, {axisCentFT0M}}}); registry.add("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig", "D0-h correlations signal region Prompt-NonPrompt MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSignalRegionNonPromptD0NonPromptHadronRecSig", "D0-h correlations signal region NonPrompt-NonPrompt MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSignalRegionSoftPiRecSig", "D0-h deltaEta MC reco signal region, signal", {HistType::kTH1F, {axisDeltaEta}}); @@ -294,18 +297,65 @@ struct HfTaskCorrelationD0Hadrons { registry.add("hBdtScoreBkgD0", "D0 BDT bkg score", {HistType::kTH1F, {axisBdtScore}}); registry.add("hBdtScorePromptD0bar", "D0bar BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); registry.add("hBdtScoreBkgD0bar", "D0bar BDT bkg score", {HistType::kTH1F, {axisBdtScore}}); + registry.add("hBdtScoreNonPromptD0", "D0 BDT non-prompt score", {HistType::kTH1F, {axisBdtScore}}); + registry.add("hBdtScoreNonPromptD0bar", "D0bar BDT non-prompt score", {HistType::kTH1F, {axisBdtScore}}); // Efficiency histograms - registry.add("hPtCandMcRecPrompt", "D0 prompt candidates pt", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcRecNonPrompt", "D0 non prompt candidates pt", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcGenPrompt", "D0,Hadron particles prompt - MC Gen", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcGenNonPrompt", "D0,Hadron particles non prompt - MC Gen", {HistType::kTH1F, {axisPtD}}); - registry.add("hPtCandMcGenDaughterInAcc", "D0,Hadron particles non prompt - MC Gen", {HistType::kTH1F, {axisPtD}}); - - auto hCandidates = registry.add("hCandidates", "Candidate count at different steps", {HistType::kStepTHnF, {axisPtD, axisMultFT0M, {RecoDecay::OriginType::NonPrompt + 1, +RecoDecay::OriginType::None - 0.5, +RecoDecay::OriginType::NonPrompt + 0.5}}, kCandidateNSteps}); - hCandidates->GetAxis(0)->SetTitle("#it{p}_{T} (GeV/#it{c})"); - hCandidates->GetAxis(1)->SetTitle("multiplicity"); - hCandidates->GetAxis(2)->SetTitle("Charm hadron origin"); + registry.add("hPtCandMcGenDaughterInAcc", "D0 daughters in acceptance - MC Gen", {HistType::kTH1F, {axisPtD}}); + + if (useCentrality) { + registry.add("hPtCandMcRecPrompt", "D0 prompt candidates", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + registry.add("hPtCandMcRecNonPrompt", "D0 non prompt candidates", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + registry.add("hPtCandMcGenPrompt", "D0 prompt particles - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + registry.add("hPtCandMcGenNonPrompt", "D0 non prompt particles - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisCentFT0M}}}); + + auto hCandidates = registry.add("hCandidates", "Candidate count at different steps", {HistType::kStepTHnF, {axisPtD, axisCentFT0M, {RecoDecay::OriginType::NonPrompt + 1, +RecoDecay::OriginType::None - 0.5, +RecoDecay::OriginType::NonPrompt + 0.5}}, kCandidateNSteps}); + hCandidates->GetAxis(0)->SetTitle("#it{p}_{T} (GeV/#it{c})"); + hCandidates->GetAxis(1)->SetTitle("centrality FT0M (%)"); + hCandidates->GetAxis(2)->SetTitle("Charm hadron origin"); + } else { + registry.add("hPtCandMcRecPrompt", "D0 prompt candidates pt", {HistType::kTH1F, {axisPtD}}); + registry.add("hPtCandMcRecNonPrompt", "D0 non prompt candidates pt", {HistType::kTH1F, {axisPtD}}); + registry.add("hPtCandMcGenPrompt", "D0 prompt particles - MC Gen", {HistType::kTH1F, {axisPtD}}); + registry.add("hPtCandMcGenNonPrompt", "D0 non prompt particles - MC Gen", {HistType::kTH1F, {axisPtD}}); + + auto hCandidates = registry.add("hCandidates", "Candidate count at different steps", {HistType::kStepTHnF, {axisPtD, axisMultFT0M, {RecoDecay::OriginType::NonPrompt + 1, +RecoDecay::OriginType::None - 0.5, +RecoDecay::OriginType::NonPrompt + 0.5}}, kCandidateNSteps}); + hCandidates->GetAxis(0)->SetTitle("#it{p}_{T} (GeV/#it{c})"); + hCandidates->GetAxis(1)->SetTitle("multiplicity"); + hCandidates->GetAxis(2)->SetTitle("Charm hadron origin"); + } + + // Associated-track efficiency histograms. + registry.add("hFakeCollisionTrackEff", "Fake collision counter - track efficiency", {HistType::kTH1F, {{1, -0.5, 0.5, "n fake coll"}}}); + registry.add("hFakeTracksTrackEff", "Fake track counter - track efficiency", {HistType::kTH1F, {{1, -0.5, 0.5, "n fake tracks"}}}); + if (useCentrality) { + registry.add("hPtParticleAssocMcGen", "Associated primary particles - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtParticleAssocMcRec", "Associated primary particles - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmPionMcGen", "Primary pions - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmKaonMcGen", "Primary kaons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmProtonMcGen", "Primary protons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmElectronMcGen", "Primary electrons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmMuonMcGen", "Primary muons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmPionMcRec", "Primary pions - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmKaonMcRec", "Primary kaons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmProtonMcRec", "Primary protons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmElectronMcRec", "Primary electrons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + registry.add("hPtPrmMuonMcRec", "Primary muons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentFT0M}}}); + } else { + // Minimum-bias tracking efficiency versus pT only. + registry.add("hPtParticleAssocMcGen", "Associated primary particles - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtParticleAssocMcRec", "Associated primary particles - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmPionMcGen", "Primary pions - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmKaonMcGen", "Primary kaons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmProtonMcGen", "Primary protons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmElectronMcGen", "Primary electrons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmMuonMcGen", "Primary muons - MC Gen", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmPionMcRec", "Primary pions - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmKaonMcRec", "Primary kaons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmProtonMcRec", "Primary protons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmElectronMcRec", "Primary electrons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + registry.add("hPtPrmMuonMcRec", "Primary muons - MC Rec", {HistType::kTH1F, {axisPtHadron}}); + } } /// D-h correlation pair filling task, from pair tables - for real data and data-like analysis (i.e. reco-level w/o matching request via MC truth) @@ -320,15 +370,28 @@ struct HfTaskCorrelationD0Hadrons { float const bdtScoreBkgD0 = candidate.mlScoreBkgD0(); float const bdtScorePromptD0bar = candidate.mlScorePromptD0bar(); float const bdtScoreBkgD0bar = candidate.mlScoreBkgD0bar(); + float const bdtScoreNonPromptD0 = candidate.mlScoreNonPromptD0(); + float const bdtScoreNonPromptD0bar = candidate.mlScoreNonPromptD0bar(); int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); + int const ptBinMl = o2::analysis::findBin(binsPtMl, ptD); + + if (ptBinMl < 0 || effBinD < 0) { + continue; + } registry.fill(HIST("hBdtScorePromptD0"), bdtScorePromptD0); registry.fill(HIST("hBdtScoreBkgD0"), bdtScoreBkgD0); registry.fill(HIST("hBdtScorePromptD0bar"), bdtScorePromptD0bar); registry.fill(HIST("hBdtScoreBkgD0bar"), bdtScoreBkgD0bar); + registry.fill(HIST("hBdtScoreNonPromptD0"), bdtScoreNonPromptD0); + registry.fill(HIST("hBdtScoreNonPromptD0bar"), bdtScoreNonPromptD0bar); + + // Reject candidates for which neither the D0 nor D0bar hypothesis passes the pT-dependent background, prompt, and non-prompt ML score cuts + + bool const passesD0Ml = bdtScorePromptD0 >= mlOutputPromptD0->at(ptBinMl) && bdtScoreBkgD0 <= mlOutputBkgD0->at(ptBinMl) && bdtScoreNonPromptD0 <= mlOutputNonPromptD0->at(ptBinMl); + bool const passesD0barMl = bdtScorePromptD0bar >= mlOutputPromptD0bar->at(ptBinMl) && bdtScoreBkgD0bar <= mlOutputBkgD0bar->at(ptBinMl) && bdtScoreNonPromptD0bar <= mlOutputNonPromptD0bar->at(ptBinMl); - if ((bdtScorePromptD0 < mlOutputPromptD0->at(effBinD) || bdtScoreBkgD0 > mlOutputBkgD0->at(effBinD)) && - (bdtScorePromptD0bar < mlOutputPromptD0bar->at(effBinD) || bdtScoreBkgD0bar > mlOutputBkgD0bar->at(effBinD))) { + if (!passesD0Ml && !passesD0barMl) { continue; } @@ -341,6 +404,7 @@ struct HfTaskCorrelationD0Hadrons { registry.fill(HIST("hMassD0VsPtWoEff"), massD, ptD); registry.fill(HIST("hBdtScorePrompt"), bdtScorePromptD0); registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkgD0); + registry.fill(HIST("hBdtScoreNonPrompt"), bdtScoreNonPromptD0); } for (const auto& pairEntry : pairEntries) { @@ -358,6 +422,7 @@ struct HfTaskCorrelationD0Hadrons { int const signalStatus = pairEntry.signalStatus(); int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); int const ptBinD = o2::analysis::findBin(binsCorrelations, ptD); + int const ptBinMl = o2::analysis::findBin(binsPtMl, ptD); int const poolBin = pairEntry.poolBin(); float const trackDcaXY = pairEntry.trackDcaXY(); float const trackDcaZ = pairEntry.trackDcaZ(); @@ -367,14 +432,32 @@ struct HfTaskCorrelationD0Hadrons { float const bdtScoreBkgD0 = pairEntry.mlScoreBkgD0(); float const bdtScorePromptD0bar = pairEntry.mlScorePromptD0bar(); float const bdtScoreBkgD0bar = pairEntry.mlScoreBkgD0bar(); + float const bdtScoreNonPromptD0 = pairEntry.mlScoreNonPromptD0(); + float const bdtScoreNonPromptD0bar = pairEntry.mlScoreNonPromptD0bar(); // reject entries outside pT ranges of interest - if (ptBinD < 0 || effBinD < 0) { + if (ptBinD < 0 || ptBinMl < 0 || effBinD < 0) { continue; } - if ((bdtScorePromptD0 < mlOutputPromptD0->at(ptBinD) || bdtScoreBkgD0 > mlOutputBkgD0->at(ptBinD)) && - (bdtScorePromptD0bar < mlOutputPromptD0bar->at(ptBinD) || bdtScoreBkgD0bar > mlOutputBkgD0bar->at(ptBinD))) { + + // Reject candidates for which neither the D0 nor D0bar hypothesis passes the pT-dependent background, prompt, and non-prompt ML score cuts + + bool const passesD0Ml = bdtScorePromptD0 >= mlOutputPromptD0->at(ptBinMl) && bdtScoreBkgD0 <= mlOutputBkgD0->at(ptBinMl) && bdtScoreNonPromptD0 <= mlOutputNonPromptD0->at(ptBinMl); + bool const passesD0barMl = bdtScorePromptD0bar >= mlOutputPromptD0bar->at(ptBinMl) && bdtScoreBkgD0bar <= mlOutputBkgD0bar->at(ptBinMl) && bdtScoreNonPromptD0bar <= mlOutputNonPromptD0bar->at(ptBinMl); + + if (!passesD0Ml && !passesD0barMl) { + continue; + } + + bool const isD0D0barBoth = signalStatus == ParticleTypeData::D0D0barBoth || signalStatus == ParticleTypeData::D0D0barBothSoftPi; + if (rejectD0D0barHypothesis && isD0D0barBoth) { continue; } + + bool const isD0 = passesD0Ml && (signalStatus == ParticleTypeData::D0Only || signalStatus == ParticleTypeData::D0D0barBoth); + bool const isD0bar = passesD0barMl && (signalStatus == ParticleTypeData::D0barOnly || signalStatus == ParticleTypeData::D0D0barBoth); + bool const isD0SoftPi = signalStatus == ParticleTypeData::D0OnlySoftPi || signalStatus == ParticleTypeData::D0D0barBothSoftPi; + bool const isD0barSoftPi = signalStatus == ParticleTypeData::D0barOnlySoftPi || signalStatus == ParticleTypeData::D0D0barBothSoftPi; + if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { continue; } @@ -408,8 +491,9 @@ struct HfTaskCorrelationD0Hadrons { break; } } + // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots - if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && (signalStatus == ParticleTypeData::D0Only)) { + if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && isD0) { // in signal region registry.fill(HIST("hCorrel2DVsPtSignalRegion"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegion"), deltaPhi, deltaEta, efficiencyWeight); @@ -417,7 +501,7 @@ struct HfTaskCorrelationD0Hadrons { registry.fill(HIST("hDeltaPhiPtIntSignalRegion"), deltaPhi, efficiencyWeight); } - if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && (signalStatus == ParticleTypeData::D0OnlySoftPi)) { + if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && isD0SoftPi) { // in signal region, fills for soft pion only in ME registry.fill(HIST("hCorrel2DVsPtSignalRegionSoftPi"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionSoftPi"), deltaPhi, deltaEta, efficiencyWeight); @@ -425,7 +509,7 @@ struct HfTaskCorrelationD0Hadrons { registry.fill(HIST("hDeltaPhiPtIntSignalRegionSoftPi"), deltaPhi, efficiencyWeight); } - if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && (signalStatus == ParticleTypeData::D0barOnly)) { + if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && isD0bar) { // in signal region registry.fill(HIST("hCorrel2DVsPtSignalRegion"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegion"), deltaPhi, deltaEta, efficiencyWeight); @@ -433,7 +517,7 @@ struct HfTaskCorrelationD0Hadrons { registry.fill(HIST("hDeltaPhiPtIntSignalRegion"), deltaPhi, efficiencyWeight); } - if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && (signalStatus >= ParticleTypeData::D0barOnlySoftPi)) { + if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && isD0barSoftPi) { // in signal region, fills for soft pion only in ME registry.fill(HIST("hCorrel2DVsPtSignalRegionSoftPi"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionSoftPi"), deltaPhi, deltaEta, efficiencyWeight); @@ -443,7 +527,7 @@ struct HfTaskCorrelationD0Hadrons { if (((massD > sidebandLeftOuter->at(ptBinD) && massD < sidebandLeftInner->at(ptBinD)) || (massD > sidebandRightInner->at(ptBinD) && massD < sidebandRightOuter->at(ptBinD))) && - (signalStatus == ParticleTypeData::D0Only)) { + isD0) { // in sideband region registry.fill(HIST("hCorrel2DVsPtSidebands"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebands"), deltaPhi, deltaEta, efficiencyWeight); @@ -453,7 +537,7 @@ struct HfTaskCorrelationD0Hadrons { if (((massD > sidebandLeftOuter->at(ptBinD) && massD < sidebandLeftInner->at(ptBinD)) || (massD > sidebandRightInner->at(ptBinD) && massD < sidebandRightOuter->at(ptBinD))) && - (signalStatus == ParticleTypeData::D0OnlySoftPi)) { + isD0SoftPi) { // in sideband region, fills for soft pion only in ME registry.fill(HIST("hCorrel2DVsPtSidebandsSoftPi"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsSoftPi"), deltaPhi, deltaEta, efficiencyWeight); @@ -463,7 +547,7 @@ struct HfTaskCorrelationD0Hadrons { if (((massDbar > sidebandLeftOuter->at(ptBinD) && massDbar < sidebandLeftInner->at(ptBinD)) || (massDbar > sidebandRightInner->at(ptBinD) && massDbar < sidebandRightOuter->at(ptBinD))) && - (signalStatus == ParticleTypeData::D0barOnly)) { + isD0bar) { // in sideband region registry.fill(HIST("hCorrel2DVsPtSidebands"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebands"), deltaPhi, deltaEta, efficiencyWeight); @@ -473,7 +557,7 @@ struct HfTaskCorrelationD0Hadrons { if (((massDbar > sidebandLeftOuter->at(ptBinD) && massDbar < sidebandLeftInner->at(ptBinD)) || (massDbar > sidebandRightInner->at(ptBinD) && massDbar < sidebandRightOuter->at(ptBinD))) && - (signalStatus >= ParticleTypeData::D0barOnlySoftPi)) { + isD0barSoftPi) { // in sideband region, fills for soft pion only in ME registry.fill(HIST("hCorrel2DVsPtSidebandsSoftPi"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsSoftPi"), deltaPhi, deltaEta, efficiencyWeight); @@ -491,22 +575,36 @@ struct HfTaskCorrelationD0Hadrons { float const ptD = candidate.ptD(); float const bdtScorePromptD0 = candidate.mlScorePromptD0(); float const bdtScoreBkgD0 = candidate.mlScoreBkgD0(); + float const bdtScoreNonPromptD0 = candidate.mlScoreNonPromptD0(); float const bdtScorePromptD0bar = candidate.mlScorePromptD0bar(); float const bdtScoreBkgD0bar = candidate.mlScoreBkgD0bar(); - int const effBinD = o2::analysis::findBin(binsPtEfficiencyD, ptD); + float const bdtScoreNonPromptD0bar = candidate.mlScoreNonPromptD0bar(); + int const ptBinMl = o2::analysis::findBin(binsPtMl, ptD); + + if (ptBinMl < 0) { + continue; + } + + // Reject candidates for which neither the D0 nor D0bar hypothesis passes the pT-dependent background, prompt, and non-prompt ML score cuts + + bool const passesD0Ml = bdtScorePromptD0 >= mlOutputPromptD0->at(ptBinMl) && bdtScoreBkgD0 <= mlOutputBkgD0->at(ptBinMl) && bdtScoreNonPromptD0 <= mlOutputNonPromptD0->at(ptBinMl); + bool const passesD0barMl = bdtScorePromptD0bar >= mlOutputPromptD0bar->at(ptBinMl) && bdtScoreBkgD0bar <= mlOutputBkgD0bar->at(ptBinMl) && bdtScoreNonPromptD0bar <= mlOutputNonPromptD0bar->at(ptBinMl); + + if (!passesD0Ml && !passesD0barMl) { + continue; + } registry.fill(HIST("hBdtScorePromptD0"), bdtScorePromptD0); registry.fill(HIST("hBdtScoreBkgD0"), bdtScoreBkgD0); registry.fill(HIST("hBdtScorePromptD0bar"), bdtScorePromptD0bar); registry.fill(HIST("hBdtScoreBkgD0bar"), bdtScoreBkgD0bar); - - if ((bdtScorePromptD0 < mlOutputPromptD0->at(effBinD) || bdtScoreBkgD0 > mlOutputBkgD0->at(effBinD)) && - (bdtScorePromptD0bar < mlOutputPromptD0bar->at(effBinD) || bdtScoreBkgD0bar > mlOutputBkgD0bar->at(effBinD))) { - continue; - } } for (const auto& pairEntry : pairEntries) { + float cent = 0.; + if (useCentrality) { + cent = pairEntry.cent(); + } // define variables for widely used quantities double const deltaPhi = pairEntry.deltaPhi(); double const deltaEta = pairEntry.deltaEta(); @@ -516,6 +614,7 @@ struct HfTaskCorrelationD0Hadrons { double const massDbar = pairEntry.mDbar(); int const signalStatus = pairEntry.signalStatus(); int const ptBinD = o2::analysis::findBin(binsCorrelations, ptD); + int const ptBinMl = o2::analysis::findBin(binsPtMl, ptD); int const poolBin = pairEntry.poolBin(); float const trackDcaXY = pairEntry.trackDcaXY(); float const trackDcaZ = pairEntry.trackDcaZ(); @@ -528,11 +627,28 @@ struct HfTaskCorrelationD0Hadrons { bool const isPhysicalPrimary = pairEntry.isPhysicalPrimary(); bool const isD0Prompt = pairEntry.isPrompt(); int const statusPromptHadron = pairEntry.trackOrigin(); + float const bdtScoreNonPromptD0 = pairEntry.mlScoreNonPromptD0(); + float const bdtScoreNonPromptD0bar = pairEntry.mlScoreNonPromptD0bar(); + + if (ptBinD < 0 || ptBinMl < 0) { + continue; + } + + // Reject candidates for which neither the D0 nor D0bar hypothesis passes the pT-dependent background, prompt, and non-prompt ML score cuts - if ((bdtScorePromptD0 < mlOutputPromptD0->at(ptBinD) || bdtScoreBkgD0 > mlOutputBkgD0->at(ptBinD)) && - (bdtScorePromptD0bar < mlOutputPromptD0bar->at(ptBinD) || bdtScoreBkgD0bar > mlOutputBkgD0bar->at(ptBinD))) { + bool const passesD0Ml = bdtScorePromptD0 >= mlOutputPromptD0->at(ptBinMl) && bdtScoreBkgD0 <= mlOutputBkgD0->at(ptBinMl) && bdtScoreNonPromptD0 <= mlOutputNonPromptD0->at(ptBinMl); + bool const passesD0barMl = bdtScorePromptD0bar >= mlOutputPromptD0bar->at(ptBinMl) && bdtScoreBkgD0bar <= mlOutputBkgD0bar->at(ptBinMl) && bdtScoreNonPromptD0bar <= mlOutputNonPromptD0bar->at(ptBinMl); + + if (!passesD0Ml && !passesD0barMl) { continue; } + + bool const isD0 = TESTBIT(signalStatus, ParticleTypeMcRec::D0Sig) || TESTBIT(signalStatus, ParticleTypeMcRec::D0Ref) || TESTBIT(signalStatus, ParticleTypeMcRec::D0Bg); + bool const isD0bar = TESTBIT(signalStatus, ParticleTypeMcRec::D0barSig) || TESTBIT(signalStatus, ParticleTypeMcRec::D0barRef) || TESTBIT(signalStatus, ParticleTypeMcRec::D0barBg); + if (rejectD0D0barHypothesis && isD0 && isD0bar) { + continue; + } + if (trackDcaXY > dcaXYTrackMax || trackDcaZ > dcaZTrackMax || trackTpcCrossedRows < nTpcCrossedRaws) { continue; } @@ -571,14 +687,14 @@ struct HfTaskCorrelationD0Hadrons { // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots // ---------------------- Fill plots for signal case, D0 ->1, D0bar ->8 --------------------------------------------- - if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && (TESTBIT(signalStatus, ParticleTypeMcRec::D0Sig))) { + if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0Sig) && passesD0Ml) { // in signal region, tests bit ParticleTypeMcRec::D0Sig, SE-> softpi removed, ME-> inclusive - registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecSig"), deltaPhi, deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegionRecSig"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecSig"), deltaPhi, efficiencyWeight); if (isPhysicalPrimary) { - registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); if (isD0Prompt && statusPromptHadron == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } else if (!isD0Prompt && statusPromptHadron == RecoDecay::OriginType::NonPrompt) { @@ -587,14 +703,14 @@ struct HfTaskCorrelationD0Hadrons { } } - if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && (TESTBIT(signalStatus, ParticleTypeMcRec::D0barSig))) { + if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0barSig) && passesD0barMl) { // in signal region, tests bit ParticleTypeMcRec::D0barSig, SE-> softpi removed, ME-> inclusive - registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegionRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecSig"), deltaPhi, deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegionRecSig"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecSig"), deltaPhi, efficiencyWeight); if (isPhysicalPrimary) { - registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryRecSig"), deltaPhi, deltaEta, ptD, ptHadron, static_cast(isD0Prompt), poolBin, cent, efficiencyWeight); if (isD0Prompt && statusPromptHadron == RecoDecay::OriginType::Prompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionPromptD0PromptHadronRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } else if (!isD0Prompt && statusPromptHadron == RecoDecay::OriginType::NonPrompt) { @@ -613,7 +729,7 @@ struct HfTaskCorrelationD0Hadrons { if ((((massD > sidebandLeftOuter->at(ptBinD)) && (massD < sidebandLeftInner->at(ptBinD))) || ((massD > sidebandRightInner->at(ptBinD) && massD < sidebandRightOuter->at(ptBinD)))) && - ((TESTBIT(signalStatus, ParticleTypeMcRec::D0Sig)))) { + TESTBIT(signalStatus, ParticleTypeMcRec::D0Sig) && passesD0Ml) { // in sideband region, tests bit ParticleTypeMcRec::D0Sig, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSidebandsRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsRecSig"), deltaPhi, deltaEta, efficiencyWeight); @@ -623,7 +739,7 @@ struct HfTaskCorrelationD0Hadrons { if ((((massDbar > sidebandLeftOuter->at(ptBinD)) && (massDbar < sidebandLeftInner->at(ptBinD))) || ((massDbar > sidebandRightInner->at(ptBinD) && massDbar < sidebandRightOuter->at(ptBinD)))) && - (TESTBIT(signalStatus, ParticleTypeMcRec::D0barSig))) { + TESTBIT(signalStatus, ParticleTypeMcRec::D0barSig) && passesD0barMl) { // in sideband region, tests bit ParticleTypeMcRec::D0barSig, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSidebandsRecSig"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsRecSig"), deltaPhi, deltaEta, efficiencyWeight); @@ -642,7 +758,7 @@ struct HfTaskCorrelationD0Hadrons { } // ---------------------- Fill plots for reflection case, D0 ->2, D0bar ->16 --------------------------------------------- - if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0Ref)) { + if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0Ref) && passesD0Ml) { // in signal region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSignalRegionRecRef"), deltaPhi, deltaEta, ptD, ptHadron, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecRef"), deltaPhi, deltaEta, efficiencyWeight); @@ -650,7 +766,7 @@ struct HfTaskCorrelationD0Hadrons { registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecRef"), deltaPhi, efficiencyWeight); } - if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0barRef)) { + if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0barRef) && passesD0barMl) { // in signal region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSignalRegionRecRef"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecRef"), deltaPhi, deltaEta, efficiencyWeight); @@ -668,7 +784,7 @@ struct HfTaskCorrelationD0Hadrons { if ((((massD > sidebandLeftOuter->at(ptBinD)) && (massD < sidebandLeftInner->at(ptBinD))) || ((massD > sidebandRightInner->at(ptBinD) && massD < sidebandRightOuter->at(ptBinD)))) && - TESTBIT(signalStatus, ParticleTypeMcRec::D0Ref)) { + TESTBIT(signalStatus, ParticleTypeMcRec::D0Ref) && passesD0Ml) { // in sideband region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSidebandsRecRef"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsRecRef"), deltaPhi, deltaEta, efficiencyWeight); @@ -678,7 +794,7 @@ struct HfTaskCorrelationD0Hadrons { if ((((massDbar > sidebandLeftOuter->at(ptBinD)) && (massDbar < sidebandLeftInner->at(ptBinD))) || ((massDbar > sidebandRightInner->at(ptBinD) && massDbar < sidebandRightOuter->at(ptBinD)))) && - TESTBIT(signalStatus, ParticleTypeMcRec::D0barRef)) { + TESTBIT(signalStatus, ParticleTypeMcRec::D0barRef) && passesD0barMl) { // in sideband region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSidebandsRecRef"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsRecRef"), deltaPhi, deltaEta, efficiencyWeight); @@ -697,7 +813,7 @@ struct HfTaskCorrelationD0Hadrons { } // ---------------------- Fill plots for background case, D0 ->4, D0bar ->32 --------------------------------------------- - if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0Bg)) { + if ((massD > signalRegionLeft->at(ptBinD) && massD < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0Bg) && passesD0Ml) { // in signal region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSignalRegionRecBg"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecBg"), deltaPhi, deltaEta, efficiencyWeight); @@ -705,7 +821,7 @@ struct HfTaskCorrelationD0Hadrons { registry.fill(HIST("hDeltaPhiPtIntSignalRegionRecBg"), deltaPhi, efficiencyWeight); } - if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0barBg)) { + if ((massDbar > signalRegionLeft->at(ptBinD) && massDbar < signalRegionRight->at(ptBinD)) && TESTBIT(signalStatus, ParticleTypeMcRec::D0barBg) && passesD0barMl) { // in signal region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSignalRegionRecBg"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSignalRegionRecBg"), deltaPhi, deltaEta, efficiencyWeight); @@ -723,7 +839,7 @@ struct HfTaskCorrelationD0Hadrons { if ((((massD > sidebandLeftOuter->at(ptBinD)) && (massD < sidebandLeftInner->at(ptBinD))) || ((massD > sidebandRightInner->at(ptBinD) && massD < sidebandRightOuter->at(ptBinD)))) && - TESTBIT(signalStatus, ParticleTypeMcRec::D0Bg)) { + TESTBIT(signalStatus, ParticleTypeMcRec::D0Bg) && passesD0Ml) { // in sideband region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSidebandsRecBg"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsRecBg"), deltaPhi, deltaEta, efficiencyWeight); @@ -733,7 +849,7 @@ struct HfTaskCorrelationD0Hadrons { if ((((massDbar > sidebandLeftOuter->at(ptBinD)) && (massDbar < sidebandLeftInner->at(ptBinD))) || ((massDbar > sidebandRightInner->at(ptBinD) && massDbar < sidebandRightOuter->at(ptBinD)))) && - TESTBIT(signalStatus, ParticleTypeMcRec::D0barBg)) { + TESTBIT(signalStatus, ParticleTypeMcRec::D0barBg) && passesD0barMl) { // in sideband region, SE-> softpi removed, ME-> inclusive registry.fill(HIST("hCorrel2DVsPtSidebandsRecBg"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); registry.fill(HIST("hCorrel2DPtIntSidebandsRecBg"), deltaPhi, deltaEta, efficiencyWeight); @@ -812,34 +928,95 @@ struct HfTaskCorrelationD0Hadrons { PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcGen, "Process MC Gen mode", false); /// D0 reconstruction and selection efficiency - void processMcCandEfficiency(soa::Join const&, - soa::Join const&, + void processMcCandEfficiency(CollisionsWithCent const& collisions, + McCollisionsWithMult const& mcCollisions, CandD0McGen const& mcParticles, CandD0McReco const& candidates, aod::TracksWMc const&) { auto hCandidates = registry.get(HIST("hCandidates")); - /// Gen loop - float multiplicity = -1.; - for (const auto& mcParticle : mcParticles) { - // generated candidates - if (std::abs(mcParticle.pdgCode()) == Pdg::kD0) { - auto mcCollision = mcParticle.template mcCollision_as>(); - multiplicity = mcCollision.multMCFT0A() + mcCollision.multMCFT0C(); // multFT0M = multFt0A + multFT0C - hCandidates->Fill(kCandidateStepMcGenAll, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); - if (std::abs(mcParticle.flagMcMatchGen()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - hCandidates->Fill(kCandidateStepMcGenD0ToPiKPi, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); - auto yD0 = RecoDecay::y(mcParticle.pVector(), o2::constants::physics::MassD0); + /// Loop over generated MC collisions + for (const auto& mcCollision : mcCollisions) { + const auto groupedCollisions = collisions.sliceBy(collPerCollMc, mcCollision.globalIndex()); + const auto groupedMcParticles = mcParticles.sliceBy(perCollisionCandMc, mcCollision.globalIndex()); + + if (groupedCollisions.size() < 1) { // skip MC events without reconstructed collisions + continue; + } + if (groupedCollisions.size() > 1 && removeCollWSplitVtx) { // optionally reject split vertices + continue; + } + + const float multiplicityMc = mcCollision.multMCFT0A() + mcCollision.multMCFT0C(); + + float centralityMc = -1.f; + if (useCentrality) { + centralityMc = getCentralityGenColl(groupedCollisions, centEstimator); + } + + const float activityMc = useCentrality ? centralityMc : multiplicityMc; + + /// Loop over reconstructed collisions linked to this MC collision + for (const auto& collision : groupedCollisions) { + if (useSel8ForEff && !collision.sel8()) { + continue; + } + if (std::abs(collision.posZ()) > cutCollPosZMc) { + continue; + } + if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { + continue; + } + + float centralityRec = -1.f; + if (useCentrality) { + centralityRec = getCentralityColl(collision, centEstimator); + + if (centralityRec < centralityMin || centralityRec > centralityMax) { + continue; + } + } + if (!collision.has_mcCollision()) { + continue; + } + + const float activityRec = useCentrality ? centralityRec : collision.multFT0M(); + const auto groupedCandidates = candidates.sliceBy(perCollisionCand, collision.globalIndex()); + + /// Generated candidate loop + for (const auto& mcParticle : groupedMcParticles) { + if (std::abs(mcParticle.pdgCode()) != Pdg::kD0) { + continue; + } + + hCandidates->Fill(kCandidateStepMcGenAll, mcParticle.pt(), activityMc, mcParticle.originMcGen()); + + if (std::abs(mcParticle.flagMcMatchGen()) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + continue; + } + + hCandidates->Fill(kCandidateStepMcGenD0ToKPi, mcParticle.pt(), activityMc, mcParticle.originMcGen()); + + const auto yD0 = RecoDecay::y(mcParticle.pVector(), o2::constants::physics::MassD0); if (std::abs(yD0) <= yCandGenMax) { - hCandidates->Fill(kCandidateStepMcCandInAcceptance, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); + hCandidates->Fill(kCandidateStepMcCandInAcceptance, mcParticle.pt(), activityMc, mcParticle.originMcGen()); + if (mcParticle.originMcGen() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt()); - } - if (mcParticle.originMcGen() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt()); + if (useCentrality) { + registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt(), centralityMc); + } else { + registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt()); + } + } else if (mcParticle.originMcGen() == RecoDecay::OriginType::NonPrompt) { + if (useCentrality) { + registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt(), centralityMc); + } else { + registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt()); + } } } + bool isDaughterInAcceptance = true; auto daughters = mcParticle.template daughters_as(); for (const auto& daughter : daughters) { @@ -848,53 +1025,241 @@ struct HfTaskCorrelationD0Hadrons { } } if (isDaughterInAcceptance) { - hCandidates->Fill(kCandidateStepMcDaughtersInAcceptance, mcParticle.pt(), multiplicity, mcParticle.originMcGen()); + hCandidates->Fill(kCandidateStepMcDaughtersInAcceptance, mcParticle.pt(), activityMc, mcParticle.originMcGen()); registry.fill(HIST("hPtCandMcGenDaughterInAcc"), mcParticle.pt()); } - } - } - } + } // end generated candidate loop - // recontructed candidates loop - for (const auto& candidate : candidates) { - if (candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { - continue; - } - std::vector outputMlD0 = {-1., -1., -1.}; - std::vector outputMlD0bar = {-1., -1., -1.}; - if (candidate.isSelD0() < selectionFlagD0 || candidate.isSelD0bar() < selectionFlagD0) { - continue; - } - for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { - outputMlD0[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; - outputMlD0bar[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; - } - if (outputMlD0[0] > mlOutputBkgD0->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt())) || outputMlD0[1] < mlOutputPromptD0->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt()))) { + /// Reconstructed candidate loop + for (const auto& candidate : groupedCandidates) { + if (candidate.pt() < ptCandMin || candidate.pt() > ptCandMax) { + continue; + } + + const bool isD0 = candidate.isSelD0() >= selectionFlagD0; + const bool isD0bar = candidate.isSelD0bar() >= selectionFlagD0; + if (!isD0 && !isD0bar) { + continue; + } + if (rejectD0D0barHypothesis && isD0 && isD0bar) { + continue; + } + + std::vector outputMlD0 = {-1., -1., -1.}; + std::vector outputMlD0bar = {-1., -1., -1.}; + for (unsigned int iclass = 0; iclass < classMl->size(); iclass++) { + if (isD0) { + outputMlD0[iclass] = candidate.mlProbD0()[classMl->at(iclass)]; + } + if (isD0bar) { + outputMlD0bar[iclass] = candidate.mlProbD0bar()[classMl->at(iclass)]; + } + } + + const int ptBinMl = o2::analysis::findBin(binsPtMl, candidate.pt()); + if (ptBinMl < 0) { + continue; + } + + const bool passesD0Ml = isD0 && + outputMlD0[2] >= mlOutputPromptD0->at(ptBinMl) && + outputMlD0[0] <= mlOutputBkgD0->at(ptBinMl) && + outputMlD0[1] <= mlOutputNonPromptD0->at(ptBinMl); + + const bool passesD0barMl = isD0bar && + outputMlD0bar[2] >= mlOutputPromptD0bar->at(ptBinMl) && + outputMlD0bar[0] <= mlOutputBkgD0bar->at(ptBinMl) && + outputMlD0bar[1] <= mlOutputNonPromptD0bar->at(ptBinMl); + + if (!passesD0Ml && !passesD0barMl) { + continue; + } + + // Count only the truth-matched hypothesis that passes its own selection and ML cuts + const bool isD0Signal = + candidate.flagMcMatchRec() == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK && + passesD0Ml; + + const bool isD0barSignal = + candidate.flagMcMatchRec() == -o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK && + passesD0barMl; + + if (!(isD0Signal || isD0barSignal)) { + continue; + } + + hCandidates->Fill(kCandidateStepMcReco, candidate.pt(), activityRec, candidate.originMcRec()); + + if (std::abs(HfHelper::yD0(candidate)) > yCandMax) { + continue; + } + + hCandidates->Fill(kCandidateStepMcRecoInAcceptance, candidate.pt(), activityRec, candidate.originMcRec()); + + if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { + if (useCentrality) { + registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt(), centralityRec); + } else { + registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt()); + } + } else if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { + if (useCentrality) { + registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt(), centralityRec); + } else { + registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt()); + } + } + } // end reconstructed candidate loop + } // end reconstructed collision loop + } // end generated MC collision loop + } + PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcCandEfficiency, "Process MC for calculating candidate reconstruction efficiency", false); + + /// D0-Hadron correlation - associated-particle tracking efficiency. + /// With useCentrality=false: minimum-bias efficiency versus pT. + /// With useCentrality=true: pT x eta x z_vtx x reconstructed-centrality efficiency. + void processMcTrackEfficiency(CollisionsWithCent const& collisions, + McCollisionsWithMult const& mcCollisions, + aod::McParticles const& mcParticles, + TracksWithMc const& tracksData) + { + for (const auto& mcCollision : mcCollisions) { + const auto groupedCollisions = collisions.sliceBy(collPerCollMc, mcCollision.globalIndex()); + const auto groupedMcParticles = mcParticles.sliceBy(perCollisionTrackMc, mcCollision.globalIndex()); + + if (groupedCollisions.size() < 1) { continue; } - if (outputMlD0bar[0] > mlOutputBkgD0bar->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt())) || outputMlD0bar[1] < mlOutputPromptD0bar->at(o2::analysis::findBin(binsPtEfficiencyD, candidate.pt()))) { + if (groupedCollisions.size() > 1 && removeCollWSplitVtx) { continue; } - auto collision = candidate.template collision_as>(); - if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { - continue; + + float centralityMc = -1.f; + if (useCentrality) { + // Reconstructed-collision centrality assigned to the corresponding MC collision. + centralityMc = getCentralityGenColl(groupedCollisions, centEstimator); } - multiplicity = collision.multFT0M(); - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - hCandidates->Fill(kCandidateStepMcReco, candidate.pt(), multiplicity, candidate.originMcRec()); - if (std::abs(HfHelper::yD0(candidate)) <= yCandMax) { - hCandidates->Fill(kCandidateStepMcRecoInAcceptance, candidate.pt(), multiplicity, candidate.originMcRec()); - if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { - registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt()); + + for (const auto& collision : groupedCollisions) { + if (useSel8ForEff && !collision.sel8()) { + continue; + } + if (std::abs(collision.posZ()) > cutCollPosZMc) { + continue; + } + if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { + continue; + } + if (!collision.has_mcCollision()) { + registry.fill(HIST("hFakeCollisionTrackEff"), 0.); + continue; + } + + float centralityRec = -1.f; + if (useCentrality) { + centralityRec = getCentralityColl(collision, centEstimator); + if (centralityRec < centralityMin || centralityRec > centralityMax) { + continue; + } + } + + const auto groupedTracks = tracksData.sliceBy(perCollisionTrack, collision.globalIndex()); + + // MC-gen denominator. + for (const auto& mcParticle : groupedMcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; } - if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { - registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt()); + if (mcParticle.pt() <= ptTrackMin || mcParticle.pt() >= ptTrackMax || std::abs(mcParticle.eta()) >= etaTrackMax) { + continue; + } + + const int pdg = std::abs(mcParticle.pdgCode()); + if (pdg != kElectron && pdg != kMuonMinus && pdg != kPiPlus && pdg != kKPlus && pdg != kProton) { + continue; + } + + if (useCentrality) { + registry.fill(HIST("hPtParticleAssocMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); + } + } else { + registry.fill(HIST("hPtParticleAssocMcGen"), mcParticle.pt()); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcGen"), mcParticle.pt()); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcGen"), mcParticle.pt()); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcGen"), mcParticle.pt()); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcGen"), mcParticle.pt()); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcGen"), mcParticle.pt()); + } + } + } + + // MC-reco numerator. TracksWithMc already carries the configured pT/eta/DCA filter. + for (const auto& track : groupedTracks) { + if (!track.isGlobalTrackWoDCA() || track.tpcNClsCrossedRows() < nTpcCrossedRaws) { + continue; + } + if (!track.has_mcParticle()) { + registry.fill(HIST("hFakeTracksTrackEff"), 0.); + continue; + } + + const auto mcParticle = track.template mcParticle_as(); + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + + const int pdg = std::abs(mcParticle.pdgCode()); + if (pdg != kElectron && pdg != kMuonMinus && pdg != kPiPlus && pdg != kKPlus && pdg != kProton) { + continue; + } + + if (useCentrality) { + registry.fill(HIST("hPtParticleAssocMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); + } + } else { + registry.fill(HIST("hPtParticleAssocMcRec"), track.pt()); + if (pdg == kPiPlus) { + registry.fill(HIST("hPtPrmPionMcRec"), track.pt()); + } else if (pdg == kKPlus) { + registry.fill(HIST("hPtPrmKaonMcRec"), track.pt()); + } else if (pdg == kProton) { + registry.fill(HIST("hPtPrmProtonMcRec"), track.pt()); + } else if (pdg == kElectron) { + registry.fill(HIST("hPtPrmElectronMcRec"), track.pt()); + } else if (pdg == kMuonMinus) { + registry.fill(HIST("hPtPrmMuonMcRec"), track.pt()); + } } } } } } - PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcCandEfficiency, "Process MC for calculating candidate reconstruction efficiency", false); + PROCESS_SWITCH(HfTaskCorrelationD0Hadrons, processMcTrackEfficiency, "Process MC for calculating associated-particle tracking efficiency", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx index c37c1f73feb..a1746b6663a 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusDplusReduced.cxx @@ -30,6 +30,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; @@ -37,8 +38,13 @@ using namespace o2::framework::expressions; struct HfTaskCorrelationDplusDplusReduced { Configurable selectionFlagDplus{"selectionFlagDplus", 1, "Selection Flag for Dplus"}; + Configurable pseudorapidityMax{"pseudorapidityMax", 1, "Selection the cut of rapidity"}; + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable applySkimming{"applySkimming", false, "Flag to apply Skimming selections"}; - using SelectedCandidates = soa::Filtered; + using SelectedCandidatesTiny = soa::Filtered; + using SelectedCandidatesLite = soa::Filtered; + using SelectedCandidatesFull = soa::Filtered; using SelectedMcParticles = o2::aod::HfCandDpMcPs; Filter filterSelectCandidates = aod::full::candidateSelFlag >= selectionFlagDplus; @@ -54,24 +60,28 @@ struct HfTaskCorrelationDplusDplusReduced { void init(InitContext const&) { - registry.add("hMassDplus", "D+ candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDminus", "D- candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDplusMatched", "D+ matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDminusMatched", "D- matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); - registry.add("hMassDplusminusPair", "D plus-minus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); - registry.add("hMassDplusPair", "D plus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); - registry.add("hMassDminusPair", "D minus pair candidates;inv. mass (#pi K) (GeV/#it{c}^{2});inv. mass (#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplus", "D^{+} candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDminus", "D^{-} candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDplusMatched", "D^{+} matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDminusMatched", "D^{-} matched candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2}))", {HistType::kTH1F, {{120, 1.5848, 2.1848}}}); + registry.add("hMassDplusPairsMatched", "D^{+} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDminusPairsMatched", "D^{-} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusminusPairsMatched", "D^{+} and D^{-} matched pairs candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusminusPair", "D^{+-} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDplusPair", "D^{+} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); + registry.add("hMassDminusPair", "D^{-} pair candidates;inv. mass (#pi#pi K) (GeV/#it{c}^{2});inv. mass (#pi#pi K) (GeV/#it{c}^{2})", {HistType::kTH2F, {{120, 1.5848, 2.1848}, {120, 1.5848, 2.1848}}}); registry.add("hDltPhiMcGen", "Azimuthal correlation for D mesons; #Delta#phi", {HistType::kTH1F, {{100, -3.141593, 3.141593}}}); } - void processLocalData(o2::aod::HfCandDpFullEvs::iterator const&, - SelectedCandidates const& localCandidates) + template + void processData(const T& localCandidates) { registry.fill(HIST("hNCand"), localCandidates.size()); for (const auto& cand1 : localCandidates) { auto mass1 = cand1.m(); auto sign1 = 1; + auto flattening = 0; if (cand1.pt() < 0) { sign1 = -1; registry.fill(HIST("hMassDminus"), mass1); @@ -85,6 +95,12 @@ struct HfTaskCorrelationDplusDplusReduced { if (cand2.pt() < 0) { sign2 = -1; } + if (flattening == 0) { + std::swap(mass1, mass2); + flattening = 1; + } else if (flattening == 1) { + flattening = 0; + } if (sign1 == sign2) { if (sign1 == 1) { registry.fill(HIST("hMassDplusPair"), mass2, mass1); @@ -97,27 +113,104 @@ struct HfTaskCorrelationDplusDplusReduced { } } } - PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalData, "Process local data", true); - void processLocalDataMcRec(o2::aod::HfCandDpFullEvs::iterator const&, - SelectedCandidates const& localCandidates) + template + void processLocalDataMcRec(const T& localCandidates) { registry.fill(HIST("hNMcRec"), localCandidates.size()); for (const auto& cand1 : localCandidates) { auto mass1 = cand1.m(); + auto sign1 = 1; + auto flattening = 0; if (cand1.pt() < 0) { + sign1 = -1; registry.fill(HIST("hMassDminus"), mass1); - if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) + if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { registry.fill(HIST("hMassDminusMatched"), mass1); + } } else { registry.fill(HIST("hMassDplus"), mass1); - if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) + if (std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { registry.fill(HIST("hMassDplusMatched"), mass1); + } + } + + for (auto cand2 = cand1 + 1; cand2 != localCandidates.end(); ++cand2) { + auto mass2 = cand2.m(); + auto sign2 = 1; + if (cand2.pt() < 0) { + sign2 = -1; + } + if (flattening == 0) { + std::swap(mass1, mass2); + flattening = 1; + } else if (flattening == 1) { + flattening = 0; + } + if (sign1 == sign2) { + if (sign1 == 1) { + registry.fill(HIST("hMassDplusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDplusPairsMatched"), mass1, mass2); + } + } else { + registry.fill(HIST("hMassDminusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDminusPairsMatched"), mass1, mass2); + } + } + } else { + registry.fill(HIST("hMassDplusminusPair"), mass1, mass2); + if (std::abs(cand2.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi && std::abs(cand1.flagMcMatchRec()) == hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + registry.fill(HIST("hMassDplusminusPairsMatched"), mass1, mass2); + } + } } } } - PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRec, "Process local MC data", false); + + void processLocalDataTiny(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesTiny const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataTiny, "Process local data for Tiny", false); + + void processLocalDataLite(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesLite const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataLite, "Process local data for Lite", false); + + void processLocalDataFull(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesFull const& localCandidates) + { + processData(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataFull, "Process local data for Full", true); + + void processLocalDataMcRecTiny(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesTiny const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecTiny, "Process local MC data for Tiny", false); + + void processLocalDataMcRecLite(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesLite const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecLite, "Process local MC data for Lite", false); + + void processLocalDataMcRecFull(o2::aod::HfCandDpFullEvs::iterator const&, + SelectedCandidatesFull const& localCandidates) + { + processLocalDataMcRec(localCandidates); + } + PROCESS_SWITCH(HfTaskCorrelationDplusDplusReduced, processLocalDataMcRecFull, "Process local MC data for Full", false); void processLocalDataMcGen(o2::aod::HfCandDpMcEvs::iterator const&, SelectedMcParticles const& localMcParticles) @@ -126,7 +219,9 @@ struct HfTaskCorrelationDplusDplusReduced { for (const auto& part1 : localMcParticles) { for (auto part2 = part1 + 1; part2 != localMcParticles.end(); ++part2) { - registry.fill(HIST("hDltPhiMcGen"), part2.phi() - part1.phi()); + if (std::abs(part1.eta()) < pseudorapidityMax && std::abs(part2.eta()) < pseudorapidityMax) { + registry.fill(HIST("hDltPhiMcGen"), part2.phi() - part1.phi()); + } } } } diff --git a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx index 1b412d170e9..3a058914624 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDplusHadrons.cxx @@ -140,6 +140,9 @@ struct HfTaskCorrelationDplusHadrons { Configurable activateQA{"activateQA", false, "Flag to enable debug histogram"}; Configurable nTpcCrossedRaws{"nTpcCrossedRaws", 70, "Number of crossed TPC Rows"}; Configurable cutCollPosZMc{"cutCollPosZMc", 10., "max z-vertex position for collision acceptance"}; + Configurable storeMassAxis{"storeMassAxis", false, "Store Mass axis in THnSparse"}; + Configurable storeBMotherInfo{"storeBMotherInfo", false, "Store generated pT(B mother) and phi(D+) - phi(B mother) of non-prompt D+ in MC Rec histograms"}; + Configurable> binsPtBMother{"binsPtBMother", std::vector{0., 2., 4., 6., 8., 12., 16., 24., 36., 50., 99.}, "pT bin limits for the B mother of non-prompt D+"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable associatedEffCcdbPath{"associatedEffCcdbPath", "", "CCDB path for associated efficiency"}; @@ -163,8 +166,9 @@ struct HfTaskCorrelationDplusHadrons { kCandidateStepMcRecoInAcceptance, kCandidateNSteps }; - using DplusHadronPair = soa::Join; + using DplusHadronPairWithBMother = soa::Join; using DplusHadronPairFullWithMl = soa::Join; + using DplusHadronPairFullWithMlAndBMother = soa::Join; using CandDplusMcReco = soa::Filtered>; using CandDplusMcGen = soa::Join; using TracksWithMc = soa::Filtered>; // trackFilter applied @@ -178,6 +182,7 @@ struct HfTaskCorrelationDplusHadrons { ConfigurableAxis binsPhi{"binsPhi", {64, -PIHalf, 3. * PIHalf}, "#it{#varphi}"}; ConfigurableAxis binsMultFT0M{"binsMultFT0M", {600, 0., 8000.}, "Multiplicity as FT0M signal amplitude"}; ConfigurableAxis binsPoolBin{"binsPoolBin", {9, 0., 9.}, "PoolBin"}; + ConfigurableAxis binsDeltaPhiBMother{"binsDeltaPhiBMother", {64, -PIHalf, 3. * PIHalf}, "#it{#varphi}^{D}-#it{#varphi}^{B}"}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -185,15 +190,17 @@ struct HfTaskCorrelationDplusHadrons { { // Axis definition AxisSpec axisMassD = {binsMassD, "inv. mass (#pi^{+}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; - AxisSpec axisPtCorr = {(std::vector)binsPtCorrelations, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisPtD = {(std::vector)binsPtEfficiencyD, "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtCorr = {std::vector(binsPtCorrelations), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtEfficiencyD), "#it{p}_{T}^{D} (GeV/#it{c})"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec axisDplusPrompt = {2, -0.5, 1.5, "Prompt D+"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; + AxisSpec axisPtBMother = {std::vector(binsPtBMother), "#it{p}_{T}^{B} (GeV/#it{c})"}; + AxisSpec axisDeltaPhiBMother = {binsDeltaPhiBMother, "#it{#varphi}^{D}-#it{#varphi}^{B} (rad)"}; // Histograms for data analysis registry.add("hBdtScorePrompt", "D+ BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); @@ -201,25 +208,30 @@ struct HfTaskCorrelationDplusHadrons { registry.add("hMassDplusVsPt", "D+ candidates massVsPt", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); registry.add("hMassDplusVsPtWoEff", "D+ candidates massVsPt without efficiency", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); if (fillHistoData) { - registry.add("hDeltaEtaPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); - registry.add("hDeltaPhiPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); - registry.add("hCorrel2DPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hDeltaEtaPtIntSidebands", stringDHadron + stringSideband + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); - registry.add("hDeltaPhiPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); - registry.add("hCorrel2DPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); - registry.add("hCorrel2DVsPtSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hDeltaEtaPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); - registry.add("hDeltaPhiPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaPhi, {HistType::kTH1F, {axisDeltaPhi}}); - registry.add("hDeltaEtaPtIntSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); - registry.add("hDeltaPhiPtIntSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaPhi, {HistType::kTH1F, {axisDeltaPhi}}); - registry.add("hCorrel2DVsPtSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); - - registry.get(HIST("hCorrel2DVsPtSignalRegion"))->Sumw2(); - registry.get(HIST("hCorrel2DVsPtSidebands"))->Sumw2(); - registry.get(HIST("hCorrel2DVsPtSidebandLeft"))->Sumw2(); - registry.get(HIST("hCorrel2DVsPtSidebandRight"))->Sumw2(); + if (storeMassAxis) { + registry.add("hCorrel2DVsPtVsMass", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}, {axisMassD}}}); + registry.get(HIST("hCorrel2DVsPtVsMass"))->Sumw2(); + } else { + registry.add("hDeltaEtaPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); + registry.add("hDeltaPhiPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); + registry.add("hCorrel2DPtIntSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); + registry.add("hCorrel2DVsPtSignalRegion", stringDHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hDeltaEtaPtIntSidebands", stringDHadron + stringSideband + stringDeltaEta + "entries", {HistType::kTH1F, {axisDeltaEta}}); + registry.add("hDeltaPhiPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + "entries", {HistType::kTH1F, {axisDeltaPhi}}); + registry.add("hCorrel2DPtIntSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); + registry.add("hCorrel2DVsPtSidebands", stringDHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hDeltaEtaPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); + registry.add("hDeltaPhiPtIntSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaPhi, {HistType::kTH1F, {axisDeltaPhi}}); + registry.add("hDeltaEtaPtIntSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaEta, {HistType::kTH1F, {axisDeltaEta}}); + registry.add("hDeltaPhiPtIntSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaPhi, {HistType::kTH1F, {axisDeltaPhi}}); + registry.add("hCorrel2DVsPtSidebandLeft", stringDHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSidebandRight", stringDHadron + "Right" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtD + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + + registry.get(HIST("hCorrel2DVsPtSignalRegion"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtSidebands"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtSidebandLeft"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtSidebandRight"))->Sumw2(); + } } // Histograms for MC Reco analysis if (fillHistoMcRec) { @@ -253,6 +265,17 @@ struct HfTaskCorrelationDplusHadrons { registry.get(HIST("hCorrel2DVsPtSidebandRightMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSignalRegionMcRec"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtPhysicalPrimaryMcRec"))->Sumw2(); + + // beauty-mother information of non-prompt D+ (generated B, reconstructed D+) + if (storeBMotherInfo) { + registry.add("hMassNonPromptDplusVsPtVsPtBMother", "D+ non prompt candidates mass Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisMassD}, {axisPtD}, {axisPtBMother}}}); + registry.add("hDeltaPhiBMotherVsPtVsPtBMother", "D+ non prompt candidates #varphi^{D}-#varphi^{B} Vs Pt Vs Pt B mother", {HistType::kTH3F, {{axisDeltaPhiBMother}, {axisPtD}, {axisPtBMother}}}); + registry.add("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother", stringDHadron + "non-prompt D (only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + registry.add("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother", stringDHadron + "signal region NonPromptD - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + + registry.get(HIST("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother"))->Sumw2(); + } } // Histograms for MC Gen analysis if (fillHistoMcGen) { @@ -268,6 +291,15 @@ struct HfTaskCorrelationDplusHadrons { registry.get(HIST("hCorrel2DVsPtMcGen"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtMcGenPrompt"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtMcGenNonPrompt"))->Sumw2(); + + // beauty-mother information of non-prompt D+, same axes as the MC Rec counterparts to allow Rec/Gen ratios + if (storeBMotherInfo) { + registry.add("hCorrel2DVsPtMcGenNonPromptBMother", stringDHadron + " NonPrompt MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + registry.add("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother", stringDHadron + " non prompt D non prompt h MC Gen", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtD}, {axisPtHadron}, {axisPtBMother}, {axisDeltaPhiBMother}}}); + + registry.get(HIST("hCorrel2DVsPtMcGenNonPromptBMother"))->Sumw2(); + registry.get(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother"))->Sumw2(); + } } // Histograms for efficiencies registry.add("Efficiency/hPtCandMcRecPrompt", stringMCRecoDPrompt + stringPtD, {HistType::kTH1F, {axisPtD}}); @@ -306,14 +338,14 @@ struct HfTaskCorrelationDplusHadrons { } LOGF(info, "Loaded associated efficiency histogram from %s", associatedEffCcdbPath.value.c_str()); } - auto effD = isPromptAnalysis ? mEfficiencyPrompt : mEfficiencyFD; + effD = isPromptAnalysis ? mEfficiencyPrompt : mEfficiencyFD; idxBdtScore = isPromptAnalysis ? 1 : 2; if (activateQA) { const int regionLimits = 6; std::string labels[regionLimits] = {"SigReg Left", "SigReg Right", "Left SB Low", "Left SB Up", "Right SB Low", "Right SB Up"}; static const AxisSpec axisSidebandLimits = {regionLimits, 0.5, 6.5, ""}; - auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {(std::vector)binsPtCorrelations, "#it{p}_{T} (GeV/#it{c})"}}}); + auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {std::vector(binsPtCorrelations), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iLim = 0; iLim < regionLimits; iLim++) { hSigSidebandLimits->GetXaxis()->SetBinLabel(iLim + 1, labels[iLim].data()); } @@ -396,6 +428,10 @@ struct HfTaskCorrelationDplusHadrons { efficiencyWeight = 1. / (effD->GetBinContent(effD->FindBin(ptD)) * mEfficiencyAssociated->GetBinContent(mEfficiencyAssociated->FindBin(ptHadron))); } } + if (storeMassAxis) { + registry.fill(HIST("hCorrel2DVsPtVsMass"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, massD, efficiencyWeight); + continue; + } // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots if (massD > signalRegionInner->at(pTBinD) && massD < signalRegionOuter->at(pTBinD)) { // in signal region @@ -429,8 +465,8 @@ struct HfTaskCorrelationDplusHadrons { PROCESS_SWITCH(HfTaskCorrelationDplusHadrons, processData, "Process data", false); /// D-Hadron correlation pair filling task, from pair tables - for MC reco-level analysis (candidates matched to true signal only, but also bkg sources are studied) - void processMcRec(DplusHadronPairFullWithMl const& pairEntries, - soa::Join const& candidates) + void processMcRec(DplusHadronPairFullWithMlAndBMother const& pairEntries, + soa::Join const& candidates) { for (const auto& candidate : candidates) { float const massD = candidate.mD(); @@ -472,6 +508,10 @@ struct HfTaskCorrelationDplusHadrons { registry.fill(HIST("hMassNonPromptDplusVsPt"), massD, ptD, efficiencyWeightD); registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); + if (storeBMotherInfo) { + registry.fill(HIST("hMassNonPromptDplusVsPtVsPtBMother"), massD, ptD, candidate.ptB(), efficiencyWeightD); + registry.fill(HIST("hDeltaPhiBMotherVsPtVsPtBMother"), candidate.deltaPhiBtoD(), ptD, candidate.ptB(), efficiencyWeightD); + } } } } @@ -546,6 +586,15 @@ struct HfTaskCorrelationDplusHadrons { } else if (!isDplusPrompt && originHadron == RecoDecay::OriginType::NonPrompt) { registry.fill(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRec"), deltaPhi, deltaEta, ptD, ptHadron, poolBin, efficiencyWeight); } + // beauty-mother information, only for true non-prompt D+, to study the structures in the MC Closure test + if (storeBMotherInfo && pairEntry.signalStatus() && !isDplusPrompt) { + float const ptB = pairEntry.ptB(); + float const deltaPhiBtoD = pairEntry.deltaPhiBtoD(); + registry.fill(HIST("hCorrel2DVsPtNonPromptDplusPhysicalPrimaryMcRecBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD, efficiencyWeight); + if (originHadron == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCorrel2DVsPtSignalRegionNonPromptDplusNonPromptHadronMcRecBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD, efficiencyWeight); + } + } } } // in sideband left region @@ -573,7 +622,7 @@ struct HfTaskCorrelationDplusHadrons { PROCESS_SWITCH(HfTaskCorrelationDplusHadrons, processMcRec, "Process MC Reco mode", true); /// D-Hadron correlation pair filling task, from pair tables - for MC gen-level analysis (no filter/selection, only true signal) - void processMcGen(DplusHadronPair const& pairEntries) + void processMcGen(DplusHadronPairWithBMother const& pairEntries) { for (const auto& pairEntry : pairEntries) { // define variables for widely used quantities @@ -598,6 +647,15 @@ struct HfTaskCorrelationDplusHadrons { if (originHadron == RecoDecay::OriginType::NonPrompt) { registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadron"), deltaPhi, deltaEta, ptD, ptHadron, poolBin); } + // beauty-mother information + if (storeBMotherInfo) { + float const ptB = pairEntry.ptB(); + float const deltaPhiBtoD = pairEntry.deltaPhiBtoD(); + registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD); + if (originHadron == RecoDecay::OriginType::NonPrompt) { + registry.fill(HIST("hCorrel2DVsPtMcGenNonPromptDNonPromptHadronBMother"), deltaPhi, deltaEta, ptD, ptHadron, ptB, deltaPhiBtoD); + } + } } } } diff --git a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx index 622c5a7a037..0df60e1e5de 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDsHadrons.cxx @@ -128,6 +128,7 @@ struct HfTaskCorrelationDsHadrons { Configurable> pidTOFMax{"pidTOFMax", std::vector{3., 0., 0.}, "maximum nSigma TOF"}; Configurable> binsPtD{"binsPtD", std::vector{o2::analysis::hf_cuts_ds_to_k_k_pi::vecBinsPt}, "pT bin limits for candidate mass plots and efficiency"}; Configurable> binsPtHadron{"binsPtHadron", std::vector{0.3, 2., 4., 8., 12., 50.}, "pT bin limits for assoc particle efficiency"}; + Configurable> binsCentrality{"binsCentrality", std::vector{0., 10., 30., 100.}, "Centrality bin limits"}; Configurable> mlOutputPromptMin{"mlOutputPromptMin", {0.5, 0.5, 0.5, 0.5}, "Machine learning scores for prompt"}; Configurable> mlOutputPromptMax{"mlOutputPromptMax", {1.0, 1.0, 1.0, 1.0}, "Machine learning scores for prompt"}; Configurable> mlOutputBkg{"mlOutputBkg", {0.5, 0.5, 0.5, 0.5}, "Machine learning scores for bkg"}; @@ -176,7 +177,7 @@ struct HfTaskCorrelationDsHadrons { Preslice perCollisionCandMc = o2::aod::mcparticle::mcCollisionId; Preslice perCollision = o2::aod::track::collisionId; Preslice perCollisionMc = o2::aod::mcparticle::mcCollisionId; - PresliceUnsorted> collPerCollMc = o2::aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted> collPerCollMc = o2::aod::mccollisionlabel::mcCollisionId; ConfigurableAxis binsMassD{"binsMassD", {200, 1.7, 2.25}, "inv. mass (K^{#pm}K^{-}#pi^{+}) (GeV/#it{c}^{2})"}; ConfigurableAxis binsBdtScore{"binsBdtScore", {100, 0., 1.}, "Bdt output scores"}; @@ -195,8 +196,9 @@ struct HfTaskCorrelationDsHadrons { AxisSpec axisDetlaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{D}"}; AxisSpec axisEta = {binsEta, "#it{#eta}"}; AxisSpec axisDetlaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{D} (rad)"}; - AxisSpec axisPtD = {(std::vector)binsPtD, "#it{p}_{T}^{D} (GeV/#it{c})"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtD = {std::vector(binsPtD), "#it{p}_{T}^{D} (GeV/#it{c})"}; + AxisSpec axisCentrality = {std::vector(binsCentrality), "centrality FT0M (%)"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; @@ -207,7 +209,7 @@ struct HfTaskCorrelationDsHadrons { // Histograms for data analysis registry.add("hBdtScorePrompt", "Ds BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); registry.add("hBdtScoreBkg", "Ds BDT bkg score", {HistType::kTH1F, {axisBdtScore}}); - registry.add("hMassDsVsPt", "Ds candidates massVsPt", {HistType::kTH2F, {{axisMassD}, {axisPtD}}}); + registry.add("hMassDsVsPt", "Ds candidates massVsPt", {HistType::kTH3F, {{axisMassD}, {axisPtD}, {axisCentrality}}}); if (doLSpair) { registry.add("hCorrel2DVsPtSignalRegionLS", "Ds-h correlations signal region - LS pairs", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSidebandLeftLS", "Ds-h correlations sideband left region - LS pairs", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); @@ -229,13 +231,13 @@ struct HfTaskCorrelationDsHadrons { if (fillHistoData) { registry.add("hDeltaEtaPtIntSignalRegion", "Ds-h deltaEta signal region", {HistType::kTH1F, {axisDetlaEta}}); registry.add("hDeltaPhiPtIntSignalRegion", "Ds-h deltaPhi signal region", {HistType::kTH1F, {axisDetlaPhi}}); - registry.add("hCorrel2DVsPtSignalRegion", "Ds-h correlations signal region", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalRegion", "Ds-h correlations signal region", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}, {axisCentrality}}}); registry.add("hDeltaEtaPtIntSidebandLeft", "Ds-h deltaEta sideband left region", {HistType::kTH1F, {axisDetlaEta}}); registry.add("hDeltaPhiPtIntSidebandLeft", "Ds-h deltaPhi sideband left region", {HistType::kTH1F, {axisDetlaPhi}}); registry.add("hDeltaEtaPtIntSidebandRight", "Ds-h deltaEta sideband right region", {HistType::kTH1F, {axisDetlaEta}}); registry.add("hDeltaPhiPtIntSidebandRight", "Ds-h deltaPhi sideband right region", {HistType::kTH1F, {axisDetlaPhi}}); - registry.add("hCorrel2DVsPtSidebandLeft", "Ds-h correlations sideband left region", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSidebandRight", "Ds-h correlations sideband right region", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSidebandLeft", "Ds-h correlations sideband left region", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}, {axisCentrality}}}); + registry.add("hCorrel2DVsPtSidebandRight", "Ds-h correlations sideband right region", {HistType::kTHnSparseD, {{axisDetlaPhi}, {axisDetlaEta}, {axisPtD}, {axisPtHadron}, {axisPoolBin}, {axisCentrality}}}); registry.get(HIST("hCorrel2DVsPtSignalRegion"))->Sumw2(); registry.get(HIST("hCorrel2DVsPtSidebandLeft"))->Sumw2(); @@ -304,22 +306,22 @@ struct HfTaskCorrelationDsHadrons { registry.add("hPtCandMcGenDaughterInAcc", "Ds,Hadron particles non prompt - MC Gen", {HistType::kTH1F, {axisPtD}}); if (useHighDimHistoForEff) { - registry.add("hPtCandMcRecPrompt", "Ds prompt candidates", {HistType::kTH2F, {{axisPtD}, {axisNumPvContr}}}); - registry.add("hPtCandMcRecNonPrompt", "Ds non prompt candidates pt", {HistType::kTH2F, {{axisPtD}, {axisNumPvContr}}}); - registry.add("hPtCandMcGenPrompt", "Ds,Hadron particles prompt - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisNumPvContr}}}); - registry.add("hPtCandMcGenNonPrompt", "Ds,Hadron particles non prompt - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisNumPvContr}}}); + registry.add("hPtCandMcRecPrompt", "Ds prompt candidates", {HistType::kTH2F, {{axisPtD}, {axisCentrality}}}); + registry.add("hPtCandMcRecNonPrompt", "Ds non prompt candidates pt", {HistType::kTH2F, {{axisPtD}, {axisCentrality}}}); + registry.add("hPtCandMcGenPrompt", "Ds,Hadron particles prompt - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisCentrality}}}); + registry.add("hPtCandMcGenNonPrompt", "Ds,Hadron particles non prompt - MC Gen", {HistType::kTH2F, {{axisPtD}, {axisCentrality}}}); registry.add("hPtParticleAssocSpecieMcRec", "Associated Particle - MC Rec", {HistType::kTHnSparseF, {axisPtHadron}}); - registry.add("hPtPrmPionMcRec", "Primary pions - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmKaonMcRec", "Primary kaons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmProtonMcRec", "Primary protons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmElectronMcRec", "Primary electrons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmMuonMcRec", "Primary muons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtParticleAssocMcGen", "Associated Particle - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmPionMcGen", "Primary pions - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmKaonMcGen", "Primary kaons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmProtonMcGen", "Primary protons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmElectronMcGen", "Primary electrons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); - registry.add("hPtPrmMuonMcGen", "Primary muons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisNumPvContr}}}); + registry.add("hPtPrmPionMcRec", "Primary pions - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmKaonMcRec", "Primary kaons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmProtonMcRec", "Primary protons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmElectronMcRec", "Primary electrons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmMuonMcRec", "Primary muons - MC Rec", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtParticleAssocMcGen", "Associated Particle - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmPionMcGen", "Primary pions - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmKaonMcGen", "Primary kaons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmProtonMcGen", "Primary protons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmElectronMcGen", "Primary electrons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); + registry.add("hPtPrmMuonMcGen", "Primary muons - MC Gen", {HistType::kTHnSparseF, {{axisPtHadron}, {axisEta}, {axisPosZ}, {axisCentrality}}}); } else { registry.add("hPtCandMcRecPrompt", "Ds prompt candidates pt", {HistType::kTH1F, {axisPtD}}); registry.add("hPtCandMcRecNonPrompt", "Ds non prompt candidates pt", {HistType::kTH1F, {axisPtD}}); @@ -487,6 +489,7 @@ struct HfTaskCorrelationDsHadrons { aod::DsCandRecoInfo const& candidates) { for (const auto& candidate : candidates) { + float const centrality = candidate.centrality(); float const massD = candidate.mD(); float const ptD = candidate.signedPtD(); float const bdtScorePrompt = candidate.mlScorePrompt(); @@ -505,13 +508,14 @@ struct HfTaskCorrelationDsHadrons { efficiencyWeightD = getEfficiencyWeight(std::abs(ptD)); } - registry.fill(HIST("hMassDsVsPt"), massD, std::abs(ptD), efficiencyWeightD); + registry.fill(HIST("hMassDsVsPt"), massD, std::abs(ptD), centrality, efficiencyWeightD); registry.fill(HIST("hBdtScorePrompt"), bdtScorePrompt); registry.fill(HIST("hBdtScoreBkg"), bdtScoreBkg); } for (const auto& pairEntry : pairEntries) { // define variables for widely used quantities + float const centrality = pairEntry.centrality(); float deltaPhi = pairEntry.deltaPhi(); float const deltaEta = pairEntry.deltaEta(); float const ptD = pairEntry.signedPtD(); @@ -549,7 +553,7 @@ struct HfTaskCorrelationDsHadrons { } else if (doULSpair && !haveSameSign) { // unlike-sign pairs registry.fill(HIST("hCorrel2DVsPtSignalRegionULS"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, efficiencyWeight); } else if (fillHistoData) { // default case - registry.fill(HIST("hCorrel2DVsPtSignalRegion"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegion"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, centrality, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegion"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegion"), deltaPhi, efficiencyWeight); } @@ -561,7 +565,7 @@ struct HfTaskCorrelationDsHadrons { } else if (doULSpair && !haveSameSign) { // unlike-sign pairs registry.fill(HIST("hCorrel2DVsPtSidebandLeftULS"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, efficiencyWeight); } else if (fillHistoData) { // default case - registry.fill(HIST("hCorrel2DVsPtSidebandLeft"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSidebandLeft"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, centrality, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSidebandLeft"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSidebandLeft"), deltaPhi, efficiencyWeight); } @@ -573,7 +577,7 @@ struct HfTaskCorrelationDsHadrons { } else if (doULSpair && !haveSameSign) { // unlike-sign pairs registry.fill(HIST("hCorrel2DVsPtSidebandRightULS"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, efficiencyWeight); } else if (fillHistoData) { // default case - registry.fill(HIST("hCorrel2DVsPtSidebandRight"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSidebandRight"), deltaPhi, deltaEta, std::abs(ptD), std::abs(ptHadron), poolBin, centrality, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSidebandRight"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSidebandRight"), deltaPhi, efficiencyWeight); } @@ -948,7 +952,7 @@ struct HfTaskCorrelationDsHadrons { PROCESS_SWITCH(HfTaskCorrelationDsHadrons, processMcRecME, "Process MC Reco ME", false); /// Ds-Hadron correlation - for calculating candidate reconstruction efficiency using MC reco-level analysis - void processMcCandEfficiency(soa::Join const& collisions, + void processMcCandEfficiency(soa::Join const& collisions, soa::Join const& mcCollisions, CandDsMcGen const& mcParticles, CandDsMcReco const& candidates, @@ -967,6 +971,8 @@ struct HfTaskCorrelationDsHadrons { continue; } + const float centralityMc = getCentralityGenColl(groupedCollisions, centEstimator); + /// loop over reconstructed collisions for (const auto& collision : groupedCollisions) { @@ -980,6 +986,11 @@ struct HfTaskCorrelationDsHadrons { if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { continue; } + const float centralityRec = getCentralityColl(collision, centEstimator); + if (centralityRec < centralityMin || centralityRec > centralityMax) { + continue; + } + if (!collision.has_mcCollision()) { registry.fill(HIST("hFakeCollision"), 0.); continue; @@ -996,14 +1007,14 @@ struct HfTaskCorrelationDsHadrons { if (std::abs(yDs) <= yCandGenMax) { if (mcParticle.originMcGen() == RecoDecay::OriginType::Prompt) { if (useHighDimHistoForEff) { - registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt(), collision.numContrib()); + registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt(), centralityMc); } else { registry.fill(HIST("hPtCandMcGenPrompt"), mcParticle.pt()); } } if (mcParticle.originMcGen() == RecoDecay::OriginType::NonPrompt) { if (useHighDimHistoForEff) { - registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt(), collision.numContrib()); + registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt(), centralityMc); } else { registry.fill(HIST("hPtCandMcGenNonPrompt"), mcParticle.pt()); } @@ -1049,14 +1060,14 @@ struct HfTaskCorrelationDsHadrons { if (std::abs(HfHelper::yDs(candidate)) <= yCandMax) { if (candidate.originMcRec() == RecoDecay::OriginType::Prompt) { if (useHighDimHistoForEff) { - registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt(), collision.numContrib()); + registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt(), centralityRec); } else { registry.fill(HIST("hPtCandMcRecPrompt"), candidate.pt()); } } if (candidate.originMcRec() == RecoDecay::OriginType::NonPrompt) { if (useHighDimHistoForEff) { - registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt(), collision.numContrib()); + registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt(), centralityRec); } else { registry.fill(HIST("hPtCandMcRecNonPrompt"), candidate.pt()); } @@ -1162,6 +1173,8 @@ struct HfTaskCorrelationDsHadrons { continue; } + const float centralityMc = getCentralityGenColl(groupedCollisions, centEstimator); + /// loop over reconstructed collisions for (const auto& collision : groupedCollisions) { @@ -1175,8 +1188,8 @@ struct HfTaskCorrelationDsHadrons { if (selNoSameBunchPileUpColl && !(collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup))) { continue; } - int centrality = getCentrality(collision); - if (centrality < centralityMin || centrality > centralityMax) { + const float centralityRec = getCentralityColl(collision, centEstimator); + if (centralityRec < centralityMin || centralityRec > centralityMax) { continue; } if (!collision.has_mcCollision()) { @@ -1192,17 +1205,17 @@ struct HfTaskCorrelationDsHadrons { if (mcParticle.pt() > ptTrackMin && mcParticle.pt() < ptTrackMax) { if (std::abs(mcParticle.eta()) < etaTrackMax) { if (useHighDimHistoForEff) { - registry.fill(HIST("hPtParticleAssocMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtParticleAssocMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); if (std::abs(mcParticle.pdgCode()) == kPiPlus) { - registry.fill(HIST("hPtPrmPionMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmPionMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); } else if (std::abs(mcParticle.pdgCode()) == kKPlus) { - registry.fill(HIST("hPtPrmKaonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmKaonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); } else if (std::abs(mcParticle.pdgCode()) == kProton) { - registry.fill(HIST("hPtPrmProtonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmProtonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); } else if (std::abs(mcParticle.pdgCode()) == kElectron) { - registry.fill(HIST("hPtPrmElectronMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmElectronMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); } else if (std::abs(mcParticle.pdgCode()) == kMuonMinus) { - registry.fill(HIST("hPtPrmMuonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmMuonMcGen"), mcParticle.pt(), mcParticle.eta(), collision.posZ(), centralityMc); } } else { registry.fill(HIST("hPtParticleAssocMcGen"), mcParticle.pt()); @@ -1251,15 +1264,15 @@ struct HfTaskCorrelationDsHadrons { if (useHighDimHistoForEff) { registry.fill(HIST("hPtParticleAssocSpecieMcRec"), track.pt()); if (std::abs(mcParticle.pdgCode()) == kPiPlus) { - registry.fill(HIST("hPtPrmPionMcRec"), track.pt(), track.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmPionMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); } else if (std::abs(mcParticle.pdgCode()) == kKPlus) { - registry.fill(HIST("hPtPrmKaonMcRec"), track.pt(), track.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmKaonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); } else if (std::abs(mcParticle.pdgCode()) == kProton) { - registry.fill(HIST("hPtPrmProtonMcRec"), track.pt(), track.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmProtonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); } else if (std::abs(mcParticle.pdgCode()) == kElectron) { - registry.fill(HIST("hPtPrmElectronMcRec"), track.pt(), track.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmElectronMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); } else if (std::abs(mcParticle.pdgCode()) == kMuonMinus) { - registry.fill(HIST("hPtPrmMuonMcRec"), track.pt(), track.eta(), collision.posZ(), collision.numContrib()); + registry.fill(HIST("hPtPrmMuonMcRec"), track.pt(), track.eta(), collision.posZ(), centralityRec); } } else { registry.fill(HIST("hPtParticleAssocSpecieMcRec"), track.pt()); diff --git a/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx index e14bc59a504..a8c1004df3e 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationDstarHadrons.cxx @@ -124,7 +124,7 @@ struct HfTaskCorrelationDstarHadrons { void init(InitContext&) { - auto axisPtDstar = (std::vector)binsPtEfficiency; + auto axisPtDstar = std::vector(binsPtEfficiency); AxisSpec const axisSpecPtDstar = {axisPtDstar}; AxisSpec const axisSpecDeltaPhi = {nBinsDeltaPhi, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}; AxisSpec const axisSpecDeltaEta = {deltaEtaBinEdges}; diff --git a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx index ad6156782d2..3261cad3f5b 100644 --- a/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx +++ b/PWGHF/HFC/Tasks/taskCorrelationLcHadrons.cxx @@ -52,6 +52,7 @@ #include #include +#include #include #include #include @@ -87,20 +88,37 @@ const TString stringMcGenLcFd = "MC gen, non-prompt #Lambda_c;"; // definition of vectors for standard ptbin and invariant mass configurables const int nPtBinsCorrelations = 8; -const double pTBinsCorrelations[nPtBinsCorrelations + 1] = {0., 2., 4., 6., 8., 12., 16., 24., 99.}; -const auto vecBinsPtCorrelations = std::vector{pTBinsCorrelations, pTBinsCorrelations + nPtBinsCorrelations + 1}; -const double signalRegionInnerDefault[nPtBinsCorrelations] = {2.269, 2.269, 2.269, 2.269, 2.269, 2.269, 2.269, 2.269}; -const double signalRegionOuterDefault[nPtBinsCorrelations] = {2.309, 2.309, 2.309, 2.309, 2.309, 2.309, 2.309, 2.309}; -const double sidebandLeftOuterDefault[nPtBinsCorrelations] = {2.209, 2.209, 2.209, 2.209, 2.209, 2.209, 2.209, 2.209}; -const double sidebandLeftInnerDefault[nPtBinsCorrelations] = {2.249, 2.249, 2.249, 2.249, 2.249, 2.249, 2.249, 2.249}; -const double sidebandRightInnerDefault[nPtBinsCorrelations] = {2.329, 2.329, 2.329, 2.329, 2.329, 2.329, 2.329, 2.329}; -const double sidebandRightOuterDefault[nPtBinsCorrelations] = {2.369, 2.369, 2.369, 2.369, 2.369, 2.369, 2.369, 2.369}; -const auto vecSignalRegionInner = std::vector{signalRegionInnerDefault, signalRegionInnerDefault + nPtBinsCorrelations}; -const auto vecSignalRegionOuter = std::vector{signalRegionOuterDefault, signalRegionOuterDefault + nPtBinsCorrelations}; -const auto vecSidebandLeftInner = std::vector{sidebandLeftInnerDefault, sidebandLeftInnerDefault + nPtBinsCorrelations}; -const auto vecSidebandLeftOuter = std::vector{sidebandLeftOuterDefault, sidebandLeftOuterDefault + nPtBinsCorrelations}; -const auto vecSidebandRightInner = std::vector{sidebandRightInnerDefault, sidebandRightInnerDefault + nPtBinsCorrelations}; -const auto vecSidebandRightOuter = std::vector{sidebandRightOuterDefault, sidebandRightOuterDefault + nPtBinsCorrelations}; + +const std::array pTBinsCorrelations = { + 0., 2., 4., 6., 8., 12., 16., 24., 99.}; +const auto vecBinsPtCorrelations = + std::vector{pTBinsCorrelations.begin(), pTBinsCorrelations.end()}; + +const std::array signalRegionInnerDefault = { + 2.269, 2.269, 2.269, 2.269, 2.269, 2.269, 2.269, 2.269}; +const std::array signalRegionOuterDefault = { + 2.309, 2.309, 2.309, 2.309, 2.309, 2.309, 2.309, 2.309}; +const std::array sidebandLeftOuterDefault = { + 2.209, 2.209, 2.209, 2.209, 2.209, 2.209, 2.209, 2.209}; +const std::array sidebandLeftInnerDefault = { + 2.249, 2.249, 2.249, 2.249, 2.249, 2.249, 2.249, 2.249}; +const std::array sidebandRightInnerDefault = { + 2.329, 2.329, 2.329, 2.329, 2.329, 2.329, 2.329, 2.329}; +const std::array sidebandRightOuterDefault = { + 2.369, 2.369, 2.369, 2.369, 2.369, 2.369, 2.369, 2.369}; + +const auto vecSignalRegionInner = + std::vector{signalRegionInnerDefault.begin(), signalRegionInnerDefault.end()}; +const auto vecSignalRegionOuter = + std::vector{signalRegionOuterDefault.begin(), signalRegionOuterDefault.end()}; +const auto vecSidebandLeftInner = + std::vector{sidebandLeftInnerDefault.begin(), sidebandLeftInnerDefault.end()}; +const auto vecSidebandLeftOuter = + std::vector{sidebandLeftOuterDefault.begin(), sidebandLeftOuterDefault.end()}; +const auto vecSidebandRightInner = + std::vector{sidebandRightInnerDefault.begin(), sidebandRightInnerDefault.end()}; +const auto vecSidebandRightOuter = + std::vector{sidebandRightOuterDefault.begin(), sidebandRightOuterDefault.end()}; /// Lc-Hadron correlation pair filling task, from pair tables - for real data and data-like analysis (i.e. reco-level w/o matching request via Mc truth) struct HfTaskCorrelationLcHadrons { @@ -197,12 +215,12 @@ struct HfTaskCorrelationLcHadrons { { // Axis definition AxisSpec axisMassLc = {binsMassLc, "inv. mass (p K #pi) (GeV/#it{c}^{2})"}; - AxisSpec axisPtCorr = {(std::vector)binsPtCorrelations, "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; - AxisSpec axisPtLc = {(std::vector)binsPtEfficiencyLc, "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; + AxisSpec axisPtCorr = {std::vector(binsPtCorrelations), "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; + AxisSpec axisPtLc = {std::vector(binsPtEfficiencyLc), "#it{p}_{T}^{#Lambda_c} (GeV/#it{c})"}; AxisSpec const axisMultFT0M = {binsMultFT0M, "MultiplicityFT0M"}; AxisSpec axisDeltaEta = {binsEta, "#it{#eta}^{Hadron}-#it{#eta}^{#Lambda_c}"}; AxisSpec axisDeltaPhi = {binsPhi, "#it{#varphi}^{Hadron}-#it{#varphi}^{#Lambda_c} (rad)"}; - AxisSpec axisPtHadron = {(std::vector)binsPtHadron, "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; + AxisSpec axisPtHadron = {std::vector(binsPtHadron), "#it{p}_{T}^{Hadron} (GeV/#it{c})"}; AxisSpec axisPoolBin = {binsPoolBin, "poolBin"}; AxisSpec axisLcPrompt = {2, -0.5, 1.5, "Prompt #Lambda_c"}; AxisSpec const axisBdtScore = {binsBdtScore, "Bdt score"}; @@ -268,7 +286,7 @@ struct HfTaskCorrelationLcHadrons { registry.add("hDeltaPhiPtIntSidebandsMcRec", stringLcHadron + stringSideband + stringDeltaPhi + "entries", {HistType::kTH1D, {axisDeltaPhi}}); registry.add("hCorrel2DPtIntSidebandsMcRec", stringLcHadron + stringSideband + stringDeltaPhi + stringDeltaEta + "entries", {HistType::kTH2F, {{axisDeltaPhi}, {axisDeltaEta}}}); registry.add("hCorrel2DVsPtSidebandsMcRec", stringLcHadron + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtPhysicalPrimaryMcRec", stringLcHadron + "(only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtLc}, {axisPtHadron}, {axisLcPrompt}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtPhysicalPrimaryMcRec", stringLcHadron + "(only true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtLc}, {axisPtHadron}, {axisLcPrompt}, {axisPoolBin}, {axisCentFT0M}}}); registry.add("hCorrel2DVsPtTrueLcPhysicalPrimaryMcRec", stringLcHadron + "(only true Lc, and true primary particles)" + stringSignal, {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtLc}, {axisPtHadron}, {axisLcPrompt}, {axisPoolBin}}}); registry.add("hDeltaEtaPtIntSidebandLeftMcRec", stringLcHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTH1D, {axisDeltaEta}}); registry.add("hDeltaPhiPtIntSidebandLeftMcRec", stringLcHadron + "Left" + stringSideband + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTH1D, {axisDeltaPhi}}); @@ -282,8 +300,8 @@ struct HfTaskCorrelationLcHadrons { registry.add("hCorrel2DVsPtSignalRegionNonPromptLcNonPromptHadronMcRec", stringLcHadron + " signal region PromptLc - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtLc}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSignalRegionTruePromptLcPromptHadronMcRec", stringLcHadron + "signal region and true PromptLc - Prompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtLc}, {axisPtHadron}, {axisPoolBin}}}); registry.add("hCorrel2DVsPtSignalRegionTrueNonPromptLcNonPromptHadronMcRec", stringLcHadron + " signal region and true PromptLc - NonPrompt Track MC reco", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtLc}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSignalMcRec", stringLcHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); - registry.add("hCorrel2DVsPtSignalRegionMcRec", stringLcHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisLcPrompt}, {axisPoolBin}}}); + registry.add("hCorrel2DVsPtSignalMcRec", stringLcHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}, {axisCentFT0M}}}); + registry.add("hCorrel2DVsPtSignalRegionMcRec", stringLcHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisLcPrompt}, {axisPoolBin}, {axisCentFT0M}}}); registry.add("hCorrel2DVsPtBkgMcRec", stringLcHadron + stringSignal + stringDeltaPhi + stringDeltaEta + stringPtLc + stringPtHadron + "entries", {HistType::kTHnSparseD, {{axisDeltaPhi}, {axisDeltaEta}, {axisPtCorr}, {axisPtHadron}, {axisPoolBin}}}); registry.get(HIST("hCorrel2DVsPtSidebandLeftMcRec"))->Sumw2(); @@ -405,9 +423,15 @@ struct HfTaskCorrelationLcHadrons { if (activateQA) { const int regionLimits = 6; - std::string labels[regionLimits] = {"SigReg Left", "SigReg Right", "Left SB Low", "Left SB Up", "Right SB Low", "Right SB Up"}; + const std::array labels = { + "SigReg Left", + "SigReg Right", + "Left SB Low", + "Left SB Up", + "Right SB Low", + "Right SB Up"}; static const AxisSpec axisSidebandLimits = {regionLimits, 0.5, 6.5, ""}; - auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {(std::vector)binsPtCorrelations, "#it{p}_{T} (GeV/#it{c})"}}}); + auto hSigSidebandLimits = registry.add("Inputs/hSigSidebandLimits", "Signal and Sideband Limits;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSidebandLimits, {std::vector(binsPtCorrelations), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iLim = 0; iLim < regionLimits; iLim++) { hSigSidebandLimits->GetXaxis()->SetBinLabel(iLim + 1, labels[iLim].data()); } @@ -521,7 +545,7 @@ struct HfTaskCorrelationLcHadrons { registry.fill(HIST("hCorrel2DVsPtSignMass"), deltaPhi, deltaEta, ptLc, ptHadron, massLc, signPair, poolBin, efficiencyWeight); } // check if correlation entry belongs to signal region, sidebands or is outside both, and fill correlation plots - if (storeMass) { + if (storeMass != 0) { registry.fill(HIST("hCorrel2DVsPtGlobalRegion"), deltaPhi, deltaEta, ptLc, ptHadron, poolBin, massLc, efficiencyWeight); continue; } @@ -636,6 +660,11 @@ struct HfTaskCorrelationLcHadrons { bool const isAutoCorrelated = pairEntry.isAutoCorrelated(); int signPair = 0; + float cent = 0.; + if (useCentrality) { + cent = pairEntry.cent(); + } + // reject entries outside pT ranges of interest if (ptLc < binsPtEfficiencyLc->front() || ptLc > binsPtEfficiencyLc->back()) { continue; @@ -696,7 +725,7 @@ struct HfTaskCorrelationLcHadrons { if (fillSign) { registry.fill(HIST("hCorrel2DVsPtSignSignalMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, signPair, poolBin, efficiencyWeight); } else { - registry.fill(HIST("hCorrel2DVsPtSignalMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, poolBin, cent, efficiencyWeight); } } else { if (fillSign) { @@ -713,13 +742,13 @@ struct HfTaskCorrelationLcHadrons { if (fillSign) { registry.fill(HIST("hCorrel2DVsPtSignSignalRegionMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, signPair, poolBin, efficiencyWeight); } else { - registry.fill(HIST("hCorrel2DVsPtSignalRegionMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, statusLcPrompt, poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtSignalRegionMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, statusLcPrompt, poolBin, cent, efficiencyWeight); } registry.fill(HIST("hCorrel2DPtIntSignalRegionMcRec"), deltaPhi, deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaEtaPtIntSignalRegionMcRec"), deltaEta, efficiencyWeight); registry.fill(HIST("hDeltaPhiPtIntSignalRegionMcRec"), deltaPhi, efficiencyWeight); if (isPhysicalPrimary) { - registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, statusLcPrompt, poolBin, efficiencyWeight); + registry.fill(HIST("hCorrel2DVsPtPhysicalPrimaryMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, statusLcPrompt, poolBin, cent, efficiencyWeight); if (pairEntry.signalStatus() != 0) { registry.fill(HIST("hCorrel2DVsPtTrueLcPhysicalPrimaryMcRec"), deltaPhi, deltaEta, ptLc, ptHadron, statusLcPrompt, poolBin, efficiencyWeight); } diff --git a/PWGHF/HFC/Tasks/taskFlow.cxx b/PWGHF/HFC/Tasks/taskFlow.cxx index 3591f8f3907..24a375c075a 100644 --- a/PWGHF/HFC/Tasks/taskFlow.cxx +++ b/PWGHF/HFC/Tasks/taskFlow.cxx @@ -22,13 +22,16 @@ #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" -#include "PWGHF/Utils/utilsPid.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGMM/Mult/DataModel/bestCollisionTable.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -38,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -46,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -57,12 +58,13 @@ #include #include -#include +#include #include #include #include +#include #include #include #include @@ -82,7 +84,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; enum CorrelationCase { - TpcTpc, + TpcTpc = 0, TpcMft, TpcFv0a, MftFv0a, @@ -103,6 +105,24 @@ enum DataType { Mc }; +enum FT0DetectorChannels { + FT0AInnerRingMin = 0, + FT0AInnerRingMax = 31, + FT0AOuterRingMin = 32, + FT0AOuterRingMax = 95, + FT0CInnerRingMin = 96, + FT0CInnerRingMax = 143, + FT0COuterRingMin = 144, + FT0COuterRingMax = 207 +}; + +enum FT0InnerOrOuter { + FT0Error, + FT0InnerRing, + FT0OuterRing, + NFT0InnerOrOuterSteps +}; + enum ReassociationMftTracks { NotReassociatedMftTracks = 0, ReassociatedMftTracks, @@ -129,14 +149,31 @@ enum MftTrackAmbiguityStep { NMftAmbiguitySteps }; +enum MftTrackTemplate { + FilteredMftTracks = 0, + FilteredMftTracksWCollsMcLabels +}; + enum MftTrackSelectionStep { NoSelection = 0, Eta, Cluster, Pt, + DCAxy, + DCAz, + Chi2OverNdf, + Phi, + IsLTF, + IsCA, NMftTrackSelectionSteps }; +enum CentralityEstimators { + CentFT0C = 0, + CentFT0CVariant1, + CentFT0M +}; + enum MultiplicityEstimators { MultNTracksPV = 0, MultNumContrib, @@ -156,6 +193,7 @@ enum SpecificEventSelectionStep { IsNoCollInTimeRangeStrict, IsNoHighMultCollInPrevRof, IsRctFlagChecked, + IsFoundFT0, NSpecificEventSelectionSteps }; @@ -164,26 +202,14 @@ enum TrackSelection { TrackSelectionGlobalTrack }; -// static constexpr std::string_view whatEventType[] = {"SameEvent/", "MixedEvent/"}; -static constexpr std::string_view WhatDataType[] = {"Data/", "MC/"}; -static constexpr std::string_view WhatCorrelationCase[] = {"TpcTpc/", "TpcMft/", "TpcFv0a/", "MftFv0a/", "TpcFt0a/", "MftFt0a/", "TpcFt0c/", "Ft0aFt0c/"}; -static constexpr std::string_view WhatParticles[] = {"ChPartChPart/", "D0ChPart/", "LcChPart/"}; -static constexpr std::string_view WhatMultiplicityEstimator[] = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; +static constexpr std::array WhatDataType = {"Data/", "MC/"}; +static constexpr std::array WhatCorrelationCase = {"TpcTpc/", "TpcMft/", "TpcFv0a/", "MftFv0a/", "TpcFt0a/", "MftFt0a/", "TpcFt0c/", "Ft0aFt0c/"}; +static constexpr std::array WhatParticles = {"ChPartChPart/", "D0ChPart/", "LcChPart/"}; +static constexpr std::array WhatCentralityEstimator = {"centFT0C", "CentFT0CVariant1", "centFT0M"}; +static constexpr std::array WhatMultiplicityEstimator = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; auto static constexpr MinFt0cCell = 96; -static constexpr TrackSelectionFlags::flagtype TrackSelectionIts = - TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | - TrackSelectionFlags::kITSHits; -static constexpr TrackSelectionFlags::flagtype TrackSelectionTpc = - TrackSelectionFlags::kTPCNCls | - TrackSelectionFlags::kTPCCrossedRowsOverNCls | - TrackSelectionFlags::kTPCChi2NDF; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = - TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = - TrackSelectionFlags::kDCAxy; - -static constexpr float PairCutDefaults[1][5] = {{-1, -1, -1, -1, -1}}; +static constexpr std::array, 1> PairCutDefaults{{{{-1, -1, -1, -1, -1}}}}; struct HfTaskFlow { @@ -198,16 +224,39 @@ struct HfTaskFlow { struct : ConfigurableGroup { std::string prefix = "ConfigTask_group"; Configurable centralityBinsForMc{"centralityBinsForMc", false, "falsce = OFF, true = ON for data like multiplicity/centrality bins for MC steps"}; + Configurable chooseCorrelationCase{"chooseCorrelationCase", 0, "0: Tpc-Tpc, 1: Tpc-Mft, 2: Tpc-Fv0a, 3: Mft-Fv0a, 4: Tpc-Ft0a, 5: Mft-Ft0a, 6: Tpc-Ft0c, 7: Ft0a-Ft0c"}; + Configurable doEtaDependentFlow{"doEtaDependentFlow", false, "Flag to know if eta dependent flow should be done"}; Configurable doHeavyFlavor{"doHeavyFlavor", false, "Flag to know we in the heavy flavor case or not"}; + Configurable doMonteCarlo{"doMonteCarlo", false, "Flag to know if Monte Carlo should be done"}; Configurable doReferenceFlow{"doReferenceFlow", false, "Flag to know if reference flow should be done"}; + Configurable doVariationContainers{"doVariationContainers", false, "Flag to know if variation containers should be filled"}; + Configurable etaMcParticlesTriggerMax{"etaMcParticlesTriggerMax", 0.8f, "Maximum value for the eta of MC particles when used in cut function"}; + Configurable etaMcParticlesTriggerMin{"etaMcParticlesTriggerMin", -0.8f, "Minimum value for the eta of MC particles when used in cut function"}; + Configurable etaMcParticlesAssocMax{"etaMcParticlesAssocMax", -2.4f, "Maximum value for the eta of MC particles when used in cut function"}; + Configurable etaMcParticlesAssocMin{"etaMcParticlesAssocMin", -3.6f, "Minimum value for the eta of MC particles when used in cut function"}; + Configurable fillOnlyStepAll{"fillOnlyStepAll", true, "Fill Mc Gen QA plots only for StepAll, or only for Primaries"}; + Configurable loadCentralityWeight{"loadCentralityWeight", "", "Ccdb path to centrality weight correction"}; + Configurable loadEfficienciesForTpc{"loadEfficienciesForTpc", "", "Ccdb path to Tpc tracks efficiency correction"}; + Configurable loadEfficienciesForMft{"loadEfficienciesForMft", "", "Ccdb path to Mft tracks efficiency correction"}; + Configurable loadEfficienciesForNch{"loadEfficienciesForNch", "", "Ccdb path to Nch estimation efficiency correction"}; + Configurable ptMcParticlesTriggerMax{"ptMcParticlesTriggerMax", 3.0f, "Maximum value for the pT of MC particles when used in cut function"}; + Configurable ptMcParticlesTriggerMin{"ptMcParticlesTriggerMin", 0.2f, "Minimum value for the pT of MC particles when used in cut function"}; + Configurable ptMcParticlesAssocMax{"ptMcParticlesAssocMax", 100.0f, "Maximum value for the pT of MC particles when used in cut function"}; + Configurable ptMcParticlesAssocMin{"ptMcParticlesAssocMin", 0.f, "Minimum value for the pT of MC particles when used in cut function"}; Configurable isReadoutCenter{"isReadoutCenter", false, "Enable Readout Center"}; Configurable nMixedEvents{"nMixedEvents", 5, "Number of mixed events per event"}; Configurable nSamples{"nSamples", 10, "number of different samples for correlations"}; + Configurable nameCorrelationContainer{"nameCorrelationContainer", "", "Add the possibility to the rename the correlation container as configurable"}; + Configurable removeQAForSystematics{"removeQAForSystematics", false, "Remove QA plots for systematics, deactivate all plots except trigger hist"}; + Configurable useCutNeutralParticles{"useCutNeutralParticles", false, "Choose to use or not use neutral particles in the correlation analysis"}; + Configurable useEfficiencyCorrection{"useEfficiencyCorrection", false, "Choose to use or not use efficiency correction, if not used, weight is set to 1"}; + Configurable useOnlyPrimaryInMc{"useOnlyPrimaryInMc", false, "Choose to use or not use only primaries for McGen"}; } configTask; // configurables for collisions struct : ConfigurableGroup { std::string prefix = "ConfigCollision_group"; + Configurable centralityEstimator{"centralityEstimator", 0, "0: centFT0C, 1: CentFT0CVariant1, 2: centFT0M"}; Configurable isApplyGoodItsLayersAll{"isApplyGoodItsLayersAll", false, "Enable GoodITSLayersAll"}; Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", false, "Enable GoodZvtxFT0vsPV cut"}; Configurable isApplyNoCollInRofStandard{"isApplyNoCollInRofStandard", false, ""}; @@ -216,9 +265,15 @@ struct HfTaskFlow { Configurable isApplyNoCollInTimeRangeStrict{"isApplyNoCollInTimeRangeStrict", false, ""}; Configurable isApplyNoHighMultCollInPrevRof{"isApplyNoHighMultCollInPrevRof", false, ""}; Configurable isApplySameBunchPileup{"isApplySameBunchPileup", false, "Enable SameBunchPileup cut"}; + Configurable maxCentrality{"maxCentrality", 20, "maximum centrality"}; + Configurable minCentrality{"minCentrality", 0, "minimum centrality"}; Configurable maxMultiplicity{"maxMultiplicity", 300, "maximum multiplicity selection for collision"}; Configurable minMultiplicity{"minMultiplicity", 0, "minimum multiplicity selection for collision"}; - Configurable multiplicityEstimator{"multiplicityEstimator", 0, "0: multNTracksPV, 1: numContrib, 2: multFT0C, 3: multFT0M, 4: centFT0C, 5: centFT0CVariants1s, 6: centFT0M, 7: centFV0A, 8: centNTracksPV, 9: centNGlobal, 10: centMFT"}; + Configurable multiplicityEstimator{"multiplicityEstimator", 0, "0: multNTracksPV, 1: numContrib, 2: multFT0C, 3: multFT0M"}; + Configurable useCentrality{"useCentrality", false, "use centrality instead of multiplicity"}; + Configurable useMultiplicityFromTracks{"useMultiplicityFromTracks", false, "Use multiplicity from counting tracks"}; + Configurable useMultiplicityFromTracksCorrected{"useMultiplicityFromTracksCorrected", false, "Use multiplicity from counting tracks, corrected but takes a lot of computation time"}; + Configurable useNoFoundFT0Cut{"useNoFoundFT0Cut", false, "Use cut on no FT0 signal"}; Configurable requireRCTFlagChecker{"requireRCTFlagChecker", false, "Check event quality in run condition table"}; Configurable requireCorrelationAnalysisRCTFlagChecker{"requireCorrelationAnalysisRCTFlagChecker", false, "Check event quality in run condition table for correlation analysis"}; Configurable setRCTFlagCheckerLabel{"setRCTFlagCheckerLabel", "CBT_muon_global", "Evt sel: RCT flag checker label"}; @@ -247,7 +302,7 @@ struct HfTaskFlow { Configurable minMergingRadius{"minMergingRadius", 0.8, "max radius for merging cut"}; Configurable minTpcClusters{"minTpcClusters", 50.0f, "cut for minimum TPC clusters"}; Configurable minTpcCrossedRows{"minTpcCrossedRows", 70.0f, "cut for minimum TOC crossed rows"}; - Configurable> pairCut{"pairCut", {PairCutDefaults[0], 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; + Configurable> pairCut{"pairCut", {PairCutDefaults[0].data(), 5, {"Photon", "K0", "Lambda", "Phi", "Rho"}}, "Pair cuts on various particles"}; Configurable ptCentralTrackMin{"ptCentralTrackMin", 0.2f, "min. pT of central tracks"}; Configurable ptCentralTrackMax{"ptCentralTrackMax", 10.0f, "max. pT of central tracks"}; Configurable trackSelectionType{"trackSelectionType", 1, "Track selection: 0 -> kGlobalTrack or isGlobalTrackSDD , 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> No globalTrack selection"}; @@ -267,16 +322,27 @@ struct HfTaskFlow { struct : ConfigurableGroup { std::string prefix = "ConfigMft_group"; Configurable cutBestCollisionId{"cutBestCollisionId", 0, "cut on the best collision Id used in a filter"}; + Configurable cutOnDcaXY{"cutOnDcaXY", false, "if true, cut on DCA XY"}; + Configurable cutOnDcaZ{"cutOnDcaZ", false, "if true, cut on DCA Z"}; Configurable etaMftTrackMax{"etaMftTrackMax", -2.4f, "Maximum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMin{"etaMftTrackMin", -3.36f, "Minimum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMaxFilter{"etaMftTrackMaxFilter", -2.0f, "Maximum value for the eta of MFT tracks when used in filter"}; Configurable etaMftTrackMinFilter{"etaMftTrackMinFilter", -3.9f, "Minimum value for the eta of MFT tracks when used in filter"}; + Configurable maxChi2OverNdf{"maxChi2OverNdf", 1000.f, "maximum chi2/ndf for MFT tracks"}; Configurable mftMaxDCAxy{"mftMaxDCAxy", 2.0f, "Cut on dcaXY for MFT tracks"}; Configurable mftMaxDCAz{"mftMaxDCAz", 2.0f, "Cut on dcaZ for MFT tracks"}; Configurable nClustersMftTrack{"nClustersMftTrack", 5, "Minimum number of clusters for the reconstruction of MFT tracks"}; + Configurable phiMftTrackMin{"phiMftTrackMin", 0.1, "Minimum value for the phi of MFT tracks when used in cut function"}; + Configurable phiMftTrackMiddleMin{"phiMftTrackMiddleMin", PI - 0.1, "cut to remove before phi = pi for MFT tracks when used in cut function"}; + Configurable phiMftTrackMiddleMax{"phiMftTrackMiddleMax", PI + 0.1, "cut to remove after phi = pi for MFT tracks when used in cut function"}; + Configurable phiMftTrackMax{"phiMftTrackMax", TwoPI - 0.1, "Maximum value for the phi of MFT tracks when used in cut function"}; Configurable ptMftTrackMax{"ptMftTrackMax", 10.0f, "max value of MFT tracks pT when used in cut function"}; Configurable ptMftTrackMin{"ptMftTrackMin", 0.f, "min value of MFT tracks pT when used in cut function"}; + Configurable useMftChi2OverNdfCut{"useMftChi2OverNdfCut", false, "use mft track chi2/ndf cut"}; + Configurable useMftPhiCut{"useMftPhiCut", false, "if true, use the Mft phi function cut"}; Configurable useMftPtCut{"useMftPtCut", false, "if true, use the Mft pt function cut"}; + Configurable useOnlyCATracks{"useOnlyCATracks", false, "if true, use strictly MFT tracks reconstructed with CA algo."}; + Configurable useOnlyLTFTracks{"useOnlyLTFTracks", false, "if true, use strictly MFT tracks reconstructed with LTF algo."}; } configMft; // configurables for MFT tracks @@ -284,32 +350,56 @@ struct HfTaskFlow { std::string prefix = "ConfigFit_group"; Configurable cfgGainEqPath{"cfgGainEqPath", "Analysis/EventPlane/GainEq", "CCDB path for gain equalization constants"}; Configurable cfgCorrLevel{"cfgCorrLevel", 1, "calibration step: 0 = no corr, 1 = gain corr"}; + Configurable discriminateInnerOrOuter{"discriminateInnerOrOuter", false, "if true, fill correlationContainer with either Inner ou Outer for FIT, if false, fill with real eta information"}; } configFit; - TF1* fPtDepDCAxy = nullptr; - SliceCache cache; Service pdg{}; Service ccdb{}; std::vector* offsetFT0{}; - std::vector* offsetFV0{}; + // std::vector* offsetFV0{}; o2::ccdb::CcdbApi ccdbApi; o2::ft0::Geometry ft0Det; - o2::fv0::Geometry* fv0Det{}; - std::vector cstFT0RelGain{}; + // o2::fv0::Geometry* fv0Det{}; + std::vector cstFT0RelGain; RCTFlagsChecker rctChecker; - RCTFlagsChecker correlationAnalysisRctChecker{kFT0Bad, kITSBad, kTPCBadTracking, kMFTBad}; + RCTFlagsChecker correlationAnalysisRctChecker{kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}; + + TH3D* mEfficiencyTpc = nullptr; + TH3D* mEfficiencyMft = nullptr; + TH1D* mEfficiencyNch = nullptr; + TH1D* mCentralityWeight = nullptr; + bool areCorrectionsLoaded = false; + + // o2::aod::rctsel::RCTFlagsChecker rctChecker{"CBT_muon_glo", false, false, true}; // ========================= // using declarations : DATA // ========================= - using FilteredCollisionsWSelMult = soa::Filtered>; + using FilteredCollisionsWSelMult = soa::Filtered>; using HfCandidatesSelD0 = soa::Filtered>; using HfCandidatesSelLc = soa::Filtered>; using FilteredTracksWDcaSel = soa::Filtered>; + using FilteredTracksWDcaSelWLabels = soa::Filtered>; using FilteredMftTracks = soa::Filtered; + using FilteredMftTracksWCollsMcLabels = soa::Filtered>; + + // ========================= + // using declarations : MC + // ========================= + + using SmallGroupMcCollisions = soa::SmallGroups>; + // using FilteredMcCollisionsWMult = soa::Filtered>; + using FilteredMcCollisionsWMult = soa::Filtered>; + using FilteredMcCollisionsWMultWCollsExtra = soa::Filtered>; + // using FilteredMcParticles = soa::Filtered; + using FilteredMcParticles = aod::McParticles; + + // MC reconstructed + + // using FilteredMftTracksWCollsMcLabels = soa::Filtered>; // ========================= // Filters & partitions : DATA @@ -332,11 +422,7 @@ struct HfTaskFlow { (aod::track::pt < configCentral.ptCentralTrackMax) && requireGlobalTrackInFilter(); - Filter centralTrackItsTpcMatchingFilter = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), ncheckbit(aod::track::trackCutFlag, TrackSelectionTpc), true) && - ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && - ncheckbit(aod::track::trackCutFlag, TrackSelectionIts); - - Filter centralTrackDcaFilter = (ifnode(configCentral.dcaZCentralTrackMax.node() > 0.f, nabs(aod::track::dcaZ) <= configCentral.dcaZCentralTrackMax && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), ncheckbit(aod::track::trackCutFlag, TrackSelectionDca))); + Filter centralTrackDcaFilter = (nabs(aod::track::dcaZ) <= configCentral.dcaZCentralTrackMax); Filter centralTrackChi2TpcClusterFilter = (aod::track::tpcChi2NCl < configCentral.maxChi2TpcClusters); @@ -344,11 +430,20 @@ struct HfTaskFlow { Filter mftTrackEtaFilter = ((aod::fwdtrack::eta < configMft.etaMftTrackMaxFilter) && (aod::fwdtrack::eta > configMft.etaMftTrackMinFilter)); - // Filters below will be used for uncertainties Filter mftTrackCollisionIdFilter = (aod::fwdtrack::bestCollisionId >= 0); - Filter mftTrackDcaXYFilter = (nabs(aod::fwdtrack::bestDCAXY) < configMft.mftMaxDCAxy); + // Filter mftTrackDcaXYFilter = (nabs(aod::fwdtrack::bestDCAXY) < configMft.mftMaxDCAxy); // Filter mftTrackDcaZFilter = (nabs(aod::fwdtrack::bestDCAZ) < configMft.mftMaxDCAz); + // ========================= + // Filters & partitions : MC + // ========================= + + // // Filter for McParticles + // Filter mcParticleFilter = (((aod::mcparticle::eta > configTask.etaMcParticlesTriggerMin) && (aod::mcparticle::eta < configTask.etaMcParticlesTriggerMax)) || ((aod::mcparticle::eta > configTask.etaMcParticlesAssocMin) && (aod::mcparticle::eta < configTask.etaMcParticlesAssocMax)) || (nabs(aod::mcparticle::eta) < configCentral.etaCentralTrackMax)) && (aod::mcparticle::pt < configTask.ptMcParticlesTriggerMax); + + // Filter for MCcollisions + Filter mcCollisionFilter = nabs(aod::mccollision::posZ) < configCollision.zVertexMax; + // ========================= // Preslice : DATA // ========================= @@ -357,6 +452,16 @@ struct HfTaskFlow { Preslice perColLcs = aod::track::collisionId; Preslice perColMftTracks = o2::aod::fwdtrack::collisionId; Preslice perColTracks = aod::track::collisionId; + // Preslice perMcColMcParticles = aod::mcparticle::mcCollisionId; + + PresliceUnsorted> perColReassociated2dTracks = o2::aod::fwdtrack::collisionId; + PresliceUnsorted> perColReassociated3dTracks = o2::aod::fwdtrack::collisionId; + + // ========================= + // Preslice : MC + // ========================= + + PresliceUnsorted collisionPerMcCollision = aod::mccollisionlabel::mcCollisionId; // configurables for containers // TODO: flow of HF will need to be done vs. invariant mass, in the signal and side-band regions @@ -368,9 +473,14 @@ struct HfTaskFlow { ConfigurableAxis axisMass{"axisMass", {1, 1.5848, 2.1848}, "axis of invariant mass of candidates"}; ConfigurableAxis binsMixingMultiplicity{"binsMixingMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity bins for event mixing"}; ConfigurableAxis binsMixingVertex{"binsMixingVertex", {20, -10, 10}, "vertex bins for event mixing"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "centrality axis for histograms"}; + ConfigurableAxis axisChi2Ndf{"axisChi2Ndf", {100, 0, 100}, "chi2/ndf axis for MFT histograms"}; + ConfigurableAxis axisNClusters{"axisNClusters", {9, 1, 10}, "axis for number of clusters of MFT tracks"}; ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {1, -1.0, 1.0}, "eta axis for efficiency histograms"}; ConfigurableAxis axisEtaAssociated{"axisEtaAssociated", {48, -4, -2}, "eta axis for MFT histograms"}; ConfigurableAxis axisEtaTrigger{"axisEtaTrigger", {48, -1, 1}, "eta axis for TPC histograms"}; + ConfigurableAxis axisDcaXY{"axisDcaXY", {100, 0, 2}, "dcaXY axis for MFT histograms"}; + ConfigurableAxis axisDcaZ{"axisDcaZ", {100, -2, 2}, "dcaZ axis for MFT histograms"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; ConfigurableAxis axisDeltaEta{"axisDeltaEta", {48, -2.4, 2.4}, "delta eta axis for histograms"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity axis for histograms"}; @@ -393,13 +503,15 @@ struct HfTaskFlow { // Correlation containers used for data OutputObj sameEvent{"sameEvent"}; OutputObj mixedEvent{"mixedEvent"}; + OutputObj sameEventTpcMft{"sameEvent_TPC_MFT"}; + OutputObj mixedEventTpcMft{"mixedEvent_TPC_MFT"}; + OutputObj sameEventTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedEventTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameEventMftFt0a{"sameEvent_MFT_FT0A"}; + OutputObj mixedEventMftFt0a{"mixedEvent_MFT_FT0A"}; OutputObj sameEventHf{"sameEventHf"}; OutputObj mixedEventHf{"mixedEventHf"}; - // Correlation containers used for Monte-Carlo - OutputObj sameEventHfMc{"sameEventHfMc"}; - OutputObj mixedEventHfMc{"mixedEventHfMc"}; - template void addHistograms() { @@ -416,7 +528,7 @@ struct HfTaskFlow { void addMftHistograms() { registry.add("Data/Mft/hAmbiguityOfMftTracks", "hAmbiguityOfMftTracks", {HistType::kTH1D, {{MftTrackAmbiguityStep::NMftAmbiguitySteps, -0.5, +MftTrackAmbiguityStep::NMftAmbiguitySteps - 0.5}}}); - std::string labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NMftAmbiguitySteps]; + std::array labelsAmbiguityOfMftTracks{}; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AllMftTracks] = "all MFT tracks"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AfterTrackSelection] = "MFT tracks after selection"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NumberOfAmbiguousTracks] = "how much tracks are ambigous"; @@ -428,11 +540,17 @@ struct HfTaskFlow { } registry.add("Data/Mft/hMftTracksSelection", "hMftTracksSelection", {HistType::kTH1D, {{MftTrackSelectionStep::NMftTrackSelectionSteps, -0.5, +MftTrackSelectionStep::NMftTrackSelectionSteps - 0.5}}}); - std::string labelsMftTracksSelection[MftTrackSelectionStep::NMftTrackSelectionSteps]; + std::array labelsMftTracksSelection{}; labelsMftTracksSelection[MftTrackSelectionStep::NoSelection] = "all MFT tracks"; labelsMftTracksSelection[MftTrackSelectionStep::Eta] = "MFT tracks after eta selection"; labelsMftTracksSelection[MftTrackSelectionStep::Cluster] = "MFT tracks after clusters selection"; labelsMftTracksSelection[MftTrackSelectionStep::Pt] = "MFT tracks after pT selection"; + labelsMftTracksSelection[MftTrackSelectionStep::DCAxy] = "MFT tracks after DCAxy selection"; + labelsMftTracksSelection[MftTrackSelectionStep::DCAz] = "MFT tracks after DCAz selection"; + labelsMftTracksSelection[MftTrackSelectionStep::Chi2OverNdf] = "MFT tracks after Chi2OverNdf selection"; + labelsMftTracksSelection[MftTrackSelectionStep::Phi] = "MFT tracks after phi selection"; + labelsMftTracksSelection[MftTrackSelectionStep::IsLTF] = "Linear Track Finder MFT tracks"; + labelsMftTracksSelection[MftTrackSelectionStep::IsCA] = "Cellular Automaton MFT tracks"; registry.get(HIST("Data/Mft/hMftTracksSelection"))->SetMinimum(0); for (int iBin = 0; iBin < MftTrackSelectionStep::NMftTrackSelectionSteps; iBin++) { @@ -440,7 +558,7 @@ struct HfTaskFlow { } registry.add("Data/Mft/hReassociationMftTracks", "hReassociationMftTracks", {HistType::kTH1D, {{ReassociationMftTracks::NReassociationMftTracksSteps, -0.5, +ReassociationMftTracks::NReassociationMftTracksSteps - 0.5}}}); - std::string labelsReassociationMftTracks[ReassociationMftTracks::NReassociationMftTracksSteps]; + std::array labelsReassociationMftTracks{}; labelsReassociationMftTracks[ReassociationMftTracks::NotReassociatedMftTracks] = "Ambiguous MFT tracks after track selection"; labelsReassociationMftTracks[ReassociationMftTracks::ReassociatedMftTracks] = "Reassociated MFT tracks by DCAxy method"; registry.get(HIST("Data/Mft/hReassociationMftTracks"))->SetMinimum(0); @@ -449,11 +567,32 @@ struct HfTaskFlow { registry.get(HIST("Data/Mft/hReassociationMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsReassociationMftTracks[iBin].data()); } + registry.add("Data/Mft/hChi2OverNdfMft", "", {HistType::kTH1D, {configAxis.axisChi2Ndf}}); registry.add("Data/Mft/hPtMft", "", {HistType::kTH1D, {configAxis.axisPt}}); + registry.add("Data/Mft/hPtVsClustersLTF", "", {HistType::kTH2D, {configAxis.axisPt, configAxis.axisNClusters}}); + registry.add("Data/Mft/hPtVsClustersCA", "", {HistType::kTH2D, {configAxis.axisPt, configAxis.axisNClusters}}); + registry.add("Data/Mft/hDcaXYMft", "", {HistType::kTH1D, {configAxis.axisDcaXY}}); + registry.add("Data/Mft/hDcaZMft", "", {HistType::kTH1D, {configAxis.axisDcaZ}}); registry.add("Data/Mft/hNMftTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); registry.add("Data/Mft/hNBestCollisionFwd", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); } + void addFt0Histograms() + { + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + registry.add("Data/FT0IsInnerOrOuter", "FT0IsInnerOrOuter", {HistType::kTH1D, {{FT0InnerOrOuter::NFT0InnerOrOuterSteps, -0.5, +FT0InnerOrOuter::NFT0InnerOrOuterSteps - 0.5}}}); + std::array labels{}; + labels[FT0InnerOrOuter::FT0InnerRing] = "FT0InnerRing"; + labels[FT0InnerOrOuter::FT0OuterRing] = "FT0OuterRing"; + registry.get(HIST("Data/FT0IsInnerOrOuter"))->SetMinimum(0); + + for (int iBin = 0; iBin < FT0InnerOrOuter::NFT0InnerOrOuterSteps; iBin++) { + registry.get(HIST("Data/FT0IsInnerOrOuter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + } + } + // ========================= // init() // ========================= @@ -466,60 +605,68 @@ struct HfTaskFlow { ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); LOGF(info, "Getting alignment offsets from the CCDB..."); offsetFT0 = ccdb->getForTimeStamp>("FT0/Calib/Align", configCcdb.noLaterThan.value); - offsetFV0 = ccdb->getForTimeStamp>("FV0/Calib/Align", configCcdb.noLaterThan.value); + // offsetFV0 = ccdb->getForTimeStamp>("FV0/Calib/Align", configCcdb.noLaterThan.value); LOGF(info, "Offset for FT0A: x = %.3f y = %.3f z = %.3f\n", (*offsetFT0)[0].getX(), (*offsetFT0)[0].getY(), (*offsetFT0)[0].getZ()); LOGF(info, "Offset for FT0C: x = %.3f y = %.3f z = %.3f\n", (*offsetFT0)[1].getX(), (*offsetFT0)[1].getY(), (*offsetFT0)[1].getZ()); - LOGF(info, "Offset for FV0-left: x = %.3f y = %.3f z = %.3f\n", (*offsetFV0)[0].getX(), (*offsetFV0)[0].getY(), (*offsetFV0)[0].getZ()); - LOGF(info, "Offset for FV0-right: x = %.3f y = %.3f z = %.3f\n", (*offsetFV0)[1].getX(), (*offsetFV0)[1].getY(), (*offsetFV0)[1].getZ()); - fv0Det = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); + // LOGF(info, "Offset for FV0-left: x = %.3f y = %.3f z = %.3f\n", (*offsetFV0)[0].getX(), (*offsetFV0)[0].getY(), (*offsetFV0)[0].getZ()); + // LOGF(info, "Offset for FV0-right: x = %.3f y = %.3f z = %.3f\n", (*offsetFV0)[1].getX(), (*offsetFV0)[1].getY(), (*offsetFV0)[1].getZ()); + // fv0Det = o2::fv0::Geometry::instance(o2::fv0::Geometry::eUninitialized); // ========================= // Event histograms // ========================= - rctChecker.init(configCollision.setRCTFlagCheckerLabel, configCollision.requireZDCCheck, configCollision.requireRCTFlagCheckerLimitAcceptanceAsBad); - correlationAnalysisRctChecker.init({kFT0Bad, kITSBad, kTPCBadTracking, kMFTBad}, configCollision.requireZDCCheck, configCollision.requireRCTFlagCheckerLimitAcceptanceAsBad); - - registry.add("Data/hVtxZ", "v_{z} (cm)", {HistType::kTH1D, {configAxis.axisVertex}}); - registry.add("Data/hNTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); - registry.add(Form("Data/hMultiplicity_%s", WhatMultiplicityEstimator[configCollision.multiplicityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); - - registry.add("Data/hEventCounter", "hEventCounter", {HistType::kTH1D, {{EventSelectionStep::NEventSelectionSteps, -0.5, +EventSelectionStep::NEventSelectionSteps - 0.5}}}); - std::string labels[EventSelectionStep::NEventSelectionSteps]; - labels[EventSelectionStep::AllEvents] = "all"; - labels[EventSelectionStep::AfterEventSelection] = "after Physics selection"; - registry.get(HIST("Data/hEventCounter"))->SetMinimum(0); - - for (int iBin = 0; iBin < EventSelectionStep::NEventSelectionSteps; iBin++) { - registry.get(HIST("Data/hEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); - } - - registry.add("Data/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); - std::string labelsPreciseEventSelection[SpecificEventSelectionStep::NSpecificEventSelectionSteps]; - labelsPreciseEventSelection[SpecificEventSelectionStep::AllEventsPrecise] = "all"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsSel8] = "sel8"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoSameBunchPileup] = "IsNoSameBunchPileup"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodItsLayersAll] = "IsGoodItsLayersAll"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodZvtxFT0vsPV] = "IsGoodZvtxFT0vsPV"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStandard] = "IsNoCollInRofStandard"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStrict] = "IsNoCollInRofStrict"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStandard] = "IsNoCollInTimeRangeStandard"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStrict] = "IsNoCollInTimeRangeStrict"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoHighMultCollInPrevRof] = "IsNoHighMultCollInPrevRof"; - labelsPreciseEventSelection[SpecificEventSelectionStep::IsRctFlagChecked] = "IsRctFlagChecked"; - registry.get(HIST("Data/hPreciseEventCounter"))->SetMinimum(0); + rctChecker.init(configCollision.setRCTFlagCheckerLabel, configCollision.requireZDCCheck, configCollision.requireRCTFlagCheckerLimitAcceptanceAsBad, true); + correlationAnalysisRctChecker.init({kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}); + + if (!configTask.removeQAForSystematics) { + registry.add("Data/hVtxZ", "v_{z} (cm)", {HistType::kTH1D, {configAxis.axisVertex}}); + registry.add("Data/hNTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); + registry.add(Form("Data/hMultiplicity_%s", WhatMultiplicityEstimator[configCollision.multiplicityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("Data/hMultiplicityTracksSize", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("Data/hMultiplicityUsed", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add(Form("Data/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("Data/hCentralityWeighted", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("Data/hCentralityUsed", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + + registry.add("Data/hEventCounter", "hEventCounter", {HistType::kTH1D, {{EventSelectionStep::NEventSelectionSteps, -0.5, +EventSelectionStep::NEventSelectionSteps - 0.5}}}); + std::array labels{}; + labels[EventSelectionStep::AllEvents] = "all"; + labels[EventSelectionStep::AfterEventSelection] = "after Physics selection"; + registry.get(HIST("Data/hEventCounter"))->SetMinimum(0); + + for (int iBin = 0; iBin < EventSelectionStep::NEventSelectionSteps; iBin++) { + registry.get(HIST("Data/hEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + } - for (int iBin = 0; iBin < SpecificEventSelectionStep::NSpecificEventSelectionSteps; iBin++) { - registry.get(HIST("Data/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsPreciseEventSelection[iBin].data()); - } + registry.add("Data/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); + std::array labelsPreciseEventSelection{}; + labelsPreciseEventSelection[SpecificEventSelectionStep::AllEventsPrecise] = "all"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsSel8] = "sel8"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoSameBunchPileup] = "IsNoSameBunchPileup"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodItsLayersAll] = "IsGoodItsLayersAll"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsGoodZvtxFT0vsPV] = "IsGoodZvtxFT0vsPV"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStandard] = "IsNoCollInRofStandard"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInRofStrict] = "IsNoCollInRofStrict"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStandard] = "IsNoCollInTimeRangeStandard"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoCollInTimeRangeStrict] = "IsNoCollInTimeRangeStrict"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsNoHighMultCollInPrevRof] = "IsNoHighMultCollInPrevRof"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsRctFlagChecked] = "IsRctFlagChecked"; + labelsPreciseEventSelection[SpecificEventSelectionStep::IsFoundFT0] = "IsFoundFT0"; + registry.get(HIST("Data/hPreciseEventCounter"))->SetMinimum(0); + + for (int iBin = 0; iBin < SpecificEventSelectionStep::NSpecificEventSelectionSteps; iBin++) { + registry.get(HIST("Data/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsPreciseEventSelection[iBin].data()); + } - mPairCuts.SetHistogramRegistry(®istry); - if (configCentral.pairCut->get("Photon") > 0 || configCentral.pairCut->get("K0") > 0 || configCentral.pairCut->get("Lambda") > 0 || configCentral.pairCut->get("Phi") > 0 || configCentral.pairCut->get("Rho") > 0) { - mPairCuts.SetPairCut(PairCuts::Photon, configCentral.pairCut->get("Photon")); - mPairCuts.SetPairCut(PairCuts::K0, configCentral.pairCut->get("K0")); - mPairCuts.SetPairCut(PairCuts::Lambda, configCentral.pairCut->get("Lambda")); - mPairCuts.SetPairCut(PairCuts::Phi, configCentral.pairCut->get("Phi")); - mPairCuts.SetPairCut(PairCuts::Rho, configCentral.pairCut->get("Rho")); + mPairCuts.SetHistogramRegistry(®istry); + if (configCentral.pairCut->get("Photon") > 0 || configCentral.pairCut->get("K0") > 0 || configCentral.pairCut->get("Lambda") > 0 || configCentral.pairCut->get("Phi") > 0 || configCentral.pairCut->get("Rho") > 0) { + mPairCuts.SetPairCut(PairCuts::Photon, configCentral.pairCut->get("Photon")); + mPairCuts.SetPairCut(PairCuts::K0, configCentral.pairCut->get("K0")); + mPairCuts.SetPairCut(PairCuts::Lambda, configCentral.pairCut->get("Lambda")); + mPairCuts.SetPairCut(PairCuts::Phi, configCentral.pairCut->get("Phi")); + mPairCuts.SetPairCut(PairCuts::Rho, configCentral.pairCut->get("Rho")); + } } // ========================= @@ -532,6 +679,18 @@ struct HfTaskFlow { {configAxis.axisMultiplicity, "multiplicity"}, {configAxis.axisDeltaPhi, "#Delta#varphi (rad)"}, {configAxis.axisVertex, "z-vtx (cm)"}}; + std::vector const corrAxisEta = {{configAxis.axisSamples, "sampling"}, + {configAxis.axisVertex, "z-vtx (cm)"}, + {configAxis.axisEtaAssociated, "#eta_{associated}"}, + {configAxis.axisEtaTrigger, "#eta_{trigger}"}, + {configAxis.axisDeltaPhi, "#Delta#varphi (rad)"}, + {configAxis.axisDeltaEta, "#Delta#eta"}}; + std::vector const corrAxisVariations = {{configAxis.axisSamples, "sampling"}, + {configAxis.axisVertex, "z-vtx (cm)"}, + {configAxis.axisPtTrigger, "p_{T} (GeV/c)"}, + {configAxis.axisMultiplicity, "multiplicity"}, + {configAxis.axisDeltaPhi, "#Delta#varphi (rad)"}, + {configAxis.axisDeltaEta, "#Delta#eta"}}; std::vector const effAxis = {{configAxis.axisEtaEfficiency, "#eta"}, {configAxis.axisPtEfficiency, "p_{T} (GeV/c)"}, {configAxis.axisVertexEfficiency, "z-vtx (cm)"}}; @@ -545,21 +704,32 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcTpcChCh) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + if (!configTask.doEtaDependentFlow) { + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + } else { + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisEta, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisEta, effAxis, {})); + } } if (doprocessSameTpcTpcD0Ch) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcTpcLcCh) { - addHistograms(); + if (!configTask.removeQAForSystematics) { + addHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -569,25 +739,48 @@ struct HfTaskFlow { // Initialization of histograms and CorrelationContainers for TpcMft cases // ========================= - if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous) { - addHistograms(); - addMftHistograms(); + // if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous || doprocessSameTpcMftChChReassociated3dMcReconstructed) { + if (doprocessSameTpcMftChCh || doprocessSameTpcMftChChReassociated3d || doprocessSameTpcMftChChNonAmbiguous || doprocessSameTpcMftChChReassociated3dMcReconstructed) { + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + registry.add("Data/hMultiplicity_uncorrected_vs_corrected", "", {HistType::kTH2D, {{configAxis.axisMultiplicity}, {configAxis.axisMultiplicity}}}); + } - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + registry.add("Trig_hist_TPC_MFT", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + } else if (configTask.doEtaDependentFlow) { + registry.add("Trig_hist_TPC_MFT", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisEtaTrigger}}}); + sameEventTpcMft.setObject(new CorrelationContainer("sameEvent_TPC_MFT", "sameEvent_TPC_MFT", corrAxisEta, effAxis, {})); + mixedEventTpcMft.setObject(new CorrelationContainer("mixedEvent_TPC_MFT", "mixedEvent_TPC_MFT", corrAxisEta, effAxis, {})); + } else { + registry.add("Trig_hist_TPC_MFT", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEventTpcMft.setObject(new CorrelationContainer("sameEvent_TPC_MFT", "sameEvent_TPC_MFT", corrAxisVariations, effAxis, {})); + mixedEventTpcMft.setObject(new CorrelationContainer("mixedEvent_TPC_MFT", "mixedEvent_TPC_MFT", corrAxisVariations, effAxis, {})); + } } - if (doprocessSameTpcMftD0Ch || doprocessSameTpcMftD0ChReassociated) { - addHistograms(); - addMftHistograms(); + // if (doprocessSameTpcMftD0Ch || doprocessSameTpcMftD0ChReassociated) { + if (doprocessSameTpcMftD0Ch) { + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } - if (doprocessSameTpcMftLcCh || doprocessSameTpcMftLcChReassociated) { - addHistograms(); - addMftHistograms(); + // if (doprocessSameTpcMftLcCh || doprocessSameTpcMftLcChReassociated) { + if (doprocessSameTpcMftLcCh) { + + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -597,66 +790,94 @@ struct HfTaskFlow { // Initialization of histograms and CorrelationContainers for TpcFv0a cases // ========================= - if (doprocessSameTpcFv0aChCh) { - addHistograms(); + // if (doprocessSameTpcFv0aChCh) { + // addHistograms(); - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); - } + // if (!configTask.doEtaDependentFlow) { + // sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + // mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + // } else { + // sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisEta, effAxis, {})); + // mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisEta, effAxis, {})); + // } + // } - if (doprocessSameTpcFv0aD0Ch) { - addHistograms(); + // if (doprocessSameTpcFv0aD0Ch) { + // addHistograms(); - sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); - mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); - } + // sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); + // mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); + // } - if (doprocessSameTpcFv0aLcCh) { - addHistograms(); + // if (doprocessSameTpcFv0aLcCh) { + // addHistograms(); - sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); - mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); - } + // sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); + // mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); + // } // ========================= // Initialization of histograms and CorrelationContainers for MftFv0a cases // ========================= - // if (doprocessSameMftFv0aChCh || doprocessSameMftFv0aChChReassociated || doprocessSameMftFv0aReassociated3d || doprocessSameMftFv0aChChNonAmbiguous) { - if (doprocessSameMftFv0aChCh || doprocessSameMftFv0aChChReassociated || doprocessSameMftFv0aChChNonAmbiguous) { - addHistograms(); - addMftHistograms(); + // // if (doprocessSameMftFv0aChCh || doprocessSameMftFv0aChChReassociated || doprocessSameMftFv0aReassociated3d || doprocessSameMftFv0aChChNonAmbiguous) { + // if (doprocessSameMftFv0aChCh || doprocessSameMftFv0aChChReassociated || doprocessSameMftFv0aChChNonAmbiguous) { + // addHistograms(); + // addMftHistograms(); - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); - } + // if (!configTask.doEtaDependentFlow) { + // sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + // mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + // } else { + // sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisEta, effAxis, {})); + // mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisEta, effAxis, {})); + // } + // } // ========================= // Initialization of histograms and CorrelationContainers for TpcFt0a cases // ========================= if (doprocessSameTpcFt0aChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + } - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + } else if (configTask.doEtaDependentFlow) { + registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisEtaTrigger}}}); + sameEventTpcFt0a.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisEta, effAxis, {})); + mixedEventTpcFt0a.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisEta, effAxis, {})); + } else { + registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEventTpcFt0a.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisVariations, effAxis, {})); + mixedEventTpcFt0a.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisVariations, effAxis, {})); + } } if (doprocessSameTpcFt0aD0Ch) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcFt0aLcCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -666,14 +887,28 @@ struct HfTaskFlow { // Initialization of histograms and CorrelationContainers for MftFt0a cases // ========================= - if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous) { - addHistograms(); - addMftHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + // if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous) { + if (doprocessSameMftFt0aChCh || doprocessSameMftFt0aChChReassociated3d || doprocessSameMftFt0aChChNonAmbiguous || doprocessSameMftFt0aChChReassociated3dMcReconstructed) { - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addMftHistograms(); + addFt0Histograms(); + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + registry.add("Trig_hist_MFT_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + } else if (configTask.doEtaDependentFlow) { + registry.add("Trig_hist_MFT_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisEtaTrigger}}}); + sameEventMftFt0a.setObject(new CorrelationContainer("sameEvent_MFT_FT0A", "sameEvent_MFT_FT0A", corrAxisEta, effAxis, {})); + mixedEventMftFt0a.setObject(new CorrelationContainer("mixedEvent_MFT_FT0A", "mixedEvent_MFT_FT0A", corrAxisEta, effAxis, {})); + } else { + registry.add("Trig_hist_MFT_FT0A", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEventMftFt0a.setObject(new CorrelationContainer("sameEvent_MFT_FT0A", "sameEvent_MFT_FT0A", corrAxisVariations, effAxis, {})); + mixedEventMftFt0a.setObject(new CorrelationContainer("mixedEvent_MFT_FT0A", "mixedEvent_MFT_FT0A", corrAxisVariations, effAxis, {})); + } } // ========================= @@ -681,27 +916,46 @@ struct HfTaskFlow { // ========================= if (doprocessSameTpcFt0cChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + } else if (configTask.doEtaDependentFlow) { + registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisEtaTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisEta, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisEta, effAxis, {})); + } else { + registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisVariations, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisVariations, effAxis, {})); + } } if (doprocessSameTpcFt0cD0Ch) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); } if (doprocessSameTpcFt0cLcCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + + if (!configTask.removeQAForSystematics) { + addHistograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } sameEventHf.setObject(new CorrelationContainer("sameEventHf", "sameEventHf", corrAxis, effAxis, hfUserAxis)); mixedEventHf.setObject(new CorrelationContainer("mixedEventHf", "mixedEventHf", corrAxis, effAxis, hfUserAxis)); @@ -712,14 +966,95 @@ struct HfTaskFlow { // ========================= if (doprocessSameFt0aFt0cChCh) { - addHistograms(); - registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); - sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); - mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + if (!configTask.removeQAForSystematics) { + addHistograms(); + addFt0Histograms(); + registry.add("Data/FT0Amp", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + registry.add("Data/FT0AmpCorr", "", {HistType::kTH2F, {configAxis.axisChID, configAxis.axisAmplitudeFit}}); + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + } else if (configTask.doEtaDependentFlow) { + registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisEtaTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisEta, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisEta, effAxis, {})); + } else { + registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisVariations, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisVariations, effAxis, {})); + } + } + + // ========================= + // Initialization of histograms and CorrelationContainers for McGen cases + // ========================= + + if (doprocessSameMcGen) { + + registry.add(Form("MC/hCentrality_%s", WhatCentralityEstimator[configCollision.centralityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hCentralityUsed", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMultiplicity_multMCFT0C", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMultiplicityTracksSize", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMultiplicityUsed", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + + registry.add("MC/hMixedMultiplicity_multMCFT0C_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityTracksSize_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityUsed_coll1", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedCentrality_coll1", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedCentralityUsed_coll1", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedMultiplicity_multMCFT0C_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityTracksSize_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedMultiplicityUsed_coll2", "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add("MC/hMixedCentrality_coll2", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + registry.add("MC/hMixedCentralityUsed_coll2", "", {HistType::kTH1D, {configAxis.axisCentrality}}); + + if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFv0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFv0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFt0a)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0c)) { + addHistograms(); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::Ft0aFt0c)) { + addHistograms(); + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxis, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxis, effAxis, {})); + } else if (configTask.doEtaDependentFlow) { + registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisEtaTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisEta, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisEta, effAxis, {})); + } else { + registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{configAxis.axisSamples, configAxis.axisVertex, configAxis.axisPtTrigger}}}); + sameEvent.setObject(new CorrelationContainer("sameEvent", "sameEvent", corrAxisVariations, effAxis, {})); + mixedEvent.setObject(new CorrelationContainer("mixedEvent", "mixedEvent", corrAxisVariations, effAxis, {})); + } + } + + if (doprocessTrackEfficiencies) { + if (!configTask.removeQAForSystematics) { + registry.add("MC/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); + registry.add("MC/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + registry.add("Data/hEfficiencyTrigger", "", {HistType::kTH3D, {{configAxis.axisPtTrigger}, {configAxis.axisEtaTrigger}, {configAxis.axisVertex}}}); + registry.add("Data/hEfficiencyAssociated", "", {HistType::kTH3D, {{configAxis.axisPtAssoc}, {configAxis.axisEtaAssociated}, {configAxis.axisVertex}}}); + } } + registry.print(); } // End of init() function // ========================= @@ -748,41 +1083,41 @@ struct HfTaskFlow { // Helper functions // ========================= - HfProngSpecies getSpecies(int pdgCode) - { - switch (std::abs(pdgCode)) { - case PDG_t::kPiPlus: // positive or negative pion - return HfProngSpecies::Pion; - case PDG_t::kKPlus: // positive or negative kaon - return HfProngSpecies::Kaon; - case PDG_t::kProton: // proton or proton bar - return HfProngSpecies::Proton; - default: // NOTE. The efficiency histogram is hardcoded to contain 4 species. Anything special will have the last slot. - return HfProngSpecies::NHfProngSpecies; - } - } + // HfProngSpecies getSpecies(int pdgCode) + // { + // switch (std::abs(pdgCode)) { + // case PDG_t::kPiPlus: // positive or negative pion + // return HfProngSpecies::Pion; + // case PDG_t::kKPlus: // positive or negative kaon + // return HfProngSpecies::Kaon; + // case PDG_t::kProton: // proton or proton bar + // return HfProngSpecies::Proton; + // default: // NOTE. The efficiency histogram is hardcoded to contain 4 species. Anything special will have the last slot. + // return HfProngSpecies::NHfProngSpecies; + // } + // } template - float getMultiplicityEstimator(TCollision collision, bool isSameEvent) + int64_t getMultiplicityEstimator(const TCollision& collision, bool isSameEvent) { switch (configCollision.multiplicityEstimator) { case MultiplicityEstimators::MultNTracksPV: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multNTracksPV"), collision.multNTracksPV()); } return collision.multNTracksPV(); case MultiplicityEstimators::MultNumContrib: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multNumContrib"), collision.numContrib()); } return collision.numContrib(); case MultiplicityEstimators::MultFT0C: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multFT0C"), collision.multFT0C()); } return collision.multFT0C(); case MultiplicityEstimators::MultFT0M: - if (isSameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hMultiplicity_multFT0M"), collision.multFT0M()); } return collision.multFT0M(); @@ -791,6 +1126,30 @@ struct HfTaskFlow { } } + template + float getCentralityEstimator(TCollision const& collision, bool isSameEvent) + { + switch (configCollision.centralityEstimator) { + case CentralityEstimators::CentFT0C: + if (isSameEvent && !configTask.removeQAForSystematics) { + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0C"), collision.centFT0C()); + } + return collision.centFT0C(); + case CentralityEstimators::CentFT0CVariant1: + if (isSameEvent && !configTask.removeQAForSystematics) { + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0CVariant1"), collision.centFT0CVariant1()); + } + return collision.centFT0CVariant1(); + case CentralityEstimators::CentFT0M: + if (isSameEvent && !configTask.removeQAForSystematics) { + registry.fill(HIST(WhatDataType[DataType]) + HIST("hCentrality_centFT0M"), collision.centFT0M()); + } + return collision.centFT0M(); + default: + return collision.centFT0C(); + } + } + template float getDPhiStar(TTrack const& track1, TTrackAssoc const& track2, float radius, int magField) { @@ -839,29 +1198,29 @@ struct HfTaskFlow { return RecoDecay::phi(chPos.X() + (*offsetFT0)[i].getX(), chPos.Y() + (*offsetFT0)[i].getY()); } - double getPhiFV0(unsigned int chno) const - { - int const cellsInLeft[] = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; - bool const isChnoInLeft = std::find(std::begin(cellsInLeft), std::end(cellsInLeft), chno) != std::end(cellsInLeft); - float offsetX{}, offsetY{}; - if (isChnoInLeft) { - offsetX = (*offsetFV0)[0].getX(); - offsetY = (*offsetFV0)[0].getY(); - } else { - offsetX = (*offsetFV0)[1].getX(); - offsetY = (*offsetFV0)[1].getY(); - } - - o2::fv0::Point3Dsimple chPos{}; - chPos = fv0Det->getReadoutCenter(chno); - - // if (configTask.isReadoutCenter) - // chPos = fv0Det->getReadoutCenter(chno); - // else - // chPos = fv0Det->getCellCenter(chno); - - return RecoDecay::phi(chPos.x + offsetX, chPos.y + offsetY); - } + // double getPhiFV0(unsigned int chno) const + // { + // int const cellsInLeft[] = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; + // bool const isChnoInLeft = std::find(std::begin(cellsInLeft), std::end(cellsInLeft), chno) != std::end(cellsInLeft); + // float offsetX{}, offsetY{}; + // if (isChnoInLeft) { + // offsetX = (*offsetFV0)[0].getX(); + // offsetY = (*offsetFV0)[0].getY(); + // } else { + // offsetX = (*offsetFV0)[1].getX(); + // offsetY = (*offsetFV0)[1].getY(); + // } + + // o2::fv0::Point3Dsimple chPos{}; + // chPos = fv0Det->getReadoutCenter(chno); + + // // if (configTask.isReadoutCenter) + // // chPos = fv0Det->getReadoutCenter(chno); + // // else + // // chPos = fv0Det->getCellCenter(chno); + + // return RecoDecay::phi(chPos.x + offsetX, chPos.y + offsetY); + // } double getEtaFT0(uint chno, int i) { @@ -878,35 +1237,35 @@ struct HfTaskFlow { return -std::log(std::tan(0.5 * theta)); } - double getEtaFV0(unsigned int chno) const - { - int const cellsInLeft[] = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; - bool const isChnoInLeft = std::find(std::begin(cellsInLeft), std::end(cellsInLeft), chno) != std::end(cellsInLeft); - float offsetX{}, offsetY{}, offsetZ{}; - if (isChnoInLeft) { - offsetX = (*offsetFV0)[0].getX(); - offsetY = (*offsetFV0)[0].getY(); - offsetZ = (*offsetFV0)[0].getZ(); - } else { - offsetX = (*offsetFV0)[1].getX(); - offsetY = (*offsetFV0)[1].getY(); - offsetZ = (*offsetFV0)[1].getZ(); - } - - o2::fv0::Point3Dsimple chPos{}; - chPos = fv0Det->getReadoutCenter(chno); - // if (configTask.isReadoutCenter) - // chPos = fv0Det->getReadoutCenter(chno); - // else - // chPos = fv0Det->getCellCenter(chno); - - auto x = chPos.x + offsetX; - auto y = chPos.y + offsetY; - auto z = chPos.z + offsetZ; - auto r = std::sqrt(x * x + y * y); - auto theta = std::atan2(r, z); - return -std::log(std::tan(0.5 * theta)); - } + // double getEtaFV0(unsigned int chno) const + // { + // int const cellsInLeft[] = {0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27, 32, 40, 33, 41, 34, 42, 35, 43}; + // bool const isChnoInLeft = std::find(std::begin(cellsInLeft), std::end(cellsInLeft), chno) != std::end(cellsInLeft); + // float offsetX{}, offsetY{}, offsetZ{}; + // if (isChnoInLeft) { + // offsetX = (*offsetFV0)[0].getX(); + // offsetY = (*offsetFV0)[0].getY(); + // offsetZ = (*offsetFV0)[0].getZ(); + // } else { + // offsetX = (*offsetFV0)[1].getX(); + // offsetY = (*offsetFV0)[1].getY(); + // offsetZ = (*offsetFV0)[1].getZ(); + // } + + // o2::fv0::Point3Dsimple chPos{}; + // chPos = fv0Det->getReadoutCenter(chno); + // // if (configTask.isReadoutCenter) + // // chPos = fv0Det->getReadoutCenter(chno); + // // else + // // chPos = fv0Det->getCellCenter(chno); + + // auto x = chPos.x + offsetX; + // auto y = chPos.y + offsetY; + // auto z = chPos.z + offsetZ; + // auto r = std::sqrt(x * x + y * y); + // auto theta = std::atan2(r, z); + // return -std::log(std::tan(0.5 * theta)); + // } template void getChannel(TFT0s const& ft0, std::size_t const& iCh, int& id, int fitType, float& amplitude) @@ -923,15 +1282,19 @@ struct HfTaskFlow { } else { LOGF(fatal, "Cor Index %d out of range", fitType); } - registry.fill(HIST("Data/FT0Amp"), rID, amplitude); - amplitude = amplitude / cstFT0RelGain[iCh]; - registry.fill(HIST("Data/FT0AmpCorr"), rID, amplitude); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0Amp"), rID, amplitude); + } + amplitude = amplitude / cstFT0RelGain[id]; + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0AmpCorr"), rID, amplitude); + } } void loadGain(aod::BCsWithTimestamps::iterator const& bc) { cstFT0RelGain.clear(); - cstFT0RelGain = {}; + // cstFT0RelGain = {}; std::string fullPath; auto timestamp = bc.timestamp(); @@ -954,15 +1317,214 @@ struct HfTaskFlow { } } + int isInnerOrOuter(int channelId, int fitType) + { + if (fitType == isFT0C) { + if (channelId >= FT0DetectorChannels::FT0CInnerRingMin && channelId <= FT0DetectorChannels::FT0CInnerRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0InnerRing); + } + return FT0InnerOrOuter::FT0InnerRing; + } else if (channelId >= FT0DetectorChannels::FT0COuterRingMin && channelId <= FT0DetectorChannels::FT0COuterRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); + } + return FT0InnerOrOuter::FT0OuterRing; + } + LOGF(fatal, "Channel ID %d out of range for FT0C", channelId); + return FT0InnerOrOuter::FT0Error; + } else if (fitType == isFT0A) { + if (channelId >= FT0DetectorChannels::FT0AInnerRingMin && channelId <= FT0DetectorChannels::FT0AInnerRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0InnerRing); + } + return FT0InnerOrOuter::FT0InnerRing; + } else if (channelId >= FT0DetectorChannels::FT0AOuterRingMin && channelId <= FT0DetectorChannels::FT0AOuterRingMax) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/FT0IsInnerOrOuter"), FT0InnerOrOuter::FT0OuterRing); + } + return FT0InnerOrOuter::FT0OuterRing; + } + LOGF(fatal, "Channel ID %d out of range for FT0A", channelId); + return FT0InnerOrOuter::FT0Error; + } else { + LOGF(fatal, "Fit type %d is not valid for FT0", fitType); + return FT0InnerOrOuter::FT0Error; + } + } + + void loadEfficiencyCorrection(uint64_t timestamp) + { + if (areCorrectionsLoaded) { + return; + } + if (!configTask.loadEfficienciesForTpc.value.empty()) { + mEfficiencyTpc = ccdb->getForTimeStamp(configTask.loadEfficienciesForTpc, timestamp); + if (mEfficiencyTpc == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for TPC tracks from %s", configTask.loadEfficienciesForTpc.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForTpc.value.c_str(), static_cast(mEfficiencyTpc)); + } + if (!configTask.loadEfficienciesForMft.value.empty()) { + mEfficiencyMft = ccdb->getForTimeStamp(configTask.loadEfficienciesForMft, timestamp); + if (mEfficiencyMft == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for MFT tracks from %s", configTask.loadEfficienciesForMft.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForMft.value.c_str(), static_cast(mEfficiencyMft)); + } + if (!configTask.loadEfficienciesForNch.value.empty()) { + mEfficiencyNch = ccdb->getForTimeStamp(configTask.loadEfficienciesForNch, timestamp); + if (!mEfficiencyNch) { + LOGF(fatal, "Could not load efficiency histogram for Nch estimator from %s", configTask.loadEfficienciesForNch.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", configTask.loadEfficienciesForNch.value.c_str(), static_cast(mEfficiencyNch)); + } + if (!configTask.loadCentralityWeight.value.empty()) { + mCentralityWeight = ccdb->getForTimeStamp(configTask.loadCentralityWeight, timestamp); + if (mCentralityWeight == nullptr) { + LOGF(fatal, "Could not load centrality weight correction from %s", configTask.loadCentralityWeight.value.c_str()); + } + LOGF(info, "Loaded centrality weight from %s (%p)", configTask.loadCentralityWeight.value.c_str(), static_cast(mCentralityWeight)); + } + areCorrectionsLoaded = true; + } + + bool getEfficiencyCorrectionTpc(float& weightTpc, float pt, float eta, float vertex) + { + float efficiencyTpc = 1.; + + if (mEfficiencyTpc) { + int ptBin = mEfficiencyTpc->GetXaxis()->FindBin(pt); + int etaBin = mEfficiencyTpc->GetYaxis()->FindBin(eta); + int vertexBin = mEfficiencyTpc->GetZaxis()->FindBin(vertex); + efficiencyTpc = mEfficiencyTpc->GetBinContent(ptBin, etaBin, vertexBin); + } + + if (efficiencyTpc == 0) { + return false; + } + + weightTpc = 1. / efficiencyTpc; + + return true; + } + + bool getEfficiencyCorrectionMft(float& weightMft, float pt, float eta, float vertex) + { + float efficiencyMft = 1.; + + if (mEfficiencyMft) { + int ptBin = mEfficiencyMft->GetXaxis()->FindBin(pt); + int etaBin = mEfficiencyMft->GetYaxis()->FindBin(eta); + int vertexBin = mEfficiencyMft->GetZaxis()->FindBin(vertex); + efficiencyMft = mEfficiencyMft->GetBinContent(ptBin, etaBin, vertexBin); + } + + if (efficiencyMft == 0) { + return false; + } + + weightMft = 1. / efficiencyMft; + + return true; + } + + template + float getCorrectedMultiplicity(const TTracks& tracks) // function to count the number of tracks in the event and fill the histogram + { + float trackCounter = 0.f; + auto weightMultiplicity = 1.0f; + for (auto const& track : tracks) { + + if (std::abs(track.eta()) > configCentral.etaCentralTrackMax || track.pt() <= configCentral.ptCentralTrackMin || track.pt() >= configCentral.ptCentralTrackMax) { + continue; + } + + auto efficiencyNch = 1.0f; + if (configTask.useEfficiencyCorrection) { + + if (mEfficiencyNch) { + int ptBin = mEfficiencyNch->FindBin(track.pt()); + efficiencyNch = mEfficiencyNch->GetBinContent(ptBin); + } + + if (efficiencyNch == 0) { + // return false; + efficiencyNch = 1.0f; + } + + weightMultiplicity = 1. / efficiencyNch; + } else { + weightMultiplicity = 1.0f; + } + + trackCounter += weightMultiplicity; + } + return trackCounter; + } + + bool getCentralityWeight(float& weightCent, const float centrality) + { + float weight = 1.; + if (mCentralityWeight) { + weight = mCentralityWeight->GetBinContent(mCentralityWeight->FindBin(centrality)); + } else { + weight = 1.0; + } + if (weight == 0) { + return false; + } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hCentralityWeighted"), centrality, weight); + } + + weightCent = weight; + return true; + } + // ========================= // Cuts with functions // ========================= + template + bool isAcceptedEventActivity(TCollision const& collision, TTracks const& tracks, auto& multiplicity, float& centrality, float& centralityWeight) + { + if (configCollision.useMultiplicityFromTracks) { + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicityTracksSize"), tracks.size()); + } + multiplicity = tracks.size(); + } else { + multiplicity = getMultiplicityEstimator(collision, true); + } + + centrality = getCentralityEstimator(collision, true); + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, centrality); + if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { + return false; + } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hCentralityUsed"), centrality); + } + } else { + if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + return false; + } + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicityUsed"), multiplicity); + } + } + + return true; + } + // FIXME: Some collisions are rejected here, what causes (part of) differences with the D0 task template bool isAcceptedCollision(TCollision const& collision, bool fillHistograms = false) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AllEvents); registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::AllEventsPrecise); } @@ -970,55 +1532,55 @@ struct HfTaskFlow { if (!collision.sel8()) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsSel8); } if (configCollision.isApplySameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoSameBunchPileup); } if (configCollision.isApplyGoodItsLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodItsLayersAll); } if (configCollision.isApplyGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodZvtxFT0vsPV); } if (configCollision.isApplyNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStandard); } if (configCollision.isApplyNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStrict); } if (configCollision.isApplyNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStandard); } if (configCollision.isApplyNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStrict); } if (configCollision.isApplyNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsNoHighMultCollInPrevRof); } if (configCollision.requireRCTFlagChecker && !rctChecker(collision)) { @@ -1027,16 +1589,21 @@ struct HfTaskFlow { if (configCollision.requireCorrelationAnalysisRCTFlagChecker && !correlationAnalysisRctChecker(collision)) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsRctFlagChecked); } + if (configCollision.useNoFoundFT0Cut && !collision.has_foundFT0()) { + return false; + } + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hPreciseEventCounter"), SpecificEventSelectionStep::IsFoundFT0); + } - if (fillHistograms) { + if (!configTask.removeQAForSystematics) { registry.fill(HIST("Data/hEventCounter"), EventSelectionStep::AfterEventSelection); + registry.fill(HIST("Data/hVtxZ"), collision.posZ()); } - registry.fill(HIST("Data/hVtxZ"), collision.posZ()); - return true; } @@ -1090,14 +1657,14 @@ struct HfTaskFlow { // I tried to put it as a filter, but filters for normal TPC tracks also apply to MFT tracks I think // and it seems that they are not compatible template - bool isAcceptedMftTrack(TTrack const& mftTrack, bool fillHistograms) + bool isAcceptedMftTrack(TTrack const& mftTrack, float dcaXY, float dcaZ, bool fillHistograms) { // cut on the eta of MFT tracks if (mftTrack.eta() > configMft.etaMftTrackMax || mftTrack.eta() < configMft.etaMftTrackMin) { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Eta); } @@ -1106,7 +1673,7 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Cluster); } @@ -1115,10 +1682,74 @@ struct HfTaskFlow { return false; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hPtMft"), mftTrack.pt()); registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Pt); } + if (configMft.cutOnDcaXY && std::abs(dcaXY) > configMft.mftMaxDCAxy) { + return false; + } + + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::DCAxy); + } + if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.mftMaxDCAz) { + return false; + } + + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hDcaXYMft"), dcaXY); + registry.fill(HIST("Data/Mft/hDcaZMft"), dcaZ); + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::DCAz); + } + + if (configMft.useMftChi2OverNdfCut) { + float ndfMftTrack = std::max(2.0f * mftTrack.nClusters() - 5.0f, 1.0f); + float mftChi2OverNdf = mftTrack.chi2() / ndfMftTrack; + if (mftChi2OverNdf > configMft.maxChi2OverNdf) { + return false; + } + } + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hChi2OverNdfMft"), mftTrack.chi2() / std::max(2.0f * mftTrack.nClusters() - 5.0f, 1.0f)); + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Chi2OverNdf); + } + + if (configMft.useMftPhiCut) { + auto phiMftTrack = mftTrack.phi(); + o2::math_utils::bringTo02Pi(phiMftTrack); + if (phiMftTrack > configMft.phiMftTrackMax || phiMftTrack < configMft.phiMftTrackMin || + (phiMftTrack > configMft.phiMftTrackMiddleMin && phiMftTrack < configMft.phiMftTrackMiddleMax)) { + return false; + } + } + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::Phi); + } + + // cut on the track algorithm of MFT tracks + if (mftTrack.isCA()) { + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::IsCA); + registry.fill(HIST("Data/Mft/hPtVsClustersCA"), mftTrack.pt(), mftTrack.nClusters()); + } + + if (configMft.useOnlyLTFTracks) { + return false; + } + + } else { + if (fillHistograms && !configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::IsLTF); + registry.fill(HIST("Data/Mft/hPtVsClustersLTF"), mftTrack.pt(), mftTrack.nClusters()); + } + + if (configMft.useOnlyCATracks) { + return false; + } + } + return true; } @@ -1127,13 +1758,13 @@ struct HfTaskFlow { bool isAmbiguousMftTrack(TTrack const& mftTrack, bool fillHistograms) { if (mftTrack.ambDegree() > 1) { - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); } return true; } - if (fillHistograms) { + if (fillHistograms && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); } return false; @@ -1151,16 +1782,16 @@ struct HfTaskFlow { template void fillCorrelations(TTarget target, CorrelationContainer::CFStep step, - TTracksTrig const& tracks1, TTracksAssoc const& tracks2, - float multiplicity, float posZ, bool sameEvent, int magneticField) + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, + float multiplicity, float posZ, bool isSameEvent, int magneticField, float centralityWeight) { - auto triggerWeight = 1; - auto associatedWeight = 1; + auto triggerWeight = 1.0f; + auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - // int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE - for (const auto& track1 : tracks1) { + for (const auto& track1 : tracksTrigger) { loopCounter++; if constexpr (std::is_same_v) { @@ -1173,6 +1804,10 @@ struct HfTaskFlow { float const pt1 = track1.pt(); float const phi1 = track1.phi(); + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionTpc(triggerWeight, pt1, eta1, posZ)) { + continue; + } + // TODO: add getter for NUE trigger efficiency here // calculating inv. mass to be filled into the container below @@ -1193,13 +1828,21 @@ struct HfTaskFlow { // fill single-track distributions if (!fillingHFcontainer) { // if not HF-h case - target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, triggerWeight); + target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, centralityWeight * triggerWeight); + + if (configTask.doEtaDependentFlow) { + registry.fill(HIST("Trig_hist_TPC_MFT"), sampleIndex, posZ, track1.eta(), centralityWeight * triggerWeight); + } + if (configTask.doVariationContainers) { + registry.fill(HIST("Trig_hist_TPC_MFT"), sampleIndex, posZ, track1.pt(), centralityWeight * triggerWeight); + } + } else { - target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, invmass, triggerWeight); + target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, invmass, centralityWeight * triggerWeight); } // FILL QA PLOTS for trigger particle - if (sameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // IF TPC-TPC case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-TPC D0-h fillTriggerQa(multiplicity, eta1, phi1, pt1); @@ -1220,7 +1863,7 @@ struct HfTaskFlow { } // ASSOCIATED PARTICLE - for (const auto& track2 : tracks2) { + for (const auto& track2 : tracksAssoc) { if constexpr (std::is_same_v) { if (!isAcceptedCentralTrack(track2)) { @@ -1231,14 +1874,16 @@ struct HfTaskFlow { // apply cuts for MFT tracks if constexpr (std::is_same_v) { - if (sameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time - registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } - if (!isAcceptedMftTrack(track2, true)) { + if (!isAcceptedMftTrack(track2, 0.f, 0.f, true)) { continue; } } else { // After the first loop, we don't fill the plots anymore but still do the selection - if (!isAcceptedMftTrack(track2, false)) { + if (!isAcceptedMftTrack(track2, 0.f, 0.f, false)) { continue; } } @@ -1248,10 +1893,10 @@ struct HfTaskFlow { continue; } - if (configCentral.isApplyPtOrderingSameEvent && sameEvent && (track1.pt() <= track2.pt())) { + if (configCentral.isApplyPtOrderingSameEvent && isSameEvent && (track1.pt() <= track2.pt())) { continue; } - if (configCentral.isApplyPtOrderingMixedEvent && !sameEvent && (track1.pt() <= track2.pt())) { + if (configCentral.isApplyPtOrderingMixedEvent && !isSameEvent && (track1.pt() <= track2.pt())) { continue; } @@ -1275,19 +1920,19 @@ struct HfTaskFlow { const double kLimit = 3.0 * configCentral.mergingCut; - bool bIsBelow = false; + [[maybe_unused]] bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(configCentral.minMergingRadius); rad < configCentral.maxMergingRadius; rad += 0.01) { // FIXME: Variable 'rad' with floating point type 'double' should not be used as a loop counter. + constexpr double StepRad = 0.01; + const int nSteps = static_cast((configCentral.maxMergingRadius - configCentral.minMergingRadius) / StepRad); + for (int iStep = 1; iStep < nSteps; ++iStep) { + double rad = configCentral.minMergingRadius + iStep * StepRad; double dPhiStar = getDPhiStar(track1, track2, rad, magneticField); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) { - continue; - } } } } @@ -1312,6 +1957,17 @@ struct HfTaskFlow { float phi2 = track2.phi(); o2::math_utils::bringTo02Pi(phi2); + if constexpr (std::is_same_v) { + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionTpc(associatedWeight, pt2, eta2, posZ)) { + continue; + } + } + if constexpr (std::is_same_v) { + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionMft(associatedWeight, pt2, eta2, posZ)) { + continue; + } + } + // TODO: add getter for NUE associated efficiency here float deltaPhi = phi1 - phi2; @@ -1320,15 +1976,23 @@ struct HfTaskFlow { if (!fillingHFcontainer) { // fill pair correlations - target->getPairHist()->Fill(step, eta1 - eta2, pt2, pt1, multiplicity, deltaPhi, posZ, - triggerWeight * associatedWeight); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + target->getPairHist()->Fill(step, eta1 - eta2, pt2, pt1, multiplicity, deltaPhi, posZ, + centralityWeight * triggerWeight * associatedWeight); + } else if (configTask.doEtaDependentFlow) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, + centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, eta1 - eta2, pt2, pt1, multiplicity, deltaPhi, posZ, invmass, - triggerWeight * associatedWeight); + centralityWeight * triggerWeight * associatedWeight); } // FILL QA PLOTS for associated particle - if (sameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // IF TPC-TPC case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-TPC D0-h fillAssociatedQa(multiplicity, eta2, phi2); @@ -1340,13 +2004,10 @@ struct HfTaskFlow { } else { // IF TPC-MFT case if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-MFT D0-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else if constexpr (std::is_same_v) { // IF LC CASE -> TPC-MFT Lc-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else { // IF NEITHER D0 NOR LC -> TPC-MFT h-h fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } // end of if condition for TPC-TPC or TPC-MFT case } } @@ -1355,18 +2016,18 @@ struct HfTaskFlow { } // end of loop over tracks 1 } - template + template void fillCorrelationsReassociatedMftTracks(TTarget target, CorrelationContainer::CFStep step, - TTracksTrig const& tracks1, TTracksAssoc const& tracks2, - float multiplicity, float posZ, bool sameEvent, bool cutAmbiguousTracks) + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, + float multiplicity, float posZ, bool isSameEvent, bool cutAmbiguousTracks, float centralityWeight) { - auto triggerWeight = 1; - auto associatedWeight = 1; + auto triggerWeight = 1.0f; + auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - // int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE - for (const auto& track1 : tracks1) { + for (const auto& track1 : tracksTrigger) { loopCounter++; if constexpr (std::is_same_v) { @@ -1379,6 +2040,10 @@ struct HfTaskFlow { float const pt1 = track1.pt(); float const phi1 = track1.phi(); + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionTpc(triggerWeight, pt1, eta1, posZ)) { + continue; + } + bool fillingHFcontainer = false; double invmass = 0; if constexpr (std::is_same_v || std::is_same_v) { @@ -1395,13 +2060,21 @@ struct HfTaskFlow { // fill single-track distributions if (!fillingHFcontainer) { // if not HF-h case - target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, triggerWeight); + target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, centralityWeight * triggerWeight); + + if (configTask.doEtaDependentFlow) { + registry.fill(HIST("Trig_hist_TPC_MFT"), sampleIndex, posZ, track1.eta(), centralityWeight * triggerWeight); + } + if (configTask.doVariationContainers) { + registry.fill(HIST("Trig_hist_TPC_MFT"), sampleIndex, posZ, track1.pt(), centralityWeight * triggerWeight); + } + } else { - target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, invmass, triggerWeight); + target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, invmass, centralityWeight * triggerWeight); } // FILL QA PLOTS for trigger particle - if (sameEvent) { + if (isSameEvent && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else if constexpr (std::is_same_v) { @@ -1412,29 +2085,60 @@ struct HfTaskFlow { } // ASSOCIATED PARTICLE - for (const auto& track2 : tracks2) { + for (const auto& track2 : tracksAssoc) { // Fill QA plot for all MFT tracks () (only if cutAmbiguousTracks is false to avoid double counting) - if (!cutAmbiguousTracks && sameEvent && (loopCounter == 1)) { + if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); } - auto reassociatedMftTrack = track2.template mfttrack_as(); + auto reassociatedMftTrack = track2.template mfttrack_as(); - if (!isAcceptedMftTrack(reassociatedMftTrack, false)) { - continue; + auto reassociatedMftTrackDcaXY = 0.f; + auto reassociatedMftTrackDcaZ = 0.f; + + if constexpr (std::is_same_v>, TTracksAssoc>) { + if (!reassociatedMftTrack.has_mcParticle()) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !reassociatedMftTrack.mcParticle().isPhysicalPrimary()) { + continue; + } + } + + if constexpr (std::is_same_v, TTracksAssoc>) { + reassociatedMftTrackDcaXY = track2.bestDCAXY(); + } + if constexpr (std::is_same_v, TTracksAssoc>) { + reassociatedMftTrackDcaXY = track2.bestDCAXY(); + reassociatedMftTrackDcaZ = track2.bestDCAZ(); + } + + if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } + + if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, true)) { + continue; + } + } else { // After the first loop, we don't fill the plots anymore but still do the selection + if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, false)) { + continue; + } } // Fill QA plot for MFT tracks after physical selection (eta + clusters) - if (!cutAmbiguousTracks && sameEvent && (loopCounter == 1)) { + if (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); } // We check if the track is ambiguous or non-ambiguous (QA plots are filled in isAmbiguousMftTrack) // Fill plots only if cutAmbiguousTracks is false (to avoid double counting) - if (isAmbiguousMftTrack(track2, (!cutAmbiguousTracks && sameEvent && (loopCounter == 1)))) { + if (isAmbiguousMftTrack(track2, (!cutAmbiguousTracks && isSameEvent && (loopCounter == 1)))) { // If the MFT track is ambiguous we may cut or not on the ambiguous track - if (sameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hReassociationMftTracks"), ReassociationMftTracks::NotReassociatedMftTracks); } if (cutAmbiguousTracks) { @@ -1443,7 +2147,7 @@ struct HfTaskFlow { } if (reassociatedMftTrack.collisionId() != track2.bestCollisionId()) { - if (sameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { registry.fill(HIST("Data/Mft/hReassociationMftTracks"), ReassociationMftTracks::ReassociatedMftTracks); } } @@ -1465,10 +2169,10 @@ struct HfTaskFlow { continue; } - if (configCentral.isApplyPtOrderingSameEvent && sameEvent && (track1.pt() <= reassociatedMftTrack.pt())) { + if (configCentral.isApplyPtOrderingSameEvent && isSameEvent && (track1.pt() <= reassociatedMftTrack.pt())) { continue; } - if (configCentral.isApplyPtOrderingMixedEvent && !sameEvent && (track1.pt() <= reassociatedMftTrack.pt())) { + if (configCentral.isApplyPtOrderingMixedEvent && !isSameEvent && (track1.pt() <= reassociatedMftTrack.pt())) { continue; } @@ -1481,31 +2185,38 @@ struct HfTaskFlow { float phi2 = reassociatedMftTrack.phi(); o2::math_utils::bringTo02Pi(phi2); - // TODO: add getter for NUE associated efficiency here + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionMft(associatedWeight, pt2, eta2, posZ)) { + continue; + } float deltaPhi = phi1 - phi2; // set range of delta phi in (-pi/2 , 3/2*pi) deltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); if (!fillingHFcontainer) { - target->getPairHist()->Fill(step, eta1 - eta2, pt2, pt1, multiplicity, deltaPhi, posZ, - triggerWeight * associatedWeight); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + target->getPairHist()->Fill(step, eta1 - eta2, pt2, pt1, multiplicity, deltaPhi, posZ, + centralityWeight * triggerWeight * associatedWeight); + } else if (configTask.doEtaDependentFlow) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, + centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, eta1 - eta2, pt2, pt1, multiplicity, deltaPhi, posZ, invmass, - triggerWeight * associatedWeight); + centralityWeight * triggerWeight * associatedWeight); } // FILL QA PLOTS for associated particle - if (sameEvent && (loopCounter == 1)) { + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else if constexpr (std::is_same_v) { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } else { fillAssociatedQa(multiplicity, eta2, phi2); - registry.fill(HIST("Data/Mft/hPtMft"), pt2); } } // end of fill QA } // end of loop over tracks2 @@ -1514,16 +2225,16 @@ struct HfTaskFlow { template void fillCorrelationsFIT(TTarget target, CorrelationContainer::CFStep step, - TTracksTrig const& tracks1, TTracksAssoc const& tracks2, TFits const&, - float multiplicity, float posZ, bool sameEvent, int fitType) + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TFits const&, + float multiplicity, float posZ, bool isSameEvent, int fitType, float centralityWeight) { - auto triggerWeight = 1; - auto associatedWeight = 1; + auto triggerWeight = 1.0f; + auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - // int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE - for (auto const& track1 : tracks1) { + for (auto const& track1 : tracksTrigger) { loopCounter++; if constexpr (std::is_same_v) { @@ -1531,8 +2242,19 @@ struct HfTaskFlow { continue; } } else if constexpr (std::is_same_v) { - if (!isAcceptedMftTrack(track1, true)) { - continue; + if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } + + if (!isAcceptedMftTrack(track1, 0.f, 0.f, true)) { + continue; + } + } else { // After the first loop, we don't fill the plots anymore but still do the selection + if (!isAcceptedMftTrack(track1, 0.f, 0.f, false)) { + continue; + } } } @@ -1541,6 +2263,13 @@ struct HfTaskFlow { float phi1 = track1.phi(); if constexpr (std::is_same_v) { o2::math_utils::bringTo02Pi(phi1); + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionMft(triggerWeight, pt1, eta1, posZ)) { + continue; + } + } else { + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionTpc(triggerWeight, pt1, eta1, posZ)) { + continue; + } } bool fillingHFcontainer = false; @@ -1559,13 +2288,46 @@ struct HfTaskFlow { // fill single-track distributions if (!fillingHFcontainer) { // if not HF-h case - target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, triggerWeight); + target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, centralityWeight * triggerWeight); + + if (configTask.doEtaDependentFlow) { + if (fitType == isFT0A) { + if constexpr (std::is_same_v) { + registry.fill(HIST("Trig_hist_MFT_FT0A"), sampleIndex, posZ, track1.eta(), centralityWeight * triggerWeight); + } else { + registry.fill(HIST("Trig_hist_TPC_FT0A"), sampleIndex, posZ, track1.eta(), centralityWeight * triggerWeight); + } + } else { + if constexpr (std::is_same_v) { + registry.fill(HIST("Trig_hist_MFT_FT0C"), sampleIndex, posZ, track1.eta(), centralityWeight * triggerWeight); + } else { + registry.fill(HIST("Trig_hist_TPC_FT0C"), sampleIndex, posZ, track1.eta(), centralityWeight * triggerWeight); + } + } + } + if (configTask.doVariationContainers) { + if (fitType == isFT0A) { + + if constexpr (std::is_same_v) { + registry.fill(HIST("Trig_hist_MFT_FT0A"), sampleIndex, posZ, track1.pt(), centralityWeight * triggerWeight); + } else { + registry.fill(HIST("Trig_hist_TPC_FT0A"), sampleIndex, posZ, track1.pt(), centralityWeight * triggerWeight); + } + } else { + if constexpr (std::is_same_v) { + registry.fill(HIST("Trig_hist_MFT_FT0C"), sampleIndex, posZ, track1.pt(), centralityWeight * triggerWeight); + } else { + registry.fill(HIST("Trig_hist_TPC_FT0C"), sampleIndex, posZ, track1.pt(), centralityWeight * triggerWeight); + } + } + } + } else { - target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, invmass, triggerWeight); + target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, invmass, centralityWeight * triggerWeight); } // FILL QA PLOTS for trigger particle - if (sameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-FV0a correlations if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h if constexpr (std::is_same_v) { // IF NEITHER D0 NOR LC -> @@ -1610,40 +2372,45 @@ struct HfTaskFlow { } } // end of if condition to fill QA plots for trigger particle - // ASSOCIATED PARTICLE IF USING FV0 - if constexpr (std::is_same_v) { - for (std::size_t indexChannel = 0; indexChannel < tracks2.channel().size(); indexChannel++) { - - auto channelId = tracks2.channel()[indexChannel]; - auto phi2 = getPhiFV0(channelId); - auto eta2 = getEtaFV0(channelId); - float deltaPhi = phi1 - phi2; - deltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); // set range of delta phi in (-pi/2 , 3/2*pi) - - if (!fillingHFcontainer) { - target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, - triggerWeight * associatedWeight); - } else { - target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, invmass, - triggerWeight * associatedWeight); - } - - // FILL QA PLOTS for associated particle - if (sameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { - if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-FV0a correlations - if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h - fillAssociatedQa(multiplicity, eta2, phi2); - } else if constexpr (std::is_same_v) { // IF LC CASE -> TPC-FV0a Lc-h - fillAssociatedQa(multiplicity, eta2, phi2); - } else if constexpr (std::is_same_v) { // IF NEITHER D0 NOR LC -> ch. part. - ch. part - fillAssociatedQa(multiplicity, eta2, phi2); - } - } else { // If FilteredMftTracks as trigger -> MFT-FV0a (non reassoc/ambiguous) correlations - fillAssociatedQa(multiplicity, eta2, phi2); - } - } // end of if condition to fill QA plots for associated particle - } // end of loop over FV0 channel indices - } // end of if condition for FV0s + // // ASSOCIATED PARTICLE IF USING FV0 + // if constexpr (std::is_same_v) { + // for (std::size_t indexChannel = 0; indexChannel < tracks2.channel().size(); indexChannel++) { + + // auto channelId = tracks2.channel()[indexChannel]; + // auto phi2 = getPhiFV0(channelId); + // auto eta2 = getEtaFV0(channelId); + // float deltaPhi = phi1 - phi2; + // deltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); // set range of delta phi in (-pi/2 , 3/2*pi) + + // if (!fillingHFcontainer) { + // if (!configTask.doEtaDependentFlow) { + // target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, + // centralityWeight * triggerWeight * associatedWeight); + // } else { + // target->getPairHist()->Fill(step, eta1 - eta2, eta2, eta1, multiplicity, deltaPhi, posZ, + // centralityWeight * triggerWeight * associatedWeight); + // } + // } else { + // target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, invmass, + // centralityWeight * triggerWeight * associatedWeight); + // } + + // // FILL QA PLOTS for associated particle + // if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + // if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-FV0a correlations + // if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h + // fillAssociatedQa(multiplicity, eta2, phi2); + // } else if constexpr (std::is_same_v) { // IF LC CASE -> TPC-FV0a Lc-h + // fillAssociatedQa(multiplicity, eta2, phi2); + // } else if constexpr (std::is_same_v) { // IF NEITHER D0 NOR LC -> ch. part. - ch. part + // fillAssociatedQa(multiplicity, eta2, phi2); + // } + // } else { // If FilteredMftTracks as trigger -> MFT-FV0a (non reassoc/ambiguous) correlations + // fillAssociatedQa(multiplicity, eta2, phi2); + // } + // } // end of if condition to fill QA plots for associated particle + // } // end of loop over FV0 channel indices + // } // end of if condition for FV0s // ASSOCIATED PARTICLE IF USING FT0 if constexpr (std::is_same_v) { @@ -1651,9 +2418,9 @@ struct HfTaskFlow { // select the right channel size if it is FT0a or FT0c std::size_t channelSize = 0; if (fitType == isFT0C) { - channelSize = tracks2.channelC().size(); + channelSize = tracksAssoc.channelC().size(); } else if (fitType == isFT0A) { - channelSize = tracks2.channelA().size(); + channelSize = tracksAssoc.channelA().size(); } else { LOGF(fatal, "Cor Index %d out of range", fitType); } @@ -1662,22 +2429,35 @@ struct HfTaskFlow { int channelId = 0; float amplitude = 0.; - getChannel(tracks2, indexChannel, channelId, fitType, amplitude); + getChannel(tracksAssoc, indexChannel, channelId, fitType, amplitude); auto phi2 = getPhiFT0(channelId, fitType); auto eta2 = getEtaFT0(channelId, fitType); float deltaPhi = phi1 - phi2; deltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); // set range of delta phi in (-pi/2 , 3/2*pi) if (!fillingHFcontainer) { - target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, - amplitude * triggerWeight * associatedWeight); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + target->getPairHist()->Fill(step, eta1 - eta2, 0.5, pt1, multiplicity, deltaPhi, posZ, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else if (configTask.doEtaDependentFlow) { + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelId, fitType), eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } } else { target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, invmass, - amplitude * triggerWeight * associatedWeight); + amplitude * centralityWeight * triggerWeight * associatedWeight); } // FILL QA PLOTS for associated particle - if (sameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (!std::is_same_v) { // If not FilteredMftTracks as trigger -> TPC-Ft0a correlations if constexpr (std::is_same_v) { // IF D0 CASE -> TPC-FV0a D0-h if (fitType == isFT0A) { @@ -1712,24 +2492,53 @@ struct HfTaskFlow { } // end of loop over tracks 1 } - template + template void fillCorrelationsFITReassociatedMftTracks(TTarget target, CorrelationContainer::CFStep step, - TTracksTrig const& tracks1, TTracksAssoc const& tracks2, TFits const&, - float multiplicity, float posZ, bool sameEvent, bool cutAmbiguousTracks, int fitType) + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TFits const&, + float multiplicity, float posZ, bool isSameEvent, bool cutAmbiguousTracks, int fitType, float centralityWeight) { - auto triggerWeight = 1; - auto associatedWeight = 1; + auto triggerWeight = 1.0f; + auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - // int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE - for (auto const& track1 : tracks1) { + for (auto const& track1 : tracksTrigger) { loopCounter++; - auto reassociatedMftTrack = track1.template mfttrack_as(); + auto reassociatedMftTrack = track1.template mfttrack_as(); + auto reassociatedMftTrackDcaXY = 0.f; + auto reassociatedMftTrackDcaZ = 0.f; - if (!isAcceptedMftTrack(reassociatedMftTrack, true)) { - continue; + if constexpr (std::is_same_v>, TTracksAssoc>) { + if (!reassociatedMftTrack.has_mcParticle()) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !reassociatedMftTrack.mcParticle().isPhysicalPrimary()) { + continue; + } + } + + if constexpr (std::is_same_v, TTracksTrig>) { + reassociatedMftTrackDcaXY = track1.bestDCAXY(); + } + if constexpr (std::is_same_v, TTracksTrig>) { + reassociatedMftTrackDcaXY = track1.bestDCAXY(); + reassociatedMftTrackDcaZ = track1.bestDCAZ(); + } + + if (isSameEvent && loopCounter == 1) { // To avoid double counting, we fill the plots only the first time + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/Mft/hMftTracksSelection"), MftTrackSelectionStep::NoSelection); + } + + if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, true)) { + continue; + } + } else { // After the first loop, we don't fill the plots anymore but still do the selection + if (!isAcceptedMftTrack(reassociatedMftTrack, reassociatedMftTrackDcaXY, reassociatedMftTrackDcaZ, false)) { + continue; + } } if (isAmbiguousMftTrack(track1, false)) { @@ -1743,10 +2552,21 @@ struct HfTaskFlow { float phi1 = reassociatedMftTrack.phi(); o2::math_utils::bringTo02Pi(phi1); - target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, triggerWeight); + if (configTask.useEfficiencyCorrection && !getEfficiencyCorrectionMft(triggerWeight, pt1, eta1, posZ)) { + continue; + } + + target->getTriggerHist()->Fill(step, pt1, multiplicity, posZ, centralityWeight * triggerWeight); + + if (configTask.doEtaDependentFlow) { + registry.fill(HIST("Trig_hist_MFT_FT0A"), sampleIndex, posZ, eta1, centralityWeight * triggerWeight); + } + if (configTask.doVariationContainers) { + registry.fill(HIST("Trig_hist_MFT_FT0A"), sampleIndex, posZ, pt1, centralityWeight * triggerWeight); + } // FILL QA PLOTS for trigger particle - if (sameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if constexpr (std::is_same_v) { fillTriggerQa(multiplicity, eta1, phi1, pt1); } else if constexpr (std::is_same_v) { @@ -1756,26 +2576,31 @@ struct HfTaskFlow { } } // end of if condition to fill QA plots for trigger particle - // ASSOCIATED PARTICLE FOR FV0s - if constexpr (std::is_same_v) { - for (std::size_t indexChannel = 0; indexChannel < tracks2.channel().size(); indexChannel++) { + // // ASSOCIATED PARTICLE FOR FV0s + // if constexpr (std::is_same_v) { + // for (std::size_t indexChannel = 0; indexChannel < tracks2.channel().size(); indexChannel++) { - auto channelId = tracks2.channel()[indexChannel]; - auto phi2 = getPhiFV0(channelId); - auto eta2 = getEtaFV0(channelId); + // auto channelId = tracks2.channel()[indexChannel]; + // auto phi2 = getPhiFV0(channelId); + // auto eta2 = getEtaFV0(channelId); - float deltaPhi = phi1 - phi2; - deltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); // set range of delta phi in (-pi/2 , 3/2*pi) + // float deltaPhi = phi1 - phi2; + // deltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); // set range of delta phi in (-pi/2 , 3/2*pi) - target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, - triggerWeight * associatedWeight); + // if (!configTask.doEtaDependentFlow) { + // target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, + // centralityWeight * triggerWeight * associatedWeight); + // } else { + // target->getPairHist()->Fill(step, eta1 - eta2, eta2, eta1, multiplicity, deltaPhi, posZ, + // centralityWeight * triggerWeight * associatedWeight); + // } - // FILL QA PLOTS for associated particle - if (sameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { - fillAssociatedQa(multiplicity, eta2, phi2); - } // end of if condition to fill QA plots for associated particle - } // end of loop over FV0 channel indices - } // end of if condition for FV0s + // // FILL QA PLOTS for associated particle + // if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + // fillAssociatedQa(multiplicity, eta2, phi2); + // } // end of if condition to fill QA plots for associated particle + // } // end of loop over FV0 channel indices + // } // end of if condition for FV0s // ASSOCIATED PARTICLE FOR FT0s if constexpr (std::is_same_v) { @@ -1783,9 +2608,9 @@ struct HfTaskFlow { // select the right channel size if it is FT0a or FT0c std::size_t channelSize = 0; if (fitType == isFT0C) { - channelSize = tracks2.channelC().size(); + channelSize = tracksAssoc.channelC().size(); } else if (fitType == isFT0A) { - channelSize = tracks2.channelA().size(); + channelSize = tracksAssoc.channelA().size(); } else { LOGF(fatal, "Cor Index %d out of range", fitType); } @@ -1794,17 +2619,30 @@ struct HfTaskFlow { int channelId = 0; float amplitude = 0.; - getChannel(tracks2, indexChannel, channelId, fitType, amplitude); + getChannel(tracksAssoc, indexChannel, channelId, fitType, amplitude); auto phi2 = getPhiFT0(channelId, fitType); auto eta2 = getEtaFT0(channelId, fitType); float deltaPhi = phi1 - phi2; deltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); // set range of delta phi in (-pi/2 , 3/2*pi) - target->getPairHist()->Fill(step, eta1 - eta2, pt1, pt1, multiplicity, deltaPhi, posZ, - amplitude * triggerWeight * associatedWeight); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + target->getPairHist()->Fill(step, eta1 - eta2, 0.5, pt1, multiplicity, deltaPhi, posZ, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else if (configTask.doEtaDependentFlow) { + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, eta2, eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelId, fitType), eta1, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, pt1, multiplicity, deltaPhi, eta1 - eta2, + amplitude * centralityWeight * triggerWeight * associatedWeight); + } // FILL QA PLOTS for associated particle - if (sameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { if (fitType == isFT0A) { fillAssociatedQa(multiplicity, eta2, phi2); } @@ -1817,67 +2655,220 @@ struct HfTaskFlow { template void fillCorrelationsFt0aFt0c(TTarget target, CorrelationContainer::CFStep step, TFT0As const& ft0as, TFT0Cs const& ft0cs, - float multiplicity, float posZ, bool sameEvent) + float multiplicity, float posZ, bool isSameEvent, float centralityWeight) { - auto triggerWeight = 1; - auto associatedWeight = 1; + auto triggerWeight = 1.0f; + auto associatedWeight = 1.0f; auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision - // int sampleIndex = gRandom->Uniform(0, configTask.nSamples); + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); // TRIGGER PARTICLE FROM FT0A for (std::size_t indexChannelA = 0; indexChannelA < ft0as.channelA().size(); indexChannelA++) { loopCounter++; int channelIdA = 0; - float amplitude = 0.; - getChannel(ft0as, indexChannelA, channelIdA, isFT0A, amplitude); + float amplitudeA = 0.; + getChannel(ft0as, indexChannelA, channelIdA, isFT0A, amplitudeA); auto phiA = getPhiFT0(channelIdA, isFT0A); auto etaA = getEtaFT0(channelIdA, isFT0A); - target->getTriggerHist()->Fill(step, 0.f, multiplicity, posZ, amplitude * triggerWeight); + target->getTriggerHist()->Fill(step, 0.5, multiplicity, posZ, amplitudeA * centralityWeight * triggerWeight); - if (sameEvent && (step == CorrelationContainer::kCFStepReconstructed)) { - fillTriggerQa(multiplicity, etaA, phiA, 0.f); + if (configTask.doEtaDependentFlow) { + registry.fill(HIST("Trig_hist_FT0A_FT0C"), sampleIndex, posZ, etaA, amplitudeA * centralityWeight * triggerWeight); + } + if (configTask.doVariationContainers) { + registry.fill(HIST("Trig_hist_FT0A_FT0C"), sampleIndex, posZ, 0.5, amplitudeA * centralityWeight * triggerWeight); + } + + if (isSameEvent && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { + fillTriggerQa(multiplicity, etaA, phiA, 0.5); } // end of fill trigger QA // ASSOCIATED PARTICLE FROM FT0C for (std::size_t indexChannelC = 0; indexChannelC < ft0cs.channelC().size(); indexChannelC++) { int channelIdC = 0; - float amplitude = 0.; - getChannel(ft0cs, indexChannelC, channelIdC, isFT0C, amplitude); + float amplitudeC = 0.; + getChannel(ft0cs, indexChannelC, channelIdC, isFT0C, amplitudeC); auto phiC = getPhiFT0(channelIdC, isFT0C); auto etaC = getEtaFT0(channelIdC, isFT0C); float deltaPhi = RecoDecay::constrainAngle(phiA - phiC, -PIHalf); - target->getPairHist()->Fill(step, etaA - etaC, 0.f, 0.f, multiplicity, deltaPhi, posZ, - amplitude * triggerWeight * associatedWeight); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + target->getPairHist()->Fill(step, etaA - etaC, 0.5, 0.5, multiplicity, deltaPhi, posZ, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } else if (configTask.doEtaDependentFlow) { + // target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, + // amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + + if (!configFit.discriminateInnerOrOuter) { + target->getPairHist()->Fill(step, sampleIndex, posZ, etaC, etaA, deltaPhi, etaA - etaC, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, isInnerOrOuter(channelIdA, isFT0A), isInnerOrOuter(channelIdC, isFT0C), deltaPhi, etaA - etaC, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, 0.5, multiplicity, deltaPhi, etaA - etaC, + amplitudeA * amplitudeC * centralityWeight * triggerWeight * associatedWeight); + } - if (sameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed)) { + if (isSameEvent && (loopCounter == 1) && (step == CorrelationContainer::kCFStepReconstructed) && !configTask.removeQAForSystematics) { fillAssociatedQa(multiplicity, etaC, phiC); } // end of fill associated QA } // end of associated loop } // end of trigger loop } // end of fillCorrelationsFt0aFt0c + template + void fillCorrelationsMonteCarlo(TTarget target, CorrelationContainer::CFStep step, + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, + float multiplicity, float posZ, bool isSameEvent) + { + auto triggerWeight = 1.0f; + auto associatedWeight = 1.0f; + auto loopCounter = 0; // To avoid filling associated tracks QA many times, I fill it only for the first trigger track of the collision + int sampleIndex = static_cast(gRandom->Uniform(0, configTask.nSamples)); + + for (auto const& track1 : tracksTrigger) { + + if (configTask.useCutNeutralParticles) { + auto pdgTriggerParticle = pdg->GetParticle(track1.pdgCode()); + if (!pdgTriggerParticle || std::abs(pdgTriggerParticle->Charge()) < o2::constants::math::Almost0) { + continue; + } + } + + if (track1.eta() < configTask.etaMcParticlesTriggerMin || track1.eta() > configTask.etaMcParticlesTriggerMax) { + continue; + } + if (track1.pt() < configTask.ptMcParticlesTriggerMin || track1.pt() > configTask.ptMcParticlesTriggerMax) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !track1.isPhysicalPrimary()) { + continue; + } + + target->getTriggerHist()->Fill(step, track1.pt(), multiplicity, posZ, triggerWeight); + + if (configTask.doEtaDependentFlow) { + registry.fill(HIST("Trig_hist"), sampleIndex, posZ, track1.eta(), triggerWeight); + } + if (configTask.doVariationContainers) { + registry.fill(HIST("Trig_hist"), sampleIndex, posZ, track1.pt(), triggerWeight); + } + + // FILL QA FOR TRIGGER PARTICLE + if (isSameEvent && !configTask.removeQAForSystematics) { + if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFv0a)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFv0a)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0a)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFt0a)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0c)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::Ft0aFt0c)) { + fillTriggerQa(multiplicity, track1.eta(), track1.phi(), track1.pt()); + } + } + + loopCounter++; + + for (auto const& track2 : tracksAssoc) { + + if (configTask.useCutNeutralParticles) { + auto pdgAssociatedParticle = pdg->GetParticle(track2.pdgCode()); + if (!pdgAssociatedParticle || std::abs(pdgAssociatedParticle->Charge()) < o2::constants::math::Almost0) { + continue; + } + } + + if (track1.globalIndex() == track2.globalIndex()) { + continue; + } + if (track2.eta() < configTask.etaMcParticlesAssocMin || track2.eta() > configTask.etaMcParticlesAssocMax) { + continue; + } + if (track2.pt() < configTask.ptMcParticlesAssocMin || track2.pt() > configTask.ptMcParticlesAssocMax) { + continue; + } + if (configTask.useOnlyPrimaryInMc && !track2.isPhysicalPrimary()) { + continue; + } + + float deltaPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -PIHalf); + float deltaEta = track1.eta() - track2.eta(); + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + target->getPairHist()->Fill(step, deltaEta, track2.pt(), track1.pt(), multiplicity, deltaPhi, posZ, + triggerWeight * associatedWeight); + } else if (configTask.doEtaDependentFlow) { + target->getPairHist()->Fill(step, sampleIndex, posZ, track2.eta(), track1.eta(), deltaPhi, deltaEta, + triggerWeight * associatedWeight); + } else { + target->getPairHist()->Fill(step, sampleIndex, posZ, track1.pt(), multiplicity, deltaPhi, deltaEta, + triggerWeight * associatedWeight); + } + + // FILL QA PLOTS for associated particle + if (isSameEvent && (loopCounter == 1) && !configTask.removeQAForSystematics) { + if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcTpc)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcMft)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFv0a)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFv0a)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0a)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::MftFt0a)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::TpcFt0c)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } else if (configTask.chooseCorrelationCase.value == static_cast(CorrelationCase::Ft0aFt0c)) { + fillAssociatedQa(multiplicity, track2.eta(), track2.phi()); + } + } // end of fill QA + } // end of loop over track2 + } // end of loop over track1 + } + // =============================================================================================================================================================================== // mixCollisions for RECONSTRUCTED events // =============================================================================================================================================================================== template void mixCollisions(TCollisions const& collisions, CorrelationContainer::CFStep step, - TTracksTrig const& tracks1, TTracksAssoc const& tracks2, + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, OutputObj& corrContainer, aod::BCsWithTimestamps const&) { - auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { - auto multiplicity = getMultiplicityEstimator(collision, false); - return multiplicity; + // auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { + // auto multiplicity = getMultiplicityEstimator(collision, false); + // return multiplicity; + // }; + + auto getMultiplicity = [&tracksTrigger, this](FilteredCollisionsWSelMult::iterator const& collision) { + auto associatedTracks = tracksTrigger.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + // auto mult = 0.f; + if (configCollision.useMultiplicityFromTracks) { + return associatedTracks.size(); + } + return getMultiplicityEstimator(collision, false); }; // The first one that I call "Data" should work for data and mc rec using BinningTypeData = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - BinningTypeData binningWithTracksSize{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; - auto tracksTuple = std::make_tuple(tracks1, tracks2); + BinningTypeData binningWithTracksSize{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; + auto tracksTuple = std::make_tuple(tracksTrigger, tracksAssoc); Pair pair{binningWithTracksSize, configTask.nMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { @@ -1890,111 +2881,322 @@ struct HfTaskFlow { } auto bc = collision1.template bc_as(); - const auto multiplicity = getMultiplicityEstimator(collision1, false); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + if (configCollision.useMultiplicityFromTracks) { + multiplicity = tracks1.size(); + } else { + multiplicity = getMultiplicityEstimator(collision1, false); + } - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; + float centralityWeight = 1.0f; + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; + } + } + + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; } corrContainer->fillEvent(multiplicity, step); - fillCorrelations(corrContainer, step, tracks1, tracks2, multiplicity, collision1.posZ(), false, getMagneticField(bc.timestamp())); + fillCorrelations(corrContainer, step, tracks1, tracks2, multiplicity, collision1.posZ(), false, getMagneticField(bc.timestamp()), centralityWeight); } } - template - void mixCollisionsFIT(TCollisions const& collisions, CorrelationContainer::CFStep step, - TTracksTrig const& tracks1, TTracksAssoc const& tracks2, TPreslice const& preslice, - OutputObj& corrContainer, int fitType) + template + void mixCollisionsBis(TCollisions const& collisions, CorrelationContainer::CFStep step, + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TPresliceTrigger const& presliceTrigger, TPresliceAssociated const& presliceAssociated, + OutputObj& corrContainer, aod::BCsWithTimestamps const&) { - auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { - auto multiplicity = getMultiplicityEstimator(collision, false); - return multiplicity; + // auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { + // auto multiplicity = getMultiplicityEstimator(collision, false); + // return multiplicity; + // }; + + auto getMultiplicity = [&tracksTrigger, this](FilteredCollisionsWSelMult::iterator const& collision) { + auto associatedTracks = tracksTrigger.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + if (configCollision.useMultiplicityFromTracks) { + return mult; + } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { - if (!isAcceptedCollision(collision1) || !isAcceptedCollision(collision2)) { + if (!(isAcceptedCollision(collision1, false))) { + continue; + } + if (!(isAcceptedCollision(collision2, false))) { continue; } - if (collision1.globalIndex() == collision2.globalIndex()) { continue; } - if constexpr (std::is_same_v) { // IF ASSOCIATED PARTICLE FROM FV0A - if (collision1.has_foundFV0() && collision2.has_foundFV0()) { - - const auto multiplicity = getMultiplicityEstimator(collision1, false); - auto slicedTriggerTracks = tracks1.sliceBy(preslice, collision1.globalIndex()); - const auto& fv0 = collision2.foundFV0(); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } + auto bc = collision1.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + if (configCollision.useMultiplicityFromTracks) { + multiplicity = tracksTrigger.size(); + } else { + multiplicity = getMultiplicityEstimator(collision1, false); + } - corrContainer->fillEvent(multiplicity, step); - fillCorrelationsFIT(corrContainer, step, slicedTriggerTracks, fv0, tracks2, multiplicity, collision1.posZ(), false, fitType); + float centralityWeight = 1.0f; + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; } - } // end of if condition for FV0s + } - if constexpr (std::is_same_v) { - if (collision1.has_foundFT0() && collision2.has_foundFT0()) { + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } - const auto multiplicity = getMultiplicityEstimator(collision1, false); - auto slicedTriggerTracks = tracks1.sliceBy(preslice, collision1.globalIndex()); - const auto& ft0 = collision2.foundFT0(); + auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); + auto slicedAssociatedTracks = tracksAssoc.sliceBy(presliceAssociated, collision2.globalIndex()); - corrContainer->fillEvent(multiplicity, step); - fillCorrelationsFIT(corrContainer, step, slicedTriggerTracks, ft0, tracks2, multiplicity, collision1.posZ(), false, fitType); - } - } // end of if condition for TPC-FT0 or MFT-FT0s - } // end of for loop + corrContainer->fillEvent(multiplicity, step); + fillCorrelations(corrContainer, step, slicedTriggerTracks, slicedAssociatedTracks, multiplicity, collision1.posZ(), false, getMagneticField(bc.timestamp()), centralityWeight); + } } - template - void mixCollisionsFt0aFt0c(TCollisions const& collisions, CorrelationContainer::CFStep step, - TFT0as const&, TFT0cs const&, - OutputObj& corrContainer) + template + void mixCollisionsReassociatedMftTracks(TCollisions const& collisions, CorrelationContainer::CFStep step, TTracksTpc const& tracksTpc, + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TPresliceTrigger const& presliceTrigger, TPresliceAssociated const& presliceAssociated, + OutputObj& corrContainer, bool cutAmbiguousTracks, aod::BCsWithTimestamps const&) { - auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { - auto multiplicity = getMultiplicityEstimator(collision, false); - return multiplicity; + // auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { + // auto multiplicity = getMultiplicityEstimator(collision, false); + // return multiplicity; + // }; + + auto getMultiplicity = [&tracksTpc, this](FilteredCollisionsWSelMult::iterator const& collision) { + auto associatedTracks = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + if (configCollision.useMultiplicityFromTracks) { + return mult; + } + return getMultiplicityEstimator(collision, false); }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; - MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}, true}; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { if (!isAcceptedCollision(collision1) || !isAcceptedCollision(collision2)) { continue; } - if (collision1.globalIndex() == collision2.globalIndex()) { continue; } - if (collision1.has_foundFT0() && collision2.has_foundFT0()) { - - const auto multiplicity = getMultiplicityEstimator(collision1, false); - const auto& ft0as = collision1.foundFT0(); - const auto& ft0cs = collision2.foundFT0(); + auto bc = collision1.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); + float multiplicity = 0.f; + if (configCollision.useMultiplicityFromTracks) { + multiplicity = tracksForMultiplicity.size(); + } else { + multiplicity = getMultiplicityEstimator(collision1, false); + } - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; + float centralityWeight = 1.0f; + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; } + } - corrContainer->fillEvent(multiplicity, step); - fillCorrelationsFt0aFt0c(corrContainer, step, ft0as, ft0cs, multiplicity, collision1.posZ(), true); + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; } - } // end of for loop - } - // =================================================================================================================================================================================================================================================================== - // =================================================================================================================================================================================================================================================================== + // if TPC-MFT cases + if constexpr (std::is_same_v) { + auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); + auto slicedAssociatedTracks = tracksAssoc.sliceBy(presliceAssociated, collision2.globalIndex()); + + corrContainer->fillEvent(multiplicity, step); + fillCorrelationsReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, slicedAssociatedTracks, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, centralityWeight); + } else if ((collision1.has_foundFT0() && collision2.has_foundFT0())) { // if MFT-FT0A cases + auto slicedTriggerTracks = tracksTrigger.sliceBy(presliceTrigger, collision1.globalIndex()); + const auto& ft0 = collision2.foundFT0(); + + corrContainer->fillEvent(multiplicity, step); + fillCorrelationsFITReassociatedMftTracks(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, cutAmbiguousTracks, isFT0A, centralityWeight); + } + } // end of for loop + } + + template + void mixCollisionsFIT(TCollisions const& collisions, CorrelationContainer::CFStep step, TTracksTpc const& tracksTpc, + TTracksTrig const& tracksTrigger, TTracksAssoc const& tracksAssoc, TPreslice const& preslice, + OutputObj& corrContainer, int fitType, aod::BCsWithTimestamps const&) + { + // auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { + // auto multiplicity = getMultiplicityEstimator(collision, false); + // return multiplicity; + // }; + + auto getMultiplicity = [&tracksTpc, this](FilteredCollisionsWSelMult::iterator const& collision) { + auto associatedTracks = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + if (configCollision.useMultiplicityFromTracks) { + return mult; + } + return getMultiplicityEstimator(collision, false); + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; + + for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { + + if (!isAcceptedCollision(collision1) || !isAcceptedCollision(collision2)) { + continue; + } + + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } + + auto bc = collision1.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + + // if constexpr (std::is_same_v) { // IF ASSOCIATED PARTICLE FROM FV0A + // if (collision1.has_foundFV0() && collision2.has_foundFV0()) { + + // auto slicedTriggerTracks = tracksTrigger.sliceBy(preslice, collision1.globalIndex()); + // auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); + // const auto& fv0 = collision2.foundFV0(); + + // float multiplicity = 0.f; + // if (configCollision.useMultiplicityFromTracks) { + // multiplicity = tracksForMultiplicity.size(); + // } else { + // multiplicity = getMultiplicityEstimator(collision1, false); + // } + + // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + // continue; + // } + + // corrContainer->fillEvent(multiplicity, step); + // fillCorrelationsFIT(corrContainer, step, slicedTriggerTracks, fv0, tracks2, multiplicity, collision1.posZ(), false, fitType); + // } + // } // end of if condition for FV0s + + if constexpr (std::is_same_v) { + if (collision1.has_foundFT0() && collision2.has_foundFT0()) { + + auto slicedTriggerTracks = tracksTrigger.sliceBy(preslice, collision1.globalIndex()); + auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); + const auto& ft0 = collision2.foundFT0(); + + float multiplicity = 0.f; + if (configCollision.useMultiplicityFromTracks) { + multiplicity = tracksForMultiplicity.size(); + } else { + multiplicity = getMultiplicityEstimator(collision1, false); + } + + float centralityWeight = 1.0f; + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; + } + } + + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + + corrContainer->fillEvent(multiplicity, step); + fillCorrelationsFIT(corrContainer, step, slicedTriggerTracks, ft0, tracksAssoc, multiplicity, collision1.posZ(), false, fitType, centralityWeight); + } + } // end of if condition for TPC-FT0 or MFT-FT0s + } // end of for loop + } + + template + void mixCollisionsFt0aFt0c(TCollisions const& collisions, CorrelationContainer::CFStep step, + TTracksTpc const& tracksTpc, TFT0as const&, TFT0cs const&, + OutputObj& corrContainer, aod::BCsWithTimestamps const&) + { + // auto getMultiplicity = [this](FilteredCollisionsWSelMult::iterator const& collision) { + // auto multiplicity = getMultiplicityEstimator(collision, false); + // return multiplicity; + // }; + + auto getMultiplicity = [&tracksTpc, this](FilteredCollisionsWSelMult::iterator const& collision) { + auto associatedTracks = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), this->cache); + auto mult = associatedTracks.size(); + if (configCollision.useMultiplicityFromTracks) { + return mult; + } + return getMultiplicityEstimator(collision, false); + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getMultiplicity)>; + MixedBinning const binningOnVtxAndMult{{getMultiplicity}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; + + for (auto const& [collision1, collision2] : soa::selfCombinations(binningOnVtxAndMult, configTask.nMixedEvents, -1, collisions, collisions)) { + + if (!isAcceptedCollision(collision1) || !isAcceptedCollision(collision2)) { + continue; + } + + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } + + if (collision1.has_foundFT0() && collision2.has_foundFT0()) { + + const auto& ft0as = collision1.foundFT0(); + const auto& ft0cs = collision2.foundFT0(); + + auto bc = collision1.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + auto tracksForMultiplicity = tracksTpc.sliceByCached(o2::aod::track::collisionId, collision1.globalIndex(), cache); + float multiplicity = 0.f; + if (configCollision.useMultiplicityFromTracks) { + multiplicity = tracksForMultiplicity.size(); + } else { + multiplicity = getMultiplicityEstimator(collision1, false); + } + + float centralityWeight = 1.0f; + if (configCollision.useCentrality) { + getCentralityWeight(centralityWeight, getCentralityEstimator(collision1, false)); + if (getCentralityEstimator(collision1, false) < configCollision.minCentrality || getCentralityEstimator(collision1, false) >= configCollision.maxCentrality) { + continue; + } + } + + if (!configCollision.useCentrality && (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity)) { + continue; + } + + corrContainer->fillEvent(multiplicity, step); + fillCorrelationsFt0aFt0c(corrContainer, step, ft0as, ft0cs, multiplicity, collision1.posZ(), true, centralityWeight); + } + } // end of for loop + } + + // =================================================================================================================================================================================================================================================================== + // =================================================================================================================================================================================================================================================================== // SAME EVENT PROCESS FUNCTIONS // =================================================================================================================================================================================================================================================================== // =================================================================================================================================================================================================================================================================== @@ -2015,16 +3217,22 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelations(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, tracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp())); + fillCorrelations(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, tracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp()), centralityWeight); } PROCESS_SWITCH(HfTaskFlow, processSameTpcTpcChCh, "DATA : Process same-event correlations for TPC-TPC h-h case", false); @@ -2048,16 +3256,22 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, tracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp())); + fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, tracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp()), centralityWeight); } PROCESS_SWITCH(HfTaskFlow, processSameTpcTpcD0Ch, "DATA : Process same-event correlations for TPC-TPC D0-h case", false); @@ -2081,16 +3295,22 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, tracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp())); + fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, tracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp()), centralityWeight); } PROCESS_SWITCH(HfTaskFlow, processSameTpcTpcLcCh, "DATA : Process same-event correlations for TPC-TPC Lc-h case", false); @@ -2107,87 +3327,145 @@ struct HfTaskFlow { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + auto bc = collision.template bc_as(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelations(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, mftTracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp())); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelations(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, mftTracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp()), centralityWeight); + } else { + sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelations(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, mftTracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp()), centralityWeight); + } } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChCh, "DATA : Process same-event correlations for TPC-MFT h-h case", false); - void processSameTpcMftChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const& mftTracks) + // void processSameTpcMftChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const& mftTracks, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // if (!configTask.removeQAForSystematics) { + // registry.fill(HIST("Data/hNTracks"), tracks.size()); + // registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + // registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + // } + + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; + + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } + + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } else { + // sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated, "DATA : Process same-event correlations for TPC-MFT h-h case reassociated", false); + + void processSameTpcMftChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, + soa::SmallGroups const& reassociatedMftTracks, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracks const& mftTracks, + aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false); - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated, "DATA : Process same-event correlations for TPC-MFT h-h case reassociated", false); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - void processSameTpcMftChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const& mftTracks) - { - if (!(isAcceptedCollision(collision, true))) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + auto uncorrectedMultiplicity = multiplicity; - // const auto multiplicity = collision.multNTracksPV(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + if (configCollision.useMultiplicityFromTracksCorrected) { + multiplicity = getCorrectedMultiplicity(tracks); + } - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } else { + sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated3d, "DATA : Process same-event correlations for TPC-MFT h-h case 3d reassociated", false); void processSameTpcMftChChNonAmbiguous(FilteredCollisionsWSelMult::iterator const& collision, soa::SmallGroups const& reassociatedMftTracks, FilteredTracksWDcaSel const& tracks, - FilteredMftTracks const& mftTracks) + FilteredMftTracks const& mftTracks, + aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; // when process function has iterator } - registry.fill(HIST("Data/hNTracks"), tracks.size()); - registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); - registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); - const auto multiplicity = getMultiplicityEstimator(collision, true); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); + } else { + sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, true, centralityWeight); + } } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChNonAmbiguous, "DATA : Process same-event correlations for TPC-MFT h-h case with non-ambiguous tracks", false); @@ -2197,7 +3475,7 @@ struct HfTaskFlow { void processSameTpcMftD0Ch(FilteredCollisionsWSelMult::iterator const& collision, HfCandidatesSelD0 const& candidates, - FilteredTracksWDcaSel const& /*tracks*/, + FilteredTracksWDcaSel const& tracks, FilteredMftTracks const& mftTracks, aod::BCsWithTimestamps const&) { @@ -2213,36 +3491,45 @@ struct HfTaskFlow { } auto bc = collision.template bc_as(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, mftTracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp())); + fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, mftTracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp()), centralityWeight); } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0Ch, "DATA : Process same-event correlations for TPC-MFT D0-h case", false); - void processSameTpcMftD0ChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelD0 const& candidates, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; // when process function has iterator - } - - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false); - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0ChReassociated, "DATA : Process same-event correlations for TPC-MFT D0-h case reassociated", false); + // void processSameTpcMftD0ChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelD0 const& candidates, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const&, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; // when process function has iterator + // } + + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; + + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } + + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftD0ChReassociated, "DATA : Process same-event correlations for TPC-MFT D0-h case reassociated", false); // ===================================== // DATA : process same event correlations: TPC-MFT HF-h case for Lc @@ -2250,7 +3537,7 @@ struct HfTaskFlow { void processSameTpcMftLcCh(FilteredCollisionsWSelMult::iterator const& collision, HfCandidatesSelLc const& candidates, - FilteredTracksWDcaSel const& /*tracks*/, + FilteredTracksWDcaSel const& tracks, FilteredMftTracks const& mftTracks, aod::BCsWithTimestamps const&) { @@ -2266,165 +3553,174 @@ struct HfTaskFlow { } auto bc = collision.template bc_as(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, mftTracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp())); + fillCorrelations(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, mftTracks, multiplicity, collision.posZ(), true, getMagneticField(bc.timestamp()), centralityWeight); } PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcCh, "DATA : Process same-event correlations for TPC-MFT Lc-h case", false); - void processSameTpcMftLcChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelLc const& candidates, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; // when process function has iterator - } - - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false); - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcChReassociated, "DATA : Process same-event correlations for TPC-MFT Lc-h case reassociated", false); + // void processSameTpcMftLcChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelLc const& candidates, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const&, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; // when process function has iterator + // } + + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; + + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } + + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsReassociatedMftTracks(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcMftLcChReassociated, "DATA : Process same-event correlations for TPC-MFT Lc-h case reassociated", false); // ===================================== // DATA : process same event correlations: TPC-FV0A Ch. Part. - Ch. Part // ===================================== - void processSameTpcFv0aChCh(FilteredCollisionsWSelMult::iterator const& collision, - FilteredTracksWDcaSel const& tracks, - aod::FV0As const& fv0as) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } - - if (collision.has_foundFV0()) { - const auto& fv0 = collision.foundFV0(); - - registry.fill(HIST("Data/hNTracks"), tracks.size()); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcFv0aChCh, "DATA : Process same-event correlations for TPC-FV0-A h-h case", false); - - // ===================================== - // DATA : process same event correlations: TPC-FV0A D0 - Ch. Part - // ===================================== - - void processSameTpcFv0aD0Ch(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelD0 const& candidates, - aod::FV0As const& fv0as) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } - - if (collision.has_foundFV0()) { - const auto& fv0 = collision.foundFV0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcFv0aD0Ch, "DATA : Process same-event correlations for TPC-FV0-A D0-h case", false); - - // ===================================== - // DATA : process same event correlations: TPC-FV0A Lc - Ch. Part - // ===================================== - - void processSameTpcFv0aLcCh(FilteredCollisionsWSelMult::iterator const& collision, - HfCandidatesSelLc const& candidates, - aod::FV0As const& fv0as) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } - - if (collision.has_foundFV0()) { - const auto& fv0 = collision.foundFV0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcFv0aLcCh, "DATA : Process same-event correlations for TPC-FV0-A Lc-h case", false); - - // ===================================== - // DATA : process same event correlations: MFT-FV0A Ch. Part. - Ch. Part - // ===================================== - - void processSameMftFv0aChCh(FilteredCollisionsWSelMult::iterator const& collision, - FilteredMftTracks const& mftTracks, - aod::FV0As const& fv0as) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } - - if (collision.has_foundFV0()) { - const auto& fv0 = collision.foundFV0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, mftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChCh, "DATA : Process same-event correlations for MFT-FV0-A h-h case", false); - - void processSameMftFv0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&, - aod::FV0As const& fv0as) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } - - if (collision.has_foundFV0()) { - const auto& fv0 = collision.foundFV0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChReassociated, "DATA : Process same-event correlations for MFT-FV0a h-h case reassociated", false); + // void processSameTpcFv0aChCh(FilteredCollisionsWSelMult::iterator const& collision, + // FilteredTracksWDcaSel const& tracks, + // aod::FV0As const& fv0as) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // if (collision.has_foundFV0()) { + // const auto& fv0 = collision.foundFV0(); + + // registry.fill(HIST("Data/hNTracks"), tracks.size()); + // auto multiplicity = getMultiplicityEstimator(collision, true); + + // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + // return; + // } + + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcFv0aChCh, "DATA : Process same-event correlations for TPC-FV0-A h-h case", false); + + // // ===================================== + // // DATA : process same event correlations: TPC-FV0A D0 - Ch. Part + // // ===================================== + + // void processSameTpcFv0aD0Ch(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelD0 const& candidates, + // aod::FV0As const& fv0as) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // if (collision.has_foundFV0()) { + // const auto& fv0 = collision.foundFV0(); + // auto multiplicity = getMultiplicityEstimator(collision, true); + + // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + // return; + // } + + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcFv0aD0Ch, "DATA : Process same-event correlations for TPC-FV0-A D0-h case", false); + + // // ===================================== + // // DATA : process same event correlations: TPC-FV0A Lc - Ch. Part + // // ===================================== + + // void processSameTpcFv0aLcCh(FilteredCollisionsWSelMult::iterator const& collision, + // HfCandidatesSelLc const& candidates, + // aod::FV0As const& fv0as) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // if (collision.has_foundFV0()) { + // const auto& fv0 = collision.foundFV0(); + // auto multiplicity = getMultiplicityEstimator(collision, true); + + // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + // return; + // } + + // sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameTpcFv0aLcCh, "DATA : Process same-event correlations for TPC-FV0-A Lc-h case", false); + + // // ===================================== + // // DATA : process same event correlations: MFT-FV0A Ch. Part. - Ch. Part + // // ===================================== + + // void processSameMftFv0aChCh(FilteredCollisionsWSelMult::iterator const& collision, + // FilteredMftTracks const& mftTracks, + // aod::FV0As const& fv0as) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // if (collision.has_foundFV0()) { + // const auto& fv0 = collision.foundFV0(); + // auto multiplicity = getMultiplicityEstimator(collision, true); + + // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + // return; + // } + + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, mftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, isFV0A); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChCh, "DATA : Process same-event correlations for MFT-FV0-A h-h case", false); + + // void processSameMftFv0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredMftTracks const&, + // aod::FV0As const& fv0as) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // if (collision.has_foundFV0()) { + // const auto& fv0 = collision.foundFV0(); + // auto multiplicity = getMultiplicityEstimator(collision, true); + + // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + // return; + // } + + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChReassociated, "DATA : Process same-event correlations for MFT-FV0a h-h case reassociated", false); /* void processSameMftFv0aChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, @@ -2438,41 +3734,41 @@ struct HfTaskFlow { if (collision.has_foundFV0()) { const auto& fv0 = collision.foundFV0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + auto multiplicity = getMultiplicityEstimator(collision, true); - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, false, isFV0A); } } PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChReassociated3d, "DATA : Process same-event correlations for MFT-FV0a h-h case 3d reassociated", false); */ - void processSameMftFv0aChChNonAmbiguous(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&, - aod::FV0As const& fv0as) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } - - if (collision.has_foundFV0()) { - const auto& fv0 = collision.foundFV0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, true, isFV0A); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChNonAmbiguous, "DATA : Process same-event correlations for MFT-FV0a h-h non-ambiguous case", false); + // void processSameMftFv0aChChNonAmbiguous(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredMftTracks const&, + // aod::FV0As const& fv0as) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // if (collision.has_foundFV0()) { + // const auto& fv0 = collision.foundFV0(); + // auto multiplicity = getMultiplicityEstimator(collision, true); + + // if (multiplicity < configCollision.minMultiplicity || multiplicity >= configCollision.maxMultiplicity) { + // return; + // } + + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, fv0, fv0as, multiplicity, collision.posZ(), true, true, isFV0A); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameMftFv0aChChNonAmbiguous, "DATA : Process same-event correlations for MFT-FV0a h-h non-ambiguous case", false); // ===================================== // DATA : process same event correlations: TPC-FT0A Ch. Part. - Ch. Part @@ -2487,20 +3783,30 @@ struct HfTaskFlow { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - registry.fill(HIST("Data/hNTracks"), tracks.size()); - const auto multiplicity = getMultiplicityEstimator(collision, true); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A, centralityWeight); + } else { + sameEventTpcFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFIT(sameEventTpcFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A, centralityWeight); + } } } PROCESS_SWITCH(HfTaskFlow, processSameTpcFt0aChCh, "DATA : Process same-event correlations for TPC-FT0-A h-h case", false); @@ -2512,25 +3818,29 @@ struct HfTaskFlow { void processSameTpcFt0aD0Ch(FilteredCollisionsWSelMult::iterator const& collision, HfCandidatesSelD0 const& candidates, aod::FT0s const& ft0as, + FilteredTracksWDcaSel const& tracks, aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A); + fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcFt0aD0Ch, "DATA : Process same-event correlations for TPC-FT0-A D0-h case", false); @@ -2542,109 +3852,142 @@ struct HfTaskFlow { void processSameTpcFt0aLcCh(FilteredCollisionsWSelMult::iterator const& collision, HfCandidatesSelLc const& candidates, aod::FT0s const& ft0as, + FilteredTracksWDcaSel const& tracks, aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); - - if (collision.has_foundFT0()) { - loadGain(bc); - const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); - - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { - return; - } - - sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A); - } - } - PROCESS_SWITCH(HfTaskFlow, processSameTpcFt0aLcCh, "DATA : Process same-event correlations for TPC-FT0-A Lc-h case", false); - - // ===================================== - // DATA : process same event correlations: TPC-FT0A Ch. Part. - Ch. Part - // ===================================== - - void processSameMftFt0aChCh(FilteredCollisionsWSelMult::iterator const& collision, - FilteredMftTracks const& mftTracks, - aod::FT0s const& ft0as, - aod::BCsWithTimestamps const&) - { - if (!(isAcceptedCollision(collision, true))) { - return; - } - - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, mftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A); + sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A, centralityWeight); } } - PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChCh, "DATA : Process same-event correlations for MFT-FT0-A h-h case", false); + PROCESS_SWITCH(HfTaskFlow, processSameTpcFt0aLcCh, "DATA : Process same-event correlations for TPC-FT0-A Lc-h case", false); - void processSameMftFt0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, - soa::SmallGroups const& reassociatedMftTracks, - FilteredMftTracks const&, - aod::FT0s const& ft0as, - aod::BCsWithTimestamps const&) + // ===================================== + // DATA : process same event correlations: TPC-FT0A Ch. Part. - Ch. Part + // ===================================== + + void processSameMftFt0aChCh(FilteredCollisionsWSelMult::iterator const& collision, + FilteredMftTracks const& mftTracks, + aod::FT0s const& ft0as, + FilteredTracksWDcaSel const& tracks, + aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, mftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A, centralityWeight); + } else { + sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFIT(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, mftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, isFT0A, centralityWeight); + } } } - PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated, "DATA : Process same-event correlations for MFT-FT0-A h-h case reassociated", false); + PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChCh, "DATA : Process same-event correlations for MFT-FT0-A h-h case", false); + + // void processSameMftFt0aChChReassociated(FilteredCollisionsWSelMult::iterator const& collision, + // soa::SmallGroups const& reassociatedMftTracks, + // FilteredMftTracks const&, + // aod::FT0s const& ft0as, + // FilteredTracksWDcaSel const& tracks, + // aod::BCsWithTimestamps const&) + // { + // if (!(isAcceptedCollision(collision, true))) { + // return; + // } + + // auto bc = collision.template bc_as(); + // loadEfficiencyCorrection(bc.timestamp()); + + // if (collision.has_foundFT0()) { + + // loadGain(bc); + // const auto& ft0 = collision.foundFT0(); + // float multiplicity = 0.f; + // float centrality = -1.f; + // float centralityWeight = 1.f; + + // if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + // return; + // } + + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + // } else { + // sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + // fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + // } + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated, "DATA : Process same-event correlations for MFT-FT0-A h-h case reassociated", false); void processSameMftFt0aChChReassociated3d(FilteredCollisionsWSelMult::iterator const& collision, soa::SmallGroups const& reassociatedMftTracks, FilteredMftTracks const&, aod::FT0s const& ft0as, + FilteredTracksWDcaSel const& tracks, aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { + loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } else { + sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } } } PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated3d, "DATA : Process same-event correlations for MFT-FT0-A h-h case reassociated 3d", false); @@ -2653,25 +3996,34 @@ struct HfTaskFlow { soa::SmallGroups const& reassociatedMftTracks, FilteredMftTracks const&, aod::FT0s const& ft0as, + FilteredTracksWDcaSel const& tracks, aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } - sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); + } else { + sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, true, isFT0A, centralityWeight); + } } } PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChNonAmbiguous, "DATA : Process same-event correlations for MFT-FT0-A h-h case non ambiguous", false); @@ -2689,20 +4041,25 @@ struct HfTaskFlow { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - registry.fill(HIST("Data/hNTracks"), tracks.size()); - const auto multiplicity = getMultiplicityEstimator(collision, true); + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + } + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, ft0, ft0cs, multiplicity, collision.posZ(), true, isFT0C); + fillCorrelationsFIT(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, ft0, ft0cs, multiplicity, collision.posZ(), true, isFT0C, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcFt0cChCh, "DATA : Process same-event correlations for TPC-FT0C h-h case", false); @@ -2714,25 +4071,29 @@ struct HfTaskFlow { void processSameTpcFt0cD0Ch(FilteredCollisionsWSelMult::iterator const& collision, HfCandidatesSelD0 const& candidates, aod::FT0s const& ft0cs, + FilteredTracksWDcaSel const& tracks, aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0cs, multiplicity, collision.posZ(), true, isFT0C); + fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0cs, multiplicity, collision.posZ(), true, isFT0C, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcFt0cD0Ch, "DATA : Process same-event correlations for TPC-FT0C D0-h case", false); @@ -2744,25 +4105,29 @@ struct HfTaskFlow { void processSameTpcFt0cLcCh(FilteredCollisionsWSelMult::iterator const& collision, HfCandidatesSelLc const& candidates, aod::FT0s const& ft0cs, + FilteredTracksWDcaSel const& tracks, aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEventHf->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0cs, multiplicity, collision.posZ(), true, isFT0C); + fillCorrelationsFIT(sameEventHf, CorrelationContainer::CFStep::kCFStepReconstructed, candidates, ft0, ft0cs, multiplicity, collision.posZ(), true, isFT0C, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameTpcFt0cLcCh, "DATA : Process same-event correlations for TPC-FT0C Lc-h case", false); @@ -2773,29 +4138,171 @@ struct HfTaskFlow { void processSameFt0aFt0cChCh(FilteredCollisionsWSelMult::iterator const& collision, aod::FT0s const&, + FilteredTracksWDcaSel const& tracks, aod::BCsWithTimestamps const&) { if (!(isAcceptedCollision(collision, true))) { return; } - auto bc = collision.bc_as(); + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); if (collision.has_foundFT0()) { loadGain(bc); const auto& ft0 = collision.foundFT0(); - const auto multiplicity = getMultiplicityEstimator(collision, true); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; - if (multiplicity < configCollision.minMultiplicity || multiplicity > configCollision.maxMultiplicity) { + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { return; } sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); - fillCorrelationsFt0aFt0c(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, ft0, ft0, multiplicity, collision.posZ(), true); + fillCorrelationsFt0aFt0c(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, ft0, ft0, multiplicity, collision.posZ(), true, centralityWeight); } } PROCESS_SWITCH(HfTaskFlow, processSameFt0aFt0cChCh, "DATA : Process same-event correlations for FT0A-FT0C h-h case", false); + // =================================================================================================================================================================================================================================================================== + // MONTE-CARLO + // =================================================================================================================================================================================================================================================================== + + void processSameTpcMftChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult::iterator const& collision, + soa::SmallGroups> const& reassociatedMftTracks, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& mftTracks, + aod::McCollisions const& /*mcCollisions*/, + aod::McParticles const& /*particles*/, + aod::BCsWithTimestamps const&) + { + if (!(isAcceptedCollision(collision, true))) { + return; + } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hNTracks"), tracks.size()); + registry.fill(HIST("Data/Mft/hNMftTracks"), mftTracks.size()); + registry.fill(HIST("Data/Mft/hNBestCollisionFwd"), reassociatedMftTracks.size()); + } + + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; + } + + auto uncorrectedMultiplicity = multiplicity; + + if (configCollision.useMultiplicityFromTracksCorrected) { + multiplicity = getCorrectedMultiplicity(tracks); + } + + if (!configTask.removeQAForSystematics) { + registry.fill(HIST("Data/hMultiplicity_uncorrected_vs_corrected"), uncorrectedMultiplicity, multiplicity); + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } else { + sameEventTpcMft->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsReassociatedMftTracks(sameEventTpcMft, CorrelationContainer::CFStep::kCFStepReconstructed, tracks, reassociatedMftTracks, multiplicity, collision.posZ(), true, false, centralityWeight); + } + } + PROCESS_SWITCH(HfTaskFlow, processSameTpcMftChChReassociated3dMcReconstructed, "Mc Rec : Process same-event correlations for TPC-MFT h-h case 3d reassociated", false); + + void processSameMftFt0aChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult::iterator const& collision, + soa::SmallGroups> const& reassociatedMftTracks, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + aod::McCollisions const& /*mcCollisions*/, + aod::McParticles const& /*particles*/, + aod::FT0s const& ft0as, + aod::BCsWithTimestamps const&) + { + if (!(isAcceptedCollision(collision, true))) { + return; + } + + auto bc = collision.template bc_as(); + loadEfficiencyCorrection(bc.timestamp()); + + if (collision.has_foundFT0()) { + + loadGain(bc); + const auto& ft0 = collision.foundFT0(); + float multiplicity = 0.f; + float centrality = -1.f; + float centralityWeight = 1.f; + + if (!isAcceptedEventActivity(collision, tracks, multiplicity, centrality, centralityWeight)) { + return; + } + + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEvent, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } else { + sameEventMftFt0a->fillEvent(multiplicity, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFITReassociatedMftTracks(sameEventMftFt0a, CorrelationContainer::CFStep::kCFStepReconstructed, reassociatedMftTracks, ft0, ft0as, multiplicity, collision.posZ(), true, false, isFT0A, centralityWeight); + } + } + } + PROCESS_SWITCH(HfTaskFlow, processSameMftFt0aChChReassociated3dMcReconstructed, "MC Rec : Process same-event correlations for MFT-FT0-A h-h case reassociated 3d", false); + + void processSameMcGen(FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision, + FilteredMcParticles const& mcParticles, + SmallGroupMcCollisions const& collisions) + { + + if (mcParticles.size() == 0) { // guard against empty filtered batch + return; + } + if (collisions.size() == 0) { + return; + } + + float multiplicity = 0.f; + if (configCollision.useMultiplicityFromTracks) { + for (const auto& track : mcParticles) { + if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + multiplicity += 1; + } + } + registry.fill(HIST("MC/hMultiplicityTracksSize"), multiplicity); + } else { + multiplicity = mcCollision.multMCFT0C(); + registry.fill(HIST("MC/hMultiplicity_multMCFT0C"), multiplicity); + } + + float centrality = 999.f; + centrality = getCentralityEstimator(mcCollision, true); + + if (configCollision.useCentrality) { + if (centrality < configCollision.minCentrality || centrality >= configCollision.maxCentrality) { + return; + } + } + if (!configCollision.useCentrality) { + if ((multiplicity < configCollision.minMultiplicity) || (multiplicity >= configCollision.maxMultiplicity)) { + return; + } + } + + registry.fill(HIST("MC/hCentralityUsed"), centrality); + registry.fill(HIST("MC/hMultiplicityUsed"), multiplicity); + + sameEvent->fillEvent(multiplicity, CorrelationContainer::kCFStepAll); + fillCorrelationsMonteCarlo(sameEvent, CorrelationContainer::CFStep::kCFStepAll, mcParticles, mcParticles, multiplicity, mcCollision.posZ(), true); + } + PROCESS_SWITCH(HfTaskFlow, processSameMcGen, "MC Gen : Process same-event correlations", false); + // =================================================================================================================================================================================================================================================================== // =================================================================================================================================================================================================================================================================== // MIXED EVENT PROCESS FUNCTIONS @@ -2853,10 +4360,69 @@ struct HfTaskFlow { FilteredMftTracks const& mftTracks, aod::BCsWithTimestamps const& bcs) { - mixCollisions(collisions, CorrelationContainer::kCFStepReconstructed, tracks, mftTracks, mixedEvent, bcs); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisions(collisions, CorrelationContainer::kCFStepReconstructed, tracks, mftTracks, mixedEvent, bcs); + } else { + mixCollisions(collisions, CorrelationContainer::kCFStepReconstructed, tracks, mftTracks, mixedEventTpcMft, bcs); + } } PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChCh, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); + void processMixedTpcMftChChBis(FilteredCollisionsWSelMult const& collisions, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracks const& mftTracks, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsBis(collisions, CorrelationContainer::kCFStepReconstructed, tracks, mftTracks, perColTracks, perColMftTracks, mixedEvent, bcs); + } else { + mixCollisionsBis(collisions, CorrelationContainer::kCFStepReconstructed, tracks, mftTracks, perColTracks, perColMftTracks, mixedEventTpcMft, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChBis, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); + + // void processMixedTpcMftChChReassociated2d(FilteredCollisionsWSelMult const& collisions, + // FilteredTracksWDcaSel const& tracks, + // FilteredMftTracks const& /*mftTracks*/, + // soa::SmallGroups const& reassociated2dMftTracks, + // aod::BCsWithTimestamps const& bcs) + // { + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, false, bcs); + // } else { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, false, bcs); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated2d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); + + void processMixedTpcMftChChReassociated3d(FilteredCollisionsWSelMult const& collisions, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracks const& /*mftTracks*/, + soa::SmallGroups const& reassociated3dMftTracks, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated3d, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); + + void processMixedTpcMftChChNonAmbiguous(FilteredCollisionsWSelMult const& collisions, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracks const& /*mftTracks*/, + soa::SmallGroups const& reassociated2dMftTracks, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEvent, true, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated2dMftTracks, perColTracks, perColReassociated2dTracks, mixedEventTpcMft, true, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChNonAmbiguous, "DATA : Process mixed-event correlations for TPC-MFT h-h case", false); + // ===================================== // DATA : process mixed event correlations: TPC-MFT HF-h case for D0 // ===================================== @@ -2888,49 +4454,49 @@ struct HfTaskFlow { // DATA : process mixed event correlations: TPC-FV0-A ch part. - ch. part. case // ===================================== - void processMixedTpcFv0aChCh(FilteredCollisionsWSelMult const& collisions, - FilteredTracksWDcaSel const& tracks, - aod::FV0As const& fv0as) - { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, fv0as, perColTracks, mixedEvent, isFV0A); - } - PROCESS_SWITCH(HfTaskFlow, processMixedTpcFv0aChCh, "DATA : Process mixed-event correlations for TPC-FV0-A h-h case", false); + // void processMixedTpcFv0aChCh(FilteredCollisionsWSelMult const& collisions, + // FilteredTracksWDcaSel const& tracks, + // aod::FV0As const& fv0as) + // { + // mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, fv0as, perColTracks, mixedEvent, isFV0A); + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedTpcFv0aChCh, "DATA : Process mixed-event correlations for TPC-FV0-A h-h case", false); // ===================================== // DATA : process mixed event correlations: TPC-FV0-A D0 - ch. part. case // ===================================== - void processMixedTpcFv0aD0Ch(FilteredCollisionsWSelMult const& collisions, - HfCandidatesSelD0 const& candidates, - aod::FV0As const& fv0as) - { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, fv0as, perColD0s, mixedEventHf, isFV0A); - } - PROCESS_SWITCH(HfTaskFlow, processMixedTpcFv0aD0Ch, "DATA : Process mixed-event correlations for TPC-FV0-A D0-h case", false); + // void processMixedTpcFv0aD0Ch(FilteredCollisionsWSelMult const& collisions, + // HfCandidatesSelD0 const& candidates, + // aod::FV0As const& fv0as) + // { + // mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, fv0as, perColD0s, mixedEventHf, isFV0A); + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedTpcFv0aD0Ch, "DATA : Process mixed-event correlations for TPC-FV0-A D0-h case", false); // ===================================== // DATA : process mixed event correlations: TPC-FV0-A Lc - ch. part. case // ===================================== - void processMixedTpcFv0aLcCh(FilteredCollisionsWSelMult const& collisions, - HfCandidatesSelLc const& candidates, - aod::FV0As const& fv0as) - { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, fv0as, perColLcs, mixedEventHf, isFV0A); - } - PROCESS_SWITCH(HfTaskFlow, processMixedTpcFv0aLcCh, "DATA : Process mixed-event correlations for TPC-FV0-A Lc-h case", false); + // void processMixedTpcFv0aLcCh(FilteredCollisionsWSelMult const& collisions, + // HfCandidatesSelLc const& candidates, + // aod::FV0As const& fv0as) + // { + // mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, fv0as, perColLcs, mixedEventHf, isFV0A); + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedTpcFv0aLcCh, "DATA : Process mixed-event correlations for TPC-FV0-A Lc-h case", false); // ===================================== // DATA : process mixed event correlations: TPC-FV0-A ch part. - ch. part. case // ===================================== - void processMixedMftFv0aChCh(FilteredCollisionsWSelMult const& collisions, - FilteredMftTracks const& mftTracks, - aod::FV0As const& fv0as) - { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, mftTracks, fv0as, perColMftTracks, mixedEvent, isFV0A); - } - PROCESS_SWITCH(HfTaskFlow, processMixedMftFv0aChCh, "DATA : Process mixed-event correlations for Mft-FV0-A h-h case", false); + // void processMixedMftFv0aChCh(FilteredCollisionsWSelMult const& collisions, + // FilteredMftTracks const& mftTracks, + // aod::FV0As const& fv0as) + // { + // mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, mftTracks, fv0as, perColMftTracks, mixedEvent, isFV0A); + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedMftFv0aChCh, "DATA : Process mixed-event correlations for Mft-FV0-A h-h case", false); // ===================================== // DATA : process mixed event correlations: TPC-FT0-A ch part. - ch. part. case @@ -2938,9 +4504,14 @@ struct HfTaskFlow { void processMixedTpcFt0aChCh(FilteredCollisionsWSelMult const& collisions, FilteredTracksWDcaSel const& tracks, - aod::FT0s const& ft0s) + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, ft0s, perColTracks, mixedEvent, isFT0A); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, ft0s, perColTracks, mixedEvent, isFT0A, bcs); + } else { + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, ft0s, perColTracks, mixedEventTpcFt0a, isFT0A, bcs); + } } PROCESS_SWITCH(HfTaskFlow, processMixedTpcFt0aChCh, "DATA : Process mixed-event correlations for TPC-FT0-A h-h case", false); @@ -2950,9 +4521,11 @@ struct HfTaskFlow { void processMixedTpcFt0aD0Ch(FilteredCollisionsWSelMult const& collisions, HfCandidatesSelD0 const& candidates, - aod::FT0s const& ft0s) + FilteredTracksWDcaSel const& tracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, ft0s, perColD0s, mixedEventHf, isFT0A); + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, candidates, ft0s, perColD0s, mixedEventHf, isFT0A, bcs); } PROCESS_SWITCH(HfTaskFlow, processMixedTpcFt0aD0Ch, "DATA : Process mixed-event correlations for TPC-FT0-A D0-h case", false); @@ -2962,9 +4535,11 @@ struct HfTaskFlow { void processMixedTpcFt0aLcCh(FilteredCollisionsWSelMult const& collisions, HfCandidatesSelLc const& candidates, - aod::FT0s const& ft0s) + FilteredTracksWDcaSel const& tracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, ft0s, perColLcs, mixedEventHf, isFT0A); + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, candidates, ft0s, perColLcs, mixedEventHf, isFT0A, bcs); } PROCESS_SWITCH(HfTaskFlow, processMixedTpcFt0aLcCh, "DATA : Process mixed-event correlations for TPC-FT0-A Lc-h case", false); @@ -2974,21 +4549,73 @@ struct HfTaskFlow { void processMixedMftFt0aChCh(FilteredCollisionsWSelMult const& collisions, FilteredMftTracks const& mftTracks, - aod::FT0s const& ft0s) + FilteredTracksWDcaSel const& tracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, mftTracks, ft0s, perColMftTracks, mixedEvent, isFT0A); + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, mftTracks, ft0s, perColMftTracks, mixedEvent, isFT0A, bcs); + } else { + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, mftTracks, ft0s, perColMftTracks, mixedEventMftFt0a, isFT0A, bcs); + } } PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChCh, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); + // void processMixedMftFt0aChChReassociated2d(FilteredCollisionsWSelMult const& collisions, + // FilteredMftTracks const& /*mftTracks*/, + // FilteredTracksWDcaSel const& tracksTpc, + // soa::SmallGroups const& reassociated2dMftTracks, + // aod::FT0s const& ft0s, + // aod::BCsWithTimestamps const& bcs) + // { + // if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, false, bcs); + // } else { + // mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, false, bcs); + // } + // } + // PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated2d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); + + void processMixedMftFt0aChChReassociated3d(FilteredCollisionsWSelMult const& collisions, + FilteredMftTracks const& /*mftTracks*/, + FilteredTracksWDcaSel const& tracksTpc, + soa::SmallGroups const& reassociated3dMftTracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated3d, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); + + void processMixedMftFt0aChChNonAmbiguous(FilteredCollisionsWSelMult const& collisions, + FilteredMftTracks const& /*mftTracks*/, + FilteredTracksWDcaSel const& tracksTpc, + soa::SmallGroups const& reassociated2dMftTracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEvent, true, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated2dMftTracks, ft0s, perColReassociated2dTracks, perColReassociated2dTracks, mixedEventMftFt0a, true, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChNonAmbiguous, "DATA : Process mixed-event correlations for MFT-FT0-A h-h case", false); + // ===================================== // DATA : process mixed event correlations: TPC-FT0C ch part. - ch. part. case // ===================================== void processMixedTpcFt0cChCh(FilteredCollisionsWSelMult const& collisions, FilteredTracksWDcaSel const& tracks, - aod::FT0s const& ft0s) + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, ft0s, perColTracks, mixedEvent, isFT0C); + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, ft0s, perColTracks, mixedEvent, isFT0C, bcs); } PROCESS_SWITCH(HfTaskFlow, processMixedTpcFt0cChCh, "DATA : Process mixed-event correlations for TPC-FT0C h-h case", false); @@ -2998,9 +4625,11 @@ struct HfTaskFlow { void processMixedTpcFt0cD0Ch(FilteredCollisionsWSelMult const& collisions, HfCandidatesSelD0 const& candidates, - aod::FT0s const& ft0s) + FilteredTracksWDcaSel const& tracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, ft0s, perColD0s, mixedEventHf, isFT0C); + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, candidates, ft0s, perColD0s, mixedEventHf, isFT0C, bcs); } PROCESS_SWITCH(HfTaskFlow, processMixedTpcFt0cD0Ch, "DATA : Process mixed-event correlations for TPC-FT0C D0-h case", false); @@ -3010,9 +4639,11 @@ struct HfTaskFlow { void processMixedTpcFt0cLcCh(FilteredCollisionsWSelMult const& collisions, HfCandidatesSelLc const& candidates, - aod::FT0s const& ft0s) + FilteredTracksWDcaSel const& tracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, candidates, ft0s, perColLcs, mixedEventHf, isFT0C); + mixCollisionsFIT(collisions, CorrelationContainer::kCFStepReconstructed, tracks, candidates, ft0s, perColLcs, mixedEventHf, isFT0C, bcs); } PROCESS_SWITCH(HfTaskFlow, processMixedTpcFt0cLcCh, "DATA : Process mixed-event correlations for TPC-FT0C Lc-h case", false); @@ -3021,12 +4652,257 @@ struct HfTaskFlow { // ===================================== void processMixedFt0aFt0cChCh(FilteredCollisionsWSelMult const& collisions, - aod::FT0s const& ft0s) + FilteredTracksWDcaSel const& tracksTpc, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) { - mixCollisionsFt0aFt0c(collisions, CorrelationContainer::kCFStepReconstructed, ft0s, ft0s, mixedEvent); + mixCollisionsFt0aFt0c(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, ft0s, ft0s, mixedEvent, bcs); } PROCESS_SWITCH(HfTaskFlow, processMixedFt0aFt0cChCh, "DATA : Process mixed-event correlations for FT0A-FT0C h-h case", false); + // =================================================================================================================================================================================================================================================================== + // MONTE-CARLO + // =================================================================================================================================================================================================================================================================== + + void processMixedTpcMftChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult const& collisions, + FilteredTracksWDcaSel const& tracks, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + soa::SmallGroups> const& reassociated3dMftTracks, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracks, tracks, reassociated3dMftTracks, perColTracks, perColReassociated3dTracks, mixedEventTpcMft, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedTpcMftChChReassociated3dMcReconstructed, "MC Rec : Process mixed-event correlations for TPC-MFT h-h case", false); + + void processMixedMftFt0aChChReassociated3dMcReconstructed(FilteredCollisionsWSelMult const& collisions, + FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + FilteredTracksWDcaSel const& tracksTpc, + soa::SmallGroups> const& reassociated3dMftTracks, + aod::FT0s const& ft0s, + aod::BCsWithTimestamps const& bcs) + { + if (!configTask.doEtaDependentFlow && !configTask.doVariationContainers) { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEvent, false, bcs); + } else { + mixCollisionsReassociatedMftTracks(collisions, CorrelationContainer::kCFStepReconstructed, tracksTpc, reassociated3dMftTracks, ft0s, perColReassociated3dTracks, perColReassociated3dTracks, mixedEventMftFt0a, false, bcs); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedMftFt0aChChReassociated3dMcReconstructed, "MC Rec : Process mixed-event correlations for MFT-FT0-A h-h case", false); + + void processMixedMcGen(FilteredMcCollisionsWMultWCollsExtra const& mcCollisions, + FilteredMcParticles const& mcParticles, + SmallGroupMcCollisions const& collisions) + { + + auto getTracksSize = [&mcParticles, this](FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision) { + auto associatedTracks = mcParticles.sliceByCached(o2::aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), this->cache); + auto multiplicity = 0; + if (configCollision.useMultiplicityFromTracks) { + for (const auto& track : associatedTracks) { + if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + multiplicity += 1; + } + } + } else { + multiplicity = mcCollision.multMCFT0C(); + } + return multiplicity; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getTracksSize)>; + MixedBinning const binningOnVtxAndMult{{getTracksSize}, {configAxis.binsMixingVertex, configAxis.binsMixingMultiplicity}}; + + auto tracksTuple = std::make_tuple(mcParticles, mcParticles); + + Pair pairs{binningOnVtxAndMult, configTask.nMixedEvents, -1, mcCollisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + auto groupedCollisions1 = collisions.sliceBy(collisionPerMcCollision, collision1.globalIndex()); + auto groupedCollisions2 = collisions.sliceBy(collisionPerMcCollision, collision2.globalIndex()); + if (groupedCollisions1.size() == 0) { + continue; + } + if (groupedCollisions2.size() == 0) { + continue; + } + + float multiplicityCollision1 = 0.f; + float multiplicityCollision2 = 0.f; + + if (configCollision.useMultiplicityFromTracks) { + // for (const auto& track : tracks1) { + // if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + // multiplicityCollision1 += 1; + // } + // } + // for (const auto& track : tracks2) { + // if (std::abs(track.eta()) < configCentral.etaCentralTrackMax && track.pt() >= configCentral.ptCentralTrackMin && track.pt() <= configCentral.ptCentralTrackMax) { + // multiplicityCollision2 += 1; + // } + // } + multiplicityCollision1 = getTracksSize(collision1); + multiplicityCollision2 = getTracksSize(collision2); + registry.fill(HIST("MC/hMixedMultiplicityTracksSize_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicityTracksSize_coll2"), multiplicityCollision2); + } else { + multiplicityCollision1 = collision1.multMCFT0C(); + multiplicityCollision2 = collision2.multMCFT0C(); + registry.fill(HIST("MC/hMixedMultiplicity_multMCFT0C_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicity_multMCFT0C_coll2"), multiplicityCollision2); + } + + float centralityCollision1 = 999.f; + float centralityCollision2 = 999.f; + centralityCollision1 = getCentralityEstimator(collision1, false); + centralityCollision2 = getCentralityEstimator(collision2, false); + registry.fill(HIST("MC/hMixedCentrality_coll1"), centralityCollision1); + registry.fill(HIST("MC/hMixedCentrality_coll2"), centralityCollision2); + + if (configCollision.useCentrality) { + if (centralityCollision1 < configCollision.minCentrality || centralityCollision1 >= configCollision.maxCentrality) { + continue; + } + if (centralityCollision2 < configCollision.minCentrality || centralityCollision2 >= configCollision.maxCentrality) { + continue; + } + } + + registry.fill(HIST("MC/hMixedCentralityUsed_coll1"), centralityCollision1); + registry.fill(HIST("MC/hMixedCentralityUsed_coll2"), centralityCollision2); + + if (!configCollision.useCentrality) { + if ((multiplicityCollision1 < configCollision.minMultiplicity || multiplicityCollision1 >= configCollision.maxMultiplicity)) { + continue; + } + if ((multiplicityCollision2 < configCollision.minMultiplicity || multiplicityCollision2 >= configCollision.maxMultiplicity)) { + continue; + } + } + + registry.fill(HIST("MC/hMixedMultiplicityUsed_coll1"), multiplicityCollision1); + registry.fill(HIST("MC/hMixedMultiplicityUsed_coll2"), multiplicityCollision2); + + mixedEvent->fillEvent(multiplicityCollision1, CorrelationContainer::kCFStepAll); + fillCorrelationsMonteCarlo(mixedEvent, CorrelationContainer::CFStep::kCFStepAll, tracks1, tracks2, multiplicityCollision1, collision1.posZ(), false); + } + } + PROCESS_SWITCH(HfTaskFlow, processMixedMcGen, "MC Gen : Process mixed-event correlations", false); + + void processTrackEfficiencies(FilteredMcCollisionsWMultWCollsExtra::iterator const& mcCollision, + SmallGroupMcCollisions const& reconstructedCollisions, + FilteredTracksWDcaSelWLabels const& tpcTracks, FilteredMftTracksWCollsMcLabels const& /*mftTracks*/, + soa::Filtered> const& reassociated3dMftTracks, + FilteredMcParticles const& mcParticles) + { + if (std::abs(mcCollision.posZ()) >= configCollision.zVertexMax) { + return; + } + + // check whether we have a selected reconstructed collision corresponding to the MC collision + bool hasReconstructedCollision = false; + for (const auto& reconstructedCollision : reconstructedCollisions) { + if (!isAcceptedCollision(reconstructedCollision)) { + continue; + } + if (reconstructedCollision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + hasReconstructedCollision = true; + } + + // fill histogram for MC Gen TPC particles + for (const auto& particle : mcParticles) { + auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + // check MC related properties of the particle + if (!particle.isPhysicalPrimary() || !particle.producedByGenerator()) { + continue; + } + // check charge of the particle + if (pdgParticle == nullptr || std::abs(pdgParticle->Charge()) == 0) { + continue; + } + // check kinematics of the particle for TPC + if (std::abs(particle.eta()) <= configTask.etaMcParticlesTriggerMax && + particle.pt() >= configTask.ptMcParticlesTriggerMin && + particle.pt() <= configTask.ptMcParticlesTriggerMax) { + if (hasReconstructedCollision) { + registry.fill(HIST("MC/hEfficiencyTrigger"), particle.pt(), particle.eta(), mcCollision.posZ()); + } + } + // check kinematics of the particle for MFT + if (particle.eta() <= configTask.etaMcParticlesAssocMax && + particle.eta() >= configTask.etaMcParticlesAssocMin && + particle.pt() >= configTask.ptMcParticlesAssocMin && + particle.pt() <= configTask.ptMcParticlesAssocMax) { + + if (configMft.useMftPhiCut) { + if (particle.phi() < configMft.phiMftTrackMax && particle.phi() > configMft.phiMftTrackMin && + (particle.phi() <= configMft.phiMftTrackMiddleMin || particle.phi() >= configMft.phiMftTrackMiddleMax)) { + if (hasReconstructedCollision) { + registry.fill(HIST("MC/hEfficiencyAssociated"), particle.pt(), particle.eta(), mcCollision.posZ()); + } + } + } else { + if (hasReconstructedCollision) { + registry.fill(HIST("MC/hEfficiencyAssociated"), particle.pt(), particle.eta(), mcCollision.posZ()); + } + } + } + } + + // fill histogram for reconstructed TPC tracks + for (const auto& reconstructedCollision : reconstructedCollisions) { + if (!isAcceptedCollision(reconstructedCollision)) { + continue; + } + if (reconstructedCollision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + + auto groupedTpcTracks = tpcTracks.sliceBy(perColTracks, reconstructedCollision.globalIndex()); + for (const auto& tpcTrack : groupedTpcTracks) { + if (!isAcceptedCentralTrack(tpcTrack)) { + continue; + } + if (!tpcTrack.has_mcParticle()) { + continue; + } + auto tpcParticle = tpcTrack.template mcParticle_as(); + if (reconstructedCollision.mcCollisionId() != tpcParticle.mcCollisionId()) { + continue; + } + if (tpcParticle.isPhysicalPrimary()) { + registry.fill(HIST("Data/hEfficiencyTrigger"), tpcParticle.pt(), tpcParticle.eta(), mcCollision.posZ()); + } + } + + auto groupedreassociated3dMftTracks = reassociated3dMftTracks.sliceBy(perColReassociated3dTracks, reconstructedCollision.globalIndex()); + for (const auto& reassociated3dMftTrack : groupedreassociated3dMftTracks) { + if (!reassociated3dMftTrack.has_mcParticle()) { + continue; + } + auto templatedMftTrack = reassociated3dMftTrack.template mfttrack_as(); + if (!isAcceptedMftTrack(templatedMftTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), false)) { + continue; + } + auto mftParticle = templatedMftTrack.template mcParticle_as(); + if (reconstructedCollision.mcCollisionId() != mftParticle.mcCollisionId()) { + continue; + } + if (mftParticle.isPhysicalPrimary()) { + registry.fill(HIST("Data/hEfficiencyAssociated"), mftParticle.pt(), mftParticle.eta(), mcCollision.posZ()); + } + } + } + } + PROCESS_SWITCH(HfTaskFlow, processTrackEfficiencies, "process track efficiencies for TPC and MFT", false); + }; // End of struct WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFC/Utils/utilsCorrelations.h b/PWGHF/HFC/Utils/utilsCorrelations.h index 48bf2e3fee1..884be2d1704 100644 --- a/PWGHF/HFC/Utils/utilsCorrelations.h +++ b/PWGHF/HFC/Utils/utilsCorrelations.h @@ -18,6 +18,7 @@ #include "PWGHF/Core/DecayChannels.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -29,6 +30,9 @@ #include #include +#include +#include +#include namespace o2::analysis::hf_correlations { @@ -96,7 +100,7 @@ bool passPIDSelection(Atrack const& track, SpeciesContainer const mPIDspecies, auto const& pid = mPIDspecies->at(speciesIndex); auto nSigmaTPC = o2::aod::pidutils::tpcNSigma(pid, track); - if (tofForced && !track.hasTOF()) { + if ((track.pt() > ptThreshold) && !track.hasTOF()) { return false; } @@ -227,5 +231,62 @@ int findLeadingParticleMcGen(TMcParticles const& mcParticles, T1 const etaTrackM } return leadingParticle.globalIndex(); } + +// ======= Beauty-mother information of non-prompt charm hadrons ============ +constexpr float PtBMotherDefault{-1.f}; // pT(B mother) returned when the charm hadron is not from beauty +constexpr float DeltaPhiBMotherDefault{-999.f}; // phi(charm hadron) - phi(B mother) returned when the charm hadron is not from beauty + +/// Gets the generated beauty mother of a reconstructed charm-hadron candidate matched to a non-prompt charm hadron +/// \tparam TTracks is the track table with MC labels, used to access the candidate prongs +/// \param candidate is the reconstructed candidate (with originMcRec column) +/// \param mcParticles is the table of MC particles +/// \param pdgCharmHadron is the PDG code of the charm hadron (e.g. Pdg::kDPlus), antiparticles are accepted +/// \param depthMaxDaughters is the max. depth of the candidate prong in the charm-hadron decay chain (> 1 for resonant channels) +/// \return pair (pT of the B mother, phi(candidate, reco) - phi(B mother, gen) in [-pi, pi)), default values if the candidate is not from beauty +template +std::pair getBMotherInfoMcRec(TCandidate const& candidate, TMcParticles const& mcParticles, int pdgCharmHadron, int8_t depthMaxDaughters = 2) +{ + std::pair bMotherInfo{PtBMotherDefault, DeltaPhiBMotherDefault}; + if (candidate.originMcRec() != RecoDecay::OriginType::NonPrompt) { + return bMotherInfo; + } + // retrieve the generated charm hadron from the MC particle of one of its daughters + auto prong0 = candidate.template prong0_as(); + if (!prong0.has_mcParticle()) { + return bMotherInfo; + } + auto prong0McPart = prong0.template mcParticle_as(); + int const indexCharmHadronGen = RecoDecay::getMother(mcParticles, prong0McPart, pdgCharmHadron, true, nullptr, depthMaxDaughters); + if (indexCharmHadronGen < 0) { + return bMotherInfo; + } + // same procedure used in the candidate creators to fill PtBhadMotherPart + auto charmHadronGen = mcParticles.rawIteratorAt(indexCharmHadronGen - mcParticles.offset()); + std::vector idxBhadMothers{}; + if (RecoDecay::getCharmHadronOrigin(mcParticles, charmHadronGen, false, &idxBhadMothers) != RecoDecay::OriginType::NonPrompt || idxBhadMothers.empty()) { + return bMotherInfo; + } + auto bHadMother = mcParticles.rawIteratorAt(idxBhadMothers[0] - mcParticles.offset()); + bMotherInfo.first = bHadMother.pt(); + bMotherInfo.second = RecoDecay::constrainAngle(candidate.phi() - bHadMother.phi(), -o2::constants::math::PIHalf); + return bMotherInfo; +} + +/// Gets the beauty mother of a generated charm hadron +/// \param particle is the generated charm hadron (with originMcGen and idxBhadMotherPart columns) +/// \param mcParticles is the table of MC particles containing the B mother +/// \return pair (pT of the B mother, phi(charm hadron) - phi(B mother) in [-pi, pi)), default values if the charm hadron is not from beauty +template +std::pair getBMotherInfoMcGen(TParticle const& particle, TMcParticles const& mcParticles) +{ + std::pair bMotherInfo{PtBMotherDefault, DeltaPhiBMotherDefault}; + if (particle.originMcGen() != RecoDecay::OriginType::NonPrompt || particle.idxBhadMotherPart() < 0) { + return bMotherInfo; + } + auto bHadMother = mcParticles.rawIteratorAt(particle.idxBhadMotherPart() - mcParticles.offset()); + bMotherInfo.first = bHadMother.pt(); + bMotherInfo.second = RecoDecay::constrainAngle(particle.phi() - bHadMother.phi(), -o2::constants::math::PIHalf); + return bMotherInfo; +} } // namespace o2::analysis::hf_correlations #endif // PWGHF_HFC_UTILS_UTILSCORRELATIONS_H_ diff --git a/PWGHF/HFL/DataModel/ElectronSelectionTable.h b/PWGHF/HFL/DataModel/ElectronSelectionTable.h index 4906b01d229..d819332e3ba 100644 --- a/PWGHF/HFL/DataModel/ElectronSelectionTable.h +++ b/PWGHF/HFL/DataModel/ElectronSelectionTable.h @@ -93,8 +93,9 @@ DECLARE_SOA_COLUMN(TpcChi2NClTrack, tpcChi2NClTrack, float); //! Chi2 / DECLARE_SOA_COLUMN(DcaXYTrack, dcaXYTrack, float); //! dca of the electron in xy direction DECLARE_SOA_COLUMN(DcaZTrack, dcaZTrack, float); //! dca of the electron in z direction -DECLARE_SOA_COLUMN(M02El, m02El, float); //! M02 of the electron cluster -DECLARE_SOA_COLUMN(EopEl, eopEl, float); //! energy momentum ratio of the electron +DECLARE_SOA_COLUMN(EopEl, eopEl, float); //! energy momentum ratio of the electron +DECLARE_SOA_COLUMN(M02El, m02El, float); //! M02 of the electron cluster + DECLARE_SOA_COLUMN(LSMassEE, lSMassEE, std::vector); //! mass of the Like sign electron pair DECLARE_SOA_COLUMN(ULSMassEE, uLSMassEE, std::vector); //! mass of UnLike sign electron pair DECLARE_SOA_COLUMN(NElPairLS, nElPairLS, int); //! Number of Like sign electron pair diff --git a/PWGHF/HFL/TableProducer/electronSelectionWithTpcEmcal.cxx b/PWGHF/HFL/TableProducer/electronSelectionWithTpcEmcal.cxx index 358d5d3642b..e41f634964a 100644 --- a/PWGHF/HFL/TableProducer/electronSelectionWithTpcEmcal.cxx +++ b/PWGHF/HFL/TableProducer/electronSelectionWithTpcEmcal.cxx @@ -85,7 +85,8 @@ struct HfElectronSelectionWithTpcEmcal { Configurable dcaZTrackMax{"dcaZTrackMax", 1.0f, "DCA Z cut"}; Configurable etaTrackMax{"etaTrackMax", 0.6f, "Eta range for electron tracks"}; Configurable etaTrackMin{"etaTrackMin", -0.6f, "Eta range for electron tracks"}; - Configurable ptTrackMin{"ptTrackMin", 3.0f, "Transverse MOmentum range for electron tracks"}; + Configurable ptTrackMin{"ptTrackMin", 3.0f, "Min Transverse MOmentum range for electron tracks"}; + Configurable ptTrackMax{"ptTrackMax", 12.0f, "Max Transverse MOmentum range for electron tracks"}; Configurable tpccrossCut{"tpccrossCut", 70, "TPC crossrows cut"}; Configurable itsChi2{"itsChi2", 36, "ITS chi2 cluster cut"}; Configurable tpcChi2NCl{"tpcChi2NCl", 4, "TPC chi2 cluster cut"}; @@ -286,7 +287,7 @@ struct HfElectronSelectionWithTpcEmcal { if ((track.phi() < phiTrackEMCalMin || track.phi() > phiTrackEMCalMax) && (track.phi() < phiTrackDCalMin || track.phi() > phiTrackDCalMax)) { return false; } - if (track.pt() < ptTrackMin) { + if (track.pt() < ptTrackMin || track.pt() > ptTrackMax) { return false; } return true; @@ -325,7 +326,7 @@ struct HfElectronSelectionWithTpcEmcal { if ((track.phi() < phiTrackEMCalMin || track.phi() > phiTrackEMCalMax) && (track.phi() < phiTrackDCalMin || track.phi() > phiTrackDCalMax)) { return false; } - if (track.pt() < ptTrackMin) { + if (track.pt() < ptTrackMin || track.pt() > ptTrackMax) { return false; } return true; @@ -589,7 +590,7 @@ struct HfElectronSelectionWithTpcEmcal { } // Pass multiplicities and other required parameters for this electron - hfElectronSelection(electron.collisionId(), electron.globalIndex(), electron.eta(), electron.phi(), electron.pt(), electron.tpcNSigmaEl(), electron.tofNSigmaEl(), electron.tpcNClsCrossedRows(), electron.tpcCrossedRowsOverFindableCls(), electron.itsChi2NCl(), electron.tpcChi2NCl(), electron.dcaXY(), electron.dcaZ(), m02, eop, vecLSMass, vecULSMass, nElPairsLS, nElPairsUS, isEMcal); + hfElectronSelection(electron.collisionId(), electron.globalIndex(), electron.eta(), electron.phi(), electron.pt(), electron.tpcNSigmaEl(), electron.tofNSigmaEl(), electron.tpcNClsCrossedRows(), electron.tpcCrossedRowsOverFindableCls(), electron.itsChi2NCl(), electron.tpcChi2NCl(), electron.dcaXY(), electron.dcaZ(), eop, m02, vecLSMass, vecULSMass, nElPairsLS, nElPairsUS, isEMcal); } // Electron Identification template diff --git a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx index a172f752afc..6fa6239f900 100644 --- a/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx +++ b/PWGHF/HFL/Tasks/taskElectronWeakBoson.cxx @@ -44,9 +44,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -76,6 +78,7 @@ struct HfTaskElectronWeakBoson { Configurable vtxZ{"vtxZ", 10.f, ""}; + Configurable applyTrackSys{"applyTrackSys", false, "apply additional track cuts for systematic study"}; Configurable etaTrMin{"etaTrMin", -1.0f, "minimun track eta"}; Configurable etaTrMax{"etaTrMax", 1.0f, "maximum track eta"}; Configurable etaEmcMax{"etaEmcMax", 0.6f, "maximum track eta"}; @@ -83,7 +86,6 @@ struct HfTaskElectronWeakBoson { Configurable chi2ItsMax{"chi2ItsMax", 15.0f, "its chi2 cut"}; Configurable ptMin{"ptMin", 3.0f, "minimum pT cut"}; Configurable ptAssMin{"ptAssMin", 0.15, "minimum pT cut for associated hadrons"}; - Configurable ptMatch{"ptMatch", 0.001, "pT match in Z->ee and associated tracks"}; Configurable ptZeeMin{"ptZeeMin", 20.0f, "minimum pT cut for Zee"}; Configurable chi2TpcMax{"chi2TpcMax", 4.0f, "tpc chi2 cut"}; Configurable nclItsMin{"nclItsMin", 2.0f, "its # of cluster cut"}; @@ -123,7 +125,10 @@ struct HfTaskElectronWeakBoson { Configurable applySel8{"applySel8", true, "Apply sel8 filter or not"}; // CCDB service configurations - Configurable cfgCCDBPath{"cfgCCDBPath", "Users/m/mpuccio/EventFiltering/OTS/", "Path to CCDB for trigger data"}; + // Zorro's own default (Common/Core/Zorro.h: mBaseCCDBPath = "EventFiltering/Zorro/"). + // The old value "Users/m/mpuccio/EventFiltering/OTS/" is a personal area from early Run 3 + // and no longer holds FilterCounters, so Zorro::initCCDB aborts the job on it. + Configurable cfgCCDBPath{"cfgCCDBPath", "EventFiltering/Zorro/", "Path to CCDB for trigger data"}; Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; // KFParticle @@ -139,22 +144,75 @@ struct HfTaskElectronWeakBoson { Configurable centralityMax{"centralityMax", 101, "maximum cut on centrality selection"}; Configurable> centralityBins{"centralityBins", {0, 20, 60, 100}, "centrality bins"}; - // QA for Z->ee - Configurable enableZeeRecoQA{"enableZeeRecoQA", false, "Enable QA for Z->ee reconstruction"}; - Configurable massZMinQA{"massZMinQA", 0.1, "minimum mass cut for Zee Reco QA"}; + // QA for Z->ee. + // Grouped in a ConfigurableGroup to stay below the O2 limit of 100 task members. + struct : ConfigurableGroup { + Configurable enableZeeTrkRecoQA{"enableZeeTrkRecoQA", false, "Enable QA for track info Z->ee reconstruction"}; + Configurable enableZeeEmcRecoQA{"enableZeeEmcRecoQA", false, "Enable QA for Emc info Z->ee reconstruction"}; + Configurable massZMinQA{"massZMinQA", 0.1, "minimum mass cut for Zee Reco QA"}; + } zeeQaCfg; + + // UE (same remark: grouped, no prefix, option names unchanged) + struct : ConfigurableGroup { + Configurable nRandomCones{"nRandomCones", 100, "number of random cones"}; + Configurable rcHardE{"rcHardE", 5.0, "hard cluster veto energy"}; + Configurable rcVetoR{"rcVetoR", 0.4, "veto radius"}; + Configurable useUEsub{"useUEsub", true, "apply UE subtraction in isolation"}; + } ueCfg; + + // W->e - h correlation. Grouped so that the three options cost one task member instead of + // three: the struct is close to the 99 the framework allows. + struct : ConfigurableGroup { + Configurable enableWeHadron{"enableWeHadron", false, "fill the isolated-electron - hadron correlation"}; + Configurable ptWeMin{"ptWeMin", 20.0f, "minimum pT of the trigger electron"}; + Configurable vetoZeeElectron{"vetoZeeElectron", true, "reject trigger electrons that build a Z->ee candidate"}; + } weCfg; + + // Systematics by cut variation (method C): one extra THnSparse axis whose bin 0 is the + // nominal selection and bins 1..N the variations. + struct : ConfigurableGroup { + Configurable enableSystematics{"enableSystematics", false, "add the cut-variation axis"}; + // associated hadron, checked against dcaxyTrk / nclusterTPC (crossed rows) / nclusterITS + Configurable> varDcaxyMax{"varDcaxyMax", {}, "variations of dcaxyMax"}; + Configurable> varNclCrossTpcMin{"varNclCrossTpcMin", {}, "variations of nclcrossTpcMin"}; + Configurable> varNclItsMin{"varNclItsMin", {}, "variations of nclItsMin"}; + // W trigger electron identification + Configurable> varEopMin{"varEopMin", {}, "variations of eopMin"}; + Configurable> varEopMax{"varEopMax", {}, "variations of eopMax"}; + Configurable> varNsigTpcMin{"varNsigTpcMin", {}, "variations of nsigTpcMin"}; + Configurable> varNsigTpcMax{"varNsigTpcMax", {}, "variations of nsigTpcMax"}; + Configurable> varM02Min{"varM02Min", {}, "variations of m02Min"}; + Configurable> varM02Max{"varM02Max", {}, "variations of m02Max"}; + // Z candidate. Two-sided: reconstructedZ holds every KF chi2-passing pair at any mass, + // so a wider window really adds candidates instead of being a no-op. + Configurable> varMassZMin{"varMassZMin", {}, "variations of massZMin"}; + Configurable> varMassZMax{"varMassZMax", {}, "variations of massZMax"}; + } sysCfg; + + // MC : efficiency of the associated hadrons in the Z-h correlation. + struct : ConfigurableGroup { + std::string prefix = "mcEfficiency"; // JSON group name + Configurable requirePhysicalPrimary{"requirePhysicalPrimary", true, "require isPhysicalPrimary for the generated associated hadrons"}; + Configurable requireSameMcCollision{"requireSameMcCollision", true, "require the matched MC particle to belong to the MC collision of the reconstructed one"}; + ConfigurableAxis confaxisPtHadron{"confaxisPtHadron", {VARIABLE_WIDTH, 0.0, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0, 20.0, 30.0, 50.0, 100.0}, "p_{T,hadron} (GeV/c)"}; + ConfigurableAxis confaxisEtaHadron{"confaxisEtaHadron", {20, -1.0, 1.0}, "#eta_{hadron}"}; + } mcCfg; + // CCDB service object Service ccdb{}; - // UE - Configurable nRandomCones{"nRandomCones", 100, "number of random cones"}; - Configurable rcHardE{"rcHardE", 5.0, "hard cluster veto energy"}; - Configurable rcVetoR{"rcVetoR", 0.4, "veto radius"}; - Configurable useUEsub{"useUEsub", true, "apply UE subtraction in isolation"}; + + // PDG database, used to get the charge of the generated particles + Service pdg{}; + + // TParticlePDG::Charge() is given in units of |e|/3, a charged particle has |Charge()| >= 1 + static constexpr double ChargeTolerance = 0.001; struct HfElectronCandidate { - float pt, eta, phi, dcaxyTrk, dcazTrk, eop, energyIso, momIso; - int ntrackIso, nclusterTPC, nclusterITS; - HfElectronCandidate(float ptr, float e, float ph, float dcaxy, float dcaz, float ep, float eiso, float piso, int ntrkiso, int nclstpc, int nclsits) - : pt(ptr), eta(e), phi(ph), dcaxyTrk(dcaxy), dcazTrk(dcaz), eop(ep), energyIso(eiso), momIso(piso), ntrackIso(ntrkiso), nclusterTPC(nclstpc), nclusterITS(nclsits) {} + float pt, eta, phi, dcaxyTrk, dcazTrk, eop, energyIso, momIso, nSigmaTpc, m02Emc; + int ntrackIso, nclusterTPC, nclusterITS, sign; + int64_t indexTrk; // globalIndex of the track, used to remove the Z daughters exactly + HfElectronCandidate(float ptr, float e, float ph, float dcaxy, float dcaz, float ep, float eiso, float piso, float nsig, float m02, int ntrkiso, int nclstpc, int nclsits, int sgn, int64_t idxtrk) + : pt(ptr), eta(e), phi(ph), dcaxyTrk(dcaxy), dcazTrk(dcaz), eop(ep), energyIso(eiso), momIso(piso), nSigmaTpc(nsig), m02Emc(m02), ntrackIso(ntrkiso), nclusterTPC(nclstpc), nclusterITS(nclsits), sign(sgn), indexTrk(idxtrk) {} }; std::vector selectedElectronsIso; std::vector selectedPositronsIso; @@ -163,20 +221,27 @@ struct HfTaskElectronWeakBoson { struct HfZeeCandidate { float pt, eta, phi, mass, ptchild0, ptchild1; int charge; - HfZeeCandidate(float ptr, float e, float ph, float m, int ch, float ptzee0, float ptzee1) - : pt(ptr), eta(e), phi(ph), mass(m), ptchild0(ptzee0), ptchild1(ptzee1), charge(ch) {} + int64_t indexChild0, indexChild1; // globalIndex of the two tracks used to build the Z + HfZeeCandidate(float ptr, float e, float ph, float m, int ch, float ptzee0, float ptzee1, int64_t idxzee0, int64_t idxzee1) + : pt(ptr), eta(e), phi(ph), mass(m), ptchild0(ptzee0), ptchild1(ptzee1), charge(ch), indexChild0(idxzee0), indexChild1(idxzee1) {} }; std::vector reconstructedZ; - using CollisionsWithCent = soa::Join; + using CollisionsWithCent = soa::Join; using SelectedClusters = o2::aod::EMCALClusters; // PbPb // using TrackEle = o2::soa::Join; - using TrackEle = o2::soa::Join; + using TrackEle = o2::soa::Join; + + // MC : same collisions/tracks as above, plus the MC labels. aod::EvSels already comes with + // CollisionsWithCent (joining it again would duplicate the table), so the data and the MC + // paths see exactly the same event selection. + using CollisionsWithCentMc = soa::Join; + using TrackEleMc = o2::soa::Join; // pp // using TrackEle = o2::soa::Filtered>; - Filter eventFilter = (applySel8 ? (o2::aod::evsel::sel8 == true) : (o2::aod::evsel::sel8 == o2::aod::evsel::sel8)); // FIXME: both sides of overloaded operator are equivalent + Filter eventFilter = ifnode(as(applySel8), o2::aod::evsel::sel8 == true, true); Filter posZFilter = (nabs(o2::aod::collision::posZ) < vtxZ); Filter etafilter = (aod::track::eta < etaTrMax) && (aod::track::eta > etaTrMin); @@ -196,20 +261,15 @@ struct HfTaskElectronWeakBoson { ConfigurableAxis confaxisPtZneg{"confaxisPtZneg", {60, 20, 80}, "p_{T,neg} (GeV/c)"}; ConfigurableAxis confaxisPtZpos{"confaxisPtZpos", {60, 20, 80}, "p_{T,pos} (GeV/c)"}; ConfigurableAxis confaxisEop{"confaxisEop", {300, -0.01, 1.49}, "E/p"}; - ConfigurableAxis confaxisEopZneg{"confaxisEopZneg", {300, -0.01, 1.49}, "E/p_{neg}"}; - ConfigurableAxis confaxisEopZpos{"confaxisEopZpos", {300, -0.01, 1.49}, "E/p_{pos}"}; ConfigurableAxis confaxisIsoEnergy{"confaxisIsoEnergy", {255, 0, 2.0}, "E_{iso}"}; - ConfigurableAxis confaxisIsoEnergyZneg{"confaxisIsoEnergyZneg", {255, 0, 2.0}, "E_{iso,neg}"}; - ConfigurableAxis confaxisIsoEnergyZpos{"confaxisIsoEnergyZpos", {255, 0, 2.0}, "E_{iso,pos}"}; - ConfigurableAxis confaxisIsoMomentum{"confaxisIsoMomentum", {255, 0, 2.0}, "E_{iso}"}; - ConfigurableAxis confaxisIsoMomentumZneg{"confaxisIsoMomentumZneg", {255, 0, 1.5}, "E_{iso,neg}"}; - ConfigurableAxis confaxisIsoMomentumZpos{"confaxisIsoMomentumZpos", {255, 0, 1.5}, "E_{iso,pos}"}; + ConfigurableAxis confaxisIsoMomentum{"confaxisIsoMomentum", {255, 0, 2.0}, "p_{iso}"}; ConfigurableAxis confaxisIsoTrack{"confaxisIsoTrack", {25, -0.5, 24.5}, "Isolation Track"}; - ConfigurableAxis confaxisIsoTrackZneg{"confaxisIsoTrackZneg", {25, -0.5, 24.5}, "N_{isotrk,neg}"}; - ConfigurableAxis confaxisIsoTrackZpos{"confaxisIsoTrackZpos", {25, -0.5, 24.5}, "N_{isotrk,pos}"}; + ConfigurableAxis confaxisEtCluster{"confaxisEtCluster", {100, 0, 100}, "E_{T} (GeV)"}; + ConfigurableAxis confaxisNsigmaHadron{"confaxisNsigmaHadron", {200, -10, 10}, "n#sigma^{#pi}_{TPC}"}; ConfigurableAxis confaxisInvMassZgamma{"confaxisInvMassZgamma", {150, 0, 150}, "M_{ee} (GeV/c^{2})"}; ConfigurableAxis confaxisInvMassZ{"confaxisInvMassZ", {130, 20, 150}, "M_{ee} (GeV/c^{2})"}; ConfigurableAxis confaxisZfrag{"confaxisZfrag", {200, 0, 2.0}, "p_{T,h}/p_{T,Z}"}; + ConfigurableAxis confaxisDEtaZh{"confaxisDEtaZh", {200, -4.0, 4.0}, "#Delta#eta"}; ConfigurableAxis confaxisMultPV{"confaxisMultPV", {200, 0, 200.0}, "multiplicity"}; ConfigurableAxis confaxisMultFT0{"confaxisMultFT0", {1000, 0, 1000.0}, "multiplicity"}; @@ -262,9 +322,16 @@ struct HfTaskElectronWeakBoson { const AxisSpec axisSectorTPCneg{360, 0, 18, "TPCsector_{neg}"}; const AxisSpec axisSectorTPCpos{360, 0, 18, "TPCsector_{pos}"}; const AxisSpec axisNsigma{100, -5, 5, "N#sigma"}; - const AxisSpec axisDedx{150, 0, 150, "dEdx"}; + const AxisSpec axisNsigmaZneg{100, -5, 5, "N#sigma_{pos}"}; + const AxisSpec axisNsigmaZpos{100, -5, 5, "N#sigma_{neg}"}; + const AxisSpec axisEtCluster{confaxisEtCluster, "E_{T} (GeV)"}; + const AxisSpec axisNsigmaHadron{confaxisNsigmaHadron, "n#sigma^{#pi}_{TPC}"}; + const AxisSpec axisSignHadron{2, -2, 2, "charge sign (hadron)"}; + const AxisSpec axisNMatch{10, -0.5, 9.5, "N_{cluster} within rMatchMax"}; const AxisSpec axisE{nBinsE, 0, binEmax, "Energy"}; const AxisSpec axisM02{100, 0, 1, "M02"}; + const AxisSpec axisM02neg{100, 0, 1, "M02(neg)"}; + const AxisSpec axisM02pos{100, 0, 1, "M02(pos)"}; const AxisSpec axisdPhi{100, -0.5, 0.5, "dPhi"}; const AxisSpec axisdEta{100, -0.5, 0.5, "dEta"}; const AxisSpec axisdR{20, 0.0, 0.2, "dR"}; @@ -276,26 +343,27 @@ struct HfTaskElectronWeakBoson { const AxisSpec axisEMCtime{100, -50.0, 50, "EMC time"}; const AxisSpec axisTrigger{3, -0.5, 2.5, "Trigger status of zorro"}; const AxisSpec axisDPhiZh{64, -o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf, "#Delta#phi(Z-h)"}; + const AxisSpec axisDEtaZh{confaxisDEtaZh, "#Delta#Eta(Z-h)"}; const AxisSpec axisPtHadron{nBinsPt, 0, binPtmax, "p_{T,hadron} (GeV/c)"}; const AxisSpec axisPtZ{nBinsPt, 0, binPtmax, "p_{T,Z} (GeV/c)"}; const AxisSpec axisSign{2, -2, 2, "charge sign"}; const AxisSpec axisCentrality{centralityBins, "centrality"}; const AxisSpec axisEop{confaxisEop, "E/p"}; - const AxisSpec axisEopZneg{confaxisEopZneg, "E/p_{neg}"}; - const AxisSpec axisEopZpos{confaxisEopZpos, "E/p_{[pos}"}; + const AxisSpec axisEopZneg{confaxisEop, "E/p_{neg}"}; + const AxisSpec axisEopZpos{confaxisEop, "E/p_{[pos}"}; const AxisSpec axisPt{confaxisPt, "p_{T}"}; const AxisSpec axisPtZee{confaxisPtZee, "p_{T}"}; const AxisSpec axisPtZneg{confaxisPtZneg, "p_{T,neg} (GeV/c)"}; const AxisSpec axisPtZpos{confaxisPtZpos, "p_{T,pos} (GeV/c)"}; const AxisSpec axisIsoEnergy{confaxisIsoEnergy, "E_{iso}"}; - const AxisSpec axisIsoEnergyZneg{confaxisIsoEnergyZneg, "E_{iso}"}; - const AxisSpec axisIsoEnergyZpos{confaxisIsoEnergyZpos, "E_{iso}"}; - const AxisSpec axisIsoMomentum{confaxisIsoMomentum, "Isolation momentum(GeV/C)"}; - const AxisSpec axisIsoMomentumZneg{confaxisIsoMomentumZneg, "Isolation momentum(GeV/C)"}; - const AxisSpec axisIsoMomentumZpos{confaxisIsoMomentumZpos, "Isolation momentum(GeV/C)"}; + const AxisSpec axisIsoEnergyZneg{confaxisIsoEnergy, "E_{iso, neg} (GeV)"}; + const AxisSpec axisIsoEnergyZpos{confaxisIsoEnergy, "E_{iso, pos} (GeV)"}; + const AxisSpec axisIsoMomentum{confaxisIsoMomentum, "p_{iso} (GeV/C)"}; + const AxisSpec axisIsoMomentumZneg{confaxisIsoMomentum, "p_{iso, neg}(GeV/C)"}; + const AxisSpec axisIsoMomentumZpos{confaxisIsoMomentum, "p_{iso, pos}(GeV/C)"}; const AxisSpec axisIsoTrack{confaxisIsoTrack, "Isolation Track"}; - const AxisSpec axisIsoTrackZneg{confaxisIsoTrackZneg, "N_{isotrk,neg}"}; - const AxisSpec axisIsoTrackZpos{confaxisIsoTrackZpos, "N_{isotrk,pos}"}; + const AxisSpec axisIsoTrackZneg{confaxisIsoTrack, "N_{isotrk,neg}"}; + const AxisSpec axisIsoTrackZpos{confaxisIsoTrack, "N_{isotrk,pos}"}; const AxisSpec axisInvMassZgamma{confaxisInvMassZgamma, "M_{ee} (GeV/c^{2})"}; const AxisSpec axisInvMassZ{confaxisInvMassZ, "M_{ee} (GeV/c^{2})"}; const AxisSpec axisZfrag{confaxisZfrag, "p_{T,h}/p_{T,Z}"}; @@ -307,9 +375,10 @@ struct HfTaskElectronWeakBoson { registry.add("hEventCounterInit", "hEventCounterInit", kTH1D, {axisCounter}); registry.add("hEventCounter", "hEventCounter", kTH1D, {axisCounter}); registry.add("hCentrality", "Centrality distribution", kTH1D, {axisCentrality}); - registry.add("hCentMultCorr", "Centrality distribution", kTH2D, {{axisCentrality}, {axisMultFT0}}); - registry.add("hMultPV", "multiplicity distribution for PV", kTH1D, {axisMultPV}); - registry.add("hMultFT0", "multiplicity distribution for FT0", kTH1D, {axisMultFT0}); + registry.add("hCentMultFT0Corr", "Centrality distribution vs. FT0 Mult", kTH2D, {{axisCentrality}, {axisMultFT0}}); + registry.add("hCentMultPVCorr", "Centrality distribution vs. PV Mult", kTH2D, {{axisCentrality}, {axisMultPV}}); + registry.add("hMultPV", "multiplicity distribution for PV", kTH2D, {{axisZvtx}, {axisMultPV}}); + registry.add("hMultFT0", "multiplicity distribution for FT0", kTH2D, {{axisZvtx}, {axisMultFT0}}); registry.add("hMultFT0PV", "multiplicity distribution", kTH2D, {{axisMultFT0}, {axisMultPV}}); registry.add("hITSchi2", "ITS #chi^{2}", kTH1F, {axisChi2}); registry.add("hTPCchi2", "TPC #chi^{2}", kTH1F, {axisChi2}); @@ -320,6 +389,9 @@ struct HfTaskElectronWeakBoson { registry.add("hPt", "track pt", kTH1F, {axisPt}); registry.add("hTPCNsigma", "TPC electron Nsigma", kTH2F, {{axisPt}, {axisNsigma}}); registry.add("hEnergy", "EMC cluster energy", kTH1F, {axisE}); + registry.add("hEnergyT", "EMC cluster transverse energy E/cosh(#eta)", kTH1F, {axisE}); + registry.add("hEnergyEt", "EMC cluster energy vs. transverse energy", kTH2F, {{axisE}, {axisE}}); + registry.add("hEnergyMult", "EMC cluster energy vs Multiplicity", kTH2F, {{axisCentrality}, {axisE}}); registry.add("hEnergyNcell", "EMC cluster energy and cell", kTH2F, {{axisE}, {axisNcell}}); registry.add("hTrMatchR", "Track EMC Match in radius", kTH2F, {{axisPt}, {axisdR}}); registry.add("hTrMatch_mim", "Track EMC Match minimu minimumm", kTH2F, {{axisdPhi}, {axisdEta}}); @@ -333,14 +405,45 @@ struct HfTaskElectronWeakBoson { registry.add("hIsolationEnergy", "Isolation Energy", kTH2F, {{axisE}, {axisIsoEnergy}}); registry.add("hInvMassZee", "invariant mass for Z ULS pair", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPt, axisInvMassZgamma}); registry.add("hKfInvMassZee", "invariant mass for Z ULS pair KFp", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPt, axisInvMassZgamma}); - registry.add("hInvMassZeeQA", "QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisDCAxyneg, axisDCAxypos, axisDCAzpos, axisNclsTPCneg, axisNclsTPCpos, axisNclsITSneg, axisNclsITSpos, axisSectorTPCneg, axisSectorTPCneg, axisEopZneg, axisEopZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); - registry.add("hInvMassZeeQAbg", "QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisDCAxyneg, axisDCAxypos, axisDCAzpos, axisNclsTPCneg, axisNclsTPCpos, axisNclsITSneg, axisNclsITSpos, axisSectorTPCneg, axisSectorTPCneg, axisEopZneg, axisEopZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); - registry.add("hTHnElectrons", "electron info", HistType::kTHnSparseF, {axisPt, axisNsigma, axisM02, axisEop, axisIsoEnergy, axisIsoMomentum, axisIsoTrack, axisEta, axisDedx}); + registry.add("hInvMassZeeTrkQA", "Track QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisDCAxyneg, axisDCAxypos, axisDCAzpos, axisNclsTPCneg, axisNclsTPCpos, axisNclsITSneg, axisNclsITSpos, axisSectorTPCneg, axisSectorTPCneg, axisEopZneg, axisEopZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); + registry.add("hInvMassZeeTrkQAbg", "Track QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisDCAxyneg, axisDCAxypos, axisDCAzpos, axisNclsTPCneg, axisNclsTPCpos, axisNclsITSneg, axisNclsITSpos, axisSectorTPCneg, axisSectorTPCneg, axisEopZneg, axisEopZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); + registry.add("hInvMassZeeEmcQA", "EMCal QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisEopZneg, axisEopZpos, axisM02neg, axisM02pos, axisNsigmaZneg, axisNsigmaZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); + registry.add("hInvMassZeeEmcQAbg", "EMCal QA for invariant mass for Z", HistType::kTHnSparseF, {axisInvMassZ, axisPtZneg, axisPtZpos, axisEopZneg, axisEopZpos, axisM02neg, axisM02pos, axisNsigmaZneg, axisNsigmaZpos, axisIsoEnergyZneg, axisIsoEnergyZpos, axisIsoMomentumZneg, axisIsoMomentumZpos, axisIsoTrackZneg, axisIsoTrackZpos}); + registry.add("hTHnElectrons", "electron info", HistType::kTHnSparseF, {axisPt, axisNsigma, axisM02, axisEop, axisIsoEnergy, axisIsoMomentum, axisIsoTrack, axisEta, axisEtCluster}); registry.add("hTHnTrMatch", "Track EMC Match", HistType::kTHnSparseF, {axisPt, axisdPhi, axisdEta}); + registry.add("hNMatchPerTrack", "number of matched clusters within rMatchMax per track", kTH2F, {{axisPt}, {axisNMatch}}); // Z-hadron correlation histograms - registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisZfrag, axisPtHadron}); - registry.add("hZptSpectrum", "Z boson p_{T} spectrum", kTH2F, {{axisSign}, {axisPtZ}}); + const int nSysVar = nCutVariations(); + if (sysCfg.enableSystematics) { + const size_t nVar = static_cast(nSysVar - 1); + if (nVar == 0) { + LOGF(warn, "systematics: enableSystematics is on but every variation vector is empty, so the axis has one bin and nothing is varied."); + } + for (const auto& [values, name] : variationVectorsNamed()) { + if (!values->empty() && values->size() != nVar) { + LOGF(fatal, "systematics: %s has %zu entries but there are %zu variations. Give it either %zu entries or none at all.", name, values->size(), nVar, nVar); + } + } + for (size_t i = 0; i < nVar; ++i) { + const int iv = static_cast(i) + 1; + if (variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax) > dcaxyMax || + variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin) < nclcrossTpcMin || + variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin) < nclItsMin) { + LOGF(warn, "systematics: variation %zu loosens an associated-hadron cut. The container only holds tracks passing the nominal selection, so that variation is identical to the nominal there. Loosen the nominal configurable instead.", i + 1); + } + } + LOGF(info, "systematics: %d bins on the variation axis (bin 0 = nominal)", nSysVar); + } + const AxisSpec axisSysVar{nSysVar, -0.5, nSysVar - 0.5, "cut variation"}; + + registry.add("hZHadronDphi", "Z-hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisZfrag, axisPtHadron, axisNsigmaHadron, axisSignHadron, axisSysVar}); + registry.add("hZptSpectrum", "Z boson p_{T} spectrum", kTH3F, {{axisSign}, {axisPtZ}, {axisSysVar}}); + + if (weCfg.enableWeHadron) { + registry.add("hWeHadronDphi", "isolated electron - hadron #Delta#phi correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisDPhiZh, axisDEtaZh, axisIsoEnergy, axisIsoMomentum, axisPtHadron, axisNsigmaHadron, axisSignHadron, axisSysVar}); + registry.add("hWeSpectrum", "trigger electrons for the e-h correlation", HistType::kTHnSparseF, {axisCentrality, axisSign, axisPtZ, axisIsoEnergy, axisIsoMomentum, axisSysVar}); + } // hisotgram for EMCal trigger registry.add("hEMCalTrigger", "EMCal trigger", kTH1D, {axisTrigger}); @@ -349,6 +452,112 @@ struct HfTaskElectronWeakBoson { registry.add("hRho", "rho UE density", kTH1F, {axisE}); registry.add("hSumERC", "RC sumE", kTH1F, {axisE}); registry.add("hEnergyUE", "UE vs. centrality", kTH2F, {{axisCentrality}, {axisE}}); + + // MC : associated-hadron efficiency + const AxisSpec axisPtHadronMc{mcCfg.confaxisPtHadron, "p_{T,hadron} (GeV/c)"}; + const AxisSpec axisEtaHadronMc{mcCfg.confaxisEtaHadron, "#eta_{hadron}"}; + const AxisSpec axisMcCollCounter{4, -0.5, 3.5, "MC collision selection step"}; + + if (doprocessMcGen) { + registry.add("hMcGenCollCounter", "generated collisions (0: all, 1: |zvtx| cut, 2: reconstructed & selected)", kTH1D, {axisMcCollCounter}); + registry.add("hMcGenZvtx", "generated Z vertex", kTH1D, {axisZvtx}); + // denominator of the efficiency + registry.add("hMcGenHadron", "generated primary charged hadrons", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc}); + } + + if (doprocessMcRec) { + registry.add("hMcRecCollCounter", "reconstructed collisions in MC", kTH1D, {axisCounter}); + // numerator of the efficiency, in generated kinematics (use with hMcGenHadron) + registry.add("hMcRecHadronMatchedGenKine", "reconstructed primary charged hadrons, generated kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + // same tracks, but in reconstructed kinematics (use to correct in measured bins) + registry.add("hMcRecHadronMatchedRecKine", "reconstructed primary charged hadrons, reconstructed kinematics", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + // purity : all selected tracks and the non-primary / unlabelled sub-samples + registry.add("hMcRecHadronAll", "all selected associated tracks", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + registry.add("hMcRecHadronSecondary", "selected tracks matched to a non-primary particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + registry.add("hMcRecHadronFake", "selected tracks without MC particle", HistType::kTHnSparseF, {axisCentrality, axisPtHadronMc, axisEtaHadronMc, axisSysVar}); + // momentum resolution + registry.add("hMcPtGenRec", "generated vs. reconstructed p_{T}", kTH2F, {{axisPtHadronMc}, {axisPtHadronMc}}); + } + } + + // Kinematic + quality selection shared by the data and the MC track loops, + // so that the efficiency is evaluated with exactly the same cuts. + template + bool isSelectedTrackKine(TTrack const& track) + { + if (!track.isGlobalTrackWoPtEta()) { + return false; + } + if (std::abs(track.eta()) > etaTrMax) { + return false; + } + if (applyTrackSys) { + if (track.tpcNClsCrossedRows() < nclcrossTpcMin) { + return false; + } + if (std::abs(track.dcaXY()) > dcaxyMax) { + return false; + } + if (track.itsChi2NCl() > chi2ItsMax) { + return false; + } + if (track.tpcChi2NCl() > chi2TpcMax) { + return false; + } + if (track.tpcNClsFound() < nclTpcMin) { + return false; + } + if (track.itsNCls() < nclItsMin) { + return false; + } + } + return true; + } + + // Centrality selection, applied on the centrality estimator itself (as in processData, + // where the cut is done before the variable is possibly overwritten by the multiplicity) + template + bool isSelectedCentrality(TCollision const& collision) + { + if (!enableCentralityAnalysis) { + return true; + } + float const centrality = o2::hf_centrality::getCentralityColl(collision, centralityEstimator); + return (centrality >= centralityMin && centrality <= centralityMax); + } + + // Value filled on the centrality axis, following the same priority as in processData + template + float getCentralityValue(TCollision const& collision) + { + float centrality = 1.0; + if (enableCentralityAnalysis) { + centrality = o2::hf_centrality::getCentralityColl(collision, centralityEstimator); + } + if (enableMultiplicityFT0MAnalysis) { + centrality = collision.multFT0M(); + } + if (enableMultiplicityPVAnalysis) { + centrality = collision.multNTracksPV(); + } + return centrality; + } + + // Generated particle counted as an associated hadron + template + bool isChargedPrimary(TParticle const& particle) + { + if (mcCfg.requirePhysicalPrimary && !particle.isPhysicalPrimary()) { + return false; + } + const TParticlePDG* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (!pdgParticle) { + return false; + } + if (std::abs(pdgParticle->Charge()) < ChargeTolerance) { + return false; + } + return true; } double getIsolatedCluster(const o2::aod::EMCALCluster& cluster, @@ -356,7 +565,7 @@ struct HfTaskElectronWeakBoson { float energyUE) { double energySum = 0.0; - double energySum_excl = 0.0; + double energySumExcl = 0.0; double isoEnergy = 10.0; double const etaAssCluster = cluster.eta(); double const phiAssCluster = cluster.phi(); @@ -377,9 +586,9 @@ struct HfTaskElectronWeakBoson { energySum += associateCluster.energy(); } } - energySum_excl = energySum - cluster.energy(); + energySumExcl = energySum - cluster.energy(); if (energySum > 0) { - isoEnergy = (energySum_excl - energyUE) / cluster.energy(); + isoEnergy = (energySumExcl - energyUE) / cluster.energy(); } // LOG(info) <<"clustE = " << cluster.energy() << " ; energySum = " << energySum << " ; nclust in Cone = " << nclustSum - 1 << " ; UE = " << energyUE << " ; isoEnergy = " << isoEnergy; @@ -425,9 +634,9 @@ struct HfTaskElectronWeakBoson { const float randomConeArea = o2::constants::math::PI * randomConeR * randomConeR; std::vector sumErc; - sumErc.reserve(nRandomCones); + sumErc.reserve(ueCfg.nRandomCones); - for (int i = 0; i < nRandomCones; i++) { + for (int i = 0; i < ueCfg.nRandomCones; i++) { float etarc = rnd->Uniform(-etaEmcMax, etaEmcMax); // in EMCal acceptance float phirc = rnd->Uniform(phiEmcMin, phiEmcMax); // in EMCal acceptance @@ -435,7 +644,7 @@ struct HfTaskElectronWeakBoson { float energySumRC = 0; for (const auto& c : clusters) { - if (c.energy() > rcHardE) { + if (c.energy() > ueCfg.rcHardE) { continue; } double dEtarc = etarc - c.eta(); @@ -465,15 +674,72 @@ struct HfTaskElectronWeakBoson { return median / randomConeArea; } + // Outcome of the track - EMCal cluster matching for one track + struct HfEmcMatchResult { + int64_t posBestMatch = -1; + double rMin = 999.9; + double dPhiMin = 999.9; + double dEtaMin = 999.9; + int nMatchInRadius = 0; + bool isMatched = false; + }; + + template + HfEmcMatchResult findBestEmcMatch(TMatchedTracks const& matchesOfTrack, float centrality) + { + HfEmcMatchResult result; + int64_t posMatch = -1; + + for (const auto& match : matchesOfTrack) { + ++posMatch; + const auto& cluster = match.template emcalcluster_as(); + if (cluster.time() < timeEmcMin || cluster.time() > timeEmcMax) { + continue; + } + + double const dEta = match.template track_as().trackEtaEmcal() - cluster.eta(); + double dPhi = match.template track_as().trackPhiEmcal() - cluster.phi(); + dPhi = RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI); + double const r = RecoDecay::sqrtSumOfSquares(dPhi, dEta); + + registry.fill(HIST("hMatchPhi"), cluster.phi(), match.template track_as().trackPhiEmcal()); + registry.fill(HIST("hMatchEta"), cluster.eta(), match.template track_as().trackEtaEmcal()); + registry.fill(HIST("hTHnTrMatch"), match.template track_as().pt(), dPhi, dEta); + registry.fill(HIST("hEMCtime"), cluster.time()); + registry.fill(HIST("hEnergy"), cluster.energy()); + double const energyT = cluster.energy() / std::cosh(cluster.eta()); + registry.fill(HIST("hEnergyT"), energyT); + registry.fill(HIST("hEnergyEt"), cluster.energy(), energyT); + registry.fill(HIST("hEnergyMult"), centrality, cluster.energy()); + + if (std::abs(dPhi) <= rMatchMax && std::abs(dEta) <= rMatchMax) { + result.nMatchInRadius++; + } + + // LOG(info) << "r match = " << r; + if (r < result.rMin) { + result.rMin = r; + result.dPhiMin = dPhi; + result.dEtaMin = dEta; + result.posBestMatch = posMatch; + // LOG(info) << "match cl info : " << r << " ; " << cluster.energy(); + } + } + + result.isMatched = (result.posBestMatch >= 0) && (std::abs(result.dPhiMin) <= rMatchMax) && (std::abs(result.dEtaMin) <= rMatchMax); + return result; + } + void recoMassZee(const KFParticle& kfpIsoEle, int charge, + int64_t indexIsoEle, float centrality, TrackEle const& tracks) { // LOG(info) << "Invarimass cal by KF particle "; for (const auto& track : tracks) { - if (std::abs(track.pt() - kfpIsoEle.GetPt()) < ptMatch) { + if (track.globalIndex() == indexIsoEle) { continue; } if (track.pt() < ptZeeMin) { @@ -504,10 +770,10 @@ struct HfTaskElectronWeakBoson { registry.fill(HIST("hInvMassZee"), centrality, track.sign() * charge, kfpIsoEle.GetPt(), invMassEE); // reco by KFparticle - const KFParticle* electronPairs[2] = {&kfpIsoEle, &kfpAssEle}; + std::array electronPairs = {&kfpIsoEle, &kfpAssEle}; KFParticle zeeKF; zeeKF.SetConstructMethod(kfConstructMethod); - zeeKF.Construct(electronPairs, 2); + zeeKF.Construct(electronPairs.data(), 2); // LOG(info) << "Invarimass cal by KF particle Chi2/NDF = " << zeeKF.GetChi2()/zeeKF.GetNDF(); float const chiSqNdf = zeeKF.GetChi2() / zeeKF.GetNDF(); if (zeeKF.GetNDF() < 1) { @@ -531,16 +797,278 @@ struct HfTaskElectronWeakBoson { massZee, track.sign() * charge, kfpIsoEle.GetPt(), - kfpAssEle.GetPt()); + kfpAssEle.GetPt(), + indexIsoEle, + track.globalIndex()); + } + } + + // ------------------------------------------------------------------------- + // Cut variations (systematics). Bin 0 is always the nominal configurables; bin iv>0 reads + // entry iv-1 of each variation vector. Member FUNCTIONS, so none of this costs a task member. + // ------------------------------------------------------------------------- + int nCutVariations() + { + if (!sysCfg.enableSystematics) { + return 1; + } + size_t n = 0; + for (const auto& [values, name] : variationVectorsNamed()) { + n = std::max(n, values->size()); + } + return 1 + static_cast(n); + } + // The eleven variation vectors, paired with their names for the messages in init(). + std::array*, const char*>, 11> variationVectorsNamed() + { + return {{{&sysCfg.varDcaxyMax.value, "varDcaxyMax"}, + {&sysCfg.varNclCrossTpcMin.value, "varNclCrossTpcMin"}, + {&sysCfg.varNclItsMin.value, "varNclItsMin"}, + {&sysCfg.varEopMin.value, "varEopMin"}, + {&sysCfg.varEopMax.value, "varEopMax"}, + {&sysCfg.varNsigTpcMin.value, "varNsigTpcMin"}, + {&sysCfg.varNsigTpcMax.value, "varNsigTpcMax"}, + {&sysCfg.varM02Min.value, "varM02Min"}, + {&sysCfg.varM02Max.value, "varM02Max"}, + {&sysCfg.varMassZMin.value, "varMassZMin"}, + {&sysCfg.varMassZMax.value, "varMassZMax"}}}; + } + // One variation's value of a cut, falling back to the nominal for bin 0 or a short vector. + static float variedValue(const std::vector& values, int iv, float nominal) + { + if (iv <= 0 || static_cast(iv - 1) >= values.size()) { + return nominal; + } + return values[iv - 1]; + } + + // Associated hadron. dcaxyTrk, nclusterTPC (= crossed rows, see where the container is + // filled) and nclusterITS are the three quantities the candidate carries. + bool passHadronVariation(const HfElectronCandidate& hadron, int iv) + { + if (iv == 0) { + return true; // the container already holds the nominal selection + } + if (std::abs(hadron.dcaxyTrk) > variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax)) { + return false; + } + if (hadron.nclusterTPC < variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin)) { + return false; + } + if (hadron.nclusterITS < variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin)) { + return false; + } + return true; + } + + // Same three cuts on a track, for the MC efficiency: the numerator has to be recomputed with + // the very selection the correlation used, or the correction does not match the data. + template + bool passHadronVariationTrack(TTrack const& track, int iv) + { + if (iv == 0) { + return true; + } + if (std::abs(track.dcaXY()) > variedValue(sysCfg.varDcaxyMax.value, iv, dcaxyMax)) { + return false; + } + if (track.tpcNClsCrossedRows() < variedValue(sysCfg.varNclCrossTpcMin.value, iv, nclcrossTpcMin)) { + return false; + } + if (track.itsNCls() < variedValue(sysCfg.varNclItsMin.value, iv, nclItsMin)) { + return false; + } + return true; + } + + // The Z mass window for one variation. Unlike the associated-hadron cuts this one is + // two-sided: reconstructedZ holds every pair that passed the KF chi2 cut, at any mass, so a + // wider window genuinely adds candidates. iv == 0 uses the nominal configurables. + bool passZVariation(const HfZeeCandidate& zBoson, int iv) + { + if (zBoson.mass < variedValue(sysCfg.varMassZMin.value, iv, massZMin) || + zBoson.mass > variedValue(sysCfg.varMassZMax.value, iv, massZMax)) { + return false; + } + return true; + } + + // W trigger electron. selectedElectronsIso carries no identification cut, so these three can + // be varied in both directions. + bool passElectronVariation(const HfElectronCandidate& ele, int iv) + { + if (ele.eop < variedValue(sysCfg.varEopMin.value, iv, eopMin) || + ele.eop > variedValue(sysCfg.varEopMax.value, iv, eopMax)) { + return false; + } + if (ele.nSigmaTpc < variedValue(sysCfg.varNsigTpcMin.value, iv, nsigTpcMin) || + ele.nSigmaTpc > variedValue(sysCfg.varNsigTpcMax.value, iv, nsigTpcMax)) { + return false; + } + if (ele.m02Emc < variedValue(sysCfg.varM02Min.value, iv, m02Min) || + ele.m02Emc > variedValue(sysCfg.varM02Max.value, iv, m02Max)) { + return false; + } + return true; + } + + // ------------------------------------------------------------------------- + // Z->ee QA. + // ------------------------------------------------------------------------- + template + void fillZeeQaPair(const TCandidate& trackNeg, const TCandidate& trackPos) + { + auto child1 = RecoDecayPtEtaPhi::pVector(trackNeg.pt, trackNeg.eta, trackNeg.phi); + auto child2 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); + double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); + if (invMass <= zeeQaCfg.massZMinQA) { + return; + } + float const sectorneg = trackNeg.phi / o2::constants::math::SectorSpanRad; + float const sectorpos = trackPos.phi / o2::constants::math::SectorSpanRad; + + if (zeeQaCfg.enableZeeTrkRecoQA) { + if constexpr (IsSignal) { + registry.fill(HIST("hInvMassZeeTrkQA"), invMass, trackNeg.pt, trackPos.pt, trackNeg.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackNeg.nclusterTPC, trackPos.nclusterTPC, trackNeg.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackNeg.eop, trackPos.eop, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } else { + registry.fill(HIST("hInvMassZeeTrkQAbg"), invMass, trackNeg.pt, trackPos.pt, trackNeg.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackNeg.nclusterTPC, trackPos.nclusterTPC, trackNeg.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackNeg.eop, trackPos.eop, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } + } + if (zeeQaCfg.enableZeeEmcRecoQA) { + if constexpr (IsSignal) { + registry.fill(HIST("hInvMassZeeEmcQA"), invMass, trackNeg.pt, trackPos.pt, trackNeg.eop, trackPos.eop, trackNeg.m02Emc, trackPos.m02Emc, trackNeg.nSigmaTpc, trackPos.nSigmaTpc, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } else { + registry.fill(HIST("hInvMassZeeEmcQAbg"), invMass, trackNeg.pt, trackPos.pt, trackNeg.eop, trackPos.eop, trackNeg.m02Emc, trackPos.m02Emc, trackNeg.nSigmaTpc, trackPos.nSigmaTpc, trackNeg.energyIso, trackPos.energyIso, trackNeg.momIso, trackPos.momIso, trackNeg.ntrackIso, trackPos.ntrackIso); + } + } + } + + void fillZeeQa() + { + if (!zeeQaCfg.enableZeeTrkRecoQA && !zeeQaCfg.enableZeeEmcRecoQA) { + return; + } + + for (const auto& trackEle : selectedElectronsIso) { + for (const auto& trackPos : selectedPositronsIso) { + fillZeeQaPair(trackEle, trackPos); + } + } + // each unordered like-sign pair once + for (size_t i = 0; i < selectedElectronsIso.size(); ++i) { + for (size_t j = i + 1; j < selectedElectronsIso.size(); ++j) { + fillZeeQaPair(selectedElectronsIso[i], selectedElectronsIso[j]); + } + } + for (size_t i = 0; i < selectedPositronsIso.size(); ++i) { + for (size_t j = i + 1; j < selectedPositronsIso.size(); ++j) { + fillZeeQaPair(selectedPositronsIso[i], selectedPositronsIso[j]); + } + } + } + + // ------------------------------------------------------------------------- + // Z - h correlation. + // ------------------------------------------------------------------------- + void fillZHadron(float centrality) + { + const int nVar = nCutVariations(); + for (const auto& zBoson : reconstructedZ) { + // The Z counter is filled per variation, so that a varied mass window moves the + // denominator of the per-trigger yield together with its numerator. + bool anyVariation = false; + for (int iv = 0; iv < nVar; ++iv) { + if (!passZVariation(zBoson, iv)) { + continue; + } + anyVariation = true; + registry.fill(HIST("hZptSpectrum"), zBoson.charge, zBoson.pt, iv); + } + if (!anyVariation) { // outside the widest window of any variation + continue; + } + + for (const auto& trackAss : selectedElectronsAss) { + // remove the two tracks used to build this Z, matched exactly by globalIndex + if (trackAss.indexTrk == zBoson.indexChild0 || trackAss.indexTrk == zBoson.indexChild1) { + continue; + } + double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - zBoson.phi, -o2::constants::math::PIHalf); + double const deltaEta = zBoson.eta - trackAss.eta; + double const ptRatio = trackAss.pt / zBoson.pt; + // one entry per cut variation the pair passes; bin 0 is the nominal selection + for (int iv = 0; iv < nVar; ++iv) { + if (!passZVariation(zBoson, iv) || !passHadronVariation(trackAss, iv)) { + continue; + } + registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, deltaEta, + ptRatio, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign, iv); + } + } } } - // void process(soa::Filtered::iterator const& collision, - void process(soa::Filtered::iterator const& collision, - aod::BCsWithTimestamps const&, - SelectedClusters const& emcClusters, - TrackEle const& tracks, - o2::aod::EMCALMatchedTracks const& matchedtracks) + // ------------------------------------------------------------------------- + // W->e - h correlation. + // ------------------------------------------------------------------------- + bool isZeeDaughter(int64_t indexTrk) + { + for (const auto& zBoson : reconstructedZ) { + if (zBoson.charge >= 0) { // like-sign pairs are the combinatorial background, not a Z + continue; + } + if (zBoson.mass < massZMin || zBoson.mass > massZMax) { + continue; + } + if (indexTrk == zBoson.indexChild0 || indexTrk == zBoson.indexChild1) { + return true; + } + } + return false; + } + + void fillWeHadron(const std::vector& electrons, float centrality) + { + for (const auto& ele : electrons) { + if (ele.pt < weCfg.ptWeMin) { + continue; + } + if (ele.eop <= 0.f) { + continue; + } + if (weCfg.vetoZeeElectron && isZeeDaughter(ele.indexTrk)) { + continue; + } + + const int nVar = nCutVariations(); + for (int iv = 0; iv < nVar; ++iv) { + if (!passElectronVariation(ele, iv)) { + continue; + } + registry.fill(HIST("hWeSpectrum"), centrality, ele.sign, ele.pt, ele.energyIso, ele.momIso, iv); + + for (const auto& trackAss : selectedElectronsAss) { + // the trigger electron itself, matched exactly by globalIndex + if (trackAss.indexTrk == ele.indexTrk) { + continue; + } + if (!passHadronVariation(trackAss, iv)) { + continue; + } + double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - ele.phi, -o2::constants::math::PIHalf); + double const deltaEta = ele.eta - trackAss.eta; + registry.fill(HIST("hWeHadronDphi"), centrality, ele.sign, ele.pt, deltaPhi, deltaEta, + ele.energyIso, ele.momIso, trackAss.pt, trackAss.nSigmaTpc, trackAss.sign, iv); + } + } + } + } + + void processData(soa::Filtered::iterator const& collision, + aod::BCsWithTimestamps const&, + SelectedClusters const& emcClusters, + TrackEle const& tracks, + o2::aod::EMCALMatchedTracks const& matchedtracks) { registry.fill(HIST("hEventCounterInit"), 0.5); @@ -608,18 +1136,21 @@ struct HfTaskElectronWeakBoson { if (centrality < centralityMin || centrality > centralityMax) { return; } - registry.fill(HIST("hCentMultCorr"), centrality, collision.multFT0M()); + registry.fill(HIST("hCentMultFT0Corr"), centrality, collision.multFT0M()); + registry.fill(HIST("hCentMultPVCorr"), centrality, collision.multNTracksPV()); } if (enableMultiplicityFT0MAnalysis || enableMultiplicityPVAnalysis) { - if (enableMultiplicityFT0MAnalysis) + if (enableMultiplicityFT0MAnalysis) { centrality = collision.multFT0M(); - if (enableMultiplicityPVAnalysis) + } + if (enableMultiplicityPVAnalysis) { centrality = collision.multNTracksPV(); + } // LOG(info) << "raw mult PV = " << collision.multNTracksPV(); // LOG(info) << "raw mult FT0M = " << collision.multFT0M(); - registry.fill(HIST("hMultPV"), collision.multNTracksPV()); - registry.fill(HIST("hMultFT0"), collision.multFT0M()); + registry.fill(HIST("hMultPV"), collision.posZ(), collision.multNTracksPV()); + registry.fill(HIST("hMultFT0"), collision.posZ(), collision.multFT0M()); registry.fill(HIST("hMultFT0PV"), collision.multFT0M(), collision.multNTracksPV()); } @@ -629,38 +1160,23 @@ struct HfTaskElectronWeakBoson { float rho = 0.f; float energyUE = 0.f; - if (useUEsub) { + if (ueCfg.useUEsub) { rho = estimateRhoRC(emcClusters); - energyUE = rho * static_cast(o2::constants::math::PI * rIsolation * rIsolation); + energyUE = rho * (o2::constants::math::PI * rIsolation * rIsolation); registry.fill(HIST("hEnergyUE"), centrality, energyUE); // LOG(info) << "UE = " << energyUE; } + const float ptIsoStoreMin = weCfg.enableWeHadron + ? std::min(static_cast(ptZeeMin), static_cast(weCfg.ptWeMin)) + : static_cast(ptZeeMin); + // track loop for (const auto& track : tracks) { - if (std::abs(track.eta()) > etaTrMax) { - continue; - } - if (track.tpcNClsCrossedRows() < nclcrossTpcMin) { - continue; - } - if (std::abs(track.dcaXY()) > dcaxyMax) { + if (!isSelectedTrackKine(track)) { continue; } - if (track.itsChi2NCl() > chi2ItsMax) { - continue; - } - if (track.tpcChi2NCl() > chi2TpcMax) { - continue; - } - if (track.tpcNClsFound() < nclTpcMin) { - continue; - } - if (track.itsNCls() < nclItsMin) { - continue; - } - registry.fill(HIST("hEta"), track.eta()); registry.fill(HIST("hITSchi2"), track.itsChi2NCl()); registry.fill(HIST("hTPCchi2"), track.tpcChi2NCl()); @@ -671,6 +1187,7 @@ struct HfTaskElectronWeakBoson { registry.fill(HIST("hTPCNsigma"), track.p(), track.tpcNSigmaEl()); float eop = -0.01; + float m02 = -0.01; float isoEnergy = 99.0; // track isolation auto [trackCount, isoMomentum] = getIsolatedTrack(track.eta(), track.phi(), track.p(), tracks); @@ -686,9 +1203,13 @@ struct HfTaskElectronWeakBoson { eop, isoEnergy, isoMomentum, + track.tpcNSigmaPi(), + m02, trackCount, track.tpcNClsCrossedRows(), - track.itsNCls()); + track.itsNCls(), + track.sign(), + track.globalIndex()); } if (track.pt() < ptMin) { @@ -714,100 +1235,75 @@ struct HfTaskElectronWeakBoson { bool isIsolatedTr = false; if ((tracksofcluster.size() != 0) && isEMCacceptance) { - int nMatch = 0; - for (const auto& match : tracksofcluster) { - if (match.emcalcluster_as().time() < timeEmcMin || match.emcalcluster_as().time() > timeEmcMax) { - continue; - } - float const m02Emc = match.emcalcluster_as().m02(); - float const energyEmc = match.emcalcluster_as().energy(); - double const phiEmc = match.emcalcluster_as().phi(); - double const etaEmc = match.emcalcluster_as().eta(); - double const timeEmc = match.emcalcluster_as().time(); - // LOG(info) << "tr phi0 = " << match.track_as().phi(); - // LOG(info) << "tr phi1 = " << track.phi(); - // LOG(info) << "emc phi = " << phiEmc; - - if (nMatch == 0) { - double const dEta = match.track_as().trackEtaEmcal() - etaEmc; - double dPhi = match.track_as().trackPhiEmcal() - phiEmc; - dPhi = RecoDecay::constrainAngle(dPhi, -o2::constants::math::PI); - - registry.fill(HIST("hMatchPhi"), phiEmc, match.track_as().trackPhiEmcal()); - registry.fill(HIST("hMatchEta"), etaEmc, match.track_as().trackEtaEmcal()); - - double const r = RecoDecay::sqrtSumOfSquares(dPhi, dEta); - // LOG(info) << "r match = " << r; - if (r < rMin) { - rMin = r; - dPhiMin = dPhi; - dEtaMin = dEta; + // Pass 1 : QA over every matched cluster, and find the closest one + auto const emcMatch = findBestEmcMatch(tracksofcluster, centrality); + rMin = emcMatch.rMin; + dPhiMin = emcMatch.dPhiMin; + dEtaMin = emcMatch.dEtaMin; + registry.fill(HIST("hNMatchPerTrack"), track.pt(), emcMatch.nMatchInRadius); + + // Pass 2 : only the closest cluster is used for E/p, isolation and the Z reconstruction + if (emcMatch.isMatched) { + auto const match = tracksofcluster.iteratorAt(emcMatch.posBestMatch); + const auto& cluster = match.emcalcluster_as(); + float const m02Emc = cluster.m02(); + float const energyEmc = cluster.energy(); + // transverse energy of the cluster; cosh(eta) >= 1, so energyTEmc <= energyEmc + float const energyTEmc = energyEmc / std::cosh(cluster.eta()); + + registry.fill(HIST("hTrMatchR"), match.track_as().pt(), rMin); + registry.fill(HIST("hEnergyNcell"), energyEmc, cluster.nCells()); + + eop = energyEmc / match.track_as().p(); + m02 = m02Emc; + // LOG(info) << "check match cl info : " << cluster.energy(); + isoEnergy = getIsolatedCluster(cluster, emcClusters, energyUE); + + if (match.track_as().pt() > ptTHnThresh && isTHnElectron) { + registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), energyTEmc); + } + // LOG(info) << "E/p" << eop; + registry.fill(HIST("hEopNsigTPC"), match.track_as().tpcNSigmaEl(), eop); + + if (m02Emc >= m02Min && m02Emc <= m02Max && + match.track_as().tpcNSigmaEl() > nsigTpcMin && match.track_as().tpcNSigmaEl() < nsigTpcMax) { + registry.fill(HIST("hEop"), match.track_as().pt(), eop); + if (eop > eopMin && eop < eopMax && isoEnergy < energyIsolationMax) { + isIsolated = true; } - registry.fill(HIST("hTHnTrMatch"), match.track_as().pt(), dPhi, dEta); - registry.fill(HIST("hEMCtime"), timeEmc); - registry.fill(HIST("hEnergy"), energyEmc); - - if (std::abs(dPhi) > rMatchMax || std::abs(dEta) > rMatchMax) { - continue; + if (eop > eopMin && eop < eopMax && trackCount < trackIsolationMax && isoMomentum < momentumIsolationMax) { + isIsolatedTr = true; } - registry.fill(HIST("hTrMatchR"), match.track_as().pt(), r); - registry.fill(HIST("hEnergyNcell"), energyEmc, match.emcalcluster_as().nCells()); - - const auto& cluster = match.emcalcluster_as(); - - eop = energyEmc / match.track_as().p(); - // LOG(info) << "eop = " << eop; - - isoEnergy = getIsolatedCluster(cluster, emcClusters, energyUE); - - if (match.track_as().pt() > ptTHnThresh && isTHnElectron) { - registry.fill(HIST("hTHnElectrons"), match.track_as().pt(), match.track_as().tpcNSigmaEl(), m02Emc, eop, isoEnergy, isoMomentum, trackCount, track.eta(), track.tpcSignal()); + if (isIsolated && isIsolatedTr) { + registry.fill(HIST("hEopIsolation"), match.track_as().pt(), eop); + + if (match.track_as().pt() > ptZeeMin) { + int pdgIso = kElectron; + if (match.track_as().sign() > 0) { + pdgIso = kPositron; + } + KFPTrack const kfpTrackIsoEle = createKFPTrackFromTrack(match.track_as()); + KFParticle const kfpIsoEle(kfpTrackIsoEle, pdgIso); + recoMassZee(kfpIsoEle, match.track_as().sign(), match.track_as().globalIndex(), centrality, tracks); + + } // end of pt cut for e from Z + } // end if isolation cut + if (isIsolatedTr) { + registry.fill(HIST("hEopIsolationTr"), match.track_as().pt(), eop); } - // LOG(info) << "E/p" << eop; - registry.fill(HIST("hEopNsigTPC"), match.track_as().tpcNSigmaEl(), eop); - if (match.emcalcluster_as().m02() < m02Min || match.emcalcluster_as().m02() > m02Max) { - continue; - } - - if (match.track_as().tpcNSigmaEl() > nsigTpcMin && match.track_as().tpcNSigmaEl() < nsigTpcMax) { - registry.fill(HIST("hEop"), match.track_as().pt(), eop); - if (eop > eopMin && eop < eopMax && isoEnergy < energyIsolationMax) { - isIsolated = true; - } - if (eop > eopMin && eop < eopMax && trackCount < trackIsolationMax && isoMomentum < momentumIsolationMax) { - isIsolatedTr = true; - } - - if (isIsolated && isIsolatedTr) { - registry.fill(HIST("hEopIsolation"), match.track_as().pt(), eop); - - if (match.track_as().pt() > ptZeeMin) { - int pdgIso = kElectron; - if (match.track_as().sign() > 0) { - pdgIso = kPositron; - } - KFPTrack const kfpTrackIsoEle = createKFPTrackFromTrack(match.track_as()); - KFParticle const kfpIsoEle(kfpTrackIsoEle, pdgIso); - recoMassZee(kfpIsoEle, match.track_as().sign(), centrality, tracks); - - } // end of pt cut for e from Z - } // end if isolation cut - if (isIsolatedTr) { - registry.fill(HIST("hEopIsolationTr"), match.track_as().pt(), eop); - } - } // end of PID cut - } // end of nmatch == 0 - nMatch++; - } // end of cluster match + } // end of M02 + PID cut + } // end of isMatched } // end of cluster if (rMin < rMatchMax) { // LOG(info) << "R mim = " << rMin; registry.fill(HIST("hTrMatch_mim"), dPhiMin, dEtaMin); } - if (enableZeeRecoQA && track.pt() > ptZeeMin) { + // The e-h correlation reads the same two containers, so they must also be filled when + // only that analysis is enabled, and down to the looser of the two pT thresholds. + if ((zeeQaCfg.enableZeeTrkRecoQA || zeeQaCfg.enableZeeEmcRecoQA || weCfg.enableWeHadron) && track.pt() > ptIsoStoreMin) { if (track.sign() < 0) { selectedElectronsIso.emplace_back( track.pt(), @@ -818,9 +1314,13 @@ struct HfTaskElectronWeakBoson { eop, isoEnergy, isoMomentum, + track.tpcNSigmaEl(), + m02, trackCount, track.tpcNClsFound(), - track.itsNCls()); + track.itsNCls(), + track.sign(), + track.globalIndex()); } else { selectedPositronsIso.emplace_back( track.pt(), @@ -831,84 +1331,160 @@ struct HfTaskElectronWeakBoson { eop, isoEnergy, isoMomentum, + track.tpcNSigmaEl(), + m02, trackCount, track.tpcNClsFound(), - track.itsNCls()); + track.itsNCls(), + track.sign(), + track.globalIndex()); } } } // end of track loop - // Z-hadron - if (!reconstructedZ.empty()) { - for (const auto& zBoson : reconstructedZ) { - // Z boson selection - if (zBoson.mass < massZMin || zBoson.mass > massZMax) { - continue; - } - registry.fill(HIST("hZptSpectrum"), zBoson.charge, zBoson.pt); - for (const auto& trackAss : selectedElectronsAss) { - if (std::abs(trackAss.pt - zBoson.ptchild0) < ptMatch) { - continue; - } - if (std::abs(trackAss.pt - zBoson.ptchild1) < ptMatch) { - continue; - } - // calculate Z-h correlation - double const deltaPhi = RecoDecay::constrainAngle(trackAss.phi - zBoson.phi, -o2::constants::math::PIHalf); - double const ptRatio = trackAss.pt / zBoson.pt; - registry.fill(HIST("hZHadronDphi"), centrality, zBoson.charge, zBoson.pt, deltaPhi, ptRatio, trackAss.pt); + // Z - hadron correlation + fillZHadron(centrality); + + // W->e - h correlation. Electrons and positrons are kept in two containers, so the same + // routine runs twice; the electron charge goes on the sign axis (W- / W+). + if (weCfg.enableWeHadron) { + fillWeHadron(selectedElectronsIso, centrality); + fillWeHadron(selectedPositronsIso, centrality); + } // end of W->e - hadron correlation + // Z->ee QA + fillZeeQa(); + } // processData + PROCESS_SWITCH(HfTaskElectronWeakBoson, processData, "process data (Z->ee and Z-h correlation)", true); + + // ------------------------------------------------------------------------- + // MC : denominator of the associated-hadron efficiency. + // Looping over the generated collisions (with the reconstructed ones collected by + // soa::SmallGroups) also gives access to the event-loss / split-collision effects + // through hMcGenCollCounter. + // ------------------------------------------------------------------------- + void processMcGen(aod::McCollision const& mcCollision, + soa::SmallGroups const& collisions, + aod::McParticles const& mcParticles) + { + registry.fill(HIST("hMcGenCollCounter"), 0.5); + + if (std::abs(mcCollision.posZ()) > vtxZ) { + return; + } + registry.fill(HIST("hMcGenCollCounter"), 1.5); + registry.fill(HIST("hMcGenZvtx"), mcCollision.posZ()); + + // look for a reconstructed collision passing the same event selection as in processData + bool isReconstructed = false; + float centrality = 1.0; + for (const auto& collision : collisions) { + if (applySel8 && !collision.sel8()) { + continue; + } + if (std::abs(collision.posZ()) > vtxZ) { + continue; + } + if (!isSelectedCentrality(collision)) { + continue; + } + isReconstructed = true; + centrality = getCentralityValue(collision); + break; + } + if (!isReconstructed) { + return; + } + registry.fill(HIST("hMcGenCollCounter"), 2.5); + + for (const auto& particle : mcParticles) { + if (!isChargedPrimary(particle)) { + continue; + } + // same acceptance as isSelectedTrackKine() applied to the reconstructed tracks + if (std::abs(particle.eta()) > etaTrMax) { + continue; + } + if (particle.pt() < ptAssMin) { + continue; + } + registry.fill(HIST("hMcGenHadron"), centrality, particle.pt(), particle.eta()); + } + } // processMcGen + PROCESS_SWITCH(HfTaskElectronWeakBoson, processMcGen, "process MC generated level for the associated-hadron efficiency", false); + + // ------------------------------------------------------------------------- + // MC : numerator of the associated-hadron efficiency (and purity). + // The track selection is identical to the one applied to selectedElectronsAss + // in processData. + // ------------------------------------------------------------------------- + void processMcRec(soa::Filtered::iterator const& collision, + TrackEleMc const& tracks, + aod::McParticles const&, + aod::McCollisions const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!isSelectedCentrality(collision)) { + return; + } + float const centrality = getCentralityValue(collision); + registry.fill(HIST("hMcRecCollCounter"), 0.5); + + for (const auto& track : tracks) { + if (!isSelectedTrackKine(track)) { + continue; + } + if (track.pt() < ptAssMin) { + continue; + } + + // The efficiency has to be recomputed with the same cut variation the correlation used, + // so the numerator histograms carry the variation axis too. hMcGenHadron does not: no + // track cut enters the denominator, so it is common to every variation. + const int nVar = nCutVariations(); + + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronAll"), centrality, track.pt(), track.eta(), iv); } } - } // end of Z-hadron correlation - // Z->ee QA - if (enableZeeRecoQA) { - if (!selectedElectronsIso.empty() && !selectedPositronsIso.empty()) { - // signal - for (const auto& trackEle : selectedElectronsIso) { - auto child1 = RecoDecayPtEtaPhi::pVector(trackEle.pt, trackEle.eta, trackEle.phi); - float const sectorneg = trackEle.phi / o2::constants::math::SectorSpanRad; - for (const auto& trackPos : selectedPositronsIso) { - auto child2 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - float const sectorpos = trackPos.phi / o2::constants::math::SectorSpanRad; - if (invMass > massZMinQA) { - registry.fill(HIST("hInvMassZeeQA"), invMass, trackEle.pt, trackPos.pt, trackEle.dcaxyTrk, trackPos.dcaxyTrk, trackPos.dcazTrk, trackEle.nclusterTPC, trackPos.nclusterTPC, trackEle.nclusterITS, trackPos.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackPos.eop, trackEle.energyIso, trackPos.energyIso, trackEle.momIso, trackPos.momIso, trackEle.ntrackIso, trackPos.ntrackIso); - } + + if (!track.has_mcParticle()) { + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronFake"), centrality, track.pt(), track.eta(), iv); } } - // bg e-e- - for (size_t i = 0; i < selectedElectronsIso.size(); ++i) { - const auto& trackEle = selectedElectronsIso[i]; - auto child1 = RecoDecayPtEtaPhi::pVector(trackEle.pt, trackEle.eta, trackEle.phi); - float const sectorneg = trackEle.phi / o2::constants::math::SectorSpanRad; - for (size_t j = i + 1; j < selectedElectronsIso.size(); ++j) { - const auto& trackEle2 = selectedElectronsIso[j]; - auto child2 = RecoDecayPtEtaPhi::pVector(trackEle2.pt, trackEle2.eta, trackEle2.phi); - float const sectorpos = trackEle2.phi / o2::constants::math::SectorSpanRad; - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > massZMinQA) { - registry.fill(HIST("hInvMassZeeQAbg"), invMass, trackEle.pt, trackEle2.pt, trackEle.dcaxyTrk, trackEle2.dcaxyTrk, trackEle2.dcazTrk, trackEle.nclusterTPC, trackEle2.nclusterTPC, trackEle.nclusterITS, trackEle2.nclusterITS, sectorneg, sectorpos, trackEle.eop, trackEle2.eop, trackEle.energyIso, trackEle2.energyIso, trackEle.momIso, trackEle2.momIso, trackEle.ntrackIso, trackEle2.ntrackIso); - } + continue; + } + + auto const mcParticle = track.mcParticle(); + + // reject pileup : the matched particle must come from this MC collision + const bool isSecondary = (mcCfg.requireSameMcCollision && mcParticle.mcCollisionId() != collision.mcCollisionId()) || !isChargedPrimary(mcParticle); + if (isSecondary) { + for (int iv = 0; iv < nVar; ++iv) { + if (passHadronVariationTrack(track, iv)) { + registry.fill(HIST("hMcRecHadronSecondary"), centrality, track.pt(), track.eta(), iv); } } - // bg e+e+ - for (size_t i = 0; i < selectedPositronsIso.size(); ++i) { - const auto& trackPos = selectedPositronsIso[i]; - auto child1 = RecoDecayPtEtaPhi::pVector(trackPos.pt, trackPos.eta, trackPos.phi); - float const sectorneg = trackPos.phi / o2::constants::math::SectorSpanRad; - for (size_t j = i + 1; j < selectedPositronsIso.size(); ++j) { - const auto& trackPos2 = selectedPositronsIso[j]; - auto child2 = RecoDecayPtEtaPhi::pVector(trackPos2.pt, trackPos2.eta, trackPos2.phi); - float const sectorpos = trackPos2.phi / o2::constants::math::SectorSpanRad; - double const invMass = RecoDecay::m(std::array{child1, child2}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); - if (invMass > massZMinQA) { - registry.fill(HIST("hInvMassZeeQAbg"), invMass, trackPos.pt, trackPos2.pt, trackPos.dcaxyTrk, trackPos2.dcaxyTrk, trackPos2.dcazTrk, trackPos.nclusterTPC, trackPos2.nclusterTPC, trackPos.nclusterITS, trackPos2.nclusterITS, sectorneg, sectorpos, trackPos.eop, trackPos2.eop, trackPos.energyIso, trackPos2.energyIso, trackPos.momIso, trackPos2.momIso, trackPos.ntrackIso, trackPos2.ntrackIso); - } - } + continue; + } + + for (int iv = 0; iv < nVar; ++iv) { + if (!passHadronVariationTrack(track, iv)) { + continue; } - } // check arrays aren't empty - } // end of Z->ee QA - } // process + registry.fill(HIST("hMcRecHadronMatchedGenKine"), centrality, mcParticle.pt(), mcParticle.eta(), iv); + registry.fill(HIST("hMcRecHadronMatchedRecKine"), centrality, track.pt(), track.eta(), iv); + } + registry.fill(HIST("hMcPtGenRec"), mcParticle.pt(), track.pt()); + } + } // processMcRec + PROCESS_SWITCH(HfTaskElectronWeakBoson, processMcRec, "process MC reconstructed level for the associated-hadron efficiency", false); + }; // struct HfTaskElectronWeakBoson WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/HFL/Tasks/taskSingleElectron.cxx b/PWGHF/HFL/Tasks/taskSingleElectron.cxx index c519765e688..3a66413094f 100644 --- a/PWGHF/HFL/Tasks/taskSingleElectron.cxx +++ b/PWGHF/HFL/Tasks/taskSingleElectron.cxx @@ -81,16 +81,19 @@ struct HfTaskSingleElectron { Configurable ptTrackMax{"ptTrackMax", 10., "max pt cut"}; Configurable ptTrackMin{"ptTrackMin", 0.5, "min pt cut"}; Configurable etaTrackMax{"etaTrackMax", 0.8, "eta cut"}; - Configurable nCrossedRowTPCMin{"nCrossedRowTPCMin", 70, "max of TPC n cluster crossed rows"}; - Configurable nClsFoundOverFindableTpcMin{"nClsFoundOverFindableTpcMin", 0.8, "min # of TPC found/findable clusters"}; - Configurable Chi2pernClTpcMax{"Chi2pernClTpcMax", 4., "min # of tpc chi2 per clusters"}; - Configurable ClsIBItsMin{"ClsIBItsMin", 3, "min # of its clusters in IB"}; - Configurable Chi2pernClItsMax{"Chi2pernClItsMax", 6., "min # of tpc chi2 per clusters"}; + Configurable nCrossedRowTpcMin{"nCrossedRowTpcMin", 70, "min # of TPC n cluster crossed rows"}; + Configurable nCrossedRowsOverFindableClsTpcMin{"nCrossedRowsOverFindableClsTpcMin", 0.8, "min ratio of TPC crossed rows over findable clusters"}; + Configurable chi2PerNClTpcMax{"chi2PerNClTpcMax", 4., "max # of tpc chi2 per clusters"}; + Configurable clsIbItsMin{"clsIbItsMin", 3, "min # of its clusters in IB"}; + Configurable chi2PerNClItsMax{"chi2PerNClItsMax", 6., "min # of its chi2 per clusters"}; Configurable dcaxyMax{"dcaxyMax", 1., "max of track dca in xy"}; Configurable dcazMax{"dcazMax", 2., "max of track dca in z"}; + Configurable nClsTpcMin{"nClsTpcMin", 0, "min # of found TPC clusters"}; + Configurable nClsItsMin{"nClsItsMin", 0, "min # of total ITS clusters"}; + Configurable requireItsTpcRefit{"requireItsTpcRefit", true, "require ITS and TPC refit"}; Configurable nSigmaTofMax{"nSigmaTofMax", 3., "max of tof nsigma"}; Configurable nSigmaTpcMin{"nSigmaTpcMin", -1., "min of tpc nsigma"}; - Configurable nSigmaTPCMax{"nSigmaTPCMax", 3., "max of tpc nsigma"}; + Configurable nSigmaTpcMax{"nSigmaTpcMax", 3., "max of tpc nsigma"}; Configurable nBinsP{"nBinsP", 1500, "number of bins of particle momentum"}; Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histo"}; @@ -104,7 +107,7 @@ struct HfTaskSingleElectron { // using declarations using MyCollisions = soa::Join; using TracksEl = soa::Join; - using McTracksEl = soa::Join; + using McTracksEl = soa::Join; // Filter Filter collZFilter = nabs(aod::collision::posZ) < posZMax; @@ -137,26 +140,53 @@ struct HfTaskSingleElectron { // QA plots for trigger track selection histos.add("hNClsTpcTrack", "hNClsTpcTrack", kTH1D, {{200, 0, 200}}); - histos.add("hNClsFoundFindableTpcTrack", "", kTH1D, {{10, 0, 1}}); + histos.add("hCrossedRowsOverFindableTpcTrack", "", kTH1D, {{200, 0, 2}}); histos.add("hChi2TpcTrack", "", kTH1D, {{100, 0, 10}}); - histos.add("hibClsItsTrack", "", kTH1D, {{10, 0, 10}}); + histos.add("hIbClsItsTrack", "", kTH1D, {{10, 0, 10}}); histos.add("hChi2ItsTrack", "", kTH1D, {{50, 0, 50}}); histos.add("hDcaXYTrack", "", kTH1D, {{600, -3, 3}}); histos.add("hDcaZTrack", "", kTH1D, {{600, -3, 3}}); + // QA of track-selection variables before any track cut, differential in pT (binning follows DPG qaEventTrack) + histos.add("hPtEtaPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});#eta", kTH2D, {{axisPtEl}, {40, -1., 1.}}); + histos.add("hPtCrossedRowsTpcPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});TPC crossed rows", kTH2D, {{axisPtEl}, {165, -0.5, 164.5}}); + histos.add("hPtCrossedRowsOverFindableTpcPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});TPC crossed rows / findable clusters", kTH2D, {{axisPtEl}, {200, 0., 2.}}); + histos.add("hPtNClsFoundTpcPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});TPC found clusters", kTH2D, {{axisPtEl}, {165, -0.5, 164.5}}); + histos.add("hPtNClsFindableTpcPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});TPC findable clusters", kTH2D, {{axisPtEl}, {165, -0.5, 164.5}}); + histos.add("hPtFoundOverFindableTpcPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});TPC found / findable clusters", kTH2D, {{axisPtEl}, {200, 0., 2.}}); + histos.add("hPtFracSharedClsTpcPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});fraction of shared TPC clusters", kTH2D, {{axisPtEl}, {100, 0., 1.}}); + histos.add("hPtChi2TpcPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});#chi^{2} / cluster TPC", kTH2D, {{axisPtEl}, {100, 0., 10.}}); + histos.add("hPtNClsItsPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});# clusters ITS", kTH2D, {{axisPtEl}, {8, -0.5, 7.5}}); + histos.add("hPtIbClsItsPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});# clusters ITS inner barrel", kTH2D, {{axisPtEl}, {4, -0.5, 3.5}}); + histos.add("hPtChi2ItsPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});#chi^{2} / cluster ITS", kTH2D, {{axisPtEl}, {100, 0., 40.}}); + histos.add("hPtDcaXYPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", kTH2D, {{axisPtEl}, {600, -3., 3.}}); + histos.add("hPtDcaZPreCut", "before track selection;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", kTH2D, {{axisPtEl}, {600, -3., 3.}}); + // pid histos.add("hTofNSigPt", "", kTH2D, {{axisPtEl}, {axisNsig}}); histos.add("hTofNSigPtQA", "", kTH2D, {{axisPtEl}, {axisNsig}}); - histos.add("hTPCNSigP", "", kTH2D, {{axisP}, {axisNsig}}); - histos.add("hTPCNSigPt", "", kTH2D, {{axisPtEl}, {axisNsig}}); - histos.add("hTPCNSigPAfterTofCut", "", kTH2D, {{axisP}, {axisNsig}}); - histos.add("hTPCNSigPtAfterTofCut", "", kTH2D, {{axisPtEl}, {axisNsig}}); - histos.add("hTPCNSigPtQA", "", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigP", "", kTH2D, {{axisP}, {axisNsig}}); + histos.add("hTpcNSigPt", "", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPAfterTofCut", "", kTH2D, {{axisP}, {axisNsig}}); + histos.add("hTpcNSigPtAfterTofCut", "", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtQA", "", kTH2D, {{axisPtEl}, {axisNsig}}); + + // TPC nSigma(e) vs pT per MC truth species (electron, pion, kaon, proton, other), after the track selection and after the TOF electron selection + histos.add("hTpcNSigPtRecElectron", "MC truth e^{#pm} after track selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtRecPion", "MC truth #pi^{#pm} after track selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtRecKaon", "MC truth K^{#pm} after track selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtRecProton", "MC truth p after track selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtRecOther", "MC truth other after track selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtAfterTofCutRecElectron", "MC truth e^{#pm} after TOF selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtAfterTofCutRecPion", "MC truth #pi^{#pm} after TOF selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtAfterTofCutRecKaon", "MC truth K^{#pm} after TOF selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtAfterTofCutRecProton", "MC truth p after TOF selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); + histos.add("hTpcNSigPtAfterTofCutRecOther", "MC truth other after TOF selection;#it{p}_{T} (GeV/#it{c});n#sigma_{e}^{TPC}", kTH2D, {{axisPtEl}, {axisNsig}}); // track impact parameter histos.add("hDcaTrack", "", kTH2D, {{axisPtEl}, {axisTrackIp}}); histos.add("hDcaBeauty", "", kTH2D, {{axisPtEl}, {axisTrackIp}}); - histos.add("hDcaChram", "", kTH2D, {{axisPtEl}, {axisTrackIp}}); + histos.add("hDcaCharm", "", kTH2D, {{axisPtEl}, {axisTrackIp}}); histos.add("hDcaDalitz", "", kTH2D, {{axisPtEl}, {axisTrackIp}}); histos.add("hDcaConv", "", kTH2D, {{axisPtEl}, {axisTrackIp}}); histos.add("hDcaHadron", "", kTH2D, {{axisPtEl}, {axisTrackIp}}); @@ -168,6 +198,61 @@ struct HfTaskSingleElectron { histos.add("hPdgCo", "", kTH1D, {{10001, -0.5, 10000.5}}); } + template + void fillTrackQaPreCut(const TrackType& track) + { + double const pt = track.pt(); + histos.fill(HIST("hPtEtaPreCut"), pt, track.eta()); + histos.fill(HIST("hPtCrossedRowsTpcPreCut"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("hPtCrossedRowsOverFindableTpcPreCut"), pt, track.tpcCrossedRowsOverFindableCls()); + histos.fill(HIST("hPtNClsFoundTpcPreCut"), pt, track.tpcNClsFound()); + histos.fill(HIST("hPtNClsFindableTpcPreCut"), pt, track.tpcNClsFindable()); + histos.fill(HIST("hPtFoundOverFindableTpcPreCut"), pt, track.tpcFoundOverFindableCls()); + histos.fill(HIST("hPtFracSharedClsTpcPreCut"), pt, track.tpcFractionSharedCls()); + histos.fill(HIST("hPtChi2TpcPreCut"), pt, track.tpcChi2NCl()); + histos.fill(HIST("hPtNClsItsPreCut"), pt, track.itsNCls()); + histos.fill(HIST("hPtIbClsItsPreCut"), pt, track.itsNClsInnerBarrel()); + histos.fill(HIST("hPtChi2ItsPreCut"), pt, track.itsChi2NCl()); + histos.fill(HIST("hPtDcaXYPreCut"), pt, track.dcaXY()); + histos.fill(HIST("hPtDcaZPreCut"), pt, track.dcaZ()); + } + + template + void fillTpcNSigPtRec(const TrackType& track, bool afterTofCut) + { + if (!track.has_mcParticle()) { + return; + } + double const pt = track.pt(); + double const nSigmaEl = track.tpcNSigmaEl(); + int const absPdg = std::abs(track.mcParticle().pdgCode()); + if (!afterTofCut) { + if (absPdg == kElectron) { + histos.fill(HIST("hTpcNSigPtRecElectron"), pt, nSigmaEl); + } else if (absPdg == kPiPlus) { + histos.fill(HIST("hTpcNSigPtRecPion"), pt, nSigmaEl); + } else if (absPdg == kKPlus) { + histos.fill(HIST("hTpcNSigPtRecKaon"), pt, nSigmaEl); + } else if (absPdg == kProton) { + histos.fill(HIST("hTpcNSigPtRecProton"), pt, nSigmaEl); + } else { + histos.fill(HIST("hTpcNSigPtRecOther"), pt, nSigmaEl); + } + return; + } + if (absPdg == kElectron) { + histos.fill(HIST("hTpcNSigPtAfterTofCutRecElectron"), pt, nSigmaEl); + } else if (absPdg == kPiPlus) { + histos.fill(HIST("hTpcNSigPtAfterTofCutRecPion"), pt, nSigmaEl); + } else if (absPdg == kKPlus) { + histos.fill(HIST("hTpcNSigPtAfterTofCutRecKaon"), pt, nSigmaEl); + } else if (absPdg == kProton) { + histos.fill(HIST("hTpcNSigPtAfterTofCutRecProton"), pt, nSigmaEl); + } else { + histos.fill(HIST("hTpcNSigPtAfterTofCutRecOther"), pt, nSigmaEl); + } + } + template bool trackSel(const TrackType& track) { @@ -178,23 +263,35 @@ struct HfTaskSingleElectron { return false; } - if (track.tpcNClsCrossedRows() < nCrossedRowTPCMin) { + if (track.tpcNClsCrossedRows() < nCrossedRowTpcMin) { return false; } - if (track.tpcCrossedRowsOverFindableCls() < nClsFoundOverFindableTpcMin) { + if (track.tpcCrossedRowsOverFindableCls() < nCrossedRowsOverFindableClsTpcMin) { return false; } - if (track.tpcChi2NCl() > Chi2pernClTpcMax) { + if (track.tpcNClsFound() < nClsTpcMin) { return false; } - if (!(track.itsNClsInnerBarrel() == ClsIBItsMin)) { + if (track.tpcChi2NCl() > chi2PerNClTpcMax) { return false; } - if (track.itsChi2NCl() > Chi2pernClItsMax) { + if (track.itsNClsInnerBarrel() < clsIbItsMin) { // inner barrel has 3 layers: default 3 is equivalent to the previous == requirement + return false; + } + + if (track.itsNCls() < nClsItsMin) { + return false; + } + + if (track.itsChi2NCl() > chi2PerNClItsMax) { + return false; + } + + if (requireItsTpcRefit && !(track.passedITSRefit() && track.passedTPCRefit())) { return false; } @@ -436,11 +533,9 @@ struct HfTaskSingleElectron { for (const auto& track : tracks) { - if (!trackSel(track)) { - continue; - } + fillTrackQaPreCut(track); - if (!(track.passedITSRefit() && track.passedTPCRefit())) { + if (!trackSel(track)) { continue; } @@ -448,25 +543,25 @@ struct HfTaskSingleElectron { histos.fill(HIST("hPtTrack"), track.pt()); histos.fill(HIST("hNClsTpcTrack"), track.tpcNClsCrossedRows()); - histos.fill(HIST("hNClsFoundFindableTpcTrack"), track.tpcCrossedRowsOverFindableCls()); + histos.fill(HIST("hCrossedRowsOverFindableTpcTrack"), track.tpcCrossedRowsOverFindableCls()); histos.fill(HIST("hChi2TpcTrack"), track.tpcChi2NCl()); - histos.fill(HIST("hibClsItsTrack"), track.itsNClsInnerBarrel()); + histos.fill(HIST("hIbClsItsTrack"), track.itsNClsInnerBarrel()); histos.fill(HIST("hChi2ItsTrack"), track.itsChi2NCl()); histos.fill(HIST("hDcaXYTrack"), track.dcaXY()); histos.fill(HIST("hDcaZTrack"), track.dcaZ()); histos.fill(HIST("hTofNSigPt"), track.pt(), track.tofNSigmaEl()); - histos.fill(HIST("hTPCNSigP"), track.p(), track.tpcNSigmaEl()); - histos.fill(HIST("hTPCNSigPt"), track.pt(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigP"), track.p(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigPt"), track.pt(), track.tpcNSigmaEl()); if (std::abs(track.tofNSigmaEl()) > nSigmaTofMax) { continue; } histos.fill(HIST("hTofNSigPtQA"), track.pt(), track.tofNSigmaEl()); - histos.fill(HIST("hTPCNSigPAfterTofCut"), track.p(), track.tpcNSigmaEl()); - histos.fill(HIST("hTPCNSigPtAfterTofCut"), track.pt(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigPAfterTofCut"), track.p(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigPtAfterTofCut"), track.pt(), track.tpcNSigmaEl()); - if (track.tpcNSigmaEl() < nSigmaTpcMin || track.tpcNSigmaEl() > nSigmaTPCMax) { + if (track.tpcNSigmaEl() < nSigmaTpcMin || track.tpcNSigmaEl() > nSigmaTpcMax) { continue; } @@ -475,7 +570,7 @@ struct HfTaskSingleElectron { histos.fill(HIST("hDcaHadron"), track.pt(), track.dcaXY()); } - histos.fill(HIST("hTPCNSigPtQA"), track.pt(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigPtQA"), track.pt(), track.tpcNSigmaEl()); histos.fill(HIST("hDcaTrack"), track.pt(), track.dcaXY()); } @@ -501,6 +596,8 @@ struct HfTaskSingleElectron { for (const auto& track : tracks) { + fillTrackQaPreCut(track); + if (!trackSel(track)) { continue; } @@ -509,14 +606,15 @@ struct HfTaskSingleElectron { histos.fill(HIST("hPtTrack"), track.pt()); histos.fill(HIST("hNClsTpcTrack"), track.tpcNClsCrossedRows()); - histos.fill(HIST("hNClsFoundFindableTpcTrack"), track.tpcCrossedRowsOverFindableCls()); + histos.fill(HIST("hCrossedRowsOverFindableTpcTrack"), track.tpcCrossedRowsOverFindableCls()); histos.fill(HIST("hChi2TpcTrack"), track.tpcChi2NCl()); - histos.fill(HIST("hibClsItsTrack"), track.itsNClsInnerBarrel()); + histos.fill(HIST("hIbClsItsTrack"), track.itsNClsInnerBarrel()); histos.fill(HIST("hDcaXYTrack"), track.dcaXY()); histos.fill(HIST("hDcaZTrack"), track.dcaZ()); histos.fill(HIST("hTofNSigPt"), track.pt(), track.tofNSigmaEl()); - histos.fill(HIST("hTPCNSigPt"), track.pt(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigPt"), track.pt(), track.tpcNSigmaEl()); + fillTpcNSigPtRec(track, false); int mpdg{}; // electron source pdg code double mpt{}; // electron source pt @@ -529,7 +627,7 @@ struct HfTaskSingleElectron { if (source == DirectCharm) { histos.fill(HIST("hPdgC"), mpdg); - histos.fill(HIST("hDcaChram"), track.pt(), track.dcaXY()); + histos.fill(HIST("hDcaCharm"), track.pt(), track.dcaXY()); } if (source >= GammaPi0 && source <= GammaSigma) { @@ -549,17 +647,18 @@ struct HfTaskSingleElectron { continue; } histos.fill(HIST("hTofNSigPtQA"), track.pt(), track.tofNSigmaEl()); - histos.fill(HIST("hTPCNSigPtAfterTofCut"), track.pt(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigPtAfterTofCut"), track.pt(), track.tpcNSigmaEl()); + fillTpcNSigPtRec(track, true); - if (track.tpcNSigmaEl() < nSigmaTpcMin || track.tpcNSigmaEl() > nSigmaTPCMax) { + if (track.tpcNSigmaEl() < nSigmaTpcMin || track.tpcNSigmaEl() > nSigmaTpcMax) { continue; } - histos.fill(HIST("hTPCNSigPtQA"), track.pt(), track.tpcNSigmaEl()); + histos.fill(HIST("hTpcNSigPtQA"), track.pt(), track.tpcNSigmaEl()); histos.fill(HIST("hDcaTrack"), track.pt(), track.dcaXY()); } } - PROCESS_SWITCH(HfTaskSingleElectron, processMc, "For real data", false); + PROCESS_SWITCH(HfTaskSingleElectron, processMc, "For MC simulations", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGHF/Macros/computeFonllPlusPythiaPredictions.C b/PWGHF/Macros/computeFonllPlusPythiaPredictions.C index 605e0a8f446..c0e92758d40 100644 --- a/PWGHF/Macros/computeFonllPlusPythiaPredictions.C +++ b/PWGHF/Macros/computeFonllPlusPythiaPredictions.C @@ -316,12 +316,12 @@ void computeFonllPlusPythiaPredictions(int nDecays, int seed, std::string inFile std::array normCrossSec{}; for (auto iFonll{0}; iFonll < 3; ++iFonll) { - normCrossSec[iFonll] = hFonllBhad[iFonll]->Integral(); + normCrossSec[iFonll] = hFonllBhad[iFonll]->Integral(1, hFonllBhad[iFonll]->GetNbinsX() + 1, "width"); for (auto iChad{0}; iChad < NCharmHadrons; ++iChad) { - hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll] = reinterpret_cast(hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][0][iFonll]->Clone(Form("hFonllNonPrompt%s%s", charmHadNames[iChad].data(), namesFonll[iFonll].data()))); + hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll] = dynamic_cast(hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][0][iFonll]->Clone(Form("hFonllNonPrompt%s%s", charmHadNames[iChad].data(), namesFonll[iFonll].data()))); hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll]->Reset(); for (auto iBHad{0}; iBHad < NBeautyHadrons; ++iBHad) { - hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][iBHad][iFonll]->Scale(normCrossSec[iFonll] / nDecays); + hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][iBHad][iFonll]->Scale(normCrossSec[iFonll] / nDecays, "width"); hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][NBeautyHadrons][iFonll]->Add(hFonllPythiaNonPromptChad[charmHadPdgs[iChad]][iBHad][iFonll]); } } diff --git a/PWGHF/TableProducer/CMakeLists.txt b/PWGHF/TableProducer/CMakeLists.txt index 174cbfa9621..0029240333b 100644 --- a/PWGHF/TableProducer/CMakeLists.txt +++ b/PWGHF/TableProducer/CMakeLists.txt @@ -25,6 +25,7 @@ o2physics_add_dpl_workflow(refit-pv-dummy o2physics_add_dpl_workflow(pid-creator SOURCES pidCreator.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -37,71 +38,85 @@ o2physics_add_dpl_workflow(mc-pid-tof o2physics_add_dpl_workflow(candidate-creator-2prong SOURCES candidateCreator2Prong.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-3prong SOURCES candidateCreator3Prong.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-b0 SOURCES candidateCreatorB0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-bplus SOURCES candidateCreatorBplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-bs SOURCES candidateCreatorBs.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-cascade SOURCES candidateCreatorCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-dstar SOURCES candidateCreatorDstar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-lb SOURCES candidateCreatorLb.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-sigmac0plusplus SOURCES candidateCreatorSigmac0plusplus.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-sigmac0plusplus-cascade SOURCES candidateCreatorSigmac0plusplusCascade.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic0-omegac0 SOURCES candidateCreatorXic0Omegac0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic0-omegac0-qa SOURCES candidateCreatorXic0Omegac0Qa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-xic-to-xi-pi-pi SOURCES candidateCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-creator-mc-gen SOURCES candidateCreatorMcGen.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -109,101 +124,127 @@ o2physics_add_dpl_workflow(candidate-creator-mc-gen o2physics_add_dpl_workflow(candidate-selector-b0-to-d-pi SOURCES candidateSelectorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-bplus-to-d0-pi SOURCES candidateSelectorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-bs-to-ds-pi SOURCES candidateSelectorBsToDsPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-d0 SOURCES candidateSelectorD0.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-dplus-to-pi-k-pi SOURCES candidateSelectorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-ds-to-k-k-pi SOURCES candidateSelectorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-dstar-to-d0-pi SOURCES candidateSelectorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lb-to-lc-pi SOURCES candidateSelectorLbToLcPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc SOURCES candidateSelectorLc.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-pid-ml SOURCES candidateSelectorLcPidMl.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-lc-to-k0s-p SOURCES candidateSelectorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-ka SOURCES candidateSelectorOmegac0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi SOURCES candidateSelectorOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(candidate-selector-omegac0-to-omega-pi-qa + SOURCES candidateSelectorOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-omegac0-xic0-to-omega-ka SOURCES candidateSelectorOmegac0Xic0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic0-to-xi-pi-kf SOURCES candidateSelectorXic0ToXiPiKf.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-to-xi-pi SOURCES candidateSelectorToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-to-xi-pi-qa SOURCES candidateSelectorToXiPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic-to-p-k-pi SOURCES candidateSelectorXicToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-xic-to-xi-pi-pi SOURCES candidateSelectorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(candidate-selector-cd SOURCES candidateSelectorCd.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -211,56 +252,67 @@ o2physics_add_dpl_workflow(candidate-selector-cd o2physics_add_dpl_workflow(tree-creator-b0-to-d-pi SOURCES treeCreatorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-bplus-to-d0-pi SOURCES treeCreatorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-bs-to-ds-pi SOURCES treeCreatorBsToDsPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-d0-to-k-pi SOURCES treeCreatorD0ToKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-dplus-to-pi-k-pi SOURCES treeCreatorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-ds-to-k-k-pi SOURCES treeCreatorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-dstar-to-d0-pi SOURCES treeCreatorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lb-to-lc-pi SOURCES treeCreatorLbToLcPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lc-to-k0s-p SOURCES treeCreatorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-lc-to-p-k-pi SOURCES treeCreatorLcToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-sigmac-corr-bkg SOURCES treeCreatorSigmacCorrBkg.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -271,87 +323,110 @@ o2physics_add_dpl_workflow(tree-creator-omegac-st o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-ka SOURCES treeCreatorOmegac0ToOmegaKa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi SOURCES treeCreatorOmegac0ToOmegaPi.cxx + REUSE_FROM HfCandidatesPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(tree-creator-omegac0-to-omega-pi-qa + SOURCES treeCreatorOmegac0ToOmegaPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-to-xi-pi SOURCES treeCreatorToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-to-xi-pi-qa SOURCES treeCreatorToXiPiQa.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic0-to-xi-pi-kf SOURCES treeCreatorXic0ToXiPiKf.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic-to-p-k-pi SOURCES treeCreatorXicToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-xic-to-xi-pi-pi SOURCES treeCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tree-creator-tcc-to-d0-d0-pi SOURCES treeCreatorTccToD0D0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) # Derived-data creators o2physics_add_dpl_workflow(derived-data-creator-b0-to-d-pi SOURCES derivedDataCreatorB0ToDPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-bplus-to-d0-pi SOURCES derivedDataCreatorBplusToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-d0-to-k-pi SOURCES derivedDataCreatorD0ToKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-dplus-to-pi-k-pi SOURCES derivedDataCreatorDplusToPiKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-ds-to-k-k-pi SOURCES derivedDataCreatorDsToKKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-dstar-to-d0-pi SOURCES derivedDataCreatorDstarToD0Pi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-lc-to-p-k-pi SOURCES derivedDataCreatorLcToPKPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-lc-to-k0s-p SOURCES derivedDataCreatorLcToK0sP.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derived-data-creator-xic-to-xi-pi-pi SOURCES derivedDataCreatorXicToXiPiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGHF/TableProducer/candidateCreator2Prong.cxx b/PWGHF/TableProducer/candidateCreator2Prong.cxx index 821936284fb..ab7f982c398 100644 --- a/PWGHF/TableProducer/candidateCreator2Prong.cxx +++ b/PWGHF/TableProducer/candidateCreator2Prong.cxx @@ -38,6 +38,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Tools/KFparticle/KFUtilities.h" #include @@ -535,7 +536,7 @@ struct HfCandidateCreator2Prong { /////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/o centrality selections - void processPvRefitWithDCAFitterN(soa::Join const& collisions, + void processPvRefitWithDCAFitterN(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -545,7 +546,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithDCAFitterN, "Run candidate creator using DCA fitter w/ PV refit and w/o centrality selections", false); /// @brief process function using DCA fitter w/o PV refit and w/o centrality selections - void processNoPvRefitWithDCAFitterN(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterN(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -555,7 +556,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processNoPvRefitWithDCAFitterN, "Run candidate creator using DCA fitter w/o PV refit and w/o centrality selections", true); /// @brief process function using KFParticle package w/ PV refit and w/o centrality selections - void processPvRefitWithKFParticle(soa::Join const& collisions, + void processPvRefitWithKFParticle(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -565,7 +566,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithKFParticle, "Run candidate creator using KFParticle package w/ PV refit and w/o centrality selections", false); /// @brief process function using KFParticle package w/o PV refit and w/o centrality selections - void processNoPvRefitWithKFParticle(soa::Join const& collisions, + void processNoPvRefitWithKFParticle(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -581,7 +582,7 @@ struct HfCandidateCreator2Prong { ///////////////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/ centrality selection on FT0C - void processPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, + void processPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -591,7 +592,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithDCAFitterNCentFT0C, "Run candidate creator using DCA fitter w/ PV refit and w/ centrality selection on FT0C", false); /// @brief process function using DCA fitter w/o PV refit and w/ centrality selection FT0C - void processNoPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -601,7 +602,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processNoPvRefitWithDCAFitterNCentFT0C, "Run candidate creator using DCA fitter w/o PV refit and w/ centrality selection FT0C", false); /// @brief process function using KFParticle package w/ PV refit and w/ centrality selection on FT0C - void processPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, + void processPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -611,7 +612,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithKFParticleCentFT0C, "Run candidate creator using KFParticle package w/ PV refit and w/ centrality selection on FT0C", false); /// @brief process function using KFParticle package w/o PV refit and w/o centrality selections - void processNoPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, + void processNoPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -627,7 +628,7 @@ struct HfCandidateCreator2Prong { ///////////////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/ centrality selection on FT0M - void processPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, + void processPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -637,7 +638,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithDCAFitterNCentFT0M, "Run candidate creator using DCA fitter w/ PV refit and w/ centrality selection on FT0M", false); /// @brief process function using DCA fitter w/o PV refit and w/ centrality selection FT0M - void processNoPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -647,7 +648,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processNoPvRefitWithDCAFitterNCentFT0M, "Run candidate creator using DCA fitter w/o PV refit and w/ centrality selection FT0M", false); /// @brief process function using KFParticle package w/ PV refit and w/ centrality selection on FT0M - void processPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, + void processPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -657,7 +658,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithKFParticleCentFT0M, "Run candidate creator using KFParticle package w/ PV refit and w/ centrality selection on FT0M", false); /// @brief process function using KFParticle package w/o PV refit and w/o centrality selections - void processNoPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, + void processNoPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -673,7 +674,7 @@ struct HfCandidateCreator2Prong { ///////////////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/ centrality selection on UPC - void processPvRefitWithDCAFitterNUpc(soa::Join const& collisions, + void processPvRefitWithDCAFitterNUpc(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -687,7 +688,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithDCAFitterNUpc, "Run candidate creator using DCA fitter w/ PV refit and w/ centrality selection on UltraPeripheral Collision", false); /// @brief process function using DCA fitter w/o PV refit and w/ centrality selection UPC - void processNoPvRefitWithDCAFitterNUpc(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterNUpc(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -701,7 +702,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processNoPvRefitWithDCAFitterNUpc, "Run candidate creator using DCA fitter w/o PV refit and w/ centrality selection UltraPeripheral Collision", false); /// @brief process function using KFParticle package w/ PV refit and w/ centrality selection on UPC - void processPvRefitWithKFParticleUpc(soa::Join const& collisions, + void processPvRefitWithKFParticleUpc(soa::Join const& collisions, soa::Join const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -715,7 +716,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processPvRefitWithKFParticleUpc, "Run candidate creator using KFParticle package w/ PV refit and w/ centrality selection on UltraPeripheral Collision", false); /// @brief process function using KFParticle package w/o PV refit and w/o centrality selections on UPC - void processNoPvRefitWithKFParticleUpc(soa::Join const& collisions, + void processNoPvRefitWithKFParticleUpc(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexProng2, TracksWCovExtraPidPiKa const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -735,7 +736,7 @@ struct HfCandidateCreator2Prong { /////////////////////////////////////////////////////////// /// @brief process function to monitor collisions - no centrality - void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -754,7 +755,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processCollisions, "Collision monitoring - no centrality", true); /// @brief process function to monitor collisions - FT0C centrality - void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -773,7 +774,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processCollisionsCentFT0C, "Collision monitoring - FT0C centrality", false); /// @brief process function to monitor collisions - FT0M centrality - void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -792,7 +793,7 @@ struct HfCandidateCreator2Prong { PROCESS_SWITCH(HfCandidateCreator2Prong, processCollisionsCentFT0M, "Collision monitoring - FT0M centrality", false); /// @brief process function to monitor collisions - UPC collision - void processCollisionsUpc(soa::Join const& collisions, + void processCollisionsUpc(soa::Join const& collisions, aod::BcFullInfos const& bcs, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0as*/, @@ -830,9 +831,9 @@ struct HfCandidateCreator2ProngExpressions { HfEventSelectionMc hfEvSelMc; // mc event selection and monitoring - using McCollisionsNoCents = soa::Join; - using McCollisionsFT0Cs = soa::Join; - using McCollisionsFT0Ms = soa::Join; + using McCollisionsNoCents = soa::Join; + using McCollisionsFT0Cs = soa::Join; + using McCollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; using BCsInfo = soa::Join; diff --git a/PWGHF/TableProducer/candidateCreator3Prong.cxx b/PWGHF/TableProducer/candidateCreator3Prong.cxx index b7542c5bbe3..1091c00a0a2 100644 --- a/PWGHF/TableProducer/candidateCreator3Prong.cxx +++ b/PWGHF/TableProducer/candidateCreator3Prong.cxx @@ -38,6 +38,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Tools/KFparticle/KFUtilities.h" #include @@ -715,7 +716,7 @@ struct HfCandidateCreator3Prong { /////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/o centrality selections - void processPvRefitWithDCAFitterN(soa::Join const& collisions, + void processPvRefitWithDCAFitterN(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -725,7 +726,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithDCAFitterN, "Run candidate creator using DCA fitter with PV refit and w/o centrality selections", false); /// @brief process function using DCA fitter w/o PV refit and w/o centrality selections - void processNoPvRefitWithDCAFitterN(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterN(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -735,7 +736,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processNoPvRefitWithDCAFitterN, "Run candidate creator using DCA fitter without PV refit and w/o centrality selections", true); /// @brief process function using KFParticle package w/ PV refit and w/o centrality selections - void processPvRefitWithKFParticle(soa::Join const& collisions, + void processPvRefitWithKFParticle(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -745,7 +746,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithKFParticle, "Run candidate creator using KFParticle package with PV refit and w/o centrality selections", false); /// @brief process function using KFParticle package w/o PV refit and w/o centrality selections - void processNoPvRefitWithKFParticle(soa::Join const& collisions, + void processNoPvRefitWithKFParticle(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -761,7 +762,7 @@ struct HfCandidateCreator3Prong { ///////////////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/ centrality selection on FT0C - void processPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, + void processPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -771,7 +772,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithDCAFitterNCentFT0C, "Run candidate creator using DCA fitter with PV refit and w/ centrality selection on FT0C", false); /// @brief process function using DCA fitter w/o PV refit and w/ centrality selection on FT0C - void processNoPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterNCentFT0C(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -781,7 +782,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processNoPvRefitWithDCAFitterNCentFT0C, "Run candidate creator using DCA fitter without PV refit and w/ centrality selection on FT0C", false); /// @brief process function using KFParticle package w/ PV refit and w/ centrality selection on FT0C - void processPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, + void processPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -791,7 +792,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithKFParticleCentFT0C, "Run candidate creator using KFParticle package with PV refit and w/ centrality selection on FT0C", false); /// @brief process function using KFParticle package w/o PV refit and w/ centrality selection on FT0C - void processNoPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, + void processNoPvRefitWithKFParticleCentFT0C(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -807,7 +808,7 @@ struct HfCandidateCreator3Prong { ///////////////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/ centrality selection on FT0M - void processPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, + void processPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -817,7 +818,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithDCAFitterNCentFT0M, "Run candidate creator using DCA fitter with PV refit and w/ centrality selection on FT0M", false); /// @brief process function using DCA fitter w/o PV refit and w/ centrality selection on FT0M - void processNoPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterNCentFT0M(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -827,7 +828,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processNoPvRefitWithDCAFitterNCentFT0M, "Run candidate creator using DCA fitter without PV refit and w/ centrality selection on FT0M", false); /// @brief process function using KFParticle package w/ PV refit and w/ centrality selection on FT0M - void processPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, + void processPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -837,7 +838,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithKFParticleCentFT0M, "Run candidate creator using KFParticle package with PV refit and w/ centrality selection on FT0M", false); /// @brief process function using KFParticle package w/o PV refit and w/ centrality selection on FT0M - void processNoPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, + void processNoPvRefitWithKFParticleCentFT0M(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BCsWithTimestamps const& bcWithTimeStamps) @@ -853,7 +854,7 @@ struct HfCandidateCreator3Prong { ///////////////////////////////////////////// /// @brief process function using DCA fitter w/ PV refit and w/ centrality selection on UPC - void processPvRefitWithDCAFitterNUpc(soa::Join const& collisions, + void processPvRefitWithDCAFitterNUpc(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -867,7 +868,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithDCAFitterNUpc, "Run candidate creator using DCA fitter with PV refit and w/ centrality selection on UPC", false); /// @brief process function using DCA fitter w/o PV refit and w/ centrality selection on UPC - void processNoPvRefitWithDCAFitterNUpc(soa::Join const& collisions, + void processNoPvRefitWithDCAFitterNUpc(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -881,7 +882,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processNoPvRefitWithDCAFitterNUpc, "Run candidate creator using DCA fitter without PV refit and w/ centrality selection on UPC", false); /// @brief process function using KFParticle package w/ PV refit and w/ centrality selection on UPC - void processPvRefitWithKFParticleUpc(soa::Join const& collisions, + void processPvRefitWithKFParticleUpc(soa::Join const& collisions, FilteredPvRefitHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -895,7 +896,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processPvRefitWithKFParticleUpc, "Run candidate creator using KFParticle package with PV refit and w/ centrality selection on UPC", false); /// @brief process function using KFParticle package w/o PV refit and w/ centrality selection on UPC - void processNoPvRefitWithKFParticleUpc(soa::Join const& collisions, + void processNoPvRefitWithKFParticleUpc(soa::Join const& collisions, FilteredHf3Prongs const& rowsTrackIndexProng3, TracksWCovExtraPidPiKaPrLightNuclei const& tracks, aod::BcFullInfos const& bcWithTimeStamps, @@ -915,7 +916,7 @@ struct HfCandidateCreator3Prong { /////////////////////////////////////////////////////////// /// @brief process function to monitor collisions - no centrality - void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -934,7 +935,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processCollisions, "Collision monitoring - no centrality", true); /// @brief process function to monitor collisions - FT0C centrality - void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -953,7 +954,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processCollisionsCentFT0C, "Collision monitoring - FT0C centrality", false); /// @brief process function to monitor collisions - FT0M centrality - void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -972,7 +973,7 @@ struct HfCandidateCreator3Prong { PROCESS_SWITCH(HfCandidateCreator3Prong, processCollisionsCentFT0M, "Collision monitoring - FT0M centrality", false); /// @brief process function to monitor collisions - UPC - void processCollisionsUpc(soa::Join const& collisions, + void processCollisionsUpc(soa::Join const& collisions, aod::BcFullInfos const& bcs, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0as*/, @@ -1014,9 +1015,9 @@ struct HfCandidateCreator3ProngExpressions { HfEventSelectionMc hfEvSelMc; // mc event selection and monitoring using BCsInfo = soa::Join; - using McCollisionsNoCents = soa::Join; - using McCollisionsFT0Cs = soa::Join; - using McCollisionsFT0Ms = soa::Join; + using McCollisionsNoCents = soa::Join; + using McCollisionsFT0Cs = soa::Join; + using McCollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; @@ -1327,6 +1328,26 @@ struct HfCandidateCreator3ProngExpressions { } } } + + // cd± → de± K∓ π± + if (flagChannelMain == 0) { + auto arrPdgDaughtersCDeuteronToDeKPi{std::array{+Pdg::kDeuteron, -kKPlus, +kPiPlus}}; + if (matchKinkedDecayTopology && matchInteractionsWithMaterial) { + indexRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughters, Pdg::kCDeuteron, arrPdgDaughtersCDeuteronToDeKPi, true, &sign, 1, &nKinkedTracks, &nInteractionsWithMaterial); + } else if (matchKinkedDecayTopology && !matchInteractionsWithMaterial) { + indexRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughters, Pdg::kCDeuteron, arrPdgDaughtersCDeuteronToDeKPi, true, &sign, 1, &nKinkedTracks); + } else if (!matchKinkedDecayTopology && matchInteractionsWithMaterial) { + indexRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughters, Pdg::kCDeuteron, arrPdgDaughtersCDeuteronToDeKPi, true, &sign, 1, nullptr, &nInteractionsWithMaterial); + } else { + indexRec = RecoDecay::getMatchedMCRec(mcParticles, arrayDaughters, Pdg::kCDeuteron, arrPdgDaughtersCDeuteronToDeKPi, true, &sign, 1); + } + if (indexRec > -1) { + flagChannelMain = static_cast(sign * DecayChannelMain::CDeuteronToDeKPi); + auto particle = mcParticles.rawIteratorAt(indexRec); + // particular treatment for c-deuteron resonant decay channels, as resonances are not stored in the stack + flagChannelResonant = hf_decay::getResonantDecayCDeuteron(particle); + } + } } // Check whether the particle is non-prompt (from a b quark). @@ -1338,6 +1359,9 @@ struct HfCandidateCreator3ProngExpressions { auto bHadMother = mcParticles.rawIteratorAt(idxBhadMothers[0]); rowMcMatchRec(flagChannelMain, origin, swapping, flagChannelResonant, bHadMother.pt(), bHadMother.pdgCode(), nKinkedTracks, nInteractionsWithMaterial); } else { + if (std::abs(flagChannelMain) == DecayChannelMain::CDeuteronToDeKPi) { + origin = RecoDecay::OriginType::Prompt; + } rowMcMatchRec(flagChannelMain, origin, swapping, flagChannelResonant, -1.f, 0, nKinkedTracks, nInteractionsWithMaterial); } } diff --git a/PWGHF/TableProducer/candidateCreatorB0.cxx b/PWGHF/TableProducer/candidateCreatorB0.cxx index 385a2d551aa..850c9796de6 100644 --- a/PWGHF/TableProducer/candidateCreatorB0.cxx +++ b/PWGHF/TableProducer/candidateCreatorB0.cxx @@ -373,7 +373,8 @@ struct HfCandidateCreatorB0 { pVecD[0], pVecD[1], pVecD[2], pVecPion[0], pVecPion[1], pVecPion[2], dcaD.getY(), dcaPion.getY(), - std::sqrt(dcaD.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2())); + std::sqrt(dcaD.getSigmaY2()), std::sqrt(dcaPion.getSigmaY2()), + candD.xSecondaryVertex(), candD.ySecondaryVertex(), candD.zSecondaryVertex()); rowCandidateProngs(candD.globalIndex(), trackPion.globalIndex()); } // pi loop diff --git a/PWGHF/TableProducer/candidateCreatorBplus.cxx b/PWGHF/TableProducer/candidateCreatorBplus.cxx index 22ded3ecc7b..8894c7ec60f 100644 --- a/PWGHF/TableProducer/candidateCreatorBplus.cxx +++ b/PWGHF/TableProducer/candidateCreatorBplus.cxx @@ -366,7 +366,8 @@ struct HfCandidateCreatorBplus { pVecD0[0], pVecD0[1], pVecD0[2], pVecBach[0], pVecBach[1], pVecBach[2], impactParameter0.getY(), impactParameter1.getY(), - std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2())); + std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2()), + candD0.xSecondaryVertex(), candD0.ySecondaryVertex(), candD0.zSecondaryVertex()); rowCandidateProngs(candD0.globalIndex(), trackPion.globalIndex()); // index D0 and bachelor } // track loop diff --git a/PWGHF/TableProducer/candidateCreatorCascade.cxx b/PWGHF/TableProducer/candidateCreatorCascade.cxx index fe9c2c3a57c..d5450ffa27e 100644 --- a/PWGHF/TableProducer/candidateCreatorCascade.cxx +++ b/PWGHF/TableProducer/candidateCreatorCascade.cxx @@ -30,6 +30,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include #include @@ -323,7 +324,7 @@ struct HfCandidateCreatorCascade { } /// @brief process function w/o centrality selections - void processNoCent(soa::Join const& collisions, + void processNoCent(soa::Join const& collisions, aod::HfCascades const& rowsTrackIndexCasc, aod::V0sLinked const& v0sLinked, V0full const& v0Full, @@ -335,7 +336,7 @@ struct HfCandidateCreatorCascade { PROCESS_SWITCH(HfCandidateCreatorCascade, processNoCent, " Run candidate creator w/o centrality selections", true); /// @brief process function w/ centrality selection on FT0C - void processCentFT0C(soa::Join const& collisions, + void processCentFT0C(soa::Join const& collisions, aod::HfCascades const& rowsTrackIndexCasc, aod::V0sLinked const& v0sLinked, V0full const& v0Full, @@ -347,7 +348,7 @@ struct HfCandidateCreatorCascade { PROCESS_SWITCH(HfCandidateCreatorCascade, processCentFT0C, " Run candidate creator w/ centrality selection on FT0C", false); /// @brief process function w/ centrality selection on FT0M - void processCentFT0M(soa::Join const& collisions, + void processCentFT0M(soa::Join const& collisions, aod::HfCascades const& rowsTrackIndexCasc, aod::V0sLinked const& v0sLinked, V0full const& v0Full, @@ -365,7 +366,7 @@ struct HfCandidateCreatorCascade { /////////////////////////////////////////////////////////// /// @brief process function to monitor collisions - no centrality - void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -384,7 +385,7 @@ struct HfCandidateCreatorCascade { PROCESS_SWITCH(HfCandidateCreatorCascade, processCollisions, "Collision monitoring - no centrality", true); /// @brief process function to monitor collisions - FT0C centrality - void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -403,7 +404,7 @@ struct HfCandidateCreatorCascade { PROCESS_SWITCH(HfCandidateCreatorCascade, processCollisionsCentFT0C, "Collision monitoring - FT0C centrality", false); /// @brief process function to monitor collisions - FT0M centrality - void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -435,9 +436,9 @@ struct HfCandidateCreatorCascadeMc { using MyTracksWMc = soa::Join; using BCsInfo = soa::Join; - using McCollisionsNoCents = soa::Join; - using McCollisionsFT0Cs = soa::Join; - using McCollisionsFT0Ms = soa::Join; + using McCollisionsNoCents = soa::Join; + using McCollisionsFT0Cs = soa::Join; + using McCollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; diff --git a/PWGHF/TableProducer/candidateCreatorDstar.cxx b/PWGHF/TableProducer/candidateCreatorDstar.cxx index 6e4701bc63b..b6cfbd8da88 100644 --- a/PWGHF/TableProducer/candidateCreatorDstar.cxx +++ b/PWGHF/TableProducer/candidateCreatorDstar.cxx @@ -31,6 +31,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include #include @@ -403,7 +404,7 @@ struct HfCandidateCreatorDstar { /////////////////////////////////// /// @brief process function w/ PV refit and w/o centrality selections - void processPvRefit(soa::Join const& collisions, + void processPvRefit(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexD0, aod::HfDstarsWithPvRefitInfo const& rowsTrackIndexDstar, TracksWCovExtraPidPiKa const& tracks, @@ -414,7 +415,7 @@ struct HfCandidateCreatorDstar { PROCESS_SWITCH(HfCandidateCreatorDstar, processPvRefit, " Run candidate creator with PV refit and w/o centrality selections", false); /// @brief process function w/o PV refit and w/o centrality selections - void processNoPvRefit(soa::Join const& collisions, + void processNoPvRefit(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexD0, aod::HfDstars const& rowsTrackIndexDstar, TracksWCovExtraPidPiKa const& tracks, @@ -431,7 +432,7 @@ struct HfCandidateCreatorDstar { ///////////////////////////////////////////// /// @brief process function w/ PV refit and w/ centrality selection on FT0C - void processPvRefitCentFT0C(soa::Join const& collisions, + void processPvRefitCentFT0C(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexD0, aod::HfDstarsWithPvRefitInfo const& rowsTrackIndexDstar, TracksWCovExtraPidPiKa const& tracks, @@ -442,7 +443,7 @@ struct HfCandidateCreatorDstar { PROCESS_SWITCH(HfCandidateCreatorDstar, processPvRefitCentFT0C, " Run candidate creator with PV refit nad w/ centrality selection on FT0C", false); /// @brief process function w/o PV refit and w/ centrality selection on FT0C - void processNoPvRefitCentFT0C(soa::Join const& collisions, + void processNoPvRefitCentFT0C(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexD0, aod::HfDstars const& rowsTrackIndexDstar, TracksWCovExtraPidPiKa const& tracks, @@ -459,7 +460,7 @@ struct HfCandidateCreatorDstar { ///////////////////////////////////////////// /// @brief process function w/ PV refit and w/ centrality selection on FT0M - void processPvRefitCentFT0M(soa::Join const& collisions, + void processPvRefitCentFT0M(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexD0, aod::HfDstarsWithPvRefitInfo const& rowsTrackIndexDstar, TracksWCovExtraPidPiKa const& tracks, @@ -470,7 +471,7 @@ struct HfCandidateCreatorDstar { PROCESS_SWITCH(HfCandidateCreatorDstar, processPvRefitCentFT0M, " Run candidate creator with PV refit nad w/ centrality selection on FT0M", false); /// @brief process function w/o PV refit and w/ centrality selection on FT0M - void processNoPvRefitCentFT0M(soa::Join const& collisions, + void processNoPvRefitCentFT0M(soa::Join const& collisions, aod::Hf2Prongs const& rowsTrackIndexD0, aod::HfDstars const& rowsTrackIndexDstar, TracksWCovExtraPidPiKa const& tracks, @@ -487,7 +488,7 @@ struct HfCandidateCreatorDstar { /////////////////////////////////////////////////////////// /// @brief process function to monitor collisions - no centrality - void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -506,7 +507,7 @@ struct HfCandidateCreatorDstar { PROCESS_SWITCH(HfCandidateCreatorDstar, processCollisions, "Collision monitoring - no centrality", true); /// @brief process function to monitor collisions - FT0C centrality - void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -525,7 +526,7 @@ struct HfCandidateCreatorDstar { PROCESS_SWITCH(HfCandidateCreatorDstar, processCollisionsCentFT0C, "Collision monitoring - FT0C centrality", false); /// @brief process function to monitor collisions - FT0M centrality - void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -555,9 +556,9 @@ struct HfCandidateCreatorDstarExpressions { Configurable matchKinkedDecayTopology{"matchKinkedDecayTopology", false, "Match also candidates with tracks that decay with kinked topology"}; Configurable matchInteractionsWithMaterial{"matchInteractionsWithMaterial", false, "Match also candidates with tracks that interact with material"}; - using McCollisionsNoCents = soa::Join; - using McCollisionsFT0Cs = soa::Join; - using McCollisionsFT0Ms = soa::Join; + using McCollisionsNoCents = soa::Join; + using McCollisionsFT0Cs = soa::Join; + using McCollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; using BCsInfo = soa::Join; diff --git a/PWGHF/TableProducer/candidateCreatorSigmac0plusplus.cxx b/PWGHF/TableProducer/candidateCreatorSigmac0plusplus.cxx index 072d7dcf530..4d02957459b 100644 --- a/PWGHF/TableProducer/candidateCreatorSigmac0plusplus.cxx +++ b/PWGHF/TableProducer/candidateCreatorSigmac0plusplus.cxx @@ -33,6 +33,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include // for dca recalculation #include @@ -424,7 +425,7 @@ struct HfCandidateSigmac0plusplusMc { using BCsInfo = soa::Join; using LambdacMc = soa::Join; using McParticlesLcGenMatch = soa::Join; // including response of particle matching to MC from candidate-creator-3-prong - using McCollisionsNoCents = soa::Join; + using McCollisionsNoCents = soa::Join; PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx index b2f6980d93f..1030e695c4b 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0.cxx @@ -28,6 +28,7 @@ #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" #include "PWGHF/Utils/utilsBfieldCCDB.h" #include "PWGHF/Utils/utilsEvSelHf.h" +// #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/mcCentrality.h" @@ -36,6 +37,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Tools/KFparticle/KFUtilities.h" #include @@ -57,6 +59,7 @@ #include #include #include +#include #include #include @@ -96,6 +99,28 @@ enum McMatchFlag : uint8_t { V0Unmatched }; +// Convert the absolute KFParticle PDG code to the O2 track PID enum needed by getTrackParCovFromKFP() +o2::track::PID::ID getTrackPIDFromPDG(const int pdg) +{ + switch (std::abs(pdg)) { + case kPiPlus: + return o2::track::PID::Pion; + case kKPlus: + return o2::track::PID::Kaon; + case kProton: + return o2::track::PID::Proton; + case kLambda0: + return o2::track::PID::Lambda; + case kXiMinus: + return o2::track::PID::XiMinus; + case kOmegaMinus: + return o2::track::PID::OmegaMinus; + default: + LOGF(fatal, "Unsupported PDG code %d in getTrackPIDFromPDG()", pdg); + return o2::track::PID::Pion; + } +} + // Reconstruction of omegac0 and xic0 candidates struct HfCandidateCreatorXic0Omegac0 { Produces rowCandToXiPi; @@ -592,7 +617,7 @@ struct HfCandidateCreatorXic0Omegac0 { } else { mCasc = casc.mOmega(); } - auto arrMassCharmBaryon = std::array{0., 0.}; + std::array arrMassCharmBaryon{}; if constexpr (DecayChannel == hf_cand_casc_lf::DecayType2Prong::XiczeroOmegaczeroToXiPi) { arrMassCharmBaryon = {MassXiMinus, MassPiPlus}; } else if constexpr (DecayChannel == hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi) { @@ -808,7 +833,7 @@ struct HfCandidateCreatorXic0Omegac0 { KFParticle const kfNegKa(kfTrackBach, kKMinus); KFParticle const kfNegPiRej(kfTrackBach, kPiMinus); // rej KFParticle const kfPosPi(kfTrack0, kPiPlus); - KFParticle const kfNegPr(kfTrack1, kProton); + KFParticle const kfNegPr(kfTrack1, kProtonBar); KFParticle const kfPosKa(kfTrackBach, kKPlus); KFParticle const kfPosPiRej(kfTrackBach, kPiPlus); // rej @@ -855,7 +880,7 @@ struct HfCandidateCreatorXic0Omegac0 { KFParticle kfV0MassConstrained = kfV0; kfV0MassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassLambda); // set mass constrain to Lambda if (kfUseV0MassConstraint) { - KFParticle const kfV0 = kfV0MassConstrained; + kfV0 = kfV0MassConstrained; } kfV0.TransportToDecayVertex(); @@ -866,6 +891,8 @@ struct HfCandidateCreatorXic0Omegac0 { // construct cascade KFParticle kfOmega; KFParticle kfOmegarej; // rej + kfOmega.SetPDG(bachCharge < 0 ? kOmegaMinus : kOmegaPlusBar); + kfOmegarej.SetPDG(bachCharge < 0 ? kOmegaMinus : kOmegaPlusBar); kfOmega.SetConstructMethod(kfConstructMethod); kfOmegarej.SetConstructMethod(kfConstructMethod); // rej try { @@ -894,7 +921,7 @@ struct HfCandidateCreatorXic0Omegac0 { kfOmegaMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassOmegaMinus); // set mass constrain to OmegaMinus if (kfUseCascadeMassConstraint) { // set mass constraint if requested - KFParticle const kfOmega = kfOmegaMassConstrained; + kfOmega = kfOmegaMassConstrained; } registry.fill(HIST("hInvMassOmegaMinus"), massCasc); kfOmega.TransportToDecayVertex(); @@ -904,7 +931,7 @@ struct HfCandidateCreatorXic0Omegac0 { //*>~<* step 3 : reconstruc Omegac0 with KF // Create KF charm bach Pion from track KFPTrack const kfTrackBachPion = createKFPTrackFromTrack(trackCharmBachelor); - KFParticle const kfBachPion(kfTrackBachPion, kPiPlus); + KFParticle const kfBachPion(kfTrackBachPion, bachCharge < 0 ? kPiPlus : kPiMinus); const KFParticle* omegaC0Daugthers[2] = {&kfBachPion, &kfOmega}; // construct OmegaC0 @@ -959,12 +986,16 @@ struct HfCandidateCreatorXic0Omegac0 { omegaDauChargedTrackParCov = getTrackParCovFromKFP(kfBachKaonToOmega, o2::track::PID::Kaon, bachCharge); // Cascade bach kaon omegaDauChargedTrackParCov.setAbsCharge(1); - o2::track::TrackParCov trackCasc = getTrackParCovFromKFP(kfOmegaToOmegaC, kfOmegaToOmegaC.GetPDG(), bachCharge); + o2::track::PID::ID pidCasc = getTrackPIDFromPDG(kfOmegaToOmegaC.GetPDG()); + o2::track::TrackParCov trackCasc = getTrackParCovFromKFP(kfOmegaToOmegaC, pidCasc, bachCharge); trackCasc.setAbsCharge(1); - trackParCovV0Dau0 = getTrackParCovFromKFP(kfPos, kfPos.GetPDG(), 1); // V0 postive daughter + o2::track::PID::ID pidV0Dau0 = getTrackPIDFromPDG(kfPos.GetPDG()); + trackParCovV0Dau0 = getTrackParCovFromKFP(kfPos, pidV0Dau0, +1); // V0 postive daughter trackParCovV0Dau0.setAbsCharge(1); - trackParCovV0Dau1 = getTrackParCovFromKFP(kfNeg, kfNeg.GetPDG(), -1); // V0 negtive daughter + + o2::track::PID::ID pidV0Dau1 = getTrackPIDFromPDG(kfNeg.GetPDG()); + trackParCovV0Dau1 = getTrackParCovFromKFP(kfNeg, pidV0Dau1, -1); // V0 negative daughter trackParCovV0Dau1.setAbsCharge(1); //-------------------------- V0 info--------------------------- @@ -1297,7 +1328,7 @@ struct HfCandidateCreatorXic0Omegac0 { KFParticle const kfNegPi(kfTrack1, kPiMinus); KFParticle const kfNegBachPi(kfTrackBach, kPiMinus); KFParticle const kfPosPi(kfTrack0, kPiPlus); - KFParticle const kfNegPr(kfTrack1, kProton); + KFParticle const kfNegPr(kfTrack1, kProtonBar); KFParticle const kfPosBachPi(kfTrackBach, kPiPlus); KFParticle kfBachPion; @@ -1355,6 +1386,7 @@ struct HfCandidateCreatorXic0Omegac0 { const KFParticle* xiDaugthers[2] = {&kfBachPion, &kfV0}; // construct cascade KFParticle kfXi; + kfXi.SetPDG(bachCharge < 0 ? kXiMinus : kXiPlusBar); kfXi.SetConstructMethod(kfConstructMethod); try { kfXi.Construct(xiDaugthers, 2); @@ -1382,7 +1414,7 @@ struct HfCandidateCreatorXic0Omegac0 { kfXiMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassXiMinus); // set mass constrain to XiMinus if (kfUseCascadeMassConstraint) { // set mass constraint if requested - KFParticle const kfXi = kfXiMassConstrained; + kfXi = kfXiMassConstrained; } registry.fill(HIST("hInvMassXiMinus"), massCasc); kfXi.TransportToDecayVertex(); @@ -1452,12 +1484,17 @@ struct HfCandidateCreatorXic0Omegac0 { xiDauChargedTrackParCov = getTrackParCovFromKFP(kfBachPionToXi, o2::track::PID::Pion, bachCharge); // Cascade bach pion xiDauChargedTrackParCov.setAbsCharge(1); - o2::track::TrackParCov trackCasc = getTrackParCovFromKFP(kfXiToXiC, kfXiToXiC.GetPDG(), bachCharge); + + o2::track::PID::ID pidCasc = getTrackPIDFromPDG(kfXiToXiC.GetPDG()); + o2::track::TrackParCov trackCasc = getTrackParCovFromKFP(kfXiToXiC, pidCasc, bachCharge); trackCasc.setAbsCharge(1); - trackParCovV0Dau0 = getTrackParCovFromKFP(kfPos, kfPos.GetPDG(), 1); // V0 postive daughter + o2::track::PID::ID pidV0Dau0 = getTrackPIDFromPDG(kfPos.GetPDG()); + trackParCovV0Dau0 = getTrackParCovFromKFP(kfPos, pidV0Dau0, +1); // V0 postive daughter trackParCovV0Dau0.setAbsCharge(1); - trackParCovV0Dau1 = getTrackParCovFromKFP(kfNeg, kfNeg.GetPDG(), -1); // V0 negtive daughter + + o2::track::PID::ID pidV0Dau1 = getTrackPIDFromPDG(kfNeg.GetPDG()); + trackParCovV0Dau1 = getTrackParCovFromKFP(kfNeg, pidV0Dau1, -1); // V0 negative daughter trackParCovV0Dau1.setAbsCharge(1); //-------------------------- V0 info--------------------------- @@ -1811,7 +1848,7 @@ struct HfCandidateCreatorXic0Omegac0 { } registry.fill(HIST("hInvMassXiMinus_rej"), massCascrej); // rej: Add competing rejection to minimize misidentified Xi impact. Reject if kfBachPionRej is Pion and the constructed cascade has Xi's invariant mass. KFParticle kfOmegaMassConstrained = kfOmega; - kfOmegaMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassOmegaMinus); // set mass constrain to XiMinus + kfOmegaMassConstrained.SetNonlinearMassConstraint(o2::constants::physics::MassOmegaMinus); // set mass constrain to OmegaMinus if (kfUseCascadeMassConstraint) { // set mass constraint if requested kfOmega = kfOmegaMassConstrained; @@ -1988,7 +2025,7 @@ struct HfCandidateCreatorXic0Omegac0 { } /// @brief process function w/o centrality selections - void processNoCentToXiPi(soa::Join const& collisions, + void processNoCentToXiPi(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2000,7 +2037,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processNoCentToXiPi, "Run candidate creator w/o centrality selections for xi pi decay channel", true); - void processNoCentToXiPiTraCasc(soa::Join const& collisions, + void processNoCentToXiPiTraCasc(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2012,7 +2049,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processNoCentToXiPiTraCasc, "Run candidate creator w/o centrality selections for xi pi decay channel with tracked cascades", false); - void processNoCentToOmegaPi(soa::Join const& collisions, + void processNoCentToOmegaPi(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2024,7 +2061,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processNoCentToOmegaPi, "Run candidate creator w/o centrality selections for omega pi decay channel", false); - void processNoCentOmegacToOmegaPiWithKFParticle(soa::Join const& collisions, + void processNoCentOmegacToOmegaPiWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2036,7 +2073,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processNoCentOmegacToOmegaPiWithKFParticle, "Run candidate creator w/o centrality selections for Omegac0 To omega pi decay channel using KFParticle", false); - void processNoCentOmegac0Xic0ToOmegaKaCreatorWithKFParticle(soa::Join const& collisions, + void processNoCentOmegac0Xic0ToOmegaKaCreatorWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2048,7 +2085,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processNoCentOmegac0Xic0ToOmegaKaCreatorWithKFParticle, "Run candidate creator w/o centrality selections for Omegac0 To omega ka decay channel using KFParticle", false); - void processNoCentXicToXiPiWithKFParticle(soa::Join const& collisions, + void processNoCentXicToXiPiWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2060,7 +2097,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processNoCentXicToXiPiWithKFParticle, "Run candidate creator w/o centrality selections for Xic0 To Xi pi decay channel using KFParticle", false); - void processNoCentToOmegaK(soa::Join const& collisions, + void processNoCentToOmegaK(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2073,7 +2110,7 @@ struct HfCandidateCreatorXic0Omegac0 { PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processNoCentToOmegaK, "Run candidate creator w/o centrality selections for omega K decay channel", false); /// @brief process function w/ FT0C centrality selections - void processCentFT0CToXiPi(soa::Join const& collisions, + void processCentFT0CToXiPi(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2085,7 +2122,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0CToXiPi, "Run candidate creator w/ centrality selection on FT0C for xi pi channel", false); - void processCentFT0CToOmegaPi(soa::Join const& collisions, + void processCentFT0CToOmegaPi(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2097,7 +2134,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0CToOmegaPi, "Run candidate creator w/ centrality selection on FT0C for omega pi channel", false); - void processCentFT0COmegacToOmegaPiWithKFParticle(soa::Join const& collisions, + void processCentFT0COmegacToOmegaPiWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2109,7 +2146,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0COmegacToOmegaPiWithKFParticle, "Run candidate creator w/o centrality selections for Omegac0 To omega pi decay channel using KFParticle", false); - void processCentFT0COmegac0Xic0ToOmegaKaCreatorWithKFParticle(soa::Join const& collisions, + void processCentFT0COmegac0Xic0ToOmegaKaCreatorWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2121,7 +2158,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0COmegac0Xic0ToOmegaKaCreatorWithKFParticle, "Run candidate creator w/o centrality selections for Omegac0 To omega ka decay channel using KFParticle", false); - void processCentFT0CXicToXiPiWithKFParticle(soa::Join const& collisions, + void processCentFT0CXicToXiPiWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2133,7 +2170,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0CXicToXiPiWithKFParticle, "Run candidate creator w FT0C centrality selections for Xic0 To Xi pi decay channel using KFParticle", false); - void processCentFT0CToOmegaK(soa::Join const& collisions, + void processCentFT0CToOmegaK(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2146,7 +2183,7 @@ struct HfCandidateCreatorXic0Omegac0 { PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0CToOmegaK, "Run candidate creator w/ centrality selection on FT0C for omega K channel", false); /// @brief process function w/ FT0M centrality selections - void processCentFT0MToXiPi(soa::Join const& collisions, + void processCentFT0MToXiPi(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2158,7 +2195,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0MToXiPi, "Run candidate creator w/ centrality selection on FT0M for xi pi channel", false); - void processCentFT0MToOmegaPi(soa::Join const& collisions, + void processCentFT0MToOmegaPi(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2170,7 +2207,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0MToOmegaPi, "Run candidate creator w/ centrality selection on FT0M for omega pi channel", false); - void processCentFT0MOmegacToOmegaPiWithKFParticle(soa::Join const& collisions, + void processCentFT0MOmegacToOmegaPiWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2182,7 +2219,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0MOmegacToOmegaPiWithKFParticle, "Run candidate creator w/o centrality selections for Omegac0 To omega pi decay channel using KFParticle", false); - void processCentFT0MOmegac0Xic0ToOmegaKaCreatorWithKFParticle(soa::Join const& collisions, + void processCentFT0MOmegac0Xic0ToOmegaKaCreatorWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2194,7 +2231,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0MOmegac0Xic0ToOmegaKaCreatorWithKFParticle, "Run candidate creator w/o centrality selections for Omegac0 To omega ka decay channel using KFParticle", false); - void processCentFT0MXicToXiPiWithKFParticle(soa::Join const& collisions, + void processCentFT0MXicToXiPiWithKFParticle(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, MyKfTracksIU const& tracksIU, MyKfTracks const& tracks, @@ -2206,7 +2243,7 @@ struct HfCandidateCreatorXic0Omegac0 { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCentFT0MXicToXiPiWithKFParticle, "Run candidate creator w FT0M centrality selections for Xic0 To Xi pi decay channel using KFParticle", false); - void processCentFT0MToOmegaK(soa::Join const& collisions, + void processCentFT0MToOmegaK(soa::Join const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, TracksWCovDca const& tracks, MyLFTracksWCov const& lfTracks, @@ -2225,7 +2262,7 @@ struct HfCandidateCreatorXic0Omegac0 { /////////////////////////////////////////////////////////// /// @brief process function to monitor collisions - no centrality - void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -2244,7 +2281,7 @@ struct HfCandidateCreatorXic0Omegac0 { PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCollisions, "Collision monitoring - no centrality", true); /// @brief process function to monitor collisions - FT0C centrality - void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -2263,7 +2300,7 @@ struct HfCandidateCreatorXic0Omegac0 { PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0, processCollisionsCentFT0C, "Collision monitoring - FT0C centrality", false); /// @brief process function to monitor collisions - FT0M centrality - void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { /// loop over collisions for (const auto& collision : collisions) { @@ -2299,9 +2336,9 @@ struct HfCandidateCreatorXic0Omegac0Mc { Configurable acceptTrackIntWithMaterial{"acceptTrackIntWithMaterial", false, " switch to accept candidates with final (i.e. p, K, pi) daughter tracks interacting with material"}; using MyTracksWMc = soa::Join; - using McCollisionsNoCents = soa::Join; - using McCollisionsFT0Cs = soa::Join; - using McCollisionsFT0Ms = soa::Join; + using McCollisionsNoCents = soa::Join; + using McCollisionsFT0Cs = soa::Join; + using McCollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; using BCsInfo = soa::Join; diff --git a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx index 20b4fcaf307..67f4bee267d 100644 --- a/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx +++ b/PWGHF/TableProducer/candidateCreatorXic0Omegac0Qa.cxx @@ -34,6 +34,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Tools/KFparticle/KFUtilities.h" #include @@ -233,7 +234,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { Service ccdb{}; Service pdgdb{}; - o2::base::MatLayerCylSet* lut; + o2::base::MatLayerCylSet* lut{}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; // DCAFitter @@ -248,7 +249,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { VertexFit }; // Table aliases - using SelectedCollisions = soa::Join; + using SelectedCollisions = soa::Join; using TracksWCovIU = soa::Join; using TracksWCovDcaExtraPidPrPiKa = soa::Join; using TracksWCovExtraPidIU = soa::Join; @@ -258,16 +259,15 @@ struct HfCandidateCreatorXic0Omegac0Qa { HfEventSelection hfEvSel; // PDG Id of daughter tracks & V0s & cascades & charm baryons - Used in KFParticle - int pdgIdOfV0DauPos, pdgIdOfV0DauNeg, pdgIdOfBach, pdgIdOfCharmBach; - int pdgIdOfAntiV0DauPos, pdgIdOfAntiV0DauNeg, pdgIdOfAntiBach, pdgIdOfAntiCharmBach; - int pdgIdOfV0, pdgIdOfCascade, pdgIdOfCharmBaryon; + int pdgIdOfV0DauPos{}, pdgIdOfV0DauNeg{}, pdgIdOfBach{}, pdgIdOfBachRej{}, pdgIdOfCharmBach{}; + int pdgIdOfV0{}, pdgIdOfCascade{}, pdgIdOfCharmBaryon{}; - // Track PID - Used in DCAFitter - int trackPidOfCascade; + // Track PID: PID value of tracks defined under o2::track::PID namespace + int trackPidOfCascade{}; // Mass of daughter tracks & V0s & cascades & charm baryons; - float massOfV0DauPos, massOfV0DauNeg, massOfBach, massOfCharmBach; - float massOfV0, massOfCascade, massOfCharmBaryon; + float massOfV0DauPos{}, massOfV0DauNeg{}, massOfCharmBach{}; + float massOfV0{}, massOfCascade{}; // Pointer of histograms for QA std::shared_ptr hInvMassCharmBaryonToXiPi, hInvMassCharmBaryonToOmegaPi, hInvMassCharmBaryonToOmegaKa; @@ -315,13 +315,9 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kPiMinus; + pdgIdOfBachRej = 0; // -> Not used in XiPi channel pdgIdOfCharmBach = kPiPlus; - pdgIdOfAntiV0DauPos = kPiPlus; - pdgIdOfAntiV0DauNeg = kProton; - pdgIdOfAntiBach = kPiPlus; - pdgIdOfAntiCharmBach = kPiMinus; - pdgIdOfV0 = kLambda0; pdgIdOfCascade = kXiMinus; pdgIdOfCharmBaryon = kXiC0; @@ -337,13 +333,9 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kKMinus; + pdgIdOfBachRej = kPiMinus; pdgIdOfCharmBach = kPiPlus; - pdgIdOfAntiV0DauPos = kPiPlus; - pdgIdOfAntiV0DauNeg = kProton; - pdgIdOfAntiBach = kKPlus; - pdgIdOfAntiCharmBach = kPiMinus; - pdgIdOfV0 = kLambda0; pdgIdOfCascade = kOmegaMinus; pdgIdOfCharmBaryon = kOmegaC0; @@ -359,13 +351,9 @@ struct HfCandidateCreatorXic0Omegac0Qa { pdgIdOfV0DauPos = kProton; pdgIdOfV0DauNeg = kPiMinus; pdgIdOfBach = kKMinus; + pdgIdOfBachRej = kPiMinus; pdgIdOfCharmBach = kKPlus; - pdgIdOfAntiV0DauPos = kPiPlus; - pdgIdOfAntiV0DauNeg = kProton; - pdgIdOfAntiBach = kKPlus; - pdgIdOfAntiCharmBach = kKMinus; - pdgIdOfV0 = kLambda0; pdgIdOfCascade = kOmegaMinus; pdgIdOfCharmBaryon = kOmegaC0; @@ -378,25 +366,22 @@ struct HfCandidateCreatorXic0Omegac0Qa { massOfV0 = o2::constants::physics::MassLambda; massOfCascade = o2::constants::physics::MassOmegaMinus; } - LOGF(info, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); - LOGF(info, "PDG ID of V0 positive daughter: %d", pdgIdOfV0DauPos); - LOGF(info, "PDG ID of V0 negative daughter: %d", pdgIdOfV0DauNeg); - LOGF(info, "PDG ID of Bachelor: %d", pdgIdOfBach); - LOGF(info, "PDG ID of Charm Bachelor: %d", pdgIdOfCharmBach); - LOGF(info, "-------------------------------------------"); - LOGF(info, "PDG ID of anti V0 positive daughter: %d", pdgIdOfAntiV0DauPos); - LOGF(info, "PDG ID of anti V0 negative daughter: %d", pdgIdOfAntiV0DauNeg); - LOGF(info, "PDG ID of anti Bachelor: %d", pdgIdOfAntiBach); - LOGF(info, "PDG ID of anti Charm Bachelor: %d", pdgIdOfAntiCharmBach); - LOGF(info, "-------------------------------------------"); - LOGF(info, "PDG ID of V0: %d", pdgIdOfV0); - LOGF(info, "PDG ID of Cascade: %d", pdgIdOfCascade); - LOGF(info, "PDG ID of Charm Baryon: %d", pdgIdOfCharmBaryon); - LOGF(info, "-------------------------------------------"); - LOGF(info, "Mass of V0 set as: %f", massOfV0); - LOGF(info, "Mass of CharmBach set as: %f", massOfCharmBach); - LOGF(info, "Mass of Casc as: %f", massOfCascade); - LOGF(info, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + LOGF(info, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + LOGF(info, " Chosen reconstruction info: ToXiPi(%d)/ ToOmegaPi(%d)/ ToOmegaKa(%d)", xipiEnabledDca + xipiEnabledKf, omegapiEnabledDca + omegapiEnabledKf, omegakaEnabledDca + omegakaEnabledKf); + LOGF(info, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + LOGF(info, " PDG ID of V0 positive daughter: %d", pdgIdOfV0DauPos); + LOGF(info, " PDG ID of V0 negative daughter: %d", pdgIdOfV0DauNeg); + LOGF(info, " PDG ID of V0: %d", pdgIdOfV0); + LOGF(info, " PDG ID of Bachelor: %d", pdgIdOfBach); + LOGF(info, " PDG ID of Bachelor Rej: %d", pdgIdOfBachRej); + LOGF(info, " PDG ID of Cascade: %d", pdgIdOfCascade); + LOGF(info, " PDG ID of Charm Bachelor: %d", pdgIdOfCharmBach); + LOGF(info, " PDG ID of Charm Baryon: %d", pdgIdOfCharmBaryon); + LOGF(info, "-----------------------------------------------------------------------"); + LOGF(info, " Mass of V0 set as: %f", massOfV0); + LOGF(info, " Mass of CharmBach set as: %f", massOfCharmBach); + LOGF(info, " Mass of Casc as: %f", massOfCascade); + LOGF(info, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); // Add histogram to indicate which sv method was used registry.add("hVertexerType", "Use KF or DCAFitterN;Vertexer type;entries", {kTH1F, {{2, 0.0, 2.0}}}); @@ -512,11 +497,29 @@ struct HfCandidateCreatorXic0Omegac0Qa { } // end of initialization - //////////////////////////////////////////////////////////// - // // - // Candidate reconstruction with DCAFitter // - // // - //////////////////////////////////////////////////////////// + // helper function to convert PDG of KFParticle object into appropriate track PID. + // exclusively used to make input of getTrackParCovFromKFP() + // \brief kfPdg is Pdg value of KFParticle object + o2::track::PID::ID convertPDGIntoPID(const int kfPdg) + { + switch (std::abs(kfPdg)) { + case kPiPlus: + return o2::track::PID::Pion; + case kKPlus: + return o2::track::PID::Kaon; + case kProton: + return o2::track::PID::Proton; + case kLambda0: + return o2::track::PID::Lambda; + case kXiMinus: + return o2::track::PID::XiMinus; + case kOmegaMinus: + return o2::track::PID::OmegaMinus; + default: + LOGF(fatal, "Undefined PDG value from KFParticle given for conversion(%d). Replace it with PID of Pion", kfPdg); + return o2::track::PID::Pion; + } + } // template function for running charm baryon reconstruction with DCAFitter /// \brief centEstimator is for different centrality estimators @@ -961,11 +964,12 @@ struct HfCandidateCreatorXic0Omegac0Qa { const KFPTrack kfTrackBach = createKFPTrackFromTrack(bachTrack); bool isAnti = (bachTrack.signed1Pt() > 0 ? true : false); + int bachCharge = (bachTrack.signed1Pt() > 0 ? +1 : -1); - KFParticle kfPos(kfTrack0, (isAnti ? pdgIdOfAntiV0DauPos : pdgIdOfV0DauPos)); - KFParticle kfNeg(kfTrack1, (isAnti ? pdgIdOfAntiV0DauNeg : pdgIdOfV0DauNeg)); - KFParticle kfBach(kfTrackBach, (isAnti ? pdgIdOfAntiBach : pdgIdOfBach)); - KFParticle kfBachRej(kfTrackBach, (isAnti ? pdgIdOfAntiBach : pdgIdOfBach)); // Rej -> Used for Omegac0->OmegaPi only + KFParticle kfPos(kfTrack0, (isAnti ? -pdgIdOfV0DauNeg : pdgIdOfV0DauPos)); + KFParticle kfNeg(kfTrack1, (isAnti ? -pdgIdOfV0DauPos : pdgIdOfV0DauNeg)); + KFParticle kfBach(kfTrackBach, (isAnti ? -pdgIdOfBach : pdgIdOfBach)); + KFParticle kfBachRej(kfTrackBach, (isAnti ? -pdgIdOfBachRej : pdgIdOfBachRej)); // Rej -> Used for Omegac0->OmegaPi only // ~~~~~~~Construct V0 with KF~~~~~~~ const KFParticle* v0Daughters[2] = {&kfPos, &kfNeg}; @@ -998,6 +1002,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { kfV0 = kfV0MassConstrained; } kfV0.TransportToDecayVertex(); + kfV0.SetPDG(pdgIdOfV0); //~~~~~~~Construct cascade with KF~~~~~~~ const KFParticle* cascDaughters[2] = {&kfBach, &kfV0}; @@ -1024,6 +1029,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { if (kfCasc.GetNDF() <= 0 || kfCasc.GetChi2() <= 0) { continue; } + kfCasc.SetPDG(pdgIdOfCascade); // perform cascade building on casc_rej - only for Omega if constexpr (decayChannel != hf_cand_casc_lf::DecayType2Prong::XiczeroOmegaczeroToXiPi) { @@ -1036,6 +1042,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } kfCascRej.GetMass(massCascRej, sigMassCascRej); + kfCascRej.SetPDG(pdgIdOfCascade); } // Set mass constraint to cascade @@ -1049,7 +1056,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { //~~~~~~~Construct Charm Baryon with KF~~~~~~~ auto trackCharmBachelor = tracks.rawIteratorAt(cand.prong0Id()); const KFPTrack kfTrackCharmBach = createKFPTrackFromTrack(trackCharmBachelor); - const KFParticle kfCharmBach(kfTrackCharmBach, (isAnti ? pdgIdOfAntiCharmBach : pdgIdOfCharmBach)); + const KFParticle kfCharmBach(kfTrackCharmBach, (isAnti ? -pdgIdOfCharmBach : pdgIdOfCharmBach)); const KFParticle* charmBaryonDaughters[2] = {&kfCharmBach, &kfCasc}; KFParticle kfCharmBaryon; @@ -1074,6 +1081,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { hCandCounter->Fill(VertexFit); hInvMassCharmBaryon->Fill(massCharmBaryon); } + kfCharmBaryon.SetPDG(pdgIdOfCharmBaryon); // Set production vertex // PV @@ -1109,6 +1117,18 @@ struct HfCandidateCreatorXic0Omegac0Qa { kfCharmBaryonToPv.SetProductionVertex(kfPv); //----------Reconstruct information after vertex fit---------- + auto trackParCovV0DauPos = getTrackParCovFromKFP(kfPos, convertPDGIntoPID(kfPos.GetPDG()), 1); + auto trackParCovV0DauNeg = getTrackParCovFromKFP(kfNeg, convertPDGIntoPID(kfNeg.GetPDG()), -1); + auto trackParCovBach = getTrackParCovFromKFP(kfBachToCasc, convertPDGIntoPID(kfBachToCasc.GetPDG()), bachCharge); + auto trackParCovCharmBach = getTrackParCovFromKFP(kfCharmBachToCharmBaryon, convertPDGIntoPID(kfCharmBachToCharmBaryon.GetPDG()), -bachCharge); + auto trackParCovCasc = getTrackParCovFromKFP(kfCascToCharmBaryon, convertPDGIntoPID(kfCascToCharmBaryon.GetPDG()), bachCharge); + + trackParCovV0DauPos.setAbsCharge(1); + trackParCovV0DauNeg.setAbsCharge(1); + trackParCovBach.setAbsCharge(1); + trackParCovCharmBach.setAbsCharge(1); + trackParCovCasc.setAbsCharge(1); + std::array vertexV0 = {kfV0.GetX(), kfV0.GetY(), kfV0.GetZ()}; std::array vertexCasc = {kfCasc.GetX(), kfCasc.GetY(), kfCasc.GetZ()}; @@ -1125,17 +1145,6 @@ struct HfCandidateCreatorXic0Omegac0Qa { std::array pvCoord = {collision.posX(), collision.posY(), collision.posZ()}; - auto trackParCovV0DauPos = getTrackParCovFromKFP(kfPos, kfPos.GetPDG(), 1); - auto trackParCovV0DauNeg = getTrackParCovFromKFP(kfNeg, kfNeg.GetPDG(), -1); - auto trackParCovBach = getTrackParCovFromKFP(kfBachToCasc, kfBachToCasc.GetPDG(), (isAnti ? 1 : -1)); - auto trackParCovCharmBach = getTrackParCovFromKFP(kfCharmBachToCharmBaryon, kfCharmBachToCharmBaryon.GetPDG(), (isAnti ? -1 : 1)); - auto trackParCovCasc = getTrackParCovFromKFP(kfCascToCharmBaryon, kfCascToCharmBaryon.GetPDG(), (isAnti ? 1 : -1)); - trackParCovV0DauPos.setAbsCharge(1); - trackParCovV0DauNeg.setAbsCharge(1); - trackParCovBach.setAbsCharge(1); - trackParCovCharmBach.setAbsCharge(1); - trackParCovCasc.setAbsCharge(1); - //----------Calculate physical quantities and fill candidate table---------- // impact parameters @@ -1281,7 +1290,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { 0.f, 0.f, 0.f, // -> vertexCharmBaryonFromFitter. For KF, this is 0 vertexCasc[0], vertexCasc[1], vertexCasc[2], vertexV0[0], vertexV0[1], vertexV0[2], - trackCharmBachelor.sign(), + bachTrack.sign(), covVtxCharmBaryon[0], covVtxCharmBaryon[1], covVtxCharmBaryon[2], covVtxCharmBaryon[3], covVtxCharmBaryon[4], covVtxCharmBaryon[5], kfCharmBaryon.GetPx(), kfCharmBaryon.GetPy(), kfCharmBaryon.GetPz(), kfCascToCharmBaryon.GetPx(), kfCascToCharmBaryon.GetPy(), kfCascToCharmBaryon.GetPz(), @@ -1387,7 +1396,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToXiPiWithDCAFitterNoCentWithTrackedCasc, "Charm candidte reconstruction with Xi Pi via DcaFitter method with tracked cascade, no centrality", false); #endif - void processToXiPiWithDCAFitterCentFT0C(soa::Join const& collisions, + void processToXiPiWithDCAFitterCentFT0C(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1399,7 +1408,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToXiPiWithDCAFitterCentFT0C, "Charm candidate reconstruction with Xi Pi via DcaFitter method, centrality selection on FT0C", false); - void processToXiPiWithDCAFitterCentFT0M(soa::Join const& collisions, + void processToXiPiWithDCAFitterCentFT0M(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1426,7 +1435,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaPiWithDCAFitterNoCent, "Charm candidte reconstruction with Omega Pi via DcaFitter method, no centrality", false); - void processToOmegaPiWithDCAFitterCentFT0C(soa::Join const& collisions, + void processToOmegaPiWithDCAFitterCentFT0C(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1438,7 +1447,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaPiWithDCAFitterCentFT0C, "Charm candidate reconstruction with Omega Pi via DcaFitter method, centrality selection on FT0C", false); - void processToOmegaPiWithDCAFitterCentFT0M(soa::Join const& collisions, + void processToOmegaPiWithDCAFitterCentFT0M(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1465,7 +1474,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaKaWithDCAFitterNoCent, "Charm candidte reconstruction with Omega Ka via DcaFitter method, no centrality", false); - void processToOmegaKaWithDCAFitterCentFT0C(soa::Join const& collisions, + void processToOmegaKaWithDCAFitterCentFT0C(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1477,7 +1486,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaKaWithDCAFitterCentFT0C, "Charm candidate reconstruction with Omega Ka via DcaFitter method, centrality selection on FT0C", false); - void processToOmegaKaWithDCAFitterCentFT0M(soa::Join const& collisions, + void processToOmegaKaWithDCAFitterCentFT0M(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1510,7 +1519,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToXiPiWithKFParticleNoCent, "Charm Baryon decaying to Xi Pi reconstruction via KFParticle method, no centrality", false); - void processToXiPiWithKFParticleCentFT0C(soa::Join const& collisions, + void processToXiPiWithKFParticleCentFT0C(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1522,7 +1531,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToXiPiWithKFParticleCentFT0C, "Charm Baryon decaying to Xi Pi reconstruction via KFParticle method, centrality on FT0C", false); - void processToXiPiWithKFParticleCentFT0M(soa::Join const& collisions, + void processToXiPiWithKFParticleCentFT0M(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1549,7 +1558,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaPiWithKFParticleNoCent, "Charm Baryon decaying to Omega Pi reconstruction via KFParticle method, no centrality", false); - void processToOmegaPiWithKFParticleCentFT0C(soa::Join const& collisions, + void processToOmegaPiWithKFParticleCentFT0C(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1561,7 +1570,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaPiWithKFParticleCentFT0C, "Charm Baryon decaying to Omega Pi reconstruction via KFParticle method, centrality on FT0C", false); - void processToOmegaPiWithKFParticleCentFT0M(soa::Join const& collisions, + void processToOmegaPiWithKFParticleCentFT0M(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1588,7 +1597,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaKaWithKFParticleNoCent, "Charm Baryon decaying to Omega Ka reconstruction via KFParticle method, no centrality", false); - void processToOmegaKaWithKFParticleCentFT0C(soa::Join const& collisions, + void processToOmegaKaWithKFParticleCentFT0C(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1600,7 +1609,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processToOmegaKaWithKFParticleCentFT0C, "Charm Baryon decaying to Omega Ka reconstruction via KFParticle method, centrality on FT0C", false); - void processToOmegaKaWithKFParticleCentFT0M(soa::Join const& collisions, + void processToOmegaKaWithKFParticleCentFT0M(soa::Join const& collisions, aod::HfCascLf2Prongs const& candidates, aod::Cascades const& cascades, aod::V0s const& v0s, @@ -1618,7 +1627,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { /// /// /////////////////////////////////////////////////////////////// - void processCollisionsNoCent(soa::Join const& collisions, + void processCollisionsNoCent(soa::Join const& collisions, aod::BCsWithTimestamps const&) { for (const auto& collision : collisions) { @@ -1634,7 +1643,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processCollisionsNoCent, "Collision monitoring - No Centrality", true); - void processCollisionsCentFT0C(soa::Join const& collisions, + void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const&) { for (const auto& collision : collisions) { @@ -1642,7 +1651,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { // bitmask with event selection info float centrality{-1.f}; float occupancy = getOccupancyColl(collision, OccupancyEstimator::Its); - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); // monitor the satisfied event selection hfEvSel.fillHistograms(collision, rejectionMask, centrality, occupancy); @@ -1650,7 +1659,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { } PROCESS_SWITCH(HfCandidateCreatorXic0Omegac0Qa, processCollisionsCentFT0C, "Collision monitoring - Centrality selection with FT0C", false); - void processCollisionsCentFT0M(soa::Join const& collisions, + void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const&) { for (const auto& collision : collisions) { @@ -1658,7 +1667,7 @@ struct HfCandidateCreatorXic0Omegac0Qa { // bitmask with event selection info float centrality{-1.f}; float occupancy = getOccupancyColl(collision, OccupancyEstimator::Its); - const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); + const auto rejectionMask = hfEvSel.getHfCollisionRejectionMask(collision, centrality, ccdb, registry); // monitor the satisfied event selection hfEvSel.fillHistograms(collision, rejectionMask, centrality, occupancy); @@ -1709,9 +1718,9 @@ struct HfCandidateCreatorXic0Omegac0QaMc { // Table aliases using TracksWMcIU = soa::Join; - using McCollisionsNoCents = soa::Join; - using McCollisionsFT0Cs = soa::Join; - using McCollisionsFT0Ms = soa::Join; + using McCollisionsNoCents = soa::Join; + using McCollisionsFT0Cs = soa::Join; + using McCollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; // -> Used for subscription for process functions of centrality with FT0Ms using BCsInfo = soa::Join; diff --git a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx index a618360598f..b3b72f70883 100644 --- a/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateCreatorXicToXiPiPi.cxx @@ -36,6 +36,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Tools/KFparticle/KFUtilities.h" #include @@ -67,6 +68,7 @@ #include #include +#include #include #include #include @@ -107,16 +109,26 @@ struct HfCandidateCreatorXicToXiPiPi { Configurable useAbsDCA{"useAbsDCA", false, "Minimise abs. distance rather than chi2"}; Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Recalculate vertex position using track covariances, effective only if useAbsDCA is true"}; // KFParticle - Configurable useXiMassConstraint{"useXiMassConstraint", true, "Use mass constraint for Xi"}; + Configurable useXiMassConstraint{"useXiMassConstraint", true, "Use mass constraint for Xi. WARNING: Only disable if you know what you are doing!"}; Configurable constrainXicPlusToPv{"constrainXicPlusToPv", false, "Constrain XicPlus to PV"}; Configurable kfConstructMethod{"kfConstructMethod", 2, "Construct method of XicPlus: 0 fast mathematics without constraint of fixed daughter particle masses, 2 daughter particle masses stay fixed in construction process"}; Configurable rejDiffCollTrack{"rejDiffCollTrack", true, "Reject tracks coming from different collisions (effective only for KFParticle w/o derived data)"}; + // configurables for software trigger selections + struct : ConfigurableGroup { + std::string prefix = "softtrig"; + Configurable applySoftwareTrigSelections{"applySoftwareTrigSelections", false, "Enable application of software trigger selections"}; + Configurable minDecayLength{"minDecayLength", 0.015, "Minimum decay length (computed with DCAFitter)"}; + Configurable minCosPA{"minCosPA", 0.9, "Minimum coine of pointing angle (computed with DCAFitter)"}; + Configurable maxChi2Pca{"maxChi2Pca", 3., "Maximum chi2 PCA (computed with DCAFitter)"}; + Configurable maxNsigmaXiDaus{"maxNsigmaXiDaus", 3., "Maximum PID Nsigma values for Xi daughters (both TPC and TOF)"}; + } softTrigCuts; Service ccdb{}; o2::base::MatLayerCylSet* lut{}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; o2::vertexing::DCAFitterN<3> df; + o2::vertexing::DCAFitterN<2> df2Prong; HfEventSelection hfEvSel; @@ -134,7 +146,6 @@ struct HfCandidateCreatorXicToXiPiPi { using CascFull = soa::Join; using KFCascadesLinked = soa::Join; using KFCascFull = soa::Join; - using TracksWCovDcaPidPrPi = soa::Join; using TracksWCovExtraPidPrPi = soa::Join; HistogramRegistry registry{"registry"}; @@ -196,6 +207,99 @@ struct HfCandidateCreatorXicToXiPiPi { df.setMinRelChi2Change(minRelChi2Change); df.setUseAbsDCA(useAbsDCA); df.setWeightedFinalPCA(useWeightedFinalPCA); + + if (softTrigCuts.applySoftwareTrigSelections) { + float maxRadSoftTrig = 200.; + float maxDZIniSoftTrig = 1.e9; + float maxDXYIniSoftTrig = 4.; + float minParamChangeSoftTrig = 1.e-3; + float maxChi2SoftTrig = 0.9; + float minRelChi2ChangeSoftTrig = 0.9; + df2Prong.setPropagateToPCA(true); + df2Prong.setMaxR(maxRadSoftTrig); + df2Prong.setMaxDZIni(maxDZIniSoftTrig); + df2Prong.setMaxDXYIni(maxDXYIniSoftTrig); + df2Prong.setMinParamChange(minParamChangeSoftTrig); + df2Prong.setMinRelChi2Change(minRelChi2ChangeSoftTrig); + df2Prong.setMaxChi2(maxChi2SoftTrig); + df2Prong.setUseAbsDCA(false); + df2Prong.setWeightedFinalPCA(false); + } + } + + /// Method that reapplies the selections applied in the software trigger + /// \param pVecCascade is the cascade momentum vector + /// \param trackParBachelor is the array with two bachelor track parametrisations + /// \param collision is the collision containing the candidate + /// \param nSigTpcBachelorPi is the TPC n-sigma for the Xi bachelor track with pion hypothesis + /// \param nSigTofBachelorPi is the TOF n-sigma for the Xi bachelor track with pion hypothesis + /// \param nSigTpcPiFromLambda is the TPC n-sigma for the pion track from the Lambda decay with pion hypothesis + /// \param nSigTofPiFromLambda is the TOF n-sigma for the pion track from the Lambda decay with pion hypothesis + /// \param nSigTpcPrFromLambda is the TPC n-sigma for the proton track from the Lambda decay with proton hypothesis + /// \param nSigTofPrFromLambda is the TOF n-sigma for the proton track from the Lambda decay with proton hypothesis + /// \param hasTofBachelorPi is true if the Xi bachelor track has TOF information, otherwise false + /// \param hasTofPiFromLambda is true if the pion track from the Lambda decay has TOF information, otherwise false + /// \param hasTofPrFromLambda is true if the proton track from the Lambda decay has TOF information, otherwise false + /// \param nClsTpc is the array with the number of TPC clusters for each track + /// \param nCrossedRowsTpc is the array with the number of crossed rows in the TPC for each track + /// \param crossedRowsOverFindableClsTpc is the array with the ratio of crossed rows over findable clusters in the TPC for each track + /// \return true if the candidate passes the software trigger selections, otherwise false + template + bool isSelectedXicSoftwareTriggers(std::array const& pVecCascade, std::array const& trackParBachelor, Coll const& collision, float nSigTpcBachelorPi, float nSigTofBachelorPi, float nSigTpcPiFromLambda, float nSigTofPiFromLambda, float nSigTpcPrFromLambda, float nSigTofPrFromLambda, bool hasTofBachelorPi, bool hasTofPiFromLambda, bool hasTofPrFromLambda, const std::array& nClsTpc, const std::array& nCrossedRowsTpc, const std::array& crossedRowsOverFindableClsTpc) + { + constexpr int16_t NumClsTpcMin = 70; + constexpr int16_t NumCrossedRowsTpcMin = 70; + constexpr float CrossedRowsOverFindableClsTpcMin = 0.8f; + constexpr int NumTracks = 3; + for (int iTrack{0}; iTrack < NumTracks; ++iTrack) { + if (nClsTpc[iTrack] < NumClsTpcMin || nCrossedRowsTpc[iTrack] < NumCrossedRowsTpcMin || crossedRowsOverFindableClsTpc[iTrack] < CrossedRowsOverFindableClsTpcMin) { + return false; + } + } + + if (std::abs(nSigTpcBachelorPi) > softTrigCuts.maxNsigmaXiDaus || (hasTofBachelorPi && std::abs(nSigTofBachelorPi) > softTrigCuts.maxNsigmaXiDaus)) { + return false; + } + if (std::abs(nSigTpcPiFromLambda) > softTrigCuts.maxNsigmaXiDaus || (hasTofPiFromLambda && std::abs(nSigTofPiFromLambda) > softTrigCuts.maxNsigmaXiDaus)) { + return false; + } + if (std::abs(nSigTpcPrFromLambda) > softTrigCuts.maxNsigmaXiDaus || (hasTofPrFromLambda && std::abs(nSigTofPrFromLambda) > softTrigCuts.maxNsigmaXiDaus)) { + return false; + } + + int nCand{0}; + try { + nCand = df2Prong.process(trackParBachelor[0], trackParBachelor[1]); + } catch (...) { + LOG(error) << "Exception caught in DCA fitter process call for bachelor + bachelor in software trigger selection function!"; + return false; + } + if (nCand == 0) { + return false; + } + + const auto& vtx = df2Prong.getPCACandidate(); + if (df2Prong.getChi2AtPCACandidate() > softTrigCuts.maxChi2Pca) { + return false; + } + + std::array pVecBachFirst{}, pVecBachSecond{}; + const auto& trackBachFirstProp = df2Prong.getTrack(0); + const auto& trackBachSecondProp = df2Prong.getTrack(1); + trackBachFirstProp.getPxPyPzGlo(pVecBachFirst); + trackBachSecondProp.getPxPyPzGlo(pVecBachSecond); + auto momXiBachBach = RecoDecay::pVec(pVecCascade, pVecBachFirst, pVecBachSecond); + + std::array primVtx = {collision.posX(), collision.posY(), collision.posZ()}; + if (RecoDecay::cpa(primVtx, std::array{vtx[0], vtx[1], vtx[2]}, momXiBachBach) < softTrigCuts.minCosPA) { + return false; + } + + if (RecoDecay::distance(primVtx, vtx) < softTrigCuts.minDecayLength) { + return false; + } + + return true; } template @@ -203,7 +307,7 @@ struct HfCandidateCreatorXicToXiPiPi { aod::HfCascLf3Prongs const& rowsTrackIndexXicPlus, CascadesLinked const&, CascFull const&, - TracksWCovDcaPidPrPi const&, + TracksWCovExtraPidPrPi const&, aod::BCsWithTimestamps const&) { // loop over triplets of track indices @@ -226,8 +330,8 @@ struct HfCandidateCreatorXicToXiPiPi { continue; } auto casc = cascAodElement.cascData_as(); - auto trackCharmBachelor0 = rowTrackIndexXicPlus.prong0_as(); - auto trackCharmBachelor1 = rowTrackIndexXicPlus.prong1_as(); + auto trackCharmBachelor0 = rowTrackIndexXicPlus.prong0_as(); + auto trackCharmBachelor1 = rowTrackIndexXicPlus.prong1_as(); // preselect cascade candidates if (doCascadePreselection) { @@ -278,10 +382,51 @@ struct HfCandidateCreatorXicToXiPiPi { trackCasc.setAbsCharge(1); trackCasc.setPID(o2::track::PID::XiMinus); + //----------------------------calculate physical properties----------------------- + // Charge of charm baryon + int8_t const signXic = casc.sign() < 0 ? +1 : -1; + //----------------------------fit SV and create XicPlus track------------------ auto trackParCovCharmBachelor0 = getTrackParCov(trackCharmBachelor0); auto trackParCovCharmBachelor1 = getTrackParCov(trackCharmBachelor1); + // if enabled, apply selections of software trigger + float pPiFromLambda{}, pPrFromLambda{}, pPionFromXi{}, ptPionFromXi{}, nSigTpcBachelorPi{}, nSigTofBachelorPi{}, nSigTpcPiFromLambda{}, nSigTofPiFromLambda{}, nSigTpcPrFromLambda{}, nSigTofPrFromLambda{}; + if (softTrigCuts.applySoftwareTrigSelections) { + // get PID information already here + auto const& trackPionFromXi = casc.bachelor_as(); + auto const& trackPosLambdaDaughter = casc.posTrack_as(); + auto const& trackNegLambdaDaughter = casc.negTrack_as(); + nSigTpcBachelorPi = trackPionFromXi.tpcNSigmaPi(); + nSigTofBachelorPi = trackPionFromXi.tofNSigmaPi(); + pPionFromXi = trackPionFromXi.p(); + ptPionFromXi = trackPionFromXi.pt(); + bool hasTofBachelorPi = trackPionFromXi.hasTOF(); + bool hasTofPiFromLambda = false; + bool hasTofPrFromLambda = false; + if (signXic == +1) { + pPiFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPiFromLambda = trackNegLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackNegLambdaDaughter.tofNSigmaPi(); + pPrFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPrFromLambda = trackPosLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackPosLambdaDaughter.tofNSigmaPr(); + } else { + pPiFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPiFromLambda = trackPosLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackPosLambdaDaughter.tofNSigmaPi(); + pPrFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPrFromLambda = trackNegLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackNegLambdaDaughter.tofNSigmaPr(); + } + std::array const nClsTpc = {trackPionFromXi.tpcNClsFound(), trackPosLambdaDaughter.tpcNClsFound(), trackNegLambdaDaughter.tpcNClsFound()}; + std::array const nCrossedRowsTpc = {trackPionFromXi.tpcNClsCrossedRows(), trackPosLambdaDaughter.tpcNClsCrossedRows(), trackNegLambdaDaughter.tpcNClsCrossedRows()}; + std::array const crossedRowsOverFindableClsTpc = {trackPionFromXi.tpcCrossedRowsOverFindableCls(), trackPosLambdaDaughter.tpcCrossedRowsOverFindableCls(), trackNegLambdaDaughter.tpcCrossedRowsOverFindableCls()}; + if (!isSelectedXicSoftwareTriggers(pVecCasc, std::array{trackParCovCharmBachelor0, trackParCovCharmBachelor1}, collision, nSigTpcBachelorPi, nSigTofBachelorPi, nSigTpcPiFromLambda, nSigTofPiFromLambda, nSigTpcPrFromLambda, nSigTofPrFromLambda, hasTofBachelorPi, hasTofPiFromLambda, hasTofPrFromLambda, nClsTpc, nCrossedRowsTpc, crossedRowsOverFindableClsTpc)) { + continue; + } + } + // reconstruct the 3-prong secondary vertex try { if (df.process(trackCasc, trackParCovCharmBachelor0, trackParCovCharmBachelor1) == 0) { @@ -293,10 +438,6 @@ struct HfCandidateCreatorXicToXiPiPi { } registry.fill(HIST("hCandCounter"), VertexFit); - //----------------------------calculate physical properties----------------------- - // Charge of charm baryon - int8_t const signXic = casc.sign() < 0 ? +1 : -1; - // get SV properties const auto& secondaryVertex = df.getPCACandidate(); auto chi2SV = df.getChi2AtPCACandidate(); @@ -338,6 +479,9 @@ struct HfCandidateCreatorXicToXiPiPi { float const cpaLambdaToXi = RecoDecay::cpa(vertexCasc, vertexV0, pVecV0); float const cpaXYLambdaToXi = RecoDecay::cpaXY(vertexCasc, vertexV0, pVecV0); + // calculate Lambda radius + float const radiusLambda = std::hypot(vertexV0[0], vertexV0[1]); + // get invariant mass of Xi-pi pairs auto arrayMomentaXiPi0 = std::array{pVecXi, pVecPi0}; massXiPi0 = RecoDecay::m(arrayMomentaXiPi0, std::array{MassXiMinus, MassPiPlus}); @@ -355,28 +499,31 @@ struct HfCandidateCreatorXicToXiPiPi { float const nSigTofPiFromXicPlus0 = trackCharmBachelor0.tofNSigmaPi(); float const nSigTpcPiFromXicPlus1 = trackCharmBachelor1.tpcNSigmaPi(); float const nSigTofPiFromXicPlus1 = trackCharmBachelor1.tofNSigmaPi(); - // Bachelor pion - auto trackPionFromXi = casc.bachelor_as(); - float const nSigTpcBachelorPi = trackPionFromXi.tpcNSigmaPi(); - float const nSigTofBachelorPi = trackPionFromXi.tofNSigmaPi(); - // Lambda daughters - auto trackPosLambdaDaughter = casc.posTrack_as(); - auto trackNegLambdaDaughter = casc.negTrack_as(); - float pPiFromLambda{}, pPrFromLambda{}, nSigTpcPiFromLambda{}, nSigTofPiFromLambda{}, nSigTpcPrFromLambda{}, nSigTofPrFromLambda{}; - if (signXic == +1) { - pPiFromLambda = trackNegLambdaDaughter.p(); - nSigTpcPiFromLambda = trackNegLambdaDaughter.tpcNSigmaPi(); - nSigTofPiFromLambda = trackNegLambdaDaughter.tofNSigmaPi(); - pPrFromLambda = trackPosLambdaDaughter.p(); - nSigTpcPrFromLambda = trackPosLambdaDaughter.tpcNSigmaPr(); - nSigTofPrFromLambda = trackPosLambdaDaughter.tofNSigmaPr(); - } else { - pPiFromLambda = trackPosLambdaDaughter.p(); - nSigTpcPiFromLambda = trackPosLambdaDaughter.tpcNSigmaPi(); - nSigTofPiFromLambda = trackPosLambdaDaughter.tofNSigmaPi(); - pPrFromLambda = trackNegLambdaDaughter.p(); - nSigTpcPrFromLambda = trackNegLambdaDaughter.tpcNSigmaPr(); - nSigTofPrFromLambda = trackNegLambdaDaughter.tofNSigmaPr(); + if (!softTrigCuts.applySoftwareTrigSelections) { + // Bachelor pion + auto trackPionFromXi = casc.bachelor_as(); + nSigTpcBachelorPi = trackPionFromXi.tpcNSigmaPi(); + nSigTofBachelorPi = trackPionFromXi.tofNSigmaPi(); + pPionFromXi = trackPionFromXi.p(); + ptPionFromXi = trackPionFromXi.pt(); + // Lambda daughters + auto trackPosLambdaDaughter = casc.posTrack_as(); + auto trackNegLambdaDaughter = casc.negTrack_as(); + if (signXic == +1) { + pPiFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPiFromLambda = trackNegLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackNegLambdaDaughter.tofNSigmaPi(); + pPrFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPrFromLambda = trackPosLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackPosLambdaDaughter.tofNSigmaPr(); + } else { + pPiFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPiFromLambda = trackPosLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackPosLambdaDaughter.tofNSigmaPi(); + pPrFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPrFromLambda = trackNegLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackNegLambdaDaughter.tofNSigmaPr(); + } } //--------------------------------------------fill histograms---------------------------------------------------------------- @@ -419,9 +566,10 @@ struct HfCandidateCreatorXicToXiPiPi { impactParameterCasc.getY(), impactParameter0.getY(), impactParameter1.getY(), std::sqrt(impactParameterCasc.getSigmaY2()), std::sqrt(impactParameter0.getSigmaY2()), std::sqrt(impactParameter1.getSigmaY2()), /*cascade specific columns*/ - trackPionFromXi.p(), pPiFromLambda, pPrFromLambda, + pPionFromXi, pPiFromLambda, pPrFromLambda, ptPionFromXi, cpaXi, cpaXYXi, cpaLambda, cpaXYLambda, cpaLambdaToXi, cpaXYLambdaToXi, casc.mXi(), casc.mLambda(), massXiPi0, massXiPi1, + radiusLambda, /*DCA information*/ casc.dcacascdaughters(), casc.dcaV0daughters(), casc.dcapostopv(), casc.dcanegtopv(), casc.dcabachtopv(), casc.dcaXYCascToPV(), casc.dcaZCascToPV(), @@ -459,8 +607,56 @@ struct HfCandidateCreatorXicToXiPiPi { continue; } auto casc = cascAodElement.kfCascData_as(); - auto trackCharmBachelor0 = rowTrackIndexXicPlus.prong0_as(); - auto trackCharmBachelor1 = rowTrackIndexXicPlus.prong1_as(); + + auto const& trackCharmBachelor0 = rowTrackIndexXicPlus.prong0_as(); + auto const& trackCharmBachelor1 = rowTrackIndexXicPlus.prong1_as(); + + // sign of charm baryon + int8_t const signXic = casc.sign() < 0 ? +1 : -1; + + // if enabled, apply selections of software trigger + float pPiFromLambda{}, pPrFromLambda{}, pPionFromXi{}, ptPionFromXi{}, nSigTpcBachelorPi{}, nSigTofBachelorPi{}, nSigTpcPiFromLambda{}, nSigTofPiFromLambda{}, nSigTpcPrFromLambda{}, nSigTofPrFromLambda{}; + if (softTrigCuts.applySoftwareTrigSelections) { + // get PID information already here + auto const& trackPionFromXi = casc.bachelor_as(); + auto const& trackPosLambdaDaughter = casc.posTrack_as(); + auto const& trackNegLambdaDaughter = casc.negTrack_as(); + nSigTpcBachelorPi = trackPionFromXi.tpcNSigmaPi(); + nSigTofBachelorPi = trackPionFromXi.tofNSigmaPi(); + pPionFromXi = trackPionFromXi.p(); + ptPionFromXi = trackPionFromXi.pt(); + bool hasTofBachelorPi = trackPionFromXi.hasTOF(); + bool hasTofPiFromLambda = false; + bool hasTofPrFromLambda = false; + if (signXic == +1) { + pPiFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPiFromLambda = trackNegLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackNegLambdaDaughter.tofNSigmaPi(); + hasTofPiFromLambda = trackNegLambdaDaughter.hasTOF(); + pPrFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPrFromLambda = trackPosLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackPosLambdaDaughter.tofNSigmaPr(); + hasTofPrFromLambda = trackPosLambdaDaughter.hasTOF(); + } else { + pPiFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPiFromLambda = trackPosLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackPosLambdaDaughter.tofNSigmaPi(); + hasTofPiFromLambda = trackPosLambdaDaughter.hasTOF(); + pPrFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPrFromLambda = trackNegLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackNegLambdaDaughter.tofNSigmaPr(); + hasTofPrFromLambda = trackNegLambdaDaughter.hasTOF(); + } + auto trackParCovCharmBachelor0 = getTrackParCov(trackCharmBachelor0); + auto trackParCovCharmBachelor1 = getTrackParCov(trackCharmBachelor1); + std::array const pVecCasc = {casc.px(), casc.py(), casc.pz()}; + std::array const nClsTpc = {trackPionFromXi.tpcNClsFound(), trackPosLambdaDaughter.tpcNClsFound(), trackNegLambdaDaughter.tpcNClsFound()}; + std::array const nCrossedRowsTpc = {trackPionFromXi.tpcNClsCrossedRows(), trackPosLambdaDaughter.tpcNClsCrossedRows(), trackNegLambdaDaughter.tpcNClsCrossedRows()}; + std::array const crossedRowsOverFindableClsTpc = {trackPionFromXi.tpcCrossedRowsOverFindableCls(), trackPosLambdaDaughter.tpcCrossedRowsOverFindableCls(), trackNegLambdaDaughter.tpcCrossedRowsOverFindableCls()}; + if (!isSelectedXicSoftwareTriggers(pVecCasc, std::array{trackParCovCharmBachelor0, trackParCovCharmBachelor1}, collision, nSigTpcBachelorPi, nSigTofBachelorPi, nSigTpcPiFromLambda, nSigTofPiFromLambda, nSigTpcPrFromLambda, nSigTofPrFromLambda, hasTofBachelorPi, hasTofPiFromLambda, hasTofPrFromLambda, nClsTpc, nCrossedRowsTpc, crossedRowsOverFindableClsTpc)) { + continue; + } + } //-------------------preselect cascade candidates-------------------------------------- if (doCascadePreselection) { @@ -511,6 +707,10 @@ struct HfCandidateCreatorXicToXiPiPi { float parPosMom[NElementsStateVector]; std::copy(xyzpxpypz.begin(), xyzpxpypz.end(), parPosMom); // create KFParticle + // README: The Xi KFParticle in this case is constructed from the parameters stored in the LF cascade table. + // The LF strangenessbuilder should in this case be run WITHOUT the mass constraint on the Xi to consistently store the unconstrained parameters and invariant mass of the Xi. + // The mass constraint on the Xi is applied here only after the KFParticle object was created from the unconstrained parameters and covariance matrix of the Xi. + // WARNING: If the Xi gets already constrained in the LF strangenessbuilder, the parameters and cov matrix passed to the KFParticle.Create() function will be constrained while the mass won't be. The stored energy, mass, and momentum will not be correctly related anymore! KFParticle kfXi; float const massXi = casc.mXi(); kfXi.Create(parPosMom, casc.kfTrackCovMat(), casc.sign(), massXi); @@ -547,9 +747,6 @@ struct HfCandidateCreatorXicToXiPiPi { } //---------------------calculate physical parameters of XicPlus candidate---------------------- - // sign of charm baryon - int8_t const signXic = casc.sign() < 0 ? +1 : -1; - // transport XicPlus daughters to XicPlus decay vertex (secondary vertex) float secondaryVertex[3] = {0.}; secondaryVertex[0] = kfXicPlus.GetX(); @@ -576,6 +773,9 @@ struct HfCandidateCreatorXicToXiPiPi { float const cpaLambdaToXi = RecoDecay::cpa(vertexCasc, vertexV0, pVecV0); float const cpaXYLambdaToXi = RecoDecay::cpaXY(vertexCasc, vertexV0, pVecV0); + // calculate Lambda radius + float const radiusLambda = std::hypot(vertexV0[0], vertexV0[1]); + // get chi2 deviation of Pi0-Pi1, Pi0-Xi, Pi1-Xi float chi2DevPi0Pi1 = kfCharmBachelor0.GetDeviationFromParticle(kfCharmBachelor1); float chi2DevPi0Xi = kfCharmBachelor0.GetDeviationFromParticle(kfXi); @@ -630,28 +830,31 @@ struct HfCandidateCreatorXicToXiPiPi { float const nSigTofPiFromXicPlus0 = trackCharmBachelor0.tofNSigmaPi(); float const nSigTpcPiFromXicPlus1 = trackCharmBachelor1.tpcNSigmaPi(); float const nSigTofPiFromXicPlus1 = trackCharmBachelor1.tofNSigmaPi(); - // Bachelor pion - auto trackPionFromXi = casc.bachelor_as(); - float const nSigTpcBachelorPi = trackPionFromXi.tpcNSigmaPi(); - float const nSigTofBachelorPi = trackPionFromXi.tofNSigmaPi(); - // Lambda daughters - auto trackPosLambdaDaughter = casc.posTrack_as(); - auto trackNegLambdaDaughter = casc.negTrack_as(); - float pPiFromLambda{}, pPrFromLambda{}, nSigTpcPiFromLambda{}, nSigTofPiFromLambda{}, nSigTpcPrFromLambda{}, nSigTofPrFromLambda{}; - if (signXic == +1) { - pPiFromLambda = trackNegLambdaDaughter.p(); - nSigTpcPiFromLambda = trackNegLambdaDaughter.tpcNSigmaPi(); - nSigTofPiFromLambda = trackNegLambdaDaughter.tofNSigmaPi(); - pPrFromLambda = trackPosLambdaDaughter.p(); - nSigTpcPrFromLambda = trackPosLambdaDaughter.tpcNSigmaPr(); - nSigTofPrFromLambda = trackPosLambdaDaughter.tofNSigmaPr(); - } else { - pPiFromLambda = trackPosLambdaDaughter.p(); - nSigTpcPiFromLambda = trackPosLambdaDaughter.tpcNSigmaPi(); - nSigTofPiFromLambda = trackPosLambdaDaughter.tofNSigmaPi(); - pPrFromLambda = trackNegLambdaDaughter.p(); - nSigTpcPrFromLambda = trackNegLambdaDaughter.tpcNSigmaPr(); - nSigTofPrFromLambda = trackNegLambdaDaughter.tofNSigmaPr(); + if (!softTrigCuts.applySoftwareTrigSelections) { + // Bachelor pion + auto trackPionFromXi = casc.bachelor_as(); + nSigTpcBachelorPi = trackPionFromXi.tpcNSigmaPi(); + nSigTofBachelorPi = trackPionFromXi.tofNSigmaPi(); + pPionFromXi = trackPionFromXi.p(); + ptPionFromXi = trackPionFromXi.pt(); + // Lambda daughters + auto trackPosLambdaDaughter = casc.posTrack_as(); + auto trackNegLambdaDaughter = casc.negTrack_as(); + if (signXic == +1) { + pPiFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPiFromLambda = trackNegLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackNegLambdaDaughter.tofNSigmaPi(); + pPrFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPrFromLambda = trackPosLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackPosLambdaDaughter.tofNSigmaPr(); + } else { + pPiFromLambda = trackPosLambdaDaughter.p(); + nSigTpcPiFromLambda = trackPosLambdaDaughter.tpcNSigmaPi(); + nSigTofPiFromLambda = trackPosLambdaDaughter.tofNSigmaPi(); + pPrFromLambda = trackNegLambdaDaughter.p(); + nSigTpcPrFromLambda = trackNegLambdaDaughter.tpcNSigmaPr(); + nSigTofPrFromLambda = trackNegLambdaDaughter.tofNSigmaPr(); + } } //-------------------------------fill histograms-------------------------------------------- @@ -692,9 +895,10 @@ struct HfCandidateCreatorXicToXiPiPi { impactParameterXiXY, impactParameterPi0XY, impactParameterPi1XY, errImpactParameterXiXY, errImpactParameterPi0XY, errImpactParameterPi1XY, /*cascade specific columns*/ - trackPionFromXi.p(), pPiFromLambda, pPrFromLambda, + pPionFromXi, pPiFromLambda, pPrFromLambda, ptPionFromXi, cpaXi, cpaXYXi, cpaLambda, cpaXYLambda, cpaLambdaToXi, cpaXYLambdaToXi, massXi, casc.mLambda(), massXiPi0, massXiPi1, + radiusLambda, /*DCA information*/ casc.dcacascdaughters(), casc.dcaV0daughters(), casc.dcapostopv(), casc.dcanegtopv(), casc.dcabachtopv(), casc.dcaXYCascToPV(), casc.dcaZCascToPV(), @@ -717,33 +921,33 @@ struct HfCandidateCreatorXicToXiPiPi { /// /// /////////////////////////////////////////////////////////// - void processNoCentXicplusWithDcaFitter(soa::Join const& collisions, + void processNoCentXicplusWithDcaFitter(soa::Join const& collisions, aod::HfCascLf3Prongs const& rowsTrackIndexXicPlus, CascadesLinked const& cascadesLinked, CascFull const& cascadesFull, - TracksWCovDcaPidPrPi const& tracks, + TracksWCovExtraPidPrPi const& tracks, aod::BCsWithTimestamps const& bcs) { runXicplusCreatorWithDcaFitter(collisions, rowsTrackIndexXicPlus, cascadesLinked, cascadesFull, tracks, bcs); } PROCESS_SWITCH(HfCandidateCreatorXicToXiPiPi, processNoCentXicplusWithDcaFitter, "Run candidate creator with DCAFitter without centrality selection.", true); - void processCentFT0CXicplusWithDcaFitter(soa::Join const& collisions, + void processCentFT0CXicplusWithDcaFitter(soa::Join const& collisions, aod::HfCascLf3Prongs const& rowsTrackIndexXicPlus, CascadesLinked const& cascadesLinked, CascFull const& cascadesFull, - TracksWCovDcaPidPrPi const& tracks, + TracksWCovExtraPidPrPi const& tracks, aod::BCsWithTimestamps const& bcs) { runXicplusCreatorWithDcaFitter(collisions, rowsTrackIndexXicPlus, cascadesLinked, cascadesFull, tracks, bcs); } PROCESS_SWITCH(HfCandidateCreatorXicToXiPiPi, processCentFT0CXicplusWithDcaFitter, "Run candidate creator with DCAFitter with centrality selection on FT0C.", false); - void processCentFT0MXicplusWithDcaFitter(soa::Join const& collisions, + void processCentFT0MXicplusWithDcaFitter(soa::Join const& collisions, aod::HfCascLf3Prongs const& rowsTrackIndexXicPlus, CascadesLinked const& cascadesLinked, CascFull const& cascadesFull, - TracksWCovDcaPidPrPi const& tracks, + TracksWCovExtraPidPrPi const& tracks, aod::BCsWithTimestamps const& bcs) { runXicplusCreatorWithDcaFitter(collisions, rowsTrackIndexXicPlus, cascadesLinked, cascadesFull, tracks, bcs); @@ -756,7 +960,7 @@ struct HfCandidateCreatorXicToXiPiPi { /// /// /////////////////////////////////////////////////////////// - void processNoCentXicplusWithKFParticle(soa::Join const& collisions, + void processNoCentXicplusWithKFParticle(soa::Join const& collisions, aod::HfCascLf3Prongs const& rowsTrackIndexXicPlus, KFCascadesLinked const& kfCascadesLinked, KFCascFull const& kfCascadesFull, @@ -767,7 +971,7 @@ struct HfCandidateCreatorXicToXiPiPi { } PROCESS_SWITCH(HfCandidateCreatorXicToXiPiPi, processNoCentXicplusWithKFParticle, "Run candidate creator with KFParticle without centrality selection.", false); - void processCentFT0CXicplusWithKFParticle(soa::Join const& collisions, + void processCentFT0CXicplusWithKFParticle(soa::Join const& collisions, aod::HfCascLf3Prongs const& rowsTrackIndexXicPlus, KFCascadesLinked const& kfCascadesLinked, KFCascFull const& kfCascadesFull, @@ -778,7 +982,7 @@ struct HfCandidateCreatorXicToXiPiPi { } PROCESS_SWITCH(HfCandidateCreatorXicToXiPiPi, processCentFT0CXicplusWithKFParticle, "Run candidate creator with KFParticle with centrality selection on FT0C.", false); - void processCentFT0MXicplusWithKFParticle(soa::Join const& collisions, + void processCentFT0MXicplusWithKFParticle(soa::Join const& collisions, aod::HfCascLf3Prongs const& rowsTrackIndexXicPlus, KFCascadesLinked const& kfCascadesLinked, KFCascFull const& kfCascadesFull, @@ -795,7 +999,7 @@ struct HfCandidateCreatorXicToXiPiPi { /// /// /////////////////////////////////////////////////////////// - void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const&) + void processCollisions(soa::Join const& collisions, aod::BCsWithTimestamps const&) { /// loop over collisions for (const auto& collision : collisions) { @@ -813,7 +1017,7 @@ struct HfCandidateCreatorXicToXiPiPi { } PROCESS_SWITCH(HfCandidateCreatorXicToXiPiPi, processCollisions, "Collision monitoring - no centrality", false); - void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const&) + void processCollisionsCentFT0C(soa::Join const& collisions, aod::BCsWithTimestamps const&) { /// loop over collisions for (const auto& collision : collisions) { @@ -831,7 +1035,7 @@ struct HfCandidateCreatorXicToXiPiPi { } PROCESS_SWITCH(HfCandidateCreatorXicToXiPiPi, processCollisionsCentFT0C, "Collision monitoring - FT0C centrality", false); - void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const&) + void processCollisionsCentFT0M(soa::Join const& collisions, aod::BCsWithTimestamps const&) { /// loop over collisions for (const auto& collision : collisions) { @@ -869,9 +1073,9 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { XiToPiPPi, LambdaToPPi }; - using McCollisionsNoCents = soa::Join; - using McCollisionsFT0Cs = soa::Join; - using McCollisionsFT0Ms = soa::Join; + using McCollisionsNoCents = soa::Join; + using McCollisionsFT0Cs = soa::Join; + using McCollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; using BCsInfo = soa::Join; @@ -889,6 +1093,7 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { registry.add("hDecayedPions", "hDecayedPions", {HistType::kTH1F, {{5, -0.5, 4.5}}}); registry.add("hInteractionsWithMaterial", "hInteractionsWithMaterial", {HistType::kTH1F, {{6, -0.5, 5.5}}}); registry.add("hDebugRec", "hDebugRec", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("hy_pt_gen", "Generated MC", {HistType::kTH2F, {{20, -1.0, 1.0}, {200, 0, 20}}}); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + TotalRec, "total"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XicToFinalState, "#Xi^{+}_{c} #rightarrow #pi^{#plus} #pi^{#plus} #pi^{#minus} p #pi^{#minus}"); registry.get(HIST("hDebugRec"))->GetXaxis()->SetBinLabel(1 + XiToPiPPi, "#Xi^{#minus} #rightarrow #pi^{#minus} p #pi^{#minus}"); @@ -1096,12 +1301,13 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { if (rejectionMask != 0) { // at least one event selection not satisfied --> reject all particles from this collision for (unsigned int i = 0; i < mcParticlesPerMcColl.size(); ++i) { - rowMcMatchGen(-99, -99, -99, decayLengthGen); + rowMcMatchGen(-99, -99, -99, decayLengthGen, -1.f); } continue; } for (const auto& particle : mcParticlesPerMcColl) { + const float massGen = RecoDecay::m(particle.pVector(), particle.e()); sign = 0; flag = 0; origin = RecoDecay::OriginType::None; @@ -1141,6 +1347,9 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Check whether the charm baryon is non-prompt (from a b quark). if (flag != 0) { + // fill inclusive generated Xic+ + registry.fill(HIST("hy_pt_gen"), particle.y(), particle.pt()); + origin = RecoDecay::getCharmHadronOrigin(mcParticles, particle, false, &idxBhadMothers); // Calculate the decay length of the generated particle auto dau0 = particle.template daughters_as().begin(); @@ -1151,9 +1360,9 @@ struct HfCandidateCreatorXicToXiPiPiExpressions { // Fill table if (origin == RecoDecay::OriginType::NonPrompt) { auto bHadMother = mcParticles.rawIteratorAt(idxBhadMothers[0]); - rowMcMatchGen(flag, origin, bHadMother.pdgCode(), decayLengthGen); + rowMcMatchGen(flag, origin, bHadMother.pdgCode(), decayLengthGen, massGen); } else { - rowMcMatchGen(flag, origin, 0, decayLengthGen); + rowMcMatchGen(flag, origin, 0, decayLengthGen, massGen); } } // close loop over generated particles } // close loop over McCollisions diff --git a/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx b/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx index c083d530baf..358df7a0326 100644 --- a/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorB0ToDPi.cxx @@ -136,7 +136,7 @@ struct HfCandidateSelectorB0ToDPi { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx b/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx index 7b51a915c4c..9c538f544fe 100644 --- a/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx +++ b/PWGHF/TableProducer/candidateSelectorBplusToD0Pi.cxx @@ -139,7 +139,7 @@ struct HfCandidateSelectorBplusToD0Pi { labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx b/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx index eee6a06b93f..376d485bffd 100644 --- a/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorBsToDsPi.cxx @@ -120,7 +120,7 @@ struct HfCandidateSelectorBsToDsPi { labels[1 + SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + SelectionStep::RecoPID] = "Skims & Topological & PID selections"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorCd.cxx b/PWGHF/TableProducer/candidateSelectorCd.cxx index 61c662ada3e..1fcbdbcfedf 100644 --- a/PWGHF/TableProducer/candidateSelectorCd.cxx +++ b/PWGHF/TableProducer/candidateSelectorCd.cxx @@ -71,10 +71,11 @@ struct HfCandidateSelectorCd { Configurable itsChi2PerClusterMax{"itsChi2PerClusterMax", 1e10f, "max its fit chi2 per ITS cluster"}; // DCA track cuts Configurable> binsPtTrack{"binsPtTrack", std::vector{hf_cuts_single_track::vecBinsPtTrack}, "track pT bin limits for DCA XY/Z pT-dependent cut"}; - Configurable> cutsSingleTrack{"cutsSingleTrack", {hf_cuts_single_track::CutsTrack[0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track selections"}; + Configurable> cutsSingleTrack{"cutsSingleTrack", {&hf_cuts_single_track::CutsTrack[0][0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track selections"}; // topological cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_cd_to_de_k_pi::vecBinsPt}, "pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_cd_to_de_k_pi::Cuts[0], hf_cuts_cd_to_de_k_pi::NBinsPt, hf_cuts_cd_to_de_k_pi::NCutVars, hf_cuts_cd_to_de_k_pi::labelsPt, hf_cuts_cd_to_de_k_pi::labelsCutVar}, "Cd candidate selection per pT bin"}; + Configurable> cuts{"cuts", {&hf_cuts_cd_to_de_k_pi::Cuts[0][0], hf_cuts_cd_to_de_k_pi::NBinsPt, hf_cuts_cd_to_de_k_pi::NCutVars, hf_cuts_cd_to_de_k_pi::labelsPt, hf_cuts_cd_to_de_k_pi::labelsCutVar}, "Cd candidate selection per pT bin"}; + Configurable acceptCandidatesWithoutCdFlag{"acceptCandidatesWithoutCdFlag", false, "Apply the Cd mass hypotheses also to 3-prong candidates without the Cd skim bit; intended for MC reflection studies"}; // QA switch Configurable activateQA{"activateQA", false, "Flag to enable QA histogram"}; @@ -102,15 +103,15 @@ struct HfCandidateSelectorCd { selectorDeuteron = selectorPion; if (activateQA) { - constexpr int kNBinsSelections = aod::SelectionStep::NSelectionSteps; - std::string labels[kNBinsSelections]; + constexpr int NBinsSelections = aod::SelectionStep::NSelectionSteps; + std::string labels[NBinsSelections]; labels[0] = "No selection"; labels[1 + aod::SelectionStep::RecoSkims] = "Skims selection"; labels[1 + aod::SelectionStep::RecoTopol] = "Skims & Topological selections"; labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; - static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); - for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { + static const AxisSpec axisSelections = {NBinsSelections, 0.5, NBinsSelections + 0.5, ""}; + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); + for (int iBin = 0; iBin < NBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } } @@ -264,7 +265,7 @@ struct HfCandidateSelectorCd { auto ptCand = candidate.pt(); - if (!(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::CdToDeKPi)) { + if (!acceptCandidatesWithoutCdFlag && !(candidate.hfflag() & 1 << aod::hf_cand_3prong::DecayType::CdToDeKPi)) { hfSelCdCandidate(statusCdToDeKPi, statusCdToPiKDe); if (activateQA) { registry.fill(HIST("hSelections"), 1, ptCand); @@ -314,24 +315,11 @@ struct HfCandidateSelectorCd { if (usePid) { // track-level PID selection - TrackSelectorPID::Status pidTrackPos1Deuteron; - TrackSelectorPID::Status pidTrackPos2Deuteron; - TrackSelectorPID::Status pidTrackPos1Pion; - TrackSelectorPID::Status pidTrackPos2Pion; - TrackSelectorPID::Status pidTrackNegKaon; - if (usePidTpcAndTof) { - pidTrackPos1Deuteron = selectorDeuteron.statusTpcAndTof(trackPos1, candidate.nSigTpcDe0(), candidate.nSigTofDe0()); - pidTrackPos2Deuteron = selectorDeuteron.statusTpcAndTof(trackPos2, candidate.nSigTpcDe2(), candidate.nSigTofDe2()); - pidTrackPos1Pion = selectorPion.statusTpcAndTof(trackPos1, candidate.nSigTpcPi0(), candidate.nSigTofPi0()); - pidTrackPos2Pion = selectorPion.statusTpcAndTof(trackPos2, candidate.nSigTpcPi2(), candidate.nSigTofPi2()); - pidTrackNegKaon = selectorKaon.statusTpcAndTof(trackNeg, candidate.nSigTpcKa1(), candidate.nSigTofKa1()); - } else { - pidTrackPos1Deuteron = selectorDeuteron.statusTpcOrTof(trackPos1, candidate.nSigTpcDe0(), candidate.nSigTofDe0()); - pidTrackPos2Deuteron = selectorDeuteron.statusTpcOrTof(trackPos2, candidate.nSigTpcDe2(), candidate.nSigTofDe2()); - pidTrackPos1Pion = selectorPion.statusTpcOrTof(trackPos1, candidate.nSigTpcPi0(), candidate.nSigTofPi0()); - pidTrackPos2Pion = selectorPion.statusTpcOrTof(trackPos2, candidate.nSigTpcPi2(), candidate.nSigTofPi2()); - pidTrackNegKaon = selectorKaon.statusTpcOrTof(trackNeg, candidate.nSigTpcKa1(), candidate.nSigTofKa1()); - } + const auto pidTrackPos1Deuteron = usePidTpcAndTof ? selectorDeuteron.statusTpcAndTof(trackPos1, candidate.nSigTpcDe0(), candidate.nSigTofDe0()) : selectorDeuteron.statusTpcOrTof(trackPos1, candidate.nSigTpcDe0(), candidate.nSigTofDe0()); + const auto pidTrackPos2Deuteron = usePidTpcAndTof ? selectorDeuteron.statusTpcAndTof(trackPos2, candidate.nSigTpcDe2(), candidate.nSigTofDe2()) : selectorDeuteron.statusTpcOrTof(trackPos2, candidate.nSigTpcDe2(), candidate.nSigTofDe2()); + const auto pidTrackPos1Pion = usePidTpcAndTof ? selectorPion.statusTpcAndTof(trackPos1, candidate.nSigTpcPi0(), candidate.nSigTofPi0()) : selectorPion.statusTpcOrTof(trackPos1, candidate.nSigTpcPi0(), candidate.nSigTofPi0()); + const auto pidTrackPos2Pion = usePidTpcAndTof ? selectorPion.statusTpcAndTof(trackPos2, candidate.nSigTpcPi2(), candidate.nSigTofPi2()) : selectorPion.statusTpcOrTof(trackPos2, candidate.nSigTpcPi2(), candidate.nSigTofPi2()); + const auto pidTrackNegKaon = usePidTpcAndTof ? selectorKaon.statusTpcAndTof(trackNeg, candidate.nSigTpcKa1(), candidate.nSigTofKa1()) : selectorKaon.statusTpcOrTof(trackNeg, candidate.nSigTpcKa1(), candidate.nSigTofKa1()); if (!isSelectedPID(pidTrackPos1Deuteron, pidTrackNegKaon, pidTrackPos2Pion)) { pidCdToDeKPi = 0; // reject CdToDeKPi diff --git a/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx b/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx index b66c7ac8540..53e26443b6c 100644 --- a/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDplusToPiKPi.cxx @@ -126,7 +126,7 @@ struct HfCandidateSelectorDplusToPiKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx b/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx index bbe41e275fe..153c19e3f6d 100644 --- a/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDsToKKPi.cxx @@ -127,7 +127,7 @@ struct HfCandidateSelectorDsToKKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx b/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx index 3e2ac92ec82..ee2310e4d34 100644 --- a/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx +++ b/PWGHF/TableProducer/candidateSelectorDstarToD0Pi.cxx @@ -69,6 +69,10 @@ struct HfCandidateSelectorDstarToD0Pi { Configurable> binsPtDstar{"binsPtDstar", std::vector{hf_cuts_dstar_to_d0_pi::vecBinsPt}, "pT bin limits for Dstar"}; Configurable> cutsDstar{"cutsDstar", {hf_cuts_dstar_to_d0_pi::Cuts[0], hf_cuts_dstar_to_d0_pi::NBinsPt, hf_cuts_dstar_to_d0_pi::NCutVars, hf_cuts_dstar_to_d0_pi::labelsPt, hf_cuts_dstar_to_d0_pi::labelsCutVar}, "Dstar candidate selection per pT bin"}; + // Single-track DCA selections + Configurable> cutsSingleTrack{"cutsSingleTrack", {hf_cuts_single_track::CutsTrack[0], hf_cuts_single_track::NBinsPtTrack, hf_cuts_single_track::NCutVarsTrack, hf_cuts_single_track::labelsPtTrack, hf_cuts_single_track::labelsCutVarTrack}, "Single-track selections"}; + Configurable> binsPtTrack{"binsPtTrack", std::vector{hf_cuts_single_track::vecBinsPtTrack}, "track pT bin limits for DCA pT-dependent cut"}; + // common Configurable // TPC PID Configurable ptPidTpcMin{"ptPidTpcMin", 0.15, "Minimum track pT for TPC PID"}; @@ -143,7 +147,7 @@ struct HfCandidateSelectorDstarToD0Pi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("QA/hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPtDstar, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("QA/hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPtDstar), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("QA/hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -168,6 +172,17 @@ struct HfCandidateSelectorDstarToD0Pi { } } + /// Single-track cuts + /// \param candidate is the Dstar candidate + /// \return true if all the prongs pass the selections + template + bool isSelectedCandidateProngDca(const T1& candidate) + { + // Applied only on D0 tracks, to mimic the skimming selections + return (isSelectedTrackDca(binsPtTrack, cutsSingleTrack, candidate.ptProng0(), candidate.impactParameter0(), candidate.impactParameterZ0()) && + isSelectedTrackDca(binsPtTrack, cutsSingleTrack, candidate.ptProng1(), candidate.impactParameter1(), candidate.impactParameterZ1())); + } + /// Conjugate-independent topological cuts on D0 /// @brief Topological selection on D0 candidate from Dstar /// @tparam T table iterator type of the candidate @@ -182,6 +197,10 @@ struct HfCandidateSelectorDstarToD0Pi { return false; } + if (!isSelectedCandidateProngDca(candidate)) { + return false; + } + // check that the candidate pT is within the analysis range if (candpT < ptD0CandMin || candpT >= ptD0CandMax) { return false; @@ -357,7 +376,6 @@ struct HfCandidateSelectorDstarToD0Pi { void process(TracksSel const&, HfFullDstarCandidate const& rowsDstarCand) { - // LOG(info) << "selector called"; for (const auto& candDstar : rowsDstarCand) { // final selection flag: false - rejected, true - accepted bool statusDstar = false, statusD0Flag = false, statusTopol = false, statusCand = false, statusPID = false; diff --git a/PWGHF/TableProducer/candidateSelectorLc.cxx b/PWGHF/TableProducer/candidateSelectorLc.cxx index 34b183aa1ce..5088a876513 100644 --- a/PWGHF/TableProducer/candidateSelectorLc.cxx +++ b/PWGHF/TableProducer/candidateSelectorLc.cxx @@ -24,6 +24,7 @@ #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" #include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" #include "Common/Core/TrackSelectorPID.h" #include "Common/DataModel/PIDResponseCombined.h" @@ -108,6 +109,8 @@ struct HfCandidateSelectorLc { Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_LcToPKPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + // Mass Cut for trigger analysis + Configurable useTriggerMassCut{"useTriggerMassCut", false, "Flag to enable parametrize pT differential mass cut for triggered data"}; o2::analysis::HfMlResponseLcToPKPi hfMlResponseDCA; o2::analysis::HfMlResponseLcToPKPi hfMlResponseKF; @@ -117,6 +120,7 @@ struct HfCandidateSelectorLc { TrackSelectorPi selectorPion; TrackSelectorKa selectorKaon; TrackSelectorPr selectorProton; + HfTrigger3ProngCuts hfTriggerCuts; using TracksSel = soa::Join; @@ -152,7 +156,7 @@ struct HfCandidateSelectorLc { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } @@ -327,6 +331,10 @@ struct HfCandidateSelectorLc { return false; } + if (useTriggerMassCut && !isCandidateInMassRange(massLc, o2::constants::physics::MassLambdaCPlus, candpT, hfTriggerCuts)) { + return false; + } + /// cut on the Kpi pair invariant mass, to study Lc->pK*(->Kpi) const double cutMassKPi = cuts->get(pTBin, "mass (Kpi)"); if (cutMassKPi > 0 && std::abs(massKPi - massK0Star892) > cutMassKPi) { diff --git a/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx b/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx index f27e5edc3cd..efccd81c0fd 100644 --- a/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx +++ b/PWGHF/TableProducer/candidateSelectorLcPidMl.cxx @@ -307,12 +307,12 @@ struct HfCandidateSelectorLcPidMl { std::vector inputFeaturesD{trackParPos1.getPt(), trackPos1.dcaXY(), trackPos1.dcaZ(), trackParNeg.getPt(), trackNeg.dcaXY(), trackNeg.dcaZ(), trackParPos2.getPt(), trackPos2.dcaXY(), trackPos2.dcaZ()}; float scores[3] = {-1.f, -1.f, -1.f}; if (dataTypeML == 1) { - auto* scoresRaw = model.evalModel(inputFeaturesF); + const auto scoresRaw = model.evalModel(inputFeaturesF); for (int iScore = 0; iScore < 3; ++iScore) { scores[iScore] = scoresRaw[iScore]; } } else if (dataTypeML == 11) { - auto* scoresRaw = model.evalModel(inputFeaturesD); + const auto scoresRaw = model.evalModel(inputFeaturesD); for (int iScore = 0; iScore < 3; ++iScore) { scores[iScore] = scoresRaw[iScore]; } diff --git a/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPi.cxx b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPi.cxx index 87856e1554a..59a17142176 100644 --- a/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPi.cxx @@ -763,6 +763,8 @@ struct HfCandidateSelectorToOmegaPi { isSelectedMlOmegac = hfMlResponse.isSelectedMl(inputFeaturesOmegaC, ptCand, outputMlOmegac); if (isSelectedMlOmegac) { registry.fill(HIST("hBDTScoreTest1"), outputMlOmegac[0]); + } else { + resultSelections = false; } hfMlSelToOmegaPi(outputMlOmegac); } diff --git a/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..2f3ff74b2fe --- /dev/null +++ b/PWGHF/TableProducer/candidateSelectorOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,887 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file candidateSelectorOmegac0ToOmegaPiQa.cxx +/// \brief Omegac0 → Omega Pi selection task + +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/HfMlResponseOmegacToOmegaPiQa.h" +#include "PWGHF/Core/SelectorCuts.h" +#include "PWGHF/DataModel/AliasTables.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/Utils/utilsAnalysis.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TrackSelectorPID.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::analysis; + +enum PidInfoStored { + PiFromLam = 0, + PrFromLam, + KaFromCasc, + PiFromCharm +}; + +enum { + doDcaFitter = 0, + doKfParticle +}; + +/// Struct for applying Omegac0 -> Omega pi selection cuts +struct HfCandidateSelectorToOmegaPiQa { + // DCAFitter and KFParticle + Produces hfSelToOmegaPi; + // ML selection - filled for both DCAFitter and KFParticle + Produces hfMlSelToOmegaPi; + + // cuts from SelectorCuts.h - pT dependent cuts + Configurable> binsPt{"binsPt", std::vector{hf_cuts_omegac_to_omega_pi::vecBinsPt}, "pT bin limits"}; + Configurable> cuts{"cuts", {hf_cuts_omegac_to_omega_pi::Cuts[0], hf_cuts_omegac_to_omega_pi::NBinsPt, hf_cuts_omegac_to_omega_pi::NCutVars, hf_cuts_omegac_to_omega_pi::labelsPt, hf_cuts_omegac_to_omega_pi::labelsCutVar}, "OmegaC0 candidate selection per pT bin"}; + + // ML inference + Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; + Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; + Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; + Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + + // CCDB configuration + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"EventFiltering/PWGHF/BDTOmegac0"}, "Paths of models on CCDB"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"ModelHandler_onnx_Omegac0ToOmegaPi.onnx"}, "ONNX file names for each pT bin (if not from CCDB full path)"}; + Configurable timestampCCDB{"timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + + // LF analysis selections + Configurable radiusCascMin{"radiusCascMin", 0.5, "Min cascade radius"}; + Configurable radiusV0Min{"radiusV0Min", 1.1, "Min V0 radius"}; + Configurable cosPAV0Min{"cosPAV0Min", 0.97, "Min value CosPA V0"}; + Configurable cosPACascMin{"cosPACascMin", 0.97, "Min value CosPA cascade"}; + Configurable dcaV0DauMax{"dcaV0DauMax", 1.0, "Max DCA V0 daughters"}; + Configurable dcaCascDauMax{"dcaCascDauMax", 1.0, "Max DCA cascade daughters"}; + Configurable dcaNegToPvMin{"dcaNegToPvMin", 0.06, "DCA Neg To PV"}; + Configurable dcaPosToPvMin{"dcaPosToPvMin", 0.06, "DCA Pos To PV"}; + Configurable dcaBachToPvMin{"dcaBachToPvMin", 0.04, "DCA Bach To PV"}; + Configurable applyTrkSelLf{"applyTrkSelLf", true, "Apply track selection for LF daughters"}; + + // Mass window + Configurable v0MassWindow{"v0MassWindow", 0.01, "V0 mass window"}; + Configurable cascadeMassWindow{"cascadeMassWindow", 0.01, "Cascade mass window"}; + Configurable invMassCharmBaryonMin{"invMassCharmBaryonMin", 2.3, "Lower limit invariant mass spectrum charm baryon"}; + Configurable invMassCharmBaryonMax{"invMassCharmBaryonMax", 3.1, "Upper limit invariant mass spectrum charm baryon"}; + + // kinematic selections + Configurable etaTrackCharmBachMax{"etaTrackCharmBachMax", 0.8, "Max absolute value of eta for charm baryon bachelor"}; + Configurable etaTrackLFDauMax{"etaTrackLFDauMax", 1.0, "Max absolute value of eta for V0 and cascade daughters"}; + Configurable ptKaFromCascMin{"ptKaFromCascMin", 0.15, "Min pT kaon <- casc"}; + + Configurable impactParameterXYPiFromCharmBaryonMin{"impactParameterXYPiFromCharmBaryonMin", 0., "Min dcaxy pi from charm baryon track to PV"}; + Configurable impactParameterXYPiFromCharmBaryonMax{"impactParameterXYPiFromCharmBaryonMax", 10., "Max dcaxy pi from charm baryon track to PV"}; + Configurable impactParameterXYCascMin{"impactParameterXYCascMin", 0., "Min dcaxy cascade track to PV"}; + Configurable impactParameterXYCascMax{"impactParameterXYCascMax", 10., "Max dcaxy cascade track to PV"}; + Configurable impactParameterZPiFromCharmBaryonMin{"impactParameterZPiFromCharmBaryonMin", 0., "Min dcaz pi from charm baryon track to PV"}; + Configurable impactParameterZPiFromCharmBaryonMax{"impactParameterZPiFromCharmBaryonMax", 10., "Max dcaz pi from charm baryon track to PV"}; + Configurable impactParameterZCascMin{"impactParameterZCascMin", 0., "Min dcaz cascade track to PV"}; + Configurable impactParameterZCascMax{"impactParameterZCascMax", 10., "Max dcaz cascade track to PV"}; + + Configurable ptCandMin{"ptCandMin", 0., "Lower bound of candidate pT"}; + Configurable ptCandMax{"ptCandMax", 50., "Upper bound of candidate pT"}; + + Configurable dcaCharmBaryonDauMax{"dcaCharmBaryonDauMax", 2.0, "Max DCA charm baryon daughters"}; + + // PID options + Configurable usePidTpcOnly{"usePidTpcOnly", true, "Perform PID using only TPC"}; + Configurable usePidTpcTofCombined{"usePidTpcTofCombined", false, "Perform PID using TPC or TOF"}; + + // PID - TPC selections + Configurable ptPiPidTpcMin{"ptPiPidTpcMin", -1, "Lower bound of track pT for TPC PID for pion selection"}; + Configurable ptPiPidTpcMax{"ptPiPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for pion selection"}; + Configurable nSigmaTpcPiMax{"nSigmaTpcPiMax", 3., "Nsigma cut on TPC only for pion selection"}; + Configurable nSigmaTpcCombinedPiMax{"nSigmaTpcCombinedPiMax", 0., "Nsigma cut on TPC combined with TOF for pion selection"}; + + Configurable ptPrPidTpcMin{"ptPrPidTpcMin", -1, "Lower bound of track pT for TPC PID for proton selection"}; + Configurable ptPrPidTpcMax{"ptPrPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for proton selection"}; + Configurable nSigmaTpcPrMax{"nSigmaTpcPrMax", 3., "Nsigma cut on TPC only for proton selection"}; + Configurable nSigmaTpcCombinedPrMax{"nSigmaTpcCombinedPrMax", 0., "Nsigma cut on TPC combined with TOF for proton selection"}; + + Configurable ptKaPidTpcMin{"ptKaPidTpcMin", -1, "Lower bound of track pT for TPC PID for kaon selection"}; + Configurable ptKaPidTpcMax{"ptKaPidTpcMax", 9999.9, "Upper bound of track pT for TPC PID for kaon selection"}; + Configurable nSigmaTpcKaMax{"nSigmaTpcKaMax", 3., "Nsigma cut on TPC only for kaon selection"}; + Configurable nSigmaTpcCombinedKaMax{"nSigmaTpcCombinedKaMax", 0., "Nsigma cut on TPC combined with TOF for kaon selection"}; + + // PID - TOF selections + Configurable ptPiPidTofMin{"ptPiPidTofMin", -1, "Lower bound of track pT for TOF PID for pion selection"}; + Configurable ptPiPidTofMax{"ptPiPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for pion selection"}; + Configurable nSigmaTofPiMax{"nSigmaTofPiMax", 3., "Nsigma cut on TOF only for pion selection"}; + Configurable nSigmaTofCombinedPiMax{"nSigmaTofCombinedPiMax", 0., "Nsigma cut on TOF combined with TPC for pion selection"}; + + Configurable ptPrPidTofMin{"ptPrPidTofMin", -1, "Lower bound of track pT for TOF PID for proton selection"}; + Configurable ptPrPidTofMax{"ptPrPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for proton selection"}; + Configurable nSigmaTofPrMax{"nSigmaTofPrMax", 3., "Nsigma cut on TOF only for proton selection"}; + Configurable nSigmaTofCombinedPrMax{"nSigmaTofCombinedPrMax", 0., "Nsigma cut on TOF combined with TPC for proton selection"}; + + Configurable ptKaPidTofMin{"ptKaPidTofMin", -1, "Lower bound of track pT for TOF PID for kaon selection"}; + Configurable ptKaPidTofMax{"ptKaPidTofMax", 9999.9, "Upper bound of track pT for TOF PID for kaon selection"}; + Configurable nSigmaTofKaMax{"nSigmaTofKaMax", 3., "Nsigma cut on TOF only for kaon selection"}; + Configurable nSigmaTofCombinedKaMax{"nSigmaTofCombinedKaMax", 0., "Nsigma cut on TOF combined with TPC for kaon selection"}; + + // detector clusters selections + Configurable nClustersTpcMin{"nClustersTpcMin", 70, "Minimum number of TPC clusters requirement"}; + Configurable nTpcCrossedRowsMin{"nTpcCrossedRowsMin", 70, "Minimum number of TPC crossed rows requirement"}; + Configurable tpcCrossedRowsOverFindableClustersRatioMin{"tpcCrossedRowsOverFindableClustersRatioMin", 0.8, "Minimum ratio TPC crossed rows over findable clusters requirement"}; + Configurable tpcChi2PerClusterMax{"tpcChi2PerClusterMax", 4, "Maximum value of chi2 fit over TPC clusters"}; + Configurable nClustersItsMin{"nClustersItsMin", 3, "Minimum number of ITS clusters requirement for pi <- charm baryon"}; + Configurable nClustersItsInnBarrMin{"nClustersItsInnBarrMin", 1, "Minimum number of ITS clusters in inner barrel requirement for pi <- charm baryon"}; + Configurable itsChi2PerClusterMax{"itsChi2PerClusterMax", 36, "Maximum value of chi2 fit over ITS clusters for pi <- charm baryon"}; + + o2::analysis::HfMlResponseOmegacToOmegaPi hfMlResponseDca; + o2::analysis::HfMlResponseOmegacToOmegaPi hfMlResponseKf; + std::vector outputMlOmegac; + o2::ccdb::CcdbApi ccdbApi; + + TrackSelectorPi selectorPion; + TrackSelectorPr selectorProton; + TrackSelectorKa selectorKaon; + + using TracksSel = soa::Join; + using TracksSelLf = soa::Join; + + HistogramRegistry registry{"registry"}; // for QA of selections + + struct : ConfigurableGroup { + //// KF selection + std::string prefix = "kfSel"; + Configurable applyKFpreselections{"applyKFpreselections", false, "Apply KFParticle related rejection"}; + Configurable applyCompetingCascRejection{"applyCompetingCascRejection", false, "Apply competing Xi(for Omegac0) rejection"}; + Configurable cascadeRejMassWindow{"cascadeRejMassWindow", 0.01, "competing Xi(for Omegac0) rejection mass window"}; + Configurable v0LdlMin{"v0LdlMin", 3., "Minimum value of l/dl of V0"}; // l/dl and Chi2 are to be determined + Configurable cascLdlMin{"cascLdlMin", 1., "Minimum value of l/dl of casc"}; + Configurable omegacLdlMax{"omegacLdlMax", 5., "Maximum value of l/dl of Omegac"}; + Configurable cTauOmegacMax{"cTauOmegacMax", 0.4, "lifetime τ of Omegac"}; + Configurable v0Chi2OverNdfMax{"v0Chi2OverNdfMax", 100., "Maximum chi2Geo/NDF of V0"}; + Configurable cascChi2OverNdfMax{"cascChi2OverNdfMax", 100., "Maximum chi2Geo/NDF of casc"}; + Configurable omegacChi2OverNdfMax{"omegacChi2OverNdfMax", 100., "Maximum chi2Geo/NDF of Omegac"}; + Configurable chi2TopoV0ToCascMax{"chi2TopoV0ToCascMax", 100., "Maximum chi2Topo/NDF of V0ToCas"}; + Configurable chi2TopoOmegacToPvMax{"chi2TopoOmegacToPvMax", 100., "Maximum chi2Topo/NDF of OmegacToPv"}; + Configurable chi2TopoCascToOmegacMax{"chi2TopoCascToOmegacMax", 100., "Maximum chi2Topo/NDF of CascToOmegac"}; + Configurable chi2TopoCascToPvMax{"chi2TopoCascToPvMax", 100., "Maximum chi2Topo/NDF of CascToPv"}; + Configurable decayLenXYOmegacMax{"decayLenXYOmegacMax", 1.5, "Maximum decay lengthXY of Omegac"}; + Configurable decayLenXYCascMin{"decayLenXYCascMin", 1., "Minimum decay lengthXY of Cascade"}; + Configurable decayLenXYLambdaMin{"decayLenXYLambdaMin", 0., "Minimum decay lengthXY of V0"}; + Configurable cosPaCascToOmegacMin{"cosPaCascToOmegacMin", 0.995, "Minimum cosPA of cascade<-Omegac"}; + Configurable cosPaV0ToCascMin{"cosPaV0ToCascMin", 0.99, "Minimum cosPA of V0<-cascade"}; + } kfConfigurableGroup; + + void init(InitContext const&) + { + std::array processesSelector = {doprocessOmegac0SelectorWithDCAFitter, doprocessOmegac0SelectorWithKFParticle}; + const int nProcessesSelector = std::accumulate(processesSelector.begin(), processesSelector.end(), 0); + if (nProcessesSelector != 1) { + LOGP(fatal, "Exactly one process function for selector can be enabled at a time."); + } + + selectorPion.setRangePtTpc(ptPiPidTpcMin, ptPiPidTpcMax); + selectorPion.setRangeNSigmaTpc(-nSigmaTpcPiMax, nSigmaTpcPiMax); + selectorPion.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedPiMax, nSigmaTpcCombinedPiMax); + selectorPion.setRangePtTof(ptPiPidTofMin, ptPiPidTofMax); + selectorPion.setRangeNSigmaTof(-nSigmaTofPiMax, nSigmaTofPiMax); + selectorPion.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedPiMax, nSigmaTofCombinedPiMax); + + selectorProton.setRangePtTpc(ptPrPidTpcMin, ptPrPidTpcMax); + selectorProton.setRangeNSigmaTpc(-nSigmaTpcPrMax, nSigmaTpcPrMax); + selectorProton.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedPrMax, nSigmaTpcCombinedPrMax); + selectorProton.setRangePtTof(ptPrPidTofMin, ptPrPidTofMax); + selectorProton.setRangeNSigmaTof(-nSigmaTofPrMax, nSigmaTofPrMax); + selectorProton.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedPrMax, nSigmaTofCombinedPrMax); + + selectorKaon.setRangePtTpc(ptKaPidTpcMin, ptKaPidTpcMax); + selectorKaon.setRangeNSigmaTpc(-nSigmaTpcKaMax, nSigmaTpcKaMax); + selectorKaon.setRangeNSigmaTpcCondTof(-nSigmaTpcCombinedKaMax, nSigmaTpcCombinedKaMax); + selectorKaon.setRangePtTof(ptKaPidTofMin, ptKaPidTofMax); + selectorKaon.setRangeNSigmaTof(-nSigmaTofKaMax, nSigmaTofKaMax); + selectorKaon.setRangeNSigmaTofCondTpc(-nSigmaTofCombinedKaMax, nSigmaTofCombinedKaMax); + + const AxisSpec axisSel{2, -0.5, 1.5, "status"}; + const AxisSpec axisSelOnLfDca{16, -0.5, 15.5, "status"}; + const AxisSpec axisSelOnLfKf{23, -0.5, 22.5, "status"}; + const AxisSpec axisSelOnHfDca{6, -0.5, 5.5, "status"}; + const AxisSpec axisSelOnHfKf{12, -0.5, 11.5, "status"}; + + // for QA of the selections (bin 0 -> candidates that did not pass the selection, bin 1 -> candidates that passed the selection) + registry.add("hSelSignDec", "hSelSignDec;status;entries", {HistType::kTH1F, {axisSel}}); + registry.add("hSelStatusCluster", "hSelStatusCluster:# of events Passed;;", {HistType::kTH1F, {{6, -0.5, 5.5}}}); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(2, "TpcCluster PiFromV0"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(3, "TpcCluster PrFromV0"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(4, "TpcCluster KaFromCasc"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(5, "TpcCluster PiFromCharm"); + registry.get(HIST("hSelStatusCluster"))->GetXaxis()->SetBinLabel(6, "ItsCluster PiFromCharm"); + + registry.add("hSelStatusPID", "hSelStatusPID;# of events Passed;;", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(2, "Lambda"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(3, "Cascade"); + registry.get(HIST("hSelStatusPID"))->GetXaxis()->SetBinLabel(4, "CharmBaryon"); + + // For QA of LF & HF selection + if (doprocessOmegac0SelectorWithDCAFitter) { + registry.add("hSelStatusLf", "hSelStatusLf;# of candidate passed;", {HistType::kTH1F, {axisSelOnLfDca}}); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(2, "etaV0PosDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(3, "etaV0NegDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(4, "etaKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(5, "radiusV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(6, "radiusCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(7, "cosPAV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(8, "cosPACasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(9, "dcaV0Dau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(10, "dcaCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(11, "dcaXYToPvV0Dau0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(12, "dcaXYToPvV0Dau1"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(13, "dcaXYToPvCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(14, "ptKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(15, "impactParCascXY"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(16, "impactParCascZ"); + + registry.add("hSelStatusHf", "hSelStatusHf;# of candidate passed;", {HistType::kTH1F, {axisSelOnHfDca}}); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(2, "etaTrackCharmBach"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(3, "dcaCharmBaryonDau"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(4, "ptPiFromCharmBaryon"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(5, "impactParBachFromCharmXY"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(6, "impactParBachFromCharmZ"); + } + + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hSelStatusLf", "hSelStatusLf;# of candidate passed;", {HistType::kTH1F, {axisSelOnLfKf}}); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(2, "etaV0PosDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(3, "etaV0NegDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(4, "etaKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(5, "radiusV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(6, "radiusCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(7, "cosPAV0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(8, "cosPACasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(9, "dcaV0Dau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(10, "dcaCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(11, "dcaXYToPvV0Dau0"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(12, "dcaXYToPvV0Dau1"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(13, "dcaXYToPvCascDau"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(14, "ptKaFromCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(15, "cosPaV0ToCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(16, "v0Chi2OverNdf"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(17, "cascChi2OverNdf"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(18, "chi2TopoCascToPv"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(19, "chi2TopoV0ToCasc"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(20, "v0ldl"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(21, "cascldl"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(22, "decayLenXYLambda"); + registry.get(HIST("hSelStatusLf"))->GetXaxis()->SetBinLabel(23, "decayLenXYCasc"); + + registry.add("hSelStatusHf", "hSelStatusHf;# of candidate passed;", {HistType::kTH1F, {axisSelOnHfKf}}); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(2, "etaTrackCharmBach"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(3, "dcaCharmBaryonDau"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(4, "ptPiFromCharmBaryon"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(5, "kfptOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(6, "cosPaCascToOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(7, "omegacChi2OverNdf"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(8, "chi2TopoOmegacToPv"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(9, "chi2TopoCascToOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(10, "decayLenXYOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(11, "cTauOmegac"); + registry.get(HIST("hSelStatusHf"))->GetXaxis()->SetBinLabel(12, "omegacldl"); + } + + registry.add("hInvMassCharmBaryon", "Charm baryon invariant mass; inv. mass; entries", {HistType::kTH1F, {{500, 2.3, 3.1}}}); + registry.add("hPtCharmBaryon", "Charm baryon transverse momentum; p_{T} (GeV/#it{c}); entries", {HistType::kTH1F, {{8000, 0., 80.}}}); + + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hSelCompetingCasc", "hSelCompetingCasc;status;entries", {HistType::kTH1F, {axisSel}}); + registry.add("hInvMassXiMinus_rej_cut", "hInvMassXiMinus_rej_cut;m_{#Lambda#pi} under Xi hypothesis (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{1000, 1.25f, 1.65f}}}); + } + + // HfMlResponse initialization + if (applyMl) { + if (doprocessOmegac0SelectorWithKFParticle) { + registry.add("hBDTScoreKF", "hBDTScoreKF", {HistType::kTH1D, {{100, 0.0f, 1.0f, "score"}}}); + hfMlResponseKf.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseKf.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseKf.setModelPathsLocal(onnxFileNames); + } + hfMlResponseKf.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseKf.init(); + } else if (doprocessOmegac0SelectorWithDCAFitter) { + registry.add("hBDTScoreDCA", "hBDTScoreDCA", {HistType::kTH1D, {{100, 0.0f, 1.0f, "score"}}}); + hfMlResponseDca.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + hfMlResponseDca.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + hfMlResponseDca.setModelPathsLocal(onnxFileNames); + } + hfMlResponseDca.cacheInputFeaturesIndices(namesInputFeatures); + hfMlResponseDca.init(); + } + } + } + + // LF cuts - Cuts on LF tracks reco + // Selection on LF related informations + // returns true if all cuts are passed + template + bool selectOnLf(const T& candidate) + { + + registry.fill(HIST("hSelStatusLf"), 0.0); + + // Eta selection of V0, Cascade daughters + double etaV0PosDau = candidate.etaV0PosDau(); + double etaV0NegDau = candidate.etaV0NegDau(); + double etaKaFromCasc = candidate.etaBachFromCasc(); + + if (std::abs(etaV0PosDau) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 1.0); + + if (std::abs(etaV0NegDau) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 2.0); + + if (std::abs(etaKaFromCasc) > etaTrackLFDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 3.0); + + // Minimum radius cut + double radiusV0 = RecoDecay::sqrtSumOfSquares(candidate.xDecayVtxV0(), candidate.yDecayVtxV0()); + double radiusCasc = RecoDecay::sqrtSumOfSquares(candidate.xDecayVtxCascade(), candidate.yDecayVtxCascade()); + + if (radiusV0 < radiusV0Min) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 4.0); + if (radiusCasc < radiusCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 5.0); + + // Cosine of pointing angle + if (candidate.cosPAV0() < cosPAV0Min) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 6.0); + if (candidate.cosPACasc() < cosPACascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 7.0); + + // Distance of Closest Approach(DCA) + if (candidate.dcaV0Dau() > dcaV0DauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 8.0); + + if (candidate.dcaCascDau() > dcaCascDauMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 9.0); + + if (std::abs(candidate.dcaXYToPvV0Dau0()) < dcaPosToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 10.0); + + if (std::abs(candidate.dcaXYToPvV0Dau1()) < dcaNegToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 11.0); + + if (std::abs(candidate.dcaXYToPvCascDau()) < dcaBachToPvMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 12.0); + + // pT: Bachelor + double ptKaFromCasc = RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCasc(), candidate.pyBachFromCasc()); + if (std::abs(ptKaFromCasc) < ptKaFromCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 13.0); + + // Extra cuts for KFParticle + if constexpr (svReco == doKfParticle) { + if (kfConfigurableGroup.applyKFpreselections) { + // Cosine of Pointing angle + if (candidate.cosPaV0ToCasc() < kfConfigurableGroup.cosPaV0ToCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 14.0); + + // Chi2 + if (candidate.v0Chi2OverNdf() < 0 || candidate.v0Chi2OverNdf() > kfConfigurableGroup.v0Chi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 15.0); + if (candidate.cascChi2OverNdf() < 0 || candidate.cascChi2OverNdf() > kfConfigurableGroup.cascChi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 16.0); + if (candidate.chi2TopoCascToPv() < 0 || candidate.chi2TopoCascToPv() > kfConfigurableGroup.chi2TopoCascToPvMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 17.0); + if (candidate.chi2TopoV0ToCasc() < 0 || candidate.chi2TopoV0ToCasc() > kfConfigurableGroup.chi2TopoV0ToCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 18.0); + + // ldl + if (candidate.v0ldl() < kfConfigurableGroup.v0LdlMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 19.0); + if (candidate.cascldl() < kfConfigurableGroup.cascLdlMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 20.0); + + // Decay length + if (std::abs(candidate.decayLenXYLambda()) < kfConfigurableGroup.decayLenXYLambdaMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 21.0); + if (std::abs(candidate.decayLenXYCasc()) < kfConfigurableGroup.decayLenXYCascMin) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 22.0); + + // Competing Xi rejection + if (kfConfigurableGroup.applyCompetingCascRejection) { + const auto invMassXiHypothesis = candidate.cascRejectInvmass(); + + if (std::abs(invMassXiHypothesis - o2::constants::physics::MassXiMinus) < kfConfigurableGroup.cascadeRejMassWindow) { + registry.fill(HIST("hSelCompetingCasc"), 0.0); + return false; + } + + registry.fill(HIST("hSelCompetingCasc"), 1.0); + registry.fill(HIST("hInvMassXiMinus_rej_cut"), invMassXiHypothesis); + } + } + } else { + // Impact parameter + if (std::abs(candidate.impactParCascXY()) < impactParameterXYCascMin || std::abs(candidate.impactParCascXY()) > impactParameterXYCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 14.0); + if (std::abs(candidate.impactParCascZ()) < impactParameterZCascMin || std::abs(candidate.impactParCascZ()) > impactParameterZCascMax) { + return false; + } + registry.fill(HIST("hSelStatusLf"), 15.0); + } + + // If passes all cuts, return true + return true; + } + + // HF cuts - Cuts on Charm baryon reco + // Apply cuts with charm baryon & charm bachelor related informations + // returns true if all cuts are passed + template + bool selectOnHf(const T& candidate, const int& inputPtBin) + { + registry.fill(HIST("hSelStatusHf"), 0.0); + + // eta selection on charm bayron bachelor + if (std::abs(candidate.etaBachFromCharmBaryon()) > etaTrackCharmBachMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 1.0); + // Distance of Closest Approach(DCA) + if (candidate.dcaCharmBaryonDau() > dcaCharmBaryonDauMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 2.0); + + // pT: Charm Bachelor + double ptPiFromCharmBaryon = RecoDecay::sqrtSumOfSquares(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon()); + if (inputPtBin < 0 || ptPiFromCharmBaryon < cuts->get(inputPtBin, "pT pi from Omegac")) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 3.0); + + // specific selections with KFParticle output + if constexpr (svReco == doKfParticle) { + if (kfConfigurableGroup.applyKFpreselections) { + // Omegac Pt selection + if (std::abs(candidate.kfptOmegac()) <= ptCandMin || std::abs(candidate.kfptOmegac()) >= ptCandMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 4.0); + + // Cosine of pointing angle + if (candidate.cosPaCascToOmegac() < kfConfigurableGroup.cosPaCascToOmegacMin) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 5.0); + + // Chi2 + if (candidate.omegacChi2OverNdf() < 0 || candidate.omegacChi2OverNdf() > kfConfigurableGroup.omegacChi2OverNdfMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 6.0); + if (candidate.chi2TopoOmegacToPv() < 0 || candidate.chi2TopoOmegacToPv() > kfConfigurableGroup.chi2TopoOmegacToPvMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 7.0); + if (candidate.chi2TopoCascToOmegac() < 0 || candidate.chi2TopoCascToOmegac() > kfConfigurableGroup.chi2TopoCascToOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 8.0); + + // Decay Length + if (std::abs(candidate.decayLenXYOmegac()) > kfConfigurableGroup.decayLenXYOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 9.0); + + // ctau + if (std::abs(candidate.cTauOmegac()) > kfConfigurableGroup.cTauOmegacMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 10.0); + + // Omegac l/dl + if (candidate.omegacldl() > kfConfigurableGroup.omegacLdlMax) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 11.0); + } + } else { + // Impact parameter + if ((std::abs(candidate.impactParBachFromCharmBaryonXY()) < impactParameterXYPiFromCharmBaryonMin) || (std::abs(candidate.impactParBachFromCharmBaryonXY()) > impactParameterXYPiFromCharmBaryonMax)) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 4.0); + if ((std::abs(candidate.impactParBachFromCharmBaryonZ()) < impactParameterZPiFromCharmBaryonMin) || (std::abs(candidate.impactParBachFromCharmBaryonZ()) > impactParameterZPiFromCharmBaryonMax)) { + return false; + } + registry.fill(HIST("hSelStatusHf"), 5.0); + } + + // If passes all cuts, return true + return true; + } + + template + void runOmegac0Selector(TCandTable const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + // looping over charm baryon candidates + for (const auto& candidate : candidates) { + + bool resultSelections = true; // True if the candidate passes all the selections, False otherwise + outputMlOmegac.clear(); + + auto trackV0PosDau = lfTracks.rawIteratorAt(candidate.posTrackId()); + auto trackV0NegDau = lfTracks.rawIteratorAt(candidate.negTrackId()); + auto trackKaFromCasc = lfTracks.rawIteratorAt(candidate.bachelorId()); + auto trackPiFromCharm = tracks.rawIteratorAt(candidate.bachelorFromCharmBaryonId()); + + auto trackPiFromLam = trackV0NegDau; + auto trackPrFromLam = trackV0PosDau; + + int8_t const signDecay = candidate.signDecay(); // sign of pi <- cascade + + if (signDecay > 0) { + trackPiFromLam = trackV0PosDau; + trackPrFromLam = trackV0NegDau; + registry.fill(HIST("hSelSignDec"), 1); // anti-particle decay + } else { + registry.fill(HIST("hSelSignDec"), 0); // particle decay + } + + // pT selection + auto ptCandOmegac = RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()); + + if (ptCandOmegac < ptCandMin || ptCandOmegac > ptCandMax) { + resultSelections = false; + } + + int pTBin = findBin(binsPt, ptCandOmegac); + if (pTBin == -1) { + resultSelections = false; + } + + // Topological selection + const bool selectionResOnLF = selectOnLf(candidate); + const bool selectionResOnHF = selectOnHf(candidate, pTBin); + if (!selectionResOnLF || !selectionResOnHF) { + resultSelections = false; + } + + // TPC clusters selections + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 0.0); + } + if (applyTrkSelLf) { + if (!isSelectedTrackTpcQuality(trackPiFromLam, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 1.0); + } + } + + if (!isSelectedTrackTpcQuality(trackPrFromLam, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 2.0); + } + } + + if (!isSelectedTrackTpcQuality(trackKaFromCasc, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 3.0); + } + } + } + + if (!isSelectedTrackTpcQuality(trackPiFromCharm, nClustersTpcMin, nTpcCrossedRowsMin, tpcCrossedRowsOverFindableClustersRatioMin, tpcChi2PerClusterMax)) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 4.0); + } + } + + // ITS clusters selection + if (!isSelectedTrackItsQuality(trackPiFromCharm, nClustersItsMin, itsChi2PerClusterMax) || trackPiFromCharm.itsNClsInnerBarrel() < nClustersItsInnBarrMin) { + resultSelections = false; + } else { + if (resultSelections) { + registry.fill(HIST("hSelStatusCluster"), 5.0); + } + } + + // Track level PID selection + if (resultSelections) { + registry.fill(HIST("hSelStatusPID"), 0.0); + } + int statusPidPrFromLam = -999; + int statusPidPiFromLam = -999; + int statusPidKaFromCasc = -999; + int statusPidPiFromCharmBaryon = -999; + + int infoTpcStored = 0; + int infoTofStored = 0; + + if (usePidTpcOnly == usePidTpcTofCombined) { + LOGF(fatal, "Check the PID configurables, usePidTpcOnly and usePidTpcTofCombined can't have the same value"); + } + + if (trackPiFromLam.hasTPC()) { + SETBIT(infoTpcStored, PiFromLam); + } + if (trackPrFromLam.hasTPC()) { + SETBIT(infoTpcStored, PrFromLam); + } + if (trackKaFromCasc.hasTPC()) { + SETBIT(infoTpcStored, KaFromCasc); + } + if (trackPiFromCharm.hasTPC()) { + SETBIT(infoTpcStored, PiFromCharm); + } + if (trackPiFromLam.hasTOF()) { + SETBIT(infoTofStored, PiFromLam); + } + if (trackPrFromLam.hasTOF()) { + SETBIT(infoTofStored, PrFromLam); + } + if (trackKaFromCasc.hasTOF()) { + SETBIT(infoTofStored, KaFromCasc); + } + if (trackPiFromCharm.hasTOF()) { + SETBIT(infoTofStored, PiFromCharm); + } + + if (usePidTpcOnly) { + statusPidPrFromLam = selectorProton.statusTpc(trackPrFromLam); + statusPidPiFromLam = selectorPion.statusTpc(trackPiFromLam); + statusPidKaFromCasc = selectorKaon.statusTpc(trackKaFromCasc); + statusPidPiFromCharmBaryon = selectorPion.statusTpc(trackPiFromCharm); + } else if (usePidTpcTofCombined) { + statusPidPrFromLam = selectorProton.statusTpcOrTof(trackPrFromLam); + statusPidPiFromLam = selectorPion.statusTpcOrTof(trackPiFromLam); + statusPidKaFromCasc = selectorKaon.statusTpcOrTof(trackKaFromCasc); + statusPidPiFromCharmBaryon = selectorPion.statusTpcOrTof(trackPiFromCharm); + } + + bool statusPidLambda = (statusPidPrFromLam == TrackSelectorPID::Accepted) && (statusPidPiFromLam == TrackSelectorPID::Accepted); + if (statusPidLambda && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 1.0); + } + bool statusPidCascade = (statusPidLambda && statusPidKaFromCasc == TrackSelectorPID::Accepted); + if (statusPidCascade && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 2.0); + } + bool statusPidCharmBaryon = (statusPidCascade && statusPidPiFromCharmBaryon == TrackSelectorPID::Accepted); + if (statusPidCharmBaryon && resultSelections) { + registry.fill(HIST("hSelStatusPID"), 3.0); + } + + // invariant mass cuts + bool statusInvMassLambda = false; + bool statusInvMassCascade = false; + bool statusInvMassCharmBaryon = false; + + double const invMassLambda = candidate.invMassLambda(); + double const invMassCascade = candidate.invMassCascade(); + double const invMassCharmBaryon = candidate.invMassCharmBaryon(); + + if (std::abs(invMassLambda - o2::constants::physics::MassLambda0) < v0MassWindow) { + statusInvMassLambda = true; + } + if (std::abs(invMassCascade - o2::constants::physics::MassOmegaMinus) < cascadeMassWindow) { + statusInvMassCascade = true; + } + if ((invMassCharmBaryon >= invMassCharmBaryonMin) && (invMassCharmBaryon <= invMassCharmBaryonMax)) { + statusInvMassCharmBaryon = true; + } + + // Fill in selection result + if (!statusPidLambda || !statusPidCascade || !statusPidCharmBaryon || + !statusInvMassLambda || !statusInvMassCascade || !statusInvMassCharmBaryon) { + resultSelections = false; + } + + // Check candidate pT range for ML inference + if (applyMl && findBin(binsPtMl, ptCandOmegac) == -1) { + resultSelections = false; + } + + // ML BDT selection + if (applyMl && resultSelections) { + bool isSelectedMlOmegac = false; + std::vector inputFeaturesOmegaC = {}; + + if constexpr (svReco == doKfParticle) { + inputFeaturesOmegaC = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackKaFromCasc, trackPiFromCharm); + isSelectedMlOmegac = hfMlResponseKf.isSelectedMl(inputFeaturesOmegaC, ptCandOmegac, outputMlOmegac); + if (isSelectedMlOmegac) { + registry.fill(HIST("hBDTScoreKF"), outputMlOmegac[0]); + } else { + resultSelections = false; + } + } else if constexpr (svReco == doDcaFitter) { + inputFeaturesOmegaC = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackKaFromCasc, trackPiFromCharm); + isSelectedMlOmegac = hfMlResponseDca.isSelectedMl(inputFeaturesOmegaC, ptCandOmegac, outputMlOmegac); + if (isSelectedMlOmegac) { + registry.fill(HIST("hBDTScoreDCA"), outputMlOmegac[0]); + } else { + resultSelections = false; + } + } + } + + if (applyMl) { + hfMlSelToOmegaPi(outputMlOmegac); + } + + hfSelToOmegaPi(statusPidLambda, statusPidCascade, statusPidCharmBaryon, statusInvMassLambda, statusInvMassCascade, statusInvMassCharmBaryon, resultSelections, infoTpcStored, infoTofStored, + trackPiFromCharm.tpcNSigmaPi(), trackKaFromCasc.tpcNSigmaKa(), trackPiFromLam.tpcNSigmaPi(), trackPrFromLam.tpcNSigmaPr(), + trackPiFromCharm.tofNSigmaPi(), trackKaFromCasc.tofNSigmaKa(), trackPiFromLam.tofNSigmaPi(), trackPrFromLam.tofNSigmaPr()); + + // Fill in invariant mass histogram + if (statusPidLambda && statusPidCascade && statusPidCharmBaryon && statusInvMassLambda && statusInvMassCascade && statusInvMassCharmBaryon && resultSelections) { + registry.fill(HIST("hInvMassCharmBaryon"), invMassCharmBaryon); + if constexpr (svReco == doKfParticle) { + registry.fill(HIST("hPtCharmBaryon"), candidate.kfptOmegac()); + } else { + registry.fill(HIST("hPtCharmBaryon"), ptCandOmegac); + } + } + + } // end of candidate loop + } // end run function + + /////////////////////////////////// + /// Process with DCAFitter // + /////////////////////////////////// + void processOmegac0SelectorWithDCAFitter(aod::HfCandToOmegaPi const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + runOmegac0Selector(candidates, tracks, lfTracks); + } + PROCESS_SWITCH(HfCandidateSelectorToOmegaPiQa, processOmegac0SelectorWithDCAFitter, "Omegac0 candidate selection with DCAFitter output", true); + + //////////////////////////////////// + /// Process with KFParticle // + //////////////////////////////////// + void processOmegac0SelectorWithKFParticle(soa::Join const& candidates, + TracksSel const& tracks, + TracksSelLf const& lfTracks) + { + runOmegac0Selector(candidates, tracks, lfTracks); + } + PROCESS_SWITCH(HfCandidateSelectorToOmegaPiQa, processOmegac0SelectorWithKFParticle, "Omegac0 candidate selection with KFParticle output", false); + +}; // end struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx b/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx index b6b40705557..2c8dfc1cbb7 100644 --- a/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx +++ b/PWGHF/TableProducer/candidateSelectorToXiPiQa.cxx @@ -78,12 +78,12 @@ struct HfCandidateSelectorToXiPiQa { // cuts from SelectorCuts.h - pT dependent cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_xic_to_xi_pi::vecBinsPt}, "pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_xic_to_xi_pi::Cuts[0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per Pt Bin"}; + Configurable> cuts{"cuts", {&hf_cuts_xic_to_xi_pi::Cuts[0][0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per Pt Bin"}; // ML inference Configurable applyMl{"applyMl", false, "Flag to apply ML selections"}; Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutsMl{"cutsMl", {&hf_cuts_ml::Cuts[0][0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; // CCDB configuration @@ -144,7 +144,7 @@ struct HfCandidateSelectorToXiPiQa { o2::analysis::HfMlResponseXic0ToXiPi hfMlResponseDca; o2::analysis::HfMlResponseXic0ToXiPiKf hfMlResponseKf; - std::vector outputMlXic0ToXiPi = {}; + std::vector outputMlXic0ToXiPi; o2::ccdb::CcdbApi ccdbApi; TrackSelectorPi selectorPion; @@ -295,7 +295,7 @@ struct HfCandidateSelectorToXiPiQa { // Selection on LF related informations // returns true if all cuts are passed template - bool SelectOnLF(const T& candidate, const int& inputPtBin) + bool selectOnLf(const T& candidate, const int& inputPtBin) { registry.fill(HIST("hSelStatusLf"), 0.0); @@ -442,7 +442,7 @@ struct HfCandidateSelectorToXiPiQa { // Apply cuts with charm baryon & charm bachelor related informations // returns true if all cuts are passed template - bool SelectOnHF(const T& candidate, const int& inputPtBin) + bool selectOnHf(const T& candidate, const int& inputPtBin) { registry.fill(HIST("hSelStatusHf"), 0.0); @@ -561,8 +561,8 @@ struct HfCandidateSelectorToXiPiQa { } // Topological selection - const bool selectionResOnLF = SelectOnLF(candidate, pTBin); - const bool selectionResOnHF = SelectOnHF(candidate, pTBin); + const bool selectionResOnLF = selectOnLf(candidate, pTBin); + const bool selectionResOnHF = selectOnHf(candidate, pTBin); if (!selectionResOnLF || !selectionResOnHF) { resultSelections = false; } @@ -699,23 +699,6 @@ struct HfCandidateSelectorToXiPiQa { statusInvMassCharmBaryon = true; } - // ML BDT selection - if (applyMl) { - bool isSelectedMlXic0 = false; - std::vector inputFeaturesXic0 = {}; - if constexpr (svReco == doDcaFitter) { - inputFeaturesXic0 = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); - isSelectedMlXic0 = hfMlResponseDca.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); - } else { - inputFeaturesXic0 = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); - isSelectedMlXic0 = hfMlResponseKf.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); - } - if (!isSelectedMlXic0) { - continue; - } - hfMlToXiPi(outputMlXic0ToXiPi); - } - // Fill in selection result if constexpr (svReco == doDcaFitter) { hfSelToXiPi(statusPidLambda, statusPidCascade, statusPidCharmBaryon, statusInvMassLambda, statusInvMassCascade, statusInvMassCharmBaryon, resultSelections, infoTpcStored, infoTofStored, @@ -732,6 +715,24 @@ struct HfCandidateSelectorToXiPiQa { trackPiFromCharm.tofNSigmaPi(), trackPiFromCasc.tofNSigmaPi(), trackPiFromLam.tofNSigmaPi(), trackPrFromLam.tofNSigmaPr()); } + // ML BDT selection if required + if (applyMl) { + bool isSelectedMlXic0 = false; + if constexpr (svReco == doDcaFitter) { + auto inputFeaturesXic0 = hfMlResponseDca.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + isSelectedMlXic0 = hfMlResponseDca.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); + } else { + auto inputFeaturesXic0 = hfMlResponseKf.getInputFeatures(candidate, trackPiFromLam, trackPrFromLam, trackPiFromCasc, trackPiFromCharm); + isSelectedMlXic0 = hfMlResponseKf.isSelectedMl(inputFeaturesXic0, ptCandXic0, outputMlXic0ToXiPi); + } + + hfMlToXiPi(outputMlXic0ToXiPi); + + if (!isSelectedMlXic0) { + continue; + } + } + // Fill in invariant mass histogram if (resultSelections) { registry.fill(HIST("hInvMassCharmBaryonWoPidInvMassCut"), invMassCharmBaryon); @@ -765,8 +766,8 @@ struct HfCandidateSelectorToXiPiQa { }; // end struct -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx b/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx index 69d2a73339b..5139c504e57 100644 --- a/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx +++ b/PWGHF/TableProducer/candidateSelectorXic0ToXiPiKf.cxx @@ -120,15 +120,23 @@ struct HfCandidateSelectorXic0ToXiPiKf { // topological cuts Configurable> binsPt{"binsPt", std::vector{hf_cuts_xic_to_xi_pi::vecBinsPt}, "pT bin limits"}; - Configurable> cuts{"cuts", {hf_cuts_xic_to_xi_pi::Cuts[0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per pT bin"}; + Configurable> cuts{"cuts", {&hf_cuts_xic_to_xi_pi::Cuts[0][0], hf_cuts_xic_to_xi_pi::NBinsPt, hf_cuts_xic_to_xi_pi::NCutVars, hf_cuts_xic_to_xi_pi::labelsPt, hf_cuts_xic_to_xi_pi::labelsCutVar}, "Xic0 candidate selection per pT bin"}; // ML inference Configurable applyMl{"applyMl", true, "Flag to apply ML selections"}; Configurable> binsPtMl{"binsPtMl", std::vector{hf_cuts_ml::vecBinsPt}, "pT bin limits for ML application"}; Configurable> cutDirMl{"cutDirMl", std::vector{hf_cuts_ml::vecCutDir}, "Whether to reject score values greater or smaller than the threshold"}; - Configurable> cutsMl{"cutsMl", {hf_cuts_ml::Cuts[0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; + Configurable> cutsMl{"cutsMl", {&hf_cuts_ml::Cuts[0][0], hf_cuts_ml::NBinsPt, hf_cuts_ml::NCutScores, hf_cuts_ml::labelsPt, hf_cuts_ml::labelsCutScore}, "ML selections per pT bin"}; Configurable nClassesMl{"nClassesMl", static_cast(hf_cuts_ml::NCutScores), "Number of classes in ML model"}; - Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"feature1", "feature2"}, "Names of ML model input features"}; + Configurable> namesInputFeatures{"namesInputFeatures", + std::vector{"fDecayLenXYXic", "fXicChi2OverNdf", "fChi2TopoXicToPv", "fDcaCharmBaryonDau", + "fKfDcaXYPiFromXic", "fChi2TopoPiFromXicToPv", "fKfptPiFromXic", "fDecayLenXYCasc", + "fCascChi2OverNdf", "fChi2TopoCascToPv", "fChi2TopoCascToXic", "fCosPaCascToPv", + "fCosPaCascToXic", "fDcaCascDau", "fKfDcaXYCascToPv", "fTpcNSigmaPiFromCasc", + "fDcaXYToPvCascDau", "fDecayLenXYLambda", "fV0Chi2OverNdf", "fChi2TopoV0ToPv", + "fChi2TopoV0ToCasc", "fCosPaV0ToPv", "fCosPaV0ToCasc", "fDcaV0Dau", + "fDcaXYToPvV0Dau0", "fDcaXYToPvV0Dau1", "fTpcNSigmaPrFromLambda", "fTpcNSigmaPiFromLambda"}, + "Names of ML model input features"}; // CCDB configuration Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -224,14 +232,11 @@ struct HfCandidateSelectorXic0ToXiPiKf { auto trackPiFromCasc = lfTracks.rawIteratorAt(trackPiFromCascId); auto trackPiFromCharm = tracks.rawIteratorAt(trackPiFromCharmId); - auto trackPiFromLam = trackV0NegDau; - auto trackPrFromLam = trackV0PosDau; - int8_t const signDecay = candidate.signDecay(); // sign of pi <- cascade + auto const trackPiFromLam = signDecay > 0 ? trackV0PosDau : trackV0NegDau; + auto const trackPrFromLam = signDecay > 0 ? trackV0NegDau : trackV0PosDau; if (signDecay > 0) { - trackPiFromLam = trackV0PosDau; - trackPrFromLam = trackV0NegDau; registry.fill(HIST("hSelSignDec"), 1); // anti-particle decay } else if (signDecay < 0) { registry.fill(HIST("hSelSignDec"), 0); // particle decay @@ -418,7 +423,7 @@ struct HfCandidateSelectorXic0ToXiPiKf { // ML selections if (applyMl) { bool isSelectedMlXic0 = false; - std::vector inputFeaturesXic0 = hfMlResponse.getInputFeatures(candidate, trackPiFromLam, trackPiFromCasc, trackPiFromCharm); + std::vector inputFeaturesXic0 = hfMlResponse.getInputFeatures(candidate, trackPiFromLam, trackPrFromLam, trackPiFromCasc, trackPiFromCharm); if (!resultSelections) { hfMlToXiPi(outputMlXic0ToXiPiKf); } else { @@ -526,7 +531,7 @@ struct HfCandidateSelectorXic0ToXiPiKf { } // dcaXY v0 daughters to PV cut - if (std::abs(candidate.dcaXYToPvV0Dau0()) < cuts->get(pTBin, "dcaXYToPvV0Dau0") || std::abs(candidate.dcaXYToPvV0Dau1()) < cuts->get(pTBin, "dcaXYToPvV0Dau0")) { + if (std::abs(candidate.dcaXYToPvV0Dau0()) < cuts->get(pTBin, "dcaXYToPvV0Dau0") || std::abs(candidate.dcaXYToPvV0Dau1()) < cuts->get(pTBin, "dcaXYToPvV0Dau1")) { return false; } @@ -597,8 +602,8 @@ struct HfCandidateSelectorXic0ToXiPiKf { }; // end struct -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx index cd7d7028925..525b569b0e1 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToPKPi.cxx @@ -123,7 +123,7 @@ struct HfCandidateSelectorXicToPKPi { labels[1 + aod::SelectionStep::RecoPID] = "Skims & Topological & PID selections"; labels[1 + aod::SelectionStep::RecoMl] = "ML selection"; static const AxisSpec axisSelections = {kNBinsSelections, 0.5, kNBinsSelections + 0.5, ""}; - registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {(std::vector)binsPt, "#it{p}_{T} (GeV/#it{c})"}}}); + registry.add("hSelections", "Selections;;#it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {axisSelections, {std::vector(binsPt), "#it{p}_{T} (GeV/#it{c})"}}}); for (int iBin = 0; iBin < kNBinsSelections; ++iBin) { registry.get(HIST("hSelections"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } diff --git a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx index 6355024ec2b..8972554209a 100644 --- a/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/candidateSelectorXicToXiPiPi.cxx @@ -245,6 +245,8 @@ struct HfCandidateSelectorXicToXiPiPi { registry.add("hNCrossedRowsTpcBachelorPionRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (#pi #leftarrow #Xi);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); registry.add("hNCrossedRowsTpcV0PosDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 pos. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); registry.add("hNCrossedRowsTpcV0NegDauRecBkg", "Reconstructed background;#it{N}_{rows}^{TPC} (V0 neg. dau.);entries", {HistType::kTH1F, {{103, 49.5, 152.5}}}); + /// reconstructed MC + registry.add("hmass_pt_reco_truth", "Reconstructed truth matching MC;inv.mass(#Xi#pi#pi) [GeV/c^{2}];p_{T} [GeV/c]", {HistType::kTH2F, {{40, 2.3, 2.7}, {200, 0, 20}}}); } } @@ -274,9 +276,9 @@ struct HfCandidateSelectorXicToXiPiPi { TrackSelectorPID::Status const statusPidPiXi, TrackSelectorPID::Status const statusPidPrLam, TrackSelectorPID::Status const statusPidPiLam, - bool const useTpcPidOnly) + bool const useOnlyTpcPid) { - if (useTpcPidOnly) { + if (useOnlyTpcPid) { return (statusPidPi0 == TrackSelectorPID::Accepted || statusPidPi0 == TrackSelectorPID::NotApplicable) && (statusPidPi1 == TrackSelectorPID::Accepted || statusPidPi1 == TrackSelectorPID::NotApplicable) && (statusPidPiXi == TrackSelectorPID::Accepted || statusPidPiXi == TrackSelectorPID::NotApplicable) && (statusPidPrLam == TrackSelectorPID::Accepted || statusPidPrLam == TrackSelectorPID::NotApplicable) && (statusPidPiLam == TrackSelectorPID::Accepted || statusPidPiLam == TrackSelectorPID::NotApplicable); } if (statusPidPi0 == TrackSelectorPID::Rejected || statusPidPi1 == TrackSelectorPID::Rejected || statusPidPiXi == TrackSelectorPID::Rejected || statusPidPrLam == TrackSelectorPID::Rejected || statusPidPiLam == TrackSelectorPID::Rejected) { @@ -567,7 +569,7 @@ struct HfCandidateSelectorXicToXiPiPi { // ITS track quality selection on pions from charm baryon if ((!isSelectedTrackItsQuality(trackPi0, nClustersItsMin, itsChi2PerClusterMax) || trackPi0.itsNClsInnerBarrel() < nClustersItsInnBarrMin) || - (!isSelectedTrackItsQuality(trackPi0, nClustersItsMin, itsChi2PerClusterMax) || trackPi1.itsNClsInnerBarrel() < nClustersItsInnBarrMin)) { + (!isSelectedTrackItsQuality(trackPi1, nClustersItsMin, itsChi2PerClusterMax) || trackPi1.itsNClsInnerBarrel() < nClustersItsInnBarrMin)) { return false; } if constexpr (IsMc) { @@ -641,6 +643,7 @@ struct HfCandidateSelectorXicToXiPiPi { if constexpr (IsMc) { if (isMatchedSignal) { registry.fill(HIST("hSelCandidatesRecSig"), PidSelected); + registry.fill(HIST("hmass_pt_reco_truth"), hfCandXic.invMassXicPlus(), hfCandXic.pt()); } else { registry.fill(HIST("hSelCandidatesRecBkg"), PidSelected); } @@ -736,7 +739,7 @@ struct HfCandidateSelectorXicToXiPiPi { } bool isMatchedCandidate = false; - if (hfCandXic.flagMcMatchRec() != int8_t(0)) { + if (hfCandXic.flagMcMatchRec() != static_cast(0)) { isMatchedCandidate = true; } diff --git a/PWGHF/TableProducer/derivedDataCreatorB0ToDPi.cxx b/PWGHF/TableProducer/derivedDataCreatorB0ToDPi.cxx index bb52685c14e..fba8b5ef210 100644 --- a/PWGHF/TableProducer/derivedDataCreatorB0ToDPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorB0ToDPi.cxx @@ -94,6 +94,7 @@ struct HfDerivedDataCreatorB0ToDPi { SliceCache cache; static constexpr double Mass{o2::constants::physics::MassB0}; + static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -139,6 +140,7 @@ struct HfDerivedDataCreatorB0ToDPi { void fillTablesCandidate(const T& candidate, const U& prongCharm, const V& prongBachelor, int candFlag, double invMass, double ct, double y, int8_t flagMc, int8_t origin, float mlScore, const std::vector& mlScoresCharm) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -249,20 +251,32 @@ struct HfDerivedDataCreatorB0ToDPi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParDplus, fillCandidateParDplus, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateMlDplus, fillCandidateMlDplus, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -270,17 +284,6 @@ struct HfDerivedDataCreatorB0ToDPi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParDplus, fillCandidateParDplus, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateMlDplus, fillCandidateMlDplus, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { diff --git a/PWGHF/TableProducer/derivedDataCreatorBplusToD0Pi.cxx b/PWGHF/TableProducer/derivedDataCreatorBplusToD0Pi.cxx index 5baaec8dafa..e337ec4649f 100644 --- a/PWGHF/TableProducer/derivedDataCreatorBplusToD0Pi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorBplusToD0Pi.cxx @@ -97,6 +97,7 @@ struct HfDerivedDataCreatorBplusToD0Pi { SliceCache cache; static constexpr double Mass{o2::constants::physics::MassBPlus}; + static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -142,6 +143,7 @@ struct HfDerivedDataCreatorBplusToD0Pi { void fillTablesCandidate(const T& candidate, const U& prongCharm, const V& prongBachelor, int candFlag, double invMass, double ct, double y, int8_t flagMc, int8_t origin, float mlScore, const std::vector& mlScoresCharm) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -284,20 +286,33 @@ struct HfDerivedDataCreatorBplusToD0Pi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParD0, fillCandidateParD0, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateParD0E, fillCandidateParD0E, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateMlD0, fillCandidateMlD0, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -305,18 +320,6 @@ struct HfDerivedDataCreatorBplusToD0Pi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParD0, fillCandidateParD0, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateParD0E, fillCandidateParD0E, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateMlD0, fillCandidateMlD0, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { diff --git a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx index 9041d4d7c2e..ad8e52744e1 100644 --- a/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorD0ToKPi.cxx @@ -83,12 +83,14 @@ struct HfDerivedDataCreatorD0ToKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; SliceCache cache; static constexpr double Mass{o2::constants::physics::MassD0}; + static constexpr int NHypothesesCand{2}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -105,7 +107,7 @@ struct HfDerivedDataCreatorD0ToKPi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = aod::hf_sel_candidate_d0::isSelD0 >= 1 || aod::hf_sel_candidate_d0::isSelD0bar >= 1; - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesKfPerCollision = aod::hf_cand::collisionId; @@ -127,13 +129,13 @@ struct HfDerivedDataCreatorD0ToKPi { Partition candidatesMcMlAll = aod::hf_sel_candidate_d0::isSelD0 >= 0; Partition candidatesMcKfMlAll = aod::hf_sel_candidate_d0::isSelD0 >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); - Partition candidatesMcKfSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcKfSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcKfBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); - Partition candidatesMcKfMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); + Partition candidatesMcKfMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcKfMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK); void init(InitContext const&) @@ -150,6 +152,7 @@ struct HfDerivedDataCreatorD0ToKPi { void fillTablesCandidate(const T& candidate, int candFlag, double invMass, double cosThetaStar, double topoChi2, double ct, double y, int8_t flagMc, int8_t origin, const std::vector& mlScores) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { std::array, 2>, 2> sigmas{}; // PID nSigma [Expected][Hypothesis][TPC/TOF/TPC+TOF] @@ -248,20 +251,30 @@ struct HfDerivedDataCreatorD0ToKPi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -269,15 +282,6 @@ struct HfDerivedDataCreatorD0ToKPi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMc) { @@ -295,7 +299,7 @@ struct HfDerivedDataCreatorD0ToKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx index ddc56bfaebc..bf66abd0420 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDplusToPiKPi.cxx @@ -84,12 +84,14 @@ struct HfDerivedDataCreatorDplusToPiKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; SliceCache cache; static constexpr double Mass{o2::constants::physics::MassDPlus}; + static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -102,7 +104,7 @@ struct HfDerivedDataCreatorDplusToPiKPi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = (aod::hf_sel_candidate_dplus::isSelDplusToPiKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -116,9 +118,9 @@ struct HfDerivedDataCreatorDplusToPiKPi { Partition candidatesMlAll = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= 0; Partition candidatesMcMlAll = aod::hf_sel_candidate_dplus::isSelDplusToPiKPi >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi); void init(InitContext const&) @@ -134,6 +136,7 @@ struct HfDerivedDataCreatorDplusToPiKPi { void fillTablesCandidate(const T& candidate, int candFlag, double invMass, double ct, double y, int8_t flagMc, int8_t origin, int8_t swapping, int8_t flagDecayChan, const std::vector& mlScores) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -246,20 +249,30 @@ struct HfDerivedDataCreatorDplusToPiKPi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -267,15 +280,6 @@ struct HfDerivedDataCreatorDplusToPiKPi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0, swapping = 0, flagDecayChanRec = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { @@ -300,7 +304,7 @@ struct HfDerivedDataCreatorDplusToPiKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx index 587b967af0b..c30f037d976 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDsToKKPi.cxx @@ -82,12 +82,14 @@ struct HfDerivedDataCreatorDsToKKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; SliceCache cache; static constexpr double Mass{o2::constants::physics::MassDS}; + static constexpr int NHypothesesCand{2}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -99,8 +101,8 @@ struct HfDerivedDataCreatorDsToKKPi { using MatchedGenCandidatesMc = soa::Filtered>; using TypeMcCollisions = soa::Join; - Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); + Filter filterSelectCandidates = (aod::hf_sel_candidate_ds::isSelDsToKKPi & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0 || (aod::hf_sel_candidate_ds::isSelDsToPiKK & static_cast(BIT(aod::SelectionStep::RecoMl - 1))) != 0; // select candidates which passed all cuts at least up to RecoMl - 1 + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -114,9 +116,9 @@ struct HfDerivedDataCreatorDsToKKPi { Partition candidatesMlAll = aod::hf_sel_candidate_ds::isSelDsToKKPi >= 0; Partition candidatesMcMlAll = aod::hf_sel_candidate_ds::isSelDsToKKPi >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK); void init(InitContext const&) @@ -132,6 +134,7 @@ struct HfDerivedDataCreatorDsToKKPi { void fillTablesCandidate(const T& candidate, int candFlag, double invMass, double ct, double y, int8_t flagMc, int8_t origin, int8_t swapping, int8_t flagDecayChan, const std::vector& mlScores) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -229,20 +232,30 @@ struct HfDerivedDataCreatorDsToKKPi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -250,19 +263,10 @@ struct HfDerivedDataCreatorDsToKKPi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0, swapping = 0, flagDecayChanRec = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { - if (!TESTBIT(candidate.isSelDsToKKPi(), aod::SelectionStep::RecoMl)) { + if (!TESTBIT(candidate.isSelDsToKKPi(), aod::SelectionStep::RecoMl) && !TESTBIT(candidate.isSelDsToPiKK(), aod::SelectionStep::RecoMl)) { continue; } } @@ -283,7 +287,7 @@ struct HfDerivedDataCreatorDsToKKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK)) { continue; } } @@ -291,11 +295,20 @@ struct HfDerivedDataCreatorDsToKKPi { double const ct = HfHelper::ctDs(candidate); double const y = HfHelper::yDs(candidate); float const massDsToKKPi = HfHelper::invMassDsToKKPi(candidate); - std::vector mlScoresDsToKKPi; + float const massDsToPiKK = HfHelper::invMassDsToPiKK(candidate); + std::vector mlScoresDsToKKPi, mlScoresDsToPiKK; + auto selectionStep = aod::SelectionStep::RecoMl - 1; if constexpr (IsMl) { + selectionStep = aod::SelectionStep::RecoMl; std::copy(candidate.mlProbDsToKKPi().begin(), candidate.mlProbDsToKKPi().end(), std::back_inserter(mlScoresDsToKKPi)); + std::copy(candidate.mlProbDsToPiKK().begin(), candidate.mlProbDsToPiKK().end(), std::back_inserter(mlScoresDsToPiKK)); + } + if (TESTBIT(candidate.isSelDsToKKPi(), selectionStep)) { + fillTablesCandidate(candidate, 0, massDsToKKPi, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToKKPi); + } + if (TESTBIT(candidate.isSelDsToPiKK(), selectionStep)) { + fillTablesCandidate(candidate, 1, massDsToPiKK, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToPiKK); } - fillTablesCandidate(candidate, 0, massDsToKKPi, ct, y, flagMcRec, origin, swapping, flagDecayChanRec, mlScoresDsToKKPi); } } } diff --git a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx index a04dc0cf033..c73262f6572 100644 --- a/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorDstarToD0Pi.cxx @@ -80,12 +80,14 @@ struct HfDerivedDataCreatorDstarToD0Pi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; SliceCache cache; static constexpr double Mass{o2::constants::physics::MassDStar}; + static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -98,7 +100,7 @@ struct HfDerivedDataCreatorDstarToD0Pi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -112,9 +114,9 @@ struct HfDerivedDataCreatorDstarToD0Pi { Partition candidatesMlAll = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; Partition candidatesMcMlAll = aod::hf_sel_candidate_dstar::isSelDstarToD0Pi == true; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi); void init(InitContext const&) @@ -130,6 +132,7 @@ struct HfDerivedDataCreatorDstarToD0Pi { void fillTablesCandidate(const T& candidate, const U& prong0, const U& prong1, const U& prongSoftPi, int candFlag, double invMass, double invMassD0, double y, int8_t flagMc, int8_t flagMcD0, int8_t origin, int8_t nTracksDecayed, double ptBhad, int pdgBhad, const std::vector& mlScores) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -217,20 +220,30 @@ struct HfDerivedDataCreatorDstarToD0Pi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParD0, fillCandidateParD0, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -238,21 +251,12 @@ struct HfDerivedDataCreatorDstarToD0Pi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParD0, fillCandidateParD0, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, flagMcRecD0 = 0, origin = 0, nTracksDecayed = 0; double ptBhadMotherPart = 0; int pdgBhadMotherPart = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { - if (!TESTBIT(candidate.isSelDstarToD0Pi(), aod::SelectionStep::RecoMl)) { + if (!candidate.isSelDstarToD0Pi()) { continue; } } @@ -275,7 +279,7 @@ struct HfDerivedDataCreatorDstarToD0Pi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorLcToK0sP.cxx b/PWGHF/TableProducer/derivedDataCreatorLcToK0sP.cxx index 371b9ab9c4c..3f6a0b77fdb 100644 --- a/PWGHF/TableProducer/derivedDataCreatorLcToK0sP.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorLcToK0sP.cxx @@ -88,6 +88,7 @@ struct HfDerivedDataCreatorLcToK0sP { SliceCache cache; static constexpr double Mass{o2::constants::physics::MassLambdaCPlus}; + static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -132,6 +133,7 @@ struct HfDerivedDataCreatorLcToK0sP { void fillTablesCandidate(const T& candidate, const U& bach, int candFlag, double invMass, double ct, double ctV0, double y, int8_t flagMc, int8_t origin, const std::vector& mlScores) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -220,20 +222,30 @@ struct HfDerivedDataCreatorLcToK0sP { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -241,15 +253,6 @@ struct HfDerivedDataCreatorLcToK0sP { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMc) { diff --git a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx index efecd6f6713..c308143988c 100644 --- a/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorLcToPKPi.cxx @@ -82,12 +82,14 @@ struct HfDerivedDataCreatorLcToPKPi { Configurable fillCandidateMl{"fillCandidateMl", true, "Fill candidate selection ML scores"}; Configurable fillCandidateId{"fillCandidateId", true, "Fill original indices from the candidate table"}; Configurable fillCandidateMc{"fillCandidateMc", true, "Fill candidate MC info"}; + Configurable acceptCorrelatedBkgs{"acceptCorrelatedBkgs", false, "Accept correlated background candidates in MC processing"}; // Parameters for production of training samples Configurable downSampleBkgFactor{"downSampleBkgFactor", 1., "Fraction of background candidates to keep for ML trainings"}; Configurable ptMaxForDownSample{"ptMaxForDownSample", 10., "Maximum pt for the application of the downsampling factor"}; SliceCache cache; static constexpr double Mass{o2::constants::physics::MassLambdaCPlus}; + static constexpr int NHypothesesCand{2}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -100,7 +102,7 @@ struct HfDerivedDataCreatorLcToPKPi { using TypeMcCollisions = soa::Join; Filter filterSelectCandidates = aod::hf_sel_candidate_lc::isSelLcToPKPi >= 1 || aod::hf_sel_candidate_lc::isSelLcToPiKP >= 1; - Filter filterMcGenMatching = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + Filter filterMcGenMatching = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi)) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchGen != 0); Preslice candidatesPerCollision = aod::hf_cand::collisionId; Preslice candidatesMcPerCollision = aod::hf_cand::collisionId; @@ -114,9 +116,9 @@ struct HfDerivedDataCreatorLcToPKPi { Partition candidatesMlAll = aod::hf_sel_candidate_lc::isSelLcToPKPi >= 0; Partition candidatesMcMlAll = aod::hf_sel_candidate_lc::isSelLcToPKPi >= 0; // partitions for signal and background - Partition candidatesMcSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + Partition candidatesMcSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec)) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); - Partition candidatesMcMlSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); + Partition candidatesMcMlSig = (nabs(aod::hf_cand_mc_flag::flagMcMatchRec)) == static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) || (acceptCorrelatedBkgs && aod::hf_cand_mc_flag::flagMcMatchRec != 0); Partition candidatesMcMlBkg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi); void init(InitContext const&) @@ -132,6 +134,7 @@ struct HfDerivedDataCreatorLcToPKPi { void fillTablesCandidate(const T& candidate, int candFlag, double invMass, double ct, double y, int8_t flagMc, int8_t origin, int8_t swapping, const std::vector& mlScores) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -228,20 +231,30 @@ struct HfDerivedDataCreatorLcToPKPi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -249,15 +262,6 @@ struct HfDerivedDataCreatorLcToPKPi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0, swapping = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMc) { @@ -276,7 +280,7 @@ struct HfDerivedDataCreatorLcToPKPi { } } if constexpr (OnlySig) { - if (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { + if (!acceptCorrelatedBkgs && (std::abs(flagMcRec) != hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi)) { continue; } } diff --git a/PWGHF/TableProducer/derivedDataCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/derivedDataCreatorXicToXiPiPi.cxx index 9e0529b97d6..fdce0301529 100644 --- a/PWGHF/TableProducer/derivedDataCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/derivedDataCreatorXicToXiPiPi.cxx @@ -89,6 +89,7 @@ struct HfDerivedDataCreatorXicToXiPiPi { SliceCache cache; static constexpr double Mass{o2::constants::physics::MassXiCPlus}; + static constexpr int NHypothesesCand{1}; // Number of possible selection hypotheses per candidate. using CollisionsWCentMult = soa::Join; using CollisionsWMcCentMult = soa::Join; @@ -134,6 +135,7 @@ struct HfDerivedDataCreatorXicToXiPiPi { void fillTablesCandidate(const T& candidate, int candFlag, double invMass, double ct, double y, int8_t flagMc, int8_t origin, const std::vector& mlScores) { + LOGF(debug, "Filling candidate at derived index %d", rowsCommon.rowCandidateBase.lastIndex() + 1); rowsCommon.fillTablesCandidate(candidate, invMass, y); if (fillCandidatePar) { rowCandidatePar( @@ -228,20 +230,30 @@ struct HfDerivedDataCreatorXicToXiPiPi { rowsCommon.matchedCollisions.clear(); } } - auto sizeTableColl = collisions.size(); - rowsCommon.reserveTablesColl(sizeTableColl); + // const auto sizeTableColl = collisions.size(); + // rowsCommon.reserveTablesColl(sizeTableColl); + const auto sizeTableCand = candidates.size() * NHypothesesCand; + rowsCommon.reserveTablesCandidates(sizeTableCand); + reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); + reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); + reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); + reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); + reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); + if constexpr (IsMc) { + reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); + } for (const auto& collision : collisions) { - auto thisCollId = collision.globalIndex(); - auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME - auto sizeTableCand = candidatesThisColl.size(); - LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCand); + const auto thisCollId = collision.globalIndex(); + const auto candidatesThisColl = candidates->sliceByCached(aod::hf_cand::collisionId, thisCollId, cache); // FIXME + const auto sizeTableCandThisColl = candidatesThisColl.size(); + LOGF(debug, "Rec. collision %d has %d candidates", thisCollId, sizeTableCandThisColl); // Skip collisions without HF candidates (and without HF particles in matched MC collisions if saving indices of reconstructed collisions matched to MC collisions) bool mcCollisionHasMcParticles{false}; if constexpr (IsMc) { mcCollisionHasMcParticles = confDerData.fillMcRCollId && collision.has_mcCollision() && rowsCommon.hasMcParticles[collision.mcCollisionId()]; LOGF(debug, "Rec. collision %d has MC collision %d with MC particles? %s", thisCollId, collision.mcCollisionId(), mcCollisionHasMcParticles ? "yes" : "no"); } - if (sizeTableCand == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { + if (sizeTableCandThisColl == 0 && (!confDerData.fillMcRCollId || !mcCollisionHasMcParticles)) { LOGF(debug, "Skipping rec. collision %d", thisCollId); continue; } @@ -249,15 +261,6 @@ struct HfDerivedDataCreatorXicToXiPiPi { rowsCommon.fillTablesCollision(collision); // Fill candidate properties - rowsCommon.reserveTablesCandidates(sizeTableCand); - reserveTable(rowCandidatePar, fillCandidatePar, sizeTableCand); - reserveTable(rowCandidateParE, fillCandidateParE, sizeTableCand); - reserveTable(rowCandidateSel, fillCandidateSel, sizeTableCand); - reserveTable(rowCandidateMl, fillCandidateMl, sizeTableCand); - reserveTable(rowCandidateId, fillCandidateId, sizeTableCand); - if constexpr (IsMc) { - reserveTable(rowCandidateMc, fillCandidateMc, sizeTableCand); - } int8_t flagMcRec = 0, origin = 0; for (const auto& candidate : candidatesThisColl) { if constexpr (IsMl) { diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index c0c6217607c..78dda49a10c 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -18,6 +18,7 @@ #include "Common/Core/CollisionTypeHelper.h" #include "Common/Core/MetadataHelper.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" @@ -40,7 +41,6 @@ #include #include #include -#include #include #include @@ -573,7 +573,7 @@ struct TofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -639,7 +639,7 @@ struct TofEventTime { } tableEvTime(eventTime / sumOfWeights, std::sqrt(1. / sumOfWeights)); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); } } } @@ -650,7 +650,7 @@ struct TofEventTime { tableFlags(0); tableEvTime(0.f, 999.f); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } continue; } @@ -682,14 +682,14 @@ struct TofEventTime { tableFlags(flags); tableEvTime(et, erret); if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); + tableEvTimeTOFOnly(static_cast(filterForTOFEventTime(trk)), et, erret, evTimeMakerTOF.mEventTimeMultiplicity); } } } } else if (mComputeEvTimeWithTOF == 0 && mComputeEvTimeWithFT0 == 1) { for (auto const& t : tracks) { // Loop on collisions if (enableTableEvTimeTOFOnly) { - tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + tableEvTimeTOFOnly(static_cast(0), 0.f, 0.f, -1); } if (!t.has_collision()) { // Track was not assigned, cannot compute event time tableFlags(0); @@ -973,16 +973,16 @@ struct McPidTof { auto* calibList = ccdb->getSpecific(mcRecalib.ccdbPath, timestamp, metadata); std::vector updatedSpecies{}; for (auto const& pidId : mEnabledParticles) { // Loop on enabled particle hypotheses (tiny) - gMcPostCalibMean[pidId] = reinterpret_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); - gMcPostCalibSigma[pidId] = reinterpret_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); + gMcPostCalibMean[pidId] = dynamic_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); + gMcPostCalibSigma[pidId] = dynamic_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); updatedSpecies.push_back(pidId); } for (auto const& pidId : mEnabledParticlesFull) { // Loop on enabled particle hypotheses (full) if (std::find(updatedSpecies.begin(), updatedSpecies.end(), pidId) != updatedSpecies.end()) { continue; } - gMcPostCalibMean[pidId] = reinterpret_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); - gMcPostCalibSigma[pidId] = reinterpret_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); + gMcPostCalibMean[pidId] = dynamic_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); + gMcPostCalibSigma[pidId] = dynamic_cast(calibList->FindObject(Form("Sigma%s", particleNames[pidId].data()))); } } diff --git a/PWGHF/TableProducer/trackIndexSkimCreator.cxx b/PWGHF/TableProducer/trackIndexSkimCreator.cxx index 16ca600001b..6f00d719a0b 100644 --- a/PWGHF/TableProducer/trackIndexSkimCreator.cxx +++ b/PWGHF/TableProducer/trackIndexSkimCreator.cxx @@ -39,6 +39,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -83,6 +84,7 @@ #include #include // std::distance #include +#include #include // std::string #include // std::forward #include // std::vector @@ -172,9 +174,9 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { } // set numerical value of the Run 2 trigger class - auto* const triggerAlias = std::find(aliasLabels, aliasLabels + kNaliases, triggerClassName.value.data()); - if (triggerAlias != aliasLabels + kNaliases) { - hfEvSel.triggerClass.value = std::distance(aliasLabels, triggerAlias); + auto* const triggerAlias = std::find(aliasLabels.begin(), aliasLabels.end(), triggerClassName.value.data()); + if (triggerAlias != aliasLabels.end()) { + hfEvSel.triggerClass.value = std::distance(aliasLabels.begin(), triggerAlias); } hfEvSel.init(registry, &zorroSummary); // collision monitoring @@ -222,7 +224,7 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { /// Event selection with trigger and FT0A centrality selection void processTrigAndCentFT0ASel(soa::Join::iterator const& collision, + aod::EvSels, aod::PVMults, aod::CentFT0As>::iterator const& collision, aod::BcFullInfos const& bcs) { selectCollision(collision, bcs); @@ -231,7 +233,7 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { /// Event selection with trigger and FT0C centrality selection void processTrigAndCentFT0CSel(soa::Join::iterator const& collision, + aod::EvSels, aod::PVMults, aod::CentFT0Cs>::iterator const& collision, aod::BcFullInfos const& bcs) { selectCollision(collision, bcs); @@ -240,7 +242,7 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { /// Event selection with trigger and FT0M centrality selection void processTrigAndCentFT0MSel(soa::Join::iterator const& collision, + aod::EvSels, aod::PVMults, aod::CentFT0Ms>::iterator const& collision, aod::BcFullInfos const& bcs) { selectCollision(collision, bcs); @@ -249,7 +251,7 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { /// Event selection with trigger and FV0A centrality selection void processTrigAndCentFV0ASel(soa::Join::iterator const& collision, + aod::EvSels, aod::PVMults, aod::CentFV0As>::iterator const& collision, aod::BcFullInfos const& bcs) { selectCollision(collision, bcs); @@ -258,7 +260,7 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { /// Event selection with trigger selection void processTrigSel(soa::Join::iterator const& collision, + aod::EvSels, aod::PVMults>::iterator const& collision, aod::BcFullInfos const& bcs) { selectCollision(collision, bcs); @@ -266,7 +268,7 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelCollisions, processTrigSel, "Use trigger selection", false); /// Event selection without trigger selection - void processNoTrigSel(aod::Collision const& collision, + void processNoTrigSel(soa::Join::iterator const& collision, aod::BcFullInfos const& bcs) { selectCollision(collision, bcs); @@ -274,7 +276,7 @@ struct HfTrackIndexSkimCreatorTagSelCollisions { PROCESS_SWITCH(HfTrackIndexSkimCreatorTagSelCollisions, processNoTrigSel, "Do not use trigger selection", true); /// Event selection with UPC - void processUpcSel(soa::Join::iterator const& collision, + void processUpcSel(soa::Join::iterator const& collision, aod::BcFullInfos const& bcs, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0as*/, @@ -502,7 +504,7 @@ struct HfTrackIndexSkimCreatorTagSelTracks { selectorKaon.setRangeNSigmaTof(-config.selectionsPid->get(ChannelKaonPid, 5u), config.selectionsPid->get(ChannelKaonPid, 5u)); // 5u == "nSigmaMaxTof" } - /// Apply track-quality (ITS/TPC) + optional ITS-PID preselection for light-nucleus daughters used in charm-nuclei 3-prong channels (Cd/Ct/Ch). + /// Apply track-quality (ITS/TPC) + optional ITS-PID preselection for light-nucleus daughters used in charm-nuclei 3-prong channels (Cd/Ct/Ch/Ca). /// \tparam TrackType Track providing ITS/TPC quality accessors. /// \param track Daughter track to be tested (either prong0 or prong2). /// \param lightnuclei Species selector: 0=Deuteron, 1=Triton, 2=Helium3. @@ -1504,6 +1506,8 @@ struct HfTrackIndexSkimCreator { std::array, kN2ProngDecays> binsPt2Prong{}; std::array, kN3ProngDecays> cut3Prong{}; std::array, kN3ProngDecays> binsPt3Prong{}; + LabeledArray cutsDstar{}; + std::vector binsPtDstar{}; // ML response o2::analysis::MlResponse hfMlResponse2Prongs; // only D0 @@ -1582,6 +1586,9 @@ struct HfTrackIndexSkimCreator { // cuts for 3-prong decays retrieved by json. the order must be then one in hf_cand_3prong::DecayType cut3Prong = {config.cutsDplusToPiKPi, config.cutsLcToPKPi, config.cutsDsToKKPi, config.cutsXicToPKPi, config.cutsCdToDeKPi, config.cutsCtToTrKPi, config.cutsChToHeKPi, config.cutsCaToAlKPi}; binsPt3Prong = {config.binsPtDplusToPiKPi, config.binsPtLcToPKPi, config.binsPtDsToKKPi, config.binsPtXicToPKPi, config.binsPtCdToDeKPi, config.binsPtCtToTrKPi, config.binsPtChToHeKPi, config.binsPtCaToAlKPi}; + // D* cuts retrieved by json + cutsDstar = config.cutsDstarToD0Pi; + binsPtDstar = config.binsPtDstarToD0Pi; df2.setPropagateToPCA(config.propagateToPCA); df2.setMaxR(config.maxR); @@ -1722,6 +1729,70 @@ struct HfTrackIndexSkimCreator { } } + /// One track of the collision under study, propagated to that collision's primary vertex. + /// \tparam TTrackIndex is the iterator type of the track-collision associations + template + struct HfPropagatedSoftPion { + TTrackIndex trackIndex; ///< track-collision association, carries isSelProng and isIdentifiedPid + int64_t trackGlobalIndex{-1}; ///< track global index + std::array pVec{}; ///< momentum at the PCA to the primary vertex + }; + + /// One track of the collision under study, propagated to that collision's primary vertex. + /// \tparam TTrackIndex is the iterator type of the track-collision associations + /// \tparam TTrack is the iterator type of the track table + template + struct HfPropagatedProng { + TTrackIndex trackIndex; ///< track-collision association, carries isSelProng and isIdentifiedPid + TTrack track; ///< track row + o2::track::TrackParCov trackParVar; ///< track parameters at the PCA to the primary vertex + std::array pVec{}; ///< momentum at the PCA to the primary vertex + std::array dcaInfo{}; ///< DCA (xy, z) to the primary vertex + }; + + /// Fill the per-collision cache of tracks propagated to the collision's primary vertex, so that + /// each track is propagated once per collision instead of once per pair or triplet. + /// \param collision is the collision under study + /// \param trackIndices are the track associations of this collision + /// \param prongs is the cache to be filled, in the order of trackIndices + template + void fillPropagatedProngCache(TCollision const& collision, TTrackIndices const& trackIndices, std::vector& prongs) + { + prongs.clear(); + const auto thisCollId = collision.globalIndex(); + for (auto trackIndex = trackIndices.begin(); trackIndex != trackIndices.end(); ++trackIndex) { + const auto track = trackIndex.template track_as(); + if constexpr (IsSoftPion) { + std::array pVec = track.pVector(); + if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it + auto trackParCov = getTrackParCov(track); + std::array dcaInfo{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParCov, 2.f, noMatCorr, &dcaInfo); + getPxPyPz(trackParCov, pVec); + } + prongs.push_back(TProng{trackIndex, track.globalIndex(), pVec}); + } else { + auto& prong = prongs.emplace_back(TProng{trackIndex, track, getTrackParCov(track), track.pVector(), {track.dcaXY(), track.dcaZ()}}); + if (thisCollId != track.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, prong.trackParVar, 2.f, noMatCorr, &prong.dcaInfo); + getPxPyPz(prong.trackParVar, prong.pVec); + } + } + } + } + + /// Overloaded function of fillPropagatedProngCache to handle optional track indices. + /// \param collision is the collision under study + /// \param trackIndices are the track associations of this collision + /// \param prongs is the cache to be filled, in the order of trackIndices + template + void fillPropagatedProngCache(TCollision const& collision, std::optional const& trackIndices, std::vector& prongs) + { + if (trackIndices.has_value()) { + fillPropagatedProngCache(collision, *trackIndices, prongs); + } + } + /// Method to perform selections for 2-prong candidates before vertex reconstruction /// \param pVecTrack0 is the momentum array of the first daughter track /// \param pVecTrack1 is the momentum array of the second daughter track @@ -1793,10 +1864,10 @@ struct HfTrackIndexSkimCreator { // additional check for D0 to be used in D* finding if (iDecay2P == hf_cand_2prong::DecayType::D0ToPiK && config.doDstar && TESTBIT(isSelected, iDecay2P)) { - const auto binPtDstar = findBin(config.binsPtDstarToD0Pi, pt * 1.2); // assuming the D* pT about 20% higher than the one of the D0 to be safe + const auto binPtDstar = findBin(&binsPtDstar, pt * 1.2); // assuming the D* pT about 20% higher than the one of the D0 to be safe if (binPtDstar >= 0) { whichHypo[kN2ProngDecays] = whichHypo[hf_cand_2prong::DecayType::D0ToPiK]; - const double deltaMass = config.cutsDstarToD0Pi->get(binPtDstar, 1u); + const double deltaMass = cutsDstar.get(binPtDstar, 1u); if (TESTBIT(whichHypo[iDecay2P], 0) && (massHypos[0] > (MassD0 + deltaMass) * (MassD0 + deltaMass) || massHypos[0] < (MassD0 - deltaMass) * (MassD0 - deltaMass))) { CLRBIT(whichHypo[kN2ProngDecays], 0); @@ -1820,23 +1891,23 @@ struct HfTrackIndexSkimCreator { template void applyPreselectionPhiDecay(const int binPt, T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, T2& cutStatus, T3& whichHypo, auto& isSelected) { - const double deltaMassMax = cut3Prong[hf_cand_3prong::DecayType::DsToKKPi].get(binPt, 4u); + const double deltaMassMax = cut3Prong[hf_cand_3prong::DecayType::DsToKKPi].get(binPt, 5u); if (TESTBIT(whichHypo[hf_cand_3prong::DecayType::DsToKKPi], 0)) { const double mass2PhiKKPi = RecoDecay::m2(std::array{pVecTrack0, pVecTrack1}, std::array{arrMass3Prong[hf_cand_3prong::DecayType::DsToKKPi][0][0], arrMass3Prong[hf_cand_3prong::DecayType::DsToKKPi][0][1]}); - if (mass2PhiKKPi > (MassPhi + deltaMassMax) * (MassPhi + deltaMassMax) || mass2PhiKKPi < (MassPhi - deltaMassMax) * (MassPhi - deltaMassMax)) { + if (mass2PhiKKPi > (MassPhi + deltaMassMax) * (MassPhi + deltaMassMax) || (deltaMassMax < MassPhi && mass2PhiKKPi < (MassPhi - deltaMassMax) * (MassPhi - deltaMassMax))) { CLRBIT(whichHypo[hf_cand_3prong::DecayType::DsToKKPi], 0); } } if (TESTBIT(whichHypo[hf_cand_3prong::DecayType::DsToKKPi], 1)) { const double mass2PhiPiKK = RecoDecay::m2(std::array{pVecTrack1, pVecTrack2}, std::array{arrMass3Prong[hf_cand_3prong::DecayType::DsToKKPi][1][1], arrMass3Prong[hf_cand_3prong::DecayType::DsToKKPi][1][2]}); - if (mass2PhiPiKK > (MassPhi + deltaMassMax) * (MassPhi + deltaMassMax) || mass2PhiPiKK < (MassPhi - deltaMassMax) * (MassPhi - deltaMassMax)) { + if (mass2PhiPiKK > (MassPhi + deltaMassMax) * (MassPhi + deltaMassMax) || (deltaMassMax < MassPhi && mass2PhiPiKK < (MassPhi - deltaMassMax) * (MassPhi - deltaMassMax))) { CLRBIT(whichHypo[hf_cand_3prong::DecayType::DsToKKPi], 1); } } if (whichHypo[hf_cand_3prong::DecayType::DsToKKPi] == 0) { CLRBIT(isSelected, hf_cand_3prong::DecayType::DsToKKPi); if (config.debug) { - cutStatus[hf_cand_3prong::DecayType::DsToKKPi][4] = false; + cutStatus[hf_cand_3prong::DecayType::DsToKKPi][5] = false; } } } @@ -1848,7 +1919,7 @@ struct HfTrackIndexSkimCreator { /// \param isIdentifiedPidTrack0 is the flag that tells if the track 0 has been tagged as a proton /// \param isIdentifiedPidTrack2 is the flag that tells if the track 2 has been tagged as a proton /// \param cutStatus is a 2D array with outcome of each selection (filled only in debug mode) - /// \param whichHypo information of the mass hypoteses that were selected + /// \param whichHypo information of the mass hypotheses that were selected /// \param isSelected is a bitmap with selection outcome template void applyPreselection3Prong(T2 const& pVecTrack0, T2 const& pVecTrack1, T2 const& pVecTrack2, const auto isIdentifiedPidTrack0, const auto isIdentifiedPidTrack2, T3& cutStatus, T4& whichHypo, auto& isSelected) @@ -1889,7 +1960,7 @@ struct HfTrackIndexSkimCreator { } // invariant mass - if ((config.debug || TESTBIT(isSelected, iDecay3P))) { + if (config.debug || TESTBIT(isSelected, iDecay3P)) { const double minMass = cut3Prong[iDecay3P].get(binPt, 0u); const double maxMass = cut3Prong[iDecay3P].get(binPt, 1u); if (minMass >= 0. && maxMass > 0.) { // no need to check isSelected but to avoid mistakes @@ -1914,6 +1985,18 @@ struct HfTrackIndexSkimCreator { } } + // prong pT + if (config.debug || TESTBIT(isSelected, iDecay3P)) { + const auto ptProngMin = cut3Prong[iDecay3P].get(binPt, 4u); // 4u == ptProngMinIndex[iDecay3P] + const auto pt2ProngMin = ptProngMin * ptProngMin; + if (RecoDecay::pt2(pVecTrack0) < pt2ProngMin || RecoDecay::pt2(pVecTrack1) < pt2ProngMin || RecoDecay::pt2(pVecTrack2) < pt2ProngMin) { + CLRBIT(isSelected, iDecay3P); + if (config.debug) { + cutStatus[iDecay3P][4] = false; + } + } + } + if ((config.debug || TESTBIT(isSelected, iDecay3P)) && iDecay3P == hf_cand_3prong::DecayType::DsToKKPi) { applyPreselectionPhiDecay(binPt, pVecTrack0, pVecTrack1, pVecTrack2, cutStatus, whichHypo, isSelected); } @@ -2029,7 +2112,7 @@ struct HfTrackIndexSkimCreator { } // decay length - if ((config.debug || TESTBIT(isSelected, iDecay3P))) { + if (config.debug || TESTBIT(isSelected, iDecay3P)) { const auto decayLength = RecoDecay::distance(primVtx, secVtx); if (decayLength < cut3Prong[iDecay3P].get(binPt, 3u)) { // 3u == decLenIndex[iDecay3P] CLRBIT(isSelected, iDecay3P); @@ -2049,7 +2132,7 @@ struct HfTrackIndexSkimCreator { /// \param outputScores is the array of vectors with the output scores to be filled /// \param isSelected ia s bitmap with selection outcome template - void applyMlSelectionForHfFilters3Prong(std::vector featuresCand, std::vector featuresCandPid, std::array, kN3ProngDecaysUsedMlForHfFilters>& outputScores, auto& isSelected) + void applyMlSelectionForHfFilters3Prong(std::vector featuresCand, const std::vector& featuresCandPid, std::array, kN3ProngDecaysUsedMlForHfFilters>& outputScores, auto& isSelected) { if (isSelected == 0) { return; @@ -2110,18 +2193,18 @@ struct HfTrackIndexSkimCreator { /// \param pVecTrack1 is the momentum array of the second daughter track (opposite charge) /// \param pVecTrack2 is the momentum array of the third daughter track (same charge) /// \param cutStatus is the cut status (filled only in case of debug) + /// \param invMassD0 is the invariant mass of the D0 candidate /// \param deltaMass is the M(Kpipi) - M(Kpi) value to be filled in the control histogram /// \return a bitmap with selection outcome template - uint8_t applySelectionDstar(T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, uint8_t& cutStatus, T2& deltaMass) + uint8_t applySelectionDstar(T1 const& pVecTrack0, T1 const& pVecTrack1, T1 const& pVecTrack2, uint8_t& cutStatus, T2 invMassD0, T2& deltaMass) { uint8_t isSelected{1}; const std::array arrMom{pVecTrack0, pVecTrack1, pVecTrack2}; - const std::array arrMomD0{pVecTrack0, pVecTrack1}; const auto pt = RecoDecay::pt(pVecTrack0, pVecTrack1, pVecTrack2) + config.ptTolerance; // add tolerance because of no reco decay vertex // pT - const auto binPt = findBin(config.binsPtDstarToD0Pi, pt); + const auto binPt = findBin(&binsPtDstar, pt); // return immediately if it is outside the defined pT bins if (binPt == -1) { isSelected = 0; @@ -2132,8 +2215,7 @@ struct HfTrackIndexSkimCreator { } // D0 mass - const double deltaMassD0 = config.cutsDstarToD0Pi->get(binPt, 1u); // 1u == deltaMassD0Index - const double invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + const double deltaMassD0 = cutsDstar.get(binPt, 1u); // 1u == deltaMassD0Index if (std::abs(invMassD0 - MassD0) > deltaMassD0) { isSelected = 0; if (config.debug) { @@ -2143,7 +2225,7 @@ struct HfTrackIndexSkimCreator { } // D*+ mass - const double maxDeltaMass = config.cutsDstarToD0Pi->get(binPt, 0u); // 0u == deltaMassIndex + const double maxDeltaMass = cutsDstar.get(binPt, 0u); // 0u == deltaMassIndex const double invMassDstar = RecoDecay::m(arrMom, std::array{MassPiPlus, MassKPlus, MassPiPlus}); deltaMass = invMassDstar - invMassD0; if (deltaMass > maxDeltaMass) { @@ -2305,8 +2387,22 @@ struct HfTrackIndexSkimCreator { } */ + // per-collision caches of the tracks propagated to the primary vertex, declared here so that their capacity is reused + using TrackIndexIterator = decltype(positiveFor2And3Prongs->sliceByCached(aod::track::collisionId, 0, cache).begin()); + using PropagatedProng = HfPropagatedProng; + std::vector prongsPos{}; + std::vector prongsNeg{}; + using SoftPionIterator = decltype(positiveSoftPions->sliceByCached(aod::track::collisionId, 0, cache).begin()); + using PropagatedSoftPi = HfPropagatedSoftPion; + std::vector softPionsPos{}; + std::vector softPionsNeg{}; + + bool softPiPosCacheFilled{false}, softPiNegCacheFilled{false}; for (const auto& collision : collisions) { + softPiPosCacheFilled = false; + softPiNegCacheFilled = false; + /// retrieve PV contributors for the current collision std::vector vecPvContributorGlobId{}; std::vector vecPvContributorTrackParCov{}; @@ -2380,41 +2476,40 @@ struct HfTrackIndexSkimCreator { // first loop over positive tracks const auto groupedTrackIndicesPos1 = positiveFor2And3Prongs->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + const auto groupedTrackIndicesNeg1 = negativeFor2And3Prongs->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + std::optionalsliceByCached(aod::track::collisionId, 0, cache))> groupedTrackIndicesSoftPionsPos; + std::optionalsliceByCached(aod::track::collisionId, 0, cache))> groupedTrackIndicesSoftPionsNeg; int lastFilledD0 = -1; // index to be filled in table for D* mesons - for (auto trackIndexPos1 = groupedTrackIndicesPos1.begin(); trackIndexPos1 != groupedTrackIndicesPos1.end(); ++trackIndexPos1) { - const auto trackPos1 = trackIndexPos1.template track_as(); + + // propagate each track to this collision's PV once, instead of once per pair or triplet + fillPropagatedProngCache(collision, groupedTrackIndicesPos1, prongsPos); + fillPropagatedProngCache(collision, groupedTrackIndicesNeg1, prongsNeg); + + for (std::size_t iPos1 = 0; iPos1 < prongsPos.size(); ++iPos1) { + const auto& trackIndexPos1 = prongsPos[iPos1].trackIndex; + const auto& trackPos1 = prongsPos[iPos1].track; + const auto& trackParVarPos1 = prongsPos[iPos1].trackParVar; + const auto& pVecTrackPos1 = prongsPos[iPos1].pVec; + const auto& dcaInfoPos1 = prongsPos[iPos1].dcaInfo; // retrieve the selection flag that corresponds to this collision const auto isSelProngPos1 = trackIndexPos1.isSelProng(); const bool sel2ProngStatusPos = TESTBIT(isSelProngPos1, CandidateType::Cand2Prong); const bool sel3ProngStatusPos1 = TESTBIT(isSelProngPos1, CandidateType::Cand3Prong); - auto trackParVarPos1 = getTrackParCov(trackPos1); - std::array pVecTrackPos1{trackPos1.pVector()}; - std::array dcaInfoPos1{trackPos1.dcaXY(), trackPos1.dcaZ()}; - if (thisCollId != trackPos1.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarPos1, 2.f, noMatCorr, &dcaInfoPos1); - getPxPyPz(trackParVarPos1, pVecTrackPos1); - } - // first loop over negative tracks - const auto groupedTrackIndicesNeg1 = negativeFor2And3Prongs->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - for (auto trackIndexNeg1 = groupedTrackIndicesNeg1.begin(); trackIndexNeg1 != groupedTrackIndicesNeg1.end(); ++trackIndexNeg1) { - const auto trackNeg1 = trackIndexNeg1.template track_as(); + for (std::size_t iNeg1 = 0; iNeg1 < prongsNeg.size(); ++iNeg1) { + const auto& trackIndexNeg1 = prongsNeg[iNeg1].trackIndex; + const auto& trackNeg1 = prongsNeg[iNeg1].track; + const auto& trackParVarNeg1 = prongsNeg[iNeg1].trackParVar; + const auto& pVecTrackNeg1 = prongsNeg[iNeg1].pVec; + const auto& dcaInfoNeg1 = prongsNeg[iNeg1].dcaInfo; // retrieve the selection flag that corresponds to this collision const auto isSelProngNeg1 = trackIndexNeg1.isSelProng(); const bool sel2ProngStatusNeg = TESTBIT(isSelProngNeg1, CandidateType::Cand2Prong); const bool sel3ProngStatusNeg1 = TESTBIT(isSelProngNeg1, CandidateType::Cand3Prong); - auto trackParVarNeg1 = getTrackParCov(trackNeg1); - std::array pVecTrackNeg1{trackNeg1.pVector()}; - std::array dcaInfoNeg1{trackNeg1.dcaXY(), trackNeg1.dcaZ()}; - if (thisCollId != trackNeg1.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarNeg1, 2.f, noMatCorr, &dcaInfoNeg1); - getPxPyPz(trackParVarNeg1, pVecTrackNeg1); - } - uint isSelected2ProngCand = n2ProngBit; // bitmap for checking status of two-prong candidates (1 is true, 0 is rejected) if (config.debug) { @@ -2622,7 +2717,8 @@ struct HfTrackIndexSkimCreator { if (config.do3Prong && is2ProngCandidateGoodFor3Prong) { // if 3 prongs are enabled and the first 2 tracks are selected for the 3-prong channels // second loop over positive tracks - for (auto trackIndexPos2 = trackIndexPos1 + 1; trackIndexPos2 != groupedTrackIndicesPos1.end(); ++trackIndexPos2) { + for (std::size_t iPos2 = iPos1 + 1; iPos2 < prongsPos.size(); ++iPos2) { + const auto& trackIndexPos2 = prongsPos[iPos2].trackIndex; uint isSelected3ProngCand = n3ProngBit; if (!TESTBIT(trackIndexPos2.isSelProng(), CandidateType::Cand3Prong)) { // continue immediately @@ -2639,18 +2735,13 @@ struct HfTrackIndexSkimCreator { isSelected3ProngCand = 0; } - const auto trackPos2 = trackIndexPos2.template track_as(); - - auto trackParVarPos2 = getTrackParCov(trackPos2); - std::array dcaInfoPos2{trackPos2.dcaXY(), trackPos2.dcaZ()}; + const auto& trackPos2 = prongsPos[iPos2].track; + const auto& trackParVarPos2 = prongsPos[iPos2].trackParVar; + const auto& dcaInfoPos2 = prongsPos[iPos2].dcaInfo; // preselection of 3-prong candidates if (isSelected3ProngCand) { - std::array pVecTrackPos2{trackPos2.pVector()}; - if (thisCollId != trackPos2.collisionId()) { // this is not the "default" collision for this track and we still did not re-propagate it, we have to re-propagate it - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarPos2, 2.f, noMatCorr, &dcaInfoPos2); - getPxPyPz(trackParVarPos2, pVecTrackPos2); - } + const auto& pVecTrackPos2 = prongsPos[iPos2].pVec; if (config.debug) { for (int iDecay3P = 0; iDecay3P < kN3ProngDecays; iDecay3P++) { @@ -2897,7 +2988,8 @@ struct HfTrackIndexSkimCreator { } // second loop over negative tracks - for (auto trackIndexNeg2 = trackIndexNeg1 + 1; trackIndexNeg2 != groupedTrackIndicesNeg1.end(); ++trackIndexNeg2) { + for (std::size_t iNeg2 = iNeg1 + 1; iNeg2 < prongsNeg.size(); ++iNeg2) { + const auto& trackIndexNeg2 = prongsNeg[iNeg2].trackIndex; int isSelected3ProngCand = n3ProngBit; if (!TESTBIT(trackIndexNeg2.isSelProng(), CandidateType::Cand3Prong)) { // continue immediately @@ -2914,17 +3006,13 @@ struct HfTrackIndexSkimCreator { isSelected3ProngCand = 0; } - auto trackNeg2 = trackIndexNeg2.template track_as(); - auto trackParVarNeg2 = getTrackParCov(trackNeg2); - std::array dcaInfoNeg2{trackNeg2.dcaXY(), trackNeg2.dcaZ()}; + const auto& trackNeg2 = prongsNeg[iNeg2].track; + const auto& trackParVarNeg2 = prongsNeg[iNeg2].trackParVar; + const auto& dcaInfoNeg2 = prongsNeg[iNeg2].dcaInfo; // preselection of 3-prong candidates if (isSelected3ProngCand) { - std::array pVecTrackNeg2{trackNeg2.pVector()}; - if (thisCollId != trackNeg2.collisionId()) { // this is not the "default" collision for this track and we still did not re-propagate it, we have to re-propagate it - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarNeg2, 2.f, noMatCorr, &dcaInfoNeg2); - getPxPyPz(trackParVarNeg2, pVecTrackNeg2); - } + const auto& pVecTrackNeg2 = prongsNeg[iNeg2].pVec; if (config.debug) { for (int iDecay3P = 0; iDecay3P < kN3ProngDecays; iDecay3P++) { @@ -3172,30 +3260,35 @@ struct HfTrackIndexSkimCreator { } } - if (config.doDstar && TESTBIT(isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance) * 1.2 > config.binsPtDstarToD0Pi->at(0) && whichHypo2Prong[kN2ProngDecays] != 0) { // o2-linter: disable="magic-number" (see comment below) - // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8) + if (config.doDstar && TESTBIT(isSelected2ProngCand, hf_cand_2prong::DecayType::D0ToPiK) && (pt2Prong + config.ptTolerance) * 1.2 > binsPtDstar[0] && whichHypo2Prong[kN2ProngDecays] != 0) { // o2-linter: disable="magic-number" (see comment below) + // if D* enabled and pt of the D0 is larger than the minimum of the D* one within 20% (D* and D0 momenta are very similar, always within 20% according to PYTHIA8) // second loop over positive tracks if (TESTBIT(whichHypo2Prong[kN2ProngDecays], 0) && (!config.applyKaonPidIn3Prongs || TESTBIT(trackIndexNeg1.isIdentifiedPid(), ChannelKaonPid))) { // only for D0 candidates; moreover if kaon PID enabled, apply to the negative track - auto groupedTrackIndicesSoftPionsPos = positiveSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - for (auto trackIndexPos2 = groupedTrackIndicesSoftPionsPos.begin(); trackIndexPos2 != groupedTrackIndicesSoftPionsPos.end(); ++trackIndexPos2) { - if (trackIndexPos2 == trackIndexPos1) { + + // Compute D0 invariant mass for all D* candidates + const std::array arrMomD0{pVecTrackPos1, pVecTrackNeg1}; + const float invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + + if (!softPiPosCacheFilled) { + groupedTrackIndicesSoftPionsPos.emplace(positiveSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache)); + fillPropagatedProngCache(collision, groupedTrackIndicesSoftPionsPos, softPionsPos); + softPiPosCacheFilled = true; + } + + for (std::size_t iPos2 = 0; iPos2 < softPionsPos.size(); ++iPos2) { + if (softPionsPos[iPos2].trackIndex == trackIndexPos1) { continue; } - auto trackPos2 = trackIndexPos2.template track_as(); - std::array pVecTrackPos2{trackPos2.pVector()}; - if (thisCollId != trackPos2.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - auto trackParVarPos2 = getTrackParCov(trackPos2); - std::array dcaInfoPos2{trackPos2.dcaXY(), trackPos2.dcaZ()}; - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarPos2, 2.f, noMatCorr, &dcaInfoPos2); - getPxPyPz(trackParVarPos2, pVecTrackPos2); - } + + const auto& trackGlobalIndexPos2 = softPionsPos[iPos2].trackGlobalIndex; + const auto& pVecTrackPos2 = softPionsPos[iPos2].pVec; uint8_t isSelectedDstar{0}; uint8_t cutStatus{BIT(kNCutsDstar) - 1}; float deltaMass{-1.}; - isSelectedDstar = applySelectionDstar(pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections + isSelectedDstar = applySelectionDstar(pVecTrackPos1, pVecTrackNeg1, pVecTrackPos2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections if (isSelectedDstar) { - rowTrackIndexDstar(thisCollId, trackPos2.globalIndex(), lastFilledD0); + rowTrackIndexDstar(thisCollId, trackGlobalIndexPos2, lastFilledD0); if (config.fillHistograms) { registry.fill(HIST("hMassDstarToD0Pi"), deltaMass); } @@ -3213,26 +3306,31 @@ struct HfTrackIndexSkimCreator { // second loop over negative tracks if (TESTBIT(whichHypo2Prong[kN2ProngDecays], 1) && (!config.applyKaonPidIn3Prongs || TESTBIT(trackIndexPos1.isIdentifiedPid(), ChannelKaonPid))) { // only for D0bar candidates; moreover if kaon PID enabled, apply to the positive track - auto groupedTrackIndicesSoftPionsNeg = negativeSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - for (auto trackIndexNeg2 = groupedTrackIndicesSoftPionsNeg.begin(); trackIndexNeg2 != groupedTrackIndicesSoftPionsNeg.end(); ++trackIndexNeg2) { - if (trackIndexNeg1 == trackIndexNeg2) { + + // Compute D0 invariant mass for all D* candidates + const std::array arrMomD0{pVecTrackNeg1, pVecTrackPos1}; + const float invMassD0 = RecoDecay::m(arrMomD0, std::array{MassPiPlus, MassKPlus}); + + if (!softPiNegCacheFilled) { + groupedTrackIndicesSoftPionsNeg.emplace(negativeSoftPions->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache)); + fillPropagatedProngCache(collision, groupedTrackIndicesSoftPionsNeg, softPionsNeg); + softPiNegCacheFilled = true; + } + + for (std::size_t iNeg2 = 0; iNeg2 < softPionsNeg.size(); ++iNeg2) { + if (trackIndexNeg1 == softPionsNeg[iNeg2].trackIndex) { continue; } - auto trackNeg2 = trackIndexNeg2.template track_as(); - std::array pVecTrackNeg2{trackNeg2.pVector()}; - if (thisCollId != trackNeg2.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it - auto trackParVarNeg2 = getTrackParCov(trackNeg2); - std::array dcaInfoNeg2{trackNeg2.dcaXY(), trackNeg2.dcaZ()}; - o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, trackParVarNeg2, 2.f, noMatCorr, &dcaInfoNeg2); - getPxPyPz(trackParVarNeg2, pVecTrackNeg2); - } + + const auto& trackGlobalIndexNeg2 = softPionsNeg[iNeg2].trackGlobalIndex; + const auto& pVecTrackNeg2 = softPionsNeg[iNeg2].pVec; uint8_t isSelectedDstar{0}; uint8_t cutStatus{BIT(kNCutsDstar) - 1}; float deltaMass{-1.}; - isSelectedDstar = applySelectionDstar(pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections + isSelectedDstar = applySelectionDstar(pVecTrackNeg1, pVecTrackPos1, pVecTrackNeg2, cutStatus, invMassD0, deltaMass); // we do not compute the D* decay vertex at this stage because we are not interested in applying topological selections if (isSelectedDstar) { - rowTrackIndexDstar(thisCollId, trackNeg2.globalIndex(), lastFilledD0); + rowTrackIndexDstar(thisCollId, trackGlobalIndexNeg2, lastFilledD0); if (config.fillHistograms) { registry.fill(HIST("hMassDstarToD0Pi"), deltaMass); } @@ -3286,7 +3384,7 @@ struct HfTrackIndexSkimCreator { SelectedCollisions const& collisions, aod::BCsWithTimestamps const& bcWithTimeStamps, FilteredTrackAssocSel const& trackIndices, - aod::TracksWCovDcaExtra const& tracks) + aod::TracksWCovDca const& tracks) { run2And3Prongs(collisions, bcWithTimeStamps, trackIndices, tracks); } @@ -3348,9 +3446,15 @@ struct HfTrackIndexSkimCreatorCascades { // cascade cuts Configurable ptCascCandMin{"ptCascCandMin", -1., "min. pT of the cascade candidate"}; // PbPb 2018: use 1 Configurable cutInvMassCascLc{"cutInvMassCascLc", 1., "Lc candidate invariant mass difference wrt PDG"}; // for PbPb 2018: use 0.2 + Configurable cutInvMassCascCharmNuclei{"cutInvMassCascCharmNuclei", 0., "charm nuclei candidate invariant mass difference wrt mass threshold"}; + // Configurable cutCascDCADaughters{"cutCascDCADaughters", .1, "DCA between V0 and bachelor in cascade"}; // proton PID Configurable applyProtonPid{"applyProtonPid", false, "Apply proton PID for Lc->pK0S"}; + Configurable applyDeuteronPid{"applyDeuteronPid", false, "Apply Deuteron PID for Cd->dK0S"}; + Configurable applyTritonPid{"applyTritonPid", false, "Apply Triton PID for Ct->trK0S"}; + Configurable applyHelium3Pid{"applyHelium3Pid", false, "Apply Helium3 PID for Ch->HeK0S"}; + Configurable applyAlphaPid{"applyAlphaPid", false, "Apply Alpha PID for Ca->AlK0S"}; // CCDB Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -3371,7 +3475,16 @@ struct HfTrackIndexSkimCreatorCascades { using FilteredTrackAssocSel = soa::Filtered>; Filter filterSelectCollisions = (aod::hf_sel_collision::whyRejectColl == static_cast(0)); - Filter filterSelectTrackIds = (aod::hf_sel_track::isSelProng & static_cast(BIT(CandidateType::CandV0bachelor))) != 0u && (!config.applyProtonPid || (aod::hf_sel_track::isIdentifiedPid & static_cast(BIT(ChannelsProtonPid::LcToPK0S))) != 0u); + + Filter filterSelectTrackIds = + ((aod::hf_sel_track::isSelProng & static_cast(BIT(CandidateType::CandV0bachelor))) != 0u) && + + ((config.applyProtonPid && (aod::hf_sel_track::isIdentifiedPid & static_cast(BIT(ChannelsProtonPid::LcToPK0S))) != 0u) || + (config.applyDeuteronPid && (aod::hf_sel_track::isIdentifiedPid & static_cast(BIT(ChannelsDeuteronPid))) != 0u) || + (config.applyTritonPid && (aod::hf_sel_track::isIdentifiedPid & static_cast(BIT(ChannelsTritonPid))) != 0u) || + (config.applyHelium3Pid && (aod::hf_sel_track::isIdentifiedPid & static_cast(BIT(ChannelsHeliumPid))) != 0u) || + (config.applyAlphaPid && (aod::hf_sel_track::isIdentifiedPid & static_cast(BIT(ChannelsAlphaPid))) != 0u) || + (!config.applyProtonPid && !config.applyDeuteronPid && !config.applyTritonPid && !config.applyHelium3Pid && !config.applyAlphaPid)); Preslice trackIndicesPerCollision = aod::track_association::collisionId; Preslice v0sPerCollision = aod::v0data::collisionId; @@ -3408,7 +3521,11 @@ struct HfTrackIndexSkimCreatorCascades { registry.add("hVtx2ProngX", "2-prong candidates;#it{x}_{sec. vtx.} (cm);entries", {HistType::kTH1D, {{1000, -2., 2.}}}); registry.add("hVtx2ProngY", "2-prong candidates;#it{y}_{sec. vtx.} (cm);entries", {HistType::kTH1D, {{1000, -2., 2.}}}); registry.add("hVtx2ProngZ", "2-prong candidates;#it{z}_{sec. vtx.} (cm);entries", {HistType::kTH1D, {{1000, -2., 2.}}}); - registry.add("hMassLcToPK0S", "#Lambda_{c}^ candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); + registry.add("hMassLcToPK0S", " #Lambda_{c}^ candidates;inv. mass (p K_{S}^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 0., 5.}}}); + registry.add("hMassCdToDeK0S", "Cd candidates;inv. mass (d K_{S}^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 2., 7.}}}); + registry.add("hMassCtToTrK0S", "Ct candidates;inv. mass (tr K_{S}^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 2., 7.}}}); + registry.add("hMassChToHeK0S", "Ch candidates;inv. mass (he3 K_{S}^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 2., 7.}}}); + registry.add("hMassCaToAlK0S", "Ca candidates;inv. mass (alpha K_{S}^{0}) (GeV/#it{c}^{2});entries", {HistType::kTH1D, {{500, 2., 7.}}}); } } @@ -3421,7 +3538,7 @@ struct HfTrackIndexSkimCreatorCascades { void processCascades(SelectedCollisions const& collisions, soa::Join const& v0s, FilteredTrackAssocSel const& trackIndices, - aod::TracksWCovDcaExtra const&, + aod::TracksWCovDca const&, aod::BCsWithTimestamps const&) { // set the magnetic field from CCDB @@ -3440,7 +3557,7 @@ struct HfTrackIndexSkimCreatorCascades { // fist we loop over the bachelor candidate for (const auto& bachIdx : groupedBachTrackIndices) { - const auto bach = bachIdx.track_as(); + const auto bach = bachIdx.track_as(); std::array pVecBach{bach.pVector()}; auto trackBach = getTrackParCov(bach); if (thisCollId != bach.collisionId()) { // this is not the "default" collision for this track, we have to re-propagate it @@ -3452,8 +3569,8 @@ struct HfTrackIndexSkimCreatorCascades { // now we loop over the V0s for (const auto& v0 : groupedV0s) { // selections on the V0 daughters - const auto& trackV0DaughPos = v0.posTrack_as(); // only used for indices and track cuts (TPC clusters, TPC refit) - const auto& trackV0DaughNeg = v0.negTrack_as(); // only used for indices and track cuts (TPC clusters, TPC refit) + const auto& trackV0DaughPos = v0.posTrack_as(); // only the global index is read here; track-quality cuts are applied upstream in tag-sel-tracks + const auto& trackV0DaughNeg = v0.negTrack_as(); // only the global index is read here; track-quality cuts are applied upstream in tag-sel-tracks // check not to take the same track twice (as bachelor and V0 daughter) if (trackV0DaughPos.globalIndex() == bach.globalIndex() || trackV0DaughNeg.globalIndex() == bach.globalIndex()) { @@ -3492,7 +3609,25 @@ struct HfTrackIndexSkimCreatorCascades { // invariant-mass cut: we do it here, before updating the momenta of bach and V0 during the fitting to save CPU // TODO: but one should better check that the value here and after the fitter do not change significantly!!! double mass2K0sP = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassProton, MassK0Short}); - if ((config.cutInvMassCascLc >= 0.) && (std::abs(mass2K0sP - MassLambdaCPlus) > config.cutInvMassCascLc)) { + double mass2K0sDe = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassDeuteron, MassK0Short}); + double mass2K0sTr = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassTriton, MassK0Short}); + double mass2K0sHe = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassHelium3, MassK0Short}); + double mass2K0sAl = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassAlpha, MassK0Short}); + + const auto isIdentifiedPid = bachIdx.isIdentifiedPid(); + const bool applyAnyBachelorPid = config.applyProtonPid || config.applyDeuteronPid || config.applyTritonPid || config.applyHelium3Pid || config.applyAlphaPid; + const bool isChannelLc = !applyAnyBachelorPid || (config.applyProtonPid && TESTBIT(isIdentifiedPid, ChannelsProtonPid::LcToPK0S)); + const bool isChannelCd = config.applyDeuteronPid && TESTBIT(isIdentifiedPid, ChannelsDeuteronPid); + const bool isChannelCt = config.applyTritonPid && TESTBIT(isIdentifiedPid, ChannelsTritonPid); + const bool isChannelCh = config.applyHelium3Pid && TESTBIT(isIdentifiedPid, ChannelsHeliumPid); + const bool isChannelCa = config.applyAlphaPid && TESTBIT(isIdentifiedPid, ChannelsAlphaPid); + + bool isSelectedLc = isChannelLc && ((config.cutInvMassCascLc < 0.) || (std::abs(mass2K0sP - MassLambdaCPlus) <= config.cutInvMassCascLc)); + bool isSelectedCd = isChannelCd && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sDe - MassDeuteron - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + bool isSelectedCt = isChannelCt && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sTr - MassTriton - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + bool isSelectedCh = isChannelCh && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sHe - MassHelium3 - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + bool isSelectedCa = isChannelCa && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sAl - MassAlpha - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + if (!isSelectedLc && !isSelectedCd && !isSelectedCt && !isSelectedCh && !isSelectedCa) { continue; } @@ -3535,6 +3670,17 @@ struct HfTrackIndexSkimCreatorCascades { // invariant mass // re-calculate invariant masses with updated momenta, to fill the histogram mass2K0sP = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassProton, MassK0Short}); + mass2K0sDe = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassDeuteron, MassK0Short}); + mass2K0sTr = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassTriton, MassK0Short}); + mass2K0sHe = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassHelium3, MassK0Short}); + mass2K0sAl = RecoDecay::m(std::array{pVecBach, pVecV0}, std::array{MassAlpha, MassK0Short}); + + isSelectedLc = isChannelLc && ((config.cutInvMassCascLc < 0.) || (std::abs(mass2K0sP - MassLambdaCPlus) <= config.cutInvMassCascLc)); + isSelectedCd = isChannelCd && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sDe - MassDeuteron - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + isSelectedCt = isChannelCt && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sTr - MassTriton - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + isSelectedCh = isChannelCh && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sHe - MassHelium3 - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + isSelectedCa = isChannelCa && ((config.cutInvMassCascCharmNuclei < 0.) || (mass2K0sAl - MassAlpha - MassLambdaCPlus <= config.cutInvMassCascCharmNuclei)); + std::array posCasc{0., 0., 0.}; if (config.useDCAFitter) { const auto& cascVtx = df2.getPCACandidate(); @@ -3550,7 +3696,21 @@ struct HfTrackIndexSkimCreatorCascades { registry.fill(HIST("hVtx2ProngX"), posCasc[0]); registry.fill(HIST("hVtx2ProngY"), posCasc[1]); registry.fill(HIST("hVtx2ProngZ"), posCasc[2]); - registry.fill(HIST("hMassLcToPK0S"), mass2K0sP); + if (isSelectedLc) { + registry.fill(HIST("hMassLcToPK0S"), mass2K0sP); + } + if (isSelectedCd) { + registry.fill(HIST("hMassCdToDeK0S"), mass2K0sDe); + } + if (isSelectedCt) { + registry.fill(HIST("hMassCtToTrK0S"), mass2K0sTr); + } + if (isSelectedCh) { + registry.fill(HIST("hMassChToHeK0S"), mass2K0sHe); + } + if (isSelectedCa) { + registry.fill(HIST("hMassCaToAlK0S"), mass2K0sAl); + } } } // loop over V0s } // loop over tracks @@ -3863,6 +4023,7 @@ struct HfTrackIndexSkimCreatorLfCascades { // cascade loop const auto thisCollId = collision.globalIndex(); const auto groupedCascades = cascades.sliceBy(cascadesPerCollision, thisCollId); + const auto groupedBachTrackIndices = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); for (const auto& casc : groupedCascades) { @@ -3920,7 +4081,6 @@ struct HfTrackIndexSkimCreatorLfCascades { trackParCovCascOmega.setPID(o2::track::PID::OmegaMinus); //--------------combining cascade and pion tracks-------------- - const auto groupedBachTrackIndices = trackIndices.sliceBy(trackIndicesPerCollision, thisCollId); for (auto trackIdCharmBachelor1 = groupedBachTrackIndices.begin(); trackIdCharmBachelor1 != groupedBachTrackIndices.end(); ++trackIdCharmBachelor1) { hfFlag = 0; @@ -4056,6 +4216,8 @@ struct HfTrackIndexSkimCreatorLfCascades { continue; } + const auto pVecCharmBachelor1 = trackCharmBachelor1.pVector(); + // second loop over tracks for (auto trackIdCharmBachelor2 = trackIdCharmBachelor1 + 1; trackIdCharmBachelor2 != groupedBachTrackIndices.end(); ++trackIdCharmBachelor2) { @@ -4079,7 +4241,7 @@ struct HfTrackIndexSkimCreatorLfCascades { continue; } - if (!isPreselectedCandidateXic(pVecCasc, trackCharmBachelor1.pVector(), trackCharmBachelor2.pVector())) { + if (!isPreselectedCandidateXic(pVecCasc, pVecCharmBachelor1, trackCharmBachelor2.pVector())) { continue; } diff --git a/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx b/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx index 36316355fad..03f97287433 100644 --- a/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx +++ b/PWGHF/TableProducer/treeCreatorD0ToKPi.cxx @@ -21,7 +21,6 @@ #include "PWGHF/Core/HfHelper.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" -#include "PWGHF/Utils/utilsAnalysis.h" #include "Common/Core/RecoDecay.h" @@ -116,6 +115,8 @@ DECLARE_SOA_TABLE(HfCandD0Lites, "AOD", "HFCANDD0LITE", hf_cand::ImpactParameter1, full::ImpactParameterNormalised0, full::ImpactParameterNormalised1, + hf_cand::ImpactParameterZ0, + hf_cand::ImpactParameterZ1, full::NSigTpcPi0, full::NSigTpcKa0, full::NSigTofPi0, @@ -175,6 +176,8 @@ DECLARE_SOA_TABLE(HfCandD0Fulls, "AOD", "HFCANDD0FULL", hf_cand::ImpactParameter1, hf_cand::ErrorImpactParameter0, hf_cand::ErrorImpactParameter1, + hf_cand::ImpactParameterZ0, + hf_cand::ImpactParameterZ1, full::NSigTpcPi0, full::NSigTpcKa0, full::NSigTofPi0, @@ -225,8 +228,8 @@ DECLARE_SOA_TABLE(HfCandD0FullPs, "AOD", "HFCANDD0FULLP", DECLARE_SOA_TABLE(HfCandD0Mls, "AOD", "HFCANDD0ML", ml::BdtOutputBkg, - ml::BdtOutputNonPrompt, - ml::BdtOutputPrompt); + ml::BdtOutputPrompt, + ml::BdtOutputNonPrompt); } // namespace o2::aod @@ -289,7 +292,7 @@ struct HfTreeCreatorD0ToKPi { template auto fillTable(const T& candidate, int candFlag, double invMass, double topoChi2, - double ct, double y, double e, int8_t flagMc, int8_t flagMcDecay, int8_t origin, int8_t flagBmother) + double ct, double y, double e, int8_t flagMc, int8_t flagMcDecay, int8_t origin, int pdgBmother) { if (fillCandidateLiteTable) { rowCandidateLite( @@ -304,6 +307,8 @@ struct HfTreeCreatorD0ToKPi { candidate.impactParameter1(), candidate.impactParameterNormalised0(), candidate.impactParameterNormalised1(), + candidate.impactParameterZ0(), + candidate.impactParameterZ1(), candidate.nSigTpcPi0(), candidate.nSigTpcKa0(), candidate.nSigTofPi0(), @@ -329,7 +334,7 @@ struct HfTreeCreatorD0ToKPi { flagMc, flagMcDecay, origin, - flagBmother); + pdgBmother); } else { double cosThetaStar = candFlag == 0 ? HfHelper::cosThetaStarD0(candidate) : HfHelper::cosThetaStarD0bar(candidate); rowCandidateFull( @@ -364,6 +369,8 @@ struct HfTreeCreatorD0ToKPi { candidate.impactParameter1(), candidate.errorImpactParameter0(), candidate.errorImpactParameter1(), + candidate.impactParameterZ0(), + candidate.impactParameterZ1(), candidate.nSigTpcPi0(), candidate.nSigTpcKa0(), candidate.nSigTofPi0(), @@ -393,7 +400,7 @@ struct HfTreeCreatorD0ToKPi { flagMc, flagMcDecay, origin, - flagBmother); + pdgBmother); } if constexpr (ApplyMl) { if (candFlag == 0) { @@ -423,12 +430,14 @@ struct HfTreeCreatorD0ToKPi { // Filling candidate properties if (fillCandidateLiteTable) { - rowCandidateLite.reserve(candidates.size()); + // Account for candidates passing both D0 and D0bar + // selection, which will be stored twice in the lite table + rowCandidateLite.reserve(candidates.size() * 2); } else { - rowCandidateFull.reserve(candidates.size()); + rowCandidateFull.reserve(candidates.size() * 2); } if constexpr (ApplyMl) { - rowCandidateMl.reserve(candidates.size()); + rowCandidateMl.reserve(candidates.size() * 2); } for (const auto& candidate : candidates) { if (downSampleBkgFactor < 1.) { @@ -451,10 +460,10 @@ struct HfTreeCreatorD0ToKPi { massD0bar = HfHelper::invMassD0barToKPi(candidate); } if (candidate.isSelD0()) { - fillTable(candidate, 0, massD0, topolChi2PerNdf, ctD, yD, eD, 0, 0, 0, -1); + fillTable(candidate, 0, massD0, topolChi2PerNdf, ctD, yD, eD, 0, 0, 0, 0); } if (candidate.isSelD0bar()) { - fillTable(candidate, 1, massD0bar, topolChi2PerNdf, ctD, yD, eD, 0, 0, 0, -1); + fillTable(candidate, 1, massD0bar, topolChi2PerNdf, ctD, yD, eD, 0, 0, 0, 0); } } } @@ -511,12 +520,12 @@ struct HfTreeCreatorD0ToKPi { // Filling candidate properties if (fillCandidateLiteTable) { - rowCandidateLite.reserve(candidates.size()); + rowCandidateLite.reserve(candidates.size() * 2); } else { - rowCandidateFull.reserve(candidates.size()); + rowCandidateFull.reserve(candidates.size() * 2); } if constexpr (ApplyMl) { - rowCandidateMl.reserve(candidates.size()); + rowCandidateMl.reserve(candidates.size() * 2); } for (const auto& candidate : candidates) { if constexpr (OnlyBkg) { @@ -552,10 +561,10 @@ struct HfTreeCreatorD0ToKPi { massD0bar = HfHelper::invMassD0barToKPi(candidate); } if (candidate.isSelD0()) { - fillTable(candidate, 0, massD0, topolChi2PerNdf, ctD, yD, eD, candidate.flagMcMatchRec(), candidate.flagMcDecayChanRec(), candidate.originMcRec(), o2::analysis::getBHadMotherFlag(candidate.pdgBhadMotherPart())); + fillTable(candidate, 0, massD0, topolChi2PerNdf, ctD, yD, eD, candidate.flagMcMatchRec(), candidate.flagMcDecayChanRec(), candidate.originMcRec(), candidate.pdgBhadMotherPart()); } if (candidate.isSelD0bar()) { - fillTable(candidate, 1, massD0bar, topolChi2PerNdf, ctD, yD, eD, candidate.flagMcMatchRec(), candidate.flagMcDecayChanRec(), candidate.originMcRec(), o2::analysis::getBHadMotherFlag(candidate.pdgBhadMotherPart())); + fillTable(candidate, 1, massD0bar, topolChi2PerNdf, ctD, yD, eD, candidate.flagMcMatchRec(), candidate.flagMcDecayChanRec(), candidate.originMcRec(), candidate.pdgBhadMotherPart()); } } diff --git a/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx b/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx index e04c1bbec9b..19460efb5df 100644 --- a/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx +++ b/PWGHF/TableProducer/treeCreatorDplusToPiKPi.cxx @@ -22,7 +22,6 @@ #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" -#include "PWGHF/Utils/utilsAnalysis.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" @@ -308,22 +307,28 @@ struct HfTreeCreatorDplusToPiKPi { int8_t flagMc = 0; int8_t originMc = 0; int8_t channelMc = 0; - int8_t bMotherFlag = -1; + int bMotherPdg = 0; if constexpr (DoMc) { flagMc = candidate.flagMcMatchRec(); originMc = candidate.originMcRec(); channelMc = candidate.flagMcDecayChanRec(); - bMotherFlag = o2::analysis::getBHadMotherFlag(candidate.pdgBhadMotherPart()); + bMotherPdg = candidate.pdgBhadMotherPart(); } std::vector outputMl = {-999., -999.}; if constexpr (DoMl) { - for (unsigned int iclass = 0; iclass < classMlIndexes->size(); iclass++) { - outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; + if (candidate.mlProbDplusToPiKPi().empty()) { + rowCandidateMl( + -999., + -999.); + } else { + for (unsigned int iclass = 0; iclass < classMlIndexes->size(); iclass++) { + outputMl[iclass] = candidate.mlProbDplusToPiKPi()[classMlIndexes->at(iclass)]; + } + rowCandidateMl( + outputMl[0], + outputMl[1]); } - rowCandidateMl( - outputMl[0], - outputMl[1]); } float cent{-1.}; @@ -380,7 +385,7 @@ struct HfTreeCreatorDplusToPiKPi { flagMc, originMc, channelMc, - bMotherFlag); + bMotherPdg); } else { rowCandidateFull( coll.numContrib(), @@ -462,7 +467,7 @@ struct HfTreeCreatorDplusToPiKPi { flagMc, originMc, channelMc, - bMotherFlag); + bMotherPdg); } } @@ -495,6 +500,35 @@ struct HfTreeCreatorDplusToPiKPi { PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processData, "Process data", true); + void processDataWMl(aod::Collisions const& collisions, + soa::Filtered> const& candidates, + TracksWPid const&) + { + // Filling event properties + rowCandidateFullEvents.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision, 0, 1); + } + + // Filling candidate properties + if (fillCandidateLiteTable) { + rowCandidateLite.reserve(candidates.size()); + } else { + rowCandidateFull.reserve(candidates.size()); + } + for (const auto& candidate : candidates) { + if (downSampleBkgFactor < 1.) { + float const pseudoRndm = candidate.ptProng0() * 1000. - static_cast(candidate.ptProng0() * 1000); + if (candidate.pt() < ptMaxForDownSample && pseudoRndm >= downSampleBkgFactor) { + continue; + } + } + fillCandidateTable(candidate); + } + } + + PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processDataWMl, "Process data with ML", false); + void processDataWCent(CollisionsCent const& collisions, soa::Filtered> const& candidates, TracksWPid const&) @@ -524,6 +558,35 @@ struct HfTreeCreatorDplusToPiKPi { PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processDataWCent, "Process data with cent", false); + void processDataWCentMl(CollisionsCent const& collisions, + soa::Filtered> const& candidates, + TracksWPid const&) + { + // Filling event properties + rowCandidateFullEvents.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision, 0, 1); + } + + // Filling candidate properties + if (fillCandidateLiteTable) { + rowCandidateLite.reserve(candidates.size()); + } else { + rowCandidateFull.reserve(candidates.size()); + } + for (const auto& candidate : candidates) { + if (downSampleBkgFactor < 1.) { + float const pseudoRndm = candidate.ptProng0() * 1000. - static_cast(candidate.ptProng0() * 1000); + if (candidate.pt() < ptMaxForDownSample && pseudoRndm >= downSampleBkgFactor) { + continue; + } + } + fillCandidateTable(candidate); + } + } + + PROCESS_SWITCH(HfTreeCreatorDplusToPiKPi, processDataWCentMl, "Process data with cent and ML", false); + template void fillMcTables(CollType const& collisions, aod::McCollisions const&, diff --git a/PWGHF/TableProducer/treeCreatorDsToKKPi.cxx b/PWGHF/TableProducer/treeCreatorDsToKKPi.cxx index 353a57cea43..98d42538fc3 100644 --- a/PWGHF/TableProducer/treeCreatorDsToKKPi.cxx +++ b/PWGHF/TableProducer/treeCreatorDsToKKPi.cxx @@ -23,7 +23,6 @@ #include "PWGHF/DataModel/AliasTables.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" -#include "PWGHF/Utils/utilsAnalysis.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" @@ -349,7 +348,7 @@ struct HfTreeCreatorDsToKKPi { int8_t flagMc{0}; int8_t originMc{0}; int8_t channelMc{0}; - int8_t bMotherFlag{-1}; + int bMotherPdg{0}; int8_t isSwapped{MassHypo}; // 0 if KKPi, 1 if PiKK float eCand{0.f}; float ctCand{0.f}; @@ -359,7 +358,7 @@ struct HfTreeCreatorDsToKKPi { originMc = candidate.originMcRec(); channelMc = candidate.flagMcDecayChanRec(); isSwapped = candidate.isCandidateSwapped(); - bMotherFlag = o2::analysis::getBHadMotherFlag(candidate.pdgBhadMotherPart()); + bMotherPdg = candidate.pdgBhadMotherPart(); if (fillDplusMc && candidate.flagMcDecayChanRec() == channelsResonant[Mother::Dplus][decayChannel]) { eCand = HfHelper::eDplus(candidate); ctCand = HfHelper::ctDplus(candidate); @@ -425,7 +424,7 @@ struct HfTreeCreatorDsToKKPi { flagMc, originMc, channelMc, - bMotherFlag, + bMotherPdg, isSwapped, prong0.sign() + prong1.sign() + prong2.sign()); } else { @@ -499,7 +498,7 @@ struct HfTreeCreatorDsToKKPi { flagMc, originMc, channelMc, - bMotherFlag, + bMotherPdg, isSwapped, prong0.sign() + prong1.sign() + prong2.sign()); } diff --git a/PWGHF/TableProducer/treeCreatorLcToPKPi.cxx b/PWGHF/TableProducer/treeCreatorLcToPKPi.cxx index 51112ffe161..7b0eb1f6a24 100644 --- a/PWGHF/TableProducer/treeCreatorLcToPKPi.cxx +++ b/PWGHF/TableProducer/treeCreatorLcToPKPi.cxx @@ -144,8 +144,8 @@ DECLARE_SOA_COLUMN(Chi2Topo, chi2Topo, float); //! chi DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! decay length, cm DECLARE_SOA_COLUMN(DecayLengthError, decayLengthError, float); //! decay length error DECLARE_SOA_COLUMN(DecayLengthNormalised, decayLengthNormalised, float); //! decay length over its error -DECLARE_SOA_COLUMN(T, t, float); //! proper lifetime, ps -DECLARE_SOA_COLUMN(ErrT, errT, float); //! lifetime error +DECLARE_SOA_COLUMN(T, t, float); //! proper decay time, ps +DECLARE_SOA_COLUMN(ErrT, errT, float); //! decay time error DECLARE_SOA_COLUMN(MassInv, massInv, float); //! invariant mass DECLARE_SOA_COLUMN(P, p, float); //! momentum DECLARE_SOA_COLUMN(Pt, pt, float); //! transverse momentum @@ -183,7 +183,7 @@ DECLARE_SOA_COLUMN(XDecay, xDecay, float); //! Secondary (decay) vertex X coordi DECLARE_SOA_COLUMN(YDecay, yDecay, float); //! Secondary (decay) vertex Y coordinate, cm DECLARE_SOA_COLUMN(ZDecay, zDecay, float); //! Secondary (decay) vertex Z coordinate, cm DECLARE_SOA_COLUMN(LDecay, lDecay, float); //! Decay length, cm (distance between PV and SV, curvature is neglected) -DECLARE_SOA_COLUMN(TDecay, tDecay, float); //! Proper lifetime, ps +DECLARE_SOA_COLUMN(TDecay, tDecay, float); //! Proper decay time, ps DECLARE_SOA_COLUMN(XEvent, xEvent, float); //! Primary (event) vertex X coordinate, cm DECLARE_SOA_COLUMN(YEvent, yEvent, float); //! Primary (event) vertex Y coordinate, cm DECLARE_SOA_COLUMN(ZEvent, zEvent, float); //! Primary (event) vertex Z coordinate, cm @@ -262,7 +262,8 @@ DECLARE_SOA_TABLE(HfCandLcLites, "AOD", "HFCANDLCLITE", full::MassKPi, full::MlScoreFirstClass, full::MlScoreSecondClass, - full::MlScoreThirdClass); + full::MlScoreThirdClass, + full::CentFT0C); DECLARE_SOA_TABLE(HfCollIdLCLite, "AOD", "HFCOLLIDLCLITE", full::CollisionId); @@ -343,7 +344,8 @@ DECLARE_SOA_TABLE(HfCandLcFulls, "AOD", "HFCANDLCFULL", full::MassKPi, full::MlScoreFirstClass, full::MlScoreSecondClass, - full::MlScoreThirdClass); + full::MlScoreThirdClass, + full::CentFT0C); DECLARE_SOA_TABLE(HfCandLcFullEvs, "AOD", "HFCANDLCFULLEV", full::CollisionId, @@ -410,7 +412,6 @@ struct HfTreeCreatorLcToPKPi { constexpr static float UndefValueFloat = -999.f; constexpr static int UndefValueInt = -999; - constexpr static float NanoToPico = 1000.f; using TracksWPid = soa::Join; using Cents = soa::Join; @@ -418,9 +419,9 @@ struct HfTreeCreatorLcToPKPi { // number showing MC status of the candidate (signal or background, prompt or non-prompt etc.) enum SigBgStatus : int { Background = 0, // combinatorial background, at least one of the prongs do not originate from the Lc decay - Prompt, // signal with Lc produced directly in the event - NonPrompt, // signal with Lc produced aftewards the event, e.g. during decay of beauty particle - WrongOrder, // all the prongs are from Lc decay, but proton and pion hypothesis are swapped + Prompt = 1, // signal with Lc produced directly in the event + NonPrompt = 2, // signal with Lc produced aftewards the event, e.g. during decay of beauty particle + WrongOrder = 3, // all the prongs are from Lc decay, but proton and pion hypothesis are swapped Default = -1 // impossible, should not be the case, to catch logical error if any }; @@ -478,6 +479,21 @@ struct HfTreeCreatorLcToPKPi { } } + /// \brief function to check when applyMl == true if the HfMlLcToPKPi size is equal to that of the table with candidates + /// \param candidates Lc->pKpi candidate table + /// \param candidateMlScores ML scores table + template + void checkHfMlLcToPKPiSize(CandType const& candidates, aod::HfMlLcToPKPi const& candidateMlScores) + { + if (applyMl) { + const auto candidatesSize = candidates.size(); + const auto candidateMlScoresSize = candidateMlScores.size(); + if (candidatesSize != candidateMlScoresSize) { + LOG(fatal) << "Tables with candidates and ML scores have different sizes (" << candidatesSize << " vs " << candidateMlScoresSize << ") while applyMl == true. Check if applyMl is enabled in the candidateSelectorLc"; + } + } + } + /// \brief function to fill event properties /// \param collisions Collision table template @@ -607,8 +623,8 @@ struct HfTreeCreatorLcToPKPi { /// \param candidate candidate instance /// \param candidateMlScore instance of handler of vectors with ML scores associated with the current candidate /// \param candFlag flag indicating if PKPi (0) or PiKP (1) hypothesis is used - template - void fillLiteTable(CandType const& candidate, aod::HfMlLcToPKPi::iterator const& candidateMlScore, int candFlag) + template + void fillLiteTable(CandType const& candidate, CollType const& collision, aod::HfMlLcToPKPi::iterator const& candidateMlScore, int candFlag) { auto [functionInvMass, functionInvMassKPi] = evaluateInvariantMassesDCAFitter(candidate, candFlag); const float functionCt = HfHelper::ctLc(candidate); @@ -634,6 +650,11 @@ struct HfTreeCreatorLcToPKPi { assignMlScores(candidateMlScore, mlScoreFirstClass, mlScoreSecondClass, mlScoreThirdClass, candFlag); } + float centFT0C = -1.f; + if constexpr (UseCentrality) { + centFT0C = collision.centFT0C(); + } + rowCandidateLite( candidate.posX(), candidate.posY(), @@ -681,7 +702,8 @@ struct HfTreeCreatorLcToPKPi { functionInvMassKPi, mlScoreFirstClass, mlScoreSecondClass, - mlScoreThirdClass); + mlScoreThirdClass, + centFT0C); if (fillCollIdTable) { /// save also candidate collision indices @@ -693,8 +715,8 @@ struct HfTreeCreatorLcToPKPi { /// \param candidate candidate instance /// \param candidateMlScore instance of handler of vectors with ML scores associated with the current candidate /// \param candFlag flag indicating if PKPi (0) or PiKP (1) hypothesis is used - template - void fillFullTable(CandType const& candidate, aod::HfMlLcToPKPi::iterator const& candidateMlScore, int candFlag) + template + void fillFullTable(CandType const& candidate, CollType const& collision, aod::HfMlLcToPKPi::iterator const& candidateMlScore, int candFlag) { auto [functionInvMass, functionInvMassKPi] = evaluateInvariantMassesDCAFitter(candidate, candFlag); const float functionCt = HfHelper::ctLc(candidate); @@ -721,6 +743,11 @@ struct HfTreeCreatorLcToPKPi { assignMlScores(candidateMlScore, mlScoreFirstClass, mlScoreSecondClass, mlScoreThirdClass, candFlag); } + float centFT0C = -1.f; + if constexpr (UseCentrality) { + centFT0C = collision.centFT0C(); + } + rowCandidateFull( candidate.collisionId(), candidate.posX(), @@ -797,7 +824,8 @@ struct HfTreeCreatorLcToPKPi { functionInvMassKPi, mlScoreFirstClass, mlScoreSecondClass, - mlScoreThirdClass); + mlScoreThirdClass, + centFT0C); } /// \brief function to fill lite table @@ -882,7 +910,7 @@ struct HfTreeCreatorLcToPKPi { const float deltaP = std::sqrt(pt * pt * deltaPt * deltaPt + candidate.kfPz() * candidate.kfPz() * candidate.kfErrorPz() * candidate.kfErrorPz()) / p; - const float lifetime = decayLength * static_cast(MassLambdaCPlus) / LightSpeedCm2PS / p; + const float decayTime = decayLength * static_cast(MassLambdaCPlus) / LightSpeedCm2PS / p; const float deltaT = dl * static_cast(MassLambdaCPlus) / LightSpeedCm2PS / p; rowCandidateKF( svX, svY, svZ, svErrX, svErrY, svErrZ, @@ -890,7 +918,7 @@ struct HfTreeCreatorLcToPKPi { chi2primProton, chi2primKaon, chi2primPion, dcaProtonKaon, dcaProtonPion, dcaPionKaon, chi2GeoProtonKaon, chi2GeoProtonPion, chi2GeoPionKaon, - chi2Geo, chi2Topo, decayLength, dl, decayLength / dl, lifetime, deltaT, + chi2Geo, chi2Topo, decayLength, dl, decayLength / dl, decayTime, deltaT, mass, p, pt, deltaP, deltaPt, functionSelection, sigbgstatus, collision.multNTracksPV(), @@ -918,12 +946,13 @@ struct HfTreeCreatorLcToPKPi { soa::Join const& particles, soa::Join const&, aod::BCs const&) { + checkHfMlLcToPKPiSize(candidates, candidateMlScores); constexpr bool IsMc = true; fillEventProperties(collisions); - const int64_t candidatesSize = static_cast(candidates.size()); + const auto candidatesSize = static_cast(candidates.size()); reserveTables(candidatesSize, IsMc); int iCand{0}; @@ -944,16 +973,16 @@ struct HfTreeCreatorLcToPKPi { const bool notSkippedBkg = isMcCandidateSignal || candidate.pt() > downSampleBkgPtMax || pseudoRndm < downSampleBkgFactor; if (passSelection && notSkippedBkg && (keepAll || (keepSignalMc && isMcCandidateSignal) || (keepBkgMc && !isMcCandidateSignal) || (keepCorrBkgMC && isCorrBkg))) { if (fillCandidateLiteTable) { - fillLiteTable(candidate, candidateMlScore, candFlag); + fillLiteTable(candidate, collision, candidateMlScore, candFlag); } else { - fillFullTable(candidate, candidateMlScore, candFlag); + fillFullTable(candidate, collision, candidateMlScore, candFlag); } if constexpr (ReconstructionType == aod::hf_cand::VertexerType::KfParticle) { fillKFTable(candidate, collision, candFlag, functionSelection, sigbgstatus); } if (fillCandidateMcTable) { - float p{}, pt{}, svX{}, svY{}, svZ{}, pvX{}, pvY{}, pvZ{}, decayLength{}, lifetime{}; + float p{}, pt{}, svX{}, svY{}, svZ{}, pvX{}, pvY{}, pvZ{}, decayLength{}, decayTime{}; if (!isMcCandidateSignal) { p = UndefValueFloat; pt = UndefValueFloat; @@ -964,7 +993,7 @@ struct HfTreeCreatorLcToPKPi { pvY = UndefValueFloat; pvZ = UndefValueFloat; decayLength = UndefValueFloat; - lifetime = UndefValueFloat; + decayTime = UndefValueFloat; } else { const auto mcParticleProng0 = candidate.template prong0_as>().template mcParticle_as>(); const auto indexMother = RecoDecay::getMother(particles, mcParticleProng0, o2::constants::physics::Pdg::kLambdaCPlus, true); @@ -972,8 +1001,6 @@ struct HfTreeCreatorLcToPKPi { const auto mcCollision = particleMother.template mcCollision_as(); p = particleMother.p(); pt = particleMother.pt(); - const float p2m = p / static_cast(MassLambdaCPlus); - const float gamma = std::sqrt(1 + p2m * p2m); // mother's particle Lorentz factor pvX = mcCollision.posX(); pvY = mcCollision.posY(); pvZ = mcCollision.posZ(); @@ -981,11 +1008,11 @@ struct HfTreeCreatorLcToPKPi { svY = mcParticleProng0.vy(); svZ = mcParticleProng0.vz(); decayLength = static_cast(RecoDecay::distance(std::array{svX, svY, svZ}, std::array{pvX, pvY, pvZ})); - lifetime = mcParticleProng0.vt() * NanoToPico / gamma; // from ns to ps * from lab time to proper time + decayTime = decayLength * static_cast(MassLambdaCPlus) / LightSpeedCm2PS / p; } rowCandidateMC( p, pt, - svX, svY, svZ, decayLength, lifetime, + svX, svY, svZ, decayLength, decayTime, pvX, pvY, pvZ); } } @@ -1002,16 +1029,14 @@ struct HfTreeCreatorLcToPKPi { const auto mcDaughter0 = particle.template daughters_as>().begin(); const auto mcCollision = particle.template mcCollision_as(); const auto p = particle.p(); - const float p2m = p / static_cast(MassLambdaCPlus); - const float gamma = std::sqrt(1 + p2m * p2m); // mother's particle Lorentz factor const float pvX = mcCollision.posX(); const float pvY = mcCollision.posY(); const float pvZ = mcCollision.posZ(); const float svX = mcDaughter0.vx(); const float svY = mcDaughter0.vy(); const float svZ = mcDaughter0.vz(); - const float l = static_cast(RecoDecay::distance(std::array{svX, svY, svZ}, std::array{pvX, pvY, pvZ})); - const float t = mcDaughter0.vt() * NanoToPico / gamma; // from ns to ps * from lab time to proper time + const float decayLength = static_cast(RecoDecay::distance(std::array{svX, svY, svZ}, std::array{pvX, pvY, pvZ})); + const float decayTime = decayLength * static_cast(MassLambdaCPlus) / LightSpeedCm2PS / p; rowCandidateFullParticles( particle.pt(), particle.eta(), @@ -1020,7 +1045,7 @@ struct HfTreeCreatorLcToPKPi { particle.flagMcMatchGen(), particle.originMcGen(), p, - svX, svY, svZ, l, t, + svX, svY, svZ, decayLength, decayTime, pvX, pvY, pvZ); } } @@ -1105,6 +1130,7 @@ struct HfTreeCreatorLcToPKPi { aod::HfMlLcToPKPi const& candidateMlScores, TracksWPid const&, aod::BCs const&) { + checkHfMlLcToPKPiSize(candidates, candidateMlScores); constexpr bool IsMc = false; @@ -1126,9 +1152,9 @@ struct HfTreeCreatorLcToPKPi { const int functionSelection = candFlag == 0 ? candidate.isSelLcToPKPi() : candidate.isSelLcToPiKP(); if (functionSelection >= selectionFlagLc && (candidate.pt() > downSampleBkgPtMax || (pseudoRndm < downSampleBkgFactor && candidate.pt() < downSampleBkgPtMax))) { if (fillCandidateLiteTable) { - fillLiteTable(candidate, candidateMlScore, candFlag); + fillLiteTable(candidate, collision, candidateMlScore, candFlag); } else { - fillFullTable(candidate, candidateMlScore, candFlag); + fillFullTable(candidate, collision, candidateMlScore, candFlag); } if constexpr (ReconstructionType == aod::hf_cand::VertexerType::KfParticle) { diff --git a/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx new file mode 100644 index 00000000000..55ba2da9efc --- /dev/null +++ b/PWGHF/TableProducer/treeCreatorOmegac0ToOmegaPiQa.cxx @@ -0,0 +1,663 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file treeCreatorOmegac0ToOmegaPiQa.cxx +/// \brief Writer of the omegac0 to Omega Pi candidates in the form of flat tables to be stored in TTrees. +/// In this file are defined and filled the output tables +/// +/// \author Fabio Catalano , University of Houston +/// \author Maria Fernanda Torres Cabrera , University of Houston + +#include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/DataModel/CandidateReconstructionTables.h" +#include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGHF/DataModel/TrackIndexSkimmingTables.h" +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// SV Reco method +enum { + DCAFITTER = 0, + KFPARTICLE +}; + +namespace o2::aod +{ +namespace full +{ +// collision info +DECLARE_SOA_COLUMN(IsEventSel8, isEventSel8, bool); +DECLARE_SOA_COLUMN(IsEventSelZ, isEventSelZ, bool); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +// from creator +DECLARE_SOA_COLUMN(XPv, xPv, float); +DECLARE_SOA_COLUMN(YPv, yPv, float); +DECLARE_SOA_COLUMN(ZPv, zPv, float); +DECLARE_SOA_COLUMN(XDecayVtxCharmBaryon, xDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(YDecayVtxCharmBaryon, yDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(ZDecayVtxCharmBaryon, zDecayVtxCharmBaryon, float); +DECLARE_SOA_COLUMN(XDecayVtxCascade, xDecayVtxCascade, float); +DECLARE_SOA_COLUMN(YDecayVtxCascade, yDecayVtxCascade, float); +DECLARE_SOA_COLUMN(ZDecayVtxCascade, zDecayVtxCascade, float); +DECLARE_SOA_COLUMN(XDecayVtxV0, xDecayVtxV0, float); +DECLARE_SOA_COLUMN(YDecayVtxV0, yDecayVtxV0, float); +DECLARE_SOA_COLUMN(ZDecayVtxV0, zDecayVtxV0, float); +DECLARE_SOA_COLUMN(SignDecay, signDecay, int8_t); // sign of ka <- omega +DECLARE_SOA_COLUMN(PxCharmBaryon, pxCharmBaryon, float); +DECLARE_SOA_COLUMN(PyCharmBaryon, pyCharmBaryon, float); +DECLARE_SOA_COLUMN(PzCharmBaryon, pzCharmBaryon, float); +DECLARE_SOA_COLUMN(PxCasc, pxCasc, float); +DECLARE_SOA_COLUMN(PyCasc, pyCasc, float); +DECLARE_SOA_COLUMN(PzCasc, pzCasc, float); +DECLARE_SOA_COLUMN(PxPiFromCharmBaryon, pxPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PyPiFromCharmBaryon, pyPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PzPiFromCharmBaryon, pzPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PxLambda, pxLambda, float); +DECLARE_SOA_COLUMN(PyLambda, pyLambda, float); +DECLARE_SOA_COLUMN(PzLambda, pzLambda, float); +DECLARE_SOA_COLUMN(PtCharmBaryon, ptCharmBaryon, float); +DECLARE_SOA_COLUMN(PtPiFromCharmBaryon, ptPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(PxKaFromCasc, pxKaFromCasc, float); +DECLARE_SOA_COLUMN(PyKaFromCasc, pyKaFromCasc, float); +DECLARE_SOA_COLUMN(PzKaFromCasc, pzKaFromCasc, float); +DECLARE_SOA_COLUMN(PxPosV0Dau, pxPosV0Dau, float); +DECLARE_SOA_COLUMN(PyPosV0Dau, pyPosV0Dau, float); +DECLARE_SOA_COLUMN(PzPosV0Dau, pzPosV0Dau, float); +DECLARE_SOA_COLUMN(PxNegV0Dau, pxNegV0Dau, float); +DECLARE_SOA_COLUMN(PyNegV0Dau, pyNegV0Dau, float); +DECLARE_SOA_COLUMN(PzNegV0Dau, pzNegV0Dau, float); +DECLARE_SOA_COLUMN(ImpactParCascXY, impactParCascXY, float); +DECLARE_SOA_COLUMN(ImpactParPiFromCharmBaryonXY, impactParPiFromCharmBaryonXY, float); +DECLARE_SOA_COLUMN(ImpactParCascZ, impactParCascZ, float); +DECLARE_SOA_COLUMN(ImpactParPiFromCharmBaryonZ, impactParPiFromCharmBaryonZ, float); +DECLARE_SOA_COLUMN(ErrImpactParCascXY, errImpactParCascXY, float); +DECLARE_SOA_COLUMN(ErrImpactParPiFromCharmBaryonXY, errImpactParPiFromCharmBaryonXY, float); +DECLARE_SOA_COLUMN(InvMassLambda, invMassLambda, float); +DECLARE_SOA_COLUMN(InvMassCascade, invMassCascade, float); +DECLARE_SOA_COLUMN(InvMassCharmBaryon, invMassCharmBaryon, float); +DECLARE_SOA_COLUMN(CosPAV0, cosPAV0, float); +DECLARE_SOA_COLUMN(CosPACharmBaryon, cosPACharmBaryon, float); +DECLARE_SOA_COLUMN(CosPACasc, cosPACasc, float); +DECLARE_SOA_COLUMN(CosPAXYV0, cosPAXYV0, float); +DECLARE_SOA_COLUMN(CosPAXYCharmBaryon, cosPAXYCharmBaryon, float); +DECLARE_SOA_COLUMN(CosPAXYCasc, cosPAXYCasc, float); +DECLARE_SOA_COLUMN(CTauCascade, cTauCascade, float); +DECLARE_SOA_COLUMN(CTauV0, cTauV0, float); +DECLARE_SOA_COLUMN(EtaV0PosDau, etaV0PosDau, float); +DECLARE_SOA_COLUMN(EtaV0NegDau, etaV0NegDau, float); +DECLARE_SOA_COLUMN(EtaKaFromCasc, etaKaFromCasc, float); +DECLARE_SOA_COLUMN(EtaPiFromCharmBaryon, etaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(EtaCharmBaryon, etaCharmBaryon, float); +DECLARE_SOA_COLUMN(EtaCascade, etaCascade, float); +DECLARE_SOA_COLUMN(EtaV0, etaV0, float); +DECLARE_SOA_COLUMN(DcaXYToPvV0Dau0, dcaXYToPvV0Dau0, float); +DECLARE_SOA_COLUMN(DcaXYToPvV0Dau1, dcaXYToPvV0Dau1, float); +DECLARE_SOA_COLUMN(DcaXYToPvCascDau, dcaXYToPvCascDau, float); +DECLARE_SOA_COLUMN(DcaZToPvV0Dau0, dcaZToPvV0Dau0, float); +DECLARE_SOA_COLUMN(DcaZToPvV0Dau1, dcaZToPvV0Dau1, float); +DECLARE_SOA_COLUMN(DcaZToPvCascDau, dcaZToPvCascDau, float); +DECLARE_SOA_COLUMN(DcaCascDau, dcaCascDau, float); +DECLARE_SOA_COLUMN(DcaV0Dau, dcaV0Dau, float); +DECLARE_SOA_COLUMN(DcaCharmBaryonDau, dcaCharmBaryonDau, float); +DECLARE_SOA_COLUMN(DecLenCharmBaryon, decLenCharmBaryon, float); +DECLARE_SOA_COLUMN(DecLenCascade, decLenCascade, float); +DECLARE_SOA_COLUMN(DecLenV0, decLenV0, float); +DECLARE_SOA_COLUMN(ErrorDecayLengthCharmBaryon, errorDecayLengthCharmBaryon, float); +DECLARE_SOA_COLUMN(NormImpParCascade, normImpParCascade, double); +DECLARE_SOA_COLUMN(NormImpParPiFromCharmBar, normImpParPiFromCharmBar, double); +DECLARE_SOA_COLUMN(NormDecayLenCharmBar, normDecayLenCharmBar, double); +DECLARE_SOA_COLUMN(IsPionGlbTrkWoDca, isPionGlbTrkWoDca, bool); +DECLARE_SOA_COLUMN(PionItsNCls, pionItsNCls, uint8_t); +DECLARE_SOA_COLUMN(NTpcRowsPion, nTpcRowsPion, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsKaFromCasc, nTpcRowsKaFromCasc, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsPosV0Dau, nTpcRowsPosV0Dau, int16_t); +DECLARE_SOA_COLUMN(NTpcRowsNegV0Dau, nTpcRowsNegV0Dau, int16_t); +// from creator KF +DECLARE_SOA_COLUMN(KfDcaXYPiFromOmegac, kfDcaXYPiFromOmegac, float); +DECLARE_SOA_COLUMN(KfDcaXYCascToPv, kfDcaXYCascToPv, float); +DECLARE_SOA_COLUMN(Chi2GeoV0, chi2GeoV0, float); +DECLARE_SOA_COLUMN(Chi2GeoCasc, chi2GeoCasc, float); +DECLARE_SOA_COLUMN(Chi2GeoOmegac, chi2GeoOmegac, float); +DECLARE_SOA_COLUMN(Chi2MassV0, chi2MassV0, float); +DECLARE_SOA_COLUMN(Chi2MassCasc, chi2MassCasc, float); +DECLARE_SOA_COLUMN(V0ldl, v0ldl, float); +DECLARE_SOA_COLUMN(Cascldl, cascldl, float); +DECLARE_SOA_COLUMN(Omegacldl, omegacldl, float); +DECLARE_SOA_COLUMN(Chi2TopoV0ToPv, chi2TopoV0ToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoCascToPv, chi2TopoCascToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoPiFromOmegacToPv, chi2TopoPiFromOmegacToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoOmegacToPv, chi2TopoOmegacToPv, float); +DECLARE_SOA_COLUMN(Chi2TopoV0ToCasc, chi2TopoV0ToCasc, float); +DECLARE_SOA_COLUMN(Chi2TopoCascToOmegac, chi2TopoCascToOmegac, float); +DECLARE_SOA_COLUMN(DecayLenXYLambda, decayLenXYLambda, float); +DECLARE_SOA_COLUMN(DecayLenXYCasc, decayLenXYCasc, float); +DECLARE_SOA_COLUMN(DecayLenXYOmegac, decayLenXYOmegac, float); +DECLARE_SOA_COLUMN(CosPaV0ToCasc, cosPaV0ToCasc, float); +DECLARE_SOA_COLUMN(CosPaV0ToPv, cosPaV0ToPv, float); +DECLARE_SOA_COLUMN(CosPaCascToOmegac, cosPaCascToOmegac, float); +DECLARE_SOA_COLUMN(CosPaCascToPv, cosPaCascToPv, float); +DECLARE_SOA_COLUMN(CosPaOmegacToPv, cosPaOmegacToPv, float); +DECLARE_SOA_COLUMN(KfRapOmegac, kfRapOmegac, float); +DECLARE_SOA_COLUMN(KfptPiFromOmegac, kfptPiFromOmegac, float); +DECLARE_SOA_COLUMN(KfptOmegac, kfptOmegac, float); +DECLARE_SOA_COLUMN(CosThetaStarPiFromOmegac, cosThetaStarPiFromOmegac, float); +DECLARE_SOA_COLUMN(CtOmegac, ctOmegac, float); +DECLARE_SOA_COLUMN(EtaOmegac, etaOmegac, float); +DECLARE_SOA_COLUMN(V0Ndf, v0Ndf, float); +DECLARE_SOA_COLUMN(CascNdf, cascNdf, float); +DECLARE_SOA_COLUMN(OmegacNdf, omegacNdf, float); +DECLARE_SOA_COLUMN(MassV0Ndf, massV0Ndf, float); +DECLARE_SOA_COLUMN(MassCascNdf, massCascNdf, float); +DECLARE_SOA_COLUMN(V0Chi2OverNdf, v0Chi2OverNdf, float); +DECLARE_SOA_COLUMN(CascChi2OverNdf, cascChi2OverNdf, float); +DECLARE_SOA_COLUMN(OmegacChi2OverNdf, omegacChi2OverNdf, float); +DECLARE_SOA_COLUMN(MassV0Chi2OverNdf, massV0Chi2OverNdf, float); +DECLARE_SOA_COLUMN(MassCascChi2OverNdf, massCascChi2OverNdf, float); +DECLARE_SOA_COLUMN(CascRejectInvmass, cascRejectInvmass, float); +// from creator - MC +DECLARE_SOA_COLUMN(FlagMcMatchRec, flagMcMatchRec, int8_t); // reconstruction level +DECLARE_SOA_COLUMN(OriginMcRec, originMcRec, int8_t); +DECLARE_SOA_COLUMN(CollisionMatched, collisionMatched, bool); +// from selector +DECLARE_SOA_COLUMN(StatusPidLambda, statusPidLambda, bool); +DECLARE_SOA_COLUMN(StatusPidCascade, statusPidCascade, bool); +DECLARE_SOA_COLUMN(StatusPidCharmBaryon, statusPidCharmBaryon, bool); +DECLARE_SOA_COLUMN(StatusInvMassLambda, statusInvMassLambda, bool); +DECLARE_SOA_COLUMN(StatusInvMassCascade, statusInvMassCascade, bool); +DECLARE_SOA_COLUMN(StatusInvMassCharmBaryon, statusInvMassCharmBaryon, bool); +DECLARE_SOA_COLUMN(ResultSelections, resultSelections, bool); +DECLARE_SOA_COLUMN(PidTpcInfoStored, pidTpcInfoStored, int); +DECLARE_SOA_COLUMN(PidTofInfoStored, pidTofInfoStored, int); +DECLARE_SOA_COLUMN(TpcNSigmaPiFromCharmBaryon, tpcNSigmaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(TpcNSigmaKaFromCasc, tpcNSigmaKaFromCasc, float); +DECLARE_SOA_COLUMN(TpcNSigmaPiFromLambda, tpcNSigmaPiFromLambda, float); +DECLARE_SOA_COLUMN(TpcNSigmaPrFromLambda, tpcNSigmaPrFromLambda, float); +DECLARE_SOA_COLUMN(TofNSigmaPiFromCharmBaryon, tofNSigmaPiFromCharmBaryon, float); +DECLARE_SOA_COLUMN(TofNSigmaKaFromCasc, tofNSigmaKaFromCasc, float); +DECLARE_SOA_COLUMN(TofNSigmaPiFromLambda, tofNSigmaPiFromLambda, float); +DECLARE_SOA_COLUMN(TofNSigmaPrFromLambda, tofNSigmaPrFromLambda, float); +} // namespace full + +DECLARE_SOA_TABLE(HfToOmegaPiEvs, "AOD", "HFTOOMEPIEV", + full::IsEventSel8, full::IsEventSelZ); + +DECLARE_SOA_TABLE(HfOmegac0ToOmegaPi, "AOD", "HFTOOMEGAPI", + full::XPv, full::YPv, full::ZPv, full::Centrality, collision::NumContrib, collision::Chi2, + full::XDecayVtxCharmBaryon, full::YDecayVtxCharmBaryon, full::ZDecayVtxCharmBaryon, + full::XDecayVtxCascade, full::YDecayVtxCascade, full::ZDecayVtxCascade, + full::XDecayVtxV0, full::YDecayVtxV0, full::ZDecayVtxV0, + full::SignDecay, + full::PtCharmBaryon, full::PtPiFromCharmBaryon, + full::PxCharmBaryon, full::PyCharmBaryon, full::PzCharmBaryon, + full::PxPiFromCharmBaryon, full::PyPiFromCharmBaryon, full::PzPiFromCharmBaryon, + full::PxKaFromCasc, full::PyKaFromCasc, full::PzKaFromCasc, + full::PxPosV0Dau, full::PyPosV0Dau, full::PzPosV0Dau, + full::PxNegV0Dau, full::PyNegV0Dau, full::PzNegV0Dau, + full::ImpactParCascXY, full::ImpactParPiFromCharmBaryonXY, + full::ErrImpactParCascXY, full::ErrImpactParPiFromCharmBaryonXY, + full::InvMassLambda, full::InvMassCascade, full::InvMassCharmBaryon, + full::CosPAV0, full::CosPACharmBaryon, full::CosPACasc, + full::EtaV0PosDau, full::EtaV0NegDau, full::EtaKaFromCasc, full::EtaPiFromCharmBaryon, full::EtaCharmBaryon, + full::DcaXYToPvV0Dau0, full::DcaXYToPvV0Dau1, full::DcaXYToPvCascDau, + full::DcaCascDau, full::DcaV0Dau, full::DcaCharmBaryonDau, + full::DecLenCharmBaryon, + full::ErrorDecayLengthCharmBaryon, full::NormImpParCascade, full::NormImpParPiFromCharmBar, + full::NormDecayLenCharmBar, + full::IsPionGlbTrkWoDca, full::PionItsNCls, + full::NTpcRowsPion, full::NTpcRowsKaFromCasc, full::NTpcRowsPosV0Dau, full::NTpcRowsNegV0Dau, + full::StatusPidLambda, full::StatusPidCascade, full::StatusPidCharmBaryon, + full::StatusInvMassLambda, full::StatusInvMassCascade, full::StatusInvMassCharmBaryon, full::ResultSelections, + full::PidTpcInfoStored, full::PidTofInfoStored, + full::TpcNSigmaPiFromCharmBaryon, full::TpcNSigmaKaFromCasc, full::TpcNSigmaPiFromLambda, full::TpcNSigmaPrFromLambda, + full::TofNSigmaPiFromCharmBaryon, full::TofNSigmaKaFromCasc, full::TofNSigmaPiFromLambda, full::TofNSigmaPrFromLambda, + full::FlagMcMatchRec, full::OriginMcRec, full::CollisionMatched); + +DECLARE_SOA_TABLE(HfKfOmegac, "AOD", "HFKFOMEGAC", + full::Centrality, + collision::NumContrib, + full::TpcNSigmaPiFromCharmBaryon, full::TofNSigmaPiFromCharmBaryon, full::TpcNSigmaKaFromCasc, full::TofNSigmaKaFromCasc, + full::TpcNSigmaPiFromLambda, full::TofNSigmaPiFromLambda, full::TpcNSigmaPrFromLambda, full::TofNSigmaPrFromLambda, + full::KfDcaXYPiFromOmegac, full::DcaCascDau, full::DcaV0Dau, full::DcaCharmBaryonDau, full::KfDcaXYCascToPv, + full::DcaXYToPvV0Dau0, full::DcaXYToPvV0Dau1, full::DcaXYToPvCascDau, + full::Chi2GeoV0, full::Chi2GeoCasc, full::Chi2GeoOmegac, + full::Chi2MassV0, full::Chi2MassCasc, + full::V0ldl, full::Cascldl, full::Omegacldl, + full::Chi2TopoV0ToPv, full::Chi2TopoCascToPv, full::Chi2TopoPiFromOmegacToPv, full::Chi2TopoOmegacToPv, + full::Chi2TopoV0ToCasc, full::Chi2TopoCascToOmegac, + full::DecayLenXYLambda, full::DecayLenXYCasc, full::DecayLenXYOmegac, + full::CosPaV0ToCasc, full::CosPaV0ToPv, full::CosPaCascToOmegac, full::CosPaCascToPv, full::CosPaOmegacToPv, + full::InvMassLambda, full::InvMassCascade, full::InvMassCharmBaryon, + full::KfRapOmegac, full::KfptPiFromOmegac, full::KfptOmegac, + full::CosThetaStarPiFromOmegac, full::CtOmegac, full::EtaOmegac, + full::V0Ndf, full::CascNdf, full::OmegacNdf, + full::MassV0Ndf, full::MassCascNdf, + full::V0Chi2OverNdf, full::CascChi2OverNdf, full::OmegacChi2OverNdf, + full::MassV0Chi2OverNdf, full::MassCascChi2OverNdf, full::CascRejectInvmass, + full::ResultSelections, + full::FlagMcMatchRec, full::OriginMcRec, full::CollisionMatched); +} // namespace o2::aod + +/// Writes the full information in an output TTree +struct HfTreeCreatorToOmegaPiQa { + + Produces rowCandidate; + Produces rowKfCandidate; + Produces rowEv; + + Configurable zPvCut{"zPvCut", 10., "Cut on absolute value of primary vertex z coordinate"}; + Configurable keepOnlyMcSignal{"keepOnlyMcSignal", true, "Fill MC tree only with signal candidates"}; + + using MyTrackTable = soa::Join; + + using CandSel = soa::Filtered>; + using CandKfSel = soa::Filtered>; + using CandMcSel = soa::Filtered>; + using CandKfMcSel = soa::Filtered>; + + using CollsSel = soa::Join; + using CollsSelWithFT0M = soa::Join; + using CollsWithMcLabels = soa::Join; + using McCollsWithFT0M = soa::Join; + + Filter filterOmegaCToOmegaPiFlag = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_casc_lf::DecayType2Prong::OmegaczeroToOmegaPi))) != static_cast(0); + Filter filterSelectCandidates = aod::hf_sel_toomegapi::resultSelections == true; + + void init(InitContext const&) + { + } + + ////////////////////////////////////////////////////// + // // + // Fill functions to fill in the tables // + // // + ////////////////////////////////////////////////////// + + template + void fillEvent(const T& collision) + { + rowEv(collision.sel8(), std::abs(collision.posZ()) < zPvCut); + } + + template + void fillCandidate(const T& candidate, int8_t flagMc, int8_t originMc, bool collisionMatched, float centrality = -999.f) + { + + // save all candidate information + if constexpr (useCentrality) { + if (centrality < -998.f) { + auto const& collision = candidate.template collision_as(); + centrality = o2::hf_centrality::getCentralityColl(collision); + } + } + + if constexpr (svReco == DCAFITTER) { + rowCandidate(candidate.xPv(), + candidate.yPv(), + candidate.zPv(), + centrality, + candidate.template collision_as().numContrib(), + candidate.template collision_as().chi2(), + candidate.xDecayVtxCharmBaryon(), + candidate.yDecayVtxCharmBaryon(), + candidate.zDecayVtxCharmBaryon(), + candidate.xDecayVtxCascade(), + candidate.yDecayVtxCascade(), + candidate.zDecayVtxCascade(), + candidate.xDecayVtxV0(), + candidate.yDecayVtxV0(), + candidate.zDecayVtxV0(), + candidate.signDecay(), + RecoDecay::pt(candidate.pxCharmBaryon(), candidate.pyCharmBaryon()), + RecoDecay::pt(candidate.pxBachFromCharmBaryon(), candidate.pyBachFromCharmBaryon()), + candidate.pxCharmBaryon(), + candidate.pyCharmBaryon(), + candidate.pzCharmBaryon(), + candidate.pxBachFromCharmBaryon(), + candidate.pyBachFromCharmBaryon(), + candidate.pzBachFromCharmBaryon(), + candidate.pxBachFromCasc(), + candidate.pyBachFromCasc(), + candidate.pzBachFromCasc(), + candidate.pxPosV0Dau(), + candidate.pyPosV0Dau(), + candidate.pzPosV0Dau(), + candidate.pxNegV0Dau(), + candidate.pyNegV0Dau(), + candidate.pzNegV0Dau(), + candidate.impactParCascXY(), + candidate.impactParBachFromCharmBaryonXY(), + candidate.errImpactParCascXY(), + candidate.errImpactParBachFromCharmBaryonXY(), + candidate.invMassLambda(), + candidate.invMassCascade(), + candidate.invMassCharmBaryon(), + candidate.cosPAV0(), + candidate.cosPACharmBaryon(), + candidate.cosPACasc(), + candidate.etaV0PosDau(), + candidate.etaV0NegDau(), + candidate.etaBachFromCasc(), + candidate.etaBachFromCharmBaryon(), + candidate.etaCharmBaryon(), + candidate.dcaXYToPvV0Dau0(), + candidate.dcaXYToPvV0Dau1(), + candidate.dcaXYToPvCascDau(), + candidate.dcaCascDau(), + candidate.dcaV0Dau(), + candidate.dcaCharmBaryonDau(), + candidate.decLenCharmBaryon(), + candidate.errorDecayLengthCharmBaryon(), + candidate.impactParCascXY() / candidate.errImpactParCascXY(), + candidate.impactParBachFromCharmBaryonXY() / candidate.errImpactParBachFromCharmBaryonXY(), + candidate.normDecayLenCharmBar(), + candidate.template bachelorFromCharmBaryon_as().isGlobalTrackWoDCA(), + candidate.template bachelorFromCharmBaryon_as().itsNCls(), + candidate.template bachelorFromCharmBaryon_as().tpcNClsCrossedRows(), + candidate.template bachelor_as().tpcNClsCrossedRows(), + candidate.template posTrack_as().tpcNClsCrossedRows(), + candidate.template negTrack_as().tpcNClsCrossedRows(), + candidate.statusPidLambda(), + candidate.statusPidCascade(), + candidate.statusPidCharmBaryon(), + candidate.statusInvMassLambda(), + candidate.statusInvMassCascade(), + candidate.statusInvMassCharmBaryon(), + candidate.resultSelections(), + candidate.pidTpcInfoStored(), + candidate.pidTofInfoStored(), + candidate.tpcNSigmaPiFromCharmBaryon(), + candidate.tpcNSigmaKaFromCasc(), + candidate.tpcNSigmaPiFromLambda(), + candidate.tpcNSigmaPrFromLambda(), + candidate.tofNSigmaPiFromCharmBaryon(), + candidate.tofNSigmaKaFromCasc(), + candidate.tofNSigmaPiFromLambda(), + candidate.tofNSigmaPrFromLambda(), + flagMc, + originMc, + collisionMatched); + } else { + rowKfCandidate(centrality, + candidate.template collision_as().numContrib(), + candidate.tpcNSigmaPiFromCharmBaryon(), + candidate.tofNSigmaPiFromCharmBaryon(), + candidate.tpcNSigmaKaFromCasc(), + candidate.tofNSigmaKaFromCasc(), + candidate.tpcNSigmaPiFromLambda(), + candidate.tofNSigmaPiFromLambda(), + candidate.tpcNSigmaPrFromLambda(), + candidate.tofNSigmaPrFromLambda(), + candidate.kfDcaXYPiFromOmegac(), + candidate.dcaCascDau(), + candidate.dcaV0Dau(), + candidate.dcaCharmBaryonDau(), + candidate.kfDcaXYCascToPv(), + candidate.dcaXYToPvV0Dau0(), + candidate.dcaXYToPvV0Dau1(), + candidate.dcaXYToPvCascDau(), + candidate.chi2GeoV0(), + candidate.chi2GeoCasc(), + candidate.chi2GeoOmegac(), + candidate.chi2MassV0(), + candidate.chi2MassCasc(), + candidate.v0ldl(), + candidate.cascldl(), + candidate.omegacldl(), + candidate.chi2TopoV0ToPv(), + candidate.chi2TopoCascToPv(), + candidate.chi2TopoPiFromOmegacToPv(), + candidate.chi2TopoOmegacToPv(), + candidate.chi2TopoV0ToCasc(), + candidate.chi2TopoCascToOmegac(), + candidate.decayLenXYLambda(), + candidate.decayLenXYCasc(), + candidate.decayLenXYOmegac(), + candidate.cosPaV0ToCasc(), + candidate.cosPAV0(), + candidate.cosPaCascToOmegac(), + candidate.cosPACasc(), + candidate.cosPACharmBaryon(), + candidate.invMassLambda(), + candidate.invMassCascade(), + candidate.invMassCharmBaryon(), + candidate.kfRapOmegac(), + candidate.kfptPiFromOmegac(), + candidate.kfptOmegac(), + candidate.cosThetaStarPiFromOmegac(), + candidate.cTauOmegac(), + candidate.etaCharmBaryon(), + candidate.v0Ndf(), + candidate.cascNdf(), + candidate.omegacNdf(), + candidate.massV0Ndf(), + candidate.massCascNdf(), + candidate.v0Chi2OverNdf(), + candidate.cascChi2OverNdf(), + candidate.omegacChi2OverNdf(), + candidate.massV0Chi2OverNdf(), + candidate.massCascChi2OverNdf(), + candidate.cascRejectInvmass(), + candidate.resultSelections(), + flagMc, + originMc, + collisionMatched); + } + } + + //////////////////////////////////// + // // + // Process functions // + // // + //////////////////////////////////// + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~Data with DCAFitter~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processData(CollsSel const& collisions, + MyTrackTable const&, + CandSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + void processDataCent(CollsSelWithFT0M const& collisions, + MyTrackTable const&, + CandSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processData, "Process DCAFitter data, no cent", true); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processDataCent, "Process DCAFitter data, with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~Data with KFParticle~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processKfData(CollsSel const& collisions, + CandKfSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + void processKfDataCent(CollsSelWithFT0M const& collisions, + CandKfSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + fillCandidate(candidate, -7, RecoDecay::OriginType::None, false); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfData, "Process KF data, no cent", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfDataCent, "Process KF data, with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~MC with DCAFitter~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processMc(CollsSel const& collisions, + MyTrackTable const&, + CandMcSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + } + + void processMcCent(CollsWithMcLabels const& collisions, + MyTrackTable const&, + CandMcSel const& candidates, + McCollsWithFT0M const&) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + auto mcCollision = candidate.collision_as().mcCollision_as(); + float centFt0m = o2::hf_centrality::getCentralityColl(mcCollision); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched(), centFt0m); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processMc, "Process DCAFitter MC for Omegac0", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processMcCent, "Process DCAFitter MC for Omegac0 with FT0M", false); + + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + //*~~~~~~~MC with KFParticle~~~~~~~~*// + //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// + + void processKfMc(CollsSel const& collisions, + CandKfMcSel const& candidates) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + } + + void processKfMcCent(CollsWithMcLabels const& collisions, + CandKfMcSel const& candidates, + McCollsWithFT0M const&) + { + // Filling event properties + rowEv.reserve(collisions.size()); + for (const auto& collision : collisions) { + fillEvent(collision); + } + + // Filling candidate properties + rowKfCandidate.reserve(candidates.size()); + for (const auto& candidate : candidates) { + if (keepOnlyMcSignal && candidate.originMcRec() == RecoDecay::OriginType::None) { + continue; + } + auto mcCollision = candidate.collision_as().mcCollision_as(); + float centFt0m = o2::hf_centrality::getCentralityColl(mcCollision); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.originMcRec(), candidate.collisionMatched(), centFt0m); + } + } + + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfMc, "Process KF MC for Omegac0", false); + PROCESS_SWITCH(HfTreeCreatorToOmegaPiQa, processKfMcCent, "Process KF MC for Omegac0 with FT0M", false); +}; // end of struct + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGHF/TableProducer/treeCreatorOmegacSt.cxx b/PWGHF/TableProducer/treeCreatorOmegacSt.cxx index 8a952cbea49..62d03a71615 100644 --- a/PWGHF/TableProducer/treeCreatorOmegacSt.cxx +++ b/PWGHF/TableProducer/treeCreatorOmegacSt.cxx @@ -366,6 +366,9 @@ struct HfTreeCreatorOmegacSt { { mapMcPartToGenTable.clear(); for (const auto& mcParticle : mcParticles) { + origin = 0; + idxBhadMothers.clear(); + decayChannel = -1; const bool isOmegaC = std::abs(mcParticle.pdgCode()) == constants::physics::Pdg::kOmegaC0; const bool isXiC = std::abs(mcParticle.pdgCode()) == constants::physics::Pdg::kXiC0; if (!isOmegaC && !isXiC) { @@ -397,7 +400,6 @@ struct HfTreeCreatorOmegacSt { continue; } - int decayChannel = -1; const int pdgCasc = std::abs(mcParticles.iteratorAt(idxCascDaughter).pdgCode()); if (isOmegaC) { @@ -716,6 +718,11 @@ struct HfTreeCreatorOmegacSt { //--- do the MC Rec match if (mcParticles) { + isMatched = false; + origin = 0; + idxBhadMothers.clear(); + indexRecCharmBaryon = -1; + indexRec = -1; auto arrayDaughters = std::array{ trackId.template track_as(), // bachelor <- charm baryon casc.bachelor_as(), // bachelor <- cascade diff --git a/PWGHF/TableProducer/treeCreatorToXiPiQa.cxx b/PWGHF/TableProducer/treeCreatorToXiPiQa.cxx index 2e219e14355..ea8b6fbebb0 100644 --- a/PWGHF/TableProducer/treeCreatorToXiPiQa.cxx +++ b/PWGHF/TableProducer/treeCreatorToXiPiQa.cxx @@ -17,9 +17,12 @@ /// \author Krista Smith , Pusan National University #include "PWGHF/Core/CentralityEstimation.h" +#include "PWGHF/Core/DecayChannelsLegacy.h" #include "PWGHF/DataModel/CandidateReconstructionTables.h" #include "PWGHF/DataModel/CandidateSelectionTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" @@ -34,10 +37,13 @@ #include #include +#include + #include using namespace o2; using namespace o2::framework; +using namespace o2::framework::expressions; // SV Reco method enum { @@ -124,6 +130,8 @@ DECLARE_SOA_COLUMN(EtaPiFromCasc, etaPiFromCasc, float); DECLARE_SOA_COLUMN(EtaPiFromCharmBaryon, etaPiFromCharmBaryon, float); DECLARE_SOA_COLUMN(EtaCharmBaryon, etaCharmBaryon, float); DECLARE_SOA_COLUMN(EtaCascade, etaCascade, float); +DECLARE_SOA_COLUMN(PhiCharmBaryon, phiCharmBaryon, float); +DECLARE_SOA_COLUMN(YCharmBaryon, yCharmBaryon, float); DECLARE_SOA_COLUMN(EtaV0, etaV0, float); DECLARE_SOA_COLUMN(DcaXYToPvV0Dau0, dcaXYToPvV0Dau0, float); DECLARE_SOA_COLUMN(DcaXYToPvV0Dau1, dcaXYToPvV0Dau1, float); @@ -212,6 +220,14 @@ DECLARE_SOA_COLUMN(CascChi2OverNdf, cascChi2OverNdf, float); DECLARE_SOA_COLUMN(XicChi2OverNdf, xicChi2OverNdf, float); DECLARE_SOA_COLUMN(MassV0Chi2OverNdf, massV0Chi2OverNdf, float); DECLARE_SOA_COLUMN(MassCascChi2OverNdf, massCascChi2OverNdf, float); +// MC +DECLARE_SOA_COLUMN(ParticlePdg, particlePdg, int); +DECLARE_SOA_COLUMN(PtGenB, ptGenB, float); +DECLARE_SOA_COLUMN(NContribMax, nContribMax, int); +DECLARE_SOA_COLUMN(NRecoColl, nRecoColl, int); +DECLARE_SOA_COLUMN(HasRecoCollTVX, hasRecoCollTVX, bool); +DECLARE_SOA_COLUMN(HasRecoCollSel8, hasRecoCollSel8, bool); +DECLARE_SOA_COLUMN(HasRecoCollSel8Zvtx, hasRecoCollSel8Zvtx, bool); } // namespace full DECLARE_SOA_TABLE(HfToXiPiEvs, "AOD", "HFTOXIPIEV", @@ -303,6 +319,22 @@ DECLARE_SOA_TABLE(HfKfXicFulls, "AOD", "HFKFXICFULL", full::ResultSelections, full::FlagMcMatchRec, full::DebugMcRec, full::OriginRec, full::CollisionMatched); +DECLARE_SOA_TABLE(HfCandToXiPiGen, "AOD", "HFCANDTOXIPIGEN", + full::InvMassCharmBaryon, + full::PtCharmBaryon, + full::EtaCharmBaryon, + full::PhiCharmBaryon, + full::YCharmBaryon, + full::FlagMcMatchRec, + full::OriginRec, + full::ParticlePdg, + full::PtGenB, + full::NContribMax, + full::NRecoColl, + full::HasRecoCollTVX, + full::HasRecoCollSel8, + full::HasRecoCollSel8Zvtx); + } // namespace o2::aod /// Writes the full information in an output TTree @@ -311,15 +343,33 @@ struct HfTreeCreatorToXiPiQa { Produces rowCandidateFull; Produces rowCandidateLite; Produces rowKfCandidate; + Produces rowCandidateParticles; Produces rowEv; Configurable zPvCut{"zPvCut", 10., "Cut on absolute value of primary vertex z coordinate"}; + Configurable genSelection{"genSelection", o2::aod::hf_cand_xic0_omegac0::DecayType::XiczeroToXiPi, "Decay channel to be used to match particle information"}; + Configurable fillGenTable{"fillGenTable", true, "Fill generated MC information if requested"}; using MyTrackTable = soa::Join; + using MyCandTableDca = soa::Join; + using MyCandTableKf = soa::Join; + using MyCandTableXic0McDca = soa::Join; + using MyCandTableXic0McKf = soa::Join; + using MyCandTableOmegac0McDca = soa::Join; using MyEventTable = soa::Join; using MyEventTableWithFT0C = soa::Join; using MyEventTableWithFT0M = soa::Join; using MyEventTableWithNTracksPV = soa::Join; + using MyEventTableWithMcLabels = soa::Join; + + using MatchedGenXiPi = soa::Filtered>; + + using McCollisionsCentFT0Cs = soa::Join; + using McCollisionsCentFT0Ms = soa::Join; + + Filter filterGenXiPi = nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(genSelection)); + + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; void init(InitContext const&) { @@ -335,9 +385,9 @@ struct HfTreeCreatorToXiPiQa { ////////////////////////////////////////////////////// template - void fillEvent(const T& collision, float cutZPv) + void fillEvent(const T& collision) { - rowEv(collision.sel8(), std::abs(collision.posZ()) < cutZPv); + rowEv(collision.sel8(), std::abs(collision.posZ()) < zPvCut); } template @@ -611,6 +661,55 @@ struct HfTreeCreatorToXiPiQa { } } + template + void fillParticle(const CandType& mcParticles, const CollType& collisions) + { + for (const auto& particle : mcParticles) { + + auto ptGen = particle.pt(); + auto yGen = particle.rapidityCharmBaryonGen(); + + int nContribMax = 0; + bool recoCollPassedTvx = false; + bool recoCollPassedSel8 = false; + bool recoCollPassedSel8Zvtx = false; + + auto mcCollision = particle.template mcCollision_as(); + const auto& recoCollsPerMcColl = collisions.sliceBy(colPerMcCollision, mcCollision.globalIndex()); + + for (const auto& recoCol : recoCollsPerMcColl) { + nContribMax = recoCol.numContrib() > nContribMax ? recoCol.numContrib() : nContribMax; + if (recoCol.selection_bit(aod::evsel::kIsTriggerTVX)) { + recoCollPassedTvx = true; + if (recoCol.sel8()) { + recoCollPassedSel8 = true; + if (std::abs(recoCol.posZ()) < zPvCut) { + recoCollPassedSel8Zvtx = true; + break; + } + } + } + } + + float ptGenBhad = (particle.originMcGen() == RecoDecay::OriginType::NonPrompt) ? mcParticles.rawIteratorAt(particle.idxBhadMotherPart()).pt() : -999.f; + + rowCandidateParticles(RecoDecay::m(particle.pVector(), particle.e()), + ptGen, + particle.eta(), + particle.phi(), + yGen, + particle.flagMcMatchGen(), + particle.originMcGen(), + particle.pdgCode(), + ptGenBhad, + nContribMax, + recoCollsPerMcColl.size(), + recoCollPassedTvx, + recoCollPassedSel8, + recoCollPassedSel8Zvtx); + } + } + //////////////////////////////////// // // // Process functions // @@ -621,13 +720,14 @@ struct HfTreeCreatorToXiPiQa { //*~~~~~~~Data with DCAFitter~~~~~~~~*// //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// - void processDataFull(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processDataFull(MyEventTable const& collisions, + MyTrackTable const&, + MyCandTableDca const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -637,13 +737,14 @@ struct HfTreeCreatorToXiPiQa { } } - void processDataLite(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processDataLite(MyEventTable const& collisions, + MyTrackTable const&, + MyCandTableDca const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -653,13 +754,14 @@ struct HfTreeCreatorToXiPiQa { } } - void processDataLiteWithFT0M(MyEventTableWithFT0M const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processDataLiteWithFT0M(MyEventTableWithFT0M const& collisions, + MyTrackTable const&, + MyCandTableDca const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -669,13 +771,14 @@ struct HfTreeCreatorToXiPiQa { } } - void processDataLiteWithFT0C(MyEventTableWithFT0C const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processDataLiteWithFT0C(MyEventTableWithFT0C const& collisions, + MyTrackTable const&, + MyCandTableDca const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -685,13 +788,14 @@ struct HfTreeCreatorToXiPiQa { } } - void processDataLiteWithNTracksPV(MyEventTableWithNTracksPV const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processDataLiteWithNTracksPV(MyEventTableWithNTracksPV const& collisions, + MyTrackTable const&, + MyCandTableDca const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -710,13 +814,14 @@ struct HfTreeCreatorToXiPiQa { //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// //*~~~~~~~Data with KFParticle~~~~~~~~*// //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// - void processKfData(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfData(MyEventTable const& collisions, + MyTrackTable const&, + MyCandTableKf const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -726,13 +831,14 @@ struct HfTreeCreatorToXiPiQa { } } - void processKfDataWithFT0M(MyEventTableWithFT0M const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfDataWithFT0M(MyEventTableWithFT0M const& collisions, + MyTrackTable const&, + MyCandTableKf const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -742,13 +848,14 @@ struct HfTreeCreatorToXiPiQa { } } - void processKfDataWithFT0C(MyEventTableWithFT0C const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfDataWithFT0C(MyEventTableWithFT0C const& collisions, + MyTrackTable const&, + MyCandTableKf const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -758,13 +865,14 @@ struct HfTreeCreatorToXiPiQa { } } - void processKfDataWithNTracksPV(MyEventTableWithNTracksPV const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfDataWithNTracksPV(MyEventTableWithNTracksPV const& collisions, + MyTrackTable const&, + MyCandTableKf const& candidates) { // Filling event properties rowEv.reserve(collisions.size()); for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + fillEvent(collision); } // Filling candidate properties @@ -783,45 +891,66 @@ struct HfTreeCreatorToXiPiQa { //*~~~~~~~MC with DCAFitter~~~~~~~~*// //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// - void processMcFullXic0(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processMcFullXic0(MyEventTableWithMcLabels const& collsWithMcLable, + aod::McCollisions const&, + MyTrackTable const&, + MyCandTableXic0McDca const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties rowCandidateFull.reserve(candidates.size()); for (const auto& candidate : candidates) { - fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); } } - void processMcFullOmegac0(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processMcFullOmegac0(MyEventTableWithMcLabels const& collsWithMcLable, + aod::McCollisions const&, + MyTrackTable const&, + MyCandTableOmegac0McDca const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties rowCandidateFull.reserve(candidates.size()); for (const auto& candidate : candidates) { - fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); + fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); + } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); } } - void processMcLiteXic0(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processMcLiteXic0(MyEventTableWithMcLabels const& collsWithMcLable, + aod::McCollisions const&, + MyTrackTable const&, + MyCandTableXic0McDca const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -829,15 +958,24 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), -7, candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } - void processMcLiteXic0WithFT0C(MyEventTableWithFT0C const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processMcLiteXic0WithFT0C(MyEventTableWithMcLabels const& collsWithMcLable, + McCollisionsCentFT0Cs const&, + MyTrackTable const&, + MyCandTableXic0McDca const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -845,15 +983,24 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), -7, candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } - void processMcLiteXic0WithFT0M(MyEventTableWithFT0M const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processMcLiteXic0WithFT0M(MyEventTableWithMcLabels const& collsWithMcLable, + McCollisionsCentFT0Ms const&, + MyTrackTable const&, + MyCandTableXic0McDca const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -861,15 +1008,24 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), -7, candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } - void processMcLiteXic0WithNTracksPV(MyEventTableWithNTracksPV const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processMcLiteXic0WithNTracksPV(MyEventTableWithMcLabels const& collsWithMcLable, + aod::McCollisions const&, + MyTrackTable const&, + MyCandTableXic0McDca const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -877,15 +1033,24 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), -7, candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } - void processMcLiteOmegac0(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processMcLiteOmegac0(MyEventTableWithMcLabels const& collsWithMcLable, + aod::McCollisions const&, + MyTrackTable const&, + MyCandTableOmegac0McDca const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -893,6 +1058,12 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), -7, candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } PROCESS_SWITCH(HfTreeCreatorToXiPiQa, processMcFullXic0, "Process MC with full information for xic0 w/o centrality", false); @@ -906,13 +1077,16 @@ struct HfTreeCreatorToXiPiQa { //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// //*~~~~~~~MC with KFParticle~~~~~~~~*// //*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*// - void processKfMcXic0(MyEventTable const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfMcXic0(MyEventTableWithMcLabels const& collsWithMcLable, + aod::McCollisions const&, + MyTrackTable const&, + MyCandTableXic0McKf const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -920,15 +1094,24 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } - void processKfMcXic0WithFT0C(MyEventTableWithFT0C const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfMcXic0WithFT0C(MyEventTableWithMcLabels const& collsWithMcLable, + McCollisionsCentFT0Cs const&, + MyTrackTable const&, + MyCandTableXic0McKf const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -936,15 +1119,24 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } - void processKfMcXic0WithFT0M(MyEventTableWithFT0M const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfMcXic0WithFT0M(MyEventTableWithMcLabels const& collsWithMcLable, + McCollisionsCentFT0Ms const&, + MyTrackTable const&, + MyCandTableXic0McKf const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate properties @@ -952,15 +1144,24 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } - void processKfMcXic0WithNTracksPV(MyEventTableWithNTracksPV const& collisions, MyTrackTable const&, - soa::Join const& candidates) + void processKfMcXic0WithNTracksPV(MyEventTableWithMcLabels const& collsWithMcLable, + aod::McCollisions const&, + MyTrackTable const&, + MyCandTableXic0McKf const& candidates, + MatchedGenXiPi const& mcParticles) { // Filling event properties - rowEv.reserve(collisions.size()); - for (const auto& collision : collisions) { - fillEvent(collision, zPvCut); + rowEv.reserve(collsWithMcLable.size()); + for (const auto& collision : collsWithMcLable) { + fillEvent(collision); } // Filling candidate table @@ -968,6 +1169,12 @@ struct HfTreeCreatorToXiPiQa { for (const auto& candidate : candidates) { fillCandidate(candidate, candidate.flagMcMatchRec(), candidate.debugMcRec(), candidate.originMcRec(), candidate.collisionMatched()); } + + // Filling particle properties if requested + if (fillGenTable) { + rowCandidateParticles.reserve(mcParticles.size()); + fillParticle(mcParticles, collsWithMcLable); + } } PROCESS_SWITCH(HfTreeCreatorToXiPiQa, processKfMcXic0, "Process MC with information for xic0", false); diff --git a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx index 11fbbf3508b..338fe6ca1fd 100644 --- a/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx +++ b/PWGHF/TableProducer/treeCreatorXicToXiPiPi.cxx @@ -31,10 +31,13 @@ #include #include +#include + #include #include #include +#include using namespace o2; using namespace o2::framework; @@ -70,13 +73,20 @@ DECLARE_SOA_COLUMN(CpaXY, cpaXY, float); //! DECLARE_SOA_COLUMN(PtXi, ptXi, float); //! Transverse momentum of Xi (prong0) (GeV/c) DECLARE_SOA_COLUMN(ImpactParameterXi, impactParameterXi, float); //! Impact parameter of Xi (prong0) DECLARE_SOA_COLUMN(ImpactParameterNormalisedXi, impactParameterNormalisedXi, float); //! Normalised impact parameter of Xi (prong0) +DECLARE_SOA_COLUMN(EtaBachelorPi, etaBachelorPi, float); //! Pseudorapidity of bachelor Pi DECLARE_SOA_COLUMN(PPi0, pPi0, float); //! Momentum of Pi0 (prong1) (GeV/c) DECLARE_SOA_COLUMN(PtPi0, ptPi0, float); //! Transverse momentum of Pi0 (prong1) (GeV/c) +DECLARE_SOA_COLUMN(EtaPi0, etaPi0, float); //! Pseudorapidity of Pi0 (prong1 DECLARE_SOA_COLUMN(ImpactParameterPi0, impactParameterPi0, float); //! Impact parameter of Pi0 (prong1) DECLARE_SOA_COLUMN(ImpactParameterNormalisedPi0, impactParameterNormalisedPi0, float); //! Normalised impact parameter of Pi0 (prong1) DECLARE_SOA_COLUMN(PPi1, pPi1, float); //! Momentum of Pi1 (prong2) (GeV/c) DECLARE_SOA_COLUMN(PtPi1, ptPi1, float); //! Transverse momentum of Pi1 (prong2) (GeV/c) -DECLARE_SOA_COLUMN(ImpactParameterPi1, impactParameterPi1, float); //! Normalised impact parameter of Pi1 (prong2) +DECLARE_SOA_COLUMN(EtaPi1, etaPi1, float); //! Pseudorapidity of Pi1 (prong2) +DECLARE_SOA_COLUMN(PtPiFromLambda, ptPiFromLambda, float); //! Transverse momentum of pion from Lambda (GeV/c) +DECLARE_SOA_COLUMN(EtaPiFromLambda, etaPiFromLambda, float); //! Pseudorapidity of pion from Lambda (GeV/c) +DECLARE_SOA_COLUMN(PtPrFromLambda, ptPrFromLambda, float); //! Transverse momentum of proton from Lambda (GeV/c) +DECLARE_SOA_COLUMN(EtaPrFromLambda, etaPrFromLambda, float); //! Pseudorapidity of proton from Lambda (GeV/c) +DECLARE_SOA_COLUMN(ImpactParameterPi1, impactParameterPi1, float); //! Impact parameter of Pi1 (prong2) DECLARE_SOA_COLUMN(ImpactParameterNormalisedPi1, impactParameterNormalisedPi1, float); //! Normalised impact parameter of Pi1 (prong2) DECLARE_SOA_COLUMN(MaxNormalisedDeltaIP, maxNormalisedDeltaIP, float); //! Maximum normalized difference between measured and expected impact parameter of candidate prongs DECLARE_SOA_COLUMN(MlScoreBkg, mlScoreBkg, float); //! ML score for background class @@ -132,6 +142,7 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiLiteKfs, "AOD", "HFXICXI2PILITKF", full::PtXi, full::PtPi0, full::PtPi1, + hf_cand_xic_to_xi_pi_pi::PtBachelorPi, full::M, full::Chi2SV, full::Ct, @@ -145,15 +156,15 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiLiteKfs, "AOD", "HFXICXI2PILITKF", hf_cand_xic_to_xi_pi_pi::CpaXYXi, hf_cand_xic_to_xi_pi_pi::CpaLambda, hf_cand_xic_to_xi_pi_pi::CpaXYLambda, - hf_cand_xic_to_xi_pi_pi::CpaLambdaToXi, - hf_cand_xic_to_xi_pi_pi::CpaXYLambdaToXi, + hf_cand_xic_to_xi_pi_pi::RadiusLambda, + hf_cand_xic_to_xi_pi_pi::InvMassXi, + hf_cand_xic_to_xi_pi_pi::InvMassLambda, full::ImpactParameterXi, full::ImpactParameterNormalisedXi, full::ImpactParameterPi0, full::ImpactParameterNormalisedPi0, full::ImpactParameterPi1, full::ImpactParameterNormalisedPi1, - full::MaxNormalisedDeltaIP, hf_cand_xic_to_xi_pi_pi::DcaXiDaughters, hf_cand_xic_to_xi_pi_pi::DcaV0Daughters, hf_cand_xic_to_xi_pi_pi::DcaXYCascToPV, @@ -175,11 +186,11 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiLiteKfs, "AOD", "HFXICXI2PILITKF", // KF specific columns full::Chi2GeoXi, full::Chi2GeoLambda, - hf_cand_xic_to_xi_pi_pi::Chi2TopoXicPlusToPVBefConst, + hf_cand_xic_to_xi_pi_pi::Chi2XicPlusToPV, hf_cand_xic_to_xi_pi_pi::Chi2TopoXicPlusToPV, - hf_cand_xic_to_xi_pi_pi::Chi2PrimXi, - hf_cand_xic_to_xi_pi_pi::Chi2PrimPi0, - hf_cand_xic_to_xi_pi_pi::Chi2PrimPi1, + hf_cand_xic_to_xi_pi_pi::Chi2XiToPV, + hf_cand_xic_to_xi_pi_pi::Chi2Pi0ToPV, + hf_cand_xic_to_xi_pi_pi::Chi2Pi1ToPV, hf_cand_xic_to_xi_pi_pi::Chi2DevPi0Pi1, hf_cand_xic_to_xi_pi_pi::Chi2DevPi0Xi, hf_cand_xic_to_xi_pi_pi::Chi2DevPi1Xi, @@ -196,27 +207,20 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiLiteMLs, "AOD", "HFXICXI2PIMLITE", full::CandidateSelFlag, full::Y, full::Eta, - full::Phi, full::P, full::Pt, full::M, - hf_cand_xic_to_xi_pi_pi::InvMassXi, - hf_cand_xic_to_xi_pi_pi::InvMassLambda, full::DecayLength, - full::DecayLengthXY, full::Cpa, - full::CpaXY, - hf_cand_xic_to_xi_pi_pi::CpaXi, - hf_cand_xic_to_xi_pi_pi::CpaXYXi, - hf_cand_xic_to_xi_pi_pi::CpaLambda, - hf_cand_xic_to_xi_pi_pi::CpaXYLambda, - full::ImpactParameterXi, - full::ImpactParameterNormalisedXi, full::ImpactParameterPi0, full::ImpactParameterNormalisedPi0, full::ImpactParameterPi1, full::ImpactParameterNormalisedPi1, - full::MaxNormalisedDeltaIP, + hf_cand_xic_to_xi_pi_pi::NSigTpcPiFromXicPlus0, + hf_cand_xic_to_xi_pi_pi::NSigTpcPiFromXicPlus1, + hf_cand_xic_to_xi_pi_pi::NSigTpcBachelorPi, + hf_cand_xic_to_xi_pi_pi::NSigTpcPiFromLambda, + hf_cand_xic_to_xi_pi_pi::NSigTpcPrFromLambda, full::MlScoreBkg, full::MlScorePrompt, full::MlScoreNonPrompt); @@ -294,6 +298,7 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullKfs, "AOD", "HFXICXI2PIFULKF", full::PtXi, full::PtPi0, full::PtPi1, + hf_cand_xic_to_xi_pi_pi::PtBachelorPi, full::M, full::Chi2SV, full::Ct, @@ -309,6 +314,9 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullKfs, "AOD", "HFXICXI2PIFULKF", hf_cand_xic_to_xi_pi_pi::CpaXYLambda, hf_cand_xic_to_xi_pi_pi::CpaLambdaToXi, hf_cand_xic_to_xi_pi_pi::CpaXYLambdaToXi, + hf_cand_xic_to_xi_pi_pi::RadiusLambda, + hf_cand_xic_to_xi_pi_pi::InvMassXi, + hf_cand_xic_to_xi_pi_pi::InvMassLambda, full::ImpactParameterXi, full::ImpactParameterNormalisedXi, full::ImpactParameterPi0, @@ -324,10 +332,8 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullKfs, "AOD", "HFXICXI2PIFULKF", hf_cand_xic_to_xi_pi_pi::DcaPosToPV, hf_cand_xic_to_xi_pi_pi::DcaNegToPV, // additional columns only stored in the full candidate table - hf_cand_xic_to_xi_pi_pi::InvMassXi, hf_cand_xic_to_xi_pi_pi::InvMassXiPi0, hf_cand_xic_to_xi_pi_pi::InvMassXiPi1, - hf_cand_xic_to_xi_pi_pi::InvMassLambda, full::P, full::PPi0, full::PPi1, @@ -348,11 +354,11 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullKfs, "AOD", "HFXICXI2PIFULKF", // KF-specific columns full::Chi2GeoXi, full::Chi2GeoLambda, - hf_cand_xic_to_xi_pi_pi::Chi2TopoXicPlusToPVBefConst, + hf_cand_xic_to_xi_pi_pi::Chi2XicPlusToPV, hf_cand_xic_to_xi_pi_pi::Chi2TopoXicPlusToPV, - hf_cand_xic_to_xi_pi_pi::Chi2PrimXi, - hf_cand_xic_to_xi_pi_pi::Chi2PrimPi0, - hf_cand_xic_to_xi_pi_pi::Chi2PrimPi1, + hf_cand_xic_to_xi_pi_pi::Chi2XiToPV, + hf_cand_xic_to_xi_pi_pi::Chi2Pi0ToPV, + hf_cand_xic_to_xi_pi_pi::Chi2Pi1ToPV, hf_cand_xic_to_xi_pi_pi::Chi2DevPi0Pi1, hf_cand_xic_to_xi_pi_pi::Chi2DevPi0Xi, hf_cand_xic_to_xi_pi_pi::Chi2DevPi1Xi, @@ -368,10 +374,21 @@ DECLARE_SOA_TABLE(HfCandXicToXiPiPiFullPs, "AOD", "HFXICXI2PIFULLP", hf_cand_mc_flag::OriginMcGen, hf_cand_mc_flag::PdgBhadMotherPart, full::Pt, + full::M, full::Eta, full::Phi, full::Y, - hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen); + hf_cand_xic_to_xi_pi_pi::DecayLengthMcGen, + full::PtPi0, + full::PtPi1, + hf_cand_xic_to_xi_pi_pi::PtBachelorPi, + full::PtPiFromLambda, + full::PtPrFromLambda, + full::EtaPi0, + full::EtaPi1, + full::EtaBachelorPi, + full::EtaPiFromLambda, + full::EtaPrFromLambda); } // namespace o2::aod /// Writes the full information in an output TTree @@ -386,6 +403,7 @@ struct HfTreeCreatorXicToXiPiPi { Configurable selectionFlagXic{"selectionFlagXic", 1, "Selection Flag for Xic"}; Configurable fillCandidateLiteTable{"fillCandidateLiteTable", false, "Switch to fill lite table with candidate properties"}; Configurable fillGenParticleTable{"fillGenParticleTable", false, "Switch to fill table with MC truth for generated particles"}; + Configurable fillGenInfoForAcceptance{"fillGenInfoForAcceptance", true, "Enable filling of information for acceptance computation"}; // parameters for production of training samples Configurable fillOnlySignal{"fillOnlySignal", false, "Flag to fill derived tables with signal for ML trainings"}; Configurable fillOnlyBackground{"fillOnlyBackground", false, "Flag to fill derived tables with background for ML trainings"}; @@ -403,12 +421,12 @@ struct HfTreeCreatorXicToXiPiPi { using MatchedGenXicToXiPiPi = soa::Filtered>; Filter filterSelectCandidates = aod::hf_sel_candidate_xic::isSelXicToXiPiPi >= selectionFlagXic; - Filter filterGenXicToXiPiPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi))); + Filter filterGenXicToXiPiPi = (nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi)) || nabs(aod::hf_cand_mc_flag::flagMcMatchGen) == static_cast(BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) || fillGenInfoForAcceptance == true); - Partition recSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != int8_t(0); - Partition recBg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == int8_t(0); - Partition recSigKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != int8_t(0); - Partition recBgKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == int8_t(0); + Partition recSig = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(0); + Partition recBg = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(0); + Partition recSigKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) != static_cast(0); + Partition recBgKf = nabs(aod::hf_cand_mc_flag::flagMcMatchRec) == static_cast(0); void init(InitContext const&) { @@ -541,6 +559,7 @@ struct HfTreeCreatorXicToXiPiPi { candidate.ptProng0(), candidate.ptProng1(), candidate.ptProng2(), + candidate.ptBachelorPi(), candidate.invMassXicPlus(), candidate.chi2PCA(), candidate.ct(o2::constants::physics::MassXiCPlus), @@ -554,15 +573,15 @@ struct HfTreeCreatorXicToXiPiPi { candidate.cpaXYXi(), candidate.cpaLambda(), candidate.cpaXYLambda(), - candidate.cpaLambdaToXi(), - candidate.cpaXYLambdaToXi(), + candidate.radiusLambda(), + candidate.invMassXi(), + candidate.invMassLambda(), candidate.impactParameter0(), candidate.impactParameterNormalised0(), candidate.impactParameter1(), candidate.impactParameterNormalised1(), candidate.impactParameter2(), candidate.impactParameterNormalised2(), - candidate.maxNormalisedDeltaIP(), candidate.dcaXiDaughters(), candidate.dcaV0Daughters(), candidate.dcaXYCascToPV(), @@ -584,11 +603,11 @@ struct HfTreeCreatorXicToXiPiPi { // KF-specific columns candidate.kfCascadeChi2(), candidate.kfV0Chi2(), - candidate.chi2TopoXicPlusToPVBefConst(), + candidate.chi2XicPlusToPV(), candidate.chi2TopoXicPlusToPV(), - candidate.chi2PrimXi(), - candidate.chi2PrimPi0(), - candidate.chi2PrimPi1(), + candidate.chi2XiToPV(), + candidate.chi2Pi0ToPV(), + candidate.chi2Pi1ToPV(), candidate.chi2DevPi0Pi1(), candidate.chi2DevPi0Xi(), candidate.chi2DevPi1Xi(), @@ -610,6 +629,7 @@ struct HfTreeCreatorXicToXiPiPi { candidate.ptProng0(), candidate.ptProng1(), candidate.ptProng2(), + candidate.ptBachelorPi(), candidate.invMassXicPlus(), candidate.chi2PCA(), candidate.ct(o2::constants::physics::MassXiCPlus), @@ -625,6 +645,9 @@ struct HfTreeCreatorXicToXiPiPi { candidate.cpaXYLambda(), candidate.cpaLambdaToXi(), candidate.cpaXYLambdaToXi(), + candidate.radiusLambda(), + candidate.invMassXi(), + candidate.invMassLambda(), candidate.impactParameter0(), candidate.impactParameterNormalised0(), candidate.impactParameter1(), @@ -640,10 +663,8 @@ struct HfTreeCreatorXicToXiPiPi { candidate.dcaPosToPV(), candidate.dcaNegToPV(), // additional columns only stored in the full candidate table - candidate.invMassXi(), candidate.invMassXiPi0(), candidate.invMassXiPi1(), - candidate.invMassLambda(), candidate.p(), candidate.pProng1(), candidate.pProng2(), @@ -664,11 +685,11 @@ struct HfTreeCreatorXicToXiPiPi { // KF-specific columns candidate.kfCascadeChi2(), candidate.kfV0Chi2(), - candidate.chi2TopoXicPlusToPVBefConst(), + candidate.chi2XicPlusToPV(), candidate.chi2TopoXicPlusToPV(), - candidate.chi2PrimXi(), - candidate.chi2PrimPi0(), - candidate.chi2PrimPi1(), + candidate.chi2XiToPV(), + candidate.chi2Pi0ToPV(), + candidate.chi2Pi1ToPV(), candidate.chi2DevPi0Pi1(), candidate.chi2DevPi0Xi(), candidate.chi2DevPi1Xi(), @@ -696,33 +717,81 @@ struct HfTreeCreatorXicToXiPiPi { candidate.isSelXicToXiPiPi(), candidate.y(o2::constants::physics::MassXiCPlus), candidate.eta(), - candidate.phi(), candidate.p(), candidate.pt(), candidate.invMassXicPlus(), - candidate.invMassXi(), - candidate.invMassLambda(), candidate.decayLength(), - candidate.decayLengthXY(), candidate.cpa(), - candidate.cpaXY(), - candidate.cpaXi(), - candidate.cpaXYXi(), - candidate.cpaLambda(), - candidate.cpaXYLambda(), - candidate.impactParameter0(), - candidate.impactParameterNormalised0(), candidate.impactParameter1(), candidate.impactParameterNormalised1(), candidate.impactParameter2(), candidate.impactParameterNormalised2(), - candidate.maxNormalisedDeltaIP(), + candidate.nSigTpcPiFromXicPlus0(), + candidate.nSigTpcPiFromXicPlus1(), + candidate.nSigTpcBachelorPi(), + candidate.nSigTpcPiFromLambda(), + candidate.nSigTpcPrFromLambda(), mlScoreBkg, mlScorePrompt, mlScoreNonPrompt); } } + template + void getMcDaughterInformation(Parts const& particles, + Parts::iterator const& particle, + float& ptPi0, + float& ptPi1, + float& ptPiBachelor, + float& ptPiFromLambda, + float& ptPrFromLambda, + float& etaPi0, + float& etaPi1, + float& etaPiBachelor, + float& etaPiFromLambda, + float& etaPrFromLambda) + { + std::vector arrDaughIndex = {}; + RecoDecay::getDaughters(particle, &arrDaughIndex, std::array{+kXiMinus, +kPiPlus, +kPiPlus}, 2); + bool foundFirstPi{false}; + for (const int iProng : arrDaughIndex) { + auto daugh = particles.rawIteratorAt(iProng); + if (std::abs(daugh.pdgCode()) == +kPiPlus) { + if (foundFirstPi) { + ptPi1 = daugh.pt(); + etaPi1 = daugh.eta(); + } else { + ptPi0 = daugh.pt(); + etaPi0 = daugh.eta(); + foundFirstPi = true; + } + } else if (std::abs(daugh.pdgCode()) == +kXiMinus) { + std::vector arrDaughIndexXi = {}; + RecoDecay::getDaughters(daugh, &arrDaughIndexXi, std::array{+kLambda0, -kPiPlus}, 1); + for (const int iProngXi : arrDaughIndexXi) { + auto daughXi = particles.rawIteratorAt(iProngXi); + if (std::abs(daughXi.pdgCode()) == +kPiPlus) { + ptPiBachelor = daughXi.pt(); + etaPiBachelor = daughXi.eta(); + } else { + std::vector arrDaughIndexLambda = {}; + RecoDecay::getDaughters(daughXi, &arrDaughIndexLambda, std::array{+kProton, -kPiPlus}, 1); + for (const int iProngLambda : arrDaughIndexLambda) { + auto daughLambda = particles.rawIteratorAt(iProngLambda); + if (std::abs(daughLambda.pdgCode()) == +kPiPlus) { + ptPiFromLambda = daughLambda.pt(); + etaPiFromLambda = daughLambda.eta(); + } else { + ptPrFromLambda = daughLambda.pt(); + etaPrFromLambda = daughLambda.eta(); + } + } + } + } + } + } + } + void processData(SelectedCandidates const& candidates) { // Filling candidate properties @@ -814,15 +883,34 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } @@ -868,15 +956,34 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } @@ -889,14 +996,14 @@ struct HfTreeCreatorXicToXiPiPi { rowCandidateLiteMl.reserve(candidates.size()); if (fillOnlySignal) { for (const auto& candidate : candidates) { - if (candidate.flagMcMatchRec() == int8_t(0)) { + if (candidate.flagMcMatchRec() == static_cast(0)) { continue; } fillCandidateTable(candidate); } } else if (fillOnlyBackground) { for (const auto& candidate : candidates) { - if (candidate.flagMcMatchRec() != int8_t(0)) { + if (candidate.flagMcMatchRec() != static_cast(0)) { continue; } float const pseudoRndm = candidate.ptProng1() * 1000. - static_cast(candidate.ptProng1() * 1000); @@ -914,15 +1021,35 @@ struct HfTreeCreatorXicToXiPiPi { if (fillGenParticleTable) { rowCandidateFullParticles.reserve(particles.size()); for (const auto& particle : particles) { + float ptPi0{-1.f}, ptPi1{-1.f}, ptPiBachelor{-1.f}, ptPiFromLambda{-1.f}, ptPrFromLambda{-1.f}; + float etaPi0{-1.f}, etaPi1{-1.f}, etaPiBachelor{-1.f}, etaPiFromLambda{-1.f}, etaPrFromLambda{-1.f}; + if (fillGenInfoForAcceptance) { + if (std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) && std::abs(particle.flagMcMatchGen()) != BIT(aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiResPiToXiPiPi)) { // we cannot filter, otherwise we have no daughter particles in the filtered table + continue; + } + getMcDaughterInformation(particles, particle, ptPi0, ptPi1, ptPiBachelor, ptPiFromLambda, ptPrFromLambda, etaPi0, etaPi1, etaPiBachelor, etaPiFromLambda, etaPrFromLambda); + } + rowCandidateFullParticles( particle.flagMcMatchGen(), particle.originMcGen(), particle.pdgBhadMotherPart(), particle.pt(), + particle.massMcGen(), particle.eta(), particle.phi(), RecoDecay::y(particle.pVector(), o2::constants::physics::MassXiCPlus), - particle.decayLengthMcGen()); + particle.decayLengthMcGen(), + ptPi0, + ptPi1, + ptPiBachelor, + ptPiFromLambda, + ptPrFromLambda, + etaPi0, + etaPi1, + etaPiBachelor, + etaPiFromLambda, + etaPrFromLambda); } } } diff --git a/PWGHF/Tasks/CMakeLists.txt b/PWGHF/Tasks/CMakeLists.txt index d75bbfee3c8..6c4a4a71b71 100644 --- a/PWGHF/Tasks/CMakeLists.txt +++ b/PWGHF/Tasks/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(task-charm-had-impact-par SOURCES taskCharmHadImpactPar.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -21,11 +22,13 @@ o2physics_add_dpl_workflow(task-charm-had-impact-par o2physics_add_dpl_workflow(task-mc-efficiency SOURCES taskMcEfficiency.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-mc-efficiency-to-xi-pi SOURCES taskMcEfficiencyToXiPi.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -36,6 +39,7 @@ o2physics_add_dpl_workflow(task-mc-gen-pt-rap-shapes o2physics_add_dpl_workflow(task-mc-validation SOURCES taskMcValidation.cxx + REUSE_FROM HfCandidatesPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGHF/Tasks/taskLcCentrality.cxx b/PWGHF/Tasks/taskLcCentrality.cxx index ce3752b40e1..0a34527690f 100644 --- a/PWGHF/Tasks/taskLcCentrality.cxx +++ b/PWGHF/Tasks/taskLcCentrality.cxx @@ -71,7 +71,7 @@ struct HfTaskLcCentrality { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hMass", "3-prong candidates;inv. mass (p K #pi) (GeV/#it{c}^{2}); p_{T}; centrality", {HistType::kTH3F, {{500, 1.6, 3.1}, {vbins, "#it{p}_{T} (GeV/#it{c})"}, {100, 0., 100.}}}); registry.add("hDecLength", "3-prong candidates;decay length (cm);entries", {HistType::kTH2F, {{200, 0., 2.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hd0Prong0", "3-prong candidates;prong 0 DCAxy to prim. vertex (cm);entries", {HistType::kTH2F, {{100, -1., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -147,7 +147,7 @@ struct HfTaskLcCentralityMc { void init(InitContext&) { - auto vbins = (std::vector)binsPt; + auto vbins = std::vector(binsPt); registry.add("hPtRecSig", "3-prong candidates (matched);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPtRecSigPrompt", "3-prong candidates (matched, prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPtRecSigNonPrompt", "3-prong candidates (matched, non-prompt);#it{p}_{T}^{rec.} (GeV/#it{c});entries", {HistType::kTH1F, {{vbins, "#it{p}_{T} (GeV/#it{c})"}}}); diff --git a/PWGHF/Tasks/taskMcEfficiency.cxx b/PWGHF/Tasks/taskMcEfficiency.cxx index dc9b708dfeb..246732243d2 100644 --- a/PWGHF/Tasks/taskMcEfficiency.cxx +++ b/PWGHF/Tasks/taskMcEfficiency.cxx @@ -118,7 +118,7 @@ struct HfTaskMcEfficiency { } template - bool checkTrack(T track) + bool checkTrack(const T& track) { // TODO configurable? return track.isGlobalTrackWoDCA(); @@ -424,7 +424,7 @@ struct HfTaskMcEfficiency { } template - void candidate2ProngMcLoop(C const& candidates, TracksWithSelectionMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisionLabels const&, std::vector pdgCodes) + void candidate2ProngMcLoop(C const& candidates, TracksWithSelectionMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisionLabels const&, const std::vector& pdgCodes) { candidate2ProngLoop(candidates, tracks, mcParticles, pdgCodes); @@ -537,7 +537,7 @@ struct HfTaskMcEfficiency { /// 3-prong analyses template - void candidate3ProngMcLoop(C const& candidates, TracksWithSelectionMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisionLabels const&, std::vector pdgCodes) + void candidate3ProngMcLoop(C const& candidates, TracksWithSelectionMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisionLabels const&, const std::vector& pdgCodes) { candidate3ProngLoop(candidates, tracks, mcParticles, pdgCodes); diff --git a/PWGHF/Tasks/taskMcValidation.cxx b/PWGHF/Tasks/taskMcValidation.cxx index 4cbb1100766..8fe64b12f51 100644 --- a/PWGHF/Tasks/taskMcValidation.cxx +++ b/PWGHF/Tasks/taskMcValidation.cxx @@ -30,6 +30,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -114,14 +115,92 @@ constexpr std::array PDGArrayParticle = {o2::constants::physics: o2::constants::physics::Pdg::kLambdaCPlus, o2::constants::physics::Pdg::kLambdaCPlus, o2::constants::physics::Pdg::kXiCPlus, o2::constants::physics::Pdg::kXiCPlus, o2::constants::physics::Pdg::kXiC0, o2::constants::physics::Pdg::kOmegaC0, o2::constants::physics::Pdg::kOmegaC0}; -constexpr std::array NDaughters = {2, 3, 3, 3, 3, 3, 5, 5, 4, 4, 4, 3, 4, 3, 3, 3, 5, 4, 4, 4}; -constexpr std::array MaxDepthForSearch = {1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 2, 3, 2, 3, 2, 4, 3, 3, 3}; +constexpr std::array NDaughters = { + 2, // DzeroToKPi + 3, // DstarToDzeroPi + 3, // DplusToPiKPi + 3, // DplusToPhiPiToKKPi + 3, // DsToPhiPiToKKPi + 3, // DsToK0starKToKKPi + 5, // Ds1ToDStarK0s + 5, // Ds2StarToDPlusK0s + 4, // D10ToDStarPi + 4, // D2Star0ToDPlusPi + 4, // B0ToDminusPi + 3, // BplusToD0Pi + 4, // BsToDsPi + 3, // LcToPKPi + 3, // LcToPiK0s + 3, // XiCplusToPKPi + 5, // XiCplusToXiPiPi + 4, // XiCzeroToXiPi + 4, // OmegaCToOmegaPi + 4 // OmegaCToXiPi +}; +constexpr std::array MaxDepthForSearch = { + 1, // DzeroToKPi + 2, // DstarToDzeroPi + 2, // DplusToPiKPi + 2, // DplusToPhiPiToKKPi + 2, // DsToPhiPiToKKPi + 2, // DsToK0starKToKKPi + 3, // Ds1ToDStarK0s + 3, // Ds2StarToDPlusK0s + 3, // D10ToDStarPi + 3, // D2Star0ToDPlusPi + 3, // B0ToDminusPi + 2, // BplusToD0Pi + 3, // BsToDsPi + 2, // LcToPKPi + 3, // LcToPiK0s + 2, // XiCplusToPKPi + 4, // XiCplusToXiPiPi + 3, // XiCzeroToXiPi + 3, // OmegaCToOmegaPi + 3 // OmegaCToXiPi +}; // keep coherent indexing with PDGArrayParticle // FIXME: look for a better solution -constexpr std::array, NChannels> ArrPdgFinal2Prong = {{{+kPiPlus, -kKPlus}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}}; -constexpr std::array, NChannels> ArrPdgFinal3Prong = {{{}, {+kPiPlus, -kKPlus, +kPiPlus}, {+kPiPlus, -kKPlus, +kPiPlus}, {+kKPlus, -kKPlus, +kPiPlus}, {+kKPlus, -kKPlus, +kPiPlus}, {+kKPlus, -kKPlus, +kPiPlus}, {}, {}, {}, {}, {}, {-kPiPlus, +kKPlus, +kPiPlus}, {}, {+kProton, -kKPlus, +kPiPlus}, {+kProton, -kPiPlus, +kPiPlus}, {+kProton, -kKPlus, +kPiPlus}, {}, {}, {}, {}}}; -constexpr std::array, NChannels> ArrPdgFinal4Prong = {{{}, {}, {}, {}, {}, {}, {}, {}, {+kPiPlus, -kKPlus, +kPiPlus, -kPiPlus}, {+kPiPlus, -kKPlus, +kPiPlus, -kPiPlus}, {-kPiPlus, +kKPlus, -kPiPlus, +kPiPlus}, {}, {-kKPlus, +kKPlus, -kPiPlus, +kPiPlus}, {}, {}, {}, {}, {+kPiPlus, -kPiPlus, -kPiPlus, +kProton}, {+kPiPlus, -kKPlus, -kPiPlus, +kProton}, {+kPiPlus, -kPiPlus, -kPiPlus, +kProton}}}; -constexpr std::array, NChannels> ArrPdgFinal5Prong = {{{}, {}, {}, {}, {}, {}, {+kPiPlus, -kKPlus, +kPiPlus, +kPiPlus, -kPiPlus}, {+kPiPlus, -kKPlus, +kPiPlus, +kPiPlus, -kPiPlus}, {}, {}, {}, {}, {}, {}, {}, {}, {+kPiPlus, +kPiPlus, -kPiPlus, -kPiPlus, +kProton}, {}, {}, {}}}; +constexpr auto ArrPdgFinal2Prong = []() { + std::array, NChannels> arr{}; + arr[DzeroToKPi] = {+kPiPlus, -kKPlus}; + return arr; +}(); + +constexpr auto ArrPdgFinal3Prong = []() { + std::array, NChannels> arr{}; + arr[DstarToDzeroPi] = {+kPiPlus, -kKPlus, +kPiPlus}; + arr[DplusToPiKPi] = {+kPiPlus, -kKPlus, +kPiPlus}; + arr[DplusToPhiPiToKKPi] = {+kKPlus, -kKPlus, +kPiPlus}; + arr[DsToPhiPiToKKPi] = {+kKPlus, -kKPlus, +kPiPlus}; + arr[DsToK0starKToKKPi] = {+kKPlus, -kKPlus, +kPiPlus}; + arr[BplusToD0Pi] = {-kPiPlus, +kKPlus, +kPiPlus}; + arr[LcToPKPi] = {+kProton, -kKPlus, +kPiPlus}; + arr[LcToPiK0s] = {+kProton, -kPiPlus, +kPiPlus}; + arr[XiCplusToPKPi] = {+kProton, -kKPlus, +kPiPlus}; + return arr; +}(); + +constexpr auto ArrPdgFinal4Prong = []() { + std::array, NChannels> arr{}; + arr[D10ToDStarPi] = {+kPiPlus, -kKPlus, +kPiPlus, -kPiPlus}; + arr[D2Star0ToDPlusPi] = {+kPiPlus, -kKPlus, +kPiPlus, -kPiPlus}; + arr[B0ToDminusPi] = {-kPiPlus, +kKPlus, -kPiPlus, +kPiPlus}; + arr[BsToDsPi] = {-kKPlus, +kKPlus, -kPiPlus, +kPiPlus}; + arr[XiCzeroToXiPi] = {+kPiPlus, -kPiPlus, -kPiPlus, +kProton}; + arr[OmegaCToOmegaPi] = {+kPiPlus, -kKPlus, -kPiPlus, +kProton}; + arr[OmegaCToXiPi] = {+kPiPlus, -kPiPlus, -kPiPlus, +kProton}; + return arr; +}(); + +constexpr auto ArrPdgFinal5Prong = []() { + std::array, NChannels> arr{}; + arr[Ds1ToDStarK0s] = {+kPiPlus, -kKPlus, +kPiPlus, +kPiPlus, -kPiPlus}; + arr[Ds2StarToDPlusK0s] = {+kPiPlus, -kKPlus, +kPiPlus, +kPiPlus, -kPiPlus}; + arr[XiCplusToXiPiPi] = {+kPiPlus, +kPiPlus, -kPiPlus, -kPiPlus, +kProton}; + return arr; +}(); + constexpr std::string_view Labels[NChannels] = {"D^{0} #rightarrow K#pi", "D*^{+} #rightarrow D^{0}#pi", "D^{+} #rightarrow K#pi#pi", "D^{+} #rightarrow KK#pi", "D_{s}^{+} #rightarrow #Phi#pi #rightarrow KK#pi", "D_{s}^{+} #rightarrow #bar{K}^{*0}K #rightarrow KK#pi", "D_{s}1 #rightarrow D*^{+}K^{0}_{s}", "D_{s}2* #rightarrow D^{+}K^{0}_{s}", "D1^{0} #rightarrow D*^{+}#pi", "D2^{*} #rightarrow D^{+}#pi", @@ -142,9 +221,9 @@ constexpr std::string_view OriginNames[NOriginTypes] = {"Prompt", "NonPrompt"}; struct HfTaskMcValidationGen { using BCsInfo = soa::Join; - using CollisionsNoCents = soa::Join; - using CollisionsFT0Cs = soa::Join; - using CollisionsFT0Ms = soa::Join; + using CollisionsNoCents = soa::Join; + using CollisionsFT0Cs = soa::Join; + using CollisionsFT0Ms = soa::Join; using McCollisionsCentFT0Ms = soa::Join; PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; PresliceUnsorted colPerMcCollisionFT0C = aod::mccollisionlabel::mcCollisionId; @@ -279,7 +358,9 @@ struct HfTaskMcValidationGen { } template - void runCheckGenParticles(GenColl const& mcCollision, Particles const& mcParticles, RecoColls const& recoCollisions, BCsInfo const&, std::array& counterPrompt, std::array& counterNonPrompt) + void runCheckGenParticles( + GenColl const& mcCollision, Particles const& mcParticles, RecoColls const& recoCollisions, + BCsInfo const&, std::array& counterPrompt, std::array& counterNonPrompt, std::array& counterBeauty) { if (eventGeneratorType >= 0 && mcCollision.getSubGeneratorId() != eventGeneratorType) { return; @@ -478,6 +559,8 @@ struct HfTaskMcValidationGen { } else if (origin == RecoDecay::OriginType::NonPrompt) { counterNonPrompt[iD]++; } + } else { // Beauty hadrons + counterBeauty[iD]++; // Matched already, no need to check origin for beauty hadrons } auto daughter0 = particle.template daughters_as().begin(); @@ -553,8 +636,8 @@ struct HfTaskMcValidationGen { for (const auto& mcCollision : mcCollisions) { const auto recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollision, mcCollision.globalIndex()); const auto mcParticlesPerMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, mcCollision.globalIndex()); - std::array counterPrompt{0}, counterNonPrompt{0}; - runCheckGenParticles(mcCollision, mcParticlesPerMcColl, recoCollsPerMcColl, bcInfo, counterPrompt, counterNonPrompt); + std::array counterPrompt{0}, counterNonPrompt{0}, counterBeauty{0}; + runCheckGenParticles(mcCollision, mcParticlesPerMcColl, recoCollsPerMcColl, bcInfo, counterPrompt, counterNonPrompt, counterBeauty); static_for<0, NCharmMesonChannels - 1>([&](auto i) { // Charm mesons constexpr int Index = i.value; registry.fill(HIST("PromptCharmMesons/hCountPrompt") + HIST(ParticleNames[Index]), counterPrompt[Index]); @@ -562,7 +645,7 @@ struct HfTaskMcValidationGen { }); static_for([&](auto i) { // Beauty hadrons constexpr int Index = i.value; - registry.fill(HIST("Beauty/hCount") + HIST(ParticleNames[Index]), counterPrompt[Index]); + registry.fill(HIST("Beauty/hCount") + HIST(ParticleNames[Index]), counterBeauty[Index]); }); static_for([&](auto i) { // Charm baryons constexpr int Index = i.value; @@ -581,8 +664,8 @@ struct HfTaskMcValidationGen { for (const auto& mcCollision : mcCollisions) { const auto recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollisionFT0C, mcCollision.globalIndex()); const auto mcParticlesPerMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, mcCollision.globalIndex()); - std::array counterPrompt{0}, counterNonPrompt{0}; - runCheckGenParticles(mcCollision, mcParticlesPerMcColl, recoCollsPerMcColl, bcInfo, counterPrompt, counterNonPrompt); + std::array counterPrompt{0}, counterNonPrompt{0}, counterBeauty{0}; + runCheckGenParticles(mcCollision, mcParticlesPerMcColl, recoCollsPerMcColl, bcInfo, counterPrompt, counterNonPrompt, counterBeauty); static_for<0, NCharmMesonChannels - 1>([&](auto i) { // Charm mesons constexpr int Index = i.value; registry.fill(HIST("PromptCharmMesons/hCountPrompt") + HIST(ParticleNames[Index]), counterPrompt[Index]); @@ -590,7 +673,7 @@ struct HfTaskMcValidationGen { }); static_for([&](auto i) { // Beauty constexpr int Index = i.value; - registry.fill(HIST("Beauty/hCount") + HIST(ParticleNames[Index]), counterPrompt[Index]); + registry.fill(HIST("Beauty/hCount") + HIST(ParticleNames[Index]), counterBeauty[Index]); }); static_for([&](auto i) { // Charm baryons constexpr int Index = i.value; @@ -609,8 +692,8 @@ struct HfTaskMcValidationGen { for (const auto& mcCollision : mcCollisions) { const auto recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollisionFT0M, mcCollision.globalIndex()); const auto mcParticlesPerMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, mcCollision.globalIndex()); - std::array counterPrompt{0}, counterNonPrompt{0}; - runCheckGenParticles(mcCollision, mcParticlesPerMcColl, recoCollsPerMcColl, bcInfo, counterPrompt, counterNonPrompt); + std::array counterPrompt{0}, counterNonPrompt{0}, counterBeauty{0}; + runCheckGenParticles(mcCollision, mcParticlesPerMcColl, recoCollsPerMcColl, bcInfo, counterPrompt, counterNonPrompt, counterBeauty); static_for<0, NCharmMesonChannels - 1>([&](auto i) { // Charm mesons constexpr int Index = i.value; registry.fill(HIST("PromptCharmMesons/hCountPrompt") + HIST(ParticleNames[Index]), counterPrompt[Index]); @@ -618,7 +701,7 @@ struct HfTaskMcValidationGen { }); static_for([&](auto i) { // Beauty mesons constexpr int Index = i.value; - registry.fill(HIST("Beauty/hCount") + HIST(ParticleNames[Index]), counterPrompt[Index]); + registry.fill(HIST("Beauty/hCount") + HIST(ParticleNames[Index]), counterBeauty[Index]); }); static_for([&](auto i) { // Charm baryons constexpr int Index = i.value; @@ -639,6 +722,7 @@ struct HfTaskMcValidationRec { Preslice perCol = aod::track::collisionId; Configurable eventGeneratorType{"eventGeneratorType", -1, "If positive, enable event selection using subGeneratorId information. The value indicates which events to keep (0 = MB, 4 = charm triggered, 5 = beauty triggered)"}; + Configurable searchUpToQuark{"searchUpToQuark", true, "Search up to quark level when determining the origin of a particle (prompt vs non-prompt)"}; Configurable storeOccupancy{"storeOccupancy", false, "Store collision occupancy for dedicated studies"}; std::array, NChannels> histDeltaPt, histDeltaPx, histDeltaPy, histDeltaPz, histDeltaSecondaryVertexX, histDeltaSecondaryVertexY, histDeltaSecondaryVertexZ, histDeltaDecayLength; @@ -652,9 +736,9 @@ struct HfTaskMcValidationRec { using HfCand2ProngWithMCRec = soa::Join; using HfCand3ProngWithMCRec = soa::Join; using CandMcGen = soa::Join; - using CollisionsWithMCLabels = soa::Join; - using CollisionsWithMCLabelsAndCentFT0C = soa::Join; - using CollisionsWithMCLabelsAndCentFT0M = soa::Join; + using CollisionsWithMCLabels = soa::Join; + using CollisionsWithMCLabelsAndCentFT0C = soa::Join; + using CollisionsWithMCLabelsAndCentFT0M = soa::Join; using TracksWithSel = soa::Join; Partition tracksFilteredGlobalTrackWoDCA = requireGlobalTrackWoDCAInFilter(); @@ -921,7 +1005,7 @@ struct HfTaskMcValidationRec { for (const auto& trackColl1 : tracksColl1) { if (trackColl1.has_mcParticle() && trackColl1.isPVContributor()) { auto particleColl1 = trackColl1.mcParticle(); - auto origin = RecoDecay::getCharmHadronOrigin(mcParticles, particleColl1, true); + auto origin = RecoDecay::getParticleOrigin(mcParticles, particleColl1, searchUpToQuark); if (origin == RecoDecay::NonPrompt) { nFromBeautyColl1++; } @@ -931,7 +1015,7 @@ struct HfTaskMcValidationRec { for (const auto& trackColl2 : tracksColl2) { if (trackColl2.has_mcParticle() && trackColl2.isPVContributor()) { auto particleColl2 = trackColl2.mcParticle(); - auto origin = RecoDecay::getCharmHadronOrigin(mcParticles, particleColl2, true); + auto origin = RecoDecay::getParticleOrigin(mcParticles, particleColl2, searchUpToQuark); if (origin == RecoDecay::NonPrompt) { nFromBeautyColl2++; } @@ -953,14 +1037,14 @@ struct HfTaskMcValidationRec { nITSlayers++; } } - uint const index = uint(track.collisionId() >= 0); + uint const index = static_cast(track.collisionId() >= 0); if (track.has_mcParticle()) { const auto& particle = track.mcParticle(); // get corresponding MC particle to check origin const auto& mcCollision = particle.mcCollision_as(); if (eventGeneratorType >= 0 && mcCollision.getSubGeneratorId() != eventGeneratorType) { continue; } - auto origin = RecoDecay::getCharmHadronOrigin(mcParticles, particle, true); + auto origin = RecoDecay::getParticleOrigin(mcParticles, particle, searchUpToQuark); histTracks->Fill(origin, track.pt()); bool const isAmbiguous = (track.compatibleCollIds().size() != 1); if (isAmbiguous) { @@ -1071,8 +1155,8 @@ struct HfTaskMcValidationRec { aod::McCollisions const&, aod::BCsWithTimestamps const&, Coll const& collisions, - Preslice cand2ProngsPerCollision, - Preslice cand3ProngsPerCollision) + const Preslice& cand2ProngsPerCollision, + const Preslice& cand3ProngsPerCollision) { // loop over collisions for (const auto& collision : collisions) { diff --git a/PWGHF/Tasks/taskPidStudies.cxx b/PWGHF/Tasks/taskPidStudies.cxx index 3c9bd0de808..b83bb3dd569 100644 --- a/PWGHF/Tasks/taskPidStudies.cxx +++ b/PWGHF/Tasks/taskPidStudies.cxx @@ -25,6 +25,7 @@ #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -203,8 +204,8 @@ struct HfTaskPidStudies { using PidTracks = soa::Join; - using CollSels = soa::Join; - using CollisionsMc = soa::Join; + using CollSels = soa::Join; + using CollisionsMc = soa::Join; using V0sMcRec = soa::Join; using CascsMcRec = soa::Join; diff --git a/PWGHF/Utils/utilsDerivedData.h b/PWGHF/Utils/utilsDerivedData.h index 8b2b4fd431a..f2b63291665 100644 --- a/PWGHF/Utils/utilsDerivedData.h +++ b/PWGHF/Utils/utilsDerivedData.h @@ -230,16 +230,18 @@ struct HfProducesDerivedData : o2::framework::ProducesGroup { const TMass massParticle) { // Fill MC collision properties - const auto sizeTableMcColl = mcCollisions.size(); - reserveTablesMcColl(sizeTableMcColl); + // const auto sizeTableMcColl = mcCollisions.size(); + // reserveTablesMcColl(sizeTableMcColl); + const auto sizeTablePart = mcParticles.size(); + reserveTablesParticles(sizeTablePart); for (const auto& mcCollision : mcCollisions) { const auto thisMcCollId = mcCollision.globalIndex(); const auto particlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, thisMcCollId); - const auto sizeTablePart = particlesThisMcColl.size(); - LOGF(debug, "MC collision %d has %d MC particles", thisMcCollId, sizeTablePart); + const auto sizeTablePartThisColl = particlesThisMcColl.size(); + LOGF(debug, "MC collision %d has %d MC particles", thisMcCollId, sizeTablePartThisColl); // Skip MC collisions without HF particles (and without HF candidates in matched reconstructed collisions if saving indices of reconstructed collisions matched to MC collisions) LOGF(debug, "MC collision %d has %d saved derived rec. collisions", thisMcCollId, matchedCollisions[thisMcCollId].size()); - if (sizeTablePart == 0 && (!conf->fillMcRCollId.value || matchedCollisions[thisMcCollId].empty())) { + if (sizeTablePartThisColl == 0 && (!conf->fillMcRCollId.value || matchedCollisions[thisMcCollId].empty())) { LOGF(debug, "Skipping MC collision %d", thisMcCollId); continue; } @@ -247,7 +249,6 @@ struct HfProducesDerivedData : o2::framework::ProducesGroup { fillTablesMcCollision(mcCollision); // Fill MC particle properties - reserveTablesParticles(sizeTablePart); for (const auto& particle : particlesThisMcColl) { fillTablesParticle(particle, massParticle); } diff --git a/PWGHF/Utils/utilsEvSelHf.h b/PWGHF/Utils/utilsEvSelHf.h index b3be162359d..7eb9d311187 100644 --- a/PWGHF/Utils/utilsEvSelHf.h +++ b/PWGHF/Utils/utilsEvSelHf.h @@ -109,11 +109,13 @@ enum EventRejection { IsGoodZvtxFT0vsPV, NoSameBunchPileup, Occupancy, + Inel0, NContrib, NoCollInTimeRangeNarrow, NoCollInTimeRangeStandard, NoCollInRofStandard, UpcEventCut, + UpcZdcNeutronClass, Chi2, PositionZ, NEventRejection @@ -121,9 +123,33 @@ enum EventRejection { using HfCollisionRejectionMask = uint32_t; // 32 bits, in case new ev. selections will be added +enum UpcZdcNeutronClassSelection { + kXn0n = 0, + k0nXn, + kInvalid +}; + const o2::framework::AxisSpec axisEvents = {EventRejection::NEventRejection, -0.5f, +EventRejection::NEventRejection - 0.5f, ""}; const o2::framework::AxisSpec axisUpcEvents = {o2::aod::sgselector::DoubleGap + 1, -0.5f, +o2::aod::sgselector::DoubleGap + 0.5f, ""}; +template +int getUpcZdcNeutronClass(TBc const& bc, const float zdcTimeMin, const float zdcTimeMax) +{ + if (!bc.has_zdc()) { + return UpcZdcNeutronClassSelection::kInvalid; + } + const auto zdc = bc.zdc(); + const bool isZNAinTime = zdc.timeZNA() >= zdcTimeMin && zdc.timeZNA() <= zdcTimeMax; + const bool isZNCinTime = zdc.timeZNC() >= zdcTimeMin && zdc.timeZNC() <= zdcTimeMax; + if (isZNAinTime && !isZNCinTime) { + return UpcZdcNeutronClassSelection::kXn0n; + } + if (!isZNAinTime && isZNCinTime) { + return UpcZdcNeutronClassSelection::k0nXn; + } + return UpcZdcNeutronClassSelection::kInvalid; +} + /// \brief Function to put labels on monitoring histogram /// \param hRejection monitoring histogram /// \param softwareTriggerLabel bin label for software trigger rejection @@ -140,9 +166,11 @@ void setEventRejectionLabels(Histo& hRejection, std::string const& softwareTrigg hRejection->GetXaxis()->SetBinLabel(EventRejection::TimeFrameBorderCut + 1, "TF border"); hRejection->GetXaxis()->SetBinLabel(EventRejection::ItsRofBorderCut + 1, "ITS ROF border"); hRejection->GetXaxis()->SetBinLabel(EventRejection::UpcEventCut + 1, "UPC event"); + hRejection->GetXaxis()->SetBinLabel(EventRejection::UpcZdcNeutronClass + 1, "UPC ZDC 0nXn/Xn0n"); hRejection->GetXaxis()->SetBinLabel(EventRejection::IsGoodZvtxFT0vsPV + 1, "PV #it{z} consistency FT0 timing"); hRejection->GetXaxis()->SetBinLabel(EventRejection::NoSameBunchPileup + 1, "No same-bunch pile-up"); // POTENTIALLY BAD FOR BEAUTY ANALYSES hRejection->GetXaxis()->SetBinLabel(EventRejection::Occupancy + 1, "Occupancy"); + hRejection->GetXaxis()->SetBinLabel(EventRejection::Inel0 + 1, "INEL > 0"); hRejection->GetXaxis()->SetBinLabel(EventRejection::NContrib + 1, "# of PV contributors"); hRejection->GetXaxis()->SetBinLabel(EventRejection::Chi2 + 1, "PV #it{#chi}^{2}"); hRejection->GetXaxis()->SetBinLabel(EventRejection::PositionZ + 1, "PV #it{z}"); @@ -167,7 +195,9 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable occEstimator{"occEstimator", 1, "Occupancy estimation (1: ITS, 2: FT0C)"}; o2::framework::Configurable occupancyMin{"occupancyMin", 0, "Minimum occupancy"}; o2::framework::Configurable occupancyMax{"occupancyMax", 1000000, "Maximum occupancy"}; + o2::framework::Configurable requireINEL0{"requireINEL0", false, "Require INEL > 0"}; o2::framework::Configurable nPvContributorsMin{"nPvContributorsMin", 0, "Minimum number of PV contributors"}; + o2::framework::Configurable nPvContributorsMax{"nPvContributorsMax", 1000000, "Maximum number of PV contributors"}; o2::framework::Configurable chi2PvMax{"chi2PvMax", -1.f, "Maximum PV chi2"}; o2::framework::Configurable zPvPosMin{"zPvPosMin", -10.f, "Minimum PV posZ (cm)"}; o2::framework::Configurable zPvPosMax{"zPvPosMax", 10.f, "Maximum PV posZ (cm)"}; @@ -185,6 +215,9 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { o2::framework::Configurable rctCheckZDC{"rctCheckZDC", false, "RCT flag to check whether the ZDC is present or not"}; o2::framework::Configurable rctTreatLimitedAcceptanceAsBad{"rctTreatLimitedAcceptanceAsBad", false, "RCT flag to reject events with limited acceptance for selected detectors"}; o2::framework::Configurable irSource{"irSource", "", "Estimator of the interaction rate (Empty: automatically set. Otherwise recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + o2::framework::Configurable useUpcZdcTimeCut{"useUpcZdcTimeCut", false, "Apply ZDC time selection for UPC neutron class (only relevant for UPC event selection)"}; + o2::framework::Configurable upcZdcTimeMin{"upcZdcTimeMin", -2.f, "Minimum ZDC time for UPC neutron class selection (ns) (only relevant for UPC event selection)"}; + o2::framework::Configurable upcZdcTimeMax{"upcZdcTimeMax", 2.f, "Maximum ZDC time for UPC neutron class selection (ns) (only relevant for UPC event selection)"}; // SG selector SGSelector sgSelector; @@ -372,8 +405,13 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { } } + /// INEL > 0 + if (requireINEL0 && !collision.isInelGt0()) { + SETBIT(rejectionMask, EventRejection::Inel0); + } + /// number of PV contributors - if (collision.numContrib() < nPvContributorsMin) { + if (collision.numContrib() < nPvContributorsMin || collision.numContrib() > nPvContributorsMax) { SETBIT(rejectionMask, EventRejection::NContrib); } @@ -435,6 +473,12 @@ struct HfEventSelection : o2::framework::ConfigurableGroup { if (upcEventType > o2::aod::sgselector::DoubleGap) { SETBIT(rejectionMaskWithUpc, EventRejection::UpcEventCut); } else { + if (useUpcZdcTimeCut) { + const auto upcZdcNeutronClass = getUpcZdcNeutronClass(*sgSelectionResult.bc, upcZdcTimeMin, upcZdcTimeMax); + if (upcZdcNeutronClass != UpcZdcNeutronClassSelection::kXn0n && upcZdcNeutronClass != UpcZdcNeutronClassSelection::k0nXn) { + SETBIT(rejectionMaskWithUpc, EventRejection::UpcZdcNeutronClass); + } + } hUpCollisions->Fill(upcEventType); } } diff --git a/PWGHF/Utils/utilsMcGen.h b/PWGHF/Utils/utilsMcGen.h index 18734c1e96a..87697009a3c 100644 --- a/PWGHF/Utils/utilsMcGen.h +++ b/PWGHF/Utils/utilsMcGen.h @@ -202,7 +202,7 @@ void fillMcMatchGen3Prong(TMcParticles const& mcParticles, if (std::abs(pdgMother) == Pdg::kDStar) { std::vector arrResoDaughIndexDStar = {}; RecoDecay::getDaughters(particle, &arrResoDaughIndexDStar, std::array{0}, DepthResoMax); - for (const int iDaug : arrResoDaughIndexDStar) { + for (const int iDaug : arrResoDaughIndexDStar) { // o2-linter: disable=const-ref-in-for-loop (not necessary for int type) auto daughDstar = mcParticles.rawIteratorAt(iDaug); if (std::abs(daughDstar.pdgCode()) == Pdg::kD0 || std::abs(daughDstar.pdgCode()) == Pdg::kDPlus) { RecoDecay::getDaughters(daughDstar, &arrResoDaughIndex, std::array{0}, DepthResoMax); @@ -301,6 +301,14 @@ void fillMcMatchGen3Prong(TMcParticles const& mcParticles, flagChannelMain = sign * DecayChannelMain::XicToPKPi; } } + + // cd± → de± K∓ π± + if (flagChannelMain == 0) { + if (RecoDecay::isMatchedMCGen(mcParticles, particle, Pdg::kCDeuteron, std::array{+Pdg::kDeuteron, -kKPlus, +kPiPlus}, true, &sign, 1)) { + flagChannelMain = sign * DecayChannelMain::CDeuteronToDeKPi; + flagChannelResonant = o2::hf_decay::getResonantDecayCDeuteron(particle); + } + } } // Check whether the particle is non-prompt (from a b quark). diff --git a/PWGHF/Utils/utilsMcMatching.h b/PWGHF/Utils/utilsMcMatching.h index 097c13a901e..d4989053dd7 100644 --- a/PWGHF/Utils/utilsMcMatching.h +++ b/PWGHF/Utils/utilsMcMatching.h @@ -154,6 +154,16 @@ static const std::unordered_map> {DecayChannelResonant::XicToPPhi, {+PDG_t::kProton, +o2::constants::physics::Pdg::kPhi}}, }; +// cd+ + +static const std::unordered_map> daughtersCDeuteronMain{ + {DecayChannelMain::CDeuteronToDeKPi, {+o2::constants::physics::Pdg::kDeuteron, +PDG_t::kKMinus, +PDG_t::kPiPlus}}}; + +/// resonances in c-deuteron decay are not stored in the particle stack for c-deuteron, but tagged with specific status codes +static constexpr std::array statusCodeCDeuteronToDeKstar0{-85, -95}; +static constexpr std::array statusCodeCDeuteronToNeDeltaplusK{-86, -96}; +static constexpr std::array statusCodeCDeuteronToNeL1520Pi{-87, -97}; + /// Returns a map of the possible final states for a specific 3-prong particle specie /// \param pdgMother PDG code of the mother particle /// \return a map of final states with their corresponding PDG codes @@ -170,6 +180,8 @@ inline std::unordered_map> getDecayChan return daughtersLcMain; case o2::constants::physics::Pdg::kXiCPlus: return daughtersXicMain; + case o2::constants::physics::Pdg::kCDeuteron: + return daughtersCDeuteronMain; default: LOG(fatal) << "Unknown PDG code for 3-prong final states: " << pdgMother; return {}; @@ -317,6 +329,27 @@ inline void flipPdgSign(const int pdgMother, const int pdgToFlip, std::array +inline int getResonantDecayCDeuteron(Part const& particle) +{ + auto statusCode = particle.getGenStatusCode(); + if (statusCode == o2::hf_decay::hf_cand_3prong::statusCodeCDeuteronToDeKstar0[0] || statusCode == o2::hf_decay::hf_cand_3prong::statusCodeCDeuteronToDeKstar0[1]) { + return o2::hf_decay::hf_cand_3prong::DecayChannelResonant::CDeuteronToDeKstar0; + } + if (statusCode == o2::hf_decay::hf_cand_3prong::statusCodeCDeuteronToNeDeltaplusK[0] || statusCode == o2::hf_decay::hf_cand_3prong::statusCodeCDeuteronToNeDeltaplusK[1]) { + return o2::hf_decay::hf_cand_3prong::DecayChannelResonant::CDeuteronToNeDeltaplusK; + } + if (statusCode == o2::hf_decay::hf_cand_3prong::statusCodeCDeuteronToNeL1520Pi[0] || statusCode == o2::hf_decay::hf_cand_3prong::statusCodeCDeuteronToNeL1520Pi[1]) { + return o2::hf_decay::hf_cand_3prong::DecayChannelResonant::CDeuteronToNeL1520Pi; + } + return 0; +} } // namespace o2::hf_decay #endif // PWGHF_UTILS_UTILSMCMATCHING_H_ diff --git a/PWGJE/Core/CMakeLists.txt b/PWGJE/Core/CMakeLists.txt index b6ccafb2be2..f1adfc59b7a 100644 --- a/PWGJE/Core/CMakeLists.txt +++ b/PWGJE/Core/CMakeLists.txt @@ -23,8 +23,45 @@ o2physics_target_root_dictionary(PWGJECore FastJetUtilities.h JetTaggingUtilities.h JetBkgSubUtils.h - JetDerivedDataUtilities.h emcalCrossTalkEmulation.h utilsTrackMatchingEMC.h LINKDEF PWGJECoreLinkDef.h) endif() + +# Precompiled header shared across PWGJE. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# Jet.h reaches 90% of the group; the existing substructure carriers already +# precompile it, so this one serves everything they do not cover. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(JetDataModelPCH OBJECT jetDataModelPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetDataModelPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(JetDataModelPCH PUBLIC O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(JetDataModelPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGJE/DataModel/Jet.h"]] # 90% + ) +endif() diff --git a/PWGJE/Core/JetBkgSubUtils.cxx b/PWGJE/Core/JetBkgSubUtils.cxx index 91254ddf7a8..b384ae59d75 100644 --- a/PWGJE/Core/JetBkgSubUtils.cxx +++ b/PWGJE/Core/JetBkgSubUtils.cxx @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -41,7 +42,7 @@ JetBkgSubUtils::JetBkgSubUtils(float jetBkgR_out, float bkgEtaMin_out, float bkg constSubAlpha(constSubAlpha_out), constSubRMax(constSubRMax_out), nHardReject(nHardReject_out), - ghostAreaSpec(ghostAreaSpec_out) + ghostAreaSpec(std::move(ghostAreaSpec_out)) { } @@ -162,11 +163,11 @@ std::vector JetBkgSubUtils::doJetConstSub(std::vector #include +#include #include #include @@ -107,7 +108,7 @@ class JetBkgSubUtils constSubRMax = rmax_out; } void setDoRhoMassSub(bool doMSub_out = true) { doRhoMassSub = doMSub_out; } - void setGhostAreaSpec(fastjet::GhostedAreaSpec ghostAreaSpec_out) { ghostAreaSpec = ghostAreaSpec_out; } + void setGhostAreaSpec(fastjet::GhostedAreaSpec ghostAreaSpec_out) { ghostAreaSpec = std::move(ghostAreaSpec_out); } // Getters float getJetBkgR() const { return jetBkgR; } @@ -124,7 +125,7 @@ class JetBkgSubUtils fastjet::Selector getRhoSelector() const { return selRho; } // Calculate the jet mass - double getMd(fastjet::PseudoJet jet) const; + double getMd(const fastjet::PseudoJet& jet) const; protected: float jetBkgR = 0.2; diff --git a/PWGJE/Core/JetCandidateUtilities.h b/PWGJE/Core/JetCandidateUtilities.h index c9cde2b206d..de6b7faf8f9 100644 --- a/PWGJE/Core/JetCandidateUtilities.h +++ b/PWGJE/Core/JetCandidateUtilities.h @@ -188,15 +188,13 @@ template auto matchedParticle(const T& candidate, const U& tracks, const V& particles) { if constexpr (jethfutilities::isHFCandidate()) { - bool isMatched = false; - return jethfutilities::matchedHFParticle(candidate, tracks, particles, isMatched); + return jethfutilities::matchedHFParticle(candidate, tracks, particles); } else if constexpr (jetv0utilities::isV0Candidate()) { return jetv0utilities::matchedV0Particle(candidate, tracks, particles); } else if constexpr (jetdqutilities::isDielectronCandidate()) { return jetdqutilities::matchedDielectronParticle(candidate, tracks, particles); } else { - bool isMatched = false; - return jethfutilities::matchedHFParticle(candidate, tracks, particles, isMatched); // this is a dummy output which should never be triggered + return jethfutilities::matchedHFParticle(candidate, tracks, particles); // this is a dummy output which should never be triggered } } diff --git a/PWGJE/Core/JetDQUtilities.h b/PWGJE/Core/JetDQUtilities.h index 690e2cef0cb..5ce3e0e1d9f 100644 --- a/PWGJE/Core/JetDQUtilities.h +++ b/PWGJE/Core/JetDQUtilities.h @@ -72,6 +72,74 @@ constexpr bool isDielectronMcTable() return isDielectronMcCandidate() || isDielectronMcCandidate(); } +/** + * returns the PDG of the candidate based on Dielectron Table + * + * @param candidate dielectron candidate that is being checked + */ +template +int getDielectronCandidatePDG(T const& /*candidate*/) +{ + if constexpr (isDielectronCandidate() || isDielectronMcCandidate()) { + return static_cast(o2::constants::physics::Pdg::kJPsi); + } else { + return 0; + } +} + +/** + * returns the PDG of the candidates in the table type + */ +template +int getDielectronTablePDG() +{ + if constexpr (isDielectronTable() || isDielectronMcTable()) { + return static_cast(o2::constants::physics::Pdg::kJPsi); + } else { + return 0; + } +} + +/** + * returns the mass of the candidate based on Dielectron Table + * + * @param candidate dielectron candidate that is being checked + */ +template +float getDielectronCandidatePDGMass(T const& /*candidate*/) +{ + if constexpr (isDielectronCandidate() || isDielectronMcCandidate()) { + return static_cast(o2::constants::physics::MassJPsi); + } else { + return -1.0; + } +} + +/** + * returns the mass of the candidates in the table type + * + */ +template +float getDielectronTablePDGMass() +{ + if constexpr (isDielectronTable() || isDielectronMcTable()) { + return static_cast(o2::constants::physics::MassJPsi); + } else { + return -1.0; + } +} + +/** + * returns the mass of the candidate based on Dielectron Table + * + * @param candidate dielectron candidate that is being checked + */ +template +float getDielectronCandidateInvariantMass(T const& candidate) +{ + return candidate.mass(); +} + /** * returns true if the candidate is matched to a reconstructed level candidate with the correct decay * * @param candidate candidate that is being checked @@ -117,10 +185,9 @@ bool isDielectronDaughterTrack(T& track, U& candidate) * @param particles particle table */ template -auto matchedDielectronParticleId(const T& candidate, const U& /*tracks*/, const V& /*particles*/) +auto matchedDielectronParticleId(const T& candidate, const U& /*tracks*/, const V& particles) { - const auto candidateDaughterParticle = candidate.template prong1_as().template mcParticle_as(); - return candidateDaughterParticle.template mothers_first_as().globalIndex(); // can we get the Id directly? + return RecoDecay::getMother(particles, candidate.template prong0_as().template mcParticle_as(), getDielectronCandidatePDG(candidate), true); } /** @@ -131,10 +198,9 @@ auto matchedDielectronParticleId(const T& candidate, const U& /*tracks*/, const * @param particles particle table */ template -auto matchedDielectronParticle(const T& candidate, const U& /*tracks*/, const V& /*particles*/) +auto matchedDielectronParticle(const T& candidate, const U& /*tracks*/, const V& particles) { - const auto candidateDaughterParticle = candidate.template prong1_as().template mcParticle_as(); - return candidateDaughterParticle.template mothers_first_as(); + return particles.iteratorAt(RecoDecay::getMother(particles, candidate.template prong0_as().template mcParticle_as(), getDielectronCandidatePDG(candidate), true)); } /** @@ -195,74 +261,6 @@ int getDielectronMcCandidateCollisionId(T const& candidate) } } -/** - * returns the PDG of the candidate based on Dielectron Table - * - * @param candidate dielectron candidate that is being checked - */ -template -int getDielectronCandidatePDG(T const& /*candidate*/) -{ - if constexpr (isDielectronCandidate() || isDielectronMcCandidate()) { - return static_cast(o2::constants::physics::Pdg::kJPsi); - } else { - return 0; - } -} - -/** - * returns the PDG of the candidates in the table type - */ -template -int getDielectronTablePDG() -{ - if constexpr (isDielectronTable() || isDielectronMcTable()) { - return static_cast(o2::constants::physics::Pdg::kJPsi); - } else { - return 0; - } -} - -/** - * returns the mass of the candidate based on Dielectron Table - * - * @param candidate dielectron candidate that is being checked - */ -template -float getDielectronCandidatePDGMass(T const& /*candidate*/) -{ - if constexpr (isDielectronCandidate() || isDielectronMcCandidate()) { - return static_cast(o2::constants::physics::MassJPsi); - } else { - return -1.0; - } -} - -/** - * returns the mass of the candidates in the table type - * - */ -template -float getDielectronTablePDGMass() -{ - if constexpr (isDielectronTable() || isDielectronMcTable()) { - return static_cast(o2::constants::physics::MassJPsi); - } else { - return -1.0; - } -} - -/** - * returns the mass of the candidate based on Dielectron Table - * - * @param candidate dielectron candidate that is being checked - */ -template -float getDielectronCandidateInvariantMass(T const& candidate) -{ - return candidate.mass(); -} - template bool isDielectronParticle(T const& particles, U const& particle) { diff --git a/PWGJE/Core/JetDerivedDataUtilities.h b/PWGJE/Core/JetDerivedDataUtilities.h index 113e240bc2b..45f0a55d214 100644 --- a/PWGJE/Core/JetDerivedDataUtilities.h +++ b/PWGJE/Core/JetDerivedDataUtilities.h @@ -54,6 +54,7 @@ enum JCollisionSel { selUPCSingleGapA = 10, selUPCSingleGapC = 11, selUPCDoubleGap = 12, + selTriggerTrackSignal = 13 // special bit for derived data. not used like other selection bits }; enum JCollisionSubGeneratorId { @@ -62,7 +63,7 @@ enum JCollisionSubGeneratorId { }; template -bool selectCollision(T const& collision, const std::vector& eventSelectionMaskBits, bool skipMBGapEvents = true, bool rctSelection = true, std::string rctLabel = "CBT_hadronPID", bool rejectLimitedAcceptanceRct = false, bool requireZDCRct = false) +bool selectCollision(T const& collision, const std::vector& eventSelectionMaskBits, bool skipMBGapEvents = true, bool rctSelection = true, const std::string& rctLabel = "CBT_hadronPID", bool rejectLimitedAcceptanceRct = false, bool requireZDCRct = false) { if (skipMBGapEvents && collision.getSubGeneratorId() == JCollisionSubGeneratorId::mbGap) { @@ -95,7 +96,7 @@ bool selectCollision(T const& collision, const std::vector& eventSelectionM return !isOrCondition; } -bool eventSelectionMasksContainSelection(const std::string& eventSelectionMasks, std::string selection) +bool eventSelectionMasksContainSelection(const std::string& eventSelectionMasks, const std::string& selection) { size_t position = 0; while ((position = eventSelectionMasks.find(selection, position)) != std::string::npos) { @@ -324,7 +325,7 @@ bool selectTrigger(T const& collision, int triggerMaskBit) return collision.triggerSel() & (1ULL << triggerMaskBit); } -bool triggerMasksContainTrigger(const std::string& triggerMasks, std::string trigger) +bool triggerMasksContainTrigger(const std::string& triggerMasks, const std::string& trigger) { size_t position = 0; while ((position = triggerMasks.find(trigger, position)) != std::string::npos) { @@ -652,12 +653,12 @@ bool applyTrackKinematics(T const& track, float pTMin = 0.15, float pTMax = 100. } template -bool selectTrack(T const& track, int trackSelection, bool isEmbedded = false) +bool selectTrack(T const& track, int trackSelection, bool rejectNonEmbedded = false) { if (!(track.trackSel() & (1ULL << JTrackSel::notBadMcTrack))) { return false; } - if (isEmbedded && !(track.trackSel() & (1ULL << JTrackSel::embeddedTrack))) { // will get rid of non embedded tracks + if (rejectNonEmbedded && !(track.trackSel() & (1ULL << JTrackSel::embeddedTrack))) { // will get rid of non embedded tracks return false; } if (trackSelection == -1) { @@ -716,6 +717,7 @@ uint8_t setSingleTrackSelectionBit(int trackSelection) if (trackSelection != -1) { SETBIT(bit, trackSelection); } + SETBIT(bit, JTrackSel::notBadMcTrack); return bit; } @@ -731,7 +733,7 @@ bool selectTrackDcaZ(T const& track, double dcaZmax = 99.) return std::abs(track.dcaZ()) < dcaZmax; } -std::vector initialiseClusterDefinitions(const std::string clusterDefinitions) +std::vector initialiseClusterDefinitions(const std::string& clusterDefinitions) { std::vector clusterDefinitionsVec; if (clusterDefinitions.empty()) { diff --git a/PWGJE/Core/JetFinder.cxx b/PWGJE/Core/JetFinder.cxx index c3c2d091908..075c92bbbfe 100644 --- a/PWGJE/Core/JetFinder.cxx +++ b/PWGJE/Core/JetFinder.cxx @@ -48,6 +48,11 @@ void JetFinder::setParams() jetDef.set_jet_algorithm(algorithm); areaDef = fastjet::AreaDefinition(areaType, ghostAreaSpec); selJets = fastjet::SelectorPtRange(jetPtMin, jetPtMax) && fastjet::SelectorEtaRange(jetEtaMin, jetEtaMax) && fastjet::SelectorPhiRange(jetPhiMin, jetPhiMax); + + if (phiExclusionMin > -998.0) { + // automatically jets around exclusion region + selJets = selJets && (!fastjet::SelectorPhiRange(phiExclusionMin - jetR, phiExclusionMax + jetR)); + } } /// Performs jet finding @@ -55,7 +60,7 @@ void JetFinder::setParams() /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents -fastjet::ClusterSequenceArea JetFinder::findJets(std::vector& inputParticles, std::vector& jets) // ideally find a way of passing the cluster sequence as a reeference +fastjet::ClusterSequenceArea JetFinder::findJets(std::vector const& inputParticles, std::vector& jets) // ideally find a way of passing the cluster sequence as a reference { setParams(); jets.clear(); diff --git a/PWGJE/Core/JetFinder.h b/PWGJE/Core/JetFinder.h index 8f727bbdbbd..7f281d3de03 100644 --- a/PWGJE/Core/JetFinder.h +++ b/PWGJE/Core/JetFinder.h @@ -46,10 +46,12 @@ class JetFinder /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents - // fastjet::ClusterSequenceArea findJets(std::vector &inputParticles, std::vector &jets); + // fastjet::ClusterSequenceArea findJets(std::vector const& inputParticles, std::vector& jets); float phiMin = -1. * M_PI; float phiMax = 2. * M_PI; + float phiExclusionMin = -999.; + float phiExclusionMax = 999.; float etaMin = -.9; float etaMax = .9; @@ -90,7 +92,7 @@ class JetFinder /// \param inputParticles vector of input particles/tracks /// \param jets veector of jets to be filled /// \return ClusterSequenceArea object needed to access constituents - fastjet::ClusterSequenceArea findJets(std::vector& inputParticles, std::vector& jets); // ideally find a way of passing the cluster sequence as a reeference + fastjet::ClusterSequenceArea findJets(std::vector const& inputParticles, std::vector& jets); // ideally find a way of passing the cluster sequence as a reference private: ClassDefNV(JetFinder, 1); diff --git a/PWGJE/Core/JetFindingUtilities.h b/PWGJE/Core/JetFindingUtilities.h index 37ca4eddaa5..eeeae31e7c5 100644 --- a/PWGJE/Core/JetFindingUtilities.h +++ b/PWGJE/Core/JetFindingUtilities.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -90,10 +91,10 @@ constexpr bool isEMCALClusterTable() */ template -bool isTrackSelected(T const& track, int trackSelection, const U* candidate = nullptr) +bool isTrackSelected(T const& track, int trackSelection, bool rejectNonEmbedded = false, const U* candidate = nullptr) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection, rejectNonEmbedded)) { return false; } if (candidate != nullptr) { @@ -114,10 +115,10 @@ bool isTrackSelected(T const& track, int trackSelection, const U* candidate = nu */ template -void analyseTracks(std::vector& inputParticles, T const& tracks, int trackSelection, const U* candidate = nullptr) +void analyseTracks(std::vector& inputParticles, T const& tracks, int trackSelection, bool rejectNonEmbedded = false, const U* candidate = nullptr) { for (auto& track : tracks) { - if (isTrackSelected(track, trackSelection, candidate)) { + if (isTrackSelected(track, trackSelection, rejectNonEmbedded, candidate)) { fastjetutilities::fillTracks(track, inputParticles, track.globalIndex()); } } @@ -133,17 +134,22 @@ void analyseTracks(std::vector& inputParticles, T const& tra */ template -void analyseTracksMultipleCandidates(std::vector& inputParticles, T const& tracks, int trackSelection, U const& candidates) +void analyseTracksMultipleCandidates(std::vector& inputParticles, T const& tracks, int trackSelection, bool rejectNonEmbedded, U const& candidates) { for (auto& track : tracks) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + bool isSelected = true; + if (!jetderiveddatautilities::selectTrack(track, trackSelection, rejectNonEmbedded)) { continue; } for (auto& candidate : candidates) { if (jetcandidateutilities::isDaughterTrack(track, candidate)) { - continue; + isSelected = false; + break; } } + if (!isSelected) { + continue; + } fastjetutilities::fillTracks(track, inputParticles, track.globalIndex()); } } @@ -278,12 +284,11 @@ bool analyseV0s(std::vector& inputParticles, T const& v0s, f * @param doHFJetFinding set whether only jets containing a HF candidate are saved */ template -void findJets(JetFinder& jetFinder, std::vector& inputParticles, float jetPtMin, float jetPtMax, std::vector jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, std::shared_ptr thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) +void findJets(JetFinder& jetFinder, std::vector const& inputParticles, float jetPtMin, float jetPtMax, std::vector const& jetRadius, float jetAreaFractionMin, T const& collision, U& jetsTable, V& constituentsTable, const std::shared_ptr& thnSparseJet, bool fillThnSparse, bool doCandidateJetFinding = false) { - auto jetRValues = static_cast>(jetRadius); jetFinder.jetPtMin = jetPtMin; jetFinder.jetPtMax = jetPtMax; - for (auto R : jetRValues) { + for (auto R : jetRadius) { jetFinder.jetR = R; std::vector jets; fastjet::ClusterSequenceArea clusterSeq(jetFinder.findJets(inputParticles, jets)); @@ -375,19 +380,25 @@ void analyseParticles(std::vector& inputParticles, const std } } } + bool isSelected = true; if constexpr (jetv0utilities::isV0McTable()) { // note that for V0s the candidate table is given to this function, not a single candidate if (candidate != nullptr) { for (auto const& cand : (*candidate)) { if (cand.mcParticleId() == particle.globalIndex()) { - continue; + isSelected = false; + break; } auto v0Particle = cand.template mcParticle_as(); if (jetcandidateutilities::isDaughterParticle(v0Particle, particle.globalIndex())) { - continue; + isSelected = false; + break; } } } } + if (!isSelected) { + continue; + } fastjetutilities::fillTracks(particle, inputParticles, particle.globalIndex(), JetConstituentStatus::track, pdgParticle->Mass()); } } diff --git a/PWGJE/Core/JetHFUtilities.h b/PWGJE/Core/JetHFUtilities.h index a018c0c177b..2ffb20bf36a 100644 --- a/PWGJE/Core/JetHFUtilities.h +++ b/PWGJE/Core/JetHFUtilities.h @@ -21,6 +21,8 @@ #include "PWGHF/Core/DecayChannelsLegacy.h" #include "PWGJE/DataModel/Jet.h" +#include "Common/Core/RecoDecay.h" + #include #include @@ -431,6 +433,130 @@ constexpr bool isHFMcTable() } } +/** + * returns the PDG of the candidate based on HF Table + * + * @param candidate HF candidate that is being checked + */ +template +int getHFCandidatePDG(T const& /*candidate*/) +{ + if constexpr (isD0Candidate() || isD0McCandidate()) { + return static_cast(o2::constants::physics::Pdg::kD0); + } else if constexpr (isDplusCandidate() || isDplusMcCandidate()) { + return static_cast(o2::constants::physics::Pdg::kDPlus); + } else if constexpr (isDsCandidate() || isDsMcCandidate()) { + return static_cast(o2::constants::physics::Pdg::kDS); + } else if constexpr (isDstarCandidate() || isDstarMcCandidate()) { + return static_cast(o2::constants::physics::Pdg::kDStar); + } else if constexpr (isLcCandidate() || isLcMcCandidate()) { + return static_cast(o2::constants::physics::Pdg::kLambdaCPlus); + } else if constexpr (isB0Candidate() || isB0McCandidate()) { + return static_cast(o2::constants::physics::Pdg::kB0); + } else if constexpr (isBplusCandidate() || isBplusMcCandidate()) { + return static_cast(o2::constants::physics::Pdg::kBPlus); + } else if constexpr (isXicToXiPiPiCandidate() || isXicToXiPiPiMcCandidate()) { + return static_cast(o2::constants::physics::Pdg::kXiCPlus); + } else { + return 0; + } +} + +/** + * returns the PDG of the candidates in the table type + */ +template +int getHFTablePDG() +{ + if constexpr (isD0Table() || isD0McTable()) { + return static_cast(o2::constants::physics::Pdg::kD0); + } else if constexpr (isDplusTable() || isDplusMcTable()) { + return static_cast(o2::constants::physics::Pdg::kDPlus); + } else if constexpr (isDsTable() || isDsMcTable()) { + return static_cast(o2::constants::physics::Pdg::kDS); + } else if constexpr (isDstarTable() || isDstarMcTable()) { + return static_cast(o2::constants::physics::Pdg::kDStar); + } else if constexpr (isLcTable() || isLcMcTable()) { + return static_cast(o2::constants::physics::Pdg::kLambdaCPlus); + } else if constexpr (isB0Table() || isB0McTable()) { + return static_cast(o2::constants::physics::Pdg::kB0); + } else if constexpr (isBplusTable() || isBplusMcTable()) { + return static_cast(o2::constants::physics::Pdg::kBPlus); + } else if constexpr (isXicToXiPiPiTable() || isXicToXiPiPiMcTable()) { + return static_cast(o2::constants::physics::Pdg::kXiCPlus); + } else { + return 0; + } +} + +/** + * returns the mass of the candidate based on HF Table + * + * @param candidate HF candidate that is being checked + */ +template +float getHFCandidatePDGMass(T const& /*candidate*/) +{ + if constexpr (isD0Candidate() || isD0McCandidate()) { + return static_cast(o2::constants::physics::MassD0); + } else if constexpr (isDplusCandidate() || isDplusMcCandidate()) { + return static_cast(o2::constants::physics::MassDPlus); + } else if constexpr (isDsCandidate() || isDsMcCandidate()) { + return static_cast(o2::constants::physics::MassDS); + } else if constexpr (isDstarCandidate() || isDstarMcCandidate()) { + return static_cast(o2::constants::physics::MassDStar); + } else if constexpr (isLcCandidate() || isLcMcCandidate()) { + return static_cast(o2::constants::physics::MassLambdaCPlus); + } else if constexpr (isB0Candidate() || isB0McCandidate()) { + return static_cast(o2::constants::physics::MassB0); + } else if constexpr (isBplusCandidate() || isBplusMcCandidate()) { + return static_cast(o2::constants::physics::MassBPlus); + } else if constexpr (isXicToXiPiPiCandidate() || isXicToXiPiPiMcCandidate()) { + return static_cast(o2::constants::physics::MassXiCPlus); + } else { + return -1.0; + } +} + +/** + * returns the mass of the candidates in the table type + * + */ +template +float getHFTablePDGMass() +{ + if constexpr (isD0Table() || isD0McTable()) { + return static_cast(o2::constants::physics::MassD0); + } else if constexpr (isDplusTable() || isDplusMcTable()) { + return static_cast(o2::constants::physics::MassDPlus); + } else if constexpr (isDsTable() || isDsMcTable()) { + return static_cast(o2::constants::physics::MassDS); + } else if constexpr (isDstarTable() || isDstarMcTable()) { + return static_cast(o2::constants::physics::MassDStar); + } else if constexpr (isLcTable() || isLcMcTable()) { + return static_cast(o2::constants::physics::MassLambdaCPlus); + } else if constexpr (isB0Table() || isB0McTable()) { + return static_cast(o2::constants::physics::MassB0); + } else if constexpr (isBplusTable() || isBplusMcTable()) { + return static_cast(o2::constants::physics::MassBPlus); + } else if constexpr (isXicToXiPiPiTable() || isXicToXiPiPiMcTable()) { + return static_cast(o2::constants::physics::MassXiCPlus); + } else { + return -1.0; + } +} + +/** + * returns the mass of the candidate based on HF Table + * + * @param candidate HF candidate that is being checked + */ +template +float getHFCandidateInvariantMass(T const& candidate) +{ + return candidate.m(); +} + /** * returns true if the candidate is matched to a reconstructed level candidate with the correct decay * * @param candidate candidate that is being checked @@ -609,60 +735,9 @@ bool isHFDaughterTrack(T& track, U& candidate) * @param particles particle table */ template -auto matchedHFParticle(const T& candidate, const U& /*tracks*/, const V& /*particles*/, bool& isMatched) +auto matchedHFParticle(const T& candidate, const U& /*tracks*/, const V& particles) { - - typename V::iterator candidateDaughterParticle; - isMatched = false; - if constexpr (isD0Candidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK) { - candidateDaughterParticle = candidate.template prong0_as().template mcParticle_as(); - isMatched = true; - } - } - if constexpr (isDplusCandidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_3prong::DecayChannelMain::DplusToPiKPi) { - candidateDaughterParticle = candidate.template prong0_as().template mcParticle_as(); - isMatched = true; - } - } - if constexpr (isDsCandidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_3prong::DecayChannelMain::DsToPiKK) { - candidateDaughterParticle = candidate.template prong0_as().template mcParticle_as(); - isMatched = true; - } - } - if constexpr (isDstarCandidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_dstar::DecayChannelMain::DstarToPiKPi) { - candidateDaughterParticle = candidate.template prong2_as().template mcParticle_as(); - isMatched = true; - } - } - if constexpr (isLcCandidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi) { - candidateDaughterParticle = candidate.template prong0_as().template mcParticle_as(); - isMatched = true; - } - } - if constexpr (isB0Candidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_beauty::DecayChannelMain::B0ToDminusPi) { - candidateDaughterParticle = candidate.template prong3_as().template mcParticle_as(); - isMatched = true; - } - } - if constexpr (isBplusCandidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::hf_decay::hf_cand_beauty::DecayChannelMain::BplusToD0Pi) { - candidateDaughterParticle = candidate.template prong2_as().template mcParticle_as(); - isMatched = true; - } - } - if constexpr (isXicToXiPiPiCandidate()) { - if (std::abs(candidate.flagMcMatchRec()) == o2::aod::hf_cand_xic_to_xi_pi_pi::DecayType::XicToXiPiPi) { - candidateDaughterParticle = candidate.template prong0_as().template mcParticle_as(); - isMatched = true; - } - } - return candidateDaughterParticle.template mothers_first_as(); + return particles.iteratorAt(RecoDecay::getMother(particles, candidate.template prong0_as().template mcParticle_as(), getHFCandidatePDG(candidate), true)); } /** @@ -675,13 +750,8 @@ auto matchedHFParticle(const T& candidate, const U& /*tracks*/, const V& /*parti template auto matchedHFParticleId(const T& candidate, const U& tracks, const V& particles) { - bool isMatched = false; - auto matchedParticle = matchedHFParticle(candidate, tracks, particles, isMatched); - if (isMatched) { - return matchedParticle.globalIndex(); - } else { - return int64_t{-1}; // does this clash with the case where a track doesnt have an associated particle? - } + auto matchedParticle = matchedHFParticle(candidate, tracks, particles); + return matchedParticle.globalIndex(); } /** @@ -767,130 +837,6 @@ int getHFMcCandidateCollisionId(T const& candidate) return candidate.hfMcCollBaseId(); } -/** - * returns the PDG of the candidate based on HF Table - * - * @param candidate HF candidate that is being checked - */ -template -int getHFCandidatePDG(T const& /*candidate*/) -{ - if constexpr (isD0Candidate() || isD0McCandidate()) { - return static_cast(o2::constants::physics::Pdg::kD0); - } else if constexpr (isDplusCandidate() || isDplusMcCandidate()) { - return static_cast(o2::constants::physics::Pdg::kDPlus); - } else if constexpr (isDsCandidate() || isDsMcCandidate()) { - return static_cast(o2::constants::physics::Pdg::kDS); - } else if constexpr (isDstarCandidate() || isDstarMcCandidate()) { - return static_cast(o2::constants::physics::Pdg::kDStar); - } else if constexpr (isLcCandidate() || isLcMcCandidate()) { - return static_cast(o2::constants::physics::Pdg::kLambdaCPlus); - } else if constexpr (isB0Candidate() || isB0McCandidate()) { - return static_cast(o2::constants::physics::Pdg::kB0); - } else if constexpr (isBplusCandidate() || isBplusMcCandidate()) { - return static_cast(o2::constants::physics::Pdg::kBPlus); - } else if constexpr (isXicToXiPiPiCandidate() || isXicToXiPiPiMcCandidate()) { - return static_cast(o2::constants::physics::Pdg::kXiCPlus); - } else { - return 0; - } -} - -/** - * returns the PDG of the candidates in the table type - */ -template -int getHFTablePDG() -{ - if constexpr (isD0Table() || isD0McTable()) { - return static_cast(o2::constants::physics::Pdg::kD0); - } else if constexpr (isDplusTable() || isDplusMcTable()) { - return static_cast(o2::constants::physics::Pdg::kDPlus); - } else if constexpr (isDsTable() || isDsMcTable()) { - return static_cast(o2::constants::physics::Pdg::kDS); - } else if constexpr (isDstarTable() || isDstarMcTable()) { - return static_cast(o2::constants::physics::Pdg::kDStar); - } else if constexpr (isLcTable() || isLcMcTable()) { - return static_cast(o2::constants::physics::Pdg::kLambdaCPlus); - } else if constexpr (isB0Table() || isB0McTable()) { - return static_cast(o2::constants::physics::Pdg::kB0); - } else if constexpr (isBplusTable() || isBplusMcTable()) { - return static_cast(o2::constants::physics::Pdg::kBPlus); - } else if constexpr (isXicToXiPiPiTable() || isXicToXiPiPiMcTable()) { - return static_cast(o2::constants::physics::Pdg::kXiCPlus); - } else { - return 0; - } -} - -/** - * returns the mass of the candidate based on HF Table - * - * @param candidate HF candidate that is being checked - */ -template -float getHFCandidatePDGMass(T const& /*candidate*/) -{ - if constexpr (isD0Candidate() || isD0McCandidate()) { - return static_cast(o2::constants::physics::MassD0); - } else if constexpr (isDplusCandidate() || isDplusMcCandidate()) { - return static_cast(o2::constants::physics::MassDPlus); - } else if constexpr (isDsCandidate() || isDsMcCandidate()) { - return static_cast(o2::constants::physics::MassDS); - } else if constexpr (isDstarCandidate() || isDstarMcCandidate()) { - return static_cast(o2::constants::physics::MassDStar); - } else if constexpr (isLcCandidate() || isLcMcCandidate()) { - return static_cast(o2::constants::physics::MassLambdaCPlus); - } else if constexpr (isB0Candidate() || isB0McCandidate()) { - return static_cast(o2::constants::physics::MassB0); - } else if constexpr (isBplusCandidate() || isBplusMcCandidate()) { - return static_cast(o2::constants::physics::MassBPlus); - } else if constexpr (isXicToXiPiPiCandidate() || isXicToXiPiPiMcCandidate()) { - return static_cast(o2::constants::physics::MassXiCPlus); - } else { - return -1.0; - } -} - -/** - * returns the mass of the candidates in the table type - * - */ -template -float getHFTablePDGMass() -{ - if constexpr (isD0Table() || isD0McTable()) { - return static_cast(o2::constants::physics::MassD0); - } else if constexpr (isDplusTable() || isDplusMcTable()) { - return static_cast(o2::constants::physics::MassDPlus); - } else if constexpr (isDsTable() || isDsMcTable()) { - return static_cast(o2::constants::physics::MassDS); - } else if constexpr (isDstarTable() || isDstarMcTable()) { - return static_cast(o2::constants::physics::MassDStar); - } else if constexpr (isLcTable() || isLcMcTable()) { - return static_cast(o2::constants::physics::MassLambdaCPlus); - } else if constexpr (isB0Table() || isB0McTable()) { - return static_cast(o2::constants::physics::MassB0); - } else if constexpr (isBplusTable() || isBplusMcTable()) { - return static_cast(o2::constants::physics::MassBPlus); - } else if constexpr (isXicToXiPiPiTable() || isXicToXiPiPiMcTable()) { - return static_cast(o2::constants::physics::MassXiCPlus); - } else { - return -1.0; - } -} - -/** - * returns the mass of the candidate based on HF Table - * - * @param candidate HF candidate that is being checked - */ -template -float getHFCandidateInvariantMass(T const& candidate) -{ - return candidate.m(); -} - template void fillHFCollisionTable(T const& collision, U& HFCollisionTable) { diff --git a/PWGJE/Core/JetTaggingUtilities.h b/PWGJE/Core/JetTaggingUtilities.h index 0e8b08c3fa2..1d07fd40844 100644 --- a/PWGJE/Core/JetTaggingUtilities.h +++ b/PWGJE/Core/JetTaggingUtilities.h @@ -990,12 +990,16 @@ int vertexClustering(AnyCollision const& collision, AnalysisJet const& jet, AnyT int trkIdx = 0; for (auto const& constituent : jet.template tracks_as()) { if (!constituent.has_mcParticle() || !constituent.template mcParticle_as().isPhysicalPrimary() || constituent.pt() < trackPtMin) { - trkLabels["trkOrigin"].push_back(0); + trkLabels["trkOrigin"].push_back(0); // fake track, non-physical primary track } else { const auto& particle = constituent.template mcParticle_as(); - int orig = RecoDecay::getParticleOrigin(particles, particle, searchUpToQuark); - trkLabels["trkOrigin"].push_back((orig > 0) ? orig : (trkLabels["trkVtxIndex"][trkIdx] == 0) ? 3 - : 4); + if (particle.mcCollisionId() != collision.globalIndex()) { + trkLabels["trkOrigin"].push_back(0); // mismatched coll track + } else { + int orig = RecoDecay::getParticleOrigin(particles, particle, searchUpToQuark); + trkLabels["trkOrigin"].push_back((orig != RecoDecay::OriginType::None) ? orig : (trkLabels["trkVtxIndex"][trkIdx] == 0) ? 3 + : 4); // 1: charm, 2: beauty, 3: primary, 4: other secondary + } } trkIdx++; @@ -1151,6 +1155,65 @@ void analyzeJetTrackInfo4GNN(AnalysisJet const& analysisJet, AnyTracks const& /* } } +// Looping over the track info and putting them in the input vectors (for ParT b-jet tagging). +template +void analyzeJetTrackInfo4ParT(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, std::vector>& featConstit, std::vector>& vecConstit, float trackPtMin = 0.5, float trackDcaXYMax = 10.0, float trackDcaZMax = 10.0) +{ + for (const auto& constituent : analysisJet.template tracks_as()) { + + if (constituent.pt() < trackPtMin || !trackAcceptanceWithDca(constituent, trackDcaXYMax, trackDcaZMax)) { + continue; + } + + int sign = getGeoSign(analysisJet, constituent); + + float trackPt = constituent.pt(); + float trackEta = constituent.eta(); + float trackPhi = constituent.phi(); + + float ip2d = std::abs(constituent.dcaXY()) * sign; + float ipz = std::abs(constituent.dcaZ()) * sign; + float ip2dSigma = constituent.sigmadcaXY(); + float ipzSigma = constituent.sigmadcaZ(); + + float dPhi = std::atan2(std::sin(trackPhi - analysisJet.phi()), std::cos(trackPhi - analysisJet.phi())); + + featConstit.emplace_back(std::vector{ + std::log(trackPt), + std::log(trackPt / analysisJet.pt()), + jetutilities::deltaR(analysisJet, constituent), + trackEta - analysisJet.eta(), + dPhi, + std::tanh(ip2d), + std::tanh(ipz), + (ip2dSigma != 0.f) ? ip2d / ip2dSigma : 0.f, + (ipzSigma != 0.f) ? ipz / ipzSigma : 0.f, + static_cast(constituent.sign())}); + + vecConstit.emplace_back(std::vector{ + trackPt * std::cos(trackPhi), + trackPt * std::sin(trackPhi), + trackPt * std::sinh(trackEta), + trackPt * std::cosh(trackEta)}); + } +} + +// Discriminant value. Probability order is assumed to be [b, c, lf]. +template +T getDbFromProb(const std::vector& probs, double fC = 0.018) +{ + // MlResponse::getModelOutput() returns exactly nClassesMl entries, so a mismatched + // nClassesMl silently truncates the model output and would read out of bounds here. + if (probs.size() < 3) { + LOG(fatal) << "getDbFromProb expects 3 class probabilities but received " << probs.size() << ". Set the nClassesMl configurable to 3 (and give cutDirMl 3 entries to match)."; + } + T denominator = fC * probs[1] + (1. - fC) * probs[2]; + if (probs[0] <= 0. || denominator <= 0.) { + return std::numeric_limits::quiet_NaN(); + } + return std::log(probs[0] / denominator); +} + // Discriminant value for GNN b-jet tagging template T getDb(const std::vector& logits, double fC = 0.018) diff --git a/PWGJE/Core/JetV0Utilities.h b/PWGJE/Core/JetV0Utilities.h index 61a8dd9ec86..cc6569e75aa 100644 --- a/PWGJE/Core/JetV0Utilities.h +++ b/PWGJE/Core/JetV0Utilities.h @@ -142,7 +142,7 @@ bool isV0Particle(T const& particles, U const& particle, bool v0ChargedDecaysOnl if (v0ChargedDecaysOnly) { return RecoDecay::isMatchedMCGen(particles, particle, +kK0Short, std::array{+kPiPlus, -kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, +kLambda0, std::array{+kProton, -kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, -kLambda0, std::array{-kProton, +kPiPlus}, true); } else { - return RecoDecay::isMatchedMCGen(particles, particle, +kK0Short, std::array{+kPiPlus, -kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, +kK0Short, std::array{+kPi0, +kPi0}, true) || RecoDecay::isMatchedMCGen(particles, particle, +kLambda0, std::array{+kProton, -kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, +kLambda0, std::array{+kNeutron, +kPi0}, true) || RecoDecay::isMatchedMCGen(particles, particle, -kLambda0, std::array{-kProton, +kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, -kLambda0, std::array{+kNeutron, +kPi0}, true); + return RecoDecay::isMatchedMCGen(particles, particle, +kK0Short, std::array{+kPiPlus, -kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, +kK0Short, std::array{+kPi0, +kPi0}, true) || RecoDecay::isMatchedMCGen(particles, particle, +kLambda0, std::array{+kProton, -kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, +kLambda0, std::array{+kNeutron, +kPi0}, true) || RecoDecay::isMatchedMCGen(particles, particle, -kLambda0, std::array{-kProton, +kPiPlus}, true) || RecoDecay::isMatchedMCGen(particles, particle, -kLambda0, std::array{-kNeutron, +kPi0}, true); } } @@ -183,6 +183,32 @@ uint8_t setV0ParticleDecayBit(T const& particles, U const& particle) return bit; } +// need to update this +template +void fillV0CandidateTable(T const& candidate, U& V0CoresTable, V& V0SignalFlagsTable) +{ + + V0CoresTable( + candidate.x(), + candidate.y(), + candidate.z(), + candidate.pxpos(), + candidate.pypos(), + candidate.pzpos(), + candidate.pxneg(), + candidate.pyneg(), + candidate.pzneg(), + candidate.dcaV0daughters(), + candidate.dcapostopv(), + candidate.dcanegtopv(), + candidate.v0cosPA(), + candidate.dcav0topv(), + candidate.v0Type()); + + V0SignalFlagsTable( + candidate.signalFlag()); +} + }; // namespace jetv0utilities #endif // PWGJE_CORE_JETV0UTILITIES_H_ diff --git a/PWGJE/Core/MlResponseHfTagging.h b/PWGJE/Core/MlResponseHfTagging.h index 4740a087a79..b6cc72ed8d9 100644 --- a/PWGJE/Core/MlResponseHfTagging.h +++ b/PWGJE/Core/MlResponseHfTagging.h @@ -34,10 +34,9 @@ // Fill the map of available input features // the key is the feature's name (std::string) // the value is the corresponding value in EnumInputFeatures -#define FILL_MAP_BJET(FEATURE) \ - { \ - #FEATURE, static_cast(InputFeaturesBTag::FEATURE) \ - } +#define FILL_MAP_BJET(FEATURE) \ + { \ + #FEATURE, static_cast(InputFeaturesBTag::FEATURE)} // Check if the index of mCachedIndices (index associated to a FEATURE) // matches the entry in EnumInputFeatures associated to this FEATURE @@ -120,6 +119,22 @@ class MlResponseHfTagging : public MlResponse /// @return number of output nodes int getOutputNodes(int imod = 0) const { return this->mModels[imod].getNumOutputNodes(); } + /// @brief Run a model that takes several pre-built input tensors (e.g. ParT: pf_features, pf_vectors, pf_mask inputs). + /// \param input the input tensors, in the order the graph declares them + /// \param nModel index of the model + /// @return the model output, mNClasses entries long + template + std::vector getModelOutputMultiInput(T& input, int nModel = 0) + { + // evalModel returns an owning copy of the (last) output tensor of the model + std::vector output = this->mModels[nModel].template evalModel(input); + if (output.size() < this->mNClasses) { + LOG(fatal) << "Model " << this->mPaths[nModel] << " returned " << output.size() << " scores, but " << static_cast(this->mNClasses) << " classes are expected. Please check your configurables."; + } + output.resize(this->mNClasses); + return output; + } + /// Method to fill the inputs of jet, tracks and secondary vertices /// \param jet is the b-jet candidate /// \param tracks is the vector of tracks associated to the jet @@ -424,7 +439,7 @@ class GNNBjetAllocator : public TensorAllocator public: GNNBjetAllocator() : TensorAllocator(), nJetFeat(4), nTrkFeat(13), nFlav(3), nTrkOrigin(5), maxNNodes(40), tfFunc([](float x) { return x; }) {} - GNNBjetAllocator(int64_t nJetFeat, int64_t nTrkFeat, int64_t nFlav, int64_t nTrkOrigin, std::vector& tfJetMean, std::vector& tfJetStdev, std::vector& tfTrkMean, std::vector& tfTrkStdev, int64_t maxNNodes = 40, std::string tfFuncType = "linear") + GNNBjetAllocator(int64_t nJetFeat, int64_t nTrkFeat, int64_t nFlav, int64_t nTrkOrigin, std::vector& tfJetMean, std::vector& tfJetStdev, std::vector& tfTrkMean, std::vector& tfTrkStdev, int64_t maxNNodes = 40, const std::string& tfFuncType = "linear") : TensorAllocator(), nJetFeat(nJetFeat), nTrkFeat(nTrkFeat), nFlav(nFlav), nTrkOrigin(nTrkOrigin), maxNNodes(maxNNodes), tfJetMean(tfJetMean), tfJetStdev(tfJetStdev), tfTrkMean(tfTrkMean), tfTrkStdev(tfTrkStdev), tfFunc([](float x) { return x; }) { if (tfFuncType == "asinh") { @@ -484,6 +499,85 @@ class GNNBjetAllocator : public TensorAllocator } }; +// TensorAllocator for the ParT b-jet tagger. +// Inputs, in graph order: pf_features (1, nFeat, nConstit), pf_vectors (1, nVec, nConstit), +// pf_mask (1, 1, nConstit). +class ParTBjetAllocator : public TensorAllocator +{ + private: + int64_t nConstit; + int64_t nFeat; + int64_t nVec; + bool padWrap; + + // Replace non-finite values in a vector, which the model cannot consume + template + static void replaceNonFinite(std::vector& vec, T value) + { + for (auto& el : vec) { // o2-linter: disable=const-ref-in-for-loop + if (std::isnan(el) || std::isinf(el)) { + el = value; + } + } + } + + public: + ParTBjetAllocator() : TensorAllocator(), nConstit(32), nFeat(10), nVec(4), padWrap(true) {} + ParTBjetAllocator(int64_t nConstit, int64_t nFeat, int64_t nVec, bool padWrap = true) + : TensorAllocator(), nConstit(nConstit), nFeat(nFeat), nVec(nVec), padWrap(padWrap) {} + ~ParTBjetAllocator() = default; + + ParTBjetAllocator& operator=(const ParTBjetAllocator& other) + { + nConstit = other.nConstit; + nFeat = other.nFeat; + nVec = other.nVec; + padWrap = other.padWrap; + return *this; + } + + // Allocate and return the ParT input tensors (std::vector). + // featData/vecData/maskData back the tensors + void getParTInput(const std::vector>& featConstit, const std::vector>& vecConstit, std::vector& featData, std::vector& vecData, std::vector& maskData, std::vector& partInput) + { + featData.assign(nFeat * nConstit, 0.f); + vecData.assign(nVec * nConstit, 0.f); + maskData.assign(nConstit, 0.f); + + int64_t nSelected = static_cast(featConstit.size()); + const int64_t nReal = (nSelected < nConstit) ? nSelected : nConstit; // jets longer than nConstit are truncated + + for (int64_t iConstit = 0; iConstit < nConstit; ++iConstit) { + int64_t iSource = iConstit; + if (iConstit < nReal) { + maskData[iConstit] = 1.f; + } else { + if (!padWrap || nReal == 0) { + continue; // zero padding, mask stays 0 + } + iSource = iConstit % nReal; // weaver 'wrap' padding: cyclic repetition, mask stays 0 + } + for (int64_t iFeat = 0; iFeat < nFeat; ++iFeat) { + featData[iFeat * nConstit + iConstit] = featConstit[iSource][iFeat]; + } + for (int64_t iVec = 0; iVec < nVec; ++iVec) { + vecData[iVec * nConstit + iConstit] = vecConstit[iSource][iVec]; + } + } + + replaceNonFinite(featData, 0.f); + replaceNonFinite(vecData, 0.f); + + std::vector featShape{1, nFeat, nConstit}; + std::vector vecShape{1, nVec, nConstit}; + std::vector maskShape{1, 1, nConstit}; + + partInput.emplace_back(createTensor(featData, featShape)); + partInput.emplace_back(createTensor(vecData, vecShape)); + partInput.emplace_back(createTensor(maskData, maskShape)); + } +}; + } // namespace o2::analysis #undef FILL_MAP_BJET diff --git a/PWGJE/Core/PWGJECoreLinkDef.h b/PWGJE/Core/PWGJECoreLinkDef.h index 869be064e38..cd25d7b94be 100644 --- a/PWGJE/Core/PWGJECoreLinkDef.h +++ b/PWGJE/Core/PWGJECoreLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGJE_CORE_PWGJECORELINKDEF_H_ -#define PWGJE_CORE_PWGJECORELINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -21,5 +20,3 @@ #pragma link C++ namespace jetutilities + ; #pragma link C++ namespace fastjetutilities + ; #pragma link C++ namespace jettaggingutilities + ; - -#endif // PWGJE_CORE_PWGJECORELINKDEF_H_ diff --git a/PWGJE/Core/jetDataModelPCH.cxx b/PWGJE/Core/jetDataModelPCH.cxx new file mode 100644 index 00000000000..ed5b36ec798 --- /dev/null +++ b/PWGJE/Core/jetDataModelPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the JetDataModelPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGJE/DataModel/EMCALClusterDefinition.h b/PWGJE/DataModel/EMCALClusterDefinition.h index 41e87590449..d2278bf7ca7 100644 --- a/PWGJE/DataModel/EMCALClusterDefinition.h +++ b/PWGJE/DataModel/EMCALClusterDefinition.h @@ -16,12 +16,14 @@ #ifndef PWGJE_DATAMODEL_EMCALCLUSTERDEFINITION_H_ #define PWGJE_DATAMODEL_EMCALCLUSTERDEFINITION_H_ +#include #include +#include namespace o2::aod { -enum class ClusterAlgorithm_t { - kV1, +enum class ClusterAlgorithm : uint8_t { + kV1 = 0, kV3 }; /// \struct EMCALClusterDefinition @@ -29,7 +31,7 @@ enum class ClusterAlgorithm_t { /// The cluster definition contains information about the used algorithm, seed threshold, /// cell energy, gradient as well as timing cut struct EMCALClusterDefinition { - ClusterAlgorithm_t algorithm; + ClusterAlgorithm algorithm{ClusterAlgorithm::kV3}; int storageID = -1; // integer ID used to store cluster definition in a flat table int selectedCellType = 1; // EMCal cell type (CURRENTLY NOT USED TO AVOID MULTIPLE CELL LOOPS) std::string name = "kUndefined"; // name of the cluster definition @@ -45,23 +47,11 @@ struct EMCALClusterDefinition { // default constructor EMCALClusterDefinition() = default; // constructor - EMCALClusterDefinition(ClusterAlgorithm_t pAlgorithm, int pStorageID, int pSelectedCellType, std::string pName, double pSeedEnergy, double pMinCellEnergy, double pTimeMin, double pTimeMax, double ptimeDiff, bool pDoGradientCut, double pGradientCut, bool precalcShowerShape5x5) + EMCALClusterDefinition(ClusterAlgorithm pAlgorithm, int pStorageID, int pSelectedCellType, std::string pName, double pSeedEnergy, double pMinCellEnergy, double pTimeMin, double pTimeMax, double ptimeDiff, bool pDoGradientCut, double pGradientCut, bool precalcShowerShape5x5) : algorithm(pAlgorithm), storageID(pStorageID), selectedCellType(pSelectedCellType), name(std::move(pName)), seedEnergy(pSeedEnergy), minCellEnergy(pMinCellEnergy), timeMin(pTimeMin), timeMax(pTimeMax), timeDiff(ptimeDiff), doGradientCut(pDoGradientCut), gradientCut(pGradientCut), recalcShowerShape5x5(precalcShowerShape5x5) { - algorithm = pAlgorithm; - storageID = pStorageID; - selectedCellType = pSelectedCellType; - name = pName; - seedEnergy = pSeedEnergy; - minCellEnergy = pMinCellEnergy; - timeMin = pTimeMin; - timeMax = pTimeMax; - timeDiff = ptimeDiff; - doGradientCut = pDoGradientCut; - gradientCut = pGradientCut; - recalcShowerShape5x5 = precalcShowerShape5x5; } - // implement comparison operators for int std::string and ClusterAlgorithm_t + // implement comparison operators for int std::string and ClusterAlgorithm bool operator==(const EMCALClusterDefinition& rhs) const { return (algorithm == rhs.algorithm && storageID == rhs.storageID && name == rhs.name && seedEnergy == rhs.seedEnergy && minCellEnergy == rhs.minCellEnergy && timeMin == rhs.timeMin && timeMax == rhs.timeMax && timeDiff == rhs.timeDiff && gradientCut == rhs.gradientCut && doGradientCut == rhs.doGradientCut && recalcShowerShape5x5 == rhs.recalcShowerShape5x5); @@ -86,11 +76,11 @@ struct EMCALClusterDefinition { { return !(name == rhs); } - bool operator==(const ClusterAlgorithm_t rhs) const + bool operator==(const ClusterAlgorithm rhs) const { return (algorithm == rhs); } - bool operator!=(const ClusterAlgorithm_t rhs) const + bool operator!=(const ClusterAlgorithm rhs) const { return !(algorithm == rhs); } @@ -106,11 +96,11 @@ struct EMCALClusterDefinition { return name; } - operator ClusterAlgorithm_t() const + operator ClusterAlgorithm() const { return algorithm; } - std::string toString() const + [[nodiscard]] const std::string& toString() const { return name; } diff --git a/PWGJE/DataModel/EMCALClusters.h b/PWGJE/DataModel/EMCALClusters.h index 5f85849bcc6..6e7dd16d676 100644 --- a/PWGJE/DataModel/EMCALClusters.h +++ b/PWGJE/DataModel/EMCALClusters.h @@ -11,7 +11,7 @@ /// \file EMCALClusters.h /// \brief Table definitions for EMCAL analysis clusters -/// \author Raymond Ehlers , ORNL +/// \author Raymond Ehlers , ORNL, Florian Jonas , Marvin Hemmer #ifndef PWGJE_DATAMODEL_EMCALCLUSTERS_H_ #define PWGJE_DATAMODEL_EMCALCLUSTERS_H_ @@ -23,6 +23,7 @@ #include #include +#include #include namespace o2::aod @@ -32,70 +33,124 @@ namespace emcalcluster // define global cluster definitions // New definitions should be added here! -const EMCALClusterDefinition kV3NoSplit(ClusterAlgorithm_t::kV3, 0, 1, "kV3NoSplit", 0.5, 0.1, -10000, 10000, 20000, false, 0., false); -const EMCALClusterDefinition kV3NoSplitLowSeed(ClusterAlgorithm_t::kV3, 1, 1, "kV3NoSplitLowSeed", 0.3, 0.1, -10000, 10000, 20000, false, 0., false); -const EMCALClusterDefinition kV3NoSplitLowerSeed(ClusterAlgorithm_t::kV3, 2, 1, "kV3NoSplitLowerSeed", 0.2, 0.1, -10000, 10000, 20000, false, 0., false); -const EMCALClusterDefinition kV3Default(ClusterAlgorithm_t::kV3, 10, 1, "kV3Default", 0.5, 0.1, -10000, 10000, 20000, true, 0.03, false); -const EMCALClusterDefinition kV3MostSplit(ClusterAlgorithm_t::kV3, 11, 1, "kV3MostSplit", 0.5, 0.1, -10000, 10000, 20000, true, 0., false); -const EMCALClusterDefinition kV3LowSeed(ClusterAlgorithm_t::kV3, 12, 1, "kV3LowSeed", 0.3, 0.1, -10000, 10000, 20000, true, 0.03, false); -const EMCALClusterDefinition kV3MostSplitLowSeed(ClusterAlgorithm_t::kV3, 13, 1, "kV3MostSplitLowSeed", 0.3, 0.1, -10000, 10000, 20000, true, 0., false); -const EMCALClusterDefinition kV3StrictTime(ClusterAlgorithm_t::kV3, 20, 1, "kV3StrictTime", 0.5, 0.1, -500, 500, 20000, true, 0.03, false); -const EMCALClusterDefinition kV3StricterTime(ClusterAlgorithm_t::kV3, 21, 1, "kV3StricterTime", 0.5, 0.1, -100, 100, 20000, true, 0.03, false); -const EMCALClusterDefinition kV3MostStrictTime(ClusterAlgorithm_t::kV3, 22, 1, "kV3MostStrictTime", 0.5, 0.1, -50, 50, 20000, true, 0.03, false); -const EMCALClusterDefinition kV3Default5x5(ClusterAlgorithm_t::kV3, 30, 1, "kV3Default5x5", 0.5, 0.1, -10000, 10000, 20000, true, 0.03, true); -const EMCALClusterDefinition kV3SmallTimeDiff(ClusterAlgorithm_t::kV3, 40, 1, "kV3SmallTimeDiff", 0.5, 0.1, -10000, 10000, 500, true, 0.03, false); -const EMCALClusterDefinition kV3SmallerTimeDiff(ClusterAlgorithm_t::kV3, 41, 1, "kV3SmallerTimeDiff", 0.5, 0.1, -10000, 10000, 100, true, 0.03, false); -const EMCALClusterDefinition kV3SmallestTimeDiff(ClusterAlgorithm_t::kV3, 42, 1, "kV3SmallestTimeDiff", 0.5, 0.1, -10000, 10000, 50, true, 0.03, false); -const EMCALClusterDefinition kV3MostSplitSmallTimeDiff(ClusterAlgorithm_t::kV3, 43, 1, "kV3MostSplitSmallTimeDiff", 0.5, 0.1, -10000, 10000, 500, true, 0., false); -const EMCALClusterDefinition kV3MostSplitSmallerTimeDiff(ClusterAlgorithm_t::kV3, 44, 1, "kV3MostSplitSmallerTimeDiff", 0.5, 0.1, -10000, 10000, 100, true, 0., false); -const EMCALClusterDefinition kV3MostSplitSmallestTimeDiff(ClusterAlgorithm_t::kV3, 45, 1, "kV3MostSplitSmallestTimeDiff", 0.5, 0.1, -10000, 10000, 50, true, 0., false); -const EMCALClusterDefinition kV3MostSplitSmallestTimeDiffLowestSeed(ClusterAlgorithm_t::kV3, 50, 1, "kV3MostSplitSmallestTimeDiffLowestSeed", 0.1, 0.1, -10000, 10000, 50, true, 0., false); +inline const EMCALClusterDefinition kV3NoSplit(ClusterAlgorithm::kV3, 0, 1, "kV3NoSplit", 0.5, 0.1, -10000, 10000, 20000, false, 0., false); +inline const EMCALClusterDefinition kV3NoSplitLowSeed(ClusterAlgorithm::kV3, 1, 1, "kV3NoSplitLowSeed", 0.3, 0.1, -10000, 10000, 20000, false, 0., false); +inline const EMCALClusterDefinition kV3NoSplitLowerSeed(ClusterAlgorithm::kV3, 2, 1, "kV3NoSplitLowerSeed", 0.2, 0.1, -10000, 10000, 20000, false, 0., false); +inline const EMCALClusterDefinition kV3Default(ClusterAlgorithm::kV3, 10, 1, "kV3Default", 0.5, 0.1, -10000, 10000, 20000, true, 0.03, false); +inline const EMCALClusterDefinition kV3MostSplit(ClusterAlgorithm::kV3, 11, 1, "kV3MostSplit", 0.5, 0.1, -10000, 10000, 20000, true, 0., false); +inline const EMCALClusterDefinition kV3LowSeed(ClusterAlgorithm::kV3, 12, 1, "kV3LowSeed", 0.3, 0.1, -10000, 10000, 20000, true, 0.03, false); +inline const EMCALClusterDefinition kV3MostSplitLowSeed(ClusterAlgorithm::kV3, 13, 1, "kV3MostSplitLowSeed", 0.3, 0.1, -10000, 10000, 20000, true, 0., false); +inline const EMCALClusterDefinition kV3StrictTime(ClusterAlgorithm::kV3, 20, 1, "kV3StrictTime", 0.5, 0.1, -500, 500, 20000, true, 0.03, false); +inline const EMCALClusterDefinition kV3StricterTime(ClusterAlgorithm::kV3, 21, 1, "kV3StricterTime", 0.5, 0.1, -100, 100, 20000, true, 0.03, false); +inline const EMCALClusterDefinition kV3MostStrictTime(ClusterAlgorithm::kV3, 22, 1, "kV3MostStrictTime", 0.5, 0.1, -50, 50, 20000, true, 0.03, false); +inline const EMCALClusterDefinition kV3Default5x5(ClusterAlgorithm::kV3, 30, 1, "kV3Default5x5", 0.5, 0.1, -10000, 10000, 20000, true, 0.03, true); +inline const EMCALClusterDefinition kV3SmallTimeDiff(ClusterAlgorithm::kV3, 40, 1, "kV3SmallTimeDiff", 0.5, 0.1, -10000, 10000, 500, true, 0.03, false); +inline const EMCALClusterDefinition kV3SmallerTimeDiff(ClusterAlgorithm::kV3, 41, 1, "kV3SmallerTimeDiff", 0.5, 0.1, -10000, 10000, 100, true, 0.03, false); +inline const EMCALClusterDefinition kV3SmallestTimeDiff(ClusterAlgorithm::kV3, 42, 1, "kV3SmallestTimeDiff", 0.5, 0.1, -10000, 10000, 50, true, 0.03, false); +inline const EMCALClusterDefinition kV3MostSplitSmallTimeDiff(ClusterAlgorithm::kV3, 43, 1, "kV3MostSplitSmallTimeDiff", 0.5, 0.1, -10000, 10000, 500, true, 0., false); +inline const EMCALClusterDefinition kV3MostSplitSmallerTimeDiff(ClusterAlgorithm::kV3, 44, 1, "kV3MostSplitSmallerTimeDiff", 0.5, 0.1, -10000, 10000, 100, true, 0., false); +inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiff(ClusterAlgorithm::kV3, 45, 1, "kV3MostSplitSmallestTimeDiff", 0.5, 0.1, -10000, 10000, 50, true, 0., false); +inline const EMCALClusterDefinition kV3SmallTimeDiffLowestSeed(ClusterAlgorithm::kV3, 46, 1, "kV3SmallTimeDiffLowestSeed", 0.1, 0.1, -10000, 10000, 500, true, 0.03, false); +inline const EMCALClusterDefinition kV3SmallerTimeDiffLowSeed(ClusterAlgorithm::kV3, 47, 1, "kV3SmallerTimeDiffLowSeed", 0.3, 0.1, -10000, 10000, 100, true, 0.03, false); +inline const EMCALClusterDefinition kV3SmallestTimeDiffLowerSeed(ClusterAlgorithm::kV3, 48, 1, "kV3SmallestTimeDiffLowerSeed", 0.2, 0.1, -10000, 10000, 50, true, 0.03, false); +inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiffLowestSeed(ClusterAlgorithm::kV3, 50, 1, "kV3MostSplitSmallestTimeDiffLowestSeed", 0.1, 0.1, -10000, 10000, 50, true, 0., false); +inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiffLowSeed(ClusterAlgorithm::kV3, 51, 1, "kV3MostSplitSmallestTimeDiffLowSeed", 0.3, 0.1, -10000, 10000, 50, true, 0., false); +inline const EMCALClusterDefinition kV3MostSplitSmallestTimeDiffLowerSeed(ClusterAlgorithm::kV3, 52, 1, "kV3MostSplitSmallestTimeDiffLowerSeed", 0.2, 0.1, -10000, 10000, 50, true, 0., false); + +/// \brief function returns EMCALClusterDefinition for the given storage ID +/// \param storageID storage ID of the cluster definition +/// \return EMCALClusterDefinition for the given storage ID +inline const EMCALClusterDefinition& getClusterDefinitionFromID(int storageID) +{ + switch (storageID) { + case 0: + return kV3NoSplit; + case 1: + return kV3NoSplitLowSeed; + case 2: + return kV3NoSplitLowerSeed; + case 10: + return kV3Default; + case 11: + return kV3MostSplit; + case 12: + return kV3LowSeed; + case 13: + return kV3MostSplitLowSeed; + case 20: + return kV3StrictTime; + case 21: + return kV3StricterTime; + case 22: + return kV3MostStrictTime; + case 30: + return kV3Default5x5; + case 40: + return kV3SmallTimeDiff; + case 41: + return kV3SmallerTimeDiff; + case 42: + return kV3SmallestTimeDiff; + case 43: + return kV3MostSplitSmallTimeDiff; + case 44: + return kV3MostSplitSmallerTimeDiff; + case 45: + return kV3MostSplitSmallestTimeDiff; + case 46: + return kV3SmallTimeDiffLowestSeed; + case 47: + return kV3SmallerTimeDiffLowSeed; + case 48: + return kV3SmallestTimeDiffLowerSeed; + case 50: + return kV3MostSplitSmallestTimeDiffLowestSeed; + case 51: + return kV3MostSplitSmallestTimeDiffLowSeed; + case 52: + return kV3MostSplitSmallestTimeDiffLowerSeed; + default: + throw std::invalid_argument("Cluster definition storageID not recognized: " + std::to_string(storageID)); + } +} /// \brief function returns EMCALClusterDefinition for the given name -/// \param name name of the cluster definition +/// \param clusterDefinitionName name of the cluster definition /// \return EMCALClusterDefinition for the given name -inline const EMCALClusterDefinition getClusterDefinitionFromString(const std::string& clusterDefinitionName) +inline const EMCALClusterDefinition& getClusterDefinitionFromString(const std::string& clusterDefinitionName) { - if (clusterDefinitionName == "kV3NoSplit") { - return kV3NoSplit; - } else if (clusterDefinitionName == "kV3NoSplitLowSeed") { - return kV3NoSplitLowSeed; - } else if (clusterDefinitionName == "kV3NoSplitLowerSeed") { - return kV3NoSplitLowerSeed; - } else if (clusterDefinitionName == "kV3Default") { - return kV3Default; - } else if (clusterDefinitionName == "kV3MostSplit") { - return kV3MostSplit; - } else if (clusterDefinitionName == "kV3LowSeed") { - return kV3LowSeed; - } else if (clusterDefinitionName == "kV3MostSplitLowSeed") { - return kV3MostSplitLowSeed; - } else if (clusterDefinitionName == "kV3StrictTime") { - return kV3StrictTime; - } else if (clusterDefinitionName == "kV3StricterTime") { - return kV3StricterTime; - } else if (clusterDefinitionName == "kV3MostStrictTime") { - return kV3MostStrictTime; - } else if (clusterDefinitionName == "kV3Default5x5") { - return kV3Default5x5; - } else if (clusterDefinitionName == "kV3SmallTimeDiff") { - return kV3SmallTimeDiff; - } else if (clusterDefinitionName == "kV3SmallerTimeDiff") { - return kV3SmallerTimeDiff; - } else if (clusterDefinitionName == "kV3SmallestTimeDiff") { - return kV3SmallestTimeDiff; - } else if (clusterDefinitionName == "kV3MostSplitSmallTimeDiff") { - return kV3MostSplitSmallTimeDiff; - } else if (clusterDefinitionName == "kV3MostSplitSmallerTimeDiff") { - return kV3MostSplitSmallerTimeDiff; - } else if (clusterDefinitionName == "kV3MostSplitSmallestTimeDiff") { - return kV3MostSplitSmallestTimeDiff; - } else if (clusterDefinitionName == "kV3MostSplitSmallestTimeDiffLowestSeed") { - return kV3MostSplitSmallestTimeDiffLowestSeed; - } else { - throw std::invalid_argument("Cluster definition name not recognized"); + static const std::unordered_map nameToID = { + {"kV3NoSplit", 0}, + {"kV3NoSplitLowSeed", 1}, + {"kV3NoSplitLowerSeed", 2}, + {"kV3Default", 10}, + {"kV3MostSplit", 11}, + {"kV3LowSeed", 12}, + {"kV3MostSplitLowSeed", 13}, + {"kV3StrictTime", 20}, + {"kV3StricterTime", 21}, + {"kV3MostStrictTime", 22}, + {"kV3Default5x5", 30}, + {"kV3SmallTimeDiff", 40}, + {"kV3SmallerTimeDiff", 41}, + {"kV3SmallestTimeDiff", 42}, + {"kV3MostSplitSmallTimeDiff", 43}, + {"kV3MostSplitSmallerTimeDiff", 44}, + {"kV3MostSplitSmallestTimeDiff", 45}, + {"kV3SmallTimeDiffLowestSeed", 46}, + {"kV3SmallerTimeDiffLowSeed", 47}, + {"kV3SmallestTimeDiffLowerSeed", 48}, + {"kV3MostSplitSmallestTimeDiffLowestSeed", 50}, + {"kV3MostSplitSmallestTimeDiffLowSeed", 51}, + {"kV3MostSplitSmallestTimeDiffLowerSeed", 52}, + }; + + auto it = nameToID.find(clusterDefinitionName); + if (it == nameToID.end()) { + throw std::invalid_argument("Cluster definition name not recognized: " + clusterDefinitionName); } -}; + return getClusterDefinitionFromID(it->second); +} DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! collisionID used as index for matched clusters DECLARE_SOA_INDEX_COLUMN(BC, bc); //! bunch crossing ID used as index for ambiguous clusters @@ -113,6 +168,7 @@ DECLARE_SOA_COLUMN(IsExotic, isExotic, bool); //! flag DECLARE_SOA_COLUMN(DistanceToBadChannel, distanceToBadChannel, float); //! distance to bad channel DECLARE_SOA_COLUMN(NLM, nlm, int); //! number of local maxima DECLARE_SOA_COLUMN(Definition, definition, int); //! cluster definition, see EMCALClusterDefinition.h +DECLARE_SOA_COLUMN(Dispersion, dispersion, float); //! cluster energy dispersion (cells) } // namespace emcalcluster // table of clusters that could be matched to a collision @@ -131,6 +187,12 @@ DECLARE_SOA_TABLE(EMCALAmbiguousClusters, "AOD", "EMCALAMBCLUS", //! using EMCALCluster = EMCALClusters::iterator; using EMCALAmbiguousCluster = EMCALAmbiguousClusters::iterator; +DECLARE_SOA_TABLE(Dispersions, "AOD", "DISPERSIONS", //! + o2::soa::Index<>, emcalcluster::Dispersion); + +DECLARE_SOA_TABLE(AmbigousDispersions, "AOD", "AMBDISPERSIONS", //! + o2::soa::Index<>, emcalcluster::Dispersion); + namespace emcalclustermc { DECLARE_SOA_ARRAY_INDEX_COLUMN(McParticle, mcParticle); //! Array of MC particles that deposited energy in this calo cell diff --git a/PWGJE/DataModel/GammaJetAnalysisTree.h b/PWGJE/DataModel/GammaJetAnalysisTree.h index 1f527ff8137..740c0749a38 100644 --- a/PWGJE/DataModel/GammaJetAnalysisTree.h +++ b/PWGJE/DataModel/GammaJetAnalysisTree.h @@ -9,9 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// +/// \file GammaJetAnalysisTree.h /// \brief Table definitions for gamma-jet analyses -/// /// \author Florian Jonas #ifndef PWGJE_DATAMODEL_GAMMAJETANALYSISTREE_H_ @@ -74,9 +73,10 @@ namespace gjmcevent DECLARE_SOA_INDEX_COLUMN(GjEvent, gjevent); DECLARE_SOA_COLUMN(Weight, weight, double); DECLARE_SOA_COLUMN(Rho, rho, float); // gen level rho +DECLARE_SOA_COLUMN(PtHard, ptHard, float); // hard-scattering scale DECLARE_SOA_COLUMN(IsMultipleAssigned, isMultipleAssigned, bool); // if the corresponding MC collision matched to this rec collision was also matched to other rec collisions (allows to skip those on analysis level ) } // namespace gjmcevent -DECLARE_SOA_TABLE(GjMCEvents, "AOD", "GJMCEVENT", gjmcevent::GjEventId, gjmcevent::Weight, gjmcevent::Rho, gjmcevent::IsMultipleAssigned) +DECLARE_SOA_TABLE(GjMCEvents, "AOD", "GJMCEVENT", gjmcevent::GjEventId, gjmcevent::Weight, gjmcevent::Rho, gjmcevent::PtHard, gjmcevent::IsMultipleAssigned) // Information about EMCal clusters namespace gjgamma { @@ -106,8 +106,16 @@ namespace gjgammamcinfo { DECLARE_SOA_COLUMN(Origin, origin, uint16_t); DECLARE_SOA_COLUMN(LeadingEnergyFraction, leadingEnergyFraction, float); // fraction of energy from the leading MC particle +// further information about the particle that produced this cluster, one could also linnk to mcgen particle table +// but for now this is easier as the MCgen table is filtered and I do not want to deal with ensuring the filered table exists and +// does not filter out a mother +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Energy, energy, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(MCIso, mcIso, float); } // namespace gjgammamcinfo -DECLARE_SOA_TABLE(GjGammaMCInfos, "AOD", "GJGAMMAMCINFO", gjgamma::GjEventId, gjgammamcinfo::Origin, gjgammamcinfo::LeadingEnergyFraction) +DECLARE_SOA_TABLE(GjGammaMCInfos, "AOD", "GJGAMMAMCINFO", gjgamma::GjEventId, gjgammamcinfo::Origin, gjgammamcinfo::LeadingEnergyFraction, gjgammamcinfo::Eta, gjgammamcinfo::Phi, gjgammamcinfo::Energy, gjgammamcinfo::Pt, gjgammamcinfo::MCIso) // Generator level particle information from the MC collision that was matched to the reconstructed collision namespace gjmcparticle @@ -178,6 +186,16 @@ DECLARE_SOA_COLUMN(PerpConeRho, perpconerho, float); } // namespace gjmcjet DECLARE_SOA_TABLE(GjMCJets, "AOD", "GJMCJET", gjgamma::GjEventId, gjmcjet::Pt, gjmcjet::Eta, gjmcjet::Phi, gjmcjet::Radius, gjmcjet::Energy, gjmcjet::Mass, gjmcjet::Area, gjmcjet::PerpConeRho) +// MC gen level substructure +namespace gjmcjetsubstructure +{ +DECLARE_SOA_COLUMN(EnergyMother, energyMother, std::vector); //! energy of mother subjet at each splitting +DECLARE_SOA_COLUMN(PtLeading, ptLeading, std::vector); //! pt of leading subjet at each splitting +DECLARE_SOA_COLUMN(PtSubLeading, ptSubLeading, std::vector); //! pt of subleading subjet at each splitting +DECLARE_SOA_COLUMN(Theta, theta, std::vector); //! opening angle theta at each splitting +} // namespace gjmcjetsubstructure +DECLARE_SOA_TABLE(GjMCJetSubstructures, "AOD", "GJMCJETSUBSTR", gjgamma::GjEventId, gjmcjetsubstructure::EnergyMother, gjmcjetsubstructure::PtLeading, gjmcjetsubstructure::PtSubLeading, gjmcjetsubstructure::Theta) + } // namespace o2::aod #endif // PWGJE_DATAMODEL_GAMMAJETANALYSISTREE_H_ diff --git a/PWGJE/DataModel/Jet.h b/PWGJE/DataModel/Jet.h index f4afdc17d1f..18780a9c979 100644 --- a/PWGJE/DataModel/Jet.h +++ b/PWGJE/DataModel/Jet.h @@ -156,6 +156,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! absolute p DECLARE_JET_TABLES(JCollision, _jet_type_##EventWiseSubtracted, _subtracted_track_type_, _hfcand_type_, _shortname_ "EWSJET") \ DECLARE_JETMATCHING_TABLE(_jet_type_, _jet_type_##EventWiseSubtracted, _shortname_ "JET2EWS") \ DECLARE_JETMATCHING_TABLE(_jet_type_##EventWiseSubtracted, _jet_type_, _shortname_ "EWSJET2") \ + DECLARE_JETMATCHING_TABLE(_jet_type_##MCDetectorLevel, _jet_type_##EventWiseSubtracted, _shortname_ "DJET2EWS") \ + DECLARE_JETMATCHING_TABLE(_jet_type_##EventWiseSubtracted, _jet_type_##MCDetectorLevel, _shortname_ "EWSJET2D") \ DECLARE_JET_TABLES(JCollision, _jet_type_##MCDetectorLevelEventWiseSubtracted, _subtracted_track_type_, _hfcand_type_, _shortname_ "DEWSJET") \ DECLARE_MCEVENTWEIGHT_TABLE(_jet_type_##MCDetectorLevelEventWiseSubtracted, _jet_type_##MCDetectorLevelEventWiseSubtracted, _shortname_ "DJETEWSW") \ DECLARE_JETMATCHING_TABLE(_jet_type_##MCDetectorLevel, _jet_type_##MCDetectorLevelEventWiseSubtracted, _shortname_ "DJET2DEWS") \ diff --git a/PWGJE/DataModel/JetReducedData.h b/PWGJE/DataModel/JetReducedData.h index 77f98dc0dbd..66bb38bce86 100644 --- a/PWGJE/DataModel/JetReducedData.h +++ b/PWGJE/DataModel/JetReducedData.h @@ -97,10 +97,14 @@ DECLARE_SOA_COLUMN(AmplitudesFT0A, amplitudesFT0A, std::vector); DECLARE_SOA_COLUMN(AmplitudesFT0C, amplitudesFT0C, std::vector); DECLARE_SOA_COLUMN(AmplitudesFDDA, amplitudesFDDA, std::vector); DECLARE_SOA_COLUMN(AmplitudesFDDC, amplitudesFDDC, std::vector); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, float); DECLARE_SOA_COLUMN(Weight, weight, float); DECLARE_SOA_COLUMN(GetSubGeneratorId, getSubGeneratorId, int); -DECLARE_SOA_COLUMN(EventSel, eventSel, uint16_t); +DECLARE_SOA_COLUMN(EventSel, eventSel, uint16_t); // if the type is changed it also needs to be changed in derived data utilities and the writer DECLARE_SOA_BITMAP_COLUMN(Alias, alias, 32); DECLARE_SOA_BITMAP_COLUMN(Rct, rct, 32); DECLARE_SOA_COLUMN(TrackOccupancyInTimeRange, trackOccupancyInTimeRange, int); @@ -160,7 +164,11 @@ DECLARE_SOA_TABLE_STAGED(JCollisionUPCs, "JCOLLISIONUPC", jcollision::AmplitudesFT0A, jcollision::AmplitudesFT0C, jcollision::AmplitudesFDDA, - jcollision::AmplitudesFDDC); + jcollision::AmplitudesFDDC, + jcollision::EnergyCommonZNA, + jcollision::EnergyCommonZNC, + jcollision::TimeZNA, + jcollision::TimeZNC); DECLARE_SOA_TABLE_STAGED(JCollisionMcInfos, "JCOLLISIONMCINFO", jcollision::Weight, diff --git a/PWGJE/DataModel/JetReducedDataDQ.h b/PWGJE/DataModel/JetReducedDataDQ.h index 6246e4c808c..2f401108a87 100644 --- a/PWGJE/DataModel/JetReducedDataDQ.h +++ b/PWGJE/DataModel/JetReducedDataDQ.h @@ -154,29 +154,29 @@ namespace jdummydq DECLARE_SOA_COLUMN(DummyDQ, dummyDQ, bool); } // namespace jdummydq -DECLARE_SOA_TABLE(JDielectron1Dummys, "AOD", "JDIEL1DUMMY", - jdummydq::DummyDQ, - o2::soa::Marker<1>); +DECLARE_SOA_TABLE_STAGED(JDielectron1Dummys, "JDIEL1DUMMY", + jdummydq::DummyDQ, + o2::soa::Marker<1>); -DECLARE_SOA_TABLE(JDielectron2Dummys, "AOD", "JDIEL2DUMMY", - jdummydq::DummyDQ, - o2::soa::Marker<2>); +DECLARE_SOA_TABLE_STAGED(JDielectron2Dummys, "JDIEL2DUMMY", + jdummydq::DummyDQ, + o2::soa::Marker<2>); -DECLARE_SOA_TABLE(JDielectron3Dummys, "AOD", "JDIEL3DUMMY", - jdummydq::DummyDQ, - o2::soa::Marker<3>); +DECLARE_SOA_TABLE_STAGED(JDielectron3Dummys, "JDIEL3DUMMY", + jdummydq::DummyDQ, + o2::soa::Marker<3>); -DECLARE_SOA_TABLE(JDielectron4Dummys, "AOD", "JDIEL4DUMMY", - jdummydq::DummyDQ, - o2::soa::Marker<4>); +DECLARE_SOA_TABLE_STAGED(JDielectron4Dummys, "JDIEL4DUMMY", + jdummydq::DummyDQ, + o2::soa::Marker<4>); -DECLARE_SOA_TABLE(JDielectron5Dummys, "AOD", "JDIEL5DUMMY", - jdummydq::DummyDQ, - o2::soa::Marker<5>); +DECLARE_SOA_TABLE_STAGED(JDielectron5Dummys, "JDIEL5DUMMY", + jdummydq::DummyDQ, + o2::soa::Marker<5>); -DECLARE_SOA_TABLE(JDielectron6Dummys, "AOD", "JDIEL6DUMMY", - jdummydq::DummyDQ, - o2::soa::Marker<6>); +DECLARE_SOA_TABLE_STAGED(JDielectron6Dummys, "JDIEL6DUMMY", + jdummydq::DummyDQ, + o2::soa::Marker<6>); } // namespace o2::aod diff --git a/PWGJE/DataModel/JetReducedDataSelector.h b/PWGJE/DataModel/JetReducedDataSelector.h index 9529debc58e..2efcfec2476 100644 --- a/PWGJE/DataModel/JetReducedDataSelector.h +++ b/PWGJE/DataModel/JetReducedDataSelector.h @@ -26,14 +26,18 @@ namespace o2::aod namespace jetreduceddataselector { DECLARE_SOA_COLUMN(IsCollisionSelected, isCollisionSelected, bool); +DECLARE_SOA_COLUMN(IsCollisionSelectedForSignalTriggerTrack, isCollisionSelectedForSignalTriggerTrack, bool); DECLARE_SOA_COLUMN(IsMcCollisionSelected, isMcCollisionSelected, bool); +DECLARE_SOA_COLUMN(IsMcCollisionSelectedForSignalTriggerTrack, isMcCollisionSelectedForSignalTriggerTrack, bool); } // namespace jetreduceddataselector DECLARE_SOA_TABLE(JCollisionSelections, "AOD", "JCOLLSELECTION", - jetreduceddataselector::IsCollisionSelected); + jetreduceddataselector::IsCollisionSelected, + jetreduceddataselector::IsCollisionSelectedForSignalTriggerTrack); DECLARE_SOA_TABLE(JMcCollisionSelections, "AOD", "JMCCOLLSELECTION", - jetreduceddataselector::IsMcCollisionSelected); + jetreduceddataselector::IsMcCollisionSelected, + jetreduceddataselector::IsMcCollisionSelectedForSignalTriggerTrack); } // namespace o2::aod #endif // PWGJE_DATAMODEL_JETREDUCEDDATASELECTOR_H_ diff --git a/PWGJE/DataModel/JetReducedDataV0.h b/PWGJE/DataModel/JetReducedDataV0.h index 18f2f3048fe..d3e4c0a5ca0 100644 --- a/PWGJE/DataModel/JetReducedDataV0.h +++ b/PWGJE/DataModel/JetReducedDataV0.h @@ -53,10 +53,10 @@ DECLARE_SOA_TABLE_STAGED(JV0CollisionIds, "JV0COLLID", DECLARE_SOA_TABLE_STAGED(JV0McCollisionIds, "JV0MCCOLLID", jv0indices::JMcCollisionId); -DECLARE_SOA_TABLE(JV0Ids, "AOD", "JV0ID", - jv0indices::JCollisionId, - jv0indices::PosTrackId, - jv0indices::NegTrackId); +DECLARE_SOA_TABLE_STAGED(JV0Ids, "JV0ID", + jv0indices::JCollisionId, + jv0indices::PosTrackId, + jv0indices::NegTrackId); namespace jv0mc { diff --git a/PWGJE/DataModel/JetTagging.h b/PWGJE/DataModel/JetTagging.h index a6b32458543..9ce0ddadb6f 100644 --- a/PWGJE/DataModel/JetTagging.h +++ b/PWGJE/DataModel/JetTagging.h @@ -49,9 +49,9 @@ using JTrackTag = JTracksTag::iterator; namespace secondary_vertex_params { -DECLARE_SOA_COLUMN(XPrimaryVertex, xPVertex, float); // o2-linter: disable=name/o2-column -DECLARE_SOA_COLUMN(YPrimaryVertex, yPVertex, float); // o2-linter: disable=name/o2-column -DECLARE_SOA_COLUMN(ZPrimaryVertex, zPVertex, float); // o2-linter: disable=name/o2-column +DECLARE_SOA_COLUMN(XPrimaryVertex, xPrimaryVertex, float); +DECLARE_SOA_COLUMN(YPrimaryVertex, yPrimaryVertex, float); +DECLARE_SOA_COLUMN(ZPrimaryVertex, zPrimaryVertex, float); DECLARE_SOA_COLUMN(XSecondaryVertex, xSecondaryVertex, float); DECLARE_SOA_COLUMN(YSecondaryVertex, ySecondaryVertex, float); DECLARE_SOA_COLUMN(ZSecondaryVertex, zSecondaryVertex, float); @@ -75,7 +75,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(DecayLength, decayLength, [](float xVtxP, float yVtxP DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthXY, decayLengthXY, [](float xVtxP, float yVtxP, float xVtxS, float yVtxS) -> float { return RecoDecay::distanceXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}); }); DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthNormalised, decayLengthNormalised, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float err) -> float { return RecoDecay::distance(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}) / err; }); DECLARE_SOA_DYNAMIC_COLUMN(DecayLengthXYNormalised, decayLengthXYNormalised, [](float xVtxP, float yVtxP, float xVtxS, float yVtxS, float err) -> float { return RecoDecay::distanceXY(std::array{xVtxP, yVtxP}, std::array{xVtxS, yVtxS}) / err; }); -DECLARE_SOA_DYNAMIC_COLUMN(CPA, cpa, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); // o2-linter: disable=name/o2-column +DECLARE_SOA_DYNAMIC_COLUMN(Cpa, cpa, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::cpa(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterXY, impactParameterXY, [](float xVtxP, float yVtxP, float zVtxP, float xVtxS, float yVtxS, float zVtxS, float px, float py, float pz) -> float { return RecoDecay::impParXY(std::array{xVtxP, yVtxP, zVtxP}, std::array{xVtxS, yVtxS, zVtxS}, std::array{px, py, pz}); }); } // namespace secondary_vertex_params @@ -113,7 +113,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterXY, impactParameterXY, [](float xVtxP, secondary_vertex_params::DecayLengthXY, \ secondary_vertex_params::DecayLengthNormalised, \ secondary_vertex_params::DecayLengthXYNormalised, \ - secondary_vertex_params::CPA, \ + secondary_vertex_params::Cpa, \ secondary_vertex_params::ImpactParameterXY); \ namespace _name_##indices \ { \ @@ -127,6 +127,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(ImpactParameterXY, impactParameterXY, [](float xVtxP, JETSV_TABLES_DEF(Charged, SecondaryVertex3Prong, "3PRONG"); JETSV_TABLES_DEF(Charged, SecondaryVertex2Prong, "2PRONG"); +JETSV_TABLES_DEF(Charged, SecondaryVertexNProng, "NPRONG"); // Defines the jet tagging table definition namespace jettagging diff --git a/PWGJE/DataModel/PhotonChargedTriggerCorrelation.h b/PWGJE/DataModel/PhotonChargedTriggerCorrelation.h index 9b3eef803a8..637a7f18c35 100644 --- a/PWGJE/DataModel/PhotonChargedTriggerCorrelation.h +++ b/PWGJE/DataModel/PhotonChargedTriggerCorrelation.h @@ -43,11 +43,13 @@ DECLARE_SOA_COLUMN(Eta, eta, float); namespace collision_extra_corr { DECLARE_SOA_COLUMN(SelEv, selEv, bool); +DECLARE_SOA_COLUMN(ThirdDecimalTruePosZ, thirdDecimalTruePosZ, int); DECLARE_SOA_COLUMN(TrigEv, trigEv, bool); +DECLARE_SOA_COLUMN(PtMax, ptMax, float); DECLARE_SOA_COLUMN(NGlobalTracks, nGlobalTracks, int); } // namespace collision_extra_corr DECLARE_SOA_TABLE(CollisionsExtraCorr, "AOD", "COLLISIONSEXTRACORR", - collision_extra_corr::SelEv, collision_extra_corr::TrigEv, collision_extra_corr::NGlobalTracks); + collision_extra_corr::SelEv, collision_extra_corr::ThirdDecimalTruePosZ, collision_extra_corr::TrigEv, collision_extra_corr::PtMax, collision_extra_corr::NGlobalTracks); // trigger namespace trigger @@ -83,12 +85,12 @@ using Pipm = Pipms::iterator; namespace photon_pcm { DECLARE_SOA_INDEX_COLUMN_FULL(V0PhotonKF, v0PhotonKF, int, V0PhotonsKF, ""); -DECLARE_SOA_COLUMN(PosTrackId, posTrackId, int); -DECLARE_SOA_COLUMN(NegTrackId, negTrackId, int); +DECLARE_SOA_INDEX_COLUMN_FULL(PosJetTrack, posJetTrack, int, JetTracks, "_pos"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegJetTrack, negJetTrack, int, JetTracks, "_neg"); } // namespace photon_pcm DECLARE_SOA_TABLE(PhotonPCMs, "AOD", "PHOTONPCMS", o2::soa::Index<>, corr_particle::JetCollisionId, photon_pcm::V0PhotonKFId, - photon_pcm::PosTrackId, photon_pcm::NegTrackId, + photon_pcm::PosJetTrackId, photon_pcm::NegJetTrackId, corr_particle::Pt, corr_particle::Phi, corr_particle::Eta); using PhotonPCM = PhotonPCMs::iterator; @@ -97,15 +99,15 @@ namespace photon_pcm_pair { DECLARE_SOA_INDEX_COLUMN_FULL(V0PhotonKF1, v0PhotonKF1, int, V0PhotonsKF, "_1"); DECLARE_SOA_INDEX_COLUMN_FULL(V0PhotonKF2, v0PhotonKF2, int, V0PhotonsKF, "_2"); -DECLARE_SOA_COLUMN(PosTrack1Id, posTrack1Id, int); -DECLARE_SOA_COLUMN(NegTrack1Id, negTrack1Id, int); -DECLARE_SOA_COLUMN(PosTrack2Id, posTrack2Id, int); -DECLARE_SOA_COLUMN(NegTrack2Id, negTrack2Id, int); +DECLARE_SOA_INDEX_COLUMN_FULL(PosJetTrack1, posJetTrack1, int, JetTracks, "_pos1"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegJetTrack1, negJetTrack1, int, JetTracks, "_neg1"); +DECLARE_SOA_INDEX_COLUMN_FULL(PosJetTrack2, posJetTrack2, int, JetTracks, "_pos2"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegJetTrack2, negJetTrack2, int, JetTracks, "_neg2"); DECLARE_SOA_COLUMN(Mgg, mgg, float); } // namespace photon_pcm_pair DECLARE_SOA_TABLE(PhotonPCMPairs, "AOD", "PHOTONPCMPAIRS", o2::soa::Index<>, corr_particle::JetCollisionId, photon_pcm_pair::V0PhotonKF1Id, photon_pcm_pair::V0PhotonKF2Id, - photon_pcm_pair::PosTrack1Id, photon_pcm_pair::NegTrack1Id, photon_pcm_pair::PosTrack2Id, photon_pcm_pair::NegTrack2Id, + photon_pcm_pair::PosJetTrack1Id, photon_pcm_pair::NegJetTrack1Id, photon_pcm_pair::PosJetTrack2Id, photon_pcm_pair::NegJetTrack2Id, corr_particle::Pt, corr_particle::Phi, corr_particle::Eta, photon_pcm_pair::Mgg); using PhotonPCMPair = PhotonPCMPairs::iterator; @@ -114,11 +116,13 @@ using PhotonPCMPair = PhotonPCMPairs::iterator; // mcCollision extension namespace mc_collision_extra_corr { +DECLARE_SOA_COLUMN(ThirdDecimalTruePosZ, thirdDecimalTruePosZ, int); DECLARE_SOA_COLUMN(TrigEv, trigEv, bool); +DECLARE_SOA_COLUMN(PtMax, ptMax, float); DECLARE_SOA_COLUMN(NChargedInEtaRange, nChargedInEtaRange, int); } // namespace mc_collision_extra_corr DECLARE_SOA_TABLE(McCollisionsExtraCorr, "AOD", "MCCOLLISIONSEXTRACORR", - mc_collision_extra_corr::TrigEv, mc_collision_extra_corr::NChargedInEtaRange); + mc_collision_extra_corr::ThirdDecimalTruePosZ, mc_collision_extra_corr::TrigEv, mc_collision_extra_corr::PtMax, mc_collision_extra_corr::NChargedInEtaRange); // trigger namespace trigger_particle diff --git a/PWGJE/JetFinders/CMakeLists.txt b/PWGJE/JetFinders/CMakeLists.txt index 4426a43c4a4..218d03cad31 100644 --- a/PWGJE/JetFinders/CMakeLists.txt +++ b/PWGJE/JetFinders/CMakeLists.txt @@ -15,227 +15,272 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-finder-data-charged SOURCES jetFinderDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-charged SOURCES jetFinderMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-charged SOURCES jetFinderMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-data-full SOURCES jetFinderDataFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-full SOURCES jetFinderMCDFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-full SOURCES jetFinderMCPFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-data-neutral SOURCES jetFinderDataNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-neutral SOURCES jetFinderMCDNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-neutral SOURCES jetFinderMCPNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-data-charged SOURCES jetFinderD0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-mcd-charged SOURCES jetFinderD0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-mcp-charged SOURCES jetFinderD0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-data-charged SOURCES jetFinderDplusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-mcd-charged SOURCES jetFinderDplusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-mcp-charged SOURCES jetFinderDplusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-data-charged SOURCES jetFinderDsDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-mcd-charged SOURCES jetFinderDsMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-mcp-charged SOURCES jetFinderDsMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-data-charged SOURCES jetFinderDstarDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-mcd-charged SOURCES jetFinderDstarMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-mcp-charged SOURCES jetFinderDstarMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-data-charged SOURCES jetFinderLcDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-mcd-charged SOURCES jetFinderLcMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-mcp-charged SOURCES jetFinderLcMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-data-charged SOURCES jetFinderB0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-mcd-charged SOURCES jetFinderB0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-mcp-charged SOURCES jetFinderB0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-data-charged SOURCES jetFinderBplusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-mcd-charged SOURCES jetFinderBplusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-mcp-charged SOURCES jetFinderBplusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-data-charged SOURCES jetFinderXicToXiPiPiDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-mcd-charged SOURCES jetFinderXicToXiPiPiMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-mcp-charged SOURCES jetFinderXicToXiPiPiMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-data-charged SOURCES jetFinderV0DataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-mcd-charged SOURCES jetFinderV0MCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-mcp-charged SOURCES jetFinderV0MCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-data-charged SOURCES jetFinderDielectronDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-mcd-charged SOURCES jetFinderDielectronMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-mcp-charged SOURCES jetFinderDielectronMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-data-charged SOURCES jetFinderD0D0BarDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-mcd-charged SOURCES jetFinderD0D0BarMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0d0bar-mcp-charged SOURCES jetFinderD0D0BarMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-data-charged SOURCES jetFinderDplusDminusDataCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-mcd-charged SOURCES jetFinderDplusDminusMCDCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplusdminus-mcp-charged SOURCES jetFinderDplusDminusMCPCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/JetFinders/Duplicates/CMakeLists.txt b/PWGJE/JetFinders/Duplicates/CMakeLists.txt index 4270de9f221..2e0217b488d 100644 --- a/PWGJE/JetFinders/Duplicates/CMakeLists.txt +++ b/PWGJE/JetFinders/Duplicates/CMakeLists.txt @@ -14,16 +14,19 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-finder-data-charged-1 SOURCES jetFinderDataCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcd-charged-1 SOURCES jetFinderMCDCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-mcp-charged-1 SOURCES jetFinderMCPCharged1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/JetFinders/jetFinder.h b/PWGJE/JetFinders/jetFinder.h index d45cfa15506..d673c550fe1 100644 --- a/PWGJE/JetFinders/jetFinder.h +++ b/PWGJE/JetFinders/jetFinder.h @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -63,6 +64,7 @@ struct JetFinderTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -71,6 +73,8 @@ struct JetFinderTask { o2::framework::Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum track eta"}; o2::framework::Configurable trackPhiMin{"trackPhiMin", -999, "minimum track phi"}; o2::framework::Configurable trackPhiMax{"trackPhiMax", 999, "maximum track phi"}; + o2::framework::Configurable phiExclusionMin{"phiExclusionMin", -999, "minimum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; + o2::framework::Configurable phiExclusionMax{"phiExclusionMax", 999, "maximum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; o2::framework::Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; o2::framework::Configurable particleSelections{"particleSelections", "PhysicalPrimary", "set particle selections"}; @@ -123,13 +127,13 @@ struct JetFinderTask { void init(o2::framework::InitContext const&) { - hadronicCorrectionTypesVec = (std::vector)hadronicCorrectionTypes; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + hadronicCorrectionTypesVec = hadronicCorrectionTypes; + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - particleSelection = static_cast(particleSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + particleSelection = particleSelections; - clusterDefinitionsVec = jetderiveddatautilities::initialiseClusterDefinitions(clusterDefinitions.value); + clusterDefinitionsVec = jetderiveddatautilities::initialiseClusterDefinitions(clusterDefinitions); jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -139,6 +143,15 @@ struct JetFinderTask { jetFinder.phiMin = -1.0 * M_PI; jetFinder.phiMax = 2.0 * M_PI; } + + if (phiExclusionMin > -998.0) { + jetFinder.phiExclusionMin = phiExclusionMin; + jetFinder.phiExclusionMax = phiExclusionMax; + if (phiExclusionMin >= phiExclusionMax) { + throw std::runtime_error("Invalid phi exclusion range: require phiExclusionMin < phiExclusionMax when both are set."); + } + } + jetFinder.jetPhiMin = jetPhiMin; jetFinder.jetPhiMax = jetPhiMax; if (jetPhiMin < -98.0) { @@ -151,8 +164,8 @@ struct JetFinderTask { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -160,7 +173,7 @@ struct JetFinderTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -184,8 +197,8 @@ struct JetFinderTask { o2::framework::expressions::Filter collisionFilter = (nabs(o2::aod::jcollision::posZ) < vertexZCut && o2::aod::jcollision::centFT0M >= centralityMin && o2::aod::jcollision::centFT0M < centralityMax && o2::aod::jcollision::trackOccupancyInTimeRange <= trackOccupancyInTimeRangeMax); // should we add a posZ vtx cut here or leave it to analysers? o2::framework::expressions::Filter mcCollisionFilter = (nabs(o2::aod::jmccollision::posZ) < vertexZCut); - o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax); // do we need eta cut both here and in globalselection? - o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax); + o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jtrack::phi <= phiExclusionMin || o2::aod::jtrack::phi >= phiExclusionMax))); // do we need eta cut both here and in globalselection? + o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jmcparticle::phi <= phiExclusionMin || o2::aod::jmcparticle::phi >= phiExclusionMax))); o2::framework::expressions::Filter clusterFilter = (o2::aod::jcluster::eta >= clusterEtaMin && o2::aod::jcluster::eta <= clusterEtaMax && o2::aod::jcluster::phi >= clusterPhiMin && o2::aod::jcluster::phi <= clusterPhiMax && o2::aod::jcluster::energy >= clusterEnergyMin && o2::aod::jcluster::time > clusterTimeMin && o2::aod::jcluster::time < clusterTimeMax && (!clusterRejectExotics || o2::aod::jcluster::isExotic != true)); void processChargedJets(o2::soa::Filtered::iterator const& collision, @@ -195,7 +208,7 @@ struct JetFinderTask { return; } inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::findJets(jetFinder, inputParticles, jetPtMin, jetPtMax, jetRadius, jetAreaFractionMin, collision, jetsTable, constituentsTable, fillTHnSparse ? registry.get(HIST("hJet")) : std::shared_ptr(nullptr), fillTHnSparse); } @@ -208,7 +221,7 @@ struct JetFinderTask { return; } inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::findJets(jetFinder, inputParticles, jetEWSPtMin, jetEWSPtMax, jetRadius, jetAreaFractionMin, collision, jetsEvtWiseSubTable, constituentsEvtWiseSubTable, fillTHnSparse ? registry.get(HIST("hJetEWS")) : std::shared_ptr(nullptr), fillTHnSparse); } @@ -240,7 +253,7 @@ struct JetFinderTask { for (auto const& clusterDefinition : clusterDefinitionsVec) { for (auto const& hadronicCorrectionType : hadronicCorrectionTypesVec) { inputParticles.clear(); - jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks, o2::soa::Filtered::iterator>(inputParticles, tracks, trackSelection, isEmbedding); jetfindingutilities::analyseClusters(inputParticles, clusters, clusterDefinition, hadronicCorrectionType); jetfindingutilities::findJets(jetFinder, inputParticles, jetPtMin, jetPtMax, jetRadius, jetAreaFractionMin, collision, jetsTable, constituentsTable, fillTHnSparse ? registry.get(HIST("hJet")) : std::shared_ptr(nullptr), fillTHnSparse); } diff --git a/PWGJE/JetFinders/jetFinderHF.h b/PWGJE/JetFinders/jetFinderHF.h index 3016cdb5265..e0a44e52fee 100644 --- a/PWGJE/JetFinders/jetFinderHF.h +++ b/PWGJE/JetFinders/jetFinderHF.h @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -64,6 +65,7 @@ struct JetFinderHFTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -72,6 +74,8 @@ struct JetFinderHFTask { o2::framework::Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum track eta"}; o2::framework::Configurable trackPhiMin{"trackPhiMin", -999, "minimum track phi"}; o2::framework::Configurable trackPhiMax{"trackPhiMax", 999, "maximum track phi"}; + o2::framework::Configurable phiExclusionMin{"phiExclusionMin", -999, "minimum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; + o2::framework::Configurable phiExclusionMax{"phiExclusionMax", 999, "maximum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; o2::framework::Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; o2::framework::Configurable particleSelections{"particleSelections", "PhysicalPrimary", "set particle selections"}; @@ -130,11 +134,11 @@ struct JetFinderHFTask { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); - clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; + clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -146,6 +150,13 @@ struct JetFinderHFTask { jetFinder.phiMin = -1.0 * M_PI; jetFinder.phiMax = 2.0 * M_PI; } + if (phiExclusionMin > -998.0) { + jetFinder.phiExclusionMin = phiExclusionMin; + jetFinder.phiExclusionMax = phiExclusionMax; + if (phiExclusionMin >= phiExclusionMax) { + throw std::runtime_error("Invalid phi exclusion range: require phiExclusionMin < phiExclusionMax when both are set."); + } + } jetFinder.jetPhiMin = jetPhiMin; jetFinder.jetPhiMax = jetPhiMax; if (jetPhiMin < -98.0) { @@ -157,8 +168,8 @@ struct JetFinderHFTask { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -166,7 +177,7 @@ struct JetFinderHFTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -186,8 +197,8 @@ struct JetFinderHFTask { o2::framework::expressions::Filter collisionFilter = (nabs(o2::aod::jcollision::posZ) < vertexZCut && o2::aod::jcollision::centFT0M >= centralityMin && o2::aod::jcollision::centFT0M < centralityMax && o2::aod::jcollision::trackOccupancyInTimeRange <= trackOccupancyInTimeRangeMax); o2::framework::expressions::Filter mcCollisionFilter = (nabs(o2::aod::jmccollision::posZ) < vertexZCut); - o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax); - o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax); + o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jtrack::phi <= phiExclusionMin || o2::aod::jtrack::phi >= phiExclusionMax))); + o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jmcparticle::phi <= phiExclusionMin || o2::aod::jmcparticle::phi >= phiExclusionMax))); o2::framework::expressions::Filter clusterFilter = (o2::aod::jcluster::definition == static_cast(clusterDefinition) && o2::aod::jcluster::eta >= clusterEtaMin && o2::aod::jcluster::eta <= clusterEtaMax && o2::aod::jcluster::phi >= clusterPhiMin && o2::aod::jcluster::phi <= clusterPhiMax && o2::aod::jcluster::energy >= clusterEnergyMin && o2::aod::jcluster::time > clusterTimeMin && o2::aod::jcluster::time < clusterTimeMax && (!clusterRejectExotics || o2::aod::jcluster::isExotic != true)); // o2::framework::expressions::Filter candidateCuts = (o2::aod::hfcand::pt >= candPtMin && o2::aod::hfcand::pt < candPtMax && o2::aod::hfcand::y >= candYMin && o2::aod::hfcand::y < candYMax); @@ -229,9 +240,9 @@ struct JetFinderHFTask { } } if constexpr (isEvtWiseSub) { - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding); } else { - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding, &candidate); } jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, true); } diff --git a/PWGJE/JetFinders/jetFinderHFHFBar.h b/PWGJE/JetFinders/jetFinderHFHFBar.h index ec4cfa9d94c..f37e8bd62b6 100644 --- a/PWGJE/JetFinders/jetFinderHFHFBar.h +++ b/PWGJE/JetFinders/jetFinderHFHFBar.h @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -64,6 +65,7 @@ struct JetFinderHFHFBarTask { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -72,6 +74,8 @@ struct JetFinderHFHFBarTask { o2::framework::Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum track eta"}; o2::framework::Configurable trackPhiMin{"trackPhiMin", -999, "minimum track phi"}; o2::framework::Configurable trackPhiMax{"trackPhiMax", 999, "maximum track phi"}; + o2::framework::Configurable phiExclusionMin{"phiExclusionMin", -999, "minimum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; + o2::framework::Configurable phiExclusionMax{"phiExclusionMax", 999, "maximum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; o2::framework::Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; o2::framework::Configurable particleSelections{"particleSelections", "PhysicalPrimary", "set particle selections"}; @@ -128,10 +132,10 @@ struct JetFinderHFHFBarTask { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -143,6 +147,13 @@ struct JetFinderHFHFBarTask { jetFinder.phiMin = -1.0 * M_PI; jetFinder.phiMax = 2.0 * M_PI; } + if (phiExclusionMin > -998.0) { + jetFinder.phiExclusionMin = phiExclusionMin; + jetFinder.phiExclusionMax = phiExclusionMax; + if (phiExclusionMin >= phiExclusionMax) { + throw std::runtime_error("Invalid phi exclusion range: require phiExclusionMin < phiExclusionMax when both are set."); + } + } jetFinder.jetPhiMin = jetPhiMin; jetFinder.jetPhiMax = jetPhiMax; if (jetPhiMin < -98.0) { @@ -154,8 +165,8 @@ struct JetFinderHFHFBarTask { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -163,7 +174,7 @@ struct JetFinderHFHFBarTask { } jetFinder.fastjetExtraParam = jetExtraParam; - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -181,11 +192,11 @@ struct JetFinderHFHFBarTask { registry.add("hJetMCP", "sparse for mcp jets", {o2::framework::HistType::kTHnD, {{jetRadiiBins, ""}, {jetPtBinNumber, jetPtMinDouble, jetPtMaxDouble}, {40, -1.0, 1.0}, {18, 0.0, 7.0}}}); } - o2::aod::EMCALClusterDefinition clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + o2::aod::EMCALClusterDefinition clusterDefinition = o2::aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); o2::framework::expressions::Filter collisionFilter = (nabs(o2::aod::jcollision::posZ) < vertexZCut && o2::aod::jcollision::centFT0M >= centralityMin && o2::aod::jcollision::centFT0M < centralityMax && o2::aod::jcollision::trackOccupancyInTimeRange <= trackOccupancyInTimeRangeMax); o2::framework::expressions::Filter mcCollisionFilter = (nabs(o2::aod::jmccollision::posZ) < vertexZCut); - o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax); - o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax); + o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jtrack::phi <= phiExclusionMin || o2::aod::jtrack::phi >= phiExclusionMax))); + o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jmcparticle::phi <= phiExclusionMin || o2::aod::jmcparticle::phi >= phiExclusionMax))); o2::framework::expressions::Filter clusterFilter = (o2::aod::jcluster::definition == static_cast(clusterDefinition) && o2::aod::jcluster::eta >= clusterEtaMin && o2::aod::jcluster::eta <= clusterEtaMax && o2::aod::jcluster::phi >= clusterPhiMin && o2::aod::jcluster::phi <= clusterPhiMax && o2::aod::jcluster::energy >= clusterEnergyMin && o2::aod::jcluster::time > clusterTimeMin && o2::aod::jcluster::time < clusterTimeMax && (clusterRejectExotics && o2::aod::jcluster::isExotic != true)); // o2::framework::expressions::Filter candidateCuts = (o2::aod::hfcand::pt >= candPtMin && o2::aod::hfcand::pt < candPtMax && o2::aod::hfcand::y >= candYMin && o2::aod::hfcand::y < candYMax); @@ -234,7 +245,7 @@ struct JetFinderHFHFBarTask { return; } } - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, isEmbedding, &candidate); jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, true); } diff --git a/PWGJE/JetFinders/jetFinderV0.h b/PWGJE/JetFinders/jetFinderV0.h index 14c2f977ada..29c95e5f5b9 100644 --- a/PWGJE/JetFinders/jetFinderV0.h +++ b/PWGJE/JetFinders/jetFinderV0.h @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -59,6 +60,7 @@ struct JetFinderV0Task { o2::framework::Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", true, "decide to run over MB gap events or not"}; o2::framework::Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; // track level configurables o2::framework::Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; @@ -67,6 +69,8 @@ struct JetFinderV0Task { o2::framework::Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum track eta"}; o2::framework::Configurable trackPhiMin{"trackPhiMin", -999, "minimum track phi"}; o2::framework::Configurable trackPhiMax{"trackPhiMax", 999, "maximum track phi"}; + o2::framework::Configurable phiExclusionMin{"phiExclusionMin", -999, "minimum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; + o2::framework::Configurable phiExclusionMax{"phiExclusionMax", 999, "maximum of phi exclusion region which is applied for tracks and for jets (jetR is added to the exclusion region for jets)"}; o2::framework::Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; o2::framework::Configurable particleSelections{"particleSelections", "PhysicalPrimary", "set particle selections"}; @@ -112,10 +116,10 @@ struct JetFinderV0Task { void init(o2::framework::InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; jetFinder.etaMin = trackEtaMin; jetFinder.etaMax = trackEtaMax; @@ -127,6 +131,13 @@ struct JetFinderV0Task { jetFinder.phiMin = -1.0 * M_PI; jetFinder.phiMax = 2.0 * M_PI; } + if (phiExclusionMin > -998.0) { + jetFinder.phiExclusionMin = phiExclusionMin; + jetFinder.phiExclusionMax = phiExclusionMax; + if (phiExclusionMin >= phiExclusionMax) { + throw std::runtime_error("Invalid phi exclusion range: require phiExclusionMin < phiExclusionMax when both are set."); + } + } jetFinder.jetPhiMin = jetPhiMin; jetFinder.jetPhiMax = jetPhiMax; if (jetPhiMin < -98.0) { @@ -138,8 +149,8 @@ struct JetFinderV0Task { if (jetEtaMin < -98.0) { jetFinder.jetEtaDefault = true; } - jetFinder.algorithm = static_cast(static_cast(jetAlgorithm)); - jetFinder.recombScheme = static_cast(static_cast(jetRecombScheme)); + jetFinder.algorithm = static_cast(jetAlgorithm.value); + jetFinder.recombScheme = static_cast(jetRecombScheme.value); jetFinder.ghostArea = jetGhostArea; jetFinder.ghostRepeatN = ghostRepeat; if (DoTriggering) { @@ -154,7 +165,7 @@ struct JetFinderV0Task { candIndex = 1; } - auto jetRadiiBins = (std::vector)jetRadius; + auto jetRadiiBins = jetRadius.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -174,8 +185,8 @@ struct JetFinderV0Task { o2::framework::expressions::Filter collisionFilter = (nabs(o2::aod::jcollision::posZ) < vertexZCut && o2::aod::jcollision::centFT0M >= centralityMin && o2::aod::jcollision::centFT0M < centralityMax && o2::aod::jcollision::trackOccupancyInTimeRange <= trackOccupancyInTimeRangeMax); o2::framework::expressions::Filter mcCollisionFilter = (nabs(o2::aod::jmccollision::posZ) < vertexZCut); - o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax); - o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax); + o2::framework::expressions::Filter trackCuts = (o2::aod::jtrack::pt >= trackPtMin && o2::aod::jtrack::pt < trackPtMax && o2::aod::jtrack::eta >= trackEtaMin && o2::aod::jtrack::eta <= trackEtaMax && o2::aod::jtrack::phi >= trackPhiMin && o2::aod::jtrack::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jtrack::phi <= phiExclusionMin || o2::aod::jtrack::phi >= phiExclusionMax))); + o2::framework::expressions::Filter partCuts = (o2::aod::jmcparticle::pt >= trackPtMin && o2::aod::jmcparticle::pt < trackPtMax && o2::aod::jmcparticle::eta >= trackEtaMin && o2::aod::jmcparticle::eta <= trackEtaMax && o2::aod::jmcparticle::phi >= trackPhiMin && o2::aod::jmcparticle::phi <= trackPhiMax && (phiExclusionMin < -998.0 || (o2::aod::jmcparticle::phi <= phiExclusionMin || o2::aod::jmcparticle::phi >= phiExclusionMax))); // function that generalically processes Data and reco level events template @@ -198,7 +209,7 @@ struct JetFinderV0Task { } } */ - jetfindingutilities::analyseTracksMultipleCandidates(inputParticles, tracks, trackSelection, candidates); + jetfindingutilities::analyseTracksMultipleCandidates(inputParticles, tracks, trackSelection, isEmbedding, candidates); jetfindingutilities::findJets(jetFinder, inputParticles, minJetPt, maxJetPt, jetRadius, jetAreaFractionMin, collision, jetsTableInput, constituentsTableInput, registry.get(HIST("hJet")), fillTHnSparse, saveJetsWithCandidatesOnly); } diff --git a/PWGJE/TableProducer/CMakeLists.txt b/PWGJE/TableProducer/CMakeLists.txt index 3e7510f56b9..c789aff9d49 100644 --- a/PWGJE/TableProducer/CMakeLists.txt +++ b/PWGJE/TableProducer/CMakeLists.txt @@ -18,6 +18,11 @@ o2physics_add_dpl_workflow(jet-deriveddata-producer PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(jet-deriveddata-embedding-producer + SOURCES derivedDataProducerEmbedding.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-deriveddata-trigger-producer SOURCES derivedDataTriggerProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore @@ -25,16 +30,19 @@ o2physics_add_dpl_workflow(jet-deriveddata-trigger-producer o2physics_add_dpl_workflow(jet-deriveddata-selector SOURCES derivedDataSelector.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-deriveddata-writer SOURCES derivedDataWriter.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-luminosity-producer SOURCES luminosityProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -45,16 +53,19 @@ o2physics_add_dpl_workflow(jet-luminosity-calculator o2physics_add_dpl_workflow(jet-eventweight-mcd SOURCES jetEventWeightMCD.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-eventweight-mcp SOURCES jetEventWeightMCP.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mc-outlier-rejector SOURCES mcOutlierRejector.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -65,26 +76,37 @@ o2physics_add_dpl_workflow(jet-track-derived o2physics_add_dpl_workflow(jet-hf-definition SOURCES heavyFlavourDefinition.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-taggerhf SOURCES jetTaggerHF.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(estimator-rho SOURCES rhoEstimator.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(subtractor-eventwiseconstituent SOURCES eventwiseConstituentSubtractor.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-sv-reconstruction SOURCES secondaryVertexReconstruction.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(quark-gluon-jets-producer + SOURCES quarkGluonJetsProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -103,10 +125,23 @@ o2physics_add_dpl_workflow(emcal-matchedtracks-writer o2physics_add_dpl_workflow(emcal-cluster-hadronic-correction-task SOURCES emcalClusterHadronicCorrectionTask.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase O2::EMCALBase O2::EMCALReconstruction COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(slim-tables-producer SOURCES slimTablesProducer.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(berkeley-tree-producer + SOURCES berkeleyTreeProducer.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(diff-wake-tree-producer + SOURCES diffWakeTreeProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/CMakeLists.txt b/PWGJE/TableProducer/Matching/CMakeLists.txt index a5450dfb05e..3ea0bfb3d33 100644 --- a/PWGJE/TableProducer/Matching/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/CMakeLists.txt @@ -16,166 +16,205 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-matching-mc-ch SOURCES jetMatchingMCCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-full SOURCES jetMatchingMCFull.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-neutral SOURCES jetMatchingMCNeutral.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-d0-ch SOURCES jetMatchingMCD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dplus-ch SOURCES jetMatchingMCDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-ds-ch SOURCES jetMatchingMCDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dstar-ch SOURCES jetMatchingMCDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-lc-ch SOURCES jetMatchingMCLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-b0-ch SOURCES jetMatchingMCB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-bplus-ch SOURCES jetMatchingMCBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-xictoxipipi-ch SOURCES jetMatchingMCXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-dielectron-ch SOURCES jetMatchingMCDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-v0-ch SOURCES jetMatchingMCV0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-ch SOURCES jetMatchingMCSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-d0-ch SOURCES jetMatchingMCSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dplus-ch SOURCES jetMatchingMCSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-ds-ch SOURCES jetMatchingMCSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dstar-ch SOURCES jetMatchingMCSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-lc-ch SOURCES jetMatchingMCSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-b0-ch SOURCES jetMatchingMCSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-bplus-ch SOURCES jetMatchingMCSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-xictoxipipi-ch SOURCES jetMatchingMCSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mc-sub-dielectron-ch SOURCES jetMatchingMCSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-ch SOURCES jetMatchingSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-d0-ch SOURCES jetMatchingSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dplus-ch SOURCES jetMatchingSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-ds-ch SOURCES jetMatchingSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dstar-ch SOURCES jetMatchingSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-lc-ch SOURCES jetMatchingSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-bplus-ch SOURCES jetMatchingSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-b0-ch SOURCES jetMatchingSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-xictoxipipi-ch SOURCES jetMatchingSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-sub-dielectron-ch SOURCES jetMatchingSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(jet-matching-mcd-sub-ch + SOURCES jetMatchingMCDSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt b/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt index 52a7a0377c9..1b754ba9fb5 100644 --- a/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/Duplicates/CMakeLists.txt @@ -13,16 +13,19 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-matching-data-ch-1 SOURCES jetMatchingDuplicatesChargedData1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mcd-ch-1 SOURCES jetMatchingDuplicatesChargedMCD1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-mcp-ch-1 SOURCES jetMatchingDuplicatesChargedMCP1.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt b/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt index 238e84ddc91..8d15ef727c8 100644 --- a/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt +++ b/PWGJE/TableProducer/Matching/Substructure/CMakeLists.txt @@ -14,101 +14,121 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-substructure-matching-mc-ch SOURCES jetSubstructureMatchingMCCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-d0-ch SOURCES jetSubstructureMatchingMCD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dplus-ch SOURCES jetSubstructureMatchingMCDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-ds-ch SOURCES jetSubstructureMatchingMCDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dstar-ch SOURCES jetSubstructureMatchingMCDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-lc-ch SOURCES jetSubstructureMatchingMCLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-b0-ch SOURCES jetSubstructureMatchingMCB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-bplus-ch SOURCES jetSubstructureMatchingMCBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-xictoxipipi-ch SOURCES jetSubstructureMatchingMCXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-mc-dielectron-ch SOURCES jetSubstructureMatchingMCDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-ch SOURCES jetSubstructureMatchingSubCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-d0-ch SOURCES jetSubstructureMatchingSubD0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dplus-ch SOURCES jetSubstructureMatchingSubDplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-ds-ch SOURCES jetSubstructureMatchingSubDsCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dstar-ch SOURCES jetSubstructureMatchingSubDstarCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-lc-ch SOURCES jetSubstructureMatchingSubLcCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-b0-ch SOURCES jetSubstructureMatchingSubB0Charged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-bplus-ch SOURCES jetSubstructureMatchingSubBplusCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-xictoxipipi-ch SOURCES jetSubstructureMatchingSubXicToXiPiPiCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-matching-sub-dielectron-ch SOURCES jetSubstructureMatchingSubDielectronCharged.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2::FrameworkPhysicsSupport COMPONENT_NAME Analysis) diff --git a/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx b/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx new file mode 100644 index 00000000000..3a38398b97d --- /dev/null +++ b/PWGJE/TableProducer/Matching/jetMatchingMCDSubCharged.cxx @@ -0,0 +1,45 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet matching mcd subtracted charged task +// +/// \author Nima Zardoshti + +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" +#include "PWGJE/TableProducer/Matching/jetMatchingSub.h" + +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using ChargedJetMatchingSub = JetMatchingSub, + soa::Join, + aod::ChargedMCDetectorLevelJetsMatchedToChargedEventWiseSubtractedJets, + aod::ChargedEventWiseSubtractedJetsMatchedToChargedMCDetectorLevelJets, + aod::JTrackSubs, + aod::JDummys>; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + std::vector tasks; + tasks.emplace_back(adaptAnalysisTask(cfgc, SetDefaultProcesses{}, TaskName{"jet-matching-mcd-sub-ch"})); + return WorkflowSpec{tasks}; +} diff --git a/PWGJE/TableProducer/berkeleyTreeProducer.cxx b/PWGJE/TableProducer/berkeleyTreeProducer.cxx new file mode 100644 index 00000000000..669ba295b7f --- /dev/null +++ b/PWGJE/TableProducer/berkeleyTreeProducer.cxx @@ -0,0 +1,211 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file berkeleyTreeProducer.cxx +/// \brief Task to save MC events into the BerkeleyTree format +/// \author Vassu Doomra +/// \author Tucker Hwang + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ +namespace berkeleytree +{ +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); +DECLARE_SOA_COLUMN(VtxZ, vtxZ, float); +DECLARE_SOA_COLUMN(Weight, weight, float); +DECLARE_SOA_COLUMN(PtHat, ptHat, float); +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); +DECLARE_SOA_COLUMN(Occupancy, occupancy, int); +DECLARE_SOA_COLUMN(EventSel, eventSel, uint16_t); +DECLARE_SOA_BITMAP_COLUMN(Rct, rct, 32); + +DECLARE_SOA_COLUMN(DetPt, detPt, std::vector); +DECLARE_SOA_COLUMN(DetEta, detEta, std::vector); +DECLARE_SOA_COLUMN(DetPhi, detPhi, std::vector); +DECLARE_SOA_COLUMN(DetTrackSel, detTrackSel, std::vector); +DECLARE_SOA_COLUMN(DetMcId, detMcId, std::vector); + +DECLARE_SOA_COLUMN(GenPt, genPt, std::vector); +DECLARE_SOA_COLUMN(GenEta, genEta, std::vector); +DECLARE_SOA_COLUMN(GenPhi, genPhi, std::vector); +DECLARE_SOA_COLUMN(GenE, genE, std::vector); +DECLARE_SOA_COLUMN(GenCharge, genCharge, std::vector); +DECLARE_SOA_COLUMN(GenMcId, genMcId, std::vector); +DECLARE_SOA_COLUMN(PdgId, pdgId, std::vector); +} // namespace berkeleytree + +DECLARE_SOA_TABLE(BerkeleyTree, "AOD", "BERKELEYTREE", + berkeleytree::RunNumber, + berkeleytree::VtxZ, + berkeleytree::Weight, + berkeleytree::PtHat, + berkeleytree::Multiplicity, + berkeleytree::EventSel, + berkeleytree::Occupancy, + berkeleytree::Rct, + berkeleytree::DetPt, + berkeleytree::DetEta, + berkeleytree::DetPhi, + berkeleytree::DetTrackSel, + berkeleytree::DetMcId, + berkeleytree::GenPt, + berkeleytree::GenEta, + berkeleytree::GenPhi, + berkeleytree::GenE, + berkeleytree::GenCharge, + berkeleytree::GenMcId, + berkeleytree::PdgId); +} // namespace o2::aod + +struct BerkeleyTreeProducer { + Service pdg; + + Configurable vertexZCut{"vertexZCut", 10.0f, "maximum Z vertex"}; + Configurable etaMaxDet{"etaMaxDet", 0.9f, "maximum eta for det-level tracks"}; + Configurable etaMaxGen{"etaMaxGen", 0.9f, "maximum eta for gen-level particles"}; + Configurable ptMinDet{"ptMinDet", 0.15f, "minimum pt (GeV) for det-level tracks"}; + Configurable ptMinGen{"ptMinGen", 0.15f, "minimum pt (GeV) for gen-level particles"}; + + Configurable eventSelections{"eventSelections", "sel8", ""}; + Configurable trackSelections{"trackSelections", "globalTracks", ""}; + Configurable skipMBGapEvents{"skipMBGapEvents", true, "skip MB gap events"}; + + Produces tree; + + std::vector eventSelectionBits; + int trackSelection = -1; + + Preslice particlesPerMcCollision = aod::jmcparticle::mcCollisionId; + + bool isChargedParticle(int code) + { + const float chargeUnit = 3.; + auto p = pdg->GetParticle(code); + auto charge = 0.; + if (p != nullptr) { + charge = p->Charge(); + } + return std::abs(charge) >= chargeUnit; + } + + int getCharge(int code) + { + auto p = pdg->GetParticle(code); + if (!p) { + LOG(fatal) << "Cannot find particle with PDG code " << code; + return 0; + } + auto charge = p->Charge() / 3.0; + return std::lround(charge); + } + + void init(InitContext const&) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + } + + using JetParticlesWithOriginal = soa::Join; + void processMCJJ(aod::JetCollisionsMCD::iterator const& collision, aod::JetTracksMCD const& tracks, JetParticlesWithOriginal const& mcParticles, aod::JetMcCollisions const&, aod::JBCs const&) + { + // do not do any RCT selections, will be done on analysis level + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, false, "", false, false)) + return; + if (std::abs(collision.posZ()) > vertexZCut) + return; + + float weight = collision.has_mcCollision() ? collision.mcCollision().weight() : 1.f; + float pthat = collision.has_mcCollision() ? collision.mcCollision().ptHard() : 1.f; + int runNumber = collision.bc_as().runNumber(); + + std::vector detPt, detEta, detPhi; + std::vector detTrackSel; + std::vector detMcId; // track.mcParticleId() returns int/int32_t, casting to int64_t + + std::vector genPt, genEta, genPhi, genE; + std::vector genCharge, pdgId; + std::vector genMcId; // mcParticle.globalIndex() returns long/int64_t + + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + continue; + + if (std::fabs(track.eta()) > etaMaxDet) + continue; + if (track.pt() < ptMinDet) + continue; + + detPt.push_back(track.pt()); + detEta.push_back(track.eta()); + detPhi.push_back(track.phi()); + detTrackSel.push_back(track.trackSel()); + if (track.has_mcParticle()) + detMcId.push_back(track.mcParticleId()); + else + detMcId.push_back(-1); + } + + int mcId = collision.has_mcCollision() ? collision.mcCollisionId() : -1; + + if (mcId >= 0) { + auto particles = mcParticles.sliceBy(particlesPerMcCollision, mcId); + + for (auto const& p : particles) { + if (!p.isPhysicalPrimary()) + continue; + if (std::fabs(p.eta()) > etaMaxGen) + continue; + if (p.pt() < ptMinGen) + continue; + if (!isChargedParticle(p.pdgCode())) + continue; + + genPt.push_back(p.pt()); + genEta.push_back(p.eta()); + genPhi.push_back(p.phi()); + genE.push_back(p.e()); + genCharge.push_back(getCharge(p.pdgCode())); + genMcId.push_back(p.globalIndex()); + pdgId.push_back(p.pdgCode()); + } + } + + tree(runNumber, collision.posZ(), weight, pthat, collision.multFT0C(), collision.eventSel(), collision.trackOccupancyInTimeRange(), collision.rct_raw(), detPt, detEta, detPhi, detTrackSel, detMcId, genPt, genEta, genPhi, genE, genCharge, genMcId, pdgId); + } + + PROCESS_SWITCH(BerkeleyTreeProducer, processMCJJ, "MC processing for JJ simulations", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/TableProducer/derivedDataProducer.cxx b/PWGJE/TableProducer/derivedDataProducer.cxx index 6a482f981df..f4cac2e7ac4 100644 --- a/PWGJE/TableProducer/derivedDataProducer.cxx +++ b/PWGJE/TableProducer/derivedDataProducer.cxx @@ -164,7 +164,6 @@ struct JetDerivedDataProducerTask { Configurable includeTriggers{"includeTriggers", false, "fill the collision information with software trigger decisions"}; Configurable includeHadronicRate{"includeHadronicRate", true, "fill the collision information with the hadronic rate"}; - Configurable includeUpcs{"includeUpcs", true, "include option to identify UPC events"}; Configurable v0ChargedDecaysOnly{"v0ChargedDecaysOnly", true, "store V0s (at particle-level) only if they decay to charged particles"}; Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; @@ -221,11 +220,13 @@ struct JetDerivedDataProducerTask { } } - upcCuts.SetNDtcoll(config.upcBCRangeTimeWindow); - upcCuts.SetMinNBCs(config.upcMinNBCs); - upcCuts.SetNTracks(config.upcMinNTracks, config.upcMaxNTracks); - upcCuts.SetMaxFITtime(config.upcMaxFITTime); - upcCuts.SetFITAmpLimits({config.upcMaxFV0AAmplitude, config.upcMaxFT0AAmplitude, config.upcMaxFT0CAmplitude, config.upcMaxFDDAAmplitude, config.upcMaxFDDCAmplitude}); + if (doprocessCollisionsUPC) { + upcCuts.SetNDtcoll(config.upcBCRangeTimeWindow); + upcCuts.SetMinNBCs(config.upcMinNBCs); + upcCuts.SetNTracks(config.upcMinNTracks, config.upcMaxNTracks); + upcCuts.SetMaxFITtime(config.upcMaxFITTime); + upcCuts.SetFITAmpLimits({config.upcMaxFV0AAmplitude, config.upcMaxFT0AAmplitude, config.upcMaxFT0CAmplitude, config.upcMaxFDDAAmplitude, config.upcMaxFDDCAmplitude}); + } if (config.applyTrackingEfficiency) { trackingEfficiencyRandomNumber.SetSeed(0); @@ -297,7 +298,7 @@ struct JetDerivedDataProducerTask { } PROCESS_SWITCH(JetDerivedDataProducerTask, processBunchCrossingsWithoutSels, "produces derived bunch crossing table with bunch crossing selections", false); - void processCollisions(soa::Join::iterator const& collision, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&) + void processCollisions(soa::Join::iterator const& collision, soa::Join const&) { auto bc = collision.bc_as>(); if (config.includeHadronicRate) { @@ -313,32 +314,63 @@ struct JetDerivedDataProducerTask { } int upcGapResult = o2::aod::sgselector::NoGap; - if (config.includeUpcs) { - amplitudesFV0.clear(); - amplitudesFT0A.clear(); - amplitudesFT0C.clear(); - amplitudesFDDA.clear(); - amplitudesFDDC.clear(); - if (collision.has_foundBC()) { - auto const upcBC = collision.foundBC_as>(); - auto const upcBCRange = udhelpers::compatibleBCs(collision, upcCuts.NDtcoll(), bcs, upcCuts.minNBCs()); - auto const upcResult = upcSelector.IsSelected(upcCuts, collision, upcBCRange, upcBC, &litudesFV0, &litudesFT0A, &litudesFT0C, &litudesFDDA, &litudesFDDC); - upcGapResult = upcResult.value; - if (upcGapResult != o2::aod::sgselector::SingleGapA && upcGapResult != o2::aod::sgselector::SingleGapC && upcGapResult != o2::aod::sgselector::DoubleGap) { - amplitudesFV0.clear(); - amplitudesFT0A.clear(); - amplitudesFT0C.clear(); - amplitudesFDDA.clear(); - amplitudesFDDC.clear(); - } + + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataProducerTask, processCollisions, "produces derived collision tables", true); + + void processCollisionsUPC(soa::Join::iterator const& collision, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&) + { + auto bc = collision.bc_as>(); + if (config.includeHadronicRate) { + if (runNumber != bc.runNumber()) { + runNumber = bc.runNumber(); + hadronicRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 0.001; + } + } + uint64_t triggerBit = 0; + if (config.includeTriggers) { + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + + int upcGapResult = o2::aod::sgselector::NoGap; + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + if (collision.has_foundBC()) { + auto const upcBC = collision.foundBC_as>(); + auto const upcBCRange = udhelpers::compatibleBCs(collision, upcCuts.NDtcoll(), bcs, upcCuts.minNBCs()); + auto const upcResult = upcSelector.IsSelected(upcCuts, collision, upcBCRange, upcBC, &litudesFV0, &litudesFT0A, &litudesFT0C, &litudesFDDA, &litudesFDDC); + upcGapResult = upcResult.value; + if (upcGapResult != o2::aod::sgselector::SingleGapA && upcGapResult != o2::aod::sgselector::SingleGapC && upcGapResult != o2::aod::sgselector::DoubleGap) { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); } - products.jCollisionUPCsTable(amplitudesFV0, amplitudesFT0A, amplitudesFT0C, amplitudesFDDA, amplitudesFDDC); } + float energyCommonZNA = -1.0; + float energyCommonZNC = -1.0; + float timeZNA = -1.0; + float timeZNC = -1.0; + if (bc.has_zdc()) { + auto const& zdc = bc.zdc_as(); + energyCommonZNA = zdc.energyCommonZNA(); + energyCommonZNC = zdc.energyCommonZNC(); + timeZNA = zdc.timeZNA(); + timeZNC = zdc.timeZNC(); + } + products.jCollisionUPCsTable(amplitudesFV0, amplitudesFT0A, amplitudesFT0C, amplitudesFDDA, amplitudesFDDC, energyCommonZNA, energyCommonZNC, timeZNA, timeZNC); products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed products.jCollisionsParentIndexTable(collision.globalIndex()); } - PROCESS_SWITCH(JetDerivedDataProducerTask, processCollisions, "produces derived collision tables", true); + PROCESS_SWITCH(JetDerivedDataProducerTask, processCollisionsUPC, "produces derived collision tables with UPC information", false); void processCollisionsWithoutCentralityAndMultiplicity(soa::Join::iterator const& collision, soa::Join const&) { @@ -582,7 +614,7 @@ struct JetDerivedDataProducerTask { auto collisionTrackIndices = assocCollisions.sliceBy(preslices.perCollisionTrackIndices, collision.globalIndex()); for (auto const& collisionTrackIndex : collisionTrackIndices) { auto track = collisionTrackIndex.track_as>(); - if (track.collisionId() == collision.globalIndex() && track.has_mcParticle()) { + if (track.has_mcParticle()) { products.jMcTracksLabelTable(track.mcParticleId()); } else { products.jMcTracksLabelTable(-1); @@ -620,7 +652,7 @@ struct JetDerivedDataProducerTask { void processClusters(aod::Collision const&, aod::EMCALClusters const& clusters, aod::EMCALClusterCells const& cells, aod::Calos const&, aod::EMCALMatchedTracks const& matchedTracks, soa::Join const&) { - for (auto cluster : clusters) { + for (const auto& cluster : clusters) { auto const clusterCells = cells.sliceBy(preslices.perClusterCells, cluster.globalIndex()); diff --git a/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx new file mode 100644 index 00000000000..e093d61b686 --- /dev/null +++ b/PWGJE/TableProducer/derivedDataProducerEmbedding.cxx @@ -0,0 +1,1252 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// task to produce a self contained data format for jet analyses from the full AO2D +// +/// \author Nima Zardoshti + +// #include "PWGJE/Core/JetDQUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +// #include "PWGJE/Core/JetV0Utilities.h" +#include "PWGJE/DataModel/EMCALClusters.h" +#include "PWGJE/DataModel/EMCALMatchedCollisions.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetReducedDataDQ.h" +#include "PWGJE/DataModel/JetReducedDataHF.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" +// +// #include "PWGDQ/DataModel/ReducedInfoTables.h" +// #include "PWGHF/DataModel/DerivedTables.h" +#include "PWGHF/Utils/utilsBfieldCCDB.h" +// #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGUD/Core/SGCutParHolder.h" +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/UDHelpers.h" + +#include "Common/CCDB/ctpRateFetcher.h" +// #include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ +// O2ORIGIN("EMB"); +// template +// using McCollisionsFrom = McCollisions_001From; +// template +// using CollisionsFrom = Collisions_001From; +// template +// using TracksExtraFrom = TracksExtra_002From; +// template +// using McParticlesFrom = McParticles_001From; +// template +// using MultsExtraMCFrom = MultMCExtras_001From; +} // namespace o2::aod + +struct JetDerivedDataEmbeddingProducerTask { + struct : ProducesGroup { + Produces bcCountsTable; + Produces collisionCountsTable; + Produces jDummysTable; + Produces jBCsTable; + Produces jBCParentIndexTable; + Produces jCollisionsTable; + Produces jCollisionUPCsTable; + Produces jCollisionMcInfosTable; + Produces jCollisionsParentIndexTable; + Produces jCollisionsEMCalLabelTable; + Produces jMcCollisionsLabelTable; + Produces jMcCollisionsTable; + Produces jMcCollisionsParentIndexTable; + Produces jTracksTable; + Produces jTracksExtraTable; + Produces jTracksEMCalTable; + Produces jTracksParentIndexTable; + Produces jMcTracksLabelTable; + Produces jMcParticlesTable; + Produces jParticlesParentIndexTable; + Produces jClustersTable; + Produces jClustersParentIndexTable; + Produces jClustersMatchedTracksTable; + Produces jMcClustersLabelTable; + Produces jD0CollisionIdsTable; + Produces jD0McCollisionIdsTable; + Produces jD0IdsTable; + Produces jD0ParticleIdsTable; + Produces jDplusCollisionIdsTable; + Produces jDplusMcCollisionIdsTable; + Produces jDplusIdsTable; + Produces jDplusParticleIdsTable; + Produces jDsCollisionIdsTable; + Produces jDsMcCollisionIdsTable; + Produces jDsIdsTable; + Produces jDsParticleIdsTable; + Produces jDstarCollisionIdsTable; + Produces jDstarMcCollisionIdsTable; + Produces jDstarIdsTable; + Produces jDstarParticleIdsTable; + Produces jLcCollisionIdsTable; + Produces jLcMcCollisionIdsTable; + Produces jLcIdsTable; + Produces jLcParticleIdsTable; + Produces jB0CollisionIdsTable; + Produces jB0McCollisionIdsTable; + Produces jB0IdsTable; + Produces jB0ParticleIdsTable; + Produces jBplusCollisionIdsTable; + Produces jBplusMcCollisionIdsTable; + Produces jBplusIdsTable; + Produces jBplusParticleIdsTable; + Produces jXicToXiPiPiCollisionIdsTable; + Produces jXicToXiPiPiMcCollisionIdsTable; + Produces jXicToXiPiPiIdsTable; + Produces jXicToXiPiPiParticleIdsTable; + Produces jV0IdsTable; + Produces jV0McCollisionsTable; + Produces jV0McCollisionIdsTable; + Produces jV0McsTable; + Produces jV0McIdsTable; + Produces jDielectronCollisionIdsTable; + Produces jDielectronIdsTable; + Produces jDielectronMcCollisionsTable; + Produces jDielectronMcCollisionIdsTable; + Produces JDielectronMcRCollDummysTable; + Produces jDielectronMcsTable; + Produces jDielectronMcIdsTable; + + Produces>> jMcCollisionsLabelMCDTable; + Produces>> jCollisionMcInfosMCDTable; + } products; + + struct : ConfigurableGroup { + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable ccdbPathLut{"ccdbPathLut", "GLO/Param/MatLUT", "Path for LUT parametrization"}; + Configurable ccdbPathGrp{"ccdbPathGrp", "GLO/GRP/GRP", "Path of the grp file (Run 2)"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object (Run 3)"}; + Configurable dcaZMax{"dcaZMax", 0.2, "maximum DCAZ selection for tracks - only applied for reassociation"}; + Configurable upcBCRangeTimeWindow{"upcBCRangeTimeWindow", 1000, "time range in ns for bunch crossing range checking in upc gap determination"}; + Configurable upcMinNBCs{"upcMinNBCs", 7, "minuimum number of bunch crossing to check in upc gap determination"}; + Configurable upcMinNTracks{"upcMinNTracks", 0, "minuimum number of tracks in upc event"}; + Configurable upcMaxNTracks{"upcMaxNTracks", 100, "maximum number of tracks in upc event"}; + Configurable upcMaxFITTime{"upcMaxFITTime", 4.0, "maximum fit time in upc event"}; + Configurable upcMaxFV0AAmplitude{"upcMaxFV0AAmplitude", 100.0, "maximum amplitude for FV0A in upc event"}; + Configurable upcMaxFT0AAmplitude{"upcMaxFT0AAmplitude", 100.0, "maximum amplitude for FT0A in upc event"}; + Configurable upcMaxFT0CAmplitude{"upcMaxFT0CAmplitude", 50.0, "maximum amplitude for FT0C in upc event"}; + Configurable upcMaxFDDAAmplitude{"upcMaxFDDAAmplitude", -1.0, "maximum amplitude for FDDA in upc event"}; + Configurable upcMaxFDDCAmplitude{"upcMaxFDDCAmplitude", -1.0, "maximum amplitude for FDDC in upc event"}; + + Configurable includeTriggers{"includeTriggers", false, "fill the collision information with software trigger decisions"}; + Configurable includeHadronicRate{"includeHadronicRate", true, "fill the collision information with the hadronic rate"}; + Configurable v0ChargedDecaysOnly{"v0ChargedDecaysOnly", true, "store V0s (at particle-level) only if they decay to charged particles"}; + Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; + + o2::framework::Configurable applyTrackingEfficiency{"applyTrackingEfficiency", {false}, "configurable to decide whether to apply artificial tracking efficiency (discarding tracks) in jet finding"}; + o2::framework::Configurable> trackingEfficiencyPtBinning{"trackingEfficiencyPtBinning", {0., 10, 999.}, "pt binning of tracking efficiency array if applyTrackingEfficiency is true"}; + o2::framework::Configurable> trackingEfficiency{"trackingEfficiency", {1.0, 1.0}, "tracking efficiency array applied to jet finding if applyTrackingEfficiency is true"}; + } config; + + struct : PresliceGroup { + Preslice perClusterCells = aod::emcalclustercell::emcalclusterId; + Preslice perClusterTracks = aod::emcalclustercell::emcalclusterId; + Preslice perCollisionTrackIndices = aod::track_association::collisionId; + Preslice perBCCollisions = aod::collision::bcId; + Preslice>> permcdBCCollisions = aod::collision::bcId; + Preslice> perCollisionTracks = aod::track::collisionId; + Preslice>, aod::TracksExtraFrom>, aod::TracksCovFrom>, aod::TracksDCAFrom>, aod::TracksDCACovFrom>, aod::TrackSelectionFrom>, aod::TrackSelectionExtensionFrom>, aod::McTrackLabelsFrom>>> perCollisionTracksMCD = aod::track::collisionId; + } preslices; + + std::map, int32_t> trackCollisionMapping; + std::map, int32_t> trackCollisionMCDMapping; + std::vector collisionMapping; + std::vector collisionTomcdCollisionMapping; + std::vector mcdcollisionToCollisionMapping; + Service ccdb; + o2::base::MatLayerCylSet* lut; + o2::base::Propagator::MatCorrType noMatCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; + Service pdgDatabase; + Zorro triggerDecider; + + SGCutParHolder upcCuts; + SGSelector upcSelector; + std::vector amplitudesFV0; + std::vector amplitudesFT0A; + std::vector amplitudesFT0C; + std::vector amplitudesFDDA; + std::vector amplitudesFDDC; + + std::vector trackMCSelection; + + std::vector bcSelMapping; + std::vector bcRctMapping; + + ctpRateFetcher rateFetcher; + int runNumber; + float hadronicRate; + bool withCollisionAssociator; + TRandom3 trackingEfficiencyRandomNumber; + + void init(InitContext const&) + { + hadronicRate = -1.0; + if (doprocessTracksWithCollisionAssociator || config.includeHadronicRate || config.includeTriggers) { + ccdb->setURL(config.ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + runNumber = 0; + if (doprocessTracksWithCollisionAssociator) { + withCollisionAssociator = true; + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(config.ccdbPathLut)); + } else { + withCollisionAssociator = false; + } + } + + upcCuts.SetNDtcoll(config.upcBCRangeTimeWindow); + upcCuts.SetMinNBCs(config.upcMinNBCs); + upcCuts.SetNTracks(config.upcMinNTracks, config.upcMaxNTracks); + upcCuts.SetMaxFITtime(config.upcMaxFITTime); + upcCuts.SetFITAmpLimits({config.upcMaxFV0AAmplitude, config.upcMaxFT0AAmplitude, config.upcMaxFT0CAmplitude, config.upcMaxFDDAAmplitude, config.upcMaxFDDCAmplitude}); + + if (config.applyTrackingEfficiency) { + trackingEfficiencyRandomNumber.SetSeed(0); + if (config.trackingEfficiencyPtBinning->size() < 2) { + LOGP(fatal, "jetFinder workflow: trackingEfficiencyPtBinning configurable should have at least two bin edges"); + } + if (config.trackingEfficiency->size() + 1 != config.trackingEfficiencyPtBinning->size()) { + LOGP(fatal, "jetFinder workflow: trackingEfficiency configurable should have exactly one less entry than the number of bin edges set in trackingEfficiencyPtBinning configurable"); + } + } + } + + void processClearMaps(aod::Collisions const& collisions, aod::CollisionsFrom> const& mcdCollisions, aod::BCs const& bcs, aod::BCsFrom> const& mcdBCs, aod::TracksFrom> const& mcTracks) + { + trackCollisionMapping.clear(); + trackCollisionMCDMapping.clear(); + trackMCSelection.clear(); + trackMCSelection.resize(mcTracks.size(), true); + + collisionTomcdCollisionMapping.clear(); + collisionTomcdCollisionMapping.resize(collisions.size(), -1); + for (const auto& collision : collisions) { + auto const& colisionBC = collision.bc_as(); + auto const& globalBC = colisionBC.globalBC(); + for (auto const& mcdBC : mcdBCs) { + if (mcdBC.globalBC() == globalBC) { + const auto bcCollisions = mcdCollisions.sliceBy(preslices.permcdBCCollisions, mcdBC.globalIndex()); + for (auto const& bcCollision : bcCollisions) { + collisionTomcdCollisionMapping[collision.globalIndex()] = bcCollision.globalIndex(); // only takes the first collision. If multiple collisions are associated to the same BC then it skips the next ones + break; + } + break; + } + } + } + + mcdcollisionToCollisionMapping.clear(); + mcdcollisionToCollisionMapping.resize(mcdCollisions.size(), -1); + for (const auto& mcdCollision : mcdCollisions) { + auto const& mcdCollisionBC = mcdCollision.bc_as>>(); + auto const& mcdGlobalBC = mcdCollisionBC.globalBC(); + for (auto const& bc : bcs) { + if (bc.globalBC() == mcdGlobalBC) { + const auto bcCollisions = collisions.sliceBy(preslices.perBCCollisions, bc.globalIndex()); + for (auto const& bcCollision : bcCollisions) { + mcdcollisionToCollisionMapping[mcdCollision.globalIndex()] = bcCollision.globalIndex(); + break; + } + break; + } + } + } + + if (config.applyTrackingEfficiency) { + std::vector trackingEfficiencyPtBinningVec = config.trackingEfficiencyPtBinning; + std::vector trackingEfficiencyVec = config.trackingEfficiency; + for (const auto& mcTrack : mcTracks) { + auto iter = std::upper_bound(trackingEfficiencyPtBinningVec.begin(), trackingEfficiencyPtBinningVec.end(), mcTrack.pt()); + if (iter != trackingEfficiencyPtBinningVec.begin() && iter != trackingEfficiencyPtBinningVec.end()) { + std::size_t index = std::distance(trackingEfficiencyPtBinningVec.begin(), iter) - 1; + if (trackingEfficiencyRandomNumber.Rndm() > trackingEfficiencyVec[index]) { + trackMCSelection[mcTrack.globalIndex()] = false; + } + } + } + } + if (!doprocessEMCalCollisionLabels) { + for (int i = 0; i < collisions.size(); i++) { // check if this is the correct collision loop + products.jCollisionsEMCalLabelTable(false, false); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processClearMaps, "clears all maps", true); + + void processBunchCrossings(soa::Join const& bcs, aod::BCsFrom> const& mcBCs) + { + bcSelMapping.clear(); + bcSelMapping.resize(mcBCs.size(), ~uint16_t{0}); + bcRctMapping.clear(); + bcRctMapping.resize(mcBCs.size(), ~uint32_t{0}); + for (const auto& bc : bcs) { + products.jBCsTable(bc.runNumber(), bc.globalBC(), bc.triggerMask(), bc.timestamp(), bc.alias_raw(), bc.selection_raw(), bc.rct_raw()); + products.jBCParentIndexTable(bc.globalIndex()); + for (const auto& mcBC : mcBCs) { + if (mcBC.globalBC() == bc.globalBC()) { + bcSelMapping[mcBC.globalIndex()] = jetderiveddatautilities::setMCEventSelectionBit(bc); // note here we are setting the selection on the bc for the MC to the one in data. Is this correct? + bcRctMapping[mcBC.globalIndex()] = bc.rct_raw(); + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBunchCrossings, "produces derived bunch crossing table", true); + + void processBunchCrossingsWithoutSels(soa::Join const& bcs, aod::BCsFrom> const& mcBCs) + { + bcSelMapping.clear(); + bcSelMapping.resize(mcBCs.size(), ~uint16_t{0}); + bcRctMapping.clear(); + bcRctMapping.resize(mcBCs.size(), ~uint32_t{0}); + for (const auto& bc : bcs) { + products.jBCsTable(bc.runNumber(), bc.globalBC(), bc.triggerMask(), bc.timestamp(), ~uint32_t{0}, ~uint64_t{0}, ~uint32_t{0}); + products.jBCParentIndexTable(bc.globalIndex()); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBunchCrossingsWithoutSels, "produces derived bunch crossing table with bunch crossing selections", false); + + void processCollisions(soa::Join::iterator const& collision, soa::Join const&) + { + // shall we skip collisions without an associated detector level collision to embed? + auto bc = collision.bc_as>(); + if (config.includeHadronicRate) { + if (runNumber != bc.runNumber()) { + runNumber = bc.runNumber(); + hadronicRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 0.001; + } + } + uint64_t triggerBit = 0; + if (config.includeTriggers) { + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + + int upcGapResult = o2::aod::sgselector::NoGap; + + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisions, "produces derived collision tables", true); + + void processCollisionsUPC(soa::Join::iterator const& collision, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&) + { + // shall we skip collisions without an associated detector level collision to embed? + auto bc = collision.bc_as>(); + if (config.includeHadronicRate) { + if (runNumber != bc.runNumber()) { + runNumber = bc.runNumber(); + hadronicRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), runNumber, "ZNC hadronic") * 0.001; + } + } + uint64_t triggerBit = 0; + if (config.includeTriggers) { + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + + int upcGapResult = o2::aod::sgselector::NoGap; + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + if (collision.has_foundBC()) { + auto const upcBC = collision.foundBC_as>(); + auto const upcBCRange = udhelpers::compatibleBCs(collision, upcCuts.NDtcoll(), bcs, upcCuts.minNBCs()); + auto const upcResult = upcSelector.IsSelected(upcCuts, collision, upcBCRange, upcBC, &litudesFV0, &litudesFT0A, &litudesFT0C, &litudesFDDA, &litudesFDDC); + upcGapResult = upcResult.value; + if (upcGapResult != o2::aod::sgselector::SingleGapA && upcGapResult != o2::aod::sgselector::SingleGapC && upcGapResult != o2::aod::sgselector::DoubleGap) { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + } + } + float energyCommonZNA = -1.0; + float energyCommonZNC = -1.0; + float timeZNA = -1.0; + float timeZNC = -1.0; + if (bc.has_zdc()) { + auto const& zdc = bc.zdc_as(); + energyCommonZNA = zdc.energyCommonZNA(); + energyCommonZNC = zdc.energyCommonZNC(); + timeZNA = zdc.timeZNA(); + timeZNC = zdc.timeZNC(); + } + products.jCollisionUPCsTable(amplitudesFV0, amplitudesFT0A, amplitudesFT0C, amplitudesFDDA, amplitudesFDDC, energyCommonZNA, energyCommonZNC, timeZNA, timeZNC); + + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), -1.0, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), hadronicRate, collision.trackOccupancyInTimeRange(), collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision, upcGapResult), collision.rct_raw(), triggerBit); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsUPC, "produces derived collision tables with UPC information", false); + + void processCollisionsWithoutCentralityAndMultiplicity(soa::Join::iterator const& collision, soa::Join const&) + { + uint64_t triggerBit = 0; + if (config.includeTriggers) { + auto bc = collision.bc_as>(); + triggerDecider.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), jetderiveddatautilities::JTriggerMasks); + triggerBit = jetderiveddatautilities::setTriggerSelectionBit(triggerDecider.getTriggerOfInterestResults(bc.globalBC())); + } + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1, collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision), collision.rct_raw(), triggerBit); + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsWithoutCentralityAndMultiplicity, "produces derived collision tables without centrality or multiplicity", false); + + void processCollisionsRun2(soa::Join::iterator const& collision) + { + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), -1.0, -1.0, -1.0, -1.0, -1.0, collision.centRun2V0A(), collision.centRun2V0M(), -1.0, -1.0, -1.0, -1.0, -1.0, -1, collision.alias_raw(), jetderiveddatautilities::setEventSelectionBit(collision), collision.rct_raw(), 0); // note change multFT0C to multFT0M when problems with multFT0A are fixed + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsRun2, "produces derived collision tables for Run 2 data", false); + + void processCollisionsALICE3(aod::Collision const& collision) + { + products.jCollisionsTable(collision.bcId(), collision.posX(), collision.posY(), collision.posZ(), -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1, -1.0, 0, 0, 0); + products.jCollisionsParentIndexTable(collision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processCollisionsALICE3, "produces derived collision tables for ALICE 3 simulations", false); + + void processEMCalCollisionLabels(aod::EMCALMatchedCollision const& collision) + { + products.jCollisionsEMCalLabelTable(collision.ambiguous(), collision.isemcreadout()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processEMCalCollisionLabels, "produces derived collision labels for EMCal", false); + + void processMcCollisionLabels(soa::Join>, aod::McCollisionLabelsFrom>> const& mcdCollisions, aod::Collisions const& collisions, aod::McCollisionsFrom> const&) + { + for (auto const& mcdCollision : mcdCollisions) { + products.jMcCollisionsLabelMCDTable(mcdCollision.mcCollisionId()); // collision.mcCollisionId() returns -1 if collision has no associated mcCollision + if (mcdCollision.has_mcCollision()) { + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + products.jCollisionMcInfosMCDTable(mcCollision.weight(), mcCollision.getSubGeneratorId()); + } else { + products.jCollisionMcInfosMCDTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } + + for (auto const& collision : collisions) { + int32_t mcdCollisionId = collisionTomcdCollisionMapping[collision.globalIndex()]; + if (mcdCollisionId >= 0) { + auto const& mcdCollision = mcdCollisions.iteratorAt(mcdCollisionId); // is this correct????!!!!! + products.jMcCollisionsLabelTable(mcdCollision.mcCollisionId()); // collision.mcCollisionId() returns -1 if collision has no associated mcCollision + if (mcdCollision.has_mcCollision()) { + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + products.jCollisionMcInfosTable(mcCollision.weight(), mcCollision.getSubGeneratorId()); + } else { + products.jCollisionMcInfosTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } else { + products.jMcCollisionsLabelTable(-1); + products.jCollisionMcInfosTable(0.0, jetderiveddatautilities::JCollisionSubGeneratorId::none); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionLabels, "produces derived MC collision labels table", true); + + void processMcCollisions(soa::Join>, aod::HepMCXSectionsFrom>, aod::MultsExtraMCFrom>, aod::McCentFT0MsFrom>>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multMCFV0A(), mcCollision.multMCFT0A(), mcCollision.multMCFT0C(), mcCollision.centFT0M(), mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisions, "produces derived MC collision table", true); + + void processMcCollisionsWithoutCentralityAndMultiplicity(soa::Join>, aod::HepMCXSectionsFrom>>::iterator const& mcCollision) + { + + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), -1.0, -1.0, -1.0, -1.0, mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutCentralityAndMultiplicity, "produces derived MC collision table without centraility and multiplicity", false); + + void processMcCollisionsWithoutXsection(soa::Join>, aod::MultsExtraMCFrom>, aod::McCentFT0MsFrom>>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multMCFV0A(), mcCollision.multMCFT0A(), mcCollision.multMCFT0C(), mcCollision.centFT0M(), mcCollision.weight(), 1, 1, 1.0, 1.0, 999.0, selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutXsection, "produces derived MC collision table without cross section information", false); + + void processMcCollisionsWithoutCentralityAndMultiplicityAndXsection(aod::McCollisionsFrom>::iterator const& mcCollision) + { + uint32_t selDecision = ~uint16_t{0}; + uint32_t rctDecision = ~uint32_t{0}; + int32_t bcId = -1; + if (!config.isMCGenOnly) { + bcId = mcCollision.bcId(); + selDecision = bcSelMapping[bcId]; + rctDecision = bcRctMapping[bcId]; + } else { + products.jBCsTable(-1, -1, -1, -1, ~uint32_t{0}, ~uint32_t{0}, ~uint32_t{0}); + bcId = products.jBCsTable.lastIndex(); + } + products.jMcCollisionsTable(bcId, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), -1.0, -1.0, -1.0, -1.0, mcCollision.weight(), 1, 1, 1.0, 1.0, 999.0, selDecision, rctDecision, mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + products.jMcCollisionsParentIndexTable(mcCollision.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcCollisionsWithoutCentralityAndMultiplicityAndXsection, "produces derived MC collision table without centrality, multiplicity and cross section information", false); + + void processTrackSelectionForWeightedMC(soa::Join>, aod::McTrackLabelsFrom>> const& tracks, soa::Join>, aod::McCollisionLabelsFrom>> const&, aod::McCollisionsFrom> const& mcCollisions, aod::McParticlesFrom> const&) + { + bool hasMBGap = false; + for (auto const& mcCollision : mcCollisions) { + if (mcCollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + hasMBGap = true; + break; + } + } + if (hasMBGap) { + for (auto const& track : tracks) { + if (track.has_collision()) { + auto const& trackCollision = track.collision_as>, aod::McCollisionLabelsFrom>>>(); + if (track.has_mcParticle() && trackCollision.has_mcCollision()) { + auto const& trackMcCollision = trackCollision.mcCollision_as>>(); + auto const& particleMcCollision = track.mcParticle().mcCollision_as>>(); + if (trackMcCollision.globalIndex() != particleMcCollision.globalIndex() && trackMcCollision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap && particleMcCollision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + trackMCSelection[track.globalIndex()] = false; + } + } + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTrackSelectionForWeightedMC, "select whether tracks should be dropped in weighted productions", false); + + void processTracks(aod::Collisions const& collisions, soa::Join const& tracks, soa::Join>, aod::TracksExtraFrom>, aod::TracksCovFrom>, aod::TracksDCAFrom>, aod::TracksDCACovFrom>, aod::TrackSelectionFrom>, aod::TrackSelectionExtensionFrom>, aod::McTrackLabelsFrom>> const& tracksMCD, aod::CollisionsFrom> const& collisionsMCD) // need to check where in the framework assumes that the JTracks are always indexed the same as tracks + { + + std::vector mcCollisionSelected(collisionsMCD.size(), false); + for (auto const& collision : collisions) { + auto collisionTrakcs = tracks.sliceBy(preslices.perCollisionTracks, collision.globalIndex()); + for (auto const& track : collisionTrakcs) { + + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + if (track.collisionId() >= 0) { + auto const& collision = track.collision_as(); + dcaX = xyzTrack.X() - collision.posX(); + dcaY = xyzTrack.Y() - collision.posY(); + } + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + products.jMcTracksLabelTable(-1); + trackCollisionMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + + if (collisionTomcdCollisionMapping[collision.globalIndex()] >= 0) { + + auto collisionMCDTrakcs = tracksMCD.sliceBy(preslices.perCollisionTracksMCD, collisionTomcdCollisionMapping[collision.globalIndex()]); + mcCollisionSelected[collisionTomcdCollisionMapping[collision.globalIndex()]] = true; + for (auto const& track : collisionMCDTrakcs) { + + products.jTracksTable(collision.globalIndex(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + if (track.collisionId() >= 0) { + auto const& collision = track.collision_as>>(); + dcaX = xyzTrack.X() - collision.posX(); + dcaY = xyzTrack.Y() - collision.posY(); + } + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMCDMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + } + } + /* + for (int i = 0; i < collisionsMCD.size(); i++) { + if (!mcCollisionSelected[i]) { + auto collisionMCDTrakcs = tracksMCD.sliceBy(preslices.perCollisionTracksMCD, i); + for (auto const& track : collisionMCDTrakcs) { + products.jTracksTable(mcdcollisionToCollisionMapping[i], track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + auto trackParCov = getTrackParCov(track); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMCDMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + } + } + + for (auto const& track : tracks) { + if (!track.has_collision()) { + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + products.jMcTracksLabelTable(-1); + trackCollisionMapping[{track.globalIndex(), -1}] = products.jTracksTable.lastIndex(); + } + } + + for (auto const& track : tracksMCD) { + if (!track.has_collision()) { + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()], true)); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + products.jTracksParentIndexTable(track.globalIndex()); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + trackCollisionMapping[{track.globalIndex(), -1}] = products.jTracksTable.lastIndex(); + } + } + */ + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracks, "produces derived track table", true); + + void processTracksWithCollisionAssociator(aod::Collisions const& collisions, soa::Join const&, soa::Join const&, aod::TrackAssoc const& assocCollisions) + { + runNumber = 0; + for (auto const& collision : collisions) { + auto collisionTrackIndices = assocCollisions.sliceBy(preslices.perCollisionTrackIndices, collision.globalIndex()); + for (auto const& collisionTrackIndex : collisionTrackIndices) { + auto track = collisionTrackIndex.track_as>(); + auto trackParCov = getTrackParCov(track); + if (track.collisionId() == collision.globalIndex()) { + products.jTracksTable(collision.globalIndex(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + products.jTracksParentIndexTable(track.globalIndex()); + auto xyzTrack = trackParCov.getXYZGlo(); + float sigmaDCAXYZ2; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + products.jTracksExtraTable(xyzTrack.X() - collision.posX(), xyzTrack.Y() - collision.posY(), track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(track.sigmaDcaZ2()), std::sqrt(track.sigmaDcaXY2()), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // why is this getSigmaZY + } else { + auto bc = collision.bc_as>(); + initCCDB(bc, runNumber, ccdb, doprocessCollisionsRun2 ? config.ccdbPathGrp : config.ccdbPathGrpMag, lut, doprocessCollisionsRun2); + o2::dataformats::DCA dcaCovInfo; + dcaCovInfo.set(-999., -999., -999., -999., -999.); + o2::dataformats::VertexBase collisionInfo; + collisionInfo.setPos({collision.posX(), collision.posY(), collision.posZ()}); + collisionInfo.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + o2::base::Propagator::Instance()->propagateToDCABxByBz(collisionInfo, trackParCov, 2.f, noMatCorr, &dcaCovInfo); + products.jTracksTable(collision.globalIndex(), trackParCov.getPt(), trackParCov.getEta(), trackParCov.getPhi(), jetderiveddatautilities::setTrackSelectionBit(track, dcaCovInfo.getZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); // only qualitytracksWDCA are a reliable selection + products.jTracksParentIndexTable(track.globalIndex()); + auto xyzTrack = trackParCov.getXYZGlo(); + float dcaXY = dcaCovInfo.getY(); + float dcaZ = dcaCovInfo.getZ(); + float dcaXYZ = std::sqrt(dcaXY * dcaXY + dcaZ * dcaZ); + float covYY = dcaCovInfo.getSigmaY2(); + float covZZ = dcaCovInfo.getSigmaZ2(); + float covYZ = dcaCovInfo.getSigmaYZ(); + float sigmaDCAXYZ; + if (dcaXYZ < o2::constants::math::Almost0) { + sigmaDCAXYZ = o2::constants::math::VeryBig; // Protection against division by zero + } else { + sigmaDCAXYZ = covYY * (2.f * dcaXY / dcaXYZ) * (2.f * dcaXY / dcaXYZ) + covZZ * (2.f * dcaZ / dcaXYZ) * (2.f * dcaZ / dcaXYZ) + 2.f * covYZ * (2.f * dcaXY / dcaXYZ) * (2.f * dcaZ / dcaXYZ); + } + products.jTracksExtraTable(xyzTrack.X() - collision.posX(), xyzTrack.Y() - collision.posY(), dcaZ, dcaXY, dcaXYZ, std::sqrt(covZZ), std::sqrt(covYY), std::sqrt(sigmaDCAXYZ), std::sqrt(trackParCov.getSigma1Pt2())); + } + trackCollisionMapping[{track.globalIndex(), collision.globalIndex()}] = products.jTracksTable.lastIndex(); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracksWithCollisionAssociator, "produces derived track table taking into account track-to-collision associations", false); + + void processTracksRun2(soa::Join::iterator const& track) + { + // TracksDCACov table is not yet available for Run 2 converted data. Remove this process function and use only processTracks when that becomes available. + products.jTracksTable(track.collisionId(), track.pt(), track.eta(), track.phi(), jetderiveddatautilities::setTrackSelectionBit(track, track.dcaZ(), config.dcaZMax, trackMCSelection[track.globalIndex()])); + float sigmaDCAXYZ2 = 0.0; + float dcaXYZ = getDcaXYZ(track, &sigmaDCAXYZ2); + float dcaX = -99.0; + float dcaY = -99.0; + + products.jTracksExtraTable(dcaX, dcaY, track.dcaZ(), track.dcaXY(), dcaXYZ, std::sqrt(1.), std::sqrt(1.), std::sqrt(sigmaDCAXYZ2), track.sigma1Pt()); // dummy values - will be fixed when TracksDCACov table is available for Run 2 + products.jTracksParentIndexTable(track.globalIndex()); + trackCollisionMapping[{track.globalIndex(), track.collisionId()}] = products.jTracksTable.lastIndex(); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processTracksRun2, "produces derived track table for Run2 AO2Ds", false); + + void processMcTrackLabelsWithCollisionAssociator(aod::Collisions const& collisions, soa::Join const&, aod::TrackAssoc const& assocCollisions) + { + for (auto const& collision : collisions) { + auto collisionTrackIndices = assocCollisions.sliceBy(preslices.perCollisionTrackIndices, collision.globalIndex()); + for (auto const& collisionTrackIndex : collisionTrackIndices) { + auto track = collisionTrackIndex.track_as>(); + if (track.has_mcParticle()) { + products.jMcTracksLabelTable(track.mcParticleId()); + } else { + products.jMcTracksLabelTable(-1); + } + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcTrackLabelsWithCollisionAssociator, "produces derived track labels table taking into account track-to-collision associations", false); + + void processParticles(aod::McParticlesFrom>::iterator const& particle) + { + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + products.jMcParticlesTable(particle.mcCollisionId(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), particle.pdgCode(), particle.statusCode(), particle.flags(), mothersId, daughtersId); + products.jParticlesParentIndexTable(particle.globalIndex()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processParticles, "produces derived parrticle table", true); + + void processClusters(aod::Collision const&, aod::EMCALClusters const& clusters, aod::EMCALClusterCells const& cells, aod::Calos const&, aod::EMCALMatchedTracks const& matchedTracks, soa::Join const&) + { + + for (auto const& cluster : clusters) { + + auto const clusterCells = cells.sliceBy(preslices.perClusterCells, cluster.globalIndex()); + + float leadingCellEnergy = -1.0; + float subleadingCellEnergy = -1.0; + int leadingCellNumber = -1; + int subleadingCellNumber = -1; + for (auto const& clutserCell : clusterCells) { + float cellAmplitude = clutserCell.calo().amplitude(); + int cellNumber = clutserCell.calo().cellNumber(); + if (cellAmplitude > subleadingCellEnergy) { + subleadingCellEnergy = cellAmplitude; + subleadingCellNumber = cellNumber; + } + if (subleadingCellEnergy > leadingCellEnergy) { + std::swap(leadingCellEnergy, subleadingCellEnergy); + std::swap(leadingCellNumber, subleadingCellNumber); + } + } + + products.jClustersTable(cluster.collisionId(), cluster.id(), cluster.energy(), cluster.coreEnergy(), cluster.rawEnergy(), cluster.eta(), cluster.phi(), cluster.m02(), cluster.m20(), cluster.nCells(), cluster.time(), cluster.isExotic(), cluster.distanceToBadChannel(), cluster.nlm(), cluster.definition(), leadingCellEnergy, subleadingCellEnergy, leadingCellNumber, subleadingCellNumber); + products.jClustersParentIndexTable(cluster.globalIndex()); + + auto const clusterTracks = matchedTracks.sliceBy(preslices.perClusterTracks, cluster.globalIndex()); + std::vector clusterTrackIDs; + for (const auto& clusterTrack : clusterTracks) { + auto JClusterID = trackCollisionMapping.find({clusterTrack.trackId(), cluster.collisionId()}); // does EMCal use its own associator? + clusterTrackIDs.push_back(JClusterID->second); + auto emcTrack = clusterTrack.track_as>(); + products.jTracksEMCalTable(JClusterID->second, emcTrack.trackEtaEmcal(), emcTrack.trackPhiEmcal(), clusterTrack.deltaEta(), clusterTrack.deltaPhi()); + } + products.jClustersMatchedTracksTable(clusterTrackIDs); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processClusters, "produces derived cluster tables", false); + + void processMcClusterLabels(aod::EMCALMCCluster const& cluster) + { + std::vector particleIds; + for (auto particleId : cluster.mcParticleIds()) { + particleIds.push_back(particleId); + } + std::vector amplitudeA; + auto amplitudeASpan = cluster.amplitudeA(); + std::copy(amplitudeASpan.begin(), amplitudeASpan.end(), std::back_inserter(amplitudeA)); + products.jMcClustersLabelTable(particleIds, amplitudeA); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processMcClusterLabels, "produces derived cluster particle label table", false); + /* + void processD0Collisions(aod::HfD0CollIds::iterator const& D0Collision) + { + products.jD0CollisionIdsTable(D0Collision.collisionId()); + } + + void processD0McCollisions(aod::HfD0McCollIdsFrom>::iterator const& D0McCollision) + { + products.jD0McCollisionIdsTable(D0McCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0McCollisions, "produces derived index for D0 MC collisions", false); + + void processD0(aod::HfD0Idss const& D0Candidates, aod::Tracks const&, aod::HfD0IdssFrom> const& D0CandidatesMCD, aod::TracksFrom> const&) + { + for (auto const& D0Candidate : D0Candidates) { + auto JProng0ID = trackCollisionMapping.find({D0Candidate.prong0Id(), D0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({D0Candidate.prong1Id(), D0Candidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({D0Candidate.prong0Id(), D0Candidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({D0Candidate.prong1Id(), D0Candidate.collisionId()}); + } + products.jD0IdsTable(D0Candidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + // need to order them by collision though. Actually no this should be done in the HF + for (auto const& D0Candidate : D0CandidatesMCD) { + auto JProng0ID = trackCollisionMCDMapping.find({D0Candidate.prong0Id(), D0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMCDMapping.find({D0Candidate.prong1Id(), D0Candidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMCDMapping.find({D0Candidate.prong0Id(), D0Candidate.collisionId()}); + JProng1ID = trackCollisionMCDMapping.find({D0Candidate.prong1Id(), D0Candidate.collisionId()}); + } + products.jD0IdsTable(D0Candidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0, "produces derived index for D0 candidates", false); + + void processD0MC(aod::HfD0PIds::iterator const& D0Particle) + { + products.jD0ParticleIdsTable(D0Particle.mcCollisionId(), D0Particle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processD0MC, "produces derived index for D0 particles", false); + + void processDplusCollisions(aod::HfDplusCollIds::iterator const& DplusCollision) + { + products.jDplusCollisionIdsTable(DplusCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusCollisions, "produces derived index for Dplus collisions", false); + + void processDplusMcCollisions(aod::HfDplusMcCollIds::iterator const& DplusMcCollision) + { + products.jDplusMcCollisionIdsTable(DplusMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusMcCollisions, "produces derived index for Dplus MC collisions", false); + + void processDplus(aod::HfDplusIds::iterator const& DplusCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DplusCandidate.prong0Id(), DplusCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DplusCandidate.prong1Id(), DplusCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DplusCandidate.prong2Id(), DplusCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DplusCandidate.prong0Id(), DplusCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DplusCandidate.prong1Id(), DplusCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DplusCandidate.prong2Id(), DplusCandidate.collisionId()}); + } + products.jDplusIdsTable(DplusCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplus, "produces derived index for Dplus candidates", false); + + void processDplusMC(aod::HfDplusPIds::iterator const& DplusParticle) + { + products.jDplusParticleIdsTable(DplusParticle.mcCollisionId(), DplusParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDplusMC, "produces derived index for Dplus particles", false); + + void processDsCollisions(aod::HfDsCollIds::iterator const& DsCollision) + { + products.jDsCollisionIdsTable(DsCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsCollisions, "produces derived index for Ds collisions", false); + + void processDsMcCollisions(aod::HfDsMcCollIds::iterator const& DsMcCollision) + { + products.jDsMcCollisionIdsTable(DsMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsMcCollisions, "produces derived index for Ds MC collisions", false); + + void processDs(aod::HfDsIds::iterator const& DsCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DsCandidate.prong0Id(), DsCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DsCandidate.prong1Id(), DsCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DsCandidate.prong2Id(), DsCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DsCandidate.prong0Id(), DsCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DsCandidate.prong1Id(), DsCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DsCandidate.prong2Id(), DsCandidate.collisionId()}); + } + products.jDsIdsTable(DsCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDs, "produces derived index for Ds candidates", false); + + void processDsMC(aod::HfDsPIds::iterator const& DsParticle) + { + products.jDsParticleIdsTable(DsParticle.mcCollisionId(), DsParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDsMC, "produces derived index for Ds particles", false); + + void processDstarCollisions(aod::HfDstarCollIds::iterator const& DstarCollision) + { + products.jDstarCollisionIdsTable(DstarCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarCollisions, "produces derived index for Dstar collisions", false); + + void processDstarMcCollisions(aod::HfDstarMcCollIds::iterator const& DstarMcCollision) + { + products.jDstarMcCollisionIdsTable(DstarMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarMcCollisions, "produces derived index for Dstar MC collisions", false); + + void processDstar(aod::HfDstarIds::iterator const& DstarCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DstarCandidate.prong0Id(), DstarCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DstarCandidate.prong1Id(), DstarCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({DstarCandidate.prong2Id(), DstarCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DstarCandidate.prong0Id(), DstarCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DstarCandidate.prong1Id(), DstarCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({DstarCandidate.prong2Id(), DstarCandidate.collisionId()}); + } + products.jDstarIdsTable(DstarCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstar, "produces derived index for Dstar candidates", false); + + void processDstarMC(aod::HfDstarPIds::iterator const& DstarParticle) + { + products.jDstarParticleIdsTable(DstarParticle.mcCollisionId(), DstarParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDstarMC, "produces derived index for Dstar particles", false); + + void processLcCollisions(aod::HfLcCollIds::iterator const& LcCollision) + { + products.jLcCollisionIdsTable(LcCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcCollisions, "produces derived index for Lc collisions", false); + + void processLcMcCollisions(aod::HfLcMcCollIds::iterator const& LcMcCollision) + { + products.jLcMcCollisionIdsTable(LcMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcMcCollisions, "produces derived index for Lc MC collisions", false); + + void processLc(aod::HfLcIds::iterator const& LcCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({LcCandidate.prong0Id(), LcCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({LcCandidate.prong1Id(), LcCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({LcCandidate.prong2Id(), LcCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({LcCandidate.prong0Id(), LcCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({LcCandidate.prong1Id(), LcCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({LcCandidate.prong2Id(), LcCandidate.collisionId()}); + } + products.jLcIdsTable(LcCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLc, "produces derived index for Lc candidates", false); + + void processLcMC(aod::HfLcPIds::iterator const& LcParticle) + { + products.jLcParticleIdsTable(LcParticle.mcCollisionId(), LcParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processLcMC, "produces derived index for Lc particles", false); + + void processB0Collisions(aod::HfB0CollIds::iterator const& B0Collision) + { + products.jB0CollisionIdsTable(B0Collision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0Collisions, "produces derived index for B0 collisions", false); + + void processB0McCollisions(aod::HfB0McCollIds::iterator const& B0McCollision) + { + products.jB0McCollisionIdsTable(B0McCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0McCollisions, "produces derived index for B0 MC collisions", false); + + void processB0(aod::HfB0Ids::iterator const& B0Candidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({B0Candidate.prong0Id(), B0Candidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({B0Candidate.prong1Id(), B0Candidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({B0Candidate.prong2Id(), B0Candidate.prong2_as().collisionId()}); + auto JProng3ID = trackCollisionMapping.find({B0Candidate.prong3Id(), B0Candidate.prong3_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({B0Candidate.prong0Id(), B0Candidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({B0Candidate.prong1Id(), B0Candidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({B0Candidate.prong2Id(), B0Candidate.collisionId()}); + JProng3ID = trackCollisionMapping.find({B0Candidate.prong3Id(), B0Candidate.collisionId()}); + } + products.jB0IdsTable(B0Candidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second, JProng3ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0, "produces derived index for B0 candidates", false); + + void processB0MC(aod::HfB0PIds::iterator const& B0Particle) + { + products.jB0ParticleIdsTable(B0Particle.mcCollisionId(), B0Particle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processB0MC, "produces derived index for B0 particles", false); + + void processBplusCollisions(aod::HfBplusCollIds::iterator const& BplusCollision) + { + products.jBplusCollisionIdsTable(BplusCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusCollisions, "produces derived index for Bplus collisions", false); + + void processBplusMcCollisions(aod::HfBplusMcCollIds::iterator const& BplusMcCollision) + { + products.jBplusMcCollisionIdsTable(BplusMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusMcCollisions, "produces derived index for Bplus MC collisions", false); + + void processBplus(aod::HfBplusIds::iterator const& BplusCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({BplusCandidate.prong0Id(), BplusCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({BplusCandidate.prong1Id(), BplusCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({BplusCandidate.prong2Id(), BplusCandidate.prong2_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({BplusCandidate.prong0Id(), BplusCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({BplusCandidate.prong1Id(), BplusCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({BplusCandidate.prong2Id(), BplusCandidate.collisionId()}); + } + products.jBplusIdsTable(BplusCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplus, "produces derived index for Bplus candidates", false); + + void processBplusMC(aod::HfBplusPIds::iterator const& BplusParticle) + { + products.jBplusParticleIdsTable(BplusParticle.mcCollisionId(), BplusParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processBplusMC, "produces derived index for Bplus particles", false); + + void processXicToXiPiPiCollisions(aod::HfXicToXiPiPiCollIds::iterator const& XicToXiPiPiCollision) + { + products.jXicToXiPiPiCollisionIdsTable(XicToXiPiPiCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiCollisions, "produces derived index for XicToXiPiPi collisions", false); + + void processXicToXiPiPiMcCollisions(aod::HfXicToXiPiPiMcCollIds::iterator const& XicToXiPiPiMcCollision) + { + products.jXicToXiPiPiMcCollisionIdsTable(XicToXiPiPiMcCollision.mcCollisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiMcCollisions, "produces derived index for XicToXiPiPi MC collisions", false); + + void processXicToXiPiPi(aod::HfXicToXiPiPiIds::iterator const& XicToXiPiPiCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong0Id(), XicToXiPiPiCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong1Id(), XicToXiPiPiCandidate.prong1_as().collisionId()}); + auto JProng2ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong2Id(), XicToXiPiPiCandidate.prong2_as().collisionId()}); + auto JProng3ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong3Id(), XicToXiPiPiCandidate.prong3_as().collisionId()}); + auto JProng4ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong4Id(), XicToXiPiPiCandidate.prong4_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong0Id(), XicToXiPiPiCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong1Id(), XicToXiPiPiCandidate.collisionId()}); + JProng2ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong2Id(), XicToXiPiPiCandidate.collisionId()}); + JProng3ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong3Id(), XicToXiPiPiCandidate.collisionId()}); + JProng4ID = trackCollisionMapping.find({XicToXiPiPiCandidate.prong4Id(), XicToXiPiPiCandidate.collisionId()}); + } + products.jXicToXiPiPiIdsTable(XicToXiPiPiCandidate.collisionId(), JProng0ID->second, JProng1ID->second, JProng2ID->second, JProng3ID->second, JProng4ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPi, "produces derived index for XicToXiPiPi candidates", false); + + void processXicToXiPiPiMC(aod::HfXicToXiPiPiPIds::iterator const& XicToXiPiPiParticle) + { + products.jXicToXiPiPiParticleIdsTable(XicToXiPiPiParticle.mcCollisionId(), XicToXiPiPiParticle.mcParticleId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processXicToXiPiPiMC, "produces derived index for XicToXiPiPi particles", false); + + void processV0(aod::V0Indices::iterator const& V0Candidate, aod::Tracks const&) + { + auto JPosTrackID = trackCollisionMapping.find({V0Candidate.posTrackId(), V0Candidate.posTrack_as().collisionId()}); + auto JNegTrackID = trackCollisionMapping.find({V0Candidate.negTrackId(), V0Candidate.negTrack_as().collisionId()}); + if (withCollisionAssociator) { + JPosTrackID = trackCollisionMapping.find({V0Candidate.posTrackId(), V0Candidate.collisionId()}); + JNegTrackID = trackCollisionMapping.find({V0Candidate.negTrackId(), V0Candidate.collisionId()}); + } + products.jV0IdsTable(V0Candidate.collisionId(), JPosTrackID->second, JNegTrackID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processV0, "produces derived index for V0 candidates", false); + + void processV0MC(aod::McCollision const& mcCollision, aod::McParticles const& particles) + { // can loop over McV0Labels tables if we want to only store matched V0Particles + bool filledV0McCollisionTable = false; + for (auto const& particle : particles) { + if (jetv0utilities::isV0Particle(particles, particle, config.v0ChargedDecaysOnly)) { + if (!filledV0McCollisionTable) { + products.jV0McCollisionsTable(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + products.jV0McCollisionIdsTable(mcCollision.globalIndex()); + filledV0McCollisionTable = true; + } + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); + products.jV0McsTable(products.jV0McCollisionsTable.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), pdgParticle->Mass(), particle.pdgCode(), particle.statusCode(), particle.flags(), jetv0utilities::setV0ParticleDecayBit(particles, particle)); + products.jV0McIdsTable(mcCollision.globalIndex(), particle.globalIndex(), mothersId, daughtersId); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processV0MC, "produces V0 particles", false); + + void processDielectronCollisions(aod::ReducedEventsInfo::iterator const& DielectronCollision) + { + products.jDielectronCollisionIdsTable(DielectronCollision.collisionId()); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectronCollisions, "produces derived index for Dielectron collisions", false); + + void processDielectron(aod::DielectronInfo const& DielectronCandidate, aod::Tracks const&) + { + auto JProng0ID = trackCollisionMapping.find({DielectronCandidate.prong0Id(), DielectronCandidate.prong0_as().collisionId()}); + auto JProng1ID = trackCollisionMapping.find({DielectronCandidate.prong1Id(), DielectronCandidate.prong1_as().collisionId()}); + if (withCollisionAssociator) { + JProng0ID = trackCollisionMapping.find({DielectronCandidate.prong0Id(), DielectronCandidate.collisionId()}); + JProng1ID = trackCollisionMapping.find({DielectronCandidate.prong1Id(), DielectronCandidate.collisionId()}); + } + products.jDielectronIdsTable(DielectronCandidate.collisionId(), JProng0ID->second, JProng1ID->second); + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectron, "produces derived index for Dielectron candidates", false); + + void processDielectronMc(aod::McCollision const& mcCollision, aod::McParticles const& particles) + { + bool filledDielectronMcCollisionTable = false; + for (auto const& particle : particles) { + if (jetdqutilities::isDielectronParticle(particles, particle)) { + if (!filledDielectronMcCollisionTable) { + products.jDielectronMcCollisionsTable(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()); + products.jDielectronMcCollisionIdsTable(mcCollision.globalIndex()); + filledDielectronMcCollisionTable = true; + } + std::vector mothersId; + if (particle.has_mothers()) { + auto mothersIdTemps = particle.mothersIds(); + for (auto mothersIdTemp : mothersIdTemps) { + mothersId.push_back(mothersIdTemp); + } + } + int daughtersId[2] = {-1, -1}; + if (particle.has_daughters()) { + auto i = 0; + for (auto daughterId : particle.daughtersIds()) { + if (i > 1) { + break; + } + daughtersId[i] = daughterId; + i++; + } + } + auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); + products.jDielectronMcsTable(products.jDielectronMcCollisionsTable.lastIndex(), particle.pt(), particle.eta(), particle.phi(), particle.y(), particle.e(), pdgParticle->Mass(), particle.vx(), particle.vy(), particle.vz(), particle.vt(), particle.pdgCode(), particle.statusCode(), particle.flags(), jetdqutilities::setDielectronParticleDecayBit(particles, particle), RecoDecay::getCharmHadronOrigin(particles, particle, false)); // Todo: should the last thing be false? + products.jDielectronMcIdsTable(mcCollision.globalIndex(), particle.globalIndex(), mothersId, daughtersId); + products.JDielectronMcRCollDummysTable(false); + } + } + } + PROCESS_SWITCH(JetDerivedDataEmbeddingProducerTask, processDielectronMc, "produces Dielectron mccollisions and particles", false); + */ +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc, TaskName{"jet-deriveddata-embedding-producer"})}; +} diff --git a/PWGJE/TableProducer/derivedDataSelector.cxx b/PWGJE/TableProducer/derivedDataSelector.cxx index c9a0ca6ca32..b82419f7079 100644 --- a/PWGJE/TableProducer/derivedDataSelector.cxx +++ b/PWGJE/TableProducer/derivedDataSelector.cxx @@ -21,6 +21,7 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetReducedDataSelector.h" +#include "PWGJE/DataModel/JetSubtraction.h" #include #include @@ -48,6 +49,7 @@ struct JetDerivedDataSelector { struct : ConfigurableGroup { Configurable thresholdChargedJetPtMin{"thresholdChargedJetPtMin", 0.0, "Minimum charged jet pt to accept event"}; + Configurable thresholdChargedRhoAreaJetPtMin{"thresholdChargedRhoAreaJetPtMin", 0.0, "Minimum charged jet pt - rho area to accept event"}; Configurable thresholdChargedEventWiseSubtractedJetPtMin{"thresholdChargedEventWiseSubtractedJetPtMin", 0.0, "Minimum charged event-wise subtracted jet pt to accept event"}; Configurable thresholdChargedMCPJetPtMin{"thresholdChargedMCPJetPtMin", 0.0, "Minimum charged mcp jet pt to accept event"}; Configurable thresholdFullJetPtMin{"thresholdFullJetPtMin", 0.0, "Minimum full jet pt to accept event"}; @@ -97,6 +99,8 @@ struct JetDerivedDataSelector { Configurable thresholdChargedDielectronJetPtMin{"thresholdChargedDielectronJetPtMin", 0.0, "Minimum charged Dielectron jet pt to accept event"}; Configurable thresholdChargedEventWiseSubtractedDielectronJetPtMin{"thresholdChargedEventWiseSubtractedDielectronJetPtMin", 0.0, "Minimum charged event-wise subtracted Dielectron jet pt to accept event"}; Configurable thresholdChargedDielectronMCPJetPtMin{"thresholdChargedDielectronMCPJetPtMin", 0.0, "Minimum charged Dielectron mcp jet pt to accept event"}; + Configurable thresholdV0PtMin{"thresholdV0PtMin", 0.0, "Minimum V0 pt to accept event"}; + Configurable thresholdChargedV0JetPtMin{"thresholdChargedV0JetPtMin", 0.0, "Minimum charged V0 jet pt to accept event"}; Configurable thresholdTriggerTrackPtMin{"thresholdTriggerTrackPtMin", 0.0, "Minimum trigger track pt to accept event"}; Configurable thresholdTriggerTrackReferencePtMin{"thresholdTriggerTrackReferencePtMin", 0.0, "Minimum trigger track reference pt to accept event"}; Configurable thresholdTriggerParticlePtMin{"thresholdTriggerParticlePtMin", 0.0, "Minimum trigger particle pt to accept event"}; @@ -126,6 +130,8 @@ struct JetDerivedDataSelector { std::vector collisionSplitFlag; std::vector mcCollisionSplitFlag; + std::vector collisionRho; + TRandom3 randomNumber; std::vector eventSelectionBitsCharged; @@ -155,9 +161,9 @@ struct JetDerivedDataSelector { void processSelectMcCollisionsPerCollision(aod::JMcCollisions const& mcCollisions, soa::Join const& collisions) { - for (auto mcCollision : mcCollisions) { + for (const auto& mcCollision : mcCollisions) { const auto collisionsPerMcCollision = collisions.sliceBy(CollisionsPerMcCollision, mcCollision.globalIndex()); - for (auto collision : collisionsPerMcCollision) { + for (const auto& collision : collisionsPerMcCollision) { if (collisionFlag[collision.globalIndex()]) { mcCollisionFlag[mcCollision.globalIndex()] = true; } @@ -267,12 +273,25 @@ struct JetDerivedDataSelector { } } - template + void processSetupCollisionsRho(soa::Join const& collisions) + { + collisionRho.clear(); + collisionRho.resize(collisions.size(), 0.0); + for (auto const& collision : collisions) { + collisionRho[collision.globalIndex()] = collision.rho(); + } + } + + template void processSelectionObjects(T const& selectionObjects) { float selectionObjectPtMin = 0.0; if constexpr (std::is_same_v, aod::ChargedJets> || std::is_same_v, aod::ChargedMCDetectorLevelJets>) { - selectionObjectPtMin = config.thresholdChargedJetPtMin; + if constexpr (isRhoAreaSubtraction) { + selectionObjectPtMin = config.thresholdChargedRhoAreaJetPtMin; + } else { + selectionObjectPtMin = config.thresholdChargedJetPtMin; + } } else if constexpr (std::is_same_v, aod::ChargedEventWiseSubtractedJets> || std::is_same_v, aod::ChargedMCDetectorLevelEventWiseSubtractedJets>) { selectionObjectPtMin = config.thresholdChargedEventWiseSubtractedJetPtMin; } else if constexpr (std::is_same_v, aod::ChargedMCParticleLevelJets>) { @@ -371,6 +390,10 @@ struct JetDerivedDataSelector { selectionObjectPtMin = config.thresholdChargedEventWiseSubtractedDielectronJetPtMin; } else if constexpr (std::is_same_v, aod::DielectronChargedMCParticleLevelJets>) { selectionObjectPtMin = config.thresholdChargedDielectronMCPJetPtMin; + } else if constexpr (std::is_same_v, aod::CandidatesV0Data>) { + selectionObjectPtMin = config.thresholdV0PtMin; + } else if constexpr (std::is_same_v, aod::V0ChargedJets> || std::is_same_v, aod::V0ChargedMCDetectorLevelJets>) { + selectionObjectPtMin = config.thresholdChargedV0JetPtMin; } else if constexpr (std::is_same_v, aod::JTracks>) { selectionObjectPtMin = config.thresholdTriggerTrackPtMin; } else if constexpr (std::is_same_v, aod::JMcParticles>) { @@ -417,8 +440,14 @@ struct JetDerivedDataSelector { } } } - if (selectionObject.pt() >= selectionObjectPtMin) { - isTriggerObject = true; + if constexpr (isRhoAreaSubtraction) { + if (selectionObject.pt() >= selectionObjectPtMin + (collisionRho[selectionObject.collisionId()] * selectionObject.area())) { + isTriggerObject = true; + } + } else { + if (selectionObject.pt() >= selectionObjectPtMin) { + isTriggerObject = true; + } } } if (isTriggerObject) { @@ -451,68 +480,70 @@ struct JetDerivedDataSelector { PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingFullMCDJets, "process full mcd jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingFullMCPJets, "process full mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0Candidates, "process D0 candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0CandidatesMCP, "process D0 particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0CandidatesMCP, "process D0 particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedJets, "process D0 charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedEventWiseSubtractedJets, "process D0 event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedMCDJets, "process D0 charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedMCDetectorLevelEventWiseSubtractedJets, "process D0 event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedMCPJets, "process D0 charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedMCDJets, "process D0 charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedMCDetectorLevelEventWiseSubtractedJets, "process D0 event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingD0ChargedMCPJets, "process D0 charged mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusCandidates, "process Dplus candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusCandidatesMCP, "process Dplus particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusCandidatesMCP, "process Dplus particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedJets, "process Dplus charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedEventWiseSubtractedJets, "process Dplus event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedMCDJets, "process Dplus charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedMCDetectorLevelEventWiseSubtractedJets, "process Dplus event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedMCPJets, "process Dplus charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedMCDJets, "process Dplus charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedMCDetectorLevelEventWiseSubtractedJets, "process Dplus event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDplusChargedMCPJets, "process Dplus charged mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsCandidates, "process Ds candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsCandidatesMCP, "process Ds particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsCandidatesMCP, "process Ds particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedJets, "process Ds charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedEventWiseSubtractedJets, "process Ds event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedMCDJets, "process Ds charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedMCDetectorLevelEventWiseSubtractedJets, "process Ds event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedMCPJets, "process Ds charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedMCDJets, "process Ds charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedMCDetectorLevelEventWiseSubtractedJets, "process Ds event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDsChargedMCPJets, "process Ds charged mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarCandidates, "process Dstar candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarCandidatesMCP, "process Dstar particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarCandidatesMCP, "process Dstar particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedJets, "process Dstar charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedEventWiseSubtractedJets, "process Dstar event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedMCDJets, "process Dstar charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedMCDetectorLevelEventWiseSubtractedJets, "process Dstar event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedMCPJets, "process Dstar charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedMCDJets, "process Dstar charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedMCDetectorLevelEventWiseSubtractedJets, "process Dstar event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDstarChargedMCPJets, "process Dstar charged mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcCandidates, "process Lc candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcCandidatesMCP, "process Lc particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcCandidatesMCP, "process Lc particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedJets, "process Lc charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedEventWiseSubtractedJets, "process Lc event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedMCDJets, "process Lc charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedMCDetectorLevelEventWiseSubtractedJets, "process Lc event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedMCPJets, "process Lc charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedMCDJets, "process Lc charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedMCDetectorLevelEventWiseSubtractedJets, "process Lc event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingLcChargedMCPJets, "process Lc charged mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0Candidates, "process B0 candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0CandidatesMCP, "process B0 particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0CandidatesMCP, "process B0 particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedJets, "process B0 charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedEventWiseSubtractedJets, "process B0 event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedMCDJets, "process B0 charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedMCDetectorLevelEventWiseSubtractedJets, "process B0 event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedMCPJets, "process B0 charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedMCDJets, "process B0 charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedMCDetectorLevelEventWiseSubtractedJets, "process B0 event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingB0ChargedMCPJets, "process B0 charged mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusCandidates, "process Bplus candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusCandidatesMCP, "process Bplus particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusCandidatesMCP, "process Bplus particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedJets, "process Bplus charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedEventWiseSubtractedJets, "process Bplus event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedMCDJets, "process Bplus charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedMCDetectorLevelEventWiseSubtractedJets, "process Bplus event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedMCPJets, "process Bplus charged mcp jets", false); - // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiCandidates, "process XicToXiPiPi candidates", false); - // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiCandidatesMCP, "process XicToXiPiPi particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedMCDJets, "process Bplus charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedMCDetectorLevelEventWiseSubtractedJets, "process Bplus event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingBplusChargedMCPJets, "process Bplus charged mcp jets", false); + PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiCandidates, "process XicToXiPiPi candidates", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiCandidatesMCP, "process XicToXiPiPi particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedJets, "process XicToXiPiPi charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedEventWiseSubtractedJets, "process XicToXiPiPi event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedMCDJets, "process XicToXiPiPi charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedMCDetectorLevelEventWiseSubtractedJets, "process XicToXiPiPi event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedMCPJets, "process XicToXiPiPi charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedMCDJets, "process XicToXiPiPi charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedMCDetectorLevelEventWiseSubtractedJets, "process XicToXiPiPi event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingXicToXiPiPiChargedMCPJets, "process XicToXiPiPi charged mcp jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronCandidates, "process Dielectron candidates", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronCandidatesMCP, "process Dielectron particles", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronCandidatesMCP, "process Dielectron particles", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedJets, "process Dielectron charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedEventWiseSubtractedJets, "process Dielectron event-wise subtracted charged jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedMCDJets, "process Dielectron charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedMCDetectorLevelEventWiseSubtractedJets, "process Dielectron event-wise subtracted charged mcd jets", false); - PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedMCPJets, "process Dielectron charged mcp jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedMCDJets, "process Dielectron charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedMCDetectorLevelEventWiseSubtractedJets, "process Dielectron event-wise subtracted charged mcd jets", false); + // PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingDielectronChargedMCPJets, "process Dielectron charged mcp jets", false); + PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingV0Candidates, "process V0 candidates", false); + PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingV0ChargedJets, "process V0 charged jets", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingClusters, "process EMCal clusters", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingTracks, "process high pt tracks", false); PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectionObjects, processSelectingParticles, "process high pt particles", false); @@ -521,23 +552,37 @@ struct JetDerivedDataSelector { PROCESS_SWITCH(JetDerivedDataSelector, processDoCollisionSelections, "process event selections for saved events", false); PROCESS_SWITCH(JetDerivedDataSelector, processSelectMcCollisionsPerCollision, "select McCollisions due to a triggered reconstructed collision", false); PROCESS_SWITCH(JetDerivedDataSelector, processSelectCollisionsPerMcCollision, "select collisions due to a triggered McCollision", false); + PROCESS_SWITCH(JetDerivedDataSelector, processSetupCollisionsRho, "setup the writing for rho", false); + void processSelectingChargedRhoAreaJetsExpanded(aod::ChargedJets const& jets) + { + processSelectionObjects(jets); + } + PROCESS_SWITCH_FULL(JetDerivedDataSelector, processSelectingChargedRhoAreaJetsExpanded, processSelectingChargedRhoAreaJets, "process charged jets with rho area subtraction", false); void processStoreCollisionDecision(aod::JCollision const& collision) { + bool isSelectedForSignalTriggerTrack = false; + if (doprocessCollisionSplitting || doprocessCollisionSplittingMC) { + isSelectedForSignalTriggerTrack = collisionSplitFlag[collision.globalIndex()]; + } if (collisionFlag[collision.globalIndex()]) { - collisionSelectionsTable(true); + collisionSelectionsTable(true, isSelectedForSignalTriggerTrack); } else { - collisionSelectionsTable(false); + collisionSelectionsTable(false, isSelectedForSignalTriggerTrack); } } PROCESS_SWITCH(JetDerivedDataSelector, processStoreCollisionDecision, "write out decision of storing collision", true); void processStoreMcCollisionDecision(aod::JMcCollision const& mcCollision) { + bool isSelectedForSignalTriggerTrack = false; + if (doprocessCollisionSplittingMC) { + isSelectedForSignalTriggerTrack = mcCollisionSplitFlag[mcCollision.globalIndex()]; + } if (mcCollisionFlag[mcCollision.globalIndex()]) { - mcCollisionSelectionsTable(true); + mcCollisionSelectionsTable(true, isSelectedForSignalTriggerTrack); } else { - mcCollisionSelectionsTable(false); + mcCollisionSelectionsTable(false, isSelectedForSignalTriggerTrack); } } PROCESS_SWITCH(JetDerivedDataSelector, processStoreMcCollisionDecision, "write out decision of storing mcCollision", false); diff --git a/PWGJE/TableProducer/derivedDataWriter.cxx b/PWGJE/TableProducer/derivedDataWriter.cxx index 41f4d80d227..d0146f7c294 100644 --- a/PWGJE/TableProducer/derivedDataWriter.cxx +++ b/PWGJE/TableProducer/derivedDataWriter.cxx @@ -22,11 +22,15 @@ #include "PWGHF/DataModel/DerivedTables.h" #include "PWGJE/Core/JetDQUtilities.h" #include "PWGJE/Core/JetHFUtilities.h" +#include "PWGJE/Core/JetV0Utilities.h" #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetReducedDataDQ.h" #include "PWGJE/DataModel/JetReducedDataHF.h" #include "PWGJE/DataModel/JetReducedDataSelector.h" +#include "PWGJE/DataModel/JetReducedDataV0.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/V0SelectorTables.h" #include #include @@ -37,6 +41,8 @@ #include #include +#include + #include #include #include @@ -246,6 +252,12 @@ struct JetDerivedDataWriter { Produces storedDielectronParticleIdsTable; } productsDielectron; + struct : ProducesGroup { + Produces storedV0sTable; + Produces storedV0IdsTable; + Produces storedV0SignalFlagsTable; + } productsV0; + } products; struct : PresliceGroup { @@ -415,6 +427,17 @@ struct JetDerivedDataWriter { } } + template + void storeV0(soa::Join::iterator const& collision, aod::JTracks const&, T const& V0Candidates) + { + if (collision.isCollisionSelected()) { + for (const auto& V0Candidate : V0Candidates) { + jetv0utilities::fillV0CandidateTable(V0Candidate, products.productsV0.storedV0sTable, products.productsV0.storedV0SignalFlagsTable); + products.productsV0.storedV0IdsTable(collisionMapping[collision.globalIndex()], trackMapping[V0Candidate.posTrackId()], trackMapping[V0Candidate.negTrackId()]); + } + } + } + void processDummyTable(aod::JDummys const&) { products.storedJDummysTable(1); @@ -482,7 +505,11 @@ struct JetDerivedDataWriter { for (auto const& collision : collisions) { if (collision.isCollisionSelected()) { - products.storedJCollisionsTable(bcMapping[collision.bcId()], collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), collision.centFV0M(), collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), collision.hadronicRate(), collision.trackOccupancyInTimeRange(), collision.alias_raw(), collision.eventSel(), collision.rct_raw(), collision.triggerSel()); + uint16_t eventSelection = collision.eventSel(); + if (collision.isCollisionSelectedForSignalTriggerTrack()) { + SETBIT(eventSelection, jetderiveddatautilities::JCollisionSel::selTriggerTrackSignal); + } + products.storedJCollisionsTable(bcMapping[collision.bcId()], collision.posX(), collision.posY(), collision.posZ(), collision.collisionTime(), collision.multFV0A(), collision.multFV0C(), collision.multFT0A(), collision.multFT0C(), collision.centFV0A(), collision.centFV0M(), collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.centFT0CVariant1(), collision.hadronicRate(), collision.trackOccupancyInTimeRange(), collision.alias_raw(), eventSelection, collision.rct_raw(), collision.triggerSel()); collisionMapping[collision.globalIndex()] = products.storedJCollisionsTable.lastIndex(); products.storedJCollisionMcInfosTable(collision.weight(), collision.getSubGeneratorId()); products.storedJCollisionsParentIndexTable(collision.collisionId()); @@ -515,7 +542,7 @@ struct JetDerivedDataWriter { std::copy(amplitudesFT0CSpan.begin(), amplitudesFT0CSpan.end(), std::back_inserter(amplitudesFT0C)); std::copy(amplitudesFDDASpan.begin(), amplitudesFDDASpan.end(), std::back_inserter(amplitudesFDDA)); std::copy(amplitudesFDDCSpan.begin(), amplitudesFDDCSpan.end(), std::back_inserter(amplitudesFDDC)); - products.storedJCollisionUPCsTable(amplitudesFV0, amplitudesFT0A, amplitudesFT0C, amplitudesFDDA, amplitudesFDDC); + products.storedJCollisionUPCsTable(amplitudesFV0, amplitudesFT0A, amplitudesFT0C, amplitudesFDDA, amplitudesFDDC, collision.energyCommonZNA(), collision.energyCommonZNC(), collision.timeZNA(), collision.timeZNC()); } } } @@ -664,6 +691,12 @@ struct JetDerivedDataWriter { } PROCESS_SWITCH(JetDerivedDataWriter, processXicToXiPiPiMCD, "write out mcd output tables for XicToXiPiPi", false); + void processV0Data(soa::Join::iterator const& collision, aod::JTracks const& tracks, aod::CandidatesV0Data const& V0Candidates) + { + storeV0(collision, tracks, V0Candidates); + } + PROCESS_SWITCH(JetDerivedDataWriter, processV0Data, "write out data output tables for V0", false); + void processDielectron(soa::Join::iterator const& collision, aod::JTracks const&, aod::CollisionsDielectron const& DielectronCollisions, aod::CandidatesDielectronData const& DielectronCandidates) { if (collision.isCollisionSelected()) { @@ -685,7 +718,11 @@ struct JetDerivedDataWriter { mcCollisionMapping.resize(mcCollisions.size(), -1); for (auto const& mcCollision : mcCollisions) { if (mcCollision.isMcCollisionSelected()) { - products.storedJMcCollisionsTable(bcMapping[mcCollision.bcId()], mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multFV0A(), mcCollision.multFT0A(), mcCollision.multFT0C(), mcCollision.centFT0M(), mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), mcCollision.eventSel(), mcCollision.rct_raw(), mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); + uint16_t eventSelection = mcCollision.eventSel(); + if (mcCollision.isMcCollisionSelectedForSignalTriggerTrack()) { + SETBIT(eventSelection, jetderiveddatautilities::JCollisionSel::selTriggerTrackSignal); + } + products.storedJMcCollisionsTable(bcMapping[mcCollision.bcId()], mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.multFV0A(), mcCollision.multFT0A(), mcCollision.multFT0C(), mcCollision.centFT0M(), mcCollision.weight(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.ptHard(), eventSelection, mcCollision.rct_raw(), mcCollision.getGeneratorId(), mcCollision.getSubGeneratorId(), mcCollision.getSourceId(), mcCollision.impactParameter(), mcCollision.eventPlaneAngle()); products.storedJMcCollisionsParentIndexTable(mcCollision.mcCollisionId()); mcCollisionMapping[mcCollision.globalIndex()] = products.storedJMcCollisionsTable.lastIndex(); } @@ -703,11 +740,11 @@ struct JetDerivedDataWriter { const auto particlesPerMcCollision = particles.sliceBy(preslices.ParticlesPerMcCollision, mcCollision.globalIndex()); - for (auto particle : particlesPerMcCollision) { + for (const auto& particle : particlesPerMcCollision) { particleMapping[particle.globalIndex()] = particleTableIndex; particleTableIndex++; } - for (auto particle : particlesPerMcCollision) { + for (const auto& particle : particlesPerMcCollision) { std::vector mothersIds; int daughtersIds[2] = {-1, -1}; diff --git a/PWGJE/TableProducer/diffWakeTreeProducer.cxx b/PWGJE/TableProducer/diffWakeTreeProducer.cxx new file mode 100644 index 00000000000..bad79ec1889 --- /dev/null +++ b/PWGJE/TableProducer/diffWakeTreeProducer.cxx @@ -0,0 +1,292 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file diffWakeTreeProducer.cxx +/// \brief This task writes a collision and track table which are further used in a diffusion wake analysis +/// \authors Nicola Wilson and Nicolas Wirth + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/EventPlaneHelper.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// Event selection: Only events that contain track above some threshold +// ------------------------------------------------------------------------------------------- +// TRACK DATA +// ------------------------------------------------------------------------------------------- +// BEFORE COMPRESSION AFTER COMPRESSION +// Name Data Type Size(b) Name Data Type Size(b) +// ColID int32_t 4 [same] +// Charge short 2 [same] +// Px, Py, Pz float 3x4 P unsigned long 8 +// DEdx float 4 DEdx unsigned short 2 +// DCAXY float 4 DCAXY short 2 +// DCAZ float 4 DCAZ short 2 +// Length float 4 Length unsigned short 2 + +// OVERALL COMPRESSION 34b->22b + +// ------------------------------------------------------------------------------------------- +// EVENT DATA +// ------------------------------------------------------------------------------------------- +// GI int64_t 8 [same] +// RN int 4 [same] +// Cent float 4 [same] +// Mult int 4 [same] +// VertexX float 4 [same] +// VertexY float 4 [same] +// VertexZ float 4 [same] +// Psi2 float 4 Psi2 short 2 +// Psi3 float 4 Psi3 short 2 + +// OVERALL COMPRESSION 40b->36b +//-------------------------------------------------------- +namespace o2::aod +{ +namespace testcol +{ +// Event properties +DECLARE_SOA_COLUMN(Rn, rn, int32_t); // run number +DECLARE_SOA_COLUMN(Cent, cent, float); // FT0C centrality +DECLARE_SOA_COLUMN(Mult, mult, int32_t); // TPC multiplicity +DECLARE_SOA_COLUMN(Occu, occu, int32_t); // Occupancy ITS +DECLARE_SOA_COLUMN(Occuft0, occuft0, float); // Occupancy FT0C amplitudes +DECLARE_SOA_COLUMN(VertexX, vertexX, float); +DECLARE_SOA_COLUMN(VertexY, vertexY, float); +DECLARE_SOA_COLUMN(VertexZ, vertexZ, float); +DECLARE_SOA_COLUMN(Psi2, psi2, int16_t); +DECLARE_SOA_COLUMN(Psi3, psi3, int16_t); +} // namespace testcol + +DECLARE_SOA_TABLE(TableCols, "AOD", "TABLECOL", + o2::soa::Index<>, + testcol::Rn, + testcol::Cent, + testcol::Mult, + testcol::Occu, + testcol::Occuft0, + testcol::VertexX, + testcol::VertexY, + testcol::VertexZ, + testcol::Psi2, + testcol::Psi3); +using TableCol = TableCols::iterator; + +namespace testtrack +{ + +// Track properties +DECLARE_SOA_INDEX_COLUMN(TableCol, tableCol); +DECLARE_SOA_COLUMN(Charge, charge, int16_t); +DECLARE_SOA_COLUMN(P, p, uint64_t); +DECLARE_SOA_COLUMN(Dedx, dedx, uint16_t); +DECLARE_SOA_COLUMN(Dcaxy, dcaxy, int16_t); +DECLARE_SOA_COLUMN(Dcaz, dcaz, int16_t); +} // namespace testtrack + +DECLARE_SOA_TABLE(TableTrack, "AOD", "TABLETRACK", + testtrack::TableColId, + testtrack::Charge, + testtrack::P, + testtrack::Dedx, + testtrack::Dcaxy, + testtrack::Dcaz); +} // namespace o2::aod +//-------------------------------------------------------- +using namespace o2; +using namespace o2::framework; + +struct DiffWakeTreeProducer { + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histo"}; + Configurable ptThresh{"ptThresh", 20.0, "pT threshold"}; + Configurable centMax{"centMax", 10, "centrality"}; + Configurable zVertCut{"zVertCut", 10.0, "z_vertex cut"}; + Configurable etaCut{"etaCut", 0.9, "eta cut"}; + + Produces testcol; + Produces testtrack; + + EventPlaneHelper helperEP; + + void init(InitContext const&) + { + const AxisSpec axisEta{30, -1.5, +1.5, "#eta"}; + const AxisSpec axispT{nBinsPt, 0, 250, "p_{T}"}; + + histos.add("etaHistogram", "etaHistogram", kTH1F, {axisEta}); + histos.add("pTHistogram", "pTHistogram", kTH1F, {axispT}); + } + + using Bcs = aod::BCs; + void process(soa::Join::iterator const& col, + soa::Join const& tracks, + Bcs const&) + { + const float maxMomentum = 173.0; // max for px, py, pz + const float minMomentum = 0.1; // min for pT + + // Event selection corresponds to sel8FullPbPb + if (!col.sel8()) { + return; + } + if (col.centFT0C() > centMax) { + return; // Centrality 0 - 10 % + } + if (std::abs(col.posZ()) > zVertCut) { + return; // z position < 10 cm + } + if (!col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return; + } + if (!col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return; + } + //------ Get Run number --------------------- + auto bc = col.bc_as(); + int run = bc.runNumber(); + //------------ EP --------------------------- + double ep2 = 0.0; + double ep3 = 0.0; + ep2 = helperEP.GetEventPlane(col.qvecFT0CRe(), col.qvecFT0CIm(), 2); + ep3 = helperEP.GetEventPlane(col.qvecFT0CRe(), col.qvecFT0CIm(), 3); + + //------- Only events with track above some thresh ---------- + + bool eventHighpT = false; + for (const auto& track : tracks) { + + if (!track.isGlobalTrackWoPtEta()) { + continue; + } + if (track.pt() < minMomentum) { + continue; + } + if (std::abs(track.eta()) > etaCut) { + continue; + } + if (track.pt() > ptThresh) { + eventHighpT = true; + break; + } + } + if (!eventHighpT) { + return; + } + //------------------------------------------------------------ + // Translate values to less memory consuming values + auto substituteEp2 = static_cast(ep2 * 1000); + auto substituteEp3 = static_cast(ep3 * 1000); + + testcol(run, + col.centFT0C(), + col.multTPC(), + col.trackOccupancyInTimeRange(), + col.ft0cOccupancyInTimeRange(), + col.posX(), + col.posY(), + col.posZ(), + substituteEp2, + substituteEp3); + + for (const auto& track : tracks) { + + // Track cut + if (!track.isGlobalTrackWoPtEta()) { + continue; // General track cuts, but pT and eta cut are set manually + } + if (track.pt() < minMomentum) { + continue; + } + if (std::abs(track.eta()) > etaCut) { + continue; + } + + if (std::abs(track.px()) > maxMomentum || std::abs(track.py()) > maxMomentum || std::abs(track.pz()) > maxMomentum) { + continue; // to avoid overflow in Substitute_p + } + + histos.fill(HIST("etaHistogram"), track.eta()); + histos.fill(HIST("pTHistogram"), track.pt()); + + //------------ Translate values to less memory consuming values -------------------- + // Px, Py, Pz + uint64_t substituteP = 0; + uint8_t uppermostBit = 20; + uint8_t lowermostBit = 0; + uint64_t bitmask20Bits = 0b11111111111111111111; + + int64_t particlePx = (track.px() * 6000); + if (particlePx < 0) { + substituteP |= static_cast(1) << uppermostBit; + particlePx = (-1) * particlePx; + } + substituteP |= (particlePx & bitmask20Bits) << lowermostBit; + + uppermostBit = 41; + lowermostBit = 21; + int64_t particlePy = (track.py() * 6000); + if (particlePy < 0) { + substituteP |= static_cast(1) << uppermostBit; + particlePy = (-1) * particlePy; + } + substituteP |= (particlePy & bitmask20Bits) << lowermostBit; + + uppermostBit = 62; + lowermostBit = 42; + int64_t particlePz = (track.pz() * 6000); + if (particlePz < 0) { + substituteP |= static_cast(1) << uppermostBit; + particlePz = (-1) * particlePz; + } + substituteP |= (particlePz & bitmask20Bits) << lowermostBit; + + // dEdx + auto substituteDEDX = static_cast(track.tpcSignal() * 10); + + // DCA + auto substituteDCAXY = static_cast(track.dcaXY() * 100); + auto substituteDCAZ = static_cast(track.dcaZ() * 100); + + //--------------- Fill track table ------------------ + testtrack(testcol.lastIndex(), + track.sign(), + substituteP, + substituteDEDX, + substituteDCAXY, + substituteDCAZ); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/TableProducer/emcalCorrectionTask.cxx b/PWGJE/TableProducer/emcalCorrectionTask.cxx index 037f793a0d1..985e622f7fa 100644 --- a/PWGJE/TableProducer/emcalCorrectionTask.cxx +++ b/PWGJE/TableProducer/emcalCorrectionTask.cxx @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -53,10 +54,11 @@ #include #include #include +#include #include -#include +#include #include #include @@ -95,8 +97,10 @@ enum CellScaleMode { struct EmcalCorrectionTask { Produces clusters; Produces mcclusters; + Produces dispersions; Produces clustersAmbiguous; Produces mcclustersAmbiguous; + Produces ambigousDispersions; Produces clustercells; // cells belonging to given cluster Produces clustercellsambiguous; Produces matchedTracks; @@ -140,6 +144,9 @@ struct EmcalCorrectionTask { Configurable applyGainCalibShift{"applyGainCalibShift", false, "Apply shift for cell gain calibration to use values before cell format change (Sept. 2023)"}; Configurable applySoftwareTriggerSelection{"applySoftwareTriggerSelection", false, "Apply software trigger selection"}; Configurable softwareTriggerSelection{"softwareTriggerSelection", "fGammaHighPtEMCAL,fGammaHighPtDCAL", "Default: fGammaHighPtEMCAL,fGammaHighPtDCAL"}; + Configurable storePerDFInfo{"storePerDFInfo", false, "store addition information per DF."}; + ConfigurableAxis thConfigAxisClusters{"thConfigAxisClusters", {1000, 0.5f, 1000.5f}, ""}; + ConfigurableAxis thConfigAxisCells{"thConfigAxisCells", {1000, 0.5f, 1000.5f}, ""}; // cross talk emulation configs EmcCrossTalkConf emcCrossTalkConf; @@ -150,7 +157,7 @@ struct EmcalCorrectionTask { Filter emccellfilter = aod::calo::caloType == selectedCellType; // CDB service (for geometry) - Service mCcdbManager; + Service mCcdbManager{}; // Zorro for optional software trigger selection // this allows to save computation time @@ -176,27 +183,37 @@ struct EmcalCorrectionTask { o2::framework::HistogramRegistry mHistManager{"EMCALCorrectionTaskQAHistograms"}; // Random number generator to draw cell time smearing for MC - std::random_device rd{}; + std::random_device rd; std::mt19937_64 rdgen{rd()}; std::normal_distribution<> normalgaus{0, 1}; // mean = 0, stddev = 1 (apply amplitude of smearing after drawing random for performance reasons) // EMCal geometry - o2::emcal::Geometry* geometry; + o2::emcal::Geometry* geometry = nullptr; // EMCal cell temperature calibrator std::unique_ptr mTempCalibExtractor; bool mIsTempCalibInitialized = false; // Gain calibration - std::array mArrGainCalibDiff; + std::array mArrGainCalibDiff{}; std::vector> mExtraTimeShiftRunRanges; // Current run number - int runNumber{0}; + int mRunNumber{0}; static constexpr float TrackNotOnEMCal = -900.f; - static constexpr int kMaxMatchesPerCluster = 20; // Maximum number of tracks to match per cluster + static constexpr int MaxMatchesPerCluster = 20; // Maximum number of tracks to match per cluster + + static constexpr uint MaxClusterPerDFPerClusterizer = 200'000; // memory footprint: 13 MB per clusterizer + static constexpr uint MaxAmbClusterPerDFPerClusterizer = 300'000; // memory footprint: 19.5 MB per clusterizer + static constexpr uint MaxCellsPerClusterPerDFPerClusterizer = 300'000; // memory footprint: 4.8 MB per clusterizer + static constexpr uint MaxCellsPerAmbClusterPerDFPerClusterizer = 450'000; // memory footprint: 7.2 MB per clusterizer + + // cluster size + size_t nCluster = 0; + size_t nClusterAmb = 0; + size_t nCells = 0; void init(InitContext const&) { @@ -236,7 +253,7 @@ struct EmcalCorrectionTask { } // read all the cluster definitions specified in the options - if (clusterDefinitions->length()) { + if (!clusterDefinitions->empty()) { std::stringstream parser(clusterDefinitions.value); std::string token; o2::aod::EMCALClusterDefinition clusDef; @@ -267,7 +284,7 @@ struct EmcalCorrectionTask { clusterizer->setGeometry(geometry); } - if (mClusterizers.size() == 0) { + if (mClusterizers.empty()) { LOG(error) << "No cluster definitions specified!"; } @@ -283,6 +300,7 @@ struct EmcalCorrectionTask { // Define the cell energy binning std::vector cellEnergyBins; + cellEnergyBins.reserve(166); for (int i = 0; i < 51; i++) { // o2-linter: disable=magic-number (just numbers for binning) cellEnergyBins.emplace_back(0.1 * (i - 0) + 0.0); // from 0 to 5 GeV/c, every 0.1 GeV } @@ -309,6 +327,8 @@ struct EmcalCorrectionTask { o2::framework::AxisSpec axisDeltaEta{400, -0.2, 0.2, "#Delta#eta"}; o2::framework::AxisSpec axisDeltaPhi{400, -0.2, 0.2, "#Delta#varphi (rad)"}; o2::framework::AxisSpec axisNCluster{1000, 0, 1000, "#it{N}_{clus.}"}; + const o2::framework::AxisSpec nClusterDFAxis{thConfigAxisClusters, "#it{N}_{cluster}"}; + const o2::framework::AxisSpec nCellsDFAxis{thConfigAxisCells, "#it{N}_{cells}"}; mHistManager.add("hCellE", "hCellE", O2HistType::kTH1D, {energyAxis}); mHistManager.add("hCellTowerID", "hCellTowerID", O2HistType::kTH1D, {{20000, 0, 20000}}); mHistManager.add("hCellEtaPhi", "hCellEtaPhi", O2HistType::kTH2F, {etaAxis, phiAxis}); @@ -379,9 +399,15 @@ struct EmcalCorrectionTask { mExtraTimeShiftRunRanges.emplace_back(536565, 536590); // Commisioning-LHC23r mExtraTimeShiftRunRanges.emplace_back(542280, 543854); // LHC23zv-LHC23zy mExtraTimeShiftRunRanges.emplace_back(559544, 559856); // PbPb 2024 + + if (storePerDFInfo.value) { + mHistManager.add("hNClusterDF", "hNClusterDF", O2HistType::kTH1D, {nClusterDFAxis}); + mHistManager.add("hNClusterAmbigousDF", "hNClusterAmbigousDF", O2HistType::kTH1D, {nClusterDFAxis}); + mHistManager.add("hNCellDF", "hNCellDF", O2HistType::kTH1D, {nCellsDFAxis}); + } } - template + template void initZorroCCDB(const BCType& bc) { if (applySoftwareTriggerSelection) { @@ -398,22 +424,31 @@ struct EmcalCorrectionTask { void processFull(BcEvSels const& bcs, CollEventSels const& collisions, MyGlobTracks const& tracks, FilteredCells const& cells) { LOG(debug) << "Starting process full."; + clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); + clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); int previousCollisionId = 0; // Collision ID of the last unique BC. Needed to skip unordered collisions to ensure ordered collisionIds in the cluster table int nBCsProcessed = 0; int nCellsProcessed = 0; std::unordered_map numberCollsInBC; // Number of collisions mapped to the global BC index of all BCs std::unordered_map numberCellsInBC; // Number of cells mapped to the global BC index of all BCs to check whether EMCal was readout + nCluster = 0; + nClusterAmb = 0; + nCells = 0; for (const auto& bc : bcs) { LOG(debug) << "Next BC"; initZorroCCDB(bc); // get run number - runNumber = bc.runNumber(); + mRunNumber = bc.runNumber(); if (applyTempCalib && !mIsTempCalibInitialized) { // needs to be called once - mTempCalibExtractor->InitializeFromCCDB(pathTempCalibCCDB, static_cast(runNumber)); + mTempCalibExtractor->InitializeFromCCDB(pathTempCalibCCDB, static_cast(mRunNumber)); mIsTempCalibInitialized = true; } @@ -427,7 +462,7 @@ struct EmcalCorrectionTask { numberCollsInBC.insert(std::pair(bc.globalIndex(), collisionsInFoundBC.size())); numberCellsInBC.insert(std::pair(bc.globalIndex(), cellsInBC.size())); - if (!cellsInBC.size()) { + if (cellsInBC.size() == 0) { LOG(debug) << "No cells found for BC"; countBC(collisionsInFoundBC.size(), false); continue; @@ -449,7 +484,7 @@ struct EmcalCorrectionTask { if (static_cast(hasShaperCorrection) && emcal::intToChannelType(cell.cellType()) == emcal::ChannelType_t::LOW_GAIN) { // Apply shaper correction to LG cells amplitude = o2::emcal::NonlinearityHandler::evaluateShaperCorrectionCellEnergy(amplitude); } - if (applyCellAbsScale) { + if (applyCellAbsScale != ModeNone) { amplitude *= getAbsCellScale(cell.cellNumber()); } if (applyGainCalibShift) { @@ -462,7 +497,7 @@ struct EmcalCorrectionTask { } cellsBC.emplace_back(cell.cellNumber(), amplitude, - cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), runNumber), + cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), mRunNumber), o2::emcal::intToChannelType(cell.cellType())); cellIndicesBC.emplace_back(cell.globalIndex()); } @@ -499,7 +534,7 @@ struct EmcalCorrectionTask { // Store the clusters in the table where a matching collision could // be identified. - fillClusterTable(col, vertexPos, iClusterizer, cellIndicesBC, &indexMapPair, &trackGlobalIndex); + fillClusterTable(col, vertexPos, iClusterizer, cellIndicesBC, &indexMapPair, &trackGlobalIndex, nullptr, nullptr); } else { mHistManager.fill(HIST("hBCMatchErrors"), 2); } @@ -539,6 +574,11 @@ struct EmcalCorrectionTask { } // end of collision loop LOG(detail) << "Processed " << nBCsProcessed << " BCs with " << nCellsProcessed << " cells"; + if (storePerDFInfo) { + mHistManager.fill(HIST("hNClusterDF"), nCluster); + mHistManager.fill(HIST("hNClusterAmbigousDF"), nClusterAmb); + mHistManager.fill(HIST("hNCellDF"), nCells); + } } PROCESS_SWITCH(EmcalCorrectionTask, processFull, "run full analysis", true); @@ -546,21 +586,32 @@ struct EmcalCorrectionTask { { LOG(debug) << "Starting process full."; + clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); + clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); + int previousCollisionId = 0; // Collision ID of the last unique BC. Needed to skip unordered collisions to ensure ordered collisionIds in the cluster table int nBCsProcessed = 0; int nCellsProcessed = 0; std::unordered_map numberCollsInBC; // Number of collisions mapped to the global BC index of all BCs std::unordered_map numberCellsInBC; // Number of cells mapped to the global BC index of all BCs to check whether EMCal was readout + + nCluster = 0; + nClusterAmb = 0; + nCells = 0; for (const auto& bc : bcs) { LOG(debug) << "Next BC"; initZorroCCDB(bc); // get run number - runNumber = bc.runNumber(); + mRunNumber = bc.runNumber(); if (applyTempCalib && !mIsTempCalibInitialized) { // needs to be called once - mTempCalibExtractor->InitializeFromCCDB(pathTempCalibCCDB, static_cast(runNumber)); + mTempCalibExtractor->InitializeFromCCDB(pathTempCalibCCDB, static_cast(mRunNumber)); mIsTempCalibInitialized = true; } @@ -574,7 +625,7 @@ struct EmcalCorrectionTask { numberCollsInBC.insert(std::pair(bc.globalIndex(), collisionsInFoundBC.size())); numberCellsInBC.insert(std::pair(bc.globalIndex(), cellsInBC.size())); - if (!cellsInBC.size()) { + if (cellsInBC.size() == 0) { LOG(debug) << "No cells found for BC"; countBC(collisionsInFoundBC.size(), false); continue; @@ -596,7 +647,7 @@ struct EmcalCorrectionTask { if (static_cast(hasShaperCorrection) && emcal::intToChannelType(cell.cellType()) == emcal::ChannelType_t::LOW_GAIN) { // Apply shaper correction to LG cells amplitude = o2::emcal::NonlinearityHandler::evaluateShaperCorrectionCellEnergy(amplitude); } - if (applyCellAbsScale) { + if (applyCellAbsScale != ModeNone) { amplitude *= getAbsCellScale(cell.cellNumber()); } if (applyGainCalibShift) { @@ -609,7 +660,7 @@ struct EmcalCorrectionTask { } cellsBC.emplace_back(cell.cellNumber(), amplitude, - cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), runNumber), + cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), mRunNumber), o2::emcal::intToChannelType(cell.cellType())); cellIndicesBC.emplace_back(cell.globalIndex()); } @@ -690,6 +741,11 @@ struct EmcalCorrectionTask { } // end of collision loop LOG(detail) << "Processed " << nBCsProcessed << " BCs with " << nCellsProcessed << " cells"; + if (storePerDFInfo) { + mHistManager.fill(HIST("hNClusterDF"), nCluster); + mHistManager.fill(HIST("hNClusterAmbigousDF"), nClusterAmb); + mHistManager.fill(HIST("hNCellDF"), nCells); + } } PROCESS_SWITCH(EmcalCorrectionTask, processWithSecondaries, "run full analysis with secondary track matching", false); @@ -697,11 +753,25 @@ struct EmcalCorrectionTask { { LOG(debug) << "Starting processMCFull."; + clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + mcclusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + mcclustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); + clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); + int previousCollisionId = 0; // Collision ID of the last unique BC. Needed to skip unordered collisions to ensure ordered collisionIds in the cluster table int nBCsProcessed = 0; int nCellsProcessed = 0; std::unordered_map numberCollsInBC; // Number of collisions mapped to the global BC index of all BCs std::unordered_map numberCellsInBC; // Number of cells mapped to the global BC index of all BCs to check whether EMCal was readout + + nCluster = 0; + nClusterAmb = 0; + nCells = 0; + for (const auto& bc : bcs) { LOG(debug) << "Next BC"; // Convert aod::Calo to o2::emcal::Cell which can be used with the clusterizer. @@ -710,7 +780,7 @@ struct EmcalCorrectionTask { initZorroCCDB(bc); // get run number - runNumber = bc.runNumber(); + mRunNumber = bc.runNumber(); // Get the collisions matched to the BC using foundBCId of the collision auto collisionsInFoundBC = collisions.sliceBy(collisionsPerFoundBC, bc.globalIndex()); @@ -719,7 +789,7 @@ struct EmcalCorrectionTask { numberCollsInBC.insert(std::pair(bc.globalIndex(), collisionsInFoundBC.size())); numberCellsInBC.insert(std::pair(bc.globalIndex(), cellsInBC.size())); - if (!cellsInBC.size()) { + if (cellsInBC.size() == 0) { LOG(debug) << "No cells found for BC"; countBC(collisionsInFoundBC.size(), false); continue; @@ -762,7 +832,7 @@ struct EmcalCorrectionTask { } cellsBC.emplace_back(cell.cellNumber(), amplitude, - cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), runNumber), + cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), mRunNumber), o2::emcal::intToChannelType(cell.cellType())); cellIndicesBC.emplace_back(cell.globalIndex()); cellLabels.emplace_back(std::vector{cell.mcParticleIds().begin(), cell.mcParticleIds().end()}, std::vector{cell.amplitudeA().begin(), cell.amplitudeA().end()}); @@ -868,6 +938,11 @@ struct EmcalCorrectionTask { } // end of collision loop LOG(detail) << "Processed " << nBCsProcessed << " BCs with " << nCellsProcessed << " cells"; + if (storePerDFInfo) { + mHistManager.fill(HIST("hNClusterDF"), nCluster); + mHistManager.fill(HIST("hNClusterAmbigousDF"), nClusterAmb); + mHistManager.fill(HIST("hNCellDF"), nCells); + } } PROCESS_SWITCH(EmcalCorrectionTask, processMCFull, "run full analysis with MC info", false); @@ -875,11 +950,24 @@ struct EmcalCorrectionTask { { LOG(debug) << "Starting processMCWithSecondaries."; + clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + mcclusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + mcclustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); + clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); + int previousCollisionId = 0; // Collision ID of the last unique BC. Needed to skip unordered collisions to ensure ordered collisionIds in the cluster table int nBCsProcessed = 0; int nCellsProcessed = 0; std::unordered_map numberCollsInBC; // Number of collisions mapped to the global BC index of all BCs std::unordered_map numberCellsInBC; // Number of cells mapped to the global BC index of all BCs to check whether EMCal was readout + + nCluster = 0; + nClusterAmb = 0; + nCells = 0; for (const auto& bc : bcs) { LOG(debug) << "Next BC"; // Convert aod::Calo to o2::emcal::Cell which can be used with the clusterizer. @@ -888,7 +976,7 @@ struct EmcalCorrectionTask { initZorroCCDB(bc); // get run number - runNumber = bc.runNumber(); + mRunNumber = bc.runNumber(); // Get the collisions matched to the BC using foundBCId of the collision auto collisionsInFoundBC = collisions.sliceBy(collisionsPerFoundBC, bc.globalIndex()); @@ -897,7 +985,7 @@ struct EmcalCorrectionTask { numberCollsInBC.insert(std::pair(bc.globalIndex(), collisionsInFoundBC.size())); numberCellsInBC.insert(std::pair(bc.globalIndex(), cellsInBC.size())); - if (!cellsInBC.size()) { + if (cellsInBC.size() == 0) { LOG(debug) << "No cells found for BC"; countBC(collisionsInFoundBC.size(), false); continue; @@ -939,7 +1027,7 @@ struct EmcalCorrectionTask { } cellsBC.emplace_back(cell.cellNumber(), amplitude, - cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), runNumber), + cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), mRunNumber), o2::emcal::intToChannelType(cell.cellType())); cellIndicesBC.emplace_back(cell.globalIndex()); cellLabels.emplace_back(std::vector{cell.mcParticleIds().begin(), cell.mcParticleIds().end()}, std::vector{cell.amplitudeA().begin(), cell.amplitudeA().end()}); @@ -1049,16 +1137,33 @@ struct EmcalCorrectionTask { } // end of collision loop LOG(detail) << "Processed " << nBCsProcessed << " BCs with " << nCellsProcessed << " cells"; + if (storePerDFInfo) { + mHistManager.fill(HIST("hNClusterDF"), nCluster); + mHistManager.fill(HIST("hNClusterAmbigousDF"), nClusterAmb); + mHistManager.fill(HIST("hNCellDF"), nCells); + } } PROCESS_SWITCH(EmcalCorrectionTask, processMCWithSecondaries, "run full analysis with MC info", false); void processStandalone(BcEvSels const& bcs, aod::Collisions const& collisions, FilteredCells const& cells) { LOG(debug) << "Starting process standalone."; + + clusters.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + dispersions.reserve(MaxClusterPerDFPerClusterizer * mClusterizers.size()); + clustersAmbiguous.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + ambigousDispersions.reserve(MaxAmbClusterPerDFPerClusterizer * mClusterizers.size()); + clustercells.reserve(MaxCellsPerClusterPerDFPerClusterizer * mClusterizers.size()); + clustercellsambiguous.reserve(MaxCellsPerAmbClusterPerDFPerClusterizer * mClusterizers.size()); + int previousCollisionId = 0; // Collision ID of the last unique BC. Needed to skip unordered collisions to ensure ordered collisionIds in the cluster table int nBCsProcessed = 0; int nCellsProcessed = 0; + nCluster = 0; + nClusterAmb = 0; + nCells = 0; + for (const auto& bc : bcs) { LOG(debug) << "Next BC"; // Convert aod::Calo to o2::emcal::Cell which can be used with the clusterizer. @@ -1076,17 +1181,17 @@ struct EmcalCorrectionTask { } // get run number - runNumber = bc.runNumber(); + mRunNumber = bc.runNumber(); if (applyTempCalib && !mIsTempCalibInitialized) { // needs to be called once - mTempCalibExtractor->InitializeFromCCDB(pathTempCalibCCDB, static_cast(runNumber)); + mTempCalibExtractor->InitializeFromCCDB(pathTempCalibCCDB, static_cast(mRunNumber)); mIsTempCalibInitialized = true; } auto collisionsInBC = collisions.sliceBy(collisionsPerBC, bc.globalIndex()); auto cellsInBC = cells.sliceBy(cellsPerFoundBC, bc.globalIndex()); - if (!cellsInBC.size()) { + if (cellsInBC.size() == 0) { LOG(debug) << "No cells found for BC"; countBC(collisionsInBC.size(), false); continue; @@ -1110,7 +1215,7 @@ struct EmcalCorrectionTask { } cellsBC.emplace_back(cell.cellNumber(), amplitude, - cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), runNumber), + cell.time() + getCellTimeShift(cell.cellNumber(), amplitude, o2::emcal::intToChannelType(cell.cellType()), mRunNumber), o2::emcal::intToChannelType(cell.cellType())); cellIndicesBC.emplace_back(cell.globalIndex()); } @@ -1165,6 +1270,11 @@ struct EmcalCorrectionTask { nBCsProcessed++; } // end of bc loop LOG(debug) << "Done with process BC."; + if (storePerDFInfo) { + mHistManager.fill(HIST("hNClusterDF"), nCluster); + mHistManager.fill(HIST("hNClusterAmbigousDF"), nClusterAmb); + mHistManager.fill(HIST("hNCellDF"), nCells); + } } PROCESS_SWITCH(EmcalCorrectionTask, processStandalone, "run stand alone analysis", false); @@ -1206,21 +1316,11 @@ struct EmcalCorrectionTask { LOG(debug) << "Converted to analysis clusters."; } - template + template void fillClusterTable(Collision const& col, math_utils::Point3D const& vertexPos, size_t iClusterizer, const gsl::span cellIndicesBC, MatchResult* indexMapPair = nullptr, const std::vector* trackGlobalIndex = nullptr, MatchResult* indexMapPairSecondaries = nullptr, const std::vector* secondariesGlobalIndex = nullptr) { - // average number of cells per cluster, only used the reseve a reasonable amount for the clustercells table - const size_t nAvgNcells = 3; - // we found a collision, put the clusters into the none ambiguous table - clusters.reserve(mAnalysisClusters.size()); - if (!mClusterLabels.empty()) { - mcclusters.reserve(mClusterLabels.size()); - } - clustercells.reserve(mAnalysisClusters.size() * nAvgNcells); - // get the clusterType once const auto clusterType = static_cast(mClusterDefinitions[iClusterizer]); - int cellindex = -1; unsigned int iCluster = 0; float energy = 0.f; @@ -1253,15 +1353,17 @@ struct EmcalCorrectionTask { cluster.getClusterTime(), cluster.getIsExotic(), cluster.getDistanceToBadChannel(), cluster.getNExMax(), clusterType); + dispersions(cluster.getDispersion()); + ++nCluster; if (!mClusterLabels.empty()) { mcclusters(mClusterLabels[iCluster].getLabels(), mClusterLabels[iCluster].getEnergyFractions()); } - // loop over cells in cluster and save to table for (int ncell = 0; ncell < cluster.getNCells(); ncell++) { cellindex = cluster.getCellIndex(ncell); LOG(debug) << "trying to find cell index " << cellindex << " in map"; if (cellIndicesBC[cellindex] >= 0) { clustercells(clusters.lastIndex(), cellIndicesBC[cellindex]); + ++nCells; } } // end of cells of cluser loop // fill histograms @@ -1276,7 +1378,7 @@ struct EmcalCorrectionTask { mHistManager.fill(HIST("hClusterFCrossSigmaShortE"), cluster.E(), cluster.getFCross(), cluster.getM20()); } if (indexMapPair && trackGlobalIndex) { - if (iCluster < indexMapPair->matchIndexTrack.size() && indexMapPair->matchIndexTrack.size() > 0) { + if (iCluster < indexMapPair->matchIndexTrack.size() && !indexMapPair->matchIndexTrack.empty()) { for (unsigned int iTrack = 0; iTrack < indexMapPair->matchIndexTrack[iCluster].size(); iTrack++) { if (indexMapPair->matchIndexTrack[iCluster][iTrack] >= 0) { LOG(debug) << "Found track " << (*trackGlobalIndex)[indexMapPair->matchIndexTrack[iCluster][iTrack]] << " in cluster " << cluster.getID(); @@ -1287,7 +1389,7 @@ struct EmcalCorrectionTask { } } if (indexMapPairSecondaries && secondariesGlobalIndex) { - if (iCluster < indexMapPairSecondaries->matchIndexTrack.size() && indexMapPairSecondaries->matchIndexTrack.size() > 0) { + if (iCluster < indexMapPairSecondaries->matchIndexTrack.size() && !indexMapPairSecondaries->matchIndexTrack.empty()) { for (unsigned int iTrack = 0; iTrack < indexMapPairSecondaries->matchIndexTrack[iCluster].size(); iTrack++) { if (indexMapPairSecondaries->matchIndexTrack[iCluster][iTrack] >= 0) { LOG(debug) << "Found secondary track " << (*secondariesGlobalIndex)[indexMapPairSecondaries->matchIndexTrack[iCluster][iTrack]] << " in cluster " << cluster.getID(); @@ -1301,17 +1403,10 @@ struct EmcalCorrectionTask { } // end of cluster loop } - template + template void fillAmbigousClusterTable(BC const& bc, size_t iClusterizer, const gsl::span cellIndicesBC, bool hasCollision) { - // average number of cells per cluster, only used the reseve a reasonable amount for the clustercells table - const size_t nAvgNcells = 3; int cellindex = -1; - clustersAmbiguous.reserve(mAnalysisClusters.size()); - if (mClusterLabels.size() > 0) { - mcclustersAmbiguous.reserve(mClusterLabels.size()); - } - clustercellsambiguous.reserve(mAnalysisClusters.size() * nAvgNcells); unsigned int iCluster = 0; float energy = 0.f; for (const auto& cluster : mAnalysisClusters) { @@ -1343,7 +1438,9 @@ struct EmcalCorrectionTask { cluster.getM20(), cluster.getNCells(), cluster.getClusterTime(), cluster.getIsExotic(), cluster.getDistanceToBadChannel(), cluster.getNExMax(), static_cast(mClusterDefinitions.at(iClusterizer))); - if (mClusterLabels.size() > 0) { + ambigousDispersions(cluster.getDispersion()); + ++nClusterAmb; + if (!mClusterLabels.empty()) { mcclustersAmbiguous(mClusterLabels[iCluster].getLabels(), mClusterLabels[iCluster].getEnergyFractions()); } for (int ncell = 0; ncell < cluster.getNCells(); ncell++) { @@ -1355,7 +1452,7 @@ struct EmcalCorrectionTask { } // end of cluster loop } - template + template void doTrackMatching(Collision const& col, MyGlobTracks const& tracks, MatchResult& indexMapPair, std::vector& trackGlobalIndex) { auto groupedTracks = tracks.sliceBy(perCollision, col.globalIndex()); @@ -1368,10 +1465,10 @@ struct EmcalCorrectionTask { trackGlobalIndex.reserve(nTracksInCol); fillTrackInfo(groupedTracks, trackPhi, trackEta, trackGlobalIndex); - indexMapPair = matchTracksToCluster(mClusterPhi, mClusterEta, trackPhi, trackEta, maxMatchingDistance, kMaxMatchesPerCluster); + indexMapPair = matchTracksToCluster(mClusterPhi, mClusterEta, trackPhi, trackEta, maxMatchingDistance, MaxMatchesPerCluster); } - template + template void doSecondaryTrackMatching(Collision const& col, EMV0Legs const& v0legs, MatchResult& indexMapPair, std::vector& trackGlobalIndex, MyGlobTracks const& tracks) { auto groupedV0Legs = v0legs.sliceBy(perCollisionEMV0Legs, col.globalIndex()); @@ -1404,10 +1501,10 @@ struct EmcalCorrectionTask { trackEta.emplace_back(trackEtaEmcal); trackGlobalIndex.emplace_back(track.globalIndex()); } - indexMapPair = matchTracksToCluster(mClusterPhi, mClusterEta, trackPhi, trackEta, maxMatchingDistance, kMaxMatchesPerCluster); + indexMapPair = matchTracksToCluster(mClusterPhi, mClusterEta, trackPhi, trackEta, maxMatchingDistance, MaxMatchesPerCluster); } - template + template void fillTrackInfo(Tracks const& tracks, std::vector& trackPhi, std::vector& trackEta, std::vector& trackGlobalIndex) { for (const auto& track : tracks) { @@ -1456,10 +1553,11 @@ struct EmcalCorrectionTask { // For convenience, use the clusterizer stored geometry to get the eta-phi for (const auto& cell : cellsBC) { mHistManager.fill(HIST("hCellE"), cell.getEnergy()); - if (cell.getLowGain()) + if (cell.getLowGain()) { mHistManager.fill(HIST("hLGCellTimeEnergy"), cell.getTimeStamp(), cell.getEnergy()); - else if (cell.getHighGain()) + } else if (cell.getHighGain()) { mHistManager.fill(HIST("hHGCellTimeEnergy"), cell.getTimeStamp(), cell.getEnergy()); + } mHistManager.fill(HIST("hCellTowerID"), cell.getTower()); auto res = mClusterizers.at(0)->getGeometry()->EtaPhiFromIndex(cell.getTower()); mHistManager.fill(HIST("hCellEtaPhi"), std::get<0>(res), RecoDecay::constrainAngle(std::get<1>(res))); @@ -1478,12 +1576,12 @@ struct EmcalCorrectionTask { return cellAbsScaleFactors.value[mClusterizers.at(0)->getGeometry()->GetSMType(iSM)]; // Apply cell scale based on columns to accoutn for material of TRD structures - } else if (applyCellAbsScale == CellScaleMode::ModeColumnWise) { + } + if (applyCellAbsScale == CellScaleMode::ModeColumnWise) { auto res = mClusterizers.at(0)->getGeometry()->GlobalRowColFromIndex(cellID); return cellAbsScaleFactors.value[std::get<1>(res)]; - } else { - return 1.f; } + return 1.f; } // Apply shift of the cell time in data and MC @@ -1507,26 +1605,28 @@ struct EmcalCorrectionTask { timeshift = -std::sqrt(215.f + timeCol * timeCol); // 215 is 14.67ns^2 (time it takes to get the cell at eta = 0) // Also smear the time to account for the broader time resolution in data than in MC - if (cellEnergy < minLeaderEnergy) // Cells with tless than 300 MeV cannot be the leading cell in the cluster, so their time does not require precise calibration + if (cellEnergy < minLeaderEnergy) { // Cells with tless than 300 MeV cannot be the leading cell in the cluster, so their time does not require precise calibration timesmear = 0.; // They will therefore not be smeared and only get their shift - else if (cellType == emcal::ChannelType_t::HIGH_GAIN) // High gain cells -> Low energies + } else if (cellType == emcal::ChannelType_t::HIGH_GAIN) { // High gain cells -> Low energies timesmear = normalgaus(rdgen) * (1.6 + 9.5 * std::exp(-3. * cellEnergy)); // Parameters extracted from LHC24f3b & LHC22o (pp), but also usable for other periods - else if (cellType == emcal::ChannelType_t::LOW_GAIN) // Low gain cells -> High energies + } else if (cellType == emcal::ChannelType_t::LOW_GAIN) { // Low gain cells -> High energies timesmear = normalgaus(rdgen) * (5.0); // Parameters extracted from LHC24g4 & LHC24aj (pp), but also usable for other periods - + } } else { // ---> Data if (cellEnergy < minLeaderEnergy) { // Cells with tless than 300 MeV cannot be the leading cell in the cluster, so their time does not require precise calibration timeshift = 0.; // In data they will not be shifted (they are close to 0 anyways) } else if (cellType == emcal::ChannelType_t::HIGH_GAIN) { // High gain cells -> Low energies - if (cellEnergy < lowEnergyRegime) // Low energy regime + if (cellEnergy < lowEnergyRegime) { // Low energy regime timeshift = 0.8 * std::log(2.7 * cellEnergy); // Parameters extracted from LHC22o (pp), but also usable for other periods - else // Medium energy regime + } else { // Medium energy regime timeshift = 1.5 * std::log(0.9 * cellEnergy); // Parameters extracted from LHC22o (pp), but also usable for other periods - } else if (cellType == emcal::ChannelType_t::LOW_GAIN) { // Low gain cells -> High energies - if (cellEnergy < highEnergyRegime) // High energy regime - timeshift = 1.9 * std::log(0.09 * cellEnergy); // Parameters extracted from LHC24aj (pp), but also usable for other periods - else // Very high energy regime - timeshift = 1.9; // Parameters extracted from LHC24aj (pp), but also usable for other periods + } + } else if (cellType == emcal::ChannelType_t::LOW_GAIN) { // Low gain cells -> High energies + if (cellEnergy < highEnergyRegime) { // High energy regime + timeshift = 1.9 * std::log(0.09 * cellEnergy); // Parameters extracted from LHC24aj (pp), but also usable for other periods + } else { // Very high energy regime + timeshift = 1.9; // Parameters extracted from LHC24aj (pp), but also usable for other periods + } } // Temporary extra shift for bug in time calibraiton of apass4 Pb-Pb 2024, requires pos shift of 2*8.8 ns for low gain cells if (cellType == emcal::ChannelType_t::LOW_GAIN) { @@ -1545,17 +1645,17 @@ struct EmcalCorrectionTask { { auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); uint64_t tsOld = 1634853602000; // timestamp corresponding to LHC22o old gain calib object - o2::emcal::GainCalibrationFactors* paramsOld = ccdbMgr.getForTimeStamp("EMC/Calib/GainCalibFactors", tsOld); + auto* paramsOld = ccdbMgr.getForTimeStamp("EMC/Calib/GainCalibFactors", tsOld); uint64_t tsNew = 1734853602000; // timestamp corresponding to new gain calib object (new cell compression) - o2::emcal::GainCalibrationFactors* paramsNew = ccdbMgr.getForTimeStamp("EMC/Calib/GainCalibFactors", tsNew); - for (uint16_t i = 0; i < mArrGainCalibDiff.size(); ++i) { + auto* paramsNew = ccdbMgr.getForTimeStamp("EMC/Calib/GainCalibFactors", tsNew); + for (uint32_t i = 0; i < mArrGainCalibDiff.size(); ++i) { mArrGainCalibDiff[i] = paramsNew->getGainCalibFactors(i) == 0 ? 1. : paramsOld->getGainCalibFactors(i) / paramsNew->getGainCalibFactors(i); } } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx index d60275c6fac..18e7f854171 100644 --- a/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx +++ b/PWGJE/TableProducer/eventwiseConstituentSubtractor.cxx @@ -103,9 +103,9 @@ struct eventWiseConstituentSubtractorTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - particleSelection = static_cast(particleSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + particleSelection = particleSelections; eventWiseConstituentSubtractor.setDoRhoMassSub(doRhoMassSub); eventWiseConstituentSubtractor.setConstSubAlphaRMax(alpha, rMax); @@ -130,7 +130,7 @@ struct eventWiseConstituentSubtractorTask { } inputParticles.clear(); tracksSubtracted.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); tracksSubtracted = eventWiseConstituentSubtractor.JetBkgSubUtils::doEventConstSub(inputParticles, candidate.rho(), candidate.rhoM()); for (auto const& trackSubtracted : tracksSubtracted) { diff --git a/PWGJE/TableProducer/jetTaggerHF.cxx b/PWGJE/TableProducer/jetTaggerHF.cxx index 10cbcef6286..4f70a9f9d31 100644 --- a/PWGJE/TableProducer/jetTaggerHF.cxx +++ b/PWGJE/TableProducer/jetTaggerHF.cxx @@ -145,6 +145,17 @@ struct JetTaggerHFTask { ConfigurableAxis binTrackProbability{"binTrackProbability", {100, 0.f, 1.f}, ""}; ConfigurableAxis binJetFlavour{"binJetFlavour", {6, -0.5, 5.5}, ""}; + // ParT (Particle Transformer) tagger. Grouped in a ConfigurableGroup because + // StructToTuple.h supports at most 99 task members and this task is near the limit. + struct ParTCfg : ConfigurableGroup { + std::string prefix = "part"; + Configurable nConstit{"nConstit", 32, "fixed number of constituents fed to the ParT model, must match training"}; + Configurable nFeat{"nFeat", 10, "number of pf_features channels"}; + Configurable nVec{"nVec", 4, "number of pf_vectors channels"}; + Configurable padWrap{"padWrap", true, "pad short jets by cyclic repetition (weaver 'wrap' mode); set false for zero padding"}; + Configurable minTracks{"minTracks", 2, "minimum number of selected constituents; training applied jet_nTracks >= 2"}; + } parTCfg; + o2::analysis::MlResponseHfTagging bMlResponse; o2::ccdb::CcdbApi ccdbApi; @@ -277,7 +288,7 @@ struct JetTaggerHFTask { } } } - if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra) { + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra || doprocessAlgorithmParT) { if (jet.pt() >= jetpTMin) { float dbRange; if (scoreML[jet.globalIndex()] < dbMin) { @@ -376,46 +387,46 @@ struct JetTaggerHFTask { // Set up the resolution function switch (resoFuncMatch) { case 0: - vecParamsData = (std::vector)paramsResoFuncData; - vecParamsCharmJetMC = (std::vector)paramsResoFuncCharmJetMC; - vecParamsBeautyJetMC = (std::vector)paramsResoFuncBeautyJetMC; - vecParamsLfJetMC = (std::vector)paramsResoFuncLfJetMC; + vecParamsData = paramsResoFuncData; + vecParamsCharmJetMC = paramsResoFuncCharmJetMC; + vecParamsBeautyJetMC = paramsResoFuncBeautyJetMC; + vecParamsLfJetMC = paramsResoFuncLfJetMC; LOG(info) << "defined parameters of resolution function: custom"; break; case 1: - vecParamsData = (std::vector)paramsResoFuncData; - vecParamsIncJetMC = (std::vector)paramsResoFuncIncJetMC; + vecParamsData = paramsResoFuncData; + vecParamsIncJetMC = paramsResoFuncIncJetMC; useResoFuncFromIncJet = true; LOG(info) << "defined parameters of resolution function: custom & use inclusive distribution"; break; case 2: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsCharmJetMC = {282119.753, -0.065, 0.893, 11.608, 0.945, 8.029, 0.131, 6.244, 0.027}; vecParamsBeautyJetMC = {74901.583, -0.082, 0.874, 10.332, 0.941, 7.352, 0.097, 6.220, 0.022}; vecParamsLfJetMC = {1539435.343, -0.061, 0.896, 13.272, 1.034, 5.884, 0.004, 7.843, 0.090}; LOG(info) << "defined parameters of resolution function: PYTHIA8, MB, LHC23d1k"; break; case 3: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsIncJetMC = {1908803.027, -0.059, 0.895, 13.467, 1.005, 8.867, 0.098, 6.929, 0.011}; LOG(info) << "defined parameters of resolution function: PYTHIA8, MB, LHC23d1k & use inclusive distribution"; useResoFuncFromIncJet = true; break; case 4: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsCharmJetMC = {743719.121, -0.960, -0.240, 13.765, 1.314, 10.761, 0.293, 8.538, 0.052}; vecParamsBeautyJetMC = {88888.418, 0.256, 1.003, 10.185, 0.740, 8.216, 0.147, 7.228, 0.040}; vecParamsLfJetMC = {414860.372, -1.000, 0.285, 14.561, 1.464, 11.693, 0.339, 9.183, 0.052}; LOG(info) << "defined parameters of resolution function: PYTHIA8, JJ, weighted, LHC24g4"; break; case 5: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; vecParamsIncJetMC = {2211391.862, 0.360, 1.028, 13.019, 0.650, 11.151, 0.215, 9.462, 0.044}; LOG(info) << "defined parameters of resolution function: PYTHIA8, JJ, weighted, LHC24g4 & use inclusive distribution"; useResoFuncFromIncJet = true; break; case 6: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; for (size_t j = 0; j < resoFuncIncCCDB.size(); j++) { std::vector params; if (resoFuncIncCCDB[j]) { @@ -429,7 +440,7 @@ struct JetTaggerHFTask { useResoFuncFromIncJet = true; break; case 7: // TODO - vecParamsData = (std::vector)paramsResoFuncData; + vecParamsData = paramsResoFuncData; for (size_t j = 0; j < resoFuncBeautyCCDB.size(); j++) { std::vector params; if (resoFuncBeautyCCDB[j]) { @@ -525,9 +536,24 @@ struct JetTaggerHFTask { bMlResponse.init(); } + if (doprocessAlgorithmParT) { + bMlResponse.configure(binsPtMl, cutsMl, cutDirMl, nClassesMl); + if (loadModelsFromCCDB) { + ccdbApi.init(ccdbUrl); + bMlResponse.setModelPathsCCDB(onnxFileNames, ccdbApi, modelPathsCCDB, timestampCCDB); + } else { + bMlResponse.setModelPathsLocal(onnxFileNames); + } + bMlResponse.init(); + + LOGF(info, "ParT: loaded %s with %d output node(s); %d constituents, %s padding", onnxFileNames.value[0].c_str(), bMlResponse.getOutputNodes(), static_cast(parTCfg.nConstit.value), parTCfg.padWrap.value ? "wrap" : "zero"); + } + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra) { tensorAlloc = o2::analysis::GNNBjetAllocator(nJetFeat.value, nTrkFeat.value, nClassesMl.value, nTrkOrigin.value, transformFeatureJetMean.value, transformFeatureJetStdev.value, transformFeatureTrkMean.value, transformFeatureTrkStdev.value, nJetConst, tfFuncTypeGNN.value); + } + if (doprocessAlgorithmGNN || doprocessAlgorithmGNNwExtra || doprocessAlgorithmParT) { registry.add("h2_count_db", "#it{D}_{b} underflow/overflow;Jet flavour;#it{D}_{b} range", {HistType::kTH2F, {{4, 0., 4.}, {3, 0., 3.}}}); auto h2CountDb = registry.get(HIST("h2_count_db")); h2CountDb->GetXaxis()->SetBinLabel(1, "b-jet"); @@ -558,7 +584,7 @@ struct JetTaggerHFTask { int nSVs = analysisJet.template secondaryVertices_as().size(); - jettaggingutilities::BJetParams jetparam = {analysisJet.pt(), analysisJet.eta(), analysisJet.phi(), static_cast(tracksParams.size()), static_cast(nSVs), analysisJet.mass()}; + jettaggingutilities::BJetParams jetparam = {analysisJet.pt(), analysisJet.eta(), analysisJet.phi(), static_cast(tracksParams.size()), nSVs, analysisJet.mass()}; tracksParams.resize(nJetConst); // resize to the number of inputs of the ML svsParams.resize(nJetConst); // resize to the number of inputs of the ML @@ -669,6 +695,42 @@ struct JetTaggerHFTask { } } + template + void analyzeJetAlgorithmParT(AnyJets const& jets, AnyTracks const& tracks) + { + o2::analysis::ParTBjetAllocator parTTensorAlloc(parTCfg.nConstit.value, parTCfg.nFeat.value, parTCfg.nVec.value, parTCfg.padWrap.value); + + for (const auto& jet : jets) { + std::vector> featConstit; + std::vector> vecConstit; + jettaggingutilities::analyzeJetTrackInfo4ParT(jet, tracks, featConstit, vecConstit, trackPtMin, trackDcaXYMax, trackDcaZMax); + + // Training applied a jet_nTracks >= 2 selection, so jets below it are out of distribution. + if (static_cast(featConstit.size()) < parTCfg.minTracks) { + scoreML[jet.globalIndex()] = -999.; + LOGF(debug, "doprocessAlgorithmParT, too few selected constituents (%d)", jet.globalIndex()); + continue; + } + + std::vector featData; + std::vector vecData; + std::vector maskData; + std::vector parTInput; + parTTensorAlloc.getParTInput(featConstit, vecConstit, featData, vecData, maskData, parTInput); + + // The exported graph applies the softmax itself, so the output is already + // [p_b, p_c, p_udsg] and must not be passed through getDb(), which would softmax again. + auto modelOutput = bMlResponse.getModelOutputMultiInput(parTInput, 0); + float db = jettaggingutilities::getDbFromProb(modelOutput, fC); + if (!std::isnan(db)) { + scoreML[jet.globalIndex()] = db; + } else { + scoreML[jet.globalIndex()] = 999.; + LOGF(debug, "doprocessAlgorithmParT, Db is NaN (%d)", jet.globalIndex()); + } + } + } + void processDummy(aod::JetCollisions const&) { } @@ -725,6 +787,12 @@ struct JetTaggerHFTask { } PROCESS_SWITCH(JetTaggerHFTask, processAlgorithmGNN, "Fill GNN evaluation score (D_b) for charged jets", false); + void processAlgorithmParT(JetTable const& jets, JetTracksExt const& jtracks) + { + analyzeJetAlgorithmParT(jets, jtracks); + } + PROCESS_SWITCH(JetTaggerHFTask, processAlgorithmParT, "Fill ParT evaluation score (D_b) for charged jets", false); + void processFillTables(std::conditional_t, JetTable>::iterator const& jet, JetTracksExt const& tracks) { fillTables(jet, tracks); @@ -732,13 +800,15 @@ struct JetTaggerHFTask { PROCESS_SWITCH(JetTaggerHFTask, processFillTables, "Fill Tables for tagging decision, jet probability, and ML score on charged jets", false); }; -using JetTaggerhfDataCharged = JetTaggerHFTask, aod::DataSecondaryVertex3ProngIndices, aod::DataSecondaryVertex3Prongs, aod::ChargedJetTags>; -using JetTaggerhfMCDCharged = JetTaggerHFTask, aod::MCDSecondaryVertex3ProngIndices, aod::MCDSecondaryVertex3Prongs, aod::ChargedMCDetectorLevelJetTags>; +using JetTaggerhfDataCharged = JetTaggerHFTask, aod::DataSecondaryVertexNProngIndices, aod::DataSecondaryVertexNProngs, aod::ChargedJetTags>; +using JetTaggerhfMcdCharged = JetTaggerHFTask, aod::MCDSecondaryVertexNProngIndices, aod::MCDSecondaryVertexNProngs, aod::ChargedMCDetectorLevelJetTags>; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, SetDefaultProcesses{}, TaskName{"jet-taggerhf-data-charged"}), // o2-linter: disable=name/o2-task - adaptAnalysisTask(cfgc, SetDefaultProcesses{}, TaskName{"jet-taggerhf-mcd-charged"})}; // o2-linter: disable=name/o2-task + adaptAnalysisTask( + cfgc, SetDefaultProcesses{}, TaskName{"jet-taggerhf-data-charged"}), // o2-linter: disable=name/o2-task (custom device name for templated task alias) + adaptAnalysisTask( + cfgc, SetDefaultProcesses{}, TaskName{"jet-taggerhf-mcd-charged"})}; // o2-linter: disable=name/o2-task (custom device name for templated task alias) } diff --git a/PWGJE/TableProducer/jetTrackDerived.cxx b/PWGJE/TableProducer/jetTrackDerived.cxx index db9a7f5325b..694f2487598 100644 --- a/PWGJE/TableProducer/jetTrackDerived.cxx +++ b/PWGJE/TableProducer/jetTrackDerived.cxx @@ -159,7 +159,7 @@ struct jetspectraDerivedMaker { return true; } - Preslice trackPerColl = aod::track::collisionId; + Preslice trackPerColl = aod::track::collisionId; Produces tableTrack; Produces tableColl; using CollisionCandidate = soa::Join; diff --git a/PWGJE/TableProducer/mcOutlierRejector.cxx b/PWGJE/TableProducer/mcOutlierRejector.cxx index 32fed7ebbb3..7a149994a4d 100644 --- a/PWGJE/TableProducer/mcOutlierRejector.cxx +++ b/PWGJE/TableProducer/mcOutlierRejector.cxx @@ -47,7 +47,7 @@ struct McOutlierRejectorTask { std::vector collisionFlag; std::vector mcCollisionFlag; - int trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + int trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); void processSetupCollisionSelection(aod::JCollisions const& collisions) { @@ -69,7 +69,7 @@ struct McOutlierRejectorTask { if (selectionObjects.size() != 0) { float selectionObjectPt = 0.0; if constexpr (std::is_same_v, aod::JetTracksMCD> || std::is_same_v, aod::JetParticles>) { - for (auto selectionObject : selectionObjects) { + for (const auto& selectionObject : selectionObjects) { selectionObjectPt = selectionObject.pt(); // may be slow - could save only MC particle then check difference only for tracks IDd as outliers? if constexpr (std::is_same_v, aod::JetTracksMCD>) { // tracks diff --git a/PWGJE/TableProducer/quarkGluonJetsProducer.cxx b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx new file mode 100644 index 00000000000..89535fd176e --- /dev/null +++ b/PWGJE/TableProducer/quarkGluonJetsProducer.cxx @@ -0,0 +1,494 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file quarkGluonJetsProducer.cxx +/// \brief Produce a self-contained quark/gluon jet ML skim from JE derived data. + +/// For every accepted detector-level charged jet the task writes: +/// * one jet row, +/// * one row per selected reconstructed jet constituent, +/// * low-level reconstructed PID information, +/// * MC-truth constituent information, +/// * leading and subleading quark/gluon candidates used for jet labelling. + +/// \author Aleksandra Mulewicz + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +namespace o2::aod +{ +namespace qgmljet +{ +DECLARE_SOA_COLUMN(FlavorLabel, flavorLabel, int32_t); +DECLARE_SOA_COLUMN(LeadingPartonPdg, leadingPartonPdg, int32_t); +DECLARE_SOA_COLUMN(LeadingPartonPt, leadingPartonPt, float); +DECLARE_SOA_COLUMN(LeadingPartonDeltaR, leadingPartonDeltaR, float); +DECLARE_SOA_COLUMN(SubleadingPartonPdg, subleadingPartonPdg, int32_t); +DECLARE_SOA_COLUMN(SubleadingPartonPt, subleadingPartonPt, float); +DECLARE_SOA_COLUMN(SubleadingPartonDeltaR, subleadingPartonDeltaR, float); +DECLARE_SOA_COLUMN(NPartonsInCone, nPartonsInCone, int32_t); +DECLARE_SOA_COLUMN(LabelAmbiguous, labelAmbiguous, bool); +DECLARE_SOA_COLUMN(JetPt, jetPt, float); +DECLARE_SOA_COLUMN(JetEta, jetEta, float); +DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); +DECLARE_SOA_COLUMN(JetArea, jetArea, float); +DECLARE_SOA_COLUMN(JetRadius, jetRadius, float); +DECLARE_SOA_COLUMN(ZVertex, zVertex, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int32_t); +} // namespace qgmljet + +DECLARE_SOA_TABLE(QGMLJets, "AOD", "QGMLJET", + o2::soa::Index<>, + qgmljet::FlavorLabel, + qgmljet::LeadingPartonPdg, + qgmljet::LeadingPartonPt, + qgmljet::LeadingPartonDeltaR, + qgmljet::SubleadingPartonPdg, + qgmljet::SubleadingPartonPt, + qgmljet::SubleadingPartonDeltaR, + qgmljet::NPartonsInCone, + qgmljet::LabelAmbiguous, + qgmljet::JetPt, + qgmljet::JetEta, + qgmljet::JetPhi, + qgmljet::JetArea, + qgmljet::JetRadius, + qgmljet::ZVertex, + qgmljet::NConstituents); + +using QGMLJet = QGMLJets::iterator; + +namespace qgmlconst +{ +DECLARE_SOA_INDEX_COLUMN(QGMLJet, qGMLJet); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(P, p, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); +DECLARE_SOA_COLUMN(DeltaR, deltaR, float); +DECLARE_SOA_COLUMN(PtFraction, ptFraction, float); +DECLARE_SOA_COLUMN(Charge, charge, int32_t); +DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(TpcNSigmaPi, tpcNSigmaPi, float); +DECLARE_SOA_COLUMN(TpcNSigmaKa, tpcNSigmaKa, float); +DECLARE_SOA_COLUMN(TpcNSigmaPr, tpcNSigmaPr, float); +DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); +DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, float); +DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); +DECLARE_SOA_COLUMN(TofBeta, tofBeta, float); +DECLARE_SOA_COLUMN(TruthPdg, truthPdg, int32_t); +DECLARE_SOA_COLUMN(TruthPt, truthPt, float); +DECLARE_SOA_COLUMN(TruthEta, truthEta, float); +DECLARE_SOA_COLUMN(TruthPhi, truthPhi, float); +DECLARE_SOA_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, bool); +} // namespace qgmlconst + +DECLARE_SOA_TABLE(QGMLConstituents, "AOD", "QGMLCONSTS", + qgmlconst::QGMLJetId, + qgmlconst::Pt, + qgmlconst::P, + qgmlconst::Eta, + qgmlconst::Phi, + qgmlconst::Px, + qgmlconst::Py, + qgmlconst::Pz, + qgmlconst::DeltaEta, + qgmlconst::DeltaPhi, + qgmlconst::DeltaR, + qgmlconst::PtFraction, + qgmlconst::Charge, + qgmlconst::HasTOF, + qgmlconst::DcaXY, + qgmlconst::DcaZ, + qgmlconst::TpcNSigmaPi, + qgmlconst::TpcNSigmaKa, + qgmlconst::TpcNSigmaPr, + qgmlconst::TofNSigmaPi, + qgmlconst::TofNSigmaKa, + qgmlconst::TofNSigmaPr, + qgmlconst::TofBeta, + qgmlconst::TruthPdg, + qgmlconst::TruthPt, + qgmlconst::TruthEta, + qgmlconst::TruthPhi, + qgmlconst::IsPhysicalPrimary); +} // namespace o2::aod + +using namespace o2; +using namespace o2::constants::math; +using namespace o2::framework; + +using MCDJetEvents = aod::JetCollisionsMCD; +using ChargedMCDJets = soa::Join; +using JetTrackRefs = soa::Join; + +using HadronTracksMC = soa::Join; + +struct QuarkGluonJetsProducer { + Produces qgMLJets; + Produces qgMLConstituents; + + static constexpr double JetRadiusTolerance = 1.e-3; + + Configurable eventSelections{"eventSelections", "selMCFull+NoITSROFrameBorder+IsGoodZvtxFT0vsPV", "JE derived-data event selections"}; + Configurable skipMBGapEvents{"skipMBGapEvents", true, "skip minimum-bias gap subgenerator events"}; + Configurable applyRCTSelection{"applyRCTSelection", false, "apply RCT selection (normally false for MC training)"}; + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT label used when applyRCTSelection=true"}; + std::vector eventSelectionBits; + + Configurable isppRefAnalysis{"isppRefAnalysis", true, "pp reference jet acceptance"}; + Configurable cfgEtaJetMax{"cfgEtaJetMax", 0.5, "maximum absolute jet eta in pp"}; + Configurable minJetPt{"minJetPt", 10.0, "minimum detector-level jet pT"}; + Configurable maxJetPt{"maxJetPt", 1000.0, "maximum detector-level jet pT"}; + Configurable rJet{"rJet", 0.4, "selected jet radius"}; + Configurable zVtx{"zVtx", 10.0, "maximum absolute collision z"}; + Configurable applyAreaCut{"applyAreaCut", false, "apply normalized jet-area cut"}; + Configurable minNormalizedJetArea{"minNormalizedJetArea", 0.6, "minimum A/(pi R^2)"}; + Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from tracking edge"}; + + Configurable requirePvContributor{"requirePvContributor", false, "require PV-contributor track"}; + Configurable minItsNclusters{"minItsNclusters", 5, "minimum ITS clusters"}; + Configurable minTpcNcrossedRows{"minTpcNcrossedRows", 70, "minimum TPC crossed rows"}; + Configurable minChiSquareTpc{"minChiSquareTpc", 0.0, "minimum TPC chi2/Ncl"}; + Configurable maxChiSquareTpc{"maxChiSquareTpc", 4.0, "maximum TPC chi2/Ncl"}; + Configurable maxChiSquareIts{"maxChiSquareIts", 36.0, "maximum ITS chi2/Ncl"}; + Configurable minPt{"minPt", 0.15, "minimum selected-track pT"}; + Configurable maxPt{"maxPt", 1000.0, "maximum selected-track pT"}; + Configurable minEta{"minEta", -0.8, "minimum selected-track eta"}; + Configurable maxEta{"maxEta", 0.8, "maximum selected-track eta"}; + Configurable maxDcaxy{"maxDcaxy", 0.2, "maximum absolute DCAxy"}; + Configurable maxDcaz{"maxDcaz", 0.1, "maximum absolute DCAz"}; + + Configurable labelAmbiguityPtFraction{"labelAmbiguityPtFraction", 0.5f, "ambiguous if opposite-flavour subleading parton exceeds this leading-pT fraction"}; + Configurable requireGeneratorPartons{"requireGeneratorPartons", true, "use only MC partons produced by the event generator for Q/G labelling"}; + Configurable partonGenStatus{"partonGenStatus", 0, "optional absolute generator-status filter for Q/G partons; 0 disables the status filter"}; + Configurable dropUnknownLabels{"dropUnknownLabels", false, "drop jets without quark/gluon label"}; + Configurable dropAmbiguousLabels{"dropAmbiguousLabels", false, "drop ambiguous labels"}; + + Preslice mcParticlesPerCollision = aod::jmcparticle::mcCollisionId; + + enum JetFlavor { UnknownJet = 0, + QuarkJet = 1, + GluonJet = 2 }; + + struct JetFlavorInfo { + int flavor{UnknownJet}; + int leadingPartonPdg{0}; + float leadingPartonPt{-1.0f}; + float leadingPartonDeltaR{-1.0f}; + int subleadingPartonPdg{0}; + float subleadingPartonPt{-1.0f}; + float subleadingPartonDeltaR{-1.0f}; + int nPartonsInCone{0}; + bool ambiguous{false}; + }; + + void init(InitContext const&) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + } + + template + [[nodiscard]] bool hasITSLayerHit(TrackT const& track, int layer) const + { + return TESTBIT(track.itsClusterMap(), layer - 1); + } + + template + bool passedTrackSelection(TrackT const& track) + { + if (requirePvContributor && !track.isPVContributor()) { + return false; + } + if (!track.hasITS() || !track.hasTPC()) { + return false; + } + if (!hasITSLayerHit(track, 1) && !hasITSLayerHit(track, 2) && !hasITSLayerHit(track, 3)) { + return false; + } + if (track.itsNCls() < minItsNclusters || track.tpcNClsCrossedRows() < minTpcNcrossedRows) { + return false; + } + if (track.tpcChi2NCl() < minChiSquareTpc || track.tpcChi2NCl() > maxChiSquareTpc) { + return false; + } + if (track.itsChi2NCl() > maxChiSquareIts) { + return false; + } + if (track.eta() < minEta || track.eta() > maxEta || track.pt() < minPt || track.pt() > maxPt) { + return false; + } + if (std::abs(track.dcaXY()) > maxDcaxy || std::abs(track.dcaZ()) > maxDcaz) { + return false; + } + return true; + } + + template + JetFlavorInfo getJetFlavorTag(JetT const& jet, ParticleRangeT const& particles) + { + JetFlavorInfo result; + const double radius = static_cast(jet.r()) / 100.0; + + for (auto const& particle : particles) { + if (requireGeneratorPartons && !particle.producedByGenerator()) { + continue; + } + if (partonGenStatus > 0 && std::abs(particle.getGenStatusCode()) != partonGenStatus) { + continue; + } + + const int absPdg = std::abs(particle.pdgCode()); + const bool isQuark = absPdg >= kDown && absPdg <= kTop; + const bool isGluon = absPdg == kGluon; + if (!isQuark && !isGluon) { + continue; + } + + const double dEta = particle.eta() - jet.eta(); + const double dPhi = RecoDecay::constrainAngle(particle.phi() - jet.phi(), -PI); + const double dR = std::hypot(dEta, dPhi); + if (dR >= radius) { + continue; + } + + ++result.nPartonsInCone; + const float pt = particle.pt(); + if (pt > result.leadingPartonPt) { + result.subleadingPartonPdg = result.leadingPartonPdg; + result.subleadingPartonPt = result.leadingPartonPt; + result.subleadingPartonDeltaR = result.leadingPartonDeltaR; + result.leadingPartonPdg = particle.pdgCode(); + result.leadingPartonPt = pt; + result.leadingPartonDeltaR = dR; + } else if (pt > result.subleadingPartonPt) { + result.subleadingPartonPdg = particle.pdgCode(); + result.subleadingPartonPt = pt; + result.subleadingPartonDeltaR = dR; + } + } + + const int leadingAbs = std::abs(result.leadingPartonPdg); + if (leadingAbs >= kDown && leadingAbs <= kTop) { + result.flavor = QuarkJet; + } else if (leadingAbs == kGluon) { + result.flavor = GluonJet; + } + + if (result.leadingPartonPt > 0.f && result.subleadingPartonPt > 0.f) { + const int subAbs = std::abs(result.subleadingPartonPdg); + const bool leadingQuark = leadingAbs >= kDown && leadingAbs <= kTop; + const bool subQuark = subAbs >= kDown && subAbs <= kTop; + result.ambiguous = (leadingQuark != subQuark) && + result.subleadingPartonPt >= labelAmbiguityPtFraction * result.leadingPartonPt; + } + return result; + } + + void process(MCDJetEvents::iterator const& collision, + ChargedMCDJets const& detectorLevelJets, + JetTrackRefs const&, + HadronTracksMC const&, + aod::JetParticles const& jetParticles, + aod::McParticles const&) + { + if (!jetderiveddatautilities::selectCollision( + collision, + eventSelectionBits, + static_cast(skipMBGapEvents), + static_cast(applyRCTSelection), + static_cast(rctLabel))) { + return; + } + if (std::abs(collision.posZ()) > zVtx) { + return; + } + + for (auto const& jet : detectorLevelJets) { + const double jetRadius = static_cast(jet.r()) / 100.0; + if (std::abs(jetRadius - static_cast(rJet)) > JetRadiusTolerance) { + continue; + } + + if (isppRefAnalysis && std::abs(jet.eta()) > cfgEtaJetMax) { + continue; + } + if (!isppRefAnalysis && (std::abs(jet.eta()) + jetRadius > maxEta - deltaEtaEdge)) { + continue; + } + if (jet.pt() < minJetPt || jet.pt() > maxJetPt) { + continue; + } + const double normalizedArea = jet.area() / (PI * jetRadius * jetRadius); + if (applyAreaCut && normalizedArea < minNormalizedJetArea) { + continue; + } + + const int jetMcCollisionId = jet.collision_as().mcCollisionId(); + + JetFlavorInfo flavor; + if (jetMcCollisionId >= 0) { + auto collisionParticles = jetParticles.sliceBy(mcParticlesPerCollision, jetMcCollisionId); + flavor = getJetFlavorTag(jet, collisionParticles); + } + if (dropUnknownLabels && flavor.flavor == UnknownJet) { + continue; + } + if (dropAmbiguousLabels && flavor.ambiguous) { + continue; + } + + int32_t nConstituents = 0; + + for (auto const& jtrack : jet.tracks_as()) { + auto track = jtrack.track_as(); + + if (!passedTrackSelection(track)) { + continue; + } + + ++nConstituents; + } + if (nConstituents == 0) { + continue; + } + + qgMLJets( + flavor.flavor, + flavor.leadingPartonPdg, + flavor.leadingPartonPt, + flavor.leadingPartonDeltaR, + flavor.subleadingPartonPdg, + flavor.subleadingPartonPt, + flavor.subleadingPartonDeltaR, + flavor.nPartonsInCone, + flavor.ambiguous, + static_cast(jet.pt()), + static_cast(jet.eta()), + static_cast(jet.phi()), + static_cast(jet.area()), + static_cast(jetRadius), + static_cast(collision.posZ()), + nConstituents); + + const auto qgMLJetId = qgMLJets.lastIndex(); + + for (auto const& jtrack : jet.tracks_as()) { + auto track = jtrack.track_as(); + + if (!passedTrackSelection(track)) { + continue; + } + + const double dEta = track.eta() - jet.eta(); + const double dPhi = RecoDecay::constrainAngle(track.phi() - jet.phi(), -PI); + const double dR = std::hypot(dEta, dPhi); + int32_t truthPdg = 0; + float truthPt = std::numeric_limits::quiet_NaN(); + float truthEta = std::numeric_limits::quiet_NaN(); + float truthPhi = std::numeric_limits::quiet_NaN(); + bool isPhysicalPrimary = false; + + if (track.has_mcParticle()) { + auto truth = track.mcParticle_as(); + truthPdg = truth.pdgCode(); + truthPt = truth.pt(); + truthEta = truth.eta(); + truthPhi = truth.phi(); + isPhysicalPrimary = truth.isPhysicalPrimary(); + } + + const float missingTOF = std::numeric_limits::quiet_NaN(); + qgMLConstituents( + qgMLJetId, + static_cast(track.pt()), + static_cast(track.p()), + static_cast(track.eta()), + static_cast(track.phi()), + static_cast(track.px()), + static_cast(track.py()), + static_cast(track.pz()), + static_cast(dEta), + static_cast(dPhi), + static_cast(dR), + jet.pt() > 0.f ? static_cast(track.pt() / jet.pt()) : 0.f, + track.sign(), + track.hasTOF(), + static_cast(track.dcaXY()), + static_cast(track.dcaZ()), + static_cast(track.tpcNSigmaPi()), + static_cast(track.tpcNSigmaKa()), + static_cast(track.tpcNSigmaPr()), + track.hasTOF() ? static_cast(track.tofNSigmaPi()) : missingTOF, + track.hasTOF() ? static_cast(track.tofNSigmaKa()) : missingTOF, + track.hasTOF() ? static_cast(track.tofNSigmaPr()) : missingTOF, + track.hasTOF() ? static_cast(track.beta()) : missingTOF, + truthPdg, + truthPt, + truthEta, + truthPhi, + isPhysicalPrimary); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/TableProducer/rhoEstimator.cxx b/PWGJE/TableProducer/rhoEstimator.cxx index c7f11e99b0c..1e340b41587 100644 --- a/PWGJE/TableProducer/rhoEstimator.cxx +++ b/PWGJE/TableProducer/rhoEstimator.cxx @@ -238,7 +238,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoD0Table(rho, rhoM); @@ -270,7 +270,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDplusTable(rho, rhoM); @@ -303,7 +303,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDsTable(rho, rhoM); @@ -335,7 +335,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDstarTable(rho, rhoM); @@ -367,7 +367,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoLcTable(rho, rhoM); @@ -399,7 +399,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoB0Table(rho, rhoM); @@ -431,7 +431,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoBplusTable(rho, rhoM); @@ -463,7 +463,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoXicToXiPiPiTable(rho, rhoM); @@ -495,7 +495,7 @@ struct RhoEstimatorTask { continue; } inputParticles.clear(); - jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, &candidate); + jetfindingutilities::analyseTracks(inputParticles, tracks, trackSelection, false, &candidate); auto [rho, rhoM] = bkgSub.estimateRhoAreaMedian(inputParticles, config.doSparse); rhoDielectronTable(rho, rhoM); diff --git a/PWGJE/TableProducer/secondaryVertexReconstruction.cxx b/PWGJE/TableProducer/secondaryVertexReconstruction.cxx index 36c17f77ec5..363538c081b 100644 --- a/PWGJE/TableProducer/secondaryVertexReconstruction.cxx +++ b/PWGJE/TableProducer/secondaryVertexReconstruction.cxx @@ -37,12 +37,11 @@ #include #include #include +#include #include #include #include -#include - #include #include #include @@ -63,12 +62,19 @@ struct SecondaryVertexReconstruction { Produces sv2prongTableData; Produces sv2prongIndicesTableData; + Produces svnprongTableData; + Produces svnprongIndicesTableData; + Produces sv3prongTableMCD; Produces sv3prongIndicesTableMCD; Produces sv2prongTableMCD; Produces sv2prongIndicesTableMCD; + Produces svnprongTableMCD; + Produces svnprongIndicesTableMCD; + + Configurable nProng{"nProng", 3, "number of prong"}; Configurable magneticField{"magneticField", 20.0f, "magnetic field in kG"}; Configurable propagateToPCA{"propagateToPCA", true, "create tracks version propagated to PCA"}; Configurable useAbsDCA{"useAbsDCA", false, "Minimise abs. distance rather than chi2"}; @@ -241,43 +247,77 @@ struct SecondaryVertexReconstruction { std::fill(massArray.begin(), massArray.end(), o2::constants::physics::MassPiPlus); double massSV = RecoDecay::m(arrayMomenta, massArray); + // calculate momentum + double xMomenta = -1; + double yMomenta = -1; + double zMomenta = -1; + if (numProngs == ThreeProngCount) { + xMomenta = arrayMomenta[0][0] + arrayMomenta[1][0] + arrayMomenta[2][0]; + yMomenta = arrayMomenta[0][1] + arrayMomenta[1][1] + arrayMomenta[2][1]; + zMomenta = arrayMomenta[0][2] + arrayMomenta[1][2] + arrayMomenta[2][2]; + } else if (numProngs == TwoProngCount) { + xMomenta = arrayMomenta[0][0] + arrayMomenta[1][0]; + yMomenta = arrayMomenta[0][1] + arrayMomenta[1][1]; + zMomenta = arrayMomenta[0][2] + arrayMomenta[1][2]; + } else { + LOG(error) << "No process momenta\n"; + } + // fill candidate table rows if ((doprocessData3Prongs || doprocessData3ProngsExternalMagneticField) && numProngs == ThreeProngCount) { sv3prongTableData(analysisJet.globalIndex(), primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], - arrayMomenta[0][0] + arrayMomenta[1][0] + arrayMomenta[2][0], - arrayMomenta[0][1] + arrayMomenta[1][1] + arrayMomenta[2][1], - arrayMomenta[0][2] + arrayMomenta[1][2] + arrayMomenta[2][2], + xMomenta, + yMomenta, + zMomenta, energySV, massSV, chi2PCA, dispersion, errorDecayLength, errorDecayLengthXY); svIndices.push_back(sv3prongTableData.lastIndex()); } else if ((doprocessData2Prongs || doprocessData2ProngsExternalMagneticField) && numProngs == TwoProngCount) { sv2prongTableData(analysisJet.globalIndex(), primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], - arrayMomenta[0][0] + arrayMomenta[1][0], - arrayMomenta[0][1] + arrayMomenta[1][1], - arrayMomenta[0][2] + arrayMomenta[1][2], + xMomenta, + yMomenta, + zMomenta, energySV, massSV, chi2PCA, dispersion, errorDecayLength, errorDecayLengthXY); svIndices.push_back(sv2prongTableData.lastIndex()); + } else if ((doprocessDataNProngs || doprocessDataNProngsExternalMagneticField)) { + svnprongTableData(analysisJet.globalIndex(), + primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), + secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], + xMomenta, + yMomenta, + zMomenta, + energySV, massSV, chi2PCA, dispersion, errorDecayLength, errorDecayLengthXY); + svIndices.push_back(svnprongTableData.lastIndex()); } else if ((doprocessMCD3Prongs || doprocessMCD3ProngsExternalMagneticField) && numProngs == ThreeProngCount) { sv3prongTableMCD(analysisJet.globalIndex(), primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], - arrayMomenta[0][0] + arrayMomenta[1][0] + arrayMomenta[2][0], - arrayMomenta[0][1] + arrayMomenta[1][1] + arrayMomenta[2][1], - arrayMomenta[0][2] + arrayMomenta[1][2] + arrayMomenta[2][2], + xMomenta, + yMomenta, + zMomenta, energySV, massSV, chi2PCA, dispersion, errorDecayLength, errorDecayLengthXY); svIndices.push_back(sv3prongTableMCD.lastIndex()); } else if ((doprocessMCD2Prongs || doprocessMCD2ProngsExternalMagneticField) && numProngs == TwoProngCount) { sv2prongTableMCD(analysisJet.globalIndex(), primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], - arrayMomenta[0][0] + arrayMomenta[1][0], - arrayMomenta[0][1] + arrayMomenta[1][1], - arrayMomenta[0][2] + arrayMomenta[1][2], + xMomenta, + yMomenta, + zMomenta, energySV, massSV, chi2PCA, dispersion, errorDecayLength, errorDecayLengthXY); svIndices.push_back(sv2prongTableMCD.lastIndex()); + } else if (doprocessMCDNProngs || doprocessMCDNProngsExternalMagneticField) { + svnprongTableMCD(analysisJet.globalIndex(), + primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ(), + secondaryVertex[0], secondaryVertex[1], secondaryVertex[2], + xMomenta, + yMomenta, + zMomenta, + energySV, massSV, chi2PCA, dispersion, errorDecayLength, errorDecayLengthXY); + svIndices.push_back(svnprongTableMCD.lastIndex()); } else { LOG(error) << "No process specified\n"; } @@ -357,6 +397,40 @@ struct SecondaryVertexReconstruction { } PROCESS_SWITCH(SecondaryVertexReconstruction, processData2ProngsExternalMagneticField, "Reconstruct the data 2-prong secondary vertex with extrernal magnetic field", false); + void processDataNProngs(JetCollisionwPIs::iterator const& collision, aod::Collisions const& /*realColl*/, soa::Join const& jets, JetTracksData const& tracks, OriginalTracks const& /*tracks*/, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + { + for (const auto& jet : jets) { + std::vector svIndices; + if (nProng == ThreeProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df3); + svnprongIndicesTableData(svIndices); + } else if (nProng == TwoProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df2); + svnprongIndicesTableData(svIndices); + } else { + LOG(error) << "set number of prong\n"; + } + } + } + PROCESS_SWITCH(SecondaryVertexReconstruction, processDataNProngs, "Reconstruct the data mult-prong secondary vertex", false); + + void processDataNProngsExternalMagneticField(JetCollisionwPIs::iterator const& collision, aod::Collisions const& /*realColl*/, soa::Join const& jets, JetTracksData const& tracks, OriginalTracks const& /*tracks*/) + { + for (const auto& jet : jets) { + std::vector svIndices; + if (nProng == ThreeProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df3); + svnprongIndicesTableData(svIndices); + } else if (nProng == TwoProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df2); + svnprongIndicesTableData(svIndices); + } else { + LOG(error) << "set number of prong\n"; + } + } + } + PROCESS_SWITCH(SecondaryVertexReconstruction, processDataNProngsExternalMagneticField, "Reconstruct the data mult-prong secondary vertex with extrernal magnetic field", false); + void processMCD3Prongs(JetCollisionwPIs::iterator const& collision, aod::Collisions const& /*realColl*/, soa::Join const& mcdjets, JetTracksMCDwPIs const& tracks, OriginalTracks const& /*tracks*/, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) { for (const auto& jet : mcdjets) { @@ -396,6 +470,40 @@ struct SecondaryVertexReconstruction { } } PROCESS_SWITCH(SecondaryVertexReconstruction, processMCD2ProngsExternalMagneticField, "Reconstruct the MCD 2-prong secondary vertex with external magnetic field", false); + + void processMCDNProngs(JetCollisionwPIs::iterator const& collision, aod::Collisions const& /*realColl*/, soa::Join const& mcdjets, JetTracksMCDwPIs const& tracks, OriginalTracks const& /*tracks*/, aod::BCsWithTimestamps const& /*bcWithTimeStamps*/) + { + for (const auto& jet : mcdjets) { + std::vector svIndices; + if (nProng == ThreeProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df3); + svnprongIndicesTableMCD(svIndices); + } else if (nProng == TwoProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df2); + svnprongIndicesTableMCD(svIndices); + } else { + LOG(error) << "set number of prong\n"; + } + } + } + PROCESS_SWITCH(SecondaryVertexReconstruction, processMCDNProngs, "Reconstruct the MCD n-prong secondary vertex", false); + + void processMCDNProngsExternalMagneticField(JetCollisionwPIs::iterator const& collision, aod::Collisions const& /*realColl*/, soa::Join const& mcdjets, JetTracksMCDwPIs const& tracks, OriginalTracks const& /*tracks*/) + { + for (const auto& jet : mcdjets) { + std::vector svIndices; + if (nProng == ThreeProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df3); + svnprongIndicesTableMCD(svIndices); + } else if (nProng == TwoProngCount) { + runCreatorNProng(collision.template collision_as(), jet, tracks, svIndices, df2); + svnprongIndicesTableMCD(svIndices); + } else { + LOG(error) << "set number of prong\n"; + } + } + } + PROCESS_SWITCH(SecondaryVertexReconstruction, processMCDNProngsExternalMagneticField, "Reconstruct the MCD n-prong secondary vertex with external magnetic field", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGJE/TableProducer/slimTablesProducer.cxx b/PWGJE/TableProducer/slimTablesProducer.cxx index a346a1404f2..5cff154999b 100644 --- a/PWGJE/TableProducer/slimTablesProducer.cxx +++ b/PWGJE/TableProducer/slimTablesProducer.cxx @@ -17,21 +17,21 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" -#include "Common/Core/TrackSelection.h" -#include "Common/Core/TrackSelectionDefaults.h" -#include "Common/DataModel/TrackSelectionTables.h" - #include #include #include #include #include +#include +#include #include #include +#include #include -#include +#include +#include #include #include @@ -108,9 +108,10 @@ struct SlimTablesProducer { Configurable trackSelections{"trackSelections", "globalTracks", "set track selections; other option: uniformTracks"}; Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events; jet-level rejection can also be applied at the jet finder level for jets only, here rejection is applied for collision and track process functions for the first time, and on jets in case it was set to false at the jet finder level"}; Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; - + Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum track occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; + Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum track occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; std::vector eventSelectionBits; - Service pdgDatabase; + Service pdgDatabase{}; int trackSelection = -1; bool doSumw2 = false; @@ -122,15 +123,19 @@ struct SlimTablesProducer { histos.add("h_collisions", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}); histos.add("h2_centrality_collisions", "event status vs. centrality;entries;centrality", {HistType::kTH2F, {centralityAxis, {4, 0.0, 4.0}}}, doSumw2); + histos.add("h_occupancy_raw", "occupancy;occupancy;entries", {HistType::kTH1F, {{200, -10, 10000}}}); + auto hColl = histos.get(HIST("h_collisions")); hColl->GetXaxis()->SetBinLabel(1, "All"); hColl->GetXaxis()->SetBinLabel(2, "eventSelection"); + hColl->GetXaxis()->SetBinLabel(3, "occupancycut"); histos.add("h_mcCollMCD_counts_weight", "MC event status;event status;weighted entries", {HistType::kTH1F, {{5, 0.0, 5.0}}}); auto hMCD = histos.get(HIST("h_mcCollMCD_counts_weight")); hMCD->GetXaxis()->SetBinLabel(1, "All"); hMCD->GetXaxis()->SetBinLabel(2, "hasMcCollision"); hMCD->GetXaxis()->SetBinLabel(3, "selectCollision"); + hMCD->GetXaxis()->SetBinLabel(4, "occupancycut"); histos.add("h_mcCollMCP_counts_weight", "MC event status;event status;weighted entries", {HistType::kTH1F, {{7, 0.0, 7.0}}}); auto hMCP = histos.get(HIST("h_mcCollMCP_counts_weight")); @@ -141,8 +146,8 @@ struct SlimTablesProducer { histos.add("Ntracks_pT", "track pT distribution;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{200, -0.5, 199.5}}}, doSumw2); histos.add("Nparticles_pT", "particle pT distribution;#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {{200, -0.5, 199.5}}}, doSumw2); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } Produces slimCollisions; @@ -171,6 +176,11 @@ struct SlimTablesProducer { return; } histos.fill(HIST("h_collisions"), 1.5); + histos.fill(HIST("h_occupancy_raw"), collision.trackOccupancyInTimeRange()); + if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { + return; + } + histos.fill(HIST("h_collisions"), 2.5); slimCollisions(collision.posZ(), collision.collisionTime(), 1.0); auto slimCollIndex = slimCollisions.lastIndex(); for (const auto& track : tracks) { @@ -211,25 +221,35 @@ struct SlimTablesProducer { continue; } histos.fill(HIST("h_mcCollMCD_counts_weight"), 2.5, eventWeightMC); + histos.fill(HIST("h_occupancy_raw"), collision.trackOccupancyInTimeRange()); + // occupancy cut only applied to the reconstructed (MCD) collision, not to the mcCollision + if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { + continue; + } + histos.fill(HIST("h_mcCollMCD_counts_weight"), 3.5, eventWeightMC); slimCollisions(collision.posZ(), collision.collisionTime(), eventWeight); auto slimCollIndex = slimCollisions.lastIndex(); auto slicedTracks = tracks.sliceBy(perCollisionTracks, collision.globalIndex()); // tracks associated to the rec collision for (const auto& track : slicedTracks) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; + } histos.fill(HIST("Ntracks_pT"), track.pt(), eventWeight); slimTracks(slimCollIndex, track.px(), track.py(), track.pz()); } slimMcCollisions(mccollision.posZ(), eventWeightMC); auto slimMcCollIndex = slimMcCollisions.lastIndex(); for (const auto& particle : particles) { - if (!particle.isPhysicalPrimary()) + if (!particle.isPhysicalPrimary()) { continue; + } auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); - if (!pdgParticle) + if (!pdgParticle) { continue; - if (pdgParticle->Charge() == 0) // keep charged particles, exclude neutrals + } + if (pdgParticle->Charge() == 0) { // keep charged particles, exclude neutrals continue; + } histos.fill(HIST("Nparticles_pT"), particle.pt(), eventWeightMC); slimParticles(slimMcCollIndex, particle.px(), particle.py(), particle.pz(), particle.energy()); } diff --git a/PWGJE/Tasks/CMakeLists.txt b/PWGJE/Tasks/CMakeLists.txt index 0275a7a66ed..8a0b245b11e 100644 --- a/PWGJE/Tasks/CMakeLists.txt +++ b/PWGJE/Tasks/CMakeLists.txt @@ -9,6 +9,12 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. add_library(JetSubstructureHFPCH OBJECT jetSubstructureHFPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetSubstructureHFPCH PROPERTY JOB_POOL_COMPILE analysis) target_link_libraries(JetSubstructureHFPCH PUBLIC O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore) if(NOT DEFINED ENV{USE_RECC}) @@ -28,6 +34,32 @@ target_precompile_headers(JetSubstructureHFPCH PRIVATE ) endif() +add_library(JetSubstructureHFOutputPCH OBJECT jetSubstructureHFOutputPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET JetSubstructureHFOutputPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(JetSubstructureHFOutputPCH + PUBLIC O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) +target_precompile_headers(JetSubstructureHFOutputPCH PRIVATE + [["PWGHF/DataModel/DerivedTables.h"]] + [["PWGJE/DataModel/Jet.h"]] + [["PWGJE/DataModel/JetSubstructure.h"]] + [["PWGJE/DataModel/JetSubtraction.h"]] + [["PWGJE/Tasks/jetSubstructureHFOutput.h"]] + + + + + + + +) +endif() + o2physics_add_dpl_workflow(emc-cellmonitor SOURCES emcCellMonitor.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::AnalysisCore @@ -66,6 +98,7 @@ o2physics_add_dpl_workflow(photon-isolation-qa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(photon-charged-trigger-correlation SOURCES photonChargedTriggerCorrelation.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-emc-extensive-mc-qa @@ -74,16 +107,19 @@ o2physics_add_dpl_workflow(task-emc-extensive-mc-qa COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(photon-charged-trigger-producer SOURCES photonChargedTriggerProducer.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hadron-photon-correlation SOURCES hadronPhotonCorrelation.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-background-analysis SOURCES jetBackgroundAnalysis.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure @@ -93,6 +129,7 @@ if(FastJet_FOUND) COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-output SOURCES jetSubstructureOutput.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-d0 @@ -143,268 +180,368 @@ if(FastJet_FOUND) o2physics_add_dpl_workflow(jet-substructure-d0-output SOURCES jetSubstructureD0Output.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-dplus-output SOURCES jetSubstructureDplusOutput.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-ds-output SOURCES jetSubstructureDsOutput.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-dstar-output SOURCES jetSubstructureDstarOutput.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-lc-output SOURCES jetSubstructureLcOutput.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-b0-output SOURCES jetSubstructureB0Output.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-bplus-output SOURCES jetSubstructureBplusOutput.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-xictoxipipi-output SOURCES jetSubstructureXicToXiPiPiOutput.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-substructure-dielectron-output SOURCES jetSubstructureDielectronOutput.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFOutputPCH COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-fragmentation SOURCES jetFragmentation.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-v0-spectra SOURCES v0JetSpectra.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-v0qa SOURCES v0QA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-charged-qa SOURCES jetFinderQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-outlier-qa SOURCES jetOutlierQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-charged-v2 SOURCES jetChargedV2.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-d0-qa SOURCES jetFinderD0QA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dplus-qa SOURCES jetFinderDplusQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-ds-qa SOURCES jetFinderDsQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dstar-qa SOURCES jetFinderDstarQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-lc-qa SOURCES jetFinderLcQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-b0-qa SOURCES jetFinderB0QA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-bplus-qa SOURCES jetFinderBplusQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-xictoxipipi-qa SOURCES jetFinderXicToXiPiPiQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-dielectron-qa SOURCES jetFinderDielectronQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-full-qa SOURCES jetFinderFullQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-finder-v0-qa SOURCES jetFinderV0QA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-spectra-charged SOURCES jetSpectraCharged.cxx + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-spectra-charged-gen + SOURCES jetSpectraChargedGen.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(charged-jet-hadron SOURCES chargedJetHadron.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(trigger-correlations SOURCES triggerCorrelations.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-trigger-charged-qa SOURCES jetTriggerChargedQa.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-full-trigger-qa SOURCES fullJetTriggerQATask.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-matching-qa SOURCES jetMatchingQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-validation-qa SOURCES jetValidationQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-tutorial SOURCES jetTutorial.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-tutorial-skeleton SOURCES jetTutorialSkeleton.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(track-jet-qa SOURCES trackJetQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(track-efficiency SOURCES trackEfficiency.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-hadron-recoil SOURCES jetHadronRecoil.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(recoil-jets SOURCES recoilJets.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-nsubjettiness SOURCES nsubjettiness.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(phi-in-jets SOURCES phiInJets.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(nuclei-in-jets SOURCES nucleiInJets.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-taggerhf-qa SOURCES jetTaggerHFQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-lund-reclustering SOURCES jetLundReclustering.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore FastJet::FastJet FastJet::Contrib COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-lund-plane SOURCES jetLundPlane.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore FastJet::FastJet FastJet::Contrib COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hf-fragmentation-function SOURCES hfFragmentationFunction.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-planarflow SOURCES jetPlanarFlow.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-ch-corr SOURCES jetChCorr.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(bjet-tree-creator SOURCES bjetTreeCreator.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stat-prompt-photon SOURCES statPromptPhoton.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(full-jet-spectra SOURCES fullJetSpectra.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(bjet-tagging-ml SOURCES bjetTaggingML.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-spectra-ese SOURCES jetSpectraEseTask.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-cross-section-efficiency SOURCES jetCrossSectionEfficiency.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsITSMFT O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(gamma-jet-tree-producer SOURCES gammaJetTreeProducer.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2::EMCALCalib O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(dijet-finder-charged-qa SOURCES dijetFinderQA.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(bjet-tagging-gnn SOURCES bjetTaggingGnn.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-shape SOURCES jetShape.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-formationtimereclustering SOURCES jetFormationTimeReclustering.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-correlation-d0 SOURCES jetCorrelationD0.cxx + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-correlation-dmeson + SOURCES jetCorrelationDmeson.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(hf-debug SOURCES hfDebug.cxx + REUSE_FROM JetSubstructureHFOutputPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-debug SOURCES jetDebug.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(substructure-debug SOURCES substructureDebug.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) - o2physics_add_dpl_workflow(jet-ds-spectrum-subs - SOURCES jetDsSpectrumAndSubstructure.cxx + o2physics_add_dpl_workflow(jet-ds-spec-subs + SOURCES jetDsSpecSubs.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(jet-d0-ang-substructure SOURCES jetD0AngSubstructure.cxx + REUSE_FROM JetSubstructureHFPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(bjet-cent-mult SOURCES bjetCentMult.cxx + REUSE_FROM JetSubstructureHFPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-hadrons-pid + SOURCES jetHadronsPid.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-hf-ang-substructure + SOURCES jetHFAngularity.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(jet-upc-qa + SOURCES jetUpcQa.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils O2Physics::SGCutParHolder + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(embedding-qa + SOURCES embeddingQA.cxx + REUSE_FROM JetDataModelPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(baryon-pair-in-jets + SOURCES baryonPairInJets.cxx + REUSE_FROM JetDataModelPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::PWGJECore O2Physics::AnalysisCore COMPONENT_NAME Analysis) endif() diff --git a/PWGJE/Tasks/baryonPairInJets.cxx b/PWGJE/Tasks/baryonPairInJets.cxx new file mode 100644 index 00000000000..5f17ed90de2 --- /dev/null +++ b/PWGJE/Tasks/baryonPairInJets.cxx @@ -0,0 +1,1691 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file baryonPairInJets.cxx +/// \brief Identified proton-pair correlations in charged jets +/// \author Hanseo Park + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using BaryonTracks = soa::Join; +using BaryonTracksMC = soa::Join; +using JetTracksWithOriginal = soa::Join; +using JetTracksWithOriginalAndMC = soa::Join; +using ChargedJetsWithConstituents = soa::Join; +using ChargedMCDJetsWithConstituents = soa::Join; +using ChargedMCPJetsWithConstituents = soa::Join; +using MatchedChargedMCDJets = soa::Join; +using MatchedChargedMCPJets = soa::Join; + +struct BaryonPairInJets { + enum class PairType : uint8_t { + SameSign = 0, + OppositeSign = 1 + }; + + enum class ChargePairType : uint8_t { + PositivePositive = 0, + NegativeNegative = 1, + OppositeSign = 2 + }; + + enum class MixedReference : uint8_t { + Nominal = 0, + Loose = 1, + Random = 2 + }; + + enum class ProtonPidMethod : int { + TpcOnly = 0, + TpcTofCircular = 1, + TpcTofRectangular = 2 + }; + + enum class ControlSpecies : uint8_t { + Pion = 0, + Kaon = 1 + }; + + enum class EfficiencyStage : uint8_t { + Generated = 0, + TrackSelected = 1, + PidSelected = 2 + }; + + // Species-independent selected-particle representation. Keep the full + // kinematics needed by pair and future jet-fragmentation observables. + struct SelectedParticle { + float pt; + float eta; + float phi; + int8_t sign; + }; + + struct SelectedParticles { + std::vector baryons; + std::vector pions; + std::vector kaons; + }; + + struct : ConfigurableGroup { + Configurable vertexZMax{"vertexZMax", 10.f, "Maximum absolute collision z vertex (cm)"}; + Configurable eventSelections{"eventSelections", "sel8", "Event selections"}; + Configurable applyRctSelection{"applyRctSelection", true, "Apply the PWGJE hadron-PID RCT selection"}; + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT selection label"}; + Configurable occupancyMin{"occupancyMin", -999999, "Minimum track occupancy in time range"}; + Configurable occupancyMax{"occupancyMax", 999999, "Maximum track occupancy in time range"}; + } eventCuts; + + struct : ConfigurableGroup { + Configurable jetRadius{"jetRadius", 0.4f, "Charged-jet resolution parameter"}; + Configurable jetPtMin{"jetPtMin", 5.f, "Minimum charged-jet transverse momentum (GeV/c); broad input range"}; + Configurable jetPtMax{"jetPtMax", 140.f, "Maximum charged-jet transverse momentum (GeV/c); broad input range"}; + Configurable jetEtaMax{"jetEtaMax", 0.5f, "Maximum absolute charged-jet pseudorapidity"}; + Configurable areaFractionMin{"areaFractionMin", -1.f, "Minimum jet area divided by pi R squared; negative disables the cut"}; + } jetCuts; + + struct : ConfigurableGroup { + Configurable protonPtMin{"protonPtMin", 0.5f, "Minimum proton transverse momentum (GeV/c)"}; + Configurable protonPtMax{"protonPtMax", 4.0f, "Maximum proton transverse momentum (GeV/c)"}; + Configurable protonEtaMax{"protonEtaMax", 0.8f, "Maximum absolute proton pseudorapidity"}; + Configurable requirePvContributor{"requirePvContributor", false, "Require the track to be a PV contributor"}; + Configurable tpcCrossedRowsMin{"tpcCrossedRowsMin", 70, "Minimum number of crossed TPC rows"}; + Configurable tpcChi2Max{"tpcChi2Max", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable itsChi2Max{"itsChi2Max", 36.f, "Maximum ITS chi2 per cluster"}; + Configurable dcaXYMax{"dcaXYMax", 0.2f, "Maximum absolute transverse DCA (cm)"}; + Configurable dcaZMax{"dcaZMax", 0.1f, "Maximum absolute longitudinal DCA (cm)"}; + } trackCuts; + + struct : ConfigurableGroup { + Configurable method{"method", static_cast(ProtonPidMethod::TpcTofCircular), "PID method: 0 TPC only, 1 circular TPC-TOF, 2 rectangular TPC-TOF"}; + Configurable tofPtThreshold{"tofPtThreshold", 0.75f, "Require TOF above this transverse momentum (GeV/c)"}; + Configurable tpcNSigmaMax{"tpcNSigmaMax", 3.f, "Maximum absolute proton TPC n-sigma"}; + Configurable tofNSigmaMax{"tofNSigmaMax", 3.f, "Maximum absolute proton TOF n-sigma"}; + Configurable combinedNSigmaMax{"combinedNSigmaMax", 3.f, "Maximum circular TPC-TOF proton n-sigma"}; + Configurable rejectPionKaon{"rejectPionKaon", false, "Reject candidates compatible with the pion or kaon hypothesis"}; + Configurable rejectionNSigma{"rejectionNSigma", 3.f, "Pion and kaon rejection threshold"}; + Configurable pionTpcNSigmaMax{"pionTpcNSigmaMax", 3.f, "Maximum absolute pion TPC n-sigma"}; + Configurable pionTofNSigmaMax{"pionTofNSigmaMax", 3.f, "Maximum absolute pion TOF n-sigma"}; + Configurable pionCombinedNSigmaMax{"pionCombinedNSigmaMax", 3.f, "Maximum circular TPC-TOF pion n-sigma"}; + Configurable kaonTpcNSigmaMax{"kaonTpcNSigmaMax", 3.f, "Maximum absolute kaon TPC n-sigma"}; + Configurable kaonTofNSigmaMax{"kaonTofNSigmaMax", 3.f, "Maximum absolute kaon TOF n-sigma"}; + Configurable kaonCombinedNSigmaMax{"kaonCombinedNSigmaMax", 3.f, "Maximum circular TPC-TOF kaon n-sigma"}; + } pidCuts; + + struct : ConfigurableGroup { + Configurable numberOfMixedEvents{"numberOfMixedEvents", 5, "Number of mixed-event neighbours"}; + ConfigurableAxis vertexZBins{"vertexZBins", {VARIABLE_WIDTH, -10.f, -5.f, -2.f, 0.f, 2.f, 5.f, 10.f}, "Mixing bins in collision z vertex"}; + ConfigurableAxis multiplicityBins{"multiplicityBins", {VARIABLE_WIDTH, 0.f, 10.f, 20.f, 40.f, 80.f, 150.f, 300.f, 600.f, 2000.f}, "Mixing bins in FT0C multiplicity"}; + Configurable nominalJetPtDifferenceMax{"nominalJetPtDifferenceMax", 2.5f, "Maximum jet-pT difference for nominal mixing (GeV/c)"}; + Configurable nominalJetNchDifferenceMax{"nominalJetNchDifferenceMax", 3, "Maximum jet-constituent multiplicity difference for nominal mixing"}; + Configurable nominalJetEtaDifferenceMax{"nominalJetEtaDifferenceMax", 0.1f, "Maximum jet-eta difference for nominal mixing"}; + Configurable looseJetPtDifferenceMax{"looseJetPtDifferenceMax", 7.5f, "Maximum jet-pT difference for loose mixing (GeV/c)"}; + Configurable looseJetNchDifferenceMax{"looseJetNchDifferenceMax", 8, "Maximum jet-constituent multiplicity difference for loose mixing"}; + Configurable looseJetEtaDifferenceMax{"looseJetEtaDifferenceMax", 0.3f, "Maximum jet-eta difference for loose mixing"}; + } mixing; + + struct : ConfigurableGroup { + Configurable fillQa{"fillQa", true, "Fill event, jet, track, and PID QA histograms"}; + Configurable fillReferenceVariations{"fillReferenceVariations", true, "Fill loose and unmatched mixed-event references"}; + Configurable fillChargeSeparated{"fillChargeSeparated", true, "Fill charge-resolved pair histograms"}; + Configurable fillPionControl{"fillPionControl", false, "Fill same-species pion control correlations"}; + Configurable fillKaonControl{"fillKaonControl", false, "Fill same-species kaon control correlations"}; + } output; + + struct : ConfigurableGroup { + Configurable nearSideDeltaPhiMax{"nearSideDeltaPhiMax", 0.2f, "Maximum absolute delta phi for near-side pair yields"}; + Configurable nearSideDeltaEtaMax{"nearSideDeltaEtaMax", 0.6f, "Maximum absolute delta eta for near-side pair yields"}; + ConfigurableAxis jetPt{"jetPt", {VARIABLE_WIDTH, 0.f, 5.f, 10.f, 15.f, 20.f, 25.f, 30.f, 40.f, 50.f, 60.f, 80.f, 100.f, 120.f, 140.f}, "#it{p}_{T, jet} (GeV/#it{c})"}; + ConfigurableAxis jetEta{"jetEta", {40, -1.f, 1.f}, "#eta_{jet}"}; + ConfigurableAxis jetPhi{"jetPhi", {72, 0.f, o2::constants::math::TwoPI}, "#varphi_{jet}"}; + ConfigurableAxis jetNch{"jetNch", {61, -0.5f, 60.5f}, "#it{N}_{ch}^{jet}"}; + ConfigurableAxis baryonPt{"baryonPt", {50, 0.f, 5.f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis pairPt{"pairPt", {VARIABLE_WIDTH, 0.5f, 0.6f, 0.75f, 1.f, 1.25f, 1.5f, 2.f, 2.5f, 3.f, 4.f, 5.f}, "Pair-particle #it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis baryonEta{"baryonEta", {48, -1.2f, 1.2f}, "#eta"}; + ConfigurableAxis baryonRapidity{"baryonRapidity", {48, -1.2f, 1.2f}, "#it{y}"}; + ConfigurableAxis nSigma{"nSigma", {100, -5.f, 5.f}, "n#sigma"}; + ConfigurableAxis dca{"dca", {100, -0.5f, 0.5f}, "DCA (cm)"}; + ConfigurableAxis deltaEta{"deltaEta", {80, -0.8f, 0.8f}, "#Delta#eta"}; + ConfigurableAxis deltaPhi{"deltaPhi", {80, -0.8f, 0.8f}, "#Delta#varphi"}; + ConfigurableAxis deltaR{"deltaR", {40, 0.f, 0.8f}, "#Delta#it{R}"}; + ConfigurableAxis particleJetDistance{"particleJetDistance", {40, 0.f, 0.4f}, "#Delta#it{R}(p, particle-level jet)"}; + ConfigurableAxis inclusiveDeltaEta{"inclusiveDeltaEta", {64, -1.6f, 1.6f}, "#Delta#eta"}; + ConfigurableAxis inclusiveDeltaPhi{"inclusiveDeltaPhi", {72, -o2::constants::math::PIHalf, 3.f * o2::constants::math::PIHalf}, "#Delta#varphi"}; + } axes; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + std::vector eventSelectionBits; + o2::aod::rctsel::RCTFlagsChecker inclusiveRctChecker; + SliceCache mixingCache; + SliceCache mcdMixingCache; + SliceCache mcpMixingCache; + SliceCache inclusiveMixingCache; + Preslice particleJetsPerMcCollision = aod::jet::mcCollisionId; + using MixingBinning = ColumnBinningPolicy; + using MCPMixingBinning = ColumnBinningPolicy; + using InclusiveSameEventCollisions = soa::Join; + using InclusiveMixedEventCollisions = soa::Join; + using InclusiveMixingBinning = ColumnBinningPolicy; + + void init(InitContext const&) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventCuts.eventSelections)); + inclusiveRctChecker.init(static_cast(eventCuts.rctLabel)); + + AxisSpec axisPairType{2, -0.5, 1.5, "pair type"}; + AxisSpec axisChargePairType{3, -0.5, 2.5, "charge pair type"}; + AxisSpec axisCounter{8, 0.5, 8.5, "counter"}; + AxisSpec axisMultiplicity{11, -0.5, 10.5, "multiplicity per jet"}; + AxisSpec axisJetPt{axes.jetPt}; + AxisSpec axisJetEta{axes.jetEta}; + AxisSpec axisJetPhi{axes.jetPhi}; + AxisSpec axisJetNch{axes.jetNch}; + AxisSpec axisBaryonPt{axes.baryonPt}; + AxisSpec axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; + AxisSpec axisBaryonEta{axes.baryonEta}; + AxisSpec axisBaryonRapidity{axes.baryonRapidity}; + AxisSpec axisNSigma{axes.nSigma}; + AxisSpec axisDca{axes.dca}; + AxisSpec axisDeltaEta{axes.deltaEta}; + AxisSpec axisDeltaPhi{axes.deltaPhi}; + AxisSpec axisDeltaR{axes.deltaR}; + AxisSpec axisParticleJetDistance{axes.particleJetDistance}; + AxisSpec axisInclusiveDeltaEta{axes.inclusiveDeltaEta}; + AxisSpec axisInclusiveDeltaPhi{axes.inclusiveDeltaPhi}; + AxisSpec axisOrigin{3, 0.5, 3.5, "origin"}; + + registry.add("event/cutFlow", "Event selection;selection step;entries", HistType::kTH1F, {axisCounter}); + if (output.fillQa.value) { + registry.add("event/zVertex", "Selected collision z vertex;#it{z}_{vtx} (cm);entries", HistType::kTH1F, {{80, -20.f, 20.f}}); + registry.add("event/multiplicityFT0C", "Selected collision FT0C multiplicity;FT0C multiplicity;entries", HistType::kTH1F, {{200, 0.f, 2000.f}}); + + registry.add("jet/ptBeforePtSelection", "Charged jets before the jet-pT selection;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH1F, {axisJetPt}); + registry.add("jet/pt", "Selected charged jets;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH1F, {axisJetPt}); + registry.add("jet/eta", "Selected charged jets;#eta_{jet};entries", HistType::kTH1F, {axisJetEta}); + registry.add("jet/phi", "Selected charged jets;#varphi_{jet};entries", HistType::kTH1F, {axisJetPhi}); + registry.add("jet/nConstituents", "Charged-jet constituent multiplicity;#it{N}_{ch}^{jet};entries", HistType::kTH1F, {axisJetNch}); + registry.add("jet/nSelectedBaryons", "Selected baryon multiplicity per jet;#it{N}_{p+#bar{p}};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nProtons", "Selected proton multiplicity per jet;#it{N}_{p};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nAntiprotons", "Selected antiproton multiplicity per jet;#it{N}_{#bar{p}};jets", HistType::kTH1F, {axisMultiplicity}); + registry.add("jet/nProtonsVsAntiprotons", "Proton and antiproton multiplicities per jet;#it{N}_{p};#it{N}_{#bar{p}}", HistType::kTH2F, {axisMultiplicity, axisMultiplicity}); + registry.add("jet/nConstituentsVsPt", "Constituent multiplicity versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});#it{N}_{ch}^{jet}", HistType::kTH2F, {axisJetPt, axisJetNch}); + registry.add("jet/nBaryonsVsPt", "Selected baryon multiplicity versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});#it{N}_{p+#bar{p}}", HistType::kTH2F, {axisJetPt, axisMultiplicity}); + registry.add("jet/sameSignPairsVsPt", "Same-sign pair count versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});pairs", HistType::kTH1F, {axisJetPt}); + registry.add("jet/oppositeSignPairsVsPt", "Opposite-sign pair count versus jet momentum;#it{p}_{T,jet} (GeV/#it{c});pairs", HistType::kTH1F, {axisJetPt}); + + registry.add("proton/pt", "Selected proton and antiproton candidates;#it{p}_{T} (GeV/#it{c});entries", HistType::kTH1F, {axisBaryonPt}); + registry.add("proton/eta", "Selected proton and antiproton candidates;#eta;entries", HistType::kTH1F, {axisBaryonEta}); + registry.add("proton/rapidity", "Selected proton and antiproton candidates;#it{y};entries", HistType::kTH1F, {axisBaryonRapidity}); + registry.add("proton/tpcNSigmaVsPt", "Proton TPC PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("proton/tofNSigmaVsPt", "Proton TOF PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("proton/dcaXYVsPt", "Selected proton transverse DCA;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axisBaryonPt, axisDca}); + registry.add("proton/dcaZVsPt", "Selected proton longitudinal DCA;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axisBaryonPt, axisDca}); + registry.add("pid/before/tpcSignalVsSignedP", "TPC response before proton PID;charge #times #it{p} (GeV/#it{c});TPC signal", HistType::kTH2F, {{120, -6.f, 6.f}, {200, 0.f, 200.f}}); + registry.add("pid/before/tofBetaVsSignedP", "TOF response before proton PID;charge #times #it{p} (GeV/#it{c});#beta", HistType::kTH2F, {{120, -6.f, 6.f}, {120, 0.f, 1.2f}}); + registry.add("pid/before/tpcNSigmaPiVsPt", "Pion TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{#pi}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tpcNSigmaKaVsPt", "Kaon TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{K}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tpcNSigmaPrVsPt", "Proton TPC PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaPiVsPt", "Pion TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{#pi}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaKaVsPt", "Kaon TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{K}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/before/tofNSigmaPrVsPt", "Proton TOF PID before proton PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}^{p}", HistType::kTH2F, {axisBaryonPt, axisNSigma}); + registry.add("pid/tofMatchDenominator", "Tracks entering PID QA;#it{p}_{T} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("pid/tofMatchNumerator", "Tracks with TOF;#it{p}_{T} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("pid/selectedPtByCharge", "Selected proton candidates;charge;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{2, -1.5f, 1.5f}, axisBaryonPt}); + } + + addPairHistograms("sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/nominal", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/loose", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mixedEvent/random", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + registry.add("sameEvent/chargePairSparse", "Charge-separated baryon pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/nominal/chargePairSparse", "Charge-separated nominal mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/loose/chargePairSparse", "Charge-separated loose mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mixedEvent/random/chargePairSparse", "Charge-separated random mixed pairs;charge pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + + addInclusivePairHistograms("inclusive/sameEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); + addInclusivePairHistograms("inclusive/mixedEvent", axisChargePairType, axisInclusiveDeltaEta, axisInclusiveDeltaPhi); + registry.add("inclusive/event/multiplicity", "Inclusive selected-event multiplicity;FT0C multiplicity;events", HistType::kTH1F, {{200, 0.f, 2000.f}}); + registry.add("inclusive/normalization/sameEvent", "Inclusive same-event normalization;counter;entries", HistType::kTH1F, {axisCounter}); + registry.add("inclusive/normalization/mixedEvent", "Inclusive mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + + registry.add("normalization/sameEvent", "Same-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventNominal", "Nominal mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventLoose", "Loose mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("normalization/mixedEventRandom", "Random mixed-event normalization counters;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("mixedEvent/jetPtDifference", "Mixed-jet matching QA;|#Delta#it{p}_{T,jet}| (GeV/#it{c});jet pairs", HistType::kTH1F, {{80, 0.f, 40.f}}); + registry.add("mixedEvent/jetEtaDifference", "Mixed-jet matching QA;|#Delta#eta_{jet}|;jet pairs", HistType::kTH1F, {{40, 0.f, 1.f}}); + registry.add("mixedEvent/jetNchDifference", "Mixed-jet matching QA;|#Delta#it{N}_{ch}^{jet}|;jet pairs", HistType::kTH1F, {{61, -0.5f, 60.5f}}); + + registry.add("mc/jet/allDetectorPt", "Accepted detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/allParticlePt", "Accepted particle-level charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/matchedDetectorPt", "Matched detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/matchedParticlePt", "Matched particle-level charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/fakeDetectorPt", "Unmatched detector-level charged jets;#it{p}_{T,jet}^{det} (GeV/#it{c});jets", HistType::kTH1F, {axisJetPt}); + registry.add("mc/jet/responsePt", "Charged-jet response;#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c})", HistType::kTH2F, {axisJetPt, axisJetPt}); + registry.add("mc/jet/relativePtResolution", "Charged-jet momentum resolution;#it{p}_{T,jet}^{part} (GeV/#it{c});(#it{p}_{T}^{det}-#it{p}_{T}^{part})/#it{p}_{T}^{part}", HistType::kTH2F, {axisJetPt, {120, -1.5f, 1.5f}}); + registry.add("mc/jet/matchingDeltaR", "Detector-particle jet matching distance;#Delta#it{R}_{match};matched jets", HistType::kTH1F, {{100, 0.f, 1.f}}); + registry.add("mc/pid/generatedPrimaryProtonPt", "Generated primary protons in accepted particle-level jets;#it{p}_{T}^{part} (GeV/#it{c});particles", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/reconstructedTrueProtonPt", "Reconstructed primary true protons before PID;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/selectedTrueProtonPt", "PID-selected primary true protons;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisBaryonPt}); + registry.add("mc/pid/selectedCandidateOriginVsPt", "Selected proton-candidate origin;#it{p}_{T}^{rec} (GeV/#it{c});origin", HistType::kTH2F, {axisBaryonPt, axisOrigin}); + registry.add("mc/pid/selectedCandidateTruePdgVsPt", "Selected proton-candidate contamination;#it{p}_{T}^{rec} (GeV/#it{c});true PDG code", HistType::kTH2F, {axisBaryonPt, {801, -4005.f, 4005.f}}); + registry.add("mc/pid/ptResponsePrimaryProton", "Primary-proton momentum response;#it{p}_{T}^{part} (GeV/#it{c});#it{p}_{T}^{rec} (GeV/#it{c})", HistType::kTH2F, {axisBaryonPt, axisBaryonPt}); + AxisSpec const axisCharge{2, -1.5f, 1.5f, "charge"}; + registry.add("mc/efficiency/generatedPrimaryProton", "Generated primary protons in matched particle-level jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/trackSelectedPrimaryProton", "Track-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/tofMatchedPrimaryProton", "TOF-matched primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/pidSelectedPrimaryProton", "PID-selected primary protons in matched jets;charge;#it{p}_{T}^{truth} (GeV/#it{c});#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#it{R}(p,jet_{part})", HistType::kTHnSparseF, {axisCharge, axisBaryonPt, axisJetPt, axisParticleJetDistance}); + registry.add("mc/efficiency/generatedPair", "Generated primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mc/efficiency/trackSelectedPair", "Track-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + registry.add("mc/efficiency/pidSelectedPair", "PID-selected primary-proton pairs in matched jets;pair type;#it{p}_{T,jet}^{det} (GeV/#it{c});#Delta#varphi^{truth};#Delta#it{R}^{truth};#it{p}_{T}^{low} (GeV/#it{c});#it{p}_{T}^{high} (GeV/#it{c})", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + addPairHistograms("mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms("mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + registry.add("mc/particle/normalizationSameEvent", "Particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + registry.add("mc/particle/normalizationMixedEvent", "Particle-level mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + + if (output.fillPionControl.value) { + addControlSpeciesHistograms("control/pion", "Pion", axisPairType, axisCounter, axisJetPt, axisBaryonPt, axisNSigma, axisOrigin, axisDeltaEta, axisDeltaPhi, axisDeltaR); + } + if (output.fillKaonControl.value) { + addControlSpeciesHistograms("control/kaon", "Kaon", axisPairType, axisCounter, axisJetPt, axisBaryonPt, axisNSigma, axisOrigin, axisDeltaEta, axisDeltaPhi, axisDeltaR); + } + + setHistogramLabels(); + } + + void addControlSpeciesHistograms(std::string const& directory, + std::string const& speciesName, + AxisSpec const& axisPairType, + AxisSpec const& axisCounter, + AxisSpec const& axisJetPt, + AxisSpec const& axisParticlePt, + AxisSpec const& axisNSigma, + AxisSpec const& axisOrigin, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi, + AxisSpec const& axisDeltaR) + { + auto addHistogram = [&](std::string const& suffix, std::string const& title, HistType type, std::vector const& histogramAxes) { + registry.add(directory + suffix, title.c_str(), type, histogramAxes); + }; + AxisSpec const axisPdg{801, -4005.f, 4005.f, "true PDG code"}; + addPairHistograms(directory + "/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms(directory + "/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addHistogram("/normalizationSameEvent", speciesName + " same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + addHistogram("/normalizationMixedEvent", speciesName + " mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + if (output.fillQa.value) { + addHistogram("/qa/pt", "Selected " + speciesName + " candidates;#it{p}_{T} (GeV/#it{c});entries", HistType::kTH1F, {axisParticlePt}); + addHistogram("/qa/tpcNSigmaVsPt", speciesName + " TPC PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axisParticlePt, axisNSigma}); + addHistogram("/qa/tofNSigmaVsPt", speciesName + " TOF PID;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axisParticlePt, axisNSigma}); + } + addHistogram("/mc/generatedPrimaryPt", "Generated primary " + speciesName + " candidates;#it{p}_{T}^{part} (GeV/#it{c});particles", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/reconstructedTruePt", "Reconstructed true primary " + speciesName + " candidates;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/selectedTruePt", "PID-selected true primary " + speciesName + " candidates;#it{p}_{T}^{rec} (GeV/#it{c});tracks", HistType::kTH1F, {axisParticlePt}); + addHistogram("/mc/selectedCandidateOriginVsPt", "Selected " + speciesName + " candidate origin;#it{p}_{T}^{rec} (GeV/#it{c});origin", HistType::kTH2F, {axisParticlePt, axisOrigin}); + addHistogram("/mc/selectedCandidateTruePdgVsPt", "Selected " + speciesName + " candidate contamination;#it{p}_{T}^{rec} (GeV/#it{c});true PDG code", HistType::kTH2F, {axisParticlePt, axisPdg}); + addHistogram("/mc/ptResponsePrimary", "Primary " + speciesName + " momentum response;#it{p}_{T}^{part} (GeV/#it{c});#it{p}_{T}^{rec} (GeV/#it{c})", HistType::kTH2F, {axisParticlePt, axisParticlePt}); + addPairHistograms(directory + "/mc/particle/sameEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addPairHistograms(directory + "/mc/particle/mixedEvent", axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR); + addHistogram("/mc/particle/normalizationSameEvent", speciesName + " particle-level same-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + addHistogram("/mc/particle/normalizationMixedEvent", speciesName + " particle-level mixed-event normalization;counter;weighted entries", HistType::kTH1F, {axisCounter}); + } + + void addPairHistograms(std::string const& directory, + AxisSpec const& axisPairType, + AxisSpec const& axisJetPt, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi, + AxisSpec const& axisDeltaR) + { + AxisSpec const axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec const axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; + registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/sameSignDeltaR", "Same-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); + registry.add(directory + "/oppositeSignDeltaR", "Opposite-sign baryon pairs;#Delta#it{R};weighted pairs", HistType::kTH1F, {axisDeltaR}); + registry.add(directory + "/pairSparse", "Baryon-pair distribution;pair type;#it{p}_{T,jet};#Delta#eta;#Delta#varphi;#Delta#it{R};#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisPairType, axisJetPt, axisDeltaEta, axisDeltaPhi, axisDeltaR, axisPairPtLow, axisPairPtHigh}); + } + + void addInclusivePairHistograms(std::string const& directory, + AxisSpec const& axisChargePairType, + AxisSpec const& axisDeltaEta, + AxisSpec const& axisDeltaPhi) + { + AxisSpec const axisPairPtLow{axes.pairPt, "#it{p}_{T}^{low} (GeV/#it{c})"}; + AxisSpec const axisPairPtHigh{axes.pairPt, "#it{p}_{T}^{high} (GeV/#it{c})"}; + registry.add(directory + "/sameSignDeltaEtaDeltaPhi", "Inclusive same-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/oppositeSignDeltaEtaDeltaPhi", "Inclusive opposite-sign baryon pairs;#Delta#eta;#Delta#varphi", HistType::kTH2F, {axisDeltaEta, axisDeltaPhi}); + registry.add(directory + "/chargePairSparse", "Inclusive charge-separated baryon pairs;charge pair type;#Delta#eta;#Delta#varphi;#it{p}_{T}^{low};#it{p}_{T}^{high}", HistType::kTHnSparseF, {axisChargePairType, axisDeltaEta, axisDeltaPhi, axisPairPtLow, axisPairPtHigh}); + } + + void setHistogramLabels() + { + auto eventCutFlow = registry.get(HIST("event/cutFlow")); + eventCutFlow->GetXaxis()->SetBinLabel(1, "all"); + eventCutFlow->GetXaxis()->SetBinLabel(2, "z vertex"); + eventCutFlow->GetXaxis()->SetBinLabel(3, "occupancy"); + eventCutFlow->GetXaxis()->SetBinLabel(4, "event selection"); + + auto setNormalizationLabels = [](std::shared_ptr const& histogram) { + histogram->GetXaxis()->SetBinLabel(1, "events or event pairs"); + histogram->GetXaxis()->SetBinLabel(2, "selected jets or jet pairs"); + histogram->GetXaxis()->SetBinLabel(3, "same-sign pairs"); + histogram->GetXaxis()->SetBinLabel(4, "opposite-sign pairs"); + histogram->GetXaxis()->SetBinLabel(5, "near-side same-sign pairs"); + histogram->GetXaxis()->SetBinLabel(6, "near-side opposite-sign pairs"); + }; + setNormalizationLabels(registry.get(HIST("normalization/sameEvent"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventNominal"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventLoose"))); + setNormalizationLabels(registry.get(HIST("normalization/mixedEventRandom"))); + if (output.fillPionControl.value) { + setNormalizationLabels(registry.get(HIST("control/pion/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/normalizationMixedEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/mc/particle/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/pion/mc/particle/normalizationMixedEvent"))); + } + if (output.fillKaonControl.value) { + setNormalizationLabels(registry.get(HIST("control/kaon/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/normalizationMixedEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/mc/particle/normalizationSameEvent"))); + setNormalizationLabels(registry.get(HIST("control/kaon/mc/particle/normalizationMixedEvent"))); + } + } + + template + bool isSelectedCollision(CollisionType const& collision, bool fillCutFlow) + { + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 1.f); + } + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 2.f); + } + if (collision.trackOccupancyInTimeRange() < eventCuts.occupancyMin.value || collision.trackOccupancyInTimeRange() > eventCuts.occupancyMax.value) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 3.f); + } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, true, eventCuts.applyRctSelection.value, static_cast(eventCuts.rctLabel))) { + return false; + } + if (fillCutFlow) { + registry.fill(HIST("event/cutFlow"), 4.f); + } + return true; + } + + template + bool isSelectedJetGeometry(JetType const& jet) const + { + if (jet.r() != static_cast(std::lround(jetCuts.jetRadius.value * 100.f)) || std::abs(jet.eta()) > jetCuts.jetEtaMax.value) { + return false; + } + if (jetCuts.areaFractionMin.value >= 0.f) { + float const fullArea = o2::constants::math::PI * jetCuts.jetRadius.value * jetCuts.jetRadius.value; + if (jet.area() / fullArea < jetCuts.areaFractionMin.value) { + return false; + } + } + return true; + } + + template + bool isSelectedJet(JetType const& jet) const + { + return isSelectedJetGeometry(jet) && jet.pt() >= jetCuts.jetPtMin.value && jet.pt() < jetCuts.jetPtMax.value; + } + + template + bool isSelectedTrack(TrackType const& track) const + { + if (trackCuts.requirePvContributor.value && !track.isPVContributor()) { + return false; + } + if (!track.hasITS() || !track.hasTPC() || !track.isGlobalTrack()) { + return false; + } + if (track.tpcNClsCrossedRows() < trackCuts.tpcCrossedRowsMin.value || track.tpcChi2NCl() > trackCuts.tpcChi2Max.value || track.itsChi2NCl() > trackCuts.itsChi2Max.value) { + return false; + } + if (std::abs(track.eta()) > trackCuts.protonEtaMax.value || track.pt() < trackCuts.protonPtMin.value || track.pt() >= trackCuts.protonPtMax.value) { + return false; + } + if (std::abs(track.dcaXY()) > trackCuts.dcaXYMax.value || std::abs(track.dcaZ()) > trackCuts.dcaZMax.value) { + return false; + } + return true; + } + + template + bool isSelectedProton(TrackType const& track) const + { + auto const pidMethod = static_cast(pidCuts.method.value); + float const tpcNSigma = std::abs(track.tpcNSigmaPr()); + bool isProton = false; + + if (pidMethod == ProtonPidMethod::TpcOnly || track.pt() < pidCuts.tofPtThreshold.value) { + isProton = tpcNSigma < pidCuts.tpcNSigmaMax.value; + } else if (track.hasTOF()) { + float const tofNSigma = std::abs(track.tofNSigmaPr()); + if (pidMethod == ProtonPidMethod::TpcTofCircular) { + isProton = std::hypot(tpcNSigma, tofNSigma) < pidCuts.combinedNSigmaMax.value; + } else if (pidMethod == ProtonPidMethod::TpcTofRectangular) { + isProton = tpcNSigma < pidCuts.tpcNSigmaMax.value && tofNSigma < pidCuts.tofNSigmaMax.value; + } + } + + if (!isProton || !pidCuts.rejectPionKaon.value) { + return isProton; + } + + bool const isPionCompatible = std::abs(track.tpcNSigmaPi()) < pidCuts.rejectionNSigma.value && + (track.pt() < pidCuts.tofPtThreshold.value || (track.hasTOF() && std::abs(track.tofNSigmaPi()) < pidCuts.rejectionNSigma.value)); + bool const isKaonCompatible = std::abs(track.tpcNSigmaKa()) < pidCuts.rejectionNSigma.value && + (track.pt() < pidCuts.tofPtThreshold.value || (track.hasTOF() && std::abs(track.tofNSigmaKa()) < pidCuts.rejectionNSigma.value)); + return !isPionCompatible && !isKaonCompatible; + } + + bool passesControlPid(float pt, bool hasTof, float tpcNSigma, float tofNSigma, float tpcMaximum, float tofMaximum, float combinedMaximum) const + { + auto const pidMethod = static_cast(pidCuts.method.value); + tpcNSigma = std::abs(tpcNSigma); + tofNSigma = std::abs(tofNSigma); + if (pidMethod == ProtonPidMethod::TpcOnly || pt < pidCuts.tofPtThreshold.value) { + return tpcNSigma < tpcMaximum; + } + if (!hasTof) { + return false; + } + if (pidMethod == ProtonPidMethod::TpcTofCircular) { + return std::hypot(tpcNSigma, tofNSigma) < combinedMaximum; + } + return tpcNSigma < tpcMaximum && tofNSigma < tofMaximum; + } + + template + bool isSelectedPion(TrackType const& track) const + { + return passesControlPid(track.pt(), track.hasTOF(), track.tpcNSigmaPi(), track.tofNSigmaPi(), pidCuts.pionTpcNSigmaMax.value, pidCuts.pionTofNSigmaMax.value, pidCuts.pionCombinedNSigmaMax.value); + } + + template + bool isSelectedKaon(TrackType const& track) const + { + return passesControlPid(track.pt(), track.hasTOF(), track.tpcNSigmaKa(), track.tofNSigmaKa(), pidCuts.kaonTpcNSigmaMax.value, pidCuts.kaonTofNSigmaMax.value, pidCuts.kaonCombinedNSigmaMax.value); + } + + template + SelectedParticles collectSelectedParticles(JetType const& jet, bool fillQa, float weight = 1.f, bool collectControls = true) + { + SelectedParticles selected; + for (auto const& jetTrack : jet.template tracks_as()) { + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track)) { + continue; + } + + if (fillQa) { + float const signedMomentum = static_cast(track.sign()) * track.p(); + registry.fill(HIST("pid/before/tpcSignalVsSignedP"), signedMomentum, track.tpcSignal(), weight); + registry.fill(HIST("pid/before/tpcNSigmaPiVsPt"), track.pt(), track.tpcNSigmaPi(), weight); + registry.fill(HIST("pid/before/tpcNSigmaKaVsPt"), track.pt(), track.tpcNSigmaKa(), weight); + registry.fill(HIST("pid/before/tpcNSigmaPrVsPt"), track.pt(), track.tpcNSigmaPr(), weight); + registry.fill(HIST("pid/tofMatchDenominator"), track.pt(), weight); + if (track.hasTOF()) { + registry.fill(HIST("pid/tofMatchNumerator"), track.pt(), weight); + registry.fill(HIST("pid/before/tofBetaVsSignedP"), signedMomentum, track.beta(), weight); + registry.fill(HIST("pid/before/tofNSigmaPiVsPt"), track.pt(), track.tofNSigmaPi(), weight); + registry.fill(HIST("pid/before/tofNSigmaKaVsPt"), track.pt(), track.tofNSigmaKa(), weight); + registry.fill(HIST("pid/before/tofNSigmaPrVsPt"), track.pt(), track.tofNSigmaPr(), weight); + } + } + + SelectedParticle const particle{track.pt(), track.eta(), track.phi(), static_cast(track.sign())}; + if (isSelectedProton(track)) { + selected.baryons.push_back(particle); + if (fillQa) { + float const rapidity = RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, o2::constants::physics::MassProton); + registry.fill(HIST("proton/pt"), particle.pt, weight); + registry.fill(HIST("proton/eta"), particle.eta, weight); + registry.fill(HIST("proton/rapidity"), rapidity, weight); + registry.fill(HIST("proton/tpcNSigmaVsPt"), track.pt(), track.tpcNSigmaPr(), weight); + if (track.hasTOF()) { + registry.fill(HIST("proton/tofNSigmaVsPt"), track.pt(), track.tofNSigmaPr(), weight); + } + registry.fill(HIST("proton/dcaXYVsPt"), track.pt(), track.dcaXY(), weight); + registry.fill(HIST("proton/dcaZVsPt"), track.pt(), track.dcaZ(), weight); + registry.fill(HIST("pid/selectedPtByCharge"), static_cast(particle.sign), particle.pt, weight); + } + } + if (collectControls && output.fillPionControl.value && isSelectedPion(track)) { + selected.pions.push_back(particle); + if (fillQa) { + registry.fill(HIST("control/pion/qa/pt"), particle.pt, weight); + registry.fill(HIST("control/pion/qa/tpcNSigmaVsPt"), particle.pt, track.tpcNSigmaPi(), weight); + if (track.hasTOF()) { + registry.fill(HIST("control/pion/qa/tofNSigmaVsPt"), particle.pt, track.tofNSigmaPi(), weight); + } + } + } + if (collectControls && output.fillKaonControl.value && isSelectedKaon(track)) { + selected.kaons.push_back(particle); + if (fillQa) { + registry.fill(HIST("control/kaon/qa/pt"), particle.pt, weight); + registry.fill(HIST("control/kaon/qa/tpcNSigmaVsPt"), particle.pt, track.tpcNSigmaKa(), weight); + if (track.hasTOF()) { + registry.fill(HIST("control/kaon/qa/tofNSigmaVsPt"), particle.pt, track.tofNSigmaKa(), weight); + } + } + } + } + return selected; + } + + static PairType getPairType(SelectedParticle const& baryon1, SelectedParticle const& baryon2) + { + return baryon1.sign == baryon2.sign ? PairType::SameSign : PairType::OppositeSign; + } + + static ChargePairType getChargePairType(SelectedParticle const& baryon1, SelectedParticle const& baryon2) + { + if (baryon1.sign > 0 && baryon2.sign > 0) { + return ChargePairType::PositivePositive; + } + if (baryon1.sign < 0 && baryon2.sign < 0) { + return ChargePairType::NegativeNegative; + } + return ChargePairType::OppositeSign; + } + + template + void fillControlNormalization(bool isMixed, float counter, float weight) + { + if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + registry.fill(HIST("control/pion/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/pion/normalizationSameEvent"), counter, weight); + } + } else { + if (isMixed) { + registry.fill(HIST("control/kaon/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/kaon/normalizationSameEvent"), counter, weight); + } + } + } + + template + void fillControlParticleNormalization(bool isMixed, float counter, float weight) + { + if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + registry.fill(HIST("control/pion/mc/particle/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/pion/mc/particle/normalizationSameEvent"), counter, weight); + } + } else { + if (isMixed) { + registry.fill(HIST("control/kaon/mc/particle/normalizationMixedEvent"), counter, weight); + } else { + registry.fill(HIST("control/kaon/mc/particle/normalizationSameEvent"), counter, weight); + } + } + } + + template + void fillControlPair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + auto fillPairHistograms = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& deltaRSameSignName, auto const& deltaROppositeSignName, auto const& sparseName) { + if (pairType == PairType::SameSign) { + registry.fill(sameSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(sameSignName, -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(deltaRSameSignName, deltaR, weight); + } else { + registry.fill(oppositeSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(deltaROppositeSignName, deltaR, weight); + } + registry.fill(sparseName, static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(sparseName, static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + }; + + if constexpr (Species == ControlSpecies::Pion && !IsParticleLevel) { + if (isMixed) { + fillPairHistograms(HIST("control/pion/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mixedEvent/sameSignDeltaR"), HIST("control/pion/mixedEvent/oppositeSignDeltaR"), HIST("control/pion/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/pion/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/sameEvent/sameSignDeltaR"), HIST("control/pion/sameEvent/oppositeSignDeltaR"), HIST("control/pion/sameEvent/pairSparse")); + } + } else if constexpr (Species == ControlSpecies::Kaon && !IsParticleLevel) { + if (isMixed) { + fillPairHistograms(HIST("control/kaon/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mixedEvent/sameSignDeltaR"), HIST("control/kaon/mixedEvent/oppositeSignDeltaR"), HIST("control/kaon/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/kaon/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/sameEvent/sameSignDeltaR"), HIST("control/kaon/sameEvent/oppositeSignDeltaR"), HIST("control/kaon/sameEvent/pairSparse")); + } + } else if constexpr (Species == ControlSpecies::Pion) { + if (isMixed) { + fillPairHistograms(HIST("control/pion/mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/mixedEvent/sameSignDeltaR"), HIST("control/pion/mc/particle/mixedEvent/oppositeSignDeltaR"), HIST("control/pion/mc/particle/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/pion/mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/pion/mc/particle/sameEvent/sameSignDeltaR"), HIST("control/pion/mc/particle/sameEvent/oppositeSignDeltaR"), HIST("control/pion/mc/particle/sameEvent/pairSparse")); + } + } else { + if (isMixed) { + fillPairHistograms(HIST("control/kaon/mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/mixedEvent/sameSignDeltaR"), HIST("control/kaon/mc/particle/mixedEvent/oppositeSignDeltaR"), HIST("control/kaon/mc/particle/mixedEvent/pairSparse")); + } else { + fillPairHistograms(HIST("control/kaon/mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), HIST("control/kaon/mc/particle/sameEvent/sameSignDeltaR"), HIST("control/kaon/mc/particle/sameEvent/oppositeSignDeltaR"), HIST("control/kaon/mc/particle/sameEvent/pairSparse")); + } + } + } + + template + void fillControlSameJetPairs(std::vector const& particles, float jetPt, float weight) + { + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(false, 2.f, weight); + } else { + fillControlNormalization(false, 2.f, weight); + } + for (size_t first = 0; first < particles.size(); ++first) { + for (size_t second = first + 1; second < particles.size(); ++second) { + PairType const pairType = getPairType(particles[first], particles[second]); + float const deltaEta = particles[first].eta - particles[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(particles[first].pt, particles[second].pt); + float const pairPtHigh = std::max(particles[first].pt, particles[second].pt); + fillControlPair(false, pairType, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(false, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } else { + fillControlNormalization(false, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + + template + void fillControlMixedJetPairs(std::vector const& particles1, + std::vector const& particles2, + JetType1 const& jet1, + JetType2 const& jet2, + float weight) + { + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(true, 2.f, weight); + } else { + fillControlNormalization(true, 2.f, weight); + } + float const mixedJetPt = 0.5f * (jet1.pt() + jet2.pt()); + for (auto const& particle1 : particles1) { + float const localEta1 = particle1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(particle1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& particle2 : particles2) { + float const localEta2 = particle2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(particle2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(particle1, particle2); + float const pairPtLow = std::min(particle1.pt, particle2.pt); + float const pairPtHigh = std::max(particle1.pt, particle2.pt); + fillControlPair(true, pairType, mixedJetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + if constexpr (IsParticleLevel) { + fillControlParticleNormalization(true, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } else { + fillControlNormalization(true, pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + + template + std::vector collectInclusiveBaryons(TracksType const& tracks) + { + std::vector baryons; + for (auto const& track : tracks) { + if (!isSelectedTrack(track)) { + continue; + } + if (!isSelectedProton(track)) { + continue; + } + baryons.push_back({track.pt(), track.eta(), track.phi(), static_cast(track.sign())}); + } + return baryons; + } + + template + bool isSelectedInclusiveCollision(CollisionType const& collision) + { + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value || + collision.trackOccupancyInTimeRange() < eventCuts.occupancyMin.value || + collision.trackOccupancyInTimeRange() > eventCuts.occupancyMax.value) { + return false; + } + + uint16_t const collisionSelection = jetderiveddatautilities::setEventSelectionBit(collision); + bool isOrCondition = false; + bool passedOrCondition = false; + for (auto const& selectionBit : eventSelectionBits) { + if (selectionBit == -1) { + isOrCondition = true; + continue; + } + bool const passed = collisionSelection & (1U << selectionBit); + if (!isOrCondition && !passed) { + return false; + } + if (isOrCondition && passed) { + passedOrCondition = true; + } + } + if (isOrCondition && !passedOrCondition) { + return false; + } + return !eventCuts.applyRctSelection.value || inclusiveRctChecker.checkTable(collision); + } + + void fillInclusivePair(bool isMixed, + SelectedParticle const& baryon1, + SelectedParticle const& baryon2, + float weight) + { + PairType const pairType = getPairType(baryon1, baryon2); + ChargePairType const chargePairType = getChargePairType(baryon1, baryon2); + float const deltaEta = baryon1.eta - baryon2.eta; + float const deltaPhi = RecoDecay::constrainAngle(baryon1.phi - baryon2.phi, -o2::constants::math::PIHalf); + float const reflectedDeltaPhi = RecoDecay::constrainAngle(-deltaPhi, -o2::constants::math::PIHalf); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + if (isMixed) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("inclusive/mixedEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } else { + registry.fill(HIST("inclusive/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("inclusive/mixedEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + } + } else { + if (pairType == PairType::SameSign) { + registry.fill(HIST("inclusive/sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } else { + registry.fill(HIST("inclusive/sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, reflectedDeltaPhi, 0.5f * weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), deltaEta, deltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("inclusive/sameEvent/chargePairSparse"), static_cast(chargePairType), -deltaEta, reflectedDeltaPhi, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + } + + template + bool isMatchedJetPair(float jetPtDifference, int jetNchDifference, float jetEtaDifference) const + { + if constexpr (Reference == MixedReference::Random) { + return true; + } else if constexpr (Reference == MixedReference::Nominal) { + return jetPtDifference <= mixing.nominalJetPtDifferenceMax.value && + jetNchDifference <= mixing.nominalJetNchDifferenceMax.value && + jetEtaDifference <= mixing.nominalJetEtaDifferenceMax.value; + } else { + return jetPtDifference <= mixing.looseJetPtDifferenceMax.value && + jetNchDifference <= mixing.looseJetNchDifferenceMax.value && + jetEtaDifference <= mixing.looseJetEtaDifferenceMax.value; + } + } + + template + void fillPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + auto const pairTypeValue = static_cast(pairType); + auto fillSymmetrized = [&](auto const& sameSignName, auto const& oppositeSignName, auto const& sparseName) { + if (pairType == PairType::SameSign) { + registry.fill(sameSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(sameSignName, -deltaEta, -deltaPhi, 0.5f * weight); + } else { + registry.fill(oppositeSignName, deltaEta, deltaPhi, 0.5f * weight); + registry.fill(oppositeSignName, -deltaEta, -deltaPhi, 0.5f * weight); + } + registry.fill(sparseName, pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(sparseName, pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + }; + + if constexpr (Reference == MixedReference::Nominal) { + fillSymmetrized(HIST("mixedEvent/nominal/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/nominal/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/nominal/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/nominal/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/nominal/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/nominal/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } else if constexpr (Reference == MixedReference::Loose) { + fillSymmetrized(HIST("mixedEvent/loose/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/loose/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/loose/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/loose/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/loose/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } else { + fillSymmetrized(HIST("mixedEvent/random/sameSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/oppositeSignDeltaEtaDeltaPhi"), HIST("mixedEvent/random/pairSparse")); + if (pairType == PairType::SameSign) { + registry.fill(HIST("mixedEvent/random/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mixedEvent/random/oppositeSignDeltaR"), deltaR, weight); + } + if (output.fillChargeSeparated.value) { + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mixedEvent/random/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + } + + template + void fillMixedNormalization(float counter, float weight) + { + if constexpr (Reference == MixedReference::Nominal) { + registry.fill(HIST("normalization/mixedEventNominal"), counter, weight); + } else if constexpr (Reference == MixedReference::Loose) { + registry.fill(HIST("normalization/mixedEventLoose"), counter, weight); + } else { + registry.fill(HIST("normalization/mixedEventRandom"), counter, weight); + } + } + + void fillSameEventPair(PairType pairType, ChargePairType chargePairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + auto const pairTypeValue = static_cast(pairType); + if (pairType == PairType::SameSign) { + registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("sameEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("sameEvent/pairSparse"), pairTypeValue, jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + if (output.fillChargeSeparated.value) { + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("sameEvent/chargePairSparse"), static_cast(chargePairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + + template + SelectedParticles collectParticleCandidates(JetType const& jet, bool fillQa, float weight) + { + SelectedParticles selected; + for (auto const& particle : jet.template tracks_as()) { + if (!particle.isPhysicalPrimary()) { + continue; + } + if (particle.pt() < trackCuts.protonPtMin.value || particle.pt() >= trackCuts.protonPtMax.value || + std::abs(particle.eta()) > trackCuts.protonEtaMax.value) { + continue; + } + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + SelectedParticle const selectedParticle{particle.pt(), particle.eta(), particle.phi(), sign}; + int const absolutePdg = std::abs(particle.pdgCode()); + if (absolutePdg == PDG_t::kProton) { + selected.baryons.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("mc/pid/generatedPrimaryProtonPt"), selectedParticle.pt, weight); + } + } else if (output.fillPionControl.value && absolutePdg == PDG_t::kPiPlus) { + selected.pions.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("control/pion/mc/generatedPrimaryPt"), selectedParticle.pt, weight); + } + } else if (output.fillKaonControl.value && absolutePdg == PDG_t::kKPlus) { + selected.kaons.push_back(selectedParticle); + if (fillQa) { + registry.fill(HIST("control/kaon/mc/generatedPrimaryPt"), selectedParticle.pt, weight); + } + } + } + return selected; + } + + void fillParticlePair(bool isMixed, PairType pairType, float jetPt, float deltaEta, float deltaPhi, float deltaR, float pairPtLow, float pairPtHigh, float weight) + { + if (isMixed) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/particle/mixedEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } else { + if (pairType == PairType::SameSign) { + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/sameSignDeltaR"), deltaR, weight); + } else { + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), deltaEta, deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaEtaDeltaPhi"), -deltaEta, -deltaPhi, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/oppositeSignDeltaR"), deltaR, weight); + } + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/particle/sameEvent/pairSparse"), static_cast(pairType), jetPt, -deltaEta, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + + template + void fillEfficiencyPairs(std::vector const& particles, float detectorJetPt, float weight) + { + for (size_t first = 0; first < particles.size(); ++first) { + for (size_t second = first + 1; second < particles.size(); ++second) { + auto const pairType = static_cast(getPairType(particles[first], particles[second])); + float const deltaEta = particles[first].eta - particles[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(particles[first].phi - particles[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(particles[first].pt, particles[second].pt); + float const pairPtHigh = std::max(particles[first].pt, particles[second].pt); + if constexpr (Stage == EfficiencyStage::Generated) { + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/generatedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } else if constexpr (Stage == EfficiencyStage::TrackSelected) { + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/trackSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } else { + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + registry.fill(HIST("mc/efficiency/pidSelectedPair"), pairType, detectorJetPt, -deltaPhi, deltaR, pairPtLow, pairPtHigh, 0.5f * weight); + } + } + } + } + + template + void fillControlMCPidQa(TrackType const& track, ParticleType const& particle, float weight) + { + int const expectedPdg = Species == ControlSpecies::Pion ? PDG_t::kPiPlus : PDG_t::kKPlus; + bool const isTruePrimary = std::abs(particle.pdgCode()) == expectedPdg && particle.isPhysicalPrimary(); + bool const isSelected = Species == ControlSpecies::Pion ? isSelectedPion(track) : isSelectedKaon(track); + int origin = 2; + if (particle.isPhysicalPrimary()) { + origin = 1; + } else if (particle.getProcess() == TMCProcess::kPDecay) { + origin = 3; + } + + if constexpr (Species == ControlSpecies::Pion) { + if (isTruePrimary) { + registry.fill(HIST("control/pion/mc/reconstructedTruePt"), track.pt(), weight); + } + if (!isSelected) { + return; + } + registry.fill(HIST("control/pion/mc/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("control/pion/mc/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimary) { + registry.fill(HIST("control/pion/mc/selectedTruePt"), track.pt(), weight); + registry.fill(HIST("control/pion/mc/ptResponsePrimary"), particle.pt(), track.pt(), weight); + } + } else { + if (isTruePrimary) { + registry.fill(HIST("control/kaon/mc/reconstructedTruePt"), track.pt(), weight); + } + if (!isSelected) { + return; + } + registry.fill(HIST("control/kaon/mc/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("control/kaon/mc/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimary) { + registry.fill(HIST("control/kaon/mc/selectedTruePt"), track.pt(), weight); + registry.fill(HIST("control/kaon/mc/ptResponsePrimary"), particle.pt(), track.pt(), weight); + } + } + } + + template + void fillMCPidQa(JetType const& jet, float weight) + { + for (auto const& jetTrack : jet.template tracks_as()) { + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track) || !track.has_mcParticle()) { + continue; + } + auto particle = track.template mcParticle_as(); + if (output.fillPionControl.value) { + fillControlMCPidQa(track, particle, weight); + } + if (output.fillKaonControl.value) { + fillControlMCPidQa(track, particle, weight); + } + bool const isTruePrimaryProton = std::abs(particle.pdgCode()) == PDG_t::kProton && particle.isPhysicalPrimary(); + if (isTruePrimaryProton) { + registry.fill(HIST("mc/pid/reconstructedTrueProtonPt"), track.pt(), weight); + } + if (!isSelectedProton(track)) { + continue; + } + int origin = 2; + if (particle.isPhysicalPrimary()) { + origin = 1; + } else if (particle.getProcess() == TMCProcess::kPDecay) { + origin = 3; + } + registry.fill(HIST("mc/pid/selectedCandidateOriginVsPt"), track.pt(), static_cast(origin), weight); + registry.fill(HIST("mc/pid/selectedCandidateTruePdgVsPt"), track.pt(), static_cast(particle.pdgCode()), weight); + if (isTruePrimaryProton) { + registry.fill(HIST("mc/pid/selectedTrueProtonPt"), track.pt(), weight); + registry.fill(HIST("mc/pid/ptResponsePrimaryProton"), particle.pt(), track.pt(), weight); + } + } + } + + template + void fillReconstructedSameEvent(CollisionType const& collision, JetsType const& jets, float weight = 1.f) + { + if (!isSelectedCollision(collision, true)) { + return; + } + + if (output.fillQa.value) { + registry.fill(HIST("event/zVertex"), collision.posZ(), weight); + registry.fill(HIST("event/multiplicityFT0C"), collision.multFT0C(), weight); + } + registry.fill(HIST("normalization/sameEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(false, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(false, 1.f, weight); + } + + for (auto const& jet : jets) { + if (output.fillQa.value && isSelectedJetGeometry(jet)) { + registry.fill(HIST("jet/ptBeforePtSelection"), jet.pt(), weight); + } + if (!isSelectedJet(jet)) { + continue; + } + + registry.fill(HIST("normalization/sameEvent"), 2.f, weight); + if (output.fillQa.value) { + registry.fill(HIST("jet/pt"), jet.pt(), weight); + registry.fill(HIST("jet/eta"), jet.eta(), weight); + registry.fill(HIST("jet/phi"), jet.phi(), weight); + registry.fill(HIST("jet/nConstituents"), static_cast(jet.tracksIds().size()), weight); + registry.fill(HIST("jet/nConstituentsVsPt"), jet.pt(), static_cast(jet.tracksIds().size()), weight); + } + + auto selectedParticles = collectSelectedParticles(jet, output.fillQa.value, weight); + auto const& baryons = selectedParticles.baryons; + if (output.fillQa.value) { + int numberOfProtons = 0; + int numberOfAntiprotons = 0; + for (auto const& baryon : baryons) { + baryon.sign > 0 ? ++numberOfProtons : ++numberOfAntiprotons; + } + registry.fill(HIST("jet/nSelectedBaryons"), static_cast(baryons.size()), weight); + registry.fill(HIST("jet/nProtons"), numberOfProtons, weight); + registry.fill(HIST("jet/nAntiprotons"), numberOfAntiprotons, weight); + registry.fill(HIST("jet/nProtonsVsAntiprotons"), numberOfProtons, numberOfAntiprotons, weight); + registry.fill(HIST("jet/nBaryonsVsPt"), jet.pt(), static_cast(baryons.size()), weight); + } + + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + PairType const pairType = getPairType(baryons[first], baryons[second]); + ChargePairType const chargePairType = getChargePairType(baryons[first], baryons[second]); + float const deltaEta = baryons[first].eta - baryons[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(baryons[first].pt, baryons[second].pt); + float const pairPtHigh = std::max(baryons[first].pt, baryons[second].pt); + fillSameEventPair(pairType, chargePairType, jet.pt(), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + if (output.fillQa.value) { + if (pairType == PairType::SameSign) { + registry.fill(HIST("jet/sameSignPairsVsPt"), jet.pt(), weight); + } else { + registry.fill(HIST("jet/oppositeSignPairsVsPt"), jet.pt(), weight); + } + } + + float const counterBin = pairType == PairType::SameSign ? 3.f : 4.f; + registry.fill(HIST("normalization/sameEvent"), counterBin, weight); + if (std::abs(deltaPhi) < axes.nearSideDeltaPhiMax.value && std::abs(deltaEta) < axes.nearSideDeltaEtaMax.value) { + registry.fill(HIST("normalization/sameEvent"), pairType == PairType::SameSign ? 5.f : 6.f, weight); + } + } + } + if (output.fillPionControl.value) { + fillControlSameJetPairs(selectedParticles.pions, jet.pt(), weight); + } + if (output.fillKaonControl.value) { + fillControlSameJetPairs(selectedParticles.kaons, jet.pt(), weight); + } + } + } + + void processSameEvent(aod::JetCollision const& collision, + ChargedJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracks const&) + { + fillReconstructedSameEvent(collision, jets); + } + PROCESS_SWITCH(BaryonPairInJets, processSameEvent, "Same-event identified baryon pairs in charged jets", true); + + template + void fillMixedJetPair(JetType1 const& jet1, JetType2 const& jet2, float weight) + { + float const jetPtDifference = std::abs(jet1.pt() - jet2.pt()); + int const jetNchDifference = std::abs(static_cast(jet1.tracksIds().size()) - static_cast(jet2.tracksIds().size())); + float const jetEtaDifference = std::abs(jet1.eta() - jet2.eta()); + if (!isMatchedJetPair(jetPtDifference, jetNchDifference, jetEtaDifference)) { + return; + } + + if constexpr (Reference == MixedReference::Random) { + registry.fill(HIST("mixedEvent/jetPtDifference"), jetPtDifference, weight); + registry.fill(HIST("mixedEvent/jetEtaDifference"), jetEtaDifference, weight); + registry.fill(HIST("mixedEvent/jetNchDifference"), jetNchDifference, weight); + } + + constexpr bool CollectControls = Reference == MixedReference::Nominal; + auto selectedParticles1 = collectSelectedParticles(jet1, false, 1.f, CollectControls); + auto selectedParticles2 = collectSelectedParticles(jet2, false, 1.f, CollectControls); + auto const& baryons1 = selectedParticles1.baryons; + auto const& baryons2 = selectedParticles2.baryons; + float const mixedJetPt = 0.5f * (jet1.pt() + jet2.pt()); + fillMixedNormalization(2.f, weight); + + for (auto const& baryon1 : baryons1) { + float const localEta1 = baryon1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(baryon1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& baryon2 : baryons2) { + float const localEta2 = baryon2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(baryon2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(baryon1, baryon2); + ChargePairType const chargePairType = getChargePairType(baryon1, baryon2); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + fillPair(pairType, chargePairType, mixedJetPt, deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + fillMixedNormalization(pairType == PairType::SameSign ? 3.f : 4.f, weight); + if (std::abs(deltaPhi) < axes.nearSideDeltaPhiMax.value && std::abs(deltaEta) < axes.nearSideDeltaEtaMax.value) { + fillMixedNormalization(pairType == PairType::SameSign ? 5.f : 6.f, weight); + } + } + } + if constexpr (Reference == MixedReference::Nominal) { + if (output.fillPionControl.value) { + fillControlMixedJetPairs(selectedParticles1.pions, selectedParticles2.pions, jet1, jet2, weight); + } + if (output.fillKaonControl.value) { + fillControlMixedJetPairs(selectedParticles1.kaons, selectedParticles2.kaons, jet1, jet2, weight); + } + } + } + + void processMixedEvent(aod::JetCollisions const& collisions, + ChargedJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracks const&) + { + MixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (!isSelectedCollision(collision1, false) || !isSelectedCollision(collision2, false)) { + continue; + } + + float const weight = 1.f / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("normalization/mixedEventNominal"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillReferenceVariations.value) { + registry.fill(HIST("normalization/mixedEventLoose"), 1.f, weight); + registry.fill(HIST("normalization/mixedEventRandom"), 1.f, weight); + } + + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + fillMixedJetPair(jet1, jet2, weight); + if (output.fillReferenceVariations.value) { + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEvent, "Mixed-event identified baryon pairs in charged jets", true); + + void processMCD(aod::JetCollisionsMCD::iterator const& collision, + aod::JetMcCollisions const&, + ChargedMCDJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracksMC const&, + aod::McParticles const&) + { + float const weight = collision.mcCollision().weight(); + fillReconstructedSameEvent(collision, jets, weight); + if (!isSelectedCollision(collision, false)) { + return; + } + for (auto const& jet : jets) { + if (isSelectedJet(jet)) { + fillMCPidQa(jet, weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMCD, "Detector-level MC reconstructed baryon pairs and PID truth QA", false); + + void processMixedEventMCD(aod::JetCollisionsMCD const& collisions, + aod::JetMcCollisions const&, + ChargedMCDJetsWithConstituents const& jets, + JetTracksWithOriginal const&, + BaryonTracksMC const&, + aod::McParticles const&) + { + MixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mcdMixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (!isSelectedCollision(collision1, false) || !isSelectedCollision(collision2, false)) { + continue; + } + float const weight = collision1.mcCollision().weight() / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("normalization/mixedEventNominal"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlNormalization(true, 1.f, weight); + } + if (output.fillReferenceVariations.value) { + registry.fill(HIST("normalization/mixedEventLoose"), 1.f, weight); + registry.fill(HIST("normalization/mixedEventRandom"), 1.f, weight); + } + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + fillMixedJetPair(jet1, jet2, weight); + if (output.fillReferenceVariations.value) { + fillMixedJetPair(jet1, jet2, weight); + fillMixedJetPair(jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEventMCD, "Detector-level MC mixed-event baryon pairs", false); + + void processMCP(aod::JetMcCollision const& collision, + ChargedMCPJetsWithConstituents const& jets, + aod::JetParticles const&) + { + if (std::abs(collision.posZ()) > eventCuts.vertexZMax.value) { + return; + } + float const weight = collision.weight(); + registry.fill(HIST("mc/particle/normalizationSameEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlParticleNormalization(false, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlParticleNormalization(false, 1.f, weight); + } + for (auto const& jet : jets) { + if (!isSelectedJet(jet)) { + continue; + } + registry.fill(HIST("mc/particle/normalizationSameEvent"), 2.f, weight); + auto selectedParticles = collectParticleCandidates(jet, true, weight); + auto const& baryons = selectedParticles.baryons; + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + PairType const pairType = getPairType(baryons[first], baryons[second]); + float const deltaEta = baryons[first].eta - baryons[second].eta; + float const deltaPhi = RecoDecay::constrainAngle(baryons[first].phi - baryons[second].phi, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + float const pairPtLow = std::min(baryons[first].pt, baryons[second].pt); + float const pairPtHigh = std::max(baryons[first].pt, baryons[second].pt); + fillParticlePair(false, pairType, jet.pt(), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + registry.fill(HIST("mc/particle/normalizationSameEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + if (output.fillPionControl.value) { + fillControlSameJetPairs(selectedParticles.pions, jet.pt(), weight); + } + if (output.fillKaonControl.value) { + fillControlSameJetPairs(selectedParticles.kaons, jet.pt(), weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMCP, "Particle-level MC baryon pairs", false); + + void processMixedEventMCP(aod::JetMcCollisions const& collisions, + ChargedMCPJetsWithConstituents const& jets, + aod::JetParticles const&) + { + MCPMixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto jetsTuple = std::make_tuple(jets); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, jetsTuple, &mcpMixingCache}; + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, jets1, collision2, jets2] = *iterator; + if (std::abs(collision1.posZ()) > eventCuts.vertexZMax.value || std::abs(collision2.posZ()) > eventCuts.vertexZMax.value) { + continue; + } + float const weight = collision1.weight() / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("mc/particle/normalizationMixedEvent"), 1.f, weight); + if (output.fillPionControl.value) { + fillControlParticleNormalization(true, 1.f, weight); + } + if (output.fillKaonControl.value) { + fillControlParticleNormalization(true, 1.f, weight); + } + for (auto const& jet1 : jets1) { + if (!isSelectedJet(jet1)) { + continue; + } + for (auto const& jet2 : jets2) { + if (!isSelectedJet(jet2)) { + continue; + } + auto selectedParticles1 = collectParticleCandidates(jet1, false, weight); + auto selectedParticles2 = collectParticleCandidates(jet2, false, weight); + auto const& baryons1 = selectedParticles1.baryons; + auto const& baryons2 = selectedParticles2.baryons; + registry.fill(HIST("mc/particle/normalizationMixedEvent"), 2.f, weight); + for (auto const& baryon1 : baryons1) { + float const localEta1 = baryon1.eta - jet1.eta(); + float const localPhi1 = RecoDecay::constrainAngle(baryon1.phi - jet1.phi(), -o2::constants::math::PI); + for (auto const& baryon2 : baryons2) { + float const localEta2 = baryon2.eta - jet2.eta(); + float const localPhi2 = RecoDecay::constrainAngle(baryon2.phi - jet2.phi(), -o2::constants::math::PI); + float const deltaEta = localEta1 - localEta2; + float const deltaPhi = RecoDecay::constrainAngle(localPhi1 - localPhi2, -o2::constants::math::PI); + float const deltaR = std::hypot(deltaEta, deltaPhi); + PairType const pairType = getPairType(baryon1, baryon2); + float const pairPtLow = std::min(baryon1.pt, baryon2.pt); + float const pairPtHigh = std::max(baryon1.pt, baryon2.pt); + fillParticlePair(true, pairType, 0.5f * (jet1.pt() + jet2.pt()), deltaEta, deltaPhi, deltaR, pairPtLow, pairPtHigh, weight); + registry.fill(HIST("mc/particle/normalizationMixedEvent"), pairType == PairType::SameSign ? 3.f : 4.f, weight); + } + } + if (output.fillPionControl.value) { + fillControlMixedJetPairs(selectedParticles1.pions, selectedParticles2.pions, jet1, jet2, weight); + } + if (output.fillKaonControl.value) { + fillControlMixedJetPairs(selectedParticles1.kaons, selectedParticles2.kaons, jet1, jet2, weight); + } + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMixedEventMCP, "Particle-level MC mixed-event baryon pairs", false); + + void processMatched(aod::JetCollisionsMCD::iterator const& collision, + aod::JetMcCollisions const&, + MatchedChargedMCDJets const& detectorJets, + MatchedChargedMCPJets const& particleJets, + JetTracksWithOriginalAndMC const&, + BaryonTracks const&, + aod::JetParticles const&) + { + if (!isSelectedCollision(collision, false) || !collision.has_mcCollision()) { + return; + } + float const weight = collision.mcCollision().weight(); + auto const particleJetsInCollision = particleJets.sliceBy(particleJetsPerMcCollision, collision.mcCollisionId()); + for (auto const& particleJet : particleJetsInCollision) { + if (isSelectedJet(particleJet)) { + registry.fill(HIST("mc/jet/allParticlePt"), particleJet.pt(), weight); + } + } + for (auto const& detectorJet : detectorJets) { + if (!isSelectedJet(detectorJet)) { + continue; + } + registry.fill(HIST("mc/jet/allDetectorPt"), detectorJet.pt(), weight); + bool hasAcceptedMatch = false; + for (auto const& particleJet : detectorJet.template matchedJetGeo_as()) { + if (!isSelectedJet(particleJet) || particleJet.mcCollisionId() != collision.mcCollisionId()) { + continue; + } + hasAcceptedMatch = true; + registry.fill(HIST("mc/jet/matchedDetectorPt"), detectorJet.pt(), weight); + registry.fill(HIST("mc/jet/matchedParticlePt"), particleJet.pt(), weight); + registry.fill(HIST("mc/jet/responsePt"), particleJet.pt(), detectorJet.pt(), weight); + if (particleJet.pt() > 0.f) { + registry.fill(HIST("mc/jet/relativePtResolution"), particleJet.pt(), (detectorJet.pt() - particleJet.pt()) / particleJet.pt(), weight); + } + float const deltaEta = detectorJet.eta() - particleJet.eta(); + float const deltaPhi = RecoDecay::constrainAngle(detectorJet.phi() - particleJet.phi(), -o2::constants::math::PI); + registry.fill(HIST("mc/jet/matchingDeltaR"), std::hypot(deltaEta, deltaPhi), weight); + + std::unordered_set generatedParticleIds; + std::vector generatedProtons; + for (auto const& particle : particleJet.template tracks_as()) { + if (!particle.isPhysicalPrimary() || std::abs(particle.pdgCode()) != PDG_t::kProton) { + continue; + } + if (particle.pt() < trackCuts.protonPtMin.value || particle.pt() >= trackCuts.protonPtMax.value || std::abs(particle.eta()) > trackCuts.protonEtaMax.value) { + continue; + } + float const particleDeltaPhi = RecoDecay::constrainAngle(particle.phi() - particleJet.phi(), -o2::constants::math::PI); + float const particleJetDistance = std::hypot(particle.eta() - particleJet.eta(), particleDeltaPhi); + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + generatedParticleIds.insert(particle.globalIndex()); + generatedProtons.push_back({particle.pt(), particle.eta(), particle.phi(), sign}); + registry.fill(HIST("mc/efficiency/generatedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + fillEfficiencyPairs(generatedProtons, detectorJet.pt(), weight); + + std::unordered_set trackSelectedParticleIds; + std::unordered_set tofMatchedParticleIds; + std::unordered_set pidSelectedParticleIds; + std::vector trackSelectedProtons; + std::vector pidSelectedProtons; + for (auto const& jetTrack : detectorJet.template tracks_as()) { + if (!jetTrack.has_mcParticle()) { + continue; + } + auto particle = jetTrack.template mcParticle_as(); + auto const particleId = particle.globalIndex(); + if (!generatedParticleIds.contains(particleId)) { + continue; + } + auto track = jetTrack.template track_as(); + if (!isSelectedTrack(track)) { + continue; + } + float const particleDeltaPhi = RecoDecay::constrainAngle(particle.phi() - particleJet.phi(), -o2::constants::math::PI); + float const particleJetDistance = std::hypot(particle.eta() - particleJet.eta(), particleDeltaPhi); + int8_t const sign = particle.pdgCode() > 0 ? 1 : -1; + SelectedParticle const selectedParticle{ + .pt = particle.pt(), + .eta = particle.eta(), + .phi = particle.phi(), + .sign = sign}; + if (trackSelectedParticleIds.insert(particleId).second) { + trackSelectedProtons.push_back(selectedParticle); + registry.fill(HIST("mc/efficiency/trackSelectedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + if (track.hasTOF() && tofMatchedParticleIds.insert(particleId).second) { + registry.fill(HIST("mc/efficiency/tofMatchedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + if (isSelectedProton(track) && pidSelectedParticleIds.insert(particleId).second) { + pidSelectedProtons.push_back(selectedParticle); + registry.fill(HIST("mc/efficiency/pidSelectedPrimaryProton"), static_cast(sign), particle.pt(), detectorJet.pt(), particleJetDistance, weight); + } + } + fillEfficiencyPairs(trackSelectedProtons, detectorJet.pt(), weight); + fillEfficiencyPairs(pidSelectedProtons, detectorJet.pt(), weight); + break; + } + if (!hasAcceptedMatch) { + registry.fill(HIST("mc/jet/fakeDetectorPt"), detectorJet.pt(), weight); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processMatched, "Matched charged-jet response and proton efficiency", false); + + void processInclusiveSameEvent(InclusiveSameEventCollisions::iterator const& collision, BaryonTracks const& tracks) + { + if (!isSelectedInclusiveCollision(collision)) { + return; + } + registry.fill(HIST("inclusive/normalization/sameEvent"), 1.f); + auto baryons = collectInclusiveBaryons(tracks); + registry.fill(HIST("inclusive/normalization/sameEvent"), 2.f, static_cast(baryons.size())); + for (size_t first = 0; first < baryons.size(); ++first) { + for (size_t second = first + 1; second < baryons.size(); ++second) { + fillInclusivePair(false, baryons[first], baryons[second], 1.f); + registry.fill(HIST("inclusive/normalization/sameEvent"), getPairType(baryons[first], baryons[second]) == PairType::SameSign ? 3.f : 4.f); + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processInclusiveSameEvent, "Inclusive same-event identified baryon pairs", true); + + void processInclusiveMixedEvent(InclusiveMixedEventCollisions const& collisions, BaryonTracks const& tracks) + { + for (auto const& collision : collisions) { + if (isSelectedInclusiveCollision(collision)) { + registry.fill(HIST("inclusive/event/multiplicity"), collision.multFT0C()); + } + } + InclusiveMixingBinning mixingBinning{{mixing.vertexZBins, mixing.multiplicityBins}}; + auto tracksTuple = std::make_tuple(tracks); + SameKindPair mixedCollisionPairs{ + mixingBinning, mixing.numberOfMixedEvents.value, -1, collisions, tracksTuple, &inclusiveMixingCache}; + + for (auto iterator = mixedCollisionPairs.begin(); iterator != mixedCollisionPairs.end(); ++iterator) { + auto& [collision1, tracks1, collision2, tracks2] = *iterator; + if (!isSelectedInclusiveCollision(collision1) || !isSelectedInclusiveCollision(collision2)) { + continue; + } + float const weight = 1.f / static_cast(iterator.currentWindowNeighbours()); + registry.fill(HIST("inclusive/normalization/mixedEvent"), 1.f, weight); + auto baryons1 = collectInclusiveBaryons(tracks1); + auto baryons2 = collectInclusiveBaryons(tracks2); + registry.fill(HIST("inclusive/normalization/mixedEvent"), 2.f, weight * static_cast(baryons1.size() + baryons2.size())); + for (auto const& baryon1 : baryons1) { + for (auto const& baryon2 : baryons2) { + fillInclusivePair(true, baryon1, baryon2, weight); + registry.fill(HIST("inclusive/normalization/mixedEvent"), getPairType(baryon1, baryon2) == PairType::SameSign ? 3.f : 4.f, weight); + } + } + } + } + PROCESS_SWITCH(BaryonPairInJets, processInclusiveMixedEvent, "Inclusive mixed-event identified baryon pairs", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& configContext) +{ + return WorkflowSpec{adaptAnalysisTask(configContext)}; +} diff --git a/PWGJE/Tasks/bjetCentMult.cxx b/PWGJE/Tasks/bjetCentMult.cxx index 8bd8111b200..9188a4bc256 100644 --- a/PWGJE/Tasks/bjetCentMult.cxx +++ b/PWGJE/Tasks/bjetCentMult.cxx @@ -51,6 +51,7 @@ struct BjetCentMultTask { Configurable fillGeneralSVQA{"fillGeneralSVQA", true, "process of general QA for sv"}; Configurable fillSVxyz{"fillSVxyz", true, "process of decay lenngth of xyz for sv"}; Configurable useEventWeight{"useEventWeight", true, "Flag whether to scale histograms with the event weight"}; + Configurable doSumw2{"doSumw2", true, "Flag whether to set Sumw2()"}; // Cut configuration Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; @@ -109,8 +110,8 @@ struct BjetCentMultTask { { numberOfJetFlavourSpecies = static_cast(numFlavourSpecies); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Axis AxisSpec axisJetFlavour = {binJetFlavour, "Jet flavour"}; AxisSpec axisJetPt = {binJetPt, "#it{p}_{T, jet}"}; @@ -137,86 +138,86 @@ struct BjetCentMultTask { AxisSpec axisPercentileMultiplicity = {110, -5., 105., "Percentile multiplicity"}; if (doprocessCentMultQa) { - registry.add("h_amplitude_FT0A", "", {HistType::kTH1F, {{axisFT0}}}); - registry.add("h_amplitude_FT0C", "", {HistType::kTH1F, {{axisFT0}}}); - registry.add("h_scaled_FT0M", "", {HistType::kTH1F, {{axisMultScaledFT0M}}}); - registry.add("h_scaled_FT0M_class", "", {HistType::kTH1F, {{axisMultScaledFT0MClass}}}); - registry.add("h2_centrality_percentile_multiplicity", "mcd collision centrality; centrality; counts", {HistType::kTH2F, {{axisRebinnedCentrality}, {axisPercentileMultiplicity}}}); - } - if (doprocessSV3ProngData) { - registry.add("h_event_centrality", "", {HistType::kTH1F, {{axisCentrality}}}); - registry.add("h2_jet_pt_centrality", "", {HistType::kTH2F, {{axisJetPt}, {axisCentrality}}}); - registry.add("h2_jet_eta_centrality", "", {HistType::kTH2F, {{axisEta}, {axisCentrality}}}); - registry.add("h2_jet_phi_centrality", "", {HistType::kTH2F, {{axisPhi}, {axisCentrality}}}); + registry.add("h_amplitude_FT0A", "", {HistType::kTH1F, {{axisFT0}}}, doSumw2); + registry.add("h_amplitude_FT0C", "", {HistType::kTH1F, {{axisFT0}}}, doSumw2); + registry.add("h_scaled_FT0M", "", {HistType::kTH1F, {{axisMultScaledFT0M}}}, doSumw2); + registry.add("h_scaled_FT0M_class", "", {HistType::kTH1F, {{axisMultScaledFT0MClass}}}, doSumw2); + registry.add("h2_centrality_percentile_multiplicity", "mcd collision centrality; centrality; counts", {HistType::kTH2F, {{axisRebinnedCentrality}, {axisPercentileMultiplicity}}}, doSumw2); + } + if (doprocessSVData) { + registry.add("h_event_centrality", "", {HistType::kTH1F, {{axisCentrality}}}, doSumw2); + registry.add("h2_jet_pt_centrality", "", {HistType::kTH2F, {{axisJetPt}, {axisCentrality}}}, doSumw2); + registry.add("h2_jet_eta_centrality", "", {HistType::kTH2F, {{axisEta}, {axisCentrality}}}, doSumw2); + registry.add("h2_jet_phi_centrality", "", {HistType::kTH2F, {{axisPhi}, {axisCentrality}}}, doSumw2); if (fillGeneralSVQA) { - registry.add("h2_3prong_nprongs_centrality", "", {HistType::kTH2F, {{axisNprongs}, {axisCentrality}}}); - registry.add("hn_jet_3prong_Sxy_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}}}); + registry.add("h2_nprongs_centrality", "", {HistType::kTH2F, {{axisNprongs}, {axisCentrality}}}, doSumw2); + registry.add("hn_jet_Sxy_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}}}); + registry.add("hn_jet_Sxyz_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}}}, doSumw2); } } - registry.add("hn_jet_3prong_Sxy_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}); - registry.add("hn_taggedjet_3prong_Sxy_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}); + registry.add("hn_jet_Sxy_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}, doSumw2); + registry.add("hn_taggedjet_Sxy_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}); - registry.add("hn_taggedjet_3prong_Sxyz_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}); + registry.add("hn_jet_Sxyz_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}, doSumw2); + registry.add("hn_taggedjet_Sxyz_N1_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}, doSumw2); } } - if (doprocessRhoAreaSubSV3ProngData) { - registry.add("h_event_centrality", "", {HistType::kTH1F, {{axisCentrality}}}); - registry.add("h2_jet_pt_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisJetPt}, {axisCentrality}}}); - registry.add("h2_jet_eta_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisEta}, {axisCentrality}}}); - registry.add("h2_jet_phi_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisPhi}, {axisCentrality}}}); + if (doprocessRhoAreaSubSVData) { + registry.add("h_event_rhoareasubtracted_centrality", "", {HistType::kTH1F, {{axisCentrality}}}, doSumw2); + registry.add("h2_jet_pt_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisJetPt}, {axisCentrality}}}, doSumw2); + registry.add("h2_jet_eta_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisEta}, {axisCentrality}}}, doSumw2); + registry.add("h2_jet_phi_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisPhi}, {axisCentrality}}}, doSumw2); if (fillGeneralSVQA) { - registry.add("h2_3prong_nprongs_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisNprongs}, {axisCentrality}}}); - registry.add("hn_jet_3prong_Sxy_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}}}); + registry.add("h2_nprongs_rhoareasubtracted_centrality", "", {HistType::kTH2F, {{axisNprongs}, {axisCentrality}}}, doSumw2); + registry.add("hn_jet_Sxy_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}}}); + registry.add("hn_jet_Sxyz_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}}}, doSumw2); } } - registry.add("hn_jet_3prong_Sxy_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}); - registry.add("hn_taggedjet_3prong_Sxy_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}); + registry.add("hn_jet_Sxy_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}, doSumw2); + registry.add("hn_taggedjet_Sxy_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}); - registry.add("hn_taggedjet_3prong_Sxyz_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}); + registry.add("hn_jet_Sxyz_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}, doSumw2); + registry.add("hn_taggedjet_Sxyz_N1_rhoareasubtracted_centrality", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}}}, doSumw2); } } - if (doprocessSV3ProngMCD || doprocessSV3ProngMCPMCDMatched) { + if (doprocessSVMCD || doprocessSVMCPMCDMatched) { registry.add("h_event_centrality", "", {HistType::kTH1F, {{axisCentrality}}}); - registry.add("h3_jet_pt_centrality_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("h3_jet_eta_centrality_flavour", "", {HistType::kTH3F, {{axisEta}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("h3_jet_phi_centrality_flavour", "", {HistType::kTH3F, {{axisPhi}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_centrality_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("h3_jet_eta_centrality_flavour", "", {HistType::kTH3F, {{axisEta}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("h3_jet_phi_centrality_flavour", "", {HistType::kTH3F, {{axisPhi}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); if (fillGeneralSVQA) { - registry.add("h3_3prong_nprongs_centrality_flavour", "", {HistType::kTH3F, {{axisNprongs}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("hn_jet_3prong_Sxy_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("h3_nprongs_centrality_flavour", "", {HistType::kTH3F, {{axisNprongs}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("hn_jet_Sxy_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("hn_jet_Sxyz_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); } } - registry.add("hn_jet_3prong_Sxy_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("hn_taggedjet_3prong_Sxy_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("hn_jet_Sxy_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("hn_taggedjet_Sxy_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("hn_taggedjet_3prong_Sxyz_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("hn_jet_Sxyz_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("hn_taggedjet_Sxyz_N1_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); } } - if (doprocessRhoAreaSubSV3ProngMCD || doprocessRhoAreaSubSV3ProngMCPMCDMatched) { - registry.add("h_event_rhoareasubtracted_centrality", "", {HistType::kTH1F, {{axisCentrality}}}); - registry.add("h3_jet_pt_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("h3_jet_eta_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisEta}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("h3_jet_phi_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisPhi}, {axisCentrality}, {axisJetFlavour}}}); + if (doprocessRhoAreaSubSVMCD || doprocessRhoAreaSubSVMCPMCDMatched) { + registry.add("h_event_rhoareasubtracted_centrality", "", {HistType::kTH1F, {{axisCentrality}}}, doSumw2); + registry.add("h3_jet_pt_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("h3_jet_eta_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisEta}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("h3_jet_phi_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisPhi}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); if (fillGeneralSVQA) { - registry.add("h3_3prong_nprongs_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisNprongs}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("hn_jet_3prong_Sxy_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("h3_nprongs_rhoareasubtracted_centrality_flavour", "", {HistType::kTH3F, {{axisNprongs}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("hn_jet_Sxy_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("hn_jet_Sxyz_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxyz}, {axisSigmaLxyz}, {axisSxyz}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); } } - registry.add("hn_jet_3prong_Sxy_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("hn_taggedjet_3prong_Sxy_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("hn_jet_Sxy_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("hn_taggedjet_Sxy_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxy}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); if (fillSVxyz) { - registry.add("hn_jet_3prong_Sxyz_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); - registry.add("hn_taggedjet_3prong_Sxyz_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}); + registry.add("hn_jet_Sxyz_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); + registry.add("hn_taggedjet_Sxyz_N1_rhoareasubtracted_centrality_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisSxyz}, {axisMass}, {axisCentrality}, {axisJetFlavour}}}, doSumw2); } } } @@ -332,7 +333,7 @@ struct BjetCentMultTask { } template - void fillRhoAreaSubtractedHistogramSV3ProngData(T const& jet, U const& /*prongs*/, float centrality, float rho) + void fillRhoAreaSubtractedHistogramSV(T const& jet, U const& /*prongs*/, float centrality, float rho) { if (jet.template secondaryVertices_as().size() < 1) return; @@ -340,11 +341,11 @@ struct BjetCentMultTask { registry.fill(HIST("h2_jet_eta_rhoareasubtracted_centrality"), jet.eta(), centrality); registry.fill(HIST("h2_jet_phi_rhoareasubtracted_centrality"), jet.phi(), centrality); if (fillGeneralSVQA) { - registry.fill(HIST("h2_3prong_nprongs_rhoareasubtracted_centrality"), jet.template secondaryVertices_as().size(), centrality); + registry.fill(HIST("h2_nprongs_rhoareasubtracted_centrality"), jet.template secondaryVertices_as().size(), centrality); for (const auto& prong : jet.template secondaryVertices_as()) { - registry.fill(HIST("hn_jet_3prong_Sxy_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality); + registry.fill(HIST("hn_jet_Sxy_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality); if (fillSVxyz) { - registry.fill(HIST("hn_jet_3prong_Sxyz_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality); + registry.fill(HIST("hn_jet_Sxyz_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality); } } } @@ -353,9 +354,9 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); auto massSV = bjetCand.m(); - registry.fill(HIST("hn_jet_3prong_Sxy_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxy, massSV, centrality); + registry.fill(HIST("hn_jet_Sxy_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxy, massSV, centrality); if (jet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxy_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxy, massSV, centrality); + registry.fill(HIST("hn_taggedjet_Sxy_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxy, massSV, centrality); } } if (fillSVxyz) { @@ -364,16 +365,16 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("hn_jet_3prong_Sxyz_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxyz, massSV, centrality); + registry.fill(HIST("hn_jet_Sxyz_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxyz, massSV, centrality); if (jet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxyz_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxyz, massSV, centrality); + registry.fill(HIST("hn_taggedjet_Sxyz_N1_rhoareasubtracted_centrality"), jet.pt() - (rho * jet.area()), maxSxyz, massSV, centrality); } } } } template - void fillHistogramSV3ProngData(T const& jet, U const& /*prongs*/, float centrality) + void fillHistogramSVData(T const& jet, U const& /*prongs*/, float centrality) { if (jet.template secondaryVertices_as().size() < 1) return; @@ -381,11 +382,11 @@ struct BjetCentMultTask { registry.fill(HIST("h2_jet_eta_centrality"), jet.eta(), centrality); registry.fill(HIST("h2_jet_phi_centrality"), jet.phi(), centrality); if (fillGeneralSVQA) { - registry.fill(HIST("h2_3prong_nprongs_centrality"), jet.template secondaryVertices_as().size(), centrality); + registry.fill(HIST("h2_nprongs_centrality"), jet.template secondaryVertices_as().size(), centrality); for (const auto& prong : jet.template secondaryVertices_as()) { - registry.fill(HIST("hn_jet_3prong_Sxy_centrality"), jet.pt(), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality); + registry.fill(HIST("hn_jet_Sxy_centrality"), jet.pt(), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality); if (fillSVxyz) { - registry.fill(HIST("hn_jet_3prong_Sxyz_centrality"), jet.pt(), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality); + registry.fill(HIST("hn_jet_Sxyz_centrality"), jet.pt(), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality); } } } @@ -394,9 +395,9 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); auto massSV = bjetCand.m(); - registry.fill(HIST("hn_jet_3prong_Sxy_N1_centrality"), jet.pt(), maxSxy, massSV, centrality); + registry.fill(HIST("hn_jet_Sxy_N1_centrality"), jet.pt(), maxSxy, massSV, centrality); if (jet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxy_N1_centrality"), jet.pt(), maxSxy, massSV, centrality); + registry.fill(HIST("hn_taggedjet_Sxy_N1_centrality"), jet.pt(), maxSxy, massSV, centrality); } } if (fillSVxyz) { @@ -405,16 +406,16 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("hn_jet_3prong_Sxyz_N1_centrality"), jet.pt(), maxSxyz, massSV, centrality); + registry.fill(HIST("hn_jet_Sxyz_N1_centrality"), jet.pt(), maxSxyz, massSV, centrality); if (jet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxyz_N1_centrality"), jet.pt(), maxSxyz, massSV, centrality); + registry.fill(HIST("hn_taggedjet_Sxyz_N1_centrality"), jet.pt(), maxSxyz, massSV, centrality); } } } } template - void fillHistogramSV3ProngMCD(T const& mcdjet, U const& /*prongs*/, float centrality, float eventWeight = 1.0) + void fillHistogramSVMCD(T const& mcdjet, U const& /*prongs*/, float centrality, float eventWeight = 1.0) { if (useEventWeight) { float pTHat = 10. / (std::pow(eventWeight, 1.0 / pTHatExponent)); @@ -429,11 +430,11 @@ struct BjetCentMultTask { registry.fill(HIST("h3_jet_eta_centrality_flavour"), mcdjet.eta(), centrality, origin, eventWeight); registry.fill(HIST("h3_jet_phi_centrality_flavour"), mcdjet.phi(), centrality, origin, eventWeight); if (fillGeneralSVQA) { - registry.fill(HIST("h3_3prong_nprongs_centrality_flavour"), mcdjet.template secondaryVertices_as().size(), centrality, origin, eventWeight); + registry.fill(HIST("h3_nprongs_centrality_flavour"), mcdjet.template secondaryVertices_as().size(), centrality, origin, eventWeight); for (const auto& prong : mcdjet.template secondaryVertices_as()) { - registry.fill(HIST("hn_jet_3prong_Sxy_centrality_flavour"), mcdjet.pt(), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxy_centrality_flavour"), mcdjet.pt(), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality, origin, eventWeight); if (fillSVxyz) { - registry.fill(HIST("hn_jet_3prong_Sxyz_centrality_flavour"), mcdjet.pt(), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxyz_centrality_flavour"), mcdjet.pt(), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality, origin, eventWeight); } } } @@ -442,9 +443,9 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); auto massSV = bjetCand.m(); - registry.fill(HIST("hn_jet_3prong_Sxy_N1_centrality_flavour"), mcdjet.pt(), maxSxy, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxy_N1_centrality_flavour"), mcdjet.pt(), maxSxy, massSV, centrality, origin, eventWeight); if (mcdjet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxy_N1_centrality_flavour"), mcdjet.pt(), maxSxy, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_taggedjet_Sxy_N1_centrality_flavour"), mcdjet.pt(), maxSxy, massSV, centrality, origin, eventWeight); } } if (fillSVxyz) { @@ -452,16 +453,16 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("hn_jet_3prong_Sxyz_N1_centrality_flavour"), mcdjet.pt(), maxSxyz, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxyz_N1_centrality_flavour"), mcdjet.pt(), maxSxyz, massSV, centrality, origin, eventWeight); if (mcdjet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxyz_N1_centrality_flavour"), mcdjet.pt(), maxSxyz, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_taggedjet_Sxyz_N1_centrality_flavour"), mcdjet.pt(), maxSxyz, massSV, centrality, origin, eventWeight); } } } } template - void fillRhoAreaSubtractedHistogramSV3ProngMCD(T const& mcdjet, U const& /*prongs*/, float centrality, float rho, float eventWeight = 1.0) + void fillRhoAreaSubtractedHistogramSVMCD(T const& mcdjet, U const& /*prongs*/, float centrality, float rho, float eventWeight = 1.0) { if (useEventWeight) { float pTHat = 10. / (std::pow(eventWeight, 1.0 / pTHatExponent)); @@ -476,11 +477,11 @@ struct BjetCentMultTask { registry.fill(HIST("h3_jet_eta_rhoareasubtracted_centrality_flavour"), mcdjet.eta(), centrality, origin, eventWeight); registry.fill(HIST("h3_jet_phi_rhoareasubtracted_centrality_flavour"), mcdjet.phi(), centrality, origin, eventWeight); if (fillGeneralSVQA) { - registry.fill(HIST("h3_3prong_nprongs_rhoareasubtracted_centrality_flavour"), mcdjet.template secondaryVertices_as().size(), centrality, origin, eventWeight); + registry.fill(HIST("h3_nprongs_rhoareasubtracted_centrality_flavour"), mcdjet.template secondaryVertices_as().size(), centrality, origin, eventWeight); for (const auto& prong : mcdjet.template secondaryVertices_as()) { - registry.fill(HIST("hn_jet_3prong_Sxy_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxy_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), centrality, origin, eventWeight); if (fillSVxyz) { - registry.fill(HIST("hn_jet_3prong_Sxyz_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxyz_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), prong.decayLength(), prong.errorDecayLength(), prong.decayLength() / prong.errorDecayLength(), centrality, origin, eventWeight); } } } @@ -489,9 +490,9 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); auto massSV = bjetCand.m(); - registry.fill(HIST("hn_jet_3prong_Sxy_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxy, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxy_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxy, massSV, centrality, origin, eventWeight); if (mcdjet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxy_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxy, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_taggedjet_Sxy_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxy, massSV, centrality, origin, eventWeight); } } if (fillSVxyz) { @@ -499,9 +500,9 @@ struct BjetCentMultTask { if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("hn_jet_3prong_Sxyz_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxyz, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_jet_Sxyz_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxyz, massSV, centrality, origin, eventWeight); if (mcdjet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("hn_taggedjet_3prong_Sxyz_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxyz, massSV, centrality, origin, eventWeight); + registry.fill(HIST("hn_taggedjet_Sxyz_N1_rhoareasubtracted_centrality_flavour"), mcdjet.pt() - (rho * mcdjet.area()), maxSxyz, massSV, centrality, origin, eventWeight); } } } @@ -554,7 +555,7 @@ struct BjetCentMultTask { } PROCESS_SWITCH(BjetCentMultTask, processMCP, "Fill impact parameter imformation for mcp jets", false); - void processSV3ProngData(soa::Filtered::iterator const& collision, soa::Join const& jets, aod::DataSecondaryVertex3Prongs const& prongs) + void processSVData(soa::Filtered::iterator const& collision, soa::Join const& jets, aod::DataSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -568,19 +569,19 @@ struct BjetCentMultTask { if (!isAcceptedJet(jet)) { continue; } - fillHistogramSV3ProngData(jet, prongs, centrality); + fillHistogramSVData(jet, prongs, centrality); } } - PROCESS_SWITCH(BjetCentMultTask, processSV3ProngData, "Fill 3prong imformation for data jets", false); + PROCESS_SWITCH(BjetCentMultTask, processSVData, "Fill n-prong imformation for data jets", false); - void processRhoAreaSubSV3ProngData(soa::Filtered>::iterator const& collision, soa::Join const& jets, aod::DataSecondaryVertex3Prongs const& prongs) + void processRhoAreaSubSVData(soa::Filtered>::iterator const& collision, soa::Join const& jets, aod::DataSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } float centrality = collision.centFT0M(); float rho = collision.rho(); - registry.fill(HIST("h_event_centrality"), centrality); + registry.fill(HIST("h_event_rhoareasubtracted_centrality"), collision.centFT0M()); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaCuts->at(0), jetEtaCuts->at(1), trackCuts->at(2), trackCuts->at(3))) { continue; @@ -588,12 +589,12 @@ struct BjetCentMultTask { if (!isAcceptedJet(jet)) { continue; } - fillRhoAreaSubtractedHistogramSV3ProngData(jet, prongs, centrality, rho); + fillRhoAreaSubtractedHistogramSV(jet, prongs, centrality, rho); } } - PROCESS_SWITCH(BjetCentMultTask, processRhoAreaSubSV3ProngData, "Fill 3prong imformation for data jets with background subtraction", false); + PROCESS_SWITCH(BjetCentMultTask, processRhoAreaSubSVData, "Fill n-prong imformation for data jets with background subtraction", false); - void processSV3ProngMCD(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertex3Prongs const& prongs) + void processSVMCD(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -611,12 +612,12 @@ struct BjetCentMultTask { if (!isAcceptedJet(mcdjet)) { continue; } - fillHistogramSV3ProngMCD(mcdjet, prongs, centrality, weight); + fillHistogramSVMCD(mcdjet, prongs, centrality, weight); } } - PROCESS_SWITCH(BjetCentMultTask, processSV3ProngMCD, "Fill 3prong imformation for mcd jets", false); + PROCESS_SWITCH(BjetCentMultTask, processSVMCD, "Fill n-prong imformation for mcd jets", false); - void processSV3ProngMCPMCDMatched(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertex3Prongs const& prongs) + void processSVMCPMCDMatched(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -637,12 +638,12 @@ struct BjetCentMultTask { if (!mcdjet.has_matchedJetGeo()) { continue; } - fillHistogramSV3ProngMCD(mcdjet, prongs, centrality, weight); + fillHistogramSVMCD(mcdjet, prongs, centrality, weight); } } - PROCESS_SWITCH(BjetCentMultTask, processSV3ProngMCPMCDMatched, "Fill 3prong imformation for mcd jets matched", false); + PROCESS_SWITCH(BjetCentMultTask, processSVMCPMCDMatched, "Fill n-prong imformation for mcd jets matched", false); - void processRhoAreaSubSV3ProngMCD(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertex3Prongs const& prongs) + void processRhoAreaSubSVMCD(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -661,12 +662,12 @@ struct BjetCentMultTask { if (!isAcceptedJet(mcdjet)) { continue; } - fillRhoAreaSubtractedHistogramSV3ProngMCD(mcdjet, prongs, centrality, rho, weight); + fillRhoAreaSubtractedHistogramSVMCD(mcdjet, prongs, centrality, rho, weight); } } - PROCESS_SWITCH(BjetCentMultTask, processRhoAreaSubSV3ProngMCD, "Fill 3prong imformation for mcd jets with background subraction", false); + PROCESS_SWITCH(BjetCentMultTask, processRhoAreaSubSVMCD, "Fill n-prong imformation for mcd jets with background subraction", false); - void processRhoAreaSubSV3ProngMCPMCDMatched(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertex3Prongs const& prongs) + void processRhoAreaSubSVMCPMCDMatched(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -688,10 +689,10 @@ struct BjetCentMultTask { if (!mcdjet.has_matchedJetGeo()) { continue; } - fillRhoAreaSubtractedHistogramSV3ProngMCD(mcdjet, prongs, centrality, rho, weight); + fillRhoAreaSubtractedHistogramSVMCD(mcdjet, prongs, centrality, rho, weight); } } - PROCESS_SWITCH(BjetCentMultTask, processRhoAreaSubSV3ProngMCPMCDMatched, "Fill 3prong imformation for mcd jets matched with background subtraction", false); + PROCESS_SWITCH(BjetCentMultTask, processRhoAreaSubSVMCPMCDMatched, "Fill n-prong imformation for mcd jets matched with background subtraction", false); }; using BjetCentMultChargedDataJets = soa::Join; diff --git a/PWGJE/Tasks/bjetTaggingGnn.cxx b/PWGJE/Tasks/bjetTaggingGnn.cxx index 8a48eab4af5..35ace3cc4f4 100644 --- a/PWGJE/Tasks/bjetTaggingGnn.cxx +++ b/PWGJE/Tasks/bjetTaggingGnn.cxx @@ -12,12 +12,13 @@ /// \file bjetTaggingGnn.cxx /// \brief b-jet tagging using GNN /// -/// \author Changhwan Choi , Pusan National University +/// \author Changhwan Choi , Yonsei University #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetTaggingUtilities.h" #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" #include "PWGJE/DataModel/JetTagging.h" #include "Common/CCDB/EventSelectionParams.h" @@ -25,10 +26,9 @@ #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include @@ -40,20 +40,23 @@ #include #include +#include #include +#include +#include #include #include #include +#include +#include #include -#include - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -namespace BjetTaggingGnnEvtSel +namespace bjet_tagging_gnn_evtsel { enum class EvtSelFlag : uint8_t { kNone = 0, @@ -62,9 +65,11 @@ enum class EvtSelFlag : uint8_t { kTVX = 1 << 2, kNoTFB = 1 << 3, kNoITSROFB = 1 << 4, - kZvtx = 1 << 5, - kINELgt0 = 1 << 6, - kINELgt0rec = 1 << 7, + kNoPileup = 1 << 5, + kIsGoodZvtx = 1 << 6, + kZvtx = 1 << 7, + // kINELgt0 = 1 << 8, + // kINELgt0rec = 1 << 9, INEL = kINEL, INELZvtx = kINEL | kZvtx, @@ -76,8 +81,12 @@ enum class EvtSelFlag : uint8_t { SelMCZvtx = kINEL | kColl | kTVX | kNoTFB | kZvtx, Sel8 = kINEL | kColl | kTVX | kNoTFB | kNoITSROFB, Sel8Zvtx = kINEL | kColl | kTVX | kNoTFB | kNoITSROFB | kZvtx, - INELgt0 = kINEL | kZvtx | kINELgt0, - INELgt0rec = kINEL | kZvtx | kColl | kTVX | kNoTFB | kNoITSROFB | kINELgt0rec + Sel8Full = kINEL | kColl | kTVX | kNoTFB | kNoITSROFB | kNoPileup, + Sel8FullZvtx = kINEL | kColl | kTVX | kNoTFB | kNoITSROFB | kNoPileup | kZvtx, + Sel8FullGood = kINEL | kColl | kTVX | kNoTFB | kNoITSROFB | kNoPileup | kIsGoodZvtx, + Sel8FullGoodZvtx = kINEL | kColl | kTVX | kNoTFB | kNoITSROFB | kNoPileup | kIsGoodZvtx | kZvtx, + // INELgt0 = kINEL | kZvtx | kINELgt0 + // INELgt0rec = kINEL | kZvtx | kColl | kTVX | kNoTFB | kNoITSROFB | kINELgt0rec }; constexpr EvtSelFlag operator|(EvtSelFlag a, EvtSelFlag b) { @@ -113,11 +122,56 @@ enum class EvtSel { SelMCZvtx, Sel8, Sel8Zvtx, - INELgt0, - INELgt0rec + Sel8Full, + Sel8FullZvtx, + Sel8FullGood, + Sel8FullGoodZvtx + // INELgt0, + // INELgt0rec +}; +// Ordered list of event-selection stages shared by all "evtsel"-differential histograms below; +// bin index in the histogram == index in this table (all "evtsel" histograms span the full table, +// so a given event-selection stage always lands on the same bin at particle- and reco-level, matching +// hCollCounter/hMcCollCounter). Reco-level histograms only ever get bins filled for stages +// [kEvtSelStageRecoFirst, kEvtSelStageRecoLast]; bins before kEvtSelStageRecoFirst are unfillable +// placeholders (see setEvtSelStageAxisLabels()), mirroring hCollCounter's "_1"/"_2" bins. +// MC-truth (MCP) ones fill the full range. +struct EvtSelStage { + EvtSelFlag flag; + const char* label; }; -}; // namespace BjetTaggingGnnEvtSel -using namespace BjetTaggingGnnEvtSel; +constexpr std::array kEvtSelStages{{{.flag = EvtSelFlag::INEL, .label = "INEL"}, + {.flag = EvtSelFlag::INELZvtx, .label = "INEL+Zvtx"}, + {.flag = EvtSelFlag::Coll, .label = "Coll"}, + {.flag = EvtSelFlag::CollZvtx, .label = "Coll+Zvtx"}, + {.flag = EvtSelFlag::TVX, .label = "TVX"}, + {.flag = EvtSelFlag::TVXZvtx, .label = "TVX+Zvtx"}, + {.flag = EvtSelFlag::SelMC, .label = "SelMC"}, + {.flag = EvtSelFlag::SelMCZvtx, .label = "SelMC+Zvtx"}, + {.flag = EvtSelFlag::Sel8, .label = "Sel8"}, + {.flag = EvtSelFlag::Sel8Zvtx, .label = "Sel8+Zvtx"}, + {.flag = EvtSelFlag::Sel8Full, .label = "Sel8Full"}, + {.flag = EvtSelFlag::Sel8FullZvtx, .label = "Sel8Full+Zvtx"}, + {.flag = EvtSelFlag::Sel8FullGood, .label = "Sel8FullGood"}, + {.flag = EvtSelFlag::Sel8FullGoodZvtx, .label = "Sel8FullGood+Zvtx"}}}; +constexpr int kEvtSelStageRecoFirst = 2; // Coll +constexpr int kEvtSelStageRecoLast = 13; // Sel8FullGood+Zvtx + +// Labels a full-range [0, kEvtSelStages.size()-1] evtsel axis. Bins below `first` (unreachable at +// reco level) get numbered placeholder labels ("_1", "_2", ...) instead of a stage label, matching +// hCollCounter's placeholder bins for the same reason: keeps every "evtsel" histogram's bin index +// aligned to the same event-selection stage across particle- and reco-level histograms. +void setEvtSelStageAxisLabels(TAxis* axis, int first = 0, int last = kEvtSelStages.size() - 1) +{ + for (int i = 0; i < first; ++i) { + axis->SetBinLabel(i + 1, ("_" + std::to_string(i + 1)).c_str()); + } + for (int i = first; i <= last; ++i) { + axis->SetBinLabel(i + 1, kEvtSelStages[i].label); + } +} +}; // namespace bjet_tagging_gnn_evtsel +using namespace bjet_tagging_gnn_evtsel; struct BjetTaggingGnn { @@ -128,17 +182,13 @@ struct BjetTaggingGnn { Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; Configurable useEventWeight{"useEventWeight", true, "Flag whether to scale histograms with the event weight"}; - Configurable pTHatMaxMCD{"pTHatMaxMCD", 999.0, "maximum fraction of hard scattering for jet acceptance in detector MC"}; - Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0, "maximum fraction of hard scattering for jet acceptance in particle MC"}; - Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; - // track level configurables Configurable trackSelections{"trackSelections", "QualityTracks", "set track selections"}; Configurable trackPtMin{"trackPtMin", 0.15, "minimum track pT"}; Configurable trackPtMax{"trackPtMax", 1000.0, "maximum track pT"}; Configurable trackEtaMin{"trackEtaMin", -0.9, "minimum track eta"}; Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum track eta"}; - Configurable trackPtMinGnn{"trackPtMinGnn", 0.5, "minimum track pT for GNN inputs"}; + Configurable trackPtMinGnn{"trackPtMinGnn", 0.15, "minimum track pT for GNN inputs"}; Configurable maxIPxy{"maxIPxy", 10, "maximum track DCA in xy plane"}; Configurable maxIPz{"maxIPz", 10, "maximum track DCA in z direction"}; @@ -146,22 +196,27 @@ struct BjetTaggingGnn { Configurable trackNppCrit{"trackNppCrit", 0.95, "track not physical primary ratio"}; // jet level configurables - Configurable jetPtMin{"jetPtMin", 5.0, "minimum jet pT"}; - Configurable jetPtMax{"jetPtMax", 1000.0, "maximum jet pT"}; - Configurable jetEtaMin{"jetEtaMin", -99.0, "minimum jet pseudorapidity"}; - Configurable jetEtaMax{"jetEtaMax", 99.0, "maximum jet pseudorapidity"}; + Configurable jetPtNbins{"jetPtNbins", 300, "number of bins for jet pT"}; + Configurable jetPtMin{"jetPtMin", 0.0, "minimum jet pT"}; + Configurable jetPtMax{"jetPtMax", 300.0, "maximum jet pT"}; + Configurable jetPtSubNbins{"jetPtSubNbins", 300, "number of bins for UE-subtracted jet pT"}; + Configurable jetPtSubMin{"jetPtSubMin", -50.0, "minimum UE-subtracted jet pT"}; + Configurable jetPtSubMax{"jetPtSubMax", 250.0, "maximum UE-subtracted jet pT"}; + Configurable jetEtaMin{"jetEtaMin", -0.9, "minimum jet pseudorapidity"}; + Configurable jetEtaMax{"jetEtaMax", 0.9, "maximum jet pseudorapidity"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT selection on jet constituent"}; Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT selection on jet constituent"}; Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; Configurable> jetRadii{"jetRadii", std::vector{0.4}, "jet resolution parameters"}; - Configurable dbMin{"dbMin", -10., "minimum GNN Db"}; - Configurable dbMax{"dbMax", 20., "maximum GNN Db"}; + Configurable dbMin{"dbMin", -15., "minimum GNN Db"}; + Configurable dbMax{"dbMax", 15., "maximum GNN Db"}; Configurable dbNbins{"dbNbins", 3000, "number of bins in axisDbFine"}; Configurable doDataDriven{"doDataDriven", false, "Flag whether to use fill THnSpase for data driven methods"}; Configurable doDataDrivenExtra{"doDataDrivenExtra", false, "Flag whether to add extra axes to THnSparses"}; + Configurable doDataDrivenSV{"doDataDrivenSV", false, "Flag whether to subscribe SV tables and to use fill THnSparse for data driven methods for SV"}; Configurable callSumw2{"callSumw2", false, "Flag whether to call THnSparse::Sumw2() for error calculation"}; Configurable trainingDatasetRatioParam{"trainingDatasetRatioParam", 0, "Parameter for splitting training/evaluation datasets by collisionId"}; @@ -174,29 +229,33 @@ struct BjetTaggingGnn { OutputObj zorroSummary{"zorroSummary"}; // Service - Service pdg; - Service ccdb; + Service pdg{}; + Service ccdb{}; // Event selection bits std::vector eventSelectionBits; std::vector eventSelectionBitsTVX; std::vector eventSelectionBitsSelMC; std::vector eventSelectionBitsSel8; + std::vector eventSelectionBitsSel8Full; + std::vector eventSelectionBitsSel8FullGood; - int trackSelectionBits; + int trackSelectionBits{}; std::vector jetRadiiValues; void init(InitContext const&) { - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); eventSelectionBitsTVX = jetderiveddatautilities::initialiseEventSelectionBits("TVX"); eventSelectionBitsSel8 = jetderiveddatautilities::initialiseEventSelectionBits("sel8"); eventSelectionBitsSelMC = jetderiveddatautilities::initialiseEventSelectionBits("selMC"); + eventSelectionBitsSel8Full = jetderiveddatautilities::initialiseEventSelectionBits("sel8Full"); + eventSelectionBitsSel8FullGood = jetderiveddatautilities::initialiseEventSelectionBits("sel8Full+IsGoodZvtxFT0vsPV"); - trackSelectionBits = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelectionBits = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (doprocessDataJetsTrig) { zorroSummary.setObject(zorro.getZorroSummary()); @@ -205,7 +264,7 @@ struct BjetTaggingGnn { registry.add("h_event_counter", ";analysis collision counter", {HistType::kTH1F, {{1, 0.0, 1.0}}}, callSumw2); registry.add("h_event_counter_mcp", ";analysis collision matched MC collision counter", {HistType::kTH1F, {{1, 0.0, 1.0}}}, callSumw2); registry.add("h_vertexZ", "Vertex Z;#it{Z} (cm)", {HistType::kTH1F, {{100, -20.0, 20.0}}}, callSumw2); - registry.add("hCollCounter", ";collision counter", {HistType::kTH1F, {{12, 1.0, 13.0}}}, callSumw2); + registry.add("hCollCounter", ";collision counter", {HistType::kTH1F, {{14, 1.0, 15.0}}}, callSumw2); auto hCollCounter = registry.get(HIST("hCollCounter")); hCollCounter->GetXaxis()->SetBinLabel(1, "_1"); hCollCounter->GetXaxis()->SetBinLabel(2, "_2"); @@ -217,9 +276,13 @@ struct BjetTaggingGnn { hCollCounter->GetXaxis()->SetBinLabel(8, "Coll+TVX+NoTFB+Zvtx"); hCollCounter->GetXaxis()->SetBinLabel(9, "Coll+TVX+NoTFB+NoITSROFB"); // sel8 hCollCounter->GetXaxis()->SetBinLabel(10, "Coll+TVX+NoTFB+NoITSROFB+Zvtx"); - hCollCounter->GetXaxis()->SetBinLabel(11, "_11"); - hCollCounter->GetXaxis()->SetBinLabel(12, "INELgt0+Zvtx(rec)"); // sel8 - registry.add("hMcCollCounter", ";MC collision counter", {HistType::kTH1F, {{12, 1.0, 13.0}}}, callSumw2); + hCollCounter->GetXaxis()->SetBinLabel(11, "Coll+TVX+NoTFB+NoITSROFB+NoPileup"); // sel8Full + hCollCounter->GetXaxis()->SetBinLabel(12, "Coll+TVX+NoTFB+NoITSROFB+NoPileup+Zvtx"); + hCollCounter->GetXaxis()->SetBinLabel(13, "Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx"); // sel8FullGood + hCollCounter->GetXaxis()->SetBinLabel(14, "Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx+Zvtx"); + // hCollCounter->GetXaxis()->SetBinLabel(11, "_11"); + // hCollCounter->GetXaxis()->SetBinLabel(12, "INELgt0+Zvtx(rec)"); // sel8 + registry.add("hMcCollCounter", ";MC collision counter", {HistType::kTH1F, {{14, 1.0, 15.0}}}, callSumw2); auto hMcCollCounter = registry.get(HIST("hMcCollCounter")); hMcCollCounter->GetXaxis()->SetBinLabel(1, "McColl(INEL)"); hMcCollCounter->GetXaxis()->SetBinLabel(2, "McColl+Zvtx"); @@ -231,8 +294,31 @@ struct BjetTaggingGnn { hMcCollCounter->GetXaxis()->SetBinLabel(8, "McColl+Zvtx(-> Coll+TVX+NoTFB+Zvtx)"); hMcCollCounter->GetXaxis()->SetBinLabel(9, "McColl(-> Coll+TVX+NoTFB+NoITSROFB)"); // sel8 hMcCollCounter->GetXaxis()->SetBinLabel(10, "McColl+Zvtx(-> Coll+TVX+NoTFB+NoITSROFB+Zvtx)"); - hMcCollCounter->GetXaxis()->SetBinLabel(11, "INELgt0+Zvtx"); - hMcCollCounter->GetXaxis()->SetBinLabel(12, "INELgt0+Zvtx(-> INELgt0+Zvtx(rec))"); // sel8 + hMcCollCounter->GetXaxis()->SetBinLabel(11, "McColl(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup)"); // sel8Full + hMcCollCounter->GetXaxis()->SetBinLabel(12, "McColl+Zvtx(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup+Zvtx)"); + hMcCollCounter->GetXaxis()->SetBinLabel(13, "McColl(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx)"); // sel8FullGood + hMcCollCounter->GetXaxis()->SetBinLabel(14, "McColl+Zvtx(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx+Zvtx)"); + // hMcCollCounter->GetXaxis()->SetBinLabel(11, "INELgt0+Zvtx"); + // hMcCollCounter->GetXaxis()->SetBinLabel(12, "INELgt0+Zvtx(-> INELgt0+Zvtx(rec))"); // sel8 + // Same 14-bin layout as hCollCounter/hMcCollCounter (see fillMcCollCounterBC()), but the ladder is + // evaluated on the MC-truth BC's raw evsel bits instead of a matched reco collision - bins 1-2 + // (INEL/INEL+Zvtx) are placeholders here too since that's already covered by hMcCollCounter. + registry.add("hMcCollCounterBC", ";MC collision counter (BC-based)", {HistType::kTH1F, {{14, 1.0, 15.0}}}, callSumw2); + auto hMcCollCounterBC = registry.get(HIST("hMcCollCounterBC")); + hMcCollCounterBC->GetXaxis()->SetBinLabel(1, "_1"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(2, "_2"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(3, "McColl(-> BC)"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(4, "McColl+Zvtx(-> BC)"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(5, "McColl(-> BC+TVX)"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(6, "McColl+Zvtx(-> BC+TVX)"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(7, "McColl(-> BC+TVX+NoTFB)"); // selMC + hMcCollCounterBC->GetXaxis()->SetBinLabel(8, "McColl+Zvtx(-> BC+TVX+NoTFB)"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(9, "McColl(-> BC+TVX+NoTFB+NoITSROFB)"); // sel8 + hMcCollCounterBC->GetXaxis()->SetBinLabel(10, "McColl+Zvtx(-> BC+TVX+NoTFB+NoITSROFB)"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(11, "McColl(-> BC+TVX+NoTFB+NoITSROFB+NoPileup)"); // sel8Full + hMcCollCounterBC->GetXaxis()->SetBinLabel(12, "McColl+Zvtx(-> BC+TVX+NoTFB+NoITSROFB+NoPileup)"); + hMcCollCounterBC->GetXaxis()->SetBinLabel(13, "McColl(-> BC+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx)"); // sel8FullGood + hMcCollCounterBC->GetXaxis()->SetBinLabel(14, "McColl+Zvtx(-> BC+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx)"); registry.add("hBCCounter", "", {HistType::kTH1F, {{10, 0.0, 10.}}}, callSumw2); auto hBCCounter = registry.get(HIST("hBCCounter")); hBCCounter->GetXaxis()->SetBinLabel(1, "BC"); @@ -248,24 +334,86 @@ struct BjetTaggingGnn { const AxisSpec axisTrackpT{200, 0., 200., "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec axisTrackpTFine{1000, 0., 10., "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisJetpT{250, 0., 250., "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisJetpT{jetPtNbins, jetPtMin, jetPtMax, "#it{p}_{T, ch jet} (GeV/#it{c})"}; + // Used in place of axisJetpT for every "_sub"-suffixed histogram (UE-subtracted jet pT can go negative). + const AxisSpec axisJetpTSub{jetPtSubNbins, jetPtSubMin, jetPtSubMax, "#it{p}_{T, ch jet}^{sub} (GeV/#it{c})"}; const AxisSpec axisJetEta{200, -0.8, 0.8, "#it{#eta}_{jet}"}; const AxisSpec axisDb{200, dbMin, dbMax, "#it{D}_{b}"}; const AxisSpec axisDbFine{dbNbins, dbMin, dbMax, "#it{D}_{b}"}; const AxisSpec axisJetMass{200, 0., 50., "#it{m}_{jet} (GeV/#it{c}^{2})"}; const AxisSpec axisJetProb{200, 0., 40., "-ln(JP)"}; - const AxisSpec axisNTracks{42, 0, 42, "#it{n}_{tracks}"}; + const AxisSpec axisNTracks{42, 0, 42, "#it{N}_{tracks}"}; + // Jet-flavour category axis shared by every "_flavor"-suffixed histogram below (see getJetFlavorCat()/setJetFlavorCatAxisLabels()). + const AxisSpec axisJetFlavorCat{3, -0.5, 2.5, "flavour"}; + const AxisSpec axisLfMatchStatus{2, -0.5, 1.5, "lf match status"}; + // Event-selection-stage axis shared by every "_evtsel"-suffixed histogram below (reco and particle-level + // alike span the full table so a given stage always lands on the same bin, see kEvtSelStages/fillEvtSelStages()). + const AxisSpec axisEvtSelStage{static_cast(kEvtSelStages.size()), -0.5, static_cast(kEvtSelStages.size()) - 0.5, "evt. sel. stage"}; + const AxisSpec axisSVMass{200, 0., 10., "#it{m}_{SV} (GeV/#it{c}^{2})"}; + const AxisSpec axisSVLxyS{200, 0., 100., "L_{xy}/#sigma"}; + const AxisSpec axisSVDispersion{100, 0., 0.5, "SV dispersion"}; + + // `suffix` ("" or "_sub") is appended to jet-pT-dependent histogram names below. Histograms whose value + // doesn't depend on jet pT (h_SVMass, h_jetPhi, ...) are booked once, unsuffixed, outside these lambdas. + if (doprocessDataJetsSV || doprocessMCDJetsSV || doprocessDataJetsSVSub || doprocessMCDJetsSVSub) { + registry.add("h_SVMass", "", {HistType::kTH1F, {axisSVMass}}, callSumw2); + } + auto addSVHistograms = [&](const AxisSpec& axisJetpT, const std::string& suffix) { + // Best SV per jet (highest decay length significance); see fillDataJetHistogramsSV()/fillMCDJetHistogramsSV(). + registry.add("h2_SVMass_jetpT" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVMass}, callSumw2); + registry.add("h2_SVLxyS_jetpT" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVLxyS}, callSumw2); + registry.add("h2_SVDispersion_jetpT" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVDispersion}, callSumw2); + }; + if (doprocessMCDJetsSV || doprocessMCDJetsSVSub) { + registry.add("h2_SVMass_flavor", "", {HistType::kTH2F, {axisSVMass, axisJetFlavorCat}}, callSumw2); + } + // Flavour split into three TH2 (b/c/lf) instead of one TH3 with a flavour axis: GRID-scale hadd + // merging of TH3 has been observed to dump all content into the flavour-axis overflow bin, while the + // equivalent TH2 merges correctly (see fillSVHistograms()/fillMCDJetHistograms() fill sites). + auto addSVFlavorHistograms = [&](const AxisSpec& axisJetpT, const std::string& suffix) { + registry.add("h2_SVMass_jetpT_b" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVMass}, callSumw2); + registry.add("h2_SVMass_jetpT_c" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVMass}, callSumw2); + registry.add("h2_SVMass_jetpT_lf" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVMass}, callSumw2); + registry.add("h2_SVLxyS_jetpT_b" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVLxyS}, callSumw2); + registry.add("h2_SVLxyS_jetpT_c" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVLxyS}, callSumw2); + registry.add("h2_SVLxyS_jetpT_lf" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVLxyS}, callSumw2); + registry.add("h2_SVDispersion_jetpT_b" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVDispersion}, callSumw2); + registry.add("h2_SVDispersion_jetpT_c" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVDispersion}, callSumw2); + registry.add("h2_SVDispersion_jetpT_lf" + suffix, "", HistType::kTH2F, {axisJetpT, axisSVDispersion}, callSumw2); + }; + auto addCoreJetHistograms = [&](const AxisSpec& axisJetpT, const std::string& suffix) { + registry.add("h_jetpT" + suffix, "", HistType::kTH1F, {axisJetpT}, callSumw2); + registry.add("h2_jetpT_Db" + suffix, "", HistType::kTH2F, {axisJetpT, axisDb}); + }; + + if (doprocessDataJetsSV || doprocessMCDJetsSV) { + addSVHistograms(axisJetpT, ""); + } + if (doprocessDataJetsSVSub || doprocessMCDJetsSVSub) { + addSVHistograms(axisJetpTSub, "_sub"); + } + if (doprocessMCDJetsSV) { + addSVFlavorHistograms(axisJetpT, ""); + } + if (doprocessMCDJetsSVSub) { + addSVFlavorHistograms(axisJetpTSub, "_sub"); + } - registry.add("h_jetpT", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); + // h_jetEta/h_jetPhi/h_jetMass/h_Db/h2_nTracks_Db don't depend on jet pT - one shared copy regardless of + // `withSub` (see addCoreJetHistograms()'s comment above). registry.add("h_jetEta", "", {HistType::kTH1F, {axisJetEta}}, callSumw2); - registry.add("h_jetPhi", "", {HistType::kTH1F, {{200, 0., 2. * M_PI, "#it{phi}_{jet}"}}}); + registry.add("h_jetPhi", "", {HistType::kTH1F, {{200, 0., o2::constants::math::TwoPI, "#it{phi}_{jet}"}}}); registry.add("h_jetMass", "", {HistType::kTH1F, {axisJetMass}}); registry.add("h_Db", "", {HistType::kTH1F, {axisDbFine}}); - registry.add("h2_jetpT_Db", "", {HistType::kTH2F, {axisJetpT, axisDb}}); registry.add("h2_nTracks_Db", "", {HistType::kTH2F, {axisNTracks, axisDb}}); + addCoreJetHistograms(axisJetpT, ""); + if (doprocessDataJetsSub || doprocessDataJetsSVSub || doprocessMCDJetsSub || doprocessMCDJetsSVSub || doprocessDataJetsTrigSub) { + addCoreJetHistograms(axisJetpTSub, "_sub"); + } + registry.add("h_gnnfeat_trackpT", "", {HistType::kTH1F, {{200, 0., 100., "#it{p}_{T} (GeV/#it{c})"}}}); - registry.add("h_gnnfeat_trackPhi", "", {HistType::kTH1F, {{200, 0., 2. * M_PI, "#it{#phi}"}}}); + registry.add("h_gnnfeat_trackPhi", "", {HistType::kTH1F, {{200, 0., o2::constants::math::TwoPI, "#it{#phi}"}}}); registry.add("h_gnnfeat_trackEta", "", {HistType::kTH1F, {{200, -0.9, 0.9, "#it{#eta}"}}}); registry.add("h_gnnfeat_trackCharge", "", {HistType::kTH1F, {{3, -1., 2., "#it{q}"}}}); registry.add("h_gnnfeat_trackDCAxy", "", {HistType::kTH1F, {{200, -5., 5., "DCA_{#it{xy}} (cm)"}}}); @@ -283,9 +431,10 @@ struct BjetTaggingGnn { if (doprocessDataTracks || doprocessMCDTracks) { registry.add("h_trackpT", "", {HistType::kTH1F, {axisTrackpT}}, callSumw2); registry.add("h_tracketa", "", {HistType::kTH1F, {{100, trackEtaMin, trackEtaMax, "#it{#eta}"}}}, callSumw2); - registry.add("h_trackphi", "", {HistType::kTH1F, {{100, 0.0, 2.0 * M_PI, "#it{#phi}"}}}, callSumw2); + registry.add("h_trackphi", "", {HistType::kTH1F, {{100, 0.0, o2::constants::math::TwoPI, "#it{#phi}"}}}, callSumw2); registry.add("h_dcaXY", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); registry.add("h_dcaZ", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); } if (doprocessMCDTracks) { @@ -301,7 +450,8 @@ struct BjetTaggingGnn { registry.add("h_dcaXY_coll_matched_c", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); // tracks from matched collisions, c hadron decay registry.add("h_dcaXY_coll_matched_lf", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); // tracks from matched collisions, others registry.add("h_dcaXY_coll_mismatched", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); // tracks from mismatched collisions - registry.add("h_dcaXY_npp", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); // non-physical primary tracks (GenStatusCode=-1) + registry.add("h_dcaXY_npp", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); // non-physical primary tracks (GenStatusCode=-1) from matched collisions + registry.add("h_dcaXY_npp_mismatched", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); // non-physical primary tracks from mismatched collisions registry.add("h_dcaZ_coll_fake", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); registry.add("h_dcaZ_fake", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); registry.add("h_dcaZ_coll_matched", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); @@ -310,164 +460,222 @@ struct BjetTaggingGnn { registry.add("h_dcaZ_coll_matched_lf", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); registry.add("h_dcaZ_coll_mismatched", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); registry.add("h_dcaZ_npp", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); + registry.add("h_dcaZ_npp_mismatched", "", {HistType::kTH1F, {{200, 0., 4., "|DCA_{#it{z}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_coll_fake", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_fake", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_coll_matched", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_coll_matched_b", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_coll_matched_c", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_coll_matched_lf", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_coll_mismatched", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_npp", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); + registry.add("hSparse_dca_pt_npp_mismatched", "", {HistType::kTHnSparseF, {{1000, 0., 100., "#it{p}_{T} (GeV/#it{c})"}, {200, 0., 4., "|DCA_{#it{z}}| (cm)"}, {200, 0., 4., "|DCA_{#it{xy}}| (cm)"}}}, callSumw2); } if (doprocessDataJetsSel || doprocessMCDJetsSel) { - registry.add("h_jetpT_sel8", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_selmc", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_tvx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_coll", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_inelgt0rec", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_sel8_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_selmc_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_tvx_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_coll_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - } - - if (doprocessMCDJets) { - registry.add("h_jetpT_b", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_c", "c-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_lf", "lf-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_Db_b", "b-jet", {HistType::kTH1F, {axisDbFine}}); - registry.add("h_Db_c", "c-jet", {HistType::kTH1F, {axisDbFine}}); - registry.add("h_Db_lf", "lf-jet", {HistType::kTH1F, {axisDbFine}}); - registry.add("h2_jetpT_Db_b", "b-jet", {HistType::kTH2F, {axisJetpT, axisDb}}); - registry.add("h2_jetpT_Db_c", "c-jet", {HistType::kTH2F, {axisJetpT, axisDb}}); - registry.add("h2_jetpT_Db_lf", "lf-jet", {HistType::kTH2F, {axisJetpT, axisDb}}); - registry.add("h2_nTracks_Db_b", "b-jet", {HistType::kTH2F, {axisNTracks, axisDb}}); - registry.add("h2_nTracks_Db_c", "c-jet", {HistType::kTH2F, {axisNTracks, axisDb}}); - registry.add("h2_nTracks_Db_lf", "lf-jet", {HistType::kTH2F, {axisNTracks, axisDb}}); - registry.add("h2_Response_DetjetpT_PartjetpT", "", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_b", "b-jet", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_c", "c-jet", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_lf", "lf-jet", {HistType::kTH2F, {axisJetpT, axisJetpT}, callSumw2}); - registry.add("h2_jetpT_Db_lf_none", "lf-jet (none)", {HistType::kTH2F, {axisJetpT, axisDb}}, callSumw2); - registry.add("h2_jetpT_Db_lf_matched", "lf-jet (matched)", {HistType::kTH2F, {axisJetpT, axisDb}}, callSumw2); - registry.add("h2_jetpT_Db_npp", "NotPhysPrim", {HistType::kTH2F, {axisJetpT, axisDb}}); - registry.add("h2_jetpT_Db_npp_b", "NotPhysPrim b-jet", {HistType::kTH2F, {axisJetpT, axisDb}}); - registry.add("h2_jetpT_Db_npp_c", "NotPhysPrim c-jet", {HistType::kTH2F, {axisJetpT, axisDb}}); - registry.add("h2_jetpT_Db_npp_lf", "NotPhysPrim lf-jet", {HistType::kTH2F, {axisJetpT, axisDb}}); + auto hEvtsel = registry.add("h2_jetpT_evtsel", "", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + setEvtSelStageAxisLabels(hEvtsel->GetYaxis(), kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + } + if (doprocessDataJetsSelSub || doprocessMCDJetsSelSub) { + auto hEvtselSub = registry.add("h2_jetpT_evtsel_sub", "", HistType::kTH2F, {axisJetpTSub, axisEvtSelStage}, callSumw2); + setEvtSelStageAxisLabels(hEvtselSub->GetYaxis(), kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + } + + // b/c/lf categories collapsed onto axisJetFlavorCat instead of one histogram per category. Also includes + // the geometric matched-response and pTHat-sparse histograms, which get UE-subtracted variants too, not + // just the core Db/flavor spectra. Histograms whose value doesn't depend on jetpT are booked once, above. + if (doprocessMCDJets || doprocessMCDJetsSV || doprocessMCDJetsSub || doprocessMCDJetsSVSub) { + registry.add("h2_Db_flavor", "", {HistType::kTH2F, {axisDbFine, axisJetFlavorCat}}); + registry.add("h2_nTracks_Db_b", "", {HistType::kTH2F, {axisNTracks, axisDb}}); + registry.add("h2_nTracks_Db_c", "", {HistType::kTH2F, {axisNTracks, axisDb}}); + registry.add("h2_nTracks_Db_lf", "", {HistType::kTH2F, {axisNTracks, axisDb}}); + registry.add("h2_Db_lfmatch", "lf-jet", {HistType::kTH2F, {axisDbFine, axisLfMatchStatus}}); registry.add("h_Db_npp", "NotPhysPrim", {HistType::kTH1F, {axisDbFine}}); - registry.add("h_Db_npp_b", "NotPhysPrim b-jet", {HistType::kTH1F, {axisDbFine}}); - registry.add("h_Db_npp_c", "NotPhysPrim c-jet", {HistType::kTH1F, {axisDbFine}}); - registry.add("h_Db_npp_lf", "NotPhysPrim lf-jet", {HistType::kTH1F, {axisDbFine}}); - registry.add("h_jetpT_matched", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_matched", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_matched", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_matched", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); + registry.add("h2_Db_npp_flavor", "NotPhysPrim", {HistType::kTH2F, {axisDbFine, axisJetFlavorCat}}); + registry.add("h2_Db_npp_lfmatch", "NotPhysPrim lf-jet", {HistType::kTH2F, {axisDbFine, axisLfMatchStatus}}); + setLfMatchStatusAxisLabels(registry.get(HIST("h2_Db_lfmatch"))->GetYaxis()); + setLfMatchStatusAxisLabels(registry.get(HIST("h2_Db_npp_lfmatch"))->GetYaxis()); + setJetFlavorCatAxisLabels(registry.get(HIST("h2_Db_flavor"))->GetYaxis()); + setJetFlavorCatAxisLabels(registry.get(HIST("h2_Db_npp_flavor"))->GetYaxis()); + } + auto addMCDFlavorAndMatchedHistograms = [&](const AxisSpec& axisJetpT, const std::string& suffix) { + auto hJetpTFlavor = registry.add("h2_jetpT_flavor" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetFlavorCat}, callSumw2); + registry.add("h2_jetpT_Db_b" + suffix, "", HistType::kTH2F, {axisJetpT, axisDb}); + registry.add("h2_jetpT_Db_c" + suffix, "", HistType::kTH2F, {axisJetpT, axisDb}); + registry.add("h2_jetpT_Db_lf" + suffix, "", HistType::kTH2F, {axisJetpT, axisDb}); + auto hJetpTLfmatch = registry.add("h2_jetpT_lfmatch" + suffix, "lf-jet", HistType::kTH2F, {axisJetpT, axisLfMatchStatus}, callSumw2); + setLfMatchStatusAxisLabels(hJetpTLfmatch->GetYaxis()); + registry.add("h2_jetpT_Db_lfmatched" + suffix, "lf-jet", HistType::kTH2F, {axisJetpT, axisDb}, callSumw2); + registry.add("h2_jetpT_Db_lfnone" + suffix, "lf-jet", HistType::kTH2F, {axisJetpT, axisDb}, callSumw2); + auto hResponse = registry.add("h2_Response_DetjetpT_PartjetpT" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetpT}, callSumw2); + registry.add("h2_Response_DetjetpT_PartjetpT_b" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetpT}, callSumw2); + registry.add("h2_Response_DetjetpT_PartjetpT_c" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetpT}, callSumw2); + registry.add("h2_Response_DetjetpT_PartjetpT_lf" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetpT}, callSumw2); + registry.add("h2_jetpT_Db_npp" + suffix, "NotPhysPrim", HistType::kTH2F, {axisJetpT, axisDb}); + registry.add("h2_jetpT_Db_npp_b" + suffix, "NotPhysPrim", HistType::kTH2F, {axisJetpT, axisDb}); + registry.add("h2_jetpT_Db_npp_c" + suffix, "NotPhysPrim", HistType::kTH2F, {axisJetpT, axisDb}); + registry.add("h2_jetpT_Db_npp_lf" + suffix, "NotPhysPrim", HistType::kTH2F, {axisJetpT, axisDb}); + setJetFlavorCatAxisLabels(hJetpTFlavor->GetYaxis()); + (void)hResponse; + + registry.add("h_jetpT_matched" + suffix, "", HistType::kTH1F, {axisJetpT}, callSumw2); + registry.add("h_jetpT_particle_matched" + suffix, "", HistType::kTH1F, {axisJetpT}, callSumw2); + auto hJetpTMatchedFlavor = registry.add("h2_jetpT_matched_flavor" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetFlavorCat}, callSumw2); + auto hJetpTParticleMatchedFlavor = registry.add("h2_jetpT_particle_matched_flavor" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetFlavorCat}, callSumw2); + setJetFlavorCatAxisLabels(hJetpTMatchedFlavor->GetYaxis()); + setJetFlavorCatAxisLabels(hJetpTParticleMatchedFlavor->GetYaxis()); + // MCP jets + registry.add("h_jetpT_particle" + suffix, "", HistType::kTH1F, {axisJetpT}, callSumw2); + auto hJetpTParticleFlavor = registry.add("h2_jetpT_particle_flavor" + suffix, "", HistType::kTH2F, {axisJetpT, axisJetFlavorCat}, callSumw2); + setJetFlavorCatAxisLabels(hJetpTParticleFlavor->GetYaxis()); + // pTHat study + registry.add("hSparse_pthat_jetpT" + suffix, "", HistType::kTHnSparseF, {{300, 0., 300., "#hat{#it{p}}_{T} (GeV/#it{c})"}, axisJetpT, axisJetpT}, callSumw2); + registry.add("hSparse_pthat_jetpT_b" + suffix, "b-jet", HistType::kTHnSparseF, {{300, 0., 300., "#hat{#it{p}}_{T} (GeV/#it{c})"}, axisJetpT, axisJetpT}, callSumw2); + registry.add("hSparse_pthat_jetpT_c" + suffix, "c-jet", HistType::kTHnSparseF, {{300, 0., 300., "#hat{#it{p}}_{T} (GeV/#it{c})"}, axisJetpT, axisJetpT}, callSumw2); + }; + + if (doprocessMCDJets || doprocessMCDJetsSV) { + addMCDFlavorAndMatchedHistograms(axisJetpT, ""); + } + if (doprocessMCDJetsSub || doprocessMCDJetsSVSub) { + addMCDFlavorAndMatchedHistograms(axisJetpTSub, "_sub"); } if (doprocessMCDJetsSel) { - registry.add("h_jetpT_b_sel8", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_sel8_zvtx", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_sel8", "", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_b_sel8", "b-jet", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_selmc", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_selmc_zvtx", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_selmc", "", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_b_selmc", "b-jet", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_tvx", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_tvx_zvtx", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_tvx", "", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_b_tvx", "b-jet", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_coll", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_coll_zvtx", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_b_inelgt0rec", "b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_inelgt0", "", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); - registry.add("h2_Response_DetjetpT_PartjetpT_b_inelgt0", "b-jet", {HistType::kTH2F, {axisJetpT, axisJetpT}}, callSumw2); + auto hEvtselB = registry.add("h2_jetpT_evtsel_b", "b-jet", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + auto hEvtselC = registry.add("h2_jetpT_evtsel_c", "c-jet", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + setEvtSelStageAxisLabels(hEvtselB->GetYaxis(), kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + setEvtSelStageAxisLabels(hEvtselC->GetYaxis(), kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + } + if (doprocessMCDJetsSelSub) { + auto hEvtselBSub = registry.add("h2_jetpT_evtsel_b_sub", "b-jet", HistType::kTH2F, {axisJetpTSub, axisEvtSelStage}, callSumw2); + auto hEvtselCSub = registry.add("h2_jetpT_evtsel_c_sub", "c-jet", HistType::kTH2F, {axisJetpTSub, axisEvtSelStage}, callSumw2); + setEvtSelStageAxisLabels(hEvtselBSub->GetYaxis(), kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + setEvtSelStageAxisLabels(hEvtselCSub->GetYaxis(), kEvtSelStageRecoFirst, kEvtSelStageRecoLast); } - if (doprocessMCPJets) { + // Used by both processMCPJets ("", axisJetpT) and processMCPJetsSub ("_sub", axisJetpTSub). + auto addMCPEvtSelHistograms = [&](const AxisSpec& axisJetpT, const std::string& suffix) { + // All INEL/Coll/TVX/SelMC/Sel8(+Zvtx) variants collapsed onto axisEvtSelStage. + auto hEvtsel = registry.add("h2_jetpT_particle_evtsel" + suffix, "", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + auto hEvtselB = registry.add("h2_jetpT_particle_evtsel_b" + suffix, "particle b-jet", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + auto hEvtselC = registry.add("h2_jetpT_particle_evtsel_c" + suffix, "particle c-jet", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + setEvtSelStageAxisLabels(hEvtsel->GetYaxis()); + setEvtSelStageAxisLabels(hEvtselB->GetYaxis()); + setEvtSelStageAxisLabels(hEvtselC->GetYaxis()); + + // Same as above, but the event-selection stage is evaluated on the MC-truth BC directly + // (see fillMcCollCounterBC()) instead of via a matched reco collision. + auto hEvtselbc = registry.add("h2_jetpT_particle_evtselbc" + suffix, "", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + auto hEvtselbcB = registry.add("h2_jetpT_particle_evtselbc_b" + suffix, "particle b-jet", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + auto hEvtselbcC = registry.add("h2_jetpT_particle_evtselbc_c" + suffix, "particle c-jet", HistType::kTH2F, {axisJetpT, axisEvtSelStage}, callSumw2); + setEvtSelStageAxisLabels(hEvtselbc->GetYaxis()); + setEvtSelStageAxisLabels(hEvtselbcB->GetYaxis()); + setEvtSelStageAxisLabels(hEvtselbcC->GetYaxis()); + }; + + // processMCPJetsCommon() fills these unconditionally regardless of `withSub` (see fillMcCollCounterBC()/ + // hMcCollCounter's convention) - must be booked whenever either the raw or Sub process is on, not just + // processMCPJets (processMCPJetsSub alone would otherwise fill an unbooked histogram and throw at runtime). + if (doprocessMCPJets || doprocessMCPJetsSub) { registry.add("h_vertexZ_truth", "Vertex Z truth;#it{Z} (cm)", {HistType::kTH1F, {{100, -20.0, 20.0}}}, callSumw2); registry.add("h_vertexZ_truth_coll", "Vertex Z truth (Coll);#it{Z} (cm)", {HistType::kTH1F, {{100, -20.0, 20.0}}}, callSumw2); registry.add("h_vertexZ_truth_tvx", "Vertex Z truth (TVX);#it{Z} (cm)", {HistType::kTH1F, {{100, -20.0, 20.0}}}, callSumw2); - - registry.add("h_jetpT_particle", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_c", "particle c-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_lf", "particle lf-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - - registry.add("h_jetpT_particle_sel8", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_sel8", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_selmc", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_selmc", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_tvx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_tvx", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_coll", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_coll", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_inel", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_inel", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - - registry.add("h_jetpT_particle_sel8_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_sel8_zvtx", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_selmc_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_selmc_zvtx", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_tvx_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_tvx_zvtx", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_coll_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_coll_zvtx", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_inel_zvtx", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_inel_zvtx", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - - registry.add("h_jetpT_particle_inelgt0", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_inelgt0", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_inelgt0rec", "", {HistType::kTH1F, {axisJetpT}}, callSumw2); - registry.add("h_jetpT_particle_b_inelgt0rec", "particle b-jet", {HistType::kTH1F, {axisJetpT}}, callSumw2); + registry.add("h_vertexZ_truth_bctvx", "Vertex Z truth (BC+TVX);#it{Z} (cm)", {HistType::kTH1F, {{100, -20.0, 20.0}}}, callSumw2); + } + if (doprocessMCPJets) { + addMCPEvtSelHistograms(axisJetpT, ""); + } + if (doprocessMCPJetsSub) { + addMCPEvtSelHistograms(axisJetpTSub, "_sub"); } - if (doDataDriven) { - if (doDataDrivenExtra) { - registry.add("hSparse_Incljets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}}, callSumw2); - if (doprocessMCDJets) { - registry.add("hSparse_bjets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}}, callSumw2); - registry.add("hSparse_cjets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}}, callSumw2); - registry.add("hSparse_lfjets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}}, callSumw2); - registry.add("hSparse_lfjets_none", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}}, callSumw2); - registry.add("hSparse_lfjets_matched", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}}, callSumw2); + auto addDataDrivenHistograms = [&](const AxisSpec& axisJetpT, const std::string& suffix, bool mcdJetsEnabled) { + // hSparse_Incljets' last axis is the best-SV mass instead of the jet mass when doDataDrivenSV is on + // (only meaningful together with processDataJetsSV/processMCDJetsSV, see fill*JetHistogramsSV()). + if (doDataDrivenSV) { + registry.add("hSparse_Incljets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisSVMass}, callSumw2); + } else if (doDataDrivenExtra) { + registry.add("hSparse_Incljets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}, callSumw2); + } else { + registry.add("hSparse_Incljets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}, callSumw2); + } + if (doDataDrivenSV) { + if (mcdJetsEnabled) { + registry.add("hSparse_bjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisSVMass}, callSumw2); + registry.add("hSparse_cjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisSVMass}, callSumw2); + registry.add("hSparse_lfjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisSVMass}, callSumw2); + registry.add("hSparse_lfjets_none" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisSVMass}, callSumw2); + registry.add("hSparse_lfjets_matched" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisSVMass}, callSumw2); + } + } else if (doDataDrivenExtra) { + if (mcdJetsEnabled) { + registry.add("hSparse_bjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}, callSumw2); + registry.add("hSparse_cjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}, callSumw2); + registry.add("hSparse_lfjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}, callSumw2); + registry.add("hSparse_lfjets_none" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}, callSumw2); + registry.add("hSparse_lfjets_matched" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks, axisJetMass}, callSumw2); } } else { - registry.add("hSparse_Incljets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}}, callSumw2); - if (doprocessMCDJets) { - registry.add("hSparse_bjets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}}, callSumw2); - registry.add("hSparse_cjets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}}, callSumw2); - registry.add("hSparse_lfjets", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}}, callSumw2); - registry.add("hSparse_lfjets_none", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}}, callSumw2); - registry.add("hSparse_lfjets_matched", "", {HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}}, callSumw2); + if (mcdJetsEnabled) { + registry.add("hSparse_bjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}, callSumw2); + registry.add("hSparse_cjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}, callSumw2); + registry.add("hSparse_lfjets" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}, callSumw2); + registry.add("hSparse_lfjets_none" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}, callSumw2); + registry.add("hSparse_lfjets_matched" + suffix, "", HistType::kTHnSparseF, {axisJetpT, axisDbFine, axisNTracks}, callSumw2); } } + }; + + if (doDataDriven) { + addDataDrivenHistograms(axisJetpT, "", doprocessMCDJets || doprocessMCDJetsSV); + if (doprocessDataJetsSub || doprocessDataJetsSVSub || doprocessMCDJetsSub || doprocessMCDJetsSVSub || doprocessDataJetsTrigSub) { + addDataDrivenHistograms(axisJetpTSub, "_sub", doprocessMCDJetsSub || doprocessMCDJetsSVSub); + } } } + // Filters Filter collisionFilter = nabs(aod::jcollision::posZ) < vertexZCut; Filter trackFilter = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); Filter jetFilter = (aod::jet::pt >= jetPtMin && aod::jet::pt < jetPtMax && aod::jet::eta < jetEtaMax - aod::jet::r / 100.f && aod::jet::eta > jetEtaMin + aod::jet::r / 100.f); - using AnalysisCollisions = soa::Join; + // Data + using AnalysisCollisions = soa::Join; using FilteredCollisions = soa::Filtered; - using AnalysisCollisionsTriggered = soa::Join; + using AnalysisCollisionsTriggered = soa::Join; using FilteredCollisionsTriggered = soa::Filtered; - using OrigCollisions = soa::Join; + // using OrigCollisions = soa::Join; + using DataJets = soa::Join; using FilteredDataJets = soa::Filtered; + using DataJetsSV = soa::Join; + using FilteredDataJetsSV = soa::Filtered; + using AnalysisTracks = soa::Join; using FilteredTracks = soa::Filtered; - using OriginalTracks = soa::Join; + // using OriginalTracks = soa::Join; + + // MCD + using AnalysisCollisionsMCD = soa::Join; + using FilteredCollisionsMCD = soa::Filtered; using MCDJets = soa::Join; using FilteredMCDJets = soa::Filtered; + using MCDJetsSV = soa::Join; + using FilteredMCDJetsSV = soa::Filtered; + using AnalysisTracksMCD = soa::Join; using FilteredTracksMCD = soa::Filtered; - using AnalysisCollisionsMCD = soa::Join; - using FilteredCollisionsMCD = soa::Filtered; + // MCP + using AnalysisCollisionsMCP = soa::Join; - Filter mccollisionFilter = nabs(aod::jmccollision::posZ) < vertexZCut; - using FilteredCollisionsMCP = soa::Filtered; using MCPJets = soa::Join; using FilteredMCPJets = soa::Filtered; - template - int analyzeJetTrackInfo(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, AnyOriginalTracks const /*origTracks*/ /*, int8_t jetFlavor = 0, double weight = 1.0*/) + template + int analyzeJetTrackInfo(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/ /*, int8_t jetFlavor = 0, double weight = 1.0*/) { int nTracks = 0; for (const auto& constituent : analysisJet.template tracks_as()) { @@ -477,21 +685,21 @@ struct BjetTaggingGnn { } int sign = jettaggingutilities::getGeoSign(analysisJet, constituent); - auto origConstit = constituent.template track_as(); + // auto origConstit = constituent.template track_as(); registry.fill(HIST("h_gnnfeat_trackpT"), constituent.pt()); - registry.fill(HIST("h_gnnfeat_trackPhi"), origConstit.phi()); + registry.fill(HIST("h_gnnfeat_trackPhi"), constituent.phi()); registry.fill(HIST("h_gnnfeat_trackEta"), constituent.eta()); registry.fill(HIST("h_gnnfeat_trackCharge"), static_cast(constituent.sign())); registry.fill(HIST("h_gnnfeat_trackDCAxy"), std::abs(constituent.dcaXY()) * sign); registry.fill(HIST("h_gnnfeat_trackSigmaDCAxy"), constituent.sigmadcaXY()); registry.fill(HIST("h_gnnfeat_trackDCAz"), std::abs(constituent.dcaZ()) * sign); registry.fill(HIST("h_gnnfeat_trackSigmaDCAz"), constituent.sigmadcaZ()); - registry.fill(HIST("h_gnnfeat_trackITSNCls"), static_cast(origConstit.itsNCls())); - registry.fill(HIST("h_gnnfeat_trackTPCNCls"), static_cast(origConstit.tpcNClsFound())); - registry.fill(HIST("h_gnnfeat_trackTPCNCrossedRows"), static_cast(origConstit.tpcNClsCrossedRows())); - registry.fill(HIST("h_gnnfeat_trackITSChi2NCl"), origConstit.itsChi2NCl()); - registry.fill(HIST("h_gnnfeat_trackTPCChi2NCl"), origConstit.tpcChi2NCl()); + // registry.fill(HIST("h_gnnfeat_trackITSNCls"), static_cast(origConstit.itsNCls())); + // registry.fill(HIST("h_gnnfeat_trackTPCNCls"), static_cast(origConstit.tpcNClsFound())); + // registry.fill(HIST("h_gnnfeat_trackTPCNCrossedRows"), static_cast(origConstit.tpcNClsCrossedRows())); + // registry.fill(HIST("h_gnnfeat_trackITSChi2NCl"), origConstit.itsChi2NCl()); + // registry.fill(HIST("h_gnnfeat_trackTPCChi2NCl"), origConstit.tpcChi2NCl()); registry.fill(HIST("h_gnnfeat_tracksIPxy"), std::abs(constituent.dcaXY()) * sign / constituent.sigmadcaXY()); registry.fill(HIST("h_gnnfeat_tracksIPz"), std::abs(constituent.dcaZ()) * sign / constituent.sigmadcaZ()); @@ -501,18 +709,20 @@ struct BjetTaggingGnn { return nTracks; } + static constexpr float largeNegativeNumber = -98.0f; + static constexpr float largePositiveNumber = 9999.0f; + template bool isAcceptedJet(AnalysisJet const& jet) { - - if (jetAreaFractionMin > -98.0) { - if (jet.area() < jetAreaFractionMin * M_PI * (jet.r() / 100.0) * (jet.r() / 100.0)) { + if (jetAreaFractionMin > largeNegativeNumber) { + if (jet.area() < jetAreaFractionMin * o2::constants::math::PI * (jet.r() / 100.0) * (jet.r() / 100.0)) { return false; } } bool checkConstituentPt = true; - bool checkConstituentMinPt = (leadingConstituentPtMin > -98.0); - bool checkConstituentMaxPt = (leadingConstituentPtMax < 9998.0); + bool checkConstituentMinPt = (leadingConstituentPtMin > largeNegativeNumber); + bool checkConstituentMaxPt = (leadingConstituentPtMax < largePositiveNumber); if (!checkConstituentMinPt && !checkConstituentMaxPt) { checkConstituentPt = false; } @@ -537,33 +747,186 @@ struct BjetTaggingGnn { return true; } - template - void fillDataJetHistograms(AnalysisJet const& analysisJet, AnyTracks const& allTracks, AnyOriginalTracks const& origTracks) + // Jet-flavour categories collapsed onto a single axis (instead of one histogram per category): b, c, lf. + static double getJetFlavorCat(int8_t jetFlavor) + { + if (jetFlavor == JetTaggingSpecies::beauty) { + return 0.; + } + if (jetFlavor == JetTaggingSpecies::charm) { + return 1.; + } + return 2.; // lf + } + + static void setJetFlavorCatAxisLabels(TAxis* axis) { - int nTracks = analyzeJetTrackInfo(analysisJet, allTracks, origTracks); + axis->SetBinLabel(1, "b"); + axis->SetBinLabel(2, "c"); + axis->SetBinLabel(3, "lf"); + } - registry.fill(HIST("h_jetpT"), analysisJet.pt()); + // Fills one bin per satisfied event-selection stage in [first, last] (see kEvtSelStages). Bin index + // == stage index i (not offset by `first`), since the histogram's axis always spans the full table. + // Callers pass the "_sub"-suffixed HIST name directly for a UE-subtracted or MC-particle-context variant + // of the same histogram (single `registry`, see its declaration). + void fillEvtSelStages(auto const& histName, double xVal, EvtSelFlag evtselCode, double weight, int first, int last) + { + for (int i = first; i <= last; ++i) { + if (hasAll(evtselCode, kEvtSelStages[i].flag)) { + registry.fill(histName, xVal, static_cast(i), weight); + } + } + } + + // lf-jets further split by MC-particle-jet match status (see h2_jetpT_Db_lfmatched/h2_jetpT_Db_lfnone). + static double getLfMatchStatus(int8_t jetFlavor) + { + return (jetFlavor == JetTaggingSpecies::none) ? 1. : 0.; + } + + static void setLfMatchStatusAxisLabels(TAxis* axis) + { + axis->SetBinLabel(1, "matched"); + axis->SetBinLabel(2, "none"); + } + + // Selects the SV with the highest decay length significance out of all SVs matched to the jet, + // shared by fillDataJetHistogramsSV()/fillMCDJetHistogramsSV(). Fills the inclusive SV histograms + // (plus the flavour-split variants when jetFlavor >= 0, i.e. for MCD jets) and returns the + // selected SV's mass (-1 if the jet has no SV). `jetpT` is passed in already raw-or-UE-subtracted by + // the caller (per `withSub`) rather than recomputed here, since this function doesn't otherwise need rho. + template + float fillSVHistograms(AnalysisJet const& analysisJet, SecondaryVertices const& /*allSVs*/, float jetpT, double weightEvt = 1.0, int8_t jetFlavor = -1) + { + auto svs = analysisJet.template secondaryVertices_as(); + if (svs.size() == 0) { + // Not jet-pT-dependent (no-SV sentinel) - one shared histogram regardless of `withSub`. + registry.fill(HIST("h_SVMass"), -1.f, weightEvt); + return -1.f; + } + const auto& sv = *std::max_element(svs.begin(), svs.end(), [](auto const& svA, auto const& svB) { + return (svA.decayLengthXY() / svA.errorDecayLengthXY()) < (svB.decayLengthXY() / svB.errorDecayLengthXY()); + }); + float massSV = sv.m(); + float lxyS = sv.decayLengthXY() / sv.errorDecayLengthXY(); + // h_SVMass/h2_SVMass_flavor don't use jetpT as a fill value - shared regardless of `withSub`. The + // jetpT-axis histograms below get the "_sub" name instead of a second registry (see `registry`'s + // declaration). + registry.fill(HIST("h_SVMass"), massSV, weightEvt); + if (jetFlavor >= 0) { + registry.fill(HIST("h2_SVMass_flavor"), massSV, getJetFlavorCat(jetFlavor), weightEvt); + } + if constexpr (withSub) { + registry.fill(HIST("h2_SVMass_jetpT_sub"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT_sub"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT_sub"), jetpT, sv.dispersion(), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("h2_SVMass_jetpT_b_sub"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT_b_sub"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT_b_sub"), jetpT, sv.dispersion(), weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("h2_SVMass_jetpT_c_sub"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT_c_sub"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT_c_sub"), jetpT, sv.dispersion(), weightEvt); + } else if (jetFlavor >= 0) { + registry.fill(HIST("h2_SVMass_jetpT_lf_sub"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT_lf_sub"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT_lf_sub"), jetpT, sv.dispersion(), weightEvt); + } + } else { + registry.fill(HIST("h2_SVMass_jetpT"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT"), jetpT, sv.dispersion(), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("h2_SVMass_jetpT_b"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT_b"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT_b"), jetpT, sv.dispersion(), weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("h2_SVMass_jetpT_c"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT_c"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT_c"), jetpT, sv.dispersion(), weightEvt); + } else if (jetFlavor >= 0) { + registry.fill(HIST("h2_SVMass_jetpT_lf"), jetpT, massSV, weightEvt); + registry.fill(HIST("h2_SVLxyS_jetpT_lf"), jetpT, lxyS, weightEvt); + registry.fill(HIST("h2_SVDispersion_jetpT_lf"), jetpT, sv.dispersion(), weightEvt); + } + } + return massSV; + } + + // `withSub` selects UE(rho*area)-subtracted jet pT + the "_sub"-suffixed histogram set in the single + // `registry` instead of raw pT + the unsuffixed names - used by processDataJets(SV)/processDataJets(SV)Sub. + template + int fillDataJetHistograms(AnalysisJet const& analysisJet, AnyTracks const& allTracks, float rho = 0.0) + { + int nTracks = analyzeJetTrackInfo(analysisJet, allTracks); + + float jetpT = withSub ? (analysisJet.pt() - rho * analysisJet.area()) : analysisJet.pt(); + // h_jetPhi/h_jetEta/h_jetMass/h_Db/h2_nTracks_Db don't use jetpT as a fill value - one shared histogram + // regardless of `withSub` (see addCoreJetHistograms()'s init() comment). Only h_jetpT/h2_jetpT_Db/ + // hSparse_Incljets actually differ between the raw and Sub call, so only those get the "_sub" name. registry.fill(HIST("h_jetPhi"), analysisJet.phi()); registry.fill(HIST("h_jetEta"), analysisJet.eta()); registry.fill(HIST("h_jetMass"), analysisJet.mass()); registry.fill(HIST("h_Db"), analysisJet.scoreML()); - registry.fill(HIST("h2_jetpT_Db"), analysisJet.pt(), analysisJet.scoreML()); registry.fill(HIST("h2_nTracks_Db"), nTracks, analysisJet.scoreML()); - if (doDataDriven) { - if (doDataDrivenExtra) { - registry.fill(HIST("hSparse_Incljets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, analysisJet.mass()); - } else { - registry.fill(HIST("hSparse_Incljets"), analysisJet.pt(), analysisJet.scoreML(), nTracks); + if constexpr (withSub) { + registry.fill(HIST("h_jetpT_sub"), jetpT); + registry.fill(HIST("h2_jetpT_Db_sub"), jetpT, analysisJet.scoreML()); + + if (doDataDriven && !doDataDrivenSV) { + if (doDataDrivenExtra) { + registry.fill(HIST("hSparse_Incljets_sub"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass()); + } else { + registry.fill(HIST("hSparse_Incljets_sub"), jetpT, analysisJet.scoreML(), nTracks); + } + } + } else { + registry.fill(HIST("h_jetpT"), jetpT); + registry.fill(HIST("h2_jetpT_Db"), jetpT, analysisJet.scoreML()); + + if (doDataDriven && !doDataDrivenSV) { + if (doDataDrivenExtra) { + registry.fill(HIST("hSparse_Incljets"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass()); + } else { + registry.fill(HIST("hSparse_Incljets"), jetpT, analysisJet.scoreML(), nTracks); + } } } + return nTracks; } - template - int8_t fillMCDJetHistograms(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, AnyOriginalTracks const& /*origTracks*/, double weightEvt = 1.0) + template + void fillDataJetHistogramsSV(AnalysisJet const& analysisJet, AnyTracks const& allTracks, AnySVs const& allSVs, float rho = 0.0) { - int8_t jetFlavor = analysisJet.origin(); + int nTracks = fillDataJetHistograms(analysisJet, allTracks, rho); + float jetpT = withSub ? (analysisJet.pt() - rho * analysisJet.area()) : analysisJet.pt(); + float massSV = fillSVHistograms(analysisJet, allSVs, jetpT); + + if (doDataDriven && doDataDrivenSV) { + if constexpr (withSub) { + registry.fill(HIST("hSparse_Incljets_sub"), jetpT, analysisJet.scoreML(), nTracks, massSV); + } else { + registry.fill(HIST("hSparse_Incljets"), jetpT, analysisJet.scoreML(), nTracks, massSV); + } + } + } + // jetFlavor: JetTaggingSpecies of the jet; nTracks: number of GNN-input constituents (reused by fillMCDJetHistogramsSV()). + struct JetHistFillResult { + int8_t jetFlavor; + // cppcheck-suppress unusedStructMember + int nTracks; + }; + + // Constituent loop factored out of fillMCDJetHistograms(): the h_gnnfeat_* track-QA histograms it fills + // don't depend on the jet's own (possibly UE-subtracted) pT, so they always use the unsuffixed names + // regardless of `withSub` - no "_sub" duplicates needed. + template + std::pair analyzeMCDJetTrackInfo(AnalysisJet const& analysisJet, AnyTracks const& /*allTracks*/, double weightEvt = 1.0) + { int nTracks = 0; int nNppTracks = 0; for (const auto& constituent : analysisJet.template tracks_as()) { @@ -575,101 +938,230 @@ struct BjetTaggingGnn { } int sign = jettaggingutilities::getGeoSign(analysisJet, constituent); - auto origConstit = constituent.template track_as(); - - registry.fill(HIST("h_gnnfeat_trackpT"), constituent.pt()); - registry.fill(HIST("h_gnnfeat_trackPhi"), origConstit.phi()); - registry.fill(HIST("h_gnnfeat_trackEta"), constituent.eta()); - registry.fill(HIST("h_gnnfeat_trackCharge"), static_cast(constituent.sign())); - registry.fill(HIST("h_gnnfeat_trackDCAxy"), std::abs(constituent.dcaXY()) * sign); - registry.fill(HIST("h_gnnfeat_trackSigmaDCAxy"), constituent.sigmadcaXY()); - registry.fill(HIST("h_gnnfeat_trackDCAz"), std::abs(constituent.dcaZ()) * sign); - registry.fill(HIST("h_gnnfeat_trackSigmaDCAz"), constituent.sigmadcaZ()); - registry.fill(HIST("h_gnnfeat_trackITSNCls"), static_cast(origConstit.itsNCls())); - registry.fill(HIST("h_gnnfeat_trackTPCNCls"), static_cast(origConstit.tpcNClsFound())); - registry.fill(HIST("h_gnnfeat_trackTPCNCrossedRows"), static_cast(origConstit.tpcNClsCrossedRows())); - registry.fill(HIST("h_gnnfeat_trackITSChi2NCl"), origConstit.itsChi2NCl()); - registry.fill(HIST("h_gnnfeat_trackTPCChi2NCl"), origConstit.tpcChi2NCl()); - - registry.fill(HIST("h_gnnfeat_tracksIPxy"), std::abs(constituent.dcaXY()) * sign / constituent.sigmadcaXY()); - registry.fill(HIST("h_gnnfeat_tracksIPz"), std::abs(constituent.dcaZ()) * sign / constituent.sigmadcaZ()); + // auto origConstit = constituent.template track_as(); + + registry.fill(HIST("h_gnnfeat_trackpT"), constituent.pt(), weightEvt); + registry.fill(HIST("h_gnnfeat_trackPhi"), constituent.phi(), weightEvt); + registry.fill(HIST("h_gnnfeat_trackEta"), constituent.eta(), weightEvt); + registry.fill(HIST("h_gnnfeat_trackCharge"), static_cast(constituent.sign()), weightEvt); + registry.fill(HIST("h_gnnfeat_trackDCAxy"), std::abs(constituent.dcaXY()) * sign, weightEvt); + registry.fill(HIST("h_gnnfeat_trackSigmaDCAxy"), constituent.sigmadcaXY(), weightEvt); + registry.fill(HIST("h_gnnfeat_trackDCAz"), std::abs(constituent.dcaZ()) * sign, weightEvt); + registry.fill(HIST("h_gnnfeat_trackSigmaDCAz"), constituent.sigmadcaZ(), weightEvt); + // registry.fill(HIST("h_gnnfeat_trackITSNCls"), static_cast(origConstit.itsNCls()), weightEvt); + // registry.fill(HIST("h_gnnfeat_trackTPCNCls"), static_cast(origConstit.tpcNClsFound()), weightEvt); + // registry.fill(HIST("h_gnnfeat_trackTPCNCrossedRows"), static_cast(origConstit.tpcNClsCrossedRows()), weightEvt); + // registry.fill(HIST("h_gnnfeat_trackITSChi2NCl"), origConstit.itsChi2NCl(), weightEvt); + // registry.fill(HIST("h_gnnfeat_trackTPCChi2NCl"), origConstit.tpcChi2NCl(), weightEvt); + + registry.fill(HIST("h_gnnfeat_tracksIPxy"), std::abs(constituent.dcaXY()) * sign / constituent.sigmadcaXY(), weightEvt); + registry.fill(HIST("h_gnnfeat_tracksIPz"), std::abs(constituent.dcaZ()) * sign / constituent.sigmadcaZ(), weightEvt); ++nTracks; } + return {nTracks, nNppTracks}; + } + + // `withSub` selects UE(rho*area)-subtracted jet pT + the "_sub"-suffixed histogram set, same convention as + // fillDataJetHistograms(). Histograms whose value doesn't depend on jetpT are shared, unsuffixed. + template + JetHistFillResult fillMCDJetHistograms(AnalysisJet const& analysisJet, AnyTracks const& allTracks, float rho = 0.f, double weightEvt = 1.0) + { + int8_t jetFlavor = analysisJet.origin(); + auto [nTracks, nNppTracks] = analyzeMCDJetTrackInfo(analysisJet, allTracks, weightEvt); + + float jetpT = withSub ? (analysisJet.pt() - rho * analysisJet.area()) : analysisJet.pt(); + double flavorCat = getJetFlavorCat(jetFlavor); - registry.fill(HIST("h_jetpT"), analysisJet.pt(), weightEvt); registry.fill(HIST("h_jetPhi"), analysisJet.phi(), weightEvt); registry.fill(HIST("h_jetEta"), analysisJet.eta(), weightEvt); registry.fill(HIST("h_jetMass"), analysisJet.mass(), weightEvt); registry.fill(HIST("h_Db"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); registry.fill(HIST("h2_nTracks_Db"), nTracks, analysisJet.scoreML(), weightEvt); - + registry.fill(HIST("h2_Db_flavor"), analysisJet.scoreML(), flavorCat, weightEvt); if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("h_jetpT_b"), analysisJet.pt(), weightEvt); - registry.fill(HIST("h_Db_b"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db_b"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); registry.fill(HIST("h2_nTracks_Db_b"), nTracks, analysisJet.scoreML(), weightEvt); } else if (jetFlavor == JetTaggingSpecies::charm) { - registry.fill(HIST("h_jetpT_c"), analysisJet.pt(), weightEvt); - registry.fill(HIST("h_Db_c"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db_c"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); registry.fill(HIST("h2_nTracks_Db_c"), nTracks, analysisJet.scoreML(), weightEvt); } else { - registry.fill(HIST("h_jetpT_lf"), analysisJet.pt(), weightEvt); - registry.fill(HIST("h_Db_lf"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db_lf"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); registry.fill(HIST("h2_nTracks_Db_lf"), nTracks, analysisJet.scoreML(), weightEvt); - if (jetFlavor == JetTaggingSpecies::none) { - registry.fill(HIST("h2_jetpT_Db_lf_none"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); - } else { - registry.fill(HIST("h2_jetpT_Db_lf_matched"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); + } + bool isLf = jetFlavor != JetTaggingSpecies::beauty && jetFlavor != JetTaggingSpecies::charm; + if (isLf) { + registry.fill(HIST("h2_Db_lfmatch"), analysisJet.scoreML(), getLfMatchStatus(jetFlavor), weightEvt); + } + bool isNpp = static_cast(nNppTracks) / nTracks > trackNppCrit; + if (isNpp) { + registry.fill(HIST("h_Db_npp"), analysisJet.scoreML(), weightEvt); + registry.fill(HIST("h2_Db_npp_flavor"), analysisJet.scoreML(), flavorCat, weightEvt); + if (isLf) { + registry.fill(HIST("h2_Db_npp_lfmatch"), analysisJet.scoreML(), getLfMatchStatus(jetFlavor), weightEvt); } } - // Inspection for jets with predominant non-physical primary tracks - if (static_cast(nNppTracks) / nTracks > trackNppCrit) { - registry.fill(HIST("h_Db_npp"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db_npp"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); + if constexpr (withSub) { + registry.fill(HIST("h_jetpT_sub"), jetpT, weightEvt); + registry.fill(HIST("h2_jetpT_Db_sub"), jetpT, analysisJet.scoreML(), weightEvt); + registry.fill(HIST("h2_jetpT_flavor_sub"), jetpT, flavorCat, weightEvt); if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("h_Db_npp_b"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db_npp_b"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); + registry.fill(HIST("h2_jetpT_Db_b_sub"), jetpT, analysisJet.scoreML(), weightEvt); } else if (jetFlavor == JetTaggingSpecies::charm) { - registry.fill(HIST("h_Db_npp_c"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db_npp_c"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); + registry.fill(HIST("h2_jetpT_Db_c_sub"), jetpT, analysisJet.scoreML(), weightEvt); } else { - registry.fill(HIST("h_Db_npp_lf"), analysisJet.scoreML(), weightEvt); - registry.fill(HIST("h2_jetpT_Db_npp_lf"), analysisJet.pt(), analysisJet.scoreML(), weightEvt); + registry.fill(HIST("h2_jetpT_Db_lf_sub"), jetpT, analysisJet.scoreML(), weightEvt); + } + if (isLf) { + registry.fill(HIST("h2_jetpT_lfmatch_sub"), jetpT, getLfMatchStatus(jetFlavor), weightEvt); + if (jetFlavor == JetTaggingSpecies::none) { + registry.fill(HIST("h2_jetpT_Db_lfnone_sub"), jetpT, analysisJet.scoreML(), weightEvt); + } else { + registry.fill(HIST("h2_jetpT_Db_lfmatched_sub"), jetpT, analysisJet.scoreML(), weightEvt); + } + } + if (isNpp) { + registry.fill(HIST("h2_jetpT_Db_npp_sub"), jetpT, analysisJet.scoreML(), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("h2_jetpT_Db_npp_b_sub"), jetpT, analysisJet.scoreML(), weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("h2_jetpT_Db_npp_c_sub"), jetpT, analysisJet.scoreML(), weightEvt); + } else { + registry.fill(HIST("h2_jetpT_Db_npp_lf_sub"), jetpT, analysisJet.scoreML(), weightEvt); + } + } + + if (doDataDriven && !doDataDrivenSV) { + if (doDataDrivenExtra) { + registry.fill(HIST("hSparse_Incljets_sub"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_bjets_sub"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("hSparse_cjets_sub"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets_sub"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + if (jetFlavor == JetTaggingSpecies::none) { + registry.fill(HIST("hSparse_lfjets_none_sub"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets_matched_sub"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } + } + } else { + registry.fill(HIST("hSparse_Incljets_sub"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_bjets_sub"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("hSparse_cjets_sub"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets_sub"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + if (jetFlavor == JetTaggingSpecies::none) { + registry.fill(HIST("hSparse_lfjets_none_sub"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets_matched_sub"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } + } + } + } + } else { + registry.fill(HIST("h_jetpT"), jetpT, weightEvt); + registry.fill(HIST("h2_jetpT_Db"), jetpT, analysisJet.scoreML(), weightEvt); + registry.fill(HIST("h2_jetpT_flavor"), jetpT, flavorCat, weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("h2_jetpT_Db_b"), jetpT, analysisJet.scoreML(), weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("h2_jetpT_Db_c"), jetpT, analysisJet.scoreML(), weightEvt); + } else { + registry.fill(HIST("h2_jetpT_Db_lf"), jetpT, analysisJet.scoreML(), weightEvt); + } + if (isLf) { + registry.fill(HIST("h2_jetpT_lfmatch"), jetpT, getLfMatchStatus(jetFlavor), weightEvt); + if (jetFlavor == JetTaggingSpecies::none) { + registry.fill(HIST("h2_jetpT_Db_lfnone"), jetpT, analysisJet.scoreML(), weightEvt); + } else { + registry.fill(HIST("h2_jetpT_Db_lfmatched"), jetpT, analysisJet.scoreML(), weightEvt); + } + } + if (isNpp) { + registry.fill(HIST("h2_jetpT_Db_npp"), jetpT, analysisJet.scoreML(), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("h2_jetpT_Db_npp_b"), jetpT, analysisJet.scoreML(), weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("h2_jetpT_Db_npp_c"), jetpT, analysisJet.scoreML(), weightEvt); + } else { + registry.fill(HIST("h2_jetpT_Db_npp_lf"), jetpT, analysisJet.scoreML(), weightEvt); + } + } + + if (doDataDriven && !doDataDrivenSV) { + if (doDataDrivenExtra) { + registry.fill(HIST("hSparse_Incljets"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_bjets"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("hSparse_cjets"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + if (jetFlavor == JetTaggingSpecies::none) { + registry.fill(HIST("hSparse_lfjets_none"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets_matched"), jetpT, analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + } + } + } else { + registry.fill(HIST("hSparse_Incljets"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_bjets"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("hSparse_cjets"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + if (jetFlavor == JetTaggingSpecies::none) { + registry.fill(HIST("hSparse_lfjets_none"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } else { + registry.fill(HIST("hSparse_lfjets_matched"), jetpT, analysisJet.scoreML(), nTracks, weightEvt); + } + } + } } } - if (doDataDriven) { - if (doDataDrivenExtra) { - registry.fill(HIST("hSparse_Incljets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + return {jetFlavor, nTracks}; + } + + // Same as fillMCDJetHistograms(), plus SV histograms (see fillSVHistograms()) and the SV-mass variant + // of hSparse_Incljets (only when doDataDrivenSV is on); used by processMCDJetsSV. + template + int8_t fillMCDJetHistogramsSV(AnalysisJet const& analysisJet, AnyTracks const& allTracks, AnySVs const& allSVs, float rho = 0.f, double weightEvt = 1.0) + { + auto [jetFlavor, nTracks] = fillMCDJetHistograms(analysisJet, allTracks, rho, weightEvt); + float jetpT = withSub ? (analysisJet.pt() - rho * analysisJet.area()) : analysisJet.pt(); + float massSV = fillSVHistograms(analysisJet, allSVs, jetpT, weightEvt, jetFlavor); + + if (doDataDriven && doDataDrivenSV) { + if constexpr (withSub) { + registry.fill(HIST("hSparse_Incljets_sub"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("hSparse_bjets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + registry.fill(HIST("hSparse_bjets_sub"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } else if (jetFlavor == JetTaggingSpecies::charm) { - registry.fill(HIST("hSparse_cjets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + registry.fill(HIST("hSparse_cjets_sub"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } else { - registry.fill(HIST("hSparse_lfjets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + registry.fill(HIST("hSparse_lfjets_sub"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); if (jetFlavor == JetTaggingSpecies::none) { - registry.fill(HIST("hSparse_lfjets_none"), analysisJet.pt(), analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + registry.fill(HIST("hSparse_lfjets_none_sub"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } else { - registry.fill(HIST("hSparse_lfjets_matched"), analysisJet.pt(), analysisJet.scoreML(), nTracks, analysisJet.mass(), weightEvt); + registry.fill(HIST("hSparse_lfjets_matched_sub"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } } } else { - registry.fill(HIST("hSparse_Incljets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, weightEvt); + registry.fill(HIST("hSparse_Incljets"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("hSparse_bjets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, weightEvt); + registry.fill(HIST("hSparse_bjets"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } else if (jetFlavor == JetTaggingSpecies::charm) { - registry.fill(HIST("hSparse_cjets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, weightEvt); + registry.fill(HIST("hSparse_cjets"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } else { - registry.fill(HIST("hSparse_lfjets"), analysisJet.pt(), analysisJet.scoreML(), nTracks, weightEvt); + registry.fill(HIST("hSparse_lfjets"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); if (jetFlavor == JetTaggingSpecies::none) { - registry.fill(HIST("hSparse_lfjets_none"), analysisJet.pt(), analysisJet.scoreML(), nTracks, weightEvt); + registry.fill(HIST("hSparse_lfjets_none"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } else { - registry.fill(HIST("hSparse_lfjets_matched"), analysisJet.pt(), analysisJet.scoreML(), nTracks, weightEvt); + registry.fill(HIST("hSparse_lfjets_matched"), jetpT, analysisJet.scoreML(), nTracks, massSV, weightEvt); } } } @@ -679,23 +1171,48 @@ struct BjetTaggingGnn { } // Check if the collision is INEL>0 + static constexpr int nPartInel0 = 3; template bool isTrueINEL0(MCColl const& /*mccoll*/, MCPart const& mcparts) { - for (auto const& mcparticle : mcparts) { - if (!mcparticle.isPhysicalPrimary()) + for (const auto& mcparticle : mcparts) { + if (!mcparticle.isPhysicalPrimary()) { continue; - auto p = pdg->GetParticle(mcparticle.pdgCode()); + } + const auto p = pdg->GetParticle(mcparticle.pdgCode()); if (p != nullptr) { - if (std::abs(p->Charge()) >= 3) { - if (std::abs(mcparticle.eta()) < 1) + if (std::abs(p->Charge()) >= nPartInel0) { + if (std::abs(mcparticle.eta()) < 1) { return true; + } } } } return false; } + // Shared entry checks for MCD collisions, used by processMCDJets(SV) and processMCDTracks: + // event selection, MC-outlier rejection (own and matched mcCollision's), and eval/train split. + template + bool isRejectedMCDCollision(AnalysisCollision const& collision) + { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return true; + } + // Reject outlier MC collisions + if (collision.isOutlier()) { + return true; + } + if (collision.has_mcCollision() && collision.template mcCollision_as().isOutlier()) { + return true; + } + // Uses only collisionId % trainingDatasetRaioParam != 0 for evaluation dataset + if (trainingDatasetRatioParam && collision.collisionId() % trainingDatasetRatioParam == 0) { + return true; + } + return false; + } + template EvtSelFlag fillCollCounter(AnalysisCollision const& collision, float weightEvt = 1.f) { @@ -725,9 +1242,80 @@ struct BjetTaggingGnn { registry.fill(HIST("hCollCounter"), static_cast(EvtSel::Sel8), weightEvt); // Coll+TVX+NoTFB+NoITSROFB if (zvtx) { registry.fill(HIST("hCollCounter"), static_cast(EvtSel::Sel8Zvtx), weightEvt); // Coll+TVX+NoTFB+NoITSROFB+Zvtx - if (collision.template collision_as().isInelGt0()) { - evtselCode |= EvtSelFlag::kINELgt0rec; - registry.fill(HIST("hCollCounter"), static_cast(EvtSel::INELgt0rec), weightEvt); // INELgt0+Zvtx(rec) + // if (collision.template collision_as().isInelGt0()) { + // evtselCode |= EvtSelFlag::kINELgt0rec; + // registry.fill(HIST("hCollCounter"), static_cast(EvtSel::INELgt0rec), weightEvt); // INELgt0+Zvtx(rec) + // } + } + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBitsSel8Full)) { + evtselCode |= EvtSelFlag::kNoPileup; + registry.fill(HIST("hCollCounter"), static_cast(EvtSel::Sel8Full), weightEvt); // Coll+TVX+NoTFB+NoITSROFB+NoPileup + if (zvtx) { + registry.fill(HIST("hCollCounter"), static_cast(EvtSel::Sel8FullZvtx), weightEvt); // Coll+TVX+NoTFB+NoITSROFB+NoPileup+Zvtx + } + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBitsSel8FullGood)) { + evtselCode |= EvtSelFlag::kIsGoodZvtx; + registry.fill(HIST("hCollCounter"), static_cast(EvtSel::Sel8FullGood), weightEvt); // Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx + if (zvtx) { + registry.fill(HIST("hCollCounter"), static_cast(EvtSel::Sel8FullGoodZvtx), weightEvt); // Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx+Zvtx + } + } + } + } + } + } + + return evtselCode; + } + + // Same stage ladder as fillCollCounter(), but evaluated directly on the MC-truth BC's raw evsel bits + // (mcCollision.bc_as(), a mandatory 1:1 index -> always resolves, no "has BC" gate needed) + // instead of a matched reco collision's packed EventSel word. Reuses EvtSelFlag::kColl/EvtSel::Coll(+Zvtx) + // to mean "BC" here (a BC always exists for an McCollision, so that stage is trivially satisfied) purely + // so this cascade lines up bin-for-bin with hCollCounter/hMcCollCounter. + template + EvtSelFlag fillMcCollCounterBC(AnalysisBC const& bc, float posZ, float weightEvt = 1.f) + { + EvtSelFlag evtselCode = EvtSelFlag::Coll; // "Coll" == BC here, see above + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::Coll), weightEvt); // BC + + bool zvtx = std::fabs(posZ) < vertexZCut; + if (zvtx) { + evtselCode |= EvtSelFlag::kZvtx; + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::CollZvtx), weightEvt); // BC+Zvtx + } + + if (bc.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + evtselCode |= EvtSelFlag::kTVX; + registry.fill(HIST("h_vertexZ_truth_bctvx"), posZ, weightEvt); + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::TVX), weightEvt); // BC+TVX + if (zvtx) { + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::TVXZvtx), weightEvt); // BC+TVX, +Zvtx + } + if (bc.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + evtselCode |= EvtSelFlag::kNoTFB; + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::SelMC), weightEvt); // BC+TVX+NoTFB + if (zvtx) { + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::SelMCZvtx), weightEvt); // BC+TVX+NoTFB, +Zvtx + } + if (bc.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + evtselCode |= EvtSelFlag::kNoITSROFB; + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::Sel8), weightEvt); // BC+TVX+NoTFB+NoITSROFB + if (zvtx) { + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::Sel8Zvtx), weightEvt); // BC+TVX+NoTFB+NoITSROFB, +Zvtx + } + if (bc.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + evtselCode |= EvtSelFlag::kNoPileup; + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::Sel8Full), weightEvt); // BC+TVX+NoTFB+NoITSROFB+NoPileup + if (zvtx) { + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::Sel8FullZvtx), weightEvt); // BC+TVX+NoTFB+NoITSROFB+NoPileup, +Zvtx + } + if (bc.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + evtselCode |= EvtSelFlag::kIsGoodZvtx; + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::Sel8FullGood), weightEvt); // BC+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx + if (zvtx) { + registry.fill(HIST("hMcCollCounterBC"), static_cast(EvtSel::Sel8FullGoodZvtx), weightEvt); // BC+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx, +Zvtx + } } } } @@ -752,29 +1340,64 @@ struct BjetTaggingGnn { } PROCESS_SWITCH(BjetTaggingGnn, processDummy, "Dummy process function turned on by default", true); - void processDataJets(FilteredCollisions::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& allTracks, OriginalTracks const& origTracks) + // Shared body of processDataJets(Sub)()/processDataJetsSV(Sub)(); see processMCDJetsCommon() for the + // withSV/withSub convention. + template + void processDataJetsCommon(FilteredCollisions::iterator const& collision, AnalysisJets const& alljets, FilteredTracks const& allTracks, SecondaryVertices const* allSVs = nullptr) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } + // Keeps hCollCounter populated consistently across every process[Data,MCD]Jets(SV)(Sub) variant, not + // just the Sel ones - these should all behave identically except for which jet histograms get filled. + fillCollCounter(collision); registry.fill(HIST("h_event_counter"), 0.0); // Coll+TVX+NoTFB+NoITSROFB+Zvtx registry.fill(HIST("h_vertexZ"), collision.posZ()); + float rho = collision.rho(); + for (const auto& analysisJet : alljets) { if (!isAcceptedJet(analysisJet)) { continue; } - fillDataJetHistograms(analysisJet, allTracks, origTracks); + if constexpr (withSV) { + fillDataJetHistogramsSV(analysisJet, allTracks, *allSVs, rho); + } else { + fillDataJetHistograms(analysisJet, allTracks, rho); + } } } + + void processDataJets(FilteredCollisions::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& allTracks) + { + processDataJetsCommon(collision, alljets, allTracks); + } PROCESS_SWITCH(BjetTaggingGnn, processDataJets, "jet information in Data", false); - void processDataJetsTrig(FilteredCollisionsTriggered::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& allTracks, OriginalTracks const& origTracks, aod::JBCs const& /*bcInfo*/) + void processDataJetsSub(FilteredCollisions::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& allTracks) + { + processDataJetsCommon(collision, alljets, allTracks); + } + PROCESS_SWITCH(BjetTaggingGnn, processDataJetsSub, "jet information in Data, UE(rho*area)-subtracted jet pT", false); + + void processDataJetsSV(FilteredCollisions::iterator const& collision, FilteredDataJetsSV const& alljets, aod::DataSecondaryVertex3Prongs const& allSVs, FilteredTracks const& allTracks) + { + processDataJetsCommon(collision, alljets, allTracks, &allSVs); + } + PROCESS_SWITCH(BjetTaggingGnn, processDataJetsSV, "jet information in Data with secondary vertex info", false); + + void processDataJetsSVSub(FilteredCollisions::iterator const& collision, FilteredDataJetsSV const& alljets, aod::DataSecondaryVertex3Prongs const& allSVs, FilteredTracks const& allTracks) + { + processDataJetsCommon(collision, alljets, allTracks, &allSVs); + } + PROCESS_SWITCH(BjetTaggingGnn, processDataJetsSVSub, "jet information in Data with secondary vertex info, UE(rho*area)-subtracted jet pT", false); + + void processDataJetsTrig(FilteredCollisionsTriggered::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& allTracks, aod::JBCs const& /*bcInfo*/) { // Get BC info associated with the collision before applying any event selections - auto bc = collision.bc_as(); + const auto& bc = collision.bc_as(); // Initialize CCDB objects using the BC info initCCDB(bc); // If SoftwareTriggerSelection (i.e. skimming) is enabled, skip this event unless it passes Zorro selection @@ -789,17 +1412,49 @@ struct BjetTaggingGnn { registry.fill(HIST("h_vertexZ"), collision.posZ()); + float rho = collision.rho(); + for (const auto& analysisJet : alljets) { if (!isAcceptedJet(analysisJet)) { continue; } - fillDataJetHistograms(analysisJet, allTracks, origTracks); + fillDataJetHistograms(analysisJet, allTracks, rho); } } PROCESS_SWITCH(BjetTaggingGnn, processDataJetsTrig, "jet information in software triggered Data", false); - void processDataJetsSel(AnalysisCollisions::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& /*allTracks*/, OrigCollisions const& /*origCollisions*/) + void processDataJetsTrigSub(FilteredCollisionsTriggered::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& allTracks, aod::JBCs const& /*bcInfo*/) + { + // Get BC info associated with the collision before applying any event selections + const auto& bc = collision.bc_as(); + // Initialize CCDB objects using the BC info + initCCDB(bc); + // If SoftwareTriggerSelection (i.e. skimming) is enabled, skip this event unless it passes Zorro selection + if (doSoftwareTriggerSelection && !zorro.isSelected(bc.globalBC())) { + return; + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + registry.fill(HIST("h_event_counter"), 0.0); // Coll+TVX+NoTFB+NoITSROFB+Zvtx + + registry.fill(HIST("h_vertexZ"), collision.posZ()); + + float rho = collision.rho(); + + for (const auto& analysisJet : alljets) { + if (!isAcceptedJet(analysisJet)) { + continue; + } + + fillDataJetHistograms(analysisJet, allTracks, rho); + } + } + PROCESS_SWITCH(BjetTaggingGnn, processDataJetsTrigSub, "jet information in software triggered Data, UE(rho*area)-subtracted jet pT", false); + + void processDataJetsSel(AnalysisCollisions::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& /*allTracks*/) { EvtSelFlag evtselCode = fillCollCounter(collision); @@ -808,19 +1463,28 @@ struct BjetTaggingGnn { continue; } - registry.fill(HIST("h_jetpT_coll"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Coll)); - registry.fill(HIST("h_jetpT_coll_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::CollZvtx)); - registry.fill(HIST("h_jetpT_tvx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::TVX)); - registry.fill(HIST("h_jetpT_tvx_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::TVXZvtx)); - registry.fill(HIST("h_jetpT_selmc"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::SelMC)); - registry.fill(HIST("h_jetpT_selmc_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::SelMCZvtx)); - registry.fill(HIST("h_jetpT_sel8"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8)); - registry.fill(HIST("h_jetpT_sel8_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8Zvtx)); - registry.fill(HIST("h_jetpT_inelgt0rec"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::INELgt0rec)); + fillEvtSelStages(HIST("h2_jetpT_evtsel"), analysisJet.pt(), evtselCode, 1.0, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); } } PROCESS_SWITCH(BjetTaggingGnn, processDataJetsSel, "jet information in Data (event selection)", false); + void processDataJetsSelSub(AnalysisCollisions::iterator const& collision, FilteredDataJets const& alljets, FilteredTracks const& /*allTracks*/) + { + EvtSelFlag evtselCode = fillCollCounter(collision); + + float rho = collision.rho(); + + for (const auto& analysisJet : alljets) { + if (!isAcceptedJet(analysisJet)) { + continue; + } + + float jetptSub = analysisJet.pt() - rho * analysisJet.area(); + fillEvtSelStages(HIST("h2_jetpT_evtsel_sub"), jetptSub, evtselCode, 1.0, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + } + } + PROCESS_SWITCH(BjetTaggingGnn, processDataJetsSelSub, "jet information in Data (event selection), UE(rho*area)-subtracted jet pT", false); + void processDataTracks(FilteredCollisions::iterator const& collision, AnalysisTracks const& tracks) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { @@ -837,272 +1501,407 @@ struct BjetTaggingGnn { if (track.pt() >= trackPtMin) { registry.fill(HIST("h_dcaXY"), std::fabs(track.dcaXY())); registry.fill(HIST("h_dcaZ"), std::fabs(track.dcaZ())); + registry.fill(HIST("hSparse_dca_pt"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY())); } } } PROCESS_SWITCH(BjetTaggingGnn, processDataTracks, "track information in Data", false); - void processMCDJets(FilteredCollisionsMCD::iterator const& collision, FilteredMCDJets const& MCDjets, FilteredTracksMCD const& allTracks, OriginalTracks const& origTracks, FilteredMCPJets const& /*MCPjets*/, aod::JetParticles const& /*mcParticles*/, FilteredCollisionsMCP const& /*mcCollisions*/) + Preslice mcpjetsPerMCPCollision = aod::jet::mcCollisionId; + + // Shared body of processMCDJets(Sub)()/processMCDJetsSV(Sub)(): identical except for whether each jet's + // histograms are filled via fillMCDJetHistograms() or fillMCDJetHistogramsSV(), and whether raw or + // UE(rho*area)-subtracted jet pT (`withSub`) + the "_sub"-suffixed names are used throughout, including the + // matched geometric-response and pTHat-sparse histograms below, not just the core Db/flavor spectra. + // `withSV` selects the branch at compile time via `if constexpr` (bool-template-flag + defaulted-pointer + // pattern, matches PWGHF/D2H/Tasks/taskCharmPolarisation.cxx's `WithEp`/`QVecs*`). + template + void processMCDJetsCommon(FilteredCollisionsMCD::iterator const& collision, AnalysisJets const& MCDjets, FilteredTracksMCD const& allTracks, FilteredMCPJets const& MCPjets, SecondaryVertices const* allSVs = nullptr) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { - return; - } - // Uses only collisionId % trainingDatasetRaioParam != 0 for evaluation dataset - if (trainingDatasetRatioParam && collision.collisionId() % trainingDatasetRatioParam == 0) { + if (isRejectedMCDCollision(collision)) { return; } + bool matchedMcColl = collision.has_mcCollision(); + float rho = collision.rho(); float weightEvt = useEventWeight ? collision.weight() : 1.f; - bool matchedMcColl = collision.has_mcCollision() && std::fabs(collision.template mcCollision_as().posZ()) < vertexZCut; + // Keeps hCollCounter populated consistently across every process[Data,MCD]Jets(SV)(Sub) variant, not + // just the Sel ones - these should all behave identically except for which jet histograms get filled. + fillCollCounter(collision, weightEvt); registry.fill(HIST("h_event_counter"), 0.0, weightEvt); registry.fill(HIST("h_vertexZ"), collision.posZ(), weightEvt); + // Only valid once matchedMcColl is confirmed true below; computed eagerly here (guarded by the ternary) + // since it's needed inside the MCDjets loop's matched-mcpjet block, not just after it. + float rhoMc = matchedMcColl ? collision.template mcCollision_as().rho() : 0.f; + + // Store matched particle jet indices to avoid double-counting in mcpjets loop + std::unordered_set matchedMcpJetIndices; + for (const auto& analysisJet : MCDjets) { if (!isAcceptedJet(analysisJet)) { continue; } - float pTHat = 10. / (std::pow(weightEvt, 1.0 / pTHatExponent)); - if (useEventWeight && analysisJet.pt() > pTHatMaxMCD * pTHat) { - continue; + int8_t jetFlavor = 0; + if constexpr (withSV) { + jetFlavor = fillMCDJetHistogramsSV(analysisJet, allTracks, *allSVs, rho, weightEvt); + } else { + jetFlavor = fillMCDJetHistograms(analysisJet, allTracks, rho, weightEvt).jetFlavor; } - int8_t jetFlavor = fillMCDJetHistograms(analysisJet, allTracks, origTracks, weightEvt); - if (!matchedMcColl) { continue; } + float jetpT = withSub ? (analysisJet.pt() - rho * analysisJet.area()) : analysisJet.pt(); + bool matchedJet = false; + // Everything below is jetpT/mcpJetpT-dependent, so the whole block is duplicated once per `withSub` + // with the "_sub"-suffixed HIST names. for (const auto& mcpjet : analysisJet.template matchedJetGeo_as()) { - if (mcpjet.pt() > pTHatMaxMCP * pTHat) { + // matchedJetGeo_as is not Filtered. + if (mcpjet.pt() < jetPtMin || mcpjet.pt() >= jetPtMax || mcpjet.eta() >= jetEtaMax - mcpjet.r() / 100.f || mcpjet.eta() <= jetEtaMin + mcpjet.r() / 100.f) { continue; } - - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT"), analysisJet.pt(), mcpjet.pt(), weightEvt); - registry.fill(HIST("h_jetpT_matched"), analysisJet.pt(), weightEvt); - registry.fill(HIST("h_jetpT_particle_matched"), mcpjet.pt(), weightEvt); - if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_b"), analysisJet.pt(), mcpjet.pt(), weightEvt); - registry.fill(HIST("h_jetpT_b_matched"), analysisJet.pt(), weightEvt); - registry.fill(HIST("h_jetpT_particle_b_matched"), mcpjet.pt(), weightEvt); - } else if (jetFlavor == JetTaggingSpecies::charm) { - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_c"), analysisJet.pt(), mcpjet.pt(), weightEvt); + matchedJet = true; + matchedMcpJetIndices.insert(mcpjet.globalIndex()); + float mcpJetpT = withSub ? (mcpjet.pt() - rhoMc * mcpjet.area()) : mcpjet.pt(); + if constexpr (withSub) { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_sub"), jetpT, mcpJetpT, weightEvt); + registry.fill(HIST("h_jetpT_matched_sub"), jetpT, weightEvt); + registry.fill(HIST("h_jetpT_particle_matched_sub"), mcpJetpT, weightEvt); + registry.fill(HIST("hSparse_pthat_jetpT_sub"), collision.template mcCollision_as().ptHard(), jetpT, mcpJetpT, weightEvt); // Matched jets + registry.fill(HIST("h2_jetpT_matched_flavor_sub"), jetpT, getJetFlavorCat(jetFlavor), weightEvt); + registry.fill(HIST("h2_jetpT_particle_matched_flavor_sub"), mcpJetpT, getJetFlavorCat(jetFlavor), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_b_sub"), jetpT, mcpJetpT, weightEvt); + registry.fill(HIST("hSparse_pthat_jetpT_b_sub"), collision.template mcCollision_as().ptHard(), jetpT, mcpJetpT, weightEvt); // Matched b-jets + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_c_sub"), jetpT, mcpJetpT, weightEvt); + registry.fill(HIST("hSparse_pthat_jetpT_c_sub"), collision.template mcCollision_as().ptHard(), jetpT, mcpJetpT, weightEvt); // Matched c-jets + } else { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_lf_sub"), jetpT, mcpJetpT, weightEvt); + } + } else { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT"), jetpT, mcpJetpT, weightEvt); + registry.fill(HIST("h_jetpT_matched"), jetpT, weightEvt); + registry.fill(HIST("h_jetpT_particle_matched"), mcpJetpT, weightEvt); + registry.fill(HIST("hSparse_pthat_jetpT"), collision.template mcCollision_as().ptHard(), jetpT, mcpJetpT, weightEvt); // Matched jets + registry.fill(HIST("h2_jetpT_matched_flavor"), jetpT, getJetFlavorCat(jetFlavor), weightEvt); + registry.fill(HIST("h2_jetpT_particle_matched_flavor"), mcpJetpT, getJetFlavorCat(jetFlavor), weightEvt); + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_b"), jetpT, mcpJetpT, weightEvt); + registry.fill(HIST("hSparse_pthat_jetpT_b"), collision.template mcCollision_as().ptHard(), jetpT, mcpJetpT, weightEvt); // Matched b-jets + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_c"), jetpT, mcpJetpT, weightEvt); + registry.fill(HIST("hSparse_pthat_jetpT_c"), collision.template mcCollision_as().ptHard(), jetpT, mcpJetpT, weightEvt); // Matched c-jets + } else { + registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_lf"), jetpT, mcpJetpT, weightEvt); + } + } + } + if (!matchedJet) { + if constexpr (withSub) { + registry.fill(HIST("hSparse_pthat_jetpT_sub"), collision.template mcCollision_as().ptHard(), jetpT, -1.f, weightEvt); // Fake jets, overflow-pTpart jets + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_pthat_jetpT_b_sub"), collision.template mcCollision_as().ptHard(), jetpT, -1.f, weightEvt); // Overflow-pTpart b-jets + } } else { - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_lf"), analysisJet.pt(), mcpjet.pt(), weightEvt); + registry.fill(HIST("hSparse_pthat_jetpT"), collision.template mcCollision_as().ptHard(), jetpT, -1.f, weightEvt); // Fake jets, overflow-pTpart jets + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_pthat_jetpT_b"), collision.template mcCollision_as().ptHard(), jetpT, -1.f, weightEvt); // Overflow-pTpart b-jets + } + } + } + } + + if (!matchedMcColl) { + return; + } + + // Fill histograms for jets matched to the analysis event selection + const auto& mcpjetspermcpcollision = MCPjets.sliceBy(mcpjetsPerMCPCollision, collision.mcCollisionId()); + for (const auto& mcpjet : mcpjetspermcpcollision) { + float mcpJetpT = withSub ? (mcpjet.pt() - rhoMc * mcpjet.area()) : mcpjet.pt(); + int8_t jetFlavor = mcpjet.origin(); + if constexpr (withSub) { + registry.fill(HIST("h_jetpT_particle_sub"), mcpJetpT, weightEvt); + // Fill hSparse_pthat_jetpT only for unmatched particle jets (reco pT = -1) + if (!matchedMcpJetIndices.contains(mcpjet.globalIndex())) { + registry.fill(HIST("hSparse_pthat_jetpT_sub"), collision.template mcCollision_as().ptHard(), -1.f, mcpJetpT, weightEvt); // Missing jets, overflow-pTreco jets + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_pthat_jetpT_b_sub"), collision.template mcCollision_as().ptHard(), -1.f, mcpJetpT, weightEvt); // Missing b-jets, overflow-pTpart b-jets + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("hSparse_pthat_jetpT_c_sub"), collision.template mcCollision_as().ptHard(), -1.f, mcpJetpT, weightEvt); // Missing c-jets, overflow-pTpart c-jets + } + } + registry.fill(HIST("h2_jetpT_particle_flavor_sub"), mcpJetpT, getJetFlavorCat(jetFlavor), weightEvt); + } else { + registry.fill(HIST("h_jetpT_particle"), mcpJetpT, weightEvt); + // Fill hSparse_pthat_jetpT only for unmatched particle jets (reco pT = -1) + if (!matchedMcpJetIndices.contains(mcpjet.globalIndex())) { + registry.fill(HIST("hSparse_pthat_jetpT"), collision.template mcCollision_as().ptHard(), -1.f, mcpJetpT, weightEvt); // Missing jets, overflow-pTreco jets + if (jetFlavor == JetTaggingSpecies::beauty) { + registry.fill(HIST("hSparse_pthat_jetpT_b"), collision.template mcCollision_as().ptHard(), -1.f, mcpJetpT, weightEvt); // Missing b-jets, overflow-pTpart b-jets + } else if (jetFlavor == JetTaggingSpecies::charm) { + registry.fill(HIST("hSparse_pthat_jetpT_c"), collision.template mcCollision_as().ptHard(), -1.f, mcpJetpT, weightEvt); // Missing c-jets, overflow-pTpart c-jets + } } + registry.fill(HIST("h2_jetpT_particle_flavor"), mcpJetpT, getJetFlavorCat(jetFlavor), weightEvt); } } } + + void processMCDJets(FilteredCollisionsMCD::iterator const& collision, FilteredMCDJets const& MCDjets, FilteredTracksMCD const& allTracks, FilteredMCPJets const& MCPjets, aod::JetParticles const& /*mcParticles*/, AnalysisCollisionsMCP const& /*mcCollisions*/) + { + processMCDJetsCommon(collision, MCDjets, allTracks, MCPjets); + } PROCESS_SWITCH(BjetTaggingGnn, processMCDJets, "jet information in MC", false); - void processMCDJetsSel(AnalysisCollisionsMCD::iterator const& collision, FilteredMCDJets const& MCDjets, FilteredTracksMCD const& /*allTracks*/, FilteredMCPJets const& /*MCPjets*/, OrigCollisions const& /*origCollisions*/, FilteredCollisionsMCP const& /*mcCollisions*/, aod::JetParticles const& mcParticles) + void processMCDJetsSub(FilteredCollisionsMCD::iterator const& collision, FilteredMCDJets const& MCDjets, FilteredTracksMCD const& allTracks, FilteredMCPJets const& MCPjets, aod::JetParticles const& /*mcParticles*/, AnalysisCollisionsMCP const& /*mcCollisions*/) { + processMCDJetsCommon(collision, MCDjets, allTracks, MCPjets); + } + PROCESS_SWITCH(BjetTaggingGnn, processMCDJetsSub, "jet information in MC, UE(rho*area)-subtracted jet pT", false); + + void processMCDJetsSV(FilteredCollisionsMCD::iterator const& collision, FilteredMCDJetsSV const& MCDjets, aod::MCDSecondaryVertex3Prongs const& allSVs, FilteredTracksMCD const& allTracks, FilteredMCPJets const& MCPjets, aod::JetParticles const& /*mcParticles*/, AnalysisCollisionsMCP const& /*mcCollisions*/) + { + processMCDJetsCommon(collision, MCDjets, allTracks, MCPjets, &allSVs); + } + PROCESS_SWITCH(BjetTaggingGnn, processMCDJetsSV, "jet information in MC with secondary vertex info", false); + + void processMCDJetsSVSub(FilteredCollisionsMCD::iterator const& collision, FilteredMCDJetsSV const& MCDjets, aod::MCDSecondaryVertex3Prongs const& allSVs, FilteredTracksMCD const& allTracks, FilteredMCPJets const& MCPjets, aod::JetParticles const& /*mcParticles*/, AnalysisCollisionsMCP const& /*mcCollisions*/) + { + processMCDJetsCommon(collision, MCDjets, allTracks, MCPjets, &allSVs); + } + PROCESS_SWITCH(BjetTaggingGnn, processMCDJetsSVSub, "jet information in MC with secondary vertex info, UE(rho*area)-subtracted jet pT", false); + + void processMCDJetsSel(AnalysisCollisionsMCD::iterator const& collision, FilteredMCDJets const& MCDjets, FilteredTracksMCD const& /*allTracks*/, FilteredMCPJets const& /*MCPjets*/, AnalysisCollisionsMCP const& /*mcCollisions*/) + { + // Reject outlier MC collisions + if (collision.isOutlier()) { + return; + } + if (collision.has_mcCollision() && collision.template mcCollision_as().isOutlier()) { + return; + } + float weightEvt = useEventWeight ? collision.weight() : 1.f; EvtSelFlag evtselCode = fillCollCounter(collision, weightEvt); - bool isTrueINELgt0 = collision.has_mcCollision() && isTrueINEL0(collision.template mcCollision_as(), mcParticles); for (const auto& analysisJet : MCDjets) { if (!isAcceptedJet(analysisJet)) { continue; } - float pTHat = 10. / (std::pow(weightEvt, 1.0 / pTHatExponent)); - if (useEventWeight && analysisJet.pt() > pTHatMaxMCD * pTHat) { + int8_t jetFlavor = analysisJet.origin(); + + fillEvtSelStages(HIST("h2_jetpT_evtsel"), analysisJet.pt(), evtselCode, weightEvt, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + if (jetFlavor == JetTaggingSpecies::beauty) { + fillEvtSelStages(HIST("h2_jetpT_evtsel_b"), analysisJet.pt(), evtselCode, weightEvt, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + } else if (jetFlavor == JetTaggingSpecies::charm) { + fillEvtSelStages(HIST("h2_jetpT_evtsel_c"), analysisJet.pt(), evtselCode, weightEvt, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + } + } + } + PROCESS_SWITCH(BjetTaggingGnn, processMCDJetsSel, "jet information in MC (event selection)", false); + + void processMCDJetsSelSub(AnalysisCollisionsMCD::iterator const& collision, FilteredMCDJets const& MCDjets, FilteredTracksMCD const& /*allTracks*/, FilteredMCPJets const& /*MCPjets*/, AnalysisCollisionsMCP const& /*mcCollisions*/) + { + // Reject outlier MC collisions + if (collision.isOutlier()) { + return; + } + if (collision.has_mcCollision() && collision.template mcCollision_as().isOutlier()) { + return; + } + + float weightEvt = useEventWeight ? collision.weight() : 1.f; + EvtSelFlag evtselCode = fillCollCounter(collision, weightEvt); + + float rho = collision.rho(); + + for (const auto& analysisJet : MCDjets) { + if (!isAcceptedJet(analysisJet)) { continue; } int8_t jetFlavor = analysisJet.origin(); + float jetptSub = analysisJet.pt() - rho * analysisJet.area(); - // Get matched particle-level jet pT - double mcpjetpT = -1.0; - for (const auto& mcpjet : analysisJet.template matchedJetGeo_as()) { - if (useEventWeight && mcpjet.pt() > pTHatMaxMCP * pTHat) { - continue; - } - mcpjetpT = mcpjet.pt(); - } - bool isMatched = mcpjetpT > 0.0; - - registry.fill(HIST("h_jetpT_coll"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Coll) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_coll_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::CollZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_tvx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::TVX) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_tvx_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::TVXZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_selmc"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::SelMC) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_selmc_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::SelMCZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_sel8"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_sel8_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8Zvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_inelgt0rec"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::INELgt0rec) ? weightEvt : 0.0); - if (isMatched) { - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_selmc"), analysisJet.pt(), mcpjetpT, hasAll(evtselCode, EvtSelFlag::SelMCZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_sel8"), analysisJet.pt(), mcpjetpT, hasAll(evtselCode, EvtSelFlag::Sel8Zvtx) ? weightEvt : 0.0); - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_inelgt0"), analysisJet.pt(), mcpjetpT, isTrueINELgt0 && (hasAll(evtselCode, EvtSelFlag::INELgt0rec)) ? weightEvt : 0.0); - } + fillEvtSelStages(HIST("h2_jetpT_evtsel_sub"), jetptSub, evtselCode, weightEvt, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("h_jetpT_b_coll"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Coll) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_coll_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::CollZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_tvx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::TVX) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_tvx_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::TVXZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_selmc"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::SelMC) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_selmc_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::SelMCZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_sel8"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_sel8_zvtx"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8Zvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_b_inelgt0rec"), analysisJet.pt(), hasAll(evtselCode, EvtSelFlag::INELgt0rec) ? weightEvt : 0.0); - if (isMatched) { - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_b_selmc"), analysisJet.pt(), mcpjetpT, hasAll(evtselCode, EvtSelFlag::SelMCZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_b_sel8"), analysisJet.pt(), mcpjetpT, hasAll(evtselCode, EvtSelFlag::Sel8Zvtx) ? weightEvt : 0.0); - registry.fill(HIST("h2_Response_DetjetpT_PartjetpT_b_inelgt0"), analysisJet.pt(), mcpjetpT, isTrueINELgt0 && (hasAll(evtselCode, EvtSelFlag::INELgt0rec)) ? weightEvt : 0.0); - } + fillEvtSelStages(HIST("h2_jetpT_evtsel_b_sub"), jetptSub, evtselCode, weightEvt, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); + } else if (jetFlavor == JetTaggingSpecies::charm) { + fillEvtSelStages(HIST("h2_jetpT_evtsel_c_sub"), jetptSub, evtselCode, weightEvt, kEvtSelStageRecoFirst, kEvtSelStageRecoLast); } } } - PROCESS_SWITCH(BjetTaggingGnn, processMCDJetsSel, "jet information in MC (event selection)", false); + PROCESS_SWITCH(BjetTaggingGnn, processMCDJetsSelSub, "jet information in MC (event selection), UE(rho*area)-subtracted jet pT", false); PresliceUnsorted collisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; - Preslice mcpjetsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; - void processMCPJets(aod::McCollisions::iterator const& mcCollision, FilteredMCPJets const& mcpjets, AnalysisCollisionsMCD const& collisions, OrigCollisions const& /*origCollisions*/, aod::JetParticles const& mcParticles) + // `withSub` selects UE(rho*area)-subtracted mcp-jet pT + the "_sub"-suffixed evtsel histogram names below + // instead of raw pT + the unsuffixed names. hMcCollCounter/h_vertexZ_truth*/h_event_counter_mcp aren't + // jet-pT-dependent and always use the plain name. + template + void processMCPJetsCommon(AnalysisCollisionsMCP const& mcCollisions, FilteredMCPJets const& mcpjets, AnalysisCollisionsMCD const& collisions, aod::JBCs const&) { - float weightEvt = useEventWeight ? mcCollision.weight() : 1.f; - auto matchedCollisions = collisions.sliceBy(collisionsPerMCPCollision, mcCollision.globalIndex()); - - EvtSelFlag evtselCode = EvtSelFlag::INEL; - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INEL), weightEvt); // INEL - registry.fill(HIST("h_vertexZ_truth"), mcCollision.posZ(), weightEvt); + // Subscribing AnalysisCollisionsMCP::iterator causes an issue related to unsorted JMcCollisionLbs index. + for (const auto& mcCollision : mcCollisions) { + if (mcCollision.isOutlier()) { + continue; + } + const auto& matchedCollisions = collisions.sliceBy(collisionsPerMCPCollision, mcCollision.mcCollisionId()); + if (matchedCollisions.size() >= 1) { + if (matchedCollisions.begin().isOutlier()) { + continue; + } + } + // mcCollision -> BC is a mandatory 1:1 index (jmccollision::JBCId), unlike the reco-collision match + // above (which can be 0, 1, or split into several matchedCollisions) - no slicing/size check needed. + const auto& matchedBC = mcCollision.bc_as(); - bool zvtx = std::fabs(mcCollision.posZ()) < vertexZCut; - bool zvtxMatched = false; + float rho = mcCollision.rho(); + float weightEvt = useEventWeight ? mcCollision.weight() : 1.f; - bool isTrueINELgt0 = isTrueINEL0(mcCollision, mcParticles); - if (zvtx) { - evtselCode |= EvtSelFlag::kZvtx; - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INELZvtx), weightEvt); // INEL+Zvtx - if (isTrueINELgt0) { - evtselCode |= EvtSelFlag::kINELgt0; - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INELgt0), weightEvt); // INELgt0 - } - } + EvtSelFlag evtselCode = EvtSelFlag::INEL; + EvtSelFlag evtselBCCode = EvtSelFlag::INEL; + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INEL), weightEvt); // INEL + registry.fill(HIST("h_vertexZ_truth"), mcCollision.posZ(), weightEvt); - bool isMatchedToAnalysisSelection = false; + bool zvtx = std::fabs(mcCollision.posZ()) < vertexZCut; + bool zvtxMatched = false; - if (matchedCollisions.size() >= 1) { - zvtxMatched = std::fabs(matchedCollisions.begin().posZ()) < vertexZCut; - evtselCode |= EvtSelFlag::kColl; - registry.fill(HIST("h_vertexZ_truth_coll"), mcCollision.posZ(), weightEvt); - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Coll), weightEvt); // McColl(-> Coll) - if (zvtxMatched) { - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::CollZvtx), weightEvt); // McColl(-> Coll+Zvtx) + // bool isTrueINELgt0 = isTrueINEL0(mcCollision, mcParticles); + if (zvtx) { + evtselCode |= EvtSelFlag::kZvtx; + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INELZvtx), weightEvt); // INEL+Zvtx + // if (isTrueINELgt0) { + // evtselCode |= EvtSelFlag::kINELgt0; + // registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INELgt0), weightEvt); // INELgt0 + // } } - if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsTVX)) { - evtselCode |= EvtSelFlag::kTVX; - registry.fill(HIST("h_vertexZ_truth_tvx"), mcCollision.posZ(), weightEvt); - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::TVX), weightEvt); // McColl(-> Coll+TVX) + + // Coll + if (matchedCollisions.size() >= 1) { + zvtxMatched = std::fabs(matchedCollisions.begin().posZ()) < vertexZCut; + evtselCode |= EvtSelFlag::kColl; + registry.fill(HIST("h_vertexZ_truth_coll"), mcCollision.posZ(), weightEvt); + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Coll), weightEvt); // McColl(-> Coll) if (zvtxMatched) { - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::TVXZvtx), weightEvt); // McColl(-> Coll+TVX+Zvtx) + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::CollZvtx), weightEvt); // McColl(-> Coll+Zvtx) } - if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsSelMC)) { - evtselCode |= EvtSelFlag::kNoTFB; - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::SelMC), weightEvt); // McColl(-> Coll+TVX+NoTFB) + if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsTVX)) { + evtselCode |= EvtSelFlag::kTVX; + registry.fill(HIST("h_vertexZ_truth_tvx"), mcCollision.posZ(), weightEvt); + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::TVX), weightEvt); // McColl(-> Coll+TVX) if (zvtxMatched) { - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::SelMCZvtx), weightEvt); // McColl(-> Coll+TVX+NoTFB+Zvtx) + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::TVXZvtx), weightEvt); // McColl(-> Coll+TVX+Zvtx) } - if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsSel8)) { - evtselCode |= EvtSelFlag::kNoITSROFB; - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB) + if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsSelMC)) { + evtselCode |= EvtSelFlag::kNoTFB; + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::SelMC), weightEvt); // McColl(-> Coll+TVX+NoTFB) if (zvtxMatched) { - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8Zvtx), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB+Zvtx) - if (matchedCollisions.begin().template collision_as().isInelGt0()) { - evtselCode |= EvtSelFlag::kINELgt0rec; - registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INELgt0rec), weightEvt); // INELgt0+Zvtx(rec) + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::SelMCZvtx), weightEvt); // McColl(-> Coll+TVX+NoTFB+Zvtx) + } + if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsSel8)) { + evtselCode |= EvtSelFlag::kNoITSROFB; + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB) + if (zvtxMatched) { + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8Zvtx), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB+Zvtx) + // if (matchedCollisions.begin().template collision_as().isInelGt0()) { + // evtselCode |= EvtSelFlag::kINELgt0rec; + // registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::INELgt0rec), weightEvt); // INELgt0+Zvtx(rec) + // } + } + if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsSel8Full)) { + evtselCode |= EvtSelFlag::kNoPileup; + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8Full), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup) + if (zvtxMatched) { + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8FullZvtx), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup+Zvtx) + } + if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBitsSel8FullGood)) { + evtselCode |= EvtSelFlag::kIsGoodZvtx; + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8FullGood), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx) + if (zvtxMatched) { + registry.fill(HIST("hMcCollCounter"), static_cast(EvtSel::Sel8FullGoodZvtx), weightEvt); // McColl(-> Coll+TVX+NoTFB+NoITSROFB+NoPileup+GoodZvtx+Zvtx) + } + } } } } } - } - if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBits) && zvtxMatched) { - isMatchedToAnalysisSelection = true; - registry.fill(HIST("h_event_counter_mcp"), 0.0, weightEvt); - } - } - - auto mcpjetspermcpcollision = mcpjets.sliceBy(mcpjetsPerMCPCollision, mcCollision.globalIndex()); - for (const auto& mcpjet : mcpjetspermcpcollision) { - bool jetIncluded = false; - for (const auto& jetR : jetRadiiValues) { - if (mcpjet.r() == static_cast(jetR * 100)) { - jetIncluded = true; - break; + if (jetderiveddatautilities::selectCollision(matchedCollisions.begin(), eventSelectionBits) && zvtxMatched) { + registry.fill(HIST("h_event_counter_mcp"), 0.0, weightEvt); } } - if (!jetIncluded) { - continue; - } + // BC (mcCollision's own truth-level BC, always resolved - see fillMcCollCounterBC()). + evtselBCCode |= fillMcCollCounterBC(matchedBC, mcCollision.posZ(), weightEvt); - float pTHat = 10. / (std::pow(weightEvt, 1.0 / pTHatExponent)); - if (useEventWeight && mcpjet.pt() > pTHatMaxMCP * pTHat) { - continue; - } + const auto& mcpjetspermcpcollision = mcpjets.sliceBy(mcpjetsPerMCPCollision, mcCollision.mcCollisionId()); + for (const auto& mcpjet : mcpjetspermcpcollision) { - int8_t jetFlavor = mcpjet.origin(); + int8_t jetFlavor = mcpjet.origin(); + float mcpJetpT = withSub ? (mcpjet.pt() - rho * mcpjet.area()) : mcpjet.pt(); - registry.fill(HIST("h_jetpT_particle_inel"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INEL) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_inel_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INELZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_coll"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::Coll) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_coll_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::CollZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_tvx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::TVX) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_tvx_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::TVXZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_selmc"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::SelMC) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_selmc_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::SelMCZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_sel8"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_sel8_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8Zvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_inelgt0"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INELgt0) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_inelgt0rec"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INELgt0rec) ? weightEvt : 0.0); - if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("h_jetpT_particle_b_inel"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INEL) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_inel_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INELZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_coll"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::Coll) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_coll_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::CollZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_tvx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::TVX) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_tvx_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::TVXZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_selmc"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::SelMC) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_selmc_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::SelMCZvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_sel8"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_sel8_zvtx"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::Sel8Zvtx) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_inelgt0"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INELgt0) ? weightEvt : 0.0); - registry.fill(HIST("h_jetpT_particle_b_inelgt0rec"), mcpjet.pt(), hasAll(evtselCode, EvtSelFlag::INELgt0rec) ? weightEvt : 0.0); - } - - // Fill histograms for jets matched to the analysis event selection - if (isMatchedToAnalysisSelection) { - registry.fill(HIST("h_jetpT_particle"), mcpjet.pt(), weightEvt); + if constexpr (withSub) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtsel_sub"), mcpJetpT, evtselCode, weightEvt, 0, kEvtSelStages.size() - 1); + if (jetFlavor == JetTaggingSpecies::beauty) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtsel_b_sub"), mcpJetpT, evtselCode, weightEvt, 0, kEvtSelStages.size() - 1); + } else if (jetFlavor == JetTaggingSpecies::charm) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtsel_c_sub"), mcpJetpT, evtselCode, weightEvt, 0, kEvtSelStages.size() - 1); + } - if (jetFlavor == JetTaggingSpecies::beauty) { - registry.fill(HIST("h_jetpT_particle_b"), mcpjet.pt(), weightEvt); - } else if (jetFlavor == JetTaggingSpecies::charm) { - registry.fill(HIST("h_jetpT_particle_c"), mcpjet.pt(), weightEvt); + fillEvtSelStages(HIST("h2_jetpT_particle_evtselbc_sub"), mcpJetpT, evtselBCCode, weightEvt, 0, kEvtSelStages.size() - 1); + if (jetFlavor == JetTaggingSpecies::beauty) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtselbc_b_sub"), mcpJetpT, evtselBCCode, weightEvt, 0, kEvtSelStages.size() - 1); + } else if (jetFlavor == JetTaggingSpecies::charm) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtselbc_c_sub"), mcpJetpT, evtselBCCode, weightEvt, 0, kEvtSelStages.size() - 1); + } } else { - registry.fill(HIST("h_jetpT_particle_lf"), mcpjet.pt(), weightEvt); + fillEvtSelStages(HIST("h2_jetpT_particle_evtsel"), mcpJetpT, evtselCode, weightEvt, 0, kEvtSelStages.size() - 1); + if (jetFlavor == JetTaggingSpecies::beauty) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtsel_b"), mcpJetpT, evtselCode, weightEvt, 0, kEvtSelStages.size() - 1); + } else if (jetFlavor == JetTaggingSpecies::charm) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtsel_c"), mcpJetpT, evtselCode, weightEvt, 0, kEvtSelStages.size() - 1); + } + + fillEvtSelStages(HIST("h2_jetpT_particle_evtselbc"), mcpJetpT, evtselBCCode, weightEvt, 0, kEvtSelStages.size() - 1); + if (jetFlavor == JetTaggingSpecies::beauty) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtselbc_b"), mcpJetpT, evtselBCCode, weightEvt, 0, kEvtSelStages.size() - 1); + } else if (jetFlavor == JetTaggingSpecies::charm) { + fillEvtSelStages(HIST("h2_jetpT_particle_evtselbc_c"), mcpJetpT, evtselBCCode, weightEvt, 0, kEvtSelStages.size() - 1); + } } } } } + + void processMCPJets(AnalysisCollisionsMCP const& mcCollisions, FilteredMCPJets const& mcpjets, AnalysisCollisionsMCD const& collisions, aod::JBCs const& bcs) + { + processMCPJetsCommon(mcCollisions, mcpjets, collisions, bcs); + } PROCESS_SWITCH(BjetTaggingGnn, processMCPJets, "mc collision information", false); + void processMCPJetsSub(AnalysisCollisionsMCP const& mcCollisions, FilteredMCPJets const& mcpjets, AnalysisCollisionsMCD const& collisions, aod::JBCs const& bcs) + { + processMCPJetsCommon(mcCollisions, mcpjets, collisions, bcs); + } + PROCESS_SWITCH(BjetTaggingGnn, processMCPJetsSub, "mc collision information, UE(rho*area)-subtracted jet pT", false); + Preslice mcparticlesPerMCPCollision = aod::jmcparticle::mcCollisionId; - void processMCDTracks(FilteredCollisionsMCD::iterator const& collision, AnalysisTracksMCD const& tracks, FilteredCollisionsMCP const& /*mcCollisions*/, aod::JetParticles const& allParticles) + void processMCDTracks(FilteredCollisionsMCD::iterator const& collision, AnalysisTracksMCD const& tracks, AnalysisCollisionsMCP const& /*mcCollisions*/, aod::JetParticles const& allParticles) { - float weightEvt = useEventWeight ? collision.weight() : 1.f; - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { - return; - } - // Uses only collisionId % trainingDatasetRaioParam != 0 for evaluation dataset - if (trainingDatasetRatioParam && collision.collisionId() % trainingDatasetRatioParam == 0) { + if (isRejectedMCDCollision(collision)) { return; } + bool matchedMcColl = collision.has_mcCollision(); - bool matchedMcColl = collision.has_mcCollision() && std::fabs(collision.template mcCollision_as().posZ()) < vertexZCut; + float weightEvt = useEventWeight ? collision.weight() : 1.f; for (const auto& track : tracks) { if (!jetderiveddatautilities::selectTrack(track, trackSelectionBits) || track.eta() <= trackEtaMin || track.eta() >= trackEtaMax) { @@ -1114,12 +1913,14 @@ struct BjetTaggingGnn { if (track.pt() >= trackPtMin) { registry.fill(HIST("h_dcaXY"), std::fabs(track.dcaXY()), weightEvt); registry.fill(HIST("h_dcaZ"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); } if (!matchedMcColl) { if (track.pt() >= trackPtMin) { registry.fill(HIST("h_dcaXY_coll_fake"), std::fabs(track.dcaXY()), weightEvt); registry.fill(HIST("h_dcaZ_coll_fake"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_coll_fake"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); } continue; } @@ -1127,10 +1928,11 @@ struct BjetTaggingGnn { if (track.pt() >= trackPtMin) { registry.fill(HIST("h_dcaXY_fake"), std::fabs(track.dcaXY()), weightEvt); registry.fill(HIST("h_dcaZ_fake"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_fake"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); } continue; } - auto particle = track.template mcParticle_as(); + const auto& particle = track.template mcParticle_as(); if (particle.eta() > trackEtaMin && particle.eta() < trackEtaMax) { if (particle.isPhysicalPrimary()) { registry.fill(HIST("h2_trackpT_partpT"), track.pt(), particle.pt(), weightEvt); @@ -1142,24 +1944,36 @@ struct BjetTaggingGnn { if (particle.mcCollisionId() == collision.mcCollisionId()) { registry.fill(HIST("h_dcaXY_coll_matched"), std::fabs(track.dcaXY()), weightEvt); // Matched to particle from the same MC collision registry.fill(HIST("h_dcaZ_coll_matched"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_coll_matched"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); int origin = RecoDecay::getParticleOrigin(allParticles, particle, false); if (origin == RecoDecay::OriginType::NonPrompt) { registry.fill(HIST("h_dcaXY_coll_matched_b"), std::fabs(track.dcaXY()), weightEvt); registry.fill(HIST("h_dcaZ_coll_matched_b"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_coll_matched_b"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); } else if (origin == RecoDecay::OriginType::Prompt) { registry.fill(HIST("h_dcaXY_coll_matched_c"), std::fabs(track.dcaXY()), weightEvt); registry.fill(HIST("h_dcaZ_coll_matched_c"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_coll_matched_c"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); } else { registry.fill(HIST("h_dcaXY_coll_matched_lf"), std::fabs(track.dcaXY()), weightEvt); registry.fill(HIST("h_dcaZ_coll_matched_lf"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_coll_matched_lf"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); } } else { registry.fill(HIST("h_dcaXY_coll_mismatched"), std::fabs(track.dcaXY()), weightEvt); // Matched to particle from a different MC collision registry.fill(HIST("h_dcaZ_coll_mismatched"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_coll_mismatched"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); } } else { - registry.fill(HIST("h_dcaXY_npp"), std::fabs(track.dcaXY()), weightEvt); - registry.fill(HIST("h_dcaZ_npp"), std::fabs(track.dcaZ()), weightEvt); + if (particle.mcCollisionId() == collision.mcCollisionId()) { + registry.fill(HIST("h_dcaXY_npp"), std::fabs(track.dcaXY()), weightEvt); + registry.fill(HIST("h_dcaZ_npp"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_npp"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); + } else { + registry.fill(HIST("h_dcaXY_npp_mismatched"), std::fabs(track.dcaXY()), weightEvt); + registry.fill(HIST("h_dcaZ_npp_mismatched"), std::fabs(track.dcaZ()), weightEvt); + registry.fill(HIST("hSparse_dca_pt_npp_mismatched"), track.pt(), std::fabs(track.dcaZ()), std::fabs(track.dcaXY()), weightEvt); + } } } } @@ -1169,10 +1983,10 @@ struct BjetTaggingGnn { return; } - auto const particles = allParticles.sliceBy(mcparticlesPerMCPCollision, collision.mcCollisionId()); + const auto& particles = allParticles.sliceBy(mcparticlesPerMCPCollision, collision.mcCollisionId()); for (const auto& particle : particles) { - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + const auto pdgParticle = pdg->GetParticle(particle.pdgCode()); if (!pdgParticle || pdgParticle->Charge() == 0.0) { continue; } @@ -1202,7 +2016,7 @@ struct BjetTaggingGnn { } } } - auto collisionsInBC = collisions.sliceBy(perFoundBC, bc.globalIndex()); + const auto& collisionsInBC = collisions.sliceBy(perFoundBC, bc.globalIndex()); for (const auto& collision : collisionsInBC) { registry.fill(HIST("hBCCounter"), 4.5); // CollinBC if (collision.sel8()) { diff --git a/PWGJE/Tasks/bjetTaggingML.cxx b/PWGJE/Tasks/bjetTaggingML.cxx index 002f3b4bf02..ff58cac44e9 100644 --- a/PWGJE/Tasks/bjetTaggingML.cxx +++ b/PWGJE/Tasks/bjetTaggingML.cxx @@ -89,9 +89,9 @@ struct BJetTaggingML { // Seed the random number generator using current time std::srand(static_cast(std::time(nullptr))); - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); const AxisSpec axisDb{300, -10., 20., "#it{D}_{b}"}; const AxisSpec axisScore{120, -0.1, 1.1, "Score"}; diff --git a/PWGJE/Tasks/bjetTreeCreator.cxx b/PWGJE/Tasks/bjetTreeCreator.cxx index a5c85560f26..7ab7bc11bc6 100644 --- a/PWGJE/Tasks/bjetTreeCreator.cxx +++ b/PWGJE/Tasks/bjetTreeCreator.cxx @@ -112,8 +112,10 @@ DECLARE_SOA_COLUMN(TrackTPCChi2NCl, tracktpcchi2ncl, float); //! The t DECLARE_SOA_COLUMN(TrackITSNCls, trackitsncls, float); //! The track ITS NCls DECLARE_SOA_COLUMN(TrackTPCNCls, tracktpcncls, float); //! The track TPC NCls (Found) DECLARE_SOA_COLUMN(TrackTPCNCrossedRows, tracktpcncrossedrows, float); //! The track TPC NCrossedRows -DECLARE_SOA_COLUMN(TrackOrigin, trk_origin, int); //! The track origin label for GNN track origin predictions -DECLARE_SOA_COLUMN(TrackVtxIndex, trk_vtx_index, int); //! The track vertex index for GNN vertex predictions +// DECLARE_SOA_COLUMN(TrackTPCNSigmaPi, tracktpcnsigmapi, float); //! The track TPC nSigma Pi +// DECLARE_SOA_COLUMN(TrackTOFNSigmaPi, tracktofnsigmapi, float); //! The track TOF nSigma Pi +DECLARE_SOA_COLUMN(TrackOrigin, trk_origin, int); //! The track origin label for GNN track origin predictions +DECLARE_SOA_COLUMN(TrackVtxIndex, trk_vtx_index, int); //! The track vertex index for GNN vertex predictions // DECLARE_SOA_COLUMN(DCATrackJet, dcatrackjet, float); //! The distance between track and jet, unfortunately it cannot be calculated in O2 } // namespace trackInfo @@ -136,6 +138,15 @@ DECLARE_SOA_TABLE(bjetTracksParams, "AOD", "BJETTRACKSPARAM", using bjetTracksParam = bjetTracksParams::iterator; DECLARE_SOA_TABLE(bjetTracksParamsExtra, "AOD", "BJETTRACKSEXTRA", + // o2::soa::Index<>, + trackInfo::TrackPhi, + trackInfo::TrackCharge, + trackInfo::TrackOrigin, + trackInfo::TrackVtxIndex); + +using bjetTracksParamExtra = bjetTracksParamsExtra::iterator; + +DECLARE_SOA_TABLE(bjetTracksParamsExtrb, "AOD", "BJETTRACKSEXTRB", // o2::soa::Index<>, trackInfo::TrackPhi, trackInfo::TrackCharge, @@ -147,7 +158,19 @@ DECLARE_SOA_TABLE(bjetTracksParamsExtra, "AOD", "BJETTRACKSEXTRA", trackInfo::TrackOrigin, trackInfo::TrackVtxIndex); -using bjetTracksParamExtra = bjetTracksParamsExtra::iterator; +using bjetTracksParamExtrb = bjetTracksParamsExtrb::iterator; + +// // PID information +// DECLARE_SOA_TABLE(bjetTracksParamsExtrc, "AOD", "BJETTRACKSEXTRC", +// // o2::soa::Index<>, +// trackInfo::TrackPhi, +// trackInfo::TrackCharge, +// trackInfo::TrackTPCNSigmaPi, +// trackInfo::TrackTOFNSigmaPi, +// trackInfo::TrackOrigin, +// trackInfo::TrackVtxIndex); + +// using bjetTracksParamExtrc = bjetTracksParamsExtrc::iterator; namespace SVInfo { @@ -204,6 +227,7 @@ struct BJetTreeCreator { Produces bjetParamsExtraTable; Produces bjetTracksParamsTable; Produces bjetTracksExtraTable; + Produces bjetTracksExtrbTable; Produces bjetSVParamsTable; Produces bjetConstituentsTable; @@ -257,6 +281,7 @@ struct BJetTreeCreator { Configurable vtxRes{"vtxRes", 0.01, "Vertex position resolution (cluster size) for GNN vertex predictions (cm)"}; Configurable trainingDatasetRatioParam{"trainingDatasetRatioParam", 0, "Parameter for splitting training/evaluation datasets by collisionId"}; + Configurable produceExtrbTableGnn{"produceExtrbTableGnn", false, "Flag whether to produce the extra table with track quality variables for GNN training"}; std::vector eventSelectionBits; @@ -269,11 +294,11 @@ struct BJetTreeCreator { // Seed the random number generator using current time std::srand(static_cast(std::time(nullptr))); - jetRadiiValues = (std::vector)jetRadii; - jetPtBinsReduction = (std::vector)jetPtBins; - jetReductionFactorsPt = (std::vector)jetReductionFactors; + jetRadiiValues = jetRadii; + jetPtBinsReduction = jetPtBins; + jetReductionFactorsPt = jetReductionFactors; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); registry.add("h_vertexZ", "Vertex Z;#it{Z} (cm)", {HistType::kTH1F, {{40, -20.0, 20.0}}}); @@ -345,16 +370,18 @@ struct BJetTreeCreator { registry.add("h_trk_dcaz", "trk_dcaxyz;#it{DCA}_{z} (cm);Entries", {HistType::kTH1F, {{200, -0.1, 0.1}}}); registry.add("h_trk_sigmadcaxy", "trk_sigmadcaxy;#it{#sigma}_{#it{DCA}_{xy}} (cm);Entries", {HistType::kTH1F, {{200, 0., 0.1}}}); registry.add("h_trk_sigmadcaz", "trk_sigmadcaxyz;#it{#sigma}_{#it{DCA}_{z}} (cm);Entries", {HistType::kTH1F, {{200, 0., 0.1}}}); - registry.add("h_trk_itsncls", "trk_itsncls;ITS NCls;Entries", {HistType::kTH1F, {{10, 0., 10.}}}); - registry.add("h_trk_tpcncls", "trk_tpcncls;TPC NCls (Found);Entries", {HistType::kTH1F, {{200, 0., 200.}}}); - registry.add("h_trk_tpcncrs", "trk_tpcncrs;TPC NCrossedRows;Entries", {HistType::kTH1F, {{200, 0., 200.}}}); - registry.add("h_trk_itschi2ncl", "trk_itschi2ncl;ITS #it{#chi}^{2}/ndf;Entries", {HistType::kTH1F, {{200, 0., 20.}}}); - registry.add("h_trk_tpcchi2ncl", "trk_tpcchi2ncl;TPC #it{#chi}^{2}/ndf;Entries", {HistType::kTH1F, {{200, 0., 10.}}}); - registry.add("h2_trk_jtrackpt_vs_origtrackpt", "JTracks::pt vs Tracks::pt", {HistType::kTH2F, {{200, 0., 100.}, {200, 0., 100.}}}); + if (produceExtrbTableGnn) { + registry.add("h_trk_itsncls", "trk_itsncls;ITS NCls;Entries", {HistType::kTH1F, {{10, 0., 10.}}}); + registry.add("h_trk_tpcncls", "trk_tpcncls;TPC NCls (Found);Entries", {HistType::kTH1F, {{200, 0., 200.}}}); + registry.add("h_trk_tpcncrs", "trk_tpcncrs;TPC NCrossedRows;Entries", {HistType::kTH1F, {{200, 0., 200.}}}); + registry.add("h_trk_itschi2ncl", "trk_itschi2ncl;ITS #it{#chi}^{2}/ndf;Entries", {HistType::kTH1F, {{200, 0., 20.}}}); + registry.add("h_trk_tpcchi2ncl", "trk_tpcchi2ncl;TPC #it{#chi}^{2}/ndf;Entries", {HistType::kTH1F, {{200, 0., 10.}}}); + registry.add("h2_trk_jtrackpt_vs_origtrackpt", "JTracks::pt vs Tracks::pt", {HistType::kTH2F, {{200, 0., 100.}, {200, 0., 100.}}}); + } registry.add("h_trk_vtx_index", "trk_vtx_index;Vertex index;Entries", {HistType::kTH1F, {{20, 0., 20.}}}); registry.add("h_trk_origin", "trk_origin;Track origin;Entries", {HistType::kTH1F, {{5, 0., 5.}}}); auto hTrackOrigin = registry.get(HIST("h_trk_origin")); - hTrackOrigin->GetXaxis()->SetBinLabel(1, "NotPhysPrim"); + hTrackOrigin->GetXaxis()->SetBinLabel(1, "Background"); hTrackOrigin->GetXaxis()->SetBinLabel(2, "Charm"); hTrackOrigin->GetXaxis()->SetBinLabel(3, "Beauty"); hTrackOrigin->GetXaxis()->SetBinLabel(4, "Primary"); @@ -541,7 +568,10 @@ struct BJetTreeCreator { trkIdx++; - if (constituent.pt() < trackPtMin || !jettaggingutilities::trackAcceptanceWithDca(constituent, maxIPxy, maxIPz)) { + // No DCA-acceptance cut here: matches BjetTaggingGnn::fillMCDJetHistograms(), which only applies + // a track pT cut (trackPtMinGnn) for GNN input tracks, so the training tree stays consistent with + // what is evaluated at inference time. + if (constituent.pt() < trackPtMin) { continue; } @@ -577,23 +607,29 @@ struct BJetTreeCreator { //+trk registry.fill(HIST("h_trk_pt"), constituent.pt(), eventweight); registry.fill(HIST("h_trk_eta"), constituent.eta(), eventweight); - registry.fill(HIST("h_trk_phi"), origConstit.phi(), eventweight); + registry.fill(HIST("h_trk_phi"), constituent.phi(), eventweight); registry.fill(HIST("h_trk_charge"), constituent.sign(), eventweight); registry.fill(HIST("h_trk_dcaxy"), std::abs(constituent.dcaXY()) * sign, eventweight); registry.fill(HIST("h_trk_dcaz"), std::abs(constituent.dcaZ()) * sign, eventweight); registry.fill(HIST("h_trk_sigmadcaxy"), constituent.sigmadcaXY(), eventweight); registry.fill(HIST("h_trk_sigmadcaz"), constituent.sigmadcaZ(), eventweight); - registry.fill(HIST("h_trk_itsncls"), origConstit.itsNCls(), eventweight); - registry.fill(HIST("h_trk_tpcncls"), origConstit.tpcNClsFound(), eventweight); - registry.fill(HIST("h_trk_tpcncrs"), origConstit.tpcNClsCrossedRows(), eventweight); - registry.fill(HIST("h_trk_itschi2ncl"), origConstit.itsChi2NCl(), eventweight); - registry.fill(HIST("h_trk_tpcchi2ncl"), origConstit.tpcChi2NCl(), eventweight); - registry.fill(HIST("h2_trk_jtrackpt_vs_origtrackpt"), constituent.pt(), origConstit.pt(), eventweight); // jtrack & new extra table are well joined (linear correlation) + if (produceExtrbTableGnn) { + registry.fill(HIST("h_trk_itsncls"), origConstit.itsNCls(), eventweight); + registry.fill(HIST("h_trk_tpcncls"), origConstit.tpcNClsFound(), eventweight); + registry.fill(HIST("h_trk_tpcncrs"), origConstit.tpcNClsCrossedRows(), eventweight); + registry.fill(HIST("h_trk_itschi2ncl"), origConstit.itsChi2NCl(), eventweight); + registry.fill(HIST("h_trk_tpcchi2ncl"), origConstit.tpcChi2NCl(), eventweight); + registry.fill(HIST("h2_trk_jtrackpt_vs_origtrackpt"), constituent.pt(), origConstit.pt(), eventweight); // jtrack & new extra table are well joined (linear correlation) + } registry.fill(HIST("h_trk_vtx_index"), trkVtxIndex); registry.fill(HIST("h_trk_origin"), trkOrigin); if (produceTree) { - bjetTracksExtraTable(/*bjetParamsTable.lastIndex() + 1, */ origConstit.phi(), constituent.sign(), origConstit.itsChi2NCl(), origConstit.tpcChi2NCl(), origConstit.itsNCls(), origConstit.tpcNClsFound(), origConstit.tpcNClsCrossedRows(), trkOrigin, trkVtxIndex); //+ + if (produceExtrbTableGnn) { + bjetTracksExtrbTable(/*bjetParamsTable.lastIndex() + 1, */ constituent.phi(), constituent.sign(), origConstit.itsChi2NCl(), origConstit.tpcChi2NCl(), origConstit.itsNCls(), origConstit.tpcNClsFound(), origConstit.tpcNClsCrossedRows(), trkOrigin, trkVtxIndex); //+ + } else { + bjetTracksExtraTable(/*bjetParamsTable.lastIndex() + 1, */ constituent.phi(), constituent.sign(), trkOrigin, trkVtxIndex); //+ + } } } @@ -762,17 +798,28 @@ struct BJetTreeCreator { } PROCESS_SWITCH(BJetTreeCreator, processMCJets, "jet information in MC", false); + using FilteredCollisionMCDOutlier = soa::Filtered>; using MCDJetTableNoSV = soa::Filtered>; + using AnalysisCollisionsMCPOutlier = soa::Join; using JetParticleswID = soa::Join; - void processMCJetsForGNN(FilteredCollisionMCD::iterator const& collision, aod::JMcCollisions const&, MCDJetTableNoSV const& MCDjets, MCPJetTable const& MCPjets, JetTracksMCDwID const& allTracks, JetParticleswID const& MCParticles, OriginalTracks const& origTracks, aod::McParticles const& origParticles) + // aod::McParticles::vx(), vy(), vz() are necessary for jettaggingutilities::vertexClustering() + void processMCJetsForGNN(FilteredCollisionMCDOutlier::iterator const& collision, MCDJetTableNoSV const& MCDjets, MCPJetTable const& MCPjets, JetTracksMCDwID const& allTracks, JetParticleswID const& MCParticles, AnalysisCollisionsMCPOutlier const&, OriginalTracks const& origTracks, aod::McParticles const& origParticles) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || (static_cast(std::rand()) / RAND_MAX < eventReductionFactor)) { return; } // Uses only collisionId % trainingDatasetRaioParam == 0 for training dataset - if (trainingDatasetRatioParam && collision.collisionId() % trainingDatasetRatioParam != 0) { + if (trainingDatasetRatioParam != 0 && collision.collisionId() % trainingDatasetRatioParam != 0) { + return; + } + + // Reject outlier MC collisions + if (collision.isOutlier()) { + return; + } + if (collision.has_mcCollision() && collision.template mcCollision_as().isOutlier()) { return; } @@ -799,7 +846,7 @@ struct BJetTreeCreator { //+ TrackLabelMap trkLabels{{"trkVtxIndex", {}}, {"trkOrigin", {}}}; - int nVertices = jettaggingutilities::vertexClustering(collision.template mcCollision_as(), analysisJet, allTracks, MCParticles, origParticles, trkLabels, true, vtxRes, trackPtMin); + int nVertices = jettaggingutilities::vertexClustering(collision.template mcCollision_as(), analysisJet, allTracks, MCParticles, origParticles, trkLabels, true, vtxRes, trackPtMin); analyzeJetTrackInfoForGNN(collision, analysisJet, allTracks, origTracks, indicesTracks, jetFlavor, eventWeight, &trkLabels); registry.fill(HIST("h2_jetMass_jetpT"), analysisJet.pt(), analysisJet.mass(), eventWeight); @@ -828,6 +875,7 @@ struct BJetTreeCreator { if (produceTree) { bjetConstituentsTable(bjetParamsTable.lastIndex() + 1, indicesTracks, indicesSVs); bjetParamsTable(analysisJet.pt(), analysisJet.eta(), analysisJet.phi(), indicesTracks.size(), nVertices, analysisJet.mass(), jetFlavor, analysisJet.r()); + bjetParamsExtraTable(eventWeight); } } } diff --git a/PWGJE/Tasks/chargedJetHadron.cxx b/PWGJE/Tasks/chargedJetHadron.cxx index f08f17b1433..4423f41b081 100644 --- a/PWGJE/Tasks/chargedJetHadron.cxx +++ b/PWGJE/Tasks/chargedJetHadron.cxx @@ -55,6 +55,8 @@ struct ChargedJetHadron { Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; Configurable centralityMin{"centralityMin", 0.0, "minimum centrality"}; Configurable centralityMax{"centralityMax", 100.0, "maximum centrality"}; + Configurable triggerHadronPtMin{"triggerHadronPtMin", 15.0, "minimum trigger hadron pT for hjet"}; + Configurable subleadingHadronPtMin{"subleadingHadronPtMin", 10.0, "minimum recoil/subleading trigger hadron pT for di-hadron"}; Configurable leadingjetptMin{"leadingjetptMin", 20.0, "minimum leadingjetpt"}; Configurable subleadingjetptMin{"subleadingjetptMin", 10.0, "minimum subleadingjetpt"}; Configurable dijetDphiCut{"dijetDphiCut", 0.5, "minimum dijetDphiCut"}; @@ -80,9 +82,9 @@ struct ChargedJetHadron { Configurable acceptSplitCollisions{"acceptSplitCollisions", 0, "0: only look at mcCollisions that are not split; 1: accept split mcCollisions, 2: accept split mcCollisions but only look at the first reco collision associated with it"}; Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events; jet-level rejection can also be applied at the jet finder level for jets only, here rejection is applied for collision and track process functions for the first time, and on jets in case it was set to false at the jet finder level"}; Configurable checkLeadConstituentPtForMcpJets{"checkLeadConstituentPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; - Configurable doDijetEta{"doDijetEta", true, "0: dijet-hadron Eta axis, 1: dijet-hadron DEta axis"}; + Configurable doDijetEta{"doDijetEta", true, "1: dijet-hadron eta axis, 0: dijet-hadron delta-eta axis"}; Configurable doEventWeighted{"doEventWeighted", false, "0: weight is 1 for MB Sample, 1: weight from Jet-Jet Sample"}; - Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0A; 2:FT0M"}; + Configurable cfgCentEstimator{"cfgCentEstimator", 0, "0:FT0C; 1:FT0A; 2:FT0M; -1:FT0CVariant1"}; Configurable numberEventsMixed{"numberEventsMixed", 5, "number of events mixed in ME process"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0, 100., 300., 600., 1000., 2000., 5000., 8000.}, "Mixing bins - multiplicity"}; @@ -102,30 +104,21 @@ struct ChargedJetHadron { SliceCache cache; using FilterCollisions = soa::Filtered>; using FilterCollision = soa::Filtered>::iterator; - using FilterMcpCollision = soa::Filtered>::iterator; + using FilterMcdCollisions = soa::Filtered>; + using FilterMcdCollision = soa::Filtered>::iterator; using FilterMcpCollisions = soa::Filtered>; + using FilterMcpCollision = soa::Filtered>::iterator; using FilterJetTracks = soa::Filtered; using CorrChargedJets = soa::Join; using CorrChargedMCDJets = soa::Join; using CorrChargedMCPJets = soa::Join; - /* - using BinningTypePP = ColumnBinningPolicy; - using BinningTypeMCPP = ColumnBinningPolicy; - using BinningType = ColumnBinningPolicy; - using BinningTypeMC = ColumnBinningPolicy; - BinningTypePP corrBinning{{binsZVtx, binsMultiplicity}, true}; - BinningTypeMCPP corrBinningMC{{binsZVtx, binsMultiplicityMc}, true}; - BinningType corrBinning{{binsZVtx, binsMultiplicity}, true}; - BinningTypeMC corrBinningMC{{binsZVtx, binsMultiplicityMc}, true}; - */ - using BinningTypePP = ColumnBinningPolicy; using BinningType = ColumnBinningPolicy; using BinningTypeMC = ColumnBinningPolicy; - BinningType corrBinningPP{{binsZVtx, binsCentrality}, true}; - BinningType corrBinning{{binsZVtx, binsCentrality}, true}; - BinningTypeMC corrBinningMC{{binsZVtx, binsCentrality}, true}; + BinningType corrBinningPP{{binsZVtx, binsCentrality}}; + BinningType corrBinning{{binsZVtx, binsCentrality}}; + BinningTypeMC corrBinningMC{{binsZVtx, binsCentrality}}; HistogramRegistry registry; // histogram registry std::vector eventSelectionBits; @@ -139,11 +132,13 @@ struct ChargedJetHadron { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (cfgCentEstimator == 0) { eventCuts = (aod::jcollision::centFT0C >= centralityMin && aod::jcollision::centFT0C <= centralityMax); + } else if (cfgCentEstimator == -1) { + eventCuts = (aod::jcollision::centFT0CVariant1 >= centralityMin && aod::jcollision::centFT0CVariant1 <= centralityMax); } else if (cfgCentEstimator == 1) { eventCuts = (aod::jcollision::centFT0A >= centralityMin && aod::jcollision::centFT0A <= centralityMax); } else { @@ -156,16 +151,19 @@ struct ChargedJetHadron { AxisSpec phiAxis = {65, -0.2, 6.3, "#varphi"}; AxisSpec jetPtAxis = {200, 0., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetPtAxisRhoAreaSub = {280, -80., 200., "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec jetmultetaAxis = {4, -0.5, 0.5, "#Delta#eta"}; + AxisSpec jetmultetaAxis = {4, -0.5, 0.5, "#eta_{jet1}#eta_{jet2}"}; + AxisSpec hadronmultetaAxis = {4, -1.0, 1.0, "#eta_{trig1}#eta_{trig2}"}; AxisSpec detaAxis = {32, -1.6, 1.6, "#Delta#eta"}; AxisSpec dphiAxis = {70, -1.7, 5.3, "#Delta#varphi"}; AxisSpec drAxis = {30, 0.0, 1.5, "#Delta#it{R}"}; - AxisSpec axisBdtScore = {100, 0., 1., "Bdt score"}; + AxisSpec trackPtThnAxis = {{0.0, 0.15, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.7, 2.0, + 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 22.0, 25.0, 30.0, 35.0, 40.0, 50.0, 60.0, 70.0, 80.0, 100.0, 140.0}, + "#it{p}_{T} (GeV/#it{c})"}; if (doprocessCollisionsQCData || doprocessCollisionsQCMCD) { if (doprocessCollisionsQCMCD && doEventWeighted) { - registry.add("h_jet_phat", "jet #hat{p};#hat{p} (GeV/#it{c});entries", {HistType::kTH1F, {{1000, 0, 1000}}}); - registry.add("h_jet_phat_weighted", "jet #hat{p};#hat{p} (GeV/#it{c});entries", {HistType::kTH1F, {{1000, 0, 1000}}}); + registry.add("h_coll_phat", "collision #hat{p};#hat{p} (GeV/#it{c});entries", {HistType::kTH1F, {{1000, 0, 1000}}}); + registry.add("h_coll_phat_weighted", "collision #hat{p};#hat{p} (GeV/#it{c});entries", {HistType::kTH1F, {{1000, 0, 1000}}}); } registry.add("h_collisions", "event status;event status; entries", {HistType::kTH1F, {{7, 0.0, 7.0}}}); registry.add("h_collisions_weighted", "event status;event status;entries", {HistType::kTH1F, {{7, 0.0, 7.0}}}); @@ -174,8 +172,8 @@ struct ChargedJetHadron { registry.add("h_collisions_zvertex", "position of collision; #it{Z} (cm)", {HistType::kTH1F, {{300, -15.0, 15.0}}}); registry.add("h_collisions_multFT0", " multiplicity using multFT0; entries", {HistType::kTH1F, {{500, 0, 100000}}}); registry.add("h2_track_eta_track_phi", "track #eta vs. track #phi; #eta; #phi; counts", {HistType::kTH2F, {etaAxis, phiAxis}}); - registry.add("h2_track_eta_pt", "track #eta vs. track #it{p}_{T}; #eta; #it{p}_{T,track} (GeV/#it{c}; counts", {HistType::kTH2F, {etaAxis, trackPtAxis}}); - registry.add("h2_track_phi_pt", "track #phi vs. track #it{p}_{T}; #phi; #it{p}_{T,track} (GeV/#it{c}; counts", {HistType::kTH2F, {phiAxis, trackPtAxis}}); + registry.add("h2_track_eta_pt", "track #eta vs. track #it{p}_{T}; #eta; #it{p}_{T,track} (GeV/#it{c}); counts", {HistType::kTH2F, {etaAxis, trackPtAxis}}); + registry.add("h2_track_phi_pt", "track #phi vs. track #it{p}_{T}; #phi; #it{p}_{T,track} (GeV/#it{c}); counts", {HistType::kTH2F, {phiAxis, trackPtAxis}}); } if (doprocessSpectraAreaSubData || doprocessSpectraAreaSubMCD) { @@ -184,26 +182,28 @@ struct ChargedJetHadron { registry.add("h_jet_phi", "jet phi; #phi_{jet}; counts", {HistType::kTH1F, {phiAxis}}); registry.add("h_jet_area", "jet Area_{jet}; Area_{jet}; counts", {HistType::kTH1F, {{150, 0., 1.5}}}); registry.add("h_jet_ntracks", "jet N_{jet tracks}; N_{jet, tracks}; counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); + registry.add("h2_jet_pt_track_pt", "jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, trackPtAxis}}); registry.add("h_jet_pt_rhoareasubtracted", "jet pt; #it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_eta_rhoareasubtracted", "jet eta; #eta_{jet}; counts", {HistType::kTH1F, {etaAxis}}); registry.add("h_jet_phi_rhoareasubtracted", "jet phi; #phi_{jet}; counts", {HistType::kTH1F, {phiAxis}}); registry.add("h_jet_area_rhoareasubtracted", "jet Area_{jet}; Area_{jet}; counts", {HistType::kTH1F, {{150, 0., 1.5}}}); registry.add("h_jet_ntracks_rhoareasubtracted", "jet N_{jet tracks}; N_{jet,tracks}; counts", {HistType::kTH1F, {{200, 0., 200.}}}); + registry.add("h2_jet_pt_track_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, trackPtAxis}}); } //========jet-hadron correlations====================== if (doprocessJetHadron || doprocessMixJetHadron || doprocessJetHadronMCD || doprocessMixJetHadronMCD) { registry.add("h_trigjet_corrpt", "trigger jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("thn_jeth_correlations", "jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtAxis, detaAxis, dphiAxis, drAxis}); - registry.add("h_jeth_event_stats", "Same event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0., 7.}}}); + registry.add("thn_jeth_correlations", "jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtThnAxis, detaAxis, dphiAxis, drAxis}); + registry.add("h_jeth_event_stats", "Same event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0.5, 7.5}}}); registry.get(HIST("h_jeth_event_stats"))->GetXaxis()->SetBinLabel(1, "Total jets"); registry.get(HIST("h_jeth_event_stats"))->GetXaxis()->SetBinLabel(2, "Total jets with pTHat cut"); registry.get(HIST("h_jeth_event_stats"))->GetXaxis()->SetBinLabel(3, "Total jets with cuts"); registry.get(HIST("h_jeth_event_stats"))->GetXaxis()->SetBinLabel(4, "Total j-h pairs"); registry.get(HIST("h_jeth_event_stats"))->GetXaxis()->SetBinLabel(5, "Total j-h pairs with accepted"); registry.add("h_mixtrigjet_corrpt", "trigger jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("thn_mixjeth_correlations", "ME: jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtAxis, detaAxis, dphiAxis, drAxis}); - registry.add("h_mixjeth_event_stats", "Mixed event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0., 7.}}}); + registry.add("thn_mixjeth_correlations", "ME: jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtThnAxis, detaAxis, dphiAxis, drAxis}); + registry.add("h_mixjeth_event_stats", "Mixed event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0.5, 7.5}}}); registry.get(HIST("h_mixjeth_event_stats"))->GetXaxis()->SetBinLabel(1, "Total mixed events"); registry.get(HIST("h_mixjeth_event_stats"))->GetXaxis()->SetBinLabel(2, "Total jets"); registry.get(HIST("h_mixjeth_event_stats"))->GetXaxis()->SetBinLabel(3, "Total jets with cuts"); @@ -211,21 +211,6 @@ struct ChargedJetHadron { registry.get(HIST("h_mixjeth_event_stats"))->GetXaxis()->SetBinLabel(5, "Total j-h pairs with accepted"); } - if (doprocessHFJetCorrelation) { - registry.add("h_d0jet_pt", "D0 jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxis}}); - registry.add("h_d0jet_corrpt", "D0 jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("h_d0jet_eta", "D0 jet eta;#eta; counts", {HistType::kTH1F, {etaAxis}}); - registry.add("h_d0jet_phi", "D0 jet phi;#phi; counts", {HistType::kTH1F, {phiAxis}}); - registry.add("h_d0_pt", ";p_{T,D^{0}};dN/dp_{T,D^{0}}", {HistType::kTH1F, {{200, 0., 10.}}}); - registry.add("h_d0_mass", ";m_{D^{0}} (GeV/c^{2});dN/dm_{D^{0}}", {HistType::kTH1F, {{1000, 0., 10.}}}); - registry.add("h_d0_eta", ";#eta_{D^{0}} (GeV/c^{2});dN/d#eta_{D^{0}}", {HistType::kTH1F, {{200, -5., 5.}}}); - registry.add("h_d0_phi", ";#varphi_{D^{0}} (GeV/c^{2});dN/d#varphi_{D^{0}}", {HistType::kTH1F, {{200, -10., 10.}}}); - registry.add("h_d0_bdtScorePrompt", "D0 BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); - registry.add("h_d0_bdtScoreBkg", "D0 BDT bkg score", {HistType::kTH1F, {axisBdtScore}}); - registry.add("h_d0bar_bdtScorePrompt", "D0bar BDT prompt score", {HistType::kTH1F, {axisBdtScore}}); - registry.add("h_d0bar_bdtScoreBkg", "D0bar BDT bkg score", {HistType::kTH1F, {axisBdtScore}}); - registry.add("h2_d0jet_detadphi", "D^{0}-jets deta vs dphi; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - } //========leading jet-hadron correlations====================== if (doprocessLeadingJetHadron || doprocessLeadingJetHadronMCD) { registry.add("h_centrality", "centrality distributions; centrality; counts", {HistType::kTH1F, {centralityAxis}}); @@ -233,11 +218,15 @@ struct ChargedJetHadron { registry.add("h_leadjet_pt", "leading jet pT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxis}}); registry.add("h_leadjet_corrpt", "leading jet corrpT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_subleadjet_pt", "subleading jet pT;#it{p}_{T,subleadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxis}}); - registry.add("h_subleadjet_corrpt", "subleading jet corrpT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_subleadjet_corrpt", "subleading jet corrpT;#it{p}_{T,subleadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_leadjet_eta", "leading jet eta;#eta; counts", {HistType::kTH1F, {etaAxis}}); registry.add("h_leadjet_phi", "leading jet phi;#phi; counts", {HistType::kTH1F, {phiAxis}}); registry.add("h_subleadjet_eta", "subleading jet eta;#eta; counts", {HistType::kTH1F, {etaAxis}}); registry.add("h_subleadjet_phi", "subleading jet phi;#phi; counts", {HistType::kTH1F, {phiAxis}}); + registry.add("h_leadjet_constituent_pt", "leading jet constituent;#it{p}_{T}^{const}", {HistType::kTH1F, {trackPtAxis}}); + registry.add("h_subleadjet_constituent_pt", "subleading jet constituent;#it{p}_{T}^{const}", {HistType::kTH1F, {trackPtAxis}}); + registry.add("h_leadjet_leadingconstituent_pt", "leading jet leading constituent;#it{p}_{T}^{leading const} (GeV/#it{c});counts", {HistType::kTH1F, {trackPtAxis}}); + registry.add("h_subleadjet_leadingconstituent_pt", "subleading jet leading constituent;#it{p}_{T}^{leading const} (GeV/#it{c});counts", {HistType::kTH1F, {trackPtAxis}}); registry.add("h2_dijet_detanoflip_dphi", "dijet #Delta#eta no flip vs #Delta#varphi; #Delta#eta_{noflip}; #Delta#varphi; counts", {HistType::kTH2F, {detaAxis, {63, 0, 6.3}}}); registry.add("h2_dijet_Asymmetry", "dijet Asymmetry; #it{p}_{T,subleadingjet} (GeV/#it{c}); #it{X}_{J}; counts", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {40, 0, 1.0}}}); registry.add("h3_dijet_deta_pt", "dijet #Delta#eta flip vs #it{p}_{T,jet1-jet2}; #Delta#eta_{flip}; #it{p}_{T,jet1} (GeV/#it{c}); #it{p}_{T,jet2} (GeV/#it{c})", {HistType::kTH3F, {{16, 0, 1.6}, jetPtAxis, jetPtAxis}}); @@ -245,50 +234,49 @@ struct ChargedJetHadron { registry.add("h_jeth_deta", "jeth #Delta#eta; #Delta#eta; counts", {HistType::kTH1F, {detaAxis}}); registry.add("h_jeth_dphi", "jeth #Delta#varphi; #Delta#varphi; counts", {HistType::kTH1F, {dphiAxis}}); if (doDijetEta) { - registry.add("h2_dijet_TimeEtaThan0_pt", "dijet #eta_{jet1}#eta_{jet1} > 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); - registry.add("h2_dijet_TimeEtaLess0_pt", "dijet #eta_{jet1}#eta_{jet1} < 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); + registry.add("h2_dijet_TimeEtaThan0_pt", "dijet #eta_{jet1}#eta_{jet2} > 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); + registry.add("h2_dijet_TimeEtaLess0_pt", "dijet #eta_{jet1}#eta_{jet2} < 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); registry.add("h2_jeth_heta_dphi", "jeth heta vs dphi; hadron #eta; #Delta#phi", {HistType::kTH2F, {etaAxis, dphiAxis}}); - registry.add("thn_ljeth_correlations", "leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; timedijeteta; track #eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, jetmultetaAxis, etaAxis, dphiAxis}); + registry.add("thn_ljeth_correlations", "leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; timedijeteta; track #eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {16, 0., 8.}, jetmultetaAxis, etaAxis, dphiAxis}); } else { registry.add("h2_jeth_physicalcutsup_deta_dphi", "jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| > 1.0; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); registry.add("h2_jeth_physicalcutsmd_deta_dphi", "jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| #in (0.5, 1.0); #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); registry.add("h2_jeth_physicalcutsdw_deta_dphi", "jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| < 0.5; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("thn_ljeth_correlations", "leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; #Delta#eta_{jet1,2}; jeth#Delta#eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, {16, 0, 1.6}, detaAxis, dphiAxis}); + registry.add("thn_ljeth_correlations", "leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; #Delta#eta_{jet1,2}; jeth#Delta#eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {16, 0., 8.}, {16, 0, 1.6}, detaAxis, dphiAxis}); } } + //=============hjet-hadron correlations====================== + if (doprocessHadronJetHadron || doprocessHadronJetHadronMCD) { + registry.add("h_hjet_trigtrack_pt", "trigger hadron pT;#it{p}_{T,trig} (GeV/#it{c}); counts", {HistType::kTH1F, {trackPtAxis}}); + registry.add("h_hjet_trigtrack_eta", "trigger hadron #eta;#eta_{trig}; counts", {HistType::kTH1F, {etaAxis}}); + registry.add("h_hjet_trigtrack_phi", "trigger hadron #varphi;#varphi_{trig}; counts", {HistType::kTH1F, {phiAxis}}); + registry.add("h_hjet_recoiljet_corrpt", "recoil jet corrected pT;#it{p}_{T,recoil jet}^{corr} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_hjet_recoiljet_eta", "recoil jet #eta;#eta_{recoil jet}; counts", {HistType::kTH1F, {etaAxis}}); + registry.add("h_hjet_recoiljet_phi", "recoil jet #varphi;#varphi_{recoil jet}; counts", {HistType::kTH1F, {phiAxis}}); + registry.add("h_hjet_dphi", "hjet #Delta#varphi; #Delta#varphi_{hjet}; counts", {HistType::kTH1F, {{63, 0., 6.3}}}); + registry.add("h_recojet_constituent_pt", "recoil jet constituent;#it{p}_{T}^{const}", {HistType::kTH1F, {trackPtAxis}}); + registry.add("h2_hjet_TimeEtaThan0_pt", "hjet #eta_{trig1}#eta_{jet2} > 0;#it{p}_{T,trig};#it{p}_{T,recoil jet}^{corr}", {HistType::kTH2F, {trackPtAxis, jetPtAxis}}); + registry.add("h2_hjet_TimeEtaLess0_pt", "hjet #eta_{trig1}#eta_{jet2} < 0;#it{p}_{T,trig};#it{p}_{T,recoil jet}^{corr}", {HistType::kTH2F, {trackPtAxis, jetPtAxis}}); + registry.add("h2_hjet_heta_dphi", "hjet heta vs dphi; hadron #eta; #Delta#phi", {HistType::kTH2F, {etaAxis, dphiAxis}}); + registry.add("thn_hjeth_correlations", "hjet-h correlations; triggerHadronpT; recoilJetpT; trackpT; timehjeteta; track #eta; hh#Delta#varphi", HistType::kTHnSparseF, {trackPtAxis, jetPtAxis, {16, 0., 8.}, hadronmultetaAxis, etaAxis, dphiAxis}); + } - if (doprocessMixLeadingJetHadron || doprocessMixLeadingJetHadronMCD) { - registry.add("h_mixdijet_pair_counts_cut", "ME: number of pairs with leadingjet & subleadingjet cut pair; jet pairs; counts", {HistType::kTH1F, {{10, 0, 10}}}); - registry.add("h_mixleadjet_corrpt", "ME: leading jet corrpT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("h_mixsubleadjet_corrpt", "ME: subleading jet corrpT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("h_mixleadjet_eta", "ME: leading jet eta; #eta; counts", {HistType::kTH1F, {etaAxis}}); - registry.add("h_mixsubleadjet_eta", "ME: subleading jet eta; #eta; counts", {HistType::kTH1F, {etaAxis}}); - registry.add("h2_mixdijet_detanoflip_dphi", "ME: dijet #Delta#eta no flip vs #Delta#varphi; #Delta#eta_{noflip}; #Delta#varphi; counts", {HistType::kTH2F, {detaAxis, {63, 0, 6.3}}}); - registry.add("h2_mixdijet_Asymmetry", "ME: dijet Asymmetry; #it{p}_{T,subleadingjet} (GeV/#it{c}); #it{X}_{J}; counts", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {40, 0, 1.0}}}); - registry.add("h3_mixdijet_deta_pt", "ME: dijet #Delta#eta flip vs #it{p}_{T,jet1-jet2}; #Delta#eta_{flip}; #Delta#varphi; counts", {HistType::kTH3F, {{16, 0, 1.6}, jetPtAxis, jetPtAxis}}); - registry.add("h_mixjeth_detatot", "ME: jeth correlations; no flip #Delta#eta", {HistType::kTH1F, {detaAxis}}); - registry.add("h_mixjeth_deta", "ME: jeth correlations; #Delta#eta", {HistType::kTH1F, {detaAxis}}); - registry.add("h_mixjeth_dphi", "ME: jeth correlations; #Delta#phi", {HistType::kTH1F, {dphiAxis}}); - if (doDijetEta) { - registry.add("h2_mixdijet_TimeEtaThan0_pt", "dijet #eta_{jet1}#eta_{jet1} > 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); - registry.add("h2_mixdijet_TimeEtaLess0_pt", "dijet #eta_{jet1}#eta_{jet1} < 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); - registry.add("h2_mixjeth_heta_dphi", "ME: jeth heta vs dphi; hadron #eta; #Delta#phi", {HistType::kTH2F, {etaAxis, dphiAxis}}); - registry.add("thn_mixljeth_correlations", "ME: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; timedijeteta; track #eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, jetmultetaAxis, etaAxis, dphiAxis}); - } else { - registry.add("h2_mixjeth_physicalcutsup_deta_dphi", "ME: jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| > 1.0; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("h2_mixjeth_physicalcutsmd_deta_dphi", "ME: jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| #in (0.5, 1.0); #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("h2_mixjeth_physicalcutsdw_deta_dphi", "ME: jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| < 0.5; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("thn_mixljeth_correlations", "ME: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; #Delta#eta_{jet1,2}; jeth#Delta#eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, {16, 0, 1.6}, detaAxis, dphiAxis}); - } - - registry.add("h_mix_event_stats", "Mixed event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0., 7.}}}); - registry.get(HIST("h_mix_event_stats"))->GetXaxis()->SetBinLabel(1, "Total mixed events"); - registry.get(HIST("h_mix_event_stats"))->GetXaxis()->SetBinLabel(2, "Total dijets"); - registry.get(HIST("h_mix_event_stats"))->GetXaxis()->SetBinLabel(3, "Total dijets with cuts"); - registry.get(HIST("h_mix_event_stats"))->GetXaxis()->SetBinLabel(4, "Total Lj-h pairs"); - registry.get(HIST("h_mix_event_stats"))->GetXaxis()->SetBinLabel(5, "Total Lj-h pairs with cut"); + //========dihadron-hadron correlations========== + if (doprocessDiHadron || doprocessDiHadronMCD) { + registry.add("h_dihadron_leadtrack_pt", "leading trigger hadron pT;#it{p}_{T,trig1} (GeV/#it{c}); counts", {HistType::kTH1F, {trackPtAxis}}); + registry.add("h_dihadron_leadtrack_eta", "leading trigger hadron #eta;#eta_{trig1}; counts", {HistType::kTH1F, {etaAxis}}); + registry.add("h_dihadron_leadtrack_phi", "leading trigger hadron #varphi;#varphi_{trig1}; counts", {HistType::kTH1F, {phiAxis}}); + registry.add("h_dihadron_subleadtrack_pt", "recoil trigger hadron pT;#it{p}_{T,trig2} (GeV/#it{c}); counts", {HistType::kTH1F, {trackPtAxis}}); + registry.add("h_dihadron_subleadtrack_eta", "recoil trigger hadron #eta;#eta_{trig2}; counts", {HistType::kTH1F, {etaAxis}}); + registry.add("h_dihadron_subleadtrack_phi", "recoil trigger hadron #varphi;#varphi_{trig2}; counts", {HistType::kTH1F, {phiAxis}}); + registry.add("h_dihadron_dphi", "di-hadron #Delta#varphi;|#Delta#varphi_{trig1,trig2}|; counts", {HistType::kTH1F, {{63, 0., 6.3}}}); + registry.add("h2_dihadron_TimeEtaThan0_pt", "di-hadron #eta_{trig1}#eta_{trig2} > 0;#it{p}_{T,trig1};#it{p}_{T,trig2}", {HistType::kTH2F, {trackPtAxis, trackPtAxis}}); + registry.add("h2_dihadron_TimeEtaLess0_pt", "di-hadron #eta_{trig1}#eta_{trig2} < 0;#it{p}_{T,trig1};#it{p}_{T,trig2}", {HistType::kTH2F, {trackPtAxis, trackPtAxis}}); + registry.add("h2_dihadron_heta_dphi", "di-hadron associated-hadron #eta vs #Delta#varphi;hadron #eta;#Delta#varphi", {HistType::kTH2F, {etaAxis, dphiAxis}}); + registry.add("thn_dihadron_correlations", "di-hadron correlations;leadingHadronpT;recoilHadronpT;associatedHadronpT;#eta_{trig1}#eta_{trig2};associated hadron #eta;h-h #Delta#varphi", HistType::kTHnSparseF, {trackPtAxis, trackPtAxis, {16, 0., 8.}, hadronmultetaAxis, etaAxis, dphiAxis}); } + //======MC===================== if (doprocessCollisionsQCMCP) { registry.add("h_mcColl_counts", " number of mc events; event status; entries", {HistType::kTH1F, {{9, 0., 9.}}}); registry.get(HIST("h_mcColl_counts"))->GetXaxis()->SetBinLabel(1, "allMcColl"); @@ -298,40 +286,42 @@ struct ChargedJetHadron { registry.get(HIST("h_mcColl_counts"))->GetXaxis()->SetBinLabel(5, "recoEvtSel"); registry.get(HIST("h_mcColl_counts"))->GetXaxis()->SetBinLabel(6, "occupancyCut"); registry.get(HIST("h_mcColl_counts"))->GetXaxis()->SetBinLabel(7, "centralityCut"); + registry.get(HIST("h_mcColl_counts"))->GetXaxis()->SetBinLabel(8, "accepted"); registry.add("h_mcpColl_zvertex", "position of collision ;#it{Z} (cm)", {HistType::kTH1F, {{300, -15.0, 15.0}}}); registry.add("h_mcpColl_centrality", "mcp collision centrality; centrality; counts", {HistType::kTH1F, {centralityAxis}}); registry.add("h_mcpColl_multFT0", " mcp multiplicity global tracks; entries", {HistType::kTH1F, {{500, 0, 100000}}}); registry.add("h2_particle_eta_phi", "particle #eta vs. particle #phi; #eta; #phi; counts", {HistType::kTH2F, {etaAxis, phiAxis}}); - registry.add("h2_particle_eta_pt", "particle #eta vs. particle #it{p}_{T}; #eta; #it{p}_{T,particle} (GeV/#it{c}; counts", {HistType::kTH2F, {etaAxis, trackPtAxis}}); - registry.add("h2_particle_phi_pt", "particle #phi vs. particle #it{p}_{T}; #phi; #it{p}_{T,particle} (GeV/#it{c}; counts", {HistType::kTH2F, {phiAxis, trackPtAxis}}); + registry.add("h2_particle_eta_pt", "particle #eta vs. particle #it{p}_{T}; #eta; #it{p}_{T,particle} (GeV/#it{c}); counts", {HistType::kTH2F, {etaAxis, trackPtAxis}}); + registry.add("h2_particle_phi_pt", "particle #phi vs. particle #it{p}_{T}; #phi; #it{p}_{T,particle} (GeV/#it{c}); counts", {HistType::kTH2F, {phiAxis, trackPtAxis}}); } if (doprocessSpectraAreaSubMCP) { if (doEventWeighted) { - registry.add("h_mcColl_counts_weight", " number of weighted mc events; event status; entries", {HistType::kTH1F, {{7, 0., 7.}}}); + registry.add("h_mcColl_counts_weight", " number of weighted mc events; event status; entries", {HistType::kTH1F, {{7, 0.5, 7.5}}}); registry.get(HIST("h_mcColl_counts_weight"))->GetXaxis()->SetBinLabel(1, "McColl"); registry.get(HIST("h_mcColl_counts_weight"))->GetXaxis()->SetBinLabel(2, "xsectGen"); registry.get(HIST("h_mcColl_counts_weight"))->GetXaxis()->SetBinLabel(3, "event weight"); } registry.add("h_mcColl_rho", "mc collision rho;#rho (GeV/#it{c}); counts", {HistType::kTH1F, {{500, 0.0, 500.0}}}); - registry.add("h_jet_pt_part", "partvjet pT;#it{p}_{T,jet}^{part} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxis}}); + registry.add("h_jet_pt_part", "part jet pT;#it{p}_{T,jet}^{part} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxis}}); registry.add("h_jet_eta_part", "part jet #eta;#eta^{part}; counts", {HistType::kTH1F, {etaAxis}}); registry.add("h_jet_phi_part", "part jet #varphi;#phi^{part}; counts", {HistType::kTH1F, {phiAxis}}); registry.add("h_jet_area_part", "part jet Area_{jet}; Area_{jet}^{part}; counts", {HistType::kTH1F, {{150, 0., 1.5}}}); registry.add("h_jet_ntracks_part", "part jet N_{jet tracks}; N_{jet, tracks}^{part}; counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); + registry.add("h2_jet_pt_part_track_pt_part", "part jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet}^{part} (GeV/#it{c}); #it{p}_{T,track}^{part} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, trackPtAxis}}); registry.add("h_jet_pt_part_rhoareasubtracted", "part jet corr pT;#it{p}_{T,jet}^{part} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_eta_part_rhoareasubtracted", "part jet #eta;#eta^{part}; counts", {HistType::kTH1F, {etaAxis}}); registry.add("h_jet_phi_part_rhoareasubtracted", "part jet #varphi;#varphi^{part}; counts", {HistType::kTH1F, {phiAxis}}); registry.add("h_jet_area_part_rhoareasubtracted", "part jet Area_{jet}; Area_{jet}^{part}; counts", {HistType::kTH1F, {{150, 0., 1.5}}}); registry.add("h_jet_ntracks_part_rhoareasubtracted", "part jet N_{jet tracks}; N_{jet, tracks}^{part}; counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); + registry.add("h2_jet_pt_part_track_pt_part_rhoareasubtracted", "part jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet}^{part} (GeV/#it{c}); #it{p}_{T,track}^{part} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, trackPtAxis}}); } if (doprocessJetHadronMCP || doprocessMixJetHadronMCP) { //.........MCP: jet-hadron correlations................. registry.add("h_trigjet_corrpt_part", "trigger jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("thn_jeth_correlations_part", "MCP: jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtAxis, detaAxis, dphiAxis, drAxis}); - - registry.add("h_jeth_event_stats_part", "MCP: same event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0., 7.}}}); + registry.add("thn_jeth_correlations_part", "MCP: jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtThnAxis, detaAxis, dphiAxis, drAxis}); + registry.add("h_jeth_event_stats_part", "MCP: same event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0.5, 7.5}}}); registry.get(HIST("h_jeth_event_stats_part"))->GetXaxis()->SetBinLabel(1, "Total jets"); registry.get(HIST("h_jeth_event_stats_part"))->GetXaxis()->SetBinLabel(2, "Total jets with pTHat cut"); registry.get(HIST("h_jeth_event_stats_part"))->GetXaxis()->SetBinLabel(3, "Total jets with cuts"); @@ -339,8 +329,8 @@ struct ChargedJetHadron { registry.get(HIST("h_jeth_event_stats_part"))->GetXaxis()->SetBinLabel(5, "Total j-h pairs with accepted"); registry.add("h_mixtrigjet_corrpt_part", "trigger jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("thn_mixjeth_correlations_part", "mcpME: jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtAxis, detaAxis, dphiAxis, drAxis}); - registry.add("h_mixjeth_event_stats_part", "MCP: mixed event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0., 7.}}}); + registry.add("thn_mixjeth_correlations_part", "mcpME: jet-h correlations; jetpT; trackpT; jeth#Delta#eta; jeth#Delta#varphi; jeth#Delta#it{R}", HistType::kTHnSparseF, {jetPtAxis, trackPtThnAxis, detaAxis, dphiAxis, drAxis}); + registry.add("h_mixjeth_event_stats_part", "MCP: mixed event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0.5, 7.5}}}); registry.get(HIST("h_mixjeth_event_stats_part"))->GetXaxis()->SetBinLabel(1, "Total mixed events"); registry.get(HIST("h_mixjeth_event_stats_part"))->GetXaxis()->SetBinLabel(2, "Total jets"); registry.get(HIST("h_mixjeth_event_stats_part"))->GetXaxis()->SetBinLabel(3, "Total jets with cuts"); @@ -350,6 +340,7 @@ struct ChargedJetHadron { if (doprocessLeadingJetHadronMCP) { //.........SE leading jet correlations............... + registry.add("h_inclusivejet_corrpt_part", "part inclusive jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_leadjet_pt_part", "MCP: leading jet pT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxis}}); registry.add("h_leadjet_corrpt_part", "MCP: leading jet corrpT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_leadjet_eta_part", "MCP: leading jet eta;#eta; counts", {HistType::kTH1F, {etaAxis}}); @@ -360,55 +351,23 @@ struct ChargedJetHadron { registry.add("h_subleadjet_phi_part", "MCP: subleading jet phi;#phi; counts", {HistType::kTH1F, {phiAxis}}); registry.add("h2_dijet_detanoflip_dphi_part", "MCP: dijet #Delta#eta no flip vs #Delta#varphi; #Delta#eta_{noflip}; #Delta#varphi; counts", {HistType::kTH2F, {detaAxis, {63, 0, 6.3}}}); registry.add("h2_dijet_Asymmetry_part", "MCP: dijet Asymmetry; #it{p}_{T,subleadingjet} (GeV/#it{c}); #it{X}_{J}; counts", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {40, 0, 1.0}}}); - registry.add("h3_dijet_deta_pt_part", "MCP: dijet #Delta#eta flip vs #it{p}_{T,jet1-jet2}; #Delta#eta_{flip}; #Delta#varphi; counts", {HistType::kTH3F, {{16, 0, 1.6}, jetPtAxis, jetPtAxis}}); + registry.add("h3_dijet_deta_pt_part", "MCP: dijet #Delta#eta flip vs #it{p}_{T,jet1-jet2}; #Delta#eta_{flip}; #it{p}_{T,jet1}^{corr}; #it{p}_{T,jet2}^{corr};", {HistType::kTH3F, {{16, 0, 1.6}, jetPtAxis, jetPtAxis}}); registry.add("h_jeth_detatot_part", "MCP: jeth tot #Delta#eta; #Delta#eta; counts", {HistType::kTH1F, {detaAxis}}); registry.add("h_jeth_deta_part", "MCP: jeth #Delta#eta; #Delta#eta; counts", {HistType::kTH1F, {detaAxis}}); registry.add("h_jeth_dphi_part", "MCP: jeth #Delta#varphi; #Delta#varphi; counts", {HistType::kTH1F, {dphiAxis}}); if (doDijetEta) { - registry.add("h2_dijet_TimeEtaThan0_pt_part", "dijet #eta_{jet1}#eta_{jet1} > 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); - registry.add("h2_dijet_TimeEtaLess0_pt_part", "dijet #eta_{jet1}#eta_{jet1} < 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); + registry.add("h2_dijet_TimeEtaThan0_pt_part", "dijet #eta_{jet1}#eta_{jet2} > 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); + registry.add("h2_dijet_TimeEtaLess0_pt_part", "dijet #eta_{jet1}#eta_{jet2} < 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); registry.add("h2_jeth_heta_dphi_part", "MCP: jeth heta vs dphi; hadron #eta; #Delta#phi", {HistType::kTH2F, {etaAxis, dphiAxis}}); - registry.add("thn_ljeth_correlations_part", "MCP: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; timedijeteta; track #eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, jetmultetaAxis, etaAxis, dphiAxis}); + registry.add("thn_ljeth_correlations_part", "MCP: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; timedijeteta; track #eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {16, 0., 8.}, jetmultetaAxis, etaAxis, dphiAxis}); } else { registry.add("h2_jeth_physicalcutsup_deta_dphi_part", "MCP: jeth deta vs dphi with physical cuts |#Delta#eta_{jet}| > 1.0; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); registry.add("h2_jeth_physicalcutsmd_deta_dphi_part", "MCP: jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| #in (0.5, 1.0); #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); registry.add("h2_jeth_physicalcutsdw_deta_dphi_part", "MCP: jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| < 0.5; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("thn_ljeth_correlations_part", "MCP: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; #Delta#eta_{jet1,2}; jeth#Delta#eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, {16, 0, 1.6}, detaAxis, dphiAxis}); + registry.add("thn_ljeth_correlations_part", "MCP: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; #Delta#eta_{jet1,2}; jeth#Delta#eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {16, 0., 8.}, {16, 0, 1.6}, detaAxis, dphiAxis}); } } - if (doprocessMixLeadingJetHadronMCP) { - //...........mcp mixed events: leading jet correlations................. - registry.add("h_mixdijet_pair_counts_cut_part", "mcpME: number of pairs with leadingjet & subleadingjet cut pair; jet pairs; counts", {HistType::kTH1F, {{10, 0, 10}}}); - registry.add("h_mixleadjet_corrpt_part", "mcpME: leading jet corrpT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("h_mixsubleadjet_corrpt_part", "mcpME: subleading jet corrpT;#it{p}_{T,leadingjet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); - registry.add("h_mixleadjet_eta_part", "mcpME: leading jet eta; #eta; counts", {HistType::kTH1F, {etaAxis}}); - registry.add("h_mixsubleadjet_eta_part", "mcpME: subleading jet eta; #eta; counts", {HistType::kTH1F, {etaAxis}}); - registry.add("h2_mixdijet_detanoflip_dphi_part", "mcpME: dijet #Delta#eta no flip vs #Delta#varphi; #Delta#eta_{noflip}; #Delta#varphi; counts", {HistType::kTH2F, {detaAxis, {63, 0, 6.3}}}); - registry.add("h2_mixdijet_Asymmetry_part", "mcpME: dijet Asymmetry; #it{p}_{T,subleadingjet} (GeV/#it{c}); #it{X}_{J}; counts", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {40, 0, 1.0}}}); - registry.add("h3_mixdijet_deta_pt_part", "mcpME: dijet #Delta#eta flip vs #it{p}_{T,jet1-jet2}; #Delta#eta_{flip}; #Delta#varphi; counts", {HistType::kTH3F, {{16, 0, 1.6}, jetPtAxis, jetPtAxis}}); - registry.add("h_mixjeth_detatot_part", "mcpME: jeth correlations; no flip #Delta#eta", {HistType::kTH1F, {detaAxis}}); - registry.add("h_mixjeth_deta_part", "mcpME: jeth correlations; #Delta#eta", {HistType::kTH1F, {detaAxis}}); - registry.add("h_mixjeth_dphi_part", "mcpME: jeth correlations; #Delta#phi", {HistType::kTH1F, {dphiAxis}}); - if (doDijetEta) { - registry.add("h2_mixdijet_TimeEtaThan0_pt_part", "dijet #eta_{jet1}#eta_{jet1} > 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); - registry.add("h2_mixdijet_TimeEtaLess0_pt_part", "dijet #eta_{jet1}#eta_{jet1} < 0", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); - registry.add("h2_mixjeth_heta_dphi_part", "mcpME: jeth heta vs dphi; hadron #eta; #Delta#phi", {HistType::kTH2F, {etaAxis, dphiAxis}}); - registry.add("thn_mixljeth_correlations_part", "mcpME: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; timedijeteta; track #eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, jetmultetaAxis, etaAxis, dphiAxis}); - } else { - registry.add("h2_mixjeth_physicalcutsup_deta_dphi_part", "mcpME: jeth deta vs dphi with physical cuts |#Delta#eta_{jet}| > 1.0; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("h2_mixjeth_physicalcutsmd_deta_dphi_part", "mcpME: jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| #in (0.5, 1.0); #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("h2_mixjeth_physicalcutsdw_deta_dphi_part", "mcpME: jeth deta vs dphi with physical cuts |#Delta#eta_{jet1,2}| < 0.5; #Delta#eta; #Delta#phi", {HistType::kTH2F, {detaAxis, dphiAxis}}); - registry.add("thn_mixljeth_correlations_part", "mcpME: leading jet-h correlations; leadingjetpT; subleadingjetpT; trackpT; #Delta#eta_{jet1,2}; jeth#Delta#eta; jeth#Delta#varphi", HistType::kTHnSparseF, {jetPtAxis, jetPtAxis, {8, 0., 8.}, {16, 0, 1.6}, detaAxis, dphiAxis}); - } - - registry.add("h_mixevent_stats_part", "MCP: mixed event statistics; Event pair type; counts", {HistType::kTH1F, {{7, 0., 7.}}}); - registry.get(HIST("h_mixevent_stats_part"))->GetXaxis()->SetBinLabel(1, "Total mixed events"); - registry.get(HIST("h_mixevent_stats_part"))->GetXaxis()->SetBinLabel(2, "Total dijets"); - registry.get(HIST("h_mixevent_stats_part"))->GetXaxis()->SetBinLabel(3, "Total dijets with cuts"); - registry.get(HIST("h_mixevent_stats_part"))->GetXaxis()->SetBinLabel(4, "Total Lj-h pairs"); - registry.get(HIST("h_mixevent_stats_part"))->GetXaxis()->SetBinLabel(5, "Total Lj-h pairs with cut"); - } if (!(acceptSplitCollisions == NonSplitOnly || acceptSplitCollisions == SplitOkCheckAnyAssocColl || acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly)) { LOGF(fatal, "Configurable acceptSplitCollisions has wrong input value; stopping workflow"); } @@ -421,6 +380,8 @@ struct ChargedJetHadron { { if (cfgCentEstimator.value == 0) return coll.centFT0C(); + if (cfgCentEstimator.value == -1) + return coll.centFT0CVariant1(); if (cfgCentEstimator.value == 1) return coll.centFT0A(); return coll.centFT0M(); @@ -431,6 +392,8 @@ struct ChargedJetHadron { { if (cfgCentEstimator.value == 0) return coll.multFT0C(); + if (cfgCentEstimator.value == -1) + return coll.multFT0C(); if (cfgCentEstimator.value == 1) return coll.multFT0A(); return coll.multFT0C() + coll.multFT0A(); @@ -535,23 +498,15 @@ struct ChargedJetHadron { template void fillTrackHistograms(TTracks const& track, float weight = 1.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); - if (track.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) - return; - if (doEventWeighted) { - registry.fill(HIST("h_jet_phat"), pTHat); - registry.fill(HIST("h_jet_phat_weighted"), pTHat, weight); - } registry.fill(HIST("h2_track_eta_track_phi"), track.eta(), track.phi(), weight); registry.fill(HIST("h2_track_eta_pt"), track.eta(), track.pt(), weight); registry.fill(HIST("h2_track_phi_pt"), track.phi(), track.pt(), weight); } template - void fillJetHistograms(TJets const& jet, float weight = 1.0) + void fillJetHistograms(TJets const& jet, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); - if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) + if (doEventWeighted && (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin)) return; if (jet.r() == round(selectedJetsRadius * 100.0f)) { registry.fill(HIST("h_jet_pt"), jet.pt(), weight); @@ -560,13 +515,15 @@ struct ChargedJetHadron { registry.fill(HIST("h_jet_area"), jet.area(), weight); registry.fill(HIST("h_jet_ntracks"), jet.tracksIds().size(), weight); } + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_track_pt"), jet.pt(), constituent.pt(), weight); + } } template - void fillJetAreaSubHistograms(TJets const& jet, float rho, float weight = 1.0) + void fillJetAreaSubHistograms(TJets const& jet, float rho, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); - if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) + if (doEventWeighted && (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin)) return; double jetcorrpt = jet.pt() - (rho * jet.area()); if (jet.r() == round(selectedJetsRadius * 100.0f)) { @@ -578,24 +535,23 @@ struct ChargedJetHadron { registry.fill(HIST("h_jet_ntracks_rhoareasubtracted"), jet.tracksIds().size(), weight); } } + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_track_pt_rhoareasubtracted"), jetcorrpt, constituent.pt(), weight); + } } template void fillParticleHistograms(const TParticles& particle, float weight = 1.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); - if (particle.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) - return; registry.fill(HIST("h2_particle_eta_phi"), particle.eta(), particle.phi(), weight); registry.fill(HIST("h2_particle_eta_pt"), particle.eta(), particle.pt(), weight); registry.fill(HIST("h2_particle_phi_pt"), particle.phi(), particle.pt(), weight); } template - void fillMCPHistograms(TJets const& jet, float weight = 1.0) + void fillMCPJetHistograms(TJets const& jet, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); - if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) + if (doEventWeighted && (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin)) return; if (jet.r() == round(selectedJetsRadius * 100.0f)) { registry.fill(HIST("h_jet_pt_part"), jet.pt(), weight); @@ -604,16 +560,18 @@ struct ChargedJetHadron { registry.fill(HIST("h_jet_area_part"), jet.area(), weight); registry.fill(HIST("h_jet_ntracks_part"), jet.tracksIds().size(), weight); } + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_part_track_pt_part"), jet.pt(), constituent.pt(), weight); + } } template - void fillMCPAreaSubHistograms(TJets const& jet, float rho = 0.0, float weight = 1.0) + void fillMCPJetAreaSubHistograms(TJets const& jet, float rho = 0.0, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); - if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) + if (doEventWeighted && (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin)) return; + double jetcorrpt = jet.pt() - (rho * jet.area()); if (jet.r() == round(selectedJetsRadius * 100.0f)) { - double jetcorrpt = jet.pt() - (rho * jet.area()); registry.fill(HIST("h_jet_pt_part_rhoareasubtracted"), jetcorrpt, weight); if (jetcorrpt > 0) { registry.fill(HIST("h_jet_eta_part_rhoareasubtracted"), jet.eta(), weight); @@ -622,15 +580,17 @@ struct ChargedJetHadron { registry.fill(HIST("h_jet_ntracks_part_rhoareasubtracted"), jet.tracksIds().size(), weight); } } + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_part_track_pt_part_rhoareasubtracted"), jetcorrpt, constituent.pt(), weight); + } } // ========================================================== //..........jet - hadron correlations........................ // ========================================================== template - void fillJetHadronHistograms(const TCollision& collision, const TJets& jets, const TTracks& tracks, float weight = 1.0) + void fillJetHadronHistograms(const TCollision& collision, const TJets& jets, const TTracks& tracks, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); using TracksTable = std::decay_t; for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -638,20 +598,20 @@ struct ChargedJetHadron { } if (!isAcceptedJet(jet)) continue; - registry.fill(HIST("h_jeth_event_stats"), 1); - if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) + registry.fill(HIST("h_jeth_event_stats"), 1, weight); + if (doEventWeighted && (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin)) continue; - registry.fill(HIST("h_jeth_event_stats"), 2); + registry.fill(HIST("h_jeth_event_stats"), 2, weight); double ptCorr = jet.pt() - jet.area() * collision.rho(); if (ptCorr < subleadingjetptMin) continue; - registry.fill(HIST("h_trigjet_corrpt"), ptCorr); - registry.fill(HIST("h_jeth_event_stats"), 3); + registry.fill(HIST("h_trigjet_corrpt"), ptCorr, weight); + registry.fill(HIST("h_jeth_event_stats"), 3, weight); for (auto const& track : tracks) { - registry.fill(HIST("h_jeth_event_stats"), 4); + registry.fill(HIST("h_jeth_event_stats"), 4, weight); if (!jetderiveddatautilities::selectTrack(track, trackSelection)) continue; - registry.fill(HIST("h_jeth_event_stats"), 5); + registry.fill(HIST("h_jeth_event_stats"), 5, weight); double deta = track.eta() - jet.eta(); double dphi = track.phi() - jet.phi(); dphi = RecoDecay::constrainAngle(dphi, -PIHalf); @@ -663,16 +623,16 @@ struct ChargedJetHadron { //.......mixed events................................. template - void fillMixJetHadronHistograms(const TCollisions& collisions, const TJets& jets, const TTracks& tracks, float weight = 1.0) + void fillMixJetHadronHistograms(const TCollisions& collisions, const TJets& jets, const TTracks& tracks) { using TracksTable = std::decay_t; auto tracksTuple = std::make_tuple(jets, tracks); Pair pairData{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; //// -1 is the number of the bin to skip for (const auto& [c1, jets1, c2, tracks2] : pairData) { - weight = doEventWeighted ? c1.weight() : 1.f; - const float pTHat = 10.f / std::pow(weight, 1.f / pTHatExponent); - registry.fill(HIST("h_mixjeth_event_stats"), 1); + const float weight = doEventWeighted ? c1.weight() : 1.f; + const float pTHat = doEventWeighted ? 10.f / std::pow(weight, 1.f / pTHatExponent) : 999.f; + registry.fill(HIST("h_mixjeth_event_stats"), 1, weight); if (!isGoodCollision(c1) || !isGoodCollision(c2)) continue; for (auto const& jet : jets1) { @@ -681,19 +641,19 @@ struct ChargedJetHadron { } if (!isAcceptedJet(jet)) continue; // for pp Reason: Trying to dereference index with a wrong type in tracks_as for base target "JTracks" - if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) + if (doEventWeighted && (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin)) continue; - registry.fill(HIST("h_mixjeth_event_stats"), 2); + registry.fill(HIST("h_mixjeth_event_stats"), 2, weight); double ptCorr = jet.pt() - jet.area() * c1.rho(); if (ptCorr < subleadingjetptMin) continue; - registry.fill(HIST("h_mixtrigjet_corrpt"), ptCorr); - registry.fill(HIST("h_mixjeth_event_stats"), 3); + registry.fill(HIST("h_mixtrigjet_corrpt"), ptCorr, weight); + registry.fill(HIST("h_mixjeth_event_stats"), 3, weight); for (auto const& track : tracks2) { - registry.fill(HIST("h_mixjeth_event_stats"), 4); + registry.fill(HIST("h_mixjeth_event_stats"), 4, weight); if (!jetderiveddatautilities::selectTrack(track, trackSelection)) continue; - registry.fill(HIST("h_mixjeth_event_stats"), 5); + registry.fill(HIST("h_mixjeth_event_stats"), 5, weight); double deta = track.eta() - jet.eta(); double dphi = track.phi() - jet.phi(); dphi = RecoDecay::constrainAngle(dphi, -PIHalf); @@ -706,9 +666,8 @@ struct ChargedJetHadron { //........MCP..jet - hadron correlations.......................................... template - void fillMCPJetHadronHistograms(const TmcCollision& mccollision, const TJets& jets, const TParticles& particles, float weight = 1.0) + void fillMCPJetHadronHistograms(const TmcCollision& mccollision, const TJets& jets, const TParticles& particles, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); using ParticlesTable = std::decay_t; for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -717,16 +676,17 @@ struct ChargedJetHadron { if (!isAcceptedJet(jet, true)) { continue; } - if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) - return; - registry.fill(HIST("h_jeth_event_stats_part"), 2); + registry.fill(HIST("h_jeth_event_stats_part"), 1, weight); + if (doEventWeighted && (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin)) + continue; + registry.fill(HIST("h_jeth_event_stats_part"), 2, weight); double ptCorr = jet.pt() - jet.area() * mccollision.rho(); if (ptCorr < subleadingjetptMin) continue; - registry.fill(HIST("h_trigjet_corrpt_part"), ptCorr); - registry.fill(HIST("h_jeth_event_stats_part"), 3); + registry.fill(HIST("h_trigjet_corrpt_part"), ptCorr, weight); + registry.fill(HIST("h_jeth_event_stats_part"), 3, weight); for (auto const& particle : particles) { - registry.fill(HIST("h_jeth_event_stats_part"), 4); + registry.fill(HIST("h_jeth_event_stats_part"), 4, weight); double deta = particle.eta() - jet.eta(); double dphi = particle.phi() - jet.phi(); dphi = RecoDecay::constrainAngle(dphi, -PIHalf); @@ -737,16 +697,16 @@ struct ChargedJetHadron { } //......mixed events...................... template - void fillMCPMixJetHadronHistograms(const TmcCollisions& mccollisions, const TCollisions& collisions, const TJets& jets, const TParticles& particles, float weight = 1.0) + void fillMCPMixJetHadronHistograms(const TmcCollisions& mccollisions, const TCollisions& collisions, const TJets& jets, const TParticles& particles) { using ParticlesTable = std::decay_t; auto particlesTuple = std::make_tuple(jets, particles); Pair pairMCData{corrBinningMC, numberEventsMixed, -1, mccollisions, particlesTuple, &cache}; for (const auto& [c1, jets1, c2, particles2] : pairMCData) { - weight = doEventWeighted ? c1.weight() : 1.f; - const float pTHat = 10.f / std::pow(weight, 1.f / pTHatExponent); - registry.fill(HIST("h_mixjeth_event_stats_part"), 1); + const float weight = doEventWeighted ? c1.weight() : 1.f; + const float pTHat = doEventWeighted ? 10.f / std::pow(weight, 1.f / pTHatExponent) : 999.f; + registry.fill(HIST("h_mixjeth_event_stats_part"), 1, weight); if (!applyMCCollisionCuts(c1, collisions) || !applyMCCollisionCuts(c2, collisions)) continue; @@ -755,18 +715,18 @@ struct ChargedJetHadron { continue; if (!isAcceptedJet(jet, true)) continue; - if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) - return; - registry.fill(HIST("h_mixjeth_event_stats_part"), 2); + if (doEventWeighted && (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin)) + continue; + registry.fill(HIST("h_mixjeth_event_stats_part"), 2, weight); double ptCorr = jet.pt() - jet.area() * c1.rho(); if (ptCorr < subleadingjetptMin) continue; - registry.fill(HIST("h_mixtrigjet_corrpt_part"), ptCorr); - registry.fill(HIST("h_mixjeth_event_stats_part"), 3); + registry.fill(HIST("h_mixtrigjet_corrpt_part"), ptCorr, weight); + registry.fill(HIST("h_mixjeth_event_stats_part"), 3, weight); for (auto const& particle : particles2) { - registry.fill(HIST("h_mixjeth_event_stats_part"), 4); + registry.fill(HIST("h_mixjeth_event_stats_part"), 4, weight); double deta = particle.eta() - jet.eta(); double dphi = particle.phi() - jet.phi(); dphi = RecoDecay::constrainAngle(dphi, -PIHalf); @@ -781,17 +741,18 @@ struct ChargedJetHadron { //..........leading jet - hadron correlations................ // ========================================================== template - void fillLeadingJetHadronHistograms(const TCollision& collision, const TJets& jets, const TTracks& tracks, float weight = 1.0) + void fillLeadingJetHadronHistograms(const TCollision& collision, const TJets& jets, const TTracks& tracks, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); using TracksTable = std::decay_t; - registry.fill(HIST("h_centrality"), getCentrality(collision)); + registry.fill(HIST("h_centrality"), getCentrality(collision), weight); typename TJets::iterator leadingJet; typename TJets::iterator subleadingJet; bool hasLeading = false; bool hasSubleading = false; - double ptLeadingCorr = -1.0; - double ptSubleadingCorr = -1.0; + double ptLeadingCorr = -999.0; + double ptSubleadingCorr = -999.0; + float leadingJetLeadingConstPt = 0.0; + float subleadingJetLeadingConstPt = 0.0; for (auto it = jets.begin(); it != jets.end(); ++it) { const auto& jet = *it; @@ -799,7 +760,7 @@ struct ChargedJetHadron { continue; if (!isAcceptedJet(jet)) continue; - if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) + if (doEventWeighted && (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin)) continue; double ptCorr = jet.pt() - jet.area() * collision.rho(); registry.fill(HIST("h_inclusivejet_corrpt"), ptCorr, weight); @@ -832,7 +793,7 @@ struct ChargedJetHadron { double etaJet2Raw = subleadingJet.eta(); double multEta1Eta2 = etaJet1Raw * etaJet2Raw; double deltaEtaJetsNoflip = etaJet1Raw - etaJet2Raw; - double inverse = (etaJet1Raw > 0) ? 1.0 : -1.0; // Dr.Yang suggestion + double inverse = (etaJet1Raw > 0) ? 1.0 : -1.0; // eta-side convention double flip = (etaJet1Raw > etaJet2Raw) ? 1.0 : -1.0; double etajet1 = flip * etaJet1Raw; // leading jet eta after flip double etajet2 = flip * etaJet2Raw; // subleading jet eta after flip @@ -856,11 +817,24 @@ struct ChargedJetHadron { else if (doDijetEta && multEta1Eta2 < 0) registry.fill(HIST("h2_dijet_TimeEtaLess0_pt"), ptLeadingCorr, ptSubleadingCorr, weight); + for (const auto& constituent : leadingJet.template tracks_as()) { + registry.fill(HIST("h_leadjet_constituent_pt"), constituent.pt(), weight); + if (constituent.pt() > leadingJetLeadingConstPt) + leadingJetLeadingConstPt = constituent.pt(); + } + for (const auto& constituent : subleadingJet.template tracks_as()) { + registry.fill(HIST("h_subleadjet_constituent_pt"), constituent.pt(), weight); + if (constituent.pt() > subleadingJetLeadingConstPt) + subleadingJetLeadingConstPt = constituent.pt(); + } + registry.fill(HIST("h_leadjet_leadingconstituent_pt"), leadingJetLeadingConstPt, weight); + registry.fill(HIST("h_subleadjet_leadingconstituent_pt"), subleadingJetLeadingConstPt, weight); + for (auto const& track : tracks) { if (!jetderiveddatautilities::selectTrack(track, trackSelection)) continue; double hpt = track.pt(); - double heta = inverse * (track.eta()); // Dr.Yang + double heta = inverse * (track.eta()); // eta-side convention double detatot = track.eta() - etaJet1Raw; double deta = flip * (track.eta() - etaJet1Raw); // always relative to leadingJet (after flip) double dphi = track.phi() - leadingJet.phi(); @@ -873,7 +847,7 @@ struct ChargedJetHadron { // registry.fill(HIST("thn_ljeth_correlations"), ptLeadingCorr, ptSubleadingCorr, multEta1Eta2, deltaEtaJetsNoflip, weight); if (doDijetEta) { registry.fill(HIST("thn_ljeth_correlations"), ptLeadingCorr, ptSubleadingCorr, hpt, multEta1Eta2, heta, dphi, weight); - if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEta1Eta2 > 0) + if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEta1Eta2 < 0) registry.fill(HIST("h2_jeth_heta_dphi"), heta, dphi, weight); } else { registry.fill(HIST("thn_ljeth_correlations"), ptLeadingCorr, ptSubleadingCorr, hpt, deltaEtaJets, deta, dphi, weight); @@ -889,138 +863,17 @@ struct ChargedJetHadron { } } - //.......mixed events leadingjet-hadrons......................... - template - void fillMixLeadingJetHadronHistograms(const TCollisions& collisions, const TJets& jets, const TTracks& tracks, float weight = 1.0) - { - using TracksTable = std::decay_t; - auto tracksTuple = std::make_tuple(jets, tracks); - Pair pairData{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, jets1, c2, tracks2] : pairData) { - weight = doEventWeighted ? c1.weight() : 1.f; - const float pTHat = 10.f / std::pow(weight, 1.f / pTHatExponent); - registry.fill(HIST("h_mix_event_stats"), 1); - // int poolBin = corrBinning.getBin(std::make_tuple(c2.posZ(), getCentrality(c2))); - if (!isGoodCollision(c1) || !isGoodCollision(c2)) - continue; - - typename TJets::iterator leadingJet; - typename TJets::iterator subleadingJet; - bool hasLeading = false; - bool hasSubleading = false; - double ptLeadingCorr = -1.0; - double ptSubleadingCorr = -1.0; - - for (auto it = jets1.begin(); it != jets1.end(); ++it) { - const auto& jet = *it; - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) - continue; - if (!isAcceptedJet(jet)) - continue; - if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) - continue; - - double ptCorr = jet.pt() - jet.area() * c1.rho(); - if (ptCorr > ptLeadingCorr) { - if (hasLeading) { - subleadingJet = leadingJet; - ptSubleadingCorr = ptLeadingCorr; - hasSubleading = true; - } - leadingJet = it; - ptLeadingCorr = ptCorr; - hasLeading = true; - } else if (ptCorr > ptSubleadingCorr) { - subleadingJet = it; - ptSubleadingCorr = ptCorr; - hasSubleading = true; - } - } - if (!hasLeading || !hasSubleading) - continue; - - double phiLead = leadingJet.phi(); - double phiSub = subleadingJet.phi(); - double deltaPhiJets = phiLead - phiSub; - deltaPhiJets = RecoDecay::constrainAngle(deltaPhiJets, -PIHalf); - if (std::abs(deltaPhiJets) < dijetDphiCut * PI) - continue; - - registry.fill(HIST("h_mix_event_stats"), 2); - double etaJet1Raw = leadingJet.eta(); - double etaJet2Raw = subleadingJet.eta(); - double multEta1Eta2 = etaJet1Raw * etaJet2Raw; - double flip = (etaJet1Raw > etaJet2Raw) ? 1.0 : -1.0; - double inverse = (etaJet1Raw > 0) ? 1.0 : -1.0; // Dr.Yang suggestion - double etajet1 = flip * etaJet1Raw; - double etajet2 = flip * etaJet2Raw; - double deltaEtaJetsNoflip = etaJet1Raw - etaJet2Raw; - double deltaEtaJets = etajet1 - etajet2; - registry.fill(HIST("h_mixleadjet_corrpt"), ptLeadingCorr, weight); - registry.fill(HIST("h_mixsubleadjet_corrpt"), ptSubleadingCorr, weight); - - if (ptLeadingCorr < leadingjetptMin || ptSubleadingCorr < subleadingjetptMin) - continue; - registry.fill(HIST("h_mix_event_stats"), 3); - registry.fill(HIST("h_mixdijet_pair_counts_cut"), 2); - registry.fill(HIST("h_mixleadjet_eta"), etaJet1Raw, weight); - registry.fill(HIST("h_mixsubleadjet_eta"), etaJet2Raw, weight); - registry.fill(HIST("h2_mixdijet_detanoflip_dphi"), deltaEtaJetsNoflip, deltaPhiJets, weight); - registry.fill(HIST("h2_mixdijet_Asymmetry"), ptSubleadingCorr, ptSubleadingCorr / ptLeadingCorr, weight); - registry.fill(HIST("h3_mixdijet_deta_pt"), deltaEtaJets, ptLeadingCorr, ptSubleadingCorr, weight); - if (doDijetEta && multEta1Eta2 > 0) - registry.fill(HIST("h2_mixdijet_TimeEtaThan0_pt"), ptLeadingCorr, ptSubleadingCorr, weight); - else if (doDijetEta && multEta1Eta2 < 0) - registry.fill(HIST("h2_mixdijet_TimeEtaLess0_pt"), ptLeadingCorr, ptSubleadingCorr, weight); - - for (auto const& track : tracks2) { - registry.fill(HIST("h_mix_event_stats"), 4); - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) - continue; - registry.fill(HIST("h_mix_event_stats"), 5); - double hpt = track.pt(); - double heta = inverse * (track.eta()); // Dr.Yang - double detatot = track.eta() - etaJet1Raw; - double deta = flip * (track.eta() - etajet1); - double dphi = track.phi() - phiLead; - dphi = RecoDecay::constrainAngle(dphi, -PIHalf); - - if (hpt > assoHadronPtMaxCut) - continue; - registry.fill(HIST("h_mixjeth_detatot"), detatot, weight); - registry.fill(HIST("h_mixjeth_deta"), deta, weight); - registry.fill(HIST("h_mixjeth_dphi"), dphi, weight); - if (doDijetEta) { - registry.fill(HIST("thn_mixljeth_correlations"), ptLeadingCorr, ptSubleadingCorr, hpt, multEta1Eta2, heta, dphi, weight); - if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEta1Eta2 > 0) - registry.fill(HIST("h2_mixjeth_heta_dphi"), heta, dphi, weight); - } else { - registry.fill(HIST("thn_mixljeth_correlations"), ptLeadingCorr, ptSubleadingCorr, hpt, deltaEtaJets, deta, dphi, weight); - if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax) { - if (std::abs(deltaEtaJets) >= etaGapup) - registry.fill(HIST("h2_mixjeth_physicalcutsup_deta_dphi"), deta, dphi, weight); - if (std::abs(deltaEtaJets) >= etaGapdw && std::abs(deltaEtaJets) < etaGapup) - registry.fill(HIST("h2_mixjeth_physicalcutsmd_deta_dphi"), deta, dphi, weight); - if (std::abs(deltaEtaJets) < etaGapdw) - registry.fill(HIST("h2_mixjeth_physicalcutsdw_deta_dphi"), deta, dphi, weight); - } - } - } - } - } - //........MCP..leading jet - hadron correlations..................... template - void fillMCPLeadingJetHadronHistograms(const TmcCollision& mccollision, const TJets& jets, const TParticles& particles, float weight = 1.0) + void fillMCPLeadingJetHadronHistograms(const TmcCollision& mccollision, const TJets& jets, const TParticles& particles, float weight = 1.0, float pTHat = 999.0) { - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); using ParticlesTable = std::decay_t; typename TJets::iterator leadingJet; typename TJets::iterator subleadingJet; bool hasLeading = false; bool hasSubleading = false; - double ptLeadingCorr = -1.0; - double ptSubleadingCorr = -1.0; + double ptLeadingCorr = -999.0; + double ptSubleadingCorr = -999.0; for (auto it = jets.begin(); it != jets.end(); ++it) { const auto& jet = *it; @@ -1028,10 +881,11 @@ struct ChargedJetHadron { continue; if (!isAcceptedJet(jet, true)) continue; - if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) - return; + if (doEventWeighted && (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin)) + continue; double ptCorr = jet.pt() - jet.area() * mccollision.rho(); + registry.fill(HIST("h_inclusivejet_corrpt_part"), ptCorr, weight); if (ptCorr > ptLeadingCorr) { if (hasLeading) { subleadingJet = leadingJet; @@ -1057,7 +911,7 @@ struct ChargedJetHadron { double etaJet2Raw = subleadingJet.eta(); double multEta1Eta2 = etaJet1Raw * etaJet2Raw; double deltaEtaJetsNoflip = etaJet1Raw - etaJet2Raw; - double inverse = (etaJet1Raw > 0) ? 1.0 : -1.0; // Dr.Yang suggestion + double inverse = (etaJet1Raw > 0) ? 1.0 : -1.0; // eta-side convention double flip = (etaJet1Raw > etaJet2Raw) ? 1.0 : -1.0; double etajet1 = flip * etaJet1Raw; // leading jet eta after flip double etajet2 = flip * etaJet2Raw; // subleading jet eta after flip @@ -1083,7 +937,7 @@ struct ChargedJetHadron { for (auto const& particle : particles) { double hpt = particle.pt(); - double heta = inverse * particle.eta(); // Dr.Yang + double heta = inverse * particle.eta(); // eta-side convention double detatot = particle.eta() - etaJet1Raw; double deta = flip * (particle.eta() - etaJet1Raw); // always relative to leadingJet (after flip) double dphi = particle.phi() - leadingJet.phi(); @@ -1095,7 +949,7 @@ struct ChargedJetHadron { registry.fill(HIST("h_jeth_dphi_part"), dphi, weight); if (doDijetEta) { registry.fill(HIST("thn_ljeth_correlations_part"), ptLeadingCorr, ptSubleadingCorr, hpt, multEta1Eta2, heta, dphi, weight); - if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEta1Eta2 > 0) + if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEta1Eta2 < 0) registry.fill(HIST("h2_jeth_heta_dphi_part"), heta, dphi, weight); } else { registry.fill(HIST("thn_ljeth_correlations_part"), ptLeadingCorr, ptSubleadingCorr, hpt, deltaEtaJets, deta, dphi, weight); @@ -1111,125 +965,195 @@ struct ChargedJetHadron { } } - //..........MCP..mixed events......................................... - template - void fillMCPMixLeadingJetHadronHistograms(const TmcCollisions& mccollisions, const TCollisions& collisions, const TJets& jets, const TParticles& particles, float weight = 1.0) + // ========================================================== + //..........hadron-triggered recoil-jet - hadron correlations. + // ========================================================== + template + void fillHadronJetHadronHistograms(const TCollision& collision, const TJets& jets, const TTracks& tracks, float weight = 1.0, float pTHat = 999.0) { - using ParticlesTable = std::decay_t; - auto particlesTuple = std::make_tuple(jets, particles); - Pair pairMCData{corrBinningMC, numberEventsMixed, -1, mccollisions, particlesTuple, &cache}; - - for (const auto& [c1, jets1, c2, particles2] : pairMCData) { - weight = doEventWeighted ? c1.weight() : 1.f; - const float pTHat = 10.f / std::pow(weight, 1.f / pTHatExponent); - registry.fill(HIST("h_mixevent_stats_part"), 1); - // int poolBin = corrBinningMC.getBin(std::make_tuple(c2.posZ(), getMultiplicity(c2))); - if (!applyMCCollisionCuts(c1, collisions) || !applyMCCollisionCuts(c2, collisions)) + using TracksTable = std::decay_t; + // ----- Step 1: find leading trigger track, not leading jet ----- + typename TTracks::iterator triggerTrack; + bool hasTriggerTrack = false; + double ptTrig = -1.0; + for (auto it = tracks.begin(); it != tracks.end(); ++it) { + const auto& track = *it; + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + continue; + if (track.pt() < triggerHadronPtMin) continue; + if (track.pt() > ptTrig) { + triggerTrack = it; + ptTrig = track.pt(); + hasTriggerTrack = true; + } + } + if (!hasTriggerTrack) + return; + double etaTrigRaw = triggerTrack.eta(); + double phiTrig = triggerTrack.phi(); - typename TJets::iterator leadingJet; - typename TJets::iterator subleadingJet; - bool hasLeading = false; - bool hasSubleading = false; - double ptLeadingCorr = -1.0; - double ptSubleadingCorr = -1.0; + // ----- Step 2: find away-side recoil jet relative to trigger track ----- + typename TJets::iterator recoilJet; + bool hasRecoilJet = false; + double ptRecoilCorr = -999.0; + for (auto it = jets.begin(); it != jets.end(); ++it) { + const auto& jet = *it; + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) + continue; + if (!isAcceptedJet(jet)) + continue; + if (doEventWeighted && (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin)) + continue; + double dphiHJ = phiTrig - jet.phi(); + dphiHJ = RecoDecay::constrainAngle(dphiHJ, -PIHalf); + if (std::abs(dphiHJ) < dijetDphiCut * PI) + continue; + double ptCorr = jet.pt() - jet.area() * collision.rho(); + if (ptCorr > ptRecoilCorr) { + recoilJet = it; + ptRecoilCorr = ptCorr; + hasRecoilJet = true; + } + } + if (!hasRecoilJet) + return; + if (ptRecoilCorr < subleadingjetptMin) + return; - for (auto it = jets1.begin(); it != jets1.end(); ++it) { - const auto& jet = *it; - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) - continue; - if (!isAcceptedJet(jet, true)) - continue; - if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) - return; + double etaRecoilRaw = recoilJet.eta(); + double phiRecoil = recoilJet.phi(); + double multEtaHJet = etaTrigRaw * etaRecoilRaw; + double inverse = (etaTrigRaw > 0) ? 1.0 : -1.0; + double deltaPhiHJet = phiTrig - phiRecoil; + deltaPhiHJet = RecoDecay::constrainAngle(deltaPhiHJet, -PIHalf); + registry.fill(HIST("h_hjet_trigtrack_pt"), ptTrig, weight); + registry.fill(HIST("h_hjet_trigtrack_eta"), etaTrigRaw, weight); + registry.fill(HIST("h_hjet_trigtrack_phi"), phiTrig, weight); + registry.fill(HIST("h_hjet_recoiljet_corrpt"), ptRecoilCorr, weight); + registry.fill(HIST("h_hjet_recoiljet_eta"), etaRecoilRaw, weight); + registry.fill(HIST("h_hjet_recoiljet_phi"), phiRecoil, weight); + registry.fill(HIST("h_hjet_dphi"), deltaPhiHJet, weight); + for (const auto& constituent : recoilJet.template tracks_as()) { + registry.fill(HIST("h_recojet_constituent_pt"), constituent.pt(), weight); + } + if (multEtaHJet > 0) + registry.fill(HIST("h2_hjet_TimeEtaThan0_pt"), ptTrig, ptRecoilCorr, weight); + else if (multEtaHJet < 0) + registry.fill(HIST("h2_hjet_TimeEtaLess0_pt"), ptTrig, ptRecoilCorr, weight); - double ptCorr = jet.pt() - jet.area() * c1.rho(); - if (ptCorr > ptLeadingCorr) { - if (hasLeading) { - subleadingJet = leadingJet; - ptSubleadingCorr = ptLeadingCorr; - hasSubleading = true; - } - leadingJet = it; - ptLeadingCorr = ptCorr; - hasLeading = true; - } else if (ptCorr > ptSubleadingCorr) { - subleadingJet = it; - ptSubleadingCorr = ptCorr; - hasSubleading = true; - } - } - if (!hasLeading || !hasSubleading) + // ----- Step 3: associated hadrons relative to trigger-track axis ----- + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + continue; + // remove trigger particle itself from associated-hadron loop + if (track.globalIndex() == triggerTrack.globalIndex()) continue; + double hpt = track.pt(); + if (hpt > assoHadronPtMaxCut) + continue; + double heta = inverse * track.eta(); + double dphi = track.phi() - phiTrig; + dphi = RecoDecay::constrainAngle(dphi, -PIHalf); + if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEtaHJet < 0) + registry.fill(HIST("h2_hjet_heta_dphi"), heta, dphi, weight); + registry.fill(HIST("thn_hjeth_correlations"), ptTrig, ptRecoilCorr, hpt, multEtaHJet, heta, dphi, weight); + } + } - double deltaPhiJets = leadingJet.phi() - subleadingJet.phi(); - deltaPhiJets = RecoDecay::constrainAngle(deltaPhiJets, -PIHalf); - if (std::abs(deltaPhiJets) < dijetDphiCut * PI) + // ========================================================== + //.............di-hadron - hadron correlations............... + // ========================================================== + template + void fillDiHadronHistograms(const TCollision&, const TTracks& tracks, float weight = 1.0, float pTHat = 999.0) + { + if (doEventWeighted && (pTHat < pTHatAbsoluteMin)) + return; + // ----- Step 1: find the leading trigger hadron ----- + typename TTracks::iterator leadingHadron; + typename TTracks::iterator subleadingHadron; + double deltaPhiTriggers = 0.0; + bool hasLeadingHadron = false; + bool hasSubleadingHadron = false; + double ptLeadingHadron = -1.0; + double ptSubleadingHadron = -1.0; + + for (auto it = tracks.begin(); it != tracks.end(); ++it) { + const auto& track = *it; + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) continue; - registry.fill(HIST("h_mixevent_stats_part"), 2); - - double etaJet1Raw = leadingJet.eta(); - double etaJet2Raw = subleadingJet.eta(); - double multEta1Eta2 = etaJet1Raw * etaJet2Raw; - double deltaEtaJetsNoflip = etaJet1Raw - etaJet2Raw; - double inverse = (etaJet1Raw > 0) ? 1.0 : -1.0; // Dr.Yang suggestion - double flip = (etaJet1Raw > etaJet2Raw) ? 1.0 : -1.0; - double etajet1 = flip * etaJet1Raw; - double etajet2 = flip * etaJet2Raw; - double deltaEtaJets = etajet1 - etajet2; - registry.fill(HIST("h_mixleadjet_corrpt_part"), ptLeadingCorr, weight); - registry.fill(HIST("h_mixsubleadjet_corrpt_part"), ptSubleadingCorr, weight); - - if (ptLeadingCorr < leadingjetptMin || ptSubleadingCorr < subleadingjetptMin) - return; - registry.fill(HIST("h_mixevent_stats_part"), 3); - registry.fill(HIST("h_mixdijet_pair_counts_cut_part"), 1); - registry.fill(HIST("h_mixleadjet_eta_part"), etaJet1Raw, weight); - registry.fill(HIST("h_mixsubleadjet_eta_part"), etaJet2Raw, weight); - registry.fill(HIST("h2_mixdijet_detanoflip_dphi_part"), deltaEtaJetsNoflip, deltaPhiJets, weight); - registry.fill(HIST("h2_mixdijet_Asymmetry_part"), ptSubleadingCorr, ptSubleadingCorr / ptLeadingCorr, weight); - registry.fill(HIST("h3_mixdijet_deta_pt_part"), deltaEtaJets, ptLeadingCorr, ptSubleadingCorr, weight); - if (doDijetEta && multEta1Eta2 > 0) - registry.fill(HIST("h2_mixdijet_TimeEtaThan0_pt_part"), ptLeadingCorr, ptSubleadingCorr, weight); - else if (doDijetEta && multEta1Eta2 < 0) - registry.fill(HIST("h2_mixdijet_TimeEtaLess0_pt_part"), ptLeadingCorr, ptSubleadingCorr, weight); - - for (auto const& particle : particles2) { - registry.fill(HIST("h_mixevent_stats_part"), 4); - double hpt = particle.pt(); - double heta = inverse * particle.eta(); // Dr.Yang - double detatot = particle.eta() - etaJet1Raw; - double deta = flip * (particle.eta() - etaJet1Raw); - double dphi = particle.phi() - leadingJet.phi(); - dphi = RecoDecay::constrainAngle(dphi, -PIHalf); + if (track.pt() < triggerHadronPtMin) + continue; + if (track.pt() > ptLeadingHadron) { + leadingHadron = it; + ptLeadingHadron = track.pt(); + hasLeadingHadron = true; + } + } + if (!hasLeadingHadron) + return; + const double etaLeadingRaw = leadingHadron.eta(); + const double phiLeading = leadingHadron.phi(); - if (hpt > assoHadronPtMaxCut) - continue; - registry.fill(HIST("h_mixevent_stats_part"), 5); - registry.fill(HIST("h_mixjeth_detatot_part"), detatot, weight); - registry.fill(HIST("h_mixjeth_deta_part"), deta, weight); - registry.fill(HIST("h_mixjeth_dphi_part"), dphi, weight); - if (doDijetEta) { - registry.fill(HIST("thn_mixljeth_correlations_part"), ptLeadingCorr, ptSubleadingCorr, hpt, multEta1Eta2, heta, dphi, weight); - if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEta1Eta2 > 0) - registry.fill(HIST("h2_mixjeth_heta_dphi_part"), heta, dphi, weight); - } else { - registry.fill(HIST("thn_mixljeth_correlations_part"), ptLeadingCorr, ptSubleadingCorr, hpt, deltaEtaJets, deta, dphi, weight); - if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax) { - if (std::abs(deltaEtaJets) >= etaGapup) - registry.fill(HIST("h2_mixjeth_physicalcutsup_deta_dphi_part"), deta, dphi, weight); - if (std::abs(deltaEtaJets) >= etaGapdw && std::abs(deltaEtaJets) < etaGapup) - registry.fill(HIST("h2_mixjeth_physicalcutsmd_deta_dphi_part"), deta, dphi, weight); - if (std::abs(deltaEtaJets) < etaGapdw) - registry.fill(HIST("h2_mixjeth_physicalcutsdw_deta_dphi_part"), deta, dphi, weight); - } - } + // ----- Step 2: find the highest-pT away-side recoil hadron ----- + for (auto it = tracks.begin(); it != tracks.end(); ++it) { + const auto& track = *it; + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + continue; + if (track.globalIndex() == leadingHadron.globalIndex()) + continue; + if (track.pt() < subleadingHadronPtMin) + continue; + double dphi = phiLeading - track.phi(); + dphi = RecoDecay::constrainAngle(dphi, -PIHalf); + if (std::abs(dphi) < dijetDphiCut * PI) + continue; + if (track.pt() > ptSubleadingHadron) { + subleadingHadron = it; + ptSubleadingHadron = track.pt(); + deltaPhiTriggers = dphi; + hasSubleadingHadron = true; } } + if (!hasSubleadingHadron) + return; + const double etaSubleadingRaw = subleadingHadron.eta(); + const double phiSubleading = subleadingHadron.phi(); + const double multEtaHadron = etaLeadingRaw * etaSubleadingRaw; + const double inverse = (etaLeadingRaw > 0.0) ? 1.0 : -1.0; + + registry.fill(HIST("h_dihadron_leadtrack_pt"), ptLeadingHadron, weight); + registry.fill(HIST("h_dihadron_leadtrack_eta"), etaLeadingRaw, weight); + registry.fill(HIST("h_dihadron_leadtrack_phi"), phiLeading, weight); + registry.fill(HIST("h_dihadron_subleadtrack_pt"), ptSubleadingHadron, weight); + registry.fill(HIST("h_dihadron_subleadtrack_eta"), etaSubleadingRaw, weight); + registry.fill(HIST("h_dihadron_subleadtrack_phi"), phiSubleading, weight); + registry.fill(HIST("h_dihadron_dphi"), std::abs(deltaPhiTriggers), weight); + if (multEtaHadron > 0.0) + registry.fill(HIST("h2_dihadron_TimeEtaThan0_pt"), ptLeadingHadron, ptSubleadingHadron, weight); + else if (multEtaHadron < 0.0) + registry.fill(HIST("h2_dihadron_TimeEtaLess0_pt"), ptLeadingHadron, ptSubleadingHadron, weight); + + // ----- Step 3: associated hadrons relative to the leading-hadron axis ----- + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + continue; + if (track.globalIndex() == leadingHadron.globalIndex() || track.globalIndex() == subleadingHadron.globalIndex()) + continue; + const double hpt = track.pt(); + if (hpt > assoHadronPtMaxCut) + continue; + const double heta = inverse * track.eta(); + double dphi = track.phi() - phiLeading; + dphi = RecoDecay::constrainAngle(dphi, -PIHalf); + if (hpt >= assoHadronPtMin && hpt < assoHadronPtMax && multEtaHadron < 0.0) + registry.fill(HIST("h2_dihadron_heta_dphi"), heta, dphi, weight); + registry.fill(HIST("thn_dihadron_correlations"), ptLeadingHadron, ptSubleadingHadron, hpt, multEtaHadron, heta, dphi, weight); + } } // ========================================================== - //.............process staring............................... + //.............process starting............................... // ========================================================== void processCollisionsQCData(aod::JetCollision const& collision, FilterJetTracks const& tracks) @@ -1284,17 +1208,26 @@ struct ChargedJetHadron { return; fillLeadingJetHadronHistograms(collision, jets, tracks); } - PROCESS_SWITCH(ChargedJetHadron, processLeadingJetHadron, "ame event subleading jet-h for Data", false); + PROCESS_SWITCH(ChargedJetHadron, processLeadingJetHadron, "same event leading jet-h for Data", false); - void processMixLeadingJetHadron(FilterCollisions const& collisions, - CorrChargedJets const& jets, - FilterJetTracks const& tracks) + void processHadronJetHadron(FilterCollision const& collision, + CorrChargedJets const& jets, + FilterJetTracks const& tracks) { - if (collisions.size() == 0) + if (!isGoodCollision(collision)) + return; + fillHadronJetHadronHistograms(collision, jets, tracks); + } + PROCESS_SWITCH(ChargedJetHadron, processHadronJetHadron, "same event hjet-hadron correlations for Data", false); + + void processDiHadron(FilterCollision const& collision, + FilterJetTracks const& tracks) + { + if (!isGoodCollision(collision)) return; - fillMixLeadingJetHadronHistograms(collisions, jets, tracks); + fillDiHadronHistograms(collision, tracks); } - PROCESS_SWITCH(ChargedJetHadron, processMixLeadingJetHadron, "mixed event leading jet-h for Data", false); + PROCESS_SWITCH(ChargedJetHadron, processDiHadron, "same event di-hadron correlations for Data", false); void processJetHadron(FilterCollision const& collision, CorrChargedJets const& jets, @@ -1316,37 +1249,6 @@ struct ChargedJetHadron { } PROCESS_SWITCH(ChargedJetHadron, processMixJetHadron, "mixed event jet-h for Data", false); - //...HF jet correlations.................... - void processHFJetCorrelation(FilterCollision const& collision, - CorrChargedJets const& jets, - aod::CandidatesD0Data const& candidates) - { - if (!isGoodCollision(collision)) - return; - for (const auto& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { - continue; - } - registry.fill(HIST("h_d0jet_pt"), jet.pt()); - registry.fill(HIST("h_d0jet_corrpt"), jet.pt() - collision.rho() * jet.area()); - registry.fill(HIST("h_d0jet_eta"), jet.eta()); - registry.fill(HIST("h_d0jet_phi"), jet.phi()); - } - for (const auto& candidate : candidates) { - registry.fill(HIST("h_d0_mass"), candidate.m()); - registry.fill(HIST("h_d0_pt"), candidate.pt()); - registry.fill(HIST("h_d0_eta"), candidate.eta()); - registry.fill(HIST("h_d0_phi"), candidate.phi()); - for (const auto& jet : jets) { - double deltaeta = candidate.eta() - jet.eta(); - double deltaphi = candidate.phi() - jet.phi(); - deltaphi = RecoDecay::constrainAngle(deltaphi, -PIHalf); - registry.fill(HIST("h2_d0jet_detadphi"), deltaeta, deltaphi); - } - } - } - PROCESS_SWITCH(ChargedJetHadron, processHFJetCorrelation, "D0-jet for Data", false); - //........MCD.................................................. void processCollisionsQCMCD(soa::Join::iterator const& collision, FilterJetTracks const& tracks) @@ -1372,7 +1274,13 @@ struct ChargedJetHadron { registry.fill(HIST("h_collisions_weighted"), 3.5, eventWeight); registry.fill(HIST("h2_centrality_occupancy"), getCentrality(collision), collision.trackOccupancyInTimeRange(), eventWeight); registry.fill(HIST("h_collisions_zvertex"), collision.posZ(), eventWeight); + registry.fill(HIST("h_collisions_multFT0"), getMultiplicity(collision), eventWeight); + const float pTHat = doEventWeighted ? collision.mcCollision().ptHard() : 999.f; + if (doEventWeighted) { + registry.fill(HIST("h_coll_phat"), pTHat); + registry.fill(HIST("h_coll_phat_weighted"), pTHat, eventWeight); + } for (auto const& track : tracks) { if (!jetderiveddatautilities::selectTrack(track, trackSelection)) continue; @@ -1381,11 +1289,12 @@ struct ChargedJetHadron { } PROCESS_SWITCH(ChargedJetHadron, processCollisionsQCMCD, "QC of collisions and tracks for MCD", false); - void processSpectraAreaSubMCD(FilterCollision const& collision, + void processSpectraAreaSubMCD(FilterMcdCollision const& collision, CorrChargedMCDJets const& jets, aod::JetTracks const&) { const float eventWeight = doEventWeighted ? collision.weight() : 1.f; + const float pTHat = doEventWeighted ? collision.mcCollision().ptHard() : 999.f; if (!isGoodCollision(collision)) return; for (auto const& jet : jets) { @@ -1395,46 +1304,60 @@ struct ChargedJetHadron { if (!isAcceptedJet(jet)) { continue; } - fillJetHistograms(jet, eventWeight); - fillJetAreaSubHistograms(jet, collision.rho(), eventWeight); + fillJetHistograms(jet, eventWeight, pTHat); + fillJetAreaSubHistograms(jet, collision.rho(), eventWeight, pTHat); } } PROCESS_SWITCH(ChargedJetHadron, processSpectraAreaSubMCD, "jet spectra with rho-area subtraction for MCD", false); - void processLeadingJetHadronMCD(FilterCollision const& collision, + void processLeadingJetHadronMCD(FilterMcdCollision const& collision, CorrChargedMCDJets const& jets, FilterJetTracks const& tracks) { const float eventWeight = doEventWeighted ? collision.weight() : 1.f; - if (!isGoodCollision(collision)) { + const float pTHat = doEventWeighted ? collision.mcCollision().ptHard() : 999.f; + if (!isGoodCollision(collision)) return; - } - fillLeadingJetHadronHistograms(collision, jets, tracks, eventWeight); + fillLeadingJetHadronHistograms(collision, jets, tracks, eventWeight, pTHat); } PROCESS_SWITCH(ChargedJetHadron, processLeadingJetHadronMCD, "same event leading jet-hadron correlations for MCD", false); - void processMixLeadingJetHadronMCD(FilterCollisions const& collisions, - CorrChargedMCDJets const& jets, - FilterJetTracks const& tracks) + void processHadronJetHadronMCD(FilterMcdCollision const& collision, + CorrChargedMCDJets const& jets, + FilterJetTracks const& tracks) { - if (collisions.size() == 0) + const float eventWeight = doEventWeighted ? collision.weight() : 1.f; + const float pTHat = doEventWeighted ? collision.mcCollision().ptHard() : 999.f; + if (!isGoodCollision(collision)) return; - fillMixLeadingJetHadronHistograms(collisions, jets, tracks); + fillHadronJetHadronHistograms(collision, jets, tracks, eventWeight, pTHat); } - PROCESS_SWITCH(ChargedJetHadron, processMixLeadingJetHadronMCD, "mixed event leading jet-hadron correlations for MCD", false); + PROCESS_SWITCH(ChargedJetHadron, processHadronJetHadronMCD, "same event hjet-hadron correlations for MCD", false); - void processJetHadronMCD(FilterCollision const& collision, + void processDiHadronMCD(FilterMcdCollision const& collision, + FilterJetTracks const& tracks) + { + const float eventWeight = doEventWeighted ? collision.weight() : 1.f; + const float pTHat = doEventWeighted ? collision.mcCollision().ptHard() : 999.f; + if (!isGoodCollision(collision)) + return; + fillDiHadronHistograms(collision, tracks, eventWeight, pTHat); + } + PROCESS_SWITCH(ChargedJetHadron, processDiHadronMCD, "same event di-hadron correlations for MCD", false); + + void processJetHadronMCD(FilterMcdCollision const& collision, CorrChargedMCDJets const& jets, FilterJetTracks const& tracks) { const float eventWeight = doEventWeighted ? collision.weight() : 1.f; + const float pTHat = doEventWeighted ? collision.mcCollision().ptHard() : 999.f; if (!isGoodCollision(collision)) return; - fillJetHadronHistograms(collision, jets, tracks, eventWeight); + fillJetHadronHistograms(collision, jets, tracks, eventWeight, pTHat); } PROCESS_SWITCH(ChargedJetHadron, processJetHadronMCD, "same event jet-hadron correlations for MCD", false); - void processMixJetHadronMCD(FilterCollisions const& collisions, + void processMixJetHadronMCD(FilterMcdCollisions const& collisions, CorrChargedMCDJets const& jets, FilterJetTracks const& tracks) { @@ -1483,13 +1406,14 @@ struct ChargedJetHadron { { bool mcLevelIsParticleLevel = true; const float eventWeight = doEventWeighted ? mccollision.weight() : 1.f; + const float pTHat = doEventWeighted ? mccollision.ptHard() : 999.f; if (!applyMCCollisionCuts(mccollision, collisions)) return; registry.fill(HIST("h_mcColl_rho"), mccollision.rho(), eventWeight); if (doEventWeighted) { - registry.fill(HIST("h_mcColl_counts_weight"), 1.5); - registry.fill(HIST("h_mcColl_counts_weight"), 2.5, mccollision.xsectGen()); - registry.fill(HIST("h_mcColl_counts_weight"), 3.5, eventWeight); + registry.fill(HIST("h_mcColl_counts_weight"), 1); + registry.fill(HIST("h_mcColl_counts_weight"), 2, mccollision.xsectGen()); + registry.fill(HIST("h_mcColl_counts_weight"), 3, eventWeight); } for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -1498,8 +1422,8 @@ struct ChargedJetHadron { if (!isAcceptedJet(jet, mcLevelIsParticleLevel)) { continue; } - fillMCPHistograms(jet, eventWeight); - fillMCPAreaSubHistograms(jet, mccollision.rho(), eventWeight); + fillMCPJetHistograms(jet, eventWeight, pTHat); + fillMCPJetAreaSubHistograms(jet, mccollision.rho(), eventWeight, pTHat); } } PROCESS_SWITCH(ChargedJetHadron, processSpectraAreaSubMCP, "jet spectra without and with UE subtraction of area-based for MCP", false); @@ -1510,34 +1434,25 @@ struct ChargedJetHadron { soa::Filtered const& particles) { const float eventWeight = doEventWeighted ? mccollision.weight() : 1.f; + const float pTHat = doEventWeighted ? mccollision.ptHard() : 999.f; if (!applyMCCollisionCuts(mccollision, collisions)) return; - fillMCPLeadingJetHadronHistograms(mccollision, jets, particles, eventWeight); + fillMCPLeadingJetHadronHistograms(mccollision, jets, particles, eventWeight, pTHat); } PROCESS_SWITCH(ChargedJetHadron, processLeadingJetHadronMCP, "same event leading jet-hadron for MCP", false); - void processMixLeadingJetHadronMCP(FilterMcpCollisions const& mccollisions, - soa::SmallGroups const& collisions, - CorrChargedMCPJets const& jets, - soa::Filtered const& particles) - { - if (mccollisions.size() < 1 || collisions.size() < 1) - return; - fillMCPMixLeadingJetHadronHistograms(mccollisions, collisions, jets, particles); - } - PROCESS_SWITCH(ChargedJetHadron, processMixLeadingJetHadronMCP, "mixed event leading jet-hadron for MCP", false); - void processJetHadronMCP(FilterMcpCollision const& mccollision, soa::SmallGroups const& collisions, CorrChargedMCPJets const& jets, soa::Filtered const& particles) { const float eventWeight = doEventWeighted ? mccollision.weight() : 1.f; + const float pTHat = doEventWeighted ? mccollision.ptHard() : 999.f; if (!applyMCCollisionCuts(mccollision, collisions)) return; - fillMCPJetHadronHistograms(mccollision, jets, particles, eventWeight); + fillMCPJetHadronHistograms(mccollision, jets, particles, eventWeight, pTHat); } PROCESS_SWITCH(ChargedJetHadron, processJetHadronMCP, "same event jet-hadron for MCP", false); diff --git a/PWGJE/Tasks/dijetFinderQA.cxx b/PWGJE/Tasks/dijetFinderQA.cxx index defb8a6c926..6ddbcf44304 100644 --- a/PWGJE/Tasks/dijetFinderQA.cxx +++ b/PWGJE/Tasks/dijetFinderQA.cxx @@ -96,8 +96,8 @@ struct DijetFinderQATask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Add histogram for event counts auto dijetMassTemp = 0.0; diff --git a/PWGJE/Tasks/embeddingQA.cxx b/PWGJE/Tasks/embeddingQA.cxx new file mode 100644 index 00000000000..96952f42055 --- /dev/null +++ b/PWGJE/Tasks/embeddingQA.cxx @@ -0,0 +1,186 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// jet tutorial task for hands on tutorial session (09/11/2023) +// +/// \author Nima Zardoshti +// + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +#include + +struct EmbeddingQATask { + HistogramRegistry registry{"registry", + {{"h_collision_zdiff", "collision z diff;z_{Data}-z_{MC} (cm);entries", {HistType::kTH1F, {{200, -10., 10.}}}}, + {"h_tracks_all_pt", "all tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_embedded_pt", "embedded tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_sub_pt", "subtracted tracks;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_particles_pt", "particles;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_pt", "jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_sub_pt", "subtracted jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_mcd_pt", "detector-level jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jets_mcp_pt", "particle-level jets;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jet_detector_response", "detector response matrix;#it{p}_{T,jet} particle (GeV/#it{c});#it{p}_{T,jet} detector (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_response", "response matrix;#it{p}_{T,jet} particle (GeV/#it{c});#it{p}_{T,jet} embedded subtracted (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_background_response", "background response matrix;#it{p}_{T,jet} detector (GeV/#it{c});#it{p}_{T,jet} embedded (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_jet_backgroundfluctuations_response", "background fluctuations response matrix;#it{p}_{T,jet} embedded subtracted (GeV/#it{c});#it{p}_{T,jet} embedded (GeV/#it{c})", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + {"h_tracks_data_pt", "tracks derived data;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcd_pt", "tracks derived detector level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcp_pt", "particles derived particle level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_data_original_pt", "tracks original data;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcd_original_pt", "tracks original detector level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_tracks_mcp_original_pt", "particles original particle level;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}}}; + + Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; + Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + + std::vector eventSelection; + int trackSelection = -1; + + void init(o2::framework::InitContext&) + { + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + } + + Preslice perBCCollisions = aod::collision::bcId; + + void processTracksOriginal(aod::Tracks const& tracks, aod::TracksFrom> const& tracksMCD, aod::McParticlesFrom> const& tracksMCP, aod::JetTracks const& tracksEmbedded, aod::JetParticles const& particles, aod::Collisions const& collisions, soa::Join>, aod::McCollisionLabelsFrom>> const& mcdCollisions, aod::McCollisionsFrom> const&, aod::BCs const& bcs, aod::BCsFrom> const&) + { + std::vector mcdCollisionGoodForEmbedding(mcdCollisions.size(), false); + for (auto const& mcdCollision : mcdCollisions) { + auto const& mcdCollisionBC = mcdCollision.bc_as>>(); + auto const& mcdGlobalBC = mcdCollisionBC.globalBC(); + for (auto const& bc : bcs) { + if (bc.globalBC() == mcdGlobalBC) { + auto const& collisionsPerBC = collisions.sliceBy(perBCCollisions, bc.globalIndex()); + auto const& mcCollision = mcdCollision.mcCollision_as>>(); + if (collisionsPerBC.size() > 0) { + mcdCollisionGoodForEmbedding[mcdCollision.globalIndex()] = true; + registry.fill(HIST("h_collision_zdiff"), collisionsPerBC.iteratorAt(0).posZ() - mcCollision.posZ()); + break; + } + } + } + } + + for (auto const& track : tracks) { + if (track.collisionId() >= 0) { + registry.fill(HIST("h_tracks_data_original_pt"), track.pt()); + } + } + for (auto const& track : tracksMCD) { + if (track.collisionId() >= 0) { + if (mcdCollisionGoodForEmbedding[track.collisionId()]) { + registry.fill(HIST("h_tracks_mcd_original_pt"), track.pt()); + } + } + } + for (auto const& track : tracksMCP) { + registry.fill(HIST("h_tracks_mcp_original_pt"), track.pt()); + } + for (auto const& track : tracksEmbedded) { + if (!(track.trackSel() & (1ULL << jetderiveddatautilities::JTrackSel::embeddedTrack))) { + registry.fill(HIST("h_tracks_data_pt"), track.pt()); + } + } + for (auto const& track : tracksEmbedded) { + if ((track.trackSel() & (1ULL << jetderiveddatautilities::JTrackSel::embeddedTrack))) { + registry.fill(HIST("h_tracks_mcd_pt"), track.pt()); + } + } + for (auto const& particle : particles) { + registry.fill(HIST("h_tracks_mcp_pt"), particle.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processTracksOriginal, "track QA", true); + + void processTracks(aod::JetCollision const&, aod::JetTracks const& tracks, aod::JetTracksSub const& subTracks) + { + for (auto const& track : tracks) { + if (jetderiveddatautilities::selectTrack(track, trackSelection)) { + registry.fill(HIST("h_tracks_all_pt"), track.pt()); + } + if (jetderiveddatautilities::selectTrack(track, trackSelection, true)) { + registry.fill(HIST("h_tracks_embedded_pt"), track.pt()); + } + } + for (auto const& track : subTracks) { + if (jetderiveddatautilities::selectTrack(track, trackSelection)) { + registry.fill(HIST("h_tracks_sub_pt"), track.pt()); + } + } + } + PROCESS_SWITCH(EmbeddingQATask, processTracks, "track QA", true); + + void processParticles(aod::JetMcCollision const&, aod::JetParticles const& particles) + { + for (auto const& particle : particles) { + registry.fill(HIST("h_particles_pt"), particle.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processParticles, "track QA", true); + + void processJets(aod::ChargedJets const& jets, aod::ChargedEventWiseSubtractedJets const& subJets, aod::ChargedMCDetectorLevelJets const& mcdJets, aod::ChargedMCParticleLevelJets const& mcpJets) + { + for (auto const& jet : jets) { + registry.fill(HIST("h_jets_pt"), jet.pt()); + } + for (auto const& jet : subJets) { + registry.fill(HIST("h_jets_sub_pt"), jet.pt()); + } + for (auto const& jet : mcdJets) { + registry.fill(HIST("h_jets_mcd_pt"), jet.pt()); + } + for (auto const& jet : mcpJets) { + registry.fill(HIST("h_jets_mcp_pt"), jet.pt()); + } + } + PROCESS_SWITCH(EmbeddingQATask, processJets, "jet QA", true); + + void processResponse(aod::ChargedJets const&, soa::Join const&, soa::Join const&, soa::Join const& mcpJets) + { + + for (auto const& mcpJet : mcpJets) { + for (auto const& mcdJet : mcpJet.matchedJetGeo_as>()) { + registry.fill(HIST("h_jet_detector_response"), mcpJet.pt(), mcdJet.pt()); + for (auto const& subJet : mcdJet.matchedJetGeo_as>()) { + registry.fill(HIST("h_jet_response"), mcpJet.pt(), subJet.pt()); + for (auto const& jet : subJet.matchedJetGeo_as()) { + registry.fill(HIST("h_jet_background_response"), mcdJet.pt(), jet.pt()); + registry.fill(HIST("h_jet_backgroundfluctuations_response"), subJet.pt(), jet.pt()); + } + } + } + } + } + PROCESS_SWITCH(EmbeddingQATask, processResponse, "jet response", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"embedding-qa"})}; } diff --git a/PWGJE/Tasks/emcClusterMonitor.cxx b/PWGJE/Tasks/emcClusterMonitor.cxx index b014ffae8fb..8820d975d6f 100644 --- a/PWGJE/Tasks/emcClusterMonitor.cxx +++ b/PWGJE/Tasks/emcClusterMonitor.cxx @@ -62,8 +62,8 @@ using namespace o2::framework; using namespace o2::framework::expressions; using collisionEvSelIt = o2::soa::Join::iterator; using bcEvSelIt = o2::soa::Join::iterator; -using selectedClusters = o2::soa::Filtered; -using selectedAmbiguousClusters = o2::soa::Filtered; +using selectedClusters = o2::soa::Filtered>; +using selectedAmbiguousClusters = o2::soa::Filtered>; struct ClusterMonitor { HistogramRegistry mHistManager{"ClusterMonitorHistograms"}; o2::emcal::Geometry* mGeometry = nullptr; @@ -104,6 +104,7 @@ struct ClusterMonitor { o2Axis numberClustersAxis{mNumberClusterBinning, "Number of clusters / event"}; const AxisSpec thAxisCellTimeDiff{3000, -1500, 1500, "#Delta#it{t}_{cell} (ns)"}; const AxisSpec thAxisCellTimeMean{1500, -600, 900, "#LT#it{t}_{cell}#GT (ns)"}; + const AxisSpec thAxisDispersion{100, 0, 10, "dispersion (cells)"}; // event properties mHistManager.add("eventsAll", "Number of events", o2HistType::kTH1D, {{1, 0.5, 1.5}}); @@ -124,8 +125,8 @@ struct ClusterMonitor { mHistManager.add("clusterESupermodule", "Energy of the cluster vs. supermoduleID", o2HistType::kTH2F, {energyAxis, supermoduleAxis}); mHistManager.add("clusterE_SimpleBinning", "Energy of cluster", o2HistType::kTH1D, {{2000, 0, 200}}); mHistManager.add("clusterEtaPhi", "Eta and phi of cluster", o2HistType::kTH2F, {{100, -1, 1}, {100, 0, 2 * TMath::Pi()}}); - mHistManager.add("clusterM02", "M02 of cluster", o2HistType::kTH1D, {{400, 0, 5}}); - mHistManager.add("clusterM20", "M20 of cluster", o2HistType::kTH1D, {{400, 0, 2.5}}); + mHistManager.add("clusterM02", "M02 of cluster;M_{02} (cells);#it{E} (GeV)", o2HistType::kTH2D, {{400, 0, 5}, {100, 0, 10}}); + mHistManager.add("clusterM20", "M20 of cluster", o2HistType::kTH2D, {{400, 0, 2.5}, {100, 0, 10}}); mHistManager.add("clusterNLM", "Number of local maxima of cluster", o2HistType::kTH1D, {{10, 0, 10}}); mHistManager.add("clusterNCells", "Number of cells in cluster", o2HistType::kTH1D, {{50, 0, 50}}); mHistManager.add("clusterDistanceToBadChannel", "Distance to bad channel", o2HistType::kTH1D, {{100, 0, 100}}); @@ -133,6 +134,7 @@ struct ClusterMonitor { mHistManager.add("clusterAmpFractionLeadingCell", "Fraction of energy in leading cell", o2HistType::kTH1D, {{100, 0, 1}}); mHistManager.add("clusterCellTimeDiff", "Cell time difference in clusters", o2HistType::kTH1D, {thAxisCellTimeDiff}); mHistManager.add("clusterCellTimeMean", "Mean cell time per cluster", o2HistType::kTH1D, {thAxisCellTimeMean}); + mHistManager.add("clusterDispersion", "Cluster dispersion vs energy", o2HistType::kTH2D, {thAxisDispersion, energyAxis}); // add histograms per supermodule for (int ism = 0; ism < 20; ++ism) { @@ -142,10 +144,10 @@ struct ClusterMonitor { mHistManager.add(Form("clusterM20VsESM/clusterM20VsESM%d", ism), Form("Cluster M20 vs energy in Supermodule %d", ism), o2HistType::kTH2F, {{400, 0, 2.5}, amplitudeAxisLarge}); } - if (mVetoBCID->length()) { + if (!mVetoBCID->empty()) { std::stringstream parser(mVetoBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Veto BCID " << bcid; @@ -155,7 +157,7 @@ struct ClusterMonitor { if (mSelectBCID.value != "all") { std::stringstream parser(mSelectBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Select BCID " << bcid; @@ -204,7 +206,7 @@ struct ClusterMonitor { mHistManager.fill(HIST("numberOfClustersEvents"), clusters.size()); LOG(debug) << "bunch crossing ID" << theCollision.bcId(); - std::array numberOfClustersSM; + std::array numberOfClustersSM{0}; std::fill(numberOfClustersSM.begin(), numberOfClustersSM.end(), 0); // loop over all clusters from accepted collision // auto eventClusters = clusters.select(o2::aod::emcalcluster::bcId == theCollision.bc().globalBC()); @@ -220,12 +222,13 @@ struct ClusterMonitor { mHistManager.fill(HIST("clusterE"), cluster.energy()); mHistManager.fill(HIST("clusterE_SimpleBinning"), cluster.energy()); mHistManager.fill(HIST("clusterEtaPhi"), cluster.eta(), cluster.phi()); - mHistManager.fill(HIST("clusterM02"), cluster.m02()); - mHistManager.fill(HIST("clusterM20"), cluster.m20()); + mHistManager.fill(HIST("clusterM02"), cluster.m02(), cluster.energy()); + mHistManager.fill(HIST("clusterM20"), cluster.m20(), cluster.energy()); mHistManager.fill(HIST("clusterTimeVsE"), cluster.time(), cluster.energy()); mHistManager.fill(HIST("clusterNLM"), cluster.nlm()); mHistManager.fill(HIST("clusterNCells"), cluster.nCells()); mHistManager.fill(HIST("clusterDistanceToBadChannel"), cluster.distanceToBadChannel()); + mHistManager.fill(HIST("clusterDispersion"), cluster.dispersion(), cluster.energy()); // loop over cells in cluster LOG(debug) << "Cluster energy: " << cluster.energy(); LOG(debug) << "Cluster index: " << cluster.index(); @@ -241,6 +244,7 @@ struct ClusterMonitor { } catch (o2::emcal::InvalidPositionException& e) { // Imprecision of the position at the sector boundaries, mostly due to // vertex imprecision. Skip these clusters for the now. + LOG(debug) << "Skipping cluster at sector boundary: " << e.what(); } // example of loop over all cells of current cluster @@ -290,7 +294,7 @@ struct ClusterMonitor { LOG(info) << "Event rejected because of veto BCID " << eventIR.bc; return; } - if (mSelectBCIDs.size() && (std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), eventIR.bc) == mSelectBCIDs.end())) { + if (!mSelectBCIDs.empty() && (std::find(mSelectBCIDs.begin(), mSelectBCIDs.end(), eventIR.bc) == mSelectBCIDs.end())) { return; } bool isSelected = true; @@ -311,19 +315,20 @@ struct ClusterMonitor { mHistManager.fill(HIST("eventBCSelected"), eventIR.bc); mHistManager.fill(HIST("numberOfClustersBC"), clusters.size()); - std::array numberOfClustersSM; + std::array numberOfClustersSM{0}; std::fill(numberOfClustersSM.begin(), numberOfClustersSM.end(), 0); // loop over ambiguous clusters for (const auto& cluster : clusters) { mHistManager.fill(HIST("clusterE"), cluster.energy()); mHistManager.fill(HIST("clusterE_SimpleBinning"), cluster.energy()); mHistManager.fill(HIST("clusterEtaPhi"), cluster.eta(), cluster.phi()); - mHistManager.fill(HIST("clusterM02"), cluster.m02()); - mHistManager.fill(HIST("clusterM20"), cluster.m20()); + mHistManager.fill(HIST("clusterM02"), cluster.m02(), cluster.energy()); + mHistManager.fill(HIST("clusterM20"), cluster.m20(), cluster.energy()); mHistManager.fill(HIST("clusterTimeVsE"), cluster.time(), cluster.energy()); mHistManager.fill(HIST("clusterNLM"), cluster.nlm()); mHistManager.fill(HIST("clusterNCells"), cluster.nCells()); mHistManager.fill(HIST("clusterDistanceToBadChannel"), cluster.distanceToBadChannel()); + mHistManager.fill(HIST("clusterDispersion"), cluster.dispersion(), cluster.energy()); try { auto supermoduleID = mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); @@ -333,6 +338,7 @@ struct ClusterMonitor { } catch (o2::emcal::InvalidPositionException& e) { // Imprecision of the position at the sector boundaries, mostly due to // vertex imprecision. Skip these clusters for the now. + LOG(debug) << "Skipping cluster at sector boundary: " << e.what(); } } for (int supermoduleID = 0; supermoduleID < 20; supermoduleID++) { @@ -456,33 +462,34 @@ struct ClusterMonitor { std::vector result; Int_t nBinsClusterE = 235; for (Int_t i = 0; i < nBinsClusterE + 1; i++) { - if (i < 1) + if (i < 1) { result.emplace_back(0.3 * i); - else if (i < 55) + } else if (i < 55) { result.emplace_back(0.3 + 0.05 * (i - 1)); - else if (i < 105) + } else if (i < 105) { result.emplace_back(3. + 0.1 * (i - 55)); - else if (i < 140) + } else if (i < 140) { result.emplace_back(8. + 0.2 * (i - 105)); - else if (i < 170) + } else if (i < 170) { result.emplace_back(15. + 0.5 * (i - 140)); - else if (i < 190) + } else if (i < 190) { result.emplace_back(30. + 1.0 * (i - 170)); - else if (i < 215) + } else if (i < 215) { result.emplace_back(50. + 2.0 * (i - 190)); - else if (i < 235) + } else if (i < 235) { result.emplace_back(100. + 5.0 * (i - 215)); - else if (i < 245) + } else if (i < 245) { result.emplace_back(200. + 10.0 * (i - 235)); + } } return result; } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc, TaskName{"EMCClusterMonitorTask"}, SetDefaultProcesses{{{"processCollisions", true}, {"processAmbiguous", false}}}), - adaptAnalysisTask(cfgc, TaskName{"EMCClusterMonitorTaskAmbiguous"}, SetDefaultProcesses{{{"processCollisions", false}, {"processAmbiguous", true}}})}; + adaptAnalysisTask(context, TaskName{"EMCClusterMonitorTask"}, SetDefaultProcesses{{{"processCollisions", true}, {"processAmbiguous", false}}}), + adaptAnalysisTask(context, TaskName{"EMCClusterMonitorTaskAmbiguous"}, SetDefaultProcesses{{{"processCollisions", false}, {"processAmbiguous", true}}})}; return workflow; } diff --git a/PWGJE/Tasks/emcTmMonitor.cxx b/PWGJE/Tasks/emcTmMonitor.cxx index 3d6baefda3b..cef13ebaf16 100644 --- a/PWGJE/Tasks/emcTmMonitor.cxx +++ b/PWGJE/Tasks/emcTmMonitor.cxx @@ -22,7 +22,11 @@ /// Simple event selection using the flag doEventSel is provided, which selects INT7 events if set to 1 /// For pilot beam data, instead of relying on the event selection, one can veto specific BC IDS using the flag /// fDoVetoBCID. +/// The MC process (processMCTruth) follows the leading MC particle of each cluster through the matching chain: +/// is its track reconstructed, extrapolated to the EMCal, a matching candidate (global track or V0 leg), +/// and matched to the cluster as primary or secondary track. +#include "PWGEM/PhotonMeson/DataModel/gammaTables.h" // for the V0 legs used in the secondary matching #include "PWGJE/DataModel/EMCALClusters.h" #include "Common/CCDB/TriggerAliases.h" @@ -30,24 +34,40 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include #include +#include #include +#include #include #include #include #include #include +#include #include #include -#include +#include +#include +#include +#include +#include +#include + +#include #include +#include +#include #include #include #include +#include +#include +#include #include using namespace o2::framework; @@ -56,16 +76,55 @@ using CollisionEvSelIt = o2::soa::Join::it using BcEvSelIt = o2::soa::Join::iterator; using SelectedClusters = o2::soa::Filtered; using TracksPID = o2::soa::Join; +using SelectedMcClusters = o2::soa::Filtered>; +using TracksMC = o2::soa::Join; + +/// \brief Truth class of the leading MC particle of a cluster +enum class TmTruthClass : uint8_t { + kConversionElectron = 0, // e+- with a photon as mother, produced in the transport (photon conversion) + kPrimaryElectron, // e+- produced by the generator (Dalitz, heavy flavour, ...) + kOtherElectron, // any other e+- (delta rays, Compton, weak decays in the transport, ...) + kOtherCharged, // charged particle that is not an e+- (hadrons, muons) + kPhoton, + kNeutral, // neutral particle that is not a photon + kOther, // no MC particle or unknown PDG code + kNClasses +}; + +/// \brief What happened to the track of the leading MC particle of a cluster in the track matching +enum class TmTruthStatus : uint8_t { + kNoRecoTrack = 0, // no reconstructed track of this particle in this collision + kNotExtrapolated, // track(s) exist, but none was extrapolated to the EMCal (trackEta/PhiEmcal = -999) + kNoMatchingCandidate, // extrapolated, but neither a global track nor a V0 leg: input to no matching + kCandidateNotMatched, // matching candidate, but not matched to this cluster (dR > maxMatchingDistance or not among the closest tracks) + kMatchedPrimary, // matched to this cluster as primary (global) track + kMatchedSecondary, // matched to this cluster as secondary track (V0 leg) + kNStatus +}; + +/// \brief Type of the reconstructed track +enum class TmTrackType : uint8_t { + kNoTrack = 0, + kGlobal, // passes isGlobalTrack() + kItsTpc, // ITS and TPC, but not a global track + kTpcOnly, + kItsOnly, + kOtherType, + kNTypes +}; + struct EmcTmMonitor { HistogramRegistry mHistManager{"TrackMatchingMonitorHistograms", {}, OutputObjHandlingPolicy::AnalysisObject}; o2::emcal::Geometry* mGeometry = nullptr; + Service mPdgDatabase{}; Preslice perCluster = o2::aod::emcalclustercell::emcalclusterId; Preslice perClusterAmb = o2::aod::emcalclustercell::emcalambiguousclusterId; Preslice perClusterMatchedTracks = o2::aod::emcalclustercell::emcalclusterId; + Preslice perClusterMatchedSecondaries = o2::aod::emcalclustercell::emcalclusterId; // configurable parameters // TODO adapt doEventSel switch to also allow selection of other triggers (e.g. EMC7) - Configurable doEventSel{"doEventSel", 0, "demand kINT7"}; + Configurable doEventSel{"doEventSel", false, "demand kINT7"}; Configurable vetoBCID{"vetoBCID", "", "BC ID(s) to be excluded, this should be used as an alternative to the event selection"}; Configurable selectBCID{"selectBCID", "all", "BC ID(s) to be included, this should be used as an alternative to the event selection"}; Configurable vertexCut{"vertexCut", -1, "apply z-vertex cut with value in cm"}; @@ -85,6 +144,7 @@ struct EmcTmMonitor { Configurable minDPhi{"minDPhi", 0.01, "Minimum dPhi between track and cluster"}; Configurable> eOverPRange{"eOverPRange", {0.9, 1.2}, "E/p range where one would search for electrons (first <= E/p <= second)"}; Configurable hasTRD{"hasTRD", false, "demand track to have a hit in TRD."}; + Configurable emcalRadius{"emcalRadius", 440.f, "MC truth: radius (cm) used to move the cluster eta from the vertex to the origin frame of the track extrapolation"}; std::vector mVetoBCIDs; std::vector mSelectBCIDs; @@ -182,10 +242,67 @@ struct EmcTmMonitor { mHistManager.add("clusterTM_EoverP_hadron_ASide", "cluster E/hadron p in A-Side", HistType::kTH3F, {eoverpAxis, trackpAxis, nmatchedtrack}); // E over p vs track pT for the Nth closest hadron track in A-Side mHistManager.add("clusterTM_EoverP_hadron_CSide", "cluster E/hadron p in C-Side", HistType::kTH3F, {eoverpAxis, trackpAxis, nmatchedtrack}); // E over p vs track pT for the Nth closest hadron track in C-Side - if (vetoBCID->length()) { + // MC truth of the track matching, only booked if processMCTruth is enabled + if (doprocessMCTruth.value) { + static constexpr std::array(TmTruthClass::kNClasses)> ClassLabels = {"conversion e^{#pm}", "primary e^{#pm}", "other e^{#pm}", "charged (non e)", "#gamma", "neutral (non #gamma)", "other"}; + static constexpr std::array(TmTruthStatus::kNStatus)> StatusLabels = {"no reco track", "not extrapolated", "no matching candidate", "candidate not matched", "matched primary", "matched secondary"}; + static constexpr std::array(TmTrackType::kNTypes)> TypeLabels = {"no track", "global", "ITS-TPC non-global", "TPC only", "ITS only", "other"}; + const AxisSpec classAxis{static_cast(TmTruthClass::kNClasses), -0.5, static_cast(TmTruthClass::kNClasses) - 0.5, "leading MC particle"}; + const AxisSpec statusAxis{static_cast(TmTruthStatus::kNStatus), -0.5, static_cast(TmTruthStatus::kNStatus) - 0.5, "track matching status"}; + const AxisSpec typeAxis{static_cast(TmTrackType::kNTypes), -0.5, static_cast(TmTrackType::kNTypes) - 0.5, "track type"}; + const AxisSpec v0LegAxis{2, -0.5, 1.5, "V0 leg"}; + const AxisSpec mcPtAxis{std::vector{0., 0.25, 0.5, 0.75, 1., 1.5, 2., 3., 4., 6., 10., 20., 50.}, "#it{p}_{T,MC} (GeV/#it{c})"}; + // residuals: fine binning around 0, coarse binning up to MaxResidual, so the tails beyond the matching windows are visible + constexpr double MaxResidual = 0.4; // range of the residual axes + constexpr double FineRange = 0.04; // fine binning within +-FineRange + constexpr double FineWidth = 0.002; // bin width within +-FineRange + constexpr double CoarseWidth = 0.01; // bin width outside +-FineRange + const int nCoarse = static_cast(std::lround((MaxResidual - FineRange) / CoarseWidth)); + const int nFine = static_cast(std::lround((FineRange - (-FineRange)) / FineWidth)); + std::vector residualBins; + residualBins.reserve(nCoarse + nFine + nCoarse + 1); // negative coarse, fine, positive coarse edges incl. the upper end + for (int i = 0; i < nCoarse; ++i) { + residualBins.push_back(-MaxResidual + CoarseWidth * i); + } + for (int i = 0; i < nFine; ++i) { + residualBins.push_back(-FineRange + FineWidth * i); + } + for (int i = 0; i <= nCoarse; ++i) { + residualBins.push_back(FineRange + CoarseWidth * i); + } + const AxisSpec mcDEtaAxis{residualBins, "#Delta#eta (track - cluster)"}; + const AxisSpec mcDPhiAxis{residualBins, "#Delta#varphi (track - cluster) (rad)"}; + const AxisSpec radiusAxis{125, 0., 250., "#it{R}_{xy} of the conversion (cm)"}; + + auto setLabels = [](TAxis* axis, auto const& labels) { + for (std::size_t i = 0; i < labels.size(); ++i) { + axis->SetBinLabel(static_cast(i) + 1, labels[i].data()); + } + }; + // histograms with labelled axes are TH2 or THn: the TH3 label merge of ROOT 6.36 is broken + auto hLeadingClass = mHistManager.add("MCTruth/hLeadingClass", "leading MC particle of the cluster", HistType::kTH2F, {classAxis, energyAxis}); + setLabels(hLeadingClass->GetXaxis(), ClassLabels); + auto hStatus = mHistManager.add("MCTruth/hStatus", "track matching status of the leading MC particle", HistType::kTHnF, {statusAxis, classAxis, typeAxis, v0LegAxis, mcPtAxis}); + setLabels(hStatus->GetAxis(0), StatusLabels); + setLabels(hStatus->GetAxis(1), ClassLabels); + setLabels(hStatus->GetAxis(2), TypeLabels); + for (const auto& name : {"MCTruth/hResidualEta", "MCTruth/hResidualPhi"}) { + const bool isEta = std::string_view(name).ends_with("Eta"); + auto hRes = mHistManager.add(name, "residual of the extrapolated track of the leading MC particle (origin frame)", HistType::kTHnF, {isEta ? mcDEtaAxis : mcDPhiAxis, mcPtAxis, classAxis, typeAxis}); + setLabels(hRes->GetAxis(2), ClassLabels); + setLabels(hRes->GetAxis(3), TypeLabels); + } + mHistManager.add("MCTruth/hResidualConversion", "conversion e^{#pm}: residual of the extrapolated track", HistType::kTH3F, {mcDEtaAxis, mcDPhiAxis, mcPtAxis}); + mHistManager.add("MCTruth/hResidualVsStored", "matched tracks: own minus stored residual;#Delta#eta_{own} - #Delta#eta_{stored};#Delta#varphi_{own} - #Delta#varphi_{stored}", HistType::kTH2F, {{200, -0.02, 0.02}, {200, -0.02, 0.02}}); + auto hConvRadius = mHistManager.add("MCTruth/hConvRadius", "conversion e^{#pm}: matching status vs conversion radius", HistType::kTHnF, {statusAxis, radiusAxis, mcPtAxis}); + setLabels(hConvRadius->GetAxis(0), StatusLabels); + mHistManager.add("MCTruth/hEoverPConversion", "conversion e^{#pm}: #it{E}_{cls}/#it{p}_{track}", HistType::kTH2F, {eoverpAxis, trackptAxis}); + } + + if (!vetoBCID->empty()) { std::stringstream parser(vetoBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Veto BCID " << bcid; @@ -195,7 +312,7 @@ struct EmcTmMonitor { if (selectBCID.value != "all") { std::stringstream parser(selectBCID.value); std::string token; - int bcid; + int bcid = -1; while (std::getline(parser, token, ',')) { bcid = std::stoi(token); LOG(info) << "Select BCID " << bcid; @@ -209,19 +326,22 @@ struct EmcTmMonitor { // an integer instead Filter clusterDefinitionSelection = (o2::aod::emcalcluster::definition == clusterDefinition) && (o2::aod::emcalcluster::time >= minTime) && (o2::aod::emcalcluster::time <= maxTime) && (o2::aod::emcalcluster::m02 > minM02) && (o2::aod::emcalcluster::m02 < maxM02); - /// \brief Process EMCAL clusters that are matched to a collisions - void processCollisions(CollisionEvSelIt const& theCollision, SelectedClusters const& clusters, o2::aod::EMCALClusterCells const&, o2::aod::Calos const&, o2::aod::EMCALMatchedTracks const& matchedtracks, TracksPID const& alltracks) + /// \brief Event selection of the monitor: trigger alias (if doEventSel) and z vertex + /// \param theCollision collision to check + /// \param fillHistos fill the event histograms (only once if several process functions run) + bool isEventSelected(CollisionEvSelIt const& theCollision, const bool fillHistos) { - mHistManager.fill(HIST("eventsAll"), 1); + if (fillHistos) { + mHistManager.fill(HIST("eventsAll"), 1); + } // do event selection if doEventSel is specified // currently the event selection is hard coded to kINT7 // but other selections are possible that are defined in TriggerAliases.h bool isSelected = true; const int beginningRun3 = 300000; - float lowP = 0.75f; - float midP = 1.25f; if (doEventSel) { + // needs aod::BCs in the process function, otherwise the bc() index is not bound if (theCollision.bc().runNumber() < beginningRun3) { if (!theCollision.alias_bit(kINT7)) { isSelected = false; @@ -234,20 +354,34 @@ struct EmcTmMonitor { } if (!isSelected) { LOG(debug) << "Event not selected because it is not kINT7 or does not have EMCAL in readout, skipping"; - return; + return false; + } + if (fillHistos) { + mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); } - mHistManager.fill(HIST("eventVertexZAll"), theCollision.posZ()); if (vertexCut > 0 && std::abs(theCollision.posZ()) > vertexCut) { LOG(debug) << "Event not selected because of z-vertex cut z= " << theCollision.posZ() << " > " << vertexCut << " cm, skipping"; + return false; + } + if (fillHistos) { + mHistManager.fill(HIST("eventsSelected"), 1); + mHistManager.fill(HIST("eventVertexZSelected"), theCollision.posZ()); + } + return true; + } + + /// \brief Process EMCAL clusters that are matched to a collisions + void processCollisions(CollisionEvSelIt const& theCollision, o2::aod::BCs const&, SelectedClusters const& clusters, o2::aod::EMCALClusterCells const&, o2::aod::Calos const&, o2::aod::EMCALMatchedTracks const& matchedtracks, TracksPID const& alltracks) + { + if (!isEventSelected(theCollision, true)) { return; } - mHistManager.fill(HIST("eventsSelected"), 1); - mHistManager.fill(HIST("eventVertexZSelected"), theCollision.posZ()); + float lowP = 0.75f; + float midP = 1.25f; for (const auto& alltrack : alltracks) { - double trackEta, trackPhi; - trackEta = alltrack.trackEtaEmcal(); - trackPhi = alltrack.trackPhiEmcal(); + double trackEta = alltrack.trackEtaEmcal(); + double trackPhi = alltrack.trackPhiEmcal(); if (alltrack.isGlobalTrack()) { // NSigma of all global tracks without matching mHistManager.fill(HIST("TrackTM_NSigma"), alltrack.tpcNSigmaEl(), alltrack.pt()); mHistManager.fill(HIST("TrackEtaPhi"), trackEta, trackPhi); @@ -284,11 +418,11 @@ struct EmcTmMonitor { // match.track_as() with // using globTracks = o2::soa::Join; // In this example the counter t is just used to only look at the closest match - float dEta, dPhi, pT, abs_p, trackEta, trackPhi, nSigmaEl; - int supermoduleID; + float dEta{0}, dPhi{0}, pT{0}, absP{0}, trackEta{0}, trackPhi{0}, nSigmaEl{0}; + int supermoduleID = -1; try { supermoduleID = mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); - } catch (o2::emcal::InvalidPositionException& e) { + } catch (o2::emcal::InvalidPositionException const& e) { // Imprecision of the position at the sector boundaries, mostly due to // vertex imprecision. Skip these clusters for the now. continue; @@ -304,7 +438,7 @@ struct EmcTmMonitor { nSigmaEl = match.track_as().tpcNSigmaEl(); // exmple of how to access any property of the matched tracks (tracks are sorted by how close they are to cluster) LOG(debug) << "Pt of match" << match.track_as().pt(); - abs_p = std::abs(match.track_as().p()); + absP = std::abs(match.track_as().p()); // only consider closest match trackEta = match.track_as().trackEtaEmcal(); trackPhi = match.track_as().trackPhiEmcal(); @@ -330,14 +464,14 @@ struct EmcTmMonitor { mHistManager.fill(HIST("clusterTM_dEtadPhi"), dEta, dPhi, supermoduleID); mHistManager.fill(HIST("clusterTM_dEtaPt"), dEta, pT, supermoduleID); } - double eOverP = cluster.energy() / abs_p; + double eOverP = cluster.energy() / absP; mHistManager.fill(HIST("clusterTM_dEtaTN"), dEta, t); mHistManager.fill(HIST("clusterTM_dPhiTN"), dPhi, t); mHistManager.fill(HIST("clusterTM_dRTN"), std::sqrt(dPhi * dPhi + dEta * dEta), t); mHistManager.fill(HIST("clusterTM_EoverP_E"), eOverP, cluster.energy(), t); mHistManager.fill(HIST("clusterTM_dEtadPhi"), dEta, dPhi, t); mHistManager.fill(HIST("clusterEMatched"), cluster.energy(), t); - mHistManager.fill(HIST("clusterTM_EvsP"), cluster.energy(), abs_p, t); + mHistManager.fill(HIST("clusterTM_EvsP"), cluster.energy(), absP, t); mHistManager.fill(HIST("clusterTM_EoverP_Pt"), eOverP, match.track_as().pt(), t); mHistManager.fill(HIST("clusterTM_NSigma"), nSigmaEl, match.track_as().pt(), t); mHistManager.fill(HIST("MatchedTrackEtaPhi"), trackEta, trackPhi); @@ -357,9 +491,9 @@ struct EmcTmMonitor { if (t == 0) { mHistManager.fill(HIST("clusterTM_PosdPhiPt"), dPhi, pT, supermoduleID); mHistManager.fill(HIST("clusterTM_PosdEtadPhi"), dEta, dPhi, supermoduleID); - if (abs_p < lowP) { + if (absP < lowP) { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_Pl0_75"), dEta, dPhi, supermoduleID); - } else if (abs_p >= midP) { + } else if (absP >= midP) { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_Pgeq1_25"), dEta, dPhi, supermoduleID); } else { mHistManager.fill(HIST("clusterTM_PosdEtadPhi_0_75leqPl1_25"), dEta, dPhi, supermoduleID); @@ -371,9 +505,9 @@ struct EmcTmMonitor { if (t == 0) { mHistManager.fill(HIST("clusterTM_NegdPhiPt"), dPhi, pT, supermoduleID); mHistManager.fill(HIST("clusterTM_NegdEtadPhi"), dEta, dPhi, supermoduleID); - if (abs_p < lowP) { + if (absP < lowP) { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_Pl0_75"), dEta, dPhi, supermoduleID); - } else if (abs_p >= midP) { + } else if (absP >= midP) { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_Pgeq1_25"), dEta, dPhi, supermoduleID); } else { mHistManager.fill(HIST("clusterTM_NegdEtadPhi_0_75leqPl1_25"), dEta, dPhi, supermoduleID); @@ -446,6 +580,191 @@ struct EmcTmMonitor { } PROCESS_SWITCH(EmcTmMonitor, processCollisions, "Process clusters from collision", true); + /// \brief Information of a reconstructed track needed for the MC truth of the matching + struct McTrackInfo { + int64_t id{-1}; + bool isGlobal{false}; + bool hasITS{false}; + bool hasTPC{false}; + float etaEmcal{-999.f}; + float phiEmcal{-999.f}; + float p{0.f}; + float pt{0.f}; + }; + + static constexpr float NotOnEmcal = -900.f; // trackEta/PhiEmcal of tracks that were not extrapolated to the EMCal are -999 + + /// \brief Truth class of an MC particle + template + TmTruthClass truthClassOf(TMcParticle const& mc, TMcParticles const& mcParticles) + { + const int pdg = mc.pdgCode(); + if (std::abs(pdg) == PDG_t::kElectron) { + if (mc.producedByGenerator()) { + return TmTruthClass::kPrimaryElectron; + } + if (mc.getProcess() == TMCProcess::kPPair && mc.has_mothers() && mcParticles.iteratorAt(mc.mothersIds()[0]).pdgCode() == PDG_t::kGamma) { + return TmTruthClass::kConversionElectron; + } + return TmTruthClass::kOtherElectron; + } + if (pdg == PDG_t::kGamma) { + return TmTruthClass::kPhoton; + } + const TParticlePDG* info = mPdgDatabase->GetParticle(pdg); + if (info == nullptr) { + return TmTruthClass::kOther; + } + return info->Charge() != 0. ? TmTruthClass::kOtherCharged : TmTruthClass::kNeutral; + } + + /// \brief Type of a reconstructed track + static TmTrackType trackTypeOf(McTrackInfo const& info) + { + if (info.isGlobal) { + return TmTrackType::kGlobal; + } + if (info.hasITS && info.hasTPC) { + return TmTrackType::kItsTpc; + } + if (info.hasTPC) { + return TmTrackType::kTpcOnly; + } + return info.hasITS ? TmTrackType::kItsOnly : TmTrackType::kOtherType; + } + + /// \brief MC truth of the track matching: follow the leading MC particle of each cluster through the matching chain. + /// Needs emcal-correction-task with processMCWithSecondaries and the photon conversion builder (aod::V0Legs). + void processMCTruth(CollisionEvSelIt const& theCollision, o2::aod::BCs const&, SelectedMcClusters const& clusters, o2::aod::EMCALMatchedTracks const& matchedTracks, o2::aod::EMCMatchSecs const& matchedSecondaries, TracksMC const& tracks, o2::aod::V0Legs const& v0Legs, o2::aod::McParticles const& mcParticles) + { + if (!isEventSelected(theCollision, !doprocessCollisions.value)) { + return; + } + + // reconstructed tracks of each MC particle in this collision, and the tracks used as V0 legs (secondary matching) + std::unordered_map> tracksOfMcParticle; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + tracksOfMcParticle[track.mcParticleId()].push_back({track.globalIndex(), track.isGlobalTrack(), track.hasITS(), track.hasTPC(), track.trackEtaEmcal(), track.trackPhiEmcal(), track.p(), track.pt()}); + } + std::unordered_set v0LegTracks; + for (const auto& leg : v0Legs) { + v0LegTracks.insert(leg.trackId()); + } + + for (const auto& cluster : clusters) { + try { + mGeometry->SuperModuleNumberFromEtaPhi(cluster.eta(), cluster.phi()); + } catch (o2::emcal::InvalidPositionException const& e) { + continue; // same as in processCollisions + } + const float clusterPt = cluster.energy() / std::cosh(cluster.eta()); + if (m02highPt > 0. && cluster.m02() >= maxM02HighPt && clusterPt >= m02highPt) { + continue; + } + + // leading contributor: the labels are ordered by energy fraction (ClusterLabel::orderLabels in the ClusterFactory) + const auto mcIds = cluster.mcParticleIds(); + const int64_t leadId = mcIds.empty() ? -1 : mcIds[0]; + TmTruthClass truthClass = TmTruthClass::kOther; + float mcPt = 0.f; + float convRadius = -1.f; + if (leadId >= 0) { + const auto mc = mcParticles.iteratorAt(leadId); + mcPt = mc.pt(); + truthClass = truthClassOf(mc, mcParticles); + convRadius = std::hypot(mc.vx(), mc.vy()); + } + mHistManager.fill(HIST("MCTruth/hLeadingClass"), static_cast(truthClass), cluster.energy()); + const bool isConversion = truthClass == TmTruthClass::kConversionElectron; + if (truthClass != TmTruthClass::kConversionElectron && truthClass != TmTruthClass::kPrimaryElectron && truthClass != TmTruthClass::kOtherElectron && truthClass != TmTruthClass::kOtherCharged) { + continue; // neutral leading particle: no track to match + } + + // best reconstructed track of the leading particle: extrapolated and matching candidate first, then highest pT + const McTrackInfo* best = nullptr; + int bestScore = -1; + if (auto it = tracksOfMcParticle.find(leadId); it != tracksOfMcParticle.end()) { + for (const auto& info : it->second) { + const bool isExtrapolated = info.etaEmcal > NotOnEmcal && info.phiEmcal > NotOnEmcal; + const bool isCandidate = info.isGlobal || v0LegTracks.contains(info.id); + const int score = 2 * (isExtrapolated ? 1 : 0) + (isCandidate ? 1 : 0); + if (score > bestScore || (score == bestScore && info.pt > best->pt)) { + best = &info; + bestScore = score; + } + } + } + + TmTruthStatus status = TmTruthStatus::kNoRecoTrack; + TmTrackType type = TmTrackType::kNoTrack; + bool isV0Leg = false; + if (best != nullptr) { + type = trackTypeOf(*best); + isV0Leg = v0LegTracks.contains(best->id); + const bool isExtrapolated = best->etaEmcal > NotOnEmcal && best->phiEmcal > NotOnEmcal; + + // is the track among the primary or secondary matches of this cluster + bool isMatchedPrimary = false, isMatchedSecondary = false; + float storedDEta = 0.f, storedDPhi = 0.f; + for (const auto& match : matchedTracks.sliceBy(perClusterMatchedTracks, cluster.globalIndex())) { + if (match.trackId() == best->id) { + isMatchedPrimary = true; + storedDEta = match.deltaEta(); + storedDPhi = match.deltaPhi(); + break; + } + } + if (!isMatchedPrimary) { + for (const auto& match : matchedSecondaries.sliceBy(perClusterMatchedSecondaries, cluster.globalIndex())) { + if (match.trackId() == best->id) { + isMatchedSecondary = true; + storedDEta = match.deltaEta(); + storedDPhi = match.deltaPhi(); + break; + } + } + } + if (isMatchedPrimary) { + status = TmTruthStatus::kMatchedPrimary; + } else if (isMatchedSecondary) { + status = TmTruthStatus::kMatchedSecondary; + } else if (!isExtrapolated) { + status = TmTruthStatus::kNotExtrapolated; + } else if (!best->isGlobal && !isV0Leg) { + status = TmTruthStatus::kNoMatchingCandidate; + } else { + status = TmTruthStatus::kCandidateNotMatched; + } + + if (isExtrapolated) { + // the matching compares to the cluster position seen from the origin, the cluster table has eta seen from the vertex + const float clusterEtaOrigin = std::asinh(std::sinh(cluster.eta()) + theCollision.posZ() / emcalRadius); + const float dEta = best->etaEmcal - clusterEtaOrigin; + const float dPhi = std::remainder(best->phiEmcal - cluster.phi(), o2::constants::math::TwoPI); + mHistManager.fill(HIST("MCTruth/hResidualEta"), dEta, mcPt, static_cast(truthClass), static_cast(type)); + mHistManager.fill(HIST("MCTruth/hResidualPhi"), dPhi, mcPt, static_cast(truthClass), static_cast(type)); + if (isConversion) { + mHistManager.fill(HIST("MCTruth/hResidualConversion"), dEta, dPhi, mcPt); + } + if (isMatchedPrimary || isMatchedSecondary) { + mHistManager.fill(HIST("MCTruth/hResidualVsStored"), dEta - storedDEta, dPhi - storedDPhi); + } + } + if (isConversion && best->p > 0.f) { + mHistManager.fill(HIST("MCTruth/hEoverPConversion"), cluster.energy() / best->p, best->pt); + } + } + mHistManager.fill(HIST("MCTruth/hStatus"), static_cast(status), static_cast(truthClass), static_cast(type), static_cast(isV0Leg), mcPt); + if (isConversion) { + mHistManager.fill(HIST("MCTruth/hConvRadius"), static_cast(status), convRadius, mcPt); + } + } + } + PROCESS_SWITCH(EmcTmMonitor, processMCTruth, "MC truth of the track matching for clusters from charged particles", false); + /// \brief Create binning for cluster energy axis (variable bin size) /// \return vector with bin limits std::vector makeEnergyBinning() const @@ -493,7 +812,7 @@ struct EmcTmMonitor { result.emplace_back(50. + 2.0 * (i - 190)); } else if (i < 235) { // o2-linter: disable=magic-number (just numbers for binning) result.emplace_back(100. + 5.0 * (i - 215)); - } else if (i < 245) { // o2-linter: disable=magic-number (just numbers for binning) + } else { result.emplace_back(200. + 10.0 * (i - 235)); } } @@ -529,9 +848,9 @@ struct EmcTmMonitor { } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGJE/Tasks/emcalGammaGammaBcWise.cxx b/PWGJE/Tasks/emcalGammaGammaBcWise.cxx index aa5e99fb70b..445a17b32d4 100644 --- a/PWGJE/Tasks/emcalGammaGammaBcWise.cxx +++ b/PWGJE/Tasks/emcalGammaGammaBcWise.cxx @@ -116,7 +116,7 @@ bool isPhotonAccepted(Photon const& p, emcal::Geometry* emcalGeom = nullptr) } struct Meson { - Meson(Photon p1, Photon p2) : p1(p1), p2(p2) + Meson(const Photon& p1, const Photon& p2) : p1(p1), p2(p2) { pMeson = p1.photon + p2.photon; } diff --git a/PWGJE/Tasks/emcalPi0EnergyScaleCalib.cxx b/PWGJE/Tasks/emcalPi0EnergyScaleCalib.cxx index 61bff6d13e1..e92aa22905a 100644 --- a/PWGJE/Tasks/emcalPi0EnergyScaleCalib.cxx +++ b/PWGJE/Tasks/emcalPi0EnergyScaleCalib.cxx @@ -178,8 +178,8 @@ struct Photon { }; struct Meson { - Meson(Photon p1, Photon p2) : pgamma1(p1), - pgamma2(p2) + Meson(const Photon& p1, const Photon& p2) : pgamma1(p1), + pgamma2(p2) { pMeson = p1.photon + p2.photon; } diff --git a/PWGJE/Tasks/fullJetSpectra.cxx b/PWGJE/Tasks/fullJetSpectra.cxx index 0af05f60cac..c0d12599f50 100644 --- a/PWGJE/Tasks/fullJetSpectra.cxx +++ b/PWGJE/Tasks/fullJetSpectra.cxx @@ -355,11 +355,11 @@ struct FullJetSpectra { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - particleSelection = static_cast(particleSelections); - jetRadiiValues = (std::vector)jetRadii; + particleSelection = particleSelections; + jetRadiiValues = jetRadii; doSumw2 = doMBGapTrigger; /* if (doMcClosure) { // randGen.SetSeed(mcSplitSeed); @@ -391,7 +391,7 @@ struct FullJetSpectra { for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (std::abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -783,7 +783,7 @@ struct FullJetSpectra { Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut && aod::jcollision::centFT0M >= centralityMin && aod::jcollision::centFT0M < centralityMax); // Filter EMCeventCuts = (nabs(aod::collision::posZ) < vertexZCut && aod::collision::centrality >= centralityMin && aod::collision::centrality < centralityMax); Filter trackCuts = (aod::jtrack::pt >= trackpTMin && aod::jtrack::pt < trackpTMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax && aod::jtrack::phi >= trackPhiMin && aod::jtrack::phi <= trackPhiMax); - aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); Filter clusterFilter = (aod::jcluster::definition == static_cast(clusterDefinition) && aod::jcluster::eta > clusterEtaMin && aod::jcluster::eta < clusterEtaMax && aod::jcluster::phi >= clusterPhiMin && aod::jcluster::phi <= clusterPhiMax && aod::jcluster::energy >= clusterEnergyMin && aod::jcluster::time > clusterTimeMin && aod::jcluster::time < clusterTimeMax && (clusterRejectExotics && aod::jcluster::isExotic != true)); Preslice JetMCPPerMcCollision = aod::jet::mcCollisionId; PresliceUnsorted> CollisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; @@ -1185,7 +1185,7 @@ struct FullJetSpectra { if (bcs.size() == 0) { return; } - for (auto bc : bcs) { + for (const auto& bc : bcs) { registry.fill(HIST("hBCCounter"), 0.5); // All BC if (bc.selection_bit(aod::evsel::EventSelectionFlags::kIsTriggerTVX)) { registry.fill(HIST("hBCCounter"), 1.5); // BC+TVX @@ -1197,7 +1197,7 @@ struct FullJetSpectra { } } auto collisionsInBC = collisions.sliceBy(perFoundBC, bc.globalIndex()); - for (auto collision : collisionsInBC) { + for (const auto& collision : collisionsInBC) { registry.fill(HIST("hBCCounter"), 4.5); // CollinBC if (collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { registry.fill(HIST("hBCCounter"), 5.5); // CollinBC+TVX diff --git a/PWGJE/Tasks/fullJetTriggerQATask.cxx b/PWGJE/Tasks/fullJetTriggerQATask.cxx index 4aff2f9ec2e..4409fdd8c34 100644 --- a/PWGJE/Tasks/fullJetTriggerQATask.cxx +++ b/PWGJE/Tasks/fullJetTriggerQATask.cxx @@ -420,7 +420,7 @@ struct JetTriggerQA { } template - void check_maxJetPt(T const jet, U& vecMaxJet) + void check_maxJetPt(T const& jet, U& vecMaxJet) { for (unsigned int i = 0; i < vecMaxJet.size(); i++) { auto maxJet = vecMaxJet[i]; diff --git a/PWGJE/Tasks/gammaJetTreeProducer.cxx b/PWGJE/Tasks/gammaJetTreeProducer.cxx index de043149abe..1f8751bcbfa 100644 --- a/PWGJE/Tasks/gammaJetTreeProducer.cxx +++ b/PWGJE/Tasks/gammaJetTreeProducer.cxx @@ -25,6 +25,7 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include @@ -50,11 +51,14 @@ #include #include +#include #include +#include #include #include #include #include +#include #include #include @@ -67,33 +71,35 @@ using namespace o2; using namespace o2::aod; using namespace o2::framework; using namespace o2::framework::expressions; -using emcClusters = o2::soa::Join; -using emcMCClusters = o2::soa::Join; +using namespace o2::constants::physics; +using EmcClusters = o2::soa::Join; +using EmcMCClusters = o2::soa::Join; struct GammaJetTreeProducer { // analysis tree // charged jets // photon candidates - Produces chargedJetsTable; // detector level jets - Produces eventsTable; // rec events - Produces gammasTable; // detector level clusters - Produces mcEventsTable; // mc collisions information - Produces mcParticlesTable; // gen level particles (photons and pi0) - Produces gammaMCInfosTable; // detector level clusters MC information - Produces chJetMCInfosTable; // detector level charged jets MC information - Produces mcJetsTable; // gen level jets - Produces jetSubstructuresTable; // jet substructure observables + Produces chargedJetsTable; // detector level jets + Produces eventsTable; // rec events + Produces gammasTable; // detector level clusters + Produces mcEventsTable; // mc collisions information + Produces mcParticlesTable; // gen level particles (photons and pi0) + Produces gammaMCInfosTable; // detector level clusters MC information + Produces chJetMCInfosTable; // detector level charged jets MC information + Produces mcJetsTable; // gen level jets + Produces jetSubstructuresTable; // jet substructure observables + Produces mcJetSubstructuresTable; // MC gen-level jet substructure observables HistogramRegistry mHistograms{"GammaJetTreeProducerHisto"}; - Service pdg; + Service pdg{}; // --------------- // Configureables // --------------- // event cuts - Configurable mVertexCut{"vertexCut", 10.0, "apply z-vertex cut with value in cm"}; + Configurable vertexCut{"vertexCut", 10.0, "apply z-vertex cut with value in cm"}; Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; @@ -109,7 +115,9 @@ struct GammaJetTreeProducer { int mRunNumber = 0; std::vector eventSelectionBits; int trackSelection = -1; - const int kMaxRecursionDepth = 100; + static constexpr int MaxRecursionDepth = 100; + static constexpr int PromptMaxStatus = 90; + static constexpr int MinFSRStatus = 40; std::unordered_map collisionMapping; std::unordered_map mcJetIndexMapping; // maps the global index to the index in the mc jets table (per event). This is because later we want to later construct all trees on a per event level, and we need to know at what position in the table per event this is stored @@ -120,9 +128,11 @@ struct GammaJetTreeProducer { std::vector trackEta; std::vector trackPhi; std::vector trackPt; + std::vector trackSourceIndex; std::vector mcParticleEta; std::vector mcParticlePhi; std::vector mcParticlePt; + std::vector mcParticleSourceIndex; TKDTree* trackTree = nullptr; TKDTree* mcParticleTree = nullptr; @@ -135,6 +145,9 @@ struct GammaJetTreeProducer { std::vector ptSubLeadingVec; std::vector thetaVec; + // other constants + static constexpr float InvalidValue = -99.0f; + // keeping track of the current collision index // to determine if we need to rebuild the kdTree // this makes loadorder more robust @@ -143,12 +156,12 @@ struct GammaJetTreeProducer { void init(InitContext const&) { - using o2HistType = HistType; - using o2Axis = AxisSpec; + using O2HistType = HistType; + using O2Axis = AxisSpec; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // for substructure calculation jetReclusterer.isReclustering = true; @@ -158,86 +171,86 @@ struct GammaJetTreeProducer { // create histograms LOG(info) << "Creating histograms"; - const o2Axis ptAxis{100, 0, 200, "p_{T} (GeV/c)"}; - const o2Axis ptRecAxis{100, 0, 200, "p_{T}^{rec} (GeV/c)"}; - const o2Axis ptGenAxis{100, 0, 200, "p_{T}^{gen} (GeV/c)"}; - const o2Axis energyAxis{100, 0, 100, "E (GeV)"}; - const o2Axis m02Axis{100, 0, 3, "m02"}; - const o2Axis etaAxis{100, -1, 1, "#eta"}; - const o2Axis phiAxis{100, 0, o2::constants::math::TwoPI, "#phi"}; - const o2Axis dRAxis{100, 0, 1, "dR"}; - const o2Axis occupancyAxis{300, 0, 30000, "occupancy"}; - const o2Axis nCollisionsAxis{10, -0.5, 9.5, "nCollisions"}; - mHistograms.add("clusterE", "Energy of cluster", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("trackPt", "pT of track", o2HistType::kTH1F, {ptAxis}); - mHistograms.add("chjetPt", "pT of charged jet", o2HistType::kTH1F, {ptAxis}); - mHistograms.add("chjetPtEtaPhi", "pT of charged jet", o2HistType::kTHnSparseF, {ptAxis, etaAxis, phiAxis}); - mHistograms.add("chjetpt_vs_constpt", "pT of charged jet vs pT of constituents", o2HistType::kTH2F, {ptRecAxis, ptGenAxis}); + const O2Axis ptAxis{100, 0, 200, "p_{T} (GeV/c)"}; + const O2Axis ptRecAxis{100, 0, 200, "p_{T}^{rec} (GeV/c)"}; + const O2Axis ptGenAxis{100, 0, 200, "p_{T}^{gen} (GeV/c)"}; + const O2Axis energyAxis{100, 0, 100, "E (GeV)"}; + const O2Axis m02Axis{100, 0, 3, "m02"}; + const O2Axis etaAxis{100, -1, 1, "#eta"}; + const O2Axis phiAxis{100, 0, o2::constants::math::TwoPI, "#phi"}; + const O2Axis dRAxis{100, 0, 1, "dR"}; + const O2Axis occupancyAxis{300, 0, 30000, "occupancy"}; + const O2Axis nCollisionsAxis{10, -0.5, 9.5, "nCollisions"}; + mHistograms.add("clusterE", "Energy of cluster", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("trackPt", "pT of track", O2HistType::kTH1F, {ptAxis}); + mHistograms.add("chjetPt", "pT of charged jet", O2HistType::kTH1F, {ptAxis}); + mHistograms.add("chjetPtEtaPhi", "pT of charged jet", O2HistType::kTHnSparseF, {ptAxis, etaAxis, phiAxis}); + mHistograms.add("chjetpt_vs_constpt", "pT of charged jet vs pT of constituents", O2HistType::kTH2F, {ptRecAxis, ptGenAxis}); // track QA THnSparse - mHistograms.add("trackPtEtaPhi", "Track QA", o2HistType::kTHnSparseF, {ptAxis, etaAxis, phiAxis}); - mHistograms.add("trackPtEtaOccupancy", "Track QA vs occupancy", o2HistType::kTHnSparseF, {ptAxis, etaAxis, occupancyAxis}); + mHistograms.add("trackPtEtaPhi", "Track QA", O2HistType::kTHnSparseF, {ptAxis, etaAxis, phiAxis}); + mHistograms.add("trackPtEtaOccupancy", "Track QA vs occupancy", O2HistType::kTHnSparseF, {ptAxis, etaAxis, occupancyAxis}); // QA for MC collisions to rec collision matching // number of reconstructed and matched collisions for each MC collision vs mc gen photon energy - mHistograms.add("numberRecCollisionsVsPhotonPt", "Number of rec collisions vs photon energy", o2HistType::kTH2F, {nCollisionsAxis, energyAxis}); + mHistograms.add("numberRecCollisionsVsPhotonPt", "Number of rec collisions vs photon energy", O2HistType::kTH2F, {nCollisionsAxis, energyAxis}); // Cluster MC histograms - mHistograms.add("clusterMC_E_All", "Cluster energy for photons", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_Photon", "Cluster energy for photons", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_PromptPhoton", "Cluster energy for prompt photons", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_DirectPromptPhoton", "Cluster energy for direct prompt photons", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_FragmentationPhoton", "Cluster energy for fragmentation photons", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_DecayPhoton", "Cluster energy for decay photons", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_DecayPhotonPi0", "Cluster energy for decay photons from pi0", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_DecayPhotonEta", "Cluster energy for decay photons from eta", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_MergedPi0", "Cluster energy for merged pi0s", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_MergedEta", "Cluster energy for merged etas", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_E_ConvertedPhoton", "Cluster energy for converted photons", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("clusterMC_m02_Photon", "M02 for photons", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_PromptPhoton", "M02 for prompt photons", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_DirectPromptPhoton", "M02 for direct prompt photons", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_FragmentationPhoton", "M02 for fragmentation photons", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_DecayPhoton", "M02 for decay photons", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_DecayPhotonPi0", "M02 for decay photons from pi0", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_DecayPhotonEta", "M02 for decay photons from eta", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_MergedPi0", "M02 for merged pi0s", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_MergedEta", "M02 for merged etas", o2HistType::kTH1F, {m02Axis}); - mHistograms.add("clusterMC_m02_ConvertedPhoton", "M02 for converted photons", o2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_E_All", "Cluster energy for photons", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_Photon", "Cluster energy for photons", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_PromptPhoton", "Cluster energy for prompt photons", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_DirectPromptPhoton", "Cluster energy for direct prompt photons", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_FragmentationPhoton", "Cluster energy for fragmentation photons", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_DecayPhoton", "Cluster energy for decay photons", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_DecayPhotonPi0", "Cluster energy for decay photons from pi0", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_DecayPhotonEta", "Cluster energy for decay photons from eta", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_MergedPi0", "Cluster energy for merged pi0s", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_MergedEta", "Cluster energy for merged etas", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_E_ConvertedPhoton", "Cluster energy for converted photons", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("clusterMC_m02_Photon", "M02 for photons", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_PromptPhoton", "M02 for prompt photons", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_DirectPromptPhoton", "M02 for direct prompt photons", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_FragmentationPhoton", "M02 for fragmentation photons", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_DecayPhoton", "M02 for decay photons", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_DecayPhotonPi0", "M02 for decay photons from pi0", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_DecayPhotonEta", "M02 for decay photons from eta", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_MergedPi0", "M02 for merged pi0s", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_MergedEta", "M02 for merged etas", O2HistType::kTH1F, {m02Axis}); + mHistograms.add("clusterMC_m02_ConvertedPhoton", "M02 for converted photons", O2HistType::kTH1F, {m02Axis}); // MC Gen trigger particle histograms - mHistograms.add("mcGenTrigger_Eta", "eta of mc gen trigger particle", o2HistType::kTH1F, {etaAxis}); - mHistograms.add("mcGenTrigger_Phi", "phi of mc gen trigger particle", o2HistType::kTH1F, {phiAxis}); - mHistograms.add("mcGenTrigger_Pt", "pT of mc gen trigger particle", o2HistType::kTH1F, {ptAxis}); - mHistograms.add("mcGenTrigger_E", "E of mc gen trigger particle", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_PromptPhoton", "E of mc gen trigger prompt photon", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_DirectPromptPhoton", "E of mc gen trigger direct prompt photon", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_FragmentationPhoton", "E of mc gen trigger fragmentation photon", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_DecayPhoton", "E of mc gen trigger decay photon", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_DecayPhotonPi0", "E of mc gen trigger decay photon from pi0", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_DecayPhotonEta", "E of mc gen trigger decay photon from eta", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_DecayPhotonOther", "E of mc gen trigger decay photon from other", o2HistType::kTH1F, {energyAxis}); - mHistograms.add("mcGenTrigger_E_Pi0", "E of mc gen trigger pi0", o2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_Eta", "eta of mc gen trigger particle", O2HistType::kTH1F, {etaAxis}); + mHistograms.add("mcGenTrigger_Phi", "phi of mc gen trigger particle", O2HistType::kTH1F, {phiAxis}); + mHistograms.add("mcGenTrigger_Pt", "pT of mc gen trigger particle", O2HistType::kTH1F, {ptAxis}); + mHistograms.add("mcGenTrigger_E", "E of mc gen trigger particle", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_PromptPhoton", "E of mc gen trigger prompt photon", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_DirectPromptPhoton", "E of mc gen trigger direct prompt photon", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_FragmentationPhoton", "E of mc gen trigger fragmentation photon", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_DecayPhoton", "E of mc gen trigger decay photon", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_DecayPhotonPi0", "E of mc gen trigger decay photon from pi0", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_DecayPhotonEta", "E of mc gen trigger decay photon from eta", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_DecayPhotonOther", "E of mc gen trigger decay photon from other", O2HistType::kTH1F, {energyAxis}); + mHistograms.add("mcGenTrigger_E_Pi0", "E of mc gen trigger pi0", O2HistType::kTH1F, {energyAxis}); // MC Particle level jet histograms - mHistograms.add("mcpJetPt", "pT of mc particle level jet", o2HistType::kTH1F, {ptAxis}); + mHistograms.add("mcpJetPt", "pT of mc particle level jet", O2HistType::kTH1F, {ptAxis}); // MC Detector level jet matching jet histograms - mHistograms.add("mcdJetPtVsTrueJetPtMatchingGeo", "pT rec (x-axis) of detector level jets vs pT true (y-axis) of mc particle level jet (geo matching)", o2HistType::kTH2F, {ptRecAxis, ptGenAxis}); - mHistograms.add("mcdJetPtVsTrueJetPtMatchingPt", "pT rec (x-axis) of detector level jets vs pT true (y-axis) of mc particle level jet (pt matching)", o2HistType::kTH2F, {ptRecAxis, ptGenAxis}); + mHistograms.add("mcdJetPtVsTrueJetPtMatchingGeo", "pT rec (x-axis) of detector level jets vs pT true (y-axis) of mc particle level jet (geo matching)", O2HistType::kTH2F, {ptRecAxis, ptGenAxis}); + mHistograms.add("mcdJetPtVsTrueJetPtMatchingPt", "pT rec (x-axis) of detector level jets vs pT true (y-axis) of mc particle level jet (pt matching)", O2HistType::kTH2F, {ptRecAxis, ptGenAxis}); // Event QA histogram const int nEventBins = 8; - const TString eventLabels[nEventBins] = {"All", "AfterVertexCut", "AfterCollisionSelection", "AfterTriggerSelection", "AfterEMCALSelection", "AfterClusterESelection", "Has MC collision", "is not MB Gap"}; - mHistograms.add("eventQA", "Event QA", o2HistType::kTH1F, {{nEventBins, -0.5, 7.5}}); + const std::array eventLabels = {"All", "AfterVertexCut", "AfterCollisionSelection", "AfterTriggerSelection", "AfterEMCALSelection", "AfterClusterESelection", "Has MC collision", "is not MB Gap"}; + mHistograms.add("eventQA", "Event QA", O2HistType::kTH1F, {{nEventBins, -0.5, 7.5}}); for (int iBin = 0; iBin < nEventBins; iBin++) { mHistograms.get(HIST("eventQA"))->GetXaxis()->SetBinLabel(iBin + 1, eventLabels[iBin]); } // MC collisions QA histograms) const int nRecCollisionBins = 4; - const TString recCollisionLabels[nRecCollisionBins] = {"All", "1 Rec collision", "More than 1 rec collisions", "No rec collisions"}; - mHistograms.add("mcCollisionsWithRecCollisions", "MC collisions with rec collisions", o2HistType::kTH1F, {{nRecCollisionBins, -0.5, 3.5}}); + const std::array recCollisionLabels = {"All", "1 Rec collision", "More than 1 rec collisions", "No rec collisions"}; + mHistograms.add("mcCollisionsWithRecCollisions", "MC collisions with rec collisions", O2HistType::kTH1F, {{nRecCollisionBins, -0.5, 3.5}}); for (int iBin = 0; iBin < nRecCollisionBins; iBin++) { mHistograms.get(HIST("mcCollisionsWithRecCollisions"))->GetXaxis()->SetBinLabel(iBin + 1, recCollisionLabels[iBin]); } @@ -265,33 +278,57 @@ struct GammaJetTreeProducer { curMcParticleTreeColIndex = collision.globalIndex(); } - trackEta.clear(); - trackPhi.clear(); - trackPt.clear(); - mcParticleEta.clear(); - mcParticlePhi.clear(); - mcParticlePt.clear(); - // if the track type is aod::JetTracks, we need to build the kd tree for the tracks if constexpr (std::is_same_v, aod::JetTracks>) { + trackEta.clear(); + trackPhi.clear(); + trackPt.clear(); + trackSourceIndex.clear(); + const float maxMatchingDistance = std::max(static_cast(isoR), static_cast(perpConeJetR)); + const float additionalMargin = 0.05f; for (const auto& track : objects) { if (!isTrackSelected(track)) { continue; } + const float phi = RecoDecay::constrainAngle(track.phi(), 0.0); + const int originalIndex = static_cast(trackPt.size()); trackEta.push_back(track.eta()); - trackPhi.push_back(track.phi()); + trackPhi.push_back(phi); trackPt.push_back(track.pt()); + trackSourceIndex.push_back(originalIndex); + if (phi <= (maxMatchingDistance + additionalMargin)) { + trackEta.push_back(track.eta()); + trackPhi.push_back(phi + o2::constants::math::TwoPI); // o2-linter: disable=two-pi-add-subtract (periodic KD-tree ghost copy) + trackPt.push_back(track.pt()); + trackSourceIndex.push_back(originalIndex); + } + if (phi >= (o2::constants::math::TwoPI - (maxMatchingDistance + additionalMargin))) { + trackEta.push_back(track.eta()); + trackPhi.push_back(phi - o2::constants::math::TwoPI); // o2-linter: disable=two-pi-add-subtract (periodic KD-tree ghost copy) + trackPt.push_back(track.pt()); + trackSourceIndex.push_back(originalIndex); + } } - if (trackEta.size() > 0) { + if (!trackEta.empty()) { delete trackTree; trackTree = new TKDTree(trackEta.size(), 2, 1); trackTree->SetData(0, trackEta.data()); trackTree->SetData(1, trackPhi.data()); trackTree->Build(); + } else { + // Keep tree state consistent with the current event content. + delete trackTree; + trackTree = nullptr; } } // if the track type is aod::JetParticles, we need to build the kd tree for the mc particles if constexpr (std::is_same_v, aod::JetParticles>) { + mcParticleEta.clear(); + mcParticlePhi.clear(); + mcParticlePt.clear(); + mcParticleSourceIndex.clear(); + const float maxMatchingDistance = std::max(static_cast(isoR), static_cast(perpConeJetR)); + const float additionalMargin = 0.05f; for (const auto& particle : objects) { if (!particle.isPhysicalPrimary()) { continue; @@ -302,16 +339,35 @@ struct GammaJetTreeProducer { if (particle.pt() < trackMinPt) { continue; } + const float phi = RecoDecay::constrainAngle(particle.phi(), 0.0); + const int originalIndex = static_cast(mcParticlePt.size()); mcParticleEta.push_back(particle.eta()); - mcParticlePhi.push_back(particle.phi()); + mcParticlePhi.push_back(phi); mcParticlePt.push_back(particle.pt()); + mcParticleSourceIndex.push_back(originalIndex); + if (phi <= (maxMatchingDistance + additionalMargin)) { + mcParticleEta.push_back(particle.eta()); + mcParticlePhi.push_back(phi + o2::constants::math::TwoPI); // o2-linter: disable=two-pi-add-subtract (periodic KD-tree ghost copy) + mcParticlePt.push_back(particle.pt()); + mcParticleSourceIndex.push_back(originalIndex); + } + if (phi >= (o2::constants::math::TwoPI - (maxMatchingDistance + additionalMargin))) { + mcParticleEta.push_back(particle.eta()); + mcParticlePhi.push_back(phi - o2::constants::math::TwoPI); // o2-linter: disable=two-pi-add-subtract (periodic KD-tree ghost copy) + mcParticlePt.push_back(particle.pt()); + mcParticleSourceIndex.push_back(originalIndex); + } } - if (mcParticleEta.size() > 0) { + if (!mcParticleEta.empty()) { delete mcParticleTree; mcParticleTree = new TKDTree(mcParticleEta.size(), 2, 1); mcParticleTree->SetData(0, mcParticleEta.data()); mcParticleTree->SetData(1, mcParticlePhi.data()); mcParticleTree->Build(); + } else { + // Keep tree state consistent with the current event content. + delete mcParticleTree; + mcParticleTree = nullptr; } } } @@ -350,11 +406,11 @@ struct GammaJetTreeProducer { { mHistograms.fill(HIST("eventQA"), 0); - if (collision.posZ() > mVertexCut) { + if (std::abs(collision.posZ()) > vertexCut) { return false; } mHistograms.fill(HIST("eventQA"), 1); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, true, true, rctLabel)) { return false; } mHistograms.fill(HIST("eventQA"), 2); @@ -385,34 +441,58 @@ struct GammaJetTreeProducer { return std::abs(pdg->GetParticle(particle.pdgCode())->Charge()) >= 1.; } + /// \brief Sums pt values once per unique source index from KD-tree query indices + /// \param indices KD-tree result indices + /// \param sourceIndexMap Maps KD-tree entry index to original object index + /// \param ptValues pt values of original objects + /// \param uniqueSourceIndices Set used to deduplicate source indices + /// \return Sum of unique pt values for the given indices + double sumUniquePtFromIndices(const std::vector& indices, + const std::vector& sourceIndexMap, + const std::vector& ptValues, + std::unordered_set& uniqueSourceIndices) + { + double ptSum = 0; + for (const auto& index : indices) { + if (index < 0 || static_cast(index) >= sourceIndexMap.size()) { + continue; + } + const int sourceIndex = sourceIndexMap[index]; + if (sourceIndex < 0 || static_cast(sourceIndex) >= ptValues.size()) { + continue; + } + if (uniqueSourceIndices.insert(sourceIndex).second) { + ptSum += ptValues[sourceIndex]; + } + } + return ptSum; + } + /// \brief Calculates the charged particle isolation in a cone of given size using a pre-built kd tree /// \param particle The particle to calculate the isolation for /// \param radius The cone radius /// \param mcGenIso Whether to use the mc gen particle tree (if false, use the track tree) /// \return The charged particle isolation template - double ch_iso_in_cone(const T& particle, float radius = 0.4, bool mcGenIso = false) + double chIsoInCone(const T& particle, float radius = 0.4, bool mcGenIso = false) { double iso = 0; - float point[2] = {particle.eta(), particle.phi()}; + std::array point = {particle.eta(), RecoDecay::constrainAngle(particle.phi(), 0.0)}; std::vector indices; + std::unordered_set uniqueSourceIndices; if (!mcGenIso) { if (trackTree) { - trackTree->FindInRange(point, radius, indices); - for (const auto& index : indices) { - iso += trackPt[index]; - } + trackTree->FindInRange(point.data(), radius, indices); + iso += sumUniquePtFromIndices(indices, trackSourceIndex, trackPt, uniqueSourceIndices); } else { LOG(error) << "Track tree not found"; return 0; } } else { if (mcParticleTree) { - mcParticleTree->FindInRange(point, radius, indices); - for (const auto& index : indices) { - iso += mcParticlePt[index]; - } + mcParticleTree->FindInRange(point.data(), radius, indices); + iso += sumUniquePtFromIndices(indices, mcParticleSourceIndex, mcParticlePt, uniqueSourceIndices); } else { LOG(error) << "MC particle tree not found"; return 0; @@ -427,7 +507,7 @@ struct GammaJetTreeProducer { /// \param radius The cone radius for density calculation /// \return The average charged particle density in the perpendicular cones template - double ch_perp_cone_rho(const T& object, float radius = 0.4, bool mcGenIso = false) + double chPerpConeRho(const T& object, float radius = 0.4, bool mcGenIso = false) { double ptSumLeft = 0; double ptSumRight = 0; @@ -435,44 +515,38 @@ struct GammaJetTreeProducer { double cPhi = TVector2::Phi_0_2pi(object.phi()); // rotate cone left by 90 degrees - float cPhiLeft = cPhi - o2::constants::math::PIHalf; - float cPhiRight = cPhi + o2::constants::math::PIHalf; + float cPhiLeft = RecoDecay::constrainAngle(cPhi - o2::constants::math::PIHalf, 0.0); + float cPhiRight = RecoDecay::constrainAngle(cPhi + o2::constants::math::PIHalf, 0.0); - float pointLeft[2] = {object.eta(), cPhiLeft}; - float pointRight[2] = {object.eta(), cPhiRight}; + std::array pointLeft = {object.eta(), cPhiLeft}; + std::array pointRight = {object.eta(), cPhiRight}; std::vector indicesLeft; std::vector indicesRight; + std::unordered_set uniqueSourceIndicesLeft; + std::unordered_set uniqueSourceIndicesRight; if (!mcGenIso) { if (trackTree) { - trackTree->FindInRange(pointLeft, radius, indicesLeft); - trackTree->FindInRange(pointRight, radius, indicesRight); + trackTree->FindInRange(pointLeft.data(), radius, indicesLeft); + trackTree->FindInRange(pointRight.data(), radius, indicesRight); } else { LOG(error) << "Track tree not found"; return 0; } - for (const auto& index : indicesLeft) { - ptSumLeft += trackPt[index]; - } - for (const auto& index : indicesRight) { - ptSumRight += trackPt[index]; - } + ptSumLeft += sumUniquePtFromIndices(indicesLeft, trackSourceIndex, trackPt, uniqueSourceIndicesLeft); + ptSumRight += sumUniquePtFromIndices(indicesRight, trackSourceIndex, trackPt, uniqueSourceIndicesRight); } else { if (mcParticleTree) { - mcParticleTree->FindInRange(pointLeft, radius, indicesLeft); - mcParticleTree->FindInRange(pointRight, radius, indicesRight); + mcParticleTree->FindInRange(pointLeft.data(), radius, indicesLeft); + mcParticleTree->FindInRange(pointRight.data(), radius, indicesRight); } else { LOG(error) << "MC particle tree not found"; return 0; } - for (const auto& index : indicesLeft) { - ptSumLeft += mcParticlePt[index]; - } - for (const auto& index : indicesRight) { - ptSumRight += mcParticlePt[index]; - } + ptSumLeft += sumUniquePtFromIndices(indicesLeft, mcParticleSourceIndex, mcParticlePt, uniqueSourceIndicesLeft); + ptSumRight += sumUniquePtFromIndices(indicesRight, mcParticleSourceIndex, mcParticlePt, uniqueSourceIndicesRight); } float rho = (ptSumLeft + ptSumRight) / (o2::constants::math::TwoPI * radius * radius); @@ -494,6 +568,43 @@ struct GammaJetTreeProducer { } } + /// \brief Prints the mother chain of a given MC particle. + /// \param particle The particle to print the mother chain for + template + void printMotherChain(const T& particle) const + { + T current = particle; + int depth = 0; + while (depth < MaxRecursionDepth) { + LOG(info) << "Level " << depth + << " | PDG: " << current.pdgCode() + << " | E: " << current.energy() + << " | pt: " << current.pt() + << " | eta: " << current.eta() + << " | phi: " << current.phi() + << " | genStatusCode: " << current.getGenStatusCode() + << " | globalIndex: " << current.globalIndex(); + auto mothers = current.template mothers_as(); + if (mothers.size() == 0) { + break; + } + // Handle case with potentially duplicate mothers + int selectedMother = -1; + if (mothers.size() == 1 || (mothers.size() == 2 && // o2-linter: disable=magic-number (it is just counting number of mothers) + mothers[0].globalIndex() == mothers[1].globalIndex() && + mothers[0].pdgCode() == mothers[1].pdgCode() && + mothers[0].getGenStatusCode() == mothers[1].getGenStatusCode())) { + selectedMother = 0; + } + if (selectedMother == -1) { + LOG(info) << "Branching in mother chain or unclear mothers, stopping trace."; + break; + } + current = mothers[selectedMother]; + ++depth; + } + } + /// \brief Finds the top-most copy of a particle in the decay chain (following carbon copies) /// \param particle The particle to start from /// \return The top-most copy of the particle @@ -504,10 +615,36 @@ struct GammaJetTreeProducer { T currentParticle = particle; int pdgCode = particle.pdgCode(); auto mothers = particle.template mothers_as(); - while (iUp > 0 && mothers.size() == 1 && mothers[0].globalIndex() > 0 && mothers[0].pdgCode() == pdgCode) { - iUp = mothers[0].globalIndex(); - currentParticle = mothers[0]; + // sometimes the particle will have two identical mothers, not sure why, but we need to cover this case + // if there are two mothers, but they are not identical, we will stop + bool twoMothersIdentical = false; + int selectedMother = -1; + if (mothers.size() == 1) { + selectedMother = 0; + } else if (mothers.size() == 2) { // o2-linter: disable=magic-number (it is just counting number of mothers) + twoMothersIdentical = (mothers[0].globalIndex() == mothers[1].globalIndex() && + mothers[0].pdgCode() == mothers[1].pdgCode() && + mothers[0].getGenStatusCode() == mothers[1].getGenStatusCode()); + if (twoMothersIdentical) { + selectedMother = 0; + } + } + while (iUp > 0 && selectedMother >= 0 && mothers[selectedMother].globalIndex() > 0 && mothers[selectedMother].pdgCode() == pdgCode) { + iUp = mothers[selectedMother].globalIndex(); + currentParticle = mothers[selectedMother]; mothers = currentParticle.template mothers_as(); + selectedMother = -1; + twoMothersIdentical = false; + if (mothers.size() == 1) { + selectedMother = 0; + } else if (mothers.size() == 2) { // o2-linter: disable=magic-number (it is just counting number of mothers) + twoMothersIdentical = (mothers[0].globalIndex() == mothers[1].globalIndex() && + mothers[0].pdgCode() == mothers[1].pdgCode() && + mothers[0].getGenStatusCode() == mothers[1].getGenStatusCode()); + if (twoMothersIdentical) { + selectedMother = 0; + } + } } return currentParticle; } @@ -517,10 +654,7 @@ struct GammaJetTreeProducer { /// \return true if particle is a prompt photon, false otherwise bool isPromptPhoton(const auto& particle) { - if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) < 90) { - return true; - } - return false; + return static_cast(particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) < PromptMaxStatus); } /// \brief Checks if a particle is a direct prompt photon /// \param particle The particle to check @@ -528,10 +662,10 @@ struct GammaJetTreeProducer { bool isDirectPromptPhoton(const auto& particle) { // check if particle isa prompt photon - if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) < 90) { + if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) < PromptMaxStatus) { // find the top carbon copy auto topCopy = iTopCopy(particle); - if (topCopy.pdgCode() == PDG_t::kGamma && std::abs(topCopy.getGenStatusCode()) < 40) { // < 40 is particle directly produced in hard scattering + if (topCopy.pdgCode() == PDG_t::kGamma && std::abs(topCopy.getGenStatusCode()) < MinFSRStatus) { // < 40 is particle directly produced in hard scattering return true; } } @@ -542,10 +676,10 @@ struct GammaJetTreeProducer { /// \return true if particle is a fragmentation photon, false otherwise bool isFragmentationPhoton(const auto& particle) { - if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) < 90) { + if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) < PromptMaxStatus) { // find the top carbon copy auto topCopy = iTopCopy(particle); - if (topCopy.pdgCode() == PDG_t::kGamma && std::abs(topCopy.getGenStatusCode()) >= 40) { // frag photon + if (topCopy.pdgCode() == PDG_t::kGamma && std::abs(topCopy.getGenStatusCode()) >= MinFSRStatus) { // frag photon return true; } } @@ -556,10 +690,7 @@ struct GammaJetTreeProducer { /// \return true if particle is a decay photon, false otherwise bool isDecayPhoton(const auto& particle) { - if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= 90) { - return true; - } - return false; + return static_cast(particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= PromptMaxStatus); } /// \brief Checks if a particle is a decay photon from pi0 /// \param particle The particle to check @@ -567,7 +698,7 @@ struct GammaJetTreeProducer { template bool isDecayPhotonPi0(const T& particle) { - if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= 90) { + if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= PromptMaxStatus) { // check if it has mothers that are pi0s const auto& mothers = particle.template mothers_as(); for (const auto& mother : mothers) { @@ -584,11 +715,11 @@ struct GammaJetTreeProducer { template bool isDecayPhotonEta(const T& particle) { - if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= 90) { + if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= PromptMaxStatus) { // check if it has mothers that are etas const auto& mothers = particle.template mothers_as(); for (const auto& mother : mothers) { - if (mother.pdgCode() == 221) { + if (mother.pdgCode() == Pdg::kEta) { return true; } } @@ -601,11 +732,11 @@ struct GammaJetTreeProducer { template bool isDecayPhotonOther(const T& particle) { - if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= 90) { + if (particle.pdgCode() == PDG_t::kGamma && particle.isPhysicalPrimary() && std::abs(particle.getGenStatusCode()) >= PromptMaxStatus) { // check if you find a pi0 mother or a eta mother const auto& mothers = particle.template mothers_as(); for (const auto& mother : mothers) { - if (mother.pdgCode() == PDG_t::kPi0 || mother.pdgCode() == 221) { + if (mother.pdgCode() == PDG_t::kPi0 || mother.pdgCode() == Pdg::kEta) { return false; } } @@ -618,10 +749,7 @@ struct GammaJetTreeProducer { /// \return true if particle is a pi0, false otherwise bool isPi0(const auto& particle) { - if (particle.pdgCode() == PDG_t::kPi0) { - return true; - } - return false; + return static_cast(particle.pdgCode() == PDG_t::kPi0); } /// \brief Gets the bitmap for a MC particle that indicated what type of particle it is @@ -666,28 +794,28 @@ struct GammaJetTreeProducer { int getIndexMotherChain(const T& particle, aod::JMcParticles const& mcParticles, int pdgCode, int depth = 0) { // Limit recursion depth to avoid infinite loops - if (depth > kMaxRecursionDepth) { // 100 generations should be more than enough + if (depth > MaxRecursionDepth) { // 100 generations should be more than enough return -1; } const auto& mothers = particle.template mothers_as(); for (const auto& mother : mothers) { if (mother.pdgCode() == pdgCode) { return mother.globalIndex(); - } else { - return getIndexMotherChain(mother, mcParticles, pdgCode, depth + 1); } + return getIndexMotherChain(mother, mcParticles, pdgCode, depth + 1); } return -1; } // return recursive list of all daughter IDs /// \brief Gets all daughter particle IDs in the decay chain /// \param particle The particle to start from + /// \param mcParticles The MC particles collection /// \return Vector of daughter particle IDs - template - void getDaughtersInChain(const T& particle, std::vector& daughters, int depth = 0) + template + void getDaughtersInChain(const T& particle, U const& mcParticles, std::vector& daughters, int depth = 0) { // Limit recursion depth to avoid infinite loops - if (depth > kMaxRecursionDepth) { // 100 generations should be more than enough + if (depth > MaxRecursionDepth) { // 100 generations should be more than enough return; } @@ -695,12 +823,13 @@ struct GammaJetTreeProducer { return; } - const auto& daughterParticles = particle.template daughters_as(); - for (const auto& daughter : daughterParticles) { - daughters.push_back(daughter.globalIndex()); - getDaughtersInChain(daughter, daughters, depth + 1); + // Daughter indices are a contiguous inclusive range. Resolve them on the full table + // so the IDs match cluster MC labels. + const auto daughterIds = particle.daughtersIds(); + for (int daughterId = daughterIds[0]; daughterId <= daughterIds[1]; ++daughterId) { + daughters.push_back(daughterId); + getDaughtersInChain(mcParticles.iteratorAt(daughterId), mcParticles, daughters, depth + 1); } - return; } /// \brief Finds the first physical primary particle in the decay chain (upwards) /// \param particle The particle to start from @@ -709,7 +838,7 @@ struct GammaJetTreeProducer { int findPhysicalPrimaryInChain(const T& particle, int depth = 0) { // Limit recursion depth to avoid infinite loops - if (depth > kMaxRecursionDepth) { // 100 generations should be more than enough + if (depth > MaxRecursionDepth) { // 100 generations should be more than enough return -1; } @@ -719,13 +848,15 @@ struct GammaJetTreeProducer { } // check if the particle has mothers - if (!particle.has_mothers()) + if (!particle.has_mothers()) { return -1; + } // now get mothers const auto mothers = particle.template mothers_as(); - if (mothers.size() == 0) + if (mothers.size() == 0) { return -1; + } // get first mother for (const auto& mother : mothers) { @@ -748,7 +879,7 @@ struct GammaJetTreeProducer { bool isMergedFromPDGDecay(const T& cluster, U const& mcParticles, int pdgCode) { auto inducerIDs = cluster.mcParticlesIds(); - if (inducerIDs.size() < 2) { // it can not me "merged" if it has less than 2 inducers + if (inducerIDs.size() < 2) { // o2-linter: disable=magic-number (it is just counting number of inducers) return false; } @@ -757,34 +888,37 @@ struct GammaJetTreeProducer { if (motherIndex != -1) { const auto& mother = mcParticles.iteratorAt(motherIndex); - // get daughters of pi0 mother - auto daughtersMother = mother.template daughters_as(); - // check if there are two daughters that are both photons - if (daughtersMother.size() == 2) { - const auto& daughter1 = daughtersMother.iteratorAt(0); - const auto& daughter2 = daughtersMother.iteratorAt(1); - if (daughter1.pdgCode() == PDG_t::kGamma && daughter2.pdgCode() == PDG_t::kGamma) { - // get the full stack of particles that these daughters create - std::vector fullDecayChain1; - std::vector fullDecayChain2; - getDaughtersInChain(daughter1, fullDecayChain1); - getDaughtersInChain(daughter2, fullDecayChain2); - bool photon1Found = false; - bool photon2Found = false; - - // check if any of the particles in the fullDecayChain are leading or subleading in the cluster - for (const auto& particleID : fullDecayChain1) { - if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { - photon1Found = true; + // Daughter indices are the inclusive [first, last] range stored on the mother. + // Resolve them on the full MC table, same index space as the cluster labels. + if (mother.has_daughters()) { + const auto daughterIds = mother.daughtersIds(); + const bool hasTwoDaughters = daughterIds[1] == daughterIds[0] + 1; // o2-linter: disable=magic-number (two-body decay) + if (hasTwoDaughters) { + const auto& daughter1 = mcParticles.iteratorAt(daughterIds[0]); + const auto& daughter2 = mcParticles.iteratorAt(daughterIds[1]); + if (daughter1.pdgCode() == PDG_t::kGamma && daughter2.pdgCode() == PDG_t::kGamma) { + // include the decay photons themselves, since unconverted photons are usually the cluster inducers + std::vector fullDecayChain1{daughterIds[0]}; + std::vector fullDecayChain2{daughterIds[1]}; + getDaughtersInChain(daughter1, mcParticles, fullDecayChain1); + getDaughtersInChain(daughter2, mcParticles, fullDecayChain2); + bool photon1Found = false; + bool photon2Found = false; + + // check if any of the particles in the fullDecayChain are leading or subleading in the cluster + for (const auto& particleID : fullDecayChain1) { + if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { + photon1Found = true; + } } - } - for (const auto& particleID : fullDecayChain2) { - if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { - photon2Found = true; + for (const auto& particleID : fullDecayChain2) { + if (particleID == inducerIDs[0] || particleID == inducerIDs[1]) { + photon2Found = true; + } + } + if (photon1Found && photon2Found) { + isMerged = true; } - } - if (photon1Found && photon2Found) { - isMerged = true; } } } @@ -796,15 +930,16 @@ struct GammaJetTreeProducer { /// \brief Gets the origin bitmap for a cluster /// \param cluster The cluster to check /// \param mcParticles The MC particles collection - /// \return A bitmap indicating the cluster's origin + /// \return A pair containing the bitmap of the origin of the particle, as well as the MC index of the associated MC gen particle template - uint16_t getClusterOrigin(const T& cluster, U const& mcParticles) + std::pair getClusterOrigin(const T& cluster, U const& mcParticles) { uint16_t origin = 0; + int returnMCIndex = -1; auto inducerIDs = cluster.mcParticlesIds(); if (inducerIDs.size() == 0) { SETBIT(origin, static_cast(gjanalysis::ClusterOrigin::kUnknown)); - return origin; + return std::make_pair(origin, returnMCIndex); } // loop over all inducers and print their energy @@ -829,7 +964,7 @@ struct GammaJetTreeProducer { LOG(debug) << "Leading particle primary ID: " << leadingParticlePrimaryID; if (leadingParticlePrimaryID == -1) { SETBIT(origin, static_cast(gjanalysis::ClusterOrigin::kUnknown)); - return origin; + return std::make_pair(origin, returnMCIndex); } const auto& leadingParticlePrimary = mcParticles.iteratorAt(leadingParticlePrimaryID); LOG(debug) << "Leading particle primary PDG: " << leadingParticlePrimary.pdgCode(); @@ -862,15 +997,21 @@ struct GammaJetTreeProducer { LOG(debug) << "Leading particle primary is a decay photon from eta"; } + // for all the cases so far, we can use as MC index the index of the leading particle primary + returnMCIndex = leadingParticlePrimaryID; + // Do checks if a cluster is a merged pi0 decay // we classify a cluster as merged pi0 if the leading and subleading contribution to a cluster come from two photons that are part of a pi0 decay if (isMergedFromPDGDecay(cluster, mcParticles, PDG_t::kPi0)) { SETBIT(origin, static_cast(gjanalysis::ClusterOrigin::kMergedPi0)); LOG(debug) << "Cluster is a merged pi0"; + // if this is a merged pion decay, it should return the MC index of the pi0 mother (it is ensured that this is properly returned otherwise we would not be in this if statement) + returnMCIndex = getIndexMotherChain(mcParticles.iteratorAt(inducerIDs[0]), mcParticles, PDG_t::kPi0); } - if (isMergedFromPDGDecay(cluster, mcParticles, 221)) { + if (isMergedFromPDGDecay(cluster, mcParticles, Pdg::kEta)) { SETBIT(origin, static_cast(gjanalysis::ClusterOrigin::kMergedEta)); LOG(debug) << "Cluster is a merged eta"; + returnMCIndex = getIndexMotherChain(mcParticles.iteratorAt(inducerIDs[0]), mcParticles, Pdg::kEta); } // check if photon conversion @@ -887,11 +1028,13 @@ struct GammaJetTreeProducer { LOG(debug) << "Got the mother with PDG 22 and daughters"; const auto& daughters = mother.template daughters_as(); // check that mother has exactly two daughters which are e+ and e- - if (daughters.size() == 2) { + if (daughters.size() == 2) { // o2-linter: disable=magic-number (it is just counting number of daughters) LOG(debug) << "Got the daughters"; - if ((daughters.iteratorAt(0).pdgCode() == PDG_t::kElectron && daughters.iteratorAt(1).pdgCode() == PDG_t::kPositron) || (daughters.iteratorAt(0).pdgCode() == -PDG_t::kPositron && daughters.iteratorAt(1).pdgCode() == PDG_t::kElectron)) { + if ((daughters.iteratorAt(0).pdgCode() == PDG_t::kElectron && daughters.iteratorAt(1).pdgCode() == PDG_t::kPositron) || (daughters.iteratorAt(0).pdgCode() == PDG_t::kPositron && daughters.iteratorAt(1).pdgCode() == PDG_t::kElectron)) { SETBIT(origin, static_cast(gjanalysis::ClusterOrigin::kConvertedPhoton)); LOG(debug) << "Cluster is a converted photon"; + // if we found a converted photon, it should use for the the MC index the one of the photon + returnMCIndex = mother.globalIndex(); } } } @@ -900,7 +1043,7 @@ struct GammaJetTreeProducer { } // display bit origin LOG(debug) << "Origin bits: " << std::bitset<16>(origin); - return origin; + return std::make_pair(origin, returnMCIndex); } // --------------------- @@ -915,7 +1058,6 @@ struct GammaJetTreeProducer { mcCollisionsMultiRecCollisions.clear(); mcJetIndexMapping.clear(); } - PROCESS_SWITCH(GammaJetTreeProducer, processClearMaps, "process function that clears all the maps in each dataframe", true); // WARNING: This function always has to run second in the processing chain /// \brief Processes MC event matching QA @@ -932,7 +1074,7 @@ struct GammaJetTreeProducer { int nRecCollisions = 0; mHistograms.fill(HIST("mcCollisionsWithRecCollisions"), 0); for (auto const& collision : collisions) { - if (collision.posZ() > mVertexCut) { + if (std::abs(collision.posZ()) > vertexCut) { continue; } if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, true, true, rctLabel)) { @@ -968,12 +1110,11 @@ struct GammaJetTreeProducer { mHistograms.fill(HIST("numberRecCollisionsVsPhotonPt"), nRecCollisions, particle.pt()); } } - PROCESS_SWITCH(GammaJetTreeProducer, processMCCollisionsMatching, "Process MC event matching QA", false); /// \brief Processes data events in data fill event table /// \param collision The collision to process /// \param clusters The EMCAL clusters in the event - void processEventData(soa::Join::iterator const& collision, emcClusters const& clusters) + void processEventData(soa::Join::iterator const& collision, EmcClusters const& clusters) { if (!isEventAccepted(collision, clusters)) { return; @@ -982,7 +1123,6 @@ struct GammaJetTreeProducer { eventsTable(collision.multFT0M(), collision.centFT0M(), collision.rho(), collision.eventSel(), collision.trackOccupancyInTimeRange(), collision.alias_raw()); collisionMapping[collision.globalIndex()] = eventsTable.lastIndex(); } - PROCESS_SWITCH(GammaJetTreeProducer, processEventData, "Process event data", true); using MCCol = o2::soa::Join; @@ -990,7 +1130,7 @@ struct GammaJetTreeProducer { /// \param collision The collision to process /// \param clusters The EMCAL clusters in the event /// \param mcCollisions The MC collisions collection - void processEventMC(soa::Join::iterator const& collision, emcClusters const& clusters, MCCol const&) + void processEventMC(soa::Join::iterator const& collision, EmcClusters const& clusters, MCCol const&) { if (!isEventAccepted(collision, clusters)) { return; @@ -1021,9 +1161,8 @@ struct GammaJetTreeProducer { if (std::find(mcCollisionsMultiRecCollisions.begin(), mcCollisionsMultiRecCollisions.end(), mcCollision.globalIndex()) != mcCollisionsMultiRecCollisions.end()) { isMultipleAssigned = true; } - mcEventsTable(eventsTable.lastIndex(), mcCollision.weight(), mcCollision.rho(), isMultipleAssigned); + mcEventsTable(eventsTable.lastIndex(), mcCollision.weight(), mcCollision.rho(), mcCollision.ptHard(), isMultipleAssigned); } - PROCESS_SWITCH(GammaJetTreeProducer, processEventMC, "Process MC event MC", false); // --------------------- // Processing functions can be safely added below this line @@ -1032,19 +1171,20 @@ struct GammaJetTreeProducer { // define cluster filter. It selects only those clusters which are of the type // sadly passing of the string at runtime is not possible for technical region so cluster definition is // an integer instead - PresliceUnsorted EMCTrackPerTrack = aod::jemctrack::trackId; + PresliceUnsorted emcTrackPerTrack = aod::jemctrack::trackId; // Process clusters /// \brief Processes clusters and fills cluster table /// \param collision The collision to process /// \param clusters The EMCAL clusters to process /// \param tracks The tracks collection /// \param emctracks The EMCAL tracks collection from track matching - void processClusters(soa::Join::iterator const& collision, emcClusters const& clusters, aod::JetTracks const& tracks, aod::JEMCTracks const& emctracks) + void processClusters(soa::Join::iterator const& collision, EmcClusters const& clusters, aod::JetTracks const& tracks, aod::JEMCTracks const& emctracks) { // event selection int32_t storedColIndex = getStoredColIndex(collision); - if (storedColIndex == -1) + if (storedColIndex == -1) { return; + } // loop over tracks one time for QA runTrackQA(collision, tracks); @@ -1058,8 +1198,8 @@ struct GammaJetTreeProducer { // fill histograms mHistograms.fill(HIST("clusterE"), cluster.energy()); - double isoraw = ch_iso_in_cone(cluster, isoR, false); - double perpconerho = ch_perp_cone_rho(cluster, isoR, false); + double isoraw = chIsoInCone(cluster, isoR, false); + double perpconerho = chPerpConeRho(cluster, isoR, false); // find closest matched track double dEta = 0; @@ -1073,17 +1213,16 @@ struct GammaJetTreeProducer { if (!isTrackSelected(track)) { continue; } - auto emcTracksPerTrack = emctracks.sliceBy(EMCTrackPerTrack, track.globalIndex()); + auto emcTracksPerTrack = emctracks.sliceBy(emcTrackPerTrack, track.globalIndex()); auto emcTrack = emcTracksPerTrack.iteratorAt(0); // find closest track that still has E/p < trackMatchingEoverP if (cluster.energy() / track.p() > trackMatchingEoverP) { continue; - } else { - dEta = cluster.eta() - emcTrack.etaEmcal(); - dPhi = RecoDecay::constrainAngle(RecoDecay::constrainAngle(emcTrack.phiEmcal(), -o2::constants::math::PI) - RecoDecay::constrainAngle(cluster.phi(), -o2::constants::math::PI), -o2::constants::math::PI); - p = track.p(); - break; } + dEta = cluster.eta() - emcTrack.etaEmcal(); + dPhi = RecoDecay::constrainAngle(RecoDecay::constrainAngle(emcTrack.phiEmcal(), -o2::constants::math::PI) - RecoDecay::constrainAngle(cluster.phi(), -o2::constants::math::PI), -o2::constants::math::PI); + p = track.p(); + break; } gammasTable(storedColIndex, cluster.energy(), cluster.definition(), cluster.eta(), cluster.phi(), cluster.m02(), cluster.m20(), cluster.nCells(), cluster.time(), cluster.isExotic(), cluster.distanceToBadChannel(), cluster.nlm(), isoraw, perpconerho, dPhi, dEta, p); } @@ -1093,70 +1232,84 @@ struct GammaJetTreeProducer { mHistograms.fill(HIST("trackPt"), track.pt()); } } - PROCESS_SWITCH(GammaJetTreeProducer, processClusters, "Process EMCal clusters", true); - /// \brief Processes MC cluster information (rec level) /// \param collision The collision to process /// \param mcClusters The MC clusters to process /// \param mcParticles The MC particles collection - void processClustersMCInfo(soa::Join::iterator const& collision, emcMCClusters const& mcClusters, aod::JMcParticles const& mcParticles) + void processClustersMCInfo(soa::Join::iterator const& collision, EmcMCClusters const& mcClusters, aod::JMcParticles const& mcParticles) { // event selection int32_t storedColIndex = getStoredColIndex(collision); - if (storedColIndex == -1) + if (storedColIndex == -1) { return; + } // loop over mcClusters // TODO: add weights for (const auto& mcCluster : mcClusters) { mHistograms.fill(HIST("clusterMC_E_All"), mcCluster.energy()); - uint16_t origin = getClusterOrigin(mcCluster, mcParticles); - float leadingEnergyFraction = mcCluster.amplitudeA()[0] / mcCluster.energy(); + auto [origin, mcIndex] = getClusterOrigin(mcCluster, mcParticles); + float leadingEnergyFraction = mcCluster.amplitudeA()[0]; // amplitudes are already stored as energy fractions // Fill MC origin QA histograms - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kPhoton))) { mHistograms.fill(HIST("clusterMC_E_Photon"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_Photon"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kPromptPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kPromptPhoton))) { mHistograms.fill(HIST("clusterMC_E_PromptPhoton"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_PromptPhoton"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kDirectPromptPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kDirectPromptPhoton))) { mHistograms.fill(HIST("clusterMC_E_DirectPromptPhoton"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_DirectPromptPhoton"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kFragmentationPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kFragmentationPhoton))) { mHistograms.fill(HIST("clusterMC_E_FragmentationPhoton"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_FragmentationPhoton"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kDecayPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kDecayPhoton))) { mHistograms.fill(HIST("clusterMC_E_DecayPhoton"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_DecayPhoton"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kDecayPhotonPi0))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kDecayPhotonPi0))) { mHistograms.fill(HIST("clusterMC_E_DecayPhotonPi0"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_DecayPhotonPi0"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kDecayPhotonEta))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kDecayPhotonEta))) { mHistograms.fill(HIST("clusterMC_E_DecayPhotonEta"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_DecayPhotonEta"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kMergedPi0))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kMergedPi0))) { mHistograms.fill(HIST("clusterMC_E_MergedPi0"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_MergedPi0"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kMergedEta))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kMergedEta))) { mHistograms.fill(HIST("clusterMC_E_MergedEta"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_MergedEta"), mcCluster.m02()); } - if (origin & (1 << static_cast(gjanalysis::ClusterOrigin::kConvertedPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ClusterOrigin::kConvertedPhoton))) { mHistograms.fill(HIST("clusterMC_E_ConvertedPhoton"), mcCluster.energy()); mHistograms.fill(HIST("clusterMC_m02_ConvertedPhoton"), mcCluster.m02()); } + + // determine properties fo particle that produced the given cluster + float mcIsolation = -1; + float mcEnergy = InvalidValue; + float mcPt = InvalidValue; + float mcEta = InvalidValue; + float mcPhi = InvalidValue; + if (mcIndex != -1) { + const auto& mcParticle = mcParticles.iteratorAt(mcIndex); + mcIsolation = chIsoInCone(mcParticle, isoR, true); + mcEnergy = mcParticle.energy(); + mcPt = mcParticle.pt(); + mcEta = mcParticle.eta(); + mcPhi = mcParticle.phi(); + } + // fill table - gammaMCInfosTable(storedColIndex, origin, leadingEnergyFraction); + gammaMCInfosTable(storedColIndex, origin, leadingEnergyFraction, mcEta, mcPhi, mcEnergy, mcPt, mcIsolation); } } - PROCESS_SWITCH(GammaJetTreeProducer, processClustersMCInfo, "Process MC cluster information", false); /// \brief Fills the charged jet table with jet information and calculates jet properties /// \param storedColIndex The stored collision index @@ -1177,7 +1330,7 @@ struct GammaJetTreeProducer { leadingTrackPt = constituent.pt(); } } - double perpconerho = ch_perp_cone_rho(jet, perpConeJetR, false); + double perpconerho = chPerpConeRho(jet, perpConeJetR, false); chargedJetsTable(storedColIndex, jet.pt(), jet.eta(), jet.phi(), jet.r(), jet.energy(), jet.mass(), jet.area(), leadingTrackPt, perpconerho, nconst); mHistograms.fill(HIST("chjetPtEtaPhi"), jet.pt(), jet.eta(), jet.phi()); mHistograms.fill(HIST("chjetPt"), jet.pt()); @@ -1203,14 +1356,14 @@ struct GammaJetTreeProducer { if (jet.pt() < jetPtMin) { return; } - for (auto& jetConstituent : jet.template tracks_as()) { + for (const auto& jetConstituent : jet.template tracks_as()) { fastjetutilities::fillTracks(jetConstituent, jetConstituents, jetConstituent.globalIndex()); } fastjet::ClusterSequenceArea clusterSeq(jetReclusterer.findJets(jetConstituents, jetReclustered)); jetReclustered = sorted_by_pt(jetReclustered); - if (jetReclustered.size() == 0) { + if (jetReclustered.empty()) { return; } @@ -1238,6 +1391,58 @@ struct GammaJetTreeProducer { jetSubstructuresTable(storedColIndex, energyMotherVec, ptLeadingVec, ptSubLeadingVec, thetaVec); } + /// \brief Fills the MC gen-level substructure table for each splitting in the jet + /// \param storedColIndex The stored collision index + /// \param jet The MC particle-level jet to process + /// \param mcparticles The MC particles collection + template + void fillMCSubstructureTable(int32_t storedColIndex, T const& jet, U const& /*mcparticles*/) + { + // adjust settings according to the jet radius + jetReclusterer.jetR = jet.r() / 100.0; + // clear vectors + energyMotherVec.clear(); + ptLeadingVec.clear(); + ptSubLeadingVec.clear(); + thetaVec.clear(); + jetReclustered.clear(); + jetConstituents.clear(); + + if (jet.pt() < jetPtMin) { + return; + } + for (const auto& jetConstituent : jet.template tracks_as()) { + fastjetutilities::fillTracks(jetConstituent, jetConstituents, jetConstituent.globalIndex()); + } + + fastjet::ClusterSequenceArea clusterSeq(jetReclusterer.findJets(jetConstituents, jetReclustered)); + jetReclustered = sorted_by_pt(jetReclustered); + + if (jetReclustered.empty()) { + return; + } + + fastjet::PseudoJet daughterSubJet = jetReclustered[0]; + fastjet::PseudoJet parentSubJet1; + fastjet::PseudoJet parentSubJet2; + + while (daughterSubJet.has_parents(parentSubJet1, parentSubJet2)) { + if (parentSubJet1.perp() < parentSubJet2.perp()) { + std::swap(parentSubJet1, parentSubJet2); + } + + energyMotherVec.push_back(daughterSubJet.e()); + ptLeadingVec.push_back(parentSubJet1.pt()); + ptSubLeadingVec.push_back(parentSubJet2.pt()); + thetaVec.push_back(parentSubJet1.delta_R(parentSubJet2)); + + // Continue with harder parent + daughterSubJet = parentSubJet1; + } + + mcJetSubstructuresTable(storedColIndex, energyMotherVec, ptLeadingVec, ptSubLeadingVec, thetaVec); + } + Filter jetCuts = aod::jet::pt > jetPtMin; /// \brief Processes charged jets and fills jet table /// \param collision The collision to process @@ -1247,8 +1452,9 @@ struct GammaJetTreeProducer { { // event selection int32_t storedColIndex = getStoredColIndex(collision); - if (storedColIndex == -1) + if (storedColIndex == -1) { return; + } // build kd tree for tracks (needed for perpendicular cone rho calculation) buildKdTree(collision, tracks); @@ -1264,9 +1470,8 @@ struct GammaJetTreeProducer { } } } - PROCESS_SWITCH(GammaJetTreeProducer, processChargedJetsData, "Process charged jets", true); - Preslice ParticlesPerMCCollisions = aod::jmcparticle::mcCollisionId; + Preslice particlesPerMcCollisions = aod::jmcparticle::mcCollisionId; /// \brief Processes MC particles and fills MC particle table /// \param collision The collision to process /// \param mcgenparticles The MC particles to process @@ -1274,15 +1479,16 @@ struct GammaJetTreeProducer { { // event selection int32_t storedColIndex = getStoredColIndex(collision); - if (storedColIndex == -1) + if (storedColIndex == -1) { return; + } if (!collision.has_mcCollision()) { return; } // only storing MC particles if we found a reconstructed collision - auto particlesPerMcCollision = mcgenparticles.sliceBy(ParticlesPerMCCollisions, collision.mcCollisionId()); + auto particlesPerMcCollision = mcgenparticles.sliceBy(particlesPerMcCollisions, collision.mcCollisionId()); // build kd tree for mc particles buildKdTree(collision, particlesPerMcCollision); @@ -1297,7 +1503,7 @@ struct GammaJetTreeProducer { // Particles produced in the collision including products of strong and // electromagnetic decay and excluding feed-down from weak decays of strange // particles. - if (!(particle.isPhysicalPrimary() || particle.pdgCode() == PDG_t::kPi0)) { + if (!particle.isPhysicalPrimary() && particle.pdgCode() != PDG_t::kPi0) { continue; } @@ -1305,46 +1511,55 @@ struct GammaJetTreeProducer { if (particle.pdgCode() != PDG_t::kPi0 && particle.pdgCode() != PDG_t::kGamma) { continue; } + // check the origin of the particle uint16_t origin = getMCParticleOrigin(particle); - double mcIsolation = ch_iso_in_cone(particle, isoR, true); + double mcIsolation = chIsoInCone(particle, isoR, true); mcParticlesTable(storedColIndex, particle.energy(), particle.eta(), particle.phi(), particle.pt(), particle.pdgCode(), mcIsolation, origin); + // DEBUGGING for photons. If it is a photon, print the origin and then print the chain + // leaving this in here + // if (particle.pdgCode() == PDG_t::kGamma) { + // LOG(info) << "Particle PDG: " << particle.pdgCode() << " isPhysicalPrimary: " << particle.isPhysicalPrimary() << " getGenStatusCode: " << particle.getGenStatusCode(); + // LOG(info) << "Origin: " << "kPromptPhoton (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kPromptPhoton))) << "), kDirectPromptPhoton (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDirectPromptPhoton))) << "), kFragmentationPhoton (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kFragmentationPhoton))) << "), kDecayPhoton (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhoton))) << "), kDecayPhotonPi0 (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhotonPi0))) << "), kDecayPhotonEta (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhotonEta))) << "), kDecayPhotonOther (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhotonOther))) << "), kPi0 (" << (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kPi0))) << ")"; + // LOG(info) << "Mother chain: "; + // printMotherChain(particle); + // } + // fill mc gen trigger particle histograms mHistograms.fill(HIST("mcGenTrigger_E"), particle.energy()); mHistograms.fill(HIST("mcGenTrigger_Eta"), particle.eta()); mHistograms.fill(HIST("mcGenTrigger_Phi"), particle.phi()); mHistograms.fill(HIST("mcGenTrigger_Pt"), particle.pt()); - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kPromptPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kPromptPhoton))) { mHistograms.fill(HIST("mcGenTrigger_E_PromptPhoton"), particle.energy()); } - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDirectPromptPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kDirectPromptPhoton))) { mHistograms.fill(HIST("mcGenTrigger_E_DirectPromptPhoton"), particle.energy()); } - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kFragmentationPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kFragmentationPhoton))) { mHistograms.fill(HIST("mcGenTrigger_E_FragmentationPhoton"), particle.energy()); } - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhoton))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kDecayPhoton))) { mHistograms.fill(HIST("mcGenTrigger_E_DecayPhoton"), particle.energy()); } - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhotonPi0))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kDecayPhotonPi0))) { mHistograms.fill(HIST("mcGenTrigger_E_DecayPhotonPi0"), particle.energy()); } - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhotonEta))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kDecayPhotonEta))) { mHistograms.fill(HIST("mcGenTrigger_E_DecayPhotonEta"), particle.energy()); } - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kDecayPhotonOther))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kDecayPhotonOther))) { mHistograms.fill(HIST("mcGenTrigger_E_DecayPhotonOther"), particle.energy()); } - if (origin & (1 << static_cast(gjanalysis::ParticleOrigin::kPi0))) { + if (TESTBIT(origin, static_cast(gjanalysis::ParticleOrigin::kPi0))) { mHistograms.fill(HIST("mcGenTrigger_E_Pi0"), particle.energy()); } } } - PROCESS_SWITCH(GammaJetTreeProducer, processMCParticles, "Process MC particles", false); // NOTE: The KD tree is now built lazily in each function that needs it, so execution order is no longer critical - Preslice PJetsPerMCCollisions = aod::jmcparticle::mcCollisionId; + Preslice pJetsPerMcCollisions = aod::jmcparticle::mcCollisionId; /// \brief Processes MC particle level charged jets and fills MC jet table /// \param collision The collision to process /// \param chargedJets The MC particle level charged jets to process @@ -1354,29 +1569,33 @@ struct GammaJetTreeProducer { { // event selection int32_t storedColIndex = getStoredColIndex(collision); - if (storedColIndex == -1) + if (storedColIndex == -1) { return; + } // loop over charged jets if (!collision.has_mcCollision()) { return; } // build kd tree for mc particles (needed for perpendicular cone rho calculation) - auto particlesPerMcCollision = mcgenparticles.sliceBy(ParticlesPerMCCollisions, collision.mcCollisionId()); + auto particlesPerMcCollision = mcgenparticles.sliceBy(particlesPerMcCollisions, collision.mcCollisionId()); buildKdTree(collision, particlesPerMcCollision); int localIndex = 0; - auto pjetsPerMcCollision = chargedJets.sliceBy(PJetsPerMCCollisions, collision.mcCollisionId()); + auto pjetsPerMcCollision = chargedJets.sliceBy(pJetsPerMcCollisions, collision.mcCollisionId()); for (const auto& pjet : pjetsPerMcCollision) { // fill MC particle level jet table - float perpconerho = ch_perp_cone_rho(pjet, perpConeJetR, true); + float perpconerho = chPerpConeRho(pjet, perpConeJetR, true); mcJetsTable(storedColIndex, pjet.pt(), pjet.eta(), pjet.phi(), pjet.r(), pjet.energy(), pjet.mass(), pjet.area(), perpconerho); mcJetIndexMapping[pjet.globalIndex()] = localIndex; localIndex++; mHistograms.fill(HIST("mcpJetPt"), pjet.pt()); + + if (calculateJetSubstructure) { + fillMCSubstructureTable(storedColIndex, pjet, mcgenparticles); + } } } - PROCESS_SWITCH(GammaJetTreeProducer, processChargedJetsMCP, "Process MC particle level jets", false); // NOTE: It is important that this function runs after the processChargedJetsMCP function (where the mc jet index mapping is built) using JetMCPTable = soa::Filtered>; @@ -1390,8 +1609,9 @@ struct GammaJetTreeProducer { { // event selection int32_t storedColIndex = getStoredColIndex(collision); - if (storedColIndex == -1) + if (storedColIndex == -1) { return; + } // build kd tree for tracks (needed for perpendicular cone rho calculation) buildKdTree(collision, tracks); @@ -1423,12 +1643,22 @@ struct GammaJetTreeProducer { chJetMCInfosTable(storedColIndex, iLocalIndexGeo, iLocalIndexPt); } } + // definition of all process switches, ordering is important! + PROCESS_SWITCH(GammaJetTreeProducer, processClearMaps, "process function that clears all the maps in each dataframe", true); // always run first + PROCESS_SWITCH(GammaJetTreeProducer, processMCCollisionsMatching, "Process MC event matching QA", false); + PROCESS_SWITCH(GammaJetTreeProducer, processEventData, "Process event data", true); + PROCESS_SWITCH(GammaJetTreeProducer, processEventMC, "Process MC event MC", false); + PROCESS_SWITCH(GammaJetTreeProducer, processClusters, "Process EMCal clusters", true); + PROCESS_SWITCH(GammaJetTreeProducer, processMCParticles, "Process MC particles", false); // has to run before processClustersMCInfo + PROCESS_SWITCH(GammaJetTreeProducer, processClustersMCInfo, "Process MC cluster information", false); + PROCESS_SWITCH(GammaJetTreeProducer, processChargedJetsData, "Process charged jets", true); + PROCESS_SWITCH(GammaJetTreeProducer, processChargedJetsMCP, "Process MC particle level jets", false); PROCESS_SWITCH(GammaJetTreeProducer, processChargedJetsMCD, "Process MC detector level jets", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { WorkflowSpec workflow{ - adaptAnalysisTask(cfgc, TaskName{"gamma-jet-tree-producer"})}; + adaptAnalysisTask(context)}; return workflow; } diff --git a/PWGJE/Tasks/hadronPhotonCorrelation.cxx b/PWGJE/Tasks/hadronPhotonCorrelation.cxx index 7040898d6d1..18e73087ff7 100644 --- a/PWGJE/Tasks/hadronPhotonCorrelation.cxx +++ b/PWGJE/Tasks/hadronPhotonCorrelation.cxx @@ -152,8 +152,8 @@ struct HadronPhotonCorrelation { AxisSpec axisEtaAssoc = {etaBinsAssoc, -etaMaxAssoc, etaMaxAssoc, "#eta"}; // Axis for eta distribution AxisSpec axisDeltaEta = {etaBinsTrig + etaBinsAssoc, -(etaMaxTrig + etaMaxAssoc), etaMaxTrig + etaMaxAssoc, "#Delta #eta"}; // Axis for Delta eta in correlations - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Generated histograms registry.add("generated/events/hEventStats", "Event statistics", kTH1F, {axisEventStats}); @@ -298,10 +298,10 @@ struct HadronPhotonCorrelation { bool isHardQCDgg2gg(const P& particles) { for (const auto& particle : particles) { - if (std::abs(particle.getGenStatusCode()) == pythiaCodeIncomingHard && (PDG_t)std::abs(particle.pdgCode()) != kGluon) { + if (std::abs(particle.getGenStatusCode()) == pythiaCodeIncomingHard && std::abs(particle.pdgCode()) != kGluon) { return false; } - if (std::abs(particle.getGenStatusCode()) == pythiaCodeOutgoingHard && (PDG_t)std::abs(particle.pdgCode()) != kGluon) { + if (std::abs(particle.getGenStatusCode()) == pythiaCodeOutgoingHard && std::abs(particle.pdgCode()) != kGluon) { return false; } } @@ -396,7 +396,7 @@ struct HadronPhotonCorrelation { return false; } - if ((PDG_t)std::abs(particle->GetPdgCode()) != kGamma) { + if (std::abs(particle->GetPdgCode()) != kGamma) { return false; } @@ -712,7 +712,7 @@ struct HadronPhotonCorrelation { if (!initParticle(track_assoc, false)) { continue; } - if ((PDG_t)std::abs(track_assoc.pdgCode()) != kGamma) { + if (std::abs(track_assoc.pdgCode()) != kGamma) { continue; } if (!track_assoc.isPhysicalPrimary() && track_assoc.getGenStatusCode() < 0) { @@ -722,7 +722,7 @@ struct HadronPhotonCorrelation { // Iterate through mother particles until original mother is reached auto origPhoton = track_assoc; auto mother = track_assoc.mothers_as().at(0); - while ((PDG_t)mother.pdgCode() == kGamma) { + while (mother.pdgCode() == kGamma) { origPhoton = mother; mother = mother.mothers_as().at(0); } @@ -968,7 +968,7 @@ struct HadronPhotonCorrelation { if (!initParticle(track_assoc)) { continue; } - if ((PDG_t)std::abs(track_assoc.pdgCode()) != kPiPlus) { + if (std::abs(track_assoc.pdgCode()) != kPiPlus) { continue; } @@ -1050,7 +1050,7 @@ struct HadronPhotonCorrelation { if (!initTrack(track_assoc)) { continue; } - if ((PDG_t)std::abs(track_assoc.mcParticle().pdgCode()) != kPiPlus) { + if (std::abs(track_assoc.mcParticle().pdgCode()) != kPiPlus) { continue; } @@ -1101,7 +1101,7 @@ struct HadronPhotonCorrelation { if (pdgParticle->Charge() != 0.) { continue; } // remove charged particles - if (std::abs(track_assoc.pdgCode()) < pidCodeHadronCut || (PDG_t)track_assoc.pdgCode() == kNeutron) { + if (std::abs(track_assoc.pdgCode()) < pidCodeHadronCut || track_assoc.pdgCode() == kNeutron) { continue; } // remove non-hadrons and neutrons @@ -1121,7 +1121,7 @@ struct HadronPhotonCorrelation { } for (const auto& daughter : daughters) { - if ((PDG_t)std::abs(daughter.pdgCode()) != kGamma) { + if (std::abs(daughter.pdgCode()) != kGamma) { continue; } if (!daughter.isPhysicalPrimary() && daughter.getGenStatusCode() == -1) { diff --git a/PWGJE/Tasks/hfFragmentationFunction.cxx b/PWGJE/Tasks/hfFragmentationFunction.cxx index abcdcdb28b9..25ffa86f906 100644 --- a/PWGJE/Tasks/hfFragmentationFunction.cxx +++ b/PWGJE/Tasks/hfFragmentationFunction.cxx @@ -17,9 +17,7 @@ /// The task store data relevant to the calculation of hadronization observables radial /// profile and/or jet momentum fraction for charmed hadrons -#include "PWGHF/Core/DecayChannels.h" #include "PWGJE/Core/JetDerivedDataUtilities.h" -#include "PWGJE/Core/JetHFUtilities.h" #include "PWGJE/Core/JetUtilities.h" #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" @@ -64,6 +62,25 @@ double deltaPhi(double phi1, double phi2) return std::abs(dphi); } +// +/// Collision counter selection indexes +/// +/// The collision selection is done and stored in multiple steps, for later QA analysis. +/// In order not to hard code which bins should be filled throughout different process +/// function, this namespace with enums is create +namespace collision_selections +{ +enum CollisionSelectionStep { + StepMcCollisions = 0, ///< raw mccollisions with no selection, starts with 0 + StepMcCollisionsZCut, ///< mccollisions with z vtx selection + StepMcCollisionsZCutSel8, ///< mccollisions with z vtx and sel8 mc emulated selections + StepMcCollisionsZCutSel8HasCollisions, ///< mccollisions with z vtx and sel8 mc emulated selections, with at least one reconstructed collisions + StepMcCollisionsZCutSel8NoSplitCollisions, ///< mccollisions with z vtx and sel8 mc emulated selections, with no split reconstructed collisions + StepRecoCollisions, ///< raw reconstructed collisions after previous mccollisions selection + StepRecoCollisionsZcut, ///< reconstructed collisions with z vtx selection after previous mccollisions selection + StepRecoCollisionsZcutSel8 ///< reconstructed collisions with z vtx and sel8 selections after previous mccollisions selection +}; +} // namespace collision_selections // creating table for storing distance data namespace o2::aod { @@ -86,17 +103,23 @@ DECLARE_SOA_COLUMN(HfMlScore1, hfMlScore1, float); DECLARE_SOA_COLUMN(HfMlScore2, hfMlScore2, float); DECLARE_SOA_COLUMN(HfMatchedFrom, hfMatchedFrom, int); DECLARE_SOA_COLUMN(HfSelectedAs, hfSelectedAs, int); +DECLARE_SOA_COLUMN(RecoCollZCut, recoCollZCut, bool); +DECLARE_SOA_COLUMN(RecoCollSel8, recoCollSel8, bool); DECLARE_SOA_COLUMN(McJetHfDist, mcJetHfDist, float); DECLARE_SOA_COLUMN(McJetPt, mcJetPt, float); DECLARE_SOA_COLUMN(McJetEta, mcJetEta, float); DECLARE_SOA_COLUMN(McJetPhi, mcJetPhi, float); -DECLARE_SOA_COLUMN(McJetNConst, mcJetNConst, float); +DECLARE_SOA_COLUMN(McJetNConst, mcJetNConst, int); DECLARE_SOA_COLUMN(McHfPt, mcHfPt, float); DECLARE_SOA_COLUMN(McHfEta, mcHfEta, float); DECLARE_SOA_COLUMN(McHfPhi, mcHfPhi, float); DECLARE_SOA_COLUMN(McHfY, mcHfY, float); DECLARE_SOA_COLUMN(McHfPrompt, mcHfPrompt, bool); DECLARE_SOA_COLUMN(McHfMatch, mcHfMatch, bool); +DECLARE_SOA_COLUMN(McCollZCut, mcCollZCut, bool); +DECLARE_SOA_COLUMN(McCollSel8, mcCollSel8, bool); +DECLARE_SOA_COLUMN(McCollNumCollisions, mcCollNumCollisions, int); +DECLARE_SOA_COLUMN(McCollNumSelectedCollisions, mcCollNumSelectedCollisions, int); } // namespace jet_distance DECLARE_SOA_TABLE(JetDistanceTable, "AOD", "JETDISTTABLE", jet_distance::JetHfDist, @@ -153,6 +176,10 @@ DECLARE_SOA_TABLE(MatchJetDistanceTable, "AOD", "MATCHTABLE", jet_distance::McHfPhi, jet_distance::McHfY, jet_distance::McHfPrompt, + jet_distance::McCollZCut, + jet_distance::McCollSel8, + jet_distance::McCollNumCollisions, + jet_distance::McCollNumSelectedCollisions, jet_distance::JetHfDist, jet_distance::JetPt, jet_distance::JetEta, @@ -168,7 +195,9 @@ DECLARE_SOA_TABLE(MatchJetDistanceTable, "AOD", "MATCHTABLE", jet_distance::HfMlScore1, jet_distance::HfMlScore2, jet_distance::HfMatchedFrom, - jet_distance::HfSelectedAs); + jet_distance::HfSelectedAs, + jet_distance::RecoCollZCut, + jet_distance::RecoCollSel8); } // namespace o2::aod struct HfFragmentationFunction { @@ -196,21 +225,25 @@ struct HfFragmentationFunction { Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; + Configurable applyMcEventSelection{"applyMcEventSelection", false, "Choose a boolean value"}; + Configurable applyRecoEventSelection{"applyRecoEventSelection", true, "data: apply z-vertex and event selection; MC: reject generated events whose reconstructed collisions all fail them"}; + Configurable rejectMCCollisionNoRecoCollision{"rejectMCCollisionNoRecoCollision", false, "reject generated events with no reconstructed collision"}; + Configurable rejectSplitCollisions{"rejectSplitCollisions", false, "reject generated events associated to more than one reconstructed collision"}; std::vector eventSelectionBits; void init(InitContext const&) { // initialise event selection: - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // create histograms - // collision system histograms - std::vector histLabels = {"mccollisions", "z_cut", "collisions", "sel8"}; + // collision counter histograms + std::vector histLabels = {"mccollisions", "mccollisions+z_cut", "mccollisions+z_{cut}+sel8", "mccollisions+z_{cut}+sel8+HasCollisions", "mccollisions+z_{cut}+sel8+NoSplitVtx", "collisions", "collisions+z_{cut}", "collisions+z_{cut}+sel8"}; registry.add("h_collision_counter", ";# of collisions;", HistType::kTH1F, {{static_cast(histLabels.size()), 0.0, static_cast(histLabels.size())}}); - auto counter = registry.get(HIST("h_collision_counter")); + auto collCounter = registry.get(HIST("h_collision_counter")); for (std::vector::size_type iCounter = 0; iCounter < histLabels.size(); iCounter++) { - counter->GetXaxis()->SetBinLabel(iCounter + 1, histLabels[iCounter].data()); + collCounter->GetXaxis()->SetBinLabel(iCounter + 1, histLabels[iCounter].data()); } registry.add("h_jet_counter", ";# of jets;", {HistType::kTH1F, {{6, 0., 3.0}}}); auto jetCounter = registry.get(HIST("h_jet_counter")); @@ -242,11 +275,11 @@ struct HfFragmentationFunction { aod::JetTracks const&) { // apply event selection and fill histograms for sanity check - registry.fill(HIST("h_collision_counter"), 2.0); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisions); + if (applyRecoEventSelection && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut))) { return; } - registry.fill(HIST("h_collision_counter"), 3.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcutSel8); for (const auto& jet : jets) { // fill jet counter histogram @@ -317,22 +350,22 @@ struct HfFragmentationFunction { { for (const auto& mccollision : mccollisions) { - registry.fill(HIST("h_collision_counter"), 0.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisions); // skip collisions outside of |z| < vertexZCut - if (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits) || !(std::abs(mccollision.posZ()) < vertexZCut)) { + if (applyMcEventSelection && (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits) || !(std::abs(mccollision.posZ()) < vertexZCut))) { continue; } - registry.fill(HIST("h_collision_counter"), 1.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8); // reconstructed collisions associated to same mccollision const auto collisionsPerMCCollision = collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); for (const auto& collision : collisionsPerMCCollision) { - registry.fill(HIST("h_collision_counter"), 2.0); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisions); + if (applyRecoEventSelection && (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut))) { continue; } - registry.fill(HIST("h_collision_counter"), 3.0); + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcutSel8); // d0 detector level jets associated to the current same collision const auto d0mcdJetsPerCollision = mcdjets.sliceBy(d0MCDJetsPerCollisionPreslice, collision.globalIndex()); @@ -348,19 +381,12 @@ struct HfFragmentationFunction { } // reflection information for storage: D0 = +1, D0bar = -1, neither = 0 - int matchedFrom = 0; - int decayChannel = o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK; int selectedAs = 0; - if (mcdd0cand.flagMcMatchRec() == decayChannel) { // matched to D0 on truth level - matchedFrom = 1; - } else if (mcdd0cand.flagMcMatchRec() == -decayChannel) { // matched to D0bar on truth level - matchedFrom = -1; - } // bitwise AND operation: Checks whether BIT(i) is set, regardless of other bits - if (mcdd0cand.candidateSelFlag() & BIT(0)) { // CandidateSelFlag == BIT(0) -> selected as D0 + if ((mcdd0cand.candidateSelFlag() & BIT(0)) != 0) { // CandidateSelFlag == BIT(0) -> selected as D0 selectedAs = 1; - } else if (mcdd0cand.candidateSelFlag() & BIT(1)) { // CandidateSelFlag == BIT(1) -> selected as D0bar + } else if ((mcdd0cand.candidateSelFlag() & BIT(1)) != 0) { // CandidateSelFlag == BIT(1) -> selected as D0bar selectedAs = -1; } @@ -368,8 +394,8 @@ struct HfFragmentationFunction { mcddistJetTable(jetutilities::deltaR(mcdjet, mcdd0cand), mcdjet.pt(), mcdjet.eta(), mcdjet.phi(), mcdjet.tracks_as().size() + mcdjet.candidates_as().size(), // detector level jet mcdd0cand.pt(), mcdd0cand.eta(), mcdd0cand.phi(), mcdd0cand.m(), mcdd0cand.y(), (mcdd0cand.originMcRec() == RecoDecay::OriginType::Prompt), // detector level D0 candidate - mcdjet.has_matchedJetCand(), mcdd0cand.mlScores()[0], mcdd0cand.mlScores()[1], mcdd0cand.mlScores()[2], // // Machine Learning PID scores: background, prompt, non-prompt - matchedFrom, selectedAs); // D0 = +1, D0bar = -1, neither = 0 + mcdjet.has_matchedJetCand(), mcdd0cand.mlScores()[0], mcdd0cand.mlScores()[1], mcdd0cand.mlScores()[2], // Machine Learning PID scores: background, prompt, non-prompt + static_cast(mcdd0cand.flagMcMatchRec()), selectedAs); // +1/-1 = D0(bar)→Kπ, ±2..5 = other D0 channels, 0 = no match } } @@ -396,25 +422,66 @@ struct HfFragmentationFunction { } PROCESS_SWITCH(HfFragmentationFunction, processMcEfficiency, "non-matched and matched MC HF and jets", false); - template + template void analyzeMC(TMCPJetsPerMCCollisionPreslice const& MCPJetsPerMCCollisionPreslice, + TMCDJetsPerCollisionPreslice const& MCDJetsPerCollisionPreslice, aod::JetMcCollisions const& mccollisions, aod::JetCollisionsMCD const& collisions, - TJetsMCD const&, TJetsMCP const& mcpjets, - TCandidatesMCD const&, + TJetsMCD const& mcdjets, TCandidatesMCP const&, - aod::JetTracks const&, - aod::JetParticles const&) + TCandidatesMCD const&, + aod::JetParticles const&, + aod::JetTracks const&) { for (const auto& mccollision : mccollisions) { - registry.fill(HIST("h_collision_counter"), 0.0); + + // --- begin event selection + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisions); // skip collisions outside of |z| < vertexZCut - if (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits) || !(std::abs(mccollision.posZ()) < vertexZCut)) { + if (applyMcEventSelection && !(std::abs(mccollision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCut); + if (applyMcEventSelection && !jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8); + + // reconstructed collisions associated to this mccollision + const auto collisionsPerMCCollision = collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); + // only consider events with at least one reconstructed collision + if (rejectMCCollisionNoRecoCollision && (collisionsPerMCCollision.size() == 0)) { + continue; + } + // only consider events with no split vertices (one mccollision-to-one collision) + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8HasCollisions); + if (rejectSplitCollisions && collisionsPerMCCollision.size() > 1) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepMcCollisionsZCutSel8NoSplitCollisions); + + int numSelectedCollisions = 0; + for (const auto& collision : collisionsPerMCCollision) { + + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisions); + if (!(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcut); + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + continue; + } + registry.fill(HIST("h_collision_counter"), collision_selections::StepRecoCollisionsZcutSel8); + numSelectedCollisions++; + } // end of collisions loop + + // separate reconstructed collision selection from the case with no existing reconstructed collision per mccollision + if (applyRecoEventSelection && (numSelectedCollisions == 0) && (collisionsPerMCCollision.size() != 0)) { continue; } - registry.fill(HIST("h_collision_counter"), 1.0); + // --- begin particle level jets storage // hf particle level jets associated to same mccollision const auto mcpJetsPerMCCollision = mcpjets.sliceBy(MCPJetsPerMCCollisionPreslice, mccollision.globalIndex()); for (const auto& mcpjet : mcpJetsPerMCCollision) { @@ -431,83 +498,109 @@ struct HfFragmentationFunction { for (const auto& mcdjet : mcpjet.template matchedJetCand_as()) { registry.fill(HIST("h_jet_counter"), 2.0); - // apply collision sel8 selection on detector level jet's collision - const auto& collision = collisions.iteratorAt(mcdjet.collisionId()); - registry.fill(HIST("h_collision_counter"), 2.0); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { - continue; - } - registry.fill(HIST("h_collision_counter"), 3.0); - // obtain leading HF candidate in jet auto mcdcand = mcdjet.template candidates_first_as(); - // reflection information for storage: HF = +1, HFbar = -1, neither = 0 - int matchedFrom = 0; - int decayChannel = 0; - if (jethfutilities::isD0Table()) { - decayChannel = o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK; - } else if (jethfutilities::isLcTable()) { - decayChannel = o2::hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi; - } int selectedAs = 0; - if (mcdcand.flagMcMatchRec() == decayChannel) { // matched to HF on truth level - matchedFrom = 1; - } else if (mcdcand.flagMcMatchRec() == -decayChannel) { // matched to HFbar on truth level - matchedFrom = -1; - } // bitwise AND operation: Checks whether BIT(i) is set, regardless of other bits - if (mcdcand.candidateSelFlag() & BIT(0)) { // CandidateSelFlag == BIT(0) -> selected as HF + if ((mcdcand.candidateSelFlag() & BIT(0)) != 0) { // CandidateSelFlag == BIT(0) -> selected as HF selectedAs = 1; - } else if (mcdcand.candidateSelFlag() & BIT(1)) { // CandidateSelFlag == BIT(1) -> selected as HFbar + } else if ((mcdcand.candidateSelFlag() & BIT(1)) != 0) { // CandidateSelFlag == BIT(1) -> selected as HFbar selectedAs = -1; } + // fetch collision associated to the detector level jet + const auto& collision = mcdjet.template collision_as(); + // store matched particle and detector level data in one single table (calculate angular distance in eta-phi plane on the fly) matchJetTable(jetutilities::deltaR(mcpjet, mcpcand), mcpjet.pt(), mcpjet.eta(), mcpjet.phi(), mcpjet.template tracks_as().size() + mcpjet.template candidates_as().size(), // particle level jet mcpcand.pt(), mcpcand.eta(), mcpcand.phi(), mcpcand.y(), (mcpcand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level HF - jetutilities::deltaR(mcdjet, mcdcand), mcdjet.pt(), mcdjet.eta(), mcdjet.phi(), mcdjet.template tracks_as().size() + +mcdjet.template candidates_as().size(), // detector level jet + std::abs(mccollision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits), static_cast(collisionsPerMCCollision.size()), numSelectedCollisions, // MC collision selections + jetutilities::deltaR(mcdjet, mcdcand), mcdjet.pt(), mcdjet.eta(), mcdjet.phi(), mcdjet.template tracks_as().size() + mcdjet.template candidates_as().size(), // detector level jet mcdcand.pt(), mcdcand.eta(), mcdcand.phi(), mcdcand.m(), mcdcand.y(), (mcdcand.originMcRec() == RecoDecay::OriginType::Prompt), // detector level HF mcdcand.mlScores()[0], mcdcand.mlScores()[1], mcdcand.mlScores()[2], // Machine Learning PID scores: background, prompt, non-prompt - matchedFrom, selectedAs); // HF = +1, HFbar = -1, neither = 0 + static_cast(mcdcand.flagMcMatchRec()), selectedAs, // HF = +1, HFbar = -1, neither = 0 + std::abs(collision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(collision, eventSelectionBits)); // Reconstructed collision selections } } else { // store matched particle and detector level data in one single table (calculate angular distance in eta-phi plane on the fly) - matchJetTable(jetutilities::deltaR(mcpjet, mcpcand), mcpjet.pt(), mcpjet.eta(), mcpjet.phi(), mcpjet.template tracks_as().size() + +mcpjet.template candidates_as().size(), // particle level jet - mcpcand.pt(), mcpcand.eta(), mcpcand.phi(), mcpcand.y(), (mcpcand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level HF - -2, -2, -2, -2, -2, // detector level jet - -2, -2, -2, -2, -2, -2, // detector level HF - -2, -2, -2, // Machine Learning PID scores: background, prompt, non-prompt - -2, -2); // HF = +1, HFbar = -1, neither = 0 + matchJetTable(jetutilities::deltaR(mcpjet, mcpcand), mcpjet.pt(), mcpjet.eta(), mcpjet.phi(), mcpjet.template tracks_as().size() + mcpjet.template candidates_as().size(), // particle level jet + mcpcand.pt(), mcpcand.eta(), mcpcand.phi(), mcpcand.y(), (mcpcand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level HF + std::abs(mccollision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits), static_cast(collisionsPerMCCollision.size()), numSelectedCollisions, // MC collision selections + -2, -2, -2, -2, -2, // no detector-level jet found + -2, -2, -2, -2, -2, false, // no detector-level jet found + -2, -2, -2, // no detector-level jet found + -2, -2, // no detector-level jet found + false, false); // no detector-level jet found } } // end of mcpjets loop + + // --- begin non-matched detector level jets storage (fake candidates and correlated background if present) + // reconstructed collisions associated to same mccollision + for (const auto& collision : collisionsPerMCCollision) { + + // d0 detector level jets associated to the current same collision + const auto mcdJetsPerCollision = mcdjets.sliceBy(MCDJetsPerCollisionPreslice, collision.globalIndex()); + for (const auto& mcdjet : mcdJetsPerCollision) { + + registry.fill(HIST("h_jet_counter"), 0.5); + + // obtain leading HF candidate in jet + auto mcdcand = mcdjet.template candidates_first_as(); + + if (mcdjet.has_matchedJetCand()) { + registry.fill(HIST("h_jet_counter"), 1.5); + } else { // store the detector level non-matched candidates + + // reflection information for storage: D0 = +1, D0bar = -1, neither = 0 + int selectedAs = 0; + + // bitwise AND operation: Checks whether BIT(i) is set, regardless of other bits + if ((mcdcand.candidateSelFlag() & BIT(0)) != 0) { // CandidateSelFlag == BIT(0) -> selected as D0 + selectedAs = 1; + } else if ((mcdcand.candidateSelFlag() & BIT(1)) != 0) { // CandidateSelFlag == BIT(1) -> selected as D0bar + selectedAs = -1; + } + + // store matched particle and detector level data in one single table (calculate angular distance in eta-phi plane on the fly) + matchJetTable(-2, -2, -2, -2, -2, // particle level jet + -2, -2, -2, -2, false, // particle level HF + std::abs(mccollision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits), static_cast(collisionsPerMCCollision.size()), numSelectedCollisions, // MC collision selections + jetutilities::deltaR(mcdjet, mcdcand), mcdjet.pt(), mcdjet.eta(), mcdjet.phi(), mcdjet.template tracks_as().size() + mcdjet.template candidates_as().size(), // detector level jet + mcdcand.pt(), mcdcand.eta(), mcdcand.phi(), mcdcand.m(), mcdcand.y(), (mcdcand.originMcRec() == RecoDecay::OriginType::Prompt), // detector level HF + mcdcand.mlScores()[0], mcdcand.mlScores()[1], mcdcand.mlScores()[2], // Machine Learning PID scores: background, prompt, non-prompt + static_cast(mcdcand.flagMcMatchRec()), selectedAs, // HF = +1, HFbar = -1, neither = 0 + std::abs(collision.posZ()) < vertexZCut, jetderiveddatautilities::selectCollision(collision, eventSelectionBits)); // Reconstructed collision selections + } + } // end of non-matched detector level jets loop + } // end of collisions loop } // end of mccollisions loop } // end of analyzeMC function void processD0MC(aod::JetMcCollisions const& mccollisions, aod::JetCollisionsMCD const& collisions, - JetD0MCDTable const& mcdjets, JetD0MCPTable const& mcpjets, - aod::CandidatesD0MCD const& mcdcands, + JetD0MCDTable const& mcdjets, aod::CandidatesD0MCP const& mcpcands, - aod::JetTracks const& jettracks, - aod::JetParticles const& jetparticles) + aod::CandidatesD0MCD const& mcdcands, + aod::JetParticles const& jetparticles, + aod::JetTracks const& jettracks) { - analyzeMC, JetD0MCDTable, JetD0MCPTable, aod::CandidatesD0MCD, aod::CandidatesD0MCP>(d0MCPJetsPerMCCollisionPreslice, mccollisions, collisions, mcdjets, mcpjets, mcdcands, mcpcands, jettracks, jetparticles); + analyzeMC, Preslice, JetD0MCPTable, JetD0MCDTable, aod::CandidatesD0MCP, aod::CandidatesD0MCD>(d0MCPJetsPerMCCollisionPreslice, d0MCDJetsPerCollisionPreslice, mccollisions, collisions, mcpjets, mcdjets, mcpcands, mcdcands, jetparticles, jettracks); } PROCESS_SWITCH(HfFragmentationFunction, processD0MC, "Store all simulated D0 jets information with matched candidate (if any found)", false); void processLcMC(aod::JetMcCollisions const& mccollisions, aod::JetCollisionsMCD const& collisions, - JetLcMCDTable const& mcdjets, JetLcMCPTable const& mcpjets, - aod::CandidatesLcMCD const& mcdcands, + JetLcMCDTable const& mcdjets, aod::CandidatesLcMCP const& mcpcands, - aod::JetTracks const& jettracks, - aod::JetParticles const& jetparticles) + aod::CandidatesLcMCD const& mcdcands, + aod::JetParticles const& jetparticles, + aod::JetTracks const& jettracks) { - analyzeMC, JetLcMCDTable, JetLcMCPTable, aod::CandidatesLcMCD, aod::CandidatesLcMCP>(lcMCPJetsPerMCCollisionPreslice, mccollisions, collisions, mcdjets, mcpjets, mcdcands, mcpcands, jettracks, jetparticles); + analyzeMC, Preslice, JetLcMCPTable, JetLcMCDTable, aod::CandidatesLcMCP, aod::CandidatesLcMCD>(lcMCPJetsPerMCCollisionPreslice, lcMCDJetsPerCollisionPreslice, mccollisions, collisions, mcpjets, mcdjets, mcpcands, mcdcands, jetparticles, jettracks); } PROCESS_SWITCH(HfFragmentationFunction, processLcMC, "Store all simulated Lc jets information with matched candidate (if any found)", false); }; diff --git a/PWGJE/Tasks/jetBackgroundAnalysis.cxx b/PWGJE/Tasks/jetBackgroundAnalysis.cxx index f7a0c56c7ac..6a5a8aaaae3 100644 --- a/PWGJE/Tasks/jetBackgroundAnalysis.cxx +++ b/PWGJE/Tasks/jetBackgroundAnalysis.cxx @@ -24,11 +24,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include @@ -55,6 +57,7 @@ struct JetBackgroundAnalysisTask { Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum track occupancy of collisions in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events"}; Configurable nBinsFluct{"nBinsFluct", 1000, "number of bins for flucuations axes"}; + Configurable deltaPtMinMaxFluct{"deltaPtMinMaxFluct", 200, "maximum and minimum delta pt value for fluctuation axes"}; Configurable trackEtaMin{"trackEtaMin", -0.9, "minimum eta acceptance for tracks"}; Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum eta acceptance for tracks"}; @@ -73,15 +76,17 @@ struct JetBackgroundAnalysisTask { int trackSelection = -1; TRandom3 randomNumber{}; + o2::framework::Service pdgDatabase; + void init(o2::framework::InitContext&) { // selection settings initialisation - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); randomNumber.SetSeed(0); // Axes definitions - AxisSpec bkgFluctuationsAxis = {nBinsFluct, -100.0, 100.0, "#delta #it{p}_{T} (GeV/#it{c})"}; + AxisSpec bkgFluctuationsAxis = {nBinsFluct, -deltaPtMinMaxFluct, deltaPtMinMaxFluct, "#delta #it{p}_{T} (GeV/#it{c})"}; // histogram definitions @@ -92,6 +97,10 @@ struct JetBackgroundAnalysisTask { registry.add("h2_centrality_rho", "; centrality; #it{rho} (GeV/area);", {HistType::kTH2F, {{1100, 0., 110.}, {400, 0., 400.0}}}); registry.add("h2_centrality_rhom", ";centrality; #it{rho}_{m} (GeV/area)", {HistType::kTH2F, {{1100, 0., 110.}, {100, 0., 100.0}}}); } + if (doprocessRhoMCP) { + registry.add("h2_nparticles_rho_mcp", "; N_{tracks}; #it{rho} (GeV/area);", {HistType::kTH2F, {{10000, 0.0, 10000.0}, {400, 0.0, 400.0}}}); + registry.add("h2_nparticles_rhom_mcp", "; N_{tracks}; #it{rho}_{m} (GeV/area);", {HistType::kTH2F, {{10000, 0.0, 10000.0}, {100, 0.0, 100.0}}}); + } if (doprocessBkgFluctuationsData || doprocessBkgFluctuationsMCD) { registry.add("h2_centrality_rhorandomcone", "; centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH2F, {{1100, 0., 110.}, bkgFluctuationsAxis}}); @@ -100,10 +109,20 @@ struct JetBackgroundAnalysisTask { registry.add("h2_centrality_rhorandomconerandomtrackdirectionwithoutoneleadingjets", "; centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH2F, {{1100, 0., 110.}, bkgFluctuationsAxis}}); registry.add("h2_centrality_rhorandomconerandomtrackdirectionwithouttwoleadingjets", "; centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH2F, {{1100, 0., 110.}, bkgFluctuationsAxis}}); } + + if (doprocessBkgFluctuationsMCP) { + registry.add("h_rhorandomcone", "; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH1F, {bkgFluctuationsAxis}}); + registry.add("h_rhorandomconerandomtrackdirection", "; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH1F, {bkgFluctuationsAxis}}); + registry.add("h_rhorandomconewithoutleadingjet", "; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH1F, {bkgFluctuationsAxis}}); + registry.add("h_rhorandomconerandomtrackdirectionwithoutoneleadingjets", "; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH1F, {bkgFluctuationsAxis}}); + registry.add("h_rhorandomconerandomtrackdirectionwithouttwoleadingjets", "; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTH1F, {bkgFluctuationsAxis}}); + } } Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); + Filter particleCuts = (aod::jmcparticle::eta > trackEtaMin && aod::jmcparticle::eta < trackEtaMax); Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut); + Filter mcEventCuts = (nabs(aod::jmccollision::posZ) < vertexZCut); template bool trackIsInJet(TTracks const& track, TJets const& jet) @@ -124,7 +143,7 @@ struct JetBackgroundAnalysisTask { float randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -137,8 +156,8 @@ struct JetBackgroundAnalysisTask { float randomConePtRandomTrackDirection = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtRandomTrackDirection += track.pt(); } @@ -151,7 +170,7 @@ struct JetBackgroundAnalysisTask { const bool hasSub = jets.size() >= 2; float randomConePtWithoutLeadingJet = randomConePt; if (hasLead) { - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; @@ -159,14 +178,14 @@ struct JetBackgroundAnalysisTask { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePtWithoutLeadingJet = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePtWithoutLeadingJet += track.pt(); @@ -185,10 +204,10 @@ struct JetBackgroundAnalysisTask { randomConePtWithoutTwoLeadJet = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, static_cast(-o2::constants::math::PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { - const bool inLead = hasLead && trackIsInJet(track, jets.iteratorAt(0)); + const bool inLead = trackIsInJet(track, jets.iteratorAt(0)); const bool inSub = hasSub && trackIsInJet(track, jets.iteratorAt(1)); if (!inLead) { randomConePtWithoutOneLeadJet += track.pt(); @@ -204,6 +223,86 @@ struct JetBackgroundAnalysisTask { registry.fill(HIST("h2_centrality_rhorandomconerandomtrackdirectionwithouttwoleadingjets"), centrality, randomConePtWithoutTwoLeadJet - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); } + template + void bkgFluctuationsRandomConeMCP(TCollisions const& collision, TJets const& jets, TTracks const& tracks) + { + float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); + float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); + float randomConePt = 0; + for (auto const& track : tracks) { + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); + float dEta = track.eta() - randomConeEta; + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + randomConePt += track.pt(); + } + } + registry.fill(HIST("h_rhorandomcone"), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + + // randomised eta,phi for tracks, to assess part of fluctuations coming from statistically independently emitted particles + float randomConePtRandomTrackDirection = 0; + for (auto const& track : tracks) { + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + randomConePtRandomTrackDirection += track.pt(); + } + } + registry.fill(HIST("h_rhorandomconerandomtrackdirection"), randomConePtRandomTrackDirection - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + + // removing the leading jet from the random cone + const bool hasLead = jets.size() >= 1; + const bool hasSub = jets.size() >= 2; + float randomConePtWithoutLeadingJet = randomConePt; + if (hasLead) { + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); + float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; + + bool jetWasInCone = false; + while ((randomConeLeadJetDeltaR <= 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < jets.iteratorAt(0).r() / 100.0 + randomConeR)) || (randomConeLeadJetDeltaR > 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < randomConeLeadJetDeltaR))) { + jetWasInCone = true; + randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); + randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); + dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; + } + if (jetWasInCone) { + randomConePtWithoutLeadingJet = 0.0; + for (auto const& track : tracks) { + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); + float dEta = track.eta() - randomConeEta; + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + randomConePtWithoutLeadingJet += track.pt(); + } + } + } + } + registry.fill(HIST("h_rhorandomconewithoutleadingjet"), randomConePtWithoutLeadingJet - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + + // randomised eta,phi for tracks, to assess part of fluctuations coming from statistically independently emitted particles, removing tracks from 2 leading jets + double randomConePtWithoutOneLeadJet = randomConePtRandomTrackDirection; + double randomConePtWithoutTwoLeadJet = randomConePtRandomTrackDirection; + if (hasLead) { + randomConePtWithoutOneLeadJet = 0.0; + randomConePtWithoutTwoLeadJet = 0.0; + for (auto const& track : tracks) { + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + const bool inLead = trackIsInJet(track, jets.iteratorAt(0)); + const bool inSub = hasSub && trackIsInJet(track, jets.iteratorAt(1)); + if (!inLead) { + randomConePtWithoutOneLeadJet += track.pt(); + if (!hasSub || !inSub) { + randomConePtWithoutTwoLeadJet += track.pt(); + } + } + } + } + } + registry.fill(HIST("h_rhorandomconerandomtrackdirectionwithoutoneleadingjets"), randomConePtWithoutOneLeadJet - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("h_rhorandomconerandomtrackdirectionwithouttwoleadingjets"), randomConePtWithoutTwoLeadJet - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + } + void processRho(soa::Filtered>::iterator const& collision, soa::Filtered const& tracks) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents)) { @@ -229,7 +328,23 @@ struct JetBackgroundAnalysisTask { registry.fill(HIST("h2_centrality_rho"), centrality, collision.rho()); registry.fill(HIST("h2_centrality_rhom"), centrality, collision.rhoM()); } - PROCESS_SWITCH(JetBackgroundAnalysisTask, processRho, "QA for rho-area subtracted jets", false); + PROCESS_SWITCH(JetBackgroundAnalysisTask, processRho, "QA for rho-area subtracted jets", true); + + void processRhoMCP(soa::Filtered>::iterator const& mcCollision, soa::Filtered const& particles) + { + // no event selection as this is used for gen-only simulations + int nParticles = 0; + for (auto const& particle : particles) { + auto pdgParticle = pdgDatabase->GetParticle(particle.pdgCode()); + auto pdgCharge = pdgParticle ? std::abs(pdgParticle->Charge()) : -1.0; + if (pdgCharge > 0) { + nParticles++; + } + } + registry.fill(HIST("h2_nparticles_rho_mcp"), nParticles, mcCollision.rho()); + registry.fill(HIST("h2_nparticles_rhom_mcp"), nParticles, mcCollision.rhoM()); + } + PROCESS_SWITCH(JetBackgroundAnalysisTask, processRhoMCP, "QA for rho-area subtracted jets at the MCP level", false); void processBkgFluctuationsData(soa::Filtered>::iterator const& collision, soa::Join const& jets, soa::Filtered const& tracks) { @@ -264,6 +379,13 @@ struct JetBackgroundAnalysisTask { bkgFluctuationsRandomCone(collision, jets, tracks, centrality); } PROCESS_SWITCH(JetBackgroundAnalysisTask, processBkgFluctuationsMCD, "QA for random cone estimation of background fluctuations in mcd", false); + + void processBkgFluctuationsMCP(soa::Filtered>::iterator const& collision, soa::Join const& jets, soa::Filtered const& tracks) + { + // no event selection as this is used for gen-only simulations + bkgFluctuationsRandomConeMCP(collision, jets, tracks); + } + PROCESS_SWITCH(JetBackgroundAnalysisTask, processBkgFluctuationsMCP, "QA for random cone estimation of background fluctuations in mcp", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGJE/Tasks/jetChargedV2.cxx b/PWGJE/Tasks/jetChargedV2.cxx index 602e896bc18..17a637da4f4 100644 --- a/PWGJE/Tasks/jetChargedV2.cxx +++ b/PWGJE/Tasks/jetChargedV2.cxx @@ -15,6 +15,7 @@ #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGJE/Core/JetUtilities.h" #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetSubtraction.h" @@ -36,6 +37,7 @@ #include #include +#include #include #include #include @@ -43,7 +45,9 @@ #include #include +#include #include +#include #include #include #include @@ -70,7 +74,6 @@ struct JetChargedV2 { Configurable centralityMax{"centralityMax", 999.0, "maximum centrality"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; - Configurable trackDcaZmax{"trackDcaZmax", 99, "additional cut on dcaZ to PV for tracks; uniformTracks in particular don't cut on this at all"}; Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable trackPtMin{"trackPtMin", 0.15, "minimum pT acceptance for tracks"}; @@ -82,42 +85,63 @@ struct JetChargedV2 { Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT selection on jet constituent"}; Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT selection on jet constituent"}; - Configurable jetPtMin{"jetPtMin", 0.15, "minimum pT acceptance for jets"}; Configurable jetPtMax{"jetPtMax", 200.0, "maximum pT acceptance for jets"}; Configurable jetEtaMin{"jetEtaMin", -0.9, "minimum eta acceptance for jets"}; Configurable jetEtaMax{"jetEtaMax", 0.9, "maximum eta acceptance for jets"}; Configurable nBinsEta{"nBinsEta", 200, "number of bins for eta axes"}; Configurable randomConeLeadJetDeltaR{"randomConeLeadJetDeltaR", -99.0, "min distance between leading jet axis and random cone (RC) axis; if negative, min distance is set to automatic value of R_leadJet+R_RC "}; Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; + Configurable nBinsFluct{"nBinsFluct", 1000, "number of bins for fluctuations axes"}; + Configurable deltaPtMinMaxFluct{"deltaPtMinMaxFluct", 200, "maximum and minimum delta pt value for fluctuation axes"}; + Configurable nBinsFluctCentrality{"nBinsFluctCentrality", 1100, "number of centrality bins for the fluctuation axes; reduce it (e.g. 110) to cut the memory of the fluctuation histograms"}; + Configurable nBinsDeltaPhi{"nBinsDeltaPhi", 100, "number of Delta phi bins for the fluctuation axes; 96 keeps the pi/4 in-/out-of-plane edges on bin boundaries"}; + Configurable fillFluctH3{"fillFluctH3", false, "fill the 3D centrality-vs-deltaPt-vs-DeltaPhi fluctuation histograms (memory heavy); the direct 2D in-/out-of-plane histograms are always filled"}; + Configurable fillQvecQA{"fillQvecQA", true, "fill the Q-vector QA histograms (histQvec*V#, 3D Qx-vs-Qy-vs-centrality, memory heavy); the event-plane QA histograms are always filled"}; //=====================< tracking efficiency >=====================// - // Configurable checkCentrality{"checkCentrality", false, ""}; - // Configurable checkOccupancy{"checkOccupancy", false, "check occupancy only in general purpose Pb-Pb MC, default as false"}; - // Configurable getPtHatFromHepMCXSection{"getPtHatFromHepMCXSection", true, "test configurable, configurable should be removed once well tested"}; - - // Configurable ptHatMin{"ptHatMin", 5, "min pT hat of collisions"}; - // Configurable ptHatMax{"ptHatMax", 300, "max pT hat of collisions"}; Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; - // Configurable pTHatMaxFractionMCD{"pTHatMaxFractionMCD", 999.0, "maximum fraction of hard scattering for reconstructed track acceptance in MC"}; //=====================< evt pln >=====================// - Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; - Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; - Configurable cfgDetName{"cfgDetName", "FT0M", "The name of detector to be analyzed"}; - Configurable cfgRefAName{"cfgRefAName", "TPCpos", "The name of detector for reference A"}; - Configurable cfgRefBName{"cfgRefBName", "TPCneg", "The name of detector for reference B"}; - - ConfigurableAxis cfgAxisQvecF{"cfgAxisQvecF", {300, -1, 1}, ""}; - ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {100, -3, 3}, ""}; - ConfigurableAxis cfgAxisCent{"cfgAxisCent", {90, 0, 90}, ""}; + struct EpQvecSettings : ConfigurableGroup { + Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; + Configurable cfgDetCheckName{"cfgDetCheckName", "FT0M", "The name of detector to be analyzed"}; + Configurable cfgRefAName{"cfgRefAName", "TPCpos", "The name of detector for reference A"}; + Configurable cfgRefBName{"cfgRefBName", "TPCneg", "The name of detector for reference B"}; + Configurable> cfgnMods{"cfgnMods", {2}, "Modulation of interest"}; + Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number"}; + Configurable detCheck{"detCheck", 4, "total qvector number"}; + ConfigurableAxis cfgAxisQvecF{"cfgAxisQvecF", {300, -1, 1}, ""}; + ConfigurableAxis cfgAxisQvec{"cfgAxisQvec", {100, -3, 3}, ""}; + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {90, 0, 90}, ""}; + ConfigurableAxis cfgAxisEvtfit{"cfgAxisEvtfit", {10000, 0, 10000}, ""}; + } epQvec; + Configurable rhoFitRangeMargin{"rhoFitRangeMargin", 0.5, "extension of the rho modulation fit function range"}; + Configurable checkAbsDeltaPhi{"checkAbsDeltaPhi", false, "use 0-pi event-plane region for in/out-plane"}; ConfigurableAxis cfgAxisVnCent{"cfgAxisVnCent", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 50, 70, 100}, " % "}; - - ConfigurableAxis cfgAxisEvtfit{"cfgAxisEvtfit", {10000, 0, 10000}, ""}; EventPlaneHelper helperEP; - int detId; - int refAId; - int refBId; + Configurable fillTHnSparse{"fillTHnSparse", false, "switch to fill the THnSparse"}; + // subsample the sparse filling: 1 = every jet/cone, N = only every N-th. At full statistics the + // sparse costs O(entries) * 40 B, so use e.g. 100 to keep the output at the MB level. + Configurable sparseSubsample{"sparseSubsample", 1, "fill every N-th jet/cone into the THnSparse (1 = all)"}; + // binning of the THnSparse histograms: coarse defaults are safe for a full production, the finer + // binning used for local tests can be set from the command line (or from the train configuration) + // all THnSparse axes packed into one configurable to stay within the framework member limit: + // (nbins, min, max) triples, in the order pT (also used for rho*A), centrality, rho, Delta phi + Configurable> sparseAxes{"sparseAxes", {105, 0., 210., 55, 0., 110., 110, 0., 110., 60, 0., 6.2831853}, "sparse axes as (nbins, min, max) triples: pT, centrality, rho, Delta phi"}; + // remaining sparse axes packed into one configurable to stay within the framework member limit: + // (nbins, min, max) triples for delta pT, area and number of tracks + Configurable> sparseExtraAxes{"sparseExtraAxes", {100.f, -200.f, 200.f, 8.f, 0.f, 0.8f, 20.f, 0.f, 100.f}, "extra sparse axes as (nbins, min, max) triples: delta pT, area, N tracks"}; + Configurable doSumw2Sparse{"doSumw2Sparse", true, "Flag whether to set Sumw2() for the sparse histograms"}; + std::shared_ptr hJetQA; //! per-jet QA quantities (pT, eta, phi, area, nTracks, centrality) + std::shared_ptr hRhoMedian; //! per-jet information using the event-averaged (median) rho + std::shared_ptr hRhoLocal; //! per-jet information using the local rho of that jet + std::shared_ptr hFluctQA; //! per-random-cone QA quantities + std::shared_ptr hFluctQAwoLeadingJet; //! per-random-cone QA, leading jet removed from the cone + int detId = 0; + int refAId = 0; + int refBId = 0; + int detIdCheck = 0; //=====================< jetSpectraConfig to this analysis >=====================// Configurable acceptSplitCollisions{"acceptSplitCollisions", 0, "0: only look at mcCollisions that are not split; 1: accept split mcCollisions, 2: accept split mcCollisions but only look at the first reco collision associated with it"}; @@ -132,6 +156,8 @@ struct JetChargedV2 { Configurable useMedianRho{"useMedianRho", false, "use median rho for subtract MCP Background"}; Configurable useLocalRho{"useLocalRho", false, "use local rho for subtract MCP Background"}; Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; + Configurable kappa{"kappa", 1.0, "angularity kappa"}; + Configurable alpha{"alpha", 1.0, "angularity alpha"}; enum AcceptSplitCollisionsOptions { NonSplitOnly = 0, @@ -142,26 +168,28 @@ struct JetChargedV2 { template int getDetId(const T& name) { - if (name.value == "BPos" || name.value == "BNeg" || name.value == "BTot") { - LOGF(warning, "Using deprecated label: %s. Please use TPCpos, TPCneg, TPCall instead.", name.value); - } if (name.value == "FT0C") { return 0; - } else if (name.value == "FT0A") { + } + if (name.value == "FT0A") { return 1; - } else if (name.value == "FT0M") { + } + if (name.value == "FT0M") { return 2; - } else if (name.value == "FV0A") { + } + if (name.value == "FV0A") { return 3; - } else if (name.value == "TPCpos" || name.value == "BPos") { + } + if (name.value == "TPCpos" || name.value == "BPos") { return 4; - } else if (name.value == "TPCneg" || name.value == "BNeg") { + } + if (name.value == "TPCneg" || name.value == "BNeg") { return 5; - } else if (name.value == "TPCall" || name.value == "BTot") { + } + if (name.value == "TPCall" || name.value == "BTot") { return 6; - } else { - return 0; } + return 0; } //=====================< evt p615ln | end >=====================// std::vector jetPtBins; @@ -169,6 +197,7 @@ struct JetChargedV2 { std::vector eventSelectionBits; int trackSelection = -1; + bool doSumw2 = false; double evtnum = 0; float collQvecAmpDetId = 1e-8; TH1F* hPtsumSumptFit = nullptr; @@ -177,16 +206,18 @@ struct JetChargedV2 { TF1* fFitModulationV2v3P = 0x0; TH1F* hPtsumSumptFitRM = nullptr; TF1* fFitModulationRM = 0x0; - TRandom3 randomNumber{}; + TRandom3 randomNumber; void init(o2::framework::InitContext&) { - detId = getDetId(cfgDetName); - refAId = getDetId(cfgRefAName); - refBId = getDetId(cfgRefBName); - if (detId == refAId || detId == refBId || refAId == refBId) { - LOGF(info, "Wrong detector configuration \n The FT0C will be used to get Q-Vector \n The TPCpos and TPCneg will be used as reference systems"); + doSumw2 = skipMBGapEvents; + detId = getDetId(epQvec.cfgDetName); + detIdCheck = getDetId(epQvec.cfgDetCheckName); + refAId = getDetId(epQvec.cfgRefAName); + refBId = getDetId(epQvec.cfgRefBName); + if (detId == refAId || detId == refBId || refAId == refBId || detId == detIdCheck) { detId = 0; + detIdCheck = 0; refAId = 4; refBId = 5; } @@ -225,14 +256,18 @@ struct JetChargedV2 { AxisSpec jetPtAxis = {200, 0., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetPtAxisRhoAreaSub = {400, -200., 200., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetEtaAxis = {nBinsEta, -1.0, 1.0, "#eta"}; + // fluctuation axes, same as jetBackgroundAnalysis's h2_centrality_rhorandomcone* + AxisSpec fluctCentralityAxis = {nBinsFluctCentrality, 0.0, 110.0, "centrality (%)"}; + AxisSpec fluctDeltaPtAxis = {nBinsFluct, -deltaPtMinMaxFluct, deltaPtMinMaxFluct, "#delta #it{p}_{T} (GeV/#it{c})"}; + AxisSpec fluctDeltaPhiAxis = {nBinsDeltaPhi, 0., o2::constants::math::TwoPI, "#Delta#varphi_{jet}"}; AxisSpec axisPt = {40, 0.0, 4.0}; AxisSpec axisEta = {32, -0.8, 0.8}; AxisSpec axixCent = {20, 0, 100}; AxisSpec axisChID = {220, 0, 220}; - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); randomNumber.SetSeed(0); if (doprocessCollisions || doprocessCollisionsWeighted) { @@ -292,27 +327,35 @@ struct JetChargedV2 { registry.add("h_mcd_pt_before_matching_mcdprocess", "jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); //=====================< evt pln plot >=====================// - AxisSpec axisCent{cfgAxisCent, "centrality"}; - AxisSpec axisQvec{cfgAxisQvec, "Q"}; - AxisSpec axisQvecF{cfgAxisQvecF, "Q"}; + AxisSpec axisCent{epQvec.cfgAxisCent, "centrality"}; + AxisSpec axisQvec{epQvec.cfgAxisQvec, "Q"}; + AxisSpec axisQvecF{epQvec.cfgAxisQvecF, "Q"}; AxisSpec axisEvtPl{360, -constants::math::PI, constants::math::PI}; - for (uint i = 0; i < cfgnMods->size(); i++) { - histosQA.add(Form("histQvecUncorV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecRectrV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecTwistV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); - histosQA.add(Form("histQvecFinalV%d", cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvec, axisQvec, axisCent}}); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + if (fillQvecQA) { // 3D Qx-vs-Qy-vs-centrality, memory heavy + histosQA.add(Form("histQvecUncorV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecRectrV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecTwistV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvecF, axisQvecF, axisCent}}); + histosQA.add(Form("histQvecFinalV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH3F, {axisQvec, axisQvec, axisCent}}); + } - histosQA.add(Form("histEvtPlUncorV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRectrV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlTwistV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlFinalV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlUncorV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRectrV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlTwistV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlFinalV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_SigRefAV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_SigRefBV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); - histosQA.add(Form("histEvtPlRes_RefARefBV%d", cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("h_ep2_FT0CV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("h_ep2_FT0MV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + + histosQA.add(Form("histEvtPlRes_SigRefAV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_SigRefBV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); + histosQA.add(Form("histEvtPlRes_RefARefBV%d", epQvec.cfgnMods->at(i)), "", {HistType::kTH2F, {axisEvtPl, axisCent}}); } histosQA.add("histCent", "Centrality TrkProcess", HistType::kTH1F, {axisCent}); + histosQA.add("h2_ep2_FT0C_FT0M", "event_plane_FT0C_FT0M; FT0C; FT0M; cent", {HistType::kTH3F, {{100, -o2::constants::math::PIHalf, o2::constants::math::PIHalf}, {100, -o2::constants::math::PIHalf, o2::constants::math::PIHalf}, {100, 0.0, 100.0}}}); + histosQA.add("h2_ep2_FT0C_FT0M_bumpRegion", "event_plane_FT0C_FT0M_bumpRegion; FT0C; FT0M; cent", {HistType::kTH3F, {{100, -o2::constants::math::PIHalf, o2::constants::math::PIHalf}, {100, -o2::constants::math::PIHalf, o2::constants::math::PIHalf}, {100, 0.0, 100.0}}}); + //< fit quality >// registry.add("h_PvalueCDF_CombinFit", "cDF #chi^{2}; entries", {HistType::kTH1F, {{50, 0, 1}}}); registry.add("h2_PvalueCDFCent_CombinFit", "p-value cDF vs centrality; centrality; p-value", {HistType::kTH2F, {{100, 0, 100}, {40, 0, 1}}}); @@ -330,34 +373,93 @@ struct JetChargedV2 { registry.add("h_fitparaPsi2_evtnum", "fitparameter #Psi_{2} vs evtnum ; #eventnumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); registry.add("h_fitparaPsi3_evtnum", "fitparameter #Psi_{3} vs evtnum ; #eventnumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); registry.add("h_evtnum_centrlity", "eventNumber vs centrality ; #eventNumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); - registry.add("h_badfit_counter", "bad fit para[1] count; #eventNumber", {HistType::kTH1F, {{5, 0.0, 5}}}); registry.add("h2_phi_rholocal", "#varphi vs #rho(#varphi); #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - registry.add("h2_phi_rholocal_absDelta", "#varphi vs #rho(#varphi), absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); - - registry.add("h2_rholocal_cent", "#varphi vs #rho(#varphi); #cent; #rho(#varphi) ", {HistType::kTH2F, {{100, 0., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_centrality_pT", "centrality vs p_{T}; p_{T}; centrality ", {HistType::kTH2F, {{210, -10.0, 200.0}, {100, 0., 100}}}); + + registry.add("h2_rho_cent_median_in", "#rho vs centrality, in-plane, <#rho>; #centrality (%); <#rho> (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_median_out", "#rho vs centrality, out-of-plane, <#rho>; #centrality (%); <#rho> (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_local_in", "#rho vs centrality, in-plane, #rho(#varphi); #centrality (%); #rho(#varphi) (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rho_cent_local_out", "#rho vs centrality, out-of-plane, #rho(#varphi); #centrality (%); #rho(#varphi) (GeV/#it{c})", {HistType::kTH2F, {{110, -10., 100.}, {210, -10., 200.}}}); + registry.add("h2_rholocal_cent", "#centrality vs #rho(#varphi); #centrality; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_averagerho_cent", "#centrality vs #rho; #centrality; #rho ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + + registry.add("h2_rholocal_pt", "#varphi vs #it{p}_{T}; #it{p}_{T}; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_rholocal_raw_pt", "#varphi vs #it{p}_{T}; #it{p}_{T} - #rhoArea; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_rholocal_pt_inplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_rholocal_pt_outplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; #rho(#varphi) ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + + registry.add("h2_averagerho_pt", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_averagerho_raw_pt", "#varphi vs #it{p}_{T}; #it{p}_{T} - <#rho>Area; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_averagerho_pt_inplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + registry.add("h2_averagerho_pt_outplane", "#varphi vs #it{p}_{T}; #it{p}_{T}; <#rho> ", {HistType::kTH2F, {{110, -10., 100}, {210, -10.0, 200.0}}}); + + registry.add("h_good_bad_ugly", "local rho large than 0, less than 0 and total", {HistType::kTH1F, {{5, 0.0, 5.0}}}); + registry.get(HIST("h_good_bad_ugly"))->GetXaxis()->SetBinLabel(1, "#rho_{local} > 0"); + registry.get(HIST("h_good_bad_ugly"))->GetXaxis()->SetBinLabel(2, "#rho_{local} <= 0"); + registry.get(HIST("h_good_bad_ugly"))->GetXaxis()->SetBinLabel(3, "#rho_{local} total"); + registry.add("h_jet_pt_badRhoLocal", "local rho info, Jet Pt", {HistType::kTH1F, {jetPtAxis}}); + registry.add("h_jet_phi_badRhoLocal", "local rho info, Jet #phi ", {HistType::kTH1F, {phiAxis}}); + registry.add("h_jet_eta_badRhoLocal", "local rho info, Jet #eta ", {HistType::kTH1F, {jetEtaAxis}}); //< \sigma p_T at local rho test plot | end > - registry.add("h_jet_pt_rhoareasubtracted", "jet pT rhoareasubtracted;#it{p}_{T,jet} (GeV/#it{c}); entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_inclusive_v2", "jet pT rhoareasubtracted;#it{p}_{T,jet} (GeV/#it{c}); entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("leadJetPt", "leadJet Pt ", {HistType::kTH1F, {{200, 0., 200.0}}}); registry.add("leadJetPhi", "leadJet constituent #phi ", {HistType::kTH1F, {{80, -1.0, 7.}}}); registry.add("leadJetEta", "leadJet constituent #eta ", {HistType::kTH1F, {{100, -1.0, 1.0}}}); //< RC test plots >// - registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - - registry.add("h1_distribution_RC", "RC #phi-#Psi_{2}", {HistType::kTH1F, {{72, 0.0, o2::constants::math::TwoPI}}}); - registry.add("h1_distribution_random", "RC #phi-#Psi_{2}", {HistType::kTH1F, {{72, 0.0, o2::constants::math::TwoPI}}}); - registry.add("h3_randomizedPhi_check", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {100, 0., o2::constants::math::TwoPI}}}); - + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + if (fillFluctH3) { + registry.add("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH3F, {fluctCentralityAxis, fluctDeltaPtAxis, fluctDeltaPhiAxis}}); + } + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}(#varphi); #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + if (checkAbsDeltaPhi) { + registry.add("h2_phi_rholocal_absDelta", "#varphi vs #rho(#varphi)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_averagerho_absDelta", "#varphi vs #rho(0)absDelta, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + + registry.add("h2_phi_rholocal_absDelta_low", "#varphi vs #rho(#varphi)absDeltaLow, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_absDelta_medium", "#varphi vs #rho(#varphi)absDeltaMediun, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_absDelta_high", "#varphi vs #rho(#varphi)absDeltahigh, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::PI}, {210, -10.0, 200.0}}}); + + registry.add("h_jet_pt_in_plane_v2_abs", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_in_plane_v2_rho_abs", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_out_of_plane_v2_abs", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + registry.add("h_jet_pt_out_of_plane_v2_rho_abs", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + registry.add("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH2F, {fluctCentralityAxis, fluctDeltaPtAxis}}); + } //< bkg sub plot | end >// //< median rho >// registry.add("h_jet_pt_in_plane_v2", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_pt_out_of_plane_v2", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + registry.add("h2_phi_rholocal_Delta_low_inplane", "#varphi vs #rho(#varphi)absDeltaLow inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_medium_inplane", "#varphi vs #rho(#varphi)absDeltaMediun inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_high_inplane", "#varphi vs #rho(#varphi)absDeltahigh inplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + + registry.add("h2_phi_rholocal_Delta_low_outplane", "#varphi vs #rho(#varphi)absDeltaLow outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_medium_outplane", "#varphi vs #rho(#varphi)absDeltaMediun outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h2_phi_rholocal_Delta_high_outplane", "#varphi vs #rho(#varphi)absDeltahigh outplane, absDelta; #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{40, 0., o2::constants::math::TwoPI}, {210, -10.0, 200.0}}}); + registry.add("h_jet_pt_in_plane_v3", "jet pT;#it{p}^{in-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_jet_pt_out_of_plane_v3", "jet pT;#it{p}^{out-of-plane}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); @@ -377,6 +479,15 @@ struct JetChargedV2 { registry.add("h2_centrality_jet_eta_rhoareasubtracted", "centrality vs. jet eta;centrality; #eta; counts", {HistType::kTH2F, {centralityAxis, jetEtaAxis}}); registry.add("h2_centrality_jet_phi_rhoareasubtracted", "centrality vs. jet phi;centrality; #varphi; counts", {HistType::kTH2F, {centralityAxis, phiAxis}}); registry.add("h2_jet_pt_track_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, trackPtAxis}}); + + registry.add("h_jet_pt_rhoareasubtracted", "jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}, doSumw2); + registry.add("h_jet_eta_rhoareasubtracted", "jet eta;#eta; counts", {HistType::kTH1F, {jetEtaAxis}}, doSumw2); + registry.add("h_jet_phi_rhoareasubtracted", "jet phi;#phi; counts", {HistType::kTH1F, {phiAxis}}, doSumw2); + registry.add("h2_jet_pt_jet_area_rhoareasubtracted", "jet #it{p}_{T,jet} vs. Area_{jet}; #it{p}_{T,jet} (GeV/#it{c}); Area_{jet}", {HistType::kTH2F, {jetPtAxis, {150, 0., 1.5}}}, doSumw2); + registry.add("h2_jet_pt_jet_ntracks_rhoareasubtracted", "jet #it{p}_{T,jet} vs. N_{jet tracks}; #it{p}_{T,jet} (GeV/#it{c}); N_{jet, tracks}", {HistType::kTH2F, {jetPtAxis, {200, -0.5, 199.5}}}, doSumw2); + registry.add("h2_jet_pt_jet_corr_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,corr}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,corr} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxisRhoAreaSub}}, doSumw2); + registry.add("h2_jet_pt_jet_angularity_rhoareasubtracted", "jet #it{p}_{T,jet} vs. angularity;#it{p}_{T,jet} (GeV/#it{c});angularity", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {30, 0., 1.}}}, doSumw2); + registry.add("h3_jet_pt_jet_eta_jet_phi_rhoareasubtracted", "jet_pt_eta_phi_rhoareasubtracted", {HistType::kTH3F, {jetPtAxisRhoAreaSub, jetEtaAxis, phiAxis}}, doSumw2); } if (doprocessSigmaPtMCP || doprocessSigmaPtAreaSubMCP) { @@ -406,6 +517,14 @@ struct JetChargedV2 { registry.add("h2_mcp_centrality_rholocal", "#varphi vs #rho(#varphi); #varphi - #Psi_{EP,2}; #rho(#varphi) ", {HistType::kTH2F, {{120, -10.0, 110.0}, {210, -10.0, 200.0}}}); registry.add("h_mcp_jet_pt_rhoareasubtracted", "jet pT rholocal;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); registry.add("h_mcp_jet_pt_rholocal", "jet pT rholocal;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxisRhoAreaSub}}); + + //< MCP fit quality (independent from the data/MCD QA histograms) >// + registry.add("h_mcp_PvalueCDF_CombinFit", "cDF #chi^{2}; entries", {HistType::kTH1F, {{50, 0, 1}}}); + registry.add("h2_mcp_PvalueCDFCent_CombinFit", "p-value cDF vs centrality; centrality; p-value", {HistType::kTH2F, {{100, 0, 100}, {40, 0, 1}}}); + registry.add("h2_mcp_Chi2Cent_CombinFit", "Chi2 vs centrality; centrality; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 100}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFit", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFitA", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); + registry.add("h2_mcp_PChi2_CombinFitB", "p-value vs #tilde{#chi^{2}}; p-value; #tilde{#chi^{2}}", {HistType::kTH2F, {{100, 0, 1}, {100, 0, 5}}}); //< MCP fit test >// registry.add("h_mcp_evtnum_centrlity", "eventNumber vs centrality ; #eventNumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); registry.add("h_ep2_evtnum", "fitparameter #rho_{0} vs evtnum ; #eventnumber", {HistType::kTH1F, {{1000, 0.0, 1000}}}); @@ -499,6 +618,45 @@ struct JetChargedV2 { registry.add("h_track_pt", "track #it{p}_{T} ; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH1F, {trackPtAxis}}); registry.add("h2_track_eta_track_phi", "track eta vs. track phi; #eta; #phi; counts", {HistType::kTH2F, {trackEtaAxis, phiAxis}}); + + // optional sparse storage of the analysis ingredients (booked always, filled only on request, + // so that the output merging always sees the same object names) + // binning follows the existing 2D outputs of this task, so that the sparses can be compared + const auto& axes = sparseAxes.value; + if (axes.size() < 12) { + LOGF(fatal, "sparseAxes needs 12 numbers (4 x (nbins, min, max)), got %d", static_cast(axes.size())); + } + // bin by bin with h2_rholocal_pt (rho 110 x pT 210), h2_phi_rholocal (phi 100 x rho 110) and + // h2_centrality_deltapT_RandomCornPhi_* (centrality 1100 x delta pT 1000) + const AxisSpec axisQAPt{static_cast(axes[0]), axes[1], axes[2], "#it{p}_{T,jet}^{raw} (GeV/#it{c})"}; + const AxisSpec axisQAEta{20, -1., 1., "#it{#eta}_{jet}"}; + const AxisSpec axisQAPhi{static_cast(axes[9]), axes[10], axes[11], "#it{#varphi}_{jet} (rad)"}; + const auto& extra = sparseExtraAxes.value; + if (extra.size() < 9) { + LOGF(fatal, "sparseExtraAxes needs 9 numbers (3 x (nbins, min, max)), got %d", static_cast(extra.size())); + } + const AxisSpec axisQAArea{static_cast(extra[3]), extra[4], extra[5], "area"}; + const AxisSpec axisQANtracks{static_cast(extra[6]), extra[7], extra[8], "N_{tracks}^{jet}"}; + const AxisSpec axisQACent{static_cast(axes[3]), axes[4], axes[5], "centrality (%)"}; + hJetQA = registry.add("hJetQA", "per-jet QA", {HistType::kTHnSparseF, {{axisQAPt}, {axisQAEta}, {axisQAPhi}, {axisQAArea}, {axisQANtracks}, {axisQACent}}}, doSumw2Sparse); + + const AxisSpec axisRhoValue{static_cast(axes[6]), axes[7], axes[8], "#it{#rho} (GeV)"}; + const AxisSpec axisRhoArea{static_cast(axes[0]), axes[1], axes[2], "#it{#rho} #upoint A (GeV)"}; + const AxisSpec axisRhoDphi{static_cast(axes[9]), axes[10], axes[11], "#varphi - #Psi_{EP,2} (rad)"}; + hRhoMedian = registry.add("hRhoMedian", "per-jet information, event-averaged (median) rho", {HistType::kTHnSparseF, {{axisQACent}, {axisQAPt}, {axisRhoValue}, {axisRhoArea}, {axisRhoDphi}, {axisQAArea}}}, doSumw2Sparse); + hRhoLocal = registry.add("hRhoLocal", "per-jet information, local rho", {HistType::kTHnSparseF, {{axisQACent}, {axisQAPt}, {axisRhoValue}, {axisRhoArea}, {axisRhoDphi}, {axisQAArea}}}, doSumw2Sparse); + + const AxisSpec axisFluctDpt{static_cast(extra[0]), extra[1], extra[2], "#delta#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisFluctAbsDphi{static_cast(axes[9]), axes[10], axes[11], "|#varphi_{cone} - #Psi_{EP,2}| (rad)"}; + const AxisSpec axisFluctArea{static_cast(extra[3]), extra[4], extra[5], "area_{cone}"}; + const AxisSpec axisFluctRho{static_cast(axes[6]), axes[7], axes[8], "#it{#rho}_{local}^{RC} (GeV)"}; + const AxisSpec axisFluctNtracks{static_cast(extra[6]), extra[7], extra[8], "N_{tracks}^{cone}"}; + hFluctQA = registry.add("hFluctQA", "per-random-cone QA", {HistType::kTHnSparseF, {{axisQACent}, {axisFluctDpt}, {axisFluctAbsDphi}, {axisFluctArea}, {axisFluctRho}, {axisFluctNtracks}}}, doSumw2Sparse); + hFluctQAwoLeadingJet = registry.add( + "hFluctQAwoLeadingJet", "per-random-cone QA, leading jet removed from the cone", + {HistType::kTHnSparseF, + {{axisQACent}, {axisFluctDpt}, {axisFluctAbsDphi}, {axisFluctArea}, {axisFluctRho}, {axisFluctNtracks}}}, + doSumw2Sparse); } Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); @@ -506,8 +664,6 @@ struct JetChargedV2 { Filter mcEventCuts = (nabs(aod::jmccollision::posZ) < vertexZCut); Preslice tracksPerJCollision = o2::aod::jtrack::collisionId; - Preslice mcdjetsPerJCollision = o2::aod::jet::collisionId; - PresliceUnsorted> collisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; template bool isAcceptedJet(TJets const& jet, bool mcLevelIsParticleLevel = false) @@ -569,8 +725,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_collisions"), 0.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 0.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_collisions_weighted"), 0.5, eventWeight); + } } if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { @@ -579,8 +736,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_collisions"), 1.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 1.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_collisions_weighted"), 1.5, eventWeight); + } } if (centrality < centralityMin || centralityMax < centrality) { @@ -589,8 +747,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_collisions"), 2.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 2.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_collisions_weighted"), 2.5, eventWeight); + } } if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { @@ -599,8 +758,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_collisions"), 3.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 3.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_collisions_weighted"), 3.5, eventWeight); + } } return true; @@ -622,8 +782,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 0.5); registry.fill(HIST("h2_centrality_mccollisions"), centrality, 0.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_mccollisions_weighted"), 0.5, eventWeight); + } } if (collisions.size() < 1) { @@ -632,8 +793,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 1.5); registry.fill(HIST("h2_centrality_mccollisions"), centrality, 1.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_mccollisions_weighted"), 1.5, eventWeight); + } } if (acceptSplitCollisions == NonSplitOnly && collisions.size() > 1) { @@ -642,8 +804,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 2.5); registry.fill(HIST("h2_centrality_mccollisions"), centrality, 2.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_mccollisions_weighted"), 2.5, eventWeight); + } } bool hasSel8Coll = false; @@ -669,7 +832,6 @@ struct JetChargedV2 { occupancyIsGood = true; } - float centrality = -1.0; switch (centralityMode) { case 1: centrality = collision.centFT0M(); @@ -693,8 +855,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 3.5); registry.fill(HIST("h2_centrality_mccollisions"), centrality, 3.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_mccollisions_weighted"), 3.5, eventWeight); + } } if (!centralityIsGood) { @@ -703,8 +866,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 4.5); registry.fill(HIST("h2_centrality_mccollisions"), centrality, 4.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_mccollisions_weighted"), 4.5, eventWeight); + } } if (!occupancyIsGood) { @@ -713,8 +877,9 @@ struct JetChargedV2 { if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 5.5); registry.fill(HIST("h2_centrality_mccollisions"), centrality, 5.5, eventWeight); - if (isWeighted) + if (isWeighted) { registry.fill(HIST("h_mccollisions_weighted"), 5.5, eventWeight); + } } return true; @@ -741,6 +906,38 @@ struct JetChargedV2 { } } + template + void fillJetAreaSubHistograms(TJets const& jet, float centrality, float rho, float weight = 1.0, float pTHat = 999.0) + { + if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) { + return; + } + double jetcorrpt = jet.pt() - (rho * jet.area()); + if (jet.r() == round(selectedJetsRadius * 100.0f)) { + // fill jet histograms after area-based subtraction + registry.fill(HIST("h_jet_pt_rhoareasubtracted"), jetcorrpt, weight); + registry.fill(HIST("h2_centrality_jet_pt_rhoareasubtracted"), centrality, jetcorrpt, weight); + registry.fill(HIST("h2_jet_pt_jet_corr_pt_rhoareasubtracted"), jet.pt(), jetcorrpt, weight); + registry.fill(HIST("h3_jet_pt_jet_eta_jet_phi_rhoareasubtracted"), jetcorrpt, jet.eta(), jet.phi(), weight); + if (jetcorrpt > 0) { + registry.fill(HIST("h_jet_eta_rhoareasubtracted"), jet.eta(), weight); + registry.fill(HIST("h_jet_phi_rhoareasubtracted"), jet.phi(), weight); + registry.fill(HIST("h2_centrality_jet_eta_rhoareasubtracted"), centrality, jet.eta(), weight); + registry.fill(HIST("h2_centrality_jet_phi_rhoareasubtracted"), centrality, jet.phi(), weight); + registry.fill(HIST("h2_jet_pt_jet_area_rhoareasubtracted"), jetcorrpt, jet.area(), weight); + registry.fill(HIST("h2_jet_pt_jet_ntracks_rhoareasubtracted"), jetcorrpt, jet.tracksIds().size(), weight); + } + } + + float angularity = 0.; + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_track_pt_rhoareasubtracted"), jetcorrpt, constituent.pt(), weight); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent), alpha); + } + angularity /= (jet.pt() * (jet.r() / 100.f)); + registry.fill(HIST("h2_jet_pt_jet_angularity_rhoareasubtracted"), jetcorrpt, angularity, weight); + } + double chiSquareCDF(int nDF, double x) { return TMath::Gamma(nDF / 2., x / 2.); @@ -765,7 +962,7 @@ struct JetChargedV2 { // create h_ptsum_sumpt_fit, with number of Track template - void getNtrk(T const& tracks, U const& jets, int& nTrk, double& evtnum, double& leadingJetEta) + void getNtrk(T const& tracks, U const& jets, int& nTrk, double& evtnumNtrk, double& leadingJetEta) { if (jets.size() > 0) { for (auto const& track : tracks) { @@ -776,13 +973,13 @@ struct JetChargedV2 { nTrk += 1; } } - registry.fill(HIST("h_evtnum_NTrk"), evtnum, nTrk); + registry.fill(HIST("h_evtnum_NTrk"), evtnumNtrk, nTrk); } } // fill nTrk plot for fit rho(varphi) template - void fillNtrkCheck(U const& tracks, J const& jets, TH1F* hPtsumSumptFit, double& leadingJetEta) + void fillNtrkCheck(U const& tracks, J const& jets, TH1F* hPtsumSumptFitFill, double& leadingJetEta) { if (jets.size() > 0) { double localRhoFitPtMin = 0.2; @@ -798,7 +995,7 @@ struct JetChargedV2 { registry.fill(HIST("h_accept_Track"), 2.5); if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > selectedJetsRadius) && track.pt() >= localRhoFitPtMin && track.pt() <= localRhoFitPtMax) { registry.fill(HIST("h_accept_Track"), 3.5); - hPtsumSumptFit->Fill(track.phi(), track.pt()); + hPtsumSumptFitFill->Fill(track.phi(), track.pt()); } } } @@ -820,9 +1017,39 @@ struct JetChargedV2 { registry.fill(HIST("leadJetEtaMCP"), leadingJetEta); } + // Integrate the rho modulation fit over [phi - radius, phi + radius]. Both limits are + // wrapped into [0, 2pi) with RecoDecay::constrainAngle; when the wrapped interval + // crosses zero it is split into two pieces (a plain integral between the two wrapped + // limits would be negative there). + double getRhoLocalIntegral(TF1* fitFunc, double phi, double radius, float fitRangeMargin) + { + if (!fitFunc) { + return 0.0; + } + if (radius > fitRangeMargin) { + static bool warnOnce = true; + if (warnOnce) { + LOGF(warning, "Rho fit range margin (%.3f) is smaller than integration radius (%.3f)", fitRangeMargin, radius); + warnOnce = false; + } + } + const double twoPi = o2::constants::math::TwoPI; + const double low = RecoDecay::constrainAngle(phi - radius, 0.); + const double high = RecoDecay::constrainAngle(phi + radius, 0.); + if (high < low) { // wrapped interval crosses zero + return fitFunc->Integral(low, twoPi) + fitFunc->Integral(0., high); + } + return fitFunc->Integral(low, high); + } + + void getRhoLocalIntegral(TF1* fitFunc, double phi, double radius, float fitRangeMargin, double& integralValue) + { + integralValue = getRhoLocalIntegral(fitFunc, phi, radius, fitRangeMargin); + } + // Run General_Purpose MC MCP template - void fitFncAreaSubMCP(U const& collision, J const& jets, TH1F* hPtsumSumptFitMCP, bool mcLevelIsParticleLevel, float weight = 1.0) + void fitFncAreaSubMCP(U const& collision, J const& jets, TH1F* hPtsumSumptFitMCPFill, bool mcLevelIsParticleLevel, float weight = 1.0) { float centrality = -1.0; switch (centralityMode) { @@ -844,9 +1071,9 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -865,33 +1092,28 @@ struct JetChargedV2 { fFitModulationV2v3P->SetParameter(1, 0.01); fFitModulationV2v3P->SetParameter(3, 0.01); - double ep2fix = 0.; - double ep3fix = 0.; - if (ep2 < 0) { - ep2fix = RecoDecay::constrainAngle(ep2); + double ep2fix = RecoDecay::constrainAngle(ep2); fFitModulationV2v3P->FixParameter(2, ep2fix); } else { fFitModulationV2v3P->FixParameter(2, ep2); } if (ep3 < 0) { - ep3fix = RecoDecay::constrainAngle(ep3); + double ep3fix = RecoDecay::constrainAngle(ep3); fFitModulationV2v3P->FixParameter(4, ep3fix); } else { fFitModulationV2v3P->FixParameter(4, ep3); } - hPtsumSumptFitMCP->Fit(fFitModulationV2v3P, "Q", "ep", 0, o2::constants::math::TwoPI); + hPtsumSumptFitMCPFill->Fit(fFitModulationV2v3P, "Q", "ep", 0, o2::constants::math::TwoPI); - double temppara[5]; + std::array temppara{}; temppara[0] = fFitModulationV2v3P->GetParameter(0); temppara[1] = fFitModulationV2v3P->GetParameter(1); temppara[2] = fFitModulationV2v3P->GetParameter(2); temppara[3] = fFitModulationV2v3P->GetParameter(3); temppara[4] = fFitModulationV2v3P->GetParameter(4); - if (temppara[0] == 0) { - registry.fill(HIST("h_badfit_counter"), 1); - } + registry.fill(HIST("h_mcp_fitparaRho_evtnum"), evtnum, temppara[0]); registry.fill(HIST("h_mcp_fitparaPsi2_evtnum"), evtnum, temppara[2]); registry.fill(HIST("h_mcp_fitparaPsi3_evtnum"), evtnum, temppara[4]); @@ -899,9 +1121,40 @@ struct JetChargedV2 { registry.fill(HIST("h_mcp_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_mcp_evtnum_centrlity"), evtnum, centrality); - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; + double chi2 = 0.; + for (int i = 0; i < hPtsumSumptFitMCPFill->GetXaxis()->GetNbins(); i++) { + if (hPtsumSumptFitMCPFill->GetBinContent(i + 1) <= 0.) { + continue; // empty bins do not enter the chi2 and must not count in the dof + } + nBinsUsed++; + chi2 += std::pow((hPtsumSumptFitMCPFill->GetBinContent(i + 1) - fFitModulationV2v3P->Eval(hPtsumSumptFitMCPFill->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFitMCPFill->GetBinContent(i + 1); + } + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters + + int evtCentAreaMin = 0; + int evtCentAreaMax = 5; + int evtMidAreaMin = 30; + int evtMidAreaMax = 50; + + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); + registry.fill(HIST("h_mcp_PvalueCDF_CombinFit"), cDF); + registry.fill(HIST("h2_mcp_PvalueCDFCent_CombinFit"), centrality, cDF); + registry.fill(HIST("h2_mcp_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_mcp_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); + if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { + registry.fill(HIST("h2_mcp_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); + } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { + registry.fill(HIST("h2_mcp_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); + } + } + + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -914,21 +1167,21 @@ struct JetChargedV2 { continue; } - double integralValue = fFitModulationV2v3P->Integral(jet.phi() - selectedJetsRadius, jet.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationV2v3P, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; registry.fill(HIST("h2_mcp_phi_rholocal"), jet.phi() - ep2, rholocal, weight); registry.fill(HIST("h2_mcp_centrality_rholocal"), centrality, rholocal, weight); if (nmode == cfgNmodA) { registry.fill(HIST("h_mcp_jet_pt_rholocal"), jet.pt() - (rholocal * jet.area()), weight); - double phiMinusPsi2; + double phiMinusPsi2 = 0.0; if (collision.qvecAmp()[detId] < collQvecAmpDetId) { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); + // same in-/out-of-plane definition as in processSigmaPtData (unfolded phi - Psi2) + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_mcp_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), weight); registry.fill(HIST("h2_mcp_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), weight); } else { @@ -936,7 +1189,7 @@ struct JetChargedV2 { registry.fill(HIST("h2_mcp_centrality_jet_pt_out_of_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), weight); } } else if (nmode == cfgNmodB) { - double phiMinusPsi3; + double phiMinusPsi3 = 0.0; if (collision.qvecAmp()[detId] < collQvecAmpDetId) { continue; @@ -1011,7 +1264,7 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - double phiMinusPsi2; + double phiMinusPsi2 = 0.0; // double phiMinusPsi2 = jetMCD.phi() - ep2; phiMinusPsi2 = RecoDecay::constrainAngle(jetMCD.phi() - ep2, -o2::constants::math::PI); float absDelta = std::abs(phiMinusPsi2); @@ -1049,7 +1302,7 @@ struct JetChargedV2 { } template - void fillGeoMatchedCorrHistograms(TBase const& jetMCD, TF1* fFitModulationRM, float tempparaA, double ep2, float rho, bool subtractMCPBackground, float mcrho, float weight = 1.0) + void fillGeoMatchedCorrHistograms(TBase const& jetMCD, TF1* fFitModulationRMFill, float tempparaA, double ep2, float rho, bool subtractMCPBackgroundBool, float mcrho, float weight = 1.0) { float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); if (jetMCD.pt() > pTHatMaxMCD * pTHat) { @@ -1076,19 +1329,19 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - double integralValue = fFitModulationRM->Integral(jetMCD.phi() - selectedJetsRadius, jetMCD.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationRMFill, jetMCD.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = rho / (2 * selectedJetsRadius * tempparaA) * integralValue; double corrBasejetpt = jetMCD.pt() - (rholocal * jetMCD.area()); - double corrTagjetpt; - if (subtractMCPBackground) { - double integralValueMCP = fFitModulationRM->Integral(jetMCP.phi() - selectedJetsRadius, jetMCP.phi() + selectedJetsRadius); + double corrTagjetpt = 0.0; + if (subtractMCPBackgroundBool) { + double integralValueMCP = getRhoLocalIntegral(fFitModulationRMFill, jetMCP.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocalMCP = mcrho / (2 * selectedJetsRadius * tempparaA) * integralValueMCP; corrTagjetpt = jetMCP.pt() - (rholocalMCP * jetMCP.area()); } else { corrTagjetpt = jetMCP.pt() - (mcrho * jetMCP.area()); } double dcorrpt = corrTagjetpt - corrBasejetpt; - double phiMinusPsi2; + double phiMinusPsi2 = 0.0; // double phiMinusPsi2 = jetMCD.phi() - ep2; phiMinusPsi2 = RecoDecay::constrainAngle(jetMCD.phi() - ep2, -o2::constants::math::PI); float absDelta = std::abs(phiMinusPsi2); @@ -1236,33 +1489,42 @@ struct JetChargedV2 { //=====================< evt pln [n=2->\Psi_2, n=3->\Psi_3] >=====================// int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refAInd = refAId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refBInd = refBId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int detIndFT0M = (detIdCheck + 2) * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refAInd = refAId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refBInd = refBId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId && collision.qvecAmp()[refAId] > collQvecAmpDetId && collision.qvecAmp()[refBId] > collQvecAmpDetId) { - histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlTwistV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlFinalV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), collision.cent()); + histosQA.fill(HIST("h_ep2_FT0CV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), collision.cent()); + histosQA.fill(HIST("h_ep2_FT0MV2"), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); + histosQA.fill(HIST("h2_ep2_FT0C_FT0M"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); + histosQA.fill(HIST("histEvtPlRes_SigRefAV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRes_SigRefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } else if (nmode == cfgNmodB) { - histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1274,79 +1536,6 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlRes_RefARefBV3"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } - - float centrality = -1.0; - switch (centralityMode) { - case 1: - centrality = collision.centFT0M(); - break; - case 2: - centrality = collision.centFT0A(); - break; - default: - centrality = collision.centFT0C(); - break; - } - int evtPlnAngleA = 7; - int evtPlnAngleB = 3; - int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - - if (nmode == cfgNmodA) { - double phiMinusPsi2; - if (collision.qvecAmp()[detId] < collQvecAmpDetId) { - continue; - } - float ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode); - for (auto const& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { - continue; - } - if (!isAcceptedJet(jet)) { - continue; - } - if (jet.r() != round(selectedJetsRadius * 100.0f)) { - continue; - } - registry.fill(HIST("h_jet_pt_rhoareasubtracted"), jet.pt() - (collision.rho() * jet.area()), 1.0); - - // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { - registry.fill(HIST("h_jet_pt_in_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); - registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); - } else { - registry.fill(HIST("h_jet_pt_out_of_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); - registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); - } - } - } else if (nmode == cfgNmodB) { - double phiMinusPsi3; - float ep3 = helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode); - for (auto const& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { - continue; - } - if (!isAcceptedJet(jet)) { - continue; - } - if (jet.r() != round(selectedJetsRadius * 100.0f)) { - continue; - } - // phiMinusPsi3 = jet.phi() - ep3; - phiMinusPsi3 = RecoDecay::constrainAngle(jet.phi() - ep3, -o2::constants::math::PI); - float absDelta3 = std::abs(phiMinusPsi3); - if ((absDelta3 < o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta3 < evtPlnAngleC * o2::constants::math::PIQuarter)) { - registry.fill(HIST("h_jet_pt_in_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); - } else { - registry.fill(HIST("h_jet_pt_out_of_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); - } - } - } - } } PROCESS_SWITCH(JetChargedV2, processInOutJetV2, "Jet V2 in and out of plane", false); @@ -1370,18 +1559,20 @@ struct JetChargedV2 { //=====================< evt pln [n=2->\Psi_2, n=3->\Psi_3] >=====================// int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refAInd = refAId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - int refBInd = refBId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refAInd = refAId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int refBInd = refBId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId && collision.qvecAmp()[refAId] > collQvecAmpDetId && collision.qvecAmp()[refBId] > collQvecAmpDetId) { - histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV2"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV2"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV2"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV2"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV2"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1393,10 +1584,12 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlRes_RefARefBV2"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } else if (nmode == cfgNmodB) { - histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); - histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); - histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); - histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + if (fillQvecQA) { + histosQA.fill(HIST("histQvecUncorV3"), collision.qvecRe()[detInd], collision.qvecIm()[detInd], collision.cent()); + histosQA.fill(HIST("histQvecRectrV3"), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], collision.cent()); + histosQA.fill(HIST("histQvecTwistV3"), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2], collision.cent()); + histosQA.fill(HIST("histQvecFinalV3"), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], collision.cent()); + } histosQA.fill(HIST("histEvtPlUncorV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode), collision.cent()); histosQA.fill(HIST("histEvtPlRectrV3"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1], nmode), collision.cent()); @@ -1408,80 +1601,6 @@ struct JetChargedV2 { histosQA.fill(HIST("histEvtPlRes_RefARefBV3"), helperEP.GetResolution(helperEP.GetEventPlane(collision.qvecRe()[refAInd + 3], collision.qvecIm()[refAInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[refBInd + 3], collision.qvecIm()[refBInd + 3], nmode), nmode), collision.cent()); } } - - float centrality = -1.0; - switch (centralityMode) { - case 1: - centrality = collision.centFT0M(); - break; - case 2: - centrality = collision.centFT0A(); - break; - default: - centrality = collision.centFT0C(); - break; - } - int evtPlnAngleA = 7; - int evtPlnAngleB = 3; - int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); - - if (nmode == cfgNmodA) { - double phiMinusPsi2; - if (collision.qvecAmp()[detId] < collQvecAmpDetId) { - continue; - } - float ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode); - for (auto const& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { - continue; - } - if (!isAcceptedJet(jet)) { - continue; - } - if (jet.r() != round(selectedJetsRadius * 100.0f)) { - continue; - } - registry.fill(HIST("h_jet_pt_rhoareasubtracted"), jet.pt() - (collision.rho() * jet.area()), 1.0); - - // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { - registry.fill(HIST("h_jet_pt_in_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); - registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); - } else { - registry.fill(HIST("h_jet_pt_out_of_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); - registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); - } - } - } else if (nmode == cfgNmodB) { - double phiMinusPsi3; - float ep3 = helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode); - for (auto const& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { - continue; - } - if (!isAcceptedJet(jet)) { - continue; - } - if (jet.r() != round(selectedJetsRadius * 100.0f)) { - continue; - } - // phiMinusPsi3 = jet.phi() - ep3; - phiMinusPsi3 = RecoDecay::constrainAngle(jet.phi() - ep3, -o2::constants::math::PI); - float absDelta3 = std::abs(phiMinusPsi3); - - if ((absDelta3 < o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta3 < evtPlnAngleC * o2::constants::math::PIQuarter)) { - registry.fill(HIST("h_jet_pt_in_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); - } else { - registry.fill(HIST("h_jet_pt_out_of_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); - } - } - } - } } PROCESS_SWITCH(JetChargedV2, processInOutJetV2MCD, "Jet V2 in and out of plane MCD", false); @@ -1512,6 +1631,7 @@ struct JetChargedV2 { if (!isAcceptedJet(jet)) { continue; } + fillJetAreaSubHistograms(jet, centrality, collision.rho()); } double leadingJetPt = -1; @@ -1523,7 +1643,8 @@ struct JetChargedV2 { int nTrk = 0; getNtrk(tracks, jets, nTrk, evtnum, leadingJetEta); if (nTrk <= 0) { - return; + registry.fill(HIST("h_collisions"), 4.5); + return; // otherwise the fit histogram below would be created with 0 bins } hPtsumSumptFit = new TH1F("h_ptsum_sumpt_fit", "h_ptsum_sumpt fit use", TMath::CeilNint(std::sqrt(nTrk)), 0., o2::constants::math::TwoPI); @@ -1533,12 +1654,20 @@ struct JetChargedV2 { double ep3 = 0.; int cfgNmodA = 2; int cfgNmodB = 3; + // In-plane / out-of-plane windows, identical to the downstream QA and unfolding + // macros (analysisCode_headFiles.cpp::GetInOutPlane2D), which cut + // Delta phi = phi - Psi_2 in [0, 2pi) at pi/4, 3pi/4, 5pi/4 and 7pi/4: + // in-plane : [0, pi/4) U [3pi/4, 5pi/4) U [7pi/4, 2pi) + // out-of-plane : [pi/4, 3pi/4) U [5pi/4, 7pi/4) + // The same composite expression also gives the correct region for a folded + // |Delta phi| in [0, pi], namely [0, pi/4) U [3pi/4, pi]. int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -1551,23 +1680,20 @@ struct JetChargedV2 { } const char* fitFunctionV2v3 = "[0] * (1. + 2. * ([1] * std::cos(2. * (x - [2])) + [3] * std::cos(3. * (x - [4]))))"; - fFitModulationV2v3 = new TF1("fit_kV3", fitFunctionV2v3, 0, o2::constants::math::TwoPI); + fFitModulationV2v3 = new TF1("fit_kV3", fitFunctionV2v3, -1, o2::constants::math::TwoPI + 1); //=========================< set parameter >=========================// fFitModulationV2v3->SetParameter(0, 1.); fFitModulationV2v3->SetParameter(1, 0.01); fFitModulationV2v3->SetParameter(3, 0.01); - double ep2fix = 0.; - double ep3fix = 0.; - if (ep2 < 0) { - ep2fix = RecoDecay::constrainAngle(ep2); + double ep2fix = RecoDecay::constrainAngle(ep2); fFitModulationV2v3->FixParameter(2, ep2fix); } else { fFitModulationV2v3->FixParameter(2, ep2); } if (ep3 < 0) { - ep3fix = RecoDecay::constrainAngle(ep3); + double ep3fix = RecoDecay::constrainAngle(ep3); fFitModulationV2v3->FixParameter(4, ep3fix); } else { fFitModulationV2v3->FixParameter(4, ep3); @@ -1575,7 +1701,8 @@ struct JetChargedV2 { hPtsumSumptFit->Fit(fFitModulationV2v3, "Q", "ep", 0, o2::constants::math::TwoPI); - double temppara[5]; + // double temppara[5]; + std::array temppara{}; temppara[0] = fFitModulationV2v3->GetParameter(0); temppara[1] = fFitModulationV2v3->GetParameter(1); temppara[2] = fFitModulationV2v3->GetParameter(2); @@ -1589,56 +1716,45 @@ struct JetChargedV2 { registry.fill(HIST("h_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_evtnum_centrlity"), evtnum, centrality); - if (temppara[0] == 0) { - registry.fill(HIST("h_badfit_counter"), 1); - } - - int nDF = 1; - int numOfFreePara = 2; - nDF = static_cast(fFitModulationV2v3->GetXaxis()->GetNbins()) - numOfFreePara; - if (nDF == 0 || static_cast(nDF) <= 0.) - return; + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; double chi2 = 0.; for (int i = 0; i < hPtsumSumptFit->GetXaxis()->GetNbins(); i++) { - if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) - continue; + if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) { + continue; // empty bins do not enter the chi2 and must not count in the dof + } + nBinsUsed++; chi2 += std::pow((hPtsumSumptFit->GetBinContent(i + 1) - fFitModulationV2v3->Eval(hPtsumSumptFit->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFit->GetBinContent(i + 1); } - - double chiSqr = 999.; - double cDF = 1.; - - chiSqr = chi2; - cDF = 1. - chiSquareCDF(nDF, chiSqr); + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters int evtCentAreaMin = 0; int evtCentAreaMax = 5; int evtMidAreaMin = 30; int evtMidAreaMax = 50; - if (cfgChkFitQuality) { + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); registry.fill(HIST("h_PvalueCDF_CombinFit"), cDF); registry.fill(HIST("h2_PvalueCDFCent_CombinFit"), centrality, cDF); - registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / (static_cast(nDF))); - registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - - } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); - } - if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - + registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); } } - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int detIndFT0M = 4 * epQvec.detCheck + epQvec.cfgnTotalSystem * 4 * (nmode - 2); + int bumpLow = 75; + int bumpUp = 125; + bool hasBumpJet = false; for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1649,142 +1765,274 @@ struct JetChargedV2 { if (jet.r() != round(selectedJetsRadius * 100.0f)) { continue; } + if (jet.pt() > bumpLow && jet.pt() < bumpUp) { + hasBumpJet = true; + } + } + if (hasBumpJet && nmode == cfgNmodA) { + if (collision.qvecAmp()[detId] >= collQvecAmpDetId) { + histosQA.fill(HIST("h2_ep2_FT0C_FT0M_bumpRegion"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); + } + } - double integralValue = fFitModulationV2v3->Integral(jet.phi() - selectedJetsRadius, jet.phi() + selectedJetsRadius); - double rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; - registry.fill(HIST("h2_rholocal_cent"), centrality, rholocal, 1.0); + for (auto const& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + continue; + } + if (!isAcceptedJet(jet)) { + continue; + } + if (jet.r() != round(selectedJetsRadius * 100.0f)) { + continue; + } + double integralValue = getRhoLocalIntegral(fFitModulationV2v3, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); + + double rholocal = 0.0; + if (integralValue <= 0) { + rholocal = collision.rho(); + registry.fill(HIST("h_jet_pt_badRhoLocal"), jet.pt(), 1.0); + registry.fill(HIST("h_jet_phi_badRhoLocal"), jet.phi(), 1.0); + registry.fill(HIST("h_jet_eta_badRhoLocal"), jet.eta(), 1.0); + registry.fill(HIST("h_good_bad_ugly"), 1.5); + // LOGF(info, "integral rho_local got 0, checking jet info: pT=%.1f, eta=%.1f, phi=%.1f, integralValue=%.1f", jet.pt(), jet.eta(), jet.phi(), integralValue); + } else { + // integralValue = fFitModulationV2v3->Integral(phi - selectedJetsRadius, phi + selectedJetsRadius); + rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; + registry.fill(HIST("h_good_bad_ugly"), 0.5); + } + registry.fill(HIST("h_good_bad_ugly"), 2.5); if (nmode == cfgNmodA) { - double phiMinusPsi2; + double phiMinusPsi2 = 0.0; if (collision.qvecAmp()[detId] < collQvecAmpDetId) { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); - registry.fill(HIST("h2_phi_rholocal"), jet.phi() - ep2, rholocal, 1.0); - registry.fill(HIST("h2_phi_rholocal_absDelta"), absDelta, rholocal, 1.0); + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + float absDelta = std::abs(RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI)); + registry.fill(HIST("h2_rholocal_cent"), centrality, rholocal, 1.0); + registry.fill(HIST("h2_averagerho_cent"), centrality, collision.rho(), 1.0); + registry.fill(HIST("h2_centrality_pT"), jet.pt(), centrality, 1.0); + registry.fill(HIST("h2_phi_rholocal"), phiMinusPsi2, rholocal, 1.0); + registry.fill(HIST("h2_rholocal_pt"), jet.pt(), rholocal, 1.0); + registry.fill(HIST("h2_rholocal_raw_pt"), jet.pt() - rholocal * jet.area(), rholocal, 1.0); + registry.fill(HIST("h2_averagerho_pt"), jet.pt(), collision.rho(), 1.0); + registry.fill(HIST("h2_averagerho_raw_pt"), jet.pt() - collision.rho() * jet.area(), collision.rho(), 1.0); + + if (checkAbsDeltaPhi) { + int lowPtCut = 20; + int mediumPtCut = 40; + int highPtCut = 70; + int highPtCutEnd = 100; + registry.fill(HIST("h2_phi_rholocal_absDelta"), absDelta, rholocal, 1.0); + registry.fill(HIST("h2_phi_averagerho_absDelta"), absDelta, collision.rho(), 1.0); + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_absDelta_low"), absDelta, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_absDelta_medium"), absDelta, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_absDelta_high"), absDelta, rholocal, 1.0); + } + } + registry.fill(HIST("h_jet_pt_inclusive_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + if (fillTHnSparse && sparseSubsample > 0 && (jet.globalIndex() % sparseSubsample) == 0) { + const std::array qaJet = {jet.pt(), jet.eta(), jet.phi(), jet.area(), static_cast(jet.tracksIds().size()), centrality}; + hJetQA->Fill(qaJet.data(), 1.0); + const std::array rhoMedianData = {centrality, jet.pt(), collision.rho(), collision.rho() * jet.area(), phiMinusPsi2, jet.area()}; + hRhoMedian->Fill(rhoMedianData.data(), 1.0); + const std::array rhoLocalData = {centrality, jet.pt(), rholocal, rholocal * jet.area(), phiMinusPsi2, jet.area()}; + hRhoLocal->Fill(rhoLocalData.data(), 1.0); + } + + if (jet.pt() > bumpLow && jet.pt() < bumpUp) { + histosQA.fill(HIST("h2_ep2_FT0C_FT0M_bumpRegion"), helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode), helperEP.GetEventPlane(collision.qvecRe()[detIndFT0M + 3], collision.qvecIm()[detIndFT0M + 3], nmode), collision.cent()); + } + + int lowPtCut = 20; + int mediumPtCut = 40; + int highPtCut = 70; + int highPtCutEnd = 100; + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h_jet_pt_in_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); + + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_low_inplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_medium_inplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_Delta_high_inplane"), phiMinusPsi2, rholocal, 1.0); + } + + registry.fill(HIST("h2_averagerho_pt_inplane"), jet.pt(), collision.rho(), 1.0); + registry.fill(HIST("h2_rholocal_pt_inplane"), jet.pt(), rholocal, 1.0); + registry.fill(HIST("h2_rho_cent_median_in"), centrality, collision.rho(), 1.0); + registry.fill(HIST("h2_rho_cent_local_in"), centrality, rholocal, 1.0); } else { + registry.fill(HIST("h_jet_pt_out_of_plane_v2"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2"), centrality, jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h_jet_pt_out_of_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); + + if (jet.pt() >= lowPtCut && jet.pt() < mediumPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_low_outplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= mediumPtCut && jet.pt() < highPtCut) { + registry.fill(HIST("h2_phi_rholocal_Delta_medium_outplane"), phiMinusPsi2, rholocal, 1.0); + } else if (jet.pt() >= highPtCut && jet.pt() < highPtCutEnd) { + registry.fill(HIST("h2_phi_rholocal_Delta_high_outplane"), phiMinusPsi2, rholocal, 1.0); + } + + registry.fill(HIST("h2_averagerho_pt_outplane"), jet.pt(), collision.rho(), 1.0); + registry.fill(HIST("h2_rholocal_pt_outplane"), jet.pt(), rholocal, 1.0); + registry.fill(HIST("h2_rho_cent_median_out"), centrality, collision.rho(), 1.0); + registry.fill(HIST("h2_rho_cent_local_out"), centrality, rholocal, 1.0); + } + if (checkAbsDeltaPhi) { + if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h_jet_pt_in_plane_v2_abs"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h_jet_pt_in_plane_v2_rho_abs"), jet.pt() - (rholocal * jet.area()), 1.0); + } else { + registry.fill(HIST("h_jet_pt_out_of_plane_v2_abs"), jet.pt() - (collision.rho() * jet.area()), 1.0); + registry.fill(HIST("h_jet_pt_out_of_plane_v2_rho_abs"), jet.pt() - (rholocal * jet.area()), 1.0); + } } } else if (nmode == cfgNmodB) { - double phiMinusPsi3; + double phiMinusPsi3 = 0.0; if (collision.qvecAmp()[detId] < collQvecAmpDetId) { continue; } ep3 = helperEP.GetEventPlane(collision.qvecRe()[detInd], collision.qvecIm()[detInd], nmode); - // phiMinusPsi3 = jet.phi() - ep3; phiMinusPsi3 = RecoDecay::constrainAngle(jet.phi() - ep3, -o2::constants::math::PI); float absDelta3 = std::abs(phiMinusPsi3); if ((absDelta3 < o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta3 >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta3 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h_jet_pt_in_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_in_plane_v3_rho"), jet.pt() - (rholocal * jet.area()), 1.0); } else { + registry.fill(HIST("h_jet_pt_out_of_plane_v3"), jet.pt() - (collision.rho() * jet.area()), 1.0); registry.fill(HIST("h_jet_pt_out_of_plane_v3_rho"), jet.pt() - (rholocal * jet.area()), 1.0); } } } } // RCpT - for (uint i = 0; i < cfgnMods->size(); i++) { - // TRandom3 randomNumber(0); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; - double integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); - double rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; + double integralValueRC = 0.0; + double rholocalRC = 0.0; + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); + rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - int nmode = cfgnMods->at(i); + int nmode = epQvec.cfgnMods->at(i); if (nmode == cfgNmodA) { - double rcPhiPsi2; - double rcPhiPsi2Rand; - - // randomized φ check test - float randomPhiTest = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - float randomEtaTest = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); - float randomConePtTest = 0.0; - // randomized φ check test end - - // rcPhiPsi2 = randomConePhi - ep2; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); - // if (rcPhiPsi2 < 0) rcPhiPsi2 += o2::constants::math::TwoPI; - if (rcPhiPsi2 < 0) { - rcPhiPsi2 = RecoDecay::constrainAngle(rcPhiPsi2, 0.0F); - } - - rcPhiPsi2Rand = RecoDecay::constrainAngle(randomPhiTest - ep2, -o2::constants::math::PI); - // if (rcPhiPsi2Rand < 0) rcPhiPsi2Rand += o2::constants::math::TwoPI; - if (rcPhiPsi2Rand < 0) { - rcPhiPsi2Rand = RecoDecay::constrainAngle(rcPhiPsi2Rand, 0.0F); - } + double rcPhiPsi2 = 0.0; + float absRcPhiPsi2 = 0.0; + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); + int nTracksRandomCone = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - // float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); - // float dEta = track.eta() - randomConeEta; - // if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { - // randomConePt += track.pt(); - // } - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); + ++nTracksRandomCone; } + } + } + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + } - float dPhiTest = RecoDecay::constrainAngle(track.phi() - randomPhiTest, static_cast(-o2::constants::math::PI)); - float dEtaTest = track.eta() - randomEtaTest; - if (std::sqrt(dEtaTest * dEtaTest + dPhiTest * dPhiTest) < randomConeR) { - randomConePtTest += track.pt(); - } + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + if (fillTHnSparse && sparseSubsample > 0 && (collision.globalIndex() % sparseSubsample) == 0) { + const std::array qaFluct = {centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, + std::fabs(rcPhiPsi2), o2::constants::math::PI * randomConeR * randomConeR, rholocalRC, + static_cast(nTracksRandomCone)}; + hFluctQA->Fill(qaFluct.data(), 1.0); + } + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); } } - registry.fill(HIST("h1_distribution_RC"), rcPhiPsi2); - registry.fill(HIST("h1_distribution_random"), rcPhiPsi2Rand); - registry.fill(HIST("h3_randomizedPhi_check"), centrality, randomConePtTest - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2Rand, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); // removing the leading jet from the random cone + int nTracksRandomConeWoLeadingJet = 0; if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet - // float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); - // float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; - float dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, -o2::constants::math::PI); float dEtaLeadingJet = leadingJetEta - randomConeEta; bool jetWasInCone = false; - // while ((randomConeLeadJetDeltaR <= 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < jets.iteratorAt(0).r() / 100.0 + randomConeR)) || (randomConeLeadJetDeltaR > 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < randomConeLeadJetDeltaR))) { while ((randomConeLeadJetDeltaR <= 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < leadingJetR + randomConeR)) || (randomConeLeadJetDeltaR > 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < randomConeLeadJetDeltaR))) { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, -o2::constants::math::PI); dEtaLeadingJet = leadingJetEta - randomConeEta; } if (jetWasInCone) { randomConePt = 0.0; + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); + rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); + for (auto const& track : tracks) { - // if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > randomConeR)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - // float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); - // float dEta = track.eta() - randomConeEta; - // if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { - // randomConePt += track.pt(); - // } if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); + ++nTracksRandomConeWoLeadingJet; } } } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + } + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + if (fillTHnSparse && sparseSubsample > 0 && (collision.globalIndex() % sparseSubsample) == 0) { + const std::array qaFluctWo = {centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, + std::fabs(rcPhiPsi2), o2::constants::math::PI * randomConeR * randomConeR, rholocalRC, + static_cast(nTracksRandomConeWoLeadingJet)}; + hFluctQAwoLeadingJet->Fill(qaFluctWo.data(), 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + } } else if (nmode == cfgNmodB) { continue; } @@ -1880,9 +2128,9 @@ struct JetChargedV2 { int evtPlnAngleA = 7; int evtPlnAngleB = 3; int evtPlnAngleC = 5; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -1919,7 +2167,8 @@ struct JetChargedV2 { hPtsumSumptFit->Fit(fFitModulationV2v3, "Q", "ep", 0, o2::constants::math::TwoPI); - double temppara[5]; + // double temppara[5]; + std::array temppara{}; temppara[0] = fFitModulationV2v3->GetParameter(0); temppara[1] = fFitModulationV2v3->GetParameter(1); temppara[2] = fFitModulationV2v3->GetParameter(2); @@ -1933,49 +2182,40 @@ struct JetChargedV2 { registry.fill(HIST("h_v3obs_centrality"), centrality, temppara[3]); registry.fill(HIST("h_evtnum_centrlity"), evtnum, centrality); - if (temppara[0] == 0) { - registry.fill(HIST("h_badfit_counter"), 1); - } - - int nDF = 1; - int numOfFreePara = 2; - nDF = static_cast(fFitModulationV2v3->GetXaxis()->GetNbins()) - numOfFreePara; - if (nDF == 0 || static_cast(nDF) <= 0.) - return; + const int numOfFreePara = 3; // psi2/psi3 are fixed with FixParameter -> [0], [1], [3] are free + int nBinsUsed = 0; double chi2 = 0.; for (int i = 0; i < hPtsumSumptFit->GetXaxis()->GetNbins(); i++) { - if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) - continue; + if (hPtsumSumptFit->GetBinContent(i + 1) <= 0.) { + continue; // empty bins do not enter the chi2 and must not count in the dof + } + nBinsUsed++; chi2 += std::pow((hPtsumSumptFit->GetBinContent(i + 1) - fFitModulationV2v3->Eval(hPtsumSumptFit->GetXaxis()->GetBinCenter(1 + i))), 2) / hPtsumSumptFit->GetBinContent(i + 1); } - - double chiSqr = 999.; - double cDF = 1.; - - chiSqr = chi2; - cDF = 1. - chiSquareCDF(nDF, chiSqr); + const int nDF = nBinsUsed - numOfFreePara; // number of fitted points minus the free parameters int evtCentAreaMin = 0; int evtCentAreaMax = 5; int evtMidAreaMin = 30; int evtMidAreaMax = 50; - if (cfgChkFitQuality) { + if (nDF > 0 && cfgChkFitQuality) { + const double chiSqr = chi2; + const double cDF = 1. - chiSquareCDF(nDF, chiSqr); registry.fill(HIST("h_PvalueCDF_CombinFit"), cDF); registry.fill(HIST("h2_PvalueCDFCent_CombinFit"), centrality, cDF); - registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / (static_cast(nDF))); - registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_Chi2Cent_CombinFit"), centrality, chiSqr / static_cast(nDF)); + registry.fill(HIST("h2_PChi2_CombinFit"), cDF, chiSqr / static_cast(nDF)); if (centrality >= evtCentAreaMin && centrality <= evtCentAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / (static_cast(nDF))); - + registry.fill(HIST("h2_PChi2_CombinFitA"), cDF, chiSqr / static_cast(nDF)); } else if (centrality >= evtMidAreaMin && centrality <= evtMidAreaMax) { - registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / (static_cast(nDF))); + registry.fill(HIST("h2_PChi2_CombinFitB"), cDF, chiSqr / static_cast(nDF)); } } - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -1990,22 +2230,22 @@ struct JetChargedV2 { registry.fill(HIST("h_mcd_pt_before_matching_mcdprocess"), corrBasejetpt); } - double integralValue = fFitModulationV2v3->Integral(jet.phi() - selectedJetsRadius, jet.phi() + selectedJetsRadius); + double integralValue = getRhoLocalIntegral(fFitModulationV2v3, jet.phi(), selectedJetsRadius, rhoFitRangeMargin); double rholocal = collision.rho() / (2 * selectedJetsRadius * temppara[0]) * integralValue; registry.fill(HIST("h2_rholocal_cent"), centrality, rholocal, 1.0); if (nmode == cfgNmodA) { - double phiMinusPsi2; + double phiMinusPsi2 = 0.0; if (collision.qvecAmp()[detId] < collQvecAmpDetId) { continue; } // phiMinusPsi2 = jet.phi() - ep2; - phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, -o2::constants::math::PI); - float absDelta = std::abs(phiMinusPsi2); + phiMinusPsi2 = RecoDecay::constrainAngle(jet.phi() - ep2, 0.0f); + // same in-/out-of-plane definition as in processSigmaPtData (unfolded phi - Psi2) registry.fill(HIST("h2_phi_rholocal"), jet.phi() - ep2, rholocal, 1.0); registry.fill(HIST("h_jet_pt_inclusive_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); - if ((absDelta < o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absDelta >= evtPlnAngleB * o2::constants::math::PIQuarter && absDelta < evtPlnAngleC * o2::constants::math::PIQuarter)) { + if ((phiMinusPsi2 < o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (phiMinusPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && phiMinusPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { registry.fill(HIST("h_jet_pt_in_plane_v2_rho"), jet.pt() - (rholocal * jet.area()), 1.0); registry.fill(HIST("h2_centrality_jet_pt_in_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); } else { @@ -2013,7 +2253,7 @@ struct JetChargedV2 { registry.fill(HIST("h2_centrality_jet_pt_out_of_plane_v2_rho"), centrality, jet.pt() - (rholocal * jet.area()), 1.0); } } else if (nmode == cfgNmodB) { - double phiMinusPsi3; + double phiMinusPsi3 = 0.0; if (collision.qvecAmp()[detId] < collQvecAmpDetId) { continue; } @@ -2031,36 +2271,52 @@ struct JetChargedV2 { } } // RCpT - for (uint i = 0; i < cfgnMods->size(); i++) { - // TRandom3 randomNumber(0); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; - double integralValueRC = fFitModulationV2v3->Integral(randomConePhi - randomConeR, randomConePhi + randomConeR); + double integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); double rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; - int nmode = cfgnMods->at(i); + int nmode = epQvec.cfgnMods->at(i); if (nmode == cfgNmodA) { - double rcPhiPsi2; + double rcPhiPsi2 = 0.0; + float absRcPhiPsi2 = 0.0; // rcPhiPsi2 = randomConePhi - ep2; - rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI); + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + // folded |Delta phi| in [0, pi], only used by the checkAbsDeltaPhi cross-check + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphi"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + } + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphi_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + } + } // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet - // float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + // float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, -o2::constants::math::PI); // float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; - float dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(leadingJetPhi - randomConePhi, -o2::constants::math::PI); float dEtaLeadingJet = leadingJetEta - randomConeEta; bool jetWasInCone = false; @@ -2069,21 +2325,25 @@ struct JetChargedV2 { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, -o2::constants::math::PI); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePt = 0.0; + integralValueRC = getRhoLocalIntegral(fFitModulationV2v3, randomConePhi, randomConeR, rhoFitRangeMargin); + rholocalRC = collision.rho() / (2 * randomConeR * temppara[0]) * integralValueRC; + rcPhiPsi2 = RecoDecay::constrainAngle(randomConePhi - ep2, 0.0); + absRcPhiPsi2 = std::abs(RecoDecay::constrainAngle(randomConePhi - ep2, -o2::constants::math::PI)); for (auto const& track : tracks) { // if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > randomConeR)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - // float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + // float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); // float dEta = track.eta() - randomConeEta; // if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { // randomConePt += track.pt(); // } // } if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-o2::constants::math::PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, -o2::constants::math::PI); float dEta = track.eta() - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); @@ -2092,8 +2352,26 @@ struct JetChargedV2 { } } } - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); - registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + if (fillFluctH3) { + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, rcPhiPsi2, 1.0); + registry.fill(HIST("h3_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), rcPhiPsi2, 1.0); + } + if ((rcPhiPsi2 < o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (rcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && rcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + if (checkAbsDeltaPhi) { + if ((absRcPhiPsi2 < o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleA * o2::constants::math::PIQuarter) || (absRcPhiPsi2 >= evtPlnAngleB * o2::constants::math::PIQuarter && absRcPhiPsi2 < evtPlnAngleC * o2::constants::math::PIQuarter)) { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_in_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } else { + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_localrhovsphiwithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rholocalRC, 1.0); + registry.fill(HIST("h2_centrality_deltapT_RandomCornPhi_rhorandomconewithoutleadingjet_out_abs"), centrality, randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), 1.0); + } + } } else if (nmode == cfgNmodB) { continue; } @@ -2253,6 +2531,8 @@ struct JetChargedV2 { leadingJetPt = mcdjet.pt(); leadingJetEta = mcdjet.eta(); } + registry.fill(HIST("leadJetPt"), leadingJetPt); + registry.fill(HIST("leadJetEta"), leadingJetEta); int nTrk = 0; double localRhoFitPtMin = 0.2; double localRhoFitPtMax = 5; @@ -2278,9 +2558,9 @@ struct JetChargedV2 { int cfgNmodA = 2; int cfgNmodB = 3; - for (uint i = 0; i < cfgnMods->size(); i++) { - int nmode = cfgnMods->at(i); - int detInd = detId * 4 + cfgnTotalSystem * 4 * (nmode - 2); + for (uint i = 0; i < epQvec.cfgnMods->size(); i++) { + int nmode = epQvec.cfgnMods->at(i); + int detInd = detId * 4 + epQvec.cfgnTotalSystem * 4 * (nmode - 2); if (nmode == cfgNmodA) { if (collision.qvecAmp()[detId] > collQvecAmpDetId) { ep2 = helperEP.GetEventPlane(collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3], nmode); @@ -2317,7 +2597,7 @@ struct JetChargedV2 { hPtsumSumptFitRM->Fit(fFitModulationRM, "Q", "ep", 0, o2::constants::math::TwoPI); - float tempparaA; + float tempparaA = 0.f; tempparaA = fFitModulationRM->GetParameter(0); if (tempparaA == 0) { @@ -2337,7 +2617,7 @@ struct JetChargedV2 { PROCESS_SWITCH(JetChargedV2, processJetsMatchedSubtracted, "matched mcp and mcd jets", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGJE/Tasks/jetCorrelationD0.cxx b/PWGJE/Tasks/jetCorrelationD0.cxx index 0196aec6d5b..c5ffa65bdd0 100644 --- a/PWGJE/Tasks/jetCorrelationD0.cxx +++ b/PWGJE/Tasks/jetCorrelationD0.cxx @@ -13,6 +13,7 @@ /// \brief Task for analysing D0 triggered jet events. /// \author Matthew Ockleton matthew.ockleton@cern.ch, University of Liverpool +#include "PWGHF/Core/DecayChannels.h" #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetHFUtilities.h" #include "PWGJE/DataModel/Jet.h" @@ -21,7 +22,6 @@ #include "Common/Core/RecoDecay.h" #include -#include #include #include #include @@ -32,16 +32,19 @@ #include #include +#include + +#include +#include #include #include #include -#include - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +namespace hf2Prong = o2::hf_decay::hf_cand_2prong; namespace o2::aod { @@ -84,7 +87,8 @@ DECLARE_SOA_COLUMN(D0MD, d0MD, float); DECLARE_SOA_COLUMN(D0PtD, d0PtD, float); DECLARE_SOA_COLUMN(D0EtaD, d0EtaD, float); DECLARE_SOA_COLUMN(D0PhiD, d0PhiD, float); -DECLARE_SOA_COLUMN(D0Reflection, d0Reflection, int); +DECLARE_SOA_COLUMN(D0Category, d0Category, int); +DECLARE_SOA_COLUMN(D0DecayChannel, d0DecayChannel, int8_t); } // namespace d0Info DECLARE_SOA_TABLE(D0Tables, "AOD", "D0TABLE", @@ -99,6 +103,19 @@ DECLARE_SOA_TABLE(D0Tables, "AOD", "D0TABLE", d0Info::D0Phi, d0Info::D0Y); +DECLARE_SOA_TABLE(D0McDTables, "AOD", "D0MCDTABLE", + o2::soa::Index<>, + collisionInfo::McCollisionTableId, + d0Info::D0PromptBDT, + d0Info::D0NonPromptBDT, + d0Info::D0BkgBDT, + d0Info::D0M, + d0Info::D0Pt, + d0Info::D0Eta, + d0Info::D0Phi, + d0Info::D0Y, + d0Info::D0Category); + DECLARE_SOA_TABLE(D0McPTables, "AOD", "D0MCPTABLE", o2::soa::Index<>, collisionInfo::McCollisionTableId, @@ -106,12 +123,14 @@ DECLARE_SOA_TABLE(D0McPTables, "AOD", "D0MCPTABLE", d0Info::D0Pt, d0Info::D0Eta, d0Info::D0Phi, - d0Info::D0Y); + d0Info::D0Y, + d0Info::D0DecayChannel); namespace jetInfo { // D0 tables DECLARE_SOA_INDEX_COLUMN(D0Table, d0Table); +DECLARE_SOA_INDEX_COLUMN(D0McDTable, d0McDTable); DECLARE_SOA_INDEX_COLUMN(D0McPTable, d0McPTable); // Jet DECLARE_SOA_COLUMN(JetPt, jetPt, float); @@ -134,6 +153,15 @@ DECLARE_SOA_TABLE_STAGED(JetTables, "JETTABLE", jetInfo::JetPhi, jetInfo::D0JetDeltaPhi); +DECLARE_SOA_TABLE_STAGED(JetMcDTables, "JETMCDTABLE", + o2::soa::Index<>, + collisionInfo::CollisionTableId, + jetInfo::D0McDTableId, + jetInfo::JetPt, + jetInfo::JetEta, + jetInfo::JetPhi, + jetInfo::D0JetDeltaPhi); + DECLARE_SOA_TABLE_STAGED(JetMcPTables, "JETMCPTABLE", o2::soa::Index<>, collisionInfo::McCollisionTableId, @@ -160,11 +188,13 @@ DECLARE_SOA_TABLE_STAGED(JetMatchedTables, "JETMATCHEDTABLE", struct JetCorrelationD0 { // Define new table Produces tableCollision; - Produces tableMatchedCollision; Produces tableMcCollision; + Produces tableMatchedCollision; Produces tableD0; + Produces tableD0McDetector; Produces tableD0McParticle; Produces tableJet; + Produces tableJetMcDetector; Produces tableJetMcParticle; Produces tableJetMatched; @@ -224,7 +254,7 @@ struct JetCorrelationD0 { } void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); // General Axes AxisSpec axisEta = {100, -1.0, 1.0, "#eta"}; AxisSpec axisPhi = {100, 0.0, o2::constants::math::TwoPI, "#phi"}; @@ -261,6 +291,15 @@ struct JetCorrelationD0 { registry.add("hPhiResolution", "#phi resolution;#p_{T,part};Resolution", {HistType::kTH2F, {{400, 0, 400}, {1000, -7.0, 7.0}}}); registry.add("hEtaResolution", "#eta resolution;#p_{T,part};Resolution", {HistType::kTH2F, {{400, 0, 400}, {1000, -1.0, 1.0}}}); } + enum D0McCategory : int { + Undefined = -1, // no truth match / unclassified + Signal = 0, // correctly identified D0 and (bar), π+ K− + cc + Reflection = 1, // true D0(bar) reconstructed with swapped mass hypothesis + CorrBkgPiKPi0 = 2, // correlated background: π+ K− π0 + CorrBkgPiPi = 3, // correlated background: π+ π− + CorrBkgPiPiPi0 = 4, // correlated background: π+ π− π0 + CorrBkgKK = 5 // correlated background: K+ K− + }; void processData(soa::Filtered::iterator const& collision, aod::CandidatesD0Data const& d0Candidates, soa::Join const& jets) @@ -318,15 +357,49 @@ struct JetCorrelationD0 { } const auto scores = d0Candidate.mlScores(); fillD0Histograms(d0Candidate, scores); - tableD0(tableCollision.lastIndex(), // might want to add some more detector level D0 quantities like prompt or non prompt info - scores[2], - scores[1], - scores[0], - d0Candidate.m(), - d0Candidate.pt(), - d0Candidate.eta(), - d0Candidate.phi(), - d0Candidate.y()); + + // flagMcMatchRec() = sign * DecayChannelMain + // |value| identifies the decay channel (D0ToPiK=1, ..., D0ToKK=5), sign identifies D0(+)/D0bar(-), 0 = no match. + int8_t d0DecayChannel = d0Candidate.flagMcMatchRec(); + + int matchedFrom = 0; + int selectedAs = 0; + int category = D0McCategory::Undefined; + + if (d0DecayChannel > 0) { // matched to a D0 on truth level (any channel) + matchedFrom = 1; + } else if (d0DecayChannel < 0) { // matched to a D0bar on truth level (any channel) + matchedFrom = -1; + } + if ((d0Candidate.candidateSelFlag() & BIT(0)) != 0) { // CandidateSelFlag == BIT(0) -> selected as D0 + selectedAs = 1; + } else if ((d0Candidate.candidateSelFlag() & BIT(1)) != 0) { // CandidateSelFlag == BIT(1) -> selected as D0bar + selectedAs = -1; + } + if ((std::abs(d0DecayChannel) == hf2Prong::DecayChannelMain::D0ToPiK) && (matchedFrom != 0) && (selectedAs == matchedFrom)) { + category = D0McCategory::Signal; // D0 or D0bar, π+ K− + } else if ((std::abs(d0DecayChannel) == hf2Prong::DecayChannelMain::D0ToPiK) && (matchedFrom != 0) && (selectedAs == -1 * matchedFrom)) { + category = D0McCategory::Reflection; + } else if (std::abs(d0DecayChannel) == hf2Prong::DecayChannelMain::D0ToPiKPi0) { + category = D0McCategory::CorrBkgPiKPi0; + } else if (std::abs(d0DecayChannel) == hf2Prong::DecayChannelMain::D0ToPiPi) { + category = D0McCategory::CorrBkgPiPi; + } else if (std::abs(d0DecayChannel) == hf2Prong::DecayChannelMain::D0ToPiPiPi0) { + category = D0McCategory::CorrBkgPiPiPi0; + } else if (std::abs(d0DecayChannel) == hf2Prong::DecayChannelMain::D0ToKK) { + category = D0McCategory::CorrBkgKK; + } + + tableD0McDetector(tableCollision.lastIndex(), // might want to add some more detector level D0 quantities like prompt or non prompt info + scores[2], + scores[1], + scores[0], + d0Candidate.m(), + d0Candidate.pt(), + d0Candidate.eta(), + d0Candidate.phi(), + d0Candidate.y(), + category); for (const auto& jet : jets) { if (jet.pt() < jetPtCutMin) { continue; @@ -336,12 +409,12 @@ struct JetCorrelationD0 { continue; } fillJetHistograms(jet, dPhi); - tableJet(tableCollision.lastIndex(), - tableD0.lastIndex(), - jet.pt(), - jet.eta(), - jet.phi(), - dPhi); + tableJetMcDetector(tableCollision.lastIndex(), + tableD0McDetector.lastIndex(), + jet.pt(), + jet.eta(), + jet.phi(), + dPhi); } } } @@ -364,7 +437,8 @@ struct JetCorrelationD0 { d0McPCandidate.pt(), d0McPCandidate.eta(), d0McPCandidate.phi(), - d0McPCandidate.y()); + d0McPCandidate.y(), + d0McPCandidate.flagMcMatchGen()); for (const auto& jet : jets) { if (jet.pt() < jetMcPtCutMin) { @@ -401,11 +475,7 @@ struct JetCorrelationD0 { if (d0Candidate.pt() < d0PtCutMin) { // once settled on a mlcut, then add the lower bound of the systematics as a cut here continue; } - bool isMatched = false; - const auto& d0Particle = jethfutilities::matchedHFParticle(d0Candidate, tracks, particles, isMatched); - if (!isMatched) { - continue; - } + const auto& d0Particle = jethfutilities::matchedHFParticle(d0Candidate, tracks, particles); for (const auto& McDJet : McDJets) { if (McDJet.pt() < jetPtCutMin) { continue; @@ -417,9 +487,6 @@ struct JetCorrelationD0 { if (McDJet.has_matchedJetGeo()) { // geometric matching for (auto const& McPJet : McDJet.template matchedJetGeo_as()) { float dPhiP = RecoDecay::constrainAngle(McPJet.phi() - d0Particle.phi(), -o2::constants::math::PI); - // if (std::abs(dPhiP - o2::constants::math::PI) > (o2::constants::math::PI / 2)) { - // continue; - // } tableJetMatched(tableMatchedCollision.lastIndex(), McDJet.pt(), McDJet.eta(), diff --git a/PWGJE/Tasks/jetCorrelationDmeson.cxx b/PWGJE/Tasks/jetCorrelationDmeson.cxx new file mode 100644 index 00000000000..666b88ab04b --- /dev/null +++ b/PWGJE/Tasks/jetCorrelationDmeson.cxx @@ -0,0 +1,623 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file jetCorrelationDmeson.cxx +/// \brief Task for analysing D-meson (D0, D*) triggered jet events +/// \author Matthew Ockleton matthew.ockleton@cern.ch, University of Liverpool +/// \author Mokshi Vaid mokshi.vaid@cern.ch, University of Jammu + +#include "PWGHF/Core/DecayChannels.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetHFUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace hf_2prong = o2::hf_decay::hf_cand_2prong; +namespace hf_dstar = o2::hf_decay::hf_cand_dstar; + +namespace o2::aod +{ + +namespace d_collision_info +{ +DECLARE_SOA_COLUMN(PosZ, posZ, float); +} // namespace d_collision_info + +DECLARE_SOA_TABLE(CollisionTables, "AOD", "COLLINFOTABLE", + o2::soa::Index<>, + d_collision_info::PosZ); + +DECLARE_SOA_TABLE(McCollisionTables, "AOD", "MCCOLLINFOTABLE", + o2::soa::Index<>, + d_collision_info::PosZ); + +DECLARE_SOA_TABLE(MatchCollTables, "AOD", "MATCHCOLLTABLE", + o2::soa::Index<>, + d_collision_info::PosZ); + +namespace collision_info +{ +DECLARE_SOA_INDEX_COLUMN_CUSTOM(CollisionTable, collisionTable, "COLLINFOTABLES"); +DECLARE_SOA_INDEX_COLUMN_CUSTOM(McCollisionTable, mcCollisionTable, "MCCOLLINFOTABLES"); +DECLARE_SOA_INDEX_COLUMN_CUSTOM(MatchCollTable, matchCollTable, "MATCHCOLLTABLES"); +} // namespace collision_info + +namespace d_info +{ +DECLARE_SOA_COLUMN(DPromptBDT, dPromptBDT, float); +DECLARE_SOA_COLUMN(DNonPromptBDT, dNonPromptBDT, float); +DECLARE_SOA_COLUMN(DBkgBDT, dBkgBDT, float); +DECLARE_SOA_COLUMN(DM, dM, float); +DECLARE_SOA_COLUMN(DDeltaM, dDeltaM, float); // -1 for D0 ("not applicable"); M(D*)-M(D0) for D* +DECLARE_SOA_COLUMN(DPt, dPt, float); +DECLARE_SOA_COLUMN(DEta, dEta, float); +DECLARE_SOA_COLUMN(DPhi, dPhi, float); +DECLARE_SOA_COLUMN(DY, dY, float); +DECLARE_SOA_COLUMN(DMcOrigin, dMcOrigin, float); +DECLARE_SOA_COLUMN(DCategory, dCategory, int); +DECLARE_SOA_COLUMN(DDecayChannel, dDecayChannel, int8_t); +} // namespace d_info + +DECLARE_SOA_TABLE(DTables, "AOD", "DTABLE", + o2::soa::Index<>, + collision_info::CollisionTableId, + d_info::DPromptBDT, + d_info::DNonPromptBDT, + d_info::DBkgBDT, + d_info::DM, + d_info::DDeltaM, + d_info::DPt, + d_info::DEta, + d_info::DPhi, + d_info::DY); + +// MC-detector level: shared table, category column +DECLARE_SOA_TABLE(DMcDTables, "AOD", "DMCDTABLE", + o2::soa::Index<>, + collision_info::CollisionTableId, + d_info::DPromptBDT, + d_info::DNonPromptBDT, + d_info::DBkgBDT, + d_info::DM, + d_info::DDeltaM, + d_info::DPt, + d_info::DEta, + d_info::DPhi, + d_info::DY, + d_info::DCategory); + +// MC-particle level +DECLARE_SOA_TABLE(DMcPTables, "AOD", "DMCPTABLE", + o2::soa::Index<>, + collision_info::McCollisionTableId, + d_info::DMcOrigin, + d_info::DPt, + d_info::DEta, + d_info::DPhi, + d_info::DY, + d_info::DDecayChannel); + +namespace jet_info +{ +// Shared D-meson table indices (were D0Table/DstarTable etc. separately) +DECLARE_SOA_INDEX_COLUMN(DTable, dTable); +DECLARE_SOA_INDEX_COLUMN(DMcDTable, dMcDTable); +DECLARE_SOA_INDEX_COLUMN(DMcPTable, dMcPTable); +// Jet +DECLARE_SOA_COLUMN(JetPt, jetPt, float); +DECLARE_SOA_COLUMN(JetEta, jetEta, float); +DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); +DECLARE_SOA_COLUMN(PJetPt, pJetPt, float); +DECLARE_SOA_COLUMN(PJetEta, pJetEta, float); +DECLARE_SOA_COLUMN(PJetPhi, pJetPhi, float); +// D-jet +DECLARE_SOA_COLUMN(DJetDeltaPhi, dJetDeltaPhi, float); +DECLARE_SOA_COLUMN(DJetDeltaPhiP, dJetDeltaPhiP, float); +} // namespace jet_info + +DECLARE_SOA_TABLE_STAGED(JetTables, "JETTABLE", + o2::soa::Index<>, + collision_info::CollisionTableId, + jet_info::DTableId, + jet_info::JetPt, + jet_info::JetEta, + jet_info::JetPhi, + jet_info::DJetDeltaPhi); + +DECLARE_SOA_TABLE_STAGED(JetMcDTables, "JETMCDTABLE", + o2::soa::Index<>, + collision_info::CollisionTableId, + jet_info::DMcDTableId, + jet_info::JetPt, + jet_info::JetEta, + jet_info::JetPhi, + jet_info::DJetDeltaPhi); + +DECLARE_SOA_TABLE_STAGED(JetMcPTables, "JETMCPTABLE", + o2::soa::Index<>, + collision_info::McCollisionTableId, + jet_info::DMcPTableId, + jet_info::JetPt, + jet_info::JetEta, + jet_info::JetPhi, + jet_info::DJetDeltaPhiP); + +DECLARE_SOA_TABLE_STAGED(JetMatchedTables, "JETMATCHEDTABLE", + o2::soa::Index<>, + collision_info::MatchCollTableId, + jet_info::JetPt, + jet_info::JetEta, + jet_info::JetPhi, + jet_info::PJetPt, + jet_info::PJetEta, + jet_info::PJetPhi, + jet_info::DJetDeltaPhi, + jet_info::DJetDeltaPhiP); + +} // namespace o2::aod + +struct JetCorrelationDmeson { + // Define new tables + Produces tableCollision; + Produces tableMcCollision; + Produces tableMatchedCollision; + Produces tableD; + Produces tableDMcDetector; + Produces tableDMcParticle; + Produces tableJet; + Produces tableJetMcDetector; + Produces tableJetMcParticle; + Produces tableJetMatched; + + // Configurables + Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; + Configurable skipMBGapEvents{"skipMBGapEvents", false, "decide to run over MB gap events or not"}; + Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + Configurable jetPtCutMin{"jetPtCutMin", 5.0, "minimum value of jet pt"}; + Configurable dPtCutMin{"dPtCutMin", 1.0, "minimum value of D-meson pt"}; + Configurable jetMcPtCutMin{"jetMcPtCutMin", 3.0, "minimum value of jet pt particle level"}; + Configurable dMcPtCutMin{"dMcPtCutMin", 0.5, "minimum value of D-meson pt particle level"}; + Configurable vertexZCut{"vertexZCut", 10.0, "Accepted z-vertex range"}; + + // Filters + Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut); + std::vector eventSelectionBits; + + // Histograms + HistogramRegistry registry{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // D0 and D* get their OWN distinct numeric values (not shared), since the + // classification is tied to species-specific decay-channel information + enum DCategory : int { + Undefined = -1, // no truth match / unclassified + // D0 + Signal = 0, // correctly identified D0 and (bar), π+ K− + cc + Reflection = 1, // true D0(bar) reconstructed with swapped mass hypothesis + CorrBkg1 = 2, // correlated background: π+ K− π0 + CorrBkg2 = 3, // correlated background: π+ π− + CorrBkg3 = 4, // correlated background: π+ π− π0 + CorrBkg4 = 5, // correlated background: K+ K− + // D* + DstarSignal = 6, // correctly identified D* and (bar), (D0 → π+ K−) π+ + cc + DstarReflection = 7, // true D*(bar) reconstructed with swapped mass hypothesis + DstarCorrBkg = 8 // correlated background: (D0 → π+ K− π0) π+ + }; + + template + void fillDHistograms(T const& dCandidate, U const& scores) + { + registry.fill(HIST("hDMlBkg"), scores[0]); + registry.fill(HIST("hDMlNonPrompt"), scores[1]); + registry.fill(HIST("hDMlPrompt"), scores[2]); + + registry.fill(HIST("hDPt"), dCandidate.pt()); + registry.fill(HIST("hDM"), dCandidate.m()); + registry.fill(HIST("hDEta"), dCandidate.eta()); + registry.fill(HIST("hDPhi"), dCandidate.phi()); + // (D0 candidates have no invMassCharm()). + if constexpr (std::is_same_v || + std::is_same_v) { + registry.fill(HIST("hDDeltaM"), dCandidate.m() - dCandidate.invMassCharm()); + } + } + + template + void fillJetHistograms(T const& jet, U const& dPhi) + { + registry.fill(HIST("hJetPt"), jet.pt()); + registry.fill(HIST("hJetEta"), jet.eta()); + registry.fill(HIST("hJetPhi"), jet.phi()); + registry.fill(HIST("hJet3D"), jet.pt(), jet.eta(), jet.phi()); + registry.fill(HIST("h_Jet_D_Jet_dPhi"), dPhi); + registry.fill(HIST("h_Jet_pT_D_Jet_dPhi"), jet.pt(), dPhi); + } + + template + bool applyCollisionSelections(T const& collision) + { + registry.fill(HIST("hCollisions"), 0.5); // All collisions + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + return false; + } + registry.fill(HIST("hCollisions"), 1.5); // Selected collisions + registry.fill(HIST("hZvtxSelected"), collision.posZ()); + return true; + } + + void init(InitContext const&) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + // General Axes + AxisSpec axisEta = {100, -1.0, 1.0, "#eta"}; + AxisSpec axisPhi = {100, 0.0, o2::constants::math::TwoPI, "#phi"}; + AxisSpec axisInvMass = {500, 0, 10, "M (GeV/c)"}; + + // General Histograms + registry.add("hCollisions", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}); + registry.add("hZvtxSelected", "Z vertex position;Z_{vtx};entries", {HistType::kTH1F, {{80, -20, 20}}}); + + // D-meson Histograms (shared) + registry.add("hDMlPrompt", "D ML Prompt Scores", {HistType::kTH1F, {{100, -1.0, 2.0}}}); + registry.add("hDMlNonPrompt", "D ML NonPrompt Scores", {HistType::kTH1F, {{100, -1.0, 2.0}}}); + registry.add("hDMlBkg", "D ML Background Scores", {HistType::kTH1F, {{100, -1.0, 2.0}}}); + + registry.add("hDPt", "D p_{T};p_{T}^{D} (GeV/c);entries", {HistType::kTH1F, {{500, -100, 400, "p_{T}^{D} (GeV/c)"}}}); + registry.add("hDM", "D Mass;M (GeV/c);entries", HistType::kTH1F, {axisInvMass}); + registry.add("hDEta", "D #eta ;#eta_{D};entries", HistType::kTH1F, {axisEta}); + registry.add("hDPhi", "D #phi ;#phi_{D};entries", HistType::kTH1F, {axisPhi}); + + // Delta-m: only ever filled for D* + registry.add("hDDeltaM", "D* - D0 Mass Difference;#Delta M (GeV/c);entries", {HistType::kTH1F, {{300, 0.13, 0.17}}}); + + // Jet Histograms + registry.add("hJetPt", "jet p_{T};p_{T,jet};entries", {HistType::kTH1F, {{500, -100, 400}}}); + registry.add("hJetEta", "jet #eta;#eta_{jet};entries", HistType::kTH1F, {axisEta}); + registry.add("hJetPhi", "jet #phi;#phi_{jet};entries", HistType::kTH1F, {axisPhi}); + registry.add("hJet3D", "3D jet distribution;p_{T};#eta;#phi", {HistType::kTH3F, {{500, -100, 400}, {100, -1.0, 1.0}, {100, 0.0, o2::constants::math::TwoPI}}}); + registry.add("h_Jet_D_Jet_dPhi", "#Delta #phi _{D, jet}", kTH1F, {{100, 0, o2::constants::math::TwoPI}}); + registry.add("h_Jet_pT_D_Jet_dPhi", "p_{T, jet} vs #Delta #phi _{D, jet}", kTH2F, {{100, 0, 100}, {100, 0, o2::constants::math::TwoPI}}); + + // Matching histograms + registry.add("hPtMatched", "p_{T} matching;p_{T,det};p_{T,part}", {HistType::kTH2F, {{500, -100, 400}, {400, 0, 400}}}); + registry.add("hPtMatched1d", "p_{T} matching 1d;p_{T,part}", {HistType::kTH1F, {{400, 0, 400}}}); + registry.add("hPhiMatched", "#phi matching;#phi_{det};#phi_{part}", {HistType::kTH2F, {{100, 0.0, o2::constants::math::TwoPI}, {100, 0.0, o2::constants::math::TwoPI}}}); + registry.add("hEtaMatched", "#eta matching;#eta_{det};#eta_{part}", {HistType::kTH2F, {{100, -1, 1}, {100, -1, 1}}}); + registry.add("hPtResolution", "p_{T} resolution;p_{T,part};Relative Resolution", {HistType::kTH2F, {{400, 0, 400}, {1000, -5.0, 5.0}}}); + registry.add("hPhiResolution", "#phi resolution;#p_{T,part};Resolution", {HistType::kTH2F, {{400, 0, 400}, {1000, -7.0, 7.0}}}); + registry.add("hEtaResolution", "#eta resolution;#p_{T,part};Resolution", {HistType::kTH2F, {{400, 0, 400}, {1000, -1.0, 1.0}}}); + } + + // numeric values per species (see DCategory above) so a bare category + // value is unambiguous even though it's stored in one shared column. + + template + int classifyDCandidate(int8_t dDecayChannel, int matchedFrom, int selectedAs) + { + int category = DCategory::Undefined; + if constexpr (std::is_same_v) { + if ((std::abs(dDecayChannel) == hf_2prong::DecayChannelMain::D0ToPiK) && (matchedFrom != 0) && (selectedAs == matchedFrom)) { + category = DCategory::Signal; + } else if ((std::abs(dDecayChannel) == hf_2prong::DecayChannelMain::D0ToPiK) && (matchedFrom != 0) && (selectedAs == -1 * matchedFrom)) { + category = DCategory::Reflection; + } else if (std::abs(dDecayChannel) == hf_2prong::DecayChannelMain::D0ToPiKPi0) { + category = DCategory::CorrBkg1; + } else if (std::abs(dDecayChannel) == hf_2prong::DecayChannelMain::D0ToPiPi) { + category = DCategory::CorrBkg2; + } else if (std::abs(dDecayChannel) == hf_2prong::DecayChannelMain::D0ToPiPiPi0) { + category = DCategory::CorrBkg3; + } else if (std::abs(dDecayChannel) == hf_2prong::DecayChannelMain::D0ToKK) { + category = DCategory::CorrBkg4; + } + } else if constexpr (std::is_same_v) { + if ((std::abs(dDecayChannel) == hf_dstar::DecayChannelMain::DstarToPiKPi) && (matchedFrom != 0) && (selectedAs == matchedFrom)) { + category = DCategory::DstarSignal; + } else if ((std::abs(dDecayChannel) == hf_dstar::DecayChannelMain::DstarToPiKPi) && (matchedFrom != 0) && (selectedAs == -1 * matchedFrom)) { + category = DCategory::DstarReflection; + } else if (std::abs(dDecayChannel) == hf_dstar::DecayChannelMain::DstarToPiKPiPi0) { + category = DCategory::DstarCorrBkg; + } + } + return category; + } + + template + void doAnalysisData(T const& collision, U const& dCandidates, V const& jets) + { + if (!applyCollisionSelections(collision)) { + return; + } + tableCollision(collision.posZ()); + for (const auto& dCandidate : dCandidates) { + if (dCandidate.pt() < dPtCutMin) { // once settled on a mlcut, then add the lower bound of the systematics as a cut here + continue; + } + const auto scores = dCandidate.mlScores(); + fillDHistograms(dCandidate, scores); + + float deltaM = -1.0; // sentinel: not applicable for D0 + if constexpr (std::is_same_v) { + deltaM = dCandidate.m() - dCandidate.invMassCharm(); + } + + tableD(tableCollision.lastIndex(), + scores[2], + scores[1], + scores[0], + dCandidate.m(), + deltaM, + dCandidate.pt(), + dCandidate.eta(), + dCandidate.phi(), + dCandidate.y()); + + for (const auto& jet : jets) { + if (jet.pt() < jetPtCutMin) { + continue; + } + float dPhi = RecoDecay::constrainAngle(jet.phi() - dCandidate.phi(), -o2::constants::math::PI); + if (std::abs(dPhi - o2::constants::math::PI) > o2::constants::math::PIHalf) { + continue; + } + fillJetHistograms(jet, dPhi); + tableJet(tableCollision.lastIndex(), + tableD.lastIndex(), + jet.pt(), + jet.eta(), + jet.phi(), + dPhi); + } + } + } + + void processD0Data(soa::Filtered::iterator const& collision, + aod::CandidatesD0Data const& d0Candidates, + soa::Join const& jets) + { + doAnalysisData(collision, d0Candidates, jets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processD0Data, "charged particle level jet analysis for D0", true); + + void processDstarData(soa::Filtered::iterator const& collision, + aod::CandidatesDstarData const& dstarCandidates, + soa::Join const& jets) + { + doAnalysisData(collision, dstarCandidates, jets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processDstarData, "charged particle level jet analysis for D*", true); + + template + void doAnalysisMcDetector(T const& collision, U const& dCandidates, V const& jets) + { + if (!applyCollisionSelections(collision)) { + return; + } + tableCollision(collision.posZ()); + for (const auto& dCandidate : dCandidates) { + if (dCandidate.pt() < dPtCutMin) { + continue; + } + const auto scores = dCandidate.mlScores(); + fillDHistograms(dCandidate, scores); + + int8_t dDecayChannel = dCandidate.flagMcMatchRec(); + int matchedFrom = 0; + int selectedAs = 0; + if (dDecayChannel > 0) { + matchedFrom = 1; + } else if (dDecayChannel < 0) { + matchedFrom = -1; + } + if ((dCandidate.candidateSelFlag() & BIT(0)) != 0) { + selectedAs = 1; + } else if ((dCandidate.candidateSelFlag() & BIT(1)) != 0) { + selectedAs = -1; + } + int category = classifyDCandidate(dDecayChannel, matchedFrom, selectedAs); + + float deltaM = -1.0; // sentinel: not applicable for D0 + if constexpr (std::is_same_v) { + deltaM = dCandidate.m() - dCandidate.invMassCharm(); + } + + tableDMcDetector(tableCollision.lastIndex(), + scores[2], + scores[1], + scores[0], + dCandidate.m(), + deltaM, + dCandidate.pt(), + dCandidate.eta(), + dCandidate.phi(), + dCandidate.y(), + category); + for (const auto& jet : jets) { + if (jet.pt() < jetPtCutMin) { + continue; + } + float dPhi = RecoDecay::constrainAngle(jet.phi() - dCandidate.phi(), -o2::constants::math::PI); + if (std::abs(dPhi - o2::constants::math::PI) > o2::constants::math::PIHalf) { + continue; + } + fillJetHistograms(jet, dPhi); + tableJetMcDetector(tableCollision.lastIndex(), + tableDMcDetector.lastIndex(), + jet.pt(), + jet.eta(), + jet.phi(), + dPhi); + } + } + } + + void processD0McDetector(soa::Filtered::iterator const& collision, + aod::CandidatesD0MCD const& d0Candidates, + soa::Join const& jets) + { + doAnalysisMcDetector(collision, d0Candidates, jets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processD0McDetector, "charged detector level jet analysis for D0", false); + + void processDstarMcDetector(soa::Filtered::iterator const& collision, + aod::CandidatesDstarMCD const& dstarCandidates, + soa::Join const& jets) + { + doAnalysisMcDetector(collision, dstarCandidates, jets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processDstarMcDetector, "charged detector level jet analysis for D*", false); + + // Shared MC-particle logic. + template + void doAnalysisMcParticle(T const& collision, U const& dMcPCandidates, V const& jets) + { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + return; + } + tableMcCollision(collision.posZ()); + for (const auto& dMcPCandidate : dMcPCandidates) { + if (dMcPCandidate.pt() < dMcPtCutMin) { + continue; + } + tableDMcParticle(tableMcCollision.lastIndex(), + dMcPCandidate.originMcGen(), + dMcPCandidate.pt(), + dMcPCandidate.eta(), + dMcPCandidate.phi(), + dMcPCandidate.y(), + dMcPCandidate.flagMcMatchGen()); + + for (const auto& jet : jets) { + if (jet.pt() < jetMcPtCutMin) { + continue; + } + float dPhi = RecoDecay::constrainAngle(jet.phi() - dMcPCandidate.phi(), -o2::constants::math::PI); + if (std::abs(dPhi - o2::constants::math::PI) > o2::constants::math::PIHalf) { + continue; + } + fillJetHistograms(jet, dPhi); + tableJetMcParticle(tableMcCollision.lastIndex(), + tableDMcParticle.lastIndex(), + jet.pt(), + jet.eta(), + jet.phi(), + dPhi); + } + } + } + + void processD0McParticle(aod::JetMcCollision const& collision, + aod::CandidatesD0MCP const& d0McPCandidates, + soa::Join const& jets) + { + doAnalysisMcParticle(collision, d0McPCandidates, jets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processD0McParticle, "charged MC Particle jets for D0", false); + + void processDstarMcParticle(aod::JetMcCollision const& collision, + aod::CandidatesDstarMCP const& dstarMcPCandidates, + soa::Join const& jets) + { + doAnalysisMcParticle(collision, dstarMcPCandidates, jets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processDstarMcParticle, "charged MC Particle jets for D*", false); + + template + void doAnalysisMcMatched(T const& collision, U const& dCandidates, V const& tracks, + W const& particles, X const& McDJets, Y const&) + { + if (!applyCollisionSelections(collision)) { + return; + } + tableMatchedCollision(collision.posZ()); + for (const auto& dCandidate : dCandidates) { + if (dCandidate.pt() < dPtCutMin) { + continue; + } + const auto& dParticle = jethfutilities::matchedHFParticle(dCandidate, tracks, particles); + for (const auto& McDJet : McDJets) { + if (McDJet.pt() < jetPtCutMin) { + continue; + } + float dPhiD = RecoDecay::constrainAngle(McDJet.phi() - dCandidate.phi(), -o2::constants::math::PI); + if (std::abs(dPhiD - o2::constants::math::PI) > o2::constants::math::PIHalf) { + continue; + } + if (McDJet.has_matchedJetGeo()) { + for (auto const& McPJet : McDJet.template matchedJetGeo_as()) { + float dPhiP = RecoDecay::constrainAngle(McPJet.phi() - dParticle.phi(), -o2::constants::math::PI); + tableJetMatched(tableMatchedCollision.lastIndex(), + McDJet.pt(), + McDJet.eta(), + McDJet.phi(), + McPJet.pt(), + McPJet.eta(), + McPJet.phi(), + dPhiD, + dPhiP); + } + } + } + } + } + + void processD0McMatched(soa::Filtered::iterator const& collision, + aod::CandidatesD0MCD const& d0Candidates, + aod::JetTracksMCD const& tracks, + aod::JetParticles const& particles, + soa::Join const& McDJets, + aod::ChargedMCParticleLevelJets const& McPJets) + { + doAnalysisMcMatched(collision, d0Candidates, tracks, particles, McDJets, McPJets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processD0McMatched, "process matching of particle level jets to detector level jets for D0", false); + + void processDstarMcMatched(soa::Filtered::iterator const& collision, + aod::CandidatesDstarMCD const& dstarCandidates, + aod::JetTracksMCD const& tracks, + aod::JetParticles const& particles, + soa::Join const& McDJets, + aod::ChargedMCParticleLevelJets const& McPJets) + { + doAnalysisMcMatched(collision, dstarCandidates, tracks, particles, McDJets, McPJets); + } + PROCESS_SWITCH(JetCorrelationDmeson, processDstarMcMatched, "process matching of particle level jets to detector level jets for D*", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx index 240fb3e7a20..35e3693f59b 100644 --- a/PWGJE/Tasks/jetCrossSectionEfficiency.cxx +++ b/PWGJE/Tasks/jetCrossSectionEfficiency.cxx @@ -9,31 +9,54 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// Step-by-step event selection efficiency for MC particle-level jets +// Step-by-step event selection efficiency for MC particle-level jets. +// Two ordering variants exposed as separate process functions: +// - CollRecoFirst: reco collision required first, BC bits read from the reco-coll EvSel bitmask. +// - BcBitsFirst: BC bits read from the MC truth BC, then truth-side selections, then reco at the end. // /// \author Joonsuk Bae +/// \author Wooseok Ham #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" #include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include #include +#include #include +#include +#include #include #include +#include #include #include #include #include +#include #include #include +#include #include +#include +#include #include #include -#include +#include +#include +#include #include +#include +#include #include #include @@ -45,8 +68,8 @@ struct JetCrossSectionEfficiency { HistogramRegistry registry; - Configurable eventSelections{"eventSelections", "selTVX", "choose event selection (e.g., sel8, selMC, sel8Full, selTVX, etc.)"}; - Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events"}; + Configurable eventSelections{"eventSelections", "sel8", "selTVX | selMC | selMCFull | sel8 | sel8Full | sel8FullPbPb"}; + Configurable skipMBGapEvents{"skipMBGapEvents", false, "reject min-bias gap events from hybrid MB+JJ MC productions"}; Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; Configurable centralityMin{"centralityMin", -999.0f, "minimum centrality"}; Configurable centralityMax{"centralityMax", 999.0f, "maximum centrality"}; @@ -63,12 +86,48 @@ struct JetCrossSectionEfficiency { Configurable leadingConstituentPtMinMCP{"leadingConstituentPtMinMCP", -99.0f, "minimum pT selection on MCP jet constituent"}; Configurable leadingConstituentPtMaxMCP{"leadingConstituentPtMaxMCP", 9999.0f, "maximum pT selection on MCP jet constituent"}; - Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum track occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions"}; - Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum track occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions"}; + Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum track occupancy of tracks in neighbouring collisions in a given time range"}; + Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum track occupancy of tracks in neighbouring collisions in a given time range"}; Configurable acceptSplitCollisions{"acceptSplitCollisions", 0, "0: only look at mcCollisions that are not split; 1: accept split mcCollisions, 2: accept split mcCollisions but only look at the first reco collision associated with it"}; - Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0f, "maximum fraction of hard scattering for jet acceptance in particle MC"}; - Configurable pTHatExponent{"pTHatExponent", 6.0f, "exponent of the event weight for the calculation of pTHat"}; - Configurable pTHatAbsoluteMin{"pTHatAbsoluteMin", -99.0f, "minimum value of pTHat"}; + Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0f, "maximum jet pT in units of pTHat for outlier rejection at MC particle level"}; + Configurable pTHatAbsoluteMin{"pTHatAbsoluteMin", -99.0f, "minimum pTHat (drops events with pTHat below this)"}; + Configurable pTHatExponent{"pTHatExponent", 6.0f, "exponent in the back-calculation of pTHat from the event weight"}; + Configurable simPtRef{"simPtRef", 10.0f, "reference pT for the back-calculation of pTHat from the event weight"}; + Configurable ptHardCalcMethodSwitch{"ptHardCalcMethodSwitch", 999.0f, "sentinel and threshold for the stored-ptHard branch (set <=0 to force weight-derived)"}; + Configurable applyRCT{"applyRCT", true, "apply RCT_pass step in the cascade (false: force the RCT step to pass)"}; + Configurable rctSelectionsLabel{"rctSelectionsLabel", "CBT_hadronPID", "RCT selection preset name (see RCTSelectionFlags)"}; + + // The reconstructed occupancy bits cannot be evaluated before hasColl. + // These configurables define the truth-only approximation used by sel8FullPbPb. + Configurable truthRofCcdbUrl{"truthRofCcdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL used to retrieve ITS ROF parameters for sel8FullPbPb truth selections"}; + Configurable truthRofOffsetInBC{"truthRofOffsetInBC", -1, "ITS ROF bias in BC for sel8FullPbPb truth selections; -1 retrieves DPLAlpideParam from CCDB"}; + Configurable truthRofLengthInBC{"truthRofLengthInBC", -1, "ITS ROF length in BC for sel8FullPbPb truth selections; -1 retrieves DPLAlpideParam from CCDB"}; + Configurable truthTimeRangeStandardMinUs{"truthTimeRangeStandardMinUs", -4.0f, "truth lower time range in us; follows kNoCollInTimeRangeStandard"}; + Configurable truthTimeRangeStandardMaxUs{"truthTimeRangeStandardMaxUs", 2.0f, "truth upper time range in us; follows kNoCollInTimeRangeStandard"}; + Configurable truthTimeRangeNarrowUs{"truthTimeRangeNarrowUs", 0.25f, "truth narrow time range in us; follows kNoCollInTimeRangeStandard"}; + Configurable truthFT0CActivityMinForTrackProxy{"truthFT0CActivityMinForTrackProxy", 0.0f, "minimum multFT0C truth activity used as an ITS-track-presence proxy"}; + Configurable truthFT0CActivityThresholdTimeRange{"truthFT0CActivityThresholdTimeRange", 8000.0f, "multFT0C truth proxy threshold for NoCollInTimeRangeStandard; reco uses FT0C amplitude, so validate or tune this value"}; + Configurable truthFT0CActivityThresholdROF{"truthFT0CActivityThresholdROF", 5000.0f, "multFT0C truth proxy threshold for NoCollInRofStandard; reco uses FT0C amplitude, so validate or tune this value"}; + Configurable truthRofCloseVzMax{"truthRofCloseVzMax", 0.3f, "maximum truth |delta z| in cm for the NoCollInRofStandard track-presence proxy"}; + Configurable tvxEnvironmentRequireOtherBcBaseline{"tvxEnvironmentRequireOtherBcBaseline", false, "require RCT, TVX, NoTFB and NoITSROFB for other BCs in the TVX-BC environment QA"}; + + o2::aod::rctsel::RCTFlagsChecker rctChecker; + uint64_t rctMask = 0; + Service ccdb{}; + + int cachedTruthRofRun = std::numeric_limits::min(); + int64_t cachedTruthRofOffsetInBC = -1; + int64_t cachedTruthRofLengthInBC = -1; + + bool applyTFB = true; + bool applyROFB = true; + bool applySBP = true; + bool applyNoCollInTimeRangeStandard = false; + bool applyNoCollInRofStandard = false; + bool isSel8FullPbPb = false; + + std::vector collRecoFirstLabels; + std::vector bcBitsFirstLabels; enum AcceptSplitCollisionsOptions { NonSplitOnly = 0, @@ -76,173 +135,787 @@ struct JetCrossSectionEfficiency { SplitOkCheckFirstAssocCollOnly // 2 }; - static constexpr float configSwitchLow = -98.0f; - static constexpr float configSwitchHigh = 9998.0f; + enum BinPbPbTruthSelectionOnly { + PbPbTruthSelectionOnlyInel = 1, + PbPbTruthSelectionOnlyRct = 2, + PbPbTruthSelectionOnlyTvx = 3, + PbPbTruthSelectionOnlyNoTimeFrameBorder = 4, + PbPbTruthSelectionOnlyNoItsRofBorder = 5, + PbPbTruthSelectionOnlySelection = 6, + PbPbTruthSelectionOnlyHasCollision = 7, + PbPbTruthSelectionOnlyVertexZ = 8, + PbPbTruthSelectionOnlyNoSplit = 9, + PbPbTruthSelectionOnlyNBins = 9 + }; + + static constexpr float ConfigSwitchLow = -98.0f; + static constexpr float ConfigSwitchHigh = 9998.0f; + static constexpr float BrokenPtHardSentinel = 1.0f; + static constexpr int MinITSClustersForOccupancy = 5; + static constexpr int NumberOfHasCollSelectionConditionDiagnostics = 5; + static constexpr int HasCollQaMaxCount = 500; + static constexpr int HasCollQaMaxDeltaGlobalBC = 100000; + static constexpr int64_t NoOtherTruthCollision = -1; + + static std::vector getNearestAbsDeltaGlobalBCEdges() + { + // Keep the no-other-BC sentinel separate, then resolve the near-BC region most relevant for reconstruction losses. + return {-1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 7.5, 15.5, 31.5, 63.5, 127.5, 255.5, 511.5, 1000.5, 2000.5, 4000.5, 8000.5, 16000.5, 32000.5, 64000.5, static_cast(HasCollQaMaxDeltaGlobalBC) + 0.5}; + } + + struct TruthPbPbSelections { + bool valid = false; + bool noCollInTimeRangeStandard = false; + bool noCollInRofStandard = false; + }; + + struct TruthHasCollEnvironment { + bool valid = false; + int nOtherSameTruthRof = 0; + int64_t nearestAbsDeltaGlobalBC = NoOtherTruthCollision; + }; - std::vector eventSelectionBits; + struct VisibleBCEnvironment { + bool valid = false; + int nOtherTVXSameRof = 0; + int64_t nearestAbsDeltaTVXGlobalBC = NoOtherTruthCollision; + int64_t originalBCIndex = -1; + }; - struct StageInfo { - int bit; - int bin; + struct VisibleBCEnvironmentCache { + std::map byBCIndex; + std::map, VisibleBCEnvironment> byRunAndGlobalBC; }; - std::vector activeStages; - int binInel = 1; - int binNoReco = 2; - int binSplit = 3; - int binZvtx = -1; - int binCentrality = -1; + using JetCollisionsMCDWithParent = soa::Join; + using JMcCollisionsWithParent = soa::Join; + using JetCollisionsWithParent = soa::Join; + using CollisionsWithEvSels = soa::Join; + using OriginalBCsWithSels = soa::Join; + using OriginalCollisionsWithMcLabels = soa::Join; + using FullTracksIU = soa::Join; + using FullTracksIUWithMcLabels = soa::Join; + using ChargedMCDJetsWithConstituents = soa::Join; + using ChargedMCPJetsWithConstituents = soa::Join; + + Partition pvTracks = ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); + Preslice pvTracksPerCollision = aod::track::collisionId; + PresliceUnsorted collisionsPerFoundBC = aod::evsel::foundBCId; + PresliceUnsorted jetCollisionsPerMcCollision = aod::jmccollisionlb::mcCollisionId; + Preslice mcpJetsPerMcCollision = aod::jet::mcCollisionId; + Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; + PresliceUnsorted recoTracksPerMcParticle = aod::mctracklabel::mcParticleId; + PresliceUnsorted jetCollisionsPerOriginalCollision = aod::jcollision::collisionId; + Preslice detectorJetsPerJetCollision = aod::jet::collisionId; + + enum EventSelectionPreset { + PresetSelTvx = 0, + PresetSelMc, + PresetSelMcFull, + PresetSel8, + PresetSel8Full, + PresetSel8FullPbPb, + PresetInvalid + }; - const char* getStageLabel(int bit) + static EventSelectionPreset getEventSelectionPreset(const std::string& preset) { - switch (bit) { - case jetderiveddatautilities::JCollisionSel::selTVX: - return "kTVX"; - case jetderiveddatautilities::JCollisionSel::selNoTimeFrameBorder: - return "kTFBorder"; - case jetderiveddatautilities::JCollisionSel::selNoITSROFrameBorder: - return "kITSROFBorder"; - case jetderiveddatautilities::JCollisionSel::selNoSameBunchPileup: - return "NoSameBunchPileup"; - case jetderiveddatautilities::JCollisionSel::selIsGoodZvtxFT0vsPV: - return "IsGoodZvtxFT0vsPV"; - case jetderiveddatautilities::JCollisionSel::selNoCollInTimeRangeStandard: - return "NoCollInTimeRangeStd"; - case jetderiveddatautilities::JCollisionSel::selNoCollInRofStandard: - return "NoCollInRofStd"; - default: - return nullptr; + if (preset == "selTVX") { + return PresetSelTvx; + } + if (preset == "selMC") { + return PresetSelMc; + } + if (preset == "selMCFull") { + return PresetSelMcFull; } + if (preset == "sel8") { + return PresetSel8; + } + if (preset == "sel8Full") { + return PresetSel8Full; + } + if (preset == "sel8FullPbPb") { + return PresetSel8FullPbPb; + } + return PresetInvalid; } + Preslice mcCollsPerBC = aod::jmccollision::bcId; + void init(InitContext&) { - if (!(acceptSplitCollisions == NonSplitOnly || acceptSplitCollisions == SplitOkCheckAnyAssocColl || acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly)) { + if (acceptSplitCollisions != NonSplitOnly && + acceptSplitCollisions != SplitOkCheckAnyAssocColl && + acceptSplitCollisions != SplitOkCheckFirstAssocCollOnly) { LOGF(fatal, "Configurable acceptSplitCollisions has wrong input value; stopping workflow"); } - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + rctChecker.init(static_cast(rctSelectionsLabel)); + rctMask = rctChecker.value(); + + switch (getEventSelectionPreset(eventSelections.value)) { + case PresetSelTvx: + applyTFB = false; + applyROFB = false; + applySBP = false; + break; + case PresetSelMc: + applyTFB = true; + applyROFB = false; + applySBP = false; + break; + case PresetSelMcFull: + applyTFB = true; + applyROFB = false; + applySBP = true; + break; + case PresetSel8: + applyTFB = true; + applyROFB = true; + applySBP = false; + break; + case PresetSel8Full: + applyTFB = true; + applyROFB = true; + applySBP = true; + break; + case PresetSel8FullPbPb: + applyTFB = true; + applyROFB = true; + applySBP = false; + applyNoCollInTimeRangeStandard = true; + applyNoCollInRofStandard = true; + isSel8FullPbPb = true; + break; + default: + LOGF(fatal, "Configurable eventSelections=%s not supported; use selTVX, selMC, selMCFull, sel8, sel8Full, or sel8FullPbPb", eventSelections.value.c_str()); + break; + } - auto hasBit = [&](int bit) { - return std::find(eventSelectionBits.begin(), eventSelectionBits.end(), bit) != eventSelectionBits.end(); - }; + const bool isDataTVXBCEnvironmentQAEnabled = doprocessDataTVXBCEnvironmentQA; + const bool isMCQAEnabled = doprocessMCTruthMultFT0CvsRecoAmplitude || + doprocessMCTruthMultFT0CvsRecoITS567 || + doprocessMCEnvironmentQA || + doprocessMCHasCollTrackFateQA || + doprocessMCHasCollJetSurvivalQA || + doprocessMCHasCollJetEnvironmentQA || + doprocessCrossSectionEfficiency || + doprocessCrossSectionEfficiencyBcBitsFirst; + if (isDataTVXBCEnvironmentQAEnabled && isMCQAEnabled) { + LOGF(fatal, "processDataTVXBCEnvironmentQA cannot run with MC-only QA or cross-section-efficiency processes; enable the data process alone for data AO2Ds"); + } + + const bool needsRofParameters = (doprocessCrossSectionEfficiencyBcBitsFirst && isSel8FullPbPb) || + doprocessMCEnvironmentQA || + doprocessMCHasCollJetEnvironmentQA || + doprocessDataTVXBCEnvironmentQA; + if (needsRofParameters && + (truthRofOffsetInBC < 0 || truthRofLengthInBC <= 0)) { + ccdb->setURL(static_cast(truthRofCcdbUrl)); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } - activeStages.clear(); - int nextEventSelectionBin = 4; - std::set addedBits; + // Preserve the upstream binning and order for existing presets. + // sel8FullPbPb replaces the SBP stage with the two PbPb occupancy selections. + collRecoFirstLabels = {"INEL", "+RCT_pass", "+hasRecoColl", "+|zReco|<10", "+noSplit", "+kTVX", "+kNoTFB", "+kNoITSROFB"}; + bcBitsFirstLabels = {"INEL", "+RCT_pass", "+kTVX(truth)", "+kNoTFB(truth)", "+kNoITSROFB(truth)"}; + if (isSel8FullPbPb) { + collRecoFirstLabels.emplace_back("+kNoCollInTimeRangeStandard"); + collRecoFirstLabels.emplace_back("+kNoCollInRofStandard"); + bcBitsFirstLabels.emplace_back("+kNoCollInTimeRangeStandard(truth)"); + bcBitsFirstLabels.emplace_back("+kNoCollInRofStandard(truth)"); + bcBitsFirstLabels.emplace_back("+hasColl"); + bcBitsFirstLabels.emplace_back("+|zReco|<10"); + bcBitsFirstLabels.emplace_back("+noSplit"); + } else { + collRecoFirstLabels.emplace_back("+kNoSBP"); + bcBitsFirstLabels.emplace_back("+kNoSBP(truth)"); + bcBitsFirstLabels.emplace_back("+hasColl"); + bcBitsFirstLabels.emplace_back("+|zReco|<10"); + bcBitsFirstLabels.emplace_back("+noSplit"); + } - auto addStageIfNotAlreadyAdded = [&](int bit) { - if (addedBits.find(bit) != addedBits.end()) { - return; + auto setAxisLabels = [](TAxis* axis, const std::vector& labels) { + for (size_t i = 0; i < labels.size(); ++i) { + axis->SetBinLabel(static_cast(i + 1), labels[i].c_str()); } - addedBits.insert(bit); - activeStages.push_back({bit, nextEventSelectionBin++}); }; - // sel8, sel7, selKINT7 are composite selections that require multiple sub-bits. - // initialiseEventSelectionBits returns them as single bits, but for step-by-step QA - // we need to expand them into their constituent bits (kTVX, kTFBorder, kITSROFBorder). - // Note: selMC, selMCFull, etc. are already expanded by initialiseEventSelectionBits. - std::vector>> compositeSelections = { - {jetderiveddatautilities::JCollisionSel::sel8, {jetderiveddatautilities::JCollisionSel::selTVX, jetderiveddatautilities::JCollisionSel::selNoTimeFrameBorder, jetderiveddatautilities::JCollisionSel::selNoITSROFrameBorder}}, - {jetderiveddatautilities::JCollisionSel::sel7, {jetderiveddatautilities::JCollisionSel::selTVX, jetderiveddatautilities::JCollisionSel::selNoTimeFrameBorder, jetderiveddatautilities::JCollisionSel::selNoITSROFrameBorder}}, - {jetderiveddatautilities::JCollisionSel::selKINT7, {jetderiveddatautilities::JCollisionSel::selTVX, jetderiveddatautilities::JCollisionSel::selNoTimeFrameBorder, jetderiveddatautilities::JCollisionSel::selNoITSROFrameBorder}}}; - - for (auto const& [compositeBit, subBits] : compositeSelections) { - if (hasBit(compositeBit)) { - for (auto subBit : subBits) { - addStageIfNotAlreadyAdded(subBit); + AxisSpec jetPtAxis = {200, 0., jetPtMax, "#it{p}_{T} (GeV/#it{c})"}; + + if (doprocessCrossSectionEfficiency) { + AxisSpec axisSelectionCollRecoFirst = {static_cast(collRecoFirstLabels.size()), 0.5, static_cast(collRecoFirstLabels.size()) + 0.5, "event selection (CollRecoFirst)"}; + registry.add("h2_jet_pt_part_eventselection_collRecoFirst", + "part jet pT vs event selection (CollRecoFirst);#it{p}_{T,jet}^{part} (GeV/#it{c});event selection;counts", + {HistType::kTH2F, {jetPtAxis, axisSelectionCollRecoFirst}}); + setAxisLabels(registry.get(HIST("h2_jet_pt_part_eventselection_collRecoFirst"))->GetYaxis(), collRecoFirstLabels); + + registry.add("h_mccollisions_eventselection_collRecoFirst", + "number of mc events vs event selection (CollRecoFirst);event selection;entries", + {HistType::kTH1F, {axisSelectionCollRecoFirst}}); + setAxisLabels(registry.get(HIST("h_mccollisions_eventselection_collRecoFirst"))->GetXaxis(), collRecoFirstLabels); + } + + if (doprocessCrossSectionEfficiencyBcBitsFirst) { + AxisSpec axisSelectionBcBitsFirst = {static_cast(bcBitsFirstLabels.size()), 0.5, static_cast(bcBitsFirstLabels.size()) + 0.5, "event selection (BcBitsFirst)"}; + registry.add("h2_jet_pt_part_eventselection_bcBitsFirst", + "part jet pT vs event selection (BcBitsFirst);#it{p}_{T,jet}^{part} (GeV/#it{c});event selection;counts", + {HistType::kTH2F, {jetPtAxis, axisSelectionBcBitsFirst}}); + setAxisLabels(registry.get(HIST("h2_jet_pt_part_eventselection_bcBitsFirst"))->GetYaxis(), bcBitsFirstLabels); + + registry.add("h_mccollisions_eventselection_bcBitsFirst", + "number of mc events vs event selection (BcBitsFirst);event selection;entries", + {HistType::kTH1F, {axisSelectionBcBitsFirst}}); + setAxisLabels(registry.get(HIST("h_mccollisions_eventselection_bcBitsFirst"))->GetXaxis(), bcBitsFirstLabels); + + if (isSel8FullPbPb) { + AxisSpec axisPbPbTruthSelectionOnly = {PbPbTruthSelectionOnlyNBins, 0.5, static_cast(PbPbTruthSelectionOnlyNBins) + 0.5, "event selection (PbPb truth-only)"}; + AxisSpec selectionConditionAxis = {NumberOfHasCollSelectionConditionDiagnostics, 0.5, static_cast(NumberOfHasCollSelectionConditionDiagnostics) + 0.5, "selection condition"}; + AxisSpec hasCollSelectionStateAxis = {4, 0.5, 4.5, "hasColl and selection-condition state"}; + std::vector timeRangeOnlyLabels = {"INEL", "+RCT_pass", "+kTVX(truth)", "+kNoTFB(truth)", "+kNoITSROFB(truth)", "+kNoCollInTimeRangeStandard(truth)", "+hasColl", "+|zReco|<10", "+noSplit"}; + std::vector rofOnlyLabels = {"INEL", "+RCT_pass", "+kTVX(truth)", "+kNoTFB(truth)", "+kNoITSROFB(truth)", "+kNoCollInRofStandard(truth)", "+hasColl", "+|zReco|<10", "+noSplit"}; + + registry.add("h2_jet_pt_part_eventselection_bcBitsFirst_timeRangeOnly_truth", + "part jet pT vs TimeRange-only truth selection;#it{p}_{T,jet}^{part} (GeV/#it{c});event selection;weighted counts", + {HistType::kTH2F, {jetPtAxis, axisPbPbTruthSelectionOnly}}); + registry.add("h_mccollisions_eventselection_bcBitsFirst_timeRangeOnly_truth", + "weighted MC events vs TimeRange-only truth selection;event selection;weighted events", + {HistType::kTH1F, {axisPbPbTruthSelectionOnly}}); + setAxisLabels(registry.get(HIST("h2_jet_pt_part_eventselection_bcBitsFirst_timeRangeOnly_truth"))->GetYaxis(), timeRangeOnlyLabels); + setAxisLabels(registry.get(HIST("h_mccollisions_eventselection_bcBitsFirst_timeRangeOnly_truth"))->GetXaxis(), timeRangeOnlyLabels); + + registry.add("h2_jet_pt_part_eventselection_bcBitsFirst_rofOnly_truth", + "part jet pT vs ROF-only truth selection;#it{p}_{T,jet}^{part} (GeV/#it{c});event selection;weighted counts", + {HistType::kTH2F, {jetPtAxis, axisPbPbTruthSelectionOnly}}); + registry.add("h_mccollisions_eventselection_bcBitsFirst_rofOnly_truth", + "weighted MC events vs ROF-only truth selection;event selection;weighted events", + {HistType::kTH1F, {axisPbPbTruthSelectionOnly}}); + setAxisLabels(registry.get(HIST("h2_jet_pt_part_eventselection_bcBitsFirst_rofOnly_truth"))->GetYaxis(), rofOnlyLabels); + setAxisLabels(registry.get(HIST("h_mccollisions_eventselection_bcBitsFirst_rofOnly_truth"))->GetXaxis(), rofOnlyLabels); + + registry.add("h2_mccollisions_selection_bit_vs_has_coll_state", + "weighted MC collisions after common RCT+TVX baseline with valid PbPb truth-proxy evaluation;selection condition;hasColl and selection-condition state", + {HistType::kTH2F, {selectionConditionAxis, hasCollSelectionStateAxis}}); + registry.add("h2_mcpjets_selection_bit_vs_has_coll_state", + "weighted selected MCP jets after common RCT+TVX baseline with valid PbPb truth-proxy evaluation;selection condition;hasColl and selection-condition state", + {HistType::kTH2F, {selectionConditionAxis, hasCollSelectionStateAxis}}); + std::vector selectionConditionLabels = {"kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup (truth proxy)", "kNoCollInTimeRangeStandard (truth proxy)", "kNoCollInRofStandard (truth proxy)"}; + std::vector hasCollSelectionStateLabels = {"noColl, fail", "noColl, pass", "hasColl, fail", "hasColl, pass"}; + setAxisLabels(registry.get(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"))->GetXaxis(), selectionConditionLabels); + setAxisLabels(registry.get(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"))->GetYaxis(), hasCollSelectionStateLabels); + setAxisLabels(registry.get(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"))->GetXaxis(), selectionConditionLabels); + setAxisLabels(registry.get(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"))->GetYaxis(), hasCollSelectionStateLabels); + } + } + + if (doprocessMCTruthMultFT0CvsRecoAmplitude) { + AxisSpec truthMultFT0CAxis = {500, -0.5, 499.5, "truth multFT0C"}; + AxisSpec recoFT0CAmplitudeAxis = {1000, 0., 20000., "A_{FT0C}^{reco}"}; + registry.add("h2_mccollision_mult_ft0c_found_ft0_sum_amp_c", + "truth multFT0C vs reconstructed A_{FT0C};truth multFT0C;A_{FT0C}^{reco};weighted counts", + {HistType::kTH2F, {truthMultFT0CAxis, recoFT0CAmplitudeAxis}}); + } + + if (doprocessMCTruthMultFT0CvsRecoITS567) { + AxisSpec truthMultFT0CAxis = {500, -0.5, 499.5, "truth multFT0C"}; + AxisSpec recoNITS567Axis = {1000, -0.5, 999.5, "N_{ITS567}^{reco}"}; + AxisSpec truthMultFT0CPositiveAxis = {2, -0.5, 1.5, "truth multFT0C > 0"}; + AxisSpec recoNITS567PositiveAxis = {2, -0.5, 1.5, "N_{ITS567}^{reco} > 0"}; + registry.add("h2_mccollision_mult_ft0c_reco_n_its567", + "truth multFT0C vs reconstructed N_{ITS567};truth multFT0C;N_{ITS567}^{reco};weighted counts", + {HistType::kTH2F, {truthMultFT0CAxis, recoNITS567Axis}}); + registry.add("h2_mccollision_mult_ft0c_positive_vs_reco_its567_positive", + "truth multFT0C activity vs reconstructed ITS567 activity;truth multFT0C > 0;N_{ITS567}^{reco} > 0;weighted counts", + {HistType::kTH2F, {truthMultFT0CPositiveAxis, recoNITS567PositiveAxis}}); + auto binaryActivity = registry.get(HIST("h2_mccollision_mult_ft0c_positive_vs_reco_its567_positive")); + binaryActivity->GetXaxis()->SetBinLabel(1, "multFT0C = 0"); + binaryActivity->GetXaxis()->SetBinLabel(2, "multFT0C > 0"); + binaryActivity->GetYaxis()->SetBinLabel(1, "N_{ITS567} = 0"); + binaryActivity->GetYaxis()->SetBinLabel(2, "N_{ITS567} > 0"); + } + + if (doprocessMCEnvironmentQA) { + AxisSpec nOtherSameTruthRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other same truth ROF}"}; + AxisSpec nearestAbsDeltaGlobalBCAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest |#Delta global BC|"}; + AxisSpec targetMultFT0CAxis = {500, -0.5, 499.5, "target truth multFT0C"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + + registry.add("h2_n_other_same_truth_rof_vs_has_coll", + "other MC collisions in the same truth ITS ROF vs hasColl;N_{other same truth ROF};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {nOtherSameTruthRofAxis, hasCollAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_vs_has_coll", + "nearest other MC collision |#Delta global BC| vs hasColl;nearest |#Delta global BC|;JetDerived hasColl;weighted counts", + {HistType::kTH2F, {nearestAbsDeltaGlobalBCAxis, hasCollAxis}}); + registry.add("h2_target_mult_ft0c_vs_has_coll", + "target truth multFT0C vs hasColl;target truth multFT0C;JetDerived hasColl;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, hasCollAxis}}); + registry.add("h2_target_mult_ft0c_vs_n_other_same_truth_rof_all", + "target truth multFT0C vs other MC collisions in the same truth ITS ROF;target truth multFT0C;N_{other same truth ROF};weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nOtherSameTruthRofAxis}}); + registry.add("h2_target_mult_ft0c_vs_n_other_same_truth_rof_has_coll", + "target truth multFT0C vs other MC collisions in the same truth ITS ROF for hasColl;target truth multFT0C;N_{other same truth ROF};weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nOtherSameTruthRofAxis}}); + registry.add("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all", + "target truth multFT0C vs nearest other MC collision distance;target truth multFT0C;nearest |#Delta global BC|;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nearestAbsDeltaGlobalBCAxis}}); + registry.add("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll", + "target truth multFT0C vs nearest other MC collision distance for hasColl;target truth multFT0C;nearest |#Delta global BC|;weighted counts", + {HistType::kTH2F, {targetMultFT0CAxis, nearestAbsDeltaGlobalBCAxis}}); + + auto nOtherSameTruthRof = registry.get(HIST("h2_n_other_same_truth_rof_vs_has_coll")); + nOtherSameTruthRof->GetYaxis()->SetBinLabel(1, "noColl"); + nOtherSameTruthRof->GetYaxis()->SetBinLabel(2, "hasColl"); + auto nearestAbsDeltaGlobalBC = registry.get(HIST("h2_nearest_abs_delta_global_bc_vs_has_coll")); + nearestAbsDeltaGlobalBC->GetXaxis()->SetBinLabel(1, "no other valid MC collision"); + nearestAbsDeltaGlobalBC->GetYaxis()->SetBinLabel(1, "noColl"); + nearestAbsDeltaGlobalBC->GetYaxis()->SetBinLabel(2, "hasColl"); + auto targetMultFT0C = registry.get(HIST("h2_target_mult_ft0c_vs_has_coll")); + targetMultFT0C->GetYaxis()->SetBinLabel(1, "noColl"); + targetMultFT0C->GetYaxis()->SetBinLabel(2, "hasColl"); + registry.get(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all"))->GetYaxis()->SetBinLabel(1, "no other valid MC collision"); + registry.get(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll"))->GetYaxis()->SetBinLabel(1, "no other valid MC collision"); + } + + if (doprocessDataTVXBCEnvironmentQA || doprocessMCEnvironmentQA) { + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaTVXGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest other TVX |#Delta global BC|"}; + AxisSpec hasRecoCollisionAtTargetBCAxis = {2, -0.5, 1.5, "reconstructed collision at target TVX BC"}; + AxisSpec targetFT0CAmplitudeAxis = {1000, 0., 20000., "A_{FT0C} at target TVX BC"}; + + registry.add("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx", + "TVX-BC environment;N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis}}); + registry.add("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco", + "TVX-BC environment for target BCs with a reconstructed collision;N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis}}); + registry.add("h2_n_other_tvx_same_rof_vs_has_reco_collision", + "TVX-BC environment vs reconstructed-collision proxy;N_{other TVX BCs in same ITS ROF};reconstructed collision at target TVX BC;BCs", + {HistType::kTH2F, {nOtherTVXSameRofAxis, hasRecoCollisionAtTargetBCAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision", + "TVX-BC environment vs reconstructed-collision proxy;nearest other TVX |#Delta global BC|;reconstructed collision at target TVX BC;BCs", + {HistType::kTH2F, {nearestAbsDeltaTVXGlobalBCEnvironmentAxis, hasRecoCollisionAtTargetBCAxis}}); + registry.add("h2_target_ft0c_amplitude_vs_n_other_tvx_same_rof", + "FT0C amplitude vs TVX-BC environment;A_{FT0C} at target TVX BC;N_{other TVX BCs in same ITS ROF};BCs", + {HistType::kTH2F, {targetFT0CAmplitudeAxis, nOtherTVXSameRofAxis}}); + + auto nOtherTVXSameRof = registry.get(HIST("h2_n_other_tvx_same_rof_vs_has_reco_collision")); + nOtherTVXSameRof->GetYaxis()->SetBinLabel(1, "no reco collision"); + nOtherTVXSameRof->GetYaxis()->SetBinLabel(2, "reco collision exists"); + auto nearestAbsDeltaTVXGlobalBC = registry.get(HIST("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision")); + nearestAbsDeltaTVXGlobalBC->GetXaxis()->SetBinLabel(1, "no other TVX BC in run"); + nearestAbsDeltaTVXGlobalBC->GetYaxis()->SetBinLabel(1, "no reco collision"); + nearestAbsDeltaTVXGlobalBC->GetYaxis()->SetBinLabel(2, "reco collision exists"); + registry.get(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx"))->GetYaxis()->SetBinLabel(1, "no other TVX BC in run"); + registry.get(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco"))->GetYaxis()->SetBinLabel(1, "no other TVX BC in run"); + } + + if (doprocessMCEnvironmentQA) { + AxisSpec nOtherSameTruthRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other MC collisions in same truth ITS ROF}"}; + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest |#Delta global BC|"}; + AxisSpec hasRecoCollisionAtTargetBCAxis = {2, -0.5, 1.5, "reconstructed collision at target TVX BC"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + + registry.add("h2_n_other_truth_same_rof_vs_n_other_tvx_same_rof", + "truth and TVX-BC same-ROF environment;N_{other MC collisions in same truth ITS ROF};N_{other TVX BCs in same ITS ROF};weighted MC collisions", + {HistType::kTH2F, {nOtherSameTruthRofAxis, nOtherTVXSameRofAxis}}); + registry.add("h2_nearest_abs_delta_global_bc_truth_vs_nearest_abs_delta_global_bc_tvx", + "truth and TVX-BC nearest collision environment;truth nearest |#Delta global BC|;TVX nearest |#Delta global BC|;weighted MC collisions", + {HistType::kTH2F, {nearestAbsDeltaGlobalBCEnvironmentAxis, nearestAbsDeltaGlobalBCEnvironmentAxis}}); + registry.add("h2_reco_collision_proxy_vs_has_coll", + "reconstructed-collision proxy vs JetDerived hasColl;reconstructed collision at target TVX BC;JetDerived hasColl;weighted MC collisions", + {HistType::kTH2F, {hasRecoCollisionAtTargetBCAxis, hasCollAxis}}); + auto recoCollisionProxy = registry.get(HIST("h2_reco_collision_proxy_vs_has_coll")); + recoCollisionProxy->GetXaxis()->SetBinLabel(1, "no reco collision"); + recoCollisionProxy->GetXaxis()->SetBinLabel(2, "reco collision exists"); + recoCollisionProxy->GetYaxis()->SetBinLabel(1, "noColl"); + recoCollisionProxy->GetYaxis()->SetBinLabel(2, "hasColl"); + } + + if (doprocessMCHasCollJetEnvironmentQA) { + AxisSpec nOtherTVXSameRofAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{other TVX BCs in same ITS ROF}"}; + AxisSpec nearestAbsDeltaTVXGlobalBCEnvironmentAxis = {getNearestAbsDeltaGlobalBCEdges(), "nearest other TVX |#Delta global BC|"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + registry.add("hs_mcp_jet_pt_tvx_environment_has_coll", + "selected MCP jets vs TVX-BC environment and hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});N_{other TVX BCs in same ITS ROF};nearest other TVX |#Delta global BC|;JetDerived hasColl;weighted jets", + {HistType::kTHnSparseF, {jetPtAxis, nOtherTVXSameRofAxis, nearestAbsDeltaTVXGlobalBCEnvironmentAxis, hasCollAxis}}, true); + auto jetEnvironment = registry.get(HIST("hs_mcp_jet_pt_tvx_environment_has_coll")); + jetEnvironment->GetAxis(3)->SetBinLabel(1, "noColl"); + jetEnvironment->GetAxis(3)->SetBinLabel(2, "hasColl"); + jetEnvironment->GetAxis(2)->SetBinLabel(1, "no other TVX BC in run"); + } + + if (doprocessMCHasCollTrackFateQA) { + AxisSpec trackFateCountAxis = {HasCollQaMaxCount + 1, -0.5, static_cast(HasCollQaMaxCount) + 0.5, "N_{target-origin reconstructed tracks}"}; + AxisSpec hasCollAxis = {2, -0.5, 1.5, "JetDerived hasColl"}; + registry.add("h2_n_reco_tracks_from_target_vs_has_coll", + "reconstructed tracks from target MC collision vs hasColl;N_{target-origin reconstructed tracks};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + registry.add("h2_n_unassigned_tracks_from_target_vs_has_coll", + "unassigned tracks from target MC collision vs hasColl;N_{target-origin unassigned tracks};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + registry.add("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll", + "target-origin tracks attached to other-labeled collision vs hasColl;N_{tracks attached to other-labeled collision};JetDerived hasColl;weighted counts", + {HistType::kTH2F, {trackFateCountAxis, hasCollAxis}}); + + auto setHasCollAxisLabels = [](const auto& histogram) { + histogram->GetYaxis()->SetBinLabel(1, "noColl"); + histogram->GetYaxis()->SetBinLabel(2, "hasColl"); + }; + setHasCollAxisLabels(registry.get(HIST("h2_n_reco_tracks_from_target_vs_has_coll"))); + setHasCollAxisLabels(registry.get(HIST("h2_n_unassigned_tracks_from_target_vs_has_coll"))); + setHasCollAxisLabels(registry.get(HIST("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll"))); + } + + if (doprocessMCHasCollJetSurvivalQA) { + AxisSpec detectorJetExistsAxis = {2, -0.5, 1.5, "detector jet with target-origin reco track"}; + AxisSpec constituentPtFractionAxis = {300, 0., 3., "p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents}"}; + + registry.add("h2_mcp_jet_pt_vs_has_detector_jet_hasColl", + "MCP jet pT vs detector-level jet survival for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});detector jet with target-origin reco track;weighted counts", + {HistType::kTH2F, {jetPtAxis, detectorJetExistsAxis}}); + registry.add("h2_mcp_jet_pt_vs_has_detector_jet_noColl", + "MCP jet pT vs detector-level jet survival for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});detector jet with target-origin reco track;weighted counts", + {HistType::kTH2F, {jetPtAxis, detectorJetExistsAxis}}); + registry.add("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_hasColl", + "MCP jet pT vs target-origin reconstructed constituent pT fraction for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});#Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_noColl", + "MCP jet pT vs target-origin reconstructed constituent pT fraction for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});#Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_hasColl", + "MCP jet pT vs best detector-jet target-track capture fraction for hasColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});best #Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + registry.add("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_noColl", + "MCP jet pT vs best detector-jet target-track capture fraction for noColl;#it{p}_{T,jet}^{MCP} (GeV/#it{c});best #Sigma p_{T}^{reco target tracks} / #Sigma p_{T}^{truth constituents};weighted counts", + {HistType::kTH2F, {jetPtAxis, constituentPtFractionAxis}}); + + auto setDetectorJetExistsLabels = [](const auto& histogram) { + histogram->GetYaxis()->SetBinLabel(1, "no detector jet"); + histogram->GetYaxis()->SetBinLabel(2, "detector jet exists"); + }; + setDetectorJetExistsLabels(registry.get(HIST("h2_mcp_jet_pt_vs_has_detector_jet_hasColl"))); + setDetectorJetExistsLabels(registry.get(HIST("h2_mcp_jet_pt_vs_has_detector_jet_noColl"))); + } + } + + template + float computePtHat(TMcCollision const& mccollision) + { + float ptHardFromMc = ptHardCalcMethodSwitch; + float storedPtHard = mccollision.ptHard(); + if (storedPtHard > BrokenPtHardSentinel && storedPtHard < ptHardCalcMethodSwitch) { + ptHardFromMc = storedPtHard; + } + float weight = mccollision.weight(); + return ptHardFromMc < ptHardCalcMethodSwitch + ? ptHardFromMc + : simPtRef / std::pow(weight, 1.0f / pTHatExponent); + } + + template + bool configureRofParameters(TBC const& bc) + { + if (truthRofOffsetInBC >= 0 && truthRofLengthInBC > 0) { + cachedTruthRofOffsetInBC = truthRofOffsetInBC; + cachedTruthRofLengthInBC = truthRofLengthInBC; + return true; + } + + if (cachedTruthRofRun == bc.runNumber() && cachedTruthRofLengthInBC > 0) { + return true; + } + + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", bc.timestamp()); + if (alppar == nullptr) { + LOGF(fatal, "Could not retrieve ITS/Config/AlpideParam for ITS ROF calculations (run %d, timestamp %" PRIu64 ")", bc.runNumber(), static_cast(bc.timestamp())); + return false; + } + + cachedTruthRofRun = bc.runNumber(); + cachedTruthRofOffsetInBC = truthRofOffsetInBC >= 0 ? truthRofOffsetInBC : alppar->roFrameBiasInBC; + cachedTruthRofLengthInBC = truthRofLengthInBC > 0 ? truthRofLengthInBC : alppar->roFrameLengthInBC; + if (cachedTruthRofLengthInBC <= 0) { + LOGF(fatal, "Invalid ITS ROF length %" PRId64 " BC", static_cast(cachedTruthRofLengthInBC)); + return false; + } + LOGF(info, "ITS ROF calculations use offset %" PRId64 " and length %" PRId64 " BC for run %d", static_cast(cachedTruthRofOffsetInBC), static_cast(cachedTruthRofLengthInBC), cachedTruthRofRun); + return true; + } + + int64_t rofId(uint64_t globalBC) const + { + // Match EventSelectionModule: use the ITS ROF bias and length from DPLAlpideParam, with one orbit added to avoid a negative numerator. + return (static_cast(globalBC) + o2::constants::lhc::LHCMaxBunches - cachedTruthRofOffsetInBC) / cachedTruthRofLengthInBC; + } + + template + void requireValidTruthFT0CActivity(TMcCollision const& mccollision) const + { + if (mccollision.multFT0C() < 0.0f) { + LOGF(fatal, "sel8FullPbPb truth selection requires valid MC multFT0C, but multFT0C < 0 was found. Enable MC multiplicity information in the derived-data production."); + } + } + + template + float truthFT0CActivity(TMcCollision const& mccollision) const + { + requireValidTruthFT0CActivity(mccollision); + return mccollision.multFT0C(); + } + + template + TruthPbPbSelections evaluateTruthPbPbSelections(TMcCollision const& mccollision, TAllMcCollisions const& allMcCollisions) + { + TruthPbPbSelections selections; + requireValidTruthFT0CActivity(mccollision); + auto truthBC = mccollision.template bc_as(); + const uint64_t currentGlobalBC = truthBC.globalBC(); + if (currentGlobalBC == std::numeric_limits::max() || !configureRofParameters(truthBC)) { + return selections; + } + + const int64_t currentRof = rofId(currentGlobalBC); + bool hasNarrowActivity = false; + bool hasHighActivityInTimeRange = false; + bool hasHighActivityInSameRof = false; + bool hasCloseVzActivityInSameRof = false; + + // EventSelectionModule uses reconstructed foundGlobalBC, FT0C digit amplitudes and ITS layer 5-7 tracks. + // Those are unavailable for hasColl=false MC collisions. + // This truth-only approximation uses nominal MC BCs and multFT0C; its thresholds are intentionally configurable. + // The scan is limited to the current input chunk because JMcCollisions has no truth-side found-BC or time-frame association. + for (auto const& otherMcCollision : allMcCollisions) { + if (otherMcCollision.globalIndex() == mccollision.globalIndex()) { + continue; + } + + auto otherTruthBC = otherMcCollision.template bc_as(); + const uint64_t otherGlobalBC = otherTruthBC.globalBC(); + if (otherTruthBC.runNumber() != truthBC.runNumber() || otherGlobalBC == std::numeric_limits::max()) { + continue; + } + + const int64_t deltaGlobalBC = static_cast(otherGlobalBC) - static_cast(currentGlobalBC); + const float deltaTimeUs = static_cast(deltaGlobalBC) * o2::constants::lhc::LHCBunchSpacingNS / 1000.0f; + const bool inNarrowWindow = std::abs(deltaTimeUs) < truthTimeRangeNarrowUs; + const bool inStandardTimeWindow = deltaTimeUs > truthTimeRangeStandardMinUs && deltaTimeUs < truthTimeRangeStandardMaxUs; + const bool isSameRof = rofId(otherGlobalBC) == currentRof; + if (!inNarrowWindow && !inStandardTimeWindow && !isSameRof) { + continue; + } + + const float otherActivity = truthFT0CActivity(otherMcCollision); + const bool hasTrackPresenceProxy = otherActivity > truthFT0CActivityMinForTrackProxy; + + if (inNarrowWindow && hasTrackPresenceProxy) { + hasNarrowActivity = true; + } + if (inStandardTimeWindow && otherActivity > truthFT0CActivityThresholdTimeRange) { + hasHighActivityInTimeRange = true; + } + + if (isSameRof) { + if (otherActivity > truthFT0CActivityThresholdROF) { + hasHighActivityInSameRof = true; + } + // The reco bit uses ITS layer 5-7 tracks and reconstructed PV z. + // The truth approximation uses multFT0C and MC collision z instead. + if (hasTrackPresenceProxy && std::abs(otherMcCollision.posZ() - mccollision.posZ()) < truthRofCloseVzMax) { + hasCloseVzActivityInSameRof = true; } } } - std::vector allPossibleBits = { - jetderiveddatautilities::JCollisionSel::selTVX, - jetderiveddatautilities::JCollisionSel::selNoTimeFrameBorder, - jetderiveddatautilities::JCollisionSel::selNoITSROFrameBorder, - jetderiveddatautilities::JCollisionSel::selNoSameBunchPileup, - jetderiveddatautilities::JCollisionSel::selIsGoodZvtxFT0vsPV, - jetderiveddatautilities::JCollisionSel::selNoCollInTimeRangeStandard, - jetderiveddatautilities::JCollisionSel::selNoCollInRofStandard}; + selections.valid = true; + selections.noCollInTimeRangeStandard = !hasNarrowActivity && !hasHighActivityInTimeRange; + selections.noCollInRofStandard = !hasHighActivityInSameRof && !hasCloseVzActivityInSameRof; + return selections; + } + + template + bool passesTVXBCEnvironmentBaseline(TBC const& bc) + { + const bool passesRct = !static_cast(applyRCT) || (bc.rct_raw() & rctMask) == 0; + return passesRct && + bc.selection_bit(aod::evsel::kIsTriggerTVX) && + bc.selection_bit(aod::evsel::kNoTimeFrameBorder) && + bc.selection_bit(aod::evsel::kNoITSROFrameBorder); + } - for (auto bit : allPossibleBits) { - if (hasBit(bit)) { - addStageIfNotAlreadyAdded(bit); + template + bool isOtherTVXBCForEnvironment(TBC const& bc) + { + if (!bc.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + return !static_cast(tvxEnvironmentRequireOtherBcBaseline) || passesTVXBCEnvironmentBaseline(bc); + } + + template + VisibleBCEnvironmentCache buildVisibleBCEnvironmentCache(TBCs const& bcs) + { + struct TVXBCInfo { + int64_t globalIndex = -1; + int runNumber = 0; + uint64_t globalBC = 0; + int64_t rof = -1; + bool passesTargetBaseline = false; + }; + + std::map> tvxBCsPerRun; + for (const auto& bc : bcs) { + if (!isOtherTVXBCForEnvironment(bc) || bc.globalBC() == std::numeric_limits::max()) { + continue; + } + if (!configureRofParameters(bc)) { + continue; } + tvxBCsPerRun[bc.runNumber()].push_back({static_cast(bc.globalIndex()), bc.runNumber(), bc.globalBC(), rofId(bc.globalBC()), passesTVXBCEnvironmentBaseline(bc)}); } - binZvtx = nextEventSelectionBin++; - binCentrality = nextEventSelectionBin++; + VisibleBCEnvironmentCache cache; + for (auto& [runNumber, tvxBCs] : tvxBCsPerRun) { + std::sort(tvxBCs.begin(), tvxBCs.end(), [](const TVXBCInfo& lhs, const TVXBCInfo& rhs) { + return lhs.globalBC < rhs.globalBC; + }); - AxisSpec jetPtAxis = {200, 0., jetPtMax, "#it{p}_{T} (GeV/#it{c})"}; - int totalEventSelectionBins = nextEventSelectionBin - 1; - AxisSpec eventSelectionAxis = {totalEventSelectionBins, 0.5, static_cast(nextEventSelectionBin) - 0.5, "event selection"}; - - if (doprocessCrossSectionEfficiency || doprocessCrossSectionEfficiencyWeighted) { - registry.add("h2_jet_pt_part_eventselection", - "part jet pT vs event selection;#it{p}_{T,jet}^{part} (GeV/#it{c});event selection;counts", - {HistType::kTH2F, {jetPtAxis, eventSelectionAxis}}); - auto histJetPtVsEventSelection = registry.get(HIST("h2_jet_pt_part_eventselection")); - histJetPtVsEventSelection->GetYaxis()->SetBinLabel(binInel, "INEL"); - histJetPtVsEventSelection->GetYaxis()->SetBinLabel(binNoReco, "noRecoColl"); - histJetPtVsEventSelection->GetYaxis()->SetBinLabel(binSplit, "splitColl"); - for (auto const& stage : activeStages) { - const char* label = getStageLabel(stage.bit); - if (label) { - histJetPtVsEventSelection->GetYaxis()->SetBinLabel(stage.bin, label); + std::unordered_map nTVXBCsPerRof; + for (const auto& tvxBC : tvxBCs) { + ++nTVXBCsPerRof[tvxBC.rof]; + } + + for (size_t i = 0; i < tvxBCs.size(); ++i) { + const auto& tvxBC = tvxBCs[i]; + if (!tvxBC.passesTargetBaseline) { + continue; + } + + uint64_t nearestAbsDeltaGlobalBC = std::numeric_limits::max(); + if (i > 0) { + nearestAbsDeltaGlobalBC = tvxBC.globalBC - tvxBCs[i - 1].globalBC; } + if (i + 1 < tvxBCs.size()) { + const uint64_t nextAbsDeltaGlobalBC = tvxBCs[i + 1].globalBC - tvxBC.globalBC; + nearestAbsDeltaGlobalBC = std::min(nearestAbsDeltaGlobalBC, nextAbsDeltaGlobalBC); + } + + VisibleBCEnvironment environment; + environment.valid = true; + environment.nOtherTVXSameRof = nTVXBCsPerRof[tvxBC.rof] - 1; + environment.originalBCIndex = tvxBC.globalIndex; + if (nearestAbsDeltaGlobalBC != std::numeric_limits::max()) { + environment.nearestAbsDeltaTVXGlobalBC = static_cast(nearestAbsDeltaGlobalBC); + } + cache.byBCIndex[tvxBC.globalIndex] = environment; + cache.byRunAndGlobalBC[{runNumber, tvxBC.globalBC}] = environment; + } + } + return cache; + } + + template + std::map buildTruthHasCollEnvironmentCache(TMcCollisions const& mcCollisions) + { + struct TruthBCInfo { + int64_t mcCollisionIndex = -1; + int runNumber = 0; + uint64_t globalBC = 0; + int64_t rof = -1; + }; + + std::map> truthBCsPerRun; + for (const auto& mccollision : mcCollisions) { + const auto truthBC = mccollision.template bc_as(); + if (truthBC.globalBC() == std::numeric_limits::max() || !configureRofParameters(truthBC)) { + continue; } - histJetPtVsEventSelection->GetYaxis()->SetBinLabel(binZvtx, "zvtx"); - histJetPtVsEventSelection->GetYaxis()->SetBinLabel(binCentrality, "centralitycut"); + truthBCsPerRun[truthBC.runNumber()].push_back({static_cast(mccollision.globalIndex()), truthBC.runNumber(), truthBC.globalBC(), rofId(truthBC.globalBC())}); } - if (doprocessCrossSectionEfficiency) { - registry.add("h_mccollisions_eventselection", - "number of mc events vs event selection;event selection;entries", - {HistType::kTH1F, {{totalEventSelectionBins, 0.5, static_cast(nextEventSelectionBin) - 0.5}}}); - auto histMcCollisionsEventSelection = registry.get(HIST("h_mccollisions_eventselection")); - histMcCollisionsEventSelection->GetXaxis()->SetBinLabel(binInel, "INEL"); - histMcCollisionsEventSelection->GetXaxis()->SetBinLabel(binNoReco, "noRecoColl"); - histMcCollisionsEventSelection->GetXaxis()->SetBinLabel(binSplit, "splitColl"); - for (auto const& stage : activeStages) { - const char* label = getStageLabel(stage.bit); - if (label) { - histMcCollisionsEventSelection->GetXaxis()->SetBinLabel(stage.bin, label); - } + std::map cache; + for (auto& [runNumber, truthBCs] : truthBCsPerRun) { + (void)runNumber; + std::sort(truthBCs.begin(), truthBCs.end(), [](const TruthBCInfo& lhs, const TruthBCInfo& rhs) { + return lhs.globalBC < rhs.globalBC; + }); + + std::unordered_map nTruthBCsPerRof; + for (const auto& truthBC : truthBCs) { + ++nTruthBCsPerRof[truthBC.rof]; } - histMcCollisionsEventSelection->GetXaxis()->SetBinLabel(binZvtx, "zvtx"); - histMcCollisionsEventSelection->GetXaxis()->SetBinLabel(binCentrality, "centralitycut"); - } - - if (doprocessCrossSectionEfficiencyWeighted) { - registry.add("h_mccollisions_eventselection_weighted", - "number of weighted mc events vs event selection;event selection;weighted entries", - {HistType::kTH1F, {{totalEventSelectionBins, 0.5, static_cast(nextEventSelectionBin) - 0.5}}}); - auto histMcCollisionsEventSelectionWeighted = registry.get(HIST("h_mccollisions_eventselection_weighted")); - histMcCollisionsEventSelectionWeighted->GetXaxis()->SetBinLabel(binInel, "INEL"); - histMcCollisionsEventSelectionWeighted->GetXaxis()->SetBinLabel(binNoReco, "noRecoColl"); - histMcCollisionsEventSelectionWeighted->GetXaxis()->SetBinLabel(binSplit, "splitColl"); - for (auto const& stage : activeStages) { - const char* label = getStageLabel(stage.bit); - if (label) { - histMcCollisionsEventSelectionWeighted->GetXaxis()->SetBinLabel(stage.bin, label); + + for (size_t i = 0; i < truthBCs.size(); ++i) { + const auto& truthBC = truthBCs[i]; + uint64_t nearestAbsDeltaGlobalBC = std::numeric_limits::max(); + if (i > 0) { + nearestAbsDeltaGlobalBC = truthBC.globalBC - truthBCs[i - 1].globalBC; + } + if (i + 1 < truthBCs.size()) { + const uint64_t nextAbsDeltaGlobalBC = truthBCs[i + 1].globalBC - truthBC.globalBC; + nearestAbsDeltaGlobalBC = std::min(nearestAbsDeltaGlobalBC, nextAbsDeltaGlobalBC); } + + TruthHasCollEnvironment environment; + environment.valid = true; + environment.nOtherSameTruthRof = nTruthBCsPerRof[truthBC.rof] - 1; + if (nearestAbsDeltaGlobalBC != std::numeric_limits::max()) { + environment.nearestAbsDeltaGlobalBC = static_cast(nearestAbsDeltaGlobalBC); + } + cache[truthBC.mcCollisionIndex] = environment; } - histMcCollisionsEventSelectionWeighted->GetXaxis()->SetBinLabel(binZvtx, "zvtx"); - histMcCollisionsEventSelectionWeighted->GetXaxis()->SetBinLabel(binCentrality, "centralitycut"); } + return cache; + } + + template + void fillTVXBCEnvironmentHistogramsFromCache(VisibleBCEnvironmentCache const& environments, + TBCs const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + for (const auto& bc : bcs) { + const auto environment = environments.byBCIndex.find(static_cast(bc.globalIndex())); + if (environment == environments.byBCIndex.end()) { + continue; + } + + // This is a data-level TVX-BC to reconstructed-collision proxy, not the MC truth hasColl definition. + const auto collisionsAtTargetBC = collisions.sliceBy(collisionsPerFoundBC, bc.globalIndex()); + const bool hasRecoCollisionAtTargetBC = collisionsAtTargetBC.size() >= 1; + const auto& visibleEnvironment = environment->second; + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx"), visibleEnvironment.nOtherTVXSameRof, visibleEnvironment.nearestAbsDeltaTVXGlobalBC); + if (hasRecoCollisionAtTargetBC) { + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_nearest_abs_delta_global_bc_tvx_has_reco"), visibleEnvironment.nOtherTVXSameRof, visibleEnvironment.nearestAbsDeltaTVXGlobalBC); + } + registry.fill(HIST("h2_n_other_tvx_same_rof_vs_has_reco_collision"), visibleEnvironment.nOtherTVXSameRof, hasRecoCollisionAtTargetBC); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_tvx_vs_has_reco_collision"), visibleEnvironment.nearestAbsDeltaTVXGlobalBC, hasRecoCollisionAtTargetBC); + if (bc.has_foundFT0()) { + const auto foundFT0 = ft0s.rawIteratorAt(bc.foundFT0Id()); + registry.fill(HIST("h2_target_ft0c_amplitude_vs_n_other_tvx_same_rof"), foundFT0.sumAmpC(), visibleEnvironment.nOtherTVXSameRof); + } + } + } + + template + void fillTVXBCEnvironmentHistograms(TBCs const& bcs, CollisionsWithEvSels const& collisions, aod::FT0s const& ft0s) + { + const auto environments = buildVisibleBCEnvironmentCache(bcs); + fillTVXBCEnvironmentHistogramsFromCache(environments, bcs, collisions, ft0s); } template bool isAcceptedJet(TJets const& jet) { - if (jetAreaFractionMin > configSwitchLow) { + if (jetAreaFractionMin > ConfigSwitchLow) { if (jet.area() < jetAreaFractionMin * o2::constants::math::PI * (jet.r() / 100.0) * (jet.r() / 100.0)) { return false; } } - bool checkConstituentMinPt = (leadingConstituentPtMinMCP > configSwitchLow); - bool checkConstituentMaxPt = (leadingConstituentPtMaxMCP < configSwitchHigh); + bool checkConstituentMinPt = (leadingConstituentPtMinMCP > ConfigSwitchLow); + bool checkConstituentMaxPt = (leadingConstituentPtMaxMCP < ConfigSwitchHigh); bool checkConstituentPt = checkConstituentMinPt || checkConstituentMaxPt; if (checkConstituentPt) { @@ -264,296 +937,598 @@ struct JetCrossSectionEfficiency { return true; } - void processCrossSectionEfficiency(aod::JetMcCollisions::iterator const& mccollision, - soa::SmallGroups const& collisions, - soa::Join const& jets, - aod::JetParticles const&) + void processMCTruthMultFT0CvsRecoAmplitude(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + CollisionsWithEvSels const&, + aod::FT0s const& ft0s) { - bool hasRecoColl = (collisions.size() >= 1); - bool passesZvtxCut = (std::abs(mccollision.posZ()) <= vertexZCut); - bool passesSplitCollCut = !(acceptSplitCollisions == NonSplitOnly && collisions.size() > 1); + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + if (mccollision.multFT0C() < 0.0f) { + return; + } + // A single associated JCollision gives a one-to-one MC-collision to reconstructed-collision association. + // The parent index restores the original EvSels relation used by EventSelectionModule. + if (collisions.size() != 1) { + return; + } + auto const& collision = collisions.begin(); + auto originalCollision = collision.template collision_as(); + if (!originalCollision.has_foundFT0()) { + return; + } + auto foundFT0 = ft0s.rawIteratorAt(originalCollision.foundFT0Id()); + registry.fill(HIST("h2_mccollision_mult_ft0c_found_ft0_sum_amp_c"), mccollision.multFT0C(), foundFT0.sumAmpC(), mccollision.weight()); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCTruthMultFT0CvsRecoAmplitude, + "[MC ONLY] truth multFT0C vs reconstructed foundFT0 A_FT0C for one-to-one MC/reco collision associations", false); - if (hasRecoColl) { - bool occupancyIsGood = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly) { - auto const& collision = collisions.begin(); - if ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { - occupancyIsGood = true; - } - } else { - for (auto const& collision : collisions) { - if ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { - occupancyIsGood = true; - break; - } - } - } - if (!occupancyIsGood) { - return; + void processMCTruthMultFT0CvsRecoITS567(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + CollisionsWithEvSels const&, + FullTracksIU const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + if (mccollision.multFT0C() < 0.0f) { + return; + } + // This matches EventSelectionModule::vTracksITS567perColl: only PV-contributor tracks with at least five ITS clusters are counted. + if (collisions.size() != 1) { + return; + } + auto const& collision = collisions.begin(); + auto originalCollision = collision.template collision_as(); + auto const& collisionPvTracks = pvTracks.sliceBy(pvTracksPerCollision, originalCollision.globalIndex()); + int nITS567 = 0; + for (const auto& track : collisionPvTracks) { + if (track.itsNCls() >= MinITSClustersForOccupancy) { + ++nITS567; } } - std::vector stagePassed(activeStages.size(), false); - bool hasCustomEventSel = false; - bool centralityIsGood = false; - float centrality = mccollision.centFT0M(); - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly) { - if (hasRecoColl) { - auto const& collision = collisions.begin(); - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (collision.eventSel() & (1 << activeStages[i].bit)) { - stagePassed[i] = true; - } - } - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents)) { - hasCustomEventSel = true; - } + const float weight = mccollision.weight(); + registry.fill(HIST("h2_mccollision_mult_ft0c_reco_n_its567"), mccollision.multFT0C(), nITS567, weight); + registry.fill(HIST("h2_mccollision_mult_ft0c_positive_vs_reco_its567_positive"), + mccollision.multFT0C() > 0.0f, nITS567 > 0, weight); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCTruthMultFT0CvsRecoITS567, + "[MC ONLY] truth multFT0C activity vs reconstructed PV-contributor ITS567 activity for one-to-one MC/reco collision associations", false); + + void processMCEnvironmentQA(aod::JetMcCollisions const& mcCollisions, + aod::JetCollisionsMCD const& jetCollisions, + aod::JBCs const&, + OriginalBCsWithSels const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) + { + const auto visibleEnvironments = buildVisibleBCEnvironmentCache(bcs); + const auto truthEnvironments = buildTruthHasCollEnvironmentCache(mcCollisions); + // The visible-BC QA is deliberately unweighted so its definition remains directly comparable to data. + fillTVXBCEnvironmentHistogramsFromCache(visibleEnvironments, bcs, collisions, ft0s); + for (const auto& mccollision : mcCollisions) { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + continue; } - if ((centralityMin < centrality) && (centrality < centralityMax)) { - centralityIsGood = true; + + const auto truthBC = mccollision.template bc_as(); + if (!passesTVXBCEnvironmentBaseline(truthBC)) { + continue; } - } else { - for (auto const& collision : collisions) { - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (collision.eventSel() & (1 << activeStages[i].bit)) { - stagePassed[i] = true; - } + + const auto environment = truthEnvironments.find(mccollision.globalIndex()); + if (environment == truthEnvironments.end() || !environment->second.valid) { + continue; + } + + const bool hasRecoColl = jetCollisions.sliceBy(jetCollisionsPerMcCollision, mccollision.globalIndex()).size() >= 1; + const float weight = mccollision.weight(); + registry.fill(HIST("h2_n_other_same_truth_rof_vs_has_coll"), environment->second.nOtherSameTruthRof, hasRecoColl, weight); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_vs_has_coll"), environment->second.nearestAbsDeltaGlobalBC, hasRecoColl, weight); + + const auto visibleEnvironment = visibleEnvironments.byRunAndGlobalBC.find({truthBC.runNumber(), truthBC.globalBC()}); + if (visibleEnvironment != visibleEnvironments.byRunAndGlobalBC.end() && visibleEnvironment->second.valid) { + const auto collisionsAtTargetBC = collisions.sliceBy(collisionsPerFoundBC, visibleEnvironment->second.originalBCIndex); + const bool hasRecoCollisionAtTargetBC = collisionsAtTargetBC.size() >= 1; + registry.fill(HIST("h2_n_other_truth_same_rof_vs_n_other_tvx_same_rof"), environment->second.nOtherSameTruthRof, visibleEnvironment->second.nOtherTVXSameRof, weight); + registry.fill(HIST("h2_nearest_abs_delta_global_bc_truth_vs_nearest_abs_delta_global_bc_tvx"), environment->second.nearestAbsDeltaGlobalBC, visibleEnvironment->second.nearestAbsDeltaTVXGlobalBC, weight); + registry.fill(HIST("h2_reco_collision_proxy_vs_has_coll"), hasRecoCollisionAtTargetBC, hasRecoColl, weight); + } + + if (mccollision.multFT0C() < 0.0f) { + continue; + } + + registry.fill(HIST("h2_target_mult_ft0c_vs_has_coll"), mccollision.multFT0C(), hasRecoColl, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_n_other_same_truth_rof_all"), mccollision.multFT0C(), environment->second.nOtherSameTruthRof, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_all"), mccollision.multFT0C(), environment->second.nearestAbsDeltaGlobalBC, weight); + if (hasRecoColl) { + registry.fill(HIST("h2_target_mult_ft0c_vs_n_other_same_truth_rof_has_coll"), mccollision.multFT0C(), environment->second.nOtherSameTruthRof, weight); + registry.fill(HIST("h2_target_mult_ft0c_vs_nearest_abs_delta_global_bc_has_coll"), mccollision.multFT0C(), environment->second.nearestAbsDeltaGlobalBC, weight); + } + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCEnvironmentQA, + "[MC ONLY] truth and visible TVX collision-environment QA and JetDerived hasColl validation", false); + + void processMCHasCollTrackFateQA(JMcCollisionsWithParent::iterator const& mccollision, + soa::SmallGroups const& collisions, + OriginalCollisionsWithMcLabels const&, + FullTracksIUWithMcLabels const& recoTracks, + aod::McCollisions const&, + aod::McParticles const& mcParticles, + aod::JBCs const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + + auto truthBC = mccollision.bc_as(); + const bool passesRct = !applyRCT || (truthBC.rct_raw() & rctMask) == 0; + const bool passesTVXTruth = truthBC.selection_bit(aod::evsel::kIsTriggerTVX); + const bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); + const bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); + if (!passesRct || !passesTVXTruth || !passesNoTFBTruth || !passesNoITSROFBTruth) { + return; + } + + const auto originalMcCollision = mccollision.template mcCollision_as(); + const auto targetMcParticles = mcParticles.sliceBy(mcParticlesPerMcCollision, originalMcCollision.globalIndex()); + int nRecoTracksFromTarget = 0; + int nUnassignedTracksFromTarget = 0; + int nTracksAttachedToOtherLabeledRecoCollision = 0; + + for (const auto& mcParticle : targetMcParticles) { + const auto targetRecoTracks = recoTracks.sliceBy(recoTracksPerMcParticle, mcParticle.globalIndex()); + for (const auto& recoTrack : targetRecoTracks) { + if (!recoTrack.has_mcParticle()) { + continue; } - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents)) { - hasCustomEventSel = true; + + ++nRecoTracksFromTarget; + if (!recoTrack.has_collision()) { + ++nUnassignedTracksFromTarget; + continue; } - float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); - if ((centralityMin < centrality) && (centrality < centralityMax)) { - centralityIsGood = true; + + const auto recoCollision = recoTrack.template collision_as(); + if (recoCollision.has_mcCollision() && recoCollision.mcCollisionId() != originalMcCollision.globalIndex()) { + ++nTracksAttachedToOtherLabeledRecoCollision; } } } - registry.fill(HIST("h_mccollisions_eventselection"), static_cast(binInel)); - if (!hasRecoColl) { - } else { - registry.fill(HIST("h_mccollisions_eventselection"), static_cast(binNoReco)); - if (!passesSplitCollCut) { - goto endEventCounter; + const bool hasRecoColl = collisions.size() >= 1; + const float weight = mccollision.weight(); + registry.fill(HIST("h2_n_reco_tracks_from_target_vs_has_coll"), nRecoTracksFromTarget, hasRecoColl, weight); + registry.fill(HIST("h2_n_unassigned_tracks_from_target_vs_has_coll"), nUnassignedTracksFromTarget, hasRecoColl, weight); + registry.fill(HIST("h2_n_target_tracks_attached_to_other_labeled_collision_vs_has_coll"), nTracksAttachedToOtherLabeledRecoCollision, hasRecoColl, weight); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollTrackFateQA, + "[MC ONLY] track fate for target MC collisions before JetDerived hasColl", false); + + void processMCHasCollJetSurvivalQA(JMcCollisionsWithParent::iterator const& mccollision, + soa::SmallGroups const& collisions, + ChargedMCPJetsWithConstituents const& mcpJets, + ChargedMCDJetsWithConstituents const& detectorJets, + JetCollisionsWithParent const& jetCollisions, + FullTracksIUWithMcLabels const& recoTracks, + aod::JetTracks const&, + aod::JTrackPIs const& jTrackParents, + aod::JetParticles const&, + aod::JMcParticlePIs const& jMcParticleParents, + aod::JBCs const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + + auto truthBC = mccollision.bc_as(); + const bool passesRct = !applyRCT || (truthBC.rct_raw() & rctMask) == 0; + const bool passesTVXTruth = truthBC.selection_bit(aod::evsel::kIsTriggerTVX); + const bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); + const bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); + if (!passesRct || !passesTVXTruth || !passesNoTFBTruth || !passesNoITSROFBTruth) { + return; + } + + const float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { + return; + } + + const bool hasRecoColl = collisions.size() >= 1; + const float weight = mccollision.weight(); + for (const auto& mcpJet : mcpJets) { + if (!jetfindingutilities::isInEtaAcceptance(mcpJet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + mcpJet.pt() < jetPtMin || mcpJet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(mcpJet)) { + continue; } - registry.fill(HIST("h_mccollisions_eventselection"), static_cast(binSplit)); - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (!stagePassed[i]) { - goto endEventCounter; + float sumTruthConstituentPt = 0.0f; + float sumRecoTrackPtFromTruthJet = 0.0f; + std::unordered_map recoTrackPtByOriginalTrackId; + std::unordered_set jetCollisionIdsReceivingTargetTracks; + for (const auto& constituent : mcpJet.template tracks_as()) { + sumTruthConstituentPt += constituent.pt(); + const auto originalMcParticle = jMcParticleParents.rawIteratorAt(constituent.globalIndex()); + const auto targetRecoTracks = recoTracks.sliceBy(recoTracksPerMcParticle, originalMcParticle.mcParticleId()); + for (const auto& recoTrack : targetRecoTracks) { + if (!recoTrack.has_mcParticle()) { + continue; + } + + const int64_t originalTrackId = recoTrack.globalIndex(); + if (!recoTrackPtByOriginalTrackId.emplace(originalTrackId, recoTrack.pt()).second) { + continue; + } + sumRecoTrackPtFromTruthJet += recoTrack.pt(); + + if (!recoTrack.has_collision()) { + continue; + } + const auto associatedJetCollisions = jetCollisions.sliceBy(jetCollisionsPerOriginalCollision, recoTrack.collisionId()); + for (const auto& jetCollision : associatedJetCollisions) { + jetCollisionIdsReceivingTargetTracks.insert(jetCollision.globalIndex()); + } } - registry.fill(HIST("h_mccollisions_eventselection"), static_cast(activeStages[i].bin)); } - if (!hasCustomEventSel || !passesZvtxCut) { - goto endEventCounter; + if (sumTruthConstituentPt <= 0.0f) { + continue; } - registry.fill(HIST("h_mccollisions_eventselection"), static_cast(binZvtx)); - if (!centralityIsGood) { - goto endEventCounter; + std::unordered_map capturedRecoPtByDetectorJetId; + for (const int64_t jetCollisionId : jetCollisionIdsReceivingTargetTracks) { + const auto associatedDetectorJets = detectorJets.sliceBy(detectorJetsPerJetCollision, jetCollisionId); + for (const auto& detectorJet : associatedDetectorJets) { + float capturedRecoPt = 0.0f; + for (const auto& detectorConstituent : detectorJet.template tracks_as()) { + const auto originalTrack = jTrackParents.rawIteratorAt(detectorConstituent.globalIndex()); + const auto recoTrack = recoTrackPtByOriginalTrackId.find(originalTrack.trackId()); + if (recoTrack != recoTrackPtByOriginalTrackId.end()) { + capturedRecoPt += recoTrack->second; + } + } + if (capturedRecoPt > 0.0f) { + capturedRecoPtByDetectorJetId[detectorJet.globalIndex()] += capturedRecoPt; + } + } } - registry.fill(HIST("h_mccollisions_eventselection"), static_cast(binCentrality)); - } - endEventCounter: - - for (auto const& jet : jets) { - if (jet.r() != round(selectedJetsRadius * 100.0f)) { - continue; + float bestDetectorJetCapturedTargetRecoPt = 0.0f; + for (const auto& [detectorJetId, capturedRecoPt] : capturedRecoPtByDetectorJetId) { + (void)detectorJetId; + if (capturedRecoPt > bestDetectorJetCapturedTargetRecoPt) { + bestDetectorJetCapturedTargetRecoPt = capturedRecoPt; + } } - if (jet.pt() < jetPtMin) { - continue; + + const bool hasDetectorJetWithTargetTrack = !capturedRecoPtByDetectorJetId.empty(); + const float recoConstituentPtFraction = sumRecoTrackPtFromTruthJet / sumTruthConstituentPt; + const float bestDetectorJetCaptureFraction = bestDetectorJetCapturedTargetRecoPt / sumTruthConstituentPt; + if (hasRecoColl) { + registry.fill(HIST("h2_mcp_jet_pt_vs_has_detector_jet_hasColl"), mcpJet.pt(), hasDetectorJetWithTargetTrack, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_hasColl"), mcpJet.pt(), recoConstituentPtFraction, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_hasColl"), mcpJet.pt(), bestDetectorJetCaptureFraction, weight); + } else { + registry.fill(HIST("h2_mcp_jet_pt_vs_has_detector_jet_noColl"), mcpJet.pt(), hasDetectorJetWithTargetTrack, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_reco_constituent_pt_fraction_noColl"), mcpJet.pt(), recoConstituentPtFraction, weight); + registry.fill(HIST("h2_mcp_jet_pt_vs_best_detector_jet_capture_fraction_noColl"), mcpJet.pt(), bestDetectorJetCaptureFraction, weight); } - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollJetSurvivalQA, + "[MC ONLY] truth-constituent overlap QA for detector-level charged jet survival before JetDerived hasColl", false); + + void processMCHasCollJetEnvironmentQA(aod::JetMcCollisions const& mcCollisions, + aod::JetCollisionsMCD const& jetCollisions, + ChargedMCPJetsWithConstituents const& mcpJets, + aod::JetParticles const&, + aod::JBCs const&, + OriginalBCsWithSels const& bcs) + { + const auto visibleEnvironments = buildVisibleBCEnvironmentCache(bcs); + for (const auto& mccollision : mcCollisions) { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { continue; } - if (!isAcceptedJet(jet)) { + + const auto truthBC = mccollision.template bc_as(); + if (!passesTVXBCEnvironmentBaseline(truthBC)) { continue; } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binInel)); - if (!hasRecoColl) { + const auto visibleEnvironment = visibleEnvironments.byRunAndGlobalBC.find({truthBC.runNumber(), truthBC.globalBC()}); + if (visibleEnvironment == visibleEnvironments.byRunAndGlobalBC.end() || !visibleEnvironment->second.valid) { continue; } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binNoReco)); - if (!passesSplitCollCut) { + const float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { continue; } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binSplit)); - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (!stagePassed[i]) { - goto nextJetUnweighted; + const bool hasRecoColl = jetCollisions.sliceBy(jetCollisionsPerMcCollision, mccollision.globalIndex()).size() >= 1; + const float weight = mccollision.weight(); + const auto targetMcpJets = mcpJets.sliceBy(mcpJetsPerMcCollision, mccollision.globalIndex()); + for (const auto& mcpJet : targetMcpJets) { + if (!jetfindingutilities::isInEtaAcceptance(mcpJet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + mcpJet.pt() < jetPtMin || mcpJet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(mcpJet)) { + continue; } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(activeStages[i].bin)); - } - - if (!hasCustomEventSel || !passesZvtxCut) { - goto nextJetUnweighted; - } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binZvtx)); - if (!centralityIsGood) { - goto nextJetUnweighted; + // hasRecoColl remains a histogram axis rather than a selection, so noColl MCP jets are part of the denominator. + registry.fill(HIST("hs_mcp_jet_pt_tvx_environment_has_coll"), mcpJet.pt(), visibleEnvironment->second.nOtherTVXSameRof, visibleEnvironment->second.nearestAbsDeltaTVXGlobalBC, hasRecoColl, weight); } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binCentrality)); - - nextJetUnweighted:; } } - PROCESS_SWITCH(JetCrossSectionEfficiency, processCrossSectionEfficiency, "jet spectra QC for MC particle level with step-by-step cuts", false); + PROCESS_SWITCH(JetCrossSectionEfficiency, processMCHasCollJetEnvironmentQA, + "[MC ONLY] selected MCP jets vs original-TVX-BC environment and JetDerived hasColl", false); - void processCrossSectionEfficiencyWeighted(aod::JetMcCollisions::iterator const& mccollision, - soa::SmallGroups const& collisions, - soa::Join const& jets, - aod::JetParticles const&) + void processDataTVXBCEnvironmentQA(OriginalBCsWithSels const& bcs, + CollisionsWithEvSels const& collisions, + aod::FT0s const& ft0s) { - bool hasRecoColl = (collisions.size() >= 1); - bool passesZvtxCut = (std::abs(mccollision.posZ()) <= vertexZCut); - bool passesSplitCollCut = !(acceptSplitCollisions == NonSplitOnly && collisions.size() > 1); + fillTVXBCEnvironmentHistograms(bcs, collisions, ft0s); + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processDataTVXBCEnvironmentQA, + "[DATA ONLY] TVX-BC collision-environment QA using original BC and reconstructed-collision tables", false); + + void processCrossSectionEfficiency(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + soa::Join const& jets, + aod::JetParticles const&, + aod::JBCs const&) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + bool hasRecoColl = (collisions.size() >= 1); + bool passesZvtxCutReco = false; if (hasRecoColl) { - bool occupancyIsGood = false; if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly) { - auto const& collision = collisions.begin(); - if ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { - occupancyIsGood = true; - } + auto const& col = collisions.begin(); + passesZvtxCutReco = (std::abs(col.posZ()) <= vertexZCut); } else { - for (auto const& collision : collisions) { - if ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { - occupancyIsGood = true; + for (auto const& col : collisions) { + if (std::abs(col.posZ()) <= vertexZCut) { + passesZvtxCutReco = true; break; } } } - if (!occupancyIsGood) { - return; + } + bool noSplitPass = (acceptSplitCollisions == NonSplitOnly) ? (collisions.size() == 1) : true; + + bool passesTVX = false; + bool passesNoTFB = false; + bool passesNoITSROFB = false; + bool passesNoSBP = false; + bool passesNoCollInTimeRangeStandard = false; + bool passesNoCollInRofStandard = false; + if (hasRecoColl) { + auto const& col = collisions.begin(); + auto evSel = col.eventSel(); + passesTVX = (evSel & (1u << jetderiveddatautilities::JCollisionSel::selTVX)) != 0u; + passesNoTFB = (evSel & (1u << jetderiveddatautilities::JCollisionSel::selNoTimeFrameBorder)) != 0u; + passesNoITSROFB = (evSel & (1u << jetderiveddatautilities::JCollisionSel::selNoITSROFrameBorder)) != 0u; + passesNoSBP = (evSel & (1u << jetderiveddatautilities::JCollisionSel::selNoSameBunchPileup)) != 0u; + passesNoCollInTimeRangeStandard = (evSel & (1u << jetderiveddatautilities::JCollisionSel::selNoCollInTimeRangeStandard)) != 0u; + passesNoCollInRofStandard = (evSel & (1u << jetderiveddatautilities::JCollisionSel::selNoCollInRofStandard)) != 0u; + } + + bool passesRct = applyRCT ? (mccollision.bc_as().rct_raw() & rctMask) == 0 : true; + std::vector pass = {true, passesRct, hasRecoColl, passesZvtxCutReco, + hasRecoColl && noSplitPass, passesTVX, + applyTFB ? passesNoTFB : true, + applyROFB ? passesNoITSROFB : true}; + if (applyNoCollInTimeRangeStandard) { + pass.push_back(passesNoCollInTimeRangeStandard); + } + if (applyNoCollInRofStandard) { + pass.push_back(passesNoCollInRofStandard); + } + if (!isSel8FullPbPb) { + pass.push_back(applySBP ? passesNoSBP : true); + } + + float weight = mccollision.weight(); + int sMax = 0; + for (size_t s = 0; s < pass.size(); ++s) { + if (!pass[s]) { + break; } + registry.fill(HIST("h_mccollisions_eventselection_collRecoFirst"), static_cast(s + 1), weight); + sMax = static_cast(s + 1); + } + if (sMax == 0) { + return; } - std::vector stagePassed(activeStages.size(), false); - bool hasCustomEventSel = false; - bool centralityIsGood = false; - float centrality = mccollision.centFT0M(); - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly) { - if (hasRecoColl) { - auto const& collision = collisions.begin(); - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (collision.eventSel() & (1 << activeStages[i].bit)) { - stagePassed[i] = true; - } - } - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents)) { - hasCustomEventSel = true; - } + float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { + return; + } + + for (auto const& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + jet.pt() < jetPtMin || jet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(jet)) { + continue; } - if ((centralityMin < centrality) && (centrality < centralityMax)) { - centralityIsGood = true; + for (int s = 1; s <= sMax; ++s) { + registry.fill(HIST("h2_jet_pt_part_eventselection_collRecoFirst"), jet.pt(), static_cast(s), weight); } - } else { - for (auto const& collision : collisions) { - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (collision.eventSel() & (1 << activeStages[i].bit)) { - stagePassed[i] = true; + } + } + PROCESS_SWITCH(JetCrossSectionEfficiency, processCrossSectionEfficiency, + "Cascade efficiency with collision-reco required first, BC bits read from the reco-coll EvSels bitmask", true); + + void processCrossSectionEfficiencyBcBitsFirst(aod::JetMcCollisions::iterator const& mccollision, + soa::SmallGroups const& collisions, + soa::Join const& jets, + aod::JetParticles const&, + aod::JBCs const&, + aod::JetMcCollisions const& allMcCollisions) + { + if (skipMBGapEvents && mccollision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + return; + } + + auto truthBC = mccollision.bc_as(); + bool passesTVXTruth = truthBC.selection_bit(aod::evsel::kIsTriggerTVX); + bool passesNoTFBTruth = truthBC.selection_bit(aod::evsel::kNoTimeFrameBorder); + bool passesNoITSROFBTruth = truthBC.selection_bit(aod::evsel::kNoITSROFrameBorder); + + bool truthNoSBPDiagnostic = true; + if (applySBP || isSel8FullPbPb) { + auto sameBC = allMcCollisions.sliceBy(mcCollsPerBC, mccollision.bcId()); + truthNoSBPDiagnostic = (sameBC.size() == 1); + } + bool truthNoSBP = applySBP ? truthNoSBPDiagnostic : true; + + bool hasRecoColl = (collisions.size() >= 1); + bool passesZvtxCutReco = false; + if (hasRecoColl) { + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly) { + auto const& col = collisions.begin(); + passesZvtxCutReco = (std::abs(col.posZ()) <= vertexZCut); + } else { + for (auto const& col : collisions) { + if (std::abs(col.posZ()) <= vertexZCut) { + passesZvtxCutReco = true; + break; } } - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents)) { - hasCustomEventSel = true; - } - float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); - if ((centralityMin < centrality) && (centrality < centralityMax)) { - centralityIsGood = true; - } } } + bool noSplitPass = (acceptSplitCollisions == NonSplitOnly) ? (collisions.size() == 1) : true; - float eventWeight = mccollision.weight(); + TruthPbPbSelections truthPbPbSelections; + if (isSel8FullPbPb) { + truthPbPbSelections = evaluateTruthPbPbSelections(mccollision, allMcCollisions); + } - registry.fill(HIST("h_mccollisions_eventselection_weighted"), static_cast(binInel), eventWeight); - if (!hasRecoColl) { - } else { - registry.fill(HIST("h_mccollisions_eventselection_weighted"), static_cast(binNoReco), eventWeight); - if (!passesSplitCollCut) { - goto endEventCounterWeighted; + bool passesRct = applyRCT ? (truthBC.rct_raw() & rctMask) == 0 : true; + float weight = mccollision.weight(); + + if (isSel8FullPbPb && passesRct && passesTVXTruth && truthPbPbSelections.valid) { + // The five selection conditions are evaluated independently on the same RCT+TVX + // baseline with valid PbPb truth-proxy evaluation. + const std::array selectionConditionPasses = { + passesNoTFBTruth, + passesNoITSROFBTruth, + truthNoSBPDiagnostic, + truthPbPbSelections.noCollInTimeRangeStandard, + truthPbPbSelections.noCollInRofStandard}; + for (size_t conditionIndex = 0; conditionIndex < selectionConditionPasses.size(); ++conditionIndex) { + const int hasCollSelectionState = (hasRecoColl ? 3 : 1) + (selectionConditionPasses[conditionIndex] ? 1 : 0); + registry.fill(HIST("h2_mccollisions_selection_bit_vs_has_coll_state"), static_cast(conditionIndex + 1), hasCollSelectionState, weight); } - registry.fill(HIST("h_mccollisions_eventselection_weighted"), static_cast(binSplit), eventWeight); - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (!stagePassed[i]) { - goto endEventCounterWeighted; + float pTHatDiagnostic = computePtHat(mccollision); + if (pTHatDiagnostic >= pTHatAbsoluteMin) { + for (const auto& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + jet.pt() < jetPtMin || jet.pt() > pTHatMaxMCP * pTHatDiagnostic || + !isAcceptedJet(jet)) { + continue; + } + for (size_t conditionIndex = 0; conditionIndex < selectionConditionPasses.size(); ++conditionIndex) { + const int hasCollSelectionState = (hasRecoColl ? 3 : 1) + (selectionConditionPasses[conditionIndex] ? 1 : 0); + registry.fill(HIST("h2_mcpjets_selection_bit_vs_has_coll_state"), static_cast(conditionIndex + 1), hasCollSelectionState, weight); + } } - registry.fill(HIST("h_mccollisions_eventselection_weighted"), static_cast(activeStages[i].bin), eventWeight); } + } - if (!hasCustomEventSel || !passesZvtxCut) { - goto endEventCounterWeighted; + std::vector pass = {true, passesRct, passesTVXTruth, + applyTFB ? passesNoTFBTruth : true, + applyROFB ? passesNoITSROFBTruth : true}; + if (applyNoCollInTimeRangeStandard) { + pass.push_back(truthPbPbSelections.valid && truthPbPbSelections.noCollInTimeRangeStandard); + } + if (applyNoCollInRofStandard) { + pass.push_back(truthPbPbSelections.valid && truthPbPbSelections.noCollInRofStandard); + } + if (!isSel8FullPbPb) { + pass.push_back(applySBP ? truthNoSBP : true); + } + pass.push_back(hasRecoColl); + pass.push_back(passesZvtxCutReco); + pass.push_back(hasRecoColl && noSplitPass); + + int sMaxTimeRangeOnly = 0; + int sMaxRofOnly = 0; + if (isSel8FullPbPb) { + std::vector timeRangeOnlyPass = {true, passesRct, passesTVXTruth, + passesNoTFBTruth, passesNoITSROFBTruth, + truthPbPbSelections.valid && truthPbPbSelections.noCollInTimeRangeStandard, + hasRecoColl, passesZvtxCutReco, hasRecoColl && noSplitPass}; + std::vector rofOnlyPass = {true, passesRct, passesTVXTruth, + passesNoTFBTruth, passesNoITSROFBTruth, + truthPbPbSelections.valid && truthPbPbSelections.noCollInRofStandard, + hasRecoColl, passesZvtxCutReco, hasRecoColl && noSplitPass}; + for (size_t s = 0; s < timeRangeOnlyPass.size(); ++s) { + if (!timeRangeOnlyPass[s]) { + break; + } + registry.fill(HIST("h_mccollisions_eventselection_bcBitsFirst_timeRangeOnly_truth"), static_cast(s + 1), weight); + sMaxTimeRangeOnly = static_cast(s + 1); } - registry.fill(HIST("h_mccollisions_eventselection_weighted"), static_cast(binZvtx), eventWeight); - - if (!centralityIsGood) { - goto endEventCounterWeighted; + for (size_t s = 0; s < rofOnlyPass.size(); ++s) { + if (!rofOnlyPass[s]) { + break; + } + registry.fill(HIST("h_mccollisions_eventselection_bcBitsFirst_rofOnly_truth"), static_cast(s + 1), weight); + sMaxRofOnly = static_cast(s + 1); } - registry.fill(HIST("h_mccollisions_eventselection_weighted"), static_cast(binCentrality), eventWeight); } - endEventCounterWeighted: + int sMax = 0; + for (size_t s = 0; s < pass.size(); ++s) { + if (!pass[s]) { + break; + } + registry.fill(HIST("h_mccollisions_eventselection_bcBitsFirst"), static_cast(s + 1), weight); + sMax = static_cast(s + 1); + } + if (sMax == 0) { + return; + } - float pTHat = 10.0f / (std::pow(eventWeight, 1.0f / pTHatExponent)); + float pTHat = computePtHat(mccollision); + if (pTHat < pTHatAbsoluteMin) { + return; + } for (auto const& jet : jets) { - if (jet.r() != round(selectedJetsRadius * 100.0f)) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax) || + jet.pt() < jetPtMin || jet.pt() > pTHatMaxMCP * pTHat || + !isAcceptedJet(jet)) { continue; } - if (jet.pt() < jetPtMin) { - continue; + for (int s = 1; s <= sMax; ++s) { + registry.fill(HIST("h2_jet_pt_part_eventselection_bcBitsFirst"), jet.pt(), static_cast(s), weight); } - if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) { - continue; - } - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { - continue; - } - if (!isAcceptedJet(jet)) { - continue; - } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binInel), eventWeight); - - if (!hasRecoColl) { - continue; - } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binNoReco), eventWeight); - - if (!passesSplitCollCut) { - continue; - } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binSplit), eventWeight); - - for (std::size_t i = 0; i < activeStages.size(); ++i) { - if (!stagePassed[i]) { - goto nextJetWeighted; + if (isSel8FullPbPb) { + for (int s = 1; s <= sMaxTimeRangeOnly; ++s) { + registry.fill(HIST("h2_jet_pt_part_eventselection_bcBitsFirst_timeRangeOnly_truth"), jet.pt(), static_cast(s), weight); + } + for (int s = 1; s <= sMaxRofOnly; ++s) { + registry.fill(HIST("h2_jet_pt_part_eventselection_bcBitsFirst_rofOnly_truth"), jet.pt(), static_cast(s), weight); } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(activeStages[i].bin), eventWeight); - } - - if (!hasCustomEventSel || !passesZvtxCut) { - goto nextJetWeighted; - } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binZvtx), eventWeight); - - if (!centralityIsGood) { - goto nextJetWeighted; } - registry.fill(HIST("h2_jet_pt_part_eventselection"), jet.pt(), static_cast(binCentrality), eventWeight); - - nextJetWeighted:; } } - PROCESS_SWITCH(JetCrossSectionEfficiency, processCrossSectionEfficiencyWeighted, "jet spectra QC for MC particle level with step-by-step cuts (weighted)", false); + PROCESS_SWITCH(JetCrossSectionEfficiency, processCrossSectionEfficiencyBcBitsFirst, + "Cascade efficiency with BC bits evaluated on MC truth BC before collision reco", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGJE/Tasks/jetD0AngSubstructure.cxx b/PWGJE/Tasks/jetD0AngSubstructure.cxx index 9650d7ea4bb..2c0fa7a6562 100644 --- a/PWGJE/Tasks/jetD0AngSubstructure.cxx +++ b/PWGJE/Tasks/jetD0AngSubstructure.cxx @@ -8,17 +8,23 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// -// \file JetD0AngSubstructure.cxx -// -// \brief Analysis task for the reconstruction and study of charged jets -// containing D_0 mesons in pp collisions. -// \inherited from D0 fragmentation and Ds -// \P. Dhankher + +/// \file jetD0AngSubstructure.cxx +/// +/// \brief Analysis task for the reconstruction and study of charged jets containing D_0 mesons in pp collisions. The code is partially inherited from hfFragmentationFunction.cxx. +/// +/// \author P. Dhankher +/// \author L.J. Huisman #include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetHFUtilities.h" #include "PWGJE/Core/JetUtilities.h" #include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +// +#include "PWGHF/Core/DecayChannels.h" + +#include "Common/Core/RecoDecay.h" #include #include @@ -31,13 +37,16 @@ #include #include +#include #include +#include + #include #include #include #include - +// using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -45,65 +54,279 @@ using namespace o2::framework::expressions; // Definition of a custom AOD table to store jet–D0 quantities namespace o2::aod { -namespace jet_obj -{ +/* // Jet-related quantities -DECLARE_SOA_COLUMN(JetHfDist, jetHfDist, float); -DECLARE_SOA_COLUMN(JetPt, jetPt, float); -DECLARE_SOA_COLUMN(JetEta, jetEta, float); -DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); -DECLARE_SOA_COLUMN(JetNConst, jetNConst, int); -DECLARE_SOA_COLUMN(JetAng, jetAng, float); +*/ + +DECLARE_SOA_COLUMN(ExpJetHfDist, expJetHfDist, float); +DECLARE_SOA_COLUMN(ExpJetPt, expJetPt, float); +DECLARE_SOA_COLUMN(ExpJetEta, expJetEta, float); +DECLARE_SOA_COLUMN(ExpJetPhi, expJetPhi, float); +DECLARE_SOA_COLUMN(ExpJetNConst, expJetNConst, int); +DECLARE_SOA_COLUMN(ExpJetAng, expJetAng, float); // D0 candidate quantities -DECLARE_SOA_COLUMN(HfPt, hfPt, float); -DECLARE_SOA_COLUMN(HfEta, hfEta, float); -DECLARE_SOA_COLUMN(HfPhi, hfPhi, float); -DECLARE_SOA_COLUMN(HfMass, hfMass, float); -DECLARE_SOA_COLUMN(HfY, hfY, float); +DECLARE_SOA_COLUMN(ExpHfZParallel, expHfZParallel, float); + +DECLARE_SOA_COLUMN(ExpHfPt, expHfPt, float); +DECLARE_SOA_COLUMN(ExpHfEta, expHfEta, float); +DECLARE_SOA_COLUMN(ExpHfPhi, expHfPhi, float); +DECLARE_SOA_COLUMN(ExpHfMass, expHfMass, float); +DECLARE_SOA_COLUMN(ExpHfY, expHfY, float); // ML scores -DECLARE_SOA_COLUMN(HfMlScore0, hfMlScore0, float); -DECLARE_SOA_COLUMN(HfMlScore1, hfMlScore1, float); -DECLARE_SOA_COLUMN(HfMlScore2, hfMlScore2, float); -} // namespace jet_obj +DECLARE_SOA_COLUMN(ExpHfMlScore0, expHfMlScore0, float); +DECLARE_SOA_COLUMN(ExpHfMlScore1, expHfMlScore1, float); +DECLARE_SOA_COLUMN(ExpHfMlScore2, expHfMlScore2, float); + +/* +// MCP quantities (Particle Level) +*/ +// Jets +DECLARE_SOA_COLUMN(McpJetHfDist, mcpJetHfDist, float); +DECLARE_SOA_COLUMN(McpJetPt, mcpJetPt, float); +DECLARE_SOA_COLUMN(McpJetEta, mcpJetEta, float); +DECLARE_SOA_COLUMN(McpJetPhi, mcpJetPhi, float); +DECLARE_SOA_COLUMN(McpJetNConst, mcpJetNConst, float); +DECLARE_SOA_COLUMN(McpJetAng, mcpJetAng, float); +// D0 candidates (Heavy Flavour) +DECLARE_SOA_COLUMN(McpHfZParallel, mcpHfZParallel, float); + +DECLARE_SOA_COLUMN(McpHfPt, mcpHfPt, float); +DECLARE_SOA_COLUMN(McpHfEta, mcpHfEta, float); +DECLARE_SOA_COLUMN(McpHfPhi, mcpHfPhi, float); +// DECLARE_SOA_COLUMN(HfMass,mcpHfMass, float); +DECLARE_SOA_COLUMN(McpHfY, mcpHfY, float); +DECLARE_SOA_COLUMN(McpHfPrompt, mcpHfPrompt, bool); +DECLARE_SOA_COLUMN(McpHfMatch, mcpHfMatch, bool); + +/* +// MCD quantities (Detector Level) +*/ +// Jets +DECLARE_SOA_COLUMN(McdJetHfDist, mcdJetHfDist, float); +DECLARE_SOA_COLUMN(McdJetPt, mcdJetPt, float); +DECLARE_SOA_COLUMN(McdJetEta, mcdJetEta, float); +DECLARE_SOA_COLUMN(McdJetPhi, mcdJetPhi, float); +DECLARE_SOA_COLUMN(McdJetNConst, mcdJetNConst, float); +DECLARE_SOA_COLUMN(McdJetAng, mcdJetAng, float); +// D0 candidates (Heavy Flavour) +DECLARE_SOA_COLUMN(McdHfZParallel, mcdHfZParallel, float); + +DECLARE_SOA_COLUMN(McdHfPt, mcdHfPt, float); +DECLARE_SOA_COLUMN(McdHfEta, mcdHfEta, float); +DECLARE_SOA_COLUMN(McdHfPhi, mcdHfPhi, float); +DECLARE_SOA_COLUMN(McdHfMass, mcdHfMass, float); +DECLARE_SOA_COLUMN(McdHfY, mcdHfY, float); +DECLARE_SOA_COLUMN(McdHfPrompt, mcdHfPrompt, bool); +DECLARE_SOA_COLUMN(McdHfMatch, mcdHfMatch, bool); +// Other +DECLARE_SOA_COLUMN(McdHfMatchedFrom, mcdHfMatchedFrom, int); +DECLARE_SOA_COLUMN(McdHfSelectedAs, mcdHfSelectedAs, int); +// ML scores +DECLARE_SOA_COLUMN(McdHfMlScore0, mcdHfMlScore0, float); +DECLARE_SOA_COLUMN(McdHfMlScore1, mcdHfMlScore1, float); +DECLARE_SOA_COLUMN(McdHfMlScore2, mcdHfMlScore2, float); + +/* // AOD table definition -DECLARE_SOA_TABLE(JetObjTable, "AOD", "JETOBJTABLE", - jet_obj::JetHfDist, - jet_obj::JetPt, - jet_obj::JetEta, - jet_obj::JetPhi, - jet_obj::JetNConst, - jet_obj::JetAng, - jet_obj::HfPt, - jet_obj::HfEta, - jet_obj::HfPhi, - jet_obj::HfMass, - jet_obj::HfY, - jet_obj::HfMlScore0, - jet_obj::HfMlScore1, - jet_obj::HfMlScore2); +*/ +DECLARE_SOA_TABLE(EXPJetObjTable, "AOD", "EXPJETOBJTABLE", + ExpJetHfDist, + ExpJetPt, + ExpJetEta, + ExpJetPhi, + ExpJetNConst, + ExpJetAng, + ExpHfZParallel, + ExpHfPt, + ExpHfEta, + ExpHfPhi, + ExpHfMass, + ExpHfY, + ExpHfMlScore0, + ExpHfMlScore1, + ExpHfMlScore2); +DECLARE_SOA_TABLE(MCPJetObjTable, "AOD", "MCPJETOBJTABLE", + McpJetHfDist, + McpJetPt, + McpJetEta, + McpJetPhi, + McpJetNConst, + McpJetAng, + McpHfZParallel, + McpHfPt, + McpHfEta, + McpHfPhi, + McpHfY, + McpHfPrompt, + McpHfMatch); +DECLARE_SOA_TABLE(MCDJetObjTable, "AOD", "MCDJETOBJTABLE", + McdJetHfDist, + McdJetPt, + McdJetEta, + McdJetPhi, + McdJetNConst, + McdJetAng, + McdHfZParallel, + McdHfPt, + McdHfEta, + McdHfPhi, + McdHfMass, + McdHfY, + McdHfPrompt, + McdHfMatch, + McdHfMlScore0, + McdHfMlScore1, + McdHfMlScore2, + McdHfMatchedFrom, + McdHfSelectedAs); +DECLARE_SOA_TABLE(MatchJetDistanceTable, "AOD", "MATCHTABLE", + McpJetHfDist, + McpJetPt, + McpJetEta, + McpJetPhi, + McpJetNConst, + McpJetAng, + McpHfZParallel, + McpHfPt, + McpHfEta, + McpHfPhi, + McpHfY, + McpHfPrompt, + McdJetHfDist, + McdJetPt, + McdJetEta, + McdJetPhi, + McdJetNConst, + McdJetAng, + McdHfZParallel, + McdHfPt, + McdHfEta, + McdHfPhi, + McdHfMass, + McdHfY, + McdHfPrompt, + McdHfMlScore0, + McdHfMlScore1, + McdHfMlScore2, + McdHfMatchedFrom, + McdHfSelectedAs); + } // namespace o2::aod + +// Helps to avoid typos in histogram names when using the "HIST" macro +/* +hf(l): Heavy flavour +ex(p): Experimental data (detector level) +mcd: Monte Carlo data (detector level) +mcp: Monte Carlo data (particle level) +*/ +namespace histnames +{ + +/* +// Experimental Data (analyseDataChargedSubstructure) +*/ +constexpr const char* ExCol = "exCol"; // Collision Counter +constexpr const char* ExJet = "exJet"; // Jet Counter +constexpr const char* ExJetPt = "exJetPt"; // Jet pT +constexpr const char* ExJetEta = "exJetEta"; // Jet eta +constexpr const char* ExJetPhi = "exJetPhi"; // Jet phi +constexpr const char* ExJetAng = "exJetAng"; // Jet angularity +constexpr const char* ExJetProj = "exJetProj"; // Projection of HF candidate momentum on jet axis +constexpr const char* ExJetDist = "exJetDist"; // Jet–HF candidate angular distance +constexpr const char* ExJetDistProj = "exJetDistProj"; // Jet–HF candidate distance vs projection +constexpr const char* ExHflPt = "exHflPt"; // HF candidate pT +constexpr const char* ExHflMass = "exHflMass"; // HF candidate mass +constexpr const char* ExHflEta = "exHflEta"; // HF candidate eta +constexpr const char* ExHflPhi = "exHflPhi"; // HF candidate phi +/* +// Monte Carlo Data Efficiency (analyseMonteCarloEfficiency) +*/ +constexpr const char* McEffCol = "mcEffCol"; // Collision Counter +constexpr const char* McEffJet = "mcEffJet"; // Jet Counter +constexpr const char* McEffDetJetPt = "mcEffDetJetPt"; // Detector level jet pT +constexpr const char* McEffDetJetEta = "mcEffDetJetEta"; // Detector level jet eta +constexpr const char* McEffDetJetPhi = "mcEffDetJetPhi"; // Detector level jet phi +constexpr const char* McEffDetJetAng = "mcEffDetJetAng"; // Detector level jet angularity +constexpr const char* McEffDetHflPt = "mcEffDetHflPt"; // Detector level HF candidate pT +constexpr const char* McEffDetHflMass = "mcEffDetHflMass"; // Detector level HF candidate mass +constexpr const char* McEffDetHflEta = "mcEffDetHflEta"; // Detector level HF candidate eta +constexpr const char* McEffDetHflPhi = "mcEffDetHflPhi"; // Detector level HF candidate phi +constexpr const char* McEffParJetPt = "mcEffParJetPt"; // Particle level jet pT +constexpr const char* McEffParJetEta = "mcEffParJetEta"; // Particle level jet eta +constexpr const char* McEffParJetPhi = "mcEffParJetPhi"; // Particle level jet phi +constexpr const char* McEffParJetAng = "mcEffParJetAng"; // Particle level jet angularity +constexpr const char* McEffParHflPt = "mcEffParHflPt"; // Particle level HF candidate pT +// constexpr const char* McEffpar_hfl_mass = "mcEffpar_hfl_mass"; // Particle level HF candidate mass -> PDG value +constexpr const char* McEffParHflEta = "mcEffParHflEta"; // Particle level HF candidate eta +constexpr const char* McEffParHflPhi = "mcEffParHflPhi"; // Particle level HF candidate phi +/* +// Monte Carlo Data Matching (analyseMonteCarlo) +*/ +constexpr const char* McCol = "mcCol"; // Collision Counter +constexpr const char* McJet = "mcJet"; // Jet Counter +constexpr const char* McDetJetPt = "mcDetJetPt"; // Detector level jet pT +constexpr const char* McDetJetEta = "mcDetJetEta"; // Detector level jet eta +constexpr const char* McDetJetPhi = "mcDetJetPhi"; // Detector level jet phi +constexpr const char* McDetJetAng = "mcDetJetAng"; // Detector level jet angularity +constexpr const char* McDetHflPt = "mcDetHflPt"; // Detector level HF candidate pT (matched) +constexpr const char* McDetHflMass = "mcDetHflMass"; // Detector level HF candidate mass (matched) +constexpr const char* McDetHflEta = "mcDetHflEta"; // Detector level HF candidate eta (matched) +constexpr const char* McDetHflPhi = "mcDetHflPhi"; // Detector level HF candidate phi (matched) +// constexpr const char* McParJet_pt = "mcParJet_pt"; // Particle level jet pT +// constexpr const char* McParJet_eta = "mcParJet_eta"; // Particle level jet eta +// constexpr const char* McParJet_phi = "mcParJet_phi"; // Particle level jet phi +// constexpr const char* McParJet_ang = "mcParJet_ang"; // Particle level jet angularity +// constexpr const char* McParHfl_pt = "mcParHfl_pt"; // Particle level HF candidate pT +// constexpr const char* McParHfl_mass = "mcParHfl_mass"; // Particle level HF candidate mass -> PDG value +// constexpr const char* mc_par_hfl_eta = "mc_par_hfl_eta"; // Particle level HF candidate eta +// constexpr const char* mc_par_hfl_phi = "mc_par_hfl_phi"; // Particle level HF candidate phi + +} // namespace histnames + +consteval float getValFromBin(int bin) +{ + return static_cast(bin) - 0.5f; +} + +enum BinExpColCntr { AllCollisions = 1, + Sel8ZCut = 2 }; + +enum BinExpJetCntr { ChargedJets = 1 }; +enum BinMCColCntr { All = 1, + ZCut = 2, + Matched = 3, + MatchedSel8ZCut = 4 }; + +enum BinMCJetCntr { DetectorLevelJetInMCCollision = 1, + ParticleLevelJetInMCCollision = 2, + DetectorLevelJetWithMatchedCandidate = 3, + ParticleLevelJetWithMatchedCandidate = 4 +}; + struct JetD0AngSubstructure { - /** - * Histogram registry - * - * Contains: - * - Event and track histograms - * - Jet kinematic distributions - * - D0–jet substructure observables - */ - HistogramRegistry registry{"registry", - {{"h_collision_counter", "# of collisions;", {HistType::kTH1F, {{2, 0., 2.}}}}, - {"h_jet_counter", ";# of D^{0} jets;", {HistType::kTH1F, {{6, 0., 3.0}}}}, - {"h_d0_jet_projection", ";z^{D^{0},jet}_{||};dN/dz^{D^{0},jet}_{||}", {HistType::kTH1F, {{1000, 0., 10.}}}}, - {"h_d0_jet_distance_vs_projection", ";#DeltaR_{D^{0},jet};z^{D^{0},jet}_{||}", {HistType::kTH2F, {{1000, 0., 10.}, {1000, 0., 10.}}}}, - {"h_d0_jet_distance", ";#DeltaR_{D^{0},jet};dN/d(#DeltaR)", {HistType::kTH1F, {{1000, 0., 10.}}}}, - {"h_d0_jet_pt", ";p_{T,D^{0} jet};dN/dp_{T,D^{0} jet}", {HistType::kTH1F, {{200, 0., 10.}}}}, - {"h_d0_jet_eta", ";#eta_{T,D^{0} jet};dN/d#eta_{D^{0} jet}", {HistType::kTH1F, {{250, -5., 5.}}}}, - {"h_d0_jet_phi", ";#phi_{T,D^{0} jet};dN/d#phi_{D^{0} jet}", {HistType::kTH1F, {{250, -10., 10.}}}}, - {"h_d0_mass", ";m_{D^{0}});dN/dm_{D^{0}}", {HistType::kTH1F, {{1000, 0., 10.}}}}, - {"h_d0_eta", ";#eta_{D^{0}});dN/d#eta_{D_{}}", {HistType::kTH1F, {{250, -5., 5.}}}}, - {"h_d0_phi", ";#phi_{D^{0}});dN/d#phi_{D^{0}}", {HistType::kTH1F, {{250, -10., 10.}}}}, - {"h_d0_ang", ";#lambda_{#kappa}^{#alpha};counts", {HistType::kTH1F, {{100, 0., 1.}}}}}}; + + // Output table producer + Produces objJetTable; + Produces mcdJetTable; + Produces mcpJetTable; + Produces matchJetTable; + // + using JetChargedTableD0 = soa::Join; + // MC Matching Tables + using JetD0MCDTable = soa::Join; + using JetD0MCPTable = soa::Join; + // Type Aliases for the constituent jets in the table (to be used for + // angularity calculation) + using JetD0MCDTableConstituent = JetD0MCDTable::iterator; + using JetD0MCPTableConstituent = JetD0MCPTable::iterator; + // Slices for access to proper HF MCD jet collision that is associated to + // MCCollision + PresliceUnsorted collisionsPerMCCollisionPreslice = aod::jmccollisionlb::mcCollisionId; + Preslice d0MCDJetsPerEXPCollisionPreslice = aod::jet::collisionId; + Preslice d0MCPJetsPerMCCollisionPreslice = aod::jet::mcCollisionId; + + HistogramRegistry registry{"registry", {}}; // Configurables Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; @@ -119,83 +342,605 @@ struct JetD0AngSubstructure { std::vector eventSelectionBits; int trackSelection = -1; - // Output table producer - Produces ObjJetTable; - - float angularity; - void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + addHistograms(); } + void addHistograms() + { + /* + // Experimental Data Histograms + */ + // Definitions + registry.add(histnames::ExCol, "N_{coll};", {HistType::kTH1F, {{2, 0., 2.}}}); + registry.add(histnames::ExJet, "N_{jet};", {HistType::kTH1F, {{1, 0., 1.0}}}); + registry.add(histnames::ExJetPt, ";p_{T,jet};dN/dp_{T,jet}", {HistType::kTH1F, {{1000, 0., 50.}}}); + registry.add(histnames::ExJetEta, ";#eta_{jet};dN/d#eta_{jet}", {HistType::kTH1F, {{250, -5., 5.}}}); + registry.add(histnames::ExJetPhi, ";#phi_{jet};dN/d#phi_{jet}", {HistType::kTH1F, {{250, -10., 10.}}}); + registry.add(histnames::ExJetAng, ";angularity_{jet};dN/d(angularity)", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::ExJetDist, ";#DeltaR_{D^{0},jet};dN/d(#DeltaR)", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::ExJetDistProj, ";#DeltaR_{D^{0},jet};z^{D^{0},jet}_{||}", {HistType::kTH2F, {{1000, 0., 10.}, {1000, 0., 10.}}}); + registry.add(histnames::ExJetProj, ";z^{D^{0},jet}_{||};dN/dz^{D^{0},jet}_{||}", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::ExHflPt, ";p_{T,D^{0}};dN/dp_{T,D^{0}}", {HistType::kTH1F, {{200, 0., 10.}}}); + registry.add(histnames::ExHflMass, ";m_{D^{0}};dN/dm_{D^{0}}", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::ExHflEta, ";#eta_{D^{0}};dN/d#eta_{D^{0}}", {HistType::kTH1F, {{250, -5., 5.}}}); + registry.add(histnames::ExHflPhi, ";#phi_{D^{0}};dN/d#phi_{D^{0}}", {HistType::kTH1F, {{250, -10., 10.}}}); + // Labels + auto expCollisionCounter = registry.get(HIST(histnames::ExCol)); + expCollisionCounter->GetXaxis()->SetBinLabel(BinExpColCntr::AllCollisions, "all"); + expCollisionCounter->GetXaxis()->SetBinLabel(BinExpColCntr::Sel8ZCut, "sel8 + zcut"); + + auto expJetCounter = registry.get(HIST(histnames::ExJet)); + expJetCounter->GetXaxis()->SetBinLabel(BinExpJetCntr::ChargedJets, "Charged jets with D0"); + /* + // Monte Carlo Data Efficiency Histograms + */ + // Definitions + registry.add(histnames::McEffCol, "N_{coll};", {HistType::kTH1F, {{4, 0., 4.0}}}); + registry.add(histnames::McEffJet, "N_{jet};", {HistType::kTH1F, {{4, 0., 4.0}}}); + registry.add(histnames::McEffDetJetPt, ";p_{T,det jet};dN/dp_{T,det jet}", {HistType::kTH1F, {{1000, 0., 50.}}}); + registry.add(histnames::McEffDetJetEta, ";#eta_{det jet};dN/d#eta_{det jet}", {HistType::kTH1F, {{250, -5., 5.}}}); + registry.add(histnames::McEffDetJetPhi, ";#phi_{det jet};dN/d#phi_{det jet}", {HistType::kTH1F, {{250, -10., 10.}}}); + registry.add(histnames::McEffDetJetAng, ";angularity_{det jet};dN/d(angularity)", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::McEffDetHflPt, ";p_{T,det D^{0}};dN/dp_{T,det D^{0}}", {HistType::kTH1F, {{200, 0., 10.}}}); + registry.add(histnames::McEffDetHflMass, ";m_{det D^{0}};dN/dm_{det D^{0}}", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::McEffDetHflEta, ";#eta_{det D^{0}};dN/d#eta_{det D^{0}}", {HistType::kTH1F, {{250, -5., 5.}}}); + registry.add(histnames::McEffDetHflPhi, ";#phi_{det D^{0}};dN/d#phi_{det D^{0}}", {HistType::kTH1F, {{250, -10., 10.}}}); + // Labels + auto mcCollisionCounter = registry.get(HIST(histnames::McEffCol)); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::All, "mccollisions"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::ZCut, "z_cut"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::Matched, "collisions"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::MatchedSel8ZCut, "sel8"); + + auto jetCounter = registry.get(HIST(histnames::McEffJet)); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::ParticleLevelJetInMCCollision, "particle level"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::DetectorLevelJetInMCCollision, "detector level"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::DetectorLevelJetWithMatchedCandidate, "particle matched jets"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate, "detector matched jets"); + /* + // Monte Carlo Data Histograms + */ + registry.add(histnames::McCol, "N_{coll};", {HistType::kTH1F, {{4, 0., 4.0}}}); + registry.add(histnames::McJet, "N_{jet};", {HistType::kTH1F, {{4, 0., 4.0}}}); + registry.add(histnames::McDetJetPt, ";p_{T,det jet};dN/dp_{T,det jet}", {HistType::kTH1F, {{1000, 0., 50.}}}); + registry.add(histnames::McDetJetEta, ";#eta_{det jet};dN/d#eta_{det jet}", {HistType::kTH1F, {{250, -5., 5.}}}); + registry.add(histnames::McDetJetPhi, ";#phi_{det jet};dN/d#phi_{det jet}", {HistType::kTH1F, {{250, -10., 10.}}}); + registry.add(histnames::McDetJetAng, ";angularity_{det jet};dN/d(angularity)", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::McDetHflPt, ";p_{T,det D^{0}};dN/dp_{T,det D^{0}}", {HistType::kTH1F, {{200, 0., 10.}}}); + registry.add(histnames::McDetHflMass, ";m_{det D^{0}};dN/dm_{det D^{0}}", {HistType::kTH1F, {{1000, 0., 10.}}}); + registry.add(histnames::McDetHflEta, ";#eta_{det D^{0}};dN/d#eta_{det D^{0}}", {HistType::kTH1F, {{250, -5., 5.}}}); + registry.add(histnames::McDetHflPhi, ";#phi_{det D^{0}};dN/d#phi_{det D^{0}}", {HistType::kTH1F, {{250, -10., 10.}}}); + // Labels + auto mcCollisionCounter2 = registry.get(HIST(histnames::McCol)); + mcCollisionCounter2->GetXaxis()->SetBinLabel(BinMCColCntr::All, "mccollisions"); + mcCollisionCounter2->GetXaxis()->SetBinLabel(BinMCColCntr::ZCut, "z_cut"); + mcCollisionCounter2->GetXaxis()->SetBinLabel(BinMCColCntr::Matched, "collisions"); + mcCollisionCounter2->GetXaxis()->SetBinLabel(BinMCColCntr::MatchedSel8ZCut, "sel8"); + + auto jetCounter2 = registry.get(HIST(histnames::McJet)); + jetCounter2->GetXaxis()->SetBinLabel(BinMCJetCntr::DetectorLevelJetInMCCollision, "detector level"); + jetCounter2->GetXaxis()->SetBinLabel(BinMCJetCntr::ParticleLevelJetInMCCollision, "particle level"); + jetCounter2->GetXaxis()->SetBinLabel(BinMCJetCntr::DetectorLevelJetWithMatchedCandidate, "particle matched jets"); + jetCounter2->GetXaxis()->SetBinLabel(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate, "detector matched jets"); + }; + template - void jetCalculateAngularity(T const& jet, U const& /*tracks*/) + float jetCalculateAngularityEXP(T const& jet, U const& /*tracks*/) { - angularity = 0.0; - for (auto& constituent : jet.template tracks_as()) { - angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); + float tAngularity = 0.0; + for (const auto& constituent : jet.template tracks_as()) { + tAngularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); } - angularity /= std::pow(jet.pt(), kappa); + tAngularity /= std::pow(jet.pt(), kappa); + return tAngularity; } - void processDataChargedSubstructure(aod::JetCollision const& collision, - soa::Join const& jets, - aod::CandidatesD0Data const&, aod::JetTracks const& tracks) + template + float jetCalculateAngularityMCD(JetTableMCDConstituent const& jet, aod::JetTracks const& tracks) { - // apply event selection and fill histograms for sanity check - registry.fill(HIST("h_collision_counter"), 0.5); + float a = 0.f; + for (const auto& id : jet.tracksIds()) { + const auto trk = tracks.iteratorAt(id); + a += std::pow(trk.pt(), kappa) * std::pow(jetutilities::deltaR(jet, trk) / (jet.r() / 100.f), alpha); + } + return a / std::pow(jet.pt(), kappa); + } + + template + float jetCalculateAngularityMCP(JetTableMCPConstituent const& jet, aod::JetParticles const& particles) + { + float a = 0.f; + for (const auto& id : jet.tracksIds()) { + const auto p = particles.iteratorAt(id); + a += std::pow(p.pt(), kappa) * std::pow(jetutilities::deltaR(jet, p) / (jet.r() / 100.f), alpha); + } + return a / std::pow(jet.pt(), kappa); + } + template + void analyseDataChargedSubstructure(aod::JetCollision const& collision, + JetChargedTable const& jets, + CandidatesTable const& /*candidates*/, + aod::JetTracks const& tracks) + { + // apply event selection and fill histograms for sanity check + registry.fill(HIST(histnames::ExCol), getValFromBin(BinExpColCntr::AllCollisions)); if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { return; } - registry.fill(HIST("h_collision_counter"), 1.5); + registry.fill(HIST(histnames::ExCol), getValFromBin(BinExpColCntr::Sel8ZCut)); // Loop over jets containing D0 candidates for (const auto& jet : jets) { // number of charged jets with D0 - registry.fill(HIST("h_jet_counter"), 0.5); + registry.fill(HIST(histnames::ExJet), getValFromBin(BinExpJetCntr::ChargedJets)); // obtaining jet 3-vector TVector3 jetVector(jet.px(), jet.py(), jet.pz()); // Loop over D0 candidates associated to the jet - for (const auto& d0Candidate : jet.candidates_as()) { + for (const auto& d0Candidate : jet.template candidates_as()) { // obtaining jet 3-vector TVector3 d0Vector(d0Candidate.px(), d0Candidate.py(), d0Candidate.pz()); - // calculating fraction of the jet momentum carried by the D0 along the direction of the jet axis + // calculating fraction of the jet momentum carried by the D0 along the + // direction of the jet axis double zParallel = (jetVector * d0Vector) / (jetVector * jetVector); // calculating angular distance in eta-phi plane double axisDistance = jetutilities::deltaR(jet, d0Candidate); - jetCalculateAngularity(jet, tracks); + float angularity = jetCalculateAngularityEXP(jet, tracks); // filling histograms - registry.fill(HIST("h_d0_jet_projection"), zParallel); - registry.fill(HIST("h_d0_jet_distance_vs_projection"), axisDistance, zParallel); - registry.fill(HIST("h_d0_jet_distance"), axisDistance); - registry.fill(HIST("h_d0_jet_pt"), jet.pt()); - registry.fill(HIST("h_d0_jet_eta"), jet.eta()); - registry.fill(HIST("h_d0_jet_phi"), jet.phi()); - registry.fill(HIST("h_d0_mass"), d0Candidate.m()); - registry.fill(HIST("h_d0_eta"), d0Candidate.eta()); - registry.fill(HIST("h_d0_phi"), d0Candidate.phi()); - registry.fill(HIST("h_d0_ang"), angularity); // add more axis + registry.fill(HIST(histnames::ExJetProj), zParallel); + registry.fill(HIST(histnames::ExJetDistProj), axisDistance, zParallel); + registry.fill(HIST(histnames::ExJetDist), axisDistance); + registry.fill(HIST(histnames::ExJetPt), jet.pt()); + registry.fill(HIST(histnames::ExJetEta), jet.eta()); + registry.fill(HIST(histnames::ExJetPhi), jet.phi()); + registry.fill(HIST(histnames::ExJetAng), angularity); + registry.fill(HIST(histnames::ExHflPt), d0Candidate.pt()); + registry.fill(HIST(histnames::ExHflMass), d0Candidate.m()); + registry.fill(HIST(histnames::ExHflEta), d0Candidate.eta()); + registry.fill(HIST(histnames::ExHflPhi), d0Candidate.phi()); // add more axis // filling table - ObjJetTable(axisDistance, - jet.pt(), jet.eta(), jet.phi(), jet.tracks_as().size(), angularity, - d0Candidate.pt(), d0Candidate.eta(), d0Candidate.phi(), d0Candidate.m(), d0Candidate.y(), d0Candidate.mlScores()[0], d0Candidate.mlScores()[1], d0Candidate.mlScores()[2]); + objJetTable(axisDistance, + jet.pt(), + jet.eta(), + jet.phi(), + jet.template tracks_as().size(), + angularity, + zParallel, + d0Candidate.pt(), + d0Candidate.eta(), + d0Candidate.phi(), + d0Candidate.m(), + d0Candidate.y(), + d0Candidate.mlScores()[0], + d0Candidate.mlScores()[1], + d0Candidate.mlScores()[2]); - break; // get out of candidates' loop after first HF particle is found in jet + break; // get out of candidates' loop after first HF particle is found + // in jet } // end of D0 candidates loop } // end of jets loop } // end of process function - PROCESS_SWITCH(JetD0AngSubstructure, processDataChargedSubstructure, "charged HF jet substructure", false); + + template + void analyseMonteCarloEfficiency(MCDJetsPerMCCollissionPreslice const& jetmcdpreslice, + MCPJetsPerMCCollissionPreslice const& jetmcppreslice, + aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + JetTableMCD const& mcdjets, + JetTableMCP const& mcpjets, + CandidatesMCD const& /*mcdCandidates*/, + CandidatesMCP const& /*mcpCandidates*/, + aod::JetTracks const& tracks, + aod::JetParticles const& particles) + { + for (const auto& mccollision : mccollisions) { + + registry.fill(HIST(histnames::McEffCol), getValFromBin(BinMCColCntr::All)); + // skip collisions outside of |z| < vertexZCut + if (std::abs(mccollision.posZ()) > vertexZCut) { + continue; + } + registry.fill(HIST(histnames::McEffCol), getValFromBin(BinMCColCntr::ZCut)); + + // reconstructed collisions associated to same mccollision + const auto collisionsPerMCCollision = collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); + for (const auto& collision : collisionsPerMCCollision) { + + registry.fill(HIST(histnames::McEffCol), getValFromBin(BinMCColCntr::Matched)); + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || + !(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST(histnames::McEffCol), getValFromBin(BinMCColCntr::MatchedSel8ZCut)); + + // d0 detector level jets associated to the current same collision + const auto d0mcdJetsPerCollision = mcdjets.sliceBy(jetmcdpreslice, collision.globalIndex()); + for (const auto& mcdjet : d0mcdJetsPerCollision) { + + registry.fill( + HIST(histnames::McEffJet), getValFromBin(BinMCJetCntr::DetectorLevelJetInMCCollision)); + + // obtain leading HF candidate in jet + auto mcdd0cand = mcdjet.template candidates_first_as(); + + if (mcdjet.has_matchedJetCand()) { + registry.fill( + HIST(histnames::McEffJet), getValFromBin(BinMCJetCntr::DetectorLevelJetWithMatchedCandidate)); + } + + // reflection information for storage: D0 = +1, D0bar = -1, neither = + // 0 + int matchedFrom = 0; + int decayChannel = o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK; + int selectedAs = 0; + + if (mcdd0cand.flagMcMatchRec() == decayChannel) { // matched to D0 on truth level + matchedFrom = 1; + } else if (mcdd0cand.flagMcMatchRec() == -decayChannel) { // matched to D0bar on truth level + matchedFrom = -1; + } + // bitwise AND operation: Checks whether BIT(i) is set, regardless of + // other bits + if (mcdd0cand.candidateSelFlag() & BIT(0)) { // CandidateSelFlag == BIT(0) -> selected as D0 + selectedAs = 1; + } else if (mcdd0cand.candidateSelFlag() & BIT(1)) { // CandidateSelFlag == BIT(1) -> selected as D0bar + selectedAs = -1; + } + + TVector3 mcdjetvector(mcdjet.px(), mcdjet.py(), mcdjet.pz()); + TVector3 mcdcandvector(mcdd0cand.px(), mcdd0cand.py(), mcdd0cand.pz()); + + float mcdzparallel = (mcdjetvector * mcdcandvector) / (mcdjetvector * mcdjetvector); + + float angularity = jetCalculateAngularityMCD(mcdjet, tracks); + registry.fill(HIST(histnames::McEffDetJetPt), mcdjet.pt()); + registry.fill(HIST(histnames::McEffDetJetEta), mcdjet.eta()); + registry.fill(HIST(histnames::McEffDetJetPhi), mcdjet.phi()); + registry.fill(HIST(histnames::McEffDetJetAng), angularity); + // Particle Histgrams + registry.fill(HIST(histnames::McEffDetHflPt), mcdd0cand.pt()); + registry.fill(HIST(histnames::McEffDetHflMass), mcdd0cand.m()); + registry.fill(HIST(histnames::McEffDetHflEta), mcdd0cand.eta()); + registry.fill(HIST(histnames::McEffDetHflPhi), mcdd0cand.phi()); + + mcdJetTable( + jetutilities::deltaR(mcdjet, mcdd0cand), + mcdjet.pt(), + mcdjet.eta(), + mcdjet.phi(), + mcdjet.template tracks_as().size(), // detector level jet + angularity, + mcdzparallel, + mcdd0cand.pt(), + mcdd0cand.eta(), mcdd0cand.phi(), + mcdd0cand.m(), mcdd0cand.y(), + (mcdd0cand.originMcRec() == RecoDecay::OriginType::Prompt), // detector level D0 candidate + mcdjet.has_matchedJetCand(), + mcdd0cand.mlScores()[0], + mcdd0cand.mlScores()[1], + mcdd0cand.mlScores()[2], // // Machine Learning PID scores: background, prompt, non-prompt + + matchedFrom, + selectedAs); // D0 = +1, D0bar = -1, neither = 0 + } + } // end of reconstructed collisions loop (detector level mc collisions + // matching with real collisions) + + // d0 particle level jets associated to same mccollision + const auto d0mcpJetsPerMCCollision = mcpjets.sliceBy(jetmcppreslice, mccollision.globalIndex()); + for (const auto& mcpjet : d0mcpJetsPerMCCollision) { + + registry.fill(HIST(histnames::McEffJet), getValFromBin(BinMCJetCntr::ParticleLevelJetInMCCollision)); + + // obtain leading HF particle in jet + auto mcpd0cand = mcpjet.template candidates_first_as(); + + if (mcpjet.has_matchedJetCand()) { + registry.fill(HIST(histnames::McEffJet), getValFromBin(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate)); + } + TVector3 mcpjetvector(mcpjet.px(), mcpjet.py(), mcpjet.pz()); + TVector3 mcpcandvector(mcpd0cand.px(), mcpd0cand.py(), mcpd0cand.pz()); + + float mcpzparallel = (mcpjetvector * mcpcandvector) / (mcpjetvector * mcpjetvector); + + float angularity = jetCalculateAngularityMCP(mcpjet, particles); + + registry.fill(HIST(histnames::McEffParJetPt), mcpjet.pt()); + registry.fill(HIST(histnames::McEffParJetEta), mcpjet.eta()); + registry.fill(HIST(histnames::McEffParJetPhi), mcpjet.phi()); + registry.fill(HIST(histnames::McEffParJetAng), angularity); + // Particle Histgrams + registry.fill(HIST(histnames::McEffParHflPt), mcpd0cand.pt()); + + registry.fill(HIST(histnames::McEffParHflEta), mcpd0cand.eta()); + registry.fill(HIST(histnames::McEffParHflPhi), mcpd0cand.phi()); + // store data in MC detector level table (calculate angular distance in + // eta-phi plane on the fly) + mcpJetTable(jetutilities::deltaR(mcpjet, mcpd0cand), + mcpjet.pt(), + mcpjet.eta(), + mcpjet.phi(), + mcpjet.template tracks_as().size(), // particle level jet + angularity, + mcpzparallel, + mcpd0cand.pt(), + mcpd0cand.eta(), + mcpd0cand.phi(), + // mcpd0cand.m(), + mcpd0cand.y(), + (mcpd0cand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level D0 + mcpjet.has_matchedJetCand()); + } // End of particle level jets loop, related to detector level collisions + // and jets. + } + } + + template + void analyseMonteCarlo(const MCPJetsPerMCCollissionPreslice& jetmcpreslice, + aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + JetTableMCD const& /*mcdjets*/, + JetTableMCP const& mcpjets, + CandidatesMCD const& /*mcdCandidates*/, + CandidatesMCP const& /*mcpCandidates*/, + aod::JetTracks const& jettracks, + aod::JetParticles const& jetparticles) + { + for (const auto& mccollision : mccollisions) { + registry.fill(HIST(histnames::McCol), getValFromBin(BinMCColCntr::All)); + // skip collisions outside of |z| < vertexZCut + if (std::abs(mccollision.posZ()) > vertexZCut) { + continue; + } + registry.fill(HIST(histnames::McCol), getValFromBin(BinMCColCntr::ZCut)); + + // hf particle level jets associated to same mccollision + const JetD0MCPTable mcpJetsPerMCCollision = mcpjets.sliceBy(jetmcpreslice, mccollision.globalIndex()); + for (const auto& mcpjet : mcpJetsPerMCCollision) { + + registry.fill(HIST(histnames::McJet), getValFromBin(BinMCJetCntr::ParticleLevelJetInMCCollision)); + + // obtain leading HF particle in jet + auto mcpcand = mcpjet.template candidates_first_as(); + + TVector3 mcpjetvector(mcpjet.px(), mcpjet.py(), mcpjet.pz()); + TVector3 mcpcandvector(mcpcand.px(), mcpcand.py(), mcpcand.pz()); + float mcpzparallel = (mcpjetvector * mcpcandvector) / (mcpjetvector * mcpjetvector); + + if (mcpjet.has_matchedJetCand()) { + registry.fill(HIST(histnames::McJet), getValFromBin(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate)); + + // loop over detector level matched to current particle level + for (const auto& mcdjet : mcpjet.template matchedJetCand_as()) { + registry.fill(HIST(histnames::McJet), getValFromBin(BinMCJetCntr::DetectorLevelJetWithMatchedCandidate)); + + // apply collision sel8 selection on detector level jet's collision + const auto& collision = collisions.iteratorAt(mcdjet.collisionId()); + registry.fill(HIST(histnames::McCol), getValFromBin(BinMCColCntr::Matched)); + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST(histnames::McCol), getValFromBin(BinMCColCntr::MatchedSel8ZCut)); + + // obtain leading HF candidate in jet + auto mcdcand = mcdjet.template candidates_first_as(); + + // reflection information for storage: HF = +1, HFbar = -1, neither + // = 0 + int matchedFrom = 0; + int decayChannel = 0; + if (jethfutilities::isD0Table()) { + decayChannel = o2::hf_decay::hf_cand_2prong::DecayChannelMain::D0ToPiK; + } else if (jethfutilities::isLcTable()) { + decayChannel = o2::hf_decay::hf_cand_3prong::DecayChannelMain::LcToPKPi; + } + int selectedAs = 0; + + if (mcdcand.flagMcMatchRec() == decayChannel) { // matched to HF on truth level + matchedFrom = 1; + } else if (mcdcand.flagMcMatchRec() == -decayChannel) { // matched to HFbar on truth level + matchedFrom = -1; + } + // bitwise AND operation: Checks whether BIT(i) is set, regardless + // of other bits + if (mcdcand.candidateSelFlag() & BIT(0)) { // CandidateSelFlag == BIT(0) -> selected as HF + selectedAs = 1; + } else if (mcdcand.candidateSelFlag() & BIT(1)) { // CandidateSelFlag == BIT(1) -> selected as + // HFbar + selectedAs = -1; + } + + TVector3 mcdjetvector(mcdjet.px(), mcdjet.py(), mcdjet.pz()); + TVector3 mcdcandvector(mcdcand.px(), mcdcand.py(), mcdcand.pz()); + + float mcdzparallel = (mcdjetvector * mcdcandvector) / (mcdjetvector * mcdjetvector); + + float mcpAngularity = jetCalculateAngularityMCP(mcpjet, jetparticles); + float mcdAngularity = jetCalculateAngularityMCD(mcdjet, jettracks); + + // filling histograms + // registry.fill(HIST(histnames::h_exp_d0_jet_projection), + // zParallel); + // registry.fill(HIST(histnames::h_exp_d0_jet_distance_vs_projection), + // axisDistance, zParallel); + // registry.fill(HIST(histnames::h_exp_d0_jet_distance), + // axisDistance); registry.fill(HIST(histnames::h_exp_d0_jet_pt), + // jet.pt()); registry.fill(HIST(histnames::h_exp_d0_jet_eta), + // jet.eta()); registry.fill(HIST(histnames::h_exp_d0_jet_phi), + // jet.phi()); registry.fill(HIST(histnames::h_exp_d0_jet_ang), + // angularity); Jet Histograms + + registry.fill(HIST(histnames::McDetJetPt), mcdjet.pt()); + registry.fill(HIST(histnames::McDetJetEta), mcdjet.eta()); + registry.fill(HIST(histnames::McDetJetPhi), mcdjet.phi()); + registry.fill(HIST(histnames::McDetJetAng), mcdAngularity); + // Particle Histgrams + registry.fill(HIST(histnames::McDetHflPt), mcdcand.pt()); + registry.fill(HIST(histnames::McDetHflMass), mcdcand.m()); + registry.fill(HIST(histnames::McDetHflEta), mcdcand.eta()); + registry.fill(HIST(histnames::McDetHflPhi), mcdcand.phi()); // add more axis + + // store matched particle and detector level data in one single + // table (calculate angular distance in eta-phi plane on the fly) + matchJetTable(jetutilities::deltaR(mcpjet, mcpcand), + mcpjet.pt(), + mcpjet.eta(), + mcpjet.phi(), + mcpjet.template tracks_as().size(), // particle level jet + mcpAngularity, + mcpzparallel, + mcpcand.pt(), + mcpcand.eta(), + mcpcand.phi(), + mcpcand.y(), + (mcpcand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level HF + jetutilities::deltaR(mcdjet, mcdcand), + mcdjet.pt(), + mcdjet.eta(), + mcdjet.phi(), + mcdjet.template tracks_as().size(), // detector level jet + mcdAngularity, + mcdzparallel, + mcdcand.pt(), + mcdcand.eta(), + mcdcand.phi(), + mcdcand.m(), + mcdcand.y(), + (mcdcand.originMcRec() == RecoDecay::OriginType::Prompt), // detector level HF + mcdcand.mlScores()[0], + mcdcand.mlScores()[1], + mcdcand.mlScores()[2], // Machine Learning PID scores: background, prompt, non-prompt + matchedFrom, + selectedAs); // HF = +1, HFbar = -1, neither = 0 + } + } else { + // store matched particle and detector level data in one single table + // (calculate angular distance in eta-phi plane on the fly) + float mcpAngularity = jetCalculateAngularityMCP(mcpjet, jetparticles); + // float mcpAngularity = 0.; + matchJetTable(jetutilities::deltaR(mcpjet, mcpcand), + mcpjet.pt(), + mcpjet.eta(), + mcpjet.phi(), + mcpjet.template tracks_as().size(), // particle level jet + mcpAngularity, + mcpzparallel, + mcpcand.pt(), + mcpcand.eta(), + mcpcand.phi(), + mcpcand.y(), + (mcpcand.originMcGen() == RecoDecay::OriginType::Prompt), // particle level HF + -2, + -2, + -2, + -2, + -2, + -2, // detector level jet + -2, + -2, + -2, + -2, + -2, + -2, + -2, // detector level HF + -2, + -2, + -2, // Machine Learning PID scores: background, prompt, non-prompt + -2, + -2); // HF = +1, HFbar = -1, neither = 0 + } + } // end of mcpjets loop + } // end of mccollisions loop + }; + + void processataChargedSubstructureD0(aod::JetCollision const& collision, + JetChargedTableD0 const& jets, + aod::CandidatesD0Data const& candidates, + aod::JetTracks const& tracks) + { + analyseDataChargedSubstructure(collision, jets, candidates, tracks); + } + + PROCESS_SWITCH(JetD0AngSubstructure, processataChargedSubstructureD0, "charged HF jet substructure", false); + + void processMonteCarloEfficiencyD0(aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + JetD0MCDTable const& mcdjets, + JetD0MCPTable const& mcpjets, + aod::CandidatesD0MCD const& mcdCandidates, + aod::CandidatesD0MCP const& mcpCandidates, + aod::JetTracks const& jettracks, + aod::JetParticles const& jetparticles) + { + analyseMonteCarloEfficiency, + Preslice, + JetD0MCDTable, + JetD0MCPTable, + aod::CandidatesD0MCD, + aod::CandidatesD0MCP>(d0MCDJetsPerEXPCollisionPreslice, + d0MCPJetsPerMCCollisionPreslice, + mccollisions, + collisions, + mcdjets, + mcpjets, + mcdCandidates, + mcpCandidates, + jettracks, + jetparticles); + } + + PROCESS_SWITCH(JetD0AngSubstructure, processMonteCarloEfficiencyD0, "non-matched and matched MC D0 and jets", false); + + void processMonteCarloD0(aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + JetD0MCDTable const& mcdjets, + JetD0MCPTable const& mcpjets, + aod::CandidatesD0MCD const& mcdCandidates, + aod::CandidatesD0MCP const& mcpCandidates, + aod::JetTracks const& jettracks, + aod::JetParticles const& jetparticles) + { + analyseMonteCarlo, + JetD0MCDTable, + JetD0MCPTable, + aod::CandidatesD0MCD, + aod::CandidatesD0MCP>(d0MCPJetsPerMCCollisionPreslice, + mccollisions, + collisions, + mcdjets, + mcpjets, + mcdCandidates, + mcpCandidates, + jettracks, + jetparticles); + } + + PROCESS_SWITCH(JetD0AngSubstructure, processMonteCarloD0, "Store all simulated D0 jets information with matched candidate (if any found)", false); }; // Workflow definition -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"jet-d0-ang-substructure"})}; } +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/jetDebug.cxx b/PWGJE/Tasks/jetDebug.cxx index 53b440e8ea8..8d220b5665b 100644 --- a/PWGJE/Tasks/jetDebug.cxx +++ b/PWGJE/Tasks/jetDebug.cxx @@ -107,8 +107,8 @@ struct JetDebugTask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } void processCollisionSelection(aod::JetCollisions const& collisions, soa::Join const& originalCollisions) @@ -147,7 +147,7 @@ struct JetDebugTask { if (jetderiveddatautilities::selectCollision(collision, eventSelection, false, false)) { registry.fill(HIST("h_collisions"), 2.0); } - for (auto track : tracks) { + for (const auto& track : tracks) { registry.fill(HIST("h_track_pt"), track.pt()); registry.fill(HIST("h_track_phi"), track.phi()); registry.fill(HIST("h_track_eta"), track.eta()); @@ -218,7 +218,7 @@ struct JetDebugTask { if (jetderiveddatautilities::selectCollision(collision, eventSelection, false, false)) { registry.fill(HIST("h_collisions"), 2.0); } - for (auto track : tracks) { + for (const auto& track : tracks) { registry.fill(HIST("h_track_pt"), track.pt()); registry.fill(HIST("h_track_phi"), track.phi()); registry.fill(HIST("h_track_eta"), track.eta()); @@ -299,7 +299,7 @@ struct JetDebugTask { void processMCPCharged(aod::JMcCollision const&, soa::Join const& jets, aod::JMcParticles const& tracks) { - for (auto track : tracks) { + for (const auto& track : tracks) { registry.fill(HIST("h_particle_pt"), track.pt()); registry.fill(HIST("h_particle_phi"), track.phi()); registry.fill(HIST("h_particle_eta"), track.eta()); diff --git a/PWGJE/Tasks/jetDsSpecSubs.cxx b/PWGJE/Tasks/jetDsSpecSubs.cxx new file mode 100644 index 00000000000..96da12c3ad6 --- /dev/null +++ b/PWGJE/Tasks/jetDsSpecSubs.cxx @@ -0,0 +1,1153 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file jetDsSpecSubs.cxx +/// \brief Ds-tagged jet analysis with substructure histogram outputs +/// \author Monalisa Melo , Universidade de São Paulo + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +consteval float getValFromBin(int bin) +{ + return static_cast(bin) - 0.5f; +} + +enum BinExpColCntr { AllCollisions = 1, + Sel8ZCut = 2 }; + +enum BinMCColCntr { + AllMCCollisions = 1, + EventSelection = 2, + VertexZCut = 3, + HasMCPDsJet = 4 +}; + +enum BinMCJetCntr { + MCPJets = 1, + MCPJetsWithMCDMatch = 2, + MCDJets = 3, + MCDJetsWithMCPMatch = 4, + MatchedPairs = 5 +}; + +//============================================================== +// AOD tables for Ds-tagged jet observables +//============================================================== +namespace o2::aod +{ +namespace jet_ds_output +{ +// Jet observables +DECLARE_SOA_COLUMN(JetPt, jetPt, float); +DECLARE_SOA_COLUMN(JetEta, jetEta, float); +DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); +DECLARE_SOA_COLUMN(JetNConst, jetNConst, int); +// Ds observables +DECLARE_SOA_COLUMN(DsPt, dsPt, float); +DECLARE_SOA_COLUMN(DsEta, dsEta, float); +DECLARE_SOA_COLUMN(DsPhi, dsPhi, float); +DECLARE_SOA_COLUMN(DsMass, dsMass, float); +// Ds-jet observables +DECLARE_SOA_COLUMN(ZParallel, zParallel, float); +DECLARE_SOA_COLUMN(DeltaR, deltaR, float); +// MC information +DECLARE_SOA_COLUMN(DsOrigin, dsOrigin, int); +DECLARE_SOA_COLUMN(IsMatched, isMatched, bool); +// Matched MCP <-> MCD observables +// Particle level +DECLARE_SOA_COLUMN(McpJetPt, mcpJetPt, float); +DECLARE_SOA_COLUMN(McpJetEta, mcpJetEta, float); +DECLARE_SOA_COLUMN(McpJetPhi, mcpJetPhi, float); +DECLARE_SOA_COLUMN(McpJetNConst, mcpJetNConst, int); +DECLARE_SOA_COLUMN(McpDsPt, mcpDsPt, float); +DECLARE_SOA_COLUMN(McpDsEta, mcpDsEta, float); +DECLARE_SOA_COLUMN(McpDsPhi, mcpDsPhi, float); +DECLARE_SOA_COLUMN(McpZParallel, mcpZParallel, float); +DECLARE_SOA_COLUMN(McpDeltaR, mcpDeltaR, float); + +DECLARE_SOA_COLUMN(McdJetPt, mcdJetPt, float); +DECLARE_SOA_COLUMN(McdJetEta, mcdJetEta, float); +DECLARE_SOA_COLUMN(McdJetPhi, mcdJetPhi, float); +DECLARE_SOA_COLUMN(McdJetNConst, mcdJetNConst, int); +DECLARE_SOA_COLUMN(McdDsPt, mcdDsPt, float); +DECLARE_SOA_COLUMN(McdDsEta, mcdDsEta, float); +DECLARE_SOA_COLUMN(McdDsPhi, mcdDsPhi, float); +DECLARE_SOA_COLUMN(McdDsMass, mcdDsMass, float); +DECLARE_SOA_COLUMN(McdZParallel, mcdZParallel, float); +DECLARE_SOA_COLUMN(McdDeltaR, mcdDeltaR, float); +// MC truth matching information +DECLARE_SOA_COLUMN(DsFlagMcMatchRec, dsFlagMcMatchRec, int); +DECLARE_SOA_COLUMN(DsFlagMcMatchGen, dsFlagMcMatchGen, int); +// MC truth information +DECLARE_SOA_COLUMN(MatchedDsOrigin, matchedDsOrigin, int); +} // namespace jet_ds_output +// Detector-level Ds-tagged jets +DECLARE_SOA_TABLE(DsMCDJetTable, "AOD", "DSMCDJET", + jet_ds_output::JetPt, + jet_ds_output::JetEta, + jet_ds_output::JetPhi, + jet_ds_output::JetNConst, + jet_ds_output::DsPt, + jet_ds_output::DsEta, + jet_ds_output::DsPhi, + jet_ds_output::DsMass, + jet_ds_output::ZParallel, + jet_ds_output::DeltaR, + jet_ds_output::DsOrigin, + jet_ds_output::DsFlagMcMatchRec, + jet_ds_output::IsMatched); +// Particle-level Ds-tagged jets +DECLARE_SOA_TABLE(DsMCPJetTable, "AOD", "DSMCPJET", + jet_ds_output::JetPt, + jet_ds_output::JetEta, + jet_ds_output::JetPhi, + jet_ds_output::JetNConst, + jet_ds_output::DsPt, + jet_ds_output::DsEta, + jet_ds_output::DsPhi, + jet_ds_output::ZParallel, + jet_ds_output::DeltaR, + jet_ds_output::DsOrigin, + jet_ds_output::DsFlagMcMatchGen, + jet_ds_output::IsMatched); +// Matched particle-level <-> detector-level Ds-tagged jet pairs +DECLARE_SOA_TABLE(DsMatchedJetTable, "AOD", "DSMATCHJET", + // Particle level + jet_ds_output::McpJetPt, + jet_ds_output::McpJetEta, + jet_ds_output::McpJetPhi, + jet_ds_output::McpJetNConst, + jet_ds_output::McpDsPt, + jet_ds_output::McpDsEta, + jet_ds_output::McpDsPhi, + jet_ds_output::McpZParallel, + jet_ds_output::McpDeltaR, + // Detector level + jet_ds_output::McdJetPt, + jet_ds_output::McdJetEta, + jet_ds_output::McdJetPhi, + jet_ds_output::McdJetNConst, + jet_ds_output::McdDsPt, + jet_ds_output::McdDsEta, + jet_ds_output::McdDsPhi, + jet_ds_output::McdDsMass, + jet_ds_output::McdZParallel, + jet_ds_output::McdDeltaR, + // Truth + jet_ds_output::MatchedDsOrigin); +} // namespace o2::aod + +struct JetDsSpecSubs { + + //================== + // Type definitions + //================== + + Produces outputMCDJets; + Produces outputMCPJets; + Produces outputMatchedJets; + + using DsCandidatesData = aod::CandidatesDsData; + using DsCandidatesMCD = aod::CandidatesDsMCD; + using DsCandidatesMCP = aod::CandidatesDsMCP; + + using DsDataJets = soa::Join; + using DsMCDJets = soa::Join; + using DsMCPJets = soa::Join; + + using DsMCPJetsOnTheFly = soa::Join; + + using DsDataJetsEWS = soa::Join< + aod::DsChargedEventWiseSubtractedJets, + aod::DsChargedEventWiseSubtractedJetConstituents>; + + // Inclusive charged jets + using ChargedJets = soa::Join; + using ChargedJetsEWS = soa::Join; + + // Slices for access to proper HF MCD jet collision that is associated to MCCollision + PresliceUnsorted collisionsPerMCCollisionPreslice = aod::jmccollisionlb::mcCollisionId; + Preslice dsMCDJetsPerEXPCollisionPreslice = aod::jet::collisionId; + // Preslice dsMCDJetsEWSPerEXPCollisionPreslice = aod::jet::collisionId; + Preslice dsMCPJetsPerMCCollisionPreslice = aod::jet::mcCollisionId; + // Preslice dsMCPJetsEWSPerMCCollisionPreslice = aod::jet::mcCollisionId; + + // Event configurables + Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; + Configurable jetPtMin{"jetPtMin", 3.0, "minimum jet pT cut"}; + // Configurable jetR{"jetR", 0.4, "jet resolution parameter"}; + + Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; + Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + + // Event-wise constituent subtraction jet tables + // Configurable centralityMin{"centralityMin", -999.f, "Minimum FT0M centrality"}; + // Configurable centralityMax{"centralityMax", 999.f, "Maximum FT0M centrality"}; + + // internals + std::vector eventSelectionBits; + int trackSelection = -1; + + // Filters + // Filter jetCuts = aod::jet::pt > jetPtMin&& aod::jet::r == nround(jetR.node() * 100.0f); + Filter jetCuts = aod::jet::pt > jetPtMin; + + // Filtered jet tables + using FilteredDsDataJets = soa::Filtered; + using FilteredDsMCDJets = soa::Filtered; + using FilteredDsMCPJets = soa::Filtered; + + using FilteredDsDataJetsEWS = soa::Filtered; + // using FilteredDsMCDJetsEWS = soa::Filtered; + // using FilteredDsMCPJetsEWS = soa::Filtered; + + // Filtered inclusive charged jets + using FilteredChargedJets = soa::Filtered; + using FilteredChargedJetsEWS = soa::Filtered; + + using FilteredDsMCPJetsOnTheFly = soa::Filtered; + + //===================================================================================== + // Histogram definitions + //===================================================================================== + + HistogramRegistry registry; + + void addInclusiveQAHistograms() + { + registry.add("h_collisions", "event status;event status;entries", {HistType::kTH1F, {{10, 0., 10.}}}); + + // Track QA + registry.add("h_track_pt", ";#it{p}_{T,track};entries", {HistType::kTH1F, {{200, 0., 200.}}}); + registry.add("h_track_eta", ";#eta_{track};entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_track_phi", ";#varphi_{track};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + + // Inclusive charged-jet QA + registry.add("h_njets_inclusive", ";Number of charged jets per event;Events", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("h_jet_pt_inclusive", ";#it{p}_{T,jet} (GeV/#it{c});Entries", {HistType::kTH1F, {{200, 0., 200.}}}); + registry.add("h_jet_eta_inclusive", ";#eta_{jet};Entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_jet_phi_inclusive", ";#varphi_{jet} (rad);Entries", {HistType::kTH1F, {{80, -1., 7.}}}); + registry.add("h_jet_mass_inclusive", ";#it{m}_{jet} (GeV/#it{c}^{2});Entries", {HistType::kTH1F, {{120, 0., 60.}}}); + registry.add("h_jet_nconst_inclusive", ";Jet constituents;Entries", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("h_lambda11_inclusive", ";#lambda_{1}^{1};Entries", {HistType::kTH1F, {{100, 0., 1.}}}); + registry.add("h_lambda21_inclusive", ";#lambda_{2}^{1};Entries", {HistType::kTH1F, {{100, 0., 1.}}}); + } + + void addDataHistograms() + { + registry.add("h_event_counter_data", ";Selection step;Events", {HistType::kTH1F, {{3, 0.5, 3.5}}}); + + // Jet QA + registry.add("h_jet_pt_data", "jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}); + registry.add("h_jet_eta_data", "jet #eta;#eta_{jet};entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_jet_phi_data", "jet #phi;#varphi_{jet};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + + // Ds QA + registry.add("h_ds_mass_data", ";m_{D_{S}} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{350, 1.6, 2.3}}}); + registry.add("h_ds_pt_data", ";#it{p}_{T,D_{S}} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 50.}}}); + registry.add("h_ds_eta_data", ";#eta_{D_{S}};entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_ds_phi_data", ";#varphi_{D_{S}};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + + // Ds-tagged jet observables + registry.add("h_ds_jet_projection_data", ";z^{D_{S},jet}_{||};entries", {HistType::kTH1F, {{200, 0., 1.}}}); + registry.add("h_ds_jet_distance_data", ";#DeltaR_{D_{S},jet};entries", {HistType::kTH1F, {{200, 0., 1.}}}); + registry.add("h_ds_jet_mass_data", ";m_{jet}^{ch} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 0., 25.}}}); + registry.add("h_ds_jet_lambda11_data", ";#lambda_{1}^{1};entries", {HistType::kTH1F, {{200, 0., 1.}}}); + registry.add("h_ds_jet_lambda21_data", ";#lambda_{2}^{1};entries", {HistType::kTH1F, {{200, 0., 1.}}}); + + // Main data sparse + registry.add("hSparse_ds_data", ";m_{D_{S}};#it{p}_{T,D_{S}};#it{p}_{T,jet};z^{D_{S},jet}_{||};#DeltaR_{D_{S},jet}", {HistType::kTHnSparseF, {{350, 1.6, 2.3}, {100, 0., 50.}, {100, 0., 100.}, {200, 0., 1.}, {200, 0., 1.}}}); + + auto hEventCounter = registry.get(HIST("h_event_counter_data")); + hEventCounter->GetXaxis()->SetBinLabel(1, "Input collisions"); + hEventCounter->GetXaxis()->SetBinLabel(2, "Event selection"); + hEventCounter->GetXaxis()->SetBinLabel(3, "|z| < 10 cm"); + } + + void addMCEfficiencyHistograms() + { + // General MC counters + registry.add("hMCJetCounter", "N_{jet};", {HistType::kTH1F, {{5, 0., 5.}}}); + registry.add("hMCColCounter", "N_{collisions};", {HistType::kTH1F, {{4, 0., 4.}}}); + // Detector-level: all MCD Ds-tagged jets + // Detector-level jet QA + registry.add("h_jet_pt_mcd", "detector-level jet pT;#it{p}_{T,jet}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("h_jet_eta_mcd", "detector-level jet #eta;#eta_{jet}^{det};entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_jet_phi_mcd", "detector-level jet #varphi;#varphi_{jet}^{det};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + // Detector-level Ds QA + registry.add("h_ds_pt_mcd", ";#it{p}_{T,D_{S}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 50.}}}); + registry.add("h_ds_eta_mcd", ";#eta_{D_{S}}^{det};entries", {HistType::kTH1F, {{60, -1., 1.}}}); + registry.add("h_ds_phi_mcd", ";#varphi_{D_{S}}^{det};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + registry.add("h_ds_mass_mcd", ";m_{D_{S}}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{350, 1.6, 2.3}}}); + // Detector-level sparse histograms + registry.add( + "hSparse_ds_mcd1", + ";m_{D_{S}}^{rec};" + "#it{p}_{T,D_{S}}^{det};" + "#it{p}_{T,jet}^{det};" + "z^{D_{S},jet}_{||,det};" + "Origin(D_{S});" + "flagMcMatchRec;" + "Jet matching status", + {HistType::kTHnSparseF, + {{350, 1.6, 2.3}, + {100, 0., 50.}, + {100, 0., 100.}, + {200, 0., 1.}, + {3, -0.5, 2.5}, + {21, -10.5, 10.5}, + {2, -0.5, 1.5}}}); + + registry.add( + "hSparse_ds_mcd2", + ";#it{p}_{T,D_{S}}^{det};#it{p}_{T,jet}^{det};#DeltaR_{D_{S},jet}^{det}", + {HistType::kTHnSparseF, + {{100, 0., 50.}, + {100, 0., 100.}, + {200, 0., 1.}}}); + + registry.add( + "hSparse_ds_mcd3", + ";#it{p}_{T,jet}^{det};z^{D_{S},jet}_{||,det};#DeltaR_{D_{S},jet}^{det}", + {HistType::kTHnSparseF, + {{100, 0., 100.}, + {200, 0., 1.}, + {200, 0., 1.}}}); + + // Particle-level: all MCP Ds-tagged jets + // Particle-level jet QA + registry.add("h_jet_pt_mcp", "particle-level jet pT;#it{p}_{T,jet}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("h_jet_eta_mcp", "particle-level jet #eta;#eta_{jet}^{part};entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_jet_phi_mcp", "particle-level jet #varphi;#varphi_{jet}^{part};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + // Particle-level Ds QA + registry.add("h_ds_pt_mcp", ";#it{p}_{T,D_{S}}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 50.}}}); + registry.add("h_ds_eta_mcp", ";#eta_{D_{S}}^{part};entries", {HistType::kTH1F, {{60, -1., 1.}}}); + registry.add("h_ds_phi_mcp", ";#varphi_{D_{S}}^{part};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + + // Particle-level sparse with origin and matching status + registry.add( + "hSparse_ds_mcp", + ";#it{p}_{T,D_{S}}^{part};" + "#it{p}_{T,jet}^{part};" + "z^{D_{S},jet}_{||,part};" + "#DeltaR_{D_{S},jet}^{part};" + "Origin(D_{S});" + "flagMcMatchGen;" + "Jet matching status", + {HistType::kTHnSparseF, + {{100, 0., 50.}, + {100, 0., 100.}, + {200, 0., 1.}, + {200, 0., 1.}, + {3, -0.5, 2.5}, + {21, -10.5, 10.5}, + {2, -0.5, 1.5}}}); + + // Matched detector-level Ds-tagged jets + // Matched detector-level jet QA + registry.add("h_jet_pt_mcd_matched", "matched detector-level jet pT;#it{p}_{T,jet}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("h_jet_eta_mcd_matched", "matched detector-level jet #eta;#eta_{jet}^{det};entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_jet_phi_mcd_matched", "matched detector-level jet #varphi;#varphi_{jet}^{det};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + // Matched detector-level Ds QA + registry.add("h_ds_pt_mcd_matched", ";#it{p}_{T,D_{S}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 50.}}}); + registry.add("h_ds_eta_mcd_matched", ";#eta_{D_{S}}^{det};entries", {HistType::kTH1F, {{60, -1., 1.}}}); + registry.add("h_ds_phi_mcd_matched", ";#varphi_{D_{S}}^{det};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + registry.add("h_ds_mass_mcd_matched", ";m_{D_{S}}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{350, 1.6, 2.3}}}); + // Matched detector-level sparse histograms + registry.add( + "hSparse_ds_mcd_matched1", + ";m_{D_{S}}^{rec};#it{p}_{T,D_{S}}^{det};#it{p}_{T,jet}^{det};z^{D_{S},jet}_{||,det};Origin(D_{S})", + {HistType::kTHnSparseF, + {{350, 1.6, 2.3}, + {100, 0., 50.}, + {100, 0., 100.}, + {200, 0., 1.}, + {3, -0.5, 2.5}}}); + + registry.add( + "hSparse_ds_mcd_matched2", + ";#it{p}_{T,D_{S}}^{det};#it{p}_{T,jet}^{det};#DeltaR_{D_{S},jet}^{det}", + {HistType::kTHnSparseF, + {{100, 0., 50.}, + {100, 0., 100.}, + {200, 0., 1.}}}); + + registry.add( + "hSparse_ds_mcd_matched3", + ";#it{p}_{T,jet}^{det};z^{D_{S},jet}_{||,det};#DeltaR_{D_{S},jet}^{det}", + {HistType::kTHnSparseF, + {{100, 0., 100.}, + {200, 0., 1.}, + {200, 0., 1.}}}); + + // MCP <-> MCD matched response + registry.add( + "hSparseMatchedJets", + "Matched Ds-tagged jets;#it{p}_{T,jet}^{part};#it{p}_{T,jet}^{det};#it{p}_{T,D_{S}}^{part};#it{p}_{T,D_{S}}^{det};z_{||}^{part};z_{||}^{det};Origin(D_{S});flagMcMatchRec", + {HistType::kTHnSparseF, + {{100, 0., 100.}, + {100, 0., 100.}, + {100, 0., 50.}, + {100, 0., 50.}, + {200, 0., 1.}, + {200, 0., 1.}, + {3, -0.5, 2.5}, + {21, -10.5, 10.5}}}); + + // Counter labels + auto mcCollisionCounter = registry.get(HIST("hMCColCounter")); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::AllMCCollisions, "All MC collisions"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::EventSelection, "Event selection"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::VertexZCut, "|z_{vtx}| < cut"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::HasMCPDsJet, "Has MCP D_{s}-jet"); + + auto jetCounter = registry.get(HIST("hMCJetCounter")); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::MCPJets, "MCP Ds-jets"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::MCPJetsWithMCDMatch, "MCP jets w/ MCD match"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::MCDJets, "MCD Ds-jets"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::MCDJetsWithMCPMatch, "MCD jets w/ MCP match"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::MatchedPairs, "MCP-MCD pairs"); + + // Matching-status labels + auto hSparseMCD = registry.get(HIST("hSparse_ds_mcd1")); + hSparseMCD->GetAxis(6)->SetBinLabel(1, "Unmatched"); + hSparseMCD->GetAxis(6)->SetBinLabel(2, "Matched"); + + auto hSparseMCP = registry.get(HIST("hSparse_ds_mcp")); + hSparseMCP->GetAxis(6)->SetBinLabel(1, "Unmatched"); + hSparseMCP->GetAxis(6)->SetBinLabel(2, "Matched"); + } + + void addMCPOnTheFlyHistograms() + { + registry.add("h_event_counter_mcp_on_the_fly", ";Selection step;MC collisions", {HistType::kTH1F, {{2, 0.5, 2.5}}}); + + // Particle-level jet QA + registry.add("h_jet_pt_mcp_on_the_fly", ";#it{p}_{T,jet}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("h_jet_eta_mcp_on_the_fly", ";#eta_{jet}^{part};entries", {HistType::kTH1F, {{100, -1., 1.}}}); + registry.add("h_jet_phi_mcp_on_the_fly", ";#varphi_{jet}^{part};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + + // Particle-level Ds QA + registry.add("h_ds_pt_mcp_on_the_fly", ";#it{p}_{T,D_{S}}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{100, 0., 50.}}}); + registry.add("h_ds_eta_mcp_on_the_fly", ";#eta_{D_{S}}^{part};entries", {HistType::kTH1F, {{60, -1., 1.}}}); + registry.add("h_ds_phi_mcp_on_the_fly", ";#varphi_{D_{S}}^{part};entries", {HistType::kTH1F, {{80, -1., 7.}}}); + + // Theoretical particle-level sparse + registry.add("hSparse_ds_mcp_on_the_fly", ";#it{p}_{T,D_{S}}^{part};#it{p}_{T,jet}^{part};z^{D_{S},jet}_{||,part};#DeltaR_{D_{S},jet}^{part};Origin(D_{S})", {HistType::kTHnSparseF, {{100, 0., 50.}, {100, 0., 100.}, {200, 0., 1.}, {200, 0., 1.}, {2, -0.5, 1.5}}}); + + auto hCounter = registry.get(HIST("h_event_counter_mcp_on_the_fly")); + hCounter->GetXaxis()->SetBinLabel(1, "Generated collisions"); + hCounter->GetXaxis()->SetBinLabel(2, "|z_{vtx}^{gen}| < cut"); + + auto hSparse = registry.get(HIST("hSparse_ds_mcp_on_the_fly")); + hSparse->GetAxis(4)->SetBinLabel(1, "Prompt"); + hSparse->GetAxis(4)->SetBinLabel(2, "Non-prompt"); + } + + //======== + // INIT + //======== + + void init(InitContext const&) + { + // Initialise event and track selection criteria + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + + // Prevent simultaneous execution of processes using alternative jet collections + if (doprocessCollisions && doprocessCollisionsEWS) { + throw std::runtime_error("Enable either processCollisions or processCollisionsEWS, not both simultaneously"); + } + + if (doprocessDataChargedSubstructure && doprocessDataChargedSubstructureEWS) { + throw std::runtime_error("Enable either processDataChargedSubstructure or processDataChargedSubstructureEWS, not both simultaneously"); + } + // Determine which histogram groups are required + const bool doInclusiveQA = doprocessCollisions || doprocessCollisionsEWS; + const bool doData = doprocessDataChargedSubstructure || doprocessDataChargedSubstructureEWS; + const bool doMCEfficiency = doprocessMonteCarloEfficiencyDs; + const bool doMCPOnTheFly = doprocessMCPOnTheFly; + + // Register histograms only if the corresponding process is enabled + if (doInclusiveQA) { + addInclusiveQAHistograms(); + } + + if (doData) { + addDataHistograms(); + } + + if (doMCEfficiency) { + addMCEfficiencyHistograms(); + } + + if (doMCPOnTheFly) { + addMCPOnTheFlyHistograms(); + } + } + //=============== + // Lambda compute + //=============== + + template + float computeLambda(JET const& jet, TRACKS const& tracks, float alpha, float kappa) + { + if (jet.pt() <= 0.f) { + return -1.f; + } + + float sum = 0.f; + for (auto const& trk : tracks) { + const float dr = jetutilities::deltaR(jet, trk); + sum += std::pow(trk.pt(), kappa) * std::pow(dr, alpha); + } + const float jetRadius = jet.r() / 100.f; + + const float denom = std::pow(jet.pt(), kappa) * std::pow(jetRadius, alpha); + if (denom <= 0.f) { + return -1.f; + } + return sum / denom; + } + + //================= + // Jet Mass compute + //================= + + template + float computeJetMass(TRACKS const& tracks) + { + double sumPx = 0.0, sumPy = 0.0, sumPz = 0.0, sumE = 0.0; + + for (auto const& trk : tracks) { + const double pt = trk.pt(); + const double phi = trk.phi(); + const double eta = trk.eta(); + + const double px = pt * std::cos(phi); + const double py = pt * std::sin(phi); + const double pz = pt * std::sinh(eta); + const double p = std::sqrt(px * px + py * py + pz * pz); + + sumPx += px; + sumPy += py; + sumPz += pz; + sumE += p; // massless + } + + const double m2 = sumE * sumE - (sumPx * sumPx + sumPy * sumPy + sumPz * sumPz); + return (m2 > 0.0) ? static_cast(std::sqrt(m2)) : 0.f; + } + + //============== + // Collision QA + //============== + + // This function is shared by the pp and Pb–Pb (event-wise subtraction) + template + void fillInclusiveJetQA(aod::JetCollision const& collision, + aod::JetTracks const& tracks, + JetTable const& jets) + { + // Collision counter + registry.fill(HIST("h_collisions"), getValFromBin(AllCollisions)); + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + registry.fill(HIST("h_collisions"), getValFromBin(Sel8ZCut)); + + // Track QA + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + continue; + } + + registry.fill(HIST("h_track_pt"), track.pt()); + registry.fill(HIST("h_track_eta"), track.eta()); + registry.fill(HIST("h_track_phi"), track.phi()); + } + + // Inclusive charged-jet QA + registry.fill(HIST("h_njets_inclusive"), jets.size()); + + for (auto const& jet : jets) { + + registry.fill(HIST("h_jet_pt_inclusive"), jet.pt()); + registry.fill(HIST("h_jet_eta_inclusive"), jet.eta()); + registry.fill(HIST("h_jet_phi_inclusive"), jet.phi()); + + auto constituents = jet.template tracks_as(); + + registry.fill(HIST("h_jet_nconst_inclusive"), constituents.size()); + + registry.fill(HIST("h_jet_mass_inclusive"), computeJetMass(constituents)); + registry.fill(HIST("h_lambda11_inclusive"), computeLambda(jet, constituents, 1.f, 1.f)); + registry.fill(HIST("h_lambda21_inclusive"), computeLambda(jet, constituents, 2.f, 1.f)); + } + } + // Inclusive charged-jet QA for pp collisions + void processCollisions(aod::JetCollision const& collision, + aod::JetTracks const& tracks, + FilteredChargedJets const& jets) + { + fillInclusiveJetQA(collision, tracks, jets); + } + PROCESS_SWITCH(JetDsSpecSubs, processCollisions, "collision QA", false); + // Inclusive charged-jet QA for Pb–Pb collisions using + // event-wise constituent-subtracted jets + void processCollisionsEWS(aod::JetCollision const& collision, + aod::JetTracks const& tracks, + FilteredChargedJetsEWS const& jets) + { + fillInclusiveJetQA(collision, tracks, jets); + } + PROCESS_SWITCH(JetDsSpecSubs, processCollisionsEWS, "collision QA EWS", false); + + //===================================================================================== + // DATA function + //===================================================================================== + + // Common implementation of the Ds-in-jet analysis for data + // Shared by the pp and Pb–Pb (event-wise subtraction) workflows + template + void analyseDataDsJet(aod::JetCollision const& collision, + JetTable const& jets) + { + registry.fill(HIST("h_event_counter_data"), 1); + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + registry.fill(HIST("h_event_counter_data"), 2); + + if (std::abs(collision.posZ()) >= vertexZCut) { + return; + } + registry.fill(HIST("h_event_counter_data"), 3); + + for (const auto& jet : jets) { + + registry.fill(HIST("h_jet_pt_data"), jet.pt()); + registry.fill(HIST("h_jet_eta_data"), jet.eta()); + registry.fill(HIST("h_jet_phi_data"), jet.phi()); + + auto jetTracks = jet.template tracks_as(); + + const float lambda11 = computeLambda(jet, jetTracks, 1.f, 1.f); + const float lambda21 = computeLambda(jet, jetTracks, 2.f, 1.f); + + const float mjet = computeJetMass(jetTracks); + + const TVector3 jetVector(jet.px(), jet.py(), jet.pz()); + + // Loop over Ds candidates + for (const auto& dsCandidate : jet.template candidates_as()) { + + const TVector3 dsVector(dsCandidate.px(), dsCandidate.py(), dsCandidate.pz()); + + // Axis distance Delta_R + const float deltaR = jetutilities::deltaR(jet, dsCandidate); + // zParallel defined as longitudinal momentum fraction along the jet axis + const float zParallel = (jetVector * dsVector) / (jetVector * jetVector); + + // --- Ds-level observables --- + registry.fill(HIST("h_ds_mass_data"), dsCandidate.m()); + registry.fill(HIST("h_ds_pt_data"), dsCandidate.pt()); + registry.fill(HIST("h_ds_eta_data"), dsCandidate.eta()); + registry.fill(HIST("h_ds_phi_data"), dsCandidate.phi()); + + registry.fill(HIST("h_ds_jet_projection_data"), zParallel); + registry.fill(HIST("h_ds_jet_distance_data"), deltaR); + + // Main THnSparse: invariant mass, pT, z, and DeltaR + registry.fill(HIST("hSparse_ds_data"), + dsCandidate.m(), + dsCandidate.pt(), + jet.pt(), + zParallel, + deltaR); + } + + if (!jet.template candidates_as().empty()) { + // Jet Mass + registry.fill(HIST("h_ds_jet_mass_data"), mjet); + + // Jet angularity + if (lambda11 >= 0.f) { + registry.fill(HIST("h_ds_jet_lambda11_data"), lambda11); + } + if (lambda21 >= 0.f) { + registry.fill(HIST("h_ds_jet_lambda21_data"), lambda21); + } + } + } + } + + //===================================================================================== + // DATA process + //===================================================================================== + + // Data analysis using standard charged jets (pp) + void processDataChargedSubstructure(aod::JetCollision const& collision, + FilteredDsDataJets const& jets, + aod::CandidatesDsData const&, + aod::JetTracks const&) + { + analyseDataDsJet(collision, jets); + } + PROCESS_SWITCH(JetDsSpecSubs, processDataChargedSubstructure, "Data charged jets", false); + + // Data analysis using event-wise constituent-subtracted charged jets (Pb–Pb) + void processDataChargedSubstructureEWS(aod::JetCollision const& collision, + FilteredDsDataJetsEWS const& jets, + aod::CandidatesDsData const&, + aod::JetTracks const&) + { + analyseDataDsJet(collision, jets); + } + PROCESS_SWITCH(JetDsSpecSubs, processDataChargedSubstructureEWS, "Data charged jets EWS", false); + + //================== + // MC function + //================== + + template + void analyseMonteCarloEfficiency( + TMCPJetsPerMCCollisionPreslice const& MCPJetsPerMCCollisionPreslice, + aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + TJetsMCD const& mcdjets, + TJetsMCP const& mcpjets, + TCandidatesMCD const&, + TCandidatesMCP const&, + aod::JetTracks const&, + aod::JetParticles const&) + { + // MC collisions and particle-level Ds-tagged jets + for (const auto& mccollision : mccollisions) { + // All MC collisions + registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::AllMCCollisions)); + + // Event selection + if (!jetderiveddatautilities::selectCollision(mccollision, eventSelectionBits)) { + continue; + } + + registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::EventSelection)); + + // Vertex-z selection + if (std::abs(mccollision.posZ()) >= vertexZCut) { + continue; + } + + registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::VertexZCut)); + + // Particle-level jets belonging to the current MC collision + const auto mcpJetsPerMCCollision = mcpjets.sliceBy(MCPJetsPerMCCollisionPreslice, mccollision.globalIndex()); + + if (mcpJetsPerMCCollision.size() > 0) { + registry.fill(HIST("hMCColCounter"), getValFromBin(BinMCColCntr::HasMCPDsJet)); + } + + // Particle-level Ds-tagged jets + for (const auto& mcpjet : mcpJetsPerMCCollision) { + + registry.fill(HIST("hMCJetCounter"), getValFromBin(BinMCJetCntr::MCPJets)); + + // Obtain the Ds particle associated with the particle-level jet + auto mcpcand = mcpjet.template candidates_first_as(); + + // Particle-level Ds-jet observables + TVector3 jetVecMCP; + jetVecMCP.SetPtEtaPhi(mcpjet.pt(), mcpjet.eta(), mcpjet.phi()); + + TVector3 dsVecMCP; + dsVecMCP.SetPtEtaPhi(mcpcand.pt(), mcpcand.eta(), mcpcand.phi()); + + const float zParallelMCP = dsVecMCP.Dot(jetVecMCP) / jetVecMCP.Mag2(); + const float deltaRMCP = jetutilities::deltaR(mcpjet, mcpcand); + + const int originMCP = static_cast(mcpcand.originMcGen()); + const int flagMcMatchGen = static_cast(mcpcand.flagMcMatchGen()); + const bool isMatchedMCP = mcpjet.has_matchedJetCand(); + + // Particle-level jet QA + registry.fill(HIST("h_jet_pt_mcp"), mcpjet.pt()); + registry.fill(HIST("h_jet_eta_mcp"), mcpjet.eta()); + registry.fill(HIST("h_jet_phi_mcp"), mcpjet.phi()); + + // Particle-level Ds QA + registry.fill(HIST("h_ds_pt_mcp"), mcpcand.pt()); + registry.fill(HIST("h_ds_eta_mcp"), mcpcand.eta()); + registry.fill(HIST("h_ds_phi_mcp"), mcpcand.phi()); + + // Particle-level sparse + registry.fill( + HIST("hSparse_ds_mcp"), + mcpcand.pt(), + mcpjet.pt(), + zParallelMCP, + deltaRMCP, + originMCP, + flagMcMatchGen, + static_cast(isMatchedMCP)); + + // Store all particle-level Ds-tagged jets + outputMCPJets( + mcpjet.pt(), + mcpjet.eta(), + mcpjet.phi(), + mcpjet.template tracks_as().size() + + mcpjet.template candidates_as().size(), + mcpcand.pt(), + mcpcand.eta(), + mcpcand.phi(), + zParallelMCP, + deltaRMCP, + originMCP, + flagMcMatchGen, + isMatchedMCP); + + // MCP -> MCD matched pairs + if (isMatchedMCP) { + + registry.fill(HIST("hMCJetCounter"), getValFromBin(BinMCJetCntr::MCPJetsWithMCDMatch)); + + for (const auto& mcdjet : mcpjet.template matchedJetCand_as()) { + + const auto& collision = collisions.iteratorAt(mcdjet.collisionId()); + + // Detector-level collision selection + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || + !(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + + // Obtain the reconstructed Ds candidate + auto mcdcand = mcdjet.template candidates_first_as(); + + // Detector-level Ds-jet observables + TVector3 jetVecMCD; + jetVecMCD.SetPtEtaPhi(mcdjet.pt(), mcdjet.eta(), mcdjet.phi()); + + TVector3 dsVecMCD; + dsVecMCD.SetPtEtaPhi(mcdcand.pt(), mcdcand.eta(), mcdcand.phi()); + + const float zParallelMCD = dsVecMCD.Dot(jetVecMCD) / jetVecMCD.Mag2(); + const float deltaRMCD = jetutilities::deltaR(mcdjet, mcdcand); + + const int originMCD = static_cast(mcdcand.originMcRec()); + const int flagMcMatchRec = static_cast(mcdcand.flagMcMatchRec()); + + // Matched detector-level jet QA + registry.fill(HIST("h_jet_pt_mcd_matched"), mcdjet.pt()); + registry.fill(HIST("h_jet_eta_mcd_matched"), mcdjet.eta()); + registry.fill(HIST("h_jet_phi_mcd_matched"), mcdjet.phi()); + + // Matched detector-level Ds QA + registry.fill(HIST("h_ds_pt_mcd_matched"), mcdcand.pt()); + registry.fill(HIST("h_ds_eta_mcd_matched"), mcdcand.eta()); + registry.fill(HIST("h_ds_phi_mcd_matched"), mcdcand.phi()); + registry.fill(HIST("h_ds_mass_mcd_matched"), mcdcand.m()); + + // Matched detector-level sparse: mass, pT Ds, pT jet, z_parallel, origin + registry.fill( + HIST("hSparse_ds_mcd_matched1"), + mcdcand.m(), + mcdcand.pt(), + mcdjet.pt(), + zParallelMCD, + originMCD); + + // Matched detector-level sparse: pT Ds, pT jet, DeltaR + registry.fill( + HIST("hSparse_ds_mcd_matched2"), + mcdcand.pt(), + mcdjet.pt(), + deltaRMCD); + + // Matched detector-level sparse: pT jet, z_parallel, DeltaR + registry.fill( + HIST("hSparse_ds_mcd_matched3"), + mcdjet.pt(), + zParallelMCD, + deltaRMCD); + + // MCP <-> MCD response + registry.fill( + HIST("hSparseMatchedJets"), + mcpjet.pt(), + mcdjet.pt(), + mcpcand.pt(), + mcdcand.pt(), + zParallelMCP, + zParallelMCD, + originMCP, + flagMcMatchRec); + + // One accepted MCP-MCD pair + registry.fill(HIST("hMCJetCounter"), getValFromBin(BinMCJetCntr::MatchedPairs)); + + // Store matched MCP <-> MCD pair + outputMatchedJets( + // Particle level + mcpjet.pt(), + mcpjet.eta(), + mcpjet.phi(), + mcpjet.template tracks_as().size() + + mcpjet.template candidates_as().size(), + mcpcand.pt(), + mcpcand.eta(), + mcpcand.phi(), + zParallelMCP, + deltaRMCP, + + // Detector level + mcdjet.pt(), + mcdjet.eta(), + mcdjet.phi(), + mcdjet.template tracks_as().size() + + mcdjet.template candidates_as().size(), + mcdcand.pt(), + mcdcand.eta(), + mcdcand.phi(), + mcdcand.m(), + zParallelMCD, + deltaRMCD, + + // MC truth + originMCP); + + } // end of matched MCD jets + } // end of isMatchedMCP + } // end of MCP jets + } // end of MC collisions + + // Detector-level Ds-tagged jets + // This loop is outside the MC-collision loop so that each + // detector-level jet is processed only once. + for (const auto& mcdjet : mcdjets) { + + const auto& collision = collisions.iteratorAt(mcdjet.collisionId()); + + // Detector-level collision selection + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || + !(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST("hMCJetCounter"), getValFromBin(BinMCJetCntr::MCDJets)); + + // Obtain the reconstructed Ds candidate + auto mcdcand = mcdjet.template candidates_first_as(); + + // Detector-level Ds-jet observables + TVector3 jetVecMCD; + jetVecMCD.SetPtEtaPhi(mcdjet.pt(), mcdjet.eta(), mcdjet.phi()); + + TVector3 dsVecMCD; + dsVecMCD.SetPtEtaPhi(mcdcand.pt(), mcdcand.eta(), mcdcand.phi()); + + const float zParallelMCD = dsVecMCD.Dot(jetVecMCD) / jetVecMCD.Mag2(); + const float deltaRMCD = jetutilities::deltaR(mcdjet, mcdcand); + const int originMCD = static_cast(mcdcand.originMcRec()); + const int flagMcMatchRec = static_cast(mcdcand.flagMcMatchRec()); + const bool isMatchedMCD = mcdjet.has_matchedJetCand(); + + if (isMatchedMCD) { + registry.fill(HIST("hMCJetCounter"), getValFromBin(BinMCJetCntr::MCDJetsWithMCPMatch)); + } + + // Detector-level jet QA + registry.fill(HIST("h_jet_pt_mcd"), mcdjet.pt()); + registry.fill(HIST("h_jet_eta_mcd"), mcdjet.eta()); + registry.fill(HIST("h_jet_phi_mcd"), mcdjet.phi()); + + // Detector-level Ds QA + registry.fill(HIST("h_ds_pt_mcd"), mcdcand.pt()); + registry.fill(HIST("h_ds_eta_mcd"), mcdcand.eta()); + registry.fill(HIST("h_ds_phi_mcd"), mcdcand.phi()); + registry.fill(HIST("h_ds_mass_mcd"), mcdcand.m()); + + // Detector-level sparse: mass, pT Ds, pT jet, z_parallel, origin, flagMcMatchRec, matching status + registry.fill( + HIST("hSparse_ds_mcd1"), + mcdcand.m(), + mcdcand.pt(), + mcdjet.pt(), + zParallelMCD, + originMCD, + flagMcMatchRec, + static_cast(isMatchedMCD)); + + // Detector-level sparse: pT Ds, pT jet, DeltaR + registry.fill( + HIST("hSparse_ds_mcd2"), + mcdcand.pt(), + mcdjet.pt(), + deltaRMCD); + + // Detector-level sparse: pT jet, z_parallel, DeltaR + registry.fill( + HIST("hSparse_ds_mcd3"), + mcdjet.pt(), + zParallelMCD, + deltaRMCD); + + // Store all detector-level Ds-tagged jets + outputMCDJets( + mcdjet.pt(), + mcdjet.eta(), + mcdjet.phi(), + mcdjet.template tracks_as().size() + + mcdjet.template candidates_as().size(), + mcdcand.pt(), + mcdcand.eta(), + mcdcand.phi(), + mcdcand.m(), + zParallelMCD, + deltaRMCD, + originMCD, + flagMcMatchRec, + isMatchedMCD); + + } // end of MCD jets + } // end of analyseMonteCarloEfficiency + + void processMonteCarloEfficiencyDs(aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + DsMCDJets const& mcdjets, + DsMCPJets const& mcpjets, + DsCandidatesMCD const& mcdDscand, + DsCandidatesMCP const& mcpDscand, + aod::JetTracks const& tracks, + aod::JetParticles const& particles) + { + analyseMonteCarloEfficiency, + DsMCDJets, + DsMCPJets, + DsCandidatesMCD, + DsCandidatesMCP>( + dsMCPJetsPerMCCollisionPreslice, + mccollisions, + collisions, + mcdjets, + mcpjets, + mcdDscand, + mcpDscand, + tracks, + particles); + } + PROCESS_SWITCH(JetDsSpecSubs, processMonteCarloEfficiencyDs, "Non-matched and matched MC Ds and jets", false); + + //===================================================================================== + // MC particle-level process for on-the-fly simulations + //===================================================================================== + + void processMCPOnTheFly(aod::JetMcCollision const& mccollision, + FilteredDsMCPJetsOnTheFly const& mcpjets, + DsCandidatesMCP const&) + { + // Count all generated MC collisions before the vertex selection + registry.fill(HIST("h_event_counter_mcp_on_the_fly"), 1); + + // Apply the generated-vertex selection + if (std::abs(mccollision.posZ()) >= vertexZCut) { + return; + } + + // Count generated MC collisions passing the vertex selection + registry.fill(HIST("h_event_counter_mcp_on_the_fly"), 2); + + // Loop over particle-level Ds-tagged charged jets in the current MC collision + for (const auto& mcpjet : mcpjets) { + + // Retrieve the leading generated Ds candidate associated with the jet + const auto mcpCandidate = mcpjet.template candidates_first_as(); + + // Classify the generated Ds origin: 0 = prompt, 1 = non-prompt + const auto origin = mcpCandidate.originMcGen(); + + // Keep only prompt and non-prompt generated Ds candidates. + if (origin != RecoDecay::OriginType::Prompt && origin != RecoDecay::OriginType::NonPrompt) { + continue; + } + // Encode prompt and non-prompt origins as 0 and 1, respectively. + const int originMCP = origin == RecoDecay::OriginType::Prompt ? 0 : 1; + + // Build the particle-level jet and Ds momentum vectors + const TVector3 jetVector(mcpjet.px(), mcpjet.py(), mcpjet.pz()); + const TVector3 candidateVector(mcpCandidate.px(), mcpCandidate.py(), mcpCandidate.pz()); + + // Compute the longitudinal momentum fraction of the Ds along the jet axis + const float zParallel = jetVector.Dot(candidateVector) / jetVector.Mag2(); + + // Compute the angular distance between the Ds candidate and the jet axis + const float deltaR = jetutilities::deltaR(mcpjet, mcpCandidate); + + // Fill particle-level jet distributions + registry.fill(HIST("h_jet_pt_mcp_on_the_fly"), mcpjet.pt()); + registry.fill(HIST("h_jet_eta_mcp_on_the_fly"), mcpjet.eta()); + registry.fill(HIST("h_jet_phi_mcp_on_the_fly"), mcpjet.phi()); + + // Fill particle-level Ds-candidate distributions + registry.fill(HIST("h_ds_pt_mcp_on_the_fly"), mcpCandidate.pt()); + registry.fill(HIST("h_ds_eta_mcp_on_the_fly"), mcpCandidate.eta()); + registry.fill(HIST("h_ds_phi_mcp_on_the_fly"), mcpCandidate.phi()); + + // Store the particle-level Ds-tagged jet observables and Ds origin + registry.fill(HIST("hSparse_ds_mcp_on_the_fly"), + mcpCandidate.pt(), + mcpjet.pt(), + zParallel, + deltaR, + originMCP); + } + } + PROCESS_SWITCH(JetDsSpecSubs, processMCPOnTheFly, "Process on-the-fly MC particle-level Ds-tagged jets", false); +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/jetDsSpectrumAndSubstructure.cxx b/PWGJE/Tasks/jetDsSpectrumAndSubstructure.cxx deleted file mode 100644 index 629ba2f75f3..00000000000 --- a/PWGJE/Tasks/jetDsSpectrumAndSubstructure.cxx +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -// Jet substructure and spectrum task for D_s mesons -// -// This task is used to reconstruct and analyse jets containing charged D_s -// mesons -// -/// \author Monalisa Melo -// - -#include "PWGJE/Core/JetDerivedDataUtilities.h" -#include "PWGJE/Core/JetUtilities.h" -#include "PWGJE/DataModel/Jet.h" -#include "PWGJE/DataModel/JetReducedData.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -namespace o2::aod -{ -namespace jet_distance -{ -DECLARE_SOA_COLUMN(JetHfDist, jetHfDist, float); -DECLARE_SOA_COLUMN(JetPt, jetPt, float); -DECLARE_SOA_COLUMN(JetEta, jetEta, float); -DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); -DECLARE_SOA_COLUMN(JetNConst, jetNConst, int); -DECLARE_SOA_COLUMN(HfPt, hfPt, float); -DECLARE_SOA_COLUMN(HfEta, hfEta, float); -DECLARE_SOA_COLUMN(HfPhi, hfPhi, float); -DECLARE_SOA_COLUMN(HfMass, hfMass, float); -DECLARE_SOA_COLUMN(HfY, hfY, float); -DECLARE_SOA_COLUMN(HfMlScore0, hfMlScore0, float); -DECLARE_SOA_COLUMN(HfMlScore1, hfMlScore1, float); -DECLARE_SOA_COLUMN(HfMlScore2, hfMlScore2, float); - -// extra -DECLARE_SOA_COLUMN(JetMass, jetMass, float); -DECLARE_SOA_COLUMN(JetGirth, jetGirth, float); -DECLARE_SOA_COLUMN(JetThrust, jetThrust, float); // lambda_2^1 -DECLARE_SOA_COLUMN(JetLambda11, jetLambda11, float); // lambda_1^1 -} // namespace jet_distance - -DECLARE_SOA_TABLE(JetDistanceTable, "AOD", "JETDISTTABLE", - jet_distance::JetHfDist, - jet_distance::JetPt, - jet_distance::JetEta, - jet_distance::JetPhi, - jet_distance::JetNConst, - jet_distance::HfPt, - jet_distance::HfEta, - jet_distance::HfPhi, - jet_distance::HfMass, - jet_distance::HfY, - jet_distance::HfMlScore0, - jet_distance::HfMlScore1, - jet_distance::HfMlScore2, - jet_distance::JetMass, - jet_distance::JetGirth, - jet_distance::JetThrust, - jet_distance::JetLambda11); -} // namespace o2::aod - -struct JetDsSpecSubs { - HistogramRegistry registry{ - "registry", - { - {"h_collisions", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}}, - {"h_track_pt", "track pT;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, - {"h_track_eta", "track #eta;#eta_{track};entries", {HistType::kTH1F, {{100, -1.0, 1.0}}}}, - {"h_track_phi", "track #varphi;#varphi_{track};entries", {HistType::kTH1F, {{80, -1.0, 7.}}}}, - {"h_jet_pt", "jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, - {"h_jet_eta", "jet #eta;#eta_{jet};entries", {HistType::kTH1F, {{100, -1.0, 1.0}}}}, - {"h_jet_phi", "jet #phi;#phi_{jet};entries", {HistType::kTH1F, {{80, -1.0, 7.}}}}, - {"h_collision_counter", "# of collisions;", {HistType::kTH1F, {{200, 0., 200.}}}}, - {"h_jet_counter", ";# of D_{S} jets;", {HistType::kTH1F, {{6, 0., 3.0}}}}, - {"h_ds_jet_projection", ";z^{D_{S},jet}_{||};dN/dz^{D_{S},jet}_{||}", {HistType::kTH1F, {{1000, 0., 2.}}}}, - {"h_ds_jet_distance_vs_projection", ";#DeltaR_{D_{S},jet};z^{D_{S},jet}_{||}", {HistType::kTH2F, {{1000, 0., 1.}, {1000, 0., 2.}}}}, - {"h_ds_jet_distance", ";#DeltaR_{D_{S},jet};dN/d(#DeltaR)", {HistType::kTH1F, {{1000, 0., 1.}}}}, - {"h_ds_jet_pt", ";p_{T,D_{S} jet};dN/dp_{T,D_{S} jet}", {HistType::kTH1F, {{1000, 0., 100.}}}}, - {"h_ds_jet_eta", ";#eta_{D_{S} jet};entries", {HistType::kTH1F, {{250, -1., 1.}}}}, - {"h_ds_jet_phi", ";#phi_{D_{S} jet};entries", {HistType::kTH1F, {{250, -1., 7.}}}}, - {"h_ds_mass", ";m_{D_{S}} (GeV/c^{2});entries", {HistType::kTH1F, {{1000, 0., 6.}}}}, - {"h_ds_eta", ";#eta_{D_{S}};entries", {HistType::kTH1F, {{250, -1., 1.}}}}, - {"h_ds_phi", ";#phi_{D_{S}};entries", {HistType::kTH1F, {{250, -1., 7.}}}}, - {"h_ds_jet_mass", ";m_{jet}^{ch} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, - {"h_ds_jet_lambda11", ";#lambda_{1}^{1};entries", {HistType::kTH1F, {{200, 0., 1.0}}}}, - {"h_ds_jet_lambda12", ";#lambda_{2}^{1} (thrust);entries", {HistType::kTH1F, {{200, 0., 1.0}}}}, - {"h_ds_jet_girth", ";g (#equiv #lambda_{1}^{1}R);entries", {HistType::kTH1F, {{200, 0., 0.5}}}}, - {"h2_dsjet_pt_lambda11", ";#it{p}_{T,jet} (GeV/#it{c});#lambda_{1}^{1}", {HistType::kTH2F, {{100, 0., 100.}, {200, 0., 1.0}}}}, - {"h2_dsjet_pt_lambda12", ";#it{p}_{T,jet} (GeV/#it{c});#lambda_{2}^{1}", {HistType::kTH2F, {{100, 0., 100.}, {200, 0., 1.0}}}}, - {"h2_dsjet_pt_mass", ";#it{p}_{T,jet} (GeV/#it{c});m_{jet}^{ch} (GeV/#it{c}^{2})", {HistType::kTH2F, {{100, 0., 100.}, {200, 0., 50.0}}}}, - {"h2_dsjet_pt_girth", ";#it{p}_{T,jet} (GeV/#it{c});g", {HistType::kTH2F, {{100, 0., 100.}, {200, 0., 0.5}}}}, - {"h_ds_jet_lambda_extra", ";#lambda_{#alpha}^{#kappa};entries", {HistType::kTH1F, {{200, 0., 1.0}}}}, - {"h2_dsjet_pt_lambda_extra", ";#it{p}_{T,jet} (GeV/#it{c});#lambda_{#alpha}^{#kappa}", {HistType::kTH2F, {{100, 0., 100.}, {200, 0., 1.0}}}}, - }}; - - Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; - Configurable jetPtMin{"jetPtMin", 5.0, "minimum jet pT cut"}; - Configurable jetR{"jetR", 0.4, "jet resolution parameter"}; - - Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; - Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; - - // extra angularity knob - Configurable kappa{"kappa", 1.0f, "angularity kappa"}; - Configurable alpha{"alpha", 1.0f, "angularity alpha"}; - - bool doExtraAngularity = false; - - std::vector eventSelectionBits; - int trackSelection = -1; - - Produces distJetTable; - - template - float computeLambda(JET const& jet, TRACKS const& tracks, float a, float k) - { - if (jet.pt() <= 0.f) { - return -1.f; - } - float sum = 0.f; - for (auto const& trk : tracks) { - const float dr = jetutilities::deltaR(jet, trk); - sum += std::pow(trk.pt(), k) * std::pow(dr, a); - } - const float R = jet.r() / 100.f; - const float denom = std::pow(jet.pt(), k) * std::pow(R, a); - if (denom <= 0.f) { - return -1.f; - } - return sum / denom; - } - - template - float computeJetMassFromTracksMass(TRACKS const& tracks) - { - double sumPx = 0.0, sumPy = 0.0, sumPz = 0.0, sumE = 0.0; - - for (auto const& trk : tracks) { - const double pt = trk.pt(); - const double phi = trk.phi(); - const double eta = trk.eta(); - - const double px = pt * std::cos(phi); - const double py = pt * std::sin(phi); - const double pz = pt * std::sinh(eta); - const double p = std::sqrt(px * px + py * py + pz * pz); - - sumPx += px; - sumPy += py; - sumPz += pz; - sumE += p; // massless - } - - const double m2 = sumE * sumE - (sumPx * sumPx + sumPy * sumPy + sumPz * sumPz); - return (m2 > 0.0) ? static_cast(std::sqrt(m2)) : 0.f; - } - - void init(o2::framework::InitContext&) - { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - - const bool is11 = (std::abs(kappa.value - 1.f) < 1e-6f) && (std::abs(alpha.value - 1.f) < 1e-6f); - const bool is12 = (std::abs(kappa.value - 1.f) < 1e-6f) && (std::abs(alpha.value - 2.f) < 1e-6f); - doExtraAngularity = !(is11 || is12); - } - - Filter jetCuts = aod::jet::pt > jetPtMin&& aod::jet::r == nround(jetR.node() * 100.0f); - Filter collisionFilter = nabs(aod::jcollision::posZ) < vertexZCut; - - void processCollisions(aod::JetCollision const& collision, aod::JetTracks const& tracks) - { - registry.fill(HIST("h_collisions"), 0.5); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { - return; - } - registry.fill(HIST("h_collisions"), 1.5); - - for (auto const& track : tracks) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { - continue; - } - registry.fill(HIST("h_track_pt"), track.pt()); - registry.fill(HIST("h_track_eta"), track.eta()); - registry.fill(HIST("h_track_phi"), track.phi()); - } - } - PROCESS_SWITCH(JetDsSpecSubs, processCollisions, "process JE collisions", false); - - void processDataCharged(soa::Filtered::iterator const& collision, - soa::Filtered const& jets) - { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { - return; - } - for (auto& jet : jets) { - registry.fill(HIST("h_jet_pt"), jet.pt()); - registry.fill(HIST("h_jet_eta"), jet.eta()); - registry.fill(HIST("h_jet_phi"), jet.phi()); - } - } - PROCESS_SWITCH(JetDsSpecSubs, processDataCharged, "charged jets in data", false); - - void processDataChargedSubstructure(aod::JetCollision const& collision, - soa::Join const& jets, - aod::CandidatesDsData const&, - aod::JetTracks const&) - { - registry.fill(HIST("h_collision_counter"), 2.0); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || - !(std::abs(collision.posZ()) < vertexZCut)) { - return; - } - registry.fill(HIST("h_collision_counter"), 3.0); - - for (const auto& jet : jets) { - registry.fill(HIST("h_jet_counter"), 0.5); - - TVector3 jetVector(jet.px(), jet.py(), jet.pz()); - - for (const auto& dsCandidate : jet.candidates_as()) { - TVector3 dsVector(dsCandidate.px(), dsCandidate.py(), dsCandidate.pz()); - - const double zParallel = (jetVector * dsVector) / (jetVector * jetVector); - const double axisDistance = jetutilities::deltaR(jet, dsCandidate); - - registry.fill(HIST("h_ds_jet_projection"), zParallel); - registry.fill(HIST("h_ds_jet_distance_vs_projection"), axisDistance, zParallel); - registry.fill(HIST("h_ds_jet_distance"), axisDistance); - registry.fill(HIST("h_ds_jet_pt"), jet.pt()); - registry.fill(HIST("h_ds_jet_eta"), jet.eta()); - registry.fill(HIST("h_ds_jet_phi"), jet.phi()); - registry.fill(HIST("h_ds_mass"), dsCandidate.m()); - registry.fill(HIST("h_ds_eta"), dsCandidate.eta()); - registry.fill(HIST("h_ds_phi"), dsCandidate.phi()); - - auto jetTracks = jet.tracks_as(); - - const float lambda11 = computeLambda(jet, jetTracks, 1.f, 1.f); - const float lambda12 = computeLambda(jet, jetTracks, 2.f, 1.f); // thrust = λ_2^1 - const float mjet = computeJetMassFromTracksMass(jetTracks); - - const float R = jet.r() / 100.f; - const float girth = (lambda11 >= 0.f) ? (lambda11 * R) : -1.f; - - if (lambda11 >= 0.f) { - registry.fill(HIST("h_ds_jet_lambda11"), lambda11); - registry.fill(HIST("h2_dsjet_pt_lambda11"), jet.pt(), lambda11); - } - if (lambda12 >= 0.f) { - registry.fill(HIST("h_ds_jet_lambda12"), lambda12); - registry.fill(HIST("h2_dsjet_pt_lambda12"), jet.pt(), lambda12); - } - registry.fill(HIST("h_ds_jet_mass"), mjet); - registry.fill(HIST("h2_dsjet_pt_mass"), jet.pt(), mjet); - - if (girth >= 0.f) { - registry.fill(HIST("h_ds_jet_girth"), girth); - registry.fill(HIST("h2_dsjet_pt_girth"), jet.pt(), girth); - } - - if (doExtraAngularity) { - const float lambdaExtra = computeLambda(jet, jetTracks, alpha.value, kappa.value); - if (lambdaExtra >= 0.f) { - registry.fill(HIST("h_ds_jet_lambda_extra"), lambdaExtra); - registry.fill(HIST("h2_dsjet_pt_lambda_extra"), jet.pt(), lambdaExtra); - } - } - - auto scores = dsCandidate.mlScores(); - const float s0 = (scores.size() > 0) ? scores[0] : -999.f; - const float s1 = (scores.size() > 1) ? scores[1] : -999.f; - const float s2 = (scores.size() > 2) ? scores[2] : -999.f; - - distJetTable(static_cast(axisDistance), - jet.pt(), jet.eta(), jet.phi(), - static_cast(jetTracks.size()), - dsCandidate.pt(), dsCandidate.eta(), dsCandidate.phi(), - dsCandidate.m(), dsCandidate.y(), - s0, s1, s2, - mjet, girth, lambda12, lambda11); - - break; // only first Ds per jet - } - } - } - PROCESS_SWITCH(JetDsSpecSubs, processDataChargedSubstructure, "charged HF jet substructure", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"jet-ds-spectrum-subs"})}; } diff --git a/PWGJE/Tasks/jetFinderFullQA.cxx b/PWGJE/Tasks/jetFinderFullQA.cxx index 69b18b6e4af..4802a5a333f 100644 --- a/PWGJE/Tasks/jetFinderFullQA.cxx +++ b/PWGJE/Tasks/jetFinderFullQA.cxx @@ -96,13 +96,13 @@ struct JetFinderFullQATask { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - jetRadiiValues = (std::vector)jetRadii; + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { @@ -253,7 +253,7 @@ struct JetFinderFullQATask { Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut && aod::jcollision::centFT0M >= centralityMin && aod::jcollision::centFT0M < centralityMax); - aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS.value); + aod::EMCALClusterDefinition clusterDefinition = aod::emcalcluster::getClusterDefinitionFromString(clusterDefinitionS); Filter clusterFilter = (aod::jcluster::definition == static_cast(clusterDefinition) && aod::jcluster::eta > clusterEtaMin && aod::jcluster::eta < clusterEtaMax && aod::jcluster::phi >= clusterPhiMin && aod::jcluster::phi <= clusterPhiMax && aod::jcluster::energy >= clusterEnergyMin && aod::jcluster::time > clusterTimeMin && aod::jcluster::time < clusterTimeMax && (clusterRejectExotics && aod::jcluster::isExotic != true)); template diff --git a/PWGJE/Tasks/jetFinderHFQA.h b/PWGJE/Tasks/jetFinderHFQA.h index ad1c25a9aa1..3da47b8f636 100644 --- a/PWGJE/Tasks/jetFinderHFQA.h +++ b/PWGJE/Tasks/jetFinderHFQA.h @@ -16,7 +16,6 @@ #ifndef PWGJE_TASKS_JETFINDERHFQA_H_ #define PWGJE_TASKS_JETFINDERHFQA_H_ -#include "PWGHF/Core/HfHelper.h" #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" #include "PWGJE/Core/JetHFUtilities.h" @@ -73,7 +72,6 @@ struct JetFinderHFQATask { o2::framework::Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; o2::framework::Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events; jet-level rejection applied at the jet finder level, here rejection is applied for collision and track process functions"}; - HfHelper hfHelper; std::vector filledJetR_Both; std::vector filledJetR_Low; std::vector filledJetR_High; @@ -90,10 +88,10 @@ struct JetFinderHFQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { filledJetR_Both.push_back(0.0); @@ -103,7 +101,7 @@ struct JetFinderHFQATask { filledJetHFR_Low.push_back(0.0); filledJetHFR_High.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/Tasks/jetFinderQA.cxx b/PWGJE/Tasks/jetFinderQA.cxx index 45110192fa8..a3114bc39ca 100644 --- a/PWGJE/Tasks/jetFinderQA.cxx +++ b/PWGJE/Tasks/jetFinderQA.cxx @@ -9,8 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// jet finder QA task -// +/// \file jetFinderQA.cxx +/// \brief jet finder QA task /// \author Nima Zardoshti #include "PWGJE/Core/JetDerivedDataUtilities.h" @@ -22,6 +22,7 @@ #include "Common/Core/RecoDecay.h" +#include #include #include #include @@ -31,8 +32,6 @@ #include #include -#include -#include #include #include @@ -42,12 +41,9 @@ #include #include -#include - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; - struct JetFinderQATask { HistogramRegistry registry; @@ -87,16 +83,16 @@ struct JetFinderQATask { Configurable checkCentFT0M{"checkCentFT0M", false, "0: centFT0C as default, 1: use centFT0M estimator"}; // Configurable qcCutOnJetMultVsPt{"qcCutOnJetMultVsPt", false, "debug configurable for LHC26a7 10% test production with strange high mult jet pupulation"}; - Configurable multCutCheck_proportionalFactor{"multCut_proportionalFactor", 0.5, "debug cut value for LHC26a7 10% test production with strange high mult jet pupulation"}; - Configurable multCutCheck_abscissaAtOrigin{"multCut_abscissaAtOrigin", 5, "debug cut value LHC26a7 10% test production with strange high mult jet pupulation"}; - Configurable multCutCheck_analyseMBGapEvents{"multCutCheck_analyseMBGapEvents", 0, "flag to choose to reject min. bias gap events; -1: MBGap only, 0: skip MBGap events, 1: analyse all events"}; + Configurable multCutCheckProportionalFactor{"multCutCheckProportionalFactor", 0.5, "debug cut value for LHC26a7 10% test production with strange high mult jet pupulation"}; + Configurable multCutCheckAbscissaAtOrigin{"multCutCheckAbscissaAtOrigin", 5, "debug cut value LHC26a7 10% test production with strange high mult jet pupulation"}; + Configurable multCutCheckAnalyseMBGapEvents{"multCutCheckAnalyseMBGapEvents", 0, "flag to choose to reject min. bias gap events; -1: MBGap only, 0: skip MBGap events, 1: analyse all events"}; Configurable doMultCutCheck{"doMultCutCheck", false, "decide to apply multCutCheck or not"}; - Configurable multCutCheck_applyRCTSelections{"multCutCheck_applyRCTSelections", true, "decide to apply RCT selections"}; - Configurable multCutCheck_applyWeightCorrection{"multCutCheck_applyWeightCorrection", true, "decide to apply weight correction"}; + Configurable multCutCheckApplyRCTSelections{"multCutCheckApplyRCTSelections", true, "decide to apply RCT selections"}; + Configurable multCutCheckApplyWeightCorrection{"multCutCheckApplyWeightCorrection", true, "decide to apply weight correction"}; - std::vector filledJetR_Both; - std::vector filledJetR_Low; - std::vector filledJetR_High; + std::vector filledJetRBoth; + std::vector filledJetRLow; + std::vector filledJetRHigh; std::vector jetRadiiValues; std::vector eventSelectionBits; @@ -109,18 +105,18 @@ struct JetFinderQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - jetRadiiValues = (std::vector)jetRadii; + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + jetRadiiValues = jetRadii; for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { - filledJetR_Both.push_back(0.0); - filledJetR_Low.push_back(0.0); - filledJetR_High.push_back(0.0); + filledJetRBoth.push_back(0.0); + filledJetRLow.push_back(0.0); + filledJetRHigh.push_back(0.0); } - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { - jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); + jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (std::abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + 0.1); } @@ -425,6 +421,7 @@ struct JetFinderQATask { registry.add("h_collisions_njets", "N_{jets};", {HistType::kTH1F, {{10000, 0.0, 10000.0}}}, doSumw2); registry.add("h2_centrality_ntracks", "; centrality; N_{tracks};", {HistType::kTH2F, {{1100, 0., 110.0}, {10000, 0.0, 10000.0}}}); + registry.add("h2_runnumber_ntracks", "; run number; N_{tracks}", {HistType::kTH2F, {{1000, 0., 1000.0}, {10000, 0.0, 10000.0}}}); registry.add("h2_centrality_njets", "; centrality; N_{jets};", {HistType::kTH2F, {{1100, 0., 110.0}, {10000, 0.0, 10000.0}}}); registry.add("h2_ntracks_rho", "; N_{tracks}; #it{rho} (GeV/area);", {HistType::kTH2F, {{10000, 0.0, 10000.0}, {400, 0.0, 400.0}}}); registry.add("h2_centrality_rho", "; centrality; #it{rho} (GeV/area);", {HistType::kTH2F, {{1100, 0., 110.}, {400, 0., 400.0}}}); @@ -435,6 +432,8 @@ struct JetFinderQATask { registry.add("h2_track_pt_high_track_sigmapt", "#sigma(#it{p}_{T})/#it{p}_{T}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{90, 10., 100.}, {100000, 0.0, 10.0}}}); registry.add("h2_track_pt_track_sigma1overpt", "#sigma(1/#it{p}_{T}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 10.}, {10000, 0.0, 1.0}}}); registry.add("h2_track_pt_high_track_sigma1overpt", "#sigma(1/#it{p}_{T}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{90, 10., 100.}, {10000, 0.0, 1.0}}}); + registry.add("h_ntracksall", "N_{tracks};", {HistType::kTH1I, {nTracksAxis}}); + registry.add("h_ntrackssel", "N_{tracks};", {HistType::kTH1I, {nTracksAxis}}); registry.add("h_mccollision_processid", "mccollision process id;mccollision process id;entries", {HistType::kTH1D, {{200, 0.0, 200.0}}}); @@ -449,6 +448,7 @@ struct JetFinderQATask { registry.add("h_particle_primary_hepmcstatuscode", "primary particle hep mc status code;primary particle hep mc status code;entries", {HistType::kTH1D, {{210, 0.0, 210.0}}}); registry.add("h_particle_primary_process", "primary particle process;primary particle process;entries", {HistType::kTH1D, {{50, 0.0, 50.0}}}); registry.add("h_particle_primary_producedbygenerator", "primary particle producedByGenerator status;primary particle producedByGenerator status;entries", {HistType::kTH1D, {{2, 0.0, 2}}}); + registry.add("h_nparticles_primary", "N(primary particles);", {HistType::kTH1I, {nTracksAxis}}); registry.add("h_jet_pt", "jet pT;#it{p}_{T,jet} (GeV/#it{c}); counts", {HistType::kTH1F, {jetPtAxis}}, doSumw2); registry.add("h_jet_eta", "jet eta;#eta; counts", {HistType::kTH1F, {jetEtaAxis}}, doSumw2); @@ -470,13 +470,14 @@ struct JetFinderQATask { ((checkCentFT0M ? aod::jcollision::centFT0M : aod::jcollision::centFT0C) < centralityMax)); PresliceUnsorted> CollisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; PresliceUnsorted> McCollisionsPerMCPCollision = aod::jmccollision::mcCollisionId; + Preslice ParticlesPerMCPCollision = aod::jmcparticle::mcCollisionId; template bool isAcceptedJet(U const& jet) { if (jetAreaFractionMin > -98.0) { - if (jet.area() < jetAreaFractionMin * M_PI * (jet.r() / 100.0) * (jet.r() / 100.0)) { + if (jet.area() < jetAreaFractionMin * o2::constants::math::PI * (jet.r() / 100.0) * (jet.r() / 100.0)) { return false; } } @@ -549,14 +550,14 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_jet_pt_jet_area"), jet.r() / 100.0, jet.pt(), jet.area(), weight); float angularity = 0.; - for (auto& constituent : jet.template tracks_as()) { + for (const auto& constituent : jet.template tracks_as()) { registry.fill(HIST("h3_jet_r_jet_pt_track_pt"), jet.r() / 100.0, jet.pt(), constituent.pt(), weight); registry.fill(HIST("h3_jet_r_jet_pt_track_eta"), jet.r() / 100.0, jet.pt(), constituent.eta(), weight); registry.fill(HIST("h3_jet_r_jet_pt_track_phi"), jet.r() / 100.0, jet.pt(), constituent.phi(), weight); - angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent), alpha); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); } - angularity /= (jet.pt() * (jet.r() / 100.f)); + angularity /= std::pow(jet.pt(), kappa); registry.fill(HIST("h3_jet_r_jet_pt_jet_angularity"), jet.r() / 100.0, jet.pt(), angularity, weight); } @@ -586,7 +587,7 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_jet_pt_jet_area_rhoareasubtracted"), jet.r() / 100.0, jet.pt() - (rho * jet.area()), jet.area(), weight); registry.fill(HIST("h3_jet_r_jet_pt_jet_pt_rhoareasubtracted"), jet.r() / 100.0, jet.pt(), jet.pt() - (rho * jet.area()), weight); - for (auto& constituent : jet.template tracks_as()) { + for (const auto& constituent : jet.template tracks_as()) { registry.fill(HIST("h3_jet_r_jet_pt_track_pt_rhoareasubtracted"), jet.r() / 100.0, jet.pt() - (rho * jet.area()), constituent.pt(), weight); registry.fill(HIST("h3_jet_r_jet_pt_track_eta_rhoareasubtracted"), jet.r() / 100.0, jet.pt() - (rho * jet.area()), constituent.eta(), weight); @@ -616,7 +617,7 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_jet_pt_jet_ntracks_eventwiseconstituentsubtracted"), jet.r() / 100.0, jet.pt(), jet.tracksIds().size(), weight); registry.fill(HIST("h3_jet_r_jet_pt_jet_area_eventwiseconstituentsubtracted"), jet.r() / 100.0, jet.pt(), jet.area(), weight); - for (auto& constituent : jet.template tracks_as()) { + for (const auto& constituent : jet.template tracks_as()) { registry.fill(HIST("h3_jet_r_jet_pt_track_pt_eventwiseconstituentsubtracted"), jet.r() / 100.0, jet.pt(), constituent.pt(), weight); registry.fill(HIST("h3_jet_r_jet_pt_track_eta_eventwiseconstituentsubtracted"), jet.r() / 100.0, jet.pt(), constituent.eta(), weight); @@ -647,14 +648,14 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_part_jet_pt_part_jet_ntracks_part"), jet.r() / 100.0, jet.pt(), jet.tracksIds().size(), weight); float angularity = 0.; - for (auto& constituent : jet.template tracks_as()) { + for (const auto& constituent : jet.template tracks_as()) { registry.fill(HIST("h3_jet_r_part_jet_pt_part_track_pt_part"), jet.r() / 100.0, jet.pt(), constituent.pt(), weight); registry.fill(HIST("h3_jet_r_part_jet_pt_part_track_eta_part"), jet.r() / 100.0, jet.pt(), constituent.eta(), weight); registry.fill(HIST("h3_jet_r_part_jet_pt_part_track_phi_part"), jet.r() / 100.0, jet.pt(), constituent.phi(), weight); - angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent), alpha); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); } - angularity /= (jet.pt() * (jet.r() / 100.f)); + angularity /= std::pow(jet.pt(), kappa); registry.fill(HIST("h3_jet_r_part_jet_pt_part_jet_angularity_part"), jet.r() / 100.0, jet.pt(), angularity, weight); } @@ -666,7 +667,7 @@ struct JetFinderQATask { } if (jetBase.has_matchedJetGeo()) { - for (auto& jetTag : jetBase.template matchedJetGeo_as>()) { + for (const auto& jetTag : jetBase.template matchedJetGeo_as>()) { if (jetTag.pt() > pTHatMaxMCP * pTHat) { continue; } @@ -679,13 +680,13 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_jet_pt_tag_jet_phi_base_diff_matchedgeo"), jetBase.r() / 100.0, jetTag.pt(), jetTag.phi() - jetBase.phi(), weight); float leadingTrackPtTag = 0.; float angularityTag = 0.; - for (auto& constituent : jetTag.template tracks_as()) { + for (const auto& constituent : jetTag.template tracks_as()) { if (constituent.pt() > leadingTrackPtTag) { leadingTrackPtTag = constituent.pt(); } - angularityTag += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetTag, constituent), alpha); + angularityTag += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetTag, constituent) / (jetTag.r() / 100.f), alpha); } - angularityTag /= (jetTag.pt() * (jetTag.r() / 100.f)); + angularityTag /= std::pow(jetTag.pt(), kappa); registry.fill(HIST("h3_jet_r_jet_pt_tag_leadingtrack_pt_diff_matchedgeo"), jetBase.r() / 100.0, jetTag.pt(), (leadingTrackPtTag - leadingTrackPtBase) / leadingTrackPtTag, weight); registry.fill(HIST("h3_jet_r_jet_pt_tag_leadingtrack_fraction_diff_matchedgeo"), jetBase.r() / 100.0, jetTag.pt(), (leadingTrackPtTag / jetTag.pt()) - (leadingTrackPtBase / jetBase.pt()), weight); registry.fill(HIST("h3_jet_r_jet_pt_tag_angularity_matchedgeo"), jetBase.r() / 100.0, jetTag.pt(), angularityTag, weight); @@ -708,7 +709,7 @@ struct JetFinderQATask { } } if (jetBase.has_matchedJetPt()) { - for (auto& jetTag : jetBase.template matchedJetPt_as>()) { + for (const auto& jetTag : jetBase.template matchedJetPt_as>()) { if (jetTag.pt() > pTHatMaxMCP * pTHat) { continue; } @@ -721,13 +722,13 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_jet_pt_tag_jet_phi_base_diff_matchedpt"), jetBase.r() / 100.0, jetTag.pt(), jetTag.phi() - jetBase.phi(), weight); float leadingTrackPtTag = 0.; float angularityTag = 0.; - for (auto& constituent : jetTag.template tracks_as()) { + for (const auto& constituent : jetTag.template tracks_as()) { if (constituent.pt() > leadingTrackPtTag) { leadingTrackPtTag = constituent.pt(); } - angularityTag += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetTag, constituent), alpha); + angularityTag += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetTag, constituent) / (jetTag.r() / 100.f), alpha); } - angularityTag /= (jetTag.pt() * (jetTag.r() / 100.f)); + angularityTag /= std::pow(jetTag.pt(), kappa); registry.fill(HIST("h3_jet_r_jet_pt_tag_leadingtrack_pt_diff_matchedpt"), jetBase.r() / 100.0, jetTag.pt(), (leadingTrackPtTag - leadingTrackPtBase) / leadingTrackPtTag, weight); registry.fill(HIST("h3_jet_r_jet_pt_tag_leadingtrack_fraction_diff_matchedpt"), jetBase.r() / 100.0, jetTag.pt(), (leadingTrackPtTag / jetTag.pt()) - (leadingTrackPtBase / jetBase.pt()), weight); registry.fill(HIST("h3_jet_r_jet_pt_tag_angularity_matchedpt"), jetBase.r() / 100.0, jetTag.pt(), angularityTag, weight); @@ -746,7 +747,7 @@ struct JetFinderQATask { if (jetBase.has_matchedJetGeo() && jetBase.has_matchedJetPt()) { - for (auto& jetTag : jetBase.template matchedJetGeo_as>()) { + for (const auto& jetTag : jetBase.template matchedJetGeo_as>()) { if (jetTag.pt() > pTHatMaxMCP * pTHat) { continue; } @@ -761,13 +762,13 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_jet_pt_tag_jet_phi_base_diff_matchedgeopt"), jetBase.r() / 100.0, jetTag.pt(), jetTag.phi() - jetBase.phi(), weight); float leadingTrackPtTag = 0.; float angularityTag = 0.; - for (auto& constituent : jetTag.template tracks_as()) { + for (const auto& constituent : jetTag.template tracks_as()) { if (constituent.pt() > leadingTrackPtTag) { leadingTrackPtTag = constituent.pt(); } - angularityTag += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetTag, constituent), alpha); + angularityTag += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetTag, constituent) / (jetTag.r() / 100.f), alpha); } - angularityTag /= (jetTag.pt() * (jetTag.r() / 100.f)); + angularityTag /= std::pow(jetTag.pt(), kappa); registry.fill(HIST("h3_jet_r_jet_pt_tag_leadingtrack_pt_diff_matchedgeopt"), jetBase.r() / 100.0, jetTag.pt(), (leadingTrackPtTag - leadingTrackPtBase) / leadingTrackPtTag, weight); registry.fill(HIST("h3_jet_r_jet_pt_tag_leadingtrack_fraction_diff_matchedgeopt"), jetBase.r() / 100.0, jetTag.pt(), (leadingTrackPtTag / jetTag.pt()) - (leadingTrackPtBase / jetBase.pt()), weight); registry.fill(HIST("h3_jet_r_jet_pt_tag_angularity_matchedgeopt"), jetBase.r() / 100.0, jetTag.pt(), angularityTag, weight); @@ -814,52 +815,52 @@ struct JetFinderQATask { } TRandom3 randomNumber(0); float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); - float randomConePhi = randomNumber.Uniform(0.0, 2 * M_PI); + float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-M_PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; - if (TMath::Sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); } } } - registry.fill(HIST("h2_centrality_rhorandomcone"), collision.centFT0M(), randomConePt - M_PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("h2_centrality_rhorandomcone"), collision.centFT0M(), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); // randomised eta,phi for tracks, to assess part of fluctuations coming from statistically independently emitted particles randomConePt = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, 2 * M_PI) - randomConePhi, static_cast(-M_PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track - if (TMath::Sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); } } } - registry.fill(HIST("h2_centrality_rhorandomconerandomtrackdirection"), collision.centFT0M(), randomConePt - M_PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("h2_centrality_rhorandomconerandomtrackdirection"), collision.centFT0M(), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); // removing the leading jet from the random cone if (jets.size() > 0) { // if there are no jets in the acceptance (from the jetfinder cuts) then there can be no leading jet - float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-M_PI)); + float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); float dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; bool jetWasInCone = false; - while ((randomConeLeadJetDeltaR <= 0 && (TMath::Sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < jets.iteratorAt(0).r() / 100.0 + randomConeR)) || (randomConeLeadJetDeltaR > 0 && (TMath::Sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < randomConeLeadJetDeltaR))) { + while ((randomConeLeadJetDeltaR <= 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < jets.iteratorAt(0).r() / 100.0 + randomConeR)) || (randomConeLeadJetDeltaR > 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < randomConeLeadJetDeltaR))) { jetWasInCone = true; randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); - randomConePhi = randomNumber.Uniform(0.0, 2 * M_PI); - dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, static_cast(-M_PI)); + randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); + dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, (-o2::constants::math::PI)); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; } if (jetWasInCone) { randomConePt = 0.0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { // if track selection is uniformTrack, dcaXY and dcaZ cuts need to be added as they aren't in the selection so that they can be studied here - float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, static_cast(-M_PI)); + float dPhi = RecoDecay::constrainAngle(track.phi() - randomConePhi, (-o2::constants::math::PI)); float dEta = track.eta() - randomConeEta; - if (TMath::Sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { randomConePt += track.pt(); } } @@ -867,16 +868,16 @@ struct JetFinderQATask { } } - registry.fill(HIST("h2_centrality_rhorandomconewithoutleadingjet"), collision.centFT0M(), randomConePt - M_PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("h2_centrality_rhorandomconewithoutleadingjet"), collision.centFT0M(), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); // randomised eta,phi for tracks, to assess part of fluctuations coming from statistically independently emitted particles, removing tracks from 2 leading jets double randomConePtWithoutOneLeadJet = 0; double randomConePtWithoutTwoLeadJet = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { - float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, 2 * M_PI) - randomConePhi, static_cast(-M_PI)); // ignores actual phi of track - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track - if (TMath::Sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { + float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, (-o2::constants::math::PI)); // ignores actual phi of track + float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; // ignores actual eta of track + if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { if (!trackIsInJet(track, jets.iteratorAt(0))) { randomConePtWithoutOneLeadJet += track.pt(); if (!trackIsInJet(track, jets.iteratorAt(1))) { @@ -886,18 +887,18 @@ struct JetFinderQATask { } } } - registry.fill(HIST("h2_centrality_rhorandomconerandomtrackdirectionwithoutoneleadingjets"), collision.centFT0M(), randomConePtWithoutOneLeadJet - M_PI * randomConeR * randomConeR * collision.rho()); - registry.fill(HIST("h2_centrality_rhorandomconerandomtrackdirectionwithouttwoleadingjets"), collision.centFT0M(), randomConePtWithoutTwoLeadJet - M_PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("h2_centrality_rhorandomconerandomtrackdirectionwithoutoneleadingjets"), collision.centFT0M(), randomConePtWithoutOneLeadJet - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("h2_centrality_rhorandomconerandomtrackdirectionwithouttwoleadingjets"), collision.centFT0M(), randomConePtWithoutTwoLeadJet - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); } - enum mbGapSelectionRequirement { - mbGapOnly = -1, - mbGapSkip = 0, - mbGapAndSignal = 1 + enum MbGapSelectionRequirement { + MbGapOnly = -1, + MbGapSkip = 0, + MbGapAndSignal = 1 }; template - bool applyCollisionCuts_multCutCheck(TColl const& collision, bool fillHistograms = false, bool isWeighted = false, float eventWeight = 1.0) + bool applyCollisionCutsMultCutCheck(TColl const& collision, bool fillHistograms = false, bool isWeighted = false, float eventWeight = 1.0) { float centrality = -1.0; checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); @@ -909,7 +910,7 @@ struct JetFinderQATask { registry.fill(HIST("h_collisions_weighted"), 0.5, eventWeight); } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, multCutCheck_applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, multCutCheckApplyRCTSelections)) { return false; } if (fillHistograms) { @@ -929,7 +930,7 @@ struct JetFinderQATask { registry.fill(HIST("h_collisions_weighted"), 2.5, eventWeight); } - if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { + if (static_cast(collision).trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < static_cast(collision).trackOccupancyInTimeRange()) { return false; } if (fillHistograms) { @@ -939,9 +940,9 @@ struct JetFinderQATask { registry.fill(HIST("h_collisions_weighted"), 3.5, eventWeight); } - if (multCutCheck_analyseMBGapEvents == mbGapSelectionRequirement::mbGapOnly && collision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + if (multCutCheckAnalyseMBGapEvents == MbGapSelectionRequirement::MbGapOnly && collision.getSubGeneratorId() != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { return false; - } else if (multCutCheck_analyseMBGapEvents == mbGapSelectionRequirement::mbGapSkip && collision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { + } else if (multCutCheckAnalyseMBGapEvents == MbGapSelectionRequirement::MbGapSkip && collision.getSubGeneratorId() == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { return false; } @@ -953,6 +954,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -972,7 +976,10 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } - for (auto jet : jets) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + for (const auto& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; } @@ -991,7 +998,10 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } - for (auto jet : jets) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + for (const auto& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; } @@ -1008,6 +1018,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1025,6 +1038,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1045,6 +1061,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (const auto& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1054,13 +1073,13 @@ struct JetFinderQATask { } float leadingTrackPtBase = 0.; float angularity = 0.; - for (auto& constituent : jet.template tracks_as()) { + for (const auto& constituent : jet.template tracks_as()) { if (constituent.pt() > leadingTrackPtBase) { leadingTrackPtBase = constituent.pt(); } - angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent), alpha); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); } - angularity /= (jet.pt() * (jet.r() / 100.f)); + angularity /= std::pow(jet.pt(), kappa); fillMatchedHistograms::iterator, soa::Join>(jet, leadingTrackPtBase, angularity); } } @@ -1071,6 +1090,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1088,6 +1110,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1161,6 +1186,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (const auto& mcdjet : mcdjets) { if (!jetfindingutilities::isInEtaAcceptance(mcdjet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1170,13 +1198,13 @@ struct JetFinderQATask { } float leadingTrackPtBase = 0.; float angularity = 0.; - for (auto& constituent : mcdjet.template tracks_as()) { + for (const auto& constituent : mcdjet.template tracks_as()) { if (constituent.pt() > leadingTrackPtBase) { leadingTrackPtBase = constituent.pt(); } - angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(mcdjet, constituent), alpha); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(mcdjet, constituent) / (mcdjet.r() / 100.f), alpha); } - angularity /= (mcdjet.pt() * (mcdjet.r() / 100.f)); + angularity /= std::pow(mcdjet.pt(), kappa); fillMatchedHistograms::iterator, soa::Join>(mcdjet, leadingTrackPtBase, angularity, 1., collision.mcCollision().ptHard()); } } @@ -1191,6 +1219,9 @@ struct JetFinderQATask { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } for (const auto& mcdjet : mcdjets) { if (!jetfindingutilities::isInEtaAcceptance(mcdjet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1200,13 +1231,13 @@ struct JetFinderQATask { } float leadingTrackPtBase = 0.; float angularity = 0.; - for (auto& constituent : mcdjet.template tracks_as()) { + for (const auto& constituent : mcdjet.template tracks_as()) { if (constituent.pt() > leadingTrackPtBase) { leadingTrackPtBase = constituent.pt(); } - angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(mcdjet, constituent), alpha); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(mcdjet, constituent) / (mcdjet.r() / 100.f), alpha); } - angularity /= (mcdjet.pt() * (mcdjet.r() / 100.f)); + angularity /= std::pow(mcdjet.pt(), kappa); fillMatchedHistograms::iterator, soa::Join>(mcdjet, leadingTrackPtBase, angularity, collision.weight(), collision.mcCollision().ptHard()); } } @@ -1253,27 +1284,27 @@ struct JetFinderQATask { } for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { - filledJetR_Both[iJetRadius] = false; - filledJetR_Low[iJetRadius] = false; - filledJetR_High[iJetRadius] = false; + filledJetRBoth[iJetRadius] = false; + filledJetRLow[iJetRadius] = false; + filledJetRHigh[iJetRadius] = false; } - for (auto& jet : jets) { + for (const auto& jet : jets) { for (std::size_t iJetRadius = 0; iJetRadius < jetRadiiValues.size(); iJetRadius++) { if (jet.r() == round(jetRadiiValues[iJetRadius] * 100.0f)) { - if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChLowPt) && !filledJetR_Low[iJetRadius]) { - filledJetR_Low[iJetRadius] = true; + if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChLowPt) && !filledJetRLow[iJetRadius]) { + filledJetRLow[iJetRadius] = true; for (double pt = 0.0; pt <= jet.pt(); pt += 1.0) { registry.fill(HIST("h2_jet_r_jet_pT_triggered_Low"), jet.r() / 100.0, pt); } } - if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChHighPt) && !filledJetR_High[iJetRadius]) { - filledJetR_High[iJetRadius] = true; + if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChHighPt) && !filledJetRHigh[iJetRadius]) { + filledJetRHigh[iJetRadius] = true; for (double pt = 0.0; pt <= jet.pt(); pt += 1.0) { registry.fill(HIST("h2_jet_r_jet_pT_triggered_High"), jet.r() / 100.0, pt); } } - if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChLowPt) && jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChHighPt) && !filledJetR_Both[iJetRadius]) { - filledJetR_Both[iJetRadius] = true; + if (jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChLowPt) && jetderiveddatautilities::selectTrigger(collision, jetderiveddatautilities::JTrigSel::JetChHighPt) && !filledJetRBoth[iJetRadius]) { + filledJetRBoth[iJetRadius] = true; for (double pt = 0.0; pt <= jet.pt(); pt += 1.0) { registry.fill(HIST("h2_jet_r_jet_pT_triggered_Both"), jet.r() / 100.0, pt); } @@ -1305,7 +1336,7 @@ struct JetFinderQATask { registry.fill(HIST("h3_jet_r_jet_phi_collision"), jet.r() / 100.0, jet.phi(), 3.0); } - for (auto& constituent : jet.template tracks_as>()) { + for (const auto& constituent : jet.template tracks_as>()) { registry.fill(HIST("h3_jet_r_jet_pt_track_pt_MB"), jet.r() / 100.0, jet.pt(), constituent.pt()); registry.fill(HIST("h3_jet_r_jet_pt_track_eta_MB"), jet.r() / 100.0, jet.pt(), constituent.eta()); registry.fill(HIST("h3_jet_r_jet_pt_track_phi_MB"), jet.r() / 100.0, jet.pt(), constituent.phi()); @@ -1328,7 +1359,7 @@ struct JetFinderQATask { } } - for (auto& track : tracks) { + for (const auto& track : tracks) { if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; } @@ -1495,15 +1526,16 @@ struct JetFinderQATask { void processQcMultCutCheck(soa::Filtered>::iterator const& collision, soa::Join const&, soa::Join const&, + aod::JBCs const&, soa::Join const& mcdjets, soa::Filtered> const& tracks, soa::Filtered const& mcparticles) { - float eventWeight = multCutCheck_applyWeightCorrection ? collision.weight() : 1; + float eventWeight = multCutCheckApplyWeightCorrection ? collision.weight() : 1; bool hasJetAboveMultCut = false; for (auto const& mcdjet : mcdjets) { - if (mcdjet.tracksIds().size() > multCutCheck_proportionalFactor * mcdjet.pt() + multCutCheck_abscissaAtOrigin) { + if (mcdjet.tracksIds().size() > multCutCheckProportionalFactor * mcdjet.pt() + multCutCheckAbscissaAtOrigin) { hasJetAboveMultCut = true; } } @@ -1512,8 +1544,8 @@ struct JetFinderQATask { } bool fillHistograms = true; - bool isWeighted = multCutCheck_applyWeightCorrection; - if (!applyCollisionCuts_multCutCheck(collision, fillHistograms, isWeighted, eventWeight)) { + bool isWeighted = multCutCheckApplyWeightCorrection; + if (!applyCollisionCutsMultCutCheck(collision, fillHistograms, isWeighted, eventWeight)) { return; } registry.fill(HIST("h_collisions_zvertex"), collision.posZ(), eventWeight); @@ -1521,7 +1553,6 @@ struct JetFinderQATask { if (collision.has_mcCollision()) { // the collision is not fake and has one associated mc coll; .mccollision() can be called auto jetMcCollision = collision.mcCollision_as>(); auto aodMcCollision = jetMcCollision.mcCollision_as>(); - registry.fill(HIST("h2_mccollision_pthardfromweight_pthardfromhepmcxsection"), simPtRef / (std::pow(collision.weight(), 1.0 / pTHatExponent)), jetMcCollision.ptHard()); registry.fill(HIST("h2_mccollision_pthardfromweight_pthardfromhepmcxsection_weighted"), simPtRef / (std::pow(collision.weight(), 1.0 / pTHatExponent)), jetMcCollision.ptHard(), eventWeight); registry.fill(HIST("h_mccollision_processid"), aodMcCollision.processId(), eventWeight); @@ -1531,36 +1562,47 @@ struct JetFinderQATask { registry.fill(HIST("h_collisions_ntracks"), tracks.size(), eventWeight); registry.fill(HIST("h2_centrality_ntracks"), collision.centFT0M(), tracks.size(), eventWeight); + registry.fill(HIST("h2_runnumber_ntracks"), (collision.bc_as().runNumber()) % 1000, tracks.size(), eventWeight); registry.fill(HIST("h_collisions_njets"), mcdjets.size(), eventWeight); registry.fill(HIST("h2_centrality_njets"), collision.centFT0M(), mcdjets.size(), eventWeight); registry.fill(HIST("h2_ntracks_rho"), tracks.size(), collision.rho(), eventWeight); registry.fill(HIST("h2_centrality_rho"), collision.centFT0M(), collision.rho(), eventWeight); + int nTracksAll = tracks.size(); + int nTracksSel = 0; for (auto const& track : tracks) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { - continue; + if (jetderiveddatautilities::selectTrack(track, trackSelection)) { + nTracksSel += 1; + registry.fill(HIST("h2_centrality_track_pt"), collision.centFT0M(), track.pt(), eventWeight); + registry.fill(HIST("h2_centrality_track_eta"), collision.centFT0M(), track.eta(), eventWeight); + registry.fill(HIST("h2_track_pt_track_sigma1overpt"), track.pt(), track.sigma1Pt(), eventWeight); + registry.fill(HIST("h2_track_pt_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), eventWeight); + registry.fill(HIST("h2_track_pt_high_track_sigma1overpt"), track.pt(), track.sigma1Pt(), eventWeight); + registry.fill(HIST("h2_track_pt_high_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), eventWeight); } - registry.fill(HIST("h2_centrality_track_pt"), collision.centFT0M(), track.pt(), eventWeight); - registry.fill(HIST("h2_centrality_track_eta"), collision.centFT0M(), track.eta(), eventWeight); - registry.fill(HIST("h2_track_pt_track_sigma1overpt"), track.pt(), track.sigma1Pt(), eventWeight); - registry.fill(HIST("h2_track_pt_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), eventWeight); - registry.fill(HIST("h2_track_pt_high_track_sigma1overpt"), track.pt(), track.sigma1Pt(), eventWeight); - registry.fill(HIST("h2_track_pt_high_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), eventWeight); - } - - for (auto const& mcparticle : mcparticles) { - registry.fill(HIST("h_particle_pdgcode"), mcparticle.pdgCode(), eventWeight); - registry.fill(HIST("h_particle_genstatuscode"), mcparticle.getGenStatusCode(), eventWeight); - registry.fill(HIST("h_particle_hepmcstatuscode"), mcparticle.getHepMCStatusCode(), eventWeight); - registry.fill(HIST("h_particle_process"), mcparticle.getProcess(), eventWeight); - registry.fill(HIST("h_particle_producedbygenerator"), mcparticle.producedByGenerator(), eventWeight); - if (mcparticle.isPhysicalPrimary()) { - registry.fill(HIST("h_particle_primary_pdgcode"), mcparticle.pdgCode(), eventWeight); - registry.fill(HIST("h_particle_primary_genstatuscode"), mcparticle.getGenStatusCode(), eventWeight); - registry.fill(HIST("h_particle_primary_hepmcstatuscode"), mcparticle.getHepMCStatusCode(), eventWeight); - registry.fill(HIST("h_particle_primary_process"), mcparticle.getProcess(), eventWeight); - registry.fill(HIST("h_particle_primary_producedbygenerator"), mcparticle.producedByGenerator(), eventWeight); + } + registry.fill(HIST("h_ntracksall"), nTracksAll); + registry.fill(HIST("h_ntrackssel"), nTracksSel); + + int nParticlesPrimary = 0; + if (collision.has_mcCollision()) { + auto particleMcCollision = mcparticles.sliceBy(ParticlesPerMCPCollision, collision.mcCollisionId()); + for (auto const& mcparticle : particleMcCollision) { + registry.fill(HIST("h_particle_pdgcode"), mcparticle.pdgCode(), eventWeight); + registry.fill(HIST("h_particle_genstatuscode"), mcparticle.getGenStatusCode(), eventWeight); + registry.fill(HIST("h_particle_hepmcstatuscode"), mcparticle.getHepMCStatusCode(), eventWeight); + registry.fill(HIST("h_particle_process"), mcparticle.getProcess(), eventWeight); + registry.fill(HIST("h_particle_producedbygenerator"), mcparticle.producedByGenerator(), eventWeight); + if (mcparticle.isPhysicalPrimary()) { + nParticlesPrimary += 1; + registry.fill(HIST("h_particle_primary_pdgcode"), mcparticle.pdgCode(), eventWeight); + registry.fill(HIST("h_particle_primary_genstatuscode"), mcparticle.getGenStatusCode(), eventWeight); + registry.fill(HIST("h_particle_primary_hepmcstatuscode"), mcparticle.getHepMCStatusCode(), eventWeight); + registry.fill(HIST("h_particle_primary_process"), mcparticle.getProcess(), eventWeight); + registry.fill(HIST("h_particle_primary_producedbygenerator"), mcparticle.producedByGenerator(), eventWeight); + } } + registry.fill(HIST("h_nparticles_primary"), nParticlesPrimary); } for (auto const& mcdjet : mcdjets) { diff --git a/PWGJE/Tasks/jetFinderV0QA.cxx b/PWGJE/Tasks/jetFinderV0QA.cxx index 55036241730..dd4dc32a394 100644 --- a/PWGJE/Tasks/jetFinderV0QA.cxx +++ b/PWGJE/Tasks/jetFinderV0QA.cxx @@ -68,10 +68,10 @@ struct JetFinderV0QATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); - auto jetRadiiBins = (std::vector)jetRadii; + auto jetRadiiBins = jetRadii.value; if (jetRadiiBins.size() > 1) { jetRadiiBins.push_back(jetRadiiBins[jetRadiiBins.size() - 1] + (TMath::Abs(jetRadiiBins[jetRadiiBins.size() - 1] - jetRadiiBins[jetRadiiBins.size() - 2]))); } else { diff --git a/PWGJE/Tasks/jetHFAngularity.cxx b/PWGJE/Tasks/jetHFAngularity.cxx new file mode 100644 index 00000000000..4850f1819fc --- /dev/null +++ b/PWGJE/Tasks/jetHFAngularity.cxx @@ -0,0 +1,1448 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// Measure heavy flavour angularity(D0 & Lambda_c) + +/// \author Rajdeep Nandi +/// \author Preeti Dhankher + +#include "PWGJE/Core/JetCandidateUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +constexpr int kOriginMcPrompt = 1; // MC origin flag: prompt +constexpr int kOriginMcNonPrompt = 2; // MC origin flag: non-prompt +constexpr int8_t kCandSelFlagD0 = 0; +} // namespace + +namespace o2::aod +{ + +// Jet-side columns (shared by all three tables) +DECLARE_SOA_COLUMN(JetHfDist, jetHfDist, float); +DECLARE_SOA_COLUMN(JetPt, jetPt, float); +DECLARE_SOA_COLUMN(JetEta, jetEta, float); +DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); +DECLARE_SOA_COLUMN(JetNConst, jetNConst, int); +DECLARE_SOA_COLUMN(JetAng, jetAng, float); +DECLARE_SOA_COLUMN(JetGirth, jetGirth, float); +DECLARE_SOA_COLUMN(JetMass, jetMass, float); + +// D0-candidate columns (shared by all three tables) +DECLARE_SOA_COLUMN(HfZParallel, hfZParallel, float); +DECLARE_SOA_COLUMN(HfPt, hfPt, float); +DECLARE_SOA_COLUMN(HfEta, hfEta, float); +DECLARE_SOA_COLUMN(HfPhi, hfPhi, float); +DECLARE_SOA_COLUMN(HfMass, hfMass, float); +DECLARE_SOA_COLUMN(HfY, hfY, float); + +// ML scores (one entry per BDT class) — DATA & MCD only +DECLARE_SOA_COLUMN(HfMlScore0, hfMlScore0, float); +DECLARE_SOA_COLUMN(HfMlScore1, hfMlScore1, float); +DECLARE_SOA_COLUMN(HfMlScore2, hfMlScore2, float); + +// MC-truth columns — MCD & MCP only +DECLARE_SOA_COLUMN(HfFlagMcMatch, hfFlagMcMatch, int8_t); +DECLARE_SOA_COLUMN(HfOriginMc, hfOriginMc, int8_t); + +DECLARE_SOA_COLUMN(JetMatchGeoStatus, jetMatchGeoStatus, int8_t); +DECLARE_SOA_COLUMN(JetMatchCandStatus, jetMatchCandStatus, int8_t); +DECLARE_SOA_COLUMN(JetMatchClean, jetMatchClean, int8_t); +DECLARE_SOA_COLUMN(JetMatchedPt, jetMatchedPt, float); +DECLARE_SOA_COLUMN(JetMatchedEta, jetMatchedEta, float); +DECLARE_SOA_COLUMN(JetMatchedPhi, jetMatchedPhi, float); +DECLARE_SOA_COLUMN(JetMatchedDR, jetMatchedDR, float); +DECLARE_SOA_COLUMN(JetMatchedD0Pt, jetMatchedD0Pt, float); + +DECLARE_SOA_TABLE(D0JetObjTable, "AOD", "D0JETOBJTABLE", + JetHfDist, + JetPt, + JetEta, + JetPhi, + JetNConst, + JetAng, + JetGirth, + JetMass, + HfZParallel, + HfPt, + HfEta, + HfPhi, + HfMass, + HfY, + HfMlScore0, + HfMlScore1, + HfMlScore2); + +DECLARE_SOA_TABLE(D0JetMCDObjTable, "AOD", "D0JETMCDOBJ", + JetHfDist, + JetPt, + JetEta, + JetPhi, + JetNConst, + JetAng, + JetGirth, + JetMass, + HfZParallel, + HfPt, + HfEta, + HfPhi, + HfMass, + HfY, + HfMlScore0, + HfMlScore1, + HfMlScore2, + HfFlagMcMatch, + HfOriginMc); + +DECLARE_SOA_TABLE(D0JetMCPObjTable, "AOD", "D0JETMCPOBJ", + JetHfDist, + JetPt, + JetEta, + JetPhi, + JetNConst, + JetAng, + JetGirth, + JetMass, + HfZParallel, + HfPt, + HfEta, + HfPhi, + HfY, + HfFlagMcMatch, + HfOriginMc); + +DECLARE_SOA_TABLE(D0JetMCDObjMatchedTable, "AOD", "D0JMCDOBJMATCH", + JetHfDist, + JetPt, + JetEta, + JetPhi, + JetNConst, + JetAng, + JetGirth, + JetMass, + HfZParallel, + HfPt, + HfEta, + HfPhi, + HfMass, + HfY, + HfMlScore0, + HfMlScore1, + HfMlScore2, + HfFlagMcMatch, + HfOriginMc, + JetMatchGeoStatus, + JetMatchCandStatus, + JetMatchClean, + JetMatchedPt, + JetMatchedEta, + JetMatchedPhi, + JetMatchedDR, + JetMatchedD0Pt); + +} // namespace o2::aod + +consteval float getValFromBin(int bin) +{ + return static_cast(bin) - 0.5f; +} + +/// Collision-counter bins +enum BinCollCntr { + AllCollisions = 1, + Sel8ZCut = 2 +}; + +/// Jet-counter bins +enum BinJetCntr { + ChargedJets = 1, + D0TaggedJets = 2 +}; + +/// MC-efficiency collision-counter bins (jet-matching based efficiency) +enum BinMCColCntr { + AllMCCollisions = 1, + SelectedMCCollisions = 2, + AssociatedRecoCollisions = 3, + SelectedAssociatedRecoCollisions = 4 +}; + +/// MC-efficiency jet-counter bins (jet-matching based efficiency) +enum BinMCJetCntr { + DetectorLevelJetInMCCollision = 1, + ParticleLevelJetInMCCollision = 2, + DetectorLevelJetWithMatchedCandidate = 3, + ParticleLevelJetWithMatchedCandidate = 4 +}; + +struct JetHFAngularityTask { + + // DATA + + using D0CandidatesData = aod::CandidatesD0Data; + + using D0DataJets = soa::Join; + + // MC-DETECTOR LEVEL(MCD) + + using D0CandidatesMCD = aod::CandidatesD0MCD; + + using D0MCDJets = soa::Join; + + using D0MCDJetsMatched = soa::Join; + + // MC PARTICLE LEVEL (MCP) + + using D0CandidatesMCP = aod::CandidatesD0MCP; + + using D0MCPJets = soa::Join; + + using D0MCPJetsMatched = soa::Join; + + // Configurables + + Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable jetPtMin{"jetPtMin", 5.0f, "Minimum jet pT (GeV/c)"}; + Configurable jetR{"jetR", 0.4f, "Jet resolution parameter R"}; + + Configurable eventSelections{"eventSelections", "sel8", + "Event selection string"}; + Configurable trackSelections{"trackSelections", "globalTracks", + "Track selection string"}; + Configurable rejectReflections{"rejectReflections", true, + "Exclude reflections from response matrices and efficiency numerators"}; + // Shared axes + ConfigurableAxis binsMassD0{"binsMassD0", {500, 1.6f, 2.1f}, "m_{D^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis binsPtD0{"binsPtD0", {200, 0.f, 100.f}, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + ConfigurableAxis binsZParallel{"binsZParallel", {200, 0.f, 2.f}, "z_{||}"}; + ConfigurableAxis binsDeltaR{"binsDeltaR", {200, 0.f, 1.f}, "#DeltaR"}; + ConfigurableAxis binsLambda11{"binsLambda11", {50, 0.f, 1.f}, "#lambda_{1}^{1}"}; + ConfigurableAxis binsLambda12{"binsLambda12", {50, 0.f, 1.f}, "#lambda_{2}^{1}"}; + + // Jet pT: data sparse vs. MC / response + ConfigurableAxis binsPtJetData{"binsPtJetData", {200, 0.f, 100.f}, "#it{p}_{T,jet} (GeV/#it{c})"}; + ConfigurableAxis binsPtJetMC{"binsPtJetMC", {40, 0.f, 200.f}, "#it{p}_{T,jet} (GeV/#it{c})"}; + + // Response-matrix axes + ConfigurableAxis binsPtD0Resp{"binsPtD0Resp", {50, 0.f, 50.f}, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + ConfigurableAxis binsLambda11Resp{"binsLambda11Resp", {50, 0.f, 1.f}, "#lambda_{1}^{1}"}; + ConfigurableAxis binsLambda12Resp{"binsLambda12Resp", {50, 0.f, 1.f}, "#lambda_{2}^{1}"}; + + Produces objJetTable; + Produces objJetMCDTable; + Produces objJetMCPTable; + + Produces objJetMCDMatchedTable; + + std::vector eventSelectionBits; + int trackSelection = -1; + + float massD0MCP = -1.f; + + // Slices for the jet-matching based MC efficiency calculation + + // Reconstructed collisions associated to a given JetMcCollision + PresliceUnsorted collisionsPerMCCollisionPreslice = aod::jmccollisionlb::mcCollisionId; + + // D0-tagged (matched) MCD jets per reconstructed collision + Preslice d0MCDJetsPerCollisionPreslice = aod::jet::collisionId; + + // D0-tagged (matched) MCP jets per MC collision + Preslice d0MCPJetsPerMCCollisionPreslice = aod::jet::mcCollisionId; + + // Filters + + Filter jetCutsPt = aod::jet::pt > jetPtMin; + Filter jetCutsR = aod::jet::r == nround(jetR.node() * 100.0f); + Filter collisionFilter = nabs(aod::jcollision::posZ) < vertexZCut; + + // Histograms + + HistogramRegistry registry{"registry", { + + // DATA + + // ----- Collision QA ----- + {"h_collisions", "Event status;status;entries", {HistType::kTH1F, {{4, 0., 4.}}}}, + + // ----- Track QA ----- + {"h_track_pt", ";#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_track_eta", ";#eta_{track};entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"h_track_phi", ";#varphi_{track};entries", {HistType::kTH1F, {{80, -1., 7.}}}}, + + // ----- Jet QA ----- + {"h_jet_counter", ";type;counts", {HistType::kTH1F, {{4, 0., 4.}}}}, + + {"h_jet_pt", ";#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jet_eta", ";#eta_{jet};entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"h_jet_phi", ";#phi_{jet};entries", {HistType::kTH1F, {{80, -1., 7.}}}}, + + // ----- D0 candidate properties ----- + {"h_d0_mass", ";m_{K#pi} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, + {"h_d0_pt", ";#it{p}_{T,D^{0}} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 100.}}}}, + {"h_d0_eta", ";#eta_{D^{0}};entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"h_d0_phi", ";#phi_{D^{0}};entries", {HistType::kTH1F, {{100, -1., 7.}}}}, + {"h_d0_y", ";y_{D^{0}};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + + // ----- D0-in-jet observables ----- + {"h_d0_jet_projection", ";z_{||}^{D^{0},jet};entries", {HistType::kTH1F, {{200, 0., 2.}}}}, + + {"h_d0_jet_distance", ";#DeltaR_{D^{0},jet};entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + + // ----- Jet substructure (D0-tagged jets only) ----- + {"h_d0_jet_lambda11", ";#lambda_{1}^{1} (angularity);entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + + {"h_d0_jet_lambda12", ";#lambda_{2}^{1} (girth);entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + + {"h_d0_jet_mass", ";m_{jet} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, + + // ----- Jet constituent multiplicity & ML scores + {"h_jet_nconst", ";N_{const};entries", {HistType::kTH1F, {{100, -0.5, 99.5}}}}, + {"h_d0_mlscore0", ";ML score 0 (bkg);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"h_d0_mlscore1", ";ML score 1 (prompt);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + {"h_d0_mlscore2", ";ML score 2 (non-prompt);entries", {HistType::kTH1F, {{100, 0., 1.}}}}, + + // hSparse_d0 is booked in init() (configurable axes) + + // MC DETECTOR LEVEL (MCD) + + {"h_collisions_mcd", "MCD event status;status;entries", {HistType::kTH1F, {{2, 0., 2.}}}}, + + {"h_jet_counter_mcd", ";type;counts", {HistType::kTH1F, {{2, 0., 2.}}}}, + {"h_jet_pt_mcd", ";#it{p}_{T,jet}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jet_eta_mcd", ";#eta_{jet}^{det};entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"h_jet_phi_mcd", ";#phi_{jet}^{det};entries", {HistType::kTH1F, {{80, -1., 7.}}}}, + + {"h_d0_mass_mcd", ";m_{K#pi}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, + {"h_d0_pt_mcd", ";#it{p}_{T,D^{0}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 100.}}}}, + {"h_d0_eta_mcd", ";#eta_{D^{0}}^{det};entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"h_d0_phi_mcd", ";#phi_{D^{0}}^{det};entries", {HistType::kTH1F, {{100, -1., 7.}}}}, + {"h_d0_y_mcd", ";y_{D^{0}}^{det};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + + {"h_d0_jet_projection_mcd", ";z_{||}^{D^{0},jet,det};entries", {HistType::kTH1F, {{200, 0., 2.}}}}, + {"h_d0_jet_distance_mcd", ";#DeltaR_{D^{0},jet}^{det};entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + + {"h_d0_jet_lambda11_mcd", ";#lambda_{1}^{1,det};entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + {"h_d0_jet_lambda12_mcd", ";#lambda_{2}^{1,det};entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + {"h_d0_jet_mass_mcd", ";m_{jet}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, + + {"h_d0_origin_mcd", ";origin (0=none,1=prompt,2=non-prompt);entries", {HistType::kTH1F, {{3, 0., 3.}}}}, + + // hSparse_d0_mcd, hSparse_d0_mcd_calib, hSparse_d0_mcd_reflection are booked in init() + + {"h_d0_mass_mcd_signal", ";m_{K#pi}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, + {"h_d0_mass_mcd_reflection", ";m_{K#pi}^{det} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{300, 1.7, 2.0}}}}, + + // MC PARTICLE LEVEL (MCP) + + {"h_collisions_mcp", "MCP event status;status;entries", {HistType::kTH1F, {{2, 0., 2.}}}}, + + {"h_jet_counter_mcp", ";type;counts", {HistType::kTH1F, {{2, 0., 2.}}}}, + {"h_jet_pt_mcp", ";#it{p}_{T,jet}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, + {"h_jet_eta_mcp", ";#eta_{jet}^{part};entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"h_jet_phi_mcp", ";#phi_{jet}^{part};entries", {HistType::kTH1F, {{80, -1., 7.}}}}, + + {"h_d0_pt_mcp", ";#it{p}_{T,D^{0}}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 100.}}}}, + {"h_d0_eta_mcp", ";#eta_{D^{0}}^{part};entries", {HistType::kTH1F, {{100, -1., 1.}}}}, + {"h_d0_phi_mcp", ";#phi_{D^{0}}^{part};entries", {HistType::kTH1F, {{100, -1., 7.}}}}, + {"h_d0_y_mcp", ";y_{D^{0}}^{part};entries", {HistType::kTH1F, {{100, -2., 2.}}}}, + + {"h_d0_jet_projection_mcp", ";z_{||}^{D^{0},jet,part};entries", {HistType::kTH1F, {{200, 0., 2.}}}}, + {"h_d0_jet_distance_mcp", ";#DeltaR_{D^{0},jet}^{part};entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + + {"h_d0_jet_lambda11_mcp", ";#lambda_{1}^{1,part};entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + {"h_d0_jet_lambda12_mcp", ";#lambda_{2}^{1,part};entries", {HistType::kTH1F, {{200, 0., 1.}}}}, + {"h_d0_jet_mass_mcp", ";m_{jet}^{part} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, + + {"h_d0_origin_mcp", ";origin (0=none,1=prompt,2=non-prompt);entries", {HistType::kTH1F, {{3, 0., 3.}}}}, + + // JET-TO-JET MATCHING (MCD <-> MCP) + + {"h_jet_matching_geo_status_mcd", ";status (0=unmatched,1=matched);entries", {HistType::kTH1F, {{2, 0., 2.}}}}, + {"h_jet_matching_cand_status_mcd", ";status (0=unmatched,1=matched);entries", {HistType::kTH1F, {{2, 0., 2.}}}}, + {"h_jet_matching_clean_mcd", ";status (0=not clean,1=clean);entries", {HistType::kTH1F, {{2, 0., 2.}}}}, + + {"h_jet_matching_geocand_disagree_mcd", ";geo & cand agree? (0=no,1=yes);entries", {HistType::kTH1F, {{2, 0., 2.}}}}, + + {"h_jet_matching_dr_mcd", ";#DeltaR(jet^{det},jet^{part});entries", {HistType::kTH1F, {{100, 0., 0.5}}}}, + + {"h_jet_matching_ngeo_mcd", ";number of geo-matched candidates;entries", {HistType::kTH1F, {{6, 0., 6.}}}}, + + {"h_jet_matching_ncand_mcd", ";number of cand-matched candidates;entries", {HistType::kTH1F, {{6, 0., 6.}}}}, + + {"h_jet_matching_dr_mcd_allcand", ";#DeltaR(jet^{det},jet^{part}) (all geo candidates);entries", {HistType::kTH1F, {{100, 0., 0.5}}}}, + + {"h_jet_pt_response_matrix_allcand", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,jet}^{part} (GeV/#it{c}) (all geo candidates)", {HistType::kTH2F, {{200, 0., 200.}, {200, 0., 200.}}}}, + + // Detector-level vs matched particle-level jet pT + // Response matrices (h_jet_pt_response_matrix[_pr/_np], h_response_*) are booked in init() + + {"h_kineff_gen_num_pr", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + {"h_kineff_gen_den_pr", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + {"h_kineff_gen_num_np", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + {"h_kineff_gen_den_np", ";#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,D^{0}}^{part} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + + {"h_kineff_det_num_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + {"h_kineff_det_den_pr", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + {"h_kineff_det_num_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + {"h_kineff_det_den_np", ";#it{p}_{T,jet}^{det} (GeV/#it{c});#it{p}_{T,D^{0}}^{det} (GeV/#it{c})", {HistType::kTH2F, {{40, 0., 200.}, {50, 0., 50.}}}}, + + {"h_eff_run2_num_pr", ";#it{p}_{T,D^{0}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, + {"h_eff_run2_num_np", ";#it{p}_{T,D^{0}}^{det} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, + {"h_eff_run2_den_pr", ";#it{p}_{T,D^{0}}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, + {"h_eff_run2_den_np", ";#it{p}_{T,D^{0}}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 50.}}}}, + + // MC EFFICIENCY (jet-to-jet matching based) + + {"MC_CollisionCounter", "N_{collisions};", {HistType::kTH1F, {{4, 0., 4.}}}}, + {"MC_JetCounter", "N_{jet};", {HistType::kTH1F, {{4, 0., 4.}}}}, + + // hSparse_d0_mcd_eff and hSparse_d0_mcp_eff are booked in init() + + }}; + + // init() + + void init(InitContext const&) + { + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections.value); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections.value); + + massD0MCP = jetcandidateutilities::getTablePDGMass(); + + // Histograms with configurable axes + // ---- Configurable axes ---- + const AxisSpec axMass{binsMassD0, "m_{D^{0}} (GeV/#it{c}^{2})"}; + const AxisSpec axPtD0{binsPtD0, "#it{p}_{T,D^{0}} (GeV/#it{c})"}; + const AxisSpec axZ{binsZParallel, "z_{||}"}; + const AxisSpec axDR{binsDeltaR, "#DeltaR"}; + const AxisSpec axL11{binsLambda11, "#lambda_{1}^{1}"}; + const AxisSpec axL12{binsLambda12, "#lambda_{2}^{1}"}; + + const AxisSpec axPtJetData{binsPtJetData, "#it{p}_{T,jet} (GeV/#it{c})"}; + const AxisSpec axPtJetDet{binsPtJetMC, "#it{p}_{T,jet}^{det} (GeV/#it{c})"}; + const AxisSpec axPtJetPart{binsPtJetMC, "#it{p}_{T,jet}^{part} (GeV/#it{c})"}; + + const AxisSpec axPtD0Det{binsPtD0Resp, "#it{p}_{T,D^{0}}^{det} (GeV/#it{c})"}; + const AxisSpec axPtD0Part{binsPtD0Resp, "#it{p}_{T,D^{0}}^{part} (GeV/#it{c})"}; + const AxisSpec axL11Det{binsLambda11Resp, "#lambda_{1}^{1,det}"}; + const AxisSpec axL11Part{binsLambda11Resp, "#lambda_{1}^{1,part}"}; + const AxisSpec axL12Det{binsLambda12Resp, "#lambda_{2}^{1,det}"}; + const AxisSpec axL12Part{binsLambda12Resp, "#lambda_{2}^{1,part}"}; + + // ---- Fixed categorical axes (not configurable) ---- + const AxisSpec axOrigin{3, -0.5, 2.5, "Origin(D^{0})"}; + const AxisSpec axMatch{2, -0.5, 1.5, "Matching status"}; + const AxisSpec axRefl{2, -0.5, 1.5, "IsReflection"}; + + // ---- DATA ---- + registry.add("hSparse_d0", "hSparse_d0", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetData, axZ, axDR, axL11, axL12}); + + // ---- MCD ---- + registry.add("hSparse_d0_mcd", "hSparse_d0_mcd", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axDR, axL11, axL12}); + registry.add("hSparse_d0_mcd_calib", "hSparse_d0_mcd_calib", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axL11, axL12}); + registry.add("hSparse_d0_mcd_reflection", "hSparse_d0_mcd_reflection", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axL11, axL12}); + + // ---- Jet pT response (detector level vs. matched particle level) ---- + registry.add("h_jet_pt_response_matrix", "", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + registry.add("h_jet_pt_response_matrix_pr", "prompt", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + registry.add("h_jet_pt_response_matrix_np", "non-prompt", HistType::kTH2F, {axPtJetDet, axPtJetPart}); + + // ---- D0 pT response. Axes: pT_jet^det, pT_D0^det, pT_jet^part, pT_D0^part ---- + registry.add("h_response_d0pt_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axPtD0Det, axPtJetPart, axPtD0Part}); + registry.add("h_response_d0pt_np", "", HistType::kTHnSparseF, + {axPtJetDet, axPtD0Det, axPtJetPart, axPtD0Part}); + + // ---- Angularity / girth response matrices ---- + registry.add("h_response_angularity_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axL11Det, axPtJetPart, axL11Part}); + registry.add("h_response_angularity_np", "", HistType::kTHnSparseF, + {axPtJetDet, axL11Det, axPtJetPart, axL11Part}); + registry.add("h_response_girth_pr", "", HistType::kTHnSparseF, + {axPtJetDet, axL12Det, axPtJetPart, axL12Part}); + registry.add("h_response_girth_np", "", HistType::kTHnSparseF, + {axPtJetDet, axL12Det, axPtJetPart, axL12Part}); + + // ---- Particle-level efficiency, consistent with the response matrices ---- + registry.add("h_eff_num_d0pt_pr", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_num_d0pt_np", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_den_d0pt_pr", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + registry.add("h_eff_den_d0pt_np", "", HistType::kTH2F, {axPtJetPart, axPtD0Part}); + + registry.add("h_eff_num_angularity_pr", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_num_angularity_np", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_den_angularity_pr", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + registry.add("h_eff_den_angularity_np", "", HistType::kTH2F, {axPtJetPart, axL11Part}); + + registry.add("h_eff_num_girth_pr", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_num_girth_np", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_den_girth_pr", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + registry.add("h_eff_den_girth_np", "", HistType::kTH2F, {axPtJetPart, axL12Part}); + + // ---- Efficiency sparses ---- + registry.add("hSparse_d0_mcd_eff", "hSparse_d0_mcd_eff", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axMatch, axL11, axL12, axRefl}); + registry.add("hSparse_d0_mcp_eff", "hSparse_d0_mcp_eff", HistType::kTHnSparseF, + {axPtD0, axPtJetPart, axZ, axOrigin, axMatch, axL11, axL12}); + + // ---- Efficiency sparses, candidate-match version (match axis = candidate-match flag) ---- + registry.add("hSparse_d0_mcd_eff_cand", "hSparse_d0_mcd_eff_cand", HistType::kTHnSparseF, + {axMass, axPtD0, axPtJetDet, axZ, axOrigin, axMatch, axL11, axL12, axRefl}); + registry.add("hSparse_d0_mcp_eff_cand", "hSparse_d0_mcp_eff_cand", HistType::kTHnSparseF, + {axPtD0, axPtJetPart, axZ, axOrigin, axMatch, axL11, axL12}); + + // Bin labels + + // ---- DATA bin labels ---- + auto hColl = registry.get(HIST("h_collisions")); + hColl->GetXaxis()->SetBinLabel(AllCollisions, "all"); + hColl->GetXaxis()->SetBinLabel(Sel8ZCut, "sel8 + |z| cut"); + hColl->GetXaxis()->SetBinLabel(3, "has D0 jet"); + hColl->GetXaxis()->SetBinLabel(4, "rejected"); + + auto hJet = registry.get(HIST("h_jet_counter")); + hJet->GetXaxis()->SetBinLabel(ChargedJets, "All charged jets"); + hJet->GetXaxis()->SetBinLabel(D0TaggedJets, "D0-tagged jets"); + + // ---- MCD bin labels ---- + auto hCollMCD = registry.get(HIST("h_collisions_mcd")); + hCollMCD->GetXaxis()->SetBinLabel(1, "selected collisions"); + hCollMCD->GetXaxis()->SetBinLabel(2, "has D0 jet"); + + auto hJetMCD = registry.get(HIST("h_jet_counter_mcd")); + hJetMCD->GetXaxis()->SetBinLabel(ChargedJets, "All charged jets"); + hJetMCD->GetXaxis()->SetBinLabel(D0TaggedJets, "D0-tagged jets"); + + auto hOriginMCD = registry.get(HIST("h_d0_origin_mcd")); + hOriginMCD->GetXaxis()->SetBinLabel(1, "none/bkg"); + hOriginMCD->GetXaxis()->SetBinLabel(2, "prompt"); + hOriginMCD->GetXaxis()->SetBinLabel(3, "non-prompt"); + + // ---- MCP bin labels ---- + auto hCollMCP = registry.get(HIST("h_collisions_mcp")); + hCollMCP->GetXaxis()->SetBinLabel(1, "all MC collisions"); + hCollMCP->GetXaxis()->SetBinLabel(2, "has D0 jet"); + + auto hJetMCP = registry.get(HIST("h_jet_counter_mcp")); + hJetMCP->GetXaxis()->SetBinLabel(ChargedJets, "All charged jets"); + hJetMCP->GetXaxis()->SetBinLabel(D0TaggedJets, "D0-tagged jets"); + + auto hOriginMCP = registry.get(HIST("h_d0_origin_mcp")); + hOriginMCP->GetXaxis()->SetBinLabel(1, "none/bkg"); + hOriginMCP->GetXaxis()->SetBinLabel(2, "prompt"); + hOriginMCP->GetXaxis()->SetBinLabel(3, "non-prompt"); + + // ---- Jet-matching bin labels ---- + auto hMatchGeoMCD = registry.get(HIST("h_jet_matching_geo_status_mcd")); + hMatchGeoMCD->GetXaxis()->SetBinLabel(1, "unmatched"); + hMatchGeoMCD->GetXaxis()->SetBinLabel(2, "matched"); + + auto hMatchCandMCD = registry.get(HIST("h_jet_matching_cand_status_mcd")); + hMatchCandMCD->GetXaxis()->SetBinLabel(1, "unmatched"); + hMatchCandMCD->GetXaxis()->SetBinLabel(2, "matched"); + + auto hMatchCleanMCD = registry.get(HIST("h_jet_matching_clean_mcd")); + hMatchCleanMCD->GetXaxis()->SetBinLabel(1, "not clean"); + hMatchCleanMCD->GetXaxis()->SetBinLabel(2, "clean"); + + auto hGeoCandDisagreeMCD = registry.get(HIST("h_jet_matching_geocand_disagree_mcd")); + hGeoCandDisagreeMCD->GetXaxis()->SetBinLabel(1, "disagree"); + hGeoCandDisagreeMCD->GetXaxis()->SetBinLabel(2, "agree"); + + // ---- MC efficiency (jet-to-jet matching based) bin labels ---- + auto mcCollisionCounter = registry.get(HIST("MC_CollisionCounter")); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::AllMCCollisions, "All MC collisions"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::SelectedMCCollisions, "Selected MC collisions"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::AssociatedRecoCollisions, "MC-Associated reco collisions"); + mcCollisionCounter->GetXaxis()->SetBinLabel(BinMCColCntr::SelectedAssociatedRecoCollisions, "Selected MC-Associated reco collisions"); + + auto jetCounter = registry.get(HIST("MC_JetCounter")); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::DetectorLevelJetInMCCollision, "Detector-level D0 jets"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::ParticleLevelJetInMCCollision, "Particle-level D0 jets"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::DetectorLevelJetWithMatchedCandidate, "Detector jets matched to particle"); + jetCounter->GetXaxis()->SetBinLabel(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate, "Particle jets matched to detector"); + + auto hSparseMCDEff = registry.get(HIST("hSparse_d0_mcd_eff")); + hSparseMCDEff->GetAxis(4)->SetBinLabel(1, "none/bkg"); + hSparseMCDEff->GetAxis(4)->SetBinLabel(2, "prompt"); + hSparseMCDEff->GetAxis(4)->SetBinLabel(3, "non-prompt"); + hSparseMCDEff->GetAxis(5)->SetBinLabel(1, "Unmatched"); + hSparseMCDEff->GetAxis(5)->SetBinLabel(2, "Matched"); + hSparseMCDEff->GetAxis(8)->SetBinLabel(1, "Not reflection"); + hSparseMCDEff->GetAxis(8)->SetBinLabel(2, "Reflection"); + + auto hSparseMCPEff = registry.get(HIST("hSparse_d0_mcp_eff")); + hSparseMCPEff->GetAxis(3)->SetBinLabel(1, "none/bkg"); + hSparseMCPEff->GetAxis(3)->SetBinLabel(2, "prompt"); + hSparseMCPEff->GetAxis(3)->SetBinLabel(3, "non-prompt"); + hSparseMCPEff->GetAxis(4)->SetBinLabel(1, "Unmatched"); + hSparseMCPEff->GetAxis(4)->SetBinLabel(2, "Matched"); + + // ---- Candidate-match efficiency sparse bin labels ---- + auto hSparseMCDEffCand = registry.get(HIST("hSparse_d0_mcd_eff_cand")); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(1, "none/bkg"); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(2, "prompt"); + hSparseMCDEffCand->GetAxis(4)->SetBinLabel(3, "non-prompt"); + hSparseMCDEffCand->GetAxis(5)->SetBinLabel(1, "Unmatched (cand)"); + hSparseMCDEffCand->GetAxis(5)->SetBinLabel(2, "Matched (cand)"); + hSparseMCDEffCand->GetAxis(8)->SetBinLabel(1, "Not reflection"); + hSparseMCDEffCand->GetAxis(8)->SetBinLabel(2, "Reflection"); + + auto hSparseMCPEffCand = registry.get(HIST("hSparse_d0_mcp_eff_cand")); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(1, "none/bkg"); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(2, "prompt"); + hSparseMCPEffCand->GetAxis(3)->SetBinLabel(3, "non-prompt"); + hSparseMCPEffCand->GetAxis(4)->SetBinLabel(1, "Unmatched (cand)"); + hSparseMCPEffCand->GetAxis(4)->SetBinLabel(2, "Matched (cand)"); + + // ---- Reflection sparse bin labels ---- + auto hSparseMCDRefl = registry.get(HIST("hSparse_d0_mcd_reflection")); + hSparseMCDRefl->GetAxis(4)->SetBinLabel(1, "none/bkg"); + hSparseMCDRefl->GetAxis(4)->SetBinLabel(2, "prompt"); + hSparseMCDRefl->GetAxis(4)->SetBinLabel(3, "non-prompt"); + } + + // Helper: reflection identification (MCD only) + + template + bool isReflectedCandidate(CAND const& cand) + { + const int8_t flagMcMatchRec = cand.flagMcMatchRec(); + if (flagMcMatchRec == 0) { + return false; // not truth-matched -> background, not a reflection + } + const bool trueIsD0 = flagMcMatchRec > 0; // sign encodes true species + const bool thisRowIsD0Hypothesis = (cand.candidateSelFlag() == kCandSelFlagD0); // which hypothesis this row uses + return thisRowIsD0Hypothesis != trueIsD0; + } + + // Helper: angularity + + template + float computeLambda(JET const& jet, TRACKS const& tracks, CANDIDATES const& candidates, + float alpha, float kappa) + { + if (jet.pt() <= 0.f) { + return -1.f; + } + + float sum = 0.f; + for (auto const& trk : tracks) { + const float dr = jetutilities::deltaR(jet, trk); + sum += std::pow(trk.pt(), kappa) * std::pow(dr, alpha); + } + for (auto const& cand : candidates) { + const float dr = jetutilities::deltaR(jet, cand); + sum += std::pow(cand.pt(), kappa) * std::pow(dr, alpha); + } + + const float jetRadius = jet.r() / 100.f; // stored as R×100 in the table + const float denom = std::pow(jet.pt(), kappa) * std::pow(jetRadius, alpha); + if (denom <= 0.f) { + return -1.f; + } + return sum / denom; + } + + // Helper: jet invariant mass + + template + float computeJetMass(TRACKS const& tracks, + CANDIDATES const& candidates, + double candMass = -1.) + { + std::array momTotal{0., 0., 0.}; + double energyTot = 0.; + + for (auto const& trk : tracks) { + momTotal[0] += trk.px(); + momTotal[1] += trk.py(); + momTotal[2] += trk.pz(); + energyTot += RecoDecay::p(trk.px(), trk.py(), trk.pz()); // massless approximation for ordinary tracks + } + + for (auto const& cand : candidates) { + double m = 0; + if (candMass > 0.) { + m = candMass; + } else if constexpr (requires { cand.m(); }) { + m = cand.m(); // DATA / MCD: reconstructed invariant mass + } else { + m = 0.; + } + + momTotal[0] += cand.px(); + momTotal[1] += cand.py(); + momTotal[2] += cand.pz(); + energyTot += RecoDecay::e(cand.px(), cand.py(), cand.pz(), m); + } + + const double mass2 = RecoDecay::m2(momTotal, energyTot); + return (mass2 > 0.) ? static_cast(std::sqrt(mass2)) : 0.f; + } + + // Process: collision QA (DATA) + + void processCollisions(aod::JetCollision const& collision, + aod::JetTracks const& tracks) + { + registry.fill(HIST("h_collisions"), getValFromBin(AllCollisions)); + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + registry.fill(HIST("h_collisions"), 3.5f); // rejected + return; + } + registry.fill(HIST("h_collisions"), getValFromBin(Sel8ZCut)); + + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + continue; + } + registry.fill(HIST("h_track_pt"), track.pt()); + registry.fill(HIST("h_track_eta"), track.eta()); + registry.fill(HIST("h_track_phi"), track.phi()); + } + } + + PROCESS_SWITCH(JetHFAngularityTask, processCollisions, + "Collision and track QA", true); + + // Process: DATA jet analysis + + void processDataChargedSubstructure( + aod::JetCollision const& collision, + soa::Filtered const& jets, + D0CandidatesData const& /*candidates*/, + aod::JetTracks const&) + { + // ---- Event selection ---- + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + + bool collisionHasD0Jet = false; + + for (const auto& jet : jets) { + + registry.fill(HIST("h_jet_counter"), getValFromBin(ChargedJets)); + registry.fill(HIST("h_jet_pt"), jet.pt()); + registry.fill(HIST("h_jet_eta"), jet.eta()); + registry.fill(HIST("h_jet_phi"), jet.phi()); + + // ---- Jet-level quantities ---- + + auto jetTracks = jet.template tracks_as(); + auto jetCandidates = jet.template candidates_as(); + + const int nConst = static_cast(jetTracks.size()) + static_cast(jetCandidates.size()); + const float angularity = computeLambda(jet, jetTracks, jetCandidates, 1.f, 1.f); // λ_1^1 + const float girth = computeLambda(jet, jetTracks, jetCandidates, 2.f, 1.f); // λ_2^1 + const float mjet = computeJetMass(jetTracks, jetCandidates); + + const std::array jetMom{jet.px(), jet.py(), jet.pz()}; + + bool hasD0 = false; + + // ---- D0 candidate loop ---- + for (const auto& d0Candidate : jetCandidates) { + + hasD0 = true; + + const std::array d0Mom{d0Candidate.px(), d0Candidate.py(), d0Candidate.pz()}; + + // Longitudinal momentum fraction: z_|| = (p_D0 . p_jet) / |p_jet|^2 + const float zParallel = RecoDecay::dotProd(d0Mom, jetMom) / RecoDecay::mag2(jetMom); + // Angular separation between D0 and jet axis + const float axisDistance = jetutilities::deltaR(jet, d0Candidate); + + // ---- Histograms ---- + registry.fill(HIST("h_d0_mass"), d0Candidate.m()); + registry.fill(HIST("h_d0_pt"), d0Candidate.pt()); + registry.fill(HIST("h_d0_eta"), d0Candidate.eta()); + registry.fill(HIST("h_d0_phi"), d0Candidate.phi()); + registry.fill(HIST("h_d0_y"), d0Candidate.y()); + registry.fill(HIST("h_d0_jet_projection"), zParallel); + registry.fill(HIST("h_d0_jet_distance"), axisDistance); + + registry.fill(HIST("h_jet_nconst"), nConst); + registry.fill(HIST("h_d0_mlscore0"), d0Candidate.mlScores()[0]); + registry.fill(HIST("h_d0_mlscore1"), d0Candidate.mlScores()[1]); + registry.fill(HIST("h_d0_mlscore2"), d0Candidate.mlScores()[2]); + + registry.fill(HIST("hSparse_d0"), + d0Candidate.m(), d0Candidate.pt(), + jet.pt(), zParallel, axisDistance, + angularity, girth); + + objJetTable(axisDistance, + jet.pt(), + jet.eta(), + jet.phi(), + nConst, + angularity, + girth, + mjet, + zParallel, + d0Candidate.pt(), + d0Candidate.eta(), + d0Candidate.phi(), + d0Candidate.m(), + d0Candidate.y(), + d0Candidate.mlScores()[0], + d0Candidate.mlScores()[1], + d0Candidate.mlScores()[2]); + + } // end D0 loop + + if (hasD0) { + registry.fill(HIST("h_jet_counter"), getValFromBin(D0TaggedJets)); + registry.fill(HIST("h_d0_jet_lambda11"), angularity); + registry.fill(HIST("h_d0_jet_lambda12"), girth); + registry.fill(HIST("h_d0_jet_mass"), mjet); + collisionHasD0Jet = true; + } + + } // end jet loop + + if (collisionHasD0Jet) { + registry.fill(HIST("h_collisions"), 2.5f); // has D0 jet + } + } + + PROCESS_SWITCH(JetHFAngularityTask, processDataChargedSubstructure, + "DATA: D0-tagged charged jet substructure", true); + + // Process: MC DETECTOR-LEVEL (MCD) + + void processMCDChargedSubstructure( + aod::JetCollision const& collision, + soa::Filtered const& mcdjets, + D0CandidatesMCD const& /*candidates*/, + aod::JetTracks const&) + { + // ---- Event selection (same sel8 + |z| ) ---- + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + + registry.fill(HIST("h_collisions_mcd"), 0.5f); // selected collisions + + bool collisionHasD0Jet = false; + + for (const auto& jet : mcdjets) { + + registry.fill(HIST("h_jet_counter_mcd"), getValFromBin(ChargedJets)); + registry.fill(HIST("h_jet_pt_mcd"), jet.pt()); + registry.fill(HIST("h_jet_eta_mcd"), jet.eta()); + registry.fill(HIST("h_jet_phi_mcd"), jet.phi()); + + // ---- Jet-level quantities (detector level) ---- + auto jetTracks = jet.template tracks_as(); + auto jetCandidates = jet.template candidates_as(); + + const int nConst = static_cast(jetTracks.size()) + static_cast(jetCandidates.size()); + const float angularity = computeLambda(jet, jetTracks, jetCandidates, 1.f, 1.f); // λ_1^1 + const float girth = computeLambda(jet, jetTracks, jetCandidates, 2.f, 1.f); // λ_2^1 + const float mjet = computeJetMass(jetTracks, jetCandidates); + + const std::array jetMom{jet.px(), jet.py(), jet.pz()}; + + bool hasD0 = false; + + // D0 candidate loop (with MC truth-matching info) + for (const auto& d0Candidate : jetCandidates) { + + hasD0 = true; + + const std::array d0Mom{d0Candidate.px(), d0Candidate.py(), d0Candidate.pz()}; + + const float zParallel = RecoDecay::dotProd(d0Mom, jetMom) / RecoDecay::mag2(jetMom); + const float axisDistance = jetutilities::deltaR(jet, d0Candidate); + + const int8_t flagMcMatch = d0Candidate.flagMcMatchRec(); + const int8_t originMc = d0Candidate.originMcRec(); // 0 none, 1 prompt, 2 non-prompt + + // ---- Reflection identification ---- + const bool isReflection = isReflectedCandidate(d0Candidate); + const bool isTrueSignal = (flagMcMatch != 0) && !isReflection; + + if (isReflection) { + registry.fill(HIST("h_d0_mass_mcd_reflection"), d0Candidate.m()); + registry.fill(HIST("hSparse_d0_mcd_reflection"), + d0Candidate.m(), d0Candidate.pt(), jet.pt(), zParallel, originMc, + angularity, girth); + } else if (isTrueSignal) { + registry.fill(HIST("h_d0_mass_mcd_signal"), d0Candidate.m()); + registry.fill(HIST("hSparse_d0_mcd_calib"), + d0Candidate.m(), d0Candidate.pt(), jet.pt(), zParallel, originMc, + angularity, girth); + } + + // ---- Histograms ---- + registry.fill(HIST("h_d0_mass_mcd"), d0Candidate.m()); + registry.fill(HIST("h_d0_pt_mcd"), d0Candidate.pt()); + registry.fill(HIST("h_d0_eta_mcd"), d0Candidate.eta()); + registry.fill(HIST("h_d0_phi_mcd"), d0Candidate.phi()); + registry.fill(HIST("h_d0_y_mcd"), d0Candidate.y()); + registry.fill(HIST("h_d0_jet_projection_mcd"), zParallel); + registry.fill(HIST("h_d0_jet_distance_mcd"), axisDistance); + registry.fill(HIST("h_d0_origin_mcd"), static_cast(originMc)); + registry.fill(HIST("hSparse_d0_mcd"), + d0Candidate.m(), d0Candidate.pt(), + jet.pt(), zParallel, axisDistance, + angularity, girth); + + if (originMc == kOriginMcPrompt) { + registry.fill(HIST("h_eff_run2_num_pr"), d0Candidate.pt()); + } else if (originMc == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_run2_num_np"), d0Candidate.pt()); + } + + objJetMCDTable(axisDistance, + jet.pt(), + jet.eta(), + jet.phi(), + nConst, + angularity, + girth, + mjet, + zParallel, + d0Candidate.pt(), + d0Candidate.eta(), + d0Candidate.phi(), + d0Candidate.m(), + d0Candidate.y(), + d0Candidate.mlScores()[0], + d0Candidate.mlScores()[1], + d0Candidate.mlScores()[2], + flagMcMatch, + originMc); + + } // end D0 loop + + if (hasD0) { + registry.fill(HIST("h_jet_counter_mcd"), getValFromBin(D0TaggedJets)); + registry.fill(HIST("h_d0_jet_lambda11_mcd"), angularity); + registry.fill(HIST("h_d0_jet_lambda12_mcd"), girth); + registry.fill(HIST("h_d0_jet_mass_mcd"), mjet); + collisionHasD0Jet = true; + } + + } // end jet loop + + if (collisionHasD0Jet) { + registry.fill(HIST("h_collisions_mcd"), 1.5f); // has D0 jet + } + } + + PROCESS_SWITCH(JetHFAngularityTask, processMCDChargedSubstructure, + "MC DETECTOR LEVEL: D0-tagged charged jet substructure", false); + + // Process: MC PARTICLE-LEVEL (MCP) + + void processMCPChargedSubstructure( + aod::JetMcCollision const& /*mcCollision*/, + soa::Filtered const& mcpjets, + D0CandidatesMCP const& /*candidates*/, + aod::JetParticles const&) + { + + registry.fill(HIST("h_collisions_mcp"), 0.5f); // all MC collisions + + bool collisionHasD0Jet = false; + + for (const auto& jet : mcpjets) { + + registry.fill(HIST("h_jet_counter_mcp"), getValFromBin(ChargedJets)); + registry.fill(HIST("h_jet_pt_mcp"), jet.pt()); + registry.fill(HIST("h_jet_eta_mcp"), jet.eta()); + registry.fill(HIST("h_jet_phi_mcp"), jet.phi()); + + // Jet-level quantities (particle / generator level) + auto jetParticles = jet.template tracks_as(); + auto jetCandidates = jet.template candidates_as(); + + const int nConst = static_cast(jetParticles.size()) + static_cast(jetCandidates.size()); + const float angularity = computeLambda(jet, jetParticles, jetCandidates, 1.f, 1.f); // λ_1^1 + const float girth = computeLambda(jet, jetParticles, jetCandidates, 2.f, 1.f); // λ_2^1 + const float mjet = computeJetMass(jetParticles, jetCandidates, massD0MCP); + + const std::array jetMom{jet.px(), jet.py(), jet.pz()}; + + bool hasD0 = false; + + // Generated D0 loop (with MC truth-matching info) + for (const auto& d0Particle : jetCandidates) { + + hasD0 = true; + + const std::array d0Mom{d0Particle.px(), d0Particle.py(), d0Particle.pz()}; + + const float zParallel = RecoDecay::dotProd(d0Mom, jetMom) / RecoDecay::mag2(jetMom); + const float axisDistance = jetutilities::deltaR(jet, d0Particle); + + const int8_t flagMcMatch = d0Particle.flagMcMatchGen(); + const int8_t originMc = d0Particle.originMcGen(); // 0 none, 1 prompt, 2 non-prompt + + // ---- Histograms ---- + registry.fill(HIST("h_d0_pt_mcp"), d0Particle.pt()); + registry.fill(HIST("h_d0_eta_mcp"), d0Particle.eta()); + registry.fill(HIST("h_d0_phi_mcp"), d0Particle.phi()); + registry.fill(HIST("h_d0_y_mcp"), d0Particle.y()); + registry.fill(HIST("h_d0_jet_projection_mcp"), zParallel); + registry.fill(HIST("h_d0_jet_distance_mcp"), axisDistance); + registry.fill(HIST("h_d0_origin_mcp"), static_cast(originMc)); + + if (originMc == kOriginMcPrompt) { + registry.fill(HIST("h_eff_run2_den_pr"), d0Particle.pt()); + } else if (originMc == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_run2_den_np"), d0Particle.pt()); + } + objJetMCPTable(axisDistance, + jet.pt(), + jet.eta(), + jet.phi(), + nConst, + angularity, + girth, + mjet, + zParallel, + d0Particle.pt(), + d0Particle.eta(), + d0Particle.phi(), + d0Particle.y(), + flagMcMatch, + originMc); + + } // end D0 loop + + if (hasD0) { + registry.fill(HIST("h_jet_counter_mcp"), getValFromBin(D0TaggedJets)); + registry.fill(HIST("h_d0_jet_lambda11_mcp"), angularity); + registry.fill(HIST("h_d0_jet_lambda12_mcp"), girth); + registry.fill(HIST("h_d0_jet_mass_mcp"), mjet); + collisionHasD0Jet = true; + } + + } // end jet loop + + if (collisionHasD0Jet) { + registry.fill(HIST("h_collisions_mcp"), 1.5f); // has D0 jet + } + } + + PROCESS_SWITCH(JetHFAngularityTask, processMCPChargedSubstructure, + "MC PARTICLE LEVEL: D0-tagged charged jet substructure", false); + + void processMCDChargedSubstructureMatched(aod::JetMcCollisions const& mccollisions, + aod::JetCollisionsMCD const& collisions, + D0MCDJetsMatched const& mcdjets, + D0MCPJetsMatched const& mcpjets, + D0CandidatesMCD const&, + D0CandidatesMCP const&, + aod::JetTracks const&, + aod::JetParticles const&) + { + + const int jetRBin = static_cast(std::lround(static_cast(jetR) * 100.0f)); + + for (const auto& mccollision : mccollisions) { + + registry.fill(HIST("MC_CollisionCounter"), getValFromBin(BinMCColCntr::AllMCCollisions)); + + if (std::abs(mccollision.posZ()) > vertexZCut) { + continue; + } + registry.fill(HIST("MC_CollisionCounter"), getValFromBin(BinMCColCntr::SelectedMCCollisions)); + + // const float mcWeight = mccollision.weight(); + + const auto collisionsPerMCCollision = collisions.sliceBy(collisionsPerMCCollisionPreslice, mccollision.globalIndex()); + for (const auto& collision : collisionsPerMCCollision) { + + registry.fill(HIST("MC_CollisionCounter"), getValFromBin(BinMCColCntr::AssociatedRecoCollisions)); + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || + !(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + registry.fill(HIST("MC_CollisionCounter"), getValFromBin(BinMCColCntr::SelectedAssociatedRecoCollisions)); + + const auto d0mcdJetsPerCollision = mcdjets.sliceBy(d0MCDJetsPerCollisionPreslice, collision.globalIndex()); + for (const auto& mcdjet : d0mcdJetsPerCollision) { + if (mcdjet.pt() <= jetPtMin || mcdjet.r() != jetRBin) { + continue; + } + registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::DetectorLevelJetInMCCollision)); + + auto jetTracks = mcdjet.template tracks_as(); + auto jetCandidates = mcdjet.template candidates_as(); + + const int nConst = static_cast(jetTracks.size()) + static_cast(jetCandidates.size()); + const float angularity = computeLambda(mcdjet, jetTracks, jetCandidates, 1.f, 1.f); // λ_1^1 + const float girth = computeLambda(mcdjet, jetTracks, jetCandidates, 2.f, 1.f); // λ_2^1 + const float mjet = computeJetMass(jetTracks, jetCandidates); + + const std::array jetMomMCD{mcdjet.px(), mcdjet.py(), mcdjet.pz()}; + + bool isGeoMatched = false; + float matchedPt = -1.f; + float matchedEta = -999.f; + float matchedPhi = -999.f; + float matchedDR = -1.f; + float matchedAngularity = -1.f; + float matchedGirth = -1.f; + float matchedD0Pt = -1.f; // NEW + int geoMatchedGlobalIndex = -1; + int nGeoMatches = 0; + + if (mcdjet.has_matchedJetGeo()) { + for (const auto& mcpjet : mcdjet.template matchedJetGeo_as()) { + ++nGeoMatches; + + const float dR = jetutilities::deltaR(mcdjet, mcpjet); + registry.fill(HIST("h_jet_matching_dr_mcd_allcand"), dR); + registry.fill(HIST("h_jet_pt_response_matrix_allcand"), mcdjet.pt(), mcpjet.pt()); + + if (!isGeoMatched) { + isGeoMatched = true; + matchedPt = mcpjet.pt(); + matchedEta = mcpjet.eta(); + matchedPhi = mcpjet.phi(); + matchedDR = dR; + geoMatchedGlobalIndex = mcpjet.globalIndex(); + + auto mcpjetParticles = mcpjet.template tracks_as(); + auto mcpjetCandidates = mcpjet.template candidates_as(); + matchedAngularity = computeLambda(mcpjet, mcpjetParticles, mcpjetCandidates, 1.f, 1.f); // λ_1^1 + matchedGirth = computeLambda(mcpjet, mcpjetParticles, mcpjetCandidates, 2.f, 1.f); // λ_2^1 + if (!mcpjetCandidates.empty()) { + auto matchedMcpD0 = mcpjet.template candidates_first_as(); + matchedD0Pt = matchedMcpD0.pt(); + } + } + } + } + registry.fill(HIST("h_jet_matching_ngeo_mcd"), nGeoMatches); + + bool isCandMatched = false; + int candMatchedGlobalIndex = -1; + int nCandMatches = 0; + + if (mcdjet.has_matchedJetCand()) { + for (const auto& mcpjet : mcdjet.template matchedJetCand_as()) { + ++nCandMatches; + if (!isCandMatched) { + isCandMatched = true; + candMatchedGlobalIndex = mcpjet.globalIndex(); + } + } + } + registry.fill(HIST("h_jet_matching_ncand_mcd"), nCandMatches); + + // "Clean" match: both criteria fire AND agree on the same target jet. + const bool isCleanMatched = isGeoMatched && isCandMatched && + (geoMatchedGlobalIndex == candMatchedGlobalIndex); + + registry.fill(HIST("h_jet_matching_geo_status_mcd"), isGeoMatched ? 1.5f : 0.5f); + registry.fill(HIST("h_jet_matching_cand_status_mcd"), isCandMatched ? 1.5f : 0.5f); + registry.fill(HIST("h_jet_matching_clean_mcd"), isCleanMatched ? 1.5f : 0.5f); + + if (isGeoMatched) { + const bool agree = isCandMatched && (geoMatchedGlobalIndex == candMatchedGlobalIndex); + registry.fill(HIST("h_jet_matching_geocand_disagree_mcd"), agree ? 1.5f : 0.5f); + } + + if (isCleanMatched) { + // registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); + registry.fill(HIST("h_jet_matching_dr_mcd"), matchedDR); + } + + const auto geoStatus = static_cast(isGeoMatched); + const auto candStatus = static_cast(isCandMatched); + const auto cleanStatus = static_cast(isCleanMatched); + + const int isCleanMatchedMCD = isCleanMatched ? 1 : 0; + + if (isCleanMatched) { + registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::DetectorLevelJetWithMatchedCandidate)); + } + + for (const auto& mcdD0cand : jetCandidates) { + + const std::array candMomMCD{mcdD0cand.px(), mcdD0cand.py(), mcdD0cand.pz()}; + const float zParallelMCD = RecoDecay::dotProd(candMomMCD, jetMomMCD) / RecoDecay::mag2(jetMomMCD); + const float axisDistance = jetutilities::deltaR(mcdjet, mcdD0cand); + + const int8_t flagMcMatch = mcdD0cand.flagMcMatchRec(); + const int originMcd = mcdD0cand.originMcRec(); // 0 none/bkg, 1 prompt, 2 non-prompt + + // Reflection identification: candidateSelFlag() hypothesis vs. true species + const bool isReflection = isReflectedCandidate(mcdD0cand); + const int isReflectionFlag = isReflection ? 1 : 0; + const bool isTrueSignal = (flagMcMatch != 0) && !isReflection; + + if (isReflection) { + registry.fill(HIST("h_d0_mass_mcd_reflection"), mcdD0cand.m()); + registry.fill(HIST("hSparse_d0_mcd_reflection"), + mcdD0cand.m(), mcdD0cand.pt(), mcdjet.pt(), zParallelMCD, originMcd, + angularity, girth); + } else if (isTrueSignal) { + registry.fill(HIST("h_d0_mass_mcd_signal"), mcdD0cand.m()); + registry.fill(HIST("hSparse_d0_mcd_calib"), + mcdD0cand.m(), mcdD0cand.pt(), mcdjet.pt(), zParallelMCD, originMcd, + angularity, girth); + } + + registry.fill(HIST("hSparse_d0_mcd_eff"), + mcdD0cand.m(), + mcdD0cand.pt(), + mcdjet.pt(), + zParallelMCD, + originMcd, + isCleanMatchedMCD, + angularity, + girth, + isReflectionFlag); + + // Same entry, but the match axis carries the candidate-match flag + registry.fill(HIST("hSparse_d0_mcd_eff_cand"), + mcdD0cand.m(), + mcdD0cand.pt(), + mcdjet.pt(), + zParallelMCD, + originMcd, + static_cast(isCandMatched), + angularity, + girth, + isReflectionFlag); + + // Reflection rows are a second copy of the same true D0: skip them (switchable). + const bool skipReflection = rejectReflections && isReflection; + + if (!skipReflection) { + if (originMcd == kOriginMcPrompt) { + registry.fill(HIST("h_eff_run2_num_pr"), mcdD0cand.pt()); + registry.fill(HIST("h_kineff_det_den_pr"), mcdjet.pt(), mcdD0cand.pt()); + if (isGeoMatched) { + registry.fill(HIST("h_kineff_det_num_pr"), mcdjet.pt(), mcdD0cand.pt()); + } + } else if (originMcd == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_run2_num_np"), mcdD0cand.pt()); + registry.fill(HIST("h_kineff_det_den_np"), mcdjet.pt(), mcdD0cand.pt()); + if (isGeoMatched) { + registry.fill(HIST("h_kineff_det_num_np"), mcdjet.pt(), mcdD0cand.pt()); + } + } + } + + if (isCleanMatched && !skipReflection) { + registry.fill(HIST("h_jet_pt_response_matrix"), mcdjet.pt(), matchedPt); + + // The matched particle-level jet must lie in the range covered by the denominator. + const bool matchedInDenominatorRange = (matchedPt > jetPtMin); + + if (originMcd == kOriginMcPrompt) { + registry.fill(HIST("h_jet_pt_response_matrix_pr"), mcdjet.pt(), matchedPt); + registry.fill(HIST("h_response_angularity_pr"), mcdjet.pt(), angularity, matchedPt, matchedAngularity); + registry.fill(HIST("h_response_girth_pr"), mcdjet.pt(), girth, matchedPt, matchedGirth); + registry.fill(HIST("h_response_d0pt_pr"), mcdjet.pt(), mcdD0cand.pt(), matchedPt, matchedD0Pt); + if (matchedInDenominatorRange) { + registry.fill(HIST("h_eff_num_d0pt_pr"), matchedPt, matchedD0Pt); + registry.fill(HIST("h_eff_num_angularity_pr"), matchedPt, matchedAngularity); + registry.fill(HIST("h_eff_num_girth_pr"), matchedPt, matchedGirth); + } + } else if (originMcd == kOriginMcNonPrompt) { + registry.fill(HIST("h_jet_pt_response_matrix_np"), mcdjet.pt(), matchedPt); + registry.fill(HIST("h_response_angularity_np"), mcdjet.pt(), angularity, matchedPt, matchedAngularity); + registry.fill(HIST("h_response_girth_np"), mcdjet.pt(), girth, matchedPt, matchedGirth); + registry.fill(HIST("h_response_d0pt_np"), mcdjet.pt(), mcdD0cand.pt(), matchedPt, matchedD0Pt); + if (matchedInDenominatorRange) { + registry.fill(HIST("h_eff_num_d0pt_np"), matchedPt, matchedD0Pt); + registry.fill(HIST("h_eff_num_angularity_np"), matchedPt, matchedAngularity); + registry.fill(HIST("h_eff_num_girth_np"), matchedPt, matchedGirth); + } + } + } + + objJetMCDMatchedTable(axisDistance, + mcdjet.pt(), + mcdjet.eta(), + mcdjet.phi(), + nConst, + angularity, + girth, + mjet, + zParallelMCD, + mcdD0cand.pt(), + mcdD0cand.eta(), + mcdD0cand.phi(), + mcdD0cand.m(), + mcdD0cand.y(), + mcdD0cand.mlScores()[0], + mcdD0cand.mlScores()[1], + mcdD0cand.mlScores()[2], + flagMcMatch, + originMcd, + geoStatus, + candStatus, + cleanStatus, + matchedPt, + matchedEta, + matchedPhi, + matchedDR, + matchedD0Pt); + } // end D0 candidate loop + } // end MCD jet loop + } // end reconstructed-collision loop + + const auto d0mcpJetsPerMCCollision = mcpjets.sliceBy(d0MCPJetsPerMCCollisionPreslice, mccollision.globalIndex()); + for (const auto& mcpjet : d0mcpJetsPerMCCollision) { + + if (mcpjet.pt() <= jetPtMin || mcpjet.r() != jetRBin) { + continue; + } + + registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::ParticleLevelJetInMCCollision)); + + bool mcpIsCleanMatched = false; + if (mcpjet.has_matchedJetGeo() && mcpjet.has_matchedJetCand()) { + int mcpGeoTarget = -1; + for (const auto& mcdjetGeo : mcpjet.template matchedJetGeo_as()) { + mcpGeoTarget = mcdjetGeo.globalIndex(); + break; // first geo match, mirrors the MCD-side "first match wins" convention + } + int mcpCandTarget = -1; + for (const auto& mcdjetCand : mcpjet.template matchedJetCand_as()) { + mcpCandTarget = mcdjetCand.globalIndex(); + break; + } + mcpIsCleanMatched = (mcpGeoTarget == mcpCandTarget) && (mcpGeoTarget != -1); + } + const int isCleanMatchedMCP = mcpIsCleanMatched ? 1 : 0; + + if (mcpIsCleanMatched) { + registry.fill(HIST("MC_JetCounter"), getValFromBin(BinMCJetCntr::ParticleLevelJetWithMatchedCandidate)); + } + // ---- First generated D0 of the jet only: one entry per MCP jet ---- + auto mcpD0cand = mcpjet.template candidates_first_as(); + + const int originMcp = mcpD0cand.originMcGen(); // 0 none/bkg, 1 prompt, 2 non-prompt + + const std::array jetMomMCP{mcpjet.px(), mcpjet.py(), mcpjet.pz()}; + const std::array candMomMCP{mcpD0cand.px(), mcpD0cand.py(), mcpD0cand.pz()}; + const float zParallelMCP = RecoDecay::dotProd(candMomMCP, jetMomMCP) / RecoDecay::mag2(jetMomMCP); + + auto mcpJetParticles = mcpjet.template tracks_as(); + auto mcpJetCandidates = mcpjet.template candidates_as(); + const float angularityMCP = computeLambda(mcpjet, mcpJetParticles, mcpJetCandidates, 1.f, 1.f); // λ_1^1 + const float girthMCP = computeLambda(mcpjet, mcpJetParticles, mcpJetCandidates, 2.f, 1.f); // λ_2^1 + + const int isCandMatchedMCP = mcpjet.has_matchedJetCand() ? 1 : 0; + const bool mcpIsGeoMatched = mcpjet.has_matchedJetGeo(); + + // clean-match version + registry.fill(HIST("hSparse_d0_mcp_eff"), + mcpD0cand.pt(), + mcpjet.pt(), + zParallelMCP, + originMcp, + isCleanMatchedMCP, + angularityMCP, + girthMCP); + + // candidate-match version + registry.fill(HIST("hSparse_d0_mcp_eff_cand"), + mcpD0cand.pt(), + mcpjet.pt(), + zParallelMCP, + originMcp, + isCandMatchedMCP, + angularityMCP, + girthMCP); + + if (originMcp == kOriginMcPrompt) { + registry.fill(HIST("h_eff_den_d0pt_pr"), mcpjet.pt(), mcpD0cand.pt()); + registry.fill(HIST("h_eff_den_angularity_pr"), mcpjet.pt(), angularityMCP); + registry.fill(HIST("h_eff_den_girth_pr"), mcpjet.pt(), girthMCP); + } else if (originMcp == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_den_d0pt_np"), mcpjet.pt(), mcpD0cand.pt()); + registry.fill(HIST("h_eff_den_angularity_np"), mcpjet.pt(), angularityMCP); + registry.fill(HIST("h_eff_den_girth_np"), mcpjet.pt(), girthMCP); + } + + if (originMcp == kOriginMcPrompt) { + registry.fill(HIST("h_eff_run2_den_pr"), mcpD0cand.pt()); + registry.fill(HIST("h_kineff_gen_den_pr"), mcpjet.pt(), mcpD0cand.pt()); + if (mcpIsGeoMatched) { + registry.fill(HIST("h_kineff_gen_num_pr"), mcpjet.pt(), mcpD0cand.pt()); + } + } else if (originMcp == kOriginMcNonPrompt) { + registry.fill(HIST("h_eff_run2_den_np"), mcpD0cand.pt()); + registry.fill(HIST("h_kineff_gen_den_np"), mcpjet.pt(), mcpD0cand.pt()); + if (mcpIsGeoMatched) { + registry.fill(HIST("h_kineff_gen_num_np"), mcpjet.pt(), mcpD0cand.pt()); + } + } + } // end MCP jet loop + } // end MC collision loop + } + + PROCESS_SWITCH(JetHFAngularityTask, processMCDChargedSubstructureMatched, + "MC: unified efficiency + jet matching + response matrix + reflection (needs jet-matching workflow)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/jetHadronRecoil.cxx b/PWGJE/Tasks/jetHadronRecoil.cxx index 0cfbcd2ea8d..8db28c34a44 100644 --- a/PWGJE/Tasks/jetHadronRecoil.cxx +++ b/PWGJE/Tasks/jetHadronRecoil.cxx @@ -137,8 +137,8 @@ struct JetHadronRecoil { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); jetReclusterer.isReclustering = true; diff --git a/PWGJE/Tasks/jetHadronsPid.cxx b/PWGJE/Tasks/jetHadronsPid.cxx new file mode 100644 index 00000000000..e2522ef9c23 --- /dev/null +++ b/PWGJE/Tasks/jetHadronsPid.cxx @@ -0,0 +1,3372 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jetHadronsPid.cxx +/// \brief Analysis of hadrons in jets +/// \author Leonard Lorenc, WUT Warsaw, leonard.lorenc@cern.ch +/// \author Aleksandra Mulewicz, WUT Warsaw, aleksandra.mulewicz@cern.ch +/// \author Hubert Zalewski, WUT Warsaw, hubert.zalewski@cern.ch +/// \author Janik Małgorzata malgorzata.janik@cern.ch +/// \author Daniela Ruggiano daniela.ruggiano@cern.ch + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::constants::math; +using namespace o2::framework; + +using StandardEvents = soa::Join; + +using JetEvents = soa::Join; +using MCPJetEvents = soa::Join; + +using ChargedMCDJets = soa::Join; +using ChargedMCPJets = soa::Join; + +using HadronTracks = soa::Join; +using HadronTracksMC = soa::Join; + +struct JetHadronsPid { + + HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + Configurable eventSelections{"eventSelections", "sel8+NoITSROFrameBorder+NoTimeFrameBorder+NoSameBunchPileup+IsGoodZvtxFT0vsPV", "choose event selection"}; + std::vector eventSelectionBits; + + Configurable isppRefAnalysis{"isppRefAnalysis", true, "Is ppRef analysis"}; + Configurable cfgEtaJetMax{"cfgEtaJetMax", 0.5, "max jet eta"}; + + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "Reject events near the ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "Reject events near the TF border"}; + Configurable requireVtxITSTPC{"requireVtxITSTPC", true, "Require at least one ITS-TPC matched track"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "Reject events with same-bunch pileup collisions"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "Require consistent FT0 vs PV z-vertex"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "Require at least one vertex track matched to TOF"}; + + Configurable minJetPt{"minJetPt", 10, "Minimum pt of the jet"}; + Configurable maxJetPt{"maxJetPt", 1e+06, "Maximum pt of the jet"}; + Configurable rJet{"rJet", 0.4, "Jet resolution parameter R"}; + Configurable zVtx{"zVtx", 10.0, "Maximum zVertex"}; + Configurable applyAreaCut{"applyAreaCut", false, "apply area cut"}; + Configurable minNormalizedJetArea{"minNormalizedJetArea", 0.6, "Minimum normalized area cut to reject fake jets"}; + Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from the edge"}; + + Configurable requirePvContributor{"requirePvContributor", false, "require that the track is a PV contributor"}; + Configurable minTpcNcrossedRows{"minTpcNcrossedRows", 100, "minimum number of TPC crossed pad rows"}; + Configurable minTpcNclusters{"minTpcNclusters", 110, "Minimum number of clusters in TPC"}; + Configurable minItsNclusters{"minItsNclusters", 7, "Minimum number of clusters in ITS"}; + Configurable minItsIbNclusters{"minItsIbNclusters", 3, "Minimum number of clusters in inner barrel (max 3) of ITS"}; + Configurable minChiSquareTpc{"minChiSquareTpc", 0.0, "minimum TPC chi^2/Ncls"}; + Configurable maxChiSquareTpc{"maxChiSquareTpc", 4.0, "maximum TPC chi^2/Ncls"}; + Configurable maxChiSquareIts{"maxChiSquareIts", 36.0, "maximum ITS chi^2/Ncls"}; + Configurable minPt{"minPt", 0.05, "minimum pt of the tracks"}; + Configurable maxPt{"maxPt", 4.0, "maximum pt of the tracks for PID analysis"}; + Configurable minEta{"minEta", -0.8, "minimum eta"}; + Configurable maxEta{"maxEta", +0.8, "maximum eta"}; + Configurable maxDcaxy{"maxDcaxy", 0.004, "constant term of the DCAxy cut -> |DCAxy| < maxDcaxy + dcaXyPtPart/pT, in cm"}; + Configurable maxDcaz{"maxDcaz", 0.004, "constant term of the DCAz cut -> |DCAz| < maxDcaz + dcaZPtPart/pT, in cm"}; + Configurable dcaXyPtPart{"dcaXyPtPart", 0.013, "pt-dependent term of the DCAxy cut -> |DCAxy| < maxDcaxy + dcaXyPtPart/pT"}; + Configurable dcaZPtPart{"dcaZPtPart", 0.013, "pt-dependent term of the DCAz cut -> |DCAz| < maxDcaz + dcaZPtPart/pT"}; + Configurable setMCDefaultItsParams{"setMCDefaultItsParams", true, "set MC default parameters"}; + + struct : ConfigurableGroup { + Configurable pidMethod{"pidMethod", 2, "Variable for choosing pid method"}; + Configurable rejectionSigma{"rejectionSigma", 3.0, "Rejection sigma for tof and tpc pid"}; + Configurable ptThresholdPion{"ptThresholdPion", 0.75, "Threshold for pt for different pid methods"}; + Configurable ptThresholdKaon{"ptThresholdKaon", 0.5, "Threshold for pt for different pid methods"}; + Configurable ptThresholdProton{"ptThresholdProton", 0.75, "Threshold for pt for different pid methods"}; + Configurable nSigmaCutPi{"nSigmaCutPi", 3.0, "Acceptence cut for pid methods"}; + Configurable nSigmaCutKa{"nSigmaCutKa", 3.0, "Acceptence cut for pid methods"}; + Configurable nSigmaCutPr{"nSigmaCutPr", 3.0, "Acceptence cut for pid methods"}; + Configurable minPtPion{"minPtPion", 0.2, "Minimal Pion pt"}; + Configurable maxPtPion{"maxPtPion", 4.0, "Maximum Pion pt"}; + Configurable minPtKaon{"minPtKaon", 0.3, "Minimal Kaon pt"}; + Configurable maxPtKaon{"maxPtKaon", 3.0, "Maximum Kaon pt"}; + Configurable minPtProton{"minPtProton", 0.5, "Minimal Proton pt"}; + Configurable maxPtProton{"maxPtProton", 4.0, "Maximum Proton pt"}; + } cfg; + + struct : ConfigurableGroup { + // ----------------- Kinematics ----------------- + ConfigurableAxis axisPt{"axisPt", {120, 0.0, 4.0}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisEta{"axisEta", {100, -1.0, 1.0}, "#eta"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, TwoPI}, "#varphi"}; + ConfigurableAxis axisMomentum{"axisMomentum", {300, 0.05, 30.0}, "#it{p} (GeV/#it{c})"}; + + // ----------------- Spatial / DCA ----------------- + ConfigurableAxis axisDcaXY{"axisDcaXY", {300, -0.5, 0.5}, "DCA_{xy} (cm)"}; + ConfigurableAxis axisDcaZ{"axisDcaZ", {300, -0.5, 0.5}, "DCA_{z} (cm)"}; + ConfigurableAxis axisZVtx{"axisZVtx", {200, -20.0, 20.0}, "z_{vtx} (cm)"}; + ConfigurableAxis axisVtxXY{"axisVtxXY", {200, -1.0, 1.0}, "vtx_{xy} (cm)"}; + + // ----------------- PID ----------------- + ConfigurableAxis axisTPCSignal{"axisTPCSignal", {400, 20.0, 2000.0}, "TPC d#it{E}/d#it{x} (a.u.)"}; + ConfigurableAxis axisTOFBeta{"axisTOFBeta", {240, 0.0, 1.2}, "#beta_{TOF}"}; + ConfigurableAxis axisNSigmaTPC{"axisNSigmaTPC", {200, -5.0, 5.0}, "n#sigma_{TPC}"}; + ConfigurableAxis axisNSigmaTOF{"axisNSigmaTOF", {200, -5.0, 5.0}, "n#sigma_{TOF}"}; + ConfigurableAxis axisNSigmaCOMB{"axisNSigmaCOMB", {200, 0.0, 15.0}, "n#sigma_{COMB}"}; + ConfigurableAxis axisParticleSpecies{"axisParticleSpecies", {3, 0.5, 3.5}, "Particle species"}; + + // ----------------- Cone / Jet Dynamics ----------------- + ConfigurableAxis axisDeltaEtaCone{"axisDeltaEtaCone", {80, -0.8, 0.8}, "#Delta#eta"}; + ConfigurableAxis axisDeltaPhiCone{"axisDeltaPhiCone", {80, -0.8, 0.8}, "#Delta#varphi"}; + ConfigurableAxis axisPtTrackCone{"axisPtTrackCone", {100, 0.0, 10.0}, "#it{p}_{T}^{track} (GeV/#it{c})"}; + ConfigurableAxis axisJetPt{"axisJetPt", {20, 0.0, 100.0}, "#it{p}_{T}^{jet} (GeV/#it{c})"}; + ConfigurableAxis axisJetSubPt{"axisJetSubPt", {200, 0.0, 10.0}, "#it{p}_{T}^{sub} (GeV/#it{c})"}; + ConfigurableAxis axisJetArea{"axisJetArea", {100, 0.0, 1.5}, "A_{jet}"}; + ConfigurableAxis axisJetNConst{"axisJetNConst", {101, -0.5, 100.5}, "N_{constituents}"}; + + // ----------------- Multiplicities / Counting ----------------- + ConfigurableAxis axisEventMultiplicity{"axisEventMultiplicity", {201, -0.5, 200.5}, "N_{particles/contributors}"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {31, -0.5, 30.5}, "N per jet"}; + ConfigurableAxis axisNPions{"axisNPions", {21, -0.5, 20.5}, "N_{#pi} per jet"}; + ConfigurableAxis axisNKaons{"axisNKaons", {11, -0.5, 10.5}, "N_{K} per jet"}; + ConfigurableAxis axisNProtons{"axisNProtons", {11, -0.5, 10.5}, "N_{p} per jet"}; + + // ----------------- Others ----------------- + ConfigurableAxis axisMCPdg{"axisMCPdg", {6001, -3000.5, 3000.5}, "PDG Code"}; + ConfigurableAxis axisTrackCategory{"axisTrackCategory", {2, 0.5, 2.5}, "Track category"}; + ConfigurableAxis axisEventCounter{"axisEventCounter", {1, 0.5, 1.5}, "N_{events}"}; + ConfigurableAxis axisMatchStatus{"axisMatchStatus", {3, 0.5, 3.5}, "Jet match status"}; + + } axis; + + o2::aod::ITSResponse itsResponse; + + Preslice tracksPerCollision = aod::track::collisionId; + Preslice mcParticlesPerCollision = aod::mcparticle::mcCollisionId; + Preslice mcpJetsPerMcCollision = aod::jet::mcCollisionId; + + void init(InitContext const&) + { + if (setMCDefaultItsParams) { + itsResponse.setMCDefaultParameters(); + } + + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + + // --- DATA: PURE TRACKS --- + registryData.add("data/pure/general/tpc_signal_vs_p", "TPC n#sigma_{#pi} vs transverse momentum ;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/pure/general/tof_beta_vs_p", "TOF #beta vs momentum;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + + // Pions + registryData.add("data/pure/pions/pion_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected pion candidates;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/pure/pions/pion_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected pion candidates;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryData.add("data/pure/pions/pion_pure_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected pion candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/pions/pion_pure_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected pion candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/pions/pion_pure_combined_tof_tpc", "Combined TOF+TPC n#sigma_{#pi} vs transverse momentum for PID-selected pion candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{COMB}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaCOMB}); + + registryData.add("data/pure/pions/pion_pure_pt", "Transverse-momentum distribution of PID-selected pion candidates;#it{p}_{T} (GeV/#it{c});N_{#pi}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/pions/pion_pure_eta", "Pseudorapidity distribution of PID-selected pion candidates;#eta;N_{#pi}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/pions/pion_pure_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected pion candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/pions/pion_pure_dcaz", "DCA_{z} vs transverse momentum for PID-selected pion candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/pions/pion_pure_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected pion candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + registryData.add("data/pure/pions/pos/pion_pure_pos_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{+} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/pions/pos/pion_pure_pos_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{+} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/pions/pos/pion_pure_pos_pt", "Transverse-momentum distribution of PID-selected #pi^{+} candidates;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/pions/pos/pion_pure_pos_eta", "Pseudorapidity distribution of PID-selected #pi^{+} candidates;#eta;N_{#pi^{+}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/pions/pos/pion_pure_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #pi^{+} candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/pions/pos/pion_pure_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected #pi^{+} candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/pions/pos/pion_pure_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected #pi^{+} candidates;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryData.add("data/pure/pions/pos/pion_pure_pos_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected #pi^{+} candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + registryData.add("data/pure/pions/neg/pion_pure_neg_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{-} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/pions/neg/pion_pure_neg_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{-} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/pions/neg/pion_pure_neg_pt", "Transverse-momentum distribution of PID-selected #pi^{-} candidates;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/pions/neg/pion_pure_neg_eta", "Pseudorapidity distribution of PID-selected #pi^{-} candidates;#eta;N_{#pi^{-}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/pions/neg/pion_pure_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #pi^{-} candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/pions/neg/pion_pure_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected #pi^{-} candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/pions/neg/pion_pure_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected #pi^{-} candidates;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryData.add("data/pure/pions/neg/pion_pure_neg_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected #pi^{-} candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + // Kaons + registryData.add("data/pure/kaons/kaon_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected kaon candidates;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/pure/kaons/kaon_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected kaon candidates;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryData.add("data/pure/kaons/kaon_pure_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected kaon candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/kaons/kaon_pure_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected kaon candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/kaons/kaon_pure_combined_tof_tpc", "Combined TOF+TPC n#sigma_{K} vs transverse momentum for PID-selected kaon candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{COMB}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaCOMB}); + + registryData.add("data/pure/kaons/kaon_pure_pt", "Transverse-momentum distribution of PID-selected kaon candidates;#it{p}_{T} (GeV/#it{c});N_{K}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/kaons/kaon_pure_eta", "Pseudorapidity distribution of PID-selected kaon candidates;#eta;N_{K}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/kaons/kaon_pure_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected kaon candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/kaons/kaon_pure_dcaz", "DCA_{z} vs transverse momentum for PID-selected kaon candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/kaons/kaon_pure_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected kaon candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + registryData.add("data/pure/kaons/pos/kaon_pure_pos_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K^{+} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/kaons/pos/kaon_pure_pos_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K^{+} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/kaons/pos/kaon_pure_pos_pt", "Transverse-momentum distribution of PID-selected K^{+} candidates;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/kaons/pos/kaon_pure_pos_eta", "Pseudorapidity distribution of PID-selected K^{+} candidates;#eta;N_{K^{+}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/kaons/pos/kaon_pure_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected K^{+} candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/kaons/pos/kaon_pure_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected K^{+} candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/kaons/pos/kaon_pure_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected K^{+} candidates;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryData.add("data/pure/kaons/pos/kaon_pure_pos_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected K^{+} candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + registryData.add("data/pure/kaons/neg/kaon_pure_neg_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K^{-} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/kaons/neg/kaon_pure_neg_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K^{-} candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/kaons/neg/kaon_pure_neg_pt", "Transverse-momentum distribution of PID-selected K^{-} candidates;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/kaons/neg/kaon_pure_neg_eta", "Pseudorapidity distribution of PID-selected K^{-} candidates;#eta;N_{K^{-}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/kaons/neg/kaon_pure_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected K^{-} candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/kaons/neg/kaon_pure_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected K^{-} candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/kaons/neg/kaon_pure_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected K^{-} candidates;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryData.add("data/pure/kaons/neg/kaon_pure_neg_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected K^{-} candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + // Protons + registryData.add("data/pure/protons/proton_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected proton candidates;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/pure/protons/proton_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected proton candidates;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryData.add("data/pure/protons/proton_pure_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/protons/proton_pure_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/protons/proton_pure_combined_tof_tpc", "Combined TOF+TPC n#sigma_{p} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{COMB}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaCOMB}); + + registryData.add("data/pure/protons/proton_pure_pt", "Transverse-momentum distribution of PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/protons/proton_pure_eta", "Pseudorapidity distribution of PID-selected proton candidates;#eta;N_{p}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/protons/proton_pure_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/protons/proton_pure_dcaz", "DCA_{z} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/protons/proton_pure_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected proton candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + registryData.add("data/pure/protons/pos/proton_pure_pos_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/protons/pos/proton_pure_pos_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/protons/pos/proton_pure_pos_pt", "Transverse-momentum distribution of PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/protons/pos/proton_pure_pos_eta", "Pseudorapidity distribution of PID-selected proton candidates;#eta;N_{p}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/protons/pos/proton_pure_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/protons/pos/proton_pure_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/protons/pos/proton_pure_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected proton candidates;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryData.add("data/pure/protons/pos/proton_pure_pos_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected proton candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + registryData.add("data/pure/protons/neg/proton_pure_neg_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected antiproton candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/pure/protons/neg/proton_pure_neg_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected antiproton candidates;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/pure/protons/neg/proton_pure_neg_pt", "Transverse-momentum distribution of PID-selected antiproton candidates;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/pure/protons/neg/proton_pure_neg_eta", "Pseudorapidity distribution of PID-selected antiproton candidates;#eta;N_{#bar{p}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/pure/protons/neg/proton_pure_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected antiproton candidates;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/pure/protons/neg/proton_pure_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected antiproton candidates;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/pure/protons/neg/proton_pure_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected antiproton candidates;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryData.add("data/pure/protons/neg/proton_pure_neg_eta_phi", "Azimuthal angle vs pseudorapidity for PID-selected antiproton candidates;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + + // --- DATA: COLLISIONS --- + registryData.add("data/pure/collisions/z_vertex", "Collision vertex;z_{vtx} (cm);N_{collisions}", HistType::kTH1F, {axis.axisZVtx}); + registryData.add("data/pure/collisions/x_vertex", "Collision vertex;x_{vtx} (cm);N_{collisions}", HistType::kTH1F, {axis.axisVtxXY}); + registryData.add("data/pure/collisions/y_vertex", "Collision vertex;y_{vtx} (cm);N_{collisions}", HistType::kTH1F, {axis.axisVtxXY}); + registryData.add("data/pure/collisions/xy_vertex", "Collision vertex;x_{vtx} (cm);y_{vtx} (cm)", HistType::kTH2F, {axis.axisVtxXY, axis.axisVtxXY}); + registryData.add("data/pure/collisions/n_contributors", "PV contributors;N_{contributors};N_{collisions}", HistType::kTH1I, {axis.axisEventMultiplicity}); + + // --- DATA: JETS GENERAL --- + registryData.add("data/jets/general/n_events", "Event counter", HistType::kTH1F, {axis.axisEventCounter}); + registryData.add("data/jets/general/n_events_raw", "All events", HistType::kTH1F, {axis.axisEventCounter}); + registryData.add("data/jets/general/collision_multiplicity", "Number of Jets in Collison;Number of Jets;Number of Collisions", HistType::kTH1F, {axis.axisMultiplicity}); + registryData.add("data/jets/general/z_vtx", "Z-Vertex Distribution", HistType::kTH1F, {axis.axisZVtx}); + + registryData.add("data/jets/general/jet_pt", "Jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetPt}); + registryData.add("data/jets/general/jet_pt_subtracted", "Jet #it{p}_{T} subtracted;#it{p}_{T}^{sub} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetSubPt}); + registryData.add("data/jets/general/jet_pt_raw_vs_sub", "Raw vs sub jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});#it{p}_{T}^{sub} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisJetSubPt}); + registryData.add("data/jets/general/jet_eta", "Jet #eta;#eta_{jet};N_{jets}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/jets/general/jet_phi", "Jet #varphi;#varphi_{jet};N_{jets}", HistType::kTH1F, {axis.axisPhi}); + registryData.add("data/jets/general/jet_area", "Jet area;A_{jet};N_{jets}", HistType::kTH1F, {axis.axisJetArea}); + registryData.add("data/jets/general/jet_n_constituents", "Jet multiplicity;N_{constituents};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + registryData.add("data/jets/general/jet_n_selected_constituents", "Jet multiplicity of selected tracks;N_{constituents}^{selected};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + + registryData.add("data/jets/general/tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for tracks in selected jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/jets/general/tof_beta_vs_p", "TOF #beta vs momentum for tracks in selected jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryData.add("data/jets/general/total_tracks_pure_vs_jets", "Selected tracks and tracks used in jets;Track category;N_{tracks}", HistType::kTH1F, {axis.axisTrackCategory}); + + auto hTrackTotals = registryData.get(HIST("data/jets/general/total_tracks_pure_vs_jets")); + hTrackTotals->GetXaxis()->SetBinLabel(1, "Pure tracks"); + hTrackTotals->GetXaxis()->SetBinLabel(2, "Tracks in jets"); + + // --- DATA: JET CONE --- + registryData.add("data/jets/cone/all_tracks_2d", "Track distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet}", HistType::kTH2F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone}); + + // Pions + registryData.add("data/jets/cone/pions/pion_pos_2d", "PID-selected #pi^{+} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet}", HistType::kTH2F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone}); + registryData.add("data/jets/cone/pions/pion_neg_2d", "PID-selected #pi^{-} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet}", HistType::kTH2F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone}); + + // Kaons + registryData.add("data/jets/cone/kaons/kaon_pos_2d", "PID-selected K^{+} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet}", HistType::kTH2F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone}); + registryData.add("data/jets/cone/kaons/kaon_neg_2d", "PID-selected K^{-} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet}", HistType::kTH2F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone}); + + // Protons + registryData.add("data/jets/cone/protons/proton_pos_2d", "PID-selected p distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet}", HistType::kTH2F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone}); + registryData.add("data/jets/cone/protons/proton_neg_2d", "PID-selected #bar{p} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet}", HistType::kTH2F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone}); + + // 3D Distributions + registryData.add("data/jets/cone/all_tracks", "Track distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet};#it{p}_{T}^{track} (GeV/#it{c})", HistType::kTH3F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone, axis.axisPtTrackCone}); + registryData.add("data/jets/cone/pions/pion_pos", "PID-selected #pi^{+} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet};#it{p}_{T}^{track} (GeV/#it{c})", HistType::kTH3F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone, axis.axisPtTrackCone}); + registryData.add("data/jets/cone/pions/pion_neg", "PID-selected #pi^{-} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet};#it{p}_{T}^{track} (GeV/#it{c})", HistType::kTH3F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone, axis.axisPtTrackCone}); + registryData.add("data/jets/cone/kaons/kaon_pos", "PID-selected K^{+} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet};#it{p}_{T}^{track} (GeV/#it{c})", HistType::kTH3F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone, axis.axisPtTrackCone}); + registryData.add("data/jets/cone/kaons/kaon_neg", "PID-selected K^{-} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet};#it{p}_{T}^{track} (GeV/#it{c})", HistType::kTH3F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone, axis.axisPtTrackCone}); + registryData.add("data/jets/cone/protons/proton_pos", "PID-selected p distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet};#it{p}_{T}^{track} (GeV/#it{c})", HistType::kTH3F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone, axis.axisPtTrackCone}); + registryData.add("data/jets/cone/protons/proton_neg", "PID-selected #bar{p} distribution inside selected jet cones;#Delta#eta_{track,jet};#Delta#varphi_{track,jet};#it{p}_{T}^{track} (GeV/#it{c})", HistType::kTH3F, {axis.axisDeltaEtaCone, axis.axisDeltaPhiCone, axis.axisPtTrackCone}); + + // --- DATA: JET COMPOSITION --- + + // Pions + registryData.add("data/jets/pions/n_pions_per_jet", "Multiplicity of PID-selected #pi^{#pm} in selected jets;N_{#pi};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryData.add("data/jets/pions/n_pions_vs_jet_pt", "Multiplicity of PID-selected #pi^{#pm} vs jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});N_{#pi}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryData.add("data/jets/pions/pion_pt_vs_jet_pt", "PID-selected #pi^{#pm} #it{p}_{T} vs jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryData.add("data/jets/pions/pos/n_pions_per_jet", "Multiplicity of PID-selected #pi^{+} in selected jets;N_{#pi^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryData.add("data/jets/pions/neg/n_pions_per_jet", "Multiplicity of PID-selected #pi^{-} in selected jets;N_{#pi^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + + registryData.add("data/jets/pions/pion_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected pion candidates in jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/jets/pions/pion_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected pion candidates in jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryData.add("data/jets/pions/pion_jet_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/pions/pion_jet_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + + registryData.add("data/jets/pions/pos/pion_jet_pos_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/pions/pos/pion_jet_pos_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/jets/pions/pos/pion_jet_pos_pt", "Transverse-momentum distribution of PID-selected #pi^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/jets/pions/pos/pion_jet_pos_eta", "Pseudorapidity distribution of PID-selected #pi^{+} candidates in jets;#eta;N_{#pi^{+}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/jets/pions/pos/pion_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #pi^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/jets/pions/pos/pion_jet_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected #pi^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/jets/pions/pos/pion_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected #pi^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + registryData.add("data/jets/pions/neg/pion_jet_neg_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/pions/neg/pion_jet_neg_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/jets/pions/neg/pion_jet_neg_pt", "Transverse-momentum distribution of PID-selected #pi^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/jets/pions/neg/pion_jet_neg_eta", "Pseudorapidity distribution of PID-selected #pi^{-} candidates in jets;#eta;N_{#pi^{-}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/jets/pions/neg/pion_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #pi^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/jets/pions/neg/pion_jet_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected #pi^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/jets/pions/neg/pion_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected #pi^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Kaons + registryData.add("data/jets/kaons/n_kaons_per_jet", "Multiplicity of PID-selected K^{#pm} in selected jets;N_{K};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryData.add("data/jets/kaons/n_kaons_vs_jet_pt", "Multiplicity of PID-selected K^{#pm} vs jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});N_{K}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryData.add("data/jets/kaons/kaon_pt_vs_jet_pt", "PID-selected K^{#pm} #it{p}_{T} vs jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryData.add("data/jets/kaons/pos/n_kaons_per_jet", "Multiplicity of PID-selected K^{+} in selected jets;N_{K^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryData.add("data/jets/kaons/neg/n_kaons_per_jet", "Multiplicity of PID-selected K^{-} in selected jets;N_{K^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + + registryData.add("data/jets/kaons/kaon_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected kaon candidates in jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/jets/kaons/kaon_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected kaon candidates in jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryData.add("data/jets/kaons/kaon_jet_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/kaons/kaon_jet_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + + registryData.add("data/jets/kaons/pos/kaon_jet_pos_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/kaons/pos/kaon_jet_pos_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/jets/kaons/pos/kaon_jet_pos_pt", "Transverse-momentum distribution of PID-selected K^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/jets/kaons/pos/kaon_jet_pos_eta", "Pseudorapidity distribution of PID-selected K^{+} candidates in jets;#eta;N_{K^{+}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/jets/kaons/pos/kaon_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected K^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/jets/kaons/pos/kaon_jet_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected K^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/jets/kaons/pos/kaon_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected K^{+} candidates in jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + registryData.add("data/jets/kaons/neg/kaon_jet_neg_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/kaons/neg/kaon_jet_neg_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/jets/kaons/neg/kaon_jet_neg_pt", "Transverse-momentum distribution of PID-selected K^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/jets/kaons/neg/kaon_jet_neg_eta", "Pseudorapidity distribution of PID-selected K^{-} candidates in jets;#eta;N_{K^{-}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/jets/kaons/neg/kaon_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected K^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/jets/kaons/neg/kaon_jet_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected K^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/jets/kaons/neg/kaon_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected K^{-} candidates in jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Protons + registryData.add("data/jets/protons/n_protons_per_jet", "Multiplicity of PID-selected p+#bar{p} in selected jets;N_{p+#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryData.add("data/jets/protons/n_protons_vs_jet_pt", "Multiplicity of PID-selected p+#bar{p} vs jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryData.add("data/jets/protons/proton_pt_vs_jet_pt", "PID-selected p+#bar{p} #it{p}_{T} vs jet #it{p}_{T};#it{p}_{T}^{jet} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryData.add("data/jets/protons/pos/n_protons_per_jet", "Multiplicity of PID-selected p in selected jets;N_{p};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryData.add("data/jets/protons/neg/n_protons_per_jet", "Multiplicity of PID-selected #bar{p} in selected jets;N_{#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + + registryData.add("data/jets/protons/proton_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected proton candidates in jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryData.add("data/jets/protons/proton_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected proton candidates in jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryData.add("data/jets/protons/proton_jet_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected p+#bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/protons/proton_jet_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected p+#bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + + registryData.add("data/jets/protons/pos/proton_jet_pos_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected p candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/protons/pos/proton_jet_pos_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected p candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/jets/protons/pos/proton_jet_pos_pt", "Transverse-momentum distribution of PID-selected p candidates in jets;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/jets/protons/pos/proton_jet_pos_eta", "Pseudorapidity distribution of PID-selected p candidates in jets;#eta;N_{p}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/jets/protons/pos/proton_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected p candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/jets/protons/pos/proton_jet_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected p candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/jets/protons/pos/proton_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected p candidates in jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + registryData.add("data/jets/protons/neg/proton_jet_neg_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected #bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/jets/protons/neg/proton_jet_neg_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected #bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/jets/protons/neg/proton_jet_neg_pt", "Transverse-momentum distribution of PID-selected #bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/jets/protons/neg/proton_jet_neg_eta", "Pseudorapidity distribution of PID-selected #bar{p} candidates in jets;#eta;N_{#bar{p}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/jets/protons/neg/proton_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/jets/protons/neg/proton_jet_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected #bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryData.add("data/jets/protons/neg/proton_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for PID-selected #bar{p} candidates in jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + registryData.add("data/jets/composition/particle_multiplicity_per_jet", "Identified-particle multiplicity in selected jets;Particle species;N_{particles} per jet", HistType::kTH2I, {axis.axisParticleSpecies, axis.axisMultiplicity}); + + auto hComposition = registryData.get(HIST("data/jets/composition/particle_multiplicity_per_jet")); + hComposition->GetXaxis()->SetBinLabel(1, "#pi"); + hComposition->GetXaxis()->SetBinLabel(2, "K"); + hComposition->GetXaxis()->SetBinLabel(3, "p"); + hComposition->SetOption("COLZ"); + hComposition->SetStats(false); + + // --- DATA: UNDERLYING EVENT (UE) --- + + // Pions + registryData.add("data/ue/pions/pion_ue_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected pion candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/pions/pion_ue_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected pion candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/pions/pion_ue_pt", "Transverse-momentum distribution of PID-selected pion candidates in UE;#it{p}_{T} (GeV/#it{c});N_{#pi}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/pions/pion_ue_eta", "Pseudorapidity distribution of PID-selected pion candidates in UE;#eta;N_{#pi}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/pions/pion_ue_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected pion candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/pions/pion_ue_dcaz", "DCA_{z} vs transverse momentum for PID-selected pion candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryData.add("data/ue/pions/pos/pion_ue_pos_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/pions/pos/pion_ue_pos_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/pions/pos/pion_ue_pos_pt", "Transverse-momentum distribution of PID-selected #pi^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/pions/pos/pion_ue_pos_eta", "Pseudorapidity distribution of PID-selected #pi^{+} candidates in UE;#eta;N_{#pi^{+}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/pions/pos/pion_ue_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #pi^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/pions/pos/pion_ue_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected #pi^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryData.add("data/ue/pions/neg/pion_ue_neg_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/pions/neg/pion_ue_neg_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/pions/neg/pion_ue_neg_pt", "Transverse-momentum distribution of PID-selected #pi^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/pions/neg/pion_ue_neg_eta", "Pseudorapidity distribution of PID-selected #pi^{-} candidates in UE;#eta;N_{#pi^{-}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/pions/neg/pion_ue_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #pi^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/pions/neg/pion_ue_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected #pi^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + // Kaons + registryData.add("data/ue/kaons/kaon_ue_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected kaon candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/kaons/kaon_ue_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected kaon candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/kaons/kaon_ue_pt", "Transverse-momentum distribution of PID-selected kaon candidates in UE;#it{p}_{T} (GeV/#it{c});N_{K}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/kaons/kaon_ue_eta", "Pseudorapidity distribution of PID-selected kaon candidates in UE;#eta;N_{K}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/kaons/kaon_ue_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected kaon candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/kaons/kaon_ue_dcaz", "DCA_{z} vs transverse momentum for PID-selected kaon candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryData.add("data/ue/kaons/pos/kaon_ue_pos_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/kaons/pos/kaon_ue_pos_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/kaons/pos/kaon_ue_pos_pt", "Transverse-momentum distribution of PID-selected K^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/kaons/pos/kaon_ue_pos_eta", "Pseudorapidity distribution of PID-selected K^{+} candidates in UE;#eta;N_{K^{+}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/kaons/pos/kaon_ue_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected K^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/kaons/pos/kaon_ue_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected K^{+} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryData.add("data/ue/kaons/neg/kaon_ue_neg_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/kaons/neg/kaon_ue_neg_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/kaons/neg/kaon_ue_neg_pt", "Transverse-momentum distribution of PID-selected K^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/kaons/neg/kaon_ue_neg_eta", "Pseudorapidity distribution of PID-selected K^{-} candidates in UE;#eta;N_{K^{-}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/kaons/neg/kaon_ue_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected K^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/kaons/neg/kaon_ue_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected K^{-} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + // Protons + registryData.add("data/ue/protons/proton_ue_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected proton candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/protons/proton_ue_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected proton candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/protons/proton_ue_pt", "Transverse-momentum distribution of PID-selected proton candidates in UE;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/protons/proton_ue_eta", "Pseudorapidity distribution of PID-selected proton candidates in UE;#eta;N_{p}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/protons/proton_ue_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected proton candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/protons/proton_ue_dcaz", "DCA_{z} vs transverse momentum for PID-selected proton candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryData.add("data/ue/protons/pos/proton_ue_pos_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected p candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/protons/pos/proton_ue_pos_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected p candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/protons/pos/proton_ue_pos_pt", "Transverse-momentum distribution of PID-selected p candidates in UE;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/protons/pos/proton_ue_pos_eta", "Pseudorapidity distribution of PID-selected p candidates in UE;#eta;N_{p}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/protons/pos/proton_ue_pos_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected p candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/protons/pos/proton_ue_pos_dcaz", "DCA_{z} vs transverse momentum for PID-selected p candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryData.add("data/ue/protons/neg/proton_ue_neg_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected #bar{p} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryData.add("data/ue/protons/neg/proton_ue_neg_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected #bar{p} candidates in UE;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryData.add("data/ue/protons/neg/proton_ue_neg_pt", "Transverse-momentum distribution of PID-selected #bar{p} candidates in UE;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryData.add("data/ue/protons/neg/proton_ue_neg_eta", "Pseudorapidity distribution of PID-selected #bar{p} candidates in UE;#eta;N_{#bar{p}}", HistType::kTH1F, {axis.axisEta}); + registryData.add("data/ue/protons/neg/proton_ue_neg_dcaxy", "DCA_{xy} vs transverse momentum for PID-selected #bar{p} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryData.add("data/ue/protons/neg/proton_ue_neg_dcaz", "DCA_{z} vs transverse momentum for PID-selected #bar{p} candidates in UE;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryData.add("data/ue/general/tracks_n_in_ue", "Number of tracks in UE;N_{tracks};Counts", HistType::kTH1I, {{100, 0, 100, "N_{tracks}"}}); + + // ========================================================================= + // --- MC REGISTRY --- + // ========================================================================= + + // --- MC: TRUTH --- + + registryMC.add("mc/truth/general/n_events", "Selected particle-level events;Event counter;N_{events}", HistType::kTH1I, {axis.axisEventCounter}); + registryMC.add("mc/truth/general/z_vtx", "Particle-level Z-vertex distribution;z_{vtx} (cm);N_{events}", HistType::kTH1F, {axis.axisZVtx}); + + // Pions + registryMC.add("mc/truth/pions/mc_gen_pion_pt", "Transverse-momentum distribution of generated primary #pi^{#pm};#it{p}_{T} (GeV/#it{c});N_{#pi}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/truth/pions/pos/mc_gen_pion_pos_pt", "Transverse-momentum distribution of generated primary #pi^{+};#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/truth/pions/neg/mc_gen_pion_neg_pt", "Transverse-momentum distribution of generated primary #pi^{-};#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Kaons + registryMC.add("mc/truth/kaons/mc_gen_kaon_pt", "Transverse-momentum distribution of generated primary K^{#pm};#it{p}_{T} (GeV/#it{c});N_{K}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/truth/kaons/pos/mc_gen_kaon_pos_pt", "Transverse-momentum distribution of generated primary K^{+};#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/truth/kaons/neg/mc_gen_kaon_neg_pt", "Transverse-momentum distribution of generated primary K^{-};#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Protons + registryMC.add("mc/truth/protons/mc_gen_proton_pt", "Transverse-momentum distribution of generated primary p+#bar{p};#it{p}_{T} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/truth/protons/pos/mc_gen_proton_pos_pt", "Transverse-momentum distribution of generated primary p;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/truth/protons/neg/mc_gen_proton_neg_pt", "Transverse-momentum distribution of generated primary #bar{p};#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + + // Hadrons + registryMC.add("mc/truth/hadrons/mc_gen_hadron_pt", "Transverse-momentum distribution of generated primary hadrons;#it{p}_{T} (GeV/#it{c});N_{hadrons}", HistType::kTH1F, {axis.axisPt}); + + // --- MC: MATCHED RECONSTRUCTION --- + registryMC.add("mc/matched/general/n_events", "Event counter;Selection;N_{events}", HistType::kTH1F, {axis.axisEventCounter}); + registryMC.add("mc/matched/general/z_vertex", "Collision vertex;z_{vtx} (cm);N_{collisions}", HistType::kTH1F, {axis.axisZVtx}); + registryMC.add("mc/matched/general/x_vertex", "Collision vertex;x_{vtx} (cm);N_{collisions}", HistType::kTH1F, {axis.axisVtxXY}); + registryMC.add("mc/matched/general/y_vertex", "Collision vertex;y_{vtx} (cm);N_{collisions}", HistType::kTH1F, {axis.axisVtxXY}); + registryMC.add("mc/matched/general/xy_vertex", "Collision vertex;x_{vtx} (cm);y_{vtx} (cm)", HistType::kTH2F, {axis.axisVtxXY, axis.axisVtxXY}); + registryMC.add("mc/matched/general/n_contributors", "PV contributors;N_{contributors};N_{collisions}", HistType::kTH1I, {axis.axisEventMultiplicity}); + + // General Hadrons + registryMC.add("mc/matched/reconstruction/hadrons/rec_hadron_all", "All reconstructed hadrons;#it{p}_{T} (GeV/#it{c});N_{tracks}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/hadrons/rec_hadron_tof_match", "TOF-matched reconstructed hadrons;#it{p}_{T} (GeV/#it{c});N_{tracks}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/hadrons/mc_rec_hadron_pt", "True primary reconstructed hadrons;#it{p}_{T} (GeV/#it{c});N_{tracks}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/hadrons/mc_sec_hadron_pt", "Secondary/fake reconstructed hadrons;#it{p}_{T} (GeV/#it{c});N_{tracks}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/general/tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for reconstructed tracks;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/matched/reconstruction/general/tof_beta_vs_p", "TOF #beta vs momentum for reconstructed tracks;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + + // Pions + registryMC.add("mc/matched/reconstruction/pions/rec_pion_all", "All PID #pi;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/contamination_matrix_pion", "PID #pi contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/mc_rec_pion_pt", "True primary #pi;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/mc_sec_pion_pt", "Secondary/fake #pi;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/pion_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for reconstructed #pi;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/matched/reconstruction/pions/pion_tof_beta_vs_p", "TOF #beta vs momentum for reconstructed #pi;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/matched/reconstruction/pions/rec_pion_eta", "Pseudorapidity distribution of reconstructed #pi;#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/pions/rec_pion_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed #pi;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/pions/rec_pion_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed #pi;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/pions/rec_pion_dcaz", "DCA_{z} vs transverse momentum for reconstructed #pi;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_all", "All PID #pi^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/pos/contamination_matrix_pion_pos", "PID #pi^{+} contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_tpc", "TPC n#sigma_{#pi} vs transverse momentum for reconstructed #pi^{+};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_tof", "TOF n#sigma_{#pi} vs transverse momentum for reconstructed #pi^{+};#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_tof_matched", "TOF-matched reconstructed #pi^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/pos/mc_rec_pion_pos_pt", "True primary #pi^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/pos/mc_sec_pion_pos_pt", "Secondary/fake #pi^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_eta", "Pseudorapidity distribution of reconstructed #pi^{+};#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed #pi^{+};#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed #pi^{+};#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_dcaz", "DCA_{z} vs transverse momentum for reconstructed #pi^{+};#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/matched/reconstruction/pions/pos/rec_pion_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for reconstructed #pi^{+};#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_all", "All PID #pi^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/neg/contamination_matrix_pion_neg", "PID #pi^{-} contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_tpc", "TPC n#sigma_{#pi} vs transverse momentum for reconstructed #pi^{-};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_tof", "TOF n#sigma_{#pi} vs transverse momentum for reconstructed #pi^{-};#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_tof_matched", "TOF-matched reconstructed #pi^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/neg/mc_rec_pion_neg_pt", "True primary #pi^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/neg/mc_sec_pion_neg_pt", "Secondary/fake #pi^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_eta", "Pseudorapidity distribution of reconstructed #pi^{-};#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed #pi^{-};#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed #pi^{-};#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_dcaz", "DCA_{z} vs transverse momentum for reconstructed #pi^{-};#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/matched/reconstruction/pions/neg/rec_pion_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for reconstructed #pi^{-};#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Kaons + registryMC.add("mc/matched/reconstruction/kaons/rec_kaon_all", "All PID K;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/contamination_matrix_kaon", "PID K contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/mc_rec_kaon_pt", "True primary K;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/mc_sec_kaon_pt", "Secondary/fake K;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/kaon_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for reconstructed K;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/matched/reconstruction/kaons/kaon_tof_beta_vs_p", "TOF #beta vs momentum for reconstructed K;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/matched/reconstruction/kaons/rec_kaon_eta", "Pseudorapidity distribution of reconstructed K;#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/kaons/rec_kaon_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed K;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/kaons/rec_kaon_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed K;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/kaons/rec_kaon_dcaz", "DCA_{z} vs transverse momentum for reconstructed K;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_all", "All PID K^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/pos/contamination_matrix_kaon_pos", "PID K^{+} contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_tpc", "TPC n#sigma_{K} vs transverse momentum for reconstructed K^{+};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_tof", "TOF n#sigma_{K} vs transverse momentum for reconstructed K^{+};#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_tof_matched", "TOF-matched reconstructed K^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/pos/mc_rec_kaon_pos_pt", "True primary K^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/pos/mc_sec_kaon_pos_pt", "Secondary/fake K^{+};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_eta", "Pseudorapidity distribution of reconstructed K^{+};#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed K^{+};#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed K^{+};#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_dcaz", "DCA_{z} vs transverse momentum for reconstructed K^{+};#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for reconstructed K^{+};#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_all", "All PID K^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/neg/contamination_matrix_kaon_neg", "PID K^{-} contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_tpc", "TPC n#sigma_{K} vs transverse momentum for reconstructed K^{-};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_tof", "TOF n#sigma_{K} vs transverse momentum for reconstructed K^{-};#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_tof_matched", "TOF-matched reconstructed K^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/neg/mc_rec_kaon_neg_pt", "True primary K^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/neg/mc_sec_kaon_neg_pt", "Secondary/fake K^{-};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_eta", "Pseudorapidity distribution of reconstructed K^{-};#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed K^{-};#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed K^{-};#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_dcaz", "DCA_{z} vs transverse momentum for reconstructed K^{-};#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for reconstructed K^{-};#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Protons + registryMC.add("mc/matched/reconstruction/protons/rec_proton_all", "All PID p;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/contamination_matrix_proton", "PID p contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/mc_rec_proton_pt", "True primary p;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/mc_sec_proton_pt", "Secondary/fake p;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/proton_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for reconstructed p+#bar{p};#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/matched/reconstruction/protons/proton_tof_beta_vs_p", "TOF #beta vs momentum for reconstructed p+#bar{p};#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/matched/reconstruction/protons/rec_proton_eta", "Pseudorapidity distribution of reconstructed p+#bar{p};#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/protons/rec_proton_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed p+#bar{p};#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/protons/rec_proton_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed p+#bar{p};#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/protons/rec_proton_dcaz", "DCA_{z} vs transverse momentum for reconstructed p+#bar{p};#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_all", "All PID p;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/pos/contamination_matrix_proton_pos", "PID p contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_tpc", "TPC n#sigma_{p} vs transverse momentum for reconstructed p;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_tof", "TOF n#sigma_{p} vs transverse momentum for reconstructed p;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_tof_matched", "TOF-matched reconstructed p;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/pos/mc_rec_proton_pos_pt", "True primary p;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/pos/mc_sec_proton_pos_pt", "Secondary/fake p;#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_eta", "Pseudorapidity distribution of reconstructed p;#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed p;#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed p;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_dcaz", "DCA_{z} vs transverse momentum for reconstructed p;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/matched/reconstruction/protons/pos/rec_proton_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for reconstructed p;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_all", "All PID #bar{p};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/neg/contamination_matrix_proton_neg", "PID #bar{p} contamination matrix;PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_tpc", "TPC n#sigma_{p} vs transverse momentum for reconstructed #bar{p};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_tof", "TOF n#sigma_{p} vs transverse momentum for reconstructed #bar{p};#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_tof_matched", "TOF-matched reconstructed #bar{p};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/neg/mc_rec_proton_neg_pt", "True primary #bar{p};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/neg/mc_sec_proton_neg_pt", "Secondary/fake #bar{p};#it{p}_{T} (GeV/#it{c});N", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_eta", "Pseudorapidity distribution of reconstructed #bar{p};#eta;N", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_eta_phi", "Azimuthal angle vs pseudorapidity for reconstructed #bar{p};#varphi;#eta", HistType::kTH2F, {axis.axisPhi, axis.axisEta}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_dcaxy", "DCA_{xy} vs transverse momentum for reconstructed #bar{p};#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_dcaz", "DCA_{z} vs transverse momentum for reconstructed #bar{p};#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/matched/reconstruction/protons/neg/rec_proton_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for reconstructed #bar{p};#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // --- MC: MATCHED TRUTH --- + + // Pions + registryMC.add("mc/matched/truth/pions/mc_gen_pion_pt", "Transverse-momentum distribution of generated primary #pi^{#pm};#it{p}_{T} (GeV/#it{c});N_{#pi}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/truth/pions/pos/mc_gen_pion_pos_pt", "Transverse-momentum distribution of generated primary #pi^{+};#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/truth/pions/neg/mc_gen_pion_neg_pt", "Transverse-momentum distribution of generated primary #pi^{-};#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Kaons + registryMC.add("mc/matched/truth/kaons/mc_gen_kaon_pt", "Transverse-momentum distribution of generated primary K^{#pm};#it{p}_{T} (GeV/#it{c});N_{K}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/truth/kaons/pos/mc_gen_kaon_pos_pt", "Transverse-momentum distribution of generated primary K^{+};#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/truth/kaons/neg/mc_gen_kaon_neg_pt", "Transverse-momentum distribution of generated primary K^{-};#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Protons + registryMC.add("mc/matched/truth/protons/mc_gen_proton_pt", "Transverse-momentum distribution of generated primary p+#bar{p};#it{p}_{T} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/truth/protons/pos/mc_gen_proton_pos_pt", "Transverse-momentum distribution of generated primary p;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/matched/truth/protons/neg/mc_gen_proton_neg_pt", "Transverse-momentum distribution of generated primary #bar{p};#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + + // Hadrons + registryMC.add("mc/matched/truth/hadrons/mc_gen_hadron_pt", "Transverse-momentum distribution of generated primary hadrons;#it{p}_{T} (GeV/#it{c});N_{hadrons}", HistType::kTH1F, {axis.axisPt}); + + // --- MC: TRUTH JETS (PARTICLE LEVEL) --- + + // General Jet QA + registryMC.add("mc/jets/truth/general/n_events", "Selected particle-level jet events;Event counter;N_{events}", HistType::kTH1I, {axis.axisEventCounter}); + registryMC.add("mc/jets/truth/general/n_events_raw", "All events", HistType::kTH1I, {axis.axisEventCounter}); + registryMC.add("mc/jets/truth/general/collision_multiplicity", "Number of particle-level jets in collision;Number of Jets;Number of Collisions", HistType::kTH1F, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/general/jet_pt", "Transverse-momentum distribution of particle-level charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetPt}); + registryMC.add("mc/jets/truth/general/jet_eta", "Pseudorapidity distribution of particle-level charged jets;#eta_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/truth/general/jet_phi", "Azimuthal-angle distribution of particle-level charged jets;#varphi_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisPhi}); + registryMC.add("mc/jets/truth/general/jet_n_constituents", "Constituent multiplicity of particle-level charged jets;N_{constituents}^{part};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + registryMC.add("mc/jets/truth/general/jet_area", "Area distribution of particle-level charged jets;A_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisJetArea}); + registryMC.add("mc/jets/truth/general/jet_n_selected_constituents", "Selected-particle multiplicity in particle-level charged jets;N_{constituents}^{selected};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + registryMC.add("mc/jets/truth/general/z_vtx", "Particle-level Z-vertex distribution;z_{vtx} (cm);N_{events}", HistType::kTH1F, {axis.axisZVtx}); + registryMC.add("mc/jets/truth/general/jet_pt_subtracted", "Particle-level jet #it{p}_{T} after background subtraction;#it{p}_{T,jet}^{part,sub} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetSubPt}); + registryMC.add("mc/jets/truth/general/jet_pt_raw_vs_sub", "Particle-level raw vs subtracted jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,jet}^{part,sub} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisJetSubPt}); + + // Pions + registryMC.add("mc/jets/truth/pions/n_pions_per_jet", "Multiplicity of generated #pi^{#pm} in particle-level jets;N_{#pi};N_{jets}", HistType::kTH1D, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/pions/n_pions_vs_jet_pt", "Multiplicity of generated #pi^{#pm} vs particle-level jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{#pi}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/pions/pion_pt_vs_jet_pt", "Generated #pi^{#pm} #it{p}_{T} vs particle-level jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/truth/pions/mc_gen_pion_pt", "Transverse-momentum distribution of generated primary #pi^{#pm} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{#pi}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/truth/pions/pos/n_pions_per_jet", "Multiplicity of generated #pi^{+} in particle-level jets;N_{#pi^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/pions/pos/mc_gen_pion_pos_pt", "Transverse-momentum distribution of generated primary #pi^{+} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/truth/pions/neg/n_pions_per_jet", "Multiplicity of generated #pi^{-} in particle-level jets;N_{#pi^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/pions/neg/mc_gen_pion_neg_pt", "Transverse-momentum distribution of generated primary #pi^{-} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Kaons + registryMC.add("mc/jets/truth/kaons/n_kaons_per_jet", "Multiplicity of generated K^{#pm} in particle-level jets;N_{K};N_{jets}", HistType::kTH1D, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/kaons/n_kaons_vs_jet_pt", "Multiplicity of generated K^{#pm} vs particle-level jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{K}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/kaons/kaon_pt_vs_jet_pt", "Generated K^{#pm} #it{p}_{T} vs particle-level jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/truth/kaons/mc_gen_kaon_pt", "Transverse-momentum distribution of generated primary K^{#pm} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{K}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/truth/kaons/pos/n_kaons_per_jet", "Multiplicity of generated K^{+} in particle-level jets;N_{K^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/kaons/pos/mc_gen_kaon_pos_pt", "Transverse-momentum distribution of generated primary K^{+} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/truth/kaons/neg/n_kaons_per_jet", "Multiplicity of generated K^{-} in particle-level jets;N_{K^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/kaons/neg/mc_gen_kaon_neg_pt", "Transverse-momentum distribution of generated primary K^{-} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Protons + registryMC.add("mc/jets/truth/protons/n_protons_per_jet", "Multiplicity of generated p+#bar{p} in particle-level jets;N_{p+#bar{p}};N_{jets}", HistType::kTH1D, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/protons/n_protons_vs_jet_pt", "Multiplicity of generated p+#bar{p} vs particle-level jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/protons/proton_pt_vs_jet_pt", "Generated p+#bar{p} #it{p}_{T} vs particle-level jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/truth/protons/mc_gen_proton_pt", "Transverse-momentum distribution of generated primary p+#bar{p} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/truth/protons/pos/n_protons_per_jet", "Multiplicity of generated p in particle-level jets;N_{p};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/protons/pos/mc_gen_proton_pos_pt", "Transverse-momentum distribution of generated primary p in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/truth/protons/neg/n_protons_per_jet", "Multiplicity of generated #bar{p} in particle-level jets;N_{#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/truth/protons/neg/mc_gen_proton_neg_pt", "Transverse-momentum distribution of generated primary #bar{p} in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/truth/composition/particle_multiplicity_per_jet", "Identified-particle multiplicity in particle-level jets;Particle species;N_{particles} per jet", HistType::kTH2I, {axis.axisParticleSpecies, axis.axisMultiplicity}); + + // General Hadrons + registryMC.add("mc/jets/truth/hadrons/mc_gen_hadron_pt", "Transverse-momentum distribution of generated primary hadrons in particle-level jets;#it{p}_{T} (GeV/#it{c});N_{hadrons}", HistType::kTH1F, {axis.axisPt}); + + // --- MC: MATCHED RECONSTRUCTED JETS --- + + // General Jet QA + registryMC.add("mc/jets/matched/reconstructed/general/n_events", "Selected matched events with reconstructed charged jets;Event counter;N_{events}", HistType::kTH1I, {axis.axisEventCounter}); + registryMC.add("mc/jets/matched/reconstructed/general/n_events_raw", "All events", HistType::kTH1I, {axis.axisEventCounter}); + registryMC.add("mc/jets/matched/reconstructed/general/collision_multiplicity", "Number of matched jets in collision;Number of Jets;Number of Collisions", HistType::kTH1F, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_pt", "Transverse-momentum distribution of matched reconstructed charged jets;#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetPt}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_pt_subtracted", "Matched reconstructed jet #it{p}_{T} after background subtraction;#it{p}_{T,jet}^{rec,sub} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetSubPt}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_pt_raw_vs_sub", "Raw vs subtracted matched reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});#it{p}_{T,jet}^{rec,sub} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisJetSubPt}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_eta", "Pseudorapidity distribution of matched reconstructed charged jets;#eta_{jet}^{rec};N_{jets}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_phi", "Azimuthal-angle distribution of matched reconstructed charged jets;#varphi_{jet}^{rec};N_{jets}", HistType::kTH1F, {axis.axisPhi}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_n_constituents", "Constituent multiplicity of matched reconstructed charged jets;N_{constituents}^{rec};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_area", "Area distribution of matched reconstructed charged jets;A_{jet}^{rec};N_{jets}", HistType::kTH1F, {axis.axisJetArea}); + registryMC.add("mc/jets/matched/reconstructed/general/jet_n_selected_constituents", "Selected-track multiplicity in matched reconstructed charged jets;N_{constituents}^{selected};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + + // Hadrons in jets + registryMC.add("mc/jets/matched/reconstructed/hadrons/rec_hadron_all", "All reconstructed hadrons in matched jets;#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/hadrons/mc_rec_hadron_pt", "True primary reconstructed hadrons in matched jets;#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/hadrons/mc_sec_hadron_pt", "Secondary/fake reconstructed hadrons in matched jets;#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + + // Composition + registryMC.add("mc/jets/matched/reconstructed/composition/n_pions_per_jet", "Multiplicity of PID-selected #pi^{#pm} in matched MC jets;N_{#pi};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/composition/n_kaons_per_jet", "Multiplicity of PID-selected K^{#pm} in matched MC jets;N_{K};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/composition/n_protons_per_jet", "Multiplicity of PID-selected p+#bar{p} in matched MC jets;N_{p+#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/composition/particle_multiplicity_per_jet", "Identified-particle multiplicity in matched MC jets;Particle species;N_{particles} per jet", HistType::kTH2I, {axis.axisParticleSpecies, axis.axisMultiplicity}); + + // Truth vs Reconstructed Matrices + registryMC.add("mc/jets/matched/reconstructed/composition/n_pions_matrix", "Truth vs reconstructed pion multiplicity;N_{truth #pi};N_{reconstructed #pi};N_{jets}", HistType::kTH2I, {axis.axisMultiplicity, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/composition/n_kaons_matrix", "Truth vs reconstructed kaon multiplicity;N_{truth K};N_{reconstructed K};N_{jets}", HistType::kTH2I, {axis.axisMultiplicity, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/composition/n_protons_matrix", "Truth vs reconstructed proton multiplicity;N_{truth p};N_{reconstructed p};N_{jets}", HistType::kTH2I, {axis.axisMultiplicity, axis.axisMultiplicity}); + + registryMC.add("mc/jets/matched/reconstructed/composition/response_pions", "Pion response matrix;#it{p}_{T,jet}^{part} (GeV/#it{c});N_{truth #pi};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{reconstructed #pi}", HistType::kTHnSparseF, {axis.axisJetPt, axis.axisMultiplicity, axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/composition/response_kaons", "Kaon response matrix;#it{p}_{T,jet}^{part} (GeV/#it{c});N_{truth K};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{reconstructed K}", HistType::kTHnSparseF, {axis.axisJetPt, axis.axisMultiplicity, axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/composition/response_protons", "Proton response matrix;#it{p}_{T,jet}^{part} (GeV/#it{c});N_{truth p};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{reconstructed p}", HistType::kTHnSparseF, {axis.axisJetPt, axis.axisMultiplicity, axis.axisJetPt, axis.axisMultiplicity}); + + registryMC.add("mc/jets/matched/reconstructed/composition/jet_pt_matrix", "Truth vs reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T,jet}^{rec} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisJetPt}); + + // Matching efficiency / fake-rate counter + registryMC.add("mc/jets/matched/general/jet_match_counter", "Jet matching outcome;Match status;N_{jets}", HistType::kTH1I, {axis.axisMatchStatus}); + auto hMatchCounter = registryMC.get(HIST("mc/jets/matched/general/jet_match_counter")); + hMatchCounter->GetXaxis()->SetBinLabel(1, "matched"); + hMatchCounter->GetXaxis()->SetBinLabel(2, "missed"); + hMatchCounter->GetXaxis()->SetBinLabel(3, "fake"); + + // --- MC: MATCHED JETS - MISS (truth jets with no reconstructed partner) --- + registryMC.add("mc/jets/matched/miss/general/jet_pt", "Transverse-momentum distribution of missed particle-level jets;#it{p}_{T,jet}^{part} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetPt}); + registryMC.add("mc/jets/matched/miss/general/jet_eta", "Pseudorapidity distribution of missed particle-level jets;#eta_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/miss/general/jet_phi", "Azimuthal-angle distribution of missed particle-level jets;#varphi_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisPhi}); + registryMC.add("mc/jets/matched/miss/general/jet_area", "Area distribution of missed particle-level jets;A_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisJetArea}); + registryMC.add("mc/jets/matched/miss/general/jet_n_constituents", "Constituent multiplicity of missed particle-level jets;N_{constituents}^{part};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + registryMC.add("mc/jets/matched/miss/general/jet_n_selected_constituents", "Selected-particle multiplicity in missed particle-level jets;N_{constituents}^{selected};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + + registryMC.add("mc/jets/matched/miss/pions/n_pions_per_jet", "Multiplicity of generated #pi^{#pm} in missed jets;N_{#pi};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/pions/n_pions_vs_jet_pt", "Multiplicity of generated #pi^{#pm} vs missed jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{#pi}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/pions/pion_pt_vs_jet_pt", "Generated #pi^{#pm} #it{p}_{T} vs missed jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/miss/pions/mc_gen_pion_pt", "Transverse-momentum distribution of generated primary #pi^{#pm} in missed jets;#it{p}_{T} (GeV/#it{c});N_{#pi}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/miss/pions/pos/n_pions_per_jet", "Multiplicity of generated #pi^{+} in missed jets;N_{#pi^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/pions/pos/mc_gen_pion_pos_pt", "Transverse-momentum distribution of generated primary #pi^{+} in missed jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/miss/pions/neg/n_pions_per_jet", "Multiplicity of generated #pi^{-} in missed jets;N_{#pi^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/pions/neg/mc_gen_pion_neg_pt", "Transverse-momentum distribution of generated primary #pi^{-} in missed jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + + registryMC.add("mc/jets/matched/miss/kaons/n_kaons_per_jet", "Multiplicity of generated K^{#pm} in missed jets;N_{K};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/kaons/n_kaons_vs_jet_pt", "Multiplicity of generated K^{#pm} vs missed jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{K}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/kaons/kaon_pt_vs_jet_pt", "Generated K^{#pm} #it{p}_{T} vs missed jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/miss/kaons/mc_gen_kaon_pt", "Transverse-momentum distribution of generated primary K^{#pm} in missed jets;#it{p}_{T} (GeV/#it{c});N_{K}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/miss/kaons/pos/n_kaons_per_jet", "Multiplicity of generated K^{+} in missed jets;N_{K^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/kaons/pos/mc_gen_kaon_pos_pt", "Transverse-momentum distribution of generated primary K^{+} in missed jets;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/miss/kaons/neg/n_kaons_per_jet", "Multiplicity of generated K^{-} in missed jets;N_{K^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/kaons/neg/mc_gen_kaon_neg_pt", "Transverse-momentum distribution of generated primary K^{-} in missed jets;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + + registryMC.add("mc/jets/matched/miss/protons/n_protons_per_jet", "Multiplicity of generated p+#bar{p} in missed jets;N_{p+#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/protons/n_protons_vs_jet_pt", "Multiplicity of generated p+#bar{p} vs missed jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/protons/proton_pt_vs_jet_pt", "Generated p+#bar{p} #it{p}_{T} vs missed jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/miss/protons/mc_gen_proton_pt", "Transverse-momentum distribution of generated primary p+#bar{p} in missed jets;#it{p}_{T} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/miss/protons/pos/n_protons_per_jet", "Multiplicity of generated p in missed jets;N_{p};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/protons/pos/mc_gen_proton_pos_pt", "Transverse-momentum distribution of generated primary p in missed jets;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/miss/protons/neg/n_protons_per_jet", "Multiplicity of generated #bar{p} in missed jets;N_{#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/miss/protons/neg/mc_gen_proton_neg_pt", "Transverse-momentum distribution of generated primary #bar{p} in missed jets;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + + registryMC.add("mc/jets/matched/miss/hadrons/mc_gen_hadron_pt", "Transverse-momentum distribution of generated primary hadrons in missed jets;#it{p}_{T} (GeV/#it{c});N_{hadrons}", HistType::kTH1F, {axis.axisPt}); + + // --- MC: MATCHED JETS - FAKE (reconstructed jets with no truth partner) --- + registryMC.add("mc/jets/matched/fake/general/jet_pt", "Transverse-momentum distribution of fake reconstructed jets;#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetPt}); + registryMC.add("mc/jets/matched/fake/general/jet_eta", "Pseudorapidity distribution of fake reconstructed jets;#eta_{jet}^{rec};N_{jets}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/fake/general/jet_phi", "Azimuthal-angle distribution of fake reconstructed jets;#varphi_{jet}^{rec};N_{jets}", HistType::kTH1F, {axis.axisPhi}); + registryMC.add("mc/jets/matched/fake/general/jet_area", "Area distribution of fake reconstructed jets;A_{jet}^{rec};N_{jets}", HistType::kTH1F, {axis.axisJetArea}); + registryMC.add("mc/jets/matched/fake/general/jet_n_constituents", "Constituent multiplicity of fake reconstructed jets;N_{constituents}^{rec};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + registryMC.add("mc/jets/matched/fake/general/jet_n_selected_constituents", "Selected-track multiplicity in fake reconstructed jets;N_{constituents}^{selected};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + + registryMC.add("mc/jets/matched/fake/hadrons/rec_hadron_all", "All reconstructed hadrons in fake jets;#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/hadrons/mc_rec_hadron_pt", "True primary reconstructed hadrons in fake jets;#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/hadrons/mc_sec_hadron_pt", "Secondary/fake reconstructed hadrons in fake jets;#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + + registryMC.add("mc/jets/matched/fake/composition/n_pions_per_jet", "Multiplicity of PID-selected #pi^{#pm} in fake jets;N_{#pi};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/composition/n_kaons_per_jet", "Multiplicity of PID-selected K^{#pm} in fake jets;N_{K};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/composition/n_protons_per_jet", "Multiplicity of PID-selected p+#bar{p} in fake jets;N_{p+#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/composition/particle_multiplicity_per_jet", "Identified-particle multiplicity in fake jets;Particle species;N_{particles} per jet", HistType::kTH2I, {axis.axisParticleSpecies, axis.axisMultiplicity}); + + // Pions + registryMC.add("mc/jets/matched/fake/pions/n_pions_vs_jet_pt", "Multiplicity of PID-selected #pi^{#pm} vs fake jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{#pi}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/pions/pion_pt_vs_jet_pt", "Reconstructed #pi^{#pm} #it{p}_{T} vs fake jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pos/n_pions_per_jet", "Multiplicity of PID-selected #pi^{+} in fake jets;N_{#pi^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/pions/neg/n_pions_per_jet", "Multiplicity of PID-selected #pi^{-} in fake jets;N_{#pi^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/pions/rec_pion_all", "All reconstructed pions #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/contamination_matrix_pion", "Pion contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/mc_rec_pion_pt", "MC true primary pions #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/mc_sec_pion_pt", "MC secondary/fake pions #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pion_jet_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi candidates in fake jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/pions/pion_jet_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi candidates in fake jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/pions/pion_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected pion candidates in fake jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/jets/matched/fake/pions/pion_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected pion candidates in fake jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/jets/matched/fake/pions/pos/rec_pion_pos_all", "All reconstructed #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pos/contamination_matrix_pion_pos", "#pi^{+} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pos/pion_jet_pos_tpc", "#pi^{+} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/pions/pos/rec_pion_pos_tof_matched", "TOF-matched reconstructed #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pos/pion_jet_pos_tof", "#pi^{+} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/pions/pos/mc_rec_pion_pos_pt", "MC true primary #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pos/mc_sec_pion_pos_pt", "MC secondary/fake #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pos/pion_jet_pos_pt", "Transverse-momentum distribution of #pi^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/pos/pion_jet_pos_eta", "Pseudorapidity distribution of #pi^{+} candidates in fake jets;#eta;N_{#pi^{+}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/fake/pions/pos/pion_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for #pi^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/fake/pions/pos/pion_jet_pos_dcaz", "DCA_{z} vs transverse momentum for #pi^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/fake/pions/pos/pion_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for #pi^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryMC.add("mc/jets/matched/fake/pions/neg/rec_pion_neg_all", "All reconstructed #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/neg/contamination_matrix_pion_neg", "#pi^{-} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/neg/pion_jet_neg_tpc", "#pi^{-} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/pions/neg/rec_pion_neg_tof_matched", "TOF-matched reconstructed #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/neg/pion_jet_neg_tof", "#pi^{-} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/pions/neg/mc_rec_pion_neg_pt", "MC true primary #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/neg/mc_sec_pion_neg_pt", "MC secondary/fake #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/neg/pion_jet_neg_pt", "Transverse-momentum distribution of #pi^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/pions/neg/pion_jet_neg_eta", "Pseudorapidity distribution of #pi^{-} candidates in fake jets;#eta;N_{#pi^{-}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/fake/pions/neg/pion_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for #pi^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/fake/pions/neg/pion_jet_neg_dcaz", "DCA_{z} vs transverse momentum for #pi^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/fake/pions/neg/pion_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for #pi^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Kaons + registryMC.add("mc/jets/matched/fake/kaons/n_kaons_vs_jet_pt", "Multiplicity of PID-selected K^{#pm} vs fake jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{K}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/kaons/kaon_pt_vs_jet_pt", "Reconstructed K^{#pm} #it{p}_{T} vs fake jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/pos/n_kaons_per_jet", "Multiplicity of PID-selected K^{+} in fake jets;N_{K^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/kaons/neg/n_kaons_per_jet", "Multiplicity of PID-selected K^{-} in fake jets;N_{K^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/kaons/rec_kaon_all", "All reconstructed kaons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/contamination_matrix_kaon", "Kaon contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/mc_rec_kaon_pt", "MC true primary kaons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/mc_sec_kaon_pt", "MC secondary/fake kaons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/kaon_jet_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K candidates in fake jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/kaons/kaon_jet_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K candidates in fake jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/kaons/kaon_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected kaon candidates in fake jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/jets/matched/fake/kaons/kaon_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected kaon candidates in fake jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/jets/matched/fake/kaons/pos/rec_kaon_pos_all", "All reconstructed K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/pos/contamination_matrix_kaon_pos", "K^{+} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_tpc", "K^{+} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/kaons/pos/rec_kaon_pos_tof_matched", "TOF-matched reconstructed K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_tof", "K^{+} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/kaons/pos/mc_rec_kaon_pos_pt", "MC true primary K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/pos/mc_sec_kaon_pos_pt", "MC secondary/fake K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_pt", "Transverse-momentum distribution of K^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_eta", "Pseudorapidity distribution of K^{+} candidates in fake jets;#eta;N_{K^{+}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for K^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_dcaz", "DCA_{z} vs transverse momentum for K^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for K^{+} candidates in fake jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryMC.add("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_all", "All reconstructed K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/neg/contamination_matrix_kaon_neg", "K^{-} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_tpc", "K^{-} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_tof_matched", "TOF-matched reconstructed K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_tof", "K^{-} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/kaons/neg/mc_rec_kaon_neg_pt", "MC true primary K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/neg/mc_sec_kaon_neg_pt", "MC secondary/fake K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_pt", "Transverse-momentum distribution of K^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_eta", "Pseudorapidity distribution of K^{-} candidates in fake jets;#eta;N_{K^{-}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for K^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_dcaz", "DCA_{z} vs transverse momentum for K^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for K^{-} candidates in fake jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Protons + registryMC.add("mc/jets/matched/fake/protons/n_protons_vs_jet_pt", "Multiplicity of PID-selected p+#bar{p} vs fake jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/protons/proton_pt_vs_jet_pt", "Reconstructed p+#bar{p} #it{p}_{T} vs fake jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/pos/n_protons_per_jet", "Multiplicity of PID-selected p in fake jets;N_{p};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/protons/neg/n_protons_per_jet", "Multiplicity of PID-selected #bar{p} in fake jets;N_{#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/fake/protons/rec_proton_all", "All reconstructed protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/contamination_matrix_proton", "Proton contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/mc_rec_proton_pt", "MC true primary protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/mc_sec_proton_pt", "MC secondary/fake protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/proton_jet_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected p+#bar{p} candidates in fake jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/protons/proton_jet_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected p+#bar{p} candidates in fake jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/protons/proton_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected proton candidates in fake jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/jets/matched/fake/protons/proton_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected proton candidates in fake jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/jets/matched/fake/protons/pos/rec_proton_pos_all", "All reconstructed protons (positive) #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/pos/contamination_matrix_proton_pos", "Proton (positive) contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/pos/proton_jet_pos_tpc", "Proton TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/protons/pos/rec_proton_pos_tof_matched", "TOF-matched reconstructed protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/pos/proton_jet_pos_tof", "Proton TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/protons/pos/mc_rec_proton_pos_pt", "MC true primary protons (positive) #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/pos/mc_sec_proton_pos_pt", "MC secondary/fake protons (positive) #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/pos/proton_jet_pos_pt", "Transverse-momentum distribution of proton candidates in fake jets;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/pos/proton_jet_pos_eta", "Pseudorapidity distribution of proton candidates in fake jets;#eta;N_{p}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/fake/protons/pos/proton_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for proton candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/fake/protons/pos/proton_jet_pos_dcaz", "DCA_{z} vs transverse momentum for proton candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/fake/protons/pos/proton_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for proton candidates in fake jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryMC.add("mc/jets/matched/fake/protons/neg/rec_proton_neg_all", "All reconstructed antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/neg/contamination_matrix_proton_neg", "Antiproton contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/neg/proton_jet_neg_tpc", "Antiproton TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/fake/protons/neg/rec_proton_neg_tof_matched", "TOF-matched reconstructed antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/neg/proton_jet_neg_tof", "Antiproton TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/fake/protons/neg/mc_rec_proton_neg_pt", "MC true primary antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/neg/mc_sec_proton_neg_pt", "MC secondary/fake antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/neg/proton_jet_neg_pt", "Transverse-momentum distribution of antiproton candidates in fake jets;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/fake/protons/neg/proton_jet_neg_eta", "Pseudorapidity distribution of antiproton candidates in fake jets;#eta;N_{#bar{p}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/fake/protons/neg/proton_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for antiproton candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/fake/protons/neg/proton_jet_neg_dcaz", "DCA_{z} vs transverse momentum for antiproton candidates in fake jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/fake/protons/neg/proton_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for antiproton candidates in fake jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Pions + registryMC.add("mc/jets/matched/reconstructed/pions/n_pions_vs_jet_pt", "Multiplicity of PID-selected #pi^{#pm} vs matched reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{#pi}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/pions/pion_pt_vs_jet_pt", "Reconstructed #pi^{#pm} #it{p}_{T} vs matched reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/n_pions_per_jet", "Multiplicity of PID-selected #pi^{+} in matched MC jets;N_{#pi^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/n_pions_per_jet", "Multiplicity of PID-selected #pi^{-} in matched MC jets;N_{#pi^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/pions/rec_pion_all", "All reconstructed pions #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/contamination_matrix_pion", "Pion contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/mc_rec_pion_pt", "MC true primary pions #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/mc_sec_pion_pt", "MC secondary/fake pions #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pion_jet_tpc", "TPC n#sigma_{#pi} vs transverse momentum for PID-selected #pi candidates in matched jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/pions/pion_jet_tof", "TOF n#sigma_{#pi} vs transverse momentum for PID-selected #pi candidates in matched jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/pions/pion_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected pion candidates in matched jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/jets/matched/reconstructed/pions/pion_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected pion candidates in matched jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/rec_pion_pos_all", "All reconstructed #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/contamination_matrix_pion_pos", "#pi^{+} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_tpc", "#pi^{+} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/rec_pion_pos_tof_matched", "TOF-matched reconstructed #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_tof", "#pi^{+} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/mc_rec_pion_pos_pt", "MC true primary #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/mc_sec_pion_pos_pt", "MC secondary/fake #pi^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_pt", "Transverse-momentum distribution of #pi^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_eta", "Pseudorapidity distribution of #pi^{+} candidates in matched MC jets;#eta;N_{#pi^{+}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for #pi^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_dcaz", "DCA_{z} vs transverse momentum for #pi^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for #pi^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/rec_pion_neg_all", "All reconstructed #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/contamination_matrix_pion_neg", "#pi^{-} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_tpc", "#pi^{-} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/rec_pion_neg_tof_matched", "TOF-matched reconstructed #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_tof", "#pi^{-} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{#pi}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/mc_rec_pion_neg_pt", "MC true primary #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/mc_sec_pion_neg_pt", "MC secondary/fake #pi^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_pt", "Transverse-momentum distribution of #pi^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_eta", "Pseudorapidity distribution of #pi^{-} candidates in matched MC jets;#eta;N_{#pi^{-}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for #pi^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_dcaz", "DCA_{z} vs transverse momentum for #pi^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for #pi^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Kaons + registryMC.add("mc/jets/matched/reconstructed/kaons/n_kaons_vs_jet_pt", "Multiplicity of PID-selected K^{#pm} vs matched reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{K}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/kaons/kaon_pt_vs_jet_pt", "Reconstructed K^{#pm} #it{p}_{T} vs matched reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/n_kaons_per_jet", "Multiplicity of PID-selected K^{+} in matched MC jets;N_{K^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/n_kaons_per_jet", "Multiplicity of PID-selected K^{-} in matched MC jets;N_{K^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/kaons/rec_kaon_all", "All reconstructed kaons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/contamination_matrix_kaon", "Kaon contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/mc_rec_kaon_pt", "MC true primary kaons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/mc_sec_kaon_pt", "MC secondary/fake kaons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/kaon_jet_tpc", "TPC n#sigma_{K} vs transverse momentum for PID-selected K candidates in matched jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/kaons/kaon_jet_tof", "TOF n#sigma_{K} vs transverse momentum for PID-selected K candidates in matched jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/kaons/kaon_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected kaon candidates in matched jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/jets/matched/reconstructed/kaons/kaon_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected kaon candidates in matched jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/rec_kaon_pos_all", "All reconstructed K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/contamination_matrix_kaon_pos", "K^{+} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_tpc", "K^{+} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/rec_kaon_pos_tof_matched", "TOF-matched reconstructed K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_tof", "K^{+} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/mc_rec_kaon_pos_pt", "MC true primary K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/mc_sec_kaon_pos_pt", "MC secondary/fake K^{+} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_pt", "Transverse-momentum distribution of K^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_eta", "Pseudorapidity distribution of K^{+} candidates in matched MC jets;#eta;N_{K^{+}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for K^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_dcaz", "DCA_{z} vs transverse momentum for K^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for K^{+} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_all", "All reconstructed K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/contamination_matrix_kaon_neg", "K^{-} contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_tpc", "K^{-} TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_tof_matched", "TOF-matched reconstructed K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_tof", "K^{-} TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{K}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/mc_rec_kaon_neg_pt", "MC true primary K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/mc_sec_kaon_neg_pt", "MC secondary/fake K^{-} #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_pt", "Transverse-momentum distribution of K^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_eta", "Pseudorapidity distribution of K^{-} candidates in matched MC jets;#eta;N_{K^{-}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for K^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_dcaz", "DCA_{z} vs transverse momentum for K^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for K^{-} candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // Protons + registryMC.add("mc/jets/matched/reconstructed/protons/n_protons_vs_jet_pt", "Multiplicity of PID-selected p+#bar{p} vs matched reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/protons/proton_pt_vs_jet_pt", "Reconstructed p+#bar{p} #it{p}_{T} vs matched reconstructed jet #it{p}_{T};#it{p}_{T,jet}^{rec} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/n_protons_per_jet", "Multiplicity of PID-selected p in matched MC jets;N_{p};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/n_protons_per_jet", "Multiplicity of PID-selected #bar{p} in matched MC jets;N_{#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/reconstructed/protons/rec_proton_all", "All reconstructed protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/contamination_matrix_proton", "Proton contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/mc_rec_proton_pt", "MC true primary protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/mc_sec_proton_pt", "MC secondary/fake protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/proton_jet_tpc", "TPC n#sigma_{p} vs transverse momentum for PID-selected p+#bar{p} candidates in matched jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/protons/proton_jet_tof", "TOF n#sigma_{p} vs transverse momentum for PID-selected p+#bar{p} candidates in matched jets;#it{p}_{T} (GeV/#it{c});n#sigma_{TOF}", HistType::kTH2F, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/protons/proton_tpc_signal_vs_p", "TPC d#it{E}/d#it{x} signal vs momentum for PID-selected proton candidates in matched jets;#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (a.u.)", HistType::kTH2F, {axis.axisMomentum, axis.axisTPCSignal}); + registryMC.add("mc/jets/matched/reconstructed/protons/proton_tof_beta_vs_p", "TOF #beta vs momentum for PID-selected proton candidates in matched jets;#it{p} (GeV/#it{c});#beta_{TOF}", HistType::kTH2F, {axis.axisMomentum, axis.axisTOFBeta}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/rec_proton_pos_all", "All reconstructed protons (positive) #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/contamination_matrix_proton_pos", "Proton (positive) contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_tpc", "Proton TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/rec_proton_pos_tof_matched", "TOF-matched reconstructed protons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_tof", "Proton TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/mc_rec_proton_pos_pt", "MC true primary protons (positive) #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/mc_sec_proton_pos_pt", "MC secondary/fake protons (positive) #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_pt", "Transverse-momentum distribution of proton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_eta", "Pseudorapidity distribution of proton candidates in matched MC jets;#eta;N_{p}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_dcaxy", "DCA_{xy} vs transverse momentum for proton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_dcaz", "DCA_{z} vs transverse momentum for proton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_phi_vs_pt", "Azimuthal angle vs transverse momentum for proton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/rec_proton_neg_all", "All reconstructed antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/contamination_matrix_proton_neg", "Antiproton contamination matrix;True PDG Code;#it{p}_{T} (GeV/#it{c})", HistType::kTH2D, {axis.axisMCPdg, axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_tpc", "Antiproton TPC n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TPC n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTPC}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/rec_proton_neg_tof_matched", "TOF-matched reconstructed antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_tof", "Antiproton TOF n#sigma vs #it{p}_{T};#it{p}_{T} (GeV/#it{c});TOF n#sigma_{p}", HistType::kTH2D, {axis.axisPt, axis.axisNSigmaTOF}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/mc_rec_proton_neg_pt", "MC true primary antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/mc_sec_proton_neg_pt", "MC secondary/fake antiprotons #it{p}_{T};#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_pt", "Transverse-momentum distribution of antiproton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_eta", "Pseudorapidity distribution of antiproton candidates in matched MC jets;#eta;N_{#bar{p}}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_dcaxy", "DCA_{xy} vs transverse momentum for antiproton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaXY}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_dcaz", "DCA_{z} vs transverse momentum for antiproton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});DCA_{z} (cm)", HistType::kTH2F, {axis.axisPt, axis.axisDcaZ}); + registryMC.add("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_phi_vs_pt", "Azimuthal angle vs transverse momentum for antiproton candidates in matched MC jets;#it{p}_{T} (GeV/#it{c});#varphi", HistType::kTH2F, {axis.axisPt, axis.axisPhi}); + + // --- MC: MATCHED TRUTH JETS --- + + // General Jet QA + registryMC.add("mc/jets/matched/truth/general/jet_pt", "Transverse-momentum distribution of particle-level matched charged jets;#it{p}_{T,jet}^{part} (GeV/#it{c});N_{jets}", HistType::kTH1F, {axis.axisJetPt}); + registryMC.add("mc/jets/matched/truth/general/jet_eta", "Pseudorapidity distribution of particle-level matched charged jets;#eta_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisEta}); + registryMC.add("mc/jets/matched/truth/general/jet_phi", "Azimuthal-angle distribution of particle-level matched charged jets;#varphi_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisPhi}); + registryMC.add("mc/jets/matched/truth/general/jet_n_constituents", "Constituent multiplicity of particle-level matched charged jets;N_{constituents}^{part};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + registryMC.add("mc/jets/matched/truth/general/jet_area", "Area distribution of particle-level matched charged jets;A_{jet}^{part};N_{jets}", HistType::kTH1F, {axis.axisJetArea}); + registryMC.add("mc/jets/matched/truth/general/jet_n_selected_constituents", "Selected-particle multiplicity in particle-level matched charged jets;N_{constituents}^{selected};N_{jets}", HistType::kTH1I, {axis.axisJetNConst}); + + // Pions + registryMC.add("mc/jets/matched/truth/pions/n_pions_per_jet", "Multiplicity of generated #pi^{#pm} in particle-level matched jets;N_{#pi};N_{jets}", HistType::kTH1D, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/pions/n_pions_vs_jet_pt", "Multiplicity of generated #pi^{#pm} vs particle-level matched jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{#pi}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/pions/pion_pt_vs_jet_pt", "Generated #pi^{#pm} #it{p}_{T} vs particle-level matched jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/truth/pions/mc_gen_pion_pt", "Transverse-momentum distribution of generated primary #pi^{#pm} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{#pi}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/truth/pions/pos/n_pions_per_jet", "Multiplicity of generated #pi^{+} in particle-level matched jets;N_{#pi^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/pions/pos/mc_gen_pion_pos_pt", "Transverse-momentum distribution of generated primary #pi^{+} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/truth/pions/neg/n_pions_per_jet", "Multiplicity of generated #pi^{-} in particle-level matched jets;N_{#pi^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/pions/neg/mc_gen_pion_neg_pt", "Transverse-momentum distribution of generated primary #pi^{-} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{#pi^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Kaons + registryMC.add("mc/jets/matched/truth/kaons/n_kaons_per_jet", "Multiplicity of generated K^{#pm} in particle-level matched jets;N_{K};N_{jets}", HistType::kTH1D, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/kaons/n_kaons_vs_jet_pt", "Multiplicity of generated K^{#pm} vs particle-level matched jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{K}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/kaons/kaon_pt_vs_jet_pt", "Generated K^{#pm} #it{p}_{T} vs particle-level matched jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/truth/kaons/mc_gen_kaon_pt", "Transverse-momentum distribution of generated primary K^{#pm} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{K}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/truth/kaons/pos/n_kaons_per_jet", "Multiplicity of generated K^{+} in particle-level matched jets;N_{K^{+}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/kaons/pos/mc_gen_kaon_pos_pt", "Transverse-momentum distribution of generated primary K^{+} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{K^{+}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/truth/kaons/neg/n_kaons_per_jet", "Multiplicity of generated K^{-} in particle-level matched jets;N_{K^{-}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/kaons/neg/mc_gen_kaon_neg_pt", "Transverse-momentum distribution of generated primary K^{-} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{K^{-}}", HistType::kTH1F, {axis.axisPt}); + + // Protons + registryMC.add("mc/jets/matched/truth/protons/n_protons_per_jet", "Multiplicity of generated p+#bar{p} in particle-level matched jets;N_{p+#bar{p}};N_{jets}", HistType::kTH1D, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/protons/n_protons_vs_jet_pt", "Multiplicity of generated p+#bar{p} vs particle-level matched jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH2I, {axis.axisJetPt, axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/protons/proton_pt_vs_jet_pt", "Generated p+#bar{p} #it{p}_{T} vs particle-level matched jet #it{p}_{T};#it{p}_{T,jet}^{part} (GeV/#it{c});#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {axis.axisJetPt, axis.axisPt}); + registryMC.add("mc/jets/matched/truth/protons/mc_gen_proton_pt", "Transverse-momentum distribution of generated primary p+#bar{p} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{p+#bar{p}}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/truth/protons/pos/n_protons_per_jet", "Multiplicity of generated p in particle-level matched jets;N_{p};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/protons/pos/mc_gen_proton_pos_pt", "Transverse-momentum distribution of generated primary p in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{p}", HistType::kTH1F, {axis.axisPt}); + registryMC.add("mc/jets/matched/truth/protons/neg/n_protons_per_jet", "Multiplicity of generated #bar{p} in particle-level matched jets;N_{#bar{p}};N_{jets}", HistType::kTH1I, {axis.axisMultiplicity}); + registryMC.add("mc/jets/matched/truth/protons/neg/mc_gen_proton_neg_pt", "Transverse-momentum distribution of generated primary #bar{p} in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{#bar{p}}", HistType::kTH1F, {axis.axisPt}); + + // General Hadrons + registryMC.add("mc/jets/matched/truth/hadrons/mc_gen_hadron_pt", "Transverse-momentum distribution of generated primary hadrons in particle-level matched jets;#it{p}_{T} (GeV/#it{c});N_{hadrons}", HistType::kTH1F, {axis.axisPt}); + } + + void getPerpendicularDirections(const TVector3& p, TVector3& u1, TVector3& u2) + { + double const threshold = 1e-9; + + if (p.Pt() < threshold) { + u1.SetXYZ(0, 0, 0); + u2.SetXYZ(0, 0, 0); + return; + } + + double pt = p.Pt(); + double eta = p.Eta(); + double phi = p.Phi(); + + u1.SetPtEtaPhi(pt, eta, phi + PIHalf); + u2.SetPtEtaPhi(pt, eta, phi - PIHalf); + } + + template + bool hasITSLayerHit(const TrackIts& track, int layer) + { + return TESTBIT(track.itsClusterMap(), layer - 1); + } + + struct PidResult { + bool isPion, isKaon, isProton; + }; + + template + PidResult getPid(const TrackType& track) + { + constexpr int ClosestMatch = 0; + constexpr int ExclusiveMatch = 1; + constexpr int RejectionBased = 2; + constexpr int NoRejection = 3; + + double const buffer = 999.0; + double pt = track.pt(); + + double dForPionsPi = 0; + double dForPionsKa = 0; + double dForPionsPr = 0; + + double dForKaonsPi = 0; + double dForKaonsKa = 0; + double dForKaonsPr = 0; + + double dForProtonsPi = 0; + double dForProtonsKa = 0; + double dForProtonsPr = 0; + + if (pt < cfg.ptThresholdPion) { + dForPionsPi = std::abs(track.tpcNSigmaPi()); + dForPionsKa = std::abs(track.tpcNSigmaKa()); + dForPionsPr = std::abs(track.tpcNSigmaPr()); + } else { + if (track.hasTOF()) { + dForPionsPi = std::hypot(track.tofNSigmaPi(), track.tpcNSigmaPi()); + dForPionsKa = std::hypot(track.tofNSigmaKa(), track.tpcNSigmaKa()); + dForPionsPr = std::hypot(track.tofNSigmaPr(), track.tpcNSigmaPr()); + } else { + dForPionsPi = buffer; + dForPionsKa = buffer; + dForPionsPr = buffer; + } + } + + if (pt < cfg.ptThresholdKaon) { + dForKaonsPi = std::abs(track.tpcNSigmaPi()); + dForKaonsKa = std::abs(track.tpcNSigmaKa()); + dForKaonsPr = std::abs(track.tpcNSigmaPr()); + } else { + if (track.hasTOF()) { + dForKaonsPi = std::hypot(track.tofNSigmaPi(), track.tpcNSigmaPi()); + dForKaonsKa = std::hypot(track.tofNSigmaKa(), track.tpcNSigmaKa()); + dForKaonsPr = std::hypot(track.tofNSigmaPr(), track.tpcNSigmaPr()); + } else { + dForKaonsPi = buffer; + dForKaonsKa = buffer; + dForKaonsPr = buffer; + } + } + + if (pt < cfg.ptThresholdProton) { + dForProtonsPi = std::abs(track.tpcNSigmaPi()); + dForProtonsKa = std::abs(track.tpcNSigmaKa()); + dForProtonsPr = std::abs(track.tpcNSigmaPr()); + } else { + if (track.hasTOF()) { + dForProtonsPi = std::hypot(track.tofNSigmaPi(), track.tpcNSigmaPi()); + dForProtonsKa = std::hypot(track.tofNSigmaKa(), track.tpcNSigmaKa()); + dForProtonsPr = std::hypot(track.tofNSigmaPr(), track.tpcNSigmaPr()); + } else { + dForProtonsPi = buffer; + dForProtonsKa = buffer; + dForProtonsPr = buffer; + } + } + + bool isPiMatch = (dForPionsPi <= cfg.nSigmaCutPi); + bool isKaMatch = (dForKaonsKa <= cfg.nSigmaCutKa); + bool isPrMatch = (dForProtonsPr <= cfg.nSigmaCutPr); + + PidResult res{.isPion = false, .isKaon = false, .isProton = false}; + if (cfg.pidMethod == NoRejection) { + if (isPiMatch) { + res.isPion = true; + } + if (isKaMatch) { + res.isKaon = true; + } + if (isPrMatch) { + res.isProton = true; + } + } else if (cfg.pidMethod == ClosestMatch) { + if (isPiMatch && dForPionsPi < dForPionsKa && dForPionsPi < dForPionsPr) { + res.isPion = true; + } + if (isKaMatch && dForKaonsKa < dForKaonsPi && dForKaonsKa < dForKaonsPr) { + res.isKaon = true; + } + if (isPrMatch && dForProtonsPr < dForProtonsPi && dForProtonsPr < dForProtonsKa) { + res.isProton = true; + } + } else if (cfg.pidMethod == ExclusiveMatch) { + if (isPiMatch && !isKaMatch && !isPrMatch) { + res.isPion = true; + } + if (isKaMatch && !isPiMatch && !isPrMatch) { + res.isKaon = true; + } + if (isPrMatch && !isPiMatch && !isKaMatch) { + res.isProton = true; + } + } else if (cfg.pidMethod == RejectionBased) { + if (isKaMatch && dForKaonsPi > cfg.rejectionSigma && dForKaonsPr > cfg.rejectionSigma) { + res.isKaon = true; + } + if (isPiMatch && dForPionsKa > cfg.rejectionSigma && dForPionsPr > cfg.rejectionSigma) { + res.isPion = true; + } + if (isPrMatch && dForProtonsPi > cfg.rejectionSigma && dForProtonsKa > cfg.rejectionSigma) { + res.isProton = true; + } + } + + if (res.isPion && (pt < cfg.minPtPion || pt > cfg.maxPtPion)) { + res.isPion = false; + } + if (res.isKaon && (pt < cfg.minPtKaon || pt > cfg.maxPtKaon)) { + res.isKaon = false; + } + if (res.isProton && (pt < cfg.minPtProton || pt > cfg.maxPtProton)) { + res.isProton = false; + } + + return res; + } + + template + bool passedTrackSelection(const TrackType& track) + { + if (requirePvContributor && !(track.isPVContributor())) { + return false; + } + if (!track.hasITS() || !track.hasTPC()) { + return false; + } + if ((!hasITSLayerHit(track, 1)) && (!hasITSLayerHit(track, 2)) && (!hasITSLayerHit(track, 3))) { + return false; + } + if (track.tpcNClsCrossedRows() < minTpcNcrossedRows) { + return false; + } + if (track.tpcNClsFound() < minTpcNclusters) { + return false; + } + if (track.itsNCls() < minItsNclusters) { + return false; + } + if (track.itsNClsInnerBarrel() < minItsIbNclusters) { + return false; + } + if (!track.passedTPCCrossedRowsOverNCls()) { + return false; + } + if (track.tpcChi2NCl() < minChiSquareTpc || track.tpcChi2NCl() > maxChiSquareTpc) { + return false; + } + if (track.itsChi2NCl() > maxChiSquareIts) { + return false; + } + if (!track.passedGoldenChi2()) { + return false; + } + if (track.eta() < minEta || track.eta() > maxEta) { + return false; + } + if (track.pt() < minPt || track.pt() > maxPt) { + return false; + } + const double maxDcaxyCut = maxDcaxy + dcaXyPtPart / track.pt(); + const double maxDcazCut = maxDcaz + dcaZPtPart / track.pt(); + if (std::abs(track.dcaXY()) > maxDcaxyCut || std::abs(track.dcaZ()) > maxDcazCut) { + return false; + } + return true; + } + + void processPureTracks(StandardEvents::iterator const& collision, HadronTracks const& globalTracks) + { + if (!collision.sel8() || std::abs(collision.posZ()) > zVtx) { + return; + } + if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return; + } + if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return; + } + if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return; + } + if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return; + } + if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return; + } + if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return; + } + + int nSelectedTracks = 0; + + registryData.fill(HIST("data/pure/collisions/z_vertex"), collision.posZ()); + registryData.fill(HIST("data/pure/collisions/x_vertex"), collision.posX()); + registryData.fill(HIST("data/pure/collisions/y_vertex"), collision.posY()); + registryData.fill(HIST("data/pure/collisions/xy_vertex"), collision.posX(), collision.posY()); + registryData.fill(HIST("data/pure/collisions/n_contributors"), collision.numContrib()); + + for (auto const& track : globalTracks) { + if (!passedTrackSelection(track)) { + continue; + } + + ++nSelectedTracks; + double pt = track.pt(); + double eta = track.eta(); + double dcaxy = track.dcaXY(); + double dcaz = track.dcaZ(); + int charge = track.sign(); + double phi = track.phi(); + + const double p = track.p(); + registryData.fill(HIST("data/pure/general/tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryData.fill(HIST("data/pure/general/tof_beta_vs_p"), p, track.beta()); + } + + PidResult pid = getPid(track); + + if (pid.isPion) { + registryData.fill(HIST("data/pure/pions/pion_tpc_signal_vs_p"), p, track.tpcSignal()); + registryData.fill(HIST("data/pure/pions/pion_pure_tpc"), pt, track.tpcNSigmaPi()); + registryData.fill(HIST("data/pure/pions/pion_pure_eta_phi"), phi, eta); + if (track.hasTOF()) { + registryData.fill( + HIST("data/pure/pions/pion_tof_beta_vs_p"), + p, + track.beta()); + registryData.fill(HIST("data/pure/pions/pion_pure_tof"), pt, track.tofNSigmaPi()); + registryData.fill(HIST("data/pure/pions/pion_pure_combined_tof_tpc"), pt, std::hypot(track.tofNSigmaPi(), track.tpcNSigmaPi())); + } + registryData.fill(HIST("data/pure/pions/pion_pure_pt"), pt); + registryData.fill(HIST("data/pure/pions/pion_pure_eta"), eta); + registryData.fill(HIST("data/pure/pions/pion_pure_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/pions/pion_pure_dcaz"), pt, dcaz); + + if (charge > 0) { + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_tpc"), pt, track.tpcNSigmaPi()); + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_eta_phi"), phi, eta); + + if (track.hasTOF()) { + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_tof"), pt, track.tofNSigmaPi()); + } + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_pt"), pt); + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_eta"), eta); + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/pions/pos/pion_pure_pos_dcaz"), pt, dcaz); + } else { + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_tpc"), pt, track.tpcNSigmaPi()); + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_eta_phi"), phi, eta); + + if (track.hasTOF()) { + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_tof"), pt, track.tofNSigmaPi()); + } + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_pt"), pt); + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_eta"), eta); + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/pions/neg/pion_pure_neg_dcaz"), pt, dcaz); + } + } + + if (pid.isKaon) { + registryData.fill(HIST("data/pure/kaons/kaon_tpc_signal_vs_p"), track.p(), track.tpcSignal()); + registryData.fill(HIST("data/pure/kaons/kaon_pure_tpc"), pt, track.tpcNSigmaKa()); + registryData.fill(HIST("data/pure/kaons/kaon_pure_eta_phi"), phi, eta); + if (track.hasTOF()) { + registryData.fill( + HIST("data/pure/kaons/kaon_tof_beta_vs_p"), + p, + track.beta()); + registryData.fill(HIST("data/pure/kaons/kaon_pure_tof"), pt, track.tofNSigmaKa()); + registryData.fill(HIST("data/pure/kaons/kaon_pure_combined_tof_tpc"), pt, std::hypot(track.tofNSigmaKa(), track.tpcNSigmaKa())); + } + registryData.fill(HIST("data/pure/kaons/kaon_pure_pt"), pt); + registryData.fill(HIST("data/pure/kaons/kaon_pure_eta"), eta); + registryData.fill(HIST("data/pure/kaons/kaon_pure_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/kaons/kaon_pure_dcaz"), pt, dcaz); + + if (charge > 0) { + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_tpc"), pt, track.tpcNSigmaKa()); + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_eta_phi"), phi, eta); + + if (track.hasTOF()) { + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_tof"), pt, track.tofNSigmaKa()); + } + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_pt"), pt); + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_eta"), eta); + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/kaons/pos/kaon_pure_pos_dcaz"), pt, dcaz); + } else { + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_tpc"), pt, track.tpcNSigmaKa()); + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_eta_phi"), phi, eta); + + if (track.hasTOF()) { + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_tof"), pt, track.tofNSigmaKa()); + } + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_pt"), pt); + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_eta"), eta); + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/kaons/neg/kaon_pure_neg_dcaz"), pt, dcaz); + } + } + + if (pid.isProton) { + registryData.fill(HIST("data/pure/protons/proton_tpc_signal_vs_p"), track.p(), track.tpcSignal()); + registryData.fill(HIST("data/pure/protons/proton_pure_tpc"), pt, track.tpcNSigmaPr()); + registryData.fill(HIST("data/pure/protons/proton_pure_eta_phi"), phi, eta); + if (track.hasTOF()) { + registryData.fill( + HIST("data/pure/protons/proton_tof_beta_vs_p"), + p, + track.beta()); + registryData.fill(HIST("data/pure/protons/proton_pure_tof"), pt, track.tofNSigmaPr()); + registryData.fill(HIST("data/pure/protons/proton_pure_combined_tof_tpc"), pt, std::hypot(track.tofNSigmaPr(), track.tpcNSigmaPr())); + } + registryData.fill(HIST("data/pure/protons/proton_pure_pt"), pt); + registryData.fill(HIST("data/pure/protons/proton_pure_eta"), eta); + registryData.fill(HIST("data/pure/protons/proton_pure_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/protons/proton_pure_dcaz"), pt, dcaz); + + if (charge > 0) { + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_tpc"), pt, track.tpcNSigmaPr()); + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_eta_phi"), phi, eta); + + if (track.hasTOF()) { + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_tof"), pt, track.tofNSigmaPr()); + } + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_pt"), pt); + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_eta"), eta); + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/protons/pos/proton_pure_pos_dcaz"), pt, dcaz); + } else { + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_tpc"), pt, track.tpcNSigmaPr()); + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_eta_phi"), phi, eta); + + if (track.hasTOF()) { + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_tof"), pt, track.tofNSigmaPr()); + } + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_pt"), pt); + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_eta"), eta); + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/pure/protons/neg/proton_pure_neg_dcaz"), pt, dcaz); + } + } + } + registryData.fill(HIST("data/jets/general/total_tracks_pure_vs_jets"), 1.0, static_cast(nSelectedTracks)); + } + PROCESS_SWITCH(JetHadronsPid, processPureTracks, "Pure Tracks Analysis", true); + + void processMC(soa::Join::iterator const& collision, HadronTracksMC const& tracks, aod::McParticles const& particles) + { + if (!collision.sel8() || std::abs(collision.posZ()) > zVtx) { + return; + } + if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return; + } + if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return; + } + if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return; + } + if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return; + } + if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return; + } + if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return; + } + + registryMC.fill(HIST("mc/matched/general/n_events"), 1); + registryMC.fill(HIST("mc/matched/general/z_vertex"), collision.posZ()); + registryMC.fill(HIST("mc/matched/general/x_vertex"), collision.posX()); + registryMC.fill(HIST("mc/matched/general/y_vertex"), collision.posY()); + registryMC.fill(HIST("mc/matched/general/xy_vertex"), collision.posX(), collision.posY()); + registryMC.fill(HIST("mc/matched/general/n_contributors"), collision.numContrib()); + + if (!collision.has_mcCollision()) { + return; + } + + auto particlesPerCollision = particles.sliceBy(mcParticlesPerCollision, collision.mcCollisionId()); + + for (auto const& track : tracks) { + + if (!passedTrackSelection(track)) { + continue; + } + + double pt = track.pt(); + double eta = track.eta(); + double phi = track.phi(); + double dcaxy = track.dcaXY(); + double dcaz = track.dcaZ(); + const double p = track.p(); + + if (!track.has_mcParticle()) { + continue; + } + auto const& trueParticle = track.mcParticle(); + int pdg = std::abs(trueParticle.pdgCode()); + + int realpdg = trueParticle.pdgCode(); + bool isPrimary = trueParticle.isPhysicalPrimary(); + int charge = track.sign(); + + registryMC.fill(HIST("mc/matched/reconstruction/general/tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/general/tof_beta_vs_p"), p, track.beta()); + } + + registryMC.fill(HIST("mc/matched/reconstruction/hadrons/rec_hadron_all"), pt); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/hadrons/rec_hadron_tof_match"), pt); + } + + if (isPrimary) { + registryMC.fill(HIST("mc/matched/reconstruction/hadrons/mc_rec_hadron_pt"), pt); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/hadrons/mc_sec_hadron_pt"), pt); + } + + PidResult pid = getPid(track); + + if (pid.isPion) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/rec_pion_all"), pt); + registryMC.fill(HIST("mc/matched/reconstruction/pions/contamination_matrix_pion"), realpdg, pt); + registryMC.fill(HIST("mc/matched/reconstruction/pions/pion_tpc_signal_vs_p"), p, track.tpcSignal()); + registryMC.fill(HIST("mc/matched/reconstruction/pions/rec_pion_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/pions/rec_pion_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/pions/rec_pion_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/pions/rec_pion_dcaz"), pt, dcaz); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/pion_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_all"), pt); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_tof_matched"), pt); + } + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/contamination_matrix_pion_pos"), realpdg, pt); + + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_tpc"), pt, track.tpcNSigmaPi()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_tof"), pt, track.tofNSigmaPi()); + } + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_dcaz"), pt, dcaz); + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/rec_pion_pos_phi_vs_pt"), pt, phi); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_all"), pt); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_tof_matched"), pt); + } + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/contamination_matrix_pion_neg"), realpdg, pt); + + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_tpc"), pt, track.tpcNSigmaPi()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_tof"), pt, track.tofNSigmaPi()); + } + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_dcaz"), pt, dcaz); + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/rec_pion_neg_phi_vs_pt"), pt, phi); + } + + if (isPrimary) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/mc_rec_pion_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/mc_rec_pion_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/mc_rec_pion_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kPiPlus) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/mc_sec_pion_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/matched/reconstruction/pions/pos/mc_sec_pion_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/pions/neg/mc_sec_pion_neg_pt"), pt); + } + } + } + } + + if (pid.isKaon) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/rec_kaon_all"), pt); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/contamination_matrix_kaon"), realpdg, pt); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/kaon_tpc_signal_vs_p"), p, track.tpcSignal()); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/rec_kaon_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/rec_kaon_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/rec_kaon_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/rec_kaon_dcaz"), pt, dcaz); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/kaon_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_all"), pt); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_tof_matched"), pt); + } + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/contamination_matrix_kaon_pos"), realpdg, pt); + + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_tpc"), pt, track.tpcNSigmaKa()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_tof"), pt, track.tofNSigmaKa()); + } + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_dcaz"), pt, dcaz); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/rec_kaon_pos_phi_vs_pt"), pt, phi); + } else { + + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_all"), pt); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_tof_matched"), pt); + } + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/contamination_matrix_kaon_neg"), realpdg, pt); + + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_tpc"), pt, track.tpcNSigmaKa()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_tof"), pt, track.tofNSigmaKa()); + } + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_dcaz"), pt, dcaz); + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/rec_kaon_neg_phi_vs_pt"), pt, phi); + } + + if (isPrimary) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/mc_rec_kaon_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/mc_rec_kaon_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/mc_rec_kaon_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kKPlus) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/mc_sec_kaon_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/pos/mc_sec_kaon_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/kaons/neg/mc_sec_kaon_neg_pt"), pt); + } + } + } + } + + if (pid.isProton) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/rec_proton_all"), pt); + registryMC.fill(HIST("mc/matched/reconstruction/protons/contamination_matrix_proton"), realpdg, pt); + registryMC.fill(HIST("mc/matched/reconstruction/protons/proton_tpc_signal_vs_p"), p, track.tpcSignal()); + registryMC.fill(HIST("mc/matched/reconstruction/protons/rec_proton_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/protons/rec_proton_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/protons/rec_proton_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/protons/rec_proton_dcaz"), pt, dcaz); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/proton_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_all"), pt); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_tof_matched"), pt); + } + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/contamination_matrix_proton_pos"), realpdg, pt); + + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_tpc"), pt, track.tpcNSigmaPr()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_tof"), pt, track.tofNSigmaPr()); + } + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_dcaz"), pt, dcaz); + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/rec_proton_pos_phi_vs_pt"), pt, phi); + } else { + + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_all"), pt); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_tof_matched"), pt); + } + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/contamination_matrix_proton_neg"), realpdg, pt); + + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_tpc"), pt, track.tpcNSigmaPr()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_tof"), pt, track.tofNSigmaPr()); + } + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_eta"), eta); + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_eta_phi"), phi, eta); + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_dcaz"), pt, dcaz); + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/rec_proton_neg_phi_vs_pt"), pt, phi); + } + + if (isPrimary) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/mc_rec_proton_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/mc_rec_proton_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/mc_rec_proton_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kProton) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/mc_sec_proton_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/matched/reconstruction/protons/pos/mc_sec_proton_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/matched/reconstruction/protons/neg/mc_sec_proton_neg_pt"), pt); + } + } + } + } + } + + for (auto const& mcpart : particlesPerCollision) { + + int realpdg = mcpart.pdgCode(); + int pdg = std::abs(realpdg); + bool isPrimary = mcpart.isPhysicalPrimary(); + double ptTrue = mcpart.pt(); + + if (!isPrimary) { + continue; + } + + if (mcpart.eta() < minEta || mcpart.eta() > maxEta) { + continue; + } + if (ptTrue < minPt || ptTrue > maxPt) { + continue; + } + + registryMC.fill(HIST("mc/matched/truth/hadrons/mc_gen_hadron_pt"), ptTrue); + + if (pdg == PDG_t::kPiPlus) { + if (ptTrue >= cfg.minPtPion && ptTrue <= cfg.maxPtPion) { + registryMC.fill(HIST("mc/matched/truth/pions/mc_gen_pion_pt"), ptTrue); + if (realpdg > 0) { + registryMC.fill(HIST("mc/matched/truth/pions/pos/mc_gen_pion_pos_pt"), ptTrue); + } else { + registryMC.fill(HIST("mc/matched/truth/pions/neg/mc_gen_pion_neg_pt"), ptTrue); + } + } + } else if (pdg == PDG_t::kKPlus) { + if (ptTrue >= cfg.minPtKaon && ptTrue <= cfg.maxPtKaon) { + registryMC.fill(HIST("mc/matched/truth/kaons/mc_gen_kaon_pt"), ptTrue); + if (realpdg > 0) { + registryMC.fill(HIST("mc/matched/truth/kaons/pos/mc_gen_kaon_pos_pt"), ptTrue); + } else { + registryMC.fill(HIST("mc/matched/truth/kaons/neg/mc_gen_kaon_neg_pt"), ptTrue); + } + } + } else if (pdg == PDG_t::kProton) { + if (ptTrue >= cfg.minPtProton && ptTrue <= cfg.maxPtProton) { + registryMC.fill(HIST("mc/matched/truth/protons/mc_gen_proton_pt"), ptTrue); + if (realpdg > 0) { + registryMC.fill(HIST("mc/matched/truth/protons/pos/mc_gen_proton_pos_pt"), ptTrue); + } else { + registryMC.fill(HIST("mc/matched/truth/protons/neg/mc_gen_proton_neg_pt"), ptTrue); + } + } + } + } + } + PROCESS_SWITCH(JetHadronsPid, processMC, "Run on Monte Carlo", false); + + void processMCTruth(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) + { + + if (std::abs(mcCollision.posZ()) > zVtx) { + return; + } + + registryMC.fill(HIST("mc/truth/general/n_events"), 1); + registryMC.fill(HIST("mc/truth/general/z_vtx"), mcCollision.posZ()); + + for (auto const& mcpart : mcParticles) { + + if (!mcpart.isPhysicalPrimary()) { + continue; + } + + if (mcpart.eta() < minEta || mcpart.eta() > maxEta) { + continue; + } + if (mcpart.pt() < minPt || mcpart.pt() > maxPt) { + continue; + } + + int originalPdg = mcpart.pdgCode(); + int pdg = std::abs(originalPdg); + double pt = mcpart.pt(); + + registryMC.fill(HIST("mc/truth/hadrons/mc_gen_hadron_pt"), pt); + + if (pdg == PDG_t::kPiPlus) { + if (pt >= cfg.minPtPion && pt <= cfg.maxPtPion) { + registryMC.fill(HIST("mc/truth/pions/mc_gen_pion_pt"), pt); + if (originalPdg > 0) { + registryMC.fill(HIST("mc/truth/pions/pos/mc_gen_pion_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/truth/pions/neg/mc_gen_pion_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kKPlus) { + if (pt >= cfg.minPtKaon && pt <= cfg.maxPtKaon) { + registryMC.fill(HIST("mc/truth/kaons/mc_gen_kaon_pt"), pt); + if (originalPdg > 0) { + registryMC.fill(HIST("mc/truth/kaons/pos/mc_gen_kaon_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/truth/kaons/neg/mc_gen_kaon_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kProton) { + if (pt >= cfg.minPtProton && pt <= cfg.maxPtProton) { + registryMC.fill(HIST("mc/truth/protons/mc_gen_proton_pt"), pt); + if (originalPdg > 0) { + registryMC.fill(HIST("mc/truth/protons/pos/mc_gen_proton_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/truth/protons/neg/mc_gen_proton_neg_pt"), pt); + } + } + } + } + } + PROCESS_SWITCH(JetHadronsPid, processMCTruth, "Run on Monte Carlo (Pure Truth)", false); + + void processJets(JetEvents::iterator const& collision, + soa::Join const& jets, + soa::Join const&, + HadronTracks const& globalTracks) + { + registryData.fill(HIST("data/jets/general/n_events_raw"), 1); + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + + float zVertex = collision.posZ(); + if (std::abs(zVertex) > zVtx) { + return; + } + + registryData.fill(HIST("data/jets/general/n_events"), 1); + registryData.fill(HIST("data/jets/general/z_vtx"), zVertex); + + double centralRho = collision.rho(); + int jetsInCollision = 0; + + auto collTracks = globalTracks.sliceBy(tracksPerCollision, collision.collisionId()); + int tracksInJetsCollision = 0; + + for (auto const& jet : jets) { + + if (!isppRefAnalysis && ((std::abs(jet.eta()) + rJet) > (maxEta - deltaEtaEdge))) { + continue; + } + if (isppRefAnalysis && std::abs(jet.eta()) > cfgEtaJetMax) { + continue; + } + + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) { + continue; + } + + double ptSub = jet.pt() - (centralRho * jet.area()); + if (ptSub < 0) { + ptSub = 0.0; + } + + if (!isppRefAnalysis && (ptSub < minJetPt || ptSub > maxJetPt)) { + continue; + } + + double normalizedJetArea = jet.area() / (PI * rJet * rJet); + if (applyAreaCut && normalizedJetArea < minNormalizedJetArea) { + continue; + } + + jetsInCollision++; + + registryData.fill(HIST("data/jets/general/jet_pt_subtracted"), ptSub); + registryData.fill(HIST("data/jets/general/jet_pt_raw_vs_sub"), jet.pt(), ptSub); + + registryData.fill(HIST("data/jets/general/jet_eta"), jet.eta()); + registryData.fill(HIST("data/jets/general/jet_phi"), jet.phi()); + registryData.fill(HIST("data/jets/general/jet_area"), jet.area()); + registryData.fill(HIST("data/jets/general/jet_pt"), jet.pt()); + registryData.fill(HIST("data/jets/general/jet_n_constituents"), jet.tracksIds().size()); + + const double magnitudeThreshold = 1e-9; + TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); + TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); + getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); + + if (ueAxis1.Mag() < magnitudeThreshold || ueAxis2.Mag() < magnitudeThreshold) { + continue; + } + + int selectedConstituentCount = 0; + + int nPionsInJet = 0; + int nKaonsInJet = 0; + int nProtonsInJet = 0; + + int nPionsInJetPos = 0; + int nKaonsInJetPos = 0; + int nProtonsInJetPos = 0; + + int nPionsInJetNeg = 0; + int nKaonsInJetNeg = 0; + int nProtonsInJetNeg = 0; + + for (auto const& jtrack : jet.tracks_as>()) { + + auto track = jtrack.track_as(); + + if (!passedTrackSelection(track)) { + continue; + } + tracksInJetsCollision++; + + selectedConstituentCount++; + const double p = track.p(); + registryData.fill(HIST("data/jets/general/tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/general/tof_beta_vs_p"), p, track.beta()); + } + + double pt = track.pt(); + double eta = track.eta(); + double dcaxy = track.dcaXY(); + double dcaz = track.dcaZ(); + int charge = track.sign(); + double phi = track.phi(); + + double deltaEtaJet = track.eta() - jet.eta(); + double deltaPhiJet = RecoDecay::constrainAngle(track.phi() - jet.phi(), -PI); + + registryData.fill(HIST("data/jets/cone/all_tracks"), deltaEtaJet, deltaPhiJet, pt); + registryData.fill(HIST("data/jets/cone/all_tracks_2d"), deltaEtaJet, deltaPhiJet); + + PidResult pid = getPid(track); + + if (pid.isPion) { + + registryData.fill(HIST("data/jets/pions/pion_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/pions/pion_jet_tof"), pt, track.tofNSigmaPi()); + registryData.fill(HIST("data/jets/pions/pion_tof_beta_vs_p"), p, track.beta()); + } + registryData.fill(HIST("data/jets/pions/pion_jet_tpc"), pt, track.tpcNSigmaPi()); + registryData.fill(HIST("data/jets/pions/pion_pt_vs_jet_pt"), jet.pt(), pt); + + if (charge > 0) { + nPionsInJetPos++; + + registryData.fill(HIST("data/jets/pions/pos/pion_jet_pos_tpc"), pt, track.tpcNSigmaPi()); + registryData.fill(HIST("data/jets/pions/pos/pion_jet_pos_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/jets/cone/pions/pion_pos"), deltaEtaJet, deltaPhiJet, pt); + registryData.fill(HIST("data/jets/cone/pions/pion_pos_2d"), deltaEtaJet, deltaPhiJet); + + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/pions/pos/pion_jet_pos_tof"), pt, track.tofNSigmaPi()); + } + registryData.fill(HIST("data/jets/pions/pos/pion_jet_pos_pt"), pt); + registryData.fill(HIST("data/jets/pions/pos/pion_jet_pos_eta"), eta); + registryData.fill(HIST("data/jets/pions/pos/pion_jet_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/jets/pions/pos/pion_jet_pos_dcaz"), pt, dcaz); + } else { + nPionsInJetNeg++; + + registryData.fill(HIST("data/jets/pions/neg/pion_jet_neg_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/jets/pions/neg/pion_jet_neg_tpc"), pt, track.tpcNSigmaPi()); + registryData.fill(HIST("data/jets/cone/pions/pion_neg"), deltaEtaJet, deltaPhiJet, pt); + registryData.fill(HIST("data/jets/cone/pions/pion_neg_2d"), deltaEtaJet, deltaPhiJet); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/pions/neg/pion_jet_neg_tof"), pt, track.tofNSigmaPi()); + } + registryData.fill(HIST("data/jets/pions/neg/pion_jet_neg_pt"), pt); + registryData.fill(HIST("data/jets/pions/neg/pion_jet_neg_eta"), eta); + registryData.fill(HIST("data/jets/pions/neg/pion_jet_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/jets/pions/neg/pion_jet_neg_dcaz"), pt, dcaz); + } + } + + if (pid.isKaon) { + + registryData.fill(HIST("data/jets/kaons/kaon_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/kaons/kaon_jet_tof"), pt, track.tofNSigmaKa()); + registryData.fill(HIST("data/jets/kaons/kaon_tof_beta_vs_p"), p, track.beta()); + } + registryData.fill(HIST("data/jets/kaons/kaon_jet_tpc"), pt, track.tpcNSigmaKa()); + registryData.fill(HIST("data/jets/kaons/kaon_pt_vs_jet_pt"), jet.pt(), pt); + + if (charge > 0) { + nKaonsInJetPos++; + + registryData.fill(HIST("data/jets/kaons/pos/kaon_jet_pos_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/jets/kaons/pos/kaon_jet_pos_tpc"), pt, track.tpcNSigmaKa()); + registryData.fill(HIST("data/jets/cone/kaons/kaon_pos"), deltaEtaJet, deltaPhiJet, pt); + registryData.fill(HIST("data/jets/cone/kaons/kaon_pos_2d"), deltaEtaJet, deltaPhiJet); + + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/kaons/pos/kaon_jet_pos_tof"), pt, track.tofNSigmaKa()); + } + registryData.fill(HIST("data/jets/kaons/pos/kaon_jet_pos_pt"), pt); + registryData.fill(HIST("data/jets/kaons/pos/kaon_jet_pos_eta"), eta); + registryData.fill(HIST("data/jets/kaons/pos/kaon_jet_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/jets/kaons/pos/kaon_jet_pos_dcaz"), pt, dcaz); + } else { + nKaonsInJetNeg++; + + registryData.fill(HIST("data/jets/kaons/neg/kaon_jet_neg_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/jets/kaons/neg/kaon_jet_neg_tpc"), pt, track.tpcNSigmaKa()); + registryData.fill(HIST("data/jets/cone/kaons/kaon_neg"), deltaEtaJet, deltaPhiJet, pt); + registryData.fill(HIST("data/jets/cone/kaons/kaon_neg_2d"), deltaEtaJet, deltaPhiJet); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/kaons/neg/kaon_jet_neg_tof"), pt, track.tofNSigmaKa()); + } + registryData.fill(HIST("data/jets/kaons/neg/kaon_jet_neg_pt"), pt); + registryData.fill(HIST("data/jets/kaons/neg/kaon_jet_neg_eta"), eta); + registryData.fill(HIST("data/jets/kaons/neg/kaon_jet_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/jets/kaons/neg/kaon_jet_neg_dcaz"), pt, dcaz); + } + } + + if (pid.isProton) { + + registryData.fill(HIST("data/jets/protons/proton_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/protons/proton_jet_tof"), pt, track.tofNSigmaPr()); + registryData.fill(HIST("data/jets/protons/proton_tof_beta_vs_p"), p, track.beta()); + } + registryData.fill(HIST("data/jets/protons/proton_jet_tpc"), pt, track.tpcNSigmaPr()); + registryData.fill(HIST("data/jets/protons/proton_pt_vs_jet_pt"), jet.pt(), pt); + + if (charge > 0) { + nProtonsInJetPos++; + + registryData.fill(HIST("data/jets/protons/pos/proton_jet_pos_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/jets/protons/pos/proton_jet_pos_tpc"), pt, track.tpcNSigmaPr()); + registryData.fill(HIST("data/jets/cone/protons/proton_pos"), deltaEtaJet, deltaPhiJet, pt); + registryData.fill(HIST("data/jets/cone/protons/proton_pos_2d"), deltaEtaJet, deltaPhiJet); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/protons/pos/proton_jet_pos_tof"), pt, track.tofNSigmaPr()); + } + registryData.fill(HIST("data/jets/protons/pos/proton_jet_pos_pt"), pt); + registryData.fill(HIST("data/jets/protons/pos/proton_jet_pos_eta"), eta); + registryData.fill(HIST("data/jets/protons/pos/proton_jet_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/jets/protons/pos/proton_jet_pos_dcaz"), pt, dcaz); + } else { + nProtonsInJetNeg++; + registryData.fill(HIST("data/jets/protons/neg/proton_jet_neg_phi_vs_pt"), pt, phi); + registryData.fill(HIST("data/jets/protons/neg/proton_jet_neg_tpc"), pt, track.tpcNSigmaPr()); + registryData.fill(HIST("data/jets/cone/protons/proton_neg"), deltaEtaJet, deltaPhiJet, pt); + registryData.fill(HIST("data/jets/cone/protons/proton_neg_2d"), deltaEtaJet, deltaPhiJet); + if (track.hasTOF()) { + registryData.fill(HIST("data/jets/protons/neg/proton_jet_neg_tof"), pt, track.tofNSigmaPr()); + } + registryData.fill(HIST("data/jets/protons/neg/proton_jet_neg_pt"), pt); + registryData.fill(HIST("data/jets/protons/neg/proton_jet_neg_eta"), eta); + registryData.fill(HIST("data/jets/protons/neg/proton_jet_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/jets/protons/neg/proton_jet_neg_dcaz"), pt, dcaz); + } + } + } + registryData.fill(HIST("data/jets/general/jet_n_selected_constituents"), selectedConstituentCount); + + registryData.fill(HIST("data/jets/pions/pos/n_pions_per_jet"), nPionsInJetPos); + registryData.fill(HIST("data/jets/kaons/pos/n_kaons_per_jet"), nKaonsInJetPos); + registryData.fill(HIST("data/jets/protons/pos/n_protons_per_jet"), nProtonsInJetPos); + + registryData.fill(HIST("data/jets/pions/neg/n_pions_per_jet"), nPionsInJetNeg); + registryData.fill(HIST("data/jets/kaons/neg/n_kaons_per_jet"), nKaonsInJetNeg); + registryData.fill(HIST("data/jets/protons/neg/n_protons_per_jet"), nProtonsInJetNeg); + + nPionsInJet = nPionsInJetPos + nPionsInJetNeg; + nProtonsInJet = nProtonsInJetNeg + nProtonsInJetPos; + nKaonsInJet = nKaonsInJetNeg + nKaonsInJetPos; + + registryData.fill(HIST("data/jets/pions/n_pions_per_jet"), nPionsInJet); + registryData.fill(HIST("data/jets/kaons/n_kaons_per_jet"), nKaonsInJet); + registryData.fill(HIST("data/jets/protons/n_protons_per_jet"), nProtonsInJet); + + registryData.fill(HIST("data/jets/pions/n_pions_vs_jet_pt"), jet.pt(), nPionsInJet); + registryData.fill(HIST("data/jets/kaons/n_kaons_vs_jet_pt"), jet.pt(), nKaonsInJet); + registryData.fill(HIST("data/jets/protons/n_protons_vs_jet_pt"), jet.pt(), nProtonsInJet); + + registryData.fill(HIST("data/jets/composition/particle_multiplicity_per_jet"), 1.0, nPionsInJet); + registryData.fill(HIST("data/jets/composition/particle_multiplicity_per_jet"), 2.0, nKaonsInJet); + registryData.fill(HIST("data/jets/composition/particle_multiplicity_per_jet"), 3.0, nProtonsInJet); + + int nTracksOut = 0; + + for (auto const& track : collTracks) { + + if (!passedTrackSelection(track)) { + continue; + } + + double deltaEtaUe1 = track.eta() - ueAxis1.Eta(); + double deltaPhiUe1 = RecoDecay::constrainAngle(track.phi() - ueAxis1.Phi(), -PI); + double deltaRUe1 = std::hypot(deltaEtaUe1, deltaPhiUe1); + + double deltaEtaUe2 = track.eta() - ueAxis2.Eta(); + double deltaPhiUe2 = RecoDecay::constrainAngle(track.phi() - ueAxis2.Phi(), -PI); + double deltaRUe2 = std::hypot(deltaEtaUe2, deltaPhiUe2); + + if (deltaRUe1 > rJet && deltaRUe2 > rJet) { + continue; + } + + double pt = track.pt(); + double eta = track.eta(); + double dcaxy = track.dcaXY(); + double dcaz = track.dcaZ(); + int charge = track.sign(); + + PidResult pid = getPid(track); + nTracksOut++; + + if (pid.isPion) { + registryData.fill(HIST("data/ue/pions/pion_ue_tpc"), pt, track.tpcNSigmaPi()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/pions/pion_ue_tof"), pt, track.tofNSigmaPi()); + } + + registryData.fill(HIST("data/ue/pions/pion_ue_pt"), pt); + registryData.fill(HIST("data/ue/pions/pion_ue_eta"), eta); + registryData.fill(HIST("data/ue/pions/pion_ue_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/pions/pion_ue_dcaz"), pt, dcaz); + + if (charge > 0) { + registryData.fill(HIST("data/ue/pions/pos/pion_ue_pos_tpc"), pt, track.tpcNSigmaPi()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/pions/pos/pion_ue_pos_tof"), pt, track.tofNSigmaPi()); + } + registryData.fill(HIST("data/ue/pions/pos/pion_ue_pos_pt"), pt); + registryData.fill(HIST("data/ue/pions/pos/pion_ue_pos_eta"), eta); + registryData.fill(HIST("data/ue/pions/pos/pion_ue_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/pions/pos/pion_ue_pos_dcaz"), pt, dcaz); + } else { + registryData.fill(HIST("data/ue/pions/neg/pion_ue_neg_tpc"), pt, track.tpcNSigmaPi()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/pions/neg/pion_ue_neg_tof"), pt, track.tofNSigmaPi()); + } + registryData.fill(HIST("data/ue/pions/neg/pion_ue_neg_pt"), pt); + registryData.fill(HIST("data/ue/pions/neg/pion_ue_neg_eta"), eta); + registryData.fill(HIST("data/ue/pions/neg/pion_ue_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/pions/neg/pion_ue_neg_dcaz"), pt, dcaz); + } + } + + if (pid.isKaon) { + registryData.fill(HIST("data/ue/kaons/kaon_ue_tpc"), pt, track.tpcNSigmaKa()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/kaons/kaon_ue_tof"), pt, track.tofNSigmaKa()); + } + + registryData.fill(HIST("data/ue/kaons/kaon_ue_pt"), pt); + registryData.fill(HIST("data/ue/kaons/kaon_ue_eta"), eta); + registryData.fill(HIST("data/ue/kaons/kaon_ue_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/kaons/kaon_ue_dcaz"), pt, dcaz); + + if (charge > 0) { + registryData.fill(HIST("data/ue/kaons/pos/kaon_ue_pos_tpc"), pt, track.tpcNSigmaKa()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/kaons/pos/kaon_ue_pos_tof"), pt, track.tofNSigmaKa()); + } + registryData.fill(HIST("data/ue/kaons/pos/kaon_ue_pos_pt"), pt); + registryData.fill(HIST("data/ue/kaons/pos/kaon_ue_pos_eta"), eta); + registryData.fill(HIST("data/ue/kaons/pos/kaon_ue_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/kaons/pos/kaon_ue_pos_dcaz"), pt, dcaz); + } else { + registryData.fill(HIST("data/ue/kaons/neg/kaon_ue_neg_tpc"), pt, track.tpcNSigmaKa()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/kaons/neg/kaon_ue_neg_tof"), pt, track.tofNSigmaKa()); + } + registryData.fill(HIST("data/ue/kaons/neg/kaon_ue_neg_pt"), pt); + registryData.fill(HIST("data/ue/kaons/neg/kaon_ue_neg_eta"), eta); + registryData.fill(HIST("data/ue/kaons/neg/kaon_ue_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/kaons/neg/kaon_ue_neg_dcaz"), pt, dcaz); + } + } + + if (pid.isProton) { + registryData.fill(HIST("data/ue/protons/proton_ue_tpc"), pt, track.tpcNSigmaPr()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/protons/proton_ue_tof"), pt, track.tofNSigmaPr()); + } + + registryData.fill(HIST("data/ue/protons/proton_ue_pt"), pt); + registryData.fill(HIST("data/ue/protons/proton_ue_eta"), eta); + registryData.fill(HIST("data/ue/protons/proton_ue_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/protons/proton_ue_dcaz"), pt, dcaz); + + if (charge > 0) { + registryData.fill(HIST("data/ue/protons/pos/proton_ue_pos_tpc"), pt, track.tpcNSigmaPr()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/protons/pos/proton_ue_pos_tof"), pt, track.tofNSigmaPr()); + } + registryData.fill(HIST("data/ue/protons/pos/proton_ue_pos_pt"), pt); + registryData.fill(HIST("data/ue/protons/pos/proton_ue_pos_eta"), eta); + registryData.fill(HIST("data/ue/protons/pos/proton_ue_pos_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/protons/pos/proton_ue_pos_dcaz"), pt, dcaz); + } else { + registryData.fill(HIST("data/ue/protons/neg/proton_ue_neg_tpc"), pt, track.tpcNSigmaPr()); + if (track.hasTOF()) { + registryData.fill(HIST("data/ue/protons/neg/proton_ue_neg_tof"), pt, track.tofNSigmaPr()); + } + registryData.fill(HIST("data/ue/protons/neg/proton_ue_neg_pt"), pt); + registryData.fill(HIST("data/ue/protons/neg/proton_ue_neg_eta"), eta); + registryData.fill(HIST("data/ue/protons/neg/proton_ue_neg_dcaxy"), pt, dcaxy); + registryData.fill(HIST("data/ue/protons/neg/proton_ue_neg_dcaz"), pt, dcaz); + } + } + } + registryData.fill(HIST("data/ue/general/tracks_n_in_ue"), nTracksOut); + } + registryData.fill(HIST("data/jets/general/total_tracks_pure_vs_jets"), 2.0, static_cast(tracksInJetsCollision)); + registryData.fill(HIST("data/jets/general/collision_multiplicity"), jetsInCollision); + } + PROCESS_SWITCH(JetHadronsPid, processJets, "Jets Analysis", true); + + void processMatchJets(soa::Join::iterator const& collision, + soa::Join const& detectorLevelJets, + soa::Join const&, + ChargedMCPJets const& particleLevelJets, + aod::JetParticles const&, + HadronTracksMC const&, aod::McParticles const&) + { + + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/n_events_raw"), 1); + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + + if (std::abs(collision.posZ()) > zVtx) { + return; + } + + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/n_events"), 1); + + double centralRho = collision.rho(); + int jetsInCollision = 0; + std::set matchedTruthJetIndices; + + for (auto const& djet : detectorLevelJets) { + + if (!isppRefAnalysis && ((std::abs(djet.eta()) + rJet) > (maxEta - deltaEtaEdge))) { + continue; + } + + if (isppRefAnalysis && std::abs(djet.eta()) > cfgEtaJetMax) { + continue; + } + + if (isppRefAnalysis && (djet.pt() < minJetPt || djet.pt() > maxJetPt)) { + continue; + } + + double ptSub = djet.pt() - (centralRho * djet.area()); + if (ptSub < 0) { + ptSub = 0.0; + } + + if (!isppRefAnalysis && (ptSub < minJetPt || ptSub > maxJetPt)) { + continue; + } + + const double normalizedJetArea = djet.area() / (PI * rJet * rJet); + + if (applyAreaCut && normalizedJetArea < minNormalizedJetArea) { + continue; + } + + auto geoMatches = djet.matchedJetGeo_as(); + auto ptMatches = djet.matchedJetPt_as(); + auto matchedIt = geoMatches.end(); + + for (auto itGeo = geoMatches.begin(); itGeo != geoMatches.end(); ++itGeo) { + bool found = false; + for (auto itPt = ptMatches.begin(); itPt != ptMatches.end(); ++itPt) { + if (itGeo->globalIndex() == itPt->globalIndex()) { + matchedIt = itGeo; + found = true; + break; + } + } + if (found) { + break; + } + } + + if (matchedIt == geoMatches.end()) { + registryMC.fill(HIST("mc/jets/matched/fake/general/jet_pt"), djet.pt()); + registryMC.fill(HIST("mc/jets/matched/fake/general/jet_eta"), djet.eta()); + registryMC.fill(HIST("mc/jets/matched/fake/general/jet_phi"), djet.phi()); + registryMC.fill(HIST("mc/jets/matched/fake/general/jet_area"), djet.area()); + registryMC.fill(HIST("mc/jets/matched/fake/general/jet_n_constituents"), djet.tracksIds().size()); + + int nSelectedConstituentsFake = 0; + + int nPionsInJetPosFake = 0; + int nKaonsInJetPosFake = 0; + int nProtonsInJetPosFake = 0; + + int nPionsInJetNegFake = 0; + int nKaonsInJetNegFake = 0; + int nProtonsInJetNegFake = 0; + + for (auto const& jtrack : djet.tracks_as>()) { + + auto track = jtrack.track_as(); + + if (!passedTrackSelection(track)) { + continue; + } + + ++nSelectedConstituentsFake; + + const double pt = track.pt(); + const double eta = track.eta(); + const double dcaxy = track.dcaXY(); + const double dcaz = track.dcaZ(); + const double phi = track.phi(); + const int charge = track.sign(); + const double p = track.p(); + + int realpdg = 0; + int pdg = 0; + bool isPrimary = false; + + if (track.has_mcParticle()) { + auto const& trueParticle = track.mcParticle(); + realpdg = trueParticle.pdgCode(); + pdg = std::abs(realpdg); + isPrimary = trueParticle.isPhysicalPrimary(); + } + + registryMC.fill(HIST("mc/jets/matched/fake/hadrons/rec_hadron_all"), pt); + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/fake/hadrons/mc_rec_hadron_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/fake/hadrons/mc_sec_hadron_pt"), pt); + } + + const PidResult pid = getPid(track); + + if (pid.isPion) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/pion_pt_vs_jet_pt"), djet.pt(), pt); + + registryMC.fill(HIST("mc/jets/matched/fake/pions/rec_pion_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/contamination_matrix_pion"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pion_jet_tpc"), pt, track.tpcNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pion_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/pion_jet_tof"), pt, track.tofNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pion_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + nPionsInJetPosFake++; + + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/rec_pion_pos_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/contamination_matrix_pion_pos"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/pion_jet_pos_tpc"), pt, track.tpcNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/pion_jet_pos_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/pion_jet_pos_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/pion_jet_pos_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/pion_jet_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/pion_jet_pos_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/rec_pion_pos_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/pion_jet_pos_tof"), pt, track.tofNSigmaPi()); + } + } else { + nPionsInJetNegFake++; + + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/rec_pion_neg_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/contamination_matrix_pion_neg"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/pion_jet_neg_tpc"), pt, track.tpcNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/pion_jet_neg_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/pion_jet_neg_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/pion_jet_neg_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/pion_jet_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/pion_jet_neg_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/rec_pion_neg_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/pion_jet_neg_tof"), pt, track.tofNSigmaPi()); + } + } + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/mc_rec_pion_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/mc_rec_pion_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/mc_rec_pion_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kPiPlus) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/mc_sec_pion_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/mc_sec_pion_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/mc_sec_pion_neg_pt"), pt); + } + } + } + } + + if (pid.isKaon) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/kaon_pt_vs_jet_pt"), djet.pt(), pt); + + registryMC.fill(HIST("mc/jets/matched/fake/kaons/rec_kaon_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/contamination_matrix_kaon"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/kaon_jet_tpc"), pt, track.tpcNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/kaon_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/kaon_jet_tof"), pt, track.tofNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/kaon_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + nKaonsInJetPosFake++; + + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/rec_kaon_pos_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/contamination_matrix_kaon_pos"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_tpc"), pt, track.tpcNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/rec_kaon_pos_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/kaon_jet_pos_tof"), pt, track.tofNSigmaKa()); + } + } else { + nKaonsInJetNegFake++; + + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/contamination_matrix_kaon_neg"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_tpc"), pt, track.tpcNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/kaon_jet_neg_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/rec_kaon_neg_tof"), pt, track.tofNSigmaKa()); + } + } + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/mc_rec_kaon_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/mc_rec_kaon_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/mc_rec_kaon_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kKPlus) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/mc_sec_kaon_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/mc_sec_kaon_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/mc_sec_kaon_neg_pt"), pt); + } + } + } + } + + if (pid.isProton) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/proton_pt_vs_jet_pt"), djet.pt(), pt); + + registryMC.fill(HIST("mc/jets/matched/fake/protons/rec_proton_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/contamination_matrix_proton"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/proton_jet_tpc"), pt, track.tpcNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/fake/protons/proton_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/proton_jet_tof"), pt, track.tofNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/fake/protons/proton_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + nProtonsInJetPosFake++; + + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/rec_proton_pos_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/contamination_matrix_proton_pos"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/proton_jet_pos_tpc"), pt, track.tpcNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/proton_jet_pos_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/proton_jet_pos_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/proton_jet_pos_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/proton_jet_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/proton_jet_pos_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/rec_proton_pos_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/proton_jet_pos_tof"), pt, track.tofNSigmaPr()); + } + } else { + nProtonsInJetNegFake++; + + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/rec_proton_neg_all"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/contamination_matrix_proton_neg"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/proton_jet_neg_tpc"), pt, track.tpcNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/proton_jet_neg_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/proton_jet_neg_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/proton_jet_neg_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/proton_jet_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/proton_jet_neg_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/rec_proton_neg_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/proton_jet_neg_tof"), pt, track.tofNSigmaPr()); + } + } + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/mc_rec_proton_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/mc_rec_proton_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/mc_rec_proton_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kProton) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/mc_sec_proton_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/mc_sec_proton_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/mc_sec_proton_neg_pt"), pt); + } + } + } + } + } + + registryMC.fill(HIST("mc/jets/matched/fake/general/jet_n_selected_constituents"), nSelectedConstituentsFake); + + const int nPionsInJetFake = nPionsInJetPosFake + nPionsInJetNegFake; + const int nKaonsInJetFake = nKaonsInJetPosFake + nKaonsInJetNegFake; + const int nProtonsInJetFake = nProtonsInJetPosFake + nProtonsInJetNegFake; + + registryMC.fill(HIST("mc/jets/matched/fake/pions/pos/n_pions_per_jet"), nPionsInJetPosFake); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/pos/n_kaons_per_jet"), nKaonsInJetPosFake); + registryMC.fill(HIST("mc/jets/matched/fake/protons/pos/n_protons_per_jet"), nProtonsInJetPosFake); + + registryMC.fill(HIST("mc/jets/matched/fake/pions/neg/n_pions_per_jet"), nPionsInJetNegFake); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/neg/n_kaons_per_jet"), nKaonsInJetNegFake); + registryMC.fill(HIST("mc/jets/matched/fake/protons/neg/n_protons_per_jet"), nProtonsInJetNegFake); + + registryMC.fill(HIST("mc/jets/matched/fake/composition/n_pions_per_jet"), nPionsInJetFake); + registryMC.fill(HIST("mc/jets/matched/fake/composition/n_kaons_per_jet"), nKaonsInJetFake); + registryMC.fill(HIST("mc/jets/matched/fake/composition/n_protons_per_jet"), nProtonsInJetFake); + + registryMC.fill(HIST("mc/jets/matched/fake/composition/particle_multiplicity_per_jet"), 1.0, nPionsInJetFake); + registryMC.fill(HIST("mc/jets/matched/fake/composition/particle_multiplicity_per_jet"), 2.0, nKaonsInJetFake); + registryMC.fill(HIST("mc/jets/matched/fake/composition/particle_multiplicity_per_jet"), 3.0, nProtonsInJetFake); + + registryMC.fill(HIST("mc/jets/matched/fake/pions/n_pions_vs_jet_pt"), djet.pt(), nPionsInJetFake); + registryMC.fill(HIST("mc/jets/matched/fake/kaons/n_kaons_vs_jet_pt"), djet.pt(), nKaonsInJetFake); + registryMC.fill(HIST("mc/jets/matched/fake/protons/n_protons_vs_jet_pt"), djet.pt(), nProtonsInJetFake); + + registryMC.fill(HIST("mc/jets/matched/general/jet_match_counter"), 3.0); + + continue; + } + + auto const& pjet = *matchedIt; + + matchedTruthJetIndices.insert(pjet.globalIndex()); + registryMC.fill(HIST("mc/jets/matched/general/jet_match_counter"), 1.0); + + ++jetsInCollision; + + int nSelectedConstituents = 0; + + int nPionsInJet = 0; + int nKaonsInJet = 0; + int nProtonsInJet = 0; + + int nPionsInJetPos = 0; + int nKaonsInJetPos = 0; + int nProtonsInJetPos = 0; + + int nPionsInJetNeg = 0; + int nKaonsInJetNeg = 0; + int nProtonsInJetNeg = 0; + + int nPionsInJetTruth = 0; + int nKaonsInJetTruth = 0; + int nProtonsInJetTruth = 0; + + int nPionsInJetPosTruth = 0; + int nKaonsInJetPosTruth = 0; + int nProtonsInJetPosTruth = 0; + + int nPionsInJetNegTruth = 0; + int nKaonsInJetNegTruth = 0; + int nProtonsInJetNegTruth = 0; + + int nSelectedConstituentsTruth = 0; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_pt"), djet.pt()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_pt_subtracted"), ptSub); + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_pt_raw_vs_sub"), djet.pt(), ptSub); + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_eta"), djet.eta()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_phi"), djet.phi()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_area"), djet.area()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_n_constituents"), djet.tracksIds().size()); + + registryMC.fill(HIST("mc/jets/matched/truth/general/jet_pt"), pjet.pt()); + registryMC.fill(HIST("mc/jets/matched/truth/general/jet_eta"), pjet.eta()); + registryMC.fill(HIST("mc/jets/matched/truth/general/jet_phi"), pjet.phi()); + registryMC.fill(HIST("mc/jets/matched/truth/general/jet_area"), pjet.area()); + registryMC.fill(HIST("mc/jets/matched/truth/general/jet_n_constituents"), pjet.tracksIds().size()); + + for (auto const& jtrack : djet.tracks_as>()) { + + auto track = jtrack.track_as(); + + if (!passedTrackSelection(track)) { + continue; + } + + ++nSelectedConstituents; + + const double pt = track.pt(); + const double eta = track.eta(); + const double dcaxy = track.dcaXY(); + const double dcaz = track.dcaZ(); + const double phi = track.phi(); + const int charge = track.sign(); + const double p = track.p(); + + int realpdg = 0; + int pdg = 0; + bool isPrimary = false; + + if (track.has_mcParticle()) { + auto const& trueParticle = track.mcParticle(); + realpdg = trueParticle.pdgCode(); + pdg = std::abs(realpdg); + isPrimary = trueParticle.isPhysicalPrimary(); + } + + registryMC.fill(HIST("mc/jets/matched/reconstructed/hadrons/rec_hadron_all"), pt); + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/hadrons/mc_rec_hadron_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/reconstructed/hadrons/mc_sec_hadron_pt"), pt); + } + + const PidResult pid = getPid(track); + + if (pid.isPion) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pion_pt_vs_jet_pt"), djet.pt(), pt); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/rec_pion_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/contamination_matrix_pion"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pion_jet_tpc"), pt, track.tpcNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pion_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pion_jet_tof"), pt, track.tofNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pion_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + nPionsInJetPos++; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/rec_pion_pos_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/contamination_matrix_pion_pos"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_tpc"), pt, track.tpcNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/rec_pion_pos_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/pion_jet_pos_tof"), pt, track.tofNSigmaPi()); + } + } else { + nPionsInJetNeg++; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/rec_pion_neg_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/contamination_matrix_pion_neg"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_tpc"), pt, track.tpcNSigmaPi()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/rec_pion_neg_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/pion_jet_neg_tof"), pt, track.tofNSigmaPi()); + } + } + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/mc_rec_pion_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/mc_rec_pion_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/mc_rec_pion_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kPiPlus) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/mc_sec_pion_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/mc_sec_pion_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/mc_sec_pion_neg_pt"), pt); + } + } + } + } + + if (pid.isKaon) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/kaon_pt_vs_jet_pt"), djet.pt(), pt); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/rec_kaon_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/contamination_matrix_kaon"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/kaon_jet_tpc"), pt, track.tpcNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/kaon_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/kaon_jet_tof"), pt, track.tofNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/kaon_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + nKaonsInJetPos++; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/rec_kaon_pos_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/contamination_matrix_kaon_pos"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_tpc"), pt, track.tpcNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/rec_kaon_pos_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/kaon_jet_pos_tof"), pt, track.tofNSigmaKa()); + } + } else { + nKaonsInJetNeg++; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/contamination_matrix_kaon_neg"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_tpc"), pt, track.tpcNSigmaKa()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/kaon_jet_neg_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/rec_kaon_neg_tof"), pt, track.tofNSigmaKa()); + } + } + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/mc_rec_kaon_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/mc_rec_kaon_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/mc_rec_kaon_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kKPlus) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/mc_sec_kaon_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/mc_sec_kaon_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/mc_sec_kaon_neg_pt"), pt); + } + } + } + } + + if (pid.isProton) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/proton_pt_vs_jet_pt"), djet.pt(), pt); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/rec_proton_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/contamination_matrix_proton"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/proton_jet_tpc"), pt, track.tpcNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/proton_tpc_signal_vs_p"), p, track.tpcSignal()); + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/proton_jet_tof"), pt, track.tofNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/proton_tof_beta_vs_p"), p, track.beta()); + } + + if (charge > 0) { + nProtonsInJetPos++; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/rec_proton_pos_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/contamination_matrix_proton_pos"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_tpc"), pt, track.tpcNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/rec_proton_pos_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/proton_jet_pos_tof"), pt, track.tofNSigmaPr()); + } + } else { + nProtonsInJetNeg++; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/rec_proton_neg_all"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/contamination_matrix_proton_neg"), realpdg, pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_tpc"), pt, track.tpcNSigmaPr()); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_phi_vs_pt"), pt, phi); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_eta"), eta); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_dcaxy"), pt, dcaxy); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_dcaz"), pt, dcaz); + + if (track.hasTOF()) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/rec_proton_neg_tof_matched"), pt); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/proton_jet_neg_tof"), pt, track.tofNSigmaPr()); + } + } + + if (isPrimary) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/mc_rec_proton_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/mc_rec_proton_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/mc_rec_proton_neg_pt"), pt); + } + } else { + if (pdg == PDG_t::kProton) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/mc_sec_proton_pt"), pt); + if (charge > 0) { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/mc_sec_proton_pos_pt"), pt); + } else { + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/mc_sec_proton_neg_pt"), pt); + } + } + } + } + } + + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/jet_n_selected_constituents"), nSelectedConstituents); + + nPionsInJet = nPionsInJetPos + nPionsInJetNeg; + nKaonsInJet = nKaonsInJetPos + nKaonsInJetNeg; + nProtonsInJet = nProtonsInJetPos + nProtonsInJetNeg; + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/pos/n_pions_per_jet"), nPionsInJetPos); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/pos/n_kaons_per_jet"), nKaonsInJetPos); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/pos/n_protons_per_jet"), nProtonsInJetPos); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/neg/n_pions_per_jet"), nPionsInJetNeg); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/neg/n_kaons_per_jet"), nKaonsInJetNeg); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/neg/n_protons_per_jet"), nProtonsInJetNeg); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/n_pions_per_jet"), nPionsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/n_kaons_per_jet"), nKaonsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/n_protons_per_jet"), nProtonsInJet); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/particle_multiplicity_per_jet"), 1.0, nPionsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/particle_multiplicity_per_jet"), 2.0, nKaonsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/particle_multiplicity_per_jet"), 3.0, nProtonsInJet); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/pions/n_pions_vs_jet_pt"), djet.pt(), nPionsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/kaons/n_kaons_vs_jet_pt"), djet.pt(), nKaonsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/protons/n_protons_vs_jet_pt"), djet.pt(), nProtonsInJet); + + for (auto const& particle : pjet.tracks_as()) { + + if (!particle.isPhysicalPrimary()) { + continue; + } + + if (particle.eta() < minEta || particle.eta() > maxEta) { + continue; + } + + double pt = particle.pt(); + if (pt < minPt || pt > maxPt) { + continue; + } + + ++nSelectedConstituentsTruth; + + int originalPdg = particle.pdgCode(); + int pdg = std::abs(originalPdg); + + registryMC.fill(HIST("mc/jets/matched/truth/hadrons/mc_gen_hadron_pt"), pt); + + if (pdg == PDG_t::kPiPlus) { + if (pt >= cfg.minPtPion && pt <= cfg.maxPtPion) { + registryMC.fill(HIST("mc/jets/matched/truth/pions/mc_gen_pion_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/truth/pions/pion_pt_vs_jet_pt"), pjet.pt(), pt); + + if (originalPdg > 0) { + nPionsInJetPosTruth++; + registryMC.fill(HIST("mc/jets/matched/truth/pions/pos/mc_gen_pion_pos_pt"), pt); + } else { + nPionsInJetNegTruth++; + registryMC.fill(HIST("mc/jets/matched/truth/pions/neg/mc_gen_pion_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kKPlus) { + if (pt >= cfg.minPtKaon && pt <= cfg.maxPtKaon) { + registryMC.fill(HIST("mc/jets/matched/truth/kaons/mc_gen_kaon_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/truth/kaons/kaon_pt_vs_jet_pt"), pjet.pt(), pt); + + if (originalPdg > 0) { + nKaonsInJetPosTruth++; + registryMC.fill(HIST("mc/jets/matched/truth/kaons/pos/mc_gen_kaon_pos_pt"), pt); + } else { + nKaonsInJetNegTruth++; + registryMC.fill(HIST("mc/jets/matched/truth/kaons/neg/mc_gen_kaon_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kProton) { + if (pt >= cfg.minPtProton && pt <= cfg.maxPtProton) { + registryMC.fill(HIST("mc/jets/matched/truth/protons/mc_gen_proton_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/truth/protons/proton_pt_vs_jet_pt"), pjet.pt(), pt); + if (originalPdg > 0) { + nProtonsInJetPosTruth++; + registryMC.fill(HIST("mc/jets/matched/truth/protons/pos/mc_gen_proton_pos_pt"), pt); + } else { + nProtonsInJetNegTruth++; + registryMC.fill(HIST("mc/jets/matched/truth/protons/neg/mc_gen_proton_neg_pt"), pt); + } + } + } + } + + registryMC.fill(HIST("mc/jets/matched/truth/general/jet_n_selected_constituents"), nSelectedConstituentsTruth); + + registryMC.fill(HIST("mc/jets/matched/truth/pions/pos/n_pions_per_jet"), nPionsInJetPosTruth); + registryMC.fill(HIST("mc/jets/matched/truth/kaons/pos/n_kaons_per_jet"), nKaonsInJetPosTruth); + registryMC.fill(HIST("mc/jets/matched/truth/protons/pos/n_protons_per_jet"), nProtonsInJetPosTruth); + + registryMC.fill(HIST("mc/jets/matched/truth/pions/neg/n_pions_per_jet"), nPionsInJetNegTruth); + registryMC.fill(HIST("mc/jets/matched/truth/kaons/neg/n_kaons_per_jet"), nKaonsInJetNegTruth); + registryMC.fill(HIST("mc/jets/matched/truth/protons/neg/n_protons_per_jet"), nProtonsInJetNegTruth); + + nPionsInJetTruth = nPionsInJetPosTruth + nPionsInJetNegTruth; + nKaonsInJetTruth = nKaonsInJetPosTruth + nKaonsInJetNegTruth; + nProtonsInJetTruth = nProtonsInJetPosTruth + nProtonsInJetNegTruth; + + registryMC.fill(HIST("mc/jets/matched/truth/pions/n_pions_per_jet"), nPionsInJetTruth); + registryMC.fill(HIST("mc/jets/matched/truth/kaons/n_kaons_per_jet"), nKaonsInJetTruth); + registryMC.fill(HIST("mc/jets/matched/truth/protons/n_protons_per_jet"), nProtonsInJetTruth); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/n_pions_matrix"), nPionsInJetTruth, nPionsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/n_kaons_matrix"), nKaonsInJetTruth, nKaonsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/n_protons_matrix"), nProtonsInJetTruth, nProtonsInJet); + + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/response_pions"), pjet.pt(), nPionsInJetTruth, djet.pt(), nPionsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/response_kaons"), pjet.pt(), nKaonsInJetTruth, djet.pt(), nKaonsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/response_protons"), pjet.pt(), nProtonsInJetTruth, djet.pt(), nProtonsInJet); + registryMC.fill(HIST("mc/jets/matched/reconstructed/composition/jet_pt_matrix"), pjet.pt(), djet.pt()); + + registryMC.fill(HIST("mc/jets/matched/truth/pions/n_pions_vs_jet_pt"), pjet.pt(), nPionsInJetTruth); + registryMC.fill(HIST("mc/jets/matched/truth/kaons/n_kaons_vs_jet_pt"), pjet.pt(), nKaonsInJetTruth); + registryMC.fill(HIST("mc/jets/matched/truth/protons/n_protons_vs_jet_pt"), pjet.pt(), nProtonsInJetTruth); + } + registryMC.fill(HIST("mc/jets/matched/reconstructed/general/collision_multiplicity"), jetsInCollision); + + auto const particleLevelJetsInCollision = particleLevelJets.sliceBy(mcpJetsPerMcCollision, collision.mcCollisionId()); + + for (auto const& truthJet : particleLevelJetsInCollision) { + + if (!isppRefAnalysis && ((std::abs(truthJet.eta()) + rJet) > (maxEta - deltaEtaEdge))) { + continue; + } + + if (isppRefAnalysis && std::abs(truthJet.eta()) > cfgEtaJetMax) { + continue; + } + + if (isppRefAnalysis && (truthJet.pt() < minJetPt || truthJet.pt() > maxJetPt)) { + continue; + } + + const double normalizedTruthJetArea = truthJet.area() / (PI * rJet * rJet); + + if (applyAreaCut && normalizedTruthJetArea < minNormalizedJetArea) { + continue; + } + + if (matchedTruthJetIndices.contains(truthJet.globalIndex())) { + continue; + } + + registryMC.fill(HIST("mc/jets/matched/miss/general/jet_pt"), truthJet.pt()); + registryMC.fill(HIST("mc/jets/matched/miss/general/jet_eta"), truthJet.eta()); + registryMC.fill(HIST("mc/jets/matched/miss/general/jet_phi"), truthJet.phi()); + registryMC.fill(HIST("mc/jets/matched/miss/general/jet_area"), truthJet.area()); + registryMC.fill(HIST("mc/jets/matched/miss/general/jet_n_constituents"), truthJet.tracksIds().size()); + + int nPionsInJetPosMiss = 0; + int nKaonsInJetPosMiss = 0; + int nProtonsInJetPosMiss = 0; + + int nPionsInJetNegMiss = 0; + int nKaonsInJetNegMiss = 0; + int nProtonsInJetNegMiss = 0; + + int nSelectedConstituentsMiss = 0; + + for (auto const& particle : truthJet.tracks_as()) { + + if (!particle.isPhysicalPrimary()) { + continue; + } + + if (particle.eta() < minEta || particle.eta() > maxEta) { + continue; + } + + const double pt = particle.pt(); + if (pt < minPt || pt > maxPt) { + continue; + } + + ++nSelectedConstituentsMiss; + + const int originalPdg = particle.pdgCode(); + const int pdg = std::abs(originalPdg); + + registryMC.fill(HIST("mc/jets/matched/miss/hadrons/mc_gen_hadron_pt"), pt); + + if (pdg == PDG_t::kPiPlus) { + if (pt >= cfg.minPtPion && pt <= cfg.maxPtPion) { + registryMC.fill(HIST("mc/jets/matched/miss/pions/mc_gen_pion_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/miss/pions/pion_pt_vs_jet_pt"), truthJet.pt(), pt); + + if (originalPdg > 0) { + nPionsInJetPosMiss++; + registryMC.fill(HIST("mc/jets/matched/miss/pions/pos/mc_gen_pion_pos_pt"), pt); + } else { + nPionsInJetNegMiss++; + registryMC.fill(HIST("mc/jets/matched/miss/pions/neg/mc_gen_pion_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kKPlus) { + if (pt >= cfg.minPtKaon && pt <= cfg.maxPtKaon) { + registryMC.fill(HIST("mc/jets/matched/miss/kaons/mc_gen_kaon_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/miss/kaons/kaon_pt_vs_jet_pt"), truthJet.pt(), pt); + + if (originalPdg > 0) { + nKaonsInJetPosMiss++; + registryMC.fill(HIST("mc/jets/matched/miss/kaons/pos/mc_gen_kaon_pos_pt"), pt); + } else { + nKaonsInJetNegMiss++; + registryMC.fill(HIST("mc/jets/matched/miss/kaons/neg/mc_gen_kaon_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kProton) { + if (pt >= cfg.minPtProton && pt <= cfg.maxPtProton) { + registryMC.fill(HIST("mc/jets/matched/miss/protons/mc_gen_proton_pt"), pt); + registryMC.fill(HIST("mc/jets/matched/miss/protons/proton_pt_vs_jet_pt"), truthJet.pt(), pt); + if (originalPdg > 0) { + nProtonsInJetPosMiss++; + registryMC.fill(HIST("mc/jets/matched/miss/protons/pos/mc_gen_proton_pos_pt"), pt); + } else { + nProtonsInJetNegMiss++; + registryMC.fill(HIST("mc/jets/matched/miss/protons/neg/mc_gen_proton_neg_pt"), pt); + } + } + } + } + + registryMC.fill(HIST("mc/jets/matched/miss/general/jet_n_selected_constituents"), nSelectedConstituentsMiss); + + registryMC.fill(HIST("mc/jets/matched/miss/pions/pos/n_pions_per_jet"), nPionsInJetPosMiss); + registryMC.fill(HIST("mc/jets/matched/miss/kaons/pos/n_kaons_per_jet"), nKaonsInJetPosMiss); + registryMC.fill(HIST("mc/jets/matched/miss/protons/pos/n_protons_per_jet"), nProtonsInJetPosMiss); + + registryMC.fill(HIST("mc/jets/matched/miss/pions/neg/n_pions_per_jet"), nPionsInJetNegMiss); + registryMC.fill(HIST("mc/jets/matched/miss/kaons/neg/n_kaons_per_jet"), nKaonsInJetNegMiss); + registryMC.fill(HIST("mc/jets/matched/miss/protons/neg/n_protons_per_jet"), nProtonsInJetNegMiss); + + const int nPionsInJetMiss = nPionsInJetPosMiss + nPionsInJetNegMiss; + const int nKaonsInJetMiss = nKaonsInJetPosMiss + nKaonsInJetNegMiss; + const int nProtonsInJetMiss = nProtonsInJetPosMiss + nProtonsInJetNegMiss; + + registryMC.fill(HIST("mc/jets/matched/miss/pions/n_pions_per_jet"), nPionsInJetMiss); + registryMC.fill(HIST("mc/jets/matched/miss/kaons/n_kaons_per_jet"), nKaonsInJetMiss); + registryMC.fill(HIST("mc/jets/matched/miss/protons/n_protons_per_jet"), nProtonsInJetMiss); + + registryMC.fill(HIST("mc/jets/matched/miss/pions/n_pions_vs_jet_pt"), truthJet.pt(), nPionsInJetMiss); + registryMC.fill(HIST("mc/jets/matched/miss/kaons/n_kaons_vs_jet_pt"), truthJet.pt(), nKaonsInJetMiss); + registryMC.fill(HIST("mc/jets/matched/miss/protons/n_protons_vs_jet_pt"), truthJet.pt(), nProtonsInJetMiss); + + registryMC.fill(HIST("mc/jets/matched/general/jet_match_counter"), 2.0); + } + } + PROCESS_SWITCH(JetHadronsPid, processMatchJets, "Process dector level jets matched to particle level", false); + + void processJetsMCP(MCPJetEvents::iterator const& collision, + ChargedMCPJets const& particleLevelJets, aod::JetParticles const&) + { + + registryMC.fill(HIST("mc/jets/truth/general/n_events_raw"), 1); + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + return; + } + + double zVertex = collision.posZ(); + if (std::abs(zVertex) > zVtx) { + return; + } + + registryMC.fill(HIST("mc/jets/truth/general/n_events"), 1); + registryMC.fill(HIST("mc/jets/truth/general/z_vtx"), zVertex); + + double centralRho = collision.rho(); + int jetsInCollision = 0; + + for (auto const& jet : particleLevelJets) { + + if (!isppRefAnalysis && ((std::abs(jet.eta()) + rJet) > (maxEta - deltaEtaEdge))) { + continue; + } + + if (isppRefAnalysis && std::abs(jet.eta()) > cfgEtaJetMax) { + continue; + } + + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) { + continue; + } + + double ptSub = jet.pt() - (centralRho * jet.area()); + if (ptSub < 0) { + ptSub = 0.0; + } + + if (!isppRefAnalysis && (ptSub < minJetPt || ptSub > maxJetPt)) { + continue; + } + + double normalizedJetArea = jet.area() / (PI * rJet * rJet); + if (applyAreaCut && normalizedJetArea < minNormalizedJetArea) { + continue; + } + + ++jetsInCollision; + + registryMC.fill(HIST("mc/jets/truth/general/jet_pt_subtracted"), ptSub); + registryMC.fill(HIST("mc/jets/truth/general/jet_pt_raw_vs_sub"), jet.pt(), ptSub); + registryMC.fill(HIST("mc/jets/truth/general/jet_eta"), jet.eta()); + registryMC.fill(HIST("mc/jets/truth/general/jet_phi"), jet.phi()); + registryMC.fill(HIST("mc/jets/truth/general/jet_area"), jet.area()); + registryMC.fill(HIST("mc/jets/truth/general/jet_pt"), jet.pt()); + registryMC.fill(HIST("mc/jets/truth/general/jet_n_constituents"), jet.tracksIds().size()); + + int nPionsInJetPos = 0; + int nKaonsInJetPos = 0; + int nProtonsInJetPos = 0; + int nPionsInJetNeg = 0; + int nKaonsInJetNeg = 0; + int nProtonsInJetNeg = 0; + int selectedConstituentCount = 0; + + for (auto const& particle : jet.tracks_as()) { + + if (!particle.isPhysicalPrimary()) { + continue; + } + + if (particle.eta() < minEta || particle.eta() > maxEta) { + continue; + } + + double pt = particle.pt(); + if (pt < minPt || pt > maxPt) { + continue; + } + + selectedConstituentCount++; + + int originalPdg = particle.pdgCode(); + int pdg = std::abs(originalPdg); + + registryMC.fill(HIST("mc/jets/truth/hadrons/mc_gen_hadron_pt"), pt); + if (pdg == PDG_t::kPiPlus) { + if (pt >= cfg.minPtPion && pt <= cfg.maxPtPion) { + registryMC.fill(HIST("mc/jets/truth/pions/mc_gen_pion_pt"), pt); + registryMC.fill(HIST("mc/jets/truth/pions/pion_pt_vs_jet_pt"), jet.pt(), pt); + + if (originalPdg > 0) { + nPionsInJetPos++; + registryMC.fill(HIST("mc/jets/truth/pions/pos/mc_gen_pion_pos_pt"), pt); + } else { + nPionsInJetNeg++; + registryMC.fill(HIST("mc/jets/truth/pions/neg/mc_gen_pion_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kKPlus) { + if (pt >= cfg.minPtKaon && pt <= cfg.maxPtKaon) { + registryMC.fill(HIST("mc/jets/truth/kaons/mc_gen_kaon_pt"), pt); + registryMC.fill(HIST("mc/jets/truth/kaons/kaon_pt_vs_jet_pt"), jet.pt(), pt); + + if (originalPdg > 0) { + nKaonsInJetPos++; + registryMC.fill(HIST("mc/jets/truth/kaons/pos/mc_gen_kaon_pos_pt"), pt); + } else { + nKaonsInJetNeg++; + registryMC.fill(HIST("mc/jets/truth/kaons/neg/mc_gen_kaon_neg_pt"), pt); + } + } + } else if (pdg == PDG_t::kProton) { + if (pt >= cfg.minPtProton && pt <= cfg.maxPtProton) { + registryMC.fill(HIST("mc/jets/truth/protons/mc_gen_proton_pt"), pt); + registryMC.fill(HIST("mc/jets/truth/protons/proton_pt_vs_jet_pt"), jet.pt(), pt); + if (originalPdg > 0) { + nProtonsInJetPos++; + registryMC.fill(HIST("mc/jets/truth/protons/pos/mc_gen_proton_pos_pt"), pt); + } else { + nProtonsInJetNeg++; + registryMC.fill(HIST("mc/jets/truth/protons/neg/mc_gen_proton_neg_pt"), pt); + } + } + } + } + + registryMC.fill(HIST("mc/jets/truth/general/jet_n_selected_constituents"), selectedConstituentCount); + + registryMC.fill(HIST("mc/jets/truth/pions/pos/n_pions_per_jet"), nPionsInJetPos); + registryMC.fill(HIST("mc/jets/truth/kaons/pos/n_kaons_per_jet"), nKaonsInJetPos); + registryMC.fill(HIST("mc/jets/truth/protons/pos/n_protons_per_jet"), nProtonsInJetPos); + registryMC.fill(HIST("mc/jets/truth/pions/neg/n_pions_per_jet"), nPionsInJetNeg); + registryMC.fill(HIST("mc/jets/truth/kaons/neg/n_kaons_per_jet"), nKaonsInJetNeg); + registryMC.fill(HIST("mc/jets/truth/protons/neg/n_protons_per_jet"), nProtonsInJetNeg); + + double nPionsInJet = nPionsInJetPos + nPionsInJetNeg; + double nKaonsInJet = nKaonsInJetPos + nKaonsInJetNeg; + double nProtonsInJet = nProtonsInJetPos + nProtonsInJetNeg; + + registryMC.fill(HIST("mc/jets/truth/pions/n_pions_per_jet"), nPionsInJet); + registryMC.fill(HIST("mc/jets/truth/kaons/n_kaons_per_jet"), nKaonsInJet); + registryMC.fill(HIST("mc/jets/truth/protons/n_protons_per_jet"), nProtonsInJet); + + registryMC.fill(HIST("mc/jets/truth/composition/particle_multiplicity_per_jet"), 1.0, nPionsInJet); + registryMC.fill(HIST("mc/jets/truth/composition/particle_multiplicity_per_jet"), 2.0, nKaonsInJet); + registryMC.fill(HIST("mc/jets/truth/composition/particle_multiplicity_per_jet"), 3.0, nProtonsInJet); + + registryMC.fill(HIST("mc/jets/truth/pions/n_pions_vs_jet_pt"), jet.pt(), nPionsInJet); + registryMC.fill(HIST("mc/jets/truth/kaons/n_kaons_vs_jet_pt"), jet.pt(), nKaonsInJet); + registryMC.fill(HIST("mc/jets/truth/protons/n_protons_vs_jet_pt"), jet.pt(), nProtonsInJet); + } + registryMC.fill(HIST("mc/jets/truth/general/collision_multiplicity"), jetsInCollision); + } + PROCESS_SWITCH(JetHadronsPid, processJetsMCP, "Particle-level charged jets in MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/jetLundPlane.cxx b/PWGJE/Tasks/jetLundPlane.cxx index 69c3af0fe1c..e956ff1e530 100644 --- a/PWGJE/Tasks/jetLundPlane.cxx +++ b/PWGJE/Tasks/jetLundPlane.cxx @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -55,8 +56,16 @@ namespace o2::aod { // Parent table: one row per collision stored in the MiniAOD DECLARE_SOA_COLUMN(MiniCollTag, miniCollTag, uint8_t); +DECLARE_SOA_COLUMN(MiniCollMcCollisionId, miniCollMcCollisionId, int64_t); +DECLARE_SOA_COLUMN(MiniCollWeight, miniCollWeight, float); +DECLARE_SOA_COLUMN(MiniCollPtHard, miniCollPtHard, float); +DECLARE_SOA_COLUMN(MiniCollCentrality, miniCollCentrality, float); DECLARE_SOA_TABLE(MiniCollisions, "AOD", "MINICOLL", - MiniCollTag); + MiniCollTag, + MiniCollMcCollisionId, + MiniCollWeight, + MiniCollPtHard, + MiniCollCentrality); // MiniJets -> MiniCollisions DECLARE_SOA_INDEX_COLUMN_CUSTOM(MiniCollision, miniCollision, "MINICOLLS"); @@ -148,6 +157,12 @@ struct JetMatchInfo { float otherPt{}; }; +struct McEventInfo { + int64_t mcCollisionId{-1}; + float weight{1.f}; + float ptHard{-1.f}; +}; + inline float deltaPhi(float phi1, float phi2) { return RecoDecay::constrainAngle(phi1 - phi2, -o2::constants::math::PI); @@ -208,7 +223,7 @@ std::vector buildUniqueSplittingMatches(const std::vector -std::vector buildFastJetInputs(ConstituentRangeT&& constituents, float trackPtMin) +std::vector buildFastJetInputs(ConstituentRangeT const& constituents, float trackPtMin) { std::vector fjInputs; fjInputs.reserve(64); @@ -271,6 +286,9 @@ struct JetLundPlaneUnfolding { Configurable jetEtaMax{"jetEtaMax", 0.5f, "max jet eta"}; Configurable jetR{"jetR", 0.4f, "jet radius (must match derived tables)"}; Configurable trackPtMin{"trackPtMin", 0.15f, "min constituent pT"}; + Configurable centralityMin{"centralityMin", 0.f, "minimum FT0M centrality for subtracted data"}; + Configurable centralityMax{"centralityMax", 100.f, "maximum FT0M centrality for subtracted data"}; + Configurable reclusteringAlgorithm{"reclusteringAlgorithm", 0, "reclustering algorithm: 0 = C/A, 1 = generalized-kT p=0.5 (tau)"}; Configurable nBinsJetPt{"nBinsJetPt", 200, "jet pT bins"}; Configurable jetPtMax{"jetPtMax", 200.f, "jet pT max"}; @@ -300,7 +318,7 @@ struct JetLundPlaneUnfolding { Produces outMiniSplittingsAll; Produces outMiniJetMatches; - // FastJet reclustering setup (C/A) + // FastJet reclustering setup JetFinder reclusterer; std::vector jetReclustered; @@ -391,7 +409,13 @@ struct JetLundPlaneUnfolding { // reclusterer config reclusterer.isReclustering = true; - reclusterer.algorithm = fastjet::cambridge_algorithm; + if (reclusteringAlgorithm.value == 0) { + reclusterer.algorithm = fastjet::cambridge_algorithm; + } else if (reclusteringAlgorithm.value == 1) { + reclusterer.algorithm = fastjet::genkt_algorithm; + reclusterer.fastjetExtraParam = 0.5; + } + reclusterer.jetR = 1.0; // recluster radius for declustering tree reclusterer.recombScheme = fastjet::E_scheme; reclusterer.strategy = fastjet::Best; @@ -405,8 +429,13 @@ struct JetLundPlaneUnfolding { (aod::jet::eta < jetEtaMax.node()) && (aod::jet::r == nround(jetR.node() * 100.f)); + // Reconstructed collisions grouped by their associated MC collision. + PresliceUnsorted> CollisionsPerMCPCollision = aod::jmccollisionlb::mcCollisionId; + // Type aliases using RecoJets = soa::Join; + using SubtractedRecoJets = soa::Join; using DetJetsMatched = soa::Join; @@ -522,15 +551,14 @@ struct JetLundPlaneUnfolding { } // DATA / RECO PROCESSING - void processData(soa::Filtered::iterator const&, - soa::Filtered const& jets, - aod::JetTracks const& tracks) + template + void processDataImpl(JetTableT const& jets, ConstituentTableT const& tracks, float centrality) { registry.fill(HIST("hEventCount"), 0.5); int miniCollIdx = -1; if (writeMiniAOD.value) { - outMiniCollisions(static_cast(0)); + outMiniCollisions(static_cast(0), int64_t{-1}, 1.f, -1.f, centrality); miniCollIdx = outMiniCollisions.lastIndex(); } for (auto const& jet : jets) { @@ -555,13 +583,34 @@ struct JetLundPlaneUnfolding { } } } - PROCESS_SWITCH(JetLundPlaneUnfolding, processData, "Reco/data Lund + jet spectra", true); + + void processData(soa::Filtered::iterator const&, + soa::Filtered const& jets, + aod::JetTracks const& tracks) + { + processDataImpl(jets, tracks, -1.f); + } + PROCESS_SWITCH(JetLundPlaneUnfolding, processData, "Unsubtracted reco/data Lund + jet spectra", true); + + void processDataEventWiseSubCentrality(soa::Filtered::iterator const& collision, + soa::Filtered const& jets, + aod::JetTracksSub const& tracks) + { + const float centrality = collision.centFT0M(); + if (centrality < centralityMin.value || centrality >= centralityMax.value) { + return; + } + processDataImpl(jets, tracks, centrality); + } + PROCESS_SWITCH(JetLundPlaneUnfolding, processDataEventWiseSubCentrality, + "Event-wise constituent-subtracted reco/data Lund with FT0M centrality", false); // MC PROCESSING (det + part + response) - void processMC(DetJetsMatched const& detJets, - PartJetsMatched const& partJets, - aod::JetCollisions const&, + void processMC(soa::Filtered const& detJets, + soa::Filtered const& partJets, + soa::Filtered const& collisions, + aod::JetMcCollisions const& mcCollisions, aod::JetTracks const& tracks, aod::JetParticles const& particles) { @@ -571,6 +620,7 @@ struct JetLundPlaneUnfolding { std::unordered_map truthMatchedById; std::unordered_map> truthSplittingsById; std::unordered_map truthBestDet; + std::unordered_set acceptedTruthJetKeys; std::unordered_set detSplittingsWritten; std::unordered_set truthSplittingsWritten; auto h6 = registry.get(HIST("hLundResponse6D")); @@ -583,6 +633,21 @@ struct JetLundPlaneUnfolding { std::unordered_map detJetToMiniJetIdx; std::unordered_map partJetToMiniJetIdx; + std::unordered_map mcEventInfoById; + for (auto const& mcCollision : mcCollisions) { + const int64_t mcCollisionId = mcCollision.globalIndex(); + mcEventInfoById.emplace(mcCollisionId, + McEventInfo{.mcCollisionId = mcCollisionId, .weight = mcCollision.weight(), .ptHard = mcCollision.ptHard()}); + } + + std::unordered_map mcEventInfoByDetCollisionId; + for (auto const& collision : collisions) { + auto mcInfoIt = mcEventInfoById.find(collision.mcCollisionId()); + if (mcInfoIt != mcEventInfoById.end()) { + mcEventInfoByDetCollisionId.emplace(collision.globalIndex(), mcInfoIt->second); + } + } + // --- Truth pass --- // Fill inclusive truth histograms, cache truth splittings, write all accepted // truth jets to MiniJets, and determine the best detector candidate for each truth jet. @@ -591,10 +656,22 @@ struct JetLundPlaneUnfolding { continue; } + auto collisionsPerMCPJet = collisions.sliceBy(CollisionsPerMCPCollision, partJet.mcCollisionId()); + if (collisionsPerMCPJet.size() < 1) { + continue; + } + + auto partMcInfoIt = mcEventInfoById.find(partJet.mcCollisionId()); + if (partMcInfoIt == mcEventInfoById.end()) { + continue; + } + const auto partMcInfo = partMcInfoIt->second; + registry.fill(HIST("hJetPtTruthAll"), partJet.pt()); fillJetCountSummary(4); const uint64_t truthJetKey = partJet.globalIndex(); + acceptedTruthJetKeys.insert(truthJetKey); auto spl = getPrimarySplittings(partJet, particles); truthSplittingsById[truthJetKey] = spl; @@ -607,7 +684,7 @@ struct JetLundPlaneUnfolding { int partMiniCollIdx = -1; auto collIt = partMiniCollByKey.find(partCollKey); if (collIt == partMiniCollByKey.end()) { - outMiniCollisions(static_cast(0)); + outMiniCollisions(static_cast(0), partMcInfo.mcCollisionId, partMcInfo.weight, partMcInfo.ptHard, -1.f); partMiniCollIdx = outMiniCollisions.lastIndex(); partMiniCollByKey.emplace(partCollKey, partMiniCollIdx); } else { @@ -629,7 +706,7 @@ struct JetLundPlaneUnfolding { JetMatchInfo bestDet{}; bool foundDet = false; - for (auto const& candDetJet : partJet.template matchedJetGeo_as()) { + for (auto const& candDetJet : partJet.template matchedJetGeo_as>()) { if (!passJetFiducial(candDetJet, rWanted)) { continue; } @@ -659,13 +736,19 @@ struct JetLundPlaneUnfolding { } // --- Detector loop --- - // Write all accepted detector jets to MiniJets. A final matched pair is accepted only + // Write accepted detector jets to MiniJets. A final matched pair is accepted only // if the detector jet and truth jet are mutual best matches under the same cuts. for (auto const& detJet : detJets) { if (!passJetFiducial(detJet, rWanted)) { continue; } + auto detMcInfoIt = mcEventInfoByDetCollisionId.find(detJet.collisionId()); + if (detMcInfoIt == mcEventInfoByDetCollisionId.end()) { + continue; + } + const auto detMcInfo = detMcInfoIt->second; + registry.fill(HIST("hJetPtRecoAll"), detJet.pt()); fillJetCountSummary(1); @@ -680,7 +763,7 @@ struct JetLundPlaneUnfolding { int detMiniCollIdx = -1; auto collIt = detMiniCollByKey.find(detCollKey); if (collIt == detMiniCollByKey.end()) { - outMiniCollisions(static_cast(0)); + outMiniCollisions(static_cast(0), detMcInfo.mcCollisionId, detMcInfo.weight, detMcInfo.ptHard, -1.f); detMiniCollIdx = outMiniCollisions.lastIndex(); detMiniCollByKey.emplace(detCollKey, detMiniCollIdx); } else { @@ -706,7 +789,12 @@ struct JetLundPlaneUnfolding { bool foundMatch = false; std::vector bestPartSpl; - for (auto const& candPartJet : detJet.template matchedJetGeo_as()) { + for (auto const& candPartJet : detJet.template matchedJetGeo_as>()) { + const uint64_t candTruthKey = candPartJet.globalIndex(); + if (!acceptedTruthJetKeys.contains(candTruthKey)) { + continue; + } + if (!passJetFiducial(candPartJet, rWanted)) { continue; } @@ -723,7 +811,6 @@ struct JetLundPlaneUnfolding { } if (!foundMatch || dR < bestTruth.dR) { - const uint64_t candTruthKey = candPartJet.globalIndex(); bestTruth.otherKey = candTruthKey; bestTruth.dR = dR; bestTruth.relPt = rel; @@ -799,9 +886,9 @@ struct JetLundPlaneUnfolding { for (const auto& m : splitMatches) { const auto& detS = detSpl[m.recoIdx]; const auto& partS = bestPartSpl[m.truthIdx]; - double x[6] = {detS.lnRoverDR, detS.lnkt, detJet.pt(), - partS.lnRoverDR, partS.lnkt, bestPartPt}; - h6->Fill(x); + const std::array x{detS.lnRoverDR, detS.lnkt, detJet.pt(), + partS.lnRoverDR, partS.lnkt, bestPartPt}; + h6->Fill(x.data()); registry.fill(HIST("hLnRoverDRResidual"), detS.lnRoverDR - partS.lnRoverDR); registry.fill(HIST("hLnKtResidual"), detS.lnkt - partS.lnkt); } @@ -813,6 +900,15 @@ struct JetLundPlaneUnfolding { continue; } + auto collisionsPerMCPJet = collisions.sliceBy(CollisionsPerMCPCollision, partJet.mcCollisionId()); + if (collisionsPerMCPJet.size() < 1) { + continue; + } + + if (!mcEventInfoById.contains(partJet.mcCollisionId())) { + continue; + } + const uint64_t truthJetKey = partJet.globalIndex(); if (!truthMatchedById[truthJetKey]) { registry.fill(HIST("hJetPtTruthMiss"), partJet.pt()); diff --git a/PWGJE/Tasks/jetLundReclustering.cxx b/PWGJE/Tasks/jetLundReclustering.cxx index 95b5b027931..f85f34497ff 100644 --- a/PWGJE/Tasks/jetLundReclustering.cxx +++ b/PWGJE/Tasks/jetLundReclustering.cxx @@ -67,7 +67,7 @@ struct JetLundReclustering { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); registry.add("PrimaryLundPlane_kT", "Primary Lund 3D plane;ln(R/Delta);ln(k_{t}/GeV);{p}_{t}", {HistType::kTH3F, {{100, 0, 10}, {100, -10, 10}, {20, 0, 200}}}); registry.add("PrimaryLundPlane_z", "Primary Lund 3D plane;ln(R/Delta);ln(1/z);{p}_{t}", {HistType::kTH3F, {{100, 0, 10}, {100, 0, 10}, {20, 0, 200}}}); diff --git a/PWGJE/Tasks/jetOutlierQA.cxx b/PWGJE/Tasks/jetOutlierQA.cxx index d0d802a95a8..d37047e6008 100644 --- a/PWGJE/Tasks/jetOutlierQA.cxx +++ b/PWGJE/Tasks/jetOutlierQA.cxx @@ -93,8 +93,8 @@ struct JetOutlierQATask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); auto jetPtTemp = 0.0; jetPtBins.push_back(jetPtTemp); @@ -358,10 +358,10 @@ struct JetOutlierQATask { } } registry.fill(HIST("h3_jet_pt_frac_pt_ambiguous_pt_hat"), jet.pt(), pt_amb / pt_total, pTHat, weight); - registry.fill(HIST("h3_jet_pt_frac_constituents_ambiguous_pt_hat"), jet.pt(), double(nAmbTracks) / double(jet.template tracks_as().size()), pTHat, weight); + registry.fill(HIST("h3_jet_pt_frac_constituents_ambiguous_pt_hat"), jet.pt(), static_cast(nAmbTracks) / static_cast(jet.template tracks_as().size()), pTHat, weight); registry.fill(HIST("h3_jet_pt_frac_pt_unmatched_particle_pt_hat"), jet.pt(), pt_unmatched / pt_total, pTHat, weight); - registry.fill(HIST("h3_jet_pt_frac_constituents_unmatched_particle_pt_hat"), jet.pt(), double(nUnmatchedTracks) / double(jet.template tracks_as().size()), pTHat, weight); + registry.fill(HIST("h3_jet_pt_frac_constituents_unmatched_particle_pt_hat"), jet.pt(), static_cast(nUnmatchedTracks) / static_cast(jet.template tracks_as().size()), pTHat, weight); } void processJetsAmbiguous(soa::Filtered>::iterator const& collision, @@ -418,7 +418,7 @@ struct JetOutlierQATask { { // // collision-based outlier checks based on BC and z position - // based on 2-event correlation checks in PWGDQ/Tasks/tableReader_withAssoc.cxx + // based on 2-event correlation checks in PWGDQ/Tasks/tableReader_withAssoc.h // fBCCollMap.clear(); @@ -607,8 +607,8 @@ struct JetOutlierQATask { if (std::abs(diffColl) < 6) { float eventWeightOutlier = collisionOutlier.mcCollision().weight(); double pTHatOutlier = collisionOutlier.mcCollision().ptHard(); - registry.fill(HIST("h2_neighbour_pt_hat_outlier"), float(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); - registry.fill(HIST("h2_neighbour_track_pt_outlier"), float(diffColl + 0.1), track.pt(), eventWeightOutlier); + registry.fill(HIST("h2_neighbour_pt_hat_outlier"), static_cast(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); + registry.fill(HIST("h2_neighbour_track_pt_outlier"), static_cast(diffColl + 0.1), track.pt(), eventWeightOutlier); } } // now match tracks to their MC particle, check the MC collision ID of this particle, and @@ -658,20 +658,20 @@ struct JetOutlierQATask { registry.fill(HIST("h2_outlier_event_Ntracks_different_selected_JJ"), pTHat, nJJdifferentSelected); registry.fill(HIST("h2_outlier_event_Ntracks_different_selected_MB"), pTHat, nMBdifferentSelected); registry.fill(HIST("h2_outlier_event_Ntracks_same_selected_JJ"), pTHat, nJJsameSelected); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, float(nMBdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_JJ"), pTHat, float(nJJdifferent) / float(nJJdifferent + nJJsame + nMBdifferent)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_MB"), pTHat, float(nMBdifferent) / float(nJJdifferent + nJJsame + nMBdifferent)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_JJ"), pTHat, float(nJJdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); - registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, float(nMBdifferentSelected) / float(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, static_cast(nMBdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_JJ"), pTHat, static_cast(nJJdifferent) / static_cast(nJJdifferent + nJJsame + nMBdifferent)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_MB"), pTHat, static_cast(nMBdifferent) / static_cast(nJJdifferent + nJJsame + nMBdifferent)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_JJ"), pTHat, static_cast(nJJdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); + registry.fill(HIST("h2_outlier_event_tracks_frac_different_selected_MB"), pTHat, static_cast(nMBdifferentSelected) / static_cast(nJJdifferentSelected + nJJsameSelected + nMBdifferentSelected)); // now check where outlier comes from // LOG(info) <<"outlier comes from " << (mcCollisionIDOutlier == mcCollisionIDcoll ? "same" : "different") << " event which is a " << (subGenIDOutlier == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap ? " MB-gap" : " jet-jet") << " collision with mcCollisionID = " << mcCollisionIDOutlier; - registry.fill(HIST("h2_outlier_collision_ID_difference"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_outlier_collision_ID_difference"), pTHat, static_cast(outlierCollisionIDDifference)); // if outlier comes from different collision, check which type and set flags if (mcCollisionIDOutlier != mcCollisionIDcoll && subGenIDOutlier != jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { isOutlierEventDifferentJJCollision = true; float deltaZ = collisionMCOutlier.begin().posZ() - collision.mcCollision().posZ(); registry.fill(HIST("h_DeltaZ_Outlier"), deltaZ); - registry.fill(HIST("h2_DeltaZ_Outlier_difference"), deltaZ, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_DeltaZ_Outlier_difference"), deltaZ, static_cast(outlierCollisionIDDifference)); } else if (mcCollisionIDOutlier != mcCollisionIDcoll && subGenIDOutlier == jetderiveddatautilities::JCollisionSubGeneratorId::mbGap) { isOutlierEventDifferentMBCollision = true; } @@ -683,7 +683,7 @@ struct JetOutlierQATask { registry.fill(HIST("h_track_pt_same_collision"), track.pt(), weight); registry.fill(HIST("h_track_pt_eta_same_collision"), track.pt(), track.eta(), weight); registry.fill(HIST("h_track_pt_phi_same_collision"), track.pt(), track.phi(), weight); - registry.fill(HIST("h2_collision_ID_difference_same_collision"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_collision_ID_difference_same_collision"), pTHat, static_cast(outlierCollisionIDDifference)); registry.fill(HIST("h_pt_hard_track_pt_same_collision"), pTHat != 0.0 ? track.pt() / pTHat : 0.0, track.pt(), weight); // include selection on pThat of particle @@ -699,7 +699,7 @@ struct JetOutlierQATask { registry.fill(HIST("h_track_pt_no_JJ_different"), track.pt(), weight); registry.fill(HIST("h_track_pt_eta_no_JJ_different"), track.pt(), track.eta(), weight); registry.fill(HIST("h_track_pt_phi_no_JJ_different"), track.pt(), track.phi(), weight); - registry.fill(HIST("h2_collision_ID_difference_no_JJ_different"), pTHat, float(outlierCollisionIDDifference)); + registry.fill(HIST("h2_collision_ID_difference_no_JJ_different"), pTHat, static_cast(outlierCollisionIDDifference)); } else { registry.fill(HIST("h_track_pt_no_JJ_different_rejected"), track.pt(), weight); } @@ -711,8 +711,8 @@ struct JetOutlierQATask { if (std::abs(diffColl) < 6) { // LOG(info) << "pThat = " << pTHat << "pThat neighbour = "<(diffColl + 0.1), pTHatOutlier, eventWeightOutlier); + registry.fill(HIST("h2_neighbour_track_pt_all"), static_cast(diffColl + 0.1), track.pt(), eventWeightOutlier); } } } diff --git a/PWGJE/Tasks/jetPlanarFlow.cxx b/PWGJE/Tasks/jetPlanarFlow.cxx index 480dcc69a53..507d4ca7aee 100644 --- a/PWGJE/Tasks/jetPlanarFlow.cxx +++ b/PWGJE/Tasks/jetPlanarFlow.cxx @@ -134,9 +134,9 @@ struct JetPlanarFlowTask { void init(o2::framework::InitContext&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - particleSelection = static_cast(particleSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + particleSelection = particleSelections; precisionMask = 0xFFFFFC00; AxisSpec centralityAxis = {1200, -10., 110., "Centrality"}; diff --git a/PWGJE/Tasks/jetShape.cxx b/PWGJE/Tasks/jetShape.cxx index 887cdad8de5..9762f2ff1df 100644 --- a/PWGJE/Tasks/jetShape.cxx +++ b/PWGJE/Tasks/jetShape.cxx @@ -24,6 +24,7 @@ #include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include +#include #include #include #include @@ -47,6 +49,10 @@ using namespace o2::framework::expressions; struct JetShapeTask { + static constexpr float PionMass = static_cast(o2::constants::physics::MassPionCharged); + static constexpr float ProtonMass = static_cast(o2::constants::physics::MassProton); + static constexpr float RandomConeNegativeSideProbability = 0.5f; + Configurable nBinsNSigma{"nBinsNSigma", 100, "Number of nsigma bins"}; Configurable nSigmaMin{"nSigmaMin", -10.0f, "Min value of nsigma"}; Configurable nSigmaMax{"nSigmaMax", 10.0f, "Max value of nsigma"}; @@ -75,12 +81,19 @@ struct JetShapeTask { Configurable nBinsDistance{"nBinsDistance", 7, "Number of distance bins"}; Configurable distanceMax{"distanceMax", 0.7f, "Max value of distance"}; Configurable nSigmaTofCut{"nSigmaTofCut", 2.0f, "Number of sigma cut for TOF PID"}; - Configurable tpcNSigmaPrMin{"tpcNSigmaPrMin", -3.5f, "Min value of tpcNsigmaProton"}; - Configurable tpcNSigmaPrMax{"tpcNSigmaPrMax", 0.5f, "Max value of tpcNsigmaProton"}; - Configurable tpcNSigmaPiMin{"tpcNSigmaPiMin", -0.5f, "Min value of tpcNsigmaPion"}; - Configurable tpcNSigmaPiMax{"tpcNSigmaPiMax", 3.5f, "Max value of tpcNsigmaPion"}; - Configurable randomConeDeltaPhiMin{"randomConeDeltaPhiMin", static_cast(o2::constants::math::PIThird), "Minimum delta phi for random cone"}; - Configurable randomConeDeltaPhiMax{"randomConeDeltaPhiMax", static_cast(2.0f * o2::constants::math::PIThird), "Maximum delta phi for random cone"}; + Configurable tpcNSigmaPrMin{"tpcNSigmaPrMin", -2.0f, "Min value of tpcNsigmaProton"}; + Configurable tpcNSigmaPrMax{"tpcNSigmaPrMax", 2.0f, "Max value of tpcNsigmaProton"}; + Configurable tpcNSigmaPiMin{"tpcNSigmaPiMin", -2.0f, "Min value of tpcNsigmaPion"}; + Configurable tpcNSigmaPiMax{"tpcNSigmaPiMax", 2.0f, "Max value of tpcNsigmaPion"}; + Configurable randomConeDeltaPhiMin{"randomConeDeltaPhiMin", +o2::constants::math::PIThird, "Minimum delta phi used only when randomConeFullAzimuth=false"}; + Configurable randomConeDeltaPhiMax{"randomConeDeltaPhiMax", 2.0f * o2::constants::math::PIThird, "Maximum delta phi used only when randomConeFullAzimuth=false"}; + Configurable enableRandomCone{"enableRandomCone", true, "Fill one accepted random cone per selected jet"}; + Configurable randomConeFullAzimuth{"randomConeFullAzimuth", true, "Generate random cones over the full azimuth instead of the old perpendicular-side window"}; + Configurable randomConeMaxAttempts{"randomConeMaxAttempts", 100, "Maximum attempts for each random cone"}; + Configurable randomConeSeed{"randomConeSeed", 0u, "TRandom3 seed; 0 lets ROOT choose a unique seed"}; + Configurable fillDetailedQA{"fillDetailedQA", false, "Fill high-volume dE/dx, beta and distance QA histograms"}; + Configurable applyCentralityCut{"applyCentralityCut", true, "Apply centralityMinForCut <= centrality < centralityMaxForCut inside this task"}; + Configurable useRapiditySelection{"useRapiditySelection", false, "Use species-dependent |y| cut in addition to the detector |eta| acceptance (cross-check only)"}; Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; @@ -89,7 +102,8 @@ struct JetShapeTask { Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; - Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + Configurable trackSelections{"trackSelections", "globalTracks", "nominal track selection for yields and tracking efficiency"}; + Configurable trackSelectionsForDca{"trackSelectionsForDca", "QualityTracksWDCA", "wide-DCA track selection used only for data DCA distributions and MC DCA templates"}; Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "used to make a cut on the jet areas"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", 5.0, "minimum pT selection on jet constituent"}; @@ -99,109 +113,225 @@ struct JetShapeTask { Configurable> distanceCategory{"distanceCategory", {0.00f, 0.05f, 0.10f, 0.15f, 0.20f, 0.25f, 0.30f, 0.35f, 0.40f, 0.45f, 0.50f, 0.55f, 0.60f, 0.65f, 0.70f}, "distance of category"}; // for ppi production - Configurable etaTrUp{"etaTrUp", 0.7f, "maximum track eta"}; + Configurable etaTrUp{"etaTrUp", 0.9f, "maximum track eta"}; + Configurable trackPtMinForAnalysis{"trackPtMinForAnalysis", 0.15f, "minimum reconstructed and generated particle pT used for yields and efficiencies"}; Configurable dcaxyCutMax{"dcaxyCutMax", 2.0f, "maximum DCA xy"}; Configurable chi2ItsMax{"chi2ItsMax", 15.0f, "its chi2 cut"}; Configurable chi2TpcMax{"chi2TpcMax", 4.0f, "tpc chi2 cut"}; Configurable nclItsMin{"nclItsMin", 2.0f, "its # of cluster cut"}; - Configurable nclTpcMin{"nclTpcMin", 100.0f, "tpc # if cluster cut"}; + Configurable nclTpcMin{"nclTpcMin", 70.0f, "tpc # if cluster cut"}; Configurable nclcrossTpcMin{"nclcrossTpcMin", 70.0f, "tpc # of crossedRows cut"}; - Configurable mcRapidityMax{"mcRapidityMax", 0.5f, "maximum mctrack y"}; + Configurable mcRapidityMax{"mcRapidityMax", 0.5f, "maximum |y| used only when useRapiditySelection=true"}; Configurable epsilon{"epsilon", 1e-6, "standard for aboid division of zero"}; Configurable maxDeltaEtaSafe{"maxDeltaEtaSafe", 0.9f, "maximum track eta for cut"}; Configurable nSigmaMaxForDcaxy{"nSigmaMaxForDcaxy", 4.0f, "maximum nSigma for DCAxy"}; Configurable triggerMasks{"triggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; - HistogramRegistry registry{ - "registry", - {{"tpcTofPi", "tpcTofPi", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"tpcTofPr", "tpcTofPr", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"tpcPi", "tpcPi", {HistType::kTH2F, {{nBinsP, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"tofPi", "tofPi", {HistType::kTH2F, {{nBinsPt, 0, ptMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"tpcPr", "tpcPr", {HistType::kTH2F, {{nBinsP, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"tofPr", "tofPr", {HistType::kTH2F, {{nBinsPt, 0, ptMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"tpcDedx", "tpcDedx", {HistType::kTHnSparseD, {{nBinsPForDedx, 0, pMax}, {nBinsTpcDedx, 0, 1000}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"tofBeta", "tofBeta", {HistType::kTHnSparseD, {{nBinsPForBeta, 0, pMax}, {nBinsTofBeta, 0.4, 1.1}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"pVsPtForPr", "pVsPtForPr", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"pVsPtForPi", "pVsPtPi", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"trackPhi", "trackPhi", {HistType::kTH1F, {{80, -1, 7}}}}, - {"trackEta", "trackEta", {HistType::kTH1F, {{100, -1, 1}}}}, - {"trackTpcNClsCrossedRows", "trackTpcNClsCrossedRows", {HistType::kTH1F, {{50, 0, 200}}}}, - {"trackDcaXY", "trackDcaXY", {HistType::kTH1F, {{40, -10, 10}}}}, - {"trackItsChi2NCl", "trackItsChi2NCl", {HistType::kTH1F, {{60, 0, 30}}}}, - {"trackTpcChi2NCl", "trackTpcChi2NCl", {HistType::kTH1F, {{100, 0, 50}}}}, - {"trackTpcNClsFound", "trackTpcNClsFound", {HistType::kTH1F, {{100, 0, 200}}}}, - {"trackItsNCls", "trackItsNCls", {HistType::kTH1F, {{10, 0, 10}}}}, - {"jetTpcTofPi", "jetTpcTofPi", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsDistance, 0, distanceMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"jetTpcTofPr", "jetTpcTofPr", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsDistance, 0, distanceMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"tpcTofPiPerpJet", "tpcTofPiPerpJet", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"tpcTofPrPerpJet", "tpcTofPrPerpJet", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"jetTpcPi", "jetTpcPi", {HistType::kTH2F, {{nBinsP, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"jetTofPi", "jetTofPi", {HistType::kTH2F, {{nBinsPt, 0, ptMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"jetTpcPr", "jetTpcPr", {HistType::kTH2F, {{nBinsP, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"jetTofPr", "jetTofPr", {HistType::kTH2F, {{nBinsPt, 0, ptMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}}}}, - {"jetTpcDedx", "jetTpcDedx", {HistType::kTHnSparseD, {{nBinsPForDedx, 0, pMax}, {nBinsTpcDedx, 0, 1000}, {nBinsDistance, 0, distanceMax}}}}, - {"tpcDedxPerpJet", "tpcDedxPerpJet", {HistType::kTH2F, {{nBinsPForDedx, 0, pMax}, {nBinsTpcDedx, 0, 1000}}}}, - {"jetTofBeta", "jetTofBeta", {HistType::kTH2F, {{nBinsPForBeta, 0, pMax}, {nBinsTofBeta, 0.4, 1.1}}}}, - {"jetpVsPtForPr", "jetpVsPtForPr", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsDistance, 0, distanceMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"jetpVsPtForPi", "jetpVsPtPi", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsDistance, 0, distanceMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"pVsPtForPrPerpJet", "pVsPtForPrPerpJet", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"pVsPtForPiPerpJet", "pVsPtPionPerpJet", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"jetDcaPr", "jetDcaPr", {HistType::kTHnSparseD, {{nBinsPtForDca, 0, ptMax}, {nBinsDcaxyForData, dcaxyMin, dcaxyMax}, {nBinsDistance, 0, distanceMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"jetDcaPi", "jetDcaPi", {HistType::kTHnSparseD, {{nBinsPtForDca, 0, ptMax}, {nBinsDcaxyForData, dcaxyMin, dcaxyMax}, {nBinsDistance, 0, distanceMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaPrPerpJet", "dcaPrPerpJet", {HistType::kTHnSparseD, {{nBinsPtForDca, 0, ptMax}, {nBinsDcaxyForData, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaPiPerpJet", "dcaPiPerpJet", {HistType::kTHnSparseD, {{nBinsPtForDca, 0, ptMax}, {nBinsDcaxyForData, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"tpcTofPiRandCone", "tpcTofPiRandCone", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"tpcTofPrRandCone", "tpcTofPrRandCone", {HistType::kTHnSparseD, {{nBinsPForCut, 0, pMax}, {nBinsNSigma, nSigmaMin, nSigmaMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"pVsPtForPrRandCone", "pVsPtForPrRandCone", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"pVsPtForPiRandCone", "pVsPtPionRandCone", {HistType::kTHnSparseD, {{nBinsP, 0, pMax}, {nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaPrRandCone", "dcaPrRandCone", {HistType::kTHnSparseD, {{nBinsPtForDca, 0, ptMax}, {nBinsDcaxyForData, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaPiRandCone", "dcaPiRandCone", {HistType::kTHnSparseD, {{nBinsPtForDca, 0, ptMax}, {nBinsDcaxyForData, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"jetPt", "jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, - {"jetEta", "jet #eta;#eta_{jet};entries", {HistType::kTH1F, {{100, -1.0, 1.0}}}}, - {"jetPhi", "jet #phi;#phi_{jet};entries", {HistType::kTH1F, {{80, -1.0, 7.}}}}, - {"area", "area", {HistType::kTH2F, {{nBinsCentrality, centralityMinForCut, centralityMaxForCut}, {100, 0, 2}}}}, - {"rho", "rho", {HistType::kTH1F, {{120, 0, 300}}}}, - {"ptCorr", "Corrected jet pT; p_{T}^{corr} (GeV/c); Counts", {HistType::kTH1F, {{200, 0, 200}}}}, - {"ptCorrVsDistance", "ptcorr_vs_distance", {HistType::kTH2F, {{70, 0, 0.7}, {100, 0, 100}}}}, - {"jetDistanceVsTrackpt", "trackpt_vs_distance_injet", {HistType::kTH2F, {{70, 0, 0.7}, {100, 0, 100}}}}, - {"ptSum", "ptSum", {HistType::kTHnSparseD, {{14, 0, 0.7}, {nBinsJetShapeFunc, 0, jetShapeFuncMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptSumBg1", "ptSumBg1", {HistType::kTHnSparseD, {{14, 0, 0.7}, {nBinsJetShapeFunc, 0, jetShapeFuncMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptSumBg2", "ptSumBg2", {HistType::kTHnSparseD, {{14, 0, 0.7}, {nBinsJetShapeFunc, 0, jetShapeFuncMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"event/vertexz", ";Vtx_{z} (cm);Entries", {HistType::kTH1F, {{100, -20, 20}}}}, - {"eventCounterJetShape", "eventCounterJetShape", {HistType::kTH1F, {{nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"eventCounterJet", "eventCounterJet", {HistType::kTH1F, {{nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"eventCounterInc", "eventCounterInc", {HistType::kTH1F, {{nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"eventCounterRandCone", "Number of Random Cones;Centrality (%);Count", {HistType::kTH1F, {{nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"eventCounterMc", "eventCounterMc", {HistType::kTH1F, {{nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptVsCentrality", "ptvscentrality", {HistType::kTH2F, {{100, 0, 100}, {300, 0, 300}}}}, - {"ptResolution", "ptResolution", {HistType::kTH2F, {{nBinsPt, 0, ptMax}, {100, -1.0, +1.0}}}}, - {"mcCentralityReco", "mcCentralityReco", {HistType::kTH1F, {{100, 0, 100}}}}, - {"mcCentralitySim", "mcCentralitySim", {HistType::kTH1F, {{100, 0, 100}}}}, - {"ptHistogramPion", "ptHistogramPion", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsDcaxyForMc, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptHistogramKaon", "ptHistogramKaon", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsDcaxyForMc, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptHistogramProton", "ptHistogramProton", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsDcaxyForMc, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptHistogramPionTof", "ptHistogramPionTof", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptHistogramKaonTof", "ptHistogramKaonTof", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptHistogramProtonTof", "ptHistogramProtonTof", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaDecayPion", "dcaDecayPion", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsDcaxyForMc, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaDecayProton", "dcaDecayProton", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsDcaxyForMc, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaMaterialPion", "dcaMaterialPion", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsDcaxyForMc, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"dcaMaterialProton", "dcaMaterialProton", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsDcaxyForMc, dcaxyMin, dcaxyMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptGeneratedPion", "ptGeneratedPion", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptGeneratedKaon", "ptGeneratedKaon", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}, - {"ptGeneratedProton", "ptGeneratedProton", {HistType::kTHnSparseD, {{nBinsPt, 0, ptMax}, {nBinsJetPt, jetPtMinForCut, jetPtMaxForCut}, {nBinsCentrality, centralityMinForCut, centralityMaxForCut}}}}}}; + HistogramRegistry registry{"registry"}; + TRandom3 randomNumber; std::vector eventSelectionBits; int trackSelection = -1; + int trackSelectionForDca = -1; std::vector triggerMaskBits; void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); + trackSelectionForDca = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelectionsForDca)); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); + randomNumber.SetSeed(randomConeSeed); + + // Histograms definition + registry.add("tpcTofPi", "tpcTofPi", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tpcTofPr", "tpcTofPr", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tpcPi", "tpcPi", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + registry.add("tofPi", "tofPi", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + registry.add("tpcPr", "tpcPr", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + registry.add("tofPr", "tofPr", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + registry.add("tpcDedx", "tpcDedx", HistType::kTHnSparseD, {{nBinsPForDedx.value, 0, pMax.value}, {nBinsTpcDedx.value, 0, 1000}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tofBeta", "tofBeta", HistType::kTHnSparseD, {{nBinsPForBeta.value, 0, pMax.value}, {nBinsTofBeta.value, 0.4, 1.1}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("pVsPtForPr", "pVsPtForPr", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("pVsPtForPi", "pVsPtPi", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPiInc", "inclusive pion-candidate DCAxy from wide-DCA tracks", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPrInc", "inclusive proton-candidate DCAxy from wide-DCA tracks", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("trackPhi", "trackPhi", HistType::kTH1F, {{80, -1, 7}}); + registry.add("trackEta", "trackEta", HistType::kTH1F, {{100, -1, 1}}); + registry.add("trackTpcNClsCrossedRows", "trackTpcNClsCrossedRows", HistType::kTH1F, {{50, 0, 200}}); + registry.add("trackDcaXY", "trackDcaXY", HistType::kTH1F, {{40, -10, 10}}); + registry.add("trackItsChi2NCl", "trackItsChi2NCl", HistType::kTH1F, {{60, 0, 30}}); + registry.add("trackTpcChi2NCl", "trackTpcChi2NCl", HistType::kTH1F, {{100, 0, 50}}); + registry.add("trackTpcNClsFound", "trackTpcNClsFound", HistType::kTH1F, {{100, 0, 200}}); + registry.add("trackItsNCls", "trackItsNCls", HistType::kTH1F, {{10, 0, 10}}); + + registry.add("jetTpcPi", "jetTpcPi", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + registry.add("jetTofPi", "jetTofPi", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + registry.add("jetTpcPr", "jetTpcPr", HistType::kTH2F, {{nBinsP.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + registry.add("jetTofPr", "jetTofPr", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}}); + + registry.add("jetTpcDedx", "jetTpcDedx", HistType::kTHnSparseD, {{nBinsPForDedx.value, 0, pMax.value}, {nBinsTpcDedx.value, 0, 1000}, {nBinsDistance.value, 0, distanceMax.value}}); + registry.add("tpcDedxPerpJet", "tpcDedxPerpJet", HistType::kTH2F, {{nBinsPForDedx.value, 0, pMax.value}, {nBinsTpcDedx.value, 0, 1000}}); + registry.add("jetTofBeta", "jetTofBeta", HistType::kTH2F, {{nBinsPForBeta.value, 0, pMax.value}, {nBinsTofBeta.value, 0.4, 1.1}}); + + // Jet (in Cone) + registry.add("jetTpcTofPi", "jetTpcTofPi", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("jetTpcTofPr", "jetTpcTofPr", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("jetpVsPtForPr", "jetpVsPtForPr", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("jetpVsPtForPi", "jetpVsPtPi", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("jetDcaPr", "jetDcaPr", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("jetDcaPi", "jetDcaPi", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Perp Cone + registry.add("tpcTofPiPerpJet", "tpcTofPiPerpJet", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tpcTofPrPerpJet", "tpcTofPrPerpJet", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("pVsPtForPrPerpJet", "pVsPtForPrPerpJet", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("pVsPtForPiPerpJet", "pVsPtPionPerpJet", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPrPerpJet", "dcaPrPerpJet", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPiPerpJet", "dcaPiPerpJet", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Random Cone + registry.add("tpcTofPiRandCone", "tpcTofPiRandCone", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("tpcTofPrRandCone", "tpcTofPrRandCone", HistType::kTHnSparseD, {{nBinsPForCut.value, 0, pMax.value}, {nBinsNSigma.value, nSigmaMin.value, nSigmaMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("pVsPtForPrRandCone", "pVsPtForPrRandCone", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("pVsPtForPiRandCone", "pVsPtPionRandCone", HistType::kTHnSparseD, {{nBinsP.value, 0, pMax.value}, {nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPrRandCone", "dcaPrRandCone", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPiRandCone", "dcaPiRandCone", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDcaxyForData.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + registry.add("jetPt", "jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH2F, {{200, 0., 200.}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("jetPtMc", "MC jet pT;#it{p}_{T,jet} (GeV/#it{c});entries", HistType::kTH2F, {{200, 0., 200.}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("jetEta", "jet #eta;#eta_{jet};entries", HistType::kTH1F, {{100, -1.0, 1.0}}); + registry.add("jetPhi", "jet #phi;#phi_{jet};entries", HistType::kTH1F, {{80, -1.0, 7.}}); + registry.add("area", "area", HistType::kTH2F, {{100, 0, 2}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("rho", "rho", HistType::kTH2F, {{120, 0, 300}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptCorr", "Corrected jet pT; p_{T}^{corr} (GeV/c); Counts", HistType::kTH1F, {{200, 0, 200}}); + registry.add("jetPtCorrCent", "Selected jets;p_{T,jet}^{corr} (GeV/c);Centrality (%)", HistType::kTH2F, {{200, 0, 200}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("areaExposureJet", "Sum of accepted annulus area for signal;r;p_{T,jet}^{corr};Centrality", HistType::kTHnSparseD, {{nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("areaExposurePerp", "Sum of accepted annulus area for two perpendicular cones;r;p_{T,jet}^{corr};Centrality", HistType::kTHnSparseD, {{nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("areaExposureRandCone", "Sum of accepted annulus area for random cones;r;p_{T,jet}^{corr};Centrality", HistType::kTHnSparseD, {{nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptCorrVsDistance", "ptcorr_vs_distance", HistType::kTH2F, {{70, 0, 0.7}, {100, 0, 100}}); + registry.add("jetDistanceVsTrackpt", "trackpt_vs_distance_injet", HistType::kTH2F, {{70, 0, 0.7}, {100, 0, 100}}); + registry.add("ptSum", "ptSum", HistType::kTHnSparseD, {{14, 0, 0.7}, {nBinsJetShapeFunc.value, 0, jetShapeFuncMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptSumBg1", "ptSumBg1", HistType::kTHnSparseD, {{14, 0, 0.7}, {nBinsJetShapeFunc.value, 0, jetShapeFuncMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptSumBg2", "ptSumBg2", HistType::kTHnSparseD, {{14, 0, 0.7}, {nBinsJetShapeFunc.value, 0, jetShapeFuncMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + registry.add("event/vertexz", ";Vtx_{z} (cm);Entries", HistType::kTH1F, {{100, -20, 20}}); + registry.add("eventCounterJetShape", "eventCounterJetShape", HistType::kTH1F, {{nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("eventCounterJet", "eventCounterJet", HistType::kTH1F, {{nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("eventCounterInc", "eventCounterInc", HistType::kTH1F, {{nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("eventCounterRandCone", "Number of Random Cones;Centrality (%);Count", HistType::kTH2F, {{nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("eventCounterMc", "eventCounterMc", HistType::kTH1F, {{nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + registry.add("ptVsCentrality", "ptvscentrality", HistType::kTH2F, {{100, 0, 100}, {300, 0, 300}}); + registry.add("ptResolution", "ptResolution", HistType::kTH2F, {{nBinsPt.value, 0, ptMax.value}, {100, -1.0, +1.0}}); + registry.add("mcCentralityReco", "mcCentralityReco", HistType::kTH1F, {{100, 0, 100}}); + + // MC Efficiency Denominators + // Jet (In-cone) + registry.add("ptGenPi", "ptGenPi", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenKa", "ptGenKa", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenPr", "ptGenPr", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + // Perp Cone + registry.add("ptGenPiPerp", "ptGenPiPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenKaPerp", "ptGenKaPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenPrPerp", "ptGenPrPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + // Random cone + registry.add("ptGenPiRand", "ptGenPiRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenKaRand", "ptGenKaRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenPrRand", "ptGenPrRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + // Inclusive (No JetPt axis) + registry.add("ptGenPiInc", "ptGenPiInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenKaInc", "ptGenKaInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("ptGenPrInc", "ptGenPrInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // MC Efficiency Numerators + // Inclusive + registry.add("effNumTpcPiInc", "effNumTpcPiInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcKaInc", "effNumTpcKaInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcPrInc", "effNumTpcPrInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPiInc", "effNumTofPiInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofKaInc", "effNumTofKaInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPrInc", "effNumTofPrInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPiInc", "True primary pion reconstructed with the full analysis PID selection", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPrInc", "True primary proton reconstructed with the full analysis PID selection", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Jet (In-cone) + registry.add("effNumTpcPiJet", "effNumTpcPiJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcKaJet", "effNumTpcKaJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcPrJet", "effNumTpcPrJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPiJet", "effNumTofPiJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofKaJet", "effNumTofKaJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPrJet", "effNumTofPrJet", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPiJet", "Full pion PID efficiency numerator in signal region", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPrJet", "Full proton PID efficiency numerator in signal region", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Perp Cone + registry.add("effNumTpcPiPerp", "effNumTpcPiPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcKaPerp", "effNumTpcKaPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcPrPerp", "effNumTpcPrPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPiPerp", "effNumTofPiPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofKaPerp", "effNumTofKaPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPrPerp", "effNumTofPrPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPiPerp", "Full pion PID efficiency numerator in perpendicular cones", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPrPerp", "Full proton PID efficiency numerator in perpendicular cones", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Random cone efficiency numerators + registry.add("effNumTpcPiRand", "effNumTpcPiRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcKaRand", "effNumTpcKaRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTpcPrRand", "effNumTpcPrRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPiRand", "effNumTofPiRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofKaRand", "effNumTofKaRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumTofPrRand", "effNumTofPrRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPiRand", "Full pion PID efficiency numerator in random cones", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("effNumPidPrRand", "Full proton PID efficiency numerator in random cones", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // DCA Templates for Primary Fraction + // Jet (In-cone) + registry.add("dcaPrimPi", "dcaPrimPi", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPi", "dcaDecayPi", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPi", "dcaMatPi", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPrimPr", "dcaPrimPr", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPr", "dcaDecayPr", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPr", "dcaMatPr", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Perp Cone + registry.add("dcaPrimPiPerp", "dcaPrimPiPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPiPerp", "dcaDecayPiPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPiPerp", "dcaMatPiPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPrimPrPerp", "dcaPrimPrPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPrPerp", "dcaDecayPrPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPrPerp", "dcaMatPrPerp", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Random cone + registry.add("dcaPrimPiRand", "dcaPrimPiRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPiRand", "dcaDecayPiRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPiRand", "dcaMatPiRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPrimPrRand", "dcaPrimPrRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPrRand", "dcaDecayPrRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPrRand", "dcaMatPrRand", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Inclusive + registry.add("dcaPrimPiInc", "dcaPrimPiInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPiInc", "dcaDecayPiInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPiInc", "dcaMatPiInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaPrimPrInc", "dcaPrimPrInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaDecayPrInc", "dcaDecayPrInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + registry.add("dcaMatPrInc", "dcaMatPrInc", HistType::kTHnSparseD, {{nBinsPt.value, 0, ptMax.value}, {nBinsDcaxyForMc.value, dcaxyMin.value, dcaxyMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}}); + + // Transfer from the wide-DCA fit sample to the nominal global-track sample. + // Axes: + // Jet/Perp: reco pT, reco distance, detector-level corrected jet pT, + // centrality, species (0=pi,1=p), origin (0=primary,1=decay,2=material) + // Inclusive: reco pT, centrality, species, origin + registry.add("originCountWideJet", "wide-DCA origin counts around matched detector-level jets", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); + registry.add("originCountGlobalJet", "nominal-global origin counts around matched detector-level jets", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); + registry.add("originCountWidePerp", "wide-DCA origin counts in perpendicular cones", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); + registry.add("originCountGlobalPerp", "nominal-global origin counts in perpendicular cones", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); + registry.add("originCountWideRand", "wide-DCA origin counts in random cones", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); + registry.add("originCountGlobalRand", "nominal-global origin counts in random cones", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsDistance.value, 0, distanceMax.value}, {nBinsJetPt.value, jetPtMinForCut.value, jetPtMaxForCut.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); + registry.add("originCountWideInc", "inclusive wide-DCA origin counts", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); + registry.add("originCountGlobalInc", "inclusive nominal-global origin counts", HistType::kTHnSparseD, {{nBinsPtForDca.value, 0, ptMax.value}, {nBinsCentrality.value, centralityMinForCut.value, centralityMaxForCut.value}, {2, 0, 2}, {3, 0, 3}}); } template @@ -218,11 +348,11 @@ struct JetShapeTask { return false; } } - static constexpr double LeadingConstituentPtMinValue = 5.0; static constexpr double LeadingConstituentPtMaxValue = 9998.0; bool checkConstituentPt = true; - bool checkConstituentMinPt = - (leadingConstituentPtMin > LeadingConstituentPtMinValue); + // A non-negative value activates the minimum-leading-constituent cut. + // Therefore the default value 5 GeV/c is actually applied. + bool checkConstituentMinPt = (leadingConstituentPtMin >= 0.0f); bool checkConstituentMaxPt = (leadingConstituentPtMax < LeadingConstituentPtMaxValue); if (!checkConstituentMinPt && !checkConstituentMaxPt) { @@ -249,13 +379,202 @@ struct JetShapeTask { return true; } - Filter jetCuts = aod::jet::pt > jetPtMin&& aod::jet::r == - nround(jetR.node() * 100.0f); + template + bool passCommonTrackQuality(T const& track) + { + return track.pt() >= trackPtMinForAnalysis && + std::abs(track.eta()) <= etaTrUp && + track.tpcNClsCrossedRows() >= nclcrossTpcMin && + track.itsChi2NCl() <= chi2ItsMax && + track.tpcChi2NCl() <= chi2TpcMax && + track.tpcNClsFound() >= nclTpcMin && + track.itsNCls() >= nclItsMin; + } + + template + bool passNominalTrackDca(T const& track) + { + return std::abs(track.dcaXY()) <= dcaxyCutMax; + } + + bool passCentrality(float centrality) const + { + return !applyCentralityCut.value || + (centrality >= centralityMinForCut.value && + centrality < centralityMaxForCut.value); + } + + static float absDeltaPhi(float phi1, float phi2) + { + float dPhi = std::abs(phi1 - phi2); + if (dPhi > o2::constants::math::PI) { + dPhi = o2::constants::math::TwoPI - dPhi; + } + return dPhi; + } + + static float rapidityFromPtEtaMass(float pt, float eta, float mass) + { + const float pz = pt * std::sinh(eta); + const float mt = std::sqrt(pt * pt + mass * mass); + return std::asinh(pz / mt); + } + + bool passSpeciesKinematics(float pt, float eta, float mass) const + { + if (std::abs(eta) >= etaTrUp.value) { + return false; + } + + if (!useRapiditySelection.value) { + return true; + } + + return std::abs(rapidityFromPtEtaMass(pt, eta, mass)) < + mcRapidityMax.value; + } + + template + bool passTruthKinematics(T const& particle) const + { + if (particle.pt() < trackPtMinForAnalysis.value || + std::abs(particle.eta()) >= etaTrUp.value) { + return false; + } + + return !useRapiditySelection.value || + std::abs(particle.y()) < mcRapidityMax.value; + } + + static double diskAreaPrimitive(double radius, double x) + { + if (radius <= 0.0) { + return 0.0; + } + x = std::clamp(x, -radius, radius); + const double root = std::sqrt(std::max(0.0, radius * radius - x * x)); + return x * root + radius * radius * std::asin(x / radius); + } + + static double effectiveDiskArea(double radius, double axisEta, double etaMin, double etaMax) + { + const double lower = std::max(-radius, etaMin - axisEta); + const double upper = std::min(radius, etaMax - axisEta); + if (upper <= lower) { + return 0.0; + } + return diskAreaPrimitive(radius, upper) - diskAreaPrimitive(radius, lower); + } + + static double effectiveAnnulusArea(double rMin, double rMax, double axisEta, double etaMin, double etaMax) + { + return effectiveDiskArea(rMax, axisEta, etaMin, etaMax) - + effectiveDiskArea(rMin, axisEta, etaMin, etaMax); + } + + struct ConeVetoJet { + float eta; + float phi; + float ptCorr; + }; + + struct RandomConeAxis { + float eta; + float phi; + float referenceJetPtCorr; + }; + + std::vector makeRandomCones(std::vector const& jets) + { + std::vector cones; + if (!enableRandomCone || jets.empty() || etaTrUp <= distanceMax) { + return cones; + } + + cones.reserve(jets.size()); + const float etaMin = -etaTrUp + distanceMax; + const float etaMax = etaTrUp - distanceMax; + const float vetoDistance = jetR + distanceMax; + + for (const auto& referenceJet : jets) { + bool accepted = false; + float rcEta = 0.0f; + float rcPhi = 0.0f; + + for (int attempt = 0; attempt < randomConeMaxAttempts; ++attempt) { + rcEta = randomNumber.Uniform(etaMin, etaMax); + if (randomConeFullAzimuth) { + rcPhi = randomNumber.Uniform(0.0f, o2::constants::math::TwoPI); + } else { + float dPhi = randomNumber.Uniform(randomConeDeltaPhiMin, randomConeDeltaPhiMax); + if (randomNumber.Uniform() < RandomConeNegativeSideProbability) { + dPhi = -dPhi; + } + rcPhi = RecoDecay::constrainAngle(referenceJet.phi + dPhi); + } + + accepted = true; + for (const auto& vetoJet : jets) { + const float dEta = rcEta - vetoJet.eta; + const float dPhi = absDeltaPhi(rcPhi, vetoJet.phi); + if (dEta * dEta + dPhi * dPhi <= vetoDistance * vetoDistance) { + accepted = false; + break; + } + } + if (accepted) { + break; + } + } + + if (accepted) { + cones.push_back({rcEta, rcPhi, referenceJet.ptCorr}); + } + } + return cones; + } + + enum class ExposureRegion { Signal, + Perpendicular }; + + void fillAreaExposure(float axisEta, float jetPtCorr, float centrality, ExposureRegion region, double multiplicity = 1.0) + { + const double dr = distanceMax / static_cast(nBinsDistance); + for (int i = 0; i < nBinsDistance; ++i) { + const double rMin = i * dr; + const double rMax = (i + 1) * dr; + const double rCenter = 0.5 * (rMin + rMax); + const double area = multiplicity * effectiveAnnulusArea(rMin, rMax, axisEta, -etaTrUp, etaTrUp); + if (region == ExposureRegion::Signal) { + registry.fill(HIST("areaExposureJet"), rCenter, jetPtCorr, centrality, area); + } else { + registry.fill(HIST("areaExposurePerp"), rCenter, jetPtCorr, centrality, area); + } + } + } + + void fillFullRandomConeArea(float jetPtCorr, float centrality) + { + const double dr = distanceMax / static_cast(nBinsDistance); + for (int i = 0; i < nBinsDistance; ++i) { + const double rMin = i * dr; + const double rMax = (i + 1) * dr; + const double rCenter = 0.5 * (rMin + rMax); + const double area = o2::constants::math::PI * (rMax * rMax - rMin * rMin); + registry.fill(HIST("areaExposureRandCone"), rCenter, jetPtCorr, centrality, area); + } + } + + Filter jetCuts = aod::jet::pt > jetPtMin&& aod::jet::r == nround(jetR.node() * 100.0f); Filter jetCollisionFilter = nabs(aod::jcollision::posZ) < vertexZCut; Filter collisionFilter = nabs(aod::collision::posZ) < vertexZCut; Filter mcCollisionFilter = nabs(aod::jmccollision::posZ) < vertexZCut; + using JetTracksWithPI = soa::Join; + using JetTracksWithPIMC = soa::Join; + using FullTrackInfo = soa::Join; + using FullTrackInfoMC = soa::Join; void processJetShape(soa::Filtered>::iterator const& collision, aod::JetTracks const& tracks, soa::Join const& jets) { @@ -263,7 +582,11 @@ struct JetShapeTask { return; } - registry.fill(HIST("eventCounterJetShape"), collision.centFT0M()); + const float cent = collision.centFT0M(); + if (!passCentrality(cent)) { + return; + } + registry.fill(HIST("eventCounterJetShape"), cent); size_t nBins = distanceCategory->size() - 1; @@ -286,7 +609,6 @@ struct JetShapeTask { accumSumBg2.reserve(jets.size()); float rho = collision.rho(); - float cent = collision.centFT0M(); for (const auto& jet : jets) { if (!isAcceptedJet(jet)) { @@ -308,6 +630,10 @@ struct JetShapeTask { accumSumBg2.push_back(std::vector(nBins, 0.f)); } + if (cachedJets.empty()) { + return; + } + for (const auto& track : tracks) { if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; @@ -322,8 +648,9 @@ struct JetShapeTask { float dEta = trkEta - jet.eta; - if (std::abs(dEta) > maxDistance) + if (std::abs(dEta) > maxDistance) { continue; + } float dPhi = RecoDecay::constrainAngle(trkPhi - jet.phi); float distance = std::sqrt(dEta * dEta + dPhi * dPhi); @@ -373,8 +700,6 @@ struct JetShapeTask { for (size_t i = 0; i < cachedJets.size(); ++i) { const auto& jet = cachedJets[i]; - registry.fill(HIST("rho"), rho); - registry.fill(HIST("jetPt"), jet.pt); registry.fill(HIST("ptCorr"), jet.ptCorr); for (size_t k = 0; k < nBins; k++) { @@ -404,522 +729,885 @@ struct JetShapeTask { PROCESS_SWITCH(JetShapeTask, processJetShape, "JetShape", false); - void processJetProductionRatio(soa::Filtered>::iterator const& collision, soa::Join const& tracks, FullTrackInfo const&, soa::Join const& jets) + template + void runJetProductionRatio(CollisionT const& collision, + JetTracksWithPI const& tracks, + soa::Join const& jets, + float rho) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - registry.fill(HIST("eventCounterJet"), collision.centFT0M()); - registry.fill(HIST("event/vertexz"), collision.posZ()); - float rho = collision.rho(); - float centrality = collision.centFT0M(); + const float centrality = collision.centFT0M(); + if (!passCentrality(centrality)) { + return; + } + + registry.fill(HIST("eventCounterJet"), centrality); + registry.fill(HIST("event/vertexz"), collision.posZ()); + registry.fill(HIST("rho"), rho, centrality); struct CachedJet { float pt, eta, phi, ptCorr; float phiBg1, phiBg2; }; std::vector cachedJets; + std::vector vetoJets; cachedJets.reserve(jets.size()); + vetoJets.reserve(jets.size()); for (const auto& jet : jets) { - if (!isAcceptedJet(jet)) + if (!isAcceptedJet(jet)) { continue; + } - float ptCorr = jet.pt() - rho * jet.area(); - float phiBg1 = - RecoDecay::constrainAngle(jet.phi() + o2::constants::math::PIHalf); - float phiBg2 = - RecoDecay::constrainAngle(jet.phi() - o2::constants::math::PIHalf); + const float ptCorr = jet.pt() - rho * jet.area(); + const float phiBg1 = RecoDecay::constrainAngle(jet.phi() + o2::constants::math::PIHalf); + const float phiBg2 = RecoDecay::constrainAngle(jet.phi() - o2::constants::math::PIHalf); registry.fill(HIST("jetEta"), jet.eta()); registry.fill(HIST("jetPhi"), jet.phi()); - registry.fill(HIST("area"), centrality, jet.area()); - - cachedJets.push_back( - {jet.pt(), jet.eta(), jet.phi(), ptCorr, phiBg1, phiBg2}); - } + registry.fill(HIST("area"), jet.area(), centrality); + registry.fill(HIST("jetPt"), jet.pt(), centrality); + registry.fill(HIST("ptCorr"), ptCorr); + registry.fill(HIST("jetPtCorrCent"), ptCorr, centrality); - bool isValidRC = false; - float rcEta = 0.0f; - float rcPhi = 0.0f; + fillAreaExposure(jet.eta(), ptCorr, centrality, ExposureRegion::Signal); + fillAreaExposure(jet.eta(), ptCorr, centrality, ExposureRegion::Perpendicular, 2.0); - if (!cachedJets.empty()) { - constexpr unsigned int RandomSeed = 0; - TRandom3 randomNumber(RandomSeed); - - const auto& leadJet = cachedJets[0]; - - constexpr float MaxTrackEta = 0.9f; - constexpr int MaxAttempts = 100; // for RandomCone - constexpr float FlipProbability = 0.5f; - - // Range to generate - float rcEtaMin = -MaxTrackEta + distanceMax; - float rcEtaMax = MaxTrackEta - distanceMax; - - int attempts = 0; - while (!isValidRC && attempts < MaxAttempts) { - rcEta = randomNumber.Uniform(rcEtaMin, rcEtaMax); - - float dPhi = randomNumber.Uniform(randomConeDeltaPhiMin, randomConeDeltaPhiMax); - - // flipProbability (0.5) - if (randomNumber.Uniform() < FlipProbability) { - dPhi = -dPhi; - } - - rcPhi = RecoDecay::constrainAngle(leadJet.phi + dPhi); - - float dPhiLead = std::abs(rcPhi - leadJet.phi); - if (dPhiLead > o2::constants::math::PI) - dPhiLead = o2::constants::math::TwoPI - dPhiLead; - float dEtaLead = rcEta - leadJet.eta; - float distLead = std::sqrt(dEtaLead * dEtaLead + dPhiLead * dPhiLead); + cachedJets.push_back({jet.pt(), jet.eta(), jet.phi(), ptCorr, phiBg1, phiBg2}); + vetoJets.push_back({jet.eta(), jet.phi(), ptCorr}); + } - if (distLead > (jetR + distanceMax)) { - isValidRC = true; - } - attempts++; - } + if (cachedJets.empty()) { + return; + } - if (isValidRC) { - registry.fill(HIST("eventCounterRandCone"), centrality); - } + const auto randomCones = makeRandomCones(vetoJets); + for (const auto& cone : randomCones) { + registry.fill(HIST("eventCounterRandCone"), cone.referenceJetPtCorr, centrality); + fillFullRandomConeArea(cone.referenceJetPtCorr, centrality); } + const float maxR2 = distanceMax * distanceMax; + + // The jet finder remains configured with globalTracks. The nominal-yield + // sample and the wide-DCA sample are selected independently relative to + // the same reconstructed jet axes. for (const auto& jetTrack : tracks) { - if (!jetderiveddatautilities::selectTrack(jetTrack, trackSelection)) { + const bool passesNominalBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelection); + const bool passesWideDcaBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelectionForDca); + if (!passesNominalBit && !passesWideDcaBit) { continue; } - auto track = jetTrack.track_as(); - - if (std::abs(track.eta()) > etaTrUp) - continue; - if (track.tpcNClsCrossedRows() < nclcrossTpcMin) - continue; - if (std::abs(track.dcaXY()) > dcaxyCutMax) - continue; - if (track.itsChi2NCl() > chi2ItsMax) - continue; - if (track.tpcChi2NCl() > chi2TpcMax) - continue; - if (track.tpcNClsFound() < nclTpcMin) + const auto track = jetTrack.track_as(); + if (!passCommonTrackQuality(track)) { continue; - if (track.itsNCls() < nclItsMin) - continue; - - float trkPt = track.pt(); - float trkP = track.p(); - float trkEta = track.eta(); - float trkPhi = track.phi(); - - float tpcSig = track.tpcSignal(); - float tpcPi = track.tpcNSigmaPi(); - float tofPi = track.tofNSigmaPi(); - float tpcPr = track.tpcNSigmaPr(); - float tofPr = track.tofNSigmaPr(); - float beta = track.beta(); - - // PID QA - registry.fill(HIST("jetTpcPi"), trkP, tpcPi); - registry.fill(HIST("jetTofPi"), trkPt, tofPi); - registry.fill(HIST("jetTpcPr"), trkP, tpcPr); - registry.fill(HIST("jetTofPr"), trkPt, tofPr); + } - bool hasTofPi = (std::abs(tofPi) < nSigmaTofCut); - bool hasTofPr = (std::abs(tofPr) < nSigmaTofCut); - bool isTpcPiRange = (tpcPi > tpcNSigmaPiMin && tpcPi < tpcNSigmaPiMax); - bool isTpcPrRange = (tpcPr > tpcNSigmaPrMin && tpcPr < tpcNSigmaPrMax); + const bool passesNominal = passesNominalBit && passNominalTrackDca(track); + const bool passesWideDca = passesWideDcaBit; + if (!passesNominal && !passesWideDca) { + continue; + } - float nSigmaSqPr = tpcPr * tpcPr + tofPr * tofPr; - float nSigmaSqPi = tpcPi * tpcPi + tofPi * tofPi; + const float trkPt = track.pt(); + const float trkP = track.p(); + const float trkEta = track.eta(); + const float trkPhi = track.phi(); + + const float tpcSig = track.tpcSignal(); + const float tpcPi = track.tpcNSigmaPi(); + const float tofPi = track.tofNSigmaPi(); + const float tpcPr = track.tpcNSigmaPr(); + const float tofPr = track.tofNSigmaPr(); + const float beta = track.beta(); + + const bool passPiKinematics = passSpeciesKinematics(trkPt, trkEta, PionMass); + const bool passPrKinematics = passSpeciesKinematics(trkPt, trkEta, ProtonMass); + const bool hasTofPi = passPiKinematics && track.hasTOF() && (std::abs(tofPi) < nSigmaTofCut); + const bool hasTofPr = passPrKinematics && track.hasTOF() && (std::abs(tofPr) < nSigmaTofCut); + const bool isTpcPiRange = (tpcPi > tpcNSigmaPiMin && tpcPi < tpcNSigmaPiMax); + const bool isTpcPrRange = (tpcPr > tpcNSigmaPrMin && tpcPr < tpcNSigmaPrMax); + + if (passesNominal && fillDetailedQA) { + registry.fill(HIST("jetTpcPi"), trkP, tpcPi); + registry.fill(HIST("jetTofPi"), trkPt, tofPi); + registry.fill(HIST("jetTpcPr"), trkP, tpcPr); + registry.fill(HIST("jetTofPr"), trkPt, tofPr); + } + // Signal and perpendicular cones: one loop over accepted jets. for (const auto& jet : cachedJets) { - - float dEta = trkEta - jet.eta; - if (std::abs(dEta) > maxDeltaEtaSafe) + const float dEta = trkEta - jet.eta; + if (std::abs(dEta) >= distanceMax) { continue; + } - float dPhi = RecoDecay::constrainAngle(trkPhi - jet.phi); - float distance = std::sqrt(dEta * dEta + dPhi * dPhi); - - // Background judge - float deltaPhiBg1 = RecoDecay::constrainAngle(trkPhi - jet.phiBg1); - float deltaPhiBg2 = RecoDecay::constrainAngle(trkPhi - jet.phiBg2); - float distBg1 = std::sqrt(dEta * dEta + deltaPhiBg1 * deltaPhiBg1); - float distBg2 = std::sqrt(dEta * dEta + deltaPhiBg2 * deltaPhiBg2); - - // --- Background Fill --- - if (distBg1 < distanceMax || distBg2 < distanceMax) { - registry.fill(HIST("tpcDedxPerpJet"), trkP, tpcSig); - - // dcaXY - if (track.hasTOF()) { - if (nSigmaSqPr < nSigmaMaxForDcaxy) { - registry.fill(HIST("dcaPrPerpJet"), trkPt, track.dcaXY(), jet.ptCorr, centrality); - } + const float dPhi = absDeltaPhi(trkPhi, jet.phi); + const float distanceSq = dEta * dEta + dPhi * dPhi; - if (nSigmaSqPi < nSigmaMaxForDcaxy) { - registry.fill(HIST("dcaPiPerpJet"), trkPt, track.dcaXY(), jet.ptCorr, centrality); - } + auto fillPerpendicular = [&](float conePhi) { + const float dPhiBg = absDeltaPhi(trkPhi, conePhi); + const float distBgSq = dEta * dEta + dPhiBg * dPhiBg; + if (distBgSq >= maxR2) { + return; } + const float distBg = std::sqrt(distBgSq); - if (hasTofPi) { - registry.fill(HIST("tpcTofPiPerpJet"), trkP, tpcPi, jet.ptCorr, centrality); - if (isTpcPiRange) { - registry.fill(HIST("pVsPtForPiPerpJet"), trkP, trkPt, jet.ptCorr, centrality); + if (passesWideDca) { + if (hasTofPr && isTpcPrRange) { + registry.fill(HIST("dcaPrPerpJet"), trkPt, track.dcaXY(), distBg, jet.ptCorr, centrality); } - } - if (hasTofPr) { - registry.fill(HIST("tpcTofPrPerpJet"), trkP, tpcPr, jet.ptCorr, centrality); - if (isTpcPrRange) { - registry.fill(HIST("pVsPtForPrPerpJet"), trkP, trkPt, jet.ptCorr, centrality); + if (hasTofPi && isTpcPiRange) { + registry.fill(HIST("dcaPiPerpJet"), trkPt, track.dcaXY(), distBg, jet.ptCorr, centrality); } } - } - - if (isValidRC) { - const auto& leadJet = cachedJets[0]; - - float dEtaRC = trkEta - rcEta; - float dPhiRC = std::abs(trkPhi - rcPhi); - if (dPhiRC > o2::constants::math::PI) - dPhiRC = o2::constants::math::TwoPI - dPhiRC; - float distRC = std::sqrt(dEtaRC * dEtaRC + dPhiRC * dPhiRC); - if (distRC < distanceMax) { - - // dcaXY - if (track.hasTOF()) { - if (nSigmaSqPr < nSigmaMaxForDcaxy) { - registry.fill(HIST("dcaPrRandCone"), trkPt, track.dcaXY(), leadJet.ptCorr, centrality); - } - if (nSigmaSqPi < nSigmaMaxForDcaxy) { - registry.fill(HIST("dcaPiRandCone"), trkPt, track.dcaXY(), leadJet.ptCorr, centrality); - } + if (passesNominal) { + if (fillDetailedQA) { + registry.fill(HIST("tpcDedxPerpJet"), trkP, tpcSig); } - if (hasTofPi) { - registry.fill(HIST("tpcTofPiRandCone"), trkP, tpcPi, leadJet.ptCorr, centrality); + registry.fill(HIST("tpcTofPiPerpJet"), trkP, tpcPi, distBg, jet.ptCorr, centrality); if (isTpcPiRange) { - registry.fill(HIST("pVsPtForPiRandCone"), trkP, trkPt, leadJet.ptCorr, centrality); + registry.fill(HIST("pVsPtForPiPerpJet"), trkP, trkPt, distBg, jet.ptCorr, centrality); } } if (hasTofPr) { - registry.fill(HIST("tpcTofPrRandCone"), trkP, tpcPr, leadJet.ptCorr, centrality); + registry.fill(HIST("tpcTofPrPerpJet"), trkP, tpcPr, distBg, jet.ptCorr, centrality); if (isTpcPrRange) { - registry.fill(HIST("pVsPtForPrRandCone"), trkP, trkPt, leadJet.ptCorr, centrality); + registry.fill(HIST("pVsPtForPrPerpJet"), trkP, trkPt, distBg, jet.ptCorr, centrality); } } } - } + }; - // --- Signal Fill --- - registry.fill(HIST("jetDistanceVsTrackpt"), distance, trkPt); - registry.fill(HIST("jetTpcDedx"), trkP, tpcSig, distance); - registry.fill(HIST("jetTofBeta"), trkP, beta); + fillPerpendicular(jet.phiBg1); + fillPerpendicular(jet.phiBg2); - // dcaXY - if (track.hasTOF()) { - if (nSigmaSqPr < nSigmaMaxForDcaxy) { + if (distanceSq >= maxR2) { + continue; + } + const float distance = std::sqrt(distanceSq); + + if (passesWideDca) { + if (hasTofPr && isTpcPrRange) { registry.fill(HIST("jetDcaPr"), trkPt, track.dcaXY(), distance, jet.ptCorr, centrality); } - - if (nSigmaSqPi < nSigmaMaxForDcaxy) { + if (hasTofPi && isTpcPiRange) { registry.fill(HIST("jetDcaPi"), trkPt, track.dcaXY(), distance, jet.ptCorr, centrality); } } - if (hasTofPr) { - registry.fill(HIST("jetTpcTofPr"), trkP, tpcPr, distance, jet.ptCorr, centrality); - if (isTpcPrRange) { - registry.fill(HIST("jetpVsPtForPr"), trkP, trkPt, distance, jet.ptCorr, centrality); + if (passesNominal) { + if (fillDetailedQA) { + registry.fill(HIST("jetDistanceVsTrackpt"), distance, trkPt); + registry.fill(HIST("jetTpcDedx"), trkP, tpcSig, distance); + registry.fill(HIST("jetTofBeta"), trkP, beta); + } + + if (hasTofPr) { + registry.fill(HIST("jetTpcTofPr"), trkP, tpcPr, distance, jet.ptCorr, centrality); + if (isTpcPrRange) { + registry.fill(HIST("jetpVsPtForPr"), trkP, trkPt, distance, jet.ptCorr, centrality); + } + } + if (hasTofPi) { + registry.fill(HIST("jetTpcTofPi"), trkP, tpcPi, distance, jet.ptCorr, centrality); + if (isTpcPiRange) { + registry.fill(HIST("jetpVsPtForPi"), trkP, trkPt, distance, jet.ptCorr, centrality); + } + } + } + } + + // Random cones are outside the jet loop, so every track-cone pair is + // filled exactly once. Each cone is associated with one selected jet. + for (const auto& cone : randomCones) { + const float dEtaRC = trkEta - cone.eta; + if (std::abs(dEtaRC) >= distanceMax) { + continue; + } + const float dPhiRC = absDeltaPhi(trkPhi, cone.phi); + const float distRCSq = dEtaRC * dEtaRC + dPhiRC * dPhiRC; + if (distRCSq >= maxR2) { + continue; + } + const float distRC = std::sqrt(distRCSq); + + if (passesWideDca) { + if (hasTofPr && isTpcPrRange) { + registry.fill(HIST("dcaPrRandCone"), trkPt, track.dcaXY(), distRC, cone.referenceJetPtCorr, centrality); + } + if (hasTofPi && isTpcPiRange) { + registry.fill(HIST("dcaPiRandCone"), trkPt, track.dcaXY(), distRC, cone.referenceJetPtCorr, centrality); } } - if (hasTofPi) { - registry.fill(HIST("jetTpcTofPi"), trkP, tpcPi, distance, jet.ptCorr, centrality); - if (isTpcPiRange) { - registry.fill(HIST("jetpVsPtForPi"), trkP, trkPt, distance, jet.ptCorr, centrality); + if (passesNominal) { + if (hasTofPi) { + registry.fill(HIST("tpcTofPiRandCone"), trkP, tpcPi, distRC, cone.referenceJetPtCorr, centrality); + if (isTpcPiRange) { + registry.fill(HIST("pVsPtForPiRandCone"), trkP, trkPt, distRC, cone.referenceJetPtCorr, centrality); + } + } + if (hasTofPr) { + registry.fill(HIST("tpcTofPrRandCone"), trkP, tpcPr, distRC, cone.referenceJetPtCorr, centrality); + if (isTpcPrRange) { + registry.fill(HIST("pVsPtForPrRandCone"), trkP, trkPt, distRC, cone.referenceJetPtCorr, centrality); + } } } } } } - PROCESS_SWITCH(JetShapeTask, processJetProductionRatio, "production ratio around jets", false); - void processInclusiveProductionRatio(soa::Filtered::iterator const& collision, soa::Join const& tracks, FullTrackInfo const&) + void processJetProductionRatioWithRho( + soa::Filtered>::iterator const& collision, + JetTracksWithPI const& tracks, + FullTrackInfo const&, + soa::Join const& jets) + { + runJetProductionRatio(collision, tracks, jets, collision.rho()); + } + PROCESS_SWITCH(JetShapeTask, processJetProductionRatioWithRho, "production ratio around jets with rho subtraction", false); + + void processJetProductionRatioNoRho( + soa::Filtered::iterator const& collision, + JetTracksWithPI const& tracks, + FullTrackInfo const&, + soa::Join const& jets) + { + runJetProductionRatio(collision, tracks, jets, 0.0f); + } + PROCESS_SWITCH(JetShapeTask, processJetProductionRatioNoRho, "production ratio around jets without rho table (recommended for pp)", false); + + void processInclusiveProductionRatio(soa::Filtered::iterator const& collision, JetTracksWithPI const& tracks, FullTrackInfo const&) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - registry.fill(HIST("eventCounterInc"), collision.centFT0M()); - // tracks conditions + const float centrality = collision.centFT0M(); + if (!passCentrality(centrality)) { + return; + } + registry.fill(HIST("eventCounterInc"), centrality); + for (const auto& jetTrack : tracks) { + const bool passesNominalBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelection); + const bool passesWideDcaBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelectionForDca); + if (!passesNominalBit && !passesWideDcaBit) { + continue; + } + + const auto track = jetTrack.track_as(); + + // Keep the original QA tied to the nominal global-track selection. + if (passesNominalBit && fillDetailedQA) { + registry.fill(HIST("trackTpcNClsCrossedRows"), track.tpcNClsCrossedRows()); + registry.fill(HIST("trackDcaXY"), track.dcaXY()); + registry.fill(HIST("trackItsChi2NCl"), track.itsChi2NCl()); + registry.fill(HIST("trackTpcChi2NCl"), track.tpcChi2NCl()); + registry.fill(HIST("trackTpcNClsFound"), track.tpcNClsFound()); + registry.fill(HIST("trackItsNCls"), track.itsNCls()); + registry.fill(HIST("trackEta"), track.eta()); + registry.fill(HIST("trackPhi"), track.phi()); + } - if (!jetderiveddatautilities::selectTrack(jetTrack, trackSelection)) { + if (!passCommonTrackQuality(track)) { continue; } - auto track = jetTrack.track_as(); + const bool passesNominal = passesNominalBit && passNominalTrackDca(track); + const bool passesWideDca = passesWideDcaBit; - registry.fill(HIST("trackTpcNClsCrossedRows"), track.tpcNClsCrossedRows()); - registry.fill(HIST("trackDcaXY"), track.dcaXY()); - registry.fill(HIST("trackItsChi2NCl"), track.itsChi2NCl()); - registry.fill(HIST("trackTpcChi2NCl"), track.tpcChi2NCl()); - registry.fill(HIST("trackTpcNClsFound"), track.tpcNClsFound()); - registry.fill(HIST("trackItsNCls"), track.itsNCls()); - registry.fill(HIST("trackEta"), track.eta()); - registry.fill(HIST("trackPhi"), track.phi()); + const bool passPiKinematics = passSpeciesKinematics(track.pt(), track.eta(), PionMass); + const bool passPrKinematics = passSpeciesKinematics(track.pt(), track.eta(), ProtonMass); + const bool hasTofPi = passPiKinematics && track.hasTOF() && std::abs(track.tofNSigmaPi()) < nSigmaTofCut; + const bool hasTofPr = passPrKinematics && track.hasTOF() && std::abs(track.tofNSigmaPr()) < nSigmaTofCut; + const bool isTpcPiRange = track.tpcNSigmaPi() > tpcNSigmaPiMin && track.tpcNSigmaPi() < tpcNSigmaPiMax; + const bool isTpcPrRange = track.tpcNSigmaPr() > tpcNSigmaPrMin && track.tpcNSigmaPr() < tpcNSigmaPrMax; - if (std::abs(track.eta()) > etaTrUp) - continue; - if (track.tpcNClsCrossedRows() < nclcrossTpcMin) - continue; - if (std::abs(track.dcaXY()) > dcaxyCutMax) - continue; - if (track.itsChi2NCl() > chi2ItsMax) - continue; - if (track.tpcChi2NCl() > chi2TpcMax) - continue; - if (track.tpcNClsFound() < nclTpcMin) - continue; - if (track.itsNCls() < nclItsMin) - continue; + if (passesWideDca) { + if (hasTofPi && isTpcPiRange) { + registry.fill(HIST("dcaPiInc"), track.pt(), track.dcaXY(), centrality); + } + if (hasTofPr && isTpcPrRange) { + registry.fill(HIST("dcaPrInc"), track.pt(), track.dcaXY(), centrality); + } + } - // PID check - registry.fill(HIST("tpcPi"), track.p(), track.tpcNSigmaPi()); - registry.fill(HIST("tofPi"), track.pt(), track.tofNSigmaPi()); - registry.fill(HIST("tpcPr"), track.p(), track.tpcNSigmaPr()); - registry.fill(HIST("tofPr"), track.pt(), track.tofNSigmaPr()); - registry.fill(HIST("tpcDedx"), track.p(), track.tpcSignal(), collision.centFT0M()); - registry.fill(HIST("tofBeta"), track.p(), track.beta(), collision.centFT0M()); + if (!passesNominal) { + continue; + } - if (std::abs(track.tofNSigmaPr()) < nSigmaTofCut) { - registry.fill(HIST("tpcTofPr"), track.p(), track.tpcNSigmaPr(), collision.centFT0M()); + if (fillDetailedQA) { + registry.fill(HIST("tpcPi"), track.p(), track.tpcNSigmaPi()); + registry.fill(HIST("tofPi"), track.pt(), track.tofNSigmaPi()); + registry.fill(HIST("tpcPr"), track.p(), track.tpcNSigmaPr()); + registry.fill(HIST("tofPr"), track.pt(), track.tofNSigmaPr()); + registry.fill(HIST("tpcDedx"), track.p(), track.tpcSignal(), centrality); + registry.fill(HIST("tofBeta"), track.p(), track.beta(), centrality); + } - if (track.tpcNSigmaPr() > tpcNSigmaPrMin && track.tpcNSigmaPr() < tpcNSigmaPrMax) { - registry.fill(HIST("pVsPtForPr"), track.p(), track.pt(), collision.centFT0M()); + if (hasTofPr) { + registry.fill(HIST("tpcTofPr"), track.p(), track.tpcNSigmaPr(), centrality); + if (isTpcPrRange) { + registry.fill(HIST("pVsPtForPr"), track.p(), track.pt(), centrality); } } - if (std::abs(track.tofNSigmaPi()) < nSigmaTofCut) { - registry.fill(HIST("tpcTofPi"), track.p(), track.tpcNSigmaPi(), collision.centFT0M()); - if (track.tpcNSigmaPi() > tpcNSigmaPiMin && track.tpcNSigmaPi() < tpcNSigmaPiMax) { - registry.fill(HIST("pVsPtForPi"), track.p(), track.pt(), collision.centFT0M()); + if (hasTofPi) { + registry.fill(HIST("tpcTofPi"), track.p(), track.tpcNSigmaPi(), centrality); + if (isTpcPiRange) { + registry.fill(HIST("pVsPtForPi"), track.p(), track.pt(), centrality); } } } } PROCESS_SWITCH(JetShapeTask, processInclusiveProductionRatio, "inclusive Production ratio", false); - void processReco(soa::Filtered>::iterator const& collision, soa::Join const& tracks, aod::ChargedMCDetectorLevelJets const& jets, aod::McParticles const& mcParticles) + void processEfficiencyAndPurity( + soa::Filtered>::iterator const& collision, + JetTracksWithPIMC const& jetTracks, + FullTrackInfoMC const&, + soa::Filtered> const& mcdJets, + aod::ChargedMCParticleLevelJets const& mcpJets, + aod::JetParticles const& mcParticles) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } + if (!collision.has_mcCollision()) { + return; + } - (void)mcParticles; - - float centrality = collision.centFT0M(); - float rho = collision.rho(); + const auto reducedMcCollisionId = collision.mcCollisionId(); + (void)mcpJets; - registry.fill(HIST("eventCounterMc"), 0.5); - registry.fill(HIST("mcCentralityReco"), centrality); + const float centrality = collision.centFT0M(); + if (!passCentrality(centrality)) { + return; + } + const float rho = collision.rho(); + const float maxR2 = distanceMax * distanceMax; - struct CachedJet { - float pt; - float eta; - float phi; - float ptCorr; + registry.fill(HIST("eventCounterMc"), centrality); + + struct MatchedJet { + float detPtCorr; + float detEta; + float detPhi; + float detPhiBg1; + float detPhiBg2; + float partEta; + float partPhi; + float partPhiBg1; + float partPhiBg2; }; - std::vector cachedJets; - cachedJets.reserve(jets.size()); - for (const auto& jet : jets) { - float mcdPtCorr = jet.pt() - rho * jet.area(); - cachedJets.push_back({jet.pt(), jet.eta(), jet.phi(), mcdPtCorr}); - registry.fill(HIST("jetPt"), jet.pt()); - } + std::vector validJets; + std::vector vetoJets; + validJets.reserve(mcdJets.size()); + vetoJets.reserve(mcdJets.size()); - // reco track loop - for (const auto& track : tracks) { - - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + for (const auto& detJet : mcdJets) { + if (!isAcceptedJet(detJet)) { continue; } - if (!track.has_mcParticle()) + const auto matchedJets = detJet.matchedJetGeo(); + if (matchedJets.empty()) { continue; + } - if (std::abs(track.eta()) > etaTrUp) - continue; - if (track.tpcNClsCrossedRows() < nclcrossTpcMin) + const auto& partJet = matchedJets[0]; + if (partJet.mcCollisionId() != reducedMcCollisionId) { continue; - if (std::abs(track.dcaXY()) > dcaxyCutMax) - continue; - if (track.itsChi2NCl() > chi2ItsMax) + } + + const float detPtCorr = detJet.pt() - rho * detJet.area(); + validJets.push_back({detPtCorr, + detJet.eta(), + detJet.phi(), + RecoDecay::constrainAngle(detJet.phi() + o2::constants::math::PIHalf), + RecoDecay::constrainAngle(detJet.phi() - o2::constants::math::PIHalf), + partJet.eta(), + partJet.phi(), + RecoDecay::constrainAngle(partJet.phi() + o2::constants::math::PIHalf), + RecoDecay::constrainAngle(partJet.phi() - o2::constants::math::PIHalf)}); + + vetoJets.push_back({detJet.eta(), detJet.phi(), detPtCorr}); + registry.fill(HIST("jetPtMc"), detJet.pt(), centrality); + } + + const auto randomCones = makeRandomCones(vetoJets); + + // Generated denominator for tracking efficiency + for (const auto& mcParticle : mcParticles) { + if (mcParticle.mcCollisionId() != reducedMcCollisionId) { continue; - if (track.tpcChi2NCl() > chi2TpcMax) + } + if (!mcParticle.isPhysicalPrimary()) { continue; - if (track.tpcNClsFound() < nclTpcMin) + } + if (!passTruthKinematics(mcParticle)) { continue; - if (track.itsNCls() < nclItsMin) + } + + const int absPdg = std::abs(mcParticle.pdgCode()); + const bool isPi = absPdg == PDG_t::kPiPlus; + const bool isKa = absPdg == PDG_t::kKPlus; + const bool isPr = absPdg == PDG_t::kProton; + if (!isPi && !isKa && !isPr) { continue; + } - auto mcParticle = track.mcParticle(); - registry.fill(HIST("ptResolution"), track.pt(), track.pt() - mcParticle.pt()); + const float mcPt = mcParticle.pt(); - if (std::fabs(mcParticle.y()) >= mcRapidityMax) - continue; + if (isPi) { + registry.fill(HIST("ptGenPiInc"), mcPt, centrality); + } else if (isKa) { + registry.fill(HIST("ptGenKaInc"), mcPt, centrality); + } else { + registry.fill(HIST("ptGenPrInc"), mcPt, centrality); + } - const int producedByDecay = 4; + for (const auto& jet : validJets) { + const float dEtaPart = mcParticle.eta() - jet.partEta; + + const float dPhiJetPart = absDeltaPhi(mcParticle.phi(), jet.partPhi); + const float distJetSq = dEtaPart * dEtaPart + dPhiJetPart * dPhiJetPart; + if (distJetSq < maxR2) { + const float distJet = std::sqrt(distJetSq); + if (isPi) { + registry.fill(HIST("ptGenPi"), mcPt, distJet, jet.detPtCorr, centrality); + } else if (isKa) { + registry.fill(HIST("ptGenKa"), mcPt, distJet, jet.detPtCorr, centrality); + } else { + registry.fill(HIST("ptGenPr"), mcPt, distJet, jet.detPtCorr, centrality); + } + } - bool isPrimary = mcParticle.isPhysicalPrimary(); - bool isSecondDecay = !isPrimary && (mcParticle.getProcess() == producedByDecay); - bool isSecondMaterial = !isPrimary && !isSecondDecay; + const float dPhiBg1Part = absDeltaPhi(mcParticle.phi(), jet.partPhiBg1); + const float dPhiBg2Part = absDeltaPhi(mcParticle.phi(), jet.partPhiBg2); + const float distBg1Sq = dEtaPart * dEtaPart + dPhiBg1Part * dPhiBg1Part; + const float distBg2Sq = dEtaPart * dEtaPart + dPhiBg2Part * dPhiBg2Part; + + float distBg = -1.0f; + if (distBg1Sq < maxR2) { + distBg = std::sqrt(distBg1Sq); + } else if (distBg2Sq < maxR2) { + distBg = std::sqrt(distBg2Sq); + } + + if (distBg >= 0.0f) { + if (isPi) { + registry.fill(HIST("ptGenPiPerp"), mcPt, distBg, jet.detPtCorr, centrality); + } else if (isKa) { + registry.fill(HIST("ptGenKaPerp"), mcPt, distBg, jet.detPtCorr, centrality); + } else { + registry.fill(HIST("ptGenPrPerp"), mcPt, distBg, jet.detPtCorr, centrality); + } + } + } - int pdg = std::abs(mcParticle.pdgCode()); - bool isPion = (pdg == PDG_t::kPiPlus); - bool isKaon = (pdg == PDG_t::kKPlus); - bool isProton = (pdg == PDG_t::kProton); + for (const auto& cone : randomCones) { + const float dEta = mcParticle.eta() - cone.eta; + if (std::abs(dEta) >= distanceMax) { + continue; + } + const float dPhi = absDeltaPhi(mcParticle.phi(), cone.phi); + const float distSq = dEta * dEta + dPhi * dPhi; + if (distSq >= maxR2) { + continue; + } + const float distance = std::sqrt(distSq); + if (isPi) { + registry.fill(HIST("ptGenPiRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } else if (isKa) { + registry.fill(HIST("ptGenKaRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } else { + registry.fill(HIST("ptGenPrRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } + } + } - if (!isPion && !isKaon && !isProton) + // Reconstructed tracks + for (const auto& jetTrack : jetTracks) { + const bool passesNominalBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelection); + const bool passesWideDcaBit = jetderiveddatautilities::selectTrack(jetTrack, trackSelectionForDca); + if (!passesNominalBit && !passesWideDcaBit) { continue; + } - bool hasTof = track.hasTOF(); + const auto track = jetTrack.track_as(); + if (!passCommonTrackQuality(track)) { + continue; + } - for (const auto& jet : cachedJets) { + const bool passesNominal = passesNominalBit && passNominalTrackDca(track); + const bool passesWideDca = passesWideDcaBit; + if (!passesNominal && !passesWideDca) { + continue; + } - float dEta = std::abs(track.eta() - jet.eta); - if (dEta > distanceMax) - continue; + if (!jetTrack.has_mcParticle()) { + continue; + } + const auto mcParticle = jetTrack.mcParticle_as(); + if (mcParticle.mcCollisionId() != reducedMcCollisionId) { + continue; + } - float dPhi = std::abs(track.phi() - jet.phi); - if (dPhi > o2::constants::math::PI) { - dPhi = o2::constants::math::TwoPI - dPhi; - } + const int absPdg = std::abs(mcParticle.pdgCode()); + const bool isTruePi = absPdg == PDG_t::kPiPlus; + const bool isTrueKa = absPdg == PDG_t::kKPlus; + const bool isTruePr = absPdg == PDG_t::kProton; + if (!isTruePi && !isTrueKa && !isTruePr) { + continue; + } - if (dPhi > distanceMax) - continue; + constexpr int ProducedByDecay = 4; // ROOT TMCProcess::kPDecay + const bool isPrimary = mcParticle.isPhysicalPrimary(); + const bool isDecay = !isPrimary && mcParticle.getProcess() == ProducedByDecay; + + const float recoPt = track.pt(); + const float mcPt = mcParticle.pt(); + const float dcaXy = track.dcaXY(); + const bool hasTof = track.hasTOF(); + + const bool passTpcPi = track.tpcNSigmaPi() > tpcNSigmaPiMin && + track.tpcNSigmaPi() < tpcNSigmaPiMax; + const bool passTpcPr = track.tpcNSigmaPr() > tpcNSigmaPrMin && + track.tpcNSigmaPr() < tpcNSigmaPrMax; + const bool isRecoPi = passSpeciesKinematics(recoPt, track.eta(), PionMass) && + passTpcPi && hasTof && + std::abs(track.tofNSigmaPi()) < nSigmaTofCut; + const bool isRecoPr = passSpeciesKinematics(recoPt, track.eta(), ProtonMass) && + passTpcPr && hasTof && + std::abs(track.tofNSigmaPr()) < nSigmaTofCut; + + float originBin = 2.5f; + if (isPrimary) { + originBin = 0.5f; + } else if (isDecay) { + originBin = 1.5f; + } - float deltaR = std::sqrt(dEta * dEta + dPhi * dPhi); - if (deltaR > distanceMax) - continue; + // DCA templates and wide->global selection transfer + auto fillInclusiveDcaAndTransfer = [&]() { + if (isRecoPi && isTruePi) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPiInc"), recoPt, dcaXy, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPiInc"), recoPt, dcaXy, centrality); + } else { + registry.fill(HIST("dcaMatPiInc"), recoPt, dcaXy, centrality); + } + registry.fill(HIST("originCountWideInc"), recoPt, centrality, 0.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalInc"), recoPt, centrality, 0.5f, originBin); + } + } - if (isPrimary) { - // Tracking - if (isPion) - registry.fill(HIST("ptHistogramPion"), mcParticle.pt(), track.dcaXY(), jet.ptCorr, centrality); - else if (isKaon) - registry.fill(HIST("ptHistogramKaon"), mcParticle.pt(), track.dcaXY(), jet.ptCorr, centrality); - else if (isProton) - registry.fill(HIST("ptHistogramProton"), mcParticle.pt(), track.dcaXY(), jet.ptCorr, centrality); + if (isRecoPr && isTruePr) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPrInc"), recoPt, dcaXy, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPrInc"), recoPt, dcaXy, centrality); + } else { + registry.fill(HIST("dcaMatPrInc"), recoPt, dcaXy, centrality); + } + registry.fill(HIST("originCountWideInc"), recoPt, centrality, 1.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalInc"), recoPt, centrality, 1.5f, originBin); + } + } + }; + fillInclusiveDcaAndTransfer(); + + for (const auto& jet : validJets) { + const float dEtaDet = track.eta() - jet.detEta; + + const float dPhiJetDet = absDeltaPhi(track.phi(), jet.detPhi); + const float distJetDetSq = dEtaDet * dEtaDet + dPhiJetDet * dPhiJetDet; + const bool inJetDet = distJetDetSq < maxR2; + const float distJetDet = inJetDet ? std::sqrt(distJetDetSq) : -1.0f; + + const float dPhiBg1Det = absDeltaPhi(track.phi(), jet.detPhiBg1); + const float dPhiBg2Det = absDeltaPhi(track.phi(), jet.detPhiBg2); + const float distBg1DetSq = dEtaDet * dEtaDet + dPhiBg1Det * dPhiBg1Det; + const float distBg2DetSq = dEtaDet * dEtaDet + dPhiBg2Det * dPhiBg2Det; + + float distBgDet = -1.0f; + if (distBg1DetSq < maxR2) { + distBgDet = std::sqrt(distBg1DetSq); + } else if (distBg2DetSq < maxR2) { + distBgDet = std::sqrt(distBg2DetSq); + } - // TOF matched - if (hasTof) { - if (isPion) - registry.fill(HIST("ptHistogramPionTof"), mcParticle.pt(), jet.ptCorr, centrality); - else if (isKaon) - registry.fill(HIST("ptHistogramKaonTof"), mcParticle.pt(), jet.ptCorr, centrality); - else if (isProton) - registry.fill(HIST("ptHistogramProtonTof"), mcParticle.pt(), jet.ptCorr, centrality); - } - } else { // Secondary - if (isSecondDecay) { - // from Decay - if (isPion) - registry.fill(HIST("dcaDecayPion"), mcParticle.pt(), track.dcaXY(), jet.ptCorr, centrality); - else if (isProton) - registry.fill(HIST("dcaDecayProton"), mcParticle.pt(), track.dcaXY(), jet.ptCorr, centrality); - } else if (isSecondMaterial) { - // from Material - if (isPion) - registry.fill(HIST("dcaMaterialPion"), mcParticle.pt(), track.dcaXY(), jet.ptCorr, centrality); - else if (isProton) - registry.fill(HIST("dcaMaterialProton"), mcParticle.pt(), track.dcaXY(), jet.ptCorr, centrality); + if (inJetDet) { + if (isRecoPi && isTruePi) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPi"), recoPt, dcaXy, distJetDet, jet.detPtCorr, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPi"), recoPt, dcaXy, distJetDet, jet.detPtCorr, centrality); + } else { + registry.fill(HIST("dcaMatPi"), recoPt, dcaXy, distJetDet, jet.detPtCorr, centrality); + } + registry.fill(HIST("originCountWideJet"), recoPt, distJetDet, jet.detPtCorr, centrality, 0.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalJet"), recoPt, distJetDet, jet.detPtCorr, centrality, 0.5f, originBin); + } + } + + if (isRecoPr && isTruePr) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPr"), recoPt, dcaXy, distJetDet, jet.detPtCorr, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPr"), recoPt, dcaXy, distJetDet, jet.detPtCorr, centrality); + } else { + registry.fill(HIST("dcaMatPr"), recoPt, dcaXy, distJetDet, jet.detPtCorr, centrality); + } + registry.fill(HIST("originCountWideJet"), recoPt, distJetDet, jet.detPtCorr, centrality, 1.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalJet"), recoPt, distJetDet, jet.detPtCorr, centrality, 1.5f, originBin); + } } } - } - } - } - PROCESS_SWITCH(JetShapeTask, processReco, "process reconstructed simulation information", true); - void processSim(aod::JetMcCollisions::iterator const& mcCollision, soa::SmallGroups const& collisions, aod::ChargedMCParticleLevelJets const& mcpjets, aod::JetParticles const& mcParticles) - { - if (std::abs(mcCollision.posZ()) > vertexZCut) { - return; - } + if (distBgDet >= 0.0f) { + if (isRecoPi && isTruePi) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPiPerp"), recoPt, dcaXy, distBgDet, jet.detPtCorr, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPiPerp"), recoPt, dcaXy, distBgDet, jet.detPtCorr, centrality); + } else { + registry.fill(HIST("dcaMatPiPerp"), recoPt, dcaXy, distBgDet, jet.detPtCorr, centrality); + } + registry.fill(HIST("originCountWidePerp"), recoPt, distBgDet, jet.detPtCorr, centrality, 0.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalPerp"), recoPt, distBgDet, jet.detPtCorr, centrality, 0.5f, originBin); + } + } - if (collisions.size() == 0) { - return; - } + if (isRecoPr && isTruePr) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPrPerp"), recoPt, dcaXy, distBgDet, jet.detPtCorr, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPrPerp"), recoPt, dcaXy, distBgDet, jet.detPtCorr, centrality); + } else { + registry.fill(HIST("dcaMatPrPerp"), recoPt, dcaXy, distBgDet, jet.detPtCorr, centrality); + } + registry.fill(HIST("originCountWidePerp"), recoPt, distBgDet, jet.detPtCorr, centrality, 1.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalPerp"), recoPt, distBgDet, jet.detPtCorr, centrality, 1.5f, originBin); + } + } + } + } - // --- centrality --- - float centrality = collisions.begin().centFT0M(); - registry.fill(HIST("mcCentralitySim"), centrality); - const float maxR2 = distanceMax * distanceMax; + for (const auto& cone : randomCones) { + const float dEta = track.eta() - cone.eta; + if (std::abs(dEta) >= distanceMax) { + continue; + } + const float dPhi = absDeltaPhi(track.phi(), cone.phi); + const float distSq = dEta * dEta + dPhi * dPhi; + if (distSq >= maxR2) { + continue; + } + const float distance = std::sqrt(distSq); + + if (isRecoPi && isTruePi) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPiRand"), recoPt, dcaXy, distance, cone.referenceJetPtCorr, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPiRand"), recoPt, dcaXy, distance, cone.referenceJetPtCorr, centrality); + } else { + registry.fill(HIST("dcaMatPiRand"), recoPt, dcaXy, distance, cone.referenceJetPtCorr, centrality); + } + registry.fill(HIST("originCountWideRand"), recoPt, distance, cone.referenceJetPtCorr, centrality, 0.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalRand"), recoPt, distance, cone.referenceJetPtCorr, centrality, 0.5f, originBin); + } + } - // --- loop over MC particles only once --- - for (const auto& mcParticle : mcParticles) { + if (isRecoPr && isTruePr) { + if (passesWideDca) { + if (isPrimary) { + registry.fill(HIST("dcaPrimPrRand"), recoPt, dcaXy, distance, cone.referenceJetPtCorr, centrality); + } else if (isDecay) { + registry.fill(HIST("dcaDecayPrRand"), recoPt, dcaXy, distance, cone.referenceJetPtCorr, centrality); + } else { + registry.fill(HIST("dcaMatPrRand"), recoPt, dcaXy, distance, cone.referenceJetPtCorr, centrality); + } + registry.fill(HIST("originCountWideRand"), recoPt, distance, cone.referenceJetPtCorr, centrality, 1.5f, originBin); + } + if (passesNominal) { + registry.fill(HIST("originCountGlobalRand"), recoPt, distance, cone.referenceJetPtCorr, centrality, 1.5f, originBin); + } + } + } - // --- early cuts on particle --- - if (!mcParticle.isPhysicalPrimary()) { + // Tracking and TOF-matching efficiency numerators + if (!passesNominal || !isPrimary) { continue; } - - if (std::abs(mcParticle.y()) > mcRapidityMax) { + if (!passTruthKinematics(mcParticle)) { continue; } - int absPdg = std::abs(mcParticle.pdgCode()); - if (absPdg != PDG_t::kPiPlus && - absPdg != PDG_t::kKPlus && - absPdg != PDG_t::kProton) { - continue; + registry.fill(HIST("ptResolution"), track.pt(), track.pt() - mcPt); + + if (isTruePi) { + registry.fill(HIST("effNumTpcPiInc"), mcPt, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPiInc"), mcPt, centrality); + } + if (isRecoPi) { + registry.fill(HIST("effNumPidPiInc"), mcPt, centrality); + } + } else if (isTrueKa) { + registry.fill(HIST("effNumTpcKaInc"), mcPt, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofKaInc"), mcPt, centrality); + } + } else { + registry.fill(HIST("effNumTpcPrInc"), mcPt, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPrInc"), mcPt, centrality); + } + if (isRecoPr) { + registry.fill(HIST("effNumPidPrInc"), mcPt, centrality); + } } - const float partPt = mcParticle.pt(); - const float partEta = mcParticle.eta(); - const float partPhi = mcParticle.phi(); + for (const auto& jet : validJets) { + const float dEtaPart = mcParticle.eta() - jet.partEta; + + const float dPhiJetPart = absDeltaPhi(mcParticle.phi(), jet.partPhi); + const float distJetPartSq = dEtaPart * dEtaPart + dPhiJetPart * dPhiJetPart; + if (distJetPartSq < maxR2) { + const float distJetPart = std::sqrt(distJetPartSq); + if (isTruePi) { + registry.fill(HIST("effNumTpcPiJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPiJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + } + if (isRecoPi) { + registry.fill(HIST("effNumPidPiJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + } + } else if (isTrueKa) { + registry.fill(HIST("effNumTpcKaJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofKaJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + } + } else { + registry.fill(HIST("effNumTpcPrJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPrJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + } + if (isRecoPr) { + registry.fill(HIST("effNumPidPrJet"), mcPt, distJetPart, jet.detPtCorr, centrality); + } + } + } - // --- loop over jets --- - for (const auto& mcpjet : mcpjets) { + const float dPhiBg1Part = absDeltaPhi(mcParticle.phi(), jet.partPhiBg1); + const float dPhiBg2Part = absDeltaPhi(mcParticle.phi(), jet.partPhiBg2); + const float distBg1PartSq = dEtaPart * dEtaPart + dPhiBg1Part * dPhiBg1Part; + const float distBg2PartSq = dEtaPart * dEtaPart + dPhiBg2Part * dPhiBg2Part; - // --- delta eta cut first --- - float dEta = partEta - mcpjet.eta(); - if (std::abs(dEta) > distanceMax) { - continue; + float distBgPart = -1.0f; + if (distBg1PartSq < maxR2) { + distBgPart = std::sqrt(distBg1PartSq); + } else if (distBg2PartSq < maxR2) { + distBgPart = std::sqrt(distBg2PartSq); } - // --- delta phi --- - float dPhi = std::abs(partPhi - mcpjet.phi()); - if (dPhi > o2::constants::math::PI) { - dPhi = o2::constants::math::TwoPI - dPhi; + if (distBgPart >= 0.0f) { + if (isTruePi) { + registry.fill(HIST("effNumTpcPiPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPiPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + } + if (isRecoPi) { + registry.fill(HIST("effNumPidPiPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + } + } else if (isTrueKa) { + registry.fill(HIST("effNumTpcKaPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofKaPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + } + } else { + registry.fill(HIST("effNumTpcPrPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPrPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + } + if (isRecoPr) { + registry.fill(HIST("effNumPidPrPerp"), mcPt, distBgPart, jet.detPtCorr, centrality); + } + } } + } - // --- delta R^2 --- - float dR2 = dEta * dEta + dPhi * dPhi; - if (dR2 > maxR2) { + for (const auto& cone : randomCones) { + const float dEta = mcParticle.eta() - cone.eta; + if (std::abs(dEta) >= distanceMax) { continue; } + const float dPhi = absDeltaPhi(mcParticle.phi(), cone.phi); + const float distSq = dEta * dEta + dPhi * dPhi; + if (distSq >= maxR2) { + continue; + } + const float distance = std::sqrt(distSq); - const float jetPt = mcpjet.pt(); - - // --- histogram fill --- - if (absPdg == PDG_t::kPiPlus) { - registry.fill(HIST("ptGeneratedPion"), partPt, jetPt, centrality); - } else if (absPdg == PDG_t::kKPlus) { - registry.fill(HIST("ptGeneratedKaon"), partPt, jetPt, centrality); - } else if (absPdg == PDG_t::kProton) { - registry.fill(HIST("ptGeneratedProton"), partPt, jetPt, centrality); + if (isTruePi) { + registry.fill(HIST("effNumTpcPiRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPiRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } + if (isRecoPi) { + registry.fill(HIST("effNumPidPiRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } + } else if (isTrueKa) { + registry.fill(HIST("effNumTpcKaRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofKaRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } + } else { + registry.fill(HIST("effNumTpcPrRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + if (hasTof) { + registry.fill(HIST("effNumTofPrRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } + if (isRecoPr) { + registry.fill(HIST("effNumPidPrRand"), mcPt, distance, cone.referenceJetPtCorr, centrality); + } } } } } - PROCESS_SWITCH(JetShapeTask, processSim, "process pure simulation information", true); + PROCESS_SWITCH(JetShapeTask, processEfficiencyAndPurity, "process MC information for Efficiency and Purity", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGJE/Tasks/jetSpectraCharged.cxx b/PWGJE/Tasks/jetSpectraCharged.cxx index bd055c36f2a..c7b6900977c 100644 --- a/PWGJE/Tasks/jetSpectraCharged.cxx +++ b/PWGJE/Tasks/jetSpectraCharged.cxx @@ -15,6 +15,7 @@ #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGJE/Core/JetUtilities.h" #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetSubtraction.h" @@ -81,6 +82,10 @@ struct JetSpectraCharged { Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events; jet-level rejection can also be applied at the jet finder level for jets only, here rejection is applied for collision and track process functions for the first time, and on jets in case it was set to false at the jet finder level"}; Configurable checkLeadConstituentPtForMcpJets{"checkLeadConstituentPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; Configurable isMCGenOnly{"isMCGenOnly", false, "analysis is run over mcGen only"}; + Configurable kappa{"kappa", 1.0, "angularity kappa"}; + Configurable alpha{"alpha", 1.0, "angularity alpha"}; + Configurable useFT0CVariant{"useFT0CVariant", false, "IF checkCentFT0M is false: false -> use standard FT0C centrality selection; true -> use FT0CVariant1"}; + Configurable rhoShift{"rhoShift", 0, "value of artificial rho shift: rho -> rho - rhoShift ; 0 by default"}; std::vector eventSelectionBits; int trackSelection = -1; @@ -89,6 +94,10 @@ struct JetSpectraCharged { float configSwitchLow = -98.0; float configSwitchHigh = 9998.0; + + float ptHardCalcMethodSwitch = 999.0; + static constexpr float BrokenPtHardSentinel = 1.0f; + enum AcceptSplitCollisionsOptions { NonSplitOnly = 0, SplitOkCheckAnyAssocColl, // 1 @@ -97,8 +106,8 @@ struct JetSpectraCharged { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); doSumw2 = skipMBGapEvents; AxisSpec centralityAxis = {1200, -10., 110., "Centrality"}; @@ -166,6 +175,7 @@ struct JetSpectraCharged { registry.add("h2_jet_pt_jet_area", "jet #it{p}_{T,jet} vs. Area_{jet}; #it{p}_{T,jet} (GeV/#it{c}); Area_{jet}", {HistType::kTH2F, {jetPtAxis, {150, 0., 1.5}}}, doSumw2); registry.add("h2_jet_pt_jet_ntracks", "jet #it{p}_{T,jet} vs. N_{jet tracks}; #it{p}_{T,jet} (GeV/#it{c}); N_{jet, tracks}", {HistType::kTH2F, {jetPtAxis, {200, -0.5, 199.5}}}, doSumw2); registry.add("h2_jet_pt_track_pt", "jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, trackPtAxis}}, doSumw2); + registry.add("h2_jet_pt_jet_angularity", "jet #it{p}_{T,jet} vs. angularity;#it{p}_{T,jet} (GeV/#it{c});angularity", {HistType::kTH2F, {jetPtAxis, {30, 0., 1.}}}, doSumw2); registry.add("h3_jet_pt_jet_eta_jet_phi", "jet pt vs. eta vs. phi", {HistType::kTH3F, {jetPtAxis, jetEtaAxis, phiAxis}}, doSumw2); } @@ -180,6 +190,7 @@ struct JetSpectraCharged { registry.add("h2_jet_pt_jet_ntracks_rhoareasubtracted", "jet #it{p}_{T,jet} vs. N_{jet tracks}; #it{p}_{T,jet} (GeV/#it{c}); N_{jet, tracks}", {HistType::kTH2F, {jetPtAxis, {200, -0.5, 199.5}}}, doSumw2); registry.add("h2_jet_pt_jet_corr_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,corr}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,corr} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxisRhoAreaSub}}, doSumw2); registry.add("h2_jet_pt_track_pt_rhoareasubtracted", "jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet} (GeV/#it{c}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, trackPtAxis}}, doSumw2); + registry.add("h2_jet_pt_jet_angularity_rhoareasubtracted", "jet #it{p}_{T,jet} vs. angularity;#it{p}_{T,jet} (GeV/#it{c});angularity", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {30, 0., 1.}}}, doSumw2); registry.add("h3_jet_pt_jet_eta_jet_phi_rhoareasubtracted", "jet_pt_eta_phi_rhoareasubtracted", {HistType::kTH3F, {jetPtAxisRhoAreaSub, jetEtaAxis, phiAxis}}, doSumw2); } @@ -190,6 +201,7 @@ struct JetSpectraCharged { registry.add("h2_jet_pt_part_jet_area_part", "part jet #it{p}_{T,jet} vs. Area_{jet}; #it{p}_{T,jet}^{part} (GeV/#it{c}); Area_{jet}^{part}", {HistType::kTH2F, {jetPtAxis, {150, 0., 1.5}}}, doSumw2); registry.add("h2_jet_pt_part_jet_ntracks_part", "part jet #it{p}_{T,jet} vs. N_{jet tracks}; #it{p}_{T,jet}^{part} (GeV/#it{c}); N_{jet, tracks}^{part}", {HistType::kTH2F, {jetPtAxis, {200, -0.5, 199.5}}}, doSumw2); registry.add("h2_jet_pt_part_track_pt_part", "part jet #it{p}_{T,jet} vs. #it{p}_{T,track}; #it{p}_{T,jet}^{part} (GeV/#it{c}); #it{p}_{T,track}^{part} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, trackPtAxis}}, doSumw2); + registry.add("h2_jet_pt_jet_angularity_part", "part jet #it{p}_{T,jet} vs. angularity;#it{p}_{T,jet} (GeV/#it{c});angularity", {HistType::kTH2F, {jetPtAxis, {30, 0., 1.}}}, doSumw2); registry.add("h3_jet_pt_jet_eta_jet_phi_part", "part jet pt vs. eta vs. phi", {HistType::kTH3F, {jetPtAxis, jetEtaAxis, phiAxis}}, doSumw2); if (doprocessSpectraMCPWeighted) { registry.add("h2_jet_ptcut_part", "p_{T} cut;p_{T,jet}^{part} (GeV/#it{c});N;entries", {HistType::kTH2F, {{300, 0, 300}, {20, 0, 5}}}, doSumw2); @@ -227,45 +239,57 @@ struct JetSpectraCharged { if (checkGeoMatched) { registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_mcdetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_mcpetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); + registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_mcpmcdetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); registry.add("h2_jet_eta_mcd_jet_eta_mcp_matchedgeo", "Eta mcd vs. Eta mcp;#eta_{jet}^{mcd};#eta_{jet}^{mcp}", {HistType::kTH2F, {jetEtaAxis, jetEtaAxis}}, doSumw2); registry.add("h2_jet_phi_mcd_jet_phi_mcp_matchedgeo_mcdetaconstraint", "Phi mcd vs. Phi mcp;#varphi_{jet}^{mcd};#varphi_{jet}^{mcp}", {HistType::kTH2F, {phiAxis, phiAxis}}, doSumw2); registry.add("h2_jet_phi_mcd_jet_phi_mcp_matchedgeo_mcpetaconstraint", "Phi mcd vs. Phi mcp;#varphi_{jet}^{mcd};#varphi_{jet}^{mcp}", {HistType::kTH2F, {phiAxis, phiAxis}}, doSumw2); registry.add("h2_jet_ntracks_mcd_jet_ntracks_mcp_matchedgeo", "Ntracks mcd vs. Ntracks mcp;N_{jet tracks}^{mcd};N_{jet tracks}^{mcp}", {HistType::kTH2F, {{200, -0.5, 199.5}, {200, -0.5, 199.5}}}, doSumw2); - registry.add("h2_jet_pt_mcp_jet_pt_diff_matchedgeo", "jet mcp pT vs. delta pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); (#it{p}_{T,jet}^{mcp} (GeV/#it{c}) - #it{p}_{T,jet}^{mcd} (GeV/#it{c})) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); + registry.add("h2_jet_pt_mcp_jet_pt_diff_matchedgeo", "jet mcp pT vs. delta pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); (#it{p}_{T,jet}^{mcd} (GeV/#it{c}) - #it{p}_{T,jet}^{mcp} (GeV/#it{c})) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_diff_matchedgeo", "jet mcd pT vs. delta pT / jet mcd pt;#it{p}_{T,jet}^{mcd} (GeV/#it{c}); (#it{p}_{T,jet}^{mcd} (GeV/#it{c}) - #it{p}_{T,jet}^{mcp} (GeV/#it{c})) / #it{p}_{T,jet}^{mcd} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); registry.add("h2_jet_pt_mcp_jet_pt_ratio_matchedgeo", "jet mcp pT vs. jet mcd pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); #it{p}_{T,jet}^{mcd} (GeV/#it{c}) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 5.0}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_mcdetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxis, jetPtAxis, {30, 0., 1.}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_mcpetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxis, jetPtAxis, {30, 0., 1.}}}, doSumw2); } if (checkPtMatched) { registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedpt_mcdetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedpt_mcpetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); + registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedpt_mcpmcdetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); registry.add("h2_jet_eta_mcd_jet_eta_mcp_matchedpt", "Eta mcd vs. Eta mcp;#eta_{jet}^{mcd};#eta_{jet}^{mcp}", {HistType::kTH2F, {jetEtaAxis, jetEtaAxis}}, doSumw2); registry.add("h2_jet_phi_mcd_jet_phi_mcp_matchedgpt_mcdetaconstraint", "Phi mcd vs. Phi mcp;#varphi_{jet}^{mcd};#varphi_{jet}^{mcp}", {HistType::kTH2F, {phiAxis, phiAxis}}, doSumw2); registry.add("h2_jet_phi_mcd_jet_phi_mcp_matchedgpt_mcpetaconstraint", "Phi mcd vs. Phi mcp;#varphi_{jet}^{mcd};#varphi_{jet}^{mcp}", {HistType::kTH2F, {phiAxis, phiAxis}}, doSumw2); registry.add("h2_jet_ntracks_mcd_jet_ntracks_mcp_matchedpt", "Ntracks mcd vs. Ntracks mcp;N_{jet tracks}^{mcd};N_{jet tracks}^{mcp}", {HistType::kTH2F, {{200, -0.5, 199.5}, {200, -0.5, 199.5}}}, doSumw2); - registry.add("h2_jet_pt_mcp_jet_pt_diff_matchedpt", "jet mcp pT vs. delta pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); (#it{p}_{T,jet}^{mcp} (GeV/#it{c}) - #it{p}_{T,jet}^{mcd} (GeV/#it{c})) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); + registry.add("h2_jet_pt_mcp_jet_pt_diff_matchedpt", "jet mcp pT vs. delta pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); (#it{p}_{T,jet}^{mcd} (GeV/#it{c}) - #it{p}_{T,jet}^{mcp} (GeV/#it{c})) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_diff_matchedpt", "jet mcd pT vs. delta pT / jet mcd pt;#it{p}_{T,jet}^{mcd} (GeV/#it{c}); (#it{p}_{T,jet}^{mcd} (GeV/#it{c}) - #it{p}_{T,jet}^{mcp} (GeV/#it{c})) / #it{p}_{T,jet}^{mcd} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); registry.add("h2_jet_pt_mcp_jet_pt_ratio_matchedpt", "jet mcp pT vs. jet mcd pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); #it{p}_{T,jet}^{mcd} (GeV/#it{c}) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 5.0}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedpt_mcdetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxis, jetPtAxis, {30, 0., 1.}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedpt_mcpetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxis, jetPtAxis, {30, 0., 1.}}}, doSumw2); } if (checkGeoPtMatched) { registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeopt_mcdetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeopt_mcpetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); + registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeopt_mcpmcdetaconstraint", "pT mcd vs. pT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, jetPtAxis}}, doSumw2); registry.add("h2_jet_eta_mcd_jet_eta_mcp_matchedgeopt", "Eta mcd vs. Eta mcp;#eta_{jet}^{mcd};#eta_{jet}^{mcp}", {HistType::kTH2F, {jetEtaAxis, jetEtaAxis}}, doSumw2); registry.add("h2_jet_phi_mcd_jet_phi_mcp_matchedgeopt_mcdetaconstraint", "Phi mcd vs. Phi mcp;#varphi_{jet}^{mcd};#varphi_{jet}^{mcp}", {HistType::kTH2F, {phiAxis, phiAxis}}, doSumw2); registry.add("h2_jet_phi_mcd_jet_phi_mcp_matchedgeopt_mcpetaconstraint", "Phi mcd vs. Phi mcp;#varphi_{jet}^{mcd};#varphi_{jet}^{mcp}", {HistType::kTH2F, {phiAxis, phiAxis}}, doSumw2); registry.add("h2_jet_ntracks_mcd_jet_ntracks_mcp_matchedgeopt", "Ntracks mcd vs. Ntracks mcp;N_{jet tracks}^{mcd};N_{jet tracks}^{mcp}", {HistType::kTH2F, {{200, -0.5, 199.5}, {200, -0.5, 199.5}}}, doSumw2); - registry.add("h2_jet_pt_mcp_jet_pt_diff_matchedgeopt", "jet mcp pT vs. delta pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); (#it{p}_{T,jet}^{mcp} (GeV/#it{c}) - #it{p}_{T,jet}^{mcd} (GeV/#it{c})) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); + registry.add("h2_jet_pt_mcp_jet_pt_diff_matchedgeopt", "jet mcp pT vs. delta pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); (#it{p}_{T,jet}^{mcd} (GeV/#it{c}) - #it{p}_{T,jet}^{mcp} (GeV/#it{c})) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_diff_matchedgeopt", "jet mcd pT vs. delta pT / jet mcd pt;#it{p}_{T,jet}^{mcd} (GeV/#it{c}); (#it{p}_{T,jet}^{mcd} (GeV/#it{c}) - #it{p}_{T,jet}^{mcp} (GeV/#it{c})) / #it{p}_{T,jet}^{mcd} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 2.0}}}, doSumw2); registry.add("h2_jet_pt_mcp_jet_pt_ratio_matchedgeopt", "jet mcp pT vs. jet mcd pT / jet mcp pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); #it{p}_{T,jet}^{mcd} (GeV/#it{c}) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxis, {1000, -5.0, 5.0}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeopt_mcdetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxis, jetPtAxis, {30, 0., 1.}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeopt_mcpetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxis, jetPtAxis, {30, 0., 1.}}}, doSumw2); } } if (doprocessJetsMatchedAreaSub || doprocessJetsMatchedAreaSubWeighted) { registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_rhoareasubtracted_mcdetaconstraint", "corr pT mcd vs. corr cpT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, jetPtAxisRhoAreaSub}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_rhoareasubtracted_mcpetaconstraint", "corr pT mcd vs. corr cpT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, jetPtAxisRhoAreaSub}}, doSumw2); + registry.add("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_rhoareasubtracted_mcpmcdetaconstraint", "corr pT mcd vs. corr cpT mcp;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, jetPtAxisRhoAreaSub}}, doSumw2); registry.add("h2_jet_pt_mcd_rhoareasubtracted_jet_pt_mcp_matchedgeo_mcdetaconstraint", "UEsub pT mcd vs. raw pT mcp;#it{p}_{T,jet}^{mcd} - #rho#it{A} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, jetPtAxis}}, doSumw2); registry.add("h2_jet_pt_mcp_jet_pt_diff_matchedgeo_rhoareasubtracted", "jet mcp corr pT vs. corr delta pT / jet mcp corr pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); (#it{p}_{T,jet}^{mcp} (GeV/#it{c}) - #it{p}_{T,jet}^{mcd} (GeV/#it{c})) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {1000, -5.0, 5.0}}}, doSumw2); registry.add("h2_jet_pt_mcd_jet_pt_diff_matchedgeo_rhoareasubtracted", "jet mcd corr pT vs. corr delta pT / jet mcd corr pt;#it{p}_{T,jet}^{mcd} (GeV/#it{c}); (#it{p}_{T,jet}^{mcd} (GeV/#it{c}) - #it{p}_{T,jet}^{mcp} (GeV/#it{c})) / #it{p}_{T,jet}^{mcd} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {1000, -5.0, 5.0}}}, doSumw2); registry.add("h2_jet_pt_mcp_jet_pt_ratio_matchedgeo_rhoareasubtracted", "jet mcp corr pT vs. jet mcd corr pT / jet mcp corr pt;#it{p}_{T,jet}^{mcp} (GeV/#it{c}); #it{p}_{T,jet}^{mcd} (GeV/#it{c}) / #it{p}_{T,jet}^{mcp} (GeV/#it{c})", {HistType::kTH2F, {jetPtAxisRhoAreaSub, {1000, -5.0, 5.0}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_rhoareasubtracted_mcdetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxisRhoAreaSub, jetPtAxisRhoAreaSub, {30, 0., 1.}}}, doSumw2); + registry.add("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_rhoareasubtracted_mcpetaconstraint", "pT mcd vs. pT mcp vs. angularity mcd;#it{p}_{T,jet}^{mcd} (GeV/#it{c});#it{p}_{T,jet}^{mcp} (GeV/#it{c});angularity", {HistType::kTH3F, {jetPtAxisRhoAreaSub, jetPtAxisRhoAreaSub, {30, 0., 1.}}}, doSumw2); } if (!(acceptSplitCollisions == NonSplitOnly || acceptSplitCollisions == SplitOkCheckAnyAssocColl || acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly)) { @@ -324,13 +348,13 @@ struct JetSpectraCharged { return true; } // if isMCGenOnly is true, skip MC selection and accept all of them - float centrality = -1.0; + float mcpCentrality = -1.0; // checkCentFT0M ? centrality = mccollision.centFT0M() : centrality = mccollision.centFT0C(); - centrality = mccollision.centFT0M(); + mcpCentrality = mccollision.centFT0M(); if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 0.5); - registry.fill(HIST("h2_centrality_mccollisions"), centrality, 0.5, eventWeight); + registry.fill(HIST("h2_centrality_mccollisions"), mcpCentrality, 0.5, eventWeight); if (isWeighted) registry.fill(HIST("h_mccollisions_weighted"), 0.5, eventWeight); } @@ -340,7 +364,7 @@ struct JetSpectraCharged { } if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 1.5); - registry.fill(HIST("h2_centrality_mccollisions"), centrality, 1.5, eventWeight); + registry.fill(HIST("h2_centrality_mccollisions"), mcpCentrality, 1.5, eventWeight); if (isWeighted) registry.fill(HIST("h_mccollisions_weighted"), 1.5, eventWeight); } @@ -350,7 +374,7 @@ struct JetSpectraCharged { } if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 2.5); - registry.fill(HIST("h2_centrality_mccollisions"), centrality, 2.5, eventWeight); + registry.fill(HIST("h2_centrality_mccollisions"), mcpCentrality, 2.5, eventWeight); if (isWeighted) registry.fill(HIST("h_mccollisions_weighted"), 2.5, eventWeight); } @@ -366,7 +390,7 @@ struct JetSpectraCharged { occupancyIsGood = true; } - if ((centralityMin < centrality) && (centrality < centralityMax)) { + if ((centralityMin < mcpCentrality) && (mcpCentrality < centralityMax)) { centralityIsGood = true; } } else { @@ -378,9 +402,9 @@ struct JetSpectraCharged { occupancyIsGood = true; } - float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); - if ((centralityMin < centrality) && (centrality < centralityMax)) { + float mcdCentrality = -1.0; + checkCentFT0M ? mcdCentrality = collision.centFT0M() : mcdCentrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C()); + if ((centralityMin < mcdCentrality) && (mcdCentrality < centralityMax)) { centralityIsGood = true; } } @@ -391,7 +415,7 @@ struct JetSpectraCharged { } if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 3.5); - registry.fill(HIST("h2_centrality_mccollisions"), centrality, 3.5, eventWeight); + registry.fill(HIST("h2_centrality_mccollisions"), mcpCentrality, 3.5, eventWeight); if (isWeighted) registry.fill(HIST("h_mccollisions_weighted"), 3.5, eventWeight); } @@ -401,7 +425,7 @@ struct JetSpectraCharged { } if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 4.5); - registry.fill(HIST("h2_centrality_mccollisions"), centrality, 4.5, eventWeight); + registry.fill(HIST("h2_centrality_mccollisions"), mcpCentrality, 4.5, eventWeight); if (isWeighted) registry.fill(HIST("h_mccollisions_weighted"), 4.5, eventWeight); } @@ -411,7 +435,7 @@ struct JetSpectraCharged { } if (fillHistograms) { registry.fill(HIST("h_mccollisions"), 5.5); - registry.fill(HIST("h2_centrality_mccollisions"), centrality, 5.5, eventWeight); + registry.fill(HIST("h2_centrality_mccollisions"), mcpCentrality, 5.5, eventWeight); if (isWeighted) registry.fill(HIST("h_mccollisions_weighted"), 5.5, eventWeight); } @@ -423,7 +447,7 @@ struct JetSpectraCharged { bool applyCollisionCuts(TColl const& collision, bool fillHistograms = false, bool isWeighted = false, float eventWeight = 1.0) { float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); if (fillHistograms) { registry.fill(HIST("h_collisions"), 0.5); @@ -483,9 +507,13 @@ struct JetSpectraCharged { registry.fill(HIST("h3_jet_pt_jet_eta_jet_phi"), jet.pt(), jet.eta(), jet.phi(), weight); } + float angularity = 0.; for (const auto& constituent : jet.template tracks_as()) { registry.fill(HIST("h2_jet_pt_track_pt"), jet.pt(), constituent.pt(), weight); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); } + angularity /= std::pow(jet.pt(), kappa); + registry.fill(HIST("h2_jet_pt_jet_angularity"), jet.pt(), angularity, weight); } template @@ -494,7 +522,7 @@ struct JetSpectraCharged { if (jet.pt() > pTHatMaxMCD * pTHat || pTHat < pTHatAbsoluteMin) { return; } - double jetcorrpt = jet.pt() - (rho * jet.area()); + double jetcorrpt = jet.pt() - ((rho - rhoShift) * jet.area()); if (jet.r() == round(selectedJetsRadius * 100.0f)) { // fill jet histograms after area-based subtraction registry.fill(HIST("h_jet_pt_rhoareasubtracted"), jetcorrpt, weight); @@ -511,9 +539,13 @@ struct JetSpectraCharged { } } + float angularity = 0.; for (const auto& constituent : jet.template tracks_as()) { registry.fill(HIST("h2_jet_pt_track_pt_rhoareasubtracted"), jetcorrpt, constituent.pt(), weight); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); } + angularity /= std::pow(jet.pt(), kappa); + registry.fill(HIST("h2_jet_pt_jet_angularity_rhoareasubtracted"), jetcorrpt, angularity, weight); } template @@ -531,10 +563,13 @@ struct JetSpectraCharged { registry.fill(HIST("h2_jet_pt_part_jet_area_part"), jet.pt(), jet.area(), weight); registry.fill(HIST("h2_jet_pt_part_jet_ntracks_part"), jet.pt(), jet.tracksIds().size(), weight); } - + float angularity = 0.; for (const auto& constituent : jet.template tracks_as()) { registry.fill(HIST("h2_jet_pt_part_track_pt_part"), jet.pt(), constituent.pt(), weight); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); } + angularity /= std::pow(jet.pt(), kappa); + registry.fill(HIST("h2_jet_pt_jet_angularity_part"), jet.pt(), angularity, weight); } template @@ -545,7 +580,7 @@ struct JetSpectraCharged { } if (jet.r() == round(selectedJetsRadius * 100.0f)) { // fill mcp jet histograms - double jetcorrpt = jet.pt() - (rho * jet.area()); + double jetcorrpt = jet.pt() - ((rho - rhoShift) * jet.area()); registry.fill(HIST("h_jet_pt_part_rhoareasubtracted"), jetcorrpt, weight); registry.fill(HIST("h3_jet_pt_jet_eta_jet_phi_part_rhoareasubtracted"), jetcorrpt, jet.eta(), jet.phi(), weight); if (jetcorrpt > 0) { @@ -584,17 +619,27 @@ struct JetSpectraCharged { } if (jetMCD.r() == round(selectedJetsRadius * 100.0f)) { double dpt = jetMCD.pt() - jetMCP.pt(); + float angularityMcd = 0.; + for (const auto& constituent : jetMCD.template tracks_as()) { + angularityMcd += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetMCD, constituent) / (jetMCD.r() / 100.f), alpha); + } + angularityMcd /= std::pow(jetMCD.pt(), kappa); if (jetfindingutilities::isInEtaAcceptance(jetMCD, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_mcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); + if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_mcpmcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); + } registry.fill(HIST("h2_jet_phi_mcd_jet_phi_mcp_matchedgeo_mcdetaconstraint"), jetMCD.phi(), jetMCP.phi(), weight); registry.fill(HIST("h2_jet_pt_mcd_jet_pt_diff_matchedgeo"), jetMCD.pt(), dpt / jetMCD.pt(), weight); registry.fill(HIST("h2_jet_ntracks_mcd_jet_ntracks_mcp_matchedgeo"), jetMCD.tracksIds().size(), jetMCP.tracksIds().size(), weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_mcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), angularityMcd, weight); } if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_mcpetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); registry.fill(HIST("h2_jet_phi_mcd_jet_phi_mcp_matchedgeo_mcpetaconstraint"), jetMCD.phi(), jetMCP.phi(), weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_diff_matchedgeo"), jetMCP.pt(), dpt / jetMCP.pt(), weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_ratio_matchedgeo"), jetMCP.pt(), jetMCD.pt() / jetMCP.pt(), weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_mcpetaconstraint"), jetMCD.pt(), jetMCP.pt(), angularityMcd, weight); } registry.fill(HIST("h2_jet_eta_mcd_jet_eta_mcp_matchedgeo"), jetMCD.eta(), jetMCP.eta(), weight); } @@ -613,17 +658,27 @@ struct JetSpectraCharged { } if (jetMCD.r() == round(selectedJetsRadius * 100.0f)) { double dpt = jetMCD.pt() - jetMCP.pt(); + float angularityMcd = 0.; + for (const auto& constituent : jetMCD.template tracks_as()) { + angularityMcd += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetMCD, constituent) / (jetMCD.r() / 100.f), alpha); + } + angularityMcd /= std::pow(jetMCD.pt(), kappa); if (jetfindingutilities::isInEtaAcceptance(jetMCD, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedpt_mcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); + if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedpt_mcpmcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); + } registry.fill(HIST("h2_jet_phi_mcd_jet_phi_mcp_matchedpt_mcdetaconstraint"), jetMCD.phi(), jetMCP.phi(), weight); registry.fill(HIST("h2_jet_pt_mcd_jet_pt_diff_matchedpt"), jetMCD.pt(), dpt / jetMCD.pt(), weight); registry.fill(HIST("h2_jet_ntracks_mcd_jet_ntracks_mcp_matchedpt"), jetMCD.tracksIds().size(), jetMCP.tracksIds().size(), weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedpt_mcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), angularityMcd, weight); } if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedpt_mcpetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); registry.fill(HIST("h2_jet_phi_mcd_jet_phi_mcp_matchedpt_mcpetaconstraint"), jetMCD.phi(), jetMCP.phi(), weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_diff_matchedpt"), jetMCP.pt(), dpt / jetMCP.pt(), weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_ratio_matchedpt"), jetMCP.pt(), jetMCD.pt() / jetMCP.pt(), weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedpt_mcpetaconstraint"), jetMCD.pt(), jetMCP.pt(), angularityMcd, weight); } registry.fill(HIST("h2_jet_eta_mcd_jet_eta_mcp_matchedpt"), jetMCD.eta(), jetMCP.eta(), weight); } @@ -642,19 +697,29 @@ struct JetSpectraCharged { } if (jetMCD.template matchedJetGeo_first_as>().globalIndex() == jetMCD.template matchedJetPt_first_as>().globalIndex()) { // not a good way to do this double dpt = jetMCD.pt() - jetMCP.pt(); + float angularityMcd = 0.; + for (const auto& constituent : jetMCD.template tracks_as()) { + angularityMcd += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetMCD, constituent) / (jetMCD.r() / 100.f), alpha); + } + angularityMcd /= std::pow(jetMCD.pt(), kappa); if (jetfindingutilities::isInEtaAcceptance(jetMCD, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeopt_mcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); + if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeopt_mcpmcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); + } registry.fill(HIST("h2_jet_phi_mcd_jet_phi_mcp_matchedgeopt_mcdetaconstraint"), jetMCD.phi(), jetMCP.phi(), weight); registry.fill(HIST("h2_jet_pt_mcd_jet_pt_diff_matchedgeopt"), jetMCD.pt(), dpt / jetMCD.pt(), weight); registry.fill(HIST("h2_jet_ntracks_mcd_jet_ntracks_mcp_matchedgeopt"), jetMCD.tracksIds().size(), jetMCP.tracksIds().size(), weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeopt_mcdetaconstraint"), jetMCD.pt(), jetMCP.pt(), angularityMcd, weight); } if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeopt_mcpetaconstraint"), jetMCD.pt(), jetMCP.pt(), weight); registry.fill(HIST("h2_jet_phi_mcd_jet_phi_mcp_matchedgeopt_mcpetaconstraint"), jetMCD.phi(), jetMCP.phi(), weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_diff_matchedgeopt"), jetMCP.pt(), dpt / jetMCP.pt(), weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_ratio_matchedgeopt"), jetMCP.pt(), jetMCD.pt() / jetMCP.pt(), weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeopt_mcpetaconstraint"), jetMCD.pt(), jetMCP.pt(), angularityMcd, weight); } - registry.fill(HIST("h2_jet_eta_mcd_jet_eta_mcp_matchedpt"), jetMCD.eta(), jetMCP.eta(), weight); + registry.fill(HIST("h2_jet_eta_mcd_jet_eta_mcp_matchedgeopt"), jetMCD.eta(), jetMCP.eta(), weight); } } } @@ -673,18 +738,28 @@ struct JetSpectraCharged { continue; } if (jetMCD.r() == round(selectedJetsRadius * 100.0f)) { - double corrTagjetpt = jetMCP.pt() - (mcrho * jetMCP.area()); - double corrBasejetpt = jetMCD.pt() - (rho * jetMCD.area()); - double dcorrpt = corrBasejetpt - corrTagjetpt; + float corrTagjetpt = jetMCP.pt() - (mcrho * jetMCP.area()); + float corrBasejetpt = jetMCD.pt() - (rho * jetMCD.area()); + float dcorrpt = corrBasejetpt - corrTagjetpt; + float angularityMcd = 0.; + for (const auto& constituent : jetMCD.template tracks_as()) { + angularityMcd += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jetMCD, constituent) / (jetMCD.r() / 100.f), alpha); + } + angularityMcd /= std::pow(corrBasejetpt, kappa); if (jetfindingutilities::isInEtaAcceptance(jetMCD, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_rhoareasubtracted_mcdetaconstraint"), corrBasejetpt, corrTagjetpt, weight); + if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_rhoareasubtracted_mcpmcdetaconstraint"), corrBasejetpt, corrTagjetpt, weight); + } registry.fill(HIST("h2_jet_pt_mcd_rhoareasubtracted_jet_pt_mcp_matchedgeo_mcdetaconstraint"), corrBasejetpt, jetMCP.pt(), weight); registry.fill(HIST("h2_jet_pt_mcd_jet_pt_diff_matchedgeo_rhoareasubtracted"), corrBasejetpt, dcorrpt / corrBasejetpt, weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_rhoareasubtracted_mcdetaconstraint"), corrBasejetpt, corrTagjetpt, angularityMcd, weight); } if (jetfindingutilities::isInEtaAcceptance(jetMCP, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { registry.fill(HIST("h2_jet_pt_mcd_jet_pt_mcp_matchedgeo_rhoareasubtracted_mcpetaconstraint"), corrBasejetpt, corrTagjetpt, weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_diff_matchedgeo_rhoareasubtracted"), corrTagjetpt, dcorrpt / corrTagjetpt, weight); registry.fill(HIST("h2_jet_pt_mcp_jet_pt_ratio_matchedgeo_rhoareasubtracted"), corrTagjetpt, corrBasejetpt / corrTagjetpt, weight); + registry.fill(HIST("h3_jet_pt_mcd_jet_pt_mcp_jet_mcd_angularity_matchedgeo_rhoareasubtracted_mcpetaconstraint"), corrBasejetpt, corrTagjetpt, angularityMcd, weight); } } } @@ -762,7 +837,7 @@ struct JetSpectraCharged { } float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -785,7 +860,7 @@ struct JetSpectraCharged { } float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -808,7 +883,7 @@ struct JetSpectraCharged { } float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -831,7 +906,7 @@ struct JetSpectraCharged { } float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -848,7 +923,7 @@ struct JetSpectraCharged { void processSpectraMCDWeighted(soa::Filtered::iterator const& collision, soa::Join const& jets, aod::JetTracks const&, - aod::JetMcCollisions const&) + aod::JetMcCollisions const& mccollisions) { bool fillHistograms = false; bool isWeighted = true; @@ -856,9 +931,18 @@ struct JetSpectraCharged { if (!applyCollisionCuts(collision, fillHistograms, isWeighted, eventWeight)) { return; } - float pTHat = collision.has_mcCollision() && collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + // collision.mcCollision() auto-resolves to the wrong JMcCollisions binding here; bind via rawIteratorAt. + float ptHardFromMc = ptHardCalcMethodSwitch; + if (collision.mcCollisionId() >= 0) { + float storedPtHard = mccollisions.rawIteratorAt(collision.mcCollisionId()).ptHard(); + // LHC26b5 ships placeholder ptHard=1.0; fall back to weight-derived. + if (storedPtHard > BrokenPtHardSentinel && storedPtHard < ptHardCalcMethodSwitch) { + ptHardFromMc = storedPtHard; + } + } + float pTHat = ptHardFromMc < ptHardCalcMethodSwitch ? ptHardFromMc : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -875,7 +959,7 @@ struct JetSpectraCharged { void processSpectraAreaSubMCDWeighted(soa::Filtered>::iterator const& collision, soa::Join const& jets, aod::JetTracks const&, - aod::JetMcCollisions const&) + aod::JetMcCollisions const& mccollisions) { bool fillHistograms = false; bool isWeighted = true; @@ -883,9 +967,18 @@ struct JetSpectraCharged { if (!applyCollisionCuts(collision, fillHistograms, isWeighted, eventWeight)) { return; } - float pTHat = collision.has_mcCollision() && collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + // See note in processSpectraMCDWeighted: avoid the wrong-type bind by + // looking the MC collision up explicitly via the named subscribed table. + float ptHardFromMc = ptHardCalcMethodSwitch; + if (collision.mcCollisionId() >= 0) { + float storedPtHard = mccollisions.rawIteratorAt(collision.mcCollisionId()).ptHard(); + if (storedPtHard > BrokenPtHardSentinel && storedPtHard < ptHardCalcMethodSwitch) { + ptHardFromMc = storedPtHard; + } + } + float pTHat = ptHardFromMc < ptHardCalcMethodSwitch ? ptHardFromMc : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -936,7 +1029,7 @@ struct JetSpectraCharged { bool hasSel8Coll = false; bool centralityIsGood = false; bool occupancyIsGood = false; - float centrality = mccollision.centFT0M(); + float mcpCentrality = mccollision.centFT0M(); if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly) { if (hasRecoColl && jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { hasSel8Coll = true; @@ -944,7 +1037,7 @@ struct JetSpectraCharged { if (hasRecoColl && (trackOccupancyInTimeRangeMin < collisions.begin().trackOccupancyInTimeRange()) && (collisions.begin().trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { occupancyIsGood = true; } - if ((centralityMin < centrality) && (centrality < centralityMax)) { + if ((centralityMin < mcpCentrality) && (mcpCentrality < centralityMax)) { centralityIsGood = true; } } else { @@ -955,9 +1048,9 @@ struct JetSpectraCharged { if ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { occupancyIsGood = true; } - float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); - if ((centralityMin < centrality) && (centrality < centralityMax)) { + float mcdCentrality = -1.0; + checkCentFT0M ? mcdCentrality = collision.centFT0M() : (mcdCentrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); + if ((centralityMin < mcdCentrality) && (mcdCentrality < centralityMax)) { centralityIsGood = true; } } @@ -1019,7 +1112,7 @@ struct JetSpectraCharged { bool hasSel8Coll = false; bool centralityIsGood = false; bool occupancyIsGood = false; - float centrality = mccollision.centFT0M(); + float mcpCentrality = mccollision.centFT0M(); if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly) { if (hasRecoColl && jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { hasSel8Coll = true; @@ -1027,7 +1120,7 @@ struct JetSpectraCharged { if (hasRecoColl && (trackOccupancyInTimeRangeMin < collisions.begin().trackOccupancyInTimeRange()) && (collisions.begin().trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { occupancyIsGood = true; } - if ((centralityMin < centrality) && (centrality < centralityMax)) { + if ((centralityMin < mcpCentrality) && (mcpCentrality < centralityMax)) { centralityIsGood = true; } } else { @@ -1038,9 +1131,9 @@ struct JetSpectraCharged { if ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax)) { occupancyIsGood = true; } - float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); - if ((centralityMin < centrality) && (centrality < centralityMax)) { + float mcdCentrality = -1.0; + checkCentFT0M ? mcdCentrality = collision.centFT0M() : (mcdCentrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); + if ((centralityMin < mcdCentrality) && (mcdCentrality < centralityMax)) { centralityIsGood = true; } } @@ -1126,7 +1219,8 @@ struct JetSpectraCharged { if (!applyMCCollisionCuts(mccollision, collisions, fillHistograms, isWeighted, eventWeight)) { return; } - float pTHat = mccollision.ptHard() < 999.0f ? mccollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + // LHC26b5 ships placeholder ptHard=1.0; fall back to weight-derived. + float pTHat = (mccollision.ptHard() > BrokenPtHardSentinel && mccollision.ptHard() < ptHardCalcMethodSwitch) ? mccollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { continue; @@ -1134,8 +1228,8 @@ struct JetSpectraCharged { if (!isAcceptedJet(jet, mcLevelIsParticleLevel)) { continue; } - int Nmax = 21; - for (int N = 1; N < Nmax; N++) { + int nMax = 21; + for (int N = 1; N < nMax; N++) { if (jet.pt() < N * 0.25 * pTHat && jet.r() == round(selectedJetsRadius * 100.0f)) { registry.fill(HIST("h2_jet_ptcut_part"), jet.pt(), N * 0.25, eventWeight); } @@ -1158,7 +1252,9 @@ struct JetSpectraCharged { if (!applyMCCollisionCuts(mccollision, collisions, fillHistograms, isWeighted, eventWeight)) { return; } - float pTHat = mccollision.ptHard() < 999.0f ? mccollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + // See note in processSpectraMCPWeighted re: ptHard=1.0 placeholder in + // current LHC26b5-class MC. + float pTHat = (mccollision.ptHard() > BrokenPtHardSentinel && mccollision.ptHard() < ptHardCalcMethodSwitch) ? mccollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); registry.fill(HIST("h_mccollisions_rho"), mccollision.rho(), eventWeight); for (auto const& jet : jets) { @@ -1182,7 +1278,7 @@ struct JetSpectraCharged { } float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -1205,7 +1301,7 @@ struct JetSpectraCharged { } float centrality = -1.0; - checkCentFT0M ? centrality = collision.centFT0M() : centrality = collision.centFT0C(); + checkCentFT0M ? centrality = collision.centFT0M() : (centrality = (useFT0CVariant ? collision.centFT0CVariant1() : collision.centFT0C())); for (auto const& jet : jets) { if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { @@ -1241,7 +1337,7 @@ struct JetSpectraCharged { ChargedMCDMatchedJets const& mcdjets, ChargedMCPMatchedJets const&, aod::JetTracks const&, aod::JetParticles const&, - aod::JetMcCollisions const&) + aod::JetMcCollisions const& mccollisions) { bool fillHistograms = false; bool isWeighted = true; @@ -1249,7 +1345,16 @@ struct JetSpectraCharged { if (!applyCollisionCuts(collision, fillHistograms, isWeighted, eventWeight)) { return; } - float pTHat = collision.has_mcCollision() && collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + // See note in processSpectraMCDWeighted: avoid the wrong-type bind by + // looking the MC collision up explicitly via the named subscribed table. + float ptHardFromMc = ptHardCalcMethodSwitch; + if (collision.mcCollisionId() >= 0) { + float storedPtHard = mccollisions.rawIteratorAt(collision.mcCollisionId()).ptHard(); + if (storedPtHard > BrokenPtHardSentinel && storedPtHard < ptHardCalcMethodSwitch) { + ptHardFromMc = storedPtHard; + } + } + float pTHat = ptHardFromMc < ptHardCalcMethodSwitch ? ptHardFromMc : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); for (const auto& mcdjet : mcdjets) { if (!isAcceptedJet(mcdjet)) { continue; @@ -1292,9 +1397,21 @@ struct JetSpectraCharged { if (!applyCollisionCuts(collision, fillHistograms, isWeighted, eventWeight)) { return; } - float pTHat = collision.has_mcCollision() && collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + // Here we already have JetBkgRhoMcCollisions explicitly subscribed; use the + // mcCollision_as() cast to dodge the wrong-type bind + // (same root cause as processSpectraMCDWeighted) and also guard against the + // ptHard=1.0 placeholder seen in current LHC26b5-class MC. + bool hasMc = collision.has_mcCollision(); + float ptHardFromMc = ptHardCalcMethodSwitch; + if (hasMc) { + float storedPtHard = collision.mcCollision_as().ptHard(); + if (storedPtHard > BrokenPtHardSentinel && storedPtHard < ptHardCalcMethodSwitch) { + ptHardFromMc = storedPtHard; + } + } + float pTHat = ptHardFromMc < ptHardCalcMethodSwitch ? ptHardFromMc : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); - double mcrho = collision.has_mcCollision() ? collision.mcCollision_as().rho() : -1; + double mcrho = hasMc ? collision.mcCollision_as().rho() : -1; for (const auto& mcdjet : mcdjets) { if (!isAcceptedJet(mcdjet)) { diff --git a/PWGJE/Tasks/jetSpectraChargedGen.cxx b/PWGJE/Tasks/jetSpectraChargedGen.cxx new file mode 100644 index 00000000000..16d24dd0c2e --- /dev/null +++ b/PWGJE/Tasks/jetSpectraChargedGen.cxx @@ -0,0 +1,287 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jetSpectraChargedGen.cxx +/// \brief Charged-particle jet spectra at MC-particle level for generator-only +/// AODs (no reconstruction tables required). Companion to +/// jetSpectraCharged.cxx for productions such as EPOS 4 on-the-fly +/// where only mc-particle and mc-collision tables exist. +/// \author Joonsuk Bae + +#include "PWGJE/Core/JetFindingUtilities.h" +#include "PWGJE/Core/JetUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" +#include "PWGJE/DataModel/JetSubtraction.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct JetSpectraChargedGen { + + using JetBkgRhoMcCollisions = soa::Join; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable selectedJetsRadius{"selectedJetsRadius", 0.2, "resolution parameter for histograms without radius"}; + Configurable jetEtaMin{"jetEtaMin", -0.7, "minimum jet pseudorapidity"}; + Configurable jetEtaMax{"jetEtaMax", 0.7, "maximum jet pseudorapidity"}; + Configurable trackEtaMin{"trackEtaMin", -0.9, "minimum particle pseudorapidity"}; + Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum particle pseudorapidity"}; + Configurable jetAreaFractionMin{"jetAreaFractionMin", -99.0, "minimum area fraction relative to pi R^2"}; + Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT of the leading constituent"}; + Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT of the leading constituent"}; + Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0, "maximum jet pT in units of pTHat (outlier rejection)"}; + Configurable pTHatAbsoluteMin{"pTHatAbsoluteMin", -99.0, "minimum pTHat"}; + Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the back-calculation of pTHat"}; + Configurable simPtRef{"simPtRef", 10.0, "reference pT for the back-calculation of pTHat from the event weight"}; + Configurable vertexZCut{"vertexZCut", 10.0, "truth vertex-z cut; <=0 disables"}; + Configurable kappa{"kappa", 1.0, "angularity kappa"}; + Configurable alpha{"alpha", 1.0, "angularity alpha"}; + Configurable jetPtMax{"jetPtMax", 200.0, "max of the jet pT axis"}; + + static constexpr float ConfigSwitchLow = -98.0f; + static constexpr float ConfigSwitchHigh = 9998.0f; + + void init(InitContext const&) + { + AxisSpec jetPtAxis = {static_cast(jetPtMax), 0.0, jetPtMax, "#it{p}_{T,jet} (GeV/#it{c})"}; + AxisSpec etaAxis = {100, -1.0, 1.0, "#eta_{jet}"}; + AxisSpec phiAxis = {160, -1.0, 7.0, "#varphi_{jet}"}; + AxisSpec rhoAxis = {200, 0.0, 200.0, "#rho (GeV/#it{c})"}; + + registry.add("h_mccollisions", "MC collisions counter;;entries", {HistType::kTH1F, {{1, 0.0, 1.0}}}); + registry.add("h_mccollisions_rho", "#rho distribution;#rho (GeV/#it{c});entries", {HistType::kTH1F, {rhoAxis}}); + registry.add("p_xSection", ";;<#sigma_{gen}> from HepMC (pb)", {HistType::kTProfile, {{1, 0.0, 1.0}}}); + + registry.add("h_jet_pt_part", ";#it{p}_{T,jet}^{part} (GeV/#it{c});entries", {HistType::kTH1F, {jetPtAxis}}); + registry.add("h_jet_eta_part", ";#eta_{jet}^{part};entries", {HistType::kTH1F, {etaAxis}}); + registry.add("h_jet_phi_part", ";#varphi_{jet}^{part};entries", {HistType::kTH1F, {phiAxis}}); + registry.add("h3_jet_pt_jet_eta_jet_phi_part", ";#it{p}_{T,jet}^{part};#eta_{jet}^{part};#varphi_{jet}^{part}", + {HistType::kTH3F, {jetPtAxis, etaAxis, phiAxis}}); + registry.add("h2_jet_pt_part_jet_area_part", ";#it{p}_{T,jet}^{part};jet area", + {HistType::kTH2F, {jetPtAxis, {200, 0.0, 4.0}}}); + registry.add("h2_jet_pt_part_jet_ntracks_part", ";#it{p}_{T,jet}^{part};#it{N}_{tracks}", + {HistType::kTH2F, {jetPtAxis, {100, 0.0, 100.0}}}); + registry.add("h2_jet_pt_part_track_pt_part", ";#it{p}_{T,jet}^{part};#it{p}_{T,track}^{part}", + {HistType::kTH2F, {jetPtAxis, jetPtAxis}}); + registry.add("h2_jet_pt_jet_angularity_part", ";#it{p}_{T,jet}^{part};angularity", + {HistType::kTH2F, {jetPtAxis, {200, 0.0, 4.0}}}); + + registry.add("h_jet_pt_part_rhoareasubtracted", ";#it{p}_{T,jet}^{part,sub} (GeV/#it{c});entries", + {HistType::kTH1F, {{static_cast(2 * jetPtMax), -jetPtMax, jetPtMax}}}); + registry.add("h_jet_eta_part_rhoareasubtracted", ";#eta_{jet}^{part};entries", {HistType::kTH1F, {etaAxis}}); + registry.add("h_jet_phi_part_rhoareasubtracted", ";#varphi_{jet}^{part};entries", {HistType::kTH1F, {phiAxis}}); + registry.add("h3_jet_pt_jet_eta_jet_phi_part_rhoareasubtracted", + ";#it{p}_{T,jet}^{part,sub};#eta_{jet}^{part};#varphi_{jet}^{part}", + {HistType::kTH3F, {{static_cast(2 * jetPtMax), -jetPtMax, jetPtMax}, etaAxis, phiAxis}}); + registry.add("h2_jet_pt_part_jet_area_part_rhoareasubtracted", ";#it{p}_{T,jet}^{part,sub};jet area", + {HistType::kTH2F, {{static_cast(2 * jetPtMax), -jetPtMax, jetPtMax}, {200, 0.0, 4.0}}}); + registry.add("h2_jet_pt_part_jet_ntracks_part_rhoareasubtracted", ";#it{p}_{T,jet}^{part,sub};#it{N}_{tracks}", + {HistType::kTH2F, {{static_cast(2 * jetPtMax), -jetPtMax, jetPtMax}, {100, 0.0, 100.0}}}); + } + + // Truth-only zvtx cut: returns true if accepted. + template + bool passVertexZ(TMcColl const& mccollision) + { + if (vertexZCut <= 0.0f) { + return true; + } + return std::abs(mccollision.posZ()) < vertexZCut; + } + + template + bool isAcceptedJet(TJet const& jet) + { + if (jetAreaFractionMin > ConfigSwitchLow) { + if (jet.area() < jetAreaFractionMin * o2::constants::math::PI * (jet.r() / 100.0) * (jet.r() / 100.0)) { + return false; + } + } + bool checkMin = (leadingConstituentPtMin > ConfigSwitchLow); + bool checkMax = (leadingConstituentPtMax < ConfigSwitchHigh); + if (!checkMin && !checkMax) { + return true; + } + bool passMin = !checkMin; + bool passMax = checkMax; + for (const auto& constituent : jet.template tracks_as()) { + double pt = constituent.pt(); + if (checkMin && pt >= leadingConstituentPtMin) { + passMin = true; + } + if (checkMax && pt > leadingConstituentPtMax) { + passMax = false; + } + } + return passMin && passMax; + } + + template + void fillRawHistograms(TJet const& jet, float weight, float pTHat) + { + if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) { + return; + } + if (jet.r() == std::round(selectedJetsRadius * 100.0f)) { + registry.fill(HIST("h_jet_pt_part"), jet.pt(), weight); + registry.fill(HIST("h_jet_eta_part"), jet.eta(), weight); + registry.fill(HIST("h_jet_phi_part"), jet.phi(), weight); + registry.fill(HIST("h3_jet_pt_jet_eta_jet_phi_part"), jet.pt(), jet.eta(), jet.phi(), weight); + registry.fill(HIST("h2_jet_pt_part_jet_area_part"), jet.pt(), jet.area(), weight); + registry.fill(HIST("h2_jet_pt_part_jet_ntracks_part"), jet.pt(), jet.tracksIds().size(), weight); + } + float angularity = 0.0f; + for (const auto& constituent : jet.template tracks_as()) { + registry.fill(HIST("h2_jet_pt_part_track_pt_part"), jet.pt(), constituent.pt(), weight); + angularity += std::pow(constituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, constituent) / (jet.r() / 100.f), alpha); + } + angularity /= std::pow(jet.pt(), kappa); + registry.fill(HIST("h2_jet_pt_jet_angularity_part"), jet.pt(), angularity, weight); + } + + template + void fillAreaSubHistograms(TJet const& jet, float rho, float weight, float pTHat) + { + if (jet.pt() > pTHatMaxMCP * pTHat || pTHat < pTHatAbsoluteMin) { + return; + } + if (jet.r() != std::round(selectedJetsRadius * 100.0f)) { + return; + } + double ptSub = jet.pt() - (rho * jet.area()); + registry.fill(HIST("h_jet_pt_part_rhoareasubtracted"), ptSub, weight); + registry.fill(HIST("h3_jet_pt_jet_eta_jet_phi_part_rhoareasubtracted"), ptSub, jet.eta(), jet.phi(), weight); + if (ptSub > 0.0) { + registry.fill(HIST("h_jet_eta_part_rhoareasubtracted"), jet.eta(), weight); + registry.fill(HIST("h_jet_phi_part_rhoareasubtracted"), jet.phi(), weight); + registry.fill(HIST("h2_jet_pt_part_jet_area_part_rhoareasubtracted"), ptSub, jet.area(), weight); + registry.fill(HIST("h2_jet_pt_part_jet_ntracks_part_rhoareasubtracted"), ptSub, jet.tracksIds().size(), weight); + } + } + + void processDummy(aod::JDummys const&) {} + PROCESS_SWITCH(JetSpectraChargedGen, processDummy, "dummy process", true); + + void processChargedGen(aod::JetMcCollisions::iterator const& mccollision, + soa::Join const& jets, + aod::JetParticles const&) + { + if (!passVertexZ(mccollision)) { + return; + } + registry.fill(HIST("h_mccollisions"), 0.5); + registry.fill(HIST("p_xSection"), 0.5, mccollision.xsectGen()); + for (auto const& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + continue; + } + if (!isAcceptedJet(jet)) { + continue; + } + fillRawHistograms(jet, /*weight*/ 1.0f, /*pTHat*/ 999.0f); + } + } + PROCESS_SWITCH(JetSpectraChargedGen, processChargedGen, + "charged-jet spectra at MC particle level (generator-only AOD)", false); + + void processChargedGenWeighted(aod::JetMcCollisions::iterator const& mccollision, + soa::Join const& jets, + aod::JetParticles const&) + { + if (!passVertexZ(mccollision)) { + return; + } + float weight = mccollision.weight(); + float pTHat = simPtRef / std::pow(weight, 1.0f / pTHatExponent); + registry.fill(HIST("h_mccollisions"), 0.5, weight); + registry.fill(HIST("p_xSection"), 0.5, mccollision.xsectGen(), weight); + for (auto const& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + continue; + } + if (!isAcceptedJet(jet)) { + continue; + } + fillRawHistograms(jet, weight, pTHat); + } + } + PROCESS_SWITCH(JetSpectraChargedGen, processChargedGenWeighted, + "charged-jet spectra at MC particle level, weighted (generator-only AOD)", false); + + void processChargedAreaSubGen(JetBkgRhoMcCollisions::iterator const& mccollision, + soa::Join const& jets, + aod::JetParticles const&) + { + if (!passVertexZ(mccollision)) { + return; + } + registry.fill(HIST("h_mccollisions"), 0.5); + registry.fill(HIST("p_xSection"), 0.5, mccollision.xsectGen()); + registry.fill(HIST("h_mccollisions_rho"), mccollision.rho()); + for (auto const& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + continue; + } + if (!isAcceptedJet(jet)) { + continue; + } + fillAreaSubHistograms(jet, mccollision.rho(), /*weight*/ 1.0f, /*pTHat*/ 999.0f); + } + } + PROCESS_SWITCH(JetSpectraChargedGen, processChargedAreaSubGen, + "charged-jet area-subtracted spectra at MC particle level (generator-only AOD)", false); + + void processChargedAreaSubGenWeighted(JetBkgRhoMcCollisions::iterator const& mccollision, + soa::Join const& jets, + aod::JetParticles const&) + { + if (!passVertexZ(mccollision)) { + return; + } + float weight = mccollision.weight(); + float pTHat = simPtRef / std::pow(weight, 1.0f / pTHatExponent); + registry.fill(HIST("h_mccollisions"), 0.5, weight); + registry.fill(HIST("p_xSection"), 0.5, mccollision.xsectGen(), weight); + registry.fill(HIST("h_mccollisions_rho"), mccollision.rho(), weight); + for (auto const& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + continue; + } + if (!isAcceptedJet(jet)) { + continue; + } + fillAreaSubHistograms(jet, mccollision.rho(), weight, pTHat); + } + } + PROCESS_SWITCH(JetSpectraChargedGen, processChargedAreaSubGenWeighted, + "charged-jet area-subtracted spectra at MC particle level, weighted (generator-only AOD)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/jetSpectraEseTask.cxx b/PWGJE/Tasks/jetSpectraEseTask.cxx index 0ddc76445c2..396a9acdef3 100644 --- a/PWGJE/Tasks/jetSpectraEseTask.cxx +++ b/PWGJE/Tasks/jetSpectraEseTask.cxx @@ -22,11 +22,14 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/EseTable.h" +#include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" #include #include #include +#include #include #include #include @@ -42,7 +45,9 @@ #include #include +#include #include +#include #include #include @@ -50,13 +55,13 @@ #include #include +#include #include #include #include #include #include #include -#include #include using namespace o2; @@ -64,7 +69,12 @@ using namespace o2::framework; using namespace o2::framework::expressions; struct JetSpectraEseTask { - Configurable cfgEfficiency{"cfgEfficiency", "", "CCDB path to efficiency"}; + Configurable cfgSystFlag{"cfgSystFlag", -1, "-1 = use configurable cuts, 0 = nominal defaults, >0 = systematic cut variation"}; + + struct : ConfigurableGroup { + Configurable cfgEfficiency{"cfgEfficiency", "", "CCDB path to efficiency"}; + Configurable cfgEfficiency3D{"cfgEfficiency3D", "", "CCDB path to 3D efficiency"}; + } efficiencyFiles; Configurable jetPtMin{"jetPtMin", 5.0, "minimum jet pT cut"}; Configurable jetR{"jetR", 0.2, "jet resolution parameter"}; Configurable randomConeR{"randomConeR", 0.4, "size of random Cone for estimating background fluctuations"}; @@ -72,29 +82,36 @@ struct JetSpectraEseTask { Configurable vertexZCut{"vertexZCut", 10.0, "vertex z cut"}; // Configurable leadingTrackPtCut{"leadingTrackPtCut", 5.0, "leading jet pT cut"}; Configurable jetAreaFractionMin{"jetAreaFractionMin", -99, "used to make a cut on the jet areas"}; - Configurable cfgCentVariant{"cfgCentVariant", false, "Flag for centrality variant 1"}; + Configurable cfgCentrality{"cfgCentrality", "FT0C", "Centrality estimator: FT0C, FT0M, or FT0CVariant1"}; Configurable cfgisPbPb{"cfgisPbPb", false, "Flag for using MC centrality in PbPb"}; Configurable cfgbkgSubMC{"cfgbkgSubMC", true, "Flag for MC background subtraction"}; Configurable cfgUseMCEventWeights{"cfgUseMCEventWeights", false, "Flag for using MC event weights"}; Configurable leadingConstituentPtMin{"leadingConstituentPtMin", -99.0, "minimum pT selection on jet constituent"}; Configurable leadingConstituentPtMax{"leadingConstituentPtMax", 9999.0, "maximum pT selection on jet constituent"}; - Configurable checkLeadConstituentMinPtForMcpJets{"checkLeadConstituentMinPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; + Configurable checkLeadConstituentPtForMcpJets{"checkLeadConstituentPtForMcpJets", false, "flag to choose whether particle level jets should have their lead track pt above leadingConstituentPtMin to be accepted; off by default, as leadingConstituentPtMin cut is only applied on MCD jets for the Pb-Pb analysis using pp MC anchored to Pb-Pb for the response matrix"}; Configurable pTHatMaxMCD{"pTHatMaxMCD", 999.0, "maximum fraction of hard scattering for jet acceptance in detector MC"}; Configurable pTHatMaxMCP{"pTHatMaxMCP", 999.0, "maximum fraction of hard scattering for jet acceptance in particle MC"}; Configurable pTHatExponent{"pTHatExponent", 6.0, "exponent of the event weight for the calculation of pTHat"}; + Configurable pTHatAbsoluteMin{"pTHatAbsoluteMin", -99.0f, "minimum value of pTHat"}; Configurable fLeadJetPtCut{"fLeadJetPtCut", false, "Flag for leading jet pT cut"}; Configurable leadJetPtMin{"leadJetPtMin", 20.0, "minimum leading jet pT cut"}; - Configurable trackEtaMin{"trackEtaMin", -0.9, "minimum eta acceptance for tracks"}; - Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum eta acceptance for tracks"}; - Configurable trackPtMin{"trackPtMin", 0.15, "minimum pT acceptance for tracks"}; - Configurable trackPtMax{"trackPtMax", 200.0, "maximum pT acceptance for tracks"}; - Configurable trackPtMinRhoPhi{"trackPtMinRhoPhi", 0.2, "minimum pT acceptance for tracks used in rho(phi) calculation"}; - Configurable trackPtMaxRhoPhi{"trackPtMaxRhoPhi", 5.0, "maximum pT acceptance for tracks used in rho(phi) calculation"}; - - Configurable jetEtaMin{"jetEtaMin", -0.7, "minimum jet pseudorapidity"}; - Configurable jetEtaMax{"jetEtaMax", 0.7, "maximum jet pseudorapidity"}; + struct : ConfigurableGroup { + Configurable trackEtaMin{"trackEtaMin", -0.9, "minimum eta acceptance for tracks"}; + Configurable trackEtaMax{"trackEtaMax", 0.9, "maximum eta acceptance for tracks"}; + Configurable trackPtMin{"trackPtMin", 0.15, "minimum pT acceptance for tracks"}; + Configurable trackPtMax{"trackPtMax", 200.0, "maximum pT acceptance for tracks"}; + Configurable trackDCAzMax{"trackDCAzMax", 2.0, "maximum DCAz for tracks"}; + Configurable cfgNTPCXrows{"cfgNTPCXrows", 70, "minimum number of crossed rows in the TPC for tracks"}; + Configurable cfgNTPCCls{"cfgNTPCCls", 50, "minimum number of clusters in the TPC for tracks"}; + Configurable cfgChi2PrTPCcls{"cfgChi2PrTPCcls", 4.0, "cut for chi2 per TPC cluster for tracks"}; + Configurable cfgChi2PrITScls{"cfgChi2PrITScls", 36, "cut for chi2 per ITS cluster for tracks"}; + Configurable dcaXYSigmaMax{"dcaXYSigmaMax", 7.0, "maximum DCAxy sigma significance for tracks"}; + } trackCuts; + + Configurable> trackPtRhoPhi{"trackPtRhoPhi", {0.2, 5.0}, "pT range for tracks used in rho(phi) calculation"}; + Configurable> cfgJetEta{"cfgJetEta", {-0.7, 0.7}, "jet eta range for analysis"}; Configurable eventSelections{"eventSelections", "sel8FullPbPb", "choose event selection"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; @@ -104,9 +121,12 @@ struct JetSpectraEseTask { Configurable> cfgOccupancyPtCut{"cfgOccupancyPtCut", {0, 100}, "pT cut"}; Configurable cfgrhoPhi{"cfgrhoPhi", true, "Flag for rho(phi)"}; + Configurable cfgRhoPhiPvalCriteria{"cfgRhoPhiPvalCriteria", true, "Use instead of rho(phi) when the rho(phi) fit p-value is below 0.01"}; + Configurable cfgRhoPhiPvalCut{"cfgRhoPhiPvalCut", 0.01, "p-value cut for rho(phi) fit"}; Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "total qvector number // look in Qvector table for this number"}; Configurable cfgnCorrLevel{"cfgnCorrLevel", 3, "QVector step: 0 = no corr, 1 = rect, 2 = twist, 3 = full"}; + Configurable cfgnredCorrLevel{"cfgnredCorrLevel", 2, "For the correlation of the reduced q vector, QVector step: 0 = no corr, 1 = rect, 2 = twist, 3 = full"}; Configurable cfgEPRefA{"cfgEPRefA", "FT0A", "EP reference A"}; Configurable cfgEPRefB{"cfgEPRefB", "TPCpos", "EP reference B"}; @@ -135,35 +155,60 @@ struct JetSpectraEseTask { Configurable numberEventsMixed{"numberEventsMixed", 5, "number of events mixed in ME process"}; ConfigurableAxis binsCentrality{"binsCentrality", {VARIABLE_WIDTH, 0.0, 10., 30., 50, 70., 100.}, "Mixing bins - centrality"}; ConfigurableAxis binsZVtx{"binsZVtx", {VARIABLE_WIDTH, -10.0f, -2.5f, 2.5f, 10.0f}, "Mixing bins - z-vertex"}; + ConfigurableAxis amplitudeAxis{"amplitudeAxis", {5000, 0, 5000}, "Amplitude"}; + + Configurable applyRCTSelections{"applyRCTSelections", true, "apply RCT selections"}; + Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events"}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; std::vector eventSelectionBits; int trackSelection{-1}; - static constexpr float Scaler = 100.0f; - static constexpr float Acceptance = 0.9f; + static constexpr float RScale = 100.0f; + static constexpr float EtaAcceptance = 0.9f; static constexpr float LowFT0Cut = 1e-8; - - Service ccdb; + static constexpr float LooseVertexZFilter = 999.0f; + + struct SystematicCuts { + float vertexZCut = 10.0f; + float trackDCAzMax = 2.0f; + float dcaXYSigmaMax = 7.0f; + float nTPCXrows = 70.0f; + float nTPCCls = 50.0f; + float chi2PrTPCcls = 4.0f; + float chi2PrITScls = 36.0f; + } systCuts; + + Service ccdb{}; struct Efficiency { - TH1D* hEff = nullptr; + TH1F* hEff = nullptr; + TH3F* h3Eff = nullptr; + TList* hEffList = nullptr; + TList* h3EffList = nullptr; bool isLoaded = false; + bool is3D = false; } cfg; - Filter trackCuts = (aod::jtrack::pt >= trackPtMin && aod::jtrack::pt < trackPtMax && aod::jtrack::eta > trackEtaMin && aod::jtrack::eta < trackEtaMax); - Filter jetCuts = aod::jet::pt > jetPtMin&& aod::jet::r == nround(jetR.node() * Scaler) && nabs(aod::jet::eta) < Acceptance - jetR; - Filter colFilter = nabs(aod::jcollision::posZ) < vertexZCut; - Filter mcCollisionFilter = nabs(aod::jmccollision::posZ) < vertexZCut; + Filter filtertrackCuts = (aod::jtrack::pt >= trackCuts.trackPtMin && aod::jtrack::pt < trackCuts.trackPtMax && aod::jtrack::eta > trackCuts.trackEtaMin && aod::jtrack::eta < trackCuts.trackEtaMax); + Filter jetCuts = aod::jet::pt > jetPtMin&& aod::jet::r == nround(jetR.node() * RScale) && nabs(aod::jet::eta) < EtaAcceptance - jetR; + Filter colFilter = nabs(aod::jcollision::posZ) < LooseVertexZFilter; + Filter mcCollisionFilter = nabs(aod::jmccollision::posZ) < LooseVertexZFilter; using ChargedMCDJets = soa::Filtered>; Preslice mcdjetsPerJCollision = o2::aod::jet::collisionId; Preslice tracksPerJCollision = o2::aod::jtrack::collisionId; Preslice mcdTracksPerJCollision = o2::aod::jtrack::collisionId; + using ETracks = soa::Join; + SliceCache cache; - using BinningType = ColumnBinningPolicy; - BinningType corrBinning{{binsZVtx, binsCentrality}, true}; - Service pdg; + using BinningTypeFT0C = ColumnBinningPolicy; + using BinningTypeFT0M = ColumnBinningPolicy; + using BinningTypeFT0CVariant1 = ColumnBinningPolicy; + BinningTypeFT0C corrBinningFT0C{{binsZVtx, binsCentrality}}; + BinningTypeFT0M corrBinningFT0M{{binsZVtx, binsCentrality}}; + BinningTypeFT0CVariant1 corrBinningFT0CVariant1{{binsZVtx, binsCentrality}}; + Service pdg{}; enum class DetID { FT0C, FT0A, @@ -173,9 +218,16 @@ struct JetSpectraEseTask { TPCneg, TPCall }; + enum class CentralityEstimator { FT0C, + FT0M, + FT0CVariant1 }; + + CentralityEstimator centralityEstimator = CentralityEstimator::FT0M; + struct EventPlane { float psi2; float psi3; + bool isValid; }; struct EventPlaneFiller { @@ -188,22 +240,124 @@ struct JetSpectraEseTask { kEventSel, kOccupancyCut, kCentCut, + kEPValid, kEse, kRhoLocal, kLeadJetCut }; - static constexpr EventPlaneFiller PsiFillerEP = {true, true}; - static constexpr EventPlaneFiller PsiFillerEse = {true, false}; - static constexpr EventPlaneFiller PsiFillerFalse = {false, false}; + static constexpr EventPlaneFiller PsiFillerEP{.psi = true, .hist = true}; + static constexpr EventPlaneFiller PsiFillerEse{.psi = true, .hist = false}; + static constexpr EventPlaneFiller PsiFillerFalse{.psi = false, .hist = false}; TRandom3* fRndm = new TRandom3(0); - static constexpr int NumSubSmpl = 10; + static constexpr int NumSubSmpl = 5; + static constexpr int NumSavedRhoFitEvents = 5; std::array, NumSubSmpl> hSameSub; + std::array, NumSubSmpl> hNtrigSub; + + void applySystematicPreset() + { + systCuts.vertexZCut = vertexZCut.value; + systCuts.trackDCAzMax = trackCuts.trackDCAzMax.value; + systCuts.dcaXYSigmaMax = trackCuts.dcaXYSigmaMax.value; + systCuts.nTPCXrows = trackCuts.cfgNTPCXrows.value; + systCuts.nTPCCls = trackCuts.cfgNTPCCls.value; + systCuts.chi2PrTPCcls = trackCuts.cfgChi2PrTPCcls.value; + systCuts.chi2PrITScls = trackCuts.cfgChi2PrITScls.value; + + const int flag = cfgSystFlag.value; + if (flag < 0) { + LOGF(info, "JetSpectraEseTask::applySystematicPreset() - using configurable cut values"); + return; + } + switch (flag) { + case 0: + break; + case 1: + systCuts.vertexZCut = 9.0f; + break; + case 2: + systCuts.vertexZCut = 8.0f; + break; + case 3: + systCuts.vertexZCut = 7.0f; + break; + case 4: + systCuts.trackDCAzMax = 1.0f; + break; + case 5: + systCuts.trackDCAzMax = 0.5f; + break; + case 6: + systCuts.dcaXYSigmaMax = 5.0f; + break; + case 7: + systCuts.nTPCCls = 60.0f; + break; + case 8: + systCuts.nTPCCls = 70.0f; + break; + case 9: + systCuts.chi2PrTPCcls = 2.5f; + break; + case 10: + systCuts.nTPCXrows = 80.0f; + break; + case 11: + systCuts.nTPCXrows = 90.0f; + break; + default: + LOGF(fatal, "JetSpectraEseTask::applySystematicPreset() - unknown cfgSystFlag=%d", flag); + } + + LOGF(info, "JetSpectraEseTask::applySystematicPreset() - cfgSystFlag=%d, vertexZ=%g, DCAz=%g, DCAxySigma=%g, TPC clusters=%g, TPC crossed rows=%g, chi2/TPC=%g, chi2/ITS=%g", + flag, systCuts.vertexZCut, systCuts.trackDCAzMax, systCuts.dcaXYSigmaMax, systCuts.nTPCCls, systCuts.nTPCXrows, systCuts.chi2PrTPCcls, systCuts.chi2PrITScls); + } + + template + bool isVertexSelected(TCollision const& collision) const + { + return std::abs(collision.posZ()) < systCuts.vertexZCut; + } + + template + float getCentrality(TCollision const& collision) const + { + switch (centralityEstimator) { + case CentralityEstimator::FT0C: + return collision.centFT0C(); + case CentralityEstimator::FT0M: + return collision.centFT0M(); + case CentralityEstimator::FT0CVariant1: + return collision.centFT0CVariant1(); + } + return -1.0f; + } void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + applySystematicPreset(); + + const std::string centralityName = cfgCentrality; + if (centralityName == "FT0C") { + centralityEstimator = CentralityEstimator::FT0C; + } else if (centralityName == "FT0M") { + centralityEstimator = CentralityEstimator::FT0M; + } else if (centralityName == "FT0CVariant1") { + centralityEstimator = CentralityEstimator::FT0CVariant1; + } else { + LOGF(fatal, "JetSpectraEseTask::init() - invalid cfgCentrality='%s'; expected FT0C, FT0M, or FT0CVariant1", centralityName.c_str()); + } + + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); LOGF(info, "jetSpectraEse::init()"); @@ -226,11 +380,13 @@ struct JetSpectraEseTask { registry.add("eventQA/before/hVtxZ", ";z_{vtx} (cm);entries", {HistType::kTH1F, {{vertexZAxis}}}); registry.add("eventQA/after/hVtxZ", ";z_{vtx} (cm);entries", {HistType::kTH1F, {{vertexZAxis}}}); + registry.add("eventQA/hRhoPhiCheck", "event status;event status;entries", {HistType::kTH1F, {{2, 0.0, 2.0}}}); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kFilteredInputEv, "Input filtered event"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kEventSel, "Event selection"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kOccupancyCut, "Occupancy cut"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kCentCut, "Centrality cut"); + registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kEPValid, "EP valid"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kEse, "ESE available"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kRhoLocal, "rho(#phi) available"); registry.get(HIST("eventQA/hEventCounter"))->GetXaxis()->SetBinLabel(kLeadJetCut, "leading jet pT cut"); @@ -239,6 +395,10 @@ struct JetSpectraEseTask { registry.get(HIST("trackQA/hRhoTrackCounter"))->GetXaxis()->SetBinLabel(3, "Tracks for rho(phi)"); registry.add("trackQA/hPhiPtsum", "jet sumpt;sum p_{T};entries", {HistType::kTH1F, {{40, 0., o2::constants::math::TwoPI}}}); + for (int i = 0; i < NumSavedRhoFitEvents; ++i) { + const auto eventName = fmt::format("rhoPhiFitEvents/event_{}", i); + registry.add(eventName, "event;#varphi;#Sigma #it{p}_{T} (GeV/#it{c})", HistType::kTH1F, {{1, 0., o2::constants::math::TwoPI}}); + } registry.add("eventQA/hfitPar0", "", {HistType::kTH2F, {{centAxis}, {fitAxis0}}}); registry.add("eventQA/hfitPar1", "", {HistType::kTH2F, {{centAxis}, {fitAxis13}}}); registry.add("eventQA/hfitPar2", "", {HistType::kTH2F, {{centAxis}, {fitAxis24}}}); @@ -269,6 +429,8 @@ struct JetSpectraEseTask { for (int i = 0; i < NumSubSmpl; ++i) { std::string n = fmt::format("subsamples/thn_jethad_corr_same_subsample{}", i); hSameSub[i] = registry.add(n, "same;...", o2::framework::HistType::kTHnSparseF, axes); + std::string n2 = fmt::format("subsamples/hNtrig_subsample{}", i); + hNtrigSub[i] = registry.add(n2, "", o2::framework::HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {dPhiAxis}, {eseAxis}}); } registry.add("hNtrig", "", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {dPhiAxis}, {eseAxis}}}); @@ -278,6 +440,18 @@ struct JetSpectraEseTask { registry.add("trackQA/after/hTrackPt", "", {HistType::kTH2F, {{centAxis}, {trackPtAxis}}}); registry.add("trackQA/after/hTrackEta", "", {HistType::kTH2F, {{centAxis}, {etaAxis}}}); registry.add("trackQA/after/hTrackPhi", "", {HistType::kTH2F, {{centAxis}, {phiAxis}}}); + registry.add("trackQA/before/hNClsCrossed", "", {HistType::kTH1F, {{100, 40, 140}}}); + registry.add("trackQA/after/hNClsCrossed", "", {HistType::kTH1F, {{100, 40, 140}}}); + registry.add("trackQA/before/hNClsMin", "", {HistType::kTH1F, {{100, 40, 140}}}); + registry.add("trackQA/after/hNClsMin", "", {HistType::kTH1F, {{100, 40, 140}}}); + registry.add("trackQA/before/hDCAz", "", {HistType::kTH1F, {{50, -3, 3}}}); + registry.add("trackQA/after/hDCAz", "", {HistType::kTH1F, {{50, -3, 3}}}); + registry.add("trackQA/before/hDCAxy", "", {HistType::kTH1F, {{50, -1, 1}}}); + registry.add("trackQA/after/hDCAxy", "", {HistType::kTH1F, {{50, -1, 1}}}); + registry.add("trackQA/before/hChi2TPC", "", {HistType::kTH1F, {{50, 0, 6}}}); + registry.add("trackQA/after/hChi2TPC", "", {HistType::kTH1F, {{50, 0, 6}}}); + registry.add("trackQA/before/hChi2ITS", "", {HistType::kTH1F, {{50, 0, 38}}}); + registry.add("trackQA/after/hChi2ITS", "", {HistType::kTH1F, {{50, 0, 38}}}); } if (doprocessESEDataChargedMixed) { registry.add("eventQA/hEventCounterMixed", "event status;event status;entries", {HistType::kTH1F, {{20, 0.0, 20.0}}}); @@ -285,6 +459,7 @@ struct JetSpectraEseTask { registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kEventSel, "Event selection"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kOccupancyCut, "Occupancy cut"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kCentCut, "Centrality cut"); + registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kEPValid, "EP valid"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kEse, "ESE available"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kRhoLocal, "rho(#phi) available"); registry.get(HIST("eventQA/hEventCounterMixed"))->GetXaxis()->SetBinLabel(kLeadJetCut, "leading jet pT cut"); @@ -317,20 +492,43 @@ struct JetSpectraEseTask { if (doprocessESEEPData) { LOGF(info, "JetSpectraEseTask::init() - Event Plane Process"); registry.add("eventQA/hPsi2FT0C", ";Centrality; #Psi_{2}", {HistType::kTH2F, {{centAxis}, {150, -2.5, 2.5}}}); - registry.addClone("eventQA/hPsi2FT0C", "hPsi2FT0A"); - registry.addClone("eventQA/hPsi2FT0C", "hPsi2FV0A"); - registry.addClone("eventQA/hPsi2FT0C", "hPsi2TPCpos"); - registry.addClone("eventQA/hPsi2FT0C", "hPsi2TPCneg"); - registry.add("eventQA/hCosPsi2AmC", ";Centrality;cos(2(#Psi_{2}^{A}-#Psi_{2}^{B}));#it{q}_{2}", {HistType::kTH3F, {{centAxis}, {cosAxis}, {eseAxis}}}); - registry.addClone("eventQA/hCosPsi2AmC", "hCosPsi2AmB"); - registry.addClone("eventQA/hCosPsi2AmC", "hCosPsi2BmC"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi2FT0A"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi2FV0A"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi2TPCpos"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi2TPCneg"); + + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi3FT0C"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi3FT0A"); + + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi4FT0C"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi4FT0A"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi4FV0A"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi4TPCpos"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hPsi4TPCneg"); + + registry.add("eventQA/hCos2Psi2AmC", ";Centrality;cos(2(#Psi_{2}^{A}-#Psi_{2}^{B}));#it{q}_{2}", {HistType::kTH3F, {{centAxis}, {cosAxis}, {eseAxis}}}); + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos2Psi2AmB"); + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos2Psi2BmC"); + + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos4Psi2AmC"); + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos4Psi2AmB"); + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos4Psi2BmC"); + + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos4Psi4AmC"); + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos4Psi4AmB"); + registry.addClone("eventQA/hCos2Psi2AmC", "eventQA/hCos4Psi4BmC"); + registry.add("eventQA/hQvecUncorV2", ";Centrality;Q_x;Q_y", {HistType::kTH3F, {{centAxis}, {qvecAxis}, {qvecAxis}}}); - registry.addClone("eventQA/hQvecUncorV2", "hQvecRectrV2"); - registry.addClone("eventQA/hQvecUncorV2", "hQvecTwistV2"); - registry.addClone("eventQA/hQvecUncorV2", "hQvecFinalV2"); - registry.addClone("eventQA/hPsi2FT0C", "hEPUncorV2"); - registry.addClone("eventQA/hPsi2FT0C", "hEPRectrV2"); - registry.addClone("eventQA/hPsi2FT0C", "hEPTwistV2"); + registry.addClone("eventQA/hQvecUncorV2", "eventQA/hQvecRectrV2"); + registry.addClone("eventQA/hQvecUncorV2", "eventQA/hQvecTwistV2"); + registry.addClone("eventQA/hQvecUncorV2", "eventQA/hQvecFinalV2"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hEPUncorV2"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hEPRectrV2"); + registry.addClone("eventQA/hPsi2FT0C", "eventQA/hEPTwistV2"); + + registry.add("eventQA/h3Centq2FT0Cq2FT0A", ";Centrality;#it{q}_{2}^{FT0C};#it{q}_{2}^{FT0A}", {HistType::kTH3F, {{centAxis}, {250, 0, 35}, {250, 0, 35}}}); + + registry.add("eventQA/h2FT0AchannelAmplitude", ";channelID;amplitude", {HistType::kTH2F, {{100, 0, 100}, {amplitudeAxis}}}); } if (doprocessESEBackground) { LOGF(info, "JetSpectraEseTask::init() - Background Process"); @@ -339,10 +537,17 @@ struct JetSpectraEseTask { registry.add("hCentRhoRandomConewoLeadingJet", "; centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTHnSparseF, {{centAxis}, {800, -400.0, 400.0}, {dPhiAxis}, {eseAxis}}}); registry.add("hCentRhoRandomConeRndTrackDirwoOneLeadingJet", "; centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTHnSparseF, {{centAxis}, {800, -400.0, 400.0}, {dPhiAxis}, {eseAxis}}}); registry.add("hCentRhoRandomConeRndTrackDirwoTwoLeadingJet", "; centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho} (GeV/c);", {HistType::kTHnSparseF, {{centAxis}, {800, -400.0, 400.0}, {dPhiAxis}, {eseAxis}}}); + + registry.add("h3CentdeltapTRndmConePhi_localrhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {dPhiAxis}}}); + registry.add("h3CentdeltapTRndmConePhi_rhovsphi", "centrality; #it{p}_{T,random cone} - #it{area, random cone} * #it{rho}; #Delta#varphi_{jet}", {HistType::kTH3F, {{100, 0.0, 100.0}, {400, -200.0, 200.0}, {dPhiAxis}}}); } + // Normalize MC spectra with hUsedEventWeights bin 1 content, not GetEntries(). if (doprocessMCParticleLevel) { LOGF(info, "JetSpectraEseTask::init() - MC Particle level"); registry.add("mcp/hEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcp/hUsedEventWeights", "Used MC collisions;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcp/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcp/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcp/hCentralitySel", ";centr;entries", {HistType::kTH1F, {{centAxis}}}); // registry.add("mcp/hJetSparse", ";Centrality;#it{p}_{T,jet part}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); registry.add("mcp/hJetSparse", ";Centrality;#it{p}_{T,jet part}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); @@ -360,6 +565,9 @@ struct JetSpectraEseTask { if (doprocessMCDetectorLevel) { LOGF(info, "JetSpectraEseTask::init() - MC Detector level"); registry.add("mcd/hEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcd/hUsedEventWeights", "Used reconstructed collisions;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcd/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcd/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcd/hCentralitySel", ";centr;entries", {HistType::kTH1F, {{centAxis}}}); // registry.add("mcd/hJetPt", "particle level jet pT;#it{p}_{T,jet part} (GeV/#it{c});entries", {HistType::kTH1F, {{jetPtAxis}}}); // registry.add("mcd/hJetSparse", ";Centrality;#it{p}_{T,jet det}; #eta; #phi", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {etaAxis}, {phiAxis}}}); @@ -382,6 +590,9 @@ struct JetSpectraEseTask { registry.add("mcm/hMCEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); registry.add("mcm/hMCDMatchedEventCounter", "event status;event status;entries", {HistType::kTH1F, {{10, 0.0, 10.0}}}); + registry.add("mcm/hUsedEventWeights", "Used reconstructed collisions with exactly one reconstructed collision per MC collision;Used events;Sum of event weights", {HistType::kTH1D, {{1, 0.0, 1.0}}}); + registry.get(HIST("mcm/hUsedEventWeights"))->Sumw2(); + registry.get(HIST("mcm/hUsedEventWeights"))->GetXaxis()->SetBinLabel(1, "Sum of weights of used events"); registry.add("mcm/hCentralityAnalyzed", ";Centrality;entries", {HistType::kTH1F, {{centAxis}}}); registry.add("mcm/hMatchedJetsPtDelta", "#it{p}_{T,jet part}; det - part", {HistType::kTH2F, {{jetPtAxis}, {100, -20., 20.0}}}); @@ -409,17 +620,28 @@ struct JetSpectraEseTask { registry.add("hTrackPt", "track pT;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}, {eseAxis}, {occAxis}}}); registry.add("hTrackEta", "track #eta;#eta_{track};entries", {HistType::kTH3F, {{centAxis}, {etaAxis}, {occAxis}}}); registry.add("hTrackPhi", "track #phi;#phi_{track};entries", {HistType::kTH3F, {{centAxis}, {phiAxis}, {occAxis}}}); - registry.add("hOccupancy", "Occupancy;Occupancy;entries", {HistType::kTH1F, {{occAxis}}}); + registry.add("hOccupancy", "Occupancy;Occupancy;entries", {HistType::kTH2F, {{centAxis}, {occAxis}}}); + registry.add("hOccupancyEv", "Occupancy;Occupancy;entries", {HistType::kTH2F, {{centAxis}, {occAxis}}}); registry.add("hPsiOccupancy", "Occupancy;#Psi_{2};entries", {HistType::kTH3F, {{centAxis}, {150, -2.5, 2.5}, {occAxis}}}); + registry.add("hSTrackPtPhiEtaOcc", ";cent;jpt;phi;eta;occ", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}, {phiAxis}, {etaAxis}, {occAxis}}}); + registry.add("hSJetPtPhiEtaOcc", ";cent;jpt;phi;eta;occ", {HistType::kTHnSparseF, {{centAxis}, {jetPtAxis}, {phiAxis}, {etaAxis}, {occAxis}}}); + } + if (doprocessJetAlignment) { + LOGF(info, "JetSpectraEseTask::init() - Jet Alignment Process"); + registry.add("hCentq2", ";cent;#it{q}_{2};occ", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}}}); + registry.add("hTrackPtPhiEta", ";cent;p_{T,track};#phi;#eta", {HistType::kTHnSparseF, {{centAxis}, {100, 0, 100}, {phiAxis}, {etaAxis}}}); + registry.add("hJetPtPhiEta", ";cent;p_{T,jet};jet #phi;jet #eta", {HistType::kTHnSparseF, {{centAxis}, {250, -50, 200}, {phiAxis}, {etaAxis}}}); } if (doprocessMCGenTrack) { LOGF(info, "JetSpectraEseTask::init() - MCGen track"); + registry.add("mcgen/h3TrackPtGen", ";#it{p}_{T,track}; vtxZ; #eta", {HistType::kTH3F, {{assocTrackPt}, {vertexZAxis}, {etaAxis}}}); registry.add("mcgen/hTrackPtGen", "", {HistType::kTH1F, {{assocTrackPt}}}); registry.add("mcgen/hTrackEtaGen", "", {HistType::kTH1F, {{etaAxis}}}); registry.add("mcgen/hTrackPhiGen", "", {HistType::kTH1F, {{phiAxis}}}); } if (doprocessMCRecoTrack) { LOGF(info, "JetSpectraEseTask::init() - MCRec track"); + registry.add("mcrec/h3TrackPtReco", ";#it{p}_{T,track}; vtxZ; #eta", {HistType::kTH3F, {{assocTrackPt}, {vertexZAxis}, {etaAxis}}}); registry.add("mcrec/hTrackPtReco", "", {HistType::kTH1F, {{assocTrackPt}}}); registry.add("mcrec/hTrackEtaReco", "", {HistType::kTH1F, {{etaAxis}}}); registry.add("mcrec/hTrackPhiReco", "", {HistType::kTH1F, {{phiAxis}}}); @@ -433,27 +655,49 @@ struct JetSpectraEseTask { if (cfg.isLoaded) { return; } - if (!cfgEfficiency.value.empty()) { - cfg.hEff = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + const auto efficiencyName = fmt::format("efficiency_{}", std::max(cfgSystFlag.value, 0)); + if (!efficiencyFiles.cfgEfficiency3D.value.empty()) { + cfg.h3EffList = ccdb->getForTimeStamp(efficiencyFiles.cfgEfficiency3D, timestamp); + if (cfg.h3EffList == nullptr) { + LOGF(fatal, "Could not load 3D track efficiency list from %s", efficiencyFiles.cfgEfficiency3D.value.c_str()); + } + cfg.h3Eff = dynamic_cast(cfg.h3EffList->FindObject(efficiencyName.c_str())); + if (cfg.h3Eff == nullptr) { + LOGF(fatal, "Could not find %s as TH3F in 3D track efficiency list %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency3D.value.c_str()); + } + LOGF(info, "Loaded 3D tracking efficiency %s from %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency3D.value.c_str()); + cfg.is3D = true; + } else if (!efficiencyFiles.cfgEfficiency.value.empty()) { + cfg.hEffList = ccdb->getForTimeStamp(efficiencyFiles.cfgEfficiency, timestamp); + if (cfg.hEffList == nullptr) { + LOGF(fatal, "Could not load track efficiency list from %s", efficiencyFiles.cfgEfficiency.value.c_str()); + } + cfg.hEff = dynamic_cast(cfg.hEffList->FindObject(efficiencyName.c_str())); if (cfg.hEff == nullptr) { - LOGF(fatal, "Could not load track efficiency from %s", cfgEfficiency.value.c_str()); + LOGF(fatal, "Could not find %s as TH1F in track efficiency list %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded tracking efficiency from %s (%p)", cfgEfficiency.value.c_str(), (void*)cfg.hEff); + LOGF(info, "Loaded tracking efficiency %s from %s", efficiencyName.c_str(), efficiencyFiles.cfgEfficiency.value.c_str()); } cfg.isLoaded = true; - return; } template - double getEfficiency(TTrack track) + double getEfficiency(const TTrack& track, auto vtxZ) { double eff{1.0}; - if (cfg.hEff) - eff = cfg.hEff->GetBinContent(cfg.hEff->FindBin(track.pt())); - if (eff == 0) + if (cfg.is3D) { + if (cfg.h3Eff) { + eff = cfg.h3Eff->GetBinContent(cfg.h3Eff->FindBin(track.pt(), vtxZ, track.eta())); + } + } else { + if (cfg.hEff) { + eff = cfg.hEff->GetBinContent(cfg.hEff->FindBin(track.pt())); + } + } + if (eff == 0) { return -1.; - else - return 1. / eff; + } + return 1. / eff; } template @@ -468,26 +712,34 @@ struct JetSpectraEseTask { TTracks const& tracks) { - auto centrality = cfgCentVariant ? collision.centFT0CVariant1() : collision.centFT0M(); - if (cfgSelCentrality && !isCentralitySelected(centrality)) + auto centrality = getCentrality(collision); + if (cfgSelCentrality && !isCentralitySelected(centrality)) { return; + } registry.fill(HIST("eventQA/hEventCounter"), kCentCut); const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } + registry.fill(HIST("eventQA/hEventCounter"), kEPValid); const auto qPerc{collision.qPERCFT0C()}; - if (qPerc[0] < 0) + if (qPerc[0] < 0) { return; + } registry.fill(HIST("eventQA/hEventCounter"), kEse); std::unique_ptr rhoFit{nullptr}; if (cfgrhoPhi) { rhoFit = fitRho(collision, psi, tracks, jets); - if (!rhoFit) + if (!rhoFit) { return; + } } registry.fill(HIST("eventQA/hEventCounter"), kRhoLocal); if (fLeadJetPtCut) { - if (!isAcceptedLeadingJet(collision, jets, centrality)) + if (!isAcceptedLeadingJet(collision, jets, centrality)) { return; + } } registry.fill(HIST("eventQA/hEventCounter"), kLeadJetCut); registry.fill(HIST("eventQA/after/hVtxZ"), collision.posZ()); @@ -498,9 +750,11 @@ struct JetSpectraEseTask { auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), collision.rho()) * j.area(); }; for (const auto& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; - if (!isAcceptedJet(jet)) { + } + // if (!isAcceptedJet(jet)) { + if (!isAcceptedJet>(jet)) { continue; } auto vCorr = corr(collision, jet); @@ -525,12 +779,18 @@ struct JetSpectraEseTask { } registry.fill(HIST("hNtrig"), centrality, vCorrL, dPhi, qPerc[0]); + hNtrigSub[lRndInd]->Fill(centrality, vCorrL, dPhi, qPerc[0]); for (const auto& track : tracks) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; - double weff = getEfficiency(track); - if (weff < 0) + } + if (!isTrackSelected(track.template track_as())) { + continue; + } + double weff = getEfficiency(track, collision.posZ()); + if (weff < 0) { continue; + } auto deta = track.eta() - jet.eta(); auto dphi = RecoDecay::constrainAngle(track.phi() - jet.phi(), -o2::constants::math::PIHalf); registry.fill(HIST("thn_jethad_corr_same"), centrality, vCorrL, track.pt(), deta, dphi, dPhi, qPerc[0], weff); @@ -538,28 +798,47 @@ struct JetSpectraEseTask { } } for (const auto& track : tracks) { - double weff = getEfficiency(track); - if (weff < 0) + double weff = getEfficiency(track, collision.posZ()); + auto trk = track.template track_as(); + if (weff < 0) { continue; + } registry.fill(HIST("trackQA/before/hTrackPt"), centrality, track.pt(), weff); registry.fill(HIST("trackQA/before/hTrackEta"), centrality, track.eta()); registry.fill(HIST("trackQA/before/hTrackPhi"), centrality, track.phi()); - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + registry.fill(HIST("trackQA/before/hNClsCrossed"), trk.tpcNClsCrossedRows()); + registry.fill(HIST("trackQA/before/hNClsMin"), trk.tpcNClsFound()); + registry.fill(HIST("trackQA/before/hDCAz"), trk.dcaZ()); + registry.fill(HIST("trackQA/before/hDCAxy"), trk.dcaXY()); + registry.fill(HIST("trackQA/before/hChi2TPC"), trk.tpcChi2NCl()); + registry.fill(HIST("trackQA/before/hChi2ITS"), trk.itsChi2NCl()); + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + continue; + } + if (!isTrackSelected(trk)) { continue; + } registry.fill(HIST("trackQA/after/hTrackPt"), centrality, track.pt(), weff); registry.fill(HIST("trackQA/after/hTrackEta"), centrality, track.eta()); registry.fill(HIST("trackQA/after/hTrackPhi"), centrality, track.phi()); + registry.fill(HIST("trackQA/after/hNClsCrossed"), trk.tpcNClsCrossedRows()); + registry.fill(HIST("trackQA/after/hNClsMin"), trk.tpcNClsFound()); + registry.fill(HIST("trackQA/after/hDCAz"), trk.dcaZ()); + registry.fill(HIST("trackQA/after/hDCAxy"), trk.dcaXY()); + registry.fill(HIST("trackQA/after/hChi2TPC"), trk.tpcChi2NCl()); + registry.fill(HIST("trackQA/after/hChi2ITS"), trk.itsChi2NCl()); registry.fill(HIST("h3CenttrPhiPsi2"), centrality, RecoDecay::constrainAngle(track.phi() - psi.psi2, -o2::constants::math::PI), qPerc[0]); } } - template - void jetMixed(TCollisions const& collisions, - TJets const& jets, - TTracks const& tracks) + template + void jetMixedWithBinning(TBinning const& corrBinning, + TCollisions const& collisions, + TJets const& jets, + TTracks const& tracks) { auto tracksTuple = std::make_tuple(jets, tracks); - Pair pairData{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; + Pair pairData{corrBinning, numberEventsMixed, -1, collisions, tracksTuple, &cache}; for (const auto& [c1, jets1, c2, tracks2] : pairData) { auto bc = c2.template bc_as(); @@ -568,40 +847,59 @@ struct JetSpectraEseTask { registry.fill(HIST("eventQA/before/hVtxZMixed"), c1.posZ()); registry.fill(HIST("eventQA/before/hVtxZMixed2"), c2.posZ()); registry.fill(HIST("eventQA/hEventCounterMixed"), kFilteredInputEv); - if (!jetderiveddatautilities::selectCollision(c1, eventSelectionBits)) + if (!isVertexSelected(c1)) { + continue; + } + if (!isVertexSelected(c2)) { continue; - if (!jetderiveddatautilities::selectCollision(c2, eventSelectionBits)) + } + if (!jetderiveddatautilities::selectCollision(c1, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { continue; + } + if (!jetderiveddatautilities::selectCollision(c2, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + continue; + } registry.fill(HIST("eventQA/hEventCounterMixed"), kEventSel); - if (cfgEvSelOccupancy && !isOccupancyAccepted(c1)) + if (cfgEvSelOccupancy && !isOccupancyAccepted(c1)) { continue; - if (cfgEvSelOccupancy && !isOccupancyAccepted(c2)) + } + if (cfgEvSelOccupancy && !isOccupancyAccepted(c2)) { continue; + } registry.fill(HIST("eventQA/hEventCounterMixed"), kOccupancyCut); - auto centrality = cfgCentVariant ? c1.centFT0CVariant1() : c1.centFT0M(); - if (cfgSelCentrality && !isCentralitySelected(centrality)) + auto centrality = getCentrality(c1); + if (cfgSelCentrality && !isCentralitySelected(centrality)) { continue; - auto centrality2 = cfgCentVariant ? c2.centFT0CVariant1() : c2.centFT0M(); - if (cfgSelCentrality && !isCentralitySelected(centrality2)) + } + auto centrality2 = getCentrality(c2); + if (cfgSelCentrality && !isCentralitySelected(centrality2)) { continue; + } registry.fill(HIST("eventQA/hEventCounterMixed"), kCentCut); const auto psi{procEP(c1)}; + if (!psi.isValid) { + continue; + } + registry.fill(HIST("eventQA/hEventCounterMixed"), kEPValid); const auto qPerc{c1.qPERCFT0C()}; - if (qPerc[0] < 0) + if (qPerc[0] < 0) { continue; + } registry.fill(HIST("eventQA/hEventCounterMixed"), kEse); std::unique_ptr rhoFit{nullptr}; if (cfgrhoPhi) { rhoFit = fitRho(c1, psi, c1Tracks, jets1); - if (!rhoFit) + if (!rhoFit) { continue; + } } registry.fill(HIST("eventQA/hEventCounterMixed"), kRhoLocal); if (fLeadJetPtCut) { - if (!isAcceptedLeadingJet(c1, jets, centrality)) - return; + if (!isAcceptedLeadingJet(c1, jets1, centrality)) { + continue; + } } registry.fill(HIST("eventQA/hEventCounterMixed"), kLeadJetCut); registry.fill(HIST("eventQA/after/hVtxZMixed"), c1.posZ()); @@ -613,9 +911,10 @@ struct JetSpectraEseTask { auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), c1.rho()) * j.area(); }; for (const auto& jet : jets1) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; - if (!isAcceptedJet(jet)) { + } + if (!isAcceptedJet>(jet)) { continue; } auto vCorr = corr(c1, jet); @@ -624,11 +923,16 @@ struct JetSpectraEseTask { registry.fill(HIST("hNtrigMixed"), centrality, vCorrL, dPhi, qPerc[0]); for (const auto& track : tracks2) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + continue; + } + if (!isTrackSelected(track.template track_as())) { continue; - double weff = getEfficiency(track); - if (weff < 0) + } + double weff = getEfficiency(track, c2.posZ()); + if (weff < 0) { continue; + } auto deta = track.eta() - jet.eta(); auto dphi = RecoDecay::constrainAngle(track.phi() - jet.phi(), -o2::constants::math::PIHalf); registry.fill(HIST("thn_jethad_corr_mixed"), centrality, vCorrL, track.pt(), deta, dphi, dPhi, qPerc[0], weff); @@ -636,14 +940,19 @@ struct JetSpectraEseTask { } for (const auto& track : tracks2) { - double weff = getEfficiency(track); - if (weff < 0) + double weff = getEfficiency(track, c2.posZ()); + if (weff < 0) { continue; + } registry.fill(HIST("trackQA/before/hTrackPtMixed"), centrality, track.pt(), weff); registry.fill(HIST("trackQA/before/hTrackEtaMixed"), centrality, track.eta()); registry.fill(HIST("trackQA/before/hTrackPhiMixed"), centrality, track.phi()); - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; + } + if (!isTrackSelected(track.template track_as())) { + continue; + } registry.fill(HIST("trackQA/after/hTrackPtMixed"), centrality, track.pt(), weff); registry.fill(HIST("trackQA/after/hTrackEtaMixed"), centrality, track.eta()); registry.fill(HIST("trackQA/after/hTrackPhiMixed"), centrality, track.phi()); @@ -651,18 +960,41 @@ struct JetSpectraEseTask { } } + template + void jetMixed(TCollisions const& collisions, + TJets const& jets, + TTracks const& tracks) + { + switch (centralityEstimator) { + case CentralityEstimator::FT0C: + jetMixedWithBinning(corrBinningFT0C, collisions, jets, tracks); + break; + case CentralityEstimator::FT0M: + jetMixedWithBinning(corrBinningFT0M, collisions, jets, tracks); + break; + case CentralityEstimator::FT0CVariant1: + jetMixedWithBinning(corrBinningFT0CVariant1, collisions, jets, tracks); + break; + } + } + void processESEDataCharged(soa::Join::iterator const& collision, soa::Filtered> const& jets, - aod::JetTracks const& tracks, aod::JBCs const&) + soa::Join const& tracks, aod::JBCs const&, ETracks const&) { registry.fill(HIST("eventQA/hEventCounter"), kFilteredInputEv); registry.fill(HIST("eventQA/before/hVtxZ"), collision.posZ()); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) + if (!isVertexSelected(collision)) { + return; + } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { return; + } registry.fill(HIST("eventQA/hEventCounter"), kEventSel); - if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) + if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) { return; + } registry.fill(HIST("eventQA/hEventCounter"), kOccupancyCut); auto bc = collision.bc_as(); @@ -673,24 +1005,41 @@ struct JetSpectraEseTask { void processESEDataChargedMixed(soa::Join const& collisions, soa::Filtered> const& jets, - aod::JetTracks const& tracks, aod::JBCs const&) + soa::Join const& tracks, aod::JBCs const&, ETracks const&) { jetMixed(collisions, jets, tracks); } PROCESS_SWITCH(JetSpectraEseTask, processESEDataChargedMixed, "process ese mixed collisions", false); - void processESEEPData(soa::Join::iterator const& collision, + using OgCol = soa::Join; + void processESEEPData(soa::Join::iterator const& collision, soa::Filtered const&, - aod::JetTracks const&) + aod::JetTracks const&, + OgCol const&, + aod::FT0s const&) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) + if (!isVertexSelected(collision)) { return; + } - if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { return; + } + + if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) { + return; + } [[maybe_unused]] const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } + detCorrelation(collision); + auto originalCollision = + collision.collision_as(); + + ft0Amplitude(collision, originalCollision); } PROCESS_SWITCH(JetSpectraEseTask, processESEEPData, "process ese collisions for filling EP and EPR", false); @@ -703,34 +1052,106 @@ struct JetSpectraEseTask { PROCESS_SWITCH(JetSpectraEseTask, processESEBackground, "process random cones with event plane and ESE", false); void processESEOccupancy(soa::Join::iterator const& collision, - soa::Join const& tracks) + soa::Join const& tracks, soa::Filtered> const& jets) { float count{0.5}; registry.fill(HIST("hEventCounterOcc"), count++); const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } const auto qPerc{collision.qPERCFT0C()}; auto occupancy{collision.trackOccupancyInTimeRange()}; - registry.fill(HIST("hPsiOccupancy"), collision.centFT0M(), psi.psi2, occupancy); - registry.fill(HIST("hOccupancy"), occupancy); + registry.fill(HIST("hPsiOccupancy"), getCentrality(collision), psi.psi2, occupancy); + registry.fill(HIST("hOccupancy"), getCentrality(collision), occupancy); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) + if (!isVertexSelected(collision)) { return; + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + return; + } registry.fill(HIST("hEventCounterOcc"), count++); + registry.fill(HIST("hOccupancyEv"), getCentrality(collision), occupancy); for (auto const& track : tracks) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; + } - registry.fill(HIST("hTrackPt"), collision.centFT0M(), track.pt(), qPerc[0], occupancy); - if (track.pt() < cfgOccupancyPtCut->at(0) || track.pt() > cfgOccupancyPtCut->at(1)) + registry.fill(HIST("hTrackPt"), getCentrality(collision), track.pt(), qPerc[0], occupancy); + registry.fill(HIST("hSTrackPtPhiEtaOcc"), getCentrality(collision), track.pt(), track.phi(), track.eta(), occupancy); + if (track.pt() < cfgOccupancyPtCut->at(0) || track.pt() > cfgOccupancyPtCut->at(1)) { continue; - registry.fill(HIST("hTrackEta"), collision.centFT0M(), track.eta(), occupancy); - registry.fill(HIST("hTrackPhi"), collision.centFT0M(), track.phi(), occupancy); + } + registry.fill(HIST("hTrackEta"), getCentrality(collision), track.eta(), occupancy); + registry.fill(HIST("hTrackPhi"), getCentrality(collision), track.phi(), occupancy); + } + for (const auto& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { + continue; + } + if (!isAcceptedJet(jet)) { + continue; + } + registry.fill(HIST("hSJetPtPhiEtaOcc"), getCentrality(collision), jet.pt(), jet.phi(), jet.eta(), occupancy); } } PROCESS_SWITCH(JetSpectraEseTask, processESEOccupancy, "process occupancy", false); + void processJetAlignment(soa::Join::iterator const& collision, + soa::Join const& tracks, soa::Filtered> const& jets) + { + const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } + const auto qPerc{collision.qPERCFT0C()}; + if (qPerc[0] < 0) { + return; + } + + std::unique_ptr rhoFit{nullptr}; + if (cfgrhoPhi) { + rhoFit = fitRho(collision, psi, tracks, jets); + if (!rhoFit) { + return; + } + } + + if (!isVertexSelected(collision)) { + return; + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + return; + } + + registry.fill(HIST("hCentq2"), getCentrality(collision), qPerc[0]); + auto corrL = [&](const auto& j) { return j.pt() - evalRho(rhoFit.get(), jetR, j.phi(), collision.rho()) * j.area(); }; + + for (const auto& jet : jets) { + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { + continue; + } + if (!isAcceptedJet>(jet)) { + continue; + } + auto vCorrL = cfgrhoPhi ? corrL(jet) : corr(collision, jet); + registry.fill(HIST("hJetPtPhiEta"), getCentrality(collision), vCorrL, jet.phi(), jet.eta()); + } + + for (auto const& track : tracks) { + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { + continue; + } + registry.fill(HIST("hTrackPtPhiEta"), getCentrality(collision), track.pt(), track.phi(), track.eta()); + } + } + PROCESS_SWITCH(JetSpectraEseTask, processJetAlignment, "process jet+track spectra for QA checking alignment issues", false); + void processMCParticleLevel(soa::Filtered>::iterator const& mcCollision, soa::SmallGroups const& collisions, soa::Filtered> const& jets, @@ -747,55 +1168,76 @@ struct JetSpectraEseTask { } registry.fill(HIST("mcp/hEventCounter"), counter++); - auto centrality{-1}; + float centrality{-1.0f}; bool fOccupancy = true; bool eventSel = true; for (const auto& col : collisions) { if (cfgisPbPb) - centrality = col.centFT0M(); + centrality = getCentrality(col); if (cfgEvSelOccupancy && !isOccupancyAccepted(col)) fOccupancy = false; - if (!jetderiveddatautilities::selectCollision(col, eventSelectionBits)) + if (!jetderiveddatautilities::selectCollision(col, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) eventSel = false; } - if (cfgEvSelOccupancy && !fOccupancy) + if (cfgEvSelOccupancy && !fOccupancy) { return; + } registry.fill(HIST("mcp/hEventCounter"), counter++); - if (!(std::abs(mcCollision.posZ()) < vertexZCut)) { + if (!(std::abs(mcCollision.posZ()) < systCuts.vertexZCut)) { return; } registry.fill(HIST("mcp/hEventCounter"), counter++); - if (!eventSel) + if (!eventSel) { return; + } registry.fill(HIST("mcp/hEventCounter"), counter++); registry.fill(HIST("mcp/hCentralitySel"), centrality); - jetLoopMCP(jets, centrality, mcCollision.rho()); + const float eventWeight = cfgUseMCEventWeights ? mcCollision.weight() : 1.0f; + const float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcp/hUsedEventWeights"), 0.5, eventWeight); + } + jetLoopMCP(jets, centrality, mcCollision.rho(), eventWeight, pTHat); } PROCESS_SWITCH(JetSpectraEseTask, processMCParticleLevel, "jets on particle level MC", false); void processMCDetectorLevel(soa::Filtered>::iterator const& collision, ChargedMCDJets const& mcdjets, aod::JetTracks const&, - aod::JetParticles const&) + aod::JetParticles const&, + aod::JetMcCollisions const&) { float counter{0.5f}; registry.fill(HIST("mcd/hEventCounter"), counter++); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { return; + } registry.fill(HIST("mcd/hEventCounter"), counter++); - if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) + if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) { return; + } registry.fill(HIST("mcd/hEventCounter"), counter++); - if (!(std::abs(collision.posZ()) < vertexZCut)) { + if (!(std::abs(collision.posZ()) < systCuts.vertexZCut)) { return; } registry.fill(HIST("mcd/hEventCounter"), counter++); - auto centrality = cfgisPbPb ? collision.centFT0M() : -1; + auto centrality = cfgisPbPb ? getCentrality(collision) : -1; registry.fill(HIST("mcd/hCentralitySel"), centrality); + float eventWeight = 1.0f; + if (cfgUseMCEventWeights) { + eventWeight = collision.has_mcCollision() ? collision.mcCollision_as().weight() : -1.0f; + } + if (eventWeight <= 0.0f) { + return; + } + const float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcd/hUsedEventWeights"), 0.5, eventWeight); + } jetLoopMCD(mcdjets, centrality, collision.rho()); } PROCESS_SWITCH(JetSpectraEseTask, processMCDetectorLevel, "jets on detector level", false); @@ -817,7 +1259,7 @@ struct JetSpectraEseTask { return; } registry.fill(HIST("mcm/hMCEventCounter"), counter++); - if (!(std::abs(mcCol.posZ()) < vertexZCut)) { + if (!(std::abs(mcCol.posZ()) < systCuts.vertexZCut)) { return; } registry.fill(HIST("mcm/hMCEventCounter"), counter++); @@ -825,26 +1267,33 @@ struct JetSpectraEseTask { for (const auto& collision : collisions) { float secCount{0.5f}; registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); - if (!(std::abs(collision.posZ()) < vertexZCut)) { + if (!(std::abs(collision.posZ()) < systCuts.vertexZCut)) { return; } registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { return; + } registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); - if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) + if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) { return; + } registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); - auto centrality = cfgisPbPb ? collision.centFT0M() : -1; + auto centrality = cfgisPbPb ? getCentrality(collision) : -1; if (cfgisPbPb) if (centrality < centRange->at(0) || centrality > centRange->at(1)) registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); registry.fill(HIST("mcm/hCentralityAnalyzed"), centrality); - matchedJetLoop(mcdjets.sliceBy(mcdjetsPerJCollision, collision.globalIndex()), centrality, collision.rho(), mcCol.rho()); + float eventWeight = cfgUseMCEventWeights ? mcCol.weight() : 1.0; + float pTHat = getPTHat(eventWeight); + if (!(pTHat < pTHatAbsoluteMin)) { + registry.fill(HIST("mcm/hUsedEventWeights"), 0.5, eventWeight); + } + matchedJetLoop(mcdjets.sliceBy(mcdjetsPerJCollision, collision.globalIndex()), centrality, collision.rho(), mcCol.rho(), eventWeight, pTHat); registry.fill(HIST("mcm/hMCDMatchedEventCounter"), secCount++); } @@ -856,7 +1305,7 @@ struct JetSpectraEseTask { bool isChargedParticle(int pdgCode) { auto pdgParticle = pdg->GetParticle(pdgCode); - return pdgParticle && pdgParticle->Charge() != 0.0; + return pdgParticle != nullptr && pdgParticle->Charge() != 0.0; } void processMCGenTrack(soa::Filtered>::iterator const& mcCollision, @@ -870,22 +1319,22 @@ struct JetSpectraEseTask { if (collisions.size() != 1) { return; } - if (!(std::abs(mcCollision.posZ()) < vertexZCut)) { + if (!(std::abs(mcCollision.posZ()) < systCuts.vertexZCut)) { return; } auto collision = collisions.begin(); - if (!(std::abs(collision.posZ()) < vertexZCut)) { + if (!(std::abs(collision.posZ()) < systCuts.vertexZCut)) { return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { return; } if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) { return; } - auto centrality = cfgisPbPb ? collision.centFT0M() : -1.0f; + auto centrality = cfgisPbPb ? getCentrality(collision) : -1.0f; if (cfgSelCentrality && cfgisPbPb && !isCentralitySelected(centrality)) { return; } @@ -900,13 +1349,14 @@ struct JetSpectraEseTask { if (!particle.isPhysicalPrimary()) { continue; } - if (particle.pt() < trackPtMin || particle.pt() >= trackPtMax) { + if (particle.pt() < trackCuts.trackPtMin || particle.pt() >= trackCuts.trackPtMax) { continue; } - if (particle.eta() <= trackEtaMin || particle.eta() >= trackEtaMax) { + if (particle.eta() <= trackCuts.trackEtaMin || particle.eta() >= trackCuts.trackEtaMax) { continue; } + registry.fill(HIST("mcgen/h3TrackPtGen"), particle.pt(), collision.posZ(), particle.eta()); registry.fill(HIST("mcgen/hTrackPtGen"), particle.pt()); registry.fill(HIST("mcgen/hTrackEtaGen"), particle.eta()); registry.fill(HIST("mcgen/hTrackPhiGen"), particle.phi()); @@ -915,32 +1365,32 @@ struct JetSpectraEseTask { PROCESS_SWITCH(JetSpectraEseTask, processMCGenTrack, "jet MC process: Generated track", false); void processMCRecoTrack(soa::Filtered>::iterator const& mcCollision, soa::SmallGroups const& collisions, - aod::JetTracksMCD const& tracks, - aod::JetParticles const&) + soa::Join const& tracks, + aod::JetParticles const&, ETracks const&) { if (mcCollision.size() < 1) { return; } - if (collisions.size() < 1) { + if (collisions.size() != 1) { return; } - if (!(std::abs(mcCollision.posZ()) < vertexZCut)) { + if (!(std::abs(mcCollision.posZ()) < systCuts.vertexZCut)) { return; } std::vector seenMcParticles; for (const auto& collision : collisions) { - if (!(std::abs(collision.posZ()) < vertexZCut)) { + if (!(std::abs(collision.posZ()) < systCuts.vertexZCut)) { continue; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { continue; } if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) { continue; } - auto centrality = cfgisPbPb ? collision.centFT0M() : -1.0f; + auto centrality = cfgisPbPb ? getCentrality(collision) : -1.0f; if (cfgSelCentrality && cfgisPbPb && !isCentralitySelected(centrality)) { continue; } @@ -950,6 +1400,9 @@ struct JetSpectraEseTask { if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { continue; } + if (!isTrackSelected(track.template track_as())) { + continue; + } if (!track.has_mcParticle()) { continue; } @@ -961,10 +1414,10 @@ struct JetSpectraEseTask { if (!particle.isPhysicalPrimary()) { continue; } - if (particle.pt() < trackPtMin || particle.pt() >= trackPtMax) { + if (particle.pt() < trackCuts.trackPtMin || particle.pt() >= trackCuts.trackPtMax) { continue; } - if (particle.eta() <= trackEtaMin || particle.eta() >= trackEtaMax) { + if (particle.eta() <= trackCuts.trackEtaMin || particle.eta() >= trackCuts.trackEtaMax) { continue; } if (std::find(seenMcParticles.begin(), seenMcParticles.end(), particle.globalIndex()) != seenMcParticles.end()) { @@ -972,6 +1425,7 @@ struct JetSpectraEseTask { } seenMcParticles.push_back(particle.globalIndex()); + registry.fill(HIST("mcrec/h3TrackPtReco"), track.pt(), collision.posZ(), track.eta()); registry.fill(HIST("mcrec/hTrackPtReco"), track.pt()); registry.fill(HIST("mcrec/hTrackEtaReco"), track.eta()); registry.fill(HIST("mcrec/hTrackPhiReco"), track.phi()); @@ -980,69 +1434,115 @@ struct JetSpectraEseTask { } PROCESS_SWITCH(JetSpectraEseTask, processMCRecoTrack, "jet MC process: Reconstructed track", false); + float redqn(const std::vector& vec) + { + float redq = -999.0f; + if (vec[2] > LowFT0Cut) { + redq = std::sqrt(vec[0] * vec[0] + vec[1] * vec[1]) * std::sqrt(vec[2]); + } + return redq; + } + template + void detCorrelation(Col const& col) + { + + auto qC = qVecNoESE(col, 2, cfgnredCorrLevel); + auto qA = qVecNoESE(col, 2, cfgnredCorrLevel); + + auto redFT0C = redqn(qC); + auto redFT0A = redqn(qA); + + registry.fill(HIST("eventQA/h3Centq2FT0Cq2FT0A"), getCentrality(col), redFT0C, redFT0A); + } + static constexpr float InvalidValue = 999.; - // template template EventPlane procEP(EPCol const& vec) { - constexpr std::array AmpCut{LowFT0Cut, 0.0}; - auto computeEP = [&AmpCut](const std::vector& vec, auto det, float n) { return vec[2] > AmpCut[det] ? (1.0 / n) * std::atan2(vec[1], vec[0]) : InvalidValue; }; - std::map epMap; - std::map ep3Map; - auto vec1{qVecNoESE(vec)}; - epMap["FT0A"] = computeEP(vec1, 0, 2.0); - ep3Map["FT0A"] = computeEP(vec1, 0, 3.0); + auto computeEP = [](const std::vector& qVec, float minAmp, float harmonic) { + return qVec[2] > minAmp ? std::atan2(qVec[1], qVec[0]) / harmonic : InvalidValue; + }; + + std::map epMap{ + {"FT0A", computeEP(qVecNoESE(vec, 2, cfgnCorrLevel), LowFT0Cut, 2.0f)}, + {"FT0C", computeEP(qVecNoESE(vec, 2, cfgnCorrLevel), LowFT0Cut, 2.0f)}, + {"FV0A", computeEP(qVecNoESE(vec, 2, cfgnCorrLevel), LowFT0Cut, 2.0f)}, + {"TPCpos", computeEP(qVecNoESE(vec, 2, cfgnCorrLevel), 0.0f, 2.0f)}, + {"TPCneg", computeEP(qVecNoESE(vec, 2, cfgnCorrLevel), 0.0f, 2.0f)}}; + std::map ep3Map{ + {"FT0A", computeEP(qVecNoESE(vec, 3, cfgnCorrLevel), LowFT0Cut, 3.0f)}, + {"FT0C", computeEP(qVecNoESE(vec, 3, cfgnCorrLevel), LowFT0Cut, 3.0f)}}; + std::map ep4Map{ + {"FT0A", computeEP(qVecNoESE(vec, 4, cfgnCorrLevel), LowFT0Cut, 4.0f)}, + {"FT0C", computeEP(qVecNoESE(vec, 4, cfgnCorrLevel), LowFT0Cut, 4.0f)}, + {"FV0A", computeEP(qVecNoESE(vec, 4, cfgnCorrLevel), LowFT0Cut, 4.0f)}, + {"TPCpos", computeEP(qVecNoESE(vec, 4, cfgnCorrLevel), 0.0f, 4.0f)}, + {"TPCneg", computeEP(qVecNoESE(vec, 4, cfgnCorrLevel), 0.0f, 4.0f)}}; + if constexpr (P.psi) { - auto vec2{qVecNoESE(vec)}; - epMap["FT0C"] = computeEP(vec2, 0, 2.0); - ep3Map["FT0C"] = computeEP(vec2, 0, 3.0); - epMap["FV0A"] = computeEP(qVecNoESE(vec), 0, 2.0); - epMap["TPCpos"] = computeEP(qVecNoESE(vec), 1, 2.0); - epMap["TPCneg"] = computeEP(qVecNoESE(vec), 1, 2.0); - if constexpr (P.hist) - fillEP(/*std::make_index_sequence<5>{},*/ vec, epMap); - auto cosPsi = [](float psiX, float psiY) { return (static_cast(psiX) == InvalidValue || static_cast(psiY) == InvalidValue) ? InvalidValue : std::cos(2.0 * (psiX - psiY)); }; - std::array epCorrContainer{}; - epCorrContainer[0] = cosPsi(epMap.at(cfgEPRefA), epMap.at(cfgEPRefC)); - epCorrContainer[1] = cosPsi(epMap.at(cfgEPRefA), epMap.at(cfgEPRefB)); - epCorrContainer[2] = cosPsi(epMap.at(cfgEPRefB), epMap.at(cfgEPRefC)); - if constexpr (P.hist) - fillEPCos(/*std::make_index_sequence<3>{},*/ vec, epCorrContainer); - } - EventPlane localPlane; - localPlane.psi2 = epMap.at(cfgEPRefA); - localPlane.psi3 = ep3Map.at(cfgEPRefA); - return localPlane; - // return epMap.at(cfgEPRefA); + if constexpr (P.hist) { + fillEP(vec, epMap, ep3Map, ep4Map); + } + + auto cosPsi = [](float psiX, float psiY, const float harmonic = 2.0f) { + return psiX == InvalidValue || psiY == InvalidValue ? InvalidValue : std::cos(harmonic * (psiX - psiY)); + }; + const std::array epCorrContainer22{ + cosPsi(epMap.at(cfgEPRefA), epMap.at(cfgEPRefC)), + cosPsi(epMap.at(cfgEPRefA), epMap.at(cfgEPRefB)), + cosPsi(epMap.at(cfgEPRefB), epMap.at(cfgEPRefC))}; + const std::array epCorrContainer24{ + cosPsi(epMap.at(cfgEPRefA), epMap.at(cfgEPRefC), 4.0f), + cosPsi(epMap.at(cfgEPRefA), epMap.at(cfgEPRefB), 4.0f), + cosPsi(epMap.at(cfgEPRefB), epMap.at(cfgEPRefC), 4.0f)}; + const std::array epCorrContainer44{ + cosPsi(ep4Map.at(cfgEPRefA), ep4Map.at(cfgEPRefC), 4.0f), + cosPsi(ep4Map.at(cfgEPRefA), ep4Map.at(cfgEPRefB), 4.0f), + cosPsi(ep4Map.at(cfgEPRefB), ep4Map.at(cfgEPRefC), 4.0f)}; + + if constexpr (P.hist) { + fillEPCos(vec, epCorrContainer22, epCorrContainer24, epCorrContainer44); + } + } + // check A is valid + bool isValidA{epMap.at(cfgEPRefA) != InvalidValue}; + return {.psi2 = epMap.at(cfgEPRefA), .psi3 = ep3Map.at(cfgEPRefA), .isValid = isValidA}; } - template - void fillEPCos(/*const std::index_sequence&,*/ const collision& col, const std::array& Corr) + template + void fillEPCos(const collision& col, const std::array& Corr22, const std::array& Corr42, const std::array& Corr44) { - // static constexpr std::string CosList[] = {"hCosPsi2AmC", "hCosPsi2AmB", "hCosPsi2BmC"}; - // (registry.fill(HIST(CosList[Idx]), col.centrality(), Corr[Idx], col.qPERCFT0C()[0]), ...); - registry.fill(HIST("eventQA/hCosPsi2AmC"), col.centFT0M(), Corr[0], col.qPERCFT0C()[0]); - registry.fill(HIST("eventQA/hCosPsi2AmB"), col.centFT0M(), Corr[1], col.qPERCFT0C()[0]); - registry.fill(HIST("eventQA/hCosPsi2BmC"), col.centFT0M(), Corr[2], col.qPERCFT0C()[0]); - } + registry.fill(HIST("eventQA/hCos2Psi2AmC"), getCentrality(col), Corr22[0], col.qPERCFT0C()[0]); + registry.fill(HIST("eventQA/hCos2Psi2AmB"), getCentrality(col), Corr22[1], col.qPERCFT0C()[0]); + registry.fill(HIST("eventQA/hCos2Psi2BmC"), getCentrality(col), Corr22[2], col.qPERCFT0C()[0]); - template - void fillEP(/*const std::index_sequence&,*/ const collision& col, const std::map& epMap) - { - // static constexpr std::string_view EpList[] = {"hPsi2FT0A", "hPsi2FV0A", "hPsi2FT0C", "hPsi2TPCpos", "hPsi2TPCneg"}; - // (registry.fill(HIST(EpList[Idx]), col.centrality(), epMap.at(std::string(RemovePrefix(EpList[Idx])))), ...); - registry.fill(HIST("eventQA/hPsi2FT0A"), col.centFT0M(), epMap.at("FT0A")); - registry.fill(HIST("eventQA/hPsi2FV0A"), col.centFT0M(), epMap.at("FV0A")); - registry.fill(HIST("eventQA/hPsi2FT0C"), col.centFT0M(), epMap.at("FT0C")); - registry.fill(HIST("eventQA/hPsi2TPCpos"), col.centFT0M(), epMap.at("TPCpos")); - registry.fill(HIST("eventQA/hPsi2TPCneg"), col.centFT0M(), epMap.at("TPCneg")); + registry.fill(HIST("eventQA/hCos4Psi2AmC"), getCentrality(col), Corr42[0], col.qPERCFT0C()[0]); + registry.fill(HIST("eventQA/hCos4Psi2AmB"), getCentrality(col), Corr42[1], col.qPERCFT0C()[0]); + registry.fill(HIST("eventQA/hCos4Psi2BmC"), getCentrality(col), Corr42[2], col.qPERCFT0C()[0]); + + registry.fill(HIST("eventQA/hCos4Psi4AmC"), getCentrality(col), Corr44[0], col.qPERCFT0C()[0]); + registry.fill(HIST("eventQA/hCos4Psi4AmB"), getCentrality(col), Corr44[1], col.qPERCFT0C()[0]); + registry.fill(HIST("eventQA/hCos4Psi4BmC"), getCentrality(col), Corr44[2], col.qPERCFT0C()[0]); } - constexpr std::string_view RemovePrefix(std::string_view str) + + template + void fillEP(const collision& col, const std::map& epMap, const std::map& ep3Map, const std::map& ep4Map) { - constexpr std::string_view Prefix{"hPsi2"}; - return str.substr(Prefix.size()); + registry.fill(HIST("eventQA/hPsi2FT0A"), getCentrality(col), epMap.at("FT0A")); + registry.fill(HIST("eventQA/hPsi2FV0A"), getCentrality(col), epMap.at("FV0A")); + registry.fill(HIST("eventQA/hPsi2FT0C"), getCentrality(col), epMap.at("FT0C")); + registry.fill(HIST("eventQA/hPsi2TPCpos"), getCentrality(col), epMap.at("TPCpos")); + registry.fill(HIST("eventQA/hPsi2TPCneg"), getCentrality(col), epMap.at("TPCneg")); + + registry.fill(HIST("eventQA/hPsi3FT0A"), getCentrality(col), ep3Map.at("FT0A")); + registry.fill(HIST("eventQA/hPsi3FT0C"), getCentrality(col), ep3Map.at("FT0C")); + + registry.fill(HIST("eventQA/hPsi4FT0A"), getCentrality(col), ep4Map.at("FT0A")); + registry.fill(HIST("eventQA/hPsi4FT0C"), getCentrality(col), ep4Map.at("FT0C")); + registry.fill(HIST("eventQA/hPsi4FV0A"), getCentrality(col), ep4Map.at("FV0A")); + registry.fill(HIST("eventQA/hPsi4TPCpos"), getCentrality(col), ep4Map.at("TPCpos")); + registry.fill(HIST("eventQA/hPsi4TPCneg"), getCentrality(col), ep4Map.at("TPCneg")); } - constexpr int detIDN(const DetID id) { switch (id) { @@ -1064,26 +1564,26 @@ struct JetSpectraEseTask { return -1; } + static constexpr int SecondHarmonic = 2; template - std::vector qVecNoESE(Col collision) + std::vector qVecNoESE(const Col& collision, int nmode = 2, int corrLevel = 3) { - // const int nmode{2}; int detId{detIDN(id)}; - int detInd{detId * 4 /*+ cfgnTotalSystem * 4 * (nmode - 2)*/}; + int detInd{detId * 4 + cfgnTotalSystem * 4 * (nmode - 2)}; if constexpr (fill) { - if (collision.qvecAmp()[detInd] > LowFT0Cut) { - registry.fill(HIST("eventQA/hQvecUncorV2"), collision.centFT0M(), collision.qvecRe()[detInd], collision.qvecIm()[detInd]); - registry.fill(HIST("eventQA/hQvecRectrV2"), collision.centFT0M(), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1]); - registry.fill(HIST("eventQA/hQvecTwistV2"), collision.centFT0M(), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2]); - registry.fill(HIST("eventQA/hQvecFinalV2"), collision.centFT0M(), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3]); - registry.fill(HIST("eventQA/hEPUncorV2"), collision.centFT0M(), 0.5 * std::atan2(collision.qvecIm()[detInd], collision.qvecRe()[detInd])); - registry.fill(HIST("eventQA/hEPRectrV2"), collision.centFT0M(), 0.5 * std::atan2(collision.qvecIm()[detInd + 1], collision.qvecRe()[detInd + 1])); - registry.fill(HIST("eventQA/hEPTwistV2"), collision.centFT0M(), 0.5 * std::atan2(collision.qvecIm()[detInd + 2], collision.qvecRe()[detInd + 2])); + if (collision.qvecAmp()[detInd] > LowFT0Cut && nmode == SecondHarmonic) { + registry.fill(HIST("eventQA/hQvecUncorV2"), getCentrality(collision), collision.qvecRe()[detInd], collision.qvecIm()[detInd]); + registry.fill(HIST("eventQA/hQvecRectrV2"), getCentrality(collision), collision.qvecRe()[detInd + 1], collision.qvecIm()[detInd + 1]); + registry.fill(HIST("eventQA/hQvecTwistV2"), getCentrality(collision), collision.qvecRe()[detInd + 2], collision.qvecIm()[detInd + 2]); + registry.fill(HIST("eventQA/hQvecFinalV2"), getCentrality(collision), collision.qvecRe()[detInd + 3], collision.qvecIm()[detInd + 3]); + registry.fill(HIST("eventQA/hEPUncorV2"), getCentrality(collision), 0.5 * std::atan2(collision.qvecIm()[detInd], collision.qvecRe()[detInd])); + registry.fill(HIST("eventQA/hEPRectrV2"), getCentrality(collision), 0.5 * std::atan2(collision.qvecIm()[detInd + 1], collision.qvecRe()[detInd + 1])); + registry.fill(HIST("eventQA/hEPTwistV2"), getCentrality(collision), 0.5 * std::atan2(collision.qvecIm()[detInd + 2], collision.qvecRe()[detInd + 2])); } } std::vector qVec{}; - qVec.push_back(collision.qvecRe()[detInd + cfgnCorrLevel]); - qVec.push_back(collision.qvecIm()[detInd + cfgnCorrLevel]); + qVec.push_back(collision.qvecRe()[detInd + corrLevel]); + qVec.push_back(collision.qvecIm()[detInd + corrLevel]); qVec.push_back(collision.qvecAmp()[detId]); return qVec; } @@ -1092,19 +1592,52 @@ struct JetSpectraEseTask { bool isOccupancyAccepted(const col& collision) { auto occupancy{collision.trackOccupancyInTimeRange()}; - if (occupancy < cfgCutOccupancy->at(0) || occupancy > cfgCutOccupancy->at(1)) - return false; - else - return true; + return occupancy >= cfgCutOccupancy->at(0) && occupancy <= cfgCutOccupancy->at(1); } template bool isCentralitySelected(const Cent& centrality) { - if (centrality < centRange->at(0) || centrality > centRange->at(1)) - return false; - else - return true; + return centrality >= centRange->at(0) && centrality <= centRange->at(1); + } + + std::shared_ptr getRhoPhiFitEvent(int eventIndex) + { + switch (eventIndex) { + case 0: + return registry.get(HIST("rhoPhiFitEvents/event_0")); + case 1: + return registry.get(HIST("rhoPhiFitEvents/event_1")); + case 2: + return registry.get(HIST("rhoPhiFitEvents/event_2")); + case 3: + return registry.get(HIST("rhoPhiFitEvents/event_3")); + case 4: + return registry.get(HIST("rhoPhiFitEvents/event_4")); + default: + return nullptr; + } + } + + template + void ft0Amplitude(const JetCol& coll, const OriginalCol& originalColl) + { + if (!originalColl.has_foundFT0()) { + return; + } + + auto ft0 = originalColl.foundFT0(); + auto centrality = getCentrality(coll); + + if (cfgSelCentrality && !isCentralitySelected(centrality)) { + return; + } + + for (std::size_t iChA = 0; iChA < ft0.channelA().size(); ++iChA) { + float ampl = ft0.amplitudeA()[iChA]; + int ft0AchId = ft0.channelA()[iChA]; + registry.fill(HIST("eventQA/h2FT0AchannelAmplitude"), ft0AchId, ampl); + } } template @@ -1124,21 +1657,24 @@ struct JetSpectraEseTask { int nTrk{0}; if (jets.size() > 0) { for (const auto& track : tracks) { - if constexpr (fillHist) + if constexpr (fillHist) { registry.fill(HIST("trackQA/hRhoTrackCounter"), 0.5); - if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > jetR) && track.pt() >= trackPtMinRhoPhi && track.pt() <= trackPtMaxRhoPhi) { + } + if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > jetR) && track.pt() >= trackPtRhoPhi->at(0) && track.pt() <= trackPtRhoPhi->at(1)) { nTrk++; - if constexpr (fillHist) + if constexpr (fillHist) { registry.fill(HIST("trackQA/hRhoTrackCounter"), 1.5); + } } } } - if (nTrk < 1) + if (nTrk < 1) { return nullptr; + } - auto hPhiPt = std::unique_ptr(new TH1F("h_ptsum_sumpt_fit", "h_ptsum_sumpt fit use", TMath::CeilNint(std::sqrt(nTrk)), 0., o2::constants::math::TwoPI)); + auto hPhiPt = std::make_unique("h_ptsum_sumpt_fit", "h_ptsum_sumpt fit use", TMath::CeilNint(std::sqrt(nTrk)), 0., o2::constants::math::TwoPI); for (const auto& track : tracks) { - if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > jetR) && track.pt() >= trackPtMinRhoPhi && track.pt() <= trackPtMaxRhoPhi) { + if (jetderiveddatautilities::selectTrack(track, trackSelection) && (std::fabs(track.eta() - leadingJetEta) > jetR) && track.pt() >= trackPtRhoPhi->at(0) && track.pt() <= trackPtRhoPhi->at(1)) { hPhiPt->Fill(track.phi(), track.pt()); if constexpr (fillHist) { registry.fill(HIST("trackQA/hRhoTrackCounter"), 2.5); @@ -1146,7 +1682,7 @@ struct JetSpectraEseTask { } } } - auto modulationFit = std::unique_ptr(new TF1("fit_rholoc", "[0] * (1. + 2. * ([1] * std::cos(2. * (x - [2])) + [3] * std::cos(3. * (x - [4]))))", 0, o2::constants::math::TwoPI)); + auto modulationFit = std::make_unique("fit_rholoc", "[0] * (1. + 2. * ([1] * std::cos(2. * (x - [2])) + [3] * std::cos(3. * (x - [4]))))", 0, o2::constants::math::TwoPI); modulationFit->SetParameter(0, 1.0); modulationFit->SetParameter(1, 0.01); @@ -1155,37 +1691,96 @@ struct JetSpectraEseTask { modulationFit->FixParameter(2, (ep.psi2 < 0) ? RecoDecay::constrainAngle(ep.psi2) : ep.psi2); modulationFit->FixParameter(4, (ep.psi3 < 0) ? RecoDecay::constrainAngle(ep.psi3) : ep.psi3); - hPhiPt->Fit(modulationFit.get(), "Q", "", 0, o2::constants::math::TwoPI); + const int fitStatus = hPhiPt->Fit(modulationFit.get(), "QN", "", 0, o2::constants::math::TwoPI); + if (fitStatus != 0) { + return nullptr; + } if constexpr (fillHist) { - registry.fill(HIST("eventQA/hfitPar0"), col.centFT0M(), modulationFit->GetParameter(0)); - registry.fill(HIST("eventQA/hfitPar1"), col.centFT0M(), modulationFit->GetParameter(1)); - registry.fill(HIST("eventQA/hfitPar2"), col.centFT0M(), modulationFit->GetParameter(2)); - registry.fill(HIST("eventQA/hfitPar3"), col.centFT0M(), modulationFit->GetParameter(3)); - registry.fill(HIST("eventQA/hfitPar4"), col.centFT0M(), modulationFit->GetParameter(4)); + registry.fill(HIST("eventQA/hfitPar0"), getCentrality(col), modulationFit->GetParameter(0)); + registry.fill(HIST("eventQA/hfitPar1"), getCentrality(col), modulationFit->GetParameter(1)); + registry.fill(HIST("eventQA/hfitPar2"), getCentrality(col), modulationFit->GetParameter(2)); + registry.fill(HIST("eventQA/hfitPar3"), getCentrality(col), modulationFit->GetParameter(3)); + registry.fill(HIST("eventQA/hfitPar4"), getCentrality(col), modulationFit->GetParameter(4)); } - if (modulationFit->GetParameter(0) <= 0) + if (modulationFit->GetParameter(0) <= 0) { return nullptr; - - double chi2{0.}; - for (int i{0}; i < hPhiPt->GetXaxis()->GetNbins(); i++) { - if (hPhiPt->GetBinContent(i + 1) <= 0.) - continue; - chi2 += std::pow((hPhiPt->GetBinContent(i + 1) - modulationFit->Eval(hPhiPt->GetXaxis()->GetBinCenter(1 + i))), 2) / hPhiPt->GetBinContent(i + 1); } - int nDF{1}; - int numParams{2}; - nDF = static_cast(modulationFit->GetXaxis()->GetNbins()) - numParams; - if (nDF <= 0) + const double chi2 = modulationFit->GetChisquare(); + const int nDF = modulationFit->GetNDF(); + if (nDF <= 0) { return nullptr; + } + + const double cDF = TMath::Prob(chi2, nDF); + if constexpr (fillHist) { + registry.fill(HIST("eventQA/hRhoPhiCheck"), 0.5); + } - auto cDF = 1. - TMath::Gamma(nDF, chi2); + if (cfgRhoPhiPvalCriteria && cDF < cfgRhoPhiPvalCut) { + const float noFlow = 0.0f; + modulationFit->SetParameter(1, noFlow); // o2-linter: disable=magic-number (fit params) + modulationFit->SetParameter(3, noFlow); // o2-linter: disable=magic-number (fit params) + if constexpr (fillHist) { + registry.fill(HIST("eventQA/hRhoPhiCheck"), 1.5); + } + } if constexpr (fillHist) { - registry.fill(HIST("eventQA/hPValueCentCDF"), col.centFT0M(), cDF); - registry.fill(HIST("eventQA/hCentChi2Ndf"), col.centFT0M(), chi2 / (static_cast(nDF))); + registry.fill(HIST("eventQA/hPValueCentCDF"), getCentrality(col), cDF); + registry.fill(HIST("eventQA/hCentChi2Ndf"), getCentrality(col), chi2 / (static_cast(nDF))); + + static int numSavedRhoFitEvents{0}; + if (numSavedRhoFitEvents < NumSavedRhoFitEvents) { + auto savedEvent = getRhoPhiFitEvent(numSavedRhoFitEvents); + savedEvent->SetBins(hPhiPt->GetNbinsX(), 0., o2::constants::math::TwoPI); + for (int bin = 0; bin <= hPhiPt->GetNbinsX() + 1; ++bin) { + savedEvent->SetBinContent(bin, hPhiPt->GetBinContent(bin)); + savedEvent->SetBinError(bin, hPhiPt->GetBinError(bin)); + } + savedEvent->SetEntries(hPhiPt->GetEntries()); + + const double rho0 = modulationFit->GetParameter(0); + const double v2 = modulationFit->GetParameter(1); + const double psi2 = modulationFit->GetParameter(2); + const double v3 = modulationFit->GetParameter(3); + const double psi3 = modulationFit->GetParameter(4); + + auto rho0Function = std::make_unique("rho_0", "pol0", 0., o2::constants::math::TwoPI, TF1::EAddToList::kNo); + rho0Function->SetParameter(0, rho0); + rho0Function->SetParError(0, modulationFit->GetParError(0)); + rho0Function->SetParName(0, "rho_0"); + rho0Function->SetLineStyle(2); + + auto rhoPhiFunction = std::unique_ptr(dynamic_cast(modulationFit->Clone("rho_phi"))); + rhoPhiFunction->SetParNames("rho_0", "v_2", "Psi_2", "v_3", "Psi_3"); + rhoPhiFunction->SetLineWidth(2); + rhoPhiFunction->ResetBit(TF1::kNotDraw); + + auto rhoV2Function = std::make_unique("rho_v2", "[0] * (1. + 2. * [1] * std::cos(2. * (x - [2])))", 0., o2::constants::math::TwoPI, TF1::EAddToList::kNo); + rhoV2Function->SetParameters(rho0, v2, psi2); + rhoV2Function->SetParError(0, modulationFit->GetParError(0)); + rhoV2Function->SetParError(1, modulationFit->GetParError(1)); + rhoV2Function->SetParError(2, modulationFit->GetParError(2)); + rhoV2Function->SetParNames("rho_0", "v_2", "Psi_2"); + rhoV2Function->SetLineStyle(7); + + auto rhoV3Function = std::make_unique("rho_v3", "[0] * (1. + 2. * [1] * std::cos(3. * (x - [2])))", 0., o2::constants::math::TwoPI, TF1::EAddToList::kNo); + rhoV3Function->SetParameters(rho0, v3, psi3); + rhoV3Function->SetParError(0, modulationFit->GetParError(0)); + rhoV3Function->SetParError(1, modulationFit->GetParError(3)); + rhoV3Function->SetParError(2, modulationFit->GetParError(4)); + rhoV3Function->SetParNames("rho_0", "v_3", "Psi_3"); + rhoV3Function->SetLineStyle(9); + + savedEvent->GetListOfFunctions()->Add(rho0Function.release()); + savedEvent->GetListOfFunctions()->Add(rhoPhiFunction.release()); + savedEvent->GetListOfFunctions()->Add(rhoV2Function.release()); + savedEvent->GetListOfFunctions()->Add(rhoV3Function.release()); + ++numSavedRhoFitEvents; + } } return modulationFit; @@ -1202,19 +1797,27 @@ struct JetSpectraEseTask { template void bkgFluctuationsRandomCone(TCollisions const& collision, TJets const& jets, TTracks const& tracks) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { + if (!isVertexSelected(collision)) { + return; + } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { return; } - if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) + if (cfgEvSelOccupancy && !isOccupancyAccepted(collision)) { return; + } const auto psi{procEP(collision)}; + if (!psi.isValid) { + return; + } auto qPerc{collision.qPERCFT0C()}; - if (qPerc[0] < 0) + if (qPerc[0] < 0) { return; + } TRandom3 randomNumber(0); - float randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); + float randomConeEta = randomNumber.Uniform(trackCuts.trackEtaMin + randomConeR, trackCuts.trackEtaMax - randomConeR); float randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); float randomConePt = 0; for (auto const& track : tracks) { @@ -1226,19 +1829,19 @@ struct JetSpectraEseTask { } } } - registry.fill(HIST("hCentRhoRandomCone"), collision.centFT0M(), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("hCentRhoRandomCone"), getCentrality(collision), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); - randomConePt = 0; + float randomConePtRandomTrackDir = 0; for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, -o2::constants::math::PI); - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; + float dEta = randomNumber.Uniform(trackCuts.trackEtaMin, trackCuts.trackEtaMax) - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { - randomConePt += track.pt(); + randomConePtRandomTrackDir += track.pt(); } } } - registry.fill(HIST("hCentRhoRandomConeRandomTrackDir"), collision.centFT0M(), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); + registry.fill(HIST("hCentRhoRandomConeRandomTrackDir"), getCentrality(collision), randomConePtRandomTrackDir - o2::constants::math::PI * randomConeR * randomConeR * collision.rho()); if (jets.size() > 0) { float dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, -o2::constants::math::PI); @@ -1247,7 +1850,7 @@ struct JetSpectraEseTask { bool jetWasInCone = false; while ((randomConeLeadJetDeltaR <= 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < jets.iteratorAt(0).r() / 100.0 + randomConeR)) || (randomConeLeadJetDeltaR > 0 && (std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet) < randomConeLeadJetDeltaR))) { jetWasInCone = true; - randomConeEta = randomNumber.Uniform(trackEtaMin + randomConeR, trackEtaMax - randomConeR); + randomConeEta = randomNumber.Uniform(trackCuts.trackEtaMin + randomConeR, trackCuts.trackEtaMax - randomConeR); randomConePhi = randomNumber.Uniform(0.0, o2::constants::math::TwoPI); dPhiLeadingJet = RecoDecay::constrainAngle(jets.iteratorAt(0).phi() - randomConePhi, -o2::constants::math::PI); dEtaLeadingJet = jets.iteratorAt(0).eta() - randomConeEta; @@ -1269,12 +1872,13 @@ struct JetSpectraEseTask { std::unique_ptr rhoFit{nullptr}; if (cfgrhoPhi) { rhoFit = fitRho(collision, psi, tracks, jets); - if (!rhoFit) + if (!rhoFit) { return; + } rho = evalRho(rhoFit.get(), randomConeR, randomConePhi, rho); } float dPhiRC{RecoDecay::constrainAngle(randomConePhi - psi.psi2, -o2::constants::math::PI)}; - registry.fill(HIST("hCentRhoRandomConewoLeadingJet"), collision.centFT0M(), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); + registry.fill(HIST("hCentRhoRandomConewoLeadingJet"), getCentrality(collision), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); double randomConePtWithoutOneLeadJet = 0; double randomConePtWithoutTwoLeadJet = 0; @@ -1282,7 +1886,7 @@ struct JetSpectraEseTask { for (auto const& track : tracks) { if (jetderiveddatautilities::selectTrack(track, trackSelection)) { float dPhi = RecoDecay::constrainAngle(randomNumber.Uniform(0.0, o2::constants::math::TwoPI) - randomConePhi, -o2::constants::math::PI); - float dEta = randomNumber.Uniform(trackEtaMin, trackEtaMax) - randomConeEta; + float dEta = randomNumber.Uniform(trackCuts.trackEtaMin, trackCuts.trackEtaMax) - randomConeEta; if (std::sqrt(dEta * dEta + dPhi * dPhi) < randomConeR) { if (!isTrackInJet(track, jets.iteratorAt(0))) { randomConePtWithoutOneLeadJet += track.pt(); @@ -1293,9 +1897,11 @@ struct JetSpectraEseTask { } } } + registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoOneLeadingJet"), getCentrality(collision), randomConePtWithoutOneLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); + registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoTwoLeadingJet"), getCentrality(collision), randomConePtWithoutTwoLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); } - registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoOneLeadingJet"), collision.centFT0M(), randomConePtWithoutOneLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); - registry.fill(HIST("hCentRhoRandomConeRndTrackDirwoTwoLeadingJet"), collision.centFT0M(), randomConePtWithoutTwoLeadJet - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC, qPerc[0]); + registry.fill(HIST("h3CentdeltapTRndmConePhi_rhovsphi"), getCentrality(collision), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * collision.rho(), dPhiRC); + registry.fill(HIST("h3CentdeltapTRndmConePhi_localrhovsphi"), getCentrality(collision), randomConePt - o2::constants::math::PI * randomConeR * randomConeR * rho, dPhiRC); } template bool isTrackInJet(TTracks const& track, TJets const& jet) @@ -1313,12 +1919,17 @@ struct JetSpectraEseTask { // MCD = 1 // }; // template + float getPTHat(float eventWeight) + { + return 10.0f / std::pow(eventWeight, 1.0f / pTHatExponent); + } + template void jetLoopMCD(const Jets& jets, const float& centrality, const float& rho) { float weight = 1.0; for (const auto& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; } if (!isAcceptedJet(jet)) { @@ -1331,17 +1942,26 @@ struct JetSpectraEseTask { if (cfgUseMCEventWeights) { weight = jet.eventWeight(); } + const float pTHat = getPTHat(weight); + if (pTHat < pTHatAbsoluteMin) { + continue; + } + if (jet.pt() > pTHatMaxMCD * pTHat) { + continue; + } registry.fill(/*HIST(LevelJets[jetLvl]) +*/ HIST("mcd/hJetSparse"), centrality, pt, jet.eta(), jet.phi(), weight); /* detector level mcm*/ } } template - void jetLoopMCP(const Jets& jets, const float& centrality, const float& rho) + void jetLoopMCP(const Jets& jets, const float& centrality, const float& rho, float weight = 1.0f, float pTHat = 999.0f) { + if (pTHat < pTHatAbsoluteMin) { + return; + } bool mcLevelIsParticleLevel = true; - float weight = 1.0; for (const auto& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; } if (!isAcceptedJet(jet, mcLevelIsParticleLevel)) { @@ -1351,43 +1971,42 @@ struct JetSpectraEseTask { if (cfgbkgSubMC) { pt = jet.pt() - (rho * jet.area()); } - if (cfgUseMCEventWeights) { - weight = jet.eventWeight(); + if (jet.pt() > pTHatMaxMCP * pTHat) { + continue; } registry.fill(/*HIST(LevelJets[jetLvl]) +*/ HIST("mcp/hJetSparse"), centrality, pt, jet.eta(), jet.phi(), weight); /* detector level mcm*/ } } template - void matchedJetLoop(const Jets& jets, const float& centrality, const float& rho, const float& rho2) + void matchedJetLoop(const Jets& jets, const float& centrality, const float& rho, const float& rho2, float weight = 1.0, float pTHat = 999.0) { - float weight = 1.0; + if (pTHat < pTHatAbsoluteMin) { + return; + } for (const auto& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) { + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; } if (!isAcceptedJet(jet)) { continue; } - float pTHat = 10. / (std::pow(weight, 1.0 / pTHatExponent)); if (jet.pt() > pTHatMaxMCD * pTHat) { - return; + continue; } auto pt = jet.pt(); if (cfgbkgSubMC) { pt = jet.pt() - (rho * jet.area()); } - if (cfgUseMCEventWeights) { - weight = jet.eventWeight(); - } registry.fill(HIST("mcm/hJetSparse"), centrality, pt, jet.eta(), jet.phi(), weight); /* detector level mcm*/ if (jet.has_matchedJetGeo()) { registry.fill(HIST("mcm/hDetSparseMatch"), centrality, pt, jet.eta(), jet.phi(), weight); for (const auto& matchedJet : jet.template matchedJetGeo_as()) { - if (matchedJet.pt() > pTHatMaxMCD * pTHat) + if (matchedJet.pt() > pTHatMaxMCP * pTHat) { continue; + } auto matchedpt = matchedJet.pt(); if (cfgbkgSubMC) { matchedpt = matchedJet.pt() - (rho2 * matchedJet.area()); @@ -1412,9 +2031,10 @@ struct JetSpectraEseTask { float leadJetEta = -999; bool hasLeadingJet = false; for (const auto& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaMin, jetEtaMax, trackEtaMin, trackEtaMax)) + if (!jetfindingutilities::isInEtaAcceptance(jet, cfgJetEta->at(0), cfgJetEta->at(1), trackCuts.trackEtaMin, trackCuts.trackEtaMax)) { continue; - if (!isAcceptedJet(jet)) { + } + if (!isAcceptedJet>(jet)) { continue; } auto jetPtBkg = jet.pt() - (collision.rho() * jet.area()); @@ -1449,6 +2069,35 @@ struct JetSpectraEseTask { } return true; } + template + bool isTrackSelected(TTrack const& track) + { + if (track.pt() < trackCuts.trackPtMin || track.pt() >= trackCuts.trackPtMax) { + return false; + } + if (track.eta() <= trackCuts.trackEtaMin || track.eta() >= trackCuts.trackEtaMax) { + return false; + } + if (track.tpcNClsCrossedRows() < systCuts.nTPCXrows) { + return false; + } + if (track.tpcNClsFound() < systCuts.nTPCCls) { + return false; + } + if (std::fabs(track.dcaZ()) >= systCuts.trackDCAzMax) { + return false; + } + if (std::fabs(track.dcaXY()) > (systCuts.dcaXYSigmaMax / 7.f) * (0.0105f + 0.0350f / std::pow(track.pt(), 1.1f))) { + return false; + } + if (track.tpcChi2NCl() > systCuts.chi2PrTPCcls) { + return false; + } + if (track.itsChi2NCl() > systCuts.chi2PrITScls) { + return false; + } + return true; + } template bool isAcceptedJet(TJets const& jet, bool mcLevelIsParticleLevel = false) @@ -1466,6 +2115,9 @@ struct JetSpectraEseTask { if (!checkConstituentMinPt && !checkConstituentMaxPt) { checkConstituentPt = false; } + if (mcLevelIsParticleLevel && !checkLeadConstituentPtForMcpJets) { + checkConstituentPt = false; + } if (checkConstituentPt) { bool isMinLeadingConstituent = !checkConstituentMinPt; @@ -1474,7 +2126,7 @@ struct JetSpectraEseTask { for (const auto& constituent : jet.template tracks_as()) { double pt = constituent.pt(); - if ((!checkLeadConstituentMinPtForMcpJets && mcLevelIsParticleLevel) || (checkConstituentMinPt && pt >= leadingConstituentPtMin)) { // if the jet is mcp level and checkLeadConstituentMinPtForMcpJets is true, then the pt of the leading track of that jet does not need to be below the defined leadingConstituentPtMin cut + if ((checkConstituentMinPt && pt >= leadingConstituentPtMin)) { isMinLeadingConstituent = true; } if (checkConstituentMaxPt && pt > leadingConstituentPtMax) { diff --git a/PWGJE/Tasks/jetSubstructure.cxx b/PWGJE/Tasks/jetSubstructure.cxx index fe568b6c2a6..6169bd82f5c 100644 --- a/PWGJE/Tasks/jetSubstructure.cxx +++ b/PWGJE/Tasks/jetSubstructure.cxx @@ -80,6 +80,7 @@ struct JetSubstructureTask { Configurable doPairBkg{"doPairBkg", true, "save bkg pairs"}; Configurable pairConstituentPtMin{"pairConstituentPtMin", 1.0, "pt cut off for constituents going into pairs"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; + Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; Service pdg; std::vector jetConstituents; @@ -129,7 +130,7 @@ struct JetSubstructureTask { jetReclusterer.algorithm = fastjet::JetAlgorithm::cambridge_algorithm; jetReclusterer.ghostRepeatN = 0; - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } Preslice TracksPerCollision = aod::jtrack::collisionId; @@ -260,7 +261,7 @@ struct JetSubstructureTask { } if constexpr (!std::is_same_v, aod::JetParticles>) { - if (!jetfindingutilities::isTrackSelected(track, trackSelection)) { + if (!jetfindingutilities::isTrackSelected(track, trackSelection, isEmbedding)) { continue; } } diff --git a/PWGJE/Tasks/jetSubstructureDielectronOutput.cxx b/PWGJE/Tasks/jetSubstructureDielectronOutput.cxx index 4f403b404ea..9be92a9434b 100644 --- a/PWGJE/Tasks/jetSubstructureDielectronOutput.cxx +++ b/PWGJE/Tasks/jetSubstructureDielectronOutput.cxx @@ -32,7 +32,7 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -using JetSubstructureOutputDielectron = JetSubstructureHFOutputTask, aod::McCollisionsDielectron, aod::CandidatesDielectronData, aod::CandidatesDielectronMCD, aod::CandidatesDielectronMCP, aod::BkgDielectronRhos, aod::BkgDielectronMcRhos, aod::JTrackDielectronSubs, soa::Join, soa::Join, aod::DielectronCJetRs, soa::Join, soa::Join, aod::DielectronCJetCOs, aod::DielectronCJetOs, aod::DielectronCJetSSOs, aod::DielectronCJetMOs, aod::DielectronCJetROs, soa::Join, aod::DielectronCMCDJetRs, soa::Join, soa::Join, aod::DielectronCMCDJetCOs, aod::DielectronCMCDJetOs, aod::DielectronCMCDJetSSOs, aod::DielectronCMCDJetMOs, aod::DielectronCMCDJetROs, aod::DielectronCMCDJetRMOs, soa::Join, soa::Join, aod::DielectronCMCPJetRs, soa::Join, soa::Join, aod::DielectronCMCPJetCOs, aod::DielectronCMCPJetMCCOs, aod::DielectronCMCPJetOs, aod::DielectronCMCPJetSSOs, aod::DielectronCMCPJetMOs, aod::DielectronCMCPJetROs, aod::DielectronCMCPJetRMOs, soa::Join, soa::Join, soa::Join, aod::DielectronCEWSJetCOs, aod::DielectronCEWSJetOs, aod::DielectronCEWSJetSSOs, aod::DielectronCEWSJetMOs, aod::StoredReducedEvents, aod::StoredDielectrons, aod::StoredDielectronsAll, aod::JDielectron1Dummys, aod::JDielectron2Dummys, aod::JDielectron3Dummys, aod::JDielectron4Dummys, aod::JDielectron5Dummys, aod::JDielectron6Dummys, aod::StoredJDielectronMcCollisions, aod::JDielectronMcRCollDummys, aod::StoredJDielectronMcs>; +using JetSubstructureOutputDielectron = JetSubstructureHFOutputTask, aod::McCollisionsDielectron, aod::CandidatesDielectronData, aod::CandidatesDielectronMCD, aod::CandidatesDielectronMCP, aod::BkgDielectronRhos, aod::BkgDielectronMcRhos, aod::JTrackDielectronSubs, soa::Join, soa::Join, aod::DielectronCJetRs, soa::Join, soa::Join, aod::DielectronCJetCOs, aod::DielectronCJetOs, aod::DielectronCJetSSOs, aod::DielectronCJetMOs, aod::DielectronCJetROs, soa::Join, aod::DielectronCMCDJetRs, soa::Join, soa::Join, aod::DielectronCMCDJetCOs, aod::DielectronCMCDJetOs, aod::DielectronCMCDJetSSOs, aod::DielectronCMCDJetMOs, aod::DielectronCMCDJetROs, aod::DielectronCMCDJetRMOs, soa::Join, soa::Join, aod::DielectronCMCPJetRs, soa::Join, soa::Join, aod::DielectronCMCPJetCOs, aod::DielectronCMCPJetMCCOs, aod::DielectronCMCPJetOs, aod::DielectronCMCPJetSSOs, aod::DielectronCMCPJetMOs, aod::DielectronCMCPJetROs, aod::DielectronCMCPJetRMOs, soa::Join, soa::Join, soa::Join, aod::DielectronCEWSJetCOs, aod::DielectronCEWSJetOs, aod::DielectronCEWSJetSSOs, aod::DielectronCEWSJetMOs, aod::StoredReducedEvents, aod::StoredDielectrons, aod::StoredDielectronsAll, aod::StoredJDielectron1Dummys, aod::StoredJDielectron2Dummys, aod::StoredJDielectron3Dummys, aod::StoredJDielectron4Dummys, aod::StoredJDielectron5Dummys, aod::StoredJDielectron6Dummys, aod::StoredJDielectronMcCollisions, aod::StoredJDielectronMcRCollDummys, aod::StoredJDielectronMcs>; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGJE/Tasks/jetSubstructureHF.h b/PWGJE/Tasks/jetSubstructureHF.h index 0dbe9aa6e3f..8ac281fad77 100644 --- a/PWGJE/Tasks/jetSubstructureHF.h +++ b/PWGJE/Tasks/jetSubstructureHF.h @@ -19,6 +19,7 @@ #include "PWGJE/Core/FastJetUtilities.h" #include "PWGJE/Core/JetDQUtilities.h" +#include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetFinder.h" #include "PWGJE/Core/JetFindingUtilities.h" #include "PWGJE/Core/JetHFUtilities.h" @@ -82,6 +83,7 @@ struct JetSubstructureHFTask { o2::framework::Configurable pairConstituentPtMin{"pairConstituentPtMin", 1.0, "pt cut off for constituents going into pairs"}; o2::framework::Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; o2::framework::Configurable recoilRegion{"recoilRegion", 0.6, "recoil acceptance in phi"}; + o2::framework::Configurable isEmbedding{"isEmbedding", false, "running on an embedded dataset"}; o2::framework::Service pdg; float candMass; @@ -135,7 +137,7 @@ struct JetSubstructureHFTask { candMass = jetcandidateutilities::getTablePDGMass(); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } o2::framework::Preslice TracksPerCollision = o2::aod::jtrack::collisionId; @@ -355,7 +357,7 @@ struct JetSubstructureHFTask { } if constexpr (!std::is_same_v, o2::aod::JetParticles>) { - if (!jetfindingutilities::isTrackSelected(track, trackSelection)) { + if (!jetfindingutilities::isTrackSelected(track, trackSelection, isEmbedding)) { continue; } } diff --git a/PWGJE/Tasks/jetSubstructureHFOutput.h b/PWGJE/Tasks/jetSubstructureHFOutput.h index 2eb799a6708..4c33d2d989e 100644 --- a/PWGJE/Tasks/jetSubstructureHFOutput.h +++ b/PWGJE/Tasks/jetSubstructureHFOutput.h @@ -139,8 +139,8 @@ struct JetSubstructureHFOutputTask { void init(o2::framework::InitContext const&) { - jetRadiiValues = (std::vector)configs.jetRadii; - recoilJetRadiiValues = (std::vector)configs.recoilJetRadii; + jetRadiiValues = configs.jetRadii.value; + recoilJetRadiiValues = configs.recoilJetRadii.value; } struct : o2::framework::PresliceGroup { diff --git a/PWGJE/Tasks/jetSubstructureHFOutputPCH.cxx b/PWGJE/Tasks/jetSubstructureHFOutputPCH.cxx new file mode 100644 index 00000000000..384b1987f80 --- /dev/null +++ b/PWGJE/Tasks/jetSubstructureHFOutputPCH.cxx @@ -0,0 +1,10 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. diff --git a/PWGJE/Tasks/jetSubstructureOutput.cxx b/PWGJE/Tasks/jetSubstructureOutput.cxx index 954214c7226..ef90a7e88c8 100644 --- a/PWGJE/Tasks/jetSubstructureOutput.cxx +++ b/PWGJE/Tasks/jetSubstructureOutput.cxx @@ -96,7 +96,7 @@ struct JetSubstructureOutputTask { void init(InitContext const&) { - jetRadiiValues = (std::vector)jetRadii; + jetRadiiValues = jetRadii; } Preslice> splittingsPerJetData = aod::chargedsplitting::jetId; @@ -308,7 +308,7 @@ struct JetSubstructureOutputTask { void processClearMapsMCP(aod::JetMcCollisions const& mcCollisions) { jetMappingMCP.clear(); - for (auto mcCollision : mcCollisions) { + for (const auto& mcCollision : mcCollisions) { mcCollisionOutputTable(mcCollision.posZ(), mcCollision.accepted(), mcCollision.attempted(), mcCollision.xsectGen(), mcCollision.xsectErr(), mcCollision.weight()); } } diff --git a/PWGJE/Tasks/jetTaggerHFQA.cxx b/PWGJE/Tasks/jetTaggerHFQA.cxx index dac7777daf7..b05c388d4ef 100644 --- a/PWGJE/Tasks/jetTaggerHFQA.cxx +++ b/PWGJE/Tasks/jetTaggerHFQA.cxx @@ -76,8 +76,6 @@ struct JetTaggerHFQA { Configurable checkMcCollisionIsMatched{"checkMcCollisionIsMatched", false, "0: count whole MCcollisions, 1: select MCcollisions which only have their correspond collisions"}; Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum occupancy of tracks in neighbouring collisions in a given time range; only applied to reconstructed collisions (data and mcd jets), not mc collisions (mcp jets)"}; - Configurable meanFT0A{"meanFT0A", -1., "Mean value of FT0A signal"}; - Configurable meanFT0C{"meanFT0C", -1., "Mean value of FT0C signal"}; Configurable eventSelections{"eventSelections", "sel8", "choose event selection"}; Configurable trackSelections{"trackSelections", "globalTracks", "set track selections"}; @@ -107,6 +105,7 @@ struct JetTaggerHFQA { ConfigurableAxis binMass{"binMass", {50, 0, 10.f}, ""}; ConfigurableAxis binSigmaLxy{"binSigmaLxy", {100, 0., 0.1}, ""}; ConfigurableAxis binSigmaLxyz{"binSigmaLxyz", {100, 0., 0.1}, ""}; + ConfigurableAxis binDispersion{"binDispersion", {200, 0., 1}, ""}; int numberOfJetFlavourSpecies = 6; std::vector eventSelectionBits; @@ -118,8 +117,8 @@ struct JetTaggerHFQA { { numberOfJetFlavourSpecies = static_cast(numFlavourSpecies); - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Axis AxisSpec axisJetFlavour = {binJetFlavour, "Jet flavour"}; AxisSpec axisJetPt = {binJetPt, "#it{p}_{T, jet}"}; @@ -147,6 +146,7 @@ struct JetTaggerHFQA { AxisSpec axisMass = {binMass, "#it{m}_{SV}"}; AxisSpec axisSigmaLxy = {binSigmaLxy, "#sigma_{L_{XY}} [cm]"}; AxisSpec axisSigmaLxyz = {binSigmaLxyz, "#sigma_{L_{XYZ}} [cm]"}; + AxisSpec axisDispersion = {binDispersion, "#sigma_{SV}"}; AxisSpec axisFracSecPt = {100, 0, 1, "#frac{#Sigma#it{p}_{T}^{secondary track}}{#it{p}_{T, jet}}"}; registry.add("h_collision_events", "data;mcd;mcp evnets", {HistType::kTH1F, {{4, 0.0, 4.0}}}); @@ -316,7 +316,7 @@ struct JetTaggerHFQA { } if (doprocessJPData) { - if (!doprocessIPsData && !doprocessSV2ProngData && !doprocessSV3ProngData) { + if (!doprocessIPsData && !doprocessSVData) { registry.add("h_jet_pt", "", {HistType::kTH1F, {{axisJetPt}}}); registry.add("h_jet_eta", "", {HistType::kTH1F, {{axisEta}}}); registry.add("h_jet_phi", "", {HistType::kTH1F, {{axisPhi}}}); @@ -331,7 +331,7 @@ struct JetTaggerHFQA { registry.add("h2_taggedjet_pt_neg_log_JP_N3", "jet pt jet probabilityun N3", {HistType::kTH2F, {{axisJetPt}, {axisJetProbabilityLog}}}); } if (doprocessJPMCD || doprocessJPMCDWeighted || doprocessJPMCPMCDMatched || doprocessJPMCPMCDMatchedWeighted) { - if (!(doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && !(doprocessSV2ProngMCD || doprocessSV2ProngMCDWeighted || doprocessSV2ProngMCPMCDMatched || doprocessSV2ProngMCPMCDMatchedWeighted) && !(doprocessSV3ProngMCD || doprocessSV3ProngMCDWeighted || doprocessSV3ProngMCPMCDMatched || doprocessSV3ProngMCPMCDMatchedWeighted)) { + if (!(doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && !(doprocessSVMCD || doprocessSVMCDWeighted || doprocessSVMCPMCDMatched || doprocessSVMCPMCDMatchedWeighted)) { registry.add("h2_jet_pt_flavour", "", {HistType::kTH2F, {{axisJetPt}, {axisJetFlavour}}}); registry.add("h2_jet_eta_flavour", "", {HistType::kTH2F, {{axisEta}, {axisJetFlavour}}}); registry.add("h2_jet_phi_flavour", "", {HistType::kTH2F, {{axisPhi}, {axisJetFlavour}}}); @@ -345,101 +345,55 @@ struct JetTaggerHFQA { registry.add("h3_taggedjet_pt_JP_N3_flavour", "jet pt jet probability flavour N3", {HistType::kTH3F, {{axisJetPt}, {axisJetProbability}, {axisJetFlavour}}}); registry.add("h3_taggedjet_pt_neg_log_JP_N3_flavour", "jet pt log jet probability flavour N3", {HistType::kTH3F, {{axisJetPt}, {axisJetProbabilityLog}, {axisJetFlavour}}}); } - if (doprocessSV2ProngData) { - if (!doprocessIPsData && !doprocessJPData && !doprocessSV3ProngData) { + if (doprocessSVData) { + if (!doprocessIPsData && !doprocessJPData) { registry.add("h_jet_pt", "", {HistType::kTH1F, {{axisJetPt}}}); registry.add("h_jet_eta", "", {HistType::kTH1F, {{axisEta}}}); registry.add("h_jet_phi", "", {HistType::kTH1F, {{axisPhi}}}); } if (fillGeneralSVQA) { - registry.add("h_2prong_nprongs", "", {HistType::kTH1F, {{axisNprongs}}}); - registry.add("h2_jet_pt_2prong_Lxy", "", {HistType::kTH2F, {{axisJetPt}, {axisLxy}}}); - registry.add("h2_jet_pt_2prong_sigmaLxy", "", {HistType::kTH2F, {{axisJetPt}, {axisSigmaLxy}}}); - registry.add("h2_jet_pt_2prong_Sxy", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); - registry.add("h2_jet_pt_2prong_Lxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisLxyz}}}); - registry.add("h2_jet_pt_2prong_sigmaLxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisSigmaLxyz}}}); - registry.add("h2_jet_pt_2prong_Sxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); - } - registry.add("h2_jet_pt_2prong_Sxy_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); - registry.add("h2_jet_pt_2prong_Sxyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); - registry.add("h2_jet_pt_2prong_mass_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - registry.add("h2_jet_pt_2prong_mass_xyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - registry.add("h2_taggedjet_pt_2prong_Sxy_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); - registry.add("h2_taggedjet_pt_2prong_Sxyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); - registry.add("h2_taggedjet_pt_2prong_mass_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - registry.add("h2_taggedjet_pt_2prong_mass_xyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - } - if (doprocessSV3ProngData) { - if (!doprocessIPsData && !doprocessJPData && !doprocessSV2ProngData) { - registry.add("h_jet_pt", "", {HistType::kTH1F, {{axisJetPt}}}); - registry.add("h_jet_eta", "", {HistType::kTH1F, {{axisEta}}}); - registry.add("h_jet_phi", "", {HistType::kTH1F, {{axisPhi}}}); - } - if (fillGeneralSVQA) { - registry.add("h_3prong_nprongs", "", {HistType::kTH1F, {{axisNprongs}}}); - registry.add("h2_jet_pt_3prong_Lxy", "", {HistType::kTH2F, {{axisJetPt}, {axisLxy}}}); - registry.add("h2_jet_pt_3prong_sigmaLxy", "", {HistType::kTH2F, {{axisJetPt}, {axisSigmaLxy}}}); - registry.add("h2_jet_pt_3prong_Sxy", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); - registry.add("h2_jet_pt_3prong_Lxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisLxyz}}}); - registry.add("h2_jet_pt_3prong_sigmaLxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisSigmaLxyz}}}); - registry.add("h2_jet_pt_3prong_Sxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); - } - registry.add("h2_jet_pt_3prong_Sxy_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); - registry.add("h2_jet_pt_3prong_Sxyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); - registry.add("h2_jet_pt_3prong_mass_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - registry.add("h2_jet_pt_3prong_mass_xyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - registry.add("h2_taggedjet_pt_3prong_Sxy_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); - registry.add("h2_taggedjet_pt_3prong_Sxyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); - registry.add("h2_taggedjet_pt_3prong_mass_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - registry.add("h2_taggedjet_pt_3prong_mass_xyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); - } - if (doprocessSV2ProngMCD || doprocessSV2ProngMCDWeighted || doprocessSV2ProngMCPMCDMatched || doprocessSV2ProngMCPMCDMatchedWeighted) { - if (!(doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && !(doprocessJPMCD || doprocessJPMCDWeighted || doprocessJPMCPMCDMatched || doprocessJPMCPMCDMatchedWeighted) && !(doprocessSV3ProngMCD || doprocessSV3ProngMCDWeighted || doprocessSV3ProngMCPMCDMatched || doprocessSV3ProngMCPMCDMatchedWeighted)) { + registry.add("h_nprongs", "", {HistType::kTH1F, {{axisNprongs}}}); + registry.add("h2_jet_pt_Lxy", "", {HistType::kTH2F, {{axisJetPt}, {axisLxy}}}); + registry.add("h2_jet_pt_sigmaLxy", "", {HistType::kTH2F, {{axisJetPt}, {axisSigmaLxy}}}); + registry.add("h2_jet_pt_Sxy", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); + registry.add("h2_jet_pt_Lxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisLxyz}}}); + registry.add("h2_jet_pt_sigmaLxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisSigmaLxyz}}}); + registry.add("h2_jet_pt_Sxyz", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); + registry.add("hn_jet_pt_Lxy_sigmaLxy_Sxy_dispersion", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisDispersion}}}); + } + registry.add("h2_jet_pt_Sxy_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); + registry.add("h2_jet_pt_Sxyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); + registry.add("h2_jet_pt_mass_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); + registry.add("h2_jet_pt_mass_xyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); + registry.add("h2_taggedjet_pt_Sxy_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxy}}}); + registry.add("h2_taggedjet_pt_Sxyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisSxyz}}}); + registry.add("h2_taggedjet_pt_mass_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); + registry.add("h2_taggedjet_pt_mass_xyz_N1", "", {HistType::kTH2F, {{axisJetPt}, {axisMass}}}); + } + if (doprocessSVMCD || doprocessSVMCDWeighted || doprocessSVMCPMCDMatched || doprocessSVMCPMCDMatchedWeighted) { + if (!(doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && !(doprocessJPMCD || doprocessJPMCDWeighted || doprocessJPMCPMCDMatched || doprocessJPMCPMCDMatchedWeighted)) { registry.add("h2_jet_pt_flavour", "", {HistType::kTH2F, {{axisJetPt}, {axisJetFlavour}}}); registry.add("h2_jet_eta_flavour", "", {HistType::kTH2F, {{axisEta}, {axisJetFlavour}}}); registry.add("h2_jet_phi_flavour", "", {HistType::kTH2F, {{axisPhi}, {axisJetFlavour}}}); } if (fillGeneralSVQA) { - registry.add("h2_2prong_nprongs_flavour", "", {HistType::kTH2F, {{axisNprongs}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_Lxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisLxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_sigmaLxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSigmaLxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_Sxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_Lxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisLxyz}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_sigmaLxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSigmaLxyz}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_Sxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); - } - registry.add("h3_jet_pt_2prong_Sxy_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_Sxyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_mass_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_2prong_mass_xyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_2prong_Sxy_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_2prong_Sxyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_2prong_mass_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_2prong_mass_xyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); - } - if (doprocessSV3ProngMCD || doprocessSV3ProngMCDWeighted || doprocessSV3ProngMCPMCDMatched || doprocessSV3ProngMCPMCDMatchedWeighted) { - if (!(doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && !(doprocessJPMCD || doprocessJPMCDWeighted || doprocessJPMCPMCDMatched || doprocessJPMCPMCDMatchedWeighted) && !(doprocessSV2ProngMCD || doprocessSV2ProngMCDWeighted || doprocessSV2ProngMCPMCDMatched || doprocessSV2ProngMCPMCDMatchedWeighted)) { - registry.add("h2_jet_pt_flavour", "", {HistType::kTH2F, {{axisJetPt}, {axisJetFlavour}}}); - registry.add("h2_jet_eta_flavour", "", {HistType::kTH2F, {{axisEta}, {axisJetFlavour}}}); - registry.add("h2_jet_phi_flavour", "", {HistType::kTH2F, {{axisPhi}, {axisJetFlavour}}}); - } - if (fillGeneralSVQA) { - registry.add("h2_3prong_nprongs_flavour", "", {HistType::kTH2F, {{axisNprongs}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_Lxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisLxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_sigmaLxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSigmaLxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_Sxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_Lxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisLxyz}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_sigmaLxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSigmaLxyz}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_Sxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); - } - registry.add("h3_jet_pt_3prong_Sxy_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_Sxyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_mass_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); - registry.add("h3_jet_pt_3prong_mass_xyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_3prong_Sxy_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_3prong_Sxyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_3prong_mass_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); - registry.add("h3_taggedjet_pt_3prong_mass_xyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); + registry.add("h2_nprongs_flavour", "", {HistType::kTH2F, {{axisNprongs}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_Lxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisLxy}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_sigmaLxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSigmaLxy}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_Sxy_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_Lxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisLxyz}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_sigmaLxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSigmaLxyz}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_Sxyz_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); + registry.add("hn_jet_pt_Lxy_sigmaLxy_Sxy_dispersion_flavour", "", {HistType::kTHnSparseF, {{axisJetPt}, {axisLxy}, {axisSigmaLxy}, {axisSxy}, {axisDispersion}, {axisJetFlavour}}}); + } + registry.add("h3_jet_pt_Sxy_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_Sxyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_mass_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); + registry.add("h3_jet_pt_mass_xyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); + registry.add("h3_taggedjet_pt_Sxy_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxy}, {axisJetFlavour}}}); + registry.add("h3_taggedjet_pt_Sxyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisSxyz}, {axisJetFlavour}}}); + registry.add("h3_taggedjet_pt_mass_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); + registry.add("h3_taggedjet_pt_mass_xyz_N1_flavour", "", {HistType::kTH3F, {{axisJetPt}, {axisMass}, {axisJetFlavour}}}); } } @@ -848,7 +802,7 @@ struct JetTaggerHFQA { if (jet.pt() > pTHatMaxMCD * pTHat) { return; } - if (!doprocessIPsData && !doprocessSV2ProngData && !doprocessSV3ProngData) { + if (!doprocessIPsData && !doprocessSVData) { registry.fill(HIST("h_jet_pt"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); @@ -870,7 +824,7 @@ struct JetTaggerHFQA { if (mcdjet.pt() > pTHatMaxMCD * pTHat) { return; } - if (!((doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && (doprocessSV2ProngMCD || doprocessSV2ProngMCDWeighted || doprocessSV2ProngMCPMCDMatched || doprocessSV2ProngMCPMCDMatchedWeighted) && (doprocessSV3ProngMCD || doprocessSV3ProngMCDWeighted || doprocessSV3ProngMCPMCDMatched || doprocessSV3ProngMCPMCDMatchedWeighted))) { + if (!((doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && (doprocessSVMCD || doprocessSVMCDWeighted || doprocessSVMCPMCDMatched || doprocessSVMCPMCDMatchedWeighted))) { registry.fill(HIST("h2_jet_pt_flavour"), mcdjet.pt(), mcdjet.origin(), eventWeight); registry.fill(HIST("h2_jet_eta_flavour"), mcdjet.eta(), mcdjet.origin(), eventWeight); registry.fill(HIST("h2_jet_phi_flavour"), mcdjet.phi(), mcdjet.origin(), eventWeight); @@ -886,14 +840,14 @@ struct JetTaggerHFQA { } template - void fillHistogramSV2ProngData(T const& jet, U const& /*prongs*/) + void fillHistogramSVData(T const& jet, U const& /*prongs*/) { float eventWeight = 1.0; float pTHat = 10. / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (jet.pt() > pTHatMaxMCD * pTHat) { return; } - if (!doprocessIPsData && !doprocessJPData && !doprocessSV3ProngData) { + if (!doprocessIPsData && !doprocessJPData) { registry.fill(HIST("h_jet_pt"), jet.pt(), eventWeight); registry.fill(HIST("h_jet_eta"), jet.eta(), eventWeight); registry.fill(HIST("h_jet_phi"), jet.phi(), eventWeight); @@ -901,15 +855,16 @@ struct JetTaggerHFQA { if (jet.template secondaryVertices_as().size() < 1) return; if (fillGeneralSVQA) { - registry.fill(HIST("h_2prong_nprongs"), jet.template secondaryVertices_as().size()); + registry.fill(HIST("h_nprongs"), jet.template secondaryVertices_as().size()); for (const auto& prong : jet.template secondaryVertices_as()) { - registry.fill(HIST("h2_jet_pt_2prong_Lxy"), jet.pt(), prong.decayLengthXY()); - registry.fill(HIST("h2_jet_pt_2prong_sigmaLxy"), jet.pt(), prong.errorDecayLengthXY()); - registry.fill(HIST("h2_jet_pt_2prong_Sxy"), jet.pt(), prong.decayLengthXY() / prong.errorDecayLengthXY()); + registry.fill(HIST("h2_jet_pt_Lxy"), jet.pt(), prong.decayLengthXY()); + registry.fill(HIST("h2_jet_pt_sigmaLxy"), jet.pt(), prong.errorDecayLengthXY()); + registry.fill(HIST("h2_jet_pt_Sxy"), jet.pt(), prong.decayLengthXY() / prong.errorDecayLengthXY()); + registry.fill(HIST("hn_jet_pt_Lxy_sigmaLxy_Sxy_dispersion"), jet.pt(), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), prong.dispersion()); if (fillSVxyz) { - registry.fill(HIST("h2_jet_pt_2prong_Lxyz"), jet.pt(), prong.decayLength()); - registry.fill(HIST("h2_jet_pt_2prong_sigmaLxyz"), jet.pt(), prong.errorDecayLength()); - registry.fill(HIST("h2_jet_pt_2prong_Sxyz"), jet.pt(), prong.decayLength() / prong.errorDecayLength()); + registry.fill(HIST("h2_jet_pt_Lxyz"), jet.pt(), prong.decayLength()); + registry.fill(HIST("h2_jet_pt_sigmaLxyz"), jet.pt(), prong.errorDecayLength()); + registry.fill(HIST("h2_jet_pt_Sxyz"), jet.pt(), prong.decayLength() / prong.errorDecayLength()); } } } @@ -918,11 +873,11 @@ struct JetTaggerHFQA { if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); auto massSV = bjetCand.m(); - registry.fill(HIST("h2_jet_pt_2prong_Sxy_N1"), jet.pt(), maxSxy); - registry.fill(HIST("h2_jet_pt_2prong_mass_N1"), jet.pt(), massSV); + registry.fill(HIST("h2_jet_pt_Sxy_N1"), jet.pt(), maxSxy); + registry.fill(HIST("h2_jet_pt_mass_N1"), jet.pt(), massSV); if (jet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("h2_taggedjet_pt_2prong_Sxy_N1"), jet.pt(), maxSxy); - registry.fill(HIST("h2_taggedjet_pt_2prong_mass_N1"), jet.pt(), massSV); + registry.fill(HIST("h2_taggedjet_pt_Sxy_N1"), jet.pt(), maxSxy); + registry.fill(HIST("h2_taggedjet_pt_mass_N1"), jet.pt(), massSV); } } if (fillSVxyz) { @@ -930,136 +885,25 @@ struct JetTaggerHFQA { if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("h2_jet_pt_2prong_Sxyz_N1"), jet.pt(), maxSxyz); - registry.fill(HIST("h2_jet_pt_2prong_mass_xyz_N1"), jet.pt(), massSV); + registry.fill(HIST("h2_jet_pt_Sxyz_N1"), jet.pt(), maxSxyz); + registry.fill(HIST("h2_jet_pt_mass_xyz_N1"), jet.pt(), massSV); if (jet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("h2_taggedjet_pt_2prong_Sxyz_N1"), jet.pt(), maxSxyz); - registry.fill(HIST("h2_taggedjet_pt_2prong_mass_xyz_N1"), jet.pt(), massSV); - } - } - } - } - - template - void fillHistogramSV3ProngData(T const& jet, U const& /*prongs*/) - { - float eventWeight = 1.0; - float pTHat = 10. / (std::pow(eventWeight, 1.0 / pTHatExponent)); - if (jet.pt() > pTHatMaxMCD * pTHat) { - return; - } - if (!doprocessIPsData && !doprocessJPData && !doprocessSV2ProngData) { - registry.fill(HIST("h_jet_pt"), jet.pt(), eventWeight); - registry.fill(HIST("h_jet_eta"), jet.eta(), eventWeight); - registry.fill(HIST("h_jet_phi"), jet.phi(), eventWeight); - } - if (jet.template secondaryVertices_as().size() < 1) - return; - if (fillGeneralSVQA) { - registry.fill(HIST("h_3prong_nprongs"), jet.template secondaryVertices_as().size()); - for (const auto& prong : jet.template secondaryVertices_as()) { - registry.fill(HIST("h2_jet_pt_3prong_Lxy"), jet.pt(), prong.decayLengthXY()); - registry.fill(HIST("h2_jet_pt_3prong_sigmaLxy"), jet.pt(), prong.errorDecayLengthXY()); - registry.fill(HIST("h2_jet_pt_3prong_Sxy"), jet.pt(), prong.decayLengthXY() / prong.errorDecayLengthXY()); - if (fillSVxyz) { - registry.fill(HIST("h2_jet_pt_3prong_Lxyz"), jet.pt(), prong.decayLength()); - registry.fill(HIST("h2_jet_pt_3prong_sigmaLxyz"), jet.pt(), prong.errorDecayLength()); - registry.fill(HIST("h2_jet_pt_3prong_Sxyz"), jet.pt(), prong.decayLength() / prong.errorDecayLength()); - } - } - } - bool checkSv = false; - auto bjetCand = jettaggingutilities::jetFromProngMaxDecayLength(jet, prongCuts->at(0), prongCuts->at(1), prongCuts->at(2), prongCuts->at(4), prongCuts->at(5), false, &checkSv); - if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { - auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); - auto massSV = bjetCand.m(); - registry.fill(HIST("h2_jet_pt_3prong_Sxy_N1"), jet.pt(), maxSxy); - registry.fill(HIST("h2_jet_pt_3prong_mass_N1"), jet.pt(), massSV); - if (jet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("h2_taggedjet_pt_3prong_Sxy_N1"), jet.pt(), maxSxy); - registry.fill(HIST("h2_taggedjet_pt_3prong_mass_N1"), jet.pt(), massSV); - } - } - if (fillSVxyz) { - auto bjetCandXYZ = jettaggingutilities::jetFromProngMaxDecayLength(jet, prongCuts->at(0), prongCuts->at(1), prongCuts->at(3), prongCuts->at(4), prongCuts->at(5), true, &checkSv); - if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { - auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); - auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("h2_jet_pt_3prong_Sxyz_N1"), jet.pt(), maxSxyz); - registry.fill(HIST("h2_jet_pt_3prong_mass_xyz_N1"), jet.pt(), massSV); - if (jet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("h2_taggedjet_pt_3prong_Sxyz_N1"), jet.pt(), maxSxyz); - registry.fill(HIST("h2_taggedjet_pt_3prong_mass_xyz_N1"), jet.pt(), massSV); - } - } - } - } - - template - void fillHistogramSV2ProngMCD(T const& mcdjet, U const& /*prongs*/, float eventWeight = 1.0) - { - float pTHat = 10. / (std::pow(eventWeight, 1.0 / pTHatExponent)); - if (mcdjet.pt() > pTHatMaxMCD * pTHat) { - return; - } - auto origin = mcdjet.origin(); - if (!((doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && (doprocessJPMCD || doprocessJPMCDWeighted || doprocessJPMCPMCDMatched || doprocessJPMCPMCDMatchedWeighted) && (doprocessSV3ProngMCD || doprocessSV3ProngMCDWeighted || doprocessSV3ProngMCPMCDMatched || doprocessSV3ProngMCPMCDMatchedWeighted))) { - registry.fill(HIST("h2_jet_pt_flavour"), mcdjet.pt(), origin, eventWeight); - registry.fill(HIST("h2_jet_eta_flavour"), mcdjet.eta(), origin, eventWeight); - registry.fill(HIST("h2_jet_phi_flavour"), mcdjet.phi(), origin, eventWeight); - } - if (mcdjet.template secondaryVertices_as().size() < 1) - return; - if (fillGeneralSVQA) { - registry.fill(HIST("h2_2prong_nprongs_flavour"), mcdjet.template secondaryVertices_as().size(), origin, eventWeight); - for (const auto& prong : mcdjet.template secondaryVertices_as()) { - registry.fill(HIST("h3_jet_pt_2prong_Lxy_flavour"), mcdjet.pt(), prong.decayLengthXY(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_2prong_sigmaLxy_flavour"), mcdjet.pt(), prong.errorDecayLengthXY(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_2prong_Sxy_flavour"), mcdjet.pt(), prong.decayLengthXY() / prong.errorDecayLengthXY(), origin, eventWeight); - if (fillSVxyz) { - registry.fill(HIST("h3_jet_pt_2prong_Lxyz_flavour"), mcdjet.pt(), prong.decayLength(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_2prong_sigmaLxyz_flavour"), mcdjet.pt(), prong.errorDecayLength(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_2prong_Sxyz_flavour"), mcdjet.pt(), prong.decayLength() / prong.errorDecayLength(), origin, eventWeight); - } - } - } - bool checkSv = false; - auto bjetCand = jettaggingutilities::jetFromProngMaxDecayLength(mcdjet, prongCuts->at(0), prongCuts->at(1), prongCuts->at(2), prongCuts->at(4), prongCuts->at(5), false, &checkSv); - if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { - auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); - auto massSV = bjetCand.m(); - registry.fill(HIST("h3_jet_pt_2prong_Sxy_N1_flavour"), mcdjet.pt(), maxSxy, origin, eventWeight); - registry.fill(HIST("h3_jet_pt_2prong_mass_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); - if (mcdjet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("h3_taggedjet_pt_2prong_Sxy_N1_flavour"), mcdjet.pt(), maxSxy, origin, eventWeight); - registry.fill(HIST("h3_taggedjet_pt_2prong_mass_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); - } - } - if (fillSVxyz) { - checkSv = false; - auto bjetCandXYZ = jettaggingutilities::jetFromProngMaxDecayLength(mcdjet, prongCuts->at(0), prongCuts->at(1), prongCuts->at(3), prongCuts->at(4), prongCuts->at(5), true, &checkSv); - if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { - auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); - auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("h3_jet_pt_2prong_Sxyz_N1_flavour"), mcdjet.pt(), maxSxyz, origin, eventWeight); - registry.fill(HIST("h3_jet_pt_2prong_mass_xyz_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); - if (mcdjet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("h3_taggedjet_pt_2prong_Sxyz_N1_flavour"), mcdjet.pt(), maxSxyz, origin, eventWeight); - registry.fill(HIST("h3_taggedjet_pt_2prong_mass_xyz_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); + registry.fill(HIST("h2_taggedjet_pt_Sxyz_N1"), jet.pt(), maxSxyz); + registry.fill(HIST("h2_taggedjet_pt_mass_xyz_N1"), jet.pt(), massSV); } } } } template - void fillHistogramSV3ProngMCD(T const& mcdjet, U const& /*prongs*/, float eventWeight = 1.0) + void fillHistogramSVMCD(T const& mcdjet, U const& /*prongs*/, float eventWeight = 1.0) { float pTHat = 10. / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (mcdjet.pt() > pTHatMaxMCD * pTHat) { return; } auto origin = mcdjet.origin(); - if (!((doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && (doprocessJPMCD || doprocessJPMCDWeighted || doprocessJPMCPMCDMatched || doprocessJPMCPMCDMatchedWeighted) && (doprocessSV2ProngMCD || doprocessSV2ProngMCDWeighted || doprocessSV2ProngMCPMCDMatched || doprocessSV2ProngMCPMCDMatchedWeighted))) { + if (!((doprocessIPsMCD || doprocessIPsMCDWeighted || doprocessIPsMCPMCDMatched || doprocessIPsMCPMCDMatchedWeighted) && (doprocessJPMCD || doprocessJPMCDWeighted || doprocessJPMCPMCDMatched || doprocessJPMCPMCDMatchedWeighted))) { registry.fill(HIST("h2_jet_pt_flavour"), mcdjet.pt(), origin, eventWeight); registry.fill(HIST("h2_jet_eta_flavour"), mcdjet.eta(), origin, eventWeight); registry.fill(HIST("h2_jet_phi_flavour"), mcdjet.phi(), origin, eventWeight); @@ -1067,15 +911,16 @@ struct JetTaggerHFQA { if (mcdjet.template secondaryVertices_as().size() < 1) return; if (fillGeneralSVQA) { - registry.fill(HIST("h2_3prong_nprongs_flavour"), mcdjet.template secondaryVertices_as().size(), origin); + registry.fill(HIST("h2_nprongs_flavour"), mcdjet.template secondaryVertices_as().size(), origin); for (const auto& prong : mcdjet.template secondaryVertices_as()) { - registry.fill(HIST("h3_jet_pt_3prong_Lxy_flavour"), mcdjet.pt(), prong.decayLengthXY(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_3prong_sigmaLxy_flavour"), mcdjet.pt(), prong.errorDecayLengthXY(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_3prong_Sxy_flavour"), mcdjet.pt(), prong.decayLengthXY() / prong.errorDecayLengthXY(), origin, eventWeight); + registry.fill(HIST("h3_jet_pt_Lxy_flavour"), mcdjet.pt(), prong.decayLengthXY(), origin, eventWeight); + registry.fill(HIST("h3_jet_pt_sigmaLxy_flavour"), mcdjet.pt(), prong.errorDecayLengthXY(), origin, eventWeight); + registry.fill(HIST("h3_jet_pt_Sxy_flavour"), mcdjet.pt(), prong.decayLengthXY() / prong.errorDecayLengthXY(), origin, eventWeight); + registry.fill(HIST("hn_jet_pt_Lxy_sigmaLxy_Sxy_dispersion"), mcdjet.pt(), prong.decayLengthXY(), prong.errorDecayLengthXY(), prong.decayLengthXY() / prong.errorDecayLengthXY(), prong.dispersion(), origin, eventWeight); if (fillSVxyz) { - registry.fill(HIST("h3_jet_pt_3prong_Lxyz_flavour"), mcdjet.pt(), prong.decayLength(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_3prong_Sxyz_flavour"), mcdjet.pt(), prong.decayLength() / prong.errorDecayLength(), origin, eventWeight); - registry.fill(HIST("h3_jet_pt_3prong_sigmaLxyz_flavour"), mcdjet.pt(), prong.errorDecayLength(), origin, eventWeight); + registry.fill(HIST("h3_jet_pt_Lxyz_flavour"), mcdjet.pt(), prong.decayLength(), origin, eventWeight); + registry.fill(HIST("h3_jet_pt_Sxyz_flavour"), mcdjet.pt(), prong.decayLength() / prong.errorDecayLength(), origin, eventWeight); + registry.fill(HIST("h3_jet_pt_sigmaLxyz_flavour"), mcdjet.pt(), prong.errorDecayLength(), origin, eventWeight); } } } @@ -1084,11 +929,11 @@ struct JetTaggerHFQA { if (checkSv && jettaggingutilities::svAcceptance(bjetCand, svDispersionMax)) { auto maxSxy = bjetCand.decayLengthXY() / bjetCand.errorDecayLengthXY(); auto massSV = bjetCand.m(); - registry.fill(HIST("h3_jet_pt_3prong_Sxy_N1_flavour"), mcdjet.pt(), maxSxy, origin, eventWeight); - registry.fill(HIST("h3_jet_pt_3prong_mass_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); + registry.fill(HIST("h3_jet_pt_Sxy_N1_flavour"), mcdjet.pt(), maxSxy, origin, eventWeight); + registry.fill(HIST("h3_jet_pt_mass_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); if (mcdjet.isTagged(BJetTaggingMethod::SV)) { - registry.fill(HIST("h3_taggedjet_pt_3prong_Sxy_N1_flavour"), mcdjet.pt(), maxSxy, origin, eventWeight); - registry.fill(HIST("h3_taggedjet_pt_3prong_mass_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); + registry.fill(HIST("h3_taggedjet_pt_Sxy_N1_flavour"), mcdjet.pt(), maxSxy, origin, eventWeight); + registry.fill(HIST("h3_taggedjet_pt_mass_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); } } if (fillSVxyz) { @@ -1097,11 +942,11 @@ struct JetTaggerHFQA { if (checkSv && jettaggingutilities::svAcceptance(bjetCandXYZ, svDispersionMax)) { auto maxSxyz = bjetCandXYZ.decayLength() / bjetCandXYZ.errorDecayLength(); auto massSV = bjetCandXYZ.m(); - registry.fill(HIST("h3_jet_pt_3prong_Sxyz_N1_flavour"), mcdjet.pt(), maxSxyz, origin, eventWeight); - registry.fill(HIST("h3_jet_pt_3prong_mass_xyz_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); + registry.fill(HIST("h3_jet_pt_Sxyz_N1_flavour"), mcdjet.pt(), maxSxyz, origin, eventWeight); + registry.fill(HIST("h3_jet_pt_mass_xyz_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); if (mcdjet.isTagged(BJetTaggingMethod::SV3D)) { - registry.fill(HIST("h3_taggedjet_pt_3prong_Sxyz_N1_flavour"), mcdjet.pt(), maxSxyz, origin, eventWeight); - registry.fill(HIST("h3_taggedjet_pt_3prong_mass_xyz_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); + registry.fill(HIST("h3_taggedjet_pt_Sxyz_N1_flavour"), mcdjet.pt(), maxSxyz, origin, eventWeight); + registry.fill(HIST("h3_taggedjet_pt_mass_xyz_N1_flavour"), mcdjet.pt(), massSV, origin, eventWeight); } } } @@ -1563,7 +1408,7 @@ struct JetTaggerHFQA { } PROCESS_SWITCH(JetTaggerHFQA, processJPMCPMCDMatchedWeighted, "Fill jet probability imformation for mcd jets", false); - void processSV2ProngData(soa::Filtered::iterator const& collision, soa::Join const& jets, aod::DataSecondaryVertex2Prongs const& prongs) + void processSVData(soa::Filtered::iterator const& collision, soa::Join const& jets, aod::DataSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -1576,116 +1421,12 @@ struct JetTaggerHFQA { if (!isAcceptedJet(jet)) { continue; } - fillHistogramSV2ProngData(jet, prongs); - } - } - PROCESS_SWITCH(JetTaggerHFQA, processSV2ProngData, "Fill 2prong imformation for data jets", false); - - void processSV3ProngData(soa::Filtered::iterator const& collision, soa::Join const& jets, aod::DataSecondaryVertex3Prongs const& prongs) - { - if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { - return; - } - registry.fill(HIST("h_collision_events"), 0.5); // mcd events - for (auto const& jet : jets) { - if (!jetfindingutilities::isInEtaAcceptance(jet, jetEtaCuts->at(0), jetEtaCuts->at(1), trackCuts->at(2), trackCuts->at(3))) { - continue; - } - if (!isAcceptedJet(jet)) { - continue; - } - fillHistogramSV3ProngData(jet, prongs); - } - } - PROCESS_SWITCH(JetTaggerHFQA, processSV3ProngData, "Fill 3prong imformation for data jets", false); - - void processSV2ProngMCD(soa::Filtered::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertex2Prongs const& prongs) - { - if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { - return; - } - registry.fill(HIST("h_collision_events"), 1.5); // mcd events - for (auto const& mcdjet : mcdjets) { - if (!jetfindingutilities::isInEtaAcceptance(mcdjet, jetEtaCuts->at(0), jetEtaCuts->at(1), trackCuts->at(2), trackCuts->at(3))) { - continue; - } - if (!isAcceptedJet(mcdjet)) { - continue; - } - fillHistogramSV2ProngMCD(mcdjet, prongs); - } - } - PROCESS_SWITCH(JetTaggerHFQA, processSV2ProngMCD, "Fill 2prong imformation for mcd jets", false); - - void processSV2ProngMCDWeighted(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertex2Prongs const& prongs) - { - if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { - return; - } - if (collision.isOutlier()) { - return; - } - registry.fill(HIST("h_collision_events"), 1.5, collision.weight()); // mcd events - for (auto const& mcdjet : mcdjets) { - if (!jetfindingutilities::isInEtaAcceptance(mcdjet, jetEtaCuts->at(0), jetEtaCuts->at(1), trackCuts->at(2), trackCuts->at(3))) { - continue; - } - if (!isAcceptedJet(mcdjet)) { - continue; - } - fillHistogramSV2ProngMCD(mcdjet, prongs, collision.weight()); - } - } - PROCESS_SWITCH(JetTaggerHFQA, processSV2ProngMCDWeighted, "Fill 2prong imformation for mcd jets", false); - - void processSV2ProngMCPMCDMatched(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertex2Prongs const& prongs) - { - if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { - return; - } - registry.fill(HIST("h_collision_events"), 1.5); // mcd events - for (auto const& mcdjet : mcdjets) { - if (!jetfindingutilities::isInEtaAcceptance(mcdjet, jetEtaCuts->at(0), jetEtaCuts->at(1), trackCuts->at(2), trackCuts->at(3))) { - continue; - } - if (!isAcceptedJet(mcdjet)) { - continue; - } - if (!mcdjet.has_matchedJetGeo()) { - continue; - } - if (!doprocessSV2ProngMCD) - fillHistogramSV2ProngMCD(mcdjet, prongs); - } - } - PROCESS_SWITCH(JetTaggerHFQA, processSV2ProngMCPMCDMatched, "Fill 2prong imformation for mcd jets", false); - - void processSV2ProngMCPMCDMatchedWeighted(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertex2Prongs const& prongs) - { - if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { - return; - } - if (collision.isOutlier()) { - return; - } - registry.fill(HIST("h_collision_events"), 1.5, collision.weight()); // mcd events - for (auto const& mcdjet : mcdjets) { - if (!jetfindingutilities::isInEtaAcceptance(mcdjet, jetEtaCuts->at(0), jetEtaCuts->at(1), trackCuts->at(2), trackCuts->at(3))) { - continue; - } - if (!isAcceptedJet(mcdjet)) { - continue; - } - if (!mcdjet.has_matchedJetGeo()) { - continue; - } - if (!doprocessSV2ProngMCDWeighted) - fillHistogramSV2ProngMCD(mcdjet, prongs, collision.weight()); + fillHistogramSVData(jet, prongs); } } - PROCESS_SWITCH(JetTaggerHFQA, processSV2ProngMCPMCDMatchedWeighted, "Fill 2prong imformation for mcd jets", false); + PROCESS_SWITCH(JetTaggerHFQA, processSVData, "Fill n-prong imformation for data jets", false); - void processSV3ProngMCD(soa::Filtered::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertex3Prongs const& prongs) + void processSVMCD(soa::Filtered::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -1698,12 +1439,12 @@ struct JetTaggerHFQA { if (!isAcceptedJet(mcdjet)) { continue; } - fillHistogramSV3ProngMCD(mcdjet, prongs); + fillHistogramSVMCD(mcdjet, prongs); } } - PROCESS_SWITCH(JetTaggerHFQA, processSV3ProngMCD, "Fill 3prong imformation for mcd jets", false); + PROCESS_SWITCH(JetTaggerHFQA, processSVMCD, "Fill n-prong imformation for mcd jets", false); - void processSV3ProngMCDWeighted(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertex3Prongs const& prongs) + void processSVMCDWeighted(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -1719,12 +1460,12 @@ struct JetTaggerHFQA { if (!isAcceptedJet(mcdjet)) { continue; } - fillHistogramSV3ProngMCD(mcdjet, prongs, collision.weight()); + fillHistogramSVMCD(mcdjet, prongs, collision.weight()); } } - PROCESS_SWITCH(JetTaggerHFQA, processSV3ProngMCDWeighted, "Fill 3prong imformation for mcd jets", false); + PROCESS_SWITCH(JetTaggerHFQA, processSVMCDWeighted, "Fill n-prong imformation for mcd jets", false); - void processSV3ProngMCPMCDMatched(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertex3Prongs const& prongs) + void processSVMCPMCDMatched(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -1740,13 +1481,13 @@ struct JetTaggerHFQA { if (!mcdjet.has_matchedJetGeo()) { continue; } - if (!doprocessSV3ProngMCD) - fillHistogramSV3ProngMCD(mcdjet, prongs); + if (!doprocessSVMCD) + fillHistogramSVMCD(mcdjet, prongs); } } - PROCESS_SWITCH(JetTaggerHFQA, processSV3ProngMCPMCDMatched, "Fill 3prong imformation for mcd jets", false); + PROCESS_SWITCH(JetTaggerHFQA, processSVMCPMCDMatched, "Fill n-prong imformation for mcd jets", false); - void processSV3ProngMCPMCDMatchedWeighted(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertex3Prongs const& prongs) + void processSVMCPMCDMatchedWeighted(soa::Filtered>::iterator const& collision, soa::Join const& mcdjets, soa::Join const& /*mcpjets*/, aod::MCDSecondaryVertexNProngs const& prongs) { if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { return; @@ -1765,11 +1506,11 @@ struct JetTaggerHFQA { if (!mcdjet.has_matchedJetGeo()) { continue; } - if (!doprocessSV3ProngMCDWeighted) - fillHistogramSV3ProngMCD(mcdjet, prongs, collision.weight()); + if (!doprocessSVMCDWeighted) + fillHistogramSVMCD(mcdjet, prongs, collision.weight()); } } - PROCESS_SWITCH(JetTaggerHFQA, processSV3ProngMCPMCDMatchedWeighted, "Fill 3prong imformation for mcd jets", false); + PROCESS_SWITCH(JetTaggerHFQA, processSVMCPMCDMatchedWeighted, "Fill n-prong imformation for mcd jets", false); }; using JetTaggerQAChargedDataJets = soa::Join; diff --git a/PWGJE/Tasks/jetTriggerChargedQa.cxx b/PWGJE/Tasks/jetTriggerChargedQa.cxx index 22ee5b8306d..bb491523d27 100644 --- a/PWGJE/Tasks/jetTriggerChargedQa.cxx +++ b/PWGJE/Tasks/jetTriggerChargedQa.cxx @@ -97,7 +97,7 @@ struct JetTriggerChargedQa { { fiducialVolume = static_cast(cfgTPCVolume) - static_cast(cfgJetR); eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(evSel)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // Basic histos spectra.add("vertexZ", "z vertex", kTH1F, {{60, -12., 12.}}); diff --git a/PWGJE/Tasks/jetTutorial.cxx b/PWGJE/Tasks/jetTutorial.cxx index 828a23a86ce..25e89677000 100644 --- a/PWGJE/Tasks/jetTutorial.cxx +++ b/PWGJE/Tasks/jetTutorial.cxx @@ -9,10 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// jet tutorial task for hands on tutorial session (16/11/2024) -// +/// \file jetTutorial.cxx +/// \brief jet tutorial task for hands on tutorial session (16/11/2024) /// \author Nima Zardoshti -// #include "PWGJE/Core/JetDerivedDataUtilities.h" #include "PWGJE/Core/JetUtilities.h" @@ -42,7 +41,7 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -struct JetTutorialTask { +struct JetTutorial { HistogramRegistry registry{"registry", {{"h_collisions", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}}, {"h_track_pt", "track pT;#it{p}_{T,track} (GeV/#it{c});entries", {HistType::kTH1F, {{200, 0., 200.}}}}, @@ -94,8 +93,8 @@ struct JetTutorialTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); } @@ -122,7 +121,7 @@ struct JetTutorialTask { registry.fill(HIST("h_track_phi"), track.phi()); } } - PROCESS_SWITCH(JetTutorialTask, processCollisions, "process JE collisions", false); + PROCESS_SWITCH(JetTutorial, processCollisions, "process JE collisions", false); void processCollisionsWithExternalTracks(soa::Filtered::iterator const& collision, soa::Join const& tracks, soa::Join const&) { @@ -143,75 +142,75 @@ struct JetTutorialTask { registry.fill(HIST("h_track_chi2PerCluster"), originalTrack.tpcChi2NCl()); } } - PROCESS_SWITCH(JetTutorialTask, processCollisionsWithExternalTracks, "process JE collisions with access to the original track table", false); + PROCESS_SWITCH(JetTutorial, processCollisionsWithExternalTracks, "process JE collisions with access to the original track table", false); void processDataCharged(soa::Filtered::iterator const& collision, soa::Filtered const& jets) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); } } - PROCESS_SWITCH(JetTutorialTask, processDataCharged, "charged jets in data", false); + PROCESS_SWITCH(JetTutorial, processDataCharged, "charged jets in data", false); void processMCDetectorLevelCharged(soa::Filtered::iterator const& collision, soa::Filtered const& jets) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); } } - PROCESS_SWITCH(JetTutorialTask, processMCDetectorLevelCharged, "charged jets in detector level MC", false); + PROCESS_SWITCH(JetTutorial, processMCDetectorLevelCharged, "charged jets in detector level MC", false); void processMCDetectorLevelWeightedCharged(soa::Filtered::iterator const& collision, aod::JetMcCollisions const&, soa::Filtered const& jets) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt"), jet.pt(), collision.mcCollision().weight()); registry.fill(HIST("h_jet_eta"), jet.eta(), collision.mcCollision().weight()); registry.fill(HIST("h_jet_phi"), jet.phi(), collision.mcCollision().weight()); } } - PROCESS_SWITCH(JetTutorialTask, processMCDetectorLevelWeightedCharged, "charged jets in weighted detector level MC", false); + PROCESS_SWITCH(JetTutorial, processMCDetectorLevelWeightedCharged, "charged jets in weighted detector level MC", false); void processMCParticleLevelCharged(soa::Filtered::iterator const& mcCollision, soa::Filtered const& jets) { - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_part_jet_pt"), jet.pt(), mcCollision.weight()); registry.fill(HIST("h_part_jet_eta"), jet.eta(), mcCollision.weight()); registry.fill(HIST("h_part_jet_phi"), jet.phi(), mcCollision.weight()); } } - PROCESS_SWITCH(JetTutorialTask, processMCParticleLevelCharged, "charged jets in particle level MC", false); + PROCESS_SWITCH(JetTutorial, processMCParticleLevelCharged, "charged jets in particle level MC", false); void processMCCharged(soa::Filtered::iterator const& collision, aod::JetMcCollisions const&, soa::Filtered const& mcdjets, soa::Filtered const& mcpjets) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto& mcdjet : mcdjets) { + for (const auto& mcdjet : mcdjets) { registry.fill(HIST("h_jet_pt"), mcdjet.pt(), collision.mcCollision().weight()); registry.fill(HIST("h_jet_eta"), mcdjet.eta(), collision.mcCollision().weight()); registry.fill(HIST("h_jet_phi"), mcdjet.phi(), collision.mcCollision().weight()); } auto mcpjetsPerCollision = mcpjets.sliceBy(perMcCollisionJets, collision.mcCollisionId()); - for (auto& mcpjet : mcpjetsPerCollision) { + for (const auto& mcpjet : mcpjetsPerCollision) { registry.fill(HIST("h_part_jet_pt"), mcpjet.pt(), collision.mcCollision().weight()); registry.fill(HIST("h_part_jet_eta"), mcpjet.eta(), collision.mcCollision().weight()); registry.fill(HIST("h_part_jet_phi"), mcpjet.phi(), collision.mcCollision().weight()); } } - PROCESS_SWITCH(JetTutorialTask, processMCCharged, "charged jets in detector and particle level MC", false); + PROCESS_SWITCH(JetTutorial, processMCCharged, "charged jets in detector and particle level MC", false); using JetMCPTable = soa::Filtered>; void processMCMatchedCharged(soa::Filtered::iterator const& collision, @@ -225,79 +224,79 @@ struct JetTutorialTask { return; } for (const auto& mcdjet : mcdjets) { - for (auto& mcpjet : mcdjet.template matchedJetGeo_as()) { + for (const auto& mcpjet : mcdjet.template matchedJetGeo_as()) { registry.fill(HIST("h_matched_jets_pt"), mcpjet.pt(), mcdjet.pt(), collision.mcCollision().weight()); registry.fill(HIST("h_matched_jets_phi"), mcpjet.phi(), mcdjet.phi(), collision.mcCollision().weight()); registry.fill(HIST("h_matched_jets_eta"), mcpjet.eta(), mcdjet.eta(), collision.mcCollision().weight()); } } } - PROCESS_SWITCH(JetTutorialTask, processMCMatchedCharged, "matched detector and particle level charged jets", false); + PROCESS_SWITCH(JetTutorial, processMCMatchedCharged, "matched detector and particle level charged jets", false); void processDataSubstructureCharged(soa::Filtered::iterator const& collision, soa::Filtered> const& jets, aod::JetTracks const&) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); registry.fill(HIST("h_jet_ntracks"), jet.tracksIds().size()); double angularity = 0.0; - for (auto& jetConstituent : jet.tracks_as()) { - angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent), alpha); + for (const auto& jetConstituent : jet.tracks_as()) { + angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent) / (jet.r() / 100.f), alpha); } - angularity /= (jet.pt() * (jet.r() / 100.f)); + angularity /= std::pow(jet.pt(), kappa); registry.fill(HIST("h_jet_angularity"), angularity); } } - PROCESS_SWITCH(JetTutorialTask, processDataSubstructureCharged, "charged jet substructure", false); + PROCESS_SWITCH(JetTutorial, processDataSubstructureCharged, "charged jet substructure", false); void processDataFull(soa::Filtered::iterator const&, soa::Filtered const& jets) { - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); } } - PROCESS_SWITCH(JetTutorialTask, processDataFull, "full jets in data", false); + PROCESS_SWITCH(JetTutorial, processDataFull, "full jets in data", false); void processDataSubstructureFull(soa::Filtered::iterator const&, soa::Filtered> const& jets, aod::JetTracks const&, aod::JetClusters const&) { - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_full_jet_pt"), jet.pt()); registry.fill(HIST("h_full_jet_eta"), jet.eta()); registry.fill(HIST("h_full_jet_phi"), jet.phi()); registry.fill(HIST("h_full_jet_ntracks"), jet.tracksIds().size()); registry.fill(HIST("h_full_jet_nclusters"), jet.clustersIds().size()); double angularity = 0.0; - for (auto& jetConstituent : jet.tracks_as()) { - angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent), alpha); + for (const auto& jetConstituent : jet.tracks_as()) { + angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent) / (jet.r() / 100.f), alpha); } - for (auto& jetCluster : jet.clusters_as()) { - angularity += std::pow(jetCluster.energy(), kappa) * std::pow(jetutilities::deltaR(jet, jetCluster), alpha); + for (const auto& jetCluster : jet.clusters_as()) { + angularity += std::pow(jetCluster.energy(), kappa) * std::pow(jetutilities::deltaR(jet, jetCluster) / (jet.r() / 100.f), alpha); } - registry.fill(HIST("h_full_jet_angularity"), angularity / (jet.pt() * round(jet.r() * 100.0f))); + registry.fill(HIST("h_full_jet_angularity"), angularity / std::pow(jet.pt(), kappa)); } } - PROCESS_SWITCH(JetTutorialTask, processDataSubstructureFull, "full jet substructure", false); + PROCESS_SWITCH(JetTutorial, processDataSubstructureFull, "full jet substructure", false); void processMCParticleLevelSubstructureFull(soa::Filtered::iterator const& mcCollision, soa::Filtered> const& jets, aod::JetParticles const&) { - for (auto& jet : jets) { + for (const auto& jet : jets) { double angularity = 0.0; - for (auto& jetConstituent : jet.tracks_as()) { - angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent), alpha); + for (const auto& jetConstituent : jet.tracks_as()) { + angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent) / (jet.r() / 100.f), alpha); } - angularity /= (jet.pt() * (jet.r() / 100.f)); + angularity /= std::pow(jet.pt(), kappa); registry.fill(HIST("h_part_jet_angularity"), angularity, mcCollision.weight()); } } - PROCESS_SWITCH(JetTutorialTask, processMCParticleLevelSubstructureFull, "full particle level jet substructure", false); + PROCESS_SWITCH(JetTutorial, processMCParticleLevelSubstructureFull, "full particle level jet substructure", false); void processRecoilDataCharged(soa::Filtered::iterator const& collision, soa::Filtered const& jets, aod::JetTracks const& tracks) { @@ -307,8 +306,10 @@ struct JetTutorialTask { bool selectedEvent = false; double leadingTrackpT = 0.0; double leadingTrackPhi = 0.0; - for (auto& track : tracks) { - if (track.pt() > 6.0 && track.pt() < 10.0) { + double trackpTMin = 6.0; + double trackpTMax = 10.0; + for (const auto& track : tracks) { + if (track.pt() > trackpTMin && track.pt() < trackpTMax) { if (track.pt() > leadingTrackpT) { leadingTrackpT = track.pt(); leadingTrackPhi = track.phi(); @@ -319,8 +320,9 @@ struct JetTutorialTask { if (!selectedEvent) { return; } - for (auto& jet : jets) { - if (std::abs(RecoDecay::constrainAngle(jet.phi() - leadingTrackPhi, -o2::constants::math::PIHalf)) > 0.6) { + double phiRange = 0.6; + for (const auto& jet : jets) { + if (std::abs(RecoDecay::constrainAngle(jet.phi() - leadingTrackPhi, -o2::constants::math::PIHalf)) > phiRange) { registry.fill(HIST("h_recoil_jet_pt"), jet.pt()); registry.fill(HIST("h_recoil_jet_eta"), jet.eta()); registry.fill(HIST("h_recoil_jet_phi"), jet.phi()); @@ -328,67 +330,67 @@ struct JetTutorialTask { } } } - PROCESS_SWITCH(JetTutorialTask, processRecoilDataCharged, "hadron-recoil charged jets", false); + PROCESS_SWITCH(JetTutorial, processRecoilDataCharged, "hadron-recoil charged jets", false); void processDataRhoAreaSubtractedCharged(soa::Filtered>::iterator const& collision, soa::Filtered const& jets) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt"), jet.pt()); registry.fill(HIST("h_jet_pt_rhosub"), jet.pt() - (collision.rho() * jet.area())); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); } } - PROCESS_SWITCH(JetTutorialTask, processDataRhoAreaSubtractedCharged, "charged rho-area subtracted jets", false); + PROCESS_SWITCH(JetTutorial, processDataRhoAreaSubtractedCharged, "charged rho-area subtracted jets", false); void processDataConstituentSubtractedCharged(soa::Filtered::iterator const& collision, soa::Filtered const& jets) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt_constsub"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); } } - PROCESS_SWITCH(JetTutorialTask, processDataConstituentSubtractedCharged, "charged constituent subtracted jets", false); + PROCESS_SWITCH(JetTutorial, processDataConstituentSubtractedCharged, "charged constituent subtracted jets", false); void processDataConstituentSubtractedSubstructureCharged(soa::Filtered::iterator const& collision, soa::Filtered> const& jets, aod::JetTracksSub const&) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { return; } - for (auto jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt_constsub"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); registry.fill(HIST("h_jet_ntracks"), jet.tracksIds().size()); double angularity = 0.0; - for (auto& jetConstituent : jet.tracks_as()) { - angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent), alpha); + for (const auto& jetConstituent : jet.tracks_as()) { + angularity += std::pow(jetConstituent.pt(), kappa) * std::pow(jetutilities::deltaR(jet, jetConstituent) / (jet.r() / 100.f), alpha); } - angularity /= (jet.pt() * (jet.r() / 100.f)); + angularity /= std::pow(jet.pt(), kappa); registry.fill(HIST("h_jet_angularity_constsub"), angularity); } } - PROCESS_SWITCH(JetTutorialTask, processDataConstituentSubtractedSubstructureCharged, "charged constituent subtracted jet substructure", false); + PROCESS_SWITCH(JetTutorial, processDataConstituentSubtractedSubstructureCharged, "charged constituent subtracted jet substructure", false); void processDataTriggered(soa::Filtered::iterator const& collision, soa::Filtered const& jets) { if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits) || !jetderiveddatautilities::selectTrigger(collision, triggerMaskBits)) { return; } - for (auto& jet : jets) { + for (const auto& jet : jets) { registry.fill(HIST("h_jet_pt"), jet.pt()); registry.fill(HIST("h_jet_eta"), jet.eta()); registry.fill(HIST("h_jet_phi"), jet.phi()); } } - PROCESS_SWITCH(JetTutorialTask, processDataTriggered, "jets triggered", false); + PROCESS_SWITCH(JetTutorial, processDataTriggered, "jets triggered", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc, TaskName{"jet-tutorial"})}; } +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGJE/Tasks/jetTutorialSkeleton.cxx b/PWGJE/Tasks/jetTutorialSkeleton.cxx index 763f2dc4c0e..e5de1f294d8 100644 --- a/PWGJE/Tasks/jetTutorialSkeleton.cxx +++ b/PWGJE/Tasks/jetTutorialSkeleton.cxx @@ -84,8 +84,8 @@ struct JetTutorialSkeletonTask { void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); } diff --git a/PWGJE/Tasks/jetUpcQa.cxx b/PWGJE/Tasks/jetUpcQa.cxx new file mode 100644 index 00000000000..ed86c080f57 --- /dev/null +++ b/PWGJE/Tasks/jetUpcQa.cxx @@ -0,0 +1,1008 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file jetUpcQa.cxx +/// \brief Standalone QA task for UPC event tagging and charged-jet information. +/// \author Jaehyeok Ryu + +#include "PWGUD/Core/SGCutParHolder.h" +#include "PWGUD/Core/SGSelector.h" + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace o2::aod +{ + +// ============================================================ +// DATA tables +// ============================================================ +namespace upcjetevent +{ +DECLARE_SOA_COLUMN(GapSide, gapSide, int); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); +DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); +DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); +DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +DECLARE_SOA_COLUMN(CollisionIndex, collisionIndex, int64_t); +} // namespace upcjetevent + +DECLARE_SOA_TABLE(UpcJetEvents, "AOD", "UPCJETEVENT", + upcjetevent::GapSide, + upcjetevent::PosZ, + upcjetevent::CentFT0M, + upcjetevent::EnergyCommonZNA, + upcjetevent::EnergyCommonZNC, + upcjetevent::TimeZNA, + upcjetevent::TimeZNC, + upcjetevent::NeutronClass, + upcjetevent::IsGapTagged, + upcjetevent::IsZDCTagged, + upcjetevent::CollisionIndex); +using UpcJetEvent = UpcJetEvents::iterator; + +namespace upcjet +{ +DECLARE_SOA_INDEX_COLUMN(UpcJetEvent, upcJetEvent); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int); +} // namespace upcjet + +DECLARE_SOA_TABLE(UpcJets, "AOD", "UPCJET", + upcjet::UpcJetEventId, + upcjet::Pt, + upcjet::Eta, + upcjet::Phi, + upcjet::Area, + upcjet::NConstituents); +using UpcJet = UpcJets::iterator; + +namespace upctrack +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEvent, upcJetEvent, int32_t, UpcJetEvents, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(TrackCutFlag, trackCutFlag, uint16_t); +DECLARE_SOA_COLUMN(TrackCutFlagFb5, trackCutFlagFb5, bool); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(PassesConfiguredSelection, passesConfiguredSelection, bool); +} // namespace upctrack + +DECLARE_SOA_TABLE(UpcTracks, "AOD", "UPCTRACK", + upctrack::UpcJetEventId, + upctrack::Pt, + upctrack::Eta, + upctrack::Phi, + upctrack::TrackCutFlag, + upctrack::TrackCutFlagFb5, + upctrack::DcaXY, + upctrack::DcaZ, + upctrack::PassesConfiguredSelection); +using UpcTrack = UpcTracks::iterator; + +namespace upcjettrack +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJet, upcJet, int32_t, UpcJets, ""); +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEvent, upcJetEvent, int32_t, UpcJetEvents, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upcjettrack + +DECLARE_SOA_TABLE(UpcJetTracks, "AOD", "UPCJETTRACK", + upcjettrack::UpcJetId, + upcjettrack::UpcJetEventId, + upcjettrack::Pt, + upcjettrack::Eta, + upcjettrack::Phi); +using UpcJetTrack = UpcJetTracks::iterator; + +// ============================================================ +// MCD tables +// ============================================================ +namespace upcjeteventmcd +{ +DECLARE_SOA_COLUMN(GapSide, gapSide, int); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); +DECLARE_SOA_COLUMN(NeutronClass, neutronClass, int); +DECLARE_SOA_COLUMN(IsGapTagged, isGapTagged, bool); +DECLARE_SOA_COLUMN(IsZDCTagged, isZDCTagged, bool); +DECLARE_SOA_COLUMN(CollisionIndex, collisionIndex, int64_t); +} // namespace upcjeteventmcd + +DECLARE_SOA_TABLE(UpcJetEventsMCD, "AOD", "UPCJETEVENTMCD", + upcjeteventmcd::GapSide, + upcjeteventmcd::PosZ, + upcjeteventmcd::CentFT0M, + upcjeteventmcd::EnergyCommonZNA, + upcjeteventmcd::EnergyCommonZNC, + upcjeteventmcd::TimeZNA, + upcjeteventmcd::TimeZNC, + upcjeteventmcd::NeutronClass, + upcjeteventmcd::IsGapTagged, + upcjeteventmcd::IsZDCTagged, + upcjeteventmcd::CollisionIndex); +using UpcJetEventMCD = UpcJetEventsMCD::iterator; + +namespace upctrackmcd +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCD, upcJetEventMCD, int32_t, UpcJetEventsMCD, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(TrackCutFlag, trackCutFlag, uint16_t); +DECLARE_SOA_COLUMN(TrackCutFlagFb5, trackCutFlagFb5, bool); +DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); +DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); +DECLARE_SOA_COLUMN(PassesConfiguredSelection, passesConfiguredSelection, bool); +} // namespace upctrackmcd + +DECLARE_SOA_TABLE(UpcTracksMCD, "AOD", "UPCTRACKMCD", + upctrackmcd::UpcJetEventMCDId, + upctrackmcd::Pt, + upctrackmcd::Eta, + upctrackmcd::Phi, + upctrackmcd::TrackCutFlag, + upctrackmcd::TrackCutFlagFb5, + upctrackmcd::DcaXY, + upctrackmcd::DcaZ, + upctrackmcd::PassesConfiguredSelection); +using UpcTrackMCD = UpcTracksMCD::iterator; + +namespace upcjetmcd +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCD, upcJetEventMCD, int32_t, UpcJetEventsMCD, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int); +} // namespace upcjetmcd + +DECLARE_SOA_TABLE(UpcJetsMCD, "AOD", "UPCJETMCD", + upcjetmcd::UpcJetEventMCDId, + upcjetmcd::Pt, + upcjetmcd::Eta, + upcjetmcd::Phi, + upcjetmcd::Area, + upcjetmcd::NConstituents); +using UpcJetMCD = UpcJetsMCD::iterator; + +namespace upcjettrackmcd +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetMCD, upcJetMCD, int32_t, UpcJetsMCD, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upcjettrackmcd + +DECLARE_SOA_TABLE(UpcJetTracksMCD, "AOD", "UPCJETTRACKMCD", + upcjettrackmcd::UpcJetMCDId, + upcjettrackmcd::Pt, + upcjettrackmcd::Eta, + upcjettrackmcd::Phi); +using UpcJetTrackMCD = UpcJetTracksMCD::iterator; + +// ============================================================ +// MCP tables +// ============================================================ +namespace upcjeteventmcp +{ +DECLARE_SOA_COLUMN(PosZ, posZ, float); +} // namespace upcjeteventmcp + +DECLARE_SOA_TABLE(UpcJetEventsMCP, "AOD", "UPCJETEVENTMCP", + upcjeteventmcp::PosZ); +using UpcJetEventMCP = UpcJetEventsMCP::iterator; + +namespace upctrackmcp +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCP, upcJetEventMCP, int32_t, UpcJetEventsMCP, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upctrackmcp + +DECLARE_SOA_TABLE(UpcTracksMCP, "AOD", "UPCTRACKMCP", + upctrackmcp::UpcJetEventMCPId, + upctrackmcp::Pt, + upctrackmcp::Eta, + upctrackmcp::Phi); +using UpcTrackMCP = UpcTracksMCP::iterator; + +namespace upcjetmcp +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetEventMCP, upcJetEventMCP, int32_t, UpcJetEventsMCP, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Area, area, float); +DECLARE_SOA_COLUMN(NConstituents, nConstituents, int); +} // namespace upcjetmcp + +DECLARE_SOA_TABLE(UpcJetsMCP, "AOD", "UPCJETMCP", + upcjetmcp::UpcJetEventMCPId, + upcjetmcp::Pt, + upcjetmcp::Eta, + upcjetmcp::Phi, + upcjetmcp::Area, + upcjetmcp::NConstituents); +using UpcJetMCP = UpcJetsMCP::iterator; + +namespace upcjettrackmcp +{ +DECLARE_SOA_INDEX_COLUMN_FULL(UpcJetMCP, upcJetMCP, int32_t, UpcJetsMCP, ""); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +} // namespace upcjettrackmcp + +DECLARE_SOA_TABLE(UpcJetTracksMCP, "AOD", "UPCJETTRACKMCP", + upcjettrackmcp::UpcJetMCPId, + upcjettrackmcp::Pt, + upcjettrackmcp::Eta, + upcjettrackmcp::Phi); +using UpcJetTrackMCP = UpcJetTracksMCP::iterator; + +} // namespace o2::aod + +/// Stand-alone QA task for validating UPC event tagging for charged-jet studies. +/// The tagging and QA developed here are intended to guide a future integration +/// of the required UPC information into the PWGJE derived-data workflow. +struct JetUpcQa { + static constexpr int RequiredSingleGapA = 1; + static constexpr int RequiredSingleGapC = 2; + static constexpr int RequiredDoubleGap = 3; + + // Output tables + Produces upcJetEventTable; + Produces upcJetTable; + Produces upcJetTrackTable; + + Produces upcJetEventTableMCD; + Produces upcJetTableMCD; + Produces upcJetTrackTableMCD; + + Produces upcJetEventTableMCP; + Produces upcJetTableMCP; + Produces upcJetTrackTableMCP; + + Produces upcTrackTable; + Produces upcTrackTableMCD; + Produces upcTrackTableMCP; + + // Type aliases + using BCsWithSel = soa::Join; + using CollisionsData = soa::Join; + using TracksData = soa::Join; + using CollisionsMC = soa::Join; + using TracksMC = soa::Join; + + // Preslices + // Preslice tracksPerCollision = aod::track::collisionId; + // Preslice tracksPerCollisionMC = aod::track::collisionId; + + // SGSelector buffers + std::vector amplitudesFV0; + std::vector amplitudesFT0A; + std::vector amplitudesFT0C; + std::vector amplitudesFDDA; + std::vector amplitudesFDDC; + + // Configurables + Configurable cfgFT0AMax{"cfgFT0AMax", 100.f, "FT0A amplitude threshold"}; + Configurable cfgFT0CMax{"cfgFT0CMax", 50.f, "FT0C amplitude threshold"}; + Configurable cfgFV0AMax{"cfgFV0AMax", 100.f, "FV0A amplitude threshold"}; + Configurable cfgFDDAMax{"cfgFDDAMax", -1.f, "FDDA amplitude (-1 = ignore)"}; + Configurable cfgFDDCMax{"cfgFDDCMax", -1.f, "FDDC amplitude (-1 = ignore)"}; + Configurable cfgFITTime{"cfgFITTime", 4.f, "FIT time window (ns)"}; + Configurable cfgMinNBCs{"cfgMinNBCs", 7, "Minimum number of BCs"}; + Configurable cfgNDtcoll{"cfgNDtcoll", 1, "Time resolution multiplier for compatible BCs"}; + Configurable cfgMinNTracks{"cfgMinNTracks", 0, "Min tracks"}; + Configurable cfgMaxNTracks{"cfgMaxNTracks", 100, "Max tracks"}; + + Configurable cfgTrackPtMin{"cfgTrackPtMin", 0.15f, "Min track pT (GeV/c)"}; + Configurable cfgTrackPtMax{"cfgTrackPtMax", 100.f, "Max track pT (GeV/c), matching derived-data kinematics"}; + Configurable cfgTrackEtaMax{"cfgTrackEtaMax", 0.9f, "Max track |eta|"}; + Configurable cfgTrackSelection{"cfgTrackSelection", "derivedDataSelection", "Reconstructed-track selection: derivedDataSelection, derivedQualityTracksWDCA, kinematicsOnly, globalTracks, or udDefault"}; + Configurable cfgDerivedTrackDCAZMax{"cfgDerivedTrackDCAZMax", 0.2f, "Maximum |DCAz| for the derived-data QualityTracksWDCA selection (cm)"}; + Configurable cfgVertexZCut{"cfgVertexZCut", 10.f, "Maximum absolute collision vertex z (cm)"}; + Configurable cfgTrackDCAZMax{"cfgTrackDCAZMax", 2.f, "Maximum |DCAz| for udDefault track selection (cm)"}; + Configurable cfgTrackDCAXYMax{"cfgTrackDCAXYMax", 0.f, "Maximum |DCAxy| for udDefault (cm); 0 uses the pT-dependent UD cut"}; + Configurable cfgTrackTPCChi2NClMax{"cfgTrackTPCChi2NClMax", 4.f, "Maximum TPC chi2 per cluster for udDefault"}; + Configurable cfgTrackTPCNClsFindableMin{"cfgTrackTPCNClsFindableMin", 70, "Minimum findable TPC clusters for udDefault"}; + Configurable cfgTrackITSChi2NClMax{"cfgTrackITSChi2NClMax", 36.f, "Maximum ITS chi2 per cluster for udDefault"}; + Configurable cfgJetR{"cfgJetR", 0.4f, "Jet radius"}; + Configurable cfgJetPtMin{"cfgJetPtMin", 5.0f, "Min jet pT (GeV/c)"}; + Configurable cfgJetEtaMax{"cfgJetEtaMax", 0.5f, "Max jet |eta|"}; + Configurable cfgZDCEnergyCut{"cfgZDCEnergyCut", 10.f, "ZDC common energy threshold for Xn tagging"}; + Configurable cfgZDCTimeCut{"cfgZDCTimeCut", 2.f, "ZDC time window for Xn tagging (ns)"}; + Configurable cfgUseGapTagging{"cfgUseGapTagging", true, "Require SGSelector gap tag for event selection"}; + Configurable cfgUseZDCTagging{"cfgUseZDCTagging", false, "Require ZDC neutron tag for event selection"}; + Configurable cfgRequiredGapSide{"cfgRequiredGapSide", 0, "Required gap side when gap tagging is enabled (0 = any selected gap, 1 = SingleGapA, 2 = SingleGapC, 3 = DoubleGap)"}; + Configurable cfgRequiredNeutronClass{"cfgRequiredNeutronClass", 0, "Required neutron class when ZDC tagging is enabled (0 = any valid class, 1 = XnXn, 2 = Xn0n, 3 = 0nXn, 4 = 0n0n)"}; + + struct ZDCTagInfo { + float energyCommonZNA = -999.f; + float energyCommonZNC = -999.f; + float timeZNA = -999.f; + float timeZNC = -999.f; + int neutronClass = 0; + }; + + struct GapSelectionInfo { + int gapSide = o2::aod::sgselector::NoGap; + ZDCTagInfo zdc; + }; + + SGSelector sgSelector; + SGCutParHolder sgCuts; + + HistogramRegistry registry{"registry"}; + enum class TrackSelectionMode { + DerivedDataSelection, + DerivedQualityTracksWDCA, + KinematicsOnly, + GlobalTracks, + UDDefault + }; + TrackSelectionMode trackSelectionMode = TrackSelectionMode::DerivedDataSelection; + + // Init + void init(InitContext&) + { + if (cfgTrackSelection.value == "derivedDataSelection") { + trackSelectionMode = TrackSelectionMode::DerivedDataSelection; + } else if (cfgTrackSelection.value == "derivedQualityTracksWDCA") { + trackSelectionMode = TrackSelectionMode::DerivedQualityTracksWDCA; + } else if (cfgTrackSelection.value == "kinematicsOnly") { + trackSelectionMode = TrackSelectionMode::KinematicsOnly; + } else if (cfgTrackSelection.value == "globalTracks") { + trackSelectionMode = TrackSelectionMode::GlobalTracks; + } else if (cfgTrackSelection.value == "udDefault") { + trackSelectionMode = TrackSelectionMode::UDDefault; + } else { + LOGP(fatal, "Unknown cfgTrackSelection '{}'. Choose derivedDataSelection, derivedQualityTracksWDCA, kinematicsOnly, globalTracks, or udDefault.", cfgTrackSelection.value); + } + + sgCuts.SetNDtcoll(cfgNDtcoll); + sgCuts.SetMinNBCs(cfgMinNBCs); + sgCuts.SetNTracks(cfgMinNTracks, cfgMaxNTracks); + sgCuts.SetMaxFITtime(cfgFITTime); + sgCuts.SetFITAmpLimits({cfgFV0AMax, cfgFT0AMax, cfgFT0CMax, cfgFDDAMax, cfgFDDCMax}); + + // Event-level QA only + registry.add("hGapSide", "Gap Side (Data);Gap Side;Counts", {HistType::kTH1F, {{6, -1.5, 4.5}}}); + registry.add("hGapSideMCD", "Gap Side (MCD);Gap Side;Counts", {HistType::kTH1F, {{6, -1.5, 4.5}}}); + + registry.add("hPosZ", "Vertex Z (Data);z (cm);Counts", {HistType::kTH1F, {{100, -15., 15.}}}); + registry.add("hPosZMCD", "Vertex Z (MCD);z (cm);Counts", {HistType::kTH1F, {{100, -15., 15.}}}); + registry.add("hPosZMCP", "Vertex Z (MCP);z (cm);Counts", {HistType::kTH1F, {{100, -15., 15.}}}); + + registry.add("hCentralityFT0M", "Centrality FT0M (Data);Cent;Counts", {HistType::kTH1F, {{100, 0., 100.}}}); + registry.add("hCentMCD", "Centrality FT0M (MCD);Cent;Counts", {HistType::kTH1F, {{100, 0., 100.}}}); + + registry.add("hMCDFoundBC", "MCD has_foundBC;0=false 1=true;Counts", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("hMCPNCharged", "MCP N charged primaries;N;Counts", {HistType::kTH1F, {{200, -0.5, 199.5}}}); + + registry.add("hZNAEnergy", "ZNA common energy (Data);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNCEnergy", "ZNC common energy (Data);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNATime", "ZNA time (Data);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hZNCTime", "ZNC time (Data);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hNeutronClass", "Neutron class (Data);class;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}); + registry.add("hZNAEnergyMCD", "ZNA common energy (MCD);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNCEnergyMCD", "ZNC common energy (MCD);E;Counts", {HistType::kTH1F, {{400, -10., 390.}}}); + registry.add("hZNATimeMCD", "ZNA time (MCD);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hZNCTimeMCD", "ZNC time (MCD);t (ns);Counts", {HistType::kTH1F, {{200, -20., 20.}}}); + registry.add("hNeutronClassMCD", "Neutron class (MCD);class;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}); + registry.add("hSelectionBits", "Selection bits (Data);0 all 1 gap 2 zdc 3 accepted;Counts", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("hSelectionBitsMCD", "Selection bits (MCD);0 all 1 gap 2 zdc 3 accepted;Counts", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + } + + template + bool passesTrackKinematics(TTrack const& track) const + { + return std::abs(track.eta()) <= cfgTrackEtaMax.value && + track.pt() >= cfgTrackPtMin.value && + track.pt() <= cfgTrackPtMax.value; + } + + template + bool isSelectedTrack(TTrack const& track) const + { + if (!passesTrackKinematics(track)) { + return false; + } + if (trackSelectionMode == TrackSelectionMode::DerivedQualityTracksWDCA) { + // Mirror JetDerivedDataUtilities::setTrackSelectionBit(...): QualityTracksWDCA. + return track.isQualityTrack() && std::abs(track.dcaZ()) < cfgDerivedTrackDCAZMax.value; + } + if (trackSelectionMode == TrackSelectionMode::DerivedDataSelection) { + // Match the derived-data writer: retain tracks passing any stored selection bit + // (global, quality, or hybrid). QualityTracksWDCA is a subset of QualityTracks. + return track.isGlobalTrackWoPtEta() || track.isQualityTrack() || track.trackCutFlagFb5(); + } + if (trackSelectionMode == TrackSelectionMode::KinematicsOnly) { + return true; + } + if (trackSelectionMode == TrackSelectionMode::GlobalTracks) { + return track.isGlobalTrack(); + } + if (!track.isPVContributor()) { + return false; + } + if (std::abs(track.dcaZ()) > cfgTrackDCAZMax.value) { + return false; + } + float maxDCAxy = cfgTrackDCAXYMax.value; + if (maxDCAxy <= 0.f) { + maxDCAxy = 0.0105f + 0.035f / std::pow(track.pt(), 1.1f); + } + if (std::abs(track.dcaXY()) > maxDCAxy) { + return false; + } + if (track.tpcChi2NCl() > cfgTrackTPCChi2NClMax.value || track.tpcNClsFindable() < cfgTrackTPCNClsFindableMin.value) { + return false; + } + if (track.itsChi2NCl() > cfgTrackITSChi2NClMax.value) { + return false; + } + return true; + } + + // Helper: event tagging and selection + bool isSelectedGap(int gapSide) const + { + if (cfgRequiredGapSide.value == RequiredSingleGapA) { + return gapSide == o2::aod::sgselector::SingleGapA; + } + if (cfgRequiredGapSide.value == RequiredSingleGapC) { + return gapSide == o2::aod::sgselector::SingleGapC; + } + if (cfgRequiredGapSide.value == RequiredDoubleGap) { + return gapSide == o2::aod::sgselector::DoubleGap; + } + return gapSide == o2::aod::sgselector::SingleGapA || + gapSide == o2::aod::sgselector::SingleGapC || + gapSide == o2::aod::sgselector::DoubleGap; + } + + int getNeutronClass(ZDCTagInfo const& zdc) + { + if (zdc.energyCommonZNA < 0.f || zdc.energyCommonZNC < 0.f) { + return 0; + } + + bool aXn = zdc.energyCommonZNA > cfgZDCEnergyCut && std::abs(zdc.timeZNA) < cfgZDCTimeCut; + bool cXn = zdc.energyCommonZNC > cfgZDCEnergyCut && std::abs(zdc.timeZNC) < cfgZDCTimeCut; + bool a0n = zdc.energyCommonZNA <= cfgZDCEnergyCut; + bool c0n = zdc.energyCommonZNC <= cfgZDCEnergyCut; + + if (aXn && cXn) { + return 1; // XnXn + } + if (aXn && c0n) { + return 2; // Xn0n + } + if (a0n && cXn) { + return 3; // 0nXn + } + if (a0n && c0n) { + return 4; // 0n0n + } + return 0; + } + + bool isSelectedZDC(ZDCTagInfo const& zdc) const + { + if (cfgRequiredNeutronClass.value > 0) { + return zdc.neutronClass == cfgRequiredNeutronClass.value; + } + return zdc.neutronClass > 0; + } + + bool isAcceptedEvent(bool isGapTagged, bool isZDCTagged, float posZ) const + { + return std::abs(posZ) <= cfgVertexZCut.value && + (!cfgUseGapTagging.value || isGapTagged) && + (!cfgUseZDCTagging.value || isZDCTagged); + } + + void fillSelectionBits(bool isMCD, bool isGapTagged, bool isZDCTagged, bool isAccepted) + { + if (isMCD) { + registry.fill(HIST("hSelectionBitsMCD"), 0); + if (isGapTagged) { + registry.fill(HIST("hSelectionBitsMCD"), 1); + } + if (isZDCTagged) { + registry.fill(HIST("hSelectionBitsMCD"), 2); + } + if (isAccepted) { + registry.fill(HIST("hSelectionBitsMCD"), 3); + } + return; + } + + registry.fill(HIST("hSelectionBits"), 0); + if (isGapTagged) { + registry.fill(HIST("hSelectionBits"), 1); + } + if (isZDCTagged) { + registry.fill(HIST("hSelectionBits"), 2); + } + if (isAccepted) { + registry.fill(HIST("hSelectionBits"), 3); + } + } + + template + ZDCTagInfo getZDCTagInfo(TBC const& bc) + { + ZDCTagInfo zdcInfo; + if (!bc.has_zdc()) { + return zdcInfo; + } + + auto const& zdc = bc.zdc(); + zdcInfo.energyCommonZNA = zdc.energyCommonZNA(); + zdcInfo.energyCommonZNC = zdc.energyCommonZNC(); + zdcInfo.timeZNA = zdc.timeZNA(); + zdcInfo.timeZNC = zdc.timeZNC(); + zdcInfo.neutronClass = getNeutronClass(zdcInfo); + return zdcInfo; + } + + template + GapSelectionInfo getGapSide(TCollision const& collision, TBCs const& bcs) + { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + + GapSelectionInfo selection; + + if (!collision.has_foundBC()) { + return selection; + } + + auto const bc = collision.template foundBC_as(); + auto const bcRange = udhelpers::compatibleBCs(collision, sgCuts.NDtcoll(), bcs, sgCuts.minNBCs()); + + auto const result = sgSelector.IsSelected(sgCuts, collision, bcRange, bc, + &litudesFV0, &litudesFT0A, &litudesFT0C, + &litudesFDDA, &litudesFDDC); + + selection.gapSide = result.value; + if (result.bc) { + selection.zdc = getZDCTagInfo(*result.bc); + } + + if (!isSelectedGap(selection.gapSide)) { + amplitudesFV0.clear(); + amplitudesFT0A.clear(); + amplitudesFT0C.clear(); + amplitudesFDDA.clear(); + amplitudesFDDC.clear(); + } + + return selection; + } + + // Helper: jet finding for detector-level tracks + template + void runJetFindingDet(TTracks const& tracks, + int evtIdx, + TJetTable& jetTable, + TTrkTable& trkTable) + { + std::vector fjInput; + fjInput.reserve(tracks.size()); + + for (auto const& t : tracks) { + if (!isSelectedTrack(t)) { + continue; + } + + fastjet::PseudoJet p; + p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(p); + } + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef(fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + jetTable(evtIdx, jet.pt(), jet.eta(), jet.phi_std(), + jet.area(), static_cast(constituents.size())); + int jetIdx = jetTable.lastIndex(); + + for (auto const& c : constituents) { + trkTable(jetIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + + // Helper: jet finding for particle-level MC + template + void runJetFindingMCP(aod::McParticles const& particles, + int evtIdx, + TJetTable& jetTable, + TTrkTable& trkTable) + { + std::vector fjInput; + int nCharged = 0; + + for (auto const& p : particles) { + if (!p.isPhysicalPrimary()) { + continue; + } + if (std::abs(p.eta()) > cfgTrackEtaMax) { + continue; + } + if (p.pt() < cfgTrackPtMin) { + continue; + } + + auto pdg = std::abs(p.pdgCode()); + bool charged = (pdg == kPiPlus || pdg == kKPlus || pdg == kProton || pdg == kElectron || pdg == kMuonMinus); + if (!charged) { + continue; + } + + fastjet::PseudoJet fpp; + fpp.reset_PtYPhiM(p.pt(), p.eta(), p.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(fpp); + ++nCharged; + } + + registry.fill(HIST("hMCPNCharged"), nCharged); + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef(fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + jetTable(evtIdx, jet.pt(), jet.eta(), jet.phi_std(), + jet.area(), static_cast(constituents.size())); + int jetIdx = jetTable.lastIndex(); + + for (auto const& c : constituents) { + trkTable(jetIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + + template + void fillEventTracksDet(TTracks const& tracks, int evtIdx, TTrackTable& trackTable) + { + int nAll = 0; + int nFilled = 0; + for (auto const& t : tracks) { + ++nAll; + if (!isSelectedTrack(t)) + continue; + trackTable(evtIdx, t.pt(), t.eta(), t.phi()); + ++nFilled; + } + LOGP(info, "fillEventTracksDet: evtIdx={} nAll={} nFilled={}", evtIdx, nAll, nFilled); + } + + template + void fillEventTracksMCP(aod::McParticles const& particles, int evtIdx, TTrackTable& trackTable) + { + for (auto const& p : particles) { + if (!p.isPhysicalPrimary()) + continue; + if (std::abs(p.eta()) > cfgTrackEtaMax) + continue; + if (p.pt() < cfgTrackPtMin) + continue; + + auto pdg = std::abs(p.pdgCode()); + bool charged = (pdg == kPiPlus || pdg == kKPlus || pdg == kProton || + pdg == kElectron || pdg == kMuonMinus); + if (!charged) + continue; + + trackTable(evtIdx, p.pt(), p.eta(), p.phi()); + } + } + + // Process: Data + void processData(CollisionsData::iterator const& collision, + BCsWithSel const& bcs, + aod::Zdcs const&, + aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, + TracksData const& tracks) + { + auto gapInfo = getGapSide(collision, bcs); + int gapSide = gapInfo.gapSide; + bool isGapTagged = isSelectedGap(gapSide); + bool isZDCTagged = isSelectedZDC(gapInfo.zdc); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged, collision.posZ()); + registry.fill(HIST("hGapSide"), gapSide); + registry.fill(HIST("hZNAEnergy"), gapInfo.zdc.energyCommonZNA); + registry.fill(HIST("hZNCEnergy"), gapInfo.zdc.energyCommonZNC); + registry.fill(HIST("hZNATime"), gapInfo.zdc.timeZNA); + registry.fill(HIST("hZNCTime"), gapInfo.zdc.timeZNC); + registry.fill(HIST("hNeutronClass"), gapInfo.zdc.neutronClass); + fillSelectionBits(false, isGapTagged, isZDCTagged, isAccepted); + + if (!isAccepted) { + return; + } + + registry.fill(HIST("hPosZ"), collision.posZ()); + registry.fill(HIST("hCentralityFT0M"), collision.centFT0M()); + + upcJetEventTable(gapSide, + collision.posZ(), + collision.centFT0M(), + gapInfo.zdc.energyCommonZNA, + gapInfo.zdc.energyCommonZNC, + gapInfo.zdc.timeZNA, + gapInfo.zdc.timeZNC, + gapInfo.zdc.neutronClass, + isGapTagged, + isZDCTagged, + collision.globalIndex()); + // This is the row index in our output event table, not the input collision index. + int evtIdx = upcJetEventTable.lastIndex(); + + std::vector fjInput; + fjInput.reserve(256); + + for (auto const& t : tracks) { + if (t.collisionId() != collision.globalIndex()) { + continue; + } + if (!passesTrackKinematics(t)) { + continue; + } + bool passesConfiguredSelection = isSelectedTrack(t); + + // Store all kinematically accepted candidates and the current decision so + // alternate track selections can be reproduced from the output table. + upcTrackTable(evtIdx, t.pt(), t.eta(), t.phi(), t.trackCutFlag(), t.trackCutFlagFb5(), t.dcaXY(), t.dcaZ(), passesConfiguredSelection); + if (!passesConfiguredSelection) { + continue; + } + + // jet finding input + fastjet::PseudoJet p; + p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(p); + } + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef( + fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + + upcJetTable(evtIdx, + jet.pt(), + jet.eta(), + jet.phi_std(), + jet.area(), + static_cast(constituents.size())); + + int jetIdx = upcJetTable.lastIndex(); + + for (auto const& c : constituents) { + upcJetTrackTable(jetIdx, evtIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + PROCESS_SWITCH(JetUpcQa, processData, "Process real data", false); + // Process: MCD + void processMCD(CollisionsMC::iterator const& collision, + BCsWithSel const& bcs, + aod::Zdcs const&, + aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, + TracksMC const& tracks) + { + registry.fill(HIST("hMCDFoundBC"), collision.has_foundBC() ? 1 : 0); + + auto gapInfo = getGapSide(collision, bcs); + int gapSide = gapInfo.gapSide; + bool isGapTagged = isSelectedGap(gapSide); + bool isZDCTagged = isSelectedZDC(gapInfo.zdc); + bool isAccepted = isAcceptedEvent(isGapTagged, isZDCTagged, collision.posZ()); + registry.fill(HIST("hGapSideMCD"), gapSide); + registry.fill(HIST("hZNAEnergyMCD"), gapInfo.zdc.energyCommonZNA); + registry.fill(HIST("hZNCEnergyMCD"), gapInfo.zdc.energyCommonZNC); + registry.fill(HIST("hZNATimeMCD"), gapInfo.zdc.timeZNA); + registry.fill(HIST("hZNCTimeMCD"), gapInfo.zdc.timeZNC); + registry.fill(HIST("hNeutronClassMCD"), gapInfo.zdc.neutronClass); + fillSelectionBits(true, isGapTagged, isZDCTagged, isAccepted); + + if (!isAccepted) { + return; + } + + registry.fill(HIST("hPosZMCD"), collision.posZ()); + registry.fill(HIST("hCentMCD"), collision.centFT0M()); + + upcJetEventTableMCD(gapSide, + collision.posZ(), + collision.centFT0M(), + gapInfo.zdc.energyCommonZNA, + gapInfo.zdc.energyCommonZNC, + gapInfo.zdc.timeZNA, + gapInfo.zdc.timeZNC, + gapInfo.zdc.neutronClass, + isGapTagged, + isZDCTagged, + collision.globalIndex()); + int evtIdx = upcJetEventTableMCD.lastIndex(); + + std::vector fjInput; + fjInput.reserve(256); + + for (auto const& t : tracks) { + if (t.collisionId() != collision.globalIndex()) { + continue; + } + if (!passesTrackKinematics(t)) { + continue; + } + bool passesConfiguredSelection = isSelectedTrack(t); + + upcTrackTableMCD(evtIdx, t.pt(), t.eta(), t.phi(), t.trackCutFlag(), t.trackCutFlagFb5(), t.dcaXY(), t.dcaZ(), passesConfiguredSelection); + if (!passesConfiguredSelection) { + continue; + } + + fastjet::PseudoJet p; + p.reset_PtYPhiM(t.pt(), t.eta(), t.phi(), o2::constants::physics::MassPiPlus); + fjInput.push_back(p); + } + + if (fjInput.empty()) { + return; + } + + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgJetR); + fastjet::AreaDefinition areaDef( + fastjet::active_area_explicit_ghosts, + fastjet::GhostedAreaSpec(cfgTrackEtaMax + cfgJetR)); + + fastjet::ClusterSequenceArea cs(fjInput, jetDef, areaDef); + auto jets = fastjet::sorted_by_pt(cs.inclusive_jets(cfgJetPtMin)); + + for (auto const& jet : jets) { + if (std::abs(jet.eta()) > cfgJetEtaMax) { + continue; + } + + auto constituents = jet.constituents(); + + upcJetTableMCD(evtIdx, + jet.pt(), + jet.eta(), + jet.phi_std(), + jet.area(), + static_cast(constituents.size())); + + int jetIdx = upcJetTableMCD.lastIndex(); + + for (auto const& c : constituents) { + upcJetTrackTableMCD(jetIdx, c.pt(), c.eta(), c.phi_std()); + } + } + } + PROCESS_SWITCH(JetUpcQa, processMCD, "Process MC detector level", true); + + // Process: MCP + void processMCP(aod::McCollision const& mcCollision, + aod::McParticles const& particles) + { + registry.fill(HIST("hPosZMCP"), mcCollision.posZ()); + + upcJetEventTableMCP(mcCollision.posZ()); + int evtIdx = upcJetEventTableMCP.lastIndex(); + fillEventTracksMCP(particles, evtIdx, upcTrackTableMCP); + runJetFindingMCP(particles, evtIdx, upcJetTableMCP, upcJetTrackTableMCP); + } + PROCESS_SWITCH(JetUpcQa, processMCP, "Process MC particle level", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGJE/Tasks/jetValidationQA.cxx b/PWGJE/Tasks/jetValidationQA.cxx index 999a1feeaef..9eac3ed0651 100644 --- a/PWGJE/Tasks/jetValidationQA.cxx +++ b/PWGJE/Tasks/jetValidationQA.cxx @@ -56,7 +56,7 @@ struct jetTrackCollisionQa { void init(InitContext const&) { - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms const AxisSpec vtxZAxis{nBins, -20, 20, "Vtx_{z} (cm)"}; @@ -65,7 +65,7 @@ struct jetTrackCollisionQa { const AxisSpec ptAxis{BinsPt, "#it{p}_{T} (GeV/#it{c})"}; // set trackselections - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms // 1)Jetvalidation on data mHistManager.add("collisionVtxZ", "selected collsion VtxZ ", HistType::kTH1D, {vtxZAxis}); @@ -337,7 +337,7 @@ struct mcJetTrackCollisionQa { void init(InitContext const&) { // set trackselection - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); // histograms const AxisSpec vtxZAxis{nBins, -20, 20, "Vtx_{z} (cm)"}; const AxisSpec ptAxis{BinsPt, "#it{p}_{T} (GeV/#it{c})"}; @@ -410,7 +410,7 @@ struct mcJetTrackCollisionQa { // fill qa histograms for selected tracks in collision template - void fillMcTrackHistos(ValidationTracks const& mct, coll collision, bool mc) // could give collision as argument for additional association + void fillMcTrackHistos(ValidationTracks const& mct, const coll& collision, bool mc) // could give collision as argument for additional association { for (const auto& track : mct) { if (!jetderiveddatautilities::selectTrack(track, trackSelection) || !(track.collisionId() == collision.globalIndex())) { diff --git a/PWGJE/Tasks/mcGeneratorStudies.cxx b/PWGJE/Tasks/mcGeneratorStudies.cxx index 1fd61f64bfc..1dccf464e99 100644 --- a/PWGJE/Tasks/mcGeneratorStudies.cxx +++ b/PWGJE/Tasks/mcGeneratorStudies.cxx @@ -80,7 +80,7 @@ struct MCGeneratorStudies { hBCCounter->GetXaxis()->SetBinLabel(2, "+TVX"); hBCCounter->GetXaxis()->SetBinLabel(3, "+Collision"); - TString mesonLatexString = (TString)mSelectedParticleCode; + TString mesonLatexString = TString(mSelectedParticleCode); switch (mSelectedParticleCode) { case 0: mesonLatexString = "particles"; @@ -224,7 +224,7 @@ struct MCGeneratorStudies { } template - bool isGammaGammaDecay(TMCParticle mcParticle, TMCParticles mcParticles) + bool isGammaGammaDecay(const TMCParticle& mcParticle, const TMCParticles& mcParticles) { auto daughtersIds = mcParticle.daughtersIds(); if (daughtersIds.size() != 2) @@ -237,7 +237,7 @@ struct MCGeneratorStudies { } template - bool isAccepted(TMCParticle mcParticle, TMCParticles mcParticles) + bool isAccepted(const TMCParticle& mcParticle, const TMCParticles& mcParticles) { auto daughtersIds = mcParticle.daughtersIds(); if (daughtersIds.size() != 2) diff --git a/PWGJE/Tasks/nucleiInJets.cxx b/PWGJE/Tasks/nucleiInJets.cxx index f6a419992b8..5b57190673e 100644 --- a/PWGJE/Tasks/nucleiInJets.cxx +++ b/PWGJE/Tasks/nucleiInJets.cxx @@ -17,6 +17,7 @@ #include "PWGJE/DataModel/JetSubtraction.h" // #include "PWGLF/DataModel/LFParticleIdentification.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGLF/Utils/inelGt.h" #include "Common/Core/RecoDecay.h" @@ -47,7 +48,6 @@ #include #include #include -#include #include #include @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -113,6 +114,18 @@ struct nucleiInJets { } } + template + int getParticleOriginType(const T& mcParticle) + { + if (mcParticle.isPhysicalPrimary()) { + return 1; // physical primary + } + if (mcParticle.getProcess() == TMCProcess::kPDecay) { + return 3; // weak-decay feed-down + } + return 2; // other secondary / material / transport + } + Configurable cfgtrackSelections{"cfgtrackSelections", "globalTracks", "set track selections"}; Configurable isMC{"isMC", false, "flag for the MC"}; Configurable isWithJetEvents{"isWithJetEvents", true, "Events with at least one jet"}; @@ -137,6 +150,7 @@ struct nucleiInJets { Configurable cfgnITSChi2{"cfgnITShi2", 36.0, "nITS Chi2 per Cluster"}; Configurable cfgjetPtMin{"cfgjetPtMin", 5.0, "minimum jet pT cut"}; + Configurable cfgjetPtBkgSubMin{"cfgjetPtBkgSubMin", 10.0f, "minimum background-subtracted jet pT cut for data jet-event normalization"}; Configurable cfgjetR{"cfgjetR", 0.4, "jet resolution parameter"}; Configurable cDebugLevel{"cDebugLevel", 0, "print debug msg"}; Configurable cMaxPt{"cMaxPt", 10, "max pt for Hist"}; @@ -163,7 +177,41 @@ struct nucleiInJets { Configurable useTOFVeto{"useTOFVeto", false, "true: use TOF veto, false: no TOF veto"}; Configurable isRequireHitsInITSLayers{"isRequireHitsInITSLayers", true, "true: at least one hit in the its inner layes"}; Configurable useMcC{"useMcC", true, "use mcC"}; + Configurable mcClosureTrainingFraction{"mcClosureTrainingFraction", 0.5f, "fraction of events assigned to MC correction training; the rest are data-like"}; + Configurable mcClosureSplitSeed{"mcClosureSplitSeed", 1234, "random seed for MC/data-like split (0: automatic seed)"}; + Configurable usebkgSubractionMC{"usebkgSubractionMC", true, "use rho-area background subtraction for detector-level matched MC jets"}; + Configurable cfgjetPtBkgSubMinMC{"cfgjetPtBkgSubMinMC", 10.0f, "minimum detector-level matched MC jet pT after optional background subtraction"}; + Configurable useFullConeAcceptance{"useFullConeAcceptance", true, "require jet/perpendicular-cone axes to satisfy |eta_axis| < cfgtrkMaxEta - cfgjetR for jet and efficiency histograms"}; Configurable useRapidityCutForPID{"useRapidityCutForPID", false, "true: use rapidity cut for PID, false: no rapidity cut for PID"}; + Configurable isPOCollision{"isPOCollision", false, "true: use pO rapidity shift for inclusive PID/efficiency rapidity cuts; false: symmetric system, no rapidity shift"}; + Configurable cfgPORapidityShiftForPID{"cfgPORapidityShiftForPID", 0.0, "pO rapidity shift for inclusive PID/efficiency rapidity cuts: y_{CMS}=y_{lab}-shift. Set sign/value according to beam convention"}; + Configurable cfgPORapidityMinForPID{"cfgPORapidityMinForPID", -0.5, "minimum pO shifted rapidity for PID/efficiency cuts"}; + Configurable cfgPORapidityMaxForPID{"cfgPORapidityMaxForPID", 0.0, "maximum pO shifted rapidity for PID/efficiency cuts"}; + + double rapidityInSelectedFrame(double rapidityLab) + { + return isPOCollision ? rapidityLab - static_cast(cfgPORapidityShiftForPID) : rapidityLab; + } + + bool isRapiditySelectedForPID(double rapidityLab) + { + const double rap = rapidityInSelectedFrame(rapidityLab); + if (isPOCollision) { + const double rapMin = std::min(static_cast(cfgPORapidityMinForPID), static_cast(cfgPORapidityMaxForPID)); + const double rapMax = std::max(static_cast(cfgPORapidityMinForPID), static_cast(cfgPORapidityMaxForPID)); + return rap > rapMin && rap < rapMax; + } + return std::abs(rap) < std::abs(static_cast(cfgtrkMaxRap)); + } + + bool isConeAxisAccepted(double etaAxis) + { + if (!useFullConeAcceptance) { + return true; + } + const double maxAcceptedConeAxisEta = static_cast(cfgtrkMaxEta) - static_cast(cfgjetR); + return maxAcceptedConeAxisEta > 0. && std::abs(etaAxis) < maxAcceptedConeAxisEta; + } Configurable addpik{"addpik", true, "add pion and kaon hist"}; ConfigurableAxis binsDCA{"binsDCA", {400, -1.f, 1.f}, ""}; @@ -188,6 +236,7 @@ struct nucleiInJets { // using EventTable = soa::Join; using EventTable = aod::JetCollisions; using EventTableMC = soa::Join; + using JetMcCollisionWithCent = soa::Join::iterator; using JetCollWithLabel = o2::soa::Join::iterator; using TrackCandidates = soa::Join>; using JetMCDetTable = soa::Filtered>; + Preslice> detectorJetsPerCollision = aod::jet::collisionId; + SliceCache cache; HistogramRegistry jetHist{"jetHist", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -218,16 +269,37 @@ struct nucleiInJets { OutputObj zorroSummary{"zorroSummary"}; Service ccdb; Service pdgDB; - TRandom3 randUniform; + uint64_t mcClosureSplitSeedEffective = 1; + + bool useTrainingHistForMcCollision(int64_t mcCollisionId) + { + // Use the MC collision ID as part of the seed, so both process functions + // obtain the same decision without storing one entry per collision. + const auto collisionId = static_cast(mcCollisionId); + std::mt19937_64 collisionRandom(mcClosureSplitSeedEffective ^ collisionId); + std::uniform_real_distribution uniform(0.0, 1.0); + return uniform(collisionRandom) < mcClosureTrainingFraction; + } + void init(o2::framework::InitContext&) { if (doprocessJetTracksData && doprocessJetTracksDataLfPid) { LOGP(fatal, "only one process function should be enabled!!!"); } + if (!(mcClosureTrainingFraction >= 0.f && mcClosureTrainingFraction <= 1.f)) { + LOGP(fatal, "mcClosureTrainingFraction must be between 0 and 1, inclusive"); + } + if (mcClosureSplitSeed == 0) { + std::random_device randomDevice; + mcClosureSplitSeedEffective = (static_cast(randomDevice()) << 32) ^ randomDevice(); + } else { + mcClosureSplitSeedEffective = static_cast(mcClosureSplitSeed); + } jetHist.print(); // const AxisSpec PtAxis = {100, 0, 10.0}; const AxisSpec PtJetAxis = {100, 0, 100.0}; + const AxisSpec PtJetBkgSubAxis = {240, -20.f, 100.f, "#it{p}_{T}^{jet,bkg sub} (GeV/#it{c})"}; const AxisSpec MultAxis = {100, 0, 100}; const AxisSpec dRAxis = {100, 0, 3.6}; const AxisSpec CentAxis = {100, 0, 100}; @@ -235,6 +307,8 @@ struct nucleiInJets { const AxisSpec dcazAxis{binsDCA, "DCAz (cm)"}; const AxisSpec dedxAxis{binsdEdx, "d#it{E}/d#it{x} A.U."}; const AxisSpec vzAxis{300, -15.f, 15.f, "Vz (cm)"}; + const AxisSpec EtaAxis{40, -1.f, 1.f, "#eta"}; + const AxisSpec ParticleOriginAxis{3, 0.5, 3.5, "origin: 1 primary, 2 secondary/material, 3 weak decay"}; const AxisSpec betaAxis{binsBeta, "TOF #beta"}; const AxisSpec ptZHeAxis{binsPtZHe, "#it{p}_{T}"}; @@ -245,12 +319,16 @@ struct nucleiInJets { const AxisSpec massHeAxis{binsMassHe, ""}; const AxisSpec PtAxis{ptAxisConf, ""}; - jetHist.add("hNEvents", "hNEvents", {HistType::kTH1D, {{6, 0.f, 6.f}}}); + jetHist.add("hNEvents", "hNEvents", {HistType::kTH1D, {{9, 0.f, 9.f}}}); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(1, "All"); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(2, "Skimmed"); jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(3, "|Vz|<10"); - jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(4, "Sel8+|Vz|<10"); - jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(5, "nJets>0"); + jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(4, "Sel8"); + jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(5, "noSameBunchPileup"); + jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(6, "isGoodZvtxFT0vsPV"); + jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(7, "OccupancySel"); + jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(8, "nJets>0"); + jetHist.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(9, "leading jet bkg-sub pT cut"); jetHist.add("hNEventsInc", "hNEventsInc", {HistType::kTH1D, {{6, 0.f, 6.f}}}); jetHist.get(HIST("hNEventsInc"))->GetXaxis()->SetBinLabel(1, "All"); @@ -274,12 +352,22 @@ struct nucleiInJets { jetHist.add("jet/h2JetPtVsBkgRho", "jet_{p_{T}} vs background #rho; jet_{p_{T}} (GeV/c); #rho (GeV/c/area)", kTH2F, {PtJetAxis, {100, 0, 20}}); jetHist.add("jet/h1BkgRho", "Background #rho; #rho (GeV/c/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEvents", "NumbeOfJetEvents", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1JetEventsBeforeRhoSelection", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin before rho selection", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1JetEvents", "Number of jet events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin and rho > 0", kTH1F, {{1, 0, 1}}); + jetHist.add("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut", "Background #rho for events with leading p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin; #rho (GeV/#it{c}/area); Entries", kTH1F, {{100, 0, 20}}); jetHist.add("jet/h1JetEta", "jet_{#eta}", kTH1F, {{100, -1.0, 1.0}}); jetHist.add("jet/h1JetPhi", "jet_{#phi}", kTH1F, {{80, -1.0, 7.}}); jetHist.add("jet/nJetsPerEvent", "nJetsPerEvent", kTH1F, {{15, .0, 15.}}); + jetHist.add("jet/qa/h2JetConeEtaPhiBeforeAcceptance", "jet-cone axis before full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("jet/qa/h2JetConeEtaPhiAfterAcceptance", "jet-cone axis after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("jet/qa/h2PerpConeEtaPhiAfterAcceptance", "perpendicular-cone axes after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("jet/qa/nAcceptedJetsPerEvent", "number of full-cone-accepted jets per event;N_{jets};Entries", kTH1F, {{15, .0, 15.}}); + jetHist.add("jetBkgSub/h1LeadingJetPtBkgSub", "leading jet background-subtracted #it{p}_{T}; #it{p}_{T}^{jet,bkg sub} (GeV/#it{c}); Entries", kTH1F, {PtJetBkgSubAxis}); + jetHist.add("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ", "leading jet background-subtracted #it{p}_{T} vs V_{z}; #it{p}_{T}^{jet,bkg sub} (GeV/#it{c}); V_{z} (cm)", kTH2F, {PtJetBkgSubAxis, vzAxis}); jetHist.add("mcpJet/nJetsPerEvent", "nJetsPerEvent", kTH1F, {{15, .0, 15.}}); jetHist.add("mcdJet/nJetsPerEvent", "nJetsPerEvent", kTH1F, {{15, .0, 15.}}); jetHist.add("jet/vertexZ", "vertexZ (Jet flag)", kTH1F, {{vzAxis}}); + jetHist.add("jetBkgSub/vertexZ", "vertexZ for events with p_{T}^{jet,bkg sub} above cfgjetPtBkgSubMin; V_{z} (cm); Entries", kTH1F, {{vzAxis}}); jetHist.add("vertexZ", "vertexZ (all)", kTH1F, {{vzAxis}}); jetHist.add("jetOut/vertexZ", "vertexZ (without z-flag)", kTH1F, {{vzAxis}}); //////////////////////////// @@ -424,9 +512,13 @@ struct nucleiInJets { jetHist.add("tracks/antiKaon/h2TofNsigmaantiKaonVsPt_jet", "h2TofNsigmaantiKaonVsPt_jet; TofNsigma; #it{p}_{T} (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); } jetHist.add("tracks/proton/h2TofNsigmaProtonVsPt_jet", "h2TofNsigmaProtonVsPt_jet; TofNsigma; #it{p}_{T} (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); + jetHist.add("tracks/proton/h3TofNsigmaProtonVsPtVsJetPtBkgSub_jet", "h3TofNsigmaProtonVsPtVsJetPtBkgSub_jet; TofNsigma; #it{p}_{T} (GeV); jet #it{p}_{T} (Bkg Sub) (GeV)", HistType::kTH3F, {{100, -5, 5}, {50, 0., 5.}, {PtJetAxis}}); jetHist.add("tracks/antiProton/h2TofNsigmaantiProtonVsPt_jet", "h2TofNsigmaantiProtonVsPt_jet; TofNsigma; #it{p}_{T} (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); + jetHist.add("tracks/antiProton/h3TofNsigmaantiProtonVsPtVsJetPtBkgSub_jet", "h3TofNsigmaantiProtonVsPtVsJetPtBkgSub_jet; TofNsigma; #it{p}_{T} (GeV); jet #it{p}_{T} (Bkg Sub) (GeV)", HistType::kTH3F, {{100, -5, 5}, {50, 0., 5.}, {PtJetAxis}}); jetHist.add("tracks/deuteron/h2TofNsigmaDeuteronVsPt_jet", "h2TofNsigmaDeuteronVsPt_jet; TofNsigma; #it{p}_{T} (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); + jetHist.add("tracks/deuteron/h3TofNsigmaDeuteronVsPtVsJetPtBkgSub_jet", "h3TofNsigmaDeuteronVsPtVsJetPtBkgSub_jet; TofNsigma; #it{p}_{T} (GeV); jet #it{p}_{T} (Bkg Sub) (GeV)", HistType::kTH3F, {{100, -5, 5}, {50, 0., 5.}, {PtJetAxis}}); jetHist.add("tracks/antiDeuteron/h2TofNsigmaantiDeuteronVsPt_jet", "h2TofNsigmaantiDeuteronVsPt_jet; TofNsigma; #it{p}_{T} (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); + jetHist.add("tracks/antiDeuteron/h3TofNsigmaantiDeuteronVsPtVsJetPtBkgSub_jet", "h3TofNsigmaantiDeuteronVsPtVsJetPtBkgSub_jet; TofNsigma; #it{p}_{T} (GeV); jet #it{p}_{T} (Bkg Sub) (GeV)", HistType::kTH3F, {{100, -5, 5}, {50, 0., 5.}, {PtJetAxis}}); jetHist.add("tracks/triton/h2TofNsigmaTritonVsPt_jet", "h2TofNsigmaTritonVsPt_jet; TofNsigma; #it{p}_{T} (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); jetHist.add("tracks/antiTriton/h2TofNsigmaantiTritonVsPt_jet", "h2TofNsigmaantiTritonVsPt_jet; TofNsigma; #it{p}_{T} (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); jetHist.add("tracks/helium/h2TofNsigmaHeliumVsPt_jet", "h2TofNsigmaHeliumVsPt_jet; TofNsigma; #it{p}_{T}/z (GeV)", HistType::kTH2F, {{100, -5, 5}, {50, 0., 5.}}); @@ -572,6 +664,16 @@ struct nucleiInJets { jetHist.add(("tracksInc/dcaxy/rec/" + particle + "/tpcPtVsDcaxy3DPIDTOF").c_str(), "pT vs Dcaxy vs Centrality (PID TOF)", HistType::kTH3F, {{PtAxis}, {100, 0, 100}, dcaxyAxis}); } + // DCAxy templates are needed by both the data and reconstructed-MC processes. + const AxisSpec antiProtonTemplateOriginAxis{4, 0.5, 4.5, "1 primary #bar{p}, 2 material #bar{p}, 3 weak-decay #bar{p}, 4 other/fake"}; + for (const auto& region : {"jetCone", "perpCone"}) { + for (const auto& pid : {"TPC", "TPCTOF", "TPCTOFVeto"}) { + const std::string base = std::string("feeddown/antiProton/template/") + region + "/" + pid + "/"; + jetHist.add(base + "data", "data #bar{p} candidates before DCAxy cut; #it{p}_{T}^{rec}; DCA_{xy}", HistType::kTH2D, {{PtAxis}, {dcaxyAxis}}); + jetHist.add(base + "mc", "MC #bar{p} candidates before DCAxy cut; #it{p}_{T}^{rec}; DCA_{xy}; truth origin", HistType::kTH3D, {{PtAxis}, {dcaxyAxis}, {antiProtonTemplateOriginAxis}}); + } + } + if (isMC) { // inc jetHist.add("recInc/eventStat", "Event statistics (inclusive)", HistType::kTH1F, {{6, 0.f, 6.f}}); @@ -591,13 +693,16 @@ struct nucleiInJets { // Event and signal loss analysis histograms (inclusive) jetHist.add("eventLoss/hEventStatistics", "Event Statistics for Loss Analysis", kTH1F, {{10, 0.f, 10.f}}); jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(1, "All Generated"); - jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(2, "Gen |Vz|<10"); + jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(2, "Gen |Vz| cut"); jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(3, "Gen True INEL>0"); jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(4, "Has Reco Coll"); jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(5, "Pass Sel8"); - jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(6, "Pass |Vz|<10"); - jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(7, "Pass rec INEL>0"); - jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(8, "EvSelPassedRecINELgt0"); + jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(6, "Pass reco |Vz| cut"); + jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(7, "Pass extra event sel"); + jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(8, "Reco selected"); + jetHist.get(HIST("eventLoss/hEventStatistics"))->GetXaxis()->SetBinLabel(9, "Reco selected + true INEL>0"); + jetHist.add("eventLoss/hRecoCollPerMCCollVsCent", "Reco collisions per MC collision;N_{reco coll};Centrality", HistType::kTH2F, {{10, -0.5f, 9.5f}, {100, 0.f, 100.f}}); + jetHist.add("eventLoss/hSelectedRecoCollPerMCCollVsCent", "Selected reco collisions per MC collision;N_{selected reco coll};Centrality", HistType::kTH2F, {{10, -0.5f, 9.5f}, {100, 0.f, 100.f}}); // Signal loss histograms (only the ones that are actually used) jetHist.add("eventLoss/signalLoss/h3GenParticlesPtVsEtaVsCent_INELgt0", "Generated Particles p_{T} vs #eta vs Centrality", HistType::kTH3F, {{PtAxis}, {100, -1.5f, 1.5f}, {100, 0, 100}}); @@ -701,6 +806,9 @@ struct nucleiInJets { jetHist.add("recmatched/h2ResponseMatrix", "matched jet pT;#it{p}_{T} (mes.); #it{p}_{T} (true)", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); jetHist.add("recmatched/h2ResponseMatrixLeadingJet", "matched jet rec pT vs true pt;#it{p}_{T} (mes.); #it{p}_{T} (true)", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); jetHist.add("recmatched/mcC/h2ResponseMatrixLeadingJet", "matched jet rec pT vs true pt;#it{p}_{T} (mes.); #it{p}_{T} (true)", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); + jetHist.add("recmatched/qa/h2JetConeEtaPhiBeforeAcceptance", "matched jet-cone axis before full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("recmatched/qa/h2JetConeEtaPhiAfterAcceptance", "matched jet-cone axis after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); + jetHist.add("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance", "matched perpendicular-cone axes after full-cone acceptance;#eta_{axis};#varphi_{axis}", HistType::kTH2F, {{EtaAxis}, {70, 0.f, 7.f}}); ///////// jetHist.add("recmatched/hRecJetPt", "matched jet pT (Rec level);#it{p}_{T,jet part} (GeV/#it{c}); #it{p}_{T,jet part} - #it{p}_{T,jet det}", HistType::kTH1F, {{100, 0., 100.}}); @@ -715,6 +823,12 @@ struct nucleiInJets { jetHist.add("eff/recmatched/pt/PtParticleTypeTPCTOF", "Pt (p) vs jetflag vs particletype", HistType::kTH3D, {{PtAxis}, {2, 0, 2}, {14, -7, 7}}); jetHist.add("eff/recmatched/pt/PtParticleTypeTPCTOFVeto", "Pt (p) vs jetflag vs particletype", HistType::kTH3D, {{PtAxis}, {2, 0, 2}, {14, -7, 7}}); + jetHist.add("eff/recmatched/jetCone/ptEta/PtEtaParticleType", "rec matched jet cone; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPC", "rec matched jet cone TPC; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTOF", "rec matched jet cone TOF; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPCTOF", "rec matched jet cone TPC+TOF; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPCTOFVeto", "rec matched jet cone TPC+TOF veto; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/perpCone/pt/PtParticleType", "Pt (p) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/perpCone/mcC/pt/PtParticleType", "Pt (rec) vs Pt (true) vs particletype", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/perpCone/mcCSpectra/pt/PtParticleType", "Pt (rec) vs Pt (true) vs particletype", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); @@ -722,8 +836,62 @@ struct nucleiInJets { jetHist.add("eff/recmatched/perpCone/pt/PtParticleTypeTOF", "Pt (p) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOF", "Pt (p) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOFVeto", "Pt (p) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/perpCone/ptEta/PtEtaParticleType", "rec matched perpendicular cone; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPC", "rec matched perpendicular cone TPC; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTOF", "rec matched perpendicular cone TOF; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPCTOF", "rec matched perpendicular cone TPC+TOF; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPCTOFVeto", "rec matched perpendicular cone TPC+TOF veto; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/gen/pt/PtParticleType", "Pt (p) vs jetflag vs particletype", HistType::kTH3D, {{PtAxis}, {2, 0, 2}, {14, -7, 7}}); + jetHist.add("eff/recmatched/gen/jetCone/ptEta/PtEtaParticleType", "generated jet cone; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); jetHist.add("eff/recmatched/gen/perpCone/pt/PtParticleType", "Pt (p) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/gen/perpCone/ptEta/PtEtaParticleType", "generated perpendicular cone; #it{p}_{T}^{true} (GeV/#it{c}); #eta^{true}; particle type", HistType::kTH3D, {{PtAxis}, {EtaAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/mcC/gen/pt/PtParticleType", "Pt (gen, mcC) vs jetflag vs particletype", HistType::kTH3D, {{PtAxis}, {2, 0, 2}, {14, -7, 7}}); + jetHist.add("eff/recmatched/mcCSpectra/gen/pt/PtParticleType", "Pt (gen, mcCSpectra) vs jetflag vs particletype", HistType::kTH3D, {{PtAxis}, {2, 0, 2}, {14, -7, 7}}); + jetHist.add("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType", "Pt (gen, mcC, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType", "Pt (gen, mcCSpectra, perp cone) vs particletype", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + + jetHist.add("eff/recmatched/mcClosure/hSelectedRecoEvents", "matched reconstructed events per closure sample; sample; events", HistType::kTH1D, {{2, 0., 2.}}); + jetHist.get(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"))->GetXaxis()->SetBinLabel(1, "training"); + jetHist.get(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"))->GetXaxis()->SetBinLabel(2, "data-like"); + for (const auto& sample : {"training", "dataLike"}) { + for (const auto& region : {"jetCone", "perpCone"}) { + const std::string base = std::string("eff/recmatched/mcClosure/") + sample + "/" + region + "/"; + jetHist.add(base + "hRecAll", "matched primary tracks; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hRecTPCTOFVeto", "matched primary tracks with TPC PID and TOF veto; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hRecTPCTOF", "matched primary tracks with TPC and TOF PID; reconstructed #it{p}_{T}; true #it{p}_{T}; particle type", HistType::kTH3D, {{PtAxis}, {PtAxis}, {14, -7, 7}}); + jetHist.add(base + "hGen", "generated primary particles; true #it{p}_{T}; particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + } + } + + jetHist.add("jetSelCorr/genSel/hLeadingJetPt", "particle-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{gen} (GeV/#it{c}); Entries", HistType::kTH1F, {{100, 0., 100.}}); + jetHist.add("jetSelCorr/recoSel/hLeadingJetPtBkgSub", "detector-level leading jet selected for jet-selection correction; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); Entries", HistType::kTH1F, {{120, -20., 100.}}); + jetHist.add("jetSelCorr/recoSel/hMatchedLeadingJetPt", "selected matched leading jet; #it{p}_{T,lead}^{reco,corr} (GeV/#it{c}); #it{p}_{T,lead}^{gen} (GeV/#it{c})", HistType::kTH2F, {{120, -20., 100.}, {100, 0., 100.}}); + jetHist.add("jetSelCorr/eventNorm/hEventCounts", "event count for leading-jet event-normalization correction; event selection; Entries", HistType::kTH1D, {{2, 0., 2.}}); + jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(1, "gen lead #it{p}_{T} > cut"); + jetHist.get(HIST("jetSelCorr/eventNorm/hEventCounts"))->GetXaxis()->SetBinLabel(2, "reco lead #it{p}_{T}^{corr} > cut"); + jetHist.add("jetSelCorr/recoSel/hMatchStatus", "selected detector leading-jet matching; status; reconstructed collisions", HistType::kTH1D, {{3, 0., 3.}}); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(1, "no truth match in MC collision"); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(2, "truth axis outside acceptance"); + jetHist.get(HIST("jetSelCorr/recoSel/hMatchStatus"))->GetXaxis()->SetBinLabel(3, "accepted truth axis"); + jetHist.add("jetSelCorr/eventNorm/hSelectedRecoCollisions", "selected reconstructed collisions per MC collision; selected reconstructed collisions; MC collisions", HistType::kTH1D, {{11, -0.5, 10.5}}); + jetHist.add("jetSelCorr/matchedTruthAxis/hEventCount", "selected reconstructed collisions with accepted matched truth axis; selection; reconstructed collisions", HistType::kTH1D, {{1, 0., 1.}}); + jetHist.add("jetSelCorr/matchedTruthAxis/jetCone/pt/PtParticleType", "generated primaries around the matched truth axis; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/matchedTruthAxis/perpCone/pt/PtParticleType", "generated primaries in perpendicular cones of the matched truth jet; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/genSel/jetCone/pt/PtParticleType", "generated primaries in particle-level leading-jet cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/genSel/perpCone/pt/PtParticleType", "generated primaries in particle-level perpendicular cone; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/jetCone/pt/PtParticleType", "generated primaries around the selected detector leading-jet axis; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + jetHist.add("jetSelCorr/recoSel/perpCone/pt/PtParticleType", "generated primaries in perpendicular cones of the selected detector leading jet; #it{p}_{T}^{gen} (GeV/#it{c}); particle type", HistType::kTH2D, {{PtAxis}, {14, -7, 7}}); + + jetHist.add("feeddown/antiProton/jetCone/PtOrigin", "reconstructed #bar{p} origin in jet cone; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTPC", "reconstructed #bar{p} origin in jet cone, TPC PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTOF", "reconstructed #bar{p} origin in jet cone, TOF matched; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTPCTOF", "reconstructed #bar{p} origin in jet cone, TPC+TOF PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/jetCone/PtOriginTPCTOFVeto", "reconstructed #bar{p} origin in jet cone, TPC+TOF veto; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOrigin", "reconstructed #bar{p} origin in perpendicular cone; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTPC", "reconstructed #bar{p} origin in perpendicular cone, TPC PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTOF", "reconstructed #bar{p} origin in perpendicular cone, TOF matched; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTPCTOF", "reconstructed #bar{p} origin in perpendicular cone, TPC+TOF PID; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); + jetHist.add("feeddown/antiProton/perpCone/PtOriginTPCTOFVeto", "reconstructed #bar{p} origin in perpendicular cone, TPC+TOF veto; #it{p}_{T}^{rec} (GeV/#it{c}); origin", HistType::kTH2D, {{PtAxis}, {ParticleOriginAxis}}); // gen matched jetHist.add("genmatched/hRecMatchedJetPt", "matched jet pT (Rec level);#it{p}_{T,jet part} (GeV/#it{c}); #it{p}_{T,jet part} - #it{p}_{T,jet det}", HistType::kTH2F, {{100, 0., 100.}, {400, -20., 20.}}); jetHist.add("genmatched/hRecMatchedVsGenJetPt", "matched jet pT (Rec level);#it{p}_{T,jet det}; #it{p}_{T,jet part} (GeV/#it{c})", HistType::kTH2F, {{100, 0., 100.}, {100, 0., 100.}}); @@ -755,8 +923,8 @@ struct nucleiInJets { { std::array PerpendicularConeAxisPhi = {-999.0f, -999.0f}; // build 2 perp cones in phi around the leading jet (right and left of the jet) - PerpendicularConeAxisPhi[0] = RecoDecay::constrainAngle(jetPhi + (o2::constants::math::PIHalf)); // This will contrain the angel between 0-2Pi - PerpendicularConeAxisPhi[1] = RecoDecay::constrainAngle(jetPhi - (o2::constants::math::PIHalf)); // This will contrain the angel between 0-2Pi + PerpendicularConeAxisPhi[0] = RecoDecay::constrainAngle(jetPhi + o2::constants::math::PIHalf); // This will contrain the angel between 0-2Pi + PerpendicularConeAxisPhi[1] = RecoDecay::constrainAngle(jetPhi - o2::constants::math::PIHalf); // This will contrain the angel between 0-2Pi return PerpendicularConeAxisPhi; } @@ -773,7 +941,7 @@ struct nucleiInJets { } template - bool isTrackSelectedWithoutDcaxy(const TrackType track) + bool isTrackSelectedWithoutDcaxy(const TrackType& track) { // standard track selection if (track.pt() < cfgtrkMinPt) @@ -804,7 +972,7 @@ struct nucleiInJets { } template - bool isTrackSelected(const TrackType track) + bool isTrackSelected(const TrackType& track) { if (!isTrackSelectedWithoutDcaxy(track)) return false; @@ -830,6 +998,36 @@ struct nucleiInJets { template void fillTrackInfo(const TracksType& trk, const JetType& jets, std::vector& leadingJetPtEtaPhi, float backgroundRho = -1.0f) { + if constexpr (!IsMC) { + if (isWithLeadingJet && leadingJetPtEtaPhi.size() >= 3 && isConeAxisAccepted(leadingJetPtEtaPhi[1]) && + backgroundRho > 0.f && leadingJetPtEtaPhi[0] - backgroundRho * M_PI * cfgjetR * cfgjetR > cfgjetPtBkgSubMin && + trk.sign() < 0 && trk.pt() <= cMaxPt && isTrackSelectedWithoutDcaxy(trk) && + (!useRapidityCutForPID || isRapiditySelectedForPID(trk.rapidity(o2::constants::physics::MassProton)))) { + const bool tpcPassed = std::abs(trk.tpcNSigmaPr()) < cfgnTPCPIDPr; + if (tpcPassed) { + const bool tofPassed = trk.hasTOF() && std::abs(trk.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const double deltaEta = leadingJetPtEtaPhi[1] - trk.eta(); + const double deltaPhi = TVector2::Phi_mpi_pi(leadingJetPtEtaPhi[2] - trk.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhi) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPC/data"), trk.pt(), trk.dcaXY()); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOF/data"), trk.pt(), trk.dcaXY()); + if (!trk.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOFVeto/data"), trk.pt(), trk.dcaXY()); + } + const auto perpPhi = getPerpendicuarPhi(leadingJetPtEtaPhi[2]); + const double deltaPhiPerp1 = TVector2::Phi_mpi_pi(perpPhi[0] - trk.phi()); + const double deltaPhiPerp2 = TVector2::Phi_mpi_pi(perpPhi[1] - trk.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp1) < cfgjetR || RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp2) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPC/data"), trk.pt(), trk.dcaXY()); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOF/data"), trk.pt(), trk.dcaXY()); + if (!trk.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOFVeto/data"), trk.pt(), trk.dcaXY()); + } + } + } + } if (!isTrackSelected(trk)) return; if (trk.pt() > cMaxPt) @@ -839,10 +1037,14 @@ struct nucleiInJets { bool jetFlagPerpCone = false; float jetPt = -999.; - if (isWithLeadingJet) { + const bool hasValidLeadingJet = leadingJetPtEtaPhi.size() >= 3 && leadingJetPtEtaPhi[0] > 0.f; + if (isWithLeadingJet && hasValidLeadingJet) { + if (!isConeAxisAccepted(leadingJetPtEtaPhi[1])) { + return; + } double delPhi = TVector2::Phi_mpi_pi(leadingJetPtEtaPhi[2] - trk.phi()); double delEta = leadingJetPtEtaPhi[1] - trk.eta(); - double R = RecoDecay::sumOfSquares(delEta, delPhi); + double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); if (R < cfgjetR) jetFlag = true; jetPt = leadingJetPtEtaPhi[0]; @@ -850,19 +1052,23 @@ struct nucleiInJets { std::array perpConePhiJet = getPerpendicuarPhi(leadingJetPtEtaPhi[2]); double delPhiPerpCone1 = TVector2::Phi_mpi_pi(perpConePhiJet[0] - trk.phi()); double delPhiPerpCone2 = TVector2::Phi_mpi_pi(perpConePhiJet[1] - trk.phi()); - double RPerpCone1 = RecoDecay::sumOfSquares(delEta, delPhiPerpCone1); - double RPerpCone2 = RecoDecay::sumOfSquares(delEta, delPhiPerpCone2); + double RPerpCone1 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone1); + double RPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); if (RPerpCone1 < cfgjetR || RPerpCone2 < cfgjetR) jetFlagPerpCone = true; - } else { + } else if (!isWithLeadingJet) { for (auto const& jet : jets) { + if (!isConeAxisAccepted(jet.eta())) { + continue; + } double delPhi = TVector2::Phi_mpi_pi(jet.phi() - trk.phi()); double delEta = jet.eta() - trk.eta(); - double R = RecoDecay::sumOfSquares(delEta, delPhi); - if (R < cfgjetR) + double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); + if (R < cfgjetR) { jetFlag = true; - jetPt = jet.pt(); - break; + jetPt = jet.pt(); + break; + } } } // tof @@ -1012,6 +1218,8 @@ struct nucleiInJets { jetHist.fill(HIST("tracks/proton/h3TOFmass2ProtonVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassProton * MassProton, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/deuteron/h3TOFmassDeuteronVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/deuteron/h3TOFmass2DeuteronVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/proton/h3TofNsigmaProtonVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaPr(), trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/deuteron/h3TofNsigmaDeuteronVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaDe(), trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/triton/h3TOFmassTritonVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/triton/h3TOFmass2TritonVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassTriton * MassTriton, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/helium/h3TOFmassHeliumVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt() / 2.0, jetPtBkgSub); @@ -1030,6 +1238,7 @@ struct nucleiInJets { if (backgroundRho > 0) { jetHist.fill(HIST("tracks/proton/h3TOFmassProtonVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/proton/h3TOFmass2ProtonVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassProton * MassProton, trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/proton/h3TofNsigmaProtonVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaPr(), trk.pt(), jetPtBkgSub); } jetHist.fill(HIST("tracks/proton/h2TofNsigmaProtonVsPt_jet"), trk.tofNSigmaPr(), trk.pt()); jetHist.fill(HIST("tracks/proton/h3TpcNsigmaTofNsigmaProtonVsPt_jet"), trk.tpcNSigmaPr(), trk.tofNSigmaPr(), trk.pt()); @@ -1040,6 +1249,7 @@ struct nucleiInJets { if (backgroundRho > 0) { jetHist.fill(HIST("tracks/deuteron/h3TOFmassDeuteronVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/deuteron/h3TOFmass2DeuteronVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/deuteron/h3TofNsigmaDeuteronVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaDe(), trk.pt(), jetPtBkgSub); } jetHist.fill(HIST("tracks/deuteron/h2TofNsigmaDeuteronVsPt_jet"), trk.tofNSigmaDe(), trk.pt()); jetHist.fill(HIST("tracks/deuteron/h3TpcNsigmaTofNsigmaDeuteronVsPt_jet"), trk.tpcNSigmaDe(), trk.tofNSigmaDe(), trk.pt()); @@ -1170,11 +1380,11 @@ struct nucleiInJets { jetHist.fill(HIST("tracks/antiDeuteron/dca/after/hDCAxyVsPtantiDeuteron_jet"), trk.dcaXY(), trk.pt()); jetHist.fill(HIST("tracks/antiDeuteron/dca/after/hDCAzVsPtantiDeuteron_jet"), trk.dcaZ(), trk.pt()); } - if (cEnableHeliumQA && std::abs(trk.tpcNSigmaTr()) < cfgnTPCPIDTr) { + if (cEnableTritonQA && std::abs(trk.tpcNSigmaTr()) < cfgnTPCPIDTr) { jetHist.fill(HIST("tracks/antiTriton/dca/after/hDCAxyVsPtantiTriton_jet"), trk.dcaXY(), trk.pt()); jetHist.fill(HIST("tracks/antiTriton/dca/after/hDCAzVsPtantiTriton_jet"), trk.dcaZ(), trk.pt()); } - if (cEnableTritonQA && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (cEnableHeliumQA && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("tracks/antiHelium/dca/after/hDCAxyVsPtantiHelium_jet"), trk.dcaXY(), trk.pt()); jetHist.fill(HIST("tracks/antiHelium/dca/after/hDCAzVsPtantiHelium_jet"), trk.dcaZ(), trk.pt()); } @@ -1202,6 +1412,8 @@ struct nucleiInJets { jetHist.fill(HIST("tracks/antiProton/h3TOFmass2antiProtonVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassProton * MassProton, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/antiDeuteron/h3TOFmassantiDeuteronVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/antiDeuteron/h3TOFmass2antiDeuteronVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/antiProton/h3TofNsigmaantiProtonVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaPr(), trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/antiDeuteron/h3TofNsigmaantiDeuteronVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaDe(), trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/antiTriton/h3TOFmassantiTritonVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/antiTriton/h3TOFmass2antiTritonVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassTriton * MassTriton, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/antiHelium/h3TOFmassantiHeliumVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt() / 2.0, jetPtBkgSub); @@ -1214,6 +1426,7 @@ struct nucleiInJets { if (backgroundRho > 0) { jetHist.fill(HIST("tracks/antiProton/h3TOFmassantiProtonVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); jetHist.fill(HIST("tracks/antiProton/h3TOFmass2antiProtonVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassProton * MassProton, trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/antiProton/h3TofNsigmaantiProtonVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaPr(), trk.pt(), jetPtBkgSub); } jetHist.fill(HIST("tracks/antiProton/h2TofNsigmaantiProtonVsPt_jet"), trk.tofNSigmaPr(), trk.pt()); jetHist.fill(HIST("tracks/antiProton/h3TpcNsigmaTofNsigmaantiProtonVsPt_jet"), trk.tpcNSigmaPr(), trk.tofNSigmaPr(), trk.pt()); @@ -1221,6 +1434,11 @@ struct nucleiInJets { if (std::abs(trk.tpcNSigmaDe()) < cfgnTPCPIDDe) { jetHist.fill(HIST("tracks/antiDeuteron/h3TOFmassantiDeuteronVsPtVsJetPt_jet"), massTOF, trk.pt(), jetPt); jetHist.fill(HIST("tracks/antiDeuteron/h3TOFmass2antiDeuteronVsPtVsJetPt_jet"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), jetPt); + if (backgroundRho > 0) { + jetHist.fill(HIST("tracks/antiDeuteron/h3TOFmassantiDeuteronVsPtVsJetPtBkgSub_jet"), massTOF, trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/antiDeuteron/h3TOFmass2antiDeuteronVsPtVsJetPtBkgSub_jet"), massTOF * massTOF - MassDeuteron * MassDeuteron, trk.pt(), jetPtBkgSub); + jetHist.fill(HIST("tracks/antiDeuteron/h3TofNsigmaantiDeuteronVsPtVsJetPtBkgSub_jet"), trk.tofNSigmaDe(), trk.pt(), jetPtBkgSub); + } jetHist.fill(HIST("tracks/antiDeuteron/h2TofNsigmaantiDeuteronVsPt_jet"), trk.tofNSigmaDe(), trk.pt()); jetHist.fill(HIST("tracks/antiDeuteron/h3TpcNsigmaTofNsigmaantiDeuteronVsPt_jet"), trk.tpcNSigmaDe(), trk.tofNSigmaDe(), trk.pt()); } @@ -1526,11 +1744,11 @@ struct nucleiInJets { jetHist.fill(HIST("tracks/antiDeuteron/dca/after/hDCAxyVsPtantiDeuteron"), trk.dcaXY(), trk.pt()); jetHist.fill(HIST("tracks/antiDeuteron/dca/after/hDCAzVsPtantiDeuteron"), trk.dcaZ(), trk.pt()); } - if (cEnableHeliumQA && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (cEnableTritonQA && std::abs(trk.tpcNSigmaTr()) < cfgnTPCPIDTr) { jetHist.fill(HIST("tracks/antiTriton/dca/after/hDCAxyVsPtantiTriton"), trk.dcaXY(), trk.pt()); jetHist.fill(HIST("tracks/antiTriton/dca/after/hDCAzVsPtantiTriton"), trk.dcaZ(), trk.pt()); } - if (cEnableTritonQA && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { + if (cEnableHeliumQA && std::abs(trk.tpcNSigmaHe()) < cfgnTPCPIDHe) { jetHist.fill(HIST("tracks/antiHelium/dca/after/hDCAxyVsPtantiHelium"), trk.dcaXY(), trk.pt()); jetHist.fill(HIST("tracks/antiHelium/dca/after/hDCAzVsPtantiHelium"), trk.dcaZ(), trk.pt()); } @@ -1609,8 +1827,8 @@ struct nucleiInJets { { auto bc = collision.bc_as(); initCCDB(bc); + jetHist.fill(HIST("hNEvents"), 0.5); if (applySkim) { - jetHist.fill(HIST("hNEvents"), 0.5); bool zorroSelected = zorro.isSelected(bc.globalBC()); if (!zorroSelected) { return; @@ -1623,9 +1841,20 @@ struct nucleiInJets { if (!jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) return; jetHist.fill(HIST("hNEvents"), 3.5); + if (selNoSameBunchPileup && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup"))) + return; + jetHist.fill(HIST("hNEvents"), 4.5); + if (selIsGoodZvtxFT0vsPV && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV"))) + return; + jetHist.fill(HIST("hNEvents"), 5.5); + if (useOccupancy && !isOccupancyAccepted(collision)) + return; + jetHist.fill(HIST("hNEvents"), 6.5); int nJets = 0; - std::vector leadingJetWithPtEtaPhi(3); + int nAcceptedJets = 0; + std::vector leadingJetWithPtEtaPhi(3, -999.f); float leadingJetPt = -1.0f; + float leadingJetPtBkgSub = -999.0f; float backgroundRho = collision.rho(); // Get background rho from collision // Fill background rho histogram once per event @@ -1635,6 +1864,7 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPt"), chargedjet.pt()); jetHist.fill(HIST("jet/h1JetEta"), chargedjet.eta()); jetHist.fill(HIST("jet/h1JetPhi"), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiBeforeAcceptance"), chargedjet.eta(), chargedjet.phi()); // Calculate background subtracted jet pt float jetArea = M_PI * cfgjetR * cfgjetR; // Assuming circular jet area @@ -1644,7 +1874,17 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPtBkgSub"), jetPtBkgSub); jetHist.fill(HIST("jet/h2JetPtVsBkgRho"), chargedjet.pt(), backgroundRho); - if (chargedjet.pt() > leadingJetPt) { + if (isConeAxisAccepted(chargedjet.eta())) { + const auto perpConePhiJet = getPerpendicuarPhi(chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiAfterAcceptance"), chargedjet.eta(), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[0]); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[1]); + nAcceptedJets++; + } + + if (isConeAxisAccepted(chargedjet.eta()) && chargedjet.pt() > leadingJetPt) { + leadingJetPt = chargedjet.pt(); + leadingJetPtBkgSub = jetPtBkgSub; leadingJetWithPtEtaPhi[0] = chargedjet.pt(); leadingJetWithPtEtaPhi[1] = chargedjet.eta(); leadingJetWithPtEtaPhi[2] = chargedjet.phi(); @@ -1652,16 +1892,34 @@ struct nucleiInJets { nJets++; } jetHist.fill(HIST("jet/nJetsPerEvent"), nJets); + jetHist.fill(HIST("jet/qa/nAcceptedJetsPerEvent"), nAcceptedJets); jetHist.fill(HIST("vertexZ"), collision.posZ()); if (nJets > 0) { jetHist.fill(HIST("jet/vertexZ"), collision.posZ()); - jetHist.fill(HIST("hNEvents"), 4.5); + jetHist.fill(HIST("hNEvents"), 7.5); } else { jetHist.fill(HIST("jetOut/vertexZ"), collision.posZ()); } + if (nAcceptedJets > 0) { + jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); + jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); + } + if (backgroundRho > 0 && leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); + jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); + jetHist.fill(HIST("hNEvents"), 8.5); + } if (isWithJetEvents && nJets == 0) return; + if (isWithJetEvents && nAcceptedJets == 0) + return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); + if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/h1JetEventsBeforeRhoSelection"), 0.5); + if (backgroundRho > 0) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } + } for (const auto& track : tracks) { auto trk = track.track_as(); fillTrackInfo(trk, chargedjets, leadingJetWithPtEtaPhi, backgroundRho); @@ -1673,8 +1931,8 @@ struct nucleiInJets { { auto bc = collision.bc_as(); initCCDB(bc); + jetHist.fill(HIST("hNEvents"), 0.5); if (applySkim) { - jetHist.fill(HIST("hNEvents"), 0.5); bool zorroSelected = zorro.isSelected(bc.globalBC()); if (!zorroSelected) { return; @@ -1688,9 +1946,20 @@ struct nucleiInJets { if (!jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) return; jetHist.fill(HIST("hNEvents"), 3.5); + if (selNoSameBunchPileup && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup"))) + return; + jetHist.fill(HIST("hNEvents"), 4.5); + if (selIsGoodZvtxFT0vsPV && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV"))) + return; + jetHist.fill(HIST("hNEvents"), 5.5); + if (useOccupancy && !isOccupancyAccepted(collision)) + return; + jetHist.fill(HIST("hNEvents"), 6.5); int nJets = 0; - std::vector leadingJetWithPtEtaPhi(3); + int nAcceptedJets = 0; + std::vector leadingJetWithPtEtaPhi(3, -999.f); float leadingJetPt = -1.0f; + float leadingJetPtBkgSub = -999.0f; float backgroundRho = collision.rho(); // Get background rho from collision // Fill background rho histogram once per event @@ -1700,6 +1969,7 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPt"), chargedjet.pt()); jetHist.fill(HIST("jet/h1JetEta"), chargedjet.eta()); jetHist.fill(HIST("jet/h1JetPhi"), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiBeforeAcceptance"), chargedjet.eta(), chargedjet.phi()); // Calculate background subtracted jet pt float jetArea = M_PI * cfgjetR * cfgjetR; // Assuming circular jet area @@ -1709,7 +1979,17 @@ struct nucleiInJets { jetHist.fill(HIST("jet/h1JetPtBkgSub"), jetPtBkgSub); jetHist.fill(HIST("jet/h2JetPtVsBkgRho"), chargedjet.pt(), backgroundRho); - if (chargedjet.pt() > leadingJetPt) { + if (isConeAxisAccepted(chargedjet.eta())) { + const auto perpConePhiJet = getPerpendicuarPhi(chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2JetConeEtaPhiAfterAcceptance"), chargedjet.eta(), chargedjet.phi()); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[0]); + jetHist.fill(HIST("jet/qa/h2PerpConeEtaPhiAfterAcceptance"), chargedjet.eta(), perpConePhiJet[1]); + nAcceptedJets++; + } + + if (isConeAxisAccepted(chargedjet.eta()) && chargedjet.pt() > leadingJetPt) { + leadingJetPt = chargedjet.pt(); + leadingJetPtBkgSub = jetPtBkgSub; leadingJetWithPtEtaPhi[0] = chargedjet.pt(); leadingJetWithPtEtaPhi[1] = chargedjet.eta(); leadingJetWithPtEtaPhi[2] = chargedjet.phi(); @@ -1717,16 +1997,34 @@ struct nucleiInJets { nJets++; } jetHist.fill(HIST("jet/nJetsPerEvent"), nJets); + jetHist.fill(HIST("jet/qa/nAcceptedJetsPerEvent"), nAcceptedJets); jetHist.fill(HIST("vertexZ"), collision.posZ()); if (nJets > 0) { jetHist.fill(HIST("jet/vertexZ"), collision.posZ()); - jetHist.fill(HIST("hNEvents"), 4.5); + jetHist.fill(HIST("hNEvents"), 7.5); } else { jetHist.fill(HIST("jetOut/vertexZ"), collision.posZ()); } + if (nAcceptedJets > 0) { + jetHist.fill(HIST("jetBkgSub/h1LeadingJetPtBkgSub"), leadingJetPtBkgSub); + jetHist.fill(HIST("jetBkgSub/h2LeadingJetPtBkgSubVsVertexZ"), leadingJetPtBkgSub, collision.posZ()); + } + if (backgroundRho > 0 && leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/vertexZ"), collision.posZ()); + jetHist.fill(HIST("jetBkgSub/h1BkgRhoLeadingJetPtBkgSubAboveCut"), backgroundRho); + jetHist.fill(HIST("hNEvents"), 8.5); + } if (isWithJetEvents && nJets == 0) return; + if (isWithJetEvents && nAcceptedJets == 0) + return; jetHist.fill(HIST("jet/h1JetEvents"), 0.5); + if (leadingJetPtBkgSub > cfgjetPtBkgSubMin) { + jetHist.fill(HIST("jetBkgSub/h1JetEventsBeforeRhoSelection"), 0.5); + if (backgroundRho > 0) { + jetHist.fill(HIST("jetBkgSub/h1JetEvents"), 0.5); + } + } for (auto& track : tracks) { auto trk = track.track_as(); fillTrackInfo(trk, chargedjets, leadingJetWithPtEtaPhi, backgroundRho); @@ -1781,8 +2079,7 @@ struct nucleiInJets { } auto rapidityData = [&](float m2z) { - const float rap = trk.rapidity(m2z); - return rap < std::abs(cfgtrkMaxRap); + return isRapiditySelectedForPID(trk.rapidity(m2z)); }; auto prRapidityWithinRange = rapidityData(o2::constants::physics::MassProton); @@ -2062,7 +2359,7 @@ struct nucleiInJets { continue; if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta) continue; - if (std::fabs(mcParticle.y()) > cfgtrkMaxRap) + if (!isRapiditySelectedForPID(mcParticle.y())) continue; bool jetFlag = false; @@ -2071,10 +2368,10 @@ struct nucleiInJets { double delPhi = TVector2::Phi_mpi_pi(mcpjet.phi() - mcParticle.phi()); double delEta = mcpjet.eta() - mcParticle.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); - if (R < cfgjetR) + if (R < cfgjetR) { jetFlag = true; - // jetPt = mcpjet.pt(); - break; + break; + } } // jet if (mapPDGToValue(mcParticle.pdgCode()) != 0) { jetHist.fill(HIST("mcpJet/pt/PtParticleType"), mcParticle.pt(), jetFlag, mapPDGToValue(mcParticle.pdgCode())); @@ -2097,13 +2394,18 @@ struct nucleiInJets { jetHist.fill(HIST("mcdJet/eventStat"), 2.5); int nJets = 0; - std::vector leadingJetWithPtEtaPhi(3); + int nAcceptedJets = 0; + std::vector leadingJetWithPtEtaPhi(3, -999.f); float leadingJetPt = -1.0f; for (auto& mcdjet : mcdjets) { jetHist.fill(HIST("mcdJet/hJetPt"), mcdjet.pt()); jetHist.fill(HIST("mcdJet/hJetEta"), mcdjet.eta()); jetHist.fill(HIST("mcdJet/hJetPhi"), mcdjet.phi()); - if (mcdjet.pt() > leadingJetPt) { + if (isConeAxisAccepted(mcdjet.eta())) { + nAcceptedJets++; + } + if (isConeAxisAccepted(mcdjet.eta()) && mcdjet.pt() > leadingJetPt) { + leadingJetPt = mcdjet.pt(); leadingJetWithPtEtaPhi[0] = mcdjet.pt(); leadingJetWithPtEtaPhi[1] = mcdjet.eta(); leadingJetWithPtEtaPhi[2] = mcdjet.phi(); @@ -2114,6 +2416,8 @@ struct nucleiInJets { jetHist.fill(HIST("mcdJet/nJetsPerEvent"), nJets); if (isWithJetEvents && nJets == 0) return; + if (isWithJetEvents && nAcceptedJets == 0) + return; for (const auto& track : tracks) { auto fullTrack = track.track_as(); if (!isTrackSelected(fullTrack)) @@ -2128,7 +2432,8 @@ struct nucleiInJets { bool jetFlag = false; bool jetFlagPerpCone = false; // float jetPt = -999.; - if (isWithLeadingJet) { + const bool hasValidLeadingJet = leadingJetWithPtEtaPhi.size() >= 3 && leadingJetWithPtEtaPhi[0] > 0.f; + if (isWithLeadingJet && hasValidLeadingJet) { double delPhi = TVector2::Phi_mpi_pi(leadingJetWithPtEtaPhi[2] - track.phi()); double delEta = leadingJetWithPtEtaPhi[1] - track.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); @@ -2141,15 +2446,18 @@ struct nucleiInJets { double RPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); if (RPerpCone1 < cfgjetR || RPerpCone2 < cfgjetR) jetFlagPerpCone = true; - } else { + } else if (!isWithLeadingJet) { for (const auto& mcdjet : mcdjets) { + if (!isConeAxisAccepted(mcdjet.eta())) { + continue; + } double delPhi = TVector2::Phi_mpi_pi(mcdjet.phi() - track.phi()); double delEta = mcdjet.eta() - track.eta(); double R = RecoDecay::sqrtSumOfSquares(delEta, delPhi); - if (R < cfgjetR) + if (R < cfgjetR) { jetFlag = true; - // jetPt = mcdjet.pt(); - break; + break; + } } // jet } if (mapPDGToValue(mcTrack.pdgCode()) != 0) { @@ -2161,6 +2469,77 @@ struct nucleiInJets { } Preslice> perMCCol = aod::jmcparticle::mcCollisionId; + + template + bool isRecoCollisionSelectedForJetSelectionCorrection(const CollType& collision) + { + if (std::abs(collision.posZ()) > cfgMaxZVertex) { + return false; + } + if (!jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) { + return false; + } + if (selNoSameBunchPileup && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup"))) { + return false; + } + if (selIsGoodZvtxFT0vsPV && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV"))) { + return false; + } + if (useOccupancy && !isOccupancyAccepted(collision)) { + return false; + } + return true; + } + + template + void fillJetSelectionCorrectionParticles(const ParticlesType& mcParticles, double jetEta, double jetPhi) + { + const auto perpConePhiJet = getPerpendicuarPhi(jetPhi); + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta) { + continue; + } + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcParticle.y())) { + continue; + } + + const auto particleType = mapPDGToValue(mcParticle.pdgCode()); + if (particleType == 0) { + continue; + } + + const double delEta = jetEta - mcParticle.eta(); + const double delPhi = TVector2::Phi_mpi_pi(jetPhi - mcParticle.phi()); + const double rJet = RecoDecay::sqrtSumOfSquares(delEta, delPhi); + if (rJet < cfgjetR) { + if constexpr (MatchedTruthAxis) { + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else if constexpr (RecoSelected) { + jetHist.fill(HIST("jetSelCorr/recoSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else { + jetHist.fill(HIST("jetSelCorr/genSel/jetCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } + } + + const double delPhiPerpCone1 = TVector2::Phi_mpi_pi(perpConePhiJet[0] - mcParticle.phi()); + const double delPhiPerpCone2 = TVector2::Phi_mpi_pi(perpConePhiJet[1] - mcParticle.phi()); + const double rPerpCone1 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone1); + const double rPerpCone2 = RecoDecay::sqrtSumOfSquares(delEta, delPhiPerpCone2); + if (rPerpCone1 < cfgjetR || rPerpCone2 < cfgjetR) { + if constexpr (MatchedTruthAxis) { + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else if constexpr (RecoSelected) { + jetHist.fill(HIST("jetSelCorr/recoSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else { + jetHist.fill(HIST("jetSelCorr/genSel/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } + } + } + } + void processRecMatched(JetCollWithLabel const& collision, JetMCDetTable const& mcdjets, soa::Join const& tracks, JetMCPartTable const&, TrackCandidatesMC const&, aod::JetParticles const& particleTracks, aod::JMcCollisions const&) @@ -2170,8 +2549,43 @@ struct nucleiInJets { if (!jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) return; + if (selNoSameBunchPileup && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup"))) + return; + if (selIsGoodZvtxFT0vsPV && !jetderiveddatautilities::selectCollision(collision, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV"))) + return; + if (useOccupancy && !isOccupancyAccepted(collision)) + return; + jetHist.fill(HIST("recmatched/vertexZ"), collision.posZ()); + // One random choice is shared by the reconstructed and generated spectra of this event. + const bool useTrainingHist = useTrainingHistForMcCollision(collision.mcCollision().globalIndex()); + const float backgroundRho = collision.rho(); + if (usebkgSubractionMC && backgroundRho <= 0.f) { + return; + } + const float jetArea = M_PI * cfgjetR * cfgjetR; + if (usebkgSubractionMC) { + jetHist.fill(HIST("jet/h1BkgRho"), backgroundRho); + } + + // Select the accepted detector-level leading jet before considering matches. + int32_t leadingDetJetId = -1; + float leadingDetJetPt = -1.f; + if (isWithLeadingJet) { + for (const auto& mcdjet : mcdjets) { + const float mcdJetPtForResponseSel = usebkgSubractionMC ? mcdjet.pt() - backgroundRho * jetArea : mcdjet.pt(); + if (isConeAxisAccepted(mcdjet.eta()) && mcdJetPtForResponseSel > leadingDetJetPt) { + leadingDetJetPt = mcdJetPtForResponseSel; + leadingDetJetId = mcdjet.globalIndex(); + } + } + if (leadingDetJetId < 0) { + return; + } + } + + std::vector mcdJetIds{}; std::vector mcdJetPt{}; std::vector mcdJetPhi{}; std::vector mcdJetEta{}; @@ -2183,7 +2597,7 @@ struct nucleiInJets { return; // LOG(info) <<" size(mcd) "< leadingJetWithPtEtaPhi(3); + std::vector leadingJetWithPtEtaPhi(3, -999.); for (const auto& mcdjet : mcdjets) { if (!mcdjet.has_matchedJetGeo()) continue; @@ -2191,21 +2605,36 @@ struct nucleiInJets { if (!mcpjet.has_matchedJetGeo()) continue; - mcdJetPt.push_back(mcdjet.pt()); + const float mcdJetPtForResponse = usebkgSubractionMC ? mcdjet.pt() - backgroundRho * jetArea : mcdjet.pt(); + if (mcdJetPtForResponse <= cfgjetPtBkgSubMinMC) { + continue; + } + const double jetAxisEtaForEff = isWithLeadingJet ? (useLeadingJetDetLevelValue ? mcdjet.eta() : mcpjet.eta()) : mcdjet.eta(); + const double jetAxisPhiForEff = isWithLeadingJet ? (useLeadingJetDetLevelValue ? mcdjet.phi() : mcpjet.phi()) : mcdjet.phi(); + jetHist.fill(HIST("recmatched/qa/h2JetConeEtaPhiBeforeAcceptance"), jetAxisEtaForEff, jetAxisPhiForEff); + if (!isConeAxisAccepted(jetAxisEtaForEff)) { + continue; + } + const auto perpConePhiJet = getPerpendicuarPhi(static_cast(jetAxisPhiForEff)); + jetHist.fill(HIST("recmatched/qa/h2JetConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, jetAxisPhiForEff); + jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[0]); + jetHist.fill(HIST("recmatched/qa/h2PerpConeEtaPhiAfterAcceptance"), jetAxisEtaForEff, perpConePhiJet[1]); + mcdJetIds.push_back(mcdjet.globalIndex()); + mcdJetPt.push_back(mcdJetPtForResponse); mcdJetPhi.push_back(mcdjet.phi()); mcdJetEta.push_back(mcdjet.eta()); mcpJetPt.push_back(mcpjet.pt()); mcpJetPhi.push_back(mcpjet.phi()); mcpJetEta.push_back(mcpjet.eta()); - jetHist.fill(HIST("recmatched/hRecMatchedJetPt"), mcpjet.pt(), mcpjet.pt() - mcdjet.pt()); + jetHist.fill(HIST("recmatched/hRecMatchedJetPt"), mcpjet.pt(), mcpjet.pt() - mcdJetPtForResponse); jetHist.fill(HIST("recmatched/hRecMatchedJetPhi"), mcpjet.phi(), mcpjet.phi() - mcdjet.phi()); jetHist.fill(HIST("recmatched/hRecMatchedJetEta"), mcpjet.eta(), mcpjet.eta() - mcdjet.eta()); - jetHist.fill(HIST("recmatched/hRecMatchedVsGenJetPtVsEta"), mcdjet.pt(), mcdjet.eta()); - jetHist.fill(HIST("recmatched/hRecJetPt"), mcdjet.pt()); + jetHist.fill(HIST("recmatched/hRecMatchedVsGenJetPtVsEta"), mcdJetPtForResponse, mcdjet.eta()); + jetHist.fill(HIST("recmatched/hRecJetPt"), mcdJetPtForResponse); jetHist.fill(HIST("recmatched/hGenJetPt"), mcpjet.pt()); - jetHist.fill(HIST("recmatched/h2ResponseMatrix"), mcdjet.pt(), mcpjet.pt()); + jetHist.fill(HIST("recmatched/h2ResponseMatrix"), mcdJetPtForResponse, mcpjet.pt()); } // mcpJet } // mcdJet @@ -2218,14 +2647,24 @@ struct nucleiInJets { } else { LOGP(fatal, "Error: Index {} is out of range for vectors!", indexJet); } + if (isWithLeadingJet) { + const auto leadingMatch = std::find(mcdJetIds.begin(), mcdJetIds.end(), leadingDetJetId); + if (leadingMatch == mcdJetIds.end()) { + return; // Do not replace an unmatched or rejected leading jet with a subleading jet. + } + indexJet = std::distance(mcdJetIds.begin(), leadingMatch); + } if (useMcC) { - if (randUniform.Uniform(0, 1) < 0.5) + if (useTrainingHist) jetHist.fill(HIST("recmatched/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); else jetHist.fill(HIST("recmatched/mcC/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); } else { jetHist.fill(HIST("recmatched/h2ResponseMatrixLeadingJet"), mcdJetPt.at(indexJet), mcpJetPt.at(indexJet)); } + if (useMcC) { + jetHist.fill(HIST("eff/recmatched/mcClosure/hSelectedRecoEvents"), useTrainingHist ? 0.5 : 1.5); + } if (useLeadingJetDetLevelValue) { leadingJetWithPtEtaPhi[0] = mcdJetPt.at(indexJet); leadingJetWithPtEtaPhi[1] = mcdJetEta.at(indexJet); @@ -2236,6 +2675,48 @@ struct nucleiInJets { leadingJetWithPtEtaPhi[2] = mcpJetPhi.at(indexJet); } + // Fill detector-level DCA templates before the DCAxy selection used by the spectra. + // mcdJetPt is already background-subtracted when usebkgSubractionMC is enabled. + const double templateJetPt = mcdJetPt.at(indexJet); + if (isWithLeadingJet && backgroundRho > 0.f && templateJetPt > cfgjetPtBkgSubMin) { + const double templateJetEta = mcdJetEta.at(indexJet); + const double templateJetPhi = mcdJetPhi.at(indexJet); + const auto perpPhi = getPerpendicuarPhi(templateJetPhi); + for (const auto& track : tracks) { + const auto completeTrack = track.track_as(); + if (completeTrack.sign() >= 0 || completeTrack.pt() > cMaxPt || !isTrackSelectedWithoutDcaxy(completeTrack) || + (useRapidityCutForPID && !isRapiditySelectedForPID(completeTrack.rapidity(o2::constants::physics::MassProton)))) + continue; + if (std::abs(completeTrack.tpcNSigmaPr()) >= cfgnTPCPIDPr) + continue; + int origin = 4; + if (track.has_mcParticle()) { + const auto mcTrack = track.mcParticle_as(); + if (mcTrack.pdgCode() == -2212) + origin = getParticleOriginType(mcTrack); + } + const bool tofPassed = completeTrack.hasTOF() && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const double deltaEta = templateJetEta - completeTrack.eta(); + const double deltaPhi = TVector2::Phi_mpi_pi(templateJetPhi - completeTrack.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhi) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPC/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOF/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (!completeTrack.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/jetCone/TPCTOFVeto/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + } + const double deltaPhiPerp1 = TVector2::Phi_mpi_pi(perpPhi[0] - completeTrack.phi()); + const double deltaPhiPerp2 = TVector2::Phi_mpi_pi(perpPhi[1] - completeTrack.phi()); + if (RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp1) < cfgjetR || RecoDecay::sqrtSumOfSquares(deltaEta, deltaPhiPerp2) < cfgjetR) { + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPC/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOF/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + if (!completeTrack.hasTOF() || tofPassed) + jetHist.fill(HIST("feeddown/antiProton/template/perpCone/TPCTOFVeto/mc"), completeTrack.pt(), completeTrack.dcaXY(), origin); + } + } + } + for (const auto& track : tracks) { auto completeTrack = track.track_as(); if (std::fabs(completeTrack.eta()) > cfgtrkMaxEta) @@ -2245,15 +2726,10 @@ struct nucleiInJets { if (!track.has_mcParticle()) continue; auto mcTrack = track.mcParticle_as(); - if (!mcTrack.isPhysicalPrimary()) - continue; - if (std::fabs(mcTrack.y()) > cfgtrkMaxRap) + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcTrack.y())) continue; bool isTof(completeTrack.hasTOF()); - bool isTOFAndTPCPreSel(completeTrack.hasTOF() && - (std::abs(completeTrack.tpcNSigmaPr()) < cfgnTPCPIDPrTOF || std::abs(completeTrack.tpcNSigmaDe()) < cfgnTPCPIDDeTOF || - std::abs(completeTrack.tpcNSigmaHe()) < cfgnTPCPIDHeTOF || std::abs(completeTrack.tpcNSigmaTr()) < cfgnTPCPIDTrTOF)); bool jetFlag = false; bool jetFlagPerpCone = false; @@ -2283,44 +2759,158 @@ struct nucleiInJets { } } // jet - if (mapPDGToValue(mcTrack.pdgCode()) != 0) { - bool isTpcPassed(true); // why is this always true? - jetHist.fill(HIST("eff/recmatched/pt/PtParticleType"), mcTrack.pt(), jetFlag, mapPDGToValue(mcTrack.pdgCode())); + if (mapPDGToValue(mcTrack.pdgCode()) == -Particle::kProton) { + const int particleOriginType = getParticleOriginType(mcTrack); + const bool isAntiProtonTpcPassed = std::abs(completeTrack.tpcNSigmaPr()) < cfgnTPCPIDPr; + const bool isAntiProtonTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + const bool isAntiProtonTPCTOFPassed = isAntiProtonTpcPassed && isAntiProtonTofPidPassed; + const bool isAntiProtonTPCTOFVetoPassed = isAntiProtonTpcPassed && (!isTof || isAntiProtonTofPidPassed); + + if (jetFlag) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOrigin"), completeTrack.pt(), particleOriginType); + if (isAntiProtonTpcPassed) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTPC"), completeTrack.pt(), particleOriginType); + } + if (isTof) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFPassed) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTPCTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFVetoPassed) { + jetHist.fill(HIST("feeddown/antiProton/jetCone/PtOriginTPCTOFVeto"), completeTrack.pt(), particleOriginType); + } + } + + if (jetFlagPerpCone) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOrigin"), completeTrack.pt(), particleOriginType); + if (isAntiProtonTpcPassed) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTPC"), completeTrack.pt(), particleOriginType); + } + if (isTof) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFPassed) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTPCTOF"), completeTrack.pt(), particleOriginType); + } + if (isAntiProtonTPCTOFVetoPassed) { + jetHist.fill(HIST("feeddown/antiProton/perpCone/PtOriginTPCTOFVeto"), completeTrack.pt(), particleOriginType); + } + } + } + + if (!mcTrack.isPhysicalPrimary()) + continue; + + const auto particleType = mapPDGToValue(mcTrack.pdgCode()); + if (particleType != 0) { + bool isTpcPassed = true; + bool isTofPidPassed = isTof; + switch (std::abs(particleType)) { + case Particle::kProton: + isTpcPassed = std::abs(completeTrack.tpcNSigmaPr()) < cfgnTPCPIDPr; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaPr()) < cfgnTPCPIDPrTOF; + break; + case Particle::kDeuteron: + isTpcPassed = std::abs(completeTrack.tpcNSigmaDe()) < cfgnTPCPIDDe; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaDe()) < cfgnTPCPIDDeTOF; + break; + case Particle::kTriton: + isTpcPassed = std::abs(completeTrack.tpcNSigmaTr()) < cfgnTPCPIDTr; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaTr()) < cfgnTPCPIDTrTOF; + break; + case Particle::kHelium: + isTpcPassed = std::abs(completeTrack.tpcNSigmaHe()) < cfgnTPCPIDHe; + isTofPidPassed = isTof && std::abs(completeTrack.tofNSigmaHe()) < cfgnTPCPIDHeTOF; + break; + default: + break; + } + const bool isTPCTOFPassed = isTpcPassed && isTofPidPassed; + const bool isTPCTOFVetoPassed = isTpcPassed && (!isTof || isTofPidPassed); + jetHist.fill(HIST("eff/recmatched/pt/PtParticleType"), mcTrack.pt(), jetFlag, particleType); if (useMcC) { - if (randUniform.Uniform(0, 1) < 0.5) - jetHist.fill(HIST("eff/recmatched/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); else - jetHist.fill(HIST("eff/recmatched/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); + jetHist.fill(HIST("eff/recmatched/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); } if (isTpcPassed) - jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPC"), mcTrack.pt(), jetFlag, mapPDGToValue(mcTrack.pdgCode())); + jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPC"), mcTrack.pt(), jetFlag, particleType); if (isTof) - jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTOF"), mcTrack.pt(), jetFlag, mapPDGToValue(mcTrack.pdgCode())); - if (isTOFAndTPCPreSel) { - jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), jetFlag, mapPDGToValue(mcTrack.pdgCode())); - jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), jetFlag, mapPDGToValue(mcTrack.pdgCode())); - } else { - jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), jetFlag, mapPDGToValue(mcTrack.pdgCode())); + jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTOF"), mcTrack.pt(), jetFlag, particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), jetFlag, particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), jetFlag, particleType); + + if (jetFlag) { + jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleType"), mcTrack.pt(), mcTrack.eta(), particleType); + if (useMcC) { + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } + } + if (isTpcPassed) + jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPC"), mcTrack.pt(), mcTrack.eta(), particleType); + if (isTof) + jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTOF"), mcTrack.pt(), mcTrack.eta(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPCTOF"), mcTrack.pt(), mcTrack.eta(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/jetCone/ptEta/PtEtaParticleTypeTPCTOFVeto"), mcTrack.pt(), mcTrack.eta(), particleType); } if (jetFlagPerpCone) { - jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleType"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); + jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleType"), mcTrack.pt(), particleType); + jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleType"), mcTrack.pt(), mcTrack.eta(), particleType); if (useMcC) { - if (randUniform.Uniform(0, 1) < 0.5) - jetHist.fill(HIST("eff/recmatched/perpCone/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecAll"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFVetoPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecTPCTOFVeto"), track.pt(), mcTrack.pt(), particleType); + if (isTPCTOFPassed) + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hRecTPCTOF"), track.pt(), mcTrack.pt(), particleType); + } + } + if (useMcC) { + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/perpCone/mcC/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); else - jetHist.fill(HIST("eff/recmatched/perpCone/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); + jetHist.fill(HIST("eff/recmatched/perpCone/mcCSpectra/pt/PtParticleType"), track.pt(), mcTrack.pt(), particleType); } - if (isTpcPassed) - jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPC"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); - if (isTof) - jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); - if (isTOFAndTPCPreSel) { - jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); - jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); + if (isTpcPassed) { + jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPC"), mcTrack.pt(), particleType); + jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPC"), mcTrack.pt(), mcTrack.eta(), particleType); + } + if (isTof) { + jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTOF"), mcTrack.pt(), particleType); + jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTOF"), mcTrack.pt(), mcTrack.eta(), particleType); + } + if (isTPCTOFPassed) { + jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOF"), mcTrack.pt(), particleType); + jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPCTOF"), mcTrack.pt(), mcTrack.eta(), particleType); + } + if (isTPCTOFVetoPassed) { + jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), particleType); + jetHist.fill(HIST("eff/recmatched/perpCone/ptEta/PtEtaParticleTypeTPCTOFVeto"), mcTrack.pt(), mcTrack.eta(), particleType); } - } else { - jetHist.fill(HIST("eff/recmatched/perpCone/pt/PtParticleTypeTPCTOFVeto"), mcTrack.pt(), mapPDGToValue(mcTrack.pdgCode())); } } } // tracks @@ -2331,7 +2921,7 @@ struct nucleiInJets { continue; if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta) continue; - if (std::fabs(mcParticle.y()) > cfgtrkMaxRap) + if (useRapidityCutForPID && !isRapiditySelectedForPID(mcParticle.y())) continue; bool jetFlagMC = false; bool jetFlagPerpConeMC = false; @@ -2361,16 +2951,154 @@ struct nucleiInJets { } } // jet - if (mapPDGToValue(mcParticle.pdgCode()) != 0) { - jetHist.fill(HIST("eff/recmatched/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, mapPDGToValue(mcParticle.pdgCode())); + const auto particleType = mapPDGToValue(mcParticle.pdgCode()); + if (particleType != 0) { + jetHist.fill(HIST("eff/recmatched/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); + if (jetFlagMC) { + jetHist.fill(HIST("eff/recmatched/gen/jetCone/ptEta/PtEtaParticleType"), mcParticle.pt(), mcParticle.eta(), particleType); + if (useMcC) { + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/jetCone/hGen"), mcParticle.pt(), particleType); + else + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/jetCone/hGen"), mcParticle.pt(), particleType); + } + } + if (useMcC) { + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcC/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcCSpectra/gen/pt/PtParticleType"), mcParticle.pt(), jetFlagMC, particleType); + } + } if (jetFlagPerpConeMC) { - jetHist.fill(HIST("eff/recmatched/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), mapPDGToValue(mcParticle.pdgCode())); + jetHist.fill(HIST("eff/recmatched/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + jetHist.fill(HIST("eff/recmatched/gen/perpCone/ptEta/PtEtaParticleType"), mcParticle.pt(), mcParticle.eta(), particleType); + if (useMcC) { + if (useTrainingHist) + jetHist.fill(HIST("eff/recmatched/mcClosure/training/perpCone/hGen"), mcParticle.pt(), particleType); + else + jetHist.fill(HIST("eff/recmatched/mcClosure/dataLike/perpCone/hGen"), mcParticle.pt(), particleType); + } + if (useMcC) { + if (useTrainingHist) { + jetHist.fill(HIST("eff/recmatched/mcC/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } else { + jetHist.fill(HIST("eff/recmatched/mcCSpectra/gen/perpCone/pt/PtParticleType"), mcParticle.pt(), particleType); + } + } } } } // mcParticle } // process int nprocessSimJEEvents = 0; + void processJetSelectionCorrection(aod::JetMcCollision const& collision, + soa::SmallGroups> const& recocolls, + JetMCDetTable const& mcdjets, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) + { + if (std::abs(collision.posZ()) > cfgMaxZVertex) { + return; + } + + bool hasGenLeadingJet = false; + double genLeadingJetPt = -999.; + double genLeadingJetEta = -999.; + double genLeadingJetPhi = -999.; + + for (const auto& mcpjet : mcpjets) { + if (!isConeAxisAccepted(mcpjet.eta())) { + continue; + } + if (mcpjet.pt() > genLeadingJetPt) { + hasGenLeadingJet = true; + genLeadingJetPt = mcpjet.pt(); + genLeadingJetEta = mcpjet.eta(); + genLeadingJetPhi = mcpjet.phi(); + } + } + + if (hasGenLeadingJet && genLeadingJetPt > cfgjetPtBkgSubMinMC) { + jetHist.fill(HIST("jetSelCorr/eventNorm/hEventCounts"), 0.5); + jetHist.fill(HIST("jetSelCorr/genSel/hLeadingJetPt"), genLeadingJetPt); + fillJetSelectionCorrectionParticles(mcParticles, genLeadingJetEta, genLeadingJetPhi); + } + + // Count each reconstructed collision, as in data. The generated selection + // above is independent: neither branch requires the other to pass the cut. + int nSelectedRecoCollisions = 0; + const double jetArea = M_PI * cfgjetR * cfgjetR; + for (const auto& recocoll : recocolls) { + if (!isRecoCollisionSelectedForJetSelectionCorrection(recocoll)) { + continue; + } + if (usebkgSubractionMC && !(recocoll.rho() > 0.)) { + continue; + } + + const auto jetsInCollision = mcdjets.sliceBy(detectorJetsPerCollision, recocoll.globalIndex()); + auto leadingJet = jetsInCollision.begin(); + bool hasLeadingJet = false; + double leadingJetPt = cfgjetPtBkgSubMinMC; + for (auto jet = jetsInCollision.begin(); jet != jetsInCollision.end(); ++jet) { + if (!isConeAxisAccepted(jet.eta())) { + continue; + } + const double correctedPt = usebkgSubractionMC ? jet.pt() - recocoll.rho() * jetArea : jet.pt(); + if (correctedPt > leadingJetPt) { + leadingJet = jet; + leadingJetPt = correctedPt; + hasLeadingJet = true; + } + } + if (!hasLeadingJet) { + continue; + } + + ++nSelectedRecoCollisions; + jetHist.fill(HIST("jetSelCorr/eventNorm/hEventCounts"), 1.5); + jetHist.fill(HIST("jetSelCorr/recoSel/hLeadingJetPtBkgSub"), leadingJetPt); + // All selected detector jets, including unmatched jets, use detector axes + // Never substitute a matched subleading jet or mix truth and detector axes + fillJetSelectionCorrectionParticles(mcParticles, leadingJet.eta(), leadingJet.phi()); + + // Preserve a separate matched-truth-axis sample for efficiency comparisons + // Resolve multiple geometrical matches by the smallest angular distance + bool hasTruthMatch = false; + double closestDistance = 1.e9; + double truthPt = 0.; + double truthEta = 0.; + double truthPhi = 0.; + if (leadingJet.has_matchedJetGeo()) { + for (const auto& truthJet : leadingJet.template matchedJetGeo_as()) { + if (truthJet.mcCollisionId() != collision.globalIndex()) { + continue; + } + const double distance = RecoDecay::sqrtSumOfSquares(leadingJet.eta() - truthJet.eta(), TVector2::Phi_mpi_pi(leadingJet.phi() - truthJet.phi())); + if (distance < closestDistance) { + closestDistance = distance; + hasTruthMatch = true; + truthPt = truthJet.pt(); + truthEta = truthJet.eta(); + truthPhi = truthJet.phi(); + } + } + } + if (!hasTruthMatch) { + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 0.5); + continue; + } + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchedLeadingJetPt"), leadingJetPt, truthPt); + if (!isConeAxisAccepted(truthEta)) { + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 1.5); + continue; + } + jetHist.fill(HIST("jetSelCorr/recoSel/hMatchStatus"), 2.5); + jetHist.fill(HIST("jetSelCorr/matchedTruthAxis/hEventCount"), 0.5); + fillJetSelectionCorrectionParticles(mcParticles, truthEta, truthPhi); + } + jetHist.fill(HIST("jetSelCorr/eventNorm/hSelectedRecoCollisions"), nSelectedRecoCollisions); + } + void processGenMatched(aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, JetMCDetTable const&, JetMCPartTable const& mcpjets, aod::JetParticles const& mcParticles) @@ -2391,6 +3119,8 @@ struct nucleiInJets { return; jetHist.fill(HIST("genmatched/vertexZ"), collision.posZ()); + const bool useTrainingHist = useTrainingHistForMcCollision(collision.globalIndex()); + std::vector mcpJetPt{}; std::vector mcpJetPhi{}; std::vector mcpJetEta{}; @@ -2441,7 +3171,7 @@ struct nucleiInJets { jetHist.fill(HIST("genmatched/hRecMatchedJetEta"), leadingMCPJet.eta(), leadingMCPJet.eta() - mcdjet.eta()); if (useMcC) { - if (randUniform.Uniform(0, 1) < 0.5) { + if (useTrainingHist) { jetHist.fill(HIST("genmatched/hRecMatchedVsGenJetPt"), mcdjet.pt(), leadingMCPJet.pt()); } else { jetHist.fill(HIST("genmatched/mcC/hRecMatchedVsGenJetPt"), mcdjet.pt(), leadingMCPJet.pt()); @@ -2478,7 +3208,6 @@ struct nucleiInJets { void processRecInc(EventTableMC::iterator const& coll, TrackCandidatesIncMC const& tracks, aod::JetParticles const& particleTracks, aod::JMcCollisions const&) { jetHist.fill(HIST("recInc/eventStat"), 0.5); - bool isSel8 = jetderiveddatautilities::selectCollision(coll, jetderiveddatautilities::initialiseEventSelectionBits("sel8")); bool isSelNoSameBunchPileup = jetderiveddatautilities::selectCollision(coll, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup")); bool isSelIsGoodZvtxFT0vsPV = jetderiveddatautilities::selectCollision(coll, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV")); @@ -2566,9 +3295,7 @@ struct nucleiInJets { // auto mass = TDatabasePDG::Instance()->GetParticle(abs(mcTrack.pdgCode()))->Mass(); // auto rapidity = RecoDecay::y(std::array{track.px(), track.py(), track.pz()}, mass); - auto rapidity = mcTrack.y(); - - if (std::abs(rapidity) > cfgtrkMaxRap) + if (!isRapiditySelectedForPID(mcTrack.y())) continue; // Proton if (std::abs(mcTrack.pdgCode()) == 2212) { // Proton @@ -2650,7 +3377,7 @@ struct nucleiInJets { continue; if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta && useEtaSelForEffDen) continue; - if (std::fabs(mcParticle.y()) > cfgtrkMaxRap) + if (!isRapiditySelectedForPID(mcParticle.y())) continue; if (mapPDGToValue(mcParticle.pdgCode()) != 0) { @@ -2660,76 +3387,95 @@ struct nucleiInJets { } // Process function for event and signal loss analysis (inclusive) - void processEventSignalLoss(aod::JetMcCollision const& mcCollision, + void processEventSignalLoss(JetMcCollisionWithCent const& mcCollision, soa::SmallGroups> const& recoColls, aod::JetParticles const& mcParticles, TrackCandidates const&) { - - // Fill generated event statistics jetHist.fill(HIST("eventLoss/hEventStatistics"), 0.5); // All Generated - // Check if we have a reconstructed collision - bool hasRecoColl = false; - bool passSel8 = false; - bool passVz = false; - bool passINELgt0 = false; - bool isSel8 = false; - - float centrality = -999; + auto mcParticles_perColl = mcParticles.sliceBy(perMCCol, mcCollision.globalIndex()); + float centrality = -999.f; switch (centralityType) { - case 0: // FT0M + case 0: centrality = mcCollision.centFT0M(); break; - case 1: // FT0C - centrality = mcCollision.multFT0C(); + case 1: + centrality = mcCollision.centFT0C(); break; - case 2: // V0A - centrality = mcCollision.multFV0A(); + case 2: + centrality = mcCollision.centFV0A(); break; default: - centrality = -999; + centrality = mcCollision.centFT0M(); + break; } + const bool passGenVz = std::abs(mcCollision.posZ()) < cfgMaxZVertex; + const bool mcINELgt0 = o2::pwglf::isINELgt0mc(mcParticles_perColl, pdgDB); - // Check INEL>0 at MC level using PWGLF functionality - bool mcINELgt0 = o2::pwglf::isINELgt0mc(mcParticles, pdgDB); - if (mcCollision.posZ() < 10) { - jetHist.fill(HIST("eventLoss/hEventStatistics"), 1.5); - if (mcINELgt0) { - jetHist.fill(HIST("eventLoss/hEventStatistics"), 2.5); - } + if (!passGenVz) { + return; } + jetHist.fill(HIST("eventLoss/hEventStatistics"), 1.5); // Gen |Vz| cut + if (!mcINELgt0) { + return; + } + jetHist.fill(HIST("eventLoss/hEventStatistics"), 2.5); // Gen True INEL>0 + + int nRecoColls = 0; + int nSelectedRecoColls = 0; + bool hasRecoColl = false; + bool passSel8 = false; + bool passRecoVz = false; + bool passExtraEventSel = false; + bool hasSelectedRecoColl = false; for (const auto& recoColl : recoColls) { + ++nRecoColls; hasRecoColl = true; - if (jetderiveddatautilities::selectCollision(recoColl, jetderiveddatautilities::initialiseEventSelectionBits("sel8"))) - isSel8 = true; - jetHist.fill(HIST("eventLoss/hEventStatistics"), 3.5); // Has Reco Coll + const bool isSel8 = jetderiveddatautilities::selectCollision(recoColl, jetderiveddatautilities::initialiseEventSelectionBits("sel8")); + const bool isRecoVz = std::abs(recoColl.posZ()) < cfgMaxZVertex; + const bool isNoSameBunchPileup = !selNoSameBunchPileup || jetderiveddatautilities::selectCollision(recoColl, jetderiveddatautilities::initialiseEventSelectionBits("NoSameBunchPileup")); + const bool isGoodZvtxFT0vsPV = !selIsGoodZvtxFT0vsPV || jetderiveddatautilities::selectCollision(recoColl, jetderiveddatautilities::initialiseEventSelectionBits("IsGoodZvtxFT0vsPV")); + const bool isOccupancy = !useOccupancy || isOccupancyAccepted(recoColl); + const bool isExtraEventSel = isNoSameBunchPileup && isGoodZvtxFT0vsPV && isOccupancy; if (isSel8) { passSel8 = true; - jetHist.fill(HIST("eventLoss/hEventStatistics"), 4.5); // Pass Sel8 } - - if (std::abs(recoColl.posZ()) < 10.0) { - passVz = true; - jetHist.fill(HIST("eventLoss/hEventStatistics"), 5.5); // Pass |Vz|<10 + if (isRecoVz) { + passRecoVz = true; } - - if (mcINELgt0) { - passINELgt0 = true; - jetHist.fill(HIST("eventLoss/hEventStatistics"), 6.5); // Pass rec INEL>0 + if (isExtraEventSel) { + passExtraEventSel = true; } - break; // Only first reco collision + if (isSel8 && isRecoVz && isExtraEventSel) { + ++nSelectedRecoColls; + hasSelectedRecoColl = true; + } } - // Final selection (all cuts passed) - if (hasRecoColl && passSel8 && passVz && passINELgt0) { - jetHist.fill(HIST("eventLoss/hEventStatistics"), 7.5); // Final Selection + jetHist.fill(HIST("eventLoss/hRecoCollPerMCCollVsCent"), nRecoColls, centrality); + jetHist.fill(HIST("eventLoss/hSelectedRecoCollPerMCCollVsCent"), nSelectedRecoColls, centrality); + + if (hasRecoColl) { + jetHist.fill(HIST("eventLoss/hEventStatistics"), 3.5); // Has Reco Coll + } + if (passSel8) { + jetHist.fill(HIST("eventLoss/hEventStatistics"), 4.5); // Pass Sel8 + } + if (passRecoVz) { + jetHist.fill(HIST("eventLoss/hEventStatistics"), 5.5); // Pass reco |Vz| cut + } + if (passExtraEventSel) { + jetHist.fill(HIST("eventLoss/hEventStatistics"), 6.5); // Pass extra event sel + } + if (hasSelectedRecoColl) { + jetHist.fill(HIST("eventLoss/hEventStatistics"), 7.5); // Reco selected + jetHist.fill(HIST("eventLoss/hEventStatistics"), 8.5); // Reco selected + true INEL>0 } - auto mcParticles_perColl = mcParticles.sliceBy(perMCCol, mcCollision.globalIndex()); for (const auto& mcParticle : mcParticles_perColl) { if (!mcParticle.isPhysicalPrimary()) continue; @@ -2737,19 +3483,17 @@ struct nucleiInJets { // Apply kinematic cuts similar to track selection if (std::fabs(mcParticle.eta()) > cfgtrkMaxEta) continue; + if (!isRapiditySelectedForPID(mcParticle.y())) + continue; int particleType = mapPDGToValue(mcParticle.pdgCode()); if (particleType == 0) continue; // Only interested particles - // Fill INEL>0 specific histograms - if (mcINELgt0) { - jetHist.fill(HIST("eventLoss/signalLoss/h3GenParticlesPtVsEtaVsCent_TrueINELgt0"), mcParticle.pt(), mcParticle.eta(), centrality); - jetHist.fill(HIST("eventLoss/signalLoss/h3GenParticleTypeVsPtVsCent_TrueINELgt0"), mcParticle.pt(), particleType, centrality); - } + jetHist.fill(HIST("eventLoss/signalLoss/h3GenParticlesPtVsEtaVsCent_TrueINELgt0"), mcParticle.pt(), mcParticle.eta(), centrality); + jetHist.fill(HIST("eventLoss/signalLoss/h3GenParticleTypeVsPtVsCent_TrueINELgt0"), mcParticle.pt(), particleType, centrality); - // Fill generated particle histograms (rec events) - if (hasRecoColl && passSel8 && passVz && passINELgt0) { + if (hasSelectedRecoColl) { jetHist.fill(HIST("eventLoss/signalLoss/h3GenParticlesPtVsEtaVsCent_INELgt0"), mcParticle.pt(), mcParticle.eta(), centrality); jetHist.fill(HIST("eventLoss/signalLoss/h3GenParticleTypeVsPtVsCent_INELgt0"), mcParticle.pt(), particleType, centrality); } @@ -2763,6 +3507,7 @@ struct nucleiInJets { PROCESS_SWITCH(nucleiInJets, processMCRec, "nuclei in Jets for detectorlevel Jets", false); PROCESS_SWITCH(nucleiInJets, processMCGen, "nuclei in Jets MC particlelevel Jets", false); PROCESS_SWITCH(nucleiInJets, processRecMatched, "nuclei in Jets rec matched", false); + PROCESS_SWITCH(nucleiInJets, processJetSelectionCorrection, "nuclei in Jets jet-selection migration correction", false); PROCESS_SWITCH(nucleiInJets, processGenMatched, "nuclei in Jets gen matched", false); PROCESS_SWITCH(nucleiInJets, processEventSignalLoss, "Event and signal loss analysis (inclusive)", false); }; diff --git a/PWGJE/Tasks/phiInJets.cxx b/PWGJE/Tasks/phiInJets.cxx index 085da029904..0da2e153923 100644 --- a/PWGJE/Tasks/phiInJets.cxx +++ b/PWGJE/Tasks/phiInJets.cxx @@ -294,7 +294,7 @@ struct phiInJets { ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// template - bool trackSelection(const TrackType track) + bool trackSelection(const TrackType& track) { // basic track cuts if (track.pt() < cfgtrkMinPt) @@ -396,7 +396,7 @@ struct phiInJets { ///////////////////////////////////////////////////////////////////////////// template - double DistinguishJets(const JetType& jets, const TLorentzVector lResonance) + double DistinguishJets(const JetType& jets, const TLorentzVector& lResonance) { if (cDebugLevel > 0) std::cout << "oof, multiple jets fit to the same phi. Time to find the best phi-jet link" << std::endl; @@ -419,7 +419,7 @@ struct phiInJets { } template - double DistinguishJetsMC(const Jet_pt& jet_pt, const Jet_phi& jet_phi, const Jet_eta& jet_eta, const TLorentzVector lResonance) + double DistinguishJetsMC(const Jet_pt& jet_pt, const Jet_phi& jet_phi, const Jet_eta& jet_eta, const TLorentzVector& lResonance) { if (cDebugLevel > 0) std::cout << "oof, multiple jets fit to the same phi. Time to find the best phi-jet link" << std::endl; @@ -593,7 +593,7 @@ struct phiInJets { JEhistos.fill(HIST("hNResoPerEventInJet"), nResoInTrig); int nJets = 0; - for (auto chargedjet : chargedjets) { + for (const auto& chargedjet : chargedjets) { JEhistos.fill(HIST("FJetaHistogram"), chargedjet.eta()); JEhistos.fill(HIST("FJphiHistogram"), chargedjet.phi()); JEhistos.fill(HIST("FJptHistogram"), chargedjet.pt()); diff --git a/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx b/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx index 81cd439194e..943bc742517 100644 --- a/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx +++ b/PWGJE/Tasks/photonChargedTriggerCorrelation.cxx @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -50,21 +51,23 @@ #include #include +#include #include #include +#include #include #include #include #include #include +#include #include +#include #include +#include +#include #include -const double absEtaMaxDefault = 0.8; -#define DPHI_SCALE constants::math::TwoPI - constants::math::PIHalf -#define DETA_SCALE 4 * absEtaMaxDefault - 2 * absEtaMaxDefault - using namespace o2; using namespace o2::framework; @@ -77,107 +80,126 @@ using CorrMcCollision = CorrMcCollisions::iterator; using BinningZPvMult = ColumnBinningPolicy; -// correlation analysis ======================================================================================================================================================================= +// correlation analysis /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// =/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/ + +const double absEtaMaxDefault = 0.8; struct PhotonChargedTriggerCorrelation { // configurables // general (kenobi) - Configurable pathCcdbEff{"pathCcdbEff", "Users/j/jkinner/efficiency/set_in_config", "base path to the ccdb efficiencies"}; + Configurable pathCcdbCorrection{"pathCcdbCorrection", "Users/j/jkinner/correction/set_in_config", "base path to the ccdb corrections"}; Configurable urlCcdb{"urlCcdb", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable noLaterThanCcdbConfig{"noLaterThanCcdbConfig", -1, "latest acceptable timestamp of creation for the object (-1 for task start time)"}; + Configurable splitMcEvN{"splitMcEvN", 1, "number of equal event fractions for selection in MC (max 10) | (select 1 for data)"}; + Configurable splitMcEvSelect{"splitMcEvSelect", 1, "selected event fraction number in MC (starts at 1) | (select 1 for data)"}; // analysis - Configurable doEffCorrectionTrigger{"doEffCorrectionTrigger", false, "whether to do on-the-fly mixing correction for triggers"}; - Configurable doEffCorrectionHadron{"doEffCorrectionHadron", false, "whether to do on-the-fly mixing correction for hadrons"}; - Configurable doEffCorrectionPipm{"doEffCorrectionPipm", false, "whether to do on-the-fly mixing correction for pipm"}; - Configurable doEffCorrectionPhotonPCM{"doEffCorrectionPhotonPCM", false, "whether to do on-the-fly mixing correction for photonPCM"}; - + Configurable applyCTotTrigger{"applyCTotTrigger", false, "whether to apply on-the-fly total correction factor for triggers"}; Configurable doTrigEvMixing{"doTrigEvMixing", false, "whether to use trigger events for trigger mixing"}; - Configurable nTriggerSavedForMixing{"nTriggerSavedForMixing", 2048, "number of triggers that are saved for mixing with other events"}; - Configurable nTriggerMixingMcTrue{"nTriggerMixingMcTrue", 8, "number of triggers that are used for mc true mixing"}; - Configurable nTriggerMixingHadron{"nTriggerMixingHadron", 64, "number of triggers that are used for hadron mixing"}; - Configurable nTriggerMixingPipm{"nTriggerMixingPipm", 64, "number of triggers that are used for pipm mixing"}; - Configurable nTriggerMixingPhotonPCM{"nTriggerMixingPhotonPCM", 256, "number of triggers that are saved for photonPCM mixing"}; - Configurable nTriggerMixingH0PCM{"nTriggerMixingH0PCM", 256, "number of triggers that are saved for h0PCM (pi0, eta) mixing"}; + + Configurable nTriggerSavedForMixing{"nTriggerSavedForMixing", 8192, "number of triggers that are saved for mixing with other events"}; + Configurable nTriggerBinMinThreshold{"nTriggerBinMinThreshold", 128, "threshold minimum number of triggers in mixing bin required"}; + Configurable iTriggerStartFraction{"iTriggerStartFraction", 0.5, "ratio of bin size for randomised iTrigger start"}; + Configurable nTriggerScaleCoefficient{"nTriggerScaleCoefficient", 2, "coeffcient a in mixing-number pt power scaling"}; + Configurable nTriggerScaleExponent{"nTriggerScaleExponent", 2, "exponent a in mixing-number pt power scaling"}; + Configurable nMixingAt0McTrue{"nMixingAt0McTrue", 4, "number of triggers that are used for mc true mixing"}; + Configurable nMixingAt0Hadron{"nMixingAt0Hadron", 16, "number of triggers that are used for hadron mixing"}; + Configurable nMixingAt0Pipm{"nMixingAt0Pipm", 16, "number of triggers that are used for pipm mixing"}; + Configurable nMixingAt0PhotonPCM{"nMixingAt0PhotonPCM", 256, "number of triggers that are saved for photonPCM mixing"}; + Configurable nMixingAt0H0PCM{"nMixingAt0H0PCM", 1024, "number of triggers that are saved for h0PCM (pi0, eta) mixing"}; + Configurable nNeighboursMixingPhotonPCMPair{"nNeighboursMixingPhotonPCMPair", 32, "number neighbours used for for photonPCM pair mixing"}; Configurable> pi0PCMPeakMassRange{"pi0PCMPeakMassRange", {0.10, 0.15}, "photon-pair mass integration range for pi0PCM"}; - Configurable> pi0PCMSideMassRange{"pi0PCMSideMassRange", {0.16, 0.24}, "photon-pair mass integration range outside pi0PCM region"}; + Configurable> pi0PCMSideMassRange{"pi0PCMSideMassRange", {0.16, 0.22}, "photon-pair mass integration range outside pi0PCM region"}; Configurable> etaPCMPeakMassRange{"etaPCMPeakMassRange", {0.51, 0.56}, "photon-pair mass integration range for etaPCM"}; - Configurable> etaPCMLowSideMassRange{"etaPCMLowSideMassRange", {0.45, 0.50}, "photon-pair mass integration range below etaPCM region"}; - Configurable> etaPCMHighSideMassRange{"etaPCMHighSideMassRange", {0.56, 0.65}, "photon-pair mass integration range above etaPCM region"}; - - Configurable doTrigEvEff{"doTrigEvEff", false, "whether to use trigger events for efficiency histograms"}; - Configurable ptCutTrigEvEff{"ptCutTrigEvEff", 4, "pT cut for efficieny calculation in trigger events (to avoid trigger bias)"}; - Configurable requireSingleCollisionPurity{"requireSingleCollisionPurity", true, "whether particle from single chosen MC-col associated to reco-col (else just type/kin match)"}; + Configurable> etaPCMLowSideMassRange{"etaPCMLowSideMassRange", {0.44, 0.48}, "photon-pair mass integration range below etaPCM region"}; + Configurable> etaPCMHighSideMassRange{"etaPCMHighSideMassRange", {0.58, 0.62}, "photon-pair mass integration range above etaPCM region"}; // for histograms - Configurable nBinsZPv{"nBinsZPv", 100, "number zPv bins in histos for QA"}; - Configurable nBinsZPvSmol{"nBinsZPvSmol", 28, "number zPv bins but smaller"}; - Configurable nBinsMult{"nBinsMult", 200, "number multiplicity bins in histos for QA"}; - Configurable nBinsMultSmol{"nBinsMultSmol", 20, "number multiplicity bins but smaller"}; + Configurable nBinsZPv{"nBinsZPv", 28, "number zPv bins in histos"}; + Configurable nBinsMult{"nBinsMult", 64, "number multiplicity bins in histos"}; Configurable nBinsOccupancy{"nBinsOccupancy", 2000, "number occupancy bins in histos for QA"}; Configurable nBinsPhi{"nBinsPhi", 72, "number phi bins"}; - Configurable nBinsEta{"nBinsEta", 40, "number eta bins"}; - Configurable nBinsMgg{"nBinsMgg", 160, "number mass-photon-pair bins"}; + Configurable nBinsEta{"nBinsEta", 32, "number eta bins"}; + Configurable nBinsDCAz{"nBinsDCAz", 100, "number DCAz bins"}; + Configurable nBinsMgg{"nBinsMgg", 240, "number mass-photon-pair bins"}; - Configurable> binsPtTrig{"binsPtTrig", {5, 10, 25, 50}, "correlation ptTrig bins"}; + Configurable> binsPtTrig{"binsPtTrig", {0, 5, 10, 25, 50, 100}, "correlation ptTrig bins"}; Configurable> binsPtAssoc{"binsPtAssoc", - {0.2, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 7.0, 8.0, 9.0, 10, 12.5, 15, 17.5, 20, 30, 40}, + {0, 0.2, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 7.0, 8.0, 9.0, 10, + 12.5, 15, 17.5, 20, 25, 35, 50, 100}, "correlation ptAssoc bins"}; Configurable> binsDPhi{"binsDPhi", - {0.00 * DPHI_SCALE, - 0.04 * DPHI_SCALE, 0.08 * DPHI_SCALE, 0.11 * DPHI_SCALE, 0.14 * DPHI_SCALE, - 0.16 * DPHI_SCALE, 0.18 * DPHI_SCALE, 0.20 * DPHI_SCALE, 0.22 * DPHI_SCALE, - 0.23 * DPHI_SCALE, 0.24 * DPHI_SCALE, 0.25 * DPHI_SCALE, 0.26 * DPHI_SCALE, 0.27 * DPHI_SCALE, 0.28 * DPHI_SCALE, - 0.30 * DPHI_SCALE, 0.32 * DPHI_SCALE, 0.34 * DPHI_SCALE, 0.36 * DPHI_SCALE, - 0.39 * DPHI_SCALE, 0.42 * DPHI_SCALE, 0.46 * DPHI_SCALE, 0.50 * DPHI_SCALE, - 0.54 * DPHI_SCALE, 0.58 * DPHI_SCALE, 0.61 * DPHI_SCALE, 0.64 * DPHI_SCALE, - 0.66 * DPHI_SCALE, 0.68 * DPHI_SCALE, 0.70 * DPHI_SCALE, 0.72 * DPHI_SCALE, - 0.74 * DPHI_SCALE, 0.76 * DPHI_SCALE, 0.78 * DPHI_SCALE, - 0.80 * DPHI_SCALE, 0.82 * DPHI_SCALE, 0.84 * DPHI_SCALE, 0.86 * DPHI_SCALE, - 0.89 * DPHI_SCALE, 0.92 * DPHI_SCALE, 0.96 * DPHI_SCALE, 1.00 * DPHI_SCALE}, + {0.00 * constants::math::TwoPI - constants::math::PIHalf, + 0.05 * constants::math::TwoPI - constants::math::PIHalf, 0.10 * constants::math::TwoPI - constants::math::PIHalf, + 0.14 * constants::math::TwoPI - constants::math::PIHalf, 0.17 * constants::math::TwoPI - constants::math::PIHalf, + 0.20 * constants::math::TwoPI - constants::math::PIHalf, + 0.22 * constants::math::TwoPI - constants::math::PIHalf, 0.24 * constants::math::TwoPI - constants::math::PIHalf, + 0.26 * constants::math::TwoPI - constants::math::PIHalf, 0.28 * constants::math::TwoPI - constants::math::PIHalf, + 0.30 * constants::math::TwoPI - constants::math::PIHalf, + 0.33 * constants::math::TwoPI - constants::math::PIHalf, 0.36 * constants::math::TwoPI - constants::math::PIHalf, + 0.40 * constants::math::TwoPI - constants::math::PIHalf, 0.45 * constants::math::TwoPI - constants::math::PIHalf, + 0.50 * constants::math::TwoPI - constants::math::PIHalf, + 0.55 * constants::math::TwoPI - constants::math::PIHalf, 0.60 * constants::math::TwoPI - constants::math::PIHalf, + 0.64 * constants::math::TwoPI - constants::math::PIHalf, 0.68 * constants::math::TwoPI - constants::math::PIHalf, + 0.71 * constants::math::TwoPI - constants::math::PIHalf, + 0.74 * constants::math::TwoPI - constants::math::PIHalf, 0.76 * constants::math::TwoPI - constants::math::PIHalf, + 0.79 * constants::math::TwoPI - constants::math::PIHalf, + 0.82 * constants::math::TwoPI - constants::math::PIHalf, 0.86 * constants::math::TwoPI - constants::math::PIHalf, + 0.90 * constants::math::TwoPI - constants::math::PIHalf, 0.95 * constants::math::TwoPI - constants::math::PIHalf, + 1.00 * constants::math::TwoPI - constants::math::PIHalf}, "correlation bins DeltaPhi"}; Configurable> binsDEta{"binsDEta", - {0 / 32. * DETA_SCALE, - 1 / 32. * DETA_SCALE, 2 / 32. * DETA_SCALE, 3 / 32. * DETA_SCALE, 4 / 32. * DETA_SCALE, - 5 / 32. * DETA_SCALE, 6 / 32. * DETA_SCALE, 7 / 32. * DETA_SCALE, 8 / 32. * DETA_SCALE, - 9 / 32. * DETA_SCALE, 10 / 32. * DETA_SCALE, 11 / 32. * DETA_SCALE, 12 / 32. * DETA_SCALE, 13 / 32. * DETA_SCALE, 14 / 32. * DETA_SCALE, - 59 / 128. * DETA_SCALE, 62 / 128. * DETA_SCALE, 64 / 128. * DETA_SCALE, 66 / 128. * DETA_SCALE, 69 / 128. * DETA_SCALE, 18 / 32. * DETA_SCALE, - 19 / 32. * DETA_SCALE, 20 / 32. * DETA_SCALE, 21 / 32. * DETA_SCALE, 22 / 32. * DETA_SCALE, 23 / 32. * DETA_SCALE, 24 / 32. * DETA_SCALE, - 25 / 32. * DETA_SCALE, 26 / 32. * DETA_SCALE, 27 / 32. * DETA_SCALE, 28 / 32. * DETA_SCALE, - 29 / 32. * DETA_SCALE, 30 / 32. * DETA_SCALE, 31 / 32. * DETA_SCALE, 32 / 32. * DETA_SCALE}, + {0 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 2 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 4 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 6 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 8 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 9.5 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 11 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 12.5 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 14 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 15.5 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 16.5 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 18 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 19.5 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 21 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 22.5 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 24 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 26 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 28 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, + 30 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault, 32 / 32. * 4 * absEtaMaxDefault - 2 * absEtaMaxDefault}, "correlation bins DeltaEta"}; - Configurable> binsZPv{"binsZPv", - {-7, -5, -3, -1, 1, 3, 5, 7}, - "zPv mixing bins"}; - Configurable> binsMult{"binsMult", - {-0.5, 9.5, 14.5, 19.5, 25.5, 32}, - "multiplicity mixing bins for mc true"}; - Configurable> binsZPvMcTrue{"binsZPvMcTrue", - {-10000, 10000}, - "zPv mixing bins"}; - Configurable> binsMultMcTrue{"binsMultMcTrue", - {-0.5, 10.5, 15.5, 20.5, 25.5, 30.5, 35.5, 40.5, 50.5, 64}, - "multiplicity mixing bins for mc true"}; + Configurable> binsZPvBinning{"binsZPvBinning", + {-7, -5, -3, -1, 1, 3, 5, 7}, + "zPv mixing bins"}; + Configurable> binsMultBinning{"binsMultBinning", + {-0.5, 10.5, 15.5, 20.5, 27.5, 42.5}, + "multiplicity mixing bins"}; + Configurable> binsZPvBinningMcTrue{"binsZPvBinningMcTrue", + {-10000, 10000}, + "zPv mixing bins for mc true"}; + Configurable> binsMultBinningMcTrue{"binsMultBinningMcTrue", + {-0.5, 16.5, 24.5, 31.5, 39.5, 64.5}, + "multiplicity mixing bins for mc true"}; // configurables from other tasks - double etaMax; + double absEtaMax = -1; + + // further variables + + std::mt19937 randomMt{3141u}; + int randomIntInInterval(int const lowEdgeInclusive, int const upEdgeInclusive) + { + return lowEdgeInclusive + (randomMt() % (upEdgeInclusive - lowEdgeInclusive + 1)); // speeds up task by factor > 2; modulo bias should not be relevant here + // return std::uniform_int_distribution(lowEdgeInclusive, upEdgeInclusive)(randomMt); + } // objects to hold histograms HistogramRegistry histos{"histogramRegistry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; // ccdb calls - const int64_t noLaterThanCcdb = noLaterThanCcdbConfig == -1 ? std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() : noLaterThanCcdbConfig; - Service ccdb; + int64_t noLaterThanCcdb = + noLaterThanCcdbConfig == -1 ? std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() : noLaterThanCcdbConfig; + Service ccdb{}; // for mc - Service pdg; - - // random number generation - static constexpr unsigned int SeedRandomEngine = 12345; - std::mt19937 randomEngine{SeedRandomEngine}; + Service pdg{}; // partitions SliceCache cache; @@ -194,156 +216,176 @@ struct PhotonChargedTriggerCorrelation { // combinations binning // cumbersome, but still better than having extra configurable or figuring out how to init binningZPvMult later while declaring it here - std::function(std::vector const&, double)> prependValueToVector = + std::function(std::vector const&, double const)> prependValueToVector = [](std::vector const& vec, double const value) { std::vector resultVec = {value}; resultVec.insert(resultVec.end(), vec.begin(), vec.end()); return resultVec; }; - BinningZPvMult binningZPvMult{{prependValueToVector(binsZPv.value, VARIABLE_WIDTH), prependValueToVector(binsMult.value, VARIABLE_WIDTH)}, true}; + BinningZPvMult binningZPvMult{{prependValueToVector(binsZPvBinning.value, VARIABLE_WIDTH), prependValueToVector(binsMultBinning.value, VARIABLE_WIDTH)}}; // declare analysis variables - // efficiency histograms - TH1D* h1PtInvEffTrigger; - TH1D* h1PtInvEffHadron; - TH1D* h1PtInvEffPipm; - TH1D* h1PtInvEffPhotonPCM; + // correction histograms + TH1D* h1PtCorrectionTrigger = nullptr; // mixing trigger memory struct MixingTrigger { + MixingTrigger(float pt, float phi, float eta) + : fPt(pt), fPhi(phi), fEta(eta) {} float fPt, fPhi, fEta; - float pt() const { return fPt; } - float phi() const { return fPhi; } - float eta() const { return fEta; } + [[nodiscard]] float pt() const { return fPt; } + [[nodiscard]] float phi() const { return fPhi; } + [[nodiscard]] float eta() const { return fEta; } }; // class to handle trigger info from previous collisions (beyond single dataframe) // organised as zPv- and mult-bin matrix of deque (pt, phi, eta) to save trigger info beyond single dataframe - // extra bin for mult overflow - // with adjusted zVtx (see triggerBinValuesZPv in init) and mult overflow -> all events accounted for + // last mult bin open to account for overflow and adjusted zVtx limits to account for rounding errors -> all events accounted for // (possibly replace by some advanced derived data method and O2 event mixing in future?) class MixingTriggerMemory { public: - // finds bin that value belongs to (assumes ordered bins) (starts at 0; includes underflow (return -1) and overlflow (return bins.size() - 1)) + // finds bin that value belongs to (assumes ordered bin edges) (starts at 0; includes underflow (return -1) and overflow (return binEdges.size() - 1)) // should be faster than some std binary search due to small number of bins (zPv, mult) - static int findIntervalBin(double value, const std::vector& bins) + static int findIntervalBin(double value, const std::vector& binEdges) { - const int n = bins.size() - 1; - if (value < bins[0]) + if (binEdges.empty()) { + throw std::invalid_argument("binEdges in findIntervalBin"); + } + const int n = binEdges.size() - 1; + if (value < binEdges[0]) { return -1; // underflow - for (int i_bin = 0; i_bin < n; i_bin++) - if (value < bins[i_bin + 1]) + } + for (int i_bin = 0; i_bin < n; i_bin++) { + if (value < binEdges[i_bin + 1]) { return i_bin; + } + } return n; // overflow } - MixingTriggerMemory(int const nTriggerSavedForMixingIn, std::vector binsZPv, std::vector binsMult) + MixingTriggerMemory(std::vector const& binEdgesZPvIn, std::vector const& binEdgesMultIn, size_t const nTriggerPerBinLimitIn) + : binEdgesZPv(binEdgesZPvIn), + nBinsZPv(binEdgesZPvIn.size() - 1), + binEdgesMult(binEdgesMultIn), + nBinsMult(binEdgesMultIn.size() - 1), + nTriggerPerBinLimit(nTriggerPerBinLimitIn) { - nTriggerSavedForMixing = nTriggerSavedForMixingIn; - triggerBinValuesZPv = binsZPv; - triggerBinValuesMult = binsMult; - // prevent rounding errors in bin finding (multiplicity accounted for by it going to 0 and already considering overflow separately) - triggerBinValuesZPv.front() *= zPvRoundingErrorAdjust; - triggerBinValuesZPv.back() *= zPvRoundingErrorAdjust; + int const nEdgesMin = 2; + if (binEdgesZPv.size() < nEdgesMin || binEdgesMult.size() < nEdgesMin) { + throw std::invalid_argument("too few bin edges to define zPv or mult bins"); + } + if (!std::is_sorted(binEdgesZPv.begin(), binEdgesZPv.end()) || !std::is_sorted(binEdgesMult.begin(), binEdgesMult.end())) { + throw std::invalid_argument("bin edges must be sorted"); + } + // prevent zPv over/underflow due to rounding errors + binEdgesZPv.front() = binEdgesZPv.front() < 0 ? binEdgesZPv.front() * zPvRoundingErrorAdjust : binEdgesZPv.front() / zPvRoundingErrorAdjust; + binEdgesZPv.back() = binEdgesZPv.back() > 0 ? binEdgesZPv.back() * zPvRoundingErrorAdjust : binEdgesZPv.back() / zPvRoundingErrorAdjust; // init correct size of zPv-mult matrix - savedTriggersZPvMult.resize(binsZPv.size() - 1); - for (size_t i_zPv = 0; i_zPv < binsZPv.size() - 1; i_zPv++) { - savedTriggersZPvMult[i_zPv].resize(binsMult.size()); + savedTriggersZPvMult.resize(nBinsZPv); + for (size_t i_zPv = 0; i_zPv < nBinsZPv; i_zPv++) { + savedTriggersZPvMult[i_zPv].resize(nBinsMult); } } // save trigger for mixing - // up to nTriggerSavedForMixing stored (LIFO) + // up to nTriggerPerBinLimit stored in rolling buffer for each zPv-mult bin void saveTrigger(float const pt, float const phi, float const eta, double const zPv, double const mult) { - int const iBinCorrZPv = findIntervalBin(zPv, triggerBinValuesZPv); - int const iBinCorrMult = findIntervalBin(mult, triggerBinValuesMult); - // special cases (floating point precision errors, mult overflow) should be taken care of by triggerBinValuesZPv and triggerBinValuesMult - savedTriggersZPvMult[iBinCorrZPv][iBinCorrMult].push_front(MixingTrigger{pt, phi, eta}); - if (static_cast(savedTriggersZPvMult[iBinCorrZPv][iBinCorrMult].size()) > nTriggerSavedForMixing) { - savedTriggersZPvMult[iBinCorrZPv][iBinCorrMult].pop_back(); + int const iBinCorrZPv = getZPvBin(zPv); + int const iBinCorrMult = getMultBin(mult); + // fill + std::deque& triggerBin = savedTriggersZPvMult[iBinCorrZPv][iBinCorrMult]; + triggerBin.push_front(MixingTrigger{pt, phi, eta}); + if (triggerBin.size() > nTriggerPerBinLimit) { + triggerBin.pop_back(); + return; // skip nTriggerBinMin update when unnecessary + } + // update nTriggerBinMin + nTriggerBinMin = nTriggerPerBinLimit; + for (size_t i_zPv = 0; i_zPv < nBinsZPv; i_zPv++) { + for (size_t i_mult = 0; i_mult < nBinsMult; i_mult++) { + nTriggerBinMin = std::min(nTriggerBinMin, savedTriggersZPvMult[i_zPv][i_mult].size()); + } } } // return deques of trigger pt, phi, eta in the given zPv/mult bin - std::deque const& getTriggers(double const zPv, double const mult) const + [[nodiscard]] std::deque const& getTriggers(double const zPv, double const mult) const { - int const iBinCorrZPv = findIntervalBin(zPv, triggerBinValuesZPv); - int const iBinCorrMult = findIntervalBin(mult, triggerBinValuesMult); + int const iBinCorrZPv = getZPvBin(zPv); + int const iBinCorrMult = getMultBin(mult); return savedTriggersZPvMult[iBinCorrZPv][iBinCorrMult]; } + [[nodiscard]] size_t getBinSizeMin() const + { + return nTriggerBinMin; + } + private: - double const zPvRoundingErrorAdjust = 1.0001; - int nTriggerSavedForMixing; - std::vector triggerBinValuesZPv; - std::vector triggerBinValuesMult; + double zPvRoundingErrorAdjust = 1.0001; + std::vector binEdgesZPv; + size_t nBinsZPv; + std::vector binEdgesMult; + size_t nBinsMult; std::vector>> savedTriggersZPvMult; + size_t nTriggerPerBinLimit; + size_t nTriggerBinMin = 0; + + [[nodiscard]] size_t getZPvBin(double const zPv) const + { + int const iBinInit = findIntervalBin(zPv, binEdgesZPv); + if (iBinInit == -1 || iBinInit == static_cast(nBinsZPv)) { + throw std::runtime_error("zPv underflow or overflow in MixingTriggerMemory"); + } + return iBinInit; + } + [[nodiscard]] size_t getMultBin(double const mult) const + { + int const iBinInit = findIntervalBin(mult, binEdgesMult); + if (iBinInit == static_cast(nBinsMult)) { + return iBinInit - 1; + } + if (iBinInit == -1) { + throw std::runtime_error("mult underflow in MixingTriggerMemory"); + } + return iBinInit; + } }; - MixingTriggerMemory mixingTriggerMemoryReco{nTriggerSavedForMixing.value, binsZPv.value, binsMult.value}; - MixingTriggerMemory mixingTriggerMemoryTrue{nTriggerSavedForMixing.value, binsZPvMcTrue.value, binsMult.value}; - MixingTriggerMemory mixingTriggerMemoryRecoColTrue{nTriggerSavedForMixing.value, binsZPvMcTrue.value, binsMult.value}; + // different buffers for mixing modes + enum class TriggerMemoryMode { Reco, + True, + TrueAssocEv, + TrueAssocEvRecoPtTrig }; - // functions ================================================================================================================================================================================ + std::unique_ptr triggerMemoryReco; + std::unique_ptr triggerMemoryTrue; + std::unique_ptr triggerMemoryTrueAssocEv; + std::unique_ptr triggerMemoryTrueAssocEvRecoPtTrig; + + // functions //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ========================================================================================================================================================================================== // general (kenobi) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ // get histograms from ccdb - // save efficiencies from ccdb in histogram registry + // save corrections from ccdb in histogram registry void initCcdbHistograms() { // trigger - h1PtInvEffTrigger = nullptr; - if (doEffCorrectionTrigger) { - h1PtInvEffTrigger = ccdb->getForTimeStamp(pathCcdbEff.value + "/trigger", noLaterThanCcdb); - - const double* effBinsTrigger = h1PtInvEffTrigger->GetXaxis()->GetXbins()->GetArray(); - const AxisSpec axisPtEffTrigger{std::vector(effBinsTrigger, effBinsTrigger + h1PtInvEffTrigger->GetNbinsX() + 1), "#it{p}_{T}"}; - histos.add("usedEff/h1_pt_invEff_trigger_ccdb", "h1_pt_invEff_trigger_ccdb", kTH1D, {axisPtEffTrigger}, true); - for (int iBin = 1; iBin <= h1PtInvEffTrigger->GetNbinsX(); iBin++) { - histos.get(HIST("usedEff/h1_pt_invEff_trigger_ccdb"))->SetBinContent(iBin, h1PtInvEffTrigger->GetBinContent(iBin)); - histos.get(HIST("usedEff/h1_pt_invEff_trigger_ccdb"))->SetBinError(iBin, h1PtInvEffTrigger->GetBinError(iBin)); - } - } - // hadron - h1PtInvEffHadron = nullptr; - if (doEffCorrectionHadron) { - h1PtInvEffHadron = ccdb->getForTimeStamp(pathCcdbEff.value + "/hadron", noLaterThanCcdb); - - const double* effBinsHadron = h1PtInvEffHadron->GetXaxis()->GetXbins()->GetArray(); - const AxisSpec axisPtEffHadron{std::vector(effBinsHadron, effBinsHadron + h1PtInvEffHadron->GetNbinsX() + 1), "#it{p}_{T}"}; - histos.add("usedEff/h1_pt_invEff_hadron_ccdb", "h1_pt_invEff_hadron_ccdb", kTH1D, {axisPtEffHadron}, true); - for (int iBin = 1; iBin <= h1PtInvEffHadron->GetNbinsX(); iBin++) { - histos.get(HIST("usedEff/h1_pt_invEff_hadron_ccdb"))->SetBinContent(iBin, h1PtInvEffHadron->GetBinContent(iBin)); - histos.get(HIST("usedEff/h1_pt_invEff_hadron_ccdb"))->SetBinError(iBin, h1PtInvEffHadron->GetBinError(iBin)); - } - } - // pipm - h1PtInvEffPipm = nullptr; - if (doEffCorrectionPipm) { - h1PtInvEffPipm = ccdb->getForTimeStamp(pathCcdbEff.value + "/pipm", noLaterThanCcdb); - - const double* effBinsPipm = h1PtInvEffPipm->GetXaxis()->GetXbins()->GetArray(); - const AxisSpec axisPtEffPipm{std::vector(effBinsPipm, effBinsPipm + h1PtInvEffPipm->GetNbinsX() + 1), "#it{p}_{T}"}; - histos.add("usedEff/h1_pt_invEff_pipm_ccdb", "h1_pt_invEff_pipm_ccdb", kTH1D, {axisPtEffPipm}, true); - for (int iBin = 1; iBin <= h1PtInvEffPipm->GetNbinsX(); iBin++) { - histos.get(HIST("usedEff/h1_pt_invEff_pipm_ccdb"))->SetBinContent(iBin, h1PtInvEffPipm->GetBinContent(iBin)); - histos.get(HIST("usedEff/h1_pt_invEff_pipm_ccdb"))->SetBinError(iBin, h1PtInvEffPipm->GetBinError(iBin)); - } - } - // photonPCM - h1PtInvEffPhotonPCM = nullptr; - if (doEffCorrectionPhotonPCM) { - h1PtInvEffPhotonPCM = ccdb->getForTimeStamp(pathCcdbEff.value + "/photonPCM", noLaterThanCcdb); - - const double* effBinsPhotonPCM = h1PtInvEffPhotonPCM->GetXaxis()->GetXbins()->GetArray(); - const AxisSpec axisPtEffPhotonPCM{std::vector(effBinsPhotonPCM, effBinsPhotonPCM + h1PtInvEffPhotonPCM->GetNbinsX() + 1), "#it{p}_{T}"}; - histos.add("usedEff/h1_pt_invEff_photonPCM_ccdb", "h1_pt_invEff_photonPCM_ccdb", kTH1D, {axisPtEffPhotonPCM}, true); - for (int iBin = 1; iBin <= h1PtInvEffPhotonPCM->GetNbinsX(); iBin++) { - histos.get(HIST("usedEff/h1_pt_invEff_photonPCM_ccdb"))->SetBinContent(iBin, h1PtInvEffPhotonPCM->GetBinContent(iBin)); - histos.get(HIST("usedEff/h1_pt_invEff_photonPCM_ccdb"))->SetBinError(iBin, h1PtInvEffPhotonPCM->GetBinError(iBin)); + h1PtCorrectionTrigger = nullptr; + if (applyCTotTrigger) { + h1PtCorrectionTrigger = ccdb->getForTimeStamp(pathCcdbCorrection.value + "/trigger", noLaterThanCcdb); + + const double* correctionBinsTrigger = h1PtCorrectionTrigger->GetXaxis()->GetXbins()->GetArray(); + const AxisSpec axisPtCorrectionTrigger{std::vector(correctionBinsTrigger, correctionBinsTrigger + h1PtCorrectionTrigger->GetNbinsX() + 1), "#it{p}_{T}"}; + histos.add("usedCorrection/h1_pt_correction_trigger_ccdb", "h1_pt_correction_trigger_ccdb", kTH1D, {axisPtCorrectionTrigger}, true); + for (int iBin = 1; iBin <= h1PtCorrectionTrigger->GetNbinsX(); iBin++) { + histos.get(HIST("usedCorrection/h1_pt_correction_trigger_ccdb"))->SetBinContent(iBin, h1PtCorrectionTrigger->GetBinContent(iBin)); + histos.get(HIST("usedCorrection/h1_pt_correction_trigger_ccdb"))->SetBinError(iBin, h1PtCorrectionTrigger->GetBinError(iBin)); } } } @@ -355,43 +397,36 @@ struct PhotonChargedTriggerCorrelation { const AxisSpec axisN{1, 0., 1., "#it{N}_{something}"}; const AxisSpec axisCategories{16, 0., 16., "categories"}; - const AxisSpec axisZPv{nBinsZPv, -10, 10, "#it{z}_{pv}"}; - const AxisSpec axisZPvSmol{nBinsZPvSmol, -7, 7, "#it{z}_{pv}"}; + const AxisSpec axisZPv{nBinsZPv, -7, 7, "#it{z}_{pv}"}; const AxisSpec axisMult{nBinsMult + 1, -0.5, nBinsMult + 0.5, "multiplicity"}; - const AxisSpec axisMultSmol{nBinsMultSmol + 1, -0.5, nBinsMultSmol + 0.5, "multiplicity"}; const AxisSpec axisOccupancy{nBinsOccupancy + 1, -0.5, nBinsOccupancy + 0.5, "occupancy"}; const AxisSpec axisPhi{nBinsPhi, 0, constants::math::TwoPI, "#it{#varphi}"}; - const AxisSpec axisEta{nBinsEta, -etaMax, etaMax, "#it{#eta}"}; + const AxisSpec axisEta{nBinsEta, -absEtaMax, absEtaMax, "#it{#eta}"}; + const AxisSpec axisDCAz{nBinsDCAz, -5, 5, "DCA_{z}"}; const AxisSpec axisMgg{nBinsMgg, 0, 0.8, "#it{m}_{#gamma#gamma}"}; const AxisSpec axisPtTrig{binsPtTrig, "#it{p}_{T}^{trig}"}; const AxisSpec axisPtAssoc{binsPtAssoc, "#it{p}_{T}^{assoc}"}; const AxisSpec axisDPhi{binsDPhi, "#Delta#it{#varphi}"}; const AxisSpec axisDEta{binsDEta, "#Delta#it{#eta}"}; - const AxisSpec axisZPvBinning{binsZPv, "#it{z}_{pv} correlation binning"}; - const AxisSpec axisMultBinning{binsMult, "multiplicity correlation binning"}; - const AxisSpec axisZPvBinningMcTrue{binsZPvMcTrue, "#it{z}_{pv} correlation binning for mc true"}; - const AxisSpec axisMultBinningMcTrue{binsMultMcTrue, "multiplicity correlation binning for mc true"}; + const AxisSpec axisZPvBinning{binsZPvBinning, "#it{z}_{pv} correlation binning"}; + const AxisSpec axisMultBinning{binsMultBinning, "multiplicity correlation binning"}; + const AxisSpec axisZPvBinningMcTrue{binsZPvBinningMcTrue, "#it{z}_{pv} correlation binning for mc true"}; + const AxisSpec axisMultBinningMcTrue{binsMultBinningMcTrue, "multiplicity correlation binning for mc true"}; // reco info - histos.add("reco/info/h1_nEvents", "h1_nEvents", kTH1D, {axisCategories}); - histos.get(HIST("reco/info/h1_nEvents"))->GetXaxis()->SetBinLabel(1, "#it{N}_{ev}^{sel}"); - histos.get(HIST("reco/info/h1_nEvents"))->GetXaxis()->SetBinLabel(2, "#it{N}_{ev}"); - histos.get(HIST("reco/info/h1_nEvents"))->GetXaxis()->SetBinLabel(3, "#it{N}_{ev}^{trig}"); - - histos.add("reco/info/h2_zPvMult", "h2_zPvMult", kTHnSparseD, {axisZPv, axisMult}, true); - histos.add("reco/info/h1_occupancy", "h1_occupancy", kTH1D, {axisOccupancy}, true); - histos.add("reco/info/h2_zPvMult_trigEv", "h2_zPvMult_trigEv", kTHnSparseD, {axisZPv, axisMult}, true); - histos.add("reco/info/h1_occupancy_trigEv", "h1_occupancy_trigEv", kTH1D, {axisOccupancy}, true); + histos.add("reco/info/h1_nEvents", "h1_nEvents", kTH1D, {axisCategories}, true); + histos.add("reco/info/h3_ptTrigZPvMult", "h3_ptTrigZPvMult", kTHnSparseD, {axisPtTrig, axisZPv, axisMult}, true); + histos.add("reco/info/h2_ptTrigOccupancy", "h2_ptTrigOccupancy", kTHnSparseD, {axisPtTrig, axisOccupancy}, true); // reco (correlation) analysis histos.add("reco/corr/h3_ptPhiEta_trig", "h3_ptPhiEta_trig", kTHnSparseD, {axisPtAssoc, axisPhi, axisEta}, true); - std::function add_corrHists = - [&](std::string const name_id) { - histos.add(std::format("reco/corr/h3_ptPhiEta_assoc_{}", name_id).data(), std::format("h3_ptPhiEta_assoc_{}", name_id).data(), - kTHnSparseD, {axisPtAssoc, axisPhi, axisEta}, true); + auto const add_corrHists = + [&](std::string const& name_id) { + histos.add(std::format("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_{}", name_id).data(), std::format("h4_ptTrigPtAssocPhiEta_assoc_{}", name_id).data(), + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisPhi, axisEta}, true); histos.add(std::format("reco/corr/h6_corr_{}", name_id).data(), std::format("h6_corr_{}", name_id).data(), kTHnSparseF, {axisDPhi, axisDEta, axisPtTrig, axisPtAssoc, axisZPvBinning, axisMultBinning}, true); histos.add(std::format("reco/corr/h6_mix_{}", name_id).data(), std::format("h6_mix_{}", name_id).data(), @@ -407,9 +442,10 @@ struct PhotonChargedTriggerCorrelation { histos.add("reco/plain/h3_ptPhiEta_photonPCM", "h3_ptPhiEta_photonPCM", kTHnSparseD, {axisPtAssoc, axisPhi, axisEta}, true); add_corrHists("photonPCM"); // photonPCM pairs - histos.add("reco/plain/h4_ptMggZPvMult_photonPCMPair", "h4_ptMggZPvMult_photonPCMPair", kTHnSparseD, {axisPtAssoc, axisMgg, axisZPvBinning, axisMultBinning}, true); - histos.add("reco/plain/h4_ptMggZPvMult_trigEv_photonPCMPair", "h4_ptMggZPvMult_trigEv_photonPCMPair", kTHnSparseD, {axisPtAssoc, axisMgg, axisZPvBinning, axisMultBinning}, true); - histos.add("reco/corr/h5_ptTrigPtAssocMggZPvMult_assoc_photonPCMPair", "h5_ptTrigPtAssocMggZPvMult_assoc_photonPCMPair", kTHnSparseD, {axisPtTrig, axisPtAssoc, axisMgg, axisZPvBinning, axisMultBinning}, true); + histos.add("reco/plain/h5_ptTrigPtAssocMggZPvMult_photonPCMPair", "h5_ptTrigPtAssocMggZPvMult_photonPCMPair", + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisMgg, axisZPvBinning, axisMultBinning}, true); + histos.add("reco/corr/h5_ptTrigPtAssocMggZPvMult_assoc_photonPCMPair", "h5_ptTrigPtAssocMggZPvMult_assoc_photonPCMPair", + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisMgg, axisZPvBinning, axisMultBinning}, true); // pi0PCM add_corrHists("pi0PCMPeak"); add_corrHists("pi0PCMSide"); @@ -420,116 +456,231 @@ struct PhotonChargedTriggerCorrelation { // event mixing for photon pairs histos.add("reco/plain/h2_zPvMult_photonPCMPair_evMix", "h2_zPvMult_photonPCMPair_evMix", kTHnSparseD, {axisZPv, axisMult}, true); histos.add("reco/plain/h4_ptMggZPvMult_photonPCMPair_evMix", "h4_ptMggZPvMult_photonPCMPair_evMix", kTHnSparseD, {axisPtAssoc, axisMgg, axisZPvBinning, axisMultBinning}, true); - histos.add("reco/plain/h4_ptMggZPvMult_trigEv_photonPCMPair_evMix", "h4_ptMggZPvMult_trigEv_photonPCMPair_evMix", kTHnSparseD, {axisPtAssoc, axisMgg, axisZPvBinning, axisMultBinning}, true); // mc info - histos.add("mc/info/h1_nEvents_mcTrue", "h1_nEvents_mcTrue", kTH1D, {axisN}); - histos.add("mc/info/h2_zPvMult_mcTrue", "h2_zPvMult_mcTrue", kTHnSparseD, {axisZPv, axisMult}, true); - histos.add("mc/info/h1_nTrigEv_mcTrue", "h1_nTrigEv_mcTrue", kTH1D, {axisN}); - histos.add("mc/info/h2_zPvMult_trigEv_mcTrue", "h2_zPvMult_trigEv_mcTrue", kTHnSparseD, {axisZPv, axisMult}, true); - histos.add("mc/info/h1_nRecoCol_mcTrue", "h1_nRecoCol_mcTrue", kTH1D, {axisN}); - histos.add("mc/info/h2_zPvMult_recoCol_mcTrue", "h2_zPvMult_recoCol_mcTrue", kTHnSparseD, {axisZPv, axisMult}, true); + histos.add("mc/info/h3_ptTrigZPvMult_true", "h3_ptTrigZPvMult_true", kTHnSparseD, {axisPtTrig, axisZPv, axisMult}, true); + histos.add("mc/info/h3_ptTrigZPvMult_trueAssocEv", "h3_ptTrigZPvMult_trueAssocEv", kTHnSparseD, {axisPtTrig, axisZPv, axisMult}, true); + histos.add("mc/info/h3_ptTrigZPvMult_trueAssocEvRecoPtTrig", "h3_ptTrigZPvMult_trueAssocEvRecoPtTrig", kTHnSparseD, {axisPtTrig, axisZPv, axisMult}, true); // reco and true collision correlations - const std::vector assocMcCorrHistNames = {"hadron", "pipm", "photon", "pi0", "eta"}; - for (auto const& collision_type : {"true", "recoCol_true"}) { - histos.add(std::format("mc/{}/corr/h3_ptPhiEta_trig", collision_type).data(), "h3_ptPhiEta_trig", kTHnSparseD, {axisPtAssoc, axisPhi, axisEta}, true); - for (auto const& assocName : assocMcCorrHistNames) { - histos.add(std::format("mc/{}/corr/h6_corr_{}", collision_type, assocName).data(), std::format("h6_corr_{}", assocName).data(), + const std::vector assocMcCorrNamesMcAll = {"hadron", "pipm", "photon", "pi0", "eta"}; + for (auto const& correlationType : {"true", "trueAssocEv", "trueAssocEvRecoPtTrig"}) { + histos.add(std::format("mc/corr/h3_ptPhiEta_trig_{}", correlationType).data(), std::format("h3_ptPhiEta_trig_{}", correlationType).data(), + kTHnSparseD, {axisPtAssoc, axisPhi, axisEta}, true); + for (auto const& assocName : assocMcCorrNamesMcAll) { + histos.add(std::format("mc/corr/h6_corr_{}_{}", correlationType, assocName).data(), std::format("h6_corr_{}_{}", correlationType, assocName).data(), kTHnSparseD, {axisDPhi, axisDEta, axisPtTrig, axisPtAssoc, axisZPvBinningMcTrue, axisMultBinningMcTrue}, true); - histos.add(std::format("mc/{}/corr/h6_mix_{}", collision_type, assocName).data(), std::format("h6_mix_{}", assocName).data(), + histos.add(std::format("mc/corr/h6_mix_{}_{}", correlationType, assocName).data(), std::format("h6_mix_{}_{}", correlationType, assocName).data(), kTHnSparseD, {axisDPhi, axisDEta, axisPtTrig, axisPtAssoc, axisZPvBinningMcTrue, axisMultBinningMcTrue}, true); } } - // mc efficiency/purity - std::function add_effHists = - [&](std::string const name_id) { - histos.add(std::format("mc/eff/h3_ptPhiEta_{}", name_id).data(), std::format("h3_ptPhiEta_{}", name_id).data(), - kTHnSparseD, {axisPtAssoc, axisPhi, axisEta}, true); - histos.add(std::format("mc/eff/h3_ptZPvMult_{}", name_id).data(), std::format("h3_ptZPvMult_{}", name_id).data(), - kTHnSparseD, {axisPtAssoc, axisZPvSmol, axisMultSmol}, true); + // decay correlation extra info (just true level) + const std::vector assocMcCorrNamesMcDecayAddition = {"photonDecay", "photonDirect", "photonPi0", "photonEta", + "omega", "photonOmega", "etaPrime", "photonEtaPrime", "photonOtherMother"}; + for (auto const& assocName : assocMcCorrNamesMcDecayAddition) { + histos.add(std::format("mc/corr/h6_corr_true_{}", assocName).data(), std::format("h6_corr_true_{}", assocName).data(), + kTHnSparseD, {axisDPhi, axisDEta, axisPtTrig, axisPtAssoc, axisZPvBinningMcTrue, axisMultBinningMcTrue}, true); + histos.add(std::format("mc/corr/h6_mix_true_{}", assocName).data(), std::format("h6_mix_true_{}", assocName).data(), + kTHnSparseD, {axisDPhi, axisDEta, axisPtTrig, axisPtAssoc, axisZPvBinningMcTrue, axisMultBinningMcTrue}, true); + } + + // extra reco correlations with MC info + auto const addMcRecoCorrHists = + [&](std::string const& correlationType, std::string const& assocName) { + histos.add(std::format("mc/corr/h6_corr_{}_{}", correlationType, assocName).data(), std::format("h6_corr_{}_{}", correlationType, assocName).data(), + kTHnSparseD, {axisDPhi, axisDEta, axisPtTrig, axisPtAssoc, axisZPvBinning, axisMultBinning}, true); + histos.add(std::format("mc/corr/h6_mix_{}_{}", correlationType, assocName).data(), std::format("h6_mix_{}_{}", correlationType, assocName).data(), + kTHnSparseD, {axisDPhi, axisDEta, axisPtTrig, axisPtAssoc, axisZPvBinning, axisMultBinning}, true); + }; + // matchable associated particles + const std::vector assocCorrNamesMcExtra = {"hadron", "pipm", "photonPCM"}; + for (auto const& correlationType : {"recoAssocEv", "recoPure", "recoPureTruePtAssoc"}) { + for (auto const& assocName : assocCorrNamesMcExtra) { + addMcRecoCorrHists(correlationType, assocName); + } + } + // invariant mass associated particles + // mc pseudo yield + addMcRecoCorrHists("pseudoReco", "pi0PCM"); + addMcRecoCorrHists("pseudoRecoAssocEv", "pi0PCM"); + addMcRecoCorrHists("pseudoRecoPure", "pi0PCM"); + addMcRecoCorrHists("pseudoRecoPureTruePtAssoc", "pi0PCM"); + // efficiency advanced info + addMcRecoCorrHists("trueGeoAcc", "pi0"); + addMcRecoCorrHists("trueMeasDecay", "pi0"); + + // mc correction + auto const addCorrectionHistsResolved = + [&](std::string const& nameId) { + histos.add(std::format("mc/correction/resol/h4_ptTrigPtAssocPhiEta_{}", nameId).data(), std::format("h4_ptTrigPtAssocPhiEta_{}", nameId).data(), + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisPhi, axisEta}, true); + histos.add(std::format("mc/correction/resol/h4_ptTrigPtAssocZPvMult_{}", nameId).data(), std::format("h4_ptTrigPtAssocZPvMult_{}", nameId).data(), + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisZPv, axisMult}, true); + }; + auto const addCorrectionHistsResolvedBasicTypes = + [&](std::string const& nameReco, std::string const& nameTrue) { + std::vector const recoTypes = {"reco", "recoAssocEv", "recoPure"}; + std::vector const trueTypes = {"true", "trueAssocEv", "trueAssocEvRecoPtTrig"}; + for (std::string const& recoType : recoTypes) { + addCorrectionHistsResolved(std::format("{}_{}", recoType, nameReco)); + } + for (std::string const& trueType : trueTypes) { + addCorrectionHistsResolved(std::format("{}_{}", trueType, nameTrue)); + } + }; + + auto const addCorrectionHistPt = + [&](std::string const& nameId) { + histos.add(std::format("mc/correction/h2_ptTrigPtAssoc_{}", nameId).data(), std::format("h2_ptTrigPtAssoc_{}", nameId).data(), + kTHnSparseD, {axisPtTrig, axisPtAssoc}, true); + }; + auto const addCorrectionHistPtCategories = + [&](std::string const& nameId) { + histos.add(std::format("mc/correction/h3_ptTrigPtAssocCategory_{}", nameId).data(), std::format("h3_ptTrigPtAssocCategory_{}", nameId).data(), + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisCategories}, true); }; - // mc tracks - add_effHists("mcReco_hadron"); - add_effHists("mcReco_hasCorrectMc_hadron"); - add_effHists("mcTrue_recoCol_hadron"); - // mc pipm PID - add_effHists("mcReco_pipm"); - add_effHists("mcReco_hasCorrectMc_pipm"); - add_effHists("mcTrue_recoCol_pipm"); - // mc photonPCM - add_effHists("mcReco_photonPCM"); - add_effHists("mcReco_hasCorrectMc_photonPCM"); - add_effHists("mcTrue_recoCol_photon"); - // mc pi0 - add_effHists("mcTrue_recoCol_pi0"); - // mc eta - add_effHists("mcTrue_recoCol_eta"); - - // test of the test while testing another test. featuring a test - histos.add("test/h2_mult_comp", "h2_mult_comp", kTH2D, {axisMult, axisMult}, true); - histos.add("test/h2_tracks_zPvMultDep", "h2_tracks_zPvMultDep", kTH2D, {axisZPv, axisMult}, true); - histos.add("test/h2_globalTracks_zPvMultDep", "h2_globalTracks_zPvMultDep", kTH2D, {axisZPv, axisMult}, true); + auto const addCorrectionHistsV0 = + [&](std::string const& nameReco, std::string const& nameTrue) { + addCorrectionHistPt(std::format("true_{}", nameTrue)); + addCorrectionHistPt(std::format("trueAssocEv_{}", nameTrue)); + addCorrectionHistPt(std::format("trueAssocEvRecoPtTrig_{}", nameTrue)); + // mc pseudo yield + addCorrectionHistPt(std::format("pseudoReco_{}", nameReco)); + addCorrectionHistPt(std::format("pseudoRecoAssocEv_{}", nameReco)); + addCorrectionHistPt(std::format("pseudoRecoPure_{}", nameReco)); + // efficiency advanced info + addCorrectionHistPt(std::format("trueGeoAcc_{}", nameTrue)); + addCorrectionHistPt(std::format("trueMeasDecay_{}", nameTrue)); + }; + + // matchables + // tracks + addCorrectionHistsResolvedBasicTypes("hadron", "hadron"); + // pipm PID + addCorrectionHistsResolvedBasicTypes("pipm", "pipm"); + // photonPCM + addCorrectionHistsResolvedBasicTypes("photonPCM", "photon"); + // impurities + addCorrectionHistPtCategories("recoImpurities_photonPCM"); + + // invariant mass reconstruction + // pi0 + addCorrectionHistsV0("pi0PCM", "pi0"); + // impurities + addCorrectionHistPtCategories("pseudoRecoImpurities_pi0PCM"); + // eta + addCorrectionHistsV0("etaPCM", "eta"); + + // tests + + // DCAz + histos.add("reco/plain/h5_ptTrigPtAssocDCAzZPvMult_photonPCM", "h5_ptTrigPtAssocDCAzZPvMult_photonPCM", + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisDCAz, axisZPvBinning, axisMultBinning}, true); + histos.add("mc/plain/h5_ptTrigPtAssocDCAzZPvMult_photonPCM", "h5_ptTrigPtAssocDCAzZPvMult_photonPCM", + kTHnSparseD, {axisPtTrig, axisPtAssoc, axisDCAz, axisZPvBinning, axisMultBinning}, true); } // selections /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + // mc split event selection based in the thrid decimal of mc-true posZ + template + bool checkSplitMcEventSelection(T_collision const& collision) + { + // select based on configurables + return collision.thirdDecimalTruePosZ() % splitMcEvN == splitMcEvSelect - 1; + } + + // total event selection (basic selections from producer and analysis level selections) + template + bool totalEvSel(T_collision const& collision) + { + bool isSelEv = true; + + if constexpr (requires { collision.selEv(); }) { + isSelEv = collision.selEv(); + } + return isSelEv && checkSplitMcEventSelection(collision); + } // checks if mcParticle is charged template bool checkChargedMc(T_mcParticle const& mcParticle) { auto const pdgParticle = pdg->GetParticle(mcParticle.pdgCode()); - if (!pdgParticle || pdgParticle->Charge() == 0) - return false; - return true; + return pdgParticle && pdgParticle->Charge() != 0; } - // checks if mcParticle should be detected (physicalPrimary, |eta|) + // checks if fast decaying mcParticle is 'primary' template - bool checkPrimaryEtaMc(T_mcParticle const& mcParticle) + bool checkDecayPrimary(T_mcParticle const& mcParticle) { - if (!mcParticle.isPhysicalPrimary()) - return false; - if (std::abs(mcParticle.eta()) > etaMax) - return false; - return true; + // identify decaying primary + return mcParticle.producedByGenerator(); } - // checks if mcParticle should be detected as primary track (physicalPrimary, charge, |eta|) + // checks if mcParticle daughters are two photons template - bool checkPrimaryTrackMc(T_mcParticle const& mcParticle) + bool checkToGG(T_mcParticle const& mcParticle) { - if (!checkPrimaryEtaMc(mcParticle)) - return false; - if (!checkChargedMc(mcParticle)) + auto const& daughters = mcParticle.template daughters_as(); + constexpr int NDaughtersToGG = 2; + if (daughters.size() != NDaughtersToGG) { return false; + } + auto daughter = daughters.begin(); + auto daughterEnd = daughters.end(); + while (daughter != daughterEnd) { + if ((*daughter).pdgCode() != PDG_t::kGamma) { + return false; + } + daughter++; + } return true; } - // checks if mcParticle should be detected as 'primary' (|eta| not checked) + // check if particle has mother in parent tree template - bool checkH0Primary(T_mcParticle const& mcParticle, int const pdg) + bool checkForMother(T_mcParticle const& mcParticle, int const pdgCode, bool const checkAntiParticle) { - if (mcParticle.pdgCode() != pdg) - return false; - const auto& h0Daughters = mcParticle.template daughters_as(); - // identify primary h0 (account for 0 daughters for some reason) - if (h0Daughters.size() == 0) + if (!mcParticle.has_mothers()) { return false; - for (auto const& h0_daughter : h0Daughters) { - if (!h0_daughter.isPhysicalPrimary()) - return false; } - return true; + auto const mothers = mcParticle.template mothers_as(); + auto mother = mothers.begin(); + auto motherEnd = mothers.end(); + while (mother != motherEnd) { + + // LOGF(info, "searchPdgCode: %i, current[ pdgCode: %i, status: %i, primary: %i ], mother[ pdgCode: %i, status: %i, primary: %i ]", + // pdgCode, + // mcParticle.pdgCode(), mcParticle.getGenStatusCode(), mcParticle.isPhysicalPrimary(), + // (*mother).pdgCode(), (*mother).getGenStatusCode(), (*mother).isPhysicalPrimary()); + + if ((*mother).pdgCode() == pdgCode || (checkAntiParticle && (*mother).pdgCode() == -pdgCode)) { + return true; + } + if (checkForMother(*mother, pdgCode, checkAntiParticle)) { + return true; + } + mother++; + } + return false; } - // checks if mcParticle should be detected as 'primary' pi0->gammagamma (|eta| not checked) + + // checks if daughters are in acceptance template - bool checkH0ToGG(T_mcParticle const& mcParticle, int const pdg) + bool checkDaughtersInAcceptance(T_mcParticle const& mcParticle, std::pair const etaRange, std::pair const phiRange = {0, constants::math::TwoPI}) { - if (!checkH0Primary(mcParticle, pdg)) - return false; - // select h0 -> gg - constexpr int NDaughtersH0ToGG = 2; - if (mcParticle.template daughters_as().size() != NDaughtersH0ToGG) - return false; + auto const& daughterPhotons = mcParticle.template daughters_as(); + auto daughterPhoton = daughterPhotons.begin(); + auto daughterPhotonEnd = daughterPhotons.end(); + while (daughterPhoton != daughterPhotonEnd) { + if ((*daughterPhoton).eta() < etaRange.first || (*daughterPhoton).eta() > etaRange.second) { + return false; + } + if ((*daughterPhoton).phi() < phiRange.first || (*daughterPhoton).phi() > phiRange.second) { + return false; + } + daughterPhoton++; + } return true; } @@ -540,61 +691,63 @@ struct PhotonChargedTriggerCorrelation { // check same mother auto const& posMothers = posTrack.mcParticle().template mothers_as(); auto const& negMothers = negTrack.mcParticle().template mothers_as(); - if (posMothers.size() != 1 || negMothers.size() != 1) + if (posMothers.size() != 1 || negMothers.size() != 1) { return false; - if (posMothers.begin()->globalIndex() != negMothers.begin()->globalIndex()) + } + if (posMothers.begin()->globalIndex() != negMothers.begin()->globalIndex()) { return false; + } // check photon - if (posMothers.begin()->pdgCode() != PDG_t::kGamma) + if (posMothers.begin()->pdgCode() != PDG_t::kGamma) { return false; + } return true; }; - // checks if tracks come from pi0 double conversion + // checks if tracks come from double conversion template - bool isGGFromPi0(T_track const& posTrack1, T_track const& negTrack1, T_track const& posTrack2, T_track const& negTrack2) + bool isGGFromDoubleConversion(T_track const& posTrack1, T_track const& negTrack1, T_track const& posTrack2, T_track const& negTrack2, int const pdgCode) { - if (!isConversionPhoton(posTrack1, negTrack1) || !isConversionPhoton(posTrack2, negTrack2)) + if (!isConversionPhoton(posTrack1, negTrack1) || !isConversionPhoton(posTrack2, negTrack2)) { return false; + } // check same mother auto const& mothers1 = (*(posTrack1.mcParticle().template mothers_as().begin())).template mothers_as(); auto const& mothers2 = (*(posTrack2.mcParticle().template mothers_as().begin())).template mothers_as(); - constexpr int NMothersPhotonFromPi0 = 2; // for some reason two mothers (same particle) for pi0 decays (contradicts PYTHIA documentation, but whatever) - if (mothers1.size() != NMothersPhotonFromPi0 || mothers2.size() != NMothersPhotonFromPi0) + constexpr int NMothersPhotonFromH0 = 2; // for some reason two mothers (same particle) for h0 decays (contradicts PYTHIA documentation, but whatever) + if (mothers1.size() != NMothersPhotonFromH0 || mothers2.size() != NMothersPhotonFromH0) { return false; - if (mothers1.begin()->globalIndex() != mothers2.begin()->globalIndex()) + } + if (mothers1.begin()->globalIndex() != mothers2.begin()->globalIndex()) { return false; - // check pi0 - if (mothers1.begin()->pdgCode() != PDG_t::kPi0) + } + // check particle type + if (mothers1.begin()->pdgCode() != pdgCode) { return false; + } return true; }; // analysis helpers ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ template double getH1ValueAt(T_h1 const* const h1, double const value) { return h1->GetBinContent(h1->FindFixBin(value)); } - // efficiency helpers - enum class EffParticleType { Trigger, - Hadron, - Pipm, - PhotonPCM }; - // efficiency function - template - double getInvEff(double const value) + // correction helpers + enum class CorrectionParticleType { Trigger, + Hadron, + Pipm, + PhotonPCM }; + + template + double getPtCorrection(double const value) { - if constexpr (T_effParticleType == EffParticleType::Trigger) { - return doEffCorrectionTrigger ? getH1ValueAt(h1PtInvEffTrigger, value) : 1; - } else if constexpr (T_effParticleType == EffParticleType::Hadron) { - return doEffCorrectionHadron ? getH1ValueAt(h1PtInvEffHadron, value) : 1; - } else if constexpr (T_effParticleType == EffParticleType::Pipm) { - return doEffCorrectionPipm ? getH1ValueAt(h1PtInvEffPipm, value) : 1; - } else if constexpr (T_effParticleType == EffParticleType::PhotonPCM) { - return doEffCorrectionPhotonPCM ? getH1ValueAt(h1PtInvEffPhotonPCM, value) : 1; + if constexpr (T_correctionParticleType == CorrectionParticleType::Trigger) { + return applyCTotTrigger ? getH1ValueAt(h1PtCorrectionTrigger, value) : 1; } else { return 1; } @@ -606,26 +759,14 @@ struct PhotonChargedTriggerCorrelation { return RecoDecay::constrainAngle(phi1 - phi2, -1 * constants::math::PIHalf); } - // finds bin that value belongs to (assumes ordered bins) (starts at 0; includes underflow (return -1) and overlflow (return bins.size() - 1)) - // should be faster than some std binary search due to small number of bins (zPv, mult) - int findIntervalBin(double value, const std::vector& bins) - { - const int n = bins.size() - 1; - if (value < bins[0]) - return -1; // underflow - for (int i_bin = 0; i_bin < n; i_bin++) - if (value < bins[i_bin + 1]) - return i_bin; - return n; // overflow - } - // checks that two values belong to the same category (assumes ordered bins) // returns -1 for negative result (also for under/overflow values) and bin number (starting at 0) otherwise int checkSameBin(double const value1, double const value2, std::vector const& bins) { // reject underflow - if (value1 < bins[0]) + if (value1 < bins[0]) { return -1; + } // loop over bins const int n = bins.size() - 1; for (int i_bin = 0; i_bin < n; i_bin++) { @@ -641,21 +782,21 @@ struct PhotonChargedTriggerCorrelation { } // check if invariant mass range - enum class MassRange { pi0PCMPeak, - pi0PCMSide, - etaPCMPeak, - etaPCMSide }; + enum class MassRange { Pi0PCMPeak, + Pi0PCMSide, + EtaPCMPeak, + EtaPCMSide }; - template + template bool checkMassRange(double const mgg) { - if constexpr (T_massRange == MassRange::pi0PCMPeak) { + if constexpr (range == MassRange::Pi0PCMPeak) { return mgg > pi0PCMPeakMassRange.value[0] && mgg < pi0PCMPeakMassRange.value[1]; - } else if constexpr (T_massRange == MassRange::pi0PCMSide) { + } else if constexpr (range == MassRange::Pi0PCMSide) { return mgg > pi0PCMSideMassRange.value[0] && mgg < pi0PCMSideMassRange.value[1]; - } else if constexpr (T_massRange == MassRange::etaPCMPeak) { + } else if constexpr (range == MassRange::EtaPCMPeak) { return mgg > etaPCMPeakMassRange.value[0] && mgg < etaPCMPeakMassRange.value[1]; - } else if constexpr (T_massRange == MassRange::etaPCMSide) { + } else if constexpr (range == MassRange::EtaPCMSide) { return (mgg > etaPCMLowSideMassRange.value[0] && mgg < etaPCMLowSideMassRange.value[1]) || (mgg > etaPCMHighSideMassRange.value[0] && mgg < etaPCMHighSideMassRange.value[1]); } @@ -663,6 +804,7 @@ struct PhotonChargedTriggerCorrelation { } // analysis ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ // generalised correlation functions // per collision @@ -671,7 +813,7 @@ struct PhotonChargedTriggerCorrelation { template void corrProcessPlain(T_collision const& collision, T_associatedThisEvent const& associatedThisEvent, - T_funcPlain&& funcPlain) + T_funcPlain const& funcPlain) { // normal spectra (per event - not per trigger) for (auto const& associated : associatedThisEvent) { @@ -683,7 +825,7 @@ struct PhotonChargedTriggerCorrelation { template void corrProcessCorrelation(T_collision const& collision, T_triggersThisEvent const& triggersThisEvent, T_associatedThisEvent const& associatedThisEvent, - T_funcCorrelation&& funcCorrelation) + T_funcCorrelation const& funcCorrelation) { // correlation combinations for (auto const& [trigger, associated] : soa::combinations(soa::CombinationsFullIndexPolicy(triggersThisEvent, associatedThisEvent))) { @@ -691,34 +833,67 @@ struct PhotonChargedTriggerCorrelation { } } + // mixing-number pt scaling with power law + size_t nMixingPtPowerScaling(double const pt, size_t const n0) + { + double const rawScale = 1 + nTriggerScaleCoefficient * std::pow(pt, nTriggerScaleExponent); + return n0 * static_cast(rawScale); + } + // mixing - template void corrProcessMixing(T_collision const& collision, T_associatedThisEvent const& associatedThisEvent, - T_funcMixing&& funcMixing, - size_t const nTriggerMixing, size_t const nTriggersThisDataFrame) + T_funcMixing const& funcMixing, + size_t const nTriggerMixingAt0) { - // skip if event does not contain valid trigger - if (doTrigEvMixing && !collision.trigEv()) + size_t triggerMemoryBinSizeMin = 0; + + // mixing loops (more dynamic than O2 mixing) + std::deque const& savedTriggers = + [&]() -> std::deque const& { + if constexpr (triggerMemoryMode == TriggerMemoryMode::Reco) { + triggerMemoryBinSizeMin = triggerMemoryReco->getBinSizeMin(); + return triggerMemoryReco->getTriggers(collision.posZ(), collision.nGlobalTracks()); + } else if constexpr (triggerMemoryMode == TriggerMemoryMode::True) { + triggerMemoryBinSizeMin = triggerMemoryTrue->getBinSizeMin(); + return triggerMemoryTrue->getTriggers(collision.posZ(), collision.nChargedInEtaRange()); + } else if constexpr (triggerMemoryMode == TriggerMemoryMode::TrueAssocEv) { + triggerMemoryBinSizeMin = triggerMemoryTrueAssocEv->getBinSizeMin(); + return triggerMemoryTrueAssocEv->getTriggers(collision.posZ(), collision.nChargedInEtaRange()); + } else if constexpr (triggerMemoryMode == TriggerMemoryMode::TrueAssocEvRecoPtTrig) { + triggerMemoryBinSizeMin = triggerMemoryTrueAssocEvRecoPtTrig->getBinSizeMin(); + return triggerMemoryTrueAssocEvRecoPtTrig->getTriggers(collision.posZ(), collision.nChargedInEtaRange()); + } + }(); + // require enough mixing statistics + if (triggerMemoryBinSizeMin < static_cast(nTriggerBinMinThreshold)) { return; - - // mixing loops (more efficient than O2 mixing (for now)) - auto savedTriggers = mixingTriggerMemoryReco.getTriggers(collision.posZ(), collision.nGlobalTracks()); - // number of triggers - const size_t mixUpToTriggerN = std::min(savedTriggers.size(), nTriggerMixing + nTriggersThisDataFrame); - const float perTriggerWeight = 1. / (mixUpToTriggerN - nTriggersThisDataFrame); // mixUpToTriggerN <= nTriggersThisDataFrame not problematic since no loop then - // mixing loops - for (size_t i_mixingTrigger = nTriggersThisDataFrame; i_mixingTrigger < mixUpToTriggerN; i_mixingTrigger++) { - for (auto const& associated : associatedThisEvent) { - funcMixing(collision, savedTriggers[i_mixingTrigger].pt(), savedTriggers[i_mixingTrigger].phi(), savedTriggers[i_mixingTrigger].eta(), associated, perTriggerWeight); + } + const size_t nSavedTriggers = savedTriggers.size(); + const size_t iStartTriggerParticle = randomIntInInterval(0, iTriggerStartFraction * nSavedTriggers); + // associated loop + for (auto const& associated : associatedThisEvent) { + // trigger numbers + const size_t nTriggerMixing = nMixingPtPowerScaling(associated.pt(), nTriggerMixingAt0); + const size_t mixUpToTriggerN = std::min(nSavedTriggers, iStartTriggerParticle + nTriggerMixing); + const float perTriggerWeight = 1. / (mixUpToTriggerN - iStartTriggerParticle); // mixUpToTriggerN <= iStartTriggerParticle caught by break statement of loop + // trigger loop + for (size_t i_mixingTrigger = iStartTriggerParticle; i_mixingTrigger < mixUpToTriggerN; i_mixingTrigger++) { + MixingTrigger const& mixingTrigger = savedTriggers[i_mixingTrigger]; + funcMixing(collision, mixingTrigger, associated, perTriggerWeight); } } } + // analysis tasks /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // =/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/=/ + void init(InitContext& initContext) { // analysis info - ccdb->setURL(urlCcdb.value); + ccdb->setURL(urlCcdb); // enabling object caching (otherwise each call goes to CCDB server) ccdb->setCaching(true); // ccdb->setLocalObjectValidityChecking(); @@ -728,76 +903,31 @@ struct PhotonChargedTriggerCorrelation { // init analysis variables // get variables from other tasks - o2::common::core::getTaskOptionValue(initContext, "photon-charged-trigger-producer", "etaMax", etaMax, false); + o2::common::core::getTaskOptionValue(initContext, "photon-charged-trigger-producer", "absEtaMax", absEtaMax, false); // histograms from ccdb initCcdbHistograms(); // create analysis histograms initHistograms(); - } - - // reconstructed //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void processInfo(CorrCollision const& collision) - { - // all events - histos.fill(HIST("reco/info/h1_nEvents"), 1.5); - - // event selection - if (!collision.selEv()) - return; - histos.fill(HIST("reco/info/h1_nEvents"), 0.5); - - histos.fill(HIST("reco/info/h2_zPvMult"), collision.posZ(), collision.nGlobalTracks()); - histos.fill(HIST("reco/info/h1_occupancy"), collision.trackOccupancyInTimeRange()); - - // trigger events - if (!collision.trigEv()) - return; - histos.fill(HIST("reco/info/h1_nEvents"), 2.5); - histos.fill(HIST("reco/info/h2_zPvMult_trigEv"), collision.posZ(), collision.nGlobalTracks()); - histos.fill(HIST("reco/info/h1_occupancy_trigEv"), collision.trackOccupancyInTimeRange()); + // mixing trigger memory + triggerMemoryReco = std::make_unique(binsZPvBinning, binsMultBinning, nTriggerSavedForMixing); + triggerMemoryTrue = std::make_unique(binsZPvBinningMcTrue, binsMultBinningMcTrue, nTriggerSavedForMixing); + triggerMemoryTrueAssocEv = std::make_unique(binsZPvBinningMcTrue, binsMultBinningMcTrue, nTriggerSavedForMixing); + triggerMemoryTrueAssocEvRecoPtTrig = std::make_unique(binsZPvBinningMcTrue, binsMultBinningMcTrue, nTriggerSavedForMixing); } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processInfo, "process general info on collisions and tracks for analysis and qa", false); - void processCorrFirst(CorrCollisions const& collisions, aod::Triggers const& triggers) - { - // do at beginning of each data frame (before other reco correlation process functions) - // (PROCESS_SWITCH of this process has to be declared first) - - // [wow, such empty] - - for (auto const& collision : collisions) { - // event selection - if (!collision.selEv()) - continue; - - // group collision - auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); - - // trigger loop - for (auto const& trigger : triggersThisEvent) { - // trigger info - histos.fill(HIST("reco/corr/h3_ptPhiEta_trig"), trigger.pt(), trigger.phi(), trigger.eta(), - getInvEff(trigger.pt())); - - // save trigger for mixing - mixingTriggerMemoryReco.saveTrigger(trigger.pt(), trigger.phi(), trigger.eta(), collision.posZ(), collision.nGlobalTracks()); - } - } - } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processCorrFirst, "process to gather info before correlation processes", false); + // reconstructed //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ========================================================================================================================================================================================== void processCorrHadron(CorrCollisions const& collisions, aod::Triggers const& triggers, aod::Hadrons const& hadrons) { - size_t const nTriggersThisDataFrame = triggers.size(); - for (auto const& collision : collisions) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { continue; + } // group collision auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); @@ -805,48 +935,51 @@ struct PhotonChargedTriggerCorrelation { auto const funcPlain = [this]([[maybe_unused]] auto const& collision, auto const& associated) { histos.fill(HIST("reco/plain/h3_ptPhiEta_hadron"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(associated.pt())); + associated.pt(), associated.phi(), associated.eta()); }; corrProcessPlain(collision, hadronsThisEvent, funcPlain); auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { // exclude self correlation - if (trigger.jetTrackId() == associated.jetTrackId()) + if (trigger.jetTrackId() == associated.jetTrackId()) { return; + } - histos.fill(HIST("reco/corr/h3_ptPhiEta_assoc_hadron"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(trigger.pt()) * getInvEff(associated.pt())); + histos.fill(HIST("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_hadron"), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta(), + getPtCorrection(trigger.pt())); histos.fill(HIST("reco/corr/h6_corr_hadron"), getDeltaPhi(trigger.phi(), associated.phi()), trigger.eta() - associated.eta(), trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt()) * getInvEff(associated.pt())); + getPtCorrection(trigger.pt())); }; corrProcessCorrelation(collision, triggersThisEvent, hadronsThisEvent, funcCorrelation); - auto const funcMixing = [this](auto const& collision, - float const mixingTriggerPt, float const mixingTriggerPhi, float const mixingTriggerEta, auto const& associated, auto const perTriggerWeight) { + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; + } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { histos.fill(HIST("reco/corr/h6_mix_hadron"), - getDeltaPhi(mixingTriggerPhi, associated.phi()), - mixingTriggerEta - associated.eta(), - mixingTriggerPt, associated.pt(), collision.posZ(), collision.nGlobalTracks(), - perTriggerWeight * getInvEff(mixingTriggerPt) * getInvEff(associated.pt())); + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); }; - corrProcessMixing(collision, hadronsThisEvent, funcMixing, nTriggerMixingHadron, nTriggersThisDataFrame); + corrProcessMixing(collision, hadronsThisEvent, funcMixing, nMixingAt0Hadron); } } PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processCorrHadron, "process standard correlation for associated hardons", false); void processCorrPipm(CorrCollisions const& collisions, aod::Triggers const& triggers, aod::Pipms const& pipms) { - size_t const nTriggersThisDataFrame = triggers.size(); - for (auto const& collision : collisions) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { continue; + } // group collision auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); @@ -854,48 +987,51 @@ struct PhotonChargedTriggerCorrelation { auto const funcPlain = [this]([[maybe_unused]] auto const& collision, auto const& associated) { histos.fill(HIST("reco/plain/h3_ptPhiEta_pipm"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(associated.pt())); + associated.pt(), associated.phi(), associated.eta()); }; corrProcessPlain(collision, pipmsThisEvent, funcPlain); auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { // exclude self correlation - if (trigger.jetTrackId() == associated.jetTrackId()) + if (trigger.jetTrackId() == associated.jetTrackId()) { return; + } - histos.fill(HIST("reco/corr/h3_ptPhiEta_assoc_pipm"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(trigger.pt()) * getInvEff(associated.pt())); + histos.fill(HIST("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_pipm"), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta(), + getPtCorrection(trigger.pt())); histos.fill(HIST("reco/corr/h6_corr_pipm"), getDeltaPhi(trigger.phi(), associated.phi()), trigger.eta() - associated.eta(), trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt()) * getInvEff(associated.pt())); + getPtCorrection(trigger.pt())); }; corrProcessCorrelation(collision, triggersThisEvent, pipmsThisEvent, funcCorrelation); - auto const funcMixing = [this](auto const& collision, - float const mixingTriggerPt, float const mixingTriggerPhi, float const mixingTriggerEta, auto const& associated, auto const perTriggerWeight) { + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; + } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { histos.fill(HIST("reco/corr/h6_mix_pipm"), - getDeltaPhi(mixingTriggerPhi, associated.phi()), - mixingTriggerEta - associated.eta(), - mixingTriggerPt, associated.pt(), collision.posZ(), collision.nGlobalTracks(), - perTriggerWeight * getInvEff(mixingTriggerPt) * getInvEff(associated.pt())); + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); }; - corrProcessMixing(collision, pipmsThisEvent, funcMixing, nTriggerMixingPipm, nTriggersThisDataFrame); + corrProcessMixing(collision, pipmsThisEvent, funcMixing, nMixingAt0Pipm); } } PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processCorrPipm, "process standard correlation for associated pipm", false); void processCorrPhotonPCM(CorrCollisions const& collisions, aod::Triggers const& triggers, aod::PhotonPCMs const& photonPCMs) { - size_t const nTriggersThisDataFrame = triggers.size(); - for (auto const& collision : collisions) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { continue; + } // group collision auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); @@ -903,161 +1039,163 @@ struct PhotonChargedTriggerCorrelation { auto const funcPlain = [this]([[maybe_unused]] auto const& collision, auto const& associated) { histos.fill(HIST("reco/plain/h3_ptPhiEta_photonPCM"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(associated.pt())); + associated.pt(), associated.phi(), associated.eta()); }; corrProcessPlain(collision, photonPCMsThisEvent, funcPlain); auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { // exclude self correlation - if (trigger.jetTrackId() == associated.posTrackId() || trigger.jetTrackId() == associated.negTrackId()) + if (trigger.jetTrackId() == associated.posJetTrackId() || trigger.jetTrackId() == associated.negJetTrackId()) { return; + } - histos.fill(HIST("reco/corr/h3_ptPhiEta_assoc_photonPCM"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(trigger.pt()) * getInvEff(associated.pt())); + histos.fill(HIST("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_photonPCM"), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta(), + getPtCorrection(trigger.pt())); histos.fill(HIST("reco/corr/h6_corr_photonPCM"), getDeltaPhi(trigger.phi(), associated.phi()), trigger.eta() - associated.eta(), trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt()) * getInvEff(associated.pt())); + getPtCorrection(trigger.pt())); }; corrProcessCorrelation(collision, triggersThisEvent, photonPCMsThisEvent, funcCorrelation); - auto const funcMixing = [this](auto const& collision, - float const mixingTriggerPt, float const mixingTriggerPhi, float const mixingTriggerEta, auto const& associated, auto const perTriggerWeight) { + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; + } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { histos.fill(HIST("reco/corr/h6_mix_photonPCM"), - getDeltaPhi(mixingTriggerPhi, associated.phi()), - mixingTriggerEta - associated.eta(), - mixingTriggerPt, associated.pt(), collision.posZ(), collision.nGlobalTracks(), - perTriggerWeight * getInvEff(mixingTriggerPt) * getInvEff(associated.pt())); + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); }; - corrProcessMixing(collision, photonPCMsThisEvent, funcMixing, nTriggerMixingPhotonPCM, nTriggersThisDataFrame); + corrProcessMixing(collision, photonPCMsThisEvent, funcMixing, nMixingAt0PhotonPCM); } } PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processCorrPhotonPCM, "process standard correlation for associated photonPCM", false); void processCorrPhotonPCMPair(CorrCollisions const& collisions, aod::Triggers const& triggers, aod::PhotonPCMPairs const& photonPCMPairs) { - size_t const nTriggersThisDataFrame = triggers.size(); - for (auto const& collision : collisions) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { continue; + } // group collision auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); auto const photonPCMPairsThisEvent = photonPCMPairs.sliceBy(perColPhotonPCMPairs, collision.globalIndex()); auto const funcPlain = [this](auto const& collision, auto const& associated) { - histos.fill(HIST("reco/plain/h4_ptMggZPvMult_photonPCMPair"), associated.pt(), associated.mgg(), collision.posZ(), collision.nGlobalTracks()); + histos.fill(HIST("reco/plain/h5_ptTrigPtAssocMggZPvMult_photonPCMPair"), collision.ptMax(), associated.pt(), associated.mgg(), collision.posZ(), collision.nGlobalTracks()); }; corrProcessPlain(collision, photonPCMPairsThisEvent, funcPlain); - auto const funcPlainTrigEv = [this](auto const& collision, auto const& associated) { - histos.fill(HIST("reco/plain/h4_ptMggZPvMult_trigEv_photonPCMPair"), associated.pt(), associated.mgg(), collision.posZ(), collision.nGlobalTracks()); - }; - if (collision.trigEv()) - corrProcessPlain(collision, photonPCMPairsThisEvent, funcPlainTrigEv); - auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { // exclude self correlation - if (trigger.jetTrackId() == associated.posTrack1Id() || trigger.jetTrackId() == associated.negTrack1Id() || - trigger.jetTrackId() == associated.negTrack2Id() || trigger.jetTrackId() == associated.posTrack2Id()) + if (trigger.jetTrackId() == associated.posJetTrack1Id() || trigger.jetTrackId() == associated.negJetTrack1Id() || + trigger.jetTrackId() == associated.negJetTrack2Id() || trigger.jetTrackId() == associated.posJetTrack2Id()) { return; + } histos.fill(HIST("reco/corr/h5_ptTrigPtAssocMggZPvMult_assoc_photonPCMPair"), trigger.pt(), associated.pt(), associated.mgg(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt())); + getPtCorrection(trigger.pt())); // pi0 - if (checkMassRange(associated.mgg())) { - histos.fill(HIST("reco/corr/h3_ptPhiEta_assoc_pi0PCMPeak"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(trigger.pt())); + if (checkMassRange(associated.mgg())) { + histos.fill(HIST("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_pi0PCMPeak"), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta(), + getPtCorrection(trigger.pt())); histos.fill(HIST("reco/corr/h6_corr_pi0PCMPeak"), getDeltaPhi(trigger.phi(), associated.phi()), trigger.eta() - associated.eta(), trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt())); + getPtCorrection(trigger.pt())); return; } - if (checkMassRange(associated.mgg())) { - histos.fill(HIST("reco/corr/h3_ptPhiEta_assoc_pi0PCMSide"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(trigger.pt())); + if (checkMassRange(associated.mgg())) { + histos.fill(HIST("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_pi0PCMSide"), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta(), + getPtCorrection(trigger.pt())); histos.fill(HIST("reco/corr/h6_corr_pi0PCMSide"), getDeltaPhi(trigger.phi(), associated.phi()), trigger.eta() - associated.eta(), trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt())); + getPtCorrection(trigger.pt())); return; } // eta - if (checkMassRange(associated.mgg())) { - histos.fill(HIST("reco/corr/h3_ptPhiEta_assoc_etaPCMPeak"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(trigger.pt())); + if (checkMassRange(associated.mgg())) { + histos.fill(HIST("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_etaPCMPeak"), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta(), + getPtCorrection(trigger.pt())); histos.fill(HIST("reco/corr/h6_corr_etaPCMPeak"), getDeltaPhi(trigger.phi(), associated.phi()), trigger.eta() - associated.eta(), trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt())); + getPtCorrection(trigger.pt())); return; } - if (checkMassRange(associated.mgg())) { - histos.fill(HIST("reco/corr/h3_ptPhiEta_assoc_etaPCMSide"), - associated.pt(), associated.phi(), associated.eta(), - getInvEff(trigger.pt())); + if (checkMassRange(associated.mgg())) { + histos.fill(HIST("reco/corr/h4_ptTrigPtAssocPhiEta_assoc_etaPCMSide"), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta(), + getPtCorrection(trigger.pt())); histos.fill(HIST("reco/corr/h6_corr_etaPCMSide"), getDeltaPhi(trigger.phi(), associated.phi()), trigger.eta() - associated.eta(), trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), - getInvEff(trigger.pt())); + getPtCorrection(trigger.pt())); return; } }; corrProcessCorrelation(collision, triggersThisEvent, photonPCMPairsThisEvent, funcCorrelation); - auto const funcMixing = [this](auto const& collision, - float const mixingTriggerPt, float const mixingTriggerPhi, float const mixingTriggerEta, auto const& associated, auto const perTriggerWeight) { + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; + } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { // pi0 - if (checkMassRange(associated.mgg())) { + if (checkMassRange(associated.mgg())) { histos.fill(HIST("reco/corr/h6_mix_pi0PCMPeak"), - getDeltaPhi(mixingTriggerPhi, associated.phi()), - mixingTriggerEta - associated.eta(), - mixingTriggerPt, associated.pt(), collision.posZ(), collision.nGlobalTracks(), - perTriggerWeight * getInvEff(mixingTriggerPt)); + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); return; } - if (checkMassRange(associated.mgg())) { + if (checkMassRange(associated.mgg())) { histos.fill(HIST("reco/corr/h6_mix_pi0PCMSide"), - getDeltaPhi(mixingTriggerPhi, associated.phi()), - mixingTriggerEta - associated.eta(), - mixingTriggerPt, associated.pt(), collision.posZ(), collision.nGlobalTracks(), - perTriggerWeight * getInvEff(mixingTriggerPt)); + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); return; } // eta - if (checkMassRange(associated.mgg())) { + if (checkMassRange(associated.mgg())) { histos.fill(HIST("reco/corr/h6_mix_etaPCMPeak"), - getDeltaPhi(mixingTriggerPhi, associated.phi()), - mixingTriggerEta - associated.eta(), - mixingTriggerPt, associated.pt(), collision.posZ(), collision.nGlobalTracks(), - perTriggerWeight * getInvEff(mixingTriggerPt)); + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); return; } - if (checkMassRange(associated.mgg())) { + if (checkMassRange(associated.mgg())) { histos.fill(HIST("reco/corr/h6_mix_etaPCMSide"), - getDeltaPhi(mixingTriggerPhi, associated.phi()), - mixingTriggerEta - associated.eta(), - mixingTriggerPt, associated.pt(), collision.posZ(), collision.nGlobalTracks(), - perTriggerWeight * getInvEff(mixingTriggerPt)); + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); return; } }; - corrProcessMixing(collision, photonPCMPairsThisEvent, funcMixing, nTriggerMixingH0PCM, nTriggersThisDataFrame); + corrProcessMixing(collision, photonPCMPairsThisEvent, funcMixing, nMixingAt0H0PCM); } } PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processCorrPhotonPCMPair, "process standard correlation for associated pi0PCM", false); @@ -1072,16 +1210,17 @@ struct PhotonChargedTriggerCorrelation { auto const& [collision1, photonPCMs1, collision2, photonPCMs2] = *pair; // // check that current und mixing-trigger event are from the same zPv/mult bins - // if (checkSameBin(collision1.posZ(), collision2.posZ(), binsZPv) == -1) { + // if (checkSameBin(collision1.posZ(), collision2.posZ(), binsZPvBinning) == -1) { // std::printf("ERROR: zPv bins do not match\n"); continue; // } - // if (checkSameBin(collision1.nGlobalTracks(), collision2.nGlobalTracks(), binsMult) == -1) { + // if (checkSameBin(collision1.nGlobalTracks(), collision2.nGlobalTracks(), binsMultBinning) == -1) { // std::printf("ERROR: multiplicity bins do not match\n"); continue; // } // event selection - if (!collision1.selEv() || !collision2.selEv()) + if (!totalEvSel(collision1) || !totalEvSel(collision2)) { continue; + } // event info histos.fill(HIST("reco/plain/h2_zPvMult_photonPCMPair_evMix"), collision1.posZ(), collision1.nGlobalTracks()); // mixing loop @@ -1092,393 +1231,1279 @@ struct PhotonChargedTriggerCorrelation { histos.fill(HIST("reco/plain/h4_ptMggZPvMult_photonPCMPair_evMix"), p4photonPCMPair.pt(), p4photonPCMPair.M(), collision1.posZ(), collision1.nGlobalTracks()); } - - // trigger events - if (!collision1.trigEv() || !collision2.trigEv()) - continue; - // mixing loop - for (auto const& [photonPCM1, photonPCM2] : soa::combinations(soa::CombinationsFullIndexPolicy(photonPCMs1, photonPCMs2))) { - ROOT::Math::PtEtaPhiMVector const p4photonPCM1(photonPCM1.pt(), photonPCM1.eta(), photonPCM1.phi(), 0.); - ROOT::Math::PtEtaPhiMVector const p4photonPCM2(photonPCM2.pt(), photonPCM2.eta(), photonPCM2.phi(), 0.); - ROOT::Math::PtEtaPhiMVector const p4photonPCMPair = p4photonPCM1 + p4photonPCM2; - - histos.fill(HIST("reco/plain/h4_ptMggZPvMult_trigEv_photonPCMPair_evMix"), p4photonPCMPair.pt(), p4photonPCMPair.M(), collision1.posZ(), collision1.nGlobalTracks()); - } } } PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processCorrPhotonPCMPairMix, "process gamma-gamma mixing for photonPCM", false); - // mc /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - void processMcInfo(CorrMcCollisions const& mcCollisions, CorrMcDCollisions const& collisions) + void processPCMDCAz(CorrCollision const& collision, aod::PhotonPCMs const& photonPCMs, aod::V0PhotonsKF const&) { - for (auto const& mcCollision : mcCollisions) { - // all events - histos.fill(HIST("mc/info/h1_nEvents_mcTrue"), 0.5); - histos.fill(HIST("mc/info/h2_zPvMult_mcTrue"), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); - - // trigger events - if (!mcCollision.trigEv()) - continue; - histos.fill(HIST("mc/info/h1_nTrigEv_mcTrue"), 0.5); - histos.fill(HIST("mc/info/h2_zPvMult_trigEv_mcTrue"), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + // event selection + if (!totalEvSel(collision)) { + return; } - for (auto const& collision : collisions) { - // event selection - if (!collision.selEv()) - continue; - histos.fill(HIST("mc/info/h1_nRecoCol_mcTrue"), 0.5); - histos.fill(HIST("mc/info/h2_zPvMult_recoCol_mcTrue"), collision.mcCollision_as().posZ(), collision.mcCollision_as().nChargedInEtaRange()); + + for (auto const& photonPCM : photonPCMs) { + histos.fill(HIST("reco/plain/h5_ptTrigPtAssocDCAzZPvMult_photonPCM"), + collision.ptMax(), photonPCM.pt(), photonPCM.v0PhotonKF().dcaZtopv(), collision.posZ(), collision.nGlobalTracks()); } } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcInfo, "process general info on mc collisions and tracks for analysis and qa", false); + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processPCMDCAz, "process to test DCAz distribution of photons in trigger collisions", false); + + // MC /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ========================================================================================================================================================================================== + + // correlations ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ // (sad) attempt at reducing code duplication - enum class McCorrEventType : int { True = 0, - RecoColTrue = 1 }; + enum class McCorrTruthLevel : int { True = 0, + TrueAssocEv = 1, + TrueAssocEvRecoPtTrig = 2, + TrueMeasDecay = 3, + TrueGeoAcc = 4 }; enum class McCorrCorrelationType : int { Correlation = 0, Mixing = 1 }; - enum class McCorrAssociatedType : int { Hadron = 0, - Pipm = 1, - Photon = 2, - Pi0 = 3, - Eta = 4 }; - static constexpr const char* McHistPaths[2][2][5] = { - {{"mc/true/corr/h6_corr_hadron", "mc/true/corr/h6_corr_pipm", "mc/true/corr/h6_corr_photon", - "mc/true/corr/h6_corr_pi0", "mc/true/corr/h6_corr_eta"}, - {"mc/true/corr/h6_mix_hadron", "mc/true/corr/h6_mix_pipm", "mc/true/corr/h6_mix_photon", - "mc/true/corr/h6_mix_pi0", "mc/true/corr/h6_mix_eta"}}, - {{"mc/recoCol_true/corr/h6_corr_hadron", "mc/recoCol_true/corr/h6_corr_pipm", "mc/recoCol_true/corr/h6_corr_photon", - "mc/recoCol_true/corr/h6_corr_pi0", "mc/recoCol_true/corr/h6_corr_eta"}, - {"mc/recoCol_true/corr/h6_mix_hadron", "mc/recoCol_true/corr/h6_mix_pipm", "mc/recoCol_true/corr/h6_mix_photon", - "mc/recoCol_true/corr/h6_mix_pi0", "mc/recoCol_true/corr/h6_mix_eta"}}}; - static constexpr const char* getMcHistPath(McCorrEventType eventType, McCorrCorrelationType correlationType, McCorrAssociatedType associatedType) + enum class McCorrAssocType : int { Hadron = 0, + Pipm = 1, + Photon = 2, + PhotonDecay = 3, + PhotonDirect = 4, + Pi0 = 5, + PhotonPi0 = 6, + Eta = 7, + PhotonEta = 8, + Omega = 9, + PhotonOmega = 10, + EtaPrime = 11, + PhotonEtaPrime = 12, + PhotonOtherMother = 13 }; + static constexpr std::array, 2>, 5> McTrueCorrHistPaths = {{{{{"mc/corr/h6_corr_true_hadron", "mc/corr/h6_corr_true_pipm", + "mc/corr/h6_corr_true_photon", "mc/corr/h6_corr_true_photonDecay", "mc/corr/h6_corr_true_photonDirect", + "mc/corr/h6_corr_true_pi0", "mc/corr/h6_corr_true_photonPi0", "mc/corr/h6_corr_true_eta", "mc/corr/h6_corr_true_photonEta", + "mc/corr/h6_corr_true_omega", "mc/corr/h6_corr_true_photonOmega", "mc/corr/h6_corr_true_etaPrime", "mc/corr/h6_corr_true_photonEtaPrime", "mc/corr/h6_corr_true_photonOtherMother"}, + {"mc/corr/h6_mix_true_hadron", "mc/corr/h6_mix_true_pipm", + "mc/corr/h6_mix_true_photon", "mc/corr/h6_mix_true_photonDecay", "mc/corr/h6_mix_true_photonDirect", + "mc/corr/h6_mix_true_pi0", "mc/corr/h6_mix_true_photonPi0", "mc/corr/h6_mix_true_eta", "mc/corr/h6_mix_true_photonEta", + "mc/corr/h6_mix_true_omega", "mc/corr/h6_mix_true_photonOmega", "mc/corr/h6_mix_true_etaPrime", "mc/corr/h6_mix_true_photonEtaPrime", "mc/corr/h6_mix_true_photonOtherMother"}}}, + {{{"mc/corr/h6_corr_trueAssocEv_hadron", "mc/corr/h6_corr_trueAssocEv_pipm", + "mc/corr/h6_corr_trueAssocEv_photon", "", "", + "mc/corr/h6_corr_trueAssocEv_pi0", "", "mc/corr/h6_corr_trueAssocEv_eta", "", + "", "", "", "", ""}, + {"mc/corr/h6_mix_trueAssocEv_hadron", "mc/corr/h6_mix_trueAssocEv_pipm", + "mc/corr/h6_mix_trueAssocEv_photon", "", "", + "mc/corr/h6_mix_trueAssocEv_pi0", "", "mc/corr/h6_mix_trueAssocEv_eta", "", + "", "", "", "", ""}}}, + {{{"mc/corr/h6_corr_trueAssocEvRecoPtTrig_hadron", "mc/corr/h6_corr_trueAssocEvRecoPtTrig_pipm", + "mc/corr/h6_corr_trueAssocEvRecoPtTrig_photon", "", "", + "mc/corr/h6_corr_trueAssocEvRecoPtTrig_pi0", "", "mc/corr/h6_corr_trueAssocEvRecoPtTrig_eta", "", + "", "", "", "", ""}, + {"mc/corr/h6_mix_trueAssocEvRecoPtTrig_hadron", "mc/corr/h6_mix_trueAssocEvRecoPtTrig_pipm", + "mc/corr/h6_mix_trueAssocEvRecoPtTrig_photon", "", "", + "mc/corr/h6_mix_trueAssocEvRecoPtTrig_pi0", "", "mc/corr/h6_mix_trueAssocEvRecoPtTrig_eta", "", + "", "", "", "", ""}}}, + {{{"", "", + "", "", "", + "mc/corr/h6_corr_trueMeasDecay_pi0", "", "", "", + "", "", "", "", ""}, + {"", "", + "", "", "", + "mc/corr/h6_mix_trueMeasDecay_pi0", "", "", "", + "", "", "", "", ""}}}, + {{{"", "", + "", "", "", + "mc/corr/h6_corr_trueGeoAcc_pi0", "", "", "", + "", "", "", "", ""}, + {"", "", + "", "", "", + "mc/corr/h6_mix_trueGeoAcc_pi0", "", "", "", + "", "", "", "", ""}}}}}; + static constexpr const char* getMcTrueCorrHistPath(McCorrTruthLevel truthLevel, McCorrCorrelationType correlationType, McCorrAssocType assocType) { - return McHistPaths[static_cast(eventType)][static_cast(correlationType)][static_cast(associatedType)]; + return McTrueCorrHistPaths[static_cast(truthLevel)][static_cast(correlationType)][static_cast(assocType)]; } - // fill mc correaltion histograms based on given associated mc particle - template - void fillMcCorrHists(auto const& mcCollision, auto const& trigger, auto const& associated, double const weight) + // fill mc correlation histograms based on given associated mc particle + template + void fillMcCorrHists(auto const& mcCollision, auto const& triggerIn, auto const& associated, double const weight) { + // check mc + if constexpr (requires { triggerIn.template jetTrack_as(); }) { + if (!triggerIn.template jetTrack_as().has_mcParticle()) { + return; + } + } + // trigger info separation + auto const triggerParticle = [&]() { + if constexpr (correlationType == McCorrCorrelationType::Correlation) { + if constexpr (truthLevel == McCorrTruthLevel::TrueAssocEvRecoPtTrig) { + static_assert(requires { triggerIn.template jetTrack_as(); }, "triggerIn must be reco level for TrueAssocEvRecoPtTrig"); + return triggerIn.template jetTrack_as().mcParticle(); + } else if constexpr (truthLevel == McCorrTruthLevel::TrueAssocEv || truthLevel == McCorrTruthLevel::True) { + static_assert(requires { triggerIn.template jetMcParticle_as(); }, "triggerIn must be true level for TrueAssocEv and True"); + return triggerIn; + } + } else if constexpr (correlationType == McCorrCorrelationType::Mixing) { + static_assert(std::same_as, MixingTrigger>, "triggerIn must be MixingTrigger for mixing"); + return triggerIn; + } + }(); + auto const& triggerForPt = triggerIn; + + // exclude self correlation + if constexpr (correlationType == McCorrCorrelationType::Correlation) { + if constexpr (truthLevel == McCorrTruthLevel::TrueAssocEvRecoPtTrig) { + if (triggerParticle.globalIndex() == associated.globalIndex()) { + return; + } + } else if constexpr (truthLevel == McCorrTruthLevel::TrueAssocEv || truthLevel == McCorrTruthLevel::True) { + if (triggerParticle.jetMcParticleId() == associated.globalIndex()) { + return; + } + } + } + + double const dPhi = getDeltaPhi(triggerParticle.phi(), associated.phi()); + double const dEta = triggerParticle.eta() - associated.eta(); + double const ptTrig = triggerForPt.pt(); + double const ptAssoc = associated.pt(); + double const posZ = mcCollision.posZ(); + double const mult = mcCollision.nChargedInEtaRange(); + + if (std::abs(associated.eta()) > absEtaMax) { + return; + } + // standard particles (marked physical primary) - if (checkPrimaryEtaMc(associated)) { - // charged primary ('hadron') selection + if (associated.isPhysicalPrimary()) { + // charged primary ('hadron') if (checkChargedMc(associated)) { - histos.fill(HIST(getMcHistPath(eventType, correlationType, McCorrAssociatedType::Hadron)), - getDeltaPhi(trigger.phi(), associated.phi()), - trigger.eta() - associated.eta(), - trigger.pt(), associated.pt(), mcCollision.posZ(), mcCollision.nChargedInEtaRange(), - weight); + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::Hadron)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); } - // pipm selection + // pipm if (std::abs(associated.pdgCode()) == PDG_t::kPiPlus) { - histos.fill(HIST(getMcHistPath(eventType, correlationType, McCorrAssociatedType::Pipm)), - getDeltaPhi(trigger.phi(), associated.phi()), - trigger.eta() - associated.eta(), - trigger.pt(), associated.pt(), mcCollision.posZ(), mcCollision.nChargedInEtaRange(), - weight); + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::Pipm)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); return; } - // photon selection + // photon if (associated.pdgCode() == PDG_t::kGamma) { - histos.fill(HIST(getMcHistPath(eventType, correlationType, McCorrAssociatedType::Photon)), - getDeltaPhi(trigger.phi(), associated.phi()), - trigger.eta() - associated.eta(), - trigger.pt(), associated.pt(), mcCollision.posZ(), mcCollision.nChargedInEtaRange(), - weight); + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::Photon)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + + // extra info for decay correlation only for total true level + if constexpr (truthLevel != McCorrTruthLevel::True) { + return; + } + + // decay and direct + int const statusDecayLow = 91; + int const statusDecayUp = 99; + int const statusDirect = 62; + + if (associated.getGenStatusCode() >= statusDecayLow && associated.getGenStatusCode() <= statusDecayUp) { + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::PhotonDecay)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, + weight); + // decays from different mothers + int const pdgMother = associated.template mothers_as().begin()->pdgCode(); + switch (pdgMother) { + case PDG_t::kPi0: + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::PhotonPi0)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + break; + case constants::physics::Pdg::kEta: + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::PhotonEta)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + break; + case constants::physics::Pdg::kOmega: + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::PhotonOmega)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + break; + case constants::physics::Pdg::kEtaPrime: + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::PhotonEtaPrime)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + break; + default: + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::PhotonOtherMother)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + break; + } + } else { + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::PhotonDirect)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, + weight); + + if (associated.getGenStatusCode() != statusDirect) { + LOGF(info, "filled primary photon with status: %i", associated.getGenStatusCode()); + } + } return; } return; } // decaying particles (not marked physical primary) - if ((std::abs(associated.eta()) < etaMax)) { - // pi0 selection - if (checkH0Primary(associated, PDG_t::kPi0)) { - histos.fill(HIST(getMcHistPath(eventType, correlationType, McCorrAssociatedType::Pi0)), - getDeltaPhi(trigger.phi(), associated.phi()), - trigger.eta() - associated.eta(), - trigger.pt(), associated.pt(), mcCollision.posZ(), mcCollision.nChargedInEtaRange(), - weight); + if (!checkDecayPrimary(associated)) { + return; + } + // pi0 + if (associated.pdgCode() == PDG_t::kPi0) { + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::Pi0)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + // efficincy extra info + if constexpr (truthLevel != McCorrTruthLevel::TrueAssocEvRecoPtTrig) { return; } - // eta selection - if (checkH0Primary(associated, 221)) { - histos.fill(HIST(getMcHistPath(eventType, correlationType, McCorrAssociatedType::Eta)), - getDeltaPhi(trigger.phi(), associated.phi()), - trigger.eta() - associated.eta(), - trigger.pt(), associated.pt(), mcCollision.posZ(), mcCollision.nChargedInEtaRange(), - weight); + // chosen decay + if (!checkToGG(associated)) { + return; + } + histos.fill(HIST(getMcTrueCorrHistPath(McCorrTruthLevel::TrueMeasDecay, correlationType, McCorrAssocType::Pi0)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + // daughters in acceptance + if (!checkDaughtersInAcceptance(associated, {-absEtaMax, absEtaMax})) { return; } + histos.fill(HIST(getMcTrueCorrHistPath(McCorrTruthLevel::TrueGeoAcc, correlationType, McCorrAssocType::Pi0)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + return; + } + // eta + if (associated.pdgCode() == constants::physics::Pdg::kEta) { + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::Eta)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + return; + } + // extra info for decay correlation only for total true level + if constexpr (truthLevel != McCorrTruthLevel::True) { + return; + } + // omega + if (associated.pdgCode() == constants::physics::Pdg::kOmega) { + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::Omega)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + return; + } + // etaPrime + if (associated.pdgCode() == constants::physics::Pdg::kEtaPrime) { + histos.fill(HIST(getMcTrueCorrHistPath(truthLevel, correlationType, McCorrAssocType::EtaPrime)), + dPhi, dEta, ptTrig, ptAssoc, posZ, mult, weight); + return; } } void processMcTrueCorr(CorrMcCollisions const& mcCollisions, aod::TriggerParticles const& triggerParticles, aod::JetParticles const& mcParticles) { for (auto const& mcCollision : mcCollisions) { + // event selection + if (!totalEvSel(mcCollision)) { + continue; + } + // group collision auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, mcCollision.globalIndex()); auto const mcParticlesThisEvent = mcParticles.sliceBy(perColMcParticles, mcCollision.globalIndex()); - // trigger pairing loop - for (auto const& trigger : triggerParticlesThisEvent) { - // trigger info - histos.fill(HIST("mc/true/corr/h3_ptPhiEta_trig"), trigger.pt(), trigger.phi(), trigger.eta()); - - // save trigger for mixing - mixingTriggerMemoryTrue.saveTrigger(trigger.pt(), trigger.phi(), trigger.eta(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); - - for (auto const& associated : mcParticlesThisEvent) { - // exclude self correlation - if (trigger.jetMcParticleId() == associated.globalIndex()) - continue; + auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { + fillMcCorrHists(collision, trigger, associated, 1); + }; + corrProcessCorrelation(mcCollision, triggerParticlesThisEvent, mcParticlesThisEvent, funcCorrelation); - fillMcCorrHists(mcCollision, trigger, associated, 1); - } + // select mixing events + if (doTrigEvMixing && !mcCollision.trigEv()) { + continue; } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { + fillMcCorrHists(collision, trigger, associated, perTriggerWeight); + }; + corrProcessMixing(mcCollision, mcParticlesThisEvent, funcMixing, nMixingAt0McTrue); } } PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcTrueCorr, "process mc-true (all collisions) correlation for multiple associated particles", false); - void processMcTrueMix(CorrMcCollisions const& mcCollisions, aod::TriggerParticles const& triggerParticles, aod::JetParticles const& mcParticles) + void processMcTrueAssocEvCorr(CorrMcDCollisions const& collisions, CorrMcCollisions const&, aod::TriggerParticles const& triggerParticles, aod::JetParticles const& mcParticles) { - for (auto const& mcCollision : mcCollisions) { + for (auto const& collision : collisions) { + // event selection + if (!totalEvSel(collision)) { + continue; + } + + auto const& mcCollision = collision.mcCollision_as(); + // group collision - auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, mcCollision.globalIndex()); - auto const mcParticlesThisEvent = mcParticles.sliceBy(perColMcParticles, mcCollision.globalIndex()); + auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, collision.mcCollisionId()); + auto const mcParticlesThisEvent = mcParticles.sliceBy(perColMcParticles, collision.mcCollisionId()); - const size_t nTriggerParticlesThisDataFrame = triggerParticles.size(); - auto savedTriggers = mixingTriggerMemoryTrue.getTriggers(mcCollision.posZ(), mcCollision.nChargedInEtaRange()); - const size_t mixUpToTriggerN = std::min(savedTriggers.size(), static_cast(nTriggerMixingMcTrue) + nTriggerParticlesThisDataFrame); - const float perTriggerWeight = 1. / (mixUpToTriggerN - nTriggerParticlesThisDataFrame); + auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { + fillMcCorrHists(collision, trigger, associated, 1); + }; + corrProcessCorrelation(mcCollision, triggerParticlesThisEvent, mcParticlesThisEvent, funcCorrelation); - // trigger loop - for (size_t i_mixingTrigger = nTriggerParticlesThisDataFrame; i_mixingTrigger < mixUpToTriggerN; i_mixingTrigger++) { - MixingTrigger const& mixingTrigger = savedTriggers[i_mixingTrigger]; - for (auto const& associated : mcParticlesThisEvent) { - fillMcCorrHists(mcCollision, mixingTrigger, associated, perTriggerWeight); - } + // select mixing events + if (doTrigEvMixing && !mcCollision.trigEv()) { + continue; } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { + fillMcCorrHists(collision, trigger, associated, perTriggerWeight); + }; + corrProcessMixing(mcCollision, mcParticlesThisEvent, funcMixing, nMixingAt0McTrue); } } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcTrueMix, "process mc-true (all collisions) correlation mixing for multiple associated particles", false); + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcTrueAssocEvCorr, "process mc-true (reco collisions) correlation for multiple associated particles", false); - void processMcRecoColTrueCorr(CorrMcDCollisions const& collisions, CorrMcCollisions const&, aod::TriggerParticles const& triggerParticles, aod::JetParticles const& mcParticles) + void processMcTrueAssocEvRecoPtTrigCorr(CorrMcDCollisions const& collisions, aod::Triggers const& triggers, aod::JetTracksMCD const&, CorrMcCollisions const&, aod::JetParticles const& mcParticles) { for (auto const& collision : collisions) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { continue; + } + + auto const& mcCollision = collision.mcCollision_as(); // group collision - auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, collision.mcCollisionId()); + auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); auto const mcParticlesThisEvent = mcParticles.sliceBy(perColMcParticles, collision.mcCollisionId()); - auto const& mcCollision = collision.mcCollision_as(); + auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { + fillMcCorrHists(collision, trigger, associated, 1); + }; + corrProcessCorrelation(mcCollision, triggersThisEvent, mcParticlesThisEvent, funcCorrelation); - // trigger pairing loop - for (auto const& trigger : triggerParticlesThisEvent) { - // trigger info - histos.fill(HIST("mc/recoCol_true/corr/h3_ptPhiEta_trig"), trigger.pt(), trigger.phi(), trigger.eta()); + // select mixing events + if (doTrigEvMixing && !mcCollision.trigEv()) { + continue; + } - // save trigger for mixing - mixingTriggerMemoryRecoColTrue.saveTrigger(trigger.pt(), trigger.phi(), trigger.eta(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { + fillMcCorrHists(collision, trigger, associated, perTriggerWeight); + }; + corrProcessMixing(mcCollision, mcParticlesThisEvent, funcMixing, nMixingAt0McTrue); + } + } + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcTrueAssocEvRecoPtTrigCorr, "process mc-true (reco collisions with reco ptTrig) correlation for multiple associated particles", false); - // hadrons (tracks) and pipm - for (auto const& associated : mcParticlesThisEvent) { - // exclude self correlation - if (trigger.jetMcParticleId() == associated.globalIndex()) - continue; + void processMcRecoCorrHadron(CorrMcDCollisions const& collisions, aod::Triggers const& triggers, aod::Hadrons const& hadrons, + aod::JetTracksMCD const&, aod::JetParticles const&) + { + for (auto const& collision : collisions) { + // event selection + if (!totalEvSel(collision)) { + continue; + } + + // group collision + auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); + auto const hadronsThisEvent = hadrons.sliceBy(perColHadrons, collision.globalIndex()); - fillMcCorrHists(mcCollision, trigger, associated, 1); + auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { + // exclude self correlation + if (trigger.jetTrackId() == associated.jetTrackId()) { + return; + } + + // check mc + if (!associated.template jetTrack_as().has_mcParticle()) { + return; + } + auto const& associatedMcParticle = associated.template jetTrack_as().mcParticle(); + // collision association + if (associatedMcParticle.mcCollisionId() != collision.mcCollisionId()) { + return; } + histos.fill(HIST("mc/corr/h6_corr_recoAssocEv_hadron"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // purity + if (!checkChargedMc(associatedMcParticle) || !associatedMcParticle.isPhysicalPrimary()) { + return; + } + histos.fill(HIST("mc/corr/h6_corr_recoPure_hadron"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_corr_recoPureTruePtAssoc_hadron"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associatedMcParticle.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + }; + corrProcessCorrelation(collision, triggersThisEvent, hadronsThisEvent, funcCorrelation); + + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { + // check mc + if (!associated.template jetTrack_as().has_mcParticle()) { + return; + } + auto const& associatedMcParticle = associated.template jetTrack_as().mcParticle(); + // collision association + if (associatedMcParticle.mcCollisionId() != collision.mcCollisionId()) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_recoAssocEv_hadron"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // purity + if (!checkChargedMc(associatedMcParticle) || !associatedMcParticle.isPhysicalPrimary()) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_recoPure_hadron"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_mix_recoPureTruePtAssoc_hadron"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associatedMcParticle.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + }; + corrProcessMixing(collision, hadronsThisEvent, funcMixing, nMixingAt0Hadron); } } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoColTrueCorr, "process mc-true (reco collisions) correlation for multiple associated particles", false); + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoCorrHadron, "process correlation for associated hardons with additional mc information", false); - void processMcRecoColTrueMix(CorrMcDCollisions const& collisions, CorrMcCollisions const&, aod::TriggerParticles const& triggerParticles, aod::JetParticles const& mcParticles) + void processMcRecoCorrPipm(CorrMcDCollisions const& collisions, aod::Triggers const& triggers, aod::Pipms const& pipms, + aod::JetTracksMCD const&, aod::JetParticles const&) { for (auto const& collision : collisions) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { continue; + } // group collision - auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, collision.mcCollisionId()); - auto const mcParticlesThisEvent = mcParticles.sliceBy(perColMcParticles, collision.mcCollisionId()); + auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); + auto const pipmsThisEvent = pipms.sliceBy(perColPipms, collision.globalIndex()); - auto const& mcCollision = collision.mcCollision_as(); + auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { + // exclude self correlation + if (trigger.jetTrackId() == associated.jetTrackId()) { + return; + } + + // check mc + if (!associated.template jetTrack_as().has_mcParticle()) { + return; + } + auto const& associatedMcParticle = associated.template jetTrack_as().mcParticle(); + // collision association + if (associatedMcParticle.mcCollisionId() != collision.mcCollisionId()) { + return; + } + histos.fill(HIST("mc/corr/h6_corr_recoAssocEv_pipm"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // purity + if (std::abs(associatedMcParticle.pdgCode()) != PDG_t::kPiPlus || !associatedMcParticle.isPhysicalPrimary()) { + return; + } + histos.fill(HIST("mc/corr/h6_corr_recoPure_pipm"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_corr_recoPureTruePtAssoc_pipm"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associatedMcParticle.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + }; + corrProcessCorrelation(collision, triggersThisEvent, pipmsThisEvent, funcCorrelation); - const size_t nTriggerParticlesThisDataFrame = triggerParticles.size(); - auto savedTriggers = mixingTriggerMemoryRecoColTrue.getTriggers(mcCollision.posZ(), mcCollision.nChargedInEtaRange()); - const size_t mixUpToTriggerN = std::min(savedTriggers.size(), static_cast(nTriggerMixingMcTrue) + nTriggerParticlesThisDataFrame); - const float perTriggerWeight = 1. / (mixUpToTriggerN - nTriggerParticlesThisDataFrame); + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; + } - // trigger loop - for (size_t i_mixingTrigger = nTriggerParticlesThisDataFrame; i_mixingTrigger < mixUpToTriggerN; i_mixingTrigger++) { - MixingTrigger const& mixingTrigger = savedTriggers[i_mixingTrigger]; - for (auto const& associated : mcParticlesThisEvent) { - fillMcCorrHists(mcCollision, mixingTrigger, associated, perTriggerWeight); + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { + // check mc + if (!associated.template jetTrack_as().has_mcParticle()) { + return; + } + auto const& associatedMcParticle = associated.template jetTrack_as().mcParticle(); + // collision association + if (associatedMcParticle.mcCollisionId() != collision.mcCollisionId()) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_recoAssocEv_pipm"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // purity + if (std::abs(associatedMcParticle.pdgCode()) != PDG_t::kPiPlus || !associatedMcParticle.isPhysicalPrimary()) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_recoPure_pipm"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_mix_recoPureTruePtAssoc_pipm"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associatedMcParticle.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + }; + corrProcessMixing(collision, pipmsThisEvent, funcMixing, nMixingAt0Pipm); + } + } + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoCorrPipm, "process correlation for associated pipms with additional mc information", false); + + void processMcRecoCorrPhotonPCM(CorrMcDCollisions const& collisions, aod::Triggers const& triggers, aod::PhotonPCMs const& photonPCMs, + aod::JetTracksMCD const&, aod::JetParticles const&) + { + for (auto const& collision : collisions) { + // event selection + if (!totalEvSel(collision)) { + continue; + } + + // group collision + auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); + auto const photonPCMsThisEvent = photonPCMs.sliceBy(perColPhotonPCMs, collision.globalIndex()); + + auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { + // exclude self correlation + if (trigger.jetTrackId() == associated.posJetTrackId() || trigger.jetTrackId() == associated.negJetTrackId()) { + return; + } + + // check mc + auto const& posTrack = associated.template posJetTrack_as(); + auto const& negTrack = associated.template negJetTrack_as(); + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { + return; + } + // collision association + if (posTrack.mcParticle().mcCollisionId() != collision.mcCollisionId() || negTrack.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + return; + } + histos.fill(HIST("mc/corr/h6_corr_recoAssocEv_photonPCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // purity + auto const& photons = posTrack.mcParticle().template mothers_as(); + if (!isConversionPhoton(posTrack, negTrack) || !photons.begin()->isPhysicalPrimary()) { + return; + } + histos.fill(HIST("mc/corr/h6_corr_recoPure_photonPCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_corr_recoPureTruePtAssoc_photonPCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), photons.begin()->pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + }; + corrProcessCorrelation(collision, triggersThisEvent, photonPCMsThisEvent, funcCorrelation); + + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; + } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { + // check mc + auto const& posTrack = associated.template posJetTrack_as(); + auto const& negTrack = associated.template negJetTrack_as(); + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { + return; + } + // collision association + if (posTrack.mcParticle().mcCollisionId() != collision.mcCollisionId() || negTrack.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_recoAssocEv_photonPCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // purity + auto const& photons = posTrack.mcParticle().template mothers_as(); + if (!isConversionPhoton(posTrack, negTrack) || !photons.begin()->isPhysicalPrimary()) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_recoPure_photonPCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_mix_recoPureTruePtAssoc_photonPCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), photons.begin()->pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + }; + corrProcessMixing(collision, photonPCMsThisEvent, funcMixing, nMixingAt0PhotonPCM); + } + } + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoCorrPhotonPCM, "process correlation for associated photonPCMs with additional mc information", false); + + void processMcRecoCorrPi0PCM(CorrMcDCollisions const& collisions, aod::Triggers const& triggers, aod::PhotonPCMPairs const& photonPCMPairs, + aod::JetTracksMCD const&, aod::JetParticles const&) + { + for (auto const& collision : collisions) { + // event selection + if (!totalEvSel(collision)) { + continue; + } + + // group collision + auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); + auto const photonPCMPairsThisEvent = photonPCMPairs.sliceBy(perColPhotonPCMPairs, collision.globalIndex()); + + auto const funcCorrelation = [this](auto const& collision, auto const& trigger, auto const& associated) { + // exclude self correlation + if (trigger.jetTrackId() == associated.posJetTrack1Id() || trigger.jetTrackId() == associated.negJetTrack1Id() || + trigger.jetTrackId() == associated.posJetTrack2Id() || trigger.jetTrackId() == associated.negJetTrack2Id()) { + return; + } + + // check mc + auto const& posTrack1 = associated.template posJetTrack1_as(); + auto const& negTrack1 = associated.template negJetTrack1_as(); + auto const& posTrack2 = associated.template posJetTrack2_as(); + auto const& negTrack2 = associated.template negJetTrack2_as(); + if (!posTrack1.has_mcParticle() || !negTrack1.has_mcParticle() || !posTrack2.has_mcParticle() || !negTrack2.has_mcParticle()) { + return; + } + // pseudo yield + if (!isGGFromDoubleConversion(posTrack1, negTrack1, posTrack2, negTrack2, PDG_t::kPi0)) { + return; + } + histos.fill(HIST("mc/corr/h6_corr_pseudoReco_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // collision association + if (posTrack1.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + return; + } + histos.fill(HIST("mc/corr/h6_corr_pseudoRecoAssocEv_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // purity (just secondaries) + auto const& photons1 = posTrack1.mcParticle().template mothers_as(); + auto const& mothersOfPhoton = photons1.begin()->template mothers_as(); + if (!checkDecayPrimary(*(mothersOfPhoton.begin()))) { + return; } + histos.fill(HIST("mc/corr/h6_corr_pseudoRecoPure_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_corr_pseudoRecoPureTruePtAssoc_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), mothersOfPhoton.begin()->pt(), collision.posZ(), collision.nGlobalTracks(), + getPtCorrection(trigger.pt())); + }; + corrProcessCorrelation(collision, triggersThisEvent, photonPCMPairsThisEvent, funcCorrelation); + + // select mixing events + if (doTrigEvMixing && !collision.trigEv()) { + continue; } + + auto const funcMixing = [this](auto const& collision, auto const& trigger, auto const& associated, auto const perTriggerWeight) { + // check mc + auto const& posTrack1 = associated.template posJetTrack1_as(); + auto const& negTrack1 = associated.template negJetTrack1_as(); + auto const& posTrack2 = associated.template posJetTrack2_as(); + auto const& negTrack2 = associated.template negJetTrack2_as(); + if (!posTrack1.has_mcParticle() || !negTrack1.has_mcParticle() || !posTrack2.has_mcParticle() || !negTrack2.has_mcParticle()) { + return; + } + // pseudo yield + if (!isGGFromDoubleConversion(posTrack1, negTrack1, posTrack2, negTrack2, PDG_t::kPi0)) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_pseudoReco_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // collision association + if (posTrack1.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_pseudoRecoAssocEv_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // purity (just secondaries) + auto const& photons1 = posTrack1.mcParticle().template mothers_as(); + auto const& mothersOfPhoton = photons1.begin()->template mothers_as(); + if (!checkDecayPrimary(*(mothersOfPhoton.begin()))) { + return; + } + histos.fill(HIST("mc/corr/h6_mix_pseudoRecoPure_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), associated.pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + // true pt + histos.fill(HIST("mc/corr/h6_mix_pseudoRecoPureTruePtAssoc_pi0PCM"), + getDeltaPhi(trigger.phi(), associated.phi()), + trigger.eta() - associated.eta(), + trigger.pt(), mothersOfPhoton.begin()->pt(), collision.posZ(), collision.nGlobalTracks(), + perTriggerWeight * getPtCorrection(trigger.pt())); + }; + corrProcessMixing(collision, photonPCMPairsThisEvent, funcMixing, nMixingAt0PhotonPCM); + } + } + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoCorrPi0PCM, "process correlation for associated pi0PCMs with additional mc information", false); + + // other //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + + enum class McCorrectionObservable : int { PhiEta = 0, + ZPvMult = 1 }; + enum class McResolvedTruthLevel : int { Reco = 0, + RecoAssocEv = 1, + RecoPure = 2, + TrueAssocEvRecoPtTrig = 3, + TrueAssocEv = 4, + True = 5 }; + enum class CorrAssocType : int { Hadron = 0, + Pipm = 1, + PhotonPCM = 2 }; + static constexpr std::array, 6>, 2> McResolvedCorrectionHistPaths = { + {{{{"mc/correction/resol/h4_ptTrigPtAssocPhiEta_reco_hadron", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_reco_pipm", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_reco_photonPCM"}, + {"mc/correction/resol/h4_ptTrigPtAssocPhiEta_recoAssocEv_hadron", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_recoAssocEv_pipm", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_recoAssocEv_photonPCM"}, + {"mc/correction/resol/h4_ptTrigPtAssocPhiEta_recoPure_hadron", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_recoPure_pipm", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_recoPure_photonPCM"}, + {"mc/correction/resol/h4_ptTrigPtAssocPhiEta_trueAssocEvRecoPtTrig_hadron", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_trueAssocEvRecoPtTrig_pipm", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_trueAssocEvRecoPtTrig_photon"}, + {"mc/correction/resol/h4_ptTrigPtAssocPhiEta_trueAssocEv_hadron", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_trueAssocEv_pipm", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_trueAssocEv_photon"}, + {"mc/correction/resol/h4_ptTrigPtAssocPhiEta_true_hadron", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_true_pipm", + "mc/correction/resol/h4_ptTrigPtAssocPhiEta_true_photon"}}}, + {{{"mc/correction/resol/h4_ptTrigPtAssocZPvMult_reco_hadron", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_reco_pipm", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_reco_photonPCM"}, + {"mc/correction/resol/h4_ptTrigPtAssocZPvMult_recoAssocEv_hadron", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_recoAssocEv_pipm", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_recoAssocEv_photonPCM"}, + {"mc/correction/resol/h4_ptTrigPtAssocZPvMult_recoPure_hadron", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_recoPure_pipm", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_recoPure_photonPCM"}, + {"mc/correction/resol/h4_ptTrigPtAssocZPvMult_trueAssocEvRecoPtTrig_hadron", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_trueAssocEvRecoPtTrig_pipm", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_trueAssocEvRecoPtTrig_photon"}, + {"mc/correction/resol/h4_ptTrigPtAssocZPvMult_trueAssocEv_hadron", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_trueAssocEv_pipm", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_trueAssocEv_photon"}, + {"mc/correction/resol/h4_ptTrigPtAssocZPvMult_true_hadron", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_true_pipm", + "mc/correction/resol/h4_ptTrigPtAssocZPvMult_true_photon"}}}}}; + static constexpr char const* getMcResolvedCorrectionHistPath(McCorrectionObservable observable, McResolvedTruthLevel truthLevel, CorrAssocType assocType) + { + return McResolvedCorrectionHistPaths[static_cast(observable)][static_cast(truthLevel)][static_cast(assocType)]; + } + template + void fillResolvedCorrectionHists(auto const& collision, auto const& associated) + { + if constexpr (truthLevel == McResolvedTruthLevel::Reco || truthLevel == McResolvedTruthLevel::RecoAssocEv || truthLevel == McResolvedTruthLevel::RecoPure) { + histos.fill(HIST(getMcResolvedCorrectionHistPath(McCorrectionObservable::PhiEta, truthLevel, assocType)), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta()); + histos.fill(HIST(getMcResolvedCorrectionHistPath(McCorrectionObservable::ZPvMult, truthLevel, assocType)), + collision.ptMax(), associated.pt(), collision.posZ(), collision.nGlobalTracks()); + } + if constexpr (truthLevel == McResolvedTruthLevel::TrueAssocEvRecoPtTrig) { + histos.fill(HIST(getMcResolvedCorrectionHistPath(McCorrectionObservable::PhiEta, truthLevel, assocType)), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta()); + histos.fill(HIST(getMcResolvedCorrectionHistPath(McCorrectionObservable::ZPvMult, truthLevel, assocType)), + collision.ptMax(), associated.pt(), collision.template mcCollision_as().posZ(), collision.template mcCollision_as().nChargedInEtaRange()); + } + if constexpr (truthLevel == McResolvedTruthLevel::True || truthLevel == McResolvedTruthLevel::TrueAssocEv) { + histos.fill(HIST(getMcResolvedCorrectionHistPath(McCorrectionObservable::PhiEta, truthLevel, assocType)), + collision.ptMax(), associated.pt(), associated.phi(), associated.eta()); + histos.fill(HIST(getMcResolvedCorrectionHistPath(McCorrectionObservable::ZPvMult, truthLevel, assocType)), + collision.ptMax(), associated.pt(), collision.posZ(), collision.nChargedInEtaRange()); } } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoColTrueMix, "process mc-true (reco collisions) correlation mixing for multiple associated particles", false); - void processMcRecoColEff(CorrMcDCollision const& collision, aod::JetTracksMCD const& tracks, - aod::Hadrons const& hadrons, aod::Pipms const& pipms, aod::PhotonPCMs const& photonPCMs, - CorrMcCollisions const&, aod::JetParticles const& mcParticles, aod::TriggerParticles const& triggerParticles) + enum class PhotonImpurity { WrongEv, + Misid, + K0Short, + K0Long, + Lambda0, + Other }; + static constexpr double binValuePi0Impurities(PhotonImpurity const impurity) + { + return static_cast(impurity) + 0.5; + } + + void processMcRecoCorrection(CorrMcDCollision const& collision, aod::JetTracksMCD const&, + aod::Hadrons const& hadrons, aod::Pipms const& pipms, aod::PhotonPCMs const& photonPCMs, aod::PhotonPCMPairs const& photonPCMPairs, + CorrMcCollisions const&, aod::JetParticles const& mcParticles) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { return; + } + auto const& mcCollision = collision.mcCollision_as(); + + // group collision auto const mcParticlesThisEvent = mcParticles.sliceBy(perColMcParticles, collision.mcCollisionId()); - auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, collision.mcCollisionId()); // hadrons for (auto const& hadron : hadrons) { - if (doTrigEvEff && !collision.trigEv() && hadron.pt() < ptCutTrigEvEff) + // reconstructed + fillResolvedCorrectionHists(collision, hadron); + // check mc + if (!hadron.jetTrack_as().has_mcParticle()) { continue; - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcReco_hadron"), hadron.pt(), hadron.phi(), hadron.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcReco_hadron"), hadron.pt(), collision.posZ(), collision.nGlobalTracks()); - // purity - if (!hadron.jetTrack_as().has_mcParticle()) - continue; - auto const hadronParticle = hadron.jetTrack_as().mcParticle(); - if (!checkPrimaryTrackMc(hadronParticle)) + } + auto const& hadronParticle = hadron.jetTrack_as().mcParticle(); + // collision association + if (hadronParticle.mcCollisionId() != collision.mcCollisionId()) { continue; - if (requireSingleCollisionPurity && hadronParticle.mcCollisionId() != collision.mcCollisionId()) + } + fillResolvedCorrectionHists(collision, hadron); + // purity + if (!checkChargedMc(hadronParticle) || !hadronParticle.isPhysicalPrimary()) { continue; - - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcReco_hasCorrectMc_hadron"), hadron.pt(), hadron.phi(), hadron.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcReco_hasCorrectMc_hadron"), hadron.pt(), collision.posZ(), collision.nGlobalTracks()); + } + fillResolvedCorrectionHists(collision, hadron); } // pipm for (auto const& pipm : pipms) { - if (doTrigEvEff && !collision.trigEv() && pipm.pt() < ptCutTrigEvEff) + // reconstructed + fillResolvedCorrectionHists(collision, pipm); + // check mc + if (!pipm.jetTrack_as().has_mcParticle()) { continue; - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcReco_pipm"), pipm.pt(), pipm.phi(), pipm.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcReco_pipm"), pipm.pt(), collision.posZ(), collision.nGlobalTracks()); - // purity - if (!pipm.jetTrack_as().has_mcParticle()) - continue; - auto const pipmParticle = pipm.jetTrack_as().mcParticle(); - if (std::abs(pipmParticle.pdgCode()) != PDG_t::kPiPlus || !checkPrimaryEtaMc(pipmParticle)) + } + auto const& pipmParticle = pipm.jetTrack_as().mcParticle(); + // collision association + if (pipmParticle.mcCollisionId() != collision.mcCollisionId()) { continue; - if (requireSingleCollisionPurity && pipmParticle.mcCollisionId() != collision.mcCollisionId()) + } + fillResolvedCorrectionHists(collision, pipm); + // purity + if (std::abs(pipmParticle.pdgCode()) != PDG_t::kPiPlus || !pipmParticle.isPhysicalPrimary()) { continue; - - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcReco_hasCorrectMc_pipm"), pipm.pt(), pipm.phi(), pipm.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcReco_hasCorrectMc_pipm"), pipm.pt(), collision.posZ(), collision.nGlobalTracks()); + } + fillResolvedCorrectionHists(collision, pipm); } // photonPCM for (auto const& photonPCM : photonPCMs) { - if (doTrigEvEff && !collision.trigEv()) + // reconstructed + fillResolvedCorrectionHists(collision, photonPCM); + // check mc + auto const& posTrack = photonPCM.posJetTrack_as(); + auto const& negTrack = photonPCM.negJetTrack_as(); + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { continue; - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcReco_photonPCM"), photonPCM.pt(), photonPCM.phi(), photonPCM.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcReco_photonPCM"), photonPCM.pt(), collision.posZ(), collision.nGlobalTracks()); - + } + // collision association + if (posTrack.mcParticle().mcCollisionId() != collision.mcCollisionId() || negTrack.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_recoImpurities_photonPCM"), + collision.ptMax(), photonPCM.pt(), binValuePi0Impurities(PhotonImpurity::WrongEv)); + continue; + } + fillResolvedCorrectionHists(collision, photonPCM); // purity - // (V0Legs does not have the tracks reference as index column (just int)??) - auto const& posTrack = tracks.rawIteratorAt(photonPCM.posTrackId() - tracks.offset()); - auto const& negTrack = tracks.rawIteratorAt(photonPCM.negTrackId() - tracks.offset()); - if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) + if (!isConversionPhoton(posTrack, negTrack)) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_recoImpurities_photonPCM"), + collision.ptMax(), photonPCM.pt(), binValuePi0Impurities(PhotonImpurity::Misid)); continue; - if (!isConversionPhoton(posTrack, negTrack) || !checkPrimaryEtaMc(*(posTrack.mcParticle().mothers_as().begin()))) + } + auto const& photons = posTrack.mcParticle().mothers_as(); + if (!photons.begin()->isPhysicalPrimary()) { + if (checkForMother(*(photons.begin()), PDG_t::kK0Short, true)) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_recoImpurities_photonPCM"), + collision.ptMax(), photonPCM.pt(), binValuePi0Impurities(PhotonImpurity::K0Short)); + continue; + } + if (checkForMother(*(photons.begin()), PDG_t::kK0Long, true)) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_recoImpurities_photonPCM"), + collision.ptMax(), photonPCM.pt(), binValuePi0Impurities(PhotonImpurity::K0Long)); + continue; + } + if (checkForMother(*(photons.begin()), PDG_t::kLambda0, true)) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_recoImpurities_photonPCM"), + collision.ptMax(), photonPCM.pt(), binValuePi0Impurities(PhotonImpurity::Lambda0)); + continue; + } + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_recoImpurities_photonPCM"), + collision.ptMax(), photonPCM.pt(), binValuePi0Impurities(PhotonImpurity::Other)); continue; - if (requireSingleCollisionPurity && posTrack.mcParticle().mcCollisionId() != collision.mcCollisionId()) + } + fillResolvedCorrectionHists(collision, photonPCM); + } + + // h0 PCM + for (auto const& photonPCMPair : photonPCMPairs) { + // check mc + auto const& posTrack1 = photonPCMPair.posJetTrack1_as(); + auto const& negTrack1 = photonPCMPair.negJetTrack1_as(); + auto const& posTrack2 = photonPCMPair.posJetTrack2_as(); + auto const& negTrack2 = photonPCMPair.negJetTrack2_as(); + if (!posTrack1.has_mcParticle() || !negTrack1.has_mcParticle() || !posTrack2.has_mcParticle() || !negTrack2.has_mcParticle()) { continue; + } - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcReco_hasCorrectMc_photonPCM"), photonPCM.pt(), photonPCM.phi(), photonPCM.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcReco_hasCorrectMc_photonPCM"), photonPCM.pt(), collision.posZ(), collision.nGlobalTracks()); + // pi0PCM + if (isGGFromDoubleConversion(posTrack1, negTrack1, posTrack2, negTrack2, PDG_t::kPi0)) { + // pseudo reconstructed + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_pseudoReco_pi0PCM"), collision.ptMax(), photonPCMPair.pt()); + // collision association + if (posTrack1.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_pseudoRecoImpurities_pi0PCM"), + collision.ptMax(), photonPCMPair.pt(), binValuePi0Impurities(PhotonImpurity::WrongEv)); + continue; + } + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_pseudoRecoAssocEv_pi0PCM"), collision.ptMax(), photonPCMPair.pt()); + // purity + // note: do not dereference 'mothers' and store element in 'const&' (leads to seg fault) + auto const& photons1 = posTrack1.mcParticle().mothers_as(); + auto const& mothersOfPhoton = photons1.begin()->mothers_as(); + auto const& grandmothersOfPhoton = mothersOfPhoton.begin()->mothers_as(); + if (!checkDecayPrimary(*(mothersOfPhoton.begin()))) { + if (std::abs(grandmothersOfPhoton.begin()->pdgCode()) == PDG_t::kK0Short) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_pseudoRecoImpurities_pi0PCM"), + collision.ptMax(), photonPCMPair.pt(), binValuePi0Impurities(PhotonImpurity::K0Short)); + continue; + } + if (std::abs(grandmothersOfPhoton.begin()->pdgCode()) == PDG_t::kK0Long) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_pseudoRecoImpurities_pi0PCM"), + collision.ptMax(), photonPCMPair.pt(), binValuePi0Impurities(PhotonImpurity::K0Long)); + continue; + } + if (std::abs(grandmothersOfPhoton.begin()->pdgCode()) == PDG_t::kLambda0) { + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_pseudoRecoImpurities_pi0PCM"), + collision.ptMax(), photonPCMPair.pt(), binValuePi0Impurities(PhotonImpurity::Lambda0)); + continue; + } + histos.fill(HIST("mc/correction/h3_ptTrigPtAssocCategory_pseudoRecoImpurities_pi0PCM"), + collision.ptMax(), photonPCMPair.pt(), binValuePi0Impurities(PhotonImpurity::Other)); + continue; + } + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_pseudoRecoPure_pi0PCM"), + collision.ptMax(), photonPCMPair.pt()); + continue; + } + // etaPCM + if (isGGFromDoubleConversion(posTrack1, negTrack1, posTrack2, negTrack2, constants::physics::Pdg::kEta)) { + // pseudo reconstructed + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_pseudoReco_etaPCM"), collision.ptMax(), photonPCMPair.pt()); + // collision association + if (posTrack1.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + continue; + } + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_pseudoRecoAssocEv_etaPCM"), collision.ptMax(), photonPCMPair.pt()); + // purity + auto const& photons1 = posTrack1.mcParticle().mothers_as(); + auto const& mothersOfPhoton = photons1.begin()->mothers_as(); + if (!checkDecayPrimary(*(mothersOfPhoton.begin()))) { + continue; + } + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_pseudoRecoPure_etaPCM"), collision.ptMax(), photonPCMPair.pt()); + continue; + } } // mcParticle loop for (auto const& mcParticle : mcParticlesThisEvent) { - bool const countChargedTrigEvEff = !doTrigEvEff || collision.trigEv() || mcParticle.pt() > ptCutTrigEvEff; - bool const countOtherTrigEvEff = !doTrigEvEff || collision.trigEv(); + if (std::abs(mcParticle.eta()) > absEtaMax) { + continue; + } // standard particles (marked physical primary) - if (checkPrimaryEtaMc(mcParticle)) { + if (mcParticle.isPhysicalPrimary()) { + // charged // hadrons - if (checkChargedMc(mcParticle) && countChargedTrigEvEff) { - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcTrue_recoCol_hadron"), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcTrue_recoCol_hadron"), mcParticle.pt(), collision.mcCollision_as().posZ(), collision.nGlobalTracks()); + if (checkChargedMc(mcParticle)) { + fillResolvedCorrectionHists(collision, mcParticle); + fillResolvedCorrectionHists(mcCollision, mcParticle); } // pipm - if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && countChargedTrigEvEff) { - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcTrue_recoCol_pipm"), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcTrue_recoCol_pipm"), mcParticle.pt(), collision.mcCollision_as().posZ(), collision.nGlobalTracks()); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { + fillResolvedCorrectionHists(collision, mcParticle); + fillResolvedCorrectionHists(mcCollision, mcParticle); + continue; } + // non charged // photons - if (mcParticle.pdgCode() == PDG_t::kGamma && countOtherTrigEvEff) { - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcTrue_recoCol_photon"), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcTrue_recoCol_photon"), mcParticle.pt(), collision.mcCollision_as().posZ(), collision.nGlobalTracks()); + if (mcParticle.pdgCode() == PDG_t::kGamma) { + fillResolvedCorrectionHists(collision, mcParticle); + fillResolvedCorrectionHists(mcCollision, mcParticle); + continue; } + continue; } // decaying particles (not marked physical primary) - if ((std::abs(mcParticle.eta()) < etaMax)) { - // pi0 - if (checkH0ToGG(mcParticle, PDG_t::kPi0) && countOtherTrigEvEff) { - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcTrue_recoCol_pi0"), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcTrue_recoCol_pi0"), mcParticle.pt(), collision.mcCollision_as().posZ(), collision.nGlobalTracks()); + if (!checkDecayPrimary(mcParticle)) { + continue; + } + // pi0 + if (mcParticle.pdgCode() == PDG_t::kPi0) { + // true + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueAssocEv_pi0"), mcCollision.ptMax(), mcParticle.pt()); + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueAssocEvRecoPtTrig_pi0"), collision.ptMax(), mcParticle.pt()); + // chosen decay + if (!checkToGG(mcParticle)) { + continue; } - // eta - if (checkH0ToGG(mcParticle, 221) && countOtherTrigEvEff) { - histos.fill(HIST("mc/eff/h3_ptPhiEta_mcTrue_recoCol_eta"), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); - histos.fill(HIST("mc/eff/h3_ptZPvMult_mcTrue_recoCol_eta"), mcParticle.pt(), collision.mcCollision_as().posZ(), collision.nGlobalTracks()); + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueMeasDecay_pi0"), collision.ptMax(), mcParticle.pt()); + // daughters in acceptance + if (!checkDaughtersInAcceptance(mcParticle, {-absEtaMax, absEtaMax})) { + continue; + } + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueGeoAcc_pi0"), collision.ptMax(), mcParticle.pt()); + continue; + } + // eta + if (mcParticle.pdgCode() == constants::physics::Pdg::kEta) { + // true + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueAssocEv_eta"), mcCollision.ptMax(), mcParticle.pt()); + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueAssocEvRecoPtTrig_eta"), collision.ptMax(), mcParticle.pt()); + // chosen decay + if (!checkToGG(mcParticle)) { + continue; } + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueMeasDecay_eta"), collision.ptMax(), mcParticle.pt()); + // daughters in acceptance + if (!checkDaughtersInAcceptance(mcParticle, {-absEtaMax, absEtaMax})) { + continue; + } + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_trueGeoAcc_eta"), collision.ptMax(), mcParticle.pt()); + continue; } } } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoColEff, "process MC data to calculate efficiencies and purities", false); + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcRecoCorrection, "process mc reconstruction to calculate corrections", false); + + void processMcTrueCorrection(CorrMcCollision const& mcCollision, aod::JetParticles const& mcParticles) + { + // event selection + if (!totalEvSel(mcCollision)) { + return; + } - // test ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + for (auto const& mcParticle : mcParticles) { + if (std::abs(mcParticle.eta()) > absEtaMax) { + continue; + } - void processTest(CorrCollision const& collision, - soa::Join const& tracks, soa::Join const&, - aod::Hadrons const& hadrons) + // standard particles (marked physical primary) + if (mcParticle.isPhysicalPrimary()) { + // charged + // hadrons + if (checkChargedMc(mcParticle)) { + fillResolvedCorrectionHists(mcCollision, mcParticle); + } + // pipm + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { + fillResolvedCorrectionHists(mcCollision, mcParticle); + continue; + } + // non charged + // photons + if (mcParticle.pdgCode() == PDG_t::kGamma) { + fillResolvedCorrectionHists(mcCollision, mcParticle); + continue; + } + continue; + } + + // decaying particles (not marked physical primary) + if (!checkDecayPrimary(mcParticle)) { + continue; + } + // pi0 + if (mcParticle.pdgCode() == PDG_t::kPi0) { + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_true_pi0"), mcCollision.ptMax(), mcParticle.pt()); + continue; + } + // eta + if (mcParticle.pdgCode() == constants::physics::Pdg::kEta) { + histos.fill(HIST("mc/correction/h2_ptTrigPtAssoc_true_eta"), mcCollision.ptMax(), mcParticle.pt()); + continue; + } + } + } + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcTrueCorrection, "process mc true data to calculate corrections", false); + + void processPCMDCAzTrue(CorrMcDCollision const& collision, aod::PhotonPCMs const& photonPCMs, aod::V0PhotonsKF const&, aod::JetTracksMCD const&, + CorrMcCollisions const&, aod::JetParticles const&) { // event selection - if (!collision.selEv()) + if (!totalEvSel(collision)) { return; + } - histos.fill(HIST("test/h2_mult_comp"), collision.nGlobalTracks(), hadrons.size()); + for (auto const& photonPCM : photonPCMs) { + // check mc + auto const& posTrack = photonPCM.posJetTrack_as(); + auto const& negTrack = photonPCM.negJetTrack_as(); + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { + continue; + } + // collision association + if (posTrack.mcParticle().mcCollisionId() != collision.mcCollisionId() || negTrack.mcParticle().mcCollisionId() != collision.mcCollisionId()) { + continue; + } - for (auto const& track : tracks) { - auto const fullTrack = track.track_as>(); + histos.fill(HIST("mc/plain/h5_ptTrigPtAssocDCAzZPvMult_photonPCM"), + collision.ptMax(), photonPCM.pt(), photonPCM.v0PhotonKF().dcaZtopv(), collision.posZ(), collision.nGlobalTracks()); + } + } + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processPCMDCAzTrue, "process to test true DCAz distribution of photons in trigger collisions", false); + + // last ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ - constexpr float Mincrossedrows = 40; - constexpr float Maxchi2tpc = 5.0; - constexpr float Maxchi2its = 6.0; - constexpr float MaxR = 83.1; - constexpr float MinPtTrackiu = 0.1; + void processRecoLast(CorrCollisions const& collisions, aod::Triggers const& triggers) + { + // do at end of each data frame (after other reco correlation process functions) + // (PROCESS_SWITCH of this process has to be declared last) - if (!fullTrack.hasITS() && !fullTrack.hasTPC()) + for (auto const& collision : collisions) { + // all events + histos.fill(HIST("reco/info/h1_nEvents"), 0.5); + + // mc split + if (!checkSplitMcEventSelection(collision)) { continue; - if (fullTrack.x() * fullTrack.x() + fullTrack.y() * fullTrack.y() > MaxR * MaxR || fullTrack.pt() < MinPtTrackiu) + } + histos.fill(HIST("reco/info/h1_nEvents"), 1.5); + + // standard event selection + if (!collision.selEv()) { continue; - if (fullTrack.hasTPC()) { - if (fullTrack.tpcNClsCrossedRows() < Mincrossedrows || fullTrack.tpcChi2NCl() > Maxchi2tpc) - continue; } - if (fullTrack.hasITS()) { - if (fullTrack.itsChi2NCl() > Maxchi2its) - continue; + histos.fill(HIST("reco/info/h1_nEvents"), 2.5); + + histos.fill(HIST("reco/info/h3_ptTrigZPvMult"), collision.ptMax(), collision.posZ(), collision.nGlobalTracks()); + histos.fill(HIST("reco/info/h2_ptTrigOccupancy"), collision.ptMax(), collision.trackOccupancyInTimeRange()); + + // group collision + auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); + + // trigger loop + for (auto const& trigger : triggersThisEvent) { + // trigger info + histos.fill(HIST("reco/corr/h3_ptPhiEta_trig"), trigger.pt(), trigger.phi(), trigger.eta(), + getPtCorrection(trigger.pt())); + + // save trigger for mixing + triggerMemoryReco->saveTrigger(trigger.pt(), trigger.phi(), trigger.eta(), collision.posZ(), collision.nGlobalTracks()); + } + } + } + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processRecoLast, "process general info for reconstructed collisions and later correlations last", true); + + void processMcLast(CorrMcDCollisions const& collisions, aod::Triggers const& triggers, aod::JetTracksMCD const&, + CorrMcCollisions const& mcCollisions, aod::TriggerParticles const& triggerParticles, aod::JetParticles const&) + { + for (auto const& mcCollision : mcCollisions) { + // event selection + if (!totalEvSel(mcCollision)) { + continue; } - histos.fill(HIST("test/h2_tracks_zPvMultDep"), collision.posZ(), collision.nGlobalTracks()); + // group collision + auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, mcCollision.globalIndex()); + + histos.fill(HIST("mc/info/h3_ptTrigZPvMult_true"), mcCollision.ptMax(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + + // trigger loop + for (auto const& trigger : triggerParticlesThisEvent) { + // trigger info + histos.fill(HIST("mc/corr/h3_ptPhiEta_trig_true"), trigger.pt(), trigger.phi(), trigger.eta()); + + // save trigger for mixing + triggerMemoryTrue->saveTrigger(trigger.pt(), trigger.phi(), trigger.eta(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + } } + for (auto const& collision : collisions) { + // event selection + if (!totalEvSel(collision)) { + continue; + } + + auto const& mcCollision = collision.mcCollision_as(); - histos.fill(HIST("test/h2_globalTracks_zPvMultDep"), collision.posZ(), collision.nGlobalTracks(), hadrons.size()); + // group collision + auto const triggerParticlesThisEvent = triggerParticles.sliceBy(perColTriggerParticles, mcCollision.globalIndex()); + auto const triggersThisEvent = triggers.sliceBy(perColTriggers, collision.globalIndex()); + + histos.fill(HIST("mc/info/h3_ptTrigZPvMult_trueAssocEv"), mcCollision.ptMax(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + histos.fill(HIST("mc/info/h3_ptTrigZPvMult_trueAssocEvRecoPtTrig"), collision.ptMax(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + + // trigger loops + for (auto const& triggerParticle : triggerParticlesThisEvent) { + // trigger info + histos.fill(HIST("mc/corr/h3_ptPhiEta_trig_trueAssocEv"), triggerParticle.pt(), triggerParticle.phi(), triggerParticle.eta()); + + // save trigger for mixing + triggerMemoryTrueAssocEv->saveTrigger(triggerParticle.pt(), triggerParticle.phi(), triggerParticle.eta(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + } + for (auto const& trigger : triggersThisEvent) { + if (!trigger.jetTrack_as().has_mcParticle()) { + continue; + } + + auto const& triggerParticle = trigger.jetTrack_as().mcParticle(); + + // trigger info + histos.fill(HIST("mc/corr/h3_ptPhiEta_trig_trueAssocEvRecoPtTrig"), trigger.pt(), triggerParticle.phi(), triggerParticle.eta()); + + // save trigger for mixing + triggerMemoryTrueAssocEvRecoPtTrig->saveTrigger(trigger.pt(), triggerParticle.phi(), triggerParticle.eta(), mcCollision.posZ(), mcCollision.nChargedInEtaRange()); + } + } } - PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processTest, "process just to test things", false); + PROCESS_SWITCH(PhotonChargedTriggerCorrelation, processMcLast, "process general MC info for collisions and later correlations last", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& configContext) diff --git a/PWGJE/Tasks/photonChargedTriggerProducer.cxx b/PWGJE/Tasks/photonChargedTriggerProducer.cxx index 4ffc372ad67..9e16a69e1cc 100644 --- a/PWGJE/Tasks/photonChargedTriggerProducer.cxx +++ b/PWGJE/Tasks/photonChargedTriggerProducer.cxx @@ -26,20 +26,26 @@ #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" +#include #include #include #include #include #include +#include +#include #include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include +#include #include +#include #include using namespace o2; @@ -47,24 +53,36 @@ using namespace o2::framework; // correlation derived data =================================================================================================================================================================== +template +concept IsJetMcCollision = std::same_as, aod::JetMcCollision>; +template +concept IsJetCollisionMCD = std::same_as, aod::JetCollisionMCD>; +template +concept HasTrueCollision = IsJetMcCollision || IsJetCollisionMCD; + struct PhotonChargedTriggerProducer { - // reco - Produces collisionExtraCorrTable; - Produces triggerTable; - Produces hadronTable; - Produces pipmTable; - Produces photonPCMTable; - Produces photonPCMPairTable; - // mc - Produces mcCollisionExtraCorrTable; - Produces triggerParticleTable; + struct : ProducesGroup { + // reco + Produces collisionExtraCorr; + Produces trigger; + Produces hadron; + Produces pipm; + Produces photonPCM; + Produces photonPCMPair; + // mc + Produces mcCollisionExtraCorr; + Produces triggerParticle; + } tableProduction; Configurable zPvMax{"zPvMax", 7, "maximum absZ primary-vertex cut"}; Configurable occupancyMin{"occupancyMin", 0, "minimum occupancy cut"}; Configurable occupancyMax{"occupancyMax", 2000, "maximum occupancy cut"}; - Configurable etaMax{"etaMax", 0.8, "maximum absEta cut"}; + Configurable absEtaMax{"absEtaMax", 0.8, "maximum absEta cut"}; Configurable eventSelections{"eventSelections", "sel8", "JE framework - event selection"}; + Configurable skipMBGapEvents{"skipMBGapEvents", true, "skip MB gap events"}; + Configurable applyRctSelection{"applyRctSelection", true, "apply RCT selection"}; + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT selection label"}; Configurable trackSelections{"trackSelections", "globalTracks", "JE framework - track selections"}; Configurable triggerMasks{"triggerMasks", "", "JE framework - skimmed data trigger masks (relevent for correlation: fTrackLowPt,fTrackHighPt)"}; @@ -75,7 +93,10 @@ struct PhotonChargedTriggerProducer { Configurable> nSigmaPiTof{"nSigmaPiTof", {-1, 2}, "minimum-maximum nSigma for pipm in tof"}; Configurable> nSigmaPiRelRise{"nSigmaPiRelRise", {0, 2}, "minimum-maximum nSigma pipm tpc at high pt"}; + Configurable> pcmAbsDcaZLim{"pcmAbsDcaZLim", {0, 5}, "absolute values of DCAz limits for PCM"}; + Configurable ptTrigMin{"ptTrigMin", 5, "minimum pT of triggers"}; + Configurable oneTriggerPerEvent{"oneTriggerPerEvent", false, "use only one trigger (of highest pt) in an event"}; // derivatives of configurables @@ -83,8 +104,10 @@ struct PhotonChargedTriggerProducer { int trackSelection = -1; std::vector triggerMaskBits; + HistogramRegistry histos{"histogramRegistry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false}; + // for mc - Service pdg; + Service pdg{}; // partitions++ SliceCache cache; @@ -102,25 +125,46 @@ struct PhotonChargedTriggerProducer { template bool checkEventSelection(T_collision const& collision) { - if (!jetderiveddatautilities::selectTrigger(collision, triggerMaskBits)) + if (!jetderiveddatautilities::selectTrigger(collision, triggerMaskBits)) { return false; - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) + } + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRctSelection, rctLabel)) { return false; - if (std::abs(collision.posZ()) > zPvMax) + } + if (std::abs(collision.posZ()) > zPvMax) { return false; - if (collision.trackOccupancyInTimeRange() < occupancyMin || collision.trackOccupancyInTimeRange() > occupancyMax) + } + if (collision.trackOccupancyInTimeRange() < occupancyMin || collision.trackOccupancyInTimeRange() > occupancyMax) { return false; + } return true; } + // mc split event selection based in the thrid decimal of mc-true posZ + template + int getThirdDecimalTruePosZ(T_collision const& collision) + { + // get third decimal + double absPosZ = -1; + if constexpr (IsJetCollisionMCD) { + absPosZ = std::abs(collision.template mcCollision_as().posZ()); + } else { + absPosZ = std::abs(collision.posZ()); + } + int const thirdDecimalPosZ = static_cast(1000 * absPosZ) % 10; + return thirdDecimalPosZ; + } + // checks global track cuts template bool checkGlobalTrackEta(T_track const& track) { - if (!jetderiveddatautilities::selectTrack(track, trackSelection)) + if (!jetderiveddatautilities::selectTrack(track, trackSelection)) { return false; - if (!jetderiveddatautilities::applyTrackKinematics(track, 0.1, 1000, -1 * etaMax, etaMax)) + } + if (!jetderiveddatautilities::applyTrackKinematics(track, 0.1, 1000, -1 * absEtaMax, absEtaMax)) { return false; + } return true; } @@ -130,10 +174,7 @@ struct PhotonChargedTriggerProducer { { // too low for tof if (track.pt() < piPIDLowPt) { - if (track.tpcNSigmaPi() > nSigmaPiTpcLowPt.value[0] && track.tpcNSigmaPi() < nSigmaPiTpcLowPt.value[1]) { - return true; - } - return false; + return (track.tpcNSigmaPi() > nSigmaPiTpcLowPt.value[0] && track.tpcNSigmaPi() < nSigmaPiTpcLowPt.value[1]); } // Bethe-Bloch overlap (-> tpc + tof) if (track.pt() < piPIDHighPt) { @@ -146,10 +187,7 @@ struct PhotonChargedTriggerProducer { return false; } // Bethe-Bloch rel rise (too high for tof) - if (track.tpcNSigmaPi() > nSigmaPiRelRise.value[0] && track.tpcNSigmaPi() < nSigmaPiRelRise.value[1]) { - return true; - } - return false; + return (track.tpcNSigmaPi() > nSigmaPiRelRise.value[0] && track.tpcNSigmaPi() < nSigmaPiRelRise.value[1]); } // checks pipm selection (just PID (no additional track cuts)) @@ -169,67 +207,132 @@ struct PhotonChargedTriggerProducer { void init(InitContext const&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(triggerMasks); + + // histograms + AxisSpec const axisZPv{28, -7, 7, "#it{z}_{pv}"}; + AxisSpec const axisMult{65, -0.5, 64.5, "multiplicity"}; + AxisSpec const axisHadronicRate{1001, -0.5, 1000.5, "hadronicRate"}; + AxisSpec const axisPt{50, 0, 25, "#it{p}_{T}"}; + AxisSpec const axisPhi{72, 0, constants::math::TwoPI, "#varphi"}; + AxisSpec const axisEta{32, -absEtaMax, absEtaMax, "#eta"}; + // reco + histos.add("reco/h2_zPvMult", "h2_zPvMult", kTHnSparseD, {axisZPv, axisMult}, true); + histos.add("reco/h1_hadronicRate", "h1_hadronicRate", kTH1D, {axisHadronicRate}, true); + histos.add("reco/h3_ptPhiEta_trigger", "h3_ptPhiEta_trigger", kTHnSparseD, {axisPt, axisPhi, axisEta}, true); + histos.add("reco/h3_ptPhiEta_hadron", "h3_ptPhiEta_hadron", kTHnSparseD, {axisPt, axisPhi, axisEta}, true); + histos.add("reco/h3_ptPhiEta_pipm", "h3_ptPhiEta_pipm", kTHnSparseD, {axisPt, axisPhi, axisEta}, true); + histos.add("reco/h3_ptPhiEta_photonPCM", "h3_ptPhiEta_photonPCM", kTHnSparseD, {axisPt, axisPhi, axisEta}, true); + histos.add("reco/h3_ptPhiEta_photonPCMPair", "h3_ptPhiEta_photonPCMPair", kTHnSparseD, {axisPt, axisPhi, axisEta}, true); + // true + histos.add("true/h2_zPvMult", "h2_zPvMult", kTHnSparseD, {axisZPv, axisMult}, true); + histos.add("true/h3_ptPhiEta_trigger", "h3_ptPhiEta_trigger", kTHnSparseD, {axisPt, axisPhi, axisEta}, true); } - void processRecoCollisionTrigger(aod::JetCollision const& collision, aod::JetTracks const& tracks) + template + void fillRecoCollisionTrigger(T_collision const& collision, T_tracks const& tracks) { // event selection - const bool isSelectedEvent = checkEventSelection(collision); - // trigger event check - bool isTriggerEvent = false; + bool isSelectedEvent = checkEventSelection(collision); + // mc split event selection + // default -1 for data reconstruction + int thirdDecimalTruePosZ = -1; + if constexpr (IsJetCollisionMCD) { + thirdDecimalTruePosZ = getThirdDecimalTruePosZ(collision); + } + // maximum track pt + float maxTrackPt = -1; + int maxTriggerCollisionId = -1; + int maxTriggerGlobalIndex = -1; + float maxTriggerPhi = 0; + float maxTriggerEta = 0; // number global tracks int nGlobalTracks = 0; // count global tracks (for independence of multiplicity task (uses only JE derieved data)) for (auto const& track : tracks) { // track selection - if (!checkGlobalTrackEta(track)) + if (!checkGlobalTrackEta(track)) { continue; + } nGlobalTracks++; + if (track.pt() > maxTrackPt) { + maxTrackPt = track.pt(); + maxTriggerCollisionId = track.collisionId(); + maxTriggerGlobalIndex = track.globalIndex(); + maxTriggerPhi = track.phi(); + maxTriggerEta = track.eta(); + } - if (!isSelectedEvent) + if (!isSelectedEvent) { continue; - if (track.pt() < ptTrigMin) + } + if (track.pt() < ptTrigMin) { continue; - - isTriggerEvent = true; + } + if (oneTriggerPerEvent) { + continue; + } // trigger info - triggerTable(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); + histos.fill(HIST("reco/h3_ptPhiEta_trigger"), track.pt(), track.phi(), track.eta()); + tableProduction.trigger(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); + } + bool const isTriggerEvent = maxTrackPt > ptTrigMin; + if (oneTriggerPerEvent && isSelectedEvent && isTriggerEvent) { + histos.fill(HIST("reco/h3_ptPhiEta_trigger"), maxTrackPt, maxTriggerPhi, maxTriggerEta); + tableProduction.trigger(maxTriggerCollisionId, maxTriggerGlobalIndex, maxTrackPt, maxTriggerPhi, maxTriggerEta); } // collision info - collisionExtraCorrTable(isSelectedEvent, isTriggerEvent, nGlobalTracks); + histos.fill(HIST("reco/h2_zPvMult"), collision.posZ(), nGlobalTracks); + histos.fill(HIST("reco/h1_hadronicRate"), collision.hadronicRate()); + tableProduction.collisionExtraCorr(isSelectedEvent, thirdDecimalTruePosZ, isTriggerEvent, maxTrackPt, nGlobalTracks); + } + + void processRecoCollisionTrigger(aod::JetCollision const& collision, aod::JetTracks const& tracks) + { + fillRecoCollisionTrigger(collision, tracks); } PROCESS_SWITCH(PhotonChargedTriggerProducer, processRecoCollisionTrigger, "process correlation collision_extra and trigger table (reconstructed)", false); + void processMcRecoCollisionTrigger(aod::JetCollisionMCD const& collision, aod::JetTracks const& tracks, aod::JetMcCollisions const&) + { + fillRecoCollisionTrigger(collision, tracks); + } + PROCESS_SWITCH(PhotonChargedTriggerProducer, processMcRecoCollisionTrigger, "process correlation collision_extra and trigger table (reconstructed MC)", false); + void processRecoPipmTPCTOF(aod::JetCollision const& collision, soa::Join const& tracks, soa::Join const&) { // event selection - if (!checkEventSelection(collision)) + if (!checkEventSelection(collision)) { return; + } // hadron/pipm for (auto const& track : tracks) { // track selection - if (!checkGlobalTrackEta(track)) + if (!checkGlobalTrackEta(track)) { continue; + } // hadron - hadronTable(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); + histos.fill(HIST("reco/h3_ptPhiEta_hadron"), track.pt(), track.phi(), track.eta()); + tableProduction.hadron(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); // pipm selection auto const& trackPID = track.track_as>(); - if (!checkPipmTPCTOF(trackPID)) + if (!checkPipmTPCTOF(trackPID)) { continue; + } // pipm - pipmTable(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); + histos.fill(HIST("reco/h3_ptPhiEta_pipm"), track.pt(), track.phi(), track.eta()); + tableProduction.pipm(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); } } PROCESS_SWITCH(PhotonChargedTriggerProducer, processRecoPipmTPCTOF, "process pipm (TPC-TOF) table (reconstructed)", false); @@ -238,25 +341,30 @@ struct PhotonChargedTriggerProducer { soa::Join const& tracks, soa::Join const&) { // event selection - if (!checkEventSelection(collision)) + if (!checkEventSelection(collision)) { return; + } // hadron/pipm for (auto const& track : tracks) { // track selection - if (!checkGlobalTrackEta(track)) + if (!checkGlobalTrackEta(track)) { continue; + } // hadron - hadronTable(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); + histos.fill(HIST("reco/h3_ptPhiEta_hadron"), track.pt(), track.phi(), track.eta()); + tableProduction.hadron(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); // pipm selection auto const& trackPID = track.track_as>(); - if (!checkPipmTPC(trackPID)) + if (!checkPipmTPC(trackPID)) { continue; + } // pipm - pipmTable(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); + histos.fill(HIST("reco/h3_ptPhiEta_pipm"), track.pt(), track.phi(), track.eta()); + tableProduction.pipm(track.collisionId(), track.globalIndex(), track.pt(), track.phi(), track.eta()); } } PROCESS_SWITCH(PhotonChargedTriggerProducer, processRecoPipmTPC, "process pipm (TPC) table (reconstructed)", false); @@ -265,8 +373,9 @@ struct PhotonChargedTriggerProducer { aod::V0PhotonsKF const& v0Photons, aod::V0Legs const&) { // event selection - if (!checkEventSelection(collision)) + if (!checkEventSelection(collision)) { return; + } // photonsPCM (for some reason collsionId not an index column (?)) auto const v0PhotonsThisEvent = v0Photons.sliceBy(perColV0Photons, collision.collisionId()); @@ -274,12 +383,17 @@ struct PhotonChargedTriggerProducer { // photonPCM for (auto const& v0Photon : v0PhotonsThisEvent) { // photon selection - if (std::abs(v0Photon.eta()) > etaMax) + if (std::abs(v0Photon.eta()) > absEtaMax) { + continue; + } + if (std::abs(v0Photon.dcaZtopv()) < pcmAbsDcaZLim.value[0] || std::abs(v0Photon.dcaZtopv()) > pcmAbsDcaZLim.value[1]) { continue; + } // photon PCM - photonPCMTable(v0Photon.collisionId(), v0Photon.globalIndex(), - v0Photon.posTrack().trackId(), v0Photon.negTrack().trackId(), v0Photon.pt(), v0Photon.phi(), v0Photon.eta()); + histos.fill(HIST("reco/h3_ptPhiEta_photonPCM"), v0Photon.pt(), v0Photon.phi(), v0Photon.eta()); + tableProduction.photonPCM(v0Photon.collisionId(), v0Photon.globalIndex(), + v0Photon.posTrack().trackId(), v0Photon.negTrack().trackId(), v0Photon.pt(), v0Photon.phi(), v0Photon.eta()); } // photonPCm pairs @@ -290,21 +404,29 @@ struct PhotonChargedTriggerProducer { ROOT::Math::PtEtaPhiMVector const p4V0PCMPair = p4V0PCM1 + p4V0PCM2; // pi0 selection - if (std::abs(p4V0PCMPair.Eta()) > etaMax) + if (std::abs(p4V0PCMPair.Eta()) > absEtaMax) { continue; + } // save info - photonPCMPairTable(v0Photon1.collisionId(), v0Photon1.globalIndex(), v0Photon2.globalIndex(), - v0Photon1.posTrack().trackId(), v0Photon1.negTrack().trackId(), v0Photon2.posTrack().trackId(), v0Photon2.negTrack().trackId(), - p4V0PCMPair.Pt(), RecoDecay::constrainAngle(p4V0PCMPair.Phi(), 0), p4V0PCMPair.Eta(), p4V0PCMPair.M()); + histos.fill(HIST("reco/h3_ptPhiEta_photonPCMPair"), p4V0PCMPair.Pt(), RecoDecay::constrainAngle(p4V0PCMPair.Phi(), 0), p4V0PCMPair.Eta()); + tableProduction.photonPCMPair(v0Photon1.collisionId(), v0Photon1.globalIndex(), v0Photon2.globalIndex(), + v0Photon1.posTrack().trackId(), v0Photon1.negTrack().trackId(), v0Photon2.posTrack().trackId(), v0Photon2.negTrack().trackId(), + p4V0PCMPair.Pt(), RecoDecay::constrainAngle(p4V0PCMPair.Phi(), 0), p4V0PCMPair.Eta(), p4V0PCMPair.M()); } } PROCESS_SWITCH(PhotonChargedTriggerProducer, processRecoPhotonPCM, "process photonPCM table (reconstructed)", false); - void processMcCorrTables(aod::JetMcCollision const&, aod::JetParticles const& mcParticles) + void processMcCorrTables(aod::JetMcCollision const& mcCollision, aod::JetParticles const& mcParticles) { - // trigger event check - bool isTriggerEvent = false; + // mc split event selection + int const thirdDecimalTruePosZ = getThirdDecimalTruePosZ(mcCollision); + // maximum charged particle pt + float maxChargedPt = -1; + int maxTriggerCollisionId = -1; + int maxTriggerGlobalIndex = -1; + float maxTriggerPhi = 0; + float maxTriggerEta = 0; // number charged particles in eta range int nCharged = 0; @@ -312,27 +434,46 @@ struct PhotonChargedTriggerProducer { for (auto const& mcParticle : mcParticles) { // track selection auto const pdgParticle = pdg->GetParticle(mcParticle.pdgCode()); - if (!pdgParticle || pdgParticle->Charge() == 0) + if (!pdgParticle || pdgParticle->Charge() == 0) { continue; - if (!mcParticle.isPhysicalPrimary()) + } + if (!mcParticle.isPhysicalPrimary()) { continue; - if (std::abs(mcParticle.eta()) > etaMax) + } + if (std::abs(mcParticle.eta()) > absEtaMax) { continue; + } nCharged++; + if (mcParticle.pt() > maxChargedPt) { + maxChargedPt = mcParticle.pt(); + maxTriggerCollisionId = mcParticle.mcCollisionId(); + maxTriggerGlobalIndex = mcParticle.globalIndex(); + maxTriggerPhi = mcParticle.phi(); + maxTriggerEta = mcParticle.eta(); + } // trigger selection - if (mcParticle.pt() < ptTrigMin) + if (mcParticle.pt() < ptTrigMin) { continue; - - isTriggerEvent = true; + } + if (oneTriggerPerEvent) { + continue; + } // trigger info - triggerParticleTable(mcParticle.mcCollisionId(), mcParticle.globalIndex(), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); + histos.fill(HIST("true/h3_ptPhiEta_trigger"), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); + tableProduction.triggerParticle(mcParticle.mcCollisionId(), mcParticle.globalIndex(), mcParticle.pt(), mcParticle.phi(), mcParticle.eta()); + } + bool const isTriggerEvent = maxChargedPt > ptTrigMin; + if (oneTriggerPerEvent && isTriggerEvent) { + histos.fill(HIST("true/h3_ptPhiEta_trigger"), maxChargedPt, maxTriggerPhi, maxTriggerEta); + tableProduction.triggerParticle(maxTriggerCollisionId, maxTriggerGlobalIndex, maxChargedPt, maxTriggerPhi, maxTriggerEta); } // collision info - mcCollisionExtraCorrTable(isTriggerEvent, nCharged); + histos.fill(HIST("true/h2_zPvMult"), mcCollision.posZ(), nCharged); + tableProduction.mcCollisionExtraCorr(thirdDecimalTruePosZ, isTriggerEvent, maxChargedPt, nCharged); } PROCESS_SWITCH(PhotonChargedTriggerProducer, processMcCorrTables, "process table production (mc)", false); }; diff --git a/PWGJE/Tasks/photonIsolationQA.cxx b/PWGJE/Tasks/photonIsolationQA.cxx index 4f31995b218..fe822de10d1 100644 --- a/PWGJE/Tasks/photonIsolationQA.cxx +++ b/PWGJE/Tasks/photonIsolationQA.cxx @@ -104,8 +104,8 @@ struct PhotonIsolationQA { int trackSelection = -1; void init(o2::framework::InitContext&) { - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); mGeometry = o2::emcal::Geometry::GetInstanceFromRunNumber(300000); auto nCells = mGeometry->GetNCells(); @@ -269,7 +269,7 @@ struct PhotonIsolationQA { return Pt_Iso; } - void fillclusterhistos(const auto cluster, HistogramRegistry registry, double weight = 1.0) + void fillclusterhistos(const auto& cluster, HistogramRegistry registry, double weight = 1.0) { registry.fill(HIST("hClusterLocation"), cluster.eta(), cluster.phi()); if (isMC == true) { @@ -397,7 +397,7 @@ struct PhotonIsolationQA { // process monte carlo data void processMC(aod::BCs const& bcs, selectedMcCollisions const& Collisions, selectedMCClusters const& mcclusters, aod::McParticles const&, myGlobTracks const& tracks, o2::aod::EMCALMatchedTracks const& matchedtracks, aod::Calos const&, aod::EMCALClusterCells const& ClusterCells) { - for (auto bc : bcs) { + for (const auto& bc : bcs) { auto collisionsInBC = Collisions.sliceBy(McCollisionsPerBC, bc.globalIndex()); MC_Info.fill(HIST("hCollperBC"), collisionsInBC.size()); if (collisionsInBC.size() == 1) { @@ -461,7 +461,7 @@ struct PhotonIsolationQA { void processData(aod::BCs const& bcs, selectedCollisions const& Collisions, selectedClusters const& clusters, o2::aod::EMCALMatchedTracks const& matchedtracks, myGlobTracks const& tracks, aod::Calos const&, aod::EMCALClusterCells const& ClusterCells) { - for (auto bc : bcs) { + for (const auto& bc : bcs) { auto collisionsInBC = Collisions.sliceBy(collisionsPerBC, bc.globalIndex()); Data_Info.fill(HIST("hCollperBC"), collisionsInBC.size()); if (collisionsInBC.size() == 1) { diff --git a/PWGJE/Tasks/recoilJets.cxx b/PWGJE/Tasks/recoilJets.cxx index bb141c84a86..67f0c004f94 100644 --- a/PWGJE/Tasks/recoilJets.cxx +++ b/PWGJE/Tasks/recoilJets.cxx @@ -17,6 +17,7 @@ #include "PWGJE/DataModel/Jet.h" #include "PWGJE/DataModel/JetReducedData.h" #include "PWGJE/DataModel/JetSubtraction.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" @@ -69,7 +70,9 @@ using CollRhoDetIt = soa::Filtered::iterator; using CollRhoPartTbl = soa::Join; using CollRhoPartIt = soa::Filtered>::iterator; +using CollCentRhoPartIt = soa::Filtered>::iterator; using CollRhoOutlierPartIt = soa::Filtered>::iterator; +using CollCentRhoOutlierPartIt = soa::Filtered>::iterator; using CollRhoOutlierPartTbl = soa::Join; // --- Event multiplicity (+ ZDC etc.) @@ -83,13 +86,13 @@ using TrackMCLbsTbl = soa::Filtered; using PartTbl = soa::Filtered; // --- Jets (with constituents) -using JetsDataTbl = soa::Filtered>; -using JetsDetTbl = soa::Filtered>; -using JetsPartTbl = soa::Filtered>; +using ChJetsDataTbl = soa::Filtered>; +using ChJetsDetTbl = soa::Filtered>; +using ChJetsPartTbl = soa::Filtered>; // --- Matched jets (det <-> part) -using MatchedJetsDetToPartTbl = soa::Filtered>; -using MatchedJetsPartToDetTbl = soa::Filtered>; +using MatchedChJetsDetToPartTbl = soa::Filtered>; +using MatchedChJetsPartToDetTbl = soa::Filtered>; // --- O2 collisions event selection (not JCollisions) using CollEvSelExtendedIt = soa::Filtered>::iterator; @@ -102,24 +105,23 @@ struct RecoilJets { // ---------- Event selection ---------- struct EvCfg : ConfigurableGroup { std::string prefix = "event"; - Configurable sel{"sel", "sel8", "Choose event selection"}, + Configurable sel{"sel", "sel8+IsGoodZvtxFT0vsPV+NoSameBunchPileup", "Choose event selection"}, triggerMasks{"triggerMasks", "", "Relevant trigger masks: fTrackLowPt,fTrackHighPt"}; Configurable vertexZCut{"vertexZCut", 10., "Accepted z-vertex range"}; - Configurable isMCJJProd{"isMCJJProd", false, "Flag to select MC production: MB (false) or JJ(true)"}, - skipMBGapEvents{"skipMBGapEvents", false, - "Flag to choose to reject min. bias gap events; jet-level rejection " - "applied at the jet finder level, here rejection is applied for " - "collision and track process functions"}; + Configurable skipMBGapEvents{"skipMBGapEvents", true, + "Flag to choose to reject min. bias gap events; jet-level rejection " + "applied at the jet finder level, here rejection is applied for " + "collision and track process functions"}; } ev; // ---------- RCT / flag-based selections ---------- struct Rct : ConfigurableGroup { std::string prefix = "rct"; - Configurable label{"label", "CBT_hadronPID", "Apply rct flag"}; // CBT + Configurable label{"label", "CBT_hadronPID", "Apply rct flag"}; Configurable enable{"enable", true, "Apply RCT selections"}, - requireZDC{"requireZDC", false, "Require ZDC flag"}, + requireZDC{"requireZDC", true, "Require ZDC flag"}, rejectLimitedAcceptance{"rejectLimitedAcceptance", false, "Reject LimitedAcceptance flag"}; } rct; @@ -134,11 +136,11 @@ struct RecoilJets { } trk; // ---------- Jet reconstruction ---------- - struct Jet : ConfigurableGroup { + struct Jets : ConfigurableGroup { std::string prefix = "jet"; Configurable constituentPtMax{"constituentPtMax", 100., "Remove jets with constituent above this pT cut"}, radius{"radius", 0.4, "Jet cone radius"}; - } jet; + } jets; // ---------- Background tools ---------- struct Bkgd : ConfigurableGroup { @@ -153,15 +155,15 @@ struct RecoilJets { // ---------- Normalization FT0 by means ---------- struct FT0A : ConfigurableGroup { std::string prefix = "ft0a"; - Configurable mean{"mean", -1., "Mean FT0A signal"}, - meanPartLevel{"meanPartLevel", -1., "Mean Nch (part level) within FT0A acceptance"}, + Configurable mean{"mean", 3499., "Mean FT0A signal"}, + meanPartLevel{"meanPartLevel", 40.7, "Mean Nch (part level) within FT0A acceptance"}, meanZeq{"meanZeq", -1., "Mean equalized FT0A signal"}; } ft0a; struct FT0C : ConfigurableGroup { std::string prefix = "ft0c"; - Configurable mean{"mean", -1., "Mean FT0C signal"}, - meanPartLevel{"meanPartLevel", -1., "Mean Nch (part level) within FT0C acceptance"}, + Configurable mean{"mean", 844.6, "Mean FT0C signal"}, + meanPartLevel{"meanPartLevel", 43.11, "Mean Nch (part level) within FT0C acceptance"}, meanZeq{"meanZeq", -1., "Mean equalized FT0C signal"}; } ft0c; @@ -171,8 +173,8 @@ struct RecoilJets { Configurable fracSig{"fracSig", 0.9, "Fraction of events used for signal TT"}; Configurable recoilRegion{"recoilRegion", 0.6, "Width of recoil acceptance"}; - Configurable> refPtRange{"refPtRange", {5., 7}, "Reference TT pT range [min,max] (GeV/c)"}, - sigPtRange{"sigPtRange", {20., 50}, "Signal TT pT range [min,max] (GeV/c)"}; + Configurable> refPtRange{"refPtRange", {5., 7.}, "Reference TT pT range [min,max] (GeV/c)"}, + sigPtRange{"sigPtRange", {10., 18.}, "Signal TT pT range [min,max] (GeV/c)"}; Configurable> phiRestr{"phiRestr", {0., 6.3}, "Phi restriction [min,max] (rad) for TT search"}; } tt; @@ -193,13 +195,33 @@ struct RecoilJets { multNBins{"multNBins", 600, "Number of bins for scaled FT0M multiplicity"}, zdcTimeNBins{"zdcTimeNBins", 240, "Number of bins for ZDC timing histograms"}; - ConfigurableAxis multFT0CThresh{"multFT0CThresh", {VARIABLE_WIDTH, 0.0, 0.133, 0.233, 0.367, 0.567, 0.767, 1.067, 1.4, 1.867, 2.5, 3.9, 5.4, 6.9, 20.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%, 0.1%, 0.01%"}; // default values for raw data - ConfigurableAxis multFT0CThreshPartLevel{"multFT0CThreshPartLevel", {VARIABLE_WIDTH, 0.0, 0.133, 0.233, 0.367, 0.567, 0.767, 1.067, 1.4, 1.867, 2.5, 3.9, 5.4, 6.9, 20.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%, 0.1%, 0.01%"}; + ConfigurableAxis multFT0CThresh{"multFT0CThresh", {VARIABLE_WIDTH, 0.0, 0.109421, 0.215022, 0.343214, 0.50515, 0.712277, 0.978245, 1.3189, 1.75744, 2.36001, 3.59696, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0CThreshPartLevel{"multFT0CThreshPartLevel", {VARIABLE_WIDTH, 0.0, 0.0570661, 0.13847, 0.280558, 0.457027, 0.692287, 0.983008, 1.34127, 1.80253, 2.41528, 3.62342, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; - ConfigurableAxis multFT0MThresh{"multFT0MThresh", {VARIABLE_WIDTH, 0.0, 0.167, 0.267, 0.4, 0.567, 0.8, 1.067, 1.4, 1.833, 2.433, 3.667, 5.1, 6.433, 20.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%, 0.1%, 0.01%"}; // default values for raw data - ConfigurableAxis multFT0MThreshPartLevel{"multFT0MThreshPartLevel", {VARIABLE_WIDTH, 0.0, 0.167, 0.267, 0.4, 0.567, 0.8, 1.067, 1.4, 1.833, 2.433, 3.667, 5.1, 6.433, 20.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%, 0.1%, 0.01%"}; + ConfigurableAxis multFT0MThresh{"multFT0MThresh", {VARIABLE_WIDTH, 0.0, 0.130987, 0.237916, 0.36664, 0.528441, 0.734705, 0.996611, 1.32785, 1.74896, 2.31024, 3.37852, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThreshPartLevel{"multFT0MThreshPartLevel", {VARIABLE_WIDTH, 0.0, 0.0682096, 0.160082, 0.302476, 0.480633, 0.699907, 0.986263, 1.34813, 1.80188, 2.39325, 3.49311, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + + ConfigurableAxis axisPtTrackEff{"axisPtTrackEff", {VARIABLE_WIDTH, 0.0, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 10.0, 12.0, 15.0, 20.0, 25.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "#it{p}_{T} (GeV/#it{c})"}; + + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, -5.0, 0.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 55.0, 60.0, 65.0, 70.0, 75.0, 80.0, 85.0, 90.0, 95.0, 100.0, 105.0}, "Centrality (%)"}; } hist; + // ---------- Rho-shift settings ---------- + struct RhoShift : ConfigurableGroup { + std::string prefix = "rhoShiftTTRef"; + + // Values from train 747883 + Configurable mb{"mb", 0.286292f, "Rho shift for MB"}; + Configurable ea0To20{"ea0To20", 0.189813f, "Rho shift for EA 0-20%"}; + Configurable ea0To10{"ea0To10", 0.180575f, "Rho shift for EA 0-10%"}; + Configurable ea20To40{"ea20To40", 0.131103f, "Rho shift for EA 20-40%"}; + Configurable ea60To80{"ea60To80", 0.092541f, "Rho shift for EA 60-80%"}; + Configurable ea50To100{"ea50To100", 0.106569f, "Rho shift for EA 50-100%"}; + Configurable ea80To100{"ea80To100", 0.0998433f, "Rho shift for EA 80-100%"}; + + Configurable mbPart{"mbPart", 0.0f, "Rho shift for MB part. level MC"}; + } cfgRhoShift; + // Auxiliary variables std::unique_ptr randGen = std::make_unique(0); @@ -213,8 +235,8 @@ struct RecoilJets { Filter partFilter = nabs(aod::jmcparticle::eta) < trk.etaCut.node(); // Declare filter on jets - Filter jetRadiusFilter = aod::jet::r == nround(jet.radius.node() * 100.); - Filter jetEtaFilter = nabs(aod::jet::eta) < trk.etaCut.node() - jet.radius.node(); // 0.5 in our analysis + Filter jetRadiusFilter = aod::jet::r == nround(jets.radius.node() * 100.); + Filter jetEtaFilter = nabs(aod::jet::eta) < trk.etaCut.node() - jets.radius.node(); // 0.5 in our analysis HistogramRegistry spectra; @@ -222,17 +244,18 @@ struct RecoilJets { int trackSelection = -1; std::vector triggerMaskBits; - Service pdg; - Preslice partJetsPerMcCollision = aod::jet::mcCollisionId; + Service pdg{}; + Preslice partJetsByMcCollision = aod::jet::mcCollisionId; + Preslice particlesByMcCollision = aod::jet::mcCollisionId; template struct AxisDesc { AxisDesc(const char* label_, const AxisObject& axis_, const std::string& axisName_ = "") : label(label_), axis(axis_), axisName(axisName_) {} - const char* label; // "FT0C" / "FT0M" - const AxisObject& axis; // AxisSpec or ConfigurableAxis - std::string axisName; // Empty for AxisSpec + const char* label; // "FT0C" / "FT0M" + AxisObject axis; // AxisSpec or ConfigurableAxis + std::string axisName; // Empty for AxisSpec }; struct FT0Metrics { @@ -244,11 +267,63 @@ struct RecoilJets { float scaledFT0M = 0.f; }; + // Indices of the FT0 percentile boundaries in the configurable axis + enum FT0PercentileEdge { + kEA100 = 0, + kEA90, + kEA80, + kEA70, + kEA60, + kEA50, + kEA40, + kEA30, + kEA20, + kEA10, + kEA1, + kEA0 + }; + + // EA intervals used for the rho-shift correction + enum EAInterval { + kMB = 0, + kEA0_20, + kEA0_10, + kEA20_40, + kEA60_80, + kEA50_100, + kEA80_100 + }; + + // Configuration of an EA interval used for the TTRef rho shift + struct EARhoShift { + EAInterval interval; + const char* label; + float rhoShift; + }; + + std::array eaRhoShifts{{{.interval = kMB, .label = "EA_MB", .rhoShift = 0.0f}, + {.interval = kEA0_20, .label = "EA_Perc_0_20", .rhoShift = 0.0f}, + {.interval = kEA0_10, .label = "EA_Perc_0_10", .rhoShift = 0.0f}, + {.interval = kEA20_40, .label = "EA_Perc_20_40", .rhoShift = 0.0f}, + {.interval = kEA60_80, .label = "EA_Perc_60_80", .rhoShift = 0.0f}, + {.interval = kEA50_100, .label = "EA_Perc_50_100", .rhoShift = 0.0f}, + {.interval = kEA80_100, .label = "EA_Perc_80_100", .rhoShift = 0.0f}}}; + void init(InitContext const&) { + // Initialize rho-shift values from configurables + eaRhoShifts[kMB].rhoShift = cfgRhoShift.mb.value; + eaRhoShifts[kEA0_20].rhoShift = cfgRhoShift.ea0To20.value; + eaRhoShifts[kEA0_10].rhoShift = cfgRhoShift.ea0To10.value; + eaRhoShifts[kEA20_40].rhoShift = cfgRhoShift.ea20To40.value; + eaRhoShifts[kEA60_80].rhoShift = cfgRhoShift.ea60To80.value; + eaRhoShifts[kEA50_100].rhoShift = cfgRhoShift.ea50To100.value; + eaRhoShifts[kEA80_100].rhoShift = cfgRhoShift.ea80To100.value; + // Initialize histogram axes: configurable AxisSpec pT{hist.jetPtMax, 0.0, hist.jetPtMax * 1., "#it{p}_{T} (GeV/#it{c})"}; AxisSpec jetPTcorr{hist.jetPtMax + 20, -20., hist.jetPtMax * 1.0, "#it{p}_{T, jet}^{ch, corr} (GeV/#it{c})"}; + AxisSpec jetPTcorrFinnerBin{4800, -20., 100., "#it{p}_{T, jet}^{ch, corr} (GeV/#it{c})"}; AxisSpec scaledFT0A{hist.multNBins, 0.0, 20., "FT0A / #LT FT0A #GT"}; AxisSpec scaledFT0C{hist.multNBins, 0.0, 20., "FT0C / #LT FT0C #GT"}; AxisSpec scaledFT0M{hist.multNBins, 0.0, 20., "FT0M^{*}"}; @@ -272,11 +347,13 @@ struct RecoilJets { AxisSpec pseudorap{40, -1., 1., "#it{#eta}"}; AxisSpec pseudorapJets{20, -0.5, 0.5, "#it{#eta}_{jet}"}; AxisSpec jetArea{50, 0.0, 5., "Area_{jet}"}; - AxisSpec rho{50, 0.0, 50., "#it{#rho}"}; + AxisSpec rho{100, 0.0, 50., "#it{#rho}"}; std::string nameFT0Caxis = "FT0C / #LT FT0C #GT"; std::string nameFT0Maxis = "FT0M^{*}"; + std::string nameCentralityAxis = "Centrality (%)"; + // FIT detector std::array, 2> arrAxisSpecScaledEA = {{{"FT0C", scaledFT0C}, {"FT0M", scaledFT0M}}}; @@ -284,11 +361,14 @@ struct RecoilJets { {"FT0C", unscaledFT0C}, {"FT0M", unscaledFT0M}}}; - std::array, 2> arrConfigurableAxis = {{{"FT0C", hist.multFT0CThresh, nameFT0Caxis}, - {"FT0M", hist.multFT0MThresh, nameFT0Maxis}}}; + std::array, 2> arrConfigurableAxisEA = {{{"FT0C", hist.multFT0CThresh, nameFT0Caxis}, + {"FT0M", hist.multFT0MThresh, nameFT0Maxis}}}; + + std::array, 2> arrConfigurableAxisEAPartLevel = {{{"FT0C", hist.multFT0CThreshPartLevel, nameFT0Caxis}, + {"FT0M", hist.multFT0MThreshPartLevel, nameFT0Maxis}}}; - std::array, 2> arrConfigurableAxisPartLevel = {{{"FT0C", hist.multFT0CThreshPartLevel, nameFT0Caxis}, - {"FT0M", hist.multFT0MThreshPartLevel, nameFT0Maxis}}}; + std::array, 2> arrConfigurableAxisCentrality = {{{"CentFT0C", hist.axisCentrality, nameCentralityAxis}, + {"CentFT0M", hist.axisCentrality, nameCentralityAxis}}}; // Zero-degree calorimeter std::array, 3> arrAxisSpecZDCNeutron = {{{"ZNA", zdcNeutronA}, @@ -312,26 +392,26 @@ struct RecoilJets { // List of raw and MC det. distributions if (doprocessData || doprocessMCDetLevel || doprocessMCDetLevelWeighted) { - spectra.add("hEventSelectionCountReco", "Count # of events in the analysis", kTH1F, {{5, 0.0, 5.}}); + spectra.add("hEventSelectionCountReco", "Count # of events", kTH1F, {{5, 0.0, 5.}}); spectra.get(HIST("hEventSelectionCountReco"))->GetXaxis()->SetBinLabel(1, "Total # of events"); spectra.get(HIST("hEventSelectionCountReco"))->GetXaxis()->SetBinLabel(2, Form("# of events after sel. %s", evSelToString.data())); spectra.get(HIST("hEventSelectionCountReco"))->GetXaxis()->SetBinLabel(3, "# of events w. outlier"); spectra.get(HIST("hEventSelectionCountReco"))->GetXaxis()->SetBinLabel(4, "# of events w/o assoc MC."); spectra.get(HIST("hEventSelectionCountReco"))->GetXaxis()->SetBinLabel(5, "# of selected events"); - spectra.add("hTrackSelectionCount", "Count # of tracks in the analysis", kTH1F, {{2, 0.0, 2.}}); + spectra.add("hTrackSelectionCount", "Count # of tracks", kTH1F, {{2, 0.0, 2.}}); spectra.get(HIST("hTrackSelectionCount"))->GetXaxis()->SetBinLabel(1, "Total # of tracks"); spectra.get(HIST("hTrackSelectionCount"))->GetXaxis()->SetBinLabel(2, Form("# of tracks after sel. %s", trkSelToString.data())); - spectra.add("hTTSig_pT", "pT spectrum of all found TT_{Sig} cand.", kTH1F, {{40, 10., 50.}}); // needed to distinguish merged data from diff. wagons + spectra.add("hTTSig_pT", "#it{p}_{T} spectrum of all found TT_{Sig}", kTH1F, {{40, 10., 50.}}); // needed to distinguish merged data from diff. wagons spectra.add("hJetPtEtaPhiRhoArea", "Charact. of inclusive jets", kTHnSparseF, {pT, pseudorapJets, phiAngle, rho, jetArea}, hist.sumw2); spectra.add("hJetArea_JetPt_Rho_TTRef", "Events w. TT_{Ref}: A_{jet} & jet pT & #rho", kTH3F, {jetArea, pT, rho}, hist.sumw2); spectra.add("hJetArea_JetPt_Rho_TTSig", "Events w. TT_{Sig}: A_{jet} & jet pT & #rho", kTH3F, {jetArea, pT, rho}, hist.sumw2); - for (const auto& eaAxis : arrConfigurableAxis) { + for (const auto& eaAxis : arrConfigurableAxisEA) { spectra.add(Form("hScaled%s_vertexZ", eaAxis.label), - "Z vertex of collisions", + Form("Scaled %s vs. Z vertex of collisions", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, {60, -12., 12., "#it{z}_{vertex}"}}, hist.sumw2); spectra.add(Form("hScaled%sTrackPtEtaPhi", eaAxis.label), @@ -339,17 +419,17 @@ struct RecoilJets { kTHnSparseF, {{eaAxis.axis, eaAxis.axisName}, pT, pseudorap, phiAngle}, hist.sumw2); auto tmpHistPointer = spectra.add(Form("hScaled%s_Ntrig", eaAxis.label), - Form("Total number of selected triggers per class vs scaled %s", eaAxis.label), - kTH2F, {{eaAxis.axis, eaAxis.axisName}, {2, 0.0, 2.}}); + Form("Scaled %s vs Total number of selected triggers per class", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, {2, 0.0, 2.}}, hist.sumw2); tmpHistPointer->GetYaxis()->SetBinLabel(1, "TT_{Ref}"); tmpHistPointer->GetYaxis()->SetBinLabel(2, "TT_{Sig}"); spectra.add(Form("hScaled%s_TTRef_per_event", eaAxis.label), - Form("Number of TT_{Ref} per event vs scaled %s", eaAxis.label), + Form("Scaled %s vs Number of TT_{Ref} per event", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, {15, 0.5, 15.5, "# of TT_{Ref}"}}); spectra.add(Form("hScaled%s_TTSig_per_event", eaAxis.label), - Form("Number of TT_{Sig} per event vs scaled %s", eaAxis.label), + Form("Scaled %s vs Number of TT_{Sig} per event", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, {10, 0.5, 10.5, "# of TT_{Sig}"}}); spectra.add(Form("hScaled%s_DPhi_JetPt_Corr_TTRef", eaAxis.label), @@ -370,11 +450,11 @@ struct RecoilJets { spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTRef", eaAxis.label), Form("Events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), - kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorr}, hist.sumw2); + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTSig", eaAxis.label), Form("Events w. TT_{Sig}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), - kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorr}, hist.sumw2); + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); spectra.add(Form("hScaled%s_Recoil_JetPt_TTRef", eaAxis.label), Form("Events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), @@ -396,6 +476,19 @@ struct RecoilJets { Form("Events w. TT_{Sig}: scaled %s & #rho", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, rho}, hist.sumw2); + // Rectricted phi range for TT selection + spectra.add(Form("hScaled%s_Ntrig_RestrictedPhi", eaAxis.label), + Form("Scaled %s vs Total number of selected triggers per class #in #varphi (%.2f, %.2f)", eaAxis.label, phiMin, phiMax), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, {2, 0.0, 2.}}, hist.sumw2); + + spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTRef_RestrictedPhi", eaAxis.label), + Form("Events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + + spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTSig_RestrictedPhi", eaAxis.label), + Form("Events w. TT_{Sig}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + spectra.add(Form("hScaled%s_DPhi_JetPt_Corr_TTRef_RestrictedPhi", eaAxis.label), Form("Events w. TT_{Ref} #in #varphi (%.2f, %.2f): scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", phiMin, phiMax, eaAxis.label), kTH3F, {{eaAxis.axis, eaAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); @@ -406,6 +499,10 @@ struct RecoilJets { } for (const auto& eaAxis : arrAxisSpecScaledEA) { + spectra.add(Form("hScaled%s", eaAxis.label), + Form("Scaled %s", eaAxis.label), + kTH1F, {eaAxis.axis}); + spectra.add(Form("hScaled%s_TTRef", eaAxis.label), Form("Events w. TT_{Ref}: scaled %s", eaAxis.label), kTH1F, {eaAxis.axis}); @@ -414,6 +511,88 @@ struct RecoilJets { Form("Events w. TT_{Sig}: scaled %s", eaAxis.label), kTH1F, {eaAxis.axis}); } + + // Centrality dependence + for (const auto& centAxis : arrConfigurableAxisCentrality) { + + // Vertex Z position + spectra.add(Form("h%s_vertexZ", centAxis.label), + Form("%s vs Z vertex of collisions", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, {60, -12., 12., "#it{z}_{vertex}"}}, hist.sumw2); + + // Distributions of centrality in events w/o and w. TT + spectra.add(Form("h%s", centAxis.label), + Form("%s", centAxis.label), + kTH1F, {{centAxis.axis, centAxis.axisName}}); + + spectra.add(Form("h%s_TTRef", centAxis.label), + Form("%s in events w. TT_{Ref}", centAxis.label), + kTH1F, {{centAxis.axis, centAxis.axisName}}); + + spectra.add(Form("h%s_TTSig", centAxis.label), + Form("%s in events w. TT_{Sig}", centAxis.label), + kTH1F, {{centAxis.axis}}); + + // Distribution of tracks + spectra.add(Form("h%sTrackPtEtaPhi", centAxis.label), + "Charact. of tracks", + kTHnSparseF, {{centAxis.axis, centAxis.axisName}, pT, pseudorap, phiAngle}, hist.sumw2); + + // Rho vs centrality + spectra.add(Form("h%s_Rho", centAxis.label), + Form("%s & #rho", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, rho}, hist.sumw2); + + spectra.add(Form("h%s_Rho_TTRef", centAxis.label), + Form("Events w. TT_{Ref}: %s & #rho", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, rho}, hist.sumw2); + + spectra.add(Form("h%s_Rho_TTSig", centAxis.label), + Form("Events w. TT_{Sig}: %s & #rho", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, rho}, hist.sumw2); + + // Recoil jets vs centrality + auto tmpHistPointer = spectra.add(Form("h%s_Ntrig", centAxis.label), + Form("%s vs Total number of selected triggers per class", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, {2, 0.0, 2.}}); + tmpHistPointer->GetYaxis()->SetBinLabel(1, "TT_{Ref}"); + tmpHistPointer->GetYaxis()->SetBinLabel(2, "TT_{Sig}"); + + spectra.add(Form("h%s_DPhi_JetPt_Corr_TTRef", centAxis.label), + Form("Events w. TT_{Ref}: %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); + + spectra.add(Form("h%s_DPhi_JetPt_Corr_TTSig", centAxis.label), + Form("Events w. TT_{Sig}: %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); + + spectra.add(Form("h%s_Recoil_JetPt_Corr_TTRef", centAxis.label), + Form("Events w. TT_{Ref}: %s & #it{p}_{T} of recoil jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + + spectra.add(Form("h%s_Recoil_JetPt_Corr_TTSig", centAxis.label), + Form("Events w. TT_{Sig}: %s & #it{p}_{T} of recoil jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + + spectra.add(Form("h%s_FT0MStar", centAxis.label), + Form("Correlation of %s vs. FT0M^{*}", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, scaledFT0M}, hist.sumw2); + } + + spectra.add("hCentFT0C_ScaledFT0C", + "Correlation of CentFT0C vs. scaled FT0C", + kTH2F, {{hist.axisCentrality, nameCentralityAxis}, scaledFT0C}, hist.sumw2); + + // Register TTRef recoil spectra with rho-shift correction. + for (const auto& ea : eaRhoShifts) { + spectra.add(Form("h%s_Recoil_JetPt_Corr_RhoShifted_TTRef", ea.label), + Form("%s: recoil jet p_{T} (#rho shifted)", ea.label), + kTH1F, {jetPTcorrFinnerBin}, hist.sumw2); + + spectra.add(Form("h%s_RhoShifted_TTRef", ea.label), + Form("%s: #rho shifted in events w. TT_{Ref}", ea.label), + kTH1F, {rho}, hist.sumw2); + } } // List of MC particle level distributions @@ -430,95 +609,183 @@ struct RecoilJets { spectra.add("hJetArea_JetPt_Rho_TTRef_Part", "Events w. TT_{Ref}: A_{jet} & jet pT & #rho", kTH3F, {jetArea, pT, rho}, hist.sumw2); spectra.add("hJetArea_JetPt_Rho_TTSig_Part", "Events w. TT_{Sig}: A_{jet} & jet pT & #rho", kTH3F, {jetArea, pT, rho}, hist.sumw2); - for (const auto& eaAxis : arrConfigurableAxisPartLevel) { - spectra.add(Form("hScaled%s_vertexZMC", eaAxis.label), + for (const auto& eaAxis : arrConfigurableAxisEAPartLevel) { + spectra.add(Form("hScaled%s_vertexZ_Part", eaAxis.label), "Z vertex of MC collision", kTH2F, {{eaAxis.axis, eaAxis.axisName}, {60, -12., 12., "#it{z}_{vertex}"}}, hist.sumw2); auto tmpHistPointer = spectra.add(Form("hScaled%s_Ntrig_Part", eaAxis.label), - Form("Total number of selected triggers per class vs scaled %s", eaAxis.label), + Form("Scaled %s vs Total number of selected triggers per class", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, {2, 0.0, 2.}}); tmpHistPointer->GetYaxis()->SetBinLabel(1, "TT_{Ref}"); tmpHistPointer->GetYaxis()->SetBinLabel(2, "TT_{Sig}"); - spectra.add(Form("hScaled%sPartPtEtaPhi", eaAxis.label), - "Charact. of particles", + spectra.add(Form("hScaled%sTrackPtEtaPhi_Part", eaAxis.label), + "Charact. of MC generated particles", kTHnSparseF, {{eaAxis.axis, eaAxis.axisName}, pT, pseudorap, phiAngle}, hist.sumw2); spectra.add(Form("hScaled%s_TTRef_per_event_Part", eaAxis.label), - Form("Number of TT_{Ref} per event vs scaled %s", eaAxis.label), + Form("Scaled %s vs Number of MC TT_{Ref} per event", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, {15, 0.5, 15.5, "# of TT_{Ref}"}}); spectra.add(Form("hScaled%s_TTSig_per_event_Part", eaAxis.label), - Form("Number of TT_{Sig} per event vs scaled %s", eaAxis.label), + Form("Scaled %s vs Number of MC TT_{Sig} per event", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, {10, 0.5, 10.5, "# of TT_{Sig}"}}); spectra.add(Form("hScaled%s_DPhi_JetPt_Corr_TTRef_Part", eaAxis.label), - Form("Events w. TT_{Ref}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), + Form("MC events w. TT_{Ref}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), kTH3F, {{eaAxis.axis, eaAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); spectra.add(Form("hScaled%s_DPhi_JetPt_Corr_TTSig_Part", eaAxis.label), - Form("Events w. TT_{Sig}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), + Form("MC events w. TT_{Sig}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), kTH3F, {{eaAxis.axis, eaAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); spectra.add(Form("hScaled%s_DPhi_JetPt_TTRef_Part", eaAxis.label), - Form("Events w. TT_{Ref}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), + Form("MC events w. TT_{Ref}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), kTH3F, {{eaAxis.axis, eaAxis.axisName}, deltaPhiAngle, pT}, hist.sumw2); spectra.add(Form("hScaled%s_DPhi_JetPt_TTSig_Part", eaAxis.label), - Form("Events w. TT_{Sig}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), + Form("MC events w. TT_{Sig}: scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", eaAxis.label), kTH3F, {{eaAxis.axis, eaAxis.axisName}, deltaPhiAngle, pT}, hist.sumw2); spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTRef_Part", eaAxis.label), - Form("Events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), - kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorr}, hist.sumw2); + Form("MC events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTSig_Part", eaAxis.label), - Form("Events w. TT_{Sig}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), - kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorr}, hist.sumw2); + Form("MC events w. TT_{Sig}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); spectra.add(Form("hScaled%s_Recoil_JetPt_TTRef_Part", eaAxis.label), - Form("Events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + Form("MC events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, pT}, hist.sumw2); spectra.add(Form("hScaled%s_Recoil_JetPt_TTSig_Part", eaAxis.label), - Form("Events w. TT_{Sig}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + Form("MC events w. TT_{Sig}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, pT}, hist.sumw2); spectra.add(Form("hScaled%s_Rho_Part", eaAxis.label), - Form("Scaled %s & #rho", eaAxis.label), + Form("MC events %s & #rho", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, rho}, hist.sumw2); spectra.add(Form("hScaled%s_Rho_TTRef_Part", eaAxis.label), - Form("Events w. TT_{Ref}: scaled %s & #rho", eaAxis.label), + Form("MC events w. TT_{Ref}: scaled %s & #rho", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, rho}, hist.sumw2); spectra.add(Form("hScaled%s_Rho_TTSig_Part", eaAxis.label), - Form("Events w. TT_{Sig}: scaled %s & #rho", eaAxis.label), + Form("MC events w. TT_{Sig}: scaled %s & #rho", eaAxis.label), kTH2F, {{eaAxis.axis, eaAxis.axisName}, rho}, hist.sumw2); // Rectricted phi range for TT selection + spectra.add(Form("hScaled%s_Ntrig_RestrictedPhi_Part", eaAxis.label), + Form("Scaled %s vs Total number of selected triggers per class #in #varphi (%.2f, %.2f)", eaAxis.label, phiMin, phiMax), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, {2, 0.0, 2.}}, hist.sumw2); + + spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTRef_RestrictedPhi_Part", eaAxis.label), + Form("Events w. TT_{Ref}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + + spectra.add(Form("hScaled%s_Recoil_JetPt_Corr_TTSig_RestrictedPhi_Part", eaAxis.label), + Form("Events w. TT_{Sig}: scaled %s & #it{p}_{T} of recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + spectra.add(Form("hScaled%s_DPhi_JetPt_Corr_TTRef_RestrictedPhi_Part", eaAxis.label), - Form("Events w. TT_{Ref} #in #varphi (%.2f, %.2f): scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", phiMin, phiMax, eaAxis.label), + Form("MC events w. TT_{Ref} #in #varphi (%.2f, %.2f): scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", phiMin, phiMax, eaAxis.label), kTH3F, {{eaAxis.axis, eaAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); spectra.add(Form("hScaled%s_DPhi_JetPt_Corr_TTSig_RestrictedPhi_Part", eaAxis.label), - Form("Events w. TT_{Sig} #in #varphi (%.2f, %.2f): scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", phiMin, phiMax, eaAxis.label), + Form("MC events w. TT_{Sig} #in #varphi (%.2f, %.2f): scaled %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", phiMin, phiMax, eaAxis.label), kTH3F, {{eaAxis.axis, eaAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); } for (const auto& eaAxis : arrAxisSpecScaledEA) { + spectra.add(Form("hScaled%s_Part", eaAxis.label), + "Scaled EA in MC collisions", + kTH1F, {eaAxis.axis}); + spectra.add(Form("hScaled%s_TTRef_Part", eaAxis.label), - Form("Events w. TT_{Ref}: scaled %s", eaAxis.label), + Form("MC events w. TT_{Ref}: scaled %s", eaAxis.label), kTH1F, {eaAxis.axis}); spectra.add(Form("hScaled%s_TTSig_Part", eaAxis.label), - Form("Events w. TT_{Sig}: scaled %s", eaAxis.label), + Form("MC events w. TT_{Sig}: scaled %s", eaAxis.label), kTH1F, {eaAxis.axis}); } + + // Centrality dependence + for (const auto& centAxis : arrConfigurableAxisCentrality) { + + // Vertex Z position + spectra.add(Form("h%s_vertexZ_Part", centAxis.label), + "Centrality vs Z vertex of MC collisions", + kTH2F, {{centAxis.axis, centAxis.axisName}, {60, -12., 12., "#it{z}_{vertex}"}}, hist.sumw2); + + // Distributions of centrality in events w/o and w. TT + spectra.add(Form("h%s_Part", centAxis.label), + "Centrality in MC collisions", + kTH1F, {{centAxis.axis, centAxis.axisName}}); + + spectra.add(Form("h%s_TTRef_Part", centAxis.label), + Form("%s in MC events w. TT_{Ref}", centAxis.label), + kTH1F, {{centAxis.axis, centAxis.axisName}}); + + spectra.add(Form("h%s_TTSig_Part", centAxis.label), + Form("%s in MC events w. TT_{Sig}", centAxis.label), + kTH1F, {{centAxis.axis}}); + + // Distribution of tracks + spectra.add(Form("h%sTrackPtEtaPhi_Part", centAxis.label), + "Charact. of MC generated particles", + kTHnSparseF, {{centAxis.axis, centAxis.axisName}, pT, pseudorap, phiAngle}, hist.sumw2); + + // Rho vs centrality + spectra.add(Form("h%s_Rho_Part", centAxis.label), + Form("MC events %s & #rho", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, rho}, hist.sumw2); + + spectra.add(Form("h%s_Rho_TTRef_Part", centAxis.label), + Form("MC events w. TT_{Ref}: %s & #rho", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, rho}, hist.sumw2); + + spectra.add(Form("h%s_Rho_TTSig_Part", centAxis.label), + Form("MC events w. TT_{Sig}: %s & #rho", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, rho}, hist.sumw2); + + // Recoil jets vs centrality + auto tmpHistPointer = spectra.add(Form("h%s_Ntrig_Part", centAxis.label), + Form("%s vs Total number of selected MC triggers per class", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, {2, 0.0, 2.}}); + tmpHistPointer->GetYaxis()->SetBinLabel(1, "TT_{Ref}"); + tmpHistPointer->GetYaxis()->SetBinLabel(2, "TT_{Sig}"); + + spectra.add(Form("h%s_DPhi_JetPt_Corr_TTRef_Part", centAxis.label), + Form("MC events w. TT_{Ref}: %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); + + spectra.add(Form("h%s_DPhi_JetPt_Corr_TTSig_Part", centAxis.label), + Form("MC events w. TT_{Sig}: %s & #Delta#varphi & #it{p}_{T, jet}^{ch}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, deltaPhiAngle, jetPTcorr}, hist.sumw2); + + spectra.add(Form("h%s_Recoil_JetPt_Corr_TTRef_Part", centAxis.label), + Form("MC events w. TT_{Ref}: %s & #it{p}_{T} of recoil jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + + spectra.add(Form("h%s_Recoil_JetPt_Corr_TTSig_Part", centAxis.label), + Form("MC events w. TT_{Sig}: %s & #it{p}_{T} of recoil jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, jetPTcorrFinnerBin}, hist.sumw2); + } + + // Register TTRef recoil spectra with rho-shift correction + spectra.add("hEA_MB_Recoil_JetPt_Corr_RhoShifted_TTRef_Part", + "EA_MB: recoil jet #it{p}_{T} (#rho shifted)", + kTH1F, {jetPTcorrFinnerBin}, hist.sumw2); + + spectra.add("hEA_MB_RhoShifted_TTRef_Part", + "EA_MB: #rho shifted in events w. TT_{Ref}", + kTH1F, {rho}, hist.sumw2); } - // Jet matching + // Jet matching analysis if (doprocessJetsGeoMatching || doprocessJetsGeoMatchingWeighted || doprocessJetsGeoPtMatchingWeighted || doprocessJetsGeoPtMatching) { AxisSpec detJetPt{200, 0.0, 200., "#it{p}_{T, det} (GeV/#it{c})"}; AxisSpec detJetPtCorr{220, -20.0, 200., "#it{p}_{T, det}^{corr.} (GeV/#it{c})"}; @@ -527,6 +794,9 @@ struct RecoilJets { AxisSpec partJetPtCorr{220, -20.0, 200., "#it{p}_{T, part}^{corr.} (GeV/#it{c})"}; AxisSpec relJetSmearPt{100, -5., 1., "(#it{p}_{T, part} - #it{p}_{T, det}) / #it{p}_{T, part}"}; + AxisSpec jetSmearPhi{100, -0.5, 0.5, "#it{#varphi}_{part} - #it{#varphi}_{det}"}; + + AxisSpec numberMatchedDetJets{4, 0.5, 4.5, "# of matched det. level jets"}; //==================================================================================== // Part. level jets @@ -534,124 +804,187 @@ struct RecoilJets { "All part. level inclusive jets", kTH1F, {partJetPt}, hist.sumw2); - spectra.add("hPartLevelInclusiveJetsPtCorr", - "All part. level inclusive jets", - kTH1F, {partJetPtCorr}, hist.sumw2); - spectra.add("hPartLevelRecoilJetsPt", "All part. level recoil jets", kTH1F, {partJetPt}, hist.sumw2); - spectra.add("hPartLevelRecoilJetsPtCorr", - "All part. level recoil jets", - kTH1F, {partJetPtCorr}, hist.sumw2); - spectra.add("hMissedInclusiveJetsPt", "Part. level inclusive jets w/o matched pair", kTH1F, {partJetPt}, hist.sumw2); - spectra.add("hMissedInclusiveJetsPtCorr", - "Part. level inclusive jets w/o matched pair", - kTH1F, {partJetPtCorr}, hist.sumw2); - spectra.add("hMissedRecoilJetsPt", "Part. level recoil jets w/o matched pair", kTH1F, {partJetPt}, hist.sumw2); - spectra.add("hMissedRecoilJetsPtCorr", - "Part. level recoil jets w/o matched pair", - kTH1F, {partJetPtCorr}, hist.sumw2); - //==================================================================================== // Det. level jets spectra.add("hDetLevelInclusiveJetsPt", "All reconstructed inclusive jets", kTH1F, {detJetPt}, hist.sumw2); - spectra.add("hDetLevelInclusiveJetsPtCorr", - "All reconstructed inclusive jets", - kTH1F, {detJetPtCorr}, hist.sumw2); - spectra.add("hDetLevelRecoilJetsPt", "All reconstructed recoil jets", kTH1F, {detJetPt}, hist.sumw2); - spectra.add("hDetLevelRecoilJetsPtCorr", - "All reconstructed recoil jets", - kTH1F, {detJetPtCorr}, hist.sumw2); - spectra.add("hFakeInclusiveJetsPt", "Det. level inclusive jets w/o matched pair", kTH1F, {detJetPt}, hist.sumw2); - spectra.add("hFakeInclusiveJetsPtCorr", - "Det. level inclusive jets w/o matched pair", - kTH1F, {detJetPtCorr}, hist.sumw2); - spectra.add("hFakeRecoilJetsPt", "Det. level recoil jets w/o matched pair", kTH1F, {detJetPt}, hist.sumw2); - spectra.add("hFakeRecoilJetsPtCorr", - "Det. level recoil jets w/o matched pair", - kTH1F, {detJetPtCorr}, hist.sumw2); - //==================================================================================== - // Response matices with inclusive jets + // Response matrices with inclusive jets spectra.add("hResponseMatrixInclusiveJetsPt", "Correlation inclusive #it{p}_{T, det.} vs. #it{p}_{T, part}", kTH2F, {detJetPt, partJetPt}, hist.sumw2); - spectra.add("hResponseMatrixInclusiveJetsPtCorr", - "Correlation inclusive #it{p}_{T, det.} vs. #it{p}_{T, part}", - kTH2F, {detJetPtCorr, partJetPtCorr}, hist.sumw2); - - //==================================================================================== - // Response matices with recoil jets + // Response matrices with recoil jets spectra.add("hResponseMatrixRecoilJetsPt", "Correlation recoil #it{p}_{T, det.} vs. #it{p}_{T, part}", kTH2F, {detJetPt, partJetPt}, hist.sumw2); - spectra.add("hResponseMatrixRecoilJetsPtCorr", - "Correlation recoil #it{p}_{T, det.} vs. #it{p}_{T, part}", - kTH2F, {detJetPtCorr, partJetPtCorr}, hist.sumw2); - //==================================================================================== - // Jet energy scale and resolution: pT and phi - spectra.add("hInclusiveJESPt", - "ES of inclusive jets vs. #it{p}_{T, part}", - kTH2F, {relJetSmearPt, partJetPt}, hist.sumw2); - + // Jet energy scale and resolution spectra.add("hInclusiveJESPtCorr", - "ES of inclusive jets vs. #it{p}_{T, part}", + "ES of inclusive jets vs. #it{p}_{T, part}^{corr.}", kTH2F, {relJetSmearPt, partJetPtCorr}, hist.sumw2); - spectra.add("hRecoilJESPt", - "ES of recoil jets vs. #it{p}_{T, part}", - kTH2F, {relJetSmearPt, partJetPt}, hist.sumw2); - spectra.add("hRecoilJESPtCorr", - "ES of recoil jets vs. #it{p}_{T, part}", + "ES of recoil jets vs. #it{p}_{T, part}^{corr.}", kTH2F, {relJetSmearPt, partJetPtCorr}, hist.sumw2); - AxisSpec jetSmearPhi{100, -0.5, 0.5, "#it{#varphi}_{part} - #it{#varphi}_{det}"}; - spectra.add("hInclusiveJESPhi", - "#varphi resolution as a func. of jet #it{p}_{T, part}", - kTH2F, {jetSmearPhi, partJetPt}, hist.sumw2); + //==================================================================================== + // Centrality-differential corrected spectra and response QA + for (const auto& centAxis : arrConfigurableAxisCentrality) { + + spectra.add(Form("h%s_ResponseMatrixInclusiveJetsPtCorr", centAxis.label), + Form("%s: correlation inclusive #it{p}_{T, det.}^{corr.} vs. #it{p}_{T, part}^{corr.}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, detJetPtCorr, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_ResponseMatrixRecoilJetsPtCorr", centAxis.label), + Form("%s: correlation recoil #it{p}_{T, det.}^{corr.} vs. #it{p}_{T, part}^{corr.}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, detJetPtCorr, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_InclusiveJESPt", centAxis.label), + Form("%s: ES of inclusive jets vs. #it{p}_{T, part}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, relJetSmearPt, partJetPt}, hist.sumw2); + + spectra.add(Form("h%s_RecoilJESPt", centAxis.label), + Form("%s: ES of recoil jets vs. #it{p}_{T, part}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, relJetSmearPt, partJetPt}, hist.sumw2); + + spectra.add(Form("h%s_InclusiveJESPhi", centAxis.label), + Form("%s: #varphi resolution as a func. of jet #it{p}_{T, part}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, jetSmearPhi, partJetPt}, hist.sumw2); + + spectra.add(Form("h%s_RecoilJESPhi", centAxis.label), + Form("%s: #varphi resolution as a func. of jet #it{p}_{T, part}", centAxis.label), + kTH3F, {{centAxis.axis, centAxis.axisName}, jetSmearPhi, partJetPt}, hist.sumw2); + + spectra.add(Form("h%s_FakeInclusiveJetsPtCorr", centAxis.label), + Form("%s: det. level inclusive jets w/o matched pair", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_FakeRecoilJetsPtCorr", centAxis.label), + Form("%s: det. level recoil jets w/o matched pair", centAxis.label), kTH2F, + {{centAxis.axis, centAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_DetLevelInclusiveJetsPtCorr", centAxis.label), + Form("%s: all reconstructed inclusive jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_DetLevelRecoilJetsPtCorr", centAxis.label), + Form("%s: all reconstructed recoil jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_PartLevelInclusiveJetsPtCorr", centAxis.label), + Form("%s: all part. level inclusive jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, partJetPtCorr}, hist.sumw2); - spectra.add("hRecoilJESPhi", - "#varphi resolution as a func. of jet #it{p}_{T, part}", - kTH2F, {jetSmearPhi, partJetPt}, hist.sumw2); + spectra.add(Form("h%s_PartLevelRecoilJetsPtCorr", centAxis.label), + Form("%s: all part. level recoil jets", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_MissedInclusiveJetsPtCorr", centAxis.label), + Form("%s: part. level inclusive jets w/o matched pair", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("h%s_MissedRecoilJetsPtCorr", centAxis.label), + Form("%s: part. level recoil jets w/o matched pair", centAxis.label), + kTH2F, {{centAxis.axis, centAxis.axisName}, partJetPtCorr}, hist.sumw2); + } + + //==================================================================================== + // EA-differential corrected spectra and response QA + for (const auto& eaAxis : arrConfigurableAxisEA) { + spectra.add(Form("hScaled%s_ResponseMatrixInclusiveJetsPtCorr", eaAxis.label), + Form("Scaled %s: correlation inclusive #it{p}_{T, det.}^{corr.} vs. #it{p}_{T, part}^{corr.}", eaAxis.label), + kTH3F, {{eaAxis.axis, eaAxis.axisName}, detJetPtCorr, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_ResponseMatrixRecoilJetsPtCorr", eaAxis.label), + Form("Scaled %s: correlation recoil #it{p}_{T, det.}^{corr.} vs. #it{p}_{T, part}^{corr.}", eaAxis.label), + kTH3F, {{eaAxis.axis, eaAxis.axisName}, detJetPtCorr, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_InclusiveJESPt", eaAxis.label), + Form("Scaled %s: ES of inclusive jets vs. #it{p}_{T, part}", eaAxis.label), + kTH3F, {{eaAxis.axis, eaAxis.axisName}, relJetSmearPt, partJetPt}, hist.sumw2); + + spectra.add(Form("hScaled%s_RecoilJESPt", eaAxis.label), + Form("Scaled %s: ES of recoil jets vs. #it{p}_{T, part}", eaAxis.label), + kTH3F, {{eaAxis.axis, eaAxis.axisName}, relJetSmearPt, partJetPt}, hist.sumw2); + + spectra.add(Form("hScaled%s_InclusiveJESPhi", eaAxis.label), + Form("Scaled %s: #varphi resolution as a func. of jet #it{p}_{T, part}", eaAxis.label), + kTH3F, {{eaAxis.axis, eaAxis.axisName}, jetSmearPhi, partJetPt}, hist.sumw2); + + spectra.add(Form("hScaled%s_RecoilJESPhi", eaAxis.label), + Form("Scaled %s: #varphi resolution as a func. of jet #it{p}_{T, part}", eaAxis.label), + kTH3F, {{eaAxis.axis, eaAxis.axisName}, jetSmearPhi, partJetPt}, hist.sumw2); + + spectra.add(Form("hScaled%s_FakeInclusiveJetsPtCorr", eaAxis.label), + Form("Scaled %s: det. level inclusive jets w/o matched pair", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_FakeRecoilJetsPtCorr", eaAxis.label), + Form("Scaled %s: det. level recoil jets w/o matched pair", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_DetLevelInclusiveJetsPtCorr", eaAxis.label), + Form("Scaled %s: all reconstructed inclusive jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_DetLevelRecoilJetsPtCorr", eaAxis.label), + Form("Scaled %s: all reconstructed recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, detJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_PartLevelInclusiveJetsPtCorr", eaAxis.label), + Form("Scaled %s: all part. level inclusive jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_PartLevelRecoilJetsPtCorr", eaAxis.label), + Form("Scaled %s: all part. level recoil jets", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_MissedInclusiveJetsPtCorr", eaAxis.label), + Form("Scaled %s: part. level inclusive jets w/o matched pair", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, partJetPtCorr}, hist.sumw2); + + spectra.add(Form("hScaled%s_MissedRecoilJetsPtCorr", eaAxis.label), + Form("Scaled %s: part. level recoil jets w/o matched pair", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, partJetPtCorr}, hist.sumw2); + } //==================================================================================== // Utility histograms spectra.add("hNumberMatchedInclusiveDetJetsPerOnePartJet", "# of det. level inclusive jets per 1 part. level jet vs. #it{p}_{T, det.} vs. #it{p}_{T, part.}", - kTH3F, {{6, 0.5, 6.5, "# of matched det. level jets"}, {200, 0.0, 200., "#it{p}_{T, det.}"}, {200, 0.0, 200., "#it{p}_{T, part.}"}}, hist.sumw2); + kTH3F, {{numberMatchedDetJets}, {detJetPt}, {partJetPt}}, hist.sumw2); spectra.add("hNumberMatchedRecoilDetJetsPerOnePartJet", "# of det. level recoil jets per 1 part. level jet vs. #it{p}_{T, det.} vs. #it{p}_{T, part.}", - kTH3F, {{6, 0.5, 6.5, "# of matched det. level jets"}, {200, 0.0, 200., "#it{p}_{T, det.}"}, {200, 0.0, 200., "#it{p}_{T, part.}"}}, hist.sumw2); + kTH3F, {{numberMatchedDetJets}, {detJetPt}, {partJetPt}}, hist.sumw2); } // Multiplicity for raw data and detector level MC @@ -665,9 +998,9 @@ struct RecoilJets { kTH1F, {eaAxis.axis}, hist.sumw2); } - spectra.add("hScaleMultFT0A", "Scaled FTOA signal", kTH1F, {scaledFT0A}, hist.sumw2); + spectra.add("hScaledMultFT0A", "Scaled FTOA signal", kTH1F, {scaledFT0A}, hist.sumw2); for (const auto& eaAxis : arrAxisSpecScaledEA) { - spectra.add(Form("hScaleMult%s", eaAxis.label), + spectra.add(Form("hScaledMult%s", eaAxis.label), Form("Scaled %s signal", eaAxis.label), kTH1F, {eaAxis.axis}, hist.sumw2); } @@ -697,24 +1030,24 @@ struct RecoilJets { kTH2F, {{arrAxisSpecUnscaledEA[i].axis}, {arrAxisSpecZDCNeutron[i].axis}}, hist.sumw2); if (i != 0) { - spectra.add(Form("hScaleMult%s_vs_%s", arrAxisSpecScaledEA[i - 1].label, arrAxisSpecZDCNeutron[i].label), + spectra.add(Form("hScaledMult%s_vs_%s", arrAxisSpecScaledEA[i - 1].label, arrAxisSpecZDCNeutron[i].label), Form("Correlation of signals scaled %s vs %s", arrAxisSpecScaledEA[i - 1].label, arrAxisSpecZDCNeutron[i].label), kTH2F, {{arrAxisSpecScaledEA[i - 1].axis}, {arrAxisSpecZDCNeutron[i].axis}}, hist.sumw2); - spectra.add(Form("hScaleMult%s_vs_%s", arrAxisSpecScaledEA[i - 1].label, arrAxisSpecZDCProton[i].label), + spectra.add(Form("hScaledMult%s_vs_%s", arrAxisSpecScaledEA[i - 1].label, arrAxisSpecZDCProton[i].label), Form("Correlation of signals scaled %s vs %s", arrAxisSpecScaledEA[i - 1].label, arrAxisSpecZDCProton[i].label), kTH2F, {{arrAxisSpecScaledEA[i - 1].axis}, {arrAxisSpecZDCProton[i].axis}}, hist.sumw2); } else { - spectra.add("hScaleMultFT0A_vs_ZNA", "Correlation of signals scaled FT0A vs ZNA", kTH2F, {{scaledFT0A}, {arrAxisSpecZDCNeutron[i].axis}}, hist.sumw2); - spectra.add("hScaleMultFT0A_vs_ZPA", "Correlation of signals scaled FT0A vs ZPA", kTH2F, {{scaledFT0A}, {arrAxisSpecZDCProton[i].axis}}, hist.sumw2); + spectra.add("hScaledMultFT0A_vs_ZNA", "Correlation of signals scaled FT0A vs ZNA", kTH2F, {{scaledFT0A}, {arrAxisSpecZDCNeutron[i].axis}}, hist.sumw2); + spectra.add("hScaledMultFT0A_vs_ZPA", "Correlation of signals scaled FT0A vs ZPA", kTH2F, {{scaledFT0A}, {arrAxisSpecZDCProton[i].axis}}, hist.sumw2); } } - spectra.add("hScaleMultFT0M_vs_ZNA_vs_ZNC", + spectra.add("hScaledMultFT0M_vs_ZNA_vs_ZNC", "Correlation of signals FT0M^{*} vs ZNA vs ZNC", kTH3F, {{scaledFT0M}, {600, 0.0, 3000., "ZNA"}, {600, 0.0, 3000., "ZNC"}}, hist.sumw2); - spectra.add("hScaleMultFT0M_vs_ZPA_vs_ZPC", + spectra.add("hScaledMultFT0M_vs_ZPA_vs_ZPC", "Correlation of signals FT0M^{*} vs ZPA vs ZPC", kTH3F, {{scaledFT0M}, {600, 0.0, 3000., "ZPA"}, {600, 0.0, 3000., "ZPC"}}, hist.sumw2); } @@ -725,9 +1058,9 @@ struct RecoilJets { spectra.add("hMultFT0CPartLevel", "# of primary particles within FTOC acceptance", kTH1F, {{2000, 0.0, 1000., "FT0C"}}, hist.sumw2); spectra.add("hMultFT0MPartLevel", "Total # of primary particles from FT0A & FTOC", kTH1F, {{4000, 0.0, 2000., "FT0M"}}, hist.sumw2); - spectra.add("hScaleMultFT0APartLevel", "Scaled # of primary particles within FTOA acceptance", kTH1F, {{scaledFT0A}}, hist.sumw2); + spectra.add("hScaledMultFT0APartLevel", "Scaled # of primary particles within FTOA acceptance", kTH1F, {{scaledFT0A}}, hist.sumw2); for (const auto& eaAxis : arrAxisSpecScaledEA) { - spectra.add(Form("hScaleMult%sPartLevel", eaAxis.label), + spectra.add(Form("hScaledMult%sPartLevel", eaAxis.label), Form("Scaled # of primary particles within %s acceptance", eaAxis.label), kTH1F, {{eaAxis.axis}}, hist.sumw2); } @@ -782,7 +1115,7 @@ struct RecoilJets { //==================================================================================== // Run-by-run study of EA - std::vector runNumbersOO = { + const std::vector runNumbersOO = { "564356", "564359", "564373", "564374", "564387", "564400", "564414", "564430", "564445"}; const int nRunsOO = runNumbersOO.size(); @@ -869,8 +1202,8 @@ struct RecoilJets { Form("Bkgd fluctuations RC with #it{R} = %.1f avoid lead, sublead jet in vic. %.1f vs. EA", bkgd.randomConeR.value, bkgd.minDeltaRToJet.value), kTH2F, {{eaAxis.axis}, {400, -40., 60., "#delta#it{p}_{T} (GeV/#it{c})"}}, hist.sumw2); - spectra.add(Form("hScaled%s_deltaPtRandomConeInEventTTSig", eaAxis.label), - Form("Bkgd fluctuations RC with #it{R} = %.1f in events with TT{%.0f, %.0f} in vic. %.1f vs. EA", bkgd.randomConeR.value, ptTTsigMin, ptTTsigMax, bkgd.minDeltaRToJet.value), + spectra.add(Form("hScaled%s_deltaPtPerpConeTTSig", eaAxis.label), + Form("Bkgd fluctuations PC with #it{R} = %.1f in events with TT{%.0f, %.0f} vs. EA", bkgd.randomConeR.value, ptTTsigMin, ptTTsigMax), kTH2F, {{eaAxis.axis}, {400, -40., 60., "#delta#it{p}_{T} (GeV/#it{c})"}}, hist.sumw2); } } @@ -894,12 +1227,13 @@ struct RecoilJets { Form("Bkgd fluctuations RC with #it{R} = %.1f avoid lead, sublead jet in vic. %.1f vs. EA", bkgd.randomConeR.value, bkgd.minDeltaRToJet.value), kTH2F, {{eaAxis.axis}, {400, -40., 60., "#delta#it{p}_{T} (GeV/#it{c})"}}, hist.sumw2); - spectra.add(Form("hScaled%s_deltaPtRandomConeInEventTTSig_PartLevel", eaAxis.label), + spectra.add(Form("hScaled%s_deltaPtPerpConeTTSig_PartLevel", eaAxis.label), Form("Bkgd fluctuations RC with #it{R} = %.1f in events with TT{%.0f, %.0f} in vic. %.1f vs. EA", bkgd.randomConeR.value, ptTTsigMin, ptTTsigMax, bkgd.minDeltaRToJet.value), kTH2F, {{eaAxis.axis}, {400, -40., 60., "#delta#it{p}_{T} (GeV/#it{c})"}}, hist.sumw2); } } + // Smearing of trigger-track parameters if (doprocessTTSmearingPtPhi || doprocessTTSmearingPtPhiWeighted) { AxisSpec relPtSmearTT{120, -5., 1., "(#it{p}_{T, part} - #it{p}_{T, det}) / #it{p}_{T, part}"}; AxisSpec smearPhi{80, -0.2, 0.2, "#it{#varphi}_{part} - #it{#varphi}_{det}"}; @@ -913,15 +1247,15 @@ struct RecoilJets { //==================================================================================== // Signal TT for (const auto& eaAxis : arrAxisSpecScaledEA) { - spectra.add(Form("hScaleMult%s_PtSmearingTTSig", eaAxis.label), + spectra.add(Form("hScaledMult%s_PtSmearingTTSig", eaAxis.label), Form("#it{p}_{T} smearing of TT_{Sig} vs %s", eaAxis.label), kTH3F, {{eaAxis.axis}, {relPtSmearTT}, {partPtTTSig}}, hist.sumw2); - spectra.add(Form("hScaleMult%s_PhiSmearingTTSig", eaAxis.label), + spectra.add(Form("hScaledMult%s_PhiSmearingTTSig", eaAxis.label), Form("#it{#varphi} smearing of TT_{Sig} vs %s", eaAxis.label), kTH3F, {{eaAxis.axis}, {smearPhi}, {partPtTTSig}}, hist.sumw2); - spectra.add(Form("hScaleMult%s_EtaSmearingTTSig", eaAxis.label), + spectra.add(Form("hScaledMult%s_EtaSmearingTTSig", eaAxis.label), Form("#it{#eta} smearing of TT_{Sig} vs %s", eaAxis.label), kTH3F, {{eaAxis.axis}, {smearEta}, {partPtTTSig}}, hist.sumw2); @@ -936,19 +1270,58 @@ struct RecoilJets { // Reference TT for (const auto& eaAxis : arrAxisSpecScaledEA) { - spectra.add(Form("hScaleMult%s_PtSmearingTTRef", eaAxis.label), + spectra.add(Form("hScaledMult%s_PtSmearingTTRef", eaAxis.label), Form("#it{p}_{T} smearing of TT_{Ref} vs %s", eaAxis.label), kTH3F, {{eaAxis.axis}, {relPtSmearTT}, {partPtTTRef}}, hist.sumw2); - spectra.add(Form("hScaleMult%s_PhiSmearingTTRef", eaAxis.label), + spectra.add(Form("hScaledMult%s_PhiSmearingTTRef", eaAxis.label), Form("#it{#varphi} smearing of TT_{Ref} vs %s", eaAxis.label), kTH3F, {{eaAxis.axis}, {smearPhi}, {partPtTTRef}}, hist.sumw2); - spectra.add(Form("hScaleMult%s_EtaSmearingTTRef", eaAxis.label), + spectra.add(Form("hScaledMult%s_EtaSmearingTTRef", eaAxis.label), Form("#it{#eta} smearing of TT_{Ref} vs %s", eaAxis.label), kTH3F, {{eaAxis.axis}, {smearEta}, {partPtTTRef}}, hist.sumw2); } } + + // Tracking efficiency as a function of EA / Centrality + if (doprocessTrackingEfficiency || doprocessTrackingEfficiencyWeighted) { + for (const auto& eaAxis : arrConfigurableAxisEA) { + // ------------------------------------------------------------------ + // Denominator: all generated particles passing phase-space cuts + // ------------------------------------------------------------------ + spectra.add(Form("hScaledMult%s_AllGeneratedParticlesPt", eaAxis.label), + Form("Generated & selected particles; %s ; #it{p}_{T}^{part} (GeV/#it{c})", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, {hist.axisPtTrackEff, "#it{p}_{T}^{part}"}}, hist.sumw2); + + // ------------------------------------------------------------------ + // Track has an associated MC particle, + // the particle belongs to the same MC collision, + // and the particle passes the generated-particle selection. + // ------------------------------------------------------------------ + spectra.add(Form("hScaledMult%s_ResponseMatrixTrackEff", eaAxis.label), + Form("Track eff.; %s; #it{p}_{T}^{rec}; #it{p}_{T}^{part}", eaAxis.label), + kTH3F, {{eaAxis.axis, eaAxis.axisName}, {hist.axisPtTrackEff, "#it{p}_{T}^{rec}"}, {hist.axisPtTrackEff, "#it{p}_{T}^{part}"}}, hist.sumw2); + + // ------------------------------------------------------------------ + // Fake reconstructed tracks + + // Case 1: no associated MC particle + spectra.add(Form("hScaledMult%s_NoMcParticle", eaAxis.label), + Form("Tracks without associated MC particle; %s; #it{p}_{T}^{rec} (GeV/#it{c})", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, {hist.axisPtTrackEff, "#it{p}_{T}^{rec}"}}, hist.sumw2); + + // Case 2: MC particle exists, but belongs to another MC collision + spectra.add(Form("hScaledMult%s_MismatchedMcCollisionTracks", eaAxis.label), + Form("Tracks matched to particle from different MC collision; %s; #it{p}_{T}^{rec} (GeV/#it{c})", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, {hist.axisPtTrackEff, "#it{p}_{T}^{rec}"}}, hist.sumw2); + + // Case 3: same MC collision, but particle fails phase-space cuts + spectra.add(Form("hScaledMult%s_MatchedToRejectedTracks", eaAxis.label), + Form("Tracks matched to rejected particle; %s; #it{p}_{T}^{rec} (GeV/#it{c})", eaAxis.label), + kTH2F, {{eaAxis.axis, eaAxis.axisName}, {hist.axisPtTrackEff, "#it{p}_{T}^{rec} (GeV/#it{c})"}}, hist.sumw2); + } + } } //============================================================================= @@ -956,43 +1329,87 @@ struct RecoilJets { //============================================================================= // Fill histograms with raw or MC det. level data - template + template void fillHistograms(JCollision const& collision, - Jets const& jets, + ChJets const& chJets, JTracks const& tracks, float weight = 1.) { + // Get the configured scaled FT0M percentile boundaries + const std::vector rawFT0MEdges = hist.multFT0MThresh; + + const std::vector ft0mEdges{ + rawFT0MEdges.begin() + 1, + rawFT0MEdges.end()}; + bool bSigEv = false; std::vector vPhiOfTT; double phiTT = 0.; int nTT = 0; - float rho = collision.rho(); + const float addCountToTTRef = 0.5; + const float addCountToTTSig = 1.5; + + const auto vertexZ = collision.posZ(); + const auto rho = collision.rho(); + const auto ft0Metrics = getFT0Metrics(collision, ft0a.mean, ft0c.mean); const auto scaledFT0C = ft0Metrics.scaledFT0C; const auto scaledFT0M = ft0Metrics.scaledFT0M; - auto dice = randGen->Rndm(); - if (dice < tt.fracSig) + const auto centFT0C = collision.centFT0C(); + const auto centFT0M = collision.centFT0M(); + + const auto dice = randGen->Rndm(); + if (dice < tt.fracSig) { bSigEv = true; + } - spectra.fill(HIST("hScaledFT0C_vertexZ"), scaledFT0C, collision.posZ(), weight); - spectra.fill(HIST("hScaledFT0M_vertexZ"), scaledFT0M, collision.posZ(), weight); + // EA distributions + spectra.fill(HIST("hScaledFT0C"), scaledFT0C, weight); + spectra.fill(HIST("hScaledFT0M"), scaledFT0M, weight); + // Centrality distribution + spectra.fill(HIST("hCentFT0C"), centFT0C, weight); + spectra.fill(HIST("hCentFT0M"), centFT0M, weight); + + // Correlation: centrality vs FT0M* + spectra.fill(HIST("hCentFT0C_FT0MStar"), centFT0C, scaledFT0M, weight); + spectra.fill(HIST("hCentFT0M_FT0MStar"), centFT0M, scaledFT0M, weight); + + // Correlation: centrality FT0C vs scaled FT0C + spectra.fill(HIST("hCentFT0C_ScaledFT0C"), centFT0C, scaledFT0C, weight); + + // Z vertex position vs EA / centrality + spectra.fill(HIST("hScaledFT0C_vertexZ"), scaledFT0C, vertexZ, weight); + spectra.fill(HIST("hScaledFT0M_vertexZ"), scaledFT0M, vertexZ, weight); + + spectra.fill(HIST("hCentFT0C_vertexZ"), centFT0C, vertexZ, weight); + spectra.fill(HIST("hCentFT0M_vertexZ"), centFT0M, vertexZ, weight); + + // Rho vs EA / centrality spectra.fill(HIST("hScaledFT0C_Rho"), scaledFT0C, rho, weight); spectra.fill(HIST("hScaledFT0M_Rho"), scaledFT0M, rho, weight); + spectra.fill(HIST("hCentFT0C_Rho"), centFT0C, rho, weight); + spectra.fill(HIST("hCentFT0M_Rho"), centFT0M, rho, weight); + for (const auto& track : tracks) { spectra.fill(HIST("hTrackSelectionCount"), 0.5); - if (skipTrack(track)) + if (skipTrack(track)) { continue; + } - float trackPt = track.pt(); - float trackPhi = track.phi(); + const float trackPt = track.pt(); + const float trackPhi = track.phi(); + const float trackEta = track.eta(); spectra.fill(HIST("hTrackSelectionCount"), 1.5); - spectra.fill(HIST("hScaledFT0CTrackPtEtaPhi"), scaledFT0C, trackPt, track.eta(), trackPhi, weight); - spectra.fill(HIST("hScaledFT0MTrackPtEtaPhi"), scaledFT0M, trackPt, track.eta(), trackPhi, weight); + spectra.fill(HIST("hScaledFT0CTrackPtEtaPhi"), scaledFT0C, trackPt, trackEta, trackPhi, weight); + spectra.fill(HIST("hScaledFT0MTrackPtEtaPhi"), scaledFT0M, trackPt, trackEta, trackPhi, weight); + + spectra.fill(HIST("hCentFT0CTrackPtEtaPhi"), centFT0C, trackPt, trackEta, trackPhi, weight); + spectra.fill(HIST("hCentFT0MTrackPtEtaPhi"), centFT0M, trackPt, trackEta, trackPhi, weight); // Search for TT candidate const auto ptTTsigMin = tt.sigPtRange->at(0); @@ -1015,9 +1432,19 @@ struct RecoilJets { phiTT = getPhiTT(vPhiOfTT); + const auto phiMin = tt.phiRestr->at(0); + const auto phiMax = tt.phiRestr->at(1); + if (bSigEv) { - spectra.fill(HIST("hScaledFT0C_Ntrig"), scaledFT0C, 1.5, weight); - spectra.fill(HIST("hScaledFT0M_Ntrig"), scaledFT0M, 1.5, weight); + // EA + spectra.fill(HIST("hScaledFT0C_Ntrig"), scaledFT0C, addCountToTTSig, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig"), scaledFT0M, addCountToTTSig, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Ntrig_RestrictedPhi"), scaledFT0C, addCountToTTSig, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig_RestrictedPhi"), scaledFT0M, addCountToTTSig, weight); + } + spectra.fill(HIST("hScaledFT0C_TTSig_per_event"), scaledFT0C, nTT, weight); spectra.fill(HIST("hScaledFT0M_TTSig_per_event"), scaledFT0M, nTT, weight); @@ -1026,9 +1453,27 @@ struct RecoilJets { spectra.fill(HIST("hScaledFT0C_Rho_TTSig"), scaledFT0C, rho, weight); spectra.fill(HIST("hScaledFT0M_Rho_TTSig"), scaledFT0M, rho, weight); + + //_____________________________________________________ + // Centrality + spectra.fill(HIST("hCentFT0C_Ntrig"), centFT0C, addCountToTTSig, weight); + spectra.fill(HIST("hCentFT0M_Ntrig"), centFT0M, addCountToTTSig, weight); + + spectra.fill(HIST("hCentFT0C_TTSig"), centFT0C, weight); + spectra.fill(HIST("hCentFT0M_TTSig"), centFT0M, weight); + + spectra.fill(HIST("hCentFT0C_Rho_TTSig"), centFT0C, rho, weight); + spectra.fill(HIST("hCentFT0M_Rho_TTSig"), centFT0M, rho, weight); + } else { - spectra.fill(HIST("hScaledFT0C_Ntrig"), scaledFT0C, 0.5, weight); - spectra.fill(HIST("hScaledFT0M_Ntrig"), scaledFT0M, 0.5, weight); + spectra.fill(HIST("hScaledFT0C_Ntrig"), scaledFT0C, addCountToTTRef, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig"), scaledFT0M, addCountToTTRef, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Ntrig_RestrictedPhi"), scaledFT0C, addCountToTTRef, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig_RestrictedPhi"), scaledFT0M, addCountToTTRef, weight); + } + spectra.fill(HIST("hScaledFT0C_TTRef_per_event"), scaledFT0C, nTT, weight); spectra.fill(HIST("hScaledFT0M_TTRef_per_event"), scaledFT0M, nTT, weight); @@ -1037,74 +1482,200 @@ struct RecoilJets { spectra.fill(HIST("hScaledFT0C_Rho_TTRef"), scaledFT0C, rho, weight); spectra.fill(HIST("hScaledFT0M_Rho_TTRef"), scaledFT0M, rho, weight); + + //_____________________________________________________ + // Centrality + spectra.fill(HIST("hCentFT0C_Ntrig"), centFT0C, addCountToTTRef, weight); + spectra.fill(HIST("hCentFT0M_Ntrig"), centFT0M, addCountToTTRef, weight); + + spectra.fill(HIST("hCentFT0C_TTRef"), centFT0C, weight); + spectra.fill(HIST("hCentFT0M_TTRef"), centFT0M, weight); + + spectra.fill(HIST("hCentFT0C_Rho_TTRef"), centFT0C, rho, weight); + spectra.fill(HIST("hCentFT0M_Rho_TTRef"), centFT0M, rho, weight); + + //_____________________________________________________ + // Fill EA-dependent rho spectra in events with TTRef with corresponding rho shift + for (const auto& ea : eaRhoShifts) { + + if (!isInEAInterval(scaledFT0M, ft0mEdges, ea.interval)) { + continue; + } + + const float rhoRefShifted = rho + ea.rhoShift; + + switch (ea.interval) { + case kMB: + spectra.fill(HIST("hEA_MB_RhoShifted_TTRef"), rhoRefShifted, weight); + break; + + case kEA0_20: + spectra.fill(HIST("hEA_Perc_0_20_RhoShifted_TTRef"), rhoRefShifted, weight); + break; + + case kEA0_10: + spectra.fill(HIST("hEA_Perc_0_10_RhoShifted_TTRef"), rhoRefShifted, weight); + break; + + case kEA20_40: + spectra.fill(HIST("hEA_Perc_20_40_RhoShifted_TTRef"), rhoRefShifted, weight); + break; + + case kEA60_80: + spectra.fill(HIST("hEA_Perc_60_80_RhoShifted_TTRef"), rhoRefShifted, weight); + break; + + case kEA50_100: + spectra.fill(HIST("hEA_Perc_50_100_RhoShifted_TTRef"), rhoRefShifted, weight); + break; + + case kEA80_100: + spectra.fill(HIST("hEA_Perc_80_100_RhoShifted_TTRef"), rhoRefShifted, weight); + break; + } + } } } - for (const auto& jet : jets) { + for (const auto& chJet : chJets) { // skip jets which have a constituent with pT above specified cut - if (isJetWithHighPtConstituent(jet)) + if (isJetWithHighPtConstituent(chJet)) { continue; + } - float jetPt = jet.pt(); - float jetArea = jet.area(); - float jetPtCorr = jetPt - rho * jetArea; + const auto jetPt = chJet.pt(); + const auto jetArea = chJet.area(); + const auto jetPtCorr = jetPt - rho * jetArea; - spectra.fill(HIST("hJetPtEtaPhiRhoArea"), jetPt, jet.eta(), jet.phi(), rho, jetArea, weight); + spectra.fill(HIST("hJetPtEtaPhiRhoArea"), jetPt, chJet.eta(), chJet.phi(), rho, jetArea, weight); if (nTT > 0) { const auto phiMin = tt.phiRestr->at(0); const auto phiMax = tt.phiRestr->at(1); - auto [dphi, bRecoilJet] = isRecoilJet(jet, phiTT); + auto [dphi, bRecoilJet] = isRecoilJet(chJet, phiTT); if (bSigEv) { + + spectra.fill(HIST("hJetArea_JetPt_Rho_TTSig"), jetArea, jetPt, rho, weight); + + // EA dependence spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTSig"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTSig"), scaledFT0M, dphi, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_TTSig"), scaledFT0C, dphi, jetPt, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_TTSig"), scaledFT0M, dphi, jetPt, weight); - spectra.fill(HIST("hJetArea_JetPt_Rho_TTSig"), jetArea, jetPt, rho, weight); if (phiTT > phiMin && phiTT < phiMax) { spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTSig_RestrictedPhi"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTSig_RestrictedPhi"), scaledFT0M, dphi, jetPtCorr, weight); } + // Centrality dependence + spectra.fill(HIST("hCentFT0C_DPhi_JetPt_Corr_TTSig"), centFT0C, dphi, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DPhi_JetPt_Corr_TTSig"), centFT0M, dphi, jetPtCorr, weight); + if (bRecoilJet) { + + // EA dependence spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTSig"), scaledFT0C, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTSig"), scaledFT0M, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_TTSig"), scaledFT0C, jetPt, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_TTSig"), scaledFT0M, jetPt, weight); + + // Centrality dependence + spectra.fill(HIST("hCentFT0C_Recoil_JetPt_Corr_TTSig"), centFT0C, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_Recoil_JetPt_Corr_TTSig"), centFT0M, jetPtCorr, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTSig_RestrictedPhi"), scaledFT0C, jetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTSig_RestrictedPhi"), scaledFT0M, jetPtCorr, weight); + } } } else { + + spectra.fill(HIST("hJetArea_JetPt_Rho_TTRef"), jetArea, jetPt, rho, weight); + + // EA dependence spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTRef"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTRef"), scaledFT0M, dphi, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_TTRef"), scaledFT0C, dphi, jetPt, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_TTRef"), scaledFT0M, dphi, jetPt, weight); - spectra.fill(HIST("hJetArea_JetPt_Rho_TTRef"), jetArea, jetPt, rho, weight); if (phiTT > phiMin && phiTT < phiMax) { spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTRef_RestrictedPhi"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTRef_RestrictedPhi"), scaledFT0M, dphi, jetPtCorr, weight); } + // Centrality dependence + spectra.fill(HIST("hCentFT0C_DPhi_JetPt_Corr_TTRef"), centFT0C, dphi, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DPhi_JetPt_Corr_TTRef"), centFT0M, dphi, jetPtCorr, weight); + if (bRecoilJet) { + + // Fill EA-dependent TTRef recoil spectra using the corresponding rho shift + for (const auto& ea : eaRhoShifts) { + + if (!isInEAInterval(scaledFT0M, ft0mEdges, ea.interval)) { + continue; + } + + const float rhoRefShifted = rho + ea.rhoShift; + const float jetPtCorrShifted = jetPt - rhoRefShifted * jetArea; + + switch (ea.interval) { + case kMB: + spectra.fill(HIST("hEA_MB_Recoil_JetPt_Corr_RhoShifted_TTRef"), jetPtCorrShifted, weight); + break; + + case kEA0_20: + spectra.fill(HIST("hEA_Perc_0_20_Recoil_JetPt_Corr_RhoShifted_TTRef"), jetPtCorrShifted, weight); + break; + + case kEA0_10: + spectra.fill(HIST("hEA_Perc_0_10_Recoil_JetPt_Corr_RhoShifted_TTRef"), jetPtCorrShifted, weight); + break; + + case kEA20_40: + spectra.fill(HIST("hEA_Perc_20_40_Recoil_JetPt_Corr_RhoShifted_TTRef"), jetPtCorrShifted, weight); + break; + + case kEA60_80: + spectra.fill(HIST("hEA_Perc_60_80_Recoil_JetPt_Corr_RhoShifted_TTRef"), jetPtCorrShifted, weight); + break; + + case kEA50_100: + spectra.fill(HIST("hEA_Perc_50_100_Recoil_JetPt_Corr_RhoShifted_TTRef"), jetPtCorrShifted, weight); + break; + + case kEA80_100: + spectra.fill(HIST("hEA_Perc_80_100_Recoil_JetPt_Corr_RhoShifted_TTRef"), jetPtCorrShifted, weight); + break; + } + } + + // EA dependence spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTRef"), scaledFT0C, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTRef"), scaledFT0M, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_TTRef"), scaledFT0C, jetPt, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_TTRef"), scaledFT0M, jetPt, weight); + + // Centrality dependence + spectra.fill(HIST("hCentFT0C_Recoil_JetPt_Corr_TTRef"), centFT0C, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_Recoil_JetPt_Corr_TTRef"), centFT0M, jetPtCorr, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTRef_RestrictedPhi"), scaledFT0C, jetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTRef_RestrictedPhi"), scaledFT0M, jetPtCorr, weight); + } } } } } } - template + template void fillHistogramsMCPartLevel(JCollision const& collision, - Jets const& jets, + ChJets const& chJets, JParticles const& particles, float weight = 1.) { @@ -1112,30 +1683,57 @@ struct RecoilJets { std::vector vPhiOfTT; double phiTT = 0.; int nTT = 0; - float rho = collision.rho(); + const float addCountToTTRef = 0.5; + const float addCountToTTSig = 1.5; + + const auto vertexZ = collision.posZ(); + const auto rho = collision.rho(); + const auto ft0Metrics = getFT0Metrics(collision, ft0a.meanPartLevel, ft0c.meanPartLevel); const auto scaledFT0C = ft0Metrics.scaledFT0C; const auto scaledFT0M = ft0Metrics.scaledFT0M; - auto dice = randGen->Rndm(); - if (dice < tt.fracSig) + const auto centFT0C = collision.centFT0C(); + const auto centFT0M = collision.centFT0M(); + + const auto dice = randGen->Rndm(); + if (dice < tt.fracSig) { bSigEv = true; + } - spectra.fill(HIST("hScaledFT0C_vertexZMC"), scaledFT0C, collision.posZ(), weight); - spectra.fill(HIST("hScaledFT0M_vertexZMC"), scaledFT0M, collision.posZ(), weight); + // EA distributions + spectra.fill(HIST("hScaledFT0C_Part"), scaledFT0C, weight); + spectra.fill(HIST("hScaledFT0M_Part"), scaledFT0M, weight); + // Centrality distribution + spectra.fill(HIST("hCentFT0C_Part"), centFT0C, weight); + spectra.fill(HIST("hCentFT0M_Part"), centFT0M, weight); + + // Z vertex position vs EA / centrality + spectra.fill(HIST("hScaledFT0C_vertexZ_Part"), scaledFT0C, vertexZ, weight); + spectra.fill(HIST("hScaledFT0M_vertexZ_Part"), scaledFT0M, vertexZ, weight); + + spectra.fill(HIST("hCentFT0C_vertexZ_Part"), centFT0C, vertexZ, weight); + spectra.fill(HIST("hCentFT0M_vertexZ_Part"), centFT0M, vertexZ, weight); + + // Rho vs EA / centrality spectra.fill(HIST("hScaledFT0C_Rho_Part"), scaledFT0C, rho, weight); spectra.fill(HIST("hScaledFT0M_Rho_Part"), scaledFT0M, rho, weight); for (const auto& particle : particles) { - if (skipParticle(particle)) + if (skipParticle(particle)) { continue; + } - float particlePt = particle.pt(); - float particlePhi = particle.phi(); + const auto particlePt = particle.pt(); + const auto particlePhi = particle.phi(); + const auto particleEta = particle.eta(); + + spectra.fill(HIST("hScaledFT0CTrackPtEtaPhi_Part"), scaledFT0C, particlePt, particleEta, particlePhi, weight); + spectra.fill(HIST("hScaledFT0MTrackPtEtaPhi_Part"), scaledFT0M, particlePt, particleEta, particlePhi, weight); - spectra.fill(HIST("hScaledFT0CPartPtEtaPhi"), scaledFT0C, particlePt, particle.eta(), particlePhi, weight); - spectra.fill(HIST("hScaledFT0MPartPtEtaPhi"), scaledFT0M, particlePt, particle.eta(), particlePhi, weight); + spectra.fill(HIST("hCentFT0CTrackPtEtaPhi_Part"), centFT0C, particlePt, particleEta, particlePhi, weight); + spectra.fill(HIST("hCentFT0MTrackPtEtaPhi_Part"), centFT0M, particlePt, particleEta, particlePhi, weight); // Search for TT candidate const auto ptTTsigMin = tt.sigPtRange->at(0); @@ -1157,9 +1755,20 @@ struct RecoilJets { phiTT = getPhiTT(vPhiOfTT); + const auto phiMin = tt.phiRestr->at(0); + const auto phiMax = tt.phiRestr->at(1); + if (bSigEv) { - spectra.fill(HIST("hScaledFT0C_Ntrig_Part"), scaledFT0C, 1.5, weight); - spectra.fill(HIST("hScaledFT0M_Ntrig_Part"), scaledFT0M, 1.5, weight); + + // EA + spectra.fill(HIST("hScaledFT0C_Ntrig_Part"), scaledFT0C, addCountToTTSig, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig_Part"), scaledFT0M, addCountToTTSig, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Ntrig_RestrictedPhi_Part"), scaledFT0C, addCountToTTSig, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig_RestrictedPhi_Part"), scaledFT0M, addCountToTTSig, weight); + } + spectra.fill(HIST("hScaledFT0C_TTSig_per_event_Part"), scaledFT0C, nTT, weight); spectra.fill(HIST("hScaledFT0M_TTSig_per_event_Part"), scaledFT0M, nTT, weight); @@ -1168,9 +1777,27 @@ struct RecoilJets { spectra.fill(HIST("hScaledFT0C_Rho_TTSig_Part"), scaledFT0C, rho, weight); spectra.fill(HIST("hScaledFT0M_Rho_TTSig_Part"), scaledFT0M, rho, weight); + + //_____________________________________________________ + // Centrality + spectra.fill(HIST("hCentFT0C_Ntrig_Part"), centFT0C, addCountToTTSig, weight); + spectra.fill(HIST("hCentFT0M_Ntrig_Part"), centFT0M, addCountToTTSig, weight); + + spectra.fill(HIST("hCentFT0C_TTSig_Part"), centFT0C, weight); + spectra.fill(HIST("hCentFT0M_TTSig_Part"), centFT0M, weight); + + spectra.fill(HIST("hCentFT0C_Rho_TTSig_Part"), centFT0C, rho, weight); + spectra.fill(HIST("hCentFT0M_Rho_TTSig_Part"), centFT0M, rho, weight); + } else { - spectra.fill(HIST("hScaledFT0C_Ntrig_Part"), scaledFT0C, 0.5, weight); - spectra.fill(HIST("hScaledFT0M_Ntrig_Part"), scaledFT0M, 0.5, weight); + spectra.fill(HIST("hScaledFT0C_Ntrig_Part"), scaledFT0C, addCountToTTRef, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig_Part"), scaledFT0M, addCountToTTRef, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Ntrig_RestrictedPhi_Part"), scaledFT0C, addCountToTTRef, weight); + spectra.fill(HIST("hScaledFT0M_Ntrig_RestrictedPhi_Part"), scaledFT0M, addCountToTTRef, weight); + } + spectra.fill(HIST("hScaledFT0C_TTRef_per_event_Part"), scaledFT0C, nTT, weight); spectra.fill(HIST("hScaledFT0M_TTRef_per_event_Part"), scaledFT0M, nTT, weight); @@ -1179,65 +1806,114 @@ struct RecoilJets { spectra.fill(HIST("hScaledFT0C_Rho_TTRef_Part"), scaledFT0C, rho, weight); spectra.fill(HIST("hScaledFT0M_Rho_TTRef_Part"), scaledFT0M, rho, weight); + + //_____________________________________________________ + // Centrality + spectra.fill(HIST("hCentFT0C_Ntrig_Part"), centFT0C, addCountToTTRef, weight); + spectra.fill(HIST("hCentFT0M_Ntrig_Part"), centFT0M, addCountToTTRef, weight); + + spectra.fill(HIST("hCentFT0C_TTRef_Part"), centFT0C, weight); + spectra.fill(HIST("hCentFT0M_TTRef_Part"), centFT0M, weight); + + spectra.fill(HIST("hCentFT0C_Rho_TTRef_Part"), centFT0C, rho, weight); + spectra.fill(HIST("hCentFT0M_Rho_TTRef_Part"), centFT0M, rho, weight); + + //_____________________________________________________ + // Fill EA-dependent rho spectra in events with TTRef with corresponding rho shift + const float rhoRefShifted = rho + cfgRhoShift.mbPart.value; + spectra.fill(HIST("hEA_MB_RhoShifted_TTRef_Part"), rhoRefShifted, weight); } } - for (const auto& jet : jets) { - float jetPt = jet.pt(); - float jetArea = jet.area(); - float jetPtCorr = jetPt - rho * jetArea; + for (const auto& chJet : chJets) { + const auto jetPt = chJet.pt(); + const auto jetArea = chJet.area(); + const auto jetPtCorr = jetPt - rho * jetArea; - spectra.fill(HIST("hJetPtEtaPhiRhoArea_Part"), jetPt, jet.eta(), jet.phi(), rho, jetArea, weight); + spectra.fill(HIST("hJetPtEtaPhiRhoArea_Part"), jetPt, chJet.eta(), chJet.phi(), rho, jetArea, weight); if (nTT > 0) { const auto phiMin = tt.phiRestr->at(0); const auto phiMax = tt.phiRestr->at(1); - auto [dphi, bRecoilJet] = isRecoilJet(jet, phiTT); + auto [dphi, bRecoilJet] = isRecoilJet(chJet, phiTT); if (bSigEv) { + + spectra.fill(HIST("hJetArea_JetPt_Rho_TTSig_Part"), jetArea, jetPt, rho, weight); + + // EA dependence spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTSig_Part"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTSig_Part"), scaledFT0M, dphi, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_TTSig_Part"), scaledFT0C, dphi, jetPt, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_TTSig_Part"), scaledFT0M, dphi, jetPt, weight); - spectra.fill(HIST("hJetArea_JetPt_Rho_TTSig_Part"), jetArea, jetPt, rho, weight); - if (phiTT > phiMin && phiTT < phiMax) { spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTSig_RestrictedPhi_Part"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTSig_RestrictedPhi_Part"), scaledFT0M, dphi, jetPtCorr, weight); } + // Centrality dependence + spectra.fill(HIST("hCentFT0C_DPhi_JetPt_Corr_TTSig_Part"), centFT0C, dphi, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DPhi_JetPt_Corr_TTSig_Part"), centFT0M, dphi, jetPtCorr, weight); + if (bRecoilJet) { + + // EA dependence spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTSig_Part"), scaledFT0C, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTSig_Part"), scaledFT0M, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_TTSig_Part"), scaledFT0C, jetPt, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_TTSig_Part"), scaledFT0M, jetPt, weight); - } + // Centrality dependence + spectra.fill(HIST("hCentFT0C_Recoil_JetPt_Corr_TTSig_Part"), centFT0C, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_Recoil_JetPt_Corr_TTSig_Part"), centFT0M, jetPtCorr, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTSig_RestrictedPhi_Part"), scaledFT0C, jetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTSig_RestrictedPhi_Part"), scaledFT0M, jetPtCorr, weight); + } + } } else { + spectra.fill(HIST("hJetArea_JetPt_Rho_TTRef_Part"), jetArea, jetPt, rho, weight); + + // EA dependence spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTRef_Part"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTRef_Part"), scaledFT0M, dphi, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_TTRef_Part"), scaledFT0C, dphi, jetPt, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_TTRef_Part"), scaledFT0M, dphi, jetPt, weight); - spectra.fill(HIST("hJetArea_JetPt_Rho_TTRef_Part"), jetArea, jetPt, rho, weight); - if (phiTT > phiMin && phiTT < phiMax) { spectra.fill(HIST("hScaledFT0C_DPhi_JetPt_Corr_TTRef_RestrictedPhi_Part"), scaledFT0C, dphi, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_DPhi_JetPt_Corr_TTRef_RestrictedPhi_Part"), scaledFT0M, dphi, jetPtCorr, weight); } + // Centrality dependence + spectra.fill(HIST("hCentFT0C_DPhi_JetPt_Corr_TTRef_Part"), centFT0C, dphi, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DPhi_JetPt_Corr_TTRef_Part"), centFT0M, dphi, jetPtCorr, weight); + if (bRecoilJet) { + + // Fill EA-dependent TTRef recoil spectra using the corresponding rho shift + const float rhoRefShifted = rho + cfgRhoShift.mbPart.value; + const float jetPtCorrShifted = jetPt - rhoRefShifted * jetArea; + spectra.fill(HIST("hEA_MB_Recoil_JetPt_Corr_RhoShifted_TTRef_Part"), jetPtCorrShifted, weight); + + // EA dependence spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTRef_Part"), scaledFT0C, jetPtCorr, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTRef_Part"), scaledFT0M, jetPtCorr, weight); - spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_TTRef_Part"), scaledFT0C, jetPt, weight); spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_TTRef_Part"), scaledFT0M, jetPt, weight); + + // Centrality dependence + spectra.fill(HIST("hCentFT0C_Recoil_JetPt_Corr_TTRef_Part"), centFT0C, jetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_Recoil_JetPt_Corr_TTRef_Part"), centFT0M, jetPtCorr, weight); + + if (phiTT > phiMin && phiTT < phiMax) { + spectra.fill(HIST("hScaledFT0C_Recoil_JetPt_Corr_TTRef_RestrictedPhi_Part"), scaledFT0C, jetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_Recoil_JetPt_Corr_TTRef_RestrictedPhi_Part"), scaledFT0M, jetPtCorr, weight); + } } } } @@ -1247,248 +1923,398 @@ struct RecoilJets { //============================================================================= // Construction of response matrix //============================================================================= - template - void fillMatchedGeoHistograms(JetsPart const& jetsPart, - JetsDet const& jetsDet, + template + void fillMatchedGeoHistograms(JCollision const& collision, + ChJetsPart const& chJetsPart, + ChJetsDet const& chJetsDet, JTracks const& tracks, - float partLevelCollRho, - float detLevelCollRho, + const float partLevelCollRho, float weight = 1.) { - /// TODO: consider TT cand. on both MC levels simultaneously - // Utilities for recoil jets; TTsis at det. level MC + //==================================================================================== + // Event properties + const auto ft0Metrics = getFT0Metrics(collision, ft0a.mean, ft0c.mean); + const auto scaledFT0C = ft0Metrics.scaledFT0C; + const auto scaledFT0M = ft0Metrics.scaledFT0M; + + const auto centFT0C = collision.centFT0C(); + const auto centFT0M = collision.centFT0M(); + + const auto detLevelCollRho = collision.rho(); + + //==================================================================================== + // Trigger-track information std::vector vPhiOfTT = getPhiOfAllTTsigCandidates(tracks); double phiTTSig = 0.0; - bool bIsThereTTSig = vPhiOfTT.size() > 0; + const bool bIsThereTTSig = !vPhiOfTT.empty(); - if (bIsThereTTSig) + if (bIsThereTTSig) { phiTTSig = getPhiTT(vPhiOfTT); + } - for (const auto& jetPart : jetsPart) { - auto partJetPt = jetPart.pt(); - auto partJetPtCorr = partJetPt - partLevelCollRho * jetPart.area(); - - bool bIsPartJetRecoil = - get<1>(isRecoilJet(jetPart, phiTTSig)) && bIsThereTTSig; + //==================================================================================== + // Particle-level jets + for (const auto& chJetPart : chJetsPart) { + const auto partJetPt = chJetPart.pt(); + const auto partJetPtCorr = partJetPt - partLevelCollRho * chJetPart.area(); + const bool bIsPartJetRecoil = bIsThereTTSig && get<1>(isRecoilJet(chJetPart, phiTTSig)); - // Distribution of all part. level jets + //================================================================================== + // All particle-level inclusive jets spectra.fill(HIST("hPartLevelInclusiveJetsPt"), partJetPt, weight); - spectra.fill(HIST("hPartLevelInclusiveJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_PartLevelInclusiveJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_PartLevelInclusiveJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_PartLevelInclusiveJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_PartLevelInclusiveJetsPtCorr"), centFT0M, partJetPtCorr, weight); + //================================================================================== + // All particle-level recoil jets if (bIsPartJetRecoil) { spectra.fill(HIST("hPartLevelRecoilJetsPt"), partJetPt, weight); - spectra.fill(HIST("hPartLevelRecoilJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_PartLevelRecoilJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_PartLevelRecoilJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_PartLevelRecoilJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_PartLevelRecoilJetsPtCorr"), centFT0M, partJetPtCorr, weight); } - if (jetPart.has_matchedJetGeo()) { - const auto& jetsDetMatched = jetPart.template matchedJetGeo_as(); - - for (const auto& jetDetMatched : jetsDetMatched) { - // Skip matches where detector level jets have a constituent with pT above specified cut - const bool skipMatchedDetJet = isJetWithHighPtConstituent(jetDetMatched); - - if (skipMatchedDetJet) { - // Miss jets - spectra.fill(HIST("hMissedInclusiveJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedInclusiveJetsPtCorr"), partJetPtCorr, weight); - - if (bIsPartJetRecoil) { - spectra.fill(HIST("hMissedRecoilJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedRecoilJetsPtCorr"), partJetPtCorr, weight); - } - - } else { - auto detJetPt = jetDetMatched.pt(); - auto detJetPtCorr = detJetPt - detLevelCollRho * jetDetMatched.area(); - - spectra.fill(HIST("hNumberMatchedInclusiveDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); + bool hasAcceptedDetMatch = false; - spectra.fill(HIST("hResponseMatrixInclusiveJetsPt"), detJetPt, partJetPt, weight); - spectra.fill(HIST("hResponseMatrixInclusiveJetsPtCorr"), detJetPtCorr, partJetPtCorr, weight); + //================================================================================== + // Matched detector-level jets + if (chJetPart.has_matchedJetGeo()) { + const auto& jetsDetMatched = chJetPart.template matchedJetGeo_as(); - spectra.fill(HIST("hInclusiveJESPt"), (partJetPt - detJetPt) / partJetPt, partJetPt, weight); - spectra.fill(HIST("hInclusiveJESPtCorr"), (partJetPtCorr - detJetPtCorr) / partJetPtCorr, partJetPtCorr, weight); - - spectra.fill(HIST("hInclusiveJESPhi"), jetPart.phi() - jetDetMatched.phi(), partJetPt, weight); - - if (bIsPartJetRecoil) { - spectra.fill(HIST("hNumberMatchedRecoilDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); - - spectra.fill(HIST("hResponseMatrixRecoilJetsPt"), detJetPt, partJetPt, weight); - spectra.fill(HIST("hResponseMatrixRecoilJetsPtCorr"), detJetPtCorr, partJetPtCorr, weight); - - spectra.fill(HIST("hRecoilJESPt"), (partJetPt - detJetPt) / partJetPt, partJetPt, weight); - spectra.fill(HIST("hRecoilJESPtCorr"), (partJetPtCorr - detJetPtCorr) / partJetPtCorr, partJetPtCorr, weight); + for (const auto& jetDetMatched : jetsDetMatched) { + if (isJetWithHighPtConstituent(jetDetMatched)) { + continue; + } - spectra.fill(HIST("hRecoilJESPhi"), jetPart.phi() - jetDetMatched.phi(), partJetPt, weight); - } + const auto detJetPt = jetDetMatched.pt(); + const auto detJetPtCorr = detJetPt - detLevelCollRho * jetDetMatched.area(); + const auto relJetSmearPt = (partJetPt - detJetPt) / partJetPt; + const auto relJetSmearPtCorr = (partJetPtCorr - detJetPtCorr) / partJetPtCorr; + const auto deltaPhi = RecoDecay::constrainAngle(chJetPart.phi() - jetDetMatched.phi(), -constants::math::PI); + hasAcceptedDetMatch = true; + + //================================================================================ + // QA data + spectra.fill(HIST("hNumberMatchedInclusiveDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); + + //================================================================================ + // Inclusive response matrix + spectra.fill(HIST("hResponseMatrixInclusiveJetsPt"), detJetPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0C_ResponseMatrixInclusiveJetsPtCorr"), scaledFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_ResponseMatrixInclusiveJetsPtCorr"), scaledFT0M, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_ResponseMatrixInclusiveJetsPtCorr"), centFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_ResponseMatrixInclusiveJetsPtCorr"), centFT0M, detJetPtCorr, partJetPtCorr, weight); + + //================================================================================ + // Inclusive JES and phi resolution + spectra.fill(HIST("hInclusiveJESPtCorr"), relJetSmearPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_InclusiveJESPt"), scaledFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_InclusiveJESPt"), scaledFT0M, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_InclusiveJESPt"), centFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_InclusiveJESPt"), centFT0M, relJetSmearPt, partJetPt, weight); + + spectra.fill(HIST("hScaledFT0C_InclusiveJESPhi"), scaledFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_InclusiveJESPhi"), scaledFT0M, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_InclusiveJESPhi"), centFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_InclusiveJESPhi"), centFT0M, deltaPhi, partJetPt, weight); + + //================================================================================ + // Recoil response, JES, phi resolution and QA data + if (bIsPartJetRecoil) { + spectra.fill(HIST("hResponseMatrixRecoilJetsPt"), detJetPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0C_ResponseMatrixRecoilJetsPtCorr"), scaledFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_ResponseMatrixRecoilJetsPtCorr"), scaledFT0M, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_ResponseMatrixRecoilJetsPtCorr"), centFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_ResponseMatrixRecoilJetsPtCorr"), centFT0M, detJetPtCorr, partJetPtCorr, weight); + + spectra.fill(HIST("hRecoilJESPtCorr"), relJetSmearPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_RecoilJESPt"), scaledFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_RecoilJESPt"), scaledFT0M, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_RecoilJESPt"), centFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_RecoilJESPt"), centFT0M, relJetSmearPt, partJetPt, weight); + + spectra.fill(HIST("hScaledFT0C_RecoilJESPhi"), scaledFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_RecoilJESPhi"), scaledFT0M, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_RecoilJESPhi"), centFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_RecoilJESPhi"), centFT0M, deltaPhi, partJetPt, weight); + + spectra.fill(HIST("hNumberMatchedRecoilDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); } } - } else { - // Miss jets + } + + //================================================================================== + // Missed particle-level jets: no matched detector level jet + if (!hasAcceptedDetMatch) { spectra.fill(HIST("hMissedInclusiveJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedInclusiveJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_MissedInclusiveJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_MissedInclusiveJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_MissedInclusiveJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_MissedInclusiveJetsPtCorr"), centFT0M, partJetPtCorr, weight); if (bIsPartJetRecoil) { spectra.fill(HIST("hMissedRecoilJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedRecoilJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_MissedRecoilJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_MissedRecoilJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_MissedRecoilJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_MissedRecoilJetsPtCorr"), centFT0M, partJetPtCorr, weight); } } } - // Reconstructed jets - for (const auto& jetDet : jetsDet) { - if (isJetWithHighPtConstituent(jetDet)) + //==================================================================================== + // Detector-level jets + for (const auto& chJetDet : chJetsDet) { + if (isJetWithHighPtConstituent(chJetDet)) { continue; + } - auto detJetPt = jetDet.pt(); - auto detJetPtCorr = detJetPt - detLevelCollRho * jetDet.area(); + const auto detJetPt = chJetDet.pt(); + const auto detJetPtCorr = detJetPt - detLevelCollRho * chJetDet.area(); - bool bIsJetRecoil = - get<1>(isRecoilJet(jetDet, phiTTSig)) && bIsThereTTSig; + const bool bIsJetRecoil = bIsThereTTSig && get<1>(isRecoilJet(chJetDet, phiTTSig)); - // Distribution of all det. level jets + //================================================================================== + // All detector-level inclusive jets spectra.fill(HIST("hDetLevelInclusiveJetsPt"), detJetPt, weight); - spectra.fill(HIST("hDetLevelInclusiveJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_DetLevelInclusiveJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_DetLevelInclusiveJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_DetLevelInclusiveJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DetLevelInclusiveJetsPtCorr"), centFT0M, detJetPtCorr, weight); + //================================================================================== + // All detector-level recoil jets if (bIsJetRecoil) { spectra.fill(HIST("hDetLevelRecoilJetsPt"), detJetPt, weight); - spectra.fill(HIST("hDetLevelRecoilJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_DetLevelRecoilJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_DetLevelRecoilJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_DetLevelRecoilJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DetLevelRecoilJetsPtCorr"), centFT0M, detJetPtCorr, weight); } - if (!jetDet.has_matchedJetGeo()) { + //================================================================================== + // Fake detector-level jets: no matched particle level jet + if (!chJetDet.has_matchedJetGeo()) { spectra.fill(HIST("hFakeInclusiveJetsPt"), detJetPt, weight); - spectra.fill(HIST("hFakeInclusiveJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_FakeInclusiveJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_FakeInclusiveJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_FakeInclusiveJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_FakeInclusiveJetsPtCorr"), centFT0M, detJetPtCorr, weight); if (bIsJetRecoil) { spectra.fill(HIST("hFakeRecoilJetsPt"), detJetPt, weight); - spectra.fill(HIST("hFakeRecoilJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_FakeRecoilJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_FakeRecoilJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_FakeRecoilJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_FakeRecoilJetsPtCorr"), centFT0M, detJetPtCorr, weight); } } } } - /// TODO: think how to implement matching matching geo/geo+pT in one function - template - void fillMatchedGeoPtHistograms(JetsPart const& jetsPart, - JetsDet const& jetsDet, + template + void fillMatchedGeoPtHistograms(JCollision const& collision, + ChJetsPart const& chJetsPart, + ChJetsDet const& chJetsDet, JTracks const& tracks, - float partLevelCollRho, - float detLevelCollRho, + const float partLevelCollRho, float weight = 1.) { - // Utilities for recoil jets; TTsis at det. level MC + //==================================================================================== + // Event properties + const auto ft0Metrics = getFT0Metrics(collision, ft0a.mean, ft0c.mean); + const auto scaledFT0C = ft0Metrics.scaledFT0C; + const auto scaledFT0M = ft0Metrics.scaledFT0M; + + const auto centFT0C = collision.centFT0C(); + const auto centFT0M = collision.centFT0M(); + + const auto detLevelCollRho = collision.rho(); + + //==================================================================================== + // Trigger-track information std::vector vPhiOfTT = getPhiOfAllTTsigCandidates(tracks); double phiTTSig = 0.0; - bool bIsThereTTSig = vPhiOfTT.size() > 0; + const bool bIsThereTTSig = !vPhiOfTT.empty(); - if (bIsThereTTSig) + if (bIsThereTTSig) { phiTTSig = getPhiTT(vPhiOfTT); + } - for (const auto& jetPart : jetsPart) { - auto partJetPt = jetPart.pt(); - auto partJetPtCorr = partJetPt - partLevelCollRho * jetPart.area(); - - bool bIsPartJetRecoil = - get<1>(isRecoilJet(jetPart, phiTTSig)) && bIsThereTTSig; + //==================================================================================== + // Particle-level jets + for (const auto& chJetPart : chJetsPart) { + const auto partJetPt = chJetPart.pt(); + const auto partJetPtCorr = partJetPt - partLevelCollRho * chJetPart.area(); + const bool bIsPartJetRecoil = bIsThereTTSig && get<1>(isRecoilJet(chJetPart, phiTTSig)); - // Distribution of all part. level jets + //================================================================================== + // All particle-level inclusive jets spectra.fill(HIST("hPartLevelInclusiveJetsPt"), partJetPt, weight); - spectra.fill(HIST("hPartLevelInclusiveJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_PartLevelInclusiveJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_PartLevelInclusiveJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_PartLevelInclusiveJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_PartLevelInclusiveJetsPtCorr"), centFT0M, partJetPtCorr, weight); + //================================================================================== + // All particle-level recoil jets if (bIsPartJetRecoil) { spectra.fill(HIST("hPartLevelRecoilJetsPt"), partJetPt, weight); - spectra.fill(HIST("hPartLevelRecoilJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_PartLevelRecoilJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_PartLevelRecoilJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_PartLevelRecoilJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_PartLevelRecoilJetsPtCorr"), centFT0M, partJetPtCorr, weight); } - // Geo + pT matching - if (jetPart.has_matchedJetGeo() && jetPart.has_matchedJetPt()) { - const auto& jetsDetMatched = jetPart.template matchedJetGeo_as(); - auto both = intersectMatchIds(jetPart.matchedJetGeoIds(), jetPart.matchedJetPtIds()); - - for (const auto& jetDetMatched : jetsDetMatched) { - // Skip matches where detector level jets have a constituent with pT above specified cut - const bool skipMatchedDetJet = isJetWithHighPtConstituent(jetDetMatched); - - // If indecies for geo and pT matching do not coinside, reject detector level jet - const bool skipNotBothId = !both.contains(jetDetMatched.globalIndex()); + bool hasAcceptedDetMatch = false; - if (skipMatchedDetJet || skipNotBothId) { - // Miss jets - spectra.fill(HIST("hMissedInclusiveJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedInclusiveJetsPtCorr"), partJetPtCorr, weight); + //================================================================================== + // Matched detector-level jets: geo + pT matching + if (chJetPart.has_matchedJetGeo() && chJetPart.has_matchedJetPt()) { + const auto& jetsDetMatched = chJetPart.template matchedJetGeo_as(); + const auto both = intersectMatchIds(chJetPart.matchedJetGeoIds(), chJetPart.matchedJetPtIds()); - if (bIsPartJetRecoil) { - spectra.fill(HIST("hMissedRecoilJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedRecoilJetsPtCorr"), partJetPtCorr, weight); - } - } else { - auto detJetPt = jetDetMatched.pt(); - auto detJetPtCorr = detJetPt - detLevelCollRho * jetDetMatched.area(); - - spectra.fill(HIST("hNumberMatchedInclusiveDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); - - spectra.fill(HIST("hResponseMatrixInclusiveJetsPt"), detJetPt, partJetPt, weight); - spectra.fill(HIST("hResponseMatrixInclusiveJetsPtCorr"), detJetPtCorr, partJetPtCorr, weight); - - spectra.fill(HIST("hInclusiveJESPt"), (partJetPt - detJetPt) / partJetPt, partJetPt, weight); - spectra.fill(HIST("hInclusiveJESPtCorr"), (partJetPtCorr - detJetPtCorr) / partJetPtCorr, partJetPtCorr, weight); - - spectra.fill(HIST("hInclusiveJESPhi"), jetPart.phi() - jetDetMatched.phi(), partJetPt, weight); - - if (bIsPartJetRecoil) { - spectra.fill(HIST("hNumberMatchedRecoilDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); - - spectra.fill(HIST("hResponseMatrixRecoilJetsPt"), detJetPt, partJetPt, weight); - spectra.fill(HIST("hResponseMatrixRecoilJetsPtCorr"), detJetPtCorr, partJetPtCorr, weight); + for (const auto& jetDetMatched : jetsDetMatched) { + if (!both.contains(jetDetMatched.globalIndex())) { + continue; + } - spectra.fill(HIST("hRecoilJESPt"), (partJetPt - detJetPt) / partJetPt, partJetPt, weight); - spectra.fill(HIST("hRecoilJESPtCorr"), (partJetPtCorr - detJetPtCorr) / partJetPtCorr, partJetPtCorr, weight); + if (isJetWithHighPtConstituent(jetDetMatched)) { + continue; + } - spectra.fill(HIST("hRecoilJESPhi"), jetPart.phi() - jetDetMatched.phi(), partJetPt, weight); - } + const auto detJetPt = jetDetMatched.pt(); + const auto detJetPtCorr = detJetPt - detLevelCollRho * jetDetMatched.area(); + const auto relJetSmearPt = (partJetPt - detJetPt) / partJetPt; + const auto relJetSmearPtCorr = (partJetPtCorr - detJetPtCorr) / partJetPtCorr; + const auto deltaPhi = RecoDecay::constrainAngle(chJetPart.phi() - jetDetMatched.phi(), -constants::math::PI); + hasAcceptedDetMatch = true; + + //================================================================================ + // QA data + spectra.fill(HIST("hNumberMatchedInclusiveDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); + + //================================================================================ + // Inclusive response matrix + spectra.fill(HIST("hResponseMatrixInclusiveJetsPt"), detJetPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0C_ResponseMatrixInclusiveJetsPtCorr"), scaledFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_ResponseMatrixInclusiveJetsPtCorr"), scaledFT0M, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_ResponseMatrixInclusiveJetsPtCorr"), centFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_ResponseMatrixInclusiveJetsPtCorr"), centFT0M, detJetPtCorr, partJetPtCorr, weight); + + //================================================================================ + // Inclusive JES and phi resolution + spectra.fill(HIST("hInclusiveJESPtCorr"), relJetSmearPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_InclusiveJESPt"), scaledFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_InclusiveJESPt"), scaledFT0M, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_InclusiveJESPt"), centFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_InclusiveJESPt"), centFT0M, relJetSmearPt, partJetPt, weight); + + spectra.fill(HIST("hScaledFT0C_InclusiveJESPhi"), scaledFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_InclusiveJESPhi"), scaledFT0M, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_InclusiveJESPhi"), centFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_InclusiveJESPhi"), centFT0M, deltaPhi, partJetPt, weight); + + //================================================================================ + // Recoil response, JES, phi resolution and QA data + if (bIsPartJetRecoil) { + spectra.fill(HIST("hResponseMatrixRecoilJetsPt"), detJetPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0C_ResponseMatrixRecoilJetsPtCorr"), scaledFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_ResponseMatrixRecoilJetsPtCorr"), scaledFT0M, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_ResponseMatrixRecoilJetsPtCorr"), centFT0C, detJetPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_ResponseMatrixRecoilJetsPtCorr"), centFT0M, detJetPtCorr, partJetPtCorr, weight); + + spectra.fill(HIST("hRecoilJESPtCorr"), relJetSmearPtCorr, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_RecoilJESPt"), scaledFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_RecoilJESPt"), scaledFT0M, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_RecoilJESPt"), centFT0C, relJetSmearPt, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_RecoilJESPt"), centFT0M, relJetSmearPt, partJetPt, weight); + + spectra.fill(HIST("hScaledFT0C_RecoilJESPhi"), scaledFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hScaledFT0M_RecoilJESPhi"), scaledFT0M, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0C_RecoilJESPhi"), centFT0C, deltaPhi, partJetPt, weight); + spectra.fill(HIST("hCentFT0M_RecoilJESPhi"), centFT0M, deltaPhi, partJetPt, weight); + + spectra.fill(HIST("hNumberMatchedRecoilDetJetsPerOnePartJet"), jetsDetMatched.size(), detJetPt, partJetPt, weight); } } - } else { - // Miss jets + } + + //================================================================================== + // Missed particle-level jets: no detector-level jet accepted by both geo and pT matching + if (!hasAcceptedDetMatch) { spectra.fill(HIST("hMissedInclusiveJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedInclusiveJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_MissedInclusiveJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_MissedInclusiveJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_MissedInclusiveJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_MissedInclusiveJetsPtCorr"), centFT0M, partJetPtCorr, weight); if (bIsPartJetRecoil) { spectra.fill(HIST("hMissedRecoilJetsPt"), partJetPt, weight); - spectra.fill(HIST("hMissedRecoilJetsPtCorr"), partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_MissedRecoilJetsPtCorr"), scaledFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_MissedRecoilJetsPtCorr"), scaledFT0M, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_MissedRecoilJetsPtCorr"), centFT0C, partJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_MissedRecoilJetsPtCorr"), centFT0M, partJetPtCorr, weight); } } } - // Reconstructed jets - for (const auto& jetDet : jetsDet) { - if (isJetWithHighPtConstituent(jetDet)) + //==================================================================================== + // Detector-level jets + for (const auto& chJetDet : chJetsDet) { + if (isJetWithHighPtConstituent(chJetDet)) { continue; + } - auto detJetPt = jetDet.pt(); - auto detJetPtCorr = detJetPt - detLevelCollRho * jetDet.area(); + const auto detJetPt = chJetDet.pt(); + const auto detJetPtCorr = detJetPt - detLevelCollRho * chJetDet.area(); - bool bIsJetRecoil = - get<1>(isRecoilJet(jetDet, phiTTSig)) && bIsThereTTSig; + const bool bIsJetRecoil = bIsThereTTSig && get<1>(isRecoilJet(chJetDet, phiTTSig)); - // Distribution of all det. level jets + //================================================================================== + // All detector-level inclusive jets spectra.fill(HIST("hDetLevelInclusiveJetsPt"), detJetPt, weight); - spectra.fill(HIST("hDetLevelInclusiveJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_DetLevelInclusiveJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_DetLevelInclusiveJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_DetLevelInclusiveJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DetLevelInclusiveJetsPtCorr"), centFT0M, detJetPtCorr, weight); + //================================================================================== + // All detector-level recoil jets if (bIsJetRecoil) { spectra.fill(HIST("hDetLevelRecoilJetsPt"), detJetPt, weight); - spectra.fill(HIST("hDetLevelRecoilJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_DetLevelRecoilJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_DetLevelRecoilJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_DetLevelRecoilJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_DetLevelRecoilJetsPtCorr"), centFT0M, detJetPtCorr, weight); + } + + //================================================================================== + // Fake detector-level jets: no matched particle-level jet accepted by both geo and pT matching + bool hasAcceptedPartMatch = false; + + if (chJetDet.has_matchedJetGeo() && chJetDet.has_matchedJetPt()) { + const auto both = intersectMatchIds(chJetDet.matchedJetGeoIds(), chJetDet.matchedJetPtIds()); + hasAcceptedPartMatch = !both.empty(); } - if (!jetDet.has_matchedJetGeo()) { + if (!hasAcceptedPartMatch) { spectra.fill(HIST("hFakeInclusiveJetsPt"), detJetPt, weight); - spectra.fill(HIST("hFakeInclusiveJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_FakeInclusiveJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_FakeInclusiveJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_FakeInclusiveJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_FakeInclusiveJetsPtCorr"), centFT0M, detJetPtCorr, weight); if (bIsJetRecoil) { spectra.fill(HIST("hFakeRecoilJetsPt"), detJetPt, weight); - spectra.fill(HIST("hFakeRecoilJetsPtCorr"), detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0C_FakeRecoilJetsPtCorr"), scaledFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hScaledFT0M_FakeRecoilJetsPtCorr"), scaledFT0M, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0C_FakeRecoilJetsPtCorr"), centFT0C, detJetPtCorr, weight); + spectra.fill(HIST("hCentFT0M_FakeRecoilJetsPtCorr"), centFT0M, detJetPtCorr, weight); } } } @@ -1509,22 +2335,22 @@ struct RecoilJets { const auto scaledFT0C = ft0Metrics.scaledFT0C; const auto scaledFT0M = ft0Metrics.scaledFT0M; - float multZNA = collision.multZNA(); - float multZNC = collision.multZNC(); - float multZNM = multZNA + multZNC; + const auto multZNA = collision.multZNA(); + const auto multZNC = collision.multZNC(); + const auto multZNM = multZNA + multZNC; - float multZPA = collision.multZPA(); - float multZPC = collision.multZPC(); - float multZPM = multZPA + multZPC; + const auto multZPA = collision.multZPA(); + const auto multZPC = collision.multZPC(); + const auto multZPM = multZPA + multZPC; // Individual distributions spectra.fill(HIST("hMultFT0A"), multFT0A, weight); spectra.fill(HIST("hMultFT0C"), multFT0C, weight); spectra.fill(HIST("hMultFT0M"), multFT0M, weight); - spectra.fill(HIST("hScaleMultFT0A"), scaledFT0A, weight); - spectra.fill(HIST("hScaleMultFT0C"), scaledFT0C, weight); - spectra.fill(HIST("hScaleMultFT0M"), scaledFT0M, weight); + spectra.fill(HIST("hScaledMultFT0A"), scaledFT0A, weight); + spectra.fill(HIST("hScaledMultFT0C"), scaledFT0C, weight); + spectra.fill(HIST("hScaledMultFT0M"), scaledFT0M, weight); spectra.fill(HIST("hMultZNA"), multZNA, weight); spectra.fill(HIST("hMultZNC"), multZNC, weight); @@ -1542,16 +2368,16 @@ struct RecoilJets { spectra.fill(HIST("hMultFT0C_vs_ZNC"), multFT0C, multZNC, weight); spectra.fill(HIST("hMultFT0M_vs_ZNM"), multFT0M, multZNM, weight); - spectra.fill(HIST("hScaleMultFT0A_vs_ZNA"), scaledFT0A, multZNA, weight); - spectra.fill(HIST("hScaleMultFT0C_vs_ZNC"), scaledFT0C, multZNC, weight); - spectra.fill(HIST("hScaleMultFT0M_vs_ZNM"), scaledFT0M, multZNM, weight); + spectra.fill(HIST("hScaledMultFT0A_vs_ZNA"), scaledFT0A, multZNA, weight); + spectra.fill(HIST("hScaledMultFT0C_vs_ZNC"), scaledFT0C, multZNC, weight); + spectra.fill(HIST("hScaledMultFT0M_vs_ZNM"), scaledFT0M, multZNM, weight); - spectra.fill(HIST("hScaleMultFT0A_vs_ZPA"), scaledFT0A, multZPA, weight); - spectra.fill(HIST("hScaleMultFT0C_vs_ZPC"), scaledFT0C, multZPC, weight); - spectra.fill(HIST("hScaleMultFT0M_vs_ZPM"), scaledFT0M, multZPM, weight); + spectra.fill(HIST("hScaledMultFT0A_vs_ZPA"), scaledFT0A, multZPA, weight); + spectra.fill(HIST("hScaledMultFT0C_vs_ZPC"), scaledFT0C, multZPC, weight); + spectra.fill(HIST("hScaledMultFT0M_vs_ZPM"), scaledFT0M, multZPM, weight); - spectra.fill(HIST("hScaleMultFT0M_vs_ZNA_vs_ZNC"), scaledFT0M, multZNA, multZNC, weight); - spectra.fill(HIST("hScaleMultFT0M_vs_ZPA_vs_ZPC"), scaledFT0M, multZPA, multZPC, weight); + spectra.fill(HIST("hScaledMultFT0M_vs_ZNA_vs_ZNC"), scaledFT0M, multZNA, multZNC, weight); + spectra.fill(HIST("hScaledMultFT0M_vs_ZPA_vs_ZPC"), scaledFT0M, multZPA, multZPC, weight); } template @@ -1563,21 +2389,19 @@ struct RecoilJets { spectra.fill(HIST("hMultFT0CPartLevel"), ft0Metrics.multFT0C, weight); spectra.fill(HIST("hMultFT0MPartLevel"), ft0Metrics.multFT0M, weight); - spectra.fill(HIST("hScaleMultFT0APartLevel"), ft0Metrics.scaledFT0A, weight); - spectra.fill(HIST("hScaleMultFT0CPartLevel"), ft0Metrics.scaledFT0C, weight); - spectra.fill(HIST("hScaleMultFT0MPartLevel"), ft0Metrics.scaledFT0M, weight); + spectra.fill(HIST("hScaledMultFT0APartLevel"), ft0Metrics.scaledFT0A, weight); + spectra.fill(HIST("hScaledMultFT0CPartLevel"), ft0Metrics.scaledFT0C, weight); + spectra.fill(HIST("hScaledMultFT0MPartLevel"), ft0Metrics.scaledFT0M, weight); } //============================================================================= // Event Activity QA analysis in raw OO data //============================================================================= - template + template void fillEventActivitySelectionQAHistograms(Collision const& collision, - BC const&, - ZDC const&, float weight = 1.) { - int runNumber = collision.multRunNumber(); + const auto runNumber = collision.multRunNumber(); int fillNumber = getBinNumberOnYaxisForGivenRun(spectra.get(HIST("hScaledFT0CPerRunPerSetOfFlags")), runNumber) - 0.5; // Same for FT0M distrib. // FT0 Signal @@ -1600,7 +2424,7 @@ struct RecoilJets { spectra.fill(HIST("hEventSelectionCountQA"), 0.5); - bool isGoodZvtxFT0vsPV = collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); + const auto isGoodZvtxFT0vsPV = collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); if (isGoodZvtxFT0vsPV) { spectra.fill(HIST("hFT0APerRunPerSetOfFlags"), multFT0A, fillNumber, 1.5, weight); spectra.fill(HIST("hFT0CPerRunPerSetOfFlags"), multFT0C, fillNumber, 1.5, weight); @@ -1613,7 +2437,7 @@ struct RecoilJets { spectra.fill(HIST("hEventSelectionCountQA"), 1.5); } - bool isNoSameBunchPileup = collision.selection_bit(aod::evsel::kNoSameBunchPileup); + const auto isNoSameBunchPileup = collision.selection_bit(aod::evsel::kNoSameBunchPileup); if (isNoSameBunchPileup) { spectra.fill(HIST("hFT0APerRunPerSetOfFlags"), multFT0A, fillNumber, 2.5, weight); spectra.fill(HIST("hFT0CPerRunPerSetOfFlags"), multFT0C, fillNumber, 2.5, weight); @@ -1626,7 +2450,7 @@ struct RecoilJets { spectra.fill(HIST("hEventSelectionCountQA"), 2.5); } - bool isNoCollInTimeRangeStandard = collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard); + const auto isNoCollInTimeRangeStandard = collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard); if (isNoCollInTimeRangeStandard) { spectra.fill(HIST("hFT0APerRunPerSetOfFlags"), multFT0A, fillNumber, 3.5, weight); @@ -1640,8 +2464,9 @@ struct RecoilJets { spectra.fill(HIST("hEventSelectionCountQA"), 3.5); } - if (!(isGoodZvtxFT0vsPV && isNoSameBunchPileup && isNoCollInTimeRangeStandard)) + if (!(isGoodZvtxFT0vsPV && isNoSameBunchPileup && isNoCollInTimeRangeStandard)) { return; + } spectra.fill(HIST("hEventSelectionCountQA"), 4.5); // All accepted events after 4 flags cut @@ -1684,13 +2509,13 @@ struct RecoilJets { spectra.fill(HIST("hTimeZnaVsZncVsFT0M"), timeZNA, timeZNC, scaledFT0M, weight); // ITS only tracks - int nITSonly = collision.multNTracksITSOnly(); + const auto nITSonly = collision.multNTracksITSOnly(); spectra.fill(HIST("hScaledFT0C_ITStracks"), scaledFT0C, nITSonly, weight); spectra.fill(HIST("hScaledFT0M_ITStracks"), scaledFT0M, nITSonly, weight); // Global tracks from PV within |eta| < 0.8 - int multNContribs = collision.multNTracksPV(); + const auto multNContribs = collision.multNTracksPV(); spectra.fill(HIST("hScaledFT0C_TracksPV"), scaledFT0C, multNContribs, weight); spectra.fill(HIST("hScaledFT0M_TracksPV"), scaledFT0M, multNContribs, weight); @@ -1705,21 +2530,23 @@ struct RecoilJets { spectra.fill(HIST("hScaledFT0MsignalWithoutBC"), scaledFT0M, fillNumber, weight); } - if (collision.foundBCId() != -1) // -1 if does not + if (collision.foundBCId() != -1) { // -1 if does not spectra.fill(HIST("hIsFT0SignalComeFromCollPerRun"), 2.5, fillNumber, weight); - else + } else { spectra.fill(HIST("hIsFT0SignalComeFromCollPerRun"), 3.5, fillNumber, weight); + } } //============================================================================= // Di-hadron azimuthal correlation in raw and MC det. level (no weight; MB events) data //============================================================================= template - void fillLeadingAndAssociatedTracksTask(JCollision const& collision, JTracks const& tracks, float weight = 1.) + void fillLeadingAndAssociatedTracksTask(JCollision const& collision, + JTracks const& tracks, + float weight = 1.) { std::vector vPhiOfLeadingTracks; std::vector vPtOfLeadingTracks; - std::vector vPhiOfAssociatedTracks; const auto ft0Metrics = getFT0Metrics(collision, ft0a.mean, ft0c.mean); const auto scaledFT0C = ft0Metrics.scaledFT0C; @@ -1727,10 +2554,11 @@ struct RecoilJets { // Search for leading tracks for (const auto& track : tracks) { - if (skipTrack(track)) + if (skipTrack(track)) { continue; + } - float trackPt = track.pt(); + const auto trackPt = track.pt(); if (trackPt > twoPartCorrel.leadPtRange->at(0) && trackPt < twoPartCorrel.leadPtRange->at(1)) { vPhiOfLeadingTracks.push_back(track.phi()); @@ -1743,22 +2571,23 @@ struct RecoilJets { if (nLeadingTracks > 0) { auto indexLeadTrack = randGen->Integer(nLeadingTracks); - double phiLeadingTrack = vPhiOfLeadingTracks[indexLeadTrack]; - double pTLeadingTrack = vPtOfLeadingTracks[indexLeadTrack]; + const auto phiLeadingTrack = vPhiOfLeadingTracks[indexLeadTrack]; + const auto pTLeadingTrack = vPtOfLeadingTracks[indexLeadTrack]; spectra.fill(HIST("hScaledFT0C_NleadTracks"), scaledFT0C, 0.5, weight); spectra.fill(HIST("hScaledFT0M_NleadTracks"), scaledFT0M, 0.5, weight); for (const auto& track : tracks) { - if (skipTrack(track)) + if (skipTrack(track)) { continue; + } - float trackPt = track.pt(); - float trackPhi = track.phi(); + const auto trackPt = track.pt(); + const auto trackPhi = track.phi(); // Search for associated tracks if (trackPt > twoPartCorrel.associatTrackPtMin && trackPt < pTLeadingTrack) { - double dphi = RecoDecay::constrainAngle(phiLeadingTrack - trackPhi, -1.3); + const auto dphi = RecoDecay::constrainAngle(phiLeadingTrack - trackPhi, -1.3); spectra.fill(HIST("hScaledFT0C_Correlation_LeadTrack_AssociatTracks"), scaledFT0C, dphi, weight); spectra.fill(HIST("hScaledFT0M_Correlation_LeadTrack_AssociatTracks"), scaledFT0M, dphi, weight); } @@ -1771,14 +2600,14 @@ struct RecoilJets { //============================================================================= // Background fluctuations in raw data and MC det. level - template + template void fillBkgdFluctuations(JCollision const& collision, - Jets const& jets, + ChJets const& chJets, JTracks const& tracks, float weight = 1.) { //---------------------------------------------------------- - float rho = collision.rho(); + const auto rho = collision.rho(); const auto ft0Metrics = getFT0Metrics(collision, ft0a.mean, ft0c.mean); const auto scaledFT0C = ft0Metrics.scaledFT0C; const auto scaledFT0M = ft0Metrics.scaledFT0M; @@ -1797,8 +2626,10 @@ struct RecoilJets { uint64_t index = 0; for (const auto& track : tracks) { - if (skipTrack(track)) + ++index; + if (skipTrack(track)) { continue; + } float dEta = std::pow(randomConeEta - track.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(randomConePhi - track.phi(), -constants::math::PI), 2); @@ -1812,9 +2643,8 @@ struct RecoilJets { const auto ptTTsigMin = tt.sigPtRange->at(0); const auto ptTTsigMax = tt.sigPtRange->at(1); if (track.pt() > ptTTsigMin && track.pt() < ptTTsigMax) { - vCandForTT.emplace_back(index); + vCandForTT.emplace_back(index - 1); } - ++index; } spectra.fill(HIST("hScaledFT0C_deltaPtRandomCone"), scaledFT0C, randomConePt - areaRC * rho, weight); spectra.fill(HIST("hScaledFT0M_deltaPtRandomCone"), scaledFT0M, randomConePt - areaRC * rho, weight); @@ -1823,15 +2653,15 @@ struct RecoilJets { // Avoid leading jet (JE jet reconstruction sorts jets by pT) // square of distance to accept RC placement in events with leading jet - float dMinR2 = std::pow(jet.radius + bkgd.randomConeR + bkgd.minDeltaRToJet, 2); + float dMinR2 = std::pow(jets.radius + bkgd.randomConeR + bkgd.minDeltaRToJet, 2); // max # of attempts to find a place for RC; to avoid possibility with infinite loop in While cycle const int maxAttempts = 15000; - if (jets.size() > 0) // at least 1 jet + if (chJets.size() > 0) // at least 1 jet { - float leadJetEta = jets.iteratorAt(0).eta(); - float leadJetPhi = jets.iteratorAt(0).phi(); + float leadJetEta = chJets.iteratorAt(0).eta(); + float leadJetPhi = chJets.iteratorAt(0).phi(); float dEtaLeadJet = std::pow(leadJetEta - randomConeEta, 2); float dPhiLeadJet = std::pow(RecoDecay::constrainAngle(leadJetPhi - randomConePhi, -constants::math::PI), 2); @@ -1853,8 +2683,9 @@ struct RecoilJets { if (isTherePlaceForRC) { randomConePt = 0.0; for (const auto& track : tracks) { - if (skipTrack(track)) + if (skipTrack(track)) { continue; + } float dEta = std::pow(randomConeEta - track.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(randomConePhi - track.phi(), -constants::math::PI), 2); @@ -1875,8 +2706,9 @@ struct RecoilJets { float perpConePt = 0.0; for (const auto& track : tracks) { - if (skipTrack(track)) + if (skipTrack(track)) { continue; + } float dEta = std::pow(perpConeEta - track.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(perpConePhi - track.phi(), -constants::math::PI), 2); @@ -1892,34 +2724,21 @@ struct RecoilJets { //---------------------------------------------------------- // Avoid leading and subleading jets - if (jets.size() > 1) // at least 2 jets in an event + if (chJets.size() > 1) // at least 2 jets in an event { // Leading jet - float leadJetEta = jets.iteratorAt(0).eta(); - float leadJetPhi = jets.iteratorAt(0).phi(); + float leadJetEta = chJets.iteratorAt(0).eta(); + float leadJetPhi = chJets.iteratorAt(0).phi(); float dEtaLeadJet = std::pow(leadJetEta - randomConeEta, 2); float dPhiLeadJet = std::pow(RecoDecay::constrainAngle(leadJetPhi - randomConePhi, -constants::math::PI), 2); // Subleading jet - float subleadJetEta = jets.iteratorAt(1).eta(); - float subleadJetPhi = jets.iteratorAt(1).phi(); + float subleadJetEta = chJets.iteratorAt(1).eta(); + float subleadJetPhi = chJets.iteratorAt(1).phi(); float dEtaSubleadJet = std::pow(subleadJetEta - randomConeEta, 2); float dPhiSubleadJet = std::pow(RecoDecay::constrainAngle(subleadJetPhi - randomConePhi, -constants::math::PI), 2); - // Try to add events with TTsig - bool keepEventWithTT = false; - if (vCandForTT.size() > 0) // at least 1 TT - { - auto randIndexTrack = randGen->Integer(vCandForTT.size()); - auto objTT = tracks.iteratorAt(vCandForTT[randIndexTrack]); - - // Skip events where TT is not a part of leading or subleading jets (mutlijet event, difficult to place RC and avoid hard jets) - if (isTrackInJet(jets.iteratorAt(0), objTT) || isTrackInJet(jets.iteratorAt(1), objTT)) { - keepEventWithTT = true; - } - } - //---------------------------------------------------------- bool isTherePlaceForRC = false; for (int attempt = 0; attempt < maxAttempts; ++attempt) { @@ -1941,8 +2760,9 @@ struct RecoilJets { if (isTherePlaceForRC) { randomConePt = 0.0; for (const auto& track : tracks) { - if (skipTrack(track)) + if (skipTrack(track)) { continue; + } float dEta = std::pow(randomConeEta - track.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(randomConePhi - track.phi(), -constants::math::PI), 2); @@ -1954,18 +2774,45 @@ struct RecoilJets { } spectra.fill(HIST("hScaledFT0C_deltaPtRandomConeAvoidLeadAndSubleadJet"), scaledFT0C, randomConePt - areaRC * rho, weight); spectra.fill(HIST("hScaledFT0M_deltaPtRandomConeAvoidLeadAndSubleadJet"), scaledFT0M, randomConePt - areaRC * rho, weight); + } + } - if (keepEventWithTT) { - spectra.fill(HIST("hScaledFT0C_deltaPtRandomConeInEventTTSig"), scaledFT0C, randomConePt - areaRC * rho, weight); - spectra.fill(HIST("hScaledFT0M_deltaPtRandomConeInEventTTSig"), scaledFT0M, randomConePt - areaRC * rho, weight); + //---------------------------------------------------------- + // Place cone perpendicular to TTSig candidate + if (!vCandForTT.empty()) // at least 1 TT + { + auto randIndexTrack = randGen->Integer(vCandForTT.size()); + auto objTT = tracks.iteratorAt(vCandForTT[randIndexTrack]); + + float perpTTConeEta = objTT.eta(); + float perpTTConePhi = RecoDecay::constrainAngle(objTT.phi() + constants::math::PIHalf, 0.0f); + + // Keep the full cone inside the track acceptance + if (std::abs(perpTTConeEta) < (trk.etaCut - bkgd.randomConeR)) { + float perpTTConePt = 0.0; + for (const auto& track : tracks) { + if (skipTrack(track)) { + continue; + } + + float dEta = std::pow(perpTTConeEta - track.eta(), 2); + float dPhi = std::pow(RecoDecay::constrainAngle(perpTTConePhi - track.phi(), -constants::math::PI), 2); + + if ((dEta + dPhi) < radiusRC2) // inside TT perpendicular cone + { + perpTTConePt += track.pt(); + } } + + spectra.fill(HIST("hScaledFT0C_deltaPtPerpConeTTSig"), scaledFT0C, perpTTConePt - areaRC * rho, weight); + spectra.fill(HIST("hScaledFT0M_deltaPtPerpConeTTSig"), scaledFT0M, perpTTConePt - areaRC * rho, weight); } } } - template + template void fillBkgdFluctuationsMCPartLevel(JCollision const& collision, - Jets const& jets, + ChJets const& chJets, JParticles const& particles, float weight = 1.) { @@ -1989,8 +2836,9 @@ struct RecoilJets { uint64_t index = 0; for (const auto& particle : particles) { - if (skipParticle(particle)) + if (skipParticle(particle)) { continue; + } float dEta = std::pow(randomConeEta - particle.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(randomConePhi - particle.phi(), -constants::math::PI), 2); @@ -2015,15 +2863,15 @@ struct RecoilJets { // Avoid leading jet (JE jet reconstruction sorts jets by pT) // square of distance to accept RC placement in events with leading jet - float dMinR2 = std::pow(jet.radius + bkgd.randomConeR + bkgd.minDeltaRToJet, 2); + float dMinR2 = std::pow(jets.radius + bkgd.randomConeR + bkgd.minDeltaRToJet, 2); // max # of attempts to find a place for RC; to avoid possibility with infinite loop in While cycle const int maxAttempts = 15000; - if (jets.size() > 0) // at least 1 jet + if (chJets.size() > 0) // at least 1 jet { - float leadJetEta = jets.iteratorAt(0).eta(); - float leadJetPhi = jets.iteratorAt(0).phi(); + float leadJetEta = chJets.iteratorAt(0).eta(); + float leadJetPhi = chJets.iteratorAt(0).phi(); float dEtaLeadJet = std::pow(leadJetEta - randomConeEta, 2); float dPhiLeadJet = std::pow(RecoDecay::constrainAngle(leadJetPhi - randomConePhi, -constants::math::PI), 2); @@ -2045,14 +2893,14 @@ struct RecoilJets { if (isTherePlaceForRC) { randomConePt = 0.0; for (const auto& particle : particles) { - if (skipParticle(particle)) + if (skipParticle(particle)) { continue; + } float dEta = std::pow(randomConeEta - particle.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(randomConePhi - particle.phi(), -constants::math::PI), 2); - if ((dEta + dPhi) < radiusRC2) // inside RC - { + if ((dEta + dPhi) < radiusRC2) { // inside RC randomConePt += particle.pt(); } } @@ -2067,8 +2915,9 @@ struct RecoilJets { float perpConePt = 0.0; for (const auto& particle : particles) { - if (skipParticle(particle)) + if (skipParticle(particle)) { continue; + } float dEta = std::pow(perpConeEta - particle.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(perpConePhi - particle.phi(), -constants::math::PI), 2); @@ -2084,30 +2933,30 @@ struct RecoilJets { //---------------------------------------------------------- // Avoid leading and subleading jets - if (jets.size() > 1) // at least 2 jets in an event + if (chJets.size() > 1) // at least 2 jets in an event { // Leading jet - float leadJetEta = jets.iteratorAt(0).eta(); - float leadJetPhi = jets.iteratorAt(0).phi(); + float leadJetEta = chJets.iteratorAt(0).eta(); + float leadJetPhi = chJets.iteratorAt(0).phi(); float dEtaLeadJet = std::pow(leadJetEta - randomConeEta, 2); float dPhiLeadJet = std::pow(RecoDecay::constrainAngle(leadJetPhi - randomConePhi, -constants::math::PI), 2); // Subleading jet - float subleadJetEta = jets.iteratorAt(1).eta(); - float subleadJetPhi = jets.iteratorAt(1).phi(); + float subleadJetEta = chJets.iteratorAt(1).eta(); + float subleadJetPhi = chJets.iteratorAt(1).phi(); float dEtaSubleadJet = std::pow(subleadJetEta - randomConeEta, 2); float dPhiSubleadJet = std::pow(RecoDecay::constrainAngle(subleadJetPhi - randomConePhi, -constants::math::PI), 2); // Try to add events with TTsig bool keepEventWithTT = false; - if (vCandForTT.size() > 0) // at least 1 TT + if (!vCandForTT.empty()) // at least 1 TT { auto randIndexParticle = randGen->Integer(vCandForTT.size()); auto objTT = particles.iteratorAt(vCandForTT[randIndexParticle]); // Skip events where TT is not a part of leading or subleading jets (mutlijet event, difficult to place RC and avoid hard jets) - if (isTrackInJet(jets.iteratorAt(0), objTT) || isTrackInJet(jets.iteratorAt(1), objTT)) { + if (isTrackInJet(chJets.iteratorAt(0), objTT) || isTrackInJet(chJets.iteratorAt(1), objTT)) { keepEventWithTT = true; } } @@ -2133,8 +2982,9 @@ struct RecoilJets { if (isTherePlaceForRC) { randomConePt = 0.0; for (const auto& particle : particles) { - if (skipParticle(particle)) + if (skipParticle(particle)) { continue; + } float dEta = std::pow(randomConeEta - particle.eta(), 2); float dPhi = std::pow(RecoDecay::constrainAngle(randomConePhi - particle.phi(), -constants::math::PI), 2); @@ -2148,8 +2998,8 @@ struct RecoilJets { spectra.fill(HIST("hScaledFT0M_deltaPtRandomConeAvoidLeadAndSubleadJet_PartLevel"), scaledFT0M, randomConePt - areaRC * rho, weight); if (keepEventWithTT) { - spectra.fill(HIST("hScaledFT0C_deltaPtRandomConeInEventTTSig_PartLevel"), scaledFT0C, randomConePt - areaRC * rho, weight); - spectra.fill(HIST("hScaledFT0M_deltaPtRandomConeInEventTTSig_PartLevel"), scaledFT0M, randomConePt - areaRC * rho, weight); + spectra.fill(HIST("hScaledFT0C_deltaPtPerpConeTTSig_PartLevel"), scaledFT0C, randomConePt - areaRC * rho, weight); + spectra.fill(HIST("hScaledFT0M_deltaPtPerpConeTTSig_PartLevel"), scaledFT0M, randomConePt - areaRC * rho, weight); } } } @@ -2158,10 +3008,9 @@ struct RecoilJets { //============================================================================= // Pt and Phi smearing of TT //============================================================================= - template + template void fillTTSmearingPtPhi(JColl const& collision, JTracks const& tracks, - JParticles const&, float weight = 1.) { const auto ft0Metrics = getFT0Metrics(collision, ft0a.mean, ft0c.mean); @@ -2170,8 +3019,9 @@ struct RecoilJets { bool bSigEv = false; auto dice = randGen->Rndm(); - if (dice < tt.fracSig) + if (dice < tt.fracSig) { bSigEv = true; + } float ptTTMin = 0.0, ptTTMax = 0.0; if (bSigEv) { @@ -2208,15 +3058,17 @@ struct RecoilJets { } // Skip if no TT - if (chosenTTPos < 0) + if (chosenTTPos < 0) { return; + } bool bHasAssocMcPart = tracks.iteratorAt(chosenTTPos).has_mcParticle(); - if (!bHasAssocMcPart) + if (!bHasAssocMcPart) { return; + } // No filter on Particles, it can be outside of |eta| acceptance - auto particle = tracks.iteratorAt(chosenTTPos).mcParticle(); + auto particle = tracks.iteratorAt(chosenTTPos).template mcParticle_as(); float particleEta = particle.eta(); bool bPartWithinEta = std::fabs(particleEta) < trk.etaCut; @@ -2244,8 +3096,9 @@ struct RecoilJets { spectra.fill(HIST("hScaledFT0M_FractionOfPartTTSigSatisfCond"), scaledFT0M, 1.5, 1.5); } } - if (!bPartWithinEta) + if (!bPartWithinEta) { return; + } //============================================================================= // Fill histograms @@ -2257,23 +3110,95 @@ struct RecoilJets { float etaSmearing = particleEta - tracks.iteratorAt(chosenTTPos).eta(); if (bSigEv) { - spectra.fill(HIST("hScaleMultFT0C_PtSmearingTTSig"), scaledFT0C, relPtSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0M_PtSmearingTTSig"), scaledFT0M, relPtSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0C_PtSmearingTTSig"), scaledFT0C, relPtSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_PtSmearingTTSig"), scaledFT0M, relPtSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0C_PhiSmearingTTSig"), scaledFT0C, phiSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0M_PhiSmearingTTSig"), scaledFT0M, phiSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0C_PhiSmearingTTSig"), scaledFT0C, phiSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_PhiSmearingTTSig"), scaledFT0M, phiSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0C_EtaSmearingTTSig"), scaledFT0C, etaSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0M_EtaSmearingTTSig"), scaledFT0M, etaSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0C_EtaSmearingTTSig"), scaledFT0C, etaSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_EtaSmearingTTSig"), scaledFT0M, etaSmearing, particlePt, weight); } else { - spectra.fill(HIST("hScaleMultFT0C_PtSmearingTTRef"), scaledFT0C, relPtSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0M_PtSmearingTTRef"), scaledFT0M, relPtSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0C_PtSmearingTTRef"), scaledFT0C, relPtSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_PtSmearingTTRef"), scaledFT0M, relPtSmearing, particlePt, weight); + + spectra.fill(HIST("hScaledMultFT0C_PhiSmearingTTRef"), scaledFT0C, phiSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_PhiSmearingTTRef"), scaledFT0M, phiSmearing, particlePt, weight); + + spectra.fill(HIST("hScaledMultFT0C_EtaSmearingTTRef"), scaledFT0C, etaSmearing, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_EtaSmearingTTRef"), scaledFT0M, etaSmearing, particlePt, weight); + } + } + + //============================================================================= + // Track reconstruction efficiency as a function of EA / Centrality + //============================================================================= + template + void fillTrackRecoEff(JColl const& collision, + JTracks const& tracks, + JParticles const& particles, + float weight = 1.) + { + const auto ft0Metrics = getFT0Metrics(collision, ft0a.mean, ft0c.mean); + const auto scaledFT0C = ft0Metrics.scaledFT0C; + const auto scaledFT0M = ft0Metrics.scaledFT0M; + const auto mcCollisionId = collision.mcCollisionId(); + + // Numerator candidates: reconstructed tracks in this detector collision + for (const auto& trackPerColl : tracks) { + if (skipTrack(trackPerColl)) { + continue; + } + + auto trackPt = trackPerColl.pt(); - spectra.fill(HIST("hScaleMultFT0C_PhiSmearingTTRef"), scaledFT0C, phiSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0M_PhiSmearingTTRef"), scaledFT0M, phiSmearing, particlePt, weight); + // Case 1: no associated MC particle + if (!trackPerColl.has_mcParticle()) { + spectra.fill(HIST("hScaledMultFT0C_NoMcParticle"), scaledFT0C, trackPt, weight); + spectra.fill(HIST("hScaledMultFT0M_NoMcParticle"), scaledFT0M, trackPt, weight); + continue; + } + + // Case 2: MC particle exists, but belongs to another MC collision + auto particle = trackPerColl.mcParticle(); + if (particle.mcCollisionId() != mcCollisionId) { + spectra.fill(HIST("hScaledMultFT0C_MismatchedMcCollisionTracks"), scaledFT0C, trackPt, weight); + spectra.fill(HIST("hScaledMultFT0M_MismatchedMcCollisionTracks"), scaledFT0M, trackPt, weight); + continue; + } + + // Case 3: same MC collision, but particle fails phase-space cuts + float particleEta = particle.eta(); + bool bKeepParticle = std::fabs(particleEta) < trk.etaCut && !skipParticle(particle); + + if (!bKeepParticle) { + spectra.fill(HIST("hScaledMultFT0C_MatchedToRejectedTracks"), scaledFT0C, trackPt, weight); + spectra.fill(HIST("hScaledMultFT0M_MatchedToRejectedTracks"), scaledFT0M, trackPt, weight); + continue; + } - spectra.fill(HIST("hScaleMultFT0C_EtaSmearingTTRef"), scaledFT0C, etaSmearing, particlePt, weight); - spectra.fill(HIST("hScaleMultFT0M_EtaSmearingTTRef"), scaledFT0M, etaSmearing, particlePt, weight); + // Track has an associated MC particle, + // the particle belongs to the same MC collision, + // and the particle passes the generated-particle selection. + float particlePt = particle.pt(); + spectra.fill(HIST("hScaledMultFT0C_ResponseMatrixTrackEff"), scaledFT0C, trackPt, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_ResponseMatrixTrackEff"), scaledFT0M, trackPt, particlePt, weight); + } + + // Denominator: generated selected particles from the same MC collision + auto particlesInMcCollision = particles.sliceBy(particlesByMcCollision, mcCollisionId); + + for (const auto& particleInMcCollision : particlesInMcCollision) { + float particleEta = particleInMcCollision.eta(); + float particlePt = particleInMcCollision.pt(); + bool bKeepParticle = std::fabs(particleEta) < trk.etaCut && !skipParticle(particleInMcCollision); + + if (!bKeepParticle) { + continue; + } + + spectra.fill(HIST("hScaledMultFT0C_AllGeneratedParticlesPt"), scaledFT0C, particlePt, weight); + spectra.fill(HIST("hScaledMultFT0M_AllGeneratedParticlesPt"), scaledFT0M, particlePt, weight); } } @@ -2285,32 +3210,34 @@ struct RecoilJets { //============================================================================= void processData(CollRhoDataIt const& collision, TrackTbl const& tracksPerColl, - JetsDataTbl const& jetsPerColl) + ChJetsDataTbl const& chJetsPerColl) { spectra.fill(HIST("hEventSelectionCountReco"), 0.5); - if (skipEvent(collision)) + if (skipEvent(collision)) { return; + } spectra.fill(HIST("hEventSelectionCountReco"), 1.5); // number of events selected for analysis - fillHistograms(collision, jetsPerColl, tracksPerColl); + fillHistograms(collision, chJetsPerColl, tracksPerColl); } PROCESS_SWITCH(RecoilJets, processData, "process raw data", true); //____________________ void processMCDetLevel(CollRhoDataIt const& collision, TrackTbl const& tracksPerColl, - JetsDetTbl const& jetsPerColl) + ChJetsDetTbl const& chJetsPerColl) { spectra.fill(HIST("hEventSelectionCountReco"), 0.5); - if (skipEvent(collision)) + if (skipEvent(collision)) { return; + } spectra.fill(HIST("hEventSelectionCountReco"), 1.5); spectra.fill(HIST("hEventSelectionCountReco"), 4.5); // number of events selected for analysis - fillHistograms(collision, jetsPerColl, tracksPerColl); + fillHistograms(collision, chJetsPerColl, tracksPerColl); } PROCESS_SWITCH(RecoilJets, processMCDetLevel, "process MC det. level data (no weight; MB events)", false); @@ -2318,11 +3245,12 @@ struct RecoilJets { void processMCDetLevelWeighted(CollRhoOutlierDetIt const& collision, aod::JetMcCollisions const&, TrackTbl const& tracksPerColl, - JetsDetTbl const& jetsPerColl) + ChJetsDetTbl const& chJetsPerColl) { spectra.fill(HIST("hEventSelectionCountReco"), 0.5); - if (skipEvent(collision)) + if (skipEvent(collision)) { return; + } spectra.fill(HIST("hEventSelectionCountReco"), 1.5); @@ -2338,14 +3266,14 @@ struct RecoilJets { spectra.fill(HIST("hEventSelectionCountReco"), 4.5); // number of events selected for analysis auto weight = collision.mcCollision().weight(); - fillHistograms(collision, jetsPerColl, tracksPerColl, weight); + fillHistograms(collision, chJetsPerColl, tracksPerColl, weight); } PROCESS_SWITCH(RecoilJets, processMCDetLevelWeighted, "process MC det. level data (weighted JJ)", false); //_____________________ - void processMCPartLevel(CollRhoPartIt const& collision, + void processMCPartLevel(CollCentRhoPartIt const& collision, PartTbl const& particlesPerColl, - JetsPartTbl const& jetsPerColl) + ChJetsPartTbl const& chJetsPerColl) { spectra.fill(HIST("hEventSelectionCountPartLevel"), 0.5); @@ -2355,14 +3283,14 @@ struct RecoilJets { } spectra.fill(HIST("hEventSelectionCountPartLevel"), 3.5); // number of events selected for analysis - fillHistogramsMCPartLevel(collision, jetsPerColl, particlesPerColl); + fillHistogramsMCPartLevel(collision, chJetsPerColl, particlesPerColl); } PROCESS_SWITCH(RecoilJets, processMCPartLevel, "process MC part. level data (no weight; MB events)", false); //_____________________________ - void processMCPartLevelWeighted(CollRhoOutlierPartIt const& collision, + void processMCPartLevelWeighted(CollCentRhoOutlierPartIt const& collision, PartTbl const& particlesPerColl, - JetsPartTbl const& jetsPerColl) + ChJetsPartTbl const& chJetsPerColl) { spectra.fill(HIST("hEventSelectionCountPartLevel"), 0.5); @@ -2380,7 +3308,7 @@ struct RecoilJets { auto weight = collision.weight(); spectra.fill(HIST("ptHat"), collision.ptHard(), weight); - fillHistogramsMCPartLevel(collision, jetsPerColl, particlesPerColl, weight); + fillHistogramsMCPartLevel(collision, chJetsPerColl, particlesPerColl, weight); } PROCESS_SWITCH(RecoilJets, processMCPartLevelWeighted, "process MC part. level data (weighted JJ)", false); @@ -2390,23 +3318,26 @@ struct RecoilJets { void processJetsGeoMatching(CollRhoDetIt const& collision, CollRhoPartTbl const&, TrackTbl const& tracksPerColl, - MatchedJetsDetToPartTbl const& mcDetJetsPerColl, - MatchedJetsPartToDetTbl const& mcPartJets) + MatchedChJetsDetToPartTbl const& mcChJetsDetPerColl, + MatchedChJetsPartToDetTbl const& mcChJetsPart) { - // Skip detector level collisions - if (skipEvent(collision) || !collision.has_mcCollision()) + if (skipEvent(collision) || !collision.has_mcCollision()) { return; + } - auto mcCollisionId = collision.mcCollisionId(); - auto detLevelCollRho = collision.rho(); + const auto mcColl = collision.mcCollision_as(); - auto mcColl = collision.mcCollision_as(); - auto partLevelCollRho = mcColl.rho(); + if (std::fabs(mcColl.posZ()) > ev.vertexZCut) { + return; + } + + const auto partLevelCollRho = mcColl.rho(); // Slice for mc part level jets associated to a given mcCollisionId - auto mcPartJetsPerMcCollision = mcPartJets.sliceBy(partJetsPerMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) + const auto mcCollisionId = collision.mcCollisionId(); + const auto mcChJetsPartPerMcCollision = mcChJetsPart.sliceBy(partJetsByMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) - fillMatchedGeoHistograms(mcPartJetsPerMcCollision, mcDetJetsPerColl, tracksPerColl, partLevelCollRho, detLevelCollRho); + fillMatchedGeoHistograms(collision, mcChJetsPartPerMcCollision, mcChJetsDetPerColl, tracksPerColl, partLevelCollRho); } PROCESS_SWITCH(RecoilJets, processJetsGeoMatching, "process matching of MC jets using Geo criterion (no weight; MB events)", false); @@ -2414,23 +3345,26 @@ struct RecoilJets { void processJetsGeoPtMatching(CollRhoDetIt const& collision, CollRhoPartTbl const&, TrackTbl const& tracksPerColl, - MatchedJetsDetToPartTbl const& mcDetJetsPerColl, - MatchedJetsPartToDetTbl const& mcPartJets) + MatchedChJetsDetToPartTbl const& mcChJetsDetPerColl, + MatchedChJetsPartToDetTbl const& mcChJetsPart) { // Skip detector level collisions - if (skipEvent(collision) || !collision.has_mcCollision()) + if (skipEvent(collision) || !collision.has_mcCollision()) { return; + } - auto mcCollisionId = collision.mcCollisionId(); - auto detLevelCollRho = collision.rho(); + const auto mcColl = collision.mcCollision_as(); + if (std::fabs(mcColl.posZ()) > ev.vertexZCut) { + return; + } - auto mcColl = collision.mcCollision_as(); - auto partLevelCollRho = mcColl.rho(); + const auto partLevelCollRho = mcColl.rho(); // Slice for mc part level jets associated to a given mcCollisionId - auto mcPartJetsPerMcCollision = mcPartJets.sliceBy(partJetsPerMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) + const auto mcCollisionId = collision.mcCollisionId(); + const auto mcChJetsPartPerMcCollision = mcChJetsPart.sliceBy(partJetsByMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) - fillMatchedGeoPtHistograms(mcPartJetsPerMcCollision, mcDetJetsPerColl, tracksPerColl, partLevelCollRho, detLevelCollRho); + fillMatchedGeoPtHistograms(collision, mcChJetsPartPerMcCollision, mcChJetsDetPerColl, tracksPerColl, partLevelCollRho); } PROCESS_SWITCH(RecoilJets, processJetsGeoPtMatching, "process matching of MC jets using Geo+Pt criteria (no weight; MB events)", false); @@ -2438,24 +3372,27 @@ struct RecoilJets { void processJetsGeoMatchingWeighted(CollRhoOutlierDetIt const& collision, CollRhoOutlierPartTbl const&, TrackTbl const& tracksPerColl, - MatchedJetsDetToPartTbl const& mcDetJetsPerColl, - MatchedJetsPartToDetTbl const& mcPartJets) + MatchedChJetsDetToPartTbl const& mcChJetsDetPerColl, + MatchedChJetsPartToDetTbl const& mcChJetsPart) { // Skip detector level collisions - if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) + if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) { return; + } - auto mcCollisionId = collision.mcCollisionId(); - auto detLevelCollRho = collision.rho(); + const auto mcColl = collision.mcCollision_as(); + if (std::fabs(mcColl.posZ()) > ev.vertexZCut) { + return; + } - auto mcColl = collision.mcCollision_as(); - auto partLevelCollRho = mcColl.rho(); - auto weight = mcColl.weight(); + const auto partLevelCollRho = mcColl.rho(); + const auto weight = mcColl.weight(); // Slice for mc part level jets associated to a given mcCollisionId - auto mcPartJetsPerMcCollision = mcPartJets.sliceBy(partJetsPerMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) + const auto mcCollisionId = collision.mcCollisionId(); + const auto mcChJetsPartPerMcCollision = mcChJetsPart.sliceBy(partJetsByMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) - fillMatchedGeoHistograms(mcPartJetsPerMcCollision, mcDetJetsPerColl, tracksPerColl, partLevelCollRho, detLevelCollRho, weight); + fillMatchedGeoHistograms(collision, mcChJetsPartPerMcCollision, mcChJetsDetPerColl, tracksPerColl, partLevelCollRho, weight); } PROCESS_SWITCH(RecoilJets, processJetsGeoMatchingWeighted, "process matching of MC jets using Geo criterion (weighted JJ)", false); @@ -2463,24 +3400,27 @@ struct RecoilJets { void processJetsGeoPtMatchingWeighted(CollRhoOutlierDetIt const& collision, CollRhoOutlierPartTbl const&, TrackTbl const& tracksPerColl, - MatchedJetsDetToPartTbl const& mcDetJetsPerColl, - MatchedJetsPartToDetTbl const& mcPartJets) + MatchedChJetsDetToPartTbl const& mcChJetsDetPerColl, + MatchedChJetsPartToDetTbl const& mcChJetsPart) { // Skip detector level collisions - if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) + if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) { return; - - auto mcCollisionId = collision.mcCollisionId(); - auto detLevelCollRho = collision.rho(); + } auto mcColl = collision.mcCollision_as(); + if (std::fabs(mcColl.posZ()) > ev.vertexZCut) { + return; + } + auto partLevelCollRho = mcColl.rho(); auto weight = mcColl.weight(); // Slice for mc part level jets associated to a given mcCollisionId - auto mcPartJetsPerMcCollision = mcPartJets.sliceBy(partJetsPerMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) + auto mcCollisionId = collision.mcCollisionId(); + auto mcChJetsPartPerMcCollision = mcChJetsPart.sliceBy(partJetsByMcCollision, mcCollisionId); // signature: (__column to slice___, __index__) - fillMatchedGeoPtHistograms(mcPartJetsPerMcCollision, mcDetJetsPerColl, tracksPerColl, partLevelCollRho, detLevelCollRho, weight); + fillMatchedGeoPtHistograms(collision, mcChJetsPartPerMcCollision, mcChJetsDetPerColl, tracksPerColl, partLevelCollRho, weight); } PROCESS_SWITCH(RecoilJets, processJetsGeoPtMatchingWeighted, "process matching of MC jets using Geo+Pt criteria (weighted JJ)", false); @@ -2489,8 +3429,9 @@ struct RecoilJets { //============================================================================= void processEventActivity(EvMultZDCDataIt const& collision) { - if (skipEvent(collision)) + if (skipEvent(collision)) { return; + } fillMultiplicityHistograms(collision); } @@ -2500,8 +3441,9 @@ struct RecoilJets { void processEventActivityMCDetLevelWeighted(EvMultOutlierZDCDetIt const& collision, aod::JetMcCollisions const&) { - if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) + if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) { return; + } auto weight = collision.mcCollision().weight(); fillMultiplicityHistograms(collision, weight); @@ -2513,8 +3455,9 @@ struct RecoilJets { //============================================================================= void processEventActivityMCPartLevel(CollPartIt const& collision) { - if (skipMCEvent(collision)) + if (skipMCEvent(collision)) { return; + } fillMultiplicityHistogramsMCPartLevel(collision); } @@ -2523,8 +3466,9 @@ struct RecoilJets { //__________________________________________ void processEventActivityMCPartLevelWeighted(EvMultOutlierPartIt const& collision) { - if (skipMCEvent(collision) || collision.isOutlier()) + if (skipMCEvent(collision) || collision.isOutlier()) { return; + } auto weight = collision.weight(); fillMultiplicityHistogramsMCPartLevel(collision, weight); @@ -2535,14 +3479,15 @@ struct RecoilJets { // Event Activity QA analysis in raw OO //============================================================================= void processEventActivitySelectionQA(CollEvSelExtendedIt const& collision, - BCsRun3Tbl const& BCs, - aod::Zdcs const& ZDCs) + BCsRun3Tbl const&, + aod::Zdcs const&) { // Base flag for event selection - if (!collision.sel8()) + if (!collision.sel8()) { return; + } - fillEventActivitySelectionQAHistograms(collision, BCs, ZDCs); + fillEventActivitySelectionQAHistograms(collision); } PROCESS_SWITCH(RecoilJets, processEventActivitySelectionQA, "process function for EA QA purposes in raw OO collisions", false); @@ -2552,8 +3497,10 @@ struct RecoilJets { void processLeadingAndAssociatedTracksTask(CollDataIt const& collision, TrackTbl const& tracksPerColl) { - if (skipEvent(collision)) + if (skipEvent(collision)) { return; + } + fillLeadingAndAssociatedTracksTask(collision, tracksPerColl); } PROCESS_SWITCH(RecoilJets, processLeadingAndAssociatedTracksTask, "process di-hadron azimuthal correlation in raw and MC det. level (no weight; MB events) data", false); @@ -2563,24 +3510,26 @@ struct RecoilJets { //============================================================================= void processBkgdFluctuations(CollRhoDataIt const& collision, TrackTbl const& tracksPerColl, - JetsDataTbl const& jetsPerColl) + ChJetsDataTbl const& chJetsPerColl) { - if (skipEvent(collision)) + if (skipEvent(collision)) { return; + } - fillBkgdFluctuations(collision, jetsPerColl, tracksPerColl); + fillBkgdFluctuations(collision, chJetsPerColl, tracksPerColl); } PROCESS_SWITCH(RecoilJets, processBkgdFluctuations, "process raw data to estimate bkgd fluctuations", false); //____________________________________ void processBkgdFluctuationsMCDetLevel(CollRhoDataIt const& collision, TrackTbl const& tracksPerColl, - JetsDetTbl const& jetsPerColl) + ChJetsDetTbl const& chJetsPerColl) { - if (skipEvent(collision)) + if (skipEvent(collision)) { return; + } - fillBkgdFluctuations(collision, jetsPerColl, tracksPerColl); + fillBkgdFluctuations(collision, chJetsPerColl, tracksPerColl); } PROCESS_SWITCH(RecoilJets, processBkgdFluctuationsMCDetLevel, "process MC det. level (no weight; MB events) data to estimate bkgd fluctuations", false); @@ -2588,38 +3537,41 @@ struct RecoilJets { void processBkgdFluctuationsMCDetLevelWeighted(CollRhoOutlierDetIt const& collision, aod::JetMcCollisions const&, TrackTbl const& tracksPerColl, - JetsDetTbl const& jetsPerColl) + ChJetsDetTbl const& chJetsPerColl) { - if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) + if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) { return; + } auto weight = collision.mcCollision().weight(); - fillBkgdFluctuations(collision, jetsPerColl, tracksPerColl, weight); + fillBkgdFluctuations(collision, chJetsPerColl, tracksPerColl, weight); } PROCESS_SWITCH(RecoilJets, processBkgdFluctuationsMCDetLevelWeighted, "process MC det. level (weighted JJ) data to estimate bkgd fluctuations", false); //_____________________________________ void processBkgdFluctuationsMCPartLevel(CollRhoPartIt const& collision, PartTbl const& particlesPerColl, - JetsPartTbl const& jetsPerColl) + ChJetsPartTbl const& chJetsPerColl) { - if (skipMCEvent(collision)) + if (skipMCEvent(collision)) { return; + } - fillBkgdFluctuationsMCPartLevel(collision, jetsPerColl, particlesPerColl); + fillBkgdFluctuationsMCPartLevel(collision, chJetsPerColl, particlesPerColl); } PROCESS_SWITCH(RecoilJets, processBkgdFluctuationsMCPartLevel, "process MC part. level (no weight; MB events) data to estimate bkgd fluctuations", false); //_____________________________________________ void processBkgdFluctuationsMCPartLevelWeighted(CollRhoOutlierPartIt const& collision, PartTbl const& particlesPerColl, - JetsPartTbl const& jetsPerColl) + ChJetsPartTbl const& chJetsPerColl) { - if (skipMCEvent(collision) || collision.isOutlier()) + if (skipMCEvent(collision) || collision.isOutlier()) { return; + } auto weight = collision.weight(); - fillBkgdFluctuationsMCPartLevel(collision, jetsPerColl, particlesPerColl, weight); + fillBkgdFluctuationsMCPartLevel(collision, chJetsPerColl, particlesPerColl, weight); } PROCESS_SWITCH(RecoilJets, processBkgdFluctuationsMCPartLevelWeighted, "process MC part. level (weighted JJ) data to estimate bkgd fluctuations", false); @@ -2627,16 +3579,16 @@ struct RecoilJets { // Pt and Phi smearing of TT //============================================================================= void processTTSmearingPtPhi(CollDetIt const& collision, - aod::JetMcCollisions const&, TrackMCLbsTbl const& tracksPerColl, - aod::JetParticles const& particles) + aod::JetParticles const&) { // Skip detector level collisions - if (skipEvent(collision) || !collision.has_mcCollision()) + if (skipEvent(collision)) { return; + } - fillTTSmearingPtPhi(collision, tracksPerColl, particles); + fillTTSmearingPtPhi(collision, tracksPerColl); } PROCESS_SWITCH(RecoilJets, processTTSmearingPtPhi, "process MC data (no weight; MB events) to estimate pT and phi smearing of TT", false); @@ -2644,18 +3596,50 @@ struct RecoilJets { void processTTSmearingPtPhiWeighted(CollOutlierDetIt const& collision, aod::JetMcCollisions const&, TrackMCLbsTbl const& tracksPerColl, - aod::JetParticles const& particles) + aod::JetParticles const&) { // Skip detector level collisions - if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) + if (skipEvent(collision) || collision.isOutlier() || !collision.has_mcCollision()) { return; + } auto weight = collision.mcCollision().weight(); - fillTTSmearingPtPhi(collision, tracksPerColl, particles, weight); + fillTTSmearingPtPhi(collision, tracksPerColl, weight); } PROCESS_SWITCH(RecoilJets, processTTSmearingPtPhiWeighted, "process MC data (weighted JJ) to estimate pT and phi smearing of TT", false); + //_________________________________ + void processTrackingEfficiency(CollDetIt const& collision, + aod::JetMcCollisions const&, + TrackMCLbsTbl const& tracksPerColl, + aod::JetParticles const& particles) + { + // Skip detector level collision + if (skipEvent(collision) || !collision.has_mcCollision()) { + return; + } + + fillTrackRecoEff(collision, tracksPerColl, particles); + } + PROCESS_SWITCH(RecoilJets, processTrackingEfficiency, "process MC data (no weight; MB events) to estimate track reco eff", false); + + //_________________________________ + void processTrackingEfficiencyWeighted(CollOutlierDetIt const& collision, + aod::JetMcCollisions const&, + TrackMCLbsTbl const& tracksPerColl, + aod::JetParticles const& particles) + { + // Skip detector level collision + if (skipEvent(collision) || !collision.has_mcCollision() || collision.isOutlier()) { + return; + } + + const auto weight = collision.mcCollision().weight(); + fillTrackRecoEff(collision, tracksPerColl, particles, weight); + } + PROCESS_SWITCH(RecoilJets, processTrackingEfficiencyWeighted, "process MC data (weighted JJ) to estimate track reco eff", false); + //------------------------------------------------------------------------------ // Auxiliary functions template @@ -2689,9 +3673,9 @@ struct RecoilJets { } template - std::tuple isRecoilJet(const Jet& jet, double phiTT) + std::tuple isRecoilJet(const Jet& chJet, double phiTT) { - double dphi = std::fabs(RecoDecay::constrainAngle(jet.phi() - phiTT, -constants::math::PI)); + double dphi = std::fabs(RecoDecay::constrainAngle(chJet.phi() - phiTT, -constants::math::PI)); return {dphi, (constants::math::PI - tt.recoilRegion) < dphi}; } @@ -2742,7 +3726,7 @@ struct RecoilJets { { bool bIsJetWithHighPtConstituent = false; for (const auto& chJetConstituent : chJet.template tracks_as()) { - if (chJetConstituent.pt() > jet.constituentPtMax) { + if (chJetConstituent.pt() > jets.constituentPtMax) { bIsJetWithHighPtConstituent = true; break; } @@ -2751,9 +3735,9 @@ struct RecoilJets { } template - bool isTrackInJet(Jet const& jet, Track const& track) + bool isTrackInJet(Jet const& chJet, Track const& track) { - for (auto const& constituentId : jet.tracksIds()) { + for (auto const& constituentId : chJet.tracksIds()) { if (constituentId == track.globalIndex()) { return true; } @@ -2775,8 +3759,9 @@ struct RecoilJets { } } - if (binNumber == -1) // No bin found + if (binNumber == -1) { // No bin found return 0; + } return binNumber; } @@ -2787,8 +3772,9 @@ struct RecoilJets { std::vector vPhiOfTT; for (const auto& track : tracks) { - if (skipTrack(track)) + if (skipTrack(track)) { continue; + } // Search for TT_Sig candidate const auto ptTTsigMin = tt.sigPtRange->at(0); @@ -2813,8 +3799,9 @@ struct RecoilJets { const int dimOf2DHist = 2; bool isHist2D = histPointer->GetDimension() == dimOf2DHist; - if (isHist2D) + if (isHist2D) { return; + } for (int iFlag = 0; iFlag < nEvSelFlags; ++iFlag) { histPointer->GetZaxis()->SetBinLabel(iFlag + 1, zAxis[iFlag]); @@ -2841,6 +3828,42 @@ struct RecoilJets { } return bothSet; } + + // Check whether scaled FT0 belongs to the requested EA interval + bool isInEAInterval(float scaledFT0, + const std::vector& ft0Edges, + EAInterval eaInterval) + { + switch (eaInterval) { + case kMB: + return true; + + case kEA0_20: + return scaledFT0 >= ft0Edges[kEA20] && + scaledFT0 < ft0Edges[kEA0]; + + case kEA0_10: + return scaledFT0 >= ft0Edges[kEA10] && + scaledFT0 < ft0Edges[kEA0]; + + case kEA20_40: + return scaledFT0 >= ft0Edges[kEA40] && + scaledFT0 < ft0Edges[kEA20]; + + case kEA60_80: + return scaledFT0 >= ft0Edges[kEA80] && + scaledFT0 < ft0Edges[kEA60]; + + case kEA50_100: + return scaledFT0 >= ft0Edges[kEA100] && + scaledFT0 < ft0Edges[kEA50]; + + case kEA80_100: + return scaledFT0 >= ft0Edges[kEA100] && + scaledFT0 < ft0Edges[kEA80]; + } + return false; + } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGJE/Tasks/statPromptPhoton.cxx b/PWGJE/Tasks/statPromptPhoton.cxx index b7d6fce2dff..a09df380079 100644 --- a/PWGJE/Tasks/statPromptPhoton.cxx +++ b/PWGJE/Tasks/statPromptPhoton.cxx @@ -87,6 +87,16 @@ struct statPromptPhoton { Configurable cfgJETracks{"cfgJETracks", false, "Enables running on derived JE data"}; Configurable cfgGenHistograms{"cfgGenHistograms", false, "Enables Generated histograms"}; Configurable cfgGenReqRec{"cfgGenReqRec", false, "Only consider generated events which are successfully reconstructed"}; + Configurable cfgReqRecPS_REC{"cfgReqRecPS_REC", false, "Only consider reconstructed photons within the EMCAl acceptence"}; + Configurable cfgReqRecPS_GEN{"cfgReqRecPS_GEN", false, "Only consider generated photons within the EMCAl acceptence"}; + Configurable cfgEMClowPSphi{"cfgEMClowPSphi", 1.42, "lower limit of the EMC acceptance if Rec PS is required"}; + Configurable cfgEMChighPSphi{"cfgEMChighPSphi", 3.26, "higher limit of the EMC acceptance if Rec PS is required"}; + Configurable cfgEMChighPSeta{"cfgEMChighPSeta", 0.62, "symmetric eta cut if Rec PS is required"}; + Configurable cfgDClowPSphi{"cfgDClowPSphi", 4.56, "lower limit of the DCal acceptance if Rec PS is required"}; + Configurable cfgDChighPSphi{"cfgDChighPSphi", 5.70, "higher limit of the DCal acceptance if Rec PS is required"}; + Configurable cfgMCptNbins{"cfgMCptNbins", 200, "number of ptbins in MC QA plots"}; + Configurable cfgMCptbinLow{"cfgMCptbinLow", 5, "lower limit of ptbins in MC QA plots"}; + Configurable cfgMCptbinHigh{"cfgMCptbinHigh", 200, "upper limit of ptbins in MC QA plots"}; Configurable cfgRecHistograms{"cfgRecHistograms", false, "Enables Reconstructed histograms"}; Configurable cfgDataHistograms{"cfgDataHistograms", false, "Enables Data histograms"}; Configurable cfgTriggerMasks{"cfgTriggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; @@ -101,6 +111,8 @@ struct statPromptPhoton { std::vector ptBinning = {0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0, 4.0, 5.0, 6.0, 8.0, 12.0, 16.0, 20.0, 25.0, 30.0, 40.0, 50.0, 75.0, 100.0, 150.0, 200.0, 300.0, 500.0}; AxisSpec pthadAxis = {ptBinning, "#it{p}_{T}^{had sum} [GeV/c]"}; + const AxisSpec MCptAxis = {cfgMCptNbins, cfgMCptbinLow, cfgMCptbinHigh}; + triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(cfgTriggerMasks); if (cfgJETracks) { trackFilter = jetderiveddatautilities::initialiseTrackSelection(static_cast(cfgTrackFilter)); @@ -184,48 +196,48 @@ struct statPromptPhoton { histos.add("REC_dR_Stern", "REC_dR_Stern", kTH1F, {{628, 0.0, 2 * TMath::Pi()}}); histos.add("REC_prompt_phiQA", "REC_prompt_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_prompt_etaQA", "REC_prompt_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_prompt_ptQA", "REC_prompt_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_prompt_ptQA", "REC_prompt_ptQA", kTH1F, {MCptAxis}); histos.add("REC_decay_phiQA", "REC_decay_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_decay_etaQA", "REC_decay_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_decay_ptQA", "REC_decay_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_decay_ptQA", "REC_decay_ptQA", kTH1F, {MCptAxis}); histos.add("REC_frag_phiQA", "REC_frag_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_frag_etaQA", "REC_frag_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_frag_ptQA", "REC_frag_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_frag_ptQA", "REC_frag_ptQA", kTH1F, {MCptAxis}); histos.add("REC_direct_phiQA", "REC_direct_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_direct_etaQA", "REC_direct_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_direct_ptQA", "REC_direct_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_direct_ptQA", "REC_direct_ptQA", kTH1F, {MCptAxis}); histos.add("REC_cluster_phiQA", "REC_cluster_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_cluster_etaQA", "REC_cluster_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_cluster_energyQA", "REC_cluster_energyQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_cluster_energyQA", "REC_cluster_energyQA", kTH1F, {MCptAxis}); histos.add("REC_clusteriso_phiQA", "REC_clusteriso_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_clusteriso_etaQA", "REC_clusteriso_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_clusteriso_energyQA", "REC_clusteriso_energyQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_clusteriso_energyQA", "REC_clusteriso_energyQA", kTH1F, {MCptAxis}); histos.add("REC_track_phiQA", "REC_track_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_track_etaQA", "REC_track_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_track_ptQA", "REC_track_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_track_ptQA", "REC_track_ptQA", kTH1F, {MCptAxis}); histos.add("REC_cluster_direct_phiQA", "REC_cluster_direct_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_cluster_direct_etaQA", "REC_cluster_direct_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_cluster_direct_energyQA", "REC_cluster_direct_energyQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_cluster_direct_energyQA", "REC_cluster_direct_energyQA", kTH1F, {MCptAxis}); histos.add("REC_cluster_frag_phiQA", "REC_cluster_frag_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_cluster_frag_etaQA", "REC_cluster_frag_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_cluster_frag_energyQA", "REC_cluster_frag_energyQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_cluster_frag_energyQA", "REC_cluster_frag_energyQA", kTH1F, {MCptAxis}); histos.add("REC_cluster_both_phiQA", "REC_cluster_both_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("REC_cluster_both_etaQA", "REC_cluster_both_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("REC_cluster_both_energyQA", "REC_cluster_both_energyQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("REC_cluster_both_energyQA", "REC_cluster_both_energyQA", kTH1F, {MCptAxis}); } if (cfgGenHistograms) { histos.add("GEN_prompt_phiQA", "GEN_prompt_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("GEN_prompt_etaQA", "GEN_prompt_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("GEN_prompt_ptQA", "GEN_prompt_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("GEN_prompt_ptQA", "GEN_prompt_ptQA", kTH1F, {MCptAxis}); histos.add("GEN_decay_phiQA", "GEN_decay_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("GEN_decay_etaQA", "GEN_decay_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("GEN_decay_ptQA", "GEN_decay_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("GEN_decay_ptQA", "GEN_decay_ptQA", kTH1F, {MCptAxis}); histos.add("GEN_frag_phiQA", "GEN_frag_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("GEN_frag_etaQA", "GEN_frag_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("GEN_frag_ptQA", "GEN_frag_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("GEN_frag_ptQA", "GEN_frag_ptQA", kTH1F, {MCptAxis}); histos.add("GEN_direct_phiQA", "GEN_direct_phiQA", kTH1F, {{640 * 2, 0, 2 * TMath::Pi()}}); histos.add("GEN_direct_etaQA", "GEN_direct_etaQA", kTH1F, {{100, -1, 1}}); - histos.add("GEN_direct_ptQA", "GEN_direct_ptQA", kTH1F, {{82, -1.0, 40.0}}); + histos.add("GEN_direct_ptQA", "GEN_direct_ptQA", kTH1F, {MCptAxis}); histos.add("GEN_nEvents", "GEN_nEvents", kTH1F, {{4, 0.0, 4.0}}); histos.add("GEN_nEvents_simple", "GEN_nEvents", kTH1F, {{4, 0.0, 4.0}}); histos.add("GEN_True_Trigger_Energy", "GEN_True_Trigger_Energy", kTH1F, {{82, -1.0, 40.0}}); @@ -414,7 +426,7 @@ struct statPromptPhoton { ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// template - bool trackSelection(const TrackType track) + bool trackSelection(const TrackType& track) { // basic track cuts if (track.pt() < cfgtrkMinPt) @@ -1513,6 +1525,15 @@ struct statPromptPhoton { continue; if (std::fabs(mcParticle.getGenStatusCode()) >= 81 || !mcParticle.isPhysicalPrimary()) continue; + if (cfgReqRecPS_GEN) { + if (std::fabs(mcParticle.eta()) > cfgEMChighPSeta) + continue; + bool insideCalPhi = false; + if ((mcParticle.phi() > cfgEMClowPSphi && mcParticle.phi() < cfgEMChighPSphi) || (mcParticle.phi() > cfgDClowPSphi && mcParticle.phi() < cfgDChighPSphi)) + insideCalPhi = true; + if (!insideCalPhi) + continue; + } // Chase this final-state photon upward int chaseindex = -1; @@ -1629,6 +1650,15 @@ struct statPromptPhoton { continue; if (std::fabs(clusterparticle.getGenStatusCode()) >= 81) continue; + if (cfgReqRecPS_REC) { + if (std::fabs(clusterparticle.eta()) > cfgEMChighPSeta) + continue; + bool insideCalPhi = false; + if ((clusterparticle.phi() > cfgEMClowPSphi && clusterparticle.phi() < cfgEMChighPSphi) || (clusterparticle.phi() > cfgDClowPSphi && clusterparticle.phi() < cfgDChighPSphi)) + insideCalPhi = true; + if (!insideCalPhi) + continue; + } int chaseindex = -1; for (auto& mom : clusterparticle.mothers_as()) { diff --git a/PWGJE/Tasks/substructureDebug.cxx b/PWGJE/Tasks/substructureDebug.cxx index 0243cce3c1a..b5943283e73 100644 --- a/PWGJE/Tasks/substructureDebug.cxx +++ b/PWGJE/Tasks/substructureDebug.cxx @@ -84,8 +84,8 @@ struct SubstructureDebugTask { void init(o2::framework::InitContext&) { - eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + eventSelection = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); } void processDataCharged(aod::CJetCOs::iterator const& collision, soa::Join const& jets) diff --git a/PWGJE/Tasks/taskEmcExtensiveMcQa.cxx b/PWGJE/Tasks/taskEmcExtensiveMcQa.cxx index f9845724101..0061742548c 100644 --- a/PWGJE/Tasks/taskEmcExtensiveMcQa.cxx +++ b/PWGJE/Tasks/taskEmcExtensiveMcQa.cxx @@ -21,6 +21,7 @@ #include "Common/CCDB/ctpRateFetcher.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include #include @@ -52,7 +53,7 @@ using namespace o2::framework::expressions; using namespace o2::constants; using namespace o2::hf_evsel; using namespace o2::hf_centrality; -using CollisionEvSels = o2::soa::Join; +using CollisionEvSels = o2::soa::Join; using BcEvSelIt = o2::soa::Join::iterator; using SelectedClusters = o2::soa::Filtered>; diff --git a/PWGJE/Tasks/trackEfficiency.cxx b/PWGJE/Tasks/trackEfficiency.cxx index c1da7482405..05de663ba17 100644 --- a/PWGJE/Tasks/trackEfficiency.cxx +++ b/PWGJE/Tasks/trackEfficiency.cxx @@ -34,9 +34,11 @@ #include #include +#include #include #include +#include #include #include @@ -55,6 +57,7 @@ struct TrackEfficiency { Configurable trackSelections{"trackSelections", "globalTracks", "set track selections; other option: uniformTracks"}; Configurable skipMBGapEvents{"skipMBGapEvents", false, "flag to choose to reject min. bias gap events"}; Configurable applyRCTSelections{"applyRCTSelections", true, "decide to apply RCT selections"}; + Configurable rctSelection{"rctSelection", "CBT", "RCT selection to apply if applyRCTSelections is set to true"}; // Tracking efficiency process function configurables: Configurable checkPrimaryPart{"checkPrimaryPart", true, "0: doesn't check mcparticle.isPhysicalPrimary() - 1: checks particle.isPhysicalPrimary()"}; @@ -67,7 +70,9 @@ struct TrackEfficiency { Configurable centralityMax{"centralityMax", 999, ""}; Configurable vertexZCut{"vertexZCut", 10.0f, "Accepted z-vertex range"}; Configurable trackDcaZmax{"trackDcaZmax", 99, "additional cut on dcaZ to PV for tracks; uniformTracks in particular don't cut on this at all"}; - Configurable nBinsLowPt{"nBinsLowPt", 200, "number of pt bins for low pt (below 10GeV) efficiency histograms"}; + Configurable ptHighEffMax{"ptHighEffMax", 100., "maximum pT in efficiency histograms for high pT (minimum is set by upper limit of ptLowEffBinning parameter)"}; + Configurable ptHighEffNBins{"ptHighEffNBins", 18, "number of pt bins in efficiency histograms for high pT"}; + ConfigurableAxis ptLowEffBinning{"ptLowEffBinning", {200, 0., 10.}, "binning of low pT axis in efficiency histograms"}; // Track QA process function configurables: Configurable trackQAEtaMin{"trackQAEtaMin", -0.9, "minimum eta acceptance for tracks in the processTracks QA"}; @@ -77,11 +82,10 @@ struct TrackEfficiency { Configurable trackOccupancyInTimeRangeMax{"trackOccupancyInTimeRangeMax", 999999, "maximum occupancy of tracks in neighbouring collisions in a given time range; only applied for reconstructed tracks, not mc particles"}; Configurable trackOccupancyInTimeRangeMin{"trackOccupancyInTimeRangeMin", -999999, "minimum occupancy of tracks in neighbouring collisions in a given time range; only applied for reconstructed tracks, not mc particles"}; - Configurable> centralityBinning{"centralityBinning", {0., 10., 50., 70., 100}, "binning of centrality histograms"}; - Configurable intRateNBins{"intRateNBins", 50, "number of bins for interaction rate axis"}; - Configurable intRateMax{"intRateMax", 50000.0, "maximum value of interaction rate axis"}; - Configurable phiEffNBins{"phiEffNBins", 200, "number of bins for phi axis in efficiency plots"}; - Configurable etaEffNBins{"etaEffNBins", 200, "number of bins for eta axis in efficiency plots"}; + ConfigurableAxis centralityBinning{"centralityBinning", {VARIABLE_WIDTH, 0., 10., 50., 70., 100}, "binning of centrality histograms"}; + ConfigurableAxis intRateBinning{"intRateBinning", {50, 0., 50000.0}, "binning for interaction rate axis"}; + ConfigurableAxis phiEffBinning{"phiEffBinning", {200, -1., 7.}, "binning for phi axis in efficiency histograms"}; + ConfigurableAxis etaEffBinning{"etaEffBinning", {200, -1., 1.}, "binning for eta axis in efficiency histograms"}; Configurable ptHatMin{"ptHatMin", -999, "min pT hat of collisions"}; Configurable ptHatMax{"ptHatMax", 999, "max pT hat of collisions"}; @@ -92,6 +96,32 @@ struct TrackEfficiency { Configurable useTrueTrackWeight{"useTrueTrackWeight", true, "test configurable, should be set to 1 then config removed once well tested"}; + // Normalization FT0 by means + struct FT0A : ConfigurableGroup { + std::string prefix = "ft0a"; + Configurable mean{"mean", 3499., "Mean FT0A signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 2640., "Mean FT0A signal at MC detector level"}; + } ft0a; + + struct FT0C : ConfigurableGroup { + std::string prefix = "ft0c"; + Configurable mean{"mean", 844.6, "Mean FT0C signal"}; + Configurable meanMCDetLevel{"meanMCDetLevel", 802.9, "Mean FT0C signal at MC detector level"}; + } ft0c; + + // FT0C event activity thresholds + struct Hist : ConfigurableGroup { + std::string prefix = "hist"; + + // Raw data + ConfigurableAxis multFT0CThresh{"multFT0CThresh", {VARIABLE_WIDTH, 0.0, 0.109421, 0.215022, 0.343214, 0.50515, 0.712277, 0.978245, 1.3189, 1.75744, 2.36001, 3.59696, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThresh{"multFT0MThresh", {VARIABLE_WIDTH, 0.0, 0.130987, 0.237916, 0.36664, 0.528441, 0.734705, 0.996611, 1.32785, 1.74896, 2.31024, 3.37852, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + + // MC detector-level data + ConfigurableAxis multFT0CThreshMCDetLevel{"multFT0CThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.124992, 0.243769, 0.380291, 0.543998, 0.748078, 1.00424, 1.3237, 1.72648, 2.27373, 3.43144, 25.}, "Percentiles of scaled FT0C: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + ConfigurableAxis multFT0MThreshMCDetLevel{"multFT0MThreshMCDetLevel", {VARIABLE_WIDTH, 0.0, 0.142622, 0.262095, 0.397069, 0.558622, 0.762008, 1.01685, 1.33219, 1.72377, 2.24008, 3.26309, 25.}, "Percentiles of scaled FT0M: 100%, 90%, 80%, 70%, 60%, 50%, 40%, 30%, 20%, 10%, 1%"}; + } hist; + // systematics variation - Run 2 guidelines: https://twiki.cern.ch/twiki/bin/view/ALICE/AliDPGtoolsTrackSystematicUncertainty TrackSelection customTrackSelection; Configurable useCustomTrackSelection{"useCustomTrackSelection", false, "whether to use the custom cuts (used for cut variation for tracking efficiency systematics)"}; @@ -106,6 +136,7 @@ struct TrackEfficiency { std::vector eventSelectionBits; int trackSelection = -1; + float pTHatSettingSentinelValue = 999.0; enum AcceptSplitCollisionsOptions { NonSplitOnly = 0, @@ -113,6 +144,11 @@ struct TrackEfficiency { SplitOkCheckFirstAssocCollOnly // 2 }; + enum class TrackSample { + Data, + MCDetLevel + }; + template bool isAcceptedTrack(TJetTrack const& jetTrack) { @@ -122,8 +158,8 @@ struct TrackEfficiency { } } else { const auto& aodTrack = jetTrack.template track_as>(); - if (effSystMinNCrossedRowsTPCUseAlternateCut) { - customTrackSelection.SetMinNCrossedRowsTPC(120 - 5. / jetTrack.pt()); + if (effSystMinNCrossedRowsTPCUseAlternateCut && (aodTrack.tpcNClsCrossedRows() < 120 - 5. / aodTrack.pt())) { // o2-linter: disable=magic-number (TPC nCrossedRows calculation) + return false; } if (customTrackSelection.IsSelected(aodTrack)) { return true; @@ -143,19 +179,20 @@ struct TrackEfficiency { return std::abs(charge) >= chargeUnit; } - template + template void fillTrackHistograms(TCollision const& collision, TJetTracks const& jetTracks, float weight = 1.0) { + const float meanFT0A = sample == TrackSample::Data ? ft0a.mean.value : ft0a.meanMCDetLevel.value; + const float meanFT0C = sample == TrackSample::Data ? ft0c.mean.value : ft0c.meanMCDetLevel.value; + const auto ft0Metrics = getFT0Metrics(collision, meanFT0A, meanFT0C); + const auto scaledFT0C = ft0Metrics.scaledFT0C; + const auto scaledFT0M = ft0Metrics.scaledFT0M; + for (auto const& track : jetTracks) { if (!isAcceptedTrack(track)) { continue; } - float pTHat = simPtRef / (std::pow(weight, 1.0 / pTHatExponent)); - if (track.pt() > pTHatMaxFractionMCD * pTHat) { - continue; - } - float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); registry.fill(HIST("h2_centrality_track_pt"), centrality, track.pt(), weight); @@ -167,11 +204,20 @@ struct TrackEfficiency { registry.fill(HIST("h2_track_pt_high_track_sigma1overpt"), track.pt(), track.sigma1Pt(), weight); registry.fill(HIST("h2_track_pt_high_track_sigmapt"), track.pt(), track.sigma1Pt() * track.pt(), weight); registry.fill(HIST("h3_intrate_centrality_track_pt"), collision.hadronicRate(), centrality, track.pt(), weight); + registry.fill(HIST("h3_track_pt_track_eta_track_phi"), track.pt(), track.eta(), track.phi(), weight); + + if constexpr (sample == TrackSample::Data) { + registry.fill(HIST("hScaledFT0CTrackPt"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt"), scaledFT0M, track.pt(), weight); + } else { + registry.fill(HIST("hScaledFT0CTrackPt_MCDetLevel"), scaledFT0C, track.pt(), weight); + registry.fill(HIST("hScaledFT0MTrackPt_MCDetLevel"), scaledFT0M, track.pt(), weight); + } } } template - void fillParticlesHistograms(TMCCollision const& /*mcCollision*/, TCollisions const& collisions, TParticles const& mcparticles, TTracks tracks, float weight = 1.0) + void fillParticlesHistograms(TMCCollision const& /*mcCollision*/, TCollisions const& collisions, TParticles const& mcparticles, const TTracks& tracks, float weight = 1.0) { // float centrality = checkCentFT0M ? mcCollision.centFT0M() : mcCollision.centFT0C(); mcCollision.centFT0C() isn't filled at the moment; can be added back when it is float centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -200,8 +246,14 @@ struct TrackEfficiency { LOGP(fatal, "Configurable acceptSplitCollisions has wrong input value; stopping workflow"); } - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(eventSelections)); - trackSelection = jetderiveddatautilities::initialiseTrackSelection(static_cast(trackSelections)); + constexpr float minFT0Mean = std::numeric_limits::epsilon(); + if (std::fabs(ft0a.mean.value) <= minFT0Mean || std::fabs(ft0c.mean.value) <= minFT0Mean || + std::fabs(ft0a.meanMCDetLevel.value) <= minFT0Mean || std::fabs(ft0c.meanMCDetLevel.value) <= minFT0Mean) { + LOGP(fatal, "FT0A and FT0C means must have magnitude greater than {} to avoid near-zero denominators; stopping workflow", minFT0Mean); + } + + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(eventSelections); + trackSelection = jetderiveddatautilities::initialiseTrackSelection(trackSelections); if (useCustomTrackSelection) { // Custom track cuts @@ -223,7 +275,11 @@ struct TrackEfficiency { customTrackSelection.SetEtaRange(-999, 999); customTrackSelection.SetPtRange(0, 1e10f); - customTrackSelection.SetMinNCrossedRowsTPC(effSystMinNCrossedRowsTPC.value); + if (effSystMinNCrossedRowsTPCUseAlternateCut) { + customTrackSelection.SetMinNCrossedRowsTPC(0.); + } else { + customTrackSelection.SetMinNCrossedRowsTPC(effSystMinNCrossedRowsTPC.value); + } customTrackSelection.SetMinNCrossedRowsOverFindableClustersTPC(effSystMinNCrossedRowsOverFindableClustersTPC.value); customTrackSelection.SetMaxChi2PerClusterTPC(effSystMaxChi2PerClusterTPC.value); customTrackSelection.SetMaxChi2PerClusterITS(effSystMaxChi2PerClusterITS.value); @@ -240,7 +296,22 @@ struct TrackEfficiency { LOGP(info, "Using standard track selection: %s", trackSelections.value); } - if (doprocessEFficiencyPurity || doprocessEFficiencyPurityWeighted) { + AxisSpec ptAxisEff = {ptLowEffBinning, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec ptAxisHighEff = {ptHighEffNBins, ptAxisEff.binEdges.back(), ptHighEffMax, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec etaAxisEff{etaEffBinning, "#eta"}; + AxisSpec phiAxisEff{phiEffBinning, "#phi"}; + AxisSpec centAxis = {centralityBinning, "centrality (%)"}; + AxisSpec scaledFT0C{hist.multFT0CThresh, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0M{hist.multFT0MThresh, "FT0M^{*}"}; + AxisSpec scaledFT0CMCDetLevel{hist.multFT0CThreshMCDetLevel, "FT0C / #LT FT0C #GT"}; + AxisSpec scaledFT0MMCDetLevel{hist.multFT0MThreshMCDetLevel, "FT0M^{*}"}; + AxisSpec intRateAxis = {intRateBinning, "int. rate (kHz)"}; + AxisSpec occupancyAxis = {140, -0.5, 13999.5, "occupancy"}; + AxisSpec nTracksAxis = {16001, -1., 16000, "n tracks"}; + AxisSpec dcaxyAxis = {1000, -1.0, 1.0, "dca_{xy}"}; + AxisSpec dcazAxis = {4000, -4.0, 4.0, "dca_{z}"}; + + if (doprocessEfficiencyPurity || doprocessEfficiencyPurityWeighted || doprocessQcCheck) { registry.add("hMcCollCutsCounts", "McColl cuts count checks", {HistType::kTH1F, {{10, 0., 10.}}}); registry.get(HIST("hMcCollCutsCounts"))->GetXaxis()->SetBinLabel(1, "allMcColl"); @@ -265,21 +336,22 @@ struct TrackEfficiency { registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(2, "trackSel"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(3, "hasMcParticle"); - if (doprocessEFficiencyPurity) { + if (doprocessEfficiencyPurity || doprocessQcCheck) { registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(4, "mcPartIsPrimary"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(5, "etaAcc"); // not actually applied here but it will give an idea of what will be done in the post processing } - if (doprocessEFficiencyPurityWeighted) { + if (doprocessEfficiencyPurityWeighted) { registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(4, "ptHatMaxFraction"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(5, "mcPartIsPrimary"); registry.get(HIST("hTrackCutsCounts"))->GetXaxis()->SetBinLabel(6, "etaAcc"); // not actually applied here but it will give an idea of what will be done in the post processing } - - AxisSpec ptAxisEff = {nBinsLowPt, 0., 10., "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec ptAxisHighEff = {18, 10., 100., "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec etaAxisEff = {etaEffNBins, -1.0, 1.0, "#eta"}; - AxisSpec phiAxisEff = {phiEffNBins, -1.0, 7., "#phi"}; - + if (doprocessQcCheck) { + registry.add("h_ntrack_nonassociatedtrack", "Non-associated tracks;N_{tracks};counts", {HistType::kTH1I, {nTracksAxis}}); + registry.add("h_ntrack_associatedtrack_primary", "Associated tracks, primary;N_{tracks};counts", {HistType::kTH1I, {nTracksAxis}}); + registry.add("h_ntrack_associatedtrack_nonprimary", "Associated tracks, non-primary;N_{tracks};counts", {HistType::kTH1I, {nTracksAxis}}); + registry.add("h_ntrack_associatedtrack_split_primary", "Associated split tracks, primary;N_{tracks};counts", {HistType::kTH1I, {nTracksAxis}}); + registry.add("h_ntrack_associatedtrack_split_nonprimary", "Associated split tracks, non-primary;N_{tracks};counts", {HistType::kTH1I, {nTracksAxis}}); + } // ptAxisLow registry.add("h3_particle_pt_particle_eta_particle_phi_mcpartofinterest", "#it{p}_{T, mcpart} (GeV/#it{c}); #eta_{mcpart}; #phi_{mcpart}", {HistType::kTH3F, {ptAxisEff, etaAxisEff, phiAxisEff}}); registry.add("h3_particle_pt_particle_eta_particle_phi_mcpart_nonprimary", "#it{p}_{T, mcpart} (GeV/#it{c}); #eta_{mcpart}; #phi_{mcpart}", {HistType::kTH3F, {ptAxisEff, etaAxisEff, phiAxisEff}}); @@ -315,8 +387,6 @@ struct TrackEfficiency { } if (doprocessTracksFromData || doprocessTracksFromMc || doprocessTracksFromMcWeighted) { - AxisSpec centAxis = {centralityBinning, "centrality (%)"}; - AxisSpec intRateAxis = {intRateNBins, 0., intRateMax, "int. rate (kHz)"}; registry.add("h2_centrality_track_pt", "centrality vs track pT; centrality; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {centAxis, {200, 0., 200.}}}); registry.add("h2_centrality_track_eta", "centrality vs track #eta; centrality; #eta_{track}", {HistType::kTH2F, {centAxis, {100, -1.0, 1.0}}}); registry.add("h2_centrality_track_phi", "centrality vs track #varphi; centrality; #varphi_{track}", {HistType::kTH2F, {centAxis, {160, -1.0, 7.}}}); @@ -326,11 +396,16 @@ struct TrackEfficiency { registry.add("h2_track_pt_track_sigma1overpt", "#sigma(1/#it{p}_{T}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{100, 0., 10.}, {10000, 0.0, 1.0}}}); registry.add("h2_track_pt_high_track_sigma1overpt", "#sigma(1/#it{p}_{T}); #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {{90, 10., 100.}, {10000, 0.0, 1.0}}}); registry.add("h3_intrate_centrality_track_pt", "interaction rate vs centrality vs track pT; int. rate; centrality; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH3F, {intRateAxis, centAxis, {200, 0., 200.}}}); + registry.add("h3_track_pt_track_eta_track_phi", "track pT vs track #eta vs track #varphi; #it{p}_{T,track} (GeV/#it{c}); #eta_{track}; #varphi_{track}", {HistType::kTH3F, {{200, 0., 200.}, {100, -1.0, 1.0}, {160, -1.0, 7.}}}); + + // EA analysis + registry.add("hScaledFT0CTrackPt", "scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0C, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt", "scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0M, {200, 0., 200.}}}); + registry.add("hScaledFT0CTrackPt_MCDetLevel", "MC detector-level scaled FT0C vs track pT; FT0C / #LT FT0C #GT; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0CMCDetLevel, {200, 0., 200.}}}); + registry.add("hScaledFT0MTrackPt_MCDetLevel", "MC detector-level scaled FT0M vs track pT; FT0M^{*}; #it{p}_{T,track} (GeV/#it{c})", {HistType::kTH2F, {scaledFT0MMCDetLevel, {200, 0., 200.}}}); } if (doprocessParticles || doprocessParticlesWeighted) { - AxisSpec centAxis = {centralityBinning, "centrality (%)"}; - AxisSpec intRateAxis = {intRateNBins, 0., intRateMax, "int. rate (kHz)"}; registry.add("h2_centrality_particle_pt", "centrality vs particle pT; centrality; #it{p}_{T,part} (GeV/#it{c})", {HistType::kTH2F, {centAxis, {200, 0., 200.}}}); registry.add("h2_centrality_particle_eta", "centrality vs particle #eta; centrality; #eta_{part}", {HistType::kTH2F, {centAxis, {100, -1.0, 1.0}}}); registry.add("h2_centrality_particle_phi", "centrality vs particle #varphi; centrality; #varphi_{part}", {HistType::kTH2F, {centAxis, {160, -1.0, 7.}}}); @@ -342,12 +417,10 @@ struct TrackEfficiency { } if (doprocessCollisionsFromData || doprocessCollisionsFromMc || doprocessCollisionsFromMcWeighted) { - AxisSpec centAxis = {centralityBinning, "centrality (%)"}; registry.add("h_collisions", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}); registry.add("h2_centrality_collisions", "centrality vs collisions; centrality; collisions", {HistType::kTH2F, {centAxis, {4, 0.0, 4.0}}}); } if (doprocessMcCollisions || doprocessMcCollisionsWeighted) { - AxisSpec centAxis = {centralityBinning, "centrality (%)"}; registry.add("h_mccollisions", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}); registry.add("h2_centrality_mccollisions", "centrality vs mccollisions; centrality; collisions", {HistType::kTH2F, {centAxis, {4, 0.0, 4.0}}}); registry.add("h2_mccollision_pthardfromweight_pthardfromhepmcxsection", "ptHard from weight vs ptHard from HepMCXSections; ptHard_weight; ptHard_hepmcxsections", {HistType::kTH2F, {{200, 0.0, 200.0}, {200, 0.0, 200.0}}}); @@ -357,12 +430,10 @@ struct TrackEfficiency { registry.add("h_fakecollisions", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}); } if (doprocessCollisionsFromMcWeighted) { - AxisSpec centAxis = {centralityBinning, "centrality (%)"}; registry.add("h_collisions_weighted", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}); registry.add("h2_centrality_collisions_weighted", "centrality vs mccollisions; centrality; collisions", {HistType::kTH2F, {centAxis, {4, 0.0, 4.0}}}); } if (doprocessMcCollisionsWeighted) { - AxisSpec centAxis = {centralityBinning, "centrality (%)"}; registry.add("h_mccollisions_weighted", "event status;event status;entries", {HistType::kTH1F, {{4, 0.0, 4.0}}}); registry.add("h2_centrality_mccollisions_weighted", "centrality vs mccollisions; centrality; collisions", {HistType::kTH2F, {centAxis, {4, 0.0, 4.0}}}); registry.add("h2_mccollision_pthardfromweight_pthardfromhepmcxsection_weighted", "ptHard from weight vs ptHard from HepMCXSections; ptHard_weight; ptHard_hepmcxsections", {HistType::kTH2F, {{200, 0.0, 200.0}, {200, 0.0, 200.0}}}); @@ -373,20 +444,17 @@ struct TrackEfficiency { registry.add("h_trackselplot_tpccrossedrowsoverfindable", "track selection variable: ratio of of tpc crossed rows over number of findable clusters", {HistType::kTH1F, {{120, 0.0, 1.2}}}); registry.add("h_trackselplot_chi2ncls_tpc", "track selection variable: Chi2 / cluster for the TPC track segment", {HistType::kTH1F, {{100, 0.0, 10.0}}}); registry.add("h_trackselplot_chi2ncls_its", "track selection variable: Chi2 / cluster for the ITS track segment", {HistType::kTH1F, {{200, 0.0, 40.0}}}); - registry.add("h_trackselplot_dcaxy", "track selection variable: dca XY", {HistType::kTH1F, {{1000, -1.0, 1.0}}}); - registry.add("h_trackselplot_dcaz", "track selection variable: dca Z", {HistType::kTH1F, {{4000, -4.0, 4.0}}}); + registry.add("h_trackselplot_dcaxy", "track selection variable: dca XY", {HistType::kTH1F, {dcaxyAxis}}); + registry.add("h_trackselplot_dcaz", "track selection variable: dca Z", {HistType::kTH1F, {dcazAxis}}); registry.add("h2_trackselplot_pt_tpccrossedrows", "track selection variable: pt vs number of tpc crossed rows", {HistType::kTH2F, {{200, 0., 200.}, {165, -0.5, 164.5}}}); registry.add("h2_trackselplot_pt_tpccrossedrowsoverfindable", "track selection variable: pt vs ratio of of tpc crossed rows over number of findable clusters", {HistType::kTH2F, {{200, 0., 200.}, {120, 0.0, 1.2}}}); registry.add("h2_trackselplot_pt_chi2ncls_tpc", "track selection variable: pt vs Chi2 / cluster for the TPC track segment", {HistType::kTH2F, {{200, 0., 200.}, {100, 0.0, 10.0}}}); registry.add("h2_trackselplot_pt_chi2ncls_its", "track selection variable: pt vs Chi2 / cluster for the ITS track segment", {HistType::kTH2F, {{200, 0., 200.}, {200, 0.0, 40.0}}}); - registry.add("h2_trackselplot_pt_dcaxy", "track selection variable: pt vs dca XY", {HistType::kTH2F, {{200, 0., 200.}, {1000, -1.0, 1.0}}}); - registry.add("h2_trackselplot_pt_dcaz", "track selection variable: pt vs dca Z", {HistType::kTH2F, {{200, 0., 200.}, {4000, -4.0, 4.0}}}); + registry.add("h2_trackselplot_pt_dcaxy", "track selection variable: pt vs dca XY", {HistType::kTH2F, {{200, 0., 200.}, dcaxyAxis}}); + registry.add("h2_trackselplot_pt_dcaz", "track selection variable: pt vs dca Z", {HistType::kTH2F, {{200, 0., 200.}, dcazAxis}}); } - AxisSpec occupancyAxis = {140, -0.5, 13999.5, "occupancy"}; - AxisSpec nTracksAxis = {16001, -1., 16000, "n tracks"}; - if (doprocessOccupancyQA) { registry.add("h2_occupancy_ntracksall_presel", "occupancy vs N_{tracks}; occupancy; N_{tracks}", {HistType::kTH2I, {occupancyAxis, nTracksAxis}}); registry.add("h2_occupancy_ntracksall_postsel", "occupancy vs N_{tracks}; occupancy; N_{tracks}", {HistType::kTH2I, {occupancyAxis, nTracksAxis}}); @@ -395,6 +463,49 @@ struct TrackEfficiency { registry.add("h2_occupancy_ntracksselptetacuts_presel", "occupancy vs N_{tracks}; occupancy; N_{tracks}", {HistType::kTH2I, {occupancyAxis, nTracksAxis}}); registry.add("h2_occupancy_ntracksselptetacuts_postsel", "occupancy vs N_{tracks}; occupancy; N_{tracks}", {HistType::kTH2I, {occupancyAxis, nTracksAxis}}); } + + if (doprocessItsTpcMatchingData) { + registry.add("h2_track_pt_track_eta_datainclusive_ITS", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_track_eta_datainclusive_TPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_track_eta_datainclusive_ITSTPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_datainclusive_ITS", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_datainclusive_TPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_datainclusive_ITSTPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + + registry.add("h_track_pt_track_dcaxy_datainclusive", "#it{p}_{T, track} (GeV/#it{c}); dca_{xy}", {HistType::kTH2F, {ptAxisEff, dcaxyAxis}}); + registry.add("h_track_pt_track_dcaz_datainclusive", "#it{p}_{T, track} (GeV/#it{c}); dca_{z}", {HistType::kTH2F, {ptAxisEff, dcazAxis}}); + registry.add("h_track_pt_high_track_dcaxy_datainclusive", "#it{p}_{T, track} (GeV/#it{c}); dca_{xy}", {HistType::kTH2F, {ptAxisHighEff, dcaxyAxis}}); + registry.add("h_track_pt_high_track_dcaz_datainclusive", "#it{p}_{T, track} (GeV/#it{c}); dca_{z}", {HistType::kTH2F, {ptAxisHighEff, dcazAxis}}); + } + if (doprocessItsTpcMatchingMC) { + registry.add("h2_track_pt_track_eta_mcprimary_ITS", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_track_eta_mcprimary_TPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_track_eta_mcprimary_ITSTPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_mcprimary_ITS", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_mcprimary_TPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_mcprimary_ITSTPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + + registry.add("h2_track_pt_track_eta_mcsecondary_ITS", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_track_eta_mcsecondary_TPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_track_eta_mcsecondary_ITSTPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_mcsecondary_ITS", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_mcsecondary_TPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + registry.add("h2_track_pt_high_track_eta_mcsecondary_ITSTPC", "#it{p}_{T, track} (GeV/#it{c}); #eta_{track}", {HistType::kTH2F, {ptAxisHighEff, etaAxisEff}}); + + registry.add("h_track_pt_track_dcaxy_mcprimary", "#it{p}_{T, track} (GeV/#it{c}); primaries dca_{xy}", {HistType::kTH2F, {ptAxisEff, dcaxyAxis}}); + registry.add("h_track_pt_track_dcaz_mcprimary", "#it{p}_{T, track} (GeV/#it{c}); primaries dca_{z}", {HistType::kTH2F, {ptAxisEff, dcazAxis}}); + registry.add("h_track_pt_track_dcaxy_mcsecondarydecay", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{xy}", {HistType::kTH2F, {ptAxisEff, dcaxyAxis}}); + registry.add("h_track_pt_track_dcaz_mcsecondarydecay", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{z}", {HistType::kTH2F, {ptAxisEff, dcazAxis}}); + registry.add("h_track_pt_track_dcaxy_mcsecondarymat", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{xy}", {HistType::kTH2F, {ptAxisEff, dcaxyAxis}}); + registry.add("h_track_pt_track_dcaz_mcsecondarymat", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{z}", {HistType::kTH2F, {ptAxisEff, dcazAxis}}); + + registry.add("h_track_pt_high_track_dcaxy_mcprimary", "#it{p}_{T, track} (GeV/#it{c}); primaries dca_{xy}", {HistType::kTH2F, {ptAxisHighEff, dcaxyAxis}}); + registry.add("h_track_pt_high_track_dcaz_mcprimary", "#it{p}_{T, track} (GeV/#it{c}); primaries dca_{z}", {HistType::kTH2F, {ptAxisHighEff, dcazAxis}}); + registry.add("h_track_pt_high_track_dcaxy_mcsecondarydecay", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{xy}", {HistType::kTH2F, {ptAxisHighEff, dcaxyAxis}}); + registry.add("h_track_pt_high_track_dcaz_mcsecondarydecay", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{z}", {HistType::kTH2F, {ptAxisHighEff, dcazAxis}}); + registry.add("h_track_pt_high_track_dcaxy_mcsecondarymat", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{xy}", {HistType::kTH2F, {ptAxisHighEff, dcaxyAxis}}); + registry.add("h_track_pt_high_track_dcaz_mcsecondarymat", "#it{p}_{T, track} (GeV/#it{c}); secondaries dca_{z}", {HistType::kTH2F, {ptAxisHighEff, dcazAxis}}); + } } Preslice tracksPerJCollision = o2::aod::jtrack::collisionId; @@ -405,7 +516,7 @@ struct TrackEfficiency { Filter particleCuts = (aod::jmcparticle::pt >= trackQAPtMin && aod::jmcparticle::pt < trackQAPtMax && aod::jmcparticle::eta > trackQAEtaMin && aod::jmcparticle::eta < trackQAEtaMax); Filter eventCuts = (nabs(aod::jcollision::posZ) < vertexZCut); - void processEFficiencyPurity(aod::JetMcCollisions::iterator const& mcCollision, + void processEfficiencyPurity(aod::JetMcCollisions::iterator const& mcCollision, soa::SmallGroups const& collisions, // smallgroups gives only the collisions associated to the current mccollision, thanks to the mccollisionlabel pre-integrated in jetcollisionsmcd soa::Join const& jetTracks, soa::Join const&, @@ -437,8 +548,8 @@ struct TrackEfficiency { bool hasSel8Coll = false; bool centralityCheck = false; bool occupancyCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collisions.begin().trackOccupancyInTimeRange()) && (collisions.begin().trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC @@ -450,7 +561,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC @@ -476,7 +587,7 @@ struct TrackEfficiency { } registry.fill(HIST("hMcCollCutsCounts"), 5.5); // at least one of the reconstructed collisions associated with this mcCollision is selected with regard to centrality - float pTHat = mcCollision.ptHard() < 999.0f ? mcCollision.ptHard() : simPtRef / (std::pow(mcCollision.weight(), 1.0 / pTHatExponent)); + float pTHat = mcCollision.ptHard() < pTHatSettingSentinelValue ? mcCollision.ptHard() : simPtRef / (std::pow(mcCollision.weight(), 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } @@ -522,7 +633,7 @@ struct TrackEfficiency { return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections) || !(std::abs(collision.posZ()) < vertexZCut)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection) || !(std::abs(collision.posZ()) < vertexZCut)) { continue; } @@ -537,7 +648,6 @@ struct TrackEfficiency { if (!track.has_mcParticle()) { registry.fill(HIST("h3_track_pt_track_eta_track_phi_nonassociatedtrack"), track.pt(), track.eta(), track.phi()); - registry.fill(HIST("h3_track_pt_high_track_eta_track_phi_nonassociatedtrack"), track.pt(), track.eta(), track.phi()); continue; } @@ -552,6 +662,8 @@ struct TrackEfficiency { registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_associatedtrack_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { + // occasionally, one particle can match to two reconstructed tracks + // since this particle will appear again in this loop over tracks, these histograms keep track of the extra entries from this duplication registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_nonprimary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); @@ -575,6 +687,8 @@ struct TrackEfficiency { registry.fill(HIST("h2_particle_pt_high_track_pt_high_residual_associatedtrack_primary"), jMcParticleFromTrack.pt(), (jMcParticleFromTrack.pt() - track.pt()) / jMcParticleFromTrack.pt()); if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { + // same as above, but doesn't overlap with the previous std::find call since we looked at non-physical primaries before and now we are only looking at physical primaries + // to get a histogram corrected for the duplication (e.g. for efficiency/resolution studies), you should subtract these histograms below from the corresponding histograms right before this if block registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_primary"), track.pt(), track.eta(), track.phi()); registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); @@ -590,9 +704,9 @@ struct TrackEfficiency { } } } - PROCESS_SWITCH(TrackEfficiency, processEFficiencyPurity, "Histograms for efficiency and purity quantities", true); + PROCESS_SWITCH(TrackEfficiency, processEfficiencyPurity, "Histograms for efficiency and purity quantities", true); - void processEFficiencyPurityWeighted(aod::JetMcCollisions::iterator const& mcCollision, + void processEfficiencyPurityWeighted(aod::JetMcCollisions::iterator const& mcCollision, soa::SmallGroups const& collisions, // smallgroups gives only the collisions associated to the current mccollision, thanks to the mccollisionlabel pre-integrated in jetcollisionsmcd soa::Join const& jetTracks, soa::Join const&, @@ -623,8 +737,8 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -633,7 +747,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -699,7 +813,7 @@ struct TrackEfficiency { return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections) || !(std::abs(collision.posZ()) < vertexZCut)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection) || !(std::abs(collision.posZ()) < vertexZCut)) { continue; } @@ -779,13 +893,13 @@ struct TrackEfficiency { } } } - PROCESS_SWITCH(TrackEfficiency, processEFficiencyPurityWeighted, "Histograms for efficiency and purity quantities for weighted simulations", false); + PROCESS_SWITCH(TrackEfficiency, processEfficiencyPurityWeighted, "Histograms for efficiency and purity quantities for weighted simulations", false); void processTracksFromData(soa::Filtered::iterator const& collision, soa::Filtered> const& jetTracks, soa::Join const&) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -796,7 +910,7 @@ struct TrackEfficiency { return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromData, "QA for charged tracks in data", false); @@ -808,7 +922,7 @@ struct TrackEfficiency { if (!collision.has_mcCollision()) { // the collision is fake and has no associated mc coll; skip as .mccollision() cannot be called return; } - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -819,16 +933,16 @@ struct TrackEfficiency { return; } - float pTHat = collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(collision.mcCollision().weight(), 1.0 / pTHatExponent)); + float pTHat = collision.mcCollision().ptHard() < pTHatSettingSentinelValue ? collision.mcCollision().ptHard() : simPtRef / (std::pow(collision.mcCollision().weight(), 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } - fillTrackHistograms(collision, jetTracks); + fillTrackHistograms(collision, jetTracks); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMc, "QA for charged tracks in MC without weights", false); - void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, + void processTracksFromMcWeighted(soa::Filtered>::iterator const& collision, aod::JetMcCollisions const&, soa::Filtered> const& jetTracks, soa::Join const&) @@ -836,10 +950,14 @@ struct TrackEfficiency { if (!collision.has_mcCollision()) { // the collision is fake and has no associated mc coll; skip as .mccollision() cannot be called return; } - float eventWeight = collision.mcCollision().weight(); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { + return; + } + + if (collision.isOutlier()) { return; } + float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); if (cutCentrality && (centrality < centralityMin || centralityMax < centrality)) { return; @@ -848,12 +966,13 @@ struct TrackEfficiency { return; } - float pTHat = collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + float eventWeight = collision.mcCollision().weight(); + float pTHat = collision.mcCollision().ptHard() < pTHatSettingSentinelValue ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } - fillTrackHistograms(collision, jetTracks, eventWeight); + fillTrackHistograms(collision, jetTracks, eventWeight); } PROCESS_SWITCH(TrackEfficiency, processTracksFromMcWeighted, "QA for charged tracks in weighted MC", false); @@ -873,7 +992,7 @@ struct TrackEfficiency { return; } - float pTHat = mcCollision.ptHard() < 999.0f ? mcCollision.ptHard() : simPtRef / (std::pow(mcCollision.weight(), 1.0 / pTHatExponent)); + float pTHat = mcCollision.ptHard() < pTHatSettingSentinelValue ? mcCollision.ptHard() : simPtRef / (std::pow(mcCollision.weight(), 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } @@ -881,8 +1000,8 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -891,7 +1010,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -936,7 +1055,7 @@ struct TrackEfficiency { return; } - float pTHat = mcCollision.ptHard() < 999.0f ? mcCollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + float pTHat = mcCollision.ptHard() < pTHatSettingSentinelValue ? mcCollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } @@ -944,8 +1063,8 @@ struct TrackEfficiency { float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -954,7 +1073,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -985,7 +1104,7 @@ struct TrackEfficiency { registry.fill(HIST("h_collisions"), 0.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 0.5); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h_collisions"), 1.5); @@ -1014,7 +1133,7 @@ struct TrackEfficiency { } registry.fill(HIST("h_collisions"), 0.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 0.5); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h_collisions"), 1.5); @@ -1030,7 +1149,7 @@ struct TrackEfficiency { registry.fill(HIST("h_collisions"), 3.5); registry.fill(HIST("h2_centrality_collisions"), centrality, 3.5); - float pTHat = collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(collision.mcCollision().weight(), 1.0 / pTHatExponent)); + float pTHat = collision.mcCollision().ptHard() < pTHatSettingSentinelValue ? collision.mcCollision().ptHard() : simPtRef / (std::pow(collision.mcCollision().weight(), 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } @@ -1051,7 +1170,7 @@ struct TrackEfficiency { float eventWeight = collision.mcCollision().weight(); registry.fill(HIST("h_collisions"), 0.5); registry.fill(HIST("h_collisions_weighted"), 0.5, eventWeight); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h_collisions"), 1.5); @@ -1067,7 +1186,7 @@ struct TrackEfficiency { registry.fill(HIST("h_collisions"), 3.5); registry.fill(HIST("h_collisions_weighted"), 3.5, eventWeight); - float pTHat = collision.mcCollision().ptHard() < 999.0f ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + float pTHat = collision.mcCollision().ptHard() < pTHatSettingSentinelValue ? collision.mcCollision().ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max return; } @@ -1082,15 +1201,15 @@ struct TrackEfficiency { // float centrality = checkCentFT0M ? mcCollision.centFT0M() : mcCollision.centFT0C(); mcCollision.centFT0C() isn't filled at the moment; can be added back when it is float eventWeight = mcCollision.weight(); - float pTHat = mcCollision.ptHard() < 999.0f ? mcCollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + float pTHat = mcCollision.ptHard() < pTHatSettingSentinelValue ? mcCollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); registry.fill(HIST("h2_mccollision_pthardfromweight_pthardfromhepmcxsection"), simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)), mcCollision.ptHard()); float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -1099,7 +1218,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1154,16 +1273,16 @@ struct TrackEfficiency { // float centrality = checkCentFT0M ? mcCollision.centFT0M() : mcCollision.centFT0C(); mcCollision.centFT0C() isn't filled at the moment; can be added back when it is float eventWeight = mcCollision.weight(); - float pTHat = mcCollision.ptHard() < 999.0f ? mcCollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); + float pTHat = mcCollision.ptHard() < pTHatSettingSentinelValue ? mcCollision.ptHard() : simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)); registry.fill(HIST("h2_mccollision_pthardfromweight_pthardfromhepmcxsection"), simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)), mcCollision.ptHard()); registry.fill(HIST("h2_mccollision_pthardfromweight_pthardfromhepmcxsection_weighted"), simPtRef / (std::pow(eventWeight, 1.0 / pTHatExponent)), mcCollision.ptHard(), eventWeight); float centrality = -1; bool hasSel8Coll = false; bool centralityCheck = false; - if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed - if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) - if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have their first associated collision not reconstructed + if (collisions.size() > 1) { // remove and move the if block below under if (collisions.size() < 1) { when mccoll.centFt0C has been fixed + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed hasSel8Coll = true; } centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); @@ -1172,7 +1291,7 @@ struct TrackEfficiency { } } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks for (auto const& collision : collisions) { - if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split hasSel8Coll = true; } centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1224,7 +1343,7 @@ struct TrackEfficiency { void processTrackSelectionHistograms(soa::Filtered::iterator const& collision, soa::Join const& jetTracks, soa::Join const&) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); @@ -1278,7 +1397,7 @@ struct TrackEfficiency { registry.fill(HIST("h2_occupancy_ntracksall_presel"), occupancy, nTracksAll); registry.fill(HIST("h2_occupancy_ntrackssel_presel"), occupancy, nTracksAllAcceptanceAndSelected); registry.fill(HIST("h2_occupancy_ntracksselptetacuts_presel"), occupancy, nTracksInAcceptanceAndSelected); - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections)) { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { return; } registry.fill(HIST("h2_occupancy_ntracksall_postsel"), occupancy, nTracksAll); @@ -1286,6 +1405,412 @@ struct TrackEfficiency { registry.fill(HIST("h2_occupancy_ntracksselptetacuts_postsel"), occupancy, nTracksInAcceptanceAndSelected); } PROCESS_SWITCH(TrackEfficiency, processOccupancyQA, "occupancy QA on jet derived data", false); + + void processItsTpcMatchingData(soa::Filtered::iterator const& collision, soa::Join const& jetTracks, soa::Join const&) + { + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { + return; + } + float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); + if (cutCentrality && (centrality < centralityMin || centralityMax < centrality)) { + return; + } + if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { + return; + } + for (auto const& jetTrack : jetTracks) { + const auto& aodTrack = jetTrack.track_as>(); // remove , aod::TrackSelectionExtension after debug + + if (!aodTrack.isPrimaryTrack()) { // dcaXY and dcaZ cuts + continue; + } + double minPtTrack = 0.15; + if (aodTrack.pt() < minPtTrack) { + continue; + } + + if (aodTrack.hasITS() && aodTrack.passedITSHits()) { + registry.fill(HIST("h2_track_pt_track_eta_datainclusive_ITS"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_datainclusive_ITS"), aodTrack.pt(), aodTrack.eta()); + } + + if (aodTrack.hasTPC() && aodTrack.passedTPCCrossedRowsOverNCls()) { + registry.fill(HIST("h2_track_pt_track_eta_datainclusive_TPC"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_datainclusive_TPC"), aodTrack.pt(), aodTrack.eta()); + } + + if (aodTrack.hasITS() && aodTrack.passedITSHits() && aodTrack.hasTPC() && aodTrack.passedTPCCrossedRowsOverNCls()) { + registry.fill(HIST("h2_track_pt_track_eta_datainclusive_TPC"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_track_eta_datainclusive_ITSTPC"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_datainclusive_ITSTPC"), aodTrack.pt(), aodTrack.eta()); + } + } + + // dca distribution in data for fit with MC templates + for (auto const& jetTrack : jetTracks) { + const auto& aodTrack = jetTrack.track_as>(); + + if (!aodTrack.isPrimaryTrack()) { // dcaXY and dcaZ cuts + continue; + } + if (aodTrack.eta() < trackQAEtaMin || trackQAEtaMax < aodTrack.eta()) { + continue; + } + if (!(aodTrack.hasITS() && aodTrack.passedITSHits())) { // run 2 asks for spd:kAny hit + continue; + } + + registry.fill(HIST("h_track_pt_track_dcaxy_datainclusive"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_track_dcaz_datainclusive"), aodTrack.pt(), aodTrack.dcaZ()); + registry.fill(HIST("h_track_pt_high_track_dcaxy_datainclusive"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_high_track_dcaz_datainclusive"), aodTrack.pt(), aodTrack.dcaZ()); + } + } + PROCESS_SWITCH(TrackEfficiency, processItsTpcMatchingData, "fills histograms for ITS-TPC matching analysis - data study, true primary and true secondary not distinguished", false); + + void processItsTpcMatchingMC(soa::Filtered::iterator const& collision, soa::Join const& jetTracks, soa::Join const&, aod::McParticles const&) + { + // could be added in future: pions+kaons vs protons distinction; easy in MC but what is the procedure for data? + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { + return; + } + float centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); + if (cutCentrality && (centrality < centralityMin || centralityMax < centrality)) { + return; + } + if (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMin || trackOccupancyInTimeRangeMax < collision.trackOccupancyInTimeRange()) { + return; + } + + for (auto const& jetTrack : jetTracks) { + const auto& aodTrack = jetTrack.track_as>(); + if (!aodTrack.has_mcParticle()) { + continue; + } + auto aodMcParticleFromTrack = aodTrack.mcParticle_as(); + + if (!aodTrack.isPrimaryTrack()) { // dcaXY and dcaZ cuts + continue; + } + double minPtTrack = 0.15; + if (aodTrack.pt() < minPtTrack) { + continue; + } + + if (aodMcParticleFromTrack.isPhysicalPrimary()) { + if (aodTrack.hasITS() && aodTrack.passedITSHits()) { + registry.fill(HIST("h2_track_pt_track_eta_mcprimary_ITS"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_mcprimary_ITS"), aodTrack.pt(), aodTrack.eta()); + } + + if (aodTrack.hasTPC() && aodTrack.passedTPCCrossedRowsOverNCls()) { + registry.fill(HIST("h2_track_pt_track_eta_mcprimary_TPC"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_mcprimary_TPC"), aodTrack.pt(), aodTrack.eta()); + } + + if (aodTrack.hasITS() && aodTrack.passedITSHits() && aodTrack.hasTPC() && aodTrack.passedTPCCrossedRowsOverNCls()) { + registry.fill(HIST("h2_track_pt_track_eta_mcprimary_ITSTPC"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_mcprimary_ITSTPC"), aodTrack.pt(), aodTrack.eta()); + } + } + + if (!aodMcParticleFromTrack.isPhysicalPrimary()) { + if (aodTrack.hasITS() && aodTrack.passedITSHits()) { + registry.fill(HIST("h2_track_pt_track_eta_mcsecondary_ITS"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_mcsecondary_ITS"), aodTrack.pt(), aodTrack.eta()); + } + + if (aodTrack.hasTPC() && aodTrack.passedTPCCrossedRowsOverNCls()) { + registry.fill(HIST("h2_track_pt_track_eta_mcsecondary_TPC"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_mcsecondary_TPC"), aodTrack.pt(), aodTrack.eta()); + } + + if (aodTrack.hasITS() && aodTrack.passedITSHits() && aodTrack.hasTPC() && aodTrack.passedTPCCrossedRowsOverNCls()) { + registry.fill(HIST("h2_track_pt_track_eta_mcsecondary_ITSTPC"), aodTrack.pt(), aodTrack.eta()); + registry.fill(HIST("h2_track_pt_high_track_eta_mcsecondary_ITSTPC"), aodTrack.pt(), aodTrack.eta()); + } + } + } + + // dca distribution in MC for template fit to Data + for (auto const& jetTrack : jetTracks) { + const auto& aodTrack = jetTrack.track_as>(); + if (!aodTrack.has_mcParticle()) { + continue; + } + auto aodMcParticleFromTrack = aodTrack.mcParticle_as(); + + if (!aodTrack.isPrimaryTrack()) { // dcaXY and dcaZ cuts + continue; + } + if (aodTrack.eta() < trackQAEtaMin || trackQAEtaMax < aodTrack.eta()) { + continue; + } + if (!(aodTrack.hasITS() && aodTrack.passedITSHits())) { // run 2 asks for spd:kAny hit + continue; + } + + if (aodMcParticleFromTrack.isPhysicalPrimary()) { + registry.fill(HIST("h_track_pt_track_dcaxy_mcprimary"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_track_dcaz_mcprimary"), aodTrack.pt(), aodTrack.dcaZ()); + registry.fill(HIST("h_track_pt_high_track_dcaxy_mcprimary"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_high_track_dcaz_mcprimary"), aodTrack.pt(), aodTrack.dcaZ()); + } + + if (!aodMcParticleFromTrack.isPhysicalPrimary()) { // Secondaries (weak decays and material) + if (aodMcParticleFromTrack.getProcess() == kPDecay) { // Particles from decay + registry.fill(HIST("h_track_pt_track_dcaxy_mcsecondarydecay"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_track_dcaz_mcsecondarydecay"), aodTrack.pt(), aodTrack.dcaZ()); + registry.fill(HIST("h_track_pt_high_track_dcaxy_mcsecondarydecay"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_high_track_dcaz_mcsecondarydecay"), aodTrack.pt(), aodTrack.dcaZ()); + } else { // Particles from the material + registry.fill(HIST("h_track_pt_track_dcaxy_mcsecondarymat"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_track_dcaz_mcsecondarymat"), aodTrack.pt(), aodTrack.dcaZ()); + registry.fill(HIST("h_track_pt_high_track_dcaxy_mcsecondarymat"), aodTrack.pt(), aodTrack.dcaXY()); + registry.fill(HIST("h_track_pt_high_track_dcaz_mcsecondarymat"), aodTrack.pt(), aodTrack.dcaZ()); + } + } + } + } + PROCESS_SWITCH(TrackEfficiency, processItsTpcMatchingMC, "fills histograms for ITS-TPC matching analysis - MC study, true primary and true secondary separated", false); + + void processQcCheck(aod::JetMcCollisions::iterator const& mcCollision, + soa::SmallGroups const& collisions, // smallgroups gives only the collisions associated to the current mccollision, thanks to the mccollisionlabel pre-integrated in jetcollisionsmcd + soa::Filtered> const& jetTracks, + soa::Join const&, + JetParticlesWithOriginal const& jMcParticles) + { + registry.fill(HIST("hMcCollCutsCounts"), 0.5); // all mcCollisions + + if (!(std::abs(mcCollision.posZ()) < vertexZCut)) { + return; + } + registry.fill(HIST("hMcCollCutsCounts"), 1.5); // mcCollision.posZ() condition + + if (collisions.size() < 1) { + return; + } + registry.fill(HIST("hMcCollCutsCounts"), 2.5); // mcCollisions with at least one reconstructed collision + + if (acceptSplitCollisions == NonSplitOnly && collisions.size() > 1) { + return; + } + registry.fill(HIST("hMcCollCutsCounts"), 3.5); // split mcCollisions condition + + float centrality = -1; + bool hasSel8Coll = false; + bool centralityCheck = false; + bool occupancyCheck = false; + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly || acceptSplitCollisions == NonSplitOnly) { // check only that the first reconstructed collision passes the check (for the NonSplitOnly case, there's only one associated collision) + if (jetderiveddatautilities::selectCollision(collisions.begin(), eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have their first associated collision not reconstructed + hasSel8Coll = true; + } + if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collisions.begin().trackOccupancyInTimeRange()) && (collisions.begin().trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC + occupancyCheck = true; + } + centrality = checkCentFT0M ? collisions.begin().centFT0M() : collisions.begin().centFT0C(); + if (!cutCentrality || ((centralityMin < centrality) && (centrality < centralityMax))) { // mcCollision.centFT0C() isn't filled at the moment; can use it instead when it is added to O2Physics + centralityCheck = true; + } + } else if (acceptSplitCollisions == SplitOkCheckAnyAssocColl) { // check that at least one of the reconstructed collisions passes the checks + for (auto const& collision : collisions) { + if (jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection)) { // Skipping MC events that have not a single selected reconstructed collision ; effect unclear if mcColl is split + hasSel8Coll = true; + } + if (!checkOccupancy || ((trackOccupancyInTimeRangeMin < collision.trackOccupancyInTimeRange()) && (collision.trackOccupancyInTimeRange() < trackOccupancyInTimeRangeMax))) { // check occupancy only in GP Pb-Pb MC + occupancyCheck = true; + } + centrality = checkCentFT0M ? collision.centFT0M() : collision.centFT0C(); + if (!cutCentrality || ((centralityMin < centrality) && (centrality < centralityMax))) { // effect unclear if mcColl is split + centralityCheck = true; + } + } + } + if (!hasSel8Coll) { + return; + } + registry.fill(HIST("hMcCollCutsCounts"), 4.5); // at least one of the reconstructed collisions associated with this mcCollision is selected + + // float centrality = checkCentFT0M ? mcCollision.centFT0M() : mcCollision.centFT0C(); mcCollision.centFT0C() isn't filled at the moment; can be added back when it is + // if (cutCentrality && (centrality < centralityMin || centralityMax < centrality)) { + // return; + // } + if (!centralityCheck) { + return; + } + registry.fill(HIST("hMcCollCutsCounts"), 5.5); // at least one of the reconstructed collisions associated with this mcCollision is selected with regard to centrality + + float pTHat = mcCollision.ptHard() < pTHatSettingSentinelValue ? mcCollision.ptHard() : simPtRef / (std::pow(mcCollision.weight(), 1.0 / pTHatExponent)); + if (pTHat < ptHatMin || pTHat > ptHatMax) { // only allows mcCollisions with weight in between min and max + return; + } + registry.fill(HIST("hMcCollCutsCounts"), 6.5); // ptHat condition + + if (checkOccupancy) { + if (!occupancyCheck) { + return; + } + registry.fill(HIST("hMcCollCutsCounts"), 7.5); + } + + for (auto const& jMcParticle : jMcParticles) { + registry.fill(HIST("hMcPartCutsCounts"), 0.5); // allPartsInSelMcColl + + if (!isChargedParticle(jMcParticle.pdgCode())) { + continue; + } + registry.fill(HIST("hMcPartCutsCounts"), 1.5); // isCharged + + registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_mcpart_nonprimary"), jMcParticle.pt(), jMcParticle.eta(), jMcParticle.phi()); + + if (checkPrimaryPart && !jMcParticle.isPhysicalPrimary()) { // global tracks should be mostly primaries + continue; + } + registry.fill(HIST("hMcPartCutsCounts"), 2.5); // isPrimary + + registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_mcpartofinterest"), jMcParticle.pt(), jMcParticle.eta(), jMcParticle.phi()); + + registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_mcpartofinterest"), jMcParticle.pt(), jMcParticle.eta(), jMcParticle.phi()); + + if ((std::abs(jMcParticle.eta()) < trackEtaAcceptanceCountQA)) { // removed from actual cuts for now because all the histograms have an eta axis + registry.fill(HIST("hMcPartCutsCounts"), 3.5); // etaAccept // not actually applied here but it will give an idea of what will be done in the post processing + } + } + + std::vector seenMcParticlesVector; // is reset every mc collision + + int splitCollCounter = 0; + for (auto const& collision : collisions) { + splitCollCounter++; + if (acceptSplitCollisions == SplitOkCheckFirstAssocCollOnly && splitCollCounter > 1) { + return; + } + + if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits, skipMBGapEvents, applyRCTSelections, rctSelection) || !(std::abs(collision.posZ()) < vertexZCut)) { + continue; + } + + auto collTracks = jetTracks.sliceBy(tracksPerJCollision, collision.globalIndex()); + int nTrackNonassociatedTrack = 0; + int nTrackAssociatedTrackNonprimary = 0; + int nTrackAssociatedTrackPrimary = 0; + int nTrackAssociatedTrackSplitNonprimary = 0; + int nTrackAssociatedTrackSplitPrimary = 0; + for (auto const& track : collTracks) { + registry.fill(HIST("hTrackCutsCounts"), 0.5); + + if (!isAcceptedTrack(track)) { + continue; + } + registry.fill(HIST("hTrackCutsCounts"), 1.5); + + if (!track.has_mcParticle()) { + nTrackNonassociatedTrack += 1; + + registry.fill(HIST("h3_track_pt_track_eta_track_phi_nonassociatedtrack"), track.pt(), track.eta(), track.phi()); + + registry.fill(HIST("h3_track_pt_high_track_eta_track_phi_nonassociatedtrack"), track.pt(), track.eta(), track.phi()); + continue; + } + registry.fill(HIST("hTrackCutsCounts"), 2.5); + + auto jMcParticleFromTrack = track.mcParticle_as(); + if (!jMcParticleFromTrack.isPhysicalPrimary()) { + nTrackAssociatedTrackNonprimary += 1; + + registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_nonprimary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + + registry.fill(HIST("h3_track_pt_high_track_eta_track_phi_associatedtrack_nonprimary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_associatedtrack_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + + if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { + nTrackAssociatedTrackSplitNonprimary += 1; + + registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_nonprimary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + + registry.fill(HIST("h3_track_pt_high_track_eta_track_phi_associatedtrack_split_nonprimary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_associatedtrack_split_nonprimary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + } else { + seenMcParticlesVector.push_back(jMcParticleFromTrack.globalIndex()); + } + + continue; + } + + registry.fill(HIST("hTrackCutsCounts"), 3.5); + + nTrackAssociatedTrackPrimary += 1; + registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_primary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + registry.fill(HIST("h2_particle_pt_track_pt_residual_associatedtrack_primary"), jMcParticleFromTrack.pt(), (jMcParticleFromTrack.pt() - track.pt()) / jMcParticleFromTrack.pt()); + + registry.fill(HIST("h3_track_pt_high_track_eta_track_phi_associatedtrack_primary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_associatedtrack_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + registry.fill(HIST("h2_particle_pt_high_track_pt_high_residual_associatedtrack_primary"), jMcParticleFromTrack.pt(), (jMcParticleFromTrack.pt() - track.pt()) / jMcParticleFromTrack.pt()); + + if (std::find(seenMcParticlesVector.begin(), seenMcParticlesVector.end(), jMcParticleFromTrack.globalIndex()) != seenMcParticlesVector.end()) { + nTrackAssociatedTrackSplitPrimary += 1; + registry.fill(HIST("h3_track_pt_track_eta_track_phi_associatedtrack_split_primary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_particle_eta_particle_phi_associatedtrack_split_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + + registry.fill(HIST("h3_track_pt_high_track_eta_track_phi_associatedtrack_split_primary"), track.pt(), track.eta(), track.phi()); + registry.fill(HIST("h3_particle_pt_high_particle_eta_particle_phi_associatedtrack_split_primary"), jMcParticleFromTrack.pt(), jMcParticleFromTrack.eta(), jMcParticleFromTrack.phi()); + } else { + seenMcParticlesVector.push_back(jMcParticleFromTrack.globalIndex()); + } + + if (std::abs(jMcParticleFromTrack.eta()) < trackEtaAcceptanceCountQA) { // not actually applied here but it will give an idea of what will be done in the post processing + registry.fill(HIST("hTrackCutsCounts"), 4.5); + } + } + registry.fill(HIST("h_ntrack_nonassociatedtrack"), nTrackNonassociatedTrack); + registry.fill(HIST("h_ntrack_associatedtrack_nonprimary"), nTrackAssociatedTrackNonprimary); + registry.fill(HIST("h_ntrack_associatedtrack_split_nonprimary"), nTrackAssociatedTrackSplitNonprimary); + registry.fill(HIST("h_ntrack_associatedtrack_primary"), nTrackAssociatedTrackPrimary); + registry.fill(HIST("h_ntrack_associatedtrack_split_primary"), nTrackAssociatedTrackSplitPrimary); + } + } + PROCESS_SWITCH(TrackEfficiency, processQcCheck, "Histograms for QC checks", false); + + // FT0 event activity helpers + struct FT0Metrics { + float multFT0A = 0.f; + float multFT0C = 0.f; + float multFT0M = 0.f; + float scaledFT0A = 0.f; + float scaledFT0C = 0.f; + float scaledFT0M = 0.f; + }; + + float getScaledFT0(const float& multFT0, const float& meanFT0) + { + return multFT0 / meanFT0; + } + + float getScaledFT0M(const float& scaledMultFT0A, const float& scaledMultFT0C) + { + return 0.5 * (scaledMultFT0A + scaledMultFT0C); + } + + FT0Metrics makeFT0Metrics(float multFT0A, float multFT0C, float meanFT0A, float meanFT0C) + { + FT0Metrics values; + values.multFT0A = multFT0A; + values.multFT0C = multFT0C; + values.multFT0M = multFT0A + multFT0C; + values.scaledFT0A = getScaledFT0(values.multFT0A, meanFT0A); + values.scaledFT0C = getScaledFT0(values.multFT0C, meanFT0C); + values.scaledFT0M = getScaledFT0M(values.scaledFT0A, values.scaledFT0C); + return values; + } + + template + FT0Metrics getFT0Metrics(const Collision& collision, float meanFT0A, float meanFT0C) + { + return makeFT0Metrics(collision.multFT0A(), collision.multFT0C(), meanFT0A, meanFT0C); + } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGJE/Tasks/trackJetQA.cxx b/PWGJE/Tasks/trackJetQA.cxx index 3b7eafded61..26668e5bf87 100644 --- a/PWGJE/Tasks/trackJetQA.cxx +++ b/PWGJE/Tasks/trackJetQA.cxx @@ -350,7 +350,7 @@ struct TrackJetQa { histos.fill(HIST("TPC/tpcChi2NCl"), track.pt(), track.sigma1Pt() * track.pt(), track.tpcChi2NCl(), collision.centFT0A(), collision.centFT0C()); } - Preslice trackPerColl = aod::track::collisionId; + Preslice trackPerColl = aod::track::collisionId; using CollisionCandidate = soa::Join; using TrackCandidates = soa::Join; diff --git a/PWGJE/Tasks/triggerCorrelations.cxx b/PWGJE/Tasks/triggerCorrelations.cxx index 2a32add9252..ba66fb5abac 100644 --- a/PWGJE/Tasks/triggerCorrelations.cxx +++ b/PWGJE/Tasks/triggerCorrelations.cxx @@ -26,6 +26,7 @@ #include +#include #include #include @@ -38,9 +39,9 @@ struct TriggerCorrelationsTask { HistogramRegistry registry; std::vector triggerMaskBits; - long unsigned int nChargedTriggers = 4; - long unsigned int nChargedHFTriggers = 4; - long unsigned int nFullTriggers = 13; + uint64_t nChargedTriggers = 4; + uint64_t nChargedHFTriggers = 4; + uint64_t nFullTriggers = 13; void init(o2::framework::InitContext&) { triggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(jetderiveddatautilities::JTriggerMasks); diff --git a/PWGLF/CMakeLists.txt b/PWGLF/CMakeLists.txt index 797ac01500f..2aa66ef9fb4 100644 --- a/PWGLF/CMakeLists.txt +++ b/PWGLF/CMakeLists.txt @@ -11,6 +11,7 @@ # add_subdirectory(Core) # add_subdirectory(DataModel) +# Utils first: it owns the LfStrangenessPCH carrier the workflows reuse. +add_subdirectory(Utils) add_subdirectory(Tasks) add_subdirectory(TableProducer) -add_subdirectory(Utils) diff --git a/PWGLF/Core/K1AnalysisMicroCore.h b/PWGLF/Core/K1AnalysisMicroCore.h new file mode 100644 index 00000000000..3b024afbf68 --- /dev/null +++ b/PWGLF/Core/K1AnalysisMicroCore.h @@ -0,0 +1,796 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file K1AnalysisMicroCore.h +/// \brief Shared K1(1270) selection, truth classification and candidate enumeration of the K1 resonance tasks +/// \author Su-Jeong Ji , Bong-Hwi Lim +/// +/// The core owns the selection and its cut-flow instrumentation (CutFlow/*, ML/*). The tasks own their +/// output histograms and fill them from the pair and candidate hooks of forEachCandidate(). + +#ifndef PWGLF_CORE_K1ANALYSISMICROCORE_H_ +#define PWGLF_CORE_K1ANALYSISMICROCORE_H_ + +#include "PWGLF/Core/K1MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" + +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::k1micro +{ + +using o2::analysis::resonance::EventCuts; +using o2::analysis::resonance::isCutEnabled; +using o2::analysis::resonance::isInRange; +using o2::analysis::resonance::isInWindow; +using o2::analysis::resonance::PIDCutConfig; +using o2::analysis::resonance::ResoAnalysisSelectionCore; +using o2::analysis::resonance::TrackCuts; +using o2::analysis::resonance::TrackStage; + +enum class K1TruthChannel { + None = 0, + RhoK = 1, + KStarPi = 2 +}; + +struct K1ParentTruth { + double pt = std::numeric_limits::quiet_NaN(); + double y = std::numeric_limits::quiet_NaN(); + int pdgCode = 0; + K1TruthChannel channel = K1TruthChannel::None; +}; +using K1ParentTruthMap = std::unordered_map; + +inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2); + +// The value is the species index of the PID configuration in ResoAnalysisSelectionCore. +enum class Species : int { + Pion = 0, + Kaon = 1 +}; + +// Cumulative selection bits of an unlike-sign candidate handed to the export hook. +enum CandidatePassBit : uint16_t { + kPassLoose = 1, // valid canonical candidate inside the K1 rapidity window + kPassQuality = 2, // track quality of all three tracks + kPassPID = 4, // TOF requirement and PID of all three tracks + kPassPair = 8, // pion-pair pT and secondary mass window + kPassCandidate = 16 // candidate cuts +}; +inline constexpr uint16_t PassBitsSelected = kPassLoose | kPassQuality | kPassPID | kPassPair | kPassCandidate; + +inline constexpr double MassRho770 = 0.77526; // PDG 2024, not available in o2::constants::physics +inline constexpr int NCandidateStages = 12; +inline constexpr int NTruthChannels = 3; + +// Configurable groups without prefix: the JSON keys are the plain configurable names. + +/// Pion PID selection +struct PionPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMaxTPCnSigmaPion{"cMaxTPCnSigmaPion", 3.0, "TPC nSigma cut for Pion (-999: off)"}; // TPC + o2::framework::Configurable cMaxTOFnSigmaPion{"cMaxTOFnSigmaPion", 3.0, "TOF nSigma cut for Pion (-999: off)"}; // TOF + o2::framework::Configurable nsigmaCutCombinedPion{"nsigmaCutCombinedPion", -999, "Combined nSigma cut for Pion"}; // Combined + o2::framework::Configurable cUseOnlyTOFTrackPi{"cUseOnlyTOFTrackPi", false, "Use only TOF track for PID selection"}; // Use only TOF track for Pion PID selection + o2::framework::Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; + o2::framework::Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; + o2::framework::Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; +}; + +/// Kaon PID selection +struct KaonPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon (-999: off)"}; // TPC + o2::framework::Configurable cMaxTOFnSigmaKaon{"cMaxTOFnSigmaKaon", 3.0, "TOF nSigma cut for Kaon (-999: off)"}; // TOF + o2::framework::Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", -999, "Combined nSigma cut for Kaon"}; // Combined + o2::framework::Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; // Use only TOF track for Kaon PID selection + o2::framework::Configurable cKaonUsePtDepPID{"cKaonUsePtDepPID", false, "Use pT-dependent PID cuts for kaon"}; + o2::framework::Configurable> cKaonPIDPtBins{"cKaonPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for kaon PID cuts"}; + o2::framework::Configurable> cKaonTPCNSigmaCuts{"cKaonTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFNSigmaCuts{"cKaonTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFRequired{"cKaonTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (kaon)"}; +}; + +/// Secondary selection (-999 switches a cut off; the values it needs are then not computed) +struct SecondaryCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinSecondaryPtCut{"cMinSecondaryPtCut", 0.5, "Min pT cut for secondary selection"}; + o2::framework::Configurable cfgModeK892orRho{"cfgModeK892orRho", false, "Secondary scenario for K892 (true) or Rho (false)"}; + o2::framework::Configurable cSecondaryMasswindow{"cSecondaryMasswindow", -999, "Secondary inv mass selection window"}; + o2::framework::Configurable cMinAnotherSecondaryMassCut{"cMinAnotherSecondaryMassCut", -999, "Min inv. mass selection of another secondary scenario"}; + o2::framework::Configurable cMaxAnotherSecondaryMassCut{"cMaxAnotherSecondaryMassCut", -999, "MAx inv. mass selection of another secondary scenario"}; + o2::framework::Configurable cMinPiKaMassCut{"cMinPiKaMassCut", -999, "bPion-Kaon pair inv mass selection minimum"}; + o2::framework::Configurable cMaxPiKaMassCut{"cMaxPiKaMassCut", -999, "bPion-Kaon pair inv mass selection maximum"}; + o2::framework::Configurable cMinAngle{"cMinAngle", -999, "Minimum angle between the secondary resonance and the bachelor"}; + o2::framework::Configurable cMaxAngle{"cMaxAngle", -999, "Maximum angle between the secondary resonance and the bachelor"}; + o2::framework::Configurable cMinPairAsym{"cMinPairAsym", -999, "Minimum pair asymmetry"}; + o2::framework::Configurable cMaxPairAsym{"cMaxPairAsym", -999, "Maximum pair asymmetry"}; +}; + +/// Common TOF switch and K1 selection +struct CandidateCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cByPassTOF{"cByPassTOF", false, "Bypass the TOF nSigma selection"}; + o2::framework::Configurable cK1MaxRap{"cK1MaxRap", 0.5, "K1 maximum rapidity"}; + o2::framework::Configurable cK1MinRap{"cK1MinRap", -0.5, "K1 minimum rapidity"}; +}; + +/// Process functions enabled in the task; they decide the configuration checks and the registered histograms. +struct ProcessModes { + bool microTracks = false; // any process function reading micro tracks (quantised DCA and nSigma) + bool mcReco = false; // reconstructed MC with full or micro tracks + bool mcRecoMicro = false; // reconstructed MC with micro tracks + bool mcGen = false; // generated K1 parents in selected reconstructed events +}; + +/// Loose-stage traversal of unlike-sign micro candidates for the export hook. +/// With the defaults and without an export hook, the candidate loop applies only the conventional selection. +struct LooseStageOptions { + bool audit = false; // fill ML/looseCutflow and ML/looseMassPtActivity + bool exportSelected = false; // hand candidates to the export hook at the selected stage instead of the loose stage +}; + +/// Selected (pion, pion, kaon) triplet handed to the candidate hook. +/// mass13, mass23, angle and pairAsym are computed only inside the K1 rapidity window, and there only +/// when a candidate cut needs them or forEachCandidate() was asked for them; otherwise they are 0. +struct K1CandidateValues { + ROOT::Math::PxPyPzMVector k1; // pion1 + pion2 + kaon + ROOT::Math::PxPyPzMVector secondary; // pion1 + pion2 + double mass13 = 0.; // pion1 + kaon (K*0 candidate in the unlike-sign case) + double mass23 = 0.; // pion2 + kaon + double angle = 0.; // opening angle between the secondary resonance and the bachelor + double pairAsym = 0.; // energy asymmetry between the secondary resonance and the bachelor + bool isUnlikeSign = false; // opposite-sign pion pair + bool inRapidity = false; // K1 rapidity window + bool passesCandidateCuts = false; // secondary mass, pi-K mass, angle and asymmetry cuts (inside the rapidity window) +}; + +// Truth classification from the immediate mothers and the sibling IDs of the reconstructed daughters. +template +bool hasSibling(const Track& directDaughter, int resonanceId) +{ + if (resonanceId < 0) { + return false; + } + const auto siblings = directDaughter.siblingIds(); + return siblings[0] == resonanceId || siblings[1] == resonanceId; +} + +template +bool matchesKStarPi(const Track& resonancePion, const Track& directPion, const Kaon& kaon) +{ + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + if (resonancePion.motherId() != kaon.motherId() || + resonancePion.motherId() == directPion.motherId()) { + return false; + } + if (resonancePion.motherPDG() != charge * o2::constants::physics::Pdg::kK0Star892 || kaon.motherPDG() != charge * o2::constants::physics::Pdg::kK0Star892) { + return false; + } + if (resonancePion.pdgCode() != -charge * kPiPlus || directPion.pdgCode() != charge * kPiPlus || + directPion.motherPDG() != charge * o2::constants::physics::Pdg::kK1_1270Plus) { + return false; + } + return hasSibling(directPion, kaon.motherId()); +} + +template +K1TruthChannel classifyK1Truth(const Track& pion1, const Track& pion2, const Kaon& kaon) +{ + if (std::abs(pion1.pdgCode()) != kPiPlus || std::abs(pion2.pdgCode()) != kPiPlus || + std::abs(kaon.pdgCode()) != kKPlus) { + return K1TruthChannel::None; + } + if (pion1.motherId() < 0 || pion2.motherId() < 0 || kaon.motherId() < 0) { + return K1TruthChannel::None; + } + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + const bool rhoPions = pion1.motherId() == pion2.motherId() && + pion1.motherPDG() == kRho770_0 && pion2.motherPDG() == kRho770_0 && + pion1.pdgCode() == -pion2.pdgCode(); + if (rhoPions && kaon.motherPDG() == charge * o2::constants::physics::Pdg::kK1_1270Plus && + kaon.motherId() != pion1.motherId() && hasSibling(kaon, pion1.motherId())) { + return K1TruthChannel::RhoK; + } + if (matchesKStarPi(pion1, pion2, kaon) || matchesKStarPi(pion2, pion1, kaon)) { + return K1TruthChannel::KStarPi; + } + return K1TruthChannel::None; +} + +template +int k1MotherId(K1TruthChannel channel, const Kaon& kaon, const Pion& pion1, const Pion& pion2) +{ + if (channel == K1TruthChannel::RhoK) { + return kaon.motherId(); + } + if (channel == K1TruthChannel::KStarPi) { + return std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() : pion2.motherId(); + } + return -1; +} + +template +K1ParentTruthMap makeK1ParentTruthMap(const ParentsType& parents) +{ + K1ParentTruthMap result; + for (const auto& part : parents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus || part.originalMcParticleId() < 0) { + continue; + } + const auto id = static_cast(part.originalMcParticleId()); + const int charge = part.pdgCode() > 0 ? 1 : -1; + result.emplace(id, K1ParentTruth{part.pt(), part.y(), part.pdgCode(), classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2())}); + } + return result; +} + +inline const K1ParentTruth* findK1ParentTruth(const K1ParentTruthMap& parents, int64_t motherId, + K1TruthChannel channel, int charge) +{ + const auto it = parents.find(motherId); + if (it == parents.end()) { + return nullptr; + } + const auto& parent = it->second; + const int expectedPdg = charge * o2::constants::physics::Pdg::kK1_1270Plus; + if (channel == K1TruthChannel::None || parent.channel != channel || parent.pdgCode != expectedPdg || + !std::isfinite(parent.pt) || parent.pt < 0. || !std::isfinite(parent.y)) { + return nullptr; + } + return &parent; +} + +// Immediate decay channel of a generated K1 from the PDG codes of its two daughters. +inline K1TruthChannel classifyGeneratedK1(int charge, int daughter1, int daughter2) +{ + if ((daughter1 == kRho770_0 && daughter2 == charge * kKPlus) || + (daughter2 == kRho770_0 && daughter1 == charge * kKPlus)) { + return K1TruthChannel::RhoK; + } + if ((daughter1 == charge * o2::constants::physics::Pdg::kK0Star892 && daughter2 == charge * kPiPlus) || + (daughter2 == charge * o2::constants::physics::Pdg::kK0Star892 && daughter1 == charge * kPiPlus)) { + return K1TruthChannel::KStarPi; + } + return K1TruthChannel::None; +} + +/// K1 selection and candidate enumeration shared by the K1 tasks. +/// The task owns the configurable groups and the histogram registry and passes them in init(). +class K1AnalysisMicroCore +{ + public: + void init(o2::framework::HistogramRegistry& histos, + EventCuts const& eventCuts, TrackCuts const& trackCuts, + PionPidCuts const& pionPidCuts, KaonPidCuts const& kaonPidCuts, + SecondaryCuts const& secondaryCuts, CandidateCuts const& candidateCuts, + ProcessModes const& modes, LooseStageOptions const& looseOptions = {}) + { + mSecondaryCuts = secondaryCuts; + mCandidateCuts = candidateCuts; + mLooseOptions = looseOptions; + + // The order follows Species + std::vector pid(2); + auto& pion = pid[static_cast(Species::Pion)]; + pion.species = "Pion"; + pion.maxTPCnSigma = pionPidCuts.cMaxTPCnSigmaPion.value; + pion.maxTOFnSigma = pionPidCuts.cMaxTOFnSigmaPion.value; + pion.combinedNSigma = pionPidCuts.nsigmaCutCombinedPion.value; + pion.onlyTOFTracks = pionPidCuts.cUseOnlyTOFTrackPi.value; + pion.usePtDependent = pionPidCuts.cPionUsePtDepPID.value; + pion.ptBins = pionPidCuts.cPionPIDPtBins.value; + pion.tpcNSigmaCuts = pionPidCuts.cPionTPCNSigmaCuts.value; + pion.tofNSigmaCuts = pionPidCuts.cPionTOFNSigmaCuts.value; + pion.tofRequired = pionPidCuts.cPionTOFRequired.value; + pion.maxTPCName = "cMaxTPCnSigmaPion"; + pion.maxTOFName = "cMaxTOFnSigmaPion"; + pion.tpcCutsName = "cPionTPCNSigmaCuts"; + pion.tofCutsName = "cPionTOFNSigmaCuts"; + auto& kaon = pid[static_cast(Species::Kaon)]; + kaon.species = "Kaon"; + kaon.maxTPCnSigma = kaonPidCuts.cMaxTPCnSigmaKaon.value; + kaon.maxTOFnSigma = kaonPidCuts.cMaxTOFnSigmaKaon.value; + kaon.combinedNSigma = kaonPidCuts.nsigmaCutCombinedKaon.value; + kaon.onlyTOFTracks = kaonPidCuts.cUseOnlyTOFTrackKa.value; + kaon.usePtDependent = kaonPidCuts.cKaonUsePtDepPID.value; + kaon.ptBins = kaonPidCuts.cKaonPIDPtBins.value; + kaon.tpcNSigmaCuts = kaonPidCuts.cKaonTPCNSigmaCuts.value; + kaon.tofNSigmaCuts = kaonPidCuts.cKaonTOFNSigmaCuts.value; + kaon.tofRequired = kaonPidCuts.cKaonTOFRequired.value; + kaon.maxTPCName = "cMaxTPCnSigmaKaon"; + kaon.maxTOFName = "cMaxTOFnSigmaKaon"; + kaon.tpcCutsName = "cKaonTPCNSigmaCuts"; + kaon.tofCutsName = "cKaonTOFNSigmaCuts"; + mSelection.init(eventCuts, trackCuts, std::move(pid), mCandidateCuts.cByPassTOF, modes.microTracks); + + mSecondaryWindowOn = isCutEnabled(mSecondaryCuts.cSecondaryMasswindow); + mAnotherMassCutOn = isCutEnabled(mSecondaryCuts.cMinAnotherSecondaryMassCut) || isCutEnabled(mSecondaryCuts.cMaxAnotherSecondaryMassCut); + mPiKaMassCutOn = isCutEnabled(mSecondaryCuts.cMinPiKaMassCut) || isCutEnabled(mSecondaryCuts.cMaxPiKaMassCut); + mAngleCutOn = isCutEnabled(mSecondaryCuts.cMinAngle) || isCutEnabled(mSecondaryCuts.cMaxAngle); + mPairAsymCutOn = isCutEnabled(mSecondaryCuts.cMinPairAsym) || isCutEnabled(mSecondaryCuts.cMaxPairAsym); + + registerHistograms(histos, modes); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return mSelection.passesEventCuts(collision); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + return mSelection.passesMCEventCuts(collision); + } + + // Full selection stage of a track (quality, TOF requirement, PID) + template + int trackSelectionStage(const TrackType& track) + { + const int qualityStage = mSelection.trackQualityStage(track); + if (qualityStage < TrackStage::kTrkClusters) { + return qualityStage; + } + constexpr int SpeciesIndex = static_cast(S); + if (!mSelection.passesTOFRequired(SpeciesIndex, track)) { + return TrackStage::kTrkClusters; + } + const bool hasTOF = track.hasTOF(); + const double tpcNSigma = (S == Species::Pion) ? track.tpcNSigmaPi() : track.tpcNSigmaKa(); + double tofNSigma = std::numeric_limits::quiet_NaN(); // TOF value is only valid with hasTOF + if constexpr (S == Species::Pion) { + if (hasTOF) { + tofNSigma = track.tofNSigmaPi(); + } + } else { + if (hasTOF) { + tofNSigma = track.tofNSigmaKa(); + } + } + if (!mSelection.passesPID(SpeciesIndex, track.pt(), hasTOF, tpcNSigma, tofNSigma)) { + return TrackStage::kTrkTOFRequired; + } + return TrackStage::kTrkPID; + } + + // Unordered (pion, pion, kaon) candidate enumeration of one collision (or one mixed pair of collisions). + // dTracks1: bachelor kaons, dTracks2: pions. The core fills the cut-flow instrumentation; the hooks + // (nullptr to skip) receive + // - onPair(trk1, trk2, secondary, passesPairPt): every pion pair passing the pion selection, + // trk1 being the pion with the lower index, + // - onCandidate(kaon, pion1, pion2, K1CandidateValues): every triplet passing the pion, pair and kaon + // selection, with the canonical pion roles (pion1: opposite sign to the kaon in the unlike-sign case), + // - onExport(collision, kaon, same-sign pion, opposite-sign pion, truth channel, pass bits): the unlike-sign + // micro same-event candidates at the loose or selected stage configured by LooseStageOptions. + // computeValues requests mass13, mass23, angle and pairAsym for every candidate in the rapidity window. + template + void forEachCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2, + bool computeValues, PairHook onPair = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + if (dTracks1.size() == 0 || dTracks2.size() == 0) { + return; + } + constexpr bool HasPairHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + // Sets are local to this reconstructed collision: IDs cannot leak across DFs. + // Source-file/DF deduplication across split collisions belongs in the audit. + std::array, NTruthChannels> matchedMothers; + + // Selection cache: every track is selected once, not once per pair x bachelor. + // dTracks1: bachelor kaons, dTracks2: pions (different collisions in mixed events). + constexpr bool FillCutFlow = IsResoMicrotrack && !IsMix; + const int64_t firstKaonIndex = dTracks1.begin().index(); + const int64_t firstPionIndex = dTracks2.begin().index(); + const auto kaonSelected = buildSelectionCache(histos, dTracks1, firstKaonIndex); + const auto pionSelected = buildSelectionCache(histos, dTracks2, firstPionIndex); + + // Only micro same-event ML work needs traversal before conventional cuts. + // The canonical-candidate validity is also required before a selected-stage export. + bool visitLoose = false; + bool checkValidity = false; + if constexpr (IsResoMicrotrack && !IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + std::vector kaonQuality(dTracks1.size(), 0), pionQuality(dTracks2.size(), 0); + if (visitLoose) { + for (const auto& track : dTracks1) { + kaonQuality[getCacheIndex(track, firstKaonIndex, kaonQuality.size())] = mSelection.trackQualityStage(track) == TrackStage::kTrkClusters; + } + for (const auto& track : dTracks2) { + pionQuality[getCacheIndex(track, firstPionIndex, pionQuality.size())] = mSelection.trackQualityStage(track) == TrackStage::kTrkClusters; + } + } + + // Values needed only by switched-on cuts or by the task are computed only then + const bool isK892Mode = mSecondaryCuts.cfgModeK892orRho; + const bool needAngle = computeValues || mAngleCutOn; + const bool needPairAsym = computeValues || mPairAsymCutOn; + // K892 mode: the K* candidate is (trk1, K), rho mode: the rho is (trk1, trk2) + const bool needMass13 = computeValues || (isK892Mode ? mSecondaryWindowOn : mAnotherMassCutOn) || (isK892Mode && (needAngle || needPairAsym)); + const bool needMass23 = computeValues || mPiKaMassCutOn; + const bool rhoWindowOn = mSecondaryWindowOn && !isK892Mode; + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonanceSecondary, lDecayDaughter_bach, lResonanceK1, lPair13, lPair23; + // Unordered pion pairs: each (pion, pion, kaon) triplet is visited once. + // Here trk1 is the pion with the lower index; the roles are assigned once the bachelor is known. + for (const auto& [trk1, trk2] : o2::soa::combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(dTracks2, dTracks2))) { + // trk1: pion, trk2: pion, bTrack: kaon + const bool pionsSelected = pionSelected[getCacheIndex(trk1, firstPionIndex, pionSelected.size())] && pionSelected[getCacheIndex(trk2, firstPionIndex, pionSelected.size())]; + bool pairPt = false; + bool rhoWindow = true; + if (pionsSelected || visitLoose) { + // Resonance reconstruction + lDecayDaughter1.SetCoordinates(trk1.px(), trk1.py(), trk1.pz(), o2::constants::physics::MassPionCharged); + lDecayDaughter2.SetCoordinates(trk2.px(), trk2.py(), trk2.pz(), o2::constants::physics::MassPionCharged); + lResonanceSecondary = lDecayDaughter1 + lDecayDaughter2; + pairPt = !(lResonanceSecondary.Pt() < mSecondaryCuts.cMinSecondaryPtCut); + rhoWindow = !rhoWindowOn || isInWindow(lResonanceSecondary.M(), MassRho770, mSecondaryCuts.cSecondaryMasswindow); + } + if constexpr (FillCutFlow) { + // Early stages count potential triplets: each pair carries N bachelor trials. + // This preserves the pair-first reconstruction and avoids a new cubic data loop. + // Distinct pion IDs are guaranteed by the strictly upper index policy (stage 1 is always passed). + const int lastStage = !pionsSelected ? 1 : !pairPt ? 3 + : !rhoWindow ? 4 + : 5; + for (int stage = 0; stage <= lastStage; ++stage) { + histos.fill(HIST("CutFlow/candidates"), stage, 0, static_cast(dTracks1.size())); + } + if constexpr (IsMC) { + // Match before rejecting quality/pT so both channels have an upstream numerator. + if (std::abs(trk1.pdgCode()) == kPiPlus && trk1.pdgCode() == -trk2.pdgCode()) { + for (const auto& bachelor : dTracks1) { + const auto channel = classifyK1Truth(trk1, trk2, bachelor); + if (channel != K1TruthChannel::None) { + for (int stage = 0; stage <= lastStage; ++stage) { + histos.fill(HIST("CutFlow/candidates"), stage, static_cast(channel)); + } + } + } + } + } + } + if (!pionsSelected && !visitLoose) { + continue; + } + if constexpr (HasPairHook) { + if (pionsSelected) { + onPair(trk1, trk2, lResonanceSecondary, pairPt); + } + } + if (!pairPt && !visitLoose) { + continue; + } + // Secondary mass window (rho mode): the bachelor loop is skipped for rejected pairs + if (!rhoWindow && !visitLoose) { + continue; + } + + for (const auto& bTrack : dTracks1) { + if (bTrack.index() == trk1.index() || bTrack.index() == trk2.index()) { + continue; + } + K1TruthChannel flowChannel = K1TruthChannel::None; + if constexpr (IsMC && IsResoMicrotrack && !IsMix) { + flowChannel = classifyK1Truth(trk1, trk2, bTrack); + } + auto countCandidate = [&](int stage) { + if constexpr (FillCutFlow) { + histos.fill(HIST("CutFlow/candidates"), stage, 0); + if constexpr (IsMC) { + if (flowChannel != K1TruthChannel::None) { + histos.fill(HIST("CutFlow/candidates"), stage, static_cast(flowChannel)); + } + } + } + }; + const bool pairSelected = pionsSelected && pairPt && rhoWindow; + const bool bachelorSelected = kaonSelected[getCacheIndex(bTrack, firstKaonIndex, kaonSelected.size())]; + if (pairSelected) { + countCandidate(6); + } + if ((!pairSelected || !bachelorSelected) && !visitLoose) { + continue; + } + const bool tripletSelected = pairSelected && bachelorSelected; + if (tripletSelected) { + countCandidate(7); + } + + // Canonical assignment of the pion roles, once the bachelor is known. + // Unlike-sign pair: the pion with the sign opposite to the kaon is pion 1 (K*0 partner), the other is pion 2. + // Like-sign pair (the rule is ambiguous): the pion with the lower index is pion 1. + const bool isUnlikeSign = trk1.sign() * trk2.sign() < 0; + const bool swapPions = isUnlikeSign && trk1.sign() == bTrack.sign(); + const auto& pion1 = swapPions ? trk2 : trk1; + const auto& pion2 = swapPions ? trk1 : trk2; + const auto& lPion1 = swapPions ? lDecayDaughter2 : lDecayDaughter1; + const auto& lPion2 = swapPions ? lDecayDaughter1 : lDecayDaughter2; + + // K1 reconstruction + lDecayDaughter_bach.SetCoordinates(bTrack.px(), bTrack.py(), bTrack.pz(), o2::constants::physics::MassKaonCharged); + lResonanceK1 = lResonanceSecondary + lDecayDaughter_bach; + K1CandidateValues values; + values.k1 = lResonanceK1; + values.secondary = lResonanceSecondary; + values.isUnlikeSign = isUnlikeSign; + + auto countMl = [&](int stage) { + if (mLooseOptions.audit && isUnlikeSign) { + histos.fill(HIST("ML/looseCutflow"), stage, 0); + if (flowChannel != K1TruthChannel::None) { + const int stratum = 2 * static_cast(flowChannel) - (bTrack.sign() > 0 ? 1 : 0); + histos.fill(HIST("ML/looseCutflow"), stage, stratum); + } + } + }; + bool validLoose = false; + if constexpr (IsResoMicrotrack && !IsMix) { + if (checkValidity && isUnlikeSign) { + const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(bTrack), o2::analysis::k1ml::makeTrackSnapshot(pion2), o2::analysis::k1ml::makeTrackSnapshot(pion1)); + validLoose = canonical.status == o2::analysis::k1ml::BuildStatus::Ok && + std::isfinite(lResonanceK1.M()) && std::isfinite(lResonanceK1.Pt()) && + std::isfinite(lResonanceK1.Rapidity()) && std::isfinite(lResonanceK1.Eta()) && + std::isfinite(lResonanceK1.Phi()) && + o2::analysis::k1ml::buildMasterFeatures(canonical.candidate).status == o2::analysis::k1ml::BuildStatus::Ok; + if (validLoose) { + countMl(0); + } + } + } + + // Stage L common acceptance uses the existing inclusive rapidity window. + values.inRapidity = lResonanceK1.Rapidity() >= mCandidateCuts.cK1MinRap && lResonanceK1.Rapidity() <= mCandidateCuts.cK1MaxRap; + if (!values.inRapidity) { + if constexpr (HasCandidateHook) { + if (tripletSelected) { + onCandidate(bTrack, pion1, pion2, values); + } + } + continue; + } + if (tripletSelected) { + countCandidate(8); + } + + if (needMass13) { + lPair13 = lPion1 + lDecayDaughter_bach; + values.mass13 = lPair13.M(); + } + if (needMass23) { + lPair23 = lPion2 + lDecayDaughter_bach; + values.mass23 = lPair23.M(); + } + // Rho mode: secondary = (trk1, trk2) against the bachelor. K892 mode: secondary = (trk1, K) against trk2. + if (needAngle) { + values.angle = isK892Mode ? ROOT::Math::VectorUtil::Angle(lPair13, lPion2) : ROOT::Math::VectorUtil::Angle(lResonanceSecondary, lDecayDaughter_bach); + } + if (needPairAsym) { + values.pairAsym = isK892Mode ? (lPair13.E() - lPion2.E()) / (lPair13.E() + lPion2.E()) + : (lResonanceSecondary.E() - lDecayDaughter_bach.E()) / (lResonanceSecondary.E() + lDecayDaughter_bach.E()); + } + + // Candidate cuts (each one is evaluated only if switched on) + values.passesCandidateCuts = + (!isK892Mode || !mSecondaryWindowOn || (isInWindow(values.mass13, o2::constants::physics::MassK0Star892, mSecondaryCuts.cSecondaryMasswindow) && pion1.sign() != bTrack.sign())) && + (!mAnotherMassCutOn || isInRange(isK892Mode ? lResonanceSecondary.M() : values.mass13, mSecondaryCuts.cMinAnotherSecondaryMassCut, mSecondaryCuts.cMaxAnotherSecondaryMassCut)) && + (!mPiKaMassCutOn || isInRange(values.mass23, mSecondaryCuts.cMinPiKaMassCut, mSecondaryCuts.cMaxPiKaMassCut)) && + (!mAngleCutOn || isInRange(values.angle, mSecondaryCuts.cMinAngle, mSecondaryCuts.cMaxAngle)) && + (!mPairAsymCutOn || isInRange(values.pairAsym, mSecondaryCuts.cMinPairAsym, mSecondaryCuts.cMaxPairAsym)); + auto exportCandidate = [&](uint16_t passBits) { + if constexpr (IsResoMicrotrack && !IsMix && HasExportHook) { + onExport(collision, bTrack, pion2, pion1, flowChannel, passBits); + } else { + static_cast(passBits); + } + }; + if (visitLoose && validLoose) { + countMl(1); + if (mLooseOptions.audit) { + histos.fill(HIST("ML/looseMassPtActivity"), lResonanceK1.M(), lResonanceK1.Pt(), collision.cent()); + } + const bool qualityPass = kaonQuality[getCacheIndex(bTrack, firstKaonIndex, kaonQuality.size())] && + pionQuality[getCacheIndex(trk1, firstPionIndex, pionQuality.size())] && + pionQuality[getCacheIndex(trk2, firstPionIndex, pionQuality.size())]; + uint16_t passBits = kPassLoose; + if (qualityPass) { + passBits |= kPassQuality; + countMl(2); + if (pionsSelected && bachelorSelected) { + passBits |= kPassPID; + countMl(3); + if (pairPt && rhoWindow) { + passBits |= kPassPair; + countMl(4); + if (values.passesCandidateCuts) { + passBits |= kPassCandidate; + countMl(5); + } + } + } + } + if (!mLooseOptions.exportSelected) { + exportCandidate(passBits); + } + } + // Stage C retains the frozen conventional selections. + if (!tripletSelected) { + continue; + } + if (values.passesCandidateCuts) { + countCandidate(9); + countCandidate(isUnlikeSign ? 10 : 11); + if (isUnlikeSign && mLooseOptions.exportSelected && validLoose) { + exportCandidate(PassBitsSelected); + } + if constexpr (IsMC && IsResoMicrotrack && !IsMix) { + if (flowChannel != K1TruthChannel::None) { + const int mother = flowChannel == K1TruthChannel::RhoK ? bTrack.motherId() : std::abs(pion1.motherPDG()) == o2::constants::physics::Pdg::kK1_1270Plus ? pion1.motherId() + : pion2.motherId(); + if (matchedMothers[static_cast(flowChannel)].insert(mother).second) { + histos.fill(HIST("CutFlow/uniqueMothersPerCollision"), static_cast(flowChannel)); + } + } + } + } + if constexpr (HasCandidateHook) { + onCandidate(bTrack, pion1, pion2, values); + } + } // bTrack + } + } // forEachCandidate + + // Generated K1 parents of a selected reconstructed MC collision. The optional callback receives + // (parent, immediate channel) for the parents inside the K1 rapidity window. + // Parents belong to selected reconstructed events; split reco collisions + // repeat parent sets. This is not an unconditional generated denominator. + template + void forEachGeneratedK1(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, Callback callback = nullptr) + { + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kK1_1270Plus) { + continue; + } + const int charge = part.pdgCode() > 0 ? 1 : -1; + const K1TruthChannel channel = classifyGeneratedK1(charge, part.daughterPDG1(), part.daughterPDG2()); + histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); + if (!std::isfinite(part.pt()) || part.pt() < 0. || !std::isfinite(part.y()) || + part.y() < mCandidateCuts.cK1MinRap || part.y() > mCandidateCuts.cK1MaxRap) { + continue; + } + histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); + if constexpr (!std::is_same_v) { + callback(part, channel); + } + } + } + + private: + // Selection cache of one track slice. The row number of a grouped slice is global, hence the offset. + template + static std::size_t getCacheIndex(const TrackType& track, int64_t firstIndex, std::size_t size) + { + const int64_t index = static_cast(track.index()) - firstIndex; + if (index < 0 || index >= static_cast(size)) { + LOG(fatal) << "Track index " << track.index() << " is outside the selection cache [" << firstIndex << ", " << firstIndex + static_cast(size) << ")"; + } + return static_cast(index); + } + + template + std::vector buildSelectionCache(o2::framework::HistogramRegistry& histos, const TracksType& tracks, int64_t firstIndex) + { + std::vector selected(tracks.size(), 0); + for (const auto& track : tracks) { + const int stage = trackSelectionStage(track); + selected[getCacheIndex(track, firstIndex, selected.size())] = (stage == TrackStage::kTrkPID) ? 1 : 0; + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/tracks"), i, static_cast(S)); + } + } + } + return selected; + } + + // Cut-flow instrumentation of the selection; the output histograms belong to the tasks. + void registerHistograms(o2::framework::HistogramRegistry& histos, ProcessModes const& modes) + { + using o2::framework::HistType; + if (mLooseOptions.audit) { + auto flow = histos.add("ML/looseCutflow", "US triplets;stage;signal stratum", HistType::kTH2D, {{6, -0.5, 5.5}, {5, -0.5, 4.5}}); + const std::array labels{"structural US", "loose acceptance", "track quality", "TOF + PID", "pair requirements", "selected US"}; + const std::array strata{"all US", "rhoK+", "rhoK-", "KstarPi+", "KstarPi-"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + for (std::size_t i = 0; i < strata.size(); ++i) { + flow->GetYaxis()->SetBinLabel(i + 1, strata[i]); + } + histos.add("ML/looseMassPtActivity", "Loose US;mass (GeV/c^{2});pT (GeV/c);FT0M percentile", HistType::kTH3D, + {{300, 0.7, 3.7}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "FT0M percentile"}}); + } + + // Micro-only instrumentation: category 0 includes all combinations, not just unmatched. + constexpr int NTrackStages = TrackStage::kTrkNStages; + auto trackFlow = histos.add("CutFlow/tracks", "Micro tracks, once per selected collision;stage;species", HistType::kTH2D, {{NTrackStages, -0.5, NTrackStages - 0.5}, {2, -0.5, 1.5}}); + const std::array trackLabels{"input", "pT", "eta", "DCAxy", "DCAz", "track flags", "clusters / crossed rows", "TOF required", "PID"}; + for (std::size_t i = 0; i < trackLabels.size(); ++i) { + trackFlow->GetXaxis()->SetBinLabel(i + 1, trackLabels[i]); + } + trackFlow->GetYaxis()->SetBinLabel(1, "pion"); + trackFlow->GetYaxis()->SetBinLabel(2, "kaon"); + auto candidateFlow = histos.add("CutFlow/candidates", "Unordered micro triplets;stage;category", HistType::kTH2D, {{NCandidateStages, -0.5, NCandidateStages - 0.5}, {3, -0.5, 2.5}}); + const std::array candidateLabels{"input unordered triplets", "distinct pion IDs", "pion selection (quality+PID)", "pion pair constructed", "pair pT", "secondary mass window (rho mode)", "three distinct IDs", "kaon selection (quality+PID)", "K1 rapidity", "candidate cuts", "final US", "final LS"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "rho K"); + candidateFlow->GetYaxis()->SetBinLabel(3, "K* pi"); + if (modes.mcRecoMicro) { + auto mothers = histos.add("CutFlow/uniqueMothersPerCollision", "Final unique K1 IDs summed over reconstructed collisions (not globally deduplicated)", HistType::kTH1D, {{2, 0.5, 2.5}}); + mothers->GetXaxis()->SetBinLabel(1, "rho K"); + mothers->GetXaxis()->SetBinLabel(2, "K* pi"); + } + if (modes.mcGen) { + auto generated = histos.add("CutFlow/generated", "K1 parent rows conditional on selected reconstructed events;stage;immediate channel", HistType::kTH2D, {{2, -0.5, 1.5}, {3, -0.5, 2.5}}); + generated->GetXaxis()->SetBinLabel(1, "all K1 parent rows"); + generated->GetXaxis()->SetBinLabel(2, "K1 rapidity window"); + generated->GetYaxis()->SetBinLabel(1, "other / unresolved"); + generated->GetYaxis()->SetBinLabel(2, "rho K"); + generated->GetYaxis()->SetBinLabel(3, "K* pi"); + } + } + + ResoAnalysisSelectionCore mSelection; + SecondaryCuts mSecondaryCuts; + CandidateCuts mCandidateCuts; + LooseStageOptions mLooseOptions; + + // Derived once in init(): which candidate cuts are switched on. + bool mSecondaryWindowOn = false; + bool mAnotherMassCutOn = false; + bool mPiKaMassCutOn = false; + bool mAngleCutOn = false; + bool mPairAsymCutOn = false; +}; + +} // namespace o2::analysis::k1micro + +#endif // PWGLF_CORE_K1ANALYSISMICROCORE_H_ diff --git a/PWGLF/Core/K1MlFeatures.h b/PWGLF/Core/K1MlFeatures.h new file mode 100644 index 00000000000..262fb0feff7 --- /dev/null +++ b/PWGLF/Core/K1MlFeatures.h @@ -0,0 +1,873 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file K1MlFeatures.h +/// \brief Canonical micro001 K1 candidate and feature contract helpers +/// \author Bong-Hwi Lim +/// + +#ifndef PWGLF_CORE_K1MLFEATURES_H_ +#define PWGLF_CORE_K1MLFEATURES_H_ + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::k1ml +{ +inline constexpr std::size_t NItsLayers = 7; +inline constexpr std::size_t NCandidateTracks = 3; +inline constexpr std::size_t NMasterFeatures = 125; + +enum class Role : uint8_t { Kaon, + PionSame, + PionOpp }; +enum class Profile : uint8_t { DetectorV1, + RelationalV1, + SubstructureV1 }; +enum class BuildStatus : uint8_t { + Ok, + InvalidChargePattern, + ReusedTrack, + InvalidMomentum, + InvalidKinematics, + InvalidContract +}; + +struct TrackSnapshot { + int64_t sourceTrackId = -1; + int8_t charge = 0; + float px = 0.f, py = 0.f, pz = 0.f; + uint8_t pidNSigmaPiFlag = 0, pidNSigmaKaFlag = 0, pidNSigmaPrFlag = 0; + uint8_t trackSelectionFlags = 0, trackFlags = 0, tpcNClsCrossedRows = 0, itsClusterMap = 0; + std::array tpcNSigma{}; // pi, ka, pr + std::array tofNSigma{}; // pi, ka, pr + float dcaXY = std::numeric_limits::quiet_NaN(); + float dcaZ = std::numeric_limits::quiet_NaN(); + bool passedPtDependentDCAxy = false, passedPtDependentDCAz = false; + bool hasTOF = false, isPVContributor = false; + std::array itsHit{}; +}; + +struct CandidateSnapshot { + std::array tracks{}; // K, same-charge pion, opposite-charge pion +}; + +struct CanonicalizationResult { + CandidateSnapshot candidate{}; + BuildStatus status = BuildStatus::InvalidChargePattern; + explicit operator bool() const { return status == BuildStatus::Ok; } +}; + +struct KinematicAudit { + float massKPiPi = 0.f; + float massPiPi = 0.f; + float massKaonPiSame = 0.f; + float massKaonPiOpp = 0.f; + float candidatePt = 0.f; + float candidateEta = 0.f; + float candidatePhi = 0.f; + float scalarSumPt = 0.f; +}; + +struct FeaturePack { + std::array master{}; + BuildStatus status = BuildStatus::InvalidContract; + KinematicAudit kinematics{}; +}; + +// Adapt the real ResoMicroTracks_001 public columns and dynamic accessors. +// Keeping the raw bytes alongside decoded accessors makes the encoding auditable. +template +TrackSnapshot makeTrackSnapshot(MicroTrack const& row) +{ + TrackSnapshot out; + out.sourceTrackId = static_cast(row.trackId()); + out.charge = static_cast(row.sign()); + out.px = static_cast(row.px()); + out.py = static_cast(row.py()); + out.pz = static_cast(row.pz()); + out.pidNSigmaPiFlag = static_cast(row.pidNSigmaPiFlag()); + out.pidNSigmaKaFlag = static_cast(row.pidNSigmaKaFlag()); + out.pidNSigmaPrFlag = static_cast(row.pidNSigmaPrFlag()); + out.trackSelectionFlags = static_cast(row.trackSelectionFlags()); + out.trackFlags = static_cast(row.trackFlags()); + out.tpcNClsCrossedRows = static_cast(row.tpcNClsCrossedRows()); + out.itsClusterMap = static_cast(row.itsClusterMap()); + out.tpcNSigma = {static_cast(row.tpcNSigmaPi()), static_cast(row.tpcNSigmaKa()), static_cast(row.tpcNSigmaPr())}; + out.tofNSigma = {static_cast(row.tofNSigmaPi()), static_cast(row.tofNSigmaKa()), static_cast(row.tofNSigmaPr())}; + out.dcaXY = static_cast(row.dcaXY()); + out.dcaZ = static_cast(row.dcaZ()); + out.passedPtDependentDCAxy = row.passedPtDependentDCAxy(); + out.passedPtDependentDCAz = row.passedPtDependentDCAz(); + out.hasTOF = row.hasTOF(); + out.isPVContributor = row.isPVContributor(); + for (std::size_t layer = 0; layer < NItsLayers; ++layer) { + out.itsHit[layer] = row.hasITSHitInLayer(static_cast(layer)); + } + return out; +} + +inline bool hasFiniteMomentum(TrackSnapshot const& track) +{ + return std::isfinite(track.px) && std::isfinite(track.py) && std::isfinite(track.pz) && + std::hypot(track.px, track.py) > 0.f; +} + +inline CanonicalizationResult canonicalizeUS(TrackSnapshot const& kaon, + TrackSnapshot const& pionA, + TrackSnapshot const& pionB) +{ + CanonicalizationResult result; + if (kaon.sourceTrackId == pionA.sourceTrackId || kaon.sourceTrackId == pionB.sourceTrackId || + pionA.sourceTrackId == pionB.sourceTrackId) { + result.status = BuildStatus::ReusedTrack; + return result; + } + if ((kaon.charge != 1 && kaon.charge != -1) || (pionA.charge != 1 && pionA.charge != -1) || + (pionB.charge != 1 && pionB.charge != -1) || pionA.charge == pionB.charge || + (pionA.charge != kaon.charge && pionB.charge != kaon.charge)) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(kaon) || !hasFiniteMomentum(pionA) || !hasFiniteMomentum(pionB)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + const auto& same = pionA.charge == kaon.charge ? pionA : pionB; + const auto& opposite = pionA.charge == kaon.charge ? pionB : pionA; + result.candidate.tracks = {kaon, same, opposite}; + result.status = BuildStatus::Ok; + return result; +} + +namespace detail +{ +struct EncodedValue { + float value; + float valid; + float overflow; +}; + +inline EncodedValue encodePID(float decoded) +{ + if (std::isnan(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + if (std::isinf(decoded)) { + return {.value = std::signbit(decoded) ? -3.5f : 3.5f, .valid = 1.f, .overflow = 1.f}; + } + return {.value = decoded, .valid = 1.f, .overflow = 0.f}; +} + +inline EncodedValue encodeDCA(float decoded) +{ + if (!std::isfinite(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + const bool overflow = decoded == o2::aod::resomicrodaughter001::DCAEncoding::MaxDCA; + return {.value = decoded, .valid = 1.f, .overflow = overflow ? 1.f : 0.f}; +} + +struct Kinematics { + double pt; + double eta; + double phi; + double energy; +}; + +inline bool getKinematics(TrackSnapshot const& track, double mass, Kinematics& out) +{ + if (!hasFiniteMomentum(track)) { + return false; + } + const double px = track.px, py = track.py, pz = track.pz; + out.pt = std::hypot(px, py); + const double p = std::hypot(out.pt, pz); + out.eta = std::asinh(pz / out.pt); + out.phi = std::atan2(py, px); + out.energy = std::sqrt(p * p + mass * mass); + return std::isfinite(out.pt) && std::isfinite(out.eta) && std::isfinite(out.phi) && std::isfinite(out.energy); +} + +inline float invariantMass(Kinematics const& a, Kinematics const& b) +{ + const auto pxa = a.pt * std::cos(a.phi), pya = a.pt * std::sin(a.phi); + const auto pxb = b.pt * std::cos(b.phi), pyb = b.pt * std::sin(b.phi); + const double e = a.energy + b.energy; + const double px = pxa + pxb, py = pya + pyb; + // pz is recovered from pt*sinh(eta), matching the stored three-momentum. + const double pz = a.pt * std::sinh(a.eta) + b.pt * std::sinh(b.eta); + const double m2 = e * e - px * px - py * py - pz * pz; + return static_cast(std::sqrt(std::max(0.0, m2))); +} + +inline float invariantMass(std::array const& tracks, + std::array const& indices) +{ + double e = 0., px = 0., py = 0., pz = 0.; + for (const auto& i : indices) { + const auto& track = tracks[i]; + e += track.energy; + px += track.pt * std::cos(track.phi); + py += track.pt * std::sin(track.phi); + pz += track.pt * std::sinh(track.eta); + } + return static_cast(std::sqrt(std::max(0.0, e * e - px * px - py * py - pz * pz))); +} + +inline void append(std::array& out, std::size_t& index, EncodedValue value) +{ + out[index++] = value.value; + out[index++] = value.valid; + out[index++] = value.overflow; +} + +template +constexpr bool isStrictlyIncreasingBelow(std::array const& indices, std::size_t bound) +{ + for (std::size_t i = 0; i < N; ++i) { + if (indices[i] >= bound || (i > 0 && indices[i - 1] >= indices[i])) { + return false; + } + } + return true; +} +} // namespace detail + +// Names and projection indices are generated from feature_contract_v1.json. +inline constexpr std::array MasterFeatureNames{ + "kaon.tpc_nsigma_pi", + "kaon.tpc_nsigma_pi_valid", + "kaon.tpc_nsigma_pi_overflow", + "kaon.tpc_nsigma_ka", + "kaon.tpc_nsigma_ka_valid", + "kaon.tpc_nsigma_ka_overflow", + "kaon.tpc_nsigma_pr", + "kaon.tpc_nsigma_pr_valid", + "kaon.tpc_nsigma_pr_overflow", + "kaon.tof_nsigma_pi", + "kaon.tof_nsigma_pi_valid", + "kaon.tof_nsigma_pi_overflow", + "kaon.tof_nsigma_ka", + "kaon.tof_nsigma_ka_valid", + "kaon.tof_nsigma_ka_overflow", + "kaon.tof_nsigma_pr", + "kaon.tof_nsigma_pr_valid", + "kaon.tof_nsigma_pr_overflow", + "kaon.abs_dca_xy", + "kaon.abs_dca_xy_valid", + "kaon.abs_dca_xy_overflow", + "kaon.abs_dca_z", + "kaon.abs_dca_z_valid", + "kaon.abs_dca_z_overflow", + "kaon.passed_ptdep_dca_xy", + "kaon.passed_ptdep_dca_z", + "kaon.has_tof", + "kaon.tpc_crossed_rows", + "kaon.its_hit_l0", + "kaon.its_hit_l1", + "kaon.its_hit_l2", + "kaon.its_hit_l3", + "kaon.its_hit_l4", + "kaon.its_hit_l5", + "kaon.its_hit_l6", + "kaon.is_pv_contributor", + "kaon.pt_fraction", + "pion_same.tpc_nsigma_pi", + "pion_same.tpc_nsigma_pi_valid", + "pion_same.tpc_nsigma_pi_overflow", + "pion_same.tpc_nsigma_ka", + "pion_same.tpc_nsigma_ka_valid", + "pion_same.tpc_nsigma_ka_overflow", + "pion_same.tpc_nsigma_pr", + "pion_same.tpc_nsigma_pr_valid", + "pion_same.tpc_nsigma_pr_overflow", + "pion_same.tof_nsigma_pi", + "pion_same.tof_nsigma_pi_valid", + "pion_same.tof_nsigma_pi_overflow", + "pion_same.tof_nsigma_ka", + "pion_same.tof_nsigma_ka_valid", + "pion_same.tof_nsigma_ka_overflow", + "pion_same.tof_nsigma_pr", + "pion_same.tof_nsigma_pr_valid", + "pion_same.tof_nsigma_pr_overflow", + "pion_same.abs_dca_xy", + "pion_same.abs_dca_xy_valid", + "pion_same.abs_dca_xy_overflow", + "pion_same.abs_dca_z", + "pion_same.abs_dca_z_valid", + "pion_same.abs_dca_z_overflow", + "pion_same.passed_ptdep_dca_xy", + "pion_same.passed_ptdep_dca_z", + "pion_same.has_tof", + "pion_same.tpc_crossed_rows", + "pion_same.its_hit_l0", + "pion_same.its_hit_l1", + "pion_same.its_hit_l2", + "pion_same.its_hit_l3", + "pion_same.its_hit_l4", + "pion_same.its_hit_l5", + "pion_same.its_hit_l6", + "pion_same.is_pv_contributor", + "pion_same.pt_fraction", + "pion_opp.tpc_nsigma_pi", + "pion_opp.tpc_nsigma_pi_valid", + "pion_opp.tpc_nsigma_pi_overflow", + "pion_opp.tpc_nsigma_ka", + "pion_opp.tpc_nsigma_ka_valid", + "pion_opp.tpc_nsigma_ka_overflow", + "pion_opp.tpc_nsigma_pr", + "pion_opp.tpc_nsigma_pr_valid", + "pion_opp.tpc_nsigma_pr_overflow", + "pion_opp.tof_nsigma_pi", + "pion_opp.tof_nsigma_pi_valid", + "pion_opp.tof_nsigma_pi_overflow", + "pion_opp.tof_nsigma_ka", + "pion_opp.tof_nsigma_ka_valid", + "pion_opp.tof_nsigma_ka_overflow", + "pion_opp.tof_nsigma_pr", + "pion_opp.tof_nsigma_pr_valid", + "pion_opp.tof_nsigma_pr_overflow", + "pion_opp.abs_dca_xy", + "pion_opp.abs_dca_xy_valid", + "pion_opp.abs_dca_xy_overflow", + "pion_opp.abs_dca_z", + "pion_opp.abs_dca_z_valid", + "pion_opp.abs_dca_z_overflow", + "pion_opp.passed_ptdep_dca_xy", + "pion_opp.passed_ptdep_dca_z", + "pion_opp.has_tof", + "pion_opp.tpc_crossed_rows", + "pion_opp.its_hit_l0", + "pion_opp.its_hit_l1", + "pion_opp.its_hit_l2", + "pion_opp.its_hit_l3", + "pion_opp.its_hit_l4", + "pion_opp.its_hit_l5", + "pion_opp.its_hit_l6", + "pion_opp.is_pv_contributor", + "pion_opp.pt_fraction", + "kaon__pion_same.delta_eta", + "kaon__pion_same.sin_delta_phi", + "kaon__pion_same.cos_delta_phi", + "kaon__pion_same.z_pt", + "kaon__pion_opp.delta_eta", + "kaon__pion_opp.sin_delta_phi", + "kaon__pion_opp.cos_delta_phi", + "kaon__pion_opp.z_pt", + "pion_same__pion_opp.delta_eta", + "pion_same__pion_opp.sin_delta_phi", + "pion_same__pion_opp.cos_delta_phi", + "pion_same__pion_opp.z_pt", + "mass_pi_pi", + "mass_kaon_pion_opp"}; +inline constexpr std::array DetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109}; +inline constexpr std::array RelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122}; +inline constexpr std::array SubstructureV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124}; +static_assert(MasterFeatureNames.size() == NMasterFeatures, "master feature name count must match NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(DetectorV1Projection, NMasterFeatures), "DetectorV1 projection indices must be strictly increasing and below NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(RelationalV1Projection, NMasterFeatures), "RelationalV1 projection indices must be strictly increasing and below NMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(SubstructureV1Projection, NMasterFeatures), "SubstructureV1 projection indices must be strictly increasing and below NMasterFeatures"); +inline constexpr std::string_view FeatureContractSha256 = "39f38ece001581d8ebf57392fad045759a63ba49f57c411b9b566d7c1cc58b8a"; + +inline FeaturePack buildMasterFeatures(CandidateSnapshot const& candidate) +{ + FeaturePack pack; + for (auto const& track : candidate.tracks) { + if ((track.charge != 1 && track.charge != -1) || !hasFiniteMomentum(track)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + } + if (candidate.tracks[0].sourceTrackId == candidate.tracks[1].sourceTrackId || + candidate.tracks[0].sourceTrackId == candidate.tracks[2].sourceTrackId || + candidate.tracks[1].sourceTrackId == candidate.tracks[2].sourceTrackId || + candidate.tracks[0].charge != candidate.tracks[1].charge || + candidate.tracks[0].charge == candidate.tracks[2].charge) { + pack.status = BuildStatus::InvalidChargePattern; + return pack; + } + + std::array kin{}; + for (std::size_t i = 0; i < NCandidateTracks; ++i) { + if (!detail::getKinematics(candidate.tracks[i], i == 0 ? o2::constants::physics::MassKaonCharged : o2::constants::physics::MassPionCharged, kin[i])) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + } + const double sumPt = kin[0].pt + kin[1].pt + kin[2].pt; + if (!std::isfinite(sumPt) || sumPt <= 0.) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + const double totalPx = static_cast(candidate.tracks[0].px) + candidate.tracks[1].px + candidate.tracks[2].px; + const double totalPy = static_cast(candidate.tracks[0].py) + candidate.tracks[1].py + candidate.tracks[2].py; + const double totalPz = static_cast(candidate.tracks[0].pz) + candidate.tracks[1].pz + candidate.tracks[2].pz; + const double candidatePt = std::hypot(totalPx, totalPy); + if (!std::isfinite(candidatePt)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + pack.kinematics.scalarSumPt = static_cast(sumPt); + pack.kinematics.candidatePt = static_cast(candidatePt); + pack.kinematics.candidateEta = candidatePt > 0. ? static_cast(std::asinh(totalPz / candidatePt)) : 0.f; + pack.kinematics.candidatePhi = static_cast(std::atan2(totalPy, totalPx)); + pack.kinematics.massKPiPi = detail::invariantMass(kin, std::array{0, 1, 2}); + pack.kinematics.massPiPi = detail::invariantMass(kin[1], kin[2]); + pack.kinematics.massKaonPiSame = detail::invariantMass(kin[0], kin[1]); + pack.kinematics.massKaonPiOpp = detail::invariantMass(kin[0], kin[2]); + + std::size_t index = 0; + for (std::size_t i = 0; i < NCandidateTracks; ++i) { + const auto& track = candidate.tracks[i]; + for (const float& decoded : track.tpcNSigma) { + detail::append(pack.master, index, detail::encodePID(decoded)); + } + for (const float& decoded : track.tofNSigma) { + detail::append(pack.master, index, track.hasTOF ? detail::encodePID(decoded) : detail::EncodedValue{.value = 0.f, .valid = 0.f, .overflow = 0.f}); + } + detail::append(pack.master, index, detail::encodeDCA(track.dcaXY)); + detail::append(pack.master, index, detail::encodeDCA(track.dcaZ)); + pack.master[index++] = track.passedPtDependentDCAxy ? 1.f : 0.f; + pack.master[index++] = track.passedPtDependentDCAz ? 1.f : 0.f; + pack.master[index++] = track.hasTOF ? 1.f : 0.f; + pack.master[index++] = static_cast(track.tpcNClsCrossedRows); + for (const bool& hit : track.itsHit) { + pack.master[index++] = hit ? 1.f : 0.f; + } + pack.master[index++] = track.isPVContributor ? 1.f : 0.f; + pack.master[index++] = static_cast(kin[i].pt / sumPt); + } + + constexpr std::array, 3> PairIndices{{{{0, 1}}, {{0, 2}}, {{1, 2}}}}; + for (auto const& pair : PairIndices) { + const auto i = pair[0], j = pair[1]; + const double dphi = kin[i].phi - kin[j].phi; + const double zDenominator = kin[i].pt + kin[j].pt; + if (!std::isfinite(dphi) || zDenominator <= 0.) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + pack.master[index++] = static_cast(kin[i].eta - kin[j].eta); + pack.master[index++] = static_cast(std::sin(dphi)); + pack.master[index++] = static_cast(std::cos(dphi)); + pack.master[index++] = static_cast(std::min(kin[i].pt, kin[j].pt) / zDenominator); + } + pack.master[index++] = pack.kinematics.massPiPi; + pack.master[index++] = pack.kinematics.massKaonPiOpp; + const bool allFinite = std::all_of(pack.master.begin(), pack.master.end(), [](float x) { return std::isfinite(x); }); + if (index != pack.master.size() || !allFinite) { + pack.status = BuildStatus::InvalidContract; + return pack; + } + pack.status = BuildStatus::Ok; + return pack; +} + +inline std::vector projectFeatures(FeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + std::vector projected; + switch (profile) { + case Profile::DetectorV1: + projected.reserve(DetectorV1Projection.size()); + for (const auto& i : DetectorV1Projection) { + projected.push_back(pack.master[i]); + } + break; + case Profile::RelationalV1: + projected.reserve(RelationalV1Projection.size()); + for (const auto& i : RelationalV1Projection) { + projected.push_back(pack.master[i]); + } + break; + case Profile::SubstructureV1: + projected.reserve(SubstructureV1Projection.size()); + for (const auto& i : SubstructureV1Projection) { + projected.push_back(pack.master[i]); + } + break; + default: + return {}; + } + return projected; +} +} // namespace o2::analysis::k1ml + +#endif // PWGLF_CORE_K1MLFEATURES_H_ diff --git a/PWGLF/Core/Omega2012AnalysisCore.h b/PWGLF/Core/Omega2012AnalysisCore.h new file mode 100644 index 00000000000..57444555806 --- /dev/null +++ b/PWGLF/Core/Omega2012AnalysisCore.h @@ -0,0 +1,1281 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Omega2012AnalysisCore.h +/// \brief Omega(2012) selection, candidate enumeration and truth classification, separately for the two decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (DecayMode::XiK0s): Omega(2012)- -> Xi- K0S. +/// Mode B (DecayMode::Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- (charged kaon track). +/// The two modes are never merged: each has its own enumeration, hooks, cut flow, pass bits and truth classification. +/// The Xi selection is common to both modes. The core owns the selection and its cut-flow instrumentation +/// (CutFlow/*, ML/*); the tasks own their output histograms and fill them from the hooks. + +#ifndef PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ +#define PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ + +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" + +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::omega2012 +{ + +using o2::analysis::resonance::EventCuts; +using o2::analysis::resonance::PIDCutConfig; +using o2::analysis::resonance::ResoAnalysisSelectionCore; +using o2::analysis::resonance::TrackCuts; +using o2::analysis::resonance::TrackStage; + +// Omega(2012) analysis and ML export share these event-level selection defaults. +// Keep the generic EventCuts defaults unchanged for other resonance tasks. +inline EventCuts makeOmega2012EventCuts() +{ + EventCuts cuts; + cuts.cRecoINELgt0.value = true; + cuts.cMCINELgt0.value = true; + cuts.cMCVtxIn10.value = true; + return cuts; +} + +enum class DecayMode : uint8_t { + XiK0s = 0, // Omega(2012)- -> Xi- K0S + Xi1530K = 1 // Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- +}; + +inline constexpr int PdgXi1530Zero = 3324; // Xi(1530)0, not in o2::constants::physics::Pdg +inline constexpr float SmallNumber = 1e-10f; // avoids division by zero (value of the original task) +inline constexpr float MaxDCAV0ToPV = 1.0f; // maximum K0S DCA to PV (value of the original task) +inline constexpr float CascDCAzPtExponent = -1.1f; +inline constexpr const char* TrackCutsPrefix = "trk."; // JSON prefix of the mode-B track selection +inline constexpr int NXiK0sCandidateStages = 4; +inline constexpr int NXi1530KCandidateStages = 4; +inline constexpr int NGeneratedChannels = 3; + +// Last stage passed by a cascade (Xi selection, common to both modes). +enum XiStage : int { + kXiInput = 0, // failed |eta| or pT + kXiKinematics = 1, // passed |eta| and pT + kXiDCA = 2, // passed the cascade DCA to PV + kXiV0Topology = 3, // passed the Lambda topology and mass window + kXiCascTopology = 4, // passed the cascade topology + kXiMass = 5, // passed the Xi mass window + kXiSelected = kXiMass, + kXiNStages = 6 +}; + +// Last stage passed by a V0 (K0S selection of mode A). +enum K0sStage : int { + kK0sInput = 0, // failed |eta| or pT + kK0sKinematics = 1, // passed |eta| and pT + kK0sTopology = 2, // passed cosPA, daughter DCAs, radius and DCA to PV + kK0sLifetime = 3, // passed the proper lifetime and the minimum qT + kK0sMass = 4, // passed the K0S mass window and the (anti)Lambda rejection + kK0sDaughters = 5, // passed the daughter pion TPC nSigma and crossed rows + kK0sArmenteros = 6, // passed the Armenteros qT > coefficient * |alpha| cut + kK0sSelected = kK0sArmenteros, + kK0sNStages = 7 +}; + +// The value is the species index of the PID configuration in ResoAnalysisSelectionCore. +enum class Species : int { + Pion = 0, + Kaon = 1 +}; + +// Cumulative pass bits of mode A (see LFOmega2012MlTables.h) +enum XiK0sPassBit : uint16_t { + kXiK0sPassLoose = 1, + kXiK0sPassXi = 2, + kXiK0sPassK0s = 4, + kXiK0sPassKinematic = 8 +}; +inline constexpr uint16_t XiK0sPassBitsSelected = kXiK0sPassLoose | kXiK0sPassXi | kXiK0sPassK0s | kXiK0sPassKinematic; + +// Cumulative pass bits of mode B (see LFOmega2012MlTables.h) +enum Xi1530KPassBit : uint16_t { + kXi1530KPassLoose = 1, + kXi1530KPassXi = 2, + kXi1530KPassQuality = 4, + kXi1530KPassPID = 8, + kXi1530KPassWindow = 16 +}; +inline constexpr uint16_t Xi1530KPassBitsSelected = kXi1530KPassLoose | kXi1530KPassXi | kXi1530KPassQuality | kXi1530KPassPID | kXi1530KPassWindow; + +enum class XiK0sTruth : uint8_t { + None = 0, + Matched = 1 +}; + +enum class Xi1530KTruth : uint8_t { + None = 0, + Matched = 1 +}; + +// Immediate decay channel of a generated Omega(2012) +enum class GeneratedChannel : uint8_t { + Other = 0, + XiK0s = 1, + Xi1530K = 2 +}; + +// Configurable groups without prefix: the JSON keys are the plain configurable names of the original task. + +/// Xi (cascade) selection, common to both modes. cMinPtcut is also the K0S minimum pT (as in the original task). +struct XiCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinPtcut{"cMinPtcut", 0.15, "Minimum pT for candidates"}; + o2::framework::Configurable cMaxEtaCut{"cMaxEtaCut", 0.8, "Maximum |eta|"}; + o2::framework::Configurable cDCAxyToPVByPtCascP0{"cDCAxyToPVByPtCascP0", 999., "Cascade DCAxy p0"}; + o2::framework::Configurable cDCAxyToPVByPtCascExp{"cDCAxyToPVByPtCascExp", 1., "Cascade DCAxy exp"}; + o2::framework::Configurable cDCAxyToPVAsPtForCasc{"cDCAxyToPVAsPtForCasc", true, "Use pt-dep DCAxy cut (casc)"}; + o2::framework::Configurable cDCAzToPVAsPtForCasc{"cDCAzToPVAsPtForCasc", true, "Use pt-dep DCAz cut (casc)"}; + // V0 topology inside cascade (Lambda) + o2::framework::Configurable cDCALambdaDaugtherscut{"cDCALambdaDaugtherscut", 0.7, "Λ daughters DCA cut"}; + o2::framework::Configurable cDCALambdaToPVcut{"cDCALambdaToPVcut", 0.02, "Λ DCA to PV min"}; + o2::framework::Configurable cDCAPionToPVcut{"cDCAPionToPVcut", 0.06, "π DCA to PV min"}; + o2::framework::Configurable cDCAProtonToPVcut{"cDCAProtonToPVcut", 0.07, "p DCA to PV min"}; + o2::framework::Configurable cV0CosPACutPtDepP0{"cV0CosPACutPtDepP0", 0.25, "V0 CosPA p0"}; + o2::framework::Configurable cV0CosPACutPtDepP1{"cV0CosPACutPtDepP1", 0.022, "V0 CosPA p1"}; + o2::framework::Configurable cMaxV0radiuscut{"cMaxV0radiuscut", 200., "V0 radius max"}; + o2::framework::Configurable cMinV0radiuscut{"cMinV0radiuscut", 2.5, "V0 radius min"}; + o2::framework::Configurable cMasswindowV0cut{"cMasswindowV0cut", 0.005, "Λ mass window for cascade V0"}; + // Cascade topology + o2::framework::Configurable cDCABachlorToPVcut{"cDCABachlorToPVcut", 0.06, "Bachelor DCA to PV min"}; + o2::framework::Configurable cDCAXiDaugthersCutPtRangeLower{"cDCAXiDaugthersCutPtRangeLower", 1., "Xi pt low boundary"}; + o2::framework::Configurable cDCAXiDaugthersCutPtRangeUpper{"cDCAXiDaugthersCutPtRangeUpper", 4., "Xi pt high boundary"}; + o2::framework::Configurable cDCAXiDaugthersCutPtDepLower{"cDCAXiDaugthersCutPtDepLower", 0.8, "Xi daugh DCA (pt cDCAXiDaugthersCutPtDepMiddle{"cDCAXiDaugthersCutPtDepMiddle", 0.5, "Xi daugh DCA (low<=pt cDCAXiDaugthersCutPtDepUpper{"cDCAXiDaugthersCutPtDepUpper", 0.2, "Xi daugh DCA (pt>=high)"}; + o2::framework::Configurable cCosPACascCutPtDepP0{"cCosPACascCutPtDepP0", 0.2, "Cascade CosPA p0"}; + o2::framework::Configurable cCosPACascCutPtDepP1{"cCosPACascCutPtDepP1", 0.022, "Cascade CosPA p1"}; + o2::framework::Configurable cMaxCascradiuscut{"cMaxCascradiuscut", 200., "Cascade radius max"}; + o2::framework::Configurable cMinCascradiuscut{"cMinCascradiuscut", 1.1, "Cascade radius min"}; + o2::framework::Configurable cMasswindowCasccut{"cMasswindowCasccut", 0.008, "Xi mass window"}; + o2::framework::Configurable cMassXiminus{"cMassXiminus", 1.32171, "Xi mass (GeV/c^2)"}; // PDG +}; + +/// K0S selection and Xi-K0S kinematic (opening-angle) cut of mode A +struct K0sCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cKinCuts{"cKinCuts", false, "Kinematic cuts for Xi-K0s opening angle"}; + o2::framework::Configurable> cKinCutsPt{"cKinCutsPt", {0.0, 0.4, 0.6, 0.8, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 4.0, 5.0, 6.0, 1e10}, "Omega(2012) pT bins for kinematic cuts"}; + o2::framework::Configurable> cKinLowerCutsAlpha{"cKinLowerCutsAlpha", {1.5, 1.0, 0.5, 0.3, 0.2, 0.15, 0.1, 0.08, 0.07, 0.06, 0.04, 0.02, 0.02}, "Lower cut on Xi-K0s opening angle"}; + o2::framework::Configurable> cKinUpperCutsAlpha{"cKinUpperCutsAlpha", {3.0, 2.0, 1.5, 1.4, 1.0, 0.8, 0.6, 0.5, 0.45, 0.35, 0.3, 0.25, 0.2}, "Upper cut on Xi-K0s opening angle"}; + o2::framework::Configurable cK0sMinCosPA{"cK0sMinCosPA", 0.98, "K0s minimum pointing angle cosine"}; + o2::framework::Configurable cK0sMaxDaughDCA{"cK0sMaxDaughDCA", 0.5, "K0s daughter DCA Maximum"}; + o2::framework::Configurable cK0sMassWindow{"cK0sMassWindow", 0.025, "Mass window for K0s selection (GeV/c^2)"}; + o2::framework::Configurable cMaxV0Etacut{"cMaxV0Etacut", 0.8, "V0 maximum eta cut"}; + o2::framework::Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm/c)"}; + o2::framework::Configurable cK0sArmenterosQtMin{"cK0sArmenterosQtMin", 0.0, "K0s Armenteros qt min"}; + o2::framework::Configurable cK0sArmenterosAlphaCoeff{"cK0sArmenterosAlphaCoeff", 0.2, "K0s Armenteros alpha coefficient"}; + o2::framework::Configurable cK0sDauPosDCAtoPVMin{"cK0sDauPosDCAtoPVMin", 0.05, "K0s positive daughter DCA to PV min"}; + o2::framework::Configurable cK0sDauNegDCAtoPVMin{"cK0sDauNegDCAtoPVMin", 0.05, "K0s negative daughter DCA to PV min"}; + o2::framework::Configurable cK0sRadiusMin{"cK0sRadiusMin", 0.5, "K0s decay radius min"}; + o2::framework::Configurable cK0sRadiusMax{"cK0sRadiusMax", 200.0, "K0s decay radius max"}; + o2::framework::Configurable cK0sCrossMassRejection{"cK0sCrossMassRejection", true, "Enable Lambda mass rejection for K0s"}; + o2::framework::Configurable cK0sCrossMassRejectionWindow{"cK0sCrossMassRejectionWindow", 0.01, "Lambda mass rejection window for K0s (GeV/c^2)"}; + o2::framework::Configurable cK0sDaughterPiTPCNSigmaMax{"cK0sDaughterPiTPCNSigmaMax", 5.0, "Maximum TPC NSigma for K0s daughter pions"}; + o2::framework::Configurable cK0sPosDaughterMinCrossedRows{"cK0sPosDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s positive daughter"}; + o2::framework::Configurable cK0sNegDaughterMinCrossedRows{"cK0sNegDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s negative daughter"}; +}; + +/// Xi(1530)0 K- selection of mode B +struct Xi1530KCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cXi1530Mass{"cXi1530Mass", 1.53, "Xi(1530) mass (GeV/c^2)"}; + o2::framework::Configurable cXi1530MassWindow{"cXi1530MassWindow", 0.01, "Xi(1530) mass window (GeV/c^2)"}; + o2::framework::Configurable cXi1530UseMassWindow{"cXi1530UseMassWindow", true, "Require m(Xi pi) inside the Xi(1530) mass window"}; + o2::framework::Configurable cXi1530KFillWrongSign{"cXi1530KFillWrongSign", true, "Fill the wrong-sign control histograms (xi1530K_wrongSign/)"}; + o2::framework::Configurable cByPassTOF{"cByPassTOF", false, "Bypass the TOF nSigma selection of the pion and the kaon"}; +}; + +/// Pion PID of mode B (keys of the original three-body pion selection) +struct PionPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cPionTPCNSigmaMax{"cPionTPCNSigmaMax", 3.0, "Maximum TPC NSigma for pion"}; + o2::framework::Configurable cPionTOFNSigmaMax{"cPionTOFNSigmaMax", 3.0, "Maximum TOF NSigma for pion"}; + o2::framework::Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; + o2::framework::Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; + o2::framework::Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; + o2::framework::Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; +}; + +/// Kaon PID of mode B (same shape as the pion keys) +struct KaonPidCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cKaonTPCNSigmaMax{"cKaonTPCNSigmaMax", 3.0, "Maximum TPC NSigma for kaon"}; + o2::framework::Configurable cKaonTOFNSigmaMax{"cKaonTOFNSigmaMax", 3.0, "Maximum TOF NSigma for kaon"}; + o2::framework::Configurable cKaonUsePtDepPID{"cKaonUsePtDepPID", false, "Use pT-dependent PID cuts for kaon"}; + o2::framework::Configurable> cKaonPIDPtBins{"cKaonPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for kaon PID cuts"}; + o2::framework::Configurable> cKaonTPCNSigmaCuts{"cKaonTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFNSigmaCuts{"cKaonTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (kaon)"}; + o2::framework::Configurable> cKaonTOFRequired{"cKaonTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (kaon)"}; +}; + +/// Common candidate selection +struct CandidateCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity cut"}; +}; + +/// Track selection of the mode-B pion and kaon: the common resonance TrackCuts with the JSON prefix "trk." +/// (the Xi selection owns cMinPtcut). The framework prefixes a group only through a `prefix` data member, which +/// TrackCuts does not have and a derived group cannot add (members must belong to one class for the structured +/// binding of the option registration); the names are therefore prefixed here in the same way ("trk.cMinPtcut"). +/// Defaults that differ from TrackCuts follow the original pion selection: |eta| < 0.8 (cPionEtaMax), +/// 70 found TPC clusters for ResoTracks (cPionTPCNClusMin) and DCAz < 0.15 cm (cPionDCAzMax was 0.2 cm, which is +/// beyond the 0.15 cm range of the micro001 DCA grid). +inline TrackCuts makeXi1530KTrackCuts() +{ + TrackCuts cuts; + cuts.cMaxEtacut.value = 0.8f; + cuts.cMaxDCAzToPVcut.value = 0.15; + cuts.cfgTPCcluster.value = 70; + o2::framework::homogeneous_apply_refs( + [](auto& option) { + if constexpr (requires { option.name; }) { + option.name.insert(0, TrackCutsPrefix); + } + return true; + }, + cuts); + return cuts; +} + +/// Process functions enabled in a task; they decide the configuration checks and the registered histograms. +struct ProcessModes { + bool xiK0s = false; // any mode-A process function + bool xi1530K = false; // any mode-B process function + bool microTracks = false; // any mode-B process function reading micro tracks (quantised DCA and nSigma) + bool mcReco = false; // reconstructed MC + bool mcGen = false; // generated Omega(2012) parents (ResoMCParents_001) + bool mixing = false; // event mixing +}; + +/// Loose-stage traversal for the export hook (as the K1 LooseStageOptions). +/// With the defaults and without an export hook, the enumeration applies only the conventional selection. +struct LooseStageOptions { + bool audit = false; // fill ML//looseCutflow and ML//looseMassPtActivity + bool exportSelected = false; // hand candidates to the export hook at the selected stage instead of the loose stage +}; + +/// Mode-A candidate handed to the candidate and export hooks. +/// alpha is computed when cKinCuts is on or the values were requested; passesKinCut is true when cKinCuts is off. +struct XiK0sCandidateValues { + ROOT::Math::PxPyPzEVector omega; // xi + k0s + ROOT::Math::PxPyPzEVector xi; // (pt, eta, phi, mXi) + ROOT::Math::PxPyPzEVector k0s; // (pt, eta, phi, MassK0Short) + float alpha = 0.f; // Xi-K0S opening angle + bool passesKinCut = true; + bool inRapidity = false; // |y| < cfgRapidityCut +}; + +/// Mode-B candidate handed to the candidate and export hooks. +/// massXiK, massPiK, openingAngleXi1530K and cosThetaStar are computed only when the values were requested. +struct Xi1530KCandidateValues { + ROOT::Math::PxPyPzEVector omega; // xi + pion + kaon + ROOT::Math::PxPyPzEVector xi; // (pt, eta, phi, mXi) + ROOT::Math::PxPyPzEVector pion; // (pt, eta, phi, MassPionCharged) + ROOT::Math::PxPyPzEVector kaon; // (pt, eta, phi, MassKaonCharged) + ROOT::Math::PxPyPzEVector xi1530; // xi + pion + float massXiPi = 0.f; + float massXiK = 0.f; + float massPiK = 0.f; + float openingAngleXi1530K = 0.f; + float cosThetaStar = 0.f; + uint8_t chargePattern = o2::analysis::omega2012ml::kSignalPattern; + bool inXi1530Window = false; + bool inRapidity = false; // |y| < cfgRapidityCut +}; + +// Truth classification, separately per mode + +/// Mode A: reconstructed Xi and K0S from the same Omega(2012) (criteria of the original task). +/// A K0S from an intermediate K0bar has motherPDG 311 and is not matched; the truth table keeps motherPDG for audit. +template +XiK0sTruth classifyXiK0sTruth(const Xi& xi, const V0& v0) +{ + if (std::abs(xi.pdgCode()) != kXiMinus || std::abs(v0.pdgCode()) != kK0Short) { + return XiK0sTruth::None; + } + if (xi.motherId() < 0 || xi.motherId() != v0.motherId()) { + return XiK0sTruth::None; + } + if (std::abs(xi.motherPDG()) != o2::constants::physics::Pdg::kOmega2012Minus || xi.motherPDG() != v0.motherPDG()) { + return XiK0sTruth::None; + } + return XiK0sTruth::Matched; +} + +template +bool hasSibling(const Track& track, int particleId) +{ + if (particleId < 0) { + return false; + } + const auto siblings = track.siblingIds(); + return siblings[0] == particleId || siblings[1] == particleId; +} + +/// Mode B: Xi and pion from the same Xi(1530)0, kaon from the Omega(2012) whose other daughter is that Xi(1530)0. +/// Cascades carry no sibling IDs, so the kaon carries the link (siblingIds contains the Xi(1530)0 ID). +template +Xi1530KTruth classifyXi1530KTruth(const Xi& xi, const Track& pion, const Track& kaon) +{ + if (std::abs(xi.pdgCode()) != kXiMinus) { + return Xi1530KTruth::None; + } + const int xiCharge = xi.pdgCode() > 0 ? -1 : 1; // Xi- has PDG code +3312 + if (pion.pdgCode() != -xiCharge * kPiPlus || kaon.pdgCode() != xiCharge * kKPlus) { + return Xi1530KTruth::None; + } + if (xi.motherId() < 0 || xi.motherId() != pion.motherId() || std::abs(xi.motherPDG()) != PdgXi1530Zero || pion.motherPDG() != xi.motherPDG()) { + return Xi1530KTruth::None; + } + // Omega(2012)- and Xi- both have positive PDG codes + if (kaon.motherId() < 0 || kaon.motherPDG() != -xiCharge * o2::constants::physics::Pdg::kOmega2012Minus) { + return Xi1530KTruth::None; + } + return hasSibling(kaon, xi.motherId()) ? Xi1530KTruth::Matched : Xi1530KTruth::None; +} + +/// Immediate channel of a generated Omega(2012) from the PDG codes of its two daughters (charge conjugates included). +inline GeneratedChannel classifyGeneratedOmega2012(int pdg, int daughter1, int daughter2) +{ + const int sign = pdg > 0 ? 1 : -1; + auto isPair = [&](int a, int b) { + if (a == sign * PdgXi1530Zero && b == -sign * kKPlus) { + return GeneratedChannel::Xi1530K; + } + if (a == sign * kXiMinus && (b == kK0Short || b == -sign * kK0)) { + return GeneratedChannel::XiK0s; + } + return GeneratedChannel::Other; + }; + const auto channel = isPair(daughter1, daughter2); + return channel != GeneratedChannel::Other ? channel : isPair(daughter2, daughter1); +} + +/// Omega(2012) selection and candidate enumeration of the Omega(2012) tasks. +/// The task owns the configurable groups and the histogram registry and passes them in init(). +class Omega2012AnalysisCore +{ + public: + void init(o2::framework::HistogramRegistry& histos, + EventCuts const& eventCuts, XiCuts const& xiCuts, K0sCuts const& k0sCuts, + Xi1530KCuts const& xi1530KCuts, TrackCuts const& trackCuts, + PionPidCuts const& pionPidCuts, KaonPidCuts const& kaonPidCuts, + CandidateCuts const& candidateCuts, ProcessModes const& modes, LooseStageOptions const& looseOptions = {}) + { + mXiCuts = xiCuts; + mK0sCuts = k0sCuts; + mXi1530KCuts = xi1530KCuts; + mCandidateCuts = candidateCuts; + mLooseOptions = looseOptions; + + // The order follows Species + std::vector pid(2); + auto& pion = pid[static_cast(Species::Pion)]; + pion.species = "Pion"; + pion.maxTPCnSigma = pionPidCuts.cPionTPCNSigmaMax.value; + pion.maxTOFnSigma = pionPidCuts.cPionTOFNSigmaMax.value; + pion.usePtDependent = pionPidCuts.cPionUsePtDepPID.value; + pion.ptBins = pionPidCuts.cPionPIDPtBins.value; + pion.tpcNSigmaCuts = pionPidCuts.cPionTPCNSigmaCuts.value; + pion.tofNSigmaCuts = pionPidCuts.cPionTOFNSigmaCuts.value; + pion.tofRequired = pionPidCuts.cPionTOFRequired.value; + pion.maxTPCName = "cPionTPCNSigmaMax"; + pion.maxTOFName = "cPionTOFNSigmaMax"; + pion.tpcCutsName = "cPionTPCNSigmaCuts"; + pion.tofCutsName = "cPionTOFNSigmaCuts"; + auto& kaon = pid[static_cast(Species::Kaon)]; + kaon.species = "Kaon"; + kaon.maxTPCnSigma = kaonPidCuts.cKaonTPCNSigmaMax.value; + kaon.maxTOFnSigma = kaonPidCuts.cKaonTOFNSigmaMax.value; + kaon.usePtDependent = kaonPidCuts.cKaonUsePtDepPID.value; + kaon.ptBins = kaonPidCuts.cKaonPIDPtBins.value; + kaon.tpcNSigmaCuts = kaonPidCuts.cKaonTPCNSigmaCuts.value; + kaon.tofNSigmaCuts = kaonPidCuts.cKaonTOFNSigmaCuts.value; + kaon.tofRequired = kaonPidCuts.cKaonTOFRequired.value; + kaon.maxTPCName = "cKaonTPCNSigmaMax"; + kaon.maxTOFName = "cKaonTOFNSigmaMax"; + kaon.tpcCutsName = "cKaonTPCNSigmaCuts"; + kaon.tofCutsName = "cKaonTOFNSigmaCuts"; + mSelection.init(eventCuts, trackCuts, std::move(pid), mXi1530KCuts.cByPassTOF.value, modes.microTracks); + + registerHistograms(histos, modes); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return mSelection.passesEventCuts(collision); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + return mSelection.passesMCEventCuts(collision); + } + + [[nodiscard]] bool kinCutsEnabled() const { return mK0sCuts.cKinCuts.value; } + [[nodiscard]] bool fillWrongSign() const { return mXi1530KCuts.cXi1530KFillWrongSign.value; } + + // Xi selection (common to both modes); equivalent to cascprimaryTrackCut && casctopCut of the original task. + template + [[nodiscard]] int xiSelectionStage(const CascadeType& c) const + { + const auto& cut = mXiCuts; + if (std::abs(c.eta()) > cut.cMaxEtaCut.value || std::abs(c.pt()) < cut.cMinPtcut.value) { + return kXiInput; + } + if (cut.cDCAxyToPVAsPtForCasc.value && std::abs(c.dcaXYCascToPV()) > (cut.cDCAxyToPVByPtCascP0.value + cut.cDCAxyToPVByPtCascExp.value * c.pt())) { + return kXiKinematics; + } + // The DCAz cut uses the DCAxy parameters, as in the original task + if (cut.cDCAzToPVAsPtForCasc.value && std::abs(c.dcaZCascToPV()) > (cut.cDCAxyToPVByPtCascP0.value + cut.cDCAxyToPVByPtCascExp.value * std::pow(c.pt(), CascDCAzPtExponent))) { + return kXiKinematics; + } + + // V0 (Lambda) topology inside the cascade + if (std::abs(c.daughDCA()) > cut.cDCALambdaDaugtherscut.value || std::abs(c.dcav0topv()) < cut.cDCALambdaToPVcut.value) { + return kXiDCA; + } + if (c.sign() < 0) { // Xi- + if (std::abs(c.dcanegtopv()) < cut.cDCAPionToPVcut.value || std::abs(c.dcapostopv()) < cut.cDCAProtonToPVcut.value) { + return kXiDCA; + } + } else { // Anti-Xi + if (std::abs(c.dcanegtopv()) < cut.cDCAProtonToPVcut.value || std::abs(c.dcapostopv()) < cut.cDCAPionToPVcut.value) { + return kXiDCA; + } + } + if (c.v0CosPA() < std::cos(cut.cV0CosPACutPtDepP0.value - cut.cV0CosPACutPtDepP1.value * c.pt())) { + return kXiDCA; + } + if (c.transRadius() > cut.cMaxV0radiuscut.value || c.transRadius() < cut.cMinV0radiuscut.value) { + return kXiDCA; + } + if (std::abs(c.mLambda() - o2::constants::physics::MassLambda) > cut.cMasswindowV0cut.value) { + return kXiDCA; + } + + // Cascade topology + if (std::abs(c.dcabachtopv()) < cut.cDCABachlorToPVcut.value) { + return kXiV0Topology; + } + if (c.pt() < cut.cDCAXiDaugthersCutPtRangeLower.value) { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepLower.value) { + return kXiV0Topology; + } + } else if (c.pt() < cut.cDCAXiDaugthersCutPtRangeUpper.value) { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepMiddle.value) { + return kXiV0Topology; + } + } else { + if (c.cascDaughDCA() > cut.cDCAXiDaugthersCutPtDepUpper.value) { + return kXiV0Topology; + } + } + if (c.cascCosPA() < std::cos(cut.cCosPACascCutPtDepP0.value - cut.cCosPACascCutPtDepP1.value * c.pt())) { + return kXiV0Topology; + } + if (c.cascTransRadius() > cut.cMaxCascradiuscut.value || c.cascTransRadius() < cut.cMinCascradiuscut.value) { + return kXiV0Topology; + } + if (std::abs(c.mXi() - cut.cMassXiminus.value) > cut.cMasswindowCasccut.value) { + return kXiCascTopology; + } + return kXiMass; + } + + // K0S proper lifetime (cm/c), the expression of the original task + template + static double k0sProperLifetime(const CollisionType& collision, const V0Type& v0) + { + float dx = v0.decayVtxX() - collision.posX(); + float dy = v0.decayVtxY() - collision.posY(); + float dz = v0.decayVtxZ() - collision.posZ(); + float l = std::sqrt(dx * dx + dy * dy + dz * dz); + float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + return (l / (p + SmallNumber)) * o2::constants::physics::MassK0Short; + } + + // K0S selection of mode A; equivalent to v0CutEnhanced of the original task. + // The collision is the one of the V0 (its primary vertex defines the proper lifetime). + template + [[nodiscard]] int k0sSelectionStage(const CollisionType& collision, const V0Type& v0) const + { + const auto& cut = mK0sCuts; + if (std::abs(v0.eta()) > cut.cMaxV0Etacut.value || v0.pt() < mXiCuts.cMinPtcut.value) { + return kK0sInput; + } + if (v0.v0CosPA() < cut.cK0sMinCosPA.value || v0.daughDCA() > cut.cK0sMaxDaughDCA.value) { + return kK0sKinematics; + } + if (std::abs(v0.dcapostopv()) < cut.cK0sDauPosDCAtoPVMin.value || std::abs(v0.dcanegtopv()) < cut.cK0sDauNegDCAtoPVMin.value) { + return kK0sKinematics; + } + auto radius = v0.transRadius(); + if (radius < cut.cK0sRadiusMin.value || radius > cut.cK0sRadiusMax.value) { + return kK0sKinematics; + } + if (std::abs(v0.dcav0topv()) > MaxDCAV0ToPV) { + return kK0sKinematics; + } + if (k0sProperLifetime(collision, v0) > cut.cK0sProperLifetimeMax.value) { + return kK0sTopology; + } + if (v0.qtarm() < cut.cK0sArmenterosQtMin.value) { + return kK0sTopology; + } + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) > cut.cK0sMassWindow.value) { + return kK0sLifetime; + } + if (cut.cK0sCrossMassRejection.value) { + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda) < cut.cK0sCrossMassRejectionWindow.value || + std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda) < cut.cK0sCrossMassRejectionWindow.value) { + return kK0sLifetime; + } + } + if (std::abs(v0.daughterTPCNSigmaPosPi()) >= cut.cK0sDaughterPiTPCNSigmaMax.value || + std::abs(v0.daughterTPCNSigmaNegPi()) >= cut.cK0sDaughterPiTPCNSigmaMax.value) { + return kK0sMass; + } + if (v0.nCrossedRowsPos() <= cut.cK0sPosDaughterMinCrossedRows.value || v0.nCrossedRowsNeg() <= cut.cK0sNegDaughterMinCrossedRows.value) { + return kK0sMass; + } + if (v0.qtarm() < cut.cK0sArmenterosAlphaCoeff.value * std::fabs(v0.alpha())) { + return kK0sDaughters; + } + return kK0sArmenteros; + } + + // Xi-K0S opening angle and the pT-dependent opening-angle window of the original task (kinCuts) + template + [[nodiscard]] bool passesKinematicCut(const FirstVecT& firstDaughter, const SecondVecT& secondDaughter, const MotherVecT& mother, float& alpha) const + { + auto firstP = std::sqrt(firstDaughter.Px() * firstDaughter.Px() + firstDaughter.Py() * firstDaughter.Py() + firstDaughter.Pz() * firstDaughter.Pz()); + auto secondP = std::sqrt(secondDaughter.Px() * secondDaughter.Px() + secondDaughter.Py() * secondDaughter.Py() + secondDaughter.Pz() * secondDaughter.Pz()); + if (firstP < SmallNumber || secondP < SmallNumber) { + alpha = 0.f; + return false; + } + + auto cosAlpha = (firstDaughter.Px() * secondDaughter.Px() + firstDaughter.Py() * secondDaughter.Py() + firstDaughter.Pz() * secondDaughter.Pz()) / (firstP * secondP); + if (cosAlpha > 1.) { + cosAlpha = 1.; + } else if (cosAlpha < -1.) { + cosAlpha = -1.; + } + alpha = std::acos(cosAlpha); + + const auto& kinCutsPt = mK0sCuts.cKinCutsPt.value; + const auto& kinLowerCutsAlpha = mK0sCuts.cKinLowerCutsAlpha.value; + const auto& kinUpperCutsAlpha = mK0sCuts.cKinUpperCutsAlpha.value; + + int kinCutsSize = static_cast(kinUpperCutsAlpha.size()); + if (kinCutsSize > static_cast(kinLowerCutsAlpha.size())) { + kinCutsSize = static_cast(kinLowerCutsAlpha.size()); + } + if (kinCutsSize > static_cast(kinCutsPt.size()) - 1) { + kinCutsSize = static_cast(kinCutsPt.size()) - 1; + } + + for (int i = 0; i < kinCutsSize; ++i) { + if ((mother.Pt() > kinCutsPt[i] && mother.Pt() <= kinCutsPt[i + 1]) && (alpha < kinLowerCutsAlpha[i] || alpha > kinUpperCutsAlpha[i])) { + return false; + } + } + return true; + } + + // Full selection stage of a mode-B track (quality, TOF requirement, PID) + template + int trackSelectionStage(const TrackType& track) + { + return speciesStage(track, mSelection.trackQualityStage(track)); + } + + template + int pionSelectionStage(const TrackType& track) + { + return trackSelectionStage(track); + } + + template + int kaonSelectionStage(const TrackType& track) + { + return trackSelectionStage(track); + } + + // Mode A: (Xi, K0S) enumeration of one collision, or of one mixed pair (Xi from collision, K0S from v0Collision). + // The order of the original task is kept: K0S selection, Xi selection, then Xi (outer) x K0S (inner). + // Hooks (nullptr to skip): + // - onK0s(v0, properLifetime, selected): every V0, in table order, + // - onXi(xi, selected): every cascade, in table order, + // - onCandidate(xi, v0, XiK0sCandidateValues): every pair of selected objects without shared daughters + // (the daughter-ID check is skipped in mixed events); kinematic and rapidity flags are in the values, + // - onExport(collision, xi, v0, values, passBits): same-event candidates at the loose or selected stage. + template + void forEachXiK0sCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const V0CollisionType& v0Collision, + const CascadesType& cascades, const V0sType& v0s, bool computeValues, + XiHook onXi = nullptr, K0sHook onK0s = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + constexpr bool HasXiHook = !std::is_same_v; + constexpr bool HasK0sHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + constexpr bool FillCutFlow = !IsMix; + bool visitLoose = false; + bool checkValidity = false; + if constexpr (!IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + + // K0S selection cache + using V0Row = std::decay_t; + std::vector> k0sList; + k0sList.reserve(v0s.size()); + for (const auto& v0 : v0s) { + const int stage = k0sSelectionStage(v0Collision, v0); + const bool selected = stage == kK0sSelected; + if constexpr (HasK0sHook) { + onK0s(v0, k0sProperLifetime(v0Collision, v0), selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xiK0s/k0s"), i); + } + } + if (selected || visitLoose) { + const auto indices = v0.indices(); + k0sList.push_back({v0, {indices[0], indices[1]}, selected}); + } + } + + // Xi selection cache + using XiRow = std::decay_t; + std::vector> xiList; + xiList.reserve(cascades.size()); + for (const auto& xi : cascades) { + const int stage = xiSelectionStage(xi); + const bool selected = stage == kXiSelected; + if constexpr (HasXiHook) { + onXi(xi, selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xiK0s/xi"), i); + } + } + if (selected || visitLoose) { + const auto indices = xi.cascadeIndices(); + xiList.push_back({xi, {indices[0], indices[1], indices[2]}, selected}); + } + } + + const bool kinCutsOn = mK0sCuts.cKinCuts.value; + const float rapidityMax = mCandidateCuts.cfgRapidityCut.value; + for (const auto& cachedXi : xiList) { + const auto& xi = cachedXi.row; + for (const auto& cachedK0s : k0sList) { + const auto& v0 = cachedK0s.row; + const bool objectsSelected = cachedXi.selected && cachedK0s.selected; + bool truthMatched = false; + if constexpr (IsMC && !IsMix) { + truthMatched = classifyXiK0sTruth(xi, v0) == XiK0sTruth::Matched; + } + auto countCandidate = [&](int stage) { + if constexpr (FillCutFlow) { + if (objectsSelected) { + histos.fill(HIST("CutFlow/xiK0s/candidates"), stage, 0); + if (truthMatched) { + histos.fill(HIST("CutFlow/xiK0s/candidates"), stage, 1); + } + } + } + }; + countCandidate(0); + if constexpr (!IsMix) { + if (sharesAnyDaughterId(cachedXi.daughterIds, cachedK0s.daughterIds)) { + continue; + } + } + countCandidate(1); + + // 4-vectors (construction of the original task) + XiK0sCandidateValues values; + values.xi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); + values.k0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), o2::constants::physics::MassK0Short)); + values.omega = values.xi + values.k0s; + if (kinCutsOn || computeValues || checkValidity) { + float alpha = 0.f; + const bool kinCutFlag = passesKinematicCut(values.xi, values.k0s, values.omega, alpha); + values.alpha = alpha; + if (kinCutsOn) { + values.passesKinCut = kinCutFlag; + } + } + values.inRapidity = !(std::abs(values.omega.Rapidity()) >= rapidityMax); + if (values.passesKinCut) { + countCandidate(2); + if (values.inRapidity) { + countCandidate(3); + } + } + + if constexpr (!IsMix) { + if (checkValidity) { + exportXiK0s(histos, collision, xi, v0, values, cachedXi.selected, cachedK0s.selected, truthMatched, visitLoose, onExport); + } + } + if constexpr (HasCandidateHook) { + if (objectsSelected) { + onCandidate(xi, v0, values); + } + } + } + } + } + + // Mode B: (Xi, pion, kaon) enumeration of one collision, or of one mixed pair (Xi from collision, tracks from the other). + // trackIds: ResoTrackTracks for full ResoTracks (positional source track IDs), unused for ResoMicroTracks_001 (trackId()). + // Hooks (nullptr to skip): + // - onXi(xi, selected): every cascade, in table order, + // - onTrack(track, pionStage, kaonStage): every track, in table order (TrackStage values), + // - onCandidate(xi, pion, kaon, Xi1530KCandidateValues): every triplet of selected objects with five distinct + // source track IDs (no Xi-daughter check in mixed events) inside the Xi(1530) window; all charge patterns, + // - onExport(collision, xi, pion, kaon, values, passBits): same-event micro candidates at the loose or selected stage. + template + void forEachXi1530KCandidate(o2::framework::HistogramRegistry& histos, const CollisionType& collision, + const CascadesType& cascades, const TracksType& tracks, const TrackIdsType& trackIds, bool computeValues, + XiHook onXi = nullptr, TrackHook onTrack = nullptr, CandidateHook onCandidate = nullptr, ExportHook onExport = nullptr) + { + constexpr bool HasXiHook = !std::is_same_v; + constexpr bool HasTrackHook = !std::is_same_v; + constexpr bool HasCandidateHook = !std::is_same_v; + constexpr bool HasExportHook = !std::is_same_v; + constexpr bool FillCutFlow = !IsMix; + bool visitLoose = false; + bool checkValidity = false; + if constexpr (IsResoMicrotrack && !IsMix) { + visitLoose = mLooseOptions.audit || (!mLooseOptions.exportSelected && HasExportHook); + checkValidity = visitLoose || (mLooseOptions.exportSelected && HasExportHook); + } + + // Xi selection cache + using XiRow = std::decay_t; + std::vector> xiList; + xiList.reserve(cascades.size()); + for (const auto& xi : cascades) { + const int stage = xiSelectionStage(xi); + const bool selected = stage == kXiSelected; + if constexpr (HasXiHook) { + onXi(xi, selected); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= stage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/xi"), i); + } + } + if (selected || visitLoose) { + const auto indices = xi.cascadeIndices(); + xiList.push_back({xi, {indices[0], indices[1], indices[2]}, selected}); + } + } + + // Track selection cache: every track is selected once per collision, for both species + const std::size_t nTracks = tracks.size(); + if (nTracks == 0) { + return; + } + const int64_t firstIndex = tracks.begin().index(); + std::vector trackCache(nTracks); + for (const auto& track : tracks) { + auto& entry = trackCache[getCacheIndex(track, firstIndex, nTracks)]; + const int qualityStage = mSelection.trackQualityStage(track); + const int pionStage = speciesStage(track, qualityStage); + const int kaonStage = speciesStage(track, qualityStage); + entry.sourceId = sourceTrackId(track, trackIds); + entry.quality = qualityStage == TrackStage::kTrkClusters; + entry.pionSelected = pionStage == TrackStage::kTrkPID; + entry.kaonSelected = kaonStage == TrackStage::kTrkPID; + if constexpr (HasTrackHook) { + onTrack(track, pionStage, kaonStage); + } + if constexpr (FillCutFlow) { + for (int i = 0; i <= pionStage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/tracks"), i, static_cast(Species::Pion)); + } + for (int i = 0; i <= kaonStage; ++i) { + histos.fill(HIST("CutFlow/xi1530K/tracks"), i, static_cast(Species::Kaon)); + } + } + } + + const float rapidityMax = mCandidateCuts.cfgRapidityCut.value; + const bool useWindow = mXi1530KCuts.cXi1530UseMassWindow.value; + const float windowCenter = mXi1530KCuts.cXi1530Mass.value; + const float windowWidth = mXi1530KCuts.cXi1530MassWindow.value; + for (const auto& cachedXi : xiList) { + const auto& xi = cachedXi.row; + const int xiSign = xi.sign() < 0 ? -1 : 1; + const ROOT::Math::PxPyPzEVector pXi(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); + for (const auto& pion : tracks) { + const auto& pionEntry = trackCache[getCacheIndex(pion, firstIndex, nTracks)]; + if (!pionEntry.pionSelected && !visitLoose) { + continue; + } + if constexpr (!IsMix) { + if (sharesDaughterId(cachedXi.daughterIds, pionEntry.sourceId)) { + continue; + } + } + const bool pairSelected = cachedXi.selected && pionEntry.pionSelected; + const bool pionSignal = pion.sign() != xiSign; + const ROOT::Math::PxPyPzEVector pPion(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), o2::constants::physics::MassPionCharged)); + const ROOT::Math::PxPyPzEVector pXi1530 = pXi + pPion; + const float massXiPi = pXi1530.M(); + const bool inWindow = !useWindow || std::abs(massXiPi - windowCenter) < windowWidth; + if constexpr (FillCutFlow) { + if (pairSelected) { + countXi1530K(histos, 0, pionSignal, false); + if (inWindow) { + countXi1530K(histos, 1, pionSignal, false); + } + } + } + if (!(pairSelected && inWindow) && !visitLoose) { + continue; + } + for (const auto& kaon : tracks) { + if (kaon.index() == pion.index()) { + continue; + } + const auto& kaonEntry = trackCache[getCacheIndex(kaon, firstIndex, nTracks)]; + if (!kaonEntry.kaonSelected && !visitLoose) { + continue; + } + if (kaonEntry.sourceId == pionEntry.sourceId) { + continue; + } + if constexpr (!IsMix) { + if (sharesDaughterId(cachedXi.daughterIds, kaonEntry.sourceId)) { + continue; + } + } + const bool tripletSelected = pairSelected && inWindow && kaonEntry.kaonSelected; + Xi1530KCandidateValues values; + values.xi = pXi; + values.pion = pPion; + values.kaon = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(kaon.pt(), kaon.eta(), kaon.phi(), o2::constants::physics::MassKaonCharged)); + values.xi1530 = pXi1530; + values.omega = pXi1530 + values.kaon; + values.massXiPi = massXiPi; + values.chargePattern = o2::analysis::omega2012ml::chargePattern(xiSign, pion.sign(), kaon.sign()); + values.inXi1530Window = inWindow; + values.inRapidity = !(std::abs(values.omega.Rapidity()) >= rapidityMax); + if (computeValues || checkValidity) { + values.massXiK = (pXi + values.kaon).M(); + values.massPiK = (pPion + values.kaon).M(); + values.openingAngleXi1530K = ROOT::Math::VectorUtil::Angle(pXi1530, values.kaon); + values.cosThetaStar = static_cast(o2::analysis::omega2012ml::detail::cosThetaStar( + o2::analysis::omega2012ml::detail::LorentzVector(pXi1530), o2::analysis::omega2012ml::detail::LorentzVector(values.omega))); + } + bool truthMatched = false; + if constexpr (IsMC && !IsMix) { + truthMatched = classifyXi1530KTruth(xi, pion, kaon) == Xi1530KTruth::Matched; + } + if constexpr (FillCutFlow) { + if (tripletSelected) { + const bool signal = values.chargePattern == o2::analysis::omega2012ml::kSignalPattern; + countXi1530K(histos, 2, signal, truthMatched); + if (values.inRapidity) { + countXi1530K(histos, 3, signal, truthMatched); + } + } + } + if constexpr (IsResoMicrotrack && !IsMix) { + if (checkValidity) { + exportXi1530K(histos, collision, xi, pion, kaon, values, cachedXi.selected, + pionEntry.quality && kaonEntry.quality, pionEntry.pionSelected && kaonEntry.kaonSelected, + truthMatched, visitLoose, onExport); + } + } + if constexpr (HasCandidateHook) { + if (tripletSelected) { + onCandidate(xi, pion, kaon, values); + } + } + } + } + } + } + + // Generated Omega(2012) parents of a selected reconstructed MC collision (ResoMCParents_001). The caller supplies + // the denominator rapidity limit so it can remain independent of a wider candidate-export window. The optional + // callback receives (parent, immediate channel) for the parents inside that rapidity window. Split reconstructed + // collisions repeat parent sets: this is not an unconditional generated denominator. + template + void forEachGeneratedOmega2012(o2::framework::HistogramRegistry& histos, const ParentsType& resoParents, float rapidityMax, Callback callback = nullptr) + { + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { + continue; + } + const GeneratedChannel channel = classifyGeneratedOmega2012(part.pdgCode(), part.daughterPDG1(), part.daughterPDG2()); + histos.fill(HIST("CutFlow/generated"), 0, static_cast(channel)); + if (!(std::abs(part.y()) < rapidityMax)) { + continue; + } + histos.fill(HIST("CutFlow/generated"), 1, static_cast(channel)); + if constexpr (!std::is_same_v) { + callback(part, channel); + } + } + } + + template + static bool sharesAnyDaughterId(const std::array& first, const std::array& second) + { + for (const auto& firstId : first) { + for (const auto& secondId : second) { + if (firstId == secondId) { + return true; + } + } + } + return false; + } + + template + static bool sharesDaughterId(const std::array& daughters, int64_t trackId) + { + for (const auto& daughterId : daughters) { + if (static_cast(daughterId) == trackId) { + return true; + } + } + return false; + } + + // Source track ID: trackId() of ResoMicroTracks_001, or the positional ResoTrackTracks row of a ResoTracks row. + template + static int64_t sourceTrackId(const TrackType& track, const TrackIdsType& trackIds) + { + if constexpr (requires { track.trackId(); }) { + return static_cast(track.trackId()); + } else { + const auto rowIndex = track.globalIndex(); + if (rowIndex >= 0 && rowIndex < static_cast(trackIds.size())) { + return static_cast(trackIds.rawIteratorAt(rowIndex).trackId()); + } + return static_cast(rowIndex); + } + } + + private: + template + struct CachedObject { + Row row; + std::array daughterIds{}; + bool selected = false; + }; + + struct TrackCacheEntry { + int64_t sourceId = -1; + bool quality = false; + bool pionSelected = false; + bool kaonSelected = false; + }; + + template + int speciesStage(const TrackType& track, int qualityStage) + { + if (qualityStage < TrackStage::kTrkClusters) { + return qualityStage; + } + constexpr int SpeciesIndex = static_cast(S); + if (!mSelection.passesTOFRequired(SpeciesIndex, track)) { + return TrackStage::kTrkClusters; + } + const bool hasTOF = track.hasTOF(); + const double tpcNSigma = (S == Species::Pion) ? track.tpcNSigmaPi() : track.tpcNSigmaKa(); + double tofNSigma = std::numeric_limits::quiet_NaN(); // TOF value is only valid with hasTOF + if (hasTOF) { + tofNSigma = (S == Species::Pion) ? track.tofNSigmaPi() : track.tofNSigmaKa(); + } + if (!mSelection.passesPID(SpeciesIndex, track.pt(), hasTOF, tpcNSigma, tofNSigma)) { + return TrackStage::kTrkTOFRequired; + } + return TrackStage::kTrkPID; + } + + template + static std::size_t getCacheIndex(const TrackType& track, int64_t firstIndex, std::size_t size) + { + const int64_t index = static_cast(track.index()) - firstIndex; + if (index < 0 || index >= static_cast(size)) { + LOG(fatal) << "Track index " << track.index() << " is outside the selection cache [" << firstIndex << ", " << firstIndex + static_cast(size) << ")"; + } + return static_cast(index); + } + + static void countXi1530K(o2::framework::HistogramRegistry& histos, int stage, bool signalPattern, bool truthMatched) + { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 0); + if (signalPattern) { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 1); + } + if (truthMatched) { + histos.fill(HIST("CutFlow/xi1530K/candidates"), stage, 2); + } + } + + // Loose / selected stage of a mode-A same-event candidate + template + void exportXiK0s(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const XiType& xi, const V0Type& v0, + XiK0sCandidateValues const& values, bool xiSelected, bool k0sSelected, bool truthMatched, + bool visitLoose, ExportHook onExport) + { + constexpr bool HasExportHook = !std::is_same_v; + namespace ml = o2::analysis::omega2012ml; + const auto canonical = ml::canonicalizeXiK0s(ml::makeCascadeSnapshot(collision, xi), ml::makeV0Snapshot(collision, v0)); + const bool valid = canonical.status == ml::BuildStatus::Ok && + std::isfinite(values.omega.M()) && std::isfinite(values.omega.Pt()) && std::isfinite(values.omega.Rapidity()) && + ml::buildXiK0sFeatures(canonical.candidate).status == ml::BuildStatus::Ok; + if (!valid) { + return; + } + auto countMl = [&](int stage) { + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xiK0s/looseCutflow"), stage, 0); + if (truthMatched) { + histos.fill(HIST("ML/xiK0s/looseCutflow"), stage, 1); + } + } + }; + countMl(0); + if (!values.inRapidity) { + return; + } + const bool selected = xiSelected && k0sSelected && values.passesKinCut; + if (visitLoose) { + countMl(1); + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xiK0s/looseMassPtActivity"), values.omega.M(), values.omega.Pt(), collision.cent()); + } + uint16_t passBits = kXiK0sPassLoose; + if (xiSelected) { + passBits |= kXiK0sPassXi; + countMl(2); + if (k0sSelected) { + passBits |= kXiK0sPassK0s; + countMl(3); + if (values.passesKinCut) { + passBits |= kXiK0sPassKinematic; + countMl(4); + } + } + } + if constexpr (HasExportHook) { + if (!mLooseOptions.exportSelected) { + onExport(collision, xi, v0, values, passBits); + } + } + } + if constexpr (HasExportHook) { + if (mLooseOptions.exportSelected && selected) { + onExport(collision, xi, v0, values, XiK0sPassBitsSelected); + } + } + } + + // Loose / selected stage of a mode-B same-event micro candidate + template + void exportXi1530K(o2::framework::HistogramRegistry& histos, const CollisionType& collision, const XiType& xi, + const TrackType& pion, const TrackType& kaon, Xi1530KCandidateValues const& values, + bool xiSelected, bool qualityBoth, bool pidBoth, bool truthMatched, bool visitLoose, ExportHook onExport) + { + constexpr bool HasExportHook = !std::is_same_v; + namespace ml = o2::analysis::omega2012ml; + const auto canonical = ml::canonicalizeXi1530K(ml::makeCascadeSnapshot(collision, xi), ml::makeTrackSnapshot(pion), ml::makeTrackSnapshot(kaon)); + const bool valid = canonical.status == ml::BuildStatus::Ok && + std::isfinite(values.omega.M()) && std::isfinite(values.omega.Pt()) && std::isfinite(values.omega.Rapidity()) && + ml::buildXi1530KFeatures(canonical.candidate).status == ml::BuildStatus::Ok; + if (!valid) { + return; + } + const int category = values.chargePattern == ml::kSignalPattern ? 0 : 1; + auto countMl = [&](int stage) { + if (mLooseOptions.audit) { + histos.fill(HIST("ML/xi1530K/looseCutflow"), stage, category); + if (truthMatched) { + histos.fill(HIST("ML/xi1530K/looseCutflow"), stage, 2); + } + } + }; + countMl(0); + if (!values.inRapidity) { + return; + } + const bool selected = xiSelected && qualityBoth && pidBoth && values.inXi1530Window; + if (visitLoose) { + countMl(1); + if (mLooseOptions.audit && category == 0) { + histos.fill(HIST("ML/xi1530K/looseMassPtActivity"), values.omega.M(), values.omega.Pt(), collision.cent()); + } + uint16_t passBits = kXi1530KPassLoose; + if (xiSelected) { + passBits |= kXi1530KPassXi; + countMl(2); + if (qualityBoth) { + passBits |= kXi1530KPassQuality; + countMl(3); + if (pidBoth) { + passBits |= kXi1530KPassPID; + countMl(4); + if (values.inXi1530Window) { + passBits |= kXi1530KPassWindow; + countMl(5); + } + } + } + } + if constexpr (HasExportHook) { + if (!mLooseOptions.exportSelected) { + onExport(collision, xi, pion, kaon, values, passBits); + } + } + } + if constexpr (HasExportHook) { + if (mLooseOptions.exportSelected && selected) { + onExport(collision, xi, pion, kaon, values, Xi1530KPassBitsSelected); + } + } + } + + // Cut-flow instrumentation of the selection, per decay mode; the output histograms belong to the tasks. + void registerHistograms(o2::framework::HistogramRegistry& histos, ProcessModes const& modes) + { + using o2::framework::HistType; + const std::array xiLabels{"input", "|#eta|, p_{T}", "DCA to PV", "#Lambda topology", "cascade topology", "#Xi mass"}; + if (modes.xiK0s) { + auto xiFlow = histos.add("CutFlow/xiK0s/xi", "XiK0s mode: cascades, once per selected collision;stage;cascades", HistType::kTH1D, {{kXiNStages, -0.5, static_cast(kXiNStages) - 0.5}}); + for (std::size_t i = 0; i < xiLabels.size(); ++i) { + xiFlow->GetXaxis()->SetBinLabel(i + 1, xiLabels[i]); + } + auto k0sFlow = histos.add("CutFlow/xiK0s/k0s", "XiK0s mode: V0s, once per selected collision;stage;V0s", HistType::kTH1D, {{kK0sNStages, -0.5, static_cast(kK0sNStages) - 0.5}}); + const std::array k0sLabels{"input", "|#eta|, p_{T}", "topology", "lifetime, min q_{T}", "mass, #Lambda rejection", "daughters", "Armenteros"}; + for (std::size_t i = 0; i < k0sLabels.size(); ++i) { + k0sFlow->GetXaxis()->SetBinLabel(i + 1, k0sLabels[i]); + } + auto candidateFlow = histos.add("CutFlow/xiK0s/candidates", "XiK0s mode: selected Xi x selected K0S, same event;stage;category", HistType::kTH2D, + {{NXiK0sCandidateStages, -0.5, NXiK0sCandidateStages - 0.5}, {2, -0.5, 1.5}}); + const std::array candidateLabels{"selected pairs", "distinct daughters", "kinematic cut", "rapidity"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "XiK0s truth"); + if (mLooseOptions.audit) { + auto flow = histos.add("ML/xiK0s/looseCutflow", "XiK0s mode: valid canonical candidates;stage;category", HistType::kTH2D, {{5, -0.5, 4.5}, {2, -0.5, 1.5}}); + const std::array labels{"valid canonical", "loose acceptance", "Xi selection", "K0S selection", "kinematic cut"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + flow->GetYaxis()->SetBinLabel(1, "all"); + flow->GetYaxis()->SetBinLabel(2, "XiK0s truth"); + histos.add("ML/xiK0s/looseMassPtActivity", "XiK0s mode loose candidates;mass (GeV/c^{2});pT (GeV/c);centrality", HistType::kTH3D, + {{300, 1.6, 2.8}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "centrality"}}); + } + } + if (modes.xi1530K) { + auto xiFlow = histos.add("CutFlow/xi1530K/xi", "Xi1530K mode: cascades, once per selected collision;stage;cascades", HistType::kTH1D, {{kXiNStages, -0.5, static_cast(kXiNStages) - 0.5}}); + for (std::size_t i = 0; i < xiLabels.size(); ++i) { + xiFlow->GetXaxis()->SetBinLabel(i + 1, xiLabels[i]); + } + constexpr int NTrackStages = TrackStage::kTrkNStages; + auto trackFlow = histos.add("CutFlow/xi1530K/tracks", "Xi1530K mode: tracks, once per selected collision;stage;species", HistType::kTH2D, + {{NTrackStages, -0.5, NTrackStages - 0.5}, {2, -0.5, 1.5}}); + const std::array trackLabels{"input", "pT", "eta", "DCAxy", "DCAz", "track flags", "clusters / crossed rows", "TOF required", "PID"}; + for (std::size_t i = 0; i < trackLabels.size(); ++i) { + trackFlow->GetXaxis()->SetBinLabel(i + 1, trackLabels[i]); + } + trackFlow->GetYaxis()->SetBinLabel(1, "pion"); + trackFlow->GetYaxis()->SetBinLabel(2, "kaon"); + auto candidateFlow = histos.add("CutFlow/xi1530K/candidates", "Xi1530K mode: same event;stage;category", HistType::kTH2D, + {{NXi1530KCandidateStages, -0.5, NXi1530KCandidateStages - 0.5}, {3, -0.5, 2.5}}); + const std::array candidateLabels{"selected Xi #pi pairs", "Xi(1530) window", "selected Xi #pi K triplets", "rapidity"}; + for (std::size_t i = 0; i < candidateLabels.size(); ++i) { + candidateFlow->GetXaxis()->SetBinLabel(i + 1, candidateLabels[i]); + } + candidateFlow->GetYaxis()->SetBinLabel(1, "all"); + candidateFlow->GetYaxis()->SetBinLabel(2, "signal charge pattern"); + candidateFlow->GetYaxis()->SetBinLabel(3, "Xi1530K truth"); + if (mLooseOptions.audit) { + auto flow = histos.add("ML/xi1530K/looseCutflow", "Xi1530K mode: valid canonical candidates;stage;category", HistType::kTH2D, {{6, -0.5, 5.5}, {3, -0.5, 2.5}}); + const std::array labels{"valid canonical", "loose acceptance", "Xi selection", "track quality", "TOF + PID", "Xi(1530) window"}; + for (std::size_t i = 0; i < labels.size(); ++i) { + flow->GetXaxis()->SetBinLabel(i + 1, labels[i]); + } + flow->GetYaxis()->SetBinLabel(1, "signal charge pattern"); + flow->GetYaxis()->SetBinLabel(2, "wrong-sign control"); + flow->GetYaxis()->SetBinLabel(3, "Xi1530K truth"); + histos.add("ML/xi1530K/looseMassPtActivity", "Xi1530K mode loose signal-pattern candidates;mass (GeV/c^{2});pT (GeV/c);centrality", HistType::kTH3D, + {{300, 1.8, 3.0}, {{0., 0.5, 1., 2., 3., 5., 8., 15., 30., 100.}, "pT"}, {{0., 10., 30., 50., 70., 100., 110.}, "centrality"}}); + } + } + if (modes.mcGen) { + auto generated = histos.add("CutFlow/generated", "Generated Omega(2012) parent rows of selected reconstructed events;stage;immediate channel", HistType::kTH2D, + {{2, -0.5, 1.5}, {NGeneratedChannels, -0.5, NGeneratedChannels - 0.5}}); + generated->GetXaxis()->SetBinLabel(1, "all parent rows"); + generated->GetXaxis()->SetBinLabel(2, "rapidity window"); + generated->GetYaxis()->SetBinLabel(1, "other / unresolved"); + generated->GetYaxis()->SetBinLabel(2, "Xi K0S"); + generated->GetYaxis()->SetBinLabel(3, "Xi(1530) K"); + } + } + + ResoAnalysisSelectionCore mSelection; + XiCuts mXiCuts; + K0sCuts mK0sCuts; + Xi1530KCuts mXi1530KCuts; + CandidateCuts mCandidateCuts; + LooseStageOptions mLooseOptions; +}; + +} // namespace o2::analysis::omega2012 + +#endif // PWGLF_CORE_OMEGA2012ANALYSISCORE_H_ diff --git a/PWGLF/Core/Omega2012MlFeatures.h b/PWGLF/Core/Omega2012MlFeatures.h new file mode 100644 index 00000000000..345a6c70c38 --- /dev/null +++ b/PWGLF/Core/Omega2012MlFeatures.h @@ -0,0 +1,1387 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file Omega2012MlFeatures.h +/// \brief Snapshots, canonical candidates and the two separate feature contracts of the Omega(2012) decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): Omega(2012)- -> Xi- K0S. Mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-. +/// The two modes have independent, separately versioned contracts (feature_contract_omega2012_v1.json); +/// no feature vector, status or projection is shared between them. + +#ifndef PWGLF_CORE_OMEGA2012MLFEATURES_H_ +#define PWGLF_CORE_OMEGA2012MLFEATURES_H_ + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include + +#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::omega2012ml +{ +inline constexpr std::size_t NItsLayers = 7; +inline constexpr std::size_t NCascadeDaughters = 3; +inline constexpr std::size_t NV0Daughters = 2; +inline constexpr std::size_t NPidSpecies = 3; +inline constexpr std::size_t XiK0sNMasterFeatures = 63; +inline constexpr std::size_t Xi1530KNMasterFeatures = 126; +inline constexpr int8_t SaturatedLow = std::numeric_limits::lowest(); // x10 nSigma code of -inf / missing TOF +inline constexpr int8_t SaturatedHigh = std::numeric_limits::max(); // x10 nSigma code of NaN / +inf +inline constexpr float NSigmaScale = 10.f; +inline constexpr float MicroPidOverflow = 3.5f; + +enum class Profile : uint8_t { DetectorV1, + RelationalV1, + StudyV1 }; +enum class BuildStatus : uint8_t { + Ok, + InvalidChargePattern, + ReusedDaughter, + InvalidMomentum, + InvalidKinematics, + InvalidContract +}; + +/// Charge pattern of a mode-B candidate relative to the Xi charge (metadata, never a feature). +enum ChargePattern : uint8_t { + kSignalPattern = 0, // pion opposite to the Xi charge, kaon equal to it (Xi- pi+ K- and conjugate) + kWrongSignPion = 1, // pion with the Xi charge + kWrongSignKaon = 2, // kaon opposite to the Xi charge + kWrongSignBoth = 3 // both wrong +}; + +/// All ResoCascades scalars of one cascade plus derived decay lengths. +/// Daughter arrays: index 0 positive, 1 negative, 2 bachelor; nSigma: [daughter * 3 + species], species pi, ka, pr. +struct CascadeSnapshot { + int64_t sourceRow = -1; // row of the cascade in the input ResoCascades table + int8_t sign = 0; + float px = 0.f, py = 0.f, pz = 0.f; + float mXi = 0.f, mLambda = 0.f; + float v0CosPA = 0.f, cascCosPA = 0.f, v0DaughDCA = 0.f, cascDaughDCA = 0.f; + float dcaPosToPV = 0.f, dcaNegToPV = 0.f, dcaBachToPV = 0.f, dcaV0ToPV = 0.f, dcaXYToPV = 0.f, dcaZToPV = 0.f; + float v0Radius = 0.f, cascRadius = 0.f; + float decayVtxX = 0.f, decayVtxY = 0.f, decayVtxZ = 0.f; + std::array tpcNSigma10{}; + std::array tofNSigma10{}; + std::array crossedRows{}; + std::array daughterIds{}; + float decayLength = 0.f; // 3D distance from the primary vertex to the decay vertex + float properLength = 0.f; // decayLength * MassXiMinus / p +}; + +/// All ResoV0s scalars of one V0 plus derived decay lengths. Daughter arrays: index 0 positive, 1 negative. +struct V0Snapshot { + int64_t sourceRow = -1; // row of the V0 in the input ResoV0s table + float px = 0.f, py = 0.f, pz = 0.f; + float mK0Short = 0.f, mLambda = 0.f, mAntiLambda = 0.f; + float cosPA = 0.f, daughDCA = 0.f, dcaPosToPV = 0.f, dcaNegToPV = 0.f, dcaV0ToPV = 0.f, radius = 0.f; + float decayVtxX = 0.f, decayVtxY = 0.f, decayVtxZ = 0.f; + float alpha = 0.f, qtarm = 0.f; + std::array tpcNSigma10{}; + std::array tofNSigma10{}; + std::array crossedRows{}; + std::array daughterIds{}; + float decayLength = 0.f; // 3D distance from the primary vertex to the decay vertex + float properLength = 0.f; // decayLength * MassK0Short / p (proper lifetime c*tau) +}; + +/// ResoMicroTracks_001 row: raw packed bytes and the decoded public accessors. +/// The encoding rules are those of the K1 micro001 contract; the type is separate so the K1 contract stays immutable. +struct TrackSnapshot { + int64_t sourceTrackId = -1; + int8_t charge = 0; + float px = 0.f, py = 0.f, pz = 0.f; + uint8_t pidNSigmaPiFlag = 0, pidNSigmaKaFlag = 0, pidNSigmaPrFlag = 0; + uint8_t trackSelectionFlags = 0, trackFlags = 0, tpcNClsCrossedRows = 0, itsClusterMap = 0; + std::array tpcNSigma{}; // pi, ka, pr + std::array tofNSigma{}; // pi, ka, pr + float dcaXY = std::numeric_limits::quiet_NaN(); + float dcaZ = std::numeric_limits::quiet_NaN(); + bool passedPtDependentDCAxy = false, passedPtDependentDCAz = false; + bool hasTOF = false, isPVContributor = false; + std::array itsHit{}; +}; + +struct XiK0sCandidateSnapshot { + CascadeSnapshot xi{}; + V0Snapshot k0s{}; +}; + +struct Xi1530KCandidateSnapshot { + CascadeSnapshot xi{}; + TrackSnapshot pion{}; + TrackSnapshot kaon{}; + uint8_t chargePattern = kSignalPattern; // metadata +}; + +template +struct CanonicalizationResult { + Snapshot candidate{}; + BuildStatus status = BuildStatus::InvalidChargePattern; + explicit operator bool() const { return status == BuildStatus::Ok; } +}; + +/// Candidate kinematics from the stored 3-momenta and PDG masses (audit only, never features) +struct KinematicAudit { + float mass = 0.f; + float pt = 0.f; + float eta = 0.f; + float phi = 0.f; + float scalarSumPt = 0.f; +}; + +template +struct FeaturePack { + std::array master{}; + BuildStatus status = BuildStatus::InvalidContract; + KinematicAudit kinematics{}; +}; +using XiK0sFeaturePack = FeaturePack; +using Xi1530KFeaturePack = FeaturePack; + +namespace detail +{ +template +float decayLength(Collision const& collision, Row const& row) +{ + const float dx = row.decayVtxX() - collision.posX(); + const float dy = row.decayVtxY() - collision.posY(); + const float dz = row.decayVtxZ() - collision.posZ(); + return std::sqrt(dx * dx + dy * dy + dz * dz); +} + +inline float properLength(float length, float px, float py, float pz, double mass) +{ + const float p = std::sqrt(px * px + py * py + pz * pz); + if (!(p > 0.f)) { + return std::numeric_limits::quiet_NaN(); + } + return static_cast(length * mass / p); +} +} // namespace detail + +template +CascadeSnapshot makeCascadeSnapshot(Collision const& collision, Cascade const& row) +{ + CascadeSnapshot out; + out.sourceRow = static_cast(row.globalIndex()); + out.sign = static_cast(row.sign()); + out.px = row.px(); + out.py = row.py(); + out.pz = row.pz(); + out.mXi = row.mXi(); + out.mLambda = row.mLambda(); + out.v0CosPA = row.v0CosPA(); + out.cascCosPA = row.cascCosPA(); + out.v0DaughDCA = row.daughDCA(); + out.cascDaughDCA = row.cascDaughDCA(); + out.dcaPosToPV = row.dcapostopv(); + out.dcaNegToPV = row.dcanegtopv(); + out.dcaBachToPV = row.dcabachtopv(); + out.dcaV0ToPV = row.dcav0topv(); + out.dcaXYToPV = row.dcaXYCascToPV(); + out.dcaZToPV = row.dcaZCascToPV(); + out.v0Radius = row.transRadius(); + out.cascRadius = row.cascTransRadius(); + out.decayVtxX = row.decayVtxX(); + out.decayVtxY = row.decayVtxY(); + out.decayVtxZ = row.decayVtxZ(); + out.tpcNSigma10 = {row.daughterTPCNSigmaPosPi10(), row.daughterTPCNSigmaPosKa10(), row.daughterTPCNSigmaPosPr10(), + row.daughterTPCNSigmaNegPi10(), row.daughterTPCNSigmaNegKa10(), row.daughterTPCNSigmaNegPr10(), + row.daughterTPCNSigmaBachPi10(), row.daughterTPCNSigmaBachKa10(), row.daughterTPCNSigmaBachPr10()}; + out.tofNSigma10 = {row.daughterTOFNSigmaPosPi10(), row.daughterTOFNSigmaPosKa10(), row.daughterTOFNSigmaPosPr10(), + row.daughterTOFNSigmaNegPi10(), row.daughterTOFNSigmaNegKa10(), row.daughterTOFNSigmaNegPr10(), + row.daughterTOFNSigmaBachPi10(), row.daughterTOFNSigmaBachKa10(), row.daughterTOFNSigmaBachPr10()}; + out.crossedRows = {row.nCrossedRowsPos(), row.nCrossedRowsNeg(), row.nCrossedRowsBach()}; + const auto indices = row.cascadeIndices(); + out.daughterIds = {indices[0], indices[1], indices[2]}; + out.decayLength = detail::decayLength(collision, row); + out.properLength = detail::properLength(out.decayLength, out.px, out.py, out.pz, o2::constants::physics::MassXiMinus); + return out; +} + +template +V0Snapshot makeV0Snapshot(Collision const& collision, V0 const& row) +{ + V0Snapshot out; + out.sourceRow = static_cast(row.globalIndex()); + out.px = row.px(); + out.py = row.py(); + out.pz = row.pz(); + out.mK0Short = row.mK0Short(); + out.mLambda = row.mLambda(); + out.mAntiLambda = row.mAntiLambda(); + out.cosPA = row.v0CosPA(); + out.daughDCA = row.daughDCA(); + out.dcaPosToPV = row.dcapostopv(); + out.dcaNegToPV = row.dcanegtopv(); + out.dcaV0ToPV = row.dcav0topv(); + out.radius = row.transRadius(); + out.decayVtxX = row.decayVtxX(); + out.decayVtxY = row.decayVtxY(); + out.decayVtxZ = row.decayVtxZ(); + out.alpha = row.alpha(); + out.qtarm = row.qtarm(); + out.tpcNSigma10 = {row.daughterTPCNSigmaPosPi10(), row.daughterTPCNSigmaPosKa10(), row.daughterTPCNSigmaPosPr10(), + row.daughterTPCNSigmaNegPi10(), row.daughterTPCNSigmaNegKa10(), row.daughterTPCNSigmaNegPr10()}; + out.tofNSigma10 = {row.daughterTOFNSigmaPosPi10(), row.daughterTOFNSigmaPosKa10(), row.daughterTOFNSigmaPosPr10(), + row.daughterTOFNSigmaNegPi10(), row.daughterTOFNSigmaNegKa10(), row.daughterTOFNSigmaNegPr10()}; + out.crossedRows = {row.nCrossedRowsPos(), row.nCrossedRowsNeg()}; + const auto indices = row.indices(); + out.daughterIds = {indices[0], indices[1]}; + out.decayLength = detail::decayLength(collision, row); + out.properLength = detail::properLength(out.decayLength, out.px, out.py, out.pz, o2::constants::physics::MassK0Short); + return out; +} + +template +TrackSnapshot makeTrackSnapshot(MicroTrack const& row) +{ + TrackSnapshot out; + out.sourceTrackId = static_cast(row.trackId()); + out.charge = static_cast(row.sign()); + out.px = static_cast(row.px()); + out.py = static_cast(row.py()); + out.pz = static_cast(row.pz()); + out.pidNSigmaPiFlag = static_cast(row.pidNSigmaPiFlag()); + out.pidNSigmaKaFlag = static_cast(row.pidNSigmaKaFlag()); + out.pidNSigmaPrFlag = static_cast(row.pidNSigmaPrFlag()); + out.trackSelectionFlags = static_cast(row.trackSelectionFlags()); + out.trackFlags = static_cast(row.trackFlags()); + out.tpcNClsCrossedRows = static_cast(row.tpcNClsCrossedRows()); + out.itsClusterMap = static_cast(row.itsClusterMap()); + out.tpcNSigma = {static_cast(row.tpcNSigmaPi()), static_cast(row.tpcNSigmaKa()), static_cast(row.tpcNSigmaPr())}; + out.tofNSigma = {static_cast(row.tofNSigmaPi()), static_cast(row.tofNSigmaKa()), static_cast(row.tofNSigmaPr())}; + out.dcaXY = static_cast(row.dcaXY()); + out.dcaZ = static_cast(row.dcaZ()); + out.passedPtDependentDCAxy = row.passedPtDependentDCAxy(); + out.passedPtDependentDCAz = row.passedPtDependentDCAz(); + out.hasTOF = row.hasTOF(); + out.isPVContributor = row.isPVContributor(); + for (std::size_t layer = 0; layer < NItsLayers; ++layer) { + out.itsHit[layer] = row.hasITSHitInLayer(static_cast(layer)); + } + return out; +} + +inline bool hasFiniteMomentum(float px, float py, float pz) +{ + return std::isfinite(px) && std::isfinite(py) && std::isfinite(pz) && std::hypot(px, py) > 0.f; +} + +inline bool sharesDaughter(CascadeSnapshot const& xi, int64_t trackId) +{ + return std::any_of(xi.daughterIds.begin(), xi.daughterIds.end(), [trackId](int id) { return static_cast(id) == trackId; }); +} + +/// Mode A canonical candidate [Xi, K0s]; the roles are fixed by the object types. +inline CanonicalizationResult canonicalizeXiK0s(CascadeSnapshot const& xi, V0Snapshot const& k0s) +{ + CanonicalizationResult result; + for (const auto& id : k0s.daughterIds) { + if (sharesDaughter(xi, id)) { + result.status = BuildStatus::ReusedDaughter; + return result; + } + } + if (xi.sign != 1 && xi.sign != -1) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(k0s.px, k0s.py, k0s.pz)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + result.candidate.xi = xi; + result.candidate.k0s = k0s; + result.status = BuildStatus::Ok; + return result; +} + +/// Charge pattern of (Xi, pion, kaon); 0 is the Omega(2012) signal pattern. +inline uint8_t chargePattern(int xiSign, int pionSign, int kaonSign) +{ + uint8_t pattern = kSignalPattern; + if (pionSign == xiSign) { + pattern |= kWrongSignPion; + } + if (kaonSign != xiSign) { + pattern |= kWrongSignKaon; + } + return pattern; +} + +/// Mode B canonical candidate [Xi, pion, kaon]; the roles are fixed by the selected species, the charge pattern is metadata. +inline CanonicalizationResult canonicalizeXi1530K(CascadeSnapshot const& xi, TrackSnapshot const& pion, TrackSnapshot const& kaon) +{ + CanonicalizationResult result; + if (pion.sourceTrackId == kaon.sourceTrackId || sharesDaughter(xi, pion.sourceTrackId) || sharesDaughter(xi, kaon.sourceTrackId)) { + result.status = BuildStatus::ReusedDaughter; + return result; + } + if ((xi.sign != 1 && xi.sign != -1) || (pion.charge != 1 && pion.charge != -1) || (kaon.charge != 1 && kaon.charge != -1)) { + result.status = BuildStatus::InvalidChargePattern; + return result; + } + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(pion.px, pion.py, pion.pz) || !hasFiniteMomentum(kaon.px, kaon.py, kaon.pz)) { + result.status = BuildStatus::InvalidMomentum; + return result; + } + result.candidate.xi = xi; + result.candidate.pion = pion; + result.candidate.kaon = kaon; + result.candidate.chargePattern = chargePattern(xi.sign, pion.charge, kaon.charge); + result.status = BuildStatus::Ok; + return result; +} + +namespace detail +{ +struct EncodedValue { + float value; + float valid; + float overflow; +}; + +// int8 x10 nSigma of ResoCascades/ResoV0s: saturated codes carry no usable value +inline EncodedValue encodeNSigma10(int8_t code) +{ + if (code == SaturatedLow || code == SaturatedHigh) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + return {.value = static_cast(code) / NSigmaScale, .valid = 1.f, .overflow = 0.f}; +} + +// micro001 nSigma, as the K1 contract +inline EncodedValue encodePID(float decoded) +{ + if (std::isnan(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + if (std::isinf(decoded)) { + return {.value = std::signbit(decoded) ? -MicroPidOverflow : MicroPidOverflow, .valid = 1.f, .overflow = 1.f}; + } + return {.value = decoded, .valid = 1.f, .overflow = 0.f}; +} + +// micro001 DCA, as the K1 contract +inline EncodedValue encodeDCA(float decoded) +{ + if (!std::isfinite(decoded)) { + return {.value = 0.f, .valid = 0.f, .overflow = 0.f}; + } + const bool overflow = decoded == o2::aod::resomicrodaughter001::DCAEncoding::MaxDCA; + return {.value = decoded, .valid = 1.f, .overflow = overflow ? 1.f : 0.f}; +} + +template +class Writer +{ + public: + explicit Writer(std::array& output) : out(output) {} + std::size_t index = 0; + // A count beyond N is detected by finishPack(); nothing is written past the end. + void add(float value) + { + if (index < N) { + out[index] = value; + } + ++index; + } + void add(double value) { add(static_cast(value)); } + void add(bool value) { add(value ? 1.f : 0.f); } + void addValid(EncodedValue value) + { + add(value.value); + add(value.valid); + } + void addFull(EncodedValue value) + { + add(value.value); + add(value.valid); + add(value.overflow); + } + + private: + std::array& out; +}; + +using LorentzVector = ROOT::Math::PxPyPzMVector; + +inline LorentzVector fourMomentum(float px, float py, float pz, double mass) +{ + return {px, py, pz, mass}; +} + +template +void appendCascade(Writer& w, CascadeSnapshot const& xi) +{ + w.add(xi.v0CosPA); + w.add(xi.cascCosPA); + w.add(xi.v0DaughDCA); + w.add(xi.cascDaughDCA); + w.add(std::abs(xi.dcaPosToPV)); + w.add(std::abs(xi.dcaNegToPV)); + w.add(std::abs(xi.dcaBachToPV)); + w.add(std::abs(xi.dcaV0ToPV)); + w.add(std::abs(xi.dcaXYToPV)); + w.add(std::abs(xi.dcaZToPV)); + w.add(xi.v0Radius); + w.add(xi.cascRadius); + w.add(static_cast(xi.mLambda) - o2::constants::physics::MassLambda0); + w.add(static_cast(xi.mXi) - o2::constants::physics::MassXiMinus); + w.add(xi.decayLength); + w.add(xi.properLength); + // Roles: the (anti)proton is the positive daughter of a Xi-, the negative daughter of a Xi+ + const bool isXiMinus = xi.sign < 0; + constexpr std::size_t Pi = 0, Pr = 2, Pos = 0, Neg = 1, Bach = 2; + const std::size_t baryon = isXiMinus ? Pos : Neg; + const std::size_t meson = isXiMinus ? Neg : Pos; + w.addValid(encodeNSigma10(xi.tpcNSigma10[baryon * NPidSpecies + Pr])); + w.addValid(encodeNSigma10(xi.tpcNSigma10[meson * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tpcNSigma10[Bach * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tofNSigma10[baryon * NPidSpecies + Pr])); + w.addValid(encodeNSigma10(xi.tofNSigma10[meson * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(xi.tofNSigma10[Bach * NPidSpecies + Pi])); + w.add(static_cast(xi.crossedRows[baryon])); + w.add(static_cast(xi.crossedRows[meson])); + w.add(static_cast(xi.crossedRows[Bach])); +} + +template +void appendV0(Writer& w, V0Snapshot const& k0s) +{ + w.add(k0s.cosPA); + w.add(k0s.daughDCA); + w.add(std::abs(k0s.dcaPosToPV)); + w.add(std::abs(k0s.dcaNegToPV)); + w.add(std::abs(k0s.dcaV0ToPV)); + w.add(k0s.radius); + w.add(static_cast(k0s.mK0Short) - o2::constants::physics::MassK0Short); + w.add(static_cast(k0s.mLambda) - o2::constants::physics::MassLambda0); + w.add(static_cast(k0s.mAntiLambda) - o2::constants::physics::MassLambda0); + w.add(k0s.alpha); + w.add(k0s.qtarm); + w.add(k0s.decayLength); + w.add(k0s.properLength); + constexpr std::size_t Pi = 0, Pos = 0, Neg = 1; + w.addValid(encodeNSigma10(k0s.tpcNSigma10[Pos * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tpcNSigma10[Neg * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tofNSigma10[Pos * NPidSpecies + Pi])); + w.addValid(encodeNSigma10(k0s.tofNSigma10[Neg * NPidSpecies + Pi])); + w.add(static_cast(k0s.crossedRows[Pos])); + w.add(static_cast(k0s.crossedRows[Neg])); +} + +template +void appendTrack(Writer& w, TrackSnapshot const& track) +{ + for (const float& decoded : track.tpcNSigma) { + w.addFull(encodePID(decoded)); + } + for (const float& decoded : track.tofNSigma) { + w.addFull(track.hasTOF ? encodePID(decoded) : EncodedValue{.value = 0.f, .valid = 0.f, .overflow = 0.f}); + } + w.addFull(encodeDCA(track.dcaXY)); + w.addFull(encodeDCA(track.dcaZ)); + w.add(track.passedPtDependentDCAxy); + w.add(track.passedPtDependentDCAz); + w.add(track.hasTOF); + w.add(static_cast(track.tpcNClsCrossedRows)); + for (const bool& hit : track.itsHit) { + w.add(hit); + } + w.add(track.isPVContributor); +} + +// delta eta, sin/cos delta phi, delta R, z_pT of two objects +template +bool appendPair(Writer& w, LorentzVector const& a, LorentzVector const& b) +{ + const double deta = a.Eta() - b.Eta(); + const double dphi = std::remainder(a.Phi() - b.Phi(), o2::constants::math::TwoPI); + const double sumPt = a.Pt() + b.Pt(); + if (!std::isfinite(deta) || !std::isfinite(dphi) || !(sumPt > 0.)) { + return false; + } + w.add(deta); + w.add(std::sin(dphi)); + w.add(std::cos(dphi)); + w.add(std::hypot(deta, dphi)); + w.add(std::min(a.Pt(), b.Pt()) / sumPt); + return true; +} + +// Cosine of the daughter direction in the mother rest frame w.r.t. the mother direction +inline double cosThetaStar(LorentzVector const& daughter, LorentzVector const& mother) +{ + const double p = mother.P(); + if (!(p > 0.) || !(mother.E() > p)) { + return std::numeric_limits::quiet_NaN(); + } + const ROOT::Math::Boost boost{mother.BoostToCM()}; + const auto inRest = boost(daughter); + const double pStar = inRest.P(); + if (!(pStar > 0.)) { + return std::numeric_limits::quiet_NaN(); + } + return inRest.Vect().Dot(mother.Vect()) / (pStar * p); +} + +template +bool finishPack(FeaturePack& pack, std::size_t index, LorentzVector const& mother, double scalarSumPt) +{ + const bool allFinite = std::all_of(pack.master.begin(), pack.master.end(), [](float x) { return std::isfinite(x); }); + if (index != N || !allFinite) { + pack.status = BuildStatus::InvalidContract; + return false; + } + pack.kinematics.mass = static_cast(mother.M()); + pack.kinematics.pt = static_cast(mother.Pt()); + pack.kinematics.eta = static_cast(mother.Eta()); + pack.kinematics.phi = static_cast(mother.Phi()); + pack.kinematics.scalarSumPt = static_cast(scalarSumPt); + pack.status = BuildStatus::Ok; + return true; +} + +template +constexpr bool isStrictlyIncreasingBelow(std::array const& indices, std::size_t bound) +{ + for (std::size_t i = 0; i < N; ++i) { + if (indices[i] >= bound || (i > 0 && indices[i - 1] >= indices[i])) { + return false; + } + } + return true; +} +} // namespace detail + +// Names, projections and SHA-256 are generated from feature_contract_omega2012_v1.json (one section per mode). +inline constexpr std::array XiK0sMasterFeatureNames{ + "xi.v0_cos_pa", + "xi.casc_cos_pa", + "xi.v0_daughter_dca", + "xi.casc_daughter_dca", + "xi.abs_dca_pos_to_pv", + "xi.abs_dca_neg_to_pv", + "xi.abs_dca_bach_to_pv", + "xi.abs_dca_v0_to_pv", + "xi.abs_dca_xy_to_pv", + "xi.abs_dca_z_to_pv", + "xi.v0_radius", + "xi.casc_radius", + "xi.delta_mass_lambda", + "xi.delta_mass_xi", + "xi.decay_length", + "xi.proper_length", + "xi.baryon_tpc_nsigma_pr", + "xi.baryon_tpc_nsigma_pr_valid", + "xi.meson_tpc_nsigma_pi", + "xi.meson_tpc_nsigma_pi_valid", + "xi.bach_tpc_nsigma_pi", + "xi.bach_tpc_nsigma_pi_valid", + "xi.baryon_tof_nsigma_pr", + "xi.baryon_tof_nsigma_pr_valid", + "xi.meson_tof_nsigma_pi", + "xi.meson_tof_nsigma_pi_valid", + "xi.bach_tof_nsigma_pi", + "xi.bach_tof_nsigma_pi_valid", + "xi.baryon_crossed_rows", + "xi.meson_crossed_rows", + "xi.bach_crossed_rows", + "k0s.cos_pa", + "k0s.daughter_dca", + "k0s.abs_dca_pos_to_pv", + "k0s.abs_dca_neg_to_pv", + "k0s.abs_dca_v0_to_pv", + "k0s.radius", + "k0s.delta_mass_k0s", + "k0s.delta_mass_lambda", + "k0s.delta_mass_antilambda", + "k0s.arm_alpha", + "k0s.arm_qt", + "k0s.decay_length", + "k0s.proper_length", + "k0s.pos_tpc_nsigma_pi", + "k0s.pos_tpc_nsigma_pi_valid", + "k0s.neg_tpc_nsigma_pi", + "k0s.neg_tpc_nsigma_pi_valid", + "k0s.pos_tof_nsigma_pi", + "k0s.pos_tof_nsigma_pi_valid", + "k0s.neg_tof_nsigma_pi", + "k0s.neg_tof_nsigma_pi_valid", + "k0s.pos_crossed_rows", + "k0s.neg_crossed_rows", + "xi.pt_fraction", + "k0s.pt_fraction", + "xi__k0s.delta_eta", + "xi__k0s.sin_delta_phi", + "xi__k0s.cos_delta_phi", + "xi__k0s.delta_r", + "xi__k0s.z_pt", + "xi__k0s.opening_angle", + "xi__k0s.cos_theta_star"}; +inline constexpr std::array XiK0sDetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53}; +inline constexpr std::array XiK0sRelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62}; +inline constexpr std::string_view XiK0sFeatureContractSha256 = "b767a5643da563246295f543ec7296c9e21b00fd921fef88acc7b19f3b24942c"; + +inline constexpr std::array Xi1530KMasterFeatureNames{ + "xi.v0_cos_pa", + "xi.casc_cos_pa", + "xi.v0_daughter_dca", + "xi.casc_daughter_dca", + "xi.abs_dca_pos_to_pv", + "xi.abs_dca_neg_to_pv", + "xi.abs_dca_bach_to_pv", + "xi.abs_dca_v0_to_pv", + "xi.abs_dca_xy_to_pv", + "xi.abs_dca_z_to_pv", + "xi.v0_radius", + "xi.casc_radius", + "xi.delta_mass_lambda", + "xi.delta_mass_xi", + "xi.decay_length", + "xi.proper_length", + "xi.baryon_tpc_nsigma_pr", + "xi.baryon_tpc_nsigma_pr_valid", + "xi.meson_tpc_nsigma_pi", + "xi.meson_tpc_nsigma_pi_valid", + "xi.bach_tpc_nsigma_pi", + "xi.bach_tpc_nsigma_pi_valid", + "xi.baryon_tof_nsigma_pr", + "xi.baryon_tof_nsigma_pr_valid", + "xi.meson_tof_nsigma_pi", + "xi.meson_tof_nsigma_pi_valid", + "xi.bach_tof_nsigma_pi", + "xi.bach_tof_nsigma_pi_valid", + "xi.baryon_crossed_rows", + "xi.meson_crossed_rows", + "xi.bach_crossed_rows", + "pion.tpc_nsigma_pi", + "pion.tpc_nsigma_pi_valid", + "pion.tpc_nsigma_pi_overflow", + "pion.tpc_nsigma_ka", + "pion.tpc_nsigma_ka_valid", + "pion.tpc_nsigma_ka_overflow", + "pion.tpc_nsigma_pr", + "pion.tpc_nsigma_pr_valid", + "pion.tpc_nsigma_pr_overflow", + "pion.tof_nsigma_pi", + "pion.tof_nsigma_pi_valid", + "pion.tof_nsigma_pi_overflow", + "pion.tof_nsigma_ka", + "pion.tof_nsigma_ka_valid", + "pion.tof_nsigma_ka_overflow", + "pion.tof_nsigma_pr", + "pion.tof_nsigma_pr_valid", + "pion.tof_nsigma_pr_overflow", + "pion.abs_dca_xy", + "pion.abs_dca_xy_valid", + "pion.abs_dca_xy_overflow", + "pion.abs_dca_z", + "pion.abs_dca_z_valid", + "pion.abs_dca_z_overflow", + "pion.passed_ptdep_dca_xy", + "pion.passed_ptdep_dca_z", + "pion.has_tof", + "pion.tpc_crossed_rows", + "pion.its_hit_l0", + "pion.its_hit_l1", + "pion.its_hit_l2", + "pion.its_hit_l3", + "pion.its_hit_l4", + "pion.its_hit_l5", + "pion.its_hit_l6", + "pion.is_pv_contributor", + "kaon.tpc_nsigma_pi", + "kaon.tpc_nsigma_pi_valid", + "kaon.tpc_nsigma_pi_overflow", + "kaon.tpc_nsigma_ka", + "kaon.tpc_nsigma_ka_valid", + "kaon.tpc_nsigma_ka_overflow", + "kaon.tpc_nsigma_pr", + "kaon.tpc_nsigma_pr_valid", + "kaon.tpc_nsigma_pr_overflow", + "kaon.tof_nsigma_pi", + "kaon.tof_nsigma_pi_valid", + "kaon.tof_nsigma_pi_overflow", + "kaon.tof_nsigma_ka", + "kaon.tof_nsigma_ka_valid", + "kaon.tof_nsigma_ka_overflow", + "kaon.tof_nsigma_pr", + "kaon.tof_nsigma_pr_valid", + "kaon.tof_nsigma_pr_overflow", + "kaon.abs_dca_xy", + "kaon.abs_dca_xy_valid", + "kaon.abs_dca_xy_overflow", + "kaon.abs_dca_z", + "kaon.abs_dca_z_valid", + "kaon.abs_dca_z_overflow", + "kaon.passed_ptdep_dca_xy", + "kaon.passed_ptdep_dca_z", + "kaon.has_tof", + "kaon.tpc_crossed_rows", + "kaon.its_hit_l0", + "kaon.its_hit_l1", + "kaon.its_hit_l2", + "kaon.its_hit_l3", + "kaon.its_hit_l4", + "kaon.its_hit_l5", + "kaon.its_hit_l6", + "kaon.is_pv_contributor", + "xi.pt_fraction", + "pion.pt_fraction", + "kaon.pt_fraction", + "xi__pion.delta_eta", + "xi__pion.sin_delta_phi", + "xi__pion.cos_delta_phi", + "xi__pion.delta_r", + "xi__pion.z_pt", + "xi__kaon.delta_eta", + "xi__kaon.sin_delta_phi", + "xi__kaon.cos_delta_phi", + "xi__kaon.delta_r", + "xi__kaon.z_pt", + "pion__kaon.delta_eta", + "pion__kaon.sin_delta_phi", + "pion__kaon.cos_delta_phi", + "pion__kaon.delta_r", + "pion__kaon.z_pt", + "xi1530__kaon.opening_angle", + "xi1530__kaon.cos_theta_star", + "mass_xi_pi", + "mass_xi_k", + "mass_pi_k"}; +inline constexpr std::array Xi1530KDetectorV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102}; +inline constexpr std::array Xi1530KRelationalV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123}; +inline constexpr std::array Xi1530KStudyV1Projection{ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125}; +inline constexpr std::string_view Xi1530KFeatureContractSha256 = "ca244fe167a599f9bc60d3909630edca5c797bb7b84d75d80944e6ad1a44aec5"; +static_assert(XiK0sMasterFeatureNames.size() == XiK0sNMasterFeatures, "XiK0s feature name count must match XiK0sNMasterFeatures"); +static_assert(Xi1530KMasterFeatureNames.size() == Xi1530KNMasterFeatures, "Xi1530K feature name count must match Xi1530KNMasterFeatures"); +static_assert(detail::isStrictlyIncreasingBelow(XiK0sDetectorV1Projection, XiK0sNMasterFeatures), "XiK0s DetectorV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(XiK0sRelationalV1Projection, XiK0sNMasterFeatures), "XiK0s RelationalV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KDetectorV1Projection, Xi1530KNMasterFeatures), "Xi1530K DetectorV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KRelationalV1Projection, Xi1530KNMasterFeatures), "Xi1530K RelationalV1 projection must be strictly increasing"); +static_assert(detail::isStrictlyIncreasingBelow(Xi1530KStudyV1Projection, Xi1530KNMasterFeatures), "Xi1530K StudyV1 projection must be strictly increasing"); + +/// Mode A master features (XiK0s contract) +inline XiK0sFeaturePack buildXiK0sFeatures(XiK0sCandidateSnapshot const& candidate) +{ + using o2::constants::physics::MassK0Short; + using o2::constants::physics::MassXiMinus; + XiK0sFeaturePack pack; + const auto& xi = candidate.xi; + const auto& k0s = candidate.k0s; + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(k0s.px, k0s.py, k0s.pz)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + const auto pXi = detail::fourMomentum(xi.px, xi.py, xi.pz, MassXiMinus); + const auto pK0s = detail::fourMomentum(k0s.px, k0s.py, k0s.pz, MassK0Short); + const auto mother = pXi + pK0s; + const double sumPt = pXi.Pt() + pK0s.Pt(); + if (!(sumPt > 0.)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + detail::Writer w{pack.master}; + detail::appendCascade(w, xi); + detail::appendV0(w, k0s); + w.add(pXi.Pt() / sumPt); + w.add(pK0s.Pt() / sumPt); + if (!detail::appendPair(w, pXi, pK0s)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + w.add(ROOT::Math::VectorUtil::Angle(pXi, pK0s)); + w.add(detail::cosThetaStar(pXi, mother)); + detail::finishPack(pack, w.index, mother, sumPt); + return pack; +} + +/// Mode B master features (Xi1530K contract) +inline Xi1530KFeaturePack buildXi1530KFeatures(Xi1530KCandidateSnapshot const& candidate) +{ + using o2::constants::physics::MassKaonCharged; + using o2::constants::physics::MassPionCharged; + using o2::constants::physics::MassXiMinus; + Xi1530KFeaturePack pack; + const auto& xi = candidate.xi; + const auto& pion = candidate.pion; + const auto& kaon = candidate.kaon; + if (!hasFiniteMomentum(xi.px, xi.py, xi.pz) || !hasFiniteMomentum(pion.px, pion.py, pion.pz) || !hasFiniteMomentum(kaon.px, kaon.py, kaon.pz)) { + pack.status = BuildStatus::InvalidMomentum; + return pack; + } + if (pion.sourceTrackId == kaon.sourceTrackId || sharesDaughter(xi, pion.sourceTrackId) || sharesDaughter(xi, kaon.sourceTrackId)) { + pack.status = BuildStatus::ReusedDaughter; + return pack; + } + const auto pXi = detail::fourMomentum(xi.px, xi.py, xi.pz, MassXiMinus); + const auto pPion = detail::fourMomentum(pion.px, pion.py, pion.pz, MassPionCharged); + const auto pKaon = detail::fourMomentum(kaon.px, kaon.py, kaon.pz, MassKaonCharged); + const auto xi1530 = pXi + pPion; + const auto mother = xi1530 + pKaon; + const double sumPt = pXi.Pt() + pPion.Pt() + pKaon.Pt(); + if (!(sumPt > 0.)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + detail::Writer w{pack.master}; + detail::appendCascade(w, xi); + detail::appendTrack(w, pion); + detail::appendTrack(w, kaon); + w.add(pXi.Pt() / sumPt); + w.add(pPion.Pt() / sumPt); + w.add(pKaon.Pt() / sumPt); + if (!detail::appendPair(w, pXi, pPion) || !detail::appendPair(w, pXi, pKaon) || !detail::appendPair(w, pPion, pKaon)) { + pack.status = BuildStatus::InvalidKinematics; + return pack; + } + w.add(ROOT::Math::VectorUtil::Angle(xi1530, pKaon)); + w.add(detail::cosThetaStar(xi1530, mother)); + w.add(xi1530.M()); + w.add((pXi + pKaon).M()); + w.add((pPion + pKaon).M()); + detail::finishPack(pack, w.index, mother, sumPt); + return pack; +} + +template +std::vector project(FeaturePack const& pack, std::array const& indices) +{ + std::vector projected; + projected.reserve(M); + for (const auto& i : indices) { + projected.push_back(pack.master[i]); + } + return projected; +} + +inline std::vector projectFeatures(XiK0sFeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + switch (profile) { + case Profile::DetectorV1: + return project(pack, XiK0sDetectorV1Projection); + case Profile::RelationalV1: + return project(pack, XiK0sRelationalV1Projection); + default: + return {}; // mode A has no study-only features + } +} + +inline std::vector projectFeatures(Xi1530KFeaturePack const& pack, Profile profile) +{ + if (pack.status != BuildStatus::Ok) { + return {}; + } + switch (profile) { + case Profile::DetectorV1: + return project(pack, Xi1530KDetectorV1Projection); + case Profile::RelationalV1: + return project(pack, Xi1530KRelationalV1Projection); + case Profile::StudyV1: + return project(pack, Xi1530KStudyV1Projection); + default: + return {}; + } +} +} // namespace o2::analysis::omega2012ml + +#endif // PWGLF_CORE_OMEGA2012MLFEATURES_H_ diff --git a/PWGLF/Core/ResoAnalysisSelectionCore.h b/PWGLF/Core/ResoAnalysisSelectionCore.h new file mode 100644 index 00000000000..225f65124b7 --- /dev/null +++ b/PWGLF/Core/ResoAnalysisSelectionCore.h @@ -0,0 +1,462 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ResoAnalysisSelectionCore.h +/// \brief Event, track-quality and PID selection of resonance daughters from the reduced v001 resonance tables +/// \author Bong-Hwi Lim +/// +/// The same selection code serves full ResoTracks (exact values) and ResoMicroTracks (quantised DCA and nSigma, +/// see LFResonanceTables.h); only the comparisons are quantisation aware. A cut set to DisabledCut is off. + +#ifndef PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ +#define PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::analysis::resonance +{ + +inline constexpr float DisabledCut = -999.f; // an optional cut with this value is off and not evaluated +inline constexpr double DCAGridStep = 0.025; // v001 micro DCA encoding, lower-inclusive bins up to DCAGridMax +inline constexpr double DCAGridMax = 0.15; +inline constexpr double PIDGridStart = 2.0; // v001 micro nSigma encoding: 0.25 bins in [2.0, 3.5] +inline constexpr double PIDGridStep = 0.25; +inline constexpr double PIDGridMax = 3.5; +inline constexpr double GridTolerance = 1e-4; +inline constexpr std::size_t MinPtBinEdges = 2; // a pT dependent PID table needs at least one bin +inline constexpr float ProducerDCAPtP0 = 0.004f; // resonanceModuleInitializer cfgTightDCAOffset default +inline constexpr float ProducerDCAPtCoeff = 0.013f; // resonanceModuleInitializer cfgTightDCAPtCoefficient default +inline constexpr float ProducerDCAPtPower = 1.f; // resonanceModuleInitializer cfgTightDCAPtPower default +inline constexpr float ConfigTolerance = 1e-6f; + +// Last stage passed by a track; a cut-flow histogram can be filled directly from this value. +enum TrackStage : int { + kTrkInput = 0, + kTrkPt, + kTrkEta, + kTrkDCAxy, + kTrkDCAz, + kTrkFlags, + kTrkClusters, + kTrkTOFRequired, + kTrkPID, + kTrkNStages +}; + +// Resolved PID cut of one species at a given pT. +struct PIDCut { + double tpcMax = 0.; + double tofMax = 0.; + double combined = 0.; + bool tofRequired = false; +}; + +// A cut is on unless it carries the disabled value (tolerant to the float parsing of the JSON value). +inline bool isCutEnabled(float value) +{ + return value > DisabledCut + 1.f; +} + +// v001 micro values are lower-inclusive bin edges: a maximum cut on the grid keeps bins below it. +inline bool passesBinnedMax(double decoded, double cut) +{ + return decoded < cut - o2::constants::math::Epsilon; +} + +// Minimum cut on the grid keeps the bin starting at the cut. +inline bool passesBinnedMin(double decoded, double cut) +{ + return decoded >= cut - o2::constants::math::Epsilon; +} + +template +bool passesMax(double value, double cut) +{ + if constexpr (IsResoMicrotrack) { + return passesBinnedMax(value, cut); + } else { + return value < cut; + } +} + +inline bool isInRange(double value, double minimum, double maximum) +{ + if (isCutEnabled(minimum) && value < minimum) { + return false; + } + if (isCutEnabled(maximum) && value > maximum) { + return false; + } + return true; +} + +inline bool isInWindow(double value, double center, double width) +{ + return std::abs(value - center) < width; +} + +// Preserve pT-bin membership [low, high). +inline int getPtBinIndex(float pt, const std::vector& ptBins) +{ + for (std::size_t i = 1; i < ptBins.size(); ++i) { + if (pt >= ptBins[i - 1] && pt < ptBins[i]) { + return static_cast(i - 1); + } + } + return -1; +} + +// Configurable groups without prefix: the JSON keys are the plain configurable names. + +/// Event selection +struct EventCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cRecoINELgt0{"cRecoINELgt0", false, "Apply reconstructed INEL>0 selection"}; + o2::framework::Configurable cMCINELgt0{"cMCINELgt0", false, "Require generator INEL>0 in MC processes"}; + o2::framework::Configurable cMCVtxIn10{"cMCVtxIn10", false, "Require generator |vz| < 10 cm in MC processes"}; +}; + +/// Track selections (common for all daughter species, -999 switches an optional cut off) +struct TrackCuts : o2::framework::ConfigurableGroup { + o2::framework::Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; + o2::framework::Configurable cMaxEtacut{"cMaxEtacut", -999.f, "Track maximum |eta| cut (-999: off)"}; + // DCAr to PV + o2::framework::Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1, "Track DCAr cut to PV Maximum"}; + // DCAz to PV + o2::framework::Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1, "Track DCAz cut to PV Maximum"}; + o2::framework::Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0, "Track DCAz cut to PV Minimum"}; + o2::framework::Configurable cfgUsePtDepDCA{"cfgUsePtDepDCA", false, "Use pT dependent DCA cut instead of the fixed maximum"}; + o2::framework::Configurable cDCAToPVByPtP0{"cDCAToPVByPtP0", 0.004f, "pT dependent DCA cut = P0 + coefficient / pT^power (cm)"}; + o2::framework::Configurable cDCAToPVByPtCoeff{"cDCAToPVByPtCoeff", 0.013f, "Coefficient in the pT dependent DCA cut"}; + o2::framework::Configurable cDCAToPVByPtPower{"cDCAToPVByPtPower", 1.f, "Power in the pT dependent DCA cut"}; + o2::framework::Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + o2::framework::Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + o2::framework::Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + o2::framework::Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + o2::framework::Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; + o2::framework::Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; + o2::framework::Configurable cfgTPCcluster{"cfgTPCcluster", 0, "Number of TPC cluster (found clusters, ResoTracks only)"}; + o2::framework::Configurable cfgTPCCrossedRowsMin{"cfgTPCCrossedRowsMin", 0, "Minimum number of TPC crossed rows"}; + o2::framework::Configurable cfgITSNClsMin{"cfgITSNClsMin", 0, "Minimum number of ITS clusters (ResoMicroTracks only)"}; + o2::framework::Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; +}; + +/// PID selection of one daughter species, filled by the task from its own (species-named) configurables. +/// The configurable names are used only in the configuration messages. +struct PIDCutConfig { + std::string species; // e.g. "Pion", used in messages + double maxTPCnSigma = DisabledCut; + double maxTOFnSigma = DisabledCut; + double combinedNSigma = DisabledCut; // combined TPC-TOF cut, on when > 0 + bool onlyTOFTracks = false; // require a TOF signal + bool usePtDependent = false; // use the pT binned cuts below instead of the fixed maxima + std::vector ptBins; // bin edges; the other vectors have one entry per bin + std::vector tpcNSigmaCuts; + std::vector tofNSigmaCuts; + std::vector tofRequired; + std::string maxTPCName; // configurable names for the messages + std::string maxTOFName; + std::string tpcCutsName; + std::string tofCutsName; +}; + +/// Event, track-quality, TOF-requirement and PID selection of resonance daughters. +/// The species index is the position of its PIDCutConfig in init(). +class ResoAnalysisSelectionCore +{ + public: + // byPassTOF skips the TOF nSigma cut and the pT binned TOF requirement; microTracks enables the grid checks. + void init(EventCuts const& eventCuts, TrackCuts const& trackCuts, std::vector pidCuts, bool byPassTOF, bool microTracks) + { + mEventCuts = eventCuts; + mTrackCuts = trackCuts; + mPID = std::move(pidCuts); + mByPassTOF = byPassTOF; + checkConfiguration(microTracks); + } + + template + bool passesEventCuts(const CollisionType& collision) + { + return !(mEventCuts.cRecoINELgt0 && !collision.isRecINELgt0()); + } + + template + bool passesMCEventCuts(const CollisionType& collision) + { + if (mEventCuts.cMCINELgt0 && !collision.isINELgt0()) { + return false; + } + if (mEventCuts.cMCVtxIn10 && !collision.isVtxIn10()) { + return false; + } + return true; + } + + // Resolve the PID cut of one species at a given pT; false if the pT is outside all pT-dependent bins. + bool getPIDCut(int species, float pt, PIDCut& cut) + { + const auto& config = mPID[species]; + cut.tpcMax = config.maxTPCnSigma; + cut.tofMax = config.maxTOFnSigma; + cut.combined = config.combinedNSigma; + cut.tofRequired = false; + if (config.usePtDependent) { + const int ptBin = getPtBinIndex(pt, config.ptBins); + if (ptBin < 0) { + return false; + } + const auto bin = static_cast(ptBin); + cut.tpcMax = config.tpcNSigmaCuts[bin]; + cut.tofMax = config.tofNSigmaCuts[bin]; + cut.tofRequired = config.tofRequired[bin] != 0; + } + return true; + } + + // Track quality selection shared by all species. Returns the last stage that was passed. + // Full tracks store exact values; micro tracks store quantised DCA (see LFResonanceTables.h). + template + int trackQualityStage(const TrackType& track) + { + const double pt = track.pt(); + const double dcaXY = track.dcaXY(); + const double dcaZ = track.dcaZ(); + // Invalid micro DCA codes decode to NaN + if (!std::isfinite(pt) || !std::isfinite(track.eta()) || !std::isfinite(dcaXY) || !std::isfinite(dcaZ)) { + return kTrkInput; + } + if (std::abs(pt) < mTrackCuts.cMinPtcut) { + return kTrkInput; + } + if (isCutEnabled(mTrackCuts.cMaxEtacut) && !(std::abs(track.eta()) < mTrackCuts.cMaxEtacut)) { + return kTrkPt; + } + + if (mTrackCuts.cfgUsePtDepDCA) { + if constexpr (IsResoMicrotrack) { + if (!track.passedPtDependentDCAxy()) { + return kTrkEta; + } + if (!track.passedPtDependentDCAz()) { + return kTrkDCAxy; + } + } else { + const double dcaPtCut = mTrackCuts.cDCAToPVByPtP0 + mTrackCuts.cDCAToPVByPtCoeff * std::pow(pt, -static_cast(mTrackCuts.cDCAToPVByPtPower)); + if (!(std::abs(dcaXY) < dcaPtCut)) { + return kTrkEta; + } + if (!(std::abs(dcaZ) < dcaPtCut)) { + return kTrkDCAxy; + } + } + } else { + if (isCutEnabled(mTrackCuts.cMaxDCArToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMax(dcaXY, mTrackCuts.cMaxDCArToPVcut)) { + return kTrkEta; + } + } else { + if (!(std::abs(dcaXY) <= mTrackCuts.cMaxDCArToPVcut)) { + return kTrkEta; + } + } + } + if (isCutEnabled(mTrackCuts.cMaxDCAzToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMax(dcaZ, mTrackCuts.cMaxDCAzToPVcut)) { + return kTrkDCAxy; + } + } else { + if (!(std::abs(dcaZ) <= mTrackCuts.cMaxDCAzToPVcut)) { + return kTrkDCAxy; + } + } + } + } + if (isCutEnabled(mTrackCuts.cMinDCAzToPVcut)) { + if constexpr (IsResoMicrotrack) { + if (!passesBinnedMin(dcaZ, mTrackCuts.cMinDCAzToPVcut)) { + return kTrkDCAxy; + } + } else { + if (!(std::abs(dcaZ) >= mTrackCuts.cMinDCAzToPVcut)) { + return kTrkDCAxy; + } + } + } + + // Track flags + if ((mTrackCuts.cfgPrimaryTrack && !track.isPrimaryTrack()) || + (mTrackCuts.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) || + (mTrackCuts.cfgGlobalTrack && !track.isGlobalTrack()) || + (mTrackCuts.cfgPVContributor && !track.isPVContributor()) || + (mTrackCuts.cfgUseITSRefit && !track.passedITSRefit()) || + (mTrackCuts.cfgUseTPCRefit && !track.passedTPCRefit())) { + return kTrkDCAz; + } + + // Clusters: found clusters exist only in ResoTracks, ITS clusters only in ResoMicroTracks + if constexpr (!IsResoMicrotrack) { + if constexpr (requires { track.tpcNClsFound(); }) { + if (track.tpcNClsFound() < mTrackCuts.cfgTPCcluster) { + return kTrkFlags; + } + } + } + if constexpr (requires { track.tpcNClsCrossedRows(); }) { + if (track.tpcNClsCrossedRows() < mTrackCuts.cfgTPCCrossedRowsMin) { + return kTrkFlags; + } + } + if constexpr (IsResoMicrotrack) { + if constexpr (requires { track.itsNCls(); }) { + if (track.itsNCls() < mTrackCuts.cfgITSNClsMin) { + return kTrkFlags; + } + } + } + return kTrkClusters; + } + + // TOF signal requirement of the track (global, per species, or per pT bin) + template + bool passesTOFRequired(int species, const TrackType& track) + { + bool required = mTrackCuts.cfgHasTOF || mPID[species].onlyTOFTracks; + PIDCut cut; + // A pT outside all bins is rejected by passesPID + if (!mByPassTOF && getPIDCut(species, track.pt(), cut) && cut.tofRequired) { + required = true; + } + return !required || track.hasTOF(); + } + + // PID selection from the nSigma values of the species; tofNSigma is used only with hasTOF. + template + bool passesPID(int species, float pt, bool hasTOF, double tpcNSigma, double tofNSigma) + { + PIDCut cut; + if (!getPIDCut(species, pt, cut)) { + return false; + } + if (isCutEnabled(cut.tpcMax) && !passesMax(std::abs(tpcNSigma), cut.tpcMax)) { + return false; + } + // Missing TOF is handled by passesTOFRequired; here the TPC alone decides + if (mByPassTOF || !hasTOF) { + return true; + } + bool tofPassed = !isCutEnabled(cut.tofMax) || passesMax(std::abs(tofNSigma), cut.tofMax); + if (!tofPassed && cut.combined > 0 && tpcNSigma * tpcNSigma + tofNSigma * tofNSigma < cut.combined * cut.combined) { + tofPassed = true; + } + return tofPassed; + } + + private: + void checkConfiguration(bool microTracks) + { + // Consistency of the pT dependent PID configuration + bool anyOnlyTOF = false; + for (const auto& config : mPID) { + if (config.usePtDependent) { + const auto& bins = config.ptBins; + if (bins.size() < MinPtBinEdges || config.tpcNSigmaCuts.size() != bins.size() - 1 || + config.tofNSigmaCuts.size() != bins.size() - 1 || config.tofRequired.size() != bins.size() - 1) { + LOG(fatal) << config.species << " pT dependent PID vectors must have (number of pT bin edges - 1) entries"; + } + } + anyOnlyTOF = anyOnlyTOF || config.onlyTOFTracks; + } + if (mByPassTOF && anyOnlyTOF) { + LOG(warning) << "cByPassTOF skips the TOF nSigma selection, but cUseOnlyTOFTrack* still requires a TOF signal"; + } + + // Micro tracks store quantised DCA and nSigma: a cut off the grid would silently act as a different cut. + if (!microTracks) { + return; + } + auto checkDCAGrid = [](const char* name, double cut) { + const double nearest = std::min(std::max(std::round(cut / DCAGridStep) * DCAGridStep, 0.), DCAGridMax); + if (std::abs(cut - nearest) > GridTolerance) { + LOG(fatal) << name << " = " << cut << " is not on the quantised DCA grid (multiples of " << DCAGridStep << " up to " << DCAGridMax << "); nearest value: " << nearest; + } + }; + auto checkPIDGrid = [](const std::string& name, double cut) { + const double nearest = std::min(std::max(PIDGridStart + std::round((cut - PIDGridStart) / PIDGridStep) * PIDGridStep, PIDGridStart), PIDGridMax); + if (std::abs(cut - nearest) > GridTolerance) { + LOG(fatal) << name << " = " << cut << " is not on the quantised nSigma grid ({2.0, 2.25, ..., 3.5}); nearest value: " << nearest; + } + }; + if (mTrackCuts.cfgUsePtDepDCA) { + LOG(info) << "Micro tracks use the producer pT dependent DCA flags (0.004 + 0.013 / pT); cDCAToPVByPt* are ignored"; + if (std::abs(mTrackCuts.cDCAToPVByPtP0 - ProducerDCAPtP0) > ConfigTolerance || std::abs(mTrackCuts.cDCAToPVByPtCoeff - ProducerDCAPtCoeff) > ConfigTolerance || std::abs(mTrackCuts.cDCAToPVByPtPower - ProducerDCAPtPower) > ConfigTolerance) { + LOG(warning) << "cDCAToPVByPt* differ from the producer defaults, but micro tracks always use the producer formula"; + } + } else { + if (isCutEnabled(mTrackCuts.cMaxDCArToPVcut)) { + checkDCAGrid("cMaxDCArToPVcut", mTrackCuts.cMaxDCArToPVcut); + } + if (isCutEnabled(mTrackCuts.cMaxDCAzToPVcut)) { + checkDCAGrid("cMaxDCAzToPVcut", mTrackCuts.cMaxDCAzToPVcut); + } + } + if (isCutEnabled(mTrackCuts.cMinDCAzToPVcut)) { + checkDCAGrid("cMinDCAzToPVcut", mTrackCuts.cMinDCAzToPVcut); + } + bool anyCombined = false; + for (const auto& config : mPID) { + if (isCutEnabled(config.maxTPCnSigma) && !config.usePtDependent) { + checkPIDGrid(config.maxTPCName, config.maxTPCnSigma); + } + if (isCutEnabled(config.maxTOFnSigma) && !config.usePtDependent) { + checkPIDGrid(config.maxTOFName, config.maxTOFnSigma); + } + anyCombined = anyCombined || config.combinedNSigma > 0; + } + for (const auto& config : mPID) { + if (!config.usePtDependent) { + continue; + } + for (const auto& cut : config.tpcNSigmaCuts) { + if (isCutEnabled(cut)) { + checkPIDGrid(config.tpcCutsName, cut); + } + } + for (const auto& cut : config.tofNSigmaCuts) { + if (isCutEnabled(cut)) { + checkPIDGrid(config.tofCutsName, cut); + } + } + } + if (anyCombined) { + LOG(warning) << "nsigmaCutCombined* on micro tracks uses quantised nSigma values (approximate)"; + } + } + + EventCuts mEventCuts; + TrackCuts mTrackCuts; + std::vector mPID; + bool mByPassTOF = false; +}; + +} // namespace o2::analysis::resonance + +#endif // PWGLF_CORE_RESOANALYSISSELECTIONCORE_H_ diff --git a/PWGLF/DataModel/LFAntinCexTables.h b/PWGLF/DataModel/LFAntinCexTables.h index 09dfd023b0b..c6810880d43 100644 --- a/PWGLF/DataModel/LFAntinCexTables.h +++ b/PWGLF/DataModel/LFAntinCexTables.h @@ -27,12 +27,18 @@ namespace o2::aod namespace antin_cex { // Metadata -DECLARE_SOA_COLUMN(IsCex, isCex, bool); // 1=CEX (from antin), 0=BG -DECLARE_SOA_COLUMN(MotherPdg, motherPdg, int32_t); // mother PDG -DECLARE_SOA_COLUMN(MotherNHitIB, motherNHitIB, int); // mother IB Hits -DECLARE_SOA_COLUMN(ColId, colId, int32_t); // mcCollisionId -DECLARE_SOA_COLUMN(PId, pId, int32_t); // proton MC id -DECLARE_SOA_COLUMN(AntipId, antipId, int32_t); // antiproton MC id +DECLARE_SOA_COLUMN(IsCex, isCex, bool); // 1=CEX (from antin), 0=BG +DECLARE_SOA_COLUMN(MotherPdg, motherPdg, int32_t); // mother PDG +DECLARE_SOA_COLUMN(MotherP, motherP, float); // mother P +DECLARE_SOA_COLUMN(MotherPt, motherPt, float); +DECLARE_SOA_COLUMN(MotherPz, motherPz, float); +DECLARE_SOA_COLUMN(MotherEta, motherEta, float); +DECLARE_SOA_COLUMN(MotherVz, motherVz, float); +DECLARE_SOA_COLUMN(HasPi0, hasPi0, bool); +DECLARE_SOA_COLUMN(AntipProcess, antipProcess, int32_t); +DECLARE_SOA_COLUMN(ColId, colId, int64_t); +DECLARE_SOA_COLUMN(PId, pId, int64_t); +DECLARE_SOA_COLUMN(AntipId, antipId, int64_t); // MC (pair) DECLARE_SOA_COLUMN(McPairP, mcPairP, float); @@ -43,12 +49,6 @@ DECLARE_SOA_COLUMN(McAngleDeg, mcAngleDeg, float); DECLARE_SOA_COLUMN(McVtxX, mcVtxX, float); DECLARE_SOA_COLUMN(McVtxY, mcVtxY, float); DECLARE_SOA_COLUMN(McVtxZ, mcVtxZ, float); -DECLARE_SOA_COLUMN(VtxNAll, vtxNAll, int16_t); -DECLARE_SOA_COLUMN(VtxNCh, vtxNCh, int16_t); -DECLARE_SOA_COLUMN(VtxNNeut, vtxNNeut, int16_t); -DECLARE_SOA_COLUMN(VtxNPi0, vtxNPi0, int16_t); -DECLARE_SOA_COLUMN(VtxNGamma, vtxNGamma, int16_t); -DECLARE_SOA_COLUMN(VtxNN, vtxNN, int16_t); // Tracks (pair, fitter) DECLARE_SOA_COLUMN(TrkPairP, trkPairP, float); @@ -109,9 +109,9 @@ DECLARE_SOA_COLUMN(SVDeltaRToLayer, svDeltaRToLayer, float); DECLARE_SOA_COLUMN(PTrkItsHitMap, pTrkItsHitMap, uint16_t); DECLARE_SOA_COLUMN(APTrkItsHitMap, apTrkItsHitMap, uint16_t); -DECLARE_SOA_COLUMN(PLayersOk, pLayersOk, int8_t); -DECLARE_SOA_COLUMN(APLayersOk, apLayersOk, int8_t); +DECLARE_SOA_COLUMN(PVtxX, pVtxX, float); +DECLARE_SOA_COLUMN(PVtxY, pVtxY, float); DECLARE_SOA_COLUMN(PVtxZ, pVtxZ, float); // Proton ITS PID @@ -123,30 +123,105 @@ DECLARE_SOA_COLUMN(PTrkTgl, pTrkTgl, float); DECLARE_SOA_COLUMN(AntipTrkItsNSigmaPr, antipTrkItsNSigmaPr, float); DECLARE_SOA_COLUMN(AntipTrkItsPidValid, antipTrkItsPidValid, int8_t); DECLARE_SOA_COLUMN(AntipTrkTgl, antipTrkTgl, float); + +// Proton track quality +DECLARE_SOA_COLUMN(PTrkTpcNClsCrossedRows, pTrkTpcNClsCrossedRows, int); +DECLARE_SOA_COLUMN(PTrkTpcCrossedRowsOverFindableCls, pTrkTpcCrossedRowsOverFindableCls, float); +DECLARE_SOA_COLUMN(PTrkTpcChi2NCl, pTrkTpcChi2NCl, float); +DECLARE_SOA_COLUMN(PTrkItsChi2NCl, pTrkItsChi2NCl, float); + +// Antiproton track quality +DECLARE_SOA_COLUMN(AntipTrkTpcNClsCrossedRows, antipTrkTpcNClsCrossedRows, int); +DECLARE_SOA_COLUMN(AntipTrkTpcCrossedRowsOverFindableCls, antipTrkTpcCrossedRowsOverFindableCls, float); +DECLARE_SOA_COLUMN(AntipTrkTpcChi2NCl, antipTrkTpcChi2NCl, float); +DECLARE_SOA_COLUMN(AntipTrkItsChi2NCl, antipTrkItsChi2NCl, float); } // namespace antin_cex // Table DECLARE_SOA_TABLE(AntinCexPairs, "AOD", "ANTINCEX", antin_cex::IsCex, - antin_cex::MotherPdg, antin_cex::MotherNHitIB, antin_cex::ColId, antin_cex::PId, antin_cex::AntipId, - antin_cex::McPairP, antin_cex::McPairPt, antin_cex::McPairPz, - antin_cex::McDplane, antin_cex::McAngleDeg, antin_cex::McVtxX, antin_cex::McVtxY, antin_cex::McVtxZ, - antin_cex::VtxNAll, antin_cex::VtxNCh, antin_cex::VtxNNeut, antin_cex::VtxNPi0, antin_cex::VtxNGamma, antin_cex::VtxNN, - antin_cex::TrkPairP, antin_cex::TrkPairPt, antin_cex::TrkPairPz, antin_cex::TrkAngleDeg, - antin_cex::TrkVtxfitDcaPair, antin_cex::TrkVtxfitR, antin_cex::TrkVtxfitDistToPv, - antin_cex::TrkVtxfitSecVtxX, antin_cex::TrkVtxfitSecVtxY, antin_cex::TrkVtxfitSecVtxZ, - antin_cex::VtxfitChi2, antin_cex::VtxfitStatus, antin_cex::NCand, - antin_cex::VtxfitDX, antin_cex::VtxfitDY, antin_cex::VtxfitDZ, antin_cex::VtxfitD3D, - antin_cex::PTrkP, antin_cex::PTrkPx, antin_cex::PTrkPy, antin_cex::PTrkPz, antin_cex::PTrkEta, antin_cex::PTrkTpcSignal, antin_cex::PTrkNClsIts, - antin_cex::AntipTrkP, antin_cex::AntipTrkPx, antin_cex::AntipTrkPy, antin_cex::AntipTrkPz, antin_cex::AntipTrkEta, antin_cex::AntipTrkTpcSignal, antin_cex::AntipTrkNClsIts, + antin_cex::MotherPdg, + antin_cex::MotherP, + antin_cex::MotherPt, + antin_cex::MotherPz, + antin_cex::MotherEta, + antin_cex::MotherVz, + antin_cex::HasPi0, + antin_cex::AntipProcess, + antin_cex::ColId, + antin_cex::PId, + antin_cex::AntipId, + antin_cex::McPairP, + antin_cex::McPairPt, + antin_cex::McPairPz, + antin_cex::McDplane, + antin_cex::McAngleDeg, + antin_cex::McVtxX, + antin_cex::McVtxY, + antin_cex::McVtxZ, + antin_cex::TrkPairP, + antin_cex::TrkPairPt, + antin_cex::TrkPairPz, + antin_cex::TrkAngleDeg, + antin_cex::TrkVtxfitDcaPair, + antin_cex::TrkVtxfitR, + antin_cex::TrkVtxfitDistToPv, + antin_cex::TrkVtxfitSecVtxX, + antin_cex::TrkVtxfitSecVtxY, + antin_cex::TrkVtxfitSecVtxZ, + antin_cex::VtxfitChi2, + antin_cex::VtxfitStatus, + antin_cex::NCand, + antin_cex::VtxfitDX, + antin_cex::VtxfitDY, + antin_cex::VtxfitDZ, + antin_cex::VtxfitD3D, + antin_cex::PTrkP, + antin_cex::PTrkPx, + antin_cex::PTrkPy, + antin_cex::PTrkPz, + antin_cex::PTrkEta, + antin_cex::PTrkTpcSignal, + antin_cex::PTrkNClsIts, + antin_cex::AntipTrkP, + antin_cex::AntipTrkPx, + antin_cex::AntipTrkPy, + antin_cex::AntipTrkPz, + antin_cex::AntipTrkEta, + antin_cex::AntipTrkTpcSignal, + antin_cex::AntipTrkNClsIts, antin_cex::SelMask, - antin_cex::PairPointingAngleDeg, antin_cex::PvsvThetaDeg, antin_cex::PvsvPhiDeg, antin_cex::PairPBalance, antin_cex::PairPtBalance, antin_cex::PairQ, - antin_cex::DPairP, antin_cex::DPairPt, antin_cex::DPairPz, antin_cex::DOpenAngle, - antin_cex::SVNearestLayerId, antin_cex::SVDeltaRToLayer, - antin_cex::PTrkItsHitMap, antin_cex::APTrkItsHitMap, antin_cex::PLayersOk, antin_cex::APLayersOk, + antin_cex::PairPointingAngleDeg, + antin_cex::PvsvThetaDeg, + antin_cex::PvsvPhiDeg, + antin_cex::PairPBalance, + antin_cex::PairPtBalance, + antin_cex::PairQ, + antin_cex::DPairP, + antin_cex::DPairPt, + antin_cex::DPairPz, + antin_cex::DOpenAngle, + antin_cex::SVNearestLayerId, + antin_cex::SVDeltaRToLayer, + antin_cex::PTrkItsHitMap, + antin_cex::APTrkItsHitMap, + antin_cex::PVtxX, + antin_cex::PVtxY, antin_cex::PVtxZ, - antin_cex::PTrkItsNSigmaPr, antin_cex::PTrkItsPidValid, antin_cex::PTrkTgl, - antin_cex::AntipTrkItsNSigmaPr, antin_cex::AntipTrkItsPidValid, antin_cex::AntipTrkTgl); + antin_cex::PTrkItsNSigmaPr, + antin_cex::PTrkItsPidValid, + antin_cex::PTrkTgl, + antin_cex::AntipTrkItsNSigmaPr, + antin_cex::AntipTrkItsPidValid, + antin_cex::AntipTrkTgl, + antin_cex::PTrkTpcNClsCrossedRows, + antin_cex::PTrkTpcCrossedRowsOverFindableCls, + antin_cex::PTrkTpcChi2NCl, + antin_cex::PTrkItsChi2NCl, + antin_cex::AntipTrkTpcNClsCrossedRows, + antin_cex::AntipTrkTpcCrossedRowsOverFindableCls, + antin_cex::AntipTrkTpcChi2NCl, + antin_cex::AntipTrkItsChi2NCl); } // namespace o2::aod diff --git a/PWGLF/DataModel/LFCKSSpinalignmentTables.h b/PWGLF/DataModel/LFCKSSpinalignmentTables.h index 7988f76da31..0d2c0605af6 100644 --- a/PWGLF/DataModel/LFCKSSpinalignmentTables.h +++ b/PWGLF/DataModel/LFCKSSpinalignmentTables.h @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file LFCKSSpinalignmentTables.h -/// \brief DataModel for Charged KStar spin alignment +/// \brief DataModel for reduced K0s and pion tables for charged KStar analysis /// /// \author Prottay Das @@ -20,7 +20,6 @@ #include #include -#include #include namespace o2::aod @@ -29,77 +28,89 @@ namespace kshortpionevent { DECLARE_SOA_COLUMN(Cent, cent, float); DECLARE_SOA_COLUMN(Posz, posz, float); -// DECLARE_SOA_COLUMN(CollIndex, collIndex, float); DECLARE_SOA_COLUMN(PsiFT0C, psiFT0C, float); -DECLARE_SOA_COLUMN(PsiFT0A, psiFT0A, float); -DECLARE_SOA_COLUMN(PsiTPC, psiTPC, float); +// DECLARE_SOA_COLUMN(PsiFT0A, psiFT0A, float); +// DECLARE_SOA_COLUMN(PsiTPC, psiTPC, float); } // namespace kshortpionevent + DECLARE_SOA_TABLE(KShortpionEvents, "AOD", "KSHORTPIONEVENT", o2::soa::Index<>, kshortpionevent::Cent, kshortpionevent::Posz, - // kshortpionevent::CollIndex, - kshortpionevent::PsiFT0C, - kshortpionevent::PsiFT0A, - kshortpionevent::PsiTPC) + kshortpionevent::PsiFT0C); +// kshortpionevent::PsiFT0A, +// kshortpionevent::PsiTPC); + using KShortpionEvent = KShortpionEvents::iterator; -namespace kshortpionpair +// ------------------------------------------------------------ +// K0s candidate table +// ------------------------------------------------------------ +namespace kshorttrack { DECLARE_SOA_INDEX_COLUMN(KShortpionEvent, kshortpionevent); -DECLARE_SOA_COLUMN(V0Cospa, v0Cospa, float); //! V0 Cospa -DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); //! V0 Radius -DECLARE_SOA_COLUMN(DcaPositive, dcaPositive, float); //! DCA Positive -DECLARE_SOA_COLUMN(DcaNegative, dcaNegative, float); //! DCA Negative -DECLARE_SOA_COLUMN(DcaBetweenDaughter, dcaBetweenDaughter, float); //! DCA between daughters -DECLARE_SOA_COLUMN(V0Lifetime, v0Lifetime, float); //! KShort lifetime -DECLARE_SOA_COLUMN(KShortPx, kShortPx, float); //! KShort Px -DECLARE_SOA_COLUMN(KShortPy, kShortPy, float); //! KShort Py -DECLARE_SOA_COLUMN(KShortPz, kShortPz, float); //! KShort Pz -DECLARE_SOA_COLUMN(KShortMass, kShortMass, float); //! KShort Mass -DECLARE_SOA_COLUMN(PionBachPx, pionBachPx, float); //! Bachelor Pion Px -DECLARE_SOA_COLUMN(PionBachPy, pionBachPy, float); //! Bachelor Pion Py -DECLARE_SOA_COLUMN(PionBachPz, pionBachPz, float); //! Bachelor Pion Pz -/*DECLARE_SOA_COLUMN(PionBachTPC, pionBachTPC, float); //! Bachelor Pion nsigmatpc -DECLARE_SOA_COLUMN(PionBachTOFHit, pionBachTOFHit, int); //! Bachelor Pion tof hit availability -DECLARE_SOA_COLUMN(PionBachTOF, pionBachTOF, float); //! Bachelor Pion nsigmatof*/ -DECLARE_SOA_COLUMN(PionBachIndex, pionBachIndex, int); //! Bachelor Pion index -DECLARE_SOA_COLUMN(PionIndex1, pionIndex1, int); //! Daughter Pion index1 -DECLARE_SOA_COLUMN(PionIndex2, pionIndex2, int); //! Daughter Pion index2 -DECLARE_SOA_COLUMN(PionPidMask, pionPidMask, uint8_t); //! bitmask for PID selections -} // namespace kshortpionpair + +DECLARE_SOA_COLUMN(V0Cospa, v0Cospa, float); +DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); +DECLARE_SOA_COLUMN(DcaPositive, dcaPositive, float); +DECLARE_SOA_COLUMN(DcaNegative, dcaNegative, float); +// DECLARE_SOA_COLUMN(DcaBetweenDaughter, dcaBetweenDaughter, float); +// DECLARE_SOA_COLUMN(V0Lifetime, v0Lifetime, float); + +DECLARE_SOA_COLUMN(KShortPx, kShortPx, float); +DECLARE_SOA_COLUMN(KShortPy, kShortPy, float); +DECLARE_SOA_COLUMN(KShortPz, kShortPz, float); +DECLARE_SOA_COLUMN(KShortMass, kShortMass, float); + +DECLARE_SOA_COLUMN(PionIndex1, pionIndex1, int64_t); +DECLARE_SOA_COLUMN(PionIndex2, pionIndex2, int64_t); +} // namespace kshorttrack + DECLARE_SOA_TABLE(KShortTracks, "AOD", "KSHORTTRACK", o2::soa::Index<>, - kshortpionpair::KShortpionEventId, - kshortpionpair::V0Cospa, - kshortpionpair::V0Radius, - kshortpionpair::DcaPositive, - kshortpionpair::DcaNegative, - kshortpionpair::DcaBetweenDaughter, - kshortpionpair::V0Lifetime, - // kshortpionpair::Armenteros, - kshortpionpair::KShortPx, - kshortpionpair::KShortPy, - kshortpionpair::KShortPz, - kshortpionpair::KShortMass, - kshortpionpair::PionIndex1, - kshortpionpair::PionIndex2); + kshorttrack::KShortpionEventId, + kshorttrack::V0Cospa, + kshorttrack::V0Radius, + kshorttrack::DcaPositive, + kshorttrack::DcaNegative, + // kshorttrack::DcaBetweenDaughter, + // kshorttrack::V0Lifetime, + kshorttrack::KShortPx, + kshorttrack::KShortPy, + kshorttrack::KShortPz, + kshorttrack::KShortMass, + kshorttrack::PionIndex1, + kshorttrack::PionIndex2); using KShortTrack = KShortTracks::iterator; +// ------------------------------------------------------------ +// Bachelor pion table +// ------------------------------------------------------------ +namespace piontrack +{ +DECLARE_SOA_INDEX_COLUMN(KShortpionEvent, kshortpionevent); + +DECLARE_SOA_COLUMN(PionBachPx, pionBachPx, float); +DECLARE_SOA_COLUMN(PionBachPy, pionBachPy, float); +DECLARE_SOA_COLUMN(PionBachPz, pionBachPz, float); +DECLARE_SOA_COLUMN(Charge, charge, int8_t); +DECLARE_SOA_COLUMN(PionBachIndex, pionBachIndex, int64_t); +DECLARE_SOA_COLUMN(PionPidMask, pionPidMask, uint8_t); +} // namespace piontrack + DECLARE_SOA_TABLE(PionTracks, "AOD", "PIONTRACK", o2::soa::Index<>, - kshortpionpair::KShortpionEventId, - kshortpionpair::PionBachPx, - kshortpionpair::PionBachPy, - kshortpionpair::PionBachPz, - // kshortpionpair::PionBachSign, - // kshortpionpair::PionBachTPC, - // kshortpionpair::PionBachTOFHit, - // kshortpionpair::PionBachTOF, - kshortpionpair::PionBachIndex, - kshortpionpair::PionPidMask); + piontrack::KShortpionEventId, + piontrack::PionBachPx, + piontrack::PionBachPy, + piontrack::PionBachPz, + piontrack::Charge, + piontrack::PionBachIndex, + piontrack::PionPidMask); using PionTrack = PionTracks::iterator; + } // namespace o2::aod + #endif // PWGLF_DATAMODEL_LFCKSSPINALIGNMENTTABLES_H_ diff --git a/PWGLF/DataModel/LFDoubleCascTables.h b/PWGLF/DataModel/LFDoubleCascTables.h deleted file mode 100644 index 03f77e8f34c..00000000000 --- a/PWGLF/DataModel/LFDoubleCascTables.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef PWGLF_DATAMODEL_LFDOUBLECASCTABLES_H_ -#define PWGLF_DATAMODEL_LFDOUBLECASCTABLES_H_ - -#include - -namespace o2::aod -{ - -namespace DoubleCascTables -{ -DECLARE_SOA_COLUMN(PtCasc1, ptCasc1, float); // signed pt of the cascade -DECLARE_SOA_COLUMN(EtaCasc1, etaCasc1, float); -DECLARE_SOA_COLUMN(PhiCasc1, phiCasc1, float); -DECLARE_SOA_COLUMN(CascDecLength1, cascDecLength1, float); -DECLARE_SOA_COLUMN(OmegaMassCasc1, omegaMassCasc1, float); -DECLARE_SOA_COLUMN(XiMassCasc1, xiMassCasc1, float); -DECLARE_SOA_COLUMN(CosPACasc1, cosPACasc1, float); -DECLARE_SOA_COLUMN(DcaBachPVCasc1, dcaBachPVCasc1, float); -DECLARE_SOA_COLUMN(DcaV0BachCasc1, dcaV0BachCasc1, float); -DECLARE_SOA_COLUMN(NSigmaKBach1, nSigmaKBach1, float); - -DECLARE_SOA_COLUMN(PtCasc2, ptCasc2, float); -DECLARE_SOA_COLUMN(EtaCasc2, etaCasc2, float); -DECLARE_SOA_COLUMN(PhiCasc2, phiCasc2, float); -DECLARE_SOA_COLUMN(CascDecLength2, cascDecLength2, float); -DECLARE_SOA_COLUMN(OmegaMassCasc2, omegaMassCasc2, float); -DECLARE_SOA_COLUMN(XiMassCasc2, xiMassCasc2, float); -DECLARE_SOA_COLUMN(CosPACasc2, cosPACasc2, float); -DECLARE_SOA_COLUMN(DcaBachPVCasc2, dcaBachPVCasc2, float); -DECLARE_SOA_COLUMN(DcaV0BachCasc2, dcaV0BachCasc2, float); -DECLARE_SOA_COLUMN(NSigmaKBach2, nSigmaKBach2, float); - -DECLARE_SOA_COLUMN(DoubleOmegaMass, doubleOmegaMass, float); -} // namespace DoubleCascTables - -DECLARE_SOA_TABLE(DoubleCascTable, "AOD", "DOUBLECASCTABLE", - DoubleCascTables::PtCasc1, - DoubleCascTables::EtaCasc1, - DoubleCascTables::PhiCasc1, - DoubleCascTables::CascDecLength1, - DoubleCascTables::OmegaMassCasc1, - DoubleCascTables::XiMassCasc1, - DoubleCascTables::CosPACasc1, - DoubleCascTables::DcaBachPVCasc1, - DoubleCascTables::DcaV0BachCasc1, - DoubleCascTables::NSigmaKBach1, - DoubleCascTables::PtCasc2, - DoubleCascTables::EtaCasc2, - DoubleCascTables::PhiCasc2, - DoubleCascTables::CascDecLength2, - DoubleCascTables::OmegaMassCasc2, - DoubleCascTables::XiMassCasc2, - DoubleCascTables::CosPACasc2, - DoubleCascTables::DcaBachPVCasc2, - DoubleCascTables::DcaV0BachCasc2, - DoubleCascTables::NSigmaKBach2, - DoubleCascTables::DoubleOmegaMass); - -} // namespace o2::aod - -#endif // PWGLF_DATAMODEL_LFDOUBLECASCTABLES_H_ diff --git a/PWGLF/DataModel/LFDoubleOmegaTables.h b/PWGLF/DataModel/LFDoubleOmegaTables.h new file mode 100644 index 00000000000..83cee30b84a --- /dev/null +++ b/PWGLF/DataModel/LFDoubleOmegaTables.h @@ -0,0 +1,224 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef PWGLF_DATAMODEL_LFDOUBLEOMEGATABLES_H_ +#define PWGLF_DATAMODEL_LFDOUBLEOMEGATABLES_H_ + +#include + +#include + +namespace o2::aod +{ + +namespace DoubleOmegaTables +{ +DECLARE_SOA_COLUMN(PtDoubleOmega, ptDoubleOmega, float); // Signed transverse momentum of the double-Omega candidate, signed by the charge of the direct kaon. +DECLARE_SOA_COLUMN(EtaDoubleOmega, etaDoubleOmega, float); +DECLARE_SOA_COLUMN(PhiDoubleOmega, phiDoubleOmega, float); +DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); +DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); +DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); +DECLARE_SOA_COLUMN(PrimaryVtxX, primaryVtxX, float); +DECLARE_SOA_COLUMN(PrimaryVtxY, primaryVtxY, float); +DECLARE_SOA_COLUMN(PrimaryVtxZ, primaryVtxZ, float); +DECLARE_SOA_COLUMN(CosPAOmega, cosPAOmega, float); +DECLARE_SOA_COLUMN(CosPADirectLambda, cosPADirectLambda, float); +DECLARE_SOA_COLUMN(CosPADoubleOmega, cosPADoubleOmega, float); +DECLARE_SOA_COLUMN(CosPAOmegaToPV, cosPAOmegaToPV, float); +DECLARE_SOA_COLUMN(CosPADirectLambdaToPV, cosPADirectLambdaToPV, float); +DECLARE_SOA_COLUMN(DCAxyOmegaToPV, dcaXYOmegaToPV, float); +DECLARE_SOA_COLUMN(DCAxyDirectLambdaToPV, dcaXYDirectLambdaToPV, float); +DECLARE_SOA_COLUMN(DCAxyDirectKaonToPV, dcaXYDirectKaonToPV, float); +// Transverse DCA to the primary vertex of the final-state tracks, ordered as +// the detector map: Omega kaon, Omega proton, Omega pion, direct proton, direct pion. +DECLARE_SOA_COLUMN(DCAxyOmegaKaonToPV, dcaXYOmegaKaonToPV, float); +DECLARE_SOA_COLUMN(DCAxyOmegaProtonToPV, dcaXYOmegaProtonToPV, float); +DECLARE_SOA_COLUMN(DCAxyOmegaPionToPV, dcaXYOmegaPionToPV, float); +DECLARE_SOA_COLUMN(DCAxyDirectProtonToPV, dcaXYDirectProtonToPV, float); +DECLARE_SOA_COLUMN(DCAxyDirectPionToPV, dcaXYDirectPionToPV, float); +DECLARE_SOA_COLUMN(TPCNSigmaDirectKaon, tpcNSigmaDirectKaon, float); // -999 when TPC is unavailable or the candidate is not reconstructed. +DECLARE_SOA_COLUMN(TOFNSigmaDirectKaon, tofNSigmaDirectKaon, float); // -999 when TOF is unavailable or the candidate is not reconstructed. +// Three bits per daughter (ITS, TPC, TOF), ordered as: Omega kaon, Omega +// proton, Omega pion, direct proton, direct pion, direct kaon (also for c.c.). +// Bits 18-31 are zero; generated-only rows have a zero bitmap. +DECLARE_SOA_COLUMN(DaughterDetectorMap, daughterDetectorMap, uint32_t); +DECLARE_SOA_COLUMN(MassDoubleOmega, massDoubleOmega, float); +DECLARE_SOA_COLUMN(MassOmega, massOmega, float); +DECLARE_SOA_COLUMN(MassXi, massXi, float); + +DECLARE_SOA_COLUMN(GenPt, genPt, float); // Signed transverse momentum of the generated double-Omega candidate, signed by the charge of the direct kaon. +DECLARE_SOA_COLUMN(GenEta, genEta, float); +DECLARE_SOA_COLUMN(GenPhi, genPhi, float); +DECLARE_SOA_COLUMN(GenDecayLength, genDecayLength, float); +DECLARE_SOA_COLUMN(PdgDoubleOmega, pdgDoubleOmega, int); +DECLARE_SOA_COLUMN(GenMotherId, genMotherId, int64_t); // MC particle index within the input dataframe. +DECLARE_SOA_COLUMN(IsReco, isReco, bool); + +DECLARE_SOA_COLUMN(MixingType, mixingType, uint8_t); // 1: Omega, 2: Lambda, 3: kaon from another collision; 4: Omega, Lambda and kaon from three different collisions. +} // namespace DoubleOmegaTables + +#define DOUBLE_OMEGA_RECO_COLUMNS \ + DoubleOmegaTables::PtDoubleOmega, \ + DoubleOmegaTables::EtaDoubleOmega, \ + DoubleOmegaTables::PhiDoubleOmega, \ + DoubleOmegaTables::DecayVtxX, \ + DoubleOmegaTables::DecayVtxY, \ + DoubleOmegaTables::DecayVtxZ, \ + DoubleOmegaTables::PrimaryVtxX, \ + DoubleOmegaTables::PrimaryVtxY, \ + DoubleOmegaTables::PrimaryVtxZ, \ + DoubleOmegaTables::CosPAOmega, \ + DoubleOmegaTables::CosPADirectLambda, \ + DoubleOmegaTables::CosPADoubleOmega, \ + DoubleOmegaTables::CosPAOmegaToPV, \ + DoubleOmegaTables::CosPADirectLambdaToPV, \ + DoubleOmegaTables::DCAxyOmegaToPV, \ + DoubleOmegaTables::DCAxyDirectLambdaToPV, \ + DoubleOmegaTables::DCAxyDirectKaonToPV, \ + DoubleOmegaTables::DCAxyOmegaKaonToPV, \ + DoubleOmegaTables::DCAxyOmegaProtonToPV, \ + DoubleOmegaTables::DCAxyOmegaPionToPV, \ + DoubleOmegaTables::DCAxyDirectProtonToPV, \ + DoubleOmegaTables::DCAxyDirectPionToPV, \ + DoubleOmegaTables::TPCNSigmaDirectKaon, \ + DoubleOmegaTables::TOFNSigmaDirectKaon, \ + DoubleOmegaTables::DaughterDetectorMap, \ + DoubleOmegaTables::MassDoubleOmega, \ + DoubleOmegaTables::MassOmega, \ + DoubleOmegaTables::MassXi + +DECLARE_SOA_TABLE(DoubleOmegaTable, "AOD", "DOUBLEOMEGATABLE", + DOUBLE_OMEGA_RECO_COLUMNS); + +DECLARE_SOA_TABLE(DoubleOmegaTableMC, "AOD", "DBLOMEGAMCTABLE", + DOUBLE_OMEGA_RECO_COLUMNS, + DoubleOmegaTables::GenPt, + DoubleOmegaTables::GenEta, + DoubleOmegaTables::GenPhi, + DoubleOmegaTables::GenDecayLength, + DoubleOmegaTables::PdgDoubleOmega, + DoubleOmegaTables::GenMotherId, + DoubleOmegaTables::IsReco); + +DECLARE_SOA_TABLE(DoubleOmegaMixedTable, "AOD", "DBLOMEGAMIXTABLE", + DOUBLE_OMEGA_RECO_COLUMNS, + DoubleOmegaTables::MixingType); + +#undef DOUBLE_OMEGA_RECO_COLUMNS + +// Derived data: Omega, Lambda and kaon candidates of the collisions with at least one Omega, one +// Lambda and one kaon candidate not sharing tracks with it. The tables only index each other, so +// they are self-contained and dataframes can be merged. +namespace domega +{ +DECLARE_SOA_COLUMN(MultFT0M, multFT0M, float); //! FT0M multiplicity of the collision +DECLARE_SOA_COLUMN(Bz, bz, float); //! Nominal magnetic field (kG) +} // namespace domega + +DECLARE_SOA_TABLE(DOmegaColls, "AOD", "DOMEGACOLL", //! Collisions with double-Omega building blocks + o2::soa::Index<>, + collision::PosX, collision::PosY, collision::PosZ, + domega::MultFT0M, + bc::RunNumber, + domega::Bz); + +namespace domega +{ +DECLARE_SOA_INDEX_COLUMN_FULL(DOmegaColl, dOmegaColl, int, DOmegaColls, ""); //! Collision index + +// Track parametrisation covariance, in the order expected by getTrackParCov. +DECLARE_SOA_COLUMN(CYY, cYY, float); +DECLARE_SOA_COLUMN(CZY, cZY, float); +DECLARE_SOA_COLUMN(CZZ, cZZ, float); +DECLARE_SOA_COLUMN(CSnpY, cSnpY, float); +DECLARE_SOA_COLUMN(CSnpZ, cSnpZ, float); +DECLARE_SOA_COLUMN(CSnpSnp, cSnpSnp, float); +DECLARE_SOA_COLUMN(CTglY, cTglY, float); +DECLARE_SOA_COLUMN(CTglZ, cTglZ, float); +DECLARE_SOA_COLUMN(CTglSnp, cTglSnp, float); +DECLARE_SOA_COLUMN(CTglTgl, cTglTgl, float); +DECLARE_SOA_COLUMN(C1PtY, c1PtY, float); +DECLARE_SOA_COLUMN(C1PtZ, c1PtZ, float); +DECLARE_SOA_COLUMN(C1PtSnp, c1PtSnp, float); +DECLARE_SOA_COLUMN(C1PtTgl, c1PtTgl, float); +DECLARE_SOA_COLUMN(C1Pt21Pt2, c1Pt21Pt2, float); + +DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, float); //! -999 when TPC is unavailable +DECLARE_SOA_COLUMN(TOFNSigmaKa, tofNSigmaKa, float); //! -999 when TOF is unavailable +DECLARE_SOA_COLUMN(DetectorMap, detectorMap, uint8_t); //! ITS, TPC, TOF bits +DECLARE_SOA_COLUMN(DcaXYToPV, dcaXYToPV, float); //! Transverse DCA to the primary vertex of its collision +DECLARE_SOA_COLUMN(IsKaon, isKaon, bool); //! Selected as a direct-kaon candidate +} // namespace domega + +#define DOMEGA_TRACK_PAR_COV_COLUMNS \ + track::X, track::Alpha, track::Y, track::Z, track::Snp, track::Tgl, track::Signed1Pt, \ + domega::CYY, domega::CZY, domega::CZZ, domega::CSnpY, domega::CSnpZ, domega::CSnpSnp, \ + domega::CTglY, domega::CTglZ, domega::CTglSnp, domega::CTglTgl, \ + domega::C1PtY, domega::C1PtZ, domega::C1PtSnp, domega::C1PtTgl, domega::C1Pt21Pt2 + +DECLARE_SOA_TABLE(DOmegaTracks, "AOD", "DOMEGATRACK", //! Direct-kaon candidates and Omega/Lambda daughters, at the innermost update + o2::soa::Index<>, + domega::DOmegaCollId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::TPCNSigmaKa, + domega::TOFNSigmaKa, + domega::DetectorMap, + domega::DcaXYToPV, + domega::IsKaon, + track::Sign); + +namespace domega +{ +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, DOmegaTracks, "_Pos"); //! Positive daughter +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrack, negTrack, int, DOmegaTracks, "_Neg"); //! Negative daughter +DECLARE_SOA_INDEX_COLUMN_FULL(Bachelor, bachelor, int, DOmegaTracks, "_Bach"); //! Bachelor kaon of the Omega + +DECLARE_SOA_COLUMN(DecayX, decayX, float); +DECLARE_SOA_COLUMN(DecayY, decayY, float); +DECLARE_SOA_COLUMN(DecayZ, decayZ, float); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(MassOmega, massOmega, float); +DECLARE_SOA_COLUMN(MassXi, massXi, float); +DECLARE_SOA_COLUMN(MassLambda, massLambda, float); +DECLARE_SOA_COLUMN(DcaDaughters, dcaDaughters, float); //! DCA between the daughters +DECLARE_SOA_COLUMN(Sign, sign, int8_t); //! Charge of the Omega (Omega table) or of the Omega the Lambda can be paired with (Lambda table) +} // namespace domega + +DECLARE_SOA_TABLE(DOmegaOmegas, "AOD", "DOMEGAOMEGA", //! Omega candidates; track parameters at the Omega decay vertex + o2::soa::Index<>, + domega::DOmegaCollId, + domega::PosTrackId, domega::NegTrackId, domega::BachelorId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::DecayX, domega::DecayY, domega::DecayZ, + domega::Px, domega::Py, domega::Pz, + domega::MassOmega, domega::MassXi, + domega::DcaDaughters, + domega::Sign); + +DECLARE_SOA_TABLE(DOmegaLambdas, "AOD", "DOMEGALAMBDA", //! Lambda candidates; neutral track parameters at the Lambda decay vertex + o2::soa::Index<>, + domega::DOmegaCollId, + domega::PosTrackId, domega::NegTrackId, + DOMEGA_TRACK_PAR_COV_COLUMNS, + domega::DecayX, domega::DecayY, domega::DecayZ, + domega::Px, domega::Py, domega::Pz, + domega::MassLambda, + domega::DcaDaughters, + domega::Sign); + +#undef DOMEGA_TRACK_PAR_COV_COLUMNS + +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFDOUBLEOMEGATABLES_H_ diff --git a/PWGLF/DataModel/LFEbyeTables.h b/PWGLF/DataModel/LFEbyeTables.h index 541bcbe5f50..e8549d6025f 100644 --- a/PWGLF/DataModel/LFEbyeTables.h +++ b/PWGLF/DataModel/LFEbyeTables.h @@ -160,6 +160,25 @@ DECLARE_SOA_TABLE(McMiniTrkTables, "AOD", "MCMINITRKTABLE", LFEbyeTable::GenEtaMask, LFEbyeTable::IsReco); using McMiniTrkTable = McMiniTrkTables::iterator; + +DECLARE_SOA_TABLE(MiniChTables, "AOD", "MINICHTABLE", + o2::soa::Index<>, + LFEbyeTable::MiniCollTableId, + LFEbyeTable::Pt, + LFEbyeTable::EtaMask, + LFEbyeTable::SelMask); +using MiniChTable = MiniChTables::iterator; + +DECLARE_SOA_TABLE(McMiniChTables, "AOD", "MCMINICHTABLE", + o2::soa::Index<>, + LFEbyeTable::MiniCollTableId, + LFEbyeTable::Pt, + LFEbyeTable::EtaMask, + LFEbyeTable::SelMask, + LFEbyeTable::GenPt, + LFEbyeTable::GenEtaMask, + LFEbyeTable::IsReco); +using McMiniChTable = McMiniChTables::iterator; } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFEBYETABLES_H_ diff --git a/PWGLF/DataModel/LFHResonanceCorrelationTables.h b/PWGLF/DataModel/LFHResonanceCorrelationTables.h new file mode 100644 index 00000000000..3caad772493 --- /dev/null +++ b/PWGLF/DataModel/LFHResonanceCorrelationTables.h @@ -0,0 +1,159 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \brief This task serves to do hadron-resonance correlation studies. +/// The yield will be calculated using the two-particle correlation method. +/// Trigger particle : Hadrons +/// Associated Particles : Phi, K*0 +/// +/// \author Hirak Kumar Koley (hirak.koley@cern.ch) + +#ifndef PWGLF_DATAMODEL_LFHRESONANCECORRELATIONTABLES_H_ +#define PWGLF_DATAMODEL_LFHRESONANCECORRELATIONTABLES_H_ + +#include +#include + +// Simple checker +#define bitcheck(var, nbit) ((var) & (1 << (nbit))) + +namespace o2::aod +{ +/// _________________________________________ +/// Table for storing trigger track indices +namespace triggerTracks +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true physical primary flag +DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Trigger"); //! +DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt +DECLARE_SOA_COLUMN(IsLeading, isLeading, bool); // is leading track in the event +} // namespace triggerTracks +DECLARE_SOA_TABLE(TriggerTracks, "AOD", "TRIGGERTRACKS", o2::soa::Index<>, triggerTracks::CollisionId, triggerTracks::MCPhysicalPrimary, triggerTracks::TrackId, triggerTracks::MCOriginalPt, triggerTracks::IsLeading); +namespace triggerTrackExtras +{ +DECLARE_SOA_COLUMN(Extra, extra, int); // true physical primary flag +} // namespace triggerTrackExtras +DECLARE_SOA_TABLE(TriggerTrackExtras, "AOD", "TRIGGERTRACKEXTRAs", triggerTrackExtras::Extra); + +/// _________________________________________ +/// Table for storing assoc track indices +namespace assocHadrons +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true physical primary flag +DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Assoc"); //! +DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt +DECLARE_SOA_COLUMN(PDGCode, pdgCode, int); // pdg code of the MC particle +// Rapidity under the mass hypothesis of the species this entry was actually +// selected as (pion or kaon, from isValidAssocTrack()); -999 for +// the species-agnostic AssocHadron pool, which has no mass hypothesis to use. +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); +} // namespace assocHadrons +DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode, assocHadrons::Rapidity); +/// _________________________________________ +/// Table for storing assoc track PID +namespace assocPID +{ +DECLARE_SOA_COLUMN(NSigmaTPCPi, nSigmaTPCPi, float); +DECLARE_SOA_COLUMN(NSigmaTPCKa, nSigmaTPCKa, float); +DECLARE_SOA_COLUMN(NSigmaTPCPr, nSigmaTPCPr, float); +DECLARE_SOA_COLUMN(NSigmaTPCEl, nSigmaTPCEl, float); +DECLARE_SOA_COLUMN(NSigmaTOFPi, nSigmaTOFPi, float); +DECLARE_SOA_COLUMN(NSigmaTOFKa, nSigmaTOFKa, float); +DECLARE_SOA_COLUMN(NSigmaTOFPr, nSigmaTOFPr, float); +DECLARE_SOA_COLUMN(NSigmaTOFEl, nSigmaTOFEl, float); +} // namespace assocPID +DECLARE_SOA_TABLE(AssocPID, "AOD", "ASSOCPID", assocPID::NSigmaTPCPi, assocPID::NSigmaTPCKa, assocPID::NSigmaTPCPr, assocPID::NSigmaTPCEl, assocPID::NSigmaTOFPi, assocPID::NSigmaTOFKa, assocPID::NSigmaTOFPr, assocPID::NSigmaTOFEl); + +/// _________________________________________ +/// Table for storing associated phi candidate indices +namespace assocPhis +{ + +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! + +// MC information +DECLARE_SOA_COLUMN(MCTruePhi, mcTruePhi, bool); +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); + +// Quality variable +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // phi candidate's own rapidity (kaon-mass-pair system) + +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_PhiPosDaughter"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_PhiNegDaughter"); + +} // namespace assocPhis + +DECLARE_SOA_TABLE( + AssocPhis, + "AOD", + "ASSOCPHIS", + o2::soa::Index<>, + assocPhis::CollisionId, + assocPhis::MCTruePhi, + assocPhis::MCPhysicalPrimary, + assocPhis::Pt, + assocPhis::Eta, + assocPhis::Phi, + assocPhis::Mass, + assocPhis::Rapidity, + assocPhis::PosTrackDaughterId, + assocPhis::NegTrackDaughterId); + +/// _________________________________________ +/// Table for storing associated K*0 candidate indices +/// K*0 -> K+ pi- and anti-K*0 -> K- pi+ are stored together (sign-agnostic, +/// same convention as AssocPhis): PosTrackDaughter/NegTrackDaughter identify +/// the positively/negatively charged daughter, irrespective of which one is +/// the kaon and which one is the pion for that candidate. +namespace assocKstars +{ + +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! + +// MC information +DECLARE_SOA_COLUMN(MCTrueKstar, mcTrueKstar, bool); +DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); + +// Quality variable +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Mass, mass, float); +DECLARE_SOA_COLUMN(Rapidity, rapidity, float); // K*0 candidate's own rapidity (kaon+pion system) + +DECLARE_SOA_INDEX_COLUMN_FULL(PosTrackDaughter, posTrackDaughter, int, Tracks, "_KstarPosDaughter"); +DECLARE_SOA_INDEX_COLUMN_FULL(NegTrackDaughter, negTrackDaughter, int, Tracks, "_KstarNegDaughter"); + +} // namespace assocKstars + +DECLARE_SOA_TABLE( + AssocKstars, + "AOD", + "ASSOCKSTARS", + o2::soa::Index<>, + assocKstars::CollisionId, + assocKstars::MCTrueKstar, + assocKstars::MCPhysicalPrimary, + assocKstars::Pt, + assocKstars::Eta, + assocKstars::Phi, + assocKstars::Mass, + assocKstars::Rapidity, + assocKstars::PosTrackDaughterId, + assocKstars::NegTrackDaughterId); +} // namespace o2::aod +#endif // PWGLF_DATAMODEL_LFHRESONANCECORRELATIONTABLES_H_ diff --git a/PWGLF/DataModel/LFHStrangeCorrelationTables.h b/PWGLF/DataModel/LFHStrangeCorrelationTables.h index 34404079cbc..fc44ef7a56f 100644 --- a/PWGLF/DataModel/LFHStrangeCorrelationTables.h +++ b/PWGLF/DataModel/LFHStrangeCorrelationTables.h @@ -29,6 +29,8 @@ #include #include +#include + // Simple checker #define bitcheck(var, nbit) ((var) & (1 << (nbit))) @@ -43,8 +45,9 @@ DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true ph DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Trigger"); //! DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt DECLARE_SOA_COLUMN(IsLeading, isLeading, bool); // is leading track in the event +DECLARE_SOA_COLUMN(MCMask, mcMask, uint16_t); // MC mask of the MC particle } // namespace triggerTracks -DECLARE_SOA_TABLE(TriggerTracks, "AOD", "TRIGGERTRACKS", o2::soa::Index<>, triggerTracks::CollisionId, triggerTracks::MCPhysicalPrimary, triggerTracks::TrackId, triggerTracks::MCOriginalPt, triggerTracks::IsLeading); +DECLARE_SOA_TABLE(TriggerTracks, "AOD", "TRIGGERTRACKS", o2::soa::Index<>, triggerTracks::CollisionId, triggerTracks::MCPhysicalPrimary, triggerTracks::TrackId, triggerTracks::MCOriginalPt, triggerTracks::IsLeading, triggerTracks::MCMask); namespace triggerTrackExtras { DECLARE_SOA_COLUMN(Extra, extra, int); // true physical primary flag @@ -59,8 +62,9 @@ DECLARE_SOA_COLUMN(MCPhysicalPrimary, mcPhysicalPrimary, bool); // true phys DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Assoc"); //! DECLARE_SOA_COLUMN(MCOriginalPt, mcOriginalPt, float); // true generated pt DECLARE_SOA_COLUMN(PDGCode, pdgCode, float); // pdg code of the MC particle +DECLARE_SOA_COLUMN(MCMask, mcMask, uint16_t); // MC mask of the MC particle } // namespace assocHadrons -DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode); +DECLARE_SOA_TABLE(AssocHadrons, "AOD", "ASSOCHADRONS", o2::soa::Index<>, assocHadrons::CollisionId, assocHadrons::MCPhysicalPrimary, assocHadrons::TrackId, assocHadrons::MCOriginalPt, assocHadrons::PDGCode, assocHadrons::MCMask); /// _________________________________________ /// Table for storing assoc track PID namespace assocPID diff --git a/PWGLF/DataModel/LFHypernucleiKfTables.h b/PWGLF/DataModel/LFHypernucleiKfTables.h index fdb223af586..41c4f367cf5 100644 --- a/PWGLF/DataModel/LFHypernucleiKfTables.h +++ b/PWGLF/DataModel/LFHypernucleiKfTables.h @@ -94,9 +94,11 @@ namespace hykftrk DECLARE_SOA_INDEX_COLUMN(HypKfColl, hypKfColl); DECLARE_SOA_COLUMN(Rigidity, rigidity, float); //! DECLARE_SOA_COLUMN(TpcNcluster, tpcNcluster, float); //! +DECLARE_SOA_COLUMN(TpcNclsPid, tpcNclsPid, float); //! +DECLARE_SOA_COLUMN(TpcNclsCr, tpcNclsCr, float); //! DECLARE_SOA_COLUMN(TpcNsigma, tpcNsigma, float); //! -DECLARE_SOA_COLUMN(TpcNsigmaNhp, tpcNsigmaNhp, float); //! -DECLARE_SOA_COLUMN(TpcNsigmaNlp, tpcNsigmaNlp, float); //! +DECLARE_SOA_COLUMN(ItsNsigma, itsNsigma, float); //! +DECLARE_SOA_COLUMN(PidForTrk, pidForTrk, uint32_t); //! DECLARE_SOA_COLUMN(TofMass, tofMass, float); //! DECLARE_SOA_COLUMN(IsPVContributor, isPVContributor, bool); //! DECLARE_SOA_COLUMN(SubMass, subMass, float); //! @@ -140,15 +142,17 @@ DECLARE_SOA_TABLE(HypKfTracks, "AOD", "HYPKFTRACK", track::Phi, track::DcaXY, track::DcaZ, - hykftrk::TpcNcluster, track::TPCChi2NCl, track::ITSClusterSizes, track::ITSChi2NCl, hykftrk::Rigidity, track::TPCSignal, + hykftrk::TpcNcluster, + hykftrk::TpcNclsPid, + hykftrk::TpcNclsCr, hykftrk::TpcNsigma, - hykftrk::TpcNsigmaNhp, - hykftrk::TpcNsigmaNlp, + hykftrk::ItsNsigma, + hykftrk::PidForTrk, hykftrk::TofMass, hykftrk::IsPVContributor, hykftrk::Px, diff --git a/PWGLF/DataModel/LFHypernucleiTables.h b/PWGLF/DataModel/LFHypernucleiTables.h index d494fcbf87a..c737e867f82 100644 --- a/PWGLF/DataModel/LFHypernucleiTables.h +++ b/PWGLF/DataModel/LFHypernucleiTables.h @@ -25,17 +25,21 @@ namespace o2::aod { namespace hyperrec { -DECLARE_SOA_COLUMN(CentralityFT0A, centralityFT0A, float); // centrality with FT0A estimator -DECLARE_SOA_COLUMN(CentralityFT0C, centralityFT0C, float); // centrality with FT0C estimator -DECLARE_SOA_COLUMN(CentralityFT0M, centralityFT0M, float); // centrality with FT0M estimator -DECLARE_SOA_COLUMN(PsiFT0A, psiFT0A, float); // Psi with FT0A estimator -DECLARE_SOA_COLUMN(MultFT0A, multFT0A, float); // Multiplicity with FT0A estimator -DECLARE_SOA_COLUMN(PsiFT0C, psiFT0C, float); // Psi with FT0C estimator -DECLARE_SOA_COLUMN(QFT0C, qFT0C, float); // Amplitude with FT0C estimator -DECLARE_SOA_COLUMN(MultFT0C, multFT0C, float); // Multiplicity with FT0C estimator -DECLARE_SOA_COLUMN(PsiTPC, psiTPC, float); // Psi with TPC estimator -DECLARE_SOA_COLUMN(MultTPC, multTPC, float); // Multiplicity with TPC estimator -DECLARE_SOA_COLUMN(CollisionId, collisionId, int64_t); // CollisionID +DECLARE_SOA_COLUMN(CentralityFT0A, centralityFT0A, float); // centrality with FT0A estimator +DECLARE_SOA_COLUMN(CentralityFT0C, centralityFT0C, float); // centrality with FT0C estimator +DECLARE_SOA_COLUMN(CentralityFT0M, centralityFT0M, float); // centrality with FT0M estimator +DECLARE_SOA_COLUMN(TrackOccupancyInTimeRange, trackOccupancyInTimeRange, int); // Track occupancy in the time range around the collision +DECLARE_SOA_COLUMN(Ft0cOccupancyInTimeRange, ft0cOccupancyInTimeRange, float); // FT0C occupancy in the time range around the collision +DECLARE_SOA_COLUMN(PsiFT0A, psiFT0A, float); // Psi with FT0A estimator +DECLARE_SOA_COLUMN(MultFT0A, multFT0A, float); // Multiplicity with FT0A estimator +DECLARE_SOA_COLUMN(PsiFT0C, psiFT0C, float); // Psi with FT0C estimator +DECLARE_SOA_COLUMN(QFT0C, qFT0C, float); // Amplitude with FT0C estimator +DECLARE_SOA_COLUMN(MultFT0C, multFT0C, float); // Multiplicity with FT0C estimator +DECLARE_SOA_COLUMN(PsiTPC, psiTPC, float); // Psi with TPC estimator +DECLARE_SOA_COLUMN(MultTPC, multTPC, float); // Multiplicity with TPC estimator +DECLARE_SOA_INDEX_COLUMN(Collision, collision); // Collision index +DECLARE_SOA_INDEX_COLUMN_FULL(HeTrack, heTrack, int, Tracks, "_He"); // Original helium daughter track +DECLARE_SOA_INDEX_COLUMN_FULL(PiTrack, piTrack, int, Tracks, "_Pi"); // Original pion daughter track DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); // Run number DECLARE_SOA_COLUMN(IsMatter, isMatter, bool); // bool: true for matter @@ -62,19 +66,19 @@ DECLARE_SOA_COLUMN(NTPCpidClusHe, nTPCpidClusHe, uint8_t); // Number DECLARE_SOA_COLUMN(NTPCpidClusPi, nTPCpidClusPi, uint8_t); // Number of TPC clusters with PID information of the Pi daughter DECLARE_SOA_COLUMN(NTPCCrossedRowsHe, nTPCCrossedRowsHe, uint8_t); // Number of TPC crossed rows of the He daughter DECLARE_SOA_COLUMN(NTPCCrossedRowsPi, nTPCCrossedRowsPi, uint8_t); // Number of TPC crossed rows of the Pi daughter -DECLARE_SOA_COLUMN(TPCsignalHe, tpcSignalHe, uint16_t); // TPC signal of the He daughter -DECLARE_SOA_COLUMN(TPCsignalPi, tpcSignalPi, uint16_t); // TPC signal of the Pi daughter -DECLARE_SOA_COLUMN(TPCChi2He, tpcChi2He, float); // TPC chi2 of the He daughter -DECLARE_SOA_COLUMN(ITSChi2He, itsChi2He, float); // ITS chi2 of the He daughter -DECLARE_SOA_COLUMN(ITSChi2Pi, itsChi2Pi, float); // ITS chi2 of the Pi daughter +DECLARE_SOA_COLUMN(TpcSignalHe, tpcSignalHe, uint16_t); // TPC signal of the He daughter +DECLARE_SOA_COLUMN(TpcSignalPi, tpcSignalPi, uint16_t); // TPC signal of the Pi daughter +DECLARE_SOA_COLUMN(TpcChi2He, tpcChi2He, float); // TPC chi2 of the He daughter +DECLARE_SOA_COLUMN(ItsChi2He, itsChi2He, float); // ITS chi2 of the He daughter +DECLARE_SOA_COLUMN(ItsChi2Pi, itsChi2Pi, float); // ITS chi2 of the Pi daughter DECLARE_SOA_COLUMN(TrackedClSize, trackedClSize, int); // int: zero for non-tracked candidates DECLARE_SOA_COLUMN(Flags, flags, uint8_t); // Flags for PID in tracking (bits [0, 3] for negative daughter, [4,7] for positive daughter) -DECLARE_SOA_COLUMN(TPCmomHe, tpcMomHe, float); // TPC momentum of the He daughter -DECLARE_SOA_COLUMN(TPCmomPi, tpcMomPi, float); // TPC momentum of the Pi daughter -DECLARE_SOA_COLUMN(TOFMass, tofMass, float); // TOF mass of the candidate -DECLARE_SOA_COLUMN(ITSclusterSizesHe, itsClusterSizesHe, uint32_t); // ITS cluster size of the He daughter -DECLARE_SOA_COLUMN(ITSclusterSizesPi, itsClusterSizesPi, uint32_t); // ITS cluster size of the Pi daughter -DECLARE_SOA_COLUMN(ITSclusterSizesHyp, itsClusterSizesHyp, uint32_t); // ITS cluster size of the Pi daughter +DECLARE_SOA_COLUMN(TpcMomHe, tpcMomHe, float); // TPC momentum of the He daughter +DECLARE_SOA_COLUMN(TpcMomPi, tpcMomPi, float); // TPC momentum of the Pi daughter +DECLARE_SOA_COLUMN(TofMass, tofMass, float); // TOF mass of the candidate +DECLARE_SOA_COLUMN(ItsClusterSizesHe, itsClusterSizesHe, uint32_t); // ITS cluster size of the He daughter +DECLARE_SOA_COLUMN(ItsClusterSizesPi, itsClusterSizesPi, uint32_t); // ITS cluster size of the Pi daughter +DECLARE_SOA_COLUMN(ItsClusterSizesHyp, itsClusterSizesHyp, uint32_t); // ITS cluster size of the Pi daughter DECLARE_SOA_COLUMN(DcaHe, dcaHe, float); // DCA between He daughter and V0 DECLARE_SOA_COLUMN(DcaPi, dcaPi, float); // DCA between pi daughter and V0 DECLARE_SOA_COLUMN(GenPt, genPt, float); // Pt of the hypertriton @@ -95,6 +99,7 @@ DECLARE_SOA_COLUMN(IsTwoBodyDecay, isTwoBodyDecay, bool); // bool: t DECLARE_SOA_TABLE(DataHypCands, "AOD", "HYPCANDS", o2::soa::Index<>, hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::TrackOccupancyInTimeRange, hyperrec::Ft0cOccupancyInTimeRange, hyperrec::XPrimVtx, hyperrec::YPrimVtx, hyperrec::ZPrimVtx, hyperrec::RunNumber, hyperrec::IsMatter, @@ -103,14 +108,15 @@ DECLARE_SOA_TABLE(DataHypCands, "AOD", "HYPCANDS", hyperrec::XDecVtx, hyperrec::YDecVtx, hyperrec::ZDecVtx, hyperrec::DcaV0Daug, hyperrec::DcaHe, hyperrec::DcaPi, hyperrec::NSigmaHe, hyperrec::NTPCclusHe, hyperrec::NTPCclusPi, hyperrec::NTPCpidClusHe, hyperrec::NTPCpidClusPi, hyperrec::NTPCCrossedRowsHe, hyperrec::NTPCCrossedRowsPi, - hyperrec::TPCmomHe, hyperrec::TPCmomPi, hyperrec::TPCsignalHe, hyperrec::TPCsignalPi, hyperrec::TPCChi2He, hyperrec::ITSChi2He, hyperrec::ITSChi2Pi, - hyperrec::TOFMass, - hyperrec::ITSclusterSizesHe, hyperrec::ITSclusterSizesPi, + hyperrec::TpcMomHe, hyperrec::TpcMomPi, hyperrec::TpcSignalHe, hyperrec::TpcSignalPi, hyperrec::TpcChi2He, hyperrec::ItsChi2He, hyperrec::ItsChi2Pi, + hyperrec::TofMass, + hyperrec::ItsClusterSizesHe, hyperrec::ItsClusterSizesPi, hyperrec::Flags, hyperrec::TrackedClSize); DECLARE_SOA_TABLE(DataHypCandsFlow, "AOD", "HYPCANDSFLOW", o2::soa::Index<>, hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::TrackOccupancyInTimeRange, hyperrec::Ft0cOccupancyInTimeRange, hyperrec::PsiFT0A, hyperrec::MultFT0A, hyperrec::PsiFT0C, hyperrec::MultFT0C, hyperrec::QFT0C, hyperrec::PsiTPC, hyperrec::MultTPC, @@ -122,14 +128,15 @@ DECLARE_SOA_TABLE(DataHypCandsFlow, "AOD", "HYPCANDSFLOW", hyperrec::XDecVtx, hyperrec::YDecVtx, hyperrec::ZDecVtx, hyperrec::DcaV0Daug, hyperrec::DcaHe, hyperrec::DcaPi, hyperrec::NSigmaHe, hyperrec::NTPCclusHe, hyperrec::NTPCclusPi, hyperrec::NTPCpidClusHe, hyperrec::NTPCpidClusPi, hyperrec::NTPCCrossedRowsHe, hyperrec::NTPCCrossedRowsPi, - hyperrec::TPCmomHe, hyperrec::TPCmomPi, hyperrec::TPCsignalHe, hyperrec::TPCsignalPi, hyperrec::TPCChi2He, hyperrec::ITSChi2He, hyperrec::ITSChi2Pi, - hyperrec::TOFMass, - hyperrec::ITSclusterSizesHe, hyperrec::ITSclusterSizesPi, + hyperrec::TpcMomHe, hyperrec::TpcMomPi, hyperrec::TpcSignalHe, hyperrec::TpcSignalPi, hyperrec::TpcChi2He, hyperrec::ItsChi2He, hyperrec::ItsChi2Pi, + hyperrec::TofMass, + hyperrec::ItsClusterSizesHe, hyperrec::ItsClusterSizesPi, hyperrec::Flags, hyperrec::TrackedClSize); DECLARE_SOA_TABLE(MCHypCands, "AOD", "MCHYPCANDS", o2::soa::Index<>, hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::TrackOccupancyInTimeRange, hyperrec::Ft0cOccupancyInTimeRange, hyperrec::XPrimVtx, hyperrec::YPrimVtx, hyperrec::ZPrimVtx, hyperrec::RunNumber, hyperrec::IsMatter, @@ -138,9 +145,40 @@ DECLARE_SOA_TABLE(MCHypCands, "AOD", "MCHYPCANDS", hyperrec::XDecVtx, hyperrec::YDecVtx, hyperrec::ZDecVtx, hyperrec::DcaV0Daug, hyperrec::DcaHe, hyperrec::DcaPi, hyperrec::NSigmaHe, hyperrec::NTPCclusHe, hyperrec::NTPCclusPi, hyperrec::NTPCpidClusHe, hyperrec::NTPCpidClusPi, hyperrec::NTPCCrossedRowsHe, hyperrec::NTPCCrossedRowsPi, - hyperrec::TPCmomHe, hyperrec::TPCmomPi, hyperrec::TPCsignalHe, hyperrec::TPCsignalPi, hyperrec::TPCChi2He, hyperrec::ITSChi2He, hyperrec::ITSChi2Pi, - hyperrec::TOFMass, - hyperrec::ITSclusterSizesHe, hyperrec::ITSclusterSizesPi, + hyperrec::TpcMomHe, hyperrec::TpcMomPi, hyperrec::TpcSignalHe, hyperrec::TpcSignalPi, hyperrec::TpcChi2He, hyperrec::ItsChi2He, hyperrec::ItsChi2Pi, + hyperrec::TofMass, + hyperrec::ItsClusterSizesHe, hyperrec::ItsClusterSizesPi, + hyperrec::Flags, hyperrec::TrackedClSize, + hyperrec::GenPt, + hyperrec::GenPhi, + hyperrec::GenEta, + hyperrec::GenPtHe3, + hyperrec::GenXDecVtx, + hyperrec::GenYDecVtx, + hyperrec::GenZDecVtx, + hyperrec::IsReco, + hyperrec::IsFakeHeOnITSLayer, + hyperrec::IsSignal, + hyperrec::IsRecoMCCollision, + hyperrec::IsSurvEvSel, + hyperrec::IsTwoBodyDecay, aod::mcparticle::StatusCode); + +DECLARE_SOA_TABLE(MCHypCandsWColl, "AOD", "MCHYPCANDSWCOLL", + o2::soa::Index<>, + hyperrec::CollisionId, hyperrec::HeTrackId, hyperrec::PiTrackId, + hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::TrackOccupancyInTimeRange, hyperrec::Ft0cOccupancyInTimeRange, + hyperrec::XPrimVtx, hyperrec::YPrimVtx, hyperrec::ZPrimVtx, + + hyperrec::RunNumber, hyperrec::IsMatter, + hyperrec::PtHe3, hyperrec::PhiHe3, hyperrec::EtaHe3, + hyperrec::PtPi, hyperrec::PhiPi, hyperrec::EtaPi, + hyperrec::XDecVtx, hyperrec::YDecVtx, hyperrec::ZDecVtx, + hyperrec::DcaV0Daug, hyperrec::DcaHe, hyperrec::DcaPi, + hyperrec::NSigmaHe, hyperrec::NTPCclusHe, hyperrec::NTPCclusPi, hyperrec::NTPCpidClusHe, hyperrec::NTPCpidClusPi, hyperrec::NTPCCrossedRowsHe, hyperrec::NTPCCrossedRowsPi, + hyperrec::TpcMomHe, hyperrec::TpcMomPi, hyperrec::TpcSignalHe, hyperrec::TpcSignalPi, hyperrec::TpcChi2He, hyperrec::ItsChi2He, hyperrec::ItsChi2Pi, + hyperrec::TofMass, + hyperrec::ItsClusterSizesHe, hyperrec::ItsClusterSizesPi, hyperrec::Flags, hyperrec::TrackedClSize, hyperrec::GenPt, hyperrec::GenPhi, @@ -158,7 +196,9 @@ DECLARE_SOA_TABLE(MCHypCands, "AOD", "MCHYPCANDS", DECLARE_SOA_TABLE(DataHypCandsWColl, "AOD", "HYPCANDSWCOLL", o2::soa::Index<>, - hyperrec::CollisionId, hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::CollisionId, hyperrec::HeTrackId, hyperrec::PiTrackId, + hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::TrackOccupancyInTimeRange, hyperrec::Ft0cOccupancyInTimeRange, hyperrec::XPrimVtx, hyperrec::YPrimVtx, hyperrec::ZPrimVtx, hyperrec::RunNumber, hyperrec::IsMatter, @@ -167,14 +207,15 @@ DECLARE_SOA_TABLE(DataHypCandsWColl, "AOD", "HYPCANDSWCOLL", hyperrec::XDecVtx, hyperrec::YDecVtx, hyperrec::ZDecVtx, hyperrec::DcaV0Daug, hyperrec::DcaHe, hyperrec::DcaPi, hyperrec::NSigmaHe, hyperrec::NTPCclusHe, hyperrec::NTPCclusPi, hyperrec::NTPCpidClusHe, hyperrec::NTPCpidClusPi, hyperrec::NTPCCrossedRowsHe, hyperrec::NTPCCrossedRowsPi, - hyperrec::TPCmomHe, hyperrec::TPCmomPi, hyperrec::TPCsignalHe, hyperrec::TPCsignalPi, hyperrec::TPCChi2He, hyperrec::ITSChi2He, hyperrec::ITSChi2Pi, - hyperrec::TOFMass, - hyperrec::ITSclusterSizesHe, hyperrec::ITSclusterSizesPi, + hyperrec::TpcMomHe, hyperrec::TpcMomPi, hyperrec::TpcSignalHe, hyperrec::TpcSignalPi, hyperrec::TpcChi2He, hyperrec::ItsChi2He, hyperrec::ItsChi2Pi, + hyperrec::TofMass, + hyperrec::ItsClusterSizesHe, hyperrec::ItsClusterSizesPi, hyperrec::Flags, hyperrec::TrackedClSize); using DataHypCand = DataHypCands::iterator; using DataHypCandFlow = DataHypCandsFlow::iterator; using MCHypCand = MCHypCands::iterator; +using MCHypCandWColl = MCHypCandsWColl::iterator; using DataHypCandWColl = DataHypCandsWColl::iterator; namespace hyperkink @@ -187,20 +228,20 @@ DECLARE_SOA_COLUMN(PhiTrit, phiTrit, float); // Phi DECLARE_SOA_COLUMN(EtaTrit, etaTrit, float); // Eta of the triton kink DECLARE_SOA_COLUMN(DcaHyperPv, dcaHyperPv, float); // DCA of the hypertriton to the primary vertex DECLARE_SOA_COLUMN(DcaTritPv, dcaTritPv, float); // DCA of the triton kink to the primary vertex -DECLARE_SOA_COLUMN(DCAKinkTopo, dcaKinkTopo, float); // DCA of the kink topology -DECLARE_SOA_COLUMN(ITSclusterSizesHyper, itsClusterSizesHyper, uint32_t); // ITS cluster size of the hypertriton -DECLARE_SOA_COLUMN(ITSclusterSizesTrit, itsClusterSizesTrit, uint32_t); // ITS cluster size of the triton kink -DECLARE_SOA_COLUMN(PIDinTrackTrit, pidInTrackTrit, uint8_t); // PID in track for the triton kink +DECLARE_SOA_COLUMN(DcaKinkTopo, dcaKinkTopo, float); // DCA of the kink topology +DECLARE_SOA_COLUMN(ItsClusterSizesHyper, itsClusterSizesHyper, uint32_t); // ITS cluster size of the hypertriton +DECLARE_SOA_COLUMN(ItsClusterSizesTrit, itsClusterSizesTrit, uint32_t); // ITS cluster size of the triton kink +DECLARE_SOA_COLUMN(PidInTrackTrit, pidInTrackTrit, uint8_t); // PID in track for the triton kink -DECLARE_SOA_COLUMN(TPCmomTrit, tpcMomTrit, float); // TPC momentum of the triton kink -DECLARE_SOA_COLUMN(TPCsignalTrit, tpcSignalTrit, uint16_t); // TPC signal of the triton kink +DECLARE_SOA_COLUMN(TpcMomTrit, tpcMomTrit, float); // TPC momentum of the triton kink +DECLARE_SOA_COLUMN(TpcSignalTrit, tpcSignalTrit, uint16_t); // TPC signal of the triton kink DECLARE_SOA_COLUMN(NSigmaTPCTrit, nSigmaTPCTrit, float); // Number of tpc sigmas of the triton kink DECLARE_SOA_COLUMN(NSigmaTOFTrit, nSigmaTOFTrit, float); // Number of tof sigmas of the triton kink // MC additional info DECLARE_SOA_COLUMN(GenPtTrit, genPtTrit, float); // Pt of the triton kink DECLARE_SOA_COLUMN(HyperPtITS, hyperPtITS, float); // Pt of the hypertriton from ITS standalone, hypertriton tagged with MC truth -DECLARE_SOA_COLUMN(MCMask, mcMask, bool); // bool: true for fake triton +DECLARE_SOA_COLUMN(McMask, mcMask, bool); // bool: true for fake triton } // namespace hyperkink @@ -210,9 +251,9 @@ DECLARE_SOA_TABLE(DataHypKinkCands, "AOD", "HYPKINKCANDS", hyperrec::XDecVtx, hyperrec::YDecVtx, hyperrec::ZDecVtx, hyperrec::IsMatter, hyperkink::PtHyper, hyperkink::PhiHyper, hyperkink::EtaHyper, hyperkink::PtTrit, hyperkink::PhiTrit, hyperkink::EtaTrit, - hyperkink::DcaHyperPv, hyperkink::DcaTritPv, hyperkink::DCAKinkTopo, - hyperkink::ITSclusterSizesHyper, hyperkink::ITSclusterSizesTrit, hyperkink::PIDinTrackTrit, - hyperkink::TPCmomTrit, hyperkink::TPCsignalTrit, hyperkink::NSigmaTPCTrit, hyperkink::NSigmaTOFTrit); + hyperkink::DcaHyperPv, hyperkink::DcaTritPv, hyperkink::DcaKinkTopo, + hyperkink::ItsClusterSizesHyper, hyperkink::ItsClusterSizesTrit, hyperkink::PidInTrackTrit, + hyperkink::TpcMomTrit, hyperkink::TpcSignalTrit, hyperkink::NSigmaTPCTrit, hyperkink::NSigmaTOFTrit); DECLARE_SOA_TABLE(MCHypKinkCands, "AOD", "MCHYPKINKCANDS", o2::soa::Index<>, @@ -220,12 +261,12 @@ DECLARE_SOA_TABLE(MCHypKinkCands, "AOD", "MCHYPKINKCANDS", hyperrec::XDecVtx, hyperrec::YDecVtx, hyperrec::ZDecVtx, hyperrec::IsMatter, hyperkink::PtHyper, hyperkink::PhiHyper, hyperkink::EtaHyper, hyperkink::PtTrit, hyperkink::PhiTrit, hyperkink::EtaTrit, - hyperkink::DcaHyperPv, hyperkink::DcaTritPv, hyperkink::DCAKinkTopo, - hyperkink::ITSclusterSizesHyper, hyperkink::ITSclusterSizesTrit, hyperkink::PIDinTrackTrit, - hyperkink::TPCmomTrit, hyperkink::TPCsignalTrit, hyperkink::NSigmaTPCTrit, hyperkink::NSigmaTOFTrit, + hyperkink::DcaHyperPv, hyperkink::DcaTritPv, hyperkink::DcaKinkTopo, + hyperkink::ItsClusterSizesHyper, hyperkink::ItsClusterSizesTrit, hyperkink::PidInTrackTrit, + hyperkink::TpcMomTrit, hyperkink::TpcSignalTrit, hyperkink::NSigmaTPCTrit, hyperkink::NSigmaTOFTrit, hyperrec::GenXDecVtx, hyperrec::GenYDecVtx, hyperrec::GenZDecVtx, hyperrec::GenPt, hyperkink::GenPtTrit, - hyperrec::IsReco, hyperrec::IsSignal, hyperkink::MCMask, hyperkink::HyperPtITS, + hyperrec::IsReco, hyperrec::IsSignal, hyperkink::McMask, hyperkink::HyperPtITS, hyperrec::IsRecoMCCollision, hyperrec::IsSurvEvSel); } // namespace o2::aod diff --git a/PWGLF/DataModel/LFK1MlTables.h b/PWGLF/DataModel/LFK1MlTables.h new file mode 100644 index 00000000000..7b2c20c8174 --- /dev/null +++ b/PWGLF/DataModel/LFK1MlTables.h @@ -0,0 +1,126 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LFK1MlTables.h +/// \brief Derived K1 microtrack training and audit tables +/// \author Bong-Hwi Lim +/// +#ifndef PWGLF_DATAMODEL_LFK1MLTABLES_H_ +#define PWGLF_DATAMODEL_LFK1MLTABLES_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace k1ml +{ +// Persisted relations target only these derived tables, so AO2D merge tools +// can relocate them. Source AO2D row numbers remain scalar audit values. +DECLARE_SOA_COLUMN(K1RecoCollisionId, k1RecoCollisionId, int64_t); //! row index of the reduced ResoCollisions_001 collision in the input DF +DECLARE_SOA_COLUMN(K1PosZ, k1PosZ, float); +DECLARE_SOA_COLUMN(K1BField, k1BField, float); +DECLARE_SOA_COLUMN(K1Centrality, k1Centrality, float); +DECLARE_SOA_COLUMN(K1Multiplicity, k1Multiplicity, float); +DECLARE_SOA_COLUMN(K1RecINELgt0, k1RecINELgt0, bool); +DECLARE_SOA_COLUMN(K1SourceTrackId, k1SourceTrackId, int64_t); //! trackId of the reduced micro track +DECLARE_SOA_COLUMN(K1Px, k1Px, float); +DECLARE_SOA_COLUMN(K1Py, k1Py, float); +DECLARE_SOA_COLUMN(K1Pz, k1Pz, float); +DECLARE_SOA_COLUMN(K1PidPi, k1PidPi, uint8_t); +DECLARE_SOA_COLUMN(K1PidKa, k1PidKa, uint8_t); +DECLARE_SOA_COLUMN(K1PidPr, k1PidPr, uint8_t); +DECLARE_SOA_COLUMN(K1SelectionFlags, k1SelectionFlags, uint8_t); +DECLARE_SOA_COLUMN(K1TrackFlags, k1TrackFlags, uint8_t); +DECLARE_SOA_COLUMN(K1CrossedRows, k1CrossedRows, uint8_t); +DECLARE_SOA_COLUMN(K1ItsClusterMap, k1ItsClusterMap, uint8_t); +DECLARE_SOA_COLUMN(K1Mass, k1Mass, float); +DECLARE_SOA_COLUMN(K1MassPiPi, k1MassPiPi, float); +DECLARE_SOA_COLUMN(K1MassKaPiSame, k1MassKaPiSame, float); +DECLARE_SOA_COLUMN(K1MassKaPiOpp, k1MassKaPiOpp, float); +DECLARE_SOA_COLUMN(K1ScalarSumPt, k1ScalarSumPt, float); +DECLARE_SOA_COLUMN(K1PiPiPt, k1PiPiPt, float); +DECLARE_SOA_COLUMN(K1Pt, k1Pt, float); +DECLARE_SOA_COLUMN(K1Y, k1Y, float); +DECLARE_SOA_COLUMN(K1Eta, k1Eta, float); +DECLARE_SOA_COLUMN(K1Phi, k1Phi, float); +DECLARE_SOA_COLUMN(K1Charge, k1Charge, int8_t); +// Cumulative selection bits of the unlike-sign candidate: +// 1 = valid canonical candidate inside the K1 rapidity window (loose stage), +// 2 = track quality of all three tracks, +// 4 = TOF requirement and PID of all three tracks, +// 8 = pion-pair pT and secondary mass window, +// 16 = candidate cuts. +// Candidates written at the "selected" export stage carry 31. +DECLARE_SOA_COLUMN(K1BaselinePassBits, k1BaselinePassBits, uint16_t); +// NOLINTNEXTLINE(modernize-avoid-c-arrays) -- persistent fixed-size AOD column +DECLARE_SOA_COLUMN(K1MasterFeatures, k1MasterFeatures, float[125]); //! 125 master features of the K1 ML feature contract (PWGLF/Core/K1MlFeatures.h) +DECLARE_SOA_COLUMN(K1FeatureStatus, k1FeatureStatus, uint8_t); //! o2::analysis::k1ml::BuildStatus; only Ok (0) rows are written +DECLARE_SOA_COLUMN(K1TruthStatus, k1TruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched +DECLARE_SOA_COLUMN(K1TruthChannel, k1TruthChannel, uint8_t); //! 0 none, 1 rho K, 2 K* pi +DECLARE_SOA_COLUMN(K1MotherPdg, k1MotherPdg, int32_t); +DECLARE_SOA_COLUMN(K1MotherId, k1MotherId, int64_t); +DECLARE_SOA_COLUMN(K1GeneratedPt, k1GeneratedPt, float); //! Parent-table pT for a validated reconstructed MC match; NaN otherwise +DECLARE_SOA_COLUMN(K1GeneratedY, k1GeneratedY, float); //! Parent-table y for a validated reconstructed MC match; NaN otherwise +DECLARE_SOA_COLUMN(K1OriginalMcParticleId, k1OriginalMcParticleId, int64_t); +DECLARE_SOA_COLUMN(K1DaughterPdg1, k1DaughterPdg1, int32_t); +DECLARE_SOA_COLUMN(K1DaughterPdg2, k1DaughterPdg2, int32_t); +DECLARE_SOA_COLUMN(K1GenSelectedRecoEvent, k1GenSelectedRecoEvent, bool); //! true: parents are taken from selected reconstructed events +} // namespace k1ml + +DECLARE_SOA_TABLE(K1MlEvents, "AOD", "K1MLEVENT", + o2::soa::Index<>, k1ml::K1RecoCollisionId, k1ml::K1PosZ, k1ml::K1BField, + k1ml::K1Centrality, k1ml::K1Multiplicity, k1ml::K1RecINELgt0); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlEvent, k1MlEvent, int, K1MlEvents, "K1mlEvents", ""); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlTracks, "AOD", "K1MLTRACK", + o2::soa::Index<>, k1ml::K1MlEventId, k1ml::K1SourceTrackId, + k1ml::K1Px, k1ml::K1Py, k1ml::K1Pz, + k1ml::K1PidPi, k1ml::K1PidKa, k1ml::K1PidPr, + k1ml::K1SelectionFlags, k1ml::K1TrackFlags, + k1ml::K1CrossedRows, k1ml::K1ItsClusterMap); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlKaonTrack, k1MlKaonTrack, int, K1MlTracks, "K1mlTracks", "_Kaon"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlSamePionTrack, k1MlSamePionTrack, int, K1MlTracks, "K1mlTracks", "_Same"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlOppPionTrack, k1MlOppPionTrack, int, K1MlTracks, "K1mlTracks", "_Opp"); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlCandidates, "AOD", "K1MLCANDIDATE", + o2::soa::Index<>, k1ml::K1MlEventId, + k1ml::K1MlKaonTrackId, k1ml::K1MlSamePionTrackId, k1ml::K1MlOppPionTrackId, + k1ml::K1Mass, k1ml::K1MassPiPi, k1ml::K1MassKaPiSame, k1ml::K1MassKaPiOpp, + k1ml::K1ScalarSumPt, k1ml::K1PiPiPt, + k1ml::K1Pt, k1ml::K1Y, k1ml::K1Eta, k1ml::K1Phi, k1ml::K1Charge, + k1ml::K1BaselinePassBits); +namespace k1ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(K1MlCandidate, k1MlCandidate, int, K1MlCandidates, "K1mlCandidates", ""); +} // namespace k1ml +DECLARE_SOA_TABLE(K1MlInputs, "AOD", "K1MLINPUT", + o2::soa::Index<>, k1ml::K1MlCandidateId, + k1ml::K1MasterFeatures, k1ml::K1FeatureStatus); +DECLARE_SOA_TABLE(K1MlTruth, "AOD", "K1MLTRUTH", + o2::soa::Index<>, k1ml::K1MlCandidateId, + k1ml::K1TruthStatus, k1ml::K1TruthChannel, + k1ml::K1MotherPdg, k1ml::K1MotherId, + k1ml::K1GeneratedPt, k1ml::K1GeneratedY); +DECLARE_SOA_TABLE(K1MlGenAudit, "AOD", "K1MLGENAUDIT", + o2::soa::Index<>, k1ml::K1RecoCollisionId, k1ml::K1OriginalMcParticleId, + k1ml::K1MotherPdg, k1ml::K1DaughterPdg1, k1ml::K1DaughterPdg2, + k1ml::K1TruthChannel, k1ml::K1GeneratedPt, k1ml::K1GeneratedY, + k1ml::K1GenSelectedRecoEvent); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFK1MLTABLES_H_ diff --git a/PWGLF/DataModel/LFKinkDecayTables.h b/PWGLF/DataModel/LFKinkDecayTables.h index 45afb41325d..c636358e20b 100644 --- a/PWGLF/DataModel/LFKinkDecayTables.h +++ b/PWGLF/DataModel/LFKinkDecayTables.h @@ -25,6 +25,7 @@ #include #include +#include namespace o2::aod { @@ -159,6 +160,258 @@ DECLARE_SOA_TABLE(SlimKinkCandsMC, "AOD", "SLIMKINKCANDSMC", kinkcand::MothPdgCode, kinkcand::DaugPdgCode, kinkcand::PtMC, kinkcand::PzMC, kinkcand::MassMC, kinkcand::DecayRadiusMC, kinkcand::CollisionIdCheck); +namespace sigmapluscand +{ + +DECLARE_SOA_INDEX_COLUMN(Collision, collision); //! Collision of the candidate (the proton's collision), -1 for generated-only MC rows +DECLARE_SOA_INDEX_COLUMN_FULL(ProtonTrack, protonTrack, int, TracksIU, "_Proton"); //! Proton track +DECLARE_SOA_INDEX_COLUMN_FULL(PhotonPosTrack, photonPosTrack, int, TracksIU, "_PosDau"); //! Positive daughter track of the measured photon +DECLARE_SOA_INDEX_COLUMN_FULL(PhotonNegTrack, photonNegTrack, int, TracksIU, "_NegDau"); //! Negative daughter track of the measured photon +DECLARE_SOA_INDEX_COLUMN(McParticle, mcParticle); //! MC particle of the true Sigma+ if proton and photon come from it, -1 otherwise + +DECLARE_SOA_COLUMN(XDecVtx, xDecVtx, float); //! Decay vertex of the candidate (x direction) +DECLARE_SOA_COLUMN(YDecVtx, yDecVtx, float); //! Decay vertex of the candidate (y direction) +DECLARE_SOA_COLUMN(ZDecVtx, zDecVtx, float); //! Decay vertex of the candidate (z direction) +DECLARE_SOA_COLUMN(FlightDistance, flightDistance, float); //! 3D distance from the PV to the decay vertex (cm) +DECLARE_SOA_COLUMN(TransDecayRadius, transDecayRadius, float); //! Transverse decay radius of the candidate (cm), stored directly in the slim tables (no XDecVtx/YDecVtx there) +DECLARE_SOA_COLUMN(DcaProtonGamma, dcaProtonGamma, float); //! DCA between proton and photon at the fitted vertex (cm) + +DECLARE_SOA_COLUMN(PxProton, pxProton, float); //! Px of the proton +DECLARE_SOA_COLUMN(PyProton, pyProton, float); //! Py of the proton +DECLARE_SOA_COLUMN(PzProton, pzProton, float); //! Pz of the proton +DECLARE_SOA_COLUMN(PxGamma1, pxGamma1, float); //! Px of the measured (PCM) photon +DECLARE_SOA_COLUMN(PyGamma1, pyGamma1, float); //! Py of the measured (PCM) photon +DECLARE_SOA_COLUMN(PzGamma1, pzGamma1, float); //! Pz of the measured (PCM) photon +DECLARE_SOA_COLUMN(PxGamma2, pxGamma2, float); //! Px of the reconstructed (missing) photon +DECLARE_SOA_COLUMN(PyGamma2, pyGamma2, float); //! Py of the reconstructed (missing) photon +DECLARE_SOA_COLUMN(PzGamma2, pzGamma2, float); //! Pz of the reconstructed (missing) photon + +DECLARE_SOA_COLUMN(NSigmaTPCProton, nSigmaTPCProton, float); //! TPC nSigma of the proton track +DECLARE_SOA_COLUMN(NSigmaTOFProton, nSigmaTOFProton, float); //! TOF nSigma of the proton track +DECLARE_SOA_COLUMN(NSigmaTPCElPos, nSigmaTPCElPos, float); //! TPC nSigma_el of the photon's positive daughter +DECLARE_SOA_COLUMN(NSigmaTPCElNeg, nSigmaTPCElNeg, float); //! TPC nSigma_el of the photon's negative daughter + +DECLARE_SOA_COLUMN(PhotonMass, photonMass, float); //! Invariant mass of the measured photon (V0) candidate (GeV/c^2) +DECLARE_SOA_COLUMN(PhotonAlpha, photonAlpha, float); //! Armenteros-Podolanski alpha of the measured photon +DECLARE_SOA_COLUMN(PhotonQt, photonQt, float); //! Armenteros-Podolanski qT of the measured photon (GeV/c) +DECLARE_SOA_COLUMN(PhotonConvRadius, photonConvRadius, float); //! Conversion radius of the measured photon (cm) +DECLARE_SOA_COLUMN(PhotonOpeningAngle, photonOpeningAngle, float); //! Opening angle between the photon's e+e- daughters (rad) +DECLARE_SOA_COLUMN(PhotonPointingAngle, photonPointingAngle, float); //! Angle between the photon momentum and the line from its conversion point to the candidate decay vertex (rad) + +DECLARE_SOA_COLUMN(RootCenter, rootCenter, float); //! -coefB/(2*coefA) of the missing-photon quadratic solve: negative flags an unphysical phase-space point +DECLARE_SOA_COLUMN(AntiSigmaPointingAngle, antiSigmaPointingAngle, float); //! Angle between the field-unbent proton momentum (from its original reference point) and the PV->decay-vertex direction +DECLARE_SOA_COLUMN(CandDcaToPV, candDcaToPV, float); //! DCA of the candidate's total reconstructed momentum line to the PV (cm) +DECLARE_SOA_COLUMN(FlightDirTilt, flightDirTilt, float); //! Tilt of the PV->decay-vertex direction needed for the missing photon to have a real solution (rad), 0 if none needed + +DECLARE_SOA_COLUMN(ProtonSign, protonSign, int); //! Charge sign of the proton track (= sign of the whole candidate, since the photon is neutral) +DECLARE_SOA_COLUMN(ProtonItsNCls, protonItsNCls, uint8_t); //! Number of ITS clusters of the proton track +DECLARE_SOA_COLUMN(ProtonTpcNCls, protonTpcNCls, int16_t); //! Number of found TPC clusters of the proton track +DECLARE_SOA_COLUMN(ProtonDcaXY, protonDcaXY, float); //! DCA of the proton track to the primary vertex, xy (cm) +DECLARE_SOA_COLUMN(ProtonDcaZ, protonDcaZ, float); //! DCA of the proton track to the primary vertex, z (cm) + +DECLARE_SOA_COLUMN(PhotonPosItsNCls, photonPosItsNCls, uint8_t); //! Number of ITS clusters of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonPosTpcNCls, photonPosTpcNCls, int16_t); //! Number of found TPC clusters of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonNegItsNCls, photonNegItsNCls, uint8_t); //! Number of ITS clusters of the photon's negative daughter +DECLARE_SOA_COLUMN(PhotonNegTpcNCls, photonNegTpcNCls, int16_t); //! Number of found TPC clusters of the photon's negative daughter + +DECLARE_SOA_COLUMN(PhotonDcaDau, photonDcaDau, float); //! DCA between the photon's e+e- daughters at the conversion point (cm) +DECLARE_SOA_COLUMN(PhotonCosPAToPV, photonCosPAToPV, float); //! Cosine of the angle between the photon momentum and the PV->conversion-point direction +DECLARE_SOA_COLUMN(PhotonDcaXYToPV, photonDcaXYToPV, float); //! Transverse DCA of the photon's flight line to the PV (cm) +DECLARE_SOA_COLUMN(PhotonDcaZToPV, photonDcaZToPV, float); //! Longitudinal distance of the photon's flight line to the PV at its transverse DCA (cm) +DECLARE_SOA_COLUMN(PhotonPsiPair, photonPsiPair, float); //! psi_pair of the e+e- legs, evaluated after propagating them outward, 999 if the propagation failed +DECLARE_SOA_COLUMN(PhotonPosDcaXY, photonPosDcaXY, float); //! DCAxy of the photon's positive daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonPosDcaZ, photonPosDcaZ, float); //! DCAz of the photon's positive daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonNegDcaXY, photonNegDcaXY, float); //! DCAxy of the photon's negative daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonNegDcaZ, photonNegDcaZ, float); //! DCAz of the photon's negative daughter to the PV of its own collision (cm) +DECLARE_SOA_COLUMN(PhotonPosTpcNClsFindable, photonPosTpcNClsFindable, int16_t); //! Number of findable TPC clusters of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonNegTpcNClsFindable, photonNegTpcNClsFindable, int16_t); //! Number of findable TPC clusters of the photon's negative daughter +DECLARE_SOA_COLUMN(PhotonPosTpcChi2NCl, photonPosTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's positive daughter +DECLARE_SOA_COLUMN(PhotonNegTpcChi2NCl, photonNegTpcChi2NCl, float); //! TPC chi2 per cluster of the photon's negative daughter +DECLARE_SOA_COLUMN(MlScore, mlScore, float); //! BDT score of the candidate (signal probability), -1 if not computed + +// MC columns +DECLARE_SOA_COLUMN(CollisionIdCheck, collisionIdCheck, bool); //! True if the proton's collision ID matches the reconstructed collision ID + +DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); //! True if the proton and photon share the same true Sigma+ mother +DECLARE_SOA_COLUMN(IsProtonFromSigma, isProtonFromSigma, bool); //! True if the proton is the daughter of a true Sigma+ -> p pi0 +DECLARE_SOA_COLUMN(IsPhotonFromSigma, isPhotonFromSigma, bool); //! True if the photon is from the pi0 of a true Sigma+ -> p pi0 + +DECLARE_SOA_COLUMN(XDecVtxMC, xDecVtxMC, float); //! MC-truth Sigma+ decay vertex (x direction) +DECLARE_SOA_COLUMN(YDecVtxMC, yDecVtxMC, float); //! MC-truth Sigma+ decay vertex (y direction) +DECLARE_SOA_COLUMN(ZDecVtxMC, zDecVtxMC, float); //! MC-truth Sigma+ decay vertex (z direction) +DECLARE_SOA_COLUMN(DecayRadiusMC, decayRadiusMC, float); //! MC-truth Sigma+ decay radius +DECLARE_SOA_COLUMN(MassMC, massMC, float); //! MC-truth invariant mass of the Sigma+ mother +DECLARE_SOA_COLUMN(PxSigmaPlusMC, pxSigmaPlusMC, float); //! MC-truth Sigma+ mother Px +DECLARE_SOA_COLUMN(PySigmaPlusMC, pySigmaPlusMC, float); //! MC-truth Sigma+ mother Py +DECLARE_SOA_COLUMN(PzSigmaPlusMC, pzSigmaPlusMC, float); //! MC-truth Sigma+ mother Pz +DECLARE_SOA_COLUMN(PxProtonMC, pxProtonMC, float); //! MC-truth proton Px +DECLARE_SOA_COLUMN(PyProtonMC, pyProtonMC, float); //! MC-truth proton Py +DECLARE_SOA_COLUMN(PzProtonMC, pzProtonMC, float); //! MC-truth proton Pz +DECLARE_SOA_COLUMN(PxGammaMC, pxGammaMC, float); //! MC-truth momentum of the measured photon (Px) +DECLARE_SOA_COLUMN(PyGammaMC, pyGammaMC, float); //! MC-truth momentum of the measured photon (Py) +DECLARE_SOA_COLUMN(PzGammaMC, pzGammaMC, float); //! MC-truth momentum of the measured photon (Pz) + +// DYNAMIC COLUMNS + +DECLARE_SOA_DYNAMIC_COLUMN(Radius, radius, //! Transverse decay radius of the candidate (cm), from the beam axis + [](float xDecVtx, float yDecVtx) -> float { return std::hypot(xDecVtx, yDecVtx); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Chi2, chi2, //! chi2 of the proton-photon vertex fit; dcaProtonGamma = sqrt(chi2) by construction in the builder + [](float dcaProtonGamma) -> float { return dcaProtonGamma * dcaProtonGamma; }); + +DECLARE_SOA_DYNAMIC_COLUMN(PxSigmaPlus, pxSigmaPlus, //! Px of the Sigma+ candidate + [](float pxProton, float pxGamma1, float pxGamma2) -> float { return pxProton + pxGamma1 + pxGamma2; }); + +DECLARE_SOA_DYNAMIC_COLUMN(PySigmaPlus, pySigmaPlus, //! Py of the Sigma+ candidate + [](float pyProton, float pyGamma1, float pyGamma2) -> float { return pyProton + pyGamma1 + pyGamma2; }); + +DECLARE_SOA_DYNAMIC_COLUMN(PzSigmaPlus, pzSigmaPlus, //! Pz of the Sigma+ candidate + [](float pzProton, float pzGamma1, float pzGamma2) -> float { return pzProton + pzGamma1 + pzGamma2; }); + +DECLARE_SOA_DYNAMIC_COLUMN(PtSigmaPlus, ptSigmaPlus, //! pT of the Sigma+ candidate + [](float pxProton, float pxGamma1, float pxGamma2, float pyProton, float pyGamma1, float pyGamma2) -> float { return std::hypot(pxProton + pxGamma1 + pxGamma2, pyProton + pyGamma1 + pyGamma2); }); + +DECLARE_SOA_DYNAMIC_COLUMN(MassSigmaPlus, massSigmaPlus, //! Invariant mass of the Sigma+ candidate (p + gamma1 + gamma2) + [](float pxProton, float pyProton, float pzProton, + float pxGamma1, float pyGamma1, float pzGamma1, + float pxGamma2, float pyGamma2, float pzGamma2) -> float { return RecoDecay::m(std::array{std::array{pxProton, pyProton, pzProton}, + std::array{pxGamma1, pyGamma1, pzGamma1}, + std::array{pxGamma2, pyGamma2, pzGamma2}}, + std::array{o2::constants::physics::MassProton, o2::constants::physics::MassGamma, o2::constants::physics::MassGamma}); }); + +DECLARE_SOA_DYNAMIC_COLUMN(PtSigmaPlusMC, ptSigmaPlusMC, //! True pT of the Sigma+ mother + [](float pxSigmaPlusMC, float pySigmaPlusMC) -> float { return std::hypot(pxSigmaPlusMC, pySigmaPlusMC); }); + +DECLARE_SOA_DYNAMIC_COLUMN(YSigmaPlusMC, ySigmaPlusMC, //! True rapidity of the Sigma+ mother + [](float pxSigmaPlusMC, float pySigmaPlusMC, float pzSigmaPlusMC) -> float { return RecoDecay::y(std::array{pxSigmaPlusMC, pySigmaPlusMC, pzSigmaPlusMC}, o2::constants::physics::MassSigmaPlus); }); + +} // namespace sigmapluscand + +DECLARE_SOA_TABLE(SigmaPlusCands, "AOD", "SIGMAPLUSCANDS", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::XDecVtx, sigmapluscand::YDecVtx, sigmapluscand::ZDecVtx, sigmapluscand::FlightDistance, + sigmapluscand::DcaProtonGamma, + sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, + sigmapluscand::PxGamma1, sigmapluscand::PyGamma1, sigmapluscand::PzGamma1, + sigmapluscand::PxGamma2, sigmapluscand::PyGamma2, sigmapluscand::PzGamma2, + sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, + sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, + sigmapluscand::PhotonMass, sigmapluscand::PhotonAlpha, sigmapluscand::PhotonQt, sigmapluscand::PhotonConvRadius, + sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, + sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, + sigmapluscand::ProtonSign, + sigmapluscand::ProtonItsNCls, sigmapluscand::ProtonTpcNCls, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, + sigmapluscand::PhotonPosItsNCls, sigmapluscand::PhotonPosTpcNCls, sigmapluscand::PhotonNegItsNCls, sigmapluscand::PhotonNegTpcNCls, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, + + // dynamic columns + sigmapluscand::Radius, + sigmapluscand::Chi2, + sigmapluscand::PxSigmaPlus, + sigmapluscand::PySigmaPlus, + sigmapluscand::PzSigmaPlus, + sigmapluscand::PtSigmaPlus, + sigmapluscand::MassSigmaPlus); + +DECLARE_SOA_TABLE(SigmaPlusCandsMC, "AOD", "SIGMAPLUSMC", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::McParticleId, + sigmapluscand::XDecVtx, sigmapluscand::YDecVtx, sigmapluscand::ZDecVtx, sigmapluscand::FlightDistance, + sigmapluscand::DcaProtonGamma, + sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, + sigmapluscand::PxGamma1, sigmapluscand::PyGamma1, sigmapluscand::PzGamma1, + sigmapluscand::PxGamma2, sigmapluscand::PyGamma2, sigmapluscand::PzGamma2, + sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, + sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, + sigmapluscand::PhotonMass, sigmapluscand::PhotonAlpha, sigmapluscand::PhotonQt, sigmapluscand::PhotonConvRadius, + sigmapluscand::PhotonOpeningAngle, sigmapluscand::PhotonPointingAngle, + sigmapluscand::RootCenter, sigmapluscand::AntiSigmaPointingAngle, sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, + sigmapluscand::ProtonSign, + sigmapluscand::ProtonItsNCls, sigmapluscand::ProtonTpcNCls, sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, + sigmapluscand::PhotonPosItsNCls, sigmapluscand::PhotonPosTpcNCls, sigmapluscand::PhotonNegItsNCls, sigmapluscand::PhotonNegTpcNCls, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, + sigmapluscand::CollisionIdCheck, + sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, + sigmapluscand::XDecVtxMC, sigmapluscand::YDecVtxMC, sigmapluscand::ZDecVtxMC, + sigmapluscand::PxProtonMC, sigmapluscand::PyProtonMC, sigmapluscand::PzProtonMC, + sigmapluscand::PxGammaMC, sigmapluscand::PyGammaMC, sigmapluscand::PzGammaMC, + sigmapluscand::PxSigmaPlusMC, sigmapluscand::PySigmaPlusMC, sigmapluscand::PzSigmaPlusMC, + sigmapluscand::DecayRadiusMC, sigmapluscand::MassMC, + + // dynamic columns + sigmapluscand::Radius, + sigmapluscand::Chi2, + sigmapluscand::PxSigmaPlus, + sigmapluscand::PySigmaPlus, + sigmapluscand::PzSigmaPlus, + sigmapluscand::PtSigmaPlus, + sigmapluscand::MassSigmaPlus, + sigmapluscand::PtSigmaPlusMC, + sigmapluscand::YSigmaPlusMC); + +DECLARE_SOA_TABLE(SlimSigmaPlusCands, "AOD", "SLIMSIGMAPLUS", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::TransDecayRadius, + sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, + sigmapluscand::DcaProtonGamma, + sigmapluscand::ProtonSign, + sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, + sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, + sigmapluscand::PxGamma1, sigmapluscand::PyGamma1, sigmapluscand::PzGamma1, + sigmapluscand::PxGamma2, sigmapluscand::PyGamma2, sigmapluscand::PzGamma2, + sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, + sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, + sigmapluscand::PhotonMass, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, + + // dynamic columns + sigmapluscand::PxSigmaPlus, + sigmapluscand::PySigmaPlus, + sigmapluscand::PzSigmaPlus, + sigmapluscand::PtSigmaPlus, + sigmapluscand::MassSigmaPlus); + +DECLARE_SOA_TABLE(SlimSigmaPlusCandsMC, "AOD", "SLIMSIGMAPLUSMC", + sigmapluscand::CollisionId, sigmapluscand::ProtonTrackId, sigmapluscand::PhotonPosTrackId, sigmapluscand::PhotonNegTrackId, + sigmapluscand::McParticleId, + sigmapluscand::TransDecayRadius, + sigmapluscand::CandDcaToPV, sigmapluscand::FlightDirTilt, + sigmapluscand::DcaProtonGamma, + sigmapluscand::ProtonSign, + sigmapluscand::ProtonDcaXY, sigmapluscand::ProtonDcaZ, + sigmapluscand::PxProton, sigmapluscand::PyProton, sigmapluscand::PzProton, + sigmapluscand::PxGamma1, sigmapluscand::PyGamma1, sigmapluscand::PzGamma1, + sigmapluscand::PxGamma2, sigmapluscand::PyGamma2, sigmapluscand::PzGamma2, + sigmapluscand::NSigmaTPCProton, sigmapluscand::NSigmaTOFProton, + sigmapluscand::NSigmaTPCElPos, sigmapluscand::NSigmaTPCElNeg, + sigmapluscand::PhotonMass, + sigmapluscand::PhotonDcaDau, sigmapluscand::PhotonCosPAToPV, sigmapluscand::PhotonDcaXYToPV, sigmapluscand::PhotonDcaZToPV, sigmapluscand::PhotonPsiPair, + sigmapluscand::PhotonPosDcaXY, sigmapluscand::PhotonPosDcaZ, sigmapluscand::PhotonNegDcaXY, sigmapluscand::PhotonNegDcaZ, + sigmapluscand::PhotonPosTpcNClsFindable, sigmapluscand::PhotonNegTpcNClsFindable, sigmapluscand::PhotonPosTpcChi2NCl, sigmapluscand::PhotonNegTpcChi2NCl, + sigmapluscand::MlScore, + sigmapluscand::CollisionIdCheck, + sigmapluscand::IsSignal, sigmapluscand::IsProtonFromSigma, sigmapluscand::IsPhotonFromSigma, + sigmapluscand::DecayRadiusMC, sigmapluscand::MassMC, + sigmapluscand::PxSigmaPlusMC, sigmapluscand::PySigmaPlusMC, sigmapluscand::PzSigmaPlusMC, + + // dynamic columns + sigmapluscand::PxSigmaPlus, + sigmapluscand::PySigmaPlus, + sigmapluscand::PzSigmaPlus, + sigmapluscand::PtSigmaPlus, + sigmapluscand::MassSigmaPlus, + sigmapluscand::PtSigmaPlusMC, + sigmapluscand::YSigmaPlusMC); + } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFKINKDECAYTABLES_H_ diff --git a/PWGLF/DataModel/LFLambda1405Table.h b/PWGLF/DataModel/LFLambda1405Table.h index be61dc083bc..571399d6b5a 100644 --- a/PWGLF/DataModel/LFLambda1405Table.h +++ b/PWGLF/DataModel/LFLambda1405Table.h @@ -9,11 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// /// \file LFLambda1405Tables.h /// \brief Slim tables for Lambda(1405) candidates -/// \author Francesco Mazzaschi /// +/// \author Francesco Mazzaschi #ifndef PWGLF_DATAMODEL_LFLAMBDA1405TABLE_H_ #define PWGLF_DATAMODEL_LFLAMBDA1405TABLE_H_ @@ -29,6 +28,7 @@ namespace lambda1405 DECLARE_SOA_COLUMN(Px, px, float); //! Px of the candidate DECLARE_SOA_COLUMN(Py, py, float); //! Py of the candidate DECLARE_SOA_COLUMN(Pz, pz, float); //! Pz of the candidate +DECLARE_SOA_COLUMN(Pt, pt, float); //! Pt of the candidate DECLARE_SOA_COLUMN(Mass, mass, float); //! Invariant mass of the candidate DECLARE_SOA_COLUMN(MassXi1530, massXi1530, float); //! Invariant mass of the Xi(1530) candidate DECLARE_SOA_COLUMN(SigmaMinusMass, sigmaMinusMass, float); //! Invariant mass of the Sigma- candidate @@ -43,16 +43,64 @@ DECLARE_SOA_COLUMN(NSigmaTPCPiKink, nSigmaTPCPiKink, float); //! Number of sigma DECLARE_SOA_COLUMN(NSigmaTOFPiKink, nSigmaTOFPiKink, float); //! Number of sigmas for the pion candidate from Sigma kink in TOF DECLARE_SOA_COLUMN(NSigmaTPCPrKink, nSigmaTPCPrKink, float); //! Number of sigmas for the proton candidate from Sigma kink in TPC DECLARE_SOA_COLUMN(NSigmaTOFPrKink, nSigmaTOFPrKink, float); //! Number of sigmas for the proton candidate from Sigma kink in TOF -DECLARE_SOA_COLUMN(DCAKinkDauToPV, dcaKinkDauToPV, float); //! DCA of the kink daughter to the primary vertex +DECLARE_SOA_COLUMN(DcaKinkDauToPv, dcaKinkDauToPv, float); //! DCA of the kink daughter to the primary vertex +DECLARE_SOA_COLUMN(DcaSigmaToPv, dcaSigmaToPv, float); //! DCA of the sigma to the primary vertex +DECLARE_SOA_COLUMN(KinkChi2PCA, kinkChi2PCA, float); //! Chi2 DCA fitter at the PCA DECLARE_SOA_COLUMN(NSigmaTPCPiDau, nSigmaTPCPiDau, float); //! Number of sigmas for the lambda1405 pion daughter in TPC DECLARE_SOA_COLUMN(NSigmaTOFPiDau, nSigmaTOFPiDau, float); //! Number of sigmas for the lambda1405 pion daughter in TOF +// Event properties +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Centrality of the candidate +DECLARE_SOA_COLUMN(Occupancy, occupancy, float); //! Occupancy of the candidate +DECLARE_SOA_COLUMN(PvContrib, pvContrib, float); //! Number of primary vertex contributors + +// Flow columns +DECLARE_SOA_COLUMN(ScalarProd, scalarProd, float); //! Scalar product of the candidate + // MC Columns DECLARE_SOA_COLUMN(PtMC, ptMC, float); //! pT of the candidate in MC DECLARE_SOA_COLUMN(MassMC, massMC, float); //! Invariant mass of the candidate in MC DECLARE_SOA_COLUMN(SigmaPdgCode, sigmaPdgCode, int); //! PDG code of the Sigma daughter DECLARE_SOA_COLUMN(KinkDauPdgCode, kinkDauPdgCode, int); //! PDG code of the kink daughter +// Sigma efficiency MC columns +DECLARE_SOA_COLUMN(PxSigma, pxSigma, float); //! Px of the sigma candidate +DECLARE_SOA_COLUMN(PySigma, pySigma, float); //! Py of the sigma candidate +DECLARE_SOA_COLUMN(PzSigma, pzSigma, float); //! Pz of the sigma candidate +DECLARE_SOA_COLUMN(MassSigma, massSigma, float); //! Mass of the sigma candidate +DECLARE_SOA_COLUMN(DeltaPxSigma, deltaPxSigma, float); //! Gen-reco diff of sigma p_x +DECLARE_SOA_COLUMN(DeltaPySigma, deltaPySigma, float); //! Gen-reco diff of sigma p_y +DECLARE_SOA_COLUMN(DeltaPzSigma, deltaPzSigma, float); //! Gen-reco diff of sigma p_z +DECLARE_SOA_COLUMN(DeltaPtSigma, deltaPtSigma, float); //! Gen-reco diff of sigma p_z +DECLARE_SOA_COLUMN(DeltaRadiusSigma, deltaRadiusSigma, float); //! Gen-reco diff of sigma radius +DECLARE_SOA_COLUMN(DeltaMassSigma, deltaMassSigma, float); //! Gen-reco diff of sigma mass +DECLARE_SOA_COLUMN(GenPhiSigma, genPhiSigma, float); //! Gen-reco diff of sigma phi +DECLARE_SOA_COLUMN(GenEtaSigma, genEtaSigma, float); //! Gen-reco diff of sigma eta +DECLARE_SOA_COLUMN(DeltaPxSigmaRecalc, deltaPxSigmaRecalc, float); //! reco recalc-original diff of sigma p_x +DECLARE_SOA_COLUMN(DeltaPySigmaRecalc, deltaPySigmaRecalc, float); //! reco recalc-original diff of sigma p_y +DECLARE_SOA_COLUMN(DeltaPzSigmaRecalc, deltaPzSigmaRecalc, float); //! reco recalc-original diff of sigma p_z +DECLARE_SOA_COLUMN(AlphaAPSigmaRecalc, alphaAPSigmaRecalc, float); //! Alpha of the Sigma +DECLARE_SOA_COLUMN(QtAPSigmaRecalc, qtAPSigmaRecalc, float); //! qT of the Sigma +DECLARE_SOA_COLUMN(PxKinkDaug, pxKinkDaug, float); //! Px of the sigma candidate +DECLARE_SOA_COLUMN(PyKinkDaug, pyKinkDaug, float); //! Py of the sigma candidate +DECLARE_SOA_COLUMN(PzKinkDaug, pzKinkDaug, float); //! Pz of the sigma candidate +DECLARE_SOA_COLUMN(PtKinkDaug, ptKinkDaug, float); //! Pt of the sigma candidate +DECLARE_SOA_COLUMN(DeltaPxKinkDaug, deltaPxKinkDaug, float); //! Gen-reco diff of kink daughter p_x +DECLARE_SOA_COLUMN(DeltaPyKinkDaug, deltaPyKinkDaug, float); //! Gen-reco diff of kink daughter p_y +DECLARE_SOA_COLUMN(DeltaPzKinkDaug, deltaPzKinkDaug, float); //! Gen-reco diff of kink daughter p_z +DECLARE_SOA_COLUMN(DeltaPtKinkDaug, deltaPtKinkDaug, float); //! Gen-reco diff of kink daughter p_t +DECLARE_SOA_COLUMN(GenPhiKinkDaug, genPhiKinkDaug, float); //! Gen-reco diff of kink daughter phi +DECLARE_SOA_COLUMN(GenEtaKinkDaug, genEtaKinkDaug, float); //! Gen-reco diff of kink daughter eta +DECLARE_SOA_COLUMN(XKinkVtx, xKinkVtx, float); //! X of kink vertex +DECLARE_SOA_COLUMN(YKinkVtx, yKinkVtx, float); //! Y of kink vertex +DECLARE_SOA_COLUMN(ZKinkVtx, zKinkVtx, float); //! Z of kink vertex +DECLARE_SOA_COLUMN(DeltaXKinkVtx, deltaXKinkVtx, float); //! Gen-reco diff of X of kink vertex +DECLARE_SOA_COLUMN(DeltaYKinkVtx, deltaYKinkVtx, float); //! Gen-reco diff of Y of kink vertex +DECLARE_SOA_COLUMN(DeltaZKinkVtx, deltaZKinkVtx, float); //! Gen-reco diff of Z of kink vertex + +// Event mixing +DECLARE_SOA_COLUMN(PoolBin, poolBin, int); //! Signed pT of the Sigma daughter for mixed events + } // namespace lambda1405 DECLARE_SOA_TABLE(Lambda1405Cands, "AOD", "LAMBDA1405", @@ -64,8 +112,10 @@ DECLARE_SOA_TABLE(Lambda1405Cands, "AOD", "LAMBDA1405", lambda1405::PtKink, lambda1405::NSigmaTPCPiKink, lambda1405::NSigmaTOFPiKink, lambda1405::NSigmaTPCPrKink, lambda1405::NSigmaTOFPrKink, - lambda1405::DCAKinkDauToPV, - lambda1405::NSigmaTPCPiDau, lambda1405::NSigmaTOFPiDau); + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, + lambda1405::NSigmaTPCPiDau, lambda1405::NSigmaTOFPiDau, + lambda1405::ScalarProd, + lambda1405::Centrality, lambda1405::Occupancy, lambda1405::PoolBin); DECLARE_SOA_TABLE(Lambda1405CandsMC, "AOD", "MCLAMBDA1405", o2::soa::Index<>, @@ -76,9 +126,35 @@ DECLARE_SOA_TABLE(Lambda1405CandsMC, "AOD", "MCLAMBDA1405", lambda1405::PtKink, lambda1405::NSigmaTPCPiKink, lambda1405::NSigmaTOFPiKink, lambda1405::NSigmaTPCPrKink, lambda1405::NSigmaTOFPrKink, - lambda1405::DCAKinkDauToPV, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, lambda1405::NSigmaTPCPiDau, lambda1405::NSigmaTOFPiDau, - lambda1405::PtMC, lambda1405::MassMC, lambda1405::SigmaPdgCode, lambda1405::KinkDauPdgCode); + lambda1405::PtMC, lambda1405::MassMC, lambda1405::SigmaPdgCode, lambda1405::KinkDauPdgCode, + lambda1405::Centrality, lambda1405::Occupancy); + +DECLARE_SOA_TABLE(Lambda1405SigmaEffMC, "AOD", "MCL1405SIGEFF", + o2::soa::Index<>, + lambda1405::PxSigma, lambda1405::DeltaPxSigma, + lambda1405::PySigma, lambda1405::DeltaPySigma, + lambda1405::PzSigma, lambda1405::DeltaPzSigma, + lambda1405::MassSigma, lambda1405::DeltaMassSigma, + lambda1405::DeltaPxSigmaRecalc, + lambda1405::DeltaPySigmaRecalc, + lambda1405::DeltaPzSigmaRecalc, + lambda1405::GenPhiSigma, + lambda1405::GenEtaSigma, + lambda1405::PxKinkDaug, lambda1405::DeltaPxKinkDaug, + lambda1405::PyKinkDaug, lambda1405::DeltaPyKinkDaug, + lambda1405::PzKinkDaug, lambda1405::DeltaPzKinkDaug, + lambda1405::GenPhiKinkDaug, + lambda1405::GenEtaKinkDaug, + lambda1405::XKinkVtx, lambda1405::DeltaXKinkVtx, + lambda1405::YKinkVtx, lambda1405::DeltaYKinkVtx, + lambda1405::ZKinkVtx, lambda1405::DeltaZKinkVtx, + lambda1405::AlphaAPSigma, lambda1405::QtAPSigma, + lambda1405::AlphaAPSigmaRecalc, lambda1405::QtAPSigmaRecalc, + lambda1405::DcaKinkDauToPv, lambda1405::DcaSigmaToPv, lambda1405::KinkChi2PCA, + lambda1405::SigmaPdgCode, lambda1405::KinkDauPdgCode, + lambda1405::Centrality, lambda1405::Occupancy, lambda1405::PvContrib); } // namespace o2::aod diff --git a/PWGLF/DataModel/LFLithium4Tables.h b/PWGLF/DataModel/LFLithium4Tables.h deleted file mode 100644 index 99700e76ea0..00000000000 --- a/PWGLF/DataModel/LFLithium4Tables.h +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file LFLithium4Tables.h -/// \brief Slim tables for Lithium4 -/// - -#ifndef PWGLF_DATAMODEL_LFLITHIUM4TABLES_H_ -#define PWGLF_DATAMODEL_LFLITHIUM4TABLES_H_ - -#include - -#include - -namespace o2::aod -{ -namespace Lithium4TablesNS -{ - -DECLARE_SOA_COLUMN(PtHe3, ptHe3, float); -DECLARE_SOA_COLUMN(EtaHe3, etaHe3, float); -DECLARE_SOA_COLUMN(PhiHe3, phiHe3, float); -DECLARE_SOA_COLUMN(PtPr, ptPr, float); -DECLARE_SOA_COLUMN(EtaPr, etaPr, float); -DECLARE_SOA_COLUMN(PhiPr, phiPr, float); - -DECLARE_SOA_COLUMN(DCAxyHe3, dcaxyHe3, float); -DECLARE_SOA_COLUMN(DCAzHe3, dcazHe3, float); -DECLARE_SOA_COLUMN(DCAxyPr, dcaxyPr, float); -DECLARE_SOA_COLUMN(DCAzPr, dcazPr, float); - -DECLARE_SOA_COLUMN(SignalTPCHe3, signalTPCHe3, float); -DECLARE_SOA_COLUMN(InnerParamTPCHe3, innerParamTPCHe3, float); -DECLARE_SOA_COLUMN(SignalTPCPr, signalTPCPr, float); -DECLARE_SOA_COLUMN(InnerParamTPCPr, innerParamTPCPr, float); -DECLARE_SOA_COLUMN(NClsTPCHe3, nClsTPCHe3, uint8_t); -DECLARE_SOA_COLUMN(NSigmaTPCHe3, nSigmaTPCHe3, float); -DECLARE_SOA_COLUMN(NSigmaTPCPr, nSigmaTOFPr, float); -DECLARE_SOA_COLUMN(Chi2TPCHe3, chi2TPCHe3, float); -DECLARE_SOA_COLUMN(Chi2TPCPr, chi2TPCPr, float); -DECLARE_SOA_COLUMN(MassTOFHe3, massTOFHe3, float); -DECLARE_SOA_COLUMN(MassTOFPr, massTOFPr, float); -DECLARE_SOA_COLUMN(PIDtrkHe3, pidTrkHe3, uint32_t); -DECLARE_SOA_COLUMN(PIDtrkPr, pidTrkPr, uint32_t); - -DECLARE_SOA_COLUMN(ItsClusterSizeHe3, itsClusterSizeHe3, uint32_t); -DECLARE_SOA_COLUMN(ItsClusterSizePr, itsClusterSizePr, uint32_t); - -DECLARE_SOA_COLUMN(SharedClustersHe3, sharedClustersHe3, uint8_t); -DECLARE_SOA_COLUMN(SharedClustersPr, sharedClustersPr, uint8_t); - -DECLARE_SOA_COLUMN(IsBkgLS, isBkgLS, bool); -DECLARE_SOA_COLUMN(IsBkgEM, isBkgEM, bool); - -DECLARE_SOA_COLUMN(PtMCHe3, ptMCHe3, float); -DECLARE_SOA_COLUMN(EtaMCHe3, etaMCHe3, float); -DECLARE_SOA_COLUMN(PhiMCHe3, phiMCHe3, float); -DECLARE_SOA_COLUMN(PtMCPr, ptMCPr, float); -DECLARE_SOA_COLUMN(EtaMCPr, etaMCPr, float); -DECLARE_SOA_COLUMN(PhiMCPr, phiMCPr, float); -DECLARE_SOA_COLUMN(SignedPtMC, signedPtMC, float); -DECLARE_SOA_COLUMN(MassMC, massMC, float); - -DECLARE_SOA_COLUMN(Multiplicity, multiplicity, uint16_t); -DECLARE_SOA_COLUMN(CentralityFT0C, centFT0C, float); -DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0C, float); - -} // namespace Lithium4TablesNS - -DECLARE_SOA_TABLE(Lithium4Table, "AOD", "LITHIUM4TABLE", - Lithium4TablesNS::PtHe3, - Lithium4TablesNS::EtaHe3, - Lithium4TablesNS::PhiHe3, - Lithium4TablesNS::PtPr, - Lithium4TablesNS::EtaPr, - Lithium4TablesNS::PhiPr, - Lithium4TablesNS::DCAxyHe3, - Lithium4TablesNS::DCAzHe3, - Lithium4TablesNS::DCAxyPr, - Lithium4TablesNS::DCAzPr, - Lithium4TablesNS::SignalTPCHe3, - Lithium4TablesNS::InnerParamTPCHe3, - Lithium4TablesNS::SignalTPCPr, - Lithium4TablesNS::InnerParamTPCPr, - Lithium4TablesNS::NClsTPCHe3, - Lithium4TablesNS::NSigmaTPCHe3, - Lithium4TablesNS::NSigmaTPCPr, - Lithium4TablesNS::Chi2TPCHe3, - Lithium4TablesNS::Chi2TPCPr, - Lithium4TablesNS::MassTOFHe3, - Lithium4TablesNS::MassTOFPr, - Lithium4TablesNS::PIDtrkHe3, - Lithium4TablesNS::PIDtrkPr, - Lithium4TablesNS::ItsClusterSizeHe3, - Lithium4TablesNS::ItsClusterSizePr, - Lithium4TablesNS::SharedClustersHe3, - Lithium4TablesNS::SharedClustersPr, - Lithium4TablesNS::IsBkgLS, - Lithium4TablesNS::IsBkgEM) -DECLARE_SOA_TABLE(Lithium4TableMC, "AOD", "LITHIUM4TABLEMC", - Lithium4TablesNS::PtMCHe3, - Lithium4TablesNS::EtaMCHe3, - Lithium4TablesNS::PhiMCHe3, - Lithium4TablesNS::PtMCPr, - Lithium4TablesNS::EtaMCPr, - Lithium4TablesNS::PhiMCPr, - Lithium4TablesNS::SignedPtMC, - Lithium4TablesNS::MassMC) -DECLARE_SOA_TABLE(Lithium4Mult, "AOD", "LITHIUM4MULT", - Lithium4TablesNS::Multiplicity, - Lithium4TablesNS::CentralityFT0C, - Lithium4TablesNS::MultiplicityFT0C) - -} // namespace o2::aod - -#endif // PWGLF_DATAMODEL_LFLITHIUM4TABLES_H_ diff --git a/PWGLF/DataModel/LFNonPromptCascadeTables.h b/PWGLF/DataModel/LFNonPromptCascadeTables.h index d9d2c30ac97..f8a532448f4 100644 --- a/PWGLF/DataModel/LFNonPromptCascadeTables.h +++ b/PWGLF/DataModel/LFNonPromptCascadeTables.h @@ -63,6 +63,9 @@ DECLARE_SOA_COLUMN(PionDCAz, pionDCAz, float); DECLARE_SOA_COLUMN(BachDCAxy, bachDCAxy, float); DECLARE_SOA_COLUMN(BachDCAz, bachDCAz, float); +DECLARE_SOA_COLUMN(CascDCADaughters, cascDCADaughters, float); // DCA between V0 and bachelor +DECLARE_SOA_COLUMN(V0DCADaughters, v0DCADaughters, float); // DCA between proton and pion + DECLARE_SOA_COLUMN(CascCosPA, casccosPA, float); DECLARE_SOA_COLUMN(V0CosPA, v0cosPA, float); @@ -121,13 +124,20 @@ DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); DECLARE_SOA_COLUMN(MultNTracksGlobal, multNTracksGlobal, int); +DECLARE_SOA_COLUMN(MultNTracksNP, multNTracksNP, int); DECLARE_SOA_COLUMN(ToiMask, toiMask, uint32_t); DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(NoSameBunchPileup, noSameBunchPileup, bool); DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(PtGen, ptGen, float); DECLARE_SOA_COLUMN(PtRec, ptRec, float); +DECLARE_SOA_COLUMN(EtaGen, etaGen, float); +DECLARE_SOA_COLUMN(EtaRec, etaRec, float); DECLARE_SOA_COLUMN(MultGen, multGen, int); +DECLARE_SOA_COLUMN(MultGenFT0, multGenFT0, int); +DECLARE_SOA_COLUMN(McCollisionId, mcCollisionId, int); +DECLARE_SOA_COLUMN(RecoCollisionId, recoCollisionId, int); +DECLARE_SOA_COLUMN(McParticleId, mcParticleId, int); } // namespace NPCascadeTable DECLARE_SOA_TABLE(NPCascTable, "AOD", "NPCASCTABLE", @@ -160,6 +170,8 @@ DECLARE_SOA_TABLE(NPCascTable, "AOD", "NPCASCTABLE", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -228,6 +240,8 @@ DECLARE_SOA_TABLE(NPCascTableNT, "AOD", "NPCASCTABLENT", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -302,6 +316,8 @@ DECLARE_SOA_TABLE(NPCascTableMC, "AOD", "NPCASCTABLEMC", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -389,6 +405,8 @@ DECLARE_SOA_TABLE(NPCascTableMCNT, "AOD", "NPCASCTABLEMCNT", NPCascadeTable::PionDCAz, NPCascadeTable::BachDCAxy, NPCascadeTable::BachDCAz, + NPCascadeTable::CascDCADaughters, + NPCascadeTable::V0DCADaughters, NPCascadeTable::CascCosPA, NPCascadeTable::V0CosPA, NPCascadeTable::MassXi, @@ -452,21 +470,41 @@ DECLARE_SOA_TABLE(NPCascTableGen, "AOD", "NPCASCTABLEGen", NPCascadeTable::IsFromCharm, NPCascadeTable::MotherDecayDaughters); DECLARE_SOA_TABLE(NPMCChargedTable, "AOD", "NPMCChargedTABLE", + NPCascadeTable::McCollisionId, + NPCascadeTable::RecoCollisionId, + NPCascadeTable::McParticleId, NPCascadeTable::PtGen, NPCascadeTable::PtRec, - NPCascadeTable::MultNTracksGlobal, - NPCascadeTable::MultGen); + NPCascadeTable::EtaGen, + NPCascadeTable::EtaRec, + NPCascadeTable::MultNTracksNP, + NPCascadeTable::MultGen, + NPCascadeTable::MultGenFT0, + NPCascadeTable::MultFT0M, + NPCascadeTable::CentFT0M); +DECLARE_SOA_TABLE(NPMCRecoCollisionAssoc, "AOD", "NPMCRecoCollAssn", + NPCascadeTable::McCollisionId, + NPCascadeTable::RecoCollisionId, + NPCascadeTable::MultNTracksNP, + NPCascadeTable::MultGen, + NPCascadeTable::MultGenFT0, + NPCascadeTable::MultFT0M, + NPCascadeTable::CentFT0M, + NPCascadeTable::NoSameBunchPileup); DECLARE_SOA_TABLE(NPCollisionTable, "AOD", "NPCollisionTABLE", NPCascadeTable::RunNumber, NPCascadeTable::GlobalBC, aod::collision::NumContrib, NPCascadeTable::MultNTracksGlobal, + NPCascadeTable::MultNTracksNP, NPCascadeTable::CentFT0M, - NPCascadeTable::MultFT0M); + NPCascadeTable::MultFT0M, + NPCascadeTable::NoSameBunchPileup); DECLARE_SOA_INDEX_COLUMN_FULL(NPCollision, npCollision, int32_t, NPCollisionTable, ""); DECLARE_SOA_TABLE(NPRecoChargedCand, "AOD", "NPRecoChargedCand", NPCollisionId, - NPCascadeTable::PtRec); + NPCascadeTable::PtRec, + NPCascadeTable::EtaRec); } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFNONPROMPTCASCADETABLES_H_ diff --git a/PWGLF/DataModel/LFNucleiTables.h b/PWGLF/DataModel/LFNucleiTables.h index f74d3b8ed9d..03366fbd0e6 100644 --- a/PWGLF/DataModel/LFNucleiTables.h +++ b/PWGLF/DataModel/LFNucleiTables.h @@ -33,7 +33,12 @@ DECLARE_SOA_COLUMN(IsEventReject, isEventReject, int); DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(CentFV0M, centFV0M, float); DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); -DECLARE_SOA_DYNAMIC_COLUMN(Selection_Bit, selection_bit, //! Dummy +DECLARE_SOA_COLUMN(MultNTracksPVeta1, multNTracksPVeta1, int); +DECLARE_SOA_DYNAMIC_COLUMN(IsInelGt0, isInelGt0, // is INEL > 0 + [](int multPveta1) -> bool { return multPveta1 > 0; }); +DECLARE_SOA_DYNAMIC_COLUMN(IsInelGt1, isInelGt1, // is INEL > 1 + [](int multPveta1) -> bool { return multPveta1 > 1; }); +DECLARE_SOA_DYNAMIC_COLUMN(Selection_bit, selection_bit, // o2-linter: disable=name/o2-column (temporary fix) [](o2::aod::evsel::EventSelectionFlags /*v*/) -> bool { return true; }); } // namespace fullEvent DECLARE_SOA_TABLE(LfNuclEvents, "AOD", "LFNUCLEvent", @@ -44,66 +49,69 @@ DECLARE_SOA_TABLE(LfNuclEvents, "AOD", "LFNUCLEvent", collision::PosZ, fullEvent::CentFV0M, fullEvent::CentFT0M, + fullEvent::MultNTracksPVeta1, + fullEvent::IsInelGt0, + fullEvent::IsInelGt1, fullEvent::IsEventReject, fullEvent::RunNumber, - fullEvent::Selection_Bit<>); + fullEvent::Selection_bit<>); using LfNuclEvent = LfNuclEvents::iterator; namespace full { DECLARE_SOA_INDEX_COLUMN(LfNuclEvent, lfNuclEvent); DECLARE_SOA_COLUMN(Pt, pt, float); -DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float pt, float eta) -> float { return pt * cosh(eta); }); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float pt, float eta) -> float { return pt * std::cosh(eta); }); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Sign, sign, int16_t); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(IsPVContributor, isPVContributor, bool); DECLARE_SOA_DYNAMIC_COLUMN(Rapidity, rapidity, [](float pt, float eta, float mass) -> float { - const auto p = pt * cosh(eta); - const auto pz = pt * sinh(eta); - const auto energy = sqrt(p * p + mass * mass); - return 0.5f * log((energy + pz) / (energy - pz)); + const auto p = pt * std::cosh(eta); + const auto pz = pt * std::sinh(eta); + const auto energy = std::sqrt(p * p + mass * mass); + return 0.5f * std::log((energy + pz) / (energy - pz)); }); // ITS -DECLARE_SOA_COLUMN(ITSClusterSizes, itsClusterSizes, uint32_t); //! ITS cluster sizes per layer +DECLARE_SOA_COLUMN(ItsClusterSizes, itsClusterSizes, uint32_t); //! ITS cluster sizes per layer // TPC -DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, float); -DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, float); -DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, float); -DECLARE_SOA_COLUMN(TPCNSigmaDe, tpcNSigmaDe, float); -DECLARE_SOA_COLUMN(TPCNSigmaTr, tpcNSigmaTr, float); -DECLARE_SOA_COLUMN(TPCNSigmaHe, tpcNSigmaHe, float); -DECLARE_SOA_COLUMN(TPCNSigmaAl, tpcNSigmaAl, float); +DECLARE_SOA_COLUMN(TpcNSigmaPi, tpcNSigmaPi, float); +DECLARE_SOA_COLUMN(TpcNSigmaKa, tpcNSigmaKa, float); +DECLARE_SOA_COLUMN(TpcNSigmaPr, tpcNSigmaPr, float); +DECLARE_SOA_COLUMN(TpcNSigmaDe, tpcNSigmaDe, float); +DECLARE_SOA_COLUMN(TpcNSigmaTr, tpcNSigmaTr, float); +DECLARE_SOA_COLUMN(TpcNSigmaHe, tpcNSigmaHe, float); +DECLARE_SOA_COLUMN(TpcNSigmaAl, tpcNSigmaAl, float); // TOF -DECLARE_SOA_COLUMN(TOFNSigmaPi, tofNSigmaPi, float); -DECLARE_SOA_COLUMN(TOFNSigmaKa, tofNSigmaKa, float); -DECLARE_SOA_COLUMN(TOFNSigmaPr, tofNSigmaPr, float); -DECLARE_SOA_COLUMN(TOFNSigmaDe, tofNSigmaDe, float); -DECLARE_SOA_COLUMN(TOFNSigmaTr, tofNSigmaTr, float); -DECLARE_SOA_COLUMN(TOFNSigmaHe, tofNSigmaHe, float); -DECLARE_SOA_COLUMN(TOFNSigmaAl, tofNSigmaAl, float); -DECLARE_SOA_COLUMN(TPCExpSignalDiffPr, tpcExpSignalDiffPr, float); -DECLARE_SOA_COLUMN(TPCExpSignalDiffDe, tpcExpSignalDiffDe, float); -DECLARE_SOA_COLUMN(TPCExpSignalDiffHe, tpcExpSignalDiffHe, float); -DECLARE_SOA_COLUMN(TOFExpSignalDiffPr, tofExpSignalDiffPr, float); -DECLARE_SOA_COLUMN(TOFExpSignalDiffDe, tofExpSignalDiffDe, float); -DECLARE_SOA_COLUMN(TOFExpSignalDiffHe, tofExpSignalDiffHe, float); +DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, float); +DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, float); +DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, float); +DECLARE_SOA_COLUMN(TofNSigmaDe, tofNSigmaDe, float); +DECLARE_SOA_COLUMN(TofNSigmaTr, tofNSigmaTr, float); +DECLARE_SOA_COLUMN(TofNSigmaHe, tofNSigmaHe, float); +DECLARE_SOA_COLUMN(TofNSigmaAl, tofNSigmaAl, float); +DECLARE_SOA_COLUMN(TpcExpSignalDiffPr, tpcExpSignalDiffPr, float); +DECLARE_SOA_COLUMN(TpcExpSignalDiffDe, tpcExpSignalDiffDe, float); +DECLARE_SOA_COLUMN(TpcExpSignalDiffHe, tpcExpSignalDiffHe, float); +DECLARE_SOA_COLUMN(TofExpSignalDiffPr, tofExpSignalDiffPr, float); +DECLARE_SOA_COLUMN(TofExpSignalDiffDe, tofExpSignalDiffDe, float); +DECLARE_SOA_COLUMN(TofExpSignalDiffHe, tofExpSignalDiffHe, float); DECLARE_SOA_COLUMN(IsEvTimeTOF, isEvTimeTOF, bool); DECLARE_SOA_COLUMN(IsEvTimeT0AC, isEvTimeT0AC, bool); DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); DECLARE_SOA_COLUMN(HasTRD, hasTRD, bool); DECLARE_SOA_COLUMN(DcaXY, dcaXY, float); DECLARE_SOA_COLUMN(DcaZ, dcaZ, float); -DECLARE_SOA_COLUMN(TPCInnerParam, tpcInnerParam, float); -DECLARE_SOA_COLUMN(TOFExpMom, tofExpMom, float); -DECLARE_SOA_COLUMN(TPCSignal, tpcSignal, float); +DECLARE_SOA_COLUMN(TpcInnerParam, tpcInnerParam, float); +DECLARE_SOA_COLUMN(TofExpMom, tofExpMom, float); +DECLARE_SOA_COLUMN(TpcSignal, tpcSignal, float); DECLARE_SOA_COLUMN(Beta, beta, float); // TPC and ITS QA -DECLARE_SOA_COLUMN(PIDForTracking, pidForTracking, uint8_t); -DECLARE_SOA_COLUMN(ITSNCls, itsNCls, int16_t); -DECLARE_SOA_COLUMN(TPCChi2Ncl, tpcChi2NCl, float); -DECLARE_SOA_COLUMN(ITSChi2NCl, itsChi2NCl, float); +DECLARE_SOA_COLUMN(PidForTracking, pidForTracking, uint8_t); +DECLARE_SOA_COLUMN(ItsNCls, itsNCls, int16_t); +DECLARE_SOA_COLUMN(TpcChi2NCl, tpcChi2NCl, float); +DECLARE_SOA_COLUMN(ItsChi2NCl, itsChi2NCl, float); DECLARE_SOA_COLUMN(TpcPassed, tpcPassed, bool); DECLARE_SOA_COLUMN(ItsPassed, itsPassed, bool); DECLARE_SOA_COLUMN(FakeHitsFlag, fakeHitsFlag, bool); @@ -116,79 +124,71 @@ DECLARE_SOA_COLUMN(GetProcess, getProcess, int); } // namespace full namespace dummy { -DECLARE_SOA_DYNAMIC_COLUMN(TPCNSigmaPi, tpcNSigmaPi, +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaPi, tpcNSigmaPi, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCNSigmaKa, tpcNSigmaKa, +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaKa, tpcNSigmaKa, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCNSigmaPr, tpcNSigmaPr, +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaPr, tpcNSigmaPr, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCNSigmaTr, tpcNSigmaTr, +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaTr, tpcNSigmaTr, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCNSigmaAl, tpcNSigmaAl, +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaAl, tpcNSigmaAl, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFNSigmaPi, tofNSigmaPi, +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaPi, tofNSigmaPi, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFNSigmaKa, tofNSigmaKa, +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaKa, tofNSigmaKa, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFNSigmaPr, tofNSigmaPr, +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaPr, tofNSigmaPr, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFNSigmaTr, tofNSigmaTr, +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaTr, tofNSigmaTr, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFNSigmaAl, tofNSigmaAl, +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaAl, tofNSigmaAl, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffPr, tpcExpSignalDiffPr, +DECLARE_SOA_DYNAMIC_COLUMN(TpcExpSignalDiffPr, tpcExpSignalDiffPr, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffDe, tpcExpSignalDiffDe, +DECLARE_SOA_DYNAMIC_COLUMN(TpcExpSignalDiffDe, tpcExpSignalDiffDe, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TPCExpSignalDiffHe, tpcExpSignalDiffHe, +DECLARE_SOA_DYNAMIC_COLUMN(TpcExpSignalDiffHe, tpcExpSignalDiffHe, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffPr, tofExpSignalDiffPr, +DECLARE_SOA_DYNAMIC_COLUMN(TofExpSignalDiffPr, tofExpSignalDiffPr, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffDe, tofExpSignalDiffDe, +DECLARE_SOA_DYNAMIC_COLUMN(TofExpSignalDiffDe, tofExpSignalDiffDe, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFExpSignalDiffHe, tofExpSignalDiffHe, +DECLARE_SOA_DYNAMIC_COLUMN(TofExpSignalDiffHe, tofExpSignalDiffHe, [](bool /*b*/) -> float { return 0.f; }); -DECLARE_SOA_DYNAMIC_COLUMN(TOFExpMom, tofExpMom, +DECLARE_SOA_DYNAMIC_COLUMN(TofExpMom, tofExpMom, [](bool /*b*/) -> float { return 0.f; }); } // namespace dummy -/* -namespace fullMC -{ -DECLARE_SOA_INDEX_COLUMN(LfNuclEvent, lfCandNucleusFullEvent); -DECLARE_SOA_COLUMN(PdgCode, pdgCode, int); -} -*/ - DECLARE_SOA_TABLE(LfCandNucleus, "AOD", "LFNUCL", o2::soa::Index<>, full::LfNuclEventId, full::DcaXY, full::DcaZ, - full::TPCNSigmaDe, full::TPCNSigmaHe, - full::TOFNSigmaDe, full::TOFNSigmaHe, + full::TpcNSigmaDe, full::TpcNSigmaHe, + full::TofNSigmaDe, full::TofNSigmaHe, full::IsEvTimeTOF, full::IsEvTimeT0AC, full::HasTOF, full::HasTRD, - full::TPCInnerParam, + full::TpcInnerParam, full::Beta, - full::PIDForTracking, - full::TPCSignal, + full::PidForTracking, + full::TpcSignal, full::Pt, full::Eta, full::Phi, full::Sign, - full::ITSNCls, + full::ItsNCls, track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, - full::TPCChi2Ncl, - full::ITSChi2NCl, + full::TpcChi2NCl, + full::ItsChi2NCl, track::ITSClusterMap, full::IsPVContributor, full::P, full::Rapidity, - full::ITSClusterSizes, + full::ItsClusterSizes, track::TPCNClsFound, track::TPCNClsCrossedRows, track::TPCCrossedRowsOverFindableCls, @@ -197,37 +197,37 @@ DECLARE_SOA_TABLE_VERSIONED(LfCandNucleusDummy, "AOD", "LFNUCL", 1, o2::soa::Index<>, full::LfNuclEventId, full::DcaXY, full::DcaZ, - full::TPCNSigmaDe, full::TPCNSigmaHe, - full::TOFNSigmaDe, full::TOFNSigmaHe, + full::TpcNSigmaDe, full::TpcNSigmaHe, + full::TofNSigmaDe, full::TofNSigmaHe, full::IsEvTimeTOF, full::IsEvTimeT0AC, full::HasTOF, full::HasTRD, - full::TPCInnerParam, + full::TpcInnerParam, full::Beta, - full::PIDForTracking, - full::TPCSignal, + full::PidForTracking, + full::TpcSignal, full::Pt, full::Eta, full::Phi, full::Sign, - full::ITSNCls, + full::ItsNCls, track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, - full::TPCChi2Ncl, - full::ITSChi2NCl, + full::TpcChi2NCl, + full::ItsChi2NCl, track::ITSClusterMap, full::IsPVContributor, full::P, - full::ITSClusterSizes, - dummy::TPCNSigmaPi, dummy::TPCNSigmaKa, dummy::TPCNSigmaPr, - dummy::TPCNSigmaTr, dummy::TPCNSigmaAl, - dummy::TOFNSigmaPi, dummy::TOFNSigmaKa, dummy::TOFNSigmaPr, - dummy::TOFNSigmaTr, dummy::TOFNSigmaAl, - dummy::TPCExpSignalDiffPr, dummy::TPCExpSignalDiffDe, dummy::TPCExpSignalDiffHe, - dummy::TOFExpSignalDiffPr, dummy::TOFExpSignalDiffDe, dummy::TOFExpSignalDiffHe, - dummy::TOFExpMom, + full::ItsClusterSizes, + dummy::TpcNSigmaPi, dummy::TpcNSigmaKa, dummy::TpcNSigmaPr, + dummy::TpcNSigmaTr, dummy::TpcNSigmaAl, + dummy::TofNSigmaPi, dummy::TofNSigmaKa, dummy::TofNSigmaPr, + dummy::TofNSigmaTr, dummy::TofNSigmaAl, + dummy::TpcExpSignalDiffPr, dummy::TpcExpSignalDiffDe, dummy::TpcExpSignalDiffHe, + dummy::TofExpSignalDiffPr, dummy::TofExpSignalDiffDe, dummy::TofExpSignalDiffHe, + dummy::TofExpMom, full::Rapidity, track::TPCNClsFound, track::TPCNClsCrossedRows, @@ -235,13 +235,13 @@ DECLARE_SOA_TABLE_VERSIONED(LfCandNucleusDummy, "AOD", "LFNUCL", 1, track::TPCFoundOverFindableCls); DECLARE_SOA_TABLE(LfCandNucleusExtra, "AOD", "LFNUCLEXTRA", - full::TPCNSigmaPi, full::TPCNSigmaKa, full::TPCNSigmaPr, - full::TPCNSigmaTr, full::TPCNSigmaAl, - full::TOFNSigmaPi, full::TOFNSigmaKa, full::TOFNSigmaPr, - full::TOFNSigmaTr, full::TOFNSigmaAl, - full::TPCExpSignalDiffPr, full::TPCExpSignalDiffDe, full::TPCExpSignalDiffHe, - full::TOFExpSignalDiffPr, full::TOFExpSignalDiffDe, full::TOFExpSignalDiffHe, - full::TOFExpMom); + full::TpcNSigmaPi, full::TpcNSigmaKa, full::TpcNSigmaPr, + full::TpcNSigmaTr, full::TpcNSigmaAl, + full::TofNSigmaPi, full::TofNSigmaKa, full::TofNSigmaPr, + full::TofNSigmaTr, full::TofNSigmaAl, + full::TpcExpSignalDiffPr, full::TpcExpSignalDiffDe, full::TpcExpSignalDiffHe, + full::TofExpSignalDiffPr, full::TofExpSignalDiffDe, full::TofExpSignalDiffHe, + full::TofExpMom); DECLARE_SOA_TABLE(LfCandNucleusMC, "AOD", "LFNUCLMC", mcparticle::PdgCode, diff --git a/PWGLF/DataModel/LFOmega2012MlTables.h b/PWGLF/DataModel/LFOmega2012MlTables.h new file mode 100644 index 00000000000..e17bde5d569 --- /dev/null +++ b/PWGLF/DataModel/LFOmega2012MlTables.h @@ -0,0 +1,273 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file LFOmega2012MlTables.h +/// \brief Derived Omega(2012) training and audit tables, separate for the XiK0s and Xi1530K decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): Omega(2012)- -> Xi- K0S. Mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K-. +/// Only the input-object tables (events, cascades, V0s, tracks) are common; every event row belongs to one +/// decay mode (column omDecayMode), so the object rows of the two modes never mix either. +/// Event rows are written only for collisions with at least one written candidate. In reconstructed MC the +/// object MC labels and the pruned McParticles rows replace the source MC tables, which are not written. +/// +/// Cumulative pass bits (each bit requires all lower bits): +/// mode A: 1 loose (valid canonical candidate inside the rapidity window), 2 Xi selection, 4 K0s selection, +/// 8 Xi-K0s kinematic (opening-angle) cut; selected = 15. +/// mode B: 1 loose (valid canonical candidate inside the rapidity window), 2 Xi selection, 4 track quality of the +/// pion and the kaon, 8 TOF requirement and PID of the pion and the kaon, 16 Xi(1530) mass window; +/// selected = 31. +#ifndef PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ +#define PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ + +#include +#include + +#include + +namespace o2::aod +{ +namespace omega2012ml +{ +// Persisted relations target only these derived tables; source AO2D row numbers are scalar audit values. +// Events +DECLARE_SOA_COLUMN(OmDecayMode, omDecayMode, uint8_t); //! 0 XiK0s, 1 Xi1530K: the mode whose candidates reference this event row +DECLARE_SOA_COLUMN(OmRecoCollisionId, omRecoCollisionId, int64_t); //! row of the reduced ResoCollisions_001 collision in the input DF +DECLARE_SOA_COLUMN(OmPosZ, omPosZ, float); +DECLARE_SOA_COLUMN(OmBField, omBField, float); +DECLARE_SOA_COLUMN(OmCentrality, omCentrality, float); +DECLARE_SOA_COLUMN(OmMultiplicity, omMultiplicity, float); +DECLARE_SOA_COLUMN(OmRecINELgt0, omRecINELgt0, bool); +// Common object columns +DECLARE_SOA_COLUMN(OmSourceRow, omSourceRow, int64_t); //! row of the object in the input ResoCascades / ResoV0s table +DECLARE_SOA_COLUMN(OmPx, omPx, float); +DECLARE_SOA_COLUMN(OmPy, omPy, float); +DECLARE_SOA_COLUMN(OmPz, omPz, float); +DECLARE_SOA_COLUMN(OmV0CosPA, omV0CosPA, float); +DECLARE_SOA_COLUMN(OmV0DaughDCA, omV0DaughDCA, float); +DECLARE_SOA_COLUMN(OmDcaPosToPV, omDcaPosToPV, float); +DECLARE_SOA_COLUMN(OmDcaNegToPV, omDcaNegToPV, float); +DECLARE_SOA_COLUMN(OmDcaV0ToPV, omDcaV0ToPV, float); +DECLARE_SOA_COLUMN(OmV0Radius, omV0Radius, float); +DECLARE_SOA_COLUMN(OmMassLambda, omMassLambda, float); +DECLARE_SOA_COLUMN(OmDecayVtxX, omDecayVtxX, float); +DECLARE_SOA_COLUMN(OmDecayVtxY, omDecayVtxY, float); +DECLARE_SOA_COLUMN(OmDecayVtxZ, omDecayVtxZ, float); +DECLARE_SOA_COLUMN(OmTpcPosPi10, omTpcPosPi10, int8_t); //! TPC nSigma x10 of the positive daughter (pion hypothesis), as in the reduced tables +DECLARE_SOA_COLUMN(OmTpcPosKa10, omTpcPosKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcPosPr10, omTpcPosPr10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegPi10, omTpcNegPi10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegKa10, omTpcNegKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcNegPr10, omTpcNegPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofPosPi10, omTofPosPi10, int8_t); //! TOF nSigma x10 of the positive daughter (pion hypothesis), as in the reduced tables +DECLARE_SOA_COLUMN(OmTofPosKa10, omTofPosKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofPosPr10, omTofPosPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegPi10, omTofNegPi10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegKa10, omTofNegKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofNegPr10, omTofNegPr10, int8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsPos, omCrossedRowsPos, uint8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsNeg, omCrossedRowsNeg, uint8_t); +// Cascade-only columns +DECLARE_SOA_COLUMN(OmSign, omSign, int8_t); +DECLARE_SOA_COLUMN(OmMassXi, omMassXi, float); +DECLARE_SOA_COLUMN(OmCascCosPA, omCascCosPA, float); +DECLARE_SOA_COLUMN(OmCascDaughDCA, omCascDaughDCA, float); +DECLARE_SOA_COLUMN(OmDcaBachToPV, omDcaBachToPV, float); +DECLARE_SOA_COLUMN(OmDcaXYCascToPV, omDcaXYCascToPV, float); +DECLARE_SOA_COLUMN(OmDcaZCascToPV, omDcaZCascToPV, float); +DECLARE_SOA_COLUMN(OmCascRadius, omCascRadius, float); +DECLARE_SOA_COLUMN(OmTpcBachPi10, omTpcBachPi10, int8_t); +DECLARE_SOA_COLUMN(OmTpcBachKa10, omTpcBachKa10, int8_t); +DECLARE_SOA_COLUMN(OmTpcBachPr10, omTpcBachPr10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachPi10, omTofBachPi10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachKa10, omTofBachKa10, int8_t); +DECLARE_SOA_COLUMN(OmTofBachPr10, omTofBachPr10, int8_t); +DECLARE_SOA_COLUMN(OmCrossedRowsBach, omCrossedRowsBach, uint8_t); +// The daughter-ID arrays mirror the persistent ResoCascades / ResoV0s columns. +DECLARE_SOA_COLUMN(OmCascadeIndices, omCascadeIndices, int[3]); //! source track IDs of the cascade daughters (positive, negative, bachelor) +// V0-only columns +DECLARE_SOA_COLUMN(OmMassK0Short, omMassK0Short, float); +DECLARE_SOA_COLUMN(OmMassAntiLambda, omMassAntiLambda, float); +DECLARE_SOA_COLUMN(OmArmAlpha, omArmAlpha, float); +DECLARE_SOA_COLUMN(OmArmQt, omArmQt, float); +DECLARE_SOA_COLUMN(OmV0Indices, omV0Indices, int[2]); //! source track IDs of the V0 daughters (positive, negative) +// Track columns +DECLARE_SOA_COLUMN(OmSourceTrackId, omSourceTrackId, int64_t); //! trackId of the reduced micro track +DECLARE_SOA_COLUMN(OmPidPi, omPidPi, uint8_t); +DECLARE_SOA_COLUMN(OmPidKa, omPidKa, uint8_t); +DECLARE_SOA_COLUMN(OmPidPr, omPidPr, uint8_t); +DECLARE_SOA_COLUMN(OmSelectionFlags, omSelectionFlags, uint8_t); +DECLARE_SOA_COLUMN(OmTrackFlags, omTrackFlags, uint8_t); +DECLARE_SOA_COLUMN(OmCrossedRows, omCrossedRows, uint8_t); +DECLARE_SOA_COLUMN(OmItsClusterMap, omItsClusterMap, uint8_t); +// Candidate columns +DECLARE_SOA_COLUMN(OmMass, omMass, float); +DECLARE_SOA_COLUMN(OmPt, omPt, float); +DECLARE_SOA_COLUMN(OmY, omY, float); +DECLARE_SOA_COLUMN(OmEta, omEta, float); +DECLARE_SOA_COLUMN(OmPhi, omPhi, float); +DECLARE_SOA_COLUMN(OmOpeningAngle, omOpeningAngle, float); //! Xi-K0s opening angle alpha_oa of the kinematic cut +DECLARE_SOA_COLUMN(OmCharge, omCharge, int8_t); //! sign of the Xi +DECLARE_SOA_COLUMN(OmMassXiPi, omMassXiPi, float); +DECLARE_SOA_COLUMN(OmMassXiK, omMassXiK, float); +DECLARE_SOA_COLUMN(OmMassPiK, omMassPiK, float); +DECLARE_SOA_COLUMN(OmChargePattern, omChargePattern, uint8_t); //! 0 signal (pion opposite to the Xi, kaon equal), 1 wrong-sign pion, 2 wrong-sign kaon, 3 both +DECLARE_SOA_COLUMN(OmPassBits, omPassBits, uint16_t); //! cumulative pass bits of the mode, see the file header +DECLARE_SOA_COLUMN(OmXiK0sFeatures, omXiK0sFeatures, float[63]); //! master features of the XiK0s contract (PWGLF/Core/Omega2012MlFeatures.h) +DECLARE_SOA_COLUMN(OmXi1530KFeatures, omXi1530KFeatures, float[126]); //! master features of the Xi1530K contract (PWGLF/Core/Omega2012MlFeatures.h) +DECLARE_SOA_COLUMN(OmFeatureStatus, omFeatureStatus, uint8_t); //! o2::analysis::omega2012ml::BuildStatus; only Ok (0) rows are written +// Truth and generated audit +DECLARE_SOA_COLUMN(OmTruthStatus, omTruthStatus, uint8_t); //! 0 data, 1 matched, 2 unmatched +DECLARE_SOA_COLUMN(OmMotherPdg, omMotherPdg, int32_t); +DECLARE_SOA_COLUMN(OmMotherId, omMotherId, int64_t); +DECLARE_SOA_COLUMN(OmXiMotherPdg, omXiMotherPdg, int32_t); //! immediate-mother PDG of the Xi (any candidate) +DECLARE_SOA_COLUMN(OmV0MotherPdg, omV0MotherPdg, int32_t); //! immediate-mother PDG of the V0 (any candidate; 311 shows a K0bar intermediate) +DECLARE_SOA_COLUMN(OmXi1530Id, omXi1530Id, int64_t); //! immediate-mother ID of the Xi (the Xi(1530)0 candidate) +DECLARE_SOA_COLUMN(OmOriginalMcParticleId, omOriginalMcParticleId, int64_t); +DECLARE_SOA_COLUMN(OmPdg, omPdg, int32_t); +DECLARE_SOA_COLUMN(OmDaughterPdg1, omDaughterPdg1, int32_t); +DECLARE_SOA_COLUMN(OmDaughterPdg2, omDaughterPdg2, int32_t); +DECLARE_SOA_COLUMN(OmGenChannel, omGenChannel, uint8_t); //! immediate channel of a generated Omega(2012): 0 other, 1 XiK0s, 2 Xi1530K +DECLARE_SOA_COLUMN(OmGenPt, omGenPt, float); +DECLARE_SOA_COLUMN(OmGenY, omGenY, float); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlEvents, "AOD", "OMMLEVENT", + o2::soa::Index<>, omega2012ml::OmDecayMode, omega2012ml::OmRecoCollisionId, + omega2012ml::OmPosZ, omega2012ml::OmBField, omega2012ml::OmCentrality, + omega2012ml::OmMultiplicity, omega2012ml::OmRecINELgt0); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlEvent, omega2012MlEvent, int, Omega2012MlEvents, "OmmlEvents", ""); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlCascades, "AOD", "OMMLCASCADE", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceRow, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, omega2012ml::OmSign, + omega2012ml::OmMassXi, omega2012ml::OmMassLambda, + omega2012ml::OmV0CosPA, omega2012ml::OmCascCosPA, omega2012ml::OmV0DaughDCA, omega2012ml::OmCascDaughDCA, + omega2012ml::OmDcaPosToPV, omega2012ml::OmDcaNegToPV, omega2012ml::OmDcaBachToPV, omega2012ml::OmDcaV0ToPV, + omega2012ml::OmDcaXYCascToPV, omega2012ml::OmDcaZCascToPV, + omega2012ml::OmV0Radius, omega2012ml::OmCascRadius, + omega2012ml::OmDecayVtxX, omega2012ml::OmDecayVtxY, omega2012ml::OmDecayVtxZ, + omega2012ml::OmTpcPosPi10, omega2012ml::OmTpcPosKa10, omega2012ml::OmTpcPosPr10, + omega2012ml::OmTpcNegPi10, omega2012ml::OmTpcNegKa10, omega2012ml::OmTpcNegPr10, + omega2012ml::OmTpcBachPi10, omega2012ml::OmTpcBachKa10, omega2012ml::OmTpcBachPr10, + omega2012ml::OmTofPosPi10, omega2012ml::OmTofPosKa10, omega2012ml::OmTofPosPr10, + omega2012ml::OmTofNegPi10, omega2012ml::OmTofNegKa10, omega2012ml::OmTofNegPr10, + omega2012ml::OmTofBachPi10, omega2012ml::OmTofBachKa10, omega2012ml::OmTofBachPr10, + omega2012ml::OmCrossedRowsPos, omega2012ml::OmCrossedRowsNeg, omega2012ml::OmCrossedRowsBach, + omega2012ml::OmCascadeIndices); +DECLARE_SOA_TABLE(Omega2012MlV0s, "AOD", "OMMLV0", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceRow, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmMassK0Short, omega2012ml::OmMassLambda, omega2012ml::OmMassAntiLambda, + omega2012ml::OmV0CosPA, omega2012ml::OmV0DaughDCA, + omega2012ml::OmDcaPosToPV, omega2012ml::OmDcaNegToPV, omega2012ml::OmDcaV0ToPV, omega2012ml::OmV0Radius, + omega2012ml::OmDecayVtxX, omega2012ml::OmDecayVtxY, omega2012ml::OmDecayVtxZ, + omega2012ml::OmArmAlpha, omega2012ml::OmArmQt, + omega2012ml::OmTpcPosPi10, omega2012ml::OmTpcPosKa10, omega2012ml::OmTpcPosPr10, + omega2012ml::OmTpcNegPi10, omega2012ml::OmTpcNegKa10, omega2012ml::OmTpcNegPr10, + omega2012ml::OmTofPosPi10, omega2012ml::OmTofPosKa10, omega2012ml::OmTofPosPr10, + omega2012ml::OmTofNegPi10, omega2012ml::OmTofNegKa10, omega2012ml::OmTofNegPr10, + omega2012ml::OmCrossedRowsPos, omega2012ml::OmCrossedRowsNeg, + omega2012ml::OmV0Indices); +DECLARE_SOA_TABLE(Omega2012MlTracks, "AOD", "OMMLTRACK", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmSourceTrackId, + omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmPidPi, omega2012ml::OmPidKa, omega2012ml::OmPidPr, + omega2012ml::OmSelectionFlags, omega2012ml::OmTrackFlags, + omega2012ml::OmCrossedRows, omega2012ml::OmItsClusterMap); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlCascade, omega2012MlCascade, int, Omega2012MlCascades, "OmmlCascades", ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlV0, omega2012MlV0, int, Omega2012MlV0s, "OmmlV0s", ""); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlPionTrack, omega2012MlPionTrack, int, Omega2012MlTracks, "OmmlTracks", "_Pion"); +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlKaonTrack, omega2012MlKaonTrack, int, Omega2012MlTracks, "OmmlTracks", "_Kaon"); +} // namespace omega2012ml + +// Mode A: Xi K0S +DECLARE_SOA_TABLE(Omega2012MlXiK0sCandidates, "AOD", "OMMLXIK0SCAND", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, + omega2012ml::Omega2012MlCascadeId, omega2012ml::Omega2012MlV0Id, + omega2012ml::OmMass, omega2012ml::OmPt, omega2012ml::OmY, omega2012ml::OmEta, omega2012ml::OmPhi, + omega2012ml::OmOpeningAngle, omega2012ml::OmCharge, omega2012ml::OmPassBits); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlXiK0sCandidate, omega2012MlXiK0sCandidate, int, Omega2012MlXiK0sCandidates, "OmmlXiK0sCands", ""); +} // namespace omega2012ml +DECLARE_SOA_TABLE(Omega2012MlXiK0sInputs, "AOD", "OMMLXIK0SINPUT", + o2::soa::Index<>, omega2012ml::Omega2012MlXiK0sCandidateId, + omega2012ml::OmXiK0sFeatures, omega2012ml::OmFeatureStatus); +DECLARE_SOA_TABLE(Omega2012MlXiK0sTruth, "AOD", "OMMLXIK0STRUTH", + o2::soa::Index<>, omega2012ml::Omega2012MlXiK0sCandidateId, + omega2012ml::OmTruthStatus, omega2012ml::OmMotherPdg, omega2012ml::OmMotherId, + omega2012ml::OmV0MotherPdg, omega2012ml::OmXiMotherPdg); + +// Mode B: Xi(1530)0 K- -> Xi- pi+ K- +DECLARE_SOA_TABLE(Omega2012MlXi1530KCandidates, "AOD", "OMMLXI1530KCAND", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, + omega2012ml::Omega2012MlCascadeId, omega2012ml::Omega2012MlPionTrackId, omega2012ml::Omega2012MlKaonTrackId, + omega2012ml::OmMass, omega2012ml::OmMassXiPi, omega2012ml::OmMassXiK, omega2012ml::OmMassPiK, + omega2012ml::OmPt, omega2012ml::OmY, omega2012ml::OmEta, omega2012ml::OmPhi, + omega2012ml::OmChargePattern, omega2012ml::OmPassBits); +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlXi1530KCandidate, omega2012MlXi1530KCandidate, int, Omega2012MlXi1530KCandidates, "OmmlXi1530KCands", ""); +} // namespace omega2012ml +DECLARE_SOA_TABLE(Omega2012MlXi1530KInputs, "AOD", "OMMLXI1530KINP", + o2::soa::Index<>, omega2012ml::Omega2012MlXi1530KCandidateId, + omega2012ml::OmXi1530KFeatures, omega2012ml::OmFeatureStatus); +DECLARE_SOA_TABLE(Omega2012MlXi1530KTruth, "AOD", "OMMLXI1530KTRU", + o2::soa::Index<>, omega2012ml::Omega2012MlXi1530KCandidateId, + omega2012ml::OmTruthStatus, omega2012ml::OmMotherPdg, omega2012ml::OmMotherId, + omega2012ml::OmXi1530Id); + +// Generated Omega(2012) parents of selected reconstructed MC events (ResoMCParents_001) +DECLARE_SOA_TABLE(Omega2012MlGenAudit, "AOD", "OMMLGENAUDIT", + o2::soa::Index<>, omega2012ml::OmRecoCollisionId, omega2012ml::OmOriginalMcParticleId, + omega2012ml::OmPdg, omega2012ml::OmDaughterPdg1, omega2012ml::OmDaughterPdg2, + omega2012ml::OmGenChannel, omega2012ml::OmGenPt, omega2012ml::OmGenY); + +// MC truth of reconstructed MC: daughter labels of the written objects and the pruned source McParticles rows +// (Omega(2012) decay trees and ancestry of the daughter labels). Source rows are DF-local scalar values. +namespace omega2012ml +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(Omega2012MlTrack, omega2012MlTrack, int, Omega2012MlTracks, "OmmlTracks", ""); +DECLARE_SOA_COLUMN(OmMcCollisionRow, omMcCollisionRow, int64_t); //! row of the MC collision in the input McCollisions table +DECLARE_SOA_COLUMN(OmCascadeMcLabels, omCascadeMcLabels, int[3]); //! McParticles rows of the cascade daughters, order of omCascadeIndices; -1 if absent +DECLARE_SOA_COLUMN(OmV0McLabels, omV0McLabels, int[2]); //! McParticles rows of the V0 daughters, order of omV0Indices; -1 if absent +DECLARE_SOA_COLUMN(OmMcLabel, omMcLabel, int); //! McParticles row of the track; -1 if absent +DECLARE_SOA_COLUMN(OmMcRow, omMcRow, int64_t); //! row of the particle in the input McParticles table +DECLARE_SOA_COLUMN(OmMcSelection, omMcSelection, uint8_t); //! 1 Omega(2012) decay tree, 2 ancestry of a written daughter label +DECLARE_SOA_COLUMN(OmNMothers, omNMothers, uint8_t); //! number of mothers in the input row +DECLARE_SOA_COLUMN(OmMotherRows, omMotherRows, int[2]); //! first two McParticles mother rows; -1 if absent +DECLARE_SOA_COLUMN(OmDaughterRows, omDaughterRows, int[2]); //! McParticles daughter slice (first, last) as stored in the input row +DECLARE_SOA_COLUMN(OmE, omE, float); +DECLARE_SOA_COLUMN(OmWeight, omWeight, float); +} // namespace omega2012ml + +DECLARE_SOA_TABLE(Omega2012MlEventMcs, "AOD", "OMMLEVENTMC", + o2::soa::Index<>, omega2012ml::Omega2012MlEventId, omega2012ml::OmMcCollisionRow); +DECLARE_SOA_TABLE(Omega2012MlCascadeMcs, "AOD", "OMMLCASCMC", + o2::soa::Index<>, omega2012ml::Omega2012MlCascadeId, omega2012ml::OmCascadeMcLabels); +DECLARE_SOA_TABLE(Omega2012MlV0Mcs, "AOD", "OMMLV0MC", + o2::soa::Index<>, omega2012ml::Omega2012MlV0Id, omega2012ml::OmV0McLabels); +DECLARE_SOA_TABLE(Omega2012MlTrackMcs, "AOD", "OMMLTRACKMC", + o2::soa::Index<>, omega2012ml::Omega2012MlTrackId, omega2012ml::OmMcLabel); +DECLARE_SOA_TABLE(Omega2012MlMcParticles, "AOD", "OMMLMCPART", + o2::soa::Index<>, omega2012ml::OmMcRow, omega2012ml::OmMcSelection, omega2012ml::OmPdg, + omega2012ml::OmMcCollisionRow, omega2012ml::OmNMothers, omega2012ml::OmMotherRows, + omega2012ml::OmDaughterRows, omega2012ml::OmPx, omega2012ml::OmPy, omega2012ml::OmPz, + omega2012ml::OmE, omega2012ml::OmWeight); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFOMEGA2012MLTABLES_H_ diff --git a/PWGLF/DataModel/LFPhiFlowTables.h b/PWGLF/DataModel/LFPhiFlowTables.h new file mode 100644 index 00000000000..139d0ee763c --- /dev/null +++ b/PWGLF/DataModel/LFPhiFlowTables.h @@ -0,0 +1,70 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file LFPhiFlowTables.h +/// \brief DataModel for Phi flow +/// +/// \author Prottay Das + +#ifndef PWGLF_DATAMODEL_LFPHIFLOWTABLES_H_ +#define PWGLF_DATAMODEL_LFPHIFLOWTABLES_H_ + +#include +#include + +#include +#include + +namespace o2::aod +{ +namespace kaonevent +{ +DECLARE_SOA_COLUMN(Cent, cent, float); +DECLARE_SOA_COLUMN(Posz, posz, float); +DECLARE_SOA_COLUMN(QxA, qxA, float); +DECLARE_SOA_COLUMN(QxC, qxC, float); +DECLARE_SOA_COLUMN(QyA, qyA, float); +DECLARE_SOA_COLUMN(QyC, qyC, float); +} // namespace kaonevent +DECLARE_SOA_TABLE(KaonEvents, "AOD", "KAONEVENT", + o2::soa::Index<>, + kaonevent::Cent, + kaonevent::Posz, + kaonevent::QxA, + kaonevent::QxC, + kaonevent::QyA, + kaonevent::QyC) +using KaonEvent = KaonEvents::iterator; + +namespace kaonpair +{ +DECLARE_SOA_INDEX_COLUMN(KaonEvent, kaonevent); +DECLARE_SOA_COLUMN(Px, px, float); //! Bachelor Kaon Px +DECLARE_SOA_COLUMN(Py, py, float); //! Bachelor Kaon Py +DECLARE_SOA_COLUMN(Pz, pz, float); //! Bachelor Kaon Pz +DECLARE_SOA_COLUMN(Charge, charge, int8_t); //! Charge +DECLARE_SOA_COLUMN(KaonIndex, kaonIndex, int64_t); //! Daughter Kaon index1 +DECLARE_SOA_COLUMN(KaonPidMask, kaonPidMask, uint16_t); //! bitmask for PID selections +} // namespace kaonpair + +DECLARE_SOA_TABLE(KaonTracks, "AOD", "KAONTRACK", + o2::soa::Index<>, + kaonpair::KaonEventId, + kaonpair::Px, + kaonpair::Py, + kaonpair::Pz, + kaonpair::Charge, + kaonpair::KaonIndex, + kaonpair::KaonPidMask); + +using KaonTrack = KaonTracks::iterator; +} // namespace o2::aod +#endif // PWGLF_DATAMODEL_LFPHIFLOWTABLES_H_ diff --git a/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h b/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h index b051294c4f3..44f552a0f6d 100644 --- a/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h +++ b/PWGLF/DataModel/LFPhiStrangeCorrelationTables.h @@ -29,12 +29,6 @@ DECLARE_SOA_COLUMN(DefaultSel, defaultSel, bool); DECLARE_SOA_COLUMN(PhimesonSel, phimesonSel, bool); } // namespace lf_selection_event -/*DECLARE_SOA_TABLE(PhiStrangeDefEvtSelDataLike, "AOD", "DEFEVTSELDATA", - lf_selection_event::DefaultSel); - -DECLARE_SOA_TABLE(PhiStrangeDefEvtSelMcGen, "AOD", "DEFEVTSELMCGEN", - lf_selection_event::DefaultSel);*/ - DECLARE_SOA_TABLE(PhiStrangeEvtSelDataLike, "AOD", "EVTSELDATA", lf_selection_event::DefaultSel, lf_selection_event::PhimesonSel); @@ -43,28 +37,6 @@ DECLARE_SOA_TABLE(PhiStrangeEvtSelMcGen, "AOD", "EVTSELMCGEN", lf_selection_event::DefaultSel, lf_selection_event::PhimesonSel); -/*namespace lf_selection_default_collision -{ -DECLARE_SOA_COLUMN(DefaultSel, defaultSel, bool); -} // namespace lf_selection_default_collision - -DECLARE_SOA_TABLE(DefaultSelectionData, "AOD", "DEFSELDATA", - lf_selection_default_collision::DefaultSel); - -DECLARE_SOA_TABLE(DefaultSelectionMcGen, "AOD", "DEFSELMCGEN", - lf_selection_default_collision::DefaultSel); - -namespace lf_selection_phi_collision -{ -DECLARE_SOA_COLUMN(PhimesonSel, phimesonSel, bool); -} // namespace lf_selection_phi_collision - -DECLARE_SOA_TABLE(PhimesonSelectionData, "AOD", "PHIINCOLLDATA", - lf_selection_phi_collision::PhimesonSel); - -DECLARE_SOA_TABLE(PhimesonSelectionMcGen, "AOD", "PHIINCOLLMCGEN", - lf_selection_phi_collision::PhimesonSel);*/ - namespace lf_selection_phi_candidate { DECLARE_SOA_INDEX_COLUMN(Collision, collision); @@ -89,7 +61,7 @@ DECLARE_SOA_TABLE(PhimesonCandidatesData, "AOD", "PHICANDDATA", lf_selection_phi_candidate::Phi, lf_selection_phi_candidate::InMassRegion); -DECLARE_SOA_TABLE(PhimesonCandidatesMcReco, "AOD", "PHICANDMCRECO", +DECLARE_SOA_TABLE(PhimesonCandidatesMcReco, "AOD", "PHICANDMCREC", soa::Index<>, lf_selection_phi_candidate::CollisionId, lf_selection_phi_candidate::M, @@ -107,7 +79,7 @@ DECLARE_SOA_TABLE(PhimesonCandidatesMcGen, "AOD", "PHICANDMCGEN", lf_selection_phi_candidate::Phi, lf_selection_phi_candidate::InMassRegion); -namespace lf_selection_k0s_reduced +namespace lf_selection_strange_reduced { DECLARE_SOA_INDEX_COLUMN(Collision, collision); @@ -120,25 +92,97 @@ DECLARE_SOA_DYNAMIC_COLUMN(InMassRegion, inMassRegion, [](float m, float minM, float maxM) -> bool { return (m >= minM && m <= maxM); }); -} // namespace lf_selection_k0s_reduced +} // namespace lf_selection_strange_reduced DECLARE_SOA_TABLE(K0sReducedCandidatesData, "AOD", "K0SCANDDATA", soa::Index<>, - lf_selection_k0s_reduced::CollisionId, - lf_selection_k0s_reduced::M, - lf_selection_k0s_reduced::Pt, - lf_selection_k0s_reduced::Y, - lf_selection_k0s_reduced::Phi, - lf_selection_k0s_reduced::InMassRegion); - -DECLARE_SOA_TABLE(K0sReducedCandidatesMcReco, "AOD", "K0SCANDMCRECO", + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(K0sReducedCandidatesMcReco, "AOD", "K0SCANDMCREC", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(LambdaReducedCandidatesData, "AOD", "LAMBDACANDDATA", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(LambdaReducedCandidatesMcReco, "AOD", "LAMBDACANDMCREC", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(AntiLambdaReducedCandidatesData, "AOD", "ALAMBCANDDATA", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(AntiLambdaReducedCandidatesMcReco, "AOD", "ALAMBCANDMCREC", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(XiReducedCandidatesData, "AOD", "XICANDDATA", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(XiReducedCandidatesMcReco, "AOD", "XICANDMCREC", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(OmegaReducedCandidatesData, "AOD", "OMEGACANDDATA", + soa::Index<>, + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); + +DECLARE_SOA_TABLE(OmegaReducedCandidatesMcReco, "AOD", "OMEGACANDMCREC", soa::Index<>, - lf_selection_k0s_reduced::CollisionId, - lf_selection_k0s_reduced::M, - lf_selection_k0s_reduced::Pt, - lf_selection_k0s_reduced::Y, - lf_selection_k0s_reduced::Phi, - lf_selection_k0s_reduced::InMassRegion); + lf_selection_strange_reduced::CollisionId, + lf_selection_strange_reduced::M, + lf_selection_strange_reduced::Pt, + lf_selection_strange_reduced::Y, + lf_selection_strange_reduced::Phi, + lf_selection_strange_reduced::InMassRegion); namespace lf_selection_pion_track { @@ -176,7 +220,7 @@ DECLARE_SOA_TABLE(PionTracksData, "AOD", "PITRACKSDATA", lf_selection_pion_track::HasTOF, lf_selection_pion_track::NSigmaTOF>); -DECLARE_SOA_TABLE(PionTracksMcReco, "AOD", "PITRACKSMCRECO", +DECLARE_SOA_TABLE(PionTracksMcReco, "AOD", "PITRACKSMCREC", soa::Index<>, lf_selection_pion_track::CollisionId, lf_selection_pion_track::NSigmaTPC, diff --git a/PWGLF/DataModel/LFPhotonDeuteronTables.h b/PWGLF/DataModel/LFPhotonDeuteronTables.h new file mode 100644 index 00000000000..41a6e3ba02c --- /dev/null +++ b/PWGLF/DataModel/LFPhotonDeuteronTables.h @@ -0,0 +1,73 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file LFPhotonDeuteronTables.h +/// \brief Tables for photon-deuteron correlation analysis +/// \author Arvind Khuntia and Francesco Noferini + +#ifndef PWGLF_DATAMODEL_LFPHOTONDEUTERONTABLES_H_ +#define PWGLF_DATAMODEL_LFPHOTONDEUTERONTABLES_H_ + +#include + +namespace o2::aod +{ +namespace photondeuteron +{ +DECLARE_SOA_COLUMN(PhotonPt, photonPt, float); //! Photon transverse momentum +DECLARE_SOA_COLUMN(PhotonEta, photonEta, float); //! Photon pseudorapidity +DECLARE_SOA_COLUMN(PhotonPhi, photonPhi, float); //! Photon azimuthal angle +DECLARE_SOA_COLUMN(PhotonMass, photonMass, float); //! Photon invariant mass +DECLARE_SOA_COLUMN(PhotonPosPt, photonPosPt, float); //! Positive daughter (e+) transverse momentum +DECLARE_SOA_COLUMN(PhotonPosEta, photonPosEta, float); //! Positive daughter (e+) pseudorapidity +DECLARE_SOA_COLUMN(PhotonPosPhi, photonPosPhi, float); //! Positive daughter (e+) azimuthal angle +DECLARE_SOA_COLUMN(PhotonPosNSigmaElTPC, photonPosNSigmaElTPC, float); //! Positive daughter (e+) NSigma electron TPC +DECLARE_SOA_COLUMN(PhotonNegPt, photonNegPt, float); //! Negative daughter (e-) transverse momentum +DECLARE_SOA_COLUMN(PhotonNegEta, photonNegEta, float); //! Negative daughter (e-) pseudorapidity +DECLARE_SOA_COLUMN(PhotonNegPhi, photonNegPhi, float); //! Negative daughter (e-) azimuthal angle +DECLARE_SOA_COLUMN(PhotonNegNSigmaElTPC, photonNegNSigmaElTPC, float); //! Negative daughter (e-) NSigma electron TPC +DECLARE_SOA_COLUMN(DeuteronPt, deuteronPt, float); //! Deuteron transverse momentum +DECLARE_SOA_COLUMN(DeuteronEta, deuteronEta, float); //! Deuteron pseudorapidity +DECLARE_SOA_COLUMN(DeuteronPhi, deuteronPhi, float); //! Deuteron azimuthal angle +DECLARE_SOA_COLUMN(DeuteronNSigmaTPC, deuteronNSigmaTPC, float); //! Deuteron NSigma TPC +DECLARE_SOA_COLUMN(DeuteronNSigmaTOF, deuteronNSigmaTOF, float); //! Deuteron NSigma TOF +DECLARE_SOA_COLUMN(DeltaPhi, deltaPhi, float); //! Delta phi between photon and deuteron +DECLARE_SOA_COLUMN(DeltaEta, deltaEta, float); //! Delta eta between photon and deuteron +DECLARE_SOA_COLUMN(InvMass, invMass, float); //! Photon-deuteron invariant mass +DECLARE_SOA_COLUMN(RelativeMomentum, relativeMomentum, float); //! Relative momentum k*_pn +} // namespace photondeuteron + +DECLARE_SOA_TABLE(PhotonDeuteronPairs, "AOD", "PHOTONDPAIRS", //! Table for photon-deuteron pairs + photondeuteron::PhotonPt, + photondeuteron::PhotonEta, + photondeuteron::PhotonPhi, + photondeuteron::PhotonMass, + photondeuteron::PhotonPosPt, + photondeuteron::PhotonPosEta, + photondeuteron::PhotonPosPhi, + photondeuteron::PhotonPosNSigmaElTPC, + photondeuteron::PhotonNegPt, + photondeuteron::PhotonNegEta, + photondeuteron::PhotonNegPhi, + photondeuteron::PhotonNegNSigmaElTPC, + photondeuteron::DeuteronPt, + photondeuteron::DeuteronEta, + photondeuteron::DeuteronPhi, + photondeuteron::DeuteronNSigmaTPC, + photondeuteron::DeuteronNSigmaTOF, + photondeuteron::DeltaPhi, + photondeuteron::DeltaEta, + photondeuteron::InvMass, + photondeuteron::RelativeMomentum); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFPHOTONDEUTERONTABLES_H_ diff --git a/PWGLF/DataModel/LFPiHypertritonFemtoTables.h b/PWGLF/DataModel/LFPiHypertritonFemtoTables.h new file mode 100644 index 00000000000..ba6416885c0 --- /dev/null +++ b/PWGLF/DataModel/LFPiHypertritonFemtoTables.h @@ -0,0 +1,239 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file LFPiHypertritonFemtoTables.h +/// \brief Slim tables for pi-hypertriton femto pairs +/// + +#ifndef PWGLF_DATAMODEL_LFPIHYPERTRITONFEMTOTABLES_H_ +#define PWGLF_DATAMODEL_LFPIHYPERTRITONFEMTOTABLES_H_ + +#include + +#include + +namespace o2::aod +{ +namespace pihypertritonfemto +{ +constexpr uint8_t ClosePairAngular = 1 << 0; +constexpr uint8_t ClosePairDistance = 1 << 1; +constexpr uint8_t ClosePairAngularUnavailable = 1 << 2; +constexpr uint8_t ClosePairDistanceUnavailable = 1 << 3; +DECLARE_SOA_COLUMN(IsMixed, isMixed, bool); +// Offline CPR bits, OR-ed across same-sign daughters: 1=angular rejection, 2=distance rejection, +// 4=angular unavailable, 8=distance unavailable. Zero means all applicable checks passed. +// Opposite-sign comparisons require no CPR. All flagged pairs remain in the table. +DECLARE_SOA_COLUMN(IsClosePairRejected, isClosePairRejected, uint8_t); +DECLARE_SOA_COLUMN(MixingDepth, mixingDepth, int); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); +DECLARE_SOA_COLUMN(TrackOccupancy, trackOccupancy, int); +DECLARE_SOA_COLUMN(Ft0cOccupancy, ft0cOccupancy, float); +DECLARE_SOA_COLUMN(MultFT0C, multFT0C, float); +DECLARE_SOA_COLUMN(XPrimVtx, xPrimVtx, float); +DECLARE_SOA_COLUMN(YPrimVtx, yPrimVtx, float); +DECLARE_SOA_COLUMN(ZPrimVtx, zPrimVtx, float); +DECLARE_SOA_COLUMN(HypIsMatter, hypIsMatter, bool); +DECLARE_SOA_COLUMN(HypPtHe3, hypPtHe3, float); +DECLARE_SOA_COLUMN(HypEtaHe3, hypEtaHe3, float); +DECLARE_SOA_COLUMN(HypPhiHe3, hypPhiHe3, float); +DECLARE_SOA_COLUMN(HypPtPi, hypPtPi, float); +DECLARE_SOA_COLUMN(HypEtaPi, hypEtaPi, float); +DECLARE_SOA_COLUMN(HypPhiPi, hypPhiPi, float); +DECLARE_SOA_COLUMN(HypDcaV0Daug, hypDcaV0Daug, float); +DECLARE_SOA_COLUMN(HypDcaHe, hypDcaHe, float); +DECLARE_SOA_COLUMN(HypDcaPi, hypDcaPi, float); +DECLARE_SOA_COLUMN(HypNSigmaHe, hypNSigmaHe, float); +DECLARE_SOA_COLUMN(HypNTPCCrossedRowsHe, hypNTPCCrossedRowsHe, uint8_t); +DECLARE_SOA_COLUMN(HypNTPCCrossedRowsPi, hypNTPCCrossedRowsPi, uint8_t); +DECLARE_SOA_COLUMN(HypTpcMomHe, hypTpcMomHe, float); +DECLARE_SOA_COLUMN(HypTpcMomPi, hypTpcMomPi, float); +DECLARE_SOA_COLUMN(HypTpcSignalHe, hypTpcSignalHe, uint16_t); +DECLARE_SOA_COLUMN(HypTpcSignalPi, hypTpcSignalPi, uint16_t); +DECLARE_SOA_COLUMN(HypItsClusterSizesHe, hypItsClusterSizesHe, uint32_t); +DECLARE_SOA_COLUMN(HypItsClusterSizesPi, hypItsClusterSizesPi, uint32_t); +DECLARE_SOA_COLUMN(HypXDecVtx, hypXDecVtx, float); +DECLARE_SOA_COLUMN(HypYDecVtx, hypYDecVtx, float); +DECLARE_SOA_COLUMN(HypZDecVtx, hypZDecVtx, float); +DECLARE_SOA_COLUMN(HadPt, hadPt, float); +DECLARE_SOA_COLUMN(HadEta, hadEta, float); +DECLARE_SOA_COLUMN(HadPhi, hadPhi, float); +DECLARE_SOA_COLUMN(HadSign, hadSign, int8_t); +DECLARE_SOA_COLUMN(HadDcaXY, hadDcaXY, float); +DECLARE_SOA_COLUMN(HadTpcNClsCrossedRows, hadTpcNClsCrossedRows, uint8_t); +DECLARE_SOA_COLUMN(HadTpcNClsPID, hadTpcNClsPID, uint8_t); +DECLARE_SOA_COLUMN(HadTpcChi2NCl, hadTpcChi2NCl, float); +DECLARE_SOA_COLUMN(HadItsClusterSizes, hadItsClusterSizes, uint32_t); +DECLARE_SOA_COLUMN(HadItsChi2NCl, hadItsChi2NCl, float); +DECLARE_SOA_COLUMN(HadHasTOF, hadHasTOF, bool); +DECLARE_SOA_COLUMN(HadTpcNSigmaPi, hadTpcNSigmaPi, float); +DECLARE_SOA_COLUMN(HadTofNSigmaPi, hadTofNSigmaPi, float); +DECLARE_SOA_COLUMN(HypGenPt, hypGenPt, float); +DECLARE_SOA_COLUMN(HypGenEta, hypGenEta, float); +DECLARE_SOA_COLUMN(HypGenPhi, hypGenPhi, float); +DECLARE_SOA_COLUMN(HypGenXDecVtx, hypGenXDecVtx, float); +DECLARE_SOA_COLUMN(HypGenYDecVtx, hypGenYDecVtx, float); +DECLARE_SOA_COLUMN(HypGenZDecVtx, hypGenZDecVtx, float); +DECLARE_SOA_COLUMN(HypIsReco, hypIsReco, bool); +DECLARE_SOA_COLUMN(HypIsSignal, hypIsSignal, bool); +DECLARE_SOA_COLUMN(HypIsRecoMCCollision, hypIsRecoMCCollision, bool); +DECLARE_SOA_COLUMN(HypIsSurvEvSel, hypIsSurvEvSel, bool); +DECLARE_SOA_COLUMN(HypIsTwoBodyDecay, hypIsTwoBodyDecay, bool); +DECLARE_SOA_COLUMN(HypStatusCode, hypStatusCode, int16_t); +DECLARE_SOA_COLUMN(HeGenPt, heGenPt, float); +DECLARE_SOA_COLUMN(HeIsPhysicalPrimary, heIsPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(DecayPiIsPhysicalPrimary, decayPiIsPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(HadRecoPt, hadRecoPt, float); +DECLARE_SOA_COLUMN(HadRecoEta, hadRecoEta, float); +DECLARE_SOA_COLUMN(HadRecoPhi, hadRecoPhi, float); +DECLARE_SOA_COLUMN(HadGenPt, hadGenPt, float); +DECLARE_SOA_COLUMN(HadGenEta, hadGenEta, float); +DECLARE_SOA_COLUMN(HadGenPhi, hadGenPhi, float); +DECLARE_SOA_COLUMN(HadIsPhysicalPrimary, hadIsPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(HadProcess, hadProcess, int16_t); +DECLARE_SOA_COLUMN(SameMCCollision, sameMCCollision, bool); +DECLARE_SOA_COLUMN(MatchesHypRecoMCCollision, matchesHypRecoMCCollision, bool); +DECLARE_SOA_COLUMN(MatchesPairRecoMCCollision, matchesPairRecoMCCollision, bool); +DECLARE_SOA_COLUMN(IsTruthSelfCorrelation, isTruthSelfCorrelation, bool); +DECLARE_SOA_COLUMN(IsTruePrimaryHadHyperPair, isTruePrimaryHadHyperPair, bool); +DECLARE_SOA_COLUMN(HadIsTruePion, hadIsTruePion, bool); // Truth species, independent of SE/ME and primary status. +} // namespace pihypertritonfemto + +DECLARE_SOA_TABLE(PiHypertritonFemtoTable, "AOD", "PIHYPFEMTO", + pihypertritonfemto::IsMixed, + pihypertritonfemto::IsClosePairRejected, + pihypertritonfemto::MixingDepth, + pihypertritonfemto::PosZ, + pihypertritonfemto::CentFT0C, + pihypertritonfemto::TrackOccupancy, + pihypertritonfemto::Ft0cOccupancy, + pihypertritonfemto::MultFT0C, + pihypertritonfemto::XPrimVtx, + pihypertritonfemto::YPrimVtx, + pihypertritonfemto::ZPrimVtx, + pihypertritonfemto::HypIsMatter, + pihypertritonfemto::HypPtHe3, + pihypertritonfemto::HypEtaHe3, + pihypertritonfemto::HypPhiHe3, + pihypertritonfemto::HypPtPi, + pihypertritonfemto::HypEtaPi, + pihypertritonfemto::HypPhiPi, + pihypertritonfemto::HypDcaV0Daug, + pihypertritonfemto::HypDcaHe, + pihypertritonfemto::HypDcaPi, + pihypertritonfemto::HypNSigmaHe, + pihypertritonfemto::HypNTPCCrossedRowsHe, + pihypertritonfemto::HypNTPCCrossedRowsPi, + pihypertritonfemto::HypTpcMomHe, + pihypertritonfemto::HypTpcMomPi, + pihypertritonfemto::HypTpcSignalHe, + pihypertritonfemto::HypTpcSignalPi, + pihypertritonfemto::HypItsClusterSizesHe, + pihypertritonfemto::HypItsClusterSizesPi, + pihypertritonfemto::HypXDecVtx, + pihypertritonfemto::HypYDecVtx, + pihypertritonfemto::HypZDecVtx, + pihypertritonfemto::HadPt, + pihypertritonfemto::HadEta, + pihypertritonfemto::HadPhi, + pihypertritonfemto::HadSign, + pihypertritonfemto::HadDcaXY, + pihypertritonfemto::HadTpcNClsCrossedRows, + pihypertritonfemto::HadTpcNClsPID, + pihypertritonfemto::HadTpcChi2NCl, + pihypertritonfemto::HadItsClusterSizes, + pihypertritonfemto::HadItsChi2NCl, + pihypertritonfemto::HadHasTOF, + pihypertritonfemto::HadTpcNSigmaPi, + pihypertritonfemto::HadTofNSigmaPi); + +DECLARE_SOA_TABLE(PiHypertritonFemtoTableMC, "AOD", "PIHYPFEMTOMC", + pihypertritonfemto::IsMixed, + pihypertritonfemto::IsClosePairRejected, + pihypertritonfemto::MixingDepth, + pihypertritonfemto::PosZ, + pihypertritonfemto::CentFT0C, + pihypertritonfemto::TrackOccupancy, + pihypertritonfemto::Ft0cOccupancy, + pihypertritonfemto::MultFT0C, + pihypertritonfemto::XPrimVtx, + pihypertritonfemto::YPrimVtx, + pihypertritonfemto::ZPrimVtx, + pihypertritonfemto::HypIsMatter, + pihypertritonfemto::HypPtHe3, + pihypertritonfemto::HypEtaHe3, + pihypertritonfemto::HypPhiHe3, + pihypertritonfemto::HypPtPi, + pihypertritonfemto::HypEtaPi, + pihypertritonfemto::HypPhiPi, + pihypertritonfemto::HypDcaV0Daug, + pihypertritonfemto::HypDcaHe, + pihypertritonfemto::HypDcaPi, + pihypertritonfemto::HypNSigmaHe, + pihypertritonfemto::HypNTPCCrossedRowsHe, + pihypertritonfemto::HypNTPCCrossedRowsPi, + pihypertritonfemto::HypTpcMomHe, + pihypertritonfemto::HypTpcMomPi, + pihypertritonfemto::HypTpcSignalHe, + pihypertritonfemto::HypTpcSignalPi, + pihypertritonfemto::HypItsClusterSizesHe, + pihypertritonfemto::HypItsClusterSizesPi, + pihypertritonfemto::HypXDecVtx, + pihypertritonfemto::HypYDecVtx, + pihypertritonfemto::HypZDecVtx, + pihypertritonfemto::HadPt, + pihypertritonfemto::HadEta, + pihypertritonfemto::HadPhi, + pihypertritonfemto::HadSign, + pihypertritonfemto::HadDcaXY, + pihypertritonfemto::HadTpcNClsCrossedRows, + pihypertritonfemto::HadTpcNClsPID, + pihypertritonfemto::HadTpcChi2NCl, + pihypertritonfemto::HadItsClusterSizes, + pihypertritonfemto::HadItsChi2NCl, + pihypertritonfemto::HadHasTOF, + pihypertritonfemto::HadTpcNSigmaPi, + pihypertritonfemto::HadTofNSigmaPi, + pihypertritonfemto::HypGenPt, + pihypertritonfemto::HypGenEta, + pihypertritonfemto::HypGenPhi, + pihypertritonfemto::HypGenXDecVtx, + pihypertritonfemto::HypGenYDecVtx, + pihypertritonfemto::HypGenZDecVtx, + pihypertritonfemto::HypIsReco, + pihypertritonfemto::HypIsSignal, + pihypertritonfemto::HypIsRecoMCCollision, + pihypertritonfemto::HypIsSurvEvSel, + pihypertritonfemto::HypIsTwoBodyDecay, + pihypertritonfemto::HypStatusCode, + pihypertritonfemto::HeGenPt, + pihypertritonfemto::HeIsPhysicalPrimary, + pihypertritonfemto::DecayPiIsPhysicalPrimary, + pihypertritonfemto::HadRecoPt, + pihypertritonfemto::HadRecoEta, + pihypertritonfemto::HadRecoPhi, + pihypertritonfemto::HadGenPt, + pihypertritonfemto::HadGenEta, + pihypertritonfemto::HadGenPhi, + pihypertritonfemto::HadIsPhysicalPrimary, + pihypertritonfemto::HadProcess, + pihypertritonfemto::SameMCCollision, + pihypertritonfemto::MatchesHypRecoMCCollision, + pihypertritonfemto::MatchesPairRecoMCCollision, + pihypertritonfemto::IsTruthSelfCorrelation, + pihypertritonfemto::IsTruePrimaryHadHyperPair, + pihypertritonfemto::HadIsTruePion); + +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFPIHYPERTRITONFEMTOTABLES_H_ diff --git a/PWGLF/DataModel/LFReducedXi1530KaonTables.h b/PWGLF/DataModel/LFReducedXi1530KaonTables.h new file mode 100644 index 00000000000..7a466201a0b --- /dev/null +++ b/PWGLF/DataModel/LFReducedXi1530KaonTables.h @@ -0,0 +1,379 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file LFReducedXi1530KaonTables.h +/// \brief Reduced data model for Xi(1530)^0--K femtoscopy. +/// +/// The Xi(1530) candidate stores two independent uint64_t selection words: +/// * TrackSelectionBits: daughter PID / track-quality / prompt-pion DCAz; +/// * TopologySelectionBits: Lambda/V0 and Xi/cascade topology, mass and +/// lifetime working points. +/// +/// This split keeps the table compact while retaining both: +/// * the Xi(1530) analysis-note working points/systematic variations; and +/// * the pp Xi-candidate selections used in Run 3 strangeness production. +/// +/// +/// External-kaon DCA choices remain in a compact uint8_t bitmap; raw TPC/TOF +/// n-sigma values are retained for downstream PID studies. + +#ifndef PWGLF_DATAMODEL_LFREDUCEDXI1530KAONTABLES_H_ +#define PWGLF_DATAMODEL_LFREDUCEDXI1530KAONTABLES_H_ + +#include +#include + +#include +#include + +namespace o2::aod +{ + +namespace redxistark +{ +// +1: Xi- pi+ US; -1: anti-Xi+ pi- US; +2/-2: corresponding LS controls. +enum XiStarChannel : int8_t { + kXiStarUS = +1, + kAntiXiStarUS = -1, + kXiStarLS = +2, + kAntiXiStarLS = -2 +}; + +// ----------------------------------------------------------------------------- +// TRACK/PID SELECTION WORD +// ----------------------------------------------------------------------------- +// Bit positions only; numerical thresholds live in producer Configurables. +enum XiStarTrackSelBit : uint8_t { + // Prompt Xi* pion, recipe A: + // no TOF -> TPC only; TOF -> circular sqrt(TPC^2+TOF^2). + kPiFirstPID3Sigma = 0, + kPiFirstPID4Sigma, + kPiFirstPID5Sigma, + kPiFirstPID6Sigma, + + // Prompt Xi* pion, recipe B: + // TPC-only below configurable pT threshold, circular above it. + kPiFirstPtThresholdPID3Sigma, + kPiFirstPtThresholdPID4Sigma, + kPiFirstPtThresholdPID5Sigma, + kPiFirstPtThresholdPID6Sigma, + + // Prompt Xi* pion, pure TPC-only PID. These bits allow the analysis + // to reproduce a TPC-only "all pion tracks" PID strategy downstream, + // independent of whether the prompt pion also has TOF information. + kPiFirstTPC3Sigma, + kPiFirstTPC4Sigma, + kPiFirstTPC5Sigma, + kPiFirstTPC6Sigma, + + // Xi bachelor pion, availability-based hybrid PID. + kXiBachelorPiHybridPID3Sigma, + kXiBachelorPiHybridPID4Sigma, + kXiBachelorPiHybridPID5Sigma, + kXiBachelorPiHybridPID6Sigma, + + // Xi bachelor pion, TPC-only PID. The 4.8-sigma point reproduces the + // dedicated pp-Xi table. + kXiBachelorPiTPC3Sigma, + kXiBachelorPiTPC4Sigma, + kXiBachelorPiTPC4p8Sigma, + kXiBachelorPiTPC5Sigma, + kXiBachelorPiTPC6Sigma, + + // Lambda daughter pion: TPC only. + kLambdaPiTPC3Sigma, + kLambdaPiTPC4Sigma, + kLambdaPiTPC4p8Sigma, + kLambdaPiTPC5Sigma, + kLambdaPiTPC6Sigma, + + // Lambda daughter proton: TPC only. + kLambdaPrTPC3Sigma, + kLambdaPrTPC4Sigma, + kLambdaPrTPC5Sigma, + kLambdaPrTPC6Sigma, + + // Prompt-pion quality. + kPiFirstTPCRows70, + kPiFirstTPCRows80, + kPiFirstTPCRows90, + kPiFirstDCAz1cm, + kPiFirstDCAzDefault, + kPiFirstDCAz0p1cm, + + // Cascade-daughter quality. + kAllCascDaughtersTPCRows50, + kV0DaughtersTPCRows70, + kV0DaughtersTPCRowsVar1, + kV0DaughtersTPCRowsVar2, + + kNXiStarTrackSelBits +}; +static_assert(kNXiStarTrackSelBits <= 64, "Xi(1530) track-selection bitmap exceeds uint64_t capacity"); + +constexpr uint64_t xiStarTrackSelMask(XiStarTrackSelBit bit) +{ + return uint64_t{1} << static_cast(bit); +} + +// ----------------------------------------------------------------------------- +// TOPOLOGY SELECTION WORD +// ----------------------------------------------------------------------------- +enum XiStarTopoSelBit : uint8_t { + // V0 daughter DCA to PV. + kV0PionDcaPV005 = 0, // > 0.05 cm + kV0PionDcaPV006, // > 0.06 cm + kV0PionDcaPV010, // > 0.10 cm + kV0PionDcaPV020, // > 0.20 cm (dedicated Lambda) + + kV0ProtonDcaPV005, // > 0.05 cm + kV0ProtonDcaPV006, // > 0.06 cm + kV0ProtonDcaPV007, // > 0.07 cm (dedicated Lambda) + kV0ProtonDcaPV010, // > 0.10 cm + + // Keep the Xi(1530)-analysis convention requested by the analysis: + // MINIMUM Lambda/V0 DCA to PV. + kV0DcaPV000, // > 0.00 cm + kV0DcaPV003, // > 0.03 cm + kV0DcaPV010, // > 0.10 cm + + // DCA between V0 daughters / native fitter metric. + kV0DcaDaughters1p0, // < 1.0 + kV0DcaDaughters0p5, // < 0.5 + kV0DcaDaughters0p1, // < 0.1 + + // V0 pointing angle. + kV0CosPA097, + kV0CosPA098, + kV0CosPA09876, // pp-Xi selection + kV0CosPA099, + kV0CosPA0995, // dedicated Lambda selection + + // Minimum V0 radius. + kV0Radius0p9, + kV0Radius1p01, + kV0Radius1p2, // pp-Xi selection + kV0Radius2p5, + kV0Radius3p0, // dedicated Lambda selection + + // Lambda mass window. + kLambdaMass10MeV, + kLambdaMass8MeV, + kLambdaMass6MeV, + kLambdaMass11p6MeV, // pp-Xi selection + + // Lambda proper lifetime. Var1/Var2 are reserved for future systematic + // values; their defaults can equal the nominal until values are supplied. + kV0LifetimeDefault, + kV0LifetimeVar1, + kV0LifetimeVar2, + + // Xi bachelor DCA to PV. + kCascBachelorDcaPV005, + kCascBachelorDcaPV006, + kCascBachelorDcaPV010, + + // DCA between bachelor and V0 / native cascade fitter metric. + kCascDcaDaughters1p0, + kCascDcaDaughters0p25, + kCascDcaDaughters0p20, + + // Cascade pointing angle. + kCascCosPA097, + kCascCosPA098, + kCascCosPA09947, // pp-Xi selection + kCascCosPA0995, + + // Minimum cascade radius. + kCascRadius0p9, + kCascRadius1p0, // pp-Xi selection + kCascRadius1p01, + kCascRadius1p3, + + // Xi mass window used by the Xi(1530) analysis. + kXiMass10MeV, + kXiMass8MeV, + kXiMass6MeV, + + // Xi proper-lifetime working points (mL/p in cm). + // Numerical thresholds are Configurable in the producer. + kXiLifetimeDefault, // default: 22.6 cm (~4.6*c*tau_Xi) + kXiLifetimeVar1, // default variation: 15 cm + kXiLifetimeVar2, // default variation: 12 cm + + // Additional pp-Xi cuts. + kBachBaryonDCAxy0020, // > 0.020 cm + kXiRapidity05, // |y_Xi| < 0.5 + + kNXiStarTopoSelBits +}; +static_assert(kNXiStarTopoSelBits <= 64, "Xi(1530) topology-selection bitmap exceeds uint64_t capacity"); + +constexpr uint64_t xiStarTopoSelMask(XiStarTopoSelBit bit) +{ + return uint64_t{1} << static_cast(bit); +} + +} // namespace redxistark + +// ----------------------------------------------------------------------------- +// Event table +// ----------------------------------------------------------------------------- +namespace redxistarkevent +{ +DECLARE_SOA_COLUMN(FT0MPercentile, fT0MPercentile, float); //! FT0M multiplicity percentile (%) +DECLARE_SOA_COLUMN(Bz, bz, float); //! nominal L3 magnetic field along z (T) +} // namespace redxistarkevent + +DECLARE_SOA_TABLE(RedXiStarKEvents, "AOD", "REDXISTARKEVENT", + o2::soa::Index<>, + collision::PosZ, + collision::NumContrib, + redxistarkevent::FT0MPercentile, + redxistarkevent::Bz); +using RedXiStarKEvent = RedXiStarKEvents::iterator; + +// ----------------------------------------------------------------------------- +// Xi(1530) candidate table +// ----------------------------------------------------------------------------- +namespace redxistarcandidate +{ +DECLARE_SOA_INDEX_COLUMN(RedXiStarKEvent, redXiStarKEvent); + +DECLARE_SOA_COLUMN(Channel, channel, int8_t); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Mass, mass, float); //! reconstructed M(Xi pi) + +DECLARE_SOA_COLUMN(TrackSelectionBits, trackSelectionBits, uint64_t); +DECLARE_SOA_COLUMN(TopologySelectionBits, topologySelectionBits, uint64_t); + +// Prompt-pion kinematics are stored for downstream CPR QA. The three +// displaced Xi daughters remain ID-only. +DECLARE_SOA_COLUMN(XiStarPionPt, xiStarPionPt, float); +DECLARE_SOA_COLUMN(XiStarPionEta, xiStarPionEta, float); +DECLARE_SOA_COLUMN(XiStarPionPhi, xiStarPionPhi, float); +DECLARE_SOA_COLUMN(XiStarPionIndex, xiStarPionIndex, int64_t); +DECLARE_SOA_COLUMN(XiBachelorIndex, xiBachelorIndex, int64_t); +DECLARE_SOA_COLUMN(V0PositiveIndex, v0PositiveIndex, int64_t); +DECLARE_SOA_COLUMN(V0NegativeIndex, v0NegativeIndex, int64_t); + +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float px, float py) -> float { + return std::sqrt(px * px + py * py); +}); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float px, float py, float pz) -> float { + return std::sqrt(px * px + py * py + pz * pz); +}); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float px, float py, float pz) -> float { + const float pt = std::sqrt(px * px + py * py); + return pt > 0.f ? std::asinh(pz / pt) : 0.f; +}); +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, [](float px, float py) -> float { + return std::atan2(py, px); +}); +} // namespace redxistarcandidate + +DECLARE_SOA_TABLE(XiStarCandidates, "AOD", "REDXSTARCAND", + o2::soa::Index<>, + redxistarcandidate::RedXiStarKEventId, + redxistarcandidate::Channel, + redxistarcandidate::Px, + redxistarcandidate::Py, + redxistarcandidate::Pz, + redxistarcandidate::Mass, + redxistarcandidate::TrackSelectionBits, + redxistarcandidate::TopologySelectionBits, + redxistarcandidate::XiStarPionPt, + redxistarcandidate::XiStarPionEta, + redxistarcandidate::XiStarPionPhi, + redxistarcandidate::XiStarPionIndex, + redxistarcandidate::XiBachelorIndex, + redxistarcandidate::V0PositiveIndex, + redxistarcandidate::V0NegativeIndex, + redxistarcandidate::Pt, + redxistarcandidate::P, + redxistarcandidate::Eta, + redxistarcandidate::Phi); +using XiStarCandidate = XiStarCandidates::iterator; + +// ----------------------------------------------------------------------------- +// External primary-kaon table +// ----------------------------------------------------------------------------- +namespace redxistarkaon +{ +DECLARE_SOA_INDEX_COLUMN(RedXiStarKEvent, redXiStarKEvent); + +enum KaonSelBit : uint8_t { + kKaonDCAxyDefault = 0, + kKaonDCAxyVar1, + kKaonDCAxyVar2, + kKaonDCAzDefault, + kKaonDCAzVar1, + kKaonDCAzVar2, + kNKaonSelBits +}; +static_assert(kNKaonSelBits <= 8, "Kaon selection bitmap exceeds uint8_t capacity"); + +constexpr uint8_t kaonSelMask(KaonSelBit bit) +{ + return uint8_t{1} << static_cast(bit); +} + +DECLARE_SOA_COLUMN(Charge, charge, int8_t); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(TrackIndex, trackIndex, int64_t); +DECLARE_SOA_COLUMN(SelectionBits, selectionBits, uint8_t); + +DECLARE_SOA_COLUMN(TPCNClsCrossedRows, tPCNClsCrossedRows, uint8_t); +DECLARE_SOA_COLUMN(TPCNSigmaKa, tPCNSigmaKa, float); +DECLARE_SOA_COLUMN(TOFNSigmaKa, tOFNSigmaKa, float); +DECLARE_SOA_COLUMN(HasTOF, hasTOF, bool); + +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float px, float py) -> float { + return std::sqrt(px * px + py * py); +}); +DECLARE_SOA_DYNAMIC_COLUMN(P, p, [](float px, float py, float pz) -> float { + return std::sqrt(px * px + py * py + pz * pz); +}); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, [](float px, float py, float pz) -> float { + const float pt = std::sqrt(px * px + py * py); + return pt > 0.f ? std::asinh(pz / pt) : 0.f; +}); +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, [](float px, float py) -> float { + return std::atan2(py, px); +}); +} // namespace redxistarkaon + +DECLARE_SOA_TABLE(KaonCandidates, "AOD", "REDXSTARKAON", + o2::soa::Index<>, + redxistarkaon::RedXiStarKEventId, + redxistarkaon::Charge, + redxistarkaon::Px, + redxistarkaon::Py, + redxistarkaon::Pz, + redxistarkaon::TrackIndex, + redxistarkaon::SelectionBits, + redxistarkaon::TPCNClsCrossedRows, + redxistarkaon::TPCNSigmaKa, + redxistarkaon::TOFNSigmaKa, + redxistarkaon::HasTOF, + redxistarkaon::Pt, + redxistarkaon::P, + redxistarkaon::Eta, + redxistarkaon::Phi); +using KaonCandidate = KaonCandidates::iterator; + +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_LFREDUCEDXI1530KAONTABLES_H_ diff --git a/PWGLF/DataModel/LFResonanceTables.h b/PWGLF/DataModel/LFResonanceTables.h index b12eea86652..4c8f3ab7836 100644 --- a/PWGLF/DataModel/LFResonanceTables.h +++ b/PWGLF/DataModel/LFResonanceTables.h @@ -34,9 +34,11 @@ #include +#include #include #include #include +#include namespace o2::aod { @@ -45,7 +47,7 @@ namespace resocollision { enum { kECbegin = 0, - kINEL = 1, + kINEL, kINEL10, kINELg0, kINELg010, @@ -65,6 +67,7 @@ enum { }; DECLARE_SOA_INDEX_COLUMN_FULL(Collision, collision, int, Collisions, "_Col"); //! DECLARE_SOA_COLUMN(Cent, cent, float); //! Centrality (Multiplicity) percentile (Default: FT0M) +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Configurable reconstructed multiplicity estimator DECLARE_SOA_COLUMN(Spherocity, spherocity, float); //! Spherocity of the event DECLARE_SOA_COLUMN(EvtPl, evtPl, float); //! Second harmonic event plane DECLARE_SOA_COLUMN(EvtPlResAB, evtPlResAB, float); //! Second harmonic event plane resolution of A-B sub events @@ -79,10 +82,37 @@ DECLARE_SOA_COLUMN(IsTriggerTVX, isTriggerTVX, bool); //! TriggerTVX DECLARE_SOA_COLUMN(IsInSel8, isInSel8, bool); //! InSel8 DECLARE_SOA_COLUMN(IsInAfterAllCuts, isInAfterAllCuts, bool); //! InAfterAllCuts DECLARE_SOA_COLUMN(ImpactParameter, impactParameter, float); //! ImpactParameter -DECLARE_SOA_COLUMN(MCMultiplicity, mcMultiplicity, float); //! MC Multiplicity +DECLARE_SOA_COLUMN(MCMultiplicity, mcMultiplicity, float); //! MC Multiplicity, o2-linter: disable=name/o2-column (pre-existing public column name kept for schema and API compatibility) } // namespace resocollision -DECLARE_SOA_TABLE(ResoCollisions, "AOD", "RESOCOLLISION", + +// Legacy version-0 companion schema. Its canonical fIndexCollisions column +// enables GroupSlicer association to aod::Collisions, but also makes the table +// dependent on that source parent. The modular initializer therefore writes +// the scalar-only version 001 declared below instead. +namespace resocollisiongroup +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(OriginalCollision, originalCollision, int, Collisions, "Collisions", ""); //! +} // namespace resocollisiongroup + +// Scalar-only original-collision mapping for standalone modular output. Unlike +// resocollisiongroup::OriginalCollisionId above, this column carries no index +// target metadata and therefore does not require the source Collisions table +// to be present while merging the derived AO2D. +namespace resocollisiongroup001 +{ +DECLARE_SOA_COLUMN(OriginalCollisionId, originalCollisionId, int); //! Original aod::Collision row number +} // namespace resocollisiongroup001 + +// Optional soft link from a positional ResoMCCollisions_001 row to its source +// generator collision. It is filled only when the source AO2D is retained as +// a linked-derived-data parent. +namespace resomccollision +{ +DECLARE_SOA_INDEX_COLUMN_FULL_CUSTOM(OriginalMcCollision, originalMcCollision, int, McCollisions, "McCollisions", "_MC"); //! +} // namespace resomccollision + +DECLARE_SOA_TABLE(ResoCollisions_000, "AOD", "RESOCOLLISION", o2::soa::Index<>, o2::aod::mult::MultNTracksPV, o2::aod::mult::MultNTracksPVeta1, @@ -93,12 +123,39 @@ DECLARE_SOA_TABLE(ResoCollisions, "AOD", "RESOCOLLISION", resocollision::Cent, resocollision::BMagField, resocollision::IsRecINELgt0); + +// Version 001 stores one configurable multiplicity estimator instead of three +// fixed PV-track multiplicities. The producer configuration determines which +// reconstructed estimator is persisted in the common float payload. +DECLARE_SOA_TABLE_VERSIONED(ResoCollisions_001, "AOD", "RESOCOLLISION", 1, + o2::soa::Index<>, + resocollision::Multiplicity, + collision::PosX, + collision::PosY, + collision::PosZ, + resocollision::Cent, + resocollision::BMagField, + resocollision::IsRecINELgt0); + +// Keep the established generic collision-table alias on version 000. The +// modular producer and its consumers request version 001 explicitly. +using ResoCollisions = ResoCollisions_000; using ResoCollision = ResoCollisions::iterator; DECLARE_SOA_TABLE(ResoCollisionColls, "AOD", "RESOCOLLISIONCOL", resocollision::CollisionId); using ResoCollisionColl = ResoCollisionColls::iterator; +DECLARE_SOA_TABLE(ResoCollisionGroups, "AOD", "RESOCOLLGROUP", + resocollisiongroup::OriginalCollisionId); +using ResoCollisionGroup = ResoCollisionGroups::iterator; + +// Version 001 replaces the hard relation to the source Collisions table with +// a scalar row number. The legacy version-0 schema remains unchanged. +DECLARE_SOA_TABLE_VERSIONED(ResoCollisionGroups_001, "AOD", "RESOCOLLGROUP", 1, + resocollisiongroup001::OriginalCollisionId); +using ResoCollisionGroup_001 = ResoCollisionGroups_001::iterator; + DECLARE_SOA_TABLE(ResoMCCollisions, "AOD", "RESOMCCOLLISION", o2::soa::Index<>, resocollision::IsVtxIn10, @@ -110,6 +167,20 @@ DECLARE_SOA_TABLE(ResoMCCollisions, "AOD", "RESOMCCOLLISION", resocollision::MCMultiplicity); using ResoMCCollision = ResoMCCollisions::iterator; +// Version 001 deliberately persists generator-collision properties only. +// Reconstructed event-selection decisions remain available only in legacy +// version-0 output and must not be mixed into this generator-level payload. +DECLARE_SOA_TABLE_VERSIONED(ResoMCCollisions_001, "AOD", "RESOMCCOLLISION", 1, + o2::soa::Index<>, + resocollision::IsVtxIn10, + resocollision::IsINELgt0, + resocollision::ImpactParameter, + resocollision::MCMultiplicity); + +DECLARE_SOA_TABLE(ResoMCCollisionIds, "AOD", "RESOMCCOLLID", + resomccollision::OriginalMcCollisionId); +using ResoMCCollisionId = ResoMCCollisionIds::iterator; + DECLARE_SOA_TABLE(ResoSpheroCollisions, "AOD", "RESOSPHEROCOLLISION", o2::soa::Index<>, resocollision::Spherocity); @@ -137,6 +208,7 @@ DECLARE_SOA_TABLE(ResoCollisionDFs, "AOD", "RESOCOLLISIONDF", resocollision::EvtPlResAC, resocollision::EvtPlResBC, resocollision::BMagField, + resocollision::IsRecINELgt0, timestamp::Timestamp, evsel::NumTracksInTimeRange); using ResoCollisionDF = ResoCollisionDFs::iterator; @@ -162,32 +234,53 @@ struct ResoTrackFlags { return (flags & mask) == mask; } }; +// These macros build framework expression nodes and cannot be replaced by +// ordinary constexpr functions without changing their public DSL API. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requireTrackFlag(mask) ((o2::aod::resodaughter::trackFlags & o2::aod::resodaughter::mask) == o2::aod::resodaughter::mask) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requirePassedITSRefit() requireTrackFlag(ResoTrackFlags::kPassedITSRefit) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requirePassedTPCRefit() requireTrackFlag(ResoTrackFlags::kPassedTPCRefit) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requireGlobalTrack() requireTrackFlag(ResoTrackFlags::kIsGlobalTrack) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requireGlobalTrackWoDCA() requireTrackFlag(ResoTrackFlags::kIsGlobalTrackWoDCA) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requirePrimaryTrack() requireTrackFlag(ResoTrackFlags::kIsPrimaryTrack) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requirePVContributor() requireTrackFlag(ResoTrackFlags::kIsPVContributor) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requireHasTOF() requireTrackFlag(ResoTrackFlags::kHasTOF) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define requireSign() requireTrackFlag(ResoTrackFlags::kSign) -#define DECLARE_DYN_TRKSEL_COLUMN(name, getter, mask) \ - DECLARE_SOA_DYNAMIC_COLUMN(name, getter, [](ResoTrackFlags::flagtype flags) -> bool { return ResoTrackFlags::checkFlag(flags, mask); }); +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DECLARE_DYN_TRKSEL_COLUMN(_Name_, _Getter_, _Mask_) \ + DECLARE_SOA_DYNAMIC_COLUMN(_Name_, _Getter_, [](ResoTrackFlags::flagtype flags) -> bool { return ResoTrackFlags::checkFlag(flags, _Mask_); }); +// Keep the default foreign-key target on the legacy ResoCollisions_000 alias. +// Modular v001 consumers use resoCollisionId() as the scalar row reference and +// explicitly call resoCollision_as() only when the parent row must be +// dereferenced, with T equal to the exact bound v001 table type (plain +// aod::ResoCollisions_001 or its analysis Join). The version-equivalence +// declaration below permits binding the same physical index column to v001. DECLARE_SOA_INDEX_COLUMN(ResoCollision, resoCollision); DECLARE_SOA_INDEX_COLUMN(ResoCollisionDF, resoCollisionDF); -DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Trk"); //! -DECLARE_SOA_INDEX_COLUMN_FULL(V0, v0, int, V0s, "_V0"); //! -DECLARE_SOA_INDEX_COLUMN_FULL(Cascade, cascade, int, Cascades, "_Cas"); //! -DECLARE_SOA_COLUMN(Pt, pt, float); //! p_t (GeV/c) -DECLARE_SOA_COLUMN(Px, px, float); //! p_x (GeV/c) -DECLARE_SOA_COLUMN(Py, py, float); //! p_y (GeV/c) -DECLARE_SOA_COLUMN(Pz, pz, float); //! p_z (GeV/c) -DECLARE_SOA_COLUMN(PartType, partType, uint8_t); //! Type of the particle, according to resodaughter::ParticleType -DECLARE_SOA_COLUMN(TempFitVar, tempFitVar, float); //! Observable for the template fitting (Track: DCA_xy, V0: CPA) -DECLARE_SOA_COLUMN(Indices, indices, int[2]); //! Field for the track indices to remove auto-correlations +DECLARE_SOA_INDEX_COLUMN_FULL(Track, track, int, Tracks, "_Trk"); //! Soft link to the original track +DECLARE_SOA_INDEX_COLUMN_FULL(V0, v0, int, V0s, "_V0"); //! Soft link to the original V0 +DECLARE_SOA_INDEX_COLUMN_FULL(Cascade, cascade, int, Cascades, "_Cas"); //! Soft link to the original cascade +DECLARE_SOA_COLUMN(Pt, pt, float); //! p_t (GeV/c) +DECLARE_SOA_COLUMN(Px, px, float); //! p_x (GeV/c) +DECLARE_SOA_COLUMN(Py, py, float); //! p_y (GeV/c) +DECLARE_SOA_COLUMN(Pz, pz, float); //! p_z (GeV/c) +DECLARE_SOA_COLUMN(PartType, partType, uint8_t); //! Type of the particle, according to resodaughter::ParticleType +DECLARE_SOA_COLUMN(TempFitVar, tempFitVar, float); //! Observable for the template fitting (Track: DCA_xy, V0: CPA) +// Fixed-size C arrays are part of the existing persistent AOD schema. +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +DECLARE_SOA_COLUMN(Indices, indices, int[2]); //! Field for the track indices to remove auto-correlations +// NOLINTNEXTLINE(modernize-avoid-c-arrays) DECLARE_SOA_COLUMN(CascadeIndices, cascadeIndices, int[3]); //! Field for the track indices to remove auto-correlations (ordered: positive, negative, bachelor) DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, uint8_t); //! Number of TPC crossed rows DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, uint8_t); //! Number of TPC clusters found @@ -214,8 +307,8 @@ DECLARE_SOA_COLUMN(DecayVtxX, decayVtxX, float); DECLARE_SOA_COLUMN(DecayVtxY, decayVtxY, float); //! Y position of the decay vertex DECLARE_SOA_COLUMN(DecayVtxZ, decayVtxZ, float); //! Z position of the decay vertex DECLARE_SOA_COLUMN(Alpha, alpha, float); //! Alpha of the decay vertex -DECLARE_SOA_COLUMN(QtArm, qtarm, float); //! Armenteros Qt of the decay vertex -DECLARE_SOA_COLUMN(TpcSignal10, tpcSignal10, int16_t); //! TPC signal of the track x10 +DECLARE_SOA_COLUMN(QtArm, qtarm, float); //! Armenteros Qt of the decay vertex, o2-linter: disable=name/o2-column (pre-existing public column name kept for schema and API compatibility) +DECLARE_SOA_COLUMN(TpcSignal10, tpcSignal10, int16_t); //! TPC signal of the track x100 (public column name retained for compatibility) DECLARE_SOA_COLUMN(DaughterTPCNSigmaPosPi10, daughterTPCNSigmaPosPi10, int8_t); //! TPC PID x10 of the positive daughter as Pion DECLARE_SOA_COLUMN(DaughterTPCNSigmaPosKa10, daughterTPCNSigmaPosKa10, int8_t); //! TPC PID x10 of the positive daughter as Kaon DECLARE_SOA_COLUMN(DaughterTPCNSigmaPosPr10, daughterTPCNSigmaPosPr10, int8_t); //! TPC PID x10 of the positive daughter as Proton @@ -249,9 +342,10 @@ DECLARE_SOA_COLUMN(DaughterPDG1, daughterPDG1, int); //! PDG code of the first D DECLARE_SOA_COLUMN(DaughterPDG2, daughterPDG2, int); //! PDG code of the second Daughter particle DECLARE_SOA_COLUMN(DaughterID1, daughterID1, int); //! Id of the first Daughter particle DECLARE_SOA_COLUMN(DaughterID2, daughterID2, int); //! Id of the second Daughter particle -DECLARE_SOA_COLUMN(SiblingIds, siblingIds, int[2]); //! Index of the particles with the same mother -DECLARE_SOA_COLUMN(BachTrkID, bachTrkID, int); //! Id of the bach track from cascade -DECLARE_SOA_COLUMN(V0ID, v0ID, int); //! Id of the V0 from cascade +// NOLINTNEXTLINE(modernize-avoid-c-arrays) -- persistent fixed-size AOD column +DECLARE_SOA_COLUMN(SiblingIds, siblingIds, int[2]); //! Index of the particles with the same mother +DECLARE_SOA_COLUMN(BachTrkID, bachTrkID, int); //! Id of the bach track from cascade +DECLARE_SOA_COLUMN(V0ID, v0ID, int); //! Id of the V0 from cascade // Dynamic columns // DCA_xy x10,000 DECLARE_SOA_DYNAMIC_COLUMN(DcaXY, dcaXY, @@ -308,7 +402,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(DaughterTOFNSigmaBachKa, daughterTOFNSigmaBachKa, [](int8_t daughterTOFNSigmaBachKa10) { return (float)daughterTOFNSigmaBachKa10 / 10.f; }); DECLARE_SOA_DYNAMIC_COLUMN(DaughterTOFNSigmaBachPr, daughterTOFNSigmaBachPr, [](int8_t daughterTOFNSigmaBachPr10) { return (float)daughterTOFNSigmaBachPr10 / 10.f; }); -// TPC signal x10 +// TPC signal x100 DECLARE_SOA_DYNAMIC_COLUMN(TpcSignal, tpcSignal, [](int16_t tpcSignal10) { return (float)tpcSignal10 / 100.f; }); // pT, Eta, Phi @@ -357,6 +451,10 @@ namespace resomicrodaughter /// @brief Save TPC & TOF nSigma info with 8-bit variable struct PidNSigma { + static constexpr double MinNSigma = 1.5; + static constexpr double NSigmaStep = 0.2; + static constexpr uint8_t MaxNSigmaCode = 10; + uint8_t flag; /// @brief Constructor: Convert TPC & TOF values and save @@ -371,29 +469,34 @@ struct PidNSigma { static uint8_t encodeNSigma(float nSigma) { const float x = std::abs(nSigma); - if (x <= 1.5) + if (x <= MinNSigma) { return 0; // Return 0 when absolute nSigma is smaller than 1.5 - float t = (x - 1.5) / 0.2; + } + float t = (x - MinNSigma) / NSigmaStep; int encoded = static_cast(std::ceil(t)); // (1.5,1.7]->1, ..., (3.3,3.5]->10 - if (encoded < 1) + if (encoded < 1) { encoded = 1; - if (encoded > 10) - encoded = 10; + } + if (encoded > MaxNSigmaCode) { + encoded = MaxNSigmaCode; + } return static_cast(encoded); } /// @brief Decode 0~10 value to original 1.5~3.5 sigma range static float decodeNSigma(uint8_t encoded) { - if (encoded == 0) - return 1.5; - if (encoded > 10) - encoded = 10; - return 1.5 + static_cast(encoded) * 0.2; + if (encoded == 0) { + return MinNSigma; + } + if (encoded > MaxNSigmaCode) { + encoded = MaxNSigmaCode; + } + return MinNSigma + static_cast(encoded) * NSigmaStep; } /// @brief Check if TOF info is available - bool hasTOF() const + [[nodiscard]] bool hasTOF() const { return (flag & 0x0F) != 0x0F; // Check if lower 4 bits are not all 1 } @@ -429,12 +532,16 @@ DECLARE_SOA_DYNAMIC_COLUMN(HasTOF, hasTOF, /// @brief DCAxy & DCAz selection flag struct ResoMicroTrackSelFlag { + static constexpr double DCAEncodingStep = 0.1; + static constexpr uint8_t MaxRegularDCAFlag = 14; + static constexpr uint8_t OverflowDCAFlag = 15; + uint8_t flag; // Flag for DCAxy & DCAz selection (8-bit variable) /// @brief Default constructor ResoMicroTrackSelFlag() + : flag(0x00) { - flag = 0x00; } /// @brief Constructor: Convert DCAxy/DCAz and save (default 1~15 values) @@ -449,13 +556,16 @@ struct ResoMicroTrackSelFlag { static uint8_t encodeDCA(float DCA) { float x = std::fabs(DCA); - if (x < 0.1) + if (x < DCAEncodingStep) { return 0; - int encoded = static_cast(std::ceil((x - 0.1) / 0.1)); // (0.1, 0.2] -> 1, ..., (1.4, 1.5] -> 14 - if (encoded < 1) + } + int encoded = static_cast(std::ceil((x - DCAEncodingStep) / DCAEncodingStep)); // (0.1, 0.2] -> 1, ..., (1.4, 1.5] -> 14 + if (encoded < 1) { encoded = 1; - if (encoded > 14) - encoded = 15; + } + if (encoded > MaxRegularDCAFlag) { + encoded = OverflowDCAFlag; + } return static_cast(encoded); } @@ -466,13 +576,13 @@ struct ResoMicroTrackSelFlag { } /// @brief Get DCAxy value - uint8_t getDCAxyFlag() const + [[nodiscard]] uint8_t getDCAxyFlag() const { return (flag >> 4) & 0x0F; // Extract upper 4 bits } /// @brief Get DCAz value - uint8_t getDCAzFlag() const + [[nodiscard]] uint8_t getDCAzFlag() const { return flag & 0x0F; // Extract lower 4 bits } @@ -506,6 +616,361 @@ struct ResoMicroTrackSelFlag { DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, [](float px, float py) -> float { return RecoDecay::sqrtSumOfSquares(px, py); }); } // namespace resomicrodaughter +// Version 001 uses signed, lower-inclusive PID bins and keeps the DCAxy/DCAz +// pT-dependent selection results independently from the quantised DCA values. +namespace resomicrodaughter001 +{ +// Keep the original row number as a scalar for standalone pair comparisons. +// The separate typed relation remains available to legacy version-0 users. +DECLARE_SOA_COLUMN(TrackId, trackId, int); //! Original track row number for pair-level comparisons + +/// @brief Compact signed TPC/TOF n-sigma values into two four-bit fields. +/// The upper bit of each field stores the sign. Magnitudes 1..6 represent +/// lower-inclusive 0.25-sigma-wide bins [2.0, 2.25), ..., [3.25, 3.5), while +/// magnitude 0 is |n-sigma| < 2 and magnitude 7 is |n-sigma| >= 3.5. Overflow +/// decodes to signed infinity, while code 8 is reserved for invalid values and +/// decodes to NaN. Missing TOF information is also carried independently by +/// resodaughter::TrackFlags::kHasTOF. +struct PidNSigma { + static constexpr float MinFineNSigma = 2.f; + static constexpr float Step = 0.25f; + static constexpr float MaxNSigma = 3.5f; + static constexpr uint8_t SignMask = 0x08; + static constexpr uint8_t MagnitudeMask = 0x07; + static constexpr uint8_t MaxRegularCode = 6; + static constexpr uint8_t AboveRangeCode = 7; + static constexpr uint8_t InvalidCode = SignMask; + + uint8_t flag; + + PidNSigma(float tpcNSigma, float tofNSigma, bool hasTOF) + { + const uint8_t tpcEncoded = encodeNSigma(tpcNSigma); + const uint8_t tofEncoded = hasTOF ? encodeNSigma(tofNSigma) : InvalidCode; + flag = (tpcEncoded << 4) | tofEncoded; + } + + static uint8_t encodeNSigma(float nSigma) + { + if (!std::isfinite(nSigma)) { + return InvalidCode; + } + const float value = std::abs(nSigma); + if (value < MinFineNSigma) { + return 0; + } + uint8_t magnitude = AboveRangeCode; + if (value < MaxNSigma) { + const int encoded = 1 + static_cast(std::floor((value - MinFineNSigma) / Step)); + magnitude = static_cast(std::clamp(encoded, 1, static_cast(MaxRegularCode))); + } + const uint8_t sign = std::signbit(nSigma) ? SignMask : 0; + return static_cast(sign | magnitude); + } + + static float decodeNSigma(uint8_t encoded) + { + const uint8_t code = encoded & 0x0F; + if (code == InvalidCode) { + return NAN; + } + const uint8_t magnitude = code & MagnitudeMask; + if (magnitude == 0) { + return 0.f; + } + const float value = magnitude == AboveRangeCode + ? std::numeric_limits::infinity() + : MinFineNSigma + static_cast(magnitude - 1) * Step; + return (code & SignMask) != 0 ? -value : value; + } + + static float getTPCNSigma(uint8_t encoded) + { + return decodeNSigma((encoded >> 4) & 0x0F); + } + + static float getTOFNSigma(uint8_t encoded, bool hasTOF) + { + return hasTOF ? decodeNSigma(encoded & 0x0F) : NAN; + } + + operator uint8_t() const { return flag; } +}; + +/// @brief Store two pT-dependent DCA pass bits and two three-bit DCA values. +/// Bits 7 and 6 store the DCAxy and DCAz pass results, respectively. Bits 5..3 +/// and 2..0 store |DCAxy| and |DCAz| in lower-inclusive 0.025 cm bins from 0 +/// to 0.15 cm. Code 6 represents overflow and code 7 an invalid value. +struct DCAEncoding { + static constexpr float MaxDCA = 0.15f; + static constexpr float Step = 0.025f; + static constexpr float InverseStep = 40.f; + static constexpr uint8_t MaxRegularCode = 5; + static constexpr uint8_t AboveRangeCode = 6; + static constexpr uint8_t InvalidCode = 7; + static constexpr uint8_t PassedPtDependentDCAxyMask = 0x80; + static constexpr uint8_t PassedPtDependentDCAzMask = 0x40; + static constexpr uint8_t DCAxyMask = 0x38; + static constexpr uint8_t DCAzMask = 0x07; + static constexpr uint8_t DCAxyShift = 3; + + uint8_t flag = 0; + + DCAEncoding() = default; + DCAEncoding(float dcaXY, float dcaZ, bool passedPtDependentDCAxy, bool passedPtDependentDCAz) + : flag(static_cast((passedPtDependentDCAxy ? PassedPtDependentDCAxyMask : 0) | + (passedPtDependentDCAz ? PassedPtDependentDCAzMask : 0) | + (encodeDCA(dcaXY) << DCAxyShift) | + encodeDCA(dcaZ))) + { + } + + static uint8_t encodeDCA(float dca) + { + const float value = std::abs(dca); + if (!std::isfinite(value)) { + return InvalidCode; + } + if (value >= MaxDCA) { + return AboveRangeCode; + } + const int encoded = static_cast(std::floor(value * InverseStep)); + return static_cast(std::clamp(encoded, 0, static_cast(MaxRegularCode))); + } + + static float decodeDCA(uint8_t encoded) + { + const uint8_t code = encoded & DCAzMask; + if (code == InvalidCode) { + return NAN; + } + return code == AboveRangeCode ? MaxDCA : static_cast(code) * Step; + } + + static float decodeDCAxy(uint8_t encoded) + { + return decodeDCA((encoded & DCAxyMask) >> DCAxyShift); + } + + static float decodeDCAz(uint8_t encoded) + { + return decodeDCA(encoded & DCAzMask); + } + + static bool testPtDependentDCAxy(uint8_t encoded) + { + return (encoded & PassedPtDependentDCAxyMask) != 0; + } + + static bool testPtDependentDCAz(uint8_t encoded) + { + return (encoded & PassedPtDependentDCAzMask) != 0; + } + + operator uint8_t() const { return flag; } +}; + +DECLARE_SOA_COLUMN(TrackSelectionFlags, trackSelectionFlags, uint8_t); //! Packed DCA selection and absolute DCAxy/DCAz values +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaPi, tpcNSigmaPi, + [](uint8_t pidNSigmaPiFlag) { return PidNSigma::getTPCNSigma(pidNSigmaPiFlag); }); +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaKa, tpcNSigmaKa, + [](uint8_t pidNSigmaKaFlag) { return PidNSigma::getTPCNSigma(pidNSigmaKaFlag); }); +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigmaPr, tpcNSigmaPr, + [](uint8_t pidNSigmaPrFlag) { return PidNSigma::getTPCNSigma(pidNSigmaPrFlag); }); +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaPi, tofNSigmaPi, + [](uint8_t pidNSigmaPiFlag, uint8_t trackFlags) -> float { + const bool hasTOF = resodaughter::ResoTrackFlags::checkFlag(trackFlags, resodaughter::ResoTrackFlags::kHasTOF); + return PidNSigma::getTOFNSigma(pidNSigmaPiFlag, hasTOF); + }); +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaKa, tofNSigmaKa, + [](uint8_t pidNSigmaKaFlag, uint8_t trackFlags) -> float { + const bool hasTOF = resodaughter::ResoTrackFlags::checkFlag(trackFlags, resodaughter::ResoTrackFlags::kHasTOF); + return PidNSigma::getTOFNSigma(pidNSigmaKaFlag, hasTOF); + }); +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigmaPr, tofNSigmaPr, + [](uint8_t pidNSigmaPrFlag, uint8_t trackFlags) -> float { + const bool hasTOF = resodaughter::ResoTrackFlags::checkFlag(trackFlags, resodaughter::ResoTrackFlags::kHasTOF); + return PidNSigma::getTOFNSigma(pidNSigmaPrFlag, hasTOF); + }); +DECLARE_SOA_DYNAMIC_COLUMN(DcaXY, dcaXY, + [](uint8_t trackSelectionFlags) { return DCAEncoding::decodeDCAxy(trackSelectionFlags); }); +DECLARE_SOA_DYNAMIC_COLUMN(DcaZ, dcaZ, + [](uint8_t trackSelectionFlags) { return DCAEncoding::decodeDCAz(trackSelectionFlags); }); +DECLARE_SOA_DYNAMIC_COLUMN(PassedPtDependentDCAxy, passedPtDependentDCAxy, + [](uint8_t trackSelectionFlags) { return DCAEncoding::testPtDependentDCAxy(trackSelectionFlags); }); +DECLARE_SOA_DYNAMIC_COLUMN(PassedPtDependentDCAz, passedPtDependentDCAz, + [](uint8_t trackSelectionFlags) { return DCAEncoding::testPtDependentDCAz(trackSelectionFlags); }); +DECLARE_SOA_DYNAMIC_COLUMN(HasITSHitInLayer, hasITSHitInLayer, + [](uint8_t itsClusterMap, int layer) -> bool { + constexpr int NumberOfITSLayers = 7; + if (layer < 0 || layer >= NumberOfITSLayers) { + return false; + } + return (itsClusterMap & (1u << layer)) != 0; + }); +} // namespace resomicrodaughter001 + +// Ultra-micro track representation. The momentum components are quantised +// to 1 MeV/c and only one (pion/kaon/proton) PID flag is retained. +namespace resoultramicrodaughter +{ +/// @brief Compact absolute TPC/TOF n-sigma values for one configured species. +/// Code 0 represents |n-sigma| < 1.5. Codes 1..14 represent lower-inclusive +/// 0.25-sigma-wide bins [1.5, 1.75), ..., [4.75, 5.0) and decode to their +/// lower edges. Code 15 represents |n-sigma| >= 5. Missing TOF information +/// is carried independently by resodaughter::TrackFlags::kHasTOF. +struct PidNSigma { + static constexpr float MinFineNSigma = 1.5f; + static constexpr float Step = 0.25f; + static constexpr float MaxNSigma = 5.f; + static constexpr uint8_t MaxRegularCode = 14; + static constexpr uint8_t AboveRangeCode = 15; + + uint8_t flag; + + PidNSigma(float tpcNSigma, float tofNSigma, bool hasTOF) + { + const uint8_t tpcEncoded = encodeNSigma(tpcNSigma); + const uint8_t tofEncoded = hasTOF && std::isfinite(tofNSigma) ? encodeNSigma(tofNSigma) : AboveRangeCode; + flag = (tpcEncoded << 4) | tofEncoded; + } + + static uint8_t encodeNSigma(float nSigma) + { + const float value = std::abs(nSigma); + if (!std::isfinite(value)) { + return AboveRangeCode; + } + if (value < MinFineNSigma) { + return 0; + } + if (value >= MaxNSigma) { + return AboveRangeCode; + } + const int encoded = 1 + static_cast(std::floor((value - MinFineNSigma) / Step)); + return static_cast(std::clamp(encoded, 1, static_cast(MaxRegularCode))); + } + + static float decodeNSigma(uint8_t encoded) + { + const uint8_t code = encoded & 0x0F; + if (code == 0) { + return 0.f; + } + return code == AboveRangeCode ? MaxNSigma : MinFineNSigma + static_cast(code - 1) * Step; + } + + static float getTPCNSigma(uint8_t encoded) + { + return decodeNSigma((encoded >> 4) & 0x0F); + } + + static float getTOFNSigma(uint8_t encoded, bool hasTOF) + { + return hasTOF ? decodeNSigma(encoded & 0x0F) : NAN; + } + + operator uint8_t() const { return flag; } +}; + +/// @brief Compact absolute DCAxy/DCAz values into two four-bit fields. +/// Codes 0..14 represent lower-inclusive 0.01 cm-wide bins [0.00, 0.01), +/// ..., [0.14, 0.15) cm and decode to their lower edges. Code 15 directly +/// represents |DCA| >= 0.15 cm and decodes to the 0.15 cm marker; no additional +/// tight-DCA or overflow flag is stored. +struct DCAEncoding { + static constexpr float MaxDCA = 0.15f; + static constexpr float Step = 0.01f; + static constexpr uint8_t MaxRegularCode = 14; + static constexpr uint8_t AboveRangeCode = 15; + + uint8_t flag = 0; + + DCAEncoding() = default; + DCAEncoding(float dcaXY, float dcaZ) + : flag(static_cast((encodeDCA(dcaXY) << 4) | encodeDCA(dcaZ))) + { + } + + static bool isValid(float dca) + { + return std::isfinite(dca); + } + + static uint8_t encodeDCA(float dca) + { + const float value = std::abs(dca); + if (!std::isfinite(value)) { + return AboveRangeCode; + } + if (value >= MaxDCA) { + return AboveRangeCode; + } + const int encoded = static_cast(std::floor(value / Step)); + return static_cast(std::clamp(encoded, 0, static_cast(MaxRegularCode))); + } + + static float decodeDCA(uint8_t encoded) + { + const uint8_t code = encoded & 0x0F; + return code == AboveRangeCode ? MaxDCA : static_cast(code) * Step; + } + + static float decodeDCAxy(uint8_t encoded) + { + return decodeDCA((encoded >> 4) & 0x0F); + } + + static float decodeDCAz(uint8_t encoded) + { + return decodeDCA(encoded & 0x0F); + } + + operator uint8_t() const { return flag; } +}; + +DECLARE_SOA_COLUMN(PidNSigmaFlag, pidNSigmaFlag, uint8_t); //! TPC/TOF PID flag for the configured species +DECLARE_SOA_COLUMN(TrackSelectionFlags, trackSelectionFlags, uint8_t); //! Packed absolute DCAxy/DCAz values +DECLARE_SOA_COLUMN(Px1000, px1000, int16_t); //! p_x x 1000 (GeV/c) +DECLARE_SOA_COLUMN(Py1000, py1000, int16_t); //! p_y x 1000 (GeV/c) +DECLARE_SOA_COLUMN(Pz1000, pz1000, int16_t); //! p_z x 1000 (GeV/c) + +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, + [](int16_t px1000) { return static_cast(px1000) / 1000.f; }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, + [](int16_t py1000) { return static_cast(py1000) / 1000.f; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, + [](int16_t pz1000) { return static_cast(pz1000) / 1000.f; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, + [](int16_t px1000, int16_t py1000) { + const float px = static_cast(px1000) / 1000.f; + const float py = static_cast(py1000) / 1000.f; + return RecoDecay::sqrtSumOfSquares(px, py); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, + [](int16_t px1000, int16_t py1000, int16_t pz1000) { + return RecoDecay::eta(std::array{static_cast(px1000) / 1000.f, + static_cast(py1000) / 1000.f, + static_cast(pz1000) / 1000.f}); + }); +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, + [](int16_t px1000, int16_t py1000) { + return RecoDecay::phi(static_cast(px1000) / 1000.f, + static_cast(py1000) / 1000.f); + }); +DECLARE_SOA_DYNAMIC_COLUMN(TpcNSigma, tpcNSigma, + [](uint8_t pidNSigmaFlag) { return PidNSigma::getTPCNSigma(pidNSigmaFlag); }); +DECLARE_SOA_DYNAMIC_COLUMN(TofNSigma, tofNSigma, + [](uint8_t pidNSigmaFlag, uint8_t trackFlags) -> float { + const bool hasTOF = resodaughter::ResoTrackFlags::checkFlag(trackFlags, resodaughter::ResoTrackFlags::kHasTOF); + return PidNSigma::getTOFNSigma(pidNSigmaFlag, hasTOF); + }); +DECLARE_SOA_DYNAMIC_COLUMN(DcaXY, dcaXY, + [](uint8_t trackSelectionFlags) { return DCAEncoding::decodeDCAxy(trackSelectionFlags); }); +DECLARE_SOA_DYNAMIC_COLUMN(DcaZ, dcaZ, + [](uint8_t trackSelectionFlags) { return DCAEncoding::decodeDCAz(trackSelectionFlags); }); +} // namespace resoultramicrodaughter + DECLARE_SOA_TABLE(ResoTracks, "AOD", "RESOTRACK", o2::soa::Index<>, resodaughter::ResoCollisionId, @@ -577,10 +1042,90 @@ DECLARE_SOA_TABLE(ResoMicroTracks, "AOD", "RESOMICROTRACK", resodaughter::Sign); using ResoMicroTrack = ResoMicroTracks::iterator; +// Keep ResoMicroTracks as the version-0 API for existing producers and +// consumers. Version-1 users must request ResoMicroTracks_001 explicitly. +// Version 1 includes TPC crossed rows and the ITS cluster map; older version-1 +// files without these columns must be regenerated or explicitly converted. +DECLARE_SOA_TABLE_VERSIONED(ResoMicroTracks_001, "AOD", "RESOMICROTRACK", 1, + o2::soa::Index<>, + resodaughter::ResoCollisionId, + resomicrodaughter001::TrackId, + resodaughter::Px, + resodaughter::Py, + resodaughter::Pz, + resomicrodaughter::PidNSigmaPiFlag, + resomicrodaughter::PidNSigmaKaFlag, + resomicrodaughter::PidNSigmaPrFlag, + resomicrodaughter001::TrackSelectionFlags, + resodaughter::TrackFlags, + resodaughter::TpcNClsCrossedRows, + track::ITSClusterMap, + // Dynamic columns + resomicrodaughter::Pt, + resodaughter::Eta, + resodaughter::Phi, + resomicrodaughter001::TpcNSigmaPi, + resomicrodaughter001::TpcNSigmaKa, + resomicrodaughter001::TpcNSigmaPr, + resomicrodaughter001::TofNSigmaPi, + resomicrodaughter001::TofNSigmaKa, + resomicrodaughter001::TofNSigmaPr, + resomicrodaughter001::DcaXY, + resomicrodaughter001::DcaZ, + resomicrodaughter001::PassedPtDependentDCAxy, + resomicrodaughter001::PassedPtDependentDCAz, + resodaughter::PassedITSRefit, + resodaughter::PassedTPCRefit, + resodaughter::IsGlobalTrackWoDCA, + resodaughter::IsGlobalTrack, + resodaughter::IsPrimaryTrack, + resodaughter::IsPVContributor, + resodaughter::HasTOF, + resodaughter::Sign, + track::ITSNCls, + track::ITSNClsInnerBarrel, + resomicrodaughter001::HasITSHitInLayer); +// Positional soft-link side table retained for ResoMicroTracks version 000. +// Version 001 stores the same row number as a scalar and should be consumed +// without joining this side table, since both columns expose trackId(). DECLARE_SOA_TABLE(ResoMicroTrackTracks, "AOD", "RESOMICROTRACKTRACK", resodaughter::TrackId); using ResoMicroTrackTrack = ResoMicroTrackTracks::iterator; +DECLARE_SOA_TABLE(ResoUltraMicroTracks, "AOD", "RESOULTRAMTRK", + o2::soa::Index<>, + resodaughter::ResoCollisionId, + resoultramicrodaughter::Px1000, + resoultramicrodaughter::Py1000, + resoultramicrodaughter::Pz1000, + resoultramicrodaughter::PidNSigmaFlag, + resoultramicrodaughter::TrackSelectionFlags, + resodaughter::TrackFlags, + // Dynamic columns + resoultramicrodaughter::Px, + resoultramicrodaughter::Py, + resoultramicrodaughter::Pz, + resoultramicrodaughter::Pt, + resoultramicrodaughter::Eta, + resoultramicrodaughter::Phi, + resoultramicrodaughter::TpcNSigma, + resoultramicrodaughter::TofNSigma, + resoultramicrodaughter::DcaXY, + resoultramicrodaughter::DcaZ, + resodaughter::PassedITSRefit, + resodaughter::PassedTPCRefit, + resodaughter::IsGlobalTrackWoDCA, + resodaughter::IsGlobalTrack, + resodaughter::IsPrimaryTrack, + resodaughter::IsPVContributor, + resodaughter::HasTOF, + resodaughter::Sign); +using ResoUltraMicroTrack = ResoUltraMicroTracks::iterator; + +DECLARE_SOA_TABLE(ResoUltraMicroTrackTracks, "AOD", "RESOULTRAMTRKID", + resodaughter::TrackId); +using ResoUltraMicroTrackTrack = ResoUltraMicroTrackTracks::iterator; + // For DF mixing study DECLARE_SOA_TABLE(ResoTrackDFs, "AOD", "RESOTRACKDF", o2::soa::Index<>, @@ -831,6 +1376,17 @@ DECLARE_SOA_TABLE(ResoMCTracks, "AOD", "RESOMCTRACK", resodaughter::ProducedByGenerator); using ResoMCTrack = ResoMCTracks::iterator; +// Positional MC extension for ResoMicroTracks_001. One row must be written for +// every ResoMicroTracks_001 row, including tracks without an MC particle label. +DECLARE_SOA_TABLE_VERSIONED(ResoMCMicroTracks_001, "AOD", "RESOMCMICROTRACK", 1, + mcparticle::PdgCode, + resodaughter::MotherId, + resodaughter::MotherPDG, + resodaughter::SiblingIds, + resodaughter::IsPhysicalPrimary, + resodaughter::ProducedByGenerator); +using ResoMCMicroTrack = ResoMCMicroTracks_001::iterator; + DECLARE_SOA_TABLE(ResoMCV0s, "AOD", "RESOMCV0", mcparticle::PdgCode, resodaughter::MotherId, @@ -880,6 +1436,34 @@ DECLARE_SOA_TABLE(ResoMCParents, "AOD", "RESOMCPARENT", resodaughter::Phi); using ResoMCParent = ResoMCParents::iterator; +// Module-specific parent rows keep the source MC-particle row number as a +// scalar. This avoids a hard relation to McParticles in standalone derived +// AO2D while preserving the legacy ResoMCParents schema and typed API. +namespace resomcparent001 +{ +DECLARE_SOA_COLUMN(OriginalMcParticleId, originalMcParticleId, int); //! Original aod::McParticle row number +} // namespace resomcparent001 + +DECLARE_SOA_TABLE_VERSIONED(ResoMCParents_001, "AOD", "RESOMCPARENT", 1, + o2::soa::Index<>, + resodaughter::ResoCollisionId, + resomcparent001::OriginalMcParticleId, + mcparticle::PdgCode, + resodaughter::DaughterPDG1, + resodaughter::DaughterPDG2, + resodaughter::IsPhysicalPrimary, + resodaughter::ProducedByGenerator, + resodaughter::Pt, + resodaughter::Px, + resodaughter::Py, + resodaughter::Pz, + mcparticle::Y, + mcparticle::E, + mcparticle::StatusCode, + resodaughter::Eta, + resodaughter::Phi); +using ResoMCParent_001 = ResoMCParents_001::iterator; + using Reso2TracksExt = soa::Join; // without Extra using Reso2TracksMC = soa::Join; using Reso2TracksPID = soa::Join; @@ -898,4 +1482,11 @@ using ResoCascadesCandidatesMC = soa::Join; } // namespace o2::aod + +namespace o2::soa +{ +// Preserve the legacy v000 default target while allowing an explicitly typed +// resoCollision_as() binding to the exact v001 parent in modular analyses. +DECLARE_EQUIVALENT_FOR_INDEX(aod::ResoCollisions_000, aod::ResoCollisions_001); +} // namespace o2::soa #endif // PWGLF_DATAMODEL_LFRESONANCETABLES_H_ diff --git a/PWGLF/DataModel/LFSigmaHadTables.h b/PWGLF/DataModel/LFSigmaHadTables.h index 77c91c30349..984aa9005a5 100644 --- a/PWGLF/DataModel/LFSigmaHadTables.h +++ b/PWGLF/DataModel/LFSigmaHadTables.h @@ -36,14 +36,17 @@ DECLARE_SOA_COLUMN(PyHad, pyHad, float); //! Py of the hadron cand DECLARE_SOA_COLUMN(PzHad, pzHad, float); //! Pz of the hadron candidate DECLARE_SOA_COLUMN(NSigmaTPCHad, nSigmaTPCHad, float); //! Number of sigmas for the hadron candidate from Sigma kink in TPC DECLARE_SOA_COLUMN(NSigmaTOFHad, nSigmaTOFHad, float); //! Number of sigmas for the hadron candidate from Sigma kink in TOF +DECLARE_SOA_COLUMN(Multiplicity, multiplicity, float); //! Collision multiplicity // MC Columns -DECLARE_SOA_COLUMN(SigmaPDG, sigmaPDG, int); //! PDG code of the Sigma daughter -DECLARE_SOA_COLUMN(DaughterPDG, daughterPDG, int); //! PDG code of the kink daughter -DECLARE_SOA_COLUMN(HadPDG, hadPDG, int); //! PDG code of the hadron candidate -DECLARE_SOA_COLUMN(SigmaGenPt, sigmaGenPt, float); //! Generated pT of the Sigma candidate -DECLARE_SOA_COLUMN(HadGenPt, hadGenPt, float); //! Generated pT of the hadron candidate -DECLARE_SOA_COLUMN(GenKStar, genKStar, float); //! Generated k* of the Sigma-hadron pair +DECLARE_SOA_COLUMN(SigmaPDG, sigmaPDG, int); //! PDG code of the Sigma candidate +DECLARE_SOA_COLUMN(DaughterPDG, daughterPDG, int); //! PDG code of the kink daughter +DECLARE_SOA_COLUMN(HadPDG, hadPDG, int); //! PDG code of the hadron candidate +DECLARE_SOA_COLUMN(SigmaMotherPDG, sigmaMotherPDG, int); //! PDG code of the direct mother of the Sigma +DECLARE_SOA_COLUMN(SigmaPartonicMotherPDG, sigmaPartonicMotherPDG, int); //! PDG code of the partonic (quark/gluon) ancestor of the Sigma +DECLARE_SOA_COLUMN(SigmaGenPt, sigmaGenPt, float); //! Generated pT of the Sigma candidate +DECLARE_SOA_COLUMN(HadGenPt, hadGenPt, float); //! Generated pT of the hadron candidate +DECLARE_SOA_COLUMN(GenKStar, genKStar, float); //! Generated k* of the Sigma-hadron pair } // namespace sigmaproton @@ -52,15 +55,16 @@ DECLARE_SOA_TABLE(SigmaProtonCands, "AOD", "SIGMAPROTONCANDS", sigmaproton::ChargeSigma, kinkcand::PxMoth, kinkcand::PyMoth, kinkcand::PzMoth, kinkcand::PxDaug, kinkcand::PyDaug, kinkcand::PzDaug, sigmaproton::SigmaDecRad, sigmaproton::SigmaCosPA, sigmaproton::ChargeHad, sigmaproton::PxHad, sigmaproton::PyHad, sigmaproton::PzHad, - sigmaproton::NSigmaTPCHad, sigmaproton::NSigmaTOFHad); + sigmaproton::NSigmaTPCHad, sigmaproton::NSigmaTOFHad, sigmaproton::Multiplicity); DECLARE_SOA_TABLE(SigmaProtonMCCands, "AOD", "SIGMAPROTONMCCANDS", o2::soa::Index<>, sigmaproton::ChargeSigma, kinkcand::PxMoth, kinkcand::PyMoth, kinkcand::PzMoth, kinkcand::PxDaug, kinkcand::PyDaug, kinkcand::PzDaug, sigmaproton::SigmaDecRad, sigmaproton::SigmaCosPA, sigmaproton::ChargeHad, sigmaproton::PxHad, sigmaproton::PyHad, sigmaproton::PzHad, - sigmaproton::NSigmaTPCHad, sigmaproton::NSigmaTOFHad, + sigmaproton::NSigmaTPCHad, sigmaproton::NSigmaTOFHad, sigmaproton::Multiplicity, sigmaproton::SigmaPDG, sigmaproton::DaughterPDG, sigmaproton::HadPDG, + sigmaproton::SigmaMotherPDG, sigmaproton::SigmaPartonicMotherPDG, sigmaproton::SigmaGenPt, sigmaproton::HadGenPt, sigmaproton::GenKStar); } // namespace o2::aod diff --git a/PWGLF/DataModel/LFSigmaTables.h b/PWGLF/DataModel/LFSigmaTables.h index 3c4d90427ad..4428b3b2dff 100644 --- a/PWGLF/DataModel/LFSigmaTables.h +++ b/PWGLF/DataModel/LFSigmaTables.h @@ -84,6 +84,11 @@ DECLARE_SOA_DYNAMIC_COLUMN(Sigma0Y, sigma0Y, return RecoDecay::y(std::array{photonPx + lambdaPx, photonPy + lambdaPy, photonPz + lambdaPz}, o2::constants::physics::MassSigma0); }); +DECLARE_SOA_DYNAMIC_COLUMN(LambdaStarY, lambdaStarY, + [](float photonPx, float photonPy, float photonPz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::y(std::array{photonPx + lambdaPx, photonPy + lambdaPy, photonPz + lambdaPz}, o2::constants::physics::MassLambda1520); + }); + DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, //! Phi in the range [0, 2pi) [](float photonPx, float photonPy, float lambdaPx, float lambdaPy) -> float { return RecoDecay::phi(photonPx + lambdaPx, photonPy + lambdaPy); }); @@ -102,6 +107,22 @@ DECLARE_SOA_DYNAMIC_COLUMN(OPAngle, opAngle, return v1.Angle(v2); }); +// Armenteros-Podolanski variables (photon = positive daughter, lambda = negative daughter) +DECLARE_SOA_DYNAMIC_COLUMN(LStarAlpha, lStarAlpha, //! Armenteros Alpha + [](float photonPx, float photonPy, float photonPz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + float momTot = RecoDecay::p(photonPx + lambdaPx, photonPy + lambdaPy, photonPz + lambdaPz); + float lQlNeg = RecoDecay::dotProd(std::array{lambdaPx, lambdaPy, lambdaPz}, std::array{photonPx + lambdaPx, photonPy + lambdaPy, photonPz + lambdaPz}) / momTot; + float lQlPos = RecoDecay::dotProd(std::array{photonPx, photonPy, photonPz}, std::array{photonPx + lambdaPx, photonPy + lambdaPy, photonPz + lambdaPz}) / momTot; + return (lQlPos - lQlNeg) / (lQlPos + lQlNeg); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LStarQtArm, lStarQtarm, //! Armenteros Qt + [](float photonPx, float photonPy, float photonPz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + float momTot = RecoDecay::p2(photonPx + lambdaPx, photonPy + lambdaPy, photonPz + lambdaPz); + float dp = RecoDecay::dotProd(std::array{lambdaPx, lambdaPy, lambdaPz}, std::array{photonPx + lambdaPx, photonPy + lambdaPy, photonPz + lambdaPz}); + return std::sqrt(RecoDecay::p2(lambdaPx, lambdaPy, lambdaPz) - dp * dp / momTot); // qtarm + }); + // Photon DECLARE_SOA_DYNAMIC_COLUMN(PhotonPt, photonPt, //! Transverse momentum in GeV/c [](float photonPx, float photonPy) -> float { @@ -169,10 +190,13 @@ DECLARE_SOA_TABLE(Sigma0Cores, "AOD", "SIGMA0CORES", sigma0Core::P, sigma0Core::Sigma0Mass, sigma0Core::Sigma0Y, + sigma0Core::LambdaStarY, sigma0Core::Phi, sigma0Core::Eta, sigma0Core::Radius, sigma0Core::OPAngle, + sigma0Core::LStarAlpha, + sigma0Core::LStarQtArm, sigma0Core::PhotonPt, sigma0Core::PhotonP, @@ -189,6 +213,9 @@ DECLARE_SOA_TABLE(Sigma0Cores, "AOD", "SIGMA0CORES", // KSTAR core info namespace kstarCore { +DECLARE_SOA_COLUMN(PhotonV0ID, photonV0ID, int); +DECLARE_SOA_COLUMN(KShortV0ID, kshortV0ID, int); + DECLARE_SOA_COLUMN(X, x, float); DECLARE_SOA_COLUMN(Y, y, float); DECLARE_SOA_COLUMN(Z, z, float); @@ -255,6 +282,20 @@ DECLARE_SOA_DYNAMIC_COLUMN(OPAngle, opAngle, return v1.Angle(v2); }); +DECLARE_SOA_DYNAMIC_COLUMN(OPAngleKShortKStar, opAngleKShortKStar, + [](float photonPx, float photonPy, float photonPz, float kshortPx, float kshortPy, float kshortPz) { + TVector3 v1(photonPx + kshortPx, photonPy + kshortPy, photonPz + kshortPz); + TVector3 v2(kshortPx, kshortPy, kshortPz); + return v1.Angle(v2); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(OPAnglePhotonKStar, opAnglePhotonKStar, + [](float photonPx, float photonPy, float photonPz, float kshortPx, float kshortPy, float kshortPz) { + TVector3 v1(photonPx, photonPy, photonPz); + TVector3 v2(kshortPx + photonPx, kshortPy + photonPy, kshortPz + photonPz); + return v1.Angle(v2); + }); + // Photon DECLARE_SOA_DYNAMIC_COLUMN(PhotonPt, photonPt, //! Transverse momentum in GeV/c [](float photonPx, float photonPy) -> float { @@ -306,6 +347,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(KShortPhi, kshortPhi, //! Phi in the range [0, 2pi) } // namespace kstarCore DECLARE_SOA_TABLE(KStarCores, "AOD", "KSTARCORES", + kstarCore::PhotonV0ID, kstarCore::KShortV0ID, kstarCore::X, kstarCore::Y, kstarCore::Z, kstarCore::DCADaughters, kstarCore::PhotonPx, kstarCore::PhotonPy, kstarCore::PhotonPz, kstarCore::PhotonMass, kstarCore::KShortPx, kstarCore::KShortPy, kstarCore::KShortPz, kstarCore::KShortMass, @@ -322,6 +364,8 @@ DECLARE_SOA_TABLE(KStarCores, "AOD", "KSTARCORES", kstarCore::Eta, kstarCore::Radius, kstarCore::OPAngle, + kstarCore::OPAngleKShortKStar, + kstarCore::OPAnglePhotonKStar, kstarCore::PhotonPt, kstarCore::PhotonP, @@ -630,6 +674,12 @@ DECLARE_SOA_DYNAMIC_COLUMN(IsSigma0, isSigma0, DECLARE_SOA_DYNAMIC_COLUMN(IsAntiSigma0, isAntiSigma0, //! IsASigma0 [](int pdgCode) -> bool { return pdgCode == PDG_t::kSigma0Bar; }); //-3212 +DECLARE_SOA_DYNAMIC_COLUMN(IsLambdaStar, isLambdaStar, //! IsLambdaStar + [](int pdgCode) -> bool { return pdgCode == 3124; }); // PYTHIA8 code for Lambda(1520) + +DECLARE_SOA_DYNAMIC_COLUMN(IsAntiLambdaStar, isAntiLambdaStar, //! IsAntiLambdaStar + [](int pdgCode) -> bool { return pdgCode == -3124; }); // PYTHIA8 code for AntiLambda(1520) + DECLARE_SOA_DYNAMIC_COLUMN(MCPx, mcpx, //! Sigma0 px [](float photonMCPx, float lambdaMCPx) -> float { return photonMCPx + lambdaMCPx; }); DECLARE_SOA_DYNAMIC_COLUMN(MCPy, mcpy, //! Sigma0 py @@ -660,6 +710,11 @@ DECLARE_SOA_DYNAMIC_COLUMN(Sigma0MCY, sigma0MCY, return RecoDecay::y(std::array{photonMCPx + lambdaMCPx, photonMCPy + lambdaMCPy, photonMCPz + lambdaMCPz}, o2::constants::physics::MassSigma0); }); +DECLARE_SOA_DYNAMIC_COLUMN(LambdaStarMCY, lambdaStarMCY, + [](float photonMCPx, float photonMCPy, float photonMCPz, float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::y(std::array{photonMCPx + lambdaMCPx, photonMCPy + lambdaMCPy, photonMCPz + lambdaMCPz}, o2::constants::physics::MassLambda1520); + }); + DECLARE_SOA_DYNAMIC_COLUMN(MCPhi, mcphi, //! Phi in the range [0, 2pi) [](float photonMCPx, float photonMCPy, float lambdaMCPx, float lambdaMCPy) -> float { return RecoDecay::phi(photonMCPx + lambdaMCPx, photonMCPy + lambdaMCPy); }); @@ -741,6 +796,8 @@ DECLARE_SOA_TABLE(Sigma0MCCores, "AOD", "SIGMA0MCCORES", // Dynamic columns sigma0MCCore::IsSigma0, sigma0MCCore::IsAntiSigma0, + sigma0MCCore::IsLambdaStar, + sigma0MCCore::IsAntiLambdaStar, sigma0MCCore::MCPx, sigma0MCCore::MCPy, @@ -749,6 +806,7 @@ DECLARE_SOA_TABLE(Sigma0MCCores, "AOD", "SIGMA0MCCORES", sigma0MCCore::MCP, sigma0MCCore::Sigma0MCMass, sigma0MCCore::Sigma0MCY, + sigma0MCCore::LambdaStarMCY, sigma0MCCore::MCPhi, sigma0MCCore::MCEta, sigma0MCCore::MCOPAngle, @@ -780,6 +838,8 @@ DECLARE_SOA_COLUMN(PhotonMCPz, photonmcpz, float); DECLARE_SOA_COLUMN(IsPhotonPrimary, isPhotonPrimary, bool); DECLARE_SOA_COLUMN(PhotonPDGCode, photonPDGCode, int); DECLARE_SOA_COLUMN(PhotonPDGCodeMother, photonPDGCodeMother, int); +DECLARE_SOA_COLUMN(PhotonPDGCodeGrandMother, photonPDGCodeGrandMother, int); +DECLARE_SOA_COLUMN(PhotonGlobalIndexGrandMother, photonGlobalIndexGrandMother, int); DECLARE_SOA_COLUMN(PhotonIsCorrectlyAssoc, photonIsCorrectlyAssoc, bool); DECLARE_SOA_COLUMN(KShortMCPx, kshortmcpx, float); @@ -788,10 +848,12 @@ DECLARE_SOA_COLUMN(KShortMCPz, kshortmcpz, float); DECLARE_SOA_COLUMN(IsKShortPrimary, isKShortPrimary, bool); DECLARE_SOA_COLUMN(KShortPDGCode, kshortPDGCode, int); DECLARE_SOA_COLUMN(KShortPDGCodeMother, kshortPDGCodeMother, int); +DECLARE_SOA_COLUMN(KShortPDGCodeGrandMother, kshortPDGCodeGrandMother, int); +DECLARE_SOA_COLUMN(KShortGlobalIndexGrandMother, kshortGlobalIndexGrandMother, int); DECLARE_SOA_COLUMN(KShortIsCorrectlyAssoc, kshortIsCorrectlyAssoc, bool); -DECLARE_SOA_DYNAMIC_COLUMN(IsKStar, isKStar, //! IsSigma0 - [](int pdgCode) -> bool { return pdgCode == o2::constants::physics::Pdg::kK0Star892; }); // 313 +DECLARE_SOA_DYNAMIC_COLUMN(IsKStar, isKStar, //! IsSigma0 + [](int pdgCode) -> bool { return std::abs(pdgCode) == o2::constants::physics::Pdg::kK0Star892; }); // 313 DECLARE_SOA_DYNAMIC_COLUMN(MCPx, mcpx, //! Sigma0 px [](float photonMCPx, float kshortMCPx) -> float { return photonMCPx + kshortMCPx; }); @@ -820,7 +882,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(KStarMCMass, kstarMCMass, DECLARE_SOA_DYNAMIC_COLUMN(KStarMCY, kstarMCY, [](float photonMCPx, float photonMCPy, float photonMCPz, float kshortMCPx, float kshortMCPy, float kshortMCPz) -> float { - return RecoDecay::y(std::array{photonMCPx + kshortMCPx, photonMCPy + kshortMCPy, photonMCPz + kshortMCPz}, o2::constants::physics::MassKaonNeutral); + return RecoDecay::y(std::array{photonMCPx + kshortMCPx, photonMCPy + kshortMCPy, photonMCPz + kshortMCPz}, o2::constants::physics::MassK0Star892); }); DECLARE_SOA_DYNAMIC_COLUMN(MCPhi, mcphi, //! Phi in the range [0, 2pi) @@ -893,10 +955,10 @@ DECLARE_SOA_TABLE(KStarMCCores, "AOD", "KSTARMCCORES", kstarMCCore::MCradius, kstarMCCore::PDGCode, kstarMCCore::PDGCodeMother, kstarMCCore::MCprocess, kstarMCCore::IsProducedByGenerator, kstarMCCore::PhotonMCPx, kstarMCCore::PhotonMCPy, kstarMCCore::PhotonMCPz, - kstarMCCore::IsPhotonPrimary, kstarMCCore::PhotonPDGCode, kstarMCCore::PhotonPDGCodeMother, kstarMCCore::PhotonIsCorrectlyAssoc, + kstarMCCore::IsPhotonPrimary, kstarMCCore::PhotonPDGCode, kstarMCCore::PhotonPDGCodeMother, kstarMCCore::PhotonPDGCodeGrandMother, kstarMCCore::PhotonGlobalIndexGrandMother, kstarMCCore::PhotonIsCorrectlyAssoc, kstarMCCore::KShortMCPx, kstarMCCore::KShortMCPy, kstarMCCore::KShortMCPz, - kstarMCCore::IsKShortPrimary, kstarMCCore::KShortPDGCode, kstarMCCore::KShortPDGCodeMother, kstarMCCore::KShortIsCorrectlyAssoc, + kstarMCCore::IsKShortPrimary, kstarMCCore::KShortPDGCode, kstarMCCore::KShortPDGCodeMother, kstarMCCore::KShortPDGCodeGrandMother, kstarMCCore::KShortGlobalIndexGrandMother, kstarMCCore::KShortIsCorrectlyAssoc, // Dynamic columns kstarMCCore::IsKStar, @@ -1333,6 +1395,531 @@ DECLARE_SOA_TABLE(Pi0Gens, "AOD", "PI0GENS", DECLARE_SOA_TABLE(Pi0GenCollRef, "AOD", "PI0GENCOLLREF", //! optional table to refer back to a collision o2::soa::Index<>, v0data::StraMCCollisionId); +// ___________________________________________________________________________ +// Xi0 +namespace Xi0Core +{ +DECLARE_SOA_COLUMN(Photon1Index, photon1Index, int); +DECLARE_SOA_COLUMN(Photon2Index, photon2Index, int); +DECLARE_SOA_COLUMN(LambdaIndex, lambdaIndex, int); +DECLARE_SOA_COLUMN(X, x, float); +DECLARE_SOA_COLUMN(Y, y, float); +DECLARE_SOA_COLUMN(Z, z, float); +DECLARE_SOA_COLUMN(XPi0, xPi0, float); +DECLARE_SOA_COLUMN(YPi0, yPi0, float); +DECLARE_SOA_COLUMN(ZPi0, zPi0, float); +DECLARE_SOA_COLUMN(XLambda, xLambda, float); +DECLARE_SOA_COLUMN(YLambda, yLambda, float); +DECLARE_SOA_COLUMN(ZLambda, zLambda, float); +DECLARE_SOA_COLUMN(DCADaughters, dcadaughters, float); +DECLARE_SOA_COLUMN(DCADaughtersPi0, dcadaughtersPi0, float); +DECLARE_SOA_COLUMN(DCAXYCascToPV, dcaXYCascToPV, float); +DECLARE_SOA_COLUMN(DCAZCascToPV, dcaZCascToPV, float); + +DECLARE_SOA_COLUMN(Photon1Px, photon1Px, float); +DECLARE_SOA_COLUMN(Photon1Py, photon1Py, float); +DECLARE_SOA_COLUMN(Photon1Pz, photon1Pz, float); + +DECLARE_SOA_COLUMN(Photon2Px, photon2Px, float); +DECLARE_SOA_COLUMN(Photon2Py, photon2Py, float); +DECLARE_SOA_COLUMN(Photon2Pz, photon2Pz, float); + +DECLARE_SOA_COLUMN(LambdaPx, lambdaPx, float); +DECLARE_SOA_COLUMN(LambdaPy, lambdaPy, float); +DECLARE_SOA_COLUMN(LambdaPz, lambdaPz, float); + +//______________________________________________________ +// DYNAMIC COLUMNS +DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! Pi0 px + [](float photon1Px, float photon2Px, float lambdaPx) -> float { return photon1Px + photon2Px + lambdaPx; }); +DECLARE_SOA_DYNAMIC_COLUMN(Py, py, //! Pi0 py + [](float photon1Py, float photon2Py, float lambdaPy) -> float { return photon1Py + photon2Py + lambdaPy; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! Pi0 pz + [](float photon1Pz, float photon2Pz, float lambdaPz) -> float { return photon1Pz + photon2Pz + lambdaPz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pt, pt, + [](float photon1Px, float photon1Py, float photon2Px, float photon2Py, float lambdaPx, float lambdaPy) -> float { + return RecoDecay::pt(std::array{photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(P, p, //! Total momentum in GeV/c + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy, photon1Pz + photon2Pz + lambdaPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Xi0Mass, xi0Mass, + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + std::array pVecPi0{photon1Px + photon2Px, photon1Py + photon2Py, photon1Pz + photon2Pz}; + std::array pVecLambda{lambdaPx, lambdaPy, lambdaPz}; + auto arrMom = std::array{pVecPi0, pVecLambda}; + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassLambda0}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(PxPi0, pxPi0, //! Pi0 px + [](float photon1Px, float photon2Px) -> float { return photon1Px + photon2Px; }); +DECLARE_SOA_DYNAMIC_COLUMN(PyPi0, pyPi0, //! Pi0 py + [](float photon1Py, float photon2Py) -> float { return photon1Py + photon2Py; }); +DECLARE_SOA_DYNAMIC_COLUMN(PzPi0, pzPi0, //! Pi0 pz + [](float photon1Pz, float photon2Pz) -> float { return photon1Pz + photon2Pz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0Mass, pi0Mass, + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz) -> float { + std::array pVecPhoton1{photon1Px, photon1Py, photon1Pz}; + std::array pVecPhoton2{photon2Px, photon2Py, photon2Pz}; + auto arrMom = std::array{pVecPhoton1, pVecPhoton2}; + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassPhoton}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Rapidity, rapidity, + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::y(std::array{photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy, photon1Pz + photon2Pz + lambdaPz}, o2::constants::physics::MassXi0); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Phi, phi, //! Phi in the range [0, 2pi) + [](float photon1Px, float photon1Py, float photon2Px, float photon2Py, float lambdaPx, float lambdaPy) -> float { return RecoDecay::phi(photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Eta, eta, //! Pseudorapidity + [](float photon1Px, float photon1Py, float photon1Pz, float photon2Px, float photon2Py, float photon2Pz, float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::eta(std::array{photon1Px + photon2Px + lambdaPx, photon1Py + photon2Py + lambdaPy, photon1Pz + photon2Pz + lambdaPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Radius, radius, //! Xi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(RadiusPi0, radiusPi0, //! Pi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(RadiusLambda, radiusLambda, //! Pi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +// CosPAs +DECLARE_SOA_DYNAMIC_COLUMN(Pi0CosPA, pi0CosPA, //! + [](float xPi0, float yPi0, float zPi0, float pxGamma1, float pyGamma1, float pzGamma1, float pxGamma2, float pyGamma2, float pzGamma2, float pvX, float pvY, float pvZ) -> float { + return RecoDecay::cpa(std::array{pvX, pvY, pvZ}, std::array{xPi0, yPi0, zPi0}, std::array{pxGamma1 + pxGamma2, pyGamma1 + pyGamma2, pzGamma1 + pzGamma2}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaCosPA, lambdaCosPA, //! + [](float xLambda, float yLambda, float zLambda, float pxLambda, float pyLambda, float pzLambda, float pvX, float pvY, float pvZ) -> float { + return RecoDecay::cpa(std::array{pvX, pvY, pvZ}, std::array{xLambda, yLambda, zLambda}, std::array{pxLambda, pyLambda, pzLambda}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(CascCosPA, cascCosPA, //! + [](float X, float Y, float Z, float PxGamma1, float PxGamma2, float PxLambda, float PyGamma1, float PyGamma2, float PyLambda, float PzGamma1, float PzGamma2, float PzLambda, float pvX, float pvY, float pvZ) -> float { return RecoDecay::cpa(std::array{pvX, pvY, pvZ}, std::array{X, Y, Z}, std::array{PxGamma1 + PxGamma2 + PxLambda, PyGamma1 + PyGamma2 + PyLambda, PzGamma1 + PzGamma2 + PzLambda}); }); + +DECLARE_SOA_DYNAMIC_COLUMN(DCALambdaToPV, dcaLambdaToPV, //! + [](float X, float Y, float Z, float px, float py, float pz, float pvX, float pvY, float pvZ) -> float { + return std::sqrt((std::pow((pvY - Y) * pz - (pvZ - Z) * py, 2) + std::pow((pvX - X) * pz - (pvZ - Z) * px, 2) + std::pow((pvX - X) * py - (pvY - Y) * px, 2)) / (px * px + py * py + pz * pz)); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(DCAPi0ToPV, dcaPi0ToPV, //! + [](float X, float Y, float Z, float pxGamma1, float pyGamma1, float pzGamma1, float pxGamma2, float pyGamma2, float pzGamma2, float pvX, float pvY, float pvZ) -> float { + float px = pxGamma1 + pxGamma2; + float py = pyGamma1 + pyGamma2; + float pz = pzGamma1 + pzGamma2; + return std::sqrt((std::pow((pvY - Y) * pz - (pvZ - Z) * py, 2) + std::pow((pvX - X) * pz - (pvZ - Z) * px, 2) + std::pow((pvX - X) * py - (pvY - Y) * px, 2)) / (px * px + py * py + pz * pz)); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Pt, photon1Pt, //! Transverse momentum in GeV/c + [](float photon1Px, float photon1Py) -> float { + return RecoDecay::sqrtSumOfSquares(photon1Px, photon1Py); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1P, photon1p, //! Total momentum in GeV/c + [](float photon1Px, float photon1Py, float photon1Pz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1Px, photon1Py, photon1Pz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Eta, photon1Eta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon1Px, float photon1Py, float photon1Pz) -> float { + return RecoDecay::eta(std::array{photon1Px, photon1Py, photon1Pz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Y, photon1Y, //! Rapidity + [](float photon1Px, float photon1Py, float photon1Pz) -> float { + return RecoDecay::y(std::array{photon1Px, photon1Py, photon1Pz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1Phi, photon1Phi, //! Phi in the range [0, 2pi) + [](float photon1Px, float photon1Py) -> float { return RecoDecay::phi(photon1Px, photon1Py); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Pt, photon2Pt, //! Transverse momentum in GeV/c + [](float photon2Px, float photon2Py) -> float { + return RecoDecay::sqrtSumOfSquares(photon2Px, photon2Py); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2P, photon2p, //! Total momentum in GeV/c + [](float photon2Px, float photon2Py, float photon2Pz) -> float { + return RecoDecay::sqrtSumOfSquares(photon2Px, photon2Py, photon2Pz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Eta, photon2Eta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon2Px, float photon2Py, float photon2Pz) -> float { + return RecoDecay::eta(std::array{photon2Px, photon2Py, photon2Pz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Y, photon2Y, //! Rapidity + [](float photon2Px, float photon2Py, float photon2Pz) -> float { + return RecoDecay::y(std::array{photon2Px, photon2Py, photon2Pz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2Phi, photon2Phi, //! Phi in the range [0, 2pi) + [](float photon2Px, float photon2Py) -> float { return RecoDecay::phi(photon2Px, photon2Py); }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaPt, lambdaPt, //! Transverse momentum in GeV/c + [](float lambdaPx, float lambdaPy) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaPx, lambdaPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaP, lambdaP, //! Total momentum in GeV/c + [](float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaPx, lambdaPy, lambdaPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaEta, lambdaEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::eta(std::array{lambdaPx, lambdaPy, lambdaPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaY, lambdaY, //! Rapidity + [](float lambdaPx, float lambdaPy, float lambdaPz) -> float { + return RecoDecay::y(std::array{lambdaPx, lambdaPy, lambdaPz}, o2::constants::physics::MassLambda); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaPhi, lambdaPhi, //! Phi in the range [0, 2pi) + [](float lambdaPx, float lambdaPy) -> float { return RecoDecay::phi(lambdaPx, lambdaPy); }); + +} // namespace Xi0Core + +DECLARE_SOA_TABLE(Xi0Cores, "AOD", "XI0CORES", + Xi0Core::X, Xi0Core::Y, Xi0Core::Z, + Xi0Core::XPi0, Xi0Core::YPi0, Xi0Core::ZPi0, + Xi0Core::XLambda, Xi0Core::YLambda, Xi0Core::ZLambda, + Xi0Core::DCADaughters, Xi0Core::DCADaughtersPi0, Xi0Core::DCAXYCascToPV, Xi0Core::DCAZCascToPV, + + // Xi0 daughter momenta + Xi0Core::Photon1Px, Xi0Core::Photon1Py, Xi0Core::Photon1Pz, + Xi0Core::Photon2Px, Xi0Core::Photon2Py, Xi0Core::Photon2Pz, + Xi0Core::LambdaPx, Xi0Core::LambdaPy, Xi0Core::LambdaPz, + + // Dynamic columns + Xi0Core::Px, + Xi0Core::Py, + Xi0Core::Pz, + Xi0Core::Pt, + Xi0Core::P, + Xi0Core::Xi0Mass, + Xi0Core::Rapidity, + Xi0Core::Phi, + Xi0Core::Eta, + Xi0Core::Radius, + Xi0Core::CascCosPA, + + // Dynamic columns for pi0 + Xi0Core::PxPi0, + Xi0Core::PyPi0, + Xi0Core::PzPi0, + Xi0Core::Pi0Mass, + Xi0Core::RadiusPi0, + Xi0Core::Pi0CosPA, + Xi0Core::DCAPi0ToPV, + + // Dynamic columns for Lambda + Xi0Core::RadiusLambda, + Xi0Core::LambdaCosPA, + Xi0Core::DCALambdaToPV, + Xi0Core::LambdaPt, + Xi0Core::LambdaP, + Xi0Core::LambdaEta, + Xi0Core::LambdaY, + Xi0Core::LambdaPhi, + + // Dynamic columns for Photon1 + Xi0Core::Photon1Pt, + Xi0Core::Photon1P, + Xi0Core::Photon1Eta, + Xi0Core::Photon1Y, + Xi0Core::Photon1Phi, + + // Dynamic columns for Photon2 + Xi0Core::Photon2Pt, + Xi0Core::Photon2P, + Xi0Core::Photon2Eta, + Xi0Core::Photon2Y, + Xi0Core::Photon2Phi); + +DECLARE_SOA_TABLE(Xi0CollRefs, "AOD", "XI0COLLREFS", //! optional table to refer back to a collision + o2::soa::Index<>, v0data::StraCollisionId); + +DECLARE_SOA_TABLE(Xi0Indices, "AOD", "XI0INDICES", //! optional table to refer back to V0Cores + o2::soa::Index<>, Xi0Core::Photon1Index, Xi0Core::Photon2Index, Xi0Core::LambdaIndex); + +// for MC +namespace Xi0MCCore +{ +DECLARE_SOA_COLUMN(Photon1Index, photon1Index, int); +DECLARE_SOA_COLUMN(Photon2Index, photon2Index, int); +DECLARE_SOA_COLUMN(LambdaIndex, lambdaIndex, int); + +DECLARE_SOA_COLUMN(Photon1MCPx, photon1mcpx, float); +DECLARE_SOA_COLUMN(Photon1MCPy, photon1mcpy, float); +DECLARE_SOA_COLUMN(Photon1MCPz, photon1mcpz, float); +DECLARE_SOA_COLUMN(IsPhoton1Primary, isPhoton1Primary, bool); +DECLARE_SOA_COLUMN(Photon1PDGCodePositive, photon1PDGCodePositive, int); +DECLARE_SOA_COLUMN(Photon1PDGCodeNegative, photon1PDGCodeNegative, int); +DECLARE_SOA_COLUMN(Photon1PDGCode, photon1PDGCode, int); +DECLARE_SOA_COLUMN(Photon1PDGCodeMother, photon1PDGCodeMother, int); + +DECLARE_SOA_COLUMN(Photon2MCPx, photon2mcpx, float); +DECLARE_SOA_COLUMN(Photon2MCPy, photon2mcpy, float); +DECLARE_SOA_COLUMN(Photon2MCPz, photon2mcpz, float); +DECLARE_SOA_COLUMN(IsPhoton2Primary, isPhoton2Primary, bool); +DECLARE_SOA_COLUMN(Photon2PDGCodePositive, photon2PDGCodePositive, int); +DECLARE_SOA_COLUMN(Photon2PDGCodeNegative, photon2PDGCodeNegative, int); +DECLARE_SOA_COLUMN(Photon2PDGCode, photon2PDGCode, int); +DECLARE_SOA_COLUMN(Photon2PDGCodeMother, photon2PDGCodeMother, int); + +DECLARE_SOA_COLUMN(LambdaMCPx, lambdamcpx, float); +DECLARE_SOA_COLUMN(LambdaMCPy, lambdamcpy, float); +DECLARE_SOA_COLUMN(LambdaMCPz, lambdamcpz, float); +DECLARE_SOA_COLUMN(IsLambdaPrimary, isLambdaPrimary, bool); +DECLARE_SOA_COLUMN(LambdaPDGCodePositive, lambdaPDGCodePositive, int); +DECLARE_SOA_COLUMN(LambdaPDGCodeNegative, lambdaPDGCodeNegative, int); +DECLARE_SOA_COLUMN(LambdaPDGCode, lambdaPDGCode, int); +DECLARE_SOA_COLUMN(LambdaPDGCodeMother, lambdaPDGCodeMother, int); + +DECLARE_SOA_COLUMN(Pi0MCx, pi0mcx, float); +DECLARE_SOA_COLUMN(Pi0MCy, pi0mcy, float); +DECLARE_SOA_COLUMN(Pi0MCz, pi0mcz, float); +DECLARE_SOA_COLUMN(IsPi0Primary, isPi0Primary, bool); +DECLARE_SOA_COLUMN(Pi0MCprocess, pi0mcprocess, int); +DECLARE_SOA_COLUMN(Pi0PDGCode, pi0PDGCode, int); + +DECLARE_SOA_COLUMN(MCx, mcx, float); +DECLARE_SOA_COLUMN(MCy, mcy, float); +DECLARE_SOA_COLUMN(MCz, mcz, float); +DECLARE_SOA_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, bool); +DECLARE_SOA_COLUMN(MCprocess, mcprocess, int); +DECLARE_SOA_COLUMN(PDGCode, pdgCode, int); + +DECLARE_SOA_COLUMN(PDGCodeMother, pdgCodeMother, int); + +DECLARE_SOA_DYNAMIC_COLUMN(MCPx, mcpx, //! Xi0 MC px + [](float photon1MCPx, float photon2MCPx, float lambdaMCPx) -> float { return photon1MCPx + photon2MCPx + lambdaMCPx; }); +DECLARE_SOA_DYNAMIC_COLUMN(MCPy, mcpy, //! Xi0 MC py + [](float photon1MCPy, float photon2MCPy, float lambdaMCPy) -> float { return photon1MCPy + photon2MCPy + lambdaMCPy; }); +DECLARE_SOA_DYNAMIC_COLUMN(MCPz, mcpz, //! Xi0 MC pz + [](float photon1MCPz, float photon2MCPz, float lambdaMCPz) -> float { return photon1MCPz + photon2MCPz + lambdaMCPz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCPt, mcpt, + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy, float lambdaMCPx, float lambdaMCPy) -> float { + return RecoDecay::pt(std::array{photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCP, mcp, //! Total momentum in GeV/c + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz, float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy, photon1MCPz + photon2MCPz + lambdaMCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(RapidityMC, rapidityMC, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz, float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::y(std::array{photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy, photon1MCPz + photon2MCPz + lambdaMCPz}, o2::constants::physics::MassXi0); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCPhi, mcPhi, //! Phi in the range [0, 2pi) + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy, float lambdaMCPx, float lambdaMCPy) -> float { return RecoDecay::phi(photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCEta, mcEta, //! Pseudorapidity + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz, float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::eta(std::array{photon1MCPx + photon2MCPx + lambdaMCPx, photon1MCPy + photon2MCPy + lambdaMCPy, photon1MCPz + photon2MCPz + lambdaMCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCRadius, mcRadius, //! Xi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(MCRadiusPi0, mcRadiusPi0, //! Pi0 decay radius (2D, centered at zero) + [](float x, float y) -> float { return RecoDecay::sqrtSumOfSquares(x, y); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPx, pi0mcpx, //! Pi0 MC px + [](float photon1MCPx, float photon2MCPx) -> float { return photon1MCPx + photon2MCPx; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPy, pi0mcpy, //! Pi0 MC py + [](float photon1MCPy, float photon2MCPy) -> float { return photon1MCPy + photon2MCPy; }); +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPz, pi0mcpz, //! Pi0 MC pz + [](float photon1MCPz, float photon2MCPz) -> float { return photon1MCPz + photon2MCPz; }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPt, pi0mcpt, + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy) -> float { + return RecoDecay::pt(std::array{photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCP, pi0mcp, //! Total momentum in GeV/c + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy, photon1MCPz + photon2MCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCMass, pi0MCMass, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + std::array pVecPhoton1{photon1MCPx, photon1MCPy, photon1MCPz}; + std::array pVecPhoton2{photon2MCPx, photon2MCPy, photon2MCPz}; + auto arrMom = std::array{pVecPhoton1, pVecPhoton2}; + return RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassPhoton}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0RapidityMC, pi0RapidityMC, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::y(std::array{photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy, photon1MCPz + photon2MCPz}, o2::constants::physics::MassPi0); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCPhi, pi0mcphi, //! Phi in the range [0, 2pi) + [](float photon1MCPx, float photon1MCPy, float photon2MCPx, float photon2MCPy) -> float { return RecoDecay::phi(photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCEta, pi0mceta, //! Pseudorapidity + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::eta(std::array{photon1MCPx + photon2MCPx, photon1MCPy + photon2MCPy, photon1MCPz + photon2MCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Pi0MCOPAngle, pi0mcopAngle, + [](float photon1MCPx, float photon1MCPy, float photon1MCPz, float photon2MCPx, float photon2MCPy, float photon2MCPz) { + TVector3 v1(photon1MCPx, photon1MCPy, photon1MCPz); + TVector3 v2(photon2MCPx, photon2MCPy, photon2MCPz); + return v1.Angle(v2); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCPt, photon1MCPt, //! Transverse momentum in GeV/c + [](float photon1MCPx, float photon1MCPy) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx, photon1MCPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCP, photon1MCp, //! Total momentum in GeV/c + [](float photon1MCPx, float photon1MCPy, float photon1MCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon1MCPx, photon1MCPy, photon1MCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCEta, photon1MCEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon1MCPx, float photon1MCPy, float photon1MCPz) -> float { + return RecoDecay::eta(std::array{photon1MCPx, photon1MCPy, photon1MCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCY, photon1MCY, //! Rapidity + [](float photon1MCPx, float photon1MCPy, float photon1MCPz) -> float { + return RecoDecay::y(std::array{photon1MCPx, photon1MCPy, photon1MCPz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon1MCPhi, photon1MCPhi, //! Phi in the range [0, 2pi) + [](float photon1MCPx, float photon1MCPy) -> float { return RecoDecay::phi(photon1MCPx, photon1MCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCPt, photon2MCPt, //! Transverse momentum in GeV/c + [](float photon2MCPx, float photon2MCPy) -> float { + return RecoDecay::sqrtSumOfSquares(photon2MCPx, photon2MCPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCP, photon2MCp, //! Total momentum in GeV/c + [](float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::sqrtSumOfSquares(photon2MCPx, photon2MCPy, photon2MCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCEta, photon2MCEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::eta(std::array{photon2MCPx, photon2MCPy, photon2MCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCY, photon2MCY, //! Rapidity + [](float photon2MCPx, float photon2MCPy, float photon2MCPz) -> float { + return RecoDecay::y(std::array{photon2MCPx, photon2MCPy, photon2MCPz}, o2::constants::physics::MassGamma); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(Photon2MCPhi, photon2MCPhi, //! Phi in the range [0, 2pi) + [](float photon2MCPx, float photon2MCPy) -> float { return RecoDecay::phi(photon2MCPx, photon2MCPy); }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCPt, lambdaMCPt, //! Transverse momentum in GeV/c + [](float lambdaMCPx, float lambdaMCPy) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaMCPx, lambdaMCPy); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCP, lambdaMCp, //! Total momentum in GeV/c + [](float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::sqrtSumOfSquares(lambdaMCPx, lambdaMCPy, lambdaMCPz); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCEta, lambdaMCEta, //! Pseudorapidity, conditionally defined to avoid FPEs + [](float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::eta(std::array{lambdaMCPx, lambdaMCPy, lambdaMCPz}); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCY, lambdaMCY, //! Rapidity + [](float lambdaMCPx, float lambdaMCPy, float lambdaMCPz) -> float { + return RecoDecay::y(std::array{lambdaMCPx, lambdaMCPy, lambdaMCPz}, o2::constants::physics::MassLambda); + }); + +DECLARE_SOA_DYNAMIC_COLUMN(LambdaMCPhi, lambdaMCPhi, //! Phi in the range [0, 2pi) + [](float lambdaMCPx, float lambdaMCPy) -> float { return RecoDecay::phi(lambdaMCPx, lambdaMCPy); }); + +} // namespace Xi0MCCore + +DECLARE_SOA_TABLE(Xi0MCCores, "AOD", "XI0MCCORES", + // Basic properties + Xi0MCCore::Photon1MCPx, Xi0MCCore::Photon1MCPy, Xi0MCCore::Photon1MCPz, + Xi0MCCore::IsPhoton1Primary, Xi0MCCore::Photon1PDGCodePositive, Xi0MCCore::Photon1PDGCodeNegative, Xi0MCCore::Photon1PDGCode, Xi0MCCore::Photon1PDGCodeMother, + + Xi0MCCore::Photon2MCPx, Xi0MCCore::Photon2MCPy, Xi0MCCore::Photon2MCPz, + Xi0MCCore::IsPhoton2Primary, Xi0MCCore::Photon2PDGCodePositive, Xi0MCCore::Photon2PDGCodeNegative, Xi0MCCore::Photon2PDGCode, Xi0MCCore::Photon2PDGCodeMother, + + Xi0MCCore::LambdaMCPx, Xi0MCCore::LambdaMCPy, Xi0MCCore::LambdaMCPz, + Xi0MCCore::IsLambdaPrimary, Xi0MCCore::LambdaPDGCodePositive, Xi0MCCore::LambdaPDGCodeNegative, Xi0MCCore::LambdaPDGCode, Xi0MCCore::LambdaPDGCodeMother, + + Xi0MCCore::Pi0MCx, Xi0MCCore::Pi0MCy, Xi0MCCore::Pi0MCz, + Xi0MCCore::IsPi0Primary, Xi0MCCore::Pi0MCprocess, Xi0MCCore::Pi0PDGCode, + + Xi0MCCore::MCx, Xi0MCCore::MCy, Xi0MCCore::MCz, + Xi0MCCore::IsPhysicalPrimary, Xi0MCCore::MCprocess, Xi0MCCore::PDGCode, Xi0MCCore::PDGCodeMother, + + // Dynamic columns + Xi0MCCore::MCPx, + Xi0MCCore::MCPy, + Xi0MCCore::MCPz, + Xi0MCCore::MCPt, + Xi0MCCore::MCP, + Xi0MCCore::RapidityMC, + Xi0MCCore::MCPhi, + Xi0MCCore::MCEta, + + Xi0MCCore::MCRadius, + Xi0MCCore::MCRadiusPi0, + + Xi0MCCore::Pi0MCPx, + Xi0MCCore::Pi0MCPy, + Xi0MCCore::Pi0MCPz, + Xi0MCCore::Pi0MCPt, + Xi0MCCore::Pi0MCP, + Xi0MCCore::Pi0MCMass, + Xi0MCCore::Pi0RapidityMC, + Xi0MCCore::Pi0MCPhi, + Xi0MCCore::Pi0MCEta, + Xi0MCCore::Pi0MCOPAngle, + + Xi0MCCore::Photon1MCPt, + Xi0MCCore::Photon1MCP, + Xi0MCCore::Photon1MCEta, + Xi0MCCore::Photon1MCY, + Xi0MCCore::Photon1MCPhi, + + Xi0MCCore::Photon2MCPt, + Xi0MCCore::Photon2MCP, + Xi0MCCore::Photon2MCEta, + Xi0MCCore::Photon2MCY, + Xi0MCCore::Photon2MCPhi, + + Xi0MCCore::LambdaMCPt, + Xi0MCCore::LambdaMCP, + Xi0MCCore::LambdaMCEta, + Xi0MCCore::LambdaMCY, + Xi0MCCore::LambdaMCPhi); + +DECLARE_SOA_TABLE(Xi0MCCollRefs, "AOD", "XI0MCCOLLREFS", //! optional table to refer back to a collision + o2::soa::Index<>, v0data::StraMCCollisionId); + +DECLARE_SOA_TABLE(Xi0MCIndices, "AOD", "XI0MCINDICES", //! optional table to refer back to V0MCCores + o2::soa::Index<>, Xi0MCCore::Photon1Index, Xi0MCCore::Photon2Index, Xi0MCCore::LambdaIndex); + } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFSIGMATABLES_H_ diff --git a/PWGLF/DataModel/LFSlimHeLambda.h b/PWGLF/DataModel/LFSlimHeLambda.h index 6b3ae954cd9..a0737effbbb 100644 --- a/PWGLF/DataModel/LFSlimHeLambda.h +++ b/PWGLF/DataModel/LFSlimHeLambda.h @@ -54,12 +54,18 @@ DECLARE_SOA_COLUMN(DCAPVProton, dcaPVProton, float); DECLARE_SOA_COLUMN(DCAPVPion, dcaPVPion, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); DECLARE_SOA_COLUMN(Sign, sign, int8_t); +DECLARE_SOA_COLUMN(Ct, ct, float); +DECLARE_SOA_COLUMN(DCAPVPos, dcaPVPos, float); +DECLARE_SOA_COLUMN(DCAPVNeg, dcaPVNeg, float); +DECLARE_SOA_COLUMN(NsigmaTPCPosPion, nSigmaTPCPosPion, float); +DECLARE_SOA_COLUMN(NsigmaTPCNegPion, nSigmaTPCNegPion, float); } // namespace lfv0he3 DECLARE_SOA_TABLE_VERSIONED(LFHe3_000, "AOD", "LFHE3V0", 0, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::DCAxy, lfv0he3::DCAz, lfv0he3::TPCnCls, lfv0he3::ITSClusterSizes, lfv0he3::NsigmaTPC, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFLambda_000, "AOD", "LFLAMBDA", 0, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVProton, lfv0he3::DCAPVPion, lfv0he3::V0Radius, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFHe3_001, "AOD", "LFHE3V0", 1, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::DCAxy, lfv0he3::DCAz, lfv0he3::TPCnCls, lfv0he3::TPCnClsPID, lfv0he3::ITSClusterSizes, lfv0he3::NsigmaTPC, lfv0he3::Sign); DECLARE_SOA_TABLE_VERSIONED(LFLambda_001, "AOD", "LFLAMBDA", 1, lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVProton, lfv0he3::DCAPVPion, lfv0he3::V0Radius, lfv0he3::NsigmaTPCProton, lfv0he3::NsigmaTPCPion, lfv0he3::Sign); +DECLARE_SOA_TABLE(LFK0s, "AOD", "LFK0S", lfv0he3::LFEventId, lfv0he3::Pt, lfv0he3::Eta, lfv0he3::Phi, lfv0he3::Mass, lfv0he3::Ct, lfv0he3::CosPA, lfv0he3::DCAdaughters, lfv0he3::DCAPVPos, lfv0he3::DCAPVNeg, lfv0he3::V0Radius, lfv0he3::NsigmaTPCPosPion, lfv0he3::NsigmaTPCNegPion); } // namespace o2::aod struct he3Candidate { @@ -87,4 +93,17 @@ struct lambdaCandidate { int8_t sign = 0; // Charge sign of the Lambda candidate }; +struct k0sCandidate { + ROOT::Math::LorentzVector> momentum; + float mass = -1.f; + float ct = -1.f; // Proper decay length in cm + float cosPA = -2.f; + float dcaV0Daughters = -999.f; + float dcaPosToPV = -999.f; + float dcaNegToPV = -999.f; + float v0Radius = -1.f; + float posPionNSigmaTPC = -999.f; + float negPionNSigmaTPC = -999.f; +}; + #endif // PWGLF_DATAMODEL_LFSLIMHELAMBDA_H_ diff --git a/PWGLF/DataModel/LFSlimNucleiTables.h b/PWGLF/DataModel/LFSlimNucleiTables.h index 7988a1322e6..2b64efe2b00 100644 --- a/PWGLF/DataModel/LFSlimNucleiTables.h +++ b/PWGLF/DataModel/LFSlimNucleiTables.h @@ -56,8 +56,11 @@ DECLARE_SOA_COLUMN(AbsoDecL, absoDecL, float); DECLARE_SOA_COLUMN(McProcess, mcProcess, uint64_t); DECLARE_SOA_COLUMN(gEventMask, genEventMask, uint8_t); -DECLARE_SOA_COLUMN(NsigmaTpc, nsigmaTpc, uint8_t); -DECLARE_SOA_COLUMN(NsigmaTof, nsigmaTof, uint8_t); +DECLARE_SOA_COLUMN(NsigmaTpc, nsigmaTpc, float); +DECLARE_SOA_COLUMN(NsigmaTof, nsigmaTof, float); + +DECLARE_SOA_COLUMN(Vx, vx, float); +DECLARE_SOA_COLUMN(Vy, vy, float); } // namespace NucleiTableNS @@ -139,6 +142,12 @@ DECLARE_SOA_TABLE(NucleiTableFlow, "AOD", "NUCLEITABLEFLOW", NucleiFlowTableNS::QTPCl, NucleiFlowTableNS::QTPCr); +DECLARE_SOA_TABLE(NucleiTableCent, "AOD", "NUCLEITABLECENT", + NucleiFlowTableNS::CentFV0A, + NucleiFlowTableNS::CentFT0M, + NucleiFlowTableNS::CentFT0A, + NucleiFlowTableNS::CentFT0C); + DECLARE_SOA_TABLE(NucleiTableMC, "AOD", "NUCLEITABLEMC", NucleiTableNS::Pt, NucleiTableNS::Eta, @@ -201,6 +210,7 @@ DECLARE_SOA_TABLE(NucleiTableRed, "AOD", "NUCLEITABLERED", NucleiTableNS::DCAxy, NucleiTableNS::DCAz, NucleiTableNS::Flags, + NucleiFlowTableNS::CentFT0C, NucleiTableNS::gPt, NucleiTableNS::McProcess, NucleiTableNS::PDGcode, @@ -244,6 +254,10 @@ DECLARE_SOA_TABLE(NucleiTableMCExtension, "AOD", "NUCTABLEMCSEL", DECLARE_SOA_TABLE(NucleiTableExt, "AOD", "NUCLEITABLEEXT", NucleiTableNS::NsigmaTpc, NucleiTableNS::NsigmaTof); +// Extended table for studies on nuclei from material +DECLARE_SOA_TABLE(NucleiTableMat, "AOD", "NUCLEITABLEMAT", + NucleiTableNS::Vx, + NucleiTableNS::Vy); } // namespace o2::aod diff --git a/PWGLF/DataModel/LFSpincorrelationTables.h b/PWGLF/DataModel/LFSpincorrelationTables.h index 571a3413549..fa7e2061f3e 100644 --- a/PWGLF/DataModel/LFSpincorrelationTables.h +++ b/PWGLF/DataModel/LFSpincorrelationTables.h @@ -52,6 +52,14 @@ DECLARE_SOA_COLUMN(ProtonEta, protonEta, float); //! Proton Et DECLARE_SOA_COLUMN(ProtonPhi, protonPhi, float); //! Proton Phi DECLARE_SOA_COLUMN(ProtonIndex, protonIndex, int); //! Proton index DECLARE_SOA_COLUMN(PionIndex, pionIndex, int); //! Pion index +DECLARE_SOA_COLUMN(DcaV0ToPV, dcaV0ToPV, float); //! DCA of V0 to primary vertex +DECLARE_SOA_COLUMN(TpcPosNClsCrossedRows, tpcPosNClsCrossedRows, float); +DECLARE_SOA_COLUMN(TpcNegNClsCrossedRows, tpcNegNClsCrossedRows, float); +DECLARE_SOA_COLUMN(Ctau, ctau, float); +DECLARE_SOA_COLUMN(TpcNsigmaPos, tpcNsigmaPos, float); +DECLARE_SOA_COLUMN(TpcNsigmaNeg, tpcNsigmaNeg, float); +DECLARE_SOA_COLUMN(MK0Short, mK0Short, float); + } // namespace lambdapair DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", o2::soa::Index<>, @@ -71,7 +79,14 @@ DECLARE_SOA_TABLE(LambdaPairs, "AOD", "LAMBDAPAIR", lambdapair::ProtonEta, lambdapair::ProtonPhi, lambdapair::ProtonIndex, - lambdapair::PionIndex); + lambdapair::PionIndex, + lambdapair::DcaV0ToPV, + lambdapair::TpcPosNClsCrossedRows, + lambdapair::TpcNegNClsCrossedRows, + lambdapair::Ctau, + lambdapair::TpcNsigmaPos, + lambdapair::TpcNsigmaNeg, + lambdapair::MK0Short); using LambdaPair = LambdaPairs::iterator; @@ -105,6 +120,9 @@ DECLARE_SOA_COLUMN(ProtonEtamc, protonEtamc, float); //! Proto DECLARE_SOA_COLUMN(ProtonPhimc, protonPhimc, float); //! Proton Phi in montecarlo DECLARE_SOA_COLUMN(ProtonIndexmc, protonIndexmc, int); //! Proton index in montecarlo DECLARE_SOA_COLUMN(PionIndexmc, pionIndexmc, int); //! Pion index in montecarlo +DECLARE_SOA_COLUMN(DcaV0ToPVmc, dcaV0ToPVmc, float); //! DCA of V0 to primary vertex + +//! TPC n sigma } // namespace lambdapairmc DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", o2::soa::Index<>, @@ -124,7 +142,8 @@ DECLARE_SOA_TABLE(LambdaPairmcs, "AOD", "LAMBDAPAIRMC", lambdapairmc::ProtonEtamc, lambdapairmc::ProtonPhimc, lambdapairmc::ProtonIndexmc, - lambdapairmc::PionIndexmc); + lambdapairmc::PionIndexmc, + lambdapairmc::DcaV0ToPVmc); using LambdaPairmc = LambdaPairmcs::iterator; diff --git a/PWGLF/DataModel/LFStrangenessMLTables.h b/PWGLF/DataModel/LFStrangenessMLTables.h index ad30c86524b..983648f791d 100644 --- a/PWGLF/DataModel/LFStrangenessMLTables.h +++ b/PWGLF/DataModel/LFStrangenessMLTables.h @@ -16,6 +16,7 @@ #include #include +#include // Creating output TTree for ML analysis namespace o2::aod @@ -179,6 +180,8 @@ DECLARE_SOA_COLUMN(DCACascDaughters, dcaCascDaughters, float); DECLARE_SOA_COLUMN(DCAv0topv, dcav0topv, float); DECLARE_SOA_COLUMN(V0PA, v0PA, float); DECLARE_SOA_COLUMN(CascPA, cascPA, float); +DECLARE_SOA_COLUMN(BachBaryonCosPA, bachBaryonCosPA, float); +DECLARE_SOA_COLUMN(BachBaryonDCAxyToPV, bachBaryonDCAxyToPV, float); DECLARE_SOA_COLUMN(IsXiMinus, isXiMinus, bool); DECLARE_SOA_COLUMN(IsXiPlus, isXiPlus, bool); @@ -230,6 +233,8 @@ DECLARE_SOA_TABLE(CascMLCandidates, "AOD", "CAMLCANDIDATES", cascmlcandidates::DCAv0topv, cascmlcandidates::V0PA, cascmlcandidates::CascPA, + cascmlcandidates::BachBaryonCosPA, + cascmlcandidates::BachBaryonDCAxyToPV, cascmlcandidates::IsXiMinus, cascmlcandidates::IsXiPlus, cascmlcandidates::IsOmegaMinus, @@ -237,8 +242,8 @@ DECLARE_SOA_TABLE(CascMLCandidates, "AOD", "CAMLCANDIDATES", namespace CascMLSelection { -DECLARE_SOA_COLUMN(XiBDTScore, xiBDTScore, float); -DECLARE_SOA_COLUMN(OmegaBDTScore, omegaBDTScore, float); +DECLARE_SOA_COLUMN(XiBDTScore, xiBDTScore, std::vector); +DECLARE_SOA_COLUMN(OmegaBDTScore, omegaBDTScore, std::vector); } // namespace CascMLSelection DECLARE_SOA_TABLE(CascXiMLScores, "AOD", "CASCXIMLSCORES", diff --git a/PWGLF/DataModel/LFStrangenessPIDTables.h b/PWGLF/DataModel/LFStrangenessPIDTables.h index 14c3dade24c..848f2a3289f 100644 --- a/PWGLF/DataModel/LFStrangenessPIDTables.h +++ b/PWGLF/DataModel/LFStrangenessPIDTables.h @@ -112,6 +112,7 @@ DECLARE_SOA_INDEX_COLUMN(DauTrackExtra, dauTrackExtra); //! point to daughter th DECLARE_SOA_INDEX_COLUMN(StraCollision, straCollision); //! point to collision associated with this track (not the V0/Casc) DECLARE_SOA_COLUMN(TOFSignal, tofSignal, float); //! track TOF signal DECLARE_SOA_COLUMN(TOFEvTime, tofEvTime, float); //! event time +DECLARE_SOA_COLUMN(TOFEvTimeErr, tofEvTimeErr, float); //! event time error for TOF DECLARE_SOA_COLUMN(Length, length, float); //! track length (to assigned PV) DECLARE_SOA_COLUMN(TOFExpMom, tofExpMom, float); //! tof Exp Mom (to assigned PV) @@ -171,7 +172,27 @@ DECLARE_SOA_TABLE_VERSIONED(DauTrackTOFPIDs_001, "AOD", "DAUTRACKTOFPID", 1, // dautrack::TOFExpTimeKa, dautrack::TOFExpTimePr); -using DauTrackTOFPIDs = DauTrackTOFPIDs_001; // second gen: with collision Id, with TOFExpMom +DECLARE_SOA_TABLE_VERSIONED(DauTrackTOFPIDs_002, "AOD", "DAUTRACKTOFPID", 2, // raw table (for posterior TOF calculation) + o2::soa::Index<>, + dautrack::StraCollisionId, dautrack::DauTrackExtraId, + dautrack::TOFSignal, dautrack::TOFEvTime, dautrack::TOFEvTimeErr, + dautrack::Length, dautrack::TOFExpMom, + dautrack::TOFExpTimeEl, + dautrack::TOFExpTimePi, + dautrack::TOFExpTimeKa, + dautrack::TOFExpTimePr); + +DECLARE_SOA_TABLE_VERSIONED(DauTrackTOFPIDs_003, "AOD", "DAUTRACKTOFPID", 3, // raw table (for posterior TOF calculation) + o2::soa::Index<>, + bc::GlobalBC, dautrack::DauTrackExtraId, + dautrack::TOFSignal, dautrack::TOFEvTime, dautrack::TOFEvTimeErr, + dautrack::Length, dautrack::TOFExpMom, + dautrack::TOFExpTimeEl, + dautrack::TOFExpTimePi, + dautrack::TOFExpTimeKa, + dautrack::TOFExpTimePr); + +using DauTrackTOFPIDs = DauTrackTOFPIDs_003; // second gen: with collision Id, with TOFExpMom namespace v0data { @@ -211,6 +232,34 @@ DECLARE_SOA_COLUMN(TOFNSigmaALaPi, tofNSigmaALaPi, float); //! positive DECLARE_SOA_COLUMN(TOFNSigmaK0PiPlus, tofNSigmaK0PiPlus, float); //! positive track NSigma from pion <- k0short expectation DECLARE_SOA_COLUMN(TOFNSigmaK0PiMinus, tofNSigmaK0PiMinus, float); //! negative track NSigma from pion <- k0short expectation +// for wrong hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElPosFromPhoton, tofNSigmaElPosFromPhoton, float); //! n sigma of positive track from photon conversion under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElNegFromPhoton, tofNSigmaElNegFromPhoton, float); //! n sigma of negative track from photon conversion under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiPosFromPhoton, tofNSigmaPiPosFromPhoton, float); //! n sigma of positive track from photon conversion under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiNegFromPhoton, tofNSigmaPiNegFromPhoton, float); //! n sigma of negative track from photon conversion under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaPosFromPhoton, tofNSigmaKaPosFromPhoton, float); //! n sigma of positive track from photon conversion under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaNegFromPhoton, tofNSigmaKaNegFromPhoton, float); //! n sigma of negative track from photon conversion under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrPosFromPhoton, tofNSigmaPrPosFromPhoton, float); //! n sigma of positive track from photon conversion under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrNegFromPhoton, tofNSigmaPrNegFromPhoton, float); //! n sigma of negative track from photon conversion under proton hypothesis + +DECLARE_SOA_COLUMN(TOFNSigmaElPosFromK0S, tofNSigmaElPosFromK0S, float); //! n sigma of positive track from K0S under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElNegFromK0S, tofNSigmaElNegFromK0S, float); //! n sigma of negative track from K0S under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiPosFromK0S, tofNSigmaPiPosFromK0S, float); //! n sigma of positive track from K0S under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiNegFromK0S, tofNSigmaPiNegFromK0S, float); //! n sigma of negative track from K0S under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaPosFromK0S, tofNSigmaKaPosFromK0S, float); //! n sigma of positive track from K0S under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaNegFromK0S, tofNSigmaKaNegFromK0S, float); //! n sigma of negative track from K0S under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrPosFromK0S, tofNSigmaPrPosFromK0S, float); //! n sigma of positive track from K0S under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrNegFromK0S, tofNSigmaPrNegFromK0S, float); //! n sigma of negative track from K0S under proton hypothesis + +DECLARE_SOA_COLUMN(TOFNSigmaElPosFromLambda, tofNSigmaElPosFromLambda, float); //! n sigma of positive track from Lambda under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElNegFromLambda, tofNSigmaElNegFromLambda, float); //! n sigma of negative track from Lambda under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiPosFromLambda, tofNSigmaPiPosFromLambda, float); //! n sigma of positive track from Lambda under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiNegFromLambda, tofNSigmaPiNegFromLambda, float); //! n sigma of negative track from Lambda under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaPosFromLambda, tofNSigmaKaPosFromLambda, float); //! n sigma of positive track from Lambda under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaNegFromLambda, tofNSigmaKaNegFromLambda, float); //! n sigma of negative track from Lambda under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrPosFromLambda, tofNSigmaPrPosFromLambda, float); //! n sigma of positive track from Lambda under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrNegFromLambda, tofNSigmaPrNegFromLambda, float); //! n sigma of negative track from Lambda under proton hypothesis + // dynamics to replace hasTOF (note: that condition does not match track hasTOF!) // note: only single hypothesis check necessary; other hypotheses will always be valid DECLARE_SOA_DYNAMIC_COLUMN(PositiveHasTOF, positiveHasTOF, //! positive daughter TOF calculation valid @@ -317,10 +366,20 @@ DECLARE_SOA_TABLE(V0TOFNSigmas, "AOD", "V0TOFNSIGMA", // processed NSigma table v0data::TofAntiLambdaCompatibility, v0data::TofK0ShortCompatibility); +DECLARE_SOA_TABLE(V0TOFNSigmasAll, "AOD", "V0TOFNSIGMAALL", // processed NSigma table (for analysis) including wrong hypothesis + v0data::TOFNSigmaElPosFromPhoton, v0data::TOFNSigmaElPosFromK0S, v0data::TOFNSigmaElPosFromLambda, + v0data::TOFNSigmaElNegFromPhoton, v0data::TOFNSigmaElNegFromK0S, v0data::TOFNSigmaElNegFromLambda, + v0data::TOFNSigmaPiPosFromPhoton, v0data::TOFNSigmaPiPosFromK0S, v0data::TOFNSigmaPiPosFromLambda, + v0data::TOFNSigmaPiNegFromPhoton, v0data::TOFNSigmaPiNegFromK0S, v0data::TOFNSigmaPiNegFromLambda, + v0data::TOFNSigmaKaPosFromPhoton, v0data::TOFNSigmaKaPosFromK0S, v0data::TOFNSigmaKaPosFromLambda, + v0data::TOFNSigmaKaNegFromPhoton, v0data::TOFNSigmaKaNegFromK0S, v0data::TOFNSigmaKaNegFromLambda, + v0data::TOFNSigmaPrPosFromPhoton, v0data::TOFNSigmaPrPosFromK0S, v0data::TOFNSigmaPrPosFromLambda, + v0data::TOFNSigmaPrNegFromPhoton, v0data::TOFNSigmaPrNegFromK0S, v0data::TOFNSigmaPrNegFromLambda); + namespace cascdata { // define constants for NSigma operation -const float kNoTOFValue = -1e+6; +constexpr float kNoTOFValue = -1e+6; const float kEpsilon = 1e-4; // lengths as stored in the AO2D for TOF calculations @@ -354,6 +413,35 @@ DECLARE_SOA_COLUMN(TOFNSigmaOmLaPi, tofNSigmaOmLaPi, float); //! meson track NSi DECLARE_SOA_COLUMN(TOFNSigmaOmLaPr, tofNSigmaOmLaPr, float); //! baryon track NSigma from proton <- lambda <- om expectation DECLARE_SOA_COLUMN(TOFNSigmaOmKa, tofNSigmaOmKa, float); //! bachelor track NSigma from kaon <- om expectation +// for wrong hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElPosFromLambdaFromXi, tofNSigmaElPosFromLambdaFromXi, float); //! nigma of positive track from Lambda from Xi under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElNegFromLambdaFromXi, tofNSigmaElNegFromLambdaFromXi, float); //! nigma of negative track from Lambda from Xi under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElFromXi, tofNSigmaElFromXi, float); //! nigma of bachelor track from Xi under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElPosFromLambdaFromOmega, tofNSigmaElPosFromLambdaFromOmega, float); //! nigma of positive track from Lambda from Omega under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElNegFromLambdaFromOmega, tofNSigmaElNegFromLambdaFromOmega, float); //! nigma of negative track from Lambda from Omega under electron hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaElFromOmega, tofNSigmaElFromOmega, float); //! nigma of bachelor track from Omega under electron hypothesis + +DECLARE_SOA_COLUMN(TOFNSigmaPiPosFromLambdaFromXi, tofNSigmaPiPosFromLambdaFromXi, float); //! nigma of positive track from Lambda from Xi under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiNegFromLambdaFromXi, tofNSigmaPiNegFromLambdaFromXi, float); //! nigma of negative track from Lambda from Xi under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiFromXi, tofNSigmaPiFromXi, float); //! nigma of bachelor track from Xi under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiPosFromLambdaFromOmega, tofNSigmaPiPosFromLambdaFromOmega, float); //! nigma of positive track from Lambda from Omega under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiNegFromLambdaFromOmega, tofNSigmaPiNegFromLambdaFromOmega, float); //! nigma of negative track from Lambda from Omega under pion hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPiFromOmega, tofNSigmaPiFromOmega, float); //! nigma of bachelor track from Omega under pion hypothesis + +DECLARE_SOA_COLUMN(TOFNSigmaKaPosFromLambdaFromXi, tofNSigmaKaPosFromLambdaFromXi, float); //! nigma of positive track from Lambda from Xi under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaNegFromLambdaFromXi, tofNSigmaKaNegFromLambdaFromXi, float); //! nigma of negative track from Lambda from Xi under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaFromXi, tofNSigmaKaFromXi, float); //! nigma of bachelor track from Xi under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaPosFromLambdaFromOmega, tofNSigmaKaPosFromLambdaFromOmega, float); //! nigma of positive track from Lambda from Omega under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaNegFromLambdaFromOmega, tofNSigmaKaNegFromLambdaFromOmega, float); //! nigma of negative track from Lambda from Omega under kaon hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaKaFromOmega, tofNSigmaKaFromOmega, float); //! nigma of bachelor track from Omega under kaon hypothesis + +DECLARE_SOA_COLUMN(TOFNSigmaPrPosFromLambdaFromXi, tofNSigmaPrPosFromLambdaFromXi, float); //! nigma of positive track from Lambda from Xi under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrNegFromLambdaFromXi, tofNSigmaPrNegFromLambdaFromXi, float); //! nigma of negative track from Lambda from Xi under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrFromXi, tofNSigmaPrFromXi, float); //! nigma of bachelor track from Xi under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrPosFromLambdaFromOmega, tofNSigmaPrPosFromLambdaFromOmega, float); //! nigma of positive track from Lambda from Omega under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrNegFromLambdaFromOmega, tofNSigmaPrNegFromLambdaFromOmega, float); //! nigma of negative track from Lambda from Omega under proton hypothesis +DECLARE_SOA_COLUMN(TOFNSigmaPrFromOmega, tofNSigmaPrFromOmega, float); //! nigma of bachelor track from Omega under proton hypothesis + // dynamics to replace hasTOF (note: that condition does not match track hasTOF!) // note: only single hypothesis check necessary; other hypotheses will always be valid DECLARE_SOA_DYNAMIC_COLUMN(PositiveHasTOF, positiveHasTOF, //! positive daughter TOF calculation valid @@ -437,6 +525,15 @@ DECLARE_SOA_TABLE(CascTOFNSigmas, "AOD", "CascTOFNSigmas", // Nsigmas for cascad cascdata::BachelorHasTOF, cascdata::TofXiCompatibility, cascdata::TofOmegaCompatibility); +DECLARE_SOA_TABLE(CascTOFNSigmasAll, "AOD", "CascTOFNSigmasAll", // Nsigmas for cascades including wrong hypothesis + cascdata::TOFNSigmaElPosFromLambdaFromXi, cascdata::TOFNSigmaElNegFromLambdaFromXi, cascdata::TOFNSigmaElFromXi, + cascdata::TOFNSigmaElPosFromLambdaFromOmega, cascdata::TOFNSigmaElNegFromLambdaFromOmega, cascdata::TOFNSigmaElFromOmega, + cascdata::TOFNSigmaPiPosFromLambdaFromXi, cascdata::TOFNSigmaPiNegFromLambdaFromXi, cascdata::TOFNSigmaPiFromXi, + cascdata::TOFNSigmaPiPosFromLambdaFromOmega, cascdata::TOFNSigmaPiNegFromLambdaFromOmega, cascdata::TOFNSigmaPiFromOmega, + cascdata::TOFNSigmaKaPosFromLambdaFromXi, cascdata::TOFNSigmaKaNegFromLambdaFromXi, cascdata::TOFNSigmaKaFromXi, + cascdata::TOFNSigmaKaPosFromLambdaFromOmega, cascdata::TOFNSigmaKaNegFromLambdaFromOmega, cascdata::TOFNSigmaKaFromOmega, + cascdata::TOFNSigmaPrPosFromLambdaFromXi, cascdata::TOFNSigmaPrNegFromLambdaFromXi, cascdata::TOFNSigmaPrFromXi, + cascdata::TOFNSigmaPrPosFromLambdaFromOmega, cascdata::TOFNSigmaPrNegFromLambdaFromOmega, cascdata::TOFNSigmaPrFromOmega); } // namespace o2::aod #endif // PWGLF_DATAMODEL_LFSTRANGENESSPIDTABLES_H_ diff --git a/PWGLF/DataModel/LFStrangenessTables.h b/PWGLF/DataModel/LFStrangenessTables.h index 33d06f159d9..56322a0e47c 100644 --- a/PWGLF/DataModel/LFStrangenessTables.h +++ b/PWGLF/DataModel/LFStrangenessTables.h @@ -19,7 +19,6 @@ #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/McCollisionExtra.h" // IWYU pragma: keep (FIXME: not used, remove asap) #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/Qvectors.h" @@ -45,6 +44,36 @@ DECLARE_SOA_COLUMN(DataframeID, dataframeID, uint64_t); //! Data frame ID (what DECLARE_SOA_TABLE(StraOrigins, "AOD", "STRAORIGIN", //! Table which contains the IDs of all dataframes merged into this dataframe o2::soa::Index<>, straorigin::DataframeID); +// for keeping track of the number of selected collisions +namespace straselections +{ +// Event selection criteria +enum EventSelectionFlags { + kIsTriggerTVX = 0, // FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level + kNoITSROFrameBorder, // bunch crossing is far from ITS RO Frame border + kNoTimeFrameBorder, // bunch crossing is far from Time Frame borders + kIsGoodZvtxAcceptance, // Zvtx within detector acceptance + kNoSameBunchPileup, // reject collisions in case of pileup with another collision in the same foundBC + kIsGoodRCT, // Good RCT + kNsel // counter +}; + +DECLARE_SOA_COLUMN(TotalNbrOfCollisions, totalNbrOfCollisions, int); //! total number of analysed collisions +DECLARE_SOA_COLUMN(TotalNbrOfSelCollisions, totalNbrOfSelCollisions, int); //! total number of selected collisions +DECLARE_SOA_COLUMN(TotalIsTriggerTVXCollisions, totalIsTriggerTVXCollisions, int); //! N selected collisions after applying IsTriggerTVX +DECLARE_SOA_COLUMN(TotalNoITSROFBorderCollisions, totalNoITSROFBorderCollisions, int); //! N selected collisions after applying IsTriggerTVX, NoITSROF +DECLARE_SOA_COLUMN(TotalNoTFBorderCollisions, totalNoTFBorderCollisions, int); //! N selected collisions after applying IsTriggerTVX, NoITSROF, NoTF +DECLARE_SOA_COLUMN(TotalIsGoodZvtxCollisions, totalIsGoodZvtxCollisions, int); //! N selected collisions after applying IsTriggerTVX, NoITSROF, NoTF, |Zvtx| < X cm +DECLARE_SOA_COLUMN(TotalNoSBPileupCollisions, totalNoSBPileupCollisions, int); //! N selected collisions after applying IsTriggerTVX, NoITSROF, NoTF, |Zvtx| < X cm, NoSameBunchPileup +DECLARE_SOA_COLUMN(TotalIsGoodRCTCollisions, totalIsGoodRCTCollisions, int); //! N selected collisions after applying IsTriggerTVX, NoITSROF, NoTF, |Zvtx| < X cm, NoSameBunchPileup, Good RCT +} // namespace straselections + +DECLARE_SOA_TABLE(StraSelections, "AOD", "STRASELECTIONS", //! keep track of the number of analysed collisions in this DF + o2::soa::Index<>, straselections::TotalNbrOfCollisions, straselections::TotalNbrOfSelCollisions, + straselections::TotalIsTriggerTVXCollisions, straselections::TotalNoITSROFBorderCollisions, + straselections::TotalNoTFBorderCollisions, straselections::TotalIsGoodZvtxCollisions, + straselections::TotalNoSBPileupCollisions, straselections::TotalIsGoodRCTCollisions); + namespace stracollision { DECLARE_SOA_DYNAMIC_COLUMN(IsUPC, isUPC, //! check whether this is a UPC or hadronic collision @@ -66,6 +95,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(EnergyCommonZNC, energyCommonZNC, //! get the total s // event time DECLARE_SOA_COLUMN(EventTime, eventTime, float); //! event time (FT0, TOF) for TOF PID (stored once per event) +// event time +DECLARE_SOA_COLUMN(EventTimeErr, eventTimeErr, float); //! event time (FT0, TOF) for TOF PID (stored once per event) } // namespace stracollision //______________________________________________________ @@ -279,15 +310,60 @@ DECLARE_SOA_TABLE_VERSIONED(StraEvSels_005, "AOD", "STRAEVSELS", 5, //! // stracollision::EnergyCommonZNC, stracollision::IsUPC); -DECLARE_SOA_TABLE(StraEvSelExtras, "AOD", "STRAEVSELEXTRAS", //! debug information - udzdc::TimeZNA, // UPC info: re-assigned ZN-A time, in case of SG event, from the most active bc - udzdc::TimeZNC, // UPC info: re-assigned ZN-C time, in case of SG event, from the most active bc - udcollision::TimeFDDA, // Average A-side time (ns) - udcollision::TimeFDDC, // Average C-side time (ns) - udcollision::TimeFV0A, // Average A-side time (ns) - udcollision::TimeFT0A, // Average A-side time (ns) - udcollision::TimeFT0C, // Average C-side time (ns) - udcollision::TriggerMaskFT0); // 8 trigger bits: OrA, OrC, Semi-central, Central, Vertex, IsActiveA, IsActiveC, IsFlangeEvent +DECLARE_SOA_TABLE_VERSIONED(StraEvSels_006, "AOD", "STRAEVSELS", 6, //! debug information + evsel::Sel8, evsel::Selection, //! event selection: sel8 + mult::MultFT0A, mult::MultFT0C, mult::MultFV0A, // FIT detectors + mult::MultFDDA, mult::MultFDDC, + mult::MultNTracksPVeta1, // track multiplicities with eta cut for INEL>0 + mult::MultPVTotalContributors, // number of PV contribs total + mult::MultNTracksGlobal, // global track multiplicities + collision::Flags, // Contains Vertex::Flags, with most notably the UPCMode to know whether the vertex has been found using UPC settings + evsel::Alias, // trigger aliases (e.g. kTVXinTRD for v2) + evsel::Rct); // Bitmask of RCT flags + +DECLARE_SOA_TABLE(StraEvSelExtras_000, "AOD", "STRAEVSELEXTRAS", //! debug information + udzdc::TimeZNA, // UPC info: re-assigned ZN-A time, in case of SG event, from the most active bc + udzdc::TimeZNC, // UPC info: re-assigned ZN-C time, in case of SG event, from the most active bc + udcollision::TimeFDDA, // Average A-side time (ns) + udcollision::TimeFDDC, // Average C-side time (ns) + udcollision::TimeFV0A, // Average A-side time (ns) + udcollision::TimeFT0A, // Average A-side time (ns) + udcollision::TimeFT0C, // Average C-side time (ns) + udcollision::TriggerMaskFT0); // 8 trigger bits: OrA, OrC, Semi-central, Central, Vertex, IsActiveA, IsActiveC, IsFlangeEvent + +DECLARE_SOA_TABLE_VERSIONED(StraEvSelExtras_001, "AOD", "STRAEVSELEXTRAS", 1, //! debug information + mult::MultZNA, mult::MultZNC, mult::MultZEM1, // ZDC signals + mult::MultZEM2, mult::MultZPA, mult::MultZPC, + mult::MultNTracksITSTPC, // track multiplicities, PV contribs, no eta cut + mult::MultAllTracksTPCOnly, // TPConly track multiplicities, all, no eta cut + mult::MultAllTracksITSTPC, // ITSTPC track multiplicities, all, no eta cut + evsel::NumTracksInTimeRange, // add occupancy in specified time interval by a number of tracks from nearby collisions + evsel::SumAmpFT0CInTimeRange, // add occupancy in specified time interval by a sum of FT0C amplitudes from nearby collisions + udcollision::TimeFDDA, // Average A-side time (ns) + udcollision::TimeFDDC, // Average C-side time (ns) + udcollision::TimeFV0A, // Average A-side time (ns) + udcollision::TimeFT0A, // Average A-side time (ns) + udcollision::TimeFT0C, // Average C-side time (ns) + udcollision::TriggerMaskFT0, // 8 trigger bits: OrA, OrC, Semi-central, Central, Vertex, IsActiveA, IsActiveC, IsFlangeEvent + udcollision::GapSide, // UPC info: 0 for side A, 1 for side C, 2 for both sides, 3 neither A or C, 4 not enough or too many pv contributors + udcollision::TotalFT0AmplitudeA, // UPC info: re-assigned FT0-A amplitude, in case of SG event, from the most active bc + udcollision::TotalFT0AmplitudeC, // UPC info: re-assigned FT0-C amplitude, in case of SG event, from the most active bc + udcollision::TotalFV0AmplitudeA, // UPC info: re-assigned FV0-A amplitude, in case of SG event, from the most active bc + udcollision::TotalFDDAmplitudeA, // UPC info: re-assigned FDD-A amplitude, in case of SG event, from the most active bc + udcollision::TotalFDDAmplitudeC, // UPC info: re-assigned FDD-C amplitude, in case of SG event, from the most active bc + udzdc::TimeZNA, // UPC info: re-assigned ZN-A time, in case of SG event, from the most active bc + udzdc::TimeZNC, // UPC info: re-assigned ZN-C time, in case of SG event, from the most active bc + udzdc::EnergyCommonZNA, // UPC info: re-assigned ZN-A amplitude, in case of SG event, from the most active bc + udzdc::EnergyCommonZNC, // UPC info: re-assigned ZN-C amplitude, in case of SG event, from the most active bc + // Dynamic columns for manipulating information + // stracollision::TotalFV0AmplitudeA, + // stracollision::TotalFT0AmplitudeA, + // stracollision::TotalFT0AmplitudeC, + // stracollision::TotalFDDAmplitudeA, + // stracollision::TotalFDDAmplitudeC, + // stracollision::EnergyCommonZNA, + // stracollision::EnergyCommonZNC, + stracollision::IsUPC); DECLARE_SOA_TABLE(StraEvSelsRun2, "AOD", "STRAEVSELSRUN2", //! debug information evsel::Sel8, evsel::Sel7, evsel::Selection, //! event selection: sel8 @@ -326,13 +402,17 @@ DECLARE_SOA_TABLE(StraStamps_000, "AOD", "STRASTAMPS", //! information for ID-in bc::RunNumber, timestamp::Timestamp); DECLARE_SOA_TABLE_VERSIONED(StraStamps_001, "AOD", "STRASTAMPS", 1, //! information for ID-ing mag field if needed bc::RunNumber, timestamp::Timestamp, bc::GlobalBC); -DECLARE_SOA_TABLE(StraEvTimes, "AOD", "STRAEVTIMES", //! event time (FT0, TOF) +DECLARE_SOA_TABLE(StraEvTimes_000, "AOD", "STRAEVTIMES", //! event time (FT0, TOF) stracollision::EventTime); +DECLARE_SOA_TABLE_VERSIONED(StraEvTimes_001, "AOD", "STRAEVTIMES", 1, //! event time (FT0, TOF) + stracollision::EventTime, stracollision::EventTimeErr); using StraRawCents = StraRawCents_004; using StraCents = StraCents_002; -using StraEvSels = StraEvSels_005; +using StraEvSels = StraEvSels_006; +using StraEvSelExtras = StraEvSelExtras_001; using StraStamps = StraStamps_001; +using StraEvTimes = StraEvTimes_001; using StraCollision = StraCollisions::iterator; using StraCent = StraCents::iterator; @@ -360,8 +440,10 @@ DECLARE_SOA_TABLE(StraMCCollMults_000, "AOD", "STRAMCCOLLMULTS", //! MC collisio mult::MultMCFT0A, mult::MultMCFT0C, mult::MultMCNParticlesEta05, mult::MultMCNParticlesEta08, mult::MultMCNParticlesEta10, o2::soa::Marker<2>); DECLARE_SOA_TABLE_VERSIONED(StraMCCollMults_001, "AOD", "STRAMCCOLLMULTS", 1, //! MC collision multiplicities mult::MultMCFT0A, mult::MultMCFT0C, mult::MultMCNParticlesEta05, mult::MultMCNParticlesEta08, mult::MultMCNParticlesEta10, stramccollision::TotalMultMCParticles); +DECLARE_SOA_TABLE_VERSIONED(StraMCCollMults_002, "AOD", "STRAMCCOLLMULTS", 2, //! MC collision multiplicities + mult::MultMCFT0A, mult::MultMCFT0C, mult::MultMCFV0A, mult::MultMCFDDA, mult::MultMCFDDC, mult::MultMCNParticlesEta05, mult::MultMCNParticlesEta08, mult::MultMCNParticlesEta10, stramccollision::TotalMultMCParticles); -using StraMCCollMults = StraMCCollMults_001; +using StraMCCollMults = StraMCCollMults_002; namespace dautrack { diff --git a/PWGLF/DataModel/LFhe3HadronTables.h b/PWGLF/DataModel/LFhe3HadronTables.h index 719776fabde..b2876d923f4 100644 --- a/PWGLF/DataModel/LFhe3HadronTables.h +++ b/PWGLF/DataModel/LFhe3HadronTables.h @@ -37,7 +37,6 @@ DECLARE_SOA_COLUMN(DCAxyHe3, dcaxyHe3, float); DECLARE_SOA_COLUMN(DCAzHe3, dcazHe3, float); DECLARE_SOA_COLUMN(DCAxyHad, dcaxyHad, float); DECLARE_SOA_COLUMN(DCAzHad, dcazHad, float); -DECLARE_SOA_COLUMN(DCApair, dcapair, float); DECLARE_SOA_COLUMN(SignalTPCHe3, signalTPCHe3, float); DECLARE_SOA_COLUMN(InnerParamTPCHe3, innerParamTPCHe3, float); @@ -45,9 +44,11 @@ DECLARE_SOA_COLUMN(SignalTPCHad, signalTPCHad, float); DECLARE_SOA_COLUMN(InnerParamTPCHad, innerParamTPCHad, float); DECLARE_SOA_COLUMN(NClsTPCHe3, nClsTPCHe3, uint8_t); DECLARE_SOA_COLUMN(NSigmaTPCHe3, nSigmaTPCHe3, float); +DECLARE_SOA_COLUMN(NSigmaTPCHad, nSigmaTPCHad, float); DECLARE_SOA_COLUMN(NSigmaTPCHadPi, nSigmaTPCHadPi, float); DECLARE_SOA_COLUMN(NSigmaTPCHadKa, nSigmaTPCHadKa, float); DECLARE_SOA_COLUMN(NSigmaTPCHadPr, nSigmaTPCHadPr, float); +DECLARE_SOA_COLUMN(NSigmaTOFHad, nSigmaTOFHad, float); DECLARE_SOA_COLUMN(NSigmaTOFHadPi, nSigmaTOFHadPi, float); DECLARE_SOA_COLUMN(NSigmaTOFHadKa, nSigmaTOFHadKa, float); DECLARE_SOA_COLUMN(NSigmaTOFHadPr, nSigmaTOFHadPr, float); @@ -79,10 +80,12 @@ DECLARE_SOA_COLUMN(SignedPtMC, signedPtMC, float); DECLARE_SOA_COLUMN(MassMC, massMC, float); DECLARE_SOA_COLUMN(CollisionId, collisionId, int64_t); +DECLARE_SOA_COLUMN(CollisionSelectionFlag, collisionSelectionFlag, int32_t); DECLARE_SOA_COLUMN(ZVertex, zVertex, float); DECLARE_SOA_COLUMN(Multiplicity, multiplicity, uint16_t); DECLARE_SOA_COLUMN(CentralityFT0C, centFT0C, float); DECLARE_SOA_COLUMN(MultiplicityFT0C, multiplicityFT0C, float); +DECLARE_SOA_COLUMN(TrackOccupancy, trackOccupancy, int); /* Flags: 0 - both primary, 1 - both from Li4, @@ -104,29 +107,21 @@ DECLARE_SOA_TABLE(he3HadronTable, "AOD", "HE3HADTABLE", he3HadronTablesNS::DCAzHe3, he3HadronTablesNS::DCAxyHad, he3HadronTablesNS::DCAzHad, - he3HadronTablesNS::DCApair, he3HadronTablesNS::SignalTPCHe3, he3HadronTablesNS::InnerParamTPCHe3, he3HadronTablesNS::SignalTPCHad, he3HadronTablesNS::InnerParamTPCHad, he3HadronTablesNS::NClsTPCHe3, he3HadronTablesNS::NSigmaTPCHe3, - he3HadronTablesNS::NSigmaTPCHadPi, - he3HadronTablesNS::NSigmaTPCHadKa, - he3HadronTablesNS::NSigmaTPCHadPr, - he3HadronTablesNS::NSigmaTOFHadPi, - he3HadronTablesNS::NSigmaTOFHadKa, - he3HadronTablesNS::NSigmaTOFHadPr, + he3HadronTablesNS::NSigmaTPCHad, + he3HadronTablesNS::NSigmaTOFHad, he3HadronTablesNS::Chi2TPCHe3, he3HadronTablesNS::Chi2TPCHad, - he3HadronTablesNS::MassTOFHe3, he3HadronTablesNS::MassTOFHad, he3HadronTablesNS::PIDtrkHe3, - he3HadronTablesNS::PIDtrkHad, he3HadronTablesNS::ItsClusterSizeHe3, he3HadronTablesNS::ItsClusterSizeHad, - he3HadronTablesNS::SharedClustersHe3, - he3HadronTablesNS::SharedClustersHad) + he3HadronTablesNS::SharedClustersHe3) DECLARE_SOA_TABLE(he3HadronTableMC, "AOD", "HE3HADTABLEMC", he3HadronTablesNS::PtMCHe3, he3HadronTablesNS::EtaMCHe3, @@ -139,13 +134,25 @@ DECLARE_SOA_TABLE(he3HadronTableMC, "AOD", "HE3HADTABLEMC", he3HadronTablesNS::Flags) DECLARE_SOA_TABLE(he3HadronMult, "AOD", "HE3HADMULT", he3HadronTablesNS::CollisionId, + he3HadronTablesNS::CollisionSelectionFlag, he3HadronTablesNS::ZVertex, he3HadronTablesNS::Multiplicity, he3HadronTablesNS::CentralityFT0C, - he3HadronTablesNS::MultiplicityFT0C) + he3HadronTablesNS::MultiplicityFT0C, + he3HadronTablesNS::TrackOccupancy) DECLARE_SOA_TABLE(he3HadronQa, "AOD", "HE3HADQA", he3HadronTablesNS::TrackIDHe3, - he3HadronTablesNS::TrackIDHad) + he3HadronTablesNS::TrackIDHad, + he3HadronTablesNS::MassTOFHe3, + he3HadronTablesNS::PIDtrkHad, + he3HadronTablesNS::SharedClustersHad) +DECLARE_SOA_TABLE(he3HadronPid, "AOD", "HE3HADPID", + he3HadronTablesNS::NSigmaTPCHadPi, + he3HadronTablesNS::NSigmaTPCHadKa, + he3HadronTablesNS::NSigmaTPCHadPr, + he3HadronTablesNS::NSigmaTOFHadPi, + he3HadronTablesNS::NSigmaTOFHadKa, + he3HadronTablesNS::NSigmaTOFHadPr) } // namespace o2::aod diff --git a/PWGLF/DataModel/ReducedDoublePhiTables.h b/PWGLF/DataModel/ReducedDoublePhiTables.h index 61175fccfc8..7346cc1b1d5 100644 --- a/PWGLF/DataModel/ReducedDoublePhiTables.h +++ b/PWGLF/DataModel/ReducedDoublePhiTables.h @@ -27,6 +27,7 @@ namespace redphievent { DECLARE_SOA_COLUMN(NumPos, numPos, int); //! Number of positive Kaon DECLARE_SOA_COLUMN(NumNeg, numNeg, int); //! Number of negative Kaon +DECLARE_SOA_COLUMN(Centrality, centrality, float); //! Number of negative Kaon } // namespace redphievent DECLARE_SOA_TABLE(RedPhiEvents, "AOD", "REDPHIEVENT", o2::soa::Index<>, @@ -36,7 +37,8 @@ DECLARE_SOA_TABLE(RedPhiEvents, "AOD", "REDPHIEVENT", collision::PosZ, collision::NumContrib, redphievent::NumPos, - redphievent::NumNeg); + redphievent::NumNeg, + redphievent::Centrality); using RedPhiEvent = RedPhiEvents::iterator; namespace phitrack @@ -88,5 +90,244 @@ DECLARE_SOA_TABLE(PhiTracks, "AOD", "PHITRACK", phitrack::Phid2TOF); using PhiTrack = PhiTracks::iterator; + +namespace phiphipair +{ +DECLARE_SOA_INDEX_COLUMN(RedPhiEvent, redPhiEvent); + +DECLARE_SOA_COLUMN(Phi1Row, phi1Row, int64_t); +DECLARE_SOA_COLUMN(Phi2Row, phi2Row, int64_t); + +DECLARE_SOA_COLUMN(PairMass, pairMass, float); +DECLARE_SOA_COLUMN(PairPx, pairPx, float); +DECLARE_SOA_COLUMN(PairPy, pairPy, float); +DECLARE_SOA_COLUMN(PairPz, pairPz, float); + +DECLARE_SOA_COLUMN(Phi1Mass, phi1Mass, float); +DECLARE_SOA_COLUMN(Phi1Px, phi1Px, float); +DECLARE_SOA_COLUMN(Phi1Py, phi1Py, float); +DECLARE_SOA_COLUMN(Phi1Pz, phi1Pz, float); + +DECLARE_SOA_COLUMN(Phi2Mass, phi2Mass, float); +DECLARE_SOA_COLUMN(Phi2Px, phi2Px, float); +DECLARE_SOA_COLUMN(Phi2Py, phi2Py, float); +DECLARE_SOA_COLUMN(Phi2Pz, phi2Pz, float); + +// Kaon 1: positive daughter of phi 1 +DECLARE_SOA_COLUMN(K1Index, k1Index, int64_t); +DECLARE_SOA_COLUMN(K1Charge, k1Charge, int8_t); +DECLARE_SOA_COLUMN(K1DcaXY, k1DcaXY, float); +DECLARE_SOA_COLUMN(K1DcaZ, k1DcaZ, float); +DECLARE_SOA_COLUMN(K1DcaXYSig, k1DcaXYSig, float); +DECLARE_SOA_COLUMN(K1DcaZSig, k1DcaZSig, float); +DECLARE_SOA_COLUMN(K1DcaChi2, k1DcaChi2, float); +DECLARE_SOA_COLUMN(K1Px, k1Px, float); +DECLARE_SOA_COLUMN(K1Py, k1Py, float); +DECLARE_SOA_COLUMN(K1Pz, k1Pz, float); +DECLARE_SOA_COLUMN(K1TOFHit, k1TOFHit, int8_t); +DECLARE_SOA_COLUMN(K1TPC, k1TPC, float); +DECLARE_SOA_COLUMN(K1TOF, k1TOF, float); +DECLARE_SOA_COLUMN(K1ITS, k1ITS, float); + +// Kaon 2: negative daughter of phi 1 +DECLARE_SOA_COLUMN(K2Index, k2Index, int64_t); +DECLARE_SOA_COLUMN(K2Charge, k2Charge, int8_t); +DECLARE_SOA_COLUMN(K2DcaXY, k2DcaXY, float); +DECLARE_SOA_COLUMN(K2DcaZ, k2DcaZ, float); +DECLARE_SOA_COLUMN(K2DcaXYSig, k2DcaXYSig, float); +DECLARE_SOA_COLUMN(K2DcaZSig, k2DcaZSig, float); +DECLARE_SOA_COLUMN(K2DcaChi2, k2DcaChi2, float); +DECLARE_SOA_COLUMN(K2Px, k2Px, float); +DECLARE_SOA_COLUMN(K2Py, k2Py, float); +DECLARE_SOA_COLUMN(K2Pz, k2Pz, float); +DECLARE_SOA_COLUMN(K2TOFHit, k2TOFHit, int8_t); +DECLARE_SOA_COLUMN(K2TPC, k2TPC, float); +DECLARE_SOA_COLUMN(K2TOF, k2TOF, float); +DECLARE_SOA_COLUMN(K2ITS, k2ITS, float); + +// Kaon 3: positive daughter of phi 2 +DECLARE_SOA_COLUMN(K3Index, k3Index, int64_t); +DECLARE_SOA_COLUMN(K3Charge, k3Charge, int8_t); +DECLARE_SOA_COLUMN(K3DcaXY, k3DcaXY, float); +DECLARE_SOA_COLUMN(K3DcaZ, k3DcaZ, float); +DECLARE_SOA_COLUMN(K3DcaXYSig, k3DcaXYSig, float); +DECLARE_SOA_COLUMN(K3DcaZSig, k3DcaZSig, float); +DECLARE_SOA_COLUMN(K3DcaChi2, k3DcaChi2, float); +DECLARE_SOA_COLUMN(K3Px, k3Px, float); +DECLARE_SOA_COLUMN(K3Py, k3Py, float); +DECLARE_SOA_COLUMN(K3Pz, k3Pz, float); +DECLARE_SOA_COLUMN(K3TOFHit, k3TOFHit, int8_t); +DECLARE_SOA_COLUMN(K3TPC, k3TPC, float); +DECLARE_SOA_COLUMN(K3TOF, k3TOF, float); +DECLARE_SOA_COLUMN(K3ITS, k3ITS, float); + +// Kaon 4: negative daughter of phi 2 +DECLARE_SOA_COLUMN(K4Index, k4Index, int64_t); +DECLARE_SOA_COLUMN(K4Charge, k4Charge, int8_t); +DECLARE_SOA_COLUMN(K4DcaXY, k4DcaXY, float); +DECLARE_SOA_COLUMN(K4DcaZ, k4DcaZ, float); +DECLARE_SOA_COLUMN(K4DcaXYSig, k4DcaXYSig, float); +DECLARE_SOA_COLUMN(K4DcaZSig, k4DcaZSig, float); +DECLARE_SOA_COLUMN(K4DcaChi2, k4DcaChi2, float); +DECLARE_SOA_COLUMN(K4Px, k4Px, float); +DECLARE_SOA_COLUMN(K4Py, k4Py, float); +DECLARE_SOA_COLUMN(K4Pz, k4Pz, float); +DECLARE_SOA_COLUMN(K4TOFHit, k4TOFHit, int8_t); +DECLARE_SOA_COLUMN(K4TPC, k4TPC, float); +DECLARE_SOA_COLUMN(K4TOF, k4TOF, float); +DECLARE_SOA_COLUMN(K4ITS, k4ITS, float); + +// Four-track vertex fit +DECLARE_SOA_COLUMN(FitStatus, fitStatus, int8_t); +DECLARE_SOA_COLUMN(FitChi2, fitChi2, float); +DECLARE_SOA_COLUMN(FitNdf, fitNdf, int8_t); +DECLARE_SOA_COLUMN(FitChi2Ndf, fitChi2Ndf, float); + +// Primary vertex and fitted common vertex +DECLARE_SOA_COLUMN(PvX, pvX, float); +DECLARE_SOA_COLUMN(PvY, pvY, float); +DECLARE_SOA_COLUMN(PvZ, pvZ, float); + +DECLARE_SOA_COLUMN(VtxX, vtxX, float); +DECLARE_SOA_COLUMN(VtxY, vtxY, float); +DECLARE_SOA_COLUMN(VtxZ, vtxZ, float); + +DECLARE_SOA_COLUMN(DeltaVtxX, deltaVtxX, float); +DECLARE_SOA_COLUMN(DeltaVtxY, deltaVtxY, float); +DECLARE_SOA_COLUMN(DeltaVtxZ, deltaVtxZ, float); + +DECLARE_SOA_COLUMN(VertexLxy, vertexLxy, float); +DECLARE_SOA_COLUMN(VertexL3D, vertexL3D, float); +DECLARE_SOA_COLUMN(VertexLxyErr, vertexLxyErr, float); +DECLARE_SOA_COLUMN(VertexL3DErr, vertexL3DErr, float); +DECLARE_SOA_COLUMN(VertexLxySig, vertexLxySig, float); +DECLARE_SOA_COLUMN(VertexL3DSig, vertexL3DSig, float); + +// Resolution-weighted DCA quantities +DECLARE_SOA_COLUMN(NValidDca, nValidDca, int8_t); +DECLARE_SOA_COLUMN(SumDcaXYSig2, sumDcaXYSig2, float); +DECLARE_SOA_COLUMN(SumDcaZSig2, sumDcaZSig2, float); +DECLARE_SOA_COLUMN(SumDcaChi2, sumDcaChi2, float); +DECLARE_SOA_COLUMN(RmsDcaSig, rmsDcaSig, float); +DECLARE_SOA_COLUMN(MaxDcaChi2, maxDcaChi2, float); +DECLARE_SOA_COLUMN(MaxAbsDcaXYSig, maxAbsDcaXYSig, float); +DECLARE_SOA_COLUMN(MaxAbsDcaZSig, maxAbsDcaZSig, float); +} // namespace phiphipair + +// clang-format off +DECLARE_SOA_TABLE(PhiPhiPairs, "AOD", "PHIPHIPAIR", + o2::soa::Index<>, + phiphipair::RedPhiEventId, + phiphipair::Phi1Row, + phiphipair::Phi2Row, + + phiphipair::PairMass, + phiphipair::PairPx, + phiphipair::PairPy, + phiphipair::PairPz, + + phiphipair::Phi1Mass, + phiphipair::Phi1Px, + phiphipair::Phi1Py, + phiphipair::Phi1Pz, + + phiphipair::Phi2Mass, + phiphipair::Phi2Px, + phiphipair::Phi2Py, + phiphipair::Phi2Pz, + + phiphipair::K1Index, + phiphipair::K1Charge, + phiphipair::K1DcaXY, + phiphipair::K1DcaZ, + phiphipair::K1DcaXYSig, + phiphipair::K1DcaZSig, + phiphipair::K1DcaChi2, + phiphipair::K1Px, + phiphipair::K1Py, + phiphipair::K1Pz, + phiphipair::K1TOFHit, + phiphipair::K1TPC, + phiphipair::K1TOF, + phiphipair::K1ITS, + + phiphipair::K2Index, + phiphipair::K2Charge, + phiphipair::K2DcaXY, + phiphipair::K2DcaZ, + phiphipair::K2DcaXYSig, + phiphipair::K2DcaZSig, + phiphipair::K2DcaChi2, + phiphipair::K2Px, + phiphipair::K2Py, + phiphipair::K2Pz, + phiphipair::K2TOFHit, + phiphipair::K2TPC, + phiphipair::K2TOF, + phiphipair::K2ITS, + + phiphipair::K3Index, + phiphipair::K3Charge, + phiphipair::K3DcaXY, + phiphipair::K3DcaZ, + phiphipair::K3DcaXYSig, + phiphipair::K3DcaZSig, + phiphipair::K3DcaChi2, + phiphipair::K3Px, + phiphipair::K3Py, + phiphipair::K3Pz, + phiphipair::K3TOFHit, + phiphipair::K3TPC, + phiphipair::K3TOF, + phiphipair::K3ITS, + + phiphipair::K4Index, + phiphipair::K4Charge, + phiphipair::K4DcaXY, + phiphipair::K4DcaZ, + phiphipair::K4DcaXYSig, + phiphipair::K4DcaZSig, + phiphipair::K4DcaChi2, + phiphipair::K4Px, + phiphipair::K4Py, + phiphipair::K4Pz, + phiphipair::K4TOFHit, + phiphipair::K4TPC, + phiphipair::K4TOF, + phiphipair::K4ITS, + + phiphipair::FitStatus, + phiphipair::FitChi2, + phiphipair::FitNdf, + phiphipair::FitChi2Ndf, + + phiphipair::PvX, + phiphipair::PvY, + phiphipair::PvZ, + phiphipair::VtxX, + phiphipair::VtxY, + phiphipair::VtxZ, + phiphipair::DeltaVtxX, + phiphipair::DeltaVtxY, + phiphipair::DeltaVtxZ, + + phiphipair::VertexLxy, + phiphipair::VertexL3D, + phiphipair::VertexLxyErr, + phiphipair::VertexL3DErr, + phiphipair::VertexLxySig, + phiphipair::VertexL3DSig, + + phiphipair::NValidDca, + phiphipair::SumDcaXYSig2, + phiphipair::SumDcaZSig2, + phiphipair::SumDcaChi2, + phiphipair::RmsDcaSig, + phiphipair::MaxDcaChi2, + phiphipair::MaxAbsDcaXYSig, + phiphipair::MaxAbsDcaZSig); +// clang-format on + +using PhiPhiPair = PhiPhiPairs::iterator; } // namespace o2::aod #endif // PWGLF_DATAMODEL_REDUCEDDOUBLEPHITABLES_H_ diff --git a/PWGLF/DataModel/Vtx3BodyTables.h b/PWGLF/DataModel/Vtx3BodyTables.h index e18efbe2ddc..fbfb258a0f1 100644 --- a/PWGLF/DataModel/Vtx3BodyTables.h +++ b/PWGLF/DataModel/Vtx3BodyTables.h @@ -17,6 +17,8 @@ #ifndef PWGLF_DATAMODEL_VTX3BODYTABLES_H_ #define PWGLF_DATAMODEL_VTX3BODYTABLES_H_ +#include "PWGLF/DataModel/LFHypernucleiTables.h" + #include "Common/Core/RecoDecay.h" #include @@ -59,6 +61,9 @@ DECLARE_SOA_COLUMN(PzTrackPi, pzTrackPi, float); //! track1 pz at min DECLARE_SOA_COLUMN(PxTrackDe, pxTrackDe, float); //! track2 px at min DECLARE_SOA_COLUMN(PyTrackDe, pyTrackDe, float); //! track2 py at min DECLARE_SOA_COLUMN(PzTrackDe, pzTrackDe, float); //! track2 pz at min +DECLARE_SOA_COLUMN(XTrackPr, xTrackPr, float); //! track0 x at IU +DECLARE_SOA_COLUMN(XTrackPi, xTrackPi, float); //! track1 x at IU +DECLARE_SOA_COLUMN(XTrackDe, xTrackDe, float); //! track2 x at IU // DCAs to PV DECLARE_SOA_COLUMN(DCAXYTrackPrToPV, dcaXYtrackPrToPv, float); //! DCAXY of proton to PV (computed with KFParticle) @@ -94,6 +99,9 @@ DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, float); //! nsigma proton o DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, float); //! nsigma pion of TPC PID of the pion daughter DECLARE_SOA_COLUMN(TPCNSigmaDe, tpcNSigmaDe, float); //! nsigma deuteron of TPC PID of the bachelor daughter DECLARE_SOA_COLUMN(TPCNSigmaPiBach, tpcNSigmaPiBach, float); //! nsigma pion of TPC PID of the bachelor daughter +DECLARE_SOA_COLUMN(TPCSignalPr, tpcSignalPr, float); //! TPC signal of proton daughter +DECLARE_SOA_COLUMN(TPCSignalPi, tpcSignalPi, float); //! TPC signal of pion daughter +DECLARE_SOA_COLUMN(TPCSignalDe, tpcSignalDe, float); //! TPC signal of deuteron daughter DECLARE_SOA_COLUMN(TOFNSigmaDe, tofNSigmaDe, float); //! nsigma deuteron of TOF PID of the bachelor daughter DECLARE_SOA_COLUMN(PIDTrackingDe, pidTrackingDe, uint32_t); //! PID during tracking of bachelor daughter @@ -112,31 +120,37 @@ DECLARE_SOA_COLUMN(CovDeuteron, covDeuteron, float[21]); //! covariance matrix e DECLARE_SOA_COLUMN(VtxCovMat, vtxCovMat, float[21]); //! covariance matrix elements of candidate // Monte Carlo info -DECLARE_SOA_COLUMN(GenPx, genPx, float); // generated Px of the hypertriton in GeV/c -DECLARE_SOA_COLUMN(GenPy, genPy, float); // generated Py of the hypertriton in GeV/c -DECLARE_SOA_COLUMN(GenPz, genPz, float); // generated Pz of the hypertriton in GeV/c -DECLARE_SOA_COLUMN(GenX, genX, float); // generated decay vtx position X of the hypertriton -DECLARE_SOA_COLUMN(GenY, genY, float); // generated decay vtx position Y of the hypertriton -DECLARE_SOA_COLUMN(GenZ, genZ, float); // generated decay vtx position Z of the hypertriton -DECLARE_SOA_COLUMN(GenCt, genCt, float); // generated Ct of the hypertriton -DECLARE_SOA_COLUMN(GenPhi, genPhi, float); // generated Phi of the hypertriton -DECLARE_SOA_COLUMN(GenEta, genEta, float); // Eta of the hypertriton -DECLARE_SOA_COLUMN(GenRap, genRap, float); // generated rapidity of the hypertriton -DECLARE_SOA_COLUMN(GenPPr, genPPr, float); //! generated momentum proton daughter particle -DECLARE_SOA_COLUMN(GenPPi, genPPi, float); //! generated momentum pion daughter particle -DECLARE_SOA_COLUMN(GenPDe, genPDe, float); //! generated momentum deuteron daughter particle -DECLARE_SOA_COLUMN(GenPtPr, genPtPr, float); //! generated transverse momentum proton daughter particle -DECLARE_SOA_COLUMN(GenPtPi, genPtPi, float); //! generated transverse momentum pion daughter particle -DECLARE_SOA_COLUMN(GenPtDe, genPtDe, float); //! generated transverse momentum deuteron daughter particle -DECLARE_SOA_COLUMN(IsTrueH3L, isTrueH3l, bool); //! flag for true hypertriton candidate -DECLARE_SOA_COLUMN(IsTrueAntiH3L, isTrueAntiH3l, bool); //! flag for true anti-hypertriton candidate -DECLARE_SOA_COLUMN(MotherPdgCode, motherPdgCode, int); //! PDG code of the mother particle -DECLARE_SOA_COLUMN(PrPdgCode, prPdgCode, int); //! MC particle proton PDG code -DECLARE_SOA_COLUMN(PiPdgCode, piPdgCode, int); //! MC particle pion PDG code -DECLARE_SOA_COLUMN(DePdgCode, dePdgCode, int); //! MC particle deuteron PDG code -DECLARE_SOA_COLUMN(IsDePrimary, isDePrimary, bool); //! flag for deuteron daughter primary -DECLARE_SOA_COLUMN(IsSurvEvSel, isSurvEvSel, int); //! flag if reco collision survived event selection -DECLARE_SOA_COLUMN(IsReco, isreco, int); //! flag if candidate was reconstructed +DECLARE_SOA_COLUMN(GenPx, genPx, float); // generated Px of the hypertriton in GeV/c +DECLARE_SOA_COLUMN(GenPy, genPy, float); // generated Py of the hypertriton in GeV/c +DECLARE_SOA_COLUMN(GenPz, genPz, float); // generated Pz of the hypertriton in GeV/c +DECLARE_SOA_COLUMN(GenX, genX, float); // generated decay vtx position X of the hypertriton +DECLARE_SOA_COLUMN(GenY, genY, float); // generated decay vtx position Y of the hypertriton +DECLARE_SOA_COLUMN(GenZ, genZ, float); // generated decay vtx position Z of the hypertriton +DECLARE_SOA_COLUMN(GenCt, genCt, float); // generated Ct of the hypertriton +DECLARE_SOA_COLUMN(GenPhi, genPhi, float); // generated Phi of the hypertriton +DECLARE_SOA_COLUMN(GenEta, genEta, float); // Eta of the hypertriton +DECLARE_SOA_COLUMN(GenRap, genRap, float); // generated rapidity of the hypertriton +DECLARE_SOA_COLUMN(GenPxPr, genPxPr, float); //! generated Px of proton daughter particle +DECLARE_SOA_COLUMN(GenPyPr, genPyPr, float); //! generated Py of proton daughter particle +DECLARE_SOA_COLUMN(GenPzPr, genPzPr, float); //! generated Pz of proton daughter particle +DECLARE_SOA_COLUMN(GenPxPi, genPxPi, float); //! generated Px of pion daughter particle +DECLARE_SOA_COLUMN(GenPyPi, genPyPi, float); //! generated Py of pion daughter particle +DECLARE_SOA_COLUMN(GenPzPi, genPzPi, float); //! generated Pz of pion daughter particle +DECLARE_SOA_COLUMN(GenPxDe, genPxDe, float); //! generated Px of deuteron daughter particle +DECLARE_SOA_COLUMN(GenPyDe, genPyDe, float); //! generated Py of deuteron daughter particle +DECLARE_SOA_COLUMN(GenPzDe, genPzDe, float); //! generated Pz of deuteron daughter particle +DECLARE_SOA_COLUMN(MotherPdgCode, motherPdgCode, int); //! PDG code of the mother particle +DECLARE_SOA_COLUMN(PrPdgCode, prPdgCode, int); //! MC particle proton PDG code +DECLARE_SOA_COLUMN(PiPdgCode, piPdgCode, int); //! MC particle pion PDG code +DECLARE_SOA_COLUMN(DePdgCode, dePdgCode, int); //! MC particle deuteron PDG code +DECLARE_SOA_COLUMN(IsDePrimary, isDePrimary, bool); //! flag for deuteron daughter primary +DECLARE_SOA_COLUMN(IsSurvEvSel, isSurvEvSel, int); //! flag if reco collision survived event selection +DECLARE_SOA_COLUMN(IsReco, isreco, int); //! flag if candidate was reconstructed +DECLARE_SOA_COLUMN(MotherLabel, motherLabel, int); //! label of the mother particle (signal: MC index, bkg: negative value) + +// strangeness tracking information +DECLARE_SOA_COLUMN(ITSTrackDCAXYToSV, itsTrackDcaXYToSv, float); //! DCAxy of ITS track to SV +DECLARE_SOA_COLUMN(ITSTrackDCAZToSV, itsTrackDcaZToSv, float); //! DCAz of ITS track to SV // Derived expressions // Momenta @@ -216,6 +230,7 @@ DECLARE_SOA_TABLE(Vtx3BodyDatas, "AOD", "VTX3BODYDATA", //! vtx3body::PxTrackPr, vtx3body::PyTrackPr, vtx3body::PzTrackPr, vtx3body::PxTrackPi, vtx3body::PyTrackPi, vtx3body::PzTrackPi, vtx3body::PxTrackDe, vtx3body::PyTrackDe, vtx3body::PzTrackDe, + vtx3body::XTrackPr, vtx3body::XTrackPi, vtx3body::XTrackDe, vtx3body::DCAXYTrackPrToPV, vtx3body::DCAXYTrackPiToPV, vtx3body::DCAXYTrackDeToPV, vtx3body::DCATrackPrToPV, vtx3body::DCATrackPiToPV, vtx3body::DCATrackDeToPV, vtx3body::DCAXYTrackPrToPVProp, vtx3body::DCAXYTrackPiToPVProp, vtx3body::DCAXYTrackDeToPVProp, @@ -224,6 +239,7 @@ DECLARE_SOA_TABLE(Vtx3BodyDatas, "AOD", "VTX3BODYDATA", //! vtx3body::DCAVtxToDaughtersAv, vtx3body::CosPA, vtx3body::Ct, vtx3body::TPCNSigmaPr, vtx3body::TPCNSigmaPi, vtx3body::TPCNSigmaDe, vtx3body::TPCNSigmaPiBach, + vtx3body::TPCSignalPr, vtx3body::TPCSignalPi, vtx3body::TPCSignalDe, vtx3body::TOFNSigmaDe, vtx3body::ITSClSizePr, vtx3body::ITSClSizePi, vtx3body::ITSClSizeDe, vtx3body::TPCNClTrackPr, vtx3body::TPCNClTrackPi, vtx3body::TPCNClTrackDe, @@ -255,6 +271,27 @@ DECLARE_SOA_TABLE(Vtx3BodyCovs, "AOD", "VTX3BODYCOV", //! vtx3body::CovProton, vtx3body::CovPion, vtx3body::CovDeuteron, vtx3body::VtxCovMat); +// strangeness tracked extra table +DECLARE_SOA_TABLE(Vtx3BodyTrackedInfo, "AOD", "VTX3BODYTR", //! + vtx3body::ITSTrackDCAXYToSV, + vtx3body::ITSTrackDCAZToSV); + +// collision info table +DECLARE_SOA_TABLE(Vtx3BodyCollision, "AOD", "VTX3BODYCOLL", //! + o2::soa::Index<>, + hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::TrackOccupancyInTimeRange, hyperrec::Ft0cOccupancyInTimeRange, + hyperrec::XPrimVtx, hyperrec::YPrimVtx, hyperrec::ZPrimVtx, + hyperrec::RunNumber); + +DECLARE_SOA_TABLE(McVtx3BodyCollision, "AOD", "MCVTX3BODYCOLL", //! + o2::soa::Index<>, + hyperrec::IsRecoMCCollision, + hyperrec::CentralityFT0A, hyperrec::CentralityFT0C, hyperrec::CentralityFT0M, + hyperrec::TrackOccupancyInTimeRange, hyperrec::Ft0cOccupancyInTimeRange, + hyperrec::XPrimVtx, hyperrec::YPrimVtx, hyperrec::ZPrimVtx, + hyperrec::RunNumber); + // MC candidate table for analysis DECLARE_SOA_TABLE(McVtx3BodyDatas, "AOD", "MC3BODYDATA", //! o2::soa::Index<>, @@ -267,6 +304,7 @@ DECLARE_SOA_TABLE(McVtx3BodyDatas, "AOD", "MC3BODYDATA", //! vtx3body::PxTrackPr, vtx3body::PyTrackPr, vtx3body::PzTrackPr, vtx3body::PxTrackPi, vtx3body::PyTrackPi, vtx3body::PzTrackPi, vtx3body::PxTrackDe, vtx3body::PyTrackDe, vtx3body::PzTrackDe, + vtx3body::XTrackPr, vtx3body::XTrackPi, vtx3body::XTrackDe, vtx3body::DCAXYTrackPrToPV, vtx3body::DCAXYTrackPiToPV, vtx3body::DCAXYTrackDeToPV, vtx3body::DCATrackPrToPV, vtx3body::DCATrackPiToPV, vtx3body::DCATrackDeToPV, vtx3body::DCAXYTrackPrToPVProp, vtx3body::DCAXYTrackPiToPVProp, vtx3body::DCAXYTrackDeToPVProp, @@ -275,6 +313,7 @@ DECLARE_SOA_TABLE(McVtx3BodyDatas, "AOD", "MC3BODYDATA", //! vtx3body::DCAVtxToDaughtersAv, vtx3body::CosPA, vtx3body::Ct, vtx3body::TPCNSigmaPr, vtx3body::TPCNSigmaPi, vtx3body::TPCNSigmaDe, vtx3body::TPCNSigmaPiBach, + vtx3body::TPCSignalPr, vtx3body::TPCSignalPi, vtx3body::TPCSignalDe, vtx3body::TOFNSigmaDe, vtx3body::ITSClSizePr, vtx3body::ITSClSizePi, vtx3body::ITSClSizeDe, vtx3body::TPCNClTrackPr, vtx3body::TPCNClTrackPi, vtx3body::TPCNClTrackDe, @@ -285,10 +324,11 @@ DECLARE_SOA_TABLE(McVtx3BodyDatas, "AOD", "MC3BODYDATA", //! vtx3body::GenX, vtx3body::GenY, vtx3body::GenZ, vtx3body::GenCt, vtx3body::GenPhi, vtx3body::GenEta, vtx3body::GenRap, - vtx3body::GenPPr, vtx3body::GenPPi, vtx3body::GenPDe, - vtx3body::GenPtPr, vtx3body::GenPtPi, vtx3body::GenPtDe, - vtx3body::IsTrueH3L, vtx3body::IsTrueAntiH3L, + vtx3body::GenPxPr, vtx3body::GenPyPr, vtx3body::GenPzPr, + vtx3body::GenPxPi, vtx3body::GenPyPi, vtx3body::GenPzPi, + vtx3body::GenPxDe, vtx3body::GenPyDe, vtx3body::GenPzDe, vtx3body::IsReco, + vtx3body::MotherLabel, vtx3body::MotherPdgCode, vtx3body::PrPdgCode, vtx3body::PiPdgCode, vtx3body::DePdgCode, vtx3body::IsDePrimary, @@ -322,6 +362,100 @@ DECLARE_SOA_TABLE(McVtx3BodyDatas, "AOD", "MC3BODYDATA", //! using Vtx3BodyDatasCovs = soa::Join; using Vtx3BodyDatasCovsIndexed = soa::Join; +namespace nuclei3body +{ +DECLARE_SOA_COLUMN(SignHe3, signHe3, int8_t); //! He3 charge sign +DECLARE_SOA_COLUMN(SignDaughter1, signDaughter1, int8_t); //! daughter 1 charge sign +DECLARE_SOA_COLUMN(SignDaughter2, signDaughter2, int8_t); //! daughter 2 charge sign +DECLARE_SOA_COLUMN(Pt, pt, float); //! candidate transverse momentum +DECLARE_SOA_COLUMN(Eta, eta, float); //! candidate pseudorapidity +DECLARE_SOA_COLUMN(Phi, phi, float); //! candidate azimuth +DECLARE_SOA_COLUMN(PHe3, pHe3, float); //! magnitude of fitted, charge-corrected He3 momentum +DECLARE_SOA_COLUMN(PDaughter1, pDaughter1, float); //! magnitude of fitted daughter 1 momentum +DECLARE_SOA_COLUMN(PDaughter2, pDaughter2, float); //! magnitude of fitted daughter 2 momentum +DECLARE_SOA_COLUMN(Chi2, chi2, float); //! DCA-fitter chi2 at PCA +DECLARE_SOA_COLUMN(DcaDaughters, dcaDaughters, float); //! square root of DCA-fitter chi2 at PCA +DECLARE_SOA_COLUMN(CosPA, cosPA, float); //! cosine of pointing angle +DECLARE_SOA_COLUMN(DecayLength, decayLength, float); //! PV-to-SV distance + +DECLARE_SOA_COLUMN(DcaXYHe3ToPv, dcaXYHe3ToPv, float); //! He3 DCAxy to PV +DECLARE_SOA_COLUMN(DcaZHe3ToPv, dcaZHe3ToPv, float); //! He3 DCAz to PV +DECLARE_SOA_COLUMN(DcaXYDaughter1ToPv, dcaXYDaughter1ToPv, float); //! daughter 1 DCAxy to PV +DECLARE_SOA_COLUMN(DcaZDaughter1ToPv, dcaZDaughter1ToPv, float); //! daughter 1 DCAz to PV +DECLARE_SOA_COLUMN(DcaXYDaughter2ToPv, dcaXYDaughter2ToPv, float); //! daughter 2 DCAxy to PV +DECLARE_SOA_COLUMN(DcaZDaughter2ToPv, dcaZDaughter2ToPv, float); //! daughter 2 DCAz to PV + +DECLARE_SOA_COLUMN(TpcNSigmaHe3, tpcNSigmaHe3, float); //! custom He3 TPC n-sigma +DECLARE_SOA_COLUMN(TofNSigmaHe3, tofNSigmaHe3, float); //! He3 TOF n-sigma; sentinel without TOF +DECLARE_SOA_COLUMN(TpcNSigmaPiDaughter1, tpcNSigmaPiDaughter1, float); +DECLARE_SOA_COLUMN(TpcNSigmaKaDaughter1, tpcNSigmaKaDaughter1, float); +DECLARE_SOA_COLUMN(TpcNSigmaPrDaughter1, tpcNSigmaPrDaughter1, float); +DECLARE_SOA_COLUMN(TofNSigmaPiDaughter1, tofNSigmaPiDaughter1, float); +DECLARE_SOA_COLUMN(TofNSigmaKaDaughter1, tofNSigmaKaDaughter1, float); +DECLARE_SOA_COLUMN(TofNSigmaPrDaughter1, tofNSigmaPrDaughter1, float); +DECLARE_SOA_COLUMN(TpcNSigmaPiDaughter2, tpcNSigmaPiDaughter2, float); +DECLARE_SOA_COLUMN(TpcNSigmaKaDaughter2, tpcNSigmaKaDaughter2, float); +DECLARE_SOA_COLUMN(TpcNSigmaPrDaughter2, tpcNSigmaPrDaughter2, float); +DECLARE_SOA_COLUMN(TofNSigmaPiDaughter2, tofNSigmaPiDaughter2, float); +DECLARE_SOA_COLUMN(TofNSigmaKaDaughter2, tofNSigmaKaDaughter2, float); +DECLARE_SOA_COLUMN(TofNSigmaPrDaughter2, tofNSigmaPrDaughter2, float); + +DECLARE_SOA_COLUMN(TpcNClsHe3, tpcNClsHe3, uint16_t); +DECLARE_SOA_COLUMN(TpcNClsDaughter1, tpcNClsDaughter1, uint16_t); +DECLARE_SOA_COLUMN(TpcNClsDaughter2, tpcNClsDaughter2, uint16_t); +DECLARE_SOA_COLUMN(TpcCrossedRowsHe3, tpcCrossedRowsHe3, uint16_t); +DECLARE_SOA_COLUMN(TpcCrossedRowsDaughter1, tpcCrossedRowsDaughter1, uint16_t); +DECLARE_SOA_COLUMN(TpcCrossedRowsDaughter2, tpcCrossedRowsDaughter2, uint16_t); +DECLARE_SOA_COLUMN(ItsNClsHe3, itsNClsHe3, uint8_t); +DECLARE_SOA_COLUMN(ItsNClsDaughter1, itsNClsDaughter1, uint8_t); +DECLARE_SOA_COLUMN(ItsNClsDaughter2, itsNClsDaughter2, uint8_t); + +DECLARE_SOA_COLUMN(McPdgHe3, mcPdgHe3, int); +DECLARE_SOA_COLUMN(McPdgDaughter1, mcPdgDaughter1, int); +DECLARE_SOA_COLUMN(McPdgDaughter2, mcPdgDaughter2, int); +DECLARE_SOA_COLUMN(McMatchStatus, mcMatchStatus, uint8_t); //! 0 missing label, 1 no common mother, 2 common mother, 3 configured decay +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); +DECLARE_SOA_COLUMN(GenMotherPt, genMotherPt, float); +DECLARE_SOA_COLUMN(GenMotherEta, genMotherEta, float); +DECLARE_SOA_COLUMN(GenMotherPhi, genMotherPhi, float); +DECLARE_SOA_COLUMN(GenDecayLength, genDecayLength, float); +} // namespace nuclei3body + +#define NUCLEI_THREE_BODY_RECO_COLUMNS \ + nuclei3body::SignHe3, nuclei3body::SignDaughter1, nuclei3body::SignDaughter2, \ + nuclei3body::Pt, nuclei3body::Eta, nuclei3body::Phi, \ + nuclei3body::PHe3, nuclei3body::PDaughter1, nuclei3body::PDaughter2, \ + nuclei3body::Chi2, nuclei3body::DcaDaughters, nuclei3body::CosPA, \ + nuclei3body::DecayLength, \ + nuclei3body::DcaXYHe3ToPv, nuclei3body::DcaZHe3ToPv, \ + nuclei3body::DcaXYDaughter1ToPv, nuclei3body::DcaZDaughter1ToPv, \ + nuclei3body::DcaXYDaughter2ToPv, nuclei3body::DcaZDaughter2ToPv, \ + nuclei3body::TpcNSigmaHe3, nuclei3body::TofNSigmaHe3, \ + nuclei3body::TpcNSigmaPiDaughter1, nuclei3body::TpcNSigmaKaDaughter1, \ + nuclei3body::TpcNSigmaPrDaughter1, nuclei3body::TofNSigmaPiDaughter1, \ + nuclei3body::TofNSigmaKaDaughter1, nuclei3body::TofNSigmaPrDaughter1, \ + nuclei3body::TpcNSigmaPiDaughter2, nuclei3body::TpcNSigmaKaDaughter2, \ + nuclei3body::TpcNSigmaPrDaughter2, nuclei3body::TofNSigmaPiDaughter2, \ + nuclei3body::TofNSigmaKaDaughter2, nuclei3body::TofNSigmaPrDaughter2, \ + nuclei3body::TpcNClsHe3, nuclei3body::TpcNClsDaughter1, nuclei3body::TpcNClsDaughter2, \ + nuclei3body::TpcCrossedRowsHe3, nuclei3body::TpcCrossedRowsDaughter1, \ + nuclei3body::TpcCrossedRowsDaughter2, nuclei3body::ItsNClsHe3, \ + nuclei3body::ItsNClsDaughter1, nuclei3body::ItsNClsDaughter2 + +DECLARE_SOA_TABLE(NucleiThreeBodyDatas, "AOD", "NUC3BODYDATA", + o2::soa::Index<>, + NUCLEI_THREE_BODY_RECO_COLUMNS); + +DECLARE_SOA_TABLE(McNucleiThreeBodyDatas, "AOD", "MCNUC3BODY", + o2::soa::Index<>, + NUCLEI_THREE_BODY_RECO_COLUMNS, + nuclei3body::McPdgHe3, nuclei3body::McPdgDaughter1, nuclei3body::McPdgDaughter2, + nuclei3body::McMatchStatus, nuclei3body::McMotherPdg, + nuclei3body::GenMotherPt, nuclei3body::GenMotherEta, + nuclei3body::GenMotherPhi, nuclei3body::GenDecayLength); + +#undef NUCLEI_THREE_BODY_RECO_COLUMNS + } // namespace o2::aod #endif // PWGLF_DATAMODEL_VTX3BODYTABLES_H_ diff --git a/PWGLF/DataModel/ZDC2StageCalibrationTables.h b/PWGLF/DataModel/ZDC2StageCalibrationTables.h new file mode 100644 index 00000000000..de4a101dac4 --- /dev/null +++ b/PWGLF/DataModel/ZDC2StageCalibrationTables.h @@ -0,0 +1,54 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author Sourav Kundu + +#ifndef PWGLF_DATAMODEL_ZDC2STAGECALIBRATIONTABLES_H_ +#define PWGLF_DATAMODEL_ZDC2STAGECALIBRATIONTABLES_H_ + +#include + +#include + +namespace o2::aod +{ +namespace zdc2stagecalib +{ +DECLARE_SOA_INDEX_COLUMN(Collision, collision); +DECLARE_SOA_COLUMN(TriggerSP, triggerSP, bool); +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); +DECLARE_SOA_COLUMN(Centrality, centrality, float); +DECLARE_SOA_COLUMN(Vx, vx, float); +DECLARE_SOA_COLUMN(Vy, vy, float); +DECLARE_SOA_COLUMN(Vz, vz, float); +DECLARE_SOA_COLUMN(QxZDCA, qxZDCA, float); +DECLARE_SOA_COLUMN(QxZDCC, qxZDCC, float); +DECLARE_SOA_COLUMN(QyZDCA, qyZDCA, float); +DECLARE_SOA_COLUMN(QyZDCC, qyZDCC, float); +} // namespace zdc2stagecalib + +DECLARE_SOA_TABLE(ZDC2StageCalibs, "AOD", "ZDC2STGCAL", + zdc2stagecalib::CollisionId, + zdc2stagecalib::TriggerSP, + zdc2stagecalib::RunNumber, + zdc2stagecalib::GlobalBC, + zdc2stagecalib::Centrality, + zdc2stagecalib::Vx, + zdc2stagecalib::Vy, + zdc2stagecalib::Vz, + zdc2stagecalib::QxZDCA, + zdc2stagecalib::QxZDCC, + zdc2stagecalib::QyZDCA, + zdc2stagecalib::QyZDCC); +} // namespace o2::aod + +#endif // PWGLF_DATAMODEL_ZDC2STAGECALIBRATIONTABLES_H_ diff --git a/PWGLF/DataModel/ZDCCalTables.h b/PWGLF/DataModel/ZDCCalTables.h index 0e80cdbab25..30851802438 100644 --- a/PWGLF/DataModel/ZDCCalTables.h +++ b/PWGLF/DataModel/ZDCCalTables.h @@ -20,6 +20,7 @@ #include #include +#include namespace o2::aod { @@ -37,6 +38,7 @@ DECLARE_SOA_COLUMN(QyA, qyA, float); DECLARE_SOA_COLUMN(QyC, qyC, float); } // namespace zdccaltable DECLARE_SOA_TABLE(ZDCCalTables, "AOD", "ZDCCALTABLE", + o2::soa::Index<>, zdccaltable::TriggerEventZDC, zdccaltable::TriggerEventRunNo, zdccaltable::Cent, @@ -48,5 +50,82 @@ DECLARE_SOA_TABLE(ZDCCalTables, "AOD", "ZDCCALTABLE", zdccaltable::QyA, zdccaltable::QyC); using ZDCCalTable = ZDCCalTables::iterator; + +// Extra optional linked table. +// This table does NOT duplicate cent, vx, vy, vz, run number, trigger, etc. +// It only stores the ZDC energies and links back to ZDCCalTables. +namespace zdcenergytable +{ +DECLARE_SOA_INDEX_COLUMN(ZDCCalTable, zdcCalTable); + +DECLARE_SOA_COLUMN(ZNACommon, znaCommon, float); +DECLARE_SOA_COLUMN(ZNCCommon, zncCommon, float); + +DECLARE_SOA_COLUMN(ZNA0, zna0, float); +DECLARE_SOA_COLUMN(ZNA1, zna1, float); +DECLARE_SOA_COLUMN(ZNA2, zna2, float); +DECLARE_SOA_COLUMN(ZNA3, zna3, float); + +DECLARE_SOA_COLUMN(ZNC0, znc0, float); +DECLARE_SOA_COLUMN(ZNC1, znc1, float); +DECLARE_SOA_COLUMN(ZNC2, znc2, float); +DECLARE_SOA_COLUMN(ZNC3, znc3, float); +} // namespace zdcenergytable + +DECLARE_SOA_TABLE(ZDCEnergyTables, "AOD", "ZDCENERGY", + zdcenergytable::ZDCCalTableId, + zdcenergytable::ZNACommon, + zdcenergytable::ZNCCommon, + zdcenergytable::ZNA0, + zdcenergytable::ZNA1, + zdcenergytable::ZNA2, + zdcenergytable::ZNA3, + zdcenergytable::ZNC0, + zdcenergytable::ZNC1, + zdcenergytable::ZNC2, + zdcenergytable::ZNC3); + +using ZDCEnergyTable = ZDCEnergyTables::iterator; + +// Extra optional linked table for time information. +// It only stores timestamp and relative time, linked back to ZDCCalTables. +namespace zdctimetable +{ +DECLARE_SOA_INDEX_COLUMN(ZDCCalTable, zdcCalTable); + +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); // bc.timestamp(), in ms +DECLARE_SOA_COLUMN(TimeMin, timeMin, float); // time from first event seen in this run, in minutes +} // namespace zdctimetable + +DECLARE_SOA_TABLE(ZDCTimeTables, "AOD", "ZDCTIME", + zdctimetable::ZDCCalTableId, + zdctimetable::Timestamp, + zdctimetable::TimeMin); + +using ZDCTimeTable = ZDCTimeTables::iterator; + +// charged-track linked table comes here + +namespace zdcchargedtrack +{ +DECLARE_SOA_INDEX_COLUMN(ZDCCalTable, zdcCalTable); + +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); +} // namespace zdcchargedtrack + +DECLARE_SOA_TABLE(ZDCChargedTracks, + "AOD", + "ZDCCHTRK", + zdcchargedtrack::ZDCCalTableId, + zdcchargedtrack::Px, + zdcchargedtrack::Py, + zdcchargedtrack::Pz, + zdcchargedtrack::Sign); + +using ZDCChargedTrack = ZDCChargedTracks::iterator; + } // namespace o2::aod #endif // PWGLF_DATAMODEL_ZDCCALTABLES_H_ diff --git a/PWGLF/DataModel/cascqaanalysis.h b/PWGLF/DataModel/cascqaanalysis.h index 93081d09fca..61783278069 100644 --- a/PWGLF/DataModel/cascqaanalysis.h +++ b/PWGLF/DataModel/cascqaanalysis.h @@ -87,7 +87,7 @@ DECLARE_SOA_COLUMN(BachHasTOF, bachhastof, int); DECLARE_SOA_COLUMN(PosPt, pospt, float); DECLARE_SOA_COLUMN(NegPt, negpt, float); DECLARE_SOA_COLUMN(BachPt, bachpt, float); -DECLARE_SOA_COLUMN(McPdgCode, mcPdgCode, int); //! -1 unknown +DECLARE_SOA_COLUMN(McPdgCode, mcPdgCode, int); //! 0 unknown/no MC association DECLARE_SOA_COLUMN(IsPrimary, isPrimary, int); //! -1 unknown, 0 not primary, 1 primary DECLARE_SOA_COLUMN(BachBaryonCosPA, bachBaryonCosPA, float); //! avoid bach-baryon correlated inv mass structure in analysis DECLARE_SOA_COLUMN(BachBaryonDCAxyToPV, bachBaryonDCAxyToPV, float); //! avoid bach-baryon correlated inv mass structure in analysis @@ -118,6 +118,8 @@ DECLARE_SOA_COLUMN(Pt, pt, float); DECLARE_SOA_COLUMN(Eta, eta, float); DECLARE_SOA_COLUMN(Phi, phi, float); DECLARE_SOA_COLUMN(MassLambda, masslambda, float); +DECLARE_SOA_COLUMN(MassK0Short, massk0short, float); +DECLARE_SOA_COLUMN(CtauLambda, ctaulambda, float); DECLARE_SOA_COLUMN(MassXi, massxi, float); DECLARE_SOA_COLUMN(MassOmega, massomega, float); DECLARE_SOA_COLUMN(V2CEP, v2CEP, float); @@ -182,7 +184,7 @@ DECLARE_SOA_TABLE(CascAnalysisFull, "AOD", "CascAnalysisFull", o2::soa::Index<>, cascadesflow::CosThetaStarLambdaFromOmega, cascadesflow::CosThetaStarLambdaFromXi, cascadesflow::CosThetaStarProton, mycascades::McPdgCode); DECLARE_SOA_TABLE(LambdaAnalysis, "AOD", "LambdaAnalysis", o2::soa::Index<>, - cascadesflow::CentFT0C, cascadesflow::HasEventPlane, cascadesflow::HasSpectatorPlane, cascadesflow::Sign, cascadesflow::Pt, cascadesflow::Phi, cascadesflow::Eta, cascadesflow::MassLambda, cascadesflow::V0Radius, cascadesflow::DcaPosToPV, cascadesflow::DcaNegToPV, cascadesflow::V0CosPA, cascadesflow::DcaV0Daughters, cascadesflow::V2CEP, cascadesflow::PsiT0C, cascadesflow::Pzs2Lambda, cascadesflow::Cos2ThetaLambda, cascadesflow::CosThetaLambda); + cascadesflow::CentFT0C, cascadesflow::CentFT0M, cascadesflow::HasEventPlane, cascadesflow::HasSpectatorPlane, cascadesflow::Sign, cascadesflow::Pt, cascadesflow::Phi, cascadesflow::Eta, cascadesflow::MassLambda, cascadesflow::MassK0Short, cascadesflow::CtauLambda, cascadesflow::V0Radius, cascadesflow::DcaPosToPV, cascadesflow::DcaNegToPV, cascadesflow::V0CosPA, cascadesflow::DcaV0Daughters, cascadesflow::V2CEP, cascadesflow::PsiT0C, cascadesflow::Pzs2Lambda, cascadesflow::Cos2ThetaLambda, cascadesflow::CosThetaLambda); namespace myMCcascades { diff --git a/PWGLF/DataModel/lambdaJetPolarizationIons.h b/PWGLF/DataModel/lambdaJetPolarizationIons.h index 00d3f98d1c5..0a336ecf385 100644 --- a/PWGLF/DataModel/lambdaJetPolarizationIons.h +++ b/PWGLF/DataModel/lambdaJetPolarizationIons.h @@ -24,6 +24,7 @@ #include #include +#include namespace o2::aod { @@ -34,12 +35,16 @@ namespace lambdajetpol DECLARE_SOA_COLUMN(CentFT0M, centFT0M, float); DECLARE_SOA_COLUMN(CentFT0C, centFT0C, float); DECLARE_SOA_COLUMN(CentFV0A, centFV0A, float); +DECLARE_SOA_COLUMN(Zvtx, zvtx, float); +DECLARE_SOA_COLUMN(InteractionRate, interactionRate, float); +// DECLARE_SOA_COLUMN(MagField, magField, float); // Jet (and jet proxies) information: DECLARE_SOA_COLUMN(JetPt, jetPt, float); DECLARE_SOA_COLUMN(JetEta, jetEta, float); DECLARE_SOA_COLUMN(JetPhi, jetPhi, float); -DECLARE_SOA_COLUMN(JetNConstituents, jetNConstituents, int); +// DECLARE_SOA_COLUMN(JetNConstituents, jetNConstituents, uint16_t); // Currently removed from datamodel. +// Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies DECLARE_SOA_COLUMN(LeadParticlePt, leadParticlePt, float); DECLARE_SOA_COLUMN(LeadParticleEta, leadParticleEta, float); @@ -50,22 +55,27 @@ DECLARE_SOA_COLUMN(V0Pt, v0Pt, float); DECLARE_SOA_COLUMN(V0Eta, v0Eta, float); DECLARE_SOA_COLUMN(V0Phi, v0Phi, float); -DECLARE_SOA_COLUMN(IsLambda, isLambda, bool); -DECLARE_SOA_COLUMN(IsAntiLambda, isAntiLambda, bool); -DECLARE_SOA_COLUMN(MassLambda, massLambda, float); -DECLARE_SOA_COLUMN(MassAntiLambda, massAntiLambda, float); +DECLARE_SOA_COLUMN(IsLambda, isLambda, bool); //! 0: antiLambda, 1: Lambda. There are no ambiguous candidates stored (those that pass both Lambda-specific and AntiLambda-specific checks) +// DECLARE_SOA_COLUMN(IsAntiLambda, isAntiLambda, bool); +DECLARE_SOA_COLUMN(MassV0, massV0, float); +// DECLARE_SOA_COLUMN(MassLambda, massLambda, float); +// DECLARE_SOA_COLUMN(MassAntiLambda, massAntiLambda, float); -DECLARE_SOA_COLUMN(PosPt, posPt, float); +DECLARE_SOA_COLUMN(PosPt, posPt, float); // Could consider rewriting this as proton/pion-like Pt, as in TPCNSigma, instead of Pos/Neg DECLARE_SOA_COLUMN(PosEta, posEta, float); DECLARE_SOA_COLUMN(PosPhi, posPhi, float); DECLARE_SOA_COLUMN(NegPt, negPt, float); DECLARE_SOA_COLUMN(NegEta, negEta, float); DECLARE_SOA_COLUMN(NegPhi, negPhi, float); -DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); -DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); -DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); -DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); +// DECLARE_SOA_COLUMN(PosTPCNSigmaPr, posTPCNSigmaPr, float); +// DECLARE_SOA_COLUMN(PosTPCNSigmaPi, posTPCNSigmaPi, float); +// DECLARE_SOA_COLUMN(NegTPCNSigmaPr, negTPCNSigmaPr, float); +// DECLARE_SOA_COLUMN(NegTPCNSigmaPi, negTPCNSigmaPi, float); +// TPC Nsigma variables now stored only for non-ambiguous candidates (those that pass only the Lambda-specific or antiLambda-specific tests): +// (PrLike: proton-like daughter of the V0, either a proton (Lambda) or an antiproton (AntiLambda). PiLike is the pion daughter) +DECLARE_SOA_COLUMN(RoundPrLikeTPCNSigma, roundPrLikeTPCNSigma, int16_t); //! Stores nsigma with a precision of 0.01 nsigma. Following LFStrangenessPIDTables.h +DECLARE_SOA_COLUMN(RoundPiLikeTPCNSigma, roundPiLikeTPCNSigma, int16_t); DECLARE_SOA_COLUMN(V0CosPA, v0CosPA, float); DECLARE_SOA_COLUMN(V0Radius, v0Radius, float); @@ -74,38 +84,46 @@ DECLARE_SOA_COLUMN(DcaPosToPV, dcaPosToPV, float); DECLARE_SOA_COLUMN(DcaNegToPV, dcaNegToPV, float); // Dynamic columns for jets (Px,Py,Pz): -DECLARE_SOA_DYNAMIC_COLUMN(JetPx, jetPx, //! Jet px +DECLARE_SOA_DYNAMIC_COLUMN(JetPx, jetPx, [](float jetPt, float jetPhi) -> float { return jetPt * std::cos(jetPhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(JetPy, jetPy, //! Jet py +DECLARE_SOA_DYNAMIC_COLUMN(JetPy, jetPy, [](float jetPt, float jetPhi) -> float { return jetPt * std::sin(jetPhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(JetPz, jetPz, //! Jet pz +DECLARE_SOA_DYNAMIC_COLUMN(JetPz, jetPz, [](float jetPt, float jetEta) -> float { return jetPt * std::sinh(jetEta); }); // Same for leading particles: -DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePx, leadParticlePx, //! Leading particle px +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePx, leadParticlePx, [](float leadParticlePt, float leadParticlePhi) -> float { return leadParticlePt * std::cos(leadParticlePhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePy, leadParticlePy, //! Leading particle py +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePy, leadParticlePy, [](float leadParticlePt, float leadParticlePhi) -> float { return leadParticlePt * std::sin(leadParticlePhi); }); -DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePz, leadParticlePz, //! Leading particle pz +DECLARE_SOA_DYNAMIC_COLUMN(LeadParticlePz, leadParticlePz, [](float leadParticlePt, float leadParticleEta) -> float { return leadParticlePt * std::sinh(leadParticleEta); }); + +// Dynamic columns for retrieving rounded Nsigma columns: +DECLARE_SOA_DYNAMIC_COLUMN(PrLikeTPCNSigma, prLikeTPCNSigma, + [](int16_t packedValue) -> float { return packedValue / 100.f; }); +DECLARE_SOA_DYNAMIC_COLUMN(PiLikeTPCNSigma, piLikeTPCNSigma, + [](int16_t packedValue) -> float { return packedValue / 100.f; }); } // namespace lambdajetpol DECLARE_SOA_TABLE(RingCollisions, "AOD", "RINGCOLLISION", - o2::soa::Index<>, // self-index: auto-assigned row number + o2::soa::Index<>, //! self-index: auto-assigned row number lambdajetpol::CentFT0M, lambdajetpol::CentFT0C, - lambdajetpol::CentFV0A); + lambdajetpol::CentFV0A, + lambdajetpol::Zvtx, + lambdajetpol::InteractionRate); namespace lambdajetpol { -DECLARE_SOA_INDEX_COLUMN(RingCollision, ringCollision); // Declare index after table is available +DECLARE_SOA_INDEX_COLUMN(RingCollision, ringCollision); //! Declare index after table is available } // namespace lambdajetpol DECLARE_SOA_TABLE(RingJets, "AOD", "RINGJET", - lambdajetpol::RingCollisionId, // relational index -> RingCollisions + lambdajetpol::RingCollisionId, //! relational index -> RingCollisions lambdajetpol::JetPt, lambdajetpol::JetEta, lambdajetpol::JetPhi, - lambdajetpol::JetNConstituents, + // lambdajetpol::JetNConstituents, // Dynamic columns (explicitly bound to their static inputs): lambdajetpol::JetPx, lambdajetpol::JetPy, @@ -127,24 +145,23 @@ DECLARE_SOA_TABLE(RingLaV0s, "AOD", "RINGLAV0", lambdajetpol::V0Eta, lambdajetpol::V0Phi, lambdajetpol::IsLambda, - lambdajetpol::IsAntiLambda, - lambdajetpol::MassLambda, - lambdajetpol::MassAntiLambda, + lambdajetpol::MassV0, lambdajetpol::PosPt, lambdajetpol::PosEta, lambdajetpol::PosPhi, lambdajetpol::NegPt, lambdajetpol::NegEta, lambdajetpol::NegPhi, - lambdajetpol::PosTPCNSigmaPr, - lambdajetpol::PosTPCNSigmaPi, - lambdajetpol::NegTPCNSigmaPr, - lambdajetpol::NegTPCNSigmaPi, + lambdajetpol::RoundPrLikeTPCNSigma, + lambdajetpol::RoundPiLikeTPCNSigma, lambdajetpol::V0CosPA, lambdajetpol::V0Radius, lambdajetpol::DcaV0Daughters, lambdajetpol::DcaPosToPV, - lambdajetpol::DcaNegToPV); + lambdajetpol::DcaNegToPV, + // Dynamic columns: + lambdajetpol::PrLikeTPCNSigma, + lambdajetpol::PiLikeTPCNSigma); using RingCollision = RingCollisions::iterator; // Useful shorthand } // namespace o2::aod diff --git a/PWGLF/DataModel/mcCentrality.h b/PWGLF/DataModel/mcCentrality.h index 94544949017..2bb14880953 100644 --- a/PWGLF/DataModel/mcCentrality.h +++ b/PWGLF/DataModel/mcCentrality.h @@ -20,6 +20,7 @@ #ifndef PWGLF_DATAMODEL_MCCENTRALITY_H_ #define PWGLF_DATAMODEL_MCCENTRALITY_H_ +// O2 includes #include "Common/DataModel/Centrality.h" #include @@ -38,8 +39,12 @@ DECLARE_SOA_TABLE(McCentFV0As, "AOD", "MCCENTFV0A", o2::soa::Marker<1>, cent::Ce DECLARE_SOA_TABLE(McCentFT0Ms, "AOD", "MCCENTFT0M", o2::soa::Marker<2>, cent::CentFT0M); DECLARE_SOA_TABLE(McCentFT0As, "AOD", "MCCENTFT0A", o2::soa::Marker<3>, cent::CentFT0A); DECLARE_SOA_TABLE(McCentFT0Cs, "AOD", "MCCENTFT0C", o2::soa::Marker<4>, cent::CentFT0C); -DECLARE_SOA_TABLE(McCentFDDMs, "AOD", "MCCENTFDDM", o2::soa::Marker<5>, cent::CentFDDM); -DECLARE_SOA_TABLE(McCentNTPVs, "AOD", "MCCENTNTPV", o2::soa::Marker<6>, cent::CentNTPV); +DECLARE_SOA_TABLE(McCentFT0CVariant1s, "AOD", "MCCENTFT0CVAR1", o2::soa::Marker<5>, cent::CentFT0CVariant1); +DECLARE_SOA_TABLE(McCentFT0CVariant2s, "AOD", "MCCENTFT0CVAR2", o2::soa::Marker<6>, cent::CentFT0CVariant2); +DECLARE_SOA_TABLE(McCentFDDMs, "AOD", "MCCENTFDDM", o2::soa::Marker<7>, cent::CentFDDM); +DECLARE_SOA_TABLE(McCentNTPVs, "AOD", "MCCENTNTPV", o2::soa::Marker<8>, cent::CentNTPV); +DECLARE_SOA_TABLE(McCentNGlobals, "AOD", "MCCENTNGLOBAL", o2::soa::Marker<9>, cent::CentNGlobal); +DECLARE_SOA_TABLE(McCentMFTs, "AOD", "MCCENTMFT", o2::soa::Marker<10>, cent::CentMFT); } // namespace o2::aod diff --git a/PWGLF/TableProducer/CMakeLists.txt b/PWGLF/TableProducer/CMakeLists.txt index ec21b553563..745d984c81d 100644 --- a/PWGLF/TableProducer/CMakeLists.txt +++ b/PWGLF/TableProducer/CMakeLists.txt @@ -12,6 +12,7 @@ add_subdirectory(QC) add_subdirectory(Common) +add_subdirectory(GlobalEventProperties) add_subdirectory(Nuspex) add_subdirectory(Strangeness) add_subdirectory(Resonances) diff --git a/PWGLF/TableProducer/Common/CMakeLists.txt b/PWGLF/TableProducer/Common/CMakeLists.txt index 19a1105dc3a..34a8b238d50 100644 --- a/PWGLF/TableProducer/Common/CMakeLists.txt +++ b/PWGLF/TableProducer/Common/CMakeLists.txt @@ -44,3 +44,8 @@ o2physics_add_dpl_workflow(kink-builder SOURCES kinkBuilder.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(zdc2stagecalibration + SOURCES zdc2stagecalibration.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsVertexing + COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Common/epvector.cxx b/PWGLF/TableProducer/Common/epvector.cxx index a001de0407f..99e401b67d8 100644 --- a/PWGLF/TableProducer/Common/epvector.cxx +++ b/PWGLF/TableProducer/Common/epvector.cxx @@ -210,7 +210,7 @@ struct epvector { } template - bool eventSelected(TCollision collision, const float& centrality) + bool eventSelected(const TCollision& collision, const float& centrality) { if (collision.alias_bit(kTVXinTRD)) { // TRD triggered // return 0; diff --git a/PWGLF/TableProducer/Common/kinkBuilder.cxx b/PWGLF/TableProducer/Common/kinkBuilder.cxx index cd2f4c89cf1..623efeeeb2f 100644 --- a/PWGLF/TableProducer/Common/kinkBuilder.cxx +++ b/PWGLF/TableProducer/Common/kinkBuilder.cxx @@ -16,7 +16,9 @@ #include "PWGLF/DataModel/LFKinkDecayTables.h" #include "PWGLF/Utils/svPoolCreator.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" #include #include @@ -40,21 +42,34 @@ #include #include +#include #include #include #include #include +#include #include #include #include using namespace o2; +using namespace o2::aod; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::constants::physics; using std::array; using VBracket = o2::math_utils::Bracket; using TracksFull = soa::Join; +using TracksFullMc = soa::Join; +using CollisionsCent = soa::Join; +using McRecoCollisions = soa::Join; +using McRecoCollisionsCent = soa::Join; + +enum KinkDecayType { SigmaMinusToPiMinusNeutron = 0, + SigmaPlusToPiPlusNeutron, + SigmaPlusToProtonPi0, + NMatchedDecays }; namespace { @@ -62,12 +77,32 @@ constexpr std::array LayerRadii{2.33959f, 3.14076f, 3.91924f, 19.6213f constexpr double betheBlochDefault[1][6]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; static const std::vector particleNames{"Daughter"}; +constexpr int DepthMcMatchMax = 1; // Max depth for MC matching std::shared_ptr h2ClsMapPtMoth; std::shared_ptr h2ClsMapPtDaug; std::shared_ptr h2DeDxDaugSel; std::shared_ptr h2KinkAnglePt; std::shared_ptr h2MothMassPt; +std::shared_ptr hSelMotherQA; +std::shared_ptr hSelDaugQA; +std::shared_ptr hSelKinkedTrackQA; +std::shared_ptr hMothDaughSignsInit; +std::shared_ptr hMothDaughSignsFinal; +std::shared_ptr h2ItsClsMothBeforeSel; +std::shared_ptr h2ItsClsDaugBeforeSel; +std::shared_ptr hZDiff; +std::shared_ptr hPhiDiff; +std::shared_ptr hDCAMothToPV; +std::shared_ptr hDCADaugToPV; +std::shared_ptr hMothDecRad2; +std::shared_ptr hGenCandidates; +std::shared_ptr hGenDecayRadius; +std::shared_ptr hRecCandidatesRecoPt; +std::shared_ptr hRecCandidatesGenPt; +std::shared_ptr hRecCandidatesDeltaPt; +std::array, NMatchedDecays> hGenPtKinkAngle; +std::array, NMatchedDecays> hRecPtKinkAngle; } // namespace struct kinkCandidate { @@ -126,6 +161,11 @@ struct kinkBuilder { o2::vertexing::DCAFitterN<2> fitter; o2::base::MatLayerCylSet* lut = nullptr; + int nItsInnerBarrelLayers = 3; + int nItsOuterBarrelLayers = 4; + int nItsTotalLayers = 7; + int nCoords = 7; + int itsChi2NClMax = 36; // constants float radToDeg = o2::constants::math::Rad2Deg; @@ -137,18 +177,32 @@ struct kinkBuilder { Configurable customVertexerTimeMargin{"customVertexerTimeMargin", 800, "Time margin for custom vertexer (ns)"}; Configurable skipAmbiTracks{"skipAmbiTracks", false, "Skip ambiguous tracks"}; Configurable unlikeSignBkg{"unlikeSignBkg", false, "Use unlike sign background"}; + Configurable skipBkgCands{"skipBkgCands", true, "Skip bkg candidates in MC process"}; + + // Centrality selection + Configurable centralityMin{"centralityMin", 0., "Minimum centrality accepted in SP/EP computation (not applied in resolution process)"}; + Configurable centralityMax{"centralityMax", 100., "Maximum centrality accepted in SP/EP computation (not applied in resolution process)"}; // CCDB options Configurable ccdbPath{"ccdbPath", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - // PDG codes - // histogram axes ConfigurableAxis rigidityBins{"rigidityBins", {200, -10.f, 10.f}, "Binning for rigidity #it{p}^{TPC}/#it{z}"}; ConfigurableAxis dedxBins{"dedxBins", {1000, 0.f, 1000.f}, "Binning for dE/dx"}; ConfigurableAxis nSigmaBins{"nSigmaBins", {200, -5.f, 5.f}, "Binning for n sigma"}; + ConfigurableAxis zDiffBins{"zDiffBins", {300, 0.f, 30.f}, "Binning for z difference"}; + ConfigurableAxis phiDiffBins{"phiDiffBins", {360, 0.f, 360.f}, "Binning for phi difference"}; + ConfigurableAxis dcaMothToPVBins{"dcaMothToPVBins", {120, 0.f, 60.f}, "Binning for DCA of mother to PV"}; + ConfigurableAxis dcaDaugToPVBins{"dcaDaugToPVBins", {200, 0.f, 20.f}, "Binning for DCA of daughter to PV"}; + ConfigurableAxis mothDecRad2Bins{"mothDecRad2Bins", {100, 0, 100}, "Binning for mother decay radius squared"}; + + // Filter collisions with centrality information (for the process with centrality selection) + using CollisionsCentSel = soa::Filtered; + using McRecoCollisionsCentSel = soa::Filtered; + + Filter filterCentrality = aod::cent::centFT0C >= centralityMin && aod::cent::centFT0C <= centralityMax; // std vector of candidates std::vector kinkCandidates; @@ -161,6 +215,7 @@ struct kinkBuilder { // mother and daughter tracks' properties (absolute charge and mass) int charge = 1; + std::vector mothMatchPdgCodes{}; float mothMass = o2::constants::physics::MassSigmaMinus; float chargedDauMass = o2::constants::physics::MassPiMinus; float neutDauMass = o2::constants::physics::MassNeutron; @@ -169,6 +224,7 @@ struct kinkBuilder { { if (hypoMoth == kSigmaMinus) { charge = 1; + mothMatchPdgCodes = {PDG_t::kSigmaMinus, PDG_t::kSigmaPlus}; mothMass = o2::constants::physics::MassSigmaMinus; chargedDauMass = o2::constants::physics::MassPiMinus; neutDauMass = o2::constants::physics::MassNeutron; @@ -209,6 +265,7 @@ struct kinkBuilder { const AxisSpec itsClusterMapAxis(128, 0, 127, "ITS cluster map"); const AxisSpec rigidityAxis{rigidityBins, "#it{p}^{TPC}/#it{z}"}; const AxisSpec ptAxis{rigidityBins, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec absPtAxis{1000, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec kinkAngleAxis{100, 0, 180, "#theta_{kink} (deg)"}; const AxisSpec dedxAxis{dedxBins, "d#it{E}/d#it{x}"}; @@ -221,213 +278,368 @@ struct kinkBuilder { massAxis = AxisSpec{100, 3.85, 4.25, "m (GeV/#it{c}^{2})"}; } - h2DeDxDaugSel = qaRegistry.add("h2DeDxDaugSel", ";p_{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {rigidityAxis, dedxAxis}); - h2KinkAnglePt = qaRegistry.add("h2KinkAnglePt", "; p_{T} (GeV/#it{c}); #theta_{kink} (deg)", HistType::kTH2F, {ptAxis, kinkAngleAxis}); - h2MothMassPt = qaRegistry.add("h2MothMassPt", "; p_{T} (GeV/#it{c}); m (GeV/#it{c}^{2})", HistType::kTH2F, {ptAxis, massAxis}); - h2ClsMapPtMoth = qaRegistry.add("h2ClsMapPtMoth", "; p_{T} (GeV/#it{c}); ITS cluster map", HistType::kTH2F, {ptAxis, itsClusterMapAxis}); - h2ClsMapPtDaug = qaRegistry.add("h2ClsMapPtDaug", "; p_{T} (GeV/#it{c}); ITS cluster map", HistType::kTH2F, {ptAxis, itsClusterMapAxis}); + h2DeDxDaugSel = qaRegistry.add("h2DeDxDaugSel", "h2DeDxDaugSel; p_{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {rigidityAxis, dedxAxis}); + h2KinkAnglePt = qaRegistry.add("h2KinkAnglePt", "h2KinkAnglePt; p_{T} (GeV/#it{c}); #theta_{kink} (deg)", HistType::kTH2F, {ptAxis, kinkAngleAxis}); + h2MothMassPt = qaRegistry.add("h2MothMassPt", "h2MothMassPt; p_{T} (GeV/#it{c}); m (GeV/#it{c}^{2})", HistType::kTH2F, {ptAxis, massAxis}); + h2ClsMapPtMoth = qaRegistry.add("h2ClsMapPtMoth", "h2ClsMapPtMoth; p_{T} (GeV/#it{c}); ITS cluster map", HistType::kTH2F, {ptAxis, itsClusterMapAxis}); + h2ClsMapPtDaug = qaRegistry.add("h2ClsMapPtDaug", "h2ClsMapPtDaug; p_{T} (GeV/#it{c}); ITS cluster map", HistType::kTH2F, {ptAxis, itsClusterMapAxis}); + h2ItsClsMothBeforeSel = qaRegistry.add("h2ItsClsMothBeforeSel", "h2ItsClsMothBeforeSel; Tot Cls; IB clusters", HistType::kTH2F, {{8, -0.5, 7.5}, {4, -0.5, 3.5}}); + h2ItsClsDaugBeforeSel = qaRegistry.add("h2ItsClsDaugBeforeSel", "h2ItsClsDaugBeforeSel; Tot Cls; IB clusters", HistType::kTH2F, {{8, -0.5, 7.5}, {4, -0.5, 3.5}}); + + // QA + hSelMotherQA = qaRegistry.add("hSelMotherQA", "hSelMotherQA;;Q_{Mother}", {HistType::kTH2F, {{9, -0.5, 8.5}, {2, -0.5, 1.5}}}); + hSelMotherQA->GetYaxis()->SetBinLabel(1, "-"); + hSelMotherQA->GetYaxis()->SetBinLabel(2, "+"); + hSelMotherQA->GetXaxis()->SetBinLabel(1, "All"); + hSelMotherQA->GetXaxis()->SetBinLabel(2, "Has Collision"); + hSelMotherQA->GetXaxis()->SetBinLabel(3, "Has ITS"); + hSelMotherQA->GetXaxis()->SetBinLabel(4, "Has NO TPC"); + hSelMotherQA->GetXaxis()->SetBinLabel(5, "Has NO TOF"); + hSelMotherQA->GetXaxis()->SetBinLabel(6, "ITS cls sel"); + hSelMotherQA->GetXaxis()->SetBinLabel(7, "ITS IB cls sel"); + hSelMotherQA->GetXaxis()->SetBinLabel(8, "ITS chi2 N cls sel"); + hSelMotherQA->GetXaxis()->SetBinLabel(9, "Pt sel"); + + // QA + hSelDaugQA = qaRegistry.add("hSelDaugQA", "hSelDaugQA;;Q_{Daug}", {HistType::kTH2F, {{8, -0.5, 7.5}, {2, -0.5, 1.5}}}); + hSelDaugQA->GetYaxis()->SetBinLabel(1, "-"); + hSelDaugQA->GetYaxis()->SetBinLabel(2, "+"); + hSelDaugQA->GetXaxis()->SetBinLabel(1, "All"); + hSelDaugQA->GetXaxis()->SetBinLabel(2, "Has ITS"); + hSelDaugQA->GetXaxis()->SetBinLabel(3, "Has TPC"); + hSelDaugQA->GetXaxis()->SetBinLabel(4, "Has TOF"); + hSelDaugQA->GetXaxis()->SetBinLabel(5, "ITS tot cls sel"); + hSelDaugQA->GetXaxis()->SetBinLabel(6, "ITS IB cls sel"); + hSelDaugQA->GetXaxis()->SetBinLabel(7, "TPC Crossed rows vs findable sel"); + hSelDaugQA->GetXaxis()->SetBinLabel(8, "TPC cls sel"); + + hSelKinkedTrackQA = qaRegistry.add("hSelKinkedTrackQA", "hSelKinkedTrackQA;;(Q_{Mother}, Q_{Daughter})", {HistType::kTH2F, {{11, -0.5, 10.5}, {4, -0.5, 3.5}}}); + hSelKinkedTrackQA->GetYaxis()->SetBinLabel(1, "(+,+)"); + hSelKinkedTrackQA->GetYaxis()->SetBinLabel(2, "(-,-)"); + hSelKinkedTrackQA->GetYaxis()->SetBinLabel(3, "(+,-)"); + hSelKinkedTrackQA->GetYaxis()->SetBinLabel(4, "(-,+)"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(1, "All"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(2, "MaxDCAMothToPV"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(3, "MaxZDiff"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(4, "MaxPhiDiff"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(5, "MinDCADaugToPV"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(6, "UpdateMothTrackUsePV"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(7, "DCAFitter"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(8, "PropagateTracksToVertex"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(9, "MothDecRad2InsideL4"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(10, "MothDaugLastLayers"); + hSelKinkedTrackQA->GetXaxis()->SetBinLabel(11, "MothLastLayerRadiusCheck"); + hMothDaughSignsInit = qaRegistry.add("hMothDaughSignsInit", "hMothDaughSignsInit; Sign Mother; Sign Daughter", {HistType::kTH2F, {{3, -1.5, 1.5}, {3, -1.5, 1.5}}}); + hMothDaughSignsFinal = qaRegistry.add("hMothDaughSignsFinal", "hMothDaughSignsFinal; Sign Mother; Sign Daughter", {HistType::kTH2F, {{3, -1.5, 1.5}, {3, -1.5, 1.5}}}); + hZDiff = qaRegistry.add("hZDiff", "hZDiff; #Delta z (#mu m);(Q_{Mother}, Q_{Daughter})", HistType::kTH2F, {zDiffBins, {4, -0.5, 3.5}}); + hZDiff->GetYaxis()->SetBinLabel(1, "(+,+)"); + hZDiff->GetYaxis()->SetBinLabel(2, "(-,-)"); + hZDiff->GetYaxis()->SetBinLabel(3, "(+,-)"); + hZDiff->GetYaxis()->SetBinLabel(4, "(-,+)"); + hPhiDiff = qaRegistry.add("hPhiDiff", "hPhiDiff; #Delta #phi (rad);(Q_{Mother}, Q_{Daughter})", HistType::kTH2F, {phiDiffBins, {4, -0.5, 3.5}}); + hPhiDiff->GetYaxis()->SetBinLabel(1, "(+,+)"); + hPhiDiff->GetYaxis()->SetBinLabel(2, "(-,-)"); + hPhiDiff->GetYaxis()->SetBinLabel(3, "(+,-)"); + hPhiDiff->GetYaxis()->SetBinLabel(4, "(-,+)"); + hDCAMothToPV = qaRegistry.add("hDCAMothToPV", "hDCAMothToPV; DCA moth to PV;(Q_{Mother}, Q_{Daughter})", HistType::kTH2F, {dcaMothToPVBins, {4, -0.5, 3.5}}); + hDCAMothToPV->GetYaxis()->SetBinLabel(1, "(+,+)"); + hDCAMothToPV->GetYaxis()->SetBinLabel(2, "(-,-)"); + hDCAMothToPV->GetYaxis()->SetBinLabel(3, "(+,-)"); + hDCAMothToPV->GetYaxis()->SetBinLabel(4, "(-,+)"); + hDCADaugToPV = qaRegistry.add("hDCADaugToPV", "hDCADaugToPV; DCA daug to PV;(Q_{Mother}, Q_{Daughter})", HistType::kTH2F, {dcaDaugToPVBins, {4, -0.5, 3.5}}); + hDCADaugToPV->GetYaxis()->SetBinLabel(1, "(+,+)"); + hDCADaugToPV->GetYaxis()->SetBinLabel(2, "(-,-)"); + hDCADaugToPV->GetYaxis()->SetBinLabel(3, "(+,-)"); + hDCADaugToPV->GetYaxis()->SetBinLabel(4, "(-,+)"); + hMothDecRad2 = qaRegistry.add("hMothDecRad2", "hMothDecRad2; Mother Dec. Radius Squared (cm^{2});(Q_{Mother}, Q_{Daughter})", HistType::kTH2F, {mothDecRad2Bins, {4, -0.5, 3.5}}); + hMothDecRad2->GetYaxis()->SetBinLabel(1, "(+,+)"); + hMothDecRad2->GetYaxis()->SetBinLabel(2, "(-,-)"); + hMothDecRad2->GetYaxis()->SetBinLabel(3, "(+,-)"); + hMothDecRad2->GetYaxis()->SetBinLabel(4, "(-,+)"); + + mBBparamsDaug[0] = cfgBetheBlochParams->get("Daughter", Form("p%i", 0)); + mBBparamsDaug[1] = cfgBetheBlochParams->get("Daughter", Form("p%i", 1)); + mBBparamsDaug[2] = cfgBetheBlochParams->get("Daughter", Form("p%i", 2)); + mBBparamsDaug[3] = cfgBetheBlochParams->get("Daughter", Form("p%i", 3)); + mBBparamsDaug[4] = cfgBetheBlochParams->get("Daughter", Form("p%i", 4)); + mBBparamsDaug[5] = cfgBetheBlochParams->get("Daughter", "resolution"); - for (int i = 0; i < 5; i++) { - mBBparamsDaug[i] = cfgBetheBlochParams->get("Daughter", Form("p%i", i)); + if (doprocessMc || doprocessMcWCent) { + if (skipBkgCands) { + hRecCandidatesRecoPt = qaRegistry.add("hRecCandidatesRecoPt", "hRecCandidates;Counts;Reco #it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{NMatchedDecays, -0.5, static_cast(NMatchedDecays) - 0.5}, absPtAxis}}); + hRecCandidatesRecoPt->GetXaxis()->SetBinLabel(1, "#Sigma^{-} #rightarrow n#pi^{-}"); + hRecCandidatesRecoPt->GetXaxis()->SetBinLabel(2, "#Sigma^{+} #rightarrow n#pi^{+}"); + hRecCandidatesRecoPt->GetXaxis()->SetBinLabel(3, "#Sigma^{+} #rightarrow p#pi^{0}"); + hRecCandidatesGenPt = qaRegistry.add("hRecCandidatesGenPt", "hRecCandidatesGenPt;Counts;Gen #it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{NMatchedDecays, -0.5, static_cast(NMatchedDecays) - 0.5}, absPtAxis}}); + hRecCandidatesGenPt->GetXaxis()->SetBinLabel(1, "#Sigma^{-} #rightarrow n#pi^{-}"); + hRecCandidatesGenPt->GetXaxis()->SetBinLabel(2, "#Sigma^{+} #rightarrow n#pi^{+}"); + hRecCandidatesGenPt->GetXaxis()->SetBinLabel(3, "#Sigma^{+} #rightarrow p#pi^{0}"); + hRecCandidatesDeltaPt = qaRegistry.add("hRecCandidatesDeltaPt", "hRecCandidatesDeltaPt;Counts;#Delta p_{T} (GeV/c)", {HistType::kTH2F, {{400, -0.5, 0.5}, absPtAxis}}); + } + hGenDecayRadius = qaRegistry.add("hGenDecayRadius", "hGenDecayRadius;Radius (cm); #it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{300, 0, 30}, absPtAxis}}); + hGenCandidates = qaRegistry.add("hGenCandidates", "hGenCandidates;Counts;Gen #it{p}_{T} (GeV/#it{c})", {HistType::kTH2F, {{NMatchedDecays, -0.5, static_cast(NMatchedDecays) - 0.5}, absPtAxis}}); + hGenCandidates->GetXaxis()->SetBinLabel(1, "#Sigma^{-} #rightarrow n#pi^{-}"); + hGenCandidates->GetXaxis()->SetBinLabel(2, "#Sigma^{+} #rightarrow n#pi^{+}"); + hGenCandidates->GetXaxis()->SetBinLabel(3, "#Sigma^{+} #rightarrow p#pi^{0}"); + hRecPtKinkAngle[SigmaMinusToPiMinusNeutron] = qaRegistry.add("hRecPtKinkAngleSigmaMinusToPi", "Rec Pt vs KinkAngle #Sigma^{-} #rightarrow #pi^{-}n;Counts;", {HistType::kTH2F, {absPtAxis, kinkAngleAxis}}); + hRecPtKinkAngle[SigmaPlusToPiPlusNeutron] = qaRegistry.add("hRecPtKinkAngleSigmaPlusToPi", "Rec Pt vs KinkAngle #Sigma^{+} #rightarrow #pi^{+}n;Counts;", {HistType::kTH2F, {absPtAxis, kinkAngleAxis}}); + hRecPtKinkAngle[SigmaPlusToProtonPi0] = qaRegistry.add("hRecPtKinkAngleSigmaPlusToPr", "Rec Pt vs KinkAngle #Sigma^{+} #rightarrow p#pi^{0};Counts;", {HistType::kTH2F, {absPtAxis, kinkAngleAxis}}); + hGenPtKinkAngle[SigmaMinusToPiMinusNeutron] = qaRegistry.add("hGenPtKinkAngleSigmaMinusToPi", "Gen Pt vs KinkAngle #Sigma^{-} #rightarrow #pi^{-}n;Counts;", {HistType::kTH2F, {absPtAxis, kinkAngleAxis}}); + hGenPtKinkAngle[SigmaPlusToPiPlusNeutron] = qaRegistry.add("hGenPtKinkAngleSigmaPlusToPi", "Gen Pt vs KinkAngle #Sigma^{+} #rightarrow #pi^{+}n;Counts;", {HistType::kTH2F, {absPtAxis, kinkAngleAxis}}); + hGenPtKinkAngle[SigmaPlusToProtonPi0] = qaRegistry.add("hGenPtKinkAngleSigmaPlusToPr", "Gen Pt vs KinkAngle #Sigma^{+} #rightarrow p#pi^{0};Counts;", {HistType::kTH2F, {absPtAxis, kinkAngleAxis}}); } - mBBparamsDaug[5] = cfgBetheBlochParams->get("Daughter", "resolution"); } template bool selectMothTrack(const T& candidate) { - if (candidate.has_collision() && candidate.hasITS() && !candidate.hasTPC() && !candidate.hasTOF() && candidate.itsNCls() < 6 && - candidate.itsNClsInnerBarrel() == 3 && candidate.itsChi2NCl() < 36 && candidate.pt() > minPtMoth) { - return true; - } - return false; - } + bool isPositive = candidate.sign() > 0; + hSelMotherQA->Fill(0.f, isPositive); - template - bool selectDaugTrack(const T& candidate) - { - if (!candidate.hasTPC() || !candidate.hasITS()) { + if (!candidate.has_collision()) return false; - } + hSelMotherQA->Fill(1.f, isPositive); - if (askTOFforDaug && !candidate.hasTOF()) { + if (!candidate.hasITS()) return false; - } + hSelMotherQA->Fill(2.f, isPositive); - bool isGoodTPCCand = false; - if (candidate.itsNClsInnerBarrel() == 0 && candidate.itsNCls() < 4 && - candidate.tpcNClsCrossedRows() > 0.8 * candidate.tpcNClsFindable() && candidate.tpcNClsFound() > nTPCClusMinDaug) { - isGoodTPCCand = true; - } + if (candidate.hasTPC()) + return false; + hSelMotherQA->Fill(3.f, isPositive); - if (!isGoodTPCCand) { + if (candidate.hasTOF()) return false; - } + hSelMotherQA->Fill(4.f, isPositive); + + int itsTotCls = static_cast(candidate.itsNCls()); + int itsIBCls = static_cast(candidate.itsNClsInnerBarrel()); + h2ItsClsMothBeforeSel->Fill(itsTotCls, itsIBCls); + if (itsTotCls >= nItsTotalLayers - 1) + return false; + hSelMotherQA->Fill(5.f, isPositive); + + if (itsIBCls != nItsInnerBarrelLayers) + return false; + hSelMotherQA->Fill(6.f, isPositive); + + if (candidate.itsChi2NCl() >= itsChi2NClMax) + return false; + hSelMotherQA->Fill(7.f, isPositive); + + if (candidate.pt() <= minPtMoth) + return false; + hSelMotherQA->Fill(8.f, isPositive); return true; } - template - void fillCandidateData(const Tcolls& collisions, const Ttracks& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCs const& bcs) + template + bool selectDaugTrack(const T& candidate) { - svCreator.clearPools(); - svCreator.fillBC2Coll(collisions, bcs); + bool isPositive = candidate.sign() > 0; + hSelDaugQA->Fill(0.f, isPositive); - for (const auto& track : tracks) { - if (std::abs(track.eta()) > etaMax) - continue; + if (!candidate.hasITS()) + return false; + hSelDaugQA->Fill(1.f, isPositive); - bool isDaug = selectDaugTrack(track); - bool isMoth = selectMothTrack(track); + if (!candidate.hasTPC()) + return false; + hSelDaugQA->Fill(2.f, isPositive); - if (!isDaug && !isMoth) - continue; + if (askTOFforDaug && !candidate.hasTOF()) + return false; + hSelDaugQA->Fill(3.f, isPositive); - int pdgHypo = isMoth ? 1 : 0; - svCreator.appendTrackCand(track, collisions, pdgHypo, ambiguousTracks, bcs); - } - auto& kinkPool = svCreator.getSVCandPool(collisions, !unlikeSignBkg); + int itsTotCls = static_cast(candidate.itsNCls()); + int itsIBCls = static_cast(candidate.itsNClsInnerBarrel()); + h2ItsClsDaugBeforeSel->Fill(itsTotCls, itsIBCls); + if (itsIBCls != 0) + return false; + hSelDaugQA->Fill(4.f, isPositive); - for (const auto& svCand : kinkPool) { - kinkCandidate kinkCand; + if (candidate.itsNCls() >= nItsOuterBarrelLayers) + return false; + hSelDaugQA->Fill(5.f, isPositive); - auto trackMoth = tracks.rawIteratorAt(svCand.tr0Idx); - auto trackDaug = tracks.rawIteratorAt(svCand.tr1Idx); + if (candidate.tpcNClsCrossedRows() <= 0.8 * candidate.tpcNClsFindable()) + return false; + hSelDaugQA->Fill(6.f, isPositive); - auto const& collision = trackMoth.template collision_as(); - auto const& bc = collision.template bc_as(); - initCCDB(bc); + if (candidate.tpcNClsFound() <= nTPCClusMinDaug) + return false; + hSelDaugQA->Fill(7.f, isPositive); - o2::dataformats::VertexBase primaryVertex; - primaryVertex.setPos({collision.posX(), collision.posY(), collision.posZ()}); - primaryVertex.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); - kinkCand.primVtx = {primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}; + return true; + } - o2::track::TrackParCov trackParCovMoth = getTrackParCov(trackMoth); - o2::track::TrackParCov trackParCovMothPV{trackParCovMoth}; - o2::base::Propagator::Instance()->PropagateToXBxByBz(trackParCovMoth, LayerRadii[trackMoth.itsNCls() - 1]); + template + void buildKinkCand(const TSVCand& svCand, const TTracks& tracks, bool& isAccepted, const TColls&) + { + isAccepted = false; + kinkCandidate kinkCand; - std::array dcaInfoMoth; - o2::base::Propagator::Instance()->propagateToDCABxByBz({primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, trackParCovMothPV, 2.f, static_cast(cfgMaterialCorrection.value), &dcaInfoMoth); + auto trackMoth = tracks.rawIteratorAt(svCand.tr0Idx); + auto trackDaug = tracks.rawIteratorAt(svCand.tr1Idx); - if (std::abs(dcaInfoMoth[0]) > maxDCAMothToPV) { - continue; - } + // Fill Selections QA histo + int chargeCombSvCand = 2 * unlikeSignBkg + (trackMoth.sign() == -1 ? 1 : 0); + hSelKinkedTrackQA->Fill(0.f, chargeCombSvCand); // all candidates bin + hMothDaughSignsInit->Fill(trackMoth.sign(), trackDaug.sign()); - o2::track::TrackParCov trackParCovDaug = getTrackParCov(trackDaug); + auto const& collision = trackMoth.template collision_as(); + auto const& bc = collision.template bc_as(); + initCCDB(bc); - // check if the kink daughter is close to the mother - if (std::abs(trackParCovMoth.getZ() - trackParCovDaug.getZ()) > maxZDiff) { - continue; - } + o2::dataformats::VertexBase primaryVertex; + primaryVertex.setPos({collision.posX(), collision.posY(), collision.posZ()}); + primaryVertex.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); + kinkCand.primVtx = {primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}; - if ((std::abs(trackParCovMoth.getPhi() - trackParCovDaug.getPhi()) * radToDeg) > maxPhiDiff) { - continue; - } + o2::track::TrackParCov trackParCovMoth = getTrackParCov(trackMoth); + o2::track::TrackParCov trackParCovMothPV{trackParCovMoth}; + o2::base::Propagator::Instance()->PropagateToXBxByBz(trackParCovMoth, LayerRadii[trackMoth.itsNCls() - 1]); - // propagate to PV - std::array dcaInfoDaug; - o2::base::Propagator::Instance()->propagateToDCABxByBz({primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, trackParCovDaug, 2.f, static_cast(cfgMaterialCorrection.value), &dcaInfoDaug); - if (std::abs(dcaInfoDaug[0]) < minDCADaugToPV) { - continue; - } + std::array dcaInfoMoth; + o2::base::Propagator::Instance()->propagateToDCABxByBz({primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, trackParCovMothPV, 2.f, static_cast(cfgMaterialCorrection.value), &dcaInfoMoth); - if (updateMothTrackUsePV) { - // update the mother track parameters using the primary vertex - trackParCovMoth = trackParCovMothPV; - if (!trackParCovMoth.update(primaryVertex)) { - continue; - } - } + hDCAMothToPV->Fill(std::abs(dcaInfoMoth[0]), chargeCombSvCand); + if (std::abs(dcaInfoMoth[0]) > maxDCAMothToPV) { + return; + } + hSelKinkedTrackQA->Fill(1.f, chargeCombSvCand); // MaxDCAMothToPV cut bin - int nCand = 0; - try { - nCand = fitter.process(trackParCovMoth, trackParCovDaug); - } catch (...) { - LOG(error) << "Exception caught in DCA fitter process call!"; - continue; - } - if (nCand == 0) { - continue; - } + o2::track::TrackParCov trackParCovDaug = getTrackParCov(trackDaug); - if (!fitter.propagateTracksToVertex()) { - continue; - } + // check if the kink daughter is close to the mother + hZDiff->Fill(std::abs(trackParCovMoth.getZ() - trackParCovDaug.getZ()), chargeCombSvCand); + if (std::abs(trackParCovMoth.getZ() - trackParCovDaug.getZ()) > maxZDiff) { + return; + } + hSelKinkedTrackQA->Fill(2.f, chargeCombSvCand); // MaxZDiff cut bin - auto propMothTrack = fitter.getTrack(0); - auto propDaugTrack = fitter.getTrack(1); - kinkCand.decVtx = fitter.getPCACandidatePos(); + hPhiDiff->Fill(std::abs(trackParCovMoth.getPhi() - trackParCovDaug.getPhi()) * radToDeg, chargeCombSvCand); + if ((std::abs(trackParCovMoth.getPhi() - trackParCovDaug.getPhi()) * radToDeg) > maxPhiDiff) { + return; + } + hSelKinkedTrackQA->Fill(3.f, chargeCombSvCand); // MaxPhiDiff cut bin - // cut on decay radius to 17 cm - float decRad2 = kinkCand.decVtx[0] * kinkCand.decVtx[0] + kinkCand.decVtx[1] * kinkCand.decVtx[1]; - if (doSVRadiusCut && decRad2 < LayerRadii[3] * LayerRadii[3]) { - continue; - } + // propagate to PV + std::array dcaInfoDaug; + o2::base::Propagator::Instance()->propagateToDCABxByBz({primaryVertex.getX(), primaryVertex.getY(), primaryVertex.getZ()}, trackParCovDaug, 2.f, static_cast(cfgMaterialCorrection.value), &dcaInfoDaug); + hDCADaugToPV->Fill(std::abs(dcaInfoDaug[0]), chargeCombSvCand); + if (std::abs(dcaInfoDaug[0]) < minDCADaugToPV) { + return; + } + hSelKinkedTrackQA->Fill(4.f, chargeCombSvCand); // MinDCADaugToPV cut bin - // get last layer hitted by the mother and the first layer hitted by the daughter - int lastLayerMoth = 0, firstLayerDaug = 0; - for (int i = 0; i < 7; i++) { - if (trackMoth.itsClusterMap() & (1 << i)) { - lastLayerMoth = i; - } + if (updateMothTrackUsePV) { + // update the mother track parameters using the primary vertex + trackParCovMoth = trackParCovMothPV; + if (!trackParCovMoth.update(primaryVertex)) { + return; } + } + hSelKinkedTrackQA->Fill(5.f, chargeCombSvCand); // UpdateMothTrackUsePV cut bin - for (int i = 0; i < 7; i++) { - if (trackDaug.itsClusterMap() & (1 << i)) { - firstLayerDaug = i; - break; - } - } + int nCand = 0; + try { + nCand = fitter.process(trackParCovMoth, trackParCovDaug); + } catch (...) { + LOG(error) << "Exception caught in DCA fitter process call!"; + return; + } + if (nCand == 0) { + return; + } + hSelKinkedTrackQA->Fill(6.f, chargeCombSvCand); // DCAFitter cut bin - if (doSVRadiusCut && lastLayerMoth >= firstLayerDaug) { - continue; - } + if (!fitter.propagateTracksToVertex()) { + return; + } + hSelKinkedTrackQA->Fill(7.f, chargeCombSvCand); // PropagateTracksToVertex cut bin - if (doSVRadiusCut && decRad2 < LayerRadii[lastLayerMoth] * LayerRadii[lastLayerMoth]) { - continue; - } + auto propMothTrack = fitter.getTrack(0); + auto propDaugTrack = fitter.getTrack(1); + kinkCand.decVtx = fitter.getPCACandidatePos(); - for (int i = 0; i < 3; i++) { - kinkCand.decVtx[i] -= kinkCand.primVtx[i]; - } + // cut on decay radius to 17 cm + float decRad2 = kinkCand.decVtx[0] * kinkCand.decVtx[0] + kinkCand.decVtx[1] * kinkCand.decVtx[1]; + hMothDecRad2->Fill(decRad2, chargeCombSvCand); + int idxFirstOuterBarrelLayer = nItsInnerBarrelLayers; + if (doSVRadiusCut && decRad2 < LayerRadii[idxFirstOuterBarrelLayer] * LayerRadii[idxFirstOuterBarrelLayer]) { + return; + } + hSelKinkedTrackQA->Fill(8.f, chargeCombSvCand); // MothDecRad2InsideL4 cut bin - propMothTrack.getPxPyPzGlo(kinkCand.momMoth); - propDaugTrack.getPxPyPzGlo(kinkCand.momDaug); - for (int i = 0; i < 3; i++) { - kinkCand.momMoth[i] *= charge; - kinkCand.momDaug[i] *= charge; + // get last layer hitted by the mother and the first layer hitted by the daughter + int lastLayerMoth = 0, firstLayerDaug = 0; + for (int i = 0; i < nItsTotalLayers; i++) { + if (trackMoth.itsClusterMap() & (1 << i)) { + lastLayerMoth = i; } - float pMoth = propMothTrack.getP() * charge; - float pDaug = propDaugTrack.getP() * charge; - float spKink = kinkCand.momMoth[0] * kinkCand.momDaug[0] + kinkCand.momMoth[1] * kinkCand.momDaug[1] + kinkCand.momMoth[2] * kinkCand.momDaug[2]; - kinkCand.kinkAngle = std::acos(spKink / (pMoth * pDaug)); - - std::array neutDauMom{0.f, 0.f, 0.f}; - for (int i = 0; i < 3; i++) { - neutDauMom[i] = kinkCand.momMoth[i] - kinkCand.momDaug[i]; + } + + for (int i = 0; i < nItsTotalLayers; i++) { + if (trackDaug.itsClusterMap() & (1 << i)) { + firstLayerDaug = i; + break; } + } - float chargedDauE = std::sqrt(pDaug * pDaug + chargedDauMass * chargedDauMass); - float neutE = std::sqrt(neutDauMom[0] * neutDauMom[0] + neutDauMom[1] * neutDauMom[1] + neutDauMom[2] * neutDauMom[2] + neutDauMass * neutDauMass); - float invMass = std::sqrt((chargedDauE + neutE) * (chargedDauE + neutE) - (pMoth * pMoth)); - - h2DeDxDaugSel->Fill(trackDaug.tpcInnerParam() * trackDaug.sign(), trackDaug.tpcSignal()); - h2KinkAnglePt->Fill(trackMoth.pt() * charge * trackMoth.sign(), kinkCand.kinkAngle * radToDeg); - h2MothMassPt->Fill(trackMoth.pt() * charge * trackMoth.sign(), invMass); - h2ClsMapPtMoth->Fill(trackMoth.pt() * charge * trackMoth.sign(), trackMoth.itsClusterMap()); - h2ClsMapPtDaug->Fill(trackDaug.pt() * charge * trackDaug.sign(), trackDaug.itsClusterMap()); - - kinkCand.collisionID = collision.globalIndex(); - kinkCand.mothTrackID = trackMoth.globalIndex(); - kinkCand.daugTrackID = trackDaug.globalIndex(); - - kinkCand.dcaXYmoth = dcaInfoMoth[0]; - kinkCand.mothSign = trackMoth.sign(); - kinkCand.dcaXYdaug = dcaInfoDaug[0]; - kinkCand.dcaKinkTopo = std::sqrt(fitter.getChi2AtPCACandidate()); - kinkCandidates.push_back(kinkCand); + if (doSVRadiusCut && lastLayerMoth >= firstLayerDaug) { + return; } + hSelKinkedTrackQA->Fill(9.f, chargeCombSvCand); // MothDaugLastLayers cut bin + + if (doSVRadiusCut && decRad2 < LayerRadii[lastLayerMoth] * LayerRadii[lastLayerMoth]) { + return; + } + hSelKinkedTrackQA->Fill(10.f, chargeCombSvCand); // MothLastLayerRadiusCheck cut bin + + for (int i = 0; i < nCoords; i++) { + kinkCand.decVtx[i] -= kinkCand.primVtx[i]; + } + + propMothTrack.getPxPyPzGlo(kinkCand.momMoth); + propDaugTrack.getPxPyPzGlo(kinkCand.momDaug); + for (int i = 0; i < nCoords; i++) { + kinkCand.momMoth[i] *= charge; + kinkCand.momDaug[i] *= charge; + } + float pMoth = propMothTrack.getP() * charge; + float pDaug = propDaugTrack.getP() * charge; + float spKink = kinkCand.momMoth[0] * kinkCand.momDaug[0] + kinkCand.momMoth[1] * kinkCand.momDaug[1] + kinkCand.momMoth[2] * kinkCand.momDaug[2]; + kinkCand.kinkAngle = std::acos(spKink / (pMoth * pDaug)); + + std::array neutDauMom{0.f, 0.f, 0.f}; + for (int i = 0; i < nCoords; i++) { + neutDauMom[i] = kinkCand.momMoth[i] - kinkCand.momDaug[i]; + } + + float chargedDauE = std::sqrt(pDaug * pDaug + chargedDauMass * chargedDauMass); + float neutE = std::sqrt(neutDauMom[0] * neutDauMom[0] + neutDauMom[1] * neutDauMom[1] + neutDauMom[2] * neutDauMom[2] + neutDauMass * neutDauMass); + float invMass = std::sqrt((chargedDauE + neutE) * (chargedDauE + neutE) - (pMoth * pMoth)); + + h2DeDxDaugSel->Fill(trackDaug.tpcInnerParam() * trackDaug.sign(), trackDaug.tpcSignal()); + h2KinkAnglePt->Fill(trackMoth.pt() * charge * trackMoth.sign(), kinkCand.kinkAngle * radToDeg); + h2MothMassPt->Fill(trackMoth.pt() * charge * trackMoth.sign(), invMass); + h2ClsMapPtMoth->Fill(trackMoth.pt() * charge * trackMoth.sign(), trackMoth.itsClusterMap()); + h2ClsMapPtDaug->Fill(trackDaug.pt() * charge * trackDaug.sign(), trackDaug.itsClusterMap()); + hMothDaughSignsFinal->Fill(trackMoth.sign(), trackDaug.sign()); + + kinkCand.collisionID = collision.globalIndex(); + kinkCand.mothTrackID = trackMoth.globalIndex(); + kinkCand.daugTrackID = trackDaug.globalIndex(); + + kinkCand.dcaXYmoth = dcaInfoMoth[0]; + kinkCand.mothSign = trackMoth.sign(); + kinkCand.dcaXYdaug = dcaInfoDaug[0]; + kinkCand.dcaKinkTopo = std::sqrt(fitter.getChi2AtPCACandidate()); + kinkCandidates.push_back(kinkCand); + isAccepted = true; + return; } void initCCDB(aod::BCs::iterator const& bc) @@ -451,11 +663,188 @@ struct kinkBuilder { LOG(info) << "Task initialized for run " << mRunNumber << " with magnetic field " << mBz << " kZG"; } - void process(aod::Collisions const& collisions, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCs const& bcs) + template + void buildSvPool(const TColls& collisions, const TTracks& tracks, const TAmbiTracks& ambiguousTracks, const aod::BCs& bcs) + { + svCreator.clearPools(); + svCreator.fillBC2Coll(collisions, bcs); + + for (const auto& track : tracks) { + if (std::abs(track.eta()) > etaMax) { + continue; + } + + bool canBeMoth = true; + bool canBeDaug = true; + + if constexpr (KeepOnlyKinks) { + if (!track.has_mcParticle()) { + LOG(debug) << "Track has no MC particle"; + continue; + } + auto genPart = track.template mcParticle_as(); + + canBeMoth = std::find( + mothMatchPdgCodes.begin(), + mothMatchPdgCodes.end(), + std::abs(genPart.pdgCode())) != mothMatchPdgCodes.end(); + + canBeDaug = false; + for (const auto& mother : genPart.template mothers_as()) { + if (std::find( + mothMatchPdgCodes.begin(), + mothMatchPdgCodes.end(), + std::abs(mother.pdgCode())) != mothMatchPdgCodes.end()) { + canBeDaug = true; + break; + } + } + } + + bool isMoth = canBeMoth && selectMothTrack(track); + bool isDaug = canBeDaug && selectDaugTrack(track); + + if (!isDaug && !isMoth) { + continue; + } + int pdgHypo = isMoth ? 1 : 0; + svCreator.appendTrackCand(track, collisions, pdgHypo, ambiguousTracks, bcs); + } + } + + template + void fillOutputsData(const TColls& collisions, const TTracks& tracks, const aod::AmbiguousTracks& ambiTracks, const aod::BCs& bcs) + { + kinkCandidates.clear(); + + buildSvPool(collisions, tracks, ambiTracks, bcs); + auto& kinkPool = svCreator.getSVCandPool(collisions, !unlikeSignBkg); + bool isAccepted = false; + for (const auto& svCand : kinkPool) { + buildKinkCand(svCand, tracks, isAccepted, collisions); + } + + // sort kinkCandidates by collisionID to allow joining with collision table + std::sort(kinkCandidates.begin(), kinkCandidates.end(), [](const kinkCandidate& a, const kinkCandidate& b) { return a.collisionID < b.collisionID; }); + + for (const auto& kinkCand : kinkCandidates) { + if (fillDebugTable) { + outputDataTableUB(kinkCand.decVtx[0], kinkCand.decVtx[1], kinkCand.decVtx[2], + kinkCand.mothSign, kinkCand.momMoth[0], kinkCand.momMoth[1], kinkCand.momMoth[2], + kinkCand.momDaug[0], kinkCand.momDaug[1], kinkCand.momDaug[2], + kinkCand.dcaXYmoth, kinkCand.dcaXYdaug, kinkCand.dcaKinkTopo); + } else { + outputDataTable(kinkCand.collisionID, kinkCand.mothTrackID, kinkCand.daugTrackID, + kinkCand.decVtx[0], kinkCand.decVtx[1], kinkCand.decVtx[2], + kinkCand.mothSign, kinkCand.momMoth[0], kinkCand.momMoth[1], kinkCand.momMoth[2], + kinkCand.momDaug[0], kinkCand.momDaug[1], kinkCand.momDaug[2], + kinkCand.dcaXYmoth, kinkCand.dcaXYdaug, kinkCand.dcaKinkTopo); + } + } + } + + void processData(aod::Collisions const& collisions, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCs const& bcs) + { + fillOutputsData(collisions, tracks, ambiTracks, bcs); + } + PROCESS_SWITCH(kinkBuilder, processData, "Data processing", true); + + void processDataWCentSel(CollisionsCentSel const& collisions, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCs const& bcs) + { + fillOutputsData(collisions, tracks, ambiTracks, bcs); + } + PROCESS_SWITCH(kinkBuilder, processDataWCentSel, "Data processing with centrality selection", false); + + template + int matchKinkDecay(const TMother& motherPart, const aod::McParticles& mcParticles) { + int pdgMother = motherPart.pdgCode(); + int8_t sign = 0; + int pdgCodeNeutralDaug{-1}, pdgCodeChargedDaug{-1}; + std::array finState = {-1, -1}; + switch (std::abs(pdgMother)) { + case PDG_t::kSigmaMinus: { + // Swap the sign of the neutral decay products in case of anti-particles + pdgCodeNeutralDaug = (pdgMother > 0) ? +PDG_t::kNeutron : -PDG_t::kNeutron; + pdgCodeChargedDaug = (pdgMother > 0) ? +PDG_t::kPiMinus : +PDG_t::kPiPlus; + finState = {pdgCodeChargedDaug, pdgCodeNeutralDaug}; // Both decay channels have the same neutral daughter + if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, pdgMother, finState, true, &sign, DepthMcMatchMax)) { + return SigmaMinusToPiMinusNeutron; + } + break; + } + case PDG_t::kSigmaPlus: { + // Swap the sign of the neutral decay products in case of anti-particles + pdgCodeNeutralDaug = (pdgMother > 0) ? +PDG_t::kNeutron : -PDG_t::kNeutron; + pdgCodeChargedDaug = (pdgMother > 0) ? +PDG_t::kPiPlus : +PDG_t::kPiMinus; + finState = {pdgCodeChargedDaug, pdgCodeNeutralDaug}; + if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, pdgMother, finState, true, &sign, DepthMcMatchMax)) { + return SigmaPlusToPiPlusNeutron; + } + + // Swap the sign of the neutral decay products in case of anti-particles + pdgCodeNeutralDaug = (pdgMother > 0) ? +PDG_t::kPi0 : -PDG_t::kPi0; + pdgCodeChargedDaug = (pdgMother > 0) ? +PDG_t::kProton : -PDG_t::kProton; + finState = {pdgCodeChargedDaug, pdgCodeNeutralDaug}; + if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, pdgMother, finState, true, &sign, DepthMcMatchMax)) { + return SigmaPlusToProtonPi0; + } + break; + } + default: + LOG(warning) << "No matching function implemented for the selected mother particle hypothesis. Returning -1."; + return -1; + } + return -1; + } + + template + void fillOutputsMc(const TColls& mcRecoCollisions, const TTracks& tracksMc, const aod::AmbiguousTracks& ambiTracksMc, const aod::BCs& bcs, const aod::McParticles& mcParticles) + { kinkCandidates.clear(); - fillCandidateData(collisions, tracks, ambiTracks, bcs); + + if (skipBkgCands) { + buildSvPool(mcRecoCollisions, tracksMc, ambiTracksMc, bcs); + } else { + buildSvPool(mcRecoCollisions, tracksMc, ambiTracksMc, bcs); + } + + auto& kinkPool = svCreator.getSVCandPool(mcRecoCollisions, !unlikeSignBkg); + for (const auto& svCand : kinkPool) { + // Perform matching of the kink candidate + auto trackMoth = tracksMc.rawIteratorAt(svCand.tr0Idx); + auto genMothPart = trackMoth.template mcParticle_as(); + auto trackDaug = tracksMc.rawIteratorAt(svCand.tr1Idx); + auto genDaugPart = trackDaug.template mcParticle_as(); + int decayChannel{-1}; + if (skipBkgCands) { + + // Check mother PDG first + if (std::find(mothMatchPdgCodes.begin(), mothMatchPdgCodes.end(), std::abs(genMothPart.pdgCode())) == mothMatchPdgCodes.end()) { + continue; + } + + // Check that the daughter was generated from the selected mother + int genDaugMothIdx = RecoDecay::getMother(mcParticles, genDaugPart, genMothPart.pdgCode(), true); + if (genDaugMothIdx != trackMoth.mcParticleId()) { + continue; // Skip candidates where the daughter is not coming from the selected mother + } + decayChannel = matchKinkDecay(genMothPart, mcParticles); + if (decayChannel < 0) { + continue; // Skip candidates that do not match the decay channels of interest + } + hRecCandidatesRecoPt->Fill(decayChannel, trackMoth.pt()); // Decay channel match bin + hRecCandidatesGenPt->Fill(decayChannel, genMothPart.pt()); // Decay channel match bin + hRecCandidatesDeltaPt->Fill((trackMoth.pt() - genMothPart.pt()) / genMothPart.pt(), genMothPart.pt()); // Decay channel match bin + } + bool isAccepted = false; + buildKinkCand(svCand, tracksMc, isAccepted, mcRecoCollisions); + // If candidate passed all selections and was built, fill histogram + if (isAccepted && skipBkgCands) { + hRecPtKinkAngle[decayChannel]->Fill(trackMoth.pt(), std::abs(trackMoth.phi() - trackDaug.phi()) * radToDeg); + } + } // sort kinkCandidates by collisionID to allow joining with collision table std::sort(kinkCandidates.begin(), kinkCandidates.end(), [](const kinkCandidate& a, const kinkCandidate& b) { return a.collisionID < b.collisionID; }); @@ -474,7 +863,48 @@ struct kinkBuilder { kinkCand.dcaXYmoth, kinkCand.dcaXYdaug, kinkCand.dcaKinkTopo); } } + + // Generated kinks + for (const auto& mcPart : mcParticles) { + if (std::find(mothMatchPdgCodes.begin(), mothMatchPdgCodes.end(), std::abs(mcPart.pdgCode())) == mothMatchPdgCodes.end()) { + continue; // Skip if mother particle does not match the PDG codes of interest + } + int decayChannel = matchKinkDecay(mcPart, mcParticles); // Dummy daughter PDG code since we are not checking it in this case + if (decayChannel < 0) { + continue; // Skip if no matching decay channel is found + } + hGenCandidates->Fill(decayChannel, mcPart.pt()); + std::vector arrDaughIdxs = {}; + RecoDecay::getDaughters(mcPart, &arrDaughIdxs, std::array{0}, DepthMcMatchMax); + for (auto iProng = 0u; iProng < arrDaughIdxs.size(); ++iProng) { + auto daughI = mcParticles.rawIteratorAt(arrDaughIdxs[iProng]); + if (std::abs(daughI.pdgCode()) == PDG_t::kPiPlus || std::abs(daughI.pdgCode()) == PDG_t::kProton) { + hGenPtKinkAngle[decayChannel]->Fill(mcPart.pt(), std::abs(mcPart.phi() - daughI.phi()) * radToDeg); + hGenDecayRadius->Fill(std::hypot(daughI.vx(), daughI.vy()), mcPart.pt()); + } + } + } + } + + void processMc(McRecoCollisions const& mcRecoCollisions, + aod::McParticles const& mcParticles, + TracksFullMc const& tracksMc, + aod::AmbiguousTracks const& ambiTracksMc, + aod::BCs const& bcs) + { + fillOutputsMc(mcRecoCollisions, tracksMc, ambiTracksMc, bcs, mcParticles); + } + PROCESS_SWITCH(kinkBuilder, processMc, "MC processing", false); + + void processMcWCent(McRecoCollisionsCentSel const& mcRecoCollisions, + aod::McParticles const& mcParticles, + TracksFullMc const& tracksMc, + aod::AmbiguousTracks const& ambiTracksMc, + aod::BCs const& bcs) + { + fillOutputsMc(mcRecoCollisions, tracksMc, ambiTracksMc, bcs, mcParticles); } + PROCESS_SWITCH(kinkBuilder, processMcWCent, "MC processing with centrality selection", false); }; WorkflowSpec diff --git a/PWGLF/TableProducer/Common/lfTPCPID.cxx b/PWGLF/TableProducer/Common/lfTPCPID.cxx index 90c124bbbe8..d84c977c101 100644 --- a/PWGLF/TableProducer/Common/lfTPCPID.cxx +++ b/PWGLF/TableProducer/Common/lfTPCPID.cxx @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -62,34 +63,34 @@ static const std::vector parameterNames{"Use default tiny", "Set parameters", "bb1", "bb2", "bb3", "bb4", "bb5", "MIP value", "Charge exponent", "Resolution"}; -static constexpr float defaultParameters[nSpecies][nParameters]{{2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, - {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}; +static constexpr std::array, nSpecies> defaultParameters{{{2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}, + {2.f, 2.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}}; static constexpr int nOptions = 4; static const std::vector optionNames{"Bethe Bloch path", // If empty using the default/json values. Can be a CCDB path if the string starts with ccdb:// "Post calibration path", // If empty using the default/json values. Can be a CCDB path if the string starts with ccdb:// "Simple Bethe Bloch", // (true/false) "RequirePostCalib"}; // (true/false) -const std::string defaultOptions[nSpecies][nOptions]{{"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}, - {"", "", "false", "false"}}; +const std::array, nSpecies> defaultOptions{{{"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}, + {"", "", "false", "false"}}}; // Structure to hold the parameters struct bbParams { const std::string name; - bbParams(const std::string& n) : name(n) {} + explicit bbParams(const std::string& n) : name(n) {} // Parameters for the Bethe-Bloch parametrization float bb1 = 0.03209809958934784f; // Aleph Bethe Bloch parameter 1 float bb2 = 19.9768009185791f; // Aleph Bethe Bloch parameter 2 @@ -472,15 +473,15 @@ struct lfTpcPid { // Parameters setting from json Configurable> bbParameters{"bbParameters", - {defaultParameters[0], nSpecies, nParameters, particleNames, parameterNames}, + {defaultParameters.front().data(), nSpecies, nParameters, particleNames, parameterNames}, "Bethe Bloch parameters"}; // Parameter setting from input file (including the ccdb) Configurable> fileParamBbPositive{"fileParamBbPositive", - {defaultOptions[0], nSpecies, nOptions, particleNames, optionNames}, + {defaultOptions.front().data(), nSpecies, nOptions, particleNames, optionNames}, "Input for the parametrization for positive particles. If empty using the default/json values. Can be a CCDB path if the string starts with ccdb://"}; Configurable> fileParamBbNegative{"fileParamBbNegative", - {defaultOptions[0], nSpecies, nOptions, particleNames, optionNames}, + {defaultOptions.front().data(), nSpecies, nOptions, particleNames, optionNames}, "Input for the parametrization for negative particles. If empty using the default/json values. Can be a CCDB path if the string starts with ccdb://"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; diff --git a/PWGLF/TableProducer/Common/mcCentrality.cxx b/PWGLF/TableProducer/Common/mcCentrality.cxx index e7ccd4d59fb..2a73fa6f785 100644 --- a/PWGLF/TableProducer/Common/mcCentrality.cxx +++ b/PWGLF/TableProducer/Common/mcCentrality.cxx @@ -17,27 +17,20 @@ /// \brief Task to produce the table for the equalized multiplicity into centrality bins /// -#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/Utils/mcCentralityModule.h" -#include "PWGLF/Utils/inelGt.h" +#include "Common/DataModel/Multiplicity.h" #include -#include #include #include #include #include #include -#include #include -#include #include #include -#include - -#include -#include #include using namespace o2; @@ -46,141 +39,37 @@ using namespace o2::framework::expressions; /// Task to produce the response table struct McCentrality { - - // Tables to produce - Produces centFV0A; - Produces centFT0M; - Produces centFT0A; - Produces centFT0C; - Produces centFDDM; - Produces centNTPV; - // Input parameters + o2::framework::Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Service ccdb; - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable ccdbTimestamp{"ccdbTimestamp", -1, "timestamp of the object used to query in CCDB the detector response. If 0 the object corresponding to the run number is used, if < 0 the latest object is used"}; - Configurable path{"path", "/tmp/InputCalibMC.root", "path to calib file or ccdb path if begins with ccdb://"}; - Configurable selectPrimaries{"selectPrimaries", true, "Select only primary particles"}; - Service pdgDB; - ConfigurableAxis binsPercentile{"binsPercentile", {VARIABLE_WIDTH, 0, 0.001, 0.01, 1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}, "Binning of the percentile axis"}; - ConfigurableAxis binsMultiplicity{"binsMultiplicity", {1000, 0, 5000}, "Binning of the multiplicity axis"}; - Configurable fillFt0A{"fillFt0A", false, "Fills the FT0A histogram"}; - Configurable fillFt0C{"fillFt0C", false, "Fills the FT0C histogram"}; - Configurable doNotCrashOnNull{"doNotCrashOnNull", false, "If ccdb object does not exist, fill with dummy values"}; - - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - TH1F* h1dFT0M = nullptr; - TH1F* h1dFT0A = nullptr; - TH1F* h1dFT0C = nullptr; - // TH1F* h1dFDD; - // TH1F* h1dNTP; + o2::pwglf::mccentrality::products products; + o2::pwglf::mccentrality::coreConfigurables baseOpts; + o2::pwglf::mccentrality::BuilderModule mcCentralityBuilderModule; - o2::pwglf::ParticleCounter mCounter; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext& /*initContext*/) + void init(o2::framework::InitContext& initContext) { // Set up the CCDB ccdb->setURL(ccdbUrl.value); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); ccdb->setFatalWhenNull(false); - mCounter.mPdgDatabase = pdgDB.service; - mCounter.mSelectPrimaries = selectPrimaries.value; - histos.add("FT0M/percentile", "FT0M percentile.", HistType::kTH1D, {{binsPercentile, "FT0M percentile"}}); - histos.add("FT0M/percentilevsMult", "FT0M percentile.", HistType::kTH2D, {{binsPercentile, "FT0M percentile"}, {binsMultiplicity, "FT0M mult."}}); - if (fillFt0A) { - histos.add("FT0A/percentile", "FT0A percentile.", HistType::kTH1D, {{binsPercentile, "FT0A percentile"}}); - histos.add("FT0A/percentilevsMult", "FT0A percentile.", HistType::kTH2D, {{binsPercentile, "FT0A percentile"}, {binsMultiplicity, "FT0A mult."}}); - } - if (fillFt0C) { - histos.add("FT0C/percentile", "FT0C percentile.", HistType::kTH1D, {{binsPercentile, "FT0C percentile"}}); - histos.add("FT0C/percentilevsMult", "FT0C percentile.", HistType::kTH2D, {{binsPercentile, "FT0C percentile"}, {binsMultiplicity, "FT0C mult."}}); - } - - TList* lOfInput = nullptr; - if (path.value.rfind("ccdb://", 0) == 0) { // Getting post calib. from CCDB - path.value.replace(0, 7, ""); - lOfInput = ccdb->get(path); - if (!lOfInput) { - if (doNotCrashOnNull) { - LOG(info) << "Could not find the calibration TList from CCDB in path " << path << ", will fill tables with dummy values"; - } else { - LOG(fatal) << "Could not find the calibration TList from CCDB in path " << path; - return; - } - } - } else { // Getting post calib. from file - TFile* f = TFile::Open(path.value.c_str(), "READ"); - if (!f) { - LOG(fatal) << "The input file " << path << " is not valid"; - } - if (!f->IsOpen()) { - LOG(fatal) << "The input file " << f->GetName() << " is not open"; - } - lOfInput = static_cast(f->Get("ccdb_object")); - if (!lOfInput) { - f->ls(); - LOG(fatal) << "The input file " << path.value << " does not contain the TList ccdb_object"; - } - } - auto getHist = [this, lOfInput](const char* name) -> TH1F* { - if (!lOfInput) { - return nullptr; - } - auto hist = static_cast(lOfInput->FindObject(name)); - if (!hist) { - lOfInput->ls(); - if (this->doNotCrashOnNull) { - LOG(info) << "Could not open histogram " << name << " from TList, will fill tables with dummy values"; - } else { - LOG(fatal) << "Could not open histogram " << name << " from TList"; - } - } - return hist; - }; - h1dFT0M = getHist("h1dFT0M"); - if (fillFt0A) { - h1dFT0A = getHist("h1dFT0A"); - } - if (fillFt0C) { - h1dFT0C = getHist("h1dFT0C"); - } + mcCentralityBuilderModule.init(baseOpts, histos, initContext); } // Full tables (independent on central calibrations) - void process(aod::McCollision const& /*mcCollision*/, - aod::McParticles const& mcParticles) + void process(soa::Join const& mcCollisions, + aod::BCsWithTimestamps const& bcs) { - const float nFT0A = mCounter.countFT0A(mcParticles); - const float nFT0C = mCounter.countFT0C(mcParticles); - const float nFT0M = nFT0A + nFT0C; - // const float nFV0A = mCounter.countFV0A(mcParticles); - - float valueCentFT0M = 105.0f; - if (h1dFT0M) - valueCentFT0M = h1dFT0M->GetBinContent(h1dFT0M->FindBin(nFT0M)); - if (fillFt0A) { - float valueCentFT0A = 105.0f; - if (h1dFT0A) - valueCentFT0A = h1dFT0A->GetBinContent(h1dFT0A->FindBin(nFT0A)); - centFT0A(valueCentFT0A); - } - if (fillFt0C) { - float valueCentFT0C = 105.0f; - if (h1dFT0C) - valueCentFT0C = h1dFT0C->GetBinContent(h1dFT0C->FindBin(nFT0C)); - centFT0C(valueCentFT0C); - } - // const float valueCentFV0A = h1dFT0M->GetBinContent(h1dFT0M->FindBin(nFV0A)); - - centFT0M(valueCentFT0M); - // centFV0A(valueCentFV0A); - histos.fill(HIST("FT0M/percentile"), valueCentFT0M); - histos.fill(HIST("FT0M/percentilevsMult"), valueCentFT0M, nFT0M); + mcCentralityBuilderModule.dataProcess(ccdb, histos, bcs, mcCollisions, products); } }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Common/spvector.cxx b/PWGLF/TableProducer/Common/spvector.cxx index 02748f5d7be..85806454c4e 100644 --- a/PWGLF/TableProducer/Common/spvector.cxx +++ b/PWGLF/TableProducer/Common/spvector.cxx @@ -20,11 +20,8 @@ #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" #include -#include #include #include #include @@ -37,6 +34,7 @@ #include #include +#include #include #include #include @@ -53,6 +51,7 @@ #include #include #include +#include #include using namespace o2; @@ -61,8 +60,6 @@ using namespace o2::framework::expressions; using namespace o2::constants::physics; using namespace o2::aod::rctsel; -using BCsRun3 = soa::Join; - struct spvector { Produces spcalibrationtable; @@ -75,7 +72,6 @@ struct spvector { // Enable access to the CCDB for the offset and correction constants and save them in dedicated variables. Service ccdb; - o2::ccdb::CcdbApi ccdbApi; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; @@ -114,8 +110,12 @@ struct spvector { Configurable CentfineNbins{"CentfineNbins", 16, "Number of bins in cent fine histograms"}; Configurable lfinebinCent{"lfinebinCent", 0.0, "lower bin value in cent fine histograms"}; Configurable hfinebinCent{"hfinebinCent", 80.0, "higher bin value in cent fine histograms"}; + Configurable TimefineNbins{"TimefineNbins", 120, "Number of bins in Time fine histograms"}; + Configurable lfinebinTime{"lfinebinTime", 0, "lower bin value in Time fine histograms"}; + Configurable hfinebinTime{"hfinebinTime", 120, "higher bin value in Time fine histograms"}; } configbins; + Configurable useoldrecentering{"useoldrecentering", true, "flag to perform old vs new recentering"}; Configurable useShift{"useShift", false, "shift histograms"}; Configurable ispolarization{"ispolarization", false, "Flag to check polarization"}; Configurable followpub{"followpub", true, "flag to use alphaZDC"}; @@ -123,10 +123,13 @@ struct spvector { Configurable useCallibvertex{"useCallibvertex", false, "use calibration for vxy"}; Configurable coarse1{"coarse1", false, "RE1"}; Configurable fine1{"fine1", false, "REfine1"}; + Configurable finetime1{"finetime1", false, "REfinetime1"}; Configurable coarse2{"coarse2", false, "RE2"}; Configurable fine2{"fine2", false, "REfine2"}; + Configurable finetime2{"finetime2", false, "REfinetime2"}; Configurable coarse3{"coarse3", false, "RE3"}; Configurable fine3{"fine3", false, "REfine3"}; + Configurable finetime3{"finetime3", false, "REfinetime3"}; Configurable coarse4{"coarse4", false, "RE4"}; Configurable fine4{"fine4", false, "REfine4"}; Configurable coarse5{"coarse5", false, "RE5"}; @@ -135,6 +138,7 @@ struct spvector { Configurable fine6{"fine6", false, "REfine6"}; Configurable useRecentereSp{"useRecentereSp", false, "use Recentering with Sparse or THn"}; Configurable useRecenterefineSp{"useRecenterefineSp", false, "use fine Recentering with THn"}; + Configurable useTimeRecentering{"useTimeRecentering", false, "Use residual time recentering"}; Configurable ConfGainPath{"ConfGainPath", "Users/p/prottay/My/Object/NewPbPbpass4_10092024/gaincallib", "Path to gain calibration"}; Configurable ConfGainPathvxy{"ConfGainPathvxy", "Users/p/prottay/My/Object/swapcoords/PbPbpass4_20112024/recentervert", "Path to gain calibration for vxy"}; Configurable ConfRecentereSp{"ConfRecentereSp", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn Path for recentere"}; @@ -169,28 +173,97 @@ struct spvector { Configurable ConfRecenterevzSp6{"ConfRecenterevzSp6", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn Path for vz recentere6"}; Configurable ConfShiftC{"ConfShiftC", "Users/p/prottay/My/Object/Testinglocaltree/shiftcallib2", "Path to shift C"}; Configurable ConfShiftA{"ConfShiftA", "Users/p/prottay/My/Object/Testinglocaltree/shiftcallib2", "Path to shift A"}; + Configurable confRecentereTimeSp1{"confRecentereTimeSp1", "Users/p/prottay/My/Object/GCwithoutcfactorgoodVztimedep/From676541/TestDDlocal/2024PbPbpass3_23062026/recenterlast2", "Path to time recentering map 1"}; + Configurable confRecentereTimeSp2{"confRecentereTimeSp2", "Users/p/prottay/My/Object/GCwithoutcfactorgoodVztimedep/From676541/TestDDlocal/2024PbPbpass3_23062026/recenterlast2", "Path to time recentering map 2"}; + Configurable confRecentereTimeSp3{"confRecentereTimeSp3", "Users/p/prottay/My/Object/GCwithoutcfactorgoodVztimedep/From676541/TestDDlocal/2024PbPbpass3_23062026/recenterlast3", "Path to time recentering map 3"}; - // Event selection cuts - Alex - /* - TF1* fMultPVCutLow = nullptr; - TF1* fMultPVCutHigh = nullptr; - TF1* fMultCutLow = nullptr; - TF1* fMultCutHigh = nullptr; - TF1* fMultMultPVCut = nullptr; - */ - /* - template - bool eventSelected(TCollision collision, const double& centrality) - { - auto multNTracksPV = collision.multNTracksPV(); - if (multNTracksPV < fMultPVCutLow->Eval(centrality)) - return 0; - if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) - return 0; - - return 1; - } - */ + struct : ConfigurableGroup { + // Five recentering stages. Each stage is applied in the strict order: + // cent -> (cent,Vx) -> (cent,Vy) -> (cent,Vz). + Configurable finecent1{"finecent1", false, "Apply fine centrality recentering, stage 1"}; + Configurable finecent2{"finecent2", false, "Apply fine centrality recentering, stage 2"}; + Configurable finecent3{"finecent3", false, "Apply fine centrality recentering, stage 3"}; + Configurable finecent4{"finecent4", false, "Apply fine centrality recentering, stage 4"}; + Configurable finecent5{"finecent5", false, "Apply fine centrality recentering, stage 5"}; + Configurable finecent6{"finecent6", false, "Apply fine centrality recentering, stage 6"}; + + Configurable finecentvx1{"finecentvx1", false, "Apply fine (cent,Vx) recentering, stage 1"}; + Configurable finecentvx2{"finecentvx2", false, "Apply fine (cent,Vx) recentering, stage 2"}; + Configurable finecentvx3{"finecentvx3", false, "Apply fine (cent,Vx) recentering, stage 3"}; + Configurable finecentvx4{"finecentvx4", false, "Apply fine (cent,Vx) recentering, stage 4"}; + Configurable finecentvx5{"finecentvx5", false, "Apply fine (cent,Vx) recentering, stage 5"}; + Configurable finecentvx6{"finecentvx6", false, "Apply fine (cent,Vx) recentering, stage 6"}; + + Configurable finecentvy1{"finecentvy1", false, "Apply fine (cent,Vy) recentering, stage 1"}; + Configurable finecentvy2{"finecentvy2", false, "Apply fine (cent,Vy) recentering, stage 2"}; + Configurable finecentvy3{"finecentvy3", false, "Apply fine (cent,Vy) recentering, stage 3"}; + Configurable finecentvy4{"finecentvy4", false, "Apply fine (cent,Vy) recentering, stage 4"}; + Configurable finecentvy5{"finecentvy5", false, "Apply fine (cent,Vy) recentering, stage 5"}; + Configurable finecentvy6{"finecentvy6", false, "Apply fine (cent,Vy) recentering, stage 6"}; + + Configurable finecentvz1{"finecentvz1", false, "Apply fine (cent,Vz) recentering, stage 1"}; + Configurable finecentvz2{"finecentvz2", false, "Apply fine (cent,Vz) recentering, stage 2"}; + Configurable finecentvz3{"finecentvz3", false, "Apply fine (cent,Vz) recentering, stage 3"}; + Configurable finecentvz4{"finecentvz4", false, "Apply fine (cent,Vz) recentering, stage 4"}; + Configurable finecentvz5{"finecentvz5", false, "Apply fine (cent,Vz) recentering, stage 5"}; + Configurable finecentvz6{"finecentvz6", false, "Apply fine (cent,Vz) recentering, cycle 6"}; + + Configurable finevztime1{"finevztime1", false, "Apply fine (Vz,time) recentering, cycle 1"}; + Configurable finevztime2{"finevztime2", false, "Apply fine (Vz,time) recentering, cycle 2"}; + Configurable finevztime3{"finevztime3", false, "Apply fine (Vz,time) recentering, cycle 3"}; + Configurable finevztime4{"finevztime4", false, "Apply fine (Vz,time) recentering, cycle 4"}; + Configurable finevztime5{"finevztime5", false, "Apply fine (Vz,time) recentering, cycle 5"}; + + Configurable useRecenterefinecentSp{"useRecenterefinecentSp", false, "use finecent Recentering with THn"}; + Configurable useRecenterefinecentvxvyvzSp{"useRecenterefinecentvxvyvzSp", false, "use finecentvxvyvz Recentering with THn"}; + Configurable useRecenterefinevztimeSp{"useRecenterefinevztimeSp", false, "use fine (Vz,time) recentering with TH3F"}; + + Configurable confRecentereCentSp{"confRecentereCentSp", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for cent recentering"}; + Configurable confRecentereCentSp2{"confRecentereCentSp2", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for cent recentering 2"}; + Configurable confRecentereCentSp3{"confRecentereCentSp3", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for cent recentering 3"}; + Configurable confRecentereCentSp4{"confRecentereCentSp4", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for cent recentering 4"}; + Configurable confRecentereCentSp5{"confRecentereCentSp5", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for cent recentering 5"}; + Configurable confRecentereCentSp6{"confRecentereCentSp6", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for cent recentering 6"}; + Configurable confRecentereCentVxSp{"confRecentereCentVxSp", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvx recentering"}; + Configurable confRecentereCentVySp{"confRecentereCentVySp", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvy recentering"}; + Configurable confRecentereCentVzSp{"confRecentereCentVzSp", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvz recentering"}; + Configurable confRecentereCentVxSp2{"confRecentereCentVxSp2", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvx2 recentering"}; + Configurable confRecentereCentVySp2{"confRecentereCentVySp2", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvy2 recentering"}; + Configurable confRecentereCentVzSp2{"confRecentereCentVzSp2", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvz2 recentering"}; + Configurable confRecentereCentVxSp3{"confRecentereCentVxSp3", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvx3 recentering"}; + Configurable confRecentereCentVySp3{"confRecentereCentVySp3", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvy3 recentering"}; + Configurable confRecentereCentVzSp3{"confRecentereCentVzSp3", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvz3 recentering"}; + Configurable confRecentereCentVxSp4{"confRecentereCentVxSp4", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvx4 recentering"}; + Configurable confRecentereCentVySp4{"confRecentereCentVySp4", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvy4 recentering"}; + Configurable confRecentereCentVzSp4{"confRecentereCentVzSp4", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvz4 recentering"}; + Configurable confRecentereCentVxSp5{"confRecentereCentVxSp5", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvx5 recentering"}; + Configurable confRecentereCentVySp5{"confRecentereCentVySp5", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvy5 recentering"}; + Configurable confRecentereCentVzSp5{"confRecentereCentVzSp5", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvz5 recentering"}; + Configurable confRecentereCentVxSp6{"confRecentereCentVxSp6", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvx6 recentering"}; + Configurable confRecentereCentVySp6{"confRecentereCentVySp6", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvy6 recentering"}; + Configurable confRecentereCentVzSp6{"confRecentereCentVzSp6", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "Sparse or THn path for centvz6 recentering"}; + Configurable confRecentereVzTimeSp{"confRecentereVzTimeSp", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "TH3F path for (Vz,time) recentering, cycle 1"}; + Configurable confRecentereVzTimeSp2{"confRecentereVzTimeSp2", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "TH3F path for (Vz,time) recentering, cycle 2"}; + Configurable confRecentereVzTimeSp3{"confRecentereVzTimeSp3", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "TH3F path for (Vz,time) recentering, cycle 3"}; + Configurable confRecentereVzTimeSp4{"confRecentereVzTimeSp4", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "TH3F path for (Vz,time) recentering, cycle 4"}; + Configurable confRecentereVzTimeSp5{"confRecentereVzTimeSp5", "Users/p/prottay/My/Object/Testingwithsparse/NewPbPbpass4_17092024/recenter", "TH3F path for (Vz,time) recentering, cycle 5"}; + + Configurable useWidthEqualization{ + "useWidthEqualization", + false, + "Apply Q-vector width equalization"}; + + Configurable confWidthSigma{ + "confWidthSigma", + "Users/p/prottay/My/Object/ZDC/widthEq", + "TH2F path for Q-vector width equalization"}; + + Configurable confWidthMean{ + "confWidthMean", + "Users/p/prottay/My/Object/ZDC/widthEq", + "TH2F path for Q-vector mean equalization"}; + + } confignewpro; struct : ConfigurableGroup { Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; @@ -217,6 +290,7 @@ struct spvector { AxisSpec vxfineAxis = {configbins.VxfineNbins, configbins.lfinebinVx, configbins.hfinebinVx, "vxfine"}; AxisSpec vyfineAxis = {configbins.VyfineNbins, configbins.lfinebinVy, configbins.hfinebinVy, "vyfine"}; AxisSpec centfineAxis = {configbins.CentfineNbins, configbins.lfinebinCent, configbins.hfinebinCent, "V0M (%) fine"}; + AxisSpec timefineAxis = {configbins.TimefineNbins, configbins.lfinebinTime, configbins.hfinebinTime, "timefine"}; AxisSpec shiftAxis = {10, 0, 10, "shift"}; AxisSpec basisAxis = {2, 0, 2, "basis"}; AxisSpec VxyAxis = {2, 0, 2, "Vxy"}; @@ -255,6 +329,155 @@ struct spvector { histos.add("hvzQyZDCA", "hvzQyZDCA", kTH2F, {{vzfineAxis}, {qxZDCAxis}}); histos.add("hvzQxZDCC", "hvzQxZDCC", kTH2F, {{vzfineAxis}, {qxZDCAxis}}); histos.add("hvzQyZDCC", "hvzQyZDCC", kTH2F, {{vzfineAxis}, {qxZDCAxis}}); + + histos.add("htimeQxZDCA", "htimeQxZDCA", kTH2F, {{timefineAxis}, {qxZDCAxis}}); + histos.add("htimeQyZDCA", "htimeQyZDCA", kTH2F, {{timefineAxis}, {qxZDCAxis}}); + histos.add("htimeQxZDCC", "htimeQxZDCC", kTH2F, {{timefineAxis}, {qxZDCAxis}}); + histos.add("htimeQyZDCC", "htimeQyZDCC", kTH2F, {{timefineAxis}, {qxZDCAxis}}); + + histos.add("pQxZDCAvsCentVzAfter", + " after recentering;" + "centrality (%);V_{z} (cm);", + kTProfile2D, + {centfineAxis, vzfineAxis}); + + histos.add("pQyZDCAvsCentVzAfter", + " after recentering;" + "centrality (%);V_{z} (cm);", + kTProfile2D, + {centfineAxis, vzfineAxis}); + + histos.add("pQxZDCCvsCentVzAfter", + " after recentering;" + "centrality (%);V_{z} (cm);", + kTProfile2D, + {centfineAxis, vzfineAxis}); + + histos.add("pQyZDCCvsCentVzAfter", + " after recentering;" + "centrality (%);V_{z} (cm);", + kTProfile2D, + {centfineAxis, vzfineAxis}); + + histos.add("pQxZDCAvsCentVyAfter", + " after recentering;" + "centrality (%);V_{y} (cm);", + kTProfile2D, + {centfineAxis, vyfineAxis}); + + histos.add("pQyZDCAvsCentVyAfter", + " after recentering;" + "centrality (%);V_{y} (cm);", + kTProfile2D, + {centfineAxis, vyfineAxis}); + + histos.add("pQxZDCCvsCentVyAfter", + " after recentering;" + "centrality (%);V_{y} (cm);", + kTProfile2D, + {centfineAxis, vyfineAxis}); + + histos.add("pQyZDCCvsCentVyAfter", + " after recentering;" + "centrality (%);V_{y} (cm);", + kTProfile2D, + {centfineAxis, vyfineAxis}); + + histos.add("pQxZDCAvsCentVxAfter", + " after recentering;" + "centrality (%);V_{x} (cm);", + kTProfile2D, + {centfineAxis, vxfineAxis}); + + histos.add("pQyZDCAvsCentVxAfter", + " after recentering;" + "centrality (%);V_{x} (cm);", + kTProfile2D, + {centfineAxis, vxfineAxis}); + + histos.add("pQxZDCCvsCentVxAfter", + " after recentering;" + "centrality (%);V_{x} (cm);", + kTProfile2D, + {centfineAxis, vxfineAxis}); + + histos.add("pQyZDCCvsCentVxAfter", + " after recentering;" + "centrality (%);V_{x} (cm);", + kTProfile2D, + {centfineAxis, vxfineAxis}); + + histos.add("pQxZDCAvsVyVxAfter", + " after recentering;" + "V_{y};V_{x} (cm);", + kTProfile2D, + {vyfineAxis, vxfineAxis}); + + histos.add("pQyZDCAvsVyVxAfter", + " after recentering;" + "V_{y};V_{x} (cm);", + kTProfile2D, + {vyfineAxis, vxfineAxis}); + + histos.add("pQxZDCCvsVyVxAfter", + " after recentering;" + "V_{y};V_{x} (cm);", + kTProfile2D, + {vyfineAxis, vxfineAxis}); + + histos.add("pQyZDCCvsVyVxAfter", + " after recentering;" + "V_{y};V_{x} (cm);", + kTProfile2D, + {vyfineAxis, vxfineAxis}); + + histos.add("pQxZDCAvsCentTimeAfter", + " after recentering;" + "centrality (%);Time (min);", + kTProfile2D, + {centfineAxis, timefineAxis}); + + histos.add("pQyZDCAvsCentTimeAfter", + " after recentering;" + "centrality (%);Time (min);", + kTProfile2D, + {centfineAxis, timefineAxis}); + + histos.add("pQxZDCCvsCentTimeAfter", + " after recentering;" + "centrality (%);Time (min);", + kTProfile2D, + {centfineAxis, timefineAxis}); + + histos.add("pQyZDCCvsCentTimeAfter", + " after recentering;" + "centrality (%);Time (min);", + kTProfile2D, + {centfineAxis, timefineAxis}); + + histos.add("pQxZDCAvsVzTimeAfter", + " after recentering;" + "vz (cm);Time (min);", + kTProfile2D, + {vzfineAxis, timefineAxis}); + + histos.add("pQyZDCAvsVzTimeAfter", + " after recentering;" + "vz (cm);Time (min);", + kTProfile2D, + {vzfineAxis, timefineAxis}); + + histos.add("pQxZDCCvsVzTimeAfter", + " after recentering;" + "vz (cm);Time (min);", + kTProfile2D, + {vzfineAxis, timefineAxis}); + + histos.add("pQyZDCCvsVzTimeAfter", + " after recentering;" + "vz (cm);Time (min);", + kTProfile2D, + {vzfineAxis, timefineAxis}); } histos.add("PsiZDCC", "PsiZDCC", kTH2F, {centfineAxis, phiAxis}); @@ -266,22 +489,12 @@ struct spvector { histos.add("hpCosPsiAPsiC", "hpCosPsiAPsiC", kTProfile, {centfineAxis}); histos.add("hpSinPsiAPsiC", "hpSinPsiAPsiC", kTProfile, {centfineAxis}); histos.add("AvgVxy", "AvgVxy", kTProfile, {VxyAxis}); + histos.add("hpQxZDCAvstime", "hpQxZDCAvstime", kTProfile, {{timefineAxis}}); + histos.add("hpQxZDCCvstime", "hpQxZDCCvstime", kTProfile, {{timefineAxis}}); + histos.add("hpQyZDCAvstime", "hpQyZDCAvstime", kTProfile, {{timefineAxis}}); + histos.add("hpQyZDCCvstime", "hpQyZDCCvstime", kTProfile, {{timefineAxis}}); - // Event selection cut additional - Alex - /* - fMultPVCutLow = new TF1("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x - 2.5*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); - fMultPVCutLow->SetParameters(2834.66, -87.0127, 0.915126, -0.00330136, 332.513, -12.3476, 0.251663, -0.00272819, 1.12242e-05); - fMultPVCutHigh = new TF1("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x + 2.5*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); - fMultPVCutHigh->SetParameters(2834.66, -87.0127, 0.915126, -0.00330136, 332.513, -12.3476, 0.251663, -0.00272819, 1.12242e-05); - fMultCutLow = new TF1("fMultCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x - 2.5*([4]+[5]*x)", 0, 100); - fMultCutLow->SetParameters(1893.94, -53.86, 0.502913, -0.0015122, 109.625, -1.19253); - fMultCutHigh = new TF1("fMultCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x + 3.*([4]+[5]*x)", 0, 100); - fMultCutHigh->SetParameters(1893.94, -53.86, 0.502913, -0.0015122, 109.625, -1.19253); - fMultMultPVCut = new TF1("fMultMultPVCut", "[0]+[1]*x+[2]*x*x", 0, 5000); - fMultMultPVCut->SetParameters(-0.1, 0.785, -4.7e-05); - */ ccdb->setURL(cfgCcdbParam.cfgURL); - ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); @@ -289,17 +502,193 @@ struct spvector { int currentRunNumber = -999; int lastRunNumber = -999; - TH2D* gainprofile; - TProfile* gainprofilevxy; + TH2D* gainprofile = nullptr; + TProfile* gainprofilevxy = nullptr; std::array hrecentereSpA; // Array of 6 histograms std::array hrecenterecentSpA; // Array of 5 histograms std::array hrecenterevxSpA; // Array of 5 histograms std::array hrecenterevySpA; // Array of 5 histograms std::array hrecenterevzSpA; // Array of 5 histograms - TProfile3D* shiftprofileA; - TProfile3D* shiftprofileC; + TProfile3D* shiftprofileA = nullptr; + TProfile3D* shiftprofileC = nullptr; + TH2F* hrecentereTimeSp1 = nullptr; + TH2F* hrecentereTimeSp2 = nullptr; + TH2F* hrecentereTimeSp3 = nullptr; + // One payload for each of the five iterations. + std::array hrecenterecentvxSpA{}; + std::array hrecenterecentvySpA{}; + std::array hrecenterecentvzSpA{}; + std::array hrecenterevztimeSpA{}; + TH2F* hWidthMean = nullptr; + TH2F* hWidthSigma = nullptr; + + bool CorrectWidthEqualization(TH2F* hMean, + TH2F* hWidth, + auto centrality, + auto& qxZDCA, + auto& qyZDCA, + auto& qxZDCC, + auto& qyZDCC) + { + + if (!hMean || !hWidth) { + return false; + } + + const double meanQxA = hMean->GetBinContent( + hMean->FindBin(centrality + 0.00001, 0.5)); + + const double meanQyA = hMean->GetBinContent( + hMean->FindBin(centrality + 0.00001, 1.5)); + + const double meanQxC = hMean->GetBinContent( + hMean->FindBin(centrality + 0.00001, 2.5)); + + const double meanQyC = hMean->GetBinContent( + hMean->FindBin(centrality + 0.00001, 3.5)); + + const double sigmaQxA = hWidth->GetBinContent( + hWidth->FindBin(centrality + 0.00001, 0.5)); + + const double sigmaQyA = hWidth->GetBinContent( + hWidth->FindBin(centrality + 0.00001, 1.5)); + + const double sigmaQxC = hWidth->GetBinContent( + hWidth->FindBin(centrality + 0.00001, 2.5)); + + const double sigmaQyC = hWidth->GetBinContent( + hWidth->FindBin(centrality + 0.00001, 3.5)); + + if (sigmaQxA <= 0.0 || sigmaQyA <= 0.0 || + sigmaQxC <= 0.0 || sigmaQyC <= 0.0) { + return false; + } + + qxZDCA = (qxZDCA - meanQxA) / sigmaQxA; + qyZDCA = (qyZDCA - meanQyA) / sigmaQyA; + + qxZDCC = (qxZDCC - meanQxC) / sigmaQxC; + qyZDCC = (qyZDCC - meanQyC) / sigmaQyC; + + return true; + } + + bool CorrectfineCent(TH2F* hrecenterecentSp, + auto centrality, + auto& qxZDCA, + auto& qyZDCA, + auto& qxZDCC, + auto& qyZDCC) + { + if (!hrecenterecentSp) { + return false; + } + + const double meanxAcent = hrecenterecentSp->GetBinContent( + hrecenterecentSp->FindBin(centrality + 0.00001, 0.5)); + const double meanyAcent = hrecenterecentSp->GetBinContent( + hrecenterecentSp->FindBin(centrality + 0.00001, 1.5)); + const double meanxCcent = hrecenterecentSp->GetBinContent( + hrecenterecentSp->FindBin(centrality + 0.00001, 2.5)); + const double meanyCcent = hrecenterecentSp->GetBinContent( + hrecenterecentSp->FindBin(centrality + 0.00001, 3.5)); + + qxZDCA -= meanxAcent; + qyZDCA -= meanyAcent; + qxZDCC -= meanxCcent; + qyZDCC -= meanyCcent; + + return true; + } - Bool_t Correctcoarse(const THnF* hrecentereSp, auto centrality, auto vx, auto vy, auto vz, auto& qxZDCA, auto& qyZDCA, auto& qxZDCC, auto& qyZDCC) + bool CorrectfineCentVx(TH3F* hrecenterecentvxSp, + auto centrality, + auto vx, + auto& qxZDCA, + auto& qyZDCA, + auto& qxZDCC, + auto& qyZDCC) + { + if (!hrecenterecentvxSp) { + return false; + } + + const double meanxAcentvx = hrecenterecentvxSp->GetBinContent( + hrecenterecentvxSp->FindBin(centrality + 0.00001, vx + 0.00001, 0.5)); + const double meanyAcentvx = hrecenterecentvxSp->GetBinContent( + hrecenterecentvxSp->FindBin(centrality + 0.00001, vx + 0.00001, 1.5)); + const double meanxCcentvx = hrecenterecentvxSp->GetBinContent( + hrecenterecentvxSp->FindBin(centrality + 0.00001, vx + 0.00001, 2.5)); + const double meanyCcentvx = hrecenterecentvxSp->GetBinContent( + hrecenterecentvxSp->FindBin(centrality + 0.00001, vx + 0.00001, 3.5)); + + qxZDCA -= meanxAcentvx; + qyZDCA -= meanyAcentvx; + qxZDCC -= meanxCcentvx; + qyZDCC -= meanyCcentvx; + + return true; + } + + bool CorrectfineCentVy(TH3F* hrecenterecentvySp, + auto centrality, + auto vy, + auto& qxZDCA, + auto& qyZDCA, + auto& qxZDCC, + auto& qyZDCC) + { + if (!hrecenterecentvySp) { + return false; + } + + const double meanxAcentvy = hrecenterecentvySp->GetBinContent( + hrecenterecentvySp->FindBin(centrality + 0.00001, vy + 0.00001, 0.5)); + const double meanyAcentvy = hrecenterecentvySp->GetBinContent( + hrecenterecentvySp->FindBin(centrality + 0.00001, vy + 0.00001, 1.5)); + const double meanxCcentvy = hrecenterecentvySp->GetBinContent( + hrecenterecentvySp->FindBin(centrality + 0.00001, vy + 0.00001, 2.5)); + const double meanyCcentvy = hrecenterecentvySp->GetBinContent( + hrecenterecentvySp->FindBin(centrality + 0.00001, vy + 0.00001, 3.5)); + + qxZDCA -= meanxAcentvy; + qyZDCA -= meanyAcentvy; + qxZDCC -= meanxCcentvy; + qyZDCC -= meanyCcentvy; + + return true; + } + + bool CorrectfineCentVz(TH3F* hrecenterecentvzSp, + auto centrality, + auto vz, + auto& qxZDCA, + auto& qyZDCA, + auto& qxZDCC, + auto& qyZDCC) + { + if (!hrecenterecentvzSp) { + return false; + } + + const double meanxAcentvz = hrecenterecentvzSp->GetBinContent( + hrecenterecentvzSp->FindBin(centrality + 0.00001, vz + 0.00001, 0.5)); + const double meanyAcentvz = hrecenterecentvzSp->GetBinContent( + hrecenterecentvzSp->FindBin(centrality + 0.00001, vz + 0.00001, 1.5)); + const double meanxCcentvz = hrecenterecentvzSp->GetBinContent( + hrecenterecentvzSp->FindBin(centrality + 0.00001, vz + 0.00001, 2.5)); + const double meanyCcentvz = hrecenterecentvzSp->GetBinContent( + hrecenterecentvzSp->FindBin(centrality + 0.00001, vz + 0.00001, 3.5)); + + qxZDCA -= meanxAcentvz; + qyZDCA -= meanyAcentvz; + qxZDCC -= meanxCcentvz; + qyZDCC -= meanyCcentvz; + + return true; + } + + bool Correctcoarse(const THnF* hrecentereSp, auto centrality, auto vx, auto vy, auto vz, auto& qxZDCA, auto& qyZDCA, auto& qxZDCC, auto& qyZDCC) { int binCoords[5]; @@ -343,7 +732,7 @@ struct spvector { return kTRUE; } - Bool_t Correctfine(TH2F* hrecenterecentSp, TH2F* hrecenterevxSp, TH2F* hrecenterevySp, TH2F* hrecenterevzSp, auto centrality, auto vx, auto vy, auto vz, auto& qxZDCA, auto& qyZDCA, auto& qxZDCC, auto& qyZDCC) + bool Correctfine(TH2F* hrecenterecentSp, TH2F* hrecenterevxSp, TH2F* hrecenterevySp, TH2F* hrecenterevzSp, auto centrality, auto vx, auto vy, auto vz, auto& qxZDCA, auto& qyZDCA, auto& qxZDCC, auto& qyZDCC) { if (!hrecenterecentSp || !hrecenterevxSp || !hrecenterevySp || !hrecenterevzSp) { @@ -379,19 +768,74 @@ struct spvector { return kTRUE; } - using MyCollisions = soa::Join; - using AllTrackCandidates = soa::Join; - Preslice zdcPerCollision = aod::collision::bcId; - - void process(MyCollisions::iterator const& collision, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0s*/, BCsRun3 const& bcs, aod::Zdcs const&, AllTrackCandidates const& tracks) + bool Correcttime(TH2F* hrecentereTimeSp, + auto timeMin, + auto& qxZDCA, + auto& qyZDCA, + auto& qxZDCC, + auto& qyZDCC) { + if (!hrecentereTimeSp) { + return false; + } - if (usemem) { - for (const auto& track : tracks) { - histos.fill(HIST("htpcnsigmapi"), track.tpcNSigmaPi()); - } + double meanxA = hrecentereTimeSp->GetBinContent(hrecentereTimeSp->FindBin(timeMin + 1.e-7, 0.5)); + double meanyA = hrecentereTimeSp->GetBinContent(hrecentereTimeSp->FindBin(timeMin + 1.e-7, 1.5)); + double meanxC = hrecentereTimeSp->GetBinContent(hrecentereTimeSp->FindBin(timeMin + 1.e-7, 2.5)); + double meanyC = hrecentereTimeSp->GetBinContent(hrecentereTimeSp->FindBin(timeMin + 1.e-7, 3.5)); + + qxZDCA -= meanxA; + qyZDCA -= meanyA; + qxZDCC -= meanxC; + qyZDCC -= meanyC; + + return true; + } + + bool CorrectfineVzTime(TH3F* hrecenterevstimeSp, + auto vz, + auto timeMin, + auto& qxZDCA, + auto& qyZDCA, + auto& qxZDCC, + auto& qyZDCC) + { + if (!hrecenterevstimeSp) { + return false; } + // Payload axes: + // X = Vz + // Y = time from run start + // Z = component + // component channels: + // 0.5 = Qx ZNA + // 1.5 = Qy ZNA + // 2.5 = Qx ZNC + // 3.5 = Qy ZNC + const double meanxAvstime = hrecenterevstimeSp->GetBinContent( + hrecenterevstimeSp->FindBin(vz + 0.00001, timeMin + 0.00001, 0.5)); + const double meanyAvstime = hrecenterevstimeSp->GetBinContent( + hrecenterevstimeSp->FindBin(vz + 0.00001, timeMin + 0.00001, 1.5)); + const double meanxCvstime = hrecenterevstimeSp->GetBinContent( + hrecenterevstimeSp->FindBin(vz + 0.00001, timeMin + 0.00001, 2.5)); + const double meanyCvstime = hrecenterevstimeSp->GetBinContent( + hrecenterevstimeSp->FindBin(vz + 0.00001, timeMin + 0.00001, 3.5)); + + qxZDCA -= meanxAvstime; + qyZDCA -= meanyAvstime; + qxZDCC -= meanxCvstime; + qyZDCC -= meanyCvstime; + + return true; + } + + using BCsRun3 = soa::Join; + using MyCollisions = soa::Join; + Preslice zdcPerCollision = aod::collision::bcId; + + void process(MyCollisions::iterator const& collision, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0s*/, BCsRun3 const& bcs, aod::Zdcs const&) + { histos.fill(HIST("hEvtSelInfo"), 0.5); auto centrality = collision.centFT0C(); bool triggerevent = false; @@ -424,6 +868,18 @@ struct spvector { histos.fill(HIST("hEvtSelInfo"), 1.5); + const uint64_t timestampzdc = bc.timestamp(); // in milliseconds + + // Convert timestamp to hours from run start (approximate) + // Store first timestamp of run to calculate relative time + static std::unordered_map runStartTime; + if (runStartTime.find(currentRunNumber) == runStartTime.end()) { + // runStartTime[currentRunNumber] = timestampzdc; + runStartTime.try_emplace(currentRunNumber, timestampzdc); + } + + double timeMin = (timestampzdc - runStartTime[currentRunNumber]) / 60000.0; // ms -> minutes + auto zdc = bc.zdc(); auto zncEnergy = zdc.energySectorZNC(); auto znaEnergy = zdc.energySectorZNA(); @@ -578,116 +1034,465 @@ struct spvector { vy = vy - gainprofilevxy->GetBinContent(2); } - Bool_t res = 0; - Bool_t resfine = 0; - Int_t check = 1; + bool res = false; + bool resfine = false; + int check = 1; - if (coarse1) { - if (useRecentereSp && (currentRunNumber != lastRunNumber)) { - hrecentereSpA[0] = ccdb->getForTimeStamp(ConfRecentereSp.value, bc.timestamp()); + if (useoldrecentering) { + if (coarse1) { + if (useRecentereSp && (currentRunNumber != lastRunNumber)) { + hrecentereSpA[0] = ccdb->getForTimeStamp(ConfRecentereSp.value, bc.timestamp()); + } + res = Correctcoarse(hrecentereSpA[0], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - res = Correctcoarse(hrecentereSpA[0], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (fine1) { + if (fine1) { - if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { - hrecenterecentSpA[0] = ccdb->getForTimeStamp(ConfRecenterecentSp.value, bc.timestamp()); - hrecenterevxSpA[0] = ccdb->getForTimeStamp(ConfRecenterevxSp.value, bc.timestamp()); - hrecenterevySpA[0] = ccdb->getForTimeStamp(ConfRecenterevySp.value, bc.timestamp()); - hrecenterevzSpA[0] = ccdb->getForTimeStamp(ConfRecenterevzSp.value, bc.timestamp()); + if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { + hrecenterecentSpA[0] = ccdb->getForTimeStamp(ConfRecenterecentSp.value, bc.timestamp()); + hrecenterevxSpA[0] = ccdb->getForTimeStamp(ConfRecenterevxSp.value, bc.timestamp()); + hrecenterevySpA[0] = ccdb->getForTimeStamp(ConfRecenterevySp.value, bc.timestamp()); + hrecenterevzSpA[0] = ccdb->getForTimeStamp(ConfRecenterevzSp.value, bc.timestamp()); + } + resfine = Correctfine(hrecenterecentSpA[0], hrecenterevxSpA[0], hrecenterevySpA[0], hrecenterevzSpA[0], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - resfine = Correctfine(hrecenterecentSpA[0], hrecenterevxSpA[0], hrecenterevySpA[0], hrecenterevzSpA[0], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (coarse2) { - if (useRecentereSp && (currentRunNumber != lastRunNumber)) { - hrecentereSpA[1] = ccdb->getForTimeStamp(ConfRecentereSp2.value, bc.timestamp()); + if (finetime1 && (currentRunNumber != lastRunNumber)) { + hrecentereTimeSp1 = ccdb->getForTimeStamp(confRecentereTimeSp1.value, bc.timestamp()); + } + bool restime = false; + if (useTimeRecentering) { + restime = Correcttime(hrecentereTimeSp1, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - res = Correctcoarse(hrecentereSpA[1], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (fine2) { - if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { - hrecenterecentSpA[1] = ccdb->getForTimeStamp(ConfRecenterecentSp2.value, bc.timestamp()); - hrecenterevxSpA[1] = ccdb->getForTimeStamp(ConfRecenterevxSp2.value, bc.timestamp()); - hrecenterevySpA[1] = ccdb->getForTimeStamp(ConfRecenterevySp2.value, bc.timestamp()); - hrecenterevzSpA[1] = ccdb->getForTimeStamp(ConfRecenterevzSp2.value, bc.timestamp()); + if (coarse2) { + if (useRecentereSp && (currentRunNumber != lastRunNumber)) { + hrecentereSpA[1] = ccdb->getForTimeStamp(ConfRecentereSp2.value, bc.timestamp()); + } + res = Correctcoarse(hrecentereSpA[1], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - resfine = Correctfine(hrecenterecentSpA[1], hrecenterevxSpA[1], hrecenterevySpA[1], hrecenterevzSpA[1], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (coarse3) { - if (useRecentereSp && (currentRunNumber != lastRunNumber)) { - hrecentereSpA[2] = ccdb->getForTimeStamp(ConfRecentereSp3.value, bc.timestamp()); + if (fine2) { + if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { + hrecenterecentSpA[1] = ccdb->getForTimeStamp(ConfRecenterecentSp2.value, bc.timestamp()); + hrecenterevxSpA[1] = ccdb->getForTimeStamp(ConfRecenterevxSp2.value, bc.timestamp()); + hrecenterevySpA[1] = ccdb->getForTimeStamp(ConfRecenterevySp2.value, bc.timestamp()); + hrecenterevzSpA[1] = ccdb->getForTimeStamp(ConfRecenterevzSp2.value, bc.timestamp()); + } + resfine = Correctfine(hrecenterecentSpA[1], hrecenterevxSpA[1], hrecenterevySpA[1], hrecenterevzSpA[1], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - res = Correctcoarse(hrecentereSpA[2], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (fine3) { - if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { - hrecenterecentSpA[2] = ccdb->getForTimeStamp(ConfRecenterecentSp3.value, bc.timestamp()); - hrecenterevxSpA[2] = ccdb->getForTimeStamp(ConfRecenterevxSp3.value, bc.timestamp()); - hrecenterevySpA[2] = ccdb->getForTimeStamp(ConfRecenterevySp3.value, bc.timestamp()); - hrecenterevzSpA[2] = ccdb->getForTimeStamp(ConfRecenterevzSp3.value, bc.timestamp()); + if (finetime2 && (currentRunNumber != lastRunNumber)) { + hrecentereTimeSp2 = ccdb->getForTimeStamp(confRecentereTimeSp2.value, bc.timestamp()); + } + if (useTimeRecentering) { + restime = Correcttime(hrecentereTimeSp2, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - resfine = Correctfine(hrecenterecentSpA[2], hrecenterevxSpA[2], hrecenterevySpA[2], hrecenterevzSpA[2], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (coarse4) { - if (useRecentereSp && (currentRunNumber != lastRunNumber)) { - hrecentereSpA[3] = ccdb->getForTimeStamp(ConfRecentereSp4.value, bc.timestamp()); + if (coarse3) { + if (useRecentereSp && (currentRunNumber != lastRunNumber)) { + hrecentereSpA[2] = ccdb->getForTimeStamp(ConfRecentereSp3.value, bc.timestamp()); + } + res = Correctcoarse(hrecentereSpA[2], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - res = Correctcoarse(hrecentereSpA[3], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (fine4) { - if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { - hrecenterecentSpA[3] = ccdb->getForTimeStamp(ConfRecenterecentSp4.value, bc.timestamp()); - hrecenterevxSpA[3] = ccdb->getForTimeStamp(ConfRecenterevxSp4.value, bc.timestamp()); - hrecenterevySpA[3] = ccdb->getForTimeStamp(ConfRecenterevySp4.value, bc.timestamp()); - hrecenterevzSpA[3] = ccdb->getForTimeStamp(ConfRecenterevzSp4.value, bc.timestamp()); + if (fine3) { + if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { + hrecenterecentSpA[2] = ccdb->getForTimeStamp(ConfRecenterecentSp3.value, bc.timestamp()); + hrecenterevxSpA[2] = ccdb->getForTimeStamp(ConfRecenterevxSp3.value, bc.timestamp()); + hrecenterevySpA[2] = ccdb->getForTimeStamp(ConfRecenterevySp3.value, bc.timestamp()); + hrecenterevzSpA[2] = ccdb->getForTimeStamp(ConfRecenterevzSp3.value, bc.timestamp()); + } + resfine = Correctfine(hrecenterecentSpA[2], hrecenterevxSpA[2], hrecenterevySpA[2], hrecenterevzSpA[2], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - resfine = Correctfine(hrecenterecentSpA[3], hrecenterevxSpA[3], hrecenterevySpA[3], hrecenterevzSpA[3], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (coarse5) { - if (useRecentereSp && (currentRunNumber != lastRunNumber)) { - hrecentereSpA[4] = ccdb->getForTimeStamp(ConfRecentereSp5.value, bc.timestamp()); + if (finetime3 && (currentRunNumber != lastRunNumber)) { + hrecentereTimeSp3 = ccdb->getForTimeStamp(confRecentereTimeSp3.value, bc.timestamp()); + } + if (useTimeRecentering) { + restime = Correcttime(hrecentereTimeSp3, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - res = Correctcoarse(hrecentereSpA[4], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (fine5) { - if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { - hrecenterecentSpA[4] = ccdb->getForTimeStamp(ConfRecenterecentSp5.value, bc.timestamp()); - hrecenterevxSpA[4] = ccdb->getForTimeStamp(ConfRecenterevxSp5.value, bc.timestamp()); - hrecenterevySpA[4] = ccdb->getForTimeStamp(ConfRecenterevySp5.value, bc.timestamp()); - hrecenterevzSpA[4] = ccdb->getForTimeStamp(ConfRecenterevzSp5.value, bc.timestamp()); + if (coarse4) { + if (useRecentereSp && (currentRunNumber != lastRunNumber)) { + hrecentereSpA[3] = ccdb->getForTimeStamp(ConfRecentereSp4.value, bc.timestamp()); + } + res = Correctcoarse(hrecentereSpA[3], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - resfine = Correctfine(hrecenterecentSpA[4], hrecenterevxSpA[4], hrecenterevySpA[4], hrecenterevzSpA[4], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (coarse6) { - if (useRecentereSp && (currentRunNumber != lastRunNumber)) { - hrecentereSpA[5] = ccdb->getForTimeStamp(ConfRecentereSp6.value, bc.timestamp()); + if (fine4) { + if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { + hrecenterecentSpA[3] = ccdb->getForTimeStamp(ConfRecenterecentSp4.value, bc.timestamp()); + hrecenterevxSpA[3] = ccdb->getForTimeStamp(ConfRecenterevxSp4.value, bc.timestamp()); + hrecenterevySpA[3] = ccdb->getForTimeStamp(ConfRecenterevySp4.value, bc.timestamp()); + hrecenterevzSpA[3] = ccdb->getForTimeStamp(ConfRecenterevzSp4.value, bc.timestamp()); + } + resfine = Correctfine(hrecenterecentSpA[3], hrecenterevxSpA[3], hrecenterevySpA[3], hrecenterevzSpA[3], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); } - res = Correctcoarse(hrecentereSpA[5], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (fine6) { - if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { - hrecenterecentSpA[5] = ccdb->getForTimeStamp(ConfRecenterecentSp6.value, bc.timestamp()); - hrecenterevxSpA[5] = ccdb->getForTimeStamp(ConfRecenterevxSp6.value, bc.timestamp()); - hrecenterevySpA[5] = ccdb->getForTimeStamp(ConfRecenterevySp6.value, bc.timestamp()); - hrecenterevzSpA[5] = ccdb->getForTimeStamp(ConfRecenterevzSp6.value, bc.timestamp()); + if (coarse5) { + if (useRecentereSp && (currentRunNumber != lastRunNumber)) { + hrecentereSpA[4] = ccdb->getForTimeStamp(ConfRecentereSp5.value, bc.timestamp()); + } + res = Correctcoarse(hrecentereSpA[4], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); + } + + if (fine5) { + if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { + hrecenterecentSpA[4] = ccdb->getForTimeStamp(ConfRecenterecentSp5.value, bc.timestamp()); + hrecenterevxSpA[4] = ccdb->getForTimeStamp(ConfRecenterevxSp5.value, bc.timestamp()); + hrecenterevySpA[4] = ccdb->getForTimeStamp(ConfRecenterevySp5.value, bc.timestamp()); + hrecenterevzSpA[4] = ccdb->getForTimeStamp(ConfRecenterevzSp5.value, bc.timestamp()); + } + resfine = Correctfine(hrecenterecentSpA[4], hrecenterevxSpA[4], hrecenterevySpA[4], hrecenterevzSpA[4], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); + } + + if (coarse6) { + if (useRecentereSp && (currentRunNumber != lastRunNumber)) { + hrecentereSpA[5] = ccdb->getForTimeStamp(ConfRecentereSp6.value, bc.timestamp()); + } + res = Correctcoarse(hrecentereSpA[5], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); + } + + if (fine6) { + if (useRecenterefineSp && (currentRunNumber != lastRunNumber)) { + hrecenterecentSpA[5] = ccdb->getForTimeStamp(ConfRecenterecentSp6.value, bc.timestamp()); + hrecenterevxSpA[5] = ccdb->getForTimeStamp(ConfRecenterevxSp6.value, bc.timestamp()); + hrecenterevySpA[5] = ccdb->getForTimeStamp(ConfRecenterevySp6.value, bc.timestamp()); + hrecenterevzSpA[5] = ccdb->getForTimeStamp(ConfRecenterevzSp6.value, bc.timestamp()); + } + resfine = Correctfine(hrecenterecentSpA[5], hrecenterevxSpA[5], hrecenterevySpA[5], hrecenterevzSpA[5], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); + } + + if (res == 0 && resfine == 0 && check == 0) { + LOG(info) << "Histograms are null"; + } + + if (useTimeRecentering && restime == 0 && check == 0) { + LOG(info) << "Histograms are null"; + } + } else { + + // Each stage must preserve this order: + // centrality -> (centrality,Vx) -> (centrality,Vy) -> (centrality,Vz). + // Keeping the switches separate allows the residual maps to be made + // in the same order in separate calibration passes. + + // -------------------- Stage 1 -------------------- + if (confignewpro.finecent1) { + if (confignewpro.useRecenterefinecentSp && + (currentRunNumber != lastRunNumber || !hrecenterecentSpA[0])) { + hrecenterecentSpA[0] = ccdb->getForTimeStamp(confignewpro.confRecentereCentSp.value, bc.timestamp()); + } + if (!CorrectfineCent(hrecenterecentSpA[0], centrality, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine centrality recentering, stage 1"); + } + } + + if (confignewpro.finecentvx1) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvxSpA[0])) { + hrecenterecentvxSpA[0] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVxSp.value, bc.timestamp()); + } + if (!CorrectfineCentVx(hrecenterecentvxSpA[0], centrality, vx, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vx) recentering, stage 1"); + } + } + + if (confignewpro.finecentvy1) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvySpA[0])) { + hrecenterecentvySpA[0] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVySp.value, bc.timestamp()); + } + if (!CorrectfineCentVy(hrecenterecentvySpA[0], centrality, vy, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vy) recentering, stage 1"); + } + } + + if (confignewpro.finecentvz1) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvzSpA[0])) { + hrecenterecentvzSpA[0] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVzSp.value, bc.timestamp()); + } + if (!CorrectfineCentVz(hrecenterecentvzSpA[0], centrality, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vz) recentering, stage 1"); + } } - resfine = Correctfine(hrecenterecentSpA[5], hrecenterevxSpA[5], hrecenterevySpA[5], hrecenterevzSpA[5], centrality, vx, vy, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC); - } - if (res == 0 && resfine == 0 && check == 0) { - LOG(info) << "Histograms are null"; + if (confignewpro.finevztime1) { + if (confignewpro.useRecenterefinevztimeSp && + (currentRunNumber != lastRunNumber || !hrecenterevztimeSpA[0])) { + hrecenterevztimeSpA[0] = ccdb->getForTimeStamp(confignewpro.confRecentereVzTimeSp.value, bc.timestamp()); + } + if (!CorrectfineVzTime(hrecenterevztimeSpA[0], vz, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (Vz, time) recentering, stage 1"); + } + } + + // -------------------- Stage 2 -------------------- + if (confignewpro.finecent2) { + if (confignewpro.useRecenterefinecentSp && + (currentRunNumber != lastRunNumber || !hrecenterecentSpA[1])) { + hrecenterecentSpA[1] = ccdb->getForTimeStamp(confignewpro.confRecentereCentSp2.value, bc.timestamp()); + } + if (!CorrectfineCent(hrecenterecentSpA[1], centrality, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine centrality recentering, stage 2"); + } + } + + if (confignewpro.finecentvx2) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvxSpA[1])) { + hrecenterecentvxSpA[1] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVxSp2.value, bc.timestamp()); + } + if (!CorrectfineCentVx(hrecenterecentvxSpA[1], centrality, vx, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vx) recentering, stage 2"); + } + } + + if (confignewpro.finecentvy2) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvySpA[1])) { + hrecenterecentvySpA[1] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVySp2.value, bc.timestamp()); + } + if (!CorrectfineCentVy(hrecenterecentvySpA[1], centrality, vy, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vy) recentering, stage 2"); + } + } + + if (confignewpro.finecentvz2) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvzSpA[1])) { + hrecenterecentvzSpA[1] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVzSp2.value, bc.timestamp()); + } + if (!CorrectfineCentVz(hrecenterecentvzSpA[1], centrality, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vz) recentering, stage 2"); + } + } + + if (confignewpro.finevztime2) { + if (confignewpro.useRecenterefinevztimeSp && + (currentRunNumber != lastRunNumber || !hrecenterevztimeSpA[1])) { + hrecenterevztimeSpA[1] = ccdb->getForTimeStamp(confignewpro.confRecentereVzTimeSp2.value, bc.timestamp()); + } + if (!CorrectfineVzTime(hrecenterevztimeSpA[1], vz, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (Vz, time) recentering, stage 2"); + } + } + + // -------------------- Stage 3 -------------------- + if (confignewpro.finecent3) { + if (confignewpro.useRecenterefinecentSp && + (currentRunNumber != lastRunNumber || !hrecenterecentSpA[2])) { + hrecenterecentSpA[2] = ccdb->getForTimeStamp(confignewpro.confRecentereCentSp3.value, bc.timestamp()); + } + if (!CorrectfineCent(hrecenterecentSpA[2], centrality, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine centrality recentering, stage 3"); + } + } + + if (confignewpro.finecentvx3) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvxSpA[2])) { + hrecenterecentvxSpA[2] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVxSp3.value, bc.timestamp()); + } + if (!CorrectfineCentVx(hrecenterecentvxSpA[2], centrality, vx, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vx) recentering, stage 3"); + } + } + + if (confignewpro.finecentvy3) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvySpA[2])) { + hrecenterecentvySpA[2] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVySp3.value, bc.timestamp()); + } + if (!CorrectfineCentVy(hrecenterecentvySpA[2], centrality, vy, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vy) recentering, stage 3"); + } + } + + if (confignewpro.finecentvz3) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvzSpA[2])) { + hrecenterecentvzSpA[2] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVzSp3.value, bc.timestamp()); + } + if (!CorrectfineCentVz(hrecenterecentvzSpA[2], centrality, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vz) recentering, stage 3"); + } + } + + if (confignewpro.finevztime3) { + if (confignewpro.useRecenterefinevztimeSp && + (currentRunNumber != lastRunNumber || !hrecenterevztimeSpA[2])) { + hrecenterevztimeSpA[2] = ccdb->getForTimeStamp(confignewpro.confRecentereVzTimeSp3.value, bc.timestamp()); + } + if (!CorrectfineVzTime(hrecenterevztimeSpA[2], vz, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (Vz, time) recentering, stage 3"); + } + } + + // -------------------- Stage 4 -------------------- + if (confignewpro.finecent4) { + if (confignewpro.useRecenterefinecentSp && + (currentRunNumber != lastRunNumber || !hrecenterecentSpA[3])) { + hrecenterecentSpA[3] = ccdb->getForTimeStamp(confignewpro.confRecentereCentSp4.value, bc.timestamp()); + } + if (!CorrectfineCent(hrecenterecentSpA[3], centrality, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine centrality recentering, stage 4"); + } + } + + if (confignewpro.finecentvx4) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvxSpA[3])) { + hrecenterecentvxSpA[3] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVxSp4.value, bc.timestamp()); + } + if (!CorrectfineCentVx(hrecenterecentvxSpA[3], centrality, vx, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vx) recentering, stage 4"); + } + } + + if (confignewpro.finecentvy4) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvySpA[3])) { + hrecenterecentvySpA[3] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVySp4.value, bc.timestamp()); + } + if (!CorrectfineCentVy(hrecenterecentvySpA[3], centrality, vy, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vy) recentering, stage 4"); + } + } + + if (confignewpro.finecentvz4) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvzSpA[3])) { + hrecenterecentvzSpA[3] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVzSp4.value, bc.timestamp()); + } + if (!CorrectfineCentVz(hrecenterecentvzSpA[3], centrality, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vz) recentering, stage 4"); + } + } + + if (confignewpro.finevztime4) { + if (confignewpro.useRecenterefinevztimeSp && + (currentRunNumber != lastRunNumber || !hrecenterevztimeSpA[3])) { + hrecenterevztimeSpA[3] = ccdb->getForTimeStamp(confignewpro.confRecentereVzTimeSp4.value, bc.timestamp()); + } + if (!CorrectfineVzTime(hrecenterevztimeSpA[3], vz, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (Vz, time) recentering, stage 4"); + } + } + + // -------------------- Stage 5 -------------------- + if (confignewpro.finecent5) { + if (confignewpro.useRecenterefinecentSp && + (currentRunNumber != lastRunNumber || !hrecenterecentSpA[4])) { + hrecenterecentSpA[4] = ccdb->getForTimeStamp(confignewpro.confRecentereCentSp5.value, bc.timestamp()); + } + if (!CorrectfineCent(hrecenterecentSpA[4], centrality, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine centrality recentering, stage 5"); + } + } + + if (confignewpro.finecentvx5) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvxSpA[4])) { + hrecenterecentvxSpA[4] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVxSp5.value, bc.timestamp()); + } + if (!CorrectfineCentVx(hrecenterecentvxSpA[4], centrality, vx, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vx) recentering, stage 5"); + } + } + + if (confignewpro.finecentvy5) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvySpA[4])) { + hrecenterecentvySpA[4] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVySp5.value, bc.timestamp()); + } + if (!CorrectfineCentVy(hrecenterecentvySpA[4], centrality, vy, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vy) recentering, stage 5"); + } + } + + if (confignewpro.finecentvz5) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvzSpA[4])) { + hrecenterecentvzSpA[4] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVzSp5.value, bc.timestamp()); + } + if (!CorrectfineCentVz(hrecenterecentvzSpA[4], centrality, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vz) recentering, stage 5"); + } + } + + if (confignewpro.finevztime5) { + if (confignewpro.useRecenterefinevztimeSp && + (currentRunNumber != lastRunNumber || !hrecenterevztimeSpA[4])) { + hrecenterevztimeSpA[4] = ccdb->getForTimeStamp(confignewpro.confRecentereVzTimeSp5.value, bc.timestamp()); + } + if (!CorrectfineVzTime(hrecenterevztimeSpA[4], vz, timeMin, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (Vz, time) recentering, stage 5"); + } + } + + // -------------------- Stage 6 -------------------- + if (confignewpro.finecent6) { + if (confignewpro.useRecenterefinecentSp && + (currentRunNumber != lastRunNumber || !hrecenterecentSpA[5])) { + hrecenterecentSpA[5] = ccdb->getForTimeStamp(confignewpro.confRecentereCentSp6.value, bc.timestamp()); + } + if (!CorrectfineCent(hrecenterecentSpA[5], centrality, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine centrality recentering, stage 6"); + } + } + + if (confignewpro.finecentvx6) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvxSpA[5])) { + hrecenterecentvxSpA[5] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVxSp6.value, bc.timestamp()); + } + if (!CorrectfineCentVx(hrecenterecentvxSpA[5], centrality, vx, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vx) recentering, stage 6"); + } + } + + if (confignewpro.finecentvy6) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvySpA[5])) { + hrecenterecentvySpA[5] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVySp6.value, bc.timestamp()); + } + if (!CorrectfineCentVy(hrecenterecentvySpA[5], centrality, vy, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vy) recentering, stage 6"); + } + } + + if (confignewpro.finecentvz6) { + if (confignewpro.useRecenterefinecentvxvyvzSp && + (currentRunNumber != lastRunNumber || !hrecenterecentvzSpA[5])) { + hrecenterecentvzSpA[5] = ccdb->getForTimeStamp(confignewpro.confRecentereCentVzSp6.value, bc.timestamp()); + } + if (!CorrectfineCentVz(hrecenterecentvzSpA[5], centrality, vz, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply fine (cent,Vz) recentering, stage 6"); + } + } + + // -------------------- Width equalization -------------------- + if (confignewpro.useWidthEqualization) { + if (currentRunNumber != lastRunNumber) { + + hWidthMean = ccdb->getForTimeStamp(confignewpro.confWidthMean.value, bc.timestamp()); + hWidthSigma = ccdb->getForTimeStamp(confignewpro.confWidthSigma.value, bc.timestamp()); + } + + if (!CorrectWidthEqualization(hWidthMean, hWidthSigma, centrality, qxZDCA, qyZDCA, qxZDCC, qyZDCC)) { + LOGF(fatal, "Cannot apply Q-vector width equalization"); + } + } } + + histos.fill(HIST("hpQxZDCAvstime"), timeMin, qxZDCA); + histos.fill(HIST("hpQxZDCCvstime"), timeMin, qxZDCC); + histos.fill(HIST("hpQyZDCAvstime"), timeMin, qyZDCA); + histos.fill(HIST("hpQyZDCCvstime"), timeMin, qyZDCC); + psiZDCC = 1.0 * TMath::ATan2(qyZDCC, qxZDCC); psiZDCA = 1.0 * TMath::ATan2(qyZDCA, qxZDCA); @@ -750,6 +1555,54 @@ struct spvector { histos.fill(HIST("hvzQyZDCA"), vz, qyZDCA); histos.fill(HIST("hvzQxZDCC"), vz, qxZDCC); histos.fill(HIST("hvzQyZDCC"), vz, qyZDCC); + + histos.fill(HIST("pQxZDCAvsCentVzAfter"), + centrality, vz, qxZDCA); + + histos.fill(HIST("pQyZDCAvsCentVzAfter"), + centrality, vz, qyZDCA); + + histos.fill(HIST("pQxZDCCvsCentVzAfter"), + centrality, vz, qxZDCC); + + histos.fill(HIST("pQyZDCCvsCentVzAfter"), + centrality, vz, qyZDCC); + + histos.fill(HIST("pQxZDCAvsCentVyAfter"), + centrality, vy, qxZDCA); + + histos.fill(HIST("pQyZDCAvsCentVyAfter"), + centrality, vy, qyZDCA); + + histos.fill(HIST("pQxZDCCvsCentVyAfter"), + centrality, vy, qxZDCC); + + histos.fill(HIST("pQyZDCCvsCentVyAfter"), + centrality, vy, qyZDCC); + + histos.fill(HIST("pQxZDCAvsCentVxAfter"), + centrality, vx, qxZDCA); + + histos.fill(HIST("pQyZDCAvsCentVxAfter"), + centrality, vx, qyZDCA); + + histos.fill(HIST("pQxZDCCvsCentVxAfter"), + centrality, vx, qxZDCC); + + histos.fill(HIST("pQyZDCCvsCentVxAfter"), + centrality, vx, qyZDCC); + + histos.fill(HIST("pQxZDCAvsVyVxAfter"), + vy, vx, qxZDCA); + + histos.fill(HIST("pQyZDCAvsVyVxAfter"), + vy, vx, qyZDCA); + + histos.fill(HIST("pQxZDCCvsVyVxAfter"), + vy, vx, qxZDCC); + + histos.fill(HIST("pQyZDCCvsVyVxAfter"), + vy, vx, qyZDCC); } histos.fill(HIST("hpCosPsiAPsiC"), centrality, (TMath::Cos(psiZDCA - psiZDCC))); diff --git a/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx new file mode 100644 index 00000000000..6bf3eb98054 --- /dev/null +++ b/PWGLF/TableProducer/Common/zdc2stagecalibration.cxx @@ -0,0 +1,1110 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +// \author: sourav kundu +// \email: sourav.kundu@cern.ch + +#include "PWGLF/DataModel/ZDC2StageCalibrationTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FT0Corrected.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; + +using BCsRun3 = o2::soa::Join; + +struct zdc2stagecalibration { + + o2::framework::Produces zdccaltable; + + struct : ConfigurableGroup { + Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; + } cfgCcdbParam; + + Service ccdb{}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgCutCentralityMax{"cfgCutCentralityMax", 80.0f, "Centrality cut Max"}; + Configurable cfgCutCentralityMin{"cfgCutCentralityMin", 0.0f, "Centrality cut Min"}; + Configurable calibrationStage{"calibrationStage", 1, "1: derive detector calibration QA, 2: apply detector calibration and produce corrected Q"}; + Configurable cfgTimeSliceMinutes{"cfgTimeSliceMinutes", 30.0f, "Calibration time-slice width in minutes"}; + Configurable cfgMaxRunHours{"cfgMaxRunHours", 48.0f, "Maximum run duration shown in QA"}; + Configurable followpub{"followpub", true, "flag to use alphaZDC"}; + Configurable usecfactor{"usecfactor", false, "use c factor"}; + Configurable useGainCallib{"useGainCallib", false, "use stage-1 tower gain calibration"}; + Configurable confGainPath{"confGainPath", "", "CCDB path to the run-wise stage-1 gain map"}; + Configurable useSpatialCalib{"useSpatialCalib", false, "use ZDC spatial calibration"}; + Configurable confSpatialPath{"confSpatialPath", "", "CCDB path to ZDC spatial calibration"}; + Configurable deriveSpatialCalib{"deriveSpatialCalib", false, "store spatial regression moments after gain correction"}; + Configurable deriveQRecentering{"deriveQRecentering", false, "store Q-vector recentering regression moments after gain and spatial correction"}; + Configurable useQRecentering{"useQRecentering", false, "apply Q-vector recentering calibration"}; + Configurable qRecenteringMethod{"qRecenteringMethod", 0, "Q recentering method: 0=current regression, 1=multidimensional cubic regression, 4=structured71 regression"}; + Configurable confQRecenteringPath{"confQRecenteringPath", "", "CCDB path to the current run-wise Q-vector recentering coefficients"}; + Configurable confQRecenteringMultidimPath{"confQRecenteringMultidimPath", "", "CCDB path to the multidimensional cubic Q-vector recentering coefficients"}; + Configurable confQRecenteringStructured71Path{"confQRecenteringStructured71Path", "", "CCDB path to the 71-feature structured Q-vector recentering coefficients"}; + Configurable useQWidthEqualization{"useQWidthEqualization", false, "equalize Qx/Qy widths after Structured71 recentering"}; + Configurable confQWidthPath{"confQWidthPath", "", "CCDB path to post-recentering Q-width map"}; + Configurable cfgSpatialCalibBins{ + "cfgSpatialCalibBins", 30, + "number of bins per cross coordinate for spatial calibration"}; + struct : ConfigurableGroup { + Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", true, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + } rctCut; + + RCTFlagsChecker rctChecker; + int currentRunNumber = -999; + int64_t bcSOR = -1; + uint64_t sorTimestamp = 0; + uint64_t eorTimestamp = 0; + TH2D* gainprofile = nullptr; + TH3D* spatialprofile = nullptr; + TH3D* qRecenteringProfile = nullptr; + TH2D* qWidthProfile = nullptr; + + static constexpr int kQRecenteringNFeatures = 21; + static constexpr int kQRecenteringNMatrixMoments = kQRecenteringNFeatures * (kQRecenteringNFeatures + 1) / 2; + static constexpr int kQRecenteringNComponents = 4; + static constexpr int kQRecenteringNMoments = kQRecenteringNMatrixMoments + kQRecenteringNComponents * kQRecenteringNFeatures; + static constexpr int kQRecenteringMultidimNFeatures = 35; + static constexpr int kQRecenteringMultidimNMatrixMoments = kQRecenteringMultidimNFeatures * (kQRecenteringMultidimNFeatures + 1) / 2; + static constexpr int kQRecenteringMultidimNMoments = kQRecenteringMultidimNMatrixMoments + kQRecenteringNComponents * kQRecenteringMultidimNFeatures; + // Additional sufficient statistics for the flatness-constrained multidimensional fit. + // Per conditioning bin store 35 sums of the cubic features followed by + // 4 sums of QxA,QyA,QxC,QyC. Feature 0 is unity, so its sum is N. + static constexpr int kQRecenteringFlatnessNMoments = kQRecenteringMultidimNFeatures + kQRecenteringNComponents; + + // Structured regression diagnostic basis. The existing 35 cubic features are + // kept unchanged and augmented with truncated-cubic 1D basis functions for + // time, Vx, Vy and Vz. The additional basis is intentionally overcomplete so + // that the final subset and regularisation can be chosen offline without + // another derivation pass. + static constexpr int kQRecenteringStructuredNKnots = 9; + static constexpr int kQRecenteringStructuredNFeatures = kQRecenteringMultidimNFeatures + 4 * kQRecenteringStructuredNKnots; + static constexpr int kQRecenteringStructuredNMatrixMoments = kQRecenteringStructuredNFeatures * (kQRecenteringStructuredNFeatures + 1) / 2; + static constexpr int kQRecenteringStructuredNMoments = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + kQRecenteringNComponents; + // Per conditioning bin store sum(features), sum(Q) and sum(Q^2). + static constexpr int kQRecenteringStructuredFlatnessNMoments = kQRecenteringStructuredNFeatures + 2 * kQRecenteringNComponents; + + void init(o2::framework::InitContext&) + { + rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + if (qRecenteringMethod.value != 0 && qRecenteringMethod.value != 1 && qRecenteringMethod.value != 4) { + LOGF(fatal, "Unsupported qRecenteringMethod=%d. Use 0 for the current regression, 1 for the multidimensional cubic regression or 4 for the structured71 regression.", qRecenteringMethod.value); + } + if (useQWidthEqualization.value && (calibrationStage.value != 2 || !useQRecentering.value || qRecenteringMethod.value != 4 || confQWidthPath.value.empty())) { + LOGF(fatal, "Width equalization requires stage=2, useQRecentering=true, method=4 and confQWidthPath"); + } + const int nTimeBins = static_cast(std::ceil(cfgMaxRunHours.value * 60.f / cfgTimeSliceMinutes.value)); + AxisSpec timeAxis = {nTimeBins, 0.0, cfgMaxRunHours.value, "time from SOR (h)"}; + AxisSpec towerAxis = {4, 0.0, 4.0, "tower"}; + AxisSpec crossAxis = {120, -1.0, 1.0, "cross asymmetry"}; + AxisSpec momentAxis = {15, 0.0, 15.0, "regression moment"}; + AxisSpec spatialMomentAxis = {6, 0.0, 6.0, "normalized pair regression moment"}; + AxisSpec spatialCalibAxis = {cfgSpatialCalibBins.value, -1.0, 1.0, "cross asymmetry"}; + AxisSpec phiAxis = {72, -3.14159265358979323846, 3.14159265358979323846, "#phi"}; + AxisSpec crossCoordAxis = {2, 0.0, 2.0, "coordinate"}; + AxisSpec ratioAxis = {240, 0.0, 2.4, "#Sigma tower/common"}; + AxisSpec qRecenteringMomentAxis = {kQRecenteringNMoments, 0.0, static_cast(kQRecenteringNMoments), "regression moment"}; + AxisSpec qRecenteringMultidimMomentAxis = {kQRecenteringMultidimNMoments, 0.0, static_cast(kQRecenteringMultidimNMoments), "multidimensional regression moment"}; + AxisSpec qRecenteringFlatnessMomentAxis = {kQRecenteringFlatnessNMoments, 0.0, static_cast(kQRecenteringFlatnessNMoments), "flatness sufficient-statistic index"}; + AxisSpec qRecenteringStructuredMomentAxis = {kQRecenteringStructuredNMoments, 0.0, static_cast(kQRecenteringStructuredNMoments), "structured regression moment"}; + AxisSpec qRecenteringStructuredFlatnessMomentAxis = {kQRecenteringStructuredFlatnessNMoments, 0.0, static_cast(kQRecenteringStructuredFlatnessNMoments), "structured flatness sufficient-statistic index"}; + AxisSpec centralityAxis = {80, 0.0, 80.0, "centrality (%)"}; + AxisSpec resolutionCentralityAxis = {50, 0.0, 50.0, "centrality (%)"}; + AxisSpec resolutionVzAxis = {20, -10.0, 10.0, "v_{z} (cm)"}; + AxisSpec vertexXYAxis = {100, -0.1, 0.1, "vertex x/y (cm)"}; + AxisSpec vertexZAxis = {100, -10.0, 10.0, "vertex z (cm)"}; + AxisSpec qComponentAxis = {4, 0.0, 4.0, "Q component"}; + AxisSpec qCorrelationComponentAxis = {4, 0.0, 4.0, "A-C correlation component"}; + AxisSpec qValueAxis = {200, -4.0, 4.0, "Q"}; + + histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0.0, 10.0}}); + auto hEvtSelInfo = histos.get(HIST("hEvtSelInfo")); + hEvtSelInfo->GetXaxis()->SetBinLabel(1, "All"); + hEvtSelInfo->GetXaxis()->SetBinLabel(2, "has ZDC"); + hEvtSelInfo->GetXaxis()->SetBinLabel(3, "valid common"); + hEvtSelInfo->GetXaxis()->SetBinLabel(4, "valid towers"); + hEvtSelInfo->GetXaxis()->SetBinLabel(5, "RCT CBT+ZDC"); + hEvtSelInfo->GetXaxis()->SetBinLabel(6, "event sel."); + hEvtSelInfo->GetXaxis()->SetBinLabel(7, "gain valid"); + hEvtSelInfo->GetXaxis()->SetBinLabel(8, "valid Q"); + hEvtSelInfo->GetXaxis()->SetBinLabel(9, "accepted"); + histos.add("GainQA/hSumOverCommonVsTimeZNA", "ZNA tower sum/common vs time;time from SOR (h);#Sigma T_{i}/C", kTH2F, {timeAxis, ratioAxis}); + histos.add("GainQA/hSumOverCommonVsTimeZNC", "ZNC tower sum/common vs time;time from SOR (h);#Sigma T_{i}/C", kTH2F, {timeAxis, ratioAxis}); + histos.add("GainQA/pTowerOverCommonVsTimeZNA", "ZNA individual tower/common vs time;time from SOR (h);tower;", kTProfile2D, {timeAxis, towerAxis}); + histos.add("GainQA/pTowerOverCommonVsTimeZNC", "ZNC individual tower/common vs time;time from SOR (h);tower;", kTProfile2D, {timeAxis, towerAxis}); + histos.add("SpatialQA/pCrossVsTimeZNA", "ZNA cross asymmetry vs time;time from SOR (h);0:X cross, 1:Y cross;", kTProfile2D, {timeAxis, crossCoordAxis}); + histos.add("SpatialQA/pCrossVsTimeZNC", "ZNC cross asymmetry vs time;time from SOR (h);0:X cross, 1:Y cross;", kTProfile2D, {timeAxis, crossCoordAxis}); + histos.add("SpatialQA/pResponseVsCrossXYZNA", "ZNA response map;X cross;Y cross;<#Sigma T_{i}/C>", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pResponseVsCrossXYZNC", "ZNC response map;X cross;Y cross;<#Sigma T_{i}/C>", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pCentroidVsTimeZNA", "ZNA centroid vs time;time from SOR (h);0:x, 1:y;", kTProfile2D, {timeAxis, crossCoordAxis}); + histos.add("SpatialQA/pCentroidVsTimeZNC", "ZNC centroid vs time;time from SOR (h);0:x, 1:y;", kTProfile2D, {timeAxis, crossCoordAxis}); + histos.add("SpatialQA/hCrossXYZNA", "ZNA cross occupancy;X cross;Y cross", kTH2F, {crossAxis, crossAxis}); + histos.add("SpatialQA/hCrossXYZNC", "ZNC cross occupancy;X cross;Y cross", kTH2F, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower1OverCommonVsCrossXYZNA", "ZNA tower 1 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower2OverCommonVsCrossXYZNA", "ZNA tower 2 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower3OverCommonVsCrossXYZNA", "ZNA tower 3 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower4OverCommonVsCrossXYZNA", "ZNA tower 4 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower1OverCommonVsCrossXYZNC", "ZNC tower 1 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower2OverCommonVsCrossXYZNC", "ZNC tower 2 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower3OverCommonVsCrossXYZNC", "ZNC tower 3 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("SpatialQA/pTower4OverCommonVsCrossXYZNC", "ZNC tower 4 response;X cross;Y cross;", kTProfile2D, {crossAxis, crossAxis}); + histos.add("PhiQA/hPhiRawZNA", "ZNA #phi raw;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiRawZNC", "ZNC #phi raw;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiAfterGainZNA", "ZNA #phi after gain calibration;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiAfterGainZNC", "ZNC #phi after gain calibration;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiAfterSpatialZNA", "ZNA #phi after spatial calibration;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiAfterSpatialZNC", "ZNC #phi after spatial calibration;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiRawVsCentralityZNA", "ZNA #phi raw vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiRawVsCentralityZNC", "ZNC #phi raw vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterGainVsCentralityZNA", "ZNA #phi after gain vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterGainVsCentralityZNC", "ZNC #phi after gain vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterSpatialVsCentralityZNA", "ZNA #phi after spatial calibration vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterSpatialVsCentralityZNC", "ZNC #phi after spatial calibration vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + + // Stage-1 correction input. For each time bin the first 10 y bins contain sum(T_i*T_j), the next 4 contain sum(C*T_i), and the last contains the event count. + histos.add("GainCalibration/hGainMomentsZNA", "ZNA linear-regression moments;time from SOR (h);moment index", kTH2D, {timeAxis, momentAxis}); + histos.add("GainCalibration/hGainMomentsZNC", "ZNC linear-regression moments;time from SOR (h);moment index", kTH2D, {timeAxis, momentAxis}); + // Normalized two-pair spatial regression moments. For each (Xcross,Ycross) cell: + // u=(T1+T4)/C, v=(T2+T3)/C, and moments are u^2, u*v, v^2, u, v, N. + histos.add("SpatialCalibration/hSpatialMomentsZNA", "ZNA normalized pair spatial regression moments;X cross;Y cross;moment index", kTH3D, {spatialCalibAxis, spatialCalibAxis, spatialMomentAxis}); + histos.add("SpatialCalibration/hSpatialMomentsZNC", "ZNC normalized pair spatial regression moments;X cross;Y cross;moment index", kTH3D, {spatialCalibAxis, spatialCalibAxis, spatialMomentAxis}); + + // Q-recentering regression moments derived independently in 1% centrality bins. + // The centrality feature C is local to each 1% bin: C=2*(cent-(floor(cent)+0.5)), so C is in [-1,1). + // Feature order: + // 0:1, 1:C, 2:T, 3:Vx, 4:Vy, 5:Vz, 6:C2, 7:T2, 8:Vx2, 9:Vy2, 10:Vz2, + // 11:C*T, 12:C*Vx, 13:C*Vy, 14:C*Vz, 15:T*Vx, 16:T*Vy, 17:T*Vz, + // 18:Vx*Vy, 19:Vx*Vz, 20:Vy*Vz. + // For each 1% centrality bin, moments 0..230 are the upper triangle of sum(X_i X_j), + // followed by 4 blocks of 21 sum(X_i Q): QxA, QyA, QxC, QyC. + histos.add("QRecenteringCalibration/hRegressionMoments", "Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMomentAxis}); + histos.add("QRecenteringCalibration/hRegressionMomentsMultidim", "Multidimensional cubic Q recentering regression moments;centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringMultidimMomentAxis}); + + // Sufficient statistics used only when deriving the multidimensional map with + // an additional flatness penalty. They do not alter the 35-feature model or + // the application-side CCDB format. For each (centrality,conditioning) bin: + // indices 0..34 = sum(X_i), indices 35..38 = sum(QxA,QyA,QxC,QyC). + if (deriveQRecentering.value && qRecenteringMethod.value == 1) { + histos.add("QRecenteringCalibration/hFlatnessMomentsTime", "Flatness sufficient statistics vs time;centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVx", "Flatness sufficient statistics vs v_{x};centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVy", "Flatness sufficient statistics vs v_{y};centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hFlatnessMomentsVz", "Flatness sufficient statistics vs v_{z};centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringFlatnessMomentAxis}); + + // Additional train/validation sufficient statistics for the structured + // regression test. These do not modify the current 35-feature correction. + histos.add("QRecenteringCalibration/hRegressionMomentsStructuredTrain", "Structured Q recentering moments (train);centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringStructuredMomentAxis}); + histos.add("QRecenteringCalibration/hRegressionMomentsStructuredValidation", "Structured Q recentering moments (validation);centrality (%);moment index", kTH2D, {centralityAxis, qRecenteringStructuredMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain", "Structured flatness statistics vs time (train);centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain", "Structured flatness statistics vs v_{x} (train);centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain", "Structured flatness statistics vs v_{y} (train);centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain", "Structured flatness statistics vs v_{z} (train);centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation", "Structured flatness statistics vs time (validation);centrality (%);time from SOR (h);statistic index", kTH3D, {centralityAxis, timeAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation", "Structured flatness statistics vs v_{x} (validation);centrality (%);v_{x} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation", "Structured flatness statistics vs v_{y} (validation);centrality (%);v_{y} (cm);statistic index", kTH3D, {centralityAxis, vertexXYAxis, qRecenteringStructuredFlatnessMomentAxis}); + histos.add("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation", "Structured flatness statistics vs v_{z} (validation);centrality (%);v_{z} (cm);statistic index", kTH3D, {centralityAxis, vertexZAxis, qRecenteringStructuredFlatnessMomentAxis}); + } + + histos.add("QRecenteringQA/pQBeforeVsCentrality", "Q before recentering vs centrality;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQBeforeVsTime", "Q before recentering vs time;time from SOR (h);Q component;", kTProfile2D, {timeAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQBeforeVsVx", "Q before recentering vs v_{x};v_{x} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQBeforeVsVy", "Q before recentering vs v_{y};v_{y} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQBeforeVsVz", "Q before recentering vs v_{z};v_{z} (cm);Q component;", kTProfile2D, {vertexZAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQAfterVsCentrality", "Q after recentering vs centrality;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQAfterVsTime", "Q after recentering vs time;time from SOR (h);Q component;", kTProfile2D, {timeAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQAfterVsVx", "Q after recentering vs v_{x};v_{x} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQAfterVsVy", "Q after recentering vs v_{y};v_{y} (cm);Q component;", kTProfile2D, {vertexXYAxis, qComponentAxis}); + histos.add("QRecenteringQA/pQAfterVsVz", "Q after recentering vs v_{z};v_{z} (cm);Q component;", kTProfile2D, {vertexZAxis, qComponentAxis}); + histos.add("ResolutionQA/pCosPsiAPsiCVsCentrality", "ZDC A-C spectator-plane correlation;centrality (%);<#cos(#Psi_{A}-#Psi_{C})>", kTProfile, {resolutionCentralityAxis}); + histos.add("ResolutionQA/pCosPsiAPsiCVsCentralityVz", "ZDC A-C correlation;centrality (%);v_{z} (cm); <#cos(#Psi_{A}-#Psi_{C})>", kTProfile2D, {resolutionCentralityAxis, resolutionVzAxis}); + histos.add("QWidthQA/pQAfterWidthEqVsCentrality", "Q after width equalization;centrality (%);Q component;", kTProfile2D, {centralityAxis, qComponentAxis}); + histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNA", "ZNA #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterWidthEqVsCentralityZNC", "ZNC #phi after width equalization;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityTime", "QxA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityTime", "QyA after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityTime", "QxC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityTime", "QyC after recentering;centrality (%);time from SOR (h);", kTProfile2D, {centralityAxis, timeAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityVx", "QxA after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityVx", "QyA after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityVx", "QxC after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityVx", "QyC after recentering;centrality (%);v_{x} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityVy", "QxA after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityVy", "QyA after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityVy", "QxC after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityVy", "QyC after recentering;centrality (%);v_{y} (cm);", kTProfile2D, {centralityAxis, vertexXYAxis}); + histos.add("QRecentering2DQA/pQxAAfterVsCentralityVz", "QxA after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); + histos.add("QRecentering2DQA/pQyAAfterVsCentralityVz", "QyA after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); + histos.add("QRecentering2DQA/pQxCAfterVsCentralityVz", "QxC after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); + histos.add("QRecentering2DQA/pQyCAfterVsCentralityVz", "QyC after recentering;centrality (%);v_{z} (cm);", kTProfile2D, {centralityAxis, vertexZAxis}); + + // A-C Q-vector correlation QA. These histograms are diagnostic only; no Qx/Qy mixing is applied. + histos.add("QCorrelationQA/hQxAQxCBefore", "QxA vs QxC before recentering;Q_{x}^{A};Q_{x}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("QCorrelationQA/hQyAQyCBefore", "QyA vs QyC before recentering;Q_{y}^{A};Q_{y}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("QCorrelationQA/hQxAQyCBefore", "QxA vs QyC before recentering;Q_{x}^{A};Q_{y}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("QCorrelationQA/hQyAQxCBefore", "QyA vs QxC before recentering;Q_{y}^{A};Q_{x}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("QCorrelationQA/hQxAQxCAfter", "QxA vs QxC after recentering;Q_{x}^{A};Q_{x}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("QCorrelationQA/hQyAQyCAfter", "QyA vs QyC after recentering;Q_{y}^{A};Q_{y}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("QCorrelationQA/hQxAQyCAfter", "QxA vs QyC after recentering;Q_{x}^{A};Q_{y}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("QCorrelationQA/hQyAQxCAfter", "QyA vs QxC after recentering;Q_{y}^{A};Q_{x}^{C}", kTH2F, {qValueAxis, qValueAxis}); + histos.add("PhiQA/hPhiAfterRecenteringZNA", "ZNA #phi after Q recentering;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiAfterRecenteringZNC", "ZNC #phi after Q recentering;#phi;events", kTH1F, {phiAxis}); + histos.add("PhiQA/hPhiAfterRecenteringVsCentralityZNA", "ZNA #phi after Q recentering vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + histos.add("PhiQA/hPhiAfterRecenteringVsCentralityZNC", "ZNC #phi after Q recentering vs centrality;centrality (%);#phi", kTH2F, {centralityAxis, phiAxis}); + + // A-C Q correlations versus centrality. Component convention: + // 0: QxA*QxC, 1: QyA*QyC, 2: QxA*QyC, 3: QyA*QxC. + // These are QA only; they are never used in the recentering correction. + histos.add("QCorrelationQA/pQACorrelationBeforeVsCentrality", "A-C Q correlations before recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); + histos.add("QCorrelationQA/pQACorrelationAfterVsCentrality", "A-C Q correlations after recentering;centrality (%);correlation component;", kTProfile2D, {centralityAxis, qCorrelationComponentAxis}); + + ccdb->setURL(cfgCcdbParam.cfgURL); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + } + + void updateRun(int runNumber, uint64_t timestamp) + { + if (runNumber == currentRunNumber) { + return; + } + + currentRunNumber = runNumber; + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(o2::ccdb::BasicCCDBManager::instance(), runNumber); + sorTimestamp = runInfo.sor; + eorTimestamp = runInfo.eor; + bcSOR = runInfo.orbitSOR * static_cast(o2::constants::lhc::LHCMaxBunches); + gainprofile = nullptr; + spatialprofile = nullptr; + qRecenteringProfile = nullptr; + qWidthProfile = nullptr; + if (calibrationStage.value == 2 && useGainCallib.value && !confGainPath.value.empty()) { + gainprofile = ccdb->getForTimeStamp(confGainPath.value, timestamp); + if (!gainprofile) { + LOGF(warn, "No ZDC stage-1 gain map found for run %d at timestamp %llu", runNumber, timestamp); + } + } + if (calibrationStage.value == 2 && useSpatialCalib.value && !confSpatialPath.value.empty()) { + spatialprofile = ccdb->getForTimeStamp(confSpatialPath.value, timestamp); + + if (!spatialprofile) { + LOGF(warn, "No ZDC spatial calibration found for run %d at timestamp %llu", runNumber, timestamp); + } + } + if (calibrationStage.value == 2 && useQRecentering.value) { + const std::string qRecenteringPath = qRecenteringMethod.value == 4 ? confQRecenteringStructured71Path.value : (qRecenteringMethod.value == 1 ? confQRecenteringMultidimPath.value : confQRecenteringPath.value); + if (!qRecenteringPath.empty()) { + qRecenteringProfile = ccdb->getForTimeStamp(qRecenteringPath, timestamp); + } + if (!qRecenteringProfile) { + LOGF(warn, "No ZDC Q-recentering calibration found for run %d at timestamp %llu", runNumber, timestamp); + } else if (qRecenteringMethod.value == 4) { + const bool structuredMapOK = qRecenteringProfile->GetYaxis()->GetNbins() == kQRecenteringStructuredNFeatures && + std::string(qRecenteringProfile->GetYaxis()->GetBinLabel(kQRecenteringMultidimNFeatures + 1)) == "(T--0.8)_+^3" && + std::string(qRecenteringProfile->GetYaxis()->GetBinLabel(kQRecenteringStructuredNFeatures)) == "(Vz-+0.8)_+^3"; + if (!structuredMapOK) { + LOGF(fatal, "CCDB object at %s is not a valid structured71 map for run %d", qRecenteringPath.c_str(), runNumber); + } + } + } + if (calibrationStage.value == 2 && useQWidthEqualization.value) { + qWidthProfile = ccdb->getForTimeStamp(confQWidthPath.value, timestamp); + if (!qWidthProfile || qWidthProfile->GetNbinsX() != 80 || qWidthProfile->GetNbinsY() != 4) { + LOGF(fatal, "Missing/invalid Q-width map for run %d", runNumber); + } + constexpr std::array labels = {"QxA", "QyA", "QxC", "QyC"}; + for (int i = 0; i < 4; ++i) { + if (std::string(qWidthProfile->GetYaxis()->GetBinLabel(i + 1)) != labels[i]) { + LOGF(fatal, "Incorrect Q-width map component ordering"); + } + } + } + } + + float getTimeFromSOR(uint64_t globalBC) const + { + if (bcSOR < 0 || globalBC < static_cast(bcSOR)) { + return -1.f; + } + const double deltaBC = static_cast(globalBC - static_cast(bcSOR)); + return static_cast(deltaBC * o2::constants::lhc::LHCBunchSpacingNS * 1.e-9 / 3600.); + } + + double getGain(float timeFromSOR, int channel, bool& gainOK) const + { + if (calibrationStage.value != 2 || !useGainCallib.value) { + return 1.0; + } + if (!gainprofile || timeFromSOR < 0.f) { + gainOK = false; + return 1.0; + } + const double gain = gainprofile->GetBinContent(gainprofile->FindBin(timeFromSOR + 1.e-7, channel + 0.5)); + if (!std::isfinite(gain) || gain <= 0.0) { + gainOK = false; + return 1.0; + } + return gain; + } + + std::array makeQRecenteringFeatures(float centrality, float timeFromSOR, float vx, float vy, float vz) const + { + // Recentering is solved independently in each 1% centrality bin. + // Keep a local centrality coordinate inside the bin so correlations with time/vertex + // are retained without using the global 0-80% centrality scale. + const double cent = static_cast(centrality); + const double centBinLow = std::floor(cent); + const double c = 2.0 * (cent - (centBinLow + 0.5)); + const double runHours = (eorTimestamp > sorTimestamp) ? static_cast(eorTimestamp - sorTimestamp) * 1.e-3 / 3600.0 : static_cast(cfgMaxRunHours.value); + const double t = (runHours > 0.0) ? (2.0 * static_cast(timeFromSOR) / runHours - 1.0) : 0.0; + const double x = static_cast(vx) / 0.1; + const double y = static_cast(vy) / 0.1; + const double z = static_cast(vz) / 10.0; + + return {1.0, c, t, x, y, z, + c * c, t * t, x * x, y * y, z * z, + c * t, c * x, c * y, c * z, + t * x, t * y, t * z, + x * y, x * z, y * z}; + } + + std::array makeQRecenteringMultidimFeatures(float timeFromSOR, float vx, float vy, float vz) const + { + const double runHours = (eorTimestamp > sorTimestamp) ? static_cast(eorTimestamp - sorTimestamp) * 1.e-3 / 3600.0 : static_cast(cfgMaxRunHours.value); + const double t = (runHours > 0.0) ? (2.0 * static_cast(timeFromSOR) / runHours - 1.0) : 0.0; + const double x = static_cast(vx) / 0.1; + const double y = static_cast(vy) / 0.1; + const double z = static_cast(vz) / 10.0; + const double t2 = t * t; + const double x2 = x * x; + const double y2 = y * y; + const double z2 = z * z; + + return {1.0, t, x, y, z, + t2, x2, y2, z2, t * x, t * y, t * z, x * y, x * z, y * z, + t2 * t, x2 * x, y2 * y, z2 * z, + t2 * x, t2 * y, t2 * z, x2 * t, x2 * y, x2 * z, + y2 * t, y2 * x, y2 * z, z2 * t, z2 * x, z2 * y, + t * x * y, t * x * z, t * y * z, x * y * z}; + } + + std::array makeQRecenteringStructuredFeatures(float timeFromSOR, float vx, float vy, float vz) const + { + const auto baseFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); + std::array features{}; + + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + features[i] = baseFeatures[i]; + } + + const double runHours = (eorTimestamp > sorTimestamp) ? static_cast(eorTimestamp - sorTimestamp) * 1.e-3 / 3600.0 : static_cast(cfgMaxRunHours.value); + const double t = (runHours > 0.0) ? (2.0 * static_cast(timeFromSOR) / runHours - 1.0) : 0.0; + const double x = static_cast(vx) / 0.1; + const double y = static_cast(vy) / 0.1; + const double z = static_cast(vz) / 10.0; + + // Internal knots in the normalized [-1,1] coordinate. Together with the + // existing 1,u,u^2,u^3 terms this is a cubic regression-spline basis. + constexpr std::array knots = { + -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}; + + int index = kQRecenteringMultidimNFeatures; + auto appendSplineTerms = [&](double value) { + for (int i = 0; i < kQRecenteringStructuredNKnots; ++i) { + const double delta = value - knots[i]; + features[index++] = delta > 0.0 ? delta * delta * delta : 0.0; + } + }; + + appendSplineTerms(t); + appendSplineTerms(x); + appendSplineTerms(y); + appendSplineTerms(z); + + return features; + } + + using MyCollisions = o2::soa::Join; + + void process(MyCollisions::iterator const& collision, o2::aod::FT0s const& /*ft0s*/, o2::aod::FV0As const& /*fv0s*/, BCsRun3 const& /*bcs*/, o2::aod::Zdcs const& /*zdcs*/) + { + histos.fill(HIST("hEvtSelInfo"), 0.5); + + const float centrality = collision.centFT0C(); + const float vx = collision.posX(); + const float vy = collision.posY(); + const float vz = collision.posZ(); + auto bc = collision.foundBC_as(); + const int runNumber = bc.runNumber(); + const uint64_t globalBC = bc.globalBC(); + const uint64_t timestamp = bc.timestamp(); + + updateRun(runNumber, timestamp); + const float timeFromSOR = getTimeFromSOR(globalBC); + + auto fillTable = [&](bool trigger, float qxA, float qxC, float qyA, float qyC) { + zdccaltable(collision.globalIndex(), trigger, runNumber, globalBC, centrality, vx, vy, vz, qxA, qxC, qyA, qyC); + }; + + if (!bc.has_zdc()) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + histos.fill(HIST("hEvtSelInfo"), 1.5); + + auto zdc = bc.zdc(); + auto znaEnergy = zdc.energySectorZNA(); + auto zncEnergy = zdc.energySectorZNC(); + const float znaEnergycommon = zdc.energyCommonZNA(); + const float zncEnergycommon = zdc.energyCommonZNC(); + + if (!std::isfinite(znaEnergycommon) || !std::isfinite(zncEnergycommon) || znaEnergycommon <= 0.f || zncEnergycommon <= 0.f) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + histos.fill(HIST("hEvtSelInfo"), 2.5); + + bool validTowers = true; + for (int i = 0; i < 4; ++i) { + if (!std::isfinite(znaEnergy[i]) || !std::isfinite(zncEnergy[i]) || znaEnergy[i] <= 0.f || zncEnergy[i] <= 0.f) { + validTowers = false; + } + } + if (!validTowers) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + histos.fill(HIST("hEvtSelInfo"), 3.5); + + if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + histos.fill(HIST("hEvtSelInfo"), 4.5); + + if (!(collision.sel8() && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && std::isfinite(centrality) && centrality > cfgCutCentralityMin && centrality < cfgCutCentralityMax && std::abs(vz) < cfgCutVertex && collision.has_foundFT0())) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + histos.fill(HIST("hEvtSelInfo"), 5.5); + + if (timeFromSOR < 0.f || timeFromSOR >= cfgMaxRunHours.value) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + + constexpr double x[4] = {-1.75, 1.75, -1.75, 1.75}; + constexpr double y[4] = {-1.75, -1.75, 1.75, 1.75}; + + double rawXA = 0.0; + double rawYA = 0.0; + double rawXC = 0.0; + double rawYC = 0.0; + double rawSumA = 0.0; + double rawSumC = 0.0; + for (int i = 0; i < 4; ++i) { + rawXA -= znaEnergy[i] * x[i]; + rawYA += znaEnergy[i] * y[i]; + rawXC += zncEnergy[i] * x[i]; + rawYC += zncEnergy[i] * y[i]; + rawSumA += znaEnergy[i]; + rawSumC += zncEnergy[i]; + } + rawXA /= rawSumA; + rawYA /= rawSumA; + rawXC /= rawSumC; + rawYC /= rawSumC; + const double phiRawA = std::atan2(rawYA, rawXA); + const double phiRawC = std::atan2(rawYC, rawXC); + histos.fill(HIST("PhiQA/hPhiRawZNA"), phiRawA); + histos.fill(HIST("PhiQA/hPhiRawZNC"), phiRawC); + histos.fill(HIST("PhiQA/hPhiRawVsCentralityZNA"), centrality, phiRawA); + histos.fill(HIST("PhiQA/hPhiRawVsCentralityZNC"), centrality, phiRawC); + + if (calibrationStage.value == 1) { + std::array momentsA{}; + std::array momentsC{}; + int moment = 0; + for (int i = 0; i < 4; ++i) { + for (int j = i; j < 4; ++j) { + momentsA[moment] = static_cast(znaEnergy[i]) * static_cast(znaEnergy[j]); + momentsC[moment] = static_cast(zncEnergy[i]) * static_cast(zncEnergy[j]); + ++moment; + } + } + for (int i = 0; i < 4; ++i) { + momentsA[10 + i] = static_cast(znaEnergycommon) * static_cast(znaEnergy[i]); + momentsC[10 + i] = static_cast(zncEnergycommon) * static_cast(zncEnergy[i]); + } + momentsA[14] = 1.0; + momentsC[14] = 1.0; + for (int i = 0; i < 15; ++i) { + histos.fill(HIST("GainCalibration/hGainMomentsZNA"), timeFromSOR, i + 0.5, momentsA[i]); + histos.fill(HIST("GainCalibration/hGainMomentsZNC"), timeFromSOR, i + 0.5, momentsC[i]); + } + } + + bool gainOK = true; + std::array znaCorr{}; + std::array zncCorr{}; + for (int i = 0; i < 4; ++i) { + znaCorr[i] = getGain(timeFromSOR, i, gainOK) * znaEnergy[i]; + zncCorr[i] = getGain(timeFromSOR, i + 4, gainOK) * zncEnergy[i]; + } + if (!gainOK) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + histos.fill(HIST("hEvtSelInfo"), 6.5); + + double gainXA = 0.0; + double gainYA = 0.0; + double gainXC = 0.0; + double gainYC = 0.0; + double gainSumA = 0.0; + double gainSumC = 0.0; + for (int i = 0; i < 4; ++i) { + gainXA -= znaCorr[i] * x[i]; + gainYA += znaCorr[i] * y[i]; + gainXC += zncCorr[i] * x[i]; + gainYC += zncCorr[i] * y[i]; + gainSumA += znaCorr[i]; + gainSumC += zncCorr[i]; + } + gainXA /= gainSumA; + gainYA /= gainSumA; + gainXC /= gainSumC; + gainYC /= gainSumC; + const double phiGainA = std::atan2(gainYA, gainXA); + const double phiGainC = std::atan2(gainYC, gainXC); + histos.fill(HIST("PhiQA/hPhiAfterGainZNA"), phiGainA); + histos.fill(HIST("PhiQA/hPhiAfterGainZNC"), phiGainC); + histos.fill(HIST("PhiQA/hPhiAfterGainVsCentralityZNA"), centrality, phiGainA); + histos.fill(HIST("PhiQA/hPhiAfterGainVsCentralityZNC"), centrality, phiGainC); + + const double crossLookupXA = (znaCorr[3] - znaCorr[0]) / (znaCorr[3] + znaCorr[0]); + const double crossLookupYA = (znaCorr[2] - znaCorr[1]) / (znaCorr[2] + znaCorr[1]); + const double crossLookupXC = (zncCorr[3] - zncCorr[0]) / (zncCorr[3] + zncCorr[0]); + const double crossLookupYC = (zncCorr[2] - zncCorr[1]) / (zncCorr[2] + zncCorr[1]); + if (calibrationStage.value == 2 && useGainCallib.value && deriveSpatialCalib.value && !useSpatialCalib.value) { + + const double uA = (znaCorr[0] + znaCorr[3]) / static_cast(znaEnergycommon); + const double vA = (znaCorr[1] + znaCorr[2]) / static_cast(znaEnergycommon); + const double uC = (zncCorr[0] + zncCorr[3]) / static_cast(zncEnergycommon); + const double vC = (zncCorr[1] + zncCorr[2]) / static_cast(zncEnergycommon); + + if (std::isfinite(uA) && std::isfinite(vA) && std::isfinite(uC) && std::isfinite(vC)) { + const std::array spatialMomentsA = { + uA * uA, + uA * vA, + vA * vA, + uA, + vA, + 1.0}; + const std::array spatialMomentsC = { + uC * uC, + uC * vC, + vC * vC, + uC, + vC, + 1.0}; + + for (int i = 0; i < 6; ++i) { + histos.fill(HIST("SpatialCalibration/hSpatialMomentsZNA"), crossLookupXA, crossLookupYA, i + 0.5, spatialMomentsA[i]); + histos.fill(HIST("SpatialCalibration/hSpatialMomentsZNC"), crossLookupXC, crossLookupYC, i + 0.5, spatialMomentsC[i]); + } + } + } + + bool spatialOK = true; + if (calibrationStage.value == 2 && useSpatialCalib.value) { + if (!spatialprofile) { + spatialOK = false; + } else { + for (int i = 0; i < 4; ++i) { + const double corrA = spatialprofile->GetBinContent(spatialprofile->FindBin(crossLookupXA, crossLookupYA, i + 0.5)); + const double corrC = spatialprofile->GetBinContent(spatialprofile->FindBin(crossLookupXC, crossLookupYC, i + 4.5)); + if (!std::isfinite(corrA) || corrA <= 0.0 || !std::isfinite(corrC) || corrC <= 0.0) { + spatialOK = false; + break; + } + znaCorr[i] *= corrA; + zncCorr[i] *= corrC; + } + } + } + if (!spatialOK) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + + double sumEnergyA = 0.0; + double sumEnergyC = 0.0; + for (int i = 0; i < 4; ++i) { + sumEnergyA += znaCorr[i]; + sumEnergyC += zncCorr[i]; + } + + const double ratioA = sumEnergyA / znaEnergycommon; + const double ratioC = sumEnergyC / zncEnergycommon; + const double crossXA = (znaCorr[3] - znaCorr[0]) / (znaCorr[3] + znaCorr[0]); + const double crossYA = (znaCorr[2] - znaCorr[1]) / (znaCorr[2] + znaCorr[1]); + const double crossXC = (zncCorr[3] - zncCorr[0]) / (zncCorr[3] + zncCorr[0]); + const double crossYC = (zncCorr[2] - zncCorr[1]) / (zncCorr[2] + zncCorr[1]); + + double centroidXA = 0.0; + double centroidYA = 0.0; + double centroidXC = 0.0; + double centroidYC = 0.0; + for (int i = 0; i < 4; ++i) { + centroidXA -= znaCorr[i] * x[i]; + centroidYA += znaCorr[i] * y[i]; + centroidXC += zncCorr[i] * x[i]; + centroidYC += zncCorr[i] * y[i]; + } + centroidXA /= sumEnergyA; + centroidYA /= sumEnergyA; + centroidXC /= sumEnergyC; + centroidYC /= sumEnergyC; + const double phiSpatialA = std::atan2(centroidYA, centroidXA); + const double phiSpatialC = std::atan2(centroidYC, centroidXC); + histos.fill(HIST("PhiQA/hPhiAfterSpatialZNA"), phiSpatialA); + histos.fill(HIST("PhiQA/hPhiAfterSpatialZNC"), phiSpatialC); + histos.fill(HIST("PhiQA/hPhiAfterSpatialVsCentralityZNA"), centrality, phiSpatialA); + histos.fill(HIST("PhiQA/hPhiAfterSpatialVsCentralityZNC"), centrality, phiSpatialC); + + histos.fill(HIST("GainQA/hSumOverCommonVsTimeZNA"), timeFromSOR, ratioA); + histos.fill(HIST("GainQA/hSumOverCommonVsTimeZNC"), timeFromSOR, ratioC); + for (int i = 0; i < 4; ++i) { + histos.fill(HIST("GainQA/pTowerOverCommonVsTimeZNA"), timeFromSOR, i + 0.5, znaCorr[i] / znaEnergycommon); + histos.fill(HIST("GainQA/pTowerOverCommonVsTimeZNC"), timeFromSOR, i + 0.5, zncCorr[i] / zncEnergycommon); + } + histos.fill(HIST("SpatialQA/pCrossVsTimeZNA"), timeFromSOR, 0.5, crossXA); + histos.fill(HIST("SpatialQA/pCrossVsTimeZNA"), timeFromSOR, 1.5, crossYA); + histos.fill(HIST("SpatialQA/pCrossVsTimeZNC"), timeFromSOR, 0.5, crossXC); + histos.fill(HIST("SpatialQA/pCrossVsTimeZNC"), timeFromSOR, 1.5, crossYC); + histos.fill(HIST("SpatialQA/pResponseVsCrossXYZNA"), crossLookupXA, crossLookupYA, ratioA); + histos.fill(HIST("SpatialQA/pResponseVsCrossXYZNC"), crossLookupXC, crossLookupYC, ratioC); + histos.fill(HIST("SpatialQA/pCentroidVsTimeZNA"), timeFromSOR, 0.5, centroidXA); + histos.fill(HIST("SpatialQA/pCentroidVsTimeZNA"), timeFromSOR, 1.5, centroidYA); + histos.fill(HIST("SpatialQA/pCentroidVsTimeZNC"), timeFromSOR, 0.5, centroidXC); + histos.fill(HIST("SpatialQA/pCentroidVsTimeZNC"), timeFromSOR, 1.5, centroidYC); + histos.fill(HIST("SpatialQA/hCrossXYZNA"), crossLookupXA, crossLookupYA); + histos.fill(HIST("SpatialQA/hCrossXYZNC"), crossLookupXC, crossLookupYC); + + histos.fill(HIST("SpatialQA/pTower1OverCommonVsCrossXYZNA"), crossLookupXA, crossLookupYA, znaCorr[0] / znaEnergycommon); + histos.fill(HIST("SpatialQA/pTower2OverCommonVsCrossXYZNA"), crossLookupXA, crossLookupYA, znaCorr[1] / znaEnergycommon); + histos.fill(HIST("SpatialQA/pTower3OverCommonVsCrossXYZNA"), crossLookupXA, crossLookupYA, znaCorr[2] / znaEnergycommon); + histos.fill(HIST("SpatialQA/pTower4OverCommonVsCrossXYZNA"), crossLookupXA, crossLookupYA, znaCorr[3] / znaEnergycommon); + + histos.fill(HIST("SpatialQA/pTower1OverCommonVsCrossXYZNC"), crossLookupXC, crossLookupYC, zncCorr[0] / zncEnergycommon); + histos.fill(HIST("SpatialQA/pTower2OverCommonVsCrossXYZNC"), crossLookupXC, crossLookupYC, zncCorr[1] / zncEnergycommon); + histos.fill(HIST("SpatialQA/pTower3OverCommonVsCrossXYZNC"), crossLookupXC, crossLookupYC, zncCorr[2] / zncEnergycommon); + histos.fill(HIST("SpatialQA/pTower4OverCommonVsCrossXYZNC"), crossLookupXC, crossLookupYC, zncCorr[3] / zncEnergycommon); + const double alphaZDC = 0.395; + double qxZDCA = 0.0; + double qxZDCC = 0.0; + double qyZDCA = 0.0; + double qyZDCC = 0.0; + double sumA = 0.0; + double sumC = 0.0; + + for (int i = 0; i < 4; ++i) { + double amplA = znaCorr[i]; + double amplC = zncCorr[i]; + if (followpub) { + amplA = std::pow(amplA, alphaZDC); + amplC = std::pow(amplC, alphaZDC); + } + qxZDCA -= amplA * x[i]; + qyZDCA += amplA * y[i]; + qxZDCC += amplC * x[i]; + qyZDCC += amplC * y[i]; + sumA += amplA; + sumC += amplC; + } + + if (sumA <= 1.e-4 || sumC <= 1.e-4) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + + double cZNA = 1.0; + double cZNC = 1.0; + if (usecfactor) { + const double beamEne = 5.36 * 0.5; + const double nSpecnA = sumEnergyA / beamEne; + const double nSpecnC = sumEnergyC / beamEne; + cZNA = 1.89358 - 0.71262 / (nSpecnA + 0.71789); + cZNC = 1.89358 - 0.71262 / (nSpecnC + 0.71789); + } + + qxZDCA = cZNA * qxZDCA / sumA; + qyZDCA = cZNA * qyZDCA / sumA; + qxZDCC = cZNC * qxZDCC / sumC; + qyZDCC = cZNC * qyZDCC / sumC; + + if (!std::isfinite(qxZDCA) || !std::isfinite(qyZDCA) || !std::isfinite(qxZDCC) || !std::isfinite(qyZDCC)) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + + std::array qValues = {qxZDCA, qyZDCA, qxZDCC, qyZDCC}; + + for (int i = 0; i < 4; ++i) { + const double component = i + 0.5; + histos.fill(HIST("QRecenteringQA/pQBeforeVsCentrality"), centrality, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQBeforeVsTime"), timeFromSOR, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQBeforeVsVx"), vx, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQBeforeVsVy"), vy, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQBeforeVsVz"), vz, component, qValues[i]); + } + + histos.fill(HIST("QCorrelationQA/hQxAQxCBefore"), qValues[0], qValues[2]); + histos.fill(HIST("QCorrelationQA/hQyAQyCBefore"), qValues[1], qValues[3]); + histos.fill(HIST("QCorrelationQA/hQxAQyCBefore"), qValues[0], qValues[3]); + histos.fill(HIST("QCorrelationQA/hQyAQxCBefore"), qValues[1], qValues[2]); + histos.fill(HIST("QCorrelationQA/pQACorrelationBeforeVsCentrality"), centrality, 0.5, qValues[0] * qValues[2]); + histos.fill(HIST("QCorrelationQA/pQACorrelationBeforeVsCentrality"), centrality, 1.5, qValues[1] * qValues[3]); + histos.fill(HIST("QCorrelationQA/pQACorrelationBeforeVsCentrality"), centrality, 2.5, qValues[0] * qValues[3]); + histos.fill(HIST("QCorrelationQA/pQACorrelationBeforeVsCentrality"), centrality, 3.5, qValues[1] * qValues[2]); + + if (calibrationStage.value == 2 && useGainCallib.value && useSpatialCalib.value && deriveQRecentering.value && !useQRecentering.value) { + if (qRecenteringMethod.value == 0) { + const auto qFeatures = makeQRecenteringFeatures(centrality, timeFromSOR, vx, vy, vz); + int moment = 0; + for (int i = 0; i < kQRecenteringNFeatures; ++i) { + for (int j = i; j < kQRecenteringNFeatures; ++j) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMoments"), centrality, moment + 0.5, qFeatures[i] * qFeatures[j]); + ++moment; + } + } + for (int component = 0; component < kQRecenteringNComponents; ++component) { + for (int i = 0; i < kQRecenteringNFeatures; ++i) { + const int index = kQRecenteringNMatrixMoments + component * kQRecenteringNFeatures + i; + histos.fill(HIST("QRecenteringCalibration/hRegressionMoments"), centrality, index + 0.5, qFeatures[i] * qValues[component]); + } + } + } else { + const auto qFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); + int moment = 0; + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + for (int j = i; j < kQRecenteringMultidimNFeatures; ++j) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsMultidim"), centrality, moment + 0.5, qFeatures[i] * qFeatures[j]); + ++moment; + } + } + for (int component = 0; component < kQRecenteringNComponents; ++component) { + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + const int index = kQRecenteringMultidimNMatrixMoments + component * kQRecenteringMultidimNFeatures + i; + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsMultidim"), centrality, index + 0.5, qFeatures[i] * qValues[component]); + } + } + + // Store only the compact per-bin sums needed to add conditional-mean + // flatness penalties in the post-processing builder. The first feature + // is 1, therefore sum(X_0) is the number of events in that bin. + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + const double value = qFeatures[i]; + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsTime"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVx"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVy"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVz"), centrality, vz, i + 0.5, value); + } + for (int component = 0; component < kQRecenteringNComponents; ++component) { + const int index = kQRecenteringMultidimNFeatures + component; + const double value = qValues[component]; + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsTime"), centrality, timeFromSOR, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVx"), centrality, vx, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVy"), centrality, vy, index + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hFlatnessMomentsVz"), centrality, vz, index + 0.5, value); + } + + // Structured regression moments. Events are deterministically split + // 50/50 using a mixed global-BC key, avoiding any dependence on the + // regression variables themselves. + const auto structuredFeatures = makeQRecenteringStructuredFeatures(timeFromSOR, vx, vy, vz); + uint64_t splitKey = globalBC + 0x9e3779b97f4a7c15ULL; + splitKey = (splitKey ^ (splitKey >> 30)) * 0xbf58476d1ce4e5b9ULL; + splitKey = (splitKey ^ (splitKey >> 27)) * 0x94d049bb133111ebULL; + splitKey = splitKey ^ (splitKey >> 31); + const bool isValidation = (splitKey & 1ULL) != 0ULL; + + int structuredMoment = 0; + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + for (int j = i; j < kQRecenteringStructuredNFeatures; ++j) { + const double value = structuredFeatures[i] * structuredFeatures[j]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, structuredMoment + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, structuredMoment + 0.5, value); + } + ++structuredMoment; + } + } + + for (int component = 0; component < kQRecenteringNComponents; ++component) { + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const int index = kQRecenteringStructuredNMatrixMoments + component * kQRecenteringStructuredNFeatures + i; + const double value = structuredFeatures[i] * qValues[component]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, index + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, index + 0.5, value); + } + } + + const int q2Index = kQRecenteringStructuredNMatrixMoments + kQRecenteringNComponents * kQRecenteringStructuredNFeatures + component; + const double q2Value = qValues[component] * qValues[component]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredValidation"), centrality, q2Index + 0.5, q2Value); + } else { + histos.fill(HIST("QRecenteringCalibration/hRegressionMomentsStructuredTrain"), centrality, q2Index + 0.5, q2Value); + } + } + + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const double value = structuredFeatures[i]; + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, i + 0.5, value); + } else { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, i + 0.5, value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, i + 0.5, value); + } + } + + for (int component = 0; component < kQRecenteringNComponents; ++component) { + const int qIndex = kQRecenteringStructuredNFeatures + component; + const int q2Index = kQRecenteringStructuredNFeatures + kQRecenteringNComponents + component; + const double qValue = qValues[component]; + const double q2Value = qValue * qValue; + + if (isValidation) { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeValidation"), centrality, timeFromSOR, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxValidation"), centrality, vx, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyValidation"), centrality, vy, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzValidation"), centrality, vz, q2Index + 0.5, q2Value); + } else { + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, qIndex + 0.5, qValue); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsTimeTrain"), centrality, timeFromSOR, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVxTrain"), centrality, vx, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVyTrain"), centrality, vy, q2Index + 0.5, q2Value); + histos.fill(HIST("QRecenteringCalibration/hStructuredFlatnessMomentsVzTrain"), centrality, vz, q2Index + 0.5, q2Value); + } + } + } + } + + bool qRecenteringOK = true; + if (calibrationStage.value == 2 && useQRecentering.value) { + if (!qRecenteringProfile) { + qRecenteringOK = false; + } else if (qRecenteringMethod.value == 0) { + const auto qFeatures = makeQRecenteringFeatures(centrality, timeFromSOR, vx, vy, vz); + for (int component = 0; component < kQRecenteringNComponents; ++component) { + double predictedBias = 0.0; + for (int i = 0; i < kQRecenteringNFeatures; ++i) { + const double coefficient = qRecenteringProfile->GetBinContent(qRecenteringProfile->FindBin(centrality, i + 0.5, component + 0.5)); + if (!std::isfinite(coefficient)) { + qRecenteringOK = false; + break; + } + predictedBias += coefficient * qFeatures[i]; + } + if (!qRecenteringOK || !std::isfinite(predictedBias)) { + qRecenteringOK = false; + break; + } + qValues[component] -= predictedBias; + } + } else if (qRecenteringMethod.value == 4) { + const auto qFeatures = makeQRecenteringStructuredFeatures(timeFromSOR, vx, vy, vz); + for (int component = 0; component < kQRecenteringNComponents; ++component) { + double predictedBias = 0.0; + for (int i = 0; i < kQRecenteringStructuredNFeatures; ++i) { + const double coefficient = qRecenteringProfile->GetBinContent(qRecenteringProfile->FindBin(centrality, i + 0.5, component + 0.5)); + if (!std::isfinite(coefficient)) { + qRecenteringOK = false; + break; + } + predictedBias += coefficient * qFeatures[i]; + } + if (!qRecenteringOK || !std::isfinite(predictedBias)) { + qRecenteringOK = false; + break; + } + qValues[component] -= predictedBias; + } + } else { + const auto qFeatures = makeQRecenteringMultidimFeatures(timeFromSOR, vx, vy, vz); + for (int component = 0; component < kQRecenteringNComponents; ++component) { + double predictedBias = 0.0; + for (int i = 0; i < kQRecenteringMultidimNFeatures; ++i) { + const double coefficient = qRecenteringProfile->GetBinContent(qRecenteringProfile->FindBin(centrality, i + 0.5, component + 0.5)); + if (!std::isfinite(coefficient)) { + qRecenteringOK = false; + break; + } + predictedBias += coefficient * qFeatures[i]; + } + if (!qRecenteringOK || !std::isfinite(predictedBias)) { + qRecenteringOK = false; + break; + } + qValues[component] -= predictedBias; + } + } + } + if (!qRecenteringOK) { + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + + for (int i = 0; i < 4; ++i) { + const double component = i + 0.5; + histos.fill(HIST("QRecenteringQA/pQAfterVsCentrality"), centrality, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQAfterVsTime"), timeFromSOR, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQAfterVsVx"), vx, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQAfterVsVy"), vy, component, qValues[i]); + histos.fill(HIST("QRecenteringQA/pQAfterVsVz"), vz, component, qValues[i]); + } + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityTime"), centrality, timeFromSOR, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityTime"), centrality, timeFromSOR, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityTime"), centrality, timeFromSOR, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityTime"), centrality, timeFromSOR, qValues[3]); + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityVx"), centrality, vx, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityVx"), centrality, vx, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityVx"), centrality, vx, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityVx"), centrality, vx, qValues[3]); + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityVy"), centrality, vy, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityVy"), centrality, vy, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityVy"), centrality, vy, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityVy"), centrality, vy, qValues[3]); + histos.fill(HIST("QRecentering2DQA/pQxAAfterVsCentralityVz"), centrality, vz, qValues[0]); + histos.fill(HIST("QRecentering2DQA/pQyAAfterVsCentralityVz"), centrality, vz, qValues[1]); + histos.fill(HIST("QRecentering2DQA/pQxCAfterVsCentralityVz"), centrality, vz, qValues[2]); + histos.fill(HIST("QRecentering2DQA/pQyCAfterVsCentralityVz"), centrality, vz, qValues[3]); + + histos.fill(HIST("QCorrelationQA/hQxAQxCAfter"), qValues[0], qValues[2]); + histos.fill(HIST("QCorrelationQA/hQyAQyCAfter"), qValues[1], qValues[3]); + histos.fill(HIST("QCorrelationQA/hQxAQyCAfter"), qValues[0], qValues[3]); + histos.fill(HIST("QCorrelationQA/hQyAQxCAfter"), qValues[1], qValues[2]); + histos.fill(HIST("QCorrelationQA/pQACorrelationAfterVsCentrality"), centrality, 0.5, qValues[0] * qValues[2]); + histos.fill(HIST("QCorrelationQA/pQACorrelationAfterVsCentrality"), centrality, 1.5, qValues[1] * qValues[3]); + histos.fill(HIST("QCorrelationQA/pQACorrelationAfterVsCentrality"), centrality, 2.5, qValues[0] * qValues[3]); + histos.fill(HIST("QCorrelationQA/pQACorrelationAfterVsCentrality"), centrality, 3.5, qValues[1] * qValues[2]); + + const double phiRecenteringA = std::atan2(qValues[1], qValues[0]); + const double phiRecenteringC = std::atan2(qValues[3], qValues[2]); + histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNA"), phiRecenteringA); + histos.fill(HIST("PhiQA/hPhiAfterRecenteringZNC"), phiRecenteringC); + histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNA"), centrality, phiRecenteringA); + histos.fill(HIST("PhiQA/hPhiAfterRecenteringVsCentralityZNC"), centrality, phiRecenteringC); + + if (useQWidthEqualization.value) { + const int centBin = qWidthProfile->GetXaxis()->FindFixBin(centrality); + if (centBin < 1 || centBin > qWidthProfile->GetNbinsX()) { + LOGF(fatal, "Invalid ZDC Q-width centrality bin for run %d: centrality=%.6f", runNumber, static_cast(centrality)); + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + std::array sigma{}; + for (int i = 0; i < 4; ++i) { + sigma[i] = qWidthProfile->GetBinContent(centBin, i + 1); + if (!std::isfinite(sigma[i]) || sigma[i] <= 0.0) { + LOGF(fatal, "Invalid ZDC Q-width sigma for run %d: centrality=%.6f, component=%d, sigma=%.6g", runNumber, static_cast(centrality), i, sigma[i]); + fillTable(false, -999.f, -999.f, -999.f, -999.f); + return; + } + } + const double factorA = std::sqrt(sigma[1] / sigma[0]); + const double factorC = std::sqrt(sigma[3] / sigma[2]); + qValues[0] *= factorA; + qValues[1] /= factorA; + qValues[2] *= factorC; + qValues[3] /= factorC; + for (int i = 0; i < 4; ++i) { + histos.fill(HIST("QWidthQA/pQAfterWidthEqVsCentrality"), centrality, i + 0.5, qValues[i]); + } + } + + const double phiFinalA = std::atan2(qValues[1], qValues[0]); + const double phiFinalC = std::atan2(qValues[3], qValues[2]); + if (useQWidthEqualization.value) { + histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNA"), centrality, phiFinalA); + histos.fill(HIST("PhiQA/hPhiAfterWidthEqVsCentralityZNC"), centrality, phiFinalC); + } + // ZDC resolution after all enabled corrections + const double qMagA = std::hypot(qValues[0], qValues[1]); + const double qMagC = std::hypot(qValues[2], qValues[3]); + if (centrality >= 0.f && centrality < 50.f && qMagA > 0. && qMagC > 0.) { + const double correlation = std::cos(phiFinalA - phiFinalC); + histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentrality"), centrality, correlation); + histos.fill(HIST("ResolutionQA/pCosPsiAPsiCVsCentralityVz"), centrality, vz, correlation); + } + + histos.fill(HIST("hEvtSelInfo"), 7.5); + fillTable(true, static_cast(qValues[0]), static_cast(qValues[2]), static_cast(qValues[1]), static_cast(qValues[3])); + histos.fill(HIST("hEvtSelInfo"), 8.5); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Common/zdcvector.cxx b/PWGLF/TableProducer/Common/zdcvector.cxx index 2d4627a36d9..ff67bf9f081 100644 --- a/PWGLF/TableProducer/Common/zdcvector.cxx +++ b/PWGLF/TableProducer/Common/zdcvector.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -58,6 +59,9 @@ using BCsRun3 = soa::Join; struct zdcvector { Produces zdccaltable; + Produces zdcenergytable; + Produces zdctimetable; + Produces zdcchargedtracks; // Configurables. struct : ConfigurableGroup { @@ -76,6 +80,12 @@ struct zdcvector { Configurable usemem{"usemem", true, "usemem"}; Configurable usecfactor{"usecfactor", false, "use c factor"}; + Configurable cfgTrackPtMin{"cfgTrackPtMin", 0.2f, "Minimum charged-track pT"}; + Configurable cfgTrackPtMax{"cfgTrackPtMax", 10.0f, "Maximum charged-track pT"}; + Configurable cfgTrackEtaMax{"cfgTrackEtaMax", 0.8f, "Maximum absolute eta"}; + Configurable cfgUseGlobalTracks{"cfgUseGlobalTracks", true, "Use global tracks"}; + Configurable storeChargedTracks{"storeChargedTracks", true, "Store selected charged tracks in a linked extra table"}; + struct : ConfigurableGroup { Configurable vzFineNbins{"vzFineNbins", 20, "Number of bins in Vz fine histograms"}; Configurable lfinebinVz{"lfinebinVz", -10.0, "lower bin value in Vz fine histograms"}; @@ -98,6 +108,9 @@ struct zdcvector { Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; } rctCut; + Configurable storeZdcEnergy{"storeZdcEnergy", true, "Store ZDC tower/common energies in a linked extra table"}; + Configurable storeZdcTime{"storeZdcTime", true, "Store timestamp and time from first event of run"}; + RCTFlagsChecker rctChecker; void init(o2::framework::InitContext&) @@ -135,13 +148,37 @@ struct zdcvector { ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); } + template + bool selectionTrack(const T& candidate) + { + if (!(candidate.isGlobalTrack() && + candidate.isPVContributor() && + candidate.itsNCls() > 3 && + candidate.tpcNClsFound() > 50.0 && + candidate.itsNClsInnerBarrel() >= 1)) { + return false; + } + + if (std::abs(candidate.dcaXY()) >= 0.1) { + return false; + } + + if (std::abs(candidate.dcaZ()) >= 0.1) { + return false; + } + + return true; + } + int currentRunNumber = -999; int lastRunNumber = -999; - TH2D* gainprofile; - TProfile* gainprofilevxy; + TH2D* gainprofile = nullptr; + TProfile* gainprofilevxy = nullptr; + int runNumberForSOR = -1; + uint64_t sorTimestamp = 0; // int lastRunNumberTimeRec = -999; - // for time since start of run + // for time since start of run // int runForStartTime = -999; // uint64_t runStartTime = 0; @@ -176,35 +213,114 @@ struct zdcvector { auto sumA = 0.0; auto sumC = 0.0; + // default values for events without ZDC + float znaEnergycommon = 0.f; + float zncEnergycommon = 0.f; + + float zna0 = 0.f; + float zna1 = 0.f; + float zna2 = 0.f; + float zna3 = 0.f; + + float znc0 = 0.f; + float znc1 = 0.f; + float znc2 = 0.f; + float znc3 = 0.f; + auto bc = collision.foundBC_as(); + const uint64_t timestampzdc = bc.timestamp(); // in milliseconds + + if (currentRunNumber != runNumberForSOR) { + + auto runDuration = ccdb->getRunDuration(currentRunNumber, true); + sorTimestamp = static_cast(runDuration.first); + runNumberForSOR = currentRunNumber; + + LOGF(info, + "Run %d: SOR timestamp = %llu ms", + currentRunNumber, + sorTimestamp); + } + + float timeInMinutes = 0.f; + + if (timestampzdc >= sorTimestamp && sorTimestamp > 0) { + timeInMinutes = + static_cast(timestampzdc - sorTimestamp) / 60000.f; + } + + // Helper to keep your early-return structure unchanged. + // Every time ZDCCalTables is filled, the optional linked energy table is also filled. + auto fillTables = [&](bool trigger, + float qxA, + float qxC, + float qyA, + float qyC) -> int32_t { + zdccaltable(trigger, + currentRunNumber, + centrality, + vx, + vy, + vz, + qxA, + qxC, + qyA, + qyC); + + const auto zdcCalIndex = static_cast(zdccaltable.lastIndex()); + + if (storeZdcEnergy) { + zdcenergytable(zdcCalIndex, + znaEnergycommon, + zncEnergycommon, + zna0, + zna1, + zna2, + zna3, + znc0, + znc1, + znc2, + znc3); + } + + if (storeZdcTime) { + zdctimetable(zdcCalIndex, + timestampzdc, + timeInMinutes); + } + + return zdcCalIndex; + }; if (!bc.has_zdc()) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); return; } - // -------- define time since run start -------- - /*uint64_t ts = bc.timestamp(); - if (currentRunNumber != runForStartTime) { - runStartTime = ts; - runForStartTime = currentRunNumber; - } - double tsec = 1.e-3 * static_cast(ts - runStartTime); - */ histos.fill(HIST("hEvtSelInfo"), 1.5); auto zdc = bc.zdc(); auto zncEnergy = zdc.energySectorZNC(); auto znaEnergy = zdc.energySectorZNA(); - auto zncEnergycommon = zdc.energyCommonZNC(); - auto znaEnergycommon = zdc.energyCommonZNA(); + zncEnergycommon = zdc.energyCommonZNC(); + znaEnergycommon = zdc.energyCommonZNA(); auto beamEne = 5.36 * 0.5; + zna0 = znaEnergy[0]; + zna1 = znaEnergy[1]; + zna2 = znaEnergy[2]; + zna3 = znaEnergy[3]; + + znc0 = zncEnergy[0]; + znc1 = zncEnergy[1]; + znc2 = zncEnergy[2]; + znc3 = zncEnergy[3]; if (znaEnergycommon <= 0.0 || zncEnergycommon <= 0.0) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); - // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, znaEnergycommon, zncEnergycommon, znaEnergy[0], znaEnergy[1], znaEnergy[2], znaEnergy[3], zncEnergy[0], zncEnergy[1], zncEnergy[2], zncEnergy[3]); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); return; } @@ -212,16 +328,16 @@ struct zdcvector { if (znaEnergy[0] <= 0.0 || znaEnergy[1] <= 0.0 || znaEnergy[2] <= 0.0 || znaEnergy[3] <= 0.0) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); - // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, znaEnergycommon, zncEnergycommon, znaEnergy[0], znaEnergy[1], znaEnergy[2], znaEnergy[3], zncEnergy[0], zncEnergy[1], zncEnergy[2], zncEnergy[3]); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); return; } histos.fill(HIST("hEvtSelInfo"), 3.5); if (zncEnergy[0] <= 0.0 || zncEnergy[1] <= 0.0 || zncEnergy[2] <= 0.0 || zncEnergy[3] <= 0.0) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); - // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, znaEnergycommon, zncEnergycommon, znaEnergy[0], znaEnergy[1], znaEnergy[2], znaEnergy[3], zncEnergy[0], zncEnergy[1], zncEnergy[2], zncEnergy[3]); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); return; } @@ -229,25 +345,17 @@ struct zdcvector { if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); - // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, znaEnergycommon, zncEnergycommon, znaEnergy[0], znaEnergy[1], znaEnergy[2], znaEnergy[3], zncEnergy[0], zncEnergy[1], zncEnergy[2], zncEnergy[3]); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); return; } histos.fill(HIST("hEvtSelInfo"), 5.5); - /*histos.fill(HIST("hZNA0"), centrality, znaEnergy[0]); - histos.fill(HIST("hZNA1"), centrality, znaEnergy[1]); - histos.fill(HIST("hZNA2"), centrality, znaEnergy[2]); - histos.fill(HIST("hZNA3"), centrality, znaEnergy[3]); - histos.fill(HIST("hZNC0"), centrality, zncEnergy[0]); - histos.fill(HIST("hZNC1"), centrality, zncEnergy[1]); - histos.fill(HIST("hZNC2"), centrality, zncEnergy[2]); - histos.fill(HIST("hZNC3"), centrality, zncEnergy[3]);*/ if (additionalEvSel && (!collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); - // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, znaEnergycommon, zncEnergycommon, znaEnergy[0], znaEnergy[1], znaEnergy[2], znaEnergy[3], zncEnergy[0], zncEnergy[1], zncEnergy[2], zncEnergy[3]); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); return; } @@ -284,7 +392,8 @@ struct zdcvector { if (znaEnergy[iChA] <= 0.0) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); return; } else { double ampl = gainequal * znaEnergy[iChA]; @@ -300,8 +409,8 @@ struct zdcvector { } else { if (zncEnergy[iChA - 4] <= 0.0) { triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); - // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, znaEnergycommon, zncEnergycommon, znaEnergy[0], znaEnergy[1], znaEnergy[2], znaEnergy[3], zncEnergy[0], zncEnergy[1], zncEnergy[2], zncEnergy[3]); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); return; } else { double ampl = gainequal * zncEnergy[iChA - 4]; @@ -345,8 +454,8 @@ struct zdcvector { qyZDCA = 0.0; qyZDCC = 0.0; triggerevent = false; - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); - // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, znaEnergycommon, zncEnergycommon, znaEnergy[0], znaEnergy[1], znaEnergy[2], znaEnergy[3], zncEnergy[0], zncEnergy[1], zncEnergy[2], zncEnergy[3]); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, 0.0, 0.0, 0.0, 0.0); + fillTables(triggerevent, 0.f, 0.f, 0.f, 0.f); return; } @@ -370,7 +479,43 @@ struct zdcvector { lastRunNumber = currentRunNumber; } - zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, qxZDCA, qxZDCC, qyZDCA, qyZDCC); + // zdccaltable(triggerevent, currentRunNumber, centrality, vx, vy, vz, qxZDCA, qxZDCC, qyZDCA, qyZDCC); + const auto zdcCalIndex = fillTables(triggerevent, + qxZDCA, + qxZDCC, + qyZDCA, + qyZDCC); + + // Do not write tracks for events rejected by your ZDC/event selection + if (!triggerevent || !storeChargedTracks) { + return; + } + + // In this process signature, "tracks" are the tracks associated with + // the current collision. + for (const auto& track : tracks) { + if (!selectionTrack(track)) { + continue; + } + + if (track.pt() < cfgTrackPtMin || track.pt() > cfgTrackPtMax) { + continue; + } + + if (track.sign() == 0) { + continue; + } + + if (std::abs(track.eta()) > cfgTrackEtaMax) { + continue; + } + + zdcchargedtracks(zdcCalIndex, + track.px(), + track.py(), + track.pz(), + static_cast(track.sign())); + } } }; diff --git a/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt new file mode 100644 index 00000000000..a93df119cbc --- /dev/null +++ b/PWGLF/TableProducer/GlobalEventProperties/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(event-shape-coex + SOURCES eventShapeCoex.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx new file mode 100644 index 00000000000..c8f66fce50f --- /dev/null +++ b/PWGLF/TableProducer/GlobalEventProperties/eventShapeCoex.cxx @@ -0,0 +1,618 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file eventShapeCoex.cxx +/// \brief Moment sums of the forward/backward sub-event mean pT of mid-rapidity tracks +/// \author Cristian Andrei +/// +/// First O2Physics task of the AliPhysics PWGLF/SPECTRA/MultEvShape analyses. + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +constexpr float PtMin = 0.15f; // GeV/c +constexpr float EtaMax = 0.8f; +constexpr double MeanPtShift = 0.6; // GeV/c + +// Sub-events are unions of |eta| x pT rings; the last pT ring is open-ended +constexpr int NEtaRings = 5; +constexpr std::array EtaRingEdges = {0.f, 0.2f, 0.3f, 0.4f, 0.5f, EtaMax}; +constexpr int NPtRings = 6; +constexpr std::array PtRingLowEdges = {PtMin, 1.f, 1.5f, 2.f, 3.f, 5.f}; +constexpr int EtaFirstNominal = 1; // |eta| > 0.2 +constexpr int PtEndNominal = 3; // pT < 2 GeV/c + +enum Side { kF = 0, + kB, + NSides }; + +// Monomial exponents of (xF, xB, vF, vB); the order is part of the output format +constexpr int NMono1 = 16; +constexpr int NMono = 81; +constexpr std::array, NMono> MonoExp = {{{0, 0, 0, 0}, {1, 0, 0, 0}, {0, 1, 0, 0}, {2, 0, 0, 0}, {0, 2, 0, 0}, {1, 1, 0, 0}, {2, 1, 0, 0}, {1, 2, 0, 0}, {2, 2, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}, {0, 1, 1, 0}, {1, 0, 0, 1}, {0, 2, 1, 0}, {2, 0, 0, 1}, {0, 0, 1, 1}, {0, 0, 0, 2}, {0, 0, 1, 2}, {0, 0, 2, 0}, {0, 0, 2, 1}, {0, 0, 2, 2}, {0, 1, 0, 1}, {0, 1, 1, 1}, {0, 1, 2, 0}, {0, 1, 2, 1}, {0, 2, 0, 1}, {0, 2, 1, 1}, {0, 2, 2, 0}, {0, 2, 2, 1}, {0, 3, 0, 0}, {0, 3, 1, 0}, {0, 3, 2, 0}, {0, 4, 0, 0}, {0, 4, 1, 0}, {0, 4, 2, 0}, {1, 0, 0, 2}, {1, 0, 1, 0}, {1, 0, 1, 1}, {1, 0, 1, 2}, {1, 1, 0, 1}, {1, 1, 1, 0}, {1, 1, 1, 1}, {1, 2, 0, 1}, {1, 2, 1, 0}, {1, 2, 1, 1}, {1, 3, 0, 0}, {1, 3, 1, 0}, {1, 4, 0, 0}, {1, 4, 1, 0}, {2, 0, 0, 2}, {2, 0, 1, 0}, {2, 0, 1, 1}, {2, 0, 1, 2}, {2, 1, 0, 1}, {2, 1, 1, 0}, {2, 1, 1, 1}, {2, 2, 0, 1}, {2, 2, 1, 0}, {2, 2, 1, 1}, {2, 3, 0, 0}, {2, 3, 1, 0}, {2, 4, 0, 0}, {2, 4, 1, 0}, {3, 0, 0, 0}, {3, 0, 0, 1}, {3, 0, 0, 2}, {3, 1, 0, 0}, {3, 1, 0, 1}, {3, 2, 0, 0}, {3, 2, 0, 1}, {3, 3, 0, 0}, {3, 4, 0, 0}, {4, 0, 0, 0}, {4, 0, 0, 1}, {4, 0, 0, 2}, {4, 1, 0, 0}, {4, 1, 0, 1}, {4, 2, 0, 0}, {4, 2, 0, 1}, {4, 3, 0, 0}, {4, 4, 0, 0}}}; + +// Event-selection bits and the pile-up selection variants built from them +constexpr int NQaBits = 12; +constexpr std::array QaBitOrder = { + o2::aod::evsel::kNoSameBunchPileup, o2::aod::evsel::kIsGoodZvtxFT0vsPV, + o2::aod::evsel::kIsVertexITSTPC, o2::aod::evsel::kIsVertexTOFmatched, + o2::aod::evsel::kNoCollInTimeRangeNarrow, o2::aod::evsel::kNoCollInTimeRangeStrict, + o2::aod::evsel::kNoCollInTimeRangeStandard, o2::aod::evsel::kNoCollInRofStrict, + o2::aod::evsel::kNoCollInRofStandard, o2::aod::evsel::kNoHighMultCollInPrevRof, + o2::aod::evsel::kNoITSROFrameBorder, o2::aod::evsel::kNoTimeFrameBorder}; +enum EvVariant { kV1 = 0, + kV0, + kV3, + kV6, + NEvVariants }; +constexpr std::array EvVariantMask = { + (1u << 0) | (1u << 1), + 0u, + (1u << 0) | (1u << 1) | (1u << 6), + (1u << 0) | (1u << 1) | (1u << 2) | (1u << 6) | (1u << 8) | (1u << 9)}; +constexpr uint16_t AllQaBits = 0xFFFF; + +// Track count defining the stratum +enum Conditioning { kCondOwn = 0, // own sub-events + kCondAll, // all tracks, gap included + kCondUnwindowed }; // nominal gap, no upper pT cut + +struct Config { + const char* name; + int etaFirst; // |eta| rings [etaFirst, NEtaRings) + int ptEnd; // pT rings [0, ptEnd) + Conditioning cond; // stratum count + int validEtaFirst; // sub-events that must hold MinTracksValid tracks + int validPtEnd; + EvVariant evVariant; // pile-up selection +}; +constexpr int NConfigs = 13; +constexpr int INominal = 0; +constexpr int IPairFirst = 1; // GAP02 +constexpr int IPairSecond = 4; // GAP05, same events and stratum count as GAP02 +constexpr std::array Configs = {{{.name = "NOM", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP02", .etaFirst = 1, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP03", .etaFirst = 2, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP04", .etaFirst = 3, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "GAP05", .etaFirst = 4, .ptEnd = 3, .cond = kCondAll, .validEtaFirst = 4, .validPtEnd = 3, .evVariant = kV1}, + {.name = "PT10", .etaFirst = 1, .ptEnd = 1, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT15", .etaFirst = 1, .ptEnd = 2, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT20", .etaFirst = 1, .ptEnd = 3, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT30", .etaFirst = 1, .ptEnd = 4, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "PT50", .etaFirst = 1, .ptEnd = 5, .cond = kCondUnwindowed, .validEtaFirst = 1, .validPtEnd = 1, .evVariant = kV1}, + {.name = "EVV0", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV0}, + {.name = "EVV3", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV3}, + {.name = "EVV6", .etaFirst = 1, .ptEnd = 3, .cond = kCondOwn, .validEtaFirst = 1, .validPtEnd = 3, .evVariant = kV6}}}; + +constexpr int NCountBins = 64; // larger counts in the overflow bin +constexpr int NSubCountBins = 32; // per sub-event +constexpr int MinTracksValid = 2; // per sub-event +constexpr int NCovariateQuantities = 7; + +// Data halves and subsample groups, defined on blocks of bunch crossings +constexpr int NHalves = 2; +constexpr std::array HalfName = {"A", "B_SEALED"}; +constexpr int HalfBlockShift = 26; // 2^26 BC = 1.7 s +constexpr int GroupBlockShift = 20; // 2^20 BC = 26 ms +constexpr int NGroups = 20; + +// MC only: nominal sums split by two hashes of the MC collision (run, globalBC) +constexpr int NSplitBins = 8; +constexpr int NSubsamples = 16; +constexpr uint64_t SplitSalt = 0x6a09e667f3bcc909ULL; + +/// splitmix64 finaliser +constexpr uint64_t mix64(uint64_t z) +{ + z += 0x9e3779b97f4a7c15ULL; + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; + return z ^ (z >> 31); +} + +/// Hash of the (run, block of 2^shift bunch crossings) of an event +uint64_t blockHash(int run, uint64_t globalBC, int shift) +{ + return mix64(mix64(static_cast(run)) ^ (globalBC >> shift)); +} + +/// Per-event hash, independent of the block hashes +uint64_t eventHash(int run, uint64_t globalBC) +{ + return mix64(mix64(static_cast(run) ^ SplitSalt) ^ globalBC); +} + +struct SubEvent { + int n = 0; + double sumPt = 0.; + double sumPt2 = 0.; +}; + +/// Track sums of one event per (side, |eta| ring, pT ring) +struct Rings { + std::array ring{}; + + void add(float eta, double pt) + { + const float absEta = std::abs(eta); + int iEta = 0; + while (iEta < NEtaRings - 1 && absEta > EtaRingEdges[iEta + 1]) { + ++iEta; + } + int iPt = 0; + while (iPt < NPtRings - 1 && pt >= PtRingLowEdges[iPt + 1]) { + ++iPt; + } + auto& r = ring[((eta > 0.f ? kF : kB) * NEtaRings + iEta) * NPtRings + iPt]; + ++r.n; + r.sumPt += pt; + r.sumPt2 += pt * pt; + } + + /// Sub-event of one side: |eta| rings [etaFirst, NEtaRings), pT rings [0, ptEnd) + [[nodiscard]] SubEvent sum(int side, int etaFirst, int ptEnd) const + { + SubEvent s; + for (int iEta = etaFirst; iEta < NEtaRings; ++iEta) { + for (int iPt = 0; iPt < ptEnd; ++iPt) { + const auto& r = ring[(side * NEtaRings + iEta) * NPtRings + iPt]; + s.n += r.n; + s.sumPt += r.sumPt; + s.sumPt2 += r.sumPt2; + } + } + return s; + } + + [[nodiscard]] int count(int etaFirst, int ptEnd) const { return sum(kF, etaFirst, ptEnd).n + sum(kB, etaFirst, ptEnd).n; } +}; + +/// x = - MeanPtShift, v = pT variance / n (0 for n < 2) +void observe(const SubEvent& s, double& x, double& v) +{ + x = s.sumPt / s.n - MeanPtShift; + v = 0.; + if (s.n > 1) { + const double var = (s.sumPt2 - s.sumPt * s.sumPt / s.n) / (s.n - 1); + v = std::max(var, 0.) / s.n; + } +} + +/// The first nMono monomials of (xF, xB, vF, vB) +void monomials(double xF, double xB, double vF, double vB, int nMono, std::array& m) +{ + const std::array pxF = {1., xF, xF * xF, xF * xF * xF, xF * xF * xF * xF}; + const std::array pxB = {1., xB, xB * xB, xB * xB * xB, xB * xB * xB * xB}; + const std::array pvF = {1., vF, vF * vF}; + const std::array pvB = {1., vB, vB * vB}; + for (int k = 0; k < nMono; ++k) { + const auto& e = MonoExp[k]; + m[k] = pxF[e[0]] * pxB[e[1]] * pvF[e[2]] * pvB[e[3]]; + } +} + +/// Moment sums of one reconstructed half or of the generated events +struct Sums { + std::array, NConfigs> config{}; // (N_fwd, stratum count, monomial) + std::shared_ptr strata3; // (N_fwd, n_F, n_B, first-order monomial) + std::shared_ptr occupancy; // (configuration, N_fwd, stratum count) + std::shared_ptr paired; // (N_fwd, stratum count, GAP02 x GAP05 first-order monomials) +}; + +/// Nominal-configuration observables of an event +struct Nominal { + bool valid = false; + double nFwd = 0.; + int nMid = 0; + double xF = 0., xB = 0., vF = 0., vB = 0.; +}; + +enum EventFlowBin { kFlowAll = 0, + kFlowSel8, + kFlowVtxZ, + kFlowPileupV1, + kFlowNominalValid, + kFlowHalfA, + kFlowHalfB, + kFlowNoMcLabel, + NEventFlowBins }; +constexpr std::array EventFlowLabels = {"all", "sel8", "|z|", "pile-up V1", "nominal valid", "half A", "half B", "nominal valid, no MC label"}; +} // namespace + +struct EventShapeCoex { + HistogramRegistry registry{"registry"}; + Service pdg{}; + Service ccdb{}; + ctpRateFetcher rateFetcher; + + Configurable vtxZCut{"vtxZCut", 10.0f, "Max |PV z| (cm)"}; + Configurable etaFwdAMin{"etaFwdAMin", 3.5f, "FT0-A proxy: lower eta edge (generator level)"}; + Configurable etaFwdAMax{"etaFwdAMax", 4.9f, "FT0-A proxy: upper eta edge (generator level)"}; + Configurable etaFwdCMin{"etaFwdCMin", -3.3f, "FT0-C proxy: lower eta edge (generator level)"}; + Configurable etaFwdCMax{"etaFwdCMax", -2.1f, "FT0-C proxy: upper eta edge (generator level)"}; + ConfigurableAxis axisNFwd{"axisNFwd", {50, 1., 30000.}, "N_fwd = FT0A + FT0C amplitude (a fixed-width setting is made logarithmic)"}; + ConfigurableAxis axisNFwdGen{"axisNFwdGen", {50, 1., 1000.}, "generator-level N_fwd = charged primaries in the FT0 acceptance (a fixed-width setting is made logarithmic)"}; + ConfigurableAxis axisRun{"axisRun", {60000, 519999.5, 579999.5}, "run number (QA)"}; + ConfigurableAxis axisOccupancy{"axisOccupancy", {50, 0., 1000.}, "track occupancy in time range (QA; unset in underflow)"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -10., 10.}, "PV z (cm, QA)"}; + ConfigurableAxis axisRate{"axisRate", {100, 0., 1000.}, "interaction rate (kHz, QA; unavailable in underflow)"}; + Configurable irSource{"irSource", "T0VTX", "interaction-rate estimator for QA; empty = not fetched"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB url"}; + Configurable doSplitSums{"doSplitSums", true, "MC: nominal sums split by hashed event bins and subsamples"}; + + Filter trackFilter = (aod::track::pt > PtMin) && (nabs(aod::track::eta) < EtaMax) && requireGlobalTrackInFilter(); + + using MyCollisions = soa::Join; + using MyCollisionsMc = soa::Join; + using MyTracks = soa::Filtered>; + + std::array sumsReco{}; + std::array, NHalves> groupSums{}; // (N_fwd, stratum count, group x first-order monomial) + Sums sumsGen{}; + + /// Nominal sums of MC events split by hashed event bin (full) and by (subsample, event bin) (sub) + struct SplitSums { + std::shared_ptr full; // (N_fwd, stratum count, event bin x monomial) + std::shared_ptr sub; // (N_fwd, stratum count, (subsample x event bin) x first-order monomial) + }; + SplitSums splitReco{}, splitGen{}; + + void bookSplit(SplitSums& split, const std::string& dir, const AxisSpec& nFwdAxis) + { + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + split.full = registry.add(dir + "/full", "NOM sums per event bin", HistType::kTH3D, {nFwdAxis, countAxis, {NSplitBins * NMono, -0.5, NSplitBins * NMono - 0.5, "event bin x monomial"}}); + split.sub = registry.add(dir + "/sub", "NOM first-order sums per subsample and event bin", HistType::kTH3D, {nFwdAxis, countAxis, {NSubsamples * NSplitBins * NMono1, -0.5, NSubsamples * NSplitBins * NMono1 - 0.5, "(subsample x event bin) x monomial"}}); + split.full->SetBit(TH1::kIsNotW); + split.sub->SetBit(TH1::kIsNotW); + } + + void fillSplit(SplitSums& split, const Nominal& nominal, int run, uint64_t globalBC) + { + const uint64_t h = eventHash(run, globalBC); + const int bin = static_cast(h % NSplitBins); + const int subsample = static_cast((h >> 32) % NSubsamples); + std::array m{}; + monomials(nominal.xF, nominal.xB, nominal.vF, nominal.vB, NMono, m); + for (int k = 0; k < NMono; ++k) { + split.full->Fill(nominal.nFwd, nominal.nMid, bin * NMono + k, m[k]); + } + for (int k = 0; k < NMono1; ++k) { + split.sub->Fill(nominal.nFwd, nominal.nMid, (subsample * NSplitBins + bin) * NMono1 + k, m[k]); + } + } + + /// Books the moment sums of one level under dir// + void bookSums(Sums& sums, const std::string& dir, const std::string& half, const AxisSpec& nFwdAxis, int nConfigs) + { + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + const AxisSpec subCountAxis{NSubCountBins, -0.5, NSubCountBins - 0.5, "sub-event count"}; + const AxisSpec monoAxis{NMono, -0.5, NMono - 0.5, "monomial"}; + const AxisSpec mono1Axis{NMono1, -0.5, NMono1 - 0.5, "monomial"}; + const AxisSpec configAxis{NConfigs, -0.5, NConfigs - 0.5, "configuration"}; + std::string label = "[half "; + label += half; + label += "] moment sums "; + for (int c = 0; c < nConfigs; ++c) { + std::string name = dir; + name += "/"; + name += Configs[c].name; + name += "/"; + name += half; + std::string title = label; + title += Configs[c].name; + sums.config[c] = registry.add(name, title.c_str(), HistType::kTH3D, {nFwdAxis, countAxis, monoAxis}); + sums.config[c]->SetBit(TH1::kIsNotW); // no Sumw2 + } + sums.strata3 = registry.add(dir + "/NOM3/" + half, (label + "NOM in (n_F, n_B) strata").c_str(), HistType::kTHnD, {nFwdAxis, subCountAxis, subCountAxis, mono1Axis}); + sums.occupancy = registry.add(dir + "/occupancy/" + half, "event counts per configuration", HistType::kTH3D, {configAxis, nFwdAxis, countAxis}); + if (nConfigs > IPairSecond) { + std::string pairName = dir; + pairName += "/"; + pairName += Configs[IPairFirst].name; + pairName += "x"; + pairName += Configs[IPairSecond].name; + pairName += "/"; + pairName += half; + sums.paired = registry.add(pairName, (label + "cross products of two configurations").c_str(), HistType::kTH3D, {nFwdAxis, countAxis, {NMono1 * NMono1, -0.5, NMono1 * NMono1 - 0.5, "monomial pair"}}); + sums.paired->SetBit(TH1::kIsNotW); + } + } + + void init(InitContext const&) + { + if (doprocessReco && doprocessRecoMC) { + LOGF(fatal, "processReco and processRecoMC both fill the reconstructed-level sums; enable only one of them."); + } + + if (doprocessReco || doprocessRecoMC) { + AxisSpec nFwdAxis{axisNFwd, "#it{N}_{fwd} (FT0A+FT0C amplitude)"}; + if (nFwdAxis.nBins) { + nFwdAxis.makeLogarithmic(); + } + const AxisSpec countAxis{NCountBins, -0.5, NCountBins - 0.5, "stratum count"}; + const AxisSpec groupMonoAxis{NGroups * NMono1, -0.5, NGroups * NMono1 - 0.5, "group x monomial"}; + for (int h = 0; h < NHalves; ++h) { + bookSums(sumsReco[h], "accum", HalfName[h], nFwdAxis, NConfigs); + groupSums[h] = registry.add(std::string("groups/") + HalfName[h], "nominal first-order sums per block group", HistType::kTH3D, {nFwdAxis, countAxis, groupMonoAxis}); + groupSums[h]->SetBit(TH1::kIsNotW); + } + if (doprocessRecoMC && doSplitSums) { + bookSplit(splitReco, "split", nFwdAxis); + } + + // QA + constexpr int NFlowBins = NEventFlowBins; + auto hFlow = registry.add("qa/hEventFlow", "events", HistType::kTH1D, {{NFlowBins, -0.5, NFlowBins - 0.5}}); + for (int b = 0; b < NFlowBins; ++b) { + hFlow->GetXaxis()->SetBinLabel(b + 1, EventFlowLabels[b]); + } + registry.add("qa/hQaBits", "events with qaBits bit i set (order: QaBitOrder)", HistType::kTH1D, {{NQaBits, -0.5, NQaBits - 0.5}}); + registry.add("qa/hNFwd", "selected events", HistType::kTH1D, {nFwdAxis}); + AxisSpec ptAxis{100, PtMin, 20., "#it{p}_{T} (GeV/#it{c})"}; + ptAxis.makeLogarithmic(); + registry.add("qa/hTrackPt", "selected tracks", HistType::kTH1D, {ptAxis}); + registry.add("qa/hTrackEta", "selected tracks", HistType::kTH1D, {{160, -EtaMax, EtaMax, "#eta"}}); + registry.add("qa/hTrackPhi", "selected tracks", HistType::kTH1D, {{72, 0., o2::constants::math::TwoPI, "#varphi"}}); + // quantities: count, a_F, a_B, v_F, v_B + const AxisSpec quantityAxis{5, -0.5, 4.5, "quantity"}; + registry.add("qa/hMarginalSums", "nominal single-side sums", HistType::kTH3D, {nFwdAxis, {NCountBins, -0.5, NCountBins - 0.5, "n_{mid}"}, quantityAxis})->SetBit(TH1::kIsNotW); + registry.add("qa/hRunSums", "nominal single-side sums per run", HistType::kTH2D, {{axisRun, "run"}, quantityAxis})->SetBit(TH1::kIsNotW); + // quantities: count, a_F, a_B, a_F^2, a_B^2, v_F, v_B + const AxisSpec quantity2Axis{NCovariateQuantities, -0.5, NCovariateQuantities - 0.5, "quantity"}; + registry.add("qa/hOccupancySums", "nominal single-side sums vs occupancy", HistType::kTH2D, {{axisOccupancy, "track occupancy"}, quantity2Axis})->SetBit(TH1::kIsNotW); + registry.add("qa/hVtxZSums", "nominal single-side sums vs PV z", HistType::kTH2D, {{axisVtxZ, "PV z (cm)"}, quantity2Axis})->SetBit(TH1::kIsNotW); + registry.add("qa/hRateSums", "nominal single-side sums vs interaction rate", HistType::kTH2D, {{axisRate, "IR (kHz)"}, quantity2Axis})->SetBit(TH1::kIsNotW); + ccdb->setURL(ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + } + + if (doprocessMC) { + AxisSpec nFwdGenAxis{axisNFwdGen, "#it{N}_{fwd} (charged primaries in FT0A+FT0C)"}; + if (nFwdGenAxis.nBins) { + nFwdGenAxis.makeLogarithmic(); + } + bookSums(sumsGen, "gen", "all", nFwdGenAxis, 1); // nominal configuration only + registry.add("gen/hEvents", "generated events", HistType::kTH1D, {{1, -0.5, 0.5}}); + if (doSplitSums) { + bookSplit(splitGen, "gen/split", nFwdGenAxis); + } + } + } + + /// Fills the sums of every valid configuration; returns the nominal observables + Nominal fillSums(Sums& sums, const Rings& rings, double nFwd, uint16_t qaBits, int nConfigs) + { + Nominal nominal; + std::array m{}; + std::array mPairFirst{}; + bool pairFirstValid = false; + double xF = 0., xB = 0., vF = 0., vB = 0.; + for (int c = 0; c < nConfigs; ++c) { + const auto& cfg = Configs[c]; + const uint16_t mask = EvVariantMask[cfg.evVariant]; + if ((qaBits & mask) != mask) { + continue; + } + if (rings.sum(kF, cfg.validEtaFirst, cfg.validPtEnd).n < MinTracksValid || rings.sum(kB, cfg.validEtaFirst, cfg.validPtEnd).n < MinTracksValid) { + continue; + } + const SubEvent fwd = rings.sum(kF, cfg.etaFirst, cfg.ptEnd); + const SubEvent bwd = rings.sum(kB, cfg.etaFirst, cfg.ptEnd); + int nCond = fwd.n + bwd.n; + if (cfg.cond == kCondAll) { + nCond = rings.count(0, PtEndNominal); + } else if (cfg.cond == kCondUnwindowed) { + nCond = rings.count(EtaFirstNominal, NPtRings); + } + observe(fwd, xF, vF); + observe(bwd, xB, vB); + monomials(xF, xB, vF, vB, NMono, m); + for (int k = 0; k < NMono; ++k) { + sums.config[c]->Fill(nFwd, nCond, k, m[k]); + } + sums.occupancy->Fill(c, nFwd, nCond); + if (c == IPairFirst) { + mPairFirst = m; + pairFirstValid = true; + } else if (c == IPairSecond && pairFirstValid) { + for (int k = 0; k < NMono1; ++k) { + for (int l = 0; l < NMono1; ++l) { + sums.paired->Fill(nFwd, nCond, k * NMono1 + l, mPairFirst[k] * m[l]); + } + } + } + if (c == INominal) { + nominal = {.valid = true, .nFwd = nFwd, .nMid = nCond, .xF = xF, .xB = xB, .vF = vF, .vB = vB}; + } + } + + // (n_F, n_B) strata, n_F, n_B >= 1 + const uint16_t maskNominal = EvVariantMask[Configs[INominal].evVariant]; + const SubEvent fwd = rings.sum(kF, EtaFirstNominal, PtEndNominal); + const SubEvent bwd = rings.sum(kB, EtaFirstNominal, PtEndNominal); + if ((qaBits & maskNominal) == maskNominal && fwd.n > 0 && bwd.n > 0) { + observe(fwd, xF, vF); + observe(bwd, xB, vB); + monomials(xF, xB, vF, vB, NMono1, m); + std::array coord = {nFwd, static_cast(fwd.n), static_cast(bwd.n), 0.}; + for (int k = 0; k < NMono1; ++k) { + coord[3] = k; + sums.strata3->Fill(coord.data(), m[k]); + } + } + return nominal; + } + + /// Fills the reconstructed-level sums; returns the nominal observables + template + Nominal fillReco(TCollision const& coll, MyTracks const& tracks) + { + registry.fill(HIST("qa/hEventFlow"), kFlowAll); + if (!coll.sel8()) { + return {}; + } + registry.fill(HIST("qa/hEventFlow"), kFlowSel8); + if (std::abs(coll.posZ()) > vtxZCut) { + return {}; + } + registry.fill(HIST("qa/hEventFlow"), kFlowVtxZ); + + uint16_t qaBits = 0; + for (int i = 0; i < NQaBits; ++i) { + if (coll.selection_bit(QaBitOrder[i])) { + qaBits |= static_cast(1u << i); + registry.fill(HIST("qa/hQaBits"), i); + } + } + if ((qaBits & EvVariantMask[kV1]) == EvVariantMask[kV1]) { + registry.fill(HIST("qa/hEventFlow"), kFlowPileupV1); + } + + Rings rings; + for (const auto& track : tracks) { + rings.add(track.eta(), track.pt()); + registry.fill(HIST("qa/hTrackPt"), track.pt()); + registry.fill(HIST("qa/hTrackEta"), track.eta()); + registry.fill(HIST("qa/hTrackPhi"), track.phi()); + } + + const auto& bc = coll.template bc_as(); + const double nFwd = coll.multFT0A() + coll.multFT0C(); + registry.fill(HIST("qa/hNFwd"), nFwd); + const int half = static_cast(blockHash(bc.runNumber(), bc.globalBC(), HalfBlockShift) & 1u); + const Nominal nominal = fillSums(sumsReco[half], rings, nFwd, qaBits, NConfigs); + if (!nominal.valid) { + return nominal; + } + registry.fill(HIST("qa/hEventFlow"), kFlowNominalValid); + registry.fill(HIST("qa/hEventFlow"), kFlowHalfA + half); + + // subsample groups + const int group = static_cast(blockHash(bc.runNumber(), bc.globalBC(), GroupBlockShift) % NGroups); + std::array m{}; + monomials(nominal.xF, nominal.xB, nominal.vF, nominal.vB, NMono1, m); + for (int k = 0; k < NMono1; ++k) { + groupSums[half]->Fill(nFwd, nominal.nMid, group * NMono1 + k, m[k]); + } + + const std::array quantities = {1., nominal.xF + MeanPtShift, nominal.xB + MeanPtShift, nominal.vF, nominal.vB}; + for (int q = 0; q < static_cast(quantities.size()); ++q) { + registry.fill(HIST("qa/hMarginalSums"), nFwd, nominal.nMid, q, quantities[q]); + registry.fill(HIST("qa/hRunSums"), bc.runNumber(), q, quantities[q]); + } + const double rate = irSource.value.empty() ? -1. : rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource.value) * 1.e-3; + const std::array covariateQuantities = {1., quantities[1], quantities[2], quantities[1] * quantities[1], quantities[2] * quantities[2], nominal.vF, nominal.vB}; + for (int q = 0; q < NCovariateQuantities; ++q) { + registry.fill(HIST("qa/hOccupancySums"), coll.trackOccupancyInTimeRange(), q, covariateQuantities[q]); + registry.fill(HIST("qa/hVtxZSums"), coll.posZ(), q, covariateQuantities[q]); + registry.fill(HIST("qa/hRateSums"), rate, q, covariateQuantities[q]); + } + return nominal; + } + + void processReco(MyCollisions::iterator const& coll, MyTracks const& tracks, aod::BCsWithTimestamps const&) + { + fillReco(coll, tracks); + } + PROCESS_SWITCH(EventShapeCoex, processReco, "Reconstructed-level moment sums", true); + + void processRecoMC(MyCollisionsMc::iterator const& coll, MyTracks const& tracks, aod::McCollisions const&, aod::BCsWithTimestamps const&) + { + const Nominal nominal = fillReco(coll, tracks); + if (!nominal.valid || !doSplitSums) { + return; + } + if (!coll.has_mcCollision()) { + registry.fill(HIST("qa/hEventFlow"), kFlowNoMcLabel); + return; + } + const auto& bc = coll.mcCollision().bc_as(); + fillSplit(splitReco, nominal, bc.runNumber(), bc.globalBC()); + } + PROCESS_SWITCH(EventShapeCoex, processRecoMC, "Reconstructed-level moment sums on MC (MC-labelled collisions)", false); + + void processMC(aod::McCollision const& mcCollision, aod::McParticles const& particles, aod::BCs const&) + { + static constexpr double ChargeTolerance = 1.e-3; + + registry.fill(HIST("gen/hEvents"), 0); + uint32_t nFwdA = 0, nFwdC = 0; + Rings rings; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle == nullptr || std::abs(pdgParticle->Charge()) < ChargeTolerance) { + continue; // unknown PDG code or neutral + } + const float eta = particle.eta(); + // forward counts: no pT window + if (eta > etaFwdAMin && eta < etaFwdAMax) { + ++nFwdA; + } + if (eta > etaFwdCMin && eta < etaFwdCMax) { + ++nFwdC; + } + if (particle.pt() <= PtMin || std::abs(eta) >= EtaMax) { + continue; + } + rings.add(eta, particle.pt()); + } + const Nominal nominal = fillSums(sumsGen, rings, nFwdA + nFwdC, AllQaBits, 1); + if (nominal.valid && doSplitSums) { + const auto& bc = mcCollision.bc_as(); + fillSplit(splitGen, nominal, bc.runNumber(), bc.globalBC()); + } + } + PROCESS_SWITCH(EventShapeCoex, processMC, "Generator-level moment sums (nominal configuration)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Nuspex/CMakeLists.txt b/PWGLF/TableProducer/Nuspex/CMakeLists.txt index d8aab98b59b..ef89cf84878 100644 --- a/PWGLF/TableProducer/Nuspex/CMakeLists.txt +++ b/PWGLF/TableProducer/Nuspex/CMakeLists.txt @@ -24,13 +24,23 @@ o2physics_add_dpl_workflow(hypertriton-reco-task PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(tracked-hypertriton-reco-task + SOURCES trackedHypertritonRecoTask.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2::TOFBase O2::DetectorsVertexing O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(nuclei-three-body-builder + SOURCES nucleiThreeBodyBuilder.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lnn-reco-task SOURCES lnnRecoTask.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(nucleustreecreator - SOURCES LFTreeCreatorNuclei.cxx + SOURCES lfTreeCreatorNuclei.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -39,11 +49,21 @@ o2physics_add_dpl_workflow(he3hadronfemto PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(pi-hypertriton-femto + SOURCES piHypertritonFemto.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(nuclei-spectra SOURCES nucleiSpectra.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(deuteron-in-triggered-events + SOURCES deuteronInTriggeredEvents.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2::DetectorsBase O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(spectra-derived SOURCES spectraDerivedMaker.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -113,3 +133,13 @@ o2physics_add_dpl_workflow(particle-composition-correction SOURCES particleCompositionCorrection.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(photon-deuteron + SOURCES photonDeuteron.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(coalescence-tree-producer + SOURCES coalescenceTreeProducer.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Nuspex/LFTreeCreatorClusterStudies.cxx b/PWGLF/TableProducer/Nuspex/LFTreeCreatorClusterStudies.cxx index 2e3573df375..0dfed02c7f6 100644 --- a/PWGLF/TableProducer/Nuspex/LFTreeCreatorClusterStudies.cxx +++ b/PWGLF/TableProducer/Nuspex/LFTreeCreatorClusterStudies.cxx @@ -14,6 +14,7 @@ // // Author: Giorgio Alberto Lucia +#include "PWGEM/Dilepton/Utils/PairUtilities.h" #include "PWGEM/PhotonMeson/DataModel/gammaTables.h" #include "PWGLF/DataModel/LFClusterStudiesTable.h" #include "PWGLF/DataModel/LFStrangenessTables.h" @@ -74,7 +75,7 @@ using CollisionsCustomMc = soa::Join, 1> defaultParams = {{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}}; static const std::vector parNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; } // namespace BetheBloch @@ -110,9 +111,10 @@ enum V0Selections { enum CascSelections { kCascNoCut = 0, kCascTopology, + kNSigmaTPCV0Daughters, kRejectedXi, - kAcceptedOmega, kNSigmaTPC, + kAcceptedOmega, kCascAll }; @@ -144,7 +146,7 @@ enum PartID { all }; -static constexpr std::string_view cNames[] = {"none", "electron", "pion", "kaon", "proton", "deuteron", "He3"}; +static constexpr std::array cNames = {"none", "electron", "pion", "kaon", "proton", "deuteron", "He3"}; struct Candidate { float p = -999.f; // momentum * charge @@ -164,89 +166,96 @@ struct Candidate { struct LfTreeCreatorClusterStudies { - Service m_ccdb; - SliceCache m_cache; - int m_runNumber; - int m_collisionCounter = 0; - float m_d_bz; - uint32_t m_randomSeed = 0.; - - Configurable setting_fillV0{"fillV0", true, "Fill the V0 tree"}; - Configurable setting_fillExtraTable{"fillExtraTable", false, "Fill the extra table"}; - Configurable setting_fillCollTable{"fillCollTable", false, "Fill the collision table"}; - - Configurable setting_materialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Type of material correction"}; - - Configurable setting_zVtxMax{"zVtxMax", 10.f, "Maximum z vertex position"}; - - Configurable setting_downscaleFactor{"downscaleFactor", 1.f, "Downscale factor for the V0 candidates"}; - Configurable setting_applyAdditionalEvSel{"applyAdditionalEvSel", false, "Apply additional event selection"}; - - Configurable track_nClsItsMin{"track_NclsItsMin", 0.f, "Minimum number of ITS clusters for the V0 daughters"}; - Configurable track_nClsTpcMin{"track_NclsTpcMin", 100.f, "Minimum number of TPC clusters for the V0 daughters"}; - Configurable track_nClsTpcMaxShared{"track_NclsTpcMaxShared", 5.f, "Maximum number of shared TPC clusters for the V0 daughters"}; - Configurable track_etaMax{"etaMax", 0.8f, "Maximum eta"}; - Configurable track_tpcChi2Min{"track_tpcChi2Min", 0.5f, "Minimum TPC chi2 per cluster"}; - - // Configurable v0setting_etaMaxV0{"etaMaxV0", 0.8f, "Maximum eta for the V0 daughters"}; - Configurable v0setting_dcaV0daughters{"v0setting_dcaV0daughters", 1.f, "DCA between the V0 daughters"}; - Configurable v0setting_dcaMinV0DaughterToPv{"v0setting_dcaMinV0DaughterToPv", 0.06f, "DCA of the daughters to the primary vertex"}; - Configurable v0setting_radiusV0{"v0setting_radiusV0", 0.5f, "Maximum radius of the V0 accepted"}; - Configurable v0setting_cosPA{"v0setting_cosPA", 0.98f, "Cosine of the pointing angle of the V0"}; - Configurable v0setting_massWindowLambda{"v0setting_massWindowLambda", 0.02f, "Mass window for the Lambda"}; - Configurable v0setting_massWindowK0s{"v0setting_massWindowK0s", 0.02f, "Mass window for the K0s"}; - Configurable v0setting_nsigmatpcPi{"v0setting_nsigmaTPCPi", 2.f, "Number of sigmas for the TPC PID for pions"}; - Configurable v0setting_nsigmatpcPr{"v0setting_nsigmaTPCPr", 2.f, "Number of sigmas for the TPC PID for protons"}; - Configurable lambdasetting_qtAPcut{"lambdasetting_qtAPcut", 0.02f, "Cut on the qt for the Armenteros-Podolanski plot for photon rejection"}; - Configurable lambdasetting_pmin{"lambdasetting_pmin", 0.0f, "Minimum momentum for the V0 daughters"}; - - Configurable cascsetting_dcaMinV0DaughterToPv{"cascsetting_dcaMinV0DaughterToPv", 0.03f, "DCA of one of the daugthers of Lambda to pv"}; - Configurable cascsetting_dcaMinProtonToPv{"cascsetting_dcaMinProtonToPv", 0.03f, "DCA of the proton coming from Lambda to pv"}; - Configurable cascsetting_dcaMinBachelorToPv{"cascsetting_dcaMinBachelorToPv", 0.04f, "DCA of the bachelor to pv"}; - Configurable cascsetting_dcaMinV0ToPv{"cascsetting_dcaMinV0ToPv", 0.04f, "DCA of the V0 to pv"}; - Configurable cascsetting_dcaV0Daughters{"cascsetting_dcaV0daughters", 0.4f, "DCA between the V0 daughters"}; - Configurable cascsetting_cascCosPA{"cascsetting_cascCosPA", 0.99f, "Minimum cCosine of the pointing angle of the cascade"}; - Configurable cascsetting_v0cosPA{"cascsetting_v0cosPA", 0.97f, "Minimum cCosine of the pointing angle of the v0"}; - Configurable cascsetting_dcaMaxBachelorToV0{"cascsetting_dcaMaxBachelorToV0", 0.8f, "DCA of the bachelor to V0"}; - // Configurable cascsetting_dcaMinBachelorToProton{"cascsetting_dcaMinBachelorToProton", 0.015f, "DCA of the bachelor to proton"}; - Configurable cascsetting_radiusV0{"cascsetting_radiusV0", 1.2f, "Minimum radius of the V0 accepted"}; - Configurable cascsetting_radiusCasc{"cascsetting_radiusCasc", 0.5f, "Minimum radius of the cascade accepted"}; - - Configurable cascsetting_massWindowOmega{"cascsetting_massWindowOmega", 0.01f, "Mass window for the Omega"}; - Configurable cascsetting_massWindowXi{"cascsetting_massWindowXi", 0.01f, "Mass window for the Xi"}; - Configurable cascsetting_nsigmatpc{"cascsetting_nsigmaTPC", 3.f, "Number of sigmas for the TPC PID"}; - - Configurable electronsetting_conversion_rmin{"electron_conversion_rmin", 1.76f, "Minimum radius for the photon conversion (cm)"}; - Configurable electronsetting_conversion_rmax{"electron_conversion_rmax", 19.77f, "Maximum radius for the photon conversion (cm)"}; - Configurable electronsetting_maxDcaxy{"electronsetting_maxDcaxy", 0.1f, "Maximum value for the DCAxy"}; - Configurable electronsetting_maxDcaz{"electronsetting_maxDcaz", 0.5f, "Maximum value for the DCAz"}; - Configurable electronsetting_minNsigmatpcEl{"electronsetting_minNsigmaTPCEl", -2.5f, "Minimum value for the number of sigmas for the TPC PID for electrons"}; - Configurable electronsetting_maxNsigmatpcEl{"electronsetting_maxNsigmaTPCEl", 3.5f, "Maximum number for the number of sigmas for the TPC PID for electrons"}; - Configurable electronsetting_maxNsigmatpcPi{"electronsetting_maxNsigmaTPCPi", 2.f, "Maximum number for the number of sigmas for pi rejection for the TPC PID for electrons"}; - Configurable electronsetting_maxNsigmatpcKa{"electronsetting_maxNsigmaTPCKa", 2.f, "Maximum number for the number of sigmas for K rejection for the TPC PID for electrons"}; - Configurable electronsetting_maxNsigmatpcPr{"electronsetting_maxNsigmaTPCPr", 2.f, "Maximum number for the number of sigmas for p rejection for the TPC PID for electrons"}; - Configurable electronsetting_maxNsigmatofEl{"electronsetting_maxNsigmaTOFEl", 4.f, "Minimum value for the number of sigmas for the TPC PID for electrons"}; - Configurable electronsetting_minPt{"electronsetting_minPt", 0.f, "Minimum pT accepted for electrons"}; - - Configurable desetting_nClsIts{"desetting_nClsIts", 6, "Minimum number of ITS clusters"}; - Configurable desetting_nsigmatpc{"desetting_nsigmaCutTPC", 2.f, "Number of sigmas for the TPC PID"}; - Configurable desetting_nsigmatof{"desetting_nsigmaCutTOF", 2.f, "Number of sigmas for the TOF PID"}; - Configurable he3setting_nClsIts{"he3setting_nClsIts", 6, "Minimum number of ITS clusters"}; - Configurable he3setting_compensatePIDinTracking{"he3setting_compensatePIDinTracking", true, "Compensate PID in tracking"}; - Configurable he3setting_nsigmatpc{"he3setting_nsigmaCutTPC", 2.f, "Number of sigmas for the TPC PID"}; - Configurable he3setting_tofmasslow{"he3setting_tofmasslow", 1.8f, "Lower limit for the TOF mass"}; - Configurable he3setting_tofmasshigh{"he3setting_tofmasshigh", 4.2f, "Upper limit for the TOF mass"}; + Service mccdb; + SliceCache mcache; + int mrunNumber = 0; + int mcollisionCounter = 0; + float md_bz = 0.f; + uint32_t mrandomSeed = 0.; + + Configurable settingFillExtraTable{"fillExtraTable", false, "Fill the extra table"}; + Configurable settingFillCollTable{"fillCollTable", false, "Fill the collision table"}; + + Configurable settingZvtxMax{"zVtxMax", 10.f, "Maximum z vertex position"}; + + // Configurable settingDownscaleFactor{"downscaleFactor", 1.f, "Downscale factor for the V0 candidates"}; + + Configurable trackNclsItsMin{"trackNclsItsMin", 0.f, "Minimum number of ITS clusters for the V0 daughters"}; + Configurable trackNclsTpcMin{"trackNclsTpcMin", 100.f, "Minimum number of TPC clusters for the V0 daughters"}; + Configurable trackNclsTpcMaxShared{"trackNclsTpcMaxShared", 5.f, "Maximum number of shared TPC clusters for the V0 daughters"}; + Configurable trackEtaMax{"etaMax", 0.8f, "Maximum eta"}; + Configurable trackTpcChi2Min{"trackTpcChi2Min", 0.5f, "Minimum TPC chi2 per cluster"}; + + // Configurable v0settingEtaMaxV0{"etaMaxV0", 0.8f, "Maximum eta for the V0 daughters"}; + Configurable v0settingDcaV0daughters{"v0settingDcaV0daughters", 1.f, "DCA between the V0 daughters"}; + Configurable v0settingDcaMinV0DaughterToPv{"v0settingDcaMinV0DaughterToPv", 0.06f, "DCA of the daughters to the primary vertex"}; + Configurable v0settingRadiusV0{"v0settingRadiusV0", 0.5f, "Maximum radius of the V0 accepted"}; + Configurable v0settingCosPA{"v0settingCosPA", 0.98f, "Cosine of the pointing angle of the V0"}; + Configurable v0settingMassWindowLambda{"v0settingMassWindowLambda", 0.02f, "Mass window for the Lambda"}; + Configurable v0settingMassWindowK0s{"v0settingMassWindowK0s", 0.02f, "Mass window for the K0s"}; + Configurable v0settingNsigmatpcPi{"v0settingNsigmaTPCPi", 2.f, "Number of sigmas for the TPC PID for pions"}; + Configurable v0settingNsigmatpcPr{"v0settingNsigmaTPCPr", 2.f, "Number of sigmas for the TPC PID for protons"}; + Configurable lambdasettingQtAPcut{"lambdasettingQtAPcut", 0.02f, "Cut on the qt for the Armenteros-Podolanski plot for photon rejection"}; + Configurable lambdasettingPmin{"lambdasettingPmin", 0.0f, "Minimum momentum for the V0 daughters"}; + + Configurable cascsettingDcaMinV0DaughterToPv{"cascsettingDcaMinV0DaughterToPv", 0.03f, "DCA of one of the daugthers of Lambda to pv"}; + Configurable cascsettingDcaMinProtonToPv{"cascsettingDcaMinProtonToPv", 0.03f, "DCA of the proton coming from Lambda to pv"}; + Configurable cascsettingDcaMinBachelorToPv{"cascsettingDcaMinBachelorToPv", 0.04f, "DCA of the bachelor to pv"}; + Configurable cascsettingDcaMinV0ToPv{"cascsettingDcaMinV0ToPv", 0.04f, "DCA of the V0 to pv"}; + Configurable cascsettingDcaV0Daughters{"cascsettingDcaV0Daughters", 0.4f, "DCA between the V0 daughters"}; + Configurable cascsettingCascCosPA{"cascsettingCascCosPA", 0.99f, "Minimum cCosine of the pointing angle of the cascade"}; + Configurable cascsettingV0CosPA{"cascsettingV0CosPA", 0.97f, "Minimum cCosine of the pointing angle of the v0"}; + Configurable cascsettingDcaMaxBachelorToV0{"cascsettingDcaMaxBachelorToV0", 0.8f, "DCA of the bachelor to V0"}; + // Configurable cascsettingDcaMinBachelorToProton{"cascsettingDcaMinBachelorToProton", 0.015f, "DCA of the bachelor to proton"}; + Configurable cascsettingRadiusV0{"cascsettingRadiusV0", 1.2f, "Minimum radius of the V0 accepted"}; + Configurable cascsettingRadiusCasc{"cascsettingRadiusCasc", 0.5f, "Minimum radius of the cascade accepted"}; + + Configurable cascsettingMassWindowOmega{"cascsettingMassWindowOmega", 0.01f, "Mass window for the Omega"}; + Configurable cascsettingMassWindowXi{"cascsettingMassWindowXi", 0.01f, "Mass window for the Xi"}; + Configurable cascsettingNsigmatpc{"cascsettingNsigmaTPC", 3.f, "Number of sigmas for the TPC PID"}; + + Configurable electronsettingFromPhotonConversion{"electronsettingFromPhotonConversion", false, "Flag to indicate if the electron is from photon conversion"}; + Configurable electronsettingConversion_rmin{"electronsettingConversion_rmin", 1.76f, "Minimum radius for the photon conversion (cm)"}; + Configurable electronsettingConversion_rmax{"electronsettingConversion_rmax", 19.77f, "Maximum radius for the photon conversion (cm)"}; + Configurable electronsettingMaxDcaxy{"electronsettingMaxDcaxy", 0.1f, "Maximum value for the DCAxy"}; + Configurable electronsettingMaxDcaz{"electronsettingMaxDcaz", 0.5f, "Maximum value for the DCAz"}; + Configurable electronsettingMinNsigmatpcEl{"electronsettingMinNsigmaTPCEl", -2.5f, "Minimum value for the number of sigmas for the TPC PID for electrons"}; + Configurable electronsettingMaxNsigmatpcEl{"electronsettingMaxNsigmaTPCEl", 3.5f, "Maximum number for the number of sigmas for the TPC PID for electrons"}; + Configurable electronsettingMaxNsigmatpcPi{"electronsettingMaxNsigmaTPCPi", 2.f, "Maximum number for the number of sigmas for pi rejection for the TPC PID for electrons"}; + Configurable electronsettingMaxNsigmatpcKa{"electronsettingMaxNsigmaTPCKa", 2.f, "Maximum number for the number of sigmas for K rejection for the TPC PID for electrons"}; + Configurable electronsettingMaxNsigmatpcPr{"electronsettingMaxNsigmaTPCPr", 2.f, "Maximum number for the number of sigmas for p rejection for the TPC PID for electrons"}; + Configurable electronsettingMaxNsigmatofEl{"electronsettingMaxNsigmaTOFEl", 4.f, "Minimum value for the number of sigmas for the TPC PID for electrons"}; + Configurable electronsettingMinPt{"electronsettingMinPt", 0.f, "Minimum pT accepted for electrons"}; + Configurable electronsettingDcaV0daughters{"electronsettingDcaV0daughters", 1.f, "DCA between the V0 daughters"}; + Configurable electronsettingDcaMinV0DaughterToPv{"electronsettingDcaMinV0DaughterToPv", 0.06f, "DCA of the daughters to the primary vertex"}; + Configurable electronsettingRadiusV0{"electronsettingRadiusV0", 0.5f, "Maximum radius of the V0 accepted"}; + Configurable electronsettingCosPA{"electronsettingCosPA", 0.98f, "Cosine of the pointing angle of the V0"}; + Configurable electronsettingMaxMassPi0{"electronsettingMaxMassPi0", o2::constants::physics::MassPi0, "Maximum mass for the Pi0"}; + Configurable electronsettingMinMassPi0{"electronsettingMinMassPi0", 0.08f, "Minimum mass for the Pi0"}; + Configurable electronsettingMaxPhiv{"electronsettingMaxPhiv", 1.5708f, "Maximum value for the Phiv"}; + Configurable electronsettingMinPhiv{"electronsettingMinPhiv", 0.f, "Minimum value for the Phiv"}; + Configurable electronsettingMaxMassPhoton{"electronsettingMaxMassPhoton", 0.f, "Maximum mass for the Photon"}; + Configurable electronsettingMinMassPhoton{"electronsettingMinMassPhoton", 0.005f, "Minimum mass for the Photon"}; + + Configurable desettingNclsIts{"desettingNclsIts", 6, "Minimum number of ITS clusters"}; + Configurable desettingNsigmatpc{"desettingNsigmaCutTPC", 2.f, "Number of sigmas for the TPC PID"}; + Configurable desettingNsigmatof{"desettingNsigmaCutTOF", 2.f, "Number of sigmas for the TOF PID"}; + Configurable he3settingNclsIts{"he3settingNclsIts", 6, "Minimum number of ITS clusters"}; + Configurable he3settingCompensatePIDinTracking{"he3settingCompensatePIDinTracking", true, "Compensate PID in tracking"}; + Configurable he3settingNsigmatpc{"he3settingNsigmaCutTPC", 2.f, "Number of sigmas for the TPC PID"}; + Configurable he3settingTofmasslow{"he3settingTofmasslow", 1.8f, "Lower limit for the TOF mass"}; + Configurable he3settingTofmasshigh{"he3settingTofmasshigh", 4.2f, "Upper limit for the TOF mass"}; // Bethe Bloch parameters - std::array m_BBparamsDe, m_BBparamsHe; - Configurable> setting_BetheBlochParams{"setting_BetheBlochParams", {BetheBloch::defaultParams[0], 2, 6, {"De", "He3"}, BetheBloch::parNames}, "TPC Bethe-Bloch parameterisation for nuclei"}; + std::array mBBparamsDe = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}, mBBparamsHe = {{0.f, 0.f, 0.f, 0.f, 0.f, 0.f}}; + Configurable> settingBetheBlochParams{"settingBetheBlochParams", {BetheBloch::defaultParams[0].data(), 2, 6, {"De", "He3"}, BetheBloch::parNames}, "TPC Bethe-Bloch parameterisation for nuclei"}; - Preslice m_perCollisionV0 = o2::aod::v0::collisionId; - Preslice m_perCollisionCascade = o2::aod::cascade::collisionId; - Preslice m_perCol = aod::track::collisionId; - Preslice m_perColMC = aod::track::collisionId; + Preslice mPerCollisionV0 = o2::aod::v0::collisionId; + Preslice mPerCollisionCascade = o2::aod::cascade::collisionId; + Preslice mPerCol = aod::track::collisionId; + Preslice mPerColMC = aod::track::collisionId; - HistogramRegistry m_hAnalysis{ + HistogramRegistry mHistograms{ "LFTreeCreator", { {"collision_selections", "Collision selection; selection; counts", {HistType::kTH1F, {{Selections::kAll, -0.5, static_cast(Selections::kAll) - 0.5}}}}, @@ -266,12 +275,12 @@ struct LfTreeCreatorClusterStudies { {"photon_conversion_position", "Photon conversion position; #it{x} (cm); #it{y} (cm)", {HistType::kTH2F, {{250, -5.f, 5.f}, {250, -5.f, 5.f}}}}, {"photon_conversion_position_layer", "Photon conversion position (ITS layers); #it{x} (cm); #it{y} (cm)", {HistType::kTH2F, {{100, -5.f, 5.f}, {100, -5.f, 5.f}}}}, {"casc_dca_daughter_pairs", "DCA (xy) for cascade daughter pairs; DCA_{#it{xy}} (cm); counts", {HistType::kTH1F, {{100, -0.1, 0.1}}}}, - {"Xi_vs_Omega", "Mass Xi vs Omega; mass Omega (GeV/#it{c}^{2}); #it{m}_#Xi (GeV/#it{c}^{2})", {HistType::kTH2F, {{50, 1.f, 2.f}, {50, 1.f, 2.f}}}}, + {"Xi_vs_Omega", "Mass Xi vs Omega; #it{m}_{#Omega} (GeV/#it{c}^{2}); #it{m}_{#Xi} (GeV/#it{c}^{2})", {HistType::kTH2F, {{60, 1.5f, 2.1f}, {50, 1.1f, 1.6f}}}}, {"massOmega", "Mass #Omega; signed #it{p}_{T} (GeV/#it{c}); #it{m}_{#Omega} (GeV/#it{c}^{2})", {HistType::kTH2F, {{100, -5.f, 5.f}, {400, 1.62f, 1.72f}}}}, {"massOmegaMc", "Mass #Omega (MC); signed #it{p}_{T} (GeV/#it{c}); #it{m}_{#Omega} (GeV/#it{c}^{2})", {HistType::kTH2F, {{100, -5.f, 5.f}, {400, 1.62f, 1.72f}}}}, - {"massPi0", "Mass #pi^{0}; #it{m}_{#pi^{0}} (GeV/#it{c}^{2})", {HistType::kTH1F, {{100, 0.0f, 0.200f}}}}, - {"massPi0Mc", "Mass #pi^{0} (MC); #it{m}_{#pi^{0}} (GeV/#it{c}^{2})", {HistType::kTH1F, {{100, 0.0f, 0.200f}}}}, - {"massPi0WithBkg", "Mass #pi^{0} with Background; #it{m}_{#pi^{0}} (GeV/#it{c}^{2}); counts", {HistType::kTH1F, {{100, 0.0f, 0.200f}}}}, + {"massPi0", "Mass #pi^{0}; #it{m}_{ee} (GeV/#it{c}^{2})", {HistType::kTH1F, {{100, 0.0f, 0.200f}}}}, + {"massPi0Mc", "Mass #pi^{0} (MC); #it{m}_{ee} (GeV/#it{c}^{2})", {HistType::kTH1F, {{100, 0.0f, 0.200f}}}}, + {"massPi0WithBkg", "Mass #pi^{0} with Background; #it{m}_{ee} (GeV/#it{c}^{2}); counts", {HistType::kTH1F, {{100, 0.0f, 0.200f}}}}, {"zVtx", "Binning for the vertex z in cm; #it{z}_{vertex} (cm)", {HistType::kTH1F, {{100, -20.f, 20.f}}}}, {"isPositive", "is the candidate positive?; isPositive; counts", {HistType::kTH1F, {{2, -0.5f, 1.5f}}}}, @@ -281,28 +290,28 @@ struct LfTreeCreatorClusterStudies { OutputObjHandlingPolicy::AnalysisObject, false}; - Produces m_ClusterStudiesTable; - Produces m_ClusterStudiesTableExtra; - Produces m_ClusterStudiesTableCollision; - Produces m_ClusterStudiesTableMc; + Produces mClusterStudiesTable; + Produces mClusterStudiesTableExtra; + Produces mClusterStudiesTableCollision; + Produces mClusterStudiesTableMc; - o2::aod::ITSResponse m_responseITS; + o2::aod::ITSResponse mresponseITS; template bool collisionSelection(const Tcollision& collision) { - m_hAnalysis.fill(HIST("collision_selections"), Selections::kNoCut); + mHistograms.fill(HIST("collision_selections"), Selections::kNoCut); if (!collision.sel8()) { return false; } - m_hAnalysis.fill(HIST("collision_selections"), Selections::kSel8); + mHistograms.fill(HIST("collision_selections"), Selections::kSel8); // if (!collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { // return false; // } - if (std::abs(collision.posZ()) > setting_zVtxMax) { + if (std::abs(collision.posZ()) > settingZvtxMax) { return false; } - m_hAnalysis.fill(HIST("collision_selections"), Selections::kVtxZ); + mHistograms.fill(HIST("collision_selections"), Selections::kVtxZ); return true; } @@ -314,59 +323,94 @@ struct LfTreeCreatorClusterStudies { template bool qualityTrackSelection(const Track& track) { - if (std::abs(track.eta()) > track_etaMax || - track.itsNCls() < track_nClsItsMin || - track.tpcNClsFound() < track_nClsTpcMin || - track.tpcNClsCrossedRows() < track_nClsTpcMin || - track.tpcNClsCrossedRows() < 0.8 * track.tpcNClsFindable() || - track.tpcNClsShared() > track_nClsTpcMaxShared || - track.tpcChi2NCl() < track_tpcChi2Min || - track.tpcChi2NCl() > 4.0f) { + return (std::abs(track.eta()) < trackEtaMax && + track.itsNCls() >= trackNclsItsMin && + track.tpcNClsFound() >= trackNclsTpcMin && + track.tpcNClsCrossedRows() >= trackNclsTpcMin && + track.tpcNClsShared() <= trackNclsTpcMaxShared && + track.tpcChi2NCl() >= trackTpcChi2Min && + track.tpcChi2NCl() <= 4.0f); + } + + bool qualitySelectionV0(aod::V0Datas::iterator const& v0) + { + if (std::abs(v0.dcapostopv()) < v0settingDcaMinV0DaughterToPv) { + return false; + } + if (std::abs(v0.dcanegtopv()) < v0settingDcaMinV0DaughterToPv) { + return false; + } + if (std::abs(v0.dcaV0daughters()) > v0settingDcaV0daughters) { + return false; + } + if (v0.v0radius() < v0settingRadiusV0) { + return false; + } + if (std::abs(v0.v0cosPA()) < v0settingCosPA) { return false; } + return true; } - bool qualitySelectionV0(aod::V0Datas::iterator const& v0) + bool qualitySelectionPhotons(aod::V0Datas::iterator const& v0) { - if (std::abs(v0.dcapostopv()) < v0setting_dcaMinV0DaughterToPv) + if (std::abs(v0.dcapostopv()) < electronsettingDcaMinV0DaughterToPv) { return false; - if (std::abs(v0.dcanegtopv()) < v0setting_dcaMinV0DaughterToPv) + } + if (std::abs(v0.dcanegtopv()) < electronsettingDcaMinV0DaughterToPv) { return false; - if (std::abs(v0.dcaV0daughters()) > v0setting_dcaV0daughters) + } + if (std::abs(v0.dcaV0daughters()) > electronsettingDcaV0daughters) { return false; - if (v0.v0radius() < v0setting_radiusV0) + } + if (v0.v0radius() < electronsettingRadiusV0) { return false; - if (std::abs(v0.v0cosPA()) < v0setting_cosPA) + } + if (std::abs(v0.v0cosPA()) < electronsettingCosPA) { + return false; + } + if (v0.qtarm() > lambdasettingQtAPcut) { return false; + } return true; } bool qualitySelectionCascade(aod::CascDatas::iterator const& cascade, const std::array& pv) { - if (std::abs(cascade.dcapostopv()) < cascsetting_dcaMinV0DaughterToPv) + if (std::abs(cascade.dcapostopv()) < cascsettingDcaMinV0DaughterToPv) { return false; - if (std::abs(cascade.dcanegtopv()) < cascsetting_dcaMinV0DaughterToPv) + } + if (std::abs(cascade.dcanegtopv()) < cascsettingDcaMinV0DaughterToPv) { return false; - if (std::abs(cascade.dcabachtopv()) < cascsetting_dcaMinBachelorToPv) + } + if (std::abs(cascade.dcabachtopv()) < cascsettingDcaMinBachelorToPv) { return false; - if (std::abs(cascade.dcav0topv(pv[0], pv[1], pv[2])) < cascsetting_dcaMinV0ToPv) + } + if (std::abs(cascade.dcav0topv(pv[0], pv[1], pv[2])) < cascsettingDcaMinV0ToPv) { return false; - if (std::abs(cascade.dcaV0daughters()) > cascsetting_dcaV0Daughters) + } + if (std::abs(cascade.dcaV0daughters()) > cascsettingDcaV0Daughters) { return false; - if (std::abs(cascade.casccosPA(pv[0], pv[1], pv[2])) < cascsetting_cascCosPA) + } + if (std::abs(cascade.casccosPA(pv[0], pv[1], pv[2])) < cascsettingCascCosPA) { return false; - if (std::abs(cascade.v0cosPA(pv[0], pv[1], pv[2])) < cascsetting_v0cosPA) + } + if (std::abs(cascade.v0cosPA(pv[0], pv[1], pv[2])) < cascsettingV0CosPA) { return false; - if (std::abs(cascade.v0radius()) < cascsetting_radiusV0) + } + if (std::abs(cascade.v0radius()) < cascsettingRadiusV0) { return false; - if (std::abs(cascade.cascradius()) < cascsetting_radiusCasc) + } + if (std::abs(cascade.cascradius()) < cascsettingRadiusCasc) { return false; + } - m_hAnalysis.fill(HIST("casc_dca_daughter_pairs"), cascade.dcacascdaughters()); - if (std::abs(cascade.dcacascdaughters()) > cascsetting_dcaMaxBachelorToV0) + mHistograms.fill(HIST("casc_dca_daughter_pairs"), cascade.dcacascdaughters()); + if (std::abs(cascade.dcacascdaughters()) > cascsettingDcaMaxBachelorToV0) { return false; + } return true; } @@ -374,13 +418,13 @@ struct LfTreeCreatorClusterStudies { uint8_t selectV0MotherHypothesis(aod::V0Datas::iterator const& v0) { uint8_t v0Bitmask(0); - if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0setting_massWindowK0s) { + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0settingMassWindowK0s) { SETBIT(v0Bitmask, K0s); } - if ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0setting_massWindowLambda) && (v0.alpha() > 0)) { + if ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0settingMassWindowLambda) && (v0.alpha() > 0)) { SETBIT(v0Bitmask, Lambda); } - if ((std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0setting_massWindowLambda) && (v0.alpha() < 0)) { + if ((std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0settingMassWindowLambda) && (v0.alpha() < 0)) { SETBIT(v0Bitmask, AntiLambda); } return v0Bitmask; @@ -391,30 +435,36 @@ struct LfTreeCreatorClusterStudies { aod::V0Datas::iterator const& v0, const Track& posTrack, const Track& negTrack, uint8_t v0Bitmask) { if (TESTBIT(v0Bitmask, Lambda)) { - if (v0.qtarm() < lambdasetting_qtAPcut) + if (v0.qtarm() < lambdasettingQtAPcut) { return false; - if (std::abs(posTrack.tpcNSigmaPr()) > v0setting_nsigmatpcPr || std::abs(negTrack.tpcNSigmaPi()) > v0setting_nsigmatpcPi) + } + if (std::abs(posTrack.tpcNSigmaPr()) > v0settingNsigmatpcPr || std::abs(negTrack.tpcNSigmaPi()) > v0settingNsigmatpcPi) { return false; - if (v0.p() < lambdasetting_pmin) + } + if (v0.p() < lambdasettingPmin) { return false; + } candidatePos.partID = PartID::pr; candidateNeg.partID = PartID::pi; candidatePos.nsigmaTPC = posTrack.tpcNSigmaPr(); candidateNeg.nsigmaTPC = negTrack.tpcNSigmaPi(); - m_hAnalysis.fill(HIST("v0_type"), V0Type::Lambda); + mHistograms.fill(HIST("v0_type"), V0Type::Lambda); } else if (TESTBIT(v0Bitmask, AntiLambda)) { - if (v0.qtarm() < lambdasetting_qtAPcut) + if (v0.qtarm() < lambdasettingQtAPcut) { return false; - if (std::abs(posTrack.tpcNSigmaPi()) > v0setting_nsigmatpcPi || std::abs(negTrack.tpcNSigmaPr()) > v0setting_nsigmatpcPr) + } + if (std::abs(posTrack.tpcNSigmaPi()) > v0settingNsigmatpcPi || std::abs(negTrack.tpcNSigmaPr()) > v0settingNsigmatpcPr) { return false; - if (v0.p() < lambdasetting_pmin) + } + if (v0.p() < lambdasettingPmin) { return false; + } candidatePos.partID = PartID::pi; candidateNeg.partID = PartID::pr; candidatePos.nsigmaTPC = posTrack.tpcNSigmaPi(); candidateNeg.nsigmaTPC = negTrack.tpcNSigmaPr(); - m_hAnalysis.fill(HIST("v0_type"), V0Type::AntiLambda); + mHistograms.fill(HIST("v0_type"), V0Type::AntiLambda); } else { return false; @@ -423,6 +473,23 @@ struct LfTreeCreatorClusterStudies { return true; } + template + bool selectPidV0Daughters(const Track& posTrack, const Track& negTrack, uint8_t v0Bitmask) + { + if (TESTBIT(v0Bitmask, Lambda)) { + if (std::abs(posTrack.tpcNSigmaPr()) > v0settingNsigmatpcPr || std::abs(negTrack.tpcNSigmaPi()) > v0settingNsigmatpcPi) { + return false; + } + } else if (TESTBIT(v0Bitmask, AntiLambda)) { + if (std::abs(posTrack.tpcNSigmaPi()) > v0settingNsigmatpcPi || std::abs(negTrack.tpcNSigmaPr()) > v0settingNsigmatpcPr) { + return false; + } + } else { + return false; + } + return true; + } + /** * Fill the histograms for the V0 candidate and return the mass of the V0 */ @@ -432,19 +499,19 @@ struct LfTreeCreatorClusterStudies { float massV0{0.f}; if (TESTBIT(v0Bitmask, Lambda)) { massV0 = v0.mLambda(); - m_hAnalysis.fill(HIST("massLambda"), v0.pt(), v0.mLambda()); + mHistograms.fill(HIST("massLambda"), v0.pt(), v0.mLambda()); fillHistogramsParticle(trackPos); fillHistogramsParticle(trackNeg); } else if (TESTBIT(v0Bitmask, AntiLambda)) { massV0 = v0.mAntiLambda(); - m_hAnalysis.fill(HIST("massLambda"), v0.pt() * -1.f, v0.mAntiLambda()); + mHistograms.fill(HIST("massLambda"), v0.pt() * -1.f, v0.mAntiLambda()); fillHistogramsParticle(trackPos); fillHistogramsParticle(trackNeg); } - m_hAnalysis.fill(HIST("radiusV0"), v0.v0radius()); - m_hAnalysis.fill(HIST("armenteros_plot_lambda"), v0.alpha(), v0.qtarm()); - m_hAnalysis.fill(HIST("armenteros_plot"), v0.alpha(), v0.qtarm()); + mHistograms.fill(HIST("radiusV0"), v0.v0radius()); + mHistograms.fill(HIST("armenteros_plot_lambda"), v0.alpha(), v0.qtarm()); + mHistograms.fill(HIST("armenteros_plot"), v0.alpha(), v0.qtarm()); return massV0; } @@ -508,38 +575,38 @@ struct LfTreeCreatorClusterStudies { float nsigmaIts = -999.f; switch (partID) { case PartID::el: - nsigmaIts = m_responseITS.nSigmaITS(track); + nsigmaIts = mresponseITS.nSigmaITS(track); break; case PartID::pi: - nsigmaIts = m_responseITS.nSigmaITS(track); + nsigmaIts = mresponseITS.nSigmaITS(track); break; case PartID::ka: - nsigmaIts = m_responseITS.nSigmaITS(track); + nsigmaIts = mresponseITS.nSigmaITS(track); break; case PartID::pr: - nsigmaIts = m_responseITS.nSigmaITS(track); + nsigmaIts = mresponseITS.nSigmaITS(track); break; case PartID::de: - nsigmaIts = m_responseITS.nSigmaITS(track); + nsigmaIts = mresponseITS.nSigmaITS(track); break; case PartID::he: - nsigmaIts = m_responseITS.nSigmaITS(track); + nsigmaIts = mresponseITS.nSigmaITS(track); break; default: nsigmaIts = -999.f; break; } - float correctedTpcInnerParam = track.tpcInnerParam(); bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; - correctedTpcInnerParam = (partID == PartID::he && he3setting_compensatePIDinTracking && heliumPID) ? track.tpcInnerParam() / 2.f : track.tpcInnerParam(); - - m_hAnalysis.fill(HIST(cNames[partID]) + HIST("/nSigmaTPC"), track.p() * track.sign(), nsigmaTpc); - m_hAnalysis.fill(HIST(cNames[partID]) + HIST("/nSigmaITS"), track.p() * track.sign(), nsigmaIts); - m_hAnalysis.fill(HIST(cNames[partID]) + HIST("/nSigmaTOF"), track.p() * track.sign(), nsigmaTof); - if (partID == static_cast(PartID::de) || partID == static_cast(PartID::he)) - m_hAnalysis.fill(HIST(cNames[partID]) + HIST("/TOFmass"), track.p() * track.sign(), massTof); - m_hAnalysis.fill(HIST(cNames[partID]) + HIST("/pmatching"), correctedTpcInnerParam * track.sign(), (correctedTpcInnerParam - track.p()) / correctedTpcInnerParam); + float correctedTpcInnerParam = (partID == PartID::he && he3settingCompensatePIDinTracking && heliumPID) ? track.tpcInnerParam() / 2.f : track.tpcInnerParam(); + + mHistograms.fill(HIST(cNames[partID]) + HIST("/nSigmaTPC"), track.p() * track.sign(), nsigmaTpc); + mHistograms.fill(HIST(cNames[partID]) + HIST("/nSigmaITS"), track.p() * track.sign(), nsigmaIts); + mHistograms.fill(HIST(cNames[partID]) + HIST("/nSigmaTOF"), track.p() * track.sign(), nsigmaTof); + if (partID == static_cast(PartID::de) || partID == static_cast(PartID::he)) { + mHistograms.fill(HIST(cNames[partID]) + HIST("/TOFmass"), track.p() * track.sign(), massTof); + } + mHistograms.fill(HIST(cNames[partID]) + HIST("/pmatching"), correctedTpcInnerParam * track.sign(), (correctedTpcInnerParam - track.p()) / correctedTpcInnerParam); } template @@ -560,9 +627,9 @@ struct LfTreeCreatorClusterStudies { } if (motherPdgCode == PDG_t::kLambda0) { - m_hAnalysis.fill(HIST("massLambdaMc"), v0.pt(), v0.mLambda()); + mHistograms.fill(HIST("massLambdaMc"), v0.pt(), v0.mLambda()); } else if (motherPdgCode == PDG_t::kLambda0Bar) { - m_hAnalysis.fill(HIST("massLambdaMc"), v0.pt() * -1.f, v0.mAntiLambda()); + mHistograms.fill(HIST("massLambdaMc"), v0.pt() * -1.f, v0.mAntiLambda()); } } @@ -591,31 +658,31 @@ struct LfTreeCreatorClusterStudies { } if (motherPdgCode == PDG_t::kOmegaMinus) { - m_hAnalysis.fill(HIST("massOmegaMc"), cascade.pt(), cascade.mOmega()); + mHistograms.fill(HIST("massOmegaMc"), cascade.pt(), cascade.mOmega()); } else if (motherPdgCode == -PDG_t::kOmegaMinus) { - m_hAnalysis.fill(HIST("massOmegaMc"), cascade.pt() * -1.f, cascade.mOmega()); + mHistograms.fill(HIST("massOmegaMc"), cascade.pt() * -1.f, cascade.mOmega()); } } template void fillTable(const Candidate& candidate) { - m_ClusterStudiesTable( + mClusterStudiesTable( candidate.p, candidate.eta, candidate.phi, candidate.itsClusterSize, static_cast(candidate.partID)); - if (setting_fillExtraTable) { - m_ClusterStudiesTableExtra( + if (settingFillExtraTable) { + mClusterStudiesTableExtra( candidate.pTPC, candidate.pidInTrk, candidate.nsigmaTPC, candidate.nsigmaTOF, candidate.tofMass, candidate.cosPAMother, candidate.massMother); } - if (setting_fillCollTable) { - m_ClusterStudiesTableCollision( - m_runNumber); + if (settingFillCollTable) { + mClusterStudiesTableCollision( + mrunNumber); } if constexpr (isMC) { - m_ClusterStudiesTableMc( + mClusterStudiesTableMc( candidate.pdgCode); } } @@ -633,9 +700,9 @@ struct LfTreeCreatorClusterStudies { float computeNSigmaTPCHe3(const Track& candidate) { bool heliumPID = candidate.pidForTracking() == o2::track::PID::Helium3 || candidate.pidForTracking() == o2::track::PID::Alpha; - float correctedTPCinnerParam = (heliumPID && he3setting_compensatePIDinTracking) ? candidate.tpcInnerParam() / 2.f : candidate.tpcInnerParam(); - float expTPCSignal = o2::common::BetheBlochAleph(static_cast(correctedTPCinnerParam * 2.f / constants::physics::MassHelium3), m_BBparamsHe[0], m_BBparamsHe[1], m_BBparamsHe[2], m_BBparamsHe[3], m_BBparamsHe[4]); - double resoTPC{expTPCSignal * m_BBparamsHe[5]}; + float correctedTPCinnerParam = (heliumPID && he3settingCompensatePIDinTracking) ? candidate.tpcInnerParam() / 2.f : candidate.tpcInnerParam(); + float expTPCSignal = o2::common::BetheBlochAleph(static_cast(correctedTPCinnerParam * 2.f / constants::physics::MassHelium3), mBBparamsHe[0], mBBparamsHe[1], mBBparamsHe[2], mBBparamsHe[3], mBBparamsHe[4]); + double resoTPC{expTPCSignal * mBBparamsHe[5]}; return static_cast((candidate.tpcSignal() - expTPCSignal) / resoTPC); } @@ -644,7 +711,7 @@ struct LfTreeCreatorClusterStudies { { float beta = o2::pid::tof::Beta::GetBeta(candidate); bool heliumPID = candidate.pidForTracking() == o2::track::PID::Helium3 || candidate.pidForTracking() == o2::track::PID::Alpha; - float correctedTPCinnerParamHe3 = (heliumPID && he3setting_compensatePIDinTracking) ? candidate.tpcInnerParam() / 2.f : candidate.tpcInnerParam(); + float correctedTPCinnerParamHe3 = (heliumPID && he3settingCompensatePIDinTracking) ? candidate.tpcInnerParam() / 2.f : candidate.tpcInnerParam(); return correctedTPCinnerParamHe3 * 2.f * std::sqrt(1.f / (beta * beta) - 1.f); } @@ -653,11 +720,11 @@ struct LfTreeCreatorClusterStudies { template bool electronPrimarySelection(const Track& track) { - m_hAnalysis.fill(HIST("electron/DCAxyBeforeSelection"), track.dcaXY()); - m_hAnalysis.fill(HIST("electron/DCAzBeforeSelection"), track.dcaZ()); + mHistograms.fill(HIST("electron/DCAxyBeforeSelection"), track.dcaXY()); + mHistograms.fill(HIST("electron/DCAzBeforeSelection"), track.dcaZ()); - if (track.dcaXY() > electronsetting_maxDcaxy || - track.dcaZ() > electronsetting_maxDcaz) { + if (track.dcaXY() > electronsettingMaxDcaxy || + track.dcaZ() > electronsettingMaxDcaz) { return false; } return true; @@ -666,16 +733,18 @@ struct LfTreeCreatorClusterStudies { template bool electronPidSelection(const Track& track) { - if (track.tpcNSigmaEl() < electronsetting_minNsigmatpcEl || - electronsetting_maxNsigmatpcEl < track.tpcNSigmaEl() || - std::abs(track.tpcNSigmaPi()) < electronsetting_maxNsigmatpcPi || - std::abs(track.tpcNSigmaKa()) < electronsetting_maxNsigmatpcKa || - std::abs(track.tpcNSigmaPr()) < electronsetting_maxNsigmatpcPr) + if (track.tpcNSigmaEl() < electronsettingMinNsigmatpcEl || + electronsettingMaxNsigmatpcEl < track.tpcNSigmaEl() || + std::abs(track.tpcNSigmaPi()) < electronsettingMaxNsigmatpcPi || + std::abs(track.tpcNSigmaKa()) < electronsettingMaxNsigmatpcKa || + std::abs(track.tpcNSigmaPr()) < electronsettingMaxNsigmatpcPr) { return false; + } - if (electronsetting_maxNsigmatofEl != 0 && - std::abs(track.tofNSigmaEl()) < electronsetting_maxNsigmatofEl) + if (electronsettingMaxNsigmatofEl > 1e-7 && + std::abs(track.tofNSigmaEl()) < electronsettingMaxNsigmatofEl) { return false; + } return true; } @@ -685,121 +754,130 @@ struct LfTreeCreatorClusterStudies { template void initCCDB(Bc const& bc) { - if (m_runNumber == bc.runNumber()) { + if (mrunNumber == bc.runNumber()) { return; } const auto& timestamp = bc.timestamp(); - o2::parameters::GRPMagField* grpmag = 0x0; + o2::parameters::GRPMagField* grpmag = nullptr; auto grpmagPath{"GLO/Config/GRPMagField"}; - grpmag = m_ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + grpmag = mccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); if (!grpmag) { LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField for timestamp " << timestamp; } o2::base::Propagator::initFieldFromGRP(grpmag); // Fetch magnetic field from ccdb for current collision - m_d_bz = o2::base::Propagator::Instance()->getNominalBz(); - LOG(info) << "Retrieved GRP for timestamp " << timestamp << " with magnetic field of " << m_d_bz << " kG"; - m_runNumber = bc.runNumber(); + md_bz = o2::base::Propagator::Instance()->getNominalBz(); + LOG(info) << "Retrieved GRP for timestamp " << timestamp << " with magnetic field of " << md_bz << " kG"; + mrunNumber = bc.runNumber(); // o2::base::Propagator::Instance()->setMatLUT(lut); } void init(o2::framework::InitContext&) { - m_runNumber = 0; - m_d_bz = 0; + mrunNumber = 0; + md_bz = 0; - m_ccdb->setURL("http://alice-ccdb.cern.ch"); - m_ccdb->setCaching(true); - m_ccdb->setFatalWhenNull(false); + mccdb->setURL("http://alice-ccdb.cern.ch"); + mccdb->setCaching(true); + mccdb->setFatalWhenNull(false); // lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get("GLO/Param/MatLUT")); for (int ipartid = 0; ipartid < static_cast(PartID::all); ipartid++) { - if (ipartid == 0) + if (ipartid == 0) { continue; + } - m_hAnalysis.add(fmt::format("{}/nSigmaITS", cNames[ipartid]).c_str(), (fmt::format("nSigma ITS {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); n#sigma_{ITS}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {100, -5.0f, 5.0f}}); - m_hAnalysis.add(fmt::format("{}/nSigmaTPC", cNames[ipartid]).c_str(), (fmt::format("nSigma TPC {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); n#sigma_{TPC}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {60, -3.0f, 3.0f}}); - m_hAnalysis.add(fmt::format("{}/nSigmaTOF", cNames[ipartid]).c_str(), (fmt::format("nSigma TOF {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); n#sigma_{TOF}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {60, -3.0f, 3.0f}}); + mHistograms.add(fmt::format("{}/nSigmaITS", cNames[ipartid]).c_str(), (fmt::format("nSigma ITS {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); n#sigma_{ITS}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {100, -5.0f, 5.0f}}); + mHistograms.add(fmt::format("{}/nSigmaTPC", cNames[ipartid]).c_str(), (fmt::format("nSigma TPC {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); n#sigma_{TPC}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {60, -3.0f, 3.0f}}); + mHistograms.add(fmt::format("{}/nSigmaTOF", cNames[ipartid]).c_str(), (fmt::format("nSigma TOF {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); n#sigma_{TOF}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {60, -3.0f, 3.0f}}); if (ipartid == static_cast(PartID::de) || ipartid == static_cast(PartID::he)) { - m_hAnalysis.add(fmt::format("{}/TOFmass", cNames[ipartid]).c_str(), (fmt::format("TOF mass {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); #it{m}_{TOF} (GeV/#it{c}^{2})")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {100, 1.0f, 5.0f}}); - m_hAnalysis.add(fmt::format("{}/trackSelections", cNames[ipartid]).c_str(), (fmt::format("track selections {};", cNames[ipartid]) + std::string("Selections; Counts")).c_str(), HistType::kTH1F, {{NucleiSelections::kNucleiAll, -0.5, static_cast(NucleiSelections::kNucleiAll) - 0.5}}); + mHistograms.add(fmt::format("{}/TOFmass", cNames[ipartid]).c_str(), (fmt::format("TOF mass {};", cNames[ipartid]) + std::string("signed #it{p} (GeV/#it{c}); #it{m}_{TOF} (GeV/#it{c}^{2})")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {100, 1.0f, 5.0f}}); + mHistograms.add(fmt::format("{}/trackSelections", cNames[ipartid]).c_str(), (fmt::format("track selections {};", cNames[ipartid]) + std::string("Selections; Counts")).c_str(), HistType::kTH1F, {{NucleiSelections::kNucleiAll, -0.5, static_cast(NucleiSelections::kNucleiAll) - 0.5}}); } - m_hAnalysis.add(fmt::format("{}/pmatching", cNames[ipartid]).c_str(), (fmt::format("p matching {};", cNames[ipartid]) + std::string("signed #it{p}_{TPC} (GeV/#it{c}); #frac{#it{p}_{TPC} - #it{p}}{#it{p}_{TPC}}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {100, -0.5f, 0.5f}}); + mHistograms.add(fmt::format("{}/pmatching", cNames[ipartid]).c_str(), (fmt::format("p matching {};", cNames[ipartid]) + std::string("signed #it{p}_{TPC} (GeV/#it{c}); #frac{#it{p}_{TPC} - #it{p}}{#it{p}_{TPC}}")).c_str(), HistType::kTH2F, {{100, -5.0f, 5.0f}, {100, -0.5f, 0.5f}}); } std::vector trackSelectionLabels = {"All", "n clusters ITS", "TPC", "TOF"}; for (int i = 0; i < NucleiSelections::kNucleiAll; i++) { - m_hAnalysis.get(HIST(cNames[static_cast(PartID::de)]) + HIST("/trackSelections"))->GetXaxis()->SetBinLabel(i + 1, trackSelectionLabels[i].c_str()); - m_hAnalysis.get(HIST(cNames[static_cast(PartID::he)]) + HIST("/trackSelections"))->GetXaxis()->SetBinLabel(i + 1, trackSelectionLabels[i].c_str()); + mHistograms.get(HIST(cNames[static_cast(PartID::de)]) + HIST("/trackSelections"))->GetXaxis()->SetBinLabel(i + 1, trackSelectionLabels[i].c_str()); + mHistograms.get(HIST(cNames[static_cast(PartID::he)]) + HIST("/trackSelections"))->GetXaxis()->SetBinLabel(i + 1, trackSelectionLabels[i].c_str()); } LOG(info) << "Bethe-Bloch parameters for He3:"; for (int i = 0; i < 5; i++) { - m_BBparamsHe[i] = setting_BetheBlochParams->get("He3", Form("p%i", i)); - LOG(info) << "p" << i << ": " << m_BBparamsHe[i]; + mBBparamsHe[i] = settingBetheBlochParams->get("He3", Form("p%i", i)); + LOG(info) << "p" << i << ": " << mBBparamsHe[i]; } - m_BBparamsHe[5] = setting_BetheBlochParams->get("He3", "resolution"); - LOG(info) << "resolution: " << m_BBparamsHe[5]; + mBBparamsHe[5] = settingBetheBlochParams->get("He3", "resolution"); + LOG(info) << "resolution: " << mBBparamsHe[5]; LOG(info) << "Bethe-Bloch parameters for De:"; for (int i = 0; i < 5; i++) { - m_BBparamsDe[i] = setting_BetheBlochParams->get("De", Form("p%i", i)); - LOG(info) << "p" << i << ": " << m_BBparamsDe[i]; + mBBparamsDe[i] = settingBetheBlochParams->get("De", Form("p%i", i)); + LOG(info) << "p" << i << ": " << mBBparamsDe[i]; } - m_BBparamsDe[5] = setting_BetheBlochParams->get("De", "resolution"); - LOG(info) << "resolution: " << m_BBparamsDe[5]; + mBBparamsDe[5] = settingBetheBlochParams->get("De", "resolution"); + LOG(info) << "resolution: " << mBBparamsDe[5]; - std::vector collision_selection_labels = {"All", "sel8", "z_{VTX} < 10 cm"}; - for (int i = 0; i < Selections::kAll; i++) - m_hAnalysis.get(HIST("collision_selections"))->GetXaxis()->SetBinLabel(i + 1, collision_selection_labels[i].c_str()); + std::vector collisionSelectionLabels = {"All", "sel8", "z_{VTX} < 10 cm"}; + for (int i = 0; i < Selections::kAll; i++) { + mHistograms.get(HIST("collision_selections"))->GetXaxis()->SetBinLabel(i + 1, collisionSelectionLabels[i].c_str()); + } - std::vector V0_selection_labels = {"All", "daughter track quality", "V0 topology", "V0 mass selection"}; - for (int i = 0; i < V0Selections::kV0All; i++) - m_hAnalysis.get(HIST("v0_selections"))->GetXaxis()->SetBinLabel(i + 1, V0_selection_labels[i].c_str()); + std::vector V0selectionLabels = {"All", "daughter track quality", "V0 topology", "V0 mass selection"}; + for (int i = 0; i < V0Selections::kV0All; i++) { + mHistograms.get(HIST("v0_selections"))->GetXaxis()->SetBinLabel(i + 1, V0selectionLabels[i].c_str()); + } - std::vector Casc_selection_labels = {"All", "Topology", "Veto Xi", "Accepted Omega", "n#sigma_{TPC} K"}; - for (int i = 0; i < CascSelections::kCascAll; i++) - m_hAnalysis.get(HIST("casc_selections"))->GetXaxis()->SetBinLabel(i + 1, Casc_selection_labels[i].c_str()); + std::vector CascSelectionLabels = {"All", "Topology", "n#sigma_{TPC} V0 daughters", "Veto Xi", "n#sigma_{TPC} K", "Accepted Omega"}; + for (int i = 0; i < CascSelections::kCascAll; i++) { + mHistograms.get(HIST("casc_selections"))->GetXaxis()->SetBinLabel(i + 1, CascSelectionLabels[i].c_str()); + } - std::vector E_selections_labels = {"All", "Track quality", "Primary", "Pid", "#pi^{0}"}; - for (int i = 0; i < ESelections::kEAll; i++) - m_hAnalysis.get(HIST("e_selections"))->GetXaxis()->SetBinLabel(i + 1, E_selections_labels[i].c_str()); + std::vector ESelectionLabels = {"All", "Track quality", "Primary", "Pid", "#pi^{0}"}; + for (int i = 0; i < ESelections::kEAll; i++) { + mHistograms.get(HIST("e_selections"))->GetXaxis()->SetBinLabel(i + 1, ESelectionLabels[i].c_str()); + } - std::vector V0Type_labels = {"K0s", "#Lambda", "#bar{#Lambda}", "Photon"}; - for (int i = 0; i < V0Type::V0TypeAll; i++) - m_hAnalysis.get(HIST("v0_type"))->GetXaxis()->SetBinLabel(i + 1, V0Type_labels[i].c_str()); + std::vector V0TypeLabels = {"K0s", "#Lambda", "#bar{#Lambda}", "Photon"}; + for (int i = 0; i < V0Type::V0TypeAll; i++) { + mHistograms.get(HIST("v0_type"))->GetXaxis()->SetBinLabel(i + 1, V0TypeLabels[i].c_str()); + } } template void fillV0Cand(const std::array& /*pv*/, const aod::V0Datas::iterator& v0, const Tracks&) { - m_hAnalysis.fill(HIST("v0_selections"), V0Selections::kV0NoCut); + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0NoCut); const auto& posTrack = v0.template posTrack_as(); const auto& negTrack = v0.template negTrack_as(); - if (!qualityTrackSelection(posTrack) || !qualityTrackSelection(negTrack)) + if (!qualityTrackSelection(posTrack) || !qualityTrackSelection(negTrack)) { return; - m_hAnalysis.fill(HIST("v0_selections"), V0Selections::kV0DaughterQuality); + } + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0DaughterQuality); - if (!qualitySelectionV0(v0)) + if (!qualitySelectionV0(v0)) { return; - m_hAnalysis.fill(HIST("v0_selections"), V0Selections::kV0Topology); + } + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0Topology); std::array momPos{v0.pxpos(), v0.pypos(), v0.pzpos()}, momNeg{v0.pxneg(), v0.pyneg(), v0.pzneg()}; - m_hAnalysis.fill(HIST("armenteros_plot_before_selections"), v0.alpha(), v0.qtarm()); - m_hAnalysis.fill(HIST("Lambda_vs_K0s"), v0.mK0Short(), v0.mAntiLambda()); + mHistograms.fill(HIST("armenteros_plot_before_selections"), v0.alpha(), v0.qtarm()); + mHistograms.fill(HIST("Lambda_vs_K0s"), v0.mK0Short(), v0.mAntiLambda()); uint8_t v0Bitmask = selectV0MotherHypothesis(v0); - if (v0Bitmask == 0 || (v0Bitmask & (v0Bitmask - 1)) != 0) + if (v0Bitmask == 0 || (v0Bitmask & (v0Bitmask - 1)) != 0) { return; - m_hAnalysis.fill(HIST("v0_selections"), V0Selections::kV0PID); + } + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0PID); Candidate candidatePos(std::hypot(momPos[0], momPos[1], momPos[2]) * posTrack.sign(), RecoDecay::eta(momPos), RecoDecay::phi(momPos), posTrack.itsClusterSizes(), @@ -810,23 +888,21 @@ struct LfTreeCreatorClusterStudies { 0, negTrack.tpcInnerParam() * negTrack.sign(), negTrack.pidForTracking(), -999.f, -999.f, -999.f, v0.v0cosPA(), -999.f, 0); - if (!selectPidV0Daughters(candidatePos, candidateNeg, v0, posTrack, negTrack, v0Bitmask)) + if (!selectPidV0Daughters(candidatePos, candidateNeg, v0, posTrack, negTrack, v0Bitmask)) { return; + } const float massV0 = fillHistogramsV0(v0, posTrack, negTrack, v0Bitmask); candidatePos.massMother = massV0; candidateNeg.massMother = massV0; - if (!setting_fillV0) - return; - if constexpr (isMC) { // MC - if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { return; + } const auto& posMcParticle = posTrack.mcParticle(); const auto& negMcParticle = negTrack.mcParticle(); - candidatePos.pdgCode = posMcParticle.pdgCode(); candidateNeg.pdgCode = negMcParticle.pdgCode(); @@ -836,14 +912,14 @@ struct LfTreeCreatorClusterStudies { fillTable(candidatePos); fillTable(candidateNeg); - m_hAnalysis.fill(HIST("isPositive"), true); - m_hAnalysis.fill(HIST("isPositive"), false); + mHistograms.fill(HIST("isPositive"), true); + mHistograms.fill(HIST("isPositive"), false); } template void fillKCand(const std::array& pv, aod::CascDatas::iterator const& cascade, const Track&) { - m_hAnalysis.fill(HIST("casc_selections"), CascSelections::kCascNoCut); + mHistograms.fill(HIST("casc_selections"), CascSelections::kCascNoCut); const auto& bachelorTrack = cascade.template bachelor_as(); std::array momBachelor{cascade.pxbach(), cascade.pybach(), cascade.pzbach()}; @@ -851,43 +927,52 @@ struct LfTreeCreatorClusterStudies { if (!qualitySelectionCascade(cascade, pv)) { return; } - m_hAnalysis.fill(HIST("casc_selections"), CascSelections::kCascTopology); + mHistograms.fill(HIST("casc_selections"), CascSelections::kCascTopology); + + const auto& posTrack = cascade.template posTrack_as(); + const auto& negTrack = cascade.template negTrack_as(); + uint8_t v0Bitmask = 0; + SETBIT(v0Bitmask, bachelorTrack.sign() < 0 ? V0Type::Lambda : V0Type::AntiLambda); + if (!selectPidV0Daughters(posTrack, negTrack, v0Bitmask)) { + return; + } + mHistograms.fill(HIST("casc_selections"), CascSelections::kNSigmaTPCV0Daughters); const float& massXi = cascade.mXi(); const float& massOmega = cascade.mOmega(); - m_hAnalysis.fill(HIST("Xi_vs_Omega"), massOmega, massXi); + mHistograms.fill(HIST("Xi_vs_Omega"), massOmega, massXi); - if (std::abs(massXi - o2::constants::physics::MassXiMinus) < cascsetting_massWindowXi) { + if (std::abs(massXi - o2::constants::physics::MassXiMinus) < cascsettingMassWindowXi) { return; } - m_hAnalysis.fill(HIST("casc_selections"), CascSelections::kRejectedXi); - - m_hAnalysis.fill(HIST("massOmega"), cascade.pt() * bachelorTrack.sign(), massOmega); - if (std::abs(massOmega - o2::constants::physics::MassOmegaMinus) > cascsetting_massWindowOmega) { + mHistograms.fill(HIST("casc_selections"), CascSelections::kRejectedXi); + if (std::abs(bachelorTrack.tpcNSigmaKa()) > cascsettingNsigmatpc) { return; } - m_hAnalysis.fill(HIST("casc_selections"), CascSelections::kAcceptedOmega); + mHistograms.fill(HIST("casc_selections"), CascSelections::kNSigmaTPC); - if (std::abs(bachelorTrack.tpcNSigmaKa()) > cascsetting_nsigmatpc) { + mHistograms.fill(HIST("massOmega"), cascade.pt() * bachelorTrack.sign(), massOmega); + if (std::abs(massOmega - o2::constants::physics::MassOmegaMinus) > cascsettingMassWindowOmega) { return; } - m_hAnalysis.fill(HIST("casc_selections"), CascSelections::kNSigmaTPC); + mHistograms.fill(HIST("casc_selections"), CascSelections::kAcceptedOmega); + fillHistogramsParticle(bachelorTrack); - m_ClusterStudiesTable( + mClusterStudiesTable( std::hypot(momBachelor[0], momBachelor[1], momBachelor[2]) * bachelorTrack.sign(), RecoDecay::eta(momBachelor), RecoDecay::phi(momBachelor), bachelorTrack.itsClusterSizes(), PartID::ka); - if (setting_fillExtraTable) { - m_ClusterStudiesTableExtra( + if (settingFillExtraTable) { + mClusterStudiesTableExtra( bachelorTrack.tpcInnerParam() * bachelorTrack.sign(), bachelorTrack.pidForTracking(), bachelorTrack.tpcNSigmaKa(), /*TOF nsigma*/ -999.f, /*TOF mass*/ -999.f, cascade.casccosPA(pv[0], pv[1], pv[2]), massOmega); } - if (setting_fillCollTable) { - m_ClusterStudiesTableCollision( - m_runNumber); + if (settingFillCollTable) { + mClusterStudiesTableCollision( + mrunNumber); } if constexpr (isMC) { @@ -896,7 +981,7 @@ struct LfTreeCreatorClusterStudies { } const auto& mcParticle = bachelorTrack.mcParticle(); - m_ClusterStudiesTableMc( + mClusterStudiesTableMc( mcParticle.pdgCode()); const auto& posV0Daughter = cascade.template posTrack_as(); @@ -907,7 +992,7 @@ struct LfTreeCreatorClusterStudies { fillMcHistogramsCascade(cascade, mcParticle, mcPosParticleV0); } - m_hAnalysis.fill(HIST("isPositive"), bachelorTrack.sign() > 0); + mHistograms.fill(HIST("isPositive"), bachelorTrack.sign() > 0); } template @@ -915,50 +1000,55 @@ struct LfTreeCreatorClusterStudies { { constexpr int kPartID = partID; - if (kPartID == static_cast(PartID::de) && track.sign() > 0) + if (kPartID == static_cast(PartID::de) && track.sign() > 0) { return; - m_hAnalysis.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiNoCut); + } + mHistograms.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiNoCut); - if (track.itsNCls() < desetting_nClsIts) + if (track.itsNCls() < desettingNclsIts) { return; - m_hAnalysis.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiNClsIts); + } + mHistograms.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiNClsIts); const float tpcNsigma = kPartID == static_cast(PartID::de) ? track.tpcNSigmaDe() : computeNSigmaTPCHe3(track); - const float tpcNsigmaMax = kPartID == static_cast(PartID::de) ? desetting_nsigmatpc : he3setting_nsigmatpc; - if (std::abs(tpcNsigma) > tpcNsigmaMax) + const float tpcNsigmaMax = kPartID == static_cast(PartID::de) ? desettingNsigmatpc : he3settingNsigmatpc; + if (std::abs(tpcNsigma) > tpcNsigmaMax) { return; - m_hAnalysis.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiPIDtpc); + } + mHistograms.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiPIDtpc); const float tofMass = track.hasTOF() ? (kPartID == static_cast(PartID::de) ? computeTOFmassDe(track) : computeTOFmassHe3(track)) : -999.f; const float tofNsigma = kPartID == static_cast(PartID::de) ? track.tofNSigmaDe() : -999.f; float correctedTPCinnerParam = track.tpcInnerParam(); if (kPartID == static_cast(PartID::de)) { - if (!track.hasTOF() || std::abs(tofNsigma) > desetting_nsigmatof) + if (!track.hasTOF() || std::abs(tofNsigma) > desettingNsigmatof) { return; + } } else { - if (track.hasTOF() && (tofMass < he3setting_tofmasslow || tofMass > he3setting_tofmasshigh)) + if (track.hasTOF() && (tofMass < he3settingTofmasslow || tofMass > he3settingTofmasshigh)) { return; + } bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; - correctedTPCinnerParam = (heliumPID && he3setting_compensatePIDinTracking) ? track.tpcInnerParam() / 2.f : track.tpcInnerParam(); + correctedTPCinnerParam = (heliumPID && he3settingCompensatePIDinTracking) ? track.tpcInnerParam() / 2.f : track.tpcInnerParam(); } - m_hAnalysis.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiPIDtof); + mHistograms.fill(HIST(cNames[kPartID]) + HIST("/trackSelections"), NucleiSelections::kNucleiPIDtof); fillHistogramsParticle(track); - m_ClusterStudiesTable( + mClusterStudiesTable( track.p() * track.sign(), track.eta(), track.phi(), track.itsClusterSizes(), kPartID); - if (setting_fillExtraTable) { - m_ClusterStudiesTableExtra( + if (settingFillExtraTable) { + mClusterStudiesTableExtra( correctedTPCinnerParam * track.sign(), track.pidForTracking(), tpcNsigma, tofNsigma, tofMass, /*cosPA*/ -999.f, /*mass mother*/ -999.f); } - if (setting_fillCollTable) { - m_ClusterStudiesTableCollision( - m_runNumber); + if (settingFillCollTable) { + mClusterStudiesTableCollision( + mrunNumber); } if constexpr (isMC) { @@ -968,136 +1058,224 @@ struct LfTreeCreatorClusterStudies { const auto& mcParticle = track.mcParticle(); - m_ClusterStudiesTableMc( + mClusterStudiesTableMc( mcParticle.pdgCode()); } - m_hAnalysis.fill(HIST("isPositive"), track.sign() > 0); + mHistograms.fill(HIST("isPositive"), track.sign() > 0); } void fillPKPiTable(const TracksFullIU::iterator& track) { uint8_t partID = 0; float tpcNSigma = 0.f; - if (std::abs(track.tpcNSigmaPi()) < v0setting_nsigmatpcPi && std::abs(track.tpcNSigmaKa()) > 3) { + if (std::abs(track.tpcNSigmaPi()) < v0settingNsigmatpcPi && std::abs(track.tpcNSigmaKa()) > 3) { partID = PartID::pi; tpcNSigma = track.tpcNSigmaPi(); - m_hAnalysis.fill(HIST("nSigmaTPCPi"), track.p() * track.sign(), tpcNSigma); - } else if (std::abs(track.tpcNSigmaKa()) < cascsetting_nsigmatpc && (std::abs(track.tpcNSigmaPi()) > 3 /*&& std::abs(track.tpcNSigmaPr()) > 3*/)) { + mHistograms.fill(HIST("nSigmaTPCPi"), track.p() * track.sign(), tpcNSigma); + } else if (std::abs(track.tpcNSigmaKa()) < cascsettingNsigmatpc && (std::abs(track.tpcNSigmaPi()) > 3 /*&& std::abs(track.tpcNSigmaPr()) > 3*/)) { partID = PartID::ka; tpcNSigma = track.tpcNSigmaKa(); - m_hAnalysis.fill(HIST("nSigmaTPCKa"), track.p() * track.sign(), tpcNSigma); - } else if (std::abs(track.tpcNSigmaPr()) < v0setting_nsigmatpcPr && std::abs(track.tpcNSigmaKa()) > 3) { + mHistograms.fill(HIST("nSigmaTPCKa"), track.p() * track.sign(), tpcNSigma); + } else if (std::abs(track.tpcNSigmaPr()) < v0settingNsigmatpcPr && std::abs(track.tpcNSigmaKa()) > 3) { partID = PartID::pr; tpcNSigma = track.tpcNSigmaPr(); - m_hAnalysis.fill(HIST("nSigmaTPCPr"), track.p() * track.sign(), tpcNSigma); + mHistograms.fill(HIST("nSigmaTPCPr"), track.p() * track.sign(), tpcNSigma); } else { return; } - m_ClusterStudiesTable( + mClusterStudiesTable( track.p() * track.sign(), track.eta(), track.phi(), track.itsClusterSizes(), partID); - if (setting_fillExtraTable) { - m_ClusterStudiesTableExtra( + if (settingFillExtraTable) { + mClusterStudiesTableExtra( track.tpcInnerParam() * track.sign(), track.pidForTracking(), tpcNSigma, /*TOF nsigma*/ -999.f, /*TOF mass*/ -999.f, /*cosPA*/ -999.f, /*mass mother*/ -999.f); } - if (setting_fillCollTable) { - m_ClusterStudiesTableCollision( - m_runNumber); + if (settingFillCollTable) { + mClusterStudiesTableCollision( + mrunNumber); } } template - void fillElectronTable(const Track& posTrack, const Track& negTrack) + void fillElectronTableFromPi0Dalitz(const Track& posTrack, const Track& negTrack) { - m_hAnalysis.fill(HIST("e_selections"), ESelections::kENoCut); + mHistograms.fill(HIST("e_selections"), ESelections::kENoCut); if (!qualityTrackSelection(posTrack) || !qualityTrackSelection(negTrack)) { return; } - m_hAnalysis.fill(HIST("e_selections"), ESelections::kETrackQuality); + mHistograms.fill(HIST("e_selections"), ESelections::kETrackQuality); if (!electronPrimarySelection(posTrack) || !electronPrimarySelection(negTrack)) { return; } - m_hAnalysis.fill(HIST("e_selections"), ESelections::kEPrimary); + mHistograms.fill(HIST("e_selections"), ESelections::kEPrimary); if (!electronPidSelection(posTrack) || !electronPidSelection(negTrack)) { return; } - m_hAnalysis.fill(HIST("e_selections"), ESelections::kEPid); + mHistograms.fill(HIST("e_selections"), ESelections::kEPid); const float invariantMass = std::sqrt(RecoDecay::m2<2>(std::array, 2>{ std::array{posTrack.px(), posTrack.py(), posTrack.pz()}, std::array{negTrack.px(), negTrack.py(), negTrack.pz()}}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron})); + float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(posTrack.px(), posTrack.py(), posTrack.pz(), negTrack.px(), negTrack.py(), negTrack.pz(), posTrack.sign(), negTrack.sign(), md_bz); - m_hAnalysis.fill(HIST("massPi0WithBkg"), invariantMass); - if (invariantMass > o2::constants::physics::MassPi0) { + mHistograms.fill(HIST("massPi0WithBkg"), invariantMass); + if (invariantMass > electronsettingMaxMassPi0 || invariantMass < electronsettingMinMassPi0) { return; } - m_hAnalysis.fill(HIST("e_selections"), ESelections::kEPi0); - m_hAnalysis.fill(HIST("massPi0"), invariantMass); + if (phiv > electronsettingMaxPhiv || phiv < electronsettingMinPhiv) { + return; + } + mHistograms.fill(HIST("e_selections"), ESelections::kEPi0); + mHistograms.fill(HIST("massPi0"), invariantMass); fillHistogramsParticle(posTrack); fillHistogramsParticle(negTrack); - // float phiv = o2::aod::pwgem::dilepton::utils::pairutil::getPhivPair(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz(), t1.sign(), t2.sign(), m_d_bz); // float opangle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(t1.px(), t1.py(), t1.pz(), t2.px(), t2.py(), t2.pz()); - m_ClusterStudiesTable( + mClusterStudiesTable( posTrack.p(), posTrack.eta(), posTrack.phi(), posTrack.itsClusterSizes(), PartID::el); - if (setting_fillExtraTable) { - m_ClusterStudiesTableExtra( + if (settingFillExtraTable) { + mClusterStudiesTableExtra( posTrack.tpcInnerParam(), posTrack.pidForTracking(), posTrack.tpcNSigmaEl(), posTrack.tofNSigmaEl(), -999.f /* TofMass */, -999.f /* cosPA */, invariantMass); } - if (setting_fillCollTable) { - m_ClusterStudiesTableCollision( - m_runNumber); + if (settingFillCollTable) { + mClusterStudiesTableCollision( + mrunNumber); } - m_ClusterStudiesTable( + mClusterStudiesTable( negTrack.p(), negTrack.eta(), negTrack.phi(), negTrack.itsClusterSizes(), PartID::el); - if (setting_fillExtraTable) { - m_ClusterStudiesTableExtra( + if (settingFillExtraTable) { + mClusterStudiesTableExtra( negTrack.tpcInnerParam(), negTrack.pidForTracking(), negTrack.tpcNSigmaEl(), negTrack.tofNSigmaEl(), -999.f /* TofMass */, -999.f /* cosPA */, invariantMass); } - if (setting_fillCollTable) { - m_ClusterStudiesTableCollision( - m_runNumber); + if (settingFillCollTable) { + mClusterStudiesTableCollision( + mrunNumber); } if constexpr (isMC) { const auto& posMcParticle = posTrack.mcParticle(); const auto& negMcParticle = negTrack.mcParticle(); - m_ClusterStudiesTableMc( + mClusterStudiesTableMc( posMcParticle.pdgCode()); - m_ClusterStudiesTableMc( + mClusterStudiesTableMc( negMcParticle.pdgCode()); - if (!posMcParticle.has_mothers() || !negMcParticle.has_mothers()) + if (!posMcParticle.has_mothers() || !negMcParticle.has_mothers()) { return; + } for (const auto& posMother : posMcParticle.template mothers_as()) { for (const auto& negMother : negMcParticle.template mothers_as()) { - if (posMother.globalIndex() != negMother.globalIndex() || std::abs(posMother.pdgCode()) != PDG_t::kPi0) + if (posMother.globalIndex() != negMother.globalIndex() || std::abs(posMother.pdgCode()) != PDG_t::kPi0) { return; - m_hAnalysis.fill(HIST("massPi0Mc"), std::sqrt((posMcParticle.e() + negMcParticle.e()) * (posMcParticle.e() + negMcParticle.e()) - - (posMcParticle.p() + negMcParticle.p()) * (posMcParticle.p() + posMcParticle.p()))); + } + mHistograms.fill(HIST("massPi0Mc"), std::sqrt((posMcParticle.e() + negMcParticle.e()) * (posMcParticle.e() + negMcParticle.e()) - + (posMcParticle.px() + negMcParticle.px()) * (posMcParticle.px() + negMcParticle.px()) - + (posMcParticle.py() + negMcParticle.py()) * (posMcParticle.py() + negMcParticle.py()) - + (posMcParticle.pz() + negMcParticle.pz()) * (posMcParticle.pz() + negMcParticle.pz()))); break; } } } } + template + void fillElectronTableFromPhotonConversion(const std::array& /*pv*/, const aod::V0Datas::iterator& v0, const Tracks&) + { + + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0NoCut); + + const auto& posTrack = v0.template posTrack_as(); + const auto& negTrack = v0.template negTrack_as(); + + if (!qualityTrackSelection(posTrack) || !qualityTrackSelection(negTrack)) { + return; + } + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0DaughterQuality); + + if (!qualitySelectionPhotons(v0)) { + return; + } + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0Topology); + + std::array momPos{v0.pxpos(), v0.pypos(), v0.pzpos()}, + momNeg{v0.pxneg(), v0.pyneg(), v0.pzneg()}; + + mHistograms.fill(HIST("armenteros_plot_before_selections"), v0.alpha(), v0.qtarm()); + + Candidate candidatePos(std::hypot(momPos[0], momPos[1], momPos[2]) * posTrack.sign(), + RecoDecay::eta(momPos), RecoDecay::phi(momPos), posTrack.itsClusterSizes(), + 0, posTrack.tpcInnerParam() * posTrack.sign(), posTrack.pidForTracking(), + -999.f, -999.f, -999.f, v0.v0cosPA(), -999.f, 0); + Candidate candidateNeg(std::hypot(momNeg[0], momNeg[1], momNeg[2]) * negTrack.sign(), + RecoDecay::eta(momNeg), RecoDecay::phi(momNeg), negTrack.itsClusterSizes(), + 0, negTrack.tpcInnerParam() * negTrack.sign(), negTrack.pidForTracking(), + -999.f, -999.f, -999.f, v0.v0cosPA(), -999.f, 0); + + float invariantMass = std::sqrt(RecoDecay::m2<2>(std::array, 2>{ + std::array{posTrack.px(), posTrack.py(), posTrack.pz()}, + std::array{negTrack.px(), negTrack.py(), negTrack.pz()}}, + std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron})); + + if (invariantMass > electronsettingMaxMassPhoton || invariantMass < electronsettingMinMassPhoton) { + return; + } + + if (!electronPidSelection(posTrack) || !electronPidSelection(negTrack)) { + return; + } + mHistograms.fill(HIST("v0_selections"), V0Selections::kV0PID); + + mHistograms.fill(HIST("armenteros_plot_gamma"), v0.alpha(), v0.qtarm()); + mHistograms.fill(HIST("photon_radiusV0"), v0.v0radius()); + candidatePos.massMother = invariantMass; + candidateNeg.massMother = invariantMass; + + if constexpr (isMC) { // MC + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { + return; + } + + const auto& posMcParticle = posTrack.mcParticle(); + const auto& negMcParticle = negTrack.mcParticle(); + + candidatePos.pdgCode = posMcParticle.pdgCode(); + candidateNeg.pdgCode = negMcParticle.pdgCode(); + + if (candidatePos.pdgCode != PDG_t::kElectron || candidateNeg.pdgCode != PDG_t::kElectron) { + return; + } + if (!posMcParticle.has_mothers() || !negMcParticle.has_mothers()) { + return; + } + } + + fillHistogramsParticle(posTrack); + fillHistogramsParticle(negTrack); + + fillTable(candidatePos); + fillTable(candidateNeg); + + mHistograms.fill(HIST("isPositive"), true); + mHistograms.fill(HIST("isPositive"), false); + } + // ========================================================================================================= void processDataV0Casc(CollisionsCustom::iterator const& collision /*s*/, TracksFullIU const& tracks, aod::V0Datas const& v0s, aod::CascDatas const& cascades, aod::BCsWithTimestamps const&) @@ -1109,7 +1287,7 @@ struct LfTreeCreatorClusterStudies { const auto& bc = collision.bc_as(); initCCDB(bc); - m_hAnalysis.fill(HIST("zVtx"), collision.posZ()); + mHistograms.fill(HIST("zVtx"), collision.posZ()); std::array PV = {collision.posX(), collision.posY(), collision.posZ()}; for (const auto& v0 : v0s) { @@ -1122,9 +1300,27 @@ struct LfTreeCreatorClusterStudies { } PROCESS_SWITCH(LfTreeCreatorClusterStudies, processDataV0Casc, "process Data V0 and cascade", false); + void processDataElectronsFromPhotonConversion(CollisionsCustom::iterator const& collision /*s*/, TracksFullIU const& tracks, aod::V0Datas const& v0s, aod::BCsWithTimestamps const&) + { + if (!collisionSelection(collision)) { + return; + } + + const auto& bc = collision.bc_as(); + initCCDB(bc); + + mHistograms.fill(HIST("zVtx"), collision.posZ()); + std::array PV = {collision.posX(), collision.posY(), collision.posZ()}; + + for (const auto& v0 : v0s) { + fillElectronTableFromPhotonConversion(PV, v0, tracks); + } + } + PROCESS_SWITCH(LfTreeCreatorClusterStudies, processDataElectronsFromPhotonConversion, "process Data Electrons from Photon Conversion", false); + Partition posTracks = o2::aod::track::signed1Pt > 0.f; Partition negTracks = o2::aod::track::signed1Pt < 0.f; - void processDataElectrons(CollisionsCustom::iterator const& collision, TracksFullIU const& /*tracks*/, aod::BCsWithTimestamps const&) + void processDataElectronsFromDalitz(CollisionsCustom::iterator const& collision, TracksFullIU const& /*tracks*/, aod::BCsWithTimestamps const&) { if (!collisionSelection(collision)) { return; @@ -1133,16 +1329,16 @@ struct LfTreeCreatorClusterStudies { const auto& bc = collision.bc_as(); initCCDB(bc); - m_hAnalysis.fill(HIST("zVtx"), collision.posZ()); + mHistograms.fill(HIST("zVtx"), collision.posZ()); - const auto& posTracks_thisCollision = posTracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), m_cache); - const auto& negTracks_thisCollision = negTracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), m_cache); + const auto& posTracks_thisCollision = posTracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), mcache); + const auto& negTracks_thisCollision = negTracks.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), mcache); for (const auto& [posTrack, negTrack] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_thisCollision, negTracks_thisCollision))) { - fillElectronTable(posTrack, negTrack); + fillElectronTableFromPi0Dalitz(posTrack, negTrack); } } - PROCESS_SWITCH(LfTreeCreatorClusterStudies, processDataElectrons, "process Data Electrons", false); + PROCESS_SWITCH(LfTreeCreatorClusterStudies, processDataElectronsFromDalitz, "process Data Electrons from Dalitz", false); void processDataNuclei(CollisionsCustom::iterator const& collision, TracksFullIU const& tracks, aod::BCsWithTimestamps const&) { @@ -1153,7 +1349,7 @@ struct LfTreeCreatorClusterStudies { const auto& bc = collision.bc_as(); initCCDB(bc); - m_hAnalysis.fill(HIST("zVtx"), collision.posZ()); + mHistograms.fill(HIST("zVtx"), collision.posZ()); for (const auto& track : tracks) { if (!qualityTrackSelection(track)) { @@ -1178,7 +1374,7 @@ struct LfTreeCreatorClusterStudies { const auto& bc = collision.bc_as(); initCCDB(bc); - m_hAnalysis.fill(HIST("zVtx"), collision.posZ()); + mHistograms.fill(HIST("zVtx"), collision.posZ()); for (const auto& track : tracks) { if (!qualityTrackSelection(track)) { @@ -1199,7 +1395,7 @@ struct LfTreeCreatorClusterStudies { const auto& bc = collision.template bc_as(); initCCDB(bc); - m_hAnalysis.fill(HIST("zVtx"), collision.posZ()); + mHistograms.fill(HIST("zVtx"), collision.posZ()); std::array PV = {collision.posX(), collision.posY(), collision.posZ()}; for (const auto& v0 : v0s) { @@ -1212,9 +1408,30 @@ struct LfTreeCreatorClusterStudies { } PROCESS_SWITCH(LfTreeCreatorClusterStudies, processMcV0Casc, "process Mc V0 and cascade", false); + void processMcElectronsFromPhotonConversion(CollisionsCustomMc::iterator const& collision /*s*/, TracksFullIUMc const& tracks, aod::V0Datas const& v0s, aod::BCsWithTimestamps const&, aod::McParticles const&, aod::McCollisions const&) + { + if (!collision.has_mcCollision()) { + return; + } + if (!collisionSelection(collision)) { + return; + } + + const auto& bc = collision.bc_as(); + initCCDB(bc); + + mHistograms.fill(HIST("zVtx"), collision.posZ()); + std::array PV = {collision.posX(), collision.posY(), collision.posZ()}; + + for (const auto& v0 : v0s) { + fillElectronTableFromPhotonConversion(PV, v0, tracks); + } + } + PROCESS_SWITCH(LfTreeCreatorClusterStudies, processMcElectronsFromPhotonConversion, "process Mc Electrons from Photon Conversion", false); + Partition posTracksMc = o2::aod::track::signed1Pt > 0.f; Partition negTracksMc = o2::aod::track::signed1Pt < 0.f; - void processMcElectrons(CollisionsCustomMc::iterator const& collision, TracksFullIUMc const& /*tracks*/, aod::BCsWithTimestamps const&, aod::McParticles const&, aod::McCollisions const&) + void processMcElectronsFromDalitz(CollisionsCustomMc::iterator const& collision, TracksFullIUMc const& /*tracks*/, aod::BCsWithTimestamps const&, aod::McParticles const&, aod::McCollisions const&) { if (!collision.has_mcCollision()) { return; @@ -1225,19 +1442,20 @@ struct LfTreeCreatorClusterStudies { const auto& bc = collision.template bc_as(); initCCDB(bc); - m_hAnalysis.fill(HIST("zVtx"), collision.posZ()); + mHistograms.fill(HIST("zVtx"), collision.posZ()); - const auto& posTracks_thisCollision = posTracksMc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), m_cache); - const auto& negTracks_thisCollision = negTracksMc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), m_cache); + const auto& posTracks_thisCollision = posTracksMc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), mcache); + const auto& negTracks_thisCollision = negTracksMc.sliceByCached(o2::aod::track::collisionId, collision.globalIndex(), mcache); for (const auto& [posTrack, negTrack] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posTracks_thisCollision, negTracks_thisCollision))) { - if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { continue; + } - fillElectronTable(posTrack, negTrack); + fillElectronTableFromPi0Dalitz(posTrack, negTrack); } } - PROCESS_SWITCH(LfTreeCreatorClusterStudies, processMcElectrons, "process Mc Electrons", false); + PROCESS_SWITCH(LfTreeCreatorClusterStudies, processMcElectronsFromDalitz, "process Mc Electrons from Dalitz", false); void processMcNuclei(CollisionsCustomMc::iterator const& collision, TracksFullIUMc const& tracks, aod::BCsWithTimestamps const&, aod::McParticles const&, aod::McCollisions const&) { @@ -1249,7 +1467,7 @@ struct LfTreeCreatorClusterStudies { } const auto& bc = collision.template bc_as(); initCCDB(bc); - m_hAnalysis.fill(HIST("zVtx"), collision.posZ()); + mHistograms.fill(HIST("zVtx"), collision.posZ()); for (const auto& track : tracks) { if (!qualityTrackSelection(track)) { diff --git a/PWGLF/TableProducer/Nuspex/coalescenceTreeProducer.cxx b/PWGLF/TableProducer/Nuspex/coalescenceTreeProducer.cxx new file mode 100644 index 00000000000..4985cecbd8f --- /dev/null +++ b/PWGLF/TableProducer/Nuspex/coalescenceTreeProducer.cxx @@ -0,0 +1,429 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file coalescenceTreeProducer.cxx +/// \brief Tree producer for deuteron, triton, helium-3, and hypertriton coalescence studies. +/// +/// The task loops over generated MC particles and builds bound-state +/// candidates according to the selected species. For each candidate, +/// the constituent particles are boosted to the candidate rest frame, +/// propagated to a common time, and used to compute the Jacobi relative +/// momenta. +/// +/// Candidate triplets/pairs are stored in a reduced tree if: +/// - p_rho < pRhoMax +/// - p_lambda < pLambdaMax (for three-body coalescence only) +/// +/// The output tree stores the kinematic and space-time coordinates of +/// the constituent baryons for offline coalescence studies. +/// +/// \author Alberto Calivà + +#include "PWGLF/DataModel/mcCentrality.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace std; +using namespace o2; +using namespace o2::soa; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; +using namespace o2::constants::math; + +constexpr double massHypertriton = 2.99116; + +using GenCollisionsMc = soa::Join; + +// Lightweight particle container +struct ReducedParticle { + int64_t idPart = 0; + int pdgPart = 0; + float xPart = 0.f; + float yPart = 0.f; + float zPart = 0.f; + float tPart = 0.f; + float pxPart = 0.f; + float pyPart = 0.f; + float pzPart = 0.f; +}; + +struct CoalescenceTreeProducer { + + // Histogram registry + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // Configurable analysis parameters + Configurable zVtxMax{"zVtxMax", 10.f, "Maximum |z vertex| in cm"}; + Configurable multMin{"multMin", 0.f, "Lower edge of the multiplicity/centrality interval (%)"}; + Configurable multMax{"multMax", 90.f, "Upper edge of the multiplicity/centrality interval (%)"}; + Configurable etaMax{"etaMax", 1.5f, "Maximum |eta| for generated particles"}; + Configurable pRhoMax{"pRhoMax", 0.5f, "Maximum Jacobi p_rho in GeV/c"}; + Configurable pLambdaMax{"pLambdaMax", 0.5f, "Maximum Jacobi p_lambda in GeV/c"}; + Configurable yMax{"yMax", 0.5f, "Maximum rapidity"}; + + // Output tree storing bound-state candidates + OutputObj treeBoundState{"treeBoundState", OutputObjHandlingPolicy::AnalysisObject}; + + // Variables to be stored in the tree + Long64_t eventID = 0; // Event identifier + Long64_t nMatterCandidatesPerEvent = 0; // Number of matter candidates per event + Long64_t nAntimatterCandidatesPerEvent = 0; // Number of antimatter candidates per event + Long64_t idB1 = 0, idB2 = 0, idB3 = 0; // Constituent identifiers + int pdgB1 = 0, pdgB2 = 0, pdgB3 = 0; // Constituent PDG codes + + // Constituent space-time coordinates and momentum components + float xB1 = 0.f, yB1 = 0.f, zB1 = 0.f, tB1 = 0.f, pxB1 = 0.f, pyB1 = 0.f, pzB1 = 0.f; + float xB2 = 0.f, yB2 = 0.f, zB2 = 0.f, tB2 = 0.f, pxB2 = 0.f, pyB2 = 0.f, pzB2 = 0.f; + float xB3 = 0.f, yB3 = 0.f, zB3 = 0.f, tB3 = 0.f, pxB3 = 0.f, pyB3 = 0.f, pzB3 = 0.f; + + // Initialize output objects and histograms + void init(InitContext const&) + { + // Event selection counter + registry.add("eventCounter", "event Counter", HistType::kTH1F, {{5, 0, 5, "counter"}}); + registry.get(HIST("eventCounter"))->GetXaxis()->SetBinLabel(1, "Before z-vertex cut"); + registry.get(HIST("eventCounter"))->GetXaxis()->SetBinLabel(2, "After z-vertex cut"); + registry.get(HIST("eventCounter"))->GetXaxis()->SetBinLabel(3, "After multiplicity selection"); + registry.get(HIST("eventCounter"))->GetXaxis()->SetBinLabel(4, "After non-empty constituent lists"); + registry.get(HIST("eventCounter"))->GetXaxis()->SetBinLabel(5, "After non-empty candidate lists"); + + // Multiplicity distribution + registry.add("multDistribution", "multiplicity distribution", HistType::kTH1F, {{200, 0, 100, "Multiplicity (%)"}}); + + // pt distributions of constituents (to be used for re-weighting) + registry.add("ptProtons", "ptProtons", HistType::kTH1F, {{200, 0, 10, "p_{T} (GeV/c)"}}); + registry.add("ptNeutrons", "ptNeutrons", HistType::kTH1F, {{200, 0, 10, "p_{T} (GeV/c)"}}); + registry.add("ptLambdas", "ptLambdas", HistType::kTH1F, {{200, 0, 10, "p_{T} (GeV/c)"}}); + + // Output tree for bound-state candidates + treeBoundState.setObject(new TTree("BoundStateTree", "Tree for coalescence")); + + // Event information + treeBoundState->Branch("eventID", &eventID, "eventID/L"); + treeBoundState->Branch("nMatterCandidatesPerEvent", &nMatterCandidatesPerEvent, "nMatterCandidatesPerEvent/L"); + treeBoundState->Branch("nAntimatterCandidatesPerEvent", &nAntimatterCandidatesPerEvent, "nAntimatterCandidatesPerEvent/L"); + + // Constituent 1: ID, PDG code, space-time coordinates, and momentum components + treeBoundState->Branch("idB1", &idB1, "idB1/L"); + treeBoundState->Branch("pdgB1", &pdgB1, "pdgB1/I"); + treeBoundState->Branch("xB1", &xB1, "xB1/F"); + treeBoundState->Branch("yB1", &yB1, "yB1/F"); + treeBoundState->Branch("zB1", &zB1, "zB1/F"); + treeBoundState->Branch("tB1", &tB1, "tB1/F"); + treeBoundState->Branch("pxB1", &pxB1, "pxB1/F"); + treeBoundState->Branch("pyB1", &pyB1, "pyB1/F"); + treeBoundState->Branch("pzB1", &pzB1, "pzB1/F"); + + // Constituent 2: ID, PDG code, space-time coordinates, and momentum components + treeBoundState->Branch("idB2", &idB2, "idB2/L"); + treeBoundState->Branch("pdgB2", &pdgB2, "pdgB2/I"); + treeBoundState->Branch("xB2", &xB2, "xB2/F"); + treeBoundState->Branch("yB2", &yB2, "yB2/F"); + treeBoundState->Branch("zB2", &zB2, "zB2/F"); + treeBoundState->Branch("tB2", &tB2, "tB2/F"); + treeBoundState->Branch("pxB2", &pxB2, "pxB2/F"); + treeBoundState->Branch("pyB2", &pyB2, "pyB2/F"); + treeBoundState->Branch("pzB2", &pzB2, "pzB2/F"); + + // Constituent 3: ID, PDG code, space-time coordinates, and momentum components (three-body bound states only) + if (doprocessHypertriton) { + treeBoundState->Branch("idB3", &idB3, "idB3/L"); + treeBoundState->Branch("pdgB3", &pdgB3, "pdgB3/I"); + treeBoundState->Branch("xB3", &xB3, "xB3/F"); + treeBoundState->Branch("yB3", &yB3, "yB3/F"); + treeBoundState->Branch("zB3", &zB3, "zB3/F"); + treeBoundState->Branch("tB3", &tB3, "tB3/F"); + treeBoundState->Branch("pxB3", &pxB3, "pxB3/F"); + treeBoundState->Branch("pyB3", &pyB3, "pyB3/F"); + treeBoundState->Branch("pzB3", &pzB3, "pzB3/F"); + } + } + + // Assign mass based on PDG code + static double massFromPdg(int pdg) + { + switch (std::abs(pdg)) { + case PDG_t::kProton: + return o2::constants::physics::MassProton; + case PDG_t::kNeutron: + return o2::constants::physics::MassNeutron; + case PDG_t::kLambda0: + return o2::constants::physics::MassLambda0; + default: + return 0.0; + } + } + + // Apply a momentum-space skim using Jacobi momenta in the candidate rest frame + template + bool passThreeBodySkim(const ReducedPart& b1, const ReducedPart& b2, const ReducedPart& b3, double massBoundState) + { + // Constituent masses from PDG codes + double m1 = massFromPdg(b1.pdgPart); + double m2 = massFromPdg(b2.pdgPart); + double m3 = massFromPdg(b3.pdgPart); + if (m1 <= 0.0 || m2 <= 0.0 || m3 <= 0.0) { + return false; + } + + // Constituent four-momenta in the laboratory frame + auto p4B1 = ROOT::Math::PxPyPzMVector{b1.pxPart, b1.pyPart, b1.pzPart, m1}; + auto p4B2 = ROOT::Math::PxPyPzMVector{b2.pxPart, b2.pyPart, b2.pzPart, m2}; + auto p4B3 = ROOT::Math::PxPyPzMVector{b3.pxPart, b3.pyPart, b3.pzPart, m3}; + + // Candidate four-momentum + ROOT::Math::PxPyPzMVector p4Candidate(p4B1.Px() + p4B2.Px() + p4B3.Px(), p4B1.Py() + p4B2.Py() + p4B3.Py(), p4B1.Pz() + p4B2.Pz() + p4B3.Pz(), massBoundState); + + // Apply rapidity selection + if (std::abs(p4Candidate.Rapidity()) > yMax) { + return false; + } + + // Boost to the candidate rest frame + auto betaCandidate = p4Candidate.BoostToCM(); + ROOT::Math::Boost boostToRest(betaCandidate); + + // Constituent momenta in the candidate rest frame + auto p4B1Star = boostToRest(p4B1); + auto p4B2Star = boostToRest(p4B2); + auto p4B3Star = boostToRest(p4B3); + const ROOT::Math::XYZVector pB1Star = p4B1Star.Vect(); + const ROOT::Math::XYZVector pB2Star = p4B2Star.Vect(); + const ROOT::Math::XYZVector pB3Star = p4B3Star.Vect(); + + // Reduced masses entering the Jacobi coordinates + const double m12 = m1 + m2; + const double mTot = m1 + m2 + m3; + + // Jacobi momenta in the candidate rest frame + ROOT::Math::XYZVector pRho = (m2 * pB1Star - m1 * pB2Star) / m12; + ROOT::Math::XYZVector pLambda = (m3 * (pB1Star + pB2Star) - m12 * pB3Star) / mTot; + + // Reject candidates outside the momentum-space acceptance + if (pRho.R() > pRhoMax || pLambda.R() > pLambdaMax) { + return false; + } + + return true; + } + + // Group MC particles by their associated MC collision + Preslice mcParticlesPerMcCollision = o2::aod::mcparticle::mcCollisionId; + + // Process Hypertriton + void processHypertriton(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) + { + // Loop over MC collisions + for (const auto& collision : collisions) { + + // Event counter before selections + registry.fill(HIST("eventCounter"), 0.5); + + // Apply z-vertex selection + if (std::fabs(collision.posZ()) > zVtxMax) + continue; + + // Event counter after z-vertex cut + registry.fill(HIST("eventCounter"), 1.5); + + // Multiplicity Distribution + const float multiplicityPerc = collision.centFT0M(); + registry.fill(HIST("multDistribution"), multiplicityPerc); + + // Multiplicity selection + if (multiplicityPerc < multMin || multiplicityPerc > multMax) + continue; + + // Event counter multiplicity selection + registry.fill(HIST("eventCounter"), 2.5); + + // Get particles in this MC collision + const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); + + // Event ID + eventID = collision.globalIndex(); + + // Containers for candidate constituents + std::vector protons; + std::vector neutrons; + std::vector lambdas; + + // Loop over MC particles + for (const auto& particle : mcParticlesThisMcColl) { + + // Select physical primary particles only + if (!particle.isPhysicalPrimary()) + continue; + + // Select particles with |eta|< eta_max + if (std::fabs(particle.eta()) > etaMax) + continue; + + // Store protons + if (std::abs(particle.pdgCode()) == PDG_t::kProton) { + protons.push_back({particle.globalIndex(), particle.pdgCode(), particle.vx(), particle.vy(), particle.vz(), particle.vt(), particle.px(), particle.py(), particle.pz()}); + } + + // Store neutrons + if (std::abs(particle.pdgCode()) == PDG_t::kNeutron) { + neutrons.push_back({particle.globalIndex(), particle.pdgCode(), particle.vx(), particle.vy(), particle.vz(), particle.vt(), particle.px(), particle.py(), particle.pz()}); + } + + // Store lambdas + if (std::abs(particle.pdgCode()) == PDG_t::kLambda0) { + lambdas.push_back({particle.globalIndex(), particle.pdgCode(), particle.vx(), particle.vy(), particle.vz(), particle.vt(), particle.px(), particle.py(), particle.pz()}); + } + + // Rapidity selection + if (std::abs(particle.y()) > yMax) + continue; + + // Pt distributions of constituents + if (std::abs(particle.pdgCode()) == PDG_t::kProton) { + registry.fill(HIST("ptProtons"), particle.pt()); + } + if (std::abs(particle.pdgCode()) == PDG_t::kNeutron) { + registry.fill(HIST("ptNeutrons"), particle.pt()); + } + if (std::abs(particle.pdgCode()) == PDG_t::kLambda0) { + registry.fill(HIST("ptLambdas"), particle.pt()); + } + } // end of loop over MC particles + + // Reject events that do not contain at least one proton, one neutron, and one lambda + if (protons.empty() || neutrons.empty() || lambdas.empty()) + continue; + + // Event counter: events containing all three constituent species + registry.fill(HIST("eventCounter"), 3.5); + + // Count matter and antimatter candidates in the event + Long64_t nCandidatesMatter(0); + Long64_t nCandidatesAntiMatter(0); + for (const auto& proton : protons) { + for (const auto& neutron : neutrons) { + for (const auto& lambda : lambdas) { + + const bool isMatter = proton.pdgPart > 0 && neutron.pdgPart > 0 && lambda.pdgPart > 0; + const bool isAntimatter = proton.pdgPart < 0 && neutron.pdgPart < 0 && lambda.pdgPart < 0; + + // Skip mixed-sign combinations + if (!isMatter && !isAntimatter) + continue; + const bool passSkim = passThreeBodySkim(proton, neutron, lambda, massHypertriton); + + if (isMatter && passSkim) + nCandidatesMatter++; + if (isAntimatter && passSkim) + nCandidatesAntiMatter++; + } + } + } + + // Reject events with no accepted candidates + if (nCandidatesMatter == 0 && nCandidatesAntiMatter == 0) + continue; + + // Event counter: number of events with at least one candidate + registry.fill(HIST("eventCounter"), 4.5); + + // Store number of candidates per event + nMatterCandidatesPerEvent = nCandidatesMatter; + nAntimatterCandidatesPerEvent = nCandidatesAntiMatter; + + // Loop over accepted triplets and fill the tree + for (const auto& proton : protons) { + for (const auto& neutron : neutrons) { + for (const auto& lambda : lambdas) { + + const bool isMatter = proton.pdgPart > 0 && neutron.pdgPart > 0 && lambda.pdgPart > 0; + const bool isAntimatter = proton.pdgPart < 0 && neutron.pdgPart < 0 && lambda.pdgPart < 0; + + // Skip mixed-sign combinations + if (!isMatter && !isAntimatter) + continue; + + // Apply momentum-space selection + const bool passSkim = passThreeBodySkim(proton, neutron, lambda, massHypertriton); + if (!passSkim) + continue; + + // Fill tree + idB1 = proton.idPart; + pdgB1 = proton.pdgPart; + xB1 = proton.xPart; + yB1 = proton.yPart; + zB1 = proton.zPart; + tB1 = proton.tPart; + pxB1 = proton.pxPart; + pyB1 = proton.pyPart; + pzB1 = proton.pzPart; + + idB2 = neutron.idPart; + pdgB2 = neutron.pdgPart; + xB2 = neutron.xPart; + yB2 = neutron.yPart; + zB2 = neutron.zPart; + tB2 = neutron.tPart; + pxB2 = neutron.pxPart; + pyB2 = neutron.pyPart; + pzB2 = neutron.pzPart; + + idB3 = lambda.idPart; + pdgB3 = lambda.pdgPart; + xB3 = lambda.xPart; + yB3 = lambda.yPart; + zB3 = lambda.zPart; + tB3 = lambda.tPart; + pxB3 = lambda.pxPart; + pyB3 = lambda.pyPart; + pzB3 = lambda.pzPart; + + treeBoundState->Fill(); + } + } + } + } // end of loop over mc collisions + } + PROCESS_SWITCH(CoalescenceTreeProducer, processHypertriton, "process hypertriton", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx index 38b1a130027..3e58c303ed4 100644 --- a/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx +++ b/PWGLF/TableProducer/Nuspex/decay3bodybuilder.cxx @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -74,7 +75,7 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -o2::common::core::MetadataHelper metadataInfo; +o2::common::core::MetadataHelper metadataInfo{}; static constexpr int nParameters = 1; static const std::vector tableNames{ @@ -219,35 +220,31 @@ struct decay3bodyBuilder { // Helper struct to contain MC information prior to filling struct mc3Bodyinfo { - int label; - std::array genDecVtx{0.f}; - std::array genMomentum{0.f}; - float genCt; - float genPhi; - float genEta; - float genRapidity; - float genMomProton; - float genMomPion; - float genMomDeuteron; - float genPtProton; - float genPtPion; - float genPtDeuteron; - bool isTrueH3L; - bool isTrueAntiH3L; - bool isReco; - int motherPdgCode; - int daughterPrPdgCode; - int daughterPiPdgCode; - int daughterDePdgCode; - bool isDeuteronPrimary; - bool survivedEventSel; + int label = -1; + std::array genDecVtx{-1.f, -1.f, -1.f}; + std::array genMomentum{-1.f, -1.f, -1.f}; + float genCt = -1.f; + float genPhi = -1.f; + float genEta = -1.f; + float genRapidity = -1.f; + std::array genMomProton{-1.f, -1.f, -1.f}; + std::array genMomPion{-1.f, -1.f, -1.f}; + std::array genMomDeuteron{-1.f, -1.f, -1.f}; + bool isReco = false; + int motherLabel = -1; + int motherPdgCode = -1; + int daughterPrPdgCode = -1; + int daughterPiPdgCode = -1; + int daughterDePdgCode = -1; + bool isDeuteronPrimary = false; + bool survivedEventSel = false; }; mc3Bodyinfo this3BodyMCInfo; // CCDB and magnetic field - int mRunNumber; - float d_bz; - Service ccdb; + int mRunNumber = 0; + float d_bz = 0.f; + Service ccdb{}; o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrNONE; std::unordered_map ccdbCache; // Maps runNumber -> d_bz o2::base::MatLayerCylSet* lut = nullptr; @@ -382,7 +379,7 @@ struct decay3bodyBuilder { // list enabled tables for (int i = 0; i < nTables; i++) { - if (mEnabledTables[i]) { + if (mEnabledTables[i] != 0) { LOGF(info, " -~> Table enabled: %s", tableNames[i]); } } @@ -436,26 +433,26 @@ struct decay3bodyBuilder { } } - if (mEnabledTables[kVtx3BodyDatas] && mEnabledTables[kMcVtx3BodyDatas]) { + if (mEnabledTables[kVtx3BodyDatas] != 0 && mEnabledTables[kMcVtx3BodyDatas] != 0) { LOG(fatal) << "Tables Vtx3BodyDatas and McVtx3BodyDatas cannot both be enabled at the same time. Choose one!"; } // Add histograms separately for different process functions - if (doprocessRealData == true || doprocessMonteCarlo == true) { + if (doprocessRealData || doprocessMonteCarlo) { auto hEventCounter = registry.add("Counters/hEventCounter", "hEventCounter", HistType::kTH1D, {{2, 0.0f, 2.0f}}); hEventCounter->GetXaxis()->SetBinLabel(1, "all"); hEventCounter->GetXaxis()->SetBinLabel(2, "selected"); hEventCounter->LabelsOption("v"); } - if (doprocessMonteCarlo == true) { + if (doprocessMonteCarlo) { auto hMcEventCounter = registry.add("Counters/hMcEventCounter", "hMcEventCounter", HistType::kTH1D, {{2, 0.0f, 2.0f}}); hMcEventCounter->GetXaxis()->SetBinLabel(1, "all"); hMcEventCounter->GetXaxis()->SetBinLabel(2, "reconstructed"); hMcEventCounter->LabelsOption("v"); } - if (doprocessRealData == true || doprocessRealDataReduced == true || doprocessMonteCarlo == true) { + if (doprocessRealData || doprocessRealDataReduced || doprocessMonteCarlo) { if (doTrackQA) { // histograms for all daughter tracks of (selected) 3body candidates registry.add("QA/Tracks/hTrackProtonTPCNcls", "hTrackProtonTPCNcls", HistType::kTH1F, {{152, 0, 152, "# TPC clusters"}}); registry.add("QA/Tracks/hTrackPionTPCNcls", "hTrackPionTPCNcls", HistType::kTH1F, {{152, 0, 152, "# TPC clusters"}}); @@ -485,7 +482,7 @@ struct decay3bodyBuilder { } } - if (doprocessRealDataReduced3bodyMixing == true) { + if (doprocessRealDataReduced3bodyMixing) { auto h3bodyCombinationCounter = registry.add("Mixing/h3bodyCombinationCounter", "h3bodyCombinationCounter", HistType::kTH1D, {{4, 0.0f, 4.0f}}); h3bodyCombinationCounter->GetXaxis()->SetBinLabel(1, "total"); h3bodyCombinationCounter->GetXaxis()->SetBinLabel(2, "not same collision"); @@ -516,7 +513,7 @@ struct decay3bodyBuilder { } auto timestamp = bc.timestamp(); - o2::parameters::GRPMagField* grpmag = 0x0; + o2::parameters::GRPMagField* grpmag = nullptr; ccdb->clearCache(ccdbConfigurations.grpmagPath); grpmag = ccdb->getSpecific(ccdbConfigurations.grpmagPath, timestamp); if (!grpmag) { @@ -524,7 +521,7 @@ struct decay3bodyBuilder { } o2::base::Propagator::initFieldFromGRP(grpmag); // Fetch magnetic field from ccdb for current collision - auto d_bz = o2::base::Propagator::Instance()->getNominalBz(); + d_bz = o2::base::Propagator::Instance()->getNominalBz(); LOG(info) << "Retrieved GRP for timestamp " << timestamp << " with magnetic field of " << d_bz << " kG"; // set magnetic field value for DCA fitter @@ -553,9 +550,9 @@ struct decay3bodyBuilder { float getMagFieldFromRunNumber(int runNumber) { - float magField; + float magField{}; // Check if the CCDB data for this run is already cached - if (ccdbCache.find(runNumber) != ccdbCache.end()) { + if (ccdbCache.contains(runNumber)) { LOG(debug) << "CCDB data already cached for run " << runNumber; magField = ccdbCache[runNumber]; // if not, retrieve it from CCDB @@ -732,10 +729,10 @@ struct decay3bodyBuilder { auto trackPion = protonSign > 0 ? trackNeg : trackPos; // get deuteron TOF PID - float tofNSigmaDeuteron; + float tofNSigmaDeuteron{}; if constexpr (!soa::is_table) { // running over derived data tofNSigmaDeuteron = trackDeuteron.tofNSigmaDe(); - } else if constexpr (soa::is_table) { // running over AO2Ds + } else { // running over AO2Ds if constexpr (soa::is_table) { // running over MC (track table with labels) tofNSigmaDeuteron = getTOFnSigma(mRespParamsV3, collision, trackDeuteron); } else { // running over real data @@ -806,11 +803,10 @@ struct decay3bodyBuilder { if (!trackProton.has_mcParticle() || !trackPion.has_mcParticle() || !trackDeuteron.has_mcParticle()) { if (!doStoreMcBkg) { continue; // if not storing MC background, skip candidates where at least one daughter is not matched to MC particle - } else { - this3BodyMCInfo.label = -5; // at least one non-matched daughter - // fill analysis table (only McVtx3BodyDatas is filled here) - fillAnalysisTables(); } + this3BodyMCInfo.motherLabel = -5; // at least one of the daughters not matched to MC particle + // fill analysis table (only McVtx3BodyDatas is filled here) + fillAnalysisTables(); } else { // all daughters are matched to MC particles, get their MC info // get MC daughter particles auto mcTrackProton = trackProton.template mcParticle_as(); @@ -822,24 +818,21 @@ struct decay3bodyBuilder { this3BodyMCInfo.daughterPiPdgCode = mcTrackPion.pdgCode(); this3BodyMCInfo.daughterDePdgCode = mcTrackDeuteron.pdgCode(); this3BodyMCInfo.isDeuteronPrimary = mcTrackDeuteron.isPhysicalPrimary(); - this3BodyMCInfo.genMomProton = mcTrackProton.p(); - this3BodyMCInfo.genMomPion = mcTrackPion.p(); - this3BodyMCInfo.genMomDeuteron = mcTrackDeuteron.p(); - this3BodyMCInfo.genPtProton = mcTrackProton.pt(); - this3BodyMCInfo.genPtPion = mcTrackPion.pt(); - this3BodyMCInfo.genPtDeuteron = mcTrackDeuteron.pt(); + this3BodyMCInfo.genMomProton = {mcTrackProton.px(), mcTrackProton.py(), mcTrackProton.pz()}; + this3BodyMCInfo.genMomPion = {mcTrackPion.px(), mcTrackPion.py(), mcTrackPion.pz()}; + this3BodyMCInfo.genMomDeuteron = {mcTrackDeuteron.px(), mcTrackDeuteron.py(), mcTrackDeuteron.pz()}; // daughters are matched to MC, now we check if reco mother is true H3L/Anti-H3l and decayed via three-body decay - this3BodyMCInfo.label = checkH3LTruth(mcTrackProton, mcTrackPion, mcTrackDeuteron); // returns global index of mother if true H3L/Anti-H3L mother decaying via three-body decay, otherwise negative value + this3BodyMCInfo.motherLabel = checkH3LTruth(mcTrackProton, mcTrackPion, mcTrackDeuteron); // returns global index of mother if true H3L/Anti-H3L mother decaying via three-body decay, otherwise negative value for background // if not storing MC background, skip candidates where mother is not true H3L/Anti-H3L decaying via three-body decay - if (!doStoreMcBkg && this3BodyMCInfo.label <= 0) { + if (!doStoreMcBkg && this3BodyMCInfo.motherLabel <= 0) { continue; } // get generated mother MC info for matched candidates - if (this3BodyMCInfo.label > -1) { - auto mcTrackH3L = mcParticles.rawIteratorAt(this3BodyMCInfo.label); + if (this3BodyMCInfo.motherLabel > 0) { + auto mcTrackH3L = mcParticles.rawIteratorAt(this3BodyMCInfo.motherLabel); this3BodyMCInfo.motherPdgCode = mcTrackH3L.pdgCode(); this3BodyMCInfo.genMomentum = {mcTrackH3L.px(), mcTrackH3L.py(), mcTrackH3L.pz()}; this3BodyMCInfo.genDecVtx = {mcTrackProton.vx(), mcTrackProton.vy(), mcTrackProton.vz()}; @@ -847,8 +840,6 @@ struct decay3bodyBuilder { this3BodyMCInfo.genPhi = mcTrackH3L.phi(); this3BodyMCInfo.genEta = mcTrackH3L.eta(); this3BodyMCInfo.genRapidity = mcTrackH3L.y(); - this3BodyMCInfo.isTrueH3L = this3BodyMCInfo.motherPdgCode > 0 ? true : false; - this3BodyMCInfo.isTrueAntiH3L = this3BodyMCInfo.motherPdgCode < 0 ? true : false; } // fill analysis tables (only McVtx3BodyDatas is filled here) @@ -856,8 +847,8 @@ struct decay3bodyBuilder { } // end of check if daughters have MC particle // mark mcParticle as reconstructed - if (this3BodyMCInfo.label > -1) { - mcParticleIsReco[this3BodyMCInfo.label] = true; + if (this3BodyMCInfo.motherLabel > 0) { + mcParticleIsReco[this3BodyMCInfo.motherLabel] = true; } } // constexpr requires mcParticles check } // decay3body loop @@ -888,41 +879,39 @@ struct decay3bodyBuilder { bool haveProton = false, havePion = false, haveDeuteron = false; bool haveAntiProton = false, haveAntiPion = false, haveAntiDeuteron = false; for (const auto& mcparticleDaughter : mcparticle.template daughters_as()) { - if (mcparticleDaughter.pdgCode() == PDG_t::kProton) + if (mcparticleDaughter.pdgCode() == PDG_t::kProton) { haveProton = true; - if (mcparticleDaughter.pdgCode() == PDG_t::kProtonBar) + } + if (mcparticleDaughter.pdgCode() == PDG_t::kProtonBar) { haveAntiProton = true; - if (mcparticleDaughter.pdgCode() == PDG_t::kPiPlus) + } + if (mcparticleDaughter.pdgCode() == PDG_t::kPiPlus) { havePion = true; - if (mcparticleDaughter.pdgCode() == PDG_t::kPiMinus) + } + if (mcparticleDaughter.pdgCode() == PDG_t::kPiMinus) { haveAntiPion = true; - if (mcparticleDaughter.pdgCode() == o2::constants::physics::Pdg::kDeuteron) + } + if (mcparticleDaughter.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { haveDeuteron = true; - if (mcparticleDaughter.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) + } + if (mcparticleDaughter.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { haveAntiDeuteron = true; + } } // check if hypertriton decayed via 3-body decay and is particle or anti-particle if ((haveProton && haveAntiPion && haveDeuteron && !(haveAntiProton || havePion || haveAntiDeuteron)) || (haveAntiProton && havePion && haveAntiDeuteron && !(haveProton || haveAntiPion || haveDeuteron))) { - if (mcparticle.pdgCode() > 0) { - this3BodyMCInfo.isTrueH3L = true; - } else if (mcparticle.pdgCode() < 0) { - this3BodyMCInfo.isTrueAntiH3L = true; - } // get daughters for (const auto& mcparticleDaughter : mcparticle.template daughters_as()) { if (std::abs(mcparticleDaughter.pdgCode()) == PDG_t::kProton) { // proton - this3BodyMCInfo.genMomProton = mcparticleDaughter.p(); - this3BodyMCInfo.genPtProton = mcparticleDaughter.pt(); + this3BodyMCInfo.genMomProton = {mcparticleDaughter.px(), mcparticleDaughter.py(), mcparticleDaughter.pz()}; this3BodyMCInfo.daughterPrPdgCode = mcparticleDaughter.pdgCode(); this3BodyMCInfo.genDecVtx = {mcparticleDaughter.vx(), mcparticleDaughter.vy(), mcparticleDaughter.vz()}; } else if (std::abs(mcparticleDaughter.pdgCode()) == PDG_t::kPiPlus) { // pion - this3BodyMCInfo.genMomPion = mcparticleDaughter.p(); - this3BodyMCInfo.genPtPion = mcparticleDaughter.pt(); + this3BodyMCInfo.genMomPion = {mcparticleDaughter.px(), mcparticleDaughter.py(), mcparticleDaughter.pz()}; this3BodyMCInfo.daughterPiPdgCode = mcparticleDaughter.pdgCode(); } else if (std::abs(mcparticleDaughter.pdgCode()) == o2::constants::physics::Pdg::kDeuteron) { // deuteron - this3BodyMCInfo.genMomDeuteron = mcparticleDaughter.p(); - this3BodyMCInfo.genPtDeuteron = mcparticleDaughter.pt(); + this3BodyMCInfo.genMomDeuteron = {mcparticleDaughter.px(), mcparticleDaughter.py(), mcparticleDaughter.pz()}; this3BodyMCInfo.daughterDePdgCode = mcparticleDaughter.pdgCode(); this3BodyMCInfo.isDeuteronPrimary = mcparticleDaughter.isPhysicalPrimary(); } @@ -945,6 +934,7 @@ struct decay3bodyBuilder { -1., -1., -1., // momProton -1., -1., -1., // momPion -1., -1., -1., // momDeuteron + -1., -1., -1., // xProton, xPion, xDeuteron -1., -1., -1., // trackDCAxyToPV: 0 - proton, 1 - pion, 2 - deuteron -1., -1., -1., // trackDCAToPV: 0 - proton, 1 - pion, 2 - deuteron -1., -1., -1., // trackDCAxyToPVprop: 0 - proton, 1 - pion, 2 - deuteron @@ -953,6 +943,7 @@ struct decay3bodyBuilder { -1., // daughterDCAtoSVaverage -1., -1., // cosPA, ctau -1., -1., -1., -1., // tpcNsigma: 0 - proton, 1 - pion, 2 - deuteron, 3 - bach with pion hyp + -1., -1., -1., // tpcSignal: 0 - proton, 1 - pion, 2 - deuteron -1., // tofNsigmaDeuteron -1., -1., -1., // average ITS cluster sizes: proton, pion, deuteron -1., -1., -1., // TPCNCl: proton, pion, deuteron @@ -962,11 +953,12 @@ struct decay3bodyBuilder { this3BodyMCInfo.genDecVtx[0], this3BodyMCInfo.genDecVtx[1], this3BodyMCInfo.genDecVtx[2], this3BodyMCInfo.genCt, mcparticle.phi(), mcparticle.eta(), mcparticle.y(), - this3BodyMCInfo.genMomProton, this3BodyMCInfo.genMomPion, this3BodyMCInfo.genMomDeuteron, - this3BodyMCInfo.genPtProton, this3BodyMCInfo.genPtPion, this3BodyMCInfo.genPtDeuteron, - this3BodyMCInfo.isTrueH3L, this3BodyMCInfo.isTrueAntiH3L, + this3BodyMCInfo.genMomProton[0], this3BodyMCInfo.genMomProton[1], this3BodyMCInfo.genMomProton[2], + this3BodyMCInfo.genMomPion[0], this3BodyMCInfo.genMomPion[1], this3BodyMCInfo.genMomPion[2], + this3BodyMCInfo.genMomDeuteron[0], this3BodyMCInfo.genMomDeuteron[1], this3BodyMCInfo.genMomDeuteron[2], this3BodyMCInfo.isReco, - mcparticle.pdgCode(), + mcparticle.globalIndex(), // motherLabel + mcparticle.pdgCode(), // motherPdgCode this3BodyMCInfo.daughterPrPdgCode, this3BodyMCInfo.daughterPiPdgCode, this3BodyMCInfo.daughterDePdgCode, this3BodyMCInfo.isDeuteronPrimary, this3BodyMCInfo.survivedEventSel); @@ -1090,13 +1082,13 @@ struct decay3bodyBuilder { void fillAnalysisTables() { // generate analysis tables - if (mEnabledTables[kDecay3BodyIndices]) { + if (mEnabledTables[kDecay3BodyIndices] != 0) { products.decay3bodyindices(helper.decay3body.decay3bodyID, helper.decay3body.protonID, helper.decay3body.pionID, helper.decay3body.deuteronID, helper.decay3body.collisionID); registry.fill(HIST("Counters/hTableBuildingStatistics"), kDecay3BodyIndices); } - if (mEnabledTables[kVtx3BodyDatas]) { + if (mEnabledTables[kVtx3BodyDatas] != 0) { products.vtx3bodydatas(helper.decay3body.sign, helper.decay3body.mass, helper.decay3body.massV0, helper.decay3body.position[0], helper.decay3body.position[1], helper.decay3body.position[2], @@ -1106,6 +1098,7 @@ struct decay3bodyBuilder { helper.decay3body.momProton[0], helper.decay3body.momProton[1], helper.decay3body.momProton[2], helper.decay3body.momPion[0], helper.decay3body.momPion[1], helper.decay3body.momPion[2], helper.decay3body.momDeuteron[0], helper.decay3body.momDeuteron[1], helper.decay3body.momDeuteron[2], + helper.decay3body.xProton, helper.decay3body.xPion, helper.decay3body.xDeuteron, helper.decay3body.trackDCAxyToPV[0], helper.decay3body.trackDCAxyToPV[1], helper.decay3body.trackDCAxyToPV[2], // 0 - proton, 1 - pion, 2 - deuteron helper.decay3body.trackDCAToPV[0], helper.decay3body.trackDCAToPV[1], helper.decay3body.trackDCAToPV[2], // 0 - proton, 1 - pion, 2 - deuteron helper.decay3body.trackDCAxyToPVprop[0], helper.decay3body.trackDCAxyToPVprop[1], helper.decay3body.trackDCAxyToPVprop[2], // 0 - proton, 1 - pion, 2 - deuteron @@ -1114,20 +1107,21 @@ struct decay3bodyBuilder { helper.decay3body.daughterDCAtoSVaverage, helper.decay3body.cosPA, helper.decay3body.ctau, helper.decay3body.tpcNsigma[0], helper.decay3body.tpcNsigma[1], helper.decay3body.tpcNsigma[2], helper.decay3body.tpcNsigma[3], // 0 - proton, 1 - pion, 2 - deuteron, 3 - bach with pion hyp + helper.decay3body.tpcSignal[0], helper.decay3body.tpcSignal[1], helper.decay3body.tpcSignal[2], helper.decay3body.tofNsigmaDeuteron, helper.decay3body.averageITSClSize[0], helper.decay3body.averageITSClSize[1], helper.decay3body.averageITSClSize[2], // 0 - proton, 1 - pion, 2 - deuteron helper.decay3body.tpcNCl[0], helper.decay3body.tpcNCl[1], helper.decay3body.tpcNCl[2], // 0 - proton, 1 - pion, 2 - deuteron helper.decay3body.pidForTrackingDeuteron); registry.fill(HIST("Counters/hTableBuildingStatistics"), kVtx3BodyDatas); } - if (mEnabledTables[kVtx3BodyCovs]) { - products.vtx3bodycovs(helper.decay3body.covProton, - helper.decay3body.covPion, - helper.decay3body.covDeuteron, - helper.decay3body.covariance); + if (mEnabledTables[kVtx3BodyCovs] != 0) { + products.vtx3bodycovs(helper.decay3body.covProton.data(), + helper.decay3body.covPion.data(), + helper.decay3body.covDeuteron.data(), + helper.decay3body.covariance.data()); registry.fill(HIST("Counters/hTableBuildingStatistics"), kVtx3BodyCovs); } - if (mEnabledTables[kMcVtx3BodyDatas]) { + if (mEnabledTables[kMcVtx3BodyDatas] != 0) { products.mcvtx3bodydatas(helper.decay3body.sign, helper.decay3body.mass, helper.decay3body.massV0, helper.decay3body.position[0], helper.decay3body.position[1], helper.decay3body.position[2], @@ -1137,6 +1131,7 @@ struct decay3bodyBuilder { helper.decay3body.momProton[0], helper.decay3body.momProton[1], helper.decay3body.momProton[2], helper.decay3body.momPion[0], helper.decay3body.momPion[1], helper.decay3body.momPion[2], helper.decay3body.momDeuteron[0], helper.decay3body.momDeuteron[1], helper.decay3body.momDeuteron[2], + helper.decay3body.xProton, helper.decay3body.xPion, helper.decay3body.xDeuteron, helper.decay3body.trackDCAxyToPV[0], helper.decay3body.trackDCAxyToPV[1], helper.decay3body.trackDCAxyToPV[2], // 0 - proton, 1 - pion, 2 - deuteron helper.decay3body.trackDCAToPV[0], helper.decay3body.trackDCAToPV[1], helper.decay3body.trackDCAToPV[2], // 0 - proton, 1 - pion, 2 - deuteron helper.decay3body.trackDCAxyToPVprop[0], helper.decay3body.trackDCAxyToPVprop[1], helper.decay3body.trackDCAxyToPVprop[2], // 0 - proton, 1 - pion, 2 - deuteron @@ -1145,6 +1140,7 @@ struct decay3bodyBuilder { helper.decay3body.daughterDCAtoSVaverage, helper.decay3body.cosPA, helper.decay3body.ctau, helper.decay3body.tpcNsigma[0], helper.decay3body.tpcNsigma[1], helper.decay3body.tpcNsigma[2], helper.decay3body.tpcNsigma[3], // 0 - proton, 1 - pion, 2 - deuteron, 3 - bach with pion hyp + helper.decay3body.tpcSignal[0], helper.decay3body.tpcSignal[1], helper.decay3body.tpcSignal[2], helper.decay3body.tofNsigmaDeuteron, helper.decay3body.averageITSClSize[0], helper.decay3body.averageITSClSize[1], helper.decay3body.averageITSClSize[2], // 0 - proton, 1 - pion, 2 - deuteron helper.decay3body.tpcNCl[0], helper.decay3body.tpcNCl[1], helper.decay3body.tpcNCl[2], // 0 - proton, 1 - pion, 2 - deuteron @@ -1154,10 +1150,11 @@ struct decay3bodyBuilder { this3BodyMCInfo.genDecVtx[0], this3BodyMCInfo.genDecVtx[1], this3BodyMCInfo.genDecVtx[2], this3BodyMCInfo.genCt, this3BodyMCInfo.genPhi, this3BodyMCInfo.genEta, this3BodyMCInfo.genRapidity, - this3BodyMCInfo.genMomProton, this3BodyMCInfo.genMomPion, this3BodyMCInfo.genMomDeuteron, - this3BodyMCInfo.genPtProton, this3BodyMCInfo.genPtPion, this3BodyMCInfo.genPtDeuteron, - this3BodyMCInfo.isTrueH3L, this3BodyMCInfo.isTrueAntiH3L, + this3BodyMCInfo.genMomProton[0], this3BodyMCInfo.genMomProton[1], this3BodyMCInfo.genMomProton[2], + this3BodyMCInfo.genMomPion[0], this3BodyMCInfo.genMomPion[1], this3BodyMCInfo.genMomPion[2], + this3BodyMCInfo.genMomDeuteron[0], this3BodyMCInfo.genMomDeuteron[1], this3BodyMCInfo.genMomDeuteron[2], this3BodyMCInfo.isReco, + this3BodyMCInfo.motherLabel, this3BodyMCInfo.motherPdgCode, this3BodyMCInfo.daughterPrPdgCode, this3BodyMCInfo.daughterPiPdgCode, this3BodyMCInfo.daughterDePdgCode, this3BodyMCInfo.isDeuteronPrimary, @@ -1190,9 +1187,8 @@ struct decay3bodyBuilder { // fill analysis tables with built candidate fillAnalysisTables(); return; - } else { - return; } + return; } // ______________________________________________________________ @@ -1208,10 +1204,11 @@ struct decay3bodyBuilder { // global mother ID: proton, pion, and deuteron have common mother and it's a hypertriton // first, check identity of MC daughters - if (std::abs(mcParticlePr.pdgCode()) != PDG_t::kProton || std::abs(mcParticleDe.pdgCode()) != o2::constants::physics::Pdg::kDeuteron || (std::abs(mcParticlePi.pdgCode()) != PDG_t::kPiPlus && std::abs(mcParticlePi.pdgCode()) != PDG_t::kMuonMinus)) { - return -4; + if (std::abs(mcParticlePr.pdgCode()) != PDG_t::kProton || (std::abs(mcParticlePi.pdgCode()) != PDG_t::kPiPlus && std::abs(mcParticlePi.pdgCode()) != PDG_t::kMuonMinus)) { + return -4; // at least one of the daughters has wrong identity (in this case it's either proton or pion wrong identity) } - // check if the pion track is a muon coming from a pi -> mu + vu decay, if yes, take the mother pi + // --> now the proton is a proton and the pion is either a pi+ or a muon + // check if the pion track is a muon coming from a pi -> mu + vu decay, if yes, take the mother pi, if not, fill as background auto mcParticlePiTmp = mcParticlePi; if (std::abs(mcParticlePiTmp.pdgCode()) == PDG_t::kMuonMinus) { bool isMuonReco = false; @@ -1224,9 +1221,10 @@ struct decay3bodyBuilder { } // If the track is a muon but none of its mothers is a pi+, treat as wrong identity if (!isMuonReco) { - return -4; + return -4; // at least one of the daughters has wrong identity (in this case it's pion wrong identity) } } + // --> now proton and pion have correct identity // now first check if the proton and pion have the same mother and it is a Lambda for (const auto& motherPr : mcParticlePr.template mothers_as()) { @@ -1236,6 +1234,13 @@ struct decay3bodyBuilder { } } } + // --> now proton and pion have correct identity and are not from a common mother Lambda + + // now check if deuteron has correct identity + if (std::abs(mcParticleDe.pdgCode()) != o2::constants::physics::Pdg::kDeuteron) { + return -4; // at least one daughter has wrong identity (in this case it's the deuteron) + } + // --> now the deuteron has correct identity // now check if all three daughters have the same mother int momID = -1; @@ -1258,25 +1263,24 @@ struct decay3bodyBuilder { // check if the common mother is a hypertriton if (std::abs(momPdgCode) == o2::constants::physics::Pdg::kHyperTriton) { return momID; - } else { - return -1; // common mother found but not a hypertriton } + return -1; // common mother found but not a hypertriton } // ______________________________________________________________ // function to reset MCInfo void resetMCInfo(mc3Bodyinfo& mcInfo) { - mcInfo.label = -1; + mcInfo.motherLabel = -999; mcInfo.genMomentum[0] = -1., mcInfo.genMomentum[1] = -1., mcInfo.genMomentum[2] = -1.; mcInfo.genDecVtx[0] = -1., mcInfo.genDecVtx[1] = -1., mcInfo.genDecVtx[2] = -1.; mcInfo.genCt = -1.; mcInfo.genPhi = -1., mcInfo.genEta = -1., mcInfo.genRapidity = -1.; - mcInfo.genMomProton = -1., mcInfo.genMomPion = -1., mcInfo.genMomDeuteron = -1.; - mcInfo.genPtProton = -1., mcInfo.genPtPion = -1., mcInfo.genPtDeuteron = -1.; - mcInfo.isTrueH3L = false, mcInfo.isTrueAntiH3L = false; + mcInfo.genMomProton[0] = -1., mcInfo.genMomProton[1] = -1., mcInfo.genMomProton[2] = -1.; + mcInfo.genMomPion[0] = -1., mcInfo.genMomPion[1] = -1., mcInfo.genMomPion[2] = -1.; + mcInfo.genMomDeuteron[0] = -1., mcInfo.genMomDeuteron[1] = -1., mcInfo.genMomDeuteron[2] = -1.; mcInfo.isReco = false; - mcInfo.motherPdgCode = -1; + mcInfo.motherPdgCode = 0; mcInfo.daughterPrPdgCode = -1, mcInfo.daughterPiPdgCode = -1, mcInfo.daughterDePdgCode = -1; mcInfo.isDeuteronPrimary = false; return; @@ -1338,7 +1342,7 @@ struct decay3bodyBuilder { auto yAxis = registry.get(HIST("Mixing/hDecay3BodyRadiusPhi"))->GetYaxis(); for (const auto& decay3body : decay3bodys) { - int bin_Radius, bin_Phi; + int bin_Radius{}, bin_Phi{}; if (decay3bodyBuilderOpts.useKFParticle) { bin_Radius = xAxis->FindBin(decay3body.radiusKF()); bin_Phi = yAxis->FindBin(decay3body.phiKF()); @@ -1352,10 +1356,10 @@ struct decay3bodyBuilder { } if (decay3bodyBuilderOpts.useKFParticle) { - Binning3BodyKF binningOnRadPhiKF{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}, true}; + Binning3BodyKF binningOnRadPhiKF{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}}; buildMixedCandidates(decay3bodys, binningOnRadPhiKF); } else { - Binning3BodyDCAfitter binningOnRadPhiDCA{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}, true}; + Binning3BodyDCAfitter binningOnRadPhiDCA{{mixingOpts.bins3BodyRadius, mixingOpts.bins3BodyPhi}}; buildMixedCandidates(decay3bodys, binningOnRadPhiDCA); } } diff --git a/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx new file mode 100644 index 00000000000..22cca32068a --- /dev/null +++ b/PWGLF/TableProducer/Nuspex/deuteronInTriggeredEvents.cxx @@ -0,0 +1,1192 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file deuteronInTriggeredEvents.cxx +/// \brief nuclei and antinuclei spectra analysis task in MB and triggered events +/// +/// \author Cristian Moscatelli +/// \since 05/2026 +// ================ +// +// Based on PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx +// +// Executable + dependencies: +// +// data (run3): +// o2-analysis-lf-deuteron-in-triggered-events, o2-analysis-event-selection-service +// o2-analysis-propagationservice, o2-analysis-trackselection, o2-analysis-track-extra-v002-converter +// o2-analysis-pid-tof-merge, o2-analysis-pid-tpc-service, o2-analysis-ft0-corrected-table +// +// mc: +// same as Data and o2-analysis-mccollision-converter + +#include "PWGLF/DataModel/LFSlimNucleiTables.h" +#include "PWGLF/Utils/inelGt.h" +// +#include "PWGJE/Core/JetBkgSubUtils.h" +#include "PWGJE/Core/JetUtilities.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/PID/PIDTOF.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include // for PDG codes +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +struct NucleusCandidate { + int globalIndex = 0; + int collTrackIndex = 0; + float pt = 0.f; + float eta = 0.f; + float phi = 0.f; + float tpcInnerParam = 0.f; + float beta = 0.f; + float zVertex = 0.f; + int nContrib = 0; + float dcaXY = 0.f; + float dcaZ = 0.f; + float tpcSignal = 0.f; + float itsChi2 = 0.f; + float tpcChi2 = 0.f; + float tofChi2 = 0.f; + std::array nSigmaTPC{}; + std::array tofMasses{}; + bool fillTree = false; + bool fillDCAHist = false; + bool correctPV = false; + bool isSecondary = false; + bool fromWeakDecay = false; + uint16_t flags = 0; + uint8_t tpcFindableCls = 0; + uint8_t tpcCrossedRows = 0; + uint8_t itsClsMap = 0; + uint8_t tpcNCls = 0; + uint8_t tpcNClsShared = 0; + uint8_t itsNCls = 0; + uint32_t clusterSizesITS = 0; +}; + +namespace nuclei +{ +constexpr double BBMomScalingDefault[5][2]{ + {1., 1.}, + {1., 1.}, + {1., 1.}, + {1., 1.}, + {1., 1.}}; +constexpr double BetheBlochDefault[5][6]{ + {-136.71, 0.441, 0.2269, 1.347, 0.8035, 0.09}, + {-136.71, 0.441, 0.2269, 1.347, 0.8035, 0.09}, + {-239.99, 1.155, 1.099, 1.137, 1.006, 0.09}, + {-321.34, 0.6539, 1.591, 0.8225, 2.363, 0.09}, + {-586.66, 1.859, 4.435, 0.282, 3.201, 0.09}}; +constexpr double NSigmaTPCdefault[5][2]{ + {-5., 5.}, + {-5., 5.}, + {-5., 5.}, + {-5., 5.}, + {-5., 5.}}; +constexpr double DCAcutDefault[5][2]{ + {1., 1.}, + {1., 1.}, + {1., 1.}, + {1., 1.}, + {1., 1.}}; +constexpr int TreeConfigDefault[5][2]{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; +constexpr int DCAHistDefault[5][2]{ + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; +constexpr double DownscalingDefault[5][1]{ + {1.}, + {1.}, + {1.}, + {1.}, + {1.}}; +constexpr int Species{5}; // Number of analysed species +constexpr int Codes[5]{2212, 1000010020, 1000010030, 1000020030, 1000020040}; // PGD code of these particles +constexpr float Charges[5]{1.f, 1.f, 1.f, 2.f, 2.f}; // Their charge +constexpr float Masses[5]{MassProton, MassDeuteron, MassTriton, MassHelium3, MassAlpha}; // Their masses +static const std::vector matter{"M", "A"}; // Type of particles (matter or antimatter) +static const std::vector pidName{"TPC", "TOF"}; // Type of PID +static const std::vector hfMothCodes{511, 521, 531, 541, 5122}; // b-mesons + Lambda_b +static const std::vector names{"proton", "deuteron", "triton", "He3", "alpha"}; // Particles name +static const std::vector treeConfigNames{"Filter trees", "Use TOF selection"}; +static const std::vector dcaConfigNames{"Save DCA hist", "Matter/Antimatter"}; +static const std::vector nSigmaConfigName{"nsigma_min", "nsigma_max"}; // label for nsigmaTPC selection +static const std::vector nDCAConfigName{"max dcaXY", "max dcaZ"}; // label for DCA selection +static const std::vector downscalingConfigName{"Fraction of kept candidates"}; // Fraction of candidates to be kept +static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; // BB paramteters +static const std::vector binnedVariableNames{"DCAxy", "dcaZ", "TPCnsigma", "TOFnsigma", "TOFmass"}; +static const std::vector chargeLabelNames{"Positive", "Negative"}; // Particles charge (pos or neg) + +float pidCuts[2][5][2]; +// This is a set of 2x5x2 3D histograms +// described by smart pointer. +// Keep in mind: first index: TPC or TOF. Second index: type of particle. Third index: charge. +std::shared_ptr hNsigma[2][5][2]; +std::shared_ptr hTOFmass[5][2]; +std::shared_ptr hGenNuclei[5][2]; +std::shared_ptr hMomRes[5][2]; +std::shared_ptr hNsigmaEta[2][5][2]; +std::shared_ptr hTOFmassEta[5][2]; +std::shared_ptr hDCAxy[2][5][2]; +std::shared_ptr hDCAz[2][5][2]; +std::shared_ptr hGloTOFtracks[2]; +std::shared_ptr hDeltaP[2][5]; +std::shared_ptr hDCAHists[2][5]; +o2::base::MatLayerCylSet* lut = nullptr; // describe detector material + +std::vector candidates; + +enum EvSel { + kTVX = 0, + kTFBorder, // Here we can substitute with sel8 + kITSROFborder, + kZvtx, + kNoSameBunchPileup, + kIsGoodZvtxFT0vsPV, + kIsGoodITSLayersAll, + kIsEPtriggered, + kINELgt0, + kIsJetTriggered, // Check: evSel for event with at least one jet over pT-threshold + kNevSels +}; + +static const std::vector eventSelectionTitle{"Event selections"}; +static const std::vector eventSelectionLabels{"TVX", "TF border", "ITS ROF border", "Z vtx", "No same-bunch pile-up", "kIsGoodZvtxFT0vsPV", "isGoodITSLayersAll", "isEPtriggered", "IsINELgt0", "IsJetTriggered"}; + +constexpr int EvSelDefault[10][1]{ + {1}, + {1}, + {1}, // Default: sel8 + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, // INEL > 0 + {1}}; // Triggered on jets + +enum EvGenSel : uint8_t { + kGenIsINELgt0 = 1 << 0, + kGenIsJetTriggered = 1 << 1, + kGenHasRecoEv = 1 << 2 +}; +} // namespace nuclei + +struct DeuteronInTriggeredEvents { + enum { + kProton = BIT(0), + kDeuteron = BIT(1), + kTriton = BIT(2), + kHe3 = BIT(3), + kHe4 = BIT(4), + kHasTOF = BIT(5), + kHasTRD = BIT(6), + kIsAmbiguous = BIT(7), /// just a placeholder now + kITSrof = BIT(8), + kIsPhysicalPrimary = BIT(9), /// MC flags starting from the second half of the short + kIsSecondaryFromMaterial = BIT(10), + kIsSecondaryFromWeakDecay = BIT(11) /// the last 4 bits are reserved for the PID in tracking + }; + + Produces nucleiTable; // For data + Produces nucleiTableMCExtension; // For MC analysis + Produces genEventMCSel; // For MC reco events + Produces nucleiTableMC; // For MC analysis as data + Service ccdb; + Service pdgDB; // For INELgt0 gen MC selection + Zorro zorro; // Definition of Zorro: helpful for skimmed data + OutputObj zorroSummary{"zorroSummary"}; + + Configurable cfgCompensatePIDinTracking{"cfgCompensatePIDinTracking", false, "If true, divide tpcInnerParam by the electric charge"}; + + struct : o2::framework::ConfigurableGroup { + Configurable> dcaMax{"dcaMax", {nuclei::DCAcutDefault[0], 5, 2, nuclei::names, nuclei::nDCAConfigName}, "Max DCAxy and dcaZ for light nuclei"}; + Configurable etaMax{"etaMax", 0.8f, "Max Eta for tracks"}; + Configurable itsNClusMin{"itsNClusMin", 5, "Minimum number of ITS clusters"}; + Configurable itsChi2ClusMax{"itsChi2ClusMax", 36.f, "Max ITS Chi2 per cluster"}; + Configurable rapidityMax{"rapidityMax", 1.f, "Maximum rapidity for tracks"}; + Configurable rapidityMin{"rapidityMin", -1.f, "Minimum rapidity for tracks"}; + Configurable rapidityToggle{"rapidityToggle", false, "If true, use rapidity cuts"}; + Configurable tpcChi2ClusMax{"tpcChi2ClusMax", 4.f, "Max TPC Chi2 per cluster"}; + Configurable tpcNCrossedRowsMin{"tpcNCrossedRowsMin", 70, "Minimum number of TPC crossed rows"}; + Configurable tpcNClsMin{"tpcNClsMin", 80, "Minimum number of TPC clusters"}; + Configurable tpcRigidityMin{"tpcRigidityMin", 0.5f, "Minimum TPC rigidity for tracks"}; + Configurable> tpcNSigmaMax{"tpcNSigmaMax", {nuclei::NSigmaTPCdefault[0], 5, 2, nuclei::names, nuclei::nSigmaConfigName}, "TPC nsigma selection for light nuclei"}; + } cfgTrackCut; + + Configurable cfgCutPtMinTree{"cfgCutPtMinTree", 0.2f, "Minimum track transverse momentum for tree saving"}; + Configurable cfgCutPtMaxTree{"cfgCutPtMaxTree", 15.0f, "Maximum track transverse momentum for tree saving"}; + + // Event selections + Configurable> cfgEventSelections{"cfgEventSelections", {nuclei::EvSelDefault[0], 10, 1, nuclei::eventSelectionLabels, nuclei::eventSelectionTitle}, "Event selections"}; + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + + Configurable> cfgMomentumScalingBetheBloch{"cfgMomentumScalingBetheBloch", {nuclei::BBMomScalingDefault[0], 5, 2, nuclei::names, nuclei::chargeLabelNames}, "TPC Bethe-Bloch momentum scaling for light nuclei"}; + Configurable> cfgBetheBlochParams{"cfgBetheBlochParams", {nuclei::BetheBlochDefault[0], 5, 6, nuclei::names, nuclei::betheBlochParNames}, "TPC Bethe-Bloch parameterisation for light nuclei"}; + Configurable> cfgDownscaling{"cfgDownscaling", {nuclei::DownscalingDefault[0], 5, 1, nuclei::names, nuclei::downscalingConfigName}, "Fraction of kept candidates for light nuclei"}; + Configurable> cfgTreeConfig{"cfgTreeConfig", {nuclei::TreeConfigDefault[0], 5, 2, nuclei::names, nuclei::treeConfigNames}, "Filtered trees configuration"}; + Configurable cfgFillGenSecondaries{"cfgFillGenSecondaries", 0, "Fill generated secondaries (0: no, 1: only weak decays, 2: all of them)"}; + Configurable> cfgDCAHists{"cfgDCAHists", {nuclei::DCAHistDefault[0], 5, 2, nuclei::names, nuclei::dcaConfigNames}, "DCA hist configuration"}; + + Configurable cfgNsigmaTPCcutDCAhists{"cfgNsigmaTPCcutDCAhists", 3., "TPC nsigma cut for DCA hists"}; + Configurable cfgDeltaTOFmassCutDCAhists{"cfgDeltaTOFmassCutDCAhists", 0.2, "Delta TOF mass cut for DCA hists"}; + Configurable cfgNsigmaTPCcutTOFhists{"cfgNsigmaTPCcutTOFhists", 3., "TPC nsigma cut for TOF analysis"}; + ConfigurableAxis cfgDCAxyBinsProtons{"cfgDCAxyBinsProtons", {1500, -1.5f, 1.5f}, "DCAxy binning for Protons"}; + ConfigurableAxis cfgDCAxyBinsDeuterons{"cfgDCAxyBinsDeuterons", {1500, -1.5f, 1.5f}, "DCAxy binning for Deuterons"}; + ConfigurableAxis cfgDCAxyBinsTritons{"cfgDCAxyBinsTritons", {1500, -1.5f, 1.5f}, "DCAxy binning for Tritons"}; + ConfigurableAxis cfgDCAxyBinsHe3{"cfgDCAxyBinsHe3", {1500, -1.5f, 1.5f}, "DCAxy binning for He3"}; + ConfigurableAxis cfgDCAxyBinsAlpha{"cfgDCAxyBinsAlpha", {1500, -1.5f, 1.5f}, "DCAxy binning for Alpha"}; + + ConfigurableAxis cfgPtBinsProtons{"cfgPtBinsProtons", {100, 0., 10.}, "Pt binning for Protons"}; + ConfigurableAxis cfgPtBinsDeuterons{"cfgPtBinsDeuterons", {100, 0., 10.}, "Pt binning for Deuterons"}; + ConfigurableAxis cfgPtBinsTritons{"cfgPtBinsTritons", {100, 0., 10.}, "Pt binning for Tritons"}; + ConfigurableAxis cfgPtBinsHe3{"cfgPtBinsHe3", {100, 0., 10.}, "Pt binning for He3"}; + ConfigurableAxis cfgPtBinsAlpha{"cfgPtBinsAlpha", {100, 0., 10.}, "Pt binning for Alpha"}; + + Configurable cfgCMrapidity{"cfgCMrapidity", 0.f, "Rapidity of the center of mass (only for p-Pb)"}; + ConfigurableAxis cfgMomResBins{"cfgMomResBins", {200, -1., 1.}, "Momentum resolution binning"}; + ConfigurableAxis cfgNsigmaTPCbins{"cfgNsigmaTPCbins", {100, -5., 5.}, "nsigma_TPC binning"}; + ConfigurableAxis cfgNsigmaTOFbins{"cfgNsigmaTOFbins", {100, -5., 5.}, "nsigma_TOF binning"}; + ConfigurableAxis cfgTOFmassBins{"cfgTOFmassBins", {200, -5., 5.}, "TOF mass binning"}; + ConfigurableAxis cfgNITSClusBins{"cfgNITSClusBins", {3, 4.5, 7.5}, "N ITS clusters binning"}; + ConfigurableAxis cfgNTPCClusBins{"cfgNTPCClusBins", {3, 89.5, 159.5}, "N TPC clusters binning"}; + + // Configurable for working with skimmed data + Configurable cfgApplyMCEvSel{"cfgApplyMCEvSel", false, "If true, apply jet-trigger selection on gen events"}; + Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable cfgTriggerList{"cfgTriggerList", "fH2", "Trigger List"}; + Configurable cfgSelectTrgEv{"cfgSelectTrgEv", false, "If true, select events with active trigger list"}; + + // Configurable for jet identification + Configurable cfgRJet{"cfgRJet", 0.6, "R_jet"}; + Configurable cfgEtaJet{"cfgEtaJet", 0.9, "Jet acceptance"}; + + // running variables for track tuner + o2::dataformats::DCA mDcaInfoCov; + o2::track::TrackParametrizationWithError mTrackParCov; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + + // CCDB options + Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrLUT), "Type of material correction"}; + Configurable cfgCCDBurl{"cfgCCDBurl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgZorroCCDBpath{"cfgZorroCCDBpath", "EventFiltering/Zorro/", "path to the zorro ccdb objects"}; + int mRunNumber = 0; + float mBz = 0.f; + + // Utility object for jet background subtraction methods + JetBkgSubUtils backgroundSub; + + using TrackCandidates = soa::Join; + + HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + double computeAbsoDecL(const aod::McParticles::iterator& particle) + { + if (!particle.has_daughters()) + return -1.f; + + const float mothVtx[3]{particle.vx(), particle.vy(), particle.vz()}; + float dauVtx[3]{0.f, 0.f, 0.f}; + auto daughters = particle.daughters_as(); + for (const auto& dau : daughters) { + if (std::abs(dau.pdgCode()) != PDG_t::kGamma && std::abs(dau.pdgCode()) != PDG_t::kElectron) { + dauVtx[0] = dau.vx(); + dauVtx[1] = dau.vy(); + dauVtx[2] = dau.vz(); + break; + } + } + return std::hypot(mothVtx[0] - dauVtx[0], mothVtx[1] - dauVtx[1], mothVtx[2] - dauVtx[2]); + } + + template + bool eventSelectionWithHisto(Tcoll& collision) + { + spectra.fill(HIST("hEventSelections"), 0); + + if (cfgEventSelections->get(nuclei::EvSel::kTVX) && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kTVX + 1); + + if (cfgEventSelections->get(nuclei::EvSel::kTFBorder) && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kTFBorder + 1); + + if (cfgEventSelections->get(nuclei::EvSel::kITSROFborder) && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kITSROFborder + 1); + + if (cfgEventSelections->get(nuclei::EvSel::kZvtx) && std::abs(collision.posZ()) > cfgCutVertex) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kZvtx + 1); + + if (cfgEventSelections->get(nuclei::EvSel::kNoSameBunchPileup) && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kNoSameBunchPileup + 1); + + if (cfgEventSelections->get(nuclei::EvSel::kIsGoodZvtxFT0vsPV) && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kIsGoodZvtxFT0vsPV + 1); + + if (cfgEventSelections->get(nuclei::EvSel::kIsGoodITSLayersAll) && !collision.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kIsGoodITSLayersAll + 1); + + if constexpr ( + requires { + collision.triggereventep(); + }) { + if (cfgEventSelections->get(nuclei::EvSel::kIsEPtriggered) && !collision.triggereventep()) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kIsEPtriggered + 1); + } + + if (cfgEventSelections->get(nuclei::EvSel::kINELgt0) && !collision.selection_bit(aod::kINELgtZERO)) { + return false; + } + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kINELgt0 + 1); + + auto bc = collision.template bc_as(); + initCCDB(bc); + + gRandom->SetSeed(bc.timestamp()); + + if (cfgSkimmedProcessing) { + bool isTriggered = zorro.isSelected(bc.globalBC()); /// Just let Zorro do the accounting + if (!isTriggered && cfgSelectTrgEv) + return false; + } + + spectra.fill(HIST("hEventSelections"), nuclei::EvSel::kIsJetTriggered + 1); + + return true; + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + if (cfgSkimmedProcessing) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), cfgTriggerList); + zorro.populateHistRegistry(spectra, bc.runNumber()); + } + auto timestamp = bc.timestamp(); + mRunNumber = bc.runNumber(); + + o2::parameters::GRPMagField* grpmag = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + o2::base::Propagator::initFieldFromGRP(grpmag); + o2::base::Propagator::Instance()->setMatLUT(nuclei::lut); + mBz = static_cast(grpmag->getNominalL3Field()); + LOGF(info, "Retrieved GRP for timestamp %ull (%i) with magnetic field of %1.2f kZG", timestamp, mRunNumber, mBz); + } + + void init(o2::framework::InitContext&) + { + zorroSummary.setObject(zorro.getZorroSummary()); + zorro.setBaseCCDBPath(cfgZorroCCDBpath.value); + ccdb->setURL(cfgCCDBurl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + const AxisSpec nSigmaAxes[2]{{cfgNsigmaTPCbins, "n#sigma_{TPC}"}, {cfgNsigmaTOFbins, "n#sigma_{TOF}"}}; + const AxisSpec tofMassAxis{cfgTOFmassBins, "TOF mass - PDG mass"}; + const AxisSpec ptResAxis{cfgMomResBins, "#Delta#it{p}_{T}/#it{p}_{T}"}; + const AxisSpec nITSClusAxis{cfgNITSClusBins, "N ITS clusters"}; + const AxisSpec nTPCClusAxis{cfgNTPCClusBins, "N TPC clusters"}; + const AxisSpec hasTRDAxis{2, -0.5, 1.5, "Has TRD"}; + const AxisSpec correctPVAxis{2, -0.5, 1.5, "Correct PV"}; + const AxisSpec isSecondaryAxis{2, -0.5, 1.5, "Is secondary"}; + const AxisSpec fromWeakDecayAxis{2, -0.5, 1.5, "From weak decay"}; + + const AxisSpec ptAxes[5]{ + {cfgPtBinsProtons, "#it{p}_{T} (GeV/#it{c})"}, + {cfgPtBinsDeuterons, "#it{p}_{T} (GeV/#it{c})"}, + {cfgPtBinsTritons, "#it{p}_{T} (GeV/#it{c})"}, + {cfgPtBinsHe3, "#it{p}_{T} (GeV/#it{c})"}, + {cfgPtBinsAlpha, "#it{p}_{T} (GeV/#it{c})"}}; + const AxisSpec dcaxyAxes[5]{ + {cfgDCAxyBinsProtons, "DCA_{xy} (cm)"}, + {cfgDCAxyBinsDeuterons, "DCA_{xy} (cm)"}, + {cfgDCAxyBinsTritons, "DCA_{xy} (cm)"}, + {cfgDCAxyBinsHe3, "DCA_{xy} (cm)"}, + {cfgDCAxyBinsAlpha, "DCA_{xy} (cm)"}}; + const AxisSpec dcazAxes[5]{ + {cfgDCAxyBinsProtons, "DCA_{z} (cm)"}, + {cfgDCAxyBinsDeuterons, "DCA_{z} (cm)"}, + {cfgDCAxyBinsTritons, "DCA_{z} (cm)"}, + {cfgDCAxyBinsHe3, "DCA_{z} (cm)"}, + {cfgDCAxyBinsAlpha, "DCA_{z} (cm)"}}; + const AxisSpec etaAxis{40, -1., 1., "#eta"}; + + spectra.add("hEventSelections", "hEventSelections", {HistType::kTH1D, {{nuclei::EvSel::kNevSels + 1, -0.5f, static_cast(nuclei::EvSel::kNevSels) + 0.5f}}}); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(1, "all"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kTVX + 2, "TVX"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kTFBorder + 2, "TFborder"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kITSROFborder + 2, "ITSROFborder"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kZvtx + 2, "Zvtx"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kNoSameBunchPileup + 2, "kNoSameBunchPileup"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kIsGoodZvtxFT0vsPV + 2, "isGoodZvtxFT0vsPV"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kIsGoodITSLayersAll + 2, "IsGoodITSLayersAll"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kIsEPtriggered + 2, "IsEPtriggered"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kINELgt0 + 2, "IsINELgt0"); + spectra.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(nuclei::EvSel::kIsJetTriggered + 2, "IsJetTriggered"); + + // Distribution of z-vertex of selected events + spectra.add("hRecVtxZData", "collision z position", HistType::kTH1F, {{200, -20., +20., "z position (cm)"}}); + // Distribution of z-vertex of generated events + if (doprocessMC) { + spectra.add("hGenVtxZ", " generated collision z position", HistType::kTH1F, {{200, -20., +20., "z position (cm)"}}); + } + spectra.add("hTpcSignalData", "Specific energy loss", HistType::kTH2F, {{600, -6., 6., "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); + spectra.add("hTpcSignalDataSelected", "Specific energy loss for selected particles", HistType::kTH2F, {{600, -6., 6., "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); + spectra.add("hTofSignalData", "TOF beta", HistType::kTH2F, {{500, 0., 5., "#it{p} (GeV/#it{c})"}, {750, 0, 1.5, "TOF #beta"}}); + + for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { + nuclei::hGloTOFtracks[iC] = spectra.add(fmt::format("hTPCTOFtracks{}", nuclei::matter[iC]).data(), fmt::format("Global vs TOF matched {} tracks in a collision", nuclei::chargeLabelNames[iC]).data(), HistType::kTH2D, {{300, -0.5, 300.5, "Number of global tracks"}, {300, -0.5, 300.5, "Number of TOF matched tracks"}}); + + for (int iS{0}; iS < nuclei::Species; ++iS) { + nuclei::hNsigma[0][iS][iC] = spectra.add(fmt::format("h{}nsigma{}_{}", nuclei::pidName[0], nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("n#sigma_{{}} {} {}", nuclei::pidName[0], nuclei::matter[iC], nuclei::names[iS]).data(), HistType::kTH2D, {ptAxes[iS], nSigmaAxes[0]}); + nuclei::hNsigmaEta[0][iS][iC] = spectra.add(fmt::format("h{}nsigmaEta{}_{}", nuclei::pidName[0], nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("n#sigma_{{}} {} {} vs #eta", nuclei::pidName[0], nuclei::matter[iC], nuclei::names[iS]).data(), HistType::kTH3D, {etaAxis, ptAxes[iS], nSigmaAxes[0]}); + + for (unsigned int iPID{0}; iPID < nuclei::matter.size(); ++iPID) { + nuclei::hDCAxy[iPID][iS][iC] = spectra.add(fmt::format("hDCAxy{}_{}_{}", nuclei::pidName[iPID], nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("DCAxy {} {} {}", nuclei::pidName[iPID], nuclei::matter[iC], nuclei::names[iS]).data(), HistType::kTH2D, {ptAxes[iS], dcaxyAxes[iS]}); + nuclei::hDCAz[iPID][iS][iC] = spectra.add(fmt::format("hDCAz{}_{}_{}", nuclei::pidName[iPID], nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("dcaZ {} {} {}", nuclei::pidName[iPID], nuclei::matter[iC], nuclei::names[iS]).data(), HistType::kTH2D, {ptAxes[iS], dcazAxes[iS]}); + } + nuclei::hTOFmass[iS][iC] = spectra.add(fmt::format("h{}TOFmass{}", nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("TOF mass - {} PDG mass", nuclei::names[iS]).data(), HistType::kTH2D, {ptAxes[iS], tofMassAxis}); + nuclei::hTOFmassEta[iS][iC] = spectra.add(fmt::format("h{}TOFmassEta{}", nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("TOF mass - {} PDG mass", nuclei::names[iS]).data(), HistType::kTH3D, {etaAxis, ptAxes[iS], tofMassAxis}); + nuclei::hDeltaP[iC][iS] = spectra.add(fmt::format("hDeltaP{}_{}", nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("#Delta#it{{p}}/#it{{p}} {} {}", nuclei::matter[iC], nuclei::names[iS]).data(), HistType::kTH2D, {{232, 0.2, 6., "#it{p} (GeV/#it{{c}})"}, {200, -1.0, 1.0, "(#it{p}_{IU} - #it{p}_{TPC}) / #it{p}"}}); + if (doprocessMC) { + nuclei::hMomRes[iS][iC] = spectra.add(fmt::format("h{}MomRes{}", nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("Momentum resolution {}", nuclei::names[iS]).data(), HistType::kTH2D, {ptAxes[iS], ptResAxis}); + nuclei::hGenNuclei[iS][iC] = spectra.add(fmt::format("h{}Gen{}", nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("Generated {}", nuclei::names[iS]).data(), HistType::kTH1D, {ptAxes[iS]}); + nuclei::hDCAHists[iC][iS] = spectra.add(fmt::format("hDCAHists{}_{}", nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("DCA histograms {} {}", nuclei::matter[iC], nuclei::names[iS]).data(), HistType::kTHnSparseF, {ptAxes[iS], dcaxyAxes[iS], dcazAxes[iS], nSigmaAxes[0], tofMassAxis, nITSClusAxis, nTPCClusAxis, correctPVAxis, isSecondaryAxis, fromWeakDecayAxis}); + } else { + nuclei::hDCAHists[iC][iS] = spectra.add(fmt::format("hDCAHists{}_{}", nuclei::matter[iC], nuclei::names[iS]).data(), fmt::format("DCA histograms {} {}", nuclei::matter[iC], nuclei::names[iS]).data(), HistType::kTHnSparseF, {ptAxes[iS], dcaxyAxes[iS], dcazAxes[iS], nSigmaAxes[0], tofMassAxis, nITSClusAxis, nTPCClusAxis}); + } + } + } + + // Setting the nsigma interval in which the the candidate is kept + // The first index is set to 0 because it is for TPC + for (int iS{0}; iS < nuclei::Species; ++iS) { + for (unsigned int iMax{0}; iMax < nuclei::pidName.size(); ++iMax) { + nuclei::pidCuts[0][iS][iMax] = cfgTrackCut.tpcNSigmaMax->get(iS, iMax); + } + } + + nuclei::lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get("GLO/Param/MatLUT")); + } + + // Find hit on ITS layer + template + bool hasHitITS(const TrackIts& track, int layer) + { + int ibit = layer - 1; + return (track.itsClusterMap() & (1 << ibit)); + } + + template + bool isJetTriggered(Ttrks const& tracks) + { + // Defining trigger condition + double jetPtThreshold(0.0); + + if (cfgTriggerList.value == "fChJetLowPt") { + jetPtThreshold = 30.0; + } else if (cfgTriggerList.value == "fChJetHighPt") { + jetPtThreshold = 55.0; + } else { + return false; + } + + // Loop over tracks + int id(-1); + std::vector fjParticles; + fjParticles.clear(); + for (auto const& track : tracks) { + id++; + if (!passedTrackSelectionForJetReconstruction(track)) + continue; + + // 4-momentum representation of a particle + fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(MassPionCharged)); + fourMomentum.set_user_index(id); + fjParticles.emplace_back(fourMomentum); + } + + if (fjParticles.empty()) + return false; // reject 0-particles events + + // Cluster particles using the the anti-kT algorithm + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgRJet); // Defining the algorithm to cluster, and the jet radius + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); // Activate area evaluation, and set area evaluation method + fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); // Declare the will of applying clustering algorithm with area evaluation to the selected candidates + std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); + + if (jets.empty()) + return false; // skip events with no reconstructed jets + auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], cfgRJet); + + // Analyse only leading jet + const auto& jet = jets[0]; + if (std::fabs(jet.eta()) > cfgEtaJet) + return false; // Jet must be fully contained in the acceptance + + // Jet pt must be larger than threshold + auto jetForSub = jet; + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + if (jetMinusBkg.pt() < jetPtThreshold) + return false; + + return true; + } + + template + bool isMCJetTriggered(McParts const& McParticles, aod::McParticles const& particlesMC) + { + // Defining trigger condition + double jetPtThreshold(0.0); + + if (cfgTriggerList.value == "fChJetLowPt") { + jetPtThreshold = 30.0; + } else if (cfgTriggerList.value == "fChJetHighPt") { + jetPtThreshold = 55.0; + } else { + return false; + } + + std::vector fjParticles; + fjParticles.clear(); + + for (auto const& particle : McParticles) { + + // MC particles per collision + if (!isPhysicalPrimaryOrFromHF(particle, particlesMC)) + continue; + + double minPtParticle = 0.1; + if (std::fabs(particle.eta()) > cfgEtaJet || particle.pt() < minPtParticle) + continue; + + // Build 4-momentum assuming charged pion mass + static constexpr float KMassPionChargedSquared = o2::constants::physics::MassPionCharged * o2::constants::physics::MassPionCharged; + const double energy = std::sqrt(particle.p() * particle.p() + KMassPionChargedSquared); + fastjet::PseudoJet fourMomentum(particle.px(), particle.py(), particle.pz(), energy); + fourMomentum.set_user_index(particle.pdgCode()); + fjParticles.emplace_back(fourMomentum); + } + + if (fjParticles.empty()) + return false; // reject 0-particles events + + // Cluster particles using the the anti-kT algorithm + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, cfgRJet); // Defining the algorithm to cluster, and the jet radius + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); // Activate area evaluation, and set area evaluation method + fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); // Declare the will of applying clustering algorithm with area evaluation to the selected candidates + std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); + + if (jets.empty()) + return false; // skip events with no reconstructed jets + auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], cfgRJet); + + // Analyse only leading jet + const auto& jet = jets[0]; + if (std::fabs(jet.eta()) > cfgEtaJet) + return false; // Jet must be fully contained in the acceptance + + // Jet pt must be larger than threshold + auto jetForSub = jet; + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + if (jetMinusBkg.pt() < jetPtThreshold) + return false; + + return true; + } + + // Check if particle is a physical primary or a decay product of a heavy-flavor hadron + bool isPhysicalPrimaryOrFromHF(aod::McParticle const& particle, aod::McParticles const& mcParticles) + { + // Keep only pi, K, p, e, mu + int pdg = std::abs(particle.pdgCode()); + if (!(pdg == PDG_t::kPiPlus || pdg == PDG_t::kKPlus || pdg == PDG_t::kProton || pdg == PDG_t::kElectron || pdg == PDG_t::kMuonMinus)) + return false; + + // Constants for identifying heavy-flavor (charm and bottom) content from PDG codes + static constexpr int KCharmQuark = 4; + static constexpr int KBottomQuark = 5; + static constexpr int Hundreds = 100; + static constexpr int Thousands = 1000; + + // Check if particle is from heavy-flavor decay + bool fromHF = false; + if (particle.has_mothers()) { + auto mother = mcParticles.iteratorAt(particle.mothersIds()[0]); + int motherPdg = std::abs(mother.pdgCode()); + fromHF = (motherPdg / Hundreds == KCharmQuark || motherPdg / Hundreds == KBottomQuark || motherPdg / Thousands == KCharmQuark || motherPdg / Thousands == KBottomQuark); + } + + // Select only physical primary particles or from heavy-flavor + return (particle.isPhysicalPrimary() || fromHF); + } + + // Track selection criteria for creating jets + template + bool passedTrackSelectionForJetReconstruction(const JetTrack& track) + { + static constexpr int MinTpcCr = 70; + static constexpr double MaxChi2Tpc = 4.0; + static constexpr double MaxChi2Its = 36.0; + static constexpr double MinPtTrack = 0.1; + static constexpr double DcaxyMaxTrackPar0 = 0.0105; + static constexpr double DcaxyMaxTrackPar1 = 0.035; + static constexpr double DcaxyMaxTrackPar2 = 1.1; + static constexpr double DcazMaxTrack = 2.0; + + // General part + if (std::fabs(track.eta()) > cfgEtaJet) + return false; + if (track.pt() < MinPtTrack) + return false; + if (std::fabs(track.dcaXY()) > (DcaxyMaxTrackPar0 + DcaxyMaxTrackPar1 / std::pow(track.pt(), DcaxyMaxTrackPar2))) + return false; // DCAxy cut + if (std::fabs(track.dcaZ()) > DcazMaxTrack) + return false; // DCAz cut + // Part relative to ITS + if (!track.hasITS()) + return false; + if ((!hasHitITS(track, 1)) && (!hasHitITS(track, 2)) && (!hasHitITS(track, 3))) + return false; // Has Inner Barrel hit + if (track.itsChi2NCl() >= MaxChi2Its) + return false; + // Part relative to TPC + if (!track.hasTPC()) + return false; + if (track.tpcNClsCrossedRows() < MinTpcCr) + return false; + if (track.tpcChi2NCl() >= MaxChi2Tpc) + return false; + + return true; + } + + template + void fillDataInfo(Tcoll const& collision, Ttrks const& tracks) + { + spectra.fill(HIST("hRecVtxZData"), collision.posZ()); + + const o2::math_utils::Point3D collVtx{collision.posX(), collision.posY(), collision.posZ()}; + + const double bgScalings[5][2]{ + {nuclei::Charges[0] * cfgMomentumScalingBetheBloch->get(0u, 0u) / nuclei::Masses[0], nuclei::Charges[0] * cfgMomentumScalingBetheBloch->get(0u, 1u) / nuclei::Masses[0]}, + {nuclei::Charges[1] * cfgMomentumScalingBetheBloch->get(1u, 0u) / nuclei::Masses[1], nuclei::Charges[1] * cfgMomentumScalingBetheBloch->get(1u, 1u) / nuclei::Masses[1]}, + {nuclei::Charges[2] * cfgMomentumScalingBetheBloch->get(2u, 0u) / nuclei::Masses[2], nuclei::Charges[2] * cfgMomentumScalingBetheBloch->get(2u, 1u) / nuclei::Masses[2]}, + {nuclei::Charges[3] * cfgMomentumScalingBetheBloch->get(3u, 0u) / nuclei::Masses[3], nuclei::Charges[3] * cfgMomentumScalingBetheBloch->get(3u, 1u) / nuclei::Masses[3]}, + {nuclei::Charges[4] * cfgMomentumScalingBetheBloch->get(4u, 0u) / nuclei::Masses[4], nuclei::Charges[4] * cfgMomentumScalingBetheBloch->get(4u, 1u) / nuclei::Masses[4]}}; + + int nGloTracks[2]{0, 0}, nTOFTracks[2]{0, 0}; + + constexpr float MinMomentumForDeltaP = 0.2f; + + for (const auto& track : tracks) { // start loop over tracks + if (std::abs(track.eta()) > cfgTrackCut.etaMax || + track.tpcInnerParam() < cfgTrackCut.tpcRigidityMin || + track.itsNCls() < cfgTrackCut.itsNClusMin || + track.tpcNClsFound() < cfgTrackCut.tpcNClsMin || + track.tpcNClsCrossedRows() < cfgTrackCut.tpcNCrossedRowsMin || + track.tpcChi2NCl() > cfgTrackCut.tpcChi2ClusMax || + track.itsChi2NCl() > cfgTrackCut.itsChi2ClusMax) { + continue; + } + // temporary fix: tpcInnerParam() returns the momentum in all the software tags before --- CM: what does it mean + bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; + float correctedTpcInnerParam = (heliumPID && cfgCompensatePIDinTracking) ? track.tpcInnerParam() / 2 : track.tpcInnerParam(); + + spectra.fill(HIST("hTpcSignalData"), correctedTpcInnerParam * track.sign(), track.tpcSignal()); + float nSigma[2][5]{ + {-10., -10., -10., -10., -10.}, + {0.f, 0.f, 0.f, 0.f, 0.f}}; /// then we will calibrate the TOF mass for the He3 and Alpha + const int iC{track.sign() < 0}; + + /// Checking if we have outliers in the TPC-TOF correlation + nGloTracks[iC]++; + if (track.hasTOF()) { + nTOFTracks[iC]++; + } + + bool selectedTPC[5]{false}, goodToAnalyse{false}; + std::array nSigmaTPC; + // This part apply a selection on tracks: + // if the nsigma of the track is less than a certain value, it is accepted + for (int iS{0}; iS < nuclei::Species; ++iS) { // The first index here is set to 0 because it is TPC analysis + double expBethe{common::BetheBlochAleph(static_cast(correctedTpcInnerParam * bgScalings[iS][iC]), cfgBetheBlochParams->get(iS, 0u), cfgBetheBlochParams->get(iS, 1u), cfgBetheBlochParams->get(iS, 2u), cfgBetheBlochParams->get(iS, 3u), cfgBetheBlochParams->get(iS, 4u))}; + double expSigma{expBethe * cfgBetheBlochParams->get(iS, 5u)}; + nSigma[0][iS] = static_cast((track.tpcSignal() - expBethe) / expSigma); + nSigmaTPC[iS] = nSigma[0][iS]; + selectedTPC[iS] = (nSigma[0][iS] > nuclei::pidCuts[0][iS][0] && nSigma[0][iS] < nuclei::pidCuts[0][iS][1]); + goodToAnalyse = goodToAnalyse || selectedTPC[iS]; + if (selectedTPC[iS] && track.p() > MinMomentumForDeltaP) { + nuclei::hDeltaP[iC][iS]->Fill(track.p(), 1 - correctedTpcInnerParam / track.p()); // Important: see of there is a shift between track momentum and tpc innerparam + } + } + if (!goodToAnalyse) { + continue; + } + + mDcaInfoCov.set(999, 999, 999, 999, 999); + setTrackParCov(track, mTrackParCov); + mTrackParCov.setPID(track.pidForTracking()); + std::array dcaInfo; + o2::base::Propagator::Instance()->propagateToDCA(collVtx, mTrackParCov, mBz, 2.f, static_cast(cfgMaterialCorrection.value), &dcaInfo); + + float beta{o2::pid::tof::Beta::GetBeta(track)}; + spectra.fill(HIST("hTpcSignalDataSelected"), correctedTpcInnerParam * track.sign(), track.tpcSignal()); + spectra.fill(HIST("hTofSignalData"), correctedTpcInnerParam, beta); + beta = std::min(1.f - 1.e-6f, std::max(1.e-4f, beta)); /// sometimes beta > 1 or < 0 + uint16_t flag = static_cast((track.pidForTracking() & 0xF) << 12); // Store the PID of the track in the 12-15 bit + std::array tofMasses{-3.f, -3.f, -3.f, -3.f, -3.f}; + bool fillTree{false}; + bool fillDCAHist{false}; + bool correctPV{false}; + bool isSecondary{false}; + bool fromWeakDecay{false}; + + if (track.hasTOF()) { + flag |= kHasTOF; + } + if (track.hasTRD()) { + flag |= kHasTRD; + } + if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + flag |= kITSrof; + } + + for (int iS{0}; iS < nuclei::Species; ++iS) { + bool selectedTOF{false}; + if (std::abs(dcaInfo[1]) > cfgTrackCut.dcaMax->get(iS, 1)) { + continue; + } + ROOT::Math::LorentzVector> fvector{mTrackParCov.getPt() * nuclei::Charges[iS], mTrackParCov.getEta(), mTrackParCov.getPhi(), nuclei::Masses[iS]}; + float y{fvector.Rapidity() + cfgCMrapidity}; + for (unsigned int iPID{0}; iPID < nuclei::pidName.size(); ++iPID) { /// 0 TPC, 1 TOF + if (selectedTPC[iS]) { + if (iPID && !track.hasTOF()) { + continue; + } else if (iPID) { + selectedTOF = true; /// temporarly skipped + float charge{nuclei::Charges[iS]}; + tofMasses[iS] = correctedTpcInnerParam * charge * std::sqrt(1.f / (beta * beta) - 1.f) - nuclei::Masses[iS]; + } + if (!cfgTrackCut.rapidityToggle || (y > cfgTrackCut.rapidityMin && y < cfgTrackCut.rapidityMax)) { + if (std::abs(nSigmaTPC[iS]) < cfgNsigmaTPCcutDCAhists && (!iPID || std::abs(tofMasses[iS]) < cfgDeltaTOFmassCutDCAhists)) { + nuclei::hDCAxy[iPID][iS][iC]->Fill(fvector.pt(), dcaInfo[0]); + nuclei::hDCAz[iPID][iS][iC]->Fill(fvector.pt(), dcaInfo[1]); + } + if (std::abs(dcaInfo[0]) < cfgTrackCut.dcaMax->get(iS, 0u)) { + if (!iPID) { /// temporary exclusion of the TOF nsigma PID for the He3 and Alpha + nuclei::hNsigma[iPID][iS][iC]->Fill(fvector.pt(), nSigma[iPID][iS]); + nuclei::hNsigmaEta[iPID][iS][iC]->Fill(fvector.eta(), fvector.pt(), nSigma[iPID][iS]); + } + if (iPID && std::abs(nSigmaTPC[iS]) < cfgNsigmaTPCcutTOFhists) { // Apply nsigmaTPC cut on TOF tracks + nuclei::hTOFmass[iS][iC]->Fill(fvector.pt(), tofMasses[iS]); + nuclei::hTOFmassEta[iS][iC]->Fill(fvector.eta(), fvector.pt(), tofMasses[iS]); + } + } + } + } + } + if (selectedTPC[iS]) { + if (cfgTreeConfig->get(iS, 1u) && !selectedTOF) { + continue; + } + !fillTree && cfgTreeConfig->get(iS, 0u) ? fillTree = true : fillTree; + !fillDCAHist && cfgDCAHists->get(iS, iC) ? fillDCAHist = true : fillDCAHist; + bool setPartFlag = cfgTreeConfig->get(iS, 0u) || cfgDCAHists->get(iS, iC); + if (setPartFlag) { + if (cfgDownscaling->get(iS) < 1. && gRandom->Rndm() > cfgDownscaling->get(iS)) { + continue; + } + flag |= BIT(iS); + } + } + } + if (flag & (kProton | kDeuteron | kTriton | kHe3 | kHe4) || doprocessMC) { /// ignore PID pre-selections for the MC + if (fillTree) { + if (flag & kTriton) { + if (track.pt() < cfgCutPtMinTree || track.pt() > cfgCutPtMaxTree || track.sign() > 0) + continue; + } + } + nuclei::candidates.emplace_back(NucleusCandidate{ + static_cast(track.globalIndex()), static_cast(track.collisionId()), (1 - 2 * iC) * mTrackParCov.getPt(), mTrackParCov.getEta(), mTrackParCov.getPhi(), + correctedTpcInnerParam, beta, collision.posZ(), collision.numContrib(), dcaInfo[0], dcaInfo[1], track.tpcSignal(), track.itsChi2NCl(), track.tpcChi2NCl(), track.tofChi2(), + nSigmaTPC, tofMasses, fillTree, fillDCAHist, correctPV, isSecondary, fromWeakDecay, flag, track.tpcNClsFindable(), static_cast(track.tpcNClsCrossedRows()), track.itsClusterMap(), + static_cast(track.tpcNClsFound()), static_cast(track.tpcNClsShared()), static_cast(track.itsNCls()), static_cast(track.itsClusterSizes())}); + } + } // end loop over tracks + + nuclei::hGloTOFtracks[0]->Fill(nGloTracks[0], nTOFTracks[0]); + nuclei::hGloTOFtracks[1]->Fill(nGloTracks[1], nTOFTracks[1]); + } + + void processData(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) + { + nuclei::candidates.clear(); + if (!eventSelectionWithHisto(collision)) { + return; + } + + fillDataInfo(collision, tracks); // Apply selection on track candidates and saving in structure nuclei + + for (size_t i1{0}; i1 < nuclei::candidates.size(); ++i1) { + auto& c1 = nuclei::candidates[i1]; + if (c1.fillTree) { + nucleiTable(c1.pt, c1.eta, c1.phi, c1.tpcInnerParam, c1.beta, c1.zVertex, c1.nContrib, c1.dcaXY, c1.dcaZ, c1.tpcSignal, c1.itsChi2, c1.tpcChi2, c1.tofChi2, c1.flags, c1.tpcFindableCls, c1.tpcCrossedRows, c1.itsClsMap, c1.tpcNCls, c1.tpcNClsShared, c1.clusterSizesITS); + } + if (c1.fillDCAHist) { + for (int iS{0}; iS < nuclei::Species; ++iS) { + if (c1.flags & BIT(iS)) { + nuclei::hDCAHists[c1.pt < 0][iS]->Fill(std::abs(c1.pt), c1.dcaXY, c1.dcaZ, c1.nSigmaTPC[iS], c1.tofMasses[iS], c1.itsNCls, c1.tpcNCls); + } + } + } + } + } + PROCESS_SWITCH(DeuteronInTriggeredEvents, processData, "Data analysis", true); + + Preslice tracksPerCollisions = aod::track::collisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + void processMC(soa::Join const& collisions, aod::McCollisions const& mcCollisions, soa::Join const& tracks, aod::McParticles const& particlesMC, aod::BCsWithTimestamps const&) + { + nuclei::candidates.clear(); + + std::vector goodCollisions(mcCollisions.size(), false); + std::vector eventMask(mcCollisions.size(), 0); + + for (const auto& c : mcCollisions) { + + spectra.fill(HIST("hGenVtxZ"), c.posZ()); + + auto mcParticlesPerColl = particlesMC.sliceBy(perMcCollision, c.globalIndex()); + auto& mask = eventMask[c.globalIndex()]; + + if (o2::pwglf::isINELgt0mc(mcParticlesPerColl, pdgDB)) + mask |= nuclei::kGenIsINELgt0; + + if (isMCJetTriggered(mcParticlesPerColl, particlesMC)) + mask |= nuclei::kGenIsJetTriggered; + } + + for (const auto& collision : collisions) { + if (!eventSelectionWithHisto(collision)) { + continue; + } + + // Avoid unwanted memory leaks + if (!collision.has_mcCollision()) + continue; + + int mcId = collision.mcCollisionId(); + if (mcId < 0 || mcId >= static_cast(eventMask.size())) + continue; + + const auto& slicedTracks = tracks.sliceBy(tracksPerCollisions, collision.globalIndex()); + + if (cfgApplyMCEvSel) { + if (!isJetTriggered(slicedTracks)) + continue; + } + + goodCollisions[mcId] = true; + auto& mask = eventMask[mcId]; + mask |= nuclei::kGenHasRecoEv; + + fillDataInfo(collision, slicedTracks); + } + + for (const auto& c : mcCollisions) { + genEventMCSel(eventMask[c.globalIndex()]); + } + + std::vector isReconstructed(particlesMC.size(), false); + for (auto& c : nuclei::candidates) { // o2-linter: disable=const-ref-in-for-loop (candidate is modified in loop) + auto label = tracks.iteratorAt(c.globalIndex); + if (label.mcParticleId() < -1 || label.mcParticleId() >= particlesMC.size()) { + continue; + } + auto particle = particlesMC.iteratorAt(label.mcParticleId()); + bool storeIt{false}; + for (int iS{0}; iS < nuclei::Species; ++iS) { + if (std::abs(particle.pdgCode()) == nuclei::Codes[iS]) { + if (c.fillTree && !storeIt) { + nuclei::hMomRes[iS][particle.pdgCode() < 0]->Fill(1., std::abs(c.pt * nuclei::Charges[iS]), 1. - std::abs(c.pt * nuclei::Charges[iS]) / particle.pt()); + storeIt = cfgTreeConfig->get(iS, 0u) || cfgTreeConfig->get(iS, 1u); /// store only the particles of interest + } + auto coll = collisions.iteratorAt(c.collTrackIndex); + int collMCGlobId = coll.mcCollisionId(); + if (particle.mcCollisionId() == collMCGlobId) { + c.correctPV = true; + } + if (!c.correctPV) { + c.flags |= kIsAmbiguous; + } + if (c.fillDCAHist && cfgDCAHists->get(iS, c.pt < 0)) { + nuclei::hDCAHists[c.pt < 0][iS]->Fill(std::abs(c.pt), c.dcaXY, c.dcaZ, c.nSigmaTPC[iS], c.tofMasses[iS], c.itsNCls, c.tpcNCls, c.correctPV, c.isSecondary, c.fromWeakDecay); + } + } + } + if (!storeIt) { + continue; + } + if (particle.y() < cfgTrackCut.rapidityMin || particle.y() > cfgTrackCut.rapidityMax) { + continue; + } + + int motherPdgCode = 0; + float motherDecRadius = -1; + isReconstructed[particle.globalIndex()] = true; + if (particle.isPhysicalPrimary()) { + c.flags |= kIsPhysicalPrimary; + if (particle.has_mothers()) { + for (const auto& motherparticle : particle.mothers_as()) { + if (std::find(nuclei::hfMothCodes.begin(), nuclei::hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != nuclei::hfMothCodes.end()) { + c.flags |= kIsSecondaryFromWeakDecay; + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + break; + } + } + } + } else if (particle.getProcess() == TMCProcess::kPDecay) { + c.flags |= kIsSecondaryFromWeakDecay; + for (const auto& motherparticle : particle.mothers_as()) { + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + } + } else { + c.flags |= kIsSecondaryFromMaterial; + } + + isReconstructed[particle.globalIndex()] = true; + float absoDecL = computeAbsoDecL(particle); + nucleiTableMCExtension(c.pt, c.eta, c.phi, c.tpcInnerParam, c.beta, c.zVertex, c.nContrib, c.dcaXY, c.dcaZ, c.tpcSignal, c.itsChi2, c.tpcChi2, c.tofChi2, c.flags, c.tpcFindableCls, c.tpcCrossedRows, c.itsClsMap, c.tpcNCls, c.tpcNClsShared, c.clusterSizesITS, goodCollisions[particle.mcCollisionId()], particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), motherPdgCode, motherDecRadius, absoDecL, eventMask[particle.mcCollisionId()]); + } + + int index{0}; + for (const auto& particle : particlesMC) { + int pdg{std::abs(particle.pdgCode())}; + for (int iS{0}; iS < nuclei::Species; ++iS) { + if (pdg != nuclei::Codes[iS]) { + continue; + } + if (particle.y() < cfgTrackCut.rapidityMin || particle.y() > cfgTrackCut.rapidityMax) { + continue; + } + + uint16_t flags = 0; + int motherPdgCode = 0; + float motherDecRadius = -1; + if (particle.isPhysicalPrimary()) { + flags |= kIsPhysicalPrimary; + nuclei::hGenNuclei[iS][particle.pdgCode() < 0]->Fill(1., particle.pt()); + // antinuclei from B hadrons are classified as physical primaries + if (particle.has_mothers()) { + for (const auto& motherparticle : particle.mothers_as()) { + if (std::find(nuclei::hfMothCodes.begin(), nuclei::hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != nuclei::hfMothCodes.end()) { + flags |= kIsSecondaryFromWeakDecay; + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + break; + } + } + } + } else if (particle.getProcess() == TMCProcess::kPDecay) { + if (!particle.has_mothers()) { + continue; // skip secondaries from weak decay without mothers + } + flags |= kIsSecondaryFromWeakDecay; + for (const auto& motherparticle : particle.mothers_as()) { + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + } + } else { + flags |= kIsSecondaryFromMaterial; + } + + if (!isReconstructed[index] && (cfgTreeConfig->get(iS, 0u) || cfgTreeConfig->get(iS, 1u))) { + if ((flags & kIsPhysicalPrimary) == 0 && cfgFillGenSecondaries == 0) { + continue; // skip secondaries if not requested + } + if ((flags & (kIsPhysicalPrimary | kIsSecondaryFromWeakDecay)) == 0 && cfgFillGenSecondaries == 1) { + continue; // skip secondaries from material if not requested + } + float absDecL = computeAbsoDecL(particle); + nucleiTableMCExtension(999., 999., 999., 0., 0., 999., -1, 999., 999., -1, -1, -1, -1, flags, 0, 0, 0, 0, 0, 0, goodCollisions[particle.mcCollisionId()], particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), motherPdgCode, motherDecRadius, absDecL, eventMask[particle.mcCollisionId()]); + } + break; + } + index++; + } + } + PROCESS_SWITCH(DeuteronInTriggeredEvents, processMC, "MC analysis", false); + + void processMCasData(soa::Join const& collisions, aod::McCollisions const& mcCollisions, soa::Join const& tracks, aod::McParticles const& particlesMC, aod::BCsWithTimestamps const&) + { + nuclei::candidates.clear(); + std::vector goodCollisions(mcCollisions.size(), false); + + for (const auto& collision : collisions) { + if (!eventSelectionWithHisto(collision)) { + continue; + } + + // Avoid unwanted memory leaks + if (!collision.has_mcCollision()) + continue; + + int mcId = collision.mcCollisionId(); + if (mcId < 0 || mcId >= static_cast(mcCollisions.size())) + continue; + + goodCollisions[collision.mcCollisionId()] = true; + const auto& slicedTracks = tracks.sliceBy(tracksPerCollisions, collision.globalIndex()); + fillDataInfo(collision, slicedTracks); + } + + std::vector isReconstructed(particlesMC.size(), false); + for (size_t i{0}; i < nuclei::candidates.size(); ++i) { + auto& c = nuclei::candidates[i]; + if (c.fillTree) { + auto label = tracks.iteratorAt(c.globalIndex); + + if (label.mcParticleId() < -1 || label.mcParticleId() >= particlesMC.size()) { + continue; + } + + auto particle = particlesMC.iteratorAt(label.mcParticleId()); + + int motherPdgCode = 0; + float motherDecRadius = -1; + isReconstructed[particle.globalIndex()] = true; + + if (particle.isPhysicalPrimary()) { + c.flags |= kIsPhysicalPrimary; + if (particle.has_mothers()) { + for (const auto& motherparticle : particle.mothers_as()) { + if (std::find(nuclei::hfMothCodes.begin(), nuclei::hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != nuclei::hfMothCodes.end()) { + c.flags |= kIsSecondaryFromWeakDecay; + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + break; + } + } + } + } else if (particle.getProcess() == TMCProcess::kPDecay) { + c.flags |= kIsSecondaryFromWeakDecay; + for (const auto& motherparticle : particle.mothers_as()) { + motherPdgCode = motherparticle.pdgCode(); + motherDecRadius = std::hypot(particle.vx() - motherparticle.vx(), particle.vy() - motherparticle.vy()); + } + } else { + c.flags |= kIsSecondaryFromMaterial; + } + + isReconstructed[particle.globalIndex()] = true; + float absoDecL = computeAbsoDecL(particle); + + nucleiTableMC(c.pt, c.eta, c.phi, c.tpcInnerParam, c.beta, c.zVertex, c.nContrib, c.dcaXY, c.dcaZ, c.tpcSignal, c.itsChi2, c.tpcChi2, c.tofChi2, c.flags, c.tpcFindableCls, c.tpcCrossedRows, c.itsClsMap, c.tpcNCls, c.tpcNClsShared, c.clusterSizesITS, goodCollisions[particle.mcCollisionId()], particle.pt(), particle.eta(), particle.phi(), particle.pdgCode(), motherPdgCode, motherDecRadius, absoDecL); + } + } + } + + PROCESS_SWITCH(DeuteronInTriggeredEvents, processMCasData, "MC as data analysis", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Nuspex/ebyeMaker.cxx b/PWGLF/TableProducer/Nuspex/ebyeMaker.cxx index 9bfaca1bef1..61a6eb70cad 100644 --- a/PWGLF/TableProducer/Nuspex/ebyeMaker.cxx +++ b/PWGLF/TableProducer/Nuspex/ebyeMaker.cxx @@ -214,6 +214,8 @@ struct EbyeMaker { Produces mcNucleiEbyeTable; Produces mcLambdaEbyeTable; Produces mcMiniTrkTable; + Produces miniChTable; + Produces mcMiniChTable; std::mt19937 gen32; std::vector candidateV0s; std::array, 2> candidateTracks; @@ -221,12 +223,12 @@ struct EbyeMaker { o2::vertexing::DCAFitterN<2> fitter; std::vector classIds; - int mRunNumber; - float dBz; - uint8_t nTrackletsColl; - uint8_t nTracksColl; - uint8_t nChPartGen; - int nTracksCollFull; + int mRunNumber{-999}; + float dBz{-999.f}; + uint8_t nTrackletsColl{0u}; + uint8_t nTracksColl{0u}; + uint8_t nChPartGen{0u}; + int nTracksCollFull{-999}; Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Type of material correction"}; Configurable> cfgBetheBlochParams{"cfgBetheBlochParams", {kBetheBlochDefault[0], 2, 6, particleNamesPar, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for deuteron"}; @@ -259,6 +261,7 @@ struct EbyeMaker { Configurable kINT7Intervals{"kINT7Intervals", false, "toggle kINT7 trigger selection in the 10-30% and 50-90% centrality intervals (2018 Pb-Pb)"}; Configurable kUsePileUpCut{"kUsePileUpCut", false, "toggle strong correlation cuts (Run 2)"}; Configurable kUseEstimatorsCorrelationCut{"kUseEstimatorsCorrelationCut", false, "toggle cut on the correlation between centrality estimators (2018 Pb-Pb)"}; + Configurable kUsePID{"kUsePID", true, "use PID information in the analysis"}; Configurable kCentCutMax{"kCentCutMax", 100, "maximum accepted centrality"}; @@ -305,11 +308,11 @@ struct EbyeMaker { Configurable> cfgTrackSels{"cfgTrackSels", {kTrackSels, 1, 12, particleName, trackSelsNames}, "Track selections"}; Configurable> cfgDcaSelsParam{"cfgDcaSelsParam", {kDcaSelsParam[0], 3, 3, dcaSelsNames, dcaParNames}, "DCA threshold settings"}; - std::array ptMin; - std::array ptTof; - std::array ptMax; - std::array nSigmaTpcCutLow; - std::array nSigmaTpcCutUp; + std::array ptMin{0}; + std::array ptTof{0}; + std::array ptMax{0}; + std::array nSigmaTpcCutLow{0}; + std::array nSigmaTpcCutUp{0}; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -497,6 +500,14 @@ struct EbyeMaker { candidateTrack.pdgcode > 0 ? candidateTrack.genpt : -candidateTrack.genpt, static_cast(candidateTrack.geneta * 100), candidateTrack.isreco); + mcMiniChTable( + miniCollTable.lastIndex(), + candidateTrack.pt, + static_cast(candidateTrack.eta * 100), + selMask, + candidateTrack.pdgcode > 0 ? candidateTrack.genpt : -candidateTrack.genpt, + static_cast(candidateTrack.geneta * 100), + candidateTrack.isreco); } else if (!isMc) { miniTrkTable( miniCollTable.lastIndex(), @@ -504,6 +515,11 @@ struct EbyeMaker { static_cast(candidateTrack.eta * 100), selMask, candidateTrack.outerPID); + miniChTable( + miniCollTable.lastIndex(), + candidateTrack.pt, + static_cast(candidateTrack.eta * 100), + selMask); } } @@ -662,7 +678,7 @@ struct EbyeMaker { continue; } - if (trackPt <= ptTof[iP] || (trackPt > ptTof[iP] && hasTof)) { + if (trackPt <= ptTof[iP] || hasTof) { CandidateTrack candTrack; candTrack.pt = track.sign() > 0. ? trackPt : -trackPt; candTrack.eta = trackEta; @@ -682,7 +698,6 @@ struct EbyeMaker { } if (lambdaPtMax > lambdaPtMin) { - std::vector trkId; for (const auto& v0 : V0s) { auto posTrack = v0.posTrack_as(); auto negTrack = v0.negTrack_as(); @@ -838,7 +853,8 @@ struct EbyeMaker { } if (mcLab.has_mcParticle()) { auto mcTrack = mcLab.template mcParticle_as(); - if (std::abs(mcTrack.pdgCode()) != kPartPdg[iP]) + auto pdgCode = mcTrack.pdgCode(); + if ((std::abs(pdgCode) != kPartPdg[iP] && kUsePID) || ((std::abs(pdgCode) == PDG_t::kPiPlus || std::abs(pdgCode) == PDG_t::kElectron || std::abs(pdgCode) == PDG_t::kMuonMinus || std::abs(pdgCode) == PDG_t::kKPlus || std::abs(pdgCode) == PDG_t::kProton) && !kUsePID)) continue; if ((((mcTrack.flags() & 0x8) || (mcTrack.flags() & 0x2)) && (doprocessMcRun2 || doprocessMiniMcRun2)) || ((mcTrack.flags() & 0x1) && !doprocessMiniMcRun2)) continue; @@ -847,7 +863,7 @@ struct EbyeMaker { continue; if (mcTrack.isPhysicalPrimary()) candidateTrack.pdgcodemoth = PartTypes::kPhysPrim; - else if (mcTrack.has_mothers() && iP == 0) + else if (mcTrack.has_mothers() && iP == 0 && kUsePID) candidateTrack.pdgcodemoth = getPartTypeMother(mcTrack); auto genPt = std::hypot(mcTrack.px(), mcTrack.py()); @@ -906,8 +922,9 @@ struct EbyeMaker { continue; auto pdgCode = mcPart.pdgCode(); auto genPt = std::hypot(mcPart.px(), mcPart.py()); + int ch = 0; if ((std::abs(pdgCode) == PDG_t::kPiPlus || std::abs(pdgCode) == PDG_t::kElectron || std::abs(pdgCode) == PDG_t::kMuonMinus || std::abs(pdgCode) == PDG_t::kKPlus || std::abs(pdgCode) == PDG_t::kProton) && mcPart.isPhysicalPrimary() && genPt > ptMin[0] && genPt < ptMax[0]) { - int ch = (pdgCode == PDG_t::kPiPlus || pdgCode == -PDG_t::kElectron || pdgCode == -PDG_t::kMuonMinus || pdgCode == PDG_t::kKPlus || pdgCode == PDG_t::kProton) ? 1 : -1; + ch = (pdgCode == PDG_t::kPiPlus || pdgCode == -PDG_t::kElectron || pdgCode == -PDG_t::kMuonMinus || pdgCode == PDG_t::kKPlus || pdgCode == PDG_t::kProton) ? 1 : -1; if ((ch < 0 && countOnlyLSTrk == TracksCharge::kNegative) || (ch > 0 && countOnlyLSTrk == TracksCharge::kPositive) || (countOnlyLSTrk == TracksCharge::kAll)) nChPartGen++; } @@ -928,31 +945,30 @@ struct EbyeMaker { candV0.genpt = genPt; candV0.geneta = mcPart.eta(); candV0.pdgcode = pdgCode; - auto it = find_if(candidateV0s.begin(), candidateV0s.end(), [&](CandidateV0 v0) { return v0.mcIndex == mcPart.globalIndex(); }); + auto it = find_if(candidateV0s.begin(), candidateV0s.end(), [&](const CandidateV0& v0) { return v0.mcIndex == mcPart.globalIndex(); }); if (it != candidateV0s.end()) { continue; } else { LOGF(debug, "not found!"); candidateV0s.emplace_back(candV0); } - } else if (std::abs(pdgCode) == kPartPdg[0] || std::abs(pdgCode) == kPartPdg[1]) { + } else if (((std::abs(pdgCode) == kPartPdg[0] || std::abs(pdgCode) == kPartPdg[1]) && kUsePID) || (std::abs(ch) == 1 && !kUsePID)) { int iP = 1; - if (std::abs(pdgCode) == kPartPdg[0]) { + if ((std::abs(pdgCode) == kPartPdg[0] && kUsePID) || !kUsePID) { iP = 0; } if ((!mcPart.isPhysicalPrimary() && !doprocessMiniMcRun2)) continue; - auto genPt = std::hypot(mcPart.px(), mcPart.py()); CandidateTrack candTrack; candTrack.genpt = genPt; candTrack.geneta = mcPart.eta(); candTrack.pdgcode = pdgCode; if (mcPart.isPhysicalPrimary()) candTrack.pdgcodemoth = PartTypes::kPhysPrim; - else if (mcPart.has_mothers() && iP == 0) + else if (mcPart.has_mothers() && iP == 0 && kUsePID) candTrack.pdgcodemoth = getPartTypeMother(mcPart); - auto it = find_if(candidateTracks[iP].begin(), candidateTracks[iP].end(), [&](CandidateTrack trk) { return trk.mcIndex == mcPart.globalIndex(); }); + auto it = find_if(candidateTracks[iP].begin(), candidateTracks[iP].end(), [&](const CandidateTrack& trk) { return trk.mcIndex == mcPart.globalIndex(); }); if (it != candidateTracks[iP].end()) { continue; } else { @@ -1010,7 +1026,7 @@ struct EbyeMaker { float centrality = collision.centRun2V0M(); const float centTriggerEdges[]{10.f, 30.f, 50.f}; - if (!(collision.sel7() && collision.alias_bit(kINT7)) && (!kINT7Intervals || (kINT7Intervals && ((centrality >= centTriggerEdges[0] && centrality < centTriggerEdges[1]) || centrality > centTriggerEdges[2])))) + if (!(collision.sel7() && collision.alias_bit(kINT7)) && (!kINT7Intervals || ((centrality >= centTriggerEdges[0] && centrality < centTriggerEdges[1]) || centrality > centTriggerEdges[2]))) continue; float centralityCl0 = collision.centRun2CL0(); diff --git a/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx b/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx index db8eced7bda..c90bc73c6cb 100644 --- a/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx +++ b/PWGLF/TableProducer/Nuspex/he3HadronFemto.cxx @@ -57,6 +57,8 @@ #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include #include #include #include @@ -69,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -85,11 +88,38 @@ using CollisionsFull = soa::Join; using TrackCandidates = soa::Join; using TrackCandidatesMC = soa::Join; +using McCollisionMults = soa::Join; +using EventCandidatesMC = soa::Join; namespace { -constexpr double betheBlochDefault[1][6]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; -static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; + +std::shared_ptr hEvtMC; +std::shared_ptr hImpactParamGen; +std::shared_ptr hImpactParamReco; +std::shared_ptr hRecoCentrality; +std::shared_ptr hImpactParamGenOneReco; +std::shared_ptr hGenOneRecoCentrality; +std::shared_ptr hGenEventsNchEta05; +std::shared_ptr hGenEventsNchEta08; +std::shared_ptr hRecoCentralityColvsMultiplicityRecoEta05; +std::shared_ptr hRecoCentralityColvsMultiplicityRecoEta08; +std::shared_ptr hRecoCentralityColvsMultiplicityFT0C; +std::shared_ptr hRecoCentralityColvsImpactParamReco; +std::shared_ptr hGenCentralityColvsMultiplicityGenEta05; +std::shared_ptr hGenCentralityColvsMultiplicityGenEta08; +std::shared_ptr hGenCentralityColvsMultiplicityFT0C; +std::shared_ptr hGenCentralityColvsImpactParamGen; +std::shared_ptr hGenLi4BeforeEvtSel; +std::shared_ptr hGenLi4vsImpactParameterBeforeEvtSel; +std::shared_ptr hGenLi4vsMultiplicityGenEta05BeforeEvtSel; +std::shared_ptr hGenLi4vsMultiplicityGenEta08BeforeEvtSel; +std::shared_ptr hGenLi4vsMultiplicityFT0CBeforeEvtSel; +std::shared_ptr hGenLi4AfterSel; +std::shared_ptr hGenLi4vsImpactParameterAfterSel; +std::shared_ptr hGenLi4vsMultiplicityGenEta05AfterSel; +std::shared_ptr hGenLi4vsMultiplicityGenEta08AfterSel; +std::shared_ptr hGenLi4vsMultiplicityFT0CAfterSel; constexpr int Li4PDG = o2::constants::physics::Pdg::kLithium4; constexpr int H3LPDG = o2::constants::physics::Pdg::kHyperTriton; @@ -145,30 +175,68 @@ enum ParticleFlags { kFromLi4 = BIT(1), // from Li4 decay kFromHypertriton = BIT(2), // from hypertriton decay kFromMaterial = BIT(3), // from material - kFromOtherDecays = BIT(4), // from other decays + kFromOtherDecays = BIT(4), // from other (weak) decays }; -std::array kDCAxyResolutionParams[static_cast(Species::kAllSpecies)] = { - {0.0118, 0.6889, 0.0017}, // He3 - {0.0032, 0.5206, 0.0012} // Pr -}; -std::array kDCAzResolutionParams[static_cast(Species::kAllSpecies)] = { - {0.1014, 1.7512, 0.0024}, // He3 - {0.0021, 1.1122, 0.0021} // Pr -}; +constexpr std::array, static_cast(Species::kAllSpecies)> kItsParamsDefault = {{ + {-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}, // He3 + {-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32} // hadron +}}; +const std::vector kItsParNames{"p0", "p1", "p2", "res0", "res1", "res2"}; + +constexpr std::array, 1> kBetheBlochDefault = {{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}}; +const std::vector kBetheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; + +constexpr std::array, 1> kBetheBlochCorrectionDefault = {{{0.0, -1.e32, -1.e32, 0.0, -1.e32, -1.e32}}}; +const std::vector kBetheBlochCorrectionParNames{"p0", "p1", "p2", "p3", "p4", "p5"}; + +constexpr std::array, static_cast(Species::kAllSpecies)> kDCAxyResDefault = {{ + {1.09e-4, 0.0011, 0.0065, 1.0399}, // He3 + {8.19e-5, 0.004, 0.0026, 1.1741} // Pr +}}; +constexpr std::array, static_cast(Species::kAllSpecies)> kDCAzResDefault = {{ + {9.36e-5, 0.0019, 0.0080, 1.416}, // He3 + {1.18e-4, 0.0020, 0.0025, 1.3460} // Pr +}}; +const std::vector kDCAResParNames{"res0", "res1", "res2", "mean"}; -std::array kHePidTrkParams = {0.1593, -0.0445}; +constexpr std::array kHePidTrkPtParamsHeDefault = {0.3101, -0.1759, 0.0262}; +constexpr std::array kHePidTrkPParamsHeDefault = {0., 0., 0.}; } // namespace +struct CollisionInfo { + int64_t collisionID = -1; + uint32_t selectionFlags = 0; + float posZ = -999.f; + int numContributors = -1; + float centFT0C = -999.f; + int multFT0C = -1; + float occupancy = -999.f; + + template + void fillFromCollision(const Collision& collision, uint32_t collisionSelectionFlag) + { + collisionID = collision.globalIndex(); + selectionFlags = collisionSelectionFlag; + posZ = collision.posZ(); + numContributors = collision.numContrib(); + centFT0C = collision.centFT0C(); + multFT0C = collision.multFT0C(); + occupancy = collision.trackOccupancyInTimeRange(); + } +}; + struct He3HadCandidate { - float recoPtHe3() const { return signHe3 * std::hypot(momHe3[0], momHe3[1]); } - float recoPhiHe3() const { return std::atan2(momHe3[1], momHe3[0]); } - float recoEtaHe3() const { return std::asinh(momHe3[2] / std::abs(recoPtHe3())); } - float recoPtHad() const { return signHad * std::hypot(momHad[0], momHad[1]); } - float recoPhiHad() const { return std::atan2(momHad[1], momHad[0]); } - float recoEtaHad() const { return std::asinh(momHad[2] / std::abs(recoPtHad())); } + [[nodiscard]] float recoPtHe3() const { return signHe3 * std::hypot(momHe3[0], momHe3[1]); } + [[nodiscard]] float recoPhiHe3() const { return std::atan2(momHe3[1], momHe3[0]); } + [[nodiscard]] float recoEtaHe3() const { return std::asinh(momHe3[2] / std::abs(recoPtHe3())); } + [[nodiscard]] float recoPtHad() const { return signHad * std::hypot(momHad[0], momHad[1]); } + [[nodiscard]] float recoPhiHad() const { return std::atan2(momHad[1], momHad[0]); } + [[nodiscard]] float recoEtaHad() const { return std::asinh(momHad[2] / std::abs(recoPtHad())); } + [[nodiscard]] float recoKstar() const { return getkstar(recoPtHe3(), recoEtaHe3(), recoPhiHe3(), constants::physics::MassHelium3, 1.f, + recoPtHad(), recoEtaHad(), recoPhiHad(), constants::physics::MassProton, 1.f); } std::array momHe3 = {99.f, 99.f, 99.f}; std::array momHad = {99.f, 99.f, 99.f}; @@ -180,7 +248,6 @@ struct He3HadCandidate { float dcazHe3 = -10.f; float dcaxyHad = -10.f; float dcazHad = -10.f; - float dcaPair = -10.f; // DCA between the two tracks uint16_t tpcSignalHe3 = 0u; uint16_t tpcSignalHad = 0u; @@ -192,9 +259,11 @@ struct He3HadCandidate { float chi2TPCHe3 = -10.f; float chi2TPCHad = -10.f; float nSigmaHe3 = -10.f; + float nSigmaTPCHad = -10.f; float nSigmaTPCHadPi = -10.f; float nSigmaTPCHadKa = -10.f; float nSigmaTPCHadPr = -10.f; + float nSigmaTOFHad = -10.f; float nSigmaTOFHadPi = -10.f; float nSigmaTOFHadKa = -10.f; float nSigmaTOFHadPr = -10.f; @@ -239,33 +308,38 @@ struct he3HadronFemto { Produces outputMcTable; Produces outputMultiplicityTable; Produces outputQaTable; + Produces outputPidTable; // Selections Configurable settingHadPDGCode{"settingHadPDGCode", 211, "Hadron - PDG code"}; - Configurable settingCutVertex{"settingCutVertex", 10.0f, "Accepted z-vertex range"}; - Configurable settingCutRigidityMinHe3{"settingCutRigidityMinHe3", 0.8f, "Minimum rigidity for He3"}; - Configurable settingCutEta{"settingCutEta", 0.9f, "Eta cut on daughter track"}; - Configurable settingCutDCAxy{"settingCutDCAxy", 2.0f, "DCAxy range for tracks"}; - Configurable settingCutDCAz{"settingCutDCAz", 2.0f, "DCAz range for tracks"}; - Configurable settingCutNClsTPC{"settingCutNClsTPC", 90, "number of TPC clusters for a generic track"}; - Configurable settingCutNClsTPCHe3{"settingCutNClsTPCHe3", 110.0f, "number of TPC clusters for a He3 track"}; - Configurable settingCutChi2tpcLow{"settingCutChi2tpcLow", 0.f, "Low cut on TPC chi2"}; - Configurable settingCutChi2tpcLowHe3{"settingCutChi2tpcLowHe3", 0.5f, "Low cut on TPC chi2 for He3"}; - Configurable settingCutInvMass{"settingCutInvMass", 0.0f, "Invariant mass upper limit"}; - Configurable settingCutPtMinhe3Had{"settingCutPtMinhe3Had", 0.0f, "Minimum PT cut on he3Had4"}; - Configurable settingCutClSizeItsHe3{"settingCutClSizeItsHe3", 4.0f, "Minimum ITS cluster size for He3"}; - Configurable settingCutNCls{"settingCutNCls", 5.0f, "Minimum ITS Ncluster for tracks"}; - Configurable settingCutChi2NClITS{"settingCutChi2NClITS", 36.f, "Maximum ITS Chi2 for tracks"}; - Configurable settingCutNsigmaDcaXy{"settingCutNsigmaDcaXy", 3.0f, "Value of the DCA xy Nsigma cut"}; - Configurable settingCutNsigmaDcaZ{"settingCutNsigmaDcaZ", 3.0f, "Value of the DCA z Nsigma cut"}; - Configurable settingCutNsigmaTPC{"settingCutNsigmaTPC", 3.0f, "Value of the TPC Nsigma cut"}; - Configurable settingCutNsigmaITSHad{"settingCutNsigmaITSHad", -2.f, "Value of the ITS Nsigma cutfor Had"}; - Configurable settingCutNsigmaITSHe3{"settingCutNsigmaITSHe3", -1.5f, "Value of the ITS Nsigma cutfor He3"}; - Configurable settingCutPtMinTOFHad{"settingCutPtMinTOFHad", 0.4f, "Minimum pT to apply the TOF cut on hadrons"}; - Configurable settingCutNsigmaTOF{"settingCutNsigmaTOF", 3.0f, "Value of the TOF Nsigma cut"}; - - Configurable> settingEventSelections{"settingEventSelections", {nuclei::EvSelDefault[0], 8, 1, nuclei::eventSelectionLabels, nuclei::eventSelectionTitle}, "Event selections"}; + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"cutSettings"}; + Configurable settingCutVertex{"settingCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable settingCutRigidityMinHe3{"settingCutRigidityMinHe3", 0.8f, "Minimum rigidity for He3"}; + Configurable settingCutEta{"settingCutEta", 0.9f, "Eta cut on daughter track"}; + Configurable settingCutDCAxy{"settingCutDCAxy", 2.0f, "DCAxy range for tracks"}; + Configurable settingCutDCAz{"settingCutDCAz", 2.0f, "DCAz range for tracks"}; + Configurable settingCutNClsTPC{"settingCutNClsTPC", 90, "number of TPC clusters for a generic track"}; + Configurable settingCutNClsTPCHe3{"settingCutNClsTPCHe3", 110.0f, "number of TPC clusters for a He3 track"}; + Configurable settingCutChi2tpcLow{"settingCutChi2tpcLow", 0.f, "Low cut on TPC chi2"}; + Configurable settingCutChi2tpcLowHe3{"settingCutChi2tpcLowHe3", 0.5f, "Low cut on TPC chi2 for He3"}; + Configurable settingCutInvMass{"settingCutInvMass", 0.0f, "Invariant mass upper limit"}; + Configurable settingCutPtMinhe3Had{"settingCutPtMinhe3Had", 0.0f, "Minimum PT cut on he3Had4"}; + Configurable settingCutClSizeItsHe3{"settingCutClSizeItsHe3", 4.0f, "Minimum ITS cluster size for He3"}; + Configurable settingCutNCls{"settingCutNCls", 5.0f, "Minimum ITS Ncluster for tracks"}; + Configurable settingCutChi2NClITS{"settingCutChi2NClITS", 36.f, "Maximum ITS Chi2 for tracks"}; + Configurable settingCutNsigmaDcaXy{"settingCutNsigmaDcaXy", 3.0f, "Value of the DCA xy Nsigma cut"}; + Configurable settingCutNsigmaDcaZ{"settingCutNsigmaDcaZ", 3.0f, "Value of the DCA z Nsigma cut"}; + Configurable settingCutNsigmaTPC{"settingCutNsigmaTPC", 3.0f, "Value of the TPC Nsigma cut"}; + Configurable settingCutNsigmaITSHad{"settingCutNsigmaITSHad", -2.f, "Value of the ITS Nsigma cutfor Had"}; + Configurable settingCutNsigmaITSHe3{"settingCutNsigmaITSHe3", -1.5f, "Value of the ITS Nsigma cutfor He3"}; + Configurable settingCutPtMinTOFHad{"settingCutPtMinTOFHad", 0.4f, "Minimum pT to apply the TOF cut on hadrons"}; + Configurable settingCutNsigmaTOF{"settingCutNsigmaTOF", 3.0f, "Value of the TOF Nsigma cut"}; + } cutSettings; + + Configurable> settingEventSelections{"settingEventSelections", {nuclei::EvSelDefault[0], nuclei::evSel::kNevSels, 1, nuclei::eventSelectionLabels, nuclei::eventSelectionTitle}, "Event selections"}; Configurable settingNoMixedEvents{"settingNoMixedEvents", 5, "Number of mixed events per event"}; Configurable settingEnableBkgUS{"settingEnableBkgUS", false, "Enable US background"}; Configurable settingEnableDCAfitter{"settingEnableDCAfitter", false, "Enable DCA fitter"}; @@ -274,7 +348,9 @@ struct he3HadronFemto { Configurable settingFillMultiplicity{"settingFillMultiplicity", false, "Fill multiplicity table"}; Configurable settingFillQa{"settingFillQa", false, "Fill QA table"}; - Configurable settingFillPrimariesAndMixedMc{"settingFillPrimariesAndMixedMc", false, "Fill primary MC tracks and mixed tracks (e.g. a primary track and one from Li4)"}; + Configurable settingFillPrimariesMc{"settingFillPrimariesMc", false, "Fill primary MC tracks"}; + Configurable settingFillLi4DaughtersMc{"settingFillLi4DaughtersMc", true, "Fill tracks from Li4 decays in MC"}; + Configurable settingFillMixedMc{"settingFillMixedMc", false, "Fill mixed MC tracks (e.g. a primary track and one from Li4)"}; // Zorro Configurable settingSkimmedProcessing{"settingSkimmedProcessing", false, "Skimmed dataset processing"}; @@ -292,7 +368,14 @@ struct he3HadronFemto { Configurable settingGeoPath{"settingGeoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; Configurable settingPidPath{"settingPidPath", "", "Path to the PID response object"}; - Configurable> settingBetheBlochParams{"settingBetheBlochParams", {betheBlochDefault[0], 1, 6, {"He3"}, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; + Configurable> settingBetheBlochParams{"settingBetheBlochParams", {kBetheBlochDefault[0].data(), 1, 6, {"He3"}, kBetheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; + Configurable> settingBetheBlochCorrectionParams{"settingBetheBlochCorrectionParams", {kBetheBlochCorrectionDefault[0].data(), 1, 6, {"He3"}, kBetheBlochCorrectionParNames}, "TPC Bethe-Bloch correction parameterisation for He3"}; + Configurable> settingItsParams{"settingItsParams", {kItsParamsDefault[0].data(), 2, 6, {"He3", "Had"}, kItsParNames}, "ITS parameterisation"}; + Configurable> settingDCAxyResParams{"settingDCAxyResParams", {kDCAxyResDefault[0].data(), 2, 4, {"He3", "Had"}, kDCAResParNames}, "DCAxy resolution parameterisation"}; + Configurable> settingDCAzResParams{"settingDCAzResParams", {kDCAzResDefault[0].data(), 2, 4, {"He3", "Had"}, kDCAResParNames}, "DCAz resolution parameterisation"}; + Configurable> settingHePidTrkPtParams{"settingHePidTrkPtParams", {kHePidTrkPtParamsHeDefault.data(), 1, 3, {"He3"}, {"p0", "p1", "p2"}}, "PID in tracking pT dependence for He3"}; + Configurable> settingHePidTrkPParams{"settingHePidTrkPParams", {kHePidTrkPParamsHeDefault.data(), 1, 3, {"He3"}, {"p0", "p1", "p2"}}, "PID in tracking p dependence for He3"}; + Configurable settingCompensatePIDinTracking{"settingCompensatePIDinTracking", false, "If true, divide tpcInnerParam by the electric charge"}; Configurable settingMaterialCorrection{"settingMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Material correction type"}; @@ -303,20 +386,30 @@ struct he3HadronFemto { ConfigurableAxis axisVertex{"axisVertex", {30, -10, 10}, "Binning for multiplicity"}; ConfigurableAxis axisCentrality{"axisCentrality", {40, 0, 100}, "Binning for centrality"}; using BinningType = ColumnBinningPolicy; - BinningType binningPolicy{{axisVertex, axisCentrality}, true}; + BinningType binningPolicy{{axisVertex, axisCentrality}}; SliceCache cache; SameKindPair mPair{binningPolicy, settingNoMixedEvents, -1, &cache}; - std::array mBBparamsHe; + struct He3HadronParams { + std::array betheBlochParams{}; + std::array betheBlochCorrectionParams{}; + std::array, static_cast(Species::kAllSpecies)> itsParams{}; + std::array, static_cast(Species::kAllSpecies)> dcaxyResParams{}; + std::array, static_cast(Species::kAllSpecies)> dcazResParams{}; + std::array hePidTrkPtParams{}; + std::array hePidTrkPParams{}; + } mHe3HadronParams; + o2::aod::ITSResponse mResponseITS; - std::vector mRecoCollisionIDs; std::vector mGoodCollisions; + std::vector mMcCollisionIdToRecoCollisionId; + std::vector mCollisionSelectionFlags; std::vector mTrackPairs; o2::vertexing::DCAFitterN<2> mFitter; svPoolCreator mSvPoolCreator{He3PDG, ProtonPDG}; - int mRunNumber; - float mDbz; + int mRunNumber = 0; + float mDbz = 0.f; Service mCcdb; Zorro mZorro; OutputObj mZorroSummary{"zorroSummary"}; @@ -330,36 +423,12 @@ struct he3HadronFemto { {"hVtxZ", "Vertex distribution in Z;Z (cm)", {HistType::kTH1F, {{400, -20.0, 20.0}}}}, {"hCentralityFT0A", ";Centrality FT0A (%)", {HistType::kTH1F, {{100, 0, 100.0}}}}, {"hCentralityFT0C", ";Centrality FT0C (%)", {HistType::kTH1F, {{100, 0, 100.0}}}}, - {"hNcontributor", "Number of primary vertex contributor", {HistType::kTH1F, {{2000, 0.0f, 2000.0f}}}}, + {"hNcontributor", "Number of primary vertex contributor", {HistType::kTH1F, {{200, 0.0f, 2000.0f}}}}, {"hTrackSel", "Accepted tracks", {HistType::kTH1F, {{Selections::kAll, -0.5, static_cast(Selections::kAll) - 0.5}}}}, {"hEmptyPool", "svPoolCreator did not find track pairs false/true", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, - {"hhe3HadtInvMass", "; M(^{3}He + p) (GeV/#it{c}^{2})", {HistType::kTH1F, {{300, 3.74f, 4.34f}}}}, + {"hhe3HadInvMass", "; M(^{3}He + p) (GeV/#it{c}^{2})", {HistType::kTH1F, {{300, 3.74f, 4.34f}}}}, + {"hhe3HadKstar", "; #it{k}* (GeV/#it{c})", {HistType::kTH1F, {{300, 0.f, 0.8f}}}}, {"hKstarRecVsKstarGen", "; #it{k}*_{gen} (GeV/#it{c}); #it{k}*_{rec} (GeV/#it{c})", {HistType::kTH2F, {{400, 0.f, 0.8f}, {400, 0.f, 0.8f}}}}, - - {"He3/hDCAxyHe3", "^{3}He;DCA_{xy} (cm)", {HistType::kTH1F, {{200, -0.5f, 0.5f}}}}, - {"He3/hDCAzHe3", "^{3}He;DCA_{z} (cm)", {HistType::kTH1F, {{200, -1.0f, 1.0f}}}}, - {"He3/hNClsHe3ITS", "^{3}He;N_{ITS} Cluster", {HistType::kTH1F, {{20, -10.0f, 10.0f}}}}, - {"He3/hChi2NClHe3ITS", "^{3}He;Chi2_{ITS} Ncluster", {HistType::kTH1F, {{100, 0, 100.0f}}}}, - {"He3/hHe3Pt", "^{3}He; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{240, -6.0f, 6.0f}}}}, - {"He3/h2dEdxHe3candidates", "dEdx distribution; #it{p} (GeV/#it{c}); dE/dx (a.u.)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {100, 0.0f, 2000.0f}}}}, - {"He3/h2NsigmaHe3ITS", "NsigmaHe3 ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} ^{3}He", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {120, -3.0f, 3.0f}}}}, - {"He3/h2NsigmaHe3ITS_preselection", "NsigmaHe3 ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS} ^{3}He", {HistType::kTH2F, {{50, -5.0f, 5.0f}, {120, -3.0f, 3.0f}}}}, - {"He3/h2NsigmaHe3TPC", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(^{3}He)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {200, -5.0f, 5.0f}}}}, - {"He3/h2NsigmaHe3TPC_preselection", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(^{3}He)", {HistType::kTH2F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - - {"Had/hNClsHadITS", "had;N_{ITS} Cluster", {HistType::kTH1F, {{20, -10.0f, 10.0f}}}}, - {"Had/hChi2NClHadITS", "had;Chi2_{ITS} Ncluster", {HistType::kTH1F, {{100, 0, 100.0f}}}}, - {"Had/hHadronPt", "had; #it{p}_{T} (GeV/#it{c})", {HistType::kTH1F, {{120, -3.0f, 3.0f}}}}, - {"Had/h2NsigmaHadronITS", "NsigmaHadron ITS distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{ITS}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {200, -5.0f, 5.0f}}}}, - {"Had/h2NsigmaHadronITS_preselection", "NsigmaHadron ITS distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"Had/h2NsigmaHadronTPC", "NsigmaHadron TPC distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {200, -5.0f, 5.0f}}}}, - {"Had/h2NsigmaHadronTPC_preselection", "NsigmaHadron TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"Had/h2NsigmaHadronTPC_mcBackground", "NsigmaHadron TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"Had/h2NsigmaHadronTPC_mcSignal", "NsigmaHadron TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"Had/h2NsigmaHadronTOF", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {200, -5.0f, 5.0f}}}}, - {"Had/h2NsigmaHadronTOF_preselection", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"Had/h2NsigmaHadronTOF_mcBackground", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, - {"Had/h2NsigmaHadronTOF_mcSignal", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had)", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, }, OutputObjHandlingPolicy::AnalysisObject, false, @@ -391,15 +460,47 @@ struct he3HadronFemto { } mQaRegistry.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(1, "All"); - for (int iSel = 1; iSel < nuclei::evSel::kNevSels + 1; iSel++) { - mQaRegistry.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(iSel + 1, nuclei::eventSelectionLabels[iSel].c_str()); + for (int iSel = 0; iSel < nuclei::evSel::kNevSels + 2; iSel++) { + mQaRegistry.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(iSel + 2, nuclei::eventSelectionLabels[iSel].c_str()); } const int nBetheBlochParameters = 5; for (int i = 0; i < nBetheBlochParameters; i++) { - mBBparamsHe[i] = settingBetheBlochParams->get("He3", Form("p%i", i)); + mHe3HadronParams.betheBlochParams[i] = settingBetheBlochParams->get("He3", Form("p%i", i)); + } + mHe3HadronParams.betheBlochParams[5] = settingBetheBlochParams->get("He3", "resolution"); + + const int nBetheBlochCorrectionParameters = 6; + for (int i = 0; i < nBetheBlochCorrectionParameters; i++) { + mHe3HadronParams.betheBlochCorrectionParams[i] = settingBetheBlochCorrectionParams->get("He3", Form("p%i", i)); + } + + const int nItsParameters = 6; + const int nDCAResParameters = 4; + for (int iSpecies = 0; iSpecies < static_cast(Species::kAllSpecies); iSpecies++) { + + for (int i = 0; i < nItsParameters; i++) { + mHe3HadronParams.itsParams[iSpecies][i] = i < 3 ? settingItsParams->get(Species(iSpecies), Form("p%i", i)) : settingItsParams->get(Species(iSpecies), Form("res%i", i - 3)); + } + + for (int i = 0; i < nDCAResParameters; i++) { + mHe3HadronParams.dcaxyResParams[iSpecies][i] = settingDCAxyResParams->get(Species(iSpecies), i < 3 ? Form("res%i", i) : "mean"); + mHe3HadronParams.dcazResParams[iSpecies][i] = settingDCAzResParams->get(Species(iSpecies), i < 3 ? Form("res%i", i) : "mean"); + } + } + + if (mHe3HadronParams.itsParams[Species::kHe3][0] > -1e31) { + mResponseITS.setParameters(mHe3HadronParams.itsParams[Species::kHad][0], mHe3HadronParams.itsParams[Species::kHad][1], mHe3HadronParams.itsParams[Species::kHad][2], + mHe3HadronParams.itsParams[Species::kHe3][0], mHe3HadronParams.itsParams[Species::kHe3][1], mHe3HadronParams.itsParams[Species::kHe3][2], + mHe3HadronParams.itsParams[Species::kHad][3], mHe3HadronParams.itsParams[Species::kHad][4], mHe3HadronParams.itsParams[Species::kHad][5], + mHe3HadronParams.itsParams[Species::kHe3][3], mHe3HadronParams.itsParams[Species::kHe3][4], mHe3HadronParams.itsParams[Species::kHe3][5]); + } + + const int nHePidTrkParams = 3; + for (int i = 0; i < nHePidTrkParams; i++) { + mHe3HadronParams.hePidTrkPtParams[i] = settingHePidTrkPtParams->get("He3", Form("p%i", i)); + mHe3HadronParams.hePidTrkPParams[i] = settingHePidTrkPParams->get("He3", Form("p%i", i)); } - mBBparamsHe[5] = settingBetheBlochParams->get("He3", "resolution"); std::vector selectionLabels = {"All", "Track selection", "PID"}; for (int i = 0; i < Selections::kAll; i++) { @@ -413,6 +514,79 @@ struct he3HadronFemto { mQaRegistry.get(HIST("hEmptyPool"))->GetXaxis()->SetBinLabel(1, "False"); mQaRegistry.get(HIST("hEmptyPool"))->GetXaxis()->SetBinLabel(2, "True"); + + if (doprocessSameEvent || doprocessMixedEvent || doprocessMC || doprocessPurity || doprocessPurityMc) { + mQaRegistry.add("He3/hDCAxyHe3", "^{3}He;DCA_{xy} (cm)", HistType::kTH1F, {{200, -0.5f, 0.5f}}); + mQaRegistry.add("He3/hDCAzHe3", "^{3}He;DCA_{z} (cm)", HistType::kTH1F, {{200, -1.0f, 1.0f}}); + mQaRegistry.add("He3/hNClsHe3ITS", "^{3}He;N_{ITS} Cluster", HistType::kTH1F, {{20, -10.0f, 10.0f}}); + mQaRegistry.add("He3/hChi2NClHe3ITS", "^{3}He;Chi2_{ITS} Ncluster", HistType::kTH1F, {{100, 0, 100.0f}}); + mQaRegistry.add("He3/hHe3Pt", "^{3}He; #it{p}_{T} (GeV/#it{c})", HistType::kTH1F, {{240, -6.0f, 6.0f}}); + mQaRegistry.add("He3/h2dEdxHe3candidates", "dEdx distribution; #it{p} (GeV/#it{c}); dE/dx (a.u.)", HistType::kTH2F, {{200, -5.0f, 5.0f}, {100, 0.0f, 2000.0f}}); + mQaRegistry.add("He3/h2NsigmaHe3ITS", "NsigmaHe3 ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(^{3}He); Centrality FT0C (%)", HistType::kTH3F, {{100, -5.0f, 5.0f}, {120, -3.0f, 3.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("He3/h2NsigmaHe3ITS_preselection", "NsigmaHe3 ITS distribution; signed #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(^{3}He); Centrality FT0C (%)", HistType::kTH3F, {{50, -5.0f, 5.0f}, {120, -3.0f, 3.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("He3/h2NsigmaHe3TPC", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(^{3}He); Centrality FT0C (%)", HistType::kTH3F, {{100, -5.0f, 5.0f}, {100, -5.0f, 5.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("He3/h2NsigmaHe3TPC_preselection", "NsigmaHe3 TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(^{3}He); Centrality FT0C (%)", HistType::kTH3F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {100, 0.0f, 100.0f}}); + + mQaRegistry.add("Had/hDCAxyHad", "had;DCA_{xy} (cm)", HistType::kTH1F, {{200, -0.5f, 0.5f}}); + mQaRegistry.add("Had/hDCAzHad", "had;DCA_{z} (cm)", HistType::kTH1F, {{200, -1.0f, 1.0f}}); + mQaRegistry.add("Had/hNClsHadITS", "had;N_{ITS} Cluster", HistType::kTH1F, {{20, -10.0f, 10.0f}}); + mQaRegistry.add("Had/hChi2NClHadITS", "had;Chi2_{ITS} Ncluster", HistType::kTH1F, {{100, 0, 100.0f}}); + mQaRegistry.add("Had/hHadronPt", "had; #it{p}_{T} (GeV/#it{c})", HistType::kTH1F, {{120, -3.0f, 3.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronITS", "NsigmaHadron ITS distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{ITS}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {100, -5.0f, 5.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronITS_preselection", "NsigmaHadron ITS distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{ITS}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {120, -3.0f, 3.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronTPC", "NsigmaHadron TPC distribution; #it{p}_{T}(GeV/#it{c}); n#sigma_{TPC}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {100, -5.0f, 5.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronTPC_preselection", "NsigmaHadron TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronTOF", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {100, -5.0f, 5.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronTOF_preselection", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {100, 0.0f, 100.0f}}); + + if (doprocessPurityMc) { + mQaRegistry.add("Had/h2NsigmaHadronTPC_mcBackground", "NsigmaHadron TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronTPC_mcSignal", "NsigmaHadron TPC distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TPC}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronTOF_mcBackground", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {100, 0.0f, 100.0f}}); + mQaRegistry.add("Had/h2NsigmaHadronTOF_mcSignal", "NsigmaHadron TOF distribution; #it{p}_{T} (GeV/#it{c}); n#sigma_{TOF}(had); Centrality FT0C (%)", HistType::kTH3F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {100, 0.0f, 100.0f}}); + } + } + + if (doprocessEventLossMC) { + hEvtMC = mQaRegistry.add("EventLoss/hEvtMC", ";; ", HistType::kTH1D, {{3, -0.5, 2.5}}); + hEvtMC->GetXaxis()->SetBinLabel(1, "All gen evts"); + hEvtMC->GetXaxis()->SetBinLabel(2, "Gen evts with al least one reconstructed"); + hEvtMC->GetXaxis()->SetBinLabel(3, "Gen evts with no reconstructed collisions"); + + hGenEventsNchEta05 = mQaRegistry.add("EventLoss/hGenEventsNchEta05", ";;", HistType::kTH2D, {{400, 0.0f, 4000.0f}, {2, -0.5f, 1.5f}}); + hGenEventsNchEta05->GetYaxis()->SetBinLabel(1, "All gen. events"); + hGenEventsNchEta05->GetYaxis()->SetBinLabel(2, "Gen evts with at least 1 rec. collisions"); + hGenEventsNchEta08 = mQaRegistry.add("EventLoss/hGenEventsNchEta08", ";;", HistType::kTH2D, {{400, 0.0f, 4000.0f}, {2, -0.5f, 1.5f}}); + hGenEventsNchEta08->GetYaxis()->SetBinLabel(1, "All gen. events"); + hGenEventsNchEta08->GetYaxis()->SetBinLabel(2, "Gen evts with at least 1 rec. collisions"); + + hImpactParamGen = mQaRegistry.add("EventLoss/hImpactParamGen", "Impact parameter of generated MC events; Impact Parameter (b); Counts", HistType::kTH1D, {{200, 0.0f, 20.0f}}); + hImpactParamReco = mQaRegistry.add("EventLoss/hImpactParamReco", "Impact parameter of generated MC events with at least one rec. evt; Impact Parameter (b); Counts", HistType::kTH1D, {{200, 0.0f, 20.0f}}); + hRecoCentrality = mQaRegistry.add("EventLoss/hRecoCentrality", "Centrality distribution of reconstructed MC events passed the event selection; Centrality FT0C (%); Counts", HistType::kTH1D, {{100, 0.0f, 100.0f}}); + hRecoCentralityColvsMultiplicityRecoEta05 = mQaRegistry.add("EventLoss/hRecoCentralityColvsMultiplicityRecoEta05", "; Centrality FT0C (%); Multiplicity #eta <0.5", HistType::kTH2D, {{100, 0.0f, 100.0f}, {400, 0.0f, 4000.0f}}); + hRecoCentralityColvsMultiplicityRecoEta08 = mQaRegistry.add("EventLoss/hRecoCentralityColvsMultiplicityRecoEta08", "; Centrality FT0C (%); Multiplicity #eta <0.8", HistType::kTH2D, {{100, 0.0f, 100.0f}, {400, 0.0f, 4000.0f}}); + hRecoCentralityColvsMultiplicityFT0C = mQaRegistry.add("EventLoss/hRecoCentralityColvsMultiplicityFT0C", "; Centrality FT0C (%); FT0C multiplicity", HistType::kTH2D, {{100, 0.0f, 100.0f}, {500, 0.0f, 3000.0f}}); + hRecoCentralityColvsImpactParamReco = mQaRegistry.add("EventLoss/hRecoCentralityColvsImpactParamReco", "; Centrality FT0C (%); Impact Parameter (b)", HistType::kTH2D, {{100, 0.0f, 100.0f}, {200, 0.0f, 20.0f}}); + + hImpactParamGenOneReco = mQaRegistry.add("EventLoss/hImpactParamGenOneReco", "Impact parameter of generated MC events with at least one rec. evt and passed the event selection; Impact Parameter (b); Counts", HistType::kTH1D, {{200, 0.0f, 20.0f}}); + hGenOneRecoCentrality = mQaRegistry.add("EventLoss/hGenOneRecoCentrality", "Centrality distribution of generated MC events with at least one rec. evt and passed the event selection; Centrality FT0C (%); Counts", HistType::kTH1D, {{100, 0.0f, 100.0f}}); + hGenCentralityColvsMultiplicityGenEta05 = mQaRegistry.add("EventLoss/hGenCentralityColvsMultiplicityGenEta05", "; Centrality FT0C (%); Multiplicity #eta <0.5", HistType::kTH2D, {{100, 0.0f, 100.0f}, {400, 0.0f, 4000.0f}}); + hGenCentralityColvsMultiplicityGenEta08 = mQaRegistry.add("EventLoss/hGenCentralityColvsMultiplicityGenEta08", "; Centrality FT0C (%); Multiplicity #eta <0.8", HistType::kTH2D, {{100, 0.0f, 100.0f}, {400, 0.0f, 4000.0f}}); + hGenCentralityColvsMultiplicityFT0C = mQaRegistry.add("EventLoss/hGenCentralityColvsMultiplicityFT0C", "; Centrality FT0C (%); FT0C multiplicity", HistType::kTH2D, {{100, 0.0f, 100.0f}, {500, 0.0f, 3000.0f}}); + hGenCentralityColvsImpactParamGen = mQaRegistry.add("EventLoss/hGenCentralityColvsImpactParamGen", "; Centrality FT0C (%); Impact Parameter (b)", HistType::kTH2D, {{100, 0.0f, 100.0f}, {200, 0.0f, 20.0f}}); + + hGenLi4BeforeEvtSel = mQaRegistry.add("EventLoss/hGenLi4BeforeEvtSel", "Li4 generated #it{p}_{T} distribution in all gen evt; #it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {{240, 0.0f, 12.0f}}); + hGenLi4vsImpactParameterBeforeEvtSel = mQaRegistry.add("EventLoss/hGenLi4vsImpactParameterBeforeEvtSel", "; #it{p}_{T} (GeV/#it{c}); Impact Parameter (b)", HistType::kTH2D, {{240, 0.0f, 12.0f}, {200, 0.0f, 20.0f}}); + hGenLi4vsMultiplicityGenEta05BeforeEvtSel = mQaRegistry.add("EventLoss/hGenLi4vsMultiplicityGenEta05BeforeEvtSel", "; #it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.5", HistType::kTH2D, {{240, 0.0f, 12.0f}, {400, 0.0f, 4000.0f}}); + hGenLi4vsMultiplicityGenEta08BeforeEvtSel = mQaRegistry.add("EventLoss/hGenLi4vsMultiplicityGenEta08BeforeEvtSel", "; #it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {{240, 0.0f, 12.0f}, {400, 0.0f, 4000.0f}}); + hGenLi4vsMultiplicityFT0CBeforeEvtSel = mQaRegistry.add("EventLoss/hGenLi4vsMultiplicityFT0CBeforeEvtSel", "; #it{p}_{T} (GeV/#it{c}); FT0C multiplicity", HistType::kTH2D, {{240, 0.0f, 12.0f}, {500, 0.0f, 3000.0f}}); + + hGenLi4AfterSel = mQaRegistry.add("EventLoss/hGenLi4AfterSel", "Li4 generated #it{p}_{T} distribution in gen. evts with at least one rec. evt; #it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {{240, 0.0f, 12.0f}}); + hGenLi4vsImpactParameterAfterSel = mQaRegistry.add("EventLoss/hGenLi4vsImpactParameterAfterSel", "; #it{p}_{T} (GeV/#it{c}); Impact Parameter (b)", HistType::kTH2D, {{240, 0.0f, 12.0f}, {200, 0.0f, 20.0f}}); + hGenLi4vsMultiplicityGenEta05AfterSel = mQaRegistry.add("EventLoss/hGenLi4vsMultiplicityGenEta05AfterSel", "; #it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.5", HistType::kTH2D, {{240, 0.0f, 12.0f}, {400, 0.0f, 4000.0f}}); + hGenLi4vsMultiplicityGenEta08AfterSel = mQaRegistry.add("EventLoss/hGenLi4vsMultiplicityGenEta08AfterSel", "; #it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {{240, 0.0f, 12.0f}, {400, 0.0f, 4000.0f}}); + hGenLi4vsMultiplicityFT0CAfterSel = mQaRegistry.add("EventLoss/hGenLi4vsMultiplicityFT0CAfterSel", "; #it{p}_{T} (GeV/#it{c}); FT0C multiplicity", HistType::kTH2D, {{240, 0.0f, 12.0f}, {500, 0.0f, 3000.0f}}); + } } void initCCDB(const aod::BCsWithTimestamps::iterator& bc) @@ -427,8 +601,8 @@ struct he3HadronFemto { mRunNumber = bc.runNumber(); const float defaultBzValue = -999.0f; auto run3GrpTimestamp = bc.timestamp(); - o2::parameters::GRPObject* grpo = mCcdb->getForTimeStamp(settingGrpPath, run3GrpTimestamp); - o2::parameters::GRPMagField* grpmag = 0x0; + auto grpo = mCcdb->getForTimeStamp(settingGrpPath, run3GrpTimestamp); + o2::parameters::GRPMagField* grpmag = nullptr; if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); if (settingDbz < defaultBzValue) { @@ -457,16 +631,22 @@ struct he3HadronFemto { // ================================================================================================================== template - bool selectCollision(const Tcollision& collision, const aod::BCsWithTimestamps&) + bool selectCollision(const Tcollision& collision, const aod::BCsWithTimestamps&, uint32_t& collisionSelectionFlag) { mQaRegistry.fill(HIST("hEvents"), 0); + if constexpr (isMC) { + if (collision.has_mcCollision()) { + mMcCollisionIdToRecoCollisionId[collision.mcCollisionId()] = collision.globalIndex(); + } + } auto bc = collision.template bc_as(); initCCDB(bc); - if (!nuclei::eventSelection(collision, mQaRegistry, settingEventSelections, settingCutVertex)) { + if (!nuclei::eventSelection(collision, mQaRegistry, settingEventSelections, cutSettings.settingCutVertex, collisionSelectionFlag)) { return false; } + if (settingSkimmedProcessing) { bool zorroSelected = mZorro.isSelected(collision.template bc_as().globalBC()); if (zorroSelected) { @@ -484,72 +664,73 @@ struct he3HadronFemto { template bool selectTrack(const Ttrack& candidate, const int ispecies) { - if (std::abs(candidate.eta()) > settingCutEta) { + if (std::abs(candidate.eta()) > cutSettings.settingCutEta) { return false; } - const int minTPCNClsFound = ispecies == Species::kHe3 ? static_cast(settingCutNClsTPCHe3) : static_cast(settingCutNClsTPCHe3); + const int minTPCNClsFound = ispecies == Species::kHe3 ? static_cast(cutSettings.settingCutNClsTPCHe3) : static_cast(cutSettings.settingCutNClsTPC); const int minTPCNClsCrossedRows = 70; - const float crossedRowsToFindableRatio = 0.8f; - const float minChi2NCl = ispecies == Species::kHe3 ? static_cast(settingCutChi2tpcLowHe3) : static_cast(settingCutChi2tpcLow); + const float minChi2NCl = ispecies == Species::kHe3 ? static_cast(cutSettings.settingCutChi2tpcLowHe3) : static_cast(cutSettings.settingCutChi2tpcLow); const float maxChi2NCl = 4.f; - if (candidate.itsNCls() < settingCutNCls || + if (candidate.itsNCls() < cutSettings.settingCutNCls || candidate.tpcNClsFound() < minTPCNClsFound || candidate.tpcNClsCrossedRows() < minTPCNClsCrossedRows || - candidate.tpcNClsCrossedRows() < crossedRowsToFindableRatio * candidate.tpcNClsFindable() || candidate.tpcChi2NCl() > maxChi2NCl || candidate.tpcChi2NCl() < minChi2NCl || - candidate.itsChi2NCl() > settingCutChi2NClITS) { + candidate.itsChi2NCl() > cutSettings.settingCutChi2NClITS) { return false; } return true; } - template - float correctPtHe3TrackedAsTriton(const Ttrack& candidate) + float correctPtHe3TrackedAsTriton(const float pt, const uint32_t pidForTracking) { - if (candidate.pt() * 2. < 2.5 && candidate.pidForTracking() == o2::track::PID::Triton) - return candidate.pt() * 2. * (1. - kHePidTrkParams[0] - kHePidTrkParams[1] * candidate.pt() * 2.); + if (pt < 2.5 && pidForTracking == o2::track::PID::Triton) { + return pt * (1. - mHe3HadronParams.hePidTrkPtParams[0] - mHe3HadronParams.hePidTrkPtParams[1] * pt - mHe3HadronParams.hePidTrkPtParams[2] * pt * pt); + } - return candidate.pt() * 2.; + return pt; } - float correctPtHe3TrackedAsTriton(const float pt, const uint32_t pidForTracking) + float correctTpcInnerParamHe3(const float tpcInnerParam, const uint32_t pidForTracking) { - if (pt < 2.5 && pidForTracking == o2::track::PID::Triton) - return pt * (1. - kHePidTrkParams[0] - kHePidTrkParams[1] * pt); + bool heliumPID = pidForTracking == o2::track::PID::Helium3 || pidForTracking == o2::track::PID::Alpha; + float correctedTPCinnerParam = (heliumPID && settingCompensatePIDinTracking) ? tpcInnerParam / 2.f : tpcInnerParam; - return pt; + correctedTPCinnerParam *= 2.f; // rigidity to momentum + if (correctedTPCinnerParam < 2.5 && pidForTracking == o2::track::PID::Triton) { + return correctedTPCinnerParam * (1. - mHe3HadronParams.hePidTrkPParams[0] - mHe3HadronParams.hePidTrkPParams[1] * correctedTPCinnerParam - mHe3HadronParams.hePidTrkPParams[2] * correctedTPCinnerParam * correctedTPCinnerParam); + } + + return correctedTPCinnerParam; } float computeNsigmaDCA(const float pt, const float dca, const int iSpecies, const char* dcaType = "xy") { - std::array parameters; + std::array parameters{{0., 0., 0., 0.}}; if (std::strcmp(dcaType, "xy") == 0) { - parameters = kDCAxyResolutionParams[iSpecies]; + parameters = mHe3HadronParams.dcaxyResParams[iSpecies]; } else if (std::strcmp(dcaType, "z") == 0) { - parameters = kDCAzResolutionParams[iSpecies]; + parameters = mHe3HadronParams.dcazResParams[iSpecies]; } else { LOG(error) << "Invalid dcaType. Accepted types are 'xy' 'z'"; - parameters = {0., 0., 0.}; + parameters = {0., 0., 0., 0.}; } - const float sigma = parameters[0] * - std::exp(-std::abs(pt) * parameters[1]) + - parameters[2]; - return dca / sigma; + const float sigma = parameters[1] + parameters[2] / std::pow(std::abs(pt), parameters[3]); + const float mean = parameters[0]; + return (dca - mean) / sigma; } - template - bool selectDcaNsigmaCut(const Ttrack& candidate, const int ispecies) + bool selectDcaNsigmaCut(const float pt, const float dcaXY, const float dcaZ, const int ispecies) { - const float pt = ispecies == Species::kHe3 ? 2. * candidate.pt() : candidate.pt(); - const float nsigmaDcaXy = computeNsigmaDCA(pt, candidate.dcaXY(), ispecies, "xy"); - const float nsigmaDcaZ = computeNsigmaDCA(pt, candidate.dcaZ(), ispecies, "z"); + const float nsigmaDcaXy = computeNsigmaDCA(pt, dcaXY, ispecies, "xy"); + const float nsigmaDcaZ = computeNsigmaDCA(pt, dcaZ, ispecies, "z"); - if (std::abs(nsigmaDcaXy) > settingCutNsigmaDcaXy || - std::abs(nsigmaDcaZ) > settingCutNsigmaDcaZ) + if (std::abs(nsigmaDcaXy) > cutSettings.settingCutNsigmaDcaXy || + std::abs(nsigmaDcaZ) > cutSettings.settingCutNsigmaDcaZ) { return false; + } return true; } @@ -582,26 +763,26 @@ struct he3HadronFemto { return tofNSigmaHad; } - template - bool selectionPIDHadron(const Ttrack& candidate) + template + bool selectionPIDHadron(const Ttrack& candidate, const Tcollision& collision) { auto tpcNSigmaHad = computeTPCNSigmaHadron(candidate); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_preselection"), candidate.tpcInnerParam(), tpcNSigmaHad); - if (candidate.hasTOF() && candidate.pt() > settingCutPtMinTOFHad) { + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_preselection"), candidate.tpcInnerParam(), tpcNSigmaHad, collision.centFT0C()); + if (candidate.hasTOF() && candidate.pt() > cutSettings.settingCutPtMinTOFHad) { auto tofNSigmaHad = computeTOFNSigmaHadron(candidate); - if (std::abs(tpcNSigmaHad) > settingCutNsigmaTPC) { + if (std::abs(tpcNSigmaHad) > cutSettings.settingCutNsigmaTPC) { return false; } - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_preselection"), candidate.pt(), tofNSigmaHad); - if (std::abs(tofNSigmaHad) > settingCutNsigmaTOF) { + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_preselection"), candidate.pt(), tofNSigmaHad, collision.centFT0C()); + if (std::abs(tofNSigmaHad) > cutSettings.settingCutNsigmaTOF) { return false; } - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC"), candidate.pt(), tpcNSigmaHad); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF"), candidate.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC"), candidate.pt(), tpcNSigmaHad, collision.centFT0C()); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF"), candidate.pt(), tofNSigmaHad, collision.centFT0C()); return true; - } else if (std::abs(tpcNSigmaHad) < settingCutNsigmaTPC) { - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC"), candidate.pt(), tpcNSigmaHad); + } else if (std::abs(tpcNSigmaHad) < cutSettings.settingCutNsigmaTPC) { + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC"), candidate.pt(), tpcNSigmaHad, collision.centFT0C()); return true; } return false; @@ -612,37 +793,50 @@ struct he3HadronFemto { { bool heliumPID = candidate.pidForTracking() == o2::track::PID::Helium3 || candidate.pidForTracking() == o2::track::PID::Alpha; float correctedTPCinnerParam = (heliumPID && settingCompensatePIDinTracking) ? candidate.tpcInnerParam() / 2.f : candidate.tpcInnerParam(); - float expTPCSignal = o2::common::BetheBlochAleph(static_cast(correctedTPCinnerParam * 2.f / constants::physics::MassHelium3), mBBparamsHe[0], mBBparamsHe[1], mBBparamsHe[2], mBBparamsHe[3], mBBparamsHe[4]); + float expTPCSignal = o2::common::BetheBlochAleph(static_cast(correctedTPCinnerParam * 2.f / constants::physics::MassHelium3), mHe3HadronParams.betheBlochParams[0], mHe3HadronParams.betheBlochParams[1], mHe3HadronParams.betheBlochParams[2], mHe3HadronParams.betheBlochParams[3], mHe3HadronParams.betheBlochParams[4]); - double resoTPC{expTPCSignal * mBBparamsHe[5]}; + double resoTPC{expTPCSignal * mHe3HadronParams.betheBlochParams[5]}; return static_cast((candidate.tpcSignal() - expTPCSignal) / resoTPC); } - template - bool selectionPIDHe3(const Ttrack& candidate) + float computeNSigmaHe3(const float tpcInnerParam, const float tpcSignal, const bool applyCorrection = false) { - bool heliumPID = candidate.pidForTracking() == o2::track::PID::Helium3 || candidate.pidForTracking() == o2::track::PID::Alpha; - float correctedTPCinnerParam = (heliumPID && settingCompensatePIDinTracking) ? candidate.tpcInnerParam() / 2.f : candidate.tpcInnerParam(); + const float betagamma = tpcInnerParam / constants::physics::MassHelium3; + float expTPCSignal = o2::common::BetheBlochAleph(static_cast(betagamma), mHe3HadronParams.betheBlochParams[0], mHe3HadronParams.betheBlochParams[1], mHe3HadronParams.betheBlochParams[2], mHe3HadronParams.betheBlochParams[3], mHe3HadronParams.betheBlochParams[4]); + if (applyCorrection) { + double residualCorrection = (mHe3HadronParams.betheBlochCorrectionParams[0] * std::exp(-(betagamma - mHe3HadronParams.betheBlochCorrectionParams[1]) * (betagamma - mHe3HadronParams.betheBlochCorrectionParams[1]) / (2 * mHe3HadronParams.betheBlochCorrectionParams[2] * mHe3HadronParams.betheBlochCorrectionParams[2])) + mHe3HadronParams.betheBlochCorrectionParams[3] * std::exp(-(betagamma - mHe3HadronParams.betheBlochCorrectionParams[4]) * (betagamma - mHe3HadronParams.betheBlochCorrectionParams[4]) / (2 * mHe3HadronParams.betheBlochCorrectionParams[5] * mHe3HadronParams.betheBlochCorrectionParams[5]))); + expTPCSignal += residualCorrection; + } + + double resoTPC{expTPCSignal * mHe3HadronParams.betheBlochParams[5]}; + return static_cast((tpcSignal - expTPCSignal) / resoTPC); + } + + template + bool selectionPIDHe3(const Ttrack& candidate, const Tcollision& collision) + { + const float correctedTPCinnerParam = correctTpcInnerParamHe3(candidate.tpcInnerParam(), candidate.pidForTracking()); + const float correctedPt = correctPtHe3TrackedAsTriton(candidate.pt(), candidate.pidForTracking()); - if (correctedTPCinnerParam < settingCutRigidityMinHe3) { + if (correctedTPCinnerParam < cutSettings.settingCutRigidityMinHe3) { return false; } - auto nSigmaHe3 = computeNSigmaHe3(candidate); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC_preselection"), candidate.sign() * 2 * candidate.pt(), nSigmaHe3); - if (std::abs(nSigmaHe3) > settingCutNsigmaTPC) { + auto nSigmaHe3 = computeNSigmaHe3(correctedTPCinnerParam, candidate.tpcSignal(), /*applyCorrection*/ true); + mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC_preselection"), candidate.sign() * correctedPt, nSigmaHe3, collision.centFT0C()); + if (std::abs(nSigmaHe3) > cutSettings.settingCutNsigmaTPC) { return false; } auto itsNsigmaHe3 = mResponseITS.nSigmaITS(candidate.itsClusterSizes(), 2 * candidate.p(), candidate.eta()); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS_preselection"), candidate.sign() * 2 * candidate.pt(), itsNsigmaHe3); - if (itsNsigmaHe3 < settingCutNsigmaITSHe3) { + mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS_preselection"), candidate.sign() * correctedPt, itsNsigmaHe3, collision.centFT0C()); + if (itsNsigmaHe3 < cutSettings.settingCutNsigmaITSHe3) { return false; } mQaRegistry.fill(HIST("He3/h2dEdxHe3candidates"), candidate.sign() * correctedTPCinnerParam, candidate.tpcSignal()); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC"), candidate.sign() * 2 * candidate.pt(), nSigmaHe3); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS"), candidate.sign() * 2 * candidate.pt(), itsNsigmaHe3); + mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC"), candidate.sign() * correctedPt, nSigmaHe3, collision.centFT0C()); + mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS"), candidate.sign() * correctedPt, itsNsigmaHe3, collision.centFT0C()); return true; } @@ -706,8 +900,9 @@ struct he3HadronFemto { std::array collisionVertex = getCollisionVertex(collisions, he3Hadcand.collisionID); he3Hadcand.momHe3 = std::array{trackHe3.px(), trackHe3.py(), trackHe3.pz()}; - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { he3Hadcand.momHe3[i] = he3Hadcand.momHe3[i] * 2; + } he3Hadcand.momHad = std::array{trackHad.px(), trackHad.py(), trackHad.pz()}; float invMass = CommonInite; if (settingHadPDGCode == PDG_t::kPiPlus) { @@ -718,11 +913,11 @@ struct he3HadronFemto { LOG(info) << "invalid PDG code for invMass"; } - if (settingCutInvMass > 0 && invMass > settingCutInvMass) { + if (cutSettings.settingCutInvMass > 0 && invMass > cutSettings.settingCutInvMass) { return false; } float pthe3Had = std::hypot(he3Hadcand.momHe3[0] + he3Hadcand.momHad[0], he3Hadcand.momHe3[1] + he3Hadcand.momHad[1]); - if (pthe3Had < settingCutPtMinhe3Had) { + if (pthe3Had < cutSettings.settingCutPtMinhe3Had) { return false; } @@ -736,28 +931,28 @@ struct he3HadronFemto { } else { auto trackCovHe3 = getTrackParCov(trackHe3); auto trackCovHad = getTrackParCov(trackHad); - std::array dcaInfo; + std::array dcaInfo{{-999., -999.}}; o2::base::Propagator::Instance()->propagateToDCABxByBz({collisionVertex[0], collisionVertex[1], collisionVertex[2]}, trackCovHe3, 2.f, mFitter.getMatCorrType(), &dcaInfo); he3Hadcand.dcaxyHe3 = dcaInfo[0]; he3Hadcand.dcazHe3 = dcaInfo[1]; o2::base::Propagator::Instance()->propagateToDCABxByBz({collisionVertex[0], collisionVertex[1], collisionVertex[2]}, trackCovHad, 2.f, mFitter.getMatCorrType(), &dcaInfo); he3Hadcand.dcaxyHad = dcaInfo[0]; he3Hadcand.dcazHad = dcaInfo[1]; - he3Hadcand.dcaPair = std::sqrt(std::abs(mFitter.getChi2AtPCACandidate())); } he3Hadcand.tpcSignalHe3 = trackHe3.tpcSignal(); - bool heliumPID = trackHe3.pidForTracking() == o2::track::PID::Helium3 || trackHe3.pidForTracking() == o2::track::PID::Alpha; - float correctedTPCinnerParamHe3 = (heliumPID && settingCompensatePIDinTracking) ? trackHe3.tpcInnerParam() / 2.f : trackHe3.tpcInnerParam(); + const float correctedTPCinnerParamHe3 = correctTpcInnerParamHe3(trackHe3.tpcInnerParam(), trackHe3.pidForTracking()); he3Hadcand.momHe3TPC = correctedTPCinnerParamHe3; he3Hadcand.tpcSignalHad = trackHad.tpcSignal(); he3Hadcand.momHadTPC = trackHad.tpcInnerParam(); he3Hadcand.nTPCClustersHe3 = trackHe3.tpcNClsFound(); - he3Hadcand.nSigmaHe3 = computeNSigmaHe3(trackHe3); + he3Hadcand.nSigmaHe3 = computeNSigmaHe3(correctedTPCinnerParamHe3, trackHe3.tpcSignal(), /*applyCorrection*/ true); + he3Hadcand.nSigmaTPCHad = computeTPCNSigmaHadron(trackHad); he3Hadcand.nSigmaTPCHadPi = trackHad.tpcNSigmaPi(); he3Hadcand.nSigmaTPCHadKa = trackHad.tpcNSigmaKa(); he3Hadcand.nSigmaTPCHadPr = trackHad.tpcNSigmaPr(); + he3Hadcand.nSigmaTOFHad = computeTOFNSigmaHadron(trackHad); he3Hadcand.nSigmaTOFHadPi = trackHad.tofNSigmaPi(); he3Hadcand.nSigmaTOFHadKa = trackHad.tofNSigmaKa(); he3Hadcand.nSigmaTOFHadPr = trackHad.tofNSigmaPr(); @@ -790,8 +985,6 @@ struct he3HadronFemto { if (trackHe3.hasTOF()) { float beta = o2::pid::tof::Beta::GetBeta(trackHe3); beta = std::min(1.f - 1.e-6f, std::max(1.e-4f, beta)); /// sometimes beta > 1 or < 0, to be checked - bool heliumPID = trackHe3.pidForTracking() == o2::track::PID::Helium3 || trackHe3.pidForTracking() == o2::track::PID::Alpha; - float correctedTPCinnerParamHe3 = (heliumPID && settingCompensatePIDinTracking) ? trackHe3.tpcInnerParam() / 2.f : trackHe3.tpcInnerParam(); he3Hadcand.massTOFHe3 = correctedTPCinnerParamHe3 * 2.f * std::sqrt(1.f / (beta * beta) - 1.f); } if (trackHad.hasTOF()) { @@ -822,8 +1015,8 @@ struct he3HadronFemto { he3Hadcand.l4MassMC = std::sqrt(eLit * eLit - mctrackMother.p() * mctrackMother.p()); } - template - void pairTracksSameEvent(const Ttrack& tracks) + template + void pairTracksSameEvent(const Ttrack& tracks, const Tcollision& collision) { for (const auto& track0 : tracks) { @@ -834,7 +1027,7 @@ struct he3HadronFemto { } mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); - if (!selectionPIDHe3(track0)) { + if (!selectionPIDHe3(track0, collision)) { continue; } mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); @@ -854,7 +1047,7 @@ struct he3HadronFemto { } } - if (!selectTrack(track1, Species::kHad) || !selectionPIDHadron(track1)) { + if (!selectTrack(track1, Species::kHad) || !selectionPIDHadron(track1, collision)) { continue; } @@ -869,15 +1062,15 @@ struct he3HadronFemto { } } - template - void pairTracksEventMixing(T& he3Cands, T& hadronCands) + template + void pairTracksEventMixing(Ttrack& he3Cands, Ttrack& hadronCands, const Tcollision& collision) { for (const auto& he3Cand : he3Cands) { - if (!selectTrack(he3Cand, Species::kHe3) || !selectionPIDHe3(he3Cand)) { + if (!selectTrack(he3Cand, Species::kHe3) || !selectionPIDHe3(he3Cand, collision)) { continue; } for (const auto& hadronCand : hadronCands) { - if (!selectTrack(hadronCand, Species::kHad) || !selectionPIDHadron(hadronCand)) { + if (!selectTrack(hadronCand, Species::kHad) || !selectionPIDHadron(hadronCand, collision)) { continue; } @@ -892,77 +1085,49 @@ struct he3HadronFemto { } } - template - void fillTable(const He3HadCandidate& he3Hadcand, const Tcoll& collision, bool isMC = false) + void fillTable(const He3HadCandidate& he3Hadcand, const CollisionInfo& collisionInfo, bool isMC = false) { outputDataTable( - he3Hadcand.recoPtHe3(), - he3Hadcand.recoEtaHe3(), - he3Hadcand.recoPhiHe3(), - he3Hadcand.recoPtHad(), - he3Hadcand.recoEtaHad(), - he3Hadcand.recoPhiHad(), - he3Hadcand.dcaxyHe3, - he3Hadcand.dcazHe3, - he3Hadcand.dcaxyHad, - he3Hadcand.dcazHad, - he3Hadcand.dcaPair, - he3Hadcand.tpcSignalHe3, - he3Hadcand.momHe3TPC, - he3Hadcand.tpcSignalHad, - he3Hadcand.momHadTPC, - he3Hadcand.nTPCClustersHe3, - he3Hadcand.nSigmaHe3, - he3Hadcand.nSigmaTPCHadPi, - he3Hadcand.nSigmaTPCHadKa, - he3Hadcand.nSigmaTPCHadPr, - he3Hadcand.nSigmaTOFHadPi, - he3Hadcand.nSigmaTOFHadKa, - he3Hadcand.nSigmaTOFHadPr, - he3Hadcand.chi2TPCHe3, - he3Hadcand.chi2TPCHad, - he3Hadcand.massTOFHe3, - he3Hadcand.massTOFHad, - he3Hadcand.pidtrkHe3, - he3Hadcand.pidtrkHad, - he3Hadcand.itsClSizeHe3, - he3Hadcand.itsClSizeHad, - he3Hadcand.sharedClustersHe3, - he3Hadcand.sharedClustersHad); + he3Hadcand.recoPtHe3(), he3Hadcand.recoEtaHe3(), he3Hadcand.recoPhiHe3(), + he3Hadcand.recoPtHad(), he3Hadcand.recoEtaHad(), he3Hadcand.recoPhiHad(), + he3Hadcand.dcaxyHe3, he3Hadcand.dcazHe3, + he3Hadcand.dcaxyHad, he3Hadcand.dcazHad, + he3Hadcand.tpcSignalHe3, he3Hadcand.momHe3TPC, + he3Hadcand.tpcSignalHad, he3Hadcand.momHadTPC, + he3Hadcand.nTPCClustersHe3, he3Hadcand.nSigmaHe3, + he3Hadcand.nSigmaTPCHad, he3Hadcand.nSigmaTOFHad, + he3Hadcand.chi2TPCHe3, he3Hadcand.chi2TPCHad, + he3Hadcand.massTOFHad, he3Hadcand.pidtrkHe3, + he3Hadcand.itsClSizeHe3, he3Hadcand.itsClSizeHad, + he3Hadcand.sharedClustersHe3); if (isMC) { outputMcTable( - he3Hadcand.momHe3MC, - he3Hadcand.etaHe3MC, - he3Hadcand.phiHe3MC, - he3Hadcand.momHadMC, - he3Hadcand.etaHadMC, - he3Hadcand.phiHadMC, - he3Hadcand.l4PtMC, - he3Hadcand.l4MassMC, - he3Hadcand.flags); - } - if (settingFillMultiplicity) { - outputMultiplicityTable( - collision.globalIndex(), - collision.posZ(), - collision.numContrib(), - collision.centFT0C(), - collision.multFT0C()); - } - if (settingFillQa) { - outputQaTable( - he3Hadcand.trackIDHe3, - he3Hadcand.trackIDHad); + he3Hadcand.momHe3MC, he3Hadcand.etaHe3MC, he3Hadcand.phiHe3MC, + he3Hadcand.momHadMC, he3Hadcand.etaHadMC, he3Hadcand.phiHadMC, + he3Hadcand.l4PtMC, he3Hadcand.l4MassMC, he3Hadcand.flags); } + outputMultiplicityTable( + collisionInfo.collisionID, collisionInfo.selectionFlags, collisionInfo.posZ, + collisionInfo.numContributors, collisionInfo.centFT0C, collisionInfo.multFT0C, + collisionInfo.occupancy); + outputQaTable( + he3Hadcand.trackIDHe3, he3Hadcand.trackIDHad, he3Hadcand.massTOFHe3, + he3Hadcand.pidtrkHad, he3Hadcand.sharedClustersHad); + outputPidTable( + he3Hadcand.nSigmaTPCHadPi, he3Hadcand.nSigmaTPCHadKa, he3Hadcand.nSigmaTPCHadPr, + he3Hadcand.nSigmaTOFHadPi, he3Hadcand.nSigmaTOFHadKa, he3Hadcand.nSigmaTOFHadPr); } void fillHistograms(const He3HadCandidate& he3Hadcand, bool isMc = false) { mQaRegistry.fill(HIST("He3/hHe3Pt"), he3Hadcand.recoPtHe3()); mQaRegistry.fill(HIST("Had/hHadronPt"), he3Hadcand.recoPtHad()); - mQaRegistry.fill(HIST("hhe3HadtInvMass"), he3Hadcand.invMass); + mQaRegistry.fill(HIST("hhe3HadInvMass"), he3Hadcand.invMass); + mQaRegistry.fill(HIST("hhe3HadKstar"), he3Hadcand.recoKstar()); mQaRegistry.fill(HIST("He3/hDCAxyHe3"), he3Hadcand.dcaxyHe3); mQaRegistry.fill(HIST("He3/hDCAzHe3"), he3Hadcand.dcazHe3); + mQaRegistry.fill(HIST("Had/hDCAxyHad"), he3Hadcand.dcaxyHad); + mQaRegistry.fill(HIST("Had/hDCAzHad"), he3Hadcand.dcazHad); mQaRegistry.fill(HIST("He3/hNClsHe3ITS"), he3Hadcand.nclsITSHe3); mQaRegistry.fill(HIST("Had/hNClsHadITS"), he3Hadcand.nclsITSHad); mQaRegistry.fill(HIST("He3/hChi2NClHe3ITS"), he3Hadcand.chi2nclITSHe3); @@ -995,7 +1160,9 @@ struct he3HadronFemto { } fillHistograms(he3Hadcand); auto collision = collisions.rawIteratorAt(he3Hadcand.collisionID); - fillTable(he3Hadcand, collision, /*isMC*/ false); + CollisionInfo collisionInfo; + collisionInfo.fillFromCollision(collision, mCollisionSelectionFlags[he3Hadcand.collisionID]); + fillTable(he3Hadcand, collisionInfo, /*isMC*/ false); } } @@ -1016,7 +1183,7 @@ struct he3HadronFemto { } else if (std::abs(mother.pdgCode()) == H3LPDG) { flag |= ParticleFlags::kFromHypertriton; } else { - flag |= ParticleFlags::kFromOtherDecays; + return; // other decays is thought for weak decays } } @@ -1089,8 +1256,15 @@ struct he3HadronFemto { He3HadCandidate he3Hadcand; fillCandidateInfoMC(mcHe3, mcHad, he3Hadcand); fillMotherInfoMC(mcHe3, mcHad, mcParticle, he3Hadcand); - auto collision = collisions.rawIteratorAt(he3Hadcand.collisionID); - fillTable(he3Hadcand, collision, /*isMC*/ true); + + const auto mcCollisionId = mcParticle.mcCollisionId(); + const auto collisionId = mMcCollisionIdToRecoCollisionId[mcCollisionId]; + CollisionInfo collisionInfo; + if (collisionId >= 0) { + auto collision = collisions.rawIteratorAt(collisionId); + collisionInfo.fillFromCollision(collision, mCollisionSelectionFlags[collisionId]); + } + fillTable(he3Hadcand, collisionInfo, /*isMC*/ true); } } } @@ -1101,23 +1275,27 @@ struct he3HadronFemto { { mGoodCollisions.clear(); mGoodCollisions.resize(collisions.size(), false); + mCollisionSelectionFlags.clear(); + mCollisionSelectionFlags.resize(collisions.size(), 0); for (const auto& collision : collisions) { mTrackPairs.clear(); - if (!selectCollision(collision, bcs)) { + uint32_t collisionSelectionFlag = 0; + if (!selectCollision(collision, bcs, collisionSelectionFlag)) { continue; } mGoodCollisions[collision.globalIndex()] = true; + mCollisionSelectionFlags[collision.globalIndex()] = collisionSelectionFlag; const uint64_t collIdx = collision.globalIndex(); auto trackTableThisCollision = tracks.sliceBy(mPerCol, collIdx); trackTableThisCollision.bindExternalIndices(&tracks); - pairTracksSameEvent(trackTableThisCollision); + pairTracksSameEvent(trackTableThisCollision, collision); - if (mTrackPairs.size() == 0) { + if (mTrackPairs.empty()) { continue; } @@ -1126,48 +1304,60 @@ struct he3HadronFemto { } PROCESS_SWITCH(he3HadronFemto, processSameEvent, "Process Same event", false); - void processMixedEvent(const CollisionsFull& collisions, const TrackCandidates& tracks) + void processMixedEvent(const CollisionsFull& collisions, const aod::BCsWithTimestamps& bcs, const TrackCandidates& tracks) { LOG(debug) << "Processing mixed event"; mTrackPairs.clear(); + mCollisionSelectionFlags.clear(); + mCollisionSelectionFlags.resize(collisions.size(), 0); for (const auto& [c1, tracks1, c2, tracks2] : mPair) { - if (!c1.sel8() || !c2.sel8()) { + uint32_t collisionSelectionFlag1 = 0; + uint32_t collisionSelectionFlag2 = 0; + if (!selectCollision(c1, bcs, collisionSelectionFlag1) || !selectCollision(c2, bcs, collisionSelectionFlag2)) { continue; } + mCollisionSelectionFlags[c1.globalIndex()] = collisionSelectionFlag1; + mCollisionSelectionFlags[c2.globalIndex()] = collisionSelectionFlag2; mQaRegistry.fill(HIST("hNcontributor"), c1.numContrib()); mQaRegistry.fill(HIST("hVtxZ"), c1.posZ()); - pairTracksEventMixing(tracks1, tracks2); - pairTracksEventMixing(tracks2, tracks1); + pairTracksEventMixing(tracks1, tracks2, c1); + pairTracksEventMixing(tracks2, tracks1, c2); } fillPairs(collisions, tracks, /*isMixedEvent*/ true); } PROCESS_SWITCH(he3HadronFemto, processMixedEvent, "Process Mixed event", false); - void processMC(const CollisionsFullMC& collisions, const aod::BCsWithTimestamps& bcs, const TrackCandidatesMC& tracks, const aod::McParticles& mcParticles) + void processMC(const CollisionsFullMC& collisions, const aod::McCollisions& mcCollisions, const aod::BCsWithTimestamps& bcs, const TrackCandidatesMC& tracks, const aod::McParticles& mcParticles) { std::vector filledMothers; mGoodCollisions.clear(); mGoodCollisions.resize(collisions.size(), false); + mMcCollisionIdToRecoCollisionId.clear(); + mMcCollisionIdToRecoCollisionId.resize(mcCollisions.size(), -1); + mCollisionSelectionFlags.clear(); + mCollisionSelectionFlags.resize(collisions.size(), 0); for (const auto& collision : collisions) { mTrackPairs.clear(); - if (!selectCollision(collision, bcs)) { + uint32_t collisionSelectionFlag = 0; + if (!selectCollision(collision, bcs, collisionSelectionFlag)) { continue; } + mCollisionSelectionFlags[collision.globalIndex()] = collisionSelectionFlag; const uint64_t collIdx = collision.globalIndex(); mGoodCollisions[collIdx] = true; auto trackTableThisCollision = tracks.sliceBy(mPerColMC, collIdx); trackTableThisCollision.bindExternalIndices(&tracks); - pairTracksSameEvent(trackTableThisCollision); + pairTracksSameEvent(trackTableThisCollision, collision); for (const auto& trackPair : mTrackPairs) { @@ -1198,14 +1388,14 @@ struct he3HadronFemto { he3Hadcand.flags |= Flags::kBothPrimaries; isMixedPair = false; - } else if ((he3Hadcand.flagsHe3 & ParticleFlags::kFromLi4) && (he3Hadcand.flagsHad & ParticleFlags::kFromLi4)) { + } else if (static_cast(he3Hadcand.flagsHe3 & ParticleFlags::kFromLi4) && static_cast(he3Hadcand.flagsHad & ParticleFlags::kFromLi4)) { searchForCommonMotherTrack(motherHe3Idxs, motherHadIdxs, mcParticles, motherParticle, he3Hadcand, isMixedPair, Li4PDG); if (!isMixedPair) { he3Hadcand.flags |= Flags::kBothFromLi4; } - } else if ((he3Hadcand.flagsHe3 & ParticleFlags::kFromHypertriton) && (he3Hadcand.flagsHad & ParticleFlags::kFromHypertriton)) { + } else if (static_cast(he3Hadcand.flagsHe3 & ParticleFlags::kFromHypertriton) && static_cast(he3Hadcand.flagsHad & ParticleFlags::kFromHypertriton)) { searchForCommonMotherTrack(motherHe3Idxs, motherHadIdxs, mcParticles, motherParticle, he3Hadcand, isMixedPair, H3LPDG); if (!isMixedPair) { @@ -1217,7 +1407,16 @@ struct he3HadronFemto { he3Hadcand.flags |= Flags::kMixedPair; } - if (!settingFillPrimariesAndMixedMc && ((he3Hadcand.flags == Flags::kMixedPair) || he3Hadcand.flags == Flags::kBothPrimaries)) { + LOG(info) << "He3 flags: " << int(he3Hadcand.flagsHe3) << ", Hadron flags: " << int(he3Hadcand.flagsHad) << ", Pair flags: " << int(he3Hadcand.flags); + if (!settingFillPrimariesMc && (he3Hadcand.flags == Flags::kBothPrimaries)) { + continue; + } + + if (!settingFillLi4DaughtersMc && (he3Hadcand.flags == Flags::kBothFromLi4)) { + continue; + } + + if (!settingFillMixedMc && (he3Hadcand.flags == Flags::kMixedPair)) { continue; } @@ -1230,10 +1429,11 @@ struct he3HadronFemto { fillMotherInfoMC(mctrackHe3, mctrackHad, motherParticle, he3Hadcand); filledMothers.push_back(motherParticle.globalIndex()); } - fillHistograms(he3Hadcand, /*isMc*/ true); - auto collision = collisions.rawIteratorAt(he3Hadcand.collisionID); - fillTable(he3Hadcand, collision, /*isMC*/ true); + + CollisionInfo collisionInfo; + collisionInfo.fillFromCollision(collision, collisionSelectionFlag); + fillTable(he3Hadcand, collisionInfo, /*isMC*/ true); } } @@ -1241,241 +1441,227 @@ struct he3HadronFemto { } PROCESS_SWITCH(he3HadronFemto, processMC, "Process MC", false); - void processSameEventPools(const CollisionsFull& collisions, const TrackCandidates& tracks, const aod::AmbiguousTracks& ambiguousTracks, const aod::BCsWithTimestamps& bcs) + void processPurity(const CollisionsFull::iterator& collision, const TrackCandidates& tracks, const aod::BCsWithTimestamps& bcs) { - mGoodCollisions.clear(); - mGoodCollisions.resize(collisions.size(), false); - - for (const auto& collision : collisions) { - if (selectCollision(collision, bcs)) { - mGoodCollisions[collision.globalIndex()] = true; - } - } - - mSvPoolCreator.clearPools(); - mSvPoolCreator.fillBC2Coll(collisions, bcs); - - for (const auto& track : tracks) { - - mQaRegistry.fill(HIST("hTrackSel"), Selections::kNoCuts); - if (!selectTrack(track, Species::kHad)) // specific he3 cuts skipped here, might need to refactor this - continue; - mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); - - bool selHad = selectionPIDHadron(track); - bool selHe = selectionPIDHe3(track); - if ((!selHad && !selHe) || (selHad && selHe)) { - continue; - } - mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); - - int pdgHypo = selHe ? He3PDG : ProtonPDG; - - mSvPoolCreator.appendTrackCand(track, collisions, pdgHypo, ambiguousTracks, bcs); - } - - mTrackPairs = mSvPoolCreator.getSVCandPool(collisions, true); - if (mTrackPairs.size() == 0) { - mQaRegistry.fill(HIST("hEmptyPool"), 1); + uint32_t collisionSelectionFlag = 0; // dummy, purity study does not fill a tree + if (!selectCollision(collision, bcs, collisionSelectionFlag)) { return; } - mQaRegistry.fill(HIST("hEmptyPool"), 0); - - fillPairs(collisions, tracks, /*isMixedEvent*/ false); - } - PROCESS_SWITCH(he3HadronFemto, processSameEventPools, "Process Same event pools", false); - - void processMcPools(const CollisionsFullMC& collisions, const TrackCandidatesMC& tracks, const aod::AmbiguousTracks& ambiguousTracks, const aod::BCsWithTimestamps& bcs, const aod::McParticles& mcParticles, const aod::McTrackLabels& mcTrackLabels) - { - std::vector filledMothers; - - mGoodCollisions.clear(); - mGoodCollisions.resize(collisions.size(), false); - - for (const auto& collision : collisions) { - if (selectCollision(collision, bcs)) { - mGoodCollisions[collision.globalIndex()] = true; - } - } - - mSvPoolCreator.clearPools(); - mSvPoolCreator.fillBC2Coll(collisions, bcs); for (const auto& track : tracks) { - mQaRegistry.fill(HIST("hTrackSel"), Selections::kNoCuts); - if (!selectTrack(track, Species::kHad)) // specific he3 cuts skipped here, might need to refactor this - continue; - mQaRegistry.fill(HIST("hTrackSel"), Selections::kTrackCuts); - - bool selHad = selectionPIDHadron(track); - bool selHe = selectionPIDHe3(track); - if ((!selHad && !selHe) || (selHad && selHe)) + if (!selectTrack(track, Species::kHad)) { continue; - mQaRegistry.fill(HIST("hTrackSel"), Selections::kPID); - - int pdgHypo = selHe ? He3PDG : ProtonPDG; - - mSvPoolCreator.appendTrackCand(track, collisions, pdgHypo, ambiguousTracks, bcs); - } - - auto& svPool = mSvPoolCreator.getSVCandPool(collisions, true); - if (svPool.size() == 0) { - mQaRegistry.fill(HIST("hEmptyPool"), 1); - return; - } - mQaRegistry.fill(HIST("hEmptyPool"), 0); - - for (const auto& svCand : svPool) { - auto heTrack = tracks.rawIteratorAt(svCand.tr0Idx); - auto prTrack = tracks.rawIteratorAt(svCand.tr1Idx); - auto heTrackLabel = mcTrackLabels.rawIteratorAt(svCand.tr0Idx); - auto prTrackLabel = mcTrackLabels.rawIteratorAt(svCand.tr1Idx); - auto collBracket = svCand.collBracket; - - if (!heTrackLabel.has_mcParticle() || !prTrackLabel.has_mcParticle()) { - continue; - } - - auto mctrackHe3 = heTrackLabel.mcParticle_as(); - auto mctrackHad = prTrackLabel.mcParticle_as(); - - if (std::abs(mctrackHe3.pdgCode()) != He3PDG || std::abs(mctrackHad.pdgCode()) != ProtonPDG || !mctrackHe3.has_mothers() || !mctrackHad.has_mothers()) { - continue; - } - - for (const auto& mothertrackHe : mctrackHe3.mothers_as()) { - for (const auto& mothertrackHad : mctrackHad.mothers_as()) { - - if (mothertrackHe.globalIndex() != mothertrackHad.globalIndex() || std::abs(mothertrackHe.pdgCode()) != Li4PDG || std::abs(mothertrackHe.y()) > 1) { - continue; - } - - He3HadCandidate he3Hadcand; - if (!fillCandidateInfo(heTrack, prTrack, collBracket, collisions, he3Hadcand, tracks, /*mix*/ false)) { - continue; - } - fillCandidateInfoMC(mctrackHe3, mctrackHad, he3Hadcand); - fillMotherInfoMC(mctrackHe3, mctrackHad, mothertrackHe, he3Hadcand); - fillHistograms(he3Hadcand); - auto collision = collisions.rawIteratorAt(he3Hadcand.collisionID); - fillTable(he3Hadcand, collision, /*isMC*/ true); - filledMothers.push_back(mothertrackHe.globalIndex()); - } } - } - - fillMcParticles(collisions, mcParticles, filledMothers); - } - PROCESS_SWITCH(he3HadronFemto, processMcPools, "Process MC pools", false); - - void processPurity(const CollisionsFull::iterator& collision, const TrackCandidates& tracks, const aod::BCsWithTimestamps& bcs) - { - if (!selectCollision(collision, bcs)) - return; - - for (const auto& track : tracks) { - - if (!selectTrack(track, Species::kHad)) - continue; const float itsNSigmaHad = settingHadPDGCode == PDG_t::kProton ? mResponseITS.nSigmaITS(track.itsClusterSizes(), track.p(), track.eta()) : mResponseITS.nSigmaITS(track.itsClusterSizes(), track.p(), track.eta()); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronITS_preselection"), track.sign() * track.pt(), itsNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronITS_preselection"), track.sign() * track.pt(), itsNSigmaHad, collision.centFT0C()); - if (selectDcaNsigmaCut(track, Species::kHad) && (itsNSigmaHad > settingCutNsigmaITSHad)) { + if (selectDcaNsigmaCut(track.pt(), track.dcaXY(), track.dcaZ(), Species::kHad) && (itsNSigmaHad > cutSettings.settingCutNsigmaITSHad)) { mQaRegistry.fill(HIST("Had/hHadronPt"), track.sign() * track.pt()); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronITS"), track.sign() * track.pt(), itsNSigmaHad); + mQaRegistry.fill(HIST("Had/hDCAxyHad"), track.dcaXY()); + mQaRegistry.fill(HIST("Had/hDCAzHad"), track.dcaZ()); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronITS"), track.sign() * track.pt(), itsNSigmaHad, collision.centFT0C()); const float tpcNSigmaHad = computeTPCNSigmaHadron(track); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_preselection"), track.sign() * track.pt(), tpcNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_preselection"), track.sign() * track.pt(), tpcNSigmaHad, collision.centFT0C()); if (track.hasTOF()) { const float tofNSigmaHad = computeTOFNSigmaHadron(track); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_preselection"), track.sign() * track.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_preselection"), track.sign() * track.pt(), tofNSigmaHad, collision.centFT0C()); } } - const float ptHe3Corrected = correctPtHe3TrackedAsTriton(track); + const float ptHe3Corrected = correctPtHe3TrackedAsTriton(track.pt(), track.pidForTracking()); const float itsNSigmaHe3 = mResponseITS.nSigmaITS(track.itsClusterSizes(), 2 * track.p(), track.eta()); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS_preselection"), ptHe3Corrected, itsNSigmaHe3); + mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS_preselection"), ptHe3Corrected, itsNSigmaHe3, collision.centFT0C()); - if (!selectTrack(track, Species::kHe3) || !selectDcaNsigmaCut(track, Species::kHe3) || (itsNSigmaHe3 < settingCutNsigmaITSHe3)) + if (!selectTrack(track, Species::kHe3) || !selectDcaNsigmaCut(ptHe3Corrected, track.dcaXY(), track.dcaZ(), Species::kHe3) || (itsNSigmaHe3 < cutSettings.settingCutNsigmaITSHe3)) { continue; + } mQaRegistry.fill(HIST("He3/hHe3Pt"), track.sign() * ptHe3Corrected); mQaRegistry.fill(HIST("He3/hDCAxyHe3"), track.dcaXY()); mQaRegistry.fill(HIST("He3/hDCAzHe3"), track.dcaZ()); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS"), track.sign() * ptHe3Corrected, itsNSigmaHe3); + mQaRegistry.fill(HIST("He3/h2NsigmaHe3ITS"), track.sign() * ptHe3Corrected, itsNSigmaHe3, collision.centFT0C()); - bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; - float correctedTPCinnerParam = (heliumPID && settingCompensatePIDinTracking) ? track.tpcInnerParam() / 2.f : track.tpcInnerParam(); - if (correctedTPCinnerParam < settingCutRigidityMinHe3) { + const float correctedTPCinnerParam = correctTpcInnerParamHe3(track.tpcInnerParam(), track.pidForTracking()); + if (correctedTPCinnerParam < cutSettings.settingCutRigidityMinHe3) { continue; } - const float tpcNSigmaHe3 = computeNSigmaHe3(track); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC_preselection"), track.sign() * ptHe3Corrected, tpcNSigmaHe3); + const float tpcNSigmaHe3 = computeNSigmaHe3(correctedTPCinnerParam, track.tpcSignal(), /*applyCorrection*/ true); + mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC_preselection"), track.sign() * ptHe3Corrected, tpcNSigmaHe3, collision.centFT0C()); } } PROCESS_SWITCH(he3HadronFemto, processPurity, "Process for purity studies", false); void processPurityMc(const CollisionsFullMC::iterator& collision, const TrackCandidatesMC& tracks, const aod::BCsWithTimestamps& bcs, const aod::McParticles& /*mcParticles*/, const aod::McTrackLabels& /*mcTrackLabels*/) { - if (!selectCollision(collision, bcs)) + uint32_t collisionSelectionFlag = 0; // dummy, purity study does not fill a tree + if (!selectCollision(collision, bcs, collisionSelectionFlag)) { return; + } for (const auto& track : tracks) { - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } const auto& particle = track.mcParticle_as(); - if (!selectTrack(track, Species::kHad)) + if (!selectTrack(track, Species::kHad)) { continue; + } - if (selectDcaNsigmaCut(track, Species::kHad)) { + if (selectDcaNsigmaCut(track.pt(), track.dcaXY(), track.dcaZ(), Species::kHad)) { mQaRegistry.fill(HIST("Had/hHadronPt"), track.pt()); const float tpcNSigmaHad = computeTPCNSigmaHadron(track); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_preselection"), track.sign() * track.pt(), tpcNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_preselection"), track.sign() * track.pt(), tpcNSigmaHad, collision.centFT0C()); if (std::abs(particle.pdgCode()) != settingHadPDGCode) { - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_mcBackground"), track.sign() * track.pt(), tpcNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_mcBackground"), track.sign() * track.pt(), tpcNSigmaHad, collision.centFT0C()); } else { - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_mcSignal"), track.sign() * track.pt(), tpcNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTPC_mcSignal"), track.sign() * track.pt(), tpcNSigmaHad, collision.centFT0C()); } if (track.hasTOF()) { const float tofNSigmaHad = computeTOFNSigmaHadron(track); - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_preselection"), track.sign() * track.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_preselection"), track.sign() * track.pt(), tofNSigmaHad, collision.centFT0C()); if (std::abs(particle.pdgCode()) != settingHadPDGCode) { - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_mcBackground"), track.sign() * track.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_mcBackground"), track.sign() * track.pt(), tofNSigmaHad, collision.centFT0C()); } else { - mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_mcSignal"), track.sign() * track.pt(), tofNSigmaHad); + mQaRegistry.fill(HIST("Had/h2NsigmaHadronTOF_mcSignal"), track.sign() * track.pt(), tofNSigmaHad, collision.centFT0C()); } } } - if (!selectTrack(track, Species::kHe3) || !selectDcaNsigmaCut(track, Species::kHe3)) + const float ptHe3Corrected = correctPtHe3TrackedAsTriton(track.pt(), track.pidForTracking()); + if (!selectTrack(track, Species::kHe3) || !selectDcaNsigmaCut(ptHe3Corrected, track.dcaXY(), track.dcaZ(), Species::kHe3)) { continue; + } - mQaRegistry.fill(HIST("He3/hHe3Pt"), track.pt() * 2.f); + mQaRegistry.fill(HIST("He3/hHe3Pt"), ptHe3Corrected); mQaRegistry.fill(HIST("He3/hDCAxyHe3"), track.dcaXY()); mQaRegistry.fill(HIST("He3/hDCAzHe3"), track.dcaZ()); - bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; - float correctedTPCinnerParam = (heliumPID && settingCompensatePIDinTracking) ? track.tpcInnerParam() / 2.f : track.tpcInnerParam(); - if (correctedTPCinnerParam < settingCutRigidityMinHe3) { + const float correctedTPCinnerParam = correctTpcInnerParamHe3(track.tpcInnerParam(), track.pidForTracking()); + if (correctedTPCinnerParam < cutSettings.settingCutRigidityMinHe3) { continue; } - const float nSigmaHe3 = computeNSigmaHe3(track); - mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC_preselection"), track.sign() * 2 * track.pt(), nSigmaHe3); + const float nSigmaHe3 = computeNSigmaHe3(correctedTPCinnerParam, track.tpcSignal(), /*applyCorrection*/ true); + mQaRegistry.fill(HIST("He3/h2NsigmaHe3TPC_preselection"), track.sign() * ptHe3Corrected, nSigmaHe3, collision.centFT0C()); } } PROCESS_SWITCH(he3HadronFemto, processPurityMc, "Process for purity studies mc", false); + + void processEventLossMC(McCollisionMults::iterator const& mcCollision, soa::SmallGroups const& collisions, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles) + { + if (std::abs(mcCollision.posZ()) > cutSettings.settingCutVertex) { + return; + } + + //////////// Event loss estimation via impact parameter and multiplicity by MCFT0C + hEvtMC->Fill(0); + hImpactParamGen->Fill(mcCollision.impactParameter()); + hGenEventsNchEta05->Fill(mcCollision.multMCNParticlesEta05(), 0); + hGenEventsNchEta08->Fill(mcCollision.multMCNParticlesEta08(), 0); + + if (collisions.size() == 0) { + hEvtMC->Fill(2); + } + + bool atLeastOneRecoEvt = false; + auto centralityFT0C = -999.; + int biggestNContribs = -1; + + for (const auto& col : collisions) { + + uint32_t collisionSelectionFlag = 0; + // isMC == true only fills the collision vectors, which are not used in this workflow + if (!selectCollision(col, bcs, collisionSelectionFlag)) { + continue; + } + + // In case of multiple reconstructed collisions associated to the same generated one, only consider the one with the biggest number of contributors + if (biggestNContribs < col.numContrib()) { + biggestNContribs = col.numContrib(); + centralityFT0C = col.centFT0C(); + } + + atLeastOneRecoEvt = true; + hImpactParamReco->Fill(mcCollision.impactParameter()); + hRecoCentrality->Fill(col.centFT0C()); + hRecoCentralityColvsMultiplicityRecoEta05->Fill(col.centFT0C(), mcCollision.multMCNParticlesEta05()); + hRecoCentralityColvsMultiplicityRecoEta08->Fill(col.centFT0C(), mcCollision.multMCNParticlesEta08()); + hRecoCentralityColvsMultiplicityFT0C->Fill(col.centFT0C(), mcCollision.multMCFT0C()); + hRecoCentralityColvsImpactParamReco->Fill(col.centFT0C(), mcCollision.impactParameter()); + } + + if (atLeastOneRecoEvt) { + hEvtMC->Fill(1); + hGenEventsNchEta05->Fill(mcCollision.multMCNParticlesEta05(), 1); + hGenEventsNchEta08->Fill(mcCollision.multMCNParticlesEta08(), 1); + hImpactParamGenOneReco->Fill(mcCollision.impactParameter()); + hGenOneRecoCentrality->Fill(centralityFT0C); + hGenCentralityColvsMultiplicityGenEta05->Fill(centralityFT0C, mcCollision.multMCNParticlesEta05()); + hGenCentralityColvsMultiplicityGenEta08->Fill(centralityFT0C, mcCollision.multMCNParticlesEta08()); + hGenCentralityColvsMultiplicityFT0C->Fill(centralityFT0C, mcCollision.multMCFT0C()); + hGenCentralityColvsImpactParamGen->Fill(centralityFT0C, mcCollision.impactParameter()); + } + + // Construct the Li4 (He3 + hadron) 4-vector from generated daughters by PDG + ROOT::Math::PxPyPzMVector daughHe3, daughHad, mother; + const double hadMass = settingHadPDGCode == PDG_t::kPiPlus ? o2::constants::physics::MassPiPlus : o2::constants::physics::MassProton; + + for (const auto& genParticle : mcParticles) { + if (std::abs(genParticle.y()) > 1) + continue; + if (std::abs(genParticle.pdgCode()) != Li4PDG) + continue; + + auto daughters = genParticle.daughters_as(); + + bool dauHe3 = false, dauHad = false; + int he3Sign = 0, hadSign = 0; + + for (const auto& daughter : daughters) { + if (std::abs(daughter.pdgCode()) == He3PDG) { + dauHe3 = true; + he3Sign = daughter.pdgCode() > 0 ? 1 : -1; + daughHe3 = ROOT::Math::PxPyPzMVector(daughter.px(), daughter.py(), daughter.pz(), o2::constants::physics::MassHelium3); + } else if (std::abs(daughter.pdgCode()) == settingHadPDGCode) { + dauHad = true; + hadSign = daughter.pdgCode() > 0 ? 1 : -1; + daughHad = ROOT::Math::PxPyPzMVector(daughter.px(), daughter.py(), daughter.pz(), hadMass); + } + } + // Only keep the charge combination consistent with a genuine Li4 decay + if (!dauHe3 || !dauHad || (he3Sign * hadSign) < 0) + continue; + + mother = daughHe3 + daughHad; + + hGenLi4BeforeEvtSel->Fill(mother.pt()); + hGenLi4vsImpactParameterBeforeEvtSel->Fill(mother.pt(), mcCollision.impactParameter()); + hGenLi4vsMultiplicityGenEta05BeforeEvtSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta05()); + hGenLi4vsMultiplicityGenEta08BeforeEvtSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta08()); + hGenLi4vsMultiplicityFT0CBeforeEvtSel->Fill(mother.pt(), mcCollision.multMCFT0C()); + + if (atLeastOneRecoEvt) { + hGenLi4AfterSel->Fill(mother.pt()); + hGenLi4vsImpactParameterAfterSel->Fill(mother.pt(), mcCollision.impactParameter()); + hGenLi4vsMultiplicityGenEta05AfterSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta05()); + hGenLi4vsMultiplicityGenEta08AfterSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta08()); + hGenLi4vsMultiplicityFT0CAfterSel->Fill(mother.pt(), mcCollision.multMCFT0C()); + } + } + } + PROCESS_SWITCH(he3HadronFemto, processEventLossMC, "Event loss analysis", false); }; WorkflowSpec defineDataProcessing(const ConfigContext& cfgc) diff --git a/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx b/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx index 9b75803f9fd..293733ad9c5 100644 --- a/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx +++ b/PWGLF/TableProducer/Nuspex/he3LambdaAnalysis.cxx @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -42,8 +43,6 @@ #include #include -#include - #include #include #include @@ -57,7 +56,7 @@ using namespace o2::framework::expressions; using namespace o2::constants::physics; namespace { -constexpr double betheBlochDefault[1][6]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; +constexpr std::array betheBlochDefault[1]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; static const std::vector particleName{"He3"}; o2::base::MatLayerCylSet* matLUT = nullptr; @@ -102,6 +101,9 @@ struct he3LambdaAnalysis { Produces lfHe3V0Collision; Produces lfHe3; Produces lfLambda; + Produces lfK0s; + + Configurable cfgStoreK0s{"cfgStoreK0s", false, "Select and store K0s candidates, including He3-K0s events without a selected Lambda"}; // Configurables for event selection struct : ConfigurableGroup { @@ -123,7 +125,7 @@ struct he3LambdaAnalysis { Configurable dcazMax{"dcazMax", 0.5f, "Maximum He3 DCA z"}; Configurable tpcClusMin{"tpcClusMin", 100, "Minimum He3 TPC clusters"}; Configurable itsClusMin{"itsClusMin", 5, "Minimum He3 ITS clusters"}; - Configurable> betheBlochParams{"betheBlochParams", {betheBlochDefault[0], 1, 6, particleName, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; + Configurable> betheBlochParams{"betheBlochParams", {betheBlochDefault[0].data(), 1, 6, particleName, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for He3"}; } cfgHe3; // Lambda selection criteria @@ -132,6 +134,8 @@ struct he3LambdaAnalysis { Configurable ptMin{"ptMin", 0.5f, "Minimum Lambda pT"}; Configurable ptMax{"ptMax", 10.0f, "Maximum Lambda pT"}; Configurable massWindow{"massWindow", 0.015f, "Lambda mass window"}; + Configurable ctMin{"ctMin", 0.f, "Minimum Lambda proper decay length (cm)"}; + Configurable ctMax{"ctMax", 30.f, "Maximum Lambda proper decay length (cm)"}; Configurable cosPAMin{"cosPAMin", 0.99f, "Minimum Lambda cosPA"}; Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5f, "Maximum Lambda DCA V0 daughters"}; Configurable v0RadiusMin{"v0RadiusMin", 0.5f, "Minimum Lambda V0 radius"}; @@ -141,6 +145,22 @@ struct he3LambdaAnalysis { Configurable pionNSigmaTPCMax{"pionNSigmaTPCMax", 4.0f, "Maximum pion TPC nSigma"}; } cfgLambda; + // K0s selection criteria + struct : ConfigurableGroup { + std::string prefix = "cfgK0s"; + Configurable ptMin{"ptMin", 0.5f, "Minimum K0s pT"}; + Configurable ptMax{"ptMax", 10.f, "Maximum K0s pT"}; + Configurable massWindow{"massWindow", 0.015f, "K0s mass window"}; + Configurable ctMin{"ctMin", 0.f, "Minimum K0s proper decay length (cm)"}; + Configurable ctMax{"ctMax", 20.f, "Maximum K0s proper decay length (cm)"}; + Configurable cosPAMin{"cosPAMin", 0.99f, "Minimum K0s cosPA"}; + Configurable dcaV0DaughtersMax{"dcaV0DaughtersMax", 0.5f, "Maximum K0s DCA V0 daughters"}; + Configurable v0RadiusMin{"v0RadiusMin", 0.5f, "Minimum K0s V0 radius"}; + Configurable v0RadiusMax{"v0RadiusMax", 35.f, "Maximum K0s V0 radius"}; + Configurable tpcNClsMin{"tpcNClsMin", 70, "Minimum TPC clusters for K0s daughters"}; + Configurable pionNSigmaTPCMax{"pionNSigmaTPCMax", 4.f, "Maximum pion TPC nSigma"}; + } cfgK0s; + // Pair selection criteria struct : ConfigurableGroup { std::string prefix = "cfgPair"; @@ -157,7 +177,7 @@ struct he3LambdaAnalysis { Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; } ccdbOptions; - std::array mBBparamsHe; + std::array mBBparamsHe = betheBlochDefault[0]; float mBz = 0.0f; // Magnetic field in T HistogramRegistry mRegistry{"He3LambdaAnalysis"}; int mRunNumber = 0; // Current run number @@ -187,7 +207,7 @@ struct he3LambdaAnalysis { zorroSummary.setObject(zorro.getZorroSummary()); mRegistry.add("hEventSelection", "Event Selection", {HistType::kTH1L, {{6, -.5, 5.5}}}); - std::vector labels{"Total Events", "Sel8 Events", "Z-Vertex OK", "Additional Event Selections", "He3 Candidates Found", "He3 and Lambda Candidates Found"}; + std::vector labels{"Total Events", "Sel8 Events", "Z-Vertex OK", "Additional Event Selections", "He3 Candidates Found", "He3 and V0 Candidates Found"}; for (size_t i = 1; i <= labels.size(); ++i) { mRegistry.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, labels[i - 1].c_str()); } @@ -204,6 +224,8 @@ struct he3LambdaAnalysis { hArmenterosPodolanskiAll = mRegistry.add("hArmenterosPodolanskiAll", "Armenteros-Podolanski All", {HistType::kTH2D, {{100, -1., 1.}, {100, 0., 0.5}}}); hArmenterosPodolanskiSelected = mRegistry.add("hArmenterosPodolanskiSelected", "Armenteros-Podolanski Selected", {HistType::kTH2D, {{100, -1., 1.}, {100, 0., 0.5}}}); + mRegistry.add("hMassK0s", "Selected K0s;pT (GeV/c);mass (GeV/c^{2})", {HistType::kTH2F, {{100, 0., 10.}, {100, MassK0Short - 0.025, MassK0Short + 0.025}}}); + constexpr double ConstituentsMass = o2::constants::physics::MassProton + o2::constants::physics::MassNeutron * 2 + o2::constants::physics::MassSigmaPlus; hInvariantMassUS = mRegistry.add("hInvariantMassUS", "Invariant Mass", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); hInvariantMassLS = mRegistry.add("hInvariantMassLS", "Invariant Mass", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); @@ -299,8 +321,9 @@ struct he3LambdaAnalysis { } mRegistry.get(HIST("hEventSelection"))->Fill(4); // He3 candidates found - // Process Lambda candidates + // Fit each V0 once and evaluate the K0s and Lambda hypotheses independently. std::vector lambdaCandidates; + std::vector k0sCandidates; for (auto const& v0 : v0s) { if (v0.v0Type() != 1) { continue; @@ -308,7 +331,9 @@ struct he3LambdaAnalysis { const auto posTrack = v0.posTrack_as(); const auto negTrack = v0.negTrack_as(); - if (posTrack.tpcNClsFound() < cfgLambda.tpcNClsMin || negTrack.tpcNClsFound() < cfgLambda.tpcNClsMin) { + const bool lambdaTrackQuality = posTrack.tpcNClsFound() >= cfgLambda.tpcNClsMin && negTrack.tpcNClsFound() >= cfgLambda.tpcNClsMin; + const bool k0sTrackQuality = cfgStoreK0s && posTrack.tpcNClsFound() >= cfgK0s.tpcNClsMin && negTrack.tpcNClsFound() >= cfgK0s.tpcNClsMin; + if (!lambdaTrackQuality && !k0sTrackQuality) { continue; // Skip V0s with insufficient TPC clusters } auto trackParPos = getTrackParCov(posTrack); @@ -333,6 +358,49 @@ struct he3LambdaAnalysis { float qt = qtAP(momV0, momPos); hArmenterosPodolanskiAll->Fill(alpha, qt); + const auto sv = fitter.getPCACandidate(0); + const float decayLength = std::hypot(sv[0] - collVtx.x(), sv[1] - collVtx.y(), sv[2] - collVtx.z()); + const float momentum = std::hypot(momV0[0], momV0[1], momV0[2]); + if (momentum <= 0.f || decayLength <= 0.f) { + continue; + } + const float cosPA = ((sv[0] - collVtx.x()) * momV0[0] + (sv[1] - collVtx.y()) * momV0[1] + (sv[2] - collVtx.z()) * momV0[2]) / (decayLength * momentum); + const float dcaDaughters = std::sqrt(fitter.getChi2AtPCACandidate(0)); + const float radius = std::hypot(sv[0], sv[1]); + std::array dcaInfoPos{}, dcaInfoNeg{}; + if (!o2::base::Propagator::Instance()->propagateToDCA(collVtx, trackParPos, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoPos) || + !o2::base::Propagator::Instance()->propagateToDCA(collVtx, trackParNeg, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoNeg)) { + continue; + } + + ROOT::Math::LorentzVector> posPionMom4D(momPos[0], momPos[1], momPos[2], MassPionCharged); + ROOT::Math::LorentzVector> negPionMom4D(momNeg[0], momNeg[1], momNeg[2], MassPionCharged); + const auto k0sMom4D = posPionMom4D + negPionMom4D; + const float ctK0s = decayLength * MassK0Short / momentum; + if (k0sTrackQuality && std::abs(posTrack.tpcNSigmaPi()) <= cfgK0s.pionNSigmaTPCMax && std::abs(negTrack.tpcNSigmaPi()) <= cfgK0s.pionNSigmaTPCMax && + std::abs(k0sMom4D.M() - MassK0Short) <= cfgK0s.massWindow && k0sMom4D.Pt() >= cfgK0s.ptMin && k0sMom4D.Pt() <= cfgK0s.ptMax && + ctK0s >= cfgK0s.ctMin && ctK0s <= cfgK0s.ctMax && cosPA >= cfgK0s.cosPAMin && dcaDaughters <= cfgK0s.dcaV0DaughtersMax && + radius >= cfgK0s.v0RadiusMin && radius <= cfgK0s.v0RadiusMax) { + k0sCandidate candidate; + candidate.momentum.SetCoordinates(k0sMom4D.Pt(), k0sMom4D.Eta(), k0sMom4D.Phi(), MassK0Short); + candidate.mass = k0sMom4D.M(); + candidate.ct = ctK0s; + candidate.cosPA = cosPA; + candidate.dcaV0Daughters = dcaDaughters; + candidate.dcaPosToPV = std::hypot(dcaInfoPos[0], dcaInfoPos[1]); + candidate.dcaNegToPV = std::hypot(dcaInfoNeg[0], dcaInfoNeg[1]); + candidate.v0Radius = radius; + candidate.posPionNSigmaTPC = posTrack.tpcNSigmaPi(); + candidate.negPionNSigmaTPC = negTrack.tpcNSigmaPi(); + k0sCandidates.push_back(candidate); + mRegistry.fill(HIST("hMassK0s"), k0sMom4D.Pt(), candidate.mass); + } + + const float ctLambda = decayLength * MassLambda0 / momentum; + if (!lambdaTrackQuality || ctLambda < cfgLambda.ctMin || ctLambda > cfgLambda.ctMax || cosPA < cfgLambda.cosPAMin || + dcaDaughters > cfgLambda.dcaV0DaughtersMax || radius < cfgLambda.v0RadiusMin || radius > cfgLambda.v0RadiusMax) { + continue; + } bool matter = alpha > 0; const auto& protonTrack = matter ? posTrack : negTrack; const auto& pionTrack = matter ? negTrack : posTrack; @@ -348,37 +416,30 @@ struct he3LambdaAnalysis { auto lambdaMom4D = protonMom4D + pionMom4D; float massLambda = lambdaMom4D.M(); - if (std::abs(massLambda - o2::constants::physics::MassLambda0) > cfgLambda.massWindow) { + if (std::abs(massLambda - o2::constants::physics::MassLambda0) > cfgLambda.massWindow || lambdaMom4D.Pt() < cfgLambda.ptMin || lambdaMom4D.Pt() > cfgLambda.ptMax) { continue; // Skip V0s outside mass window } hArmenterosPodolanskiSelected->Fill(alpha, qt); - std::array dcaInfoProton, dcaInfoPion; - o2::base::Propagator::Instance()->propagateToDCA(collVtx, matter ? trackParPos : trackParNeg, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoProton); - o2::base::Propagator::Instance()->propagateToDCA(collVtx, matter ? trackParNeg : trackParPos, mBz, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dcaInfoPion); - - const auto sv = fitter.getPCACandidate(0); - + const auto& dcaInfoProton = matter ? dcaInfoPos : dcaInfoNeg; + const auto& dcaInfoPion = matter ? dcaInfoNeg : dcaInfoPos; lambdaCandidate candidate; - candidate.momentum.SetCoordinates(lambdaMom4D.Pt(), lambdaMom4D.Eta(), lambdaMom4D.Phi(), o2::constants::physics::MassLambda0); + candidate.momentum.SetCoordinates(lambdaMom4D.Pt(), lambdaMom4D.Eta(), lambdaMom4D.Phi(), MassLambda0); candidate.mass = massLambda; - candidate.cosPA = (sv[0] - collVtx.x()) * lambdaMom4D.Px() + - (sv[1] - collVtx.y()) * lambdaMom4D.Py() + - (sv[2] - collVtx.z()) * lambdaMom4D.Pz(); - candidate.cosPA /= std::hypot(sv[0] - collVtx.x(), sv[1] - collVtx.y(), sv[2] - collVtx.z()) * lambdaMom4D.P(); - candidate.dcaV0Daughters = std::sqrt(fitter.getChi2AtPCACandidate(0)); + candidate.cosPA = cosPA; + candidate.dcaV0Daughters = dcaDaughters; candidate.dcaProtonToPV = std::hypot(dcaInfoProton[0], dcaInfoProton[1]); candidate.dcaPionToPV = std::hypot(dcaInfoPion[0], dcaInfoPion[1]); - candidate.v0Radius = std::hypot(sv[0], sv[1]); + candidate.v0Radius = radius; candidate.protonNSigmaTPC = protonTrack.tpcNSigmaPr(); candidate.pionNSigmaTPC = pionTrack.tpcNSigmaPi(); candidate.sign = matter ? 1 : -1; // Positive sign for Lambda, negative for anti-Lambda lambdaCandidates.push_back(candidate); } - if (lambdaCandidates.empty()) { - return; // No valid Lambda candidates found + if (lambdaCandidates.empty() && (!cfgStoreK0s || k0sCandidates.empty())) { + return; // Require a Lambda, or a K0s when cfgStoreK0s is enabled } - mRegistry.get(HIST("hEventSelection"))->Fill(5); // He3 and Lambda candidates found + mRegistry.get(HIST("hEventSelection"))->Fill(5); // He3 and V0 candidates found mRegistry.get(HIST("hCentralitySelected"))->Fill(collision.centFT0C()); // Fill output tables @@ -387,6 +448,10 @@ struct he3LambdaAnalysis { lfHe3(lfHe3V0Collision.lastIndex(), he3.momentum.Pt(), he3.momentum.Eta(), he3.momentum.Phi(), he3.dcaXY, he3.dcaZ, he3.tpcNClsFound, he3.tpcNClsPID, he3.itsClusterSizes, he3.nSigmaTPC, he3.sign); } + for (const auto& k0s : k0sCandidates) { + lfK0s(lfHe3V0Collision.lastIndex(), k0s.momentum.Pt(), k0s.momentum.Eta(), k0s.momentum.Phi(), + k0s.mass, k0s.ct, k0s.cosPA, k0s.dcaV0Daughters, k0s.dcaPosToPV, k0s.dcaNegToPV, k0s.v0Radius, k0s.posPionNSigmaTPC, k0s.negPionNSigmaTPC); + } for (const auto& lambda : lambdaCandidates) { lfLambda(lfHe3V0Collision.lastIndex(), lambda.momentum.Pt(), lambda.momentum.Eta(), lambda.momentum.Phi(), lambda.mass, lambda.cosPA, lambda.dcaV0Daughters, lambda.dcaProtonToPV, lambda.dcaPionToPV, lambda.v0Radius, lambda.protonNSigmaTPC, lambda.pionNSigmaTPC, lambda.sign); diff --git a/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx b/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx index f4ddba0fdbe..50e450b7936 100644 --- a/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx +++ b/PWGLF/TableProducer/Nuspex/hyhe4builder.cxx @@ -47,6 +47,7 @@ #include #include #include +#include using namespace std; using namespace o2; diff --git a/PWGLF/TableProducer/Nuspex/hypKfRecoTask.cxx b/PWGLF/TableProducer/Nuspex/hypKfRecoTask.cxx index 7755a77bc8a..c164f9233ca 100644 --- a/PWGLF/TableProducer/Nuspex/hypKfRecoTask.cxx +++ b/PWGLF/TableProducer/Nuspex/hypKfRecoTask.cxx @@ -20,6 +20,7 @@ #include "PWGLF/DataModel/LFHypernucleiKfTables.h" #include "PWGLF/DataModel/LFPIDTOFGenericTables.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/MetadataHelper.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" @@ -198,7 +199,7 @@ struct DaughterParticle { std::array betheParams; std::array trkSettings; bool active; - DaughterParticle(std::string name_, int pdgCode_, double mass_, int charge_, LabeledArray bethe, LabeledArray settings) : name(name_), pdgCode(pdgCode_), charge(charge_), mass(mass_), active(false) + DaughterParticle(const std::string& name_, int pdgCode_, double mass_, int charge_, const LabeledArray& bethe, const LabeledArray& settings) : name(name_), pdgCode(pdgCode_), charge(charge_), mass(mass_), active(false) { for (unsigned int i = 0; i < betheParams.size(); i++) { betheParams[i] = bethe.get(name, i); @@ -220,19 +221,19 @@ struct HyperNucleus { bool active, savePrimary; std::vector daughters, daughterTrackSigns, v0DaughterVec; std::vector primSettings; - HyperNucleus(std::string name_, int pdgCode_, bool active_, std::vector daughters_, std::vector daughterTrackSigns_, std::vector v0DaughterVec_, LabeledArray primSettings_) : pdgCode(pdgCode_), active(active_), savePrimary(active_) + HyperNucleus(const std::string& name_, int pdgCode_, bool active_, const std::vector& daughters_, const std::vector& daughterTrackSigns_, const std::vector& v0DaughterVec_, const LabeledArray& primSettings_) : pdgCode(pdgCode_), active(active_), savePrimary(active_) { - init(name_, daughters_, daughterTrackSigns_, v0DaughterVec_); + init(std::move(name_), std::move(daughters_), std::move(daughterTrackSigns_), std::move(v0DaughterVec_)); for (unsigned int i = 0; i < nSelPrim; i++) { primSettings.push_back(primSettings_.get(name, i)); } } - HyperNucleus(std::string name_, int pdgCode_, bool active_, int hypDaughter, std::vector daughters_, std::vector daughterTrackSigns_) : pdgCode(pdgCode_), active(active_), savePrimary(active_) + HyperNucleus(const std::string& name_, int pdgCode_, bool active_, int hypDaughter, const std::vector& daughters_, const std::vector& daughterTrackSigns_) : pdgCode(pdgCode_), active(active_), savePrimary(active_) { daughters.push_back(hypDaughter); - init(name_, daughters_, daughterTrackSigns_); + init(std::move(name_), std::move(daughters_), std::move(daughterTrackSigns_)); } - void init(std::string name_, std::vector daughters_, std::vector daughterTrackSigns_, std::vector v0DaughterVec_ = {}) + void init(const std::string& name_, const std::vector& daughters_, const std::vector& daughterTrackSigns_, const std::vector& v0DaughterVec_ = {}) { name = TString(name_); for (const int& d : daughters_) @@ -262,11 +263,11 @@ struct DaughterKf { int64_t daughterTrackId; int id, species, sign, hypNucId; KFParticle daughterKfp; - float dcaToPv, dcaToPvXY, dcaToPvZ, tpcNsigma, tpcNsigmaNLP, tpcNsigmaNHP; + float dcaToPv, dcaToPvXY, dcaToPvZ, tpcNsigma, itsNsigma; bool active; std::vector vtx; - DaughterKf(int species_, int64_t daughterTrackId_, int sign_, std::vector vtx_, float tpcNsigma_, float tpcNsigmaNLP_, float tpcNsigmaNHP_) : daughterTrackId(daughterTrackId_), id(uniqueId++), species(species_), sign(sign_), hypNucId(-1), tpcNsigma(tpcNsigma_), tpcNsigmaNLP(tpcNsigmaNLP_), tpcNsigmaNHP(tpcNsigmaNHP_), vtx(vtx_) {} - void addKfp(KFParticle daughterKfp_) + DaughterKf(int species_, int64_t daughterTrackId_, int sign_, std::vector vtx_, float tpcNsigma_, float itsNsigma_) : daughterTrackId(daughterTrackId_), id(uniqueId++), species(species_), sign(sign_), hypNucId(-1), tpcNsigma(tpcNsigma_), itsNsigma(itsNsigma_), vtx(std::move(vtx_)) {} + void addKfp(const KFParticle& daughterKfp_) { daughterKfp = daughterKfp_; dcaToPvXY = daughterKfp.GetDistanceFromVertexXY(&vtx[0]); @@ -290,7 +291,7 @@ struct HyperNucCandidate { bool mcTrue, isPhysPrimary, isPrimaryCandidate, isSecondaryCandidate, isUsedSecondary; int64_t mcParticleId; int tableId; - HyperNucCandidate(int species_, HyperNucCandidate* hypNucDaughter_, std::vector daughters_) : species(species_), hypNucDaughter(hypNucDaughter_), devToPvXY(-999), dcaToPvXY(-999), dcaToPvZ(-999), dcaToVtxXY(-999), dcaToVtxZ(-999), chi2(-999), itsMeanClsSize(-1), mcTrue(false), isPhysPrimary(false), isPrimaryCandidate(false), isSecondaryCandidate(false), isUsedSecondary(false), mcParticleId(-1), tableId(-1) + HyperNucCandidate(int species_, HyperNucCandidate* hypNucDaughter_, const std::vector& daughters_) : species(species_), hypNucDaughter(hypNucDaughter_), devToPvXY(-999), dcaToPvXY(-999), dcaToPvZ(-999), dcaToVtxXY(-999), dcaToVtxZ(-999), chi2(-999), itsMeanClsSize(-1), mcTrue(false), isPhysPrimary(false), isPrimaryCandidate(false), isSecondaryCandidate(false), isUsedSecondary(false), mcParticleId(-1), tableId(-1) { for (const auto& d : daughters_) daughters.push_back(d); @@ -402,7 +403,7 @@ struct HyperNucCandidate { } return calcSubDaughterMass(daughters.at(d1)->daughterKfp, hypNucDaughter->daughters.at(d2)->daughterKfp); } - float calcSubDaughterMass(KFParticle d1, KFParticle d2) + float calcSubDaughterMass(const KFParticle& d1, const KFParticle& d2) { KFParticle subDaughter; subDaughter.SetConstructMethod(2); @@ -475,7 +476,7 @@ struct DaughterCombinations { int nVecs, nCombinations; bool end; std::vector nonV0daughters; - DaughterCombinations(std::vector*>& vecs, std::vector nonV0daughters_) : nVecs(0), nCombinations(1), end(false), nonV0daughters(nonV0daughters_) + DaughterCombinations(std::vector*>& vecs, std::vector nonV0daughters_) : nVecs(0), nCombinations(1), end(false), nonV0daughters(std::move(nonV0daughters_)) { for (const auto& vec : vecs) { nVecs++; @@ -593,7 +594,7 @@ struct HypKfRecoTask { // define histogram axes const AxisSpec axisMagField{10, -10., 10., "magnetic field"}; - const AxisSpec axisNev{3, 0., 3., "Number of events"}; + const AxisSpec axisNev{5, 0., 5., "Number of events"}; const AxisSpec axisRigidity{4000, -10., 10., "#it{p}^{TPC}/#it{z}"}; const AxisSpec axisdEdx{2000, 0, 2000, "d#it{E}/d#it{x}"}; const AxisSpec axisInvMass{1000, 1, 6, "inv mass"}; @@ -644,12 +645,8 @@ struct HypKfRecoTask { const float itsNsigma = getITSnSigma(track, daughterParticles.at(i)); if (daughterParticles.at(i).trkSettings[kMaxITSnSigma] >= 0 && std::abs(itsNsigma) > daughterParticles.at(i).trkSettings[kMaxITSnSigma]) continue; - float tpcNsigmaNlp = NoVal; - if (daughterParticles.at(i).name == "alpha") { - tpcNsigmaNlp = getTPCnSigma(track, daughterParticles.at(i - 1)); - } filldedx(track, i); - foundDaughterKfs.at(i).push_back(DaughterKf(i, track.globalIndex(), track.sign(), primVtx, tpcNsigma, tpcNsigmaNlp, itsNsigma)); + foundDaughterKfs.at(i).push_back(DaughterKf(i, track.globalIndex(), track.sign(), primVtx, tpcNsigma, itsNsigma)); } } // track loop } @@ -906,9 +903,9 @@ struct HypKfRecoTask { if (!trackIndices.getIndex(daughter->species, daughterTrackId, trackTableId)) { const auto& track = tracks.rawIteratorAt(daughterTrackId); outputTrackTable( - daughter->species * track.sign(), track.pt(), track.eta(), track.phi(), daughter->dcaToPvXY, daughter->dcaToPvZ, track.tpcNClsFound(), track.tpcChi2NCl(), - track.itsClusterSizes(), track.itsChi2NCl(), getRigidity(track), track.tpcSignal(), daughter->tpcNsigma, daughter->tpcNsigmaNHP, daughter->tpcNsigmaNLP, - getMass2(track), track.isPVContributor()); + daughter->species * track.sign(), track.pt(), track.eta(), track.phi(), daughter->dcaToPvXY, daughter->dcaToPvZ, track.tpcChi2NCl(), + track.itsClusterSizes(), track.itsChi2NCl(), getRigidity(track), track.tpcSignal(), track.tpcNClsFound(), track.tpcNClsPID(), + track.tpcNClsCrossedRows(), daughter->tpcNsigma, daughter->itsNsigma, track.pidForTracking(), getMass2(track), track.isPVContributor()); trackTableId = outputTrackTable.lastIndex(); trackIndices.add(daughter->species, daughterTrackId, trackTableId); } @@ -1006,7 +1003,8 @@ struct HypKfRecoTask { initCollision(collision); if (!collision.has_mcCollision() || !mcCollInfos.at(collision.mcCollisionId()).passedEvSel) continue; - + if (!collPassedEvSel) + continue; const uint64_t collIdx = collision.globalIndex(); auto tracksByColl = tracksColl.sliceBy(perCollision, collIdx); auto v0TableThisCollision = V0s.sliceBy(perCollisionV0, collIdx); @@ -1107,12 +1105,34 @@ struct HypKfRecoTask { trackIndices.clear(); collHasCandidate = false; collHasMcTrueCandidate = false; + collPassedEvSel = true; histos.fill(HIST("histMagField"), dBz); histos.fill(HIST("histNev"), 0.5); - collPassedEvSel = collision.sel8() && std::abs(collision.posZ()) < cfgVtxCutZ; - occupancy = collision.trackOccupancyInTimeRange(); + if (!collision.sel8()) { + collPassedEvSel = false; + } if (collPassedEvSel) { histos.fill(HIST("histNev"), 1.5); + } + if (std::abs(collision.posZ()) > cfgVtxCutZ) { + collPassedEvSel = false; + } + if (collPassedEvSel) { + histos.fill(HIST("histNev"), 2.5); + } + if (!collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + collPassedEvSel = false; + } + if (collPassedEvSel) { + histos.fill(HIST("histNev"), 3.5); + } + if (!collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + collPassedEvSel = false; + } + + occupancy = collision.trackOccupancyInTimeRange(); + if (collPassedEvSel) { + histos.fill(HIST("histNev"), 4.5); histos.fill(HIST("histVtxZ"), collision.posZ()); histos.fill(HIST("histCentFT0A"), collision.centFT0A()); histos.fill(HIST("histCentFT0C"), collision.centFT0C()); @@ -1311,7 +1331,7 @@ struct HypKfRecoTask { } //---------------------------------------------------------------------------------------------------------------- - int getHypDaughterVec(unsigned int cascade, LabeledArray cfg) + int getHypDaughterVec(unsigned int cascade, const LabeledArray& cfg) { std::string daughter = cfg.get(cascade, 0u); if (std::find(hyperNucNames.begin(), hyperNucNames.end(), daughter) == hyperNucNames.end()) @@ -1319,7 +1339,7 @@ struct HypKfRecoTask { return std::find(hyperNucNames.begin(), hyperNucNames.end(), daughter) - hyperNucNames.begin(); } //---------------------------------------------------------------------------------------------------------------- - std::vector getDaughterVec(unsigned int hypNuc, LabeledArray cfg) + std::vector getDaughterVec(unsigned int hypNuc, const LabeledArray& cfg) { std::vector vec; for (unsigned int i = kD1; i <= kD4; i++) { @@ -1332,7 +1352,7 @@ struct HypKfRecoTask { } //---------------------------------------------------------------------------------------------------------------- - std::vector getDaughterSignVec(unsigned int hypNuc, LabeledArray cfg) + std::vector getDaughterSignVec(unsigned int hypNuc, const LabeledArray& cfg) { std::vector vec; std::string signs = cfg.get(hypNuc, "daughterSigns"); @@ -1345,7 +1365,7 @@ struct HypKfRecoTask { return vec; } //---------------------------------------------------------------------------------------------------------------- - std::vector getV0DaughterVec(unsigned int hypNuc, LabeledArray cfg) + std::vector getV0DaughterVec(unsigned int hypNuc, const LabeledArray& cfg) { std::vector vec; std::string v0ds = cfg.get(hypNuc, "useV0for"); diff --git a/PWGLF/TableProducer/Nuspex/hypKfTreeCreator.cxx b/PWGLF/TableProducer/Nuspex/hypKfTreeCreator.cxx index 3a3a2ab3463..27d7be47253 100644 --- a/PWGLF/TableProducer/Nuspex/hypKfTreeCreator.cxx +++ b/PWGLF/TableProducer/Nuspex/hypKfTreeCreator.cxx @@ -45,11 +45,12 @@ enum Decays { kTwoBody = 2, kThreeBody = 3 }; struct TrackProperties { - TrackProperties() : x(0), y(0), z(0), px(0), py(0), pz(0), tpcNcls(0), itsNcls(0), tpcChi2(0), itsChi2(0), itsMeanClsSizeL(0), rigidity(0), tpcSignal(0), tpcNsigma(0), tpcNsigmaNhp(0), tpcNsigmaNlp(0), tofMass(0), dcaXY(0), dcaZ(0), isPvContributor(0), subMass(0) {} + TrackProperties() : x(0), y(0), z(0), px(0), py(0), pz(0), tpcNcls(0), itsNcls(0), tpcChi2(0), itsChi2(0), itsMeanClsSizeL(0), rigidity(0), tpcSignal(0), tpcNsigma(0), itsNsigma(0), tpcNclsPid(0), tpcNclsCr(0), pidForTrk(0), tofMass(0), dcaXY(0), dcaZ(0), isPvContributor(0), subMass(0) {} float x, y, z, px, py, pz; uint8_t tpcNcls, itsNcls; float tpcChi2, itsChi2, itsMeanClsSizeL; - float rigidity, tpcSignal, tpcNsigma, tpcNsigmaNhp, tpcNsigmaNlp; + float rigidity, tpcSignal, tpcNsigma, itsNsigma, tpcNclsPid, tpcNclsCr; + uint32_t pidForTrk; float tofMass, dcaXY, dcaZ; bool isPvContributor; float subMass; @@ -125,8 +126,10 @@ DECLARE_SOA_COLUMN(D1ITSmeanClsSizeL, d1ITSmeanClsSizeL, float); DECLARE_SOA_COLUMN(D1Rigidity, d1Rigidity, float); DECLARE_SOA_COLUMN(D1TPCsignal, d1TPCsignal, float); DECLARE_SOA_COLUMN(D1TPCnSigma, d1TPCnSigma, float); -DECLARE_SOA_COLUMN(D1TPCnSigmaNhp, d1TPCnSigmaNhp, float); -DECLARE_SOA_COLUMN(D1TPCnSigmaNlp, d1TPCnSigmaNlp, float); +DECLARE_SOA_COLUMN(D1TPCnClsPid, d1TPCnClsPid, uint8_t); +DECLARE_SOA_COLUMN(D1TPCnClsCr, d1TPCnClsCr, uint8_t); +DECLARE_SOA_COLUMN(D1ITSnSigma, d1ITSnSigma, float); +DECLARE_SOA_COLUMN(D1PidForTrk, d1PidForTrk, uint32_t); DECLARE_SOA_COLUMN(D1TOFmass, d1TOFmass, float); DECLARE_SOA_COLUMN(D1DcaXY, d1DcaXY, float); DECLARE_SOA_COLUMN(D1DcaZ, d1DcaZ, float); @@ -145,8 +148,10 @@ DECLARE_SOA_COLUMN(D2ITSmeanClsSizeL, d2ITSmeanClsSizeL, float); DECLARE_SOA_COLUMN(D2Rigidity, d2Rigidity, float); DECLARE_SOA_COLUMN(D2TPCsignal, d2TPCsignal, float); DECLARE_SOA_COLUMN(D2TPCnSigma, d2TPCnSigma, float); -DECLARE_SOA_COLUMN(D2TPCnSigmaNhp, d2TPCnSigmaNhp, float); -DECLARE_SOA_COLUMN(D2TPCnSigmaNlp, d2TPCnSigmaNlp, float); +DECLARE_SOA_COLUMN(D2TPCnClsPid, d2TPCnClsPid, uint8_t); +DECLARE_SOA_COLUMN(D2TPCnClsCr, d2TPCnClsCr, uint8_t); +DECLARE_SOA_COLUMN(D2ITSnSigma, d2ITSnSigma, float); +DECLARE_SOA_COLUMN(D2PidForTrk, d2PidForTrk, uint32_t); DECLARE_SOA_COLUMN(D2TOFmass, d2TOFmass, float); DECLARE_SOA_COLUMN(D2DcaXY, d2DcaXY, float); DECLARE_SOA_COLUMN(D2DcaZ, d2DcaZ, float); @@ -165,8 +170,10 @@ DECLARE_SOA_COLUMN(D3ITSmeanClsSizeL, d3ITSmeanClsSizeL, float); DECLARE_SOA_COLUMN(D3Rigidity, d3Rigidity, float); DECLARE_SOA_COLUMN(D3TPCsignal, d3TPCsignal, float); DECLARE_SOA_COLUMN(D3TPCnSigma, d3TPCnSigma, float); -DECLARE_SOA_COLUMN(D3TPCnSigmaNhp, d3TPCnSigmaNhp, float); -DECLARE_SOA_COLUMN(D3TPCnSigmaNlp, d3TPCnSigmaNlp, float); +DECLARE_SOA_COLUMN(D3TPCnClsPid, d3TPCnClsPid, uint8_t); +DECLARE_SOA_COLUMN(D3TPCnClsCr, d3TPCnClsCr, uint8_t); +DECLARE_SOA_COLUMN(D3ITSnSigma, d3ITSnSigma, float); +DECLARE_SOA_COLUMN(D3PidForTrk, d3PidForTrk, uint32_t); DECLARE_SOA_COLUMN(D3TOFmass, d3TOFmass, float); DECLARE_SOA_COLUMN(D3DcaXY, d3DcaXY, float); DECLARE_SOA_COLUMN(D3DcaZ, d3DcaZ, float); @@ -184,11 +191,11 @@ DECLARE_SOA_COLUMN(D3IsPvContributor, d3IsPvContributor, bool); #define HYPKFHYPNUCMC hypkftree::McTrue, hykfmc::IsPhysicalPrimary -#define HYPKFD1 hypkftree::D1X, hypkftree::D1Y, hypkftree::D1Z, hypkftree::D1Px, hypkftree::D1Py, hypkftree::D1Pz, hypkftree::D1TPCnCls, hypkftree::D1TPCchi2, hypkftree::D1ITSnCls, hypkftree::D1ITSchi2, hypkftree::D1ITSmeanClsSizeL, hypkftree::D1Rigidity, hypkftree::D1TPCsignal, hypkftree::D1TPCnSigma, hypkftree::D1TPCnSigmaNhp, hypkftree::D1TPCnSigmaNlp, hypkftree::D1TOFmass, hypkftree::D1DcaXY, hypkftree::D1DcaZ, hypkftree::D1IsPvContributor +#define HYPKFD1 hypkftree::D1X, hypkftree::D1Y, hypkftree::D1Z, hypkftree::D1Px, hypkftree::D1Py, hypkftree::D1Pz, hypkftree::D1TPCnCls, hypkftree::D1TPCnClsPid, hypkftree::D1TPCnClsCr, hypkftree::D1TPCchi2, hypkftree::D1ITSnCls, hypkftree::D1ITSchi2, hypkftree::D1ITSmeanClsSizeL, hypkftree::D1Rigidity, hypkftree::D1TPCsignal, hypkftree::D1TPCnSigma, hypkftree::D1ITSnSigma, hypkftree::D1PidForTrk, hypkftree::D1TOFmass, hypkftree::D1DcaXY, hypkftree::D1DcaZ, hypkftree::D1IsPvContributor -#define HYPKFD2 hypkftree::D2X, hypkftree::D2Y, hypkftree::D2Z, hypkftree::D2Px, hypkftree::D2Py, hypkftree::D2Pz, hypkftree::D2TPCnCls, hypkftree::D2TPCchi2, hypkftree::D2ITSnCls, hypkftree::D2ITSchi2, hypkftree::D2ITSmeanClsSizeL, hypkftree::D2Rigidity, hypkftree::D2TPCsignal, hypkftree::D2TPCnSigma, hypkftree::D2TPCnSigmaNhp, hypkftree::D2TPCnSigmaNlp, hypkftree::D2TOFmass, hypkftree::D2DcaXY, hypkftree::D2DcaZ, hypkftree::D2IsPvContributor +#define HYPKFD2 hypkftree::D2X, hypkftree::D2Y, hypkftree::D2Z, hypkftree::D2Px, hypkftree::D2Py, hypkftree::D2Pz, hypkftree::D2TPCnCls, hypkftree::D2TPCnClsPid, hypkftree::D2TPCnClsCr, hypkftree::D2TPCchi2, hypkftree::D2ITSnCls, hypkftree::D2ITSchi2, hypkftree::D2ITSmeanClsSizeL, hypkftree::D2Rigidity, hypkftree::D2TPCsignal, hypkftree::D2TPCnSigma, hypkftree::D2ITSnSigma, hypkftree::D2PidForTrk, hypkftree::D2TOFmass, hypkftree::D2DcaXY, hypkftree::D2DcaZ, hypkftree::D2IsPvContributor -#define HYPKFD3 hypkftree::D3X, hypkftree::D3Y, hypkftree::D3Z, hypkftree::D3Px, hypkftree::D3Py, hypkftree::D3Pz, hypkftree::D3TPCnCls, hypkftree::D3TPCchi2, hypkftree::D3ITSnCls, hypkftree::D3ITSchi2, hypkftree::D3ITSmeanClsSizeL, hypkftree::D3Rigidity, hypkftree::D3TPCsignal, hypkftree::D3TPCnSigma, hypkftree::D3TPCnSigmaNhp, hypkftree::D3TPCnSigmaNlp, hypkftree::D3TOFmass, hypkftree::D3DcaXY, hypkftree::D3DcaZ, hypkftree::D3IsPvContributor +#define HYPKFD3 hypkftree::D3X, hypkftree::D3Y, hypkftree::D3Z, hypkftree::D3Px, hypkftree::D3Py, hypkftree::D3Pz, hypkftree::D3TPCnCls, hypkftree::D3TPCnClsPid, hypkftree::D3TPCnClsCr, hypkftree::D3TPCchi2, hypkftree::D3ITSnCls, hypkftree::D3ITSchi2, hypkftree::D3ITSmeanClsSizeL, hypkftree::D3Rigidity, hypkftree::D3TPCsignal, hypkftree::D3TPCnSigma, hypkftree::D3ITSnSigma, hypkftree::D3PidForTrk, hypkftree::D3TOFmass, hypkftree::D3DcaXY, hypkftree::D3DcaZ, hypkftree::D3IsPvContributor #define HYPKFSDMASS hypkftree::D1d2Mass, hypkftree::D1d3Mass, hypkftree::D2d3Mass @@ -252,10 +259,10 @@ struct HypKfTreeCreator { outputTableTwo( cand.species, cand.isMatter, cand.cent, cand.occu, cand.runNumber, cand.passedEvSel, cand.mass, cand.y, cand.pt, cand.ct, cand.cpaPv, cand.maxDcaTracks, cand.maxDcaTracksSv, cand.dcaToPvXY, cand.dcaToPvZ, cand.devToPvXY, cand.chi2, cand.pvx, cand.pvy, cand.pvz, cand.svx, cand.svy, cand.svz, cand.px, cand.py, cand.pz, cand.collisionMcTrue, - cand.mcTrue, cand.mcPhysicalPrimary, d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, - d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.tpcNsigmaNhp, d1.tpcNsigmaNlp, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, - d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, - d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.tpcNsigmaNhp, d2.tpcNsigmaNlp, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor); + cand.mcTrue, cand.mcPhysicalPrimary, d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcNclsPid, d1.tpcNclsCr, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, + d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.itsNsigma, d1.pidForTrk, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, + d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcNclsPid, d2.tpcNclsCr, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, + d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.itsNsigma, d2.pidForTrk, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor); if (isMC && cfgMCCombined) outputTableMcTwo( cand.speciesMC, cand.pdgCode, cand.isMatterMC, cand.isReconstructed, cand.isPhysicalPrimary, cand.passedEvSelMC, cand.yGen, cand.ptGen, cand.ctGen, @@ -263,10 +270,10 @@ struct HypKfTreeCreator { cand.species, cand.isMatter, cand.cent, cand.occu, cand.runNumber, cand.passedEvSel, cand.mass, cand.y, cand.pt, cand.ct, cand.cpaPv, cand.maxDcaTracks, cand.maxDcaTracksSv, cand.dcaToPvXY, cand.dcaToPvZ, cand.devToPvXY, cand.chi2, cand.pvx, cand.pvy, cand.pvz, cand.svx, cand.svy, cand.svz, cand.px, cand.py, cand.pz, cand.collisionMcTrue, - d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, - d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.tpcNsigmaNhp, d1.tpcNsigmaNlp, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, - d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, - d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.tpcNsigmaNhp, d2.tpcNsigmaNlp, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor); + d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcNclsPid, d1.tpcNclsCr, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, + d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.itsNsigma, d1.pidForTrk, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, + d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcNclsPid, d2.tpcNclsCr, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, + d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.itsNsigma, d2.pidForTrk, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor); } if (cfgNprimDaughters == Decays::kThreeBody) { const auto& d1 = cand.daughterTracks.at(0); @@ -277,12 +284,12 @@ struct HypKfTreeCreator { cand.species, cand.isMatter, cand.cent, cand.occu, cand.runNumber, cand.passedEvSel, cand.mass, cand.y, cand.pt, cand.ct, cand.cpaPv, cand.maxDcaTracks, cand.maxDcaTracksSv, cand.dcaToPvXY, cand.dcaToPvZ, cand.devToPvXY, cand.chi2, cand.pvx, cand.pvy, cand.pvz, cand.svx, cand.svy, cand.svz, cand.px, cand.py, cand.pz, cand.collisionMcTrue, cand.mcTrue, cand.mcPhysicalPrimary, - d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, - d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.tpcNsigmaNhp, d1.tpcNsigmaNlp, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, - d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, - d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.tpcNsigmaNhp, d2.tpcNsigmaNlp, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor, - d3.x, d3.y, d3.z, d3.px, d3.py, d3.pz, d3.tpcNcls, d3.tpcChi2, d3.itsNcls, d3.itsChi2, d3.itsMeanClsSizeL, - d3.rigidity, d3.tpcSignal, d3.tpcNsigma, d3.tpcNsigmaNhp, d3.tpcNsigmaNlp, d3.tofMass, d3.dcaXY, d3.dcaZ, d3.isPvContributor, + d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcNclsPid, d1.tpcNclsCr, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, + d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.itsNsigma, d1.pidForTrk, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, + d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcNclsPid, d2.tpcNclsCr, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, + d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.itsNsigma, d2.pidForTrk, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor, + d3.x, d3.y, d3.z, d3.px, d3.py, d3.pz, d3.tpcNcls, d3.tpcNclsPid, d3.tpcNclsCr, d3.tpcChi2, d3.itsNcls, d3.itsChi2, d3.itsMeanClsSizeL, + d3.rigidity, d3.tpcSignal, d3.tpcNsigma, d3.itsNsigma, d3.pidForTrk, d3.tofMass, d3.dcaXY, d3.dcaZ, d3.isPvContributor, d1.subMass, d2.subMass, d3.subMass); if (isMC && cfgMCCombined) outputTableMcThree( @@ -291,12 +298,12 @@ struct HypKfTreeCreator { cand.species, cand.isMatter, cand.cent, cand.occu, cand.runNumber, cand.passedEvSel, cand.mass, cand.y, cand.pt, cand.ct, cand.cpaPv, cand.maxDcaTracks, cand.maxDcaTracksSv, cand.dcaToPvXY, cand.dcaToPvZ, cand.devToPvXY, cand.chi2, cand.pvx, cand.pvy, cand.pvz, cand.svx, cand.svy, cand.svz, cand.px, cand.py, cand.pz, cand.collisionMcTrue, - d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, - d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.tpcNsigmaNhp, d1.tpcNsigmaNlp, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, - d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, - d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.tpcNsigmaNhp, d2.tpcNsigmaNlp, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor, - d3.x, d3.y, d3.z, d3.px, d3.py, d3.pz, d3.tpcNcls, d3.tpcChi2, d3.itsNcls, d3.itsChi2, d3.itsMeanClsSizeL, - d3.rigidity, d3.tpcSignal, d3.tpcNsigma, d3.tpcNsigmaNhp, d3.tpcNsigmaNlp, d3.tofMass, d3.dcaXY, d3.dcaZ, d3.isPvContributor, + d1.x, d1.y, d1.z, d1.px, d1.py, d1.pz, d1.tpcNcls, d1.tpcNclsPid, d1.tpcNclsCr, d1.tpcChi2, d1.itsNcls, d1.itsChi2, d1.itsMeanClsSizeL, + d1.rigidity, d1.tpcSignal, d1.tpcNsigma, d1.itsNsigma, d1.pidForTrk, d1.tofMass, d1.dcaXY, d1.dcaZ, d1.isPvContributor, + d2.x, d2.y, d2.z, d2.px, d2.py, d2.pz, d2.tpcNcls, d2.tpcNclsPid, d2.tpcNclsCr, d2.tpcChi2, d2.itsNcls, d2.itsChi2, d2.itsMeanClsSizeL, + d2.rigidity, d2.tpcSignal, d2.tpcNsigma, d2.itsNsigma, d2.pidForTrk, d2.tofMass, d2.dcaXY, d2.dcaZ, d2.isPvContributor, + d3.x, d3.y, d3.z, d3.px, d3.py, d3.pz, d3.tpcNcls, d3.tpcNclsPid, d3.tpcNclsCr, d3.tpcChi2, d3.itsNcls, d3.itsChi2, d3.itsMeanClsSizeL, + d3.rigidity, d3.tpcSignal, d3.tpcNsigma, d3.itsNsigma, d3.pidForTrk, d3.tofMass, d3.dcaXY, d3.dcaZ, d3.isPvContributor, d1.subMass, d2.subMass, d3.subMass); } } @@ -349,8 +356,10 @@ struct HypKfTreeCreator { daughter.rigidity = track.rigidity(); daughter.tpcSignal = track.tpcSignal(); daughter.tpcNsigma = track.tpcNsigma(); - daughter.tpcNsigmaNhp = track.tpcNsigmaNhp(); - daughter.tpcNsigmaNlp = track.tpcNsigmaNlp(); + daughter.itsNsigma = track.itsNsigma(); + daughter.pidForTrk = track.pidForTrk(); + daughter.tpcNclsPid = track.tpcNclsPid(); + daughter.tpcNclsCr = track.tpcNclsCr(); daughter.tofMass = track.tofMass(); daughter.dcaXY = track.dcaXY(); daughter.dcaZ = track.dcaZ(); @@ -438,7 +447,7 @@ struct HypKfTreeCreator { PROCESS_SWITCH(HypKfTreeCreator, processMC, "MC tree", false); //___________________________________________________________________________________________________________________________________________________________ - std::vector dcaTracksAll(std::vector& posVec, TString opt = "") + std::vector dcaTracksAll(std::vector& posVec, const TString& opt = "") { std::vector vec; int n = posVec.size(); @@ -450,7 +459,7 @@ struct HypKfTreeCreator { return vec; } template - std::vector dcaTrackSvAll(std::vector& posVec, T const& hypNuc, TString opt = "") + std::vector dcaTrackSvAll(std::vector& posVec, T const& hypNuc, const TString& opt = "") { std::vector vec; for (size_t i = 0; i < posVec.size(); i++) { @@ -463,14 +472,14 @@ struct HypKfTreeCreator { { return *max_element(vec.begin(), vec.end()); } - float meanValue(std::vector vec) + float meanValue(const std::vector& vec) { float sum = 0; for (const auto& value : vec) sum += value; return sum / vec.size(); } - float mean2Value(std::vector vec) + float mean2Value(const std::vector& vec) { float sum = 0; for (const auto& value : vec) @@ -478,7 +487,7 @@ struct HypKfTreeCreator { return std::sqrt(sum / vec.size()); } - float dcaTracks(std::vector v, int track1, int track2, TString opt = "XY") + float dcaTracks(std::vector v, int track1, int track2, const TString& opt = "XY") { if (opt == "XY") return RecoDecay::distanceXY(v.at(track1), v.at(track2)); @@ -488,7 +497,7 @@ struct HypKfTreeCreator { return RecoDecay::distance(v.at(track1), v.at(track2)); } template - float dcaTrackSv(std::vector& v, int track, T const& hypNuc, TString opt = "") + float dcaTrackSv(std::vector& v, int track, T const& hypNuc, const TString& opt = "") { if (opt == "XY") return RecoDecay::distanceXY(v.at(track), decayVtx(hypNuc)); diff --git a/PWGLF/TableProducer/Nuspex/hyperRecoTask.cxx b/PWGLF/TableProducer/Nuspex/hyperRecoTask.cxx index d7fe4488ec0..1bf6fd11035 100644 --- a/PWGLF/TableProducer/Nuspex/hyperRecoTask.cxx +++ b/PWGLF/TableProducer/Nuspex/hyperRecoTask.cxx @@ -56,8 +56,8 @@ #include #include #include +#include #include -#include #include #include @@ -79,65 +79,29 @@ using EventCandidatesMC = soa::Join betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; static const std::vector particleName{"He3"}; -std::shared_ptr hEvents; -std::shared_ptr hEventsZorro; -std::shared_ptr hZvtx; -std::shared_ptr hCentFT0A; -std::shared_ptr hCentFT0C; -std::shared_ptr hCentFT0M; -std::shared_ptr hNsigma3HeSel; -std::shared_ptr hDeDx3HeSel; -std::shared_ptr hDeDxTot; -std::shared_ptr hH3LMassBefSel; -std::shared_ptr hH3LMassTracked; -std::shared_ptr hH4LMassBefSel; -std::shared_ptr hH4LMassTracked; -std::shared_ptr hDecayChannel; -std::shared_ptr hIsMatterGen; -std::shared_ptr hIsMatterGenTwoBody; -std::shared_ptr hEvtMC; -std::shared_ptr hImpactParamGen; -std::shared_ptr hImpactParamReco; -std::shared_ptr hGen3HLBeforeEvtSel; -std::shared_ptr hGen3HLAfterSel; -std::shared_ptr hGenEventsNchEta05; -std::shared_ptr hGenEventsNchEta08; -std::shared_ptr hGenCentralityColvsMultiplicityGenEta05; -std::shared_ptr hGenCentralityColvsMultiplicityGenEta08; -std::shared_ptr hGenCentralityColvsImpactParamGen; -std::shared_ptr hGenCentralityColvsFT0Cmultiplicity; -std::shared_ptr hRecoCentralityColvsMultiplicityRecoEta05; -std::shared_ptr hRecoCentralityColvsMultiplicityRecoEta08; -std::shared_ptr hRecoCentralityColvsImpactParamReco; -std::shared_ptr hRecoCentralityColvsFT0Cmultiplicity; -std::shared_ptr hGen3HLvsImpactParameterBeforeEvtSel; -std::shared_ptr hGen3HLvsImpactParameterAfterSel; -std::shared_ptr hGen3HLvsMultiplicityGenEta05BeforeEvtSel; -std::shared_ptr hGen3HLvsMultiplicityGenEta05AfterSel; -std::shared_ptr hGen3HLvsMultiplicityGenEta08BeforeEvtSel; -std::shared_ptr hGen3HLvsMultiplicityGenEta08AfterSel; -std::shared_ptr hGen3HLvsMultiplicityFT0CBeforeEvtSel; -std::shared_ptr hGen3HLvsMultiplicityFT0CAfterSel; } // namespace struct hyperCandidate { - float recoPtHe3() const { return std::hypot(momHe3[0], momHe3[1]); } - float recoPhiHe3() const { return std::atan2(momHe3[1], momHe3[0]); } - float recoEtaHe3() const { return std::asinh(momHe3[2] / recoPtHe3()); } - float recoPtPi() const { return std::hypot(momPi[0], momPi[1]); } - float recoPhiPi() const { return std::atan2(momPi[1], momPi[0]); } - float recoEtaPi() const { return std::asinh(momPi[2] / recoPtPi()); } - float genPt() const { return std::hypot(gMom[0], gMom[1]); } - float genPtHe3() const { return std::hypot(gMomHe3[0], gMomHe3[1]); } - float genPhi() const { return std::atan2(gMom[1], gMom[0]); } - float genEta() const { return std::asinh(gMom[2] / genPt()); } + [[nodiscard]] float recoPtHe3() const { return std::hypot(momHe3[0], momHe3[1]); } + [[nodiscard]] float recoPhiHe3() const { return std::atan2(momHe3[1], momHe3[0]); } + [[nodiscard]] float recoEtaHe3() const { return std::asinh(momHe3[2] / recoPtHe3()); } + [[nodiscard]] float recoPtPi() const { return std::hypot(momPi[0], momPi[1]); } + [[nodiscard]] float recoPhiPi() const { return std::atan2(momPi[1], momPi[0]); } + [[nodiscard]] float recoEtaPi() const { return std::asinh(momPi[2] / recoPtPi()); } + [[nodiscard]] float genPt() const { return std::hypot(gMom[0], gMom[1]); } + [[nodiscard]] float genPtHe3() const { return std::hypot(gMomHe3[0], gMomHe3[1]); } + [[nodiscard]] float genPhi() const { return std::atan2(gMom[1], gMom[0]); } + [[nodiscard]] float genEta() const { return std::asinh(gMom[2] / genPt()); } int v0ID = -1; - int heTrackID; - int piTrackID; + int heTrackID = -1; + int piTrackID = -1; float dcaV0dau = -10; float cosPA = -10; float nSigmaHe3 = -10; @@ -145,12 +109,12 @@ struct hyperCandidate { float piDCAXY = -10; float momHe3TPC = -10.f; float momPiTPC = -10.f; - std::array momHe3; - std::array momPi; - std::array decVtx; - std::array gMom; - std::array gMomHe3; - std::array gDecVtx; + std::array momHe3{}; + std::array momPi{}; + std::array decVtx{}; + std::array gMom{}; + std::array gMomHe3{}; + std::array gDecVtx{}; uint16_t tpcSignalHe3 = 0u; uint16_t tpcSignalPi = 0u; float tpcChi2He3 = 0.f; @@ -180,36 +144,38 @@ struct hyperCandidate { uint8_t flags = 0u; // flags for dughter particles }; -struct hyperRecoTask { +struct HyperRecoTask { Produces outputDataTable; Produces outputDataTableWithFlow; Produces outputMCTable; + Produces outputMCTableWithCollID; Produces outputDataTableWithCollID; Service ccdb; Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; // PDG codes - Configurable hyperPdg{"hyperPDG", 1010010030, "PDG code of the hyper-mother (could be 3LamH or 4LamH)"}; - Configurable heDauPdg{"heDauPDG", 1000020030, "PDG code of the helium (could be 3He or 4He)"}; + Configurable hyperPdg{"hyperPdg", 1010010030, "PDG code of the hyper-mother (could be 3LamH or 4LamH)"}; + Configurable heDauPdg{"heDauPdg", 1000020030, "PDG code of the helium (could be 3He or 4He)"}; Configurable piDauPdg{"piDauPdg", 211, "PDG code of pion"}; // Selection criteria - Configurable v0cospacut{"hypcospa", 0.95, "V0 CosPA"}; - Configurable masswidth{"hypmasswidth", 0.06, "Mass width (GeV/c^2)"}; - Configurable dcaToPvPion{"dcapvPi", 0., "DCA to PV pion"}; - Configurable dcaToPvHe{"dcapvHe", 0., "DCA to PV helium"}; - Configurable dcav0dau{"hypdcaDau", 1.0, "DCA V0 Daughters"}; + Configurable v0CosPaCut{"v0CosPaCut", 0.95, "V0 CosPA"}; + Configurable massWidth{"massWidth", 0.06, "Mass width (GeV/c^2)"}; + Configurable dcaToPvPion{"dcaToPvPion", 0., "DCA to PV pion"}; + Configurable dcaToPvHe{"dcaToPvHe", 0., "DCA to PV helium"}; + Configurable dcaV0Dau{"dcaV0Dau", 1.0, "DCA V0 Daughters"}; Configurable ptMin{"ptMin", 0.5, "Minimum pT of the hypercandidate"}; - Configurable TPCRigidityMinHe{"TPCRigidityMinHe", 0.2, "Minimum rigidity of the helium candidate"}; - Configurable etaMax{"eta", 1., "eta daughter"}; + Configurable tpcRigidityMinHe{"tpcRigidityMinHe", 0.2, "Minimum rigidity of the helium candidate"}; + Configurable etaMax{"etaMax", 1., "eta daughter"}; Configurable nSigmaMaxHe{"nSigmaMaxHe", 5, "helium dEdx cut (n sigma)"}; Configurable nTPCClusMinHe{"nTPCClusMinHe", 70, "helium NTPC clusters cut"}; Configurable nTPCClusMinPi{"nTPCClusMinPi", -1., "pion NTPC clusters cut"}; Configurable nTPCCrossedRowsMinHe{"nTPCCrossedRowsMinHe", 70, "helium minimum crossed rows"}; Configurable nTPCCrossedRowsMinPi{"nTPCCrossedRowsMinPi", -1., "pion minimum crossed rows"}; Configurable mcSignalOnly{"mcSignalOnly", true, "If true, save only signal in MC"}; + Configurable useLikeSignPairs{"useLikeSignPairs", false, "If true, reconstruct like-sign pairs for background estimation. Does not work with processDataTracked"}; Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; Configurable isEventUsedForEPCalibration{"isEventUsedForEPCalibration", 1, "Event is used for EP calibration"}; @@ -233,8 +199,8 @@ struct hyperRecoTask { Configurable cfgEvSelkIsGoodZvtxFT0vsPV{"cfgEvSelkIsGoodZvtxFT0vsPV", false, "Verifies the consistency between the primary vertex z position from tracking and the z position of the PV from FT0 timing"}; // CCDB options - Configurable d_bz_input{"d_bz", -999, "bz field, -999 is automatic"}; - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable dBzInput{"dBzInput", -999, "bz field, -999 is automatic"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; @@ -269,9 +235,9 @@ struct hyperRecoTask { HistogramRegistry qaRegistry{"QA", {}, OutputObjHandlingPolicy::AnalysisObject}; - int mRunNumber; - float d_bz; - std::array mBBparamsHe; + int mRunNumber = -1; + float d_bz = 0.f; + std::array mBBparamsHe{}; void init(InitContext const&) { @@ -280,7 +246,7 @@ struct hyperRecoTask { mRunNumber = 0; d_bz = 0; - ccdb->setURL(ccdburl); + ccdb->setURL(ccdbUrl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); @@ -312,76 +278,80 @@ struct hyperRecoTask { const AxisSpec ptAxis{binsPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec multAxis = {binsMult, "Multiplicity #eta <0.5"}; - hNsigma3HeSel = qaRegistry.add("hNsigma3HeSel", "; p_{TPC}/z (GeV/#it{c}); n_{#sigma} ({}^{3}He)", HistType::kTH2F, {rigidityAxis, nSigma3HeAxis}); - hDeDx3HeSel = qaRegistry.add("hDeDx3HeSel", ";p_{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {rigidityAxis, dedxAxis}); - hDeDxTot = qaRegistry.add("hDeDxTot", ";p_{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {rigidityAxis, dedxAxis}); - hH3LMassBefSel = qaRegistry.add("hH3LMassBefSel", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 2.96, 3.04}}); - hH3LMassTracked = qaRegistry.add("hH3LMassTracked", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 2.96, 3.04}}); - hH4LMassBefSel = qaRegistry.add("hH4LMassBefSel", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 3.76, 3.84}}); - hH4LMassTracked = qaRegistry.add("hH4LMassTracked", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 3.76, 3.84}}); + qaRegistry.add("hNsigma3HeSel", "; p_{TPC}/z (GeV/#it{c}); n_{#sigma} ({}^{3}He)", HistType::kTH2F, {rigidityAxis, nSigma3HeAxis}); + qaRegistry.add("hDeDx3HeSel", ";p_{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {rigidityAxis, dedxAxis}); + qaRegistry.add("hDeDxTot", ";p_{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {rigidityAxis, dedxAxis}); + qaRegistry.add("hH3LMassBefSel", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 2.96, 3.04}}); + qaRegistry.add("hH3LMassTracked", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 2.96, 3.04}}); + qaRegistry.add("hH4LMassBefSel", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 3.76, 3.84}}); + qaRegistry.add("hH4LMassTracked", ";M (GeV/#it{c}^{2}); ", HistType::kTH1D, {{60, 3.76, 3.84}}); - hEvents = qaRegistry.add("hEvents", ";Events; ", HistType::kTH1D, {{5, -0.5, 4.5}}); + const auto hEvents = qaRegistry.add("hEvents", ";Events; ", HistType::kTH1D, {{5, -0.5, 4.5}}); hEvents->GetXaxis()->SetBinLabel(1, "All"); hEvents->GetXaxis()->SetBinLabel(2, "sel8"); hEvents->GetXaxis()->SetBinLabel(3, "z_{vtx}"); hEvents->GetXaxis()->SetBinLabel(4, "kNoSameBunchPileup"); hEvents->GetXaxis()->SetBinLabel(5, "kIsGoodZvtxFT0vsPV"); - hEventsZorro = qaRegistry.add("hEventsZorro", ";Events; ", HistType::kTH1D, {{2, -0.5, 1.5}}); + const auto hEventsZorro = qaRegistry.add("hEventsZorro", ";Events; ", HistType::kTH1D, {{2, -0.5, 1.5}}); hEventsZorro->GetXaxis()->SetBinLabel(1, "Zorro before evsel"); hEventsZorro->GetXaxis()->SetBinLabel(2, "Zorro after evsel"); - if (doprocessMC || doprocessMCTracked) { - hDecayChannel = qaRegistry.add("hDecayChannel", ";Decay channel; ", HistType::kTH1D, {{2, -0.5, 1.5}}); + if (doprocessMC || doprocessMCWithCollID || doprocessMCTracked) { + const auto hDecayChannel = qaRegistry.add("hDecayChannel", ";Decay channel; ", HistType::kTH1D, {{2, -0.5, 1.5}}); hDecayChannel->GetXaxis()->SetBinLabel(1, "2-body"); hDecayChannel->GetXaxis()->SetBinLabel(2, "3-body"); - hIsMatterGen = qaRegistry.add("hIsMatterGen", ";; ", HistType::kTH1D, {{2, -0.5, 1.5}}); + const auto hIsMatterGen = qaRegistry.add("hIsMatterGen", ";; ", HistType::kTH1D, {{2, -0.5, 1.5}}); hIsMatterGen->GetXaxis()->SetBinLabel(1, "Matter"); hIsMatterGen->GetXaxis()->SetBinLabel(2, "Antimatter"); - hIsMatterGenTwoBody = qaRegistry.add("hIsMatterGenTwoBody", ";; ", HistType::kTH1D, {{2, -0.5, 1.5}}); + const auto hIsMatterGenTwoBody = qaRegistry.add("hIsMatterGenTwoBody", ";; ", HistType::kTH1D, {{2, -0.5, 1.5}}); hIsMatterGenTwoBody->GetXaxis()->SetBinLabel(1, "Matter"); hIsMatterGenTwoBody->GetXaxis()->SetBinLabel(2, "Antimatter"); } - hZvtx = qaRegistry.add("hZvtx", ";z_{vtx} (cm); ", HistType::kTH1D, {{100, -20, 20}}); - hCentFT0A = qaRegistry.add("hCentFT0A", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); - hCentFT0C = qaRegistry.add("hCentFT0C", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); - hCentFT0M = qaRegistry.add("hCentFT0M", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + qaRegistry.add("hZvtx", ";z_{vtx} (cm); ", HistType::kTH1D, {{100, -20, 20}}); + qaRegistry.add("hCentFT0A", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + qaRegistry.add("hCentFT0C", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + qaRegistry.add("hCentFT0M", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); if (doprocessEventLossMC) { - hEvtMC = qaRegistry.add("QAEvent/hEvtMC", ";; ", HistType::kTH1D, {{3, -0.5, 2.5}}); + // Infomation for all generated collisions + const auto hEvtMC = qaRegistry.add("QAEvent/hEvtMC", ";; ", HistType::kTH1D, {{3, -0.5, 2.5}}); hEvtMC->GetXaxis()->SetBinLabel(1, "All gen evts"); hEvtMC->GetXaxis()->SetBinLabel(2, "Gen evts with al least one reconstructed"); hEvtMC->GetXaxis()->SetBinLabel(3, "Gen evts with no reconstructed collisions"); - hGenEventsNchEta05 = qaRegistry.add("QAEvent/hGenEventsNchEta05", ";;", HistType::kTH2D, {{multAxis}, {2, -0.5f, +1.5f}}); + const auto hGenEventsNchEta05 = qaRegistry.add("QAEvent/hGenEventsNchEta05", ";;", HistType::kTH2D, {{multAxis}, {2, -0.5f, +1.5f}}); hGenEventsNchEta05->GetYaxis()->SetBinLabel(1, "All gen. events"); hGenEventsNchEta05->GetYaxis()->SetBinLabel(2, "Gen evts with at least 1 rec. collisions"); - hGenEventsNchEta08 = qaRegistry.add("QAEvent/hGenEventsNchEta08", ";;", HistType::kTH2D, {{multAxis}, {2, -0.5f, +1.5f}}); + const auto hGenEventsNchEta08 = qaRegistry.add("QAEvent/hGenEventsNchEta08", ";;", HistType::kTH2D, {{multAxis}, {2, -0.5f, +1.5f}}); hGenEventsNchEta08->GetYaxis()->SetBinLabel(1, "All gen. events"); hGenEventsNchEta08->GetYaxis()->SetBinLabel(2, "Gen evts with at least 1 rec. collisions"); - // Infomation for all generated collisions collisions - hImpactParamGen = qaRegistry.add("QAEvent/McColAll/hImpactParamGen", "Impact parameter of generated MC events; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); - hGenCentralityColvsMultiplicityGenEta05 = qaRegistry.add("QAEvent/McColAll/hGenCentralityColvsMultiplicityGenEta05", "Correlation between FT0C centrality and charged particle multiplicity in generated MC events; Multiplicity #eta <0.5; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); - hGenCentralityColvsMultiplicityGenEta08 = qaRegistry.add("QAEvent/McColAll/hGenCentralityColvsMultiplicityGenEta08", "Correlation between FT0C centrality and charged particle multiplicity in generated MC events; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); - hGenCentralityColvsImpactParamGen = qaRegistry.add("QAEvent/McColAll/hGenCentralityColvsImpactParamGen", "Correlation between FT0C centrality and impact parameter in generated MC events; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, impactParamAxis}); - hGenCentralityColvsFT0Cmultiplicity = qaRegistry.add("QAEvent/McColAll/hGenCentralityColvsFT0Cmultiplicity", "Correlation between FT0C centrality and FT0C multiplicity in generated MC events; FT0c multiplicity", HistType::kTH2D, {centFT0CAxis, binsFT0CMultAxis}); - // Infomation for generated collisions collisions with at least one rec. event - hImpactParamReco = qaRegistry.add("QAEvent/McColAll/hImpactParamReco", "Impact parameter of generated MC events with at least one rec. evt; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); - hRecoCentralityColvsMultiplicityRecoEta05 = qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsMultiplicityRecoEta05", "Correlation between FT0C centrality and charged particle multiplicity in generated MC events with at least one rec. evt; Multiplicity #eta <0.5; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); - hRecoCentralityColvsMultiplicityRecoEta08 = qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsMultiplicityRecoEta08", "Correlation between FT0C centrality and charged particle multiplicity in generated MC events with at least one rec. evt; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); - hRecoCentralityColvsImpactParamReco = qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsImpactParamReco", "Correlation between FT0C centrality and impact parameter in generated MC events with at least one rec. evt; Impact Parameter (b); Counts", HistType::kTH2D, {centFT0CAxis, impactParamAxis}); - hRecoCentralityColvsFT0Cmultiplicity = qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsFT0Cmultiplicity", "Correlation between FT0C centrality and FT0C multiplicity in generated MC events with at least one rec. evt; FT0C (%); FT0c multiplicity", HistType::kTH2D, {centFT0CAxis, binsFT0CMultAxis}); + qaRegistry.add("QAEvent/McColAll/hImpactParamGen", "Impact parameter of generated MC events; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); + // Infomation for generated collisions which at least one rec. collision and passed the event selection + qaRegistry.add("QAEvent/McColPassedEvSel/hImpactParamGenOneReco", "Impact parameter of generated MC events with at least one rec. evt and passed the event selection; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); + qaRegistry.add("QAEvent/McColPassedEvSel/hGenOneRecoCentrality", "Centrality distribution of generated MC events with at least one rec. evt and passed the event selection; Centrality (FT0C %); Counts", HistType::kTH1D, {centFT0CAxis}); + qaRegistry.add("QAEvent/McColPassedEvSel/hGenCentralityColvsMultiplicityGenEta05", "Correlation between FT0C centrality and charged particle multiplicity in generated MC events with at least one rec. evt and passed the event selection; Multiplicity #eta <0.5; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); + qaRegistry.add("QAEvent/McColPassedEvSel/hGenCentralityColvsMultiplicityGenEta08", "Correlation between FT0C centrality and charged particle multiplicity in generated MC events with at least one rec. evt and passed the event selection; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); + qaRegistry.add("QAEvent/McColPassedEvSel/hGenCentralityColvsImpactParamGen", "Correlation between FT0C centrality and impact parameter in generated MC events with at least one rec. evt and passed the event selection; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, impactParamAxis}); + qaRegistry.add("QAEvent/McColPassedEvSel/hGenCentralityColvsFT0Cmultiplicity", "Correlation between FT0C centrality and FT0C multiplicity in generated MC events with at least one rec. evt and passed the event selection; FT0c multiplicity", HistType::kTH2D, {centFT0CAxis, binsFT0CMultAxis}); + // Infomation for all reconstructed collisions passed the event selection (for Event Splitting) + qaRegistry.add("QAEvent/McColAll/hImpactParamReco", "Impact parameter of generated MC events with at least one rec. evt; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); + qaRegistry.add("QAEvent/McColAll/hRecoCentrality", "Centrality distribution of reconstructed MC events passed the event selection; Centrality (FT0C %); Counts", HistType::kTH1D, {centFT0CAxis}); + qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsMultiplicityRecoEta05", "Correlation between FT0C centrality and charged particle multiplicity in reconstructed MC events passed the event selection; Multiplicity #eta <0.5; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); + qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsMultiplicityRecoEta08", "Correlation between FT0C centrality and charged particle multiplicity in reconstructed MC events passed the event selection; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); + qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsImpactParamReco", "Correlation between FT0C centrality and impact parameter in reconstructed MC events passed the event selection; Impact Parameter (b); Counts", HistType::kTH2D, {centFT0CAxis, impactParamAxis}); + qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsFT0Cmultiplicity", "Correlation between FT0C centrality and FT0C multiplicity in reconstructed MC events passed the event selection; FT0C (%); FT0c multiplicity", HistType::kTH2D, {centFT0CAxis, binsFT0CMultAxis}); // Information of generated 3HL in generated events - hGen3HLBeforeEvtSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLBeforeEvtSel", "3HL generated #it{p}_{T} distribution in all gen evt;#it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {ptAxis}); - hGen3HLvsImpactParameterBeforeEvtSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsImpactParameterBeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and impact parameter in all gen evt;#it{p}_{T} (GeV/#it{c}); Impact parameter (b)", HistType::kTH2D, {ptAxis, impactParamAxis}); - hGen3HLvsMultiplicityGenEta05BeforeEvtSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta05BeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.5", HistType::kTH2D, {ptAxis, multAxis}); - hGen3HLvsMultiplicityGenEta08BeforeEvtSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta08BeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {ptAxis, multAxis}); - hGen3HLvsMultiplicityFT0CBeforeEvtSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityFT0CBeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and FT0C multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); FT0C Multiplicity", HistType::kTH2D, {ptAxis, binsFT0CMultAxis}); - // Information of generated 3HL in generated events with at least one rec. event - hGen3HLAfterSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLAfterSel", "3HL generated #it{p}_{T} distribution in gen. evts with at least one rec. evt; #it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {ptAxis}); - hGen3HLvsImpactParameterAfterSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsImpactParameterAfterSel", "Correlation 3HL generated #it{p}_{T} and impact parameter in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Impact parameter (b)", HistType::kTH2D, {ptAxis, impactParamAxis}); - hGen3HLvsMultiplicityGenEta05AfterSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta05AfterSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.5", HistType::kTH2D, {ptAxis, multAxis}); - hGen3HLvsMultiplicityGenEta08AfterSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta08AfterSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {ptAxis, multAxis}); - hGen3HLvsMultiplicityFT0CAfterSel = qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityFT0CAfterSel", "Correlation 3HL generated #it{p}_{T} and FT0C multiplicity in gen. evts with at least one rec;#it{p}_{T} (GeV/#it{c}); FT0C Multiplicity", HistType::kTH2D, {ptAxis, binsFT0CMultAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLBeforeEvtSel", "3HL generated #it{p}_{T} distribution in all gen evt;#it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {ptAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsImpactParameterBeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and impact parameter in all gen evt;#it{p}_{T} (GeV/#it{c}); Impact parameter (b)", HistType::kTH2D, {ptAxis, impactParamAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta05BeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.5", HistType::kTH2D, {ptAxis, multAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta08BeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {ptAxis, multAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityFT0CBeforeEvtSel", "Correlation 3HL generated #it{p}_{T} and FT0C multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); FT0C Multiplicity", HistType::kTH2D, {ptAxis, binsFT0CMultAxis}); + // Information of generated 3HL in generated events with at least one rec. event and passed the event selection + qaRegistry.add("QAEvent/McCol3HL/hGen3HLAfterSel", "3HL generated #it{p}_{T} distribution in gen. evts with at least one rec. evt; #it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {ptAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsImpactParameterAfterSel", "Correlation 3HL generated #it{p}_{T} and impact parameter in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Impact parameter (b)", HistType::kTH2D, {ptAxis, impactParamAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta05AfterSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.5", HistType::kTH2D, {ptAxis, multAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta08AfterSel", "Correlation 3HL generated #it{p}_{T} and charged particle multiplicity in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {ptAxis, multAxis}); + qaRegistry.add("QAEvent/McCol3HL/hGen3HLvsMultiplicityFT0CAfterSel", "Correlation 3HL generated #it{p}_{T} and FT0C multiplicity in gen. evts with at least one rec;#it{p}_{T} (GeV/#it{c}); FT0C Multiplicity", HistType::kTH2D, {ptAxis, binsFT0CMultAxis}); } } @@ -400,12 +370,12 @@ struct hyperRecoTask { o2::parameters::GRPMagField* grpmag = 0x0; if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); - if (d_bz_input < -990) { + if (dBzInput < UseCCDBMagneticFieldThreshold) { // Fetch magnetic field from ccdb for current collision d_bz = grpo->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; } else { - d_bz = d_bz_input; + d_bz = dBzInput; } } else { grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); @@ -413,22 +383,22 @@ struct hyperRecoTask { LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; } o2::base::Propagator::initFieldFromGRP(grpmag); - if (d_bz_input < -990) { + if (dBzInput < UseCCDBMagneticFieldThreshold) { // Fetch magnetic field from ccdb for current collision d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; } else { - d_bz = d_bz_input; + d_bz = dBzInput; } } if (!pidPath.value.empty()) { auto he3pid = ccdb->getForTimeStamp>(pidPath.value + "_He3", run3grp_timestamp); std::copy(he3pid->begin(), he3pid->end(), mBBparamsHe.begin()); } else { - for (int i = 0; i < 5; i++) { + for (int i = 0; i < NumBetheBlochParameters; i++) { mBBparamsHe[i] = cfgBetheBlochParams->get("He3", Form("p%i", i)); } - mBBparamsHe[5] = cfgBetheBlochParams->get("He3", "resolution"); + mBBparamsHe[NumBetheBlochParameters] = cfgBetheBlochParams->get("He3", "resolution"); } fitter.setBz(d_bz); mRunNumber = bc.runNumber(); @@ -450,7 +420,7 @@ struct hyperRecoTask { for (const auto& collision : collisions) { auto bc = collision.template bc_as(); initCCDB(bc); - hEvents->Fill(0.); + qaRegistry.fill(HIST("hEvents"), 0.); if (!collision.selection_bit(aod::evsel::kNoITSROFrameBorder) && !disableITSROFCut) { continue; @@ -461,7 +431,7 @@ struct hyperRecoTask { // accounting done after ITS border cut, to properly correct with the MC zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); if (zorroSelected) { - hEventsZorro->Fill(0.); + qaRegistry.fill(HIST("hEventsZorro"), 0.); } } @@ -469,35 +439,35 @@ struct hyperRecoTask { continue; } - hEvents->Fill(1.); + qaRegistry.fill(HIST("hEvents"), 1.); - if (std::abs(collision.posZ()) > 10) { + if (std::abs(collision.posZ()) > MaxAbsVertexZ) { continue; } - hEvents->Fill(2.); + qaRegistry.fill(HIST("hEvents"), 2.); if (zorroSelected) { - hEventsZorro->Fill(1.); + qaRegistry.fill(HIST("hEventsZorro"), 1.); } if (cfgEvSelkNoSameBunchPileup) { if (!collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { continue; } - hEvents->Fill(3.); + qaRegistry.fill(HIST("hEvents"), 3.); } if (cfgEvSelkIsGoodZvtxFT0vsPV) { if (!collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { continue; } - hEvents->Fill(4.); + qaRegistry.fill(HIST("hEvents"), 4.); } goodCollision[collision.globalIndex()] = true; - hZvtx->Fill(collision.posZ()); - hCentFT0A->Fill(collision.centFT0A()); - hCentFT0C->Fill(collision.centFT0C()); - hCentFT0M->Fill(collision.centFT0M()); + qaRegistry.fill(HIST("hZvtx"), collision.posZ()); + qaRegistry.fill(HIST("hCentFT0A"), collision.centFT0A()); + qaRegistry.fill(HIST("hCentFT0C"), collision.centFT0C()); + qaRegistry.fill(HIST("hCentFT0M"), collision.centFT0M()); } } @@ -507,41 +477,42 @@ struct hyperRecoTask { for (const auto& collision : collisions) { auto bc = collision.template bc_as(); initCCDB(bc); - hEvents->Fill(0.); + qaRegistry.fill(HIST("hEvents"), 0.); if (collision.has_mcCollision()) { recoCollisionIds[collision.mcCollisionId()] = collision.globalIndex(); } - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { continue; + } - hEvents->Fill(1.); + qaRegistry.fill(HIST("hEvents"), 1.); - if (std::abs(collision.posZ()) > 10) { + if (std::abs(collision.posZ()) > MaxAbsVertexZ) { continue; } - hEvents->Fill(2.); + qaRegistry.fill(HIST("hEvents"), 2.); if (cfgEvSelkNoSameBunchPileup) { if (!collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { continue; } - hEvents->Fill(3.); + qaRegistry.fill(HIST("hEvents"), 3.); } if (cfgEvSelkIsGoodZvtxFT0vsPV) { if (!collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { continue; } - hEvents->Fill(4.); + qaRegistry.fill(HIST("hEvents"), 4.); } if (collision.has_mcCollision()) { isSurvEvSelCollision[collision.mcCollisionId()] = true; } goodCollision[collision.globalIndex()] = true; - hZvtx->Fill(collision.posZ()); - hCentFT0A->Fill(collision.centFT0A()); - hCentFT0C->Fill(collision.centFT0C()); - hCentFT0M->Fill(collision.centFT0M()); + qaRegistry.fill(HIST("hZvtx"), collision.posZ()); + qaRegistry.fill(HIST("hCentFT0A"), collision.centFT0A()); + qaRegistry.fill(HIST("hCentFT0C"), collision.centFT0C()); + qaRegistry.fill(HIST("hCentFT0M"), collision.centFT0M()); } } template @@ -565,11 +536,12 @@ struct hyperRecoTask { hypCand.clusterSizeITSPi = piTrack.itsClusterSizes(); bool heliumPID = heTrack.pidForTracking() == o2::track::PID::Helium3 || heTrack.pidForTracking() == o2::track::PID::Alpha; hypCand.momHe3TPC = (heliumPID && cfgCompensatePIDinTracking) ? heTrack.tpcInnerParam() / 2 : heTrack.tpcInnerParam(); - if (hypCand.momHe3TPC < TPCRigidityMinHe) + if (hypCand.momHe3TPC < tpcRigidityMinHe) { return; + } hypCand.momPiTPC = piTrack.tpcInnerParam(); - hDeDxTot->Fill(hypCand.momHe3TPC * heTrack.sign(), heTrack.tpcSignal()); - hDeDxTot->Fill(hypCand.momPiTPC * piTrack.sign(), piTrack.tpcSignal()); + qaRegistry.fill(HIST("hDeDxTot"), hypCand.momHe3TPC * heTrack.sign(), heTrack.tpcSignal()); + qaRegistry.fill(HIST("hDeDxTot"), hypCand.momPiTPC * piTrack.sign(), piTrack.tpcSignal()); hypCand.flags |= static_cast((heTrack.pidForTracking() & 0xF) << 4); hypCand.flags |= static_cast(piTrack.pidForTracking() & 0xF); auto heTrackCov = getTrackParCov(heTrack); @@ -591,7 +563,7 @@ struct hyperRecoTask { hePropTrack.getPxPyPzGlo(hypCand.momHe3); piPropTrack.getPxPyPzGlo(hypCand.momPi); // the momentum has to be multiplied by 2 (charge) - for (int i = 0; i < 3; i++) { + for (std::size_t i = 0; i < hypCand.momHe3.size(); i++) { hypCand.momHe3[i] *= 2; } float heP2 = hypCand.momHe3[0] * hypCand.momHe3[0] + hypCand.momHe3[1] * hypCand.momHe3[1] + hypCand.momHe3[2] * hypCand.momHe3[2]; @@ -601,34 +573,38 @@ struct hyperRecoTask { float piE = std::sqrt(piP2 + piMass * piMass); float h3lE = he3E + piE; float h4lE = he4E + piE; - std::array hypMom; + std::array hypMom{}; const auto& vtx = fitter.getPCACandidate(); - for (int i = 0; i < 3; i++) { + for (std::size_t i = 0; i < hypCand.decVtx.size(); i++) { hypCand.decVtx[i] = vtx[i]; hypMom[i] = hypCand.momHe3[i] + hypCand.momPi[i]; } float hypPt = std::hypot(hypMom[0], hypMom[1]); - if (hypPt < ptMin) + if (hypPt < ptMin) { return; + } float massH3L = std::sqrt(h3lE * h3lE - hypMom[0] * hypMom[0] - hypMom[1] * hypMom[1] - hypMom[2] * hypMom[2]); float massH4L = std::sqrt(h4lE * h4lE - hypMom[0] * hypMom[0] - hypMom[1] * hypMom[1] - hypMom[2] * hypMom[2]); bool isHypMass = false; - if (massH3L > o2::constants::physics::MassHyperTriton - masswidth && massH3L < o2::constants::physics::MassHyperTriton + masswidth) + if (massH3L > o2::constants::physics::MassHyperTriton - massWidth && massH3L < o2::constants::physics::MassHyperTriton + massWidth) { isHypMass = true; - if (massH4L > o2::constants::physics::MassHyperhydrog4 - masswidth && massH4L < o2::constants::physics::MassHyperhydrog4 + masswidth) + } + if (massH4L > o2::constants::physics::MassHyperhydrog4 - massWidth && massH4L < o2::constants::physics::MassHyperhydrog4 + massWidth) { isHypMass = true; - if (!isHypMass) + } + if (!isHypMass) { return; + } - hH3LMassBefSel->Fill(massH3L); - hH4LMassBefSel->Fill(massH4L); + qaRegistry.fill(HIST("hH3LMassBefSel"), massH3L); + qaRegistry.fill(HIST("hH4LMassBefSel"), massH4L); if (!trackedClSize.empty() && trackedClSize[hypCand.v0ID] > 0) { - hH3LMassTracked->Fill(massH3L); - hH4LMassTracked->Fill(massH4L); + qaRegistry.fill(HIST("hH3LMassTracked"), massH3L); + qaRegistry.fill(HIST("hH4LMassTracked"), massH4L); } hypCand.dcaV0dau = std::sqrt(fitter.getChi2AtPCACandidate()); - if (hypCand.dcaV0dau > dcav0dau) { + if (hypCand.dcaV0dau > dcaV0Dau) { return; } @@ -650,13 +626,13 @@ struct hyperRecoTask { return; } - if (cosPAmax < v0cospacut) { + if (cosPAmax < v0CosPaCut) { return; } auto collision = collisions.rawIteratorAt(collIDmax); std::array primVtx = {collision.posX(), collision.posY(), collision.posZ()}; - for (int i = 0; i < 3; i++) { + for (std::size_t i = 0; i < hypCand.decVtx.size(); i++) { hypCand.decVtx[i] = hypCand.decVtx[i] - primVtx[i]; } @@ -683,8 +659,8 @@ struct hyperRecoTask { hypCand.massTOFHe3 = hypCand.momHe3TPC * 2.f * std::sqrt(1.f / (beta * beta) - 1.f); } - hDeDx3HeSel->Fill(heTrack.sign() * hypCand.momHe3TPC, heTrack.tpcSignal()); - hNsigma3HeSel->Fill(heTrack.sign() * hypCand.momHe3TPC, hypCand.nSigmaHe3); + qaRegistry.fill(HIST("hDeDx3HeSel"), heTrack.sign() * hypCand.momHe3TPC, heTrack.tpcSignal()); + qaRegistry.fill(HIST("hNsigma3HeSel"), heTrack.sign() * hypCand.momHe3TPC, hypCand.nSigmaHe3); hyperCandidates.push_back(hypCand); } @@ -700,8 +676,9 @@ struct hyperRecoTask { auto posTrack = tracks.rawIteratorAt(v0.posTrackId()); auto negTrack = tracks.rawIteratorAt(v0.negTrackId()); - if (std::abs(posTrack.eta()) > etaMax || std::abs(negTrack.eta()) > etaMax) + if (std::abs(posTrack.eta()) > etaMax || std::abs(negTrack.eta()) > etaMax) { continue; + } // temporary fix: tpcInnhRecoCentralityColvsFT0CmultiplicityerParam() returns the momentum in all the software tags before: https://github.com/AliceO2Group/AliceO2/pull/12521 auto nSigmaTPCpos = computeNSigmaHe3(posTrack); @@ -740,28 +717,34 @@ struct hyperRecoTask { for (const auto& track : tracks) { - if (std::abs(track.eta()) > etaMax) + if (std::abs(track.eta()) > etaMax) { continue; + } - if (!track.hasITS()) + if (!track.hasITS()) { continue; + } auto nSigmaHe = computeNSigmaHe3(track); bool isHe = nSigmaHe > -1 * nSigmaMaxHe; int pdgHypo = isHe ? heDauPdg : PDG_t::kPiPlus; // LOG(info) << "ncls found: " << track.tpcNClsFound(); - if (isHe && track.tpcNClsFound() < nTPCClusMinHe) + if (isHe && track.tpcNClsFound() < nTPCClusMinHe) { continue; - if (!isHe && track.tpcNClsFound() < nTPCClusMinPi) + } + if (!isHe && track.tpcNClsFound() < nTPCClusMinPi) { continue; - if (isHe && track.tpcNClsCrossedRows() < nTPCCrossedRowsMinHe) + } + if (isHe && track.tpcNClsCrossedRows() < nTPCCrossedRowsMinHe) { continue; - if (!isHe && track.tpcNClsCrossedRows() < nTPCCrossedRowsMinPi) + } + if (!isHe && track.tpcNClsCrossedRows() < nTPCCrossedRowsMinPi) { continue; + } svCreator.appendTrackCand(track, collisions, pdgHypo, ambiguousTracks, bcs); } - auto& svPool = svCreator.getSVCandPool(collisions); + auto& svPool = svCreator.getSVCandPool(collisions, useLikeSignPairs); LOG(debug) << "SV pool size: " << svPool.size(); for (const auto& svCand : svPool) { @@ -775,7 +758,7 @@ struct hyperRecoTask { } void fillMCinfo(aod::McTrackLabels const& trackLabels, aod::McParticles const&) { - for (auto& hypCand : hyperCandidates) { + for (auto& hypCand : hyperCandidates) { // o2-linter: disable=const-ref-in-for-loop (candidate is enriched with MC information in this loop) auto mcLabHe = trackLabels.rawIteratorAt(hypCand.heTrackID); auto mcLabPi = trackLabels.rawIteratorAt(hypCand.piTrackID); @@ -785,24 +768,27 @@ struct hyperRecoTask { if (mcTrackHe.has_mothers() && mcTrackPi.has_mothers()) { for (const auto& heMother : mcTrackHe.mothers_as()) { for (const auto& piMother : mcTrackPi.mothers_as()) { - if (heMother.globalIndex() != piMother.globalIndex()) + if (heMother.globalIndex() != piMother.globalIndex()) { continue; - if (std::abs(mcTrackHe.pdgCode()) != heDauPdg || std::abs(mcTrackPi.pdgCode()) != PDG_t::kPiPlus) + } + if (std::abs(mcTrackHe.pdgCode()) != heDauPdg || std::abs(mcTrackPi.pdgCode()) != PDG_t::kPiPlus) { continue; - if (std::abs(heMother.pdgCode()) != hyperPdg) + } + if (std::abs(heMother.pdgCode()) != hyperPdg) { continue; + } auto primVtx = std::array{heMother.vx(), heMother.vy(), heMother.vz()}; auto secVtx = std::array{mcTrackHe.vx(), mcTrackHe.vy(), mcTrackHe.vz()}; hypCand.gMom = std::array{heMother.px(), heMother.py(), heMother.pz()}; hypCand.gMomHe3 = std::array{mcTrackHe.px(), mcTrackHe.py(), mcTrackHe.pz()}; - for (int i = 0; i < 3; i++) { + for (std::size_t i = 0; i < hypCand.gDecVtx.size(); i++) { hypCand.gDecVtx[i] = secVtx[i] - primVtx[i]; } hypCand.isSignal = true; hypCand.isFakeHeOnITSLayer = mcLabHe.mcMask() & 0x7F; // check if any of the first 7 bits is set hypCand.pdgCode = heMother.pdgCode(); - hypCand.isRecoMCCollision = recoCollisionIds[heMother.mcCollisionId()] > 0; + hypCand.isRecoMCCollision = recoCollisionIds[heMother.mcCollisionId()] >= 0; hypCand.isSurvEvSelection = isSurvEvSelCollision[heMother.mcCollisionId()]; filledMothers.push_back(heMother.globalIndex()); } @@ -812,6 +798,143 @@ struct hyperRecoTask { } } + template + void fillMCCandidate(int64_t collisionId, int64_t heTrackId, int64_t piTrackId, Columns const&... columns) + { + if constexpr (withCollID) { + outputMCTableWithCollID(collisionId, heTrackId, piTrackId, columns...); + } else { + outputMCTable(columns...); + } + } + + template + void runMC(CollisionsFullMC const& collisions, aod::McCollisions const& mcCollisions, aod::V0s const& V0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs, aod::McTrackLabels const& trackLabelsMC, aod::McParticles const& particlesMC) + { + filledMothers.clear(); + recoCollisionIds.clear(); + recoCollisionIds.resize(mcCollisions.size(), -1); + isSurvEvSelCollision.clear(); + isSurvEvSelCollision.resize(mcCollisions.size(), false); + goodCollision.clear(); + goodCollision.resize(collisions.size(), false); + hyperCandidates.clear(); + + selectGoodCollisionsMC(collisions); + useCustomVertexer ? fillCustomV0s(collisions, tracks, ambiTracks, bcs) : fillV0s(collisions, tracks, V0s); + fillMCinfo(trackLabelsMC, particlesMC); + for (const auto& hypCand : hyperCandidates) { + auto collision = collisions.rawIteratorAt(hypCand.collisionID); + if (!hypCand.isSignal && mcSignalOnly) { + continue; + } + int chargeFactor = -1 + 2 * (hypCand.pdgCode > 0); + float trackedHypClSize = !trackedClSize.empty() ? trackedClSize[hypCand.v0ID] : 0; + fillMCCandidate(hypCand.collisionID, hypCand.heTrackID, hypCand.piTrackID, + collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), + collision.posX(), collision.posY(), collision.posZ(), + mRunNumber, hypCand.isMatter, + hypCand.recoPtHe3(), hypCand.recoPhiHe3(), hypCand.recoEtaHe3(), + hypCand.recoPtPi(), hypCand.recoPhiPi(), hypCand.recoEtaPi(), + hypCand.decVtx[0], hypCand.decVtx[1], hypCand.decVtx[2], + hypCand.dcaV0dau, hypCand.he3DCAXY, hypCand.piDCAXY, + hypCand.nSigmaHe3, hypCand.nTPCClustersHe3, hypCand.nTPCClustersPi, hypCand.nTPCpidClusHe3, hypCand.nTPCpidClusPi, hypCand.nTPCCrossedRowsHe3, hypCand.nTPCCrossedRowsPi, + hypCand.momHe3TPC, hypCand.momPiTPC, hypCand.tpcSignalHe3, hypCand.tpcSignalPi, hypCand.tpcChi2He3, hypCand.itsChi2He3, hypCand.itsChi2Pi, + hypCand.massTOFHe3, + hypCand.clusterSizeITSHe3, hypCand.clusterSizeITSPi, hypCand.flags, trackedHypClSize, + chargeFactor * hypCand.genPt(), hypCand.genPhi(), hypCand.genEta(), hypCand.genPtHe3(), + hypCand.gDecVtx[0], hypCand.gDecVtx[1], hypCand.gDecVtx[2], + hypCand.isReco, hypCand.isFakeHeOnITSLayer, hypCand.isSignal, hypCand.isRecoMCCollision, hypCand.isSurvEvSelection, 1, 0); + } + + // now we fill only the signal candidates that were not reconstructed + for (const auto& mcPart : particlesMC) { + + if (std::abs(mcPart.pdgCode()) != hyperPdg) { + continue; + } + std::array secVtx{0.f, 0.f, 0.f}; + std::array lastDaugVtx{0.f, 0.f, 0.f}; + std::array primVtx = {mcPart.vx(), mcPart.vy(), mcPart.vz()}; + std::array momMother = {mcPart.px(), mcPart.py(), mcPart.pz()}; + std::array momHe3{0.f, 0.f, 0.f}; + bool isHeFound = false; + int mcProcess = {0}; + for (const auto& mcDaught : mcPart.daughters_as()) { + if (mcDaught.pdgCode() != PDG_t::kElectron) { // we do not care about delta electrons + lastDaugVtx = {mcDaught.vx(), mcDaught.vy(), mcDaught.vz()}; + mcProcess = mcDaught.getProcess(); + } + if (std::abs(mcDaught.pdgCode()) == heDauPdg) { + secVtx = lastDaugVtx; + momHe3 = {mcDaught.px(), mcDaught.py(), mcDaught.pz()}; + isHeFound = true; + } + } + if (mcPart.pdgCode() > 0) { + qaRegistry.fill(HIST("hIsMatterGen"), 0.); + } else { + qaRegistry.fill(HIST("hIsMatterGen"), 1.); + } + if (!isHeFound) { + qaRegistry.fill(HIST("hDecayChannel"), 1.); + } + qaRegistry.fill(HIST("hDecayChannel"), 0.); + if (mcPart.pdgCode() > 0) { + qaRegistry.fill(HIST("hIsMatterGenTwoBody"), 0.); + } else { + qaRegistry.fill(HIST("hIsMatterGenTwoBody"), 1.); + } + if (std::find(filledMothers.begin(), filledMothers.end(), mcPart.globalIndex()) != std::end(filledMothers)) { + continue; + } + hyperCandidate hypCand; + hypCand.pdgCode = mcPart.pdgCode(); + hypCand.isRecoMCCollision = recoCollisionIds[mcPart.mcCollisionId()] >= 0; + hypCand.isSurvEvSelection = isSurvEvSelCollision[mcPart.mcCollisionId()]; + int chargeFactor = -1 + 2 * (hypCand.pdgCode > 0); + for (std::size_t i = 0; i < hypCand.gDecVtx.size(); i++) { + hypCand.gDecVtx[i] = (isHeFound ? secVtx[i] : lastDaugVtx[i]) - primVtx[i]; + hypCand.gMom[i] = momMother[i]; + hypCand.gMomHe3[i] = momHe3[i]; + } + hypCand.heTrackID = -1; + hypCand.piTrackID = -1; + hypCand.isSignal = true; + + float centFT0A = -1, centFT0C = -1, centFT0M = -1; + int trackOccupancyInTimeRange = -1; + float ft0cOccupancyInTimeRange = -1.f; + int recoCollisionId = -1; + if (hypCand.isRecoMCCollision) { + recoCollisionId = recoCollisionIds[mcPart.mcCollisionId()]; + auto recoCollision = collisions.rawIteratorAt(recoCollisionId); + centFT0A = recoCollision.centFT0A(); + centFT0C = recoCollision.centFT0C(); + centFT0M = recoCollision.centFT0M(); + trackOccupancyInTimeRange = recoCollision.trackOccupancyInTimeRange(); + ft0cOccupancyInTimeRange = recoCollision.ft0cOccupancyInTimeRange(); + } + + fillMCCandidate(recoCollisionId, -1, -1, + centFT0A, centFT0C, centFT0M, + trackOccupancyInTimeRange, ft0cOccupancyInTimeRange, + primVtx[0], primVtx[1], primVtx[2], + mRunNumber, 0, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 0, 0, 0, 0, + -1, -1, -1, false, + chargeFactor * hypCand.genPt(), hypCand.genPhi(), hypCand.genEta(), hypCand.genPtHe3(), + hypCand.gDecVtx[0], hypCand.gDecVtx[1], hypCand.gDecVtx[2], + hypCand.isReco, -1, hypCand.isSignal, hypCand.isRecoMCCollision, hypCand.isSurvEvSelection, isHeFound, mcProcess); + } + } + void processDataTracked(CollisionsFull const& collisions, aod::V0s const& V0s, aod::TrackedV0s const& tV0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs) { trackedClSize.clear(); @@ -821,7 +944,7 @@ struct hyperRecoTask { } processData(collisions, V0s, tracks, ambiTracks, bcs); } - PROCESS_SWITCH(hyperRecoTask, processDataTracked, "Data analysis wit tracked V0s information", false); + PROCESS_SWITCH(HyperRecoTask, processDataTracked, "Data analysis wit tracked V0s information", false); void processData(CollisionsFull const& collisions, aod::V0s const& V0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs) { @@ -836,6 +959,7 @@ struct hyperRecoTask { auto collision = collisions.rawIteratorAt(hypCand.collisionID); float trackedHypClSize = !trackedClSize.empty() ? trackedClSize[hypCand.v0ID] : 0; outputDataTable(collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), collision.posX(), collision.posY(), collision.posZ(), mRunNumber, hypCand.isMatter, hypCand.recoPtHe3(), hypCand.recoPhiHe3(), hypCand.recoEtaHe3(), @@ -850,7 +974,7 @@ struct hyperRecoTask { hypCand.clusterSizeITSHe3, hypCand.clusterSizeITSPi, hypCand.flags, trackedHypClSize); } } - PROCESS_SWITCH(hyperRecoTask, processData, "Data analysis", true); + PROCESS_SWITCH(HyperRecoTask, processData, "Data analysis", true); void processDataWithFlow(CollisionsFullWithFlow const& collisions, aod::V0s const& V0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs) { @@ -869,6 +993,7 @@ struct hyperRecoTask { } float trackedHypClSize = !trackedClSize.empty() ? trackedClSize[hypCand.v0ID] : 0; outputDataTableWithFlow(collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), collision.psiFT0A(), collision.multFT0A(), collision.psiFT0C(), collision.multFT0C(), collision.qFT0C(), collision.psiTPC(), collision.multTPC(), @@ -886,7 +1011,7 @@ struct hyperRecoTask { hypCand.clusterSizeITSHe3, hypCand.clusterSizeITSPi, hypCand.flags, trackedHypClSize); } } - PROCESS_SWITCH(hyperRecoTask, processDataWithFlow, "Data analysis with flow", false); + PROCESS_SWITCH(HyperRecoTask, processDataWithFlow, "Data analysis with flow", false); void processDataWithCollID(CollisionsFull const& collisions, aod::V0s const& V0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs) { @@ -900,7 +1025,9 @@ struct hyperRecoTask { for (const auto& hypCand : hyperCandidates) { auto collision = collisions.rawIteratorAt(hypCand.collisionID); float trackedHypClSize = !trackedClSize.empty() ? trackedClSize[hypCand.v0ID] : 0; - outputDataTableWithCollID(hypCand.collisionID, collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + outputDataTableWithCollID(hypCand.collisionID, hypCand.heTrackID, hypCand.piTrackID, + collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), collision.posX(), collision.posY(), collision.posZ(), mRunNumber, hypCand.isMatter, hypCand.recoPtHe3(), hypCand.recoPhiHe3(), hypCand.recoEtaHe3(), @@ -915,120 +1042,19 @@ struct hyperRecoTask { hypCand.clusterSizeITSHe3, hypCand.clusterSizeITSPi, hypCand.flags, trackedHypClSize); } } - PROCESS_SWITCH(hyperRecoTask, processDataWithCollID, "Data analysis with collision ID", false); + PROCESS_SWITCH(HyperRecoTask, processDataWithCollID, "Data analysis with collision ID", false); void processMC(CollisionsFullMC const& collisions, aod::McCollisions const& mcCollisions, aod::V0s const& V0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs, aod::McTrackLabels const& trackLabelsMC, aod::McParticles const& particlesMC) { - filledMothers.clear(); - recoCollisionIds.clear(); - recoCollisionIds.resize(mcCollisions.size(), -1); - isSurvEvSelCollision.clear(); - isSurvEvSelCollision.resize(mcCollisions.size(), false); - goodCollision.clear(); - goodCollision.resize(collisions.size(), false); - hyperCandidates.clear(); - - selectGoodCollisionsMC(collisions); - useCustomVertexer ? fillCustomV0s(collisions, tracks, ambiTracks, bcs) : fillV0s(collisions, tracks, V0s); - fillMCinfo(trackLabelsMC, particlesMC); - for (const auto& hypCand : hyperCandidates) { - auto collision = collisions.rawIteratorAt(hypCand.collisionID); - if (!hypCand.isSignal && mcSignalOnly) - continue; - int chargeFactor = -1 + 2 * (hypCand.pdgCode > 0); - float trackedHypClSize = !trackedClSize.empty() ? trackedClSize[hypCand.v0ID] : 0; - outputMCTable(collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), - collision.posX(), collision.posY(), collision.posZ(), - mRunNumber, hypCand.isMatter, - hypCand.recoPtHe3(), hypCand.recoPhiHe3(), hypCand.recoEtaHe3(), - hypCand.recoPtPi(), hypCand.recoPhiPi(), hypCand.recoEtaPi(), - hypCand.decVtx[0], hypCand.decVtx[1], hypCand.decVtx[2], - hypCand.dcaV0dau, hypCand.he3DCAXY, hypCand.piDCAXY, - hypCand.nSigmaHe3, hypCand.nTPCClustersHe3, hypCand.nTPCClustersPi, hypCand.nTPCpidClusHe3, hypCand.nTPCpidClusPi, hypCand.nTPCCrossedRowsHe3, hypCand.nTPCCrossedRowsPi, - hypCand.momHe3TPC, hypCand.momPiTPC, hypCand.tpcSignalHe3, hypCand.tpcSignalPi, hypCand.tpcChi2He3, hypCand.itsChi2He3, hypCand.itsChi2Pi, - hypCand.massTOFHe3, - hypCand.clusterSizeITSHe3, hypCand.clusterSizeITSPi, hypCand.flags, trackedHypClSize, - chargeFactor * hypCand.genPt(), hypCand.genPhi(), hypCand.genEta(), hypCand.genPtHe3(), - hypCand.gDecVtx[0], hypCand.gDecVtx[1], hypCand.gDecVtx[2], - hypCand.isReco, hypCand.isFakeHeOnITSLayer, hypCand.isSignal, hypCand.isRecoMCCollision, hypCand.isSurvEvSelection, 1, 0); - } - - // now we fill only the signal candidates that were not reconstructed - for (const auto& mcPart : particlesMC) { - - if (std::abs(mcPart.pdgCode()) != hyperPdg) - continue; - std::array secVtx; - std::array primVtx = {mcPart.vx(), mcPart.vy(), mcPart.vz()}; - std::array momMother = {mcPart.px(), mcPart.py(), mcPart.pz()}; - std::array momHe3; - bool isHeFound = false; - int mcProcess = {0}; - for (const auto& mcDaught : mcPart.daughters_as()) { - if (std::abs(mcDaught.pdgCode()) == heDauPdg) { - secVtx = {mcDaught.vx(), mcDaught.vy(), mcDaught.vz()}; - momHe3 = {mcDaught.px(), mcDaught.py(), mcDaught.pz()}; - isHeFound = true; - } - if (mcDaught.pdgCode() != PDG_t::kElectron) { // we do not care about delta electrons - mcProcess = mcDaught.getProcess(); - } - } - if (mcPart.pdgCode() > 0) { - hIsMatterGen->Fill(0.); - } else { - hIsMatterGen->Fill(1.); - } - if (!isHeFound) { - hDecayChannel->Fill(1.); - } - hDecayChannel->Fill(0.); - if (mcPart.pdgCode() > 0) { - hIsMatterGenTwoBody->Fill(0.); - } else { - hIsMatterGenTwoBody->Fill(1.); - } - if (std::find(filledMothers.begin(), filledMothers.end(), mcPart.globalIndex()) != std::end(filledMothers)) { - continue; - } - hyperCandidate hypCand; - hypCand.pdgCode = mcPart.pdgCode(); - hypCand.isRecoMCCollision = recoCollisionIds[mcPart.mcCollisionId()] > 0; - hypCand.isSurvEvSelection = isSurvEvSelCollision[mcPart.mcCollisionId()]; - int chargeFactor = -1 + 2 * (hypCand.pdgCode > 0); - for (int i = 0; i < 3; i++) { - hypCand.gDecVtx[i] = secVtx[i] - primVtx[i]; - hypCand.gMom[i] = momMother[i]; - hypCand.gMomHe3[i] = momHe3[i]; - } - hypCand.heTrackID = -1; - hypCand.piTrackID = -1; - hypCand.isSignal = true; - - float centFT0A = -1, centFT0C = -1, centFT0M = -1; - if (hypCand.isRecoMCCollision) { - auto recoCollision = collisions.rawIteratorAt(recoCollisionIds[mcPart.mcCollisionId()]); - centFT0A = recoCollision.centFT0A(); - centFT0C = recoCollision.centFT0C(); - centFT0M = recoCollision.centFT0M(); - } + runMC(collisions, mcCollisions, V0s, tracks, ambiTracks, bcs, trackLabelsMC, particlesMC); + } + PROCESS_SWITCH(HyperRecoTask, processMC, "MC analysis", false); - outputMCTable(centFT0A, centFT0C, centFT0M, - -1, -1, -1, - mRunNumber, 0, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 0, 0, 0, 0, - -1, -1, -1, false, - chargeFactor * hypCand.genPt(), hypCand.genPhi(), hypCand.genEta(), hypCand.genPtHe3(), - hypCand.gDecVtx[0], hypCand.gDecVtx[1], hypCand.gDecVtx[2], - hypCand.isReco, -1, hypCand.isSignal, hypCand.isRecoMCCollision, hypCand.isSurvEvSelection, isHeFound, mcProcess); - } + void processMCWithCollID(CollisionsFullMC const& collisions, aod::McCollisions const& mcCollisions, aod::V0s const& V0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs, aod::McTrackLabels const& trackLabelsMC, aod::McParticles const& particlesMC) + { + runMC(collisions, mcCollisions, V0s, tracks, ambiTracks, bcs, trackLabelsMC, particlesMC); } - PROCESS_SWITCH(hyperRecoTask, processMC, "MC analysis", false); + PROCESS_SWITCH(HyperRecoTask, processMCWithCollID, "MC analysis with collision ID", false); void processMCTracked(CollisionsFullMC const& collisions, aod::McCollisions const& mcCollisions, aod::V0s const& V0s, aod::TrackedV0s const& tV0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiTracks, aod::BCsWithTimestamps const& bcs, aod::McTrackLabels const& trackLabelsMC, aod::McParticles const& particlesMC) { @@ -1039,80 +1065,93 @@ struct hyperRecoTask { } processMC(collisions, mcCollisions, V0s, tracks, ambiTracks, bcs, trackLabelsMC, particlesMC); } - PROCESS_SWITCH(hyperRecoTask, processMCTracked, "MC analysis with tracked V0s", false); + PROCESS_SWITCH(HyperRecoTask, processMCTracked, "MC analysis with tracked V0s", false); template bool passEvtSel(const CollType& collision) { - if (!collision.sel8()) + if (!collision.sel8()) { return false; + } - if ((std::abs(collision.posZ())) > 10) + if ((std::abs(collision.posZ())) > MaxAbsVertexZ) { return false; + } - if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) + if (cfgEvSelkNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; + } - if (cfgEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + if (cfgEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; + } return true; } void processEventLossMC(McCollisionMults::iterator const& mcCollision, soa::SmallGroups const& collisions, aod::McParticles const& GenParticles) { - if (std::abs(mcCollision.posZ()) > 10) { + if (std::abs(mcCollision.posZ()) > MaxAbsVertexZ) { return; } //////////// Event loss estimation via impact parameter and multiplicity by MCFT0C // Fill all generated events - hEvtMC->Fill(0); - hImpactParamGen->Fill(mcCollision.impactParameter()); - hGenEventsNchEta05->Fill(mcCollision.multMCNParticlesEta05(), 0); - hGenEventsNchEta08->Fill(mcCollision.multMCNParticlesEta08(), 0); + qaRegistry.fill(HIST("QAEvent/hEvtMC"), 0); + qaRegistry.fill(HIST("QAEvent/McColAll/hImpactParamGen"), mcCollision.impactParameter()); + qaRegistry.fill(HIST("QAEvent/hGenEventsNchEta05"), mcCollision.multMCNParticlesEta05(), 0); + qaRegistry.fill(HIST("QAEvent/hGenEventsNchEta08"), mcCollision.multMCNParticlesEta08(), 0); // Fill generated events with no reconstructed collisions if (collisions.size() == 0) { - hEvtMC->Fill(1); + qaRegistry.fill(HIST("QAEvent/hEvtMC"), 1); } // Define the generated events with at least one reconstructed event bool atLeastOneRecoEvt = false; auto centralityFT0C = -999.; + int biggestNContribs = -1; for (auto const& col : collisions) { if (!passEvtSel(col)) { continue; } - centralityFT0C = col.centFT0C(); + // In case of multiple reconstructed collisions associated to the same generated one, only consider the one with the biggest number of contributors + if (biggestNContribs < col.numContrib()) { + biggestNContribs = col.numContrib(); + centralityFT0C = col.centFT0C(); + } atLeastOneRecoEvt = true; + qaRegistry.fill(HIST("QAEvent/McColAll/hImpactParamReco"), mcCollision.impactParameter()); + qaRegistry.fill(HIST("QAEvent/McColAll/hRecoCentrality"), col.centFT0C()); + qaRegistry.fill(HIST("QAEvent/McColAll/hRecoCentralityColvsMultiplicityRecoEta05"), col.centFT0C(), mcCollision.multMCNParticlesEta05()); + qaRegistry.fill(HIST("QAEvent/McColAll/hRecoCentralityColvsMultiplicityRecoEta08"), col.centFT0C(), mcCollision.multMCNParticlesEta08()); + qaRegistry.fill(HIST("QAEvent/McColAll/hRecoCentralityColvsImpactParamReco"), col.centFT0C(), mcCollision.impactParameter()); + qaRegistry.fill(HIST("QAEvent/McColAll/hRecoCentralityColvsFT0Cmultiplicity"), col.centFT0C(), mcCollision.multMCFT0C()); } - hGenCentralityColvsMultiplicityGenEta05->Fill(centralityFT0C, mcCollision.multMCNParticlesEta05()); - hGenCentralityColvsMultiplicityGenEta08->Fill(centralityFT0C, mcCollision.multMCNParticlesEta08()); - hGenCentralityColvsImpactParamGen->Fill(centralityFT0C, mcCollision.impactParameter()); - hGenCentralityColvsFT0Cmultiplicity->Fill(centralityFT0C, mcCollision.multMCFT0C()); - if (atLeastOneRecoEvt) { - hEvtMC->Fill(2); - hGenEventsNchEta05->Fill(mcCollision.multMCNParticlesEta05(), 1); - hGenEventsNchEta08->Fill(mcCollision.multMCNParticlesEta08(), 1); - hImpactParamReco->Fill(mcCollision.impactParameter()); - hRecoCentralityColvsMultiplicityRecoEta05->Fill(centralityFT0C, mcCollision.multMCNParticlesEta05()); - hRecoCentralityColvsMultiplicityRecoEta08->Fill(centralityFT0C, mcCollision.multMCNParticlesEta08()); - hRecoCentralityColvsImpactParamReco->Fill(centralityFT0C, mcCollision.impactParameter()); - hRecoCentralityColvsFT0Cmultiplicity->Fill(centralityFT0C, mcCollision.multMCFT0C()); + qaRegistry.fill(HIST("QAEvent/hEvtMC"), 2); + qaRegistry.fill(HIST("QAEvent/hGenEventsNchEta05"), mcCollision.multMCNParticlesEta05(), 1); + qaRegistry.fill(HIST("QAEvent/hGenEventsNchEta08"), mcCollision.multMCNParticlesEta08(), 1); + qaRegistry.fill(HIST("QAEvent/McColPassedEvSel/hImpactParamGenOneReco"), mcCollision.impactParameter()); + qaRegistry.fill(HIST("QAEvent/McColPassedEvSel/hGenOneRecoCentrality"), centralityFT0C); + qaRegistry.fill(HIST("QAEvent/McColPassedEvSel/hGenCentralityColvsMultiplicityGenEta05"), centralityFT0C, mcCollision.multMCNParticlesEta05()); + qaRegistry.fill(HIST("QAEvent/McColPassedEvSel/hGenCentralityColvsMultiplicityGenEta08"), centralityFT0C, mcCollision.multMCNParticlesEta08()); + qaRegistry.fill(HIST("QAEvent/McColPassedEvSel/hGenCentralityColvsImpactParamGen"), centralityFT0C, mcCollision.impactParameter()); + qaRegistry.fill(HIST("QAEvent/McColPassedEvSel/hGenCentralityColvsFT0Cmultiplicity"), centralityFT0C, mcCollision.multMCFT0C()); } // Construct the H3L 4-vector based on the generated daugthers identification by PDG ROOT::Math::PxPyPzMVector daugh1, daugh2, mother; for (const auto& genParticle : GenParticles) { - if (std::abs(genParticle.y()) > 1) + if (std::abs(genParticle.y()) > 1) { continue; - if (std::abs(genParticle.pdgCode()) != hyperPdg) + } + if (std::abs(genParticle.pdgCode()) != hyperPdg) { continue; + } auto daughters = genParticle.daughters_as(); @@ -1135,34 +1174,35 @@ struct hyperRecoTask { } } // Check pairs to avoid wrong charge associations - if (!((dauHe3 && dauPiMinus) || !(dauAntiHe3 && dauPiPos))) + if (!((dauHe3 && dauPiMinus) || !(dauAntiHe3 && dauPiPos))) { continue; + } mother = daugh1 + daugh2; // Fill informations for generated 3HL in all generated events - hGen3HLBeforeEvtSel->Fill(mother.pt()); - hGen3HLvsImpactParameterBeforeEvtSel->Fill(mother.pt(), mcCollision.impactParameter()); - hGen3HLvsMultiplicityGenEta05BeforeEvtSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta05()); - hGen3HLvsMultiplicityGenEta08BeforeEvtSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta08()); - hGen3HLvsMultiplicityFT0CBeforeEvtSel->Fill(mother.pt(), mcCollision.multMCFT0C()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLBeforeEvtSel"), mother.pt()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsImpactParameterBeforeEvtSel"), mother.pt(), mcCollision.impactParameter()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta05BeforeEvtSel"), mother.pt(), mcCollision.multMCNParticlesEta05()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta08BeforeEvtSel"), mother.pt(), mcCollision.multMCNParticlesEta08()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsMultiplicityFT0CBeforeEvtSel"), mother.pt(), mcCollision.multMCFT0C()); // Fill informations for generated 3HL in generated events with at least one reconstructed event if (atLeastOneRecoEvt) { - hGen3HLAfterSel->Fill(mother.pt()); - hGen3HLvsImpactParameterAfterSel->Fill(mother.pt(), mcCollision.impactParameter()); - hGen3HLvsMultiplicityGenEta05AfterSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta05()); - hGen3HLvsMultiplicityGenEta08AfterSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta08()); - hGen3HLvsMultiplicityFT0CAfterSel->Fill(mother.pt(), mcCollision.multMCFT0C()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLAfterSel"), mother.pt()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsImpactParameterAfterSel"), mother.pt(), mcCollision.impactParameter()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta05AfterSel"), mother.pt(), mcCollision.multMCNParticlesEta05()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsMultiplicityGenEta08AfterSel"), mother.pt(), mcCollision.multMCNParticlesEta08()); + qaRegistry.fill(HIST("QAEvent/McCol3HL/hGen3HLvsMultiplicityFT0CAfterSel"), mother.pt(), mcCollision.multMCFT0C()); } } } - PROCESS_SWITCH(hyperRecoTask, processEventLossMC, "Event loss analysis", false); + PROCESS_SWITCH(HyperRecoTask, processEventLossMC, "Event loss analysis", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/TableProducer/Nuspex/hyperkinkRecoTask.cxx b/PWGLF/TableProducer/Nuspex/hyperkinkRecoTask.cxx index cc3be8f90d8..cff24b20ee9 100644 --- a/PWGLF/TableProducer/Nuspex/hyperkinkRecoTask.cxx +++ b/PWGLF/TableProducer/Nuspex/hyperkinkRecoTask.cxx @@ -20,6 +20,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/MetadataHelper.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" #include "Common/DataModel/EventSelection.h" @@ -41,7 +42,6 @@ #include #include #include -#include #include #include @@ -1162,7 +1162,7 @@ struct HyperkinkQa { // QA for mother track selection template - bool motherTrackCheck(const TTrack& track, const std::shared_ptr hist) + bool motherTrackCheck(const TTrack& track, const std::shared_ptr& hist) { hist->Fill(1); @@ -1206,7 +1206,7 @@ struct HyperkinkQa { // qa for daughter track selection template - bool daughterTrackCheck(const TTrack& track, const std::shared_ptr hist, float tpcNSigma) + bool daughterTrackCheck(const TTrack& track, const std::shared_ptr& hist, float tpcNSigma) { hist->Fill(1); diff --git a/PWGLF/TableProducer/Nuspex/LFTreeCreatorNuclei.cxx b/PWGLF/TableProducer/Nuspex/lfTreeCreatorNuclei.cxx similarity index 89% rename from PWGLF/TableProducer/Nuspex/LFTreeCreatorNuclei.cxx rename to PWGLF/TableProducer/Nuspex/lfTreeCreatorNuclei.cxx index 4a255202ea5..438f082f070 100644 --- a/PWGLF/TableProducer/Nuspex/LFTreeCreatorNuclei.cxx +++ b/PWGLF/TableProducer/Nuspex/lfTreeCreatorNuclei.cxx @@ -10,13 +10,12 @@ // or submit itself to any jurisdiction. /// -/// \file LFTreeCreatorNuclei.cxx +/// \file lfTreeCreatorNuclei.cxx /// \brief Writer of the nuclei candidates in the form of flat tables to be stored in TTrees. /// Intended for debug or for the local optimization of analysis on small samples. /// In this file are defined and filled the output tables /// /// \author Nicolò Jacazio and Francesca Bellini -/// #include "PWGLF/DataModel/LFNucleiTables.h" #include "PWGLF/DataModel/LFParticleIdentification.h" @@ -26,6 +25,7 @@ #include "Common/Core/TrackSelectionDefaults.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -92,7 +92,7 @@ struct LfTreeCreatorNuclei { Configurable cfgHighCutVertex{"cfgHighCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgLowCutVertex{"cfgLowCutVertex", -10.0f, "Accepted z-vertex range"}; Configurable useSel8{"useSel8", true, "Use Sel8 for run3 Event Selection"}; - Configurable TVXtrigger{"TVXtrigger", false, "Use TVX for Event Selection (default w/ Sel8)"}; + Configurable useTVXtrigger{"useTVXtrigger", false, "Use TVX for Event Selection (default w/ Sel8)"}; Configurable removeTFBorder{"removeTFBorder", false, "Remove TimeFrame border (default w/ Sel8)"}; Configurable removeITSROFBorder{"removeITSROFBorder", false, "Remove ITS Read-Out Frame border (default w/ Sel8)"}; @@ -113,12 +113,12 @@ struct LfTreeCreatorNuclei { Filter collisionFilter = (aod::collision::posZ < cfgHighCutVertex && aod::collision::posZ > cfgLowCutVertex); Filter etaFilter = (nabs(aod::track::eta) < cfgCutEta); - Filter trackFilter = (trackSelType.value == 0 && requireGlobalTrackWoDCAInFilter()) || - (trackSelType.value == 1 && requireGlobalTrackInFilter()) || - (trackSelType.value == 2) || - (trackSelType.value == 3); - Filter DCAcutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); - using EventCandidates = soa::Join; + Filter trackFilter = (trackSelType.value == 0 && requireGlobalTrackWoDCAInFilter()) || // o2-linter: disable=magic-number (filters) + (trackSelType.value == 1 && requireGlobalTrackInFilter()) || // o2-linter: disable=magic-number (filters) + (trackSelType.value == 2) || // o2-linter: disable=magic-number (filters) + (trackSelType.value == 3); // o2-linter: disable=magic-number (filters) + Filter dcaFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + using EventCandidates = soa::Join; using TrackCandidates = soa::Join ptcutHigh.value)) { continue; } - if ((TMath::Abs(track.tpcNSigmaDe()) > filterDeTPC.value) && (TMath::Abs(track.tpcNSigmaHe()) > filterHeTPC.value)) { + if ((std::abs(track.tpcNSigmaDe()) > filterDeTPC.value) && (std::abs(track.tpcNSigmaHe()) > filterHeTPC.value)) { continue; } out = kTRUE; @@ -202,6 +202,7 @@ struct LfTreeCreatorNuclei { collision.posZ(), collision.centFV0A(), collision.centFT0M(), + collision.multNTracksPVeta1(), collision.sel8(), collision.bc().runNumber()); @@ -211,26 +212,26 @@ struct LfTreeCreatorNuclei { if constexpr (isMC) { tableCandidateMC.reserve(tracks.size()); } - for (auto& track : tracks) { + for (const auto& track : tracks) { if (track.itsNClsInnerBarrel() < nITSInnerBarrelHits) { continue; } - if (trackSelType.value == 2) { // custom track selection mode + if (trackSelType.value == 2) { // custom track selection mode // o2-linter: disable=magic-number (filters) if (!customTrackSelection.IsSelected(track)) { continue; } } - if (trackSelType.value == 3) { // Filtering mode + if (trackSelType.value == 3) { // Filtering mode // o2-linter: disable=magic-number (filters) if (!track.hasTPC()) { continue; } - if (track.tpcNClsCrossedRows() < 90) { + if (track.tpcNClsCrossedRows() < 90) { // o2-linter: disable=magic-number (filters) continue; } if ((track.pt() < ptcutLow.value) || (track.pt() > ptcutHigh.value)) { continue; } - if ((TMath::Abs(track.tpcNSigmaDe()) > filterDeTPC.value) && (TMath::Abs(track.tpcNSigmaHe()) > filterHeTPC.value)) { + if ((std::abs(track.tpcNSigmaDe()) > filterDeTPC.value) && (std::abs(track.tpcNSigmaHe()) > filterHeTPC.value)) { continue; } } @@ -250,7 +251,6 @@ struct LfTreeCreatorNuclei { track.tpcSignal(), track.pt(), track.eta(), track.phi(), track.sign(), - track.itsClusterSizes(), track.itsNCls(), track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), @@ -258,7 +258,8 @@ struct LfTreeCreatorNuclei { track.tpcChi2NCl(), track.itsChi2NCl(), track.itsClusterMap(), - track.isPVContributor()); + track.isPVContributor(), + track.itsClusterSizes()); tableCandidateExtra( track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), @@ -274,7 +275,7 @@ struct LfTreeCreatorNuclei { bool itsPassed = isITStrack(track); bool tpcPassed = isTPCtrack(track); bool hasFakeHit = false; - for (int i = 0; i < 10; i++) { // From ITS to TPC + for (int i = 0; i < 10; i++) { // From ITS to TPC // o2-linter: disable=magic-number (fake hit loop) if (track.mcMask() & 1 << i) { hasFakeHit = true; break; @@ -310,7 +311,7 @@ struct LfTreeCreatorNuclei { hEvents.fill(HIST("eventSelection"), 0); if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) { - if (TVXtrigger) + if (useTVXtrigger) continue; } else { hEvents.fill(HIST("eventSelection"), 1); @@ -346,11 +347,11 @@ struct LfTreeCreatorNuclei { hEvents.fill(HIST("eventSelection"), 6); // Fill the norm. column with good events with |z| < 10 cm before skimming - if (collision.posZ() < 10 && collision.posZ() > -10) { + if (collision.posZ() < cfgHighCutVertex && collision.posZ() > cfgLowCutVertex) { hEvents.fill(HIST("eventSelection"), 7); } - if (doSkim && (trackSelType.value == 3) && !checkQuality(collision, tracksInCollision)) + if (doSkim && (trackSelType.value == 3) && !checkQuality(collision, tracksInCollision)) // o2-linter: disable=magic-number (filters) continue; fillForOneEvent(collision, tracksInCollision); hEvents.fill(HIST("eventSelection"), 8); @@ -368,7 +369,7 @@ struct LfTreeCreatorNuclei { hEvents.fill(HIST("eventSelection"), 0); if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) { - if (TVXtrigger) + if (useTVXtrigger) continue; } else { hEvents.fill(HIST("eventSelection"), 1); @@ -399,7 +400,7 @@ struct LfTreeCreatorNuclei { hEvents.fill(HIST("eventSelection"), 5); // Fill the norm. column with good events with |z| < 10 cm before skimming - if (collision.posZ() < 10 && collision.posZ() > -10) { + if (collision.posZ() < cfgHighCutVertex && collision.posZ() > cfgLowCutVertex) { hEvents.fill(HIST("eventSelection"), 7); } diff --git a/PWGLF/TableProducer/Nuspex/lnnRecoTask.cxx b/PWGLF/TableProducer/Nuspex/lnnRecoTask.cxx index c5edd7f2043..fd53c9ab92c 100644 --- a/PWGLF/TableProducer/Nuspex/lnnRecoTask.cxx +++ b/PWGLF/TableProducer/Nuspex/lnnRecoTask.cxx @@ -20,6 +20,7 @@ #include "Common/Core/trackUtilities.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -41,6 +42,8 @@ #include #include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include #include #include #include @@ -59,16 +62,20 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using std::array; + using TracksFull = soa::Join; using TracksFullMC = soa::Join; -using CollisionsFull = soa::Join; -using CollisionsFullMC = soa::Join; +using CollisionsFull = soa::Join; +using CollisionsFullMC = soa::Join; using CollisionCandidates = o2::soa::Join; using CollisionCandidatesMC = o2::soa::Join; using TrackCandidates = o2::soa::Join; using TrackCandidatesMC = o2::soa::Join; +using McCollisionMults = soa::Join; +using EventCandidatesMC = soa::Join; + namespace { constexpr double BetheBlochDefault[1][6]{{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}}; @@ -83,6 +90,7 @@ std::shared_ptr hCentFT0M; std::shared_ptr hCentFV0A; std::shared_ptr hNsigma3HSel; std::shared_ptr hNsigma3HSelTOF; +std::shared_ptr h2Nsigma3HSelTPCTOF; std::shared_ptr hdEdx3HSel; std::shared_ptr hdEdx3HPosTrack; std::shared_ptr hdEdx3HNegTrack; @@ -92,6 +100,8 @@ std::shared_ptr h3HSignalPtTOF; std::shared_ptr hIsMatterGen; std::shared_ptr hLnnCandLoss; std::shared_ptr hDecayChannel; +// QA centrality scaling +std::shared_ptr h2GlobalTrackMultVsCent; // QA histograms before track selections std::shared_ptr h2FT0CnClusTPCtoTrBfSel; std::shared_ptr h2FT0CnClusTPCtoPiBfSel; @@ -106,20 +116,42 @@ std::shared_ptr h2FT0CptPiBfSelItsTpc; // QA MC tracks from the lnn decay std::shared_ptr hPtGeneratedTrStr; std::shared_ptr hPtGeneratedPiStr; +std::shared_ptr hPtGeneratedLnnStr; +std::shared_ptr hPtTpcTrStr; std::shared_ptr hPtItsTpcTrStr; +std::shared_ptr hPtTrkTpcTrStr; std::shared_ptr hPtTrkItsTpcTrStr; std::shared_ptr hPtItsTpcTofTrStr; std::shared_ptr hPtTrkItsTpcTofTrStr; -std::shared_ptr hPtItsTpcPiStr; -std::shared_ptr hPtTrkItsTpcPiStr; + // QA reco generated candidate and daugher particles from secondary vertex std::shared_ptr h2FT0CPtGenColRecCandMC; std::shared_ptr h2FT0CPtGenColRecTrStrMC; -std::shared_ptr h2FT0CPtGenColRecPiStrMC; // QA signal generated candidate and daugher particles from secondary vertex std::shared_ptr h2FT0CPtGenColGenCandMC; std::shared_ptr h2FT0CPtGenColGenTrStrMC; -std::shared_ptr h2FT0CPtGenColGenPiStrMC; +// Event and signal loss +std::shared_ptr hEvtMC; +std::shared_ptr hImpactParamGen; +std::shared_ptr hImpactParamGenOneReco; +std::shared_ptr hImpactParamReco; +std::shared_ptr hGenLnnBeforeEvtSel; +std::shared_ptr hGenLnnAfterSel; +std::shared_ptr hGenOneRecoCentrality; +std::shared_ptr hRecoCentrality; +std::shared_ptr hGenEventsNchEta08; +std::shared_ptr hGenCentralityColvsMultiplicityGenEta08; +std::shared_ptr hGenCentralityColvsImpactParamGen; +std::shared_ptr hGenCentralityColvsFT0Cmultiplicity; +std::shared_ptr hRecoCentralityColvsMultiplicityRecoEta08; +std::shared_ptr hRecoCentralityColvsImpactParamReco; +std::shared_ptr hRecoCentralityColvsFT0Cmultiplicity; +std::shared_ptr hGenLnnvsImpactParameterBeforeEvtSel; +std::shared_ptr hGenLnnvsImpactParameterAfterSel; +std::shared_ptr hGenLnnvsMultiplicityGenEta08BeforeEvtSel; +std::shared_ptr hGenLnnvsMultiplicityGenEta08AfterSel; +std::shared_ptr hGenLnnvsMultiplicityFT0CBeforeEvtSel; +std::shared_ptr hGenLnnvsMultiplicityFT0CAfterSel; float alphaAP(std::array const& momB, std::array const& momC) { @@ -149,10 +181,6 @@ struct LnnCandidate { float genPhi3H() const { return std::atan2(gMom3H[1], gMom3H[0]); } float genEta3H() const { return std::asinh(gMom3H[2] / genPt3H()); } - float genPtPi() const { return std::hypot(gMomPi[0], gMomPi[1]); } - float genPhiPi() const { return std::atan2(gMomPi[1], gMomPi[0]); } - float genEtaPi() const { return std::asinh(gMomPi[2] / genPtPi()); } - int posTrackID; int negTrackID; float dcaV0dau = -10; @@ -172,7 +200,6 @@ struct LnnCandidate { std::array decVtx; std::array gMom; std::array gMom3H; - std::array gMomPi; std::array gDecVtx; uint16_t tpcSignal3H = 0u; uint16_t tpcSignalPi = 0u; @@ -191,7 +218,7 @@ struct LnnCandidate { uint8_t flags = 0u; // flags for dughter particles }; -struct lnnRecoTask { +struct LnnRecoTask { Produces outputDataTable; Produces outputMCTable; @@ -216,6 +243,7 @@ struct lnnRecoTask { Configurable ptMinTOF{"ptMinTOF", 0.8, "minimum pt for TOF cut"}; Configurable trTOFMass2Cut{"trTOFMass2Cut", 5.5, "minimum Triton mass square to TOF"}; Configurable betaTrTOF{"betaTrTOF", 0.4, "minimum beta TOF cut"}; + Configurable vtxZCut{"vtxZCut", 10., "vxt Z cut for event selection"}; Configurable mcSignalOnly{"mcSignalOnly", true, "If true, save only signal in MC"}; Configurable doTrackQA{"doTrackQA", true, "if true, compute the QA studies beased on detectors (ITS-TPC-TOF) signals"}; Configurable useNoSameBunchPileup{"useNoSameBunchPileup", false, "reject collisions in case of pileup with another collision in the same foundBC"}; @@ -232,7 +260,7 @@ struct lnnRecoTask { Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Type of material correction"}; // CCDB options - Configurable d_bz_input{"d_bz_input", -999., "bz field, -999 is automatic"}; + Configurable dBzInput{"dBzInput", -999., "bz field, -999 is automatic"}; Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; @@ -245,7 +273,7 @@ struct lnnRecoTask { Configurable piDauPdg{"piDauPdg", 211, "PDG Pi"}; // PDG Pi Configurable lnnPdg{"lnnPdg", 1010000030, "PDG Lnn"}; // PDG Lnn - // Histogram configuration QA lnn-task + // histograms axes ConfigurableAxis rigidityBins{"rigidityBins", {200, -10.f, 10.f}, "Binning for rigidity"}; ConfigurableAxis dEdxBins{"dEdxBins", {5000, 0.f, 1000.f}, "Binning for dE/dx"}; ConfigurableAxis nSigmaBins{"nSigmaBins", {200, -5.f, 5.f}, "Binning for n#sigma_{TPC}"}; @@ -259,12 +287,20 @@ struct lnnRecoTask { ConfigurableAxis betaBins{"betaBins", {550, 0.f, 1.1f}, "Binning for Beta"}; ConfigurableAxis dcaXYBins{"dcaXYBins", {550, -5.f, 5.f}, "Binning for dcaXY triton"}; ConfigurableAxis tpcNClusBins{"tpcNClusBins", {260, 30, 165}, "Binning for nClusTPC"}; - ConfigurableAxis tpcNClsCrossedRowsBins{"TPCNClsCrossedRowsBins", {260, 30, 165}, "Binning for TPCNClsCrossedRows"}; + ConfigurableAxis tpcNClsCrossedRowsBins{"tpcNClsCrossedRowsBins", {260, 30, 165}, "Binning for TPCNClsCrossedRows"}; ConfigurableAxis tpcChi2NClusBins{"tpcChi2NClusBins", {20, 0.5, 10}, "Binning for chi2NClusTPC"}; ConfigurableAxis itsChi2NClusBins{"itsChi2NClusBins", {72, 0, 36}, "Binning for chi2NClusTPC"}; - ConfigurableAxis candPtBins{"candPtBins", {160, 0, 8}, "Binning for lnn cand pt"}; + ConfigurableAxis candPtBins{"candPtBins", {32, -8, 8}, "Binning for lnn cand pt"}; ConfigurableAxis candEtaBins{"candEtaBins", {160, -0.8, 0.8}, "Binning for eta"}; + // histogram axes for EvtLossMC + ConfigurableAxis binsImpactPar{"binsImpactPar", {80, 0, 16}, "Binning of the impact parameter axis"}; + ConfigurableAxis binsCent{"binsCent", {10, 0.0, 100.0}, "Binning of the centrality axis"}; + ConfigurableAxis binsPt{"binsPt", {20, 0, 10}, "Binning of the pt"}; + ConfigurableAxis binsFT0CMult{"binsFT0CMult", {500, 0.0f, +500.0f}, "Binning of the FT0C multiplicity"}; + ConfigurableAxis binsMult{"binsMult", {500, 0.0f, +500.0f}, ""}; + ConfigurableAxis binsglobalTrackMult{"binsglobalTrackMult", {100, 0.f, 1000.f}, "Binning for global track multiplicity"}; + // std vector of candidates std::vector lnnCandidates; // vector to keep track of MC mothers already filled @@ -276,17 +312,26 @@ struct lnnRecoTask { // vector to armazenade h3Track Preslice perCollision = o2::aod::v0::collisionId; + + Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; + Preslice tracksPerCollision = aod::track::collisionId; + HistogramRegistry qaRegistry{"QA", {}, OutputObjHandlingPolicy::AnalysisObject}; - int mRunNumber; - float d_bz; - std::array mBBparams3H; + int mRunNumber{-1}; + float dBz{0.f}; + std::array mBBparams3H{}; + + static constexpr float KallEvents = 0.; + static constexpr float KevAfterSel8 = 1.; + static constexpr float KevZVertexCut = 2.; + static constexpr float KevPileupCut = 3.; // Definiton of histograms to real data [hNsigma3HSelected, hdEdx3HSelected, dEdxtotal, hEVents, hCentFT0(A/C/M) and hCentFV0A] and MC [hDecayChannel, hIsMatterGen, hIsMatterGenTwoBody] void init(InitContext const&) { mRunNumber = 0; - d_bz = 0; + dBz = 0; ccdb->setURL(ccdburl); ccdb->setCaching(true); @@ -305,7 +350,8 @@ struct lnnRecoTask { const AxisSpec rigidityAxis{rigidityBins, "#it{p}^{TPC}/#it{z}"}; const AxisSpec dEdxAxis{dEdxBins, "d#it{E}/d#it{x}"}; - const AxisSpec nSigma3HAxis{nSigmaBins, "n_{#sigma}({}^{3}H)"}; + const AxisSpec nSigma3HtpcAxis{nSigmaBins, "n_{#sigma TPC}({}^{3}H)"}; + const AxisSpec nSigma3HtofAxis{nSigmaBins, "n_{#sigma TOF}({}^{3}H)"}; const AxisSpec zVtxAxis{zVtxBins, "z_{vtx} (cm)"}; const AxisSpec centAxis{centBins, "FT0C (%)"}; const AxisSpec mTOFAxis{mTOFBins, "#frac{m^{2}}{z^{2}}"}; @@ -313,7 +359,6 @@ struct lnnRecoTask { const AxisSpec tPosRigidityAxis{tPPosBins, "#it{p}^{TPC}/#it{z}"}; const AxisSpec tPNegRigidityAxis{tPNegBins, "#it{p}^{TPC}/#it{z}"}; const AxisSpec betaAxis{betaBins, "#beta_{TOF}"}; - const AxisSpec dcaXYAxis(dcaXYBins, "DCA_{xy} ({}^{3}H (cm)"); const AxisSpec tpcNClusAxis(tpcNClusBins, "N_{clus}^{TPC}"); const AxisSpec tpcNClsCrossedRowsAxis(tpcNClsCrossedRowsBins, "N_{TPC} crossed rows"); const AxisSpec tpcChi2NClusAxis(tpcChi2NClusBins, "{#Chi}^{2}/N_{clus}^{TPC}"); @@ -321,8 +366,16 @@ struct lnnRecoTask { const AxisSpec candPtAxis(candPtBins, "#it{p}_{T} (Gev/#it{c})"); const AxisSpec candEtaAxis(candEtaBins, "#eta"); - hNsigma3HSel = qaRegistry.add("PID/hNsigma3HSel", ";#it{p}^{TPC}/z (GeV/#it{c}); n_{#sigma} ({}^{3}H)", HistType::kTH2F, {rigidityAxis, nSigma3HAxis}); - hNsigma3HSelTOF = qaRegistry.add("PID/hNsigma3HSelTOF", ";#it{p}_{T} (GeV/#it{c}); n_{#sigma} ({}^{3}H)", HistType::kTH2F, {tPtAxis, nSigma3HAxis}); + const AxisSpec impactParamAxis{binsImpactPar, "Impact Parameter (b)"}; + const AxisSpec centFT0CAxis{binsCent, "Centrality (FT0C %)"}; + const AxisSpec binsFT0CMultAxis{binsFT0CMult, "FT0C multiplicity"}; + const AxisSpec ptAxis{binsPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec multAxis = {binsMult, "Multiplicity #eta <0.5"}; + const AxisSpec globalTrackMultAxis = {binsglobalTrackMult, "N_{global track mult.}"}; + + hNsigma3HSel = qaRegistry.add("PID/hNsigma3HSel", ";#it{p}^{TPC}/z (GeV/#it{c}); n_{#sigma} ({}^{3}H)", HistType::kTH2F, {rigidityAxis, nSigma3HtpcAxis}); + hNsigma3HSelTOF = qaRegistry.add("PID/hNsigma3HSelTOF", ";#it{p}_{T} (GeV/#it{c}); n_{#sigma} ({}^{3}H)", HistType::kTH2F, {tPtAxis, nSigma3HtpcAxis}); + h2Nsigma3HSelTPCTOF = qaRegistry.add("PID/h2Nsigma3HSelTPCTOF", ";n_{#sigma TPC} ({}^{3}H); n_{#sigma TOF} ({}^{3}H)", HistType::kTH2F, {nSigma3HtpcAxis, nSigma3HtofAxis}); hdEdx3HSel = qaRegistry.add("hdEdx3HSel", ";#it{p}^{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {rigidityAxis, dEdxAxis}); hdEdx3HPosTrack = qaRegistry.add("PID/hdEdx3HPosTrack", "; #it{p}^{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {tPosRigidityAxis, dEdxAxis}); hdEdx3HNegTrack = qaRegistry.add("PID/hdEdx3HNegTrack", "; #it{p}^{TPC}/z (GeV/#it{c}); dE/dx", HistType::kTH2F, {tPNegRigidityAxis, dEdxAxis}); @@ -330,27 +383,16 @@ struct lnnRecoTask { h3HMassPtTOF = qaRegistry.add("PID/hTrMassPtTOF", "; #it{p}_{T} ({}^{3}H) (GeV/#it{c}); #frac{m^{2}}{z^{2}} (GeV^{2}/#it{c}^{4})", HistType::kTH2F, {tPtAxis, mTOFAxis}); h3HSignalPtTOF = qaRegistry.add("PID/h3HSignalPtTOF", "; #it{p}_{T}({}^{3}H) (GeV/#it{c}); #beta_{TOF}", HistType::kTH2F, {tPtAxis, betaAxis}); hEvents = qaRegistry.add("hEvents", ";Events; ", HistType::kTH1D, {{4, -0.5, 3.5}}); - hLnnCandLoss = qaRegistry.add("CandCounts/hLnnCandLoss", ";CandLoss; ", HistType::kTH1D, {{7, -0.5, 6.5}}); - // QA its-tpc and its-tpc-tof tracks before selection - h2FT0CnClusTPCtoTrBfSel = qaRegistry.add("QATracks/h2FT0CnClusTPCtoTrBfSel", ";FT0C (%);N_{clus}^{TPC}", HistType::kTH2F, {centAxis, tpcNClusAxis}); - h2FT0CnClusTPCtoPiBfSel = qaRegistry.add("QATracks/h2FT0CnClusTPCtoPiBfSel", ";FT0C (%);N_{clus}^{TPC}", HistType::kTH2F, {centAxis, tpcNClusAxis}); - h2FT0Cchi2NClTPCtoTrBfSel = qaRegistry.add("QATracks/h2FT0Cchi2NClTPCtoTrBfSel", ";FT0C (%);{#Chi}^{2}/N_{clus}^{TPC} ", HistType::kTH2F, {centAxis, tpcChi2NClusAxis}); - h2FT0Cchi2NClITStoTrBfSel = qaRegistry.add("QATracks/h2FT0Cchi2NClITStoTrBfSel", ";FT0C (%);{#Chi}^{2}/N_{clus}^{ITS}", HistType::kTH2F, {centAxis, itsChi2NClusAxis}); - h2FT0CnTPCNClsCrossedRows3HBfSel = qaRegistry.add("QATracks/h2FT0CnTPCNClsCrossedRows3H", ";FT0C (%);N_{TPC} crossed rows", HistType::kTH2F, {centAxis, tpcNClsCrossedRowsAxis}); - h2FT0CnTPCNClsCrossedRowsPiBfSel = qaRegistry.add("QATracks/h2FT0CnTPCNClsCrossedRowsPi", ";FT0C (%);N_{TPC} crossed rows", HistType::kTH2F, {centAxis, tpcNClsCrossedRowsAxis}); - h2FT0CptTrBfSelItsTpc = qaRegistry.add("QATracks/h2FT0CptTrBfSelItsTpc", ";FT0C (%);#it{p}_{T}", HistType::kTH2F, {centAxis, candPtBins}); - h2FT0CptTrBfSelItsTpcTof = qaRegistry.add("QATracks/h2FT0CptTrBfSelItsTpcTof", ";FT0C (%);#it{p}_{T}", HistType::kTH2F, {centAxis, candPtBins}); - h2FT0CptPiBfSelItsTpc = qaRegistry.add("QATracks/h2FT0CptPiBfSelItsTpc", ";FT0C (%);#it{p}_{T}", HistType::kTH2F, {centAxis, candPtBins}); - // QA its-tpc, its-tpc-tof, and generated MC - hPtItsTpcTrStr = qaRegistry.add("MC/McTracks/hPtItsTpcTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); - hPtTrkItsTpcTrStr = qaRegistry.add("MC/McTracks/hPtTrkItsTpcTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); - hPtItsTpcTofTrStr = qaRegistry.add("MC/McTracks/hPtItsTpcTofTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); - hPtTrkItsTpcTofTrStr = qaRegistry.add("MC/McTracks/hPtTrkItsTpcTofTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); - hPtItsTpcPiStr = qaRegistry.add("MC/McTracks/hPtItsTpcPiStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); - hPtTrkItsTpcPiStr = qaRegistry.add("MC/McTracks/hPtTrkItsTpcPiStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); - hPtGeneratedTrStr = qaRegistry.add("MC/McGen/hPtGeneratedTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); - hPtGeneratedPiStr = qaRegistry.add("MC/McGen/hPtGeneratedPiStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {160, 0, 8}}); + h2GlobalTrackMultVsCent = qaRegistry.add("QATracks/h2GlobalTrackMultVsCent", ";FT0C (%); N_{global track mult.}", HistType::kTH2F, {centAxis, globalTrackMultAxis}); + + hZvtx = qaRegistry.add("hZvtx", ";z_{vtx} (cm); ", HistType::kTH1D, {{100, -20, 20}}); + hCentFT0A = qaRegistry.add("hCentFT0A", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + hCentFT0C = qaRegistry.add("hCentFT0C", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + hCentFT0M = qaRegistry.add("hCentFT0M", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + hCentFV0A = qaRegistry.add("hCentFV0A", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + + hLnnCandLoss = qaRegistry.add("CandCounts/hLnnCandLoss", ";CandLoss; ", HistType::kTH1D, {{7, -0.5, 6.5}}); hEvents->GetXaxis()->SetBinLabel(1, "All"); hEvents->GetXaxis()->SetBinLabel(2, "sel8"); hEvents->GetXaxis()->SetBinLabel(3, "z_{vtx}"); @@ -364,6 +406,27 @@ struct lnnRecoTask { hLnnCandLoss->GetXaxis()->SetBinLabel(5, "!isLnnMass"); hLnnCandLoss->GetXaxis()->SetBinLabel(6, "DCA #it{V}_{0} daughter"); hLnnCandLoss->GetXaxis()->SetBinLabel(7, "cosPA"); + + // QA its-tpc and its-tpc-tof tracks before selection + h2FT0CnClusTPCtoTrBfSel = qaRegistry.add("QATracks/h2FT0CnClusTPCtoTrBfSel", ";FT0C (%);N_{clus}^{TPC}", HistType::kTH2F, {centAxis, tpcNClusAxis}); + h2FT0CnClusTPCtoPiBfSel = qaRegistry.add("QATracks/h2FT0CnClusTPCtoPiBfSel", ";FT0C (%);N_{clus}^{TPC}", HistType::kTH2F, {centAxis, tpcNClusAxis}); + h2FT0Cchi2NClTPCtoTrBfSel = qaRegistry.add("QATracks/h2FT0Cchi2NClTPCtoTrBfSel", ";FT0C (%);{#Chi}^{2}/N_{clus}^{TPC} ", HistType::kTH2F, {centAxis, tpcChi2NClusAxis}); + h2FT0Cchi2NClITStoTrBfSel = qaRegistry.add("QATracks/h2FT0Cchi2NClITStoTrBfSel", ";FT0C (%);{#Chi}^{2}/N_{clus}^{ITS}", HistType::kTH2F, {centAxis, itsChi2NClusAxis}); + h2FT0CnTPCNClsCrossedRows3HBfSel = qaRegistry.add("QATracks/h2FT0CnTPCNClsCrossedRows3H", ";FT0C (%);N_{TPC} crossed rows", HistType::kTH2F, {centAxis, tpcNClsCrossedRowsAxis}); + h2FT0CnTPCNClsCrossedRowsPiBfSel = qaRegistry.add("QATracks/h2FT0CnTPCNClsCrossedRowsPi", ";FT0C (%);N_{TPC} crossed rows", HistType::kTH2F, {centAxis, tpcNClsCrossedRowsAxis}); + h2FT0CptTrBfSelItsTpc = qaRegistry.add("QATracks/h2FT0CptTrBfSelItsTpc", ";FT0C (%);#it{p}_{T}", HistType::kTH2F, {centAxis, candPtAxis}); + h2FT0CptTrBfSelItsTpcTof = qaRegistry.add("QATracks/h2FT0CptTrBfSelItsTpcTof", ";FT0C (%);#it{p}_{T}", HistType::kTH2F, {centAxis, candPtAxis}); + h2FT0CptPiBfSelItsTpc = qaRegistry.add("QATracks/h2FT0CptPiBfSelItsTpc", ";FT0C (%);#it{p}_{T}", HistType::kTH2F, {centAxis, candPtAxis}); + // QA its-tpc, its-tpc-tof, and generated MC + hPtTpcTrStr = qaRegistry.add("MC/McTracks/hPtTpcTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + hPtTrkTpcTrStr = qaRegistry.add("MC/McTracks/hPtTrkTpcTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + hPtItsTpcTrStr = qaRegistry.add("MC/McTracks/hPtItsTpcTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + hPtTrkItsTpcTrStr = qaRegistry.add("MC/McTracks/hPtTrkItsTpcTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + hPtItsTpcTofTrStr = qaRegistry.add("MC/McTracks/hPtItsTpcTofTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + hPtTrkItsTpcTofTrStr = qaRegistry.add("MC/McTracks/hPtTrkItsTpcTofTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + hPtGeneratedTrStr = qaRegistry.add("MC/McGen/hPtGeneratedTrStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + hPtGeneratedLnnStr = qaRegistry.add("MC/McGen/hPtGeneratedLnnStr", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {{10, 0, 100}, {320, -8, 8}}); + if (doprocessMC) { hDecayChannel = qaRegistry.add("MC/hDecayChannel", ";Decay channel; ", HistType::kTH1D, {{2, -0.5, 1.5}}); hDecayChannel->GetXaxis()->SetBinLabel(1, "All"); @@ -374,17 +437,44 @@ struct lnnRecoTask { // QA for generated mother candidate and daughter particles h2FT0CPtGenColRecCandMC = qaRegistry.add("MC/RecMcCol/h2FT0CPtGenColRecCandMC", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {centAxis, candPtAxis}); h2FT0CPtGenColRecTrStrMC = qaRegistry.add("MC/RecMcCol/h2FT0CPtGenColRecTrStrMC", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {centAxis, candPtAxis}); - h2FT0CPtGenColRecPiStrMC = qaRegistry.add("MC/RecMcCol/h2FT0CPtGenColRecPiStrMC", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {centAxis, candPtAxis}); // QA signal generated candidate and daugher particles from secondary vertex h2FT0CPtGenColGenCandMC = qaRegistry.add("MC/QASignalGenSV/h2FT0CPtGenColGenCandMC", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {centAxis, candPtAxis}); h2FT0CPtGenColGenTrStrMC = qaRegistry.add("MC/QASignalGenSV/h2FT0CPtGenColGenTrStrMC", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {centAxis, candPtAxis}); - h2FT0CPtGenColGenPiStrMC = qaRegistry.add("MC/QASignalGenSV/h2FT0CPtGenColGenPiStrMC", ";FT0C (%);#it{p}_{T} (GeV/#it{c})", HistType::kTH2F, {centAxis, candPtAxis}); } - hZvtx = qaRegistry.add("hZvtx", ";z_{vtx} (cm); ", HistType::kTH1D, {{100, -20, 20}}); - hCentFT0A = qaRegistry.add("hCentFT0A", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); - hCentFT0C = qaRegistry.add("hCentFT0C", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); - hCentFT0M = qaRegistry.add("hCentFT0M", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); - hCentFV0A = qaRegistry.add("hCentFV0A", ";Centrality; ", HistType::kTH1D, {{100, 0, 100}}); + + if (doprocessSigEvtLossMC) { + // Infomation for all generated collisions + hEvtMC = qaRegistry.add("QAEvent/hEvtMC", ";; ", HistType::kTH1D, {{3, -0.5, 2.5}}); + hEvtMC->GetXaxis()->SetBinLabel(1, "All gen evts"); + hEvtMC->GetXaxis()->SetBinLabel(2, "Gen evts with al least one reconstructed"); + hEvtMC->GetXaxis()->SetBinLabel(3, "Gen evts with no reconstructed collisions"); + hGenEventsNchEta08 = qaRegistry.add("QAEvent/hGenEventsNchEta08", ";;", HistType::kTH2D, {{multAxis}, {2, -0.5f, +1.5f}}); + hGenEventsNchEta08->GetYaxis()->SetBinLabel(1, "All gen. events"); + hGenEventsNchEta08->GetYaxis()->SetBinLabel(2, "Gen evts with at least 1 rec. collisions"); + hImpactParamGen = qaRegistry.add("QAEvent/McColAll/hImpactParamGen", "Impact parameter of generated MC events; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); + // Infomation for generated collisions which at least one rec. collision and passed the event selection + hImpactParamGenOneReco = qaRegistry.add("QAEvent/McColPassedEvSel/hImpactParamGenOneReco", "Impact parameter of generated MC events with at least one rec. evt and passed the event selection; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); + hGenOneRecoCentrality = qaRegistry.add("QAEvent/McColPassedEvSel/hGenOneRecoCentrality", "Centrality distribution of generated MC events with at least one rec. evt and passed the event selection; Centrality (FT0C %); Counts", HistType::kTH1D, {centFT0CAxis}); + hGenCentralityColvsMultiplicityGenEta08 = qaRegistry.add("QAEvent/McColPassedEvSel/hGenCentralityColvsMultiplicityGenEta08", "Correlation between FT0C centrality and charged particle multiplicity in generated MC events with at least one rec. evt and passed the event selection; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); + hGenCentralityColvsImpactParamGen = qaRegistry.add("QAEvent/McColPassedEvSel/hGenCentralityColvsImpactParamGen", "Correlation between FT0C centrality and impact parameter in generated MC events with at least one rec. evt and passed the event selection; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, impactParamAxis}); + hGenCentralityColvsFT0Cmultiplicity = qaRegistry.add("QAEvent/McColPassedEvSel/hGenCentralityColvsFT0Cmultiplicity", "Correlation between FT0C centrality and FT0C multiplicity in generated MC events with at least one rec. evt and passed the event selection; FT0c multiplicity", HistType::kTH2D, {centFT0CAxis, binsFT0CMultAxis}); + // Infomation for all reconstructed collisions passed the event selection (for Event Splitting) + hImpactParamReco = qaRegistry.add("QAEvent/McColAll/hImpactParamReco", "Impact parameter of generated MC events with at least one rec. evt; Impact Parameter (b); Counts", HistType::kTH1D, {impactParamAxis}); + hRecoCentrality = qaRegistry.add("QAEvent/McColAll/hRecoCentrality", "Centrality distribution of reconstructed MC events passed the event selection; Centrality (FT0C %); Counts", HistType::kTH1D, {centFT0CAxis}); + hRecoCentralityColvsMultiplicityRecoEta08 = qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsMultiplicityRecoEta08", "Correlation between FT0C centrality and charged particle multiplicity in reconstructed MC events passed the event selection; Multiplicity #eta <0.8; Counts", HistType::kTH2D, {centFT0CAxis, multAxis}); + hRecoCentralityColvsImpactParamReco = qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsImpactParamReco", "Correlation between FT0C centrality and impact parameter in reconstructed MC events passed the event selection; Impact Parameter (b); Counts", HistType::kTH2D, {centFT0CAxis, impactParamAxis}); + hRecoCentralityColvsFT0Cmultiplicity = qaRegistry.add("QAEvent/McColAll/hRecoCentralityColvsFT0Cmultiplicity", "Correlation between FT0C centrality and FT0C multiplicity in reconstructed MC events passed the event selection; FT0C (%); FT0c multiplicity", HistType::kTH2D, {centFT0CAxis, binsFT0CMultAxis}); + // Information of generated Lnn in generated events + hGenLnnBeforeEvtSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnBeforeEvtSel", "Lnn generated #it{p}_{T} distribution in all gen evt;#it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {ptAxis}); + hGenLnnvsImpactParameterBeforeEvtSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnvsImpactParameterBeforeEvtSel", "Correlation Lnn generated #it{p}_{T} and impact parameter in all gen evt;#it{p}_{T} (GeV/#it{c}); Impact parameter (b)", HistType::kTH2D, {ptAxis, impactParamAxis}); + hGenLnnvsMultiplicityGenEta08BeforeEvtSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnvsMultiplicityGenEta08BeforeEvtSel", "Correlation Lnn generated #it{p}_{T} and charged particle multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {ptAxis, multAxis}); + hGenLnnvsMultiplicityFT0CBeforeEvtSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnvsMultiplicityFT0CBeforeEvtSel", "Correlation Lnn generated #it{p}_{T} and FT0C multiplicity in all gen evt;#it{p}_{T} (GeV/#it{c}); FT0C Multiplicity", HistType::kTH2D, {ptAxis, binsFT0CMultAxis}); + // Information of generated Lnn in generated events with at least one rec. event and passed the event selection + hGenLnnAfterSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnAfterSel", "Lnn generated #it{p}_{T} distribution in gen. evts with at least one rec. evt; #it{p}_{T} (GeV/#it{c}); Counts", HistType::kTH1D, {ptAxis}); + hGenLnnvsImpactParameterAfterSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnvsImpactParameterAfterSel", "Correlation Lnn generated #it{p}_{T} and impact parameter in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Impact parameter (b)", HistType::kTH2D, {ptAxis, impactParamAxis}); + hGenLnnvsMultiplicityGenEta08AfterSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnvsMultiplicityGenEta08AfterSel", "Correlation Lnn generated #it{p}_{T} and charged particle multiplicity in gen. evts with at least one rec. evt;#it{p}_{T} (GeV/#it{c}); Multiplicity #eta <0.8", HistType::kTH2D, {ptAxis, multAxis}); + hGenLnnvsMultiplicityFT0CAfterSel = qaRegistry.add("QAEvent/McColLnn/hGenLnnvsMultiplicityFT0CAfterSel", "Correlation Lnn generated #it{p}_{T} and FT0C multiplicity in gen. evts with at least one rec;#it{p}_{T} (GeV/#it{c}); FT0C Multiplicity", HistType::kTH2D, {ptAxis, binsFT0CMultAxis}); + } } // group BCs @@ -393,45 +483,47 @@ struct lnnRecoTask { if (mRunNumber == bc.runNumber()) { return; } - auto run3grp_timestamp = bc.timestamp(); + auto run3grptimestamp = bc.timestamp(); static const double kBzAutoThreshold = -990.; - o2::parameters::GRPObject* grpo = ccdb->getForTimeStamp(grpPath, run3grp_timestamp); + o2::parameters::GRPObject* grpo = ccdb->getForTimeStamp(grpPath, run3grptimestamp); o2::parameters::GRPMagField* grpmag = 0x0; if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); - if (d_bz_input < kBzAutoThreshold) { + if (dBzInput < kBzAutoThreshold) { // Fetch magnetic field from ccdb for current collision - d_bz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + dBz = grpo->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grptimestamp << " with magnetic field of " << dBz << " kZG"; } else { - d_bz = d_bz_input; + dBz = dBzInput; } } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grp_timestamp); + grpmag = ccdb->getForTimeStamp(grpmagPath, run3grptimestamp); if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grp_timestamp; + LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grptimestamp; } o2::base::Propagator::initFieldFromGRP(grpmag); - if (d_bz_input < kBzAutoThreshold) { + if (dBzInput < kBzAutoThreshold) { // Fetch magnetic field from ccdb for current collision - d_bz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grp_timestamp << " with magnetic field of " << d_bz << " kZG"; + dBz = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for timestamp " << run3grptimestamp << " with magnetic field of " << dBz << " kZG"; } else { - d_bz = d_bz_input; + dBz = dBzInput; } } if (!pidPath.value.empty()) { - auto h3pid = ccdb->getForTimeStamp>(pidPath.value + "_3H", run3grp_timestamp); + auto h3pid = ccdb->getForTimeStamp>(pidPath.value + "_3H", run3grptimestamp); std::copy(h3pid->begin(), h3pid->end(), mBBparams3H.begin()); } else { - for (int i = 0; i < 5; i++) { + int kNBetheBlochParams = 5; + int kResolutionIndex = 5; + for (int i = 0; i < kNBetheBlochParams; i++) { mBBparams3H[i] = cfgBetheBlochParams->get("3H", Form("p%i", i)); } - mBBparams3H[5] = cfgBetheBlochParams->get("3H", "resolution"); + mBBparams3H[kResolutionIndex] = cfgBetheBlochParams->get("3H", "resolution"); } - fitter.setBz(d_bz); + fitter.setBz(dBz); mRunNumber = bc.runNumber(); } @@ -495,15 +587,16 @@ struct lnnRecoTask { auto& h3Rigidity = lnnCand.isMatter ? posRigidity : negRigidity; auto& piRigidity = lnnCand.isMatter ? negRigidity : posRigidity; - // fill QA track histogram studies to check track signal before selections - h2FT0CnClusTPCtoTrBfSel->Fill(collision.centFT0C(), h3track.tpcNClsFound()); - h2FT0CnClusTPCtoPiBfSel->Fill(collision.centFT0C(), pitrack.tpcNClsFound()); - h2FT0CnTPCNClsCrossedRows3HBfSel->Fill(collision.centFT0C(), h3track.tpcNClsCrossedRows()); - h2FT0CnTPCNClsCrossedRowsPiBfSel->Fill(collision.centFT0C(), pitrack.tpcNClsCrossedRows()); - h2FT0Cchi2NClTPCtoTrBfSel->Fill(collision.centFT0C(), h3track.tpcChi2NCl()); - h2FT0Cchi2NClITStoTrBfSel->Fill(collision.centFT0C(), h3track.itsChi2NCl()); - if (doTrackQA) { + // fill QA track histogram studies to check track signal before selections + + h2FT0CnClusTPCtoTrBfSel->Fill(collision.centFT0C(), h3track.tpcNClsFound()); + h2FT0CnClusTPCtoPiBfSel->Fill(collision.centFT0C(), pitrack.tpcNClsFound()); + h2FT0CnTPCNClsCrossedRows3HBfSel->Fill(collision.centFT0C(), h3track.tpcNClsCrossedRows()); + h2FT0CnTPCNClsCrossedRowsPiBfSel->Fill(collision.centFT0C(), pitrack.tpcNClsCrossedRows()); + h2FT0Cchi2NClTPCtoTrBfSel->Fill(collision.centFT0C(), h3track.tpcChi2NCl()); + h2FT0Cchi2NClITStoTrBfSel->Fill(collision.centFT0C(), h3track.itsChi2NCl()); + bool passedTrTrackITS = h3track.hasITS(); bool passedTrTrackTOF = h3track.hasTOF(); bool passedPiTrackITS = pitrack.hasITS(); @@ -519,11 +612,13 @@ struct lnnRecoTask { } if (h3Rigidity < tpcRigidityMin3H || - h3track.tpcNClsCrossedRows() < nTPCNClsCrossedRows3H || h3track.tpcChi2NCl() < chi2nClusTPCMin || h3track.tpcChi2NCl() > chi2nClusTPCMax || h3track.itsChi2NCl() > chi2nClusITS || - pitrack.tpcNClsCrossedRows() < nTPCNClsCrossedRowsPi) { + h3track.tpcNClsCrossedRows() < nTPCNClsCrossedRows3H || + pitrack.tpcNClsCrossedRows() < nTPCNClsCrossedRowsPi || + h3track.tpcNClsFound() < nTPCClusMin3H || + pitrack.tpcNClsFound() < nTPCClusMinPi) { continue; } @@ -586,11 +681,13 @@ struct lnnRecoTask { float h3lE = h3E + piE; // Building the mother particle: lnn - constexpr std::size_t kMomDim = 3; - std::array lnnMom; + constexpr std::size_t KMomDim = 3; + std::array lnnMom; const auto& vtx = fitter.getPCACandidate(); - for (int i = 0; i < 3; i++) { + + int kNDim = 3; + for (int i = 0; i < kNDim; i++) { lnnCand.decVtx[i] = vtx[i]; lnnMom[i] = lnnCand.mom3H[i] + lnnCand.momPi[i]; } @@ -601,10 +698,10 @@ struct lnnRecoTask { continue; } // Definition of lnn mass - float mLNN_HypHI = 3.00; // , but 2993.7 MeV/c**2 + float mLnnHypHi = 3.00; // , but 2993.7 MeV/c**2 float massLNNL = std::sqrt(h3lE * h3lE - lnnMom[0] * lnnMom[0] - lnnMom[1] * lnnMom[1] - lnnMom[2] * lnnMom[2]); bool isLNNMass = false; - if (massLNNL > mLNN_HypHI - masswidth && massLNNL < mLNN_HypHI + masswidth) { + if (massLNNL > mLnnHypHi - masswidth && massLNNL < mLnnHypHi + masswidth) { isLNNMass = true; } if (!isLNNMass) { @@ -619,8 +716,8 @@ struct lnnRecoTask { continue; } - constexpr std::size_t kprimVtxDim = 3; - std::array primVtx = {collision.posX(), collision.posY(), collision.posZ()}; + constexpr std::size_t KPrimVtxDim = 3; + std::array primVtx = {collision.posX(), collision.posY(), collision.posZ()}; double cosPA = RecoDecay::cpa(primVtx, lnnCand.decVtx, lnnMom); if (cosPA < v0cospa) { @@ -628,7 +725,7 @@ struct lnnRecoTask { continue; } - for (int i = 0; i < 3; i++) { + for (int i = 0; i < kNDim; i++) { lnnCand.decVtx[i] = lnnCand.decVtx[i] - primVtx[i]; } @@ -649,14 +746,17 @@ struct lnnRecoTask { // Fill 2D map after all selections hdEdx3HSel->Fill(chargeFactor * lnnCand.mom3HTPC, h3track.tpcSignal()); - hdEdx3HPosTrack->Fill(lnnCand.mom3HTPC, h3track.tpcSignal()); - if (!lnnCand.isMatter) { + if (lnnCand.isMatter) { + hdEdx3HPosTrack->Fill(lnnCand.mom3HTPC, h3track.tpcSignal()); + } else { hdEdx3HNegTrack->Fill(-lnnCand.mom3HTPC, h3track.tpcSignal()); } + hNsigma3HSel->Fill(chargeFactor * lnnCand.mom3HTPC, lnnCand.nSigma3H); if (h3track.hasTOF()) { h3HSignalPtTOF->Fill(chargeFactor * h3track.pt(), beta); - hNsigma3HSelTOF->Fill(chargeFactor * h3track.p(), h3track.tofNSigmaTr()); + hNsigma3HSelTOF->Fill(chargeFactor * h3track.pt(), h3track.tofNSigmaTr()); + h2Nsigma3HSelTPCTOF->Fill(lnnCand.nSigma3H, h3track.tofNSigmaTr()); h3HMassPtTOF->Fill(chargeFactor * h3track.pt(), lnnCand.mass2TrTOF); } } @@ -665,7 +765,8 @@ struct lnnRecoTask { // Monte Carlo information void fillMCinfo(aod::McTrackLabels const& trackLabels, aod::McParticles const&) { - for (auto& lnnCand : lnnCandidates) { + for (size_t iCand = 0; iCand < lnnCandidates.size(); ++iCand) { + auto& lnnCand = lnnCandidates[iCand]; auto mcLabPos = trackLabels.rawIteratorAt(lnnCand.posTrackID); auto mcLabNeg = trackLabels.rawIteratorAt(lnnCand.negTrackID); @@ -675,8 +776,8 @@ struct lnnRecoTask { auto mcTrackNeg = mcLabNeg.mcParticle_as(); if (mcTrackPos.has_mothers() && mcTrackNeg.has_mothers()) { - for (auto& negMother : mcTrackNeg.mothers_as()) { - for (auto& posMother : mcTrackPos.mothers_as()) { + for (auto const& negMother : mcTrackNeg.mothers_as()) { + for (auto const& posMother : mcTrackPos.mothers_as()) { if (posMother.globalIndex() != negMother.globalIndex()) continue; if (!((mcTrackPos.pdgCode() == h3DauPdg && mcTrackNeg.pdgCode() == -1 * piDauPdg) || (mcTrackPos.pdgCode() == piDauPdg && mcTrackNeg.pdgCode() == -1 * h3DauPdg))) @@ -685,10 +786,10 @@ struct lnnRecoTask { continue; // Checking primary and second vertex with MC simulations - constexpr std::size_t kposVtxDim = 3; - std::array posPrimVtx = {posMother.vx(), posMother.vy(), posMother.vz()}; - constexpr std::size_t ksecVtxDim = 3; - std::array secVtx = {mcTrackPos.vx(), mcTrackPos.vy(), mcTrackPos.vz()}; + constexpr std::size_t KPosVtxDim = 3; + std::array posPrimVtx = {posMother.vx(), posMother.vy(), posMother.vz()}; + constexpr std::size_t KSecVtxDim = 3; + std::array secVtx = {mcTrackPos.vx(), mcTrackPos.vy(), mcTrackPos.vz()}; lnnCand.gMom = posMother.pVector(); @@ -696,9 +797,8 @@ struct lnnRecoTask { lnnCand.gMom3H = isTrTrack ? mcTrackPos.pVector() : mcTrackNeg.pVector(); - lnnCand.gMomPi = isTrTrack ? mcTrackNeg.pVector() : mcTrackPos.pVector(); - - for (int i = 0; i < 3; i++) { + int kNDimGen = 3; + for (int i = 0; i < kNDimGen; i++) { lnnCand.gDecVtx[i] = secVtx[i] - posPrimVtx[i]; } @@ -719,7 +819,8 @@ struct lnnRecoTask { if (!mcPart.has_mothers()) return false; - if (mcPart.getProcess() != 4) + int decayProcess = 4; + if (mcPart.getProcess() != decayProcess) return false; bool motherIsAccepted = false; @@ -733,36 +834,47 @@ struct lnnRecoTask { return motherIsAccepted; } + // Generated MC particles template - void fillMcHistograms(TracksFullMC::iterator const& mcTrack, aod::McParticles::iterator const& mc, Tcoll const& collision) + void fillGeneratedMcHistograms(aod::McParticles::iterator const& mc, Tcoll const& collision) { + const int pdg = std::abs(mc.pdgCode()); + int chargeFactor = -1 + 2 * (mc.pdgCode() > 0); + if (pdg == lnnPdg) { + hPtGeneratedLnnStr->Fill(collision.centFT0C(), chargeFactor * mc.pt()); + } + if (pdg == h3DauPdg) { + if (isLnnDecay(mc)) { + hPtGeneratedTrStr->Fill(collision.centFT0C(), chargeFactor * mc.pt()); + } + } + } + // Reconstructed tracks associated to MC generated particles + template + void fillMcHistograms(TracksFullMC::iterator const& mcTrack, aod::McParticles::iterator const& mc, Tcoll const& collision) + { bool passedTrackITS = mcTrack.hasITS(); bool passedTrackTPC = mcTrack.hasTPC(); bool passedTrackTOF = mcTrack.hasTOF(); int pdg = std::abs(mc.pdgCode()); - + int chargeFactor = -1 + 2 * (mc.pdgCode() > 0); + int chargeTrackFactor = mcTrack.sign(); if (pdg == h3DauPdg) { - hPtGeneratedTrStr->Fill(collision.centFT0C(), mc.pt()); - - if (passedTrackITS && passedTrackTPC) { - hPtItsTpcTrStr->Fill(collision.centFT0C(), mc.pt()); - hPtTrkItsTpcTrStr->Fill(collision.centFT0C(), mcTrack.pt()); - if (passedTrackTOF) { - hPtItsTpcTofTrStr->Fill(collision.centFT0C(), mc.pt()); - hPtTrkItsTpcTofTrStr->Fill(collision.centFT0C(), mcTrack.pt()); + if (passedTrackITS) { + hPtTpcTrStr->Fill(collision.centFT0C(), chargeFactor * mc.pt()); + hPtTrkTpcTrStr->Fill(collision.centFT0C(), chargeTrackFactor * mcTrack.pt()); + if (passedTrackTPC) { + hPtItsTpcTrStr->Fill(collision.centFT0C(), chargeFactor * mc.pt()); + hPtTrkItsTpcTrStr->Fill(collision.centFT0C(), chargeTrackFactor * mcTrack.pt()); + if (passedTrackTOF) { + hPtItsTpcTofTrStr->Fill(collision.centFT0C(), chargeFactor * mc.pt()); + hPtTrkItsTpcTofTrStr->Fill(collision.centFT0C(), chargeTrackFactor * mcTrack.pt()); + } } } } - - if (pdg == piDauPdg) { - hPtGeneratedPiStr->Fill(collision.centFT0C(), mc.pt()); - if (passedTrackITS && passedTrackTPC) { - hPtItsTpcPiStr->Fill(collision.centFT0C(), mc.pt()); - hPtTrkItsTpcPiStr->Fill(collision.centFT0C(), mcTrack.pt()); - } - } } void processData(CollisionsFull const& collisions, aod::V0s const& V0s, TracksFull const& tracks, aod::BCsWithTimestamps const&) @@ -773,19 +885,21 @@ struct lnnRecoTask { auto bc = collision.bc_as(); initCCDB(bc); - hEvents->Fill(0.); + hEvents->Fill(KallEvents); if ((!collision.sel8())) { continue; } - hEvents->Fill(1.); - if (std::abs(collision.posZ()) > 10) { + hEvents->Fill(KevAfterSel8); + if (std::abs(collision.posZ()) > vtxZCut) { continue; } - hEvents->Fill(2.); + hEvents->Fill(KevZVertexCut); if (useNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { continue; } - hEvents->Fill(3.); + hEvents->Fill(KevPileupCut); + + h2GlobalTrackMultVsCent->Fill(collision.centFT0C(), collision.multNTracksGlobal()); hZvtx->Fill(collision.posZ()); hCentFT0A->Fill(collision.centFT0A()); @@ -794,10 +908,10 @@ struct lnnRecoTask { hCentFV0A->Fill(collision.centFV0A()); const uint64_t collIdx = collision.globalIndex(); - auto V0Table_thisCollision = V0s.sliceBy(perCollision, collIdx); - V0Table_thisCollision.bindExternalIndices(&tracks); + auto v0TablethisCollision = V0s.sliceBy(perCollision, collIdx); + v0TablethisCollision.bindExternalIndices(&tracks); - fillCandidateData(collision, V0Table_thisCollision); + fillCandidateData(collision, v0TablethisCollision); for (const auto& lnnCand : lnnCandidates) { outputDataTable(collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), @@ -814,38 +928,27 @@ struct lnnRecoTask { } } } - PROCESS_SWITCH(lnnRecoTask, processData, "Data analysis", true); + PROCESS_SWITCH(LnnRecoTask, processData, "Data analysis", true); // MC process void processMC(CollisionsFullMC const& collisions, aod::McCollisions const& mcCollisions, aod::V0s const& V0s, aod::BCsWithTimestamps const&, TracksFull const& tracks, TracksFullMC const& tracksMC, aod::McTrackLabels const& trackLabelsMC, aod::McParticles const& particlesMC) { filledMothers.clear(); isGoodCollision.clear(); - isGoodCollision.resize(mcCollisions.size(), false); collisionFT0Ccent.clear(); - collisionFT0Ccent.resize(mcCollisions.size(), -1.f); recoCollisionIds.clear(); + + isGoodCollision.resize(mcCollisions.size(), false); + collisionFT0Ccent.resize(mcCollisions.size(), -1.f); recoCollisionIds.resize(mcCollisions.size(), -1); for (const auto& collision : collisions) { - for (auto const& trackMC : tracksMC) { - - if (!trackMC.has_mcParticle()) - continue; - - auto mc = trackMC.mcParticle(); - - if (!isLnnDecay(mc)) - continue; - - fillMcHistograms(trackMC, mc, collision); - } lnnCandidates.clear(); auto bc = collision.bc_as(); initCCDB(bc); - hEvents->Fill(0.); + hEvents->Fill(KallEvents); if (collision.has_mcCollision()) { recoCollisionIds[collision.mcCollisionId()] = collision.globalIndex(); } @@ -853,15 +956,17 @@ struct lnnRecoTask { if ((!collision.sel8())) { continue; } - hEvents->Fill(1.); - if (std::abs(collision.posZ()) > 10) { + hEvents->Fill(KevAfterSel8); + if (std::abs(collision.posZ()) > vtxZCut) { continue; } - hEvents->Fill(2.); + hEvents->Fill(KevZVertexCut); if (useNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { continue; } - hEvents->Fill(3.); + hEvents->Fill(KevPileupCut); + + h2GlobalTrackMultVsCent->Fill(collision.centFT0C(), collision.multNTracksGlobal()); hZvtx->Fill(collision.posZ()); hCentFT0A->Fill(collision.centFT0A()); @@ -875,12 +980,33 @@ struct lnnRecoTask { } const uint64_t collIdx = collision.globalIndex(); - auto V0Table_thisCollision = V0s.sliceBy(perCollision, collIdx); - V0Table_thisCollision.bindExternalIndices(&tracks); + auto v0TablethisCollision = V0s.sliceBy(perCollision, collIdx); + v0TablethisCollision.bindExternalIndices(&tracks); - fillCandidateData(collision, V0Table_thisCollision); + fillCandidateData(collision, v0TablethisCollision); fillMCinfo(trackLabelsMC, particlesMC); + // Fill MC gen. particles producing to the current MC collision + const auto mcCollId = collision.mcCollisionId(); + auto particlesThisMcCollision = particlesMC.sliceBy(mcParticlesPerMcCollision, mcCollId); + for (const auto& mc : particlesThisMcCollision) { + fillGeneratedMcHistograms(mc, collision); + } + + // Fill only tracks producing to the current reconstructed collision + auto tracksThisCollision = tracksMC.sliceBy(tracksPerCollision, collIdx); + for (const auto& trackMC : tracksThisCollision) { + if (!trackMC.has_mcParticle()) { + continue; + } + auto mc = trackMC.mcParticle(); + // Select only daughters from the Λnn decay + if (!isLnnDecay(mc)) { + continue; + } + fillMcHistograms(trackMC, mc, collision); + } + for (const auto& lnnCand : lnnCandidates) { if (!lnnCand.isSignal && mcSignalOnly) { continue; @@ -889,7 +1015,6 @@ struct lnnRecoTask { if (lnnCand.recoMcColl && lnnCand.survEvSelection) { h2FT0CPtGenColRecCandMC->Fill(collision.centFT0C(), chargeFactor * lnnCand.genPt()); h2FT0CPtGenColRecTrStrMC->Fill(collision.centFT0C(), chargeFactor * lnnCand.genPt3H()); - h2FT0CPtGenColRecPiStrMC->Fill(collision.centFT0C(), chargeFactor * lnnCand.genPtPi()); } outputMCTable(collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), collision.posX(), collision.posY(), collision.posZ(), @@ -905,101 +1030,226 @@ struct lnnRecoTask { chargeFactor * lnnCand.genPt(), lnnCand.genPhi(), lnnCand.genEta(), chargeFactor * lnnCand.genPt3H(), lnnCand.gDecVtx[0], lnnCand.gDecVtx[1], lnnCand.gDecVtx[2], lnnCand.isReco, lnnCand.isSignal, lnnCand.recoMcColl, lnnCand.survEvSelection); } + } - // now we fill only the signal candidates that were not reconstructed - for (const auto& mcPart : particlesMC) { + // now we fill only the signal candidates that were not reconstructed + for (const auto& mcPart : particlesMC) { - if (std::abs(mcPart.pdgCode()) != lnnPdg) { - continue; - } - float cent = collisionFT0Ccent[mcPart.mcCollisionId()]; + if (std::abs(mcPart.pdgCode()) != lnnPdg) { + continue; + } + float cent = collisionFT0Ccent[mcPart.mcCollisionId()]; - h2FT0CPtGenColGenCandMC->Fill(cent, mcPart.pt()); + int chargeFactorGen = mcPart.pdgCode() > 0 ? 1 : -1; - constexpr std::size_t kVtxDim = 3; - std::array secVtx; - std::array primVtx = {mcPart.vx(), mcPart.vy(), mcPart.vz()}; + h2FT0CPtGenColGenCandMC->Fill(cent, chargeFactorGen * mcPart.pt()); - constexpr std::size_t kArrayDim = 3; - std::array momMother = mcPart.pVector(); + constexpr std::size_t KVtxDim = 3; + std::array secVtx; + std::array primVtx = {mcPart.vx(), mcPart.vy(), mcPart.vz()}; - std::array mom3H; - std::array momPi; - bool is3HFound = false; + constexpr std::size_t KArrayDim = 3; + std::array momMother = mcPart.pVector(); - for (const auto& mcDaught : mcPart.daughters_as()) { - int pdg = std::abs(mcDaught.pdgCode()); + std::array mom3H; - if (pdg == h3DauPdg) { - secVtx = {mcDaught.vx(), mcDaught.vy(), mcDaught.vz()}; - mom3H = mcDaught.pVector(); - h2FT0CPtGenColGenTrStrMC->Fill(cent, mcDaught.pt()); - is3HFound = true; - } - if (pdg == piDauPdg) { - momPi = mcDaught.pVector(); - h2FT0CPtGenColGenPiStrMC->Fill(cent, mcDaught.pt()); - } - } + bool is3HFound = false; - if (mcPart.pdgCode() > 0) { - hIsMatterGen->Fill(0.); - } else { - hIsMatterGen->Fill(1.); - } + for (const auto& mcDaught : mcPart.daughters_as()) { + int pdg = std::abs(mcDaught.pdgCode()); - if (!is3HFound) { - hDecayChannel->Fill(1.); - } - hDecayChannel->Fill(0.); - if (std::find(filledMothers.begin(), filledMothers.end(), mcPart.globalIndex()) != std::end(filledMothers)) { - continue; + if (pdg == h3DauPdg) { + secVtx = {mcDaught.vx(), mcDaught.vy(), mcDaught.vz()}; + mom3H = mcDaught.pVector(); + h2FT0CPtGenColGenTrStrMC->Fill(cent, chargeFactorGen * mcDaught.pt()); + is3HFound = true; + break; } - LnnCandidate lnnCand; - lnnCand.pdgCode = mcPart.pdgCode(); - lnnCand.survEvSelection = isGoodCollision[mcPart.mcCollisionId()]; - int chargeFactor = -1 + 2 * (lnnCand.pdgCode > 0); - for (int i = 0; i < 3; i++) { - lnnCand.gDecVtx[i] = secVtx[i] - primVtx[i]; - lnnCand.gMom[i] = momMother[i]; - lnnCand.gMom3H[i] = mom3H[i]; - lnnCand.gMomPi[i] = momPi[i]; + if (is3HFound) { + break; } + } + + if (mcPart.pdgCode() > 0) { + hIsMatterGen->Fill(0.); + } else { + hIsMatterGen->Fill(1.); + } + + if (!is3HFound) { + hDecayChannel->Fill(1.); + continue; + } + hDecayChannel->Fill(0.); + if (std::find(filledMothers.begin(), filledMothers.end(), mcPart.globalIndex()) != std::end(filledMothers)) { + continue; + } + + LnnCandidate lnnCand; + lnnCand.pdgCode = mcPart.pdgCode(); + lnnCand.survEvSelection = isGoodCollision[mcPart.mcCollisionId()]; - lnnCand.posTrackID = -1; - lnnCand.negTrackID = -1; - lnnCand.isSignal = true; + int chargeFactor = -1 + 2 * (lnnCand.pdgCode > 0); + + int kDimGen = 3; + for (int i = 0; i < kDimGen; i++) { + lnnCand.gDecVtx[i] = secVtx[i] - primVtx[i]; + lnnCand.gMom[i] = momMother[i]; + lnnCand.gMom3H[i] = mom3H[i]; + } + + lnnCand.posTrackID = -1; + lnnCand.negTrackID = -1; + lnnCand.isSignal = true; + + float centFT0C = -1; + if (lnnCand.recoMcColl) { + auto recoCollision = collisions.rawIteratorAt(recoCollisionIds[mcPart.mcCollisionId()]); + centFT0C = recoCollision.centFT0C(); + } + outputMCTable(-1, centFT0C, -1, + -1, -1, -1, + 0, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, + -1, -1, + -1, -1, -1, + chargeFactor * lnnCand.genPt(), lnnCand.genPhi(), lnnCand.genEta(), lnnCand.genPt3H(), + lnnCand.gDecVtx[0], lnnCand.gDecVtx[1], lnnCand.gDecVtx[2], lnnCand.isReco, lnnCand.isSignal, lnnCand.recoMcColl, lnnCand.survEvSelection); + } + } + PROCESS_SWITCH(LnnRecoTask, processMC, "MC analysis", false); + + template + bool passEvtSel(const CollType& collision) + { + if (!collision.sel8()) + return false; - float centFT0A = -1, centFT0C = -1, centFT0M = -1; - if (lnnCand.recoMcColl) { - auto recoCollision = collisions.rawIteratorAt(recoCollisionIds[mcPart.mcCollisionId()]); - centFT0A = recoCollision.centFT0A(); - centFT0C = recoCollision.centFT0C(); - centFT0M = recoCollision.centFT0M(); + if ((std::abs(collision.posZ())) > vtxZCut) + return false; + + if (useNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) + return false; + + return true; + } + + void processSigEvtLossMC(McCollisionMults::iterator const& mcCollision, soa::SmallGroups const& collisions, aod::McParticles const& GenParticles) + { + if (std::abs(mcCollision.posZ()) > vtxZCut) { + return; + } + //////////// Event loss estimation via impact parameter and multiplicity by FT0C + + // Fill all generated events + hEvtMC->Fill(0); + hImpactParamGen->Fill(mcCollision.impactParameter()); + hGenEventsNchEta08->Fill(mcCollision.multMCNParticlesEta08(), 0); + + // Fill generated events with no reconstructed collisions + if (collisions.size() == 0) { + hEvtMC->Fill(1); + } + + // Define the generated events with at least one reconstructed event + bool atLeastOneRecoEvt = false; + auto centralityFT0C = -999.; + int biggestNContribs = -1; + + for (auto const& col : collisions) { + if (!passEvtSel(col)) { + continue; + } + // In case of multiple reconstructed collisions associated to the same generated one, only consider the one with the biggest number of contributors + if (biggestNContribs < col.numContrib()) { + biggestNContribs = col.numContrib(); + centralityFT0C = col.centFT0C(); + } + atLeastOneRecoEvt = true; + hImpactParamReco->Fill(mcCollision.impactParameter()); + hRecoCentrality->Fill(col.centFT0C()); + hRecoCentralityColvsMultiplicityRecoEta08->Fill(col.centFT0C(), mcCollision.multMCNParticlesEta08()); + hRecoCentralityColvsImpactParamReco->Fill(col.centFT0C(), mcCollision.impactParameter()); + hRecoCentralityColvsFT0Cmultiplicity->Fill(col.centFT0C(), mcCollision.multMCFT0C()); + } + + if (atLeastOneRecoEvt) { + hEvtMC->Fill(2); + hGenEventsNchEta08->Fill(mcCollision.multMCNParticlesEta08(), 1); + hImpactParamGenOneReco->Fill(mcCollision.impactParameter()); + hGenOneRecoCentrality->Fill(centralityFT0C); + hGenCentralityColvsMultiplicityGenEta08->Fill(centralityFT0C, mcCollision.multMCNParticlesEta08()); + hGenCentralityColvsImpactParamGen->Fill(centralityFT0C, mcCollision.impactParameter()); + hGenCentralityColvsFT0Cmultiplicity->Fill(centralityFT0C, mcCollision.multMCFT0C()); + } + // Construct the H3L 4-vector based on the generated daugthers identification by PDG + ROOT::Math::PxPyPzMVector daugh1, daugh2, mother; + + for (const auto& genParticle : GenParticles) { + if (std::abs(genParticle.y()) > 1) + continue; + if (std::abs(genParticle.pdgCode()) != lnnPdg) + continue; + + auto daughters = genParticle.daughters_as(); + + bool dauTr = false; + bool dauPi = false; + + int trSign = 0; + int piSign = 0; + + for (const auto& daughter : daughters) { + + if (std::abs(daughter.pdgCode()) == h3DauPdg) { + + dauTr = true; + trSign = daughter.pdgCode() > 0 ? 1 : -1; + + daugh1 = ROOT::Math::PxPyPzMVector(daughter.px(), daughter.py(), daughter.pz(), h3Mass); + + } else if (std::abs(daughter.pdgCode()) == piDauPdg) { + + dauPi = true; + piSign = daughter.pdgCode() > 0 ? 1 : -1; + + daugh2 = ROOT::Math::PxPyPzMVector(daughter.px(), daughter.py(), daughter.pz(), piMass); } - outputMCTable(centFT0A, centFT0C, centFT0M, - -1, -1, -1, - 0, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, - -1, -1, -1, -1, -1, - -1, -1, -1, -1, - -1, -1, - -1, -1, -1, - chargeFactor * lnnCand.genPt(), lnnCand.genPhi(), lnnCand.genEta(), lnnCand.genPt3H(), - lnnCand.gDecVtx[0], lnnCand.gDecVtx[1], lnnCand.gDecVtx[2], lnnCand.isReco, lnnCand.isSignal, lnnCand.recoMcColl, lnnCand.survEvSelection); + } + + if (!dauTr || !dauPi || (trSign * piSign) > 0) { + continue; + } + + mother = daugh1 + daugh2; + + // Fill informations for generated 3HL in all generated events + hGenLnnBeforeEvtSel->Fill(mother.pt()); + hGenLnnvsImpactParameterBeforeEvtSel->Fill(mother.pt(), mcCollision.impactParameter()); + hGenLnnvsMultiplicityGenEta08BeforeEvtSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta08()); + hGenLnnvsMultiplicityFT0CBeforeEvtSel->Fill(mother.pt(), mcCollision.multMCFT0C()); + + // Fill informations for generated 3HL in generated events with at least one reconstructed event + if (atLeastOneRecoEvt) { + hGenLnnAfterSel->Fill(mother.pt()); + hGenLnnvsImpactParameterAfterSel->Fill(mother.pt(), mcCollision.impactParameter()); + hGenLnnvsMultiplicityGenEta08AfterSel->Fill(mother.pt(), mcCollision.multMCNParticlesEta08()); + hGenLnnvsMultiplicityFT0CAfterSel->Fill(mother.pt(), mcCollision.multMCFT0C()); } } } - PROCESS_SWITCH(lnnRecoTask, processMC, "MC analysis", false); + PROCESS_SWITCH(LnnRecoTask, processSigEvtLossMC, "Signal and event loss analysis", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/TableProducer/Nuspex/nucleiAntineutronCex.cxx b/PWGLF/TableProducer/Nuspex/nucleiAntineutronCex.cxx index 1418a3f76d3..cdbaf517726 100644 --- a/PWGLF/TableProducer/Nuspex/nucleiAntineutronCex.cxx +++ b/PWGLF/TableProducer/Nuspex/nucleiAntineutronCex.cxx @@ -14,7 +14,6 @@ /// \brief Analysis task for antineutron detection through cex interactions /// \author Fabiola Lugo /// - #include "PWGLF/DataModel/LFAntinCexTables.h" #include "Common/DataModel/PIDResponseITS.h" @@ -33,7 +32,6 @@ #include #include -#include #include #include #include @@ -51,15 +49,15 @@ using o2::constants::math::Rad2Deg; struct NucleiAntineutronCex { // Slicing per colision Preslice perMcByColl = aod::mcparticle::mcCollisionId; - // Check available tables in the AOD, specifically TracksIU, TracksCovIU + using TracksWCovMc = soa::Join; // === Cut values === static constexpr double kIts2MinR = 4.5; // ITS2 min radius (exluding IB) [cm] static constexpr double kIts2MaxR = 48.0; // ITS2 max radius [cm] static constexpr double kIts2MaxVz = 39.0; // ITS2 max |vz| [cm] - static constexpr double kAccMaxEta = 1.2; // acceptance |eta| - static constexpr double kAccMaxVz = 10.0; // acceptance |vz| [cm] + static constexpr double kAccMaxEta = 1.2; // acceptance |eta| (primary particles) + static constexpr double kAccMaxVz = 10.0; // acceptance |vz| (primary particles) [cm] static constexpr double kStrictEta = 0.9; // tighter eta cut static constexpr double kInitDplane = 10.0; // init dplane static constexpr double kHuge = 1e9; // fallback for bad denom @@ -84,6 +82,11 @@ struct NucleiAntineutronCex { histos.add("antin_eta", "Pseudorapidity;#eta;Entries", kTH1F, {{100, -10., 10.}}); histos.add("antin_p_ITScuts", "Momentum with ITS cuts;|p| (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("antin_pt", "Antineutron transverse momentum;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("antin_pt_ITScuts", "Antineutron transverse momentum with ITS cuts;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("antin_pt_closureA", "Generated antineutron p_{T}, closure sample A;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("antin_pt_closureB", "Generated antineutron p_{T}, closure sample B;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + // Primary neutrons histos.add("n_p", "Total momentum;|p| (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); histos.add("n_px", "p_{x};p_{x} (GeV/c);Entries", kTH1F, {{100, -10., 10.}}); @@ -112,31 +115,21 @@ struct NucleiAntineutronCex { histos.add("hProcEnumAP_CEX", "procEnum of secondary #bar{p} (CEX);procEnum;Entries", kTH1I, {{100, -0.5, 99.5}}); histos.add("hProcEnumAP_BG", "procEnum of secondary #bar{p} (BG);procEnum;Entries", kTH1I, {{100, -0.5, 99.5}}); - // Multiplicity/composition at the SV (MC truth, for FINAL selected candidates) - histos.add("hVtxNAll_CEX", "N(all) secondaries at SV (CEX);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - histos.add("hVtxNAll_BG", "N(all) secondaries at SV (BG);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - histos.add("hVtxNCh_CEX", "N(charged) secondaries at SV (CEX);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - histos.add("hVtxNCh_BG", "N(charged) secondaries at SV (BG);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - histos.add("hVtxNNeut_CEX", "N(neutral) secondaries at SV (CEX);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - histos.add("hVtxNNeut_BG", "N(neutral) secondaries at SV (BG);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - - histos.add("hVtxNPi0_CEX", "N(#pi^{0}) at SV (CEX);N;Entries", kTH1I, {{40, -0.5, 39.5}}); - histos.add("hVtxNPi0_BG", "N(#pi^{0}) at SV (BG);N;Entries", kTH1I, {{40, -0.5, 39.5}}); - histos.add("hVtxNGamma_CEX", "N(#gamma) at SV (CEX);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - histos.add("hVtxNGamma_BG", "N(#gamma) at SV (BG);N;Entries", kTH1I, {{60, -0.5, 59.5}}); - histos.add("hVtxNN_CEX", "N(n) at SV (CEX);N;Entries", kTH1I, {{40, -0.5, 39.5}}); - histos.add("hVtxNN_BG", "N(n) at SV (BG);N;Entries", kTH1I, {{40, -0.5, 39.5}}); - // CEX pair from antineutron (MC) histos.add("cexPairMcP", "CEX pair total momentum;|p| (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); histos.add("cexPairMcPt", "CEX pair p_{T};p_{T} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); histos.add("cexPairMcPz", "CEX pair p_{z};p_{z} (GeV/c);Entries", kTH1F, {{100, -10., 10.}}); histos.add("cex_pairmcDplane", "CEX pair d_{plane};d_{plane} (cm);Entries", kTH1F, {{100, 0., 10.}}); histos.add("cex_pairmc_angle", "Pair opening angle;Angle (°);Entries", kTH1F, {{180, 0., 180.}}); - histos.add("cex_pairmc_vtx", "MC CEX pair vertex;X (cm);Y (cm)", kTH2F, {{100, -50., 50.}, {100, -50., 50.}}); + histos.add("cex_pairmc_vtx", "MC CEX pair vertex;X (cm);Y (cm)", kTH2F, {{200, -60., 60.}, {200, -60., 60.}}); histos.add("cex_pairmc_vtxz", "MC secondary vertex Z;Z (cm);Entries", kTH1F, {{200, -60., 60.}}); histos.add("cexPairMcPITScuts", "CEX pair momentum (ITS cuts);|p| (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexMotherMcP", "Antineutron momentum for true CEX;p_{#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexMotherMcPt", "Antineutron transverse momentum for true CEX;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexMotherMcPt_closureA", "Antineutron p_{T} for true CEX, closure sample A;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexMotherMcPt_closureB", "Antineutron p_{T} for true CEX, closure sample B;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + // CEX pair normalized to antineutron (MC) histos.add("cexn_pairmc_p", "Pair p / antineutron p;p/p_{#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); histos.add("cexn_pairmc_pt", "Pair p_{T} / antineutron p_{T};p_{T}/p_{T,#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); @@ -148,16 +141,22 @@ struct NucleiAntineutronCex { histos.add("cexbg_pairmc_pz", "Background pair p_{z};p_{z} (GeV/c);Entries", kTH1F, {{100, -10., 10.}}); histos.add("cexbg_pairmcDplane", "Background d_{plane};d_{plane} (cm);Entries", kTH1F, {{100, 0., 10.}}); histos.add("cexbg_pairmc_angle", "Background opening angle;Angle (°);Entries", kTH1F, {{180, 0., 180.}}); - histos.add("cexbg_pairmc_vtx", "Background pair vertex;X (cm);Y (cm)", kTH2F, {{100, -50., 50.}, {100, -50., 50.}}); + histos.add("cexbg_pairmc_vtx", "Background pair vertex;X (cm);Y (cm)", kTH2F, {{200, -60., 60.}, {200, -60., 60.}}); histos.add("cexbg_pairmc_vtxz", "Background secondary vertex Z;Z (cm);Entries", kTH1F, {{200, -60., 60.}}); histos.add("cexbg_pairmc_pITScuts", "Background momentum (ITS cuts);|p| (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); - histos.add("hDeltaP_CEX", "|p_{mother}-Σp_{SV}| (CEX);Δp (GeV/c);Entries", kTH1F, {{200, 0., 10.}}); - histos.add("hDeltaP_BG", "|p_{mother}-Σp_{SV}| (BG);Δp (GeV/c);Entries", kTH1F, {{200, 0., 10.}}); + // Pi0 events + histos.add("cexn_pairmc_p_pi0", "Pair p / antineutron p for CEX + #pi^{0};p/p_{#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + histos.add("cexn_pairmc_pt_pi0", "Pair p_{T} / antineutron p_{T} for CEX + #pi^{0};p_{T}/p_{T,#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + + histos.add("cexn_pairmc_p_noPi0", "Pair p / antineutron p for two-body CEX;p/p_{#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + histos.add("cexn_pairmc_pt_noPi0", "Pair p_{T} / antineutron p_{T} for two-body CEX;p_{T}/p_{T,#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); - // Mother IB hits - histos.add("hMotherNHitIB_CEX", "Mother IB hit layers (L0-L2) (CEX);N_{IB layers};Entries", kTH1I, {{5, -1.5, 4.5}}); - histos.add("hMotherNHitIB_BG", "Mother IB hit layers (L0-L2) (BG);N_{IB layers};Entries", kTH1I, {{5, -1.5, 4.5}}); + histos.add("cexn_pairtrk_p_pi0", "Reconstructed pair p / antineutron p for CEX + #pi^{0};p_{p#bar{p}}^{reco}/p_{#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + histos.add("cexn_pairtrk_pt_pi0", "Reconstructed pair p_{T} / antineutron p_{T} for CEX + #pi^{0};p_{T,p#bar{p}}^{reco}/p_{T,#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + + histos.add("cexn_pairtrk_p_noPi0", "Reconstructed pair p / antineutron p for two-body CEX;p_{p#bar{p}}^{reco}/p_{#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + histos.add("cexn_pairtrk_pt_noPi0", "Reconstructed pair p_{T} / antineutron p_{T} for two-body CEX;p_{T,p#bar{p}}^{reco}/p_{T,#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); // CEX pair from antineutron (TRK) histos.add("cex_pairtrk_angle", "Pair opening angle (tracks);Angle (°);Entries", kTH1F, {{180, 0., 180.}}); @@ -169,6 +168,17 @@ struct NucleiAntineutronCex { histos.add("cex_pairtrkVtxfitDistToPv", "Distance from secondary vertex to PV;dist (cm);Entries", kTH1F, {{240, 0., 120.}}); histos.add("cex_pairtrk_vtxfit_secVtxXY", "Secondary vertex (PCA);X (cm);Y (cm)", kTH2F, {{200, -60., 60.}, {200, -60., 60.}}); histos.add("cex_pairtrk_vtxfit_secVtxZ", "Secondary vertex Z (PCA);Z (cm);Entries", kTH1F, {{240, -60., 60.}}); + histos.add("cexMotherTrkP", "Antineutron momentum for reconstructed CEX;p_{#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexMotherTrkPt", "Antineutron transverse momentum for reconstructed CEX;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexMotherTrkPt_closureA", "Antineutron p_{T} for reconstructed CEX, closure sample A;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexMotherTrkPt_closureB", "Antineutron p_{T} for reconstructed CEX, closure sample B;p_{T,#bar{n}} (GeV/c);Entries", kTH1F, {{100, 0., 10.}}); + histos.add("cexResponseP", "CEX momentum response;p_{#bar{n}}^{MC} (GeV/c);p_{p#bar{p}}^{reco} (GeV/c)", kTH2F, {{100, 0., 10.}, {100, 0., 10.}}); + histos.add("cexResponsePt", "CEX transverse-momentum response;p_{T,#bar{n}}^{MC} (GeV/c);p_{T,p#bar{p}}^{reco} (GeV/c)", kTH2F, {{100, 0., 10.}, {100, 0., 10.}}); + + // CEX reconstructed pair normalized to antineutron + histos.add("cexn_pairtrk_p", "Reconstructed pair p / antineutron p;p_{p#bar{p}}^{reco}/p_{#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + histos.add("cexn_pairtrk_pt", "Reconstructed pair p_{T} / antineutron p_{T};p_{T,p#bar{p}}^{reco}/p_{T,#bar{n}};Entries", kTH1F, {{100, 0., 2.}}); + histos.add("cexn_pairtrk_pz", "Reconstructed pair p_{z} / antineutron p_{z};p_{z,p#bar{p}}^{reco}/p_{z,#bar{n}};Entries", kTH1F, {{100, -2., 2.}}); // BG pair (not from antineutron) (TRK) histos.add("cexbg_pairtrk_angle", "Background opening angle (tracks);Angle (°);Entries", kTH1F, {{180, 0., 180.}}); @@ -230,6 +240,8 @@ struct NucleiAntineutronCex { double pvtxZ = 0; for (auto const& col : cols) { const auto colId = col.globalIndex(); + const bool isSampleA = (colId % 2 == 0); + auto mcPartsThis = particles.sliceBy(perMcByColl, colId); if (std::isfinite(col.posX()) && std::isfinite(col.posY()) && std::isfinite(col.posZ())) { @@ -246,12 +258,20 @@ struct NucleiAntineutronCex { // Primary antineutrons if (particle.pdgCode() == -kNeutron && particle.isPhysicalPrimary()) { histos.fill(HIST("antin_p"), particle.p()); + histos.fill(HIST("antin_pt"), particle.pt()); histos.fill(HIST("antin_px"), particle.px()); histos.fill(HIST("antin_py"), particle.py()); histos.fill(HIST("antin_pz"), particle.pz()); histos.fill(HIST("antin_eta"), particle.eta()); - if (std::abs(particle.eta()) < kAccMaxEta && std::abs(particle.vz()) < kAccMaxVz) + if (std::abs(particle.eta()) < kAccMaxEta && std::abs(particle.vz()) < kAccMaxVz) { histos.fill(HIST("antin_p_ITScuts"), particle.p()); + histos.fill(HIST("antin_pt_ITScuts"), particle.pt()); + } + if (isSampleA) { + histos.fill(HIST("antin_pt_closureA"), particle.pt()); + } else { + histos.fill(HIST("antin_pt_closureB"), particle.pt()); + } } // Primary neutrons if (particle.pdgCode() == kNeutron && particle.isPhysicalPrimary()) { @@ -293,27 +313,21 @@ struct NucleiAntineutronCex { // Primary mother bool hasPrimaryMotherAntip = false; double motherPt = 0.0; - double motherPx = 0.0; - double motherPy = 0.0; double motherPz = 0.0; double motherVz = 0.0; double motherP = 0.0; double motherEta = 0.0; int motherPdg = 0; - int motherId = -1; for (const auto& mother : particle.mothers_as()) { if (mother.isPhysicalPrimary()) { hasPrimaryMotherAntip = true; motherPt = mother.pt(); - motherPx = mother.px(); - motherPy = mother.py(); motherPz = mother.pz(); motherVz = mother.vz(); motherP = mother.p(); motherEta = mother.eta(); motherPdg = mother.pdgCode(); - motherId = mother.globalIndex(); break; } } @@ -329,7 +343,8 @@ struct NucleiAntineutronCex { double antipE = particle.e(); int antipId = particle.globalIndex(); - // Selection conditions: Produced in the ITS + // Selection conditions MC: Produced in the ITS IB + const double r = std::sqrt(antipVx * antipVx + antipVy * antipVy); // Config for ITS // if(3.9<=r && r<=43.0 && std::abs(antipVz)<=48.9){ @@ -396,6 +411,33 @@ struct NucleiAntineutronCex { if (pionPlus || pionMinus) continue; + // Check for neutral pion at the same secondary vertex + bool pion0 = false; + for (const auto& particle4 : mcPartsThis) { + if (particle4.mcCollisionId() != colId) + continue; + const auto proc4Enum = particle4.getProcess(); + const bool isSecondaryFromMaterial4 = (!particle4.producedByGenerator()) && (proc4Enum == kPHadronic || proc4Enum == kPHInhelastic); + if (particle4.pdgCode() != kPi0 || !isSecondaryFromMaterial4 || particle4.mothersIds().empty()) + continue; + bool hasPrimaryMotherPi0 = false; + for (const auto& mother : particle4.mothers_as()) { + if (mother.isPhysicalPrimary()) { + hasPrimaryMotherPi0 = true; + break; + } + } + if (!hasPrimaryMotherPi0) + continue; + double pi0Vx = particle4.vx(); + double pi0Vy = particle4.vy(); + double pi0Vz = particle4.vz(); + if (std::abs(pi0Vx - antipVx) < kVtxTol && std::abs(pi0Vy - antipVy) < kVtxTol && std::abs(pi0Vz - antipVz) < kVtxTol) { + pion0 = true; + break; + } + } + // CEX selection double dplane = kInitDplane; double dplaneTmp = 0; @@ -453,7 +495,6 @@ struct NucleiAntineutronCex { continue; // CEX proton selection - // dplaneTmp = (pPy*antipPz - pPz*antipPy)*(pvtxX-antipVx) + (pPz*antipPx - pPx*antipPz)*(pvtxY-antipVy) + (pPx*antipPy - pPy*antipPx)*(pvtxZ-antipVz); double nx = (pPy * antipPz - pPz * antipPy); double ny = (pPz * antipPx - pPx * antipPz); double nz = (pPx * antipPy - pPy * antipPx); @@ -509,6 +550,13 @@ struct NucleiAntineutronCex { histos.fill(HIST("cex_pairmc_angle"), mcangleDeg); histos.fill(HIST("cex_pairmc_vtx"), antipVx, antipVy); histos.fill(HIST("cex_pairmc_vtxz"), antipVz); + histos.fill(HIST("cexMotherMcP"), motherP); + histos.fill(HIST("cexMotherMcPt"), motherPt); + if (isSampleA) { + histos.fill(HIST("cexMotherMcPt_closureA"), motherPt); + } else { + histos.fill(HIST("cexMotherMcPt_closureB"), motherPt); + } if (std::abs(motherEta) < kStrictEta && std::abs(motherVz) < kAccMaxVz) histos.fill(HIST("cexPairMcPITScuts"), cexPairMcP); // CEX pair normalized @@ -518,6 +566,17 @@ struct NucleiAntineutronCex { histos.fill(HIST("cexn_pairmc_pt"), cexPairMcPt / motherPt); if (motherPz != 0) histos.fill(HIST("cexn_pairmc_pz"), cexPairMcPz / motherPz); + if (pion0) { + if (motherP != 0.) + histos.fill(HIST("cexn_pairmc_p_pi0"), cexPairMcP / motherP); + if (motherPt != 0.) + histos.fill(HIST("cexn_pairmc_pt_pi0"), cexPairMcPt / motherPt); + } else { + if (motherP != 0.) + histos.fill(HIST("cexn_pairmc_p_noPi0"), cexPairMcP / motherP); + if (motherPt != 0.) + histos.fill(HIST("cexn_pairmc_pt_noPi0"), cexPairMcPt / motherPt); + } } // BG mother if (motherPdg != -kNeutron) { @@ -533,8 +592,8 @@ struct NucleiAntineutronCex { histos.fill(HIST("cexbg_pairmc_pITScuts"), cexPairMcP); } - // Detector signal - bool antipLayers = false; + // Reconstructed data + bool antipLayersCondition = false; bool antipHasTrack = false; double antipTrkPx = 0.; double antipTrkPy = 0.; @@ -542,14 +601,17 @@ struct NucleiAntineutronCex { double antipTrkP = 0.; double antipTrkEta = 0.; double antipTrkTpcSignal = 0; - // int antip_trk_nClsTPC = 0; int antipTrkNClsIts = 0; uint16_t apItsMap = 0; - float pTrkItsNSigmaPr = -999.f; - int8_t pTrkItsPidValid = 0; - float pTrkTgl = 0.f; + float antipTrkItsNSigmaPr = -999.f; + int8_t antipTrkItsPidValid = 0; + float antipTrkTgl = 0.f; + int antipTrkTpcNClsCrossedRows = -1; + float antipTrkTpcCrossedRowsOverFindableCls = -999.f; + float antipTrkTpcChi2NCl = -999.f; + float antipTrkItsChi2NCl = -999.f; - bool pLayers = false; + bool pLayersCondition = false; bool pHasTrack = false; double pTrkPx = 0.; double pTrkPy = 0.; @@ -557,15 +619,15 @@ struct NucleiAntineutronCex { double pTrkP = 0.; double pTrkEta = 0.; double pTrkTpcSignal = 0; - // int p_trk_nClsTPC = 0; int pTrkNClsIts = 0; uint16_t pItsMap = 0; - float antipTrkItsNSigmaPr = -999.f; - int8_t antipTrkItsPidValid = 0; - float antipTrkTgl = 0.f; - - bool motherHasTrack = false; - int motherNHitIB = -1; // number of hits in IB (L0-L2) + float pTrkItsNSigmaPr = -999.f; + int8_t pTrkItsPidValid = 0; + float pTrkTgl = 0.f; + int pTrkTpcNClsCrossedRows = -1; + float pTrkTpcCrossedRowsOverFindableCls = -999.f; + float pTrkTpcChi2NCl = -999.f; + float pTrkItsChi2NCl = -999.f; o2::aod::ITSResponse itsResponse; @@ -594,11 +656,6 @@ struct NucleiAntineutronCex { int nITS = track.itsNCls(); bool layerCondition = (!hitIB) && hitOuter && (nITS >= kMinItsHits); - if (mc.globalIndex() == motherId) { - motherHasTrack = true; - motherNHitIB = static_cast(hitL0) + static_cast(hitL1) + static_cast(hitL2); - } - if (mc.globalIndex() == antipId) { antipTrkP = track.p(); antipTrkPx = track.px(); @@ -609,14 +666,17 @@ struct NucleiAntineutronCex { // antip_trk_nClsTPC = track.tpcNCls(); antipTrkNClsIts = track.itsNCls(); antipTrkTgl = track.tgl(); + antipTrkTpcNClsCrossedRows = track.tpcNClsCrossedRows(); + antipTrkTpcCrossedRowsOverFindableCls = track.tpcCrossedRowsOverFindableCls(); + antipTrkTpcChi2NCl = track.tpcChi2NCl(); + antipTrkItsChi2NCl = track.itsChi2NCl(); const auto nsigmaITSantip = itsResponse.nSigmaITS(track); antipTrkItsNSigmaPr = static_cast(nsigmaITSantip); antipTrkItsPidValid = std::isfinite(nsigmaITSantip) ? 1 : 0; antipHasTrack = true; apItsMap = static_cast(track.itsClusterMap()); - antipLayers = (apItsMap != 0); if (layerCondition) - antipLayers = true; + antipLayersCondition = true; if (motherPdg == -kNeutron) { histos.fill(HIST("apItsNsigmaPr"), antipTrkItsNSigmaPr); histos.fill(HIST("apItsPidValid"), antipTrkItsPidValid); @@ -637,15 +697,17 @@ struct NucleiAntineutronCex { // p_trk_nClsTPC = track.tpcNCls(); pTrkNClsIts = track.itsNCls(); pTrkTgl = track.tgl(); - const auto nsigmaITSp = - itsResponse.nSigmaITS(track); + pTrkTpcNClsCrossedRows = track.tpcNClsCrossedRows(); + pTrkTpcCrossedRowsOverFindableCls = track.tpcCrossedRowsOverFindableCls(); + pTrkTpcChi2NCl = track.tpcChi2NCl(); + pTrkItsChi2NCl = track.itsChi2NCl(); + const auto nsigmaITSp = itsResponse.nSigmaITS(track); pTrkItsNSigmaPr = static_cast(nsigmaITSp); pTrkItsPidValid = std::isfinite(nsigmaITSp) ? 1 : 0; pHasTrack = true; pItsMap = static_cast(track.itsClusterMap()); - pLayers = (pItsMap != 0); if (layerCondition) - pLayers = true; + pLayersCondition = true; if (motherPdg == -kNeutron) { histos.fill(HIST("pItsNsigmaPr"), pTrkItsNSigmaPr); histos.fill(HIST("pItsPidValid"), pTrkItsPidValid); @@ -723,12 +785,17 @@ struct NucleiAntineutronCex { if (motherPdg != -kNeutron) histos.fill(HIST("cexbg_pairtrkVtxfitDcaPair"), dcaPair); - if (!(antipLayers && pLayers)) + if (pRow->isPVContributor() || apRow->isPVContributor()) + continue; + if (!(antipLayersCondition && pLayersCondition)) + continue; + const double radius = std::hypot(secX, secY); + if (radius < kIts2MinR || radius > kIts2MaxR || std::abs(secZ) > kIts2MaxVz) continue; + double cexPairTrkP = total_trk_pVec.Mag(); double cexPairTrkPt = total_trk_pVec.Pt(); double cexPairTrkPz = pTrkPz + antipTrkPz; - const double radius = std::hypot(secX, secY); const double dxPv = secX - pvtxX; const double dyPv = secY - pvtxY; const double dzPv = secZ - pvtxZ; @@ -783,6 +850,34 @@ struct NucleiAntineutronCex { histos.fill(HIST("cex_pairtrkVtxfitDistToPv"), distToPrimary); histos.fill(HIST("cex_pairtrk_vtxfit_secVtxXY"), secX, secY); histos.fill(HIST("cex_pairtrk_vtxfit_secVtxZ"), secZ); + histos.fill(HIST("cexMotherTrkP"), motherP); + histos.fill(HIST("cexMotherTrkPt"), motherPt); + // Response matrices + histos.fill(HIST("cexResponseP"), motherP, cexPairTrkP); + histos.fill(HIST("cexResponsePt"), motherPt, cexPairTrkPt); + // Pi0 track + if (pion0) { + if (motherP != 0.) + histos.fill(HIST("cexn_pairtrk_p_pi0"), cexPairTrkP / motherP); + if (motherPt != 0.) + histos.fill(HIST("cexn_pairtrk_pt_pi0"), cexPairTrkPt / motherPt); + } else { + if (motherP != 0.) + histos.fill(HIST("cexn_pairtrk_p_noPi0"), cexPairTrkP / motherP); + if (motherPt != 0.) + histos.fill(HIST("cexn_pairtrk_pt_noPi0"), cexPairTrkPt / motherPt); + } + if (isSampleA) { + histos.fill(HIST("cexMotherTrkPt_closureA"), motherPt); + } else { + histos.fill(HIST("cexMotherTrkPt_closureB"), motherPt); + } + if (motherP != 0.) + histos.fill(HIST("cexn_pairtrk_p"), cexPairTrkP / motherP); + if (motherPt != 0.) + histos.fill(HIST("cexn_pairtrk_pt"), cexPairTrkPt / motherPt); + if (motherPz != 0.) + histos.fill(HIST("cexn_pairtrk_pz"), cexPairTrkPz / motherPz); } else { histos.fill(HIST("cexbg_pairtrk_p"), cexPairTrkP); histos.fill(HIST("cexbg_pairtrk_pt"), cexPairTrkPt); @@ -813,108 +908,28 @@ struct NucleiAntineutronCex { histos.fill(HIST("hProcEnumAP_BG"), static_cast(procEnum)); } - // Count material secondaries produced at the same SV as the selected secondary antiproton. - int vtxNAll = 0; - int vtxNCh = 0; - int vtxNNeut = 0; - int vtxNPi0 = 0; - int vtxNGamma = 0; - int vtxNN = 0; - double sumPx_vtx = 0.0; - double sumPy_vtx = 0.0; - double sumPz_vtx = 0.0; - auto* pdgDB = TDatabasePDG::Instance(); - - for (const auto& particle5 : mcPartsThis) { - if (particle5.mcCollisionId() != colId) { - continue; - } - // Same SV (use the SV of the selected secondary antiproton) - if (std::abs(particle5.vx() - antipVx) >= kVtxTol || std::abs(particle5.vy() - antipVy) >= kVtxTol || std::abs(particle5.vz() - antipVz) >= kVtxTol) { - continue; - } - const auto proc5Enum = particle5.getProcess(); - const bool isSecondaryFromMaterial5 = - (!particle5.producedByGenerator()) && (proc5Enum == kPHadronic || proc5Enum == kPHInhelastic); - if (!isSecondaryFromMaterial5) { - continue; - } - ++vtxNAll; - sumPx_vtx += particle5.px(); - sumPy_vtx += particle5.py(); - sumPz_vtx += particle5.pz(); - const int pdg = particle5.pdgCode(); - if (pdg == kPi0) { - ++vtxNPi0; - } - if (pdg == kGamma) { - ++vtxNGamma; - } - if (pdg == kNeutron) { - ++vtxNN; - } - // Charged vs neutral via PDG database (Charge() is in units of e/3) - double q = 0.0; - if (auto* part = pdgDB->GetParticle(pdg)) { - q = part->Charge() / 3.0; - } - if (std::abs(q) > 0.0) { - ++vtxNCh; - } else { - ++vtxNNeut; - } - } - - // Fill histos (final selected candidates only) - if (isCex) { - histos.fill(HIST("hVtxNAll_CEX"), vtxNAll); - histos.fill(HIST("hVtxNCh_CEX"), vtxNCh); - histos.fill(HIST("hVtxNNeut_CEX"), vtxNNeut); - histos.fill(HIST("hVtxNPi0_CEX"), vtxNPi0); - histos.fill(HIST("hVtxNGamma_CEX"), vtxNGamma); - histos.fill(HIST("hVtxNN_CEX"), vtxNN); - } else { - histos.fill(HIST("hVtxNAll_BG"), vtxNAll); - histos.fill(HIST("hVtxNCh_BG"), vtxNCh); - histos.fill(HIST("hVtxNNeut_BG"), vtxNNeut); - histos.fill(HIST("hVtxNPi0_BG"), vtxNPi0); - histos.fill(HIST("hVtxNGamma_BG"), vtxNGamma); - histos.fill(HIST("hVtxNN_BG"), vtxNN); - } - const float vtxfitDX = secX - antipVx; const float vtxfitDY = secY - antipVy; const float vtxfitDZ = secZ - antipVz; const float vtxfitD3D = std::sqrt(vtxfitDX * vtxfitDX + vtxfitDY * vtxfitDY + vtxfitDZ * vtxfitDZ); - - const double dPx = motherPx - sumPx_vtx; - const double dPy = motherPy - sumPy_vtx; - const double dPz = motherPz - sumPz_vtx; - const double deltaP = std::sqrt(dPx * dPx + dPy * dPy + dPz * dPz); - - if (isCex) { - histos.fill(HIST("hDeltaP_CEX"), deltaP); - } else { - histos.fill(HIST("hDeltaP_BG"), deltaP); - } - - if (motherHasTrack) { - if (isCex) { - histos.fill(HIST("hMotherNHitIB_CEX"), motherNHitIB); - } else { - histos.fill(HIST("hMotherNHitIB_BG"), motherNHitIB); - } - } - const uint32_t selMask = 0u; outPairs( isCex, + motherPdg, - motherNHitIB, - colId, - pId, - antipId, + motherP, + motherPt, + motherPz, + motherEta, + motherVz, + + pion0, + static_cast(procEnum), + + static_cast(colId), + static_cast(pId), + static_cast(antipId), cexPairMcP, cexPairMcPt, @@ -925,13 +940,6 @@ struct NucleiAntineutronCex { antipVy, antipVz, - static_cast(vtxNAll), - static_cast(vtxNCh), - static_cast(vtxNNeut), - static_cast(vtxNPi0), - static_cast(vtxNGamma), - static_cast(vtxNN), - cexPairTrkP, cexPairTrkPt, cexPairTrkPz, @@ -986,9 +994,9 @@ struct NucleiAntineutronCex { pItsMap, apItsMap, - static_cast(pLayers ? 1 : 0), - static_cast(antipLayers ? 1 : 0), + pvtxX, + pvtxY, pvtxZ, pTrkItsNSigmaPr, @@ -997,7 +1005,17 @@ struct NucleiAntineutronCex { antipTrkItsNSigmaPr, antipTrkItsPidValid, - antipTrkTgl); + antipTrkTgl, + + pTrkTpcNClsCrossedRows, + pTrkTpcCrossedRowsOverFindableCls, + pTrkTpcChi2NCl, + pTrkItsChi2NCl, + + antipTrkTpcNClsCrossedRows, + antipTrkTpcCrossedRowsOverFindableCls, + antipTrkTpcChi2NCl, + antipTrkItsChi2NCl); } } // ==== end DCAFitter2 ==== diff --git a/PWGLF/TableProducer/Nuspex/nucleiFlowTree.cxx b/PWGLF/TableProducer/Nuspex/nucleiFlowTree.cxx index a17eefdcf21..762d9a07c16 100644 --- a/PWGLF/TableProducer/Nuspex/nucleiFlowTree.cxx +++ b/PWGLF/TableProducer/Nuspex/nucleiFlowTree.cxx @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +58,6 @@ #include #include -#include #include #include @@ -110,7 +110,7 @@ struct nucleiFlowTree { Configurable cfgCutPtMinTree{"cfgCutPtMinTree", 0.2f, "Minimum track transverse momentum for tree saving"}; Configurable cfgCutPtMaxTree{"cfgCutPtMaxTree", 15.0f, "Maximum track transverse momentum for tree saving"}; - Configurable> cfgEventSelections{"cfgEventSelections", {nuclei::EvSelDefault[0], 8, 1, nuclei::eventSelectionLabels, nuclei::eventSelectionTitle}, "Event selections"}; + Configurable> cfgEventSelections{"cfgEventSelections", {nuclei::EvSelDefault[0], nuclei::evSel::kNevSels, 1, nuclei::eventSelectionLabels, nuclei::eventSelectionTitle}, "Event selections"}; Configurable> cfgMomentumScalingBetheBloch{"cfgMomentumScalingBetheBloch", {nuclei::bbMomScalingDefault[0], 5, 2, nuclei::names, nuclei::chargeLabelNames}, "TPC Bethe-Bloch momentum scaling for light nuclei"}; Configurable> cfgBetheBlochParams{"cfgBetheBlochParams", {nuclei::betheBlochDefault[0], 5, 6, nuclei::names, nuclei::betheBlochParNames}, "TPC Bethe-Bloch parameterisation for light nuclei"}; diff --git a/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx b/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx index e4d3987f8da..17dcdd62ec5 100644 --- a/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx +++ b/PWGLF/TableProducer/Nuspex/nucleiSpectra.cxx @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -72,7 +73,6 @@ #include -#include #include #include @@ -230,6 +230,8 @@ std::shared_ptr hFlowHists[2][5]; std::shared_ptr hDCAHists[2][5]; std::shared_ptr hMatchingStudy[2]; std::shared_ptr hMatchingStudyHadrons[2]; +std::shared_ptr hMatchingCountsHadrons; +std::shared_ptr hMatchingCountsHe3; o2::base::MatLayerCylSet* lut = nullptr; std::vector candidates; @@ -297,6 +299,7 @@ struct nucleiSpectra { }; Produces nucleiTable; + Produces nucleiTableCent; Produces nucleiPairTable; Produces nucleiTableMC; Produces nucleiTableMCExtension; @@ -365,6 +368,7 @@ struct nucleiSpectra { ConfigurableAxis cfgNITSClusBins{"cfgNITSClusBins", {3, 4.5, 7.5}, "N ITS clusters binning"}; ConfigurableAxis cfgNTPCClusBins{"cfgNTPCClusBins", {3, 89.5, 159.5}, "N TPC clusters binning"}; + Configurable cfgMatchingCounts{"cfgMatchingCounts", false, "Enable event-level ITS-TPC matching counts for binomiality studies"}; Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; Configurable cfgTriggerList{"cfgTriggerList", "fHe", "Trigger List"}; Configurable cfgSelectTrgEv{"cfgSelectTrgEv", false, "If true, select events with active trigger list"}; @@ -393,7 +397,7 @@ struct nucleiSpectra { HistogramRegistry spectra{"spectra", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - double computeAbsoDecL(aod::McParticles::iterator particle) + double computeAbsoDecL(const aod::McParticles::iterator& particle) { if (!particle.has_daughters()) return -1.f; @@ -600,6 +604,10 @@ struct nucleiSpectra { if (doprocessMatching) { std::vector occBins{-0.5, 499.5, 999.5, 1999.5, 2999.5, 3999.5, 4999.5, 10000., 50000.}; AxisSpec occAxis{occBins, "Occupancy"}; + if (cfgMatchingCounts) { + nuclei::hMatchingCountsHadrons = spectra.add("hMatchingCountsHadrons", ";N_{ITS};N_{ITS+TPC};Centrality;Occupancy;Charge sign", HistType::kTHnSparseD, {{10001, -0.5, 10000.5}, {10001, -0.5, 10000.5}, {8, 0., 80.}, occAxis, {2, -0.5, 1.5}}); + nuclei::hMatchingCountsHe3 = spectra.add("hMatchingCountsHe3", ";N_{ITS};N_{ITS+TPC};Centrality;Occupancy;Charge sign", HistType::kTHnSparseD, {{10001, -0.5, 10000.5}, {10001, -0.5, 10000.5}, {8, 0., 80.}, occAxis, {2, -0.5, 1.5}}); + } for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { nuclei::hMatchingStudy[iC] = spectra.add(fmt::format("hMatchingStudy{}", nuclei::matter[iC]).data(), ";#it{p}_{T};#phi;#eta;n#sigma_{ITS};n#sigma{TPC};n#sigma_{TOF};Centrality", HistType::kTHnSparseF, {{20, 1., 9.}, {10, 0., o2::constants::math::TwoPI}, {10, -1., 1.}, {50, -5., 5.}, {50, -5., 5.}, {50, 0., 1.}, {8, 0., 80.}}); nuclei::hMatchingStudyHadrons[iC] = spectra.add(fmt::format("hMatchingStudyHadrons{}", nuclei::matter[iC]).data(), ";#it{p}_{T};#phi;#eta;Centrality;Track type; Occupancy", HistType::kTHnF, {{23, 0.4, 5.}, {20, 0., o2::constants::math::TwoPI}, {10, -1., 1.}, {8, 0., 80.}, {2, -0.5, 1.5}, occAxis}); @@ -841,6 +849,16 @@ struct nucleiSpectra { std::hypot(collision.qvecBTotIm(), collision.qvecBTotRe()), std::hypot(collision.qvecBNegIm(), collision.qvecBNegRe()), std::hypot(collision.qvecBPosIm(), collision.qvecBPosRe())}); + } else if constexpr (requires { + collision.centFT0C(); + }) { + nuclei::candidates_flow.emplace_back(NucleusCandidateFlow{ + collision.centFV0A(), + collision.centFT0M(), + collision.centFT0A(), + collision.centFT0C(), + 0.f, 0.f, 0.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 0.f, 0.f}); } if (fillTree) { if (flag & kTriton) { @@ -860,9 +878,10 @@ struct nucleiSpectra { nuclei::hGloTOFtracks[1]->Fill(nGloTracks[1], nTOFTracks[1]); } - void processData(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) + void processData(CollWithCent const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) { nuclei::candidates.clear(); + nuclei::candidates_flow.clear(); if (!eventSelectionWithHisto(collision)) { return; } @@ -870,8 +889,10 @@ struct nucleiSpectra { fillDataInfo(collision, tracks); for (size_t i1{0}; i1 < nuclei::candidates.size(); ++i1) { auto& c1 = nuclei::candidates[i1]; + auto& c1Cent = nuclei::candidates_flow[i1]; if (c1.fillTree) { nucleiTable(c1.pt, c1.eta, c1.phi, c1.tpcInnerParam, c1.beta, c1.zVertex, c1.nContrib, c1.DCAxy, c1.DCAz, c1.TPCsignal, c1.ITSchi2, c1.TPCchi2, c1.TOFchi2, c1.flags, c1.TPCfindableCls, c1.TPCcrossedRows, c1.ITSclsMap, c1.TPCnCls, c1.TPCnClsShared, c1.clusterSizesITS); + nucleiTableCent(c1Cent.centFV0A, c1Cent.centFT0M, c1Cent.centFT0A, c1Cent.centFT0C); if (cfgFillPairTree) { for (size_t i2{i1 + 1}; i2 < nuclei::candidates.size(); ++i2) { auto& c2 = nuclei::candidates[i2]; @@ -1142,12 +1163,17 @@ struct nucleiSpectra { } PROCESS_SWITCH(nucleiSpectra, processMC, "MC analysis", false); - void processMatching(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) + void processMatching(soa::Join::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) { - if (!eventSelection(collision) || !collision.triggereventep()) { + if (!eventSelection(collision) || !collision.isCalibrated()) { return; } const float centrality = collision.centFT0C(); + const float psi2FT0C = std::atan2(collision.qvecFT0CIm(), collision.qvecFT0CRe()) / 2.f; + int nITSHadrons[2]{}; + int nITSTPCHadrons[2]{}; + int nITSHe3[2]{}; + int nITSTPCHe3[2]{}; o2::aod::ITSResponse itsResponse; for (const auto& track : tracks) { if (std::abs(track.eta()) > cfgTrackCut.EtaMax || @@ -1159,13 +1185,28 @@ struct nucleiSpectra { double expSigma{expBethe * cfgBetheBlochParams->get(4, 5u)}; double nSigmaTPC{(track.tpcSignal() - expBethe) / expSigma}; int iC = track.signed1Pt() > 0; + if (cfgMatchingCounts) { + ++nITSHadrons[iC]; + nITSTPCHadrons[iC] += track.hasTPC(); + } const float pt = track.pt(); - const float phi = 2.f * RecoDecay::constrainAngle(track.phi() - collision.psiFT0C(), 0.f, 2); + const float phi = 2.f * RecoDecay::constrainAngle(track.phi() - psi2FT0C, 0.f, 2); nuclei::hMatchingStudyHadrons[iC]->Fill(pt, phi, track.eta(), centrality, track.hasTPC(), collision.trackOccupancyInTimeRange()); - if (itsResponse.nSigmaITS(track) > -1.) { + if (itsResponse.nSigmaITS(track) > -2.) { + if (cfgMatchingCounts) { + ++nITSHe3[iC]; + nITSTPCHe3[iC] += track.hasTPC(); + } nuclei::hMatchingStudy[iC]->Fill(pt * 2, phi, track.eta(), itsResponse.nSigmaITS(track), nSigmaTPC, o2::pid::tof::Beta::GetBeta(track), centrality); } } + if (cfgMatchingCounts) { + // One entry per collision and charge sign, including zero-track events. + for (unsigned int iC{0}; iC < nuclei::matter.size(); ++iC) { + nuclei::hMatchingCountsHadrons->Fill(nITSHadrons[iC], nITSTPCHadrons[iC], centrality, collision.trackOccupancyInTimeRange(), iC); + nuclei::hMatchingCountsHe3->Fill(nITSHe3[iC], nITSTPCHe3[iC], centrality, collision.trackOccupancyInTimeRange(), iC); + } + } } PROCESS_SWITCH(nucleiSpectra, processMatching, "Matching analysis", false); diff --git a/PWGLF/TableProducer/Nuspex/nucleiThreeBodyBuilder.cxx b/PWGLF/TableProducer/Nuspex/nucleiThreeBodyBuilder.cxx new file mode 100644 index 00000000000..a78843606e5 --- /dev/null +++ b/PWGLF/TableProducer/Nuspex/nucleiThreeBodyBuilder.cxx @@ -0,0 +1,750 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file nucleiThreeBodyBuilder.cxx +/// \brief Generic He3-hadron-hadron secondary-vertex table producer +/// \author ALICE Collaboration + +#include "PWGLF/DataModel/Vtx3BodyTables.h" +#include "PWGLF/Utils/svPoolCreator.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +namespace +{ +using Collisions = soa::Join; +using CollisionsPbPb = soa::Join; +using CollisionsMC = soa::Join; +using Tracks = soa::Join; +using TracksMC = soa::Join; + +constexpr int He3Pdg = o2::constants::physics::Pdg::kHelium3; +constexpr float InvalidFloat = -999.f; +constexpr int InvalidLabel = -1; +constexpr int NProngs = 3; +constexpr float He3Charge = 2.f; +constexpr float UseCCDBBzThreshold = -990.f; +constexpr int He3PoolId = 1; +constexpr int Daughter1PoolId = 2; +constexpr int Daughter2PoolId = 3; +constexpr std::array DefaultHe3BetheBloch{-321.34, 0.6539, 1.591, 0.8225, 2.363, 0.09}; +const std::vector BetheBlochNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; +const std::vector He3Name{"He3"}; + +enum MCMatchStatus : uint8_t { + MissingDaughterLabel = 0, + NoCommonImmediateMother, + CommonImmediateMother, + ConfiguredDecay +}; + +bool isSupportedHadronPdg(int pdg) +{ + const int absPdg = std::abs(pdg); + return absPdg == PDG_t::kPiPlus || + absPdg == PDG_t::kKPlus || absPdg == PDG_t::kProton; +} +} // namespace + +struct NucleiThreeBodyBuilder { + Produces outputData; + Produces outputMC; + + Service ccdb; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + struct : ConfigurableGroup { + std::string prefix{"event"}; + Configurable requireSel8{"requireSel8", true, "Require sel8 (main pp default)"}; + Configurable maxAbsZvtx{"maxAbsZvtx", 10.f, "Maximum absolute PV z; negative disables"}; + Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", false, "Require kNoITSROFrameBorder"}; + Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", false, "Require kNoTimeFrameBorder"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "Require kNoSameBunchPileup"}; + Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, "Require kIsGoodZvtxFT0vsPV"}; + } event; + + struct : ConfigurableGroup { + std::string prefix{"pbpb"}; + Configurable minCentFT0C{"minCentFT0C", 0.f, "Minimum FT0C centrality"}; + Configurable maxCentFT0C{"maxCentFT0C", 100.f, "Maximum FT0C centrality"}; + } pbpb; + + struct : ConfigurableGroup { + std::string prefix{"he3"}; + Configurable minTPCInnerParam{"minTPCInnerParam", 0.8f, "Minimum He3 TPC rigidity"}; + Configurable maxAbsTPCNSigma{"maxAbsTPCNSigma", 5.f, "Maximum absolute custom He3 TPC n-sigma"}; + Configurable requirePIDForTracking{"requirePIDForTracking", false, "Require Helium3 or Alpha PID during tracking"}; + Configurable compensatePIDinTracking{"compensatePIDinTracking", true, "Divide TPC inner parameter by two for charge-two tracking PID"}; + Configurable> betheBlochParams{"betheBlochParams", {DefaultHe3BetheBloch.data(), 1, 6, He3Name, BetheBlochNames}, "TPC Bethe-Bloch parameters for He3"}; + } he3; + + struct : ConfigurableGroup { + std::string prefix{"track"}; + Configurable maxAbsEta{"maxAbsEta", 0.9f, "Maximum absolute eta for every daughter"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum TPC crossed rows for every daughter"}; + Configurable minAbsDCAxyToPV{"minAbsDCAxyToPV", 0.f, "Minimum absolute DCAxy to the assigned/candidate PV for every daughter; zero disables"}; + Configurable minAbsDCAzToPV{"minAbsDCAzToPV", 0.f, "Minimum absolute DCAz to the assigned/candidate PV for every daughter; zero disables"}; + } trackSelections; + + struct : ConfigurableGroup { + std::string prefix{"decay"}; + Configurable daughter1Pdg{"daughter1Pdg", PDG_t::kProton, "Signed pi/K/p PDG relative to a positive He3; used for charge pairing, optional PID selection, and MC matching"}; + Configurable daughter2Pdg{"daughter2Pdg", -PDG_t::kPiPlus, "Signed pi/K/p PDG relative to a positive He3; used for charge pairing, optional PID selection, and MC matching"}; + Configurable motherPdg{"motherPdg", 1010020040, "Mother PDG for MC matching; zero accepts either common mother"}; + } decay; + + struct : ConfigurableGroup { + std::string prefix{"vertex"}; + Configurable minCosPA{"minCosPA", 0.9f, "Minimum cosine of pointing angle; below -1 disables"}; + } vertexSelections; + + struct : ConfigurableGroup { + std::string prefix{"candidate"}; + Configurable applySelections{"applySelections", false, "Apply the additional candidate selections"}; + Configurable maxChi2{"maxChi2", 100.f, "Maximum DCA-fitter chi2"}; + Configurable maxDCADaughters{"maxDCADaughters", 1.f, "Maximum square root of fitter chi2"}; + Configurable maxAbsTPCNSigmaDaughter1{"maxAbsTPCNSigmaDaughter1", 5.f, "Maximum daughter 1 TPC n-sigma for its configured hypothesis"}; + Configurable maxAbsTOFNSigmaDaughter1{"maxAbsTOFNSigmaDaughter1", 5.f, "Maximum daughter 1 TOF n-sigma; applied only with TOF"}; + Configurable maxAbsTPCNSigmaDaughter2{"maxAbsTPCNSigmaDaughter2", 5.f, "Maximum daughter 2 TPC n-sigma for its configured hypothesis"}; + Configurable maxAbsTOFNSigmaDaughter2{"maxAbsTOFNSigmaDaughter2", 5.f, "Maximum daughter 2 TOF n-sigma; applied only with TOF"}; + Configurable minDecayLength{"minDecayLength", 0.f, "Minimum decay length"}; + Configurable maxDecayLength{"maxDecayLength", 100.f, "Maximum decay length"}; + } candidateSelections; + + struct : ConfigurableGroup { + std::string prefix{"pool"}; + Configurable timeMargin{"timeMargin", 800.f, "Track-collision compatibility margin in ns"}; + Configurable skipAmbiguousTracks{"skipAmbiguousTracks", false, "Reject unassigned ambiguous tracks"}; + } pool; + + struct : ConfigurableGroup { + std::string prefix{"zorro"}; + Configurable enabled{"enabled", false, "Enable fHe trigger selection and accounting for skimmed pp data"}; + Configurable ccdbPath{"ccdbPath", "EventFiltering/Zorro/", "Base path of Zorro CCDB objects"}; + Configurable bcTolerance{"bcTolerance", 100, "Zorro BC matching tolerance"}; + } zorroOptions; + + struct : ConfigurableGroup { + std::string prefix{"mc"}; + Configurable requireConfiguredDecayForGen{"requireConfiguredDecayForGen", true, "Fill generated quantities only for the configured daughters and optional mother PDG"}; + } mc; + + Configurable ccdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "CCDB URL"}; // o2-linter: disable=name/configurable (Established CCDB option name.) + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of magnetic-field object"}; + Configurable bzInput{"bz", -999.f, "Magnetic field in kG; below -990 uses CCDB"}; // o2-linter: disable=name/configurable (Established magnetic-field option name.) + + o2::vertexing::DCAFitterN<3> fitter; + svPoolCreator3Body tripletPool{He3PoolId, Daughter1PoolId, Daughter2PoolId}; + int runNumber = -1; + float bz = 0.f; + std::array he3BetheBloch{}; + std::vector selectedCollisions; + std::vector collisionsWithHe3; + std::vector identifiedHe3Tracks; + std::vector preselectedTracks; + + struct RecoCandidate { + std::array signs{}; + float pt = 0.f; + float eta = 0.f; + float phi = 0.f; + std::array daughterMomentum{}; + float chi2 = 0.f; + float dcaDaughters = 0.f; + float cosPA = 0.f; + float decayLength = 0.f; + std::array dcaXYToPV{}; + std::array dcaZToPV{}; + float tpcNSigmaHe3 = InvalidFloat; + float tofNSigmaHe3 = InvalidFloat; + std::array, 2> tpcNSigmaHadron{}; + std::array, 2> tofNSigmaHadron{}; + std::array tpcNCls{}; + std::array tpcCrossedRows{}; + std::array itsNCls{}; + }; + + struct MCInfo { + std::array pdgs{}; + uint8_t matchStatus = MissingDaughterLabel; + int motherPdg = 0; + float motherPt = InvalidFloat; + float motherEta = InvalidFloat; + float motherPhi = InvalidFloat; + float decayLength = InvalidFloat; + }; + + void init(InitContext const&) + { + const int enabledProcesses = static_cast(doprocessData) + static_cast(doprocessPbPb) + static_cast(doprocessMC); + if (enabledProcesses != 1) { + LOG(fatal) << "Enable exactly one of processData, processPbPb, and processMC"; + } + if (zorroOptions.enabled && !doprocessData) { + LOG(fatal) << "Zorro is supported only by the pp data process path"; + } + if (!isSupportedHadronPdg(decay.daughter1Pdg) || !isSupportedHadronPdg(decay.daughter2Pdg)) { + LOG(fatal) << "Signed daughter PDG codes must identify a pion, kaon, or proton"; + } + + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + zorroSummary.setObject(zorro.getZorroSummary()); + zorro.setBaseCCDBPath(zorroOptions.ccdbPath); + zorro.setBCtolerance(zorroOptions.bcTolerance); + + fitter.setPropagateToPCA(true); + fitter.setMaxR(200.f); + fitter.setMinParamChange(1.e-3f); + fitter.setMinRelChi2Change(0.9f); + fitter.setMaxDZIni(1.e9f); + fitter.setMaxChi2(1.e9f); + fitter.setUseAbsDCA(true); + + tripletPool.setTimeMargin(pool.timeMargin); + if (pool.skipAmbiguousTracks) { + tripletPool.setSkipAmbiTracks(); + } + for (size_t i = 0; i < he3BetheBloch.size(); ++i) { + he3BetheBloch[i] = he3.betheBlochParams->get("He3", BetheBlochNames[i].c_str()); + } + + registry.add("events", "Event and He3 preselection;step;events", HistType::kTH1D, {{5, -0.5, 4.5}}); + auto events = registry.get(HIST("events")); + events->GetXaxis()->SetBinLabel(1, "all"); + events->GetXaxis()->SetBinLabel(2, "Zorro fHe"); + events->GetXaxis()->SetBinLabel(3, "event selected"); + events->GetXaxis()->SetBinLabel(4, "identified He3"); + events->GetXaxis()->SetBinLabel(5, "candidate filled"); + registry.add("zorroEvents", "Zorro accounting;selection;events", HistType::kTH1D, {{2, -0.5, 1.5}}); + auto zorroEvents = registry.get(HIST("zorroEvents")); + zorroEvents->GetXaxis()->SetBinLabel(1, "fHe before event selection"); + zorroEvents->GetXaxis()->SetBinLabel(2, "fHe after event selection"); + registry.add("triplets", "Triplet building;step;count", HistType::kTH1D, {{3, -0.5, 2.5}}); + auto triplets = registry.get(HIST("triplets")); + triplets->GetXaxis()->SetBinLabel(1, "pool combinations"); + triplets->GetXaxis()->SetBinLabel(2, "fit succeeded"); + triplets->GetXaxis()->SetBinLabel(3, "stored"); + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (runNumber == bc.runNumber()) { + return; + } + if (zorroOptions.enabled) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fHe"); + zorro.populateHistRegistry(registry, bc.runNumber()); + } + auto* grpmag = ccdb->getForTimeStamp(grpmagPath, bc.timestamp()); + if (!grpmag) { + LOG(fatal) << "Missing magnetic-field object " << grpmagPath << " for timestamp " << bc.timestamp(); + } + o2::base::Propagator::initFieldFromGRP(grpmag); + bz = bzInput < UseCCDBBzThreshold ? o2::base::Propagator::Instance()->getNominalBz() : bzInput; + o2::base::Propagator::Instance()->setNominalBz(bz); + fitter.setBz(bz); + runNumber = bc.runNumber(); + } + + template + float correctedHe3Rigidity(TTrack const& track) + { + const bool chargeTwoPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; + return chargeTwoPID && he3.compensatePIDinTracking ? track.tpcInnerParam() / He3Charge : track.tpcInnerParam(); + } + + template + float tpcNSigmaHe3(TTrack const& track) + { + const float rigidity = correctedHe3Rigidity(track); + const float expected = o2::common::BetheBlochAleph(static_cast(rigidity * He3Charge / constants::physics::MassHelium3), + he3BetheBloch[0], he3BetheBloch[1], he3BetheBloch[2], + he3BetheBloch[3], he3BetheBloch[4]); + return (track.tpcSignal() - expected) / (expected * he3BetheBloch[5]); + } + + template + bool isIdentifiedHe3(TTrack const& track) + { + if (!track.hasTPC() || correctedHe3Rigidity(track) < he3.minTPCInnerParam || + std::abs(tpcNSigmaHe3(track)) > he3.maxAbsTPCNSigma) { + return false; + } + if (he3.requirePIDForTracking && + track.pidForTracking() != o2::track::PID::Helium3 && + track.pidForTracking() != o2::track::PID::Alpha) { + return false; + } + return true; + } + + template + bool passesEventSelection(TCollision const& collision) + { + if (event.requireSel8 && !collision.sel8()) { + return false; + } + if (event.maxAbsZvtx >= 0.f && std::abs(collision.posZ()) > event.maxAbsZvtx) { + return false; + } + if (event.requireNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (event.requireNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (event.requireNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (event.requireGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + return true; + } + + template + void selectCollisions(TCollisions const& collisions, bool applyZorro) + { + selectedCollisions.assign(collisions.size(), false); + collisionsWithHe3.assign(collisions.size(), false); + for (const auto& collision : collisions) { + registry.fill(HIST("events"), 0.); + const auto bc = collision.template bc_as(); + initCCDB(bc); + + if (applyZorro) { + if (!zorro.isSelected(bc.globalBC(), zorroOptions.bcTolerance)) { + continue; + } + registry.fill(HIST("events"), 1.); + registry.fill(HIST("zorroEvents"), 0.); + } + if (!passesEventSelection(collision)) { + continue; + } + if constexpr (applyCentrality) { + if (collision.centFT0C() < pbpb.minCentFT0C || collision.centFT0C() > pbpb.maxCentFT0C) { + continue; + } + } + selectedCollisions[collision.globalIndex()] = true; + registry.fill(HIST("events"), 2.); + if (applyZorro) { + registry.fill(HIST("zorroEvents"), 1.); + } + } + } + + template + std::array dcaToPV(TCollision const& collision, TTrack const& track) + { + auto trackPar = getTrackParCov(track); + std::array dca{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz( + {collision.posX(), collision.posY(), collision.posZ()}, trackPar, 2.f, fitter.getMatCorrType(), &dca); + return dca; + } + + template + bool passesTrackSelections(TTrack const& track, std::array const& dca) + { + return std::abs(track.eta()) <= trackSelections.maxAbsEta && + track.tpcNClsCrossedRows() >= trackSelections.minTPCCrossedRows && + std::abs(dca[0]) >= trackSelections.minAbsDCAxyToPV && + std::abs(dca[1]) >= trackSelections.minAbsDCAzToPV; + } + + template + static std::array tpcHadronNSigma(TTrack const& track) + { + return {track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()}; + } + + template + static std::array tofHadronNSigma(TTrack const& track) + { + if (!track.hasTOF()) { + return {InvalidFloat, InvalidFloat, InvalidFloat}; + } + return {track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()}; + } + + static float selectedNSigma(std::array const& values, int pdg) + { + switch (std::abs(pdg)) { + case PDG_t::kPiPlus: + return values[0]; + case PDG_t::kKPlus: + return values[1]; + case PDG_t::kProton: + return values[2]; + default: + return 0.f; + } + } + + template + bool buildCandidate(TCollision const& collision, TTrack const& he3Track, + TTrack const& daughter1Track, TTrack const& daughter2Track, + RecoCandidate& out) + { + const std::array tracks{he3Track, daughter1Track, daughter2Track}; + for (int i = 0; i < NProngs; ++i) { + const auto dca = dcaToPV(collision, tracks[i]); + if (!passesTrackSelections(tracks[i], dca)) { + return false; + } + out.dcaXYToPV[i] = dca[0]; + out.dcaZToPV[i] = dca[1]; + } + + auto he3TrackFit = getTrackParCov(he3Track); + auto daughter1TrackFit = getTrackParCov(daughter1Track); + auto daughter2TrackFit = getTrackParCov(daughter2Track); + int nCandidates = 0; + try { + nCandidates = fitter.process(he3TrackFit, daughter1TrackFit, daughter2TrackFit); + } catch (...) { + LOG(error) << "Exception while fitting a nuclei three-body candidate"; + return false; + } + if (nCandidates == 0) { + return false; + } + registry.fill(HIST("triplets"), 1.); + + out.signs = {static_cast(he3Track.sign() > 0 ? 1 : -1), + static_cast(daughter1Track.sign() > 0 ? 1 : -1), + static_cast(daughter2Track.sign() > 0 ? 1 : -1)}; + + std::array, 3> daughterMomenta{}; + std::array momentum{}; + for (int i = 0; i < NProngs; ++i) { + fitter.getTrack(i).getPxPyPzGlo(daughterMomenta[i]); + } + for (int component = 0; component < NProngs; ++component) { + daughterMomenta[0][component] *= He3Charge; + momentum[component] = daughterMomenta[0][component] + + daughterMomenta[1][component] + + daughterMomenta[2][component]; + } + for (int i = 0; i < NProngs; ++i) { + out.daughterMomentum[i] = RecoDecay::sqrtSumOfSquares( + daughterMomenta[i][0], daughterMomenta[i][1], daughterMomenta[i][2]); + } + out.pt = RecoDecay::sqrtSumOfSquares(momentum[0], momentum[1]); + out.eta = RecoDecay::eta(momentum); + out.phi = RecoDecay::phi(momentum); + const float momentumMagnitude = RecoDecay::sqrtSumOfSquares(momentum[0], momentum[1], momentum[2]); + + out.chi2 = fitter.getChi2AtPCACandidate(); + out.dcaDaughters = std::sqrt(std::max(0.f, out.chi2)); + const auto& secondaryVertex = fitter.getPCACandidate(); + const std::array flight{ + static_cast(secondaryVertex[0] - collision.posX()), + static_cast(secondaryVertex[1] - collision.posY()), + static_cast(secondaryVertex[2] - collision.posZ())}; + out.decayLength = RecoDecay::sqrtSumOfSquares(flight[0], flight[1], flight[2]); + out.cosPA = (flight[0] * momentum[0] + flight[1] * momentum[1] + flight[2] * momentum[2]) / + (out.decayLength * momentumMagnitude + 1.e-10f); + if (out.cosPA < vertexSelections.minCosPA) { + return false; + } + + out.tpcNSigmaHe3 = tpcNSigmaHe3(he3Track); + out.tofNSigmaHe3 = he3Track.hasTOF() ? he3Track.tofNSigmaHe() : InvalidFloat; + out.tpcNSigmaHadron[0] = tpcHadronNSigma(daughter1Track); + out.tpcNSigmaHadron[1] = tpcHadronNSigma(daughter2Track); + out.tofNSigmaHadron[0] = tofHadronNSigma(daughter1Track); + out.tofNSigmaHadron[1] = tofHadronNSigma(daughter2Track); + for (int i = 0; i < NProngs; ++i) { + out.tpcNCls[i] = tracks[i].tpcNClsFound(); + out.tpcCrossedRows[i] = tracks[i].tpcNClsCrossedRows(); + out.itsNCls[i] = tracks[i].itsNCls(); + } + + if (!candidateSelections.applySelections) { + return true; + } + if (out.chi2 > candidateSelections.maxChi2 || + out.dcaDaughters > candidateSelections.maxDCADaughters || + out.decayLength < candidateSelections.minDecayLength || + out.decayLength > candidateSelections.maxDecayLength || + std::abs(selectedNSigma(out.tpcNSigmaHadron[0], decay.daughter1Pdg)) > candidateSelections.maxAbsTPCNSigmaDaughter1 || + std::abs(selectedNSigma(out.tpcNSigmaHadron[1], decay.daughter2Pdg)) > candidateSelections.maxAbsTPCNSigmaDaughter2 || + (daughter1Track.hasTOF() && + std::abs(selectedNSigma(out.tofNSigmaHadron[0], decay.daughter1Pdg)) > candidateSelections.maxAbsTOFNSigmaDaughter1) || + (daughter2Track.hasTOF() && + std::abs(selectedNSigma(out.tofNSigmaHadron[1], decay.daughter2Pdg)) > candidateSelections.maxAbsTOFNSigmaDaughter2)) { + return false; + } + return true; + } + + void fillDataTable(RecoCandidate const& c) + { + outputData(c.signs[0], c.signs[1], c.signs[2], + c.pt, c.eta, c.phi, + c.daughterMomentum[0], c.daughterMomentum[1], c.daughterMomentum[2], + c.chi2, c.dcaDaughters, c.cosPA, c.decayLength, + c.dcaXYToPV[0], c.dcaZToPV[0], + c.dcaXYToPV[1], c.dcaZToPV[1], + c.dcaXYToPV[2], c.dcaZToPV[2], + c.tpcNSigmaHe3, c.tofNSigmaHe3, + c.tpcNSigmaHadron[0][0], c.tpcNSigmaHadron[0][1], c.tpcNSigmaHadron[0][2], + c.tofNSigmaHadron[0][0], c.tofNSigmaHadron[0][1], c.tofNSigmaHadron[0][2], + c.tpcNSigmaHadron[1][0], c.tpcNSigmaHadron[1][1], c.tpcNSigmaHadron[1][2], + c.tofNSigmaHadron[1][0], c.tofNSigmaHadron[1][1], c.tofNSigmaHadron[1][2], + c.tpcNCls[0], c.tpcNCls[1], c.tpcNCls[2], + c.tpcCrossedRows[0], c.tpcCrossedRows[1], c.tpcCrossedRows[2], + c.itsNCls[0], c.itsNCls[1], c.itsNCls[2]); + } + + template + MCInfo getMCInfo(TTrack const& he3Track, TTrack const& daughter1Track, + TTrack const& daughter2Track, aod::McParticles const& mcParticles) + { + MCInfo info; + if (!he3Track.has_mcParticle() || !daughter1Track.has_mcParticle() || !daughter2Track.has_mcParticle()) { + return info; + } + const auto he3Particle = he3Track.template mcParticle_as(); + const auto daughter1Particle = daughter1Track.template mcParticle_as(); + const auto daughter2Particle = daughter2Track.template mcParticle_as(); + info.pdgs = {he3Particle.pdgCode(), daughter1Particle.pdgCode(), daughter2Particle.pdgCode()}; + info.matchStatus = NoCommonImmediateMother; + + int commonMotherLabel = InvalidLabel; + for (const auto& he3Mother : he3Particle.template mothers_as()) { + for (const auto& daughter1Mother : daughter1Particle.template mothers_as()) { + if (he3Mother.globalIndex() != daughter1Mother.globalIndex()) { + continue; + } + for (const auto& daughter2Mother : daughter2Particle.template mothers_as()) { + if (he3Mother.globalIndex() == daughter2Mother.globalIndex()) { + commonMotherLabel = he3Mother.globalIndex(); + break; + } + } + } + } + if (commonMotherLabel < 0) { + return info; + } + + const auto selectedMother = mcParticles.rawIteratorAt(commonMotherLabel); + info.matchStatus = CommonImmediateMother; + info.motherPdg = selectedMother.pdgCode(); + const int he3Sign = he3Track.sign() > 0 ? 1 : -1; + const bool expectedDaughters = info.pdgs[0] == he3Sign * He3Pdg && + info.pdgs[1] == he3Sign * decay.daughter1Pdg && + info.pdgs[2] == he3Sign * decay.daughter2Pdg; + const bool motherAccepted = decay.motherPdg == 0 || + info.motherPdg == he3Sign * std::abs(decay.motherPdg.value); + const bool configuredDecay = expectedDaughters && motherAccepted; + if (configuredDecay) { + info.matchStatus = ConfiguredDecay; + } + if ((!mc.requireConfiguredDecayForGen || configuredDecay) && motherAccepted) { + const std::array motherMomentum{selectedMother.px(), selectedMother.py(), selectedMother.pz()}; + info.motherPt = RecoDecay::sqrtSumOfSquares(motherMomentum[0], motherMomentum[1]); + info.motherEta = RecoDecay::eta(motherMomentum); + info.motherPhi = RecoDecay::phi(motherMomentum); + info.decayLength = RecoDecay::sqrtSumOfSquares(he3Particle.vx() - selectedMother.vx(), + he3Particle.vy() - selectedMother.vy(), + he3Particle.vz() - selectedMother.vz()); + } + return info; + } + + void fillMCTable(RecoCandidate const& c, MCInfo const& m) + { + outputMC(c.signs[0], c.signs[1], c.signs[2], + c.pt, c.eta, c.phi, + c.daughterMomentum[0], c.daughterMomentum[1], c.daughterMomentum[2], + c.chi2, c.dcaDaughters, c.cosPA, c.decayLength, + c.dcaXYToPV[0], c.dcaZToPV[0], + c.dcaXYToPV[1], c.dcaZToPV[1], + c.dcaXYToPV[2], c.dcaZToPV[2], + c.tpcNSigmaHe3, c.tofNSigmaHe3, + c.tpcNSigmaHadron[0][0], c.tpcNSigmaHadron[0][1], c.tpcNSigmaHadron[0][2], + c.tofNSigmaHadron[0][0], c.tofNSigmaHadron[0][1], c.tofNSigmaHadron[0][2], + c.tpcNSigmaHadron[1][0], c.tpcNSigmaHadron[1][1], c.tpcNSigmaHadron[1][2], + c.tofNSigmaHadron[1][0], c.tofNSigmaHadron[1][1], c.tofNSigmaHadron[1][2], + c.tpcNCls[0], c.tpcNCls[1], c.tpcNCls[2], + c.tpcCrossedRows[0], c.tpcCrossedRows[1], c.tpcCrossedRows[2], + c.itsNCls[0], c.itsNCls[1], c.itsNCls[2], + m.pdgs[0], m.pdgs[1], m.pdgs[2], + m.matchStatus, m.motherPdg, + m.motherPt, m.motherEta, m.motherPhi, m.decayLength); + } + + template + void build(TCollisions const& collisions, TTracks const& tracks, + aod::AmbiguousTracks const& ambiguousTracks, + aod::BCsWithTimestamps const& bcs, + aod::McParticles const* mcParticles = nullptr) + { + selectCollisions(collisions, zorroOptions.enabled && !isMC); + identifiedHe3Tracks.assign(tracks.size(), false); + preselectedTracks.assign(tracks.size(), false); + + // Apply the common track preselection before identifying He3 candidates. + // Consequently, the triplet pools are never built unless a selected event + // contains at least one He3 that also passes the minimal track selections. + for (const auto& track : tracks) { + if (!track.has_collision() || track.collisionId() < 0 || + !selectedCollisions[track.collisionId()]) { + continue; + } + const auto collision = collisions.rawIteratorAt(track.collisionId()); + const auto dca = dcaToPV(collision, track); + if (!passesTrackSelections(track, dca)) { + continue; + } + preselectedTracks[track.globalIndex()] = true; + if (isIdentifiedHe3(track)) { + identifiedHe3Tracks[track.globalIndex()] = true; + collisionsWithHe3[track.collisionId()] = true; + } + } + for (size_t i = 0; i < collisionsWithHe3.size(); ++i) { + if (collisionsWithHe3[i]) { + registry.fill(HIST("events"), 3.); + } + } + + tripletPool.clearPools(); + tripletPool.fillBC2Coll(collisions, bcs); + for (const auto& track : tracks) { + if (!track.has_collision() || track.collisionId() < 0 || + !preselectedTracks[track.globalIndex()] || + !collisionsWithHe3[track.collisionId()]) { + continue; + } + if (identifiedHe3Tracks[track.globalIndex()]) { + tripletPool.appendTrackCand(track, collisions, He3PoolId, ambiguousTracks, bcs); + } else { + tripletPool.appendTrackCand(track, collisions, Daughter1PoolId, ambiguousTracks, bcs); + tripletPool.appendTrackCand(track, collisions, Daughter2PoolId, ambiguousTracks, bcs); + } + } + + const bool daughter1LikeSign = decay.daughter1Pdg > 0; + const bool daughter2LikeSign = decay.daughter2Pdg > 0; + auto& triplets = tripletPool.getSVCandPool(collisions, daughter1LikeSign, daughter2LikeSign); + for (const auto& triplet : triplets) { + registry.fill(HIST("triplets"), 0.); + const auto he3Track = tracks.rawIteratorAt(triplet.tr0Idx); + const auto daughter1Track = tracks.rawIteratorAt(triplet.tr1Idx); + const auto daughter2Track = tracks.rawIteratorAt(triplet.tr2Idx); + for (int collisionIndex = triplet.collBracket.getMin(); collisionIndex <= triplet.collBracket.getMax(); ++collisionIndex) { + if (!selectedCollisions[collisionIndex] || !collisionsWithHe3[collisionIndex]) { + continue; + } + const auto collision = collisions.rawIteratorAt(collisionIndex); + RecoCandidate reco; + if (!buildCandidate(collision, he3Track, daughter1Track, daughter2Track, reco)) { + continue; + } + if constexpr (isMC) { + fillMCTable(reco, getMCInfo(he3Track, daughter1Track, daughter2Track, *mcParticles)); + } else { + fillDataTable(reco); + } + registry.fill(HIST("events"), 4.); + registry.fill(HIST("triplets"), 2.); + } + } + } + + void processData(Collisions const& collisions, Tracks const& tracks, + aod::AmbiguousTracks const& ambiguousTracks, + aod::BCsWithTimestamps const& bcs) + { + build(collisions, tracks, ambiguousTracks, bcs); + } + PROCESS_SWITCH(NucleiThreeBodyBuilder, processData, "Process pp data", true); + + void processPbPb(CollisionsPbPb const& collisions, Tracks const& tracks, + aod::AmbiguousTracks const& ambiguousTracks, + aod::BCsWithTimestamps const& bcs) + { + build(collisions, tracks, ambiguousTracks, bcs); + } + PROCESS_SWITCH(NucleiThreeBodyBuilder, processPbPb, "Process Pb-Pb data", false); + + void processMC(CollisionsMC const& collisions, TracksMC const& tracks, + aod::AmbiguousTracks const& ambiguousTracks, + aod::BCsWithTimestamps const& bcs, + aod::McParticles const& mcParticles) + { + build(collisions, tracks, ambiguousTracks, bcs, &mcParticles); + } + PROCESS_SWITCH(NucleiThreeBodyBuilder, processMC, "Process reconstructed MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx b/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx index 20f740f13c9..b2f5e201476 100644 --- a/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx +++ b/PWGLF/TableProducer/Nuspex/particleCompositionCorrection.cxx @@ -70,7 +70,7 @@ struct ParticleCompositionCorrection { Configurable ptMaxCut{"ptMaxCut", 10.f, "pt max cut"}; Configurable enableQAHistos{"enableQAHistos", true, "enable qa histograms showing the effect of the PCC"}; - Configurable ccdbBasePath{"ccdbBasePath", "/Users/m/makruger/", "ccdb directory contianing the particle fraction networks"}; + Configurable ccdbBasePath{"ccdbBasePath", "/Users/m/mcalmonb/", "ccdb directory contianing the particle fraction networks"}; Configurable modelPathData{"modelPathData", "PCC/data/pp", "Path to the .onnx file containing the particle fractions in data"}; Configurable modelPathMC{"modelPathMC", "PCC/pythia/pp", "Path to the .onnx file containing the particle fractions in MC"}; @@ -97,14 +97,16 @@ void ParticleCompositionCorrection::init(InitContext const&) return; } if (!ccdbBasePath.value.empty()) { - ccdbApi.init("http://ccdb-test.cern.ch:8080"); + // ccdbApi.init("http://ccdb-test.cern.ch:8080"); + ccdbApi.init("http://alice-ccdb.cern.ch"); static const int64_t dummyTimeStamp = 2; - if (!ccdbApi.retrieveBlob(ccdbBasePath.value + modelPathData.value, modelPathData.value, {}, dummyTimeStamp, false, "ParticleFractions_Data.onnx") || !ccdbApi.retrieveBlob(ccdbBasePath.value + modelPathMC.value, modelPathMC.value, {}, dummyTimeStamp, false, "ParticleFractions_MC.onnx")) { + if (!ccdbApi.retrieveBlob(ccdbBasePath.value + modelPathData.value, modelPathData.value, {}, dummyTimeStamp, false, "ParticleFractions_data.onnx") || !ccdbApi.retrieveBlob(ccdbBasePath.value + modelPathMC.value, modelPathMC.value, {}, dummyTimeStamp, false, "ParticleFractions_pythia.onnx")) { LOGP(fatal, "Could not download particle fraction networks!"); } } - particleFractionsData.initModel(modelPathData.value + "/ParticleFractions_Data.onnx", true); - particleFractionsMC.initModel(modelPathMC.value + "/ParticleFractions_MC.onnx", true); + + particleFractionsData.initModel(modelPathData.value + "/ParticleFractions_data.onnx", true); + particleFractionsMC.initModel(modelPathMC.value + "/ParticleFractions_pythia.onnx", true); if (enableQAHistos) { std::vector ptBinEdges = {0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, @@ -113,19 +115,37 @@ void ParticleCompositionCorrection::init(InitContext const&) 6.0, 6.5, 7.0, 8.0, 9.0, 10.0}; const AxisSpec ptAxis{ptBinEdges, "#it{p}_{T} (GeV/#it{c})", "pt"}; - histos.add("frac/data/pion", "", kTProfile, {ptAxis}); + const int maxMult = 100; + const int nBinsMult = maxMult + 1; + const AxisSpec multAxis = {nBinsMult, -0.5, nBinsMult - 0.5, "#it{N}_{ch}", "mult"}; + + histos.add("multDist_INELg0", "", kTH1D, {multAxis}); + histos.add("multDist_fid", "", kTH1D, {multAxis}); + histos.add("multDist_fidVsINELg0", "", kTH2D, {multAxis, multAxis}); + histos.add("frac/data/kaon", "", kTProfile, {ptAxis}); histos.add("frac/data/proton", "", kTProfile, {ptAxis}); histos.add("frac/data/sigma", "", kTProfile, {ptAxis}); + + histos.add("frac/data/kaon_mult", "", kTProfile2D, {multAxis, ptAxis}); + histos.add("frac/data/proton_mult", "", kTProfile2D, {multAxis, ptAxis}); + histos.add("frac/data/sigma_mult", "", kTProfile2D, {multAxis, ptAxis}); + histos.addClone("frac/data/", "frac/mc/"); - histos.add("weight/pion", "", kTProfile, {ptAxis}); histos.add("weight/kaon", "", kTProfile, {ptAxis}); histos.add("weight/proton", "", kTProfile, {ptAxis}); histos.add("weight/sigma", "", kTProfile, {ptAxis}); + histos.add("weight/kaon_mult", "", kTProfile2D, {multAxis, ptAxis}); + histos.add("weight/proton_mult", "", kTProfile2D, {multAxis, ptAxis}); + histos.add("weight/sigma_mult", "", kTProfile2D, {multAxis, ptAxis}); + histos.add("weight/secDec", "", kTProfile, {ptAxis}); histos.add("weight/secMat", "", kTProfile, {ptAxis}); + + histos.add("weight/secDec_mult", "", kTProfile2D, {multAxis, ptAxis}); + histos.add("weight/secMat_mult", "", kTProfile2D, {multAxis, ptAxis}); } } @@ -143,6 +163,11 @@ std::tuple ParticleCompositionCorrection::getWeights(aod::M } auto absPDGCode = std::abs(particle.pdgCode()); // translate abs PDG code to PID variable of neural networks (0: pion, 1: kaon, 2: proton, 3: sigma) + + if (absPDGCode == PDG_t::kPiPlus || absPDGCode == PDG_t::kPi0) { + return noWeights; + } + static const std::map mapPID = { {PDG_t::kPiPlus, 0.f}, {PDG_t::kPi0, 0.f}, @@ -175,25 +200,29 @@ std::tuple ParticleCompositionCorrection::getWeights(aod::M storedWeights[particle.index()] = weights; } if (enableQAHistos && particle.isPhysicalPrimary() && std::abs(particle.eta()) < 0.8) { // o2-linter: disable=magic-number (usual range of charged-partilce measurements) - if (iterMapPID->first == PDG_t::kPiPlus) { - histos.fill(HIST("frac/data/pion"), pt, fracData); - histos.fill(HIST("frac/mc/pion"), pt, fracMC); - histos.fill(HIST("weight/pion"), pt, weight); - } if (iterMapPID->first == PDG_t::kKPlus) { histos.fill(HIST("frac/data/kaon"), pt, fracData); histos.fill(HIST("frac/mc/kaon"), pt, fracMC); + histos.fill(HIST("frac/data/kaon_mult"), dNdEta, pt, fracData); + histos.fill(HIST("frac/mc/kaon_mult"), dNdEta, pt, fracMC); histos.fill(HIST("weight/kaon"), pt, weight); + histos.fill(HIST("weight/kaon_mult"), dNdEta, pt, weight); } if (iterMapPID->first == PDG_t::kProton) { histos.fill(HIST("frac/data/proton"), pt, fracData); histos.fill(HIST("frac/mc/proton"), pt, fracMC); + histos.fill(HIST("frac/data/proton_mult"), dNdEta, pt, fracData); + histos.fill(HIST("frac/mc/proton_mult"), dNdEta, pt, fracMC); histos.fill(HIST("weight/proton"), pt, weight); + histos.fill(HIST("weight/proton_mult"), dNdEta, pt, weight); } if (iterMapPID->first == PDG_t::kSigmaPlus || iterMapPID->first == PDG_t::kSigmaMinus) { histos.fill(HIST("frac/data/sigma"), pt, fracData); histos.fill(HIST("frac/mc/sigma"), pt, fracMC); + histos.fill(HIST("frac/data/sigma_mult"), dNdEta, pt, fracData); + histos.fill(HIST("frac/mc/sigma_mult"), dNdEta, pt, fracMC); histos.fill(HIST("weight/sigma"), pt, weight); + histos.fill(HIST("weight/sigma_mult"), dNdEta, pt, weight); } } return weights; @@ -218,8 +247,10 @@ std::tuple ParticleCompositionCorrection::getWeights(aod::M if (pdgParticle && pdgParticle->Charge() != 0.) { if (particle.getProcess() == TMCProcess::kPDecay) { histos.fill(HIST("weight/secDec"), particle.pt(), weight); + histos.fill(HIST("weight/secDec_mult"), dNdEta, particle.pt(), weight); } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { histos.fill(HIST("weight/secMat"), particle.pt(), weight); + histos.fill(HIST("weight/secMat_mult"), dNdEta, particle.pt(), weight); } } } @@ -232,6 +263,8 @@ void ParticleCompositionCorrection::process(aod::McCollisions::iterator const&, { // determine dNdEta of the collision float dNdEta = 0.f; + float dNdEtafid = 0.f; + for (const auto& particle : particles) { if (!particle.isPhysicalPrimary()) { continue; @@ -240,10 +273,19 @@ void ParticleCompositionCorrection::process(aod::McCollisions::iterator const&, if (!pdgParticle || pdgParticle->Charge() == 0.) { continue; } - if (std::abs(particle.eta()) >= 0.5) { // o2-linter: disable=magic-number (particle density at mid-rapidity) + if (std::abs(particle.eta()) >= etaCut) { continue; } - ++dNdEta; + if (std::abs(particle.eta()) < 0.5) { // o2-linter: disable=magic-number (particle density at mid-rapidity) + ++dNdEta; + } + ++dNdEtafid; + } + + if (dNdEtafid > 0.f) { + histos.fill(HIST("multDist_INELg0"), dNdEta); + histos.fill(HIST("multDist_fid"), dNdEtafid); + histos.fill(HIST("multDist_fidVsINELg0"), dNdEta, dNdEtafid); } std::map> storedWeights; diff --git a/PWGLF/TableProducer/Nuspex/photonDeuteron.cxx b/PWGLF/TableProducer/Nuspex/photonDeuteron.cxx new file mode 100644 index 00000000000..a0d5d894398 --- /dev/null +++ b/PWGLF/TableProducer/Nuspex/photonDeuteron.cxx @@ -0,0 +1,331 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file photonDeuteron.cxx +/// \brief Task to analyze photon-deuteron correlations +/// \author Arvind Khuntia and Francesco Noferini + +#include "PWGLF/DataModel/LFPhotonDeuteronTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct PhotonDeuteron { + // Histogram registry + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Produce output table + Produces photonDeuteronTable; + + // Configurable parameters + Configurable cfgPhotonMassWindow{"cfgPhotonMassWindow", 0.05, "Photon mass window (GeV/c2)"}; + Configurable cfgPhotonMinPt{"cfgPhotonMinPt", 0.05, "Minimum photon pT (GeV/c)"}; + Configurable cfgPhotonMaxPt{"cfgPhotonMaxPt", 10.0, "Maximum photon pT (GeV/c)"}; + Configurable cfgPhotonMaxEta{"cfgPhotonMaxEta", 0.8, "Maximum photon eta"}; + Configurable cfgPhotonDaughterNSigmaElTPCMax{"cfgPhotonDaughterNSigmaElTPCMax", 5.0, "Maximum NSigma TPC electron for photon daughters (999=off)"}; + + Configurable cfgDeuteronMinPt{"cfgDeuteronMinPt", 0.2, "Minimum deuteron pT (GeV/c)"}; + Configurable cfgDeuteronMaxPt{"cfgDeuteronMaxPt", 5.0, "Maximum deuteron pT (GeV/c)"}; + Configurable cfgDeuteronMaxEta{"cfgDeuteronMaxEta", 0.8, "Maximum deuteron eta"}; + Configurable cfgDeuteronNSigmaTPCMax{"cfgDeuteronNSigmaTPCMax", 3.0, "Maximum NSigma TPC for deuteron"}; + Configurable cfgDeuteronNSigmaTOFMax{"cfgDeuteronNSigmaTOFMax", 3.0, "Maximum NSigma TOF for deuteron"}; + + Configurable cfgV0CosPA{"cfgV0CosPA", 0.995, "Minimum V0 cosine of pointing angle"}; + Configurable cfgV0Radius{"cfgV0Radius", 5.0, "Minimum V0 radius (cm)"}; + Configurable cfgUsePhotonDaughterPIDTPCOnly{"cfgUsePhotonDaughterPIDTPCOnly", true, "Use TPC-only PID for photon daughters"}; + + // Initialize histograms + void init(InitContext const&) + { + const AxisSpec axisPhotonMass{200, 0.0, 0.2, "M_{ee} (GeV/c^{2})"}; + const AxisSpec axisPt{100, 0.0, 10.0, "p_{T} (GeV/c)"}; + const AxisSpec axisEta{100, -1.0, 1.0, "#eta"}; + const AxisSpec axisPhi{64, 0.0, 2.0 * o2::constants::math::PI, "#phi (rad)"}; + const AxisSpec axisDeltaPhi{64, -0.5 * o2::constants::math::PI, 1.5 * o2::constants::math::PI, "#Delta#phi (rad)"}; + const AxisSpec axisDeltaEta{100, -2.0, 2.0, "#Delta#eta"}; + const AxisSpec axisNSigma{200, -10.0, 10.0, "n#sigma"}; + const AxisSpec axisV0CosPA{100, 0.9, 1.0, "cos(PA)"}; + const AxisSpec axisV0Radius{100, 0.0, 50.0, "R (cm)"}; + const AxisSpec axisRelativeMomentum{200, 0.0, 1.0, "k^{*}_{pn} (GeV/c)"}; + + // Event histograms + histos.add("hEventCounter", "Event counter", kTH1F, {{5, 0.5, 5.5}}); + + // V0 (photon) histograms + histos.add("hV0Mass", "V0 invariant mass", kTH1F, {axisPhotonMass}); + histos.add("hV0Pt", "V0 p_{T}", kTH1F, {axisPt}); + histos.add("hV0Eta", "V0 #eta", kTH1F, {axisEta}); + histos.add("hV0Phi", "V0 #phi", kTH1F, {axisPhi}); + histos.add("hV0CosPA", "V0 cos(PA)", kTH1F, {axisV0CosPA}); + histos.add("hV0Radius", "V0 radius", kTH1F, {axisV0Radius}); + histos.add("hPhotonPtEta", "Photon p_{T} vs #eta", kTH2F, {axisPt, axisEta}); + + // Deuteron histograms + histos.add("hDeuteronPt", "Deuteron p_{T}", kTH1F, {axisPt}); + histos.add("hDeuteronEta", "Deuteron #eta", kTH1F, {axisEta}); + histos.add("hDeuteronPhi", "Deuteron #phi", kTH1F, {axisPhi}); + histos.add("hDeuteronNSigmaTPC", "Deuteron n#sigma TPC", kTH1F, {axisNSigma}); + histos.add("hDeuteronNSigmaTOF", "Deuteron n#sigma TOF", kTH1F, {axisNSigma}); + histos.add("hDeuteronNSigmaTPCvsPt", "Deuteron n#sigma TPC vs p_{T}", kTH2F, {axisPt, axisNSigma}); + histos.add("hDeuteronNSigmaTOFvsPt", "Deuteron n#sigma TOF vs p_{T}", kTH2F, {axisPt, axisNSigma}); + + // Correlation histograms + histos.add("hPhotonDeuteronDeltaPhi", "Photon-Deuteron #Delta#phi", kTH1F, {axisDeltaPhi}); + histos.add("hPhotonDeuteronDeltaEta", "Photon-Deuteron #Delta#eta", kTH1F, {axisDeltaEta}); + histos.add("hPhotonDeuteronCorrelation", "Photon-Deuteron correlation", kTH2F, {axisDeltaPhi, axisDeltaEta}); + histos.add("hPhotonDeuteronInvMass", "Photon-Deuteron invariant mass", kTH1F, {{200, 0.0, 5.0, "M_{#gamma d} (GeV/c^{2})"}}); + histos.add("hPhotonDeuteronPtCorr", "Photon p_{T} vs Deuteron p_{T}", kTH2F, {axisPt, axisPt}); + histos.add("hRelativeMomentum", "Relative momentum k^{*}_{pn}", kTH1F, {axisRelativeMomentum}); + histos.add("hRelativeMomentumVsInvMass", "k^{*}_{pn} vs invariant mass", kTH2F, {{200, 0.0, 5.0, "M_{#gamma d} (GeV/c^{2})"}, {200, 0.0, 1.0, "k^{*}_{pn} (GeV/c)"}}); + } + + // V0 photon selection + template + bool selectPhoton(TV0 const& v0, TTracks const& /*tracks*/) + { + // Basic V0 quality cuts + if (v0.v0cosPA() < cfgV0CosPA) { + return false; + } + if (v0.v0radius() < cfgV0Radius) { + return false; + } + + // Photon mass window + if (std::abs(v0.mGamma()) > cfgPhotonMassWindow) { + return false; + } + + // Kinematic cuts + if (v0.pt() < cfgPhotonMinPt || v0.pt() > cfgPhotonMaxPt) { + return false; + } + if (std::abs(v0.eta()) > cfgPhotonMaxEta) { + return false; + } + + // Optional electron PID cuts for daughter tracks (TPC-only) + if (cfgUsePhotonDaughterPIDTPCOnly) { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + + if (std::abs(posTrack.tpcNSigmaEl()) > cfgPhotonDaughterNSigmaElTPCMax) { + return false; + } + if (std::abs(negTrack.tpcNSigmaEl()) > cfgPhotonDaughterNSigmaElTPCMax) { + return false; + } + } + + return true; + } + + // Deuteron PID selection + template + bool selectDeuteron(TTrack const& track) + { + // Kinematic cuts + if (track.pt() < cfgDeuteronMinPt || track.pt() > cfgDeuteronMaxPt) { + return false; + } + if (std::abs(track.eta()) > cfgDeuteronMaxEta) { + return false; + } + + // TPC PID + if (std::abs(track.tpcNSigmaDe()) > cfgDeuteronNSigmaTPCMax) { + return false; + } + + // TOF PID (if available) + if (track.hasTOF() && std::abs(track.tofNSigmaDe()) > cfgDeuteronNSigmaTOFMax) { + return false; + } + + return true; + } + + // Calculate relative momentum k*_pn from the photon-deuteron invariant mass [Eq. 4.6] + float calculateRelativeMomentum(float invMass) + { + if (invMass <= 0.0) { + return -1.0; + } + + float M = invMass; + float M2 = M * M; + float M4 = M2 * M2; + float mn2 = o2::constants::physics::MassNeutron * o2::constants::physics::MassNeutron; + float mp2 = o2::constants::physics::MassProton * o2::constants::physics::MassProton; + float deltaMass2 = mn2 - mp2; + float sumMass2 = mn2 + mp2; + + float term = M4 + deltaMass2 * deltaMass2 - 2.0 * sumMass2 * M2; + + if (term < 0.0) { + return -1.0; + } + + float kpn = 0.5 / M * std::sqrt(term); + return kpn; + } + + // Process function for V0s and tracks + void process(aod::Collision const& /*collision*/, + soa::Join const& V0s, + soa::Join const& tracks) + { + histos.fill(HIST("hEventCounter"), 1); + + // Loop over V0s to find photons + std::vector photonIndices; + for (auto const& v0 : V0s) { + // Fill V0 QA histograms + histos.fill(HIST("hV0Mass"), v0.mGamma()); + histos.fill(HIST("hV0CosPA"), v0.v0cosPA()); + histos.fill(HIST("hV0Radius"), v0.v0radius()); + + // Select photons + if (!selectPhoton(v0, tracks)) { + continue; + } + + // Fill photon histograms + histos.fill(HIST("hV0Pt"), v0.pt()); + histos.fill(HIST("hV0Eta"), v0.eta()); + histos.fill(HIST("hV0Phi"), v0.phi()); + histos.fill(HIST("hPhotonPtEta"), v0.pt(), v0.eta()); + + if (v0.isPhotonTPConly()) { + photonIndices.push_back(v0.index()); + LOGF(debug, "[main] global index photon: %d v0 id: %d pt %.3f", v0.globalIndex(), v0.index(), v0.pt()); + } + } + + // Loop over tracks to find deuterons + std::vector deuteronIndices; + for (auto const& track : tracks) { + // Basic track quality + if (!track.isGlobalTrack()) { + continue; + } + + // Select deuterons + if (!selectDeuteron(track)) { + continue; + } + + // Fill deuteron histograms + histos.fill(HIST("hDeuteronPt"), track.pt()); + histos.fill(HIST("hDeuteronEta"), track.eta()); + histos.fill(HIST("hDeuteronPhi"), track.phi()); + histos.fill(HIST("hDeuteronNSigmaTPC"), track.tpcNSigmaDe()); + histos.fill(HIST("hDeuteronNSigmaTPCvsPt"), track.pt(), track.tpcNSigmaDe()); + + if (track.hasTOF()) { + histos.fill(HIST("hDeuteronNSigmaTOF"), track.tofNSigmaDe()); + histos.fill(HIST("hDeuteronNSigmaTOFvsPt"), track.pt(), track.tofNSigmaDe()); + } + deuteronIndices.push_back(track.index()); + } + // Calculate correlations between photons and deuterons + for (auto const& photonIdx : photonIndices) { + const auto& photon = V0s.iteratorAt(photonIdx); + + for (auto const& deuteronIdx : deuteronIndices) { + const auto& deuteron = tracks.iteratorAt(deuteronIdx); + + // Calculate angular correlations + float deltaPhi = RecoDecay::constrainAngle(photon.phi() - deuteron.phi(), -0.5 * o2::constants::math::PI); + float deltaEta = photon.eta() - deuteron.eta(); + + // Fill correlation histograms + histos.fill(HIST("hPhotonDeuteronDeltaPhi"), deltaPhi); + histos.fill(HIST("hPhotonDeuteronDeltaEta"), deltaEta); + histos.fill(HIST("hPhotonDeuteronCorrelation"), deltaPhi, deltaEta); + histos.fill(HIST("hPhotonDeuteronPtCorr"), photon.pt(), deuteron.pt()); + + // Calculate invariant mass using RecoDecay + std::array pPhoton{photon.px(), photon.py(), photon.pz()}; + std::array pDeuteron{deuteron.px(), deuteron.py(), deuteron.pz()}; + float invMass = RecoDecay::m(std::array{pPhoton, pDeuteron}, std::array{0.0f, static_cast(o2::constants::physics::MassDeuteron)}); + histos.fill(HIST("hPhotonDeuteronInvMass"), invMass); + + // Calculate relative momentum using Equation 4.6 + float kpn = calculateRelativeMomentum(invMass); + if (kpn >= 0.0) { + histos.fill(HIST("hRelativeMomentum"), kpn); + histos.fill(HIST("hRelativeMomentumVsInvMass"), invMass, kpn); + } + + // Fill the output table + auto posTrack = photon.posTrack_as>(); + auto negTrack = photon.negTrack_as>(); + + photonDeuteronTable( + photon.pt(), + photon.eta(), + photon.phi(), + photon.mGamma(), + posTrack.pt(), + posTrack.eta(), + posTrack.phi(), + posTrack.tpcNSigmaEl(), + negTrack.pt(), + negTrack.eta(), + negTrack.phi(), + negTrack.tpcNSigmaEl(), + deuteron.pt(), + deuteron.eta(), + deuteron.phi(), + deuteron.tpcNSigmaDe(), + deuteron.hasTOF() ? deuteron.tofNSigmaDe() : -999.0f, + deltaPhi, + deltaEta, + invMass, + kpn); + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx new file mode 100644 index 00000000000..77de9f213f7 --- /dev/null +++ b/PWGLF/TableProducer/Nuspex/piHypertritonFemto.cxx @@ -0,0 +1,1222 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file piHypertritonFemto.cxx +/// \brief LF pi-hypertriton femto pair table producer Shifted from PWGCF +/// \author zhengqing and meiyi +/// \date 2026-09-17 + +#include "PWGLF/DataModel/LFHypernucleiTables.h" +#include "PWGLF/DataModel/LFPiHypertritonFemtoTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using HyperCandidates = aod::DataHypCandsWColl; +using HyperCandidatesMC = aod::MCHypCandsWColl; +using HadHyperCollisionsFull = soa::Join; +using HadHyperCollisionsFullMC = soa::Join; +using TrackCandidates = soa::Join; +using TrackCandidatesMC = soa::Join; + +namespace +{ +constexpr std::array InvalidMomentum{-1.f, -1.f, -1.f}; +constexpr int HyperTritonPDG = o2::constants::physics::Pdg::kHyperTriton; +// FemtoDream average-phi-star reference radii, in cm. +constexpr std::array CPRTPCRadii{85.f, 105.f, 125.f, 145.f, 165.f, 185.f, 205.f, 225.f, 245.f}; +using PairLorentzVector = ROOT::Math::LorentzVector>; +} // namespace + +struct PiHypertritonFemto { + // First aggregate constructor argument, supplied by defineDataProcessing. + std::shared_ptr> mEndOfStreamCallback; + Produces mOutputDataTable; + Produces mOutputMCTable; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"eventMixing"}; + Configurable settingCutVertex{"settingCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable settingNoMixedEvents{"settingNoMixedEvents", 5, "Number of mixed events per event"}; + } eventMixing; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"eventSelection"}; + Configurable disableITSROFCut{"disableITSROFCut", false, "Disable the ITS readout-frame border cut for data, as in hyperRecoTask"}; + Configurable cfgEvSelkNoSameBunchPileup{"cfgEvSelkNoSameBunchPileup", false, "Reject collisions sharing the same found-by-T0 bunch crossing"}; + Configurable cfgEvSelkIsGoodZvtxFT0vsPV{"cfgEvSelkIsGoodZvtxFT0vsPV", false, "Require consistent primary vertex z positions from tracking and FT0 timing"}; + } eventSelection; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"pionTrack"}; + Configurable settingCutEta{"settingCutEta", 0.8f, "Maximum pion track |eta|"}; + Configurable settingPtMin{"settingPtMin", 0.14f, "Minimum pion pT"}; + Configurable settingPtMax{"settingPtMax", 2.5f, "Maximum pion pT"}; + Configurable settingITSInnerBarrelMin{"settingITSInnerBarrelMin", 3, "Minimum pion ITS inner-barrel clusters"}; + Configurable settingITSNClsMin{"settingITSNClsMin", 7, "Minimum pion ITS clusters"}; + Configurable settingTPCNClsFoundMin{"settingTPCNClsFoundMin", 80, "Minimum pion found TPC clusters"}; + Configurable settingTPCCrossedRowsMin{"settingTPCCrossedRowsMin", 90, "Minimum pion crossed TPC rows"}; + Configurable settingDCAxyOffset{"settingDCAxyOffset", 0.004f, "Pion DCAxy offset"}; + Configurable settingDCAxyPtCoeff{"settingDCAxyPtCoeff", 0.013f, "Pion DCAxy 1/pT coefficient"}; + Configurable settingDCAzOffset{"settingDCAzOffset", 0.004f, "Pion DCAz offset"}; + Configurable settingDCAzPtCoeff{"settingDCAzPtCoeff", 0.013f, "Pion DCAz 1/pT coefficient"}; + } pionTrack; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"pionPid"}; + Configurable settingMomCombMin{"settingMomCombMin", 0.5f, "Minimum momentum to use combined TPC+TOF pion PID"}; + Configurable settingTPCNsigMax{"settingTPCNsigMax", 3.0f, "Maximum pion TPC n-sigma below the TOF threshold"}; + Configurable settingCombNsigMax{"settingCombNsigMax", 3.0f, "Maximum combined pion TPC+TOF n-sigma"}; + Configurable settingReqSingleNsig{"settingReqSingleNsig", false, "Also require individual TPC and TOF n-sigma cuts in combined PID"}; + } pionPid; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"CPR"}; + Configurable settingClosePairDeltaPhiMax{"settingClosePairDeltaPhiMax", 0.01f, "Average delta-phi-star ellipse semiaxis for the offline close-pair flag"}; + Configurable settingClosePairDeltaEtaMax{"settingClosePairDeltaEtaMax", 0.01f, "Delta-eta ellipse semiaxis for the offline close-pair flag"}; + Configurable settingClosePairDistanceMax{"settingClosePairDistanceMax", 8.f, "Average 3D TPC separation below which a same-sign daughter pair is tagged (cm)"}; + } CPR; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"mc"}; + Configurable settingRequireSel8{"settingRequireSel8", false, "Additionally require sel8 for reconstructed MC; TVX and timeframe border cuts always apply"}; + Configurable settingRequireRecoMCCollisionMatch{"settingRequireRecoMCCollisionMatch", true, "Require reconstructed collision MC labels"}; + } mc; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"hypertriton"}; + Configurable settingHypMassMin{"settingHypMassMin", 2.94f, "Minimum hypertriton invariant mass"}; + Configurable settingHypMassMax{"settingHypMassMax", 3.10f, "Maximum hypertriton invariant mass"}; + } hypertriton; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"output"}; + Configurable settingFillTable{"settingFillTable", false, "Enable output table filling"}; + } output; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"hadHyper"}; + Configurable enableMixing{"enableMixing", true, "Build mixed-event pion-hypertriton pairs"}; + Configurable maxOutputKstar{"maxOutputKstar", -1.f, "Maximum pair k* (GeV/c); negative saves all selected pairs"}; + } hadHyper; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"zorro"}; + Configurable settingSkimmedProcessing{"settingSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable settingTriggerMask{"settingTriggerMask", "fPiHypertritonFemto", "Zorro trigger mask"}; + } zorro; + + struct : o2::framework::ConfigurableGroup { + // cppcheck-suppress unusedStructMember + std::string prefix{"ccdb"}; + Configurable settingCcdburl{"settingCcdburl", "http://alice-ccdb.cern.ch", "URL of the CCDB repository used by Zorro and the magnetic field"}; + Configurable settingGrpmagPath{"settingGrpmagPath", "GLO/Config/GRPMagField", "CCDB path of the Run 3 magnetic field"}; + } ccdb; + + Preslice mPerCol = aod::track::collisionId; + Preslice mPerColMC = aod::track::collisionId; + PresliceUnsorted hypPerCol = o2::aod::hyperrec::collisionId; + PresliceUnsorted hypPerColMC = o2::aod::hyperrec::collisionId; + + ConfigurableAxis axisVertex{"axisVertex", {30, -10, 10}, "Binning for mixed-event vertex z"}; + ConfigurableAxis axisCentrality{"axisCentrality", {40, 0, 100}, "Binning for mixed-event centrality"}; + ConfigurableAxis axisPairMultiplicity{"axisPairMultiplicity", {1501, -0.5, 1500.5}, "Binning for accepted pairs per hypertriton candidate (SE and ME)"}; + using BinningType = ColumnBinningPolicy; + + using HadHyperTrackInfo = std::tuple; + using HadHyperEventInfo = std::tuple; + using HadHyperCandidateInfo = std::tuple; + using HadHyperDataInfo = decltype(std::tuple_cat(std::declval>(), + std::declval(), + std::declval(), + std::declval())); + using HadHyperMCInfo = std::tuple; + + struct HadHyperParticleTruth { + int64_t particleId{-1}; + int64_t collisionId{-1}; + int32_t pdgCode{0}; + float pt{-1.f}; + float eta{-999.f}; + float phi{-999.f}; + bool isPhysicalPrimary{false}; + int16_t statusCode{0}; + int16_t process{0}; + std::array momentum{InvalidMomentum}; + }; + + struct ClosePairTrack { + float eta{0.f}; + int8_t sign{0}; + std::array phiStar{}; + std::array valid{}; + std::array, CPRTPCRadii.size()> positions{}; // Relative to the source collision PV. + std::array positionValid{}; + }; + + struct ClosePairResult { + float deltaEta{0.f}; + float deltaPhiStar{0.f}; + int validRadii{0}; + float averageDistance{0.f}; + int validPositionRadii{0}; + bool angularRejected{false}; + bool distanceRejected{false}; + bool angularUnavailable{false}; + bool distanceUnavailable{false}; + }; + + struct ClosePairQA { + std::shared_ptr before; + std::shared_ptr after; + std::shared_ptr validRadii; + std::shared_ptr distanceBefore; + std::shared_ptr distanceAfter; + std::shared_ptr validPositionRadii; + std::shared_ptr decisions; + }; + + struct HadHyperCandidate { + HadHyperCandidateInfo info{}; + HadHyperParticleTruth hyperTruth{}; + HadHyperParticleTruth heTruth{}; + HadHyperParticleTruth decayPionTruth{}; + int64_t heTrackId{-1}; + int64_t pionTrackId{-1}; + int16_t statusCode{0}; + bool isReco{true}; + bool isSignal{false}; + bool isRecoMCCollision{false}; + bool isSurvEvSel{false}; + bool isTwoBodyDecay{false}; + float genPt{-1.f}; + float genEta{-1.f}; + float genPhi{-1.f}; + float genPtHe3{-1.f}; + std::array genDecVtx{InvalidMomentum}; + std::array momentum{}; + std::array genMomentum{InvalidMomentum}; + float mass{0.f}; + bool isMatter{false}; + std::array daughterClosePairTracks{}; // He3, decay pion. + uint64_t mixedPairCount{0}; // Accumulated over the complete residence in the mixing pool. + + [[nodiscard]] float pt() const { return std::hypot(momentum[0], momentum[1]); } + [[nodiscard]] float eta() const + { + const float transverseMomentum = pt(); + return transverseMomentum > 0.f ? std::asinh(momentum[2] / transverseMomentum) : 999.f; + } + [[nodiscard]] float phi() const { return std::atan2(momentum[1], momentum[0]); } + [[nodiscard]] int8_t sign() const { return isMatter ? 1 : -1; } + }; + + struct HadHyperHadron { + HadHyperTrackInfo info{}; + HadHyperParticleTruth truth{}; + std::vector motherIds; + int64_t sourceId{-1}; + std::array momentum{}; + int8_t signValue{0}; + ClosePairTrack closePairTrack{}; + + [[nodiscard]] float eta() const { return std::get<1>(info); } + [[nodiscard]] float phi() const { return std::get<2>(info); } + [[nodiscard]] int8_t sign() const { return signValue; } + }; + + struct HadHyperEvent { + HadHyperEventInfo info{}; + uint64_t sourceFrameId{0}; // Task-local scope for track and MC indices stored in the mixing pool. + int64_t mcCollisionId{-1}; + bool hasMCCollision{false}; + float centrality{0.f}; + std::vector candidates; + std::vector hadrons; + }; + + std::unordered_map> mMixingPools; + std::unordered_map> mMCMixingPools; + int mMixingRunNumber{-1}; + int mMCMixingRunNumber{-1}; + uint64_t mSourceFrameId{0}; + int mRunNumber{-1}; + float mMagneticFieldTesla{0.f}; + std::array, 2> mClosePairQA{}; // SE/ME, He3/decay pion. + Service mCcdb{}; + Zorro mZorro; + OutputObj mZorroSummary{"zorroSummary"}; + + HistogramRegistry mQaRegistry{ + "QA", + {{"hEvents", "Event selection cut-flow;Selection step;Events", {HistType::kTH1F, {{11, -0.5, 10.5}}}}, + {"hVtxZ", "Vertex distribution in Z;Z (cm);Entries", {HistType::kTH1F, {{400, -20.0, 20.0}}}}, + {"hNcontributor", "Number of primary vertex contributors;contributors;Entries", {HistType::kTH1F, {{2000, 0.0f, 2000.0f}}}}, + {"hPionTPCNSigmaPreselection", "Pion TPC n#sigma before PID;signed #it{p}_{TPC};n#sigma_{TPC}^{#pi}", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {400, -10.0f, 10.0f}}}}, + {"hPionTOFNSigmaPreselection", "Pion TOF n#sigma before PID;signed #it{p}_{T};n#sigma_{TOF}^{#pi}", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {400, -10.0f, 10.0f}}}}, + {"hPionCombNSigmaPreselection", "Pion combined n#sigma before PID;signed #it{p}_{T};n#sigma_{comb}^{#pi}", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, + {"hPionTPCNSigma", "Selected pion TPC n#sigma;signed #it{p}_{T};n#sigma_{TPC}^{#pi}", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, + {"hPionTOFNSigma", "Selected pion TOF n#sigma;signed #it{p}_{T};n#sigma_{TOF}^{#pi}", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, + {"hPionCombNSigma", "Selected pion combined n#sigma;signed #it{p}_{T};n#sigma_{comb}^{#pi}", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {100, 0.0f, 5.0f}}}}, + {"hPionTPC", "Selected pion TPC d#it{E}/d#it{x};signed #it{p}_{TPC};TPC signal", {HistType::kTH2F, {{200, -5.0f, 5.0f}, {100, 0.0f, 2000.0f}}}}, + {"hHypHe3TPCNSigma", "Selected hypertriton candidates;#it{p}_{T}^{^{3}He};n#sigma_{TPC}^{^{3}He}", {HistType::kTH2F, {{280, -7.0f, 7.0f}, {200, -5.0f, 5.0f}}}}, + {"hHypHe3TPCMomPreselection", "Hypertriton candidate He3 TPC momentum before mass cut;#it{p}_{TPC};Entries", {HistType::kTH1F, {{120, -3.0f, 3.0f}}}}, + {"hHypHe3TPCMom", "Hypertriton candidate He3 TPC momentum after mass cut;#it{p}_{TPC};Entries", {HistType::kTH1F, {{120, -3.0f, 3.0f}}}}}, + OutputObjHandlingPolicy::AnalysisObject, + false, + true}; + + HistogramRegistry hadHyperRegistry{ + "hadHyperRegistry", + {{"hSE", "Raw same-event pairs;k*;Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"hME", "Raw mixed-event pairs;k*;Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"hMass", "Unique selected candidates;mass He3-pi;Entries", {HistType::kTH1D, {{160, 2.94, 3.10}}}}, + {"hSelfPairs", "Rejected daughter reuse;reason (0=He3,1=pion,2=truth);k*", {HistType::kTH2F, {{3, -0.5, 2.5}, {300, 0., 3.}}}}, + {"hSameEventSelfPairs", "Same-event rejected daughter reuse;reason (0=He3,1=pion,2=truth);k*", {HistType::kTH2F, {{3, -0.5, 2.5}, {300, 0., 3.}}}}, + {"hMixEventDeltaPosZVsCent", "Mixed-event #Delta z_{vtx} vs hypertriton centrality;hypertriton CentFT0C;#Delta z_{vtx}", {HistType::kTH2F, {{100, 0., 100.}, {120, -30., 30.}}}}, + {"hMixEventDeltaCentFT0CVsCent", "Mixed-event #Delta CentFT0C vs hypertriton centrality;hypertriton CentFT0C;#Delta CentFT0C", {HistType::kTH2F, {{100, 0., 100.}, {200, -100., 100.}}}}, + {"hMixingDepth", "Available partner events;events;anchor events", {HistType::kTH1F, {{101, -0.5, 100.5}}}}, + {"hPoolFlow", "Mixing pool selection;0=selected,1=in range;events", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, + {"hDaughterHeTPC", "He3 daughter QA per candidate;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, + {"hDaughterPiTPC", "Pion daughter QA per candidate;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, + {"hPairPionTPC", "Pair pion QA per selected track;TPC rigidity;TPC signal", {HistType::kTH2F, {{100, 0., 10.}, {300, 0., 1500.}}}}, + {"MC/SE/hKstarRecVsGenHyperReco", "Pion-hypertriton k* response for reconstructed true hypertritons;generated k* (GeV/#it{c});reconstructed k* (GeV/#it{c})", {HistType::kTH2F, {{300, 0., 3.}, {300, 0., 3.}}}}, + {"MC/SE/hKstarResolutionHyperReco", "Pion-hypertriton k* resolution for reconstructed true hypertritons;reconstructed k* (GeV/#it{c});k*_{reco}-k*_{gen} (GeV/#it{c})", {HistType::kTH2F, {{300, 0., 3.}, {200, -0.2, 0.2}}}}, + {"MC/SE/hPrimaryPionVsKstarDen", "Truth-matched selected pion pair-weighted denominator;k* (GeV/#it{c});Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"MC/SE/hPrimaryPionVsKstarNum", "Physical-primary selected pion pair-weighted numerator;k* (GeV/#it{c});Entries", {HistType::kTH1D, {{300, 0., 3.}}}}, + {"MC/SE/hPrimaryPionVsCentDen", "Truth-matched selected pion pair-weighted denominator;hypertriton CentFT0C;Entries", {HistType::kTH1D, {{100, 0., 100.}}}}, + {"MC/SE/hPrimaryPionVsCentNum", "Physical-primary selected pion pair-weighted numerator;hypertriton CentFT0C;Entries", {HistType::kTH1D, {{100, 0., 100.}}}}}}; + + void init(o2::framework::InitContext&); + template + void initCCDB(const aod::BCsWithTimestamps::iterator& bc); + template + bool selectCollision(const Tcollision& collision, const aod::BCsWithTimestamps& bcs); + template + bool selectPionTrack(const Ttrack& candidate) const; + template + bool selectionPIDPion(const Ttrack& candidate); + template + bool selectHyperCandidate(const Tcandidate& candidate); + template + void processPairs(const Tcollisions& collisions, const Ttracks& tracks, const Tcandidates& candidates, + const aod::BCsWithTimestamps& bcs, + std::unordered_map>& mixingPools, + int& mixingRunNumber); + + template + float computeHyperCandidateMass(const Tcandidate& candidate) const + { + const std::array heMomentum{candidate.ptHe3() * std::cos(candidate.phiHe3()), candidate.ptHe3() * std::sin(candidate.phiHe3()), candidate.ptHe3() * std::sinh(candidate.etaHe3())}; + const std::array pionMomentum{candidate.ptPi() * std::cos(candidate.phiPi()), candidate.ptPi() * std::sin(candidate.phiPi()), candidate.ptPi() * std::sinh(candidate.etaPi())}; + return RecoDecay::m(std::array{heMomentum, pionMomentum}, + std::array{static_cast(o2::constants::physics::MassHelium3), + static_cast(o2::constants::physics::MassPiPlus)}); + } + + float wrapDeltaPhi(float dphi) const { return std::atan2(std::sin(dphi), std::cos(dphi)); } + + template + ClosePairTrack makeClosePairTrack(const Ttrack& track, const std::array& primaryVertex) const + { + ClosePairTrack result; + result.eta = track.eta(); + result.sign = track.sign(); + if (!std::isfinite(result.eta) || !std::isfinite(track.phi()) || !std::isfinite(track.signed1Pt()) || track.signed1Pt() == 0.f) { + return result; + } + // FemtoDream helix approximation, using the original daughter track, not the mother. + // AO2D signed1Pt is signed inverse rigidity: He3's |Z|=2 is already included. + // Do not multiply it by two again. Cache each source event's field-dependent values. + constexpr float CurvatureFactor = 0.3f * 0.01f / 2.f; // B in T, radius in cm. + for (std::size_t i = 0; i < CPRTPCRadii.size(); ++i) { + const float argument = CurvatureFactor * mMagneticFieldTesla * CPRTPCRadii[i] * track.signed1Pt(); + if (std::isfinite(argument) && std::abs(argument) < 1.f) { + result.phiStar[i] = track.phi() - std::asin(argument); + result.valid[i] = true; + } + } + // Uniform-field extrapolation from the measured track state, retaining daughter displacement. + // This is not material-aware transport. getTrackPar keeps q/pT (including He3 rigidity). + const auto trackPar = getTrackPar(track); + constexpr float TeslaToKilogauss = 10.f; + constexpr float TPCHalfLength = 250.f; // cm + const float fieldKilogauss = TeslaToKilogauss * mMagneticFieldTesla; + for (std::size_t i = 0; i < CPRTPCRadii.size(); ++i) { + float localX = 0.f; + if (!trackPar.getXatLabR(CPRTPCRadii[i], localX, fieldKilogauss, o2::track::DirOutward)) { + continue; + } + bool propagated = false; + const auto point = trackPar.getXYZGloAt(localX, fieldKilogauss, propagated); + if (!propagated || !std::isfinite(point.X()) || !std::isfinite(point.Y()) || !std::isfinite(point.Z()) || std::abs(point.Z()) > TPCHalfLength) { + continue; + } + // Apply the same source-PV convention to SE and ME; in SE the common PV cancels. + result.positions[i] = {point.X() - primaryVertex[0], point.Y() - primaryVertex[1], point.Z() - primaryVertex[2]}; + result.positionValid[i] = true; + } + return result; + } + + ClosePairResult evaluateClosePair(const ClosePairTrack& pion, const ClosePairTrack& daughter) const + { + ClosePairResult result; + result.deltaEta = pion.eta - daughter.eta; + for (std::size_t i = 0; i < CPRTPCRadii.size(); ++i) { + if (pion.valid[i] && daughter.valid[i]) { + result.deltaPhiStar += wrapDeltaPhi(pion.phiStar[i] - daughter.phiStar[i]); + ++result.validRadii; + } + if (pion.positionValid[i] && daughter.positionValid[i]) { + const auto& pionPosition = pion.positions[i]; + const auto& daughterPosition = daughter.positions[i]; + result.averageDistance += std::hypot(pionPosition[0] - daughterPosition[0], + pionPosition[1] - daughterPosition[1], + pionPosition[2] - daughterPosition[2]); + ++result.validPositionRadii; + } + } + const bool sameSign = pion.sign * daughter.sign > 0; + result.angularUnavailable = sameSign && result.validRadii == 0; + result.distanceUnavailable = sameSign && result.validPositionRadii == 0; + // Undefined comparisons set separate status bits, never a rejection bit. + if (result.validRadii > 0) { + result.deltaPhiStar /= result.validRadii; + const float scaledEta = result.deltaEta / CPR.settingClosePairDeltaEtaMax.value; + const float scaledPhi = result.deltaPhiStar / CPR.settingClosePairDeltaPhiMax.value; + result.angularRejected = sameSign && scaledEta * scaledEta + scaledPhi * scaledPhi < 1.f; + } + if (result.validPositionRadii > 0) { + result.averageDistance /= result.validPositionRadii; + result.distanceRejected = sameSign && result.averageDistance < CPR.settingClosePairDistanceMax.value; + } + return result; + } + + uint8_t flagClosePair(const HadHyperCandidate& candidate, const HadHyperHadron& pion, bool mixed) + { + const std::array results{ + evaluateClosePair(pion.closePairTrack, candidate.daughterClosePairTracks[0]), + evaluateClosePair(pion.closePairTrack, candidate.daughterClosePairTracks[1])}; + uint8_t rejectionFlags = 0; + for (const auto& result : results) { + if (result.angularRejected) { + rejectionFlags |= aod::pihypertritonfemto::ClosePairAngular; + } + if (result.distanceRejected) { + rejectionFlags |= aod::pihypertritonfemto::ClosePairDistance; + } + if (result.angularUnavailable) { + rejectionFlags |= aod::pihypertritonfemto::ClosePairAngularUnavailable; + } + if (result.distanceUnavailable) { + rejectionFlags |= aod::pihypertritonfemto::ClosePairDistanceUnavailable; + } + } + for (std::size_t i = 0; i < results.size(); ++i) { + const auto& result = results[i]; + const auto& qa = mClosePairQA[mixed ? 1 : 0][i]; + qa.validRadii->Fill(result.validRadii); + qa.validPositionRadii->Fill(result.validPositionRadii); + if (result.validRadii > 0 && result.validPositionRadii > 0) { + qa.decisions->Fill(result.angularRejected, result.distanceRejected); + } + if (result.validPositionRadii > 0) { + qa.distanceBefore->Fill(result.averageDistance); + if (rejectionFlags == 0) { + qa.distanceAfter->Fill(result.averageDistance); + } + } + if (result.validRadii > 0) { + qa.before->Fill(result.deltaEta, result.deltaPhiStar); + if (rejectionFlags == 0) { + qa.after->Fill(result.deltaEta, result.deltaPhiStar); + } + } + } + return rejectionFlags; + } + + float computePairKstar(const std::array& momPion, const std::array& momHyper) const + { + const PairLorentzVector vecPion(momPion[0], momPion[1], momPion[2], o2::constants::physics::MassPiPlus); + const PairLorentzVector vecHyper(momHyper[0], momHyper[1], momHyper[2], o2::constants::physics::MassHyperTriton); + const PairLorentzVector trackSum = vecPion + vecHyper; + const float beta = trackSum.Beta(); + const float betax = beta * std::cos(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betay = beta * std::sin(trackSum.Phi()) * std::sin(trackSum.Theta()); + const float betaz = beta * std::cos(trackSum.Theta()); + PairLorentzVector pionCMS(vecPion); + PairLorentzVector hyperCMS(vecHyper); + const ROOT::Math::Boost boostPRF = ROOT::Math::Boost(-betax, -betay, -betaz); + pionCMS = boostPRF(pionCMS); + hyperCMS = boostPRF(hyperCMS); + return 0.5f * (pionCMS - hyperCMS).P(); + } + + template + HadHyperTrackInfo makePionInfo(const Ttrack& track) + { + constexpr float InvalidPID = -999.f; + return {track.pt(), track.eta(), track.phi(), static_cast(track.sign()), + track.dcaXY(), track.tpcNClsCrossedRows(), track.tpcNClsPID(), track.tpcChi2NCl(), + track.itsClusterSizes(), track.itsChi2NCl(), track.hasTOF(), + track.tpcNSigmaPi(), track.hasTOF() ? track.tofNSigmaPi() : InvalidPID}; + } + + template + HadHyperEventInfo makeEventInfo(const Tcollision& collision) + { + return {collision.posZ(), collision.centFT0C(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), + collision.multFT0C(), + collision.posX(), collision.posY(), collision.posZ()}; + } + + template + HadHyperParticleTruth makeParticleTruth(const Tparticle& particle) + { + return {particle.globalIndex(), particle.mcCollisionId(), particle.pdgCode(), particle.pt(), particle.eta(), particle.phi(), particle.isPhysicalPrimary(), static_cast(particle.statusCode()), static_cast(particle.getProcess()), {particle.px(), particle.py(), particle.pz()}}; + } + + template + HadHyperHadron makePion(const Ttrack& track, const std::array& primaryVertex) + { + HadHyperHadron pion{makePionInfo(track), {}, {}, track.globalIndex(), {track.px(), track.py(), track.pz()}, static_cast(track.sign())}; + pion.closePairTrack = makeClosePairTrack(track, primaryVertex); + if constexpr (isMC) { + if (track.has_mcParticle()) { + const auto particle = track.template mcParticle_as(); + pion.truth = makeParticleTruth(particle); + if (particle.has_mothers()) { + for (const auto& mother : particle.template mothers_as()) { + pion.motherIds.push_back(mother.globalIndex()); + } + } + } + } + return pion; + } + + template + HadHyperCandidate makeHyperCandidate(const Tcandidate& candidate, const Ttracks& tracks, const std::array& primaryVertex) + { + const auto he = tracks.rawIteratorAt(candidate.heTrackId() - tracks.offset()); + const auto decayPion = tracks.rawIteratorAt(candidate.piTrackId() - tracks.offset()); + const std::array heMomentum{candidate.ptHe3() * std::cos(candidate.phiHe3()), candidate.ptHe3() * std::sin(candidate.phiHe3()), candidate.ptHe3() * std::sinh(candidate.etaHe3())}; + const std::array decayPionMomentum{candidate.ptPi() * std::cos(candidate.phiPi()), candidate.ptPi() * std::sin(candidate.phiPi()), candidate.ptPi() * std::sinh(candidate.etaPi())}; + + HadHyperCandidate result; + for (std::size_t i = 0; i < result.momentum.size(); ++i) { + result.momentum[i] = heMomentum[i] + decayPionMomentum[i]; + } + result.mass = computeHyperCandidateMass(candidate); + result.heTrackId = candidate.heTrackId(); + result.pionTrackId = candidate.piTrackId(); + result.isMatter = candidate.isMatter(); + result.daughterClosePairTracks = {makeClosePairTrack(he, primaryVertex), makeClosePairTrack(decayPion, primaryVertex)}; + + if constexpr (isMC) { + if (he.has_mcParticle()) { + result.heTruth = makeParticleTruth(he.template mcParticle_as()); + } + if (decayPion.has_mcParticle()) { + result.decayPionTruth = makeParticleTruth(decayPion.template mcParticle_as()); + } + if (he.has_mcParticle() && decayPion.has_mcParticle()) { + const auto heParticle = he.template mcParticle_as(); + const auto decayPionParticle = decayPion.template mcParticle_as(); + if (heParticle.has_mothers() && decayPionParticle.has_mothers()) { + for (const auto& heMother : heParticle.template mothers_as()) { + for (const auto& pionMother : decayPionParticle.template mothers_as()) { + if (heMother.globalIndex() == pionMother.globalIndex() && std::abs(heMother.pdgCode()) == HyperTritonPDG) { + result.hyperTruth = makeParticleTruth(heMother); + } + } + } + } + } + } + + result.info = {candidate.isMatter(), candidate.ptHe3(), candidate.etaHe3(), candidate.phiHe3(), + candidate.ptPi(), candidate.etaPi(), candidate.phiPi(), + candidate.dcaV0Daug(), candidate.dcaHe(), candidate.dcaPi(), + candidate.nSigmaHe(), + candidate.nTPCCrossedRowsHe(), candidate.nTPCCrossedRowsPi(), + candidate.tpcMomHe(), candidate.tpcMomPi(), candidate.tpcSignalHe(), candidate.tpcSignalPi(), + candidate.itsClusterSizesHe(), candidate.itsClusterSizesPi(), + candidate.xDecVtx(), candidate.yDecVtx(), candidate.zDecVtx()}; + + if constexpr (isMC) { + result.statusCode = static_cast(candidate.statusCode()); + result.isReco = candidate.isReco(); + result.isSignal = candidate.isSignal(); + result.isRecoMCCollision = candidate.isRecoMCCollision(); + result.isSurvEvSel = candidate.isSurvEvSel(); + result.isTwoBodyDecay = candidate.isTwoBodyDecay(); + result.genPt = candidate.genPt(); + result.genEta = candidate.genEta(); + result.genPhi = candidate.genPhi(); + result.genPtHe3 = candidate.genPtHe3(); + result.genDecVtx = {candidate.genXDecVtx(), candidate.genYDecVtx(), candidate.genZDecVtx()}; + const float absGenPt = std::abs(candidate.genPt()); + result.genMomentum = {absGenPt * std::cos(candidate.genPhi()), absGenPt * std::sin(candidate.genPhi()), absGenPt * std::sinh(candidate.genEta())}; + } + + hadHyperRegistry.fill(HIST("hMass"), result.mass); + hadHyperRegistry.fill(HIST("hDaughterHeTPC"), he.tpcInnerParam(), he.tpcSignal()); + hadHyperRegistry.fill(HIST("hDaughterPiTPC"), decayPion.tpcInnerParam(), decayPion.tpcSignal()); + return result; + } + + HadHyperDataInfo makeDataInfo(const HadHyperCandidate& candidate, const HadHyperHadron& pion, const HadHyperEvent& hyperEvent, bool mixed, uint8_t closePairRejected, int mixingDepth) + { + return std::tuple_cat(std::make_tuple(mixed, closePairRejected, mixingDepth), hyperEvent.info, candidate.info, pion.info); + } + + bool hasTruthMother(const HadHyperHadron& pion, int64_t motherId) const + { + return motherId >= 0 && std::find(pion.motherIds.begin(), pion.motherIds.end(), motherId) != pion.motherIds.end(); + } + + bool isRecoSelfCorrelation(const HadHyperCandidate& candidate, const HadHyperHadron& pion, + const HadHyperEvent& hyperEvent, const HadHyperEvent& pionEvent) const + { + return hyperEvent.sourceFrameId == pionEvent.sourceFrameId && pion.sourceId >= 0 && + (pion.sourceId == candidate.heTrackId || pion.sourceId == candidate.pionTrackId); + } + + bool isTruthSelfCorrelation(const HadHyperCandidate& candidate, const HadHyperHadron& pion, + const HadHyperEvent& hyperEvent, const HadHyperEvent& pionEvent) const + { + return hyperEvent.sourceFrameId == pionEvent.sourceFrameId && pion.truth.particleId >= 0 && + (pion.truth.particleId == candidate.heTruth.particleId || + pion.truth.particleId == candidate.decayPionTruth.particleId || + hasTruthMother(pion, candidate.hyperTruth.particleId)); + } + + HadHyperMCInfo makeMCInfo(const HadHyperCandidate& candidate, const HadHyperHadron& pion, + const HadHyperEvent& hyperEvent, const HadHyperEvent& pionEvent) const + { + const bool sameMCCollision = hyperEvent.sourceFrameId == pionEvent.sourceFrameId && + candidate.hyperTruth.collisionId >= 0 && pion.truth.collisionId == candidate.hyperTruth.collisionId; + const bool matchesHypRecoMCCollision = hyperEvent.hasMCCollision && candidate.hyperTruth.collisionId == hyperEvent.mcCollisionId; + const bool matchesPairRecoMCCollision = pionEvent.hasMCCollision && pion.truth.collisionId == pionEvent.mcCollisionId; + const bool truthSelfCorrelation = isTruthSelfCorrelation(candidate, pion, hyperEvent, pionEvent); + const bool hadIsTruePion = pion.truth.particleId >= 0 && std::abs(pion.truth.pdgCode) == static_cast(PDG_t::kPiPlus); + const bool isTruePrimaryPiHyperPair = candidate.isSignal && + std::abs(candidate.hyperTruth.pdgCode) == HyperTritonPDG && + hadIsTruePion && + pion.truth.isPhysicalPrimary && sameMCCollision && !truthSelfCorrelation; + return {candidate.genPt, candidate.genEta, candidate.genPhi, + candidate.genDecVtx[0], candidate.genDecVtx[1], candidate.genDecVtx[2], + candidate.isReco, candidate.isSignal, candidate.isRecoMCCollision, candidate.isSurvEvSel, candidate.isTwoBodyDecay, candidate.statusCode, + candidate.heTruth.pt, candidate.heTruth.isPhysicalPrimary, candidate.decayPionTruth.isPhysicalPrimary, + std::get<0>(pion.info), std::get<1>(pion.info), std::get<2>(pion.info), + pion.truth.pt, pion.truth.eta, pion.truth.phi, pion.truth.isPhysicalPrimary, pion.truth.process, + sameMCCollision, matchesHypRecoMCCollision, matchesPairRecoMCCollision, truthSelfCorrelation, isTruePrimaryPiHyperPair, hadIsTruePion}; + } + + void fillMCQA(const HadHyperCandidate& candidate, const HadHyperHadron& pion, const HadHyperEvent& hyperEvent, float kstar, bool mixed) + { + if (!candidate.isReco || !candidate.isSignal || std::abs(candidate.hyperTruth.pdgCode) != HyperTritonPDG || std::abs(pion.truth.pdgCode) != static_cast(PDG_t::kPiPlus)) { + return; + } + + const float kstarMC = computePairKstar(pion.truth.momentum, candidate.genMomentum); + const auto fill = [&](const auto& prefix) { + if (std::isfinite(kstarMC)) { + hadHyperRegistry.fill(prefix + HIST("hKstarRecVsGenHyperReco"), kstarMC, kstar); + hadHyperRegistry.fill(prefix + HIST("hKstarResolutionHyperReco"), kstar, kstar - kstarMC); + } + + hadHyperRegistry.fill(prefix + HIST("hPrimaryPionVsKstarDen"), kstar); + hadHyperRegistry.fill(prefix + HIST("hPrimaryPionVsCentDen"), hyperEvent.centrality); + if (pion.truth.isPhysicalPrimary) { + hadHyperRegistry.fill(prefix + HIST("hPrimaryPionVsKstarNum"), kstar); + hadHyperRegistry.fill(prefix + HIST("hPrimaryPionVsCentNum"), hyperEvent.centrality); + } + }; + if (mixed) { + fill(HIST("MC/ME/")); + } else { + fill(HIST("MC/SE/")); + } + } + + template + bool fillPair(const HadHyperCandidate& candidate, const HadHyperHadron& pion, const HadHyperEvent& hyperEvent, const HadHyperEvent& pionEvent, bool mixed, int mixingDepth) + { + const float kstar = computePairKstar(pion.momentum, candidate.momentum); + if (isRecoSelfCorrelation(candidate, pion, hyperEvent, pionEvent)) { + const int reason = pion.sourceId == candidate.heTrackId ? 0 : 1; + hadHyperRegistry.fill(HIST("hSelfPairs"), reason, kstar); + if (!mixed) { + hadHyperRegistry.fill(HIST("hSameEventSelfPairs"), reason, kstar); + } + return false; + } + if constexpr (isMC) { + if (isTruthSelfCorrelation(candidate, pion, hyperEvent, pionEvent)) { + hadHyperRegistry.fill(HIST("hSelfPairs"), 2, kstar); + if (!mixed) { + hadHyperRegistry.fill(HIST("hSameEventSelfPairs"), 2, kstar); + } + return false; + } + } + + if (!std::isfinite(kstar)) { + return false; + } + // CPR is an offline flag only: do not remove rows or change raw-pair/MC QA counts. + const uint8_t closePairRejected = flagClosePair(candidate, pion, mixed); + + if (mixed) { + hadHyperRegistry.fill(HIST("hME"), kstar); + } else { + hadHyperRegistry.fill(HIST("hSE"), kstar); + } + if constexpr (isMC) { + fillMCQA(candidate, pion, hyperEvent, kstar, mixed); + } + + if (!output.settingFillTable || (hadHyper.maxOutputKstar.value > 0.f && kstar >= hadHyper.maxOutputKstar.value)) { + return true; + } + + const auto dataInfo = makeDataInfo(candidate, pion, hyperEvent, mixed, closePairRejected, mixingDepth); + if constexpr (isMC) { + std::apply([this](const auto&... columns) { mOutputMCTable(columns...); }, std::tuple_cat(dataInfo, makeMCInfo(candidate, pion, hyperEvent, pionEvent))); + } else { + std::apply([this](const auto&... columns) { mOutputDataTable(columns...); }, dataInfo); + } + return true; + } + + template + bool hasValidHyperDaughterIndices(const Ttracks& tracks, int64_t heTrackId, int64_t pionTrackId) const + { + const auto first = static_cast(tracks.offset()); + const auto last = first + static_cast(tracks.size()); + return heTrackId >= first && heTrackId < last && pionTrackId >= first && pionTrackId < last; + } + + template + void collectPions(HadHyperEvent& event, const Ttracks& eventTracks) + { + const std::array primaryVertex{std::get<5>(event.info), std::get<6>(event.info), std::get<7>(event.info)}; + for (const auto& track : eventTracks) { + if (!selectPionTrack(track) || !selectionPIDPion(track)) { + continue; + } + event.hadrons.push_back(makePion(track, primaryVertex)); + hadHyperRegistry.fill(HIST("hPairPionTPC"), track.tpcInnerParam(), track.tpcSignal()); + } + } + + template + void collectHyperCandidates(HadHyperEvent& event, const Tcandidates& eventCandidates, const Ttracks& tracks) + { + const std::array primaryVertex{std::get<5>(event.info), std::get<6>(event.info), std::get<7>(event.info)}; + for (const auto& candidate : eventCandidates) { + if constexpr (isMC) { + if (!candidate.isReco()) { + continue; + } + } + if (!hasValidHyperDaughterIndices(tracks, candidate.heTrackId(), candidate.piTrackId())) { + if constexpr (isMC) { + continue; + } else { + LOG(fatal) << "Hypertriton daughter indices must reference the input Tracks table"; + } + } + if (!selectHyperCandidate(candidate)) { + continue; + } + event.candidates.push_back(makeHyperCandidate(candidate, tracks, primaryVertex)); + } + } + + template + HadHyperEvent buildEvent(const Tcollision& collision, const Ttracks& tracks, const Tcandidates& candidates) + { + HadHyperEvent event; + event.info = makeEventInfo(collision); + event.centrality = collision.centFT0C(); + if constexpr (isMC) { + event.hasMCCollision = collision.has_mcCollision(); + event.mcCollisionId = collision.has_mcCollision() ? collision.mcCollisionId() : -1; + } + + if constexpr (isMC) { + const auto eventTracks = tracks.sliceBy(mPerColMC, collision.globalIndex()); + collectPions(event, eventTracks); + const auto eventCandidates = candidates.sliceBy(hypPerColMC, collision.globalIndex()); + collectHyperCandidates(event, eventCandidates, tracks); + } else { + const auto eventTracks = tracks.sliceBy(mPerCol, collision.globalIndex()); + collectPions(event, eventTracks); + const auto eventCandidates = candidates.sliceBy(hypPerCol, collision.globalIndex()); + collectHyperCandidates(event, eventCandidates, tracks); + } + return event; + } + + template + void fillSameEventPairs(const HadHyperEvent& event) + { + for (const auto& candidate : event.candidates) { + int acceptedPairs = 0; + for (const auto& pion : event.hadrons) { + if (fillPair(candidate, pion, event, event, false, 0)) { + ++acceptedPairs; + } + } + hadHyperRegistry.fill(HIST("hCandidatePairMultiplicitySE"), acceptedPairs, event.centrality); + } + } + + template + void fillMixedEventPairs(HadHyperEvent& currentEvent, std::deque& pool) + { + const int depth = static_cast(pool.size()); + hadHyperRegistry.fill(HIST("hMixingDepth"), depth); + for (std::size_t partnerIndex = 0; partnerIndex < pool.size(); ++partnerIndex) { + auto& partner = pool[partnerIndex]; + const float currentPosZ = std::get<0>(currentEvent.info); + const float partnerPosZ = std::get<0>(partner.info); + hadHyperRegistry.fill(HIST("hMixEventDeltaPosZVsCent"), currentEvent.centrality, currentPosZ - partnerPosZ); + hadHyperRegistry.fill(HIST("hMixEventDeltaCentFT0CVsCent"), currentEvent.centrality, currentEvent.centrality - partner.centrality); + hadHyperRegistry.fill(HIST("hMixEventDeltaPosZVsCent"), partner.centrality, partnerPosZ - currentPosZ); + hadHyperRegistry.fill(HIST("hMixEventDeltaCentFT0CVsCent"), partner.centrality, partner.centrality - currentEvent.centrality); + for (std::size_t candidateIndex = 0; candidateIndex < currentEvent.candidates.size(); ++candidateIndex) { + auto& candidate = currentEvent.candidates[candidateIndex]; + for (const auto& pion : partner.hadrons) { + if (fillPair(candidate, pion, currentEvent, partner, true, depth)) { + ++candidate.mixedPairCount; + } + } + } + for (std::size_t candidateIndex = 0; candidateIndex < partner.candidates.size(); ++candidateIndex) { + auto& candidate = partner.candidates[candidateIndex]; + for (const auto& pion : currentEvent.hadrons) { + if (fillPair(candidate, pion, partner, currentEvent, true, depth)) { + ++candidate.mixedPairCount; + } + } + } + } + } + + void flushMixedEventMultiplicity(const HadHyperEvent& event) + { + // One entry per candidate, including zero partners, after all its actual ME pairings. + for (const auto& candidate : event.candidates) { + hadHyperRegistry.fill(HIST("hCandidatePairMultiplicityME"), candidate.mixedPairCount, event.centrality); + } + } + + void flushMixingPools(std::unordered_map>& pools) + { + for (const auto& [bin, pool] : pools) { + for (const auto& event : pool) { + flushMixedEventMultiplicity(event); + } + } + pools.clear(); + } + + void storeEventInPool(std::deque& pool, HadHyperEvent&& event) + { + const int requestedMixingDepth = eventMixing.settingNoMixedEvents.value; + if (requestedMixingDepth <= 0) { + return; + } + const auto mixingDepth = static_cast(requestedMixingDepth); + if (pool.size() >= mixingDepth) { + flushMixedEventMultiplicity(pool.front()); + pool.pop_front(); + } + pool.push_back(std::move(event)); + } + + void endOfStream(o2::framework::EndOfStreamContext&) + { + flushMixingPools(mMixingPools); + flushMixingPools(mMCMixingPools); + } + + void processHyper(const HadHyperCollisionsFull& collisions, const TrackCandidates& tracks, + const HyperCandidates& candidates, const aod::BCsWithTimestamps& bcs) + { + processPairs(collisions, tracks, candidates, bcs, mMixingPools, mMixingRunNumber); + } + PROCESS_SWITCH(PiHypertritonFemto, processHyper, "Process pion-hypertriton same-event and mixed-event pairs", false); + + void processMCHyper(const HadHyperCollisionsFullMC& collisions, const TrackCandidatesMC& tracks, + const HyperCandidatesMC& candidates, const aod::McParticles&, const aod::BCsWithTimestamps& bcs) + { + processPairs(collisions, tracks, candidates, bcs, mMCMixingPools, mMCMixingRunNumber); + } + PROCESS_SWITCH(PiHypertritonFemto, processMCHyper, "Process MC pion-hypertriton same-event and mixed-event pairs", false); +}; + +void PiHypertritonFemto::init(o2::framework::InitContext&) +{ + *mEndOfStreamCallback = [this](EndOfStreamContext& context) { endOfStream(context); }; + hadHyperRegistry.addClone("MC/SE/", "MC/ME/"); + const AxisSpec pairMultiplicityAxis{axisPairMultiplicity, "pairs"}; + hadHyperRegistry.add("hCandidatePairMultiplicitySE", "Accepted same-event pion pairs per hypertriton candidate;pairs;centrality", + HistType::kTH2F, {pairMultiplicityAxis, {10, 0., 100.}}); + hadHyperRegistry.add("hCandidatePairMultiplicityME", "Total mixed-event pion pairs per hypertriton candidate over its pool lifetime;pairs;centrality", + HistType::kTH2F, {pairMultiplicityAxis, {10, 0., 100.}}); + + const std::array eventNames{"SE", "ME"}; + const std::array daughterNames{"He3", "DecayPi"}; + for (std::size_t eventIndex = 0; eventIndex < eventNames.size(); ++eventIndex) { + for (std::size_t daughterIndex = 0; daughterIndex < daughterNames.size(); ++daughterIndex) { + const std::string prefix = "CPR/" + eventNames[eventIndex] + "/" + daughterNames[daughterIndex]; + auto& qa = mClosePairQA[eventIndex][daughterIndex]; + qa.before = hadHyperRegistry.add((prefix + "/hBefore").c_str(), "All pairs;#Delta#eta;#LT#Delta#varphi*#GT", + HistType::kTH2F, {{200, -0.1, 0.1}, {200, -0.1, 0.1}}); + qa.after = hadHyperRegistry.add((prefix + "/hAfter").c_str(), "Pairs with offline CPR flags zero;#Delta#eta;#LT#Delta#varphi*#GT", + HistType::kTH2F, {{200, -0.1, 0.1}, {200, -0.1, 0.1}}); + qa.validRadii = hadHyperRegistry.add((prefix + "/hValidRadii").c_str(), "Common valid TPC radii (0=undefined CPR);radii;Comparisons", + HistType::kTH1F, {{10, -0.5, 9.5}}); + qa.distanceBefore = hadHyperRegistry.add((prefix + "/hDistanceBefore").c_str(), "All pairs;Average TPC separation (cm);Comparisons", + HistType::kTH1F, {{500, 0., 100.}}); + qa.distanceAfter = hadHyperRegistry.add((prefix + "/hDistanceAfter").c_str(), "Pairs with offline CPR flags zero;Average TPC separation (cm);Comparisons", + HistType::kTH1F, {{500, 0., 100.}}); + qa.validPositionRadii = hadHyperRegistry.add((prefix + "/hValidPositionRadii").c_str(), "Common extrapolated TPC radii (0=undefined distance);radii;Comparisons", + HistType::kTH1F, {{10, -0.5, 9.5}}); + qa.decisions = hadHyperRegistry.add((prefix + "/hDecisions").c_str(), "Valid angular and distance comparisons;Angular flag;Distance flag", + HistType::kTH2F, {{2, -0.5, 1.5}, {2, -0.5, 1.5}}); + } + } + + const bool processHyperPairs = doprocessHyper || doprocessMCHyper; + if (processHyperPairs && hadHyper.maxOutputKstar.value == 0.f) { + LOG(fatal) << "Pion-hypertriton mode requires a nonzero output k* range"; + } + if (processHyperPairs && hadHyper.enableMixing.value && eventMixing.settingNoMixedEvents.value <= 0) { + LOG(fatal) << "Pion-hypertriton mixed-event mode requires a positive mixing depth"; + } + if (processHyperPairs && hypertriton.settingHypMassMin.value >= hypertriton.settingHypMassMax.value) { + LOG(fatal) << "Pion-hypertriton mode requires settingHypMassMin < settingHypMassMax"; + } + if (!std::isfinite(CPR.settingClosePairDeltaEtaMax.value) || !std::isfinite(CPR.settingClosePairDeltaPhiMax.value) || + CPR.settingClosePairDeltaEtaMax.value <= 0.f || CPR.settingClosePairDeltaPhiMax.value <= 0.f) { + LOG(fatal) << "Close-pair rejection requires positive delta-eta and delta-phi limits"; + } + if (!std::isfinite(CPR.settingClosePairDistanceMax.value) || CPR.settingClosePairDistanceMax.value <= 0.f) { + LOG(fatal) << "Close-pair tagging requires a finite positive distance threshold"; + } + mZorroSummary.setObject(mZorro.getZorroSummary()); + mRunNumber = -1; + mCcdb->setURL(ccdb.settingCcdburl); + mCcdb->setCaching(true); + mCcdb->setLocalObjectValidityChecking(); + mCcdb->setFatalWhenNull(false); + + const std::array eventsLabels = { + "All", + "kNoITSROFrameBorder (data, optional)", + "kIsTriggerTVX", + "kNoTimeFrameBorder", + "z_{vtx}", + "kNoSameBunchPileup (optional)", + "kIsGoodZvtxFT0vsPV (optional)", + "sel8 (MC, optional)", + "Zorro (data, optional)", + "MC collision label (MC, optional)", + "Mixing bin (if enabled)"}; + for (std::size_t i = 0; i < eventsLabels.size(); i++) { + mQaRegistry.get(HIST("hEvents"))->GetXaxis()->SetBinLabel(i + 1, eventsLabels[i].c_str()); + } +} + +template +void PiHypertritonFemto::initCCDB(const aod::BCsWithTimestamps::iterator& bc) +{ + if (mRunNumber == bc.runNumber()) { + return; + } + auto* magneticField = mCcdb->getForTimeStamp(ccdb.settingGrpmagPath.value, bc.timestamp()); + if (!magneticField) { + LOG(fatal) << "Cannot load GRPMagField for daughter close-pair tagging at timestamp " << bc.timestamp(); + return; + } + constexpr float KilogaussToTesla = 0.1f; + mMagneticFieldTesla = KilogaussToTesla * magneticField->getNominalL3Field(); + if constexpr (!isMC) { + if (zorro.settingSkimmedProcessing.value) { + mZorro.initCCDB(mCcdb.service, bc.runNumber(), bc.timestamp(), zorro.settingTriggerMask.value.c_str()); + mZorro.populateHistRegistry(mQaRegistry, bc.runNumber()); + } + } + mRunNumber = bc.runNumber(); +} + +template +bool PiHypertritonFemto::selectCollision(const Tcollision& collision, const aod::BCsWithTimestamps&) +{ + auto bc = collision.template bc_as(); + initCCDB(bc); + mQaRegistry.fill(HIST("hEvents"), 0); + + // Match hyperRecoTask: the ITS readout-frame border cut applies only to data. + if constexpr (!isMC) { + if (!eventSelection.disableITSROFCut.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + } + // Disabled or inapplicable cuts pass through, keeping the cut-flow cumulative. + mQaRegistry.fill(HIST("hEvents"), 1); + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + mQaRegistry.fill(HIST("hEvents"), 2); + if (!collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + mQaRegistry.fill(HIST("hEvents"), 3); + if (std::abs(collision.posZ()) > eventMixing.settingCutVertex.value) { + return false; + } + mQaRegistry.fill(HIST("hEvents"), 4); + if (eventSelection.cfgEvSelkNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + mQaRegistry.fill(HIST("hEvents"), 5); + if (eventSelection.cfgEvSelkIsGoodZvtxFT0vsPV.value && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + mQaRegistry.fill(HIST("hEvents"), 6); + if constexpr (isMC) { + if (mc.settingRequireSel8.value && !collision.sel8()) { + return false; + } + } + + mQaRegistry.fill(HIST("hEvents"), 7); + if constexpr (!isMC) { + if (zorro.settingSkimmedProcessing.value) { + if (!mZorro.isSelected(bc.globalBC())) { + return false; + } + } + } + mQaRegistry.fill(HIST("hEvents"), 8); + mQaRegistry.fill(HIST("hNcontributor"), collision.numContrib()); + mQaRegistry.fill(HIST("hVtxZ"), collision.posZ()); + return true; +} + +template +bool PiHypertritonFemto::selectPionTrack(const Ttrack& candidate) const +{ + const float absPt = std::abs(candidate.pt()); + if (std::abs(candidate.eta()) > pionTrack.settingCutEta.value || absPt < pionTrack.settingPtMin.value || absPt > pionTrack.settingPtMax.value || absPt <= 0.f) { + return false; + } + if (candidate.itsNClsInnerBarrel() < pionTrack.settingITSInnerBarrelMin.value || + candidate.itsNCls() < pionTrack.settingITSNClsMin.value || + candidate.tpcNClsFound() < pionTrack.settingTPCNClsFoundMin.value || + candidate.tpcNClsCrossedRows() < pionTrack.settingTPCCrossedRowsMin.value) { + return false; + } + const float pionDCAxyMax = pionTrack.settingDCAxyOffset.value + pionTrack.settingDCAxyPtCoeff.value / absPt; + const float pionDCAzMax = pionTrack.settingDCAzOffset.value + pionTrack.settingDCAzPtCoeff.value / absPt; + return !(std::abs(candidate.dcaXY()) > pionDCAxyMax || std::abs(candidate.dcaZ()) > pionDCAzMax); +} + +template +bool PiHypertritonFemto::selectionPIDPion(const Ttrack& candidate) +{ + const float tpcNSigmaPi = candidate.tpcNSigmaPi(); + const float absP = std::abs(candidate.p()); + mQaRegistry.fill(HIST("hPionTPCNSigmaPreselection"), candidate.sign() * candidate.tpcInnerParam(), tpcNSigmaPi); + if (absP <= pionPid.settingMomCombMin.value) { + if (std::abs(tpcNSigmaPi) > pionPid.settingTPCNsigMax.value) { + return false; + } + mQaRegistry.fill(HIST("hPionTPCNSigma"), candidate.sign() * candidate.pt(), tpcNSigmaPi); + mQaRegistry.fill(HIST("hPionTPC"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; + } + if (!candidate.hasTOF()) { + return false; + } + const float tofNSigmaPi = candidate.tofNSigmaPi(); + const float combNsigma = std::sqrt(tofNSigmaPi * tofNSigmaPi + tpcNSigmaPi * tpcNSigmaPi); + mQaRegistry.fill(HIST("hPionTOFNSigmaPreselection"), candidate.sign() * candidate.pt(), tofNSigmaPi); + mQaRegistry.fill(HIST("hPionCombNSigmaPreselection"), candidate.sign() * candidate.pt(), combNsigma); + if (combNsigma > pionPid.settingCombNsigMax.value) { + return false; + } + if (pionPid.settingReqSingleNsig.value && (std::abs(tpcNSigmaPi) > pionPid.settingCombNsigMax.value || std::abs(tofNSigmaPi) > pionPid.settingCombNsigMax.value)) { + return false; + } + mQaRegistry.fill(HIST("hPionTPCNSigma"), candidate.sign() * candidate.pt(), tpcNSigmaPi); + mQaRegistry.fill(HIST("hPionTOFNSigma"), candidate.sign() * candidate.pt(), tofNSigmaPi); + mQaRegistry.fill(HIST("hPionCombNSigma"), candidate.sign() * candidate.pt(), combNsigma); + mQaRegistry.fill(HIST("hPionTPC"), candidate.sign() * candidate.tpcInnerParam(), candidate.tpcSignal()); + return true; +} + +template +bool PiHypertritonFemto::selectHyperCandidate(const Tcandidate& candidate) +{ + mQaRegistry.fill(HIST("hHypHe3TPCMomPreselection"), candidate.tpcMomHe()); + const float mass = computeHyperCandidateMass(candidate); + if (!std::isfinite(mass) || mass < hypertriton.settingHypMassMin.value || mass > hypertriton.settingHypMassMax.value) { + return false; + } + mQaRegistry.fill(HIST("hHypHe3TPCMom"), candidate.tpcMomHe()); + mQaRegistry.fill(HIST("hHypHe3TPCNSigma"), candidate.ptHe3(), candidate.nSigmaHe()); + return true; +} + +template +void PiHypertritonFemto::processPairs(const Tcollisions& collisions, const Ttracks& tracks, const Tcandidates& candidates, + const aod::BCsWithTimestamps& bcs, + std::unordered_map>& mixingPools, + int& mixingRunNumber) +{ + // Each invocation receives a new input-table scope; row indices can repeat in later frames. + const uint64_t sourceFrameId = ++mSourceFrameId; + const BinningType configuredBinningPolicy{{axisVertex, axisCentrality}}; + for (const auto& collision : collisions) { + if (!selectCollision(collision, bcs)) { + continue; + } + if constexpr (isMC) { + if (mc.settingRequireRecoMCCollisionMatch.value && !collision.has_mcCollision()) { + continue; + } + } + mQaRegistry.fill(HIST("hEvents"), 9); + hadHyperRegistry.fill(HIST("hPoolFlow"), 0); + int poolBin = -1; + if (hadHyper.enableMixing.value) { + poolBin = configuredBinningPolicy.getBin(std::make_tuple(collision.posZ(), collision.centFT0C())); + if (poolBin < 0) { + continue; + } + hadHyperRegistry.fill(HIST("hPoolFlow"), 1); + } + mQaRegistry.fill(HIST("hEvents"), 10); + auto event = buildEvent(collision, tracks, candidates); + event.sourceFrameId = sourceFrameId; + fillSameEventPairs(event); + if (!hadHyper.enableMixing.value) { + continue; + } + const auto bc = collision.template bc_as(); + if (mixingRunNumber != bc.runNumber()) { + flushMixingPools(mixingPools); + mixingRunNumber = bc.runNumber(); + } + auto& pool = mixingPools[poolBin]; + fillMixedEventPairs(event, pool); + storeEventInPool(pool, std::move(event)); + } +} + +WorkflowSpec defineDataProcessing(const ConfigContext& cfgc) +{ + auto finishMixing = std::make_shared>(); + auto task = adaptAnalysisTask(cfgc, finishMixing); + auto initializeTask = task.algorithm.onInit; + task.algorithm.onInit = [initializeTask, finishMixing](InitContext& context) { + // CallbackService preserves registration order. Register before adaptAnalysisTask's + // initializer so the pools are flushed BEFORE its histogram snapshot/cleanup callback. + context.services().get().set( + [finishMixing](EndOfStreamContext& eosContext) { + if (*finishMixing) { + (*finishMixing)(eosContext); + } + }); + return initializeTask(context); + }; + return WorkflowSpec{std::move(task)}; +} diff --git a/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx b/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx index be61cd4385d..7f9a02892f5 100644 --- a/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx +++ b/PWGLF/TableProducer/Nuspex/pidTOFGeneric.cxx @@ -23,6 +23,7 @@ #include "Common/Core/CollisionTypeHelper.h" #include "Common/Core/MetadataHelper.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" @@ -37,7 +38,6 @@ #include #include #include -#include #include #include diff --git a/PWGLF/TableProducer/Nuspex/threebodymcfinder.cxx b/PWGLF/TableProducer/Nuspex/threebodymcfinder.cxx index 47c8a72c420..4c2c2dc2ef3 100644 --- a/PWGLF/TableProducer/Nuspex/threebodymcfinder.cxx +++ b/PWGLF/TableProducer/Nuspex/threebodymcfinder.cxx @@ -59,7 +59,7 @@ struct threebodymcfinder { Configurable requireITS{"requireITS", false, "require ITS information used in tracks"}; - Preslice perMcCollision = aod::mcparticle::mcCollisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; std::vector d3bcollisionId; std::vector d3bprong0Index; diff --git a/PWGLF/TableProducer/Nuspex/trackedHypertritonRecoTask.cxx b/PWGLF/TableProducer/Nuspex/trackedHypertritonRecoTask.cxx new file mode 100644 index 00000000000..4297ebc4eba --- /dev/null +++ b/PWGLF/TableProducer/Nuspex/trackedHypertritonRecoTask.cxx @@ -0,0 +1,1234 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trackedHypertritonRecoTask.cxx +/// \brief Build two- and three-body hypertriton analysis tables from strangeness-tracked candidates + +#ifndef HomogeneousField +#define HomogeneousField +#endif + +#include "PWGLF/DataModel/LFHypernucleiTables.h" +#include "PWGLF/DataModel/LFPIDTOFGenericTables.h" +#include "PWGLF/DataModel/Vtx3BodyTables.h" +#include "PWGLF/Utils/decay3bodyBuilderHelper.h" +#include "PWGLF/Utils/pidTOFGeneric.h" + +#include "Common/Core/MetadataHelper.h" +#include "Common/Core/PID/PIDTOF.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +o2::common::core::MetadataHelper metadataInfo{}; + +namespace +{ +using Collisions = soa::Join; +using Tracks = soa::Join; +using CollisionsMC = soa::Join; +using TracksMC = soa::Join; + +constexpr std::array betheBlochDefault{-1.e32, -1.e32, -1.e32, -1.e32, -1.e32, -1.e32}; +constexpr double UseCCDBMagneticFieldThreshold = -990.; +const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; +const std::vector particleName{"He3"}; + +enum ZorroTrigger : std::size_t { + kHe = 0, + kTracked3Body, + kNZorroTriggers +}; +} // namespace + +struct TrackedHypertritonRecoTask { + Produces dataHypCands; + Produces mcHypCands; + Produces vtx3BodyDatas; + Produces vtx3BodyCovs; + Produces vtx3BodyTrackedInfo; + Produces vtx3BodyCollision; + Produces mcVtx3BodyDatas; + Produces mcVtx3BodyCollision; + + Service ccdb{}; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + Configurable requireSel8{"requireSel8", true, "Require the standard sel8 event selection"}; + Configurable maxAbsZvtx{"maxAbsZvtx", 10.f, "Maximum absolute primary-vertex z; negative disables the cut"}; + Configurable skimmedProcessing{"skimmedProcessing", false, "Enable Zorro accounting and trigger selection for skimmed data"}; + Configurable zorroCCDBPath{"zorroCCDBPath", "EventFiltering/Zorro/", "Base path of the Zorro CCDB objects"}; + Configurable zorroBCTolerance{"zorroBCTolerance", 100, "Zorro BC matching tolerance"}; + + Configurable ccdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "CCDB URL"}; // o2-linter: disable=name/configurable (Keep the standard CCDB option name.) + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the magnetic-field object"}; + Configurable bzInput{"bz", -999., "Magnetic field in kG; values below -990 use CCDB"}; // o2-linter: disable=name/configurable (Keep the established magnetic-field option name.) + + struct : ConfigurableGroup { + std::string prefix = "mc"; + Configurable storeBackground{"storeBackground", true, "Store reconstructed MC candidates not matched to a hypertriton"}; + } mc; + + struct : ConfigurableGroup { + std::string prefix = "twoBody"; + Configurable useKFParticle{"useKFParticle", false, "Use KFParticle to build the two-body candidate"}; + Configurable useSelections{"useSelections", false, "Apply the two-body candidate selections"}; + Configurable kfSetTopologicalConstraint{"kfSetTopologicalConstraint", false, "Set topological vertex constraint in case of KFParticle reconstruction"}; + Configurable maxEtaDaughters{"maxEtaDaughters", 1.e10f, "Maximum absolute daughter eta"}; + Configurable minTPCNClsHe{"minTPCNClsHe", -1.f, "Minimum He TPC clusters"}; + Configurable minTPCNClsPi{"minTPCNClsPi", -1.f, "Minimum pion TPC clusters"}; + Configurable minTPCCrossedRowsHe{"minTPCCrossedRowsHe", -1.f, "Minimum He TPC crossed rows"}; + Configurable minTPCCrossedRowsPi{"minTPCCrossedRowsPi", -1.f, "Minimum pion TPC crossed rows"}; + Configurable minTPCInnerParamHe{"minTPCInnerParamHe", -1.f, "Minimum He TPC rigidity"}; + Configurable minPt{"minPt", -1.f, "Minimum candidate transverse momentum"}; + Configurable massWindow{"massWindow", 1.e10f, "Half-width of the hypertriton mass window"}; + Configurable maxChi2{"maxChi2", 1.e10f, "KFParticle: Maximum SV chi2, DCA fitter: Maximum DCA between daughters"}; + Configurable minCosPA{"minCosPA", -2.f, "Minimum cosine of the pointing angle"}; + Configurable minDcaHeToPV{"minDcaHeToPV", -1.f, "Minimum absolute He DCA to PV"}; + Configurable minDcaPiToPV{"minDcaPiToPV", -1.f, "Minimum absolute pion DCA to PV"}; + Configurable minNSigmaHe{"minNSigmaHe", -1.e10f, "Minimum He TPC n-sigma"}; + Configurable compensatePIDinTracking{"compensatePIDinTracking", true, "Correct the TPC inner parameter for charge-two PID in tracking"}; + Configurable> betheBlochParams{"betheBlochParams", {betheBlochDefault.data(), 1, 6, particleName, betheBlochParNames}, "TPC Bethe-Bloch parameters for He3"}; + } twoBody; + + struct : ConfigurableGroup { + std::string prefix = "threeBody"; + Configurable useKFParticle{"useKFParticle", false, "Use KFParticle to build the three-body candidate"}; + Configurable setTopologicalConstraint{"setTopologicalConstraint", false, "Apply the KFParticle topological constraint"}; + Configurable useSelections{"useSelections", false, "Apply the standard three-body candidate selections"}; + Configurable useChi2Selection{"useChi2Selection", false, "Apply the candidate chi2 selection"}; + Configurable useTPCforPion{"useTPCforPion", false, "Require TPC information for the pion"}; + Configurable acceptTPCOnly{"acceptTPCOnly", false, "Accept TPC-only daughter tracks"}; + Configurable askOnlyITSMatch{"askOnlyITSMatch", true, "Require an ITS match when rejecting TPC-only tracks"}; + Configurable calculateCovariance{"calculateCovariance", true, "Calculate candidate and daughter covariance matrices"}; + Configurable maxEtaDaughters{"maxEtaDaughters", 0.9f, "Maximum absolute daughter eta"}; + Configurable minTPCNClProton{"minTPCNClProton", 90, "Minimum proton TPC clusters"}; + Configurable minTPCNClPion{"minTPCNClPion", 70, "Minimum pion TPC clusters"}; + Configurable minTPCNClDeuteron{"minTPCNClDeuteron", 100, "Minimum deuteron TPC clusters"}; + Configurable minDCAProtonToPV{"minDCAProtonToPV", 0.1f, "Minimum proton DCA to PV"}; + Configurable minDCAPionToPV{"minDCAPionToPV", 0.1f, "Minimum pion DCA to PV"}; + Configurable minDCADeuteronToPV{"minDCADeuteronToPV", 0.1f, "Minimum deuteron DCA to PV"}; + Configurable minPtProton{"minPtProton", 0.3f, "Minimum proton pT"}; + Configurable minPtPion{"minPtPion", 0.1f, "Minimum pion pT"}; + Configurable minPtDeuteron{"minPtDeuteron", 0.6f, "Minimum deuteron pT"}; + Configurable maxPtProton{"maxPtProton", 5.f, "Maximum proton pT"}; + Configurable maxPtPion{"maxPtPion", 1.2f, "Maximum pion pT"}; + Configurable maxPtDeuteron{"maxPtDeuteron", 10.f, "Maximum deuteron pT"}; + Configurable maxTPCNSigma{"maxTPCNSigma", 5.f, "Maximum absolute daughter TPC n-sigma"}; + Configurable minTOFNSigmaDeuteron{"minTOFNSigmaDeuteron", -5.f, "Minimum deuteron TOF n-sigma"}; + Configurable maxTOFNSigmaDeuteron{"maxTOFNSigmaDeuteron", 5.f, "Maximum deuteron TOF n-sigma"}; + Configurable minPDeuteronUseTOF{"minPDeuteronUseTOF", 1.f, "Minimum deuteron momentum at which TOF PID is required"}; + Configurable maxDCADaughtersToSVAverage{"maxDCADaughtersToSVAverage", 0.5f, "Maximum average daughter DCA to SV"}; + Configurable maxRapidity{"maxRapidity", 1.f, "Maximum absolute candidate rapidity"}; + Configurable minPt{"minPt", 2.f, "Minimum candidate pT"}; + Configurable maxPt{"maxPt", 5.f, "Maximum candidate pT"}; + Configurable minMass{"minMass", 2.96f, "Minimum candidate mass"}; + Configurable maxMass{"maxMass", 3.04f, "Maximum candidate mass"}; + Configurable minCtau{"minCtau", 0.f, "Minimum candidate c tau"}; + Configurable maxCtau{"maxCtau", 100.f, "Maximum candidate c tau"}; + Configurable minCosPA{"minCosPA", 0.9f, "Minimum candidate cosine of the pointing angle"}; + Configurable maxChi2{"maxChi2", 100.f, "Maximum candidate chi2"}; + Configurable maxITSDCAxytrackToSV{"maxITSDCAxytrackToSV", 10.0, "Maximum distance of ITS matched track to SV in xy"}; + Configurable maxITSDCAztrackToSV{"maxITSDCAztrackToSV", 10.0, "Maximum distance of ITS matched track to SV in z"}; + } threeBody; + + o2::vertexing::DCAFitterN<2> fitter2Body; + o2::pwglf::decay3bodyBuilderHelper builder3Body; + o2::pid::tof::TOFResoParamsV3 tofResponse; + o2::aod::pidtofgeneric::TOFCalibConfig tofCalibConfig; + o2::aod::pidtofgeneric::TofPidNewCollision deuteronTOFPID; + o2::aod::pidtofgeneric::TofPidNewCollision deuteronTOFPIDMC; + + int runNumber = 0; + float bz = 0.f; + std::array bbParamsHe{}; + std::vector goodCollision; + std::vector> zorroDecision; + std::vector recoCollisionForMC; + std::vector survivedMCEventSelection; + + struct v0Candidate { + // daughter properties + std::array momHelium{}; + std::array momPion{}; + std::array posHelium{}; + std::array posPion{}; + // vertex properties + float mass{}; + float chi2{}; + float cosPA{}; + std::array decayVertex{}; + std::array momentum{}; + }; + + v0Candidate v0; + + struct TwoBodyMCInfo { + float genPt = -1.f; + float genPhi = -1.f; + float genEta = -1.f; + float genPtHe3 = -1.f; + std::array genDecayVertex{-1.f, -1.f, -1.f}; + bool isSignal = false; + bool isRecoMCCollision = false; + bool survivedEventSelection = false; + uint8_t fakeHeITSLayerMap{}; + int motherLabel = -1; + int statusCode = 0; + }; + + struct ThreeBodyMCInfo { + std::array genMomentum{-1.f, -1.f, -1.f}; + std::array genDecayVertex{-1.f, -1.f, -1.f}; + float genCt = -1.f; + float genPhi = -1.f; + float genEta = -1.f; + float genRapidity = -1.f; + std::array genMomProton{-1.f, -1.f, -1.f}; + std::array genMomPion{-1.f, -1.f, -1.f}; + std::array genMomDeuteron{-1.f, -1.f, -1.f}; + bool isReco = true; + int motherLabel = -1; + int motherPdgCode = 0; + int protonPdgCode = -1; + int pionPdgCode = -1; + int deuteronPdgCode = -1; + bool isDeuteronPrimary = false; + bool isRecoMCCollision = false; + bool survivedEventSelection = false; + }; + + void init(InitContext& context) + { + zorroSummary.setObject(zorro.getZorroSummary()); + zorro.setBaseCCDBPath(zorroCCDBPath); + zorro.setBCtolerance(zorroBCTolerance); + + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + fitter2Body.setPropagateToPCA(true); + fitter2Body.setMaxR(200.f); + fitter2Body.setMinParamChange(1.e-3f); + fitter2Body.setMinRelChi2Change(0.9f); + fitter2Body.setMaxDZIni(1.e9f); + fitter2Body.setMaxChi2(1.e9f); + fitter2Body.setUseAbsDCA(true); + + deuteronTOFPID.SetPidType(o2::track::PID::Deuteron); + deuteronTOFPIDMC.SetPidType(o2::track::PID::Deuteron); + tofCalibConfig.metadataInfo = metadataInfo; + tofCalibConfig.inheritFromBaseTask(context); + tofCalibConfig.initSetup(tofResponse, ccdb); + + auto& selections = builder3Body.decay3bodyselections; + selections.maxEtaDaughters = threeBody.maxEtaDaughters; + selections.minTPCNClProton = threeBody.minTPCNClProton; + selections.minTPCNClPion = threeBody.minTPCNClPion; + selections.minTPCNClDeuteron = threeBody.minTPCNClDeuteron; + selections.minDCAProtonToPV = threeBody.minDCAProtonToPV; + selections.minDCAPionToPV = threeBody.minDCAPionToPV; + selections.minDCADeuteronToPV = threeBody.minDCADeuteronToPV; + selections.minDCAProtonToPVprop = threeBody.minDCAProtonToPV; + selections.minDCAPionToPVprop = threeBody.minDCAPionToPV; + selections.minDCADeuteronToPVprop = threeBody.minDCADeuteronToPV; + selections.minPtProton = threeBody.minPtProton; + selections.minPtPion = threeBody.minPtPion; + selections.minPtDeuteron = threeBody.minPtDeuteron; + selections.maxPtProton = threeBody.maxPtProton; + selections.maxPtPion = threeBody.maxPtPion; + selections.maxPtDeuteron = threeBody.maxPtDeuteron; + selections.maxTPCnSigma = threeBody.maxTPCNSigma; + selections.minTOFnSigmaDeuteron = threeBody.minTOFNSigmaDeuteron; + selections.maxTOFnSigmaDeuteron = threeBody.maxTOFNSigmaDeuteron; + selections.minPDeuteronUseTOF = threeBody.minPDeuteronUseTOF; + selections.maxDCADauToSVaverage = threeBody.maxDCADaughtersToSVAverage; + selections.maxRapidity = threeBody.maxRapidity; + selections.minPt = threeBody.minPt; + selections.maxPt = threeBody.maxPt; + selections.minMass = threeBody.minMass; + selections.maxMass = threeBody.maxMass; + selections.minCtau = threeBody.minCtau; + selections.maxCtau = threeBody.maxCtau; + selections.minCosPA = threeBody.minCosPA; + selections.maxChi2 = threeBody.maxChi2; + + registry.add("events", "Event selection;selection;events", HistType::kTH1D, {{2, -0.5, 1.5}}); + auto events = registry.get(HIST("events")); + events->GetXaxis()->SetBinLabel(1, "all"); + events->GetXaxis()->SetBinLabel(2, "selected"); + registry.add("zorroEvents", "Zorro accounting;trigger;selection", HistType::kTH2D, + {{2, -0.5, 1.5}, {2, -0.5, 1.5}}); + auto zorroEvents = registry.get(HIST("zorroEvents")); + zorroEvents->GetXaxis()->SetBinLabel(1, "fHe"); + zorroEvents->GetXaxis()->SetBinLabel(2, "fTracked3Body"); + zorroEvents->GetYaxis()->SetBinLabel(1, "before sel8"); + zorroEvents->GetYaxis()->SetBinLabel(2, "after sel8"); + + registry.add("hTrackProtonTPCSignal", "hTrackProtonTPCSignal", HistType::kTH2F, {{100, -10.0f, 10.0f, "p/z (GeV/c)"}, {2000, 0.0f, 2000.0f, "d#it{E}/d#it{x}"}}); + registry.add("hTrackPionTPCSignal", "hTrackPionTPCSignal", HistType::kTH2F, {{100, -10.0f, 10.0f, "p/z (GeV/c)"}, {2000, 0.0f, 2000.0f, "d#it{E}/d#it{x}"}}); + registry.add("hTrackDeuteronTPCSignal", "hTrackDeuteronTPCSignal", HistType::kTH2F, {{100, -10.0f, 10.0f, "p/z (GeV/c)"}, {2000, 0.0f, 2000.0f, "d#it{E}/d#it{x}"}}); + registry.add("hTrackProtonTPCSignalSelected", "hTrackProtonTPCSignalSelected", HistType::kTH2F, {{100, -10.0f, 10.0f, "p/z (GeV/c)"}, {2000, 0.0f, 2000.0f, "d#it{E}/d#it{x}"}}); + registry.add("hTrackPionTPCSignalSelected", "hTrackPionTPCSignalSelected", HistType::kTH2F, {{100, -10.0f, 10.0f, "p/z (GeV/c)"}, {2000, 0.0f, 2000.0f, "d#it{E}/d#it{x}"}}); + registry.add("hTrackDeuteronTPCSignalSelected", "hTrackDeuteronTPCSignalSelected", HistType::kTH2F, {{100, -10.0f, 10.0f, "p/z (GeV/c)"}, {2000, 0.0f, 2000.0f, "d#it{E}/d#it{x}"}}); + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (runNumber == bc.runNumber()) { + return; + } + + if (skimmedProcessing) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fHe,fTracked3Body"); + zorro.populateHistRegistry(registry, bc.runNumber()); + } + + auto* grpmag = ccdb->getForTimeStamp(grpmagPath, bc.timestamp()); + if (!grpmag) { + LOG(fatal) << "Missing magnetic-field object " << grpmagPath << " for timestamp " << bc.timestamp(); + } + o2::base::Propagator::initFieldFromGRP(grpmag); + bz = bzInput < UseCCDBMagneticFieldThreshold ? o2::base::Propagator::Instance()->getNominalBz() : bzInput; + o2::base::Propagator::Instance()->setNominalBz(bz); + fitter2Body.setBz(bz); + builder3Body.fitterV0.setBz(bz); + builder3Body.fitter3body.setBz(bz); +#ifdef HomogeneousField + KFParticle::SetField(bz); +#endif + + for (size_t i = 0; i < bbParamsHe.size(); ++i) { + bbParamsHe[i] = twoBody.betheBlochParams->get("He3", betheBlochParNames[i].c_str()); + } + tofCalibConfig.processSetup(tofResponse, ccdb, bc); + runNumber = bc.runNumber(); + } + + template + void selectCollisions(TCollision const& collisions, bool applyZorro) + { + goodCollision.assign(collisions.size(), false); + zorroDecision.assign(collisions.size(), {}); + for (const auto& collision : collisions) { + registry.fill(HIST("events"), 0.); + auto bc = collision.template bc_as(); + initCCDB(bc); + + if (applyZorro) { + zorro.isSelected(bc.globalBC(), zorroBCTolerance); + auto decisions = zorro.getTriggerOfInterestResults(); + for (size_t i = 0; i < std::min(decisions.size(), static_cast(kNZorroTriggers)); ++i) { + zorroDecision[collision.globalIndex()][i] = decisions[i]; + if (decisions[i]) { + registry.fill(HIST("zorroEvents"), static_cast(i), 0.); + } + } + } + + if (requireSel8 && !collision.sel8()) { + continue; + } + if (maxAbsZvtx >= 0.f && std::abs(collision.posZ()) > maxAbsZvtx) { + continue; + } + goodCollision[collision.globalIndex()] = true; + registry.fill(HIST("events"), 1.); + if (applyZorro) { + for (size_t i = 0; i < kNZorroTriggers; ++i) { + if (zorroDecision[collision.globalIndex()][i]) { + registry.fill(HIST("zorroEvents"), static_cast(i), 1.); + } + } + } + } + } + + template + void prepareMCEventInformation(TCollisions const& collisions, aod::McCollisions const& mcCollisions) + { + recoCollisionForMC.assign(mcCollisions.size(), -1); + survivedMCEventSelection.assign(mcCollisions.size(), false); + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + const int mcCollisionId = collision.mcCollisionId(); + if (mcCollisionId < 0 || mcCollisionId >= static_cast(mcCollisions.size())) { + continue; + } + if (recoCollisionForMC[mcCollisionId] < 0) { + recoCollisionForMC[mcCollisionId] = collision.globalIndex(); + } + if (goodCollision[collision.globalIndex()]) { + recoCollisionForMC[mcCollisionId] = collision.globalIndex(); + survivedMCEventSelection[mcCollisionId] = true; + } + } + } + + template + int findCommonMother(TParticleA const& particleA, TParticleB const& particleB) const + { + if (!particleA.has_mothers() || !particleB.has_mothers()) { + return -1; + } + for (const auto& motherA : particleA.template mothers_as()) { + for (const auto& motherB : particleB.template mothers_as()) { + if (motherA.globalIndex() == motherB.globalIndex()) { + return motherA.globalIndex(); + } + } + } + return -1; + } + + template + int findCommonMother(TParticleA const& particleA, TParticleB const& particleB, TParticleC const& particleC) const + { + if (!particleA.has_mothers() || !particleB.has_mothers() || !particleC.has_mothers()) { + return -1; + } + for (const auto& motherA : particleA.template mothers_as()) { + for (const auto& motherB : particleB.template mothers_as()) { + if (motherA.globalIndex() != motherB.globalIndex()) { + continue; + } + for (const auto& motherC : particleC.template mothers_as()) { + if (motherA.globalIndex() == motherC.globalIndex()) { + return motherA.globalIndex(); + } + } + } + } + return -1; + } + + template + TwoBodyMCInfo getTwoBodyMCInfo(TTrack const& heTrack, TTrack const& piTrack, TCollision const& collision, aod::McParticles const& mcParticles) const + { + TwoBodyMCInfo info; + if (collision.has_mcCollision()) { + info.survivedEventSelection = survivedMCEventSelection[collision.mcCollisionId()]; + } + if (!heTrack.has_mcParticle() || !piTrack.has_mcParticle()) { + return info; + } + + const auto mcHe = heTrack.template mcParticle_as(); + const auto mcPi = piTrack.template mcParticle_as(); + info.fakeHeITSLayerMap = heTrack.mcMask() & 0x7f; + const int motherLabel = findCommonMother(mcHe, mcPi); + if (motherLabel < 0) { + return info; + } + const auto mother = mcParticles.rawIteratorAt(motherLabel); + const int sign = mother.pdgCode() > 0 ? 1 : -1; + if (std::abs(mother.pdgCode()) != constants::physics::Pdg::kHyperTriton || + mcHe.pdgCode() != sign * constants::physics::Pdg::kHelium3 || mcPi.pdgCode() != -sign * PDG_t::kPiPlus) { + return info; + } + + info.isSignal = true; + info.motherLabel = motherLabel; + info.genPt = sign * mother.pt(); + info.genPhi = mother.phi(); + info.genEta = mother.eta(); + info.genPtHe3 = mcHe.pt(); + info.genDecayVertex = {mcHe.vx() - mother.vx(), mcHe.vy() - mother.vy(), mcHe.vz() - mother.vz()}; + info.statusCode = mcHe.getProcess(); + if (mother.mcCollisionId() >= 0 && mother.mcCollisionId() < static_cast(recoCollisionForMC.size())) { + info.isRecoMCCollision = recoCollisionForMC[mother.mcCollisionId()] >= 0; + info.survivedEventSelection = survivedMCEventSelection[mother.mcCollisionId()]; + } + return info; + } + + template + ThreeBodyMCInfo getThreeBodyMCInfo(TTrack const& trackProton, TTrack const& trackPion, TTrack const& trackDeuteron, + TCollision const& collision, aod::McParticles const& mcParticles) const + { + ThreeBodyMCInfo info; + if (collision.has_mcCollision()) { + info.survivedEventSelection = survivedMCEventSelection[collision.mcCollisionId()]; + } + if (!trackProton.has_mcParticle() || !trackPion.has_mcParticle() || !trackDeuteron.has_mcParticle()) { + return info; + } + + const auto mcProton = trackProton.template mcParticle_as(); + const auto mcPion = trackPion.template mcParticle_as(); + const auto mcDeuteron = trackDeuteron.template mcParticle_as(); + info.protonPdgCode = mcProton.pdgCode(); + info.pionPdgCode = mcPion.pdgCode(); + info.deuteronPdgCode = mcDeuteron.pdgCode(); + info.isDeuteronPrimary = mcDeuteron.isPhysicalPrimary(); + info.genMomProton = {mcProton.px(), mcProton.py(), mcProton.pz()}; + info.genMomPion = {mcPion.px(), mcPion.py(), mcPion.pz()}; + info.genMomDeuteron = {mcDeuteron.px(), mcDeuteron.py(), mcDeuteron.pz()}; + + const int motherLabel = findCommonMother(mcProton, mcPion, mcDeuteron); + if (motherLabel < 0) { + return info; + } + const auto mother = mcParticles.rawIteratorAt(motherLabel); + const int sign = mother.pdgCode() > 0 ? 1 : -1; + if (std::abs(mother.pdgCode()) != constants::physics::Pdg::kHyperTriton || + mcProton.pdgCode() != sign * PDG_t::kProton || mcPion.pdgCode() != -sign * PDG_t::kPiPlus || + mcDeuteron.pdgCode() != sign * constants::physics::Pdg::kDeuteron) { + return info; + } + + info.motherLabel = motherLabel; + info.motherPdgCode = mother.pdgCode(); + info.genMomentum = {mother.px(), mother.py(), mother.pz()}; + info.genDecayVertex = {mcProton.vx(), mcProton.vy(), mcProton.vz()}; + info.genCt = RecoDecay::sqrtSumOfSquares(mcProton.vx() - mother.vx(), mcProton.vy() - mother.vy(), mcProton.vz() - mother.vz()) * constants::physics::MassHyperTriton / (mother.p() + 1.e-10f); + info.genPhi = mother.phi(); + info.genEta = mother.eta(); + info.genRapidity = mother.y(); + if (mother.mcCollisionId() >= 0 && mother.mcCollisionId() < static_cast(recoCollisionForMC.size())) { + info.isRecoMCCollision = recoCollisionForMC[mother.mcCollisionId()] >= 0; + info.survivedEventSelection = survivedMCEventSelection[mother.mcCollisionId()]; + } + return info; + } + + template + float nSigmaHe3(TTrack const& track) const + { + const bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; + const float rigidity = heliumPID && twoBody.compensatePIDinTracking.value ? track.tpcInnerParam() / 2.f : track.tpcInnerParam(); + const float expected = o2::common::BetheBlochAleph(rigidity * 2.f / static_cast(constants::physics::MassHelium3), + bbParamsHe[0], bbParamsHe[1], bbParamsHe[2], bbParamsHe[3], bbParamsHe[4]); + return (track.tpcSignal() - expected) / (expected * bbParamsHe[5]); + } + + template + bool fit2BodyWithKF(TCollision const& collision, + TTrack const& trackHelium, + TTrack const& trackPion, + TTrackParCov const& trackHeliumCov, + TTrackParCov const& trackPionCov) + { + // initialise KF primary vertex + KFParticle kfpv(createKFPVertexFromCollision(collision)); + + // create KFParticle objects + KFParticle kfpHelium, kfpPion; + // helium + std::array xyz{}; + std::array pxpypz{}; + std::array xyzpxpypz{}; + trackHeliumCov.getPxPyPzGlo(pxpypz); + trackHeliumCov.getXYZGlo(xyz); + for (std::size_t i = 0; i < xyz.size(); ++i) { + xyzpxpypz[i] = xyz[i]; + xyzpxpypz[i + xyz.size()] = pxpypz[i] * 2; + } + std::array cv{}; + trackHeliumCov.getCovXYZPxPyPzGlo(cv); + kfpHelium.Create(xyzpxpypz.data(), cv.data(), trackHelium.sign() * 2, constants::physics::MassHelium3); + // pion + kfpPion = createKFParticleFromTrackParCov(trackPionCov, trackPion.sign(), constants::physics::MassPionCharged); + + // construct V0 vertex + KFParticle KFV0; + int nDaughtersV0 = 2; + const KFParticle* DaughtersV0[2] = {&kfpHelium, &kfpPion}; + KFV0.SetConstructMethod(2); + try { + KFV0.Construct(DaughtersV0, nDaughtersV0); + } catch (std::runtime_error& e) { + LOG(debug) << "Failed to create V0 vertex." << e.what(); + return false; + } + + // topological constraint + if (twoBody.kfSetTopologicalConstraint) { + KFV0.SetProductionVertex(kfpv); + KFV0.TransportToDecayVertex(); + } + + // get vertex position and momentum + v0.decayVertex[0] = KFV0.GetX(); + v0.decayVertex[1] = KFV0.GetY(); + v0.decayVertex[2] = KFV0.GetZ(); + v0.momentum[0] = KFV0.GetPx(); + v0.momentum[1] = KFV0.GetPy(); + v0.momentum[2] = KFV0.GetPz(); + + // transport all daughter tracks to hypertriton vertex + // float position[3]; + // for (int i; i < 3; i++) { + // position[i] = v0.decayVertex[i]; + // } + kfpHelium.TransportToPoint(v0.decayVertex.data()); + kfpPion.TransportToPoint(v0.decayVertex.data()); + + // daughter positions + v0.posHelium[0] = kfpHelium.GetX(); + v0.posHelium[1] = kfpHelium.GetY(); + v0.posHelium[2] = kfpHelium.GetZ(); + v0.posPion[0] = kfpPion.GetX(); + v0.posPion[1] = kfpPion.GetY(); + v0.posPion[2] = kfpPion.GetZ(); + + // daughter momenta + v0.momHelium[0] = kfpHelium.GetPx(); + v0.momHelium[1] = kfpHelium.GetPy(); + v0.momHelium[2] = kfpHelium.GetPz(); + v0.momPion[0] = kfpPion.GetPx(); + v0.momPion[1] = kfpPion.GetPy(); + v0.momPion[2] = kfpPion.GetPz(); + + // candidate mass + float mass{}; + float massErr{}; + KFV0.GetMass(mass, massErr); + v0.mass = mass; + + // vertex chi2 + v0.chi2 = KFV0.GetChi2() / KFV0.GetNDF(); + + return true; + } + + template + bool fit2bodyWithDCAFitter(TTrackParCov const& trackHeliumCov, + TTrackParCov const& trackPionCov) + { + int nCandidates = 0; + try { + nCandidates = fitter2Body.process(trackHeliumCov, trackPionCov); + } catch (...) { + LOG(error) << "Exception while fitting a tracked two-body candidate"; + return false; + } + if (nCandidates == 0) { + return false; + } + + // get daughter momenta + fitter2Body.getTrack(0).getPxPyPzGlo(v0.momHelium); + fitter2Body.getTrack(1).getPxPyPzGlo(v0.momPion); + + for (std::size_t i = 0; i < v0.momHelium.size(); ++i) { + v0.momHelium[i] *= 2.f; + } + + // compute candidate momentum + v0.momentum = {v0.momHelium[0] + v0.momPion[0], v0.momHelium[1] + v0.momPion[1], v0.momHelium[2] + v0.momPion[2]}; + + // compute candidate mass + const float heMomentum2 = RecoDecay::sumOfSquares(v0.momHelium[0], v0.momHelium[1], v0.momHelium[2]); + const float piMomentum2 = RecoDecay::sumOfSquares(v0.momPion[0], v0.momPion[1], v0.momPion[2]); + const float candidateMomentum2 = RecoDecay::sumOfSquares(v0.momentum[0], v0.momentum[1], v0.momentum[2]); + const float heEnergy = std::sqrt(heMomentum2 + constants::physics::MassHelium3 * constants::physics::MassHelium3); + const float piEnergy = std::sqrt(piMomentum2 + constants::physics::MassPionCharged * constants::physics::MassPionCharged); + v0.mass = std::sqrt((heEnergy + piEnergy) * (heEnergy + piEnergy) - candidateMomentum2); + + // get SV position + const auto& secondaryVertex = fitter2Body.getPCACandidate(); + for (std::size_t i = 0; i < v0.decayVertex.size(); i++) { + v0.decayVertex[i] = secondaryVertex[i]; + } + v0.chi2 = std::sqrt(fitter2Body.getChi2AtPCACandidate()); + + return true; + } + + template + void buildTwoBody(TTrack const& heTrack, TTrack const& piTrack, TCollision const& collision, float trackedClSize, TFillCandidate const& fillCandidate) + { + const std::array primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; + + auto heTrackCov = getTrackParCov(heTrack); + auto piTrackCov = getTrackParCov(piTrack); + + const bool fitSucceeded = twoBody.useKFParticle + ? fit2BodyWithKF(collision, heTrack, piTrack, heTrackCov, piTrackCov) + : fit2bodyWithDCAFitter(heTrackCov, piTrackCov); + if (!fitSucceeded) { + return; + } + + v0.cosPA = RecoDecay::cpa(primaryVertex, v0.decayVertex, v0.momentum); + if (twoBody.useSelections && + (std::hypot(v0.momentum[0], v0.momentum[1]) < twoBody.minPt || + std::abs(v0.mass - constants::physics::MassHyperTriton) > twoBody.massWindow || + v0.chi2 > twoBody.maxChi2 || + v0.cosPA < twoBody.minCosPA)) { + return; + } + + // Do propagation with Propagator including material interactions in all cases (KFParticle propagation would not include material) + std::array dcaInfo{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, heTrackCov, 2.f, fitter2Body.getMatCorrType(), &dcaInfo); + const float dcaHe = dcaInfo[0]; + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, piTrackCov, 2.f, fitter2Body.getMatCorrType(), &dcaInfo); + const float dcaPi = dcaInfo[0]; + if (twoBody.useSelections && (std::abs(dcaHe) < twoBody.minDcaHeToPV || std::abs(dcaPi) < twoBody.minDcaPiToPV)) { + return; + } + + const bool heliumPID = heTrack.pidForTracking() == o2::track::PID::Helium3 || heTrack.pidForTracking() == o2::track::PID::Alpha; + const float tpcMomentumHe = heliumPID && twoBody.compensatePIDinTracking ? heTrack.tpcInnerParam() / 2.f : heTrack.tpcInnerParam(); + float tofMass = 0.f; + if (heTrack.hasTOF()) { + float beta = o2::pid::tof::Beta::GetBeta(heTrack); + beta = std::clamp(beta, 1.e-4f, 1.f - 1.e-6f); + tofMass = 2.f * tpcMomentumHe * std::sqrt(1.f / (beta * beta) - 1.f); + } + auto flags = static_cast((heTrack.pidForTracking() & 0xf) << 4); + flags |= static_cast(piTrack.pidForTracking() & 0xf); + + fillCandidate(collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), + collision.posX(), collision.posY(), collision.posZ(), + runNumber, heTrack.sign() > 0, + std::hypot(v0.momHelium[0], v0.momHelium[1]), std::atan2(v0.momHelium[1], v0.momHelium[0]), RecoDecay::eta(v0.momHelium), + std::hypot(v0.momPion[0], v0.momPion[1]), std::atan2(v0.momPion[1], v0.momPion[0]), RecoDecay::eta(v0.momPion), + v0.decayVertex[0], v0.decayVertex[1], v0.decayVertex[2], + v0.chi2, dcaHe, dcaPi, + nSigmaHe3(heTrack), heTrack.tpcNClsFound(), piTrack.tpcNClsFound(), + static_cast(heTrack.tpcNClsFindable()) - heTrack.tpcNClsFindableMinusPID(), + static_cast(piTrack.tpcNClsFindable()) - piTrack.tpcNClsFindableMinusPID(), + heTrack.tpcNClsCrossedRows(), piTrack.tpcNClsCrossedRows(), + tpcMomentumHe, piTrack.tpcInnerParam(), heTrack.tpcSignal(), piTrack.tpcSignal(), + heTrack.tpcChi2NCl(), heTrack.itsChi2NCl(), piTrack.itsChi2NCl(), tofMass, + heTrack.itsClusterSizes(), piTrack.itsClusterSizes(), flags, static_cast(trackedClSize)); + } + + template + double deuteronTOFNSigma(TCollision const& collision, TTrack const& track) + { + if (!track.has_collision() || !track.hasTOF()) { + return -999.; + } + auto originalCollision = track.template collision_as(); + return deuteronTOFPID.GetTOFNSigma(tofResponse, track, originalCollision, collision); + } + + template + double deuteronTOFNSigmaMC(TCollision const& collision, TTrack const& track) + { + if (!track.has_collision() || !track.hasTOF()) { + return -999.; + } + auto originalCollision = track.template collision_as(); + return deuteronTOFPIDMC.GetTOFNSigma(tofResponse, track, originalCollision, collision); + } + + template + std::array getItsTrackDCAToSV(TTracked3body const& tracked3Body) + { + const auto itsTrack = tracked3Body.template itsTrack_as(); + auto itsTrackParCov = getTrackParCov(itsTrack); + std::array dcaInfo{}; + o2::base::Propagator::Instance()->propagateToDCABxByBz({builder3Body.decay3body.position[0], builder3Body.decay3body.position[1], builder3Body.decay3body.position[2]}, itsTrackParCov, 2.f, fitter2Body.getMatCorrType(), &dcaInfo); + return dcaInfo; + } + + template + void fillThreeBodyTables(TCollision const& collision) + { + const auto& candidate = builder3Body.decay3body; + vtx3BodyDatas(static_cast(candidate.sign), + candidate.mass, candidate.massV0, + candidate.position[0], candidate.position[1], candidate.position[2], + candidate.momentum[0], candidate.momentum[1], candidate.momentum[2], + candidate.chi2, candidate.trackedClSize, + candidate.momProton[0], candidate.momProton[1], candidate.momProton[2], + candidate.momPion[0], candidate.momPion[1], candidate.momPion[2], + candidate.momDeuteron[0], candidate.momDeuteron[1], candidate.momDeuteron[2], + candidate.xProton, candidate.xPion, candidate.xDeuteron, + candidate.trackDCAxyToPV[0], candidate.trackDCAxyToPV[1], candidate.trackDCAxyToPV[2], + candidate.trackDCAToPV[0], candidate.trackDCAToPV[1], candidate.trackDCAToPV[2], + candidate.trackDCAxyToPVprop[0], candidate.trackDCAxyToPVprop[1], candidate.trackDCAxyToPVprop[2], + candidate.trackDCAToPVprop[0], candidate.trackDCAToPVprop[1], candidate.trackDCAToPVprop[2], + candidate.daughterDCAtoSV[0], candidate.daughterDCAtoSV[1], candidate.daughterDCAtoSV[2], + candidate.daughterDCAtoSVaverage, candidate.cosPA, candidate.ctau, + candidate.tpcNsigma[0], candidate.tpcNsigma[1], candidate.tpcNsigma[2], candidate.tpcNsigma[3], + candidate.tpcSignal[0], candidate.tpcSignal[1], candidate.tpcSignal[2], + static_cast(candidate.tofNsigmaDeuteron), + candidate.averageITSClSize[0], candidate.averageITSClSize[1], candidate.averageITSClSize[2], + static_cast(candidate.tpcNCl[0]), static_cast(candidate.tpcNCl[1]), static_cast(candidate.tpcNCl[2]), + static_cast(candidate.pidForTrackingDeuteron)); + vtx3BodyCovs(candidate.covProton.data(), candidate.covPion.data(), candidate.covDeuteron.data(), candidate.covariance.data()); + vtx3BodyTrackedInfo(candidate.itsTrackDCAToSV[0], candidate.itsTrackDCAToSV[1]); + vtx3BodyCollision(collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), + collision.posX(), collision.posY(), collision.posZ(), + runNumber); + } + + void fillThreeBodyMCTable(ThreeBodyMCInfo const& info) + { + const auto& candidate = builder3Body.decay3body; + mcVtx3BodyDatas(static_cast(candidate.sign), + candidate.mass, candidate.massV0, + candidate.position[0], candidate.position[1], candidate.position[2], + candidate.momentum[0], candidate.momentum[1], candidate.momentum[2], + candidate.chi2, candidate.trackedClSize, + candidate.momProton[0], candidate.momProton[1], candidate.momProton[2], + candidate.momPion[0], candidate.momPion[1], candidate.momPion[2], + candidate.momDeuteron[0], candidate.momDeuteron[1], candidate.momDeuteron[2], + candidate.xProton, candidate.xPion, candidate.xDeuteron, + candidate.trackDCAxyToPV[0], candidate.trackDCAxyToPV[1], candidate.trackDCAxyToPV[2], + candidate.trackDCAToPV[0], candidate.trackDCAToPV[1], candidate.trackDCAToPV[2], + candidate.trackDCAxyToPVprop[0], candidate.trackDCAxyToPVprop[1], candidate.trackDCAxyToPVprop[2], + candidate.trackDCAToPVprop[0], candidate.trackDCAToPVprop[1], candidate.trackDCAToPVprop[2], + candidate.daughterDCAtoSV[0], candidate.daughterDCAtoSV[1], candidate.daughterDCAtoSV[2], + candidate.daughterDCAtoSVaverage, candidate.cosPA, candidate.ctau, + candidate.tpcNsigma[0], candidate.tpcNsigma[1], candidate.tpcNsigma[2], candidate.tpcNsigma[3], + candidate.tpcSignal[0], candidate.tpcSignal[1], candidate.tpcSignal[2], + static_cast(candidate.tofNsigmaDeuteron), + candidate.averageITSClSize[0], candidate.averageITSClSize[1], candidate.averageITSClSize[2], + static_cast(candidate.tpcNCl[0]), static_cast(candidate.tpcNCl[1]), static_cast(candidate.tpcNCl[2]), + static_cast(candidate.pidForTrackingDeuteron), + info.genMomentum[0], info.genMomentum[1], info.genMomentum[2], + info.genDecayVertex[0], info.genDecayVertex[1], info.genDecayVertex[2], + info.genCt, info.genPhi, info.genEta, info.genRapidity, + info.genMomProton[0], info.genMomProton[1], info.genMomProton[2], + info.genMomPion[0], info.genMomPion[1], info.genMomPion[2], + info.genMomDeuteron[0], info.genMomDeuteron[1], info.genMomDeuteron[2], + static_cast(info.isReco), info.motherLabel, info.motherPdgCode, + info.protonPdgCode, info.pionPdgCode, info.deuteronPdgCode, + info.isDeuteronPrimary, static_cast(info.survivedEventSelection)); + vtx3BodyCovs(candidate.covProton.data(), candidate.covPion.data(), candidate.covDeuteron.data(), candidate.covariance.data()); + vtx3BodyTrackedInfo(candidate.itsTrackDCAToSV[0], candidate.itsTrackDCAToSV[1]); + } + + void fillGeneratedThreeBodyMCTable(ThreeBodyMCInfo const& info) + { + mcVtx3BodyDatas(-1.f, // sign + -1.f, -1.f, // mass, massV0 + -1.f, -1.f, -1.f, // position + -1.f, -1.f, -1.f, // momentum + -1.f, -1.f, // chi2, trackedClSize + -1.f, -1.f, -1.f, // proton momentum + -1.f, -1.f, -1.f, // pion momentum + -1.f, -1.f, -1.f, // deuteron momentum + -1.f, -1.f, -1.f, // daughter x at inner update + -1.f, -1.f, -1.f, // track DCAxy to PV + -1.f, -1.f, -1.f, // track DCA to PV + -1.f, -1.f, -1.f, // propagated track DCAxy to PV + -1.f, -1.f, -1.f, // propagated track DCA to PV + -1.f, -1.f, -1.f, // daughter DCA to SV + -1.f, -1.f, -1.f, // average daughter DCA, cosPA, ctau + -1.f, -1.f, -1.f, -1.f, // TPC nSigmas + -1.f, 1.f, -1.f, // TPC signals + -1.f, // TOF nSigma deuteron + -1.f, -1.f, -1.f, // average cluster sizes + -1, -1, -1, std::numeric_limits::max(), + info.genMomentum[0], info.genMomentum[1], info.genMomentum[2], + info.genDecayVertex[0], info.genDecayVertex[1], info.genDecayVertex[2], + info.genCt, info.genPhi, info.genEta, info.genRapidity, + info.genMomProton[0], info.genMomProton[1], info.genMomProton[2], + info.genMomPion[0], info.genMomPion[1], info.genMomPion[2], + info.genMomDeuteron[0], info.genMomDeuteron[1], info.genMomDeuteron[2], + 0, info.motherLabel, info.motherPdgCode, + info.protonPdgCode, info.pionPdgCode, info.deuteronPdgCode, + info.isDeuteronPrimary, static_cast(info.survivedEventSelection)); + } + + template + void fillQAHistograms(TTrack const& trackProton, TTrack const& trackPion, TTrack const& trackDeuteron, bool isSelected) + { + if (!isSelected) { + registry.fill(HIST("hTrackProtonTPCSignal"), trackProton.sign() * trackProton.tpcInnerParam(), trackProton.tpcSignal()); + registry.fill(HIST("hTrackPionTPCSignal"), trackPion.sign() * trackPion.tpcInnerParam(), trackPion.tpcSignal()); + registry.fill(HIST("hTrackDeuteronTPCSignal"), trackDeuteron.sign() * trackDeuteron.tpcInnerParam(), trackDeuteron.tpcSignal()); + } else { + registry.fill(HIST("hTrackProtonTPCSignalSelected"), trackProton.sign() * trackProton.tpcInnerParam(), trackProton.tpcSignal()); + registry.fill(HIST("hTrackPionTPCSignalSelected"), trackPion.sign() * trackPion.tpcInnerParam(), trackPion.tpcSignal()); + registry.fill(HIST("hTrackDeuteronTPCSignalSelected"), trackDeuteron.sign() * trackDeuteron.tpcInnerParam(), trackDeuteron.tpcSignal()); + } + } + + void processData(Collisions const& collisions, + aod::V0s const& /*v0s*/, + aod::Decay3Bodys const& /*decay3Bodys*/, + aod::TrackedV0s const& trackedV0s, + aod::Tracked3Bodys const& tracked3Bodys, + Tracks const& /*tracks*/, + aod::BCsWithTimestamps const&) + { + selectCollisions(collisions, skimmedProcessing); + + for (const auto& trackedV0 : trackedV0s) { + const auto inputV0 = trackedV0.v0_as(); + if (inputV0.collisionId() < 0 || !goodCollision[inputV0.collisionId()] || (skimmedProcessing && !zorroDecision[inputV0.collisionId()][kHe])) { + continue; + } + const auto collision = inputV0.collision_as(); + const auto positiveTrack = inputV0.posTrack_as(); + const auto negativeTrack = inputV0.negTrack_as(); + const float nSigmaPositive = nSigmaHe3(positiveTrack); + const float nSigmaNegative = nSigmaHe3(negativeTrack); + const bool positiveTrackedAsHe = positiveTrack.pidForTracking() == o2::track::PID::Helium3 || positiveTrack.pidForTracking() == o2::track::PID::Alpha; + const bool negativeTrackedAsHe = negativeTrack.pidForTracking() == o2::track::PID::Helium3 || negativeTrack.pidForTracking() == o2::track::PID::Alpha; + const bool positiveIsHe = positiveTrackedAsHe != negativeTrackedAsHe ? positiveTrackedAsHe : nSigmaPositive > nSigmaNegative; + const float selectedNSigmaHe = positiveIsHe ? nSigmaPositive : nSigmaNegative; + if (twoBody.useSelections && selectedNSigmaHe < twoBody.minNSigmaHe) { + continue; + } + const auto heTrack = positiveIsHe ? positiveTrack : negativeTrack; + const auto piTrack = positiveIsHe ? negativeTrack : positiveTrack; + if (twoBody.useSelections && + (std::abs(heTrack.eta()) > twoBody.maxEtaDaughters || std::abs(piTrack.eta()) > twoBody.maxEtaDaughters || + heTrack.tpcNClsFound() < twoBody.minTPCNClsHe || piTrack.tpcNClsFound() < twoBody.minTPCNClsPi || + heTrack.tpcNClsCrossedRows() < twoBody.minTPCCrossedRowsHe || piTrack.tpcNClsCrossedRows() < twoBody.minTPCCrossedRowsPi)) { + continue; + } + const bool heliumPID = heTrack.pidForTracking() == o2::track::PID::Helium3 || heTrack.pidForTracking() == o2::track::PID::Alpha; + const float tpcMomentumHe = heliumPID && twoBody.compensatePIDinTracking ? heTrack.tpcInnerParam() / 2.f : heTrack.tpcInnerParam(); + if (twoBody.useSelections && tpcMomentumHe < twoBody.minTPCInnerParamHe) { + continue; + } + buildTwoBody(heTrack, piTrack, collision, trackedV0.itsClsSize(), [&](auto... values) { dataHypCands(values...); }); + } + + for (const auto& tracked3Body : tracked3Bodys) { + const auto decay3Body = tracked3Body.decay3Body_as(); + if (decay3Body.collisionId() < 0 || !goodCollision[decay3Body.collisionId()] || (skimmedProcessing && !zorroDecision[decay3Body.collisionId()][kTracked3Body])) { + continue; + } + + const auto collision = decay3Body.collision_as(); + const auto trackPositive = decay3Body.track0_as(); + const auto trackNegative = decay3Body.track1_as(); + const auto trackDeuteron = decay3Body.track2_as(); + const auto trackProton = trackDeuteron.sign() > 0 ? trackPositive : trackNegative; + const auto trackPion = trackDeuteron.sign() > 0 ? trackNegative : trackPositive; + + fillQAHistograms(trackProton, trackPion, trackDeuteron, false); + + if (builder3Body.buildDecay3BodyCandidate(collision, trackProton, trackPion, trackDeuteron, + decay3Body.globalIndex(), deuteronTOFNSigma(collision, trackDeuteron), tracked3Body.itsClsSize(), + threeBody.useKFParticle, threeBody.setTopologicalConstraint, + threeBody.useSelections, threeBody.useChi2Selection, threeBody.useTPCforPion, + threeBody.acceptTPCOnly, threeBody.askOnlyITSMatch, threeBody.calculateCovariance)) { + // get DCA of ITS track to SV + std::array dcaInfoItsTrack = getItsTrackDCAToSV(tracked3Body); + builder3Body.decay3body.itsTrackDCAToSV[0] = dcaInfoItsTrack[0]; + builder3Body.decay3body.itsTrackDCAToSV[1] = dcaInfoItsTrack[1]; + if (threeBody.useSelections && (std::abs(builder3Body.decay3body.itsTrackDCAToSV[0]) > threeBody.maxITSDCAxytrackToSV || std::abs(builder3Body.decay3body.itsTrackDCAToSV[1]) > threeBody.maxITSDCAztrackToSV)) { + continue; + } + + fillQAHistograms(trackProton, trackPion, trackDeuteron, true); + fillThreeBodyTables(collision); + } + } + } + + void processMC(CollisionsMC const& collisions, + aod::McCollisions const& mcCollisions, + aod::V0s const& /*v0s*/, + aod::Decay3Bodys const& /*decay3Bodys*/, + aod::TrackedV0s const& trackedV0s, + aod::Tracked3Bodys const& tracked3Bodys, + TracksMC const& /*tracks*/, + aod::BCsWithTimestamps const&, + aod::McParticles const& mcParticles) + { + selectCollisions(collisions, false); + prepareMCEventInformation(collisions, mcCollisions); + std::vector reconstructedTwoBody(mcParticles.size(), false); + std::vector reconstructedThreeBody(mcParticles.size(), false); + + for (const auto& trackedV0 : trackedV0s) { + const auto inputV0 = trackedV0.v0_as(); + if (inputV0.collisionId() < 0 || !goodCollision[inputV0.collisionId()]) { + continue; + } + const auto collision = inputV0.collision_as(); + const auto positiveTrack = inputV0.posTrack_as(); + const auto negativeTrack = inputV0.negTrack_as(); + const float nSigmaPositive = nSigmaHe3(positiveTrack); + const float nSigmaNegative = nSigmaHe3(negativeTrack); + const bool positiveTrackedAsHe = positiveTrack.pidForTracking() == o2::track::PID::Helium3 || positiveTrack.pidForTracking() == o2::track::PID::Alpha; + const bool negativeTrackedAsHe = negativeTrack.pidForTracking() == o2::track::PID::Helium3 || negativeTrack.pidForTracking() == o2::track::PID::Alpha; + const bool positiveIsHe = positiveTrackedAsHe != negativeTrackedAsHe ? positiveTrackedAsHe : nSigmaPositive > nSigmaNegative; + const float selectedNSigmaHe = positiveIsHe ? nSigmaPositive : nSigmaNegative; + if (twoBody.useSelections && selectedNSigmaHe < twoBody.minNSigmaHe) { + continue; + } + const auto heTrack = positiveIsHe ? positiveTrack : negativeTrack; + const auto piTrack = positiveIsHe ? negativeTrack : positiveTrack; + if (twoBody.useSelections && + (std::abs(heTrack.eta()) > twoBody.maxEtaDaughters || std::abs(piTrack.eta()) > twoBody.maxEtaDaughters || + heTrack.tpcNClsFound() < twoBody.minTPCNClsHe || piTrack.tpcNClsFound() < twoBody.minTPCNClsPi || + heTrack.tpcNClsCrossedRows() < twoBody.minTPCCrossedRowsHe || piTrack.tpcNClsCrossedRows() < twoBody.minTPCCrossedRowsPi)) { + continue; + } + const bool heliumPID = heTrack.pidForTracking() == o2::track::PID::Helium3 || heTrack.pidForTracking() == o2::track::PID::Alpha; + const float tpcMomentumHe = heliumPID && twoBody.compensatePIDinTracking ? heTrack.tpcInnerParam() / 2.f : heTrack.tpcInnerParam(); + if (twoBody.useSelections && tpcMomentumHe < twoBody.minTPCInnerParamHe) { + continue; + } + + const auto mcInfo = getTwoBodyMCInfo(heTrack, piTrack, collision, mcParticles); + if (!mcInfo.isSignal && !mc.storeBackground) { + continue; + } + buildTwoBody(heTrack, piTrack, collision, trackedV0.itsClsSize(), [&](auto... values) { + mcHypCands(values..., + mcInfo.genPt, mcInfo.genPhi, mcInfo.genEta, mcInfo.genPtHe3, + mcInfo.genDecayVertex[0], mcInfo.genDecayVertex[1], mcInfo.genDecayVertex[2], + true, mcInfo.fakeHeITSLayerMap, mcInfo.isSignal, + mcInfo.isRecoMCCollision, mcInfo.survivedEventSelection, true, mcInfo.statusCode); + if (mcInfo.isSignal && mcInfo.motherLabel >= 0) { + reconstructedTwoBody[mcInfo.motherLabel] = true; + } + }); + } + + for (const auto& tracked3Body : tracked3Bodys) { + const auto decay3Body = tracked3Body.decay3Body_as(); + if (decay3Body.collisionId() < 0 || !goodCollision[decay3Body.collisionId()]) { + continue; + } + const auto collision = decay3Body.collision_as(); + const auto trackPositive = decay3Body.track0_as(); + const auto trackNegative = decay3Body.track1_as(); + const auto trackDeuteron = decay3Body.track2_as(); + const auto trackProton = trackDeuteron.sign() > 0 ? trackPositive : trackNegative; + const auto trackPion = trackDeuteron.sign() > 0 ? trackNegative : trackPositive; + + fillQAHistograms(trackProton, trackPion, trackDeuteron, false); + + if (!builder3Body.buildDecay3BodyCandidate(collision, trackProton, trackPion, trackDeuteron, + decay3Body.globalIndex(), deuteronTOFNSigmaMC(collision, trackDeuteron), tracked3Body.itsClsSize(), + threeBody.useKFParticle, threeBody.setTopologicalConstraint, + threeBody.useSelections, threeBody.useChi2Selection, threeBody.useTPCforPion, + threeBody.acceptTPCOnly, threeBody.askOnlyITSMatch, threeBody.calculateCovariance)) { + + continue; + } + + fillQAHistograms(trackProton, trackPion, trackDeuteron, true); + const auto mcInfo = getThreeBodyMCInfo(trackProton, trackPion, trackDeuteron, collision, mcParticles); + if (mcInfo.motherLabel < 0 && !mc.storeBackground) { + continue; + } + + // get DCA of ITS track to SV + std::array dcaInfoItsTrack = getItsTrackDCAToSV(tracked3Body); + builder3Body.decay3body.itsTrackDCAToSV[0] = dcaInfoItsTrack[0]; + builder3Body.decay3body.itsTrackDCAToSV[1] = dcaInfoItsTrack[1]; + if (threeBody.useSelections && (std::abs(builder3Body.decay3body.itsTrackDCAToSV[0]) > threeBody.maxITSDCAxytrackToSV || std::abs(builder3Body.decay3body.itsTrackDCAToSV[1]) > threeBody.maxITSDCAztrackToSV)) { + continue; + } + + fillThreeBodyMCTable(mcInfo); + mcVtx3BodyCollision(true, // IsRecoMCCollision + collision.centFT0A(), collision.centFT0C(), collision.centFT0M(), + collision.trackOccupancyInTimeRange(), collision.ft0cOccupancyInTimeRange(), + collision.posX(), collision.posY(), collision.posZ(), + runNumber); + + if (mcInfo.motherLabel >= 0) { + reconstructedThreeBody[mcInfo.motherLabel] = true; + } + } + + for (const auto& mother : mcParticles) { + if (std::abs(mother.pdgCode()) != constants::physics::Pdg::kHyperTriton) { + continue; + } + const int sign = mother.pdgCode() > 0 ? 1 : -1; + bool hasHelium3 = false; + bool hasPionTwoBody = false; + bool hasProton = false; + bool hasPionThreeBody = false; + bool hasDeuteron = false; + std::array heliumMomentum{}; + std::array twoBodyDecayVertex{}; + ThreeBodyMCInfo threeBodyInfo; + threeBodyInfo.isReco = false; + threeBodyInfo.motherLabel = mother.globalIndex(); + threeBodyInfo.motherPdgCode = mother.pdgCode(); + threeBodyInfo.genMomentum = {mother.px(), mother.py(), mother.pz()}; + threeBodyInfo.genPhi = mother.phi(); + threeBodyInfo.genEta = mother.eta(); + threeBodyInfo.genRapidity = mother.y(); + if (mother.mcCollisionId() >= 0 && mother.mcCollisionId() < static_cast(survivedMCEventSelection.size())) { + threeBodyInfo.survivedEventSelection = survivedMCEventSelection[mother.mcCollisionId()]; + } + int twoBodyStatusCode = 0; + + for (const auto& daughter : mother.daughters_as()) { + if (daughter.pdgCode() == sign * constants::physics::Pdg::kHelium3) { + hasHelium3 = true; + heliumMomentum = {daughter.px(), daughter.py(), daughter.pz()}; + twoBodyDecayVertex = {daughter.vx(), daughter.vy(), daughter.vz()}; + twoBodyStatusCode = daughter.getProcess(); + } else if (daughter.pdgCode() == -sign * PDG_t::kPiPlus) { + hasPionTwoBody = true; + hasPionThreeBody = true; + threeBodyInfo.pionPdgCode = daughter.pdgCode(); + threeBodyInfo.genMomPion = {daughter.px(), daughter.py(), daughter.pz()}; + } else if (daughter.pdgCode() == sign * PDG_t::kProton) { + hasProton = true; + threeBodyInfo.protonPdgCode = daughter.pdgCode(); + threeBodyInfo.genMomProton = {daughter.px(), daughter.py(), daughter.pz()}; + threeBodyInfo.genDecayVertex = {daughter.vx(), daughter.vy(), daughter.vz()}; + } else if (daughter.pdgCode() == sign * constants::physics::Pdg::kDeuteron) { + hasDeuteron = true; + threeBodyInfo.deuteronPdgCode = daughter.pdgCode(); + threeBodyInfo.genMomDeuteron = {daughter.px(), daughter.py(), daughter.pz()}; + threeBodyInfo.isDeuteronPrimary = daughter.isPhysicalPrimary(); + } + } + + if (hasHelium3 && hasPionTwoBody && !reconstructedTwoBody[mother.globalIndex()]) { + float centralityFT0A = -1.f; + float centralityFT0C = -1.f; + float centralityFT0M = -1.f; + int trackOccupancyInTimeRange = -1; + float ft0cOccupancyInTimeRange = -1.f; + float primaryVertexX = -1.f; + float primaryVertexY = -1.f; + float primaryVertexZ = -1.f; + bool isRecoMCCollision = false; + bool survivedEventSelection = false; + if (mother.mcCollisionId() >= 0 && mother.mcCollisionId() < static_cast(recoCollisionForMC.size())) { + const int recoCollisionId = recoCollisionForMC[mother.mcCollisionId()]; + isRecoMCCollision = recoCollisionId >= 0; + survivedEventSelection = survivedMCEventSelection[mother.mcCollisionId()]; + if (isRecoMCCollision) { + const auto collision = collisions.rawIteratorAt(recoCollisionId); + centralityFT0A = collision.centFT0A(); + centralityFT0C = collision.centFT0C(); + centralityFT0M = collision.centFT0M(); + trackOccupancyInTimeRange = collision.trackOccupancyInTimeRange(); + ft0cOccupancyInTimeRange = collision.ft0cOccupancyInTimeRange(); + primaryVertexX = collision.posX(); + primaryVertexY = collision.posY(); + primaryVertexZ = collision.posZ(); + } + } + mcHypCands(centralityFT0A, centralityFT0C, centralityFT0M, + trackOccupancyInTimeRange, ft0cOccupancyInTimeRange, + primaryVertexX, primaryVertexY, primaryVertexZ, + runNumber, mother.pdgCode() > 0, + -1.f, -1.f, -1.f, + -1.f, -1.f, -1.f, + -1.f, -1.f, -1.f, + -1.f, -1.f, -1.f, + -1.f, -1, -1, -1, -1, -1, -1, + -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, + 0u, 0u, 0u, -1, + sign * mother.pt(), mother.phi(), mother.eta(), std::hypot(heliumMomentum[0], heliumMomentum[1]), + twoBodyDecayVertex[0] - mother.vx(), twoBodyDecayVertex[1] - mother.vy(), twoBodyDecayVertex[2] - mother.vz(), + false, 0u, true, isRecoMCCollision, survivedEventSelection, true, twoBodyStatusCode); + } + + if (hasProton && hasPionThreeBody && hasDeuteron && !reconstructedThreeBody[mother.globalIndex()]) { + float centralityFT0A = -1.f; + float centralityFT0C = -1.f; + float centralityFT0M = -1.f; + int trackOccupancyInTimeRange = -1; + float ft0cOccupancyInTimeRange = -1.f; + float primaryVertexX = -1.f; + float primaryVertexY = -1.f; + float primaryVertexZ = -1.f; + bool isRecoMCCollision = false; + if (mother.mcCollisionId() >= 0 && mother.mcCollisionId() < static_cast(recoCollisionForMC.size())) { + const int recoCollisionId = recoCollisionForMC[mother.mcCollisionId()]; + isRecoMCCollision = recoCollisionId >= 0; + if (isRecoMCCollision) { + const auto collision = collisions.rawIteratorAt(recoCollisionId); + centralityFT0A = collision.centFT0A(); + centralityFT0C = collision.centFT0C(); + centralityFT0M = collision.centFT0M(); + trackOccupancyInTimeRange = collision.trackOccupancyInTimeRange(); + ft0cOccupancyInTimeRange = collision.ft0cOccupancyInTimeRange(); + primaryVertexX = collision.posX(); + primaryVertexY = collision.posY(); + primaryVertexZ = collision.posZ(); + } + } + threeBodyInfo.genCt = RecoDecay::sqrtSumOfSquares(threeBodyInfo.genDecayVertex[0] - mother.vx(), + threeBodyInfo.genDecayVertex[1] - mother.vy(), + threeBodyInfo.genDecayVertex[2] - mother.vz()) * + constants::physics::MassHyperTriton / (mother.p() + 1.e-10f); + fillGeneratedThreeBodyMCTable(threeBodyInfo); + mcVtx3BodyCollision(isRecoMCCollision, + centralityFT0A, centralityFT0C, centralityFT0M, + trackOccupancyInTimeRange, ft0cOccupancyInTimeRange, + primaryVertexX, primaryVertexY, primaryVertexZ, + runNumber); + } + } + } + PROCESS_SWITCH(TrackedHypertritonRecoTask, processData, "Process tracked hypertriton candidates in data", true); + PROCESS_SWITCH(TrackedHypertritonRecoTask, processMC, "Process tracked hypertriton candidates in Monte Carlo", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + metadataInfo.initMetadata(cfgc); + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/QC/flowQC.cxx b/PWGLF/TableProducer/QC/flowQC.cxx index 2d590e0df69..6c9606bdef2 100644 --- a/PWGLF/TableProducer/QC/flowQC.cxx +++ b/PWGLF/TableProducer/QC/flowQC.cxx @@ -40,17 +40,15 @@ #include #include #include +#include #include -#include #include #include #include #include -#include - #include #include #include @@ -89,8 +87,6 @@ enum methods { }; static const std::vector suffixes = {"EP", "Qvec"}; -std::shared_ptr hQxQy[kNmethods][kNqVecDetectors]; -std::shared_ptr hNormQxQy[kNmethods][kNqVecDetectors]; std::shared_ptr hPsi[kNmethods][kNqVecDetectors]; std::shared_ptr hDeltaPsi[kNmethods][kNqVecDetectors][kNqVecDetectors]; std::shared_ptr hScalarProduct[kNmethods][kNqVecDetectors][kNqVecDetectors]; @@ -122,6 +118,7 @@ struct flowQC { float mBz = 0.f; Configurable cfgHarmonic{"cfgHarmonic", 2.f, "Harmonics for flow analysis"}; + Configurable cfgQuadraticResponse{"cfgQuadraticResponse", false, "Use quadratic response for Q-vector quantities"}; // Flow analysis using CollWithEPandQvec = soa::Join -cfgCutVertex && collision.posZ() < cfgCutVertex && collision.selection_bit(aod::evsel::kNoTimeFrameBorder) && collision.triggereventep() && collision.selection_bit(aod::evsel::kNoSameBunchPileup); } - float computeEventPlane(float y, float x) + float computeEventPlane(float y, float x, float harmonic) { - return 0.5 * TMath::ATan2(y, x); + return (1.f / harmonic) * TMath::ATan2(y, x); } void initCCDB(aod::BCsWithTimestamps::iterator const& bc) @@ -183,11 +180,7 @@ struct flowQC { const AxisSpec centAxis{cfgCentralityBins, fmt::format("{} percentile", (std::string)centDetectorNames[cfgCentralityEstimator])}; - const AxisSpec QxAxis{cfgQvecBins, Form("Q_{%.0f,x}", cfgHarmonic.value)}; - const AxisSpec QyAxis{cfgQvecBins, Form("Q_{%.0f,y}", cfgHarmonic.value)}; - - const AxisSpec NormQxAxis{cfgQvecBins, Form("#frac{Q_{%.0f,x}}{||#vec{Q_{%.0f}}||}", cfgHarmonic.value, cfgHarmonic.value)}; - const AxisSpec NormQyAxis{cfgQvecBins, Form("#frac{Q_{%.0f,y}}{||#vec{Q_{%.0f}}||}", cfgHarmonic.value, cfgHarmonic.value)}; + const char* qLabel = cfgQuadraticResponse ? "Q^{2}" : "Q"; const AxisSpec psiAxis{cfgPhiBins, Form("#psi_{%.0f}", cfgHarmonic.value)}; const AxisSpec psiCompAxis{cfgPhiBins, Form("#psi_{%.0f}^{EP} - #psi_{%.0f}^{Qvec}", cfgHarmonic.value, cfgHarmonic.value)}; @@ -206,8 +199,6 @@ struct flowQC { HistogramRegistry* registry = (iMethod == methods::kEP) ? &flow_ep : &flow_qvec; for (int iQvecDet = 0; iQvecDet < qVecDetectors::kNqVecDetectors; iQvecDet++) { - hQxQy[iMethod][iQvecDet] = registry->add(Form("hQxQy_%s_%s", qVecDetectorNames[iQvecDet].c_str(), suffixes[iMethod].c_str()), "", HistType::kTH3F, {centAxis, QxAxis, QyAxis}); - hNormQxQy[iMethod][iQvecDet] = registry->add(Form("hNormQxQy_%s_%s", qVecDetectorNames[iQvecDet].c_str(), suffixes[iMethod].c_str()), "", HistType::kTH3F, {centAxis, NormQxAxis, NormQyAxis}); hPsi[iMethod][iQvecDet] = registry->add(Form("hPsi_%s_%s", qVecDetectorNames[iQvecDet].c_str(), suffixes[iMethod].c_str()), "", HistType::kTH2F, {centAxis, psiAxis}); for (int jQvecDet = iQvecDet + 1; jQvecDet < qVecDetectors::kNqVecDetectors; jQvecDet++) { @@ -215,12 +206,12 @@ struct flowQC { hDeltaPsi[iMethod][iQvecDet][jQvecDet] = registry->add(Form("hDeltaPsi_%s_%s_%s", qVecDetectorNames[iQvecDet].c_str(), qVecDetectorNames[jQvecDet].c_str(), suffixes[iMethod].c_str()), "", HistType::kTH2F, {centAxis, {cfgDeltaPhiBins, Form("#psi_{%s} - #psi_{%s}", qVecDetectorNames[iQvecDet].c_str(), qVecDetectorNames[jQvecDet].c_str())}}); // Scalar-product histograms - auto spLabel = Form("#vec{Q}_{%.0f}^{%s} #upoint #vec{Q}_{%.0f}^{%s}", cfgHarmonic.value, qVecDetectorNames[iQvecDet].c_str(), cfgHarmonic.value, qVecDetectorNames[jQvecDet].c_str()); + auto spLabel = Form("#vec{%s}_{%.0f}^{%s} #upoint #vec{%s}_{%.0f}^{%s}", qLabel, cfgHarmonic.value, qVecDetectorNames[iQvecDet].c_str(), qLabel, cfgHarmonic.value, qVecDetectorNames[jQvecDet].c_str()); hScalarProduct[iMethod][iQvecDet][jQvecDet] = registry->add(Form("hScalarProduct_%s_%s_%s", qVecDetectorNames[iQvecDet].c_str(), qVecDetectorNames[jQvecDet].c_str(), suffixes[iMethod].c_str()), "", HistType::kTH2F, {centAxis, {cfgQvecBins, spLabel}}); // Normalised scalar-product histograms - auto normSpLabel = Form("#frac{#vec{Q}_{%.0f}^{%s} #upoint #vec{Q}_{%.0f}^{%s}}{||#vec{Q}_{%.0f}^{%s}|| ||#vec{Q}_{%.0f}^{%s}||}", cfgHarmonic.value, qVecDetectorNames[iQvecDet].c_str(), cfgHarmonic.value, qVecDetectorNames[jQvecDet].c_str(), cfgHarmonic.value, qVecDetectorNames[iQvecDet].c_str(), cfgHarmonic.value, qVecDetectorNames[jQvecDet].c_str()); + auto normSpLabel = Form("#frac{#vec{%s}_{%.0f}^{%s} #upoint #vec{%s}_{%.0f}^{%s}}{||#vec{%s}_{%.0f}^{%s}|| ||#vec{%s}_{%.0f}^{%s}||}", qLabel, cfgHarmonic.value, qVecDetectorNames[iQvecDet].c_str(), qLabel, cfgHarmonic.value, qVecDetectorNames[jQvecDet].c_str(), qLabel, cfgHarmonic.value, qVecDetectorNames[iQvecDet].c_str(), qLabel, cfgHarmonic.value, qVecDetectorNames[jQvecDet].c_str()); hNormalisedScalarProduct[iMethod][iQvecDet][jQvecDet] = registry->add(Form("hNormalisedScalarProduct_%s_%s_%s", qVecDetectorNames[iQvecDet].c_str(), qVecDetectorNames[jQvecDet].c_str(), suffixes[iMethod].c_str()), "", HistType::kTH2F, {centAxis, {cfgQvecBins, normSpLabel}}); } @@ -270,74 +261,66 @@ struct flowQC { float centrality = getCentrality(collision); + auto maybeSquare = [this](float value) { + return cfgQuadraticResponse ? value * value : value; + }; + const float qvecHarmonic = cfgQuadraticResponse ? (cfgHarmonic.value / 2.f) : cfgHarmonic.value; + const int qvecHarmonicIndex = static_cast(qvecHarmonic) - 2; + // EP method - float QmodFT0A_EP = collision.qFT0A(); + float QmodFT0A_EP = maybeSquare(collision.qFT0A()); float psiFT0A_EP = collision.psiFT0A(); - float QxFT0A_EP = QmodFT0A_EP * std::cos(cfgHarmonic.value * psiFT0A_EP); - float QyFT0A_EP = QmodFT0A_EP * std::sin(cfgHarmonic.value * psiFT0A_EP); - float QmodFT0C_EP = collision.qFT0C(); + float QmodFT0C_EP = maybeSquare(collision.qFT0C()); float psiFT0C_EP = collision.psiFT0C(); - float QxFT0C_EP = QmodFT0C_EP * std::cos(cfgHarmonic.value * psiFT0C_EP); - float QyFT0C_EP = QmodFT0C_EP * std::sin(cfgHarmonic.value * psiFT0C_EP); - float QmodTPCl_EP = collision.qTPCL(); + float QmodTPCl_EP = maybeSquare(collision.qTPCL()); float psiTPCl_EP = collision.psiTPCL(); - float QxTPCl_EP = QmodTPCl_EP * std::cos(cfgHarmonic.value * psiTPCl_EP); - float QyTPCl_EP = QmodTPCl_EP * std::sin(cfgHarmonic.value * psiTPCl_EP); - float QmodTPCr_EP = collision.qTPCR(); + float QmodTPCr_EP = maybeSquare(collision.qTPCR()); float psiTPCr_EP = collision.psiTPCR(); - float QxTPCr_EP = QmodTPCr_EP * std::cos(cfgHarmonic.value * psiTPCr_EP); - float QyTPCr_EP = QmodTPCr_EP * std::sin(cfgHarmonic.value * psiTPCr_EP); - float QmodTPC_EP = collision.qTPC(); + float QmodTPC_EP = maybeSquare(collision.qTPC()); float psiTPC_EP = collision.psiTPC(); - float QxTPC_EP = QmodTPC_EP * std::cos(cfgHarmonic.value * psiTPC_EP); - float QyTPC_EP = QmodTPC_EP * std::sin(cfgHarmonic.value * psiTPC_EP); // Qvec method - float QxFT0A_Qvec = collision.qvecFT0AReVec()[cfgHarmonic.value - 2]; - float QyFT0A_Qvec = collision.qvecFT0AImVec()[cfgHarmonic.value - 2]; - float QmodFT0A_Qvec = std::hypot(QxFT0A_Qvec, QyFT0A_Qvec); - float psiFT0A_Qvec = computeEventPlane(QyFT0A_Qvec, QxFT0A_Qvec); - - float QxFT0C_Qvec = collision.qvecFT0CReVec()[cfgHarmonic.value - 2]; - float QyFT0C_Qvec = collision.qvecFT0CImVec()[cfgHarmonic.value - 2]; - float QmodFT0C_Qvec = std::hypot(QxFT0C_Qvec, QyFT0C_Qvec); - float psiFT0C_Qvec = computeEventPlane(QyFT0C_Qvec, QxFT0C_Qvec); - - float QxTPCl_Qvec = collision.qvecTPCnegReVec()[cfgHarmonic.value - 2]; - float QyTPCl_Qvec = collision.qvecTPCnegImVec()[cfgHarmonic.value - 2]; - float QmodTPCl_Qvec = std::hypot(QxTPCl_Qvec, QyTPCl_Qvec); - float psiTPCl_Qvec = computeEventPlane(QyTPCl_Qvec, QxTPCl_Qvec); - - float QxTPCr_Qvec = collision.qvecTPCposReVec()[cfgHarmonic.value - 2]; - float QyTPCr_Qvec = collision.qvecTPCposImVec()[cfgHarmonic.value - 2]; - float QmodTPCr_Qvec = std::hypot(QxTPCr_Qvec, QyTPCr_Qvec); - float psiTPCr_Qvec = computeEventPlane(QyTPCr_Qvec, QxTPCr_Qvec); - - float QxTPC_Qvec = collision.qvecTPCallReVec()[cfgHarmonic.value - 2]; - float QyTPC_Qvec = collision.qvecTPCallImVec()[cfgHarmonic.value - 2]; - float QmodTPC_Qvec = std::hypot(QxTPC_Qvec, QyTPC_Qvec); - float psiTPC_Qvec = computeEventPlane(QyTPC_Qvec, QxTPC_Qvec); - - std::array vec_Qx[2] = {{QxFT0C_EP, QxFT0A_EP, QxTPCl_EP, QxTPCr_EP, QxTPC_EP}, {QxFT0C_Qvec, QxFT0A_Qvec, QxTPCl_Qvec, QxTPCr_Qvec, QxTPC_Qvec}}; - std::array vec_Qy[2] = {{QyFT0C_EP, QyFT0A_EP, QyTPCl_EP, QyTPCr_EP, QyTPC_EP}, {QyFT0C_Qvec, QyFT0A_Qvec, QyTPCl_Qvec, QyTPCr_Qvec, QyTPC_Qvec}}; + float QxFT0A_Qvec_raw = collision.qvecFT0AReVec()[qvecHarmonicIndex]; + float QyFT0A_Qvec_raw = collision.qvecFT0AImVec()[qvecHarmonicIndex]; + float psiFT0A_Qvec = computeEventPlane(QyFT0A_Qvec_raw, QxFT0A_Qvec_raw, qvecHarmonic); + float QmodFT0A_Qvec = maybeSquare(std::hypot(QxFT0A_Qvec_raw, QyFT0A_Qvec_raw)); + + float QxFT0C_Qvec_raw = collision.qvecFT0CReVec()[qvecHarmonicIndex]; + float QyFT0C_Qvec_raw = collision.qvecFT0CImVec()[qvecHarmonicIndex]; + float psiFT0C_Qvec = computeEventPlane(QyFT0C_Qvec_raw, QxFT0C_Qvec_raw, qvecHarmonic); + float QmodFT0C_Qvec = maybeSquare(std::hypot(QxFT0C_Qvec_raw, QyFT0C_Qvec_raw)); + + float QxTPCl_Qvec_raw = collision.qvecTPCnegReVec()[qvecHarmonicIndex]; + float QyTPCl_Qvec_raw = collision.qvecTPCnegImVec()[qvecHarmonicIndex]; + float psiTPCl_Qvec = computeEventPlane(QyTPCl_Qvec_raw, QxTPCl_Qvec_raw, qvecHarmonic); + float QmodTPCl_Qvec = maybeSquare(std::hypot(QxTPCl_Qvec_raw, QyTPCl_Qvec_raw)); + + float QxTPCr_Qvec_raw = collision.qvecTPCposReVec()[qvecHarmonicIndex]; + float QyTPCr_Qvec_raw = collision.qvecTPCposImVec()[qvecHarmonicIndex]; + float psiTPCr_Qvec = computeEventPlane(QyTPCr_Qvec_raw, QxTPCr_Qvec_raw, qvecHarmonic); + float QmodTPCr_Qvec = maybeSquare(std::hypot(QxTPCr_Qvec_raw, QyTPCr_Qvec_raw)); + + float QxTPC_Qvec_raw = collision.qvecTPCallReVec()[qvecHarmonicIndex]; + float QyTPC_Qvec_raw = collision.qvecTPCallImVec()[qvecHarmonicIndex]; + float psiTPC_Qvec = computeEventPlane(QyTPC_Qvec_raw, QxTPC_Qvec_raw, qvecHarmonic); + float QmodTPC_Qvec = maybeSquare(std::hypot(QxTPC_Qvec_raw, QyTPC_Qvec_raw)); + std::array vec_Qmod[2] = {{QmodFT0C_EP, QmodFT0A_EP, QmodTPCl_EP, QmodTPCr_EP, QmodTPC_EP}, {QmodFT0C_Qvec, QmodFT0A_Qvec, QmodTPCl_Qvec, QmodTPCr_Qvec, QmodTPC_Qvec}}; std::array vec_Qpsi[2] = {{psiFT0C_EP, psiFT0A_EP, psiTPCl_EP, psiTPCr_EP, psiTPC_EP}, {psiFT0C_Qvec, psiFT0A_Qvec, psiTPCl_Qvec, psiTPCr_Qvec, psiTPC_Qvec}}; for (int iMethod = 0; iMethod < methods::kNmethods; iMethod++) { for (int iQvecDet = 0; iQvecDet < qVecDetectors::kNqVecDetectors; iQvecDet++) { - hQxQy[iMethod][iQvecDet]->Fill(centrality, vec_Qx[iMethod][iQvecDet], vec_Qy[iMethod][iQvecDet]); - hNormQxQy[iMethod][iQvecDet]->Fill(centrality, vec_Qx[iMethod][iQvecDet] / vec_Qmod[iMethod][iQvecDet], vec_Qy[iMethod][iQvecDet] / vec_Qmod[iMethod][iQvecDet]); hPsi[iMethod][iQvecDet]->Fill(centrality, vec_Qpsi[iMethod][iQvecDet]); for (int jQvecDet = iQvecDet + 1; jQvecDet < qVecDetectors::kNqVecDetectors; jQvecDet++) { // Q-vector azimuthal-angle differences hDeltaPsi[iMethod][iQvecDet][jQvecDet]->Fill(centrality, vec_Qpsi[iMethod][iQvecDet] - vec_Qpsi[iMethod][jQvecDet]); // Scalar-product histograms auto getSP = [&](int iDet1, int iDet2) { - return vec_Qx[iMethod][iDet1] * vec_Qx[iMethod][iDet2] + vec_Qy[iMethod][iDet1] * vec_Qy[iMethod][iDet2]; + return vec_Qmod[iMethod][iDet1] * vec_Qmod[iMethod][iDet2] * std::cos(cfgHarmonic.value * (vec_Qpsi[iMethod][iDet1] - vec_Qpsi[iMethod][iDet2])); }; hScalarProduct[iMethod][iQvecDet][jQvecDet]->Fill(centrality, getSP(iQvecDet, jQvecDet)); // Normalised scalar-product histograms diff --git a/PWGLF/TableProducer/QC/nucleiQC.cxx b/PWGLF/TableProducer/QC/nucleiQC.cxx index db4031b010a..c9c417b08eb 100644 --- a/PWGLF/TableProducer/QC/nucleiQC.cxx +++ b/PWGLF/TableProducer/QC/nucleiQC.cxx @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -51,16 +52,15 @@ #include -#include - -#include #include #include #include #include +#include #include #include #include +#include #include using namespace o2; @@ -78,10 +78,11 @@ enum trackQuality { kNCrossedRowsCut = 5, kTpcChi2Cut = 6, kItsChi2Cut = 7, - kNtrackQuality = 8 + kDcaCuts = 8, + kNtrackQuality = 9 }; -std::array trackQualityLabels{"All", "#eta cut", "#it{p}_{TPC}^{min} cut", "#it{N}_{cls}^{ITS} cut", "#it{N}_{cls}^{TPC} cut", "Crossed rows cut", "#chi^{2}_{TPC} cut", "#chi^{2}_{ITS} cut"}; +std::array trackQualityLabels{"All", "#eta cut", "#it{p}_{TPC}^{min} cut", "#it{N}_{cls}^{ITS} cut", "#it{N}_{cls}^{TPC} cut", "Crossed rows cut", "#chi^{2}_{TPC} cut", "#chi^{2}_{ITS} cut", "DCA cuts"}; } // namespace @@ -96,9 +97,10 @@ struct nucleiQC { Configurable cfgFillTable{"cfgFillTable", true, "Fill output tree"}; Configurable cfgDoCheckPdgCode{"cfgDoCheckPdgCode", true, "Should you only select tracks associated to a mc particle with the correct PDG code?"}; + Configurable cfgSkipNonReconstructedCollisions{"cfgSkipNonReconstructedCollisions", true, "Should you skip collisions for which no particle is reconstructed?"}; Configurable cfgFillOnlyPhysicalPrimaries{"cfgFillOnlyPhysicalPrimaries", true, "Should you only select physical primary particles?"}; Configurable> cfgSpeciesToProcess{"cfgSpeciesToProcess", {nuclei::speciesToProcessDefault[0], nuclei::Species::kNspecies, 1, nuclei::names, {"processNucleus"}}, "Nuclei to process"}; - Configurable> cfgEventSelections{"cfgEventSelections", {nuclei::EvSelDefault[0], 8, 1, nuclei::eventSelectionLabels, nuclei::eventSelectionTitle}, "Event selections"}; + Configurable> cfgEventSelections{"cfgEventSelections", {nuclei::EvSelDefault[0], nuclei::evSel::kNevSels, 1, nuclei::eventSelectionLabels, nuclei::eventSelectionTitle}, "Event selections"}; Configurable cfgCentralityEstimator{"cfgCentralityEstimator", 0, "Centrality estimator (FV0A: 0, FT0M: 1, FT0A: 2, FT0C: 3)"}; Configurable cfgPerformPidSelectionInIts{"cfgPerformPidSelectionInIts", false, "Perform PID selections in ITS"}; Configurable cfgPerformPidSelectionInTpc{"cfgPerformPidSelectionInTpc", false, "Perform PID selections in TPC"}; @@ -125,6 +127,8 @@ struct nucleiQC { Configurable cfgCutNclusCrossedRowsTPC{"cfgCutNclusCrossedRowsTPC", 70, "Minimum number of TPC clusters crossed rows"}; Configurable cfgCutChi2PerClusterTPC{"cfgCutChi2PerClusterTPC", 4.f, "Maximum chi2 per TPC cluster"}; Configurable cfgCutChi2PerClusterITS{"cfgCutChi2PerClusterITS", 36.f, "Maximum chi2 per ITS cluster"}; + Configurable cfgCutDCAxy{"cfgCutDCAxy", 10.f, "Maximum DCA in the transverse plane"}; + Configurable cfgCutDCAz{"cfgCutDCAz", 10.f, "Maximum DCA in the longitudinal direction"}; Configurable> cfgNsigmaTPC{"cfgNsigmaTPC", {nuclei::nSigmaTPCdefault[0], nuclei::Species::kNspecies, 2, nuclei::names, nuclei::nSigmaConfigName}, "TPC nsigma selection for light nuclei"}; Configurable> cfgNsigmaTOF{"cfgNsigmaTOF", {nuclei::nSigmaTOFdefault[0], nuclei::Species::kNspecies, 2, nuclei::names, nuclei::nSigmaConfigName}, "TPC nsigma selection for light nuclei"}; @@ -142,6 +146,7 @@ struct nucleiQC { {"hEventSelections", "Event selections; Selection step; Counts", {HistType::kTH1D, {{nuclei::evSel::kNevSels + 1, -0.5f, static_cast(nuclei::evSel::kNevSels) + 0.5f}}}}, {"hVtxZBefore", "Vertex distribution in Z before selections;Z (cm)", {HistType::kTH1F, {{400, -20.0, 20.0}}}}, {"hVtxZ", "Vertex distribution in Z;Z (cm)", {HistType::kTH1F, {{400, -20.0, 20.0}}}}, + {"hCentrality", "Centrality distribution;Centrality (%)", {HistType::kTH1F, {{100, 0.0, 100.0}}}}, {"hFailCentrality", "0: all the times the centrality filling function is called - 1: each time it fails ; Bool", {HistType::kTH1F, {{2, -0.5, 1.50}}}}, {"hTrackTunedTracks", "", {HistType::kTH1F, {{1, 0.5, 1.5}}}}, }, @@ -152,8 +157,10 @@ struct nucleiQC { std::shared_ptr mHistTrackTunedTracks = mHistograms.get(HIST("hTrackTunedTracks")); std::vector mSpeciesToProcess; + std::array mFillSpecies{false}; Produces mNucleiTableRed; Produces mNucleiTableExt; + Produces mNucleiTableMat; std::vector mNucleiCandidates; std::vector mFilledMcParticleIds; @@ -165,6 +172,7 @@ struct nucleiQC { o2::dataformats::VertexBase mVtx; o2::track::TrackParametrizationWithError mTrackParCov; std::array(nuclei::Species::kNspecies)> mPidManagers; + bool mAnyTrackTuner = false; void init(o2::framework::InitContext&) { @@ -173,22 +181,24 @@ struct nucleiQC { mCcdb->setCaching(true); mCcdb->setLocalObjectValidityChecking(); mCcdb->setFatalWhenNull(false); - nuclei::lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(mCcdb->get("GLO/Param/MatLUT")); + // nuclei::lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(mCcdb->get("GLO/Param/MatLUT")); for (int iSel = 0; iSel < nuclei::evSel::kNevSels; iSel++) { mHistograms.get(HIST("hEventSelections"))->GetXaxis()->SetBinLabel(iSel + 1, nuclei::eventSelectionLabels[iSel].c_str()); } for (int iSpecies = 0; iSpecies < static_cast(nuclei::Species::kNspecies); iSpecies++) { - if (cfgSpeciesToProcess->get(iSpecies) == 1) + if (cfgSpeciesToProcess->get(iSpecies) == 1) { mSpeciesToProcess.emplace_back(iSpecies); + mFillSpecies[iSpecies] = true; + } } static_for<0, nuclei::kNspecies - 1>([&](auto iSpecies) { constexpr int kSpeciesCt = decltype(iSpecies)::value; const int kSpeciesRt = kSpeciesCt; - if (std::find(mSpeciesToProcess.begin(), mSpeciesToProcess.end(), kSpeciesCt) == mSpeciesToProcess.end()) + if (!mFillSpecies[kSpeciesCt]) return; float tpcBetheBlochParams[6]; @@ -198,6 +208,7 @@ struct nucleiQC { nuclei::createHistogramRegistryNucleus(mHistograms); mHistograms.add(fmt::format("{}/hTrackQuality", nuclei::cNames[kSpeciesRt]).c_str(), (fmt::format("{} track quality;", nuclei::cNames[kSpeciesRt]) + std::string("#it{p}_{T} / #it{Z} (GeV/#it{c}); Selection step; Counts")).c_str(), o2::framework::HistType::kTH2D, {{400, -10.0f, 10.0f}, {trackQuality::kNtrackQuality, -0.5f, static_cast(trackQuality::kNtrackQuality) - 0.5f}}); + mHistograms.add(fmt::format("{}/h2Productionvertex", nuclei::cNames[kSpeciesRt]).c_str(), (fmt::format("{} production vertex;", nuclei::cNames[kSpeciesRt]) + std::string("#it{x} (cm); #it{y} (cm); Counts")).c_str(), o2::framework::HistType::kTH2D, {{400, -100.0f, 100.0f}, {400, -100.0f, 100.0f}}); for (size_t iSel = 0; iSel < trackQuality::kNtrackQuality; iSel++) { mHistograms.get(HIST(nuclei::cNames[kSpeciesRt]) + HIST("/hTrackQuality"))->GetYaxis()->SetBinLabel(iSel + 1, trackQualityLabels[iSel].c_str()); } @@ -210,12 +221,12 @@ struct nucleiQC { }); /// TrackTuner initialization - bool anyTrackTuner = false; + mAnyTrackTuner = false; for (int iSpecies = 0; iSpecies < static_cast(nuclei::Species::kNspecies); iSpecies++) { - anyTrackTuner = anyTrackTuner || cfgUseTrackTuner->get(iSpecies); + mAnyTrackTuner = mAnyTrackTuner || cfgUseTrackTuner->get(iSpecies); } - if (anyTrackTuner) { + if (mAnyTrackTuner) { std::string outputStringParams = ""; switch (cfgTrackTunerConfigSource) { case aod::track_tuner::InputString: @@ -247,12 +258,16 @@ struct nucleiQC { o2::parameters::GRPMagField* grpmag = mCcdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); o2::base::Propagator::initFieldFromGRP(grpmag); - o2::base::Propagator::Instance()->setMatLUT(nuclei::lut); + // o2::base::Propagator::Instance()->setMatLUT(nuclei::lut); + if (!o2::base::Propagator::Instance()->getMatLUT()) { + nuclei::lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(mCcdb->get("GLO/Param/MatLUT")); + o2::base::Propagator::Instance()->setMatLUT(nuclei::lut); + } mBz = static_cast(grpmag->getNominalL3Field()); LOGF(info, "Retrieved GRP for timestamp %ull (%i) with magnetic field of %1.2f kZG", timestamp, mRunNumber, mBz); } - template + template bool trackSelection(const Ttrack& track) { mHistograms.fill(HIST(nuclei::cNames[iSpecies]) + HIST("/hTrackQuality"), track.sign() * track.pt(), trackQuality::kNoCuts); @@ -285,6 +300,27 @@ struct nucleiQC { return false; mHistograms.fill(HIST(nuclei::cNames[iSpecies]) + HIST("/hTrackQuality"), track.sign() * track.pt(), trackQuality::kItsChi2Cut); + mDcaInfoCov.set(999, 999, 999, 999, 999); + setTrackParCov(track, mTrackParCov); + mTrackParCov.setPID(track.pidForTracking()); + + if constexpr (isMc) { + if (track.has_mcParticle() && cfgUseTrackTuner->get(iSpecies)) { + const auto& particle = track.mcParticle(); + mHistTrackTunedTracks->Fill(1.); + mTrackTuner.tuneTrackParams(particle, mTrackParCov, mMatCorr, &mDcaInfoCov, mHistTrackTunedTracks); + } + } else { + mMatCorr = static_cast(cfgMaterialCorrection.value); + } + + o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, mTrackParCov, 2.f, mMatCorr, &mDcaInfoCov); + mDcaInfo[0] = mDcaInfoCov.getY(); + mDcaInfo[1] = mDcaInfoCov.getZ(); + if (std::abs(mDcaInfo[0]) > cfgCutDCAxy || std::abs(mDcaInfo[1]) > cfgCutDCAz) + return false; + mHistograms.fill(HIST(nuclei::cNames[iSpecies]) + HIST("/hTrackQuality"), track.sign() * track.pt(), trackQuality::kDcaCuts); + return true; } @@ -293,7 +329,7 @@ struct nucleiQC { { constexpr int kIndex = iSpecies; if (!nuclei::checkSpeciesValidity(kIndex)) - std::runtime_error("species contains invalid nucleus kIndex"); + throw std::runtime_error("species contains invalid nucleus kIndex"); float centrality = nuclei::getCentrality(collision, cfgCentralityEstimator); float nsigmaTPC = mPidManagers[kIndex].getNSigmaTPC(track); @@ -316,7 +352,7 @@ struct nucleiQC { return true; } - template + template void fillNucleusFlagsPdgsMc(const Tparticle& particle, nuclei::SlimCandidate& candidate) { candidate.pdgCode = particle.pdgCode(); @@ -329,7 +365,7 @@ struct nucleiQC { } if (particle.isPhysicalPrimary()) { - candidate.flags |= nuclei::Flags::kIsPhysicalPrimary; + candidate.flags |= nuclei::QcFlags::kQcIsPhysicalPrimary; ///< heavy flavour mother /*if (particle.has_mothers()) { @@ -344,52 +380,36 @@ struct nucleiQC { } else if (particle.getProcess() == TMCProcess::kPDecay) { ///< assuming that strong decays are included in the previous step - candidate.flags |= nuclei::Flags::kIsSecondaryFromWeakDecay; + candidate.flags |= nuclei::QcFlags::kQcIsSecondaryFromWeakDecay; } else { - candidate.flags |= nuclei::Flags::kIsSecondaryFromMaterial; + candidate.flags |= nuclei::QcFlags::kQcIsSecondaryFromMaterial; + mHistograms.fill(HIST(nuclei::cNames[iSpecies]) + HIST("/h2Productionvertex"), particle.vx(), particle.vy()); + candidate.vx = particle.vx(); + candidate.vy = particle.vy(); } } - void fillSpeciesFlags(const int iSpecies, nuclei::SlimCandidate& candidate) + template + void fillCollisionFlag(const Tparticle& particle, nuclei::SlimCandidate& candidate, const std::unordered_set& reconstructedCollision) { - - switch (iSpecies) { - case nuclei::Species::kPr: - candidate.flags |= nuclei::Flags::kProton; - break; - case nuclei::Species::kDe: - candidate.flags |= nuclei::Flags::kDeuteron; - break; - case nuclei::Species::kTr: - candidate.flags |= nuclei::Flags::kTriton; - break; - case nuclei::Species::kHe: - candidate.flags |= nuclei::Flags::kHe3; - break; - case nuclei::Species::kAl: - candidate.flags |= nuclei::Flags::kHe4; - break; - default: - candidate.flags |= 0; - break; + if (reconstructedCollision.count(particle.mcCollisionId()) > 0) { + candidate.flags |= nuclei::QcFlags::kQcHasReconstructedCollision; } } template - void fillNucleusFlagsPdgs(const int iSpecies, const Tcollision& collision, const Ttrack& track, nuclei::SlimCandidate& candidate) + void fillNucleusFlagsPdgs(const Tcollision& collision, const Ttrack& track, nuclei::SlimCandidate& candidate) { candidate.flags = static_cast((track.pidForTracking() & 0xF) << 12); - fillSpeciesFlags(iSpecies, candidate); - if (track.hasTOF()) - candidate.flags |= nuclei::Flags::kHasTOF; + candidate.flags |= nuclei::QcFlags::kQcHasTOF; if (track.hasTRD()) - candidate.flags |= nuclei::Flags::kHasTRD; + candidate.flags |= nuclei::QcFlags::kQcHasTRD; if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - candidate.flags |= nuclei::Flags::kITSrof; + candidate.flags |= nuclei::QcFlags::kQcITSrof; } template @@ -401,38 +421,11 @@ struct nucleiQC { candidate.phiGenerated = particle.phi(); } - template - void fillDcaInformation(const int iSpecies, const Tcollision& collision, const Ttrack& track, nuclei::SlimCandidate& candidate, const aod::McParticles::iterator& particle) - { - - const o2::math_utils::Point3D collisionVertex{collision.posX(), collision.posY(), collision.posZ()}; - - mDcaInfoCov.set(999, 999, 999, 999, 999); - setTrackParCov(track, mTrackParCov); - mTrackParCov.setPID(track.pidForTracking()); - - if constexpr (isMc) { - if (track.has_mcParticle() && cfgUseTrackTuner->get(iSpecies)) { - mHistTrackTunedTracks->Fill(1.); - mTrackTuner.tuneTrackParams(particle, mTrackParCov, mMatCorr, &mDcaInfoCov, mHistTrackTunedTracks); - } - } else { - mMatCorr = static_cast(cfgMaterialCorrection.value); - } - - mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); - mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); - o2::base::Propagator::Instance()->propagateToDCABxByBz(mVtx, mTrackParCov, 2.f, mMatCorr, &mDcaInfoCov); - - candidate.DCAxy = mDcaInfoCov.getY(); - candidate.DCAz = mDcaInfoCov.getZ(); - } - template nuclei::SlimCandidate fillCandidate(const int iSpecies, Tcollision const& collision, Ttrack const& track) { if (!nuclei::checkSpeciesValidity(iSpecies)) - std::runtime_error("species contains invalid nucleus index"); + throw std::runtime_error("species contains invalid nucleus index"); nuclei::SlimCandidate candidate = {.pt = track.pt() * track.sign(), .eta = track.eta(), @@ -441,8 +434,8 @@ struct nucleiQC { .clusterSizesITS = track.itsClusterSizes(), .TPCsignal = track.tpcSignal(), .beta = mPidManagers[iSpecies].getBetaTOF(track), - .DCAxy = 0.f, - .DCAz = 0.f, + .DCAxy = mDcaInfo[0], // set in the track selection function + .DCAz = mDcaInfo[1], // set in the track selection function .flags = 0, .pdgCode = 0, .motherPdgCode = 0, @@ -453,23 +446,26 @@ struct nucleiQC { .centrality = nuclei::getCentrality(collision, cfgCentralityEstimator, mHistFailCentrality), .mcProcess = TMCProcess::kPNoProcess, .nsigmaTpc = mPidManagers[iSpecies].getNSigmaTPC(track), - .nsigmaTof = mPidManagers[iSpecies].getNSigmaTOF(track)}; - - fillNucleusFlagsPdgs(iSpecies, collision, track, candidate); + .nsigmaTof = mPidManagers[iSpecies].getNSigmaTOF(track), + .vx = -999.f, + .vy = -999.f}; - aod::McParticles::iterator particle; + fillNucleusFlagsPdgs(collision, track, candidate); if constexpr (isMc) { if (track.has_mcParticle()) { - particle = track.mcParticle(); - fillNucleusFlagsPdgsMc(particle, candidate); + const auto& particle = track.mcParticle(); + static_for<0, nuclei::kNspecies - 1>([&](auto iSpeciesCtV) { + constexpr int kSpeciesCt = decltype(iSpeciesCtV)::value; + if (std::abs(particle.pdgCode()) == nuclei::pdgCodes[kSpeciesCt]) { + fillNucleusFlagsPdgsMc(particle, candidate); + } + }); fillNucleusGeneratedVariables(particle, candidate); } } - fillDcaInformation(iSpecies, collision, track, candidate, particle); - return candidate; } @@ -497,7 +493,7 @@ struct nucleiQC { { constexpr int kIndex = iSpecies; if (!nuclei::checkSpeciesValidity(kIndex)) - std::runtime_error("species contains invalid nucleus kIndex"); + throw std::runtime_error("species contains invalid nucleus kIndex"); if (isGenerated) { const float ptGenerated = (kIndex == nuclei::Species::kPr || kIndex == nuclei::Species::kDe || kIndex == nuclei::Species::kTr) ? candidate.ptGenerated : candidate.ptGenerated / 2.f; @@ -517,11 +513,41 @@ struct nucleiQC { } } - void processMc(const Collisions& collisions, const TrackCandidatesMC& tracks, const aod::BCsWithTimestamps&, const aod::McParticles& mcParticles) + void writeCandidate(const nuclei::SlimCandidate& candidate) + { + if (!cfgFillTable) + return; + + mNucleiTableRed( + candidate.pt, + candidate.eta, + candidate.phi, + candidate.tpcInnerParam, + candidate.clusterSizesITS, + candidate.TPCsignal, + candidate.beta, + candidate.DCAxy, + candidate.DCAz, + candidate.flags, + candidate.centrality, + candidate.ptGenerated, + candidate.mcProcess, + candidate.pdgCode, + candidate.motherPdgCode); + mNucleiTableExt( + candidate.nsigmaTpc, + candidate.nsigmaTof); + mNucleiTableMat( + candidate.vx, + candidate.vy); + } + + void processMc(const Collisions& collisions, const TrackCandidatesMC& tracks, const aod::BCsWithTimestamps&, const aod::McParticles& mcParticles, const aod::McCollisions& /*mcCollisions*/) { gRandom->SetSeed(67); - mNucleiCandidates.clear(); - std::vector reconstructedMcParticles(mcParticles.size(), false); + std::unordered_set reconstructedMcParticles; + std::unordered_set reconstructedCollisions; + std::map mcCollisionIdToCentrality; for (const auto& collision : collisions) { @@ -530,12 +556,14 @@ struct nucleiQC { if (!nuclei::eventSelection(collision, mHistograms, cfgEventSelections, cfgCutVertex)) continue; + const float centrality = nuclei::getCentrality(collision, cfgCentralityEstimator, mHistFailCentrality); + mHistograms.fill(HIST("hCentrality"), centrality); + reconstructedCollisions.insert(collision.mcCollisionId()); + mcCollisionIdToCentrality[collision.mcCollisionId()] = centrality; + mVtx.setPos({collision.posX(), collision.posY(), collision.posZ()}); + mVtx.setCov(collision.covXX(), collision.covXY(), collision.covYY(), collision.covXZ(), collision.covYZ(), collision.covZZ()); - bool anyTrackTuner = false; - for (int iSpecies = 0; iSpecies < static_cast(nuclei::Species::kNspecies); iSpecies++) { - anyTrackTuner = anyTrackTuner || cfgUseTrackTuner->get(iSpecies); - } - if (anyTrackTuner && mTrackTuner.autoDetectDcaCalib && !mTrackTuner.areGraphsConfigured) { + if (mAnyTrackTuner && mTrackTuner.autoDetectDcaCalib && !mTrackTuner.areGraphsConfigured) { mTrackTuner.setRunNumber(mRunNumber); @@ -548,40 +576,41 @@ struct nucleiQC { auto tracksThisCollision = tracks.sliceBy(mTracksPerCollision, collision.globalIndex()); for (const auto& track : tracksThisCollision) { - static_for<0, nuclei::kNspecies - 1>([&](auto iSpecies) { - constexpr int kSpeciesCt = decltype(iSpecies)::value; - const int kSpeciesRt = kSpeciesCt; + // selections shared among all species + if (!track.has_mcParticle()) + continue; - if (std::find(mSpeciesToProcess.begin(), mSpeciesToProcess.end(), kSpeciesRt) == mSpeciesToProcess.end()) - return; + if (track.mcParticleId() < -1 || track.mcParticleId() >= mcParticles.size()) + continue; + const auto& particle = mcParticles.iteratorAt(track.mcParticleId()); - if (!track.has_mcParticle()) - return; + if (cfgRapidityToggle && ((particle.y() - cfgRapidityCenterMass) < cfgRapidityMin || (particle.y() - cfgRapidityCenterMass) > cfgRapidityMax)) + continue; + + if (cfgFillOnlyPhysicalPrimaries && !particle.isPhysicalPrimary()) + continue; - if (track.mcParticleId() < -1 || track.mcParticleId() >= mcParticles.size()) + // species-specific selections and filling + static_for<0, nuclei::kNspecies - 1>([&](auto iSpecies) { + constexpr int kSpeciesCt = decltype(iSpecies)::value; + const int kSpeciesRt = kSpeciesCt; + if (!mFillSpecies[kSpeciesCt]) return; - const auto& particle = mcParticles.iteratorAt(track.mcParticleId()); if (cfgDoCheckPdgCode) { if (std::abs(particle.pdgCode()) != nuclei::pdgCodes[kSpeciesRt]) return; } + mDcaInfo = {-999.f, -999.f}; + if (cfgDownscalingFactor->get(kSpeciesRt) < 1.) { if ((gRandom->Uniform()) > cfgDownscalingFactor->get(kSpeciesRt)) return; } - if (cfgRapidityToggle && ((particle.y() - cfgRapidityCenterMass) < cfgRapidityMin || (particle.y() - cfgRapidityCenterMass) > cfgRapidityMax)) - return; - - if (cfgFillOnlyPhysicalPrimaries && !particle.isPhysicalPrimary()) - return; - - LOG(info) << "track passed physical primary cut"; - mHistograms.fill(HIST(nuclei::cNames[kSpeciesCt]) + HIST("/hTrackSelections"), nuclei::trackSelection::kNoCuts); - if (!trackSelection(track)) + if (!trackSelection(track)) return; mHistograms.fill(HIST(nuclei::cNames[kSpeciesCt]) + HIST("/hTrackSelections"), nuclei::trackSelection::kTrackCuts); @@ -591,9 +620,10 @@ struct nucleiQC { nuclei::SlimCandidate candidate; candidate = fillCandidate(kSpeciesCt, collision, track); + fillCollisionFlag(particle, candidate, reconstructedCollisions); - mNucleiCandidates.emplace_back(candidate); - reconstructedMcParticles[particle.globalIndex()] = true; + writeCandidate(candidate); + reconstructedMcParticles.insert(particle.globalIndex()); dispatchFillHistograms(kSpeciesRt, candidate); dispatchFillHistograms(kSpeciesRt, candidate); @@ -605,15 +635,17 @@ struct nucleiQC { for (const auto& particle : mcParticles) { mcIndex++; - int iSpecies = nuclei::getSpeciesFromPdg(particle.pdgCode()); - if (std::find(mSpeciesToProcess.begin(), mSpeciesToProcess.end(), iSpecies) == mSpeciesToProcess.end()) + if (!mFillSpecies[iSpecies]) continue; - if ((particle.y() - cfgRapidityCenterMass) < cfgRapidityMin || (particle.y() - cfgRapidityCenterMass) > cfgRapidityMax) + if (cfgSkipNonReconstructedCollisions && reconstructedCollisions.count(particle.mcCollisionId()) == 0) + continue; + + if (reconstructedMcParticles.count(mcIndex) > 0) continue; - if (reconstructedMcParticles[mcIndex]) + if ((particle.y() - cfgRapidityCenterMass) < cfgRapidityMin || (particle.y() - cfgRapidityCenterMass) > cfgRapidityMax) continue; if (cfgFillOnlyPhysicalPrimaries && !particle.isPhysicalPrimary()) @@ -625,40 +657,21 @@ struct nucleiQC { } nuclei::SlimCandidate candidate; - // candidate.centrality = nuclei::getCentrality(collision, cfgCentralityEstimator, mHistFailCentrality); - candidate.centrality = -1.f; // centrality is not well defined for non-reconstructed particles, set to -1 for now - fillSpeciesFlags(iSpecies, candidate); - fillNucleusFlagsPdgsMc(particle, candidate); + const auto& centralityIt = mcCollisionIdToCentrality.find(particle.mcCollisionId()); + candidate.centrality = centralityIt != mcCollisionIdToCentrality.end() ? centralityIt->second : -1.f; + fillCollisionFlag(particle, candidate, reconstructedCollisions); + static_for<0, nuclei::kNspecies - 1>([&](auto iSpeciesCtV) { + constexpr int kSpeciesCt = decltype(iSpeciesCtV)::value; + if (std::abs(particle.pdgCode()) == nuclei::pdgCodes[kSpeciesCt]) { + fillNucleusFlagsPdgsMc(particle, candidate); + } + }); fillNucleusGeneratedVariables(particle, candidate); - mNucleiCandidates.emplace_back(candidate); + writeCandidate(candidate); mFilledMcParticleIds.emplace_back(particle.globalIndex()); dispatchFillHistograms(iSpecies, candidate); } - - if (!cfgFillTable) - return; - - for (const auto& candidate : mNucleiCandidates) { - mNucleiTableRed( - candidate.pt, - candidate.eta, - candidate.phi, - candidate.tpcInnerParam, - candidate.clusterSizesITS, - candidate.TPCsignal, - candidate.beta, - candidate.DCAxy, - candidate.DCAz, - candidate.flags, - candidate.ptGenerated, - candidate.mcProcess, - candidate.pdgCode, - candidate.motherPdgCode); - mNucleiTableExt( - candidate.nsigmaTpc, - candidate.nsigmaTof); - } } PROCESS_SWITCH(nucleiQC, processMc, "Mc analysis", false); }; diff --git a/PWGLF/TableProducer/Resonances/CMakeLists.txt b/PWGLF/TableProducer/Resonances/CMakeLists.txt index a3e59673293..bafd0b12ea0 100644 --- a/PWGLF/TableProducer/Resonances/CMakeLists.txt +++ b/PWGLF/TableProducer/Resonances/CMakeLists.txt @@ -22,7 +22,7 @@ o2physics_add_dpl_workflow(f1protonreducedtable o2physics_add_dpl_workflow(doublephitable SOURCES doublephitable.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsVertexing + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsVertexing O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(filterf1proton @@ -55,7 +55,22 @@ o2physics_add_dpl_workflow(cksspinalignment PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(phiflow + SOURCES phiflow.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(resonance-tree-creator SOURCES resonanceTreeCreator.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(xi1530kaonreducedtable + SOURCES xi1530kaonreducedtable.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hresonancecorrelationfilter + SOURCES hResonanceCorrelationFilter.cxx + PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Resonances/HeptaQuarktable.cxx b/PWGLF/TableProducer/Resonances/HeptaQuarktable.cxx index 3171b1c93dd..3516949aa9c 100644 --- a/PWGLF/TableProducer/Resonances/HeptaQuarktable.cxx +++ b/PWGLF/TableProducer/Resonances/HeptaQuarktable.cxx @@ -262,7 +262,7 @@ struct heptaquarktable { auto posThisColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); auto negThisColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - for (auto track1 : posThisColl) { + for (const auto& track1 : posThisColl) { if (!selectionTrack(track1)) continue; @@ -279,7 +279,7 @@ struct heptaquarktable { } */ auto track1ID = track1.globalIndex(); - for (auto track2 : negThisColl) { + for (const auto& track2 : negThisColl) { if (!selectionTrack(track2)) continue; diff --git a/PWGLF/TableProducer/Resonances/cksspinalignment.cxx b/PWGLF/TableProducer/Resonances/cksspinalignment.cxx index 014002da82b..8a62bb13ad4 100644 --- a/PWGLF/TableProducer/Resonances/cksspinalignment.cxx +++ b/PWGLF/TableProducer/Resonances/cksspinalignment.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// \file cksspinalignment.cxx -/// \brief Table producer for Charged KStar spin alignment +/// \brief Reduced table producer for K0s and charged pions for later charged K* reconstruction /// /// \author prottay.das@cern.ch @@ -28,7 +28,6 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" -#include #include #include #include @@ -44,10 +43,9 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include +#include #include #include -#include #include using namespace o2; @@ -61,8 +59,6 @@ struct cksspinalignment { Produces kshortTrack; Produces pionTrack; - Service ccdb; - struct : ConfigurableGroup { Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; @@ -74,459 +70,482 @@ struct cksspinalignment { Configurable useGoodITSLayersAll{"useGoodITSLayersAll", true, "Apply kIsGoodITSLayersAll selection bit"}; Configurable cfgCutOccupancy{"cfgCutOccupancy", 2000, "Occupancy cut"}; - // events Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; - Configurable cfgCutCentralityMax{"cfgCutCentralityMax", 80.0f, "Accepted maximum Centrality"}; - Configurable cfgCutCentralityMin{"cfgCutCentralityMin", 0.0f, "Accepted minimum Centrality"}; + Configurable cfgCutCentralityMax{"cfgCutCentralityMax", 80.0f, "Accepted maximum centrality"}; + Configurable cfgCutCentralityMin{"cfgCutCentralityMin", 0.0f, "Accepted minimum centrality"}; - // Configs for track - Configurable cfgCutPt{"cfgCutPt", 0.2, "Pt cut on daughter track"}; - Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut on daughter track"}; + Configurable cfgCutPt{"cfgCutPt", 0.2f, "Prefilter minimum pT for bachelor pion"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Prefilter maximum eta for bachelor pion"}; - // Configs for pion struct : ConfigurableGroup { - Configurable itsPIDSelection{"itsPIDSelection", true, "PID ITS"}; - Configurable lowITSPIDNsigma{"lowITSPIDNsigma", -3.0, "lower cut on PID nsigma for ITS"}; - Configurable highITSPIDNsigma{"highITSPIDNsigma", 3.0, "higher cut on PID nsigma for ITS"}; - Configurable itsclusterPiMeson{"itsclusterPiMeson", 5, "Minimum number of ITS cluster for pi meson track"}; - Configurable tpcCrossedRowsPiMeson{"tpcCrossedRowsPiMeson", 80, "Minimum number of TPC Crossed Rows for pi meson track"}; - Configurable cutDCAxyPiMeson{"cutDCAxyPiMeson", 0.1, "Maximum DCAxy for pi meson track"}; - Configurable cutDCAzPiMeson{"cutDCAzPiMeson", 0.1, "Maximum DCAz for pi meson track"}; - Configurable cutEtaPiMeson{"cutEtaPiMeson", 0.8, "Maximum eta for pi meson track"}; - Configurable cutPTPiMeson{"cutPTPiMeson", 0.8, "Maximum pt for pi meson track"}; - Configurable usePID{"usePID", true, "Flag for using PID selection for pi meson track"}; - Configurable nsigmaCutTPCPiMeson{"nsigmaCutTPCPiMeson", 3.0, "Maximum nsigma cut TPC for pi meson track"}; - Configurable nsigmaCutTOFPiMeson{"nsigmaCutTOFPiMeson", 3.0, "Maximum nsigma cut TOF for pi meson track"}; - Configurable cutTOFBetaPiMeson{"cutTOFBetaPiMeson", 3.0, "Maximum beta cut for pi meson track"}; + Configurable itsPIDSelection{"itsPIDSelection", true, "Apply ITS PID for bachelor pion"}; + Configurable lowITSPIDNsigma{"lowITSPIDNsigma", -3.0f, "Lower ITS n-sigma pion cut"}; + Configurable highITSPIDNsigma{"highITSPIDNsigma", 3.0f, "Upper ITS n-sigma pion cut"}; + Configurable itsclusterPiMeson{"itsclusterPiMeson", 5, "Minimum ITS clusters for bachelor pion"}; + Configurable tpcCrossedRowsPiMeson{"tpcCrossedRowsPiMeson", 80, "Minimum TPC crossed rows for bachelor pion"}; + Configurable cutDCAxyPiMeson{"cutDCAxyPiMeson", 0.1f, "Maximum DCAxy for bachelor pion"}; + Configurable cutDCAzPiMeson{"cutDCAzPiMeson", 0.1f, "Maximum DCAz for bachelor pion"}; + Configurable cutEtaPiMeson{"cutEtaPiMeson", 0.8f, "Maximum eta for bachelor pion"}; + Configurable cutPTPiMeson{"cutPTPiMeson", 0.2f, "Minimum pT for bachelor pion"}; + Configurable usePID{"usePID", true, "Apply pion PID"}; + Configurable nsigmaCutTPCPiMeson{"nsigmaCutTPCPiMeson", 3.0f, "Maximum TPC n-sigma pion cut"}; + Configurable nsigmaCutTOFPiMeson{"nsigmaCutTOFPiMeson", 3.0f, "Maximum TOF n-sigma pion cut"}; + Configurable cutTOFBetaPiMeson{"cutTOFBetaPiMeson", 0.5f, "Minimum TOF beta for bachelor pion"}; + Configurable pSwitchPID{"pSwitchPID", 0.5f, "pT switch for pT-dependent pion PID"}; } grpPion; enum PionPidBits : uint8_t { - kPID1 = 1u << 0, // selectionPID - kPID2 = 1u << 1, // selectionPID2 - kPID3 = 1u << 2, // selectionPID3 - kPID4 = 1u << 3 // selectionPID4 + kPID1 = 1u << 0, + kPID2 = 1u << 1, }; - // Configs for V0 - Configurable confV0PtMin{"confV0PtMin", 0.f, "Minimum transverse momentum of V0"}; - Configurable confV0PtMax{"confV0PtMax", 1000.f, "Maximum transverse momentum of V0"}; - Configurable confV0Rap{"confV0Rap", 0.8f, "Rapidity range of V0"}; - Configurable confV0DCADaughMax{"confV0DCADaughMax", 1.0f, "Maximum DCA between the V0 daughters"}; - Configurable confV0CPAMin{"confV0CPAMin", 0.9998f, "Minimum CPA of V0"}; - Configurable confV0TranRadV0Min{"confV0TranRadV0Min", 1.5f, "Minimum transverse radius"}; - Configurable confV0TranRadV0Max{"confV0TranRadV0Max", 100.f, "Maximum transverse radius"}; - Configurable cMaxV0DCA{"cMaxV0DCA", 1.2, "Maximum V0 DCA to PV"}; - Configurable cMinV0DCAPi{"cMinV0DCAPi", 0.05, "Minimum V0 daughters DCA to PV for Pi"}; - Configurable cMaxV0LifeTime{"cMaxV0LifeTime", 50, "Maximum V0 life time"}; - Configurable qtArmenterosMin{"qtArmenterosMin", 0.2, "Minimum armenteros cut for K0s"}; - // config for V0 daughters - Configurable confDaughEta{"confDaughEta", 0.8f, "V0 Daugh sel: max eta"}; - Configurable cfgDaughPiPt{"cfgDaughPiPt", 0.2, "minimum daughter pion pt"}; - Configurable confDaughTPCnclsMin{"confDaughTPCnclsMin", 50.f, "V0 Daugh sel: Min. nCls TPC"}; - Configurable confDaughTPCncrwsMin{"confDaughTPCncrwsMin", 70.f, "V0 Daugh sel: Min. nCrws TPC"}; - Configurable confDaughPIDCuts{"confDaughPIDCuts", 3, "PID selections for Kshortpion daughters"}; - - // configurable for chargedkstar - Configurable cfgKstarMassMin{"cfgKstarMassMin", 0.75f, "K* mass min"}; - Configurable cfgKstarMassMax{"cfgKstarMassMax", 1.05f, "K* mass max"}; - - Configurable iMNbins{"iMNbins", 50, "Number of bins in invariant mass"}; - Configurable lbinIM{"lbinIM", 1.09, "lower bin value in IM histograms"}; - Configurable hbinIM{"hbinIM", 1.14, "higher bin value in IM histograms"}; + Configurable confV0PtMin{"confV0PtMin", 0.0f, "Minimum K0s pT"}; + Configurable confV0PtMax{"confV0PtMax", 1000.0f, "Maximum K0s pT"}; + Configurable confV0Rap{"confV0Rap", 0.8f, "Maximum K0s rapidity"}; + Configurable confV0DCADaughMax{"confV0DCADaughMax", 1.0f, "Maximum DCA between V0 daughters"}; + Configurable confV0CPAMin{"confV0CPAMin", 0.9998, "Minimum K0s CPA"}; + Configurable confV0TranRadV0Min{"confV0TranRadV0Min", 1.5f, "Minimum K0s transverse radius"}; + Configurable confV0TranRadV0Max{"confV0TranRadV0Max", 100.0f, "Maximum K0s transverse radius"}; + Configurable cMaxV0DCA{"cMaxV0DCA", 1.2, "Maximum K0s DCA to PV"}; + Configurable cMinV0DCAPi{"cMinV0DCAPi", 0.05f, "Minimum V0 daughter DCA to PV"}; + Configurable cMaxV0LifeTime{"cMaxV0LifeTime", 50.0f, "Maximum K0s lifetime"}; + Configurable qtArmenterosMin{"qtArmenterosMin", 0.2f, "Minimum Armenteros qT/|alpha| for K0s"}; + + Configurable confDaughEta{"confDaughEta", 0.8f, "Maximum V0 daughter eta"}; + Configurable cfgDaughPiPt{"cfgDaughPiPt", 0.2f, "Minimum V0 daughter pion pT"}; + Configurable confDaughTPCnclsMin{"confDaughTPCnclsMin", 50.0f, "Minimum V0 daughter TPC clusters"}; + Configurable confDaughTPCncrwsMin{"confDaughTPCncrwsMin", 70.0f, "Minimum V0 daughter TPC crossed rows"}; + Configurable confDaughPIDCuts{"confDaughPIDCuts", 3.0f, "TPC pion PID cut for V0 daughters"}; + + Configurable cfgK0sMassMin{"cfgK0sMassMin", 0.45f, "Minimum K0s invariant mass"}; + Configurable cfgK0sMassMax{"cfgK0sMassMax", 0.55f, "Maximum K0s invariant mass"}; + + Configurable cfgKeepFraction{"cfgKeepFraction", 0.4f, "Fraction of events to keep"}; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; RCTFlagsChecker rctChecker; + void init(o2::framework::InitContext&) { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - AxisSpec thnAxisInvMass{iMNbins, lbinIM, hbinIM, "#it{M} (GeV/#it{c}^{2})"}; - histos.add("hCent", "hCent", kTH1F, {{8, 0, 80.0}}); - histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{5, 0, 5.0}}); - histos.add("hTrkSelInfo", "hTrkSelInfo", kTH1F, {{5, 0, 5.0}}); - histos.add("hKShortMass", "hKShortMass", kTH1F, {thnAxisInvMass}); - histos.add("hV0Info", "hV0Info", kTH1F, {{5, 0, 5.0}}); + histos.add("hCent", "hCent", kTH1F, {{16, 0.0f, 80.0f}}); + histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{6, 0.0f, 6.0f}}); + histos.add("hTrkSelInfo", "hTrkSelInfo", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("hV0Info", "hV0Info", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("hKShortMass", "hKShortMass;M_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {{200, 0.4f, 0.6f}}); + histos.add("hNStoredK0s", "hNStoredK0s;N_{K^{0}_{S}};Events", kTH1F, {{100, 0.0f, 100.0f}}); + histos.add("hNStoredPions", "hNStoredPions;N_{#pi};Events", kTH1F, {{500, 0.0f, 500.0f}}); } - template - bool selectionTrack(const T& candidate) + bool keepEvent(uint64_t eventIndex) const { - if (candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() >= grpPion.itsclusterPiMeson && candidate.tpcNClsCrossedRows() > grpPion.tpcCrossedRowsPiMeson && std::abs(candidate.dcaXY()) <= grpPion.cutDCAxyPiMeson && std::abs(candidate.dcaZ()) <= grpPion.cutDCAzPiMeson && std::abs(candidate.eta()) <= grpPion.cutEtaPiMeson && candidate.pt() >= grpPion.cutPTPiMeson) { - return true; - } - return false; + // if (cfgKeepFraction >= 1.0f) { + // return true; + // } + // SplitMix64 pseudo-random hash + uint64_t x = eventIndex + 0x9e3779b97f4a7c15ULL; + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + x = x ^ (x >> 31); + + // Convert hash to uniform random number in [0,1) + const double u = + static_cast(x >> 11) * + (1.0 / 9007199254740992.0); + + return u < cfgKeepFraction.value; } template - bool selectionPID(const T& candidate) + bool selectionTrack(const T& candidate) { - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < grpPion.nsigmaCutTPCPiMeson) { - return true; - } - if (candidate.hasTOF() && candidate.beta() > grpPion.cutTOFBetaPiMeson && std::abs(candidate.tpcNSigmaPi()) < grpPion.nsigmaCutTPCPiMeson && std::abs(candidate.tofNSigmaPi()) < grpPion.nsigmaCutTOFPiMeson) { - return true; - } - return false; + return candidate.isGlobalTrack() && + candidate.isPVContributor() && + candidate.itsNCls() > grpPion.itsclusterPiMeson && + candidate.tpcNClsCrossedRows() > grpPion.tpcCrossedRowsPiMeson && + std::abs(candidate.dcaXY()) < grpPion.cutDCAxyPiMeson && + std::abs(candidate.dcaZ()) < grpPion.cutDCAzPiMeson && + std::abs(candidate.eta()) < grpPion.cutEtaPiMeson && + candidate.pt() > grpPion.cutPTPiMeson; } template - bool selectionPID2(const T& candidate) + bool selectionPID(const T& candidate) { - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < grpPion.nsigmaCutTPCPiMeson) { - return true; + const float pt = candidate.pt(); + const float nTPC = candidate.tpcNSigmaPi(); + + if (pt < grpPion.pSwitchPID) { + return std::abs(nTPC) < grpPion.nsigmaCutTPCPiMeson; } - if (candidate.hasTOF() && candidate.beta() > grpPion.cutTOFBetaPiMeson && std::sqrt(candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi() + candidate.tofNSigmaPi() * candidate.tofNSigmaPi()) < grpPion.nsigmaCutTOFPiMeson) { - return true; + + if (!candidate.hasTOF()) { + return false; } - return false; - } - template - bool selectionPID3(const T& candidate) - { - auto px = candidate.px(); - auto py = candidate.py(); - // auto pz = candidate.pz(); - auto pt = std::sqrt(px * px + py * py); - float lowmom = 0.5; - if (pt < lowmom) { - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < grpPion.nsigmaCutTPCPiMeson) { - return true; - } else if (candidate.hasTOF() && std::sqrt(candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi() + candidate.tofNSigmaPi() * candidate.tofNSigmaPi()) < grpPion.nsigmaCutTOFPiMeson) { - return true; - } - } else if (candidate.hasTOF() && std::sqrt(candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi() + candidate.tofNSigmaPi() * candidate.tofNSigmaPi()) < grpPion.nsigmaCutTOFPiMeson) { - return true; + if (candidate.beta() <= grpPion.cutTOFBetaPiMeson) { + return false; } - return false; + + const float nTOF = candidate.tofNSigmaPi(); + const float nCombined = std::sqrt(nTPC * nTPC + nTOF * nTOF); + + return nCombined < grpPion.nsigmaCutTOFPiMeson; } template - bool selectionPID4(const T& candidate) + bool selectionPID2(const T& candidate) { - // Use total momentum p (as you said). If you really want pT, replace with sqrt(px^2+py^2). - const float px = candidate.px(); - const float py = candidate.py(); - // const float pz = candidate.pz(); - const float pt = std::sqrt(px * px + py * py); - - constexpr float pSwitch = 0.5f; // GeV/c - + const float pt = candidate.pt(); const float nTPC = candidate.tpcNSigmaPi(); - // Low momentum: TPC-only, TOF not required and not used - if (pt < pSwitch) { - return std::abs(nTPC) < grpPion.nsigmaCutTPCPiMeson; // e.g. 3 + if (pt < grpPion.pSwitchPID) { + return std::abs(nTPC) < grpPion.nsigmaCutTPCPiMeson; } - // High momentum: TOF hit mandatory + separate 3σ cuts if (!candidate.hasTOF()) { return false; } + if (candidate.beta() <= grpPion.cutTOFBetaPiMeson) { + return false; + } + const float nTOF = candidate.tofNSigmaPi(); - return (std::abs(nTPC) < grpPion.nsigmaCutTPCPiMeson) && - (std::abs(nTOF) < grpPion.nsigmaCutTOFPiMeson); + + return std::abs(nTPC) < grpPion.nsigmaCutTPCPiMeson && + std::abs(nTOF) < grpPion.nsigmaCutTOFPiMeson; } template uint8_t pionPidMask(const T& trk) { - uint8_t m = 0; - if (selectionPID(trk)) - m |= kPID1; - if (selectionPID2(trk)) - m |= kPID2; - if (selectionPID3(trk)) - m |= kPID3; - if (selectionPID4(trk)) - m |= kPID4; - return m; + uint8_t mask = 0; + + if (selectionPID(trk)) { + mask |= kPID1; + } + + if (selectionPID2(trk)) { + mask |= kPID2; + } + + return mask; } + struct StoredK0s { + float px; + float py; + float pz; + float mass; + float cospa; + float radius; + float dcaPositive; + float dcaNegative; + // float dcaBetweenDaughters; + // float lifetime; + int64_t positiveIndex; + int64_t negativeIndex; + }; + + struct StoredPion { + float px; + float py; + float pz; + int8_t charge; + int64_t index; + uint8_t pidMask; + }; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter centralityFilter = (aod::cent::centFT0C < cfgCutCentralityMax && aod::cent::centFT0C >= cfgCutCentralityMin); + + using EventCandidates = soa::Filtered>; + using FullTrackCandidates = soa::Join; + using ResoV0s = aod::V0Datas; + Partition bachelorPionCandidates = nabs(aod::track::eta) < cfgCutEta && aod::track::pt > cfgCutPt; + template bool selectionV0(Collision const& collision, V0 const& candidate) { - if (std::abs(candidate.dcav0topv()) > cMaxV0DCA) { - return false; - } const float pT = candidate.pt(); const float tranRad = candidate.v0radius(); const float dcaDaughv0 = std::abs(candidate.dcaV0daughters()); const float cpav0 = candidate.v0cosPA(); - float ctauKShort = candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * (o2::constants::physics::MassK0); + const float ctauKShort = candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0; - if (pT < confV0PtMin) { - return false; - } - if (pT > confV0PtMax) { + if (std::abs(candidate.dcav0topv()) >= cMaxV0DCA) { return false; } - if (dcaDaughv0 > confV0DCADaughMax) { + + if (pT <= confV0PtMin || pT >= confV0PtMax) { return false; } - if (cpav0 < confV0CPAMin) { + + if (dcaDaughv0 >= confV0DCADaughMax) { return false; } - if (tranRad < confV0TranRadV0Min) { + + if (cpav0 <= confV0CPAMin) { return false; } - if (tranRad > confV0TranRadV0Max) { + + if (tranRad <= confV0TranRadV0Min || tranRad >= confV0TranRadV0Max) { return false; } - if (std::abs(ctauKShort) > cMaxV0LifeTime) { + + if (std::abs(ctauKShort) >= cMaxV0LifeTime) { return false; } - if ((candidate.qtarm() / std::abs(candidate.alpha())) < qtArmenterosMin) { + + if ((candidate.qtarm() / std::abs(candidate.alpha())) <= qtArmenterosMin) { return false; } - if (std::abs(candidate.yK0Short()) > confV0Rap) { // use full rapidity 0.8 for K0s + + if (std::abs(candidate.yK0Short()) >= confV0Rap) { return false; } + return true; } template bool isSelectedV0Daughter(V0 const& candidate) { - auto postrack = candidate.template posTrack_as(); - auto negtrack = candidate.template negTrack_as(); - - const auto ncrfc = 0.8; + auto postrack = candidate.template posTrack_as(); + auto negtrack = candidate.template negTrack_as(); if (postrack.sign() < 0 || negtrack.sign() > 0) { return false; } - if (postrack.tpcNClsCrossedRows() < confDaughTPCncrwsMin || negtrack.tpcNClsCrossedRows() < confDaughTPCncrwsMin) { + + if (postrack.tpcNClsCrossedRows() <= confDaughTPCncrwsMin || + negtrack.tpcNClsCrossedRows() <= confDaughTPCncrwsMin) { + return false; + } + + if (postrack.tpcNClsFound() <= confDaughTPCnclsMin || + negtrack.tpcNClsFound() <= confDaughTPCnclsMin) { return false; } - if (postrack.tpcNClsFound() < confDaughTPCnclsMin || negtrack.tpcNClsFound() < confDaughTPCnclsMin) { + + if (std::abs(postrack.tpcNSigmaPi()) >= confDaughPIDCuts || + std::abs(negtrack.tpcNSigmaPi()) >= confDaughPIDCuts) { return false; } - if (postrack.tpcCrossedRowsOverFindableCls() < ncrfc || negtrack.tpcCrossedRowsOverFindableCls() < ncrfc) { + + if (candidate.positivept() <= cfgDaughPiPt || + candidate.negativept() <= cfgDaughPiPt) { return false; } - if (std::abs(postrack.tpcNSigmaPi()) > confDaughPIDCuts || std::abs(negtrack.tpcNSigmaPi()) > confDaughPIDCuts) { + + if (std::abs(candidate.positiveeta()) >= confDaughEta || + std::abs(candidate.negativeeta()) >= confDaughEta) { return false; } - if (candidate.positivept() < cfgDaughPiPt || candidate.negativept() < cfgDaughPiPt) { + + if (std::abs(candidate.dcapostopv()) <= cMinV0DCAPi || + std::abs(candidate.dcanegtopv()) <= cMinV0DCAPi) { + return false; + } + + return true; + } + + template + bool selectionK0s(Collision const& collision, V0 const& v0) + { + if (!isSelectedV0Daughter(v0)) { return false; } - if (std::abs(candidate.positiveeta()) > confDaughEta || std::abs(candidate.negativeeta()) > confDaughEta) { + + if (!selectionV0(collision, v0)) { return false; } - if (std::abs(candidate.dcapostopv()) < cMinV0DCAPi || std::abs(candidate.dcanegtopv()) < cMinV0DCAPi) { + + if (v0.mK0Short() < cfgK0sMassMin || v0.mK0Short() > cfgK0sMassMax) { return false; } return true; } - std::tuple getK0sTags(const auto& v0, const auto& collision) + void processData(EventCandidates::iterator const& collision, + FullTrackCandidates const& /*tracks*/, + ResoV0s const& v0s) { - // auto postrack = v0.template posTrack_as(); - // auto negtrack = v0.template negTrack_as(); + o2::aod::ITSResponse itsResponse; - int kshortTag = 0; + const float centrality = collision.centFT0C(); + const float vz = collision.posZ(); + const int occupancy = collision.trackOccupancyInTimeRange(); - if (isSelectedV0Daughter(v0) && v0.mK0Short() > lbinIM && v0.mK0Short() < hbinIM) { - kshortTag = 1; + const float psiFT0C = collision.psiFT0C(); + // const float psiFT0A = collision.psiFT0A(); + // const float psiTPC = collision.psiTPC(); + + histos.fill(HIST("hEvtSelInfo"), 0.5); + + if ((rctCut.requireRCTFlagChecker && !rctChecker(collision)) || + !collision.selection_bit(aod::evsel::kNoSameBunchPileup) || + !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || + !collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || + !collision.selection_bit(aod::evsel::kNoITSROFrameBorder) || + (useNoCollInTimeRangeStandard && + !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) || + !collision.sel8() || + (useGoodITSLayersAll && + !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) || + occupancy >= cfgCutOccupancy) { + return; } - if (!kshortTag) { - return {0, false}; // No valid tags + histos.fill(HIST("hEvtSelInfo"), 1.5); + + if (!collision.triggereventep()) { + return; } - if (!selectionV0(collision, v0)) { - return {0, false}; // Fails selection + if (!keepEvent(static_cast(collision.globalIndex()))) { + return; } - return {kshortTag, true}; // Valid candidate - } + histos.fill(HIST("hEvtSelInfo"), 2.5); + histos.fill(HIST("hCent"), centrality); - ROOT::Math::PxPyPzMVector kshort, pion, pionbach, antiPion; - ROOT::Math::PxPyPzMVector kshortDummy, pionDummy; + std::vector selectedK0s; + std::vector selectedPions; - Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter centralityFilter = (nabs(aod::cent::centFT0C) < cfgCutCentralityMax && nabs(aod::cent::centFT0C) > cfgCutCentralityMin); - Filter acceptanceFilter = (nabs(aod::track::eta) < cfgCutEta && (aod::track::pt) > cfgCutPt); + for (const auto& track : bachelorPionCandidates) { + histos.fill(HIST("hTrkSelInfo"), 0.5); - using EventCandidates = soa::Filtered>; - using AllTrackCandidates = soa::Filtered>; - using ResoV0s = aod::V0Datas; - void processData(EventCandidates::iterator const& collision, AllTrackCandidates const& tracks, ResoV0s const& V0s) - { - o2::aod::ITSResponse itsResponse; - std::vector kshortMother, pionBachelor; - std::vector v0Cospa = {}; - std::vector v0Radius = {}; - std::vector dcaPositive = {}; - std::vector dcaNegative = {}; - std::vector positiveIndex = {}; - std::vector negativeIndex = {}; - std::vector dcaBetweenDaughter = {}; - std::vector v0Lifetime = {}; - // std::vector armenteros = {}; - std::vector pionBachelorIndex = {}; - // std::vector pionBachelorSign = {}; - // std::vector pionBachelorTPC = {}; - // std::vector pionBachelorTOF = {}; - // std::vector pionBachelorTOFHit = {}; - std::vector pionBachelorPidMask = {}; - - int numbV0 = 0; - auto centrality = collision.centFT0C(); - auto vz = collision.posZ(); - int occupancy = collision.trackOccupancyInTimeRange(); - auto psiFT0C = collision.psiFT0C(); - auto psiFT0A = collision.psiFT0A(); - auto psiTPC = collision.psiTPC(); - // auto psiTPCR = collision.psiTPCR(); - // auto psiTPCL = collision.psiTPCL(); - histos.fill(HIST("hEvtSelInfo"), 0.5); - // if ((!rctCut.requireRCTFlagChecker || rctChecker(collision)) && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kNoTimeFrameBorder) && collision.selection_bit(aod::evsel::kNoITSROFrameBorder) && collision.sel8() && collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && occupancy < cfgCutOccupancy) { - if ((!rctCut.requireRCTFlagChecker || rctChecker(collision)) && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && collision.selection_bit(aod::evsel::kNoTimeFrameBorder) && collision.selection_bit(aod::evsel::kNoITSROFrameBorder) && (!useNoCollInTimeRangeStandard || collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) && collision.sel8() && (!useGoodITSLayersAll || collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) && occupancy < cfgCutOccupancy) { - histos.fill(HIST("hEvtSelInfo"), 1.5); - if (collision.triggereventep()) { - histos.fill(HIST("hEvtSelInfo"), 2.5); - histos.fill(HIST("hCent"), centrality); - - for (const auto& track1 : tracks) { - histos.fill(HIST("hTrkSelInfo"), 0.5); - if (!selectionTrack(track1)) { - continue; - } - histos.fill(HIST("hTrkSelInfo"), 1.5); - - if (grpPion.itsPIDSelection && !(itsResponse.nSigmaITS(track1) > grpPion.lowITSPIDNsigma && itsResponse.nSigmaITS(track1) < grpPion.highITSPIDNsigma)) { - continue; - } - histos.fill(HIST("hTrkSelInfo"), 2.5); - /* - if (grpPion.usePID && !selectionPID2(track1)) { - continue; - }*/ - const uint8_t mask = pionPidMask(track1); - if (grpPion.usePID && mask == 0) { - continue; - } - histos.fill(HIST("hTrkSelInfo"), 3.5); - - auto track1ID = track1.globalIndex(); - auto track1sign = track1.sign(); - if (track1sign == 0) - continue; - /*auto track1nsigTPC = track1.tpcNSigmaPi(); - auto track1nsigTOF = -999.9; - auto track1TOFHit = -1; - if (track1.hasTOF()) { - track1TOFHit = 1; - track1nsigTOF = track1.tofNSigmaPi(); - histos.fill(HIST("hTrkSelInfo"), 4.5); - }*/ - pionbach = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassPionCharged); - pionBachelor.push_back(pionbach); - pionBachelorIndex.push_back(track1ID); - // pionBachelorSign.push_back(track1sign); - // pionBachelorTPC.push_back(track1nsigTPC); - // pionBachelorTOF.push_back(track1nsigTOF); - // pionBachelorTOFHit.push_back(track1TOFHit); - pionBachelorPidMask.push_back(mask); - } - for (const auto& v0 : V0s) { - histos.fill(HIST("hV0Info"), 0.5); - auto [kshortTag, isValid] = getK0sTags(v0, collision); - if (kshortTag && isValid) { - histos.fill(HIST("hV0Info"), 1.5); - auto postrack1 = v0.template posTrack_as(); - auto negtrack1 = v0.template negTrack_as(); - positiveIndex.push_back(postrack1.globalIndex()); - negativeIndex.push_back(negtrack1.globalIndex()); - v0Cospa.push_back(v0.v0cosPA()); - v0Radius.push_back(v0.v0radius()); - dcaPositive.push_back(std::abs(v0.dcapostopv())); - dcaNegative.push_back(std::abs(v0.dcanegtopv())); - dcaBetweenDaughter.push_back(std::abs(v0.dcaV0daughters())); - v0Lifetime.push_back(v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * (o2::constants::physics::MassK0)); - // armenteros.push_back((v0.qtarm() / std::abs(v0.alpha()))); - - pion = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), o2::constants::physics::MassPionCharged); - antiPion = ROOT::Math::PxPyPzMVector(v0.pxneg(), v0.pyneg(), v0.pzneg(), o2::constants::physics::MassPionCharged); - kshort = pion + antiPion; - // chargedkstar = kshort + pionbach; - kshortMother.push_back(kshort); - // chargedkstarMother.push_back(chargedkstar); - histos.fill(HIST("hKShortMass"), kshort.M()); - } - numbV0 = numbV0 + 1; - } - if (numbV0 > 1 && v0Cospa.size() > 1 && !kshortMother.empty() && !pionBachelor.empty()) { - - std::vector useK0(kshortMother.size(), 0); - std::vector usePi(pionBachelor.size(), 0); - bool anyPair = false; - for (size_t ik0 = 0; ik0 < kshortMother.size(); ++ik0) { - const auto& k0 = kshortMother[ik0]; - const int posId = positiveIndex[ik0]; - const int negId = negativeIndex[ik0]; - - for (size_t ipi = 0; ipi < pionBachelor.size(); ++ipi) { - const int piId = pionBachelorIndex[ipi]; - - // avoid self-combination: bachelor pion can't be a V0 daughter - if (piId == posId || piId == negId) { - continue; - } - - const auto kstar = k0 + pionBachelor[ipi]; - const float m = kstar.M(); - - if (m < cfgKstarMassMin || m > cfgKstarMassMax) { - continue; - } - - useK0[ik0] = 1; - usePi[ipi] = 1; - anyPair = true; - } - } - - // only write event + tables if at least one K* candidate exists in window - if (anyPair) { - histos.fill(HIST("hEvtSelInfo"), 3.5); - // kshortpionEvent(centrality, vz, collision.index(), psiFT0C, psiFT0A, psiTPC); - kshortpionEvent(centrality, vz, psiFT0C, psiFT0A, psiTPC); - auto indexEvent = kshortpionEvent.lastIndex(); - // write only used K0s - for (size_t ik0 = 0; ik0 < kshortMother.size(); ++ik0) { - if (!useK0[ik0]) { - continue; - } - kshortDummy = kshortMother[ik0]; - kshortTrack(indexEvent, v0Cospa[ik0], v0Radius[ik0], dcaPositive[ik0], dcaNegative[ik0], dcaBetweenDaughter[ik0], v0Lifetime[ik0], kshortDummy.Px(), kshortDummy.Py(), kshortDummy.Pz(), kshortDummy.M(), positiveIndex[ik0], negativeIndex[ik0]); - } - // write only used pions - for (size_t ipi = 0; ipi < pionBachelor.size(); ++ipi) { - if (!usePi[ipi]) { - continue; - } - pionDummy = pionBachelor[ipi]; - /* - pionTrack(indexEvent, - pionDummy.Px(), pionDummy.Py(), pionDummy.Pz(), - pionBachelorTPC[ipi], - pionBachelorTOFHit[ipi], - pionBachelorTOF[ipi], - pionBachelorIndex[ipi]);*/ - pionTrack(indexEvent, pionDummy.Px(), pionDummy.Py(), pionDummy.Pz(), pionBachelorIndex[ipi], pionBachelorPidMask[ipi]); - } - } - } + if (!selectionTrack(track)) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 1.5); + + const float nSigmaITS = + itsResponse.nSigmaITS(track); + + if (grpPion.itsPIDSelection && + (nSigmaITS <= grpPion.lowITSPIDNsigma || + nSigmaITS >= grpPion.highITSPIDNsigma)) { + continue; } + + histos.fill(HIST("hTrkSelInfo"), 2.5); + + const uint8_t mask = pionPidMask(track); + + if (grpPion.usePID && mask == 0) { + continue; + } + + const auto charge = static_cast(track.sign()); + + if (charge == 0) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 3.5); + + selectedPions.push_back({track.px(), + track.py(), + track.pz(), + charge, + track.globalIndex(), + mask}); } + + for (const auto& v0 : v0s) { + histos.fill(HIST("hV0Info"), 0.5); + + if (!selectionK0s(collision, v0)) { + continue; + } + + auto postrack = v0.template posTrack_as(); + auto negtrack = v0.template negTrack_as(); + const auto posId = static_cast(postrack.globalIndex()); + const auto negId = static_cast(negtrack.globalIndex()); + + ROOT::Math::PxPyPzMVector pionPos(v0.pxpos(), v0.pypos(), v0.pzpos(), o2::constants::physics::MassPionCharged); + ROOT::Math::PxPyPzMVector pionNeg(v0.pxneg(), v0.pyneg(), v0.pzneg(), o2::constants::physics::MassPionCharged); + ROOT::Math::PxPyPzMVector k0s = pionPos + pionNeg; + + // const float lifetime = + // v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * + // o2::constants::physics::MassK0; + + selectedK0s.push_back({static_cast(k0s.Px()), + static_cast(k0s.Py()), + static_cast(k0s.Pz()), + static_cast(k0s.M()), + static_cast(v0.v0cosPA()), + static_cast(v0.v0radius()), + static_cast(std::abs(v0.dcapostopv())), + static_cast(std::abs(v0.dcanegtopv())), + // static_cast(std::abs(v0.dcaV0daughters())), + // lifetime, + posId, + negId}); + + histos.fill(HIST("hV0Info"), 1.5); + histos.fill(HIST("hKShortMass"), k0s.M()); + } + + histos.fill(HIST("hNStoredK0s"), selectedK0s.size()); + histos.fill(HIST("hNStoredPions"), selectedPions.size()); + + // if (selectedK0s.empty() && selectedPions.empty()) { + if (selectedK0s.empty()) { + return; + } + + histos.fill(HIST("hEvtSelInfo"), 3.5); + + kshortpionEvent(centrality, + vz, + psiFT0C); + // psiFT0A, + // psiTPC); + + const int64_t indexEvent = kshortpionEvent.lastIndex(); + + for (const auto& k0s : selectedK0s) { + kshortTrack(indexEvent, + k0s.cospa, + k0s.radius, + k0s.dcaPositive, + k0s.dcaNegative, + // k0s.dcaBetweenDaughters, + // k0s.lifetime, + k0s.px, + k0s.py, + k0s.pz, + k0s.mass, + k0s.positiveIndex, + k0s.negativeIndex); + } + + for (const auto& pion : selectedPions) { + pionTrack(indexEvent, + pion.px, + pion.py, + pion.pz, + pion.charge, + pion.index, + pion.pidMask); + } + + histos.fill(HIST("hEvtSelInfo"), 4.5); } + PROCESS_SWITCH(cksspinalignment, processData, "Process data", true); }; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; diff --git a/PWGLF/TableProducer/Resonances/doublephitable.cxx b/PWGLF/TableProducer/Resonances/doublephitable.cxx index b682d8cbb40..1cd5309b47e 100644 --- a/PWGLF/TableProducer/Resonances/doublephitable.cxx +++ b/PWGLF/TableProducer/Resonances/doublephitable.cxx @@ -16,7 +16,10 @@ #include "PWGLF/DataModel/ReducedDoublePhiTables.h" -#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseITS.h" @@ -24,23 +27,36 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include +#include #include +#include +#include +#include #include #include #include #include #include #include +#include #include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include +#include +#include +#include +#include +#include +#include #include #include +#include #include #include @@ -49,16 +65,32 @@ using namespace o2::framework; using namespace o2::framework::expressions; struct doublephitable { - // Produce derived tables Produces redPhiEvents; Produces phiTrack; + Produces phiPhiPair; + + Service ccdb; + o2::ccdb::CcdbApi ccdbApi; + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + /// Event selection + struct : ConfigurableGroup { + std::string prefix = std::string("EventSel"); + Configurable ConfEvtSelectZvtx{"ConfEvtSelectZvtx", true, "Event selection includes max. z-Vertex"}; + Configurable ConfEvtZvtx{"ConfEvtZvtx", 10.f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable ConfEvtSel8{"ConfEvtSel8", true, "Event selection sel8"}; + } EventSel; // events Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; // Configurable cfgCutCentralityMax{"cfgCutCentralityMax", 0.0f, "Accepted maximum Centrality"}; // Configurable cfgCutCentralityMin{"cfgCutCentralityMin", 100.0f, "Accepted minimum Centrality"}; // track + Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "ccdb-url"}; + Configurable useTrigger{"useTrigger", true, "use Trigger"}; Configurable useGlobalTrack{"useGlobalTrack", true, "use Global track"}; Configurable cfgCutTOFBeta{"cfgCutTOFBeta", 0.0, "cut TOF beta"}; Configurable cfgCutCharge{"cfgCutCharge", 0.0, "cut on Charge"}; @@ -66,7 +98,7 @@ struct doublephitable { Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut on daughter track"}; Configurable cfgCutDCAxy{"cfgCutDCAxy", 2.0f, "DCAxy range for tracks"}; Configurable cfgCutDCAz{"cfgCutDCAz", 2.0f, "DCAz range for tracks"}; - Configurable nsigmaCutTPC{"nsigmacutTPC", 3.0, "Value of the TPC Nsigma cut"}; + Configurable nsigmaCutTPC{"nsigmacutTPC", -2.0, "Value of the TPC Nsigma cut"}; Configurable nsigmaCutTOF{"nsigmaCutTOF", 3.0, "Value of the TOF Nsigma cut"}; Configurable cfgITScluster{"cfgITScluster", 0, "Number of ITS cluster"}; Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Number of TPC cluster"}; @@ -76,35 +108,103 @@ struct doublephitable { ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0, 10.}, "#it{p}_{T} (GeV/#it{c})"}; Configurable minPhiMass{"minPhiMass", 1.01, "Minimum phi mass"}; Configurable maxPhiMass{"maxPhiMass", 1.03, "Maximum phi mass"}; + Configurable nsigmaCutTPCPreSel{"nsigmacutTPCPreSel", 3.0, "Value of the TPC Nsigma cut Pre selection"}; + Configurable ccdbPathGrpMag{"ccdbPathGrpMag", "GLO/Config/GRPMagField", "CCDB path for Run 3 magnetic field"}; + Configurable propagateToPCA{"propagateToPCA", true, "Propagate tracks to the fitted common vertex"}; + Configurable useAbsDCAFit{"useAbsDCAFit", false, "Use absolute-distance minimisation"}; + Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Use covariance-weighted final PCA"}; + Configurable maxRFit{"maxRFit", 200.f, "Maximum fitted-vertex radius"}; + Configurable maxDZIniFit{"maxDZIniFit", 4.f, "Maximum initial longitudinal separation"}; + Configurable maxDXYIniFit{"maxDXYIniFit", 4.f, "Maximum initial transverse separation"}; + Configurable maxChi2Fit{"maxChi2Fit", 1.e9f, "Maximum four-track fit chi2"}; + Configurable minParamChangeFit{"minParamChangeFit", 1.e-3f, "Fitter convergence threshold"}; + Configurable minRelChi2ChangeFit{"minRelChi2ChangeFit", 0.9f, "Relative chi2 convergence threshold"}; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; // Filter centralityFilter = (nabs(aod::cent::centFT0C) < cfgCutCentralityMax && nabs(aod::cent::centFT0C) > cfgCutCentralityMin); Filter acceptanceFilter = (nabs(aod::track::eta) < cfgCutEta && nabs(aod::track::pt) > cfgCutPT); Filter DCAcutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); - Filter PIDcutFilter = nabs(aod::pidtpc::tpcNSigmaKa) < nsigmaCutTPC; + Filter PIDcutFilter = nabs(aod::pidtpc::tpcNSigmaKa) < nsigmaCutTPCPreSel; - using EventCandidates = soa::Filtered>; - using TrackCandidates = soa::Filtered>; + using EventCandidates = soa::Filtered>; + using TrackCandidates = soa::Filtered< + soa::Join>; SliceCache cache; Partition posTracks = aod::track::signed1Pt > cfgCutCharge; Partition negTracks = aod::track::signed1Pt < cfgCutCharge; + o2::vertexing::DCAFitterN<4> df4; + int fieldRunNumber = -999; + float bz = 0.f; + + OutputObj hProcessedEvents{TH1D("hProcessedEvents", ";; Number of events", 4, 0.0f, 4.0f)}; // Histogram - HistogramRegistry qaRegistry{"QAHistos", { - {"hEventstat", "hEventstat", {HistType::kTH1F, {{2, 0.0f, 2.0f}}}}, - {"hInvMassPhi", "hInvMassPhi", {HistType::kTH2F, {{40, 1.0f, 1.04f}, {100, 0.0f, 10.0f}}}}, - {"hNsigmaPtkaonTPC", "hNsigmaPtkaonTPC", {HistType::kTH2F, {{200, -10.0f, 10.0f}, {100, 0.0f, 10.0f}}}}, - {"hNsigmaPtkaonTOF", "hNsigmaPtkaonTOF", {HistType::kTH2F, {{200, -10.0f, 10.0f}, {100, 0.0f, 10.0f}}}}, - }, + HistogramRegistry qaRegistry{"QAHistos", + { + {"hEventstat", "hEventstat", {HistType::kTH1F, {{2, 0.0f, 2.0f}}}}, + {"hInvMassPhi", "hInvMassPhi", {HistType::kTH2F, {{40, 1.0f, 1.04f}, {100, 0.0f, 10.0f}}}}, + {"hNsigmaPtkaonTPCITS", "hNsigmaPtkaonTPCITS", {HistType::kTH3F, {{200, -10.0f, 10.0f}, {200, -10.0f, 10.0f}, {100, 0.0f, 10.0f}}}}, + {"hNsigmaPtkaonTPC", "hNsigmaPtkaonTPC", {HistType::kTH2F, {{200, -10.0f, 10.0f}, {100, 0.0f, 10.0f}}}}, + {"hNsigmaPtkaonTOF", "hNsigmaPtkaonTOF", {HistType::kTH2F, {{200, -10.0f, 10.0f}, {100, 0.0f, 10.0f}}}}, + }, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry pairQaRegistry{"PairVertexQAHistos", + { + {"hPairMassPtFit", + "Refitted pair;M_{#phi#phi} (GeV/#it{c}^{2});" + "#it{p}_{T}^{#phi#phi} (GeV/#it{c})", + {HistType::kTH2F, {{400, 2.2f, 3.4f}, {200, 0.0f, 20.0f}}}}, + {"hFitChi2Ndf", "Four-kaon common-vertex fit;#chi^{2}/NDF;counts", {HistType::kTH1F, {{500, 0.f, 100.f}}}}, + {"hSumDcaChi2", "Four-kaon DCA compatibility;#Sigma_{i=1}^{4} #chi^{2}_{DCA,i};counts", {HistType::kTH1F, {{500, 0.f, 200.f}}}}, + {"hVertexL3DSig", "Fitted vertex relative to PV;L_{3D}/#sigma_{L_{3D}};counts", {HistType::kTH1F, {{500, 0.f, 100.f}}}}, + {"hRmsDcaSig", "Four-kaon DCA compatibility;#sqrt{#Sigma#chi^{2}_{DCA}/8};counts", {HistType::kTH1F, {{300, 0.0f, 15.0f}}}}, + }, + OutputObjHandlingPolicy::AnalysisObject}; + double massKa = o2::constants::physics::MassKPlus; + void init(o2::framework::InitContext&) + { + ccdb->setURL(url.value); + ccdbApi.init(url); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + zorroSummary.setObject(zorro.getZorroSummary()); + hProcessedEvents->GetXaxis()->SetBinLabel(1, "All Trigger events"); + hProcessedEvents->GetXaxis()->SetBinLabel(2, "Events with Double Phi Trigger"); + hProcessedEvents->GetXaxis()->SetBinLabel(3, "Events eith trigger and Evsel"); + hProcessedEvents->GetXaxis()->SetBinLabel(4, "Final Event"); + df4.setPropagateToPCA(propagateToPCA); + df4.setMaxR(maxRFit); + df4.setMaxDZIni(maxDZIniFit); + df4.setMaxDXYIni(maxDXYIniFit); + df4.setMaxChi2(maxChi2Fit); + df4.setMinParamChange(minParamChangeFit); + df4.setMinRelChi2Change(minRelChi2ChangeFit); + df4.setUseAbsDCA(useAbsDCAFit); + df4.setWeightedFinalPCA(useWeightedFinalPCA); + df4.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); + } + + template + bool isSelectedEvent(T const& col) + { + if (EventSel.ConfEvtSelectZvtx && std::abs(col.posZ()) > EventSel.ConfEvtZvtx) { + return false; + } + if (EventSel.ConfEvtSel8 && !col.sel8()) { + return false; + } + return true; + } template bool selectionTrack(const T& candidate) { - if (useGlobalTrack && !(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > cfgITScluster && candidate.tpcNClsFound() > cfgTPCcluster)) { + // if (useGlobalTrack && !(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > cfgITScluster && candidate.tpcNClsCrossedRows() > cfgTPCcluster)) { + if (useGlobalTrack && !(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > cfgITScluster && candidate.tpcNClsFound() > cfgTPCcluster && candidate.tpcNClsCrossedRows() > cfgTPCcluster)) { return false; } return true; @@ -120,60 +220,56 @@ struct doublephitable { } return false; } - // deep angle cut on pair to remove photon conversion - template - bool selectionPair(const T1& candidate1, const T2& candidate2) - { - double pt1, pt2, pz1, pz2, p1, p2, angle; - pt1 = candidate1.pt(); - pt2 = candidate2.pt(); - pz1 = candidate1.pz(); - pz2 = candidate2.pz(); - p1 = candidate1.p(); - p2 = candidate2.p(); - angle = TMath::ACos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - if (isDeepAngle && angle < cfgDeepAngle) { - return false; - } - return true; - } + int currentRunNumber = -999; + int lastRunNumber = -999; ROOT::Math::PxPyPzMVector KaonPlus, KaonMinus, PhiMesonMother, PhiVectorDummy, Phid1dummy, Phid2dummy; void processPhiReducedTable(EventCandidates::iterator const& collision, TrackCandidates const&, aod::BCsWithTimestamps const&) { o2::aod::ITSResponse itsResponse; bool keepEventDoublePhi = false; int numberPhi = 0; - auto currentRunNumber = collision.bc_as().runNumber(); - auto bc = collision.bc_as(); std::vector Phid1Index = {}; std::vector Phid2Index = {}; - std::vector Phid1Charge = {}; std::vector Phid2Charge = {}; - std::vector Phid1TPC = {}; std::vector Phid2TPC = {}; - std::vector Phid1TOF = {}; std::vector Phid2TOF = {}; - std::vector Phid1TOFHit = {}; std::vector Phid2TOFHit = {}; - std::vector phiresonance, phiresonanced1, phiresonanced2; - int Npostrack = 0; int Nnegtrack = 0; - - if (collision.sel8() && collision.selection_bit(aod::evsel::kNoTimeFrameBorder) && collision.selection_bit(aod::evsel::kNoITSROFrameBorder) && collision.selection_bit(aod::evsel::kNoSameBunchPileup) && collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + float centrality = collision.centFT0M(); + currentRunNumber = collision.bc_as().runNumber(); + auto bc = collision.bc_as(); + hProcessedEvents->Fill(0.5); + bool zorroSelected = false; + if (currentRunNumber != lastRunNumber) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fTriggerEventDoublePhi"); + zorro.populateHistRegistry(qaRegistry, bc.runNumber()); + lastRunNumber = currentRunNumber; + } + if (useTrigger) { + zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); + } else { + zorroSelected = true; + } + if (zorroSelected) { + hProcessedEvents->Fill(1.5); + } + if (zorroSelected && isSelectedEvent(collision)) { + hProcessedEvents->Fill(2.5); auto posThisColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); auto negThisColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - for (auto track1 : posThisColl) { + for (const auto& track1 : posThisColl) { // track selection if (!selectionTrack(track1)) { continue; } + qaRegistry.fill(HIST("hNsigmaPtkaonTPCITS"), itsResponse.nSigmaITS(track1), track1.tpcNSigmaKa(), track1.pt()); // PID check if (!selectionPID(track1)) { continue; @@ -187,7 +283,11 @@ struct doublephitable { qaRegistry.fill(HIST("hNsigmaPtkaonTOF"), track1.tofNSigmaKa(), track1.pt()); } auto track1ID = track1.globalIndex(); - for (auto track2 : negThisColl) { + for (const auto& track2 : negThisColl) { + auto track2ID = track2.globalIndex(); + if (track2ID == track1ID) { + continue; + } // track selection if (!selectionTrack(track2)) { continue; @@ -196,18 +296,11 @@ struct doublephitable { if (!selectionPID(track2)) { continue; } - if (Npostrack == 1) { - Nnegtrack = Nnegtrack + 1; - } - auto track2ID = track2.globalIndex(); - if (track2ID == track1ID) { - continue; - } if (!(itsResponse.nSigmaITS(track2) > -3.0 && itsResponse.nSigmaITS(track2) < 3.0)) { continue; } - if (!selectionPair(track1, track2)) { - continue; + if (Npostrack == 1) { + Nnegtrack = Nnegtrack + 1; } KaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); KaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); @@ -217,17 +310,13 @@ struct doublephitable { ROOT::Math::PtEtaPhiMVector temp1(track1.pt(), track1.eta(), track1.phi(), massKa); ROOT::Math::PtEtaPhiMVector temp2(track2.pt(), track2.eta(), track2.phi(), massKa); ROOT::Math::PtEtaPhiMVector temp3(PhiMesonMother.pt(), PhiMesonMother.eta(), PhiMesonMother.phi(), PhiMesonMother.M()); - phiresonanced1.push_back(temp1); phiresonanced2.push_back(temp2); phiresonance.push_back(temp3); - Phid1Index.push_back(track1.globalIndex()); Phid2Index.push_back(track2.globalIndex()); - Phid1Charge.push_back(track1.sign()); Phid2Charge.push_back(track2.sign()); - Phid1TPC.push_back(track1.tpcNSigmaKa()); Phid2TPC.push_back(track2.tpcNSigmaKa()); auto d1TOFHit = -1; @@ -251,14 +340,15 @@ struct doublephitable { } } } // select collision - if (numberPhi > 1 && Npostrack > 1 && Nnegtrack > 1) { + if (numberPhi > 1 && zorroSelected && Nnegtrack > 1 && Npostrack > 1) { keepEventDoublePhi = true; + hProcessedEvents->Fill(3.5); } qaRegistry.fill(HIST("hEventstat"), 0.5); - if (keepEventDoublePhi && numberPhi > 1 && Npostrack > 1 && Nnegtrack > 1 && (phiresonance.size() == phiresonanced1.size()) && (phiresonance.size() == phiresonanced2.size())) { + if (keepEventDoublePhi && numberPhi > 1 && (phiresonance.size() == phiresonanced1.size()) && (phiresonance.size() == phiresonanced2.size())) { qaRegistry.fill(HIST("hEventstat"), 1.5); /////////// Fill collision table/////////////// - redPhiEvents(bc.globalBC(), currentRunNumber, bc.timestamp(), collision.posZ(), collision.numContrib(), Npostrack, Nnegtrack); + redPhiEvents(bc.globalBC(), currentRunNumber, bc.timestamp(), collision.posZ(), collision.numContrib(), Npostrack, Nnegtrack, centrality); auto indexEvent = redPhiEvents.lastIndex(); //// Fill track table for Phi////////////////// for (auto if1 = phiresonance.begin(); if1 != phiresonance.end(); ++if1) { @@ -266,15 +356,462 @@ struct doublephitable { PhiVectorDummy = phiresonance.at(i5); Phid1dummy = phiresonanced1.at(i5); Phid2dummy = phiresonanced2.at(i5); - phiTrack(indexEvent, PhiVectorDummy.Px(), PhiVectorDummy.Py(), PhiVectorDummy.Pz(), Phid1dummy.Px(), Phid1dummy.Py(), Phid1dummy.Pz(), Phid2dummy.Px(), Phid2dummy.Py(), Phid2dummy.Pz(), - PhiVectorDummy.M(), - Phid1Index.at(i5), Phid2Index.at(i5), - Phid1Charge.at(i5), Phid2Charge.at(i5), - Phid1TPC.at(i5), Phid2TPC.at(i5), Phid1TOFHit.at(i5), Phid2TOFHit.at(i5), Phid1TOF.at(i5), Phid2TOF.at(i5)); + phiTrack(indexEvent, PhiVectorDummy.Px(), PhiVectorDummy.Py(), PhiVectorDummy.Pz(), Phid1dummy.Px(), Phid1dummy.Py(), Phid1dummy.Pz(), Phid2dummy.Px(), Phid2dummy.Py(), + Phid2dummy.Pz(), PhiVectorDummy.M(), Phid1Index.at(i5), Phid2Index.at(i5), Phid1Charge.at(i5), Phid2Charge.at(i5), Phid1TPC.at(i5), Phid2TPC.at(i5), Phid1TOFHit.at(i5), + Phid2TOFHit.at(i5), Phid1TOF.at(i5), Phid2TOF.at(i5)); } } } // process - PROCESS_SWITCH(doublephitable, processPhiReducedTable, "Process table creation for double phi", true); + PROCESS_SWITCH(doublephitable, processPhiReducedTable, "Process table creation for double phi", false); + + struct PhiCandidateVtx { + TrackCandidates::iterator kPlus; + TrackCandidates::iterator kMinus; + ROOT::Math::PxPyPzMVector phiPreFit; + float itsPlus = -999.f; + float itsMinus = -999.f; + }; + + struct KaonPairPayload { + int64_t index = -1; + int8_t charge = 0; + float dcaXY = -999.f; + float dcaZ = -999.f; + float dcaXYSig = -999.f; + float dcaZSig = -999.f; + float dcaChi2 = -1.f; + float px = 0.f; + float py = 0.f; + float pz = 0.f; + int8_t tofHit = -1; + float tpc = -999.f; + float tof = -999.f; + float its = -999.f; + }; + + struct PhiPhiPairPayload { + size_t phi1Index = 0; + size_t phi2Index = 0; + float pairMass = 0.f; + float pairPx = 0.f; + float pairPy = 0.f; + float pairPz = 0.f; + float phi1Mass = 0.f; + float phi1Px = 0.f; + float phi1Py = 0.f; + float phi1Pz = 0.f; + float phi2Mass = 0.f; + float phi2Px = 0.f; + float phi2Py = 0.f; + float phi2Pz = 0.f; + KaonPairPayload k1; + KaonPairPayload k2; + KaonPairPayload k3; + KaonPairPayload k4; + int8_t fitStatus = -1; + float fitChi2 = -1.f; + + // Nominal geometric NDF for four tracks fitted to one 3D point: + // 2 * Ntracks - 3 = 5. + int8_t fitNdf = 5; + float fitChi2Ndf = -1.f; + float pvX = 0.f; + float pvY = 0.f; + float pvZ = 0.f; + float vtxX = 0.f; + float vtxY = 0.f; + float vtxZ = 0.f; + float deltaVtxX = 0.f; + float deltaVtxY = 0.f; + float deltaVtxZ = 0.f; + float vertexLxy = 0.f; + float vertexL3D = 0.f; + float vertexLxyErr = 0.f; + float vertexL3DErr = 0.f; + float vertexLxySig = 0.f; + float vertexL3DSig = 0.f; + int8_t nValidDca = 0; + float sumDcaXYSig2 = 0.f; + float sumDcaZSig2 = 0.f; + float sumDcaChi2 = 0.f; + float rmsDcaSig = -1.f; + float maxDcaChi2 = -1.f; + float maxAbsDcaXYSig = -1.f; + float maxAbsDcaZSig = -1.f; + }; + + template + bool selectionITSKaon(const T& candidate, float& nSigmaITS) + { + nSigmaITS = o2::aod::ITSResponse::nSigmaITS(candidate); + if (!(nSigmaITS > -3.0f && nSigmaITS < 3.0f)) { + return false; + } + + return true; + } + + void updateMagneticField(aod::BCsWithTimestamps::iterator const& bc) + { + if (fieldRunNumber == bc.runNumber()) { + return; + } + + auto* grpmag = ccdb->getForTimeStamp(ccdbPathGrpMag.value, bc.timestamp()); + if (grpmag == nullptr) { + LOGF(fatal, "GRPMagField unavailable for run %d, timestamp %llu", bc.runNumber(), bc.timestamp()); + } + + o2::base::Propagator::initFieldFromGRP(grpmag); + bz = o2::base::Propagator::Instance()->getNominalBz(); + df4.setBz(bz); + fieldRunNumber = bc.runNumber(); + } + + template + KaonPairPayload makeKaonPayload(const T& track, const std::array& momentumAtVertex, float itsNSigma, const V& primaryVertex) + { + KaonPairPayload out; + out.index = track.globalIndex(); + out.charge = static_cast(track.sign()); + out.dcaXY = track.dcaXY(); + out.dcaZ = track.dcaZ(); + out.px = momentumAtVertex[0]; + out.py = momentumAtVertex[1]; + out.pz = momentumAtVertex[2]; + out.tofHit = track.hasTOF() ? 1 : -1; + out.tpc = track.tpcNSigmaKa(); + out.tof = track.hasTOF() ? track.tofNSigmaKa() : -999.f; + out.its = itsNSigma; + auto trackParCov = getTrackParCov(track); + o2::dataformats::DCA dca; + if (trackParCov.propagateToDCA(primaryVertex, bz, &dca)) { + out.dcaXY = dca.getY(); + out.dcaZ = dca.getZ(); + if (dca.getSigmaY2() > 0.f) { + out.dcaXYSig = out.dcaXY / std::sqrt(dca.getSigmaY2()); + } + + if (dca.getSigmaZ2() > 0.f) { + out.dcaZSig = out.dcaZ / std::sqrt(dca.getSigmaZ2()); + } + + const float chi2 = dca.calcChi2(); + if (std::isfinite(chi2) && chi2 >= 0.f) { + out.dcaChi2 = chi2; + } + } + + return out; + } + + template + static void calculateVertexQuantities(PhiPhiPairPayload& pair, const CovPV& covPV, const CovVtx& covVtx) + { + const float cxx = covPV[0] + covVtx[0]; + const float cxy = covPV[1] + covVtx[1]; + const float cyy = covPV[2] + covVtx[2]; + const float cxz = covPV[3] + covVtx[3]; + const float cyz = covPV[4] + covVtx[4]; + const float czz = covPV[5] + covVtx[5]; + pair.vertexLxy = std::hypot(pair.deltaVtxX, pair.deltaVtxY); + pair.vertexL3D = std::sqrt(pair.deltaVtxX * pair.deltaVtxX + pair.deltaVtxY * pair.deltaVtxY + pair.deltaVtxZ * pair.deltaVtxZ); + float varLxy = 0.5f * (cxx + cyy); + if (pair.vertexLxy > 1.e-12f) { + varLxy = (pair.deltaVtxX * pair.deltaVtxX * cxx + 2.f * pair.deltaVtxX * pair.deltaVtxY * cxy + pair.deltaVtxY * pair.deltaVtxY * cyy) / (pair.vertexLxy * pair.vertexLxy); + } + + float varL3D = (cxx + cyy + czz) / 3.f; + if (pair.vertexL3D > 1.e-12f) { + varL3D = (pair.deltaVtxX * pair.deltaVtxX * cxx + pair.deltaVtxY * pair.deltaVtxY * cyy + pair.deltaVtxZ * pair.deltaVtxZ * czz + + 2.f * pair.deltaVtxX * pair.deltaVtxY * cxy + 2.f * pair.deltaVtxX * pair.deltaVtxZ * cxz + 2.f * pair.deltaVtxY * pair.deltaVtxZ * cyz) / + (pair.vertexL3D * pair.vertexL3D); + } + + pair.vertexLxyErr = varLxy > 0.f ? std::sqrt(varLxy) : 0.f; + pair.vertexL3DErr = varL3D > 0.f ? std::sqrt(varL3D) : 0.f; + pair.vertexLxySig = pair.vertexLxyErr > 0.f ? pair.vertexLxy / pair.vertexLxyErr : 0.f; + pair.vertexL3DSig = pair.vertexL3DErr > 0.f ? pair.vertexL3D / pair.vertexL3DErr : 0.f; + } + + static void calculateDcaSummary(PhiPhiPairPayload& pair) + { + const std::array kaons{&pair.k1, &pair.k2, &pair.k3, &pair.k4}; + pair.nValidDca = 0; + pair.sumDcaXYSig2 = 0.f; + pair.sumDcaZSig2 = 0.f; + pair.sumDcaChi2 = 0.f; + pair.maxDcaChi2 = -1.f; + pair.maxAbsDcaXYSig = -1.f; + pair.maxAbsDcaZSig = -1.f; + for (const auto* kaon : kaons) { + if (std::isfinite(kaon->dcaXYSig) && kaon->dcaXYSig > -900.f) { + pair.sumDcaXYSig2 += kaon->dcaXYSig * kaon->dcaXYSig; + pair.maxAbsDcaXYSig = std::max(pair.maxAbsDcaXYSig, std::abs(kaon->dcaXYSig)); + } + + if (std::isfinite(kaon->dcaZSig) && kaon->dcaZSig > -900.f) { + pair.sumDcaZSig2 += kaon->dcaZSig * kaon->dcaZSig; + pair.maxAbsDcaZSig = std::max(pair.maxAbsDcaZSig, std::abs(kaon->dcaZSig)); + } + + if (std::isfinite(kaon->dcaChi2) && kaon->dcaChi2 >= 0.f) { + ++pair.nValidDca; + pair.sumDcaChi2 += kaon->dcaChi2; + pair.maxDcaChi2 = std::max(pair.maxDcaChi2, kaon->dcaChi2); + } + } + + if (pair.nValidDca == 4) { + pair.rmsDcaSig = std::sqrt(pair.sumDcaChi2 / 8.f); + } + } + void processPhiPairVertexTable(EventCandidates::iterator const& collision, TrackCandidates const&, aod::BCsWithTimestamps const&) + { + qaRegistry.fill(HIST("hEventstat"), 0.5); + hProcessedEvents->Fill(0.5); + auto bc = collision.bc_as(); + currentRunNumber = bc.runNumber(); + if (currentRunNumber != lastRunNumber) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fTriggerEventDoublePhi"); + zorro.populateHistRegistry(qaRegistry, bc.runNumber()); + lastRunNumber = currentRunNumber; + } + + const bool zorroSelected = useTrigger ? zorro.isSelected(bc.globalBC()) : true; + if (!zorroSelected) { + return; + } + + hProcessedEvents->Fill(1.5); + if (!isSelectedEvent(collision)) { + return; + } + + hProcessedEvents->Fill(2.5); + updateMagneticField(bc); + auto posThisColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negThisColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + std::vector selectedPos; + std::vector selectedNeg; + std::vector selectedPosITS; + std::vector selectedNegITS; + selectedPos.reserve(posThisColl.size()); + selectedNeg.reserve(negThisColl.size()); + selectedPosITS.reserve(posThisColl.size()); + selectedNegITS.reserve(negThisColl.size()); + for (const auto& track : posThisColl) { + if (!selectionTrack(track)) { + continue; + } + + if (!selectionPID(track)) { + continue; + } + + float nSigmaITS = -999.f; + if (!selectionITSKaon(track, nSigmaITS)) { + continue; + } + + selectedPos.push_back(track); + selectedPosITS.push_back(nSigmaITS); + qaRegistry.fill(HIST("hNsigmaPtkaonTPC"), track.tpcNSigmaKa(), track.pt()); + if (track.hasTOF()) { + qaRegistry.fill(HIST("hNsigmaPtkaonTOF"), track.tofNSigmaKa(), track.pt()); + } + } + + for (const auto& track : negThisColl) { + if (!selectionTrack(track)) { + continue; + } + + if (!selectionPID(track)) { + continue; + } + + float nSigmaITS = -999.f; + if (!selectionITSKaon(track, nSigmaITS)) { + continue; + } + + selectedNeg.push_back(track); + selectedNegITS.push_back(nSigmaITS); + qaRegistry.fill(HIST("hNsigmaPtkaonTPC"), track.tpcNSigmaKa(), track.pt()); + if (track.hasTOF()) { + qaRegistry.fill(HIST("hNsigmaPtkaonTOF"), track.tofNSigmaKa(), track.pt()); + } + } + + std::vector phiCandidates; + phiCandidates.reserve(selectedPos.size() * selectedNeg.size()); + for (size_t iPos = 0; iPos < selectedPos.size(); ++iPos) { + for (size_t iNeg = 0; iNeg < selectedNeg.size(); ++iNeg) { + const auto& trackPlus = selectedPos[iPos]; + const auto& trackMinus = selectedNeg[iNeg]; + ROOT::Math::PxPyPzMVector kaonPlus(trackPlus.px(), trackPlus.py(), trackPlus.pz(), massKa); + ROOT::Math::PxPyPzMVector kaonMinus(trackMinus.px(), trackMinus.py(), trackMinus.pz(), massKa); + const auto phi = kaonPlus + kaonMinus; + if (phi.M() < minPhiMass || phi.M() > maxPhiMass) { + continue; + } + + phiCandidates.push_back({trackPlus, trackMinus, phi, selectedPosITS[iPos], selectedNegITS[iNeg]}); + qaRegistry.fill(HIST("hInvMassPhi"), phi.M(), phi.Pt()); + } + } + + if (phiCandidates.size() < 2) { + return; + } + const int nPosTrack = static_cast(selectedPos.size()); + const int nNegTrack = static_cast(selectedNeg.size()); + if (nPosTrack < 2 || nNegTrack < 2) { + return; + } + const auto primaryVertex = getPrimaryVertex(collision); + const auto covPV = primaryVertex.getCov(); + std::vector acceptedPairs; + for (size_t iPhi = 0; iPhi < phiCandidates.size(); ++iPhi) { + for (size_t jPhi = iPhi + 1; jPhi < phiCandidates.size(); ++jPhi) { + const auto& phi1 = phiCandidates[iPhi]; + const auto& phi2 = phiCandidates[jPhi]; + const int64_t id1 = phi1.kPlus.globalIndex(); + const int64_t id2 = phi1.kMinus.globalIndex(); + const int64_t id3 = phi2.kPlus.globalIndex(); + const int64_t id4 = phi2.kMinus.globalIndex(); + if (id1 == id2 || id1 == id3 || id1 == id4 || id2 == id3 || id2 == id4 || id3 == id4) { + // LOGF(info, "same track: %d %d %d %d", id1, id2, id3, id4); + continue; + } + auto trackPar1 = getTrackParCov(phi1.kPlus); + auto trackPar2 = getTrackParCov(phi1.kMinus); + auto trackPar3 = getTrackParCov(phi2.kPlus); + auto trackPar4 = getTrackParCov(phi2.kMinus); + int nFitCandidates = 0; + try { + nFitCandidates = df4.process(trackPar1, trackPar2, trackPar3, trackPar4); + } catch (const std::runtime_error& error) { + LOGF(debug, "Four-kaon DCAFitterN failed: %s", error.what()); + continue; + } catch (...) { + LOG(debug) << "Four-kaon DCAFitterN failed with an unknown exception"; + continue; + } + + if (nFitCandidates == 0) { + // LOG(info) << "Four-kaon DCAFitterN failed with an unknown exception"; + continue; + } + + std::array pK1{0.f, 0.f, 0.f}; + std::array pK2{0.f, 0.f, 0.f}; + std::array pK3{0.f, 0.f, 0.f}; + std::array pK4{0.f, 0.f, 0.f}; + df4.getTrack(0).getPxPyPzGlo(pK1); + df4.getTrack(1).getPxPyPzGlo(pK2); + df4.getTrack(2).getPxPyPzGlo(pK3); + df4.getTrack(3).getPxPyPzGlo(pK4); + const ROOT::Math::PxPyPzMVector k1Fit(pK1[0], pK1[1], pK1[2], massKa); + const ROOT::Math::PxPyPzMVector k2Fit(pK2[0], pK2[1], pK2[2], massKa); + const ROOT::Math::PxPyPzMVector k3Fit(pK3[0], pK3[1], pK3[2], massKa); + const ROOT::Math::PxPyPzMVector k4Fit(pK4[0], pK4[1], pK4[2], massKa); + const auto phi1Fit = k1Fit + k2Fit; + const auto phi2Fit = k3Fit + k4Fit; + const auto pairFit = phi1Fit + phi2Fit; + + if (pairFit.Pt() < 6.0) { + continue; + } + if (pairFit.M() < 2.5 || pairFit.M() > 3.2) { + continue; + } + // No pair-pT or pair-mass cut here. + + PhiPhiPairPayload pair; + pair.phi1Index = iPhi; + pair.phi2Index = jPhi; + pair.pairMass = pairFit.M(); + pair.pairPx = pairFit.Px(); + pair.pairPy = pairFit.Py(); + pair.pairPz = pairFit.Pz(); + pair.phi1Mass = phi1Fit.M(); + pair.phi1Px = phi1Fit.Px(); + pair.phi1Py = phi1Fit.Py(); + pair.phi1Pz = phi1Fit.Pz(); + pair.phi2Mass = phi2Fit.M(); + pair.phi2Px = phi2Fit.Px(); + pair.phi2Py = phi2Fit.Py(); + pair.phi2Pz = phi2Fit.Pz(); + pair.k1 = makeKaonPayload(phi1.kPlus, pK1, phi1.itsPlus, primaryVertex); + pair.k2 = makeKaonPayload(phi1.kMinus, pK2, phi1.itsMinus, primaryVertex); + pair.k3 = makeKaonPayload(phi2.kPlus, pK3, phi2.itsPlus, primaryVertex); + pair.k4 = makeKaonPayload(phi2.kMinus, pK4, phi2.itsMinus, primaryVertex); + pair.fitStatus = static_cast(df4.getFitStatus()); + pair.fitChi2 = df4.getChi2AtPCACandidate(); + pair.fitChi2Ndf = pair.fitChi2 / static_cast(pair.fitNdf); + const auto& fittedVertex = df4.getPCACandidate(); + const auto covVtx = df4.calcPCACovMatrixFlat(); + pair.pvX = collision.posX(); + pair.pvY = collision.posY(); + pair.pvZ = collision.posZ(); + pair.vtxX = fittedVertex[0]; + pair.vtxY = fittedVertex[1]; + pair.vtxZ = fittedVertex[2]; + pair.deltaVtxX = pair.vtxX - pair.pvX; + pair.deltaVtxY = pair.vtxY - pair.pvY; + pair.deltaVtxZ = pair.vtxZ - pair.pvZ; + calculateVertexQuantities(pair, covPV, covVtx); + calculateDcaSummary(pair); + acceptedPairs.push_back(pair); + } + } + + if (acceptedPairs.empty()) { + return; + } + + redPhiEvents(bc.globalBC(), currentRunNumber, bc.timestamp(), collision.posZ(), collision.numContrib(), nPosTrack, nNegTrack, collision.centFT0M()); + const auto indexEvent = redPhiEvents.lastIndex(); + std::vector phiRows(phiCandidates.size(), -1); + for (size_t iPhi = 0; iPhi < phiCandidates.size(); ++iPhi) { + const auto& phi = phiCandidates[iPhi]; + const int d1TOFHit = phi.kPlus.hasTOF() ? 1 : -1; + const int d2TOFHit = phi.kMinus.hasTOF() ? 1 : -1; + const float d1TOF = phi.kPlus.hasTOF() ? phi.kPlus.tofNSigmaKa() : -999.f; + const float d2TOF = phi.kMinus.hasTOF() ? phi.kMinus.tofNSigmaKa() : -999.f; + phiTrack(indexEvent, phi.phiPreFit.Px(), phi.phiPreFit.Py(), phi.phiPreFit.Pz(), phi.kPlus.px(), phi.kPlus.py(), phi.kPlus.pz(), phi.kMinus.px(), phi.kMinus.py(), + phi.kMinus.pz(), phi.phiPreFit.M(), phi.kPlus.globalIndex(), phi.kMinus.globalIndex(), phi.kPlus.sign(), phi.kMinus.sign(), phi.kPlus.tpcNSigmaKa(), + phi.kMinus.tpcNSigmaKa(), d1TOFHit, d2TOFHit, d1TOF, d2TOF); + + phiRows[iPhi] = phiTrack.lastIndex(); + } + + for (const auto& pair : acceptedPairs) { + const auto& k1 = pair.k1; + const auto& k2 = pair.k2; + const auto& k3 = pair.k3; + const auto& k4 = pair.k4; + pairQaRegistry.fill(HIST("hPairMassPtFit"), pair.pairMass, std::hypot(pair.pairPx, pair.pairPy)); + pairQaRegistry.fill(HIST("hFitChi2Ndf"), pair.fitChi2Ndf); + pairQaRegistry.fill(HIST("hSumDcaChi2"), pair.sumDcaChi2); + pairQaRegistry.fill(HIST("hRmsDcaSig"), pair.rmsDcaSig); + pairQaRegistry.fill(HIST("hVertexL3DSig"), pair.vertexL3DSig); + phiPhiPair(indexEvent, phiRows[pair.phi1Index], phiRows[pair.phi2Index], pair.pairMass, pair.pairPx, pair.pairPy, pair.pairPz, pair.phi1Mass, pair.phi1Px, pair.phi1Py, + pair.phi1Pz, pair.phi2Mass, pair.phi2Px, pair.phi2Py, pair.phi2Pz, k1.index, k1.charge, k1.dcaXY, k1.dcaZ, k1.dcaXYSig, k1.dcaZSig, k1.dcaChi2, k1.px, k1.py, k1.pz, + k1.tofHit, k1.tpc, k1.tof, k1.its, k2.index, k2.charge, k2.dcaXY, k2.dcaZ, k2.dcaXYSig, k2.dcaZSig, k2.dcaChi2, k2.px, k2.py, k2.pz, k2.tofHit, k2.tpc, k2.tof, k2.its, + k3.index, k3.charge, k3.dcaXY, k3.dcaZ, k3.dcaXYSig, k3.dcaZSig, k3.dcaChi2, k3.px, k3.py, k3.pz, k3.tofHit, k3.tpc, k3.tof, k3.its, k4.index, k4.charge, k4.dcaXY, + k4.dcaZ, k4.dcaXYSig, k4.dcaZSig, k4.dcaChi2, k4.px, k4.py, k4.pz, k4.tofHit, k4.tpc, k4.tof, k4.its, pair.fitStatus, pair.fitChi2, pair.fitNdf, pair.fitChi2Ndf, + pair.pvX, pair.pvY, pair.pvZ, pair.vtxX, pair.vtxY, pair.vtxZ, pair.deltaVtxX, pair.deltaVtxY, pair.deltaVtxZ, pair.vertexLxy, pair.vertexL3D, pair.vertexLxyErr, + pair.vertexL3DErr, pair.vertexLxySig, pair.vertexL3DSig, pair.nValidDca, pair.sumDcaXYSig2, pair.sumDcaZSig2, pair.sumDcaChi2, pair.rmsDcaSig, pair.maxDcaChi2, + pair.maxAbsDcaXYSig, pair.maxAbsDcaZSig); + } + + hProcessedEvents->Fill(3.5); + qaRegistry.fill(HIST("hEventstat"), 1.5); + } + PROCESS_SWITCH(doublephitable, processPhiPairVertexTable, "Process collision, phi and four-kaon pair tables", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfg) diff --git a/PWGLF/TableProducer/Resonances/f1protonInitializer.cxx b/PWGLF/TableProducer/Resonances/f1protonInitializer.cxx index 5c52b6ca4d3..4ec94e32a03 100644 --- a/PWGLF/TableProducer/Resonances/f1protonInitializer.cxx +++ b/PWGLF/TableProducer/Resonances/f1protonInitializer.cxx @@ -222,7 +222,7 @@ struct f1protoninitializer { bool triggerF1 = false; bool triggerF1Proton = false; bool triggerF1ProtonFemto = false; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!SelectionTrack(track1)) { continue; } @@ -242,7 +242,7 @@ struct f1protoninitializer { qaRegistry.fill(HIST("hNsigmaPtpionTOF"), track1.tofNSigmaPi(), track1.pt()); } auto track1ID = track1.globalIndex(); - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!SelectionTrack(track2)) { continue; } @@ -273,7 +273,7 @@ struct f1protoninitializer { int track2Sign = track2.sign(); numberPiKpair = numberPiKpair + 1; - for (auto track3 : V0s) { + for (const auto& track3 : V0s) { if (!SelectionV0(collision, track3)) { continue; } @@ -308,7 +308,7 @@ struct f1protoninitializer { F1Vector.SetXYZM(track1.px() + track2.px() + track3.px(), track1.py() + track2.py() + track3.py(), track1.pz() + track2.pz() + track3.pz(), massF1); ////////////// proton loop for F1-proton trigger///////////////// - for (auto track4 : tracks) { + for (const auto& track4 : tracks) { auto collisionId4 = track4.collisionId(); if (collisionId1 != collisionId4) { continue; diff --git a/PWGLF/TableProducer/Resonances/f1protonreducedtable.cxx b/PWGLF/TableProducer/Resonances/f1protonreducedtable.cxx index 44ce411c9eb..e64210a9ec7 100644 --- a/PWGLF/TableProducer/Resonances/f1protonreducedtable.cxx +++ b/PWGLF/TableProducer/Resonances/f1protonreducedtable.cxx @@ -355,7 +355,7 @@ struct f1protonreducedtable { return false; } - inline bool passProtonPID(float nsigmaTPC, float nsigmaTOF, float TOFHit, ROOT::Math::PtEtaPhiMVector proton) + inline bool passProtonPID(float nsigmaTPC, float nsigmaTOF, float TOFHit, const ROOT::Math::PtEtaPhiMVector& proton) { // pidMode: // 0 = default: p < thr -> |TPC| < 2.5 ; p >= thr -> TOF mandatory AND circular(TPC,TOF) < 2.0 @@ -424,8 +424,8 @@ struct f1protonreducedtable { return true; } - float getkstar(const ROOT::Math::PtEtaPhiMVector part1, - const ROOT::Math::PtEtaPhiMVector part2) + float getkstar(const ROOT::Math::PtEtaPhiMVector& part1, + const ROOT::Math::PtEtaPhiMVector& part2) { const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; const float beta = trackSum.Beta(); @@ -444,7 +444,7 @@ struct f1protonreducedtable { return 0.5 * trackRelK.P(); } - std::vector setValuesBB(o2::ccdb::CcdbApi& ccdbApi, aod::BCsWithTimestamps::iterator const& bunchCrossing, const std::string ccdbPath) + std::vector setValuesBB(o2::ccdb::CcdbApi& ccdbApi, aod::BCsWithTimestamps::iterator const& bunchCrossing, const std::string& ccdbPath) { std::map metadata; auto h = ccdbApi.retrieveFromTFileAny(ccdbPath, metadata, bunchCrossing.timestamp()); diff --git a/PWGLF/TableProducer/Resonances/filterf1proton.cxx b/PWGLF/TableProducer/Resonances/filterf1proton.cxx index c9f56828434..995cd37c504 100644 --- a/PWGLF/TableProducer/Resonances/filterf1proton.cxx +++ b/PWGLF/TableProducer/Resonances/filterf1proton.cxx @@ -355,8 +355,8 @@ struct filterf1proton { return true; } - float getkstar(const ROOT::Math::PtEtaPhiMVector part1, - const ROOT::Math::PtEtaPhiMVector part2) + float getkstar(const ROOT::Math::PtEtaPhiMVector& part1, + const ROOT::Math::PtEtaPhiMVector& part2) { const ROOT::Math::PtEtaPhiMVector trackSum = part1 + part2; const float beta = trackSum.Beta(); @@ -375,7 +375,7 @@ struct filterf1proton { return 0.5 * trackRelK.P(); } - std::vector setValuesBB(o2::ccdb::CcdbApi& ccdbApi, aod::BCsWithTimestamps::iterator const& bunchCrossing, const std::string ccdbPath) + std::vector setValuesBB(o2::ccdb::CcdbApi& ccdbApi, aod::BCsWithTimestamps::iterator const& bunchCrossing, const std::string& ccdbPath) { std::map metadata; auto h = ccdbApi.retrieveFromTFileAny(ccdbPath, metadata, bunchCrossing.timestamp()); diff --git a/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx new file mode 100644 index 00000000000..bca62f39f87 --- /dev/null +++ b/PWGLF/TableProducer/Resonances/hResonanceCorrelationFilter.cxx @@ -0,0 +1,1520 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \brief This task pre-filters tracks to do h-resonance (phi, K*0) +/// correlations with an analysis task. +/// +/// \file hResonanceCorrelationFilter.cxx +/// \author Hirak Kumar Koley (hirak.koley@cern.ch) + +#include "PWGLF/DataModel/LFHResonanceCorrelationTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::soa; +using namespace o2::constants::math; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using LorentzVectorPtEtaPhiMass = ROOT::Math::PtEtaPhiMVector; + +enum PIDCutType { + SquareType = 1, + CircularType, +}; + +#define BIT_SET(var, nbit) ((var) |= (1 << (nbit))) +#define BIT_CHECK(var, nbit) ((var) & (1 << (nbit))) + +struct HResonanceCorrelationFilter { + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Single, shared event-selection cut list used by every process function + // (processTriggers, processAssocPions/Kaons/Hadrons, processPhis, + // processKstars, and their MC counterparts) via isSelectedEvents(), so the + // trigger table and every associated-track/resonance table are always + // built over the exact same set of selected collisions. + struct : ConfigurableGroup { + std::string prefix = "configEvents"; + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; + // Centrality source switch for hCentralitySelected: O-O collisions have a + // well-defined FT0C centrality estimator; p-O (asymmetric system) does + // not, so FV0A is used there instead. Only affects which estimator is + // read for that one QA histogram -- doesn't change any selection cut. + Configurable cfgUseFV0ACentrality{"cfgUseFV0ACentrality", false, "Centrality estimator for hCentralitySelected: false = O-O (FT0C), true = p-O (FV0A)"}; + } configEvents; + + // Merged from the formerly separate configTracks/generalSelections/ + // trackSelections groups -- all three were "cuts applied to a track or + // track pair", just split by who last touched them. No member names + // collided, so every Configurable below keeps its original key; only the + // "." prefix at each call site changed, to configTracks. + // General, species-agnostic track-quality cuts: applied identically to + // every track that reaches trackCut() -- trigger, associated hadron, AND + // Phi/K*0 daughter candidates alike (see trackCut() below, and its call + // sites at isValidTrigger(), isValidAssocTrack(), and both + // processPhis()/processKstars()). cfgCutEta/cMinPtcut additionally gate + // the framework-level `acceptanceFilter` Filter, applied to every track + // in every process function before any of the above ever sees it. + struct : ConfigurableGroup { + std::string prefix = "configTracks"; + // Pre-selection Track cuts + Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; + Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "max TPC chi2/clusters for good track"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "max ITS chi2/clusters for good track"}; + Configurable cfgMinTPCCrossedRowsOverFindableCls{"cfgMinTPCCrossedRowsOverFindableCls", 0.8f, "min ratio of TPC crossed rows over findable clusters for good track"}; + Configurable cfgMinITSNCls{"cfgMinITSNCls", 5, "min number of ITS hits (clusters) for good track"}; + + // DCA Selections + // DCAz to PV + Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1f, "Track DCAz cut to PV Maximum"}; + + // Track selections + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; + Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; + + // Track quality shared by trigger AND assoc selection (isValidTrigger() + // and isValidAssocTrack() both read this same configurable -- + // it is NOT split into trigger-/assoc-only copies below). + Configurable minTPCNCrossedRows{"minTPCNCrossedRows", 70, "Minimum TPC crossed rows"}; + + // primary particle DCAxy selections (formerly trackSelections) + // formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + } configTracks; + + // Phi/K*0 resonance-candidate cuts: applied to the RECONSTRUCTED candidate + // (the K+K- or K-pi 4-vector), not to the daughter tracks themselves -- + // the daughters go through the shared configTracks/trackCut() cuts above + // like every other track. Currently holds just the one configurable; kept + // as its own group rather than folded into configTracks so it reads as + // "resonance-level", not "track-level". + struct : ConfigurableGroup { + std::string prefix = "configResoDauTracks"; + Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for the reconstructed Phi/K*0 candidate"}; + } configResoDauTracks; + + // Trigger-hadron-only phase-space and quality cuts. + struct : ConfigurableGroup { + std::string prefix = "configTriggerTracks"; + Configurable triggerEtaMin{"triggerEtaMin", -0.8, "triggeretamin"}; + Configurable triggerEtaMax{"triggerEtaMax", 0.8, "triggeretamax"}; + Configurable triggerPtCutMin{"triggerPtCutMin", 2, "triggerptmin"}; + Configurable triggerPtCutMax{"triggerPtCutMax", 20, "triggerptmax"}; + Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; + Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; + Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; + } configTriggerTracks; + + // Associated-hadron-only phase-space and quality cuts. + struct : ConfigurableGroup { + std::string prefix = "configAssocTracks"; + Configurable assocEtaMin{"assocEtaMin", -0.8, "triggeretamin"}; + Configurable assocEtaMax{"assocEtaMax", 0.8, "triggeretamax"}; + Configurable assocPtCutMin{"assocPtCutMin", 0.2, "assocptmin"}; + Configurable assocPtCutMax{"assocPtCutMax", 10, "assocptmax"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; + } configAssocTracks; + + struct : ConfigurableGroup { + std::string prefix = "configPID"; + /// PID Selections + Configurable cByPassTOF{"cByPassTOF", false, "By pass TOF PID selection"}; // By pass TOF PID selection + Configurable cPIDcutType{"cPIDcutType", 2, "cPIDcutType = 1 for square cut, 2 for circular cut"}; // By pass TOF PID selection + Configurable ispTdepPID{"ispTdepPID", false, "enable pT dependent PID"}; + // Were loose top-level Configurables; folded in here as unused (no call + // sites reference them) and logically the same "Kaon PID" kind as the + // kaonTPCPIDcuts/kaonTOFPIDcuts group below. + Configurable cfgTPCNsigmaKaon{"cfgTPCNsigmaKaon", 3.0f, "TPC Kaon PID"}; + Configurable cfgTOFNsigmaKaon{"cfgTOFNsigmaKaon", 3.0f, "TOF Kaon PID"}; + + // Kaon + Configurable> kaonTPCPIDpTintv{"kaonTPCPIDpTintv", {0.5f}, "pT intervals for Kaon TPC PID cuts"}; + Configurable> kaonTPCPIDcuts{"kaonTPCPIDcuts", {2}, "nSigma list for Kaon TPC PID cuts"}; + Configurable> kaonTOFPIDpTintv{"kaonTOFPIDpTintv", {999.0f}, "pT intervals for Kaon TOF PID cuts"}; + Configurable> kaonTOFPIDcuts{"kaonTOFPIDcuts", {2}, "nSigma list for Kaon TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedpTintv{"kaonTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Kaon TPC-TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedPIDcuts{"kaonTPCTOFCombinedPIDcuts", {2}, "nSigma list for Kaon TPC-TOF PID cuts"}; + + // Pion (K*0 daughter) + Configurable> pionTPCPIDpTintv{"pionTPCPIDpTintv", {0.5f}, "pT intervals for Pion TPC PID cuts"}; + Configurable> pionTPCPIDcuts{"pionTPCPIDcuts", {2}, "nSigma list for Pion TPC PID cuts"}; + Configurable> pionTOFPIDpTintv{"pionTOFPIDpTintv", {999.0f}, "pT intervals for Pion TOF PID cuts"}; + Configurable> pionTOFPIDcuts{"pionTOFPIDcuts", {2}, "nSigma list for Pion TOF PID cuts"}; + Configurable> pionTPCTOFCombinedpTintv{"pionTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Pion TPC-TOF PID cuts"}; + Configurable> pionTPCTOFCombinedPIDcuts{"pionTPCTOFCombinedPIDcuts", {2}, "nSigma list for Pion TPC-TOF PID cuts"}; + + // Associated kaon identification (formerly trackSelections; mirrors the + // pion selection above, with the accept/reject roles swapped: accept + // kaon-consistent tracks, reject pion-/proton-consistent ones). Selected + // via the Species template tag on isValidAssocTrack() below -- + // production path (which table rows get made) is still separate per + // process function, only the PID gate logic itself is shared. + Configurable assocKaonNSigmaTPCFOF{"assocKaonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Kaon ID"}; + + // Associated pion identification (formerly trackSelections) + Configurable assocPionNSigmaTPCFOF{"assocPionNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Pion ID"}; + Configurable rejectSigma{"rejectSigma", 1, "n sigma for rejecting pion candidates"}; + } configPID; + + // must include windows for background and peak + // + // This filter-level cut is an acceptance pre-cut only -- it decides what + // ever reaches AssocPhis/AssocKstars, not what counts as signal. It must + // stay wider than whatever signal+background region hResonanceCorrelation.cxx + // wants downstream (its massWindowConfigurationsPhi/Kstar go out to + // maxBgNSigma=6 by default), or that background region gets silently + // truncated before it ever reaches the analysis task. peakMass/sigma here + // mirror the analysis task's own massWindowConfigurationsPhi/Kstar + // (sigma = PDG Gamma/2.355 placeholder -- refit from your own peak and + // update both files together, they must agree on what "sigma" means). + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurations"; + Configurable maxMassNSigma{"maxMassNSigma", 12.0f, "max mass region to be considered for further analysis, in units of sigma"}; + } massWindowConfigurations; + + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsPhi"; + Configurable peakMass{"peakMass", 1.019455f, "Phi(1020) PDG mass (GeV)"}; + Configurable sigma{"sigma", 0.0018f, "effective width (GeV) for the maxMassNSigma pre-cut -- keep in sync with hResonanceCorrelation.cxx's massWindowConfigurationsPhi.sigma"}; + } massWindowConfigurationsPhi; + + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsKstar"; + Configurable peakMass{"peakMass", 0.89555f, "K*0(892) PDG mass (GeV)"}; + Configurable sigma{"sigma", 0.0201f, "effective width (GeV) for the maxMassNSigma pre-cut -- keep in sync with hResonanceCorrelation.cxx's massWindowConfigurationsKstar.sigma"}; + } massWindowConfigurationsKstar; + + // Derived acceptance-window bounds, computed once in init() from the + // Configurables above (peakMass +/- maxMassNSigma * sigma) rather than + // hand-entered GeV numbers, so there is exactly one place to widen the + // pre-cut instead of four independent literals that can drift apart. + float mMinMassPhi = 0.f; + float mMaxMassPhi = 0.f; + float mMinMassKstar = 0.f; + float mMaxMassKstar = 0.f; + + // For extracting strangeness mass QA plots + struct : ConfigurableGroup { + std::string prefix = "axesConfigurations"; + ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; + // Daughter/trigger-track QA axes (Phi->KK, K*0->Kpi, trigger hadrons): + // each is a 1D QA histogram's own axis, booked in init(). + ConfigurableAxis axisEtaQA{"axisEtaQA", {100, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisNSigmaQA{"axisNSigmaQA", {100, -10.0f, 10.0f}, "n#sigma"}; + ConfigurableAxis axisDCAxyQA{"axisDCAxyQA", {200, -0.5f, 0.5f}, "DCA_{xy} (cm)"}; + ConfigurableAxis axisDCAzQA{"axisDCAzQA", {200, -0.5f, 0.5f}, "DCA_{z} (cm)"}; + ConfigurableAxis axisTPCCrossedRowsQA{"axisTPCCrossedRowsQA", {160, 0, 160}, "TPC crossed rows"}; + ConfigurableAxis axisVertexZQA{"axisVertexZQA", {60, -15.0f, 15.0f}, "Vertex Z (cm)"}; + ConfigurableAxis axisCentralityQA{"axisCentralityQA", {100, 0.0f, 100.0f}, "Centrality (%)"}; + } axesConfigurations; + + // QA + struct : ConfigurableGroup { + std::string prefix = "qaConfigurations"; + Configurable doTrueSelectionInMass{"doTrueSelectionInMass", false, "Fill mass histograms only with true primary Particles for MC"}; + } qaConfigurations; + + // Do declarative selections for DCAs, if possible + Filter acceptanceFilter = (nabs(aod::track::eta) < configTracks.cfgCutEta && aod::track::pt > configTracks.cMinPtcut) && + (nabs(aod::track::dcaXY) < configTracks.dcaXYconstant + configTracks.dcaXYpTdep * nabs(aod::track::signed1Pt)) && + (nabs(aod::track::dcaZ) < configTracks.cMaxDCAzToPVcut); + + // All four aliases below join TrackSelection + TrackSelectionExtension so + // that trackCut() -- the shared "is this a good, well-reconstructed track" + // baseline -- is callable (and applied) on trigger and associated-particle + // tracks exactly as it already is on Phi/K*0 daughters (TrackCandidates), + // not just on the latter. + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; + using DauTracks = soa::Join; + using DauTracksMC = soa::Join; + // using IDTracks= soa::Join; // prepared for Bayesian PID + using IDTracks = soa::Join; + using IDTracksMC = soa::Join; + + Produces triggerTrack; + Produces triggerTrackExtra; + Produces assocPhis; + Produces assocKstars; + Produces assocHadrons; + Produces assocPID; + + using EventCandidates = soa::Join; + using TrackCandidates = soa::Filtered>; + + // for MC reco + using MCEventCandidates = soa::Join; + using MCTrackCandidates = soa::Filtered>; + + struct TriggCandidate { + float pt = 0.f; + int collisionId = -1; + int trackId = -1; + bool isPhysicalPrimary = false; + float origPt = 0.f; + }; + TriggCandidate thisTrigg; + + std::vector triggerCandidates; + + void init(InitContext const&) + { + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + // Z-vertex and centrality of selected events (filled in isSelectedEvents() + // once a collision passes every cut, i.e. the same "All Passed Events" + // point as CollCutCounts bin 8). Centrality source for the latter is + // configEvents.cfgUseFV0ACentrality: FT0C for O-O, FV0A for p-O. + histos.add("hVertexZSelected", "Vertex Z of selected events", kTH1F, {axesConfigurations.axisVertexZQA}); + histos.add("hCentralitySelected", "Centrality of selected events", kTH1F, {axesConfigurations.axisCentralityQA}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); + + mMinMassPhi = massWindowConfigurationsPhi.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMaxMassPhi = massWindowConfigurationsPhi.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsPhi.sigma; + mMinMassKstar = massWindowConfigurationsKstar.peakMass - massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; + mMaxMassKstar = massWindowConfigurationsKstar.peakMass + massWindowConfigurations.maxMassNSigma * massWindowConfigurationsKstar.sigma; + LOGF(info, "Assoc mass pre-cut windows: Phi [%.4f, %.4f] GeV, K*0 [%.4f, %.4f] GeV", + mMinMassPhi, mMaxMassPhi, mMinMassKstar, mMaxMassKstar); + + // QA histograms for Phi (K+K-) and K*0 (K-pi) daughter tracks: booked once + // per daughter species (Phi's kaon, Kstar's kaon, Kstar's pion), filled at + // the point each candidate passes all cuts (including the mass window) in + // processPhis/processKstars (and their MC counterparts). + auto bookDaughterQA = [&](const std::string& dir) { + histos.add((dir + "/hPt").c_str(), "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add((dir + "/hEta").c_str(), "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add((dir + "/hTPCNSigma").c_str(), "TPC n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hTOFNSigma").c_str(), "TOF n#sigma", kTH1F, {axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hDCAxy").c_str(), "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hDCAz").c_str(), "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hTPCCrossedRows").c_str(), "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + // 2D pT-binned QA, same variables as above, to see pT dependence + histos.add((dir + "/hPtVsDCAxy").c_str(), "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add((dir + "/hPtVsDCAz").c_str(), "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); + histos.add((dir + "/hPtVsTPCNSigma").c_str(), "p_{T} vs TPC n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + histos.add((dir + "/hPtVsTOFNSigma").c_str(), "p_{T} vs TOF n#sigma", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisNSigmaQA}); + // TPC vs TOF nSigma correlation, for the usual PID "banana plot" check + histos.add((dir + "/hTPCNSigmaVsTOFNSigma").c_str(), "TPC n#sigma vs TOF n#sigma", kTH2F, {axesConfigurations.axisNSigmaQA, axesConfigurations.axisNSigmaQA}); + }; + bookDaughterQA("QA/Phi/Kaon"); + bookDaughterQA("QA/Kstar/Kaon"); + bookDaughterQA("QA/Kstar/Pion"); + + // Trigger hadron QA: no PID applied to trigger tracks, so only the 5 + // non-PID variables are booked (pt, eta, dcaXY, dcaZ, TPC crossed rows), + // plus the pT-binned 2D versions of DCAxy/DCAz (no nSigma -- no PID here). + histos.add("QA/TriggerHadron/hPt", "p_{T}", kTH1F, {axesConfigurations.axisPtQA}); + histos.add("QA/TriggerHadron/hEta", "#eta", kTH1F, {axesConfigurations.axisEtaQA}); + histos.add("QA/TriggerHadron/hDCAxy", "DCA_{xy}", kTH1F, {axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hDCAz", "DCA_{z}", kTH1F, {axesConfigurations.axisDCAzQA}); + histos.add("QA/TriggerHadron/hTPCCrossedRows", "TPC crossed rows", kTH1F, {axesConfigurations.axisTPCCrossedRowsQA}); + histos.add("QA/TriggerHadron/hPtVsDCAxy", "p_{T} vs DCA_{xy}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAxyQA}); + histos.add("QA/TriggerHadron/hPtVsDCAz", "p_{T} vs DCA_{z}", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisDCAzQA}); + } + + // HIST() needs a compile-time literal path, so each daughter species gets + // its own explicit fill function rather than one generic/parameterized + // helper. Kaon QA uses the Ka PID accessors, pion QA uses the Pi ones. + template + void fillPhiKaonQA(const Track& track) + { + histos.fill(HIST("QA/Phi/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Phi/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Phi/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Phi/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Phi/Kaon/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + } + + template + void fillKstarKaonQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Kaon/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Kaon/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCNSigma"), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hTOFNSigma"), track.tofNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("QA/Kstar/Kaon/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Kstar/Kaon/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaKa(), track.tofNSigmaKa()); + } + } + + template + void fillKstarPionQA(const Track& track) + { + histos.fill(HIST("QA/Kstar/Pion/hPt"), track.pt()); + histos.fill(HIST("QA/Kstar/Pion/hEta"), track.eta()); + histos.fill(HIST("QA/Kstar/Pion/hTPCNSigma"), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hTOFNSigma"), track.tofNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("QA/Kstar/Pion/hPtVsTOFNSigma"), track.pt(), track.tofNSigmaPi()); + if (track.hasTOF()) { + histos.fill(HIST("QA/Kstar/Pion/hTPCNSigmaVsTOFNSigma"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + } + + // Trigger hadrons carry no PID selection, so only the 5 non-PID variables + // are filled here. + template + void fillTriggerHadronQA(const Track& track) + { + histos.fill(HIST("QA/TriggerHadron/hPt"), track.pt()); + histos.fill(HIST("QA/TriggerHadron/hEta"), track.eta()); + histos.fill(HIST("QA/TriggerHadron/hDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hDCAz"), track.dcaZ()); + histos.fill(HIST("QA/TriggerHadron/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QA/TriggerHadron/hPtVsDCAz"), track.pt(), track.dcaZ()); + } + + template + bool isSelectedEvents(const Coll& collision, bool fillHist = true) + { + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) { + return true; + } + if (!condition) { + return false; + } + if (fillHist) { + histos.fill(HIST("CollCutCounts"), bin); + } + return true; + }; + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 0); + } + + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { + return false; + } + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 8); + histos.fill(HIST("hVertexZSelected"), collision.posZ()); + // Guarded with if constexpr: isSelectedEvents() is called with several + // different collision join types (some callers pass fillHist=false and + // don't join centrality tables at all), so this only compiles/fills for + // callers whose collision type actually has both estimators joined. + if constexpr (requires { collision.centFT0C(); collision.centFV0A(); }) { + histos.fill(HIST("hCentralitySelected"), configEvents.cfgUseFV0ACentrality ? collision.centFV0A() : collision.centFT0C()); + } + } + + return true; + } + + template + bool trackCut(const TrackType& track) + { + // basic track cuts + // NOTE: pT-dependent |DCAxy| cut is NOT re-applied here -- it's already + // enforced unconditionally, for every track in every process function, + // by acceptanceFilter (Filter on aod::track::dcaXY using the live + // configTracks.dcaXYconstant/dcaXYpTdep). A second check here would be + // either a no-op (same cut) or a stale one (if those configurables are + // ever retuned and this duplicate doesn't track them). + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { + return false; + } + if (configTracks.cfgPVContributor && !track.isPVContributor()) { + return false; + } + if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) { + return false; + } + if (track.tpcChi2NCl() > configTracks.cfgMaxTPCChi2NCl) { + return false; + } + if (track.itsChi2NCl() > configTracks.cfgMaxITSChi2NCl) { + return false; + } + if (track.tpcCrossedRowsOverFindableCls() < configTracks.cfgMinTPCCrossedRowsOverFindableCls) { + return false; + } + if (track.itsNCls() < configTracks.cfgMinITSNCls) { + return false; + } + + return true; + } + + template + bool ptDependentPidKaon(const T& candidate) + { + auto vKaonTPCPIDpTintv = configPID.kaonTPCPIDpTintv.value; + vKaonTPCPIDpTintv.insert(vKaonTPCPIDpTintv.begin(), configTracks.cMinPtcut); + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTOFPIDpTintv = configPID.kaonTOFPIDpTintv.value; + auto vKaonTPCTOFCombinedpTintv = configPID.kaonTPCTOFCombinedpTintv.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; + auto vKaonTOFPIDcuts = configPID.kaonTOFPIDcuts.value; + + float pt = candidate.pt(); + float ptSwitchToTOF = vKaonTPCPIDpTintv.back(); + float tpcNsigmaKa = candidate.tpcNSigmaKa(); + float tofNsigmaKa = candidate.tofNSigmaKa(); + + bool tpcPIDPassed = false; + + // TPC PID interval-based check + for (size_t i = 0; i < vKaonTPCPIDpTintv.size() - 1; ++i) { + if (pt > vKaonTPCPIDpTintv[i] && pt < vKaonTPCPIDpTintv[i + 1]) { + if (std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts[i]) { + tpcPIDPassed = true; + break; + } + } + } + + // TOF bypass option + if (configPID.cByPassTOF) { + return std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back(); + } + + // Case 1: No TOF and pt ≤ ptSwitch → use TPC-only + if (!candidate.hasTOF() && pt <= ptSwitchToTOF) { + return tpcPIDPassed; + } + + // Case 2: No TOF but pt > ptSwitch → reject + if (!candidate.hasTOF() && pt > ptSwitchToTOF) { + return false; + } + + // Case 3: TOF is available → apply TPC+TOF PID logic + if (candidate.hasTOF()) { + if (configPID.cPIDcutType == SquareType) { + // Rectangular cut + for (size_t i = 0; i < vKaonTOFPIDpTintv.size(); ++i) { + if (pt < vKaonTOFPIDpTintv[i]) { + if (std::abs(tofNsigmaKa) < vKaonTOFPIDcuts[i] && + std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back()) { + return true; + } + } + } + } else if (configPID.cPIDcutType == CircularType) { + // Circular cut + for (size_t i = 0; i < vKaonTPCTOFCombinedpTintv.size(); ++i) { + if (pt < vKaonTPCTOFCombinedpTintv[i]) { + float combinedSigma2 = tpcNsigmaKa * tpcNsigmaKa + + tofNsigmaKa * tofNsigmaKa; + if (combinedSigma2 < vKaonTPCTOFCombinedPIDcuts[i] * vKaonTPCTOFCombinedPIDcuts[i]) { + return true; + } + } + } + } + } + + return false; + } + + template + bool selectionPID(const T& candidate) + { + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; + + if (!configPID.cByPassTOF && candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (vKaonTPCTOFCombinedPIDcuts[0] * vKaonTPCTOFCombinedPIDcuts[0])) { + return true; + } + if (!configPID.cByPassTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { + return true; + } + if (configPID.cByPassTOF && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { + return true; + } + return false; + } + + template + bool ptDependentPidPion(const T& candidate) + { + auto vPionTPCPIDpTintv = configPID.pionTPCPIDpTintv.value; + vPionTPCPIDpTintv.insert(vPionTPCPIDpTintv.begin(), configTracks.cMinPtcut); + auto vPionTPCPIDcuts = configPID.pionTPCPIDcuts.value; + auto vPionTOFPIDpTintv = configPID.pionTOFPIDpTintv.value; + auto vPionTPCTOFCombinedpTintv = configPID.pionTPCTOFCombinedpTintv.value; + auto vPionTPCTOFCombinedPIDcuts = configPID.pionTPCTOFCombinedPIDcuts.value; + auto vPionTOFPIDcuts = configPID.pionTOFPIDcuts.value; + + float pt = candidate.pt(); + float ptSwitchToTOF = vPionTPCPIDpTintv.back(); + float tpcNsigmaPi = candidate.tpcNSigmaPi(); + float tofNsigmaPi = candidate.tofNSigmaPi(); + + bool tpcPIDPassed = false; + + // TPC PID interval-based check + for (size_t i = 0; i < vPionTPCPIDpTintv.size() - 1; ++i) { + if (pt > vPionTPCPIDpTintv[i] && pt < vPionTPCPIDpTintv[i + 1]) { + if (std::abs(tpcNsigmaPi) < vPionTPCPIDcuts[i]) { + tpcPIDPassed = true; + break; + } + } + } + + // TOF bypass option + if (configPID.cByPassTOF) { + return std::abs(tpcNsigmaPi) < vPionTPCPIDcuts.back(); + } + + // Case 1: No TOF and pt ≤ ptSwitch → use TPC-only + if (!candidate.hasTOF() && pt <= ptSwitchToTOF) { + return tpcPIDPassed; + } + + // Case 2: No TOF but pt > ptSwitch → reject + if (!candidate.hasTOF() && pt > ptSwitchToTOF) { + return false; + } + + // Case 3: TOF is available → apply TPC+TOF PID logic + if (candidate.hasTOF()) { + if (configPID.cPIDcutType == SquareType) { + // Rectangular cut + for (size_t i = 0; i < vPionTOFPIDpTintv.size(); ++i) { + if (pt < vPionTOFPIDpTintv[i]) { + if (std::abs(tofNsigmaPi) < vPionTOFPIDcuts[i] && + std::abs(tpcNsigmaPi) < vPionTPCPIDcuts.back()) { + return true; + } + } + } + } else if (configPID.cPIDcutType == CircularType) { + // Circular cut + for (size_t i = 0; i < vPionTPCTOFCombinedpTintv.size(); ++i) { + if (pt < vPionTPCTOFCombinedpTintv[i]) { + float combinedSigma2 = tpcNsigmaPi * tpcNsigmaPi + + tofNsigmaPi * tofNsigmaPi; + if (combinedSigma2 < vPionTPCTOFCombinedPIDcuts[i] * vPionTPCTOFCombinedPIDcuts[i]) { + return true; + } + } + } + } + } + + return false; + } + + template + bool selectionPIDPion(const T& candidate) + { + auto vPionTPCPIDcuts = configPID.pionTPCPIDcuts.value; + auto vPionTPCTOFCombinedPIDcuts = configPID.pionTPCTOFCombinedPIDcuts.value; + + if (!configPID.cByPassTOF && candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (vPionTPCTOFCombinedPIDcuts[0] * vPionTPCTOFCombinedPIDcuts[0])) { + return true; + } + if (!configPID.cByPassTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < vPionTPCPIDcuts[0]) { + return true; + } + if (configPID.cByPassTOF && std::abs(candidate.tpcNSigmaPi()) < vPionTPCPIDcuts[0]) { + return true; + } + return false; + } + + // reco-level trigger quality checks (N.B.: DCA is filtered, not selected) + template + bool isValidTrigger(TTrack const& track) + { + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(track)) { + return false; + } + if (track.eta() > configTriggerTracks.triggerEtaMax || track.eta() < configTriggerTracks.triggerEtaMin) { + return false; + } + // if (track.sign()= 1 ) {continue;} + if (track.pt() > configTriggerTracks.triggerPtCutMax || track.pt() < configTriggerTracks.triggerPtCutMin) { + return false; + } + if (track.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { + return false; // crossed rows + } + if (!track.hasITS() && configTriggerTracks.triggerRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + if (track.tpcNClsShared() > configTriggerTracks.triggerMaxTPCSharedClusters) { + return false; // skip, has shared clusters + } + if (!(BIT_CHECK(track.itsClusterMap(), 0)) && configTriggerTracks.triggerRequireL0) { + return false; // skip, doesn't have cluster in ITS L0 + } + return true; + } + + // Species tag for isValidAssocTrack()'s PID gate below. AssocPion + // and AssocKaon select which nSigma cut direction applies (accept that + // species, reject the other two); AssocHadron carries no PID logic of its + // own -- passed for clarity at Hadron call sites, but never actually reaches + // the `if constexpr (Species == AssocKaon) ... else ...` branch, since the + // outer `requires { assoc.tofSignal(); }` is already false for FullTracks + // (Hadron's track type, which has no PID columns at all). + enum AssocSpecies { AssocPion = 0, + AssocKaon = 1, + AssocHadron = 2 }; + + // Merged predicate for the Pion/Kaon/Hadron associated-track pools. Pion and + // Kaon both run over IDTracks/IDTracksMC (identical C++ type), so the two + // PID directions cannot be told apart by a `requires{}` type check the way + // Hadron (FullTracks, no PID columns) is told apart from the other two -- + // hence the explicit non-type template parameter instead. Everything except + // the PID nSigma cut direction (phase-space cuts, MC-truth lookup, table + // fills) is identical across all three species, so it stays unduplicated + // here; only the accept/reject block branches on Species. + template + bool isValidAssocTrack(TTrack const& assoc) + { + static_assert(Species == AssocPion || Species == AssocKaon || Species == AssocHadron, + "isValidAssocTrack: unknown species tag"); + // Shared "good track" baseline, applied identically across trigger, + // associated-particle, and resonance-daughter tracks (see trackCut()). + if (!trackCut(assoc)) { + return false; + } + if (assoc.eta() > configAssocTracks.assocEtaMax || assoc.eta() < configAssocTracks.assocEtaMin) { + return false; + } + if (assoc.pt() > configAssocTracks.assocPtCutMax || assoc.pt() < configAssocTracks.assocPtCutMin) { + return false; + } + if (assoc.tpcNClsCrossedRows() < configTracks.minTPCNCrossedRows) { + return false; // crossed rows + } + if (!assoc.hasITS() && configAssocTracks.assocRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + + // do this only if information is available + float nSigmaTPCTOF[8] = {-10, -10, -10, -10, -10, -10, -10, -10}; + if constexpr (requires { assoc.tofSignal(); } && !requires { assoc.mcParticle(); }) { + if (assoc.tofSignal() > 0) { + if constexpr (Species == AssocKaon) { + if (std::sqrt(assoc.tofNSigmaKa() * assoc.tofNSigmaKa() + assoc.tpcNSigmaKa() * assoc.tpcNSigmaKa()) > configPID.assocKaonNSigmaTPCFOF) + return false; + if (assoc.tofNSigmaPr() < configPID.rejectSigma) + return false; + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) + return false; + if (assoc.tofNSigmaPi() < configPID.rejectSigma) + return false; + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) + return false; + } else { + if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > configPID.assocPionNSigmaTPCFOF) + return false; + if (assoc.tofNSigmaPr() < configPID.rejectSigma) + return false; + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) + return false; + if (assoc.tofNSigmaKa() < configPID.rejectSigma) + return false; + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) + return false; + } + nSigmaTPCTOF[4] = assoc.tofNSigmaPi(); + nSigmaTPCTOF[5] = assoc.tofNSigmaKa(); + nSigmaTPCTOF[6] = assoc.tofNSigmaPr(); + nSigmaTPCTOF[7] = assoc.tofNSigmaEl(); + } else { + if constexpr (Species == AssocKaon) { + if (assoc.tpcNSigmaKa() > configPID.assocKaonNSigmaTPCFOF) + return false; + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) + return false; + if (assoc.tpcNSigmaPi() < configPID.rejectSigma) + return false; + } else { + if (assoc.tpcNSigmaPi() > configPID.assocPionNSigmaTPCFOF) + return false; + if (assoc.tpcNSigmaPr() < configPID.rejectSigma) + return false; + if (assoc.tpcNSigmaKa() < configPID.rejectSigma) + return false; + } + } + nSigmaTPCTOF[0] = assoc.tpcNSigmaPi(); + nSigmaTPCTOF[1] = assoc.tpcNSigmaKa(); + nSigmaTPCTOF[2] = assoc.tpcNSigmaPr(); + nSigmaTPCTOF[3] = assoc.tpcNSigmaEl(); + } + + bool physicalPrimary = false; + float origPt = -1; + float code = -9999; + if constexpr (requires { assoc.mcParticle(); }) { + if (assoc.has_mcParticle()) { + auto mcParticle = assoc.mcParticle(); + physicalPrimary = mcParticle.isPhysicalPrimary(); + origPt = mcParticle.pt(); + code = mcParticle.pdgCode(); + } + } + + // Rapidity under the species mass hypothesis; AssocHadron has no mass + // hypothesis to assume, so it's filled with a dummy value instead. + float rapidity = -999.f; + if constexpr (Species == AssocKaon) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassKPlus) + .Rapidity(); + } else if constexpr (Species == AssocPion) { + rapidity = LorentzVectorPtEtaPhiMass(assoc.pt(), assoc.eta(), assoc.phi(), + o2::constants::physics::MassPiPlus) + .Rapidity(); + } + + assocHadrons( + assoc.collisionId(), + physicalPrimary, + assoc.globalIndex(), + origPt, + code, + rapidity); + assocPID( + nSigmaTPCTOF[0], + nSigmaTPCTOF[1], + nSigmaTPCTOF[2], + nSigmaTPCTOF[3], + nSigmaTPCTOF[4], + nSigmaTPCTOF[5], + nSigmaTPCTOF[6], + nSigmaTPCTOF[7]); + return true; + } + + // for real data processing + void processTriggers(soa::Join::iterator const& collision, soa::Filtered const& tracks) + { + triggerCandidates.clear(); + if (!isSelectedEvents(collision)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + double leadingPt = -1.; + int leadingId = -1; + for (auto const& track : tracks) { + if (!isValidTrigger(track)) + continue; + fillTriggerHadronQA(track); + thisTrigg.pt = track.pt(); + thisTrigg.trackId = track.globalIndex(); + thisTrigg.collisionId = track.collisionId(); + thisTrigg.isPhysicalPrimary = false; // if you decide to check real data for primaries, you'll have a hard time + thisTrigg.origPt = 0; + triggerCandidates.push_back(thisTrigg); + if (track.pt() > leadingPt) { + leadingPt = track.pt(); + leadingId = track.globalIndex(); + } + } + for (auto const& TriggCandidate : triggerCandidates) { + bool isLeading = (leadingId == TriggCandidate.trackId); + triggerTrack( + TriggCandidate.collisionId, + TriggCandidate.isPhysicalPrimary, + TriggCandidate.trackId, + TriggCandidate.origPt, + isLeading); + triggerTrackExtra(1); + } + } + + // for MC processing + void processTriggersMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) + { + triggerCandidates.clear(); + if (!isSelectedEvents(collision)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + double leadingPt = -1.; + int leadingId = -1; + for (auto const& track : tracks) { + if (!isValidTrigger(track)) + continue; + fillTriggerHadronQA(track); + thisTrigg.pt = track.pt(); + thisTrigg.trackId = track.globalIndex(); + thisTrigg.collisionId = track.collisionId(); + if (track.has_mcParticle()) { + auto mcParticle = track.mcParticle(); + thisTrigg.isPhysicalPrimary = mcParticle.isPhysicalPrimary(); + thisTrigg.origPt = mcParticle.pt(); + } + triggerCandidates.push_back(thisTrigg); + if (track.pt() > leadingPt) { + leadingPt = track.pt(); + leadingId = track.globalIndex(); + } + } + + for (auto const& TriggCandidate : triggerCandidates) { + bool isLeading = (leadingId == TriggCandidate.trackId); + triggerTrack( + TriggCandidate.collisionId, + TriggCandidate.isPhysicalPrimary, + TriggCandidate.trackId, + TriggCandidate.origPt, + isLeading); + triggerTrackExtra(1); + } + } + + void processAssocPions(soa::Join::iterator const& collision, soa::Filtered const& tracks) + { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocPionsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) + { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocKaons(soa::Join::iterator const& collision, soa::Filtered const& tracks) + { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocKaonsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) + { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processAssocHadrons(soa::Join::iterator const& collision, soa::Filtered const& tracks) + { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) + { + // Perform basic event selection (fillHist=false: processTriggers/processTriggersMC own the shared CollCutCounts fill) + if (!isSelectedEvents(collision, false)) { + return; + } + + /// _________________________________________________ + /// Step 1: Populate table with trigger tracks + for (auto const& track : tracks) { + if (!isValidAssocTrack(track)) + continue; + } + } + + void processPhis(EventCandidates::iterator const& collision, + TrackCandidates const& tracks) + { + if (!isSelectedEvents(collision, false)) { + return; + } + + LorentzVectorPtEtaPhiMass kplus; + LorentzVectorPtEtaPhiMass kminus; + LorentzVectorPtEtaPhiMass phi; + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + if (trk2.index() <= trk1.index()) { + continue; + } + + // same collision + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + // quality cuts + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + // unlike-sign only + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + // kaon PID + if (configPID.ispTdepPID) { + if (!ptDependentPidKaon(trk1) || + !ptDependentPidKaon(trk2)) { + continue; + } + } else { + if (!selectionPID(trk1) || + !selectionPID(trk2)) { + continue; + } + } + + // assign K+ and K- + if (trk1.sign() > 0) { + kplus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + } else { + kplus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + } + + phi = kplus + kminus; + + float invMass = phi.M(); + + // rapidity cut + if (std::abs(phi.Rapidity()) > configResoDauTracks.cfgCutRapidity) { + continue; + } + + // optional mass window + if (invMass < mMinMassPhi || invMass > mMaxMassPhi) { + continue; + } + + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + + assocPhis( + collision.globalIndex(), + false, + false, + phi.Pt(), + phi.Eta(), + phi.Phi(), + invMass, + phi.Rapidity(), + trk1.globalIndex(), + trk2.globalIndex()); + } + } + + void processPhisMC( + MCEventCandidates::iterator const& collision, + aod::McCollisions const&, + MCTrackCandidates const& tracks, + aod::McParticles const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!isSelectedEvents(collision, false)) { + return; + } + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + // ----------------------------- + // Same cuts as phi1020analysis + // ----------------------------- + + if (trk2.index() <= trk1.index()) { + continue; + } + + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + if (!selectionPID(trk1) || !selectionPID(trk2)) { + continue; + } + + LorentzVectorPtEtaPhiMass kplus; + LorentzVectorPtEtaPhiMass kminus; + LorentzVectorPtEtaPhiMass phi; + + // assign K+ and K- + if (trk1.sign() > 0) { + kplus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + } else { + kplus = LorentzVectorPtEtaPhiMass(trk2.pt(), trk2.eta(), trk2.phi(), + o2::constants::physics::MassKPlus); + kminus = LorentzVectorPtEtaPhiMass(trk1.pt(), trk1.eta(), trk1.phi(), + o2::constants::physics::MassKPlus); + } + + phi = kplus + kminus; + + float invMass = phi.M(); + + // rapidity cut + if (std::abs(phi.Rapidity()) > configResoDauTracks.cfgCutRapidity) { + continue; + } + + // optional mass window + if (invMass < mMinMassPhi || invMass > mMaxMassPhi) { + continue; + } + + // Daughter QA (both legs are kaons for Phi) + fillPhiKaonQA(trk1); + fillPhiKaonQA(trk2); + + bool mcTruePhi = false; + bool mcPhysicalPrimary = false; + + // ----------------------------- + // MC matching + // ----------------------------- + + if (trk1.has_mcParticle() && trk2.has_mcParticle()) { + + auto mc1 = trk1.mcParticle(); + auto mc2 = trk2.mcParticle(); + + if (mc1.has_mothers() && mc2.has_mothers()) { + + for (auto const& mother1 : mc1.mothers_as()) { + for (auto const& mother2 : mc2.mothers_as()) { + + if (mother1.globalIndex() != mother2.globalIndex()) { + continue; + } + + if (std::abs(mother1.pdgCode()) != 333) { + continue; + } + + mcTruePhi = true; + mcPhysicalPrimary = mother1.isPhysicalPrimary(); + } + } + } + } + + assocPhis( + collision.globalIndex(), + mcTruePhi, + mcPhysicalPrimary, + phi.Pt(), + phi.Eta(), + phi.Phi(), + invMass, + phi.Rapidity(), + trk1.globalIndex(), + trk2.globalIndex()); + } + } + + // K*0 -> K+ pi- (and c.c.) reconstruction. Unlike phi (K+K-, symmetric daughter + // mass), the two daughter tracks are different species, so both mass hypotheses + // (trk1=kaon,trk2=pion) and (trk1=pion,trk2=kaon) are tried independently; either, + // both, or neither may pass PID for a given unlike-sign pair. + void processKstars(EventCandidates::iterator const& collision, + TrackCandidates const& tracks) + { + if (!isSelectedEvents(collision, false)) { + return; + } + + LorentzVectorPtEtaPhiMass kaon; + LorentzVectorPtEtaPhiMass pion; + LorentzVectorPtEtaPhiMass kstar; + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + if (trk2.index() <= trk1.index()) { + continue; + } + + // same collision + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + // quality cuts + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + // unlike-sign only + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + for (int hypothesis = 0; hypothesis < 2; hypothesis++) { + auto const& kaonTrack = (hypothesis == 0) ? trk1 : trk2; + auto const& pionTrack = (hypothesis == 0) ? trk2 : trk1; + + // kaon + pion PID + bool passKaonPID = configPID.ispTdepPID ? ptDependentPidKaon(kaonTrack) : selectionPID(kaonTrack); + bool passPionPID = configPID.ispTdepPID ? ptDependentPidPion(pionTrack) : selectionPIDPion(pionTrack); + if (!passKaonPID || !passPionPID) { + continue; + } + + kaon = LorentzVectorPtEtaPhiMass(kaonTrack.pt(), kaonTrack.eta(), kaonTrack.phi(), + o2::constants::physics::MassKPlus); + pion = LorentzVectorPtEtaPhiMass(pionTrack.pt(), pionTrack.eta(), pionTrack.phi(), + o2::constants::physics::MassPiPlus); + + kstar = kaon + pion; + + float invMass = kstar.M(); + + // rapidity cut + if (std::abs(kstar.Rapidity()) > configResoDauTracks.cfgCutRapidity) { + continue; + } + + // mass window + if (invMass < mMinMassKstar || invMass > mMaxMassKstar) { + continue; + } + + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + + auto const& posTrack = (kaonTrack.sign() > 0) ? kaonTrack : pionTrack; + auto const& negTrack = (kaonTrack.sign() > 0) ? pionTrack : kaonTrack; + // Rapidity under whichever mass hypothesis (kaon or pion) was + // actually assigned to that daughter for this hypothesis -- matches + // posTrack/negTrack above, not a fixed species per pos/neg slot. + + assocKstars( + collision.globalIndex(), + false, + false, + kstar.Pt(), + kstar.Eta(), + kstar.Phi(), + invMass, + kstar.Rapidity(), + posTrack.globalIndex(), + negTrack.globalIndex()); + } + } + } + + void processKstarsMC( + MCEventCandidates::iterator const& collision, + aod::McCollisions const&, + MCTrackCandidates const& tracks, + aod::McParticles const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!isSelectedEvents(collision, false)) { + return; + } + + for (auto const& [trk1, trk2] : + combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + // ----------------------------- + // Same cuts as processKstars + // ----------------------------- + + if (trk2.index() <= trk1.index()) { + continue; + } + + if (trk1.collisionId() != collision.globalIndex() || + trk2.collisionId() != collision.globalIndex()) { + continue; + } + + if (!trackCut(trk1) || !trackCut(trk2)) { + continue; + } + + if (trk1.sign() * trk2.sign() > 0) { + continue; + } + + for (int hypothesis = 0; hypothesis < 2; hypothesis++) { + auto const& kaonTrack = (hypothesis == 0) ? trk1 : trk2; + auto const& pionTrack = (hypothesis == 0) ? trk2 : trk1; + + if (!selectionPID(kaonTrack) || !selectionPIDPion(pionTrack)) { + continue; + } + + LorentzVectorPtEtaPhiMass kaon = LorentzVectorPtEtaPhiMass(kaonTrack.pt(), kaonTrack.eta(), kaonTrack.phi(), + o2::constants::physics::MassKPlus); + LorentzVectorPtEtaPhiMass pion = LorentzVectorPtEtaPhiMass(pionTrack.pt(), pionTrack.eta(), pionTrack.phi(), + o2::constants::physics::MassPiPlus); + LorentzVectorPtEtaPhiMass kstar = kaon + pion; + + float invMass = kstar.M(); + + // rapidity cut + if (std::abs(kstar.Rapidity()) > configResoDauTracks.cfgCutRapidity) { + continue; + } + + // mass window + if (invMass < mMinMassKstar || invMass > mMaxMassKstar) { + continue; + } + + // Daughter QA (kaon leg and pion leg filled separately) + fillKstarKaonQA(kaonTrack); + fillKstarPionQA(pionTrack); + + bool mcTrueKstar = false; + bool mcPhysicalPrimary = false; + + // ----------------------------- + // MC matching: common mother with |pdgCode| == 313 (K*0/anti-K*0) + // ----------------------------- + + if (kaonTrack.has_mcParticle() && pionTrack.has_mcParticle()) { + + auto mcKaon = kaonTrack.mcParticle(); + auto mcPion = pionTrack.mcParticle(); + + if (mcKaon.has_mothers() && mcPion.has_mothers()) { + + for (auto const& motherKaon : mcKaon.mothers_as()) { + for (auto const& motherPion : mcPion.mothers_as()) { + + if (motherKaon.globalIndex() != motherPion.globalIndex()) { + continue; + } + + if (std::abs(motherKaon.pdgCode()) != 313) { + continue; + } + + mcTrueKstar = true; + mcPhysicalPrimary = motherKaon.isPhysicalPrimary(); + } + } + } + } + + auto const& posTrack = (kaonTrack.sign() > 0) ? kaonTrack : pionTrack; + auto const& negTrack = (kaonTrack.sign() > 0) ? pionTrack : kaonTrack; + + assocKstars( + collision.globalIndex(), + mcTrueKstar, + mcPhysicalPrimary, + kstar.Pt(), + kstar.Eta(), + kstar.Phi(), + invMass, + kstar.Rapidity(), + posTrack.globalIndex(), + negTrack.globalIndex()); + } + } + } + + PROCESS_SWITCH(HResonanceCorrelationFilter, processTriggers, "Produce trigger tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processTriggersMC, "Produce trigger tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocPions, "Produce associated Pion tables", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocPionsMC, "Produce associated Pion tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocKaons, "Produce associated Kaon tables", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocKaonsMC, "Produce associated Kaon tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocHadrons, "Produce associated Hadron tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processAssocHadronsMC, "Produce associated Hadron tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processPhis, "Produce associated phi tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processPhisMC, "Produce associated phi tables for MC", false); + PROCESS_SWITCH(HResonanceCorrelationFilter, processKstars, "Produce associated K*0 tables", true); + PROCESS_SWITCH(HResonanceCorrelationFilter, processKstarsMC, "Produce associated K*0 tables for MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Resonances/phiflow.cxx b/PWGLF/TableProducer/Resonances/phiflow.cxx new file mode 100644 index 00000000000..8504d9e1e6a --- /dev/null +++ b/PWGLF/TableProducer/Resonances/phiflow.cxx @@ -0,0 +1,394 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiflow.cxx +/// \brief Table producer for Phi FLow +/// +/// \author prottay.das@cern.ch + +#include "PWGLF/DataModel/LFPhiFlowTables.h" +#include "PWGLF/DataModel/SPCalibrationTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; + +struct phiflow { + + Produces kaonEvent; + Produces kaonTrack; + + struct : ConfigurableGroup { + Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + } rctCut; + + Configurable useNoCollInTimeRangeStandard{"useNoCollInTimeRangeStandard", false, "Apply kNoCollInTimeRangeStandard selection bit"}; + Configurable useGoodITSLayersAll{"useGoodITSLayersAll", true, "Apply kIsGoodITSLayersAll selection bit"}; + Configurable cfgCutOccupancy{"cfgCutOccupancy", 2000, "Occupancy cut"}; + + // events + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgCutCentralityMax{"cfgCutCentralityMax", 80.0f, "Accepted maximum Centrality"}; + Configurable cfgCutCentralityMin{"cfgCutCentralityMin", 0.0f, "Accepted minimum Centrality"}; + + // Configs for track filtering + Configurable cfgCutCharge{"cfgCutCharge", 0.0, "Charge cut on daughter"}; + Configurable cfgCutPt{"cfgCutPt", 0.2, "Pt cut on daughter track"}; + Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut on daughter track"}; + Configurable cfgCutDCAxy{"cfgCutDCAxy", 0.1, "DCAxy cut on daughter track"}; + Configurable cfgCutDCAz{"cfgCutDCAz", 0.1, "DCAz cut on daughter track"}; + Configurable nsigmaCutTPC{"nsigmaCutTPC", 3.0, "Maximum nsigma cut TPC for filtered kaon track"}; + + // Configs for kaon + struct : ConfigurableGroup { + Configurable itsPIDSelection{"itsPIDSelection", true, "PID ITS"}; + Configurable lowITSPIDNsigma{"lowITSPIDNsigma", -3.0, "lower cut on PID nsigma for ITS"}; + Configurable highITSPIDNsigma{"highITSPIDNsigma", 3.0, "higher cut on PID nsigma for ITS"}; + Configurable itsclusterKaMeson{"itsclusterKaMeson", 5, "Minimum number of ITS cluster for kaon meson track"}; + Configurable tpcCrossedRowsKaMeson{"tpcCrossedRowsKaMeson", 80, "Minimum number of TPC Crossed Rows for kaon meson track"}; + Configurable cutDCAxyKaMeson{"cutDCAxyKaMeson", 0.1, "Maximum DCAxy for kaon meson track"}; + Configurable cutDCAzKaMeson{"cutDCAzKaMeson", 0.1, "Maximum DCAz for kaon meson track"}; + Configurable cutEtaKaMeson{"cutEtaKaMeson", 0.8, "Maximum eta for kaon meson track"}; + Configurable cutPTKaMeson{"cutPTKaMeson", 0.2, "Minimum pt for kaon meson track"}; + Configurable usePID{"usePID", true, "Flag for using PID selection for kaon meson track"}; + Configurable nsigmaCutTPCKaMeson{"nsigmaCutTPCKaMeson", 3.0, "Maximum nsigma cut TPC for kaon meson track"}; + Configurable nsigmaCutTOFKaMeson{"nsigmaCutTOFKaMeson", 3.0, "Maximum nsigma cut TOF for kaon meson track"}; + Configurable cutTOFBetaKaMeson{"cutTOFBetaKaMeson", 0.5f, "Maximum beta cut for kaon meson track"}; + Configurable pSwitchPID{"pSwitchPID", 0.5f, "pT switch for pT-dependent kaon PID"}; + } grpKaon; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + RCTFlagsChecker rctChecker; + void init(o2::framework::InitContext&) + { + rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + histos.add("hCent", "hCent", kTH1F, {{16, 0, 80.0}}); + histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{5, 0, 5.0}}); + histos.add("hTrkSelInfo", "hTrkSelInfo", kTH1F, {{10, 0, 10.0}}); + } + + template + bool selectionTrack(const T& candidate) + { + return candidate.isGlobalTrack() && + candidate.isPVContributor() && + candidate.itsNCls() >= grpKaon.itsclusterKaMeson && + candidate.tpcNClsCrossedRows() > grpKaon.tpcCrossedRowsKaMeson && + std::abs(candidate.dcaXY()) <= grpKaon.cutDCAxyKaMeson && + std::abs(candidate.dcaZ()) <= grpKaon.cutDCAzKaMeson && + std::abs(candidate.eta()) <= grpKaon.cutEtaKaMeson && + candidate.pt() >= grpKaon.cutPTKaMeson; + } + + enum KaonPidBits : uint8_t { + kPID1 = 1u << 0, + kPID2 = 1u << 1, + kPID3 = 1u << 2, + }; + + template + bool selectionPID(const T& candidate) + { + const float pt = candidate.pt(); + const float nTPC = candidate.tpcNSigmaKa(); + + // Low pT: TPC-only. TOF is ignored even if present. + if (pt < grpKaon.pSwitchPID) { + return std::abs(nTPC) < grpKaon.nsigmaCutTPCKaMeson; + } + + // High pT: TOF is mandatory. + if (!candidate.hasTOF()) { + return false; + } + + if (candidate.beta() <= grpKaon.cutTOFBetaKaMeson) { + return false; + } + + const float nTOF = candidate.tofNSigmaKa(); + const float nCombined = std::sqrt(nTPC * nTPC + nTOF * nTOF); + + return nCombined < grpKaon.nsigmaCutTOFKaMeson; + } + + template + bool selectionPID2(const T& candidate) + { + const float pt = candidate.pt(); + const float nTPC = candidate.tpcNSigmaKa(); + + // Low pT: TPC-only. TOF is ignored even if present. + if (pt < grpKaon.pSwitchPID) { + return std::abs(nTPC) < grpKaon.nsigmaCutTPCKaMeson; + } + + // High pT: TOF is mandatory. + if (!candidate.hasTOF()) { + return false; + } + + if (candidate.beta() <= grpKaon.cutTOFBetaKaMeson) { + return false; + } + + const float nTOF = candidate.tofNSigmaKa(); + + return std::abs(nTPC) < grpKaon.nsigmaCutTPCKaMeson && + std::abs(nTOF) < grpKaon.nsigmaCutTOFKaMeson; + } + + template + bool selectionPID3(const T& candidate) + { + // This matches the TOF-only selection from the other task. + if (!candidate.hasTOF()) { + return false; + } + + if (candidate.beta() <= grpKaon.cutTOFBetaKaMeson) { + return false; + } + + return std::abs(candidate.tofNSigmaKa()) < + grpKaon.nsigmaCutTOFKaMeson; + } + + template + uint8_t kaonPidMask(const T& track) + { + uint8_t mask = 0; + + if (selectionPID(track)) { + mask |= kPID1; + } + + if (selectionPID2(track)) { + mask |= kPID2; + } + + if (selectionPID3(track)) { + mask |= kPID3; + } + + return mask; + } + + struct StoredKaon { + float px; + float py; + float pz; + int8_t charge; + int64_t trackId; + uint8_t pidMask; + }; + + Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; + Filter centralityFilter = (aod::cent::centFT0C < cfgCutCentralityMax && aod::cent::centFT0C > cfgCutCentralityMin); + Filter acceptanceFilter = (nabs(aod::track::eta) < cfgCutEta && (aod::track::pt) > cfgCutPt); + Filter DCAcutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter PIDcutFilter = nabs(aod::pidtpc::tpcNSigmaKa) < nsigmaCutTPC; + + using EventCandidates = soa::Filtered>; + using AllTrackCandidates = soa::Filtered>; + SliceCache cache; + Partition posTracks = aod::track::signed1Pt > cfgCutCharge; + Partition negTracks = aod::track::signed1Pt < cfgCutCharge; + + void processData(EventCandidates::iterator const& collision, + AllTrackCandidates const& /*tracks*/) + { + o2::aod::ITSResponse itsResponse; + + const auto centrality = collision.centFT0C(); + const auto vz = collision.posZ(); + const int occupancy = collision.trackOccupancyInTimeRange(); + + histos.fill(HIST("hEvtSelInfo"), 0.5); + + if (!collision.triggereventsp()) { + return; + } + + histos.fill(HIST("hEvtSelInfo"), 1.5); + + if ((rctCut.requireRCTFlagChecker && !rctChecker(collision)) || + !collision.selection_bit(aod::evsel::kNoSameBunchPileup) || + !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || + (useNoCollInTimeRangeStandard && + !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) || + !collision.sel8() || + (useGoodITSLayersAll && + !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) || + occupancy >= cfgCutOccupancy) { + return; + } + + histos.fill(HIST("hEvtSelInfo"), 2.5); + + histos.fill(HIST("hCent"), centrality); + + std::vector selectedKaons; + + // Same slicing pattern that worked in your old producer. + auto posThisColl = posTracks->sliceByCached( + aod::track::collisionId, + collision.globalIndex(), + cache); + + auto negThisColl = negTracks->sliceByCached( + aod::track::collisionId, + collision.globalIndex(), + cache); + + // ---------------- Positive kaons ---------------- + for (const auto& track1 : posThisColl) { + histos.fill(HIST("hTrkSelInfo"), 0.5); + + if (!selectionTrack(track1)) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 1.5); + + const float nSigmaITS1 = + itsResponse.nSigmaITS(track1); + + if (grpKaon.itsPIDSelection && track1.p() < 1.0 && + (nSigmaITS1 <= grpKaon.lowITSPIDNsigma || + nSigmaITS1 >= grpKaon.highITSPIDNsigma)) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 2.5); + + const uint8_t mask1 = kaonPidMask(track1); + + if (grpKaon.usePID && mask1 == 0) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 3.5); + + selectedKaons.push_back({track1.px(), + track1.py(), + track1.pz(), + +1, + track1.globalIndex(), + mask1}); + } + + // ---------------- Negative kaons ---------------- + for (const auto& track2 : negThisColl) { + histos.fill(HIST("hTrkSelInfo"), 4.5); + + if (!selectionTrack(track2)) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 5.5); + + const float nSigmaITS2 = + itsResponse.nSigmaITS(track2); + + if (grpKaon.itsPIDSelection && track2.p() < 1.0 && + (nSigmaITS2 <= grpKaon.lowITSPIDNsigma || + nSigmaITS2 >= grpKaon.highITSPIDNsigma)) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 6.5); + + const uint8_t mask2 = kaonPidMask(track2); + + if (grpKaon.usePID && mask2 == 0) { + continue; + } + + histos.fill(HIST("hTrkSelInfo"), 7.5); + + selectedKaons.push_back({track2.px(), + track2.py(), + track2.pz(), + -1, + track2.globalIndex(), + mask2}); + } + + // No selected K+ or K- in this collision: + // not writing a reduced event row. + histos.fill(HIST("hEvtSelInfo"), 3.5); + + if (selectedKaons.empty()) { + return; + } + + histos.fill(HIST("hEvtSelInfo"), 4.5); + + kaonEvent(centrality, + vz, + collision.qxZDCA(), + collision.qxZDCC(), + collision.qyZDCA(), + collision.qyZDCC()); + + const int64_t indexEvent = kaonEvent.lastIndex(); + + // One table row per selected kaon. + for (const auto& kaon : selectedKaons) { + kaonTrack(indexEvent, + kaon.px, + kaon.py, + kaon.pz, + kaon.charge, + kaon.trackId, + kaon.pidMask); + } + } + PROCESS_SWITCH(phiflow, processData, "Process data", true); +}; +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Resonances/resonanceInitializer.cxx b/PWGLF/TableProducer/Resonances/resonanceInitializer.cxx index 0ba7074043d..f1ab80425f5 100644 --- a/PWGLF/TableProducer/Resonances/resonanceInitializer.cxx +++ b/PWGLF/TableProducer/Resonances/resonanceInitializer.cxx @@ -596,7 +596,7 @@ struct ResonanceInitializer { // Centralicity estimator selection template - float centEst(ResoColl ResoEvents) + float centEst(const ResoColl& ResoEvents) { float returnValue = -999.0; switch (multEstimator) { @@ -673,7 +673,7 @@ struct ResonanceInitializer { } template - float getEvtPl(ResoColl ResoEvents) + float getEvtPl(const ResoColl& ResoEvents) { float returnValue = -999.0; if (ResoEvents.qvecAmp()[evtPlDetId] > 1e-8) @@ -682,7 +682,7 @@ struct ResonanceInitializer { } template - float getEvtPlRes(ResoColl ResoEvents, int a, int b) + float getEvtPlRes(const ResoColl& ResoEvents, int a, int b) { float returnValue = -999.0; if (ResoEvents.qvecAmp()[a] < 1e-8 || ResoEvents.qvecAmp()[b] < 1e-8) @@ -1457,11 +1457,16 @@ struct ResonanceInitializer { // Default event selection if (!colCuts.isSelected(collision)) return; + + bool isRecINELgt0 = 0; + if (checkIsRecINELgt0) + isRecINELgt0 = collision.isInelGt0(); + if (EventCuts.cfgEvtUseRCTFlagChecker && !rctChecker(collision)) return; colCuts.fillQA(collision); - resoCollisions(0, 0, 0, collision.posX(), collision.posY(), collision.posZ(), centEst(collision), dBz, 0); + resoCollisions(0, 0, 0, collision.posX(), collision.posY(), collision.posZ(), centEst(collision), dBz, isRecINELgt0); if (!cfgBypassCollIndexFill) { resoCollisionColls(collision.globalIndex()); } @@ -1664,8 +1669,11 @@ struct ResonanceInitializer { if (EventCuts.cfgEvtUseRCTFlagChecker && !rctChecker(collision)) return; colCuts.fillQA(collision); + bool isRecINELgt0 = 0; + if (checkIsRecINELgt0) + isRecINELgt0 = collision.isInelGt0(); - resoCollisions(0, 0, 0, collision.posX(), collision.posY(), collision.posZ(), centEst(collision), dBz, 0); + resoCollisions(0, 0, 0, collision.posX(), collision.posY(), collision.posZ(), centEst(collision), dBz, isRecINELgt0); if (!cfgBypassCollIndexFill) { resoCollisionColls(collision.globalIndex()); } diff --git a/PWGLF/TableProducer/Resonances/resonanceMergeDF.cxx b/PWGLF/TableProducer/Resonances/resonanceMergeDF.cxx index 55491c775d6..1c544ab1874 100644 --- a/PWGLF/TableProducer/Resonances/resonanceMergeDF.cxx +++ b/PWGLF/TableProducer/Resonances/resonanceMergeDF.cxx @@ -88,7 +88,7 @@ struct ResonanceMergeDF { Produces reso2cascadesdf; int df = 0; - std::vector> vecOfTuples; + std::vector> vecOfTuples; std::vector(tuple)); - resoCollisionsdf(0, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple), std::get<5>(tuple), 0., 0., 0., 0., 0, std::get<6>(tuple)); + resoCollisionsdf(0, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple), std::get<5>(tuple), 0., 0., 0., 0., std::get<6>(tuple), 0, std::get<7>(tuple)); // LOGF(info, "collisions: Index = %d ) %f - %f - %f %f %d -- %d", std::get<0>(tuple).globalIndex(),std::get<1>(tuple),std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple), std::get<5>(tuple).size(),resoCollisionsdf.lastIndex()); for (const auto& tuple : innerVector) { @@ -211,7 +211,7 @@ struct ResonanceMergeDF { { int nCollisions = nDF; - vecOfTuples.push_back(std::make_tuple(collision.posX(), collision.posY(), collision.posZ(), collision.cent(), 0, 0, 0)); + vecOfTuples.push_back(std::make_tuple(collision.posX(), collision.posY(), collision.posZ(), collision.cent(), 0, 0, collision.isRecINELgt0(), 0)); std::vector(tuple)); - resoCollisionsdf(0, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple), std::get<5>(tuple), 0., 0., 0., 0., 0, std::get<6>(tuple)); + resoCollisionsdf(0, std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple), std::get<5>(tuple), 0., 0., 0., 0., std::get<6>(tuple), 0, std::get<7>(tuple)); // LOGF(info, "collisions: Index = %d ) %f - %f - %f %f %d -- %d", std::get<0>(tuple).globalIndex(),std::get<1>(tuple),std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple), std::get<5>(tuple).size(),resoCollisionsdf.lastIndex()); for (const auto& tuple : innerVector) { @@ -417,7 +417,7 @@ struct ResonanceMergeDF { histos.fill(HIST("Event/h1d_ft0_mult_percentile"), collision.cent()); - resoCollisionsdf(0, collision.posX(), collision.posY(), collision.posZ(), collision.cent(), 0, 0, 0., 0., 0., 0., 0, 0); + resoCollisionsdf(0, collision.posX(), collision.posY(), collision.posZ(), collision.cent(), 0, 0, 0., 0., 0., 0., collision.isRecINELgt0(), 0, 0); for (const auto& track : tracks) { if (isPrimary && !track.isPrimaryTrack()) @@ -474,7 +474,7 @@ struct ResonanceMergeDF { if (collision.cent() < minCent || collision.cent() > maxCent) return; - resoCollisionsdf(0, collision.posX(), collision.posY(), collision.posZ(), collision.cent(), 0, 0, 0., 0., 0., 0., 0, 0); + resoCollisionsdf(0, collision.posX(), collision.posY(), collision.posZ(), collision.cent(), 0, 0, 0., 0., 0., 0., collision.isRecINELgt0(), 0, 0); histos.fill(HIST("Event/h1d_ft0_mult_percentile"), collision.cent()); for (const auto& track : tracks) { diff --git a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx index 17e1048c06e..57640b2d3fc 100644 --- a/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx +++ b/PWGLF/TableProducer/Resonances/resonanceModuleInitializer.cxx @@ -12,21 +12,24 @@ /// \file resonanceModuleInitializer.cxx /// \brief Initializes variables for the resonance candidate producers /// -/// \author Bong-Hwi Lim +/// \author Bong-Hwi Lim , Minjae Kim +/// \since Aug.31 2026 #include "PWGLF/DataModel/LFResonanceTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/mcCentrality.h" #include "PWGLF/Utils/collisionCuts.h" -#include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/Core/EventPlaneHelper.h" +#include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" #include #include +#include #include #include #include @@ -37,13 +40,20 @@ #include #include #include -#include #include #include +#include +#include +#include + +#include +#include #include #include +#include #include +#include #include #include @@ -62,78 +72,178 @@ using namespace o2::aod::rctsel; * for resonance studies. It handles event selection, centrality estimation, * and QA histogram filling. */ +// Framework Service members are populated by the analysis framework. +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) struct ResonanceModuleInitializer { - int mRunNumber; ///< Run number for the current data - int multEstimator; ///< Multiplicity estimator type - float dBz; ///< Magnetic field value - float centrality; ///< Centrality value for the event - Service ccdb; ///< CCDB manager service - Service pdg; ///< PDG database service - EventPlaneHelper helperEP; ///< Helper for event plane calculations - - Produces resoCollisions; ///< Output table for resonance collisions - Produces resoCollisionColls; ///< Output table for collision references - Produces resoMCCollisions; ///< Output table for MC resonance collisions - Produces resoSpheroCollisions; ///< Output table for spherocity - Produces resoEvtPlCollisions; ///< Output table for event plane + static constexpr double BzOverrideThreshold = -990.; + static constexpr double MinimumNonzeroBz = 1.e-5; + static constexpr int CentralityFT0M = 0; + static constexpr int CentralityFT0C = 1; + static constexpr int CentralityFT0A = 2; + static constexpr int CentralityFV0A = 3; + static constexpr int MultiplicityNTracksPV = 0; + static constexpr int MultiplicityNTracksPVeta1 = 1; + static constexpr int MultiplicityNTracksPVetaHalf = 2; + static constexpr int MultiplicityFT0M = 3; + static constexpr int MultiplicityFT0A = 4; + static constexpr int MultiplicityFT0C = 5; + static constexpr int MultiplicityFV0A = 6; + static constexpr int DetailedQARCTStage = o2::analysis::CollisonCuts::kAllpassed + 1; + static constexpr int DetailedQAStages = DetailedQARCTStage + 1; + static constexpr float MCVertexZMax = 10.f; + // PDG codes used by the persistent resonance-parent selection. Named O2/ROOT + // values are preferred where available; the remaining resonances are kept as + // local named constants because neither PDG_t nor PhysicsConstants defines them. + static constexpr int PdgKStar0 = o2::constants::physics::Pdg::kK0Star892; + static constexpr int PdgKStarCharged = o2::constants::physics::Pdg::kKPlusStar892; + static constexpr int PdgPhi = o2::constants::physics::Pdg::kPhi; + static constexpr int F0Code980 = 9010221; + static constexpr int F0Code1370 = 10221; + static constexpr int F0Code1500 = 9030221; + static constexpr int F0Code1710 = 10331; + static constexpr int F1Code1285 = 20223; + static constexpr int F1Code1420 = 20333; + static constexpr int F2PrimeCode1525 = 335; + static constexpr int PdgRho0 = PDG_t::kRho770_0; + static constexpr int PdgRhoCharged = PDG_t::kRho770Plus; + static constexpr int SigmaStarPlusCode = 3224; + static constexpr int PdgLambda1520 = o2::constants::physics::Pdg::kLambda1520_Py; + static constexpr int Xi1530Code = 3324; + static constexpr int PdgK1Plus1270 = o2::constants::physics::Pdg::kK1_1270Plus; + static constexpr int Xi1820MinusCode = o2::constants::physics::Pdg::kXi1820Minus; + static constexpr int Xi1820ZeroCode = o2::constants::physics::Pdg::kXi1820Zero; + static constexpr int Omega2012MinusCode = o2::constants::physics::Pdg::kOmega2012Minus; + static constexpr int PdgProton = PDG_t::kProton; + static constexpr int PdgLambda0 = PDG_t::kLambda0; + static constexpr int PdgXiMinus = PDG_t::kXiMinus; + static constexpr int PdgXi0 = o2::constants::physics::Pdg::kXi0; + static constexpr int PdgOmegaMinus = PDG_t::kOmegaMinus; + + int mRunNumber = 0; ///< Run number for the current data + int multEstimator = CentralityFT0M; ///< Centrality estimator type + float dBz = 0.f; ///< Magnetic field value + float centrality = 0.f; ///< Centrality value for the event + Service ccdb; ///< CCDB manager service + + Produces resoCollisions; ///< Output table for resonance collisions + Produces resoCollisionColls; ///< Optional source collision soft links + Produces resoCollisionGroups001; ///< Scalar original-collision grouping keys + Produces resoMCCollisions001; ///< Generator-only MC collision extension + Produces resoMCCollisionIds; ///< Optional source generator-collision soft links + Produces reso2mcparents; ///< Generated parents with scalar source-particle IDs // CCDB options - Configurable ccdbURL{"ccdbURL", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - - Configurable cfgFatalWhenNull{"cfgFatalWhenNull", true, "Fatal when null on ccdb access"}; - Configurable cfgBypassCollIndexFill{"cfgBypassCollIndexFill", false, "Bypass collision index fill"}; - - // Configurables - Configurable dBzInput{"dBzInput", -999, "bz field, -999 is automatic"}; - Configurable cfgFillQA{"cfgFillQA", false, "Fill QA histograms"}; - Configurable cfgBypassCCDB{"cfgBypassCCDB", true, "Bypass loading CCDB part to save CPU time and memory"}; // will be affected to b_z value. - Configurable cfgMultName{"cfgMultName", "FT0M", "The name of multiplicity estimator"}; - Configurable cfgCentralityMC{"cfgCentralityMC", 0, "Centrality estimator for MC (0: Reco, 1: MC, 2: impact parameter)"}; - - // EventCorrection for MC - ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 0.01, 0.1, 1.0, 5.0, 10., 15., 20., 30., 40., 50., 70., 100.0, 105.}, "Binning of the centrality axis"}; - ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -20, -15, -10, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 10, 15, 20}, "Mixing bins - z-vertex"}; + struct : ConfigurableGroup { + Configurable ccdbURL{"ccdbURL", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable cfgFatalWhenNull{"cfgFatalWhenNull", true, "Fatal when null on ccdb access"}; + Configurable cfgBypassCollIndexFill{"cfgBypassCollIndexFill", false, "Deprecated compatibility option; collision mapping tables are always written"}; + } CCDB; + + // General event options + struct : ConfigurableGroup { + Configurable dBzInput{"dBzInput", -999, "bz field, -999 is automatic"}; + Configurable cfgFillQA{"cfgFillQA", true, "Fill QA histograms"}; + Configurable cfgFillDetailedQA{"cfgFillDetailedQA", true, "Fill the Run 3 event-selection stage vs vertex-z vs centrality vs multiplicity THnSparse"}; + Configurable cfgBypassCCDB{"cfgBypassCCDB", true, "Bypass loading CCDB part to save CPU time and memory"}; // will be affected to b_z value. + Configurable cfgMultName{"cfgMultName", "FT0M", "Centrality estimator: FT0M, FT0C, FT0A, or FV0A"}; + Configurable cfgMultiplicityEstimator{"cfgMultiplicityEstimator", 3, + "Stored multiplicity (NOT percentile): 0 -> NTracksPV, 1 -> NTracksPVeta1, 2 -> NTracksPVetaHalf, 3 -> FT0M, 4 -> FT0A, 5 -> FT0C, 6 -> FV0A"}; + ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 0.01, 0.1, 1., 5., 10., 15., 20., 30., 40., 50., 60., 70., 80., 90., 100., 105.}, "Binning of the centrality axis"}; + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {500, 0.f, 5000.f}, "Binning of the reconstructed multiplicity axis for detailed collision QA"}; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {400, -20.f, 20.f}, "Binning of the collision vertex-z axis for detailed QA"}; + } EventConfig; /// Event cuts o2::analysis::CollisonCuts colCuts; - Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.f, "Evt sel: Max. z-Vertex (cm)"}; - Configurable cfgEvtOccupancyInTimeRange{"cfgEvtOccupancyInTimeRange", -1, "Evt sel: maximum track occupancy"}; - Configurable cfgEvtTriggerCheck{"cfgEvtTriggerCheck", false, "Evt sel: check for trigger"}; - Configurable cfgEvtOfflineCheck{"cfgEvtOfflineCheck", true, "Evt sel: check for offline selection"}; - Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", false, "Evt sel: triggerTVX selection (MB)"}; - Configurable cfgEvtTFBorderCut{"cfgEvtTFBorderCut", false, "Evt sel: apply TF border cut"}; - Configurable cfgEvtUseITSTPCvertex{"cfgEvtUseITSTPCvertex", false, "Evt sel: use at lease on ITS-TPC track for vertexing"}; - Configurable cfgEvtCollInTimeRangeNarrow{"cfgEvtCollInTimeRangeNarrow", false, "Evt sel: apply NoCollInTimeRangeNarrow"}; - Configurable cfgEvtZvertexTimedifference{"cfgEvtZvertexTimedifference", false, "Evt sel: apply Z-vertex time difference"}; - Configurable cfgEvtPileupRejection{"cfgEvtPileupRejection", false, "Evt sel: apply pileup rejection"}; - Configurable cfgEvtNoITSROBorderCut{"cfgEvtNoITSROBorderCut", false, "Evt sel: apply NoITSRO border cut"}; - Configurable cfgEvtRun2AliEventCuts{"cfgEvtRun2AliEventCuts", true, "Evt sel: apply Run2 AliEventCuts"}; - Configurable cfgEvtRun2INELgtZERO{"cfgEvtRun2INELgtZERO", false, "Evt sel: apply Run2 INELgtZERO"}; - Configurable cfgEvtUseRCTFlagChecker{"cfgEvtUseRCTFlagChecker", false, "Evt sel: use RCT flag checker"}; - Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; - Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; - Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; - RCTFlagsChecker rctChecker; - - // Spherocity configuration - Configurable cfgTrackSphMin{"cfgTrackSphMin", 10, "Number of tracks for Spherocity Calculation"}; - Configurable cfgTrackSphDef{"cfgTrackSphDef", 0, "Spherocity Definition: |pT| = 1 -> 0, otherwise -> 1"}; - - // Qvector configuration - Configurable cfgEvtPl{"cfgEvtPl", 40500, "Configuration of three subsystems for the event plane and its resolution, 10000*RefA + 100*RefB + S, where FT0C:0, FT0A:1, FT0M:2, FV0A:3, BPos:5, BNeg:6"}; - - int evtPlRefAId = static_cast(cfgEvtPl / 10000); - int evtPlRefBId = static_cast((cfgEvtPl - evtPlRefAId * 10000) / 100); - int evtPlDetId = cfgEvtPl - evtPlRefAId * 10000 - evtPlRefBId * 100; + struct : ConfigurableGroup { + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtOccupancyInTimeRange{"cfgEvtOccupancyInTimeRange", -1, "Evt sel: maximum track occupancy"}; + Configurable cfgEvtTriggerCheck{"cfgEvtTriggerCheck", false, "Evt sel: check for trigger"}; + Configurable cfgEvtOfflineCheck{"cfgEvtOfflineCheck", false, "Evt sel: check for offline selection (sel8)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtTFBorderCut{"cfgEvtTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtUseITSTPCvertex{"cfgEvtUseITSTPCvertex", false, "Evt sel: use at lease on ITS-TPC track for vertexing"}; + Configurable cfgEvtCollInTimeRangeNarrow{"cfgEvtCollInTimeRangeNarrow", false, "Evt sel: apply NoCollInTimeRangeNarrow"}; + Configurable cfgEvtZvertexTimedifference{"cfgEvtZvertexTimedifference", false, "Evt sel: apply Z-vertex time difference"}; + Configurable cfgEvtPileupRejection{"cfgEvtPileupRejection", false, "Evt sel: apply pileup rejection"}; + Configurable cfgEvtNoITSROBorderCut{"cfgEvtNoITSROBorderCut", false, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtRun2AliEventCuts{"cfgEvtRun2AliEventCuts", false, "Evt sel: apply Run2 AliEventCuts"}; + Configurable cfgEvtRun2INELgtZERO{"cfgEvtRun2INELgtZERO", false, "Evt sel: apply Run2 INELgtZERO"}; + Configurable cfgEvtUseRCTFlagChecker{"cfgEvtUseRCTFlagChecker", true, "Evt sel: use RCT flag checker"}; + Configurable cfgEvtBCRCT{"cfgEvtBCRCT", false, "Evt sel: check RCT on the nominal associated BCSEL instead of the collision EVSEL"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + Configurable cfgEvtRCTCheckTableValidity{"cfgEvtRCTCheckTableValidity", false, "Evt sel: reject collisions when the RCT CCDB payload is unavailable"}; + } EventCuts; + RCTFlagsChecker recoRCTChecker; + + // Generator-level event and resonance QA + struct : ConfigurableGroup { + Configurable cfgGenBCRCT{"cfgGenBCRCT", false, "GenEvent: apply the RCT flag checker to the associated BC"}; + Configurable cfgGenRCTCheckTableValidity{"cfgGenRCTCheckTableValidity", false, "GenEvent: reject MC collisions when the RCT CCDB payload is unavailable"}; + Configurable cfgGenMult05{"cfgGenMult05", true, "GenEvent: multiplicity in |eta| < 0.5"}; + Configurable cfgGenMult10{"cfgGenMult10", false, "GenEvent: multiplicity in |eta| < 1.0"}; + Configurable cfgGenMultFT0M{"cfgGenMultFT0M", false, "GenEvent: generated charged-particle multiplicity in the FT0A + FT0C acceptance"}; + Configurable cfgGenMultFT0C{"cfgGenMultFT0C", false, "GenEvent: generated charged-particle multiplicity in the FT0C acceptance"}; + Configurable cfgGenMultFV0A{"cfgGenMultFV0A", false, "GenEvent: generated charged-particle multiplicity in the FV0A acceptance"}; + Configurable cfgGenMultPercentile{"cfgGenMultPercentile", true, "Use the configured FT0M, FT0C, or FV0A percentile from the MC centrality wagon"}; + Configurable cfgFillMCCollisionSoftLink{"cfgFillMCCollisionSoftLink", false, + "Write the source MC-collision soft link; enable only with the original AO2D as linked parent"}; + Configurable isZvtxcutGen{"isZvtxcutGen", true, "Apply the generator-collision z-vertex cut"}; + Configurable cutzvertexGen{"cutzvertexGen", 10.f, "Maximum absolute generator-collision z vertex (cm)"}; + Configurable checkIsTrueINELgt0{"checkIsTrueINELgt0", true, "Classify true INEL>0 generator collisions"}; + ConfigurableAxis binsCentGen{"binsCentGen", + {VARIABLE_WIDTH, 0., 0.01, 0.1, 1., 5., 10., 15., 20., 30., 40., 50., 60., 70., 80., 90., 100., 105.}, + "Generator centrality axis"}; + ConfigurableAxis ptAxisGen{"ptAxisGen", {400, 0.f, 20.f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis multNTracksAxis{"multNTracksAxis", {500, 0.f, 5000.f}, "Number of charged particles"}; + ConfigurableAxis impactParameterAxis{"impactParameterAxis", {500, 0.f, 50.f}, "Impact parameter (fm)"}; + Configurable isDaughterCheck{"isDaughterCheck", true, "Require the configured two-body decay"}; + Configurable cfgRapidityCutMinGen{"cfgRapidityCutMinGen", -0.5f, "Minimum generated-particle rapidity"}; + Configurable cfgRapidityCutMaxGen{"cfgRapidityCutMaxGen", 0.5f, "Maximum generated-particle rapidity"}; + Configurable pdgTruthMother{"pdgTruthMother", static_cast(Xi1530Code), "Absolute PDG code of the generated mother"}; + Configurable pdgTruthDaughter1{"pdgTruthDaughter1", static_cast(PdgXiMinus), "Absolute PDG code of the first daughter"}; + Configurable pdgTruthDaughter2{"pdgTruthDaughter2", PDG_t::kPiPlus, "Absolute PDG code of the second daughter"}; + Configurable cfgDoSignalLoss{"cfgDoSignalLoss", false, "Save reference particles for mT-scaling signal-loss studies"}; + } GenCuts; + RCTFlagsChecker genRCTChecker; + + // Keep the established ResoMCParents content compatible with the legacy + // initializer. The additional stable-particle species are written only for + // signal-loss studies and are filtered in fillMCParents. + Partition selectedMCParticles = (nabs(aod::mcparticle::pdgCode) == PdgKStar0) // K*(892)0 + || (nabs(aod::mcparticle::pdgCode) == PdgKStarCharged) // K*(892)+ + || (nabs(aod::mcparticle::pdgCode) == PdgPhi) // phi(1020) + || (nabs(aod::mcparticle::pdgCode) == F0Code980) // f0(980) + || (nabs(aod::mcparticle::pdgCode) == F0Code1370) // f0(1370) + || (nabs(aod::mcparticle::pdgCode) == F0Code1500) // f0(1500) + || (nabs(aod::mcparticle::pdgCode) == F0Code1710) // f0(1710) + || (nabs(aod::mcparticle::pdgCode) == F1Code1285) // f1(1285) + || (nabs(aod::mcparticle::pdgCode) == F1Code1420) // f1(1420) + || (nabs(aod::mcparticle::pdgCode) == F2PrimeCode1525) // f2'(1525) + || (nabs(aod::mcparticle::pdgCode) == PdgRho0) // rho(770)0 + || (nabs(aod::mcparticle::pdgCode) == PdgRhoCharged) // rho(770)+ + || (nabs(aod::mcparticle::pdgCode) == SigmaStarPlusCode) // Sigma(1385)+ + || (nabs(aod::mcparticle::pdgCode) == PdgLambda1520) // Lambda(1520) + || (nabs(aod::mcparticle::pdgCode) == Xi1530Code) // Xi(1530)0 + || (nabs(aod::mcparticle::pdgCode) == PdgK1Plus1270) // K1(1270)+ + || (nabs(aod::mcparticle::pdgCode) == Xi1820ZeroCode) // Xi(1820)0 + || (nabs(aod::mcparticle::pdgCode) == Xi1820MinusCode) // Xi(1820)- + || (nabs(aod::mcparticle::pdgCode) == Omega2012MinusCode) // Omega(2012)- + || (nabs(aod::mcparticle::pdgCode) == PdgProton) // proton + || (nabs(aod::mcparticle::pdgCode) == PdgLambda0) // Lambda0 + || (nabs(aod::mcparticle::pdgCode) == PdgXiMinus) // Xi- + || (nabs(aod::mcparticle::pdgCode) == PdgXi0) // Xi0 + || (nabs(aod::mcparticle::pdgCode) == PdgOmegaMinus); // Omega- + Preslice mcParticlesPerMcCollision = aod::mcparticle::mcCollisionId; HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Filter collisionFilter = nabs(aod::collision::posZ) < cfgEvtZvtx; - /** * @brief Initializes the task * @@ -144,59 +254,150 @@ struct ResonanceModuleInitializer { mRunNumber = 0; dBz = 0; centrality = 0; - // Determine the multiplicity estimator based on the configuration - multEstimator = 0; - if (cfgMultName.value == "FT0M") { - multEstimator = 0; - } else if (cfgMultName.value == "FT0C") { - multEstimator = 1; - } else if (cfgMultName.value == "FT0A") { - multEstimator = 2; + // Determine the centrality estimator based on the configuration. + if (EventConfig.cfgMultName.value == "FT0M") { + multEstimator = CentralityFT0M; + } else if (EventConfig.cfgMultName.value == "FT0C") { + multEstimator = CentralityFT0C; + } else if (EventConfig.cfgMultName.value == "FT0A") { + multEstimator = CentralityFT0A; + } else if (EventConfig.cfgMultName.value == "FV0A") { + multEstimator = CentralityFV0A; + } else { + LOGF(fatal, "Unsupported cfgMultName '%s'; choose FT0M, FT0C, FT0A, or FV0A", EventConfig.cfgMultName.value.c_str()); + } + LOGF(info, "Centrality estimator: %d, %s", multEstimator, EventConfig.cfgMultName.value.c_str()); + if (EventConfig.cfgMultiplicityEstimator.value < MultiplicityNTracksPV || + EventConfig.cfgMultiplicityEstimator.value > MultiplicityFV0A) { + LOG(fatal) << "cfgMultiplicityEstimator must be in the range [0, 6]"; + } + LOGF(info, "Stored collision multiplicity estimator: %d", EventConfig.cfgMultiplicityEstimator.value); + + const int enabledGenProcesses = static_cast(doprocessMCgen) + + static_cast(doprocessMCgenFT0M) + + static_cast(doprocessMCgenFT0C) + + static_cast(doprocessMCgenFV0A) + + static_cast(doprocessMCgenNoCent); + const bool anyGenProcess = enabledGenProcesses > 0; + if (enabledGenProcesses > 1) { + LOGF(fatal, "Enable only one generator process: processMCgen, processMCgenFT0M, processMCgenFT0C, processMCgenFV0A, or processMCgenNoCent"); + } + if (doprocessMCgenNoCent && GenCuts.cfgGenMultPercentile) { + LOGF(fatal, "processMCgenNoCent requires cfgGenMultPercentile=false"); + } + if ((doprocessMCgenFT0M || doprocessMCgenFT0C || doprocessMCgenFV0A) && !GenCuts.cfgGenMultPercentile) { + LOGF(fatal, "The estimator-specific generator processes require cfgGenMultPercentile=true; use processMCgenNoCent otherwise"); } - LOGF(info, "Mult estimator: %d, %s", multEstimator, cfgMultName.value.c_str()); - // Ensure that only one process type is active at a time - if (doprocessRun3 && doprocessRun2) { - LOG(fatal) << "You cannot run both Run2 and Run3 processes at the same time"; + // Run 2 and Run 3 callbacks require different event-selection semantics. + const bool anyRun2Process = doprocessRun2 || doprocessRun2MC; + const bool anyRun3Process = doprocessRun3 || doprocessRun3MC || anyGenProcess; + if (anyRun2Process && anyRun3Process) { + LOG(fatal) << "Run 2 and Run 3 processes cannot be enabled in the same ResonanceModuleInitializer"; } - if (doprocessRun2MC && doprocessRun3MC) { - LOG(fatal) << "You cannot run both Run2 and Run3 MC processes at the same time"; + if (doprocessRun2 && doprocessRun2MC) { + LOG(fatal) << "processRun2MC writes both ResoCollisions and ResoMCCollisions_001; do not enable processRun2 with it"; + } + if (doprocessRun3 && doprocessRun3MC) { + LOG(fatal) << "processRun3MC writes both ResoCollisions and ResoMCCollisions_001; do not enable processRun3 with it"; + } + const int enabledGenMultiplicityEstimators = static_cast(GenCuts.cfgGenMult05.value) + + static_cast(GenCuts.cfgGenMult10.value) + + static_cast(GenCuts.cfgGenMultFT0M.value) + + static_cast(GenCuts.cfgGenMultFT0C.value) + + static_cast(GenCuts.cfgGenMultFV0A.value); + if ((anyGenProcess || doprocessRun2MC || doprocessRun3MC) && enabledGenMultiplicityEstimators > 1) { + LOG(fatal) << "Only one generator multiplicity estimator can be enabled: cfgGenMult05, cfgGenMult10, cfgGenMultFT0M, cfgGenMultFT0C, or cfgGenMultFV0A"; + } + if (anyGenProcess) { + if (doprocessMCgen && GenCuts.cfgGenMultPercentile && multEstimator != CentralityFT0M && + multEstimator != CentralityFT0C && multEstimator != CentralityFV0A) { + LOGF(fatal, "processMCgen with cfgGenMultPercentile supports cfgMultName=FT0M, FT0C, or FV0A"); + } + if (GenCuts.isZvtxcutGen && + (!std::isfinite(GenCuts.cutzvertexGen.value) || GenCuts.cutzvertexGen.value <= 0.f)) { + LOG(fatal) << "cutzvertexGen must be finite and positive when the generator vertex cut is enabled"; + } + if (!std::isfinite(GenCuts.cfgRapidityCutMinGen.value) || + !std::isfinite(GenCuts.cfgRapidityCutMaxGen.value) || + GenCuts.cfgRapidityCutMinGen.value >= GenCuts.cfgRapidityCutMaxGen.value) { + LOG(fatal) << "Generator rapidity limits must be finite and satisfy cfgRapidityCutMinGen < cfgRapidityCutMaxGen"; + } } // Initialize event selection cuts based on the process type - if (doprocessRun2) { - colCuts.setCuts(cfgEvtZvtx, cfgEvtTriggerCheck, cfgEvtOfflineCheck, false); - } else if (doprocessRun3) { - colCuts.setCuts(cfgEvtZvtx, cfgEvtTriggerCheck, cfgEvtOfflineCheck, true, false, cfgEvtOccupancyInTimeRange); + if (anyRun2Process) { + colCuts.setCuts(EventCuts.cfgEvtZvtx, EventCuts.cfgEvtTriggerCheck, EventCuts.cfgEvtOfflineCheck, false); + } else if (anyRun3Process) { + colCuts.setCuts(EventCuts.cfgEvtZvtx, EventCuts.cfgEvtTriggerCheck, EventCuts.cfgEvtOfflineCheck, true, false, EventCuts.cfgEvtOccupancyInTimeRange); } colCuts.init(&qaRegistry); - colCuts.setTriggerTVX(cfgEvtTriggerTVXSel); - colCuts.setApplyTFBorderCut(cfgEvtTFBorderCut); - colCuts.setApplyITSTPCvertex(cfgEvtUseITSTPCvertex); - colCuts.setApplyCollInTimeRangeNarrow(cfgEvtCollInTimeRangeNarrow); - colCuts.setApplyZvertexTimedifference(cfgEvtZvertexTimedifference); - colCuts.setApplyPileupRejection(cfgEvtPileupRejection); - colCuts.setApplyNoITSROBorderCut(cfgEvtNoITSROBorderCut); - colCuts.setApplyRun2AliEventCuts(cfgEvtRun2AliEventCuts); - colCuts.setApplyRun2INELgtZERO(cfgEvtRun2INELgtZERO); - - rctChecker.init(cfgEvtRCTFlagCheckerLabel, cfgEvtRCTFlagCheckerZDCCheck, cfgEvtRCTFlagCheckerLimitAcceptAsBad); + colCuts.setTriggerTVX(EventCuts.cfgEvtTriggerTVXSel); + colCuts.setApplyTFBorderCut(EventCuts.cfgEvtTFBorderCut); + colCuts.setApplyITSTPCvertex(EventCuts.cfgEvtUseITSTPCvertex); + colCuts.setApplyCollInTimeRangeNarrow(EventCuts.cfgEvtCollInTimeRangeNarrow); + colCuts.setApplyZvertexTimedifference(EventCuts.cfgEvtZvertexTimedifference); + colCuts.setApplyPileupRejection(EventCuts.cfgEvtPileupRejection); + colCuts.setApplyNoITSROBorderCut(EventCuts.cfgEvtNoITSROBorderCut); + colCuts.setApplyRun2AliEventCuts(EventCuts.cfgEvtRun2AliEventCuts); + colCuts.setApplyRun2INELgtZERO(EventCuts.cfgEvtRun2INELgtZERO); + + if (EventConfig.cfgFillDetailedQA && (doprocessRun3 || doprocessRun3MC)) { + AxisSpec selectionStageAxis{DetailedQAStages, -0.5f, static_cast(DetailedQAStages) - 0.5f, "Passed event-selection stage"}; + AxisSpec vertexAxis{EventConfig.cfgVtxBins, "Collision vertex z (cm)"}; + AxisSpec centralityAxis{EventConfig.binsCent, "Centrality (%)"}; + AxisSpec multiplicityAxis{EventConfig.binsMultiplicity, "Multiplicity"}; + qaRegistry.add("Event/h4EventSelectionDetail", "Event-selection cut flow", kTHnSparseD, + {selectionStageAxis, vertexAxis, centralityAxis, multiplicityAxis}); + + auto detailedQA = qaRegistry.get(HIST("Event/h4EventSelectionDetail")); + auto cutCounts = qaRegistry.get(HIST("CollCutCounts")); + for (int stage = o2::analysis::CollisonCuts::kAllEvent; + stage <= o2::analysis::CollisonCuts::kAllpassed; ++stage) { + detailedQA->GetAxis(0)->SetBinLabel(stage + 1, cutCounts->GetXaxis()->GetBinLabel(colCuts.binLabel(stage))); + } + detailedQA->GetAxis(0)->SetBinLabel(DetailedQARCTStage + 1, "RCT"); + } + + recoRCTChecker.init(EventCuts.cfgEvtRCTFlagCheckerLabel, + EventCuts.cfgEvtRCTFlagCheckerZDCCheck, + EventCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad, + EventCuts.cfgEvtRCTCheckTableValidity.value); + genRCTChecker.init(EventCuts.cfgEvtRCTFlagCheckerLabel, + EventCuts.cfgEvtRCTFlagCheckerZDCCheck, + EventCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad, + GenCuts.cfgGenRCTCheckTableValidity.value); // Configure CCDB access if not bypassed - if (!cfgBypassCCDB) { - ccdb->setURL(ccdbURL.value); + if (!EventConfig.cfgBypassCCDB) { + ccdb->setURL(CCDB.ccdbURL.value); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(cfgFatalWhenNull); + ccdb->setFatalWhenNull(CCDB.cfgFatalWhenNull); uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time } - // Initialize QA histograms if required - if (doprocessRun3MC || doprocessRun2MC) { - AxisSpec centAxis = {binsCent, "Centrality (%)"}; - AxisSpec idxMCAxis = {26, -0.5, 25.5, "Index"}; - qaRegistry.add("Event/hMCEventIndices", "hMCEventIndices", kTH2D, {centAxis, idxMCAxis}); + if (anyGenProcess) { + constexpr std::array MCEventLabels{"All", "z vertex", "BC RCT", "INEL", "INEL>0"}; + AxisSpec centAxisGen = {GenCuts.binsCentGen, "Centrality (%)"}; + AxisSpec eventTypeAxis = {2, 0.f, 2.f, "Event type"}; + qaRegistry.add("EventGen/hNEventsMC", "Generator event selection", kTH1D, {{5, 0.f, 5.f}}); + auto eventCounter = qaRegistry.get(HIST("EventGen/hNEventsMC")); + for (std::size_t i = 0; i < MCEventLabels.size(); ++i) { + eventCounter->GetXaxis()->SetBinLabel(static_cast(i + 1), MCEventLabels[i]); + } + qaRegistry.add("EventGen/h5ResonanceTruth", "Generated resonance", kTHnSparseD, + {eventTypeAxis, GenCuts.ptAxisGen, centAxisGen, GenCuts.multNTracksAxis, GenCuts.impactParameterAxis}); + qaRegistry.add("EventGen/h5ResonanceTruthAnti", "Generated anti-resonance", kTHnSparseD, + {eventTypeAxis, GenCuts.ptAxisGen, centAxisGen, GenCuts.multNTracksAxis, GenCuts.impactParameterAxis}); + qaRegistry.add("EventGen/hZCollisionGen", "Generator collision z vertex", kTH1D, {{100, -20.f, 20.f}}); + qaRegistry.add("EventGen/h4MultCent_genMC", "Generator-event multiplicity and centrality", kTHnSparseD, + {eventTypeAxis, centAxisGen, GenCuts.multNTracksAxis, GenCuts.impactParameterAxis}); + qaRegistry.add("EventGen/h4MultCent_recMC", "Reconstructed-event multiplicity and centrality", kTHnSparseD, + {eventTypeAxis, centAxisGen, GenCuts.multNTracksAxis, GenCuts.impactParameterAxis}); + qaRegistry.add("EventGen/h2CentralityVsMultMC", "Representative reconstructed centrality vs generator multiplicity", kTH2D, + {centAxisGen, GenCuts.multNTracksAxis}); } } @@ -205,20 +406,21 @@ struct ResonanceModuleInitializer { * * @param bc BC iterator */ - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) // Simple copy from LambdaKzeroFinder.cxx + template + void initCCDB(BCType const& bc) // Simple copy from LambdaKzeroFinder.cxx { - if (cfgBypassCCDB) + if (EventConfig.cfgBypassCCDB) { return; + } if (mRunNumber == bc.runNumber()) { return; } // In case override, don't proceed, please - no CCDB access required - if (dBzInput > -990) { - dBz = dBzInput; - ; + if (EventConfig.dBzInput > BzOverrideThreshold) { + dBz = EventConfig.dBzInput; o2::parameters::GRPMagField grpmag; - if (std::fabs(dBz) > 1e-5) { + if (std::fabs(dBz) > MinimumNonzeroBz) { grpmag.setL3Current(30000.f / (dBz / 5.0f)); } o2::base::Propagator::initFieldFromGRP(&grpmag); @@ -227,17 +429,17 @@ struct ResonanceModuleInitializer { } auto run3grpTimestamp = bc.timestamp(); - o2::parameters::GRPObject* grpo = ccdb->getForTimeStamp(grpPath, run3grpTimestamp); - o2::parameters::GRPMagField* grpmag = 0x0; + auto* grpo = ccdb->getForTimeStamp(CCDB.grpPath, run3grpTimestamp); + o2::parameters::GRPMagField* grpmag = nullptr; if (grpo) { o2::base::Propagator::initFieldFromGRP(grpo); // Fetch magnetic field from ccdb for current collision dBz = grpo->getNominalL3Field(); LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grpTimestamp); + grpmag = ccdb->getForTimeStamp(CCDB.grpmagPath, run3grpTimestamp); if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grpTimestamp; + LOG(fatal) << "Got nullptr from CCDB for path " << CCDB.grpmagPath << " of object GRPMagField and " << CCDB.grpPath << " of object GRPObject for timestamp " << run3grpTimestamp; } o2::base::Propagator::initFieldFromGRP(grpmag); // Fetch magnetic field from ccdb for current collision @@ -249,275 +451,359 @@ struct ResonanceModuleInitializer { LOGF(info, "Bz set to %f for run: ", dBz, mRunNumber); } - /** - * @brief Checks if the collision is INEL>0 - * - * @tparam MCPart Type of MC particles - * @param mcparts MC particles - * @return true if INEL>0, false otherwise - */ - template - bool isTrueINEL0(MCPart const& mcparts) - { - for (auto const& mcparticle : mcparts) { - if (!mcparticle.isPhysicalPrimary()) - continue; - auto p = pdg->GetParticle(mcparticle.pdgCode()); - if (p != nullptr) { - if (std::abs(p->Charge()) >= 3) { - if (std::abs(mcparticle.eta()) < 1) - return true; - } - } - } - return false; - } - /** * @brief Centrality estimator selection * * @tparam ResoColl Type of resonance collision - * @tparam isMC Boolean indicating if it's MC - * @param ResoEvents Resonance events + * @param resoEvents Resonance events * @return Centrality value */ - template - float centEst(ResoColl ResoEvents) + template + float centEst(ResoColl const& resoEvents) { - float returnValue = -999.0; switch (multEstimator) { - case 0: - returnValue = ResoEvents.centFT0M(); - break; - case 1: - if constexpr (isMC) { - LOG(fatal) << "CentFT0C is not available for MC"; - return returnValue; - } else { - returnValue = ResoEvents.centFT0C(); - break; - } - case 2: - if constexpr (isMC) { - LOG(fatal) << "CentFT0A is not available for MC"; - return returnValue; - } else { - returnValue = ResoEvents.centFT0A(); - break; - } + case CentralityFT0M: + return resoEvents.centFT0M(); + case CentralityFT0C: + return resoEvents.centFT0C(); + case CentralityFT0A: + return resoEvents.centFT0A(); + case CentralityFV0A: + return resoEvents.centFV0A(); default: - returnValue = ResoEvents.centFT0M(); - break; + return -999.f; } - return returnValue; } - using GenMCCollisions = soa::Join; - float centEstMC(const GenMCCollisions::iterator& collision) { return centEst(collision); } /** - * @brief Computes the spherocity of an event - * - * @tparam T Type of the tracks - * @param tracks All tracks - * @param nTracksMin Minimum number of tracks - * @param spdef Spherocity definition - * @return Spherocity value + * @brief Returns the configured reconstructed multiplicity estimator */ - template - float computeSpherocity(T const& tracks, int nTracksMin, int spdef) + template + float collisionMultiplicity(CollisionType const& collision) { - // if number of tracks is not enough for spherocity estimation. - int ntrks = tracks.size(); - if (ntrks < nTracksMin) - return -99.; + switch (EventConfig.cfgMultiplicityEstimator.value) { + case MultiplicityNTracksPV: + return collision.multNTracksPV(); + case MultiplicityNTracksPVeta1: + return collision.multNTracksPVeta1(); + case MultiplicityNTracksPVetaHalf: + return collision.multNTracksPVetaHalf(); + case MultiplicityFT0M: + return collision.multFT0M(); + case MultiplicityFT0A: + return collision.multFT0A(); + case MultiplicityFT0C: + return collision.multFT0C(); + case MultiplicityFV0A: + return collision.multFV0A(); + default: + return -1.f; + } + } - // start computing spherocity + /// Fill the detailed Run 3 collision QA at one passed selection stage. + template + void fillDetailedCollisionQA(CollisionType const& collision, int selectionStage) + { + qaRegistry.fill(HIST("Event/h4EventSelectionDetail"), + selectionStage, + collision.posZ(), + centEst(collision), + collisionMultiplicity(collision)); + } - float ptSum = 0.; - for (auto const& track : tracks) { - if (cfgFillQA) { - qaRegistry.fill(HIST("Phi"), track.phi()); - } - if (spdef == 0) { - ptSum += 1.; - } else { - ptSum += track.pt(); - } + /// Reproduce the configured Run 3 collision cut flow for detailed QA only. + /// The authoritative event decision remains CollisonCuts::isSelected(). + template + void fillDetailedRun3SelectionQA(CollisionType const& collision) + { + fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::kAllEvent); + if (std::abs(collision.posZ()) > EventCuts.cfgEvtZvtx.value) { + return; } + fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::kFlagZvertex); + +// Keep this QA-only mapping synchronized with CollisonCuts without exposing +// its internal selection registry. +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -- X-macro interface of EventSelectionFlagsMapping.def +#define EVSEL_FLAG(enumVal, member, defaultVal, evtSelEnum, setter, getter, label, desc) \ + if (colCuts.getSelection(o2::analysis::CollisonCuts::evtSelEnum)) { \ + if (!collision.selection_bit(o2::aod::evsel::enumVal)) { \ + return; \ + } \ + fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::evtSelEnum); \ + } +#include "PWGLF/Utils/EventSelectionFlagsMapping.def" // NOLINT(build/include) +#undef EVSEL_FLAG - float tempSph = 1.; - for (int i = 0; i < 360 / 0.1; ++i) { - float sum = 0., pt = 0.; - float phiparm = (PI * i * 0.1) / 180.; - float nx = std::cos(phiparm); - float ny = std::sin(phiparm); - for (auto const& trk : tracks) { - pt = trk.pt(); - if (spdef == 0) { - pt = 1.; - } - float phi = trk.phi(); - float px = pt * std::cos(phi); - float py = pt * std::sin(phi); - // sum += pt * abs(sin(phiparm - phi)); - sum += std::abs(px * ny - py * nx); - } - float sph = std::pow((sum / ptSum), 2); - if (sph < tempSph) - tempSph = sph; + if (EventCuts.cfgEvtOfflineCheck.value && !collision.sel8()) { + return; } + fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::kFlagSel8); - return std::pow(PIHalf, 2) * tempSph; + if (EventCuts.cfgEvtOccupancyInTimeRange.value > 0 && + collision.trackOccupancyInTimeRange() > EventCuts.cfgEvtOccupancyInTimeRange.value) { + return; + } + fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::kFlagOccupancy); + fillDetailedCollisionQA(collision, o2::analysis::CollisonCuts::kAllpassed); } + // Keep the original input for existing processMCgen configurations. The + // dedicated callbacks select the MC percentile independently of cfgMultName + // (reconstructed centrality) and cfgGenMult* (generator multiplicity). + using GenMCCollisions = soa::Join; + using GenMCCollisionsFT0M = soa::Join; + using GenMCCollisionsFT0C = soa::Join; + using GenMCCollisionsFV0A = soa::Join; + using GenMCCollisionsNoCent = soa::Join; + using Run3MCCollisions = soa::Join; + using Run2MCCollisions = soa::Join; + using GenRecoCollisions = soa::Join; + using BCsWithRCT = soa::Join; + /** - * @brief Gets the event plane + * @brief Applies the configured RCT selection to a reconstructed collision * - * @tparam ResoColl Type of resonance collision - * @param ResoEvents Resonance events - * @return Event plane value + * The collision-level mode reads the RCT value stored in EVSEL. The BC-level + * mode reads the value directly from the nominal BC referenced by collision. */ - template - float getEvtPl(ResoColl ResoEvents) + template + bool isRecoRCTSelected(CollisionType const& collision) { - float returnValue = -999.0; - if (ResoEvents.qvecAmp()[evtPlDetId] > 1e-8) - returnValue = helperEP.GetEventPlane(ResoEvents.qvecRe()[evtPlDetId * 4 + 3], ResoEvents.qvecIm()[evtPlDetId * 4 + 3], 2); - return returnValue; + if (!EventCuts.cfgEvtUseRCTFlagChecker.value) { + return true; + } + if (!EventCuts.cfgEvtBCRCT.value) { + return recoRCTChecker(collision); + } + const auto bc = collision.template bc_as(); + return recoRCTChecker(bc); + } + + /// Apply the reconstructed Run 3 event and RCT selections. Detailed cut-flow + /// QA is evaluated independently and never controls the event decision. + template + bool isRun3CollisionSelected(CollisionType const& collision) + { + if (EventConfig.cfgFillDetailedQA) { + fillDetailedRun3SelectionQA(collision); + } + if (!colCuts.isSelected(collision, EventConfig.cfgFillQA) || !isRecoRCTSelected(collision)) { + return false; + } + if (EventConfig.cfgFillDetailedQA) { + fillDetailedCollisionQA(collision, DetailedQARCTStage); + } + return true; } /** - * @brief Gets the event plane resolution + * @brief Fills generator-level resonance QA * - * @tparam ResoColl Type of resonance collision - * @param ResoEvents Resonance events - * @param a First index - * @param b Second index - * @return Event plane resolution + * @tparam MCParticlesType Type of MC-particle group + * @param mcParticles MC particles grouped by generator collision + * @param generatorCentrality Generator or representative reconstructed centrality + * @param multiplicity Generator-level charged-particle multiplicity + * @param impactParameter Generator collision impact parameter + * @param eventType INEL/INEL>0 category */ - template - float getEvtPlRes(ResoColl ResoEvents, int a, int b) + template + void fillMCGenParticles(MCParticlesType const& mcParticles, + float generatorCentrality, + float multiplicity, + float impactParameter, + int eventType) { - float returnValue = -999.0; - if (ResoEvents.qvecAmp()[a] < 1e-8 || ResoEvents.qvecAmp()[b] < 1e-8) - return returnValue; - returnValue = helperEP.GetResolution(helperEP.GetEventPlane(ResoEvents.qvecRe()[a * 4 + 3], ResoEvents.qvecIm()[a * 4 + 3], 2), helperEP.GetEventPlane(ResoEvents.qvecRe()[b * 4 + 3], ResoEvents.qvecIm()[b * 4 + 3], 2), 2); - return returnValue; + for (auto const& mcPart : mcParticles) { + if (std::abs(mcPart.pdgCode()) != std::abs(GenCuts.pdgTruthMother.value)) { + continue; + } + if (mcPart.y() <= GenCuts.cfgRapidityCutMinGen || mcPart.y() >= GenCuts.cfgRapidityCutMaxGen) { + continue; + } + + std::array daughterPDGs{-1, -1}; + const bool hasDaughters = mcPart.has_daughters(); + const auto daughterIds = mcPart.daughtersIds(); + if (hasDaughters) { + auto daughter01 = mcParticles.rawIteratorAt(daughterIds[0] - mcParticles.offset()); + auto daughter02 = mcParticles.rawIteratorAt(daughterIds[1] - mcParticles.offset()); + daughterPDGs = {daughter01.pdgCode(), daughter02.pdgCode()}; + } + if (GenCuts.isDaughterCheck) { + const int daughter1 = std::abs(GenCuts.pdgTruthDaughter1.value); + const int daughter2 = std::abs(GenCuts.pdgTruthDaughter2.value); + const int firstDaughterPDG = std::abs(daughterPDGs[0]); + const int secondDaughterPDG = std::abs(daughterPDGs[1]); + // Match the configured decay to two distinct daughter slots. This is + // important when both configured absolute PDGs are equal (e.g. K+K-): + // one matching daughter must not satisfy both requirements. + const bool matchesConfiguredDecay = + (firstDaughterPDG == daughter1 && secondDaughterPDG == daughter2) || + (firstDaughterPDG == daughter2 && secondDaughterPDG == daughter1); + if (!matchesConfiguredDecay) { + continue; + } + } + + if (mcPart.pdgCode() > 0) { + qaRegistry.fill(HIST("EventGen/h5ResonanceTruth"), eventType, mcPart.pt(), generatorCentrality, multiplicity, impactParameter); + } else { + qaRegistry.fill(HIST("EventGen/h5ResonanceTruthAnti"), eventType, mcPart.pt(), generatorCentrality, multiplicity, impactParameter); + } + } } /** - * @brief Fills MC particles + * @brief Fills generated resonance parents for one reduced collision * - * @tparam CollisionType Type of collision - * @tparam SelectedMCPartType Type of selected MC particles - * @tparam TotalMCParts Type of total MC particles - * @param collision Collision data - * @param mcParts Selected MC particles - * @param mcParticles Total MC particles + * @tparam SelectedMCParticlesType Type of the selected MC-particle slice + * @tparam MCParticlesType Type of the complete MC-particle table + * @param reducedCollisionId Reduced collision referenced by the output rows + * @param selectedParents Selected parent particles in the associated MC collision + * @param mcParticles Complete MC-particle table used to resolve daughter indices + * + * The source MC-particle global index is persisted as a scalar in + * ResoMCParents_001; it is not a relation requiring McParticles at merge time. */ - template - void fillMCParticles(CollisionType collision, SelectedMCPartType const& mcParts, TotalMCParts const& mcParticles) + template + void fillMCParents(int64_t reducedCollisionId, + SelectedMCParticlesType const& selectedParents, + MCParticlesType const& mcParticles) { - for (auto const& mcPart : mcParts) { - std::vector daughterPDGs; + for (auto const& mcPart : selectedParents) { + if (!GenCuts.cfgDoSignalLoss) { + const int absPdg = std::abs(mcPart.pdgCode()); + if (absPdg == PdgProton || absPdg == PdgLambda0 || absPdg == PdgXiMinus || absPdg == PdgXi0 || absPdg == PdgOmegaMinus) { + continue; + } + } + + std::array daughterPDGs{-1, -1}; if (mcPart.has_daughters()) { - auto daughter01 = mcParticles.rawIteratorAt(mcPart.daughtersIds()[0] - mcParticles.offset()); - auto daughter02 = mcParticles.rawIteratorAt(mcPart.daughtersIds()[1] - mcParticles.offset()); - daughterPDGs = {daughter01.pdgCode(), daughter02.pdgCode()}; - } else { - daughterPDGs = {-1, -1}; + const auto daughter1 = mcParticles.rawIteratorAt(mcPart.daughtersIds()[0] - mcParticles.offset()); + const auto daughter2 = mcParticles.rawIteratorAt(mcPart.daughtersIds()[1] - mcParticles.offset()); + daughterPDGs = {daughter1.pdgCode(), daughter2.pdgCode()}; } - reso2mcparents(collision.globalIndex(), + reso2mcparents(reducedCollisionId, mcPart.globalIndex(), mcPart.pdgCode(), - daughterPDGs[0], daughterPDGs[1], + daughterPDGs[0], + daughterPDGs[1], mcPart.isPhysicalPrimary(), mcPart.producedByGenerator(), mcPart.pt(), mcPart.px(), mcPart.py(), mcPart.pz(), - mcPart.eta(), - mcPart.phi(), - mcPart.y()); - daughterPDGs.clear(); + mcPart.y(), + mcPart.e(), + mcPart.statusCode()); + } + } + + /** + * @brief Returns the configured generator-level multiplicity estimator + */ + template + float getMCMultiplicity(MCCollision const& mcCollision) + { + if (GenCuts.cfgGenMult05) { + return mcCollision.multMCNParticlesEta05(); + } + if (GenCuts.cfgGenMult10) { + return mcCollision.multMCNParticlesEta10(); + } + if (GenCuts.cfgGenMultFT0M) { + return mcCollision.multMCFT0A() + mcCollision.multMCFT0C(); + } + if (GenCuts.cfgGenMultFT0C) { + return mcCollision.multMCFT0C(); + } + if (GenCuts.cfgGenMultFV0A) { + return mcCollision.multMCFV0A(); + } + return -1.f; + } + + /** + * @brief Returns the configured generator-level centrality percentile + */ + template + float getMCCentrality(MCCollision const& mcCollision) const + { + if (!GenCuts.cfgGenMultPercentile.value) { + return 100.5f; + } + switch (multEstimator) { + case CentralityFT0M: + return mcCollision.centFT0M(); + case CentralityFT0C: + return mcCollision.centFT0C(); + case CentralityFV0A: + return mcCollision.centFV0A(); + default: + return 100.5f; } } /** - * @brief Fills MC collision data + * @brief Fills the generator-only MC collision extension and optional source link * - * @tparam isRun2 Boolean indicating if it's Run2 - * @tparam MCCol Type of MC collision - * @tparam MCPart Type of MC particles - * @param mccol MC collision data - * @param mcparts MC particles + * Accepting only the generator collision here prevents reconstructed event + * selection state from accidentally entering ResoMCCollisions_001. */ - template - void fillMCCollision(MCCol const& mccol, MCPart const& mcparts) - { - const auto& mcColg = mccol.template mcCollision_as(); - float mcCent = 999.0; - if constexpr (isRun2) { - if (cfgCentralityMC == 0) { - mcCent = mccol.centRun2V0M(); - } else { - mcCent = mcColg.impactParameter(); - } - } else { - if (cfgCentralityMC == 0) { - mcCent = centEst(mccol); - } else if (cfgCentralityMC == 1) { - mcCent = centEstMC(mcColg); - } else if (cfgCentralityMC == 2) { - mcCent = mcColg.impactParameter(); - } - } - bool inVtx10 = (std::abs(mcColg.posZ()) > 10.) ? false : true; - bool isTrueINELgt0 = isTrueINEL0(mcparts); - bool isTriggerTVX = mccol.selection_bit(aod::evsel::kIsTriggerTVX); - bool isSel8 = mccol.sel8(); - bool isSelected = colCuts.isSelected(mccol); - resoMCCollisions(inVtx10, isTrueINELgt0, isTriggerTVX, isSel8, isSelected, mcCent, -1.0f); - - // QA for Trigger efficiency - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kINEL); - if (inVtx10) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kINEL10); - if (isTrueINELgt0) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kINELg0); - if (inVtx10 && isTrueINELgt0) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kINELg010); - - // TVX MB trigger - if (isTriggerTVX) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kTrig); - if (isTriggerTVX && inVtx10) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kTrig10); - if (isTriggerTVX && isTrueINELgt0) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kTrigINELg0); - if (isTriggerTVX && isTrueINELgt0 && inVtx10) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kTrigINELg010); - - // Sel8 event selection - if (isSel8) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kSel8); - if (isSel8 && inVtx10) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kSel810); - if (isSel8 && isTrueINELgt0) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kSel8INELg0); - if (isSel8 && isTrueINELgt0 && inVtx10) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kSel8INELg010); - - // CollisionCuts selection - if (isSelected) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kAllCuts); - if (isSelected && inVtx10) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kAllCuts10); - if (isSelected && isTrueINELgt0) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kAllCutsINELg0); - if (isSelected && isTrueINELgt0 && inVtx10) - qaRegistry.fill(HIST("Event/hMCEventIndices"), mcCent, aod::resocollision::kAllCutsINELg010); + template + void fillMCCollision001(MCCollision const& mcCollision) + { + const float mcMultiplicity = getMCMultiplicity(mcCollision); + const bool inTrueVtx10 = std::abs(mcCollision.posZ()) < MCVertexZMax; + // Keep the generator-level definition identical to MultsExtraMC: + // at least one physical-primary charged particle within |eta| < 1. + const bool isTrueINELgt0 = mcCollision.isInelGt0(); + resoMCCollisions001(inTrueVtx10, + isTrueINELgt0, + mcCollision.impactParameter(), + mcMultiplicity); + if (GenCuts.cfgFillMCCollisionSoftLink) { + resoMCCollisionIds(mcCollision.globalIndex()); + } + } + + /// Write the Run 3 base collision, optional soft link, and scalar grouping key. + template + void fillRun3Collision(CollisionType const& collision) + { + const bool isRecINELgt0 = collision.isInelGt0(); + centrality = centEst(collision); + resoCollisions(collisionMultiplicity(collision), + collision.posX(), + collision.posY(), + collision.posZ(), + centrality, + dBz, + isRecINELgt0); + resoCollisionColls(collision.globalIndex()); + resoCollisionGroups001(collision.globalIndex()); + } + + /// Write the Run 2 base collision, optional soft link, and scalar grouping key. + template + void fillRun2Collision(CollisionType const& collision) + { + centrality = collision.centRun2V0M(); + // The configured Run 3 estimators are not available in the Run 2 input + // schema. Preserve the previous zero-filled Run 2 behaviour. + resoCollisions(0.f, + collision.posX(), + collision.posY(), + collision.posZ(), + centrality, + dBz, + 0); + resoCollisionColls(collision.globalIndex()); + resoCollisionGroups001(collision.globalIndex()); } /** @@ -536,23 +822,19 @@ struct ResonanceModuleInitializer { * @param collision Collision data * @param bc BC data */ - void processRun3(soa::Filtered::iterator const& collision, - aod::BCsWithTimestamps const&) + void processRun3(aod::ResoCollisionCandidates::iterator const& collision, + BCsWithRCT const&) { - auto bc = collision.bc_as(); + auto bc = collision.bc_as(); initCCDB(bc); // Default event selection - if (!colCuts.isSelected(collision)) - return; - if (cfgEvtUseRCTFlagChecker && !rctChecker(collision)) + if (!isRun3CollisionSelected(collision)) { return; - colCuts.fillQA(collision); - centrality = centEst(collision); - - resoCollisions(0, 0, 0, collision.posX(), collision.posY(), collision.posZ(), centrality, dBz, 0); - if (!cfgBypassCollIndexFill) { - resoCollisionColls(collision.globalIndex()); } + if (EventConfig.cfgFillQA) { + colCuts.fillQA(collision); + } + fillRun3Collision(collision); } PROCESS_SWITCH(ResonanceModuleInitializer, processRun3, "Default process for RUN3", false); @@ -562,248 +844,1386 @@ struct ResonanceModuleInitializer { * @param collision Collision data * @param bc BC data */ - void processRun2(soa::Filtered::iterator const& collision, + void processRun2(aod::ResoRun2CollisionCandidates::iterator const& collision, aod::BCsWithRun2Info const&) { // auto bc = collision.bc_as(); // Default event selection - if (!colCuts.isSelected(collision)) + if (!colCuts.isSelected(collision, EventConfig.cfgFillQA)) { return; - colCuts.fillQARun2(collision); - centrality = collision.centRun2V0M(); + } + if (EventConfig.cfgFillQA) { + colCuts.fillQARun2(collision); + } + fillRun2Collision(collision); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processRun2, "process for RUN2", false); + + /** + * @brief Processes generator-level MC event and resonance QA + * + * The original MC collision is the grouping key. MC particles are therefore + * grouped automatically, while reconstructed collisions arrive as a 0..N + * SmallGroup through their McCollisionLabels relation. This auxiliary + * callback intentionally fills generator-level QA only and does not write + * reduced AOD tables. RCT quality is evaluated through the generator + * collision's associated BC because it is a run-condition property. + */ + template + void processMCgenQA(MCCollision const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + float generatorCentrality) + { + auto bc = mcCollision.template bc_as(); + initCCDB(bc); + + const auto getReconstructedCentrality = [&](auto const& collision) { + return centEst(collision); + }; + + const float impactParameter = mcCollision.impactParameter(); + const float multiplicity = getMCMultiplicity(mcCollision); + + qaRegistry.fill(HIST("EventGen/hNEventsMC"), 0.5); + if (GenCuts.isZvtxcutGen && std::abs(mcCollision.posZ()) > GenCuts.cutzvertexGen) { + return; + } + qaRegistry.fill(HIST("EventGen/hNEventsMC"), 1.5); + if (GenCuts.cfgGenBCRCT && !genRCTChecker(bc)) { + return; + } + qaRegistry.fill(HIST("EventGen/hNEventsMC"), 2.5); + qaRegistry.fill(HIST("EventGen/hZCollisionGen"), mcCollision.posZ()); + + int eventType = 0; + qaRegistry.fill(HIST("EventGen/hNEventsMC"), 3.5); + if (GenCuts.checkIsTrueINELgt0 && mcCollision.isInelGt0()) { + eventType = 1; + qaRegistry.fill(HIST("EventGen/hNEventsMC"), 4.5); + } + + bool hasSelectedRecoCollision = false; + int largestNContributors = -1; + float reconstructedCentrality = 100.5f; + for (auto const& collision : collisions) { + if (!isRecoRCTSelected(collision)) { + continue; + } + if (!colCuts.isSelected(collision, false)) { + continue; + } + const int nContributors = static_cast(collision.multPVTotalContributors()); + if (nContributors > largestNContributors) { + largestNContributors = nContributors; + reconstructedCentrality = getReconstructedCentrality(collision); + } + hasSelectedRecoCollision = true; + } + + if (GenCuts.cfgGenMultPercentile) { + fillMCGenParticles(mcParticles, generatorCentrality, multiplicity, impactParameter, eventType); + qaRegistry.fill(HIST("EventGen/h4MultCent_genMC"), eventType, generatorCentrality, multiplicity, impactParameter); + } else { + fillMCGenParticles(mcParticles, reconstructedCentrality, multiplicity, impactParameter, eventType); + qaRegistry.fill(HIST("EventGen/h4MultCent_genMC"), eventType, reconstructedCentrality, multiplicity, impactParameter); + qaRegistry.fill(HIST("EventGen/h2CentralityVsMultMC"), reconstructedCentrality, multiplicity); + } + if (hasSelectedRecoCollision) { + qaRegistry.fill(HIST("EventGen/h4MultCent_recMC"), eventType, reconstructedCentrality, multiplicity, impactParameter); + } + } + + void processMCgen(GenMCCollisions::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, getMCCentrality(mcCollision)); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgen, "Process generator-level MC QA with all MC percentile inputs (legacy)", false); + + void processMCgenFT0M(GenMCCollisionsFT0M::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFT0M()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFT0M, "Process generator-level MC QA with only the FT0M MC percentile input", false); + + void processMCgenFT0C(GenMCCollisionsFT0C::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFT0C()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFT0C, "Process generator-level MC QA with only the FT0C MC percentile input", false); + + void processMCgenFV0A(GenMCCollisionsFV0A::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, mcCollision.centFV0A()); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenFV0A, "Process generator-level MC QA with only the FV0A MC percentile input", false); + + void processMCgenNoCent(GenMCCollisionsNoCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions, + BCsWithRCT const&) + { + processMCgenQA(mcCollision, mcParticles, collisions, 100.5f); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processMCgenNoCent, "Process generator-level MC QA using reconstructed centrality without MC percentile inputs", false); + + /** + * @brief Processes Run3 MC data + * + * Reconstructed collisions pass the same event selection and QA sequence as + * Run 3 data before the reduced collision and its MC extension are written. + * + * @param collision Collision data + * @param mcCollisions MC collisions + * @param mcParticles MC particles used to fill reduced resonance parents + */ + void processRun3MC(aod::ResoCollisionCandidatesMC::iterator const& collision, + Run3MCCollisions const&, + aod::McParticles const& mcParticles, + BCsWithRCT const&) + { + auto bc = collision.bc_as(); + initCCDB(bc); + if (!isRun3CollisionSelected(collision)) { + return; + } + if (EventConfig.cfgFillQA) { + colCuts.fillQA(collision); + } + if (!collision.has_mcCollision()) { + return; + } + const auto& mcCollision = collision.mcCollision_as(); + + // ResoMCCollisions_001 is a positional extension of ResoCollisions. When + // enabled, ResoMCCollisionIds is written in the same callback and is 1:1. + fillRun3Collision(collision); + const int64_t reducedCollisionId = resoCollisions.lastIndex(); + fillMCCollision001(mcCollision); + + // A generator collision can be associated with more than one reconstructed + // collision. Write one parent set for each reduced collision, as in the + // legacy collision-wise producer, so ResoCollisionId remains unambiguous. + auto selectedParents = selectedMCParticles->sliceBy(mcParticlesPerMcCollision, mcCollision.globalIndex()); + fillMCParents(reducedCollisionId, selectedParents, mcParticles); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processRun3MC, "process MC for RUN3", false); + + /** + * @brief Processes Run2 MC data + * + * Reconstructed collisions pass the same event selection and QA sequence as + * Run 2 data before the reduced collision and its MC extension are written. + * + * @param collision Collision data + */ + void processRun2MC(aod::ResoRun2CollisionCandidatesMC::iterator const& collision, + Run2MCCollisions const&) + { + if (!colCuts.isSelected(collision, EventConfig.cfgFillQA)) { + return; + } + if (EventConfig.cfgFillQA) { + colCuts.fillQARun2(collision); + } + if (!collision.has_mcCollision()) { + return; + } + const auto& mcCollision = collision.mcCollision_as(); + + // Keep the base and MC extension one-to-one; the optional source link is + // written by fillMCCollision001 in the same order. + fillRun2Collision(collision); + fillMCCollision001(mcCollision); + } + PROCESS_SWITCH(ResonanceModuleInitializer, processRun2MC, "process MC for RUN2", false); +}; + +/** + * @brief Initializer for the resonance daughters producer + * + * This struct initializes and processes daughters for resonance studies. + * It applies daughter selection criteria and fills QA histograms for daughter properties. + */ +struct ResonanceDaughterInitializer { + enum class UltraMicroPidSpecies : uint8_t { + Pion, + Kaon, + Proton + }; + + static constexpr int TrackSelectionNone = 0; + static constexpr int TrackSelectionGlobal = 1; + static constexpr int TrackSelectionGlobalWoPtEta = 2; + static constexpr int TrackSelectionGlobalWoDCA = 3; + static constexpr int TrackSelectionQuality = 4; + static constexpr int TrackSelectionInAcceptance = 5; + static constexpr int PairGateModeConfigured = 0; + static constexpr int PairGateModeEither = 1; + static constexpr float MomentumQuantizationScale = 1000.f; + static constexpr std::size_t StoredMCRelationCount = 2; + static constexpr std::size_t MaxCandidateDaughters = 3; + static constexpr int NumberOfITSLayers = 7; + + /// Selected-candidate state and the optional global daughter-ID veto set. + /// By default only candidate existence is recorded and daughter reuse is + /// rejected later for each concrete pair through the stored trackId. + struct SelectedCandidateDaughters { + std::vector allDaughterIds; + bool hasSelectedCandidate = false; + bool useGlobalDaughterVeto = false; + + explicit SelectedCandidateDaughters(bool globalDaughterVeto = false) + : useGlobalDaughterVeto(globalDaughterVeto) + { + } + + void addCandidate() + { + hasSelectedCandidate = true; + } + + template + void addCandidate(std::array const& daughterIds) + { + static_assert(DaughterCount <= MaxCandidateDaughters); + allDaughterIds.insert(allDaughterIds.end(), daughterIds.begin(), daughterIds.end()); + hasSelectedCandidate = true; + } + + void finalize() + { + if (!useGlobalDaughterVeto) { + return; + } + std::sort(allDaughterIds.begin(), allDaughterIds.end()); + allDaughterIds.erase(std::unique(allDaughterIds.begin(), allDaughterIds.end()), allDaughterIds.end()); + } + + [[nodiscard]] bool accepts(int64_t trackId) const + { + if (!hasSelectedCandidate) { + return false; + } + if (useGlobalDaughterVeto) { + return !std::binary_search(allDaughterIds.begin(), allDaughterIds.end(), trackId); + } + return true; + } + }; + + /// Producer-side track retention for the candidate types enabled by a callback. + struct PairTrackSelection { + SelectedCandidateDaughters v0Candidates; + SelectedCandidateDaughters cascadeCandidates; + bool useV0Candidates = false; + bool useCascadeCandidates = false; + bool useGlobalDaughterVeto = false; + + explicit PairTrackSelection(bool globalDaughterVeto = false) + : v0Candidates(globalDaughterVeto), + cascadeCandidates(globalDaughterVeto), + useGlobalDaughterVeto(globalDaughterVeto) + { + } + + template + bool operator()(TrackType const& track) const + { + if (!useGlobalDaughterVeto) { + return (useV0Candidates && v0Candidates.hasSelectedCandidate) || + (useCascadeCandidates && cascadeCandidates.hasSelectedCandidate); + } + + const auto trackId = static_cast(track.globalIndex()); + bool hasCandidate = false; + if (useV0Candidates && v0Candidates.hasSelectedCandidate) { + hasCandidate = true; + if (!v0Candidates.accepts(trackId)) { + return false; + } + } + if (useCascadeCandidates && cascadeCandidates.hasSelectedCandidate) { + hasCandidate = true; + if (!cascadeCandidates.accepts(trackId)) { + return false; + } + } + return hasCandidate; + } + }; + + struct KeepAllTracks { + template + bool operator()(TrackType const&) const + { + return true; + } + }; + + UltraMicroPidSpecies ultraMicroPidSpecies = UltraMicroPidSpecies::Pion; + bool warnedUltraMicroMomentumRange = false; + + Preslice> tracksPerCollision = aod::track::collisionId; + Preslice> tracksMCPerCollision = aod::track::collisionId; + Preslice v0sPerCollision = aod::v0data::collisionId; + Preslice v0sMCPerCollision = aod::v0data::collisionId; + Preslice cascadesPerCollision = aod::cascdata::collisionId; + Preslice cascadesMCPerCollision = aod::cascdata::collisionId; + Produces reso2trks; ///< Output table for resonance tracks + Produces resoTrackTracks; ///< Output table for original track row IDs + Produces reso2microtrks; ///< Output table for resonance microtracks + Produces resoMicroTrackTracks; ///< Positional original-track soft links for microtracks + Produces reso2mcmicrotrks; ///< Positional MC extension for resonance microtracks + Produces reso2ultramicrotrks; ///< Output table for resonance ultra-microtracks + Produces resoUltraMicroTrackTracks; ///< Output table for original ultra-microtrack row IDs + Produces reso2mctracks; ///< Output table for MC resonance tracks + Produces reso2v0s; ///< Output table for resonance V0s + Produces resoV0V0s; ///< Output table for original V0 row IDs + Produces reso2mcv0s; ///< Output table for MC resonance V0s + Produces reso2cascades; ///< Output table for resonance cascades + Produces resoCascadeCascades; ///< Output table for original cascade row IDs + Produces reso2mccascades; ///< Output table for MC resonance cascades + + // General daughter output options + Configurable cfgFillQA{"cfgFillQA", false, "Fill QA histograms"}; + Configurable cfgDetailTrackQA{"cfgDetailTrackQA", false, "Fill detailed QA histograms for enabled track output tables"}; + Configurable cfgQAITS{"cfgQAITS", false, "Fill Micro001 ITS quality vs pT (requires cfgFillQA and cfgFillMicroTracks)"}; + Configurable cfgQATPC{"cfgQATPC", false, "Fill Micro001 TPC crossed rows vs pT (requires cfgFillQA and cfgFillMicroTracks)"}; + + // Track pre-selection and DCA cuts + struct : ConfigurableGroup { + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable cfgCutMinPt{"cfgCutMinPt", 0.1f, "Minimum pT for tracks (GeV/c)"}; + Configurable cfgCutMaxPt{"cfgCutMaxPt", 999.0f, "Maximum pT for tracks (GeV/c)"}; + Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 5.0f, "TPC PID half-width around the configured species mean (loose preselection)"}; + Configurable pidnSigmaPreSelectionCutTOF{"pidnSigmaPreSelectionCutTOF", 5.0f, "TOF PID half-width around the configured species mean (loose preselection)"}; + Configurable pidnSigmaPreSelectionMeanPion{"pidnSigmaPreSelectionMeanPion", 0.000f, "Offset for TPC PID mean for pions"}; + Configurable pidnSigmaPreSelectionMeanKaon{"pidnSigmaPreSelectionMeanKaon", 0.000f, "Offset for TPC PID mean for kaons"}; + Configurable pidnSigmaPreSelectionMeanProton{"pidnSigmaPreSelectionMeanProton", 0.000f, "Offset for TPC PID mean for protons"}; + Configurable pidnSigmaPreSelectionMeanTOFPion{"pidnSigmaPreSelectionMeanTOFPion", 0.000f, "Offset for TOF PID mean for pions"}; + Configurable pidnSigmaPreSelectionMeanTOFKaon{"pidnSigmaPreSelectionMeanTOFKaon", 0.000f, "Offset for TOF PID mean for kaons"}; + Configurable pidnSigmaPreSelectionMeanTOFProton{"pidnSigmaPreSelectionMeanTOFProton", 0.000f, "Offset for TOF PID mean for protons"}; + Configurable cfgUseTOFPIDPreSelection{"cfgUseTOFPIDPreSelection", false, + "Apply the TOF PID cut to tracks with TOF; false ignores TOF PID, and tracks without TOF use TPC only"}; + Configurable trackSelection{"trackSelection", 3, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; + Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.5f, "Track DCAr cut to PV Maximum"}; + Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 1.0f, "Track DCAz cut to PV Maximum"}; + Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0f, "Track DCAz cut to PV Minimum"}; + Configurable cfgApplyTightDCAPtDepSelection{"cfgApplyTightDCAPtDepSelection", true, "Apply the pT-dependent tight DCA selection"}; + Configurable cfgTightDCAOffset{"cfgTightDCAOffset", 0.004f, "Constant term of the tight DCA threshold (cm)"}; + Configurable cfgTightDCAPtCoefficient{"cfgTightDCAPtCoefficient", 0.013f, "Coefficient of the pT-dependent tight DCA threshold"}; + Configurable cfgTightDCAPtPower{"cfgTightDCAPtPower", 1.f, "Power in tight DCA = offset + coefficient / pT^power"}; + } TrackCuts; + + // V0 and V0-daughter cuts : based on loose cuts in V0s production analysis in pp 13.6 TeV + struct : ConfigurableGroup { + Configurable mincrossedrowsV0s{"mincrossedrowsV0s", 70, "Minimum crossed rows for V0 daughter tracks"}; + Configurable cMinV0PosDCArToPVcut{"cMinV0PosDCArToPVcut", 0.05f, "V0 Positive Track DCAr cut to PV Minimum"}; + Configurable cMinV0NegDCArToPVcut{"cMinV0NegDCArToPVcut", 0.05f, "V0 Negative Track DCAr cut to PV Minimum"}; + Configurable cMinV0Radius{"cMinV0Radius", 0.9f, "Minimum V0 radius from PV"}; + Configurable cMaxV0Radius{"cMaxV0Radius", 200.0f, "Maximum V0 radius from PV"}; + Configurable cMinV0CosPA{"cMinV0CosPA", 0.95f, "Minimum V0 CosPA to PV"}; + } V0Cuts; + + // Additional K0s and Lambda0 selections not covered by V0Cuts: based on loose cuts in V0s production analysis in pp 13.6 TeV + struct : ConfigurableGroup { + Configurable cfgSecondaryRequire{"cfgSecondaryRequire", true, "Secondary cuts on/off"}; + Configurable cfgSecondaryArmenterosCut{"cfgSecondaryArmenterosCut", false, "cut on Armenteros-Podolanski graph"}; + Configurable cfgSecondaryCrossMassHypothesisCut{"cfgSecondaryCrossMassHypothesisCut", false, "Apply cut based on the lambda mass hypothesis"}; + Configurable cfgByPassDauPIDSelection{"cfgByPassDauPIDSelection", false, "Bypass TPC PID preselection for V0 daughters"}; + Configurable cfgSecondaryDauDCAMax{"cfgSecondaryDauDCAMax", 1.0f, "Maximum DCA between V0 daughters"}; + Configurable cfgSecondaryPtMin{"cfgSecondaryPtMin", 0.0f, "Minimum transverse momentum of Secondary"}; + Configurable cfgSecondaryRapidityMax{"cfgSecondaryRapidityMax", 0.5f, "Maximum rapidity of Secondary"}; + Configurable cfgSecondaryDCAtoPVMax{"cfgSecondaryDCAtoPVMax", 0.4f, "Maximum DCA Secondary to PV"}; + Configurable cfgSecondaryProperLifetimeMax{"cfgSecondaryProperLifetimeMax", 40.f, "Maximum Secondary Lifetime"}; + Configurable cfgSecondaryparamArmenterosCut{"cfgSecondaryparamArmenterosCut", 0.2f, "parameter for Armenteros Cut"}; + Configurable cfgSecondaryMassWindow{"cfgSecondaryMassWindow", 0.03f, "Secondary inv mass selection window (GeV/c^2)"}; + Configurable cfgSecondaryCrossMassCutWindow{"cfgSecondaryCrossMassCutWindow", 0.02f, "Secondary inv mass selection window with (anti)lambda hypothesis (GeV/c^2)"}; + } SecondaryCuts; + + // Cascade and cascade-daughter cuts: based on loose cuts in cascades production analysis in pp 13.6 TeV + struct : ConfigurableGroup { + Configurable cfgMinCrossedRowsCascBach{"cfgMinCrossedRowsCascBach", 50, "min crossed rows for bachelor track from cascade"}; + Configurable cMinCascBachDCArToPVcut{"cMinCascBachDCArToPVcut", 0.05f, "Cascade Bachelor Track DCAr cut to PV Minimum"}; + Configurable cMaxCascBachDCArToPVcut{"cMaxCascBachDCArToPVcut", 200.0f, "Cascade Bachelor Track DCAr cut to PV Maximum"}; + Configurable cMaxCascDCAV0Daughters{"cMaxCascDCAV0Daughters", 0.5f, "Cascade DCA between V0 daughters Maximum"}; + Configurable cMaxCascDCACascDaughters{"cMaxCascDCACascDaughters", 1.2f, "Cascade DCA between Casc daughters Maximum"}; + Configurable cMinCascV0CosPA{"cMinCascV0CosPA", 0.98f, "Minimum Cascade V0 CosPA to PV"}; + Configurable cMaxCascV0Radius{"cMaxCascV0Radius", 200.0f, "Maximum Cascade V0 radius from PV"}; + Configurable cMinCascV0Radius{"cMinCascV0Radius", 0.4f, "Minimum Cascade V0 radius from PV"}; + Configurable cMinCascRadius{"cMinCascRadius", 0.4f, "Minimum Cascade radius from PV"}; + Configurable cMaxCascRadius{"cMaxCascRadius", 200.0f, "Maximum Cascade radius from PV"}; + Configurable cMinCascCosPA{"cMinCascCosPA", 0.99, "Minimum Cascade CosPA to PV"}; + Configurable cMaxXiMassWindow{"cMaxXiMassWindow", 0.02f, "Xi mass Window (GeV/c^2)"}; + } CascadeCuts; + + // Derived dataset selections + struct : ConfigurableGroup { + Configurable cfgFillPionTracks{"cfgFillPionTracks", false, "Apply the pion PID filter to every enabled track table"}; + Configurable cfgFillKaonTracks{"cfgFillKaonTracks", false, "Apply the kaon PID filter to every enabled track table"}; + Configurable cfgFillProtonTracks{"cfgFillProtonTracks", false, "Apply the proton PID filter to every enabled track table"}; + Configurable cfgFillK0s{"cfgFillK0s", true, "Fill K0s"}; + Configurable cfgFillLambda0{"cfgFillLambda0", true, "Fill Lambda0"}; + Configurable cfgPairGateMode{"cfgPairGateMode", 0, + "Combined *WithPairGate mode: 0 -> require every enabled candidate type, " + "1 -> require a selected V0 or cascade"}; + Configurable cfgBypassNoPairV0s{"cfgBypassNoPairV0s", false, + "Require a selected V0 and at least one selected collision track in pair-gate mode 0"}; + Configurable cfgBypassNoPairCascades{"cfgBypassNoPairCascades", true, + "Require a selected cascade and at least one selected collision track in pair-gate mode 0"}; + Configurable cfgGlobalDaughterVeto{"cfgGlobalDaughterVeto", false, + "Pair-gate callbacks only: reject a track if its original ID is a daughter " + "of any selected V0 or cascade; false stores all selected collision tracks " + "for mandatory candidate-local rejection through trackId"}; + Configurable cfgFillMicroTracks{"cfgFillMicroTracks", false, "Fill micro tracks"}; + Configurable cfgFillUltraMicroTracks{"cfgFillUltraMicroTracks", false, "Fill ultra micro tracks"}; + Configurable cfgBypassTrackFill{"cfgBypassTrackFill", false, "Bypass the full ResoTracks table fill"}; + Configurable cfgBypassTrackIndexFill{"cfgBypassTrackIndexFill", false, + "Bypass optional source-object soft-link side tables; " + "ResoMicroTracks_001 keeps its inline scalar trackId, but Full/Ultra " + "outputs then cannot perform candidate-local daughter rejection"}; + } FilterForDerivedTables; + + // Track selection filter based on configuration + Filter trackFilter = (TrackCuts.trackSelection.node() == TrackSelectionNone) || + ((TrackCuts.trackSelection.node() == TrackSelectionGlobal) && requireGlobalTrackInFilter()) || // kGlobalTrack = kQualityTracks | kPrimaryTracks | kInAcceptanceTracks + ((TrackCuts.trackSelection.node() == TrackSelectionGlobalWoPtEta) && requireGlobalTrackWoPtEtaInFilter()) || // kGlobalTrackWoPtEta = kQualityTracks | kPrimaryTracks + ((TrackCuts.trackSelection.node() == TrackSelectionGlobalWoDCA) && requireGlobalTrackWoDCAInFilter()) || // kGlobalTrackWoDCA = kQualityTracks | kInAcceptanceTracks + ((TrackCuts.trackSelection.node() == TrackSelectionQuality) && requireQualityTracksInFilter()) || // kQualityTracks = kQualityTracksITS | kQualityTracksTPC + ((TrackCuts.trackSelection.node() == TrackSelectionInAcceptance) && requireTrackCutInFilter(TrackSelectionFlags::kInAcceptanceTracks)); // kInAcceptanceTracks = kPtRange | kEtaRange + Filter trackKinematicsFilter = nabs(aod::track::eta) < TrackCuts.cfgCutEta && + aod::track::pt >= TrackCuts.cfgCutMinPt && + aod::track::pt <= TrackCuts.cfgCutMaxPt; + HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // The daughter task treats fIndexResoCollisions as a scalar v001 row ID. The + // data-model relation keeps its legacy v000 default target; v001 consumers + // must use resoCollision_as() with the exact bound v001 table type for + // dereferencing (aod::ResoCollisions_001 when the parent is not joined). + // Daughter callbacks use the original collision ID from ResoCollisionColls + // to slice input candidates explicitly. The scalar ResoCollisionGroups_001 + // output is retained for consumers that do not need source-AO2D dereferencing. + // Collision mappings are always written; cfgBypassCollIndexFill is retained + // only so existing configuration files remain accepted. + using ResoCollisionWithIndex = soa::Join; + + /** + * @brief Initializes the task + * + * @param context Initialization context + */ + void init(InitContext&) + { + if (FilterForDerivedTables.cfgPairGateMode.value < PairGateModeConfigured || + FilterForDerivedTables.cfgPairGateMode.value > PairGateModeEither) { + LOGF(fatal, "cfgPairGateMode must be 0 (configured gates) or 1 (V0 or cascade)"); + } + const bool useEitherPairGate = FilterForDerivedTables.cfgPairGateMode.value == PairGateModeEither; + const bool processTrackDataEnabled = doprocessData || doprocessDataWithPairGate || + doprocessDataWithV0PairGate || doprocessDataWithCascPairGate; + const bool processTrackMCEnabled = doprocessMC || doprocessMCWithPairGate || + doprocessMCWithV0PairGate || doprocessMCWithCascPairGate; + const bool pairTrackProcessEnabled = doprocessDataWithPairGate || doprocessDataWithV0PairGate || + doprocessDataWithCascPairGate || doprocessMCWithPairGate || + doprocessMCWithV0PairGate || doprocessMCWithCascPairGate; + const bool processV0DataEnabled = doprocessV0Data; + const bool processCascDataEnabled = doprocessCascData; + const bool anyDataProcessEnabled = processTrackDataEnabled || processV0DataEnabled || processCascDataEnabled; + const bool anyMCProcessEnabled = processTrackMCEnabled || doprocessV0MC || doprocessCascMC; + const int enabledTrackProcesses = static_cast(doprocessData) + + static_cast(doprocessDataWithPairGate) + + static_cast(doprocessDataWithV0PairGate) + + static_cast(doprocessDataWithCascPairGate) + + static_cast(doprocessMC) + + static_cast(doprocessMCWithPairGate) + + static_cast(doprocessMCWithV0PairGate) + + static_cast(doprocessMCWithCascPairGate); + + if (enabledTrackProcesses > 1) { + LOGF(fatal, "Only one track process can be enabled in ResonanceDaughterInitializer"); + } + if (pairTrackProcessEnabled && + FilterForDerivedTables.cfgBypassTrackFill && + !FilterForDerivedTables.cfgFillMicroTracks && + !FilterForDerivedTables.cfgFillUltraMicroTracks) { + LOGF(fatal, "A pair-gate process requires at least one enabled Full, Micro, or UltraMicro track output"); + } + if (pairTrackProcessEnabled && !FilterForDerivedTables.cfgGlobalDaughterVeto && + FilterForDerivedTables.cfgBypassTrackIndexFill && + (!FilterForDerivedTables.cfgBypassTrackFill || FilterForDerivedTables.cfgFillUltraMicroTracks)) { + LOGF(warn, + "Default pair-gate mode defers daughter reuse rejection to analysis trackId comparisons, " + "but cfgBypassTrackIndexFill removes that ID from Full/Ultra track outputs"); + } + if (useEitherPairGate && + (doprocessDataWithV0PairGate || doprocessDataWithCascPairGate || + doprocessMCWithV0PairGate || doprocessMCWithCascPairGate)) { + LOGF(fatal, "cfgPairGateMode 1 requires the combined processDataWithPairGate or processMCWithPairGate callback"); + } + if (static_cast(doprocessV0Data) + static_cast(doprocessV0MC) > 1) { + LOGF(fatal, "Only one V0 process can be enabled in ResonanceDaughterInitializer"); + } + if (static_cast(doprocessCascData) + static_cast(doprocessCascMC) > 1) { + LOGF(fatal, "Only one cascade process can be enabled in ResonanceDaughterInitializer"); + } + if ((doprocessData || doprocessMC) && + (useEitherPairGate || FilterForDerivedTables.cfgBypassNoPairV0s || FilterForDerivedTables.cfgBypassNoPairCascades || + FilterForDerivedTables.cfgGlobalDaughterVeto)) { + LOGF(warn, "Pair-gate options are ignored by processData/processMC; enable the matching *WithPairGate process to apply them"); + } + const auto validatePairGateOutputs = [&](bool pairProcessEnabled, + bool v0OutputEnabled, + bool cascadeOutputEnabled, + char const* processName) { + if (!pairProcessEnabled) { + return; + } + if (useEitherPairGate) { + if (!v0OutputEnabled || !cascadeOutputEnabled) { + LOGF(fatal, "%s with pair-gate mode 1 requires both V0 and cascade output processes", processName); + } + } else { + if (!FilterForDerivedTables.cfgBypassNoPairV0s && !FilterForDerivedTables.cfgBypassNoPairCascades) { + LOGF(fatal, "%s with pair-gate mode 0 requires at least one enabled V0/cascade gate", processName); + } + if (FilterForDerivedTables.cfgBypassNoPairV0s && !v0OutputEnabled) { + LOGF(fatal, "%s requires a V0 output process when cfgBypassNoPairV0s is enabled", processName); + } + if (FilterForDerivedTables.cfgBypassNoPairCascades && !cascadeOutputEnabled) { + LOGF(fatal, "%s requires a cascade output process when cfgBypassNoPairCascades is enabled", processName); + } + } + }; + validatePairGateOutputs(doprocessDataWithPairGate, + processV0DataEnabled, + processCascDataEnabled, + "processDataWithPairGate"); + validatePairGateOutputs(doprocessMCWithPairGate, + doprocessV0MC, + doprocessCascMC, + "processMCWithPairGate"); + if (doprocessDataWithV0PairGate && !processV0DataEnabled) { + LOGF(fatal, "processDataWithV0PairGate requires processV0Data"); + } + if (doprocessDataWithCascPairGate && !processCascDataEnabled) { + LOGF(fatal, "processDataWithCascPairGate requires processCascData"); + } + if (doprocessMCWithV0PairGate && !doprocessV0MC) { + LOGF(fatal, "processMCWithV0PairGate requires processV0MC"); + } + if (doprocessMCWithCascPairGate && !doprocessCascMC) { + LOGF(fatal, "processMCWithCascPairGate requires processCascMC"); + } + if (!std::isfinite(TrackCuts.cfgCutMinPt.value) || + !std::isfinite(TrackCuts.cfgCutMaxPt.value) || + TrackCuts.cfgCutMinPt.value < 0.f || + TrackCuts.cfgCutMaxPt.value <= TrackCuts.cfgCutMinPt.value) { + LOGF(fatal, "Track pT limits must be finite and satisfy 0 <= cfgCutMinPt < cfgCutMaxPt"); + } + if (!std::isfinite(TrackCuts.cfgCutEta.value) || TrackCuts.cfgCutEta.value <= 0.f) { + LOGF(fatal, "cfgCutEta must be finite and positive"); + } + if (TrackCuts.trackSelection.value < TrackSelectionNone || TrackCuts.trackSelection.value > TrackSelectionInAcceptance) { + LOGF(fatal, "trackSelection must be in [0, 5]"); + } + if (!std::isfinite(TrackCuts.cMaxDCArToPVcut.value) || + !std::isfinite(TrackCuts.cMaxDCAzToPVcut.value) || + !std::isfinite(TrackCuts.cMinDCAzToPVcut.value) || + TrackCuts.cMaxDCArToPVcut.value < 0. || + TrackCuts.cMinDCAzToPVcut.value < 0. || + TrackCuts.cMaxDCAzToPVcut.value < TrackCuts.cMinDCAzToPVcut.value) { + LOGF(fatal, "Track DCA limits must be finite and satisfy 0 <= cMinDCAzToPVcut <= cMaxDCAzToPVcut and cMaxDCArToPVcut >= 0"); + } + if (!std::isfinite(TrackCuts.pidnSigmaPreSelectionCut.value) || + TrackCuts.pidnSigmaPreSelectionCut.value < 0.f) { + LOGF(fatal, "pidnSigmaPreSelectionCut must be finite and non-negative"); + } + const std::array tpcPidMeans{TrackCuts.pidnSigmaPreSelectionMeanPion.value, + TrackCuts.pidnSigmaPreSelectionMeanKaon.value, + TrackCuts.pidnSigmaPreSelectionMeanProton.value}; + if (!std::all_of(tpcPidMeans.begin(), tpcPidMeans.end(), [](float mean) { + return std::isfinite(mean); + })) { + LOGF(fatal, "All TPC PID preselection means must be finite"); + } + if (TrackCuts.cfgUseTOFPIDPreSelection.value) { + if (!std::isfinite(TrackCuts.pidnSigmaPreSelectionCutTOF.value) || + TrackCuts.pidnSigmaPreSelectionCutTOF.value < 0.f) { + LOGF(fatal, "pidnSigmaPreSelectionCutTOF must be finite and non-negative when TOF PID selection is enabled"); + } + const std::array tofPidMeans{TrackCuts.pidnSigmaPreSelectionMeanTOFPion.value, + TrackCuts.pidnSigmaPreSelectionMeanTOFKaon.value, + TrackCuts.pidnSigmaPreSelectionMeanTOFProton.value}; + if (!std::all_of(tofPidMeans.begin(), tofPidMeans.end(), [](float mean) { + return std::isfinite(mean); + })) { + LOGF(fatal, "All TOF PID preselection means must be finite when TOF PID selection is enabled"); + } + } + if (TrackCuts.cfgApplyTightDCAPtDepSelection.value && + (!std::isfinite(TrackCuts.cfgTightDCAOffset.value) || + !std::isfinite(TrackCuts.cfgTightDCAPtCoefficient.value) || + !std::isfinite(TrackCuts.cfgTightDCAPtPower.value) || + TrackCuts.cfgTightDCAOffset.value < 0.f || + TrackCuts.cfgTightDCAPtCoefficient.value < 0.f || + TrackCuts.cfgTightDCAPtPower.value < 0.f)) { + LOGF(fatal, "Tight-DCA offset, pT coefficient, and power must be finite and non-negative"); + } + if (!std::isfinite(V0Cuts.cMinV0CosPA.value) || + V0Cuts.cMinV0CosPA.value < -1. || V0Cuts.cMinV0CosPA.value >= 1.) { + LOGF(fatal, "cMinV0CosPA must be finite and satisfy -1 <= cMinV0CosPA < 1"); + } + if (!std::isfinite(CascadeCuts.cMinCascCosPA.value) || + CascadeCuts.cMinCascCosPA.value < -1. || CascadeCuts.cMinCascCosPA.value >= 1.) { + LOGF(fatal, "cMinCascCosPA must be finite and satisfy -1 <= cMinCascCosPA < 1"); + } + if (!std::isfinite(CascadeCuts.cMinCascRadius.value) || + !std::isfinite(CascadeCuts.cMaxCascRadius.value) || + CascadeCuts.cMinCascRadius.value < 0. || + CascadeCuts.cMaxCascRadius.value <= CascadeCuts.cMinCascRadius.value) { + LOGF(fatal, "Cascade radius limits must be finite and satisfy 0 <= cMinCascRadius < cMaxCascRadius"); + } + if (FilterForDerivedTables.cfgFillUltraMicroTracks) { + int enabledUltraMicroSpecies = 0; + enabledUltraMicroSpecies += FilterForDerivedTables.cfgFillPionTracks ? 1 : 0; + enabledUltraMicroSpecies += FilterForDerivedTables.cfgFillKaonTracks ? 1 : 0; + enabledUltraMicroSpecies += FilterForDerivedTables.cfgFillProtonTracks ? 1 : 0; + if (enabledUltraMicroSpecies != 1) { + LOGF(fatal, "Exactly one of cfgFillPionTracks, cfgFillKaonTracks, or cfgFillProtonTracks must be enabled when filling ultra-micro tracks"); + } + if (TrackCuts.pidnSigmaPreSelectionCut.value > o2::aod::resoultramicrodaughter::PidNSigma::MaxNSigma) { + LOGF(fatal, "Ultra-micro TPC PID encoding requires pidnSigmaPreSelectionCut <= 5"); + } + if (TrackCuts.cfgUseTOFPIDPreSelection.value && + TrackCuts.pidnSigmaPreSelectionCutTOF.value > o2::aod::resoultramicrodaughter::PidNSigma::MaxNSigma) { + LOGF(fatal, "Ultra-micro TOF PID encoding requires pidnSigmaPreSelectionCutTOF <= 5"); + } + if (FilterForDerivedTables.cfgFillKaonTracks) { + ultraMicroPidSpecies = UltraMicroPidSpecies::Kaon; + } else if (FilterForDerivedTables.cfgFillProtonTracks) { + ultraMicroPidSpecies = UltraMicroPidSpecies::Proton; + } else { + ultraMicroPidSpecies = UltraMicroPidSpecies::Pion; + } + } + + if (cfgFillQA) { + AxisSpec idxAxis = {8, 0.0, 8.0, "Cumulative selection stage"}; + AxisSpec ptAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec dcaPtAxis = {300, 0.f, 30.f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec etaAxis = {100, -1.0f, 1.0f, "#eta"}; + // Keep the azimuthal QA bins aligned with the 18 TPC sectors (10 bins per sector). + AxisSpec phiAxis = {180, 0.0f, TwoPI, "#phi"}; + AxisSpec dcaXYAxis = {1000, -0.5, 0.5, "DCA_{xy} (cm)"}; + AxisSpec dcaZAxis = {1000, -0.5, 0.5, "DCA_{z} (cm)"}; + // Tiny PID has 255 discrete values from -6.35 to 6.35 in 0.05 steps. + // Extend the histogram limits by half a step so every decoded value is + // located at a bin centre instead of on a floating-point bin boundary. + constexpr int NTinyTPCPidBins = aod::pidtpc_tiny::binning::nbins + 1; + constexpr float HalfTinyTPCPidBinWidth = 0.5f * aod::pidtpc_tiny::binning::bin_width; + constexpr int NTinyTOFPidBins = aod::pidtof_tiny::binning::nbins + 1; + constexpr float HalfTinyTOFPidBinWidth = 0.5f * aod::pidtof_tiny::binning::bin_width; + AxisSpec nSigmaTPCAxis = {NTinyTPCPidBins, + aod::pidtpc_tiny::binning::binned_min - HalfTinyTPCPidBinWidth, + aod::pidtpc_tiny::binning::binned_max + HalfTinyTPCPidBinWidth, + "TPC N#sigma"}; + AxisSpec nSigmaTOFAxis = {NTinyTOFPidBins, + aod::pidtof_tiny::binning::binned_min - HalfTinyTOFPidBinWidth, + aod::pidtof_tiny::binning::binned_max + HalfTinyTOFPidBinWidth, + "TOF N#sigma"}; + + if (processTrackDataEnabled || processTrackMCEnabled) { + qaRegistry.add("QA/hGoodTrackIndices", "hGoodTrackIndices", kTH1D, {idxAxis}); + auto trackSelection = qaRegistry.get(HIST("QA/hGoodTrackIndices")); + trackSelection->GetXaxis()->SetBinLabel(1, "Before DCA cuts"); + trackSelection->GetXaxis()->SetBinLabel(2, "Finite DCA, |DCA_{xy}| #leq max"); + trackSelection->GetXaxis()->SetBinLabel(3, "min #leq |DCA_{z}| #leq max"); + trackSelection->GetXaxis()->SetBinLabel(8, "Pass DCA selection"); + if (processTrackMCEnabled) { + qaRegistry.add("QA/hGoodMCTrackIndices", "hGoodMCTrackIndices", kTH1D, {idxAxis}); + qaRegistry.get(HIST("QA/hGoodMCTrackIndices"))->GetXaxis()->SetBinLabel(1, "MC path: before DCA cuts"); + } + if (cfgDetailTrackQA) { + if (!FilterForDerivedTables.cfgBypassTrackFill) { + qaRegistry.add("QA/h4TrackPtEtaPhi", "ResoTracks pT, eta, phi", kTHnSparseD, {ptAxis, etaAxis, phiAxis}); + qaRegistry.add("QA/h2TrackDCAxyVsPt", "ResoTracks DCAxy vs pT", kTH2D, {dcaPtAxis, dcaXYAxis}); + qaRegistry.add("QA/h2TrackDCAzVsPt", "ResoTracks DCAz vs pT", kTH2D, {dcaPtAxis, dcaZAxis}); + qaRegistry.add("QA/h4TrackTPCnSigma", "ResoTracks TPC nSigma Pi, Ka, Pr as pT", kTHnSparseD, {ptAxis, nSigmaTPCAxis, nSigmaTPCAxis, nSigmaTPCAxis}); + qaRegistry.add("QA/h4TrackTOFnSigma", "ResoTracks TOF nSigma Pi, Ka, Pr as pT", kTHnSparseD, {ptAxis, nSigmaTOFAxis, nSigmaTOFAxis, nSigmaTOFAxis}); + } + if (FilterForDerivedTables.cfgFillMicroTracks) { + qaRegistry.add("QA/h4MicroTrackPtEtaPhi", "ResoMicroTracks pT, eta, phi", kTHnSparseD, {ptAxis, etaAxis, phiAxis}); + qaRegistry.add("QA/h2MicroTrackDCAxyVsPt", "ResoMicroTracks DCAxy vs pT", kTH2D, {dcaPtAxis, dcaXYAxis}); + qaRegistry.add("QA/h2MicroTrackDCAzVsPt", "ResoMicroTracks DCAz vs pT", kTH2D, {dcaPtAxis, dcaZAxis}); + qaRegistry.add("QA/h4MicroTrackTPCnSigma", "ResoMicroTracks TPC nSigma Pi, Ka, Pr as pT", kTHnSparseD, {ptAxis, nSigmaTPCAxis, nSigmaTPCAxis, nSigmaTPCAxis}); + qaRegistry.add("QA/h4MicroTrackTOFnSigma", "ResoMicroTracks TOF nSigma Pi, Ka, Pr as pT", kTHnSparseD, {ptAxis, nSigmaTOFAxis, nSigmaTOFAxis, nSigmaTOFAxis}); + } + if (FilterForDerivedTables.cfgFillUltraMicroTracks) { + qaRegistry.add("QA/h4UltraMicroTrackPtEtaPhi", "ResoUltraMicroTracks pT, eta, phi", kTHnSparseD, {ptAxis, etaAxis, phiAxis}); + qaRegistry.add("QA/h2UltraMicroTrackDCAxyVsPt", "ResoUltraMicroTracks DCAxy vs pT", kTH2D, {dcaPtAxis, dcaXYAxis}); + qaRegistry.add("QA/h2UltraMicroTrackDCAzVsPt", "ResoUltraMicroTracks DCAz vs pT", kTH2D, {dcaPtAxis, dcaZAxis}); + qaRegistry.add("QA/h4UltraMicroTrackTPCnSigma", "ResoUltraMicroTracks TPC nSigma Pi, Ka, Pr as pT", kTHnSparseD, {ptAxis, nSigmaTPCAxis, nSigmaTPCAxis, nSigmaTPCAxis}); + qaRegistry.add("QA/h4UltraMicroTrackTOFnSigma", "ResoUltraMicroTracks TOF nSigma Pi, Ka, Pr as pT", kTHnSparseD, {ptAxis, nSigmaTOFAxis, nSigmaTOFAxis, nSigmaTOFAxis}); + } + } + } + + if ((processTrackDataEnabled || processTrackMCEnabled) && FilterForDerivedTables.cfgFillMicroTracks) { + AxisSpec qualityPtAxis = {300, 0.f, 30.f, "#it{p}_{T} (GeV/#it{c})"}; + if (cfgQAITS) { + AxisSpec itsMapAxis = {128, -0.5, 127.5, "ITS cluster map"}; + AxisSpec itsNClsAxis = {8, -0.5, 7.5, "ITS occupied layers"}; + AxisSpec itsInnerBarrelNClsAxis = {4, -0.5, 3.5, "ITS inner-barrel occupied layers"}; + AxisSpec itsLayerAxis = {NumberOfITSLayers, -0.5, NumberOfITSLayers - 0.5, "ITS layer (0 = innermost)"}; + qaRegistry.add("QA/h2MicroTrackITSClusterMapVsPt", "ResoMicroTracks ITS cluster map vs pT", kTH2D, {qualityPtAxis, itsMapAxis}); + qaRegistry.add("QA/h2MicroTrackITSNClsVsPt", "ResoMicroTracks ITS occupied layers vs pT", kTH2D, {qualityPtAxis, itsNClsAxis}); + qaRegistry.add("QA/h2MicroTrackITSNClsInnerBarrelVsPt", "ResoMicroTracks ITS inner-barrel occupied layers vs pT", kTH2D, {qualityPtAxis, itsInnerBarrelNClsAxis}); + qaRegistry.add("QA/h2MicroTrackITSLayerHitsVsPt", "ResoMicroTracks ITS hits vs pT (one entry per occupied layer)", kTH2D, {qualityPtAxis, itsLayerAxis}); + } + if (cfgQATPC) { + AxisSpec tpcCrossedRowsAxis = {256, -0.5, 255.5, "TPC crossed rows"}; + qaRegistry.add("QA/h2MicroTrackTPCCrossedRowsVsPt", "ResoMicroTracks TPC crossed rows vs pT", kTH2D, {qualityPtAxis, tpcCrossedRowsAxis}); + } + } + + if (processV0DataEnabled || doprocessV0MC) { + qaRegistry.add("QA/hGoodV0Indices", "hGoodV0Indices", kTH1D, {idxAxis}); + auto v0Selection = qaRegistry.get(HIST("QA/hGoodV0Indices")); + v0Selection->GetXaxis()->SetBinLabel(1, "Before V0 cuts"); + v0Selection->GetXaxis()->SetBinLabel(2, "Daughter TPC rows"); + v0Selection->GetXaxis()->SetBinLabel(3, "Daughter |DCA_{xy}| to PV"); + v0Selection->GetXaxis()->SetBinLabel(4, "V0 radius window"); + v0Selection->GetXaxis()->SetBinLabel(5, "V0 cosPA cut passed"); + if (doprocessV0MC) { + qaRegistry.add("QA/hGoodMCV0Indices", "hGoodMCV0Indices", kTH1D, {idxAxis}); + qaRegistry.get(HIST("QA/hGoodMCV0Indices"))->GetXaxis()->SetBinLabel(1, "Reco V0 cuts passed (MC)"); + } + } + + if (processCascDataEnabled || doprocessCascMC) { + qaRegistry.add("QA/hGoodCascIndices", "hGoodCascIndices", kTH1D, {idxAxis}); + auto cascadeSelection = qaRegistry.get(HIST("QA/hGoodCascIndices")); + cascadeSelection->GetXaxis()->SetBinLabel(1, "Before cascade cuts"); + cascadeSelection->GetXaxis()->SetBinLabel(2, "Bachelor TPC rows"); + cascadeSelection->GetXaxis()->SetBinLabel(3, "Bachelor DCA_{xy} window"); + cascadeSelection->GetXaxis()->SetBinLabel(4, "V0/cascade daughter DCA"); + cascadeSelection->GetXaxis()->SetBinLabel(5, "Cascade/V0 cosPA"); + cascadeSelection->GetXaxis()->SetBinLabel(6, "V0 radius window"); + cascadeSelection->GetXaxis()->SetBinLabel(7, "Cascade radius window"); + cascadeSelection->GetXaxis()->SetBinLabel(8, "#Xi mass window"); + if (doprocessCascMC) { + qaRegistry.add("QA/hGoodMCCascIndices", "hGoodMCCascIndices", kTH1D, {idxAxis}); + qaRegistry.get(HIST("QA/hGoodMCCascIndices"))->GetXaxis()->SetBinLabel(1, "Reco cascade cuts passed (MC)"); + } + } + } + if (processTrackDataEnabled || processTrackMCEnabled) { + LOGF(info, "ResonanceDaughterInitializer initialized with tracks"); + } + if (processV0DataEnabled || doprocessV0MC) { + LOGF(info, "ResonanceDaughterInitializer initialized with V0s"); + } + if (processCascDataEnabled || doprocessCascMC) { + LOGF(info, "ResonanceDaughterInitializer initialized with cascades"); + } + + // Check if none of the processes are enabled + if (!doprocessDummy && !anyDataProcessEnabled && !anyMCProcessEnabled) { + LOGF(fatal, "ResonanceDaughterInitializer not initialized, enable at least one process"); + } + } + static bool passesCenteredPID(float nSigma, float mean, float cut) + { + return std::isfinite(nSigma) && std::abs(nSigma - mean) < cut; + } + + bool passesPIDPreSelection(float tpcNSigma, + float tofNSigma, + bool hasTOF, + float tpcMean, + float tofMean, + float tpcCut, + float tofCut) const + { + if (!passesCenteredPID(tpcNSigma, tpcMean, tpcCut)) { + return false; + } + return !TrackCuts.cfgUseTOFPIDPreSelection.value || + !hasTOF || passesCenteredPID(tofNSigma, tofMean, tofCut); + } + + template + bool filterTrack(T const& track) + { + // if no selection is requested, return true + if (!FilterForDerivedTables.cfgFillPionTracks && !FilterForDerivedTables.cfgFillKaonTracks && !FilterForDerivedTables.cfgFillProtonTracks) { + return true; + } + const float tpcCut = TrackCuts.pidnSigmaPreSelectionCut.value; + const float tofCut = TrackCuts.pidnSigmaPreSelectionCutTOF.value; + if (FilterForDerivedTables.cfgFillPionTracks && + passesPIDPreSelection(track.tpcNSigmaPi(), track.tofNSigmaPi(), track.hasTOF(), + TrackCuts.pidnSigmaPreSelectionMeanPion.value, + TrackCuts.pidnSigmaPreSelectionMeanTOFPion.value, tpcCut, tofCut)) { + return true; + } + if (FilterForDerivedTables.cfgFillKaonTracks && + passesPIDPreSelection(track.tpcNSigmaKa(), track.tofNSigmaKa(), track.hasTOF(), + TrackCuts.pidnSigmaPreSelectionMeanKaon.value, + TrackCuts.pidnSigmaPreSelectionMeanTOFKaon.value, tpcCut, tofCut)) { + return true; + } + if (FilterForDerivedTables.cfgFillProtonTracks && + passesPIDPreSelection(track.tpcNSigmaPr(), track.tofNSigmaPr(), track.hasTOF(), + TrackCuts.pidnSigmaPreSelectionMeanProton.value, + TrackCuts.pidnSigmaPreSelectionMeanTOFProton.value, tpcCut, tofCut)) { + return true; + } + return false; + } - resoCollisions(0, 0, 0, collision.posX(), collision.posY(), collision.posZ(), centrality, dBz, 0); - if (!cfgBypassCollIndexFill) { - resoCollisionColls(collision.globalIndex()); + template + bool filterV0(CollisionType const& collision, V0Type const& v0, TrackType const&) + { + if (!FilterForDerivedTables.cfgFillK0s && !FilterForDerivedTables.cfgFillLambda0) { + return true; + } + if (!SecondaryCuts.cfgSecondaryRequire) { + return true; + } + if (v0.dcaV0daughters() > SecondaryCuts.cfgSecondaryDauDCAMax || + v0.pt() < SecondaryCuts.cfgSecondaryPtMin || + v0.dcav0topv() > SecondaryCuts.cfgSecondaryDCAtoPVMax) { + return false; + } + if (SecondaryCuts.cfgSecondaryArmenterosCut && + v0.qtarm() < SecondaryCuts.cfgSecondaryparamArmenterosCut * std::abs(v0.alpha())) { + return false; + } + + const float decayLengthOverMomentum = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()); + const auto posTrack = v0.template posTrack_as(); + const auto negTrack = v0.template negTrack_as(); + const bool bypassDaughterPID = SecondaryCuts.cfgByPassDauPIDSelection; + const float daughterPIDCut = TrackCuts.pidnSigmaPreSelectionCut.value; + bool selected = false; + if (FilterForDerivedTables.cfgFillK0s) { + const bool passesK0DaughterPID = bypassDaughterPID || + (passesCenteredPID(posTrack.tpcNSigmaPi(), TrackCuts.pidnSigmaPreSelectionMeanPion.value, daughterPIDCut) && + passesCenteredPID(negTrack.tpcNSigmaPi(), TrackCuts.pidnSigmaPreSelectionMeanPion.value, daughterPIDCut)); + const bool passesK0 = std::fabs(v0.yK0Short()) <= SecondaryCuts.cfgSecondaryRapidityMax && + decayLengthOverMomentum * MassK0Short <= SecondaryCuts.cfgSecondaryProperLifetimeMax && + std::fabs(v0.mK0Short() - MassK0Short) <= SecondaryCuts.cfgSecondaryMassWindow && + passesK0DaughterPID && + (!SecondaryCuts.cfgSecondaryCrossMassHypothesisCut || + (std::fabs(v0.mLambda() - MassLambda0) >= SecondaryCuts.cfgSecondaryCrossMassCutWindow && + std::fabs(v0.mAntiLambda() - MassLambda0Bar) >= SecondaryCuts.cfgSecondaryCrossMassCutWindow)); + selected = passesK0; + } + if (FilterForDerivedTables.cfgFillLambda0) { + const bool passesLambdaPID = bypassDaughterPID || + (passesCenteredPID(posTrack.tpcNSigmaPr(), TrackCuts.pidnSigmaPreSelectionMeanProton.value, daughterPIDCut) && + passesCenteredPID(negTrack.tpcNSigmaPi(), TrackCuts.pidnSigmaPreSelectionMeanPion.value, daughterPIDCut)); + const bool passesAntiLambdaPID = bypassDaughterPID || + (passesCenteredPID(posTrack.tpcNSigmaPi(), TrackCuts.pidnSigmaPreSelectionMeanPion.value, daughterPIDCut) && + passesCenteredPID(negTrack.tpcNSigmaPr(), TrackCuts.pidnSigmaPreSelectionMeanProton.value, daughterPIDCut)); + const bool passesLambdaMassAndPID = + (std::fabs(v0.mLambda() - MassLambda0) <= SecondaryCuts.cfgSecondaryMassWindow && passesLambdaPID) || + (std::fabs(v0.mAntiLambda() - MassLambda0Bar) <= SecondaryCuts.cfgSecondaryMassWindow && passesAntiLambdaPID); + const bool passesLambda = std::fabs(v0.yLambda()) <= SecondaryCuts.cfgSecondaryRapidityMax && + decayLengthOverMomentum * MassLambda0 <= SecondaryCuts.cfgSecondaryProperLifetimeMax && + passesLambdaMassAndPID && + (!SecondaryCuts.cfgSecondaryCrossMassHypothesisCut || + std::fabs(v0.mK0Short() - MassK0Short) >= SecondaryCuts.cfgSecondaryCrossMassCutWindow); + selected = selected || passesLambda; + } + return selected; + } + + template + bool isMicroTrackSelected(CollisionType const&, TrackType const& track) + { + if (!std::isfinite(track.dcaXY()) || !std::isfinite(track.dcaZ())) { + return false; + } + if (std::fabs(track.dcaXY()) > TrackCuts.cMaxDCArToPVcut) { + return false; + } + if (std::fabs(track.dcaZ()) > TrackCuts.cMaxDCAzToPVcut || std::fabs(track.dcaZ()) < TrackCuts.cMinDCAzToPVcut) { + return false; + } + + return true; + } + + template + bool isTrackSelected(CollisionType const&, TrackType const& track, bool fillSelectionQA = true) + { + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodTrackIndices"), 0.5); + if constexpr (isMC) { + qaRegistry.fill(HIST("QA/hGoodMCTrackIndices"), 0.5); + } + } + if (!std::isfinite(track.dcaXY()) || !std::isfinite(track.dcaZ())) { + return false; + } + if (std::fabs(track.dcaXY()) > TrackCuts.cMaxDCArToPVcut) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodTrackIndices"), 1.5); + } + if (std::fabs(track.dcaZ()) > TrackCuts.cMaxDCAzToPVcut || std::fabs(track.dcaZ()) < TrackCuts.cMinDCAzToPVcut) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodTrackIndices"), 2.5); + qaRegistry.fill(HIST("QA/hGoodTrackIndices"), 7.5); + } + return true; + } + + template + bool isV0Selected(CollisionType const&, V0Type const& v0, TrackType const&, bool fillSelectionQA = true) + { + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodV0Indices"), 0.5); + } + + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + if (posTrack.tpcNClsCrossedRows() < V0Cuts.mincrossedrowsV0s || negTrack.tpcNClsCrossedRows() < V0Cuts.mincrossedrowsV0s) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodV0Indices"), 1.5); + } + if (std::fabs(posTrack.dcaXY()) < V0Cuts.cMinV0PosDCArToPVcut || + std::fabs(negTrack.dcaXY()) < V0Cuts.cMinV0NegDCArToPVcut) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodV0Indices"), 2.5); + } + if (v0.v0radius() > V0Cuts.cMaxV0Radius || v0.v0radius() < V0Cuts.cMinV0Radius) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodV0Indices"), 3.5); + } + if (v0.v0cosPA() < V0Cuts.cMinV0CosPA) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodV0Indices"), 4.5); + if constexpr (isMC) { + qaRegistry.fill(HIST("QA/hGoodMCV0Indices"), 0.5); + } + } + return true; + } + + template + bool isCascSelected(CollisionType const& collision, CascType const& casc, TrackType const&, bool fillSelectionQA = true) + { + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 0.5); + } + + auto bachelor = casc.template bachelor_as(); + if (bachelor.tpcNClsCrossedRows() < CascadeCuts.cfgMinCrossedRowsCascBach) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 1.5); + } + if (std::fabs(bachelor.dcaXY()) < CascadeCuts.cMinCascBachDCArToPVcut || + std::fabs(bachelor.dcaXY()) > CascadeCuts.cMaxCascBachDCArToPVcut) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 2.5); + } + if (casc.dcaV0daughters() > CascadeCuts.cMaxCascDCAV0Daughters || + casc.dcacascdaughters() > CascadeCuts.cMaxCascDCACascDaughters) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 3.5); + } + if (casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < CascadeCuts.cMinCascCosPA || + casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) < CascadeCuts.cMinCascV0CosPA) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 4.5); + } + auto v0Radius = casc.v0radius(); + if (v0Radius > CascadeCuts.cMaxCascV0Radius || v0Radius < CascadeCuts.cMinCascV0Radius) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 5.5); + } + auto cascRadius = casc.cascradius(); + if (cascRadius > CascadeCuts.cMaxCascRadius || cascRadius < CascadeCuts.cMinCascRadius) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 6.5); + } + if (std::abs(casc.mXi() - MassXiMinus) > CascadeCuts.cMaxXiMassWindow) { + return false; + } + if (cfgFillQA && fillSelectionQA) { + qaRegistry.fill(HIST("QA/hGoodCascIndices"), 7.5); + if constexpr (isMC) { + qaRegistry.fill(HIST("QA/hGoodMCCascIndices"), 0.5); + } + } + return true; + } + + /// @brief Find a selected V0 and collect daughter IDs only for the optional global veto + template + SelectedCandidateDaughters collectSelectedV0Daughters(CollisionType const& collision, + V0Type const& v0s, + TrackType const& tracks, + bool useGlobalDaughterVeto) + { + SelectedCandidateDaughters selectedCandidates{useGlobalDaughterVeto}; + for (auto const& v0 : v0s) { + if (!isV0Selected(collision, v0, tracks, false) || + !filterV0(collision, v0, tracks)) { + continue; + } + if (!useGlobalDaughterVeto) { + selectedCandidates.addCandidate(); + break; + } + selectedCandidates.addCandidate( + std::array{static_cast(v0.posTrackId()), + static_cast(v0.negTrackId())}); + } + selectedCandidates.finalize(); + return selectedCandidates; + } + + /// @brief Find a selected cascade and collect daughter IDs only for the optional global veto + template + SelectedCandidateDaughters collectSelectedCascadeDaughters(CollisionType const& collision, + CascType const& cascades, + TrackType const& tracks, + bool useGlobalDaughterVeto) + { + SelectedCandidateDaughters selectedCandidates{useGlobalDaughterVeto}; + for (auto const& casc : cascades) { + if (!isCascSelected(collision, casc, tracks, false)) { + continue; + } + if (!useGlobalDaughterVeto) { + selectedCandidates.addCandidate(); + break; + } + selectedCandidates.addCandidate( + std::array{static_cast(casc.posTrackId()), + static_cast(casc.negTrackId()), + static_cast(casc.bachelorId())}); + } + selectedCandidates.finalize(); + return selectedCandidates; + } + + /// @brief Build the per-track selection while preserving the configured collision gate + template + bool preparePairTrackSelection(CollisionType const& collision, + TrackType const& tracks, + V0Type const& v0s, + CascType const& cascades, + V0PresliceType const& v0Preslice, + CascPresliceType const& cascPreslice, + PairTrackSelection& selection) + { + const bool useEitherPairGate = FilterForDerivedTables.cfgPairGateMode.value == PairGateModeEither; + selection.useV0Candidates = useEitherPairGate || FilterForDerivedTables.cfgBypassNoPairV0s; + selection.useCascadeCandidates = useEitherPairGate || FilterForDerivedTables.cfgBypassNoPairCascades; + + if (selection.useV0Candidates) { + auto v0sThisCollision = v0s.sliceBy(v0Preslice, collision.collisionId()); + selection.v0Candidates = + collectSelectedV0Daughters(collision, v0sThisCollision, tracks, selection.useGlobalDaughterVeto); + } + if (selection.useCascadeCandidates && + (!useEitherPairGate || selection.useGlobalDaughterVeto || !selection.v0Candidates.hasSelectedCandidate)) { + auto cascadesThisCollision = cascades.sliceBy(cascPreslice, collision.collisionId()); + selection.cascadeCandidates = + collectSelectedCascadeDaughters(collision, cascadesThisCollision, tracks, selection.useGlobalDaughterVeto); + } + + if (useEitherPairGate) { + return selection.v0Candidates.hasSelectedCandidate || + selection.cascadeCandidates.hasSelectedCandidate; + } + + if (FilterForDerivedTables.cfgBypassNoPairV0s && !selection.v0Candidates.hasSelectedCandidate) { + return false; + } + if (FilterForDerivedTables.cfgBypassNoPairCascades && !selection.cascadeCandidates.hasSelectedCandidate) { + return false; + } + return true; + } + + /// @brief Round and saturate a floating-point value into a persistent integer column + template + static Integer quantizeSaturated(float value, double scale) + { + constexpr Integer LowerLimit = std::numeric_limits::lowest(); + constexpr Integer UpperLimit = std::numeric_limits::max(); + if (std::isnan(value)) { + return UpperLimit; + } + if (!std::isfinite(value)) { + return std::signbit(value) ? LowerLimit : UpperLimit; + } + const double rounded = std::round(static_cast(value) * scale); + if (rounded <= static_cast(LowerLimit)) { + return LowerLimit; } + if (rounded >= static_cast(UpperLimit)) { + return UpperLimit; + } + return static_cast(rounded); } - PROCESS_SWITCH(ResonanceModuleInitializer, processRun2, "process for RUN2", false); - /** - * @brief Processes Run3 MC data - * - * @param collision Collision data - * @param mcParticles MC particles - * @param mcCollisions MC collisions - */ - void processRun3MC(soa::Filtered::iterator const& collision, - aod::McParticles const& mcParticles, GenMCCollisions const&) + static bool quantizeP(float p, int16_t& quantized) { - if (cfgEvtUseRCTFlagChecker && !rctChecker(collision)) - return; - fillMCCollision(collision, mcParticles); + if (!std::isfinite(p)) { + return false; + } + const double rounded = std::round(static_cast(p) * MomentumQuantizationScale); + if (rounded < static_cast(std::numeric_limits::min()) || + rounded > static_cast(std::numeric_limits::max())) { + return false; + } + quantized = static_cast(rounded); + return true; } - PROCESS_SWITCH(ResonanceModuleInitializer, processRun3MC, "process MC for RUN3", false); - /** - * @brief Processes Run2 MC data - * - * @param collision Collision data - * @param mcParticles MC particles - */ - void processRun2MC(soa::Filtered::iterator const& collision, - aod::McParticles const& mcParticles) + float tightDCAThreshold(float pt) const { - fillMCCollision(collision, mcParticles); + if (!std::isfinite(pt) || pt <= 0.f) { + return -1.f; + } + if (TrackCuts.cfgTightDCAPtCoefficient.value == 0.f) { + return TrackCuts.cfgTightDCAOffset.value; + } + const float threshold = TrackCuts.cfgTightDCAOffset.value + + TrackCuts.cfgTightDCAPtCoefficient.value / + std::pow(pt, TrackCuts.cfgTightDCAPtPower.value); + return std::isfinite(threshold) ? threshold : -1.f; } - PROCESS_SWITCH(ResonanceModuleInitializer, processRun2MC, "process MC for RUN2", false); - /** - * @brief Processes Spherocity - * - * @param collision Collision data - * @param tracks Track data - */ - void processSpherocity(soa::Filtered::iterator const& /*collision*/, aod::ResoTrackCandidates const& tracks) + template + bool evaluatePtDependentDCA(TrackType const& track, + bool& passedPtDependentDCAxy, + bool& passedPtDependentDCAz) const { - float spherocity = computeSpherocity(tracks, cfgTrackSphMin, cfgTrackSphDef); - resoSpheroCollisions(spherocity); + passedPtDependentDCAxy = false; + passedPtDependentDCAz = false; + if (!TrackCuts.cfgApplyTightDCAPtDepSelection.value) { + return true; + } + const float dcaThreshold = tightDCAThreshold(track.pt()); + if (dcaThreshold < 0.f) { + return false; + } + passedPtDependentDCAxy = std::isfinite(track.dcaXY()) && std::abs(track.dcaXY()) < dcaThreshold; + passedPtDependentDCAz = std::isfinite(track.dcaZ()) && std::abs(track.dcaZ()) < dcaThreshold; + return passedPtDependentDCAxy && passedPtDependentDCAz; } - PROCESS_SWITCH(ResonanceModuleInitializer, processSpherocity, "process Spherocity", false); - /** - * @brief Processes Event Plane - * - * @param collision Collision data with Qvectors - * @param tracks Track data - */ - void processEventPlane(soa::Filtered>::iterator const& collision) + template + bool isFullTrackOutputSelected(CollisionType const& collision, + TrackType const& track, + bool fillSelectionQA = true) { - resoEvtPlCollisions(getEvtPl(collision), getEvtPlRes(collision, evtPlDetId, evtPlRefAId), getEvtPlRes(collision, evtPlDetId, evtPlRefBId), getEvtPlRes(collision, evtPlRefAId, evtPlRefBId)); + return isTrackSelected(collision, track, fillSelectionQA) && filterTrack(track); } - PROCESS_SWITCH(ResonanceModuleInitializer, processEventPlane, "process Event Plane", false); -}; - -/** - * @brief Initializer for the resonance daughters producer - * - * This struct initializes and processes daughters for resonance studies. - * It applies daughter selection criteria and fills QA histograms for daughter properties. - */ -struct ResonanceDaughterInitializer { - SliceCache cache; - Produces reso2trks; ///< Output table for resonance tracks - Produces resoTrackTracks; ///< Output table for resonance track tracks - Produces reso2microtrks; ///< Output table for resonance microtracks - Produces resoMicroTrackTracks; ///< Output table for resonance microtrack tracks - Produces reso2mctracks; ///< Output table for MC resonance tracks - Produces reso2v0s; ///< Output table for resonance V0s - Produces resoV0V0s; ///< Output table for resonance V0-V0s - Produces reso2mcv0s; ///< Output table for MC resonance V0s - Produces reso2cascades; ///< Output table for resonance cascades - Produces resoCascadeCascades; ///< Output table for resonance cascade-cascades - Produces reso2mccascades; ///< Output table for MC resonance cascades - - // Configurables - Configurable cfgFillQA{"cfgFillQA", false, "Fill QA histograms"}; - Configurable cfgFillMicroTracks{"cfgFillMicroTracks", false, "Fill micro tracks"}; - Configurable cfgBypassTrackFill{"cfgBypassTrackFill", true, "Bypass track fill"}; - Configurable cfgBypassTrackIndexFill{"cfgBypassTrackIndexFill", false, "Bypass track index fill"}; - - // Configurables for tracks - Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 2.0, "Track DCAr cut to PV Maximum"}; - Configurable cMinDCArToPVcut{"cMinDCArToPVcut", 0.0, "Track DCAr cut to PV Minimum"}; - Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 2.0, "Track DCAz cut to PV Maximum"}; - Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0, "Track DCAz cut to PV Minimum"}; - Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 5.0f, "TPC and TOF PID cut (loose, improve performance)"}; - Configurable trackSelection{"trackSelection", 1, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; - - // Configurables for V0s - Configurable cMinV0Radius{"cMinV0Radius", 0.0, "Minimum V0 radius from PV"}; - Configurable cMaxV0Radius{"cMaxV0Radius", 200.0, "Maximum V0 radius from PV"}; - Configurable cMinV0CosPA{"cMinV0CosPA", 0.995, "Minimum V0 CosPA to PV"}; - - // Configurables for cascades - Configurable cMinCascRadius{"cMinCascRadius", 0.0, "Minimum Cascade radius from PV"}; - Configurable cMaxCascRadius{"cMaxCascRadius", 200.0, "Maximum Cascade radius from PV"}; - Configurable cMinCascCosPA{"cMinCascCosPA", 0.97, "Minimum Cascade CosPA to PV"}; - // Derived dataset selections - struct : ConfigurableGroup { - Configurable cfgFillPionTracks{"cfgFillPionTracks", false, "Fill pion tracks"}; - Configurable cfgFillKaonTracks{"cfgFillKaonTracks", false, "Fill kaon tracks"}; - Configurable cfgFillProtonTracks{"cfgFillProtonTracks", false, "Fill proton tracks"}; - Configurable cfgFillPionMicroTracks{"cfgFillPionMicroTracks", false, "Fill pion micro tracks"}; - Configurable cfgFillKaonMicroTracks{"cfgFillKaonMicroTracks", false, "Fill kaon micro tracks"}; - Configurable cfgFillProtonMicroTracks{"cfgFillProtonMicroTracks", false, "Fill proton micro tracks"}; - } FilterForDerivedTables; + template + bool isMicroTrackOutputSelected(CollisionType const& collision, + TrackType const& track, + bool& passedPtDependentDCAxy, + bool& passedPtDependentDCAz) + { + return isMicroTrackSelected(collision, track) && + filterTrack(track) && + evaluatePtDependentDCA(track, passedPtDependentDCAxy, passedPtDependentDCAz); + } - // Filters - Filter dcaXYFilter = nabs(aod::track::dcaXY) < cMaxDCArToPVcut && nabs(aod::track::dcaXY) > cMinDCArToPVcut; - Filter dcaZFilter = nabs(aod::track::dcaZ) < cMaxDCAzToPVcut && nabs(aod::track::dcaZ) > cMinDCAzToPVcut; - Preslice perMcCollision = aod::mcparticle::mcCollisionId; + template + bool isUltraMicroTrackOutputSelected(CollisionType const& collision, TrackType const& track) + { + return isMicroTrackSelected(collision, track) && + filterTrack(track) && + o2::aod::resoultramicrodaughter::DCAEncoding::isValid(track.dcaXY()) && + o2::aod::resoultramicrodaughter::DCAEncoding::isValid(track.dcaZ()); + } - // Track selection filter based on configuration - Filter trackFilter = (trackSelection.node() == 0) || - ((trackSelection.node() == 1) && requireGlobalTrackInFilter()) || // kGlobalTrack = kQualityTracks | kPrimaryTracks | kInAcceptanceTracks - ((trackSelection.node() == 2) && requireGlobalTrackWoPtEtaInFilter()) || // kGlobalTrackWoPtEta = kQualityTracks | kPrimaryTracks - ((trackSelection.node() == 3) && requireGlobalTrackWoDCAInFilter()) || // kGlobalTrackWoDCA = kQualityTracks | kInAcceptanceTracks - ((trackSelection.node() == 4) && requireQualityTracksInFilter()) || // kQualityTracks = kQualityTracksITS | kQualityTracksTPC - ((trackSelection.node() == 5) && requireTrackCutInFilter(TrackSelectionFlags::kInAcceptanceTracks)); // kInAcceptanceTracks = kPtRange | kEtaRange - HistogramRegistry qaRegistry{"QAHistos", {}, OutputObjHandlingPolicy::AnalysisObject}; + template + static bool quantizeUltraMicroMomentum(TrackType const& track, + int16_t& px1000, + int16_t& py1000, + int16_t& pz1000) + { + return quantizeP(track.px(), px1000) && + quantizeP(track.py(), py1000) && + quantizeP(track.pz(), pz1000); + } - /** - * @brief Initializes the task - * - * @param context Initialization context - */ - void init(InitContext&) + /// Check that every enabled track output will receive at least one row. + template + bool hasTracksForEnabledOutputs(CollisionType const& collision, + TrackTableType const& tracks, + TrackPredicate const& keepTrack) { - if (cfgFillQA) { - AxisSpec idxAxis = {8, -0.5, 7.5, "Index"}; - AxisSpec ptAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec etaAxis = {100, -1.0f, 1.0f, "#eta"}; - AxisSpec phiAxis = {100, 0.0f, TwoPI, "#phi"}; + constexpr uint8_t FullTrackOutput = 1u << 0; + constexpr uint8_t MicroTrackOutput = 1u << 1; + constexpr uint8_t UltraMicroTrackOutput = 1u << 2; + uint8_t requiredOutputs = 0; + if (!FilterForDerivedTables.cfgBypassTrackFill.value) { + requiredOutputs |= FullTrackOutput; + } + if (FilterForDerivedTables.cfgFillMicroTracks.value) { + requiredOutputs |= MicroTrackOutput; + } + if (FilterForDerivedTables.cfgFillUltraMicroTracks.value) { + requiredOutputs |= UltraMicroTrackOutput; + } - qaRegistry.add("QA/hGoodTrackIndices", "hGoodTrackIndices", kTH1D, {idxAxis}); - if (doprocessMC) { - qaRegistry.add("QA/hGoodMCTrackIndices", "hGoodMCTrackIndices", kTH1D, {idxAxis}); + uint8_t availableOutputs = 0; + for (auto const& track : tracks) { + if (!keepTrack(track)) { + continue; } - qaRegistry.add("QA/hTrackPt", "Track pT", kTH1F, {ptAxis}); - qaRegistry.add("QA/hTrackEta", "Track eta", kTH1F, {etaAxis}); - qaRegistry.add("QA/hTrackPhi", "Track phi", kTH1F, {phiAxis}); - - if (doprocessV0Data || doprocessV0MC) { - qaRegistry.add("QA/hGoodV0Indices", "hGoodV0Indices", kTH1D, {idxAxis}); - if (doprocessMC) { - qaRegistry.add("QA/hGoodMCV0Indices", "hGoodMCV0Indices", kTH1D, {idxAxis}); + if ((requiredOutputs & FullTrackOutput) != 0 && + (availableOutputs & FullTrackOutput) == 0 && + isFullTrackOutputSelected(collision, track, false)) { + availableOutputs |= FullTrackOutput; + } + if ((requiredOutputs & MicroTrackOutput) != 0 && + (availableOutputs & MicroTrackOutput) == 0) { + bool passedPtDependentDCAxy = false; + bool passedPtDependentDCAz = false; + if (isMicroTrackOutputSelected(collision, track, + passedPtDependentDCAxy, + passedPtDependentDCAz)) { + availableOutputs |= MicroTrackOutput; } - AxisSpec radiusAxis = {100, 0.0, 200.0, "V0 Radius"}; - AxisSpec cosPAAxis = {100, 0.995, 1.0, "V0 CosPA"}; - qaRegistry.add("QA/hV0Radius", "V0 Radius", kTH1F, {radiusAxis}); - qaRegistry.add("QA/hV0CosPA", "V0 CosPA", kTH1F, {cosPAAxis}); } - - if (doprocessCascData || doprocessCascMC) { - AxisSpec radiusAxis = {100, 0.0, 200.0, "Cascade Radius"}; - AxisSpec cosPAAxis = {100, 0.97, 1.0, "Cascade CosPA"}; - qaRegistry.add("QA/hGoodCascIndices", "hGoodCascIndices", kTH1D, {idxAxis}); - if (doprocessMC) { - qaRegistry.add("QA/hGoodMCCascIndices", "hGoodMCCascIndices", kTH1D, {idxAxis}); + if ((requiredOutputs & UltraMicroTrackOutput) != 0 && + (availableOutputs & UltraMicroTrackOutput) == 0 && + isUltraMicroTrackOutputSelected(collision, track)) { + int16_t px1000 = 0; + int16_t py1000 = 0; + int16_t pz1000 = 0; + if (quantizeUltraMicroMomentum(track, px1000, py1000, pz1000)) { + availableOutputs |= UltraMicroTrackOutput; } - qaRegistry.add("QA/hCascRadius", "Cascade Radius", kTH1F, {radiusAxis}); - qaRegistry.add("QA/hCascCosPA", "Cascade CosPA", kTH1F, {cosPAAxis}); } - } - if (doprocessData || doprocessMC) { - LOGF(info, "ResonanceDaughterInitializer initialized with tracks"); - } - if (doprocessV0Data || doprocessV0MC) { - LOGF(info, "ResonanceDaughterInitializer initialized with V0s"); - } - if (doprocessCascData || doprocessCascMC) { - LOGF(info, "ResonanceDaughterInitializer initialized with cascades"); - } - - // Check if the module is initialized with both data and MC - if ((doprocessData && doprocessMC) || (doprocessV0Data && doprocessV0MC) || (doprocessCascData && doprocessCascMC)) { - LOGF(fatal, "ResonanceDaughterInitializer initialized with both data and MC"); - } - // Check if none of the processes are enabled - if (!doprocessDummy && !doprocessData && !doprocessMC && !doprocessV0Data && !doprocessV0MC && !doprocessCascData && !doprocessCascMC) { - LOGF(fatal, "ResonanceDaughterInitializer not initialized, enable at least one process"); - } - } - template - bool filterMicroTrack(T const& track) - { - // if no selection is requested, return true - if (!FilterForDerivedTables.cfgFillPionMicroTracks && !FilterForDerivedTables.cfgFillKaonMicroTracks && !FilterForDerivedTables.cfgFillProtonMicroTracks) - return true; - if (FilterForDerivedTables.cfgFillPionMicroTracks) { - if (std::abs(track.tpcNSigmaPi()) < pidnSigmaPreSelectionCut) - return true; - } - if (FilterForDerivedTables.cfgFillKaonMicroTracks) { - if (std::abs(track.tpcNSigmaKa()) < pidnSigmaPreSelectionCut) - return true; - } - if (FilterForDerivedTables.cfgFillProtonMicroTracks) { - if (std::abs(track.tpcNSigmaPr()) < pidnSigmaPreSelectionCut) + if (availableOutputs == requiredOutputs) { return true; + } } return false; } - template - bool filterTrack(T const& track) + template + void fillUltraMicroTracks(CollisionType const& collision, TrackType const& tracks, TrackPredicate const& keepTrack) { - // if no selection is requested, return true - if (!FilterForDerivedTables.cfgFillPionTracks && !FilterForDerivedTables.cfgFillKaonTracks && !FilterForDerivedTables.cfgFillProtonTracks) - return true; - if (FilterForDerivedTables.cfgFillPionTracks) { - if (std::abs(track.tpcNSigmaPi()) < pidnSigmaPreSelectionCut) - return true; - } - if (FilterForDerivedTables.cfgFillKaonTracks) { - if (std::abs(track.tpcNSigmaKa()) < pidnSigmaPreSelectionCut) - return true; - } - if (FilterForDerivedTables.cfgFillProtonTracks) { - if (std::abs(track.tpcNSigmaPr()) < pidnSigmaPreSelectionCut) - return true; + // Loop over tracks + for (auto const& track : tracks) { + if (!keepTrack(track)) { + continue; + } + if (!isUltraMicroTrackOutputSelected(collision, track)) { + continue; + } + o2::aod::resoultramicrodaughter::DCAEncoding dcaEncoding(track.dcaXY(), track.dcaZ()); + int16_t px1000 = 0; + int16_t py1000 = 0; + int16_t pz1000 = 0; + if (!quantizeUltraMicroMomentum(track, px1000, py1000, pz1000)) { + if (!warnedUltraMicroMomentumRange) { + LOGF(warn, "Skipping ultra-micro tracks with non-finite or out-of-range momentum components"); + warnedUltraMicroMomentumRange = true; + } + continue; + } + uint8_t trackFlags = (track.passedITSRefit() << 0) | + (track.passedTPCRefit() << 1) | + (track.isGlobalTrackWoDCA() << 2) | + (track.isGlobalTrack() << 3) | + (track.isPrimaryTrack() << 4) | + (track.isPVContributor() << 5) | + (track.hasTOF() << 6) | + ((track.sign() > 0) << 7); // sign +1: 1, -1: 0 + uint8_t pidFlag = 0; + switch (ultraMicroPidSpecies) { + case UltraMicroPidSpecies::Pion: + pidFlag = static_cast(o2::aod::resoultramicrodaughter::PidNSigma( + track.tpcNSigmaPi(), track.tofNSigmaPi(), track.hasTOF())); + break; + case UltraMicroPidSpecies::Kaon: + pidFlag = static_cast(o2::aod::resoultramicrodaughter::PidNSigma( + track.tpcNSigmaKa(), track.tofNSigmaKa(), track.hasTOF())); + break; + case UltraMicroPidSpecies::Proton: + pidFlag = static_cast(o2::aod::resoultramicrodaughter::PidNSigma( + track.tpcNSigmaPr(), track.tofNSigmaPr(), track.hasTOF())); + break; + } + if (cfgFillQA && cfgDetailTrackQA) { + qaRegistry.fill(HIST("QA/h4UltraMicroTrackPtEtaPhi"), track.pt(), track.eta(), track.phi()); + qaRegistry.fill(HIST("QA/h2UltraMicroTrackDCAxyVsPt"), track.pt(), track.dcaXY()); + qaRegistry.fill(HIST("QA/h2UltraMicroTrackDCAzVsPt"), track.pt(), track.dcaZ()); + qaRegistry.fill(HIST("QA/h4UltraMicroTrackTPCnSigma"), track.pt(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()); + if (track.hasTOF()) { + qaRegistry.fill(HIST("QA/h4UltraMicroTrackTOFnSigma"), track.pt(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()); + } + } + reso2ultramicrotrks(collision.globalIndex(), + px1000, + py1000, + pz1000, + pidFlag, + static_cast(dcaEncoding), + trackFlags); + if (!FilterForDerivedTables.cfgBypassTrackIndexFill) { + resoUltraMicroTrackTracks(track.globalIndex()); + } } - return false; } /** @@ -814,21 +2234,36 @@ struct ResonanceDaughterInitializer { * @tparam CollisionType Type of collision * @param collision Collision data * @param tracks Track data + * @note ResoMicroTracks_001 is intended for fixed producer-side selections. + * Exact downstream cuts on its decoded values require zero-centred + * strict PID windows |nSigma| < C with + * C in {2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5}, and strict DCA windows + * |DCA| < C with C = N * 0.025 cm (N = 1, ..., 6). The listed PID + * guarantee assumes a zero mean; shifted PID windows require both + * interval edges to align with encoding boundaries and separate + * validation. PID cuts below 2 sigma and off-grid DCA/PID cuts cannot + * be reconstructed exactly. In particular, pT-dependent DCA cuts must + * be applied to the unquantised values here via + * cfgApplyTightDCAPtDepSelection; consumers should retain that producer + * decision rather than retune the decoded lower-edge DCA values. */ - template - void fillMicroTracks(CollisionType const& collision, TrackType const& tracks) + template + void fillMicroTracks(CollisionType const& collision, TrackType const& tracks, TrackPredicate const& keepTrack) { // Loop over tracks for (auto const& track : tracks) { - if (!filterMicroTrack(track)) + if (!keepTrack(track)) { continue; - o2::aod::resomicrodaughter::ResoMicroTrackSelFlag trackSelFlag(track.dcaXY(), track.dcaZ()); - if (std::abs(track.dcaXY()) < (0.004 + (0.013 / track.pt()))) { - trackSelFlag.setDCAxy0(); } - if (std::abs(track.dcaZ()) < (0.004 + (0.013 / track.pt()))) { // TODO: check this - trackSelFlag.setDCAz0(); + bool passedPtDependentDCAxy = false; + bool passedPtDependentDCAz = false; + if (!isMicroTrackOutputSelected(collision, track, + passedPtDependentDCAxy, + passedPtDependentDCAz)) { + continue; } + const o2::aod::resomicrodaughter001::DCAEncoding trackSelFlag( + track.dcaXY(), track.dcaZ(), passedPtDependentDCAxy, passedPtDependentDCAz); uint8_t trackFlags = (track.passedITSRefit() << 0) | (track.passedTPCRefit() << 1) | (track.isGlobalTrackWoDCA() << 2) | @@ -837,18 +2272,49 @@ struct ResonanceDaughterInitializer { (track.isPVContributor() << 5) | (track.hasTOF() << 6) | ((track.sign() > 0) << 7); // sign +1: 1, -1: 0 + if (cfgFillQA && cfgDetailTrackQA) { + qaRegistry.fill(HIST("QA/h4MicroTrackPtEtaPhi"), track.pt(), track.eta(), track.phi()); + qaRegistry.fill(HIST("QA/h2MicroTrackDCAxyVsPt"), track.pt(), track.dcaXY()); + qaRegistry.fill(HIST("QA/h2MicroTrackDCAzVsPt"), track.pt(), track.dcaZ()); + qaRegistry.fill(HIST("QA/h4MicroTrackTPCnSigma"), track.pt(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()); + if (track.hasTOF()) { + qaRegistry.fill(HIST("QA/h4MicroTrackTOFnSigma"), track.pt(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()); + } + } + // Quality QA follows the same selected rows as Micro001, independently + // of the detailed PID/DCA QA. ITS count getters count occupied layers. + if (cfgFillQA && cfgQAITS) { + const auto itsClusterMap = track.itsClusterMap(); + qaRegistry.fill(HIST("QA/h2MicroTrackITSClusterMapVsPt"), track.pt(), itsClusterMap); + qaRegistry.fill(HIST("QA/h2MicroTrackITSNClsVsPt"), track.pt(), track.itsNCls()); + qaRegistry.fill(HIST("QA/h2MicroTrackITSNClsInnerBarrelVsPt"), track.pt(), track.itsNClsInnerBarrel()); + for (int layer = 0; layer < NumberOfITSLayers; ++layer) { + if ((itsClusterMap & (1u << layer)) != 0) { + qaRegistry.fill(HIST("QA/h2MicroTrackITSLayerHitsVsPt"), track.pt(), layer); + } + } + } + if (cfgFillQA && cfgQATPC) { + qaRegistry.fill(HIST("QA/h2MicroTrackTPCCrossedRowsVsPt"), track.pt(), track.tpcNClsCrossedRows()); + } reso2microtrks(collision.globalIndex(), + track.globalIndex(), track.px(), track.py(), track.pz(), - static_cast(o2::aod::resomicrodaughter::PidNSigma(std::abs(track.tpcNSigmaPi()), std::abs(track.tofNSigmaPi()), track.hasTOF())), - static_cast(o2::aod::resomicrodaughter::PidNSigma(std::abs(track.tpcNSigmaKa()), std::abs(track.tofNSigmaKa()), track.hasTOF())), - static_cast(o2::aod::resomicrodaughter::PidNSigma(std::abs(track.tpcNSigmaPr()), std::abs(track.tofNSigmaPr()), track.hasTOF())), + static_cast(o2::aod::resomicrodaughter001::PidNSigma(track.tpcNSigmaPi(), track.tofNSigmaPi(), track.hasTOF())), + static_cast(o2::aod::resomicrodaughter001::PidNSigma(track.tpcNSigmaKa(), track.tofNSigmaKa(), track.hasTOF())), + static_cast(o2::aod::resomicrodaughter001::PidNSigma(track.tpcNSigmaPr(), track.tofNSigmaPr(), track.hasTOF())), static_cast(trackSelFlag), - trackFlags); - if (!cfgBypassTrackIndexFill) { + trackFlags, + static_cast(track.tpcNClsCrossedRows()), + track.itsClusterMap()); + if (!FilterForDerivedTables.cfgBypassTrackIndexFill) { resoMicroTrackTracks(track.globalIndex()); } + if constexpr (isMC) { + fillMCTrack(track, reso2mcmicrotrks); + } } } @@ -861,21 +2327,19 @@ struct ResonanceDaughterInitializer { * @param collision Collision data * @param tracks Track data */ - template - void fillTracks(CollisionType const& collision, TrackType const& tracks) + template + void fillTracks(CollisionType const& collision, TrackType const& tracks, TrackPredicate const& keepTrack) { - if (cfgBypassTrackFill) { + if (FilterForDerivedTables.cfgBypassTrackFill) { return; } // Loop over tracks for (auto const& track : tracks) { - if (!filterTrack(track)) + if (!keepTrack(track)) { + continue; + } + if (!isFullTrackOutputSelected(collision, track)) { continue; - if (cfgFillQA) { - qaRegistry.fill(HIST("QA/hGoodTrackIndices"), 0); - qaRegistry.fill(HIST("QA/hTrackPt"), track.pt()); - qaRegistry.fill(HIST("QA/hTrackEta"), track.eta()); - qaRegistry.fill(HIST("QA/hTrackPhi"), track.phi()); } uint8_t trackFlags = (track.passedITSRefit() << 0) | (track.passedTPCRefit() << 1) | @@ -885,28 +2349,37 @@ struct ResonanceDaughterInitializer { (track.isPVContributor() << 5) | (track.hasTOF() << 6) | ((track.sign() > 0) << 7); // sign +1: 1, -1: 0 + if (cfgFillQA && cfgDetailTrackQA) { + qaRegistry.fill(HIST("QA/h4TrackPtEtaPhi"), track.pt(), track.eta(), track.phi()); + qaRegistry.fill(HIST("QA/h2TrackDCAxyVsPt"), track.pt(), track.dcaXY()); + qaRegistry.fill(HIST("QA/h2TrackDCAzVsPt"), track.pt(), track.dcaZ()); + qaRegistry.fill(HIST("QA/h4TrackTPCnSigma"), track.pt(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr()); + if (track.hasTOF()) { + qaRegistry.fill(HIST("QA/h4TrackTOFnSigma"), track.pt(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()); + } + } reso2trks(collision.globalIndex(), track.pt(), track.px(), track.py(), track.pz(), - (uint8_t)track.tpcNClsCrossedRows(), - (uint8_t)track.tpcNClsFound(), - static_cast(track.dcaXY() * 10000), - static_cast(track.dcaZ() * 10000), - (int8_t)(track.tpcNSigmaPi() * 10), - (int8_t)(track.tpcNSigmaKa() * 10), - (int8_t)(track.tpcNSigmaPr() * 10), - (int8_t)(track.tofNSigmaPi() * 10), - (int8_t)(track.tofNSigmaKa() * 10), - (int8_t)(track.tofNSigmaPr() * 10), - (int8_t)(track.tpcSignal() * 10), + static_cast(track.tpcNClsCrossedRows()), + static_cast(track.tpcNClsFound()), + quantizeSaturated(track.dcaXY(), 10000.), + quantizeSaturated(track.dcaZ(), 10000.), + quantizeSaturated(track.tpcNSigmaPi(), 10.), + quantizeSaturated(track.tpcNSigmaKa(), 10.), + quantizeSaturated(track.tpcNSigmaPr(), 10.), + quantizeSaturated(track.tofNSigmaPi(), 10.), + quantizeSaturated(track.tofNSigmaKa(), 10.), + quantizeSaturated(track.tofNSigmaPr(), 10.), + quantizeSaturated(track.tpcSignal(), 100.), trackFlags); - if (!cfgBypassTrackIndexFill) { + if (!FilterForDerivedTables.cfgBypassTrackIndexFill) { resoTrackTracks(track.globalIndex()); } if constexpr (isMC) { - fillMCTrack(track); + fillMCTrack(track, reso2mctracks); } } } @@ -915,10 +2388,12 @@ struct ResonanceDaughterInitializer { * @brief Fills MC track data * * @tparam TrackType Type of track + * @tparam MCOutputTable Type of positional MC extension to fill * @param track Track data + * @param output Positional MC extension writer */ - template - void fillMCTrack(TrackType const& track) + template + void fillMCTrack(TrackType const& track, Produces& output) { // ------ Temporal lambda function to prevent error in build auto getMothersIndeces = [&](auto const& theMcParticle) { @@ -943,7 +2418,7 @@ struct ResonanceDaughterInitializer { LOGF(debug, " mother index lMother: %d", lMother.globalIndex()); for (auto const& lDaughter : lMother.template daughters_as()) { LOGF(debug, " daughter index lDaughter: %d", lDaughter.globalIndex()); - if (lDaughter.globalIndex() != 0 && lDaughter.globalIndex() != theMcParticle.globalIndex()) { + if (lDaughter.globalIndex() != theMcParticle.globalIndex()) { lSiblingsIndeces.push_back(lDaughter.globalIndex()); } } @@ -953,12 +2428,9 @@ struct ResonanceDaughterInitializer { // ------ std::vector mothers = {-1, -1}; std::vector motherPDGs = {-1, -1}; - int siblings[2] = {0, 0}; - std::vector siblingsTemp = {-1, -1}; + std::array siblings{-1, -1}; + std::vector siblingsTemp{}; if (track.has_mcParticle()) { - if (cfgFillQA) { - qaRegistry.fill(HIST("QA/hGoodMCTrackIndices"), 0); - } // Get the MC particle const auto& particle = track.mcParticle(); if (particle.has_mothers()) { @@ -966,28 +2438,28 @@ struct ResonanceDaughterInitializer { motherPDGs = getMothersPDGCodes(particle); siblingsTemp = getSiblingsIndeces(particle); } - while (mothers.size() > 2) { - mothers.pop_back(); - motherPDGs.pop_back(); - } - if (siblingsTemp.size() > 0) + mothers.resize(StoredMCRelationCount, -1); + motherPDGs.resize(StoredMCRelationCount, -1); + if (!siblingsTemp.empty()) { siblings[0] = siblingsTemp[0]; - if (siblingsTemp.size() > 1) + } + if (siblingsTemp.size() > 1) { siblings[1] = siblingsTemp[1]; - reso2mctracks(particle.pdgCode(), - mothers[0], - motherPDGs[0], - siblings, - particle.isPhysicalPrimary(), - particle.producedByGenerator()); + } + output(particle.pdgCode(), + mothers[0], + motherPDGs[0], + siblings.data(), + particle.isPhysicalPrimary(), + particle.producedByGenerator()); } else { // No MC particle associated - reso2mctracks(0, - mothers[0], - motherPDGs[0], - siblings, - 0, - 0); + output(0, + mothers[0], + motherPDGs[0], + siblings.data(), + 0, + 0); } } @@ -1003,49 +2475,50 @@ struct ResonanceDaughterInitializer { * @param tracks Track data */ template - void fillV0s(CollisionType const& collision, V0Type const& v0s, TrackType const&) + void fillV0s(CollisionType const& collision, V0Type const& v0s, TrackType const& tracks) { - int childIDs[2] = {0, 0}; // these IDs are necessary to keep track of the children for (auto const& v0 : v0s) { - if (cfgFillQA) { - qaRegistry.fill(HIST("QA/hGoodV0Indices"), 0); - qaRegistry.fill(HIST("QA/hV0Radius"), v0.v0radius()); - qaRegistry.fill(HIST("QA/hV0CosPA"), v0.v0cosPA()); + if (!isV0Selected(collision, v0, tracks)) { + continue; + } + if (!filterV0(collision, v0, tracks)) { + continue; } - childIDs[0] = v0.posTrackId(); - childIDs[1] = v0.negTrackId(); + const auto posTrack = v0.template posTrack_as(); + const auto negTrack = v0.template negTrack_as(); + const std::array childIDs{v0.posTrackId(), v0.negTrackId()}; // Original track IDs for downstream pair-level shared-daughter rejection reso2v0s(collision.globalIndex(), v0.pt(), v0.px(), v0.py(), v0.pz(), - childIDs, - (int8_t)(v0.template posTrack_as().tpcNSigmaPi() * 10), - (int8_t)(v0.template posTrack_as().tpcNSigmaKa() * 10), - (int8_t)(v0.template posTrack_as().tpcNSigmaPr() * 10), - (int8_t)(v0.template negTrack_as().tpcNSigmaPi() * 10), - (int8_t)(v0.template negTrack_as().tpcNSigmaKa() * 10), - (int8_t)(v0.template negTrack_as().tpcNSigmaPr() * 10), - (int8_t)(v0.template negTrack_as().tofNSigmaPi() * 10), - (int8_t)(v0.template negTrack_as().tofNSigmaKa() * 10), - (int8_t)(v0.template negTrack_as().tofNSigmaPr() * 10), - (int8_t)(v0.template posTrack_as().tofNSigmaPi() * 10), - (int8_t)(v0.template posTrack_as().tofNSigmaKa() * 10), - (int8_t)(v0.template posTrack_as().tofNSigmaPr() * 10), + childIDs.data(), + quantizeSaturated(posTrack.tpcNSigmaPi(), 10.), + quantizeSaturated(posTrack.tpcNSigmaKa(), 10.), + quantizeSaturated(posTrack.tpcNSigmaPr(), 10.), + quantizeSaturated(negTrack.tpcNSigmaPi(), 10.), + quantizeSaturated(negTrack.tpcNSigmaKa(), 10.), + quantizeSaturated(negTrack.tpcNSigmaPr(), 10.), + quantizeSaturated(posTrack.tofNSigmaPi(), 10.), + quantizeSaturated(posTrack.tofNSigmaKa(), 10.), + quantizeSaturated(posTrack.tofNSigmaPr(), 10.), + quantizeSaturated(negTrack.tofNSigmaPi(), 10.), + quantizeSaturated(negTrack.tofNSigmaKa(), 10.), + quantizeSaturated(negTrack.tofNSigmaPr(), 10.), v0.v0cosPA(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv(), v0.dcav0topv(), - 0, - 0, + static_cast(posTrack.tpcNClsCrossedRows()), + static_cast(negTrack.tpcNClsCrossedRows()), v0.mLambda(), v0.mAntiLambda(), v0.mK0Short(), v0.v0radius(), v0.x(), v0.y(), v0.z(), v0.alpha(), v0.qtarm()); - if (!cfgBypassTrackIndexFill) { - resoV0V0s(v0.globalIndex()); + if (!FilterForDerivedTables.cfgBypassTrackIndexFill) { + resoV0V0s(v0.v0Id()); } if constexpr (isMC) { fillMCV0(v0); @@ -1079,13 +2552,27 @@ struct ResonanceDaughterInitializer { } return lMothersPDGs; }; + auto getMothersPt = [&](auto const& theMcParticle) { + std::vector lMothersPts{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother pt lMother: %f", lMother.pt()); + lMothersPts.push_back(lMother.pt()); + } + return lMothersPts; + }; + auto getMothersRap = [&](auto const& theMcParticle) { + std::vector lMothersRaps{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother rap lMother: %f", lMother.y()); + lMothersRaps.push_back(lMother.y()); + } + return lMothersRaps; + }; auto getDaughtersIndeces = [&](auto const& theMcParticle) { std::vector lDaughtersIndeces{}; for (auto const& lDaughter : theMcParticle.template daughters_as()) { LOGF(debug, " daughter index lDaughter: %d", lDaughter.globalIndex()); - if (lDaughter.globalIndex() != 0) { - lDaughtersIndeces.push_back(lDaughter.globalIndex()); - } + lDaughtersIndeces.push_back(lDaughter.globalIndex()); } return lDaughtersIndeces; }; @@ -1093,44 +2580,43 @@ struct ResonanceDaughterInitializer { std::vector lDaughtersPDGs{}; for (auto const& lDaughter : theMcParticle.template daughters_as()) { LOGF(debug, " daughter pdgcode lDaughter: %d", lDaughter.pdgCode()); - if (lDaughter.globalIndex() != 0) { - lDaughtersPDGs.push_back(lDaughter.pdgCode()); - } + lDaughtersPDGs.push_back(lDaughter.pdgCode()); } return lDaughtersPDGs; }; // ------ std::vector mothers = {-1, -1}; std::vector motherPDGs = {-1, -1}; + std::vector mothersPts = {-1.0f, -1.0f}; + std::vector mothersRaps = {-1.0f, -1.0f}; std::vector daughters = {-1, -1}; std::vector daughterPDGs = {-1, -1}; if (v0.has_mcParticle()) { - if (cfgFillQA) { - qaRegistry.fill(HIST("QA/hGoodMCV0Indices"), 0); - } auto v0mc = v0.mcParticle(); if (v0mc.has_mothers()) { mothers = getMothersIndeces(v0mc); motherPDGs = getMothersPDGCodes(v0mc); + mothersPts = getMothersPt(v0mc); + mothersRaps = getMothersRap(v0mc); } - while (mothers.size() > 2) { - mothers.pop_back(); - motherPDGs.pop_back(); - } + mothers.resize(StoredMCRelationCount, -1); + motherPDGs.resize(StoredMCRelationCount, -1); + mothersPts.resize(StoredMCRelationCount, -1.0f); + mothersRaps.resize(StoredMCRelationCount, -1.0f); if (v0mc.has_daughters()) { daughters = getDaughtersIndeces(v0mc); daughterPDGs = getDaughtersPDGCodes(v0mc); } - while (daughters.size() > 2) { - LOGF(info, "daughters.size() is larger than 2"); - daughters.pop_back(); - daughterPDGs.pop_back(); - } + // if (daughters.size() > StoredMCRelationCount) { + // LOGF(info, "daughters.size() is larger than 2"); + // } + daughters.resize(StoredMCRelationCount, -1); + daughterPDGs.resize(StoredMCRelationCount, -1); reso2mcv0s(v0mc.pdgCode(), mothers[0], motherPDGs[0], - 0, - 0, + mothersPts[0], + mothersRaps[0], daughters[0], daughters[1], daughterPDGs[0], @@ -1141,8 +2627,8 @@ struct ResonanceDaughterInitializer { reso2mcv0s(0, mothers[0], motherPDGs[0], - 0, - 0, + mothersPts[0], + mothersRaps[0], daughters[0], daughters[1], daughterPDGs[0], @@ -1164,42 +2650,40 @@ struct ResonanceDaughterInitializer { * @param tracks Track data */ template - void fillCascades(CollisionType const& collision, CascType const& cascades, TrackType const&) + void fillCascades(CollisionType const& collision, CascType const& cascades, TrackType const& tracks) { - int childIDs[3] = {0, 0, 0}; // these IDs are necessary to keep track of the children for (auto const& casc : cascades) { - if (cfgFillQA) { - qaRegistry.fill(HIST("QA/hGoodCascIndices"), 0); - qaRegistry.fill(HIST("QA/hCascRadius"), casc.cascradius()); - qaRegistry.fill(HIST("QA/hCascCosPA"), casc.casccosPA(collision.posX(), collision.posY(), collision.posZ())); - } - childIDs[0] = casc.posTrackId(); - childIDs[1] = casc.negTrackId(); - childIDs[2] = casc.bachelorId(); + if (!isCascSelected(collision, casc, tracks)) { + continue; + } + const auto posTrack = casc.template posTrack_as(); + const auto negTrack = casc.template negTrack_as(); + const auto bachelor = casc.template bachelor_as(); + const std::array childIDs{casc.posTrackId(), casc.negTrackId(), casc.bachelorId()}; // Original track IDs for downstream pair-level shared-daughter rejection reso2cascades(collision.globalIndex(), casc.pt(), casc.px(), casc.py(), casc.pz(), - childIDs, - (int8_t)(casc.template posTrack_as().tpcNSigmaPi() * 10), - (int8_t)(casc.template posTrack_as().tpcNSigmaKa() * 10), - (int8_t)(casc.template posTrack_as().tpcNSigmaPr() * 10), - (int8_t)(casc.template negTrack_as().tpcNSigmaPi() * 10), - (int8_t)(casc.template negTrack_as().tpcNSigmaKa() * 10), - (int8_t)(casc.template negTrack_as().tpcNSigmaPr() * 10), - (int8_t)(casc.template bachelor_as().tpcNSigmaPi() * 10), - (int8_t)(casc.template bachelor_as().tpcNSigmaKa() * 10), - (int8_t)(casc.template bachelor_as().tpcNSigmaPr() * 10), - (int8_t)(casc.template posTrack_as().tofNSigmaPi() * 10), - (int8_t)(casc.template posTrack_as().tofNSigmaKa() * 10), - (int8_t)(casc.template posTrack_as().tofNSigmaPr() * 10), - (int8_t)(casc.template negTrack_as().tofNSigmaPi() * 10), - (int8_t)(casc.template negTrack_as().tofNSigmaKa() * 10), - (int8_t)(casc.template negTrack_as().tofNSigmaPr() * 10), - (int8_t)(casc.template bachelor_as().tofNSigmaPi() * 10), - (int8_t)(casc.template bachelor_as().tofNSigmaKa() * 10), - (int8_t)(casc.template bachelor_as().tofNSigmaPr() * 10), + childIDs.data(), + quantizeSaturated(posTrack.tpcNSigmaPi(), 10.), + quantizeSaturated(posTrack.tpcNSigmaKa(), 10.), + quantizeSaturated(posTrack.tpcNSigmaPr(), 10.), + quantizeSaturated(negTrack.tpcNSigmaPi(), 10.), + quantizeSaturated(negTrack.tpcNSigmaKa(), 10.), + quantizeSaturated(negTrack.tpcNSigmaPr(), 10.), + quantizeSaturated(bachelor.tpcNSigmaPi(), 10.), + quantizeSaturated(bachelor.tpcNSigmaKa(), 10.), + quantizeSaturated(bachelor.tpcNSigmaPr(), 10.), + quantizeSaturated(posTrack.tofNSigmaPi(), 10.), + quantizeSaturated(posTrack.tofNSigmaKa(), 10.), + quantizeSaturated(posTrack.tofNSigmaPr(), 10.), + quantizeSaturated(negTrack.tofNSigmaPi(), 10.), + quantizeSaturated(negTrack.tofNSigmaKa(), 10.), + quantizeSaturated(negTrack.tofNSigmaPr(), 10.), + quantizeSaturated(bachelor.tofNSigmaPi(), 10.), + quantizeSaturated(bachelor.tofNSigmaKa(), 10.), + quantizeSaturated(bachelor.tofNSigmaPr(), 10.), casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()), casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()), casc.dcaV0daughters(), @@ -1211,14 +2695,14 @@ struct ResonanceDaughterInitializer { casc.dcaXYCascToPV(), casc.dcaZCascToPV(), casc.sign(), - 0, - 0, - 0, + static_cast(posTrack.tpcNClsCrossedRows()), + static_cast(negTrack.tpcNClsCrossedRows()), + static_cast(bachelor.tpcNClsCrossedRows()), casc.mLambda(), casc.mXi(), casc.v0radius(), casc.cascradius(), casc.x(), casc.y(), casc.z()); - if (!cfgBypassTrackIndexFill) { - resoCascadeCascades(casc.globalIndex()); + if (!FilterForDerivedTables.cfgBypassTrackIndexFill) { + resoCascadeCascades(casc.cascadeId()); } if constexpr (isMC) { fillMCCascade(casc); @@ -1252,13 +2736,27 @@ struct ResonanceDaughterInitializer { } return lMothersPDGs; }; + auto getMothersPt = [&](auto const& theMcParticle) { + std::vector lMothersPts{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother pt lMother: %f", lMother.pt()); + lMothersPts.push_back(lMother.pt()); + } + return lMothersPts; + }; + auto getMothersRap = [&](auto const& theMcParticle) { + std::vector lMothersRaps{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother rap lMother: %f", lMother.y()); + lMothersRaps.push_back(lMother.y()); + } + return lMothersRaps; + }; auto getDaughtersIndeces = [&](auto const& theMcParticle) { std::vector lDaughtersIndeces{}; for (auto const& lDaughter : theMcParticle.template daughters_as()) { LOGF(debug, " daughter index lDaughter: %d", lDaughter.globalIndex()); - if (lDaughter.globalIndex() != 0) { - lDaughtersIndeces.push_back(lDaughter.globalIndex()); - } + lDaughtersIndeces.push_back(lDaughter.globalIndex()); } return lDaughtersIndeces; }; @@ -1266,9 +2764,7 @@ struct ResonanceDaughterInitializer { std::vector lDaughtersPDGs{}; for (auto const& lDaughter : theMcParticle.template daughters_as()) { LOGF(debug, " daughter pdgcode lDaughter: %d", lDaughter.pdgCode()); - if (lDaughter.globalIndex() != 0) { - lDaughtersPDGs.push_back(lDaughter.pdgCode()); - } + lDaughtersPDGs.push_back(lDaughter.pdgCode()); } return lDaughtersPDGs; }; @@ -1277,33 +2773,34 @@ struct ResonanceDaughterInitializer { std::vector motherPDGs = {-1, -1}; std::vector daughters = {-1, -1}; std::vector daughterPDGs = {-1, -1}; + std::vector mothersPts = {-1.0f, -1.0f}; + std::vector mothersRaps = {-1.0f, -1.0f}; if (casc.has_mcParticle()) { - if (cfgFillQA) { - qaRegistry.fill(HIST("QA/hGoodMCCascIndices"), 0); - } auto cascmc = casc.mcParticle(); if (cascmc.has_mothers()) { mothers = getMothersIndeces(cascmc); motherPDGs = getMothersPDGCodes(cascmc); + mothersPts = getMothersPt(cascmc); + mothersRaps = getMothersRap(cascmc); } - while (mothers.size() > 2) { - mothers.pop_back(); - motherPDGs.pop_back(); - } + mothers.resize(StoredMCRelationCount, -1); + motherPDGs.resize(StoredMCRelationCount, -1); + mothersPts.resize(StoredMCRelationCount, -1.0f); + mothersRaps.resize(StoredMCRelationCount, -1.0f); if (cascmc.has_daughters()) { daughters = getDaughtersIndeces(cascmc); daughterPDGs = getDaughtersPDGCodes(cascmc); } - while (daughters.size() > 2) { - LOGF(info, "daughters.size() is larger than 2"); - daughters.pop_back(); - daughterPDGs.pop_back(); - } + // if (daughters.size() > StoredMCRelationCount) { + // LOGF(info, "daughters.size() is larger than 2"); + // } + daughters.resize(StoredMCRelationCount, -1); + daughterPDGs.resize(StoredMCRelationCount, -1); reso2mccascades(cascmc.pdgCode(), mothers[0], motherPDGs[0], - 0, - 0, + mothersPts[0], + mothersRaps[0], daughters[0], daughters[1], daughterPDGs[0], @@ -1314,8 +2811,8 @@ struct ResonanceDaughterInitializer { reso2mccascades(0, mothers[0], motherPDGs[0], - 0, - 0, + mothersPts[0], + mothersRaps[0], daughters[0], daughters[1], daughterPDGs[0], @@ -1330,27 +2827,133 @@ struct ResonanceDaughterInitializer { * * @param collision Collision data */ - void processDummy(aod::ResoCollision const&) + void processDummy(aod::ResoCollisions_001::iterator const&) { } PROCESS_SWITCH(ResonanceDaughterInitializer, processDummy, "Process dummy", true); + /** + * @brief Fills all enabled track tables from an already grouped track slice + * + * @tparam isMC Boolean indicating if it's MC + * @param collision Reduced collision used as the output foreign key + * @param tracks Tracks belonging to the corresponding original collision + */ + template + void fillTrackTables(CollisionType const& collision, TrackTableType const& tracks, TrackPredicate const& keepTrack) + { + fillTracks(collision, tracks, keepTrack); + if (FilterForDerivedTables.cfgFillMicroTracks) { + fillMicroTracks(collision, tracks, keepTrack); + } + if (FilterForDerivedTables.cfgFillUltraMicroTracks) { + fillUltraMicroTracks(collision, tracks, keepTrack); + } + } + + template + void fillTrackTables(CollisionType const& collision, TrackTableType const& tracks) + { + fillTrackTables(collision, tracks, KeepAllTracks{}); + } + + /** + * @brief Fills track tables for one original collision + * + * @tparam isMC Boolean indicating if it's MC + * @tparam CollisionType Type of reduced collision + * @tparam TrackTableType Type of input track table + * @tparam PresliceType Type of the original-collision preslice + * @param collision Reduced collision with the original collision index + * @param tracks Input track table + */ + template + void fillTrackTablesForCollision(CollisionType const& collision, TrackTableType const& tracks, PresliceType const& perCollision) + { + auto tracksThisCollision = tracks.sliceBy(perCollision, collision.collisionId()); + fillTrackTables(collision, tracksThisCollision); + } + /** * @brief Processes data tracks * * @param collision Collision data * @param tracks Track data */ - void processData(aod::ResoCollision const& collision, + void processData(ResoCollisionWithIndex::iterator const& collision, soa::Filtered const& tracks) { - fillTracks(collision, tracks); - if (cfgFillMicroTracks) { - fillMicroTracks(collision, tracks); - } + fillTrackTablesForCollision(collision, tracks, tracksPerCollision); } PROCESS_SWITCH(ResonanceDaughterInitializer, processData, "Process tracks for data", false); + /** + * @brief Processes data tracks with configurable selected V0 and cascade gates + */ + void processDataWithPairGate(ResoCollisionWithIndex::iterator const& collision, + soa::Filtered const& tracks, + aod::ResoV0Candidates const& v0s, + aod::ResoCascadesCandidates const& cascades) + { + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.collisionId()); + PairTrackSelection pairSelection{FilterForDerivedTables.cfgGlobalDaughterVeto.value}; + if (!preparePairTrackSelection(collision, tracks, v0s, cascades, + v0sPerCollision, cascadesPerCollision, pairSelection)) { + return; + } + if (!hasTracksForEnabledOutputs(collision, tracksThisCollision, pairSelection)) { + return; + } + fillTrackTables(collision, tracksThisCollision, pairSelection); + } + PROCESS_SWITCH(ResonanceDaughterInitializer, processDataWithPairGate, "Process data tracks with the configured pair-gate mode", false); + + /** + * @brief Processes data tracks when a selectex0 and collision track exist + * + * This dedicated callback deliberately has no cascade input, so enabling + * the V0 pair gate does not activate the cascade upstream dependency. + */ + void processDataWithV0PairGate(ResoCollisionWithIndex::iterator const& collision, + soa::Filtered const& tracks, + aod::ResoV0Candidates const& v0s) + { + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.collisionId()); + auto v0sThisCollision = v0s.sliceBy(v0sPerCollision, collision.collisionId()); + PairTrackSelection pairSelection{FilterForDerivedTables.cfgGlobalDaughterVeto.value}; + pairSelection.useV0Candidates = true; + pairSelection.v0Candidates = + collectSelectedV0Daughters(collision, v0sThisCollision, tracks, pairSelection.useGlobalDaughterVeto); + if (!hasTracksForEnabledOutputs(collision, tracksThisCollision, pairSelection)) { + return; + } + fillTrackTables(collision, tracksThisCollision, pairSelection); + } + PROCESS_SWITCH(ResonanceDaughterInitializer, processDataWithV0PairGate, "Process data tracks requiring a selected V0", false); + + /** + * @brief Processes data tracks when a selected cascade and collision track exist + * + * This dedicated callback deliberately has no V0 input, so enabling the + * cascade pair gate does not activate the V0 upstream dependency. + */ + void processDataWithCascPairGate(ResoCollisionWithIndex::iterator const& collision, + soa::Filtered const& tracks, + aod::ResoCascadesCandidates const& cascades) + { + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.collisionId()); + auto cascadesThisCollision = cascades.sliceBy(cascadesPerCollision, collision.collisionId()); + PairTrackSelection pairSelection{FilterForDerivedTables.cfgGlobalDaughterVeto.value}; + pairSelection.useCascadeCandidates = true; + pairSelection.cascadeCandidates = + collectSelectedCascadeDaughters(collision, cascadesThisCollision, tracks, pairSelection.useGlobalDaughterVeto); + if (!hasTracksForEnabledOutputs(collision, tracksThisCollision, pairSelection)) { + return; + } + fillTrackTables(collision, tracksThisCollision, pairSelection); + } + PROCESS_SWITCH(ResonanceDaughterInitializer, processDataWithCascPairGate, "Process data tracks requiring a selected cascade", false); + /** * @brief Processes MC tracks * @@ -1358,17 +2961,78 @@ struct ResonanceDaughterInitializer { * @param tracks Track data * @param mcParticles MC particles */ - void processMC(aod::ResoCollision const& collision, + void processMC(ResoCollisionWithIndex::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&) { - fillTracks(collision, tracks); - if (cfgFillMicroTracks) { - fillMicroTracks(collision, tracks); - } + fillTrackTablesForCollision(collision, tracks, tracksMCPerCollision); } PROCESS_SWITCH(ResonanceDaughterInitializer, processMC, "Process tracks for MC", false); + /** + * @brief Processes MC tracks with configurable selected V0 and cascade gates + */ + void processMCWithPairGate(ResoCollisionWithIndex::iterator const& collision, + soa::Filtered const& tracks, + aod::ResoV0CandidatesMC const& v0s, + aod::ResoCascadesCandidatesMC const& cascades, + aod::McParticles const&) + { + auto tracksThisCollision = tracks.sliceBy(tracksMCPerCollision, collision.collisionId()); + PairTrackSelection pairSelection{FilterForDerivedTables.cfgGlobalDaughterVeto.value}; + if (!preparePairTrackSelection(collision, tracks, v0s, cascades, + v0sMCPerCollision, cascadesMCPerCollision, pairSelection)) { + return; + } + if (!hasTracksForEnabledOutputs(collision, tracksThisCollision, pairSelection)) { + return; + } + fillTrackTables(collision, tracksThisCollision, pairSelection); + } + PROCESS_SWITCH(ResonanceDaughterInitializer, processMCWithPairGate, "Process MC tracks with the configured pair-gate mode", false); + + /** + * @brief Processes MC tracks when a selected V0 and collision track exist + */ + void processMCWithV0PairGate(ResoCollisionWithIndex::iterator const& collision, + soa::Filtered const& tracks, + aod::ResoV0CandidatesMC const& v0s, + aod::McParticles const&) + { + auto tracksThisCollision = tracks.sliceBy(tracksMCPerCollision, collision.collisionId()); + auto v0sThisCollision = v0s.sliceBy(v0sMCPerCollision, collision.collisionId()); + PairTrackSelection pairSelection{FilterForDerivedTables.cfgGlobalDaughterVeto.value}; + pairSelection.useV0Candidates = true; + pairSelection.v0Candidates = + collectSelectedV0Daughters(collision, v0sThisCollision, tracks, pairSelection.useGlobalDaughterVeto); + if (!hasTracksForEnabledOutputs(collision, tracksThisCollision, pairSelection)) { + return; + } + fillTrackTables(collision, tracksThisCollision, pairSelection); + } + PROCESS_SWITCH(ResonanceDaughterInitializer, processMCWithV0PairGate, "Process MC tracks requiring a selected V0", false); + + /** + * @brief Processes MC tracks when a selected cascade and collision track exist + */ + void processMCWithCascPairGate(ResoCollisionWithIndex::iterator const& collision, + soa::Filtered const& tracks, + aod::ResoCascadesCandidatesMC const& cascades, + aod::McParticles const&) + { + auto tracksThisCollision = tracks.sliceBy(tracksMCPerCollision, collision.collisionId()); + auto cascadesThisCollision = cascades.sliceBy(cascadesMCPerCollision, collision.collisionId()); + PairTrackSelection pairSelection{FilterForDerivedTables.cfgGlobalDaughterVeto.value}; + pairSelection.useCascadeCandidates = true; + pairSelection.cascadeCandidates = + collectSelectedCascadeDaughters(collision, cascadesThisCollision, tracks, pairSelection.useGlobalDaughterVeto); + if (!hasTracksForEnabledOutputs(collision, tracksThisCollision, pairSelection)) { + return; + } + fillTrackTables(collision, tracksThisCollision, pairSelection); + } + PROCESS_SWITCH(ResonanceDaughterInitializer, processMCWithCascPairGate, "Process MC tracks requiring a selected cascade", false); + /** * @brief Processes V0 data * @@ -1376,9 +3040,10 @@ struct ResonanceDaughterInitializer { * @param v0s V0 data * @param tracks Track data */ - void processV0Data(aod::ResoCollision const& collision, aod::ResoV0Candidates const& v0s, aod::ResoTrackCandidates const& tracks) + void processV0Data(ResoCollisionWithIndex::iterator const& collision, aod::ResoV0Candidates const& v0s, aod::ResoTrackCandidates const& tracks) { - fillV0s(collision, v0s, tracks); + auto v0sThisCollision = v0s.sliceBy(v0sPerCollision, collision.collisionId()); + fillV0s(collision, v0sThisCollision, tracks); } PROCESS_SWITCH(ResonanceDaughterInitializer, processV0Data, "Process V0s for data", false); @@ -1389,9 +3054,10 @@ struct ResonanceDaughterInitializer { * @param v0s V0 data * @param tracks Track data */ - void processV0MC(aod::ResoCollision const& collision, aod::ResoV0CandidatesMC const& v0s, aod::ResoTrackCandidatesMC const& tracks) + void processV0MC(ResoCollisionWithIndex::iterator const& collision, aod::ResoV0CandidatesMC const& v0s, aod::ResoTrackCandidatesMC const& tracks, aod::McParticles const&) { - fillV0s(collision, v0s, tracks); + auto v0sThisCollision = v0s.sliceBy(v0sMCPerCollision, collision.collisionId()); + fillV0s(collision, v0sThisCollision, tracks); } PROCESS_SWITCH(ResonanceDaughterInitializer, processV0MC, "Process V0s for MC", false); @@ -1402,9 +3068,10 @@ struct ResonanceDaughterInitializer { * @param cascades Cascade data * @param tracks Track data */ - void processCascData(aod::ResoCollision const& collision, aod::ResoCascadesCandidates const& cascades, aod::ResoTrackCandidates const& tracks) + void processCascData(ResoCollisionWithIndex::iterator const& collision, aod::ResoCascadesCandidates const& cascades, aod::ResoTrackCandidates const& tracks) { - fillCascades(collision, cascades, tracks); + auto cascadesThisCollision = cascades.sliceBy(cascadesPerCollision, collision.collisionId()); + fillCascades(collision, cascadesThisCollision, tracks); } PROCESS_SWITCH(ResonanceDaughterInitializer, processCascData, "Process Cascades for data", false); @@ -1415,16 +3082,17 @@ struct ResonanceDaughterInitializer { * @param cascades Cascade data * @param tracks Track data */ - void processCascMC(aod::ResoCollision const& collision, aod::ResoCascadesCandidatesMC const& cascades, aod::ResoTrackCandidatesMC const& tracks) + void processCascMC(ResoCollisionWithIndex::iterator const& collision, aod::ResoCascadesCandidatesMC const& cascades, aod::ResoTrackCandidatesMC const& tracks, aod::McParticles const&) { - fillCascades(collision, cascades, tracks); + auto cascadesThisCollision = cascades.sliceBy(cascadesMCPerCollision, collision.collisionId()); + fillCascades(collision, cascadesThisCollision, tracks); } PROCESS_SWITCH(ResonanceDaughterInitializer, processCascMC, "Process Cascades for MC", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context), + adaptAnalysisTask(context)}; } diff --git a/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx new file mode 100644 index 00000000000..a13f5a57ae2 --- /dev/null +++ b/PWGLF/TableProducer/Resonances/xi1530kaonreducedtable.cxx @@ -0,0 +1,1206 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file xi1530kaonreducedtable.cxx +/// \brief Producer of compact Xi(1530)^0--K reduced tables for femtoscopy. +/// \author Prottay Das, prottay.das@cern.ch + +#include "PWGLF/DataModel/LFReducedXi1530KaonTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; + +namespace +{ +constexpr float kTinyMomentum = 1.e-6f; + +inline uint8_t toU8(int value) +{ + return static_cast(std::clamp(value, 0, 255)); +} + +inline void setTrackSelBit(uint64_t& word, o2::aod::redxistark::XiStarTrackSelBit bit, bool pass) +{ + if (pass) { + word |= o2::aod::redxistark::xiStarTrackSelMask(bit); + } +} + +inline void setTopoSelBit(uint64_t& word, o2::aod::redxistark::XiStarTopoSelBit bit, bool pass) +{ + if (pass) { + word |= o2::aod::redxistark::xiStarTopoSelMask(bit); + } +} + +inline void setKaonSelBit(uint8_t& word, o2::aod::redxistarkaon::KaonSelBit bit, bool pass) +{ + if (pass) { + word |= o2::aod::redxistarkaon::kaonSelMask(bit); + } +} + +} // namespace + +struct xi1530kaonreducedtable { + // CCDB is used only to store the nominal magnetic field in the reduced event + // row. This keeps the downstream CPR QA self-contained without storing the + // run number or timestamp. + Service ccdb; + Configurable ccdbURL{"ccdbURL", "http://alice-ccdb.cern.ch", "CCDB URL for the magnetic field"}; + + // --------------------------------------------------------------------------- + // Output tables + // --------------------------------------------------------------------------- + Produces redEvents; + Produces xiStarCandidates; + Produces kaonCandidates; + + // --------------------------------------------------------------------------- + // Input table types + // --------------------------------------------------------------------------- + using EventCandidates = soa::Join; + + // One joined track type is used both for primary tracks and for the daughters + // obtained through CascDatas::{posTrack,negTrack,bachelor}_as(). + using TracksFull = soa::Join; + + // --------------------------------------------------------------------------- + // Event selections -- copied from the Xi(1530) Run-3 analysis philosophy. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "event"; + Configurable zVtxMax{"zVtxMax", 10.f, "Maximum |z_PV| (cm)"}; + Configurable requireSel8{"requireSel8", true, "Require collision.sel8()"}; + Configurable requireINELgt0{"requireINELgt0", true, "Require multNTracksPVeta1 > 0"}; + } eventCuts; + + // --------------------------------------------------------------------------- + // Prompt / first pion from Xi(1530) -> Xi + pi. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "piFirst"; + + Configurable etaMax{"etaMax", 0.8f, "Maximum |eta|"}; + Configurable ptMin{"ptMin", 0.15f, "Minimum pT (GeV/c)"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require isGlobalTrack"}; + Configurable requirePVContributor{"requirePVContributor", true, "Require isPVContributor"}; + Configurable minITSInnerBarrelClusters{"minITSInnerBarrelClusters", 1, "Minimum ITS inner-barrel clusters"}; + Configurable minITSClusters{"minITSClusters", 3, "Minimum ITS clusters"}; + + // DCAxy: configurable; nominal/default form is pT dependent. + Configurable dcaXYUsePtDependent{"dcaXYUsePtDependent", true, "Use pT-dependent default DCAxy cut"}; + Configurable dcaXYp0{"dcaXYp0", 0.004f, "DCAxy(pT) p0 (cm)"}; + Configurable dcaXYp1{"dcaXYp1", 0.013f, "DCAxy(pT) p1 (cm GeV/c)"}; + Configurable dcaXYMaxConstant{"dcaXYMaxConstant", 0.1f, "Constant |DCAxy| max if pT-dependent mode is disabled (cm)"}; + + // DCAz: independently configurable; nominal/default form is also pT dependent. + // Two additional alternatives are retained in the track-selection bitmap. + Configurable dcaZUsePtDependent{"dcaZUsePtDependent", true, "Use pT-dependent default DCAz cut"}; + Configurable dcaZp0{"dcaZp0", 0.004f, "DCAz(pT) p0 (cm)"}; + Configurable dcaZp1{"dcaZp1", 0.013f, "DCAz(pT) p1 (cm GeV/c)"}; + Configurable dcaZMaxConstant{"dcaZMaxConstant", 0.1f, "Constant default |DCAz| max if pT-dependent mode is disabled (cm)"}; + Configurable dcaZLoose{"dcaZLoose", 1.0f, "Alternative loose |DCAz| maximum (cm)"}; + Configurable dcaZTightVariation{"dcaZTightVariation", 0.1f, "Alternative |DCAz| maximum (cm)"}; + + // TPC rows: loose/default/tight from the Xi(1530) systematic table. + Configurable tpcRowsLoose{"tpcRowsLoose", 70, "Loose minimum crossed rows (strict >)"}; + Configurable tpcRowsDefault{"tpcRowsDefault", 80, "Default minimum crossed rows (strict >)"}; + Configurable tpcRowsTight{"tpcRowsTight", 90, "Tight minimum crossed rows (strict >)"}; + } piFirstCuts; + + // --------------------------------------------------------------------------- + // PID working points. + // V0 daughters (Lambda pion/proton): TPC only. + // Prompt Xi* pion: retain BOTH pure TPC-only and hybrid TPC/TOF choices. + // Xi bachelor pion: retain BOTH pure TPC-only and hybrid TPC/TOF choices. + // Hybrid means: if no TOF, TPC only; if TOF exists, use a circular + // sqrt(nSigmaTPC^2+nSigmaTOF^2) cut. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "v0Pid"; + Configurable nSigma3{"nSigma3", 3.f, "V0-daughter TPC PID 3-sigma WP"}; + Configurable nSigma4{"nSigma4", 4.f, "V0-daughter TPC PID 4-sigma WP (dedicated Lambda nominal)"}; + Configurable nSigma4p8{"nSigma4p8", 4.8f, "V0 pion TPC PID pp-Xi WP"}; + Configurable nSigma5{"nSigma5", 5.f, "V0-daughter TPC PID 5-sigma WP / pp-Xi proton WP"}; + Configurable nSigma6{"nSigma6", 6.f, "V0-daughter TPC PID 6-sigma loose envelope"}; + } v0PidCuts; + + struct : ConfigurableGroup { + std::string prefix = "otherTrackPid"; + Configurable nSigma3{"nSigma3", 3.f, "Prompt/bachelor pion PID 3-sigma WP (TPC-only or hybrid)"}; + Configurable nSigma4{"nSigma4", 4.f, "Prompt/bachelor pion PID 4-sigma WP (TPC-only or hybrid)"}; + Configurable nSigma5{"nSigma5", 5.f, "Prompt/bachelor pion PID 5-sigma WP (TPC-only or hybrid)"}; + Configurable nSigma6{"nSigma6", 6.f, "Prompt/bachelor pion PID 6-sigma loose envelope (TPC-only or hybrid)"}; + Configurable bachelorTPC4p8{"bachelorTPC4p8", 4.8f, "TPC-only Xi-bachelor pion pp-Xi WP"}; + } otherTrackPidCuts; + + // Second prompt-pion PID recipe: + // pT < ptThreshold -> TPC-only PID, even if the track has TOF + // pT >= ptThreshold -> circular sqrt(TPC^2 + TOF^2) PID + // By default TOF is required above the threshold. This can be disabled. + // TPC and circular limits are independently configurable for each WP. + struct : ConfigurableGroup { + std::string prefix = "piFirstThresholdPid"; + Configurable ptThreshold{"ptThreshold", 0.5f, "Prompt-pion pT threshold above which circular TPC+TOF PID is applied (GeV/c)"}; + Configurable requireTOFAboveThreshold{"requireTOFAboveThreshold", true, "Require valid TOF for prompt pion at/above ptThreshold"}; + + Configurable tpcNSigma3{"tpcNSigma3", 3.f, "TPC-only limit for threshold PID 3-sigma WP"}; + Configurable tpcNSigma4{"tpcNSigma4", 4.f, "TPC-only limit for threshold PID 4-sigma WP"}; + Configurable tpcNSigma5{"tpcNSigma5", 5.f, "TPC-only limit for threshold PID 5-sigma WP"}; + Configurable tpcNSigma6{"tpcNSigma6", 6.f, "TPC-only limit for threshold PID 6-sigma loose envelope"}; + + Configurable circularNSigma3{"circularNSigma3", 3.f, "Circular TPC+TOF limit for threshold PID 3-sigma WP"}; + Configurable circularNSigma4{"circularNSigma4", 4.f, "Circular TPC+TOF limit for threshold PID 4-sigma WP"}; + Configurable circularNSigma5{"circularNSigma5", 5.f, "Circular TPC+TOF limit for threshold PID 5-sigma WP"}; + Configurable circularNSigma6{"circularNSigma6", 6.f, "Circular TPC+TOF limit for threshold PID 6-sigma loose envelope"}; + } piFirstThresholdPidCuts; + + // --------------------------------------------------------------------------- + // Cascade daughter quality and fixed cascade acceptance/lifetime cuts. + // Exact source-defined topology working points are in the next group. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "cascadeBase"; + + // Fixed producer-level acceptance / quality cuts. + Configurable daughterEtaMax{"daughterEtaMax", 0.8f, "Maximum |eta| of cascade daughters (fixed producer cut)"}; + Configurable daughterTPCrowsMin{"daughterTPCrowsMin", 50, "Broad minimum TPC crossed rows for all cascade daughters (strict >)"}; + Configurable lambdaPionPtMin{"lambdaPionPtMin", 0.2f, "Minimum Lambda-daughter pion pT (GeV/c), fixed producer cut"}; + Configurable lambdaProtonPtMin{"lambdaProtonPtMin", 0.2f, "Minimum Lambda-daughter proton pT (GeV/c), fixed producer cut"}; + Configurable lambdaRapidityMax{"lambdaRapidityMax", 0.5f, "Maximum |y_Lambda|, fixed producer cut"}; + Configurable cascadeEtaMax{"cascadeEtaMax", 0.8f, "Maximum |eta| of Xi candidate (fixed producer cut)"}; + Configurable cascadePtMin{"cascadePtMin", 0.15f, "Minimum Xi-candidate pT (GeV/c), fixed producer cut"}; + Configurable maxV0Radius{"maxV0Radius", 30.f, "Maximum V0/Lambda radius (cm), fixed producer cut"}; + Configurable maxCascRadius{"maxCascRadius", 200.f, "Maximum cascade radius (cm), fixed producer cut"}; + } cascadeBaseCuts; + + // --------------------------------------------------------------------------- + // Exact topology working points retained in the topology-selection bitmap. + // The producer applies only the loosest UNION envelope of these values. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "topology"; + + // V0 daughter DCA to PV (minimum, cm). + Configurable v0PionDcaPV005{"v0PionDcaPV005", 0.05f, "Min pion DCA to PV: 0.05 cm"}; + Configurable v0PionDcaPV006{"v0PionDcaPV006", 0.06f, "Min pion DCA to PV: 0.06 cm (Xi* nominal)"}; + Configurable v0PionDcaPV010{"v0PionDcaPV010", 0.10f, "Min pion DCA to PV: 0.10 cm"}; + Configurable v0PionDcaPV020{"v0PionDcaPV020", 0.20f, "Min pion DCA to PV: 0.20 cm (dedicated Lambda)"}; + + Configurable v0ProtonDcaPV005{"v0ProtonDcaPV005", 0.05f, "Min proton DCA to PV: 0.05 cm"}; + Configurable v0ProtonDcaPV006{"v0ProtonDcaPV006", 0.06f, "Min proton DCA to PV: 0.06 cm (Xi* nominal)"}; + Configurable v0ProtonDcaPV007{"v0ProtonDcaPV007", 0.07f, "Min proton DCA to PV: 0.07 cm (dedicated Lambda)"}; + Configurable v0ProtonDcaPV010{"v0ProtonDcaPV010", 0.10f, "Min proton DCA to PV: 0.10 cm"}; + + // As requested, keep the Xi(1530)-analysis MINIMUM DCA(V0,PV) convention. + Configurable v0DcaPV000{"v0DcaPV000", 0.00f, "Min Lambda/V0 DCA to PV: 0.00 cm"}; + Configurable v0DcaPV003{"v0DcaPV003", 0.03f, "Min Lambda/V0 DCA to PV: 0.03 cm"}; + Configurable v0DcaPV010{"v0DcaPV010", 0.10f, "Min Lambda/V0 DCA to PV: 0.10 cm"}; + + // Native CascDatas V0-daughter DCA / fitter metric (maximum). + Configurable v0DcaDaughters1p0{"v0DcaDaughters1p0", 1.0f, "Max V0-daughter DCA/native fitter metric: 1.0"}; + Configurable v0DcaDaughters0p5{"v0DcaDaughters0p5", 0.5f, "Max V0-daughter DCA/native fitter metric: 0.5"}; + Configurable v0DcaDaughters0p1{"v0DcaDaughters0p1", 0.1f, "Max V0-daughter DCA/native fitter metric: 0.1"}; + + Configurable v0CosPA097{"v0CosPA097", 0.97f, "Min V0 cosPA: 0.97"}; + Configurable v0CosPA098{"v0CosPA098", 0.98f, "Min V0 cosPA: 0.98 (Xi* nominal)"}; + Configurable v0CosPA09876{"v0CosPA09876", 0.9876f, "Min V0 cosPA: 0.9876 (pp-Xi)"}; + Configurable v0CosPA099{"v0CosPA099", 0.99f, "Min V0 cosPA: 0.99"}; + Configurable v0CosPA0995{"v0CosPA0995", 0.995f, "Min V0 cosPA: 0.995 (dedicated Lambda)"}; + + Configurable v0Radius0p9{"v0Radius0p9", 0.9f, "Min V0 radius: 0.9 cm"}; + Configurable v0Radius1p01{"v0Radius1p01", 1.01f, "Min V0 radius: 1.01 cm (Xi* nominal)"}; + Configurable v0Radius1p2{"v0Radius1p2", 1.2f, "Min V0 radius: 1.2 cm (pp-Xi)"}; + Configurable v0Radius2p5{"v0Radius2p5", 2.5f, "Min V0 radius: 2.5 cm"}; + Configurable v0Radius3p0{"v0Radius3p0", 3.0f, "Min V0 radius: 3.0 cm (dedicated Lambda)"}; + + Configurable lambdaMass10MeV{"lambdaMass10MeV", 0.010f, "Lambda mass window: 10 MeV/c2"}; + Configurable lambdaMass8MeV{"lambdaMass8MeV", 0.008f, "Lambda mass window: 8 MeV/c2 (Xi* nominal)"}; + Configurable lambdaMass6MeV{"lambdaMass6MeV", 0.006f, "Lambda mass window: 6 MeV/c2"}; + Configurable lambdaMass11p6MeV{"lambdaMass11p6MeV", 0.0116f, "Lambda mass window: 11.6 MeV/c2 (pp-Xi)"}; + + Configurable v0LifetimeDefault{"v0LifetimeDefault", 30.f, "Max Lambda proper lifetime: 30 cm"}; + Configurable v0LifetimeVar1{"v0LifetimeVar1", 30.f, "Reserved Lambda lifetime Var1 max; set when defined"}; + Configurable v0LifetimeVar2{"v0LifetimeVar2", 30.f, "Reserved Lambda lifetime Var2 max; set when defined"}; + + Configurable v0DaughterRows70{"v0DaughterRows70", 70, "Both Lambda daughters: TPC crossed rows >70"}; + Configurable v0DaughterRowsVar1{"v0DaughterRowsVar1", 70, "Reserved Lambda-daughter crossed-row Var1; set when defined"}; + Configurable v0DaughterRowsVar2{"v0DaughterRowsVar2", 70, "Reserved Lambda-daughter crossed-row Var2; set when defined"}; + + // Xi / cascade. + Configurable cascBachelorDcaPV005{"cascBachelorDcaPV005", 0.05f, "Min bachelor DCA to PV: 0.05 cm (pp-Xi)"}; + Configurable cascBachelorDcaPV006{"cascBachelorDcaPV006", 0.06f, "Min bachelor DCA to PV: 0.06 cm (Xi* nominal)"}; + Configurable cascBachelorDcaPV010{"cascBachelorDcaPV010", 0.10f, "Min bachelor DCA to PV: 0.10 cm"}; + + // Native CascDatas cascade-daughter DCA / fitter metric (maximum). + Configurable cascDcaDaughters1p0{"cascDcaDaughters1p0", 1.0f, "Max cascade-daughter DCA/native fitter metric: 1.0"}; + Configurable cascDcaDaughters0p25{"cascDcaDaughters0p25", 0.25f, "Max cascade-daughter DCA/native fitter metric: 0.25 (Xi* and pp-Xi)"}; + Configurable cascDcaDaughters0p20{"cascDcaDaughters0p20", 0.20f, "Max cascade-daughter DCA/native fitter metric: 0.20"}; + + Configurable cascCosPA097{"cascCosPA097", 0.97f, "Min cascade cosPA: 0.97"}; + Configurable cascCosPA098{"cascCosPA098", 0.98f, "Min cascade cosPA: 0.98 (Xi* nominal)"}; + Configurable cascCosPA09947{"cascCosPA09947", 0.9947f, "Min cascade cosPA: 0.9947 (pp-Xi)"}; + Configurable cascCosPA0995{"cascCosPA0995", 0.995f, "Min cascade cosPA: 0.995"}; + + Configurable cascRadius0p9{"cascRadius0p9", 0.9f, "Min cascade radius: 0.9 cm"}; + Configurable cascRadius1p0{"cascRadius1p0", 1.0f, "Min cascade radius: 1.0 cm (pp-Xi)"}; + Configurable cascRadius1p01{"cascRadius1p01", 1.01f, "Min cascade radius: 1.01 cm (Xi* nominal)"}; + Configurable cascRadius1p3{"cascRadius1p3", 1.3f, "Min cascade radius: 1.3 cm"}; + + Configurable xiMass10MeV{"xiMass10MeV", 0.010f, "Xi mass window: 10 MeV/c2"}; + Configurable xiMass8MeV{"xiMass8MeV", 0.008f, "Xi mass window: 8 MeV/c2 (Xi* nominal)"}; + Configurable xiMass6MeV{"xiMass6MeV", 0.006f, "Xi mass window: 6 MeV/c2"}; + + // Xi proper-lifetime working points, mL/p in cm. + // The default 22.6 cm corresponds approximately to 4.6*c*tau_Xi. + Configurable xiLifetimeDefault{"xiLifetimeDefault", 22.6f, "Default Xi proper-lifetime maximum mL/p (cm), ~4.6*c*tau_Xi"}; + Configurable xiLifetimeVar1{"xiLifetimeVar1", 15.0f, "Xi proper-lifetime variation 1 maximum mL/p (cm)"}; + Configurable xiLifetimeVar2{"xiLifetimeVar2", 12.0f, "Xi proper-lifetime variation 2 maximum mL/p (cm)"}; + + Configurable bachBaryonDCAxyPP{"bachBaryonDCAxyPP", 0.020f, "pp-Xi min |DCAxy| between bachelor and baryon (cm)"}; + Configurable xiRapidityPPMax{"xiRapidityPPMax", 0.5f, "pp-Xi maximum |y_Xi|"}; + } topoCuts; + + // --------------------------------------------------------------------------- + // Xi(1530) candidate retention. + // 1.45--1.70 GeV/c2 deliberately contains the existing LS-normalisation + // sidebands; in particular the right sideband extends to about 1.678 GeV/c2. + // There is NO Xi(1530) pT cut here. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "xiStar"; + Configurable massMin{"massMin", 1.45f, "Minimum retained M(Xi pi) (GeV/c2)"}; + Configurable massMax{"massMax", 1.70f, "Maximum retained M(Xi pi) (GeV/c2)"}; + Configurable rapidityMax{"rapidityMax", 0.5f, "Maximum |y_XiStar|"}; + } xiStarCuts; + + // --------------------------------------------------------------------------- + // External primary K. + // Raw TPC/TOF PID is retained downstream. DCA is NOT stored as floats; + // instead, default/variation decisions are packed into a uint8_t bitmap. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "kaon"; + Configurable etaMax{"etaMax", 0.8f, "Maximum |eta_K|"}; + Configurable ptMin{"ptMin", 0.15f, "Minimum pT_K (GeV/c)"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require isGlobalTrack (same quality baseline as prompt pion)"}; + Configurable requirePVContributor{"requirePVContributor", true, "Require isPVContributor"}; + Configurable tpcNSigmaEnvelope{"tpcNSigmaEnvelope", 6.f, "Loose |TPC nSigma_K| producer envelope"}; + + // Default K DCA working points are pT dependent. Optional constant Var1/Var2 + // values are disabled by default (<=0). If enabled, the producer retains the + // union of the default and enabled alternatives, so looser variations remain + // available downstream without storing raw DCA values. + Configurable dcaXYUsePtDependent{"dcaXYUsePtDependent", true, "Use pT-dependent default DCAxy cut"}; + Configurable dcaXYp0{"dcaXYp0", 0.004f, "K DCAxy(pT) p0 (cm)"}; + Configurable dcaXYp1{"dcaXYp1", 0.013f, "K DCAxy(pT) p1 (cm GeV/c)"}; + Configurable dcaXYMaxConstant{"dcaXYMaxConstant", 0.1f, "Constant default |DCAxy| max if pT-dependent mode is disabled (cm)"}; + Configurable dcaXYVar1Max{"dcaXYVar1Max", -1.f, "Optional K |DCAxy| Var1 max; <=0 disables (cm)"}; + Configurable dcaXYVar2Max{"dcaXYVar2Max", -1.f, "Optional K |DCAxy| Var2 max; <=0 disables (cm)"}; + + Configurable dcaZUsePtDependent{"dcaZUsePtDependent", true, "Use pT-dependent default DCAz cut"}; + Configurable dcaZp0{"dcaZp0", 0.004f, "K DCAz(pT) p0 (cm)"}; + Configurable dcaZp1{"dcaZp1", 0.013f, "K DCAz(pT) p1 (cm GeV/c)"}; + Configurable dcaZMaxConstant{"dcaZMaxConstant", 0.1f, "Constant default |DCAz| max if pT-dependent mode is disabled (cm)"}; + Configurable dcaZVar1Max{"dcaZVar1Max", -1.f, "Optional K |DCAz| Var1 max; <=0 disables (cm)"}; + Configurable dcaZVar2Max{"dcaZVar2Max", -1.f, "Optional K |DCAz| Var2 max; <=0 disables (cm)"}; + } kaonCuts; + + // --------------------------------------------------------------------------- + // Minimal QA. Histograms do not contribute to the reduced AOD row size. + // --------------------------------------------------------------------------- + HistogramRegistry qa{"qa", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext const&) + { + ccdb->setURL(ccdbURL.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + qa.add("hEventCutFlow", "Event cut flow;;events", HistType::kTH1F, {{6, 0.5, 6.5}}); + qa.add("hFT0MPercentile", "FT0M multiplicity percentile of written events;FT0M percentile (%);events", HistType::kTH1F, {{110, 0., 110.}}); + qa.add("hLambdaMass", "Selected loose-envelope Lambda mass;M_{p#pi} (GeV/c^{2});counts", HistType::kTH1F, {{240, 1.09, 1.14}}); + qa.add("hXiMass", "Selected loose-envelope Xi mass;M_{#Lambda#pi} (GeV/c^{2});counts", HistType::kTH1F, {{240, 1.29, 1.35}}); + qa.add("hXiStarMassUS", "US #Xi#pi candidates;M_{#Xi#pi} (GeV/c^{2});counts", HistType::kTH1F, {{500, 1.45, 1.70}}); + qa.add("hXiStarMassLS", "LS #Xi#pi candidates;M_{#Xi#pi} (GeV/c^{2});counts", HistType::kTH1F, {{500, 1.45, 1.70}}); + qa.add("hXiStarMassVsChannel", "#Xi#pi mass vs channel;M_{#Xi#pi} (GeV/c^{2});channel", HistType::kTH2F, {{500, 1.45, 1.70}, {5, -2.5, 2.5}}); + qa.add("hKaonTPCnSigmaVsP", "External K TPC PID;p (GeV/c);n#sigma_{TPC}^{K}", HistType::kTH2F, {{200, 0., 10.}, {160, -8., 8.}}); + qa.add("hKaonTOFnSigmaVsP", "External K TOF PID;p (GeV/c);n#sigma_{TOF}^{K}", HistType::kTH2F, {{200, 0., 10.}, {160, -8., 8.}}); + qa.add("hPiFirstTPCnSigmaVsPt", "Prompt pion TPC PID;p_{T} (GeV/c);n#sigma_{TPC}^{#pi}", HistType::kTH2F, {{200, 0., 10.}, {160, -8., 8.}}); + qa.add("hNObjectsPerWrittenEvent", "Objects in written events;N;type (0=#Xi#pi,1=K)", HistType::kTH2F, {{100, -0.5, 99.5}, {2, -0.5, 1.5}}); + + auto h = qa.get(HIST("hEventCutFlow")); + h->GetXaxis()->SetBinLabel(1, "all"); + h->GetXaxis()->SetBinLabel(2, "|z|<10"); + h->GetXaxis()->SetBinLabel(3, "sel8"); + h->GetXaxis()->SetBinLabel(4, "INEL>0"); + h->GetXaxis()->SetBinLabel(5, "XiPi present"); + h->GetXaxis()->SetBinLabel(6, "written"); + } + + // --------------------------------------------------------------------------- + // Small PODs used only inside one collision. They keep us from writing any + // reduced row until we know the event is useful. + // --------------------------------------------------------------------------- + struct PromptPionTmp { + float px{0.f}, py{0.f}, pz{0.f}; + int8_t charge{0}; + int64_t trackIndex{-1}; + uint64_t trackBits{0}; + }; + + struct XiTmp { + float px{0.f}, py{0.f}, pz{0.f}; + float mass{0.f}; + int8_t sign{0}; + int64_t bachelorIndex{-1}; + int64_t posIndex{-1}; + int64_t negIndex{-1}; + uint64_t trackBits{0}; + uint64_t topologyBits{0}; + }; + + struct XiStarTmp { + int8_t channel{0}; + float px{0.f}, py{0.f}, pz{0.f}; + float mass{0.f}; + uint64_t trackBits{0}; + uint64_t topologyBits{0}; + float pionPt{0.f}, pionEta{0.f}, pionPhi{0.f}; + int64_t pionIndex{-1}; + int64_t bachelorIndex{-1}; + int64_t posIndex{-1}; + int64_t negIndex{-1}; + }; + + struct KaonTmp { + int8_t charge{0}; + float px{0.f}, py{0.f}, pz{0.f}; + int64_t trackIndex{-1}; + uint8_t bits{0}; + uint8_t tpcNClsCrossedRows{0}; + float tpcNSigmaKa{999.f}; + float tofNSigmaKa{-999.f}; + bool hasTOF{false}; + }; + + // --------------------------------------------------------------------------- + // Event selection + // --------------------------------------------------------------------------- + template + bool passEvent(TCollision const& collision) + { + qa.fill(HIST("hEventCutFlow"), 1.0); + + if (std::abs(collision.posZ()) >= eventCuts.zVtxMax) { + return false; + } + qa.fill(HIST("hEventCutFlow"), 2.0); + + if (eventCuts.requireSel8 && !collision.sel8()) { + return false; + } + qa.fill(HIST("hEventCutFlow"), 3.0); + + if (eventCuts.requireINELgt0 && collision.multNTracksPVeta1() < 1) { + return false; + } + qa.fill(HIST("hEventCutFlow"), 4.0); + return true; + } + + // --------------------------------------------------------------------------- + // Prompt-pion selection and bitmap + // --------------------------------------------------------------------------- + template + bool passPionHybridPID(TTrack const& track, float nSigmaLimit) const + { + const float tpc = track.tpcNSigmaPi(); + if (!track.hasTOF()) { + return std::abs(tpc) < nSigmaLimit; + } + const float tof = track.tofNSigmaPi(); + return std::hypot(tpc, tof) < nSigmaLimit; + } + + // Threshold-based prompt-pion PID recipe. + // Below threshold: TPC only, irrespective of whether TOF happens to exist. + // At/above threshold: circular TPC+TOF. If TOF is absent, the configurable + // requireTOFAboveThreshold decides whether the track fails or falls back to TPC. + template + bool passPromptPionThresholdPID(TTrack const& track, + float tpcNSigmaLimit, + float circularNSigmaLimit) const + { + const float tpc = track.tpcNSigmaPi(); + + if (track.pt() < piFirstThresholdPidCuts.ptThreshold.value) { + return std::abs(tpc) < tpcNSigmaLimit; + } + + if (!track.hasTOF()) { + if (piFirstThresholdPidCuts.requireTOFAboveThreshold.value) { + return false; + } + return std::abs(tpc) < tpcNSigmaLimit; + } + + const float tof = track.tofNSigmaPi(); + return std::hypot(tpc, tof) < circularNSigmaLimit; + } + + static float dcaLimit(float pt, bool usePtDependent, float p0, float p1, float constantMax) + { + if (usePtDependent) { + return p0 + p1 / std::max(pt, kTinyMomentum); + } + return constantMax; + } + + float getMagneticField(uint64_t timestamp) + { + auto* grpo = ccdb->getForTimeStamp("/GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRPMagField object not found in CCDB for timestamp %llu", timestamp); + return 0.f; + } + return 0.1f * grpo->getNominalL3Field(); // kG -> T + } + + template + bool buildPromptPion(TTrack const& track, PromptPionTmp& out) + { + if (std::abs(track.eta()) >= piFirstCuts.etaMax) { + return false; + } + if (track.pt() <= piFirstCuts.ptMin) { + return false; + } + if (piFirstCuts.requireGlobalTrack && !track.isGlobalTrack()) { + return false; + } + if (piFirstCuts.requirePVContributor && !track.isPVContributor()) { + return false; + } + if (track.itsNClsInnerBarrel() < piFirstCuts.minITSInnerBarrelClusters) { + return false; + } + if (track.itsNCls() < piFirstCuts.minITSClusters) { + return false; + } + + const float dcaXYDefault = dcaLimit(track.pt(), piFirstCuts.dcaXYUsePtDependent.value, + piFirstCuts.dcaXYp0.value, piFirstCuts.dcaXYp1.value, + piFirstCuts.dcaXYMaxConstant.value); + if (std::abs(track.dcaXY()) >= dcaXYDefault) { + return false; + } + + const float dcaZDefault = dcaLimit(track.pt(), piFirstCuts.dcaZUsePtDependent.value, + piFirstCuts.dcaZp0.value, piFirstCuts.dcaZp1.value, + piFirstCuts.dcaZMaxConstant.value); + const float dcaZEnvelope = std::max({dcaZDefault, piFirstCuts.dcaZLoose.value, + piFirstCuts.dcaZTightVariation.value}); + if (std::abs(track.dcaZ()) >= dcaZEnvelope) { + return false; + } + + if (track.tpcNClsCrossedRows() <= piFirstCuts.tpcRowsLoose) { + return false; + } + + // Retain the UNION of all prompt-pion PID choices through their loosest + // (6-sigma) working points. The explicit TPC-only envelope is essential: + // a track with valid TOF may fail the circular hybrid cut while still + // satisfying the desired TPC-only selection. + const bool passTPCOnlyEnvelope = + std::abs(track.tpcNSigmaPi()) < otherTrackPidCuts.nSigma6.value; + const bool passAvailabilityEnvelope = + passPionHybridPID(track, otherTrackPidCuts.nSigma6.value); + const bool passThresholdEnvelope = + passPromptPionThresholdPID(track, + piFirstThresholdPidCuts.tpcNSigma6.value, + piFirstThresholdPidCuts.circularNSigma6.value); + if (!(passTPCOnlyEnvelope || passAvailabilityEnvelope || passThresholdEnvelope)) { + return false; + } + + uint64_t trackBits = 0; + + setTrackSelBit(trackBits, redxistark::kPiFirstPID3Sigma, + passPionHybridPID(track, otherTrackPidCuts.nSigma3.value)); + setTrackSelBit(trackBits, redxistark::kPiFirstPID4Sigma, + passPionHybridPID(track, otherTrackPidCuts.nSigma4.value)); + setTrackSelBit(trackBits, redxistark::kPiFirstPID5Sigma, + passPionHybridPID(track, otherTrackPidCuts.nSigma5.value)); + setTrackSelBit(trackBits, redxistark::kPiFirstPID6Sigma, + passPionHybridPID(track, otherTrackPidCuts.nSigma6.value)); + + setTrackSelBit(trackBits, redxistark::kPiFirstPtThresholdPID3Sigma, + passPromptPionThresholdPID(track, + piFirstThresholdPidCuts.tpcNSigma3.value, + piFirstThresholdPidCuts.circularNSigma3.value)); + setTrackSelBit(trackBits, redxistark::kPiFirstPtThresholdPID4Sigma, + passPromptPionThresholdPID(track, + piFirstThresholdPidCuts.tpcNSigma4.value, + piFirstThresholdPidCuts.circularNSigma4.value)); + setTrackSelBit(trackBits, redxistark::kPiFirstPtThresholdPID5Sigma, + passPromptPionThresholdPID(track, + piFirstThresholdPidCuts.tpcNSigma5.value, + piFirstThresholdPidCuts.circularNSigma5.value)); + setTrackSelBit(trackBits, redxistark::kPiFirstPtThresholdPID6Sigma, + passPromptPionThresholdPID(track, + piFirstThresholdPidCuts.tpcNSigma6.value, + piFirstThresholdPidCuts.circularNSigma6.value)); + + // Pure TPC-only prompt-pion PID bits. These remain valid irrespective of + // TOF availability and therefore support an all-pion TPC-only analysis. + const float absPromptTPCPi = std::abs(track.tpcNSigmaPi()); + setTrackSelBit(trackBits, redxistark::kPiFirstTPC3Sigma, + absPromptTPCPi < otherTrackPidCuts.nSigma3.value); + setTrackSelBit(trackBits, redxistark::kPiFirstTPC4Sigma, + absPromptTPCPi < otherTrackPidCuts.nSigma4.value); + setTrackSelBit(trackBits, redxistark::kPiFirstTPC5Sigma, + absPromptTPCPi < otherTrackPidCuts.nSigma5.value); + setTrackSelBit(trackBits, redxistark::kPiFirstTPC6Sigma, + absPromptTPCPi < otherTrackPidCuts.nSigma6.value); + + setTrackSelBit(trackBits, redxistark::kPiFirstTPCRows70, track.tpcNClsCrossedRows() > piFirstCuts.tpcRowsLoose); + setTrackSelBit(trackBits, redxistark::kPiFirstTPCRows80, track.tpcNClsCrossedRows() > piFirstCuts.tpcRowsDefault); + setTrackSelBit(trackBits, redxistark::kPiFirstTPCRows90, track.tpcNClsCrossedRows() > piFirstCuts.tpcRowsTight); + + setTrackSelBit(trackBits, redxistark::kPiFirstDCAz1cm, std::abs(track.dcaZ()) < piFirstCuts.dcaZLoose.value); + setTrackSelBit(trackBits, redxistark::kPiFirstDCAzDefault, std::abs(track.dcaZ()) < dcaZDefault); + setTrackSelBit(trackBits, redxistark::kPiFirstDCAz0p1cm, std::abs(track.dcaZ()) < piFirstCuts.dcaZTightVariation.value); + + out.px = track.px(); + out.py = track.py(); + out.pz = track.pz(); + out.charge = static_cast(track.sign()); + out.trackIndex = track.globalIndex(); + out.trackBits = trackBits; + + qa.fill(HIST("hPiFirstTPCnSigmaVsPt"), track.pt(), track.tpcNSigmaPi()); + return true; + } + + // --------------------------------------------------------------------------- + // External primary-K producer envelope + // --------------------------------------------------------------------------- + template + bool buildKaon(TTrack const& track, KaonTmp& out) + { + if (std::abs(track.eta()) >= kaonCuts.etaMax) { + return false; + } + if (track.pt() <= kaonCuts.ptMin) { + return false; + } + if (kaonCuts.requireGlobalTrack && !track.isGlobalTrack()) { + return false; + } + if (kaonCuts.requirePVContributor && !track.isPVContributor()) { + return false; + } + if (track.itsNClsInnerBarrel() < piFirstCuts.minITSInnerBarrelClusters) { + return false; + } + if (track.itsNCls() < piFirstCuts.minITSClusters) { + return false; + } + if (track.tpcNClsCrossedRows() <= piFirstCuts.tpcRowsLoose) { + return false; + } + if (std::abs(track.tpcNSigmaKa()) >= kaonCuts.tpcNSigmaEnvelope) { + return false; + } + + const float absDcaXY = std::abs(track.dcaXY()); + const float absDcaZ = std::abs(track.dcaZ()); + const float dcaXYDefault = dcaLimit(track.pt(), kaonCuts.dcaXYUsePtDependent.value, + kaonCuts.dcaXYp0.value, kaonCuts.dcaXYp1.value, + kaonCuts.dcaXYMaxConstant.value); + const float dcaZDefault = dcaLimit(track.pt(), kaonCuts.dcaZUsePtDependent.value, + kaonCuts.dcaZp0.value, kaonCuts.dcaZp1.value, + kaonCuts.dcaZMaxConstant.value); + + const bool passXYDefault = absDcaXY < dcaXYDefault; + const bool passXYVar1 = kaonCuts.dcaXYVar1Max.value > 0.f && absDcaXY < kaonCuts.dcaXYVar1Max.value; + const bool passXYVar2 = kaonCuts.dcaXYVar2Max.value > 0.f && absDcaXY < kaonCuts.dcaXYVar2Max.value; + const bool passZDefault = absDcaZ < dcaZDefault; + const bool passZVar1 = kaonCuts.dcaZVar1Max.value > 0.f && absDcaZ < kaonCuts.dcaZVar1Max.value; + const bool passZVar2 = kaonCuts.dcaZVar2Max.value > 0.f && absDcaZ < kaonCuts.dcaZVar2Max.value; + + // Retain the union of enabled DCA working points, but do not store raw DCA. + if (!(passXYDefault || passXYVar1 || passXYVar2) || + !(passZDefault || passZVar1 || passZVar2)) { + return false; + } + + uint8_t bits = 0; + setKaonSelBit(bits, redxistarkaon::kKaonDCAxyDefault, passXYDefault); + setKaonSelBit(bits, redxistarkaon::kKaonDCAxyVar1, passXYVar1); + setKaonSelBit(bits, redxistarkaon::kKaonDCAxyVar2, passXYVar2); + setKaonSelBit(bits, redxistarkaon::kKaonDCAzDefault, passZDefault); + setKaonSelBit(bits, redxistarkaon::kKaonDCAzVar1, passZVar1); + setKaonSelBit(bits, redxistarkaon::kKaonDCAzVar2, passZVar2); + + out.charge = static_cast(track.sign()); + out.px = track.px(); + out.py = track.py(); + out.pz = track.pz(); + out.trackIndex = track.globalIndex(); + out.bits = bits; + out.tpcNClsCrossedRows = toU8(track.tpcNClsCrossedRows()); + out.tpcNSigmaKa = track.tpcNSigmaKa(); + out.hasTOF = track.hasTOF(); + out.tofNSigmaKa = out.hasTOF ? track.tofNSigmaKa() : -999.f; + + const float p = std::sqrt(track.px() * track.px() + track.py() * track.py() + track.pz() * track.pz()); + qa.fill(HIST("hKaonTPCnSigmaVsP"), p, track.tpcNSigmaKa()); + if (track.hasTOF()) { + qa.fill(HIST("hKaonTOFnSigmaVsP"), p, track.tofNSigmaKa()); + } + return true; + } + + // --------------------------------------------------------------------------- + // Xi candidate from a native CascDatas row. + // --------------------------------------------------------------------------- + template + bool buildXi(TCollision const& collision, TCascade const& casc, XiTmp& out) + { + if (casc.sign() == 0) { + return false; + } + if (std::abs(casc.eta()) >= cascadeBaseCuts.cascadeEtaMax) { + return false; + } + if (casc.pt() <= cascadeBaseCuts.cascadePtMin) { + return false; + } + + const auto pos = casc.template posTrack_as(); + const auto neg = casc.template negTrack_as(); + const auto bach = casc.template bachelor_as(); + + // Fixed broad daughter acceptance. The >50 crossed-row pp-Xi WP is also + // explicitly stored as a track-selection bit below. + if (std::abs(pos.eta()) >= cascadeBaseCuts.daughterEtaMax || + std::abs(neg.eta()) >= cascadeBaseCuts.daughterEtaMax || + std::abs(bach.eta()) >= cascadeBaseCuts.daughterEtaMax) { + return false; + } + if (pos.tpcNClsCrossedRows() <= cascadeBaseCuts.daughterTPCrowsMin || + neg.tpcNClsCrossedRows() <= cascadeBaseCuts.daughterTPCrowsMin || + bach.tpcNClsCrossedRows() <= cascadeBaseCuts.daughterTPCrowsMin) { + return false; + } + + // Daughter roles: + // Xi- : pos = proton, neg = pion, bachelor = pion + // anti-Xi+ : pos = pion, neg = proton, bachelor = pion + const bool isXiMinus = casc.sign() < 0; + const auto& lambdaPion = isXiMinus ? neg : pos; + const auto& lambdaProton = isXiMinus ? pos : neg; + + // Dedicated Lambda fixed daughter kinematics. + if (lambdaPion.pt() <= cascadeBaseCuts.lambdaPionPtMin || + lambdaProton.pt() <= cascadeBaseCuts.lambdaProtonPtMin) { + return false; + } + + const float pLambdaX = casc.pxlambda(); + const float pLambdaY = casc.pylambda(); + const float pLambdaZ = casc.pzlambda(); + ROOT::Math::PxPyPzMVector lambda4(pLambdaX, pLambdaY, pLambdaZ, o2::constants::physics::MassLambda0); + if (std::abs(lambda4.Rapidity()) >= cascadeBaseCuts.lambdaRapidityMax) { + return false; + } + + // PID producer envelope: + // * keep TPC-only pion/proton decisions up to 6 sigma; + // * for the bachelor, TPC-only 6 sigma is sufficient to retain both the + // pp-Xi TPC-only selection and all tighter hybrid selections. + if (std::abs(bach.tpcNSigmaPi()) >= otherTrackPidCuts.nSigma6.value || + std::abs(lambdaPion.tpcNSigmaPi()) >= v0PidCuts.nSigma6.value || + std::abs(lambdaProton.tpcNSigmaPr()) >= v0PidCuts.nSigma6.value) { + return false; + } + + const float pionDcaPV = std::abs(isXiMinus ? casc.dcanegtopv() : casc.dcapostopv()); + const float protonDcaPV = std::abs(isXiMinus ? casc.dcapostopv() : casc.dcanegtopv()); + const float v0DcaPV = std::abs(casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ())); + const float v0DcaDaughters = std::abs(casc.dcaV0daughters()); + const float v0CosPA = casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()); + const float v0Radius = casc.v0radius(); + const float lambdaMassDiff = std::abs(casc.mLambda() - o2::constants::physics::MassLambda0); + + // Lambda proper-lifetime proxy: L(PV->V0)*m_Lambda/p_Lambda. + const float dxLambda = casc.xlambda() - collision.posX(); + const float dyLambda = casc.ylambda() - collision.posY(); + const float dzLambda = casc.zlambda() - collision.posZ(); + const float lambdaDecayLength = std::sqrt(dxLambda * dxLambda + dyLambda * dyLambda + dzLambda * dzLambda); + const float pLambda = std::sqrt(pLambdaX * pLambdaX + pLambdaY * pLambdaY + pLambdaZ * pLambdaZ); + const float v0ProperLifetime = lambdaDecayLength * o2::constants::physics::MassLambda0 / (pLambda + kTinyMomentum); + + const float bachelorDcaPV = std::abs(casc.dcabachtopv()); + + // IMPORTANT: these are the native CascDatas stored quantities. + const float cascDcaDaughters = std::abs(casc.dcacascdaughters()); + + const float cascCosPA = casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()); + const float cascRadius = casc.cascradius(); + const float xiMassDiff = std::abs(casc.mXi() - o2::constants::physics::MassXiMinus); + const float bachBaryonDCAxy = std::abs(casc.bachBaryonDCAxyToPV()); + + // Xi proper lifetime and rapidity. + const float dxXi = casc.x() - collision.posX(); + const float dyXi = casc.y() - collision.posY(); + const float dzXi = casc.z() - collision.posZ(); + const float xiDecayLength = std::sqrt(dxXi * dxXi + dyXi * dyXi + dzXi * dzXi); + const float pXi = std::sqrt(casc.px() * casc.px() + casc.py() * casc.py() + casc.pz() * casc.pz()); + const float xiProperLifetime = xiDecayLength * casc.mXi() / (pXi + kTinyMomentum); + ROOT::Math::PxPyPzMVector xi4(casc.px(), casc.py(), casc.pz(), casc.mXi()); + const float xiRapidity = xi4.Rapidity(); + + // ----------------------------------------------------------------------- + // Producer envelopes: retain the UNION of all source-defined WPs. + // ----------------------------------------------------------------------- + const float pionDcaPVEnvelope = std::min({topoCuts.v0PionDcaPV005.value, + topoCuts.v0PionDcaPV006.value, + topoCuts.v0PionDcaPV010.value, + topoCuts.v0PionDcaPV020.value}); + const float protonDcaPVEnvelope = std::min({topoCuts.v0ProtonDcaPV005.value, + topoCuts.v0ProtonDcaPV006.value, + topoCuts.v0ProtonDcaPV007.value, + topoCuts.v0ProtonDcaPV010.value}); + const float v0DcaPVEnvelope = std::min({topoCuts.v0DcaPV000.value, + topoCuts.v0DcaPV003.value, + topoCuts.v0DcaPV010.value}); + const float v0DcaDaughtersEnvelope = std::max({topoCuts.v0DcaDaughters1p0.value, + topoCuts.v0DcaDaughters0p5.value, + topoCuts.v0DcaDaughters0p1.value}); + const float v0CosPAEnvelope = std::min({topoCuts.v0CosPA097.value, + topoCuts.v0CosPA098.value, + topoCuts.v0CosPA09876.value, + topoCuts.v0CosPA099.value, + topoCuts.v0CosPA0995.value}); + const float v0RadiusEnvelope = std::min({topoCuts.v0Radius0p9.value, + topoCuts.v0Radius1p01.value, + topoCuts.v0Radius1p2.value, + topoCuts.v0Radius2p5.value, + topoCuts.v0Radius3p0.value}); + const float lambdaMassEnvelope = std::max({topoCuts.lambdaMass10MeV.value, + topoCuts.lambdaMass8MeV.value, + topoCuts.lambdaMass6MeV.value, + topoCuts.lambdaMass11p6MeV.value}); + const float v0LifetimeEnvelope = std::max({topoCuts.v0LifetimeDefault.value, + topoCuts.v0LifetimeVar1.value, + topoCuts.v0LifetimeVar2.value}); + + const float bachelorDcaPVEnvelope = std::min({topoCuts.cascBachelorDcaPV005.value, + topoCuts.cascBachelorDcaPV006.value, + topoCuts.cascBachelorDcaPV010.value}); + const float cascDcaDaughtersEnvelope = std::max({topoCuts.cascDcaDaughters1p0.value, + topoCuts.cascDcaDaughters0p25.value, + topoCuts.cascDcaDaughters0p20.value}); + const float cascCosPAEnvelope = std::min({topoCuts.cascCosPA097.value, + topoCuts.cascCosPA098.value, + topoCuts.cascCosPA09947.value, + topoCuts.cascCosPA0995.value}); + const float xiMassEnvelope = std::max({topoCuts.xiMass10MeV.value, + topoCuts.xiMass8MeV.value, + topoCuts.xiMass6MeV.value}); + const float xiLifetimeEnvelope = std::max({topoCuts.xiLifetimeDefault.value, + topoCuts.xiLifetimeVar1.value, + topoCuts.xiLifetimeVar2.value}); + + if (pionDcaPV <= pionDcaPVEnvelope || + protonDcaPV <= protonDcaPVEnvelope || + v0DcaPV < v0DcaPVEnvelope || + v0DcaDaughters >= v0DcaDaughtersEnvelope || + v0CosPA <= v0CosPAEnvelope || + v0Radius <= v0RadiusEnvelope || + v0Radius >= cascadeBaseCuts.maxV0Radius || + v0ProperLifetime >= v0LifetimeEnvelope || + lambdaMassDiff >= lambdaMassEnvelope || + bachelorDcaPV <= bachelorDcaPVEnvelope || + cascDcaDaughters >= cascDcaDaughtersEnvelope || + cascCosPA <= cascCosPAEnvelope || + cascRadius >= cascadeBaseCuts.maxCascRadius || + xiMassDiff >= xiMassEnvelope || + xiProperLifetime >= xiLifetimeEnvelope) { + return false; + } + + uint64_t trackBits = 0; + uint64_t topologyBits = 0; + + // ----------------------------------------------------------------------- + // Track / PID bits + // ----------------------------------------------------------------------- + setTrackSelBit(trackBits, redxistark::kXiBachelorPiHybridPID3Sigma, + passPionHybridPID(bach, otherTrackPidCuts.nSigma3.value)); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiHybridPID4Sigma, + passPionHybridPID(bach, otherTrackPidCuts.nSigma4.value)); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiHybridPID5Sigma, + passPionHybridPID(bach, otherTrackPidCuts.nSigma5.value)); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiHybridPID6Sigma, + passPionHybridPID(bach, otherTrackPidCuts.nSigma6.value)); + + const float absBachelorTPCPi = std::abs(bach.tpcNSigmaPi()); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiTPC3Sigma, absBachelorTPCPi < otherTrackPidCuts.nSigma3.value); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiTPC4Sigma, absBachelorTPCPi < otherTrackPidCuts.nSigma4.value); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiTPC4p8Sigma, absBachelorTPCPi < otherTrackPidCuts.bachelorTPC4p8.value); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiTPC5Sigma, absBachelorTPCPi < otherTrackPidCuts.nSigma5.value); + setTrackSelBit(trackBits, redxistark::kXiBachelorPiTPC6Sigma, absBachelorTPCPi < otherTrackPidCuts.nSigma6.value); + + const float absLambdaPiTPC = std::abs(lambdaPion.tpcNSigmaPi()); + setTrackSelBit(trackBits, redxistark::kLambdaPiTPC3Sigma, absLambdaPiTPC < v0PidCuts.nSigma3.value); + setTrackSelBit(trackBits, redxistark::kLambdaPiTPC4Sigma, absLambdaPiTPC < v0PidCuts.nSigma4.value); + setTrackSelBit(trackBits, redxistark::kLambdaPiTPC4p8Sigma, absLambdaPiTPC < v0PidCuts.nSigma4p8.value); + setTrackSelBit(trackBits, redxistark::kLambdaPiTPC5Sigma, absLambdaPiTPC < v0PidCuts.nSigma5.value); + setTrackSelBit(trackBits, redxistark::kLambdaPiTPC6Sigma, absLambdaPiTPC < v0PidCuts.nSigma6.value); + + const float absLambdaPrTPC = std::abs(lambdaProton.tpcNSigmaPr()); + setTrackSelBit(trackBits, redxistark::kLambdaPrTPC3Sigma, absLambdaPrTPC < v0PidCuts.nSigma3.value); + setTrackSelBit(trackBits, redxistark::kLambdaPrTPC4Sigma, absLambdaPrTPC < v0PidCuts.nSigma4.value); + setTrackSelBit(trackBits, redxistark::kLambdaPrTPC5Sigma, absLambdaPrTPC < v0PidCuts.nSigma5.value); + setTrackSelBit(trackBits, redxistark::kLambdaPrTPC6Sigma, absLambdaPrTPC < v0PidCuts.nSigma6.value); + + const bool allCascRows50 = + pos.tpcNClsCrossedRows() > cascadeBaseCuts.daughterTPCrowsMin && + neg.tpcNClsCrossedRows() > cascadeBaseCuts.daughterTPCrowsMin && + bach.tpcNClsCrossedRows() > cascadeBaseCuts.daughterTPCrowsMin; + setTrackSelBit(trackBits, redxistark::kAllCascDaughtersTPCRows50, allCascRows50); + + const bool v0Rows70 = + pos.tpcNClsCrossedRows() > topoCuts.v0DaughterRows70 && + neg.tpcNClsCrossedRows() > topoCuts.v0DaughterRows70; + const bool v0RowsVar1 = + pos.tpcNClsCrossedRows() > topoCuts.v0DaughterRowsVar1 && + neg.tpcNClsCrossedRows() > topoCuts.v0DaughterRowsVar1; + const bool v0RowsVar2 = + pos.tpcNClsCrossedRows() > topoCuts.v0DaughterRowsVar2 && + neg.tpcNClsCrossedRows() > topoCuts.v0DaughterRowsVar2; + setTrackSelBit(trackBits, redxistark::kV0DaughtersTPCRows70, v0Rows70); + setTrackSelBit(trackBits, redxistark::kV0DaughtersTPCRowsVar1, v0RowsVar1); + setTrackSelBit(trackBits, redxistark::kV0DaughtersTPCRowsVar2, v0RowsVar2); + + // ----------------------------------------------------------------------- + // V0 / Lambda topology bits + // ----------------------------------------------------------------------- + setTopoSelBit(topologyBits, redxistark::kV0PionDcaPV005, pionDcaPV > topoCuts.v0PionDcaPV005); + setTopoSelBit(topologyBits, redxistark::kV0PionDcaPV006, pionDcaPV > topoCuts.v0PionDcaPV006); + setTopoSelBit(topologyBits, redxistark::kV0PionDcaPV010, pionDcaPV > topoCuts.v0PionDcaPV010); + setTopoSelBit(topologyBits, redxistark::kV0PionDcaPV020, pionDcaPV > topoCuts.v0PionDcaPV020); + + setTopoSelBit(topologyBits, redxistark::kV0ProtonDcaPV005, protonDcaPV > topoCuts.v0ProtonDcaPV005); + setTopoSelBit(topologyBits, redxistark::kV0ProtonDcaPV006, protonDcaPV > topoCuts.v0ProtonDcaPV006); + setTopoSelBit(topologyBits, redxistark::kV0ProtonDcaPV007, protonDcaPV > topoCuts.v0ProtonDcaPV007); + setTopoSelBit(topologyBits, redxistark::kV0ProtonDcaPV010, protonDcaPV > topoCuts.v0ProtonDcaPV010); + + setTopoSelBit(topologyBits, redxistark::kV0DcaPV000, v0DcaPV > topoCuts.v0DcaPV000); + setTopoSelBit(topologyBits, redxistark::kV0DcaPV003, v0DcaPV > topoCuts.v0DcaPV003); + setTopoSelBit(topologyBits, redxistark::kV0DcaPV010, v0DcaPV > topoCuts.v0DcaPV010); + + setTopoSelBit(topologyBits, redxistark::kV0DcaDaughters1p0, v0DcaDaughters < topoCuts.v0DcaDaughters1p0); + setTopoSelBit(topologyBits, redxistark::kV0DcaDaughters0p5, v0DcaDaughters < topoCuts.v0DcaDaughters0p5); + setTopoSelBit(topologyBits, redxistark::kV0DcaDaughters0p1, v0DcaDaughters < topoCuts.v0DcaDaughters0p1); + + setTopoSelBit(topologyBits, redxistark::kV0CosPA097, v0CosPA > topoCuts.v0CosPA097); + setTopoSelBit(topologyBits, redxistark::kV0CosPA098, v0CosPA > topoCuts.v0CosPA098); + setTopoSelBit(topologyBits, redxistark::kV0CosPA09876, v0CosPA > topoCuts.v0CosPA09876); + setTopoSelBit(topologyBits, redxistark::kV0CosPA099, v0CosPA > topoCuts.v0CosPA099); + setTopoSelBit(topologyBits, redxistark::kV0CosPA0995, v0CosPA > topoCuts.v0CosPA0995); + + setTopoSelBit(topologyBits, redxistark::kV0Radius0p9, v0Radius > topoCuts.v0Radius0p9); + setTopoSelBit(topologyBits, redxistark::kV0Radius1p01, v0Radius > topoCuts.v0Radius1p01); + setTopoSelBit(topologyBits, redxistark::kV0Radius1p2, v0Radius > topoCuts.v0Radius1p2); + setTopoSelBit(topologyBits, redxistark::kV0Radius2p5, v0Radius > topoCuts.v0Radius2p5); + setTopoSelBit(topologyBits, redxistark::kV0Radius3p0, v0Radius > topoCuts.v0Radius3p0); + + setTopoSelBit(topologyBits, redxistark::kLambdaMass10MeV, lambdaMassDiff < topoCuts.lambdaMass10MeV); + setTopoSelBit(topologyBits, redxistark::kLambdaMass8MeV, lambdaMassDiff < topoCuts.lambdaMass8MeV); + setTopoSelBit(topologyBits, redxistark::kLambdaMass6MeV, lambdaMassDiff < topoCuts.lambdaMass6MeV); + setTopoSelBit(topologyBits, redxistark::kLambdaMass11p6MeV, lambdaMassDiff < topoCuts.lambdaMass11p6MeV); + + setTopoSelBit(topologyBits, redxistark::kV0LifetimeDefault, v0ProperLifetime < topoCuts.v0LifetimeDefault); + setTopoSelBit(topologyBits, redxistark::kV0LifetimeVar1, v0ProperLifetime < topoCuts.v0LifetimeVar1); + setTopoSelBit(topologyBits, redxistark::kV0LifetimeVar2, v0ProperLifetime < topoCuts.v0LifetimeVar2); + + // ----------------------------------------------------------------------- + // Xi / cascade topology bits + // ----------------------------------------------------------------------- + setTopoSelBit(topologyBits, redxistark::kCascBachelorDcaPV005, bachelorDcaPV > topoCuts.cascBachelorDcaPV005); + setTopoSelBit(topologyBits, redxistark::kCascBachelorDcaPV006, bachelorDcaPV > topoCuts.cascBachelorDcaPV006); + setTopoSelBit(topologyBits, redxistark::kCascBachelorDcaPV010, bachelorDcaPV > topoCuts.cascBachelorDcaPV010); + + setTopoSelBit(topologyBits, redxistark::kCascDcaDaughters1p0, cascDcaDaughters < topoCuts.cascDcaDaughters1p0); + setTopoSelBit(topologyBits, redxistark::kCascDcaDaughters0p25, cascDcaDaughters < topoCuts.cascDcaDaughters0p25); + setTopoSelBit(topologyBits, redxistark::kCascDcaDaughters0p20, cascDcaDaughters < topoCuts.cascDcaDaughters0p20); + + setTopoSelBit(topologyBits, redxistark::kCascCosPA097, cascCosPA > topoCuts.cascCosPA097); + setTopoSelBit(topologyBits, redxistark::kCascCosPA098, cascCosPA > topoCuts.cascCosPA098); + setTopoSelBit(topologyBits, redxistark::kCascCosPA09947, cascCosPA > topoCuts.cascCosPA09947); + setTopoSelBit(topologyBits, redxistark::kCascCosPA0995, cascCosPA > topoCuts.cascCosPA0995); + + setTopoSelBit(topologyBits, redxistark::kCascRadius0p9, cascRadius > topoCuts.cascRadius0p9); + setTopoSelBit(topologyBits, redxistark::kCascRadius1p0, cascRadius > topoCuts.cascRadius1p0); + setTopoSelBit(topologyBits, redxistark::kCascRadius1p01, cascRadius > topoCuts.cascRadius1p01); + setTopoSelBit(topologyBits, redxistark::kCascRadius1p3, cascRadius > topoCuts.cascRadius1p3); + + setTopoSelBit(topologyBits, redxistark::kXiMass10MeV, xiMassDiff < topoCuts.xiMass10MeV); + setTopoSelBit(topologyBits, redxistark::kXiMass8MeV, xiMassDiff < topoCuts.xiMass8MeV); + setTopoSelBit(topologyBits, redxistark::kXiMass6MeV, xiMassDiff < topoCuts.xiMass6MeV); + + setTopoSelBit(topologyBits, redxistark::kXiLifetimeDefault, xiProperLifetime < topoCuts.xiLifetimeDefault.value); + setTopoSelBit(topologyBits, redxistark::kXiLifetimeVar1, xiProperLifetime < topoCuts.xiLifetimeVar1.value); + setTopoSelBit(topologyBits, redxistark::kXiLifetimeVar2, xiProperLifetime < topoCuts.xiLifetimeVar2.value); + + setTopoSelBit(topologyBits, redxistark::kBachBaryonDCAxy0020, bachBaryonDCAxy > topoCuts.bachBaryonDCAxyPP); + setTopoSelBit(topologyBits, redxistark::kXiRapidity05, std::abs(xiRapidity) < topoCuts.xiRapidityPPMax); + + out.px = casc.px(); + out.py = casc.py(); + out.pz = casc.pz(); + out.mass = casc.mXi(); + out.sign = static_cast(casc.sign()); + out.bachelorIndex = bach.globalIndex(); + out.posIndex = pos.globalIndex(); + out.negIndex = neg.globalIndex(); + out.trackBits = trackBits; + out.topologyBits = topologyBits; + + qa.fill(HIST("hLambdaMass"), casc.mLambda()); + qa.fill(HIST("hXiMass"), casc.mXi()); + return true; + } + + // --------------------------------------------------------------------------- + // Xi + prompt-pion -> Xi(1530) candidate. + // --------------------------------------------------------------------------- + bool buildXiStar(XiTmp const& xi, PromptPionTmp const& pion, XiStarTmp& out) + { + // Never allow the prompt pion to reuse a cascade daughter track. + if (pion.trackIndex == xi.bachelorIndex || + pion.trackIndex == xi.posIndex || + pion.trackIndex == xi.negIndex) { + return false; + } + + ROOT::Math::PxPyPzMVector xi4(xi.px, xi.py, xi.pz, xi.mass); + ROOT::Math::PxPyPzMVector pion4(pion.px, pion.py, pion.pz, o2::constants::physics::MassPionCharged); + const auto mother = xi4 + pion4; + + if (std::abs(mother.Rapidity()) >= xiStarCuts.rapidityMax) { + return false; + } + if (mother.M() <= xiStarCuts.massMin || mother.M() >= xiStarCuts.massMax) { + return false; + } + + int8_t channel = 0; + if (xi.sign < 0) { // Xi- + channel = pion.charge > 0 ? redxistark::kXiStarUS : redxistark::kXiStarLS; + } else { // anti-Xi+ + channel = pion.charge < 0 ? redxistark::kAntiXiStarUS : redxistark::kAntiXiStarLS; + } + + out.channel = channel; + out.px = mother.Px(); + out.py = mother.Py(); + out.pz = mother.Pz(); + out.mass = mother.M(); + out.trackBits = xi.trackBits | pion.trackBits; + out.topologyBits = xi.topologyBits; + out.pionPt = std::hypot(pion.px, pion.py); + out.pionEta = out.pionPt > 0.f ? std::asinh(pion.pz / out.pionPt) : 0.f; + out.pionPhi = std::atan2(pion.py, pion.px); + out.pionIndex = pion.trackIndex; + out.bachelorIndex = xi.bachelorIndex; + out.posIndex = xi.posIndex; + out.negIndex = xi.negIndex; + + if (std::abs(channel) == 1) { + qa.fill(HIST("hXiStarMassUS"), out.mass); + } else { + qa.fill(HIST("hXiStarMassLS"), out.mass); + } + qa.fill(HIST("hXiStarMassVsChannel"), out.mass, static_cast(channel)); + return true; + } + + // --------------------------------------------------------------------------- + // Main producer + // --------------------------------------------------------------------------- + void processData(EventCandidates::iterator const& collision, + aod::BCsWithTimestamps const&, + TracksFull const& tracks, + aod::CascDatas const& cascades) + { + if (!passEvent(collision)) { + return; + } + + const float ft0MPercentile = collision.centFT0M(); + qa.fill(HIST("hFT0MPercentile"), ft0MPercentile); + + std::vector promptPions; + std::vector kaons; + std::vector xis; + std::vector xiStars; + + // CPU protection for Hyperloop: Xi candidates are much rarer than ordinary + // primary tracks. Select cascades first and do not scan all primary tracks + // in events that contain no Xi candidate inside the loose envelope. + if (cascades.size() == 0) { + return; + } + xis.reserve(cascades.size()); + for (auto const& casc : cascades) { + XiTmp xi; + if (buildXi(collision, casc, xi)) { + xis.push_back(xi); + } + } + if (xis.empty()) { + return; + } + + // One pass over primary tracks: build prompt-pion and external-K lists. + for (auto const& track : tracks) { + PromptPionTmp pion; + if (buildPromptPion(track, pion)) { + promptPions.push_back(pion); + } + + KaonTmp kaon; + if (buildKaon(track, kaon)) { + kaons.push_back(kaon); + } + } + + if (promptPions.empty()) { + return; + } + + // Build both US and LS Xi-pi candidates. No k* selection is made here. + xiStars.reserve(xis.size() * std::min(promptPions.size(), 8)); + for (auto const& xi : xis) { + for (auto const& pion : promptPions) { + XiStarTmp candidate; + if (buildXiStar(xi, pion, candidate)) { + xiStars.push_back(candidate); + } + } + } + + if (xiStars.empty()) { + return; + } + + qa.fill(HIST("hEventCutFlow"), 5.0); + + const auto bc = collision.bc_as(); + const float bz = getMagneticField(bc.timestamp()); + + redEvents(collision.posZ(), collision.numContrib(), ft0MPercentile, bz); + const auto redEventIndex = redEvents.lastIndex(); + + for (auto const& candidate : xiStars) { + xiStarCandidates(redEventIndex, + candidate.channel, + candidate.px, + candidate.py, + candidate.pz, + candidate.mass, + candidate.trackBits, + candidate.topologyBits, + candidate.pionPt, + candidate.pionEta, + candidate.pionPhi, + candidate.pionIndex, + candidate.bachelorIndex, + candidate.posIndex, + candidate.negIndex); + } + + for (auto const& kaon : kaons) { + kaonCandidates(redEventIndex, + kaon.charge, + kaon.px, + kaon.py, + kaon.pz, + kaon.trackIndex, + kaon.bits, + kaon.tpcNClsCrossedRows, + kaon.tpcNSigmaKa, + kaon.tofNSigmaKa, + kaon.hasTOF); + } + + qa.fill(HIST("hNObjectsPerWrittenEvent"), static_cast(xiStars.size()), 0.f); + qa.fill(HIST("hNObjectsPerWrittenEvent"), static_cast(kaons.size()), 1.f); + qa.fill(HIST("hEventCutFlow"), 6.0); + } + + PROCESS_SWITCH(xi1530kaonreducedtable, processData, "Produce reduced Xi(1530)-K femtoscopy tables", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/CMakeLists.txt index 1522e629b48..a0a64e95190 100644 --- a/PWGLF/TableProducer/Strangeness/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/CMakeLists.txt @@ -13,26 +13,31 @@ add_subdirectory(Converters) o2physics_add_dpl_workflow(cascadefinder SOURCES cascadefinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeflow SOURCES cascadeflow.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademcbuilder SOURCES cascademcbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademcfinder SOURCES cascademcfinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangenesstofpid SOURCES strangenesstofpid.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -43,36 +48,48 @@ o2physics_add_dpl_workflow(cascadespawner o2physics_add_dpl_workflow(cascqaanalysis SOURCES cascqaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascderivedqaanalysis SOURCES cascderivedqaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(hstrangecorrelationfilter +o2physics_add_dpl_workflow(hstrangecorrelationfilter # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES hStrangeCorrelationFilter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(double-casc-tree-creator - SOURCES doubleCascTreeCreator.cxx +o2physics_add_dpl_workflow(double-omega-tree-creator + SOURCES doubleOmegaTreeCreator.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(double-omega-builder + SOURCES doubleOmegaBuilder.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzerofinder SOURCES lambdakzerofinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromcbuilder SOURCES lambdakzeromcbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromcfinder SOURCES lambdakzeromcfinder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -81,11 +98,17 @@ o2physics_add_dpl_workflow(lambdakzerospawner PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(sigmaminus-task +o2physics_add_dpl_workflow(sigmaminus-task # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES sigmaminustask.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(sigmaplusbuilder + SOURCES sigmaplusbuilder.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2::DCAFitter O2Physics::AnalysisCore O2Physics::MLCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(strange-tree-creator SOURCES strangeTreeCreator.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore @@ -93,81 +116,96 @@ o2physics_add_dpl_workflow(strange-tree-creator o2physics_add_dpl_workflow(st-coll-ids SOURCES stCollIds.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangederivedbuilder SOURCES strangederivedbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangenessbuilder SOURCES strangenessbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager O2Physics::MLCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(v0-selector +o2physics_add_dpl_workflow(v0-selector # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES v0selector.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0qaanalysis SOURCES v0qaanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambdalambdatable +o2physics_add_dpl_workflow(lambdalambdatable # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES LambdaLambdatable.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DetectorsVertexing COMPONENT_NAME Analysis) # ML selection -o2physics_add_dpl_workflow(lambdakzeromlselectiontreecreator +o2physics_add_dpl_workflow(lambdakzeromlselectiontreecreator # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdakzeroMLSelectionTreeCreator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(cascademlselectiontreecreator +o2physics_add_dpl_workflow(cascademlselectiontreecreator # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES cascadeMLSelectionTreeCreator.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeromlselection SOURCES lambdakzeromlselection.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascademlselection SOURCES cascademlselection.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sigma0builder SOURCES sigma0builder.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2::EMCALBase O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB O2::DCAFitter COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambdajetpolarizationbuilder +o2physics_add_dpl_workflow(lambdajetpolarizationbuilder # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdaJetpolarizationbuilder.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambdajetpolarizationions +o2physics_add_dpl_workflow(lambdajetpolarizationions # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES lambdaJetPolarizationIons.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracents SOURCES stracents.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdaspincorrelation SOURCES lambdaspincorrelation.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(sigmahadcorr +o2physics_add_dpl_workflow(sigmahadcorr # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility.) SOURCES sigmaHadCorr.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -175,11 +213,19 @@ o2physics_add_dpl_workflow(sigmahadcorr if(FastJet_FOUND) o2physics_add_dpl_workflow(lfinjets SOURCES lfinjets.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) endif() o2physics_add_dpl_workflow(phi-strange-correlator SOURCES phiStrangeCorrelator.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(strangeness-mc-centrality + SOURCES strangenessMcCentrality.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt b/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt index 8239715863d..64bcc3a249d 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt +++ b/PWGLF/TableProducer/Strangeness/Converters/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(stradautracksconverter SOURCES stradautracksconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -21,125 +22,186 @@ o2physics_add_dpl_workflow(stradautrackstpcpidconverter o2physics_add_dpl_workflow(stradautrackstofpidconverter SOURCES stradautrackstofpidconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautrackstofpidconverter2 SOURCES stradautrackstofpidconverter2.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(stradautrackstofpidconverter3 + SOURCES stradautrackstofpidconverter3.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(stradautrackstofpidconverter4 + SOURCES stradautrackstofpidconverter4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter SOURCES stradautracksextraconverter.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter2 SOURCES stradautracksextraconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stradautracksextraconverter3 SOURCES stradautracksextraconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strarawcentsconverter SOURCES strarawcentsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strarawcentsconverter2v4 SOURCES strarawcentsconverter2v4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter SOURCES straevselsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2 SOURCES straevselsconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::ReconstructionDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter3 SOURCES straevselsconverter3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::ReconstructionDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter4 SOURCES straevselsconverter4.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter5 SOURCES straevselsconverter5.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(straevselsconverter6 + SOURCES straevselsconverter6.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(straevselextrasconverter + SOURCES straevselextrasconverter.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(straevselextrasconverter2 + SOURCES straevselextrasconverter2.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(straevselsconverter2rawcents SOURCES straevselsconverter2rawcents.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents2 SOURCES straevselsconverter2rawcents2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(straevselsconverter2rawcents3 SOURCES straevselsconverter2rawcents3.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0coresconverter SOURCES v0coresconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0coresconverter2 SOURCES v0coresconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0mlscoresconverter SOURCES v0mlscoresconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollisionconverter SOURCES stramccollisionconverter.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(stramccollisionconverter2 + SOURCES stramccollisionconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strastampsconverter SOURCES strastampsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracentconverter SOURCES stracentconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stracentconverter2 SOURCES stracentconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stramccollmultconverter SOURCES stramccollmultconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(stramccollisionconverter2 - SOURCES stramccollisionconverter2.cxx +o2physics_add_dpl_workflow(stramccollmultconverter2 + SOURCES stramccollmultconverter2.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(zdcneutronsconverter SOURCES zdcneutronsconverter.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/TableProducer/Strangeness/Converters/README.md b/PWGLF/TableProducer/Strangeness/Converters/README.md new file mode 100644 index 00000000000..9779e642606 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/README.md @@ -0,0 +1,58 @@ +# Strangeness table converters + +Converter tasks migrate old versions of the strangeness derived-data tables to +newer ones, so that analyses written against the current data model can still +run over older derived data. Each task reads one or more legacy tables and +produces the corresponding newer version, filling any column that did not exist +in the old table with a dummy value. + +Add the workflow you need to your job with `o2-analysis-lf-`; only the +converters whose *input* tables are actually present in your derived data need +to be enabled. + +The table below is generated automatically from the sources. + +| Converter | Reads | Produces | Description | +|---|---|---|---| +| `stracentconverter.cxx` | `StraCents_000` | `StraCents_001` | Converts Stra Cents from 000 to 001 | +| `stracentconverter2.cxx` | `StraCents_001` | `StraCents_002` | Converts Stra Cents from 001 to 002 | +| `stradautracksconverter.cxx` | `V0Cores` + `V0Extras` + `V0TOFs`, `CascCores` + `CascExtras` + `CascTOFs`, `DauTrackExtras` *(=DauTrackExtras_003)* | `DauTrackTOFPIDs_000` | | +| `stradautracksextraconverter.cxx` | `DauTrackExtras_000` | `DauTrackExtras_001` | Converts V0 version 001 to 002 | +| `stradautracksextraconverter2.cxx` | `DauTrackExtras_001` | `DauTrackExtras_002` | Converts daughter TracksExtra from 1 to 2 | +| `stradautracksextraconverter3.cxx` | `DauTrackExtras_002` | `DauTrackExtras_003` | Converts daughter TracksExtra from 2 to 3 | +| `stradautrackstofpidconverter.cxx` | `V0Cores` + `V0Extras` + `V0TOFs`, `CascCores` + `CascExtras` + `CascTOFs`, `DauTrackExtras` *(=DauTrackExtras_003)* | `DauTrackTOFPIDs` *(=DauTrackTOFPIDs_003)* | | +| `stradautrackstofpidconverter2.cxx` | `StraCollisions`, `DauTrackExtras` *(=DauTrackExtras_003)* + `DauTrackTOFPIDs_000`, `V0CollRefs` + `V0Cores` + `V0Extras` | `DauTrackTOFPIDs_001`
`StraEvTimes_000` | converts DauTrackTOFPIDs_000 to _001 | +| `stradautrackstofpidconverter3.cxx` | `DauTrackTOFPIDs_001`, `StraEvTimes_000` | `DauTrackTOFPIDs_002`
`StraEvTimes_001` | converts DauTrackTOFPIDs_001 to _002 | +| `stradautrackstofpidconverter4.cxx` | `DauTrackTOFPIDs_002`, `StraCollisions` + `StraStamps` *(=StraStamps_001)* | `DauTrackTOFPIDs_003` | converts DauTrackTOFPIDs_002 to _003 | +| `stradautrackstpcpidconverter.cxx` | `DauTrackTPCPIDs_000` | `DauTrackTPCPIDs_001` | converts DauTrackTOFPIDs_000 to _001 | +| `straevselextrasconverter.cxx` | **processAll**: `StraEvSels_005` + `StraEvSelExtras_000`
**processStraEvSelsOnly**: `StraEvSels_005` *(off by default)* | `StraEvSelExtras_001` | | +| `straevselextrasconverter2.cxx` | `StraEvSels` *(=StraEvSels_006)* | `StraEvSelExtras` *(=StraEvSelExtras_001)* | Produce dummy StraEvSelExtras for analysis subscribing to StraEvSelExtras but not saved in the strangeness derived data (typically when running over pp strangeness derived data) | +| `straevselsconverter.cxx` | `StraEvSels_000` + `StraRawCents_004` | `StraEvSels_001` | Converts Stra Event selections from 000 to 001 | +| `straevselsconverter2.cxx` | `StraEvSels_001` | `StraEvSels_002` | Converts Stra Event selections from 000 to 001 | +| `straevselsconverter2rawcents.cxx` | `StraEvSels_001` | `StraRawCents_004` | Converts V0 version 001 to 002 | +| `straevselsconverter2rawcents2.cxx` | `StraEvSels_002` | `StraRawCents_004` | Converts V0 version 001 to 002 | +| `straevselsconverter2rawcents3.cxx` | `StraEvSels_003` | `StraRawCents_004` | Converts V0 version 001 to 002 | +| `straevselsconverter3.cxx` | `StraEvSels_002` | `StraEvSels_003` | Converts Stra Event selections from 000 to 001 | +| `straevselsconverter4.cxx` | `StraEvSels_003` | `StraEvSels_004` | Converts Stra Event selections from 000 to 001 | +| `straevselsconverter5.cxx` | `StraEvSels_004` + `StraStamps` *(=StraStamps_001)* | `StraEvSels_005` | Converts Stra Event selections from 004 to 005 | +| `straevselsconverter6.cxx` | `StraEvSels_005` | `StraEvSels_006` | Converts Stra Event selections from 005 to 006 | +| `stramccollisionconverter.cxx` | `StraMCCollisions_000` | `StraMCCollisions_001` | Converts V0 version 001 to 002 | +| `stramccollisionconverter2.cxx` | `StraMCCollisions_001` | `StraMCCollisions_002` | Converts V0 version 001 to 002 | +| `stramccollmultconverter.cxx` | `StraMCCollMults_000` | `StraMCCollMults_001` | Converts V0 version 001 to 002 | +| `stramccollmultconverter2.cxx` | `StraMCCollMults_001` | `StraMCCollMults_002` | Converts V0 version 001 to 002 | +| `strarawcentsconverter.cxx` | **process000to001**: `StraRawCents_000` *(off by default)*
**process002to003**: `StraRawCents_002` *(off by default)* | `StraRawCents_001`
`StraRawCents_003` | Converts V0 version 001 to 002 | +| `strarawcentsconverter2v4.cxx` | `StraRawCents_003` | `StraRawCents_004` | Converts V0 version 001 to 002 | +| `strastampsconverter.cxx` | `StraStamps_000` | `StraStamps_001` | Converts Stra Stamps from 000 to 001 | +| `v0coresconverter.cxx` | `V0MCCores_000` | `V0MCCores_001` | Converts V0 version 001 to 002 | +| `v0coresconverter2.cxx` | `V0MCCores_001` | `V0MCCores_002` | Converts V0 version 001 to 002 | +| `v0mlscoresconverter.cxx` | `V0Cores` | `V0GammaMLScores`
`V0LambdaMLScores`
`V0AntiLambdaMLScores`
`V0K0ShortMLScores` | Converts V0 version 001 to 002 | +| `zdcneutronsconverter.cxx` | `StraEvSels` *(=StraEvSels_006)* | `ZDCNeutrons`
`ZDCNMCCollRefs` | | + +Notes: + +* Tables joined with `+` are subscribed together in a single argument + (`soa::Join<...>`); a comma separates independent arguments. +* `Name *(=Name_00N)*` means the task subscribes to (or produces) the + unversioned alias, which currently resolves to version `N` in the data model. +* Tasks with more than one `process` function list each one separately; + a switch marked *off by default* must be enabled explicitly. diff --git a/PWGLF/TableProducer/Strangeness/Converters/makeConverterDocumentation.py b/PWGLF/TableProducer/Strangeness/Converters/makeConverterDocumentation.py new file mode 100644 index 00000000000..0914bc295a4 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/makeConverterDocumentation.py @@ -0,0 +1,662 @@ +#!/usr/bin/env python3 +""" +converter_tables.py -- summarise O2Physics converter tasks. + +Scans a directory of O2Physics workflow sources (by default the PWGLF +strangeness Converters directory), works out which AOD tables each task +subscribes to and which it produces, and writes the result as a Markdown +table (README.md) or as JSON/CSV. + +Typical use: + + ./converter_tables.py # print Markdown to stdout + ./converter_tables.py -o README.md # write the README in place + ./converter_tables.py --check # CI: fail if README is stale + ./converter_tables.py --format json + +The parser is regex based: it does not need a compiler, O2 headers or a +build environment, and it deliberately errs on the side of reporting what +it cannot parse (see the --strict flag) rather than silently dropping it. + +Recognised constructs +--------------------- + outputs Produces, Spawns, Builds + inputs arguments of every void process*(...) member function, + including soa::Join<...>, soa::Filtered<...>, ::iterator, + and file-scope `using X = ...;` aliases + extras PROCESS_SWITCH(...) descriptions and default on/off state, + the descriptive comment above the struct, + resolution of unversioned table names (StraEvSels) to the + concrete version they currently alias (StraEvSels_006) + +Written for the ALICE O2Physics repository; stdlib only, Python >= 3.8. +""" + +from __future__ import annotations + +import argparse +import csv +import io +import json +import os +import re +import sys +from dataclasses import dataclass, field, asdict +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Tuple + +# -------------------------------------------------------------------------- +# Source cleaning +# -------------------------------------------------------------------------- + +_STRING_RE = re.compile(r'"(?:\\.|[^"\\])*"') + + +def strip_comments(text: str) -> str: + """Remove // and /* */ comments, preserving line structure. + + Line count is preserved so that reported line numbers stay meaningful. + String literals are protected so that "http://..." is not eaten. + """ + out = [] + i, n = 0, len(text) + while i < n: + c = text[i] + if c == '"': + m = _STRING_RE.match(text, i) + if m: + out.append(m.group(0)) + i = m.end() + continue + out.append(c) + i += 1 + elif c == "'" and i + 2 < n: + j = text.find("'", i + 1) + while j != -1 and text[j - 1] == "\\": + j = text.find("'", j + 1) + if j != -1: + out.append(text[i:j + 1]) + i = j + 1 + continue + out.append(c) + i += 1 + elif text.startswith("//", i): + j = text.find("\n", i) + i = n if j == -1 else j + elif text.startswith("/*", i): + j = text.find("*/", i + 2) + chunk = text[i:] if j == -1 else text[i:j + 2] + out.append("\n" * chunk.count("\n")) + i = n if j == -1 else j + 2 + else: + out.append(c) + i += 1 + return "".join(out) + + +# -------------------------------------------------------------------------- +# Small C++-ish helpers +# -------------------------------------------------------------------------- + +TABLE_RE = re.compile(r"\b(?:o2\s*::\s*)?aod\s*::\s*(\w+)") + + +def match_paren(text: str, open_pos: int) -> int: + """Index of the ')' matching the '(' at open_pos (-1 if unbalanced).""" + depth = 0 + for i in range(open_pos, len(text)): + if text[i] == "(": + depth += 1 + elif text[i] == ")": + depth -= 1 + if depth == 0: + return i + return -1 + + +def split_top_level(text: str, sep: str = ",") -> List[str]: + """Split on `sep`, ignoring separators nested in <>, (), [] or {}.""" + parts, depth, cur = [], 0, [] + for ch in text: + if ch in "<([{": + depth += 1 + elif ch in ">)]}": + depth -= 1 + if ch == sep and depth == 0: + parts.append("".join(cur)) + cur = [] + else: + cur.append(ch) + parts.append("".join(cur)) + return [p.strip() for p in parts if p.strip()] + + +# -------------------------------------------------------------------------- +# Data model: `using X = X_00N;` aliases and versioned declarations +# -------------------------------------------------------------------------- + +ALIAS_RE = re.compile(r"^\s*using\s+(\w+)\s*=\s*([^;]+);", re.M) +VERSIONED_RE = re.compile( + r"DECLARE_SOA_TABLE_VERSIONED\s*\(\s*(\w+)\s*,\s*\"(\w+)\"\s*,\s*\"([^\"]+)\"\s*,\s*(\d+)" +) +PLAIN_TABLE_RE = re.compile( + r"DECLARE_SOA_TABLE(?:_FULL|_STAGED)?\s*\(\s*(\w+)\s*,\s*\"(\w+)\"\s*,\s*\"([^\"]+)\"" +) + + +@dataclass +class DataModel: + """Maps unversioned table names to the version they currently alias.""" + + alias: Dict[str, str] = field(default_factory=dict) # StraEvSels -> StraEvSels_006 + declared: Dict[str, str] = field(default_factory=dict) # name -> "AOD/STRAEVSELS/6" + + def resolve(self, name: str) -> Optional[str]: + """Return Name_00N if `name` is an alias for a versioned table.""" + target = self.alias.get(name) + if not target or target == name: + return None + # only report genuine version aliases (StraEvSels -> StraEvSels_006), + # not renames such as V0Cores -> V0CoresBase + return target if re.fullmatch(re.escape(name) + r"_\d+", target) else None + + @classmethod + def from_paths(cls, paths: Iterable[Path]) -> "DataModel": + dm = cls() + for path in paths: + if not path.exists(): + continue + headers = [path] if path.is_file() else sorted(path.rglob("*.h")) + for header in headers: + try: + src = strip_comments(header.read_text(errors="replace")) + except OSError: + continue + for name, origin, desc, ver in VERSIONED_RE.findall(src): + dm.declared[name] = f"{origin}/{desc}/v{ver}" + for name, origin, desc in PLAIN_TABLE_RE.findall(src): + dm.declared.setdefault(name, f"{origin}/{desc}") + for lhs, rhs in ALIAS_RE.findall(src): + rhs = rhs.strip() + if re.fullmatch(r"(?:o2::)?(?:aod::)?\w+", rhs): + dm.alias[lhs] = rhs.split("::")[-1] + # follow alias chains (A = B, B = B_002) + for name in list(dm.alias): + seen, cur = {name}, dm.alias[name] + while cur in dm.alias and cur not in seen: + seen.add(cur) + cur = dm.alias[cur] + dm.alias[name] = cur + return dm + + +# -------------------------------------------------------------------------- +# Parsed representation of one workflow file +# -------------------------------------------------------------------------- + +@dataclass +class Subscription: + """One argument of a process function: one or more joined tables.""" + + tables: List[str] + joined: bool = False + filtered: bool = False + iterator: bool = False + raw: str = "" + + +@dataclass +class ProcessFn: + name: str + subscriptions: List[Subscription] = field(default_factory=list) + description: str = "" + default_on: Optional[bool] = None # None: no PROCESS_SWITCH (always on) + + +@dataclass +class Task: + file: str + struct: str + comment: str = "" + outputs: List[str] = field(default_factory=list) + processes: List[ProcessFn] = field(default_factory=list) + warnings: List[str] = field(default_factory=list) + + @property + def inputs(self) -> List[str]: + seen, flat = set(), [] + for p in self.processes: + for s in p.subscriptions: + for t in s.tables: + if t not in seen: + seen.add(t) + flat.append(t) + return flat + + +STRUCT_RE = re.compile(r"^\s*struct\s+(\w+)\s*(?::[^{]*)?\{", re.M) +PRODUCES_RE = re.compile(r"\b(Produces|Spawns|Builds)\s*<\s*([^>]+?)\s*>") +PROCESS_RE = re.compile(r"\b(?:void|auto)\s+(process\w*)\s*\(") +SWITCH_RE = re.compile( + r"PROCESS_SWITCH\s*\(\s*(\w+)\s*,\s*(\w+)\s*,\s*\"([^\"]*)\"\s*,\s*(true|false)\s*\)" +) +LOCAL_ALIAS_RE = re.compile(r"^\s*using\s+(\w+)\s*=\s*([^;]+);", re.M) + + +def struct_body(src: str, start: int) -> str: + """Return the body of the struct whose opening brace follows `start`.""" + brace = src.find("{", start) + if brace == -1: + return "" + depth = 0 + for i in range(brace, len(src)): + if src[i] == "{": + depth += 1 + elif src[i] == "}": + depth -= 1 + if depth == 0: + return src[brace + 1:i] + return src[brace + 1:] + + +def leading_comment(raw: str, struct_pos: int) -> str: + """The // comment block immediately above a struct, as one line.""" + lines = raw[:struct_pos].splitlines() + picked: List[str] = [] + for line in reversed(lines): + s = line.strip() + if not s: + if picked: + break + continue + if s.startswith("//"): + body = s.lstrip("/").strip() + if body: + picked.append(body) + continue + break + return " ".join(reversed(picked)).strip() + + +def parse_subscription(arg: str, aliases: Dict[str, str]) -> Optional[Subscription]: + """Turn one process-function argument into a Subscription.""" + expr = arg + # expand file-scope using aliases, repeatedly (aliases may nest) + for _ in range(8): + expanded = re.sub( + r"\b(\w+)\b", + lambda m: aliases.get(m.group(1), m.group(1)), + expr, + ) + if expanded == expr: + break + expr = expanded + + tables = [] + for name in TABLE_RE.findall(expr): + if name not in tables: + tables.append(name) + if not tables: + return None + return Subscription( + tables=tables, + joined="Join" in expr and len(tables) > 1, + filtered="Filtered" in expr, + iterator="::iterator" in expr.replace(" ", ""), + raw=" ".join(arg.split()), + ) + + +def parse_file(path: Path) -> List[Task]: + raw = path.read_text(errors="replace") + src = strip_comments(raw) + + aliases: Dict[str, str] = {} + for lhs, rhs in LOCAL_ALIAS_RE.findall(src): + aliases[lhs] = rhs.strip() + + switches = {(s[0], s[1]): (s[2], s[3] == "true") for s in SWITCH_RE.findall(src)} + + tasks: List[Task] = [] + for m in STRUCT_RE.finditer(src): + struct = m.group(1) + body = struct_body(src, m.start()) + # the descriptive comment lives in the uncleaned source + raw_pos = raw.find(f"struct {struct}") + task = Task( + file=path.name, + struct=struct, + comment=leading_comment(raw, raw_pos) if raw_pos != -1 else "", + ) + + for kind, tmpl in PRODUCES_RE.findall(body): + name = tmpl.split("::")[-1].strip() + label = name if kind == "Produces" else f"{name} ({kind.lower()})" + if label not in task.outputs: + task.outputs.append(label) + + for pm in PROCESS_RE.finditer(body): + open_paren = body.index("(", pm.end() - 1) + close = match_paren(body, open_paren) + if close == -1: + task.warnings.append(f"unbalanced parentheses in {pm.group(1)}()") + continue + fn = ProcessFn(name=pm.group(1)) + for arg in split_top_level(body[open_paren + 1:close]): + sub = parse_subscription(arg, aliases) + if sub: + fn.subscriptions.append(sub) + elif arg.strip(): + task.warnings.append( + f"{fn.name}(): unrecognised argument '{' '.join(arg.split())}'" + ) + desc, default_on = switches.get((struct, fn.name), (None, None)) + fn.description = desc or "" + fn.default_on = default_on + task.processes.append(fn) + + if not task.outputs: + task.warnings.append("no Produces<> found") + if not task.processes: + task.warnings.append("no process() function found") + tasks.append(task) + return tasks + + +# -------------------------------------------------------------------------- +# Consistency checks on the sources themselves +# -------------------------------------------------------------------------- + +VERSION_SUFFIX_RE = re.compile(r"^(.*?)_(\d+)$") +COMMENT_RANGE_RE = re.compile( + r"(?:version\s+|from\s+|v)?(\d{1,3})\s*(?:to|->|-->|=>)\s*v?(\d{1,3})", re.I +) + + +def split_version(name: str) -> Tuple[str, Optional[int]]: + """'StraEvSels_006' -> ('StraEvSels', 6); 'V0Extras' -> ('V0Extras', None).""" + m = VERSION_SUFFIX_RE.match(name) + return (m.group(1), int(m.group(2))) if m else (name, None) + + +def family(task: "Task") -> str: + """Table family a converter belongs to, taken from its first output.""" + if task.outputs: + return split_version(task.outputs[0].split(" ")[0])[0] + return "other" + + +def lint_task(task: "Task", dm: DataModel) -> List[str]: + """Flag descriptive comments that disagree with the parsed code. + + Several converters in the repository carry copy-pasted comments (e.g. + 'Converts V0 version 001 to 002' on a task that converts StraRawCents + 003 to 004). These are harmless to the build but actively mislead the + reader, which is exactly who this summary is for. + """ + issues: List[str] = [] + if not task.comment: + return issues + + out_names = [o.split(" ")[0] for o in task.outputs] + in_names = [t for p in task.processes for s in p.subscriptions for t in s.tables] + out_ver = next((v for _, v in map(split_version, out_names) if v is not None), None) + in_ver = next((v for _, v in map(split_version, in_names) if v is not None), None) + + m = COMMENT_RANGE_RE.search(task.comment) + if m and in_ver is not None and out_ver is not None: + said = (int(m.group(1)), int(m.group(2))) + if said != (in_ver, out_ver): + issues.append( + f"comment says {said[0]} -> {said[1]} but the task converts " + f"{in_ver:03d} -> {out_ver:03d}" + ) + + # a table family named in the comment that the task never touches + mentioned = set(re.findall(r"\b([A-Z][A-Za-z0-9]{3,})\b", task.comment)) + touched = {split_version(n)[0] for n in in_names + out_names} + touched |= {n for n in in_names + out_names} + stray = {w for w in mentioned + if (w in dm.declared or w in dm.alias) + and split_version(w)[0] not in touched and w not in touched} + if stray: + issues.append("comment mentions " + ", ".join(sorted(stray)) + + " which the task does not use") + return issues + + +# -------------------------------------------------------------------------- +# Rendering +# -------------------------------------------------------------------------- + +def fmt_subscription(sub: Subscription, dm: DataModel, resolve: bool) -> str: + names = [] + for t in sub.tables: + target = dm.resolve(t) if resolve else None + names.append(f"`{t}`" + (f" *(={target})*" if target else "")) + text = " + ".join(names) + if sub.filtered: + text += " *(filtered)*" + return text + + +def fmt_inputs(task: Task, dm: DataModel, resolve: bool) -> str: + chunks = [] + multi = len(task.processes) > 1 + for p in task.processes: + if not p.subscriptions: + body = "*(none)*" + else: + body = ", ".join(fmt_subscription(s, dm, resolve) for s in p.subscriptions) + if multi: + flag = "" if p.default_on is None else ("" if p.default_on else " *(off by default)*") + chunks.append(f"**{p.name}**: {body}{flag}") + else: + chunks.append(body) + return "
".join(chunks) if chunks else "*(none)*" + + +def fmt_outputs(task: Task, dm: DataModel, resolve: bool) -> str: + if not task.outputs: + return "*(none)*" + out = [] + for o in task.outputs: + base = o.split(" ")[0] + target = dm.resolve(base) if resolve else None + out.append(f"`{o}`" + (f" *(={target})*" if target else "")) + return "
".join(out) + + +HEADER = """ + + + + +# Strangeness table converters + +Converter tasks migrate old versions of the strangeness derived-data tables to +newer ones, so that analyses written against the current data model can still +run over older derived data. Each task reads one or more legacy tables and +produces the corresponding newer version, filling any column that did not exist +in the old table with a dummy value. + +Add the workflow you need to your job with `o2-analysis-lf-`; only the +converters whose *input* tables are actually present in your derived data need +to be enabled. + +The table below is generated automatically from the sources. +""" + +FOOTER = """ +Notes: + +* Tables joined with `+` are subscribed together in a single argument + (`soa::Join<...>`); a comma separates independent arguments. +* `Name *(=Name_00N)*` means the task subscribes to (or produces) the + unversioned alias, which currently resolves to version `N` in the data model. +* Tasks with more than one `process` function list each one separately; + a switch marked *off by default* must be enabled explicitly. +""" + + +def _table(buf: io.StringIO, tasks: List[Task], dm: DataModel, + resolve: bool, show_comments: bool) -> None: + cols = ["Converter", "Reads", "Produces"] + if show_comments: + cols.append("Description") + buf.write("| " + " | ".join(cols) + " |\n") + buf.write("|" + "|".join(["---"] * len(cols)) + "|\n") + for t in sorted(tasks, key=lambda x: x.file): + row = [ + f"`{t.file}`", + fmt_inputs(t, dm, resolve), + fmt_outputs(t, dm, resolve), + ] + if show_comments: + row.append(t.comment or "") + buf.write("| " + " | ".join(c.replace("|", "\\|") for c in row) + " |\n") + + +def render_markdown(tasks: List[Task], dm: DataModel, resolve: bool, + show_comments: bool, with_header: bool, + group: bool = False) -> str: + buf = io.StringIO() + if with_header: + buf.write(HEADER) + buf.write("\n") + if not group: + _table(buf, tasks, dm, resolve, show_comments) + else: + groups: Dict[str, List[Task]] = {} + for t in tasks: + groups.setdefault(family(t), []).append(t) + for fam in sorted(groups): + buf.write(f"\n## {fam}\n\n") + _table(buf, groups[fam], dm, resolve, show_comments) + if with_header: + buf.write(FOOTER) + return buf.getvalue() + + +def render_json(tasks: List[Task], dm: DataModel) -> str: + payload = [] + for t in sorted(tasks, key=lambda x: x.file): + d = asdict(t) + d["inputs"] = t.inputs + d["resolved"] = {n: dm.resolve(n) for n in t.inputs + t.outputs if dm.resolve(n)} + payload.append(d) + return json.dumps(payload, indent=2) + + +def render_csv(tasks: List[Task]) -> str: + buf = io.StringIO() + w = csv.writer(buf) + w.writerow(["file", "struct", "process", "reads", "produces", "description"]) + for t in sorted(tasks, key=lambda x: x.file): + for p in t.processes or [ProcessFn(name="process")]: + reads = "; ".join(" + ".join(s.tables) for s in p.subscriptions) + w.writerow([t.file, t.struct, p.name, reads, + "; ".join(t.outputs), t.comment]) + return buf.getvalue() + + +# -------------------------------------------------------------------------- +# CLI +# -------------------------------------------------------------------------- + +# DEFAULT_DIR = "PWGLF/TableProducer/Strangeness/Converters" +DEFAULT_DIR = "." + + +def find_repo_root(start: Path) -> Optional[Path]: + for parent in [start.resolve()] + list(start.resolve().parents): + if (parent / ".git").exists() or (parent / "PWGLF").is_dir(): + return parent + return None + + +def main(argv: Optional[List[str]] = None) -> int: + ap = argparse.ArgumentParser( + description="Summarise the input/output tables of O2Physics converter tasks." + ) + ap.add_argument("directory", nargs="?", default=DEFAULT_DIR, + help=f"directory of .cxx workflows (default: {DEFAULT_DIR})") + ap.add_argument("-o", "--output", help="write to this file instead of stdout") + ap.add_argument("--format", choices=["markdown", "json", "csv"], default="markdown") + ap.add_argument("--datamodel", action="append", default=[], + help="extra header file or directory to scan for table " + "declarations and aliases (repeatable)") + ap.add_argument("--no-resolve", action="store_true", + help="do not annotate unversioned names with their current version") + ap.add_argument("--no-comments", action="store_true", + help="omit the Description column") + ap.add_argument("--group", action="store_true", + help="split the table into one section per table family") + ap.add_argument("--lint", action="store_true", + help="also report source comments that contradict the code") + ap.add_argument("--bare", action="store_true", + help="emit only the Markdown table, without the surrounding text") + ap.add_argument("--check", action="store_true", + help="exit 2 if --output exists and differs (for CI)") + ap.add_argument("--strict", action="store_true", + help="exit 1 if any file could not be fully parsed") + args = ap.parse_args(argv) + + directory = Path(args.directory) + if not directory.is_dir(): + print(f"error: {directory} is not a directory", file=sys.stderr) + return 1 + + sources = sorted(p for p in directory.glob("*.cxx")) + if not sources: + print(f"error: no .cxx files in {directory}", file=sys.stderr) + return 1 + + tasks: List[Task] = [] + for src in sources: + tasks.extend(parse_file(src)) + + dm_paths = [Path(p) for p in args.datamodel] + if not dm_paths: + root = find_repo_root(directory) + if root: + dm_paths = [root / "PWGLF" / "DataModel", root / "Common" / "DataModel"] + dm = DataModel.from_paths(dm_paths) + + if args.format == "json": + text = render_json(tasks, dm) + elif args.format == "csv": + text = render_csv(tasks) + else: + text = render_markdown(tasks, dm, not args.no_resolve, + not args.no_comments, not args.bare, + group=args.group) + + problems = [(t.file, w) for t in tasks for w in t.warnings] + if args.lint: + for t in tasks: + problems.extend((t.file, f"[lint] {i}") for i in lint_task(t, dm)) + for f, w in problems: + print(f"warning: {f}: {w}", file=sys.stderr) + + if args.output: + out = Path(args.output) + if args.check: + existing = out.read_text() if out.exists() else None + if existing != text: + print(f"{out} is out of date; regenerate with " + f"`{Path(sys.argv[0]).name} -o {out}`", file=sys.stderr) + return 2 + print(f"{out} is up to date ({len(tasks)} tasks).", file=sys.stderr) + return 0 + out.write_text(text) + print(f"wrote {out} ({len(tasks)} tasks, " + f"{len(sources)} files).", file=sys.stderr) + else: + sys.stdout.write(text) + + return 1 if (args.strict and problems) else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/PWGLF/TableProducer/Strangeness/Converters/stracentconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/stracentconverter.cxx index da4a817f834..b0da93f7686 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stracentconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stracentconverter.cxx @@ -24,6 +24,7 @@ struct stracentconverter { void process(aod::StraCents_000 const& straCents_000) { + straCents_001.reserve(straCents_000.size()); for (auto& values : straCents_000) { straCents_001(values.centFT0M(), values.centFT0A(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/stracentconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/stracentconverter2.cxx index a56d834c697..1d9fe4f3f36 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stracentconverter2.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stracentconverter2.cxx @@ -24,6 +24,7 @@ struct stracentconverter2 { void process(aod::StraCents_001 const& straCents_001) { + straCents_002.reserve(straCents_001.size()); for (auto& values : straCents_001) { straCents_002(values.centFT0M(), values.centFT0A(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautracksconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautracksconverter.cxx index 26a68b115f2..3ae50ec076a 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stradautracksconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautracksconverter.cxx @@ -35,6 +35,7 @@ struct stradautracksconverter { void process(soa::Join const& v0s, soa::Join const& cascs, aod::DauTrackExtras const& dauTracks) { // prepare arrays with the relevant information + dautracktofpids.reserve(dauTracks.size()); std::vector lLengths(dauTracks.size(), 1.e+6), lTOFSignals(dauTracks.size(), -1e+3f), lTOFEvTimes(dauTracks.size(), -1e+3f); for (const auto& v0 : v0s) { lLengths[v0.posTrackExtraId()] = v0.posTOFLengthToPV(); diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter.cxx index 6ec2cffbbc9..ebbeb37acd7 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter.cxx @@ -24,6 +24,7 @@ struct stradautracksextraconverter { void process(aod::DauTrackExtras_000 const& dauTrackExtras_000) { + dauTrackExtras_001.reserve(dauTrackExtras_000.size()); for (auto& values : dauTrackExtras_000) { dauTrackExtras_001(0, values.detectorMap(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter2.cxx index c6e373898d4..f22fea5ef47 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter2.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter2.cxx @@ -26,6 +26,7 @@ struct stradautracksextraconverter2 { void process(aod::DauTrackExtras_001 const& dauTrackExtras_001) { + dauTrackExtras_002.reserve(dauTrackExtras_001.size()); for (auto& values : dauTrackExtras_001) { const int maxFindable = 130; // synthetic findable to ensure range is ok int findableMinusFound = maxFindable - values.tpcClusters(); diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter3.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter3.cxx index 1697f64705e..432303ffd1d 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter3.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautracksextraconverter3.cxx @@ -24,6 +24,7 @@ struct stradautracksextraconverter3 { void process(aod::DauTrackExtras_002 const& dauTrackExtras_002) { + dauTrackExtras_003.reserve(dauTrackExtras_002.size()); for (auto& values : dauTrackExtras_002) { dauTrackExtras_003(values.itsChi2PerNcl(), -1 /* dummy tpcChi2PerNcl value */, diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter.cxx index 6c57959c7f4..185bf7017fc 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter.cxx @@ -8,6 +8,14 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// +/// \file stradautrackstofpidconverter.cxx +/// \brief Produces DauTrackTOFPIDs from V0TOFs and CascTOFs table +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// + #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" @@ -21,13 +29,13 @@ using namespace o2; using namespace o2::framework; -// Converts V0 version 001 to 002 struct stradautrackstofpidconverter { Produces dautracktofpids; void process(soa::Join const& v0s, soa::Join const& cascs, aod::DauTrackExtras const& dauTracks) { // prepare arrays with the relevant information + dautracktofpids.reserve(dauTracks.size()); std::vector lLengths(dauTracks.size(), 1.e+6), lTOFSignals(dauTracks.size(), -1e+3f), lTOFEvTimes(dauTracks.size(), -1e+3f); for (const auto& v0 : v0s) { lLengths[v0.posTrackExtraId()] = v0.posTOFLengthToPV(); @@ -49,7 +57,7 @@ struct stradautrackstofpidconverter { lTOFEvTimes[casc.bachTrackExtraId()] = casc.bachTOFEventTime(); } for (unsigned int ii = 0; ii < dauTracks.size(); ii++) { - dautracktofpids(-1, -1, lTOFSignals[ii], lTOFEvTimes[ii], lLengths[ii], 0.0f); + dautracktofpids(-1, -1, lTOFSignals[ii], lTOFEvTimes[ii], 999.0f /*dummy event time error for TOF*/, lLengths[ii], 0.0f); } } }; diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter2.cxx index 875450e6f67..5c1b9b508c2 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter2.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter2.cxx @@ -8,6 +8,14 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// +/// \file stradautrackstofpidconverter2.cxx +/// \brief Converts DauTrackTOFPIDs_000 into DauTrackTOFPID_001 and StraEvTimes_000 +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// + #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" @@ -24,11 +32,12 @@ using namespace o2::framework; // converts DauTrackTOFPIDs_000 to _001 struct stradautrackstofpidconverter2 { Produces dautracktofpids; - Produces straEvTimes; + Produces straEvTimes; void process(aod::StraCollisions const& collisions, soa::Join const& dauTracks, soa::Join const& v0s) { // create new TOFPIDs + dautracktofpids.reserve(dauTracks.size()); for (int ii = 0; ii < dauTracks.size(); ii++) { auto dauTrack = dauTracks.rawIteratorAt(ii); dautracktofpids(-1, -1, dauTrack.tofSignal(), dauTrack.tofEvTime(), dauTrack.length(), 0.0f); @@ -51,6 +60,7 @@ struct stradautrackstofpidconverter2 { collisionNtracks[v0.straCollisionId()]++; } } + straEvTimes.reserve(collisions.size()); for (const auto& collision : collisions) { if (collisionNtracks[collision.globalIndex()] > 0) { collisionEventTime[collision.globalIndex()] /= static_cast(collisionNtracks[collision.globalIndex()]); diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter3.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter3.cxx new file mode 100644 index 00000000000..0a5a09ba7c7 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter3.cxx @@ -0,0 +1,59 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file stradautrackstofpidconverter3.cxx +/// \brief Converts DauTrackTOFPID_001 and StraEvTimes_000 into DauTrackTOFPID_002 and StraEvTimes_001 +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Romain Schotter , Austrian Academy of Sciences & MBI + +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// converts DauTrackTOFPIDs_001 to _002 +struct stradautrackstofpidconverter3 { + Produces dautracktofpids; + Produces straEvTimes; + + void process(aod::DauTrackTOFPIDs_001 const& dauTracks, aod::StraEvTimes_000 const& straEvTimes_000) + { + // create new TOFPIDs + dautracktofpids.reserve(dauTracks.size()); + for (const auto& dauTrack : dauTracks) { + dautracktofpids( + dauTrack.straCollisionId(), + dauTrack.dauTrackExtraId(), + dauTrack.tofSignal(), + dauTrack.tofEvTime(), + 999.0f, /*dummy event time error for TOF*/ + dauTrack.length(), + dauTrack.tofExpMom()); + } + straEvTimes.reserve(straEvTimes_000.size()); + for (const auto& value : straEvTimes_000) { + straEvTimes(value.eventTime(), 999.0f /*dummy event time error for TOF*/); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter4.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter4.cxx new file mode 100644 index 00000000000..e8f0d23ed0d --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstofpidconverter4.cxx @@ -0,0 +1,61 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file stradautrackstofpidconverter4.cxx +/// \brief Converts DauTrackTOFPID_002 into DauTrackTOFPID_003 +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Romain Schotter , Austrian Academy of Sciences & MBI + +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; + +// converts DauTrackTOFPIDs_002 to _003 +struct stradautrackstofpidconverter4 { + Produces dautracktofpids; + + void process(aod::DauTrackTOFPIDs_002 const& dauTracks, soa::Join const& collisions) + { + // create new TOFPIDs + dautracktofpids.reserve(dauTracks.size()); + for (const auto& dauTrack : dauTracks) { + uint64_t bc = 0; + if (dauTrack.straCollisionId() >= 0) { + auto collision = collisions.rawIteratorAt(dauTrack.straCollisionId()); + bc = collision.globalBC(); + } + dautracktofpids( + bc, + dauTrack.dauTrackExtraId(), + dauTrack.tofSignal(), + dauTrack.tofEvTime(), + dauTrack.tofEvTimeErr(), + dauTrack.length(), + dauTrack.tofExpMom()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstpcpidconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstpcpidconverter.cxx index bba6043dad4..8aef7eacd92 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stradautrackstpcpidconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stradautrackstpcpidconverter.cxx @@ -24,6 +24,7 @@ struct stradautrackstpcpidconverter { void process(aod::DauTrackTPCPIDs_000 const& v000s) { + dautrackpcpids.reserve(v000s.size()); for (int ii = 0; ii < v000s.size(); ii++) { auto dauTrackTPCPID = v000s.rawIteratorAt(ii); dautrackpcpids(dauTrackTPCPID.tpcSignal(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselextrasconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselextrasconverter.cxx new file mode 100644 index 00000000000..2ae89c97d84 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselextrasconverter.cxx @@ -0,0 +1,120 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file straevselextrasconverter.cxx +/// \brief Converts straevselsextrasconverter1 converts StraEvSelExtras_000 into StraEvSelExtras_001 +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// +//__________________________________________________ +// + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::evsel; + +struct straevselextrasconverter { + Produces straEvSelExtras_001; + + void init(InitContext&) + { + LOGF(info, "Initializing now: cross-checking correctness..."); + if (doprocessAll + doprocessStraEvSelsOnly > 1) { + LOGF(fatal, "You have enabled more than one process function. Please check your configuration! Aborting now."); + } + } + + void processAll(soa::Join const& straEvSels_005) + { + straEvSelExtras_001.reserve(straEvSels_005.size()); + for (const auto& values : straEvSels_005) { + straEvSelExtras_001(values.multZNA(), + values.multZNC(), + values.multZEM1(), + values.multZEM2(), + values.multZPA(), + values.multZPC(), + values.multNTracksITSTPC(), + values.multAllTracksTPCOnly(), + values.multAllTracksITSTPC(), + values.trackOccupancyInTimeRange(), + values.ft0cOccupancyInTimeRange(), + values.timeFDDA(), + values.timeFDDC(), + values.timeFV0A(), + values.timeFT0A(), + values.timeFT0C(), + values.triggerMaskFT0(), + values.gapSide(), + values.totalFT0AmplitudeA(), + values.totalFT0AmplitudeC(), + values.totalFV0AmplitudeA(), + values.totalFDDAmplitudeA(), + values.totalFDDAmplitudeC(), + values.timeZNA(), + values.timeZNC(), + values.energyCommonZNA(), + values.energyCommonZNC()); + } + } + + void processStraEvSelsOnly(aod::StraEvSels_005 const& straEvSels_005) + { + straEvSelExtras_001.reserve(straEvSels_005.size()); + for (const auto& values : straEvSels_005) { + straEvSelExtras_001(values.multZNA(), + values.multZNC(), + values.multZEM1(), + values.multZEM2(), + values.multZPA(), + values.multZPC(), + values.multNTracksITSTPC(), + values.multAllTracksTPCOnly(), + values.multAllTracksITSTPC(), + values.trackOccupancyInTimeRange(), + values.ft0cOccupancyInTimeRange(), + -999., // dummy timeFDDA, + -999., // dummy timeFDDC, + -999., // dummy timeFV0A, + -999., // dummy timeFT0A, + -999., // dummy timeFT0C, + 0, // dummy triggerMaskFT0, + values.gapSide(), + values.totalFT0AmplitudeA(), + values.totalFT0AmplitudeC(), + values.totalFV0AmplitudeA(), + values.totalFDDAmplitudeA(), + values.totalFDDAmplitudeC(), + -999., // dummy timeZNA, + -999., // dummy timeZNC, + values.energyCommonZNA(), + values.energyCommonZNC()); + } + } + PROCESS_SWITCH(straevselextrasconverter, processAll, "Store StraEvSels005 and StraEvSelExtras000 into StraEvSelExtras001", true); + PROCESS_SWITCH(straevselextrasconverter, processStraEvSelsOnly, "Store only StraEvSels005 into StraEvSelExtras001. Other columns are set to dummy values", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselextrasconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselextrasconverter2.cxx new file mode 100644 index 00000000000..b24fdae1a5a --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselextrasconverter2.cxx @@ -0,0 +1,64 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::evsel; + +// Produce dummy StraEvSelExtras for analysis subscribing to StraEvSelExtras but not saved in the strangeness derived data (typically when running over pp strangeness derived data) +struct straevselextrasconverter2 { + Produces straEvSelExtras; + + void process(aod::StraEvSels const& straEvSels) + { + for (int ii = 0; ii < straEvSels.size(); ii++) { + straEvSelExtras(-999., // dummy multZNA, + -999., // dummy multZNC, + -999., // dummy multZEM1, + -999., // dummy multZEM2, + -999., // dummy multZPA, + -999., // dummy multZPC, + -999., // dummy multNTracksITSTPC, + -999., // dummy multAllTracksTPCOnly, + -999., // dummy multAllTracksITSTPC, + -999., // dummy trackOccupancyInTimeRange, + -999., // dummy ft0cOccupancyInTimeRange, + -999., // dummy timeFDDA, + -999., // dummy timeFDDC, + -999., // dummy timeFV0A, + -999., // dummy timeFT0A, + -999., // dummy timeFT0C, + 0, // dummy triggerMaskFT0, + -999, // dummy gapSide, + -999., // dummy totalFT0AmplitudeA, + -999., // dummy totalFT0AmplitudeC, + -999., // dummy totalFV0AmplitudeA, + -999., // dummy totalFDDAmplitudeA, + -999., // dummy totalFDDAmplitudeC, + -999., // dummy timeZNA, + -999., // dummy timeZNC, + -999., // dummy energyCommonZNA, + -999.); // dummy energyCommonZNC); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter.cxx index bdcbc4b5f68..c75002e9280 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter.cxx @@ -24,6 +24,7 @@ struct straevselsconverter { void process(soa::Join const& straEvSels_000_RawCents_004) { + straEvSels_001.reserve(straEvSels_000_RawCents_004.size()); for (auto& values : straEvSels_000_RawCents_004) { straEvSels_001(values.sel8(), values.selection_raw(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2.cxx index 147a3c986fa..5c6293f07ab 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2.cxx @@ -25,6 +25,7 @@ struct straevselsconverter2 { void process(aod::StraEvSels_001 const& straEvSels_001) { + straEvSels_002.reserve(straEvSels_001.size()); for (auto& values : straEvSels_001) { straEvSels_002(values.sel8(), values.selection_raw(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents.cxx index 5a706d83cc6..42d9eedc133 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents.cxx @@ -24,6 +24,7 @@ struct straevselsconverter2rawcents { void process(aod::StraEvSels_001 const& straEvSels_001) { + straRawCents_004.reserve(straEvSels_001.size()); for (auto& values : straEvSels_001) { straRawCents_004(values.multFT0A(), values.multFT0C(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents2.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents2.cxx index edccbbfb5fe..7cd834daeba 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents2.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents2.cxx @@ -24,6 +24,7 @@ struct straevselsconverter2rawcents2 { void process(aod::StraEvSels_002 const& straEvSels_002) { + straRawCents_004.reserve(straEvSels_002.size()); for (auto& values : straEvSels_002) { straRawCents_004(values.multFT0A(), values.multFT0C(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents3.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents3.cxx index 157b40ffd24..a3419abeee7 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents3.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter2rawcents3.cxx @@ -24,6 +24,7 @@ struct straevselsconverter2rawcents3 { void process(aod::StraEvSels_003 const& straEvSels_003) { + straRawCents_004.reserve(straEvSels_003.size()); for (auto& values : straEvSels_003) { straRawCents_004(values.multFT0A(), values.multFT0C(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter3.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter3.cxx index e4ac27b2d59..9afd1c5e555 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter3.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter3.cxx @@ -25,6 +25,7 @@ struct straevselsconverter3 { void process(aod::StraEvSels_002 const& straEvSels_002) { + straEvSels_003.reserve(straEvSels_002.size()); for (auto& values : straEvSels_002) { straEvSels_003(values.sel8(), values.selection_raw(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter4.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter4.cxx index 7d092e76a65..a99fe4fc009 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter4.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter4.cxx @@ -24,6 +24,7 @@ struct straevselsconverter4 { void process(aod::StraEvSels_003 const& straEvSels_003) { + straEvSels_004.reserve(straEvSels_003.size()); for (auto& values : straEvSels_003) { straEvSels_004(values.sel8(), values.selection_raw(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter5.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter5.cxx index 17dc9544b14..d044b1fe842 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter5.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter5.cxx @@ -90,6 +90,7 @@ struct straevselsconverter5 { void process(soa::Join const& straEvSels_004) { + straEvSels_005.reserve(straEvSels_004.size()); for (auto& values : straEvSels_004) { straEvSels_005(values.sel8(), values.selection_raw(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter6.cxx b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter6.cxx new file mode 100644 index 00000000000..5b8c29ad1b3 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/straevselsconverter6.cxx @@ -0,0 +1,51 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::evsel; + +// Converts Stra Event selections from 005 to 006 +struct straevselsconverter6 { + Produces straEvSels_006; + + void process(aod::StraEvSels_005 const& straEvSels_005) + { + for (auto& values : straEvSels_005) { + straEvSels_006(values.sel8(), + values.selection_raw(), + values.multFT0A(), + values.multFT0C(), + values.multFT0A(), + values.multFDDA(), + values.multFDDC(), + values.multNTracksPVeta1(), + values.multPVTotalContributors(), + values.multNTracksGlobal(), + values.flags(), + values.alias_raw(), + values.rct_raw()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter.cxx index 8f35332bc8c..19d905712c5 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter.cxx @@ -24,6 +24,7 @@ struct stramccollisionconverter { void process(aod::StraMCCollisions_000 const& straMCcoll) { + straMCCollisions_001.reserve(straMCcoll.size()); for (auto& mccollision : straMCcoll) { straMCCollisions_001(mccollision.posX(), mccollision.posY(), mccollision.posZ(), mccollision.impactParameter(), 0.0f); diff --git a/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter2.cxx index c9c27af9a2b..fcb260477eb 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter2.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stramccollisionconverter2.cxx @@ -32,6 +32,7 @@ struct stramccollisionconverter2 { void process(aod::StraMCCollisions_001 const& straMCcoll) { + straMCCollisions_002.reserve(straMCcoll.size()); for (auto& mccollision : straMCcoll) { straMCCollisions_002(mccollision.posX(), mccollision.posY(), mccollision.posZ(), mccollision.impactParameter(), mccollision.eventPlaneAngle(), 0); diff --git a/PWGLF/TableProducer/Strangeness/Converters/stramccollmultconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/stramccollmultconverter.cxx index fbf6047b9fc..9566629cbcd 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/stramccollmultconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/stramccollmultconverter.cxx @@ -24,6 +24,7 @@ struct stramccollmultconverter { void process(aod::StraMCCollMults_000 const& straMCcolls) { + straMCCollMults_001.reserve(straMCcolls.size()); for (auto& straMCcoll : straMCcolls) { straMCCollMults_001(straMCcoll.multMCFT0A(), straMCcoll.multMCFT0C(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/stramccollmultconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/stramccollmultconverter2.cxx new file mode 100644 index 00000000000..cc56470aacd --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/Converters/stramccollmultconverter2.cxx @@ -0,0 +1,46 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +// Converts V0 version 001 to 002 +struct stramccollmultconverter2 { + Produces straMCCollMults_002; + + void process(aod::StraMCCollMults_001 const& straMCcolls) + { + straMCCollMults_002.reserve(straMCcolls.size()); + for (auto& straMCcoll : straMCcolls) { + straMCCollMults_002(straMCcoll.multMCFT0A(), + straMCcoll.multMCFT0C(), + -1., // dummy value multMCFV0A + -1., // dummy value multMCFDDA + -1., // dummy value multMCFDDC + straMCcoll.multMCNParticlesEta05(), + straMCcoll.multMCNParticlesEta08(), + straMCcoll.multMCNParticlesEta10(), + straMCcoll.totalMultMCParticles()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter.cxx index ad3f7d7a290..3eeb2d190cb 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter.cxx @@ -26,12 +26,14 @@ struct strarawcentsconverter { void process000to001(aod::StraRawCents_000 const& straRawCents_000) { + straRawCents_001.reserve(straRawCents_000.size()); for (auto& values : straRawCents_000) { straRawCents_001(values.multFT0A(), values.multFT0C(), values.multFV0A(), values.multNTracksPVeta1(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); } } void process002to003(aod::StraRawCents_002 const& straRawCents_002) { + straRawCents_003.reserve(straRawCents_002.size()); for (auto& values : straRawCents_002) { straRawCents_003(values.multFT0A(), values.multFT0C(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter2v4.cxx b/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter2v4.cxx index 656102b5fe4..38cf47b6e31 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter2v4.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/strarawcentsconverter2v4.cxx @@ -24,6 +24,7 @@ struct strarawcentsconverter2v4 { void process(aod::StraRawCents_003 const& straRawCents_003) { + straRawCents_004.reserve(straRawCents_003.size()); for (auto& values : straRawCents_003) { straRawCents_004(values.multFT0A(), values.multFT0C(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/strastampsconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/strastampsconverter.cxx index a99f769250f..49f739420b0 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/strastampsconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/strastampsconverter.cxx @@ -24,6 +24,7 @@ struct strastampsconverter { void process(aod::StraStamps_000 const& straStamps_000) { + straStamps_001.reserve(straStamps_000.size()); for (auto& values : straStamps_000) { straStamps_001(values.runNumber(), values.timestamp(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter.cxx index 78f09ec339a..1e3c6c46609 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter.cxx @@ -24,6 +24,7 @@ struct v0coresconverter { void process(aod::V0MCCores_000 const& v0MCCores_000) { + v0MCCores_001.reserve(v0MCCores_000.size()); for (auto& values : v0MCCores_000) { v0MCCores_001(0, values.pdgCode(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter2.cxx b/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter2.cxx index 3e0570c78bd..eb748858498 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter2.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/v0coresconverter2.cxx @@ -24,6 +24,7 @@ struct v0coresconverter2 { void process(aod::V0MCCores_001 const& v0MCCores_001) { + v0MCCores_002.reserve(v0MCCores_001.size()); for (auto& values : v0MCCores_001) { v0MCCores_002(0, values.pdgCode(), diff --git a/PWGLF/TableProducer/Strangeness/Converters/v0mlscoresconverter.cxx b/PWGLF/TableProducer/Strangeness/Converters/v0mlscoresconverter.cxx index c4ca9d75584..156c420b8a8 100644 --- a/PWGLF/TableProducer/Strangeness/Converters/v0mlscoresconverter.cxx +++ b/PWGLF/TableProducer/Strangeness/Converters/v0mlscoresconverter.cxx @@ -29,6 +29,10 @@ struct v0mlscoresconverter { void process(aod::V0Cores const& v0cores) { + gammaMLSelections.reserve(v0cores.size()); + lambdaMLSelections.reserve(v0cores.size()); + antiLambdaMLSelections.reserve(v0cores.size()); + k0ShortMLSelections.reserve(v0cores.size()); for (int64_t i = 0; i < v0cores.size(); ++i) { gammaMLSelections(-1); lambdaMLSelections(-1); diff --git a/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx index a97f2dad659..7fe676a81e0 100644 --- a/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx +++ b/PWGLF/TableProducer/Strangeness/cascadeMLSelectionTreeCreator.cxx @@ -139,13 +139,15 @@ struct cascadeMLSelectionTreeCreator { float dcav0topv; float v0CosPA; float cascCosPA; + float bachBaryonCosPA; + float bachBaryonDCAxyToPV; // reserved for MC operation bool isXiMinus; bool isXiPlus; bool isOmegaMinus; bool isOmegaPlus; - } cascadeCandidate; + } cascadeCandidate{}; // Process candidate and store properties in object template @@ -161,10 +163,10 @@ struct cascadeMLSelectionTreeCreator { // Track quality cascadeCandidate.posITSCls = posTrackExtra.itsNCls(); cascadeCandidate.negITSCls = negTrackExtra.itsNCls(); - cascadeCandidate.bachITSCls = negTrackExtra.itsNCls(); + cascadeCandidate.bachITSCls = bachTrackExtra.itsNCls(); cascadeCandidate.posITSClsSizes = posTrackExtra.itsClusterSizes(); cascadeCandidate.negITSClsSizes = negTrackExtra.itsClusterSizes(); - cascadeCandidate.bachITSClsSizes = negTrackExtra.itsClusterSizes(); + cascadeCandidate.bachITSClsSizes = bachTrackExtra.itsClusterSizes(); cascadeCandidate.posTPCRows = posTrackExtra.tpcCrossedRows(); cascadeCandidate.negTPCRows = negTrackExtra.tpcCrossedRows(); cascadeCandidate.bachTPCRows = bachTrackExtra.tpcCrossedRows(); @@ -207,6 +209,8 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.dcav0topv = cand.dcav0topv(coll.posX(), coll.posY(), coll.posZ()); cascadeCandidate.v0CosPA = cand.v0cosPA(coll.posX(), coll.posY(), coll.posZ()); cascadeCandidate.cascCosPA = cand.casccosPA(coll.posX(), coll.posY(), coll.posZ()); + cascadeCandidate.bachBaryonCosPA = cand.bachBaryonCosPA(); + cascadeCandidate.bachBaryonDCAxyToPV = cand.bachBaryonDCAxyToPV(); // MC flags cascadeCandidate.isXiMinus = false; @@ -214,36 +218,50 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.isOmegaMinus = false; cascadeCandidate.isOmegaPlus = false; - if constexpr (requires { cand.pdgCode(); }) { - cascadeCandidate.isXiMinus = (cand.pdgCode() == 3312); - cascadeCandidate.isXiPlus = (cand.pdgCode() == -3312); - cascadeCandidate.isOmegaMinus = (cand.pdgCode() == 3334); - cascadeCandidate.isOmegaPlus = (cand.pdgCode() == -3334); + if constexpr (requires { cand.has_cascMCCore(); }) { + if (cand.has_cascMCCore()) { + auto cascMC = cand.template cascMCCore_as>(); + cascadeCandidate.isXiMinus = (cascMC.pdgCode() == 3312); + cascadeCandidate.isXiPlus = (cascMC.pdgCode() == -3312); + cascadeCandidate.isOmegaMinus = (cascMC.pdgCode() == 3334); + cascadeCandidate.isOmegaPlus = (cascMC.pdgCode() == -3334); + } } // base topological selections - if (cascadeCandidate.v0CosPA < v0setting_cospa) + if (cascadeCandidate.v0CosPA < v0setting_cospa) { return; - if (cascadeCandidate.dcaV0Daughters > v0setting_dcav0dau) + } + if (cascadeCandidate.dcaV0Daughters > v0setting_dcav0dau) { return; - if (cascadeCandidate.dcapostopv < v0setting_dcapostopv) + } + if (std::abs(cascadeCandidate.dcapostopv) < v0setting_dcapostopv) { return; - if (cascadeCandidate.dcapostopv < v0setting_dcanegtopv) + } + if (std::abs(cascadeCandidate.dcanegtopv) < v0setting_dcanegtopv) { return; - if (cascadeCandidate.dcabachtopv < cascadesetting_dcabachtopv) + } + if (std::abs(cascadeCandidate.dcabachtopv) < cascadesetting_dcabachtopv) { return; - if (cascadeCandidate.v0radius < v0setting_radius) + } + if (cascadeCandidate.v0radius < v0setting_radius) { return; - if (cascadeCandidate.cascCosPA < cascadesetting_cospa) + } + if (cascadeCandidate.cascCosPA < cascadesetting_cospa) { return; - if (cascadeCandidate.dcaCascDaughters > cascadesetting_dcacascdau) + } + if (cascadeCandidate.dcaCascDaughters > cascadesetting_dcacascdau) { return; - if (cascadeCandidate.cascradius < cascadesetting_cascradius) + } + if (cascadeCandidate.cascradius < cascadesetting_cascradius) { return; - if (std::abs(cascadeCandidate.mLambdaDaughter - 1.116) > cascadesetting_v0masswindow) + } + if (std::abs(cascadeCandidate.mLambdaDaughter - 1.116) > cascadesetting_v0masswindow) { return; - if (cascadeCandidate.dcav0topv < cascadesetting_mindcav0topv) + } + if (cascadeCandidate.dcav0topv < cascadesetting_mindcav0topv) { return; + } // Mass window selections cascadeCandidate.massWindows = 0; @@ -312,6 +330,8 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.dcav0topv, TMath::ACos(cascadeCandidate.v0CosPA), TMath::ACos(cascadeCandidate.cascCosPA), + cascadeCandidate.bachBaryonCosPA, + cascadeCandidate.bachBaryonDCAxyToPV, // MC identity cascadeCandidate.isXiMinus, @@ -320,17 +340,17 @@ struct cascadeMLSelectionTreeCreator { cascadeCandidate.isOmegaPlus); } - void processRealData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) + void processRealData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) { histos.fill(HIST("hEventCentrality"), coll.centFT0C()); - for (auto& casc : cascades) { // looping over lambdas + for (const auto& casc : cascades) { // looping over cascades processCandidate(coll, casc); } } - void processSimData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&) + void processSimData(soa::Join::iterator const& coll, soa::Join const& cascades, dauTracks const&, soa::Join const&) { histos.fill(HIST("hEventCentrality"), coll.centFT0C()); - for (auto& casc : cascades) { // looping over lambdas + for (const auto& casc : cascades) { // looping over cascades processCandidate(coll, casc); } } diff --git a/PWGLF/TableProducer/Strangeness/cascadeflow.cxx b/PWGLF/TableProducer/Strangeness/cascadeflow.cxx index d6e51874fb8..92472e75394 100644 --- a/PWGLF/TableProducer/Strangeness/cascadeflow.cxx +++ b/PWGLF/TableProducer/Strangeness/cascadeflow.cxx @@ -67,11 +67,11 @@ using namespace o2::framework::expressions; using std::array; using DauTracks = soa::Join; -using CollEventPlane = soa::Join::iterator; -using CollEventPlaneCentralFW = soa::Join::iterator; -using CollEventPlaneCentralFWOnlyFT0C = soa::Join::iterator; -using CollEventAndSpecPlane = soa::Join::iterator; -using CollEventAndSpecPlaneCentralFW = soa::Join::iterator; +using CollEventPlane = soa::Join::iterator; +using CollEventPlaneCentralFW = soa::Join::iterator; +using CollEventPlaneCentralFWOnlyFT0C = soa::Join::iterator; +using CollEventAndSpecPlane = soa::Join::iterator; +using CollEventAndSpecPlaneCentralFW = soa::Join::iterator; using MCCollisionsStra = soa::Join; using V0Candidates = soa::Join; using V0MCCandidates = soa::Join; @@ -308,6 +308,8 @@ struct cascadeFlow { Configurable isStoreTrueCascOnly{"isStoreTrueCascOnly", 1, ""}; Configurable etaCascMCGen{"etaCascMCGen", 0.8, "etaCascMCGen"}; Configurable yCascMCGen{"yCascMCGen", 0.5, "yCascMCGen"}; + Configurable etaLambdaFromXiMCGen{"etaLambdaFromXiMCGen", 0.8, "etaLambdaFromXiMCGen"}; + Configurable yXiToLambdaMCGen{"yXiToLambdaMCGen", 1.0, "yXiToLambdaMCGen"}; struct : ConfigurableGroup { Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; @@ -417,18 +419,9 @@ struct cascadeFlow { return false; } - if (isFillHisto) + if (isFillHisto) { histos.fill(HIST("hNEvents"), 7.5); - - // TVX in TRD - // if (isNoTVXinTRD && collision.alias_bit(kTVXinTRD)){ - // return false; - // } - - if (isFillHisto) histos.fill(HIST("hNEvents"), 8.5); - - if (isFillHisto) { histos.fill(HIST("hEventNchCorrelation"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); histos.fill(HIST("hEventPVcontributorsVsCentrality"), collision.centFT0C(), collision.multNTracksPVeta1()); histos.fill(HIST("hEventGlobalTracksVsCentrality"), collision.centFT0C(), collision.multNTracksGlobal()); @@ -438,7 +431,7 @@ struct cascadeFlow { } template - bool IsCascAccepted(TCascade casc, TDaughter negExtra, TDaughter posExtra, TDaughter bachExtra, int& counter) // loose cuts on topological selections of cascades + bool IsCascAccepted(const TCascade& casc, const TDaughter& negExtra, const TDaughter& posExtra, const TDaughter& bachExtra, int& counter) // loose cuts on topological selections of cascades { // TPC cuts as those implemented for the training of the signal if (doNTPCSigmaCut) { @@ -476,7 +469,7 @@ struct cascadeFlow { } template - bool isLambdaAccepted(TDaughter negExtra, TDaughter posExtra, int& counter) // loose cuts on topological selections of v0s + bool isLambdaAccepted(const TDaughter& negExtra, const TDaughter& posExtra, int& counter) // loose cuts on topological selections of v0s { // TPC cuts as those implemented for the training of the signal if (doNTPCSigmaCut) { @@ -492,7 +485,7 @@ struct cascadeFlow { return true; } template - bool isAntiLambdaAccepted(TDaughter negExtra, TDaughter posExtra, int& counter) // loose cuts on topological selections of v0s + bool isAntiLambdaAccepted(const TDaughter& negExtra, const TDaughter& posExtra, int& counter) // loose cuts on topological selections of v0s { // TPC cuts as those implemented for the training of the signal if (doNTPCSigmaCut) { @@ -509,7 +502,7 @@ struct cascadeFlow { } template - bool isV0TopoAccepted(TV0 v0) + bool isV0TopoAccepted(const TV0& v0) { // topological selections if (v0.v0radius() < V0Configs.v0radius) @@ -568,13 +561,11 @@ struct cascadeFlow { int currentRunNumber = -999; int lastRunNumber = -999; - TProfile3D* shiftprofile; - TProfile3D* shiftprofileFT0C; - TProfile3D* shiftprofileFV0A; - TProfile3D* shiftprofileFT0A; - TProfile3D* shiftprofileTPCL; - TProfile3D* shiftprofileTPCR; - std::string fullCCDBShiftCorrPath; + TProfile3D* shiftprofileFT0C = nullptr; + TProfile3D* shiftprofileFV0A = nullptr; + TProfile3D* shiftprofileFT0A = nullptr; + TProfile3D* shiftprofileTPCL = nullptr; + TProfile3D* shiftprofileTPCR = nullptr; std::string fullCCDBShiftCorrPathFT0C; std::string fullCCDBShiftCorrPathFV0A; std::string fullCCDBShiftCorrPathFT0A; @@ -582,7 +573,7 @@ struct cascadeFlow { std::string fullCCDBShiftCorrPathTPCR; template - double ApplyShiftCorrection(TCollision coll, double psiT0C, TProfile3D* shiftprofile) + double ApplyShiftCorrection(const TCollision& coll, double psiT0C, TProfile3D* shiftprofile) { auto deltapsiFT0C = 0.0; int nmode = 2; @@ -597,8 +588,11 @@ struct cascadeFlow { } template - double ComputeEPResolutionwShifts(TCollision coll, double psiT0C, double psiV0A, double psiT0A, double psiTPCA, double psiTPCC, TProfile3D* shiftprofileA, TProfile3D* shiftprofileB, TProfile3D* shiftprofileC, TProfile3D* shiftprofileD, TProfile3D* shiftprofileE) + double ComputeEPResolutionwShifts(const TCollision& coll, double psiT0C, double psiV0A, double psiT0A, double psiTPCA, double psiTPCC, TProfile3D* shiftprofileA, TProfile3D* shiftprofileB, TProfile3D* shiftprofileC, TProfile3D* shiftprofileD, TProfile3D* shiftprofileE) { + float collcentrality = coll.centFT0C(); + if (isCollisionCentrality == 1) + collcentrality = coll.centFT0M(); int nmode = 2; auto deltapsiFT0C = 0.0; auto deltapsiFV0A = 0.0; @@ -606,50 +600,50 @@ struct cascadeFlow { auto deltapsiTPCA = 0.0; auto deltapsiTPCC = 0.0; for (int ishift = 1; ishift <= 10; ishift++) { - auto coeffshiftxFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); - auto coeffshiftxFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(coll.centFT0C(), 0.5, ishift - 0.5)); - auto coeffshiftyFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(coll.centFT0C(), 1.5, ishift - 0.5)); + auto coeffshiftxFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFT0C = shiftprofileA->GetBinContent(shiftprofileA->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyTPCA = shiftprofileB->GetBinContent(shiftprofileB->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyTPCC = shiftprofileC->GetBinContent(shiftprofileC->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFV0A = shiftprofileD->GetBinContent(shiftprofileD->FindBin(collcentrality, 1.5, ishift - 0.5)); + auto coeffshiftxFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(collcentrality, 0.5, ishift - 0.5)); + auto coeffshiftyFT0A = shiftprofileE->GetBinContent(shiftprofileE->FindBin(collcentrality, 1.5, ishift - 0.5)); deltapsiFT0C += ((1 / (1.0 * ishift)) * (-coeffshiftxFT0C * std::cos(ishift * static_cast(nmode) * psiT0C) + coeffshiftyFT0C * TMath::Sin(ishift * static_cast(nmode) * psiT0C))); deltapsiFV0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFV0A * std::cos(ishift * static_cast(nmode) * psiV0A) + coeffshiftyFV0A * TMath::Sin(ishift * static_cast(nmode) * psiV0A))); deltapsiFT0A += ((1 / (1.0 * ishift)) * (-coeffshiftxFT0A * std::cos(ishift * static_cast(nmode) * psiT0A) + coeffshiftyFT0A * TMath::Sin(ishift * static_cast(nmode) * psiT0A))); deltapsiTPCA += ((1 / (1.0 * ishift)) * (-coeffshiftxTPCA * std::cos(ishift * static_cast(nmode) * psiTPCA) + coeffshiftyTPCA * TMath::Sin(ishift * static_cast(nmode) * psiTPCA))); deltapsiTPCC += ((1 / (1.0 * ishift)) * (-coeffshiftxTPCC * std::cos(ishift * static_cast(nmode) * psiTPCC) + coeffshiftyTPCC * TMath::Sin(ishift * static_cast(nmode) * psiTPCC))); } - histos.fill(HIST("Psi_EP_FT0C_shifted"), coll.centFT0C(), psiT0C + deltapsiFT0C); - histos.fill(HIST("Psi_EP_FV0A_shifted"), coll.centFT0C(), psiV0A + deltapsiFV0A); - histos.fill(HIST("Psi_EP_FT0A_shifted"), coll.centFT0C(), psiT0A + deltapsiFT0A); - histos.fill(HIST("Psi_EP_TPCA_shifted"), coll.centFT0C(), psiTPCA + deltapsiTPCA); - histos.fill(HIST("Psi_EP_TPCC_shifted"), coll.centFT0C(), psiTPCC + deltapsiTPCC); - resolution.fill(HIST("QVectorsT0CTPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CTPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CV0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiV0A - deltapsiFV0A)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0CT0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiT0A - deltapsiFT0A)), coll.centFT0C()); - resolution.fill(HIST("QVectorsV0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsV0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCC - deltapsiTPCC)), coll.centFT0C()); - resolution.fill(HIST("QVectorsT0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCA - deltapsiTPCA)), coll.centFT0C()); - resolution.fill(HIST("QVectorsTPCAC_Shifted"), std::cos(static_cast(nmode) * (psiTPCA + deltapsiTPCA - psiTPCC - deltapsiTPCC)), coll.centFT0C()); + histos.fill(HIST("Psi_EP_FT0C_shifted"), collcentrality, psiT0C + deltapsiFT0C); + histos.fill(HIST("Psi_EP_FV0A_shifted"), collcentrality, psiV0A + deltapsiFV0A); + histos.fill(HIST("Psi_EP_FT0A_shifted"), collcentrality, psiT0A + deltapsiFT0A); + histos.fill(HIST("Psi_EP_TPCA_shifted"), collcentrality, psiTPCA + deltapsiTPCA); + histos.fill(HIST("Psi_EP_TPCC_shifted"), collcentrality, psiTPCC + deltapsiTPCC); + resolution.fill(HIST("QVectorsT0CTPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsT0CTPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsT0CV0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiV0A - deltapsiFV0A)), collcentrality); + resolution.fill(HIST("QVectorsT0CT0A_Shifted"), std::cos(static_cast(nmode) * (psiT0C + deltapsiFT0C - psiT0A - deltapsiFT0A)), collcentrality); + resolution.fill(HIST("QVectorsV0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsV0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiV0A + deltapsiFV0A - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsT0ATPCC_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCC - deltapsiTPCC)), collcentrality); + resolution.fill(HIST("QVectorsT0ATPCA_Shifted"), std::cos(static_cast(nmode) * (psiT0A + deltapsiFT0A - psiTPCA - deltapsiTPCA)), collcentrality); + resolution.fill(HIST("QVectorsTPCAC_Shifted"), std::cos(static_cast(nmode) * (psiTPCA + deltapsiTPCA - psiTPCC - deltapsiTPCC)), collcentrality); return true; } // objects to use for acceptance correction - TH2F* hAcceptanceXi; - TH2F* hAcceptanceOmega; - TH2F* hAcceptanceLambda; - TH2F* hAcceptancePrimaryLambda; + TH2F* hAcceptanceXi = nullptr; + TH2F* hAcceptanceOmega = nullptr; + TH2F* hAcceptanceLambda = nullptr; + TH2F* hAcceptancePrimaryLambda = nullptr; // objects to use for resolution correction - TH1F* hReso; + TH1F* hReso = nullptr; // objects to use for centrality weight - TH1F* hCentWeight; + TH1F* hCentWeight = nullptr; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; HistogramRegistry histosMCGen{"histosMCGen", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; @@ -674,7 +668,7 @@ struct cascadeFlow { } template - void fillTrainingTable(collision_t coll, cascade_t casc, int pdgCode) + void fillTrainingTable(const collision_t& coll, const cascade_t& casc, int pdgCode) { trainingSample(coll.centFT0C(), casc.sign(), @@ -699,7 +693,7 @@ struct cascadeFlow { } template - void fillAnalysedTable(collision_t coll, bool hasEventPlane, bool hasSpectatorPlane, cascade_t casc, float v2CSP, float v2CEP, float v1SP_ZDCA, float v1SP_ZDCC, float PsiT0C, float BDTresponseXi, float BDTresponseOmega, int pdgCode, bachExtra_t bachExtra) + void fillAnalysedTable(const collision_t& coll, bool hasEventPlane, bool hasSpectatorPlane, const cascade_t& casc, float v2CSP, float v2CEP, float v1SP_ZDCA, float v1SP_ZDCC, float PsiT0C, float BDTresponseXi, float BDTresponseOmega, int pdgCode, const bachExtra_t& bachExtra) { double masses[nParticles]{o2::constants::physics::MassXiMinus, o2::constants::physics::MassOmegaMinus}; ROOT::Math::PxPyPzMVector cascadeVector[nParticles], lambdaVector, protonVector; @@ -802,7 +796,7 @@ struct cascadeFlow { } template - void fillAnalysedLambdaTable(collision_t coll, bool hasEventPlane, bool hasSpectatorPlane, int chargeIndex, v0_t v0, float v2CEP, float psiT0C, double pzs2Lambda, double cos2ThetaLambda, double cosThetaLambda) + void fillAnalysedLambdaTable(const collision_t& coll, bool hasEventPlane, bool hasSpectatorPlane, int chargeIndex, const v0_t& v0, float v2CEP, float psiT0C, double pzs2Lambda, double cos2ThetaLambda, double cosThetaLambda) { double invMassLambda = 0; if (chargeIndex == 0) @@ -811,7 +805,9 @@ struct cascadeFlow { invMassLambda = v0.mAntiLambda(); else invMassLambda = v0.mLambda(); + double ctauLambda = v0.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassLambda0; analysisLambdaSample(coll.centFT0C(), + coll.centFT0M(), hasEventPlane, hasSpectatorPlane, chargeIndex, @@ -819,6 +815,8 @@ struct cascadeFlow { v0.phi(), v0.eta(), invMassLambda, + v0.mK0Short(), + ctauLambda, v0.v0radius(), v0.dcapostopv(), v0.dcanegtopv(), @@ -1138,6 +1136,8 @@ struct cascadeFlow { histosMCGen.add("h2DGenLambdaY05", "h2DGenLambdaY05", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCGen.add("h2DGenAntiLambdaEta08", "h2DGenAntiLambdaEta08", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCGen.add("h2DGenAntiLambdaY05", "h2DGenAntiLambdaY05", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCGen.add("h2DGenXiVsPtLambda", "h2DGenXiVsPtLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCGen.add("h2DGenLambdaFromXiPtMatrix", "h2DGenLambdaFromXiPtMatrix", HistType::kTH2F, {{200, 0, 20}, {200, 0, 20}}); histosMCGen.add("hGenXiY", "hGenXiY", HistType::kTH1F, {{100, -1, 1}}); histosMCGen.add("hGenOmegaY", "hGenOmegaY", HistType::kTH1F, {{100, -1, 1}}); histosMCGen.add("hGenLambdaY", "hGenLambdaY", HistType::kTH1F, {{100, -2, 2}}); @@ -1158,6 +1158,10 @@ struct cascadeFlow { histosMCReco.add("h2DRecoTrueLambda", "h2DRecoTrueLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); histosMCReco.add("h2DRecoTrueAntiLambda", "h2DRecoTrueAntiLambda", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueLambdaSec", "h2DRecoTrueLambdaSec", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueAntiLambdaSec", "h2DRecoTrueAntiLambdaSec", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueLambdaFromXi", "h2DRecoTrueLambdaFromXi", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); + histosMCReco.add("h2DRecoTrueAntiLambdaFromXi", "h2DRecoTrueAntiLambdaFromXi", HistType::kTH2F, {{100, 0, 100}, {400, 0, 20}}); for (int iS{0}; iS < nParticles; ++iS) { cascadev2::hMassBeforeSelVsPt[iS] = histos.add(Form("hMassBeforeSelVsPt%s", cascadev2::speciesNames[iS].data()), "hMassBeforeSelVsPt", HistType::kTH2F, {massCascAxis[iS], ptAxisCasc}); @@ -1213,7 +1217,7 @@ struct cascadeFlow { } } - void processTrainingBackground(soa::Join::iterator const& coll, soa::Join const& Cascades, DauTracks const&) + void processTrainingBackground(soa::Join::iterator const& coll, soa::Join const& Cascades, DauTracks const&) { int counter = 0; @@ -1230,8 +1234,8 @@ struct cascadeFlow { continue; } - float sigmaRangeXi[2]{getNsigmaMass(cascadev2::Xi, casc.pt(), sideBandStart), getNsigmaMass(cascadev2::Xi, casc.pt(), sideBandEnd)}; - float sigmaRangeOmega[2]{getNsigmaMass(cascadev2::Omega, casc.pt(), sideBandStart), getNsigmaMass(cascadev2::Omega, casc.pt(), sideBandEnd)}; + const float sigmaRangeXi[2]{getNsigmaMass(cascadev2::Xi, casc.pt(), sideBandStart), getNsigmaMass(cascadev2::Xi, casc.pt(), sideBandEnd)}; + const float sigmaRangeOmega[2]{getNsigmaMass(cascadev2::Omega, casc.pt(), sideBandStart), getNsigmaMass(cascadev2::Omega, casc.pt(), sideBandEnd)}; if ((std::abs(casc.mXi() - constants::physics::MassXiMinus) < sigmaRangeXi[0] || std::abs(casc.mXi() - constants::physics::MassXiMinus) > sigmaRangeXi[1]) && @@ -1269,7 +1273,7 @@ struct cascadeFlow { } } - void processTrainingSignal(soa::Join::iterator const& coll, CascMCCandidates const& Cascades, DauTracks const&, soa::Join const&) + void processTrainingSignal(soa::Join::iterator const& coll, CascMCCandidates const& Cascades, DauTracks const&, soa::Join const&) { if (!AcceptEvent(coll, 1)) { @@ -1402,7 +1406,7 @@ struct cascadeFlow { bool isCascCandidate = 0; isCascCandidate = IsCascAccepted(casc, negExtra, posExtra, bachExtra, counter); histos.fill(HIST("hCascade"), counter); - histos.fill(HIST("hCascadeDauSel"), (int)isCascCandidate); + histos.fill(HIST("hCascadeDauSel"), static_cast(isCascCandidate)); if (!isCascCandidate) continue; @@ -1478,7 +1482,7 @@ struct cascadeFlow { // polarization variables double masses[2]{o2::constants::physics::MassXiMinus, o2::constants::physics::MassOmegaMinus}; ROOT::Math::PxPyPzMVector cascadeVector[2], lambdaVector, protonVector; - float cosThetaStarLambda[2], cosThetaStarProton; + double cosThetaStarLambda[2], cosThetaStarProton; double massLambda = casc.mLambda(); if (fillingConfigs.isFillNominalMass) @@ -1733,7 +1737,7 @@ struct cascadeFlow { bool isCascCandidate = 0; isCascCandidate = IsCascAccepted(casc, negExtra, posExtra, bachExtra, counter); histos.fill(HIST("hCascade"), counter); - histos.fill(HIST("hCascadeDauSel"), (int)isCascCandidate); + histos.fill(HIST("hCascadeDauSel"), static_cast(isCascCandidate)); if (!isCascCandidate) continue; @@ -1803,7 +1807,7 @@ struct cascadeFlow { // polarization variables double masses[nParticles]{o2::constants::physics::MassXiMinus, o2::constants::physics::MassOmegaMinus}; ROOT::Math::PxPyPzMVector cascadeVector[nParticles], lambdaVector, protonVector; - float cosThetaStarLambda[nParticles], cosThetaStarProton; + double cosThetaStarLambda[nParticles], cosThetaStarProton; double massLambda = casc.mLambda(); if (fillingConfigs.isFillNominalMass) @@ -2101,15 +2105,15 @@ struct cascadeFlow { resolution.fill(HIST("QVectorsT0ATPCC"), eventplaneVecT0A.Dot(eventplaneVecTPCC), collisionCentrality); resolution.fill(HIST("QVectorsT0ATPCA"), eventplaneVecT0A.Dot(eventplaneVecTPCA), collisionCentrality); - resolution.fill(HIST("EP_T0CTPCA"), std::cos(2 * (psiT0C - psiTPCA)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CTPCC"), std::cos(2 * (psiT0C - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_TPCAC"), std::cos(2 * (psiTPCA - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CV0A"), std::cos(2 * (psiT0C - psiV0A)), coll.centFT0C()); - resolution.fill(HIST("EP_V0ATPCC"), std::cos(2 * (psiV0A - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_V0ATPCA"), std::cos(2 * (psiV0A - psiTPCA)), coll.centFT0C()); - resolution.fill(HIST("EP_T0CT0A"), std::cos(2 * (psiT0C - psiT0A)), coll.centFT0C()); - resolution.fill(HIST("EP_T0ATPCC"), std::cos(2 * (psiT0A - psiTPCC)), coll.centFT0C()); - resolution.fill(HIST("EP_T0ATPCA"), std::cos(2 * (psiT0A - psiTPCA)), coll.centFT0C()); + resolution.fill(HIST("EP_T0CTPCA"), std::cos(2 * (psiT0C - psiTPCA)), collisionCentrality); + resolution.fill(HIST("EP_T0CTPCC"), std::cos(2 * (psiT0C - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_TPCAC"), std::cos(2 * (psiTPCA - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_T0CV0A"), std::cos(2 * (psiT0C - psiV0A)), collisionCentrality); + resolution.fill(HIST("EP_V0ATPCC"), std::cos(2 * (psiV0A - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_V0ATPCA"), std::cos(2 * (psiV0A - psiTPCA)), collisionCentrality); + resolution.fill(HIST("EP_T0CT0A"), std::cos(2 * (psiT0C - psiT0A)), collisionCentrality); + resolution.fill(HIST("EP_T0ATPCC"), std::cos(2 * (psiT0A - psiTPCC)), collisionCentrality); + resolution.fill(HIST("EP_T0ATPCA"), std::cos(2 * (psiT0A - psiTPCA)), collisionCentrality); resolution.fill(HIST("QVectorsNormT0CTPCA"), eventplaneVecT0C.Dot(eventplaneVecTPCA) / (coll.qTPCR() * coll.sumAmplFT0C()), collisionCentrality); resolution.fill(HIST("QVectorsNormT0CTPCC"), eventplaneVecT0C.Dot(eventplaneVecTPCC) / (coll.qTPCL() * coll.sumAmplFT0C()), collisionCentrality); @@ -2132,7 +2136,6 @@ struct cascadeFlow { centWeight = hCentWeight->GetBinContent(centBin); } - std::vector bdtScore[nParticles]; for (auto const& v0 : V0s) { /// Add some minimal cuts for single track variables (min number of TPC clusters) @@ -2180,11 +2183,11 @@ struct cascadeFlow { histos.fill(HIST("hLambdaCandidate"), 3); continue; // in case of ambiguity between Lambda and AntiLambda, I skip the particle; checked to be zero in range 1.105 - 1.125 } - if (v0.mLambda() > V0Configs.MinMassLambda && v0.mLambda() < V0Configs.MaxMassLambda) + if (v0.mLambda() > V0Configs.MinMassLambda && v0.mLambda() < V0Configs.MaxMassLambda) { chargeIndex = 0; - else if (v0.mAntiLambda() > V0Configs.MinMassLambda && v0.mAntiLambda() < V0Configs.MaxMassLambda) + } else if (v0.mAntiLambda() > V0Configs.MinMassLambda && v0.mAntiLambda() < V0Configs.MaxMassLambda) { chargeIndex = 1; - else { + } else { chargeIndex = 2; // these are bkg candidates histos.fill(HIST("hLambdaCandidate"), 4); } @@ -2209,7 +2212,7 @@ struct cascadeFlow { if (fillingConfigs.isFillNominalMass) massLambda = o2::constants::physics::MassLambda; - float cosThetaStarProton[nCharges]; + double cosThetaStarProton[nCharges] = {0}; ROOT::Math::PxPyPzMVector lambdaVector, protonVector[nCharges]; lambdaVector.SetCoordinates(v0.px(), v0.py(), v0.pz(), massLambda); ROOT::Math::Boost lambdaBoost{lambdaVector.BoostToCM()}; @@ -2251,31 +2254,31 @@ struct cascadeFlow { histos.fill(HIST("hLambdaPhi"), v0.phi()); histos.fill(HIST("hlambdaminuspsiT0C"), lambdaminuspsiT0C); + double invMassLambda = 0; + if (chargeIndex == 0) + invMassLambda = v0.mLambda(); + else if (chargeIndex == 1) + invMassLambda = v0.mAntiLambda(); + else + invMassLambda = v0.mLambda(); + if (fillingConfigs.isFillTHNLambda) { if (fillingConfigs.isFillTHN_V2) - histos.get(HIST("hLambdaV2"))->Fill(collisionCentrality, chargeIndex, v0.pt(), v0.mLambda(), v2CEP); + histos.get(HIST("hLambdaV2"))->Fill(collisionCentrality, chargeIndex, v0.pt(), invMassLambda, v2CEP); if (fillingConfigs.isFillTHN_Pz) { - // histos.get(HIST("hLambdaPzs2"))->Fill(collisionCentrality, chargeIndex, v0.pt(), v0.mLambda(), pzs2Lambda); - histos.get(HIST("hLambdaPzs2"))->Fill(collisionCentrality, chargeIndex, v0.pt(), v0.mLambda(), pzs2Lambda, centWeight); + // histos.get(HIST("hLambdaPzs2"))->Fill(collisionCentrality, chargeIndex, v0.pt(), invMassLambda, pzs2Lambda); + histos.get(HIST("hLambdaPzs2"))->Fill(collisionCentrality, chargeIndex, v0.pt(), invMassLambda, pzs2Lambda, centWeight); } if (fillingConfigs.isFillTHN_Acc) - histos.get(HIST("hLambdaCos2Theta"))->Fill(collisionCentrality, chargeIndex, v0.eta(), v0.pt(), v0.mLambda(), cos2ThetaLambda); + histos.get(HIST("hLambdaCos2Theta"))->Fill(collisionCentrality, chargeIndex, v0.eta(), v0.pt(), invMassLambda, cos2ThetaLambda); } if (fillingConfigs.isFillTHNLambda_PzVsPsi) { if (fillingConfigs.isFillTHN_Pz) - histos.get(HIST("hLambdaPzVsPsi"))->Fill(collisionCentrality, chargeIndex, v0.pt(), v0.mLambda(), cosThetaLambda, 2 * lambdaminuspsiT0C, centWeight); + histos.get(HIST("hLambdaPzVsPsi"))->Fill(collisionCentrality, chargeIndex, v0.pt(), invMassLambda, cosThetaLambda, 2 * lambdaminuspsiT0C, centWeight); if (fillingConfigs.isFillTHN_Acc) - histos.get(HIST("hLambdaCos2ThetaVsPsi"))->Fill(collisionCentrality, chargeIndex, v0.eta(), v0.pt(), v0.mLambda(), cos2ThetaLambda, 2 * lambdaminuspsiT0C); + histos.get(HIST("hLambdaCos2ThetaVsPsi"))->Fill(collisionCentrality, chargeIndex, v0.eta(), v0.pt(), invMassLambda, cos2ThetaLambda, 2 * lambdaminuspsiT0C); } - double invMassLambda = 0; - if (chargeIndex == 0) - invMassLambda = v0.mLambda(); - else if (chargeIndex == 1) - invMassLambda = v0.mAntiLambda(); - else - invMassLambda = v0.mLambda(); - // mass selection if (invMassLambda < V0Configs.MinMassLambdaInTree || invMassLambda > V0Configs.MaxMassLambdaInTree) continue; @@ -2390,7 +2393,7 @@ struct cascadeFlow { bool isCascCandidate = 0; isCascCandidate = IsCascAccepted(casc, negExtra, posExtra, bachExtra, counter); histos.fill(HIST("hCascade"), counter); - histos.fill(HIST("hCascadeDauSel"), (int)isCascCandidate); + histos.fill(HIST("hCascadeDauSel"), static_cast(isCascCandidate)); if (!isCascCandidate) continue; @@ -2489,7 +2492,7 @@ struct cascadeFlow { } } - void processAnalyseMC(soa::Join::iterator const& coll, CascMCCandidates const& Cascades, DauTracks const&, soa::Join const&) + void processAnalyseMC(soa::Join::iterator const& coll, CascMCCandidates const& Cascades, DauTracks const&, soa::Join const&) { if (!AcceptEvent(coll, 1)) { @@ -2549,7 +2552,7 @@ struct cascadeFlow { bool isCascCandidate = 0; isCascCandidate = IsCascAccepted(casc, negExtra, posExtra, bachExtra, counter); histos.fill(HIST("hCascade"), counter); - histos.fill(HIST("hCascadeDauSel"), (int)isCascCandidate); + histos.fill(HIST("hCascadeDauSel"), static_cast(isCascCandidate)); if (!isCascCandidate) continue; @@ -2622,6 +2625,19 @@ struct cascadeFlow { BDTresponse[0] = bdtScore[0][1]; BDTresponse[1] = bdtScore[1][1]; } + int chargeIndex = 0; + if (casc.sign() > 0) + chargeIndex = 1; + if (std::abs(casc.eta()) < CandidateConfigs.etaCasc) { + if (fillingConfigs.isFillTHNXi && fillingConfigs.isFillTHN_Pz) { + if (std::abs(pdgCode) == PDG_t::kXiMinus) + histos.get(HIST("hXiPzs2"))->Fill(coll.centFT0C(), chargeIndex, casc.pt(), casc.mXi(), BDTresponse[0], 0); + } + if (fillingConfigs.isFillTHNOmega && fillingConfigs.isFillTHN_Pz) { + if (std::abs(pdgCode) == PDG_t::kOmegaMinus) + histos.get(HIST("hOmegaPzs2"))->Fill(coll.centFT0C(), chargeIndex, casc.pt(), casc.mOmega(), BDTresponse[1], 0); + } + } if (isStoreTrueCascOnly) { if (pdgCode == 0) continue; @@ -2631,7 +2647,7 @@ struct cascadeFlow { } } - void processMCGen(MCCollisionsStra::iterator const& mcCollision, const soa::SmallGroups>& collisions, const soa::SmallGroups>& v0MC, const soa::SmallGroups>& cascMC) + void processMCGen(MCCollisionsStra::iterator const& mcCollision, const soa::SmallGroups>& collisions, const soa::SmallGroups>& v0MC, const soa::SmallGroups>& cascMC) { histosMCGen.fill(HIST("hZvertexGen"), mcCollision.posZ()); @@ -2684,6 +2700,7 @@ struct cascadeFlow { float ptmc = RecoDecay::sqrtSumOfSquares(v0mc.pxMC(), v0mc.pyMC()); float lambdaMCeta = RecoDecay::eta(std::array{v0mc.pxMC(), v0mc.pyMC(), v0mc.pzMC()}); float lambdaMCy = 0; + if (std::abs(v0mc.pdgCode()) == PDG_t::kLambda0) { lambdaMCy = RecoDecay::y(std::array{v0mc.pxMC(), v0mc.pyMC(), v0mc.pzMC()}, constants::physics::MassLambda); if (std::abs(lambdaMCeta) < etaCascMCGen) { @@ -2737,6 +2754,19 @@ struct cascadeFlow { theta1 = o2::constants::math::PI + theta; // pi/2 < theta1 < pi --> pi/4 < theta1/2 < pi/2 --> 1 < tan (theta1/2) --> negative eta float cascMCeta = -std::log(std::tan(theta1 / 2)); + + float pxLambda = cascmc.pxPosMC() + cascmc.pxNegMC(); + float pyLambda = cascmc.pyPosMC() + cascmc.pyNegMC(); + float pzLambda = cascmc.pzPosMC() + cascmc.pzNegMC(); + float ptmcLambda = RecoDecay::sqrtSumOfSquares(pxLambda, pyLambda); + float thetaLambda = std::atan(ptmcLambda / pzLambda); + float theta1Lambda = 0; + if (thetaLambda > 0) + theta1Lambda = thetaLambda; + else + theta1Lambda = o2::constants::math::PI + thetaLambda; + float lambdaMCeta = -std::log(std::tan(theta1Lambda / 2)); + float cascMCy = 0; if (std::abs(cascmc.pdgCode()) == PDG_t::kXiMinus) { cascMCy = RecoDecay::y(std::array{cascmc.pxMC(), cascmc.pyMC(), cascmc.pzMC()}, constants::physics::MassXiMinus); @@ -2747,6 +2777,10 @@ struct cascadeFlow { if (std::abs(cascMCy) < yCascMCGen) histosMCGen.fill(HIST("h2DGenXiY05"), centrality, ptmc); histosMCGen.fill(HIST("hGenXiY"), cascMCy); + if (std::abs(cascMCy) < yXiToLambdaMCGen && std::abs(lambdaMCeta) < etaLambdaFromXiMCGen) { + histosMCGen.fill(HIST("h2DGenXiVsPtLambda"), centrality, ptmcLambda); // to compute secondary lambda efficiency + histosMCGen.fill(HIST("h2DGenLambdaFromXiPtMatrix"), ptmc, ptmcLambda); + } } else if (std::abs(cascmc.pdgCode()) == PDG_t::kOmegaMinus) { cascMCy = RecoDecay::y(std::array{cascmc.pxMC(), cascmc.pyMC(), cascmc.pzMC()}, constants::physics::MassOmegaMinus); if (std::abs(cascMCeta) < etaCascMCGen) { @@ -2760,7 +2794,7 @@ struct cascadeFlow { } } - void processMCPrimaryLambdaFraction(soa::Join::iterator const& coll, V0MCCandidates const& V0s, DauTracks const&, soa::Join const&) + void processMCPrimaryLambdaFraction(soa::Join::iterator const& coll, V0MCCandidates const& V0s, DauTracks const&, soa::Join const&) { Float_t collisionCentrality = 0; @@ -2868,6 +2902,9 @@ struct cascadeFlow { } else { histos.fill(HIST("hCentvsPtvsPrimaryFracLambda"), collisionCentrality, v0.pt(), 1); histos.fill(HIST("hCentvsPrimaryFracLambda"), collisionCentrality, 1); + histosMCReco.fill(HIST("h2DRecoTrueLambdaSec"), collisionCentrality, ptmc); + if (v0MC.pdgCodeMother() == PDG_t::kXiMinus) + histosMCReco.fill(HIST("h2DRecoTrueLambdaFromXi"), collisionCentrality, ptmc); } } else if (isTrueALambda) { if (isPrimary) { @@ -2877,6 +2914,9 @@ struct cascadeFlow { } else { histos.fill(HIST("hCentvsPtvsPrimaryFracLambda"), collisionCentrality, v0.pt(), 3); histos.fill(HIST("hCentvsPrimaryFracLambda"), collisionCentrality, 3); + histosMCReco.fill(HIST("h2DRecoTrueAntiLambdaSec"), collisionCentrality, ptmc); + if (v0MC.pdgCodeMother() == -PDG_t::kXiMinus) + histosMCReco.fill(HIST("h2DRecoTrueAntiLambdaFromXi"), collisionCentrality, ptmc); } } } diff --git a/PWGLF/TableProducer/Strangeness/cascademcbuilder.cxx b/PWGLF/TableProducer/Strangeness/cascademcbuilder.cxx index 04df29d88c4..614a590caea 100644 --- a/PWGLF/TableProducer/Strangeness/cascademcbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/cascademcbuilder.cxx @@ -122,7 +122,7 @@ struct cascademcbuilder { } template - void generateCascadeMCinfo(TCascadeTable cascTable, TMCParticleTable mcParticles) + void generateCascadeMCinfo(const TCascadeTable& cascTable, const TMCParticleTable& mcParticles) { // to be used if using the asymmetric mode, kept empty otherwise diff --git a/PWGLF/TableProducer/Strangeness/cascademcfinder.cxx b/PWGLF/TableProducer/Strangeness/cascademcfinder.cxx index e732b79adce..8c95a1579d4 100644 --- a/PWGLF/TableProducer/Strangeness/cascademcfinder.cxx +++ b/PWGLF/TableProducer/Strangeness/cascademcfinder.cxx @@ -67,7 +67,7 @@ struct cascademcfinder { Configurable doQA{"doQA", true, "do qa plots"}; // For manual sliceBy - Preslice perMcCollision = aod::mcparticle::mcCollisionId; + Preslice perMcCollision = aod::mcparticle::mcCollisionId; std::vector casccollisionId; std::vector cascv0Index; diff --git a/PWGLF/TableProducer/Strangeness/cascademlselection.cxx b/PWGLF/TableProducer/Strangeness/cascademlselection.cxx index dd2341964f0..7c828926dbc 100644 --- a/PWGLF/TableProducer/Strangeness/cascademlselection.cxx +++ b/PWGLF/TableProducer/Strangeness/cascademlselection.cxx @@ -9,21 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* -// Lambdakzero ML selection task -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* -// -// Comments, questions, complaints, suggestions? -// Please write to: -// gianni.shigeru.setoue.liveraro@cern.ch -// romain.schotter@cern.ch -// david.dobrigkeit.chinellato@cern.ch +/// \file Cascade ML selection task +/// \brief Produces ML response table for cascade selection at analysis level, either when running over original data or derived data. +/// \author Gianni Shigeru Setoue Liveraro Catalano , UNICAMP +/// \author Romain Schotter , Austrian Academy of Sciences +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences // #include "PWGLF/DataModel/LFStrangenessMLTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/CascadeMlResponse.h" -#include "Tools/ML/model.h" +#include "Tools/ML/MlResponse.h" #include #include @@ -31,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -38,10 +36,15 @@ #include #include +#include +#include + +#include #include #include #include -#include +#include +#include #include #include @@ -51,34 +54,39 @@ using namespace o2::framework::expressions; using namespace o2::ml; // For original data loops -using CascOriginalDatas = soa::Join; +using CascOriginalDatas = soa::Join; // For derived data analysis -using CascDerivedDatas = soa::Join; +using CascDerivedDatas = soa::Join; struct cascademlselection { - o2::ml::OnnxModel mlModelXiMinus; - o2::ml::OnnxModel mlModelXiPlus; - o2::ml::OnnxModel mlModelOmegaMinus; - o2::ml::OnnxModel mlModelOmegaPlus; + o2::analysis::CascadeMlResponse mlModelXiMinus; + o2::analysis::CascadeMlResponse mlModelXiPlus; + o2::analysis::CascadeMlResponse mlModelOmegaMinus; + o2::analysis::CascadeMlResponse mlModelOmegaPlus; // Custom grouping std::vector> cascadesGrouped; - std::map metadata; - Produces xiMLSelections; // optionally aggregate information from ML output for posterior analysis (derived data) Produces omegaMLSelections; // optionally aggregate information from ML output for posterior analysis (derived data) HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // BDT score histograms, indexed [pT bin][class], one set per particle species + std::vector>> histScoreXiMinus; + std::vector>> histScoreXiPlus; + std::vector>> histScoreOmegaMinus; + std::vector>> histScoreOmegaPlus; + // CCDB configuration o2::ccdb::CcdbApi ccdbApi; Service ccdb; - int mRunNumber; + int mRunNumber = -1; // CCDB options struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; @@ -88,29 +96,43 @@ struct cascademlselection { // Machine learning evaluation for pre-selection and corresponding information generation struct : ConfigurableGroup { + std::string prefix = "mlConfigurations"; // ML classifiers: master flags to populate ML Selection tables Configurable calculateXiMinusScores{"mlConfigurations.calculateXiMinusScores", true, "calculate XiMinus ML scores"}; Configurable calculateXiPlusScores{"mlConfigurations.calculateXiPlusScores", true, "calculate XiPlus ML scores"}; Configurable calculateOmegaMinusScores{"mlConfigurations.calculateOmegaMinusScores", true, "calculate OmegaMinus ML scores"}; Configurable calculateOmegaPlusScores{"mlConfigurations.calculateOmegaPlusScores", true, "calculate OmegaPlus ML scores"}; - // ML input for ML calculation - Configurable modelPathCCDB{"mlConfigurations.modelPathCCDB", "", "ML Model path in CCDB"}; + // List and order of input features fed to the ONNX models; any subset/order of the names + // registered in CascMlResponse::setAvailableInputFeatures can be used here + Configurable> namesInputFeatures{"mlConfigurations.namesInputFeatures", std::vector{"cascradius", "v0radius", "casccosPA", "v0cosPA", "dcapostopv", "dcanegtopv", "dcabachtopv", "dcacascdaughters", "dcaV0daughters", "dcav0topv", "bachBaryonCosPA", "bachBaryonDCAxyToPV"}, "Names (and order) of the input features to be used in the ML models"}; + + // ML input for ML calculation: one model (ONNX file / CCDB path) per pT bin + Configurable> modelPathsCCDBXiMinus{"mlConfigurations.modelPathsCCDBXiMinus", std::vector{""}, "ML Model paths in CCDB for Xi-. One per pT bin."}; + Configurable> modelPathsCCDBXiPlus{"mlConfigurations.modelPathsCCDBXiPlus", std::vector{""}, "ML Model paths in CCDB for Xi+. One per pT bin."}; + Configurable> modelPathsCCDBOmegaMinus{"mlConfigurations.modelPathsCCDBOmegaMinus", std::vector{""}, "ML Model paths in CCDB for Omega-. One per pT bin."}; + Configurable> modelPathsCCDBOmegaPlus{"mlConfigurations.modelPathsCCDBOmegaPlus", std::vector{""}, "ML Model paths in CCDB for Omega+. One per pT bin."}; Configurable timestampCCDB{"mlConfigurations.timestampCCDB", -1, "timestamp of the ONNX file for ML model used to query in CCDB. Exceptions: > 0 for the specific timestamp, 0 gets the run dependent timestamp"}; Configurable loadModelsFromCCDB{"mlConfigurations.loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; Configurable enableOptimizations{"mlConfigurations.enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; - // Local paths for test purposes - Configurable localModelPathXiMinus{"mlConfigurations.localModelPathXiMinus", "XiMinus_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - Configurable localModelPathXiPlus{"mlConfigurations.localModelPathXiPlus", "XiPlus_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - Configurable localModelPathOmegaMinus{"mlConfigurations.localModelPathOmegaMinus", "OmegaMinus_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - Configurable localModelPathOmegaPlus{"mlConfigurations.localModelPathOmegaPlus", "OmegaPlus_BDTModel.onnx", "(std::string) Path to the local .onnx file."}; - - // Thresholds for choosing to populate V0Cores tables with pre-selections - Configurable thresholdXiMinus{"mlConfigurations.thresholdXiMinus", -1.0f, "Threshold to keep XiMinus candidates"}; - Configurable thresholdXiPlus{"mlConfigurations.thresholdXiPlus", -1.0f, "Threshold to keep XiPlus candidates"}; - Configurable thresholdOmegaMinus{"mlConfigurations.thresholdOmegaMinus", -1.0f, "Threshold to keep OmegaMinus candidates"}; - Configurable thresholdOmegaPlus{"mlConfigurations.thresholdOmegaPlus", -1.0f, "Threshold to keep OmegaPlus candidates"}; + // Local/cvmfs paths (also used as CCDB download destination filenames), one per pT bin + Configurable> onnxFileNamesXiMinus{"mlConfigurations.onnxFileNamesXiMinus", std::vector{"XiMinus_BDTModel.onnx"}, "(std::string) Paths to the local .onnx file. One per pT bin."}; + Configurable> onnxFileNamesXiPlus{"mlConfigurations.onnxFileNamesXiPlus", std::vector{"XiPlus_BDTModel.onnx"}, "(std::string) Paths to the local .onnx file. One per pT bin."}; + Configurable> onnxFileNamesOmegaMinus{"mlConfigurations.onnxFileNamesOmegaMinus", std::vector{"OmegaMinus_BDTModel.onnx"}, "(std::string) Paths to the local .onnx file. One per pT bin."}; + Configurable> onnxFileNamesOmegaPlus{"mlConfigurations.onnxFileNamesOmegaPlus", std::vector{"OmegaPlus_BDTModel.onnx"}, "(std::string) Paths to the local .onnx file. One per pT bin."}; + + // Binning + Configurable> binsPtXiMinus{"binsPtXiMinus", std::vector{0., 10.}, "pT bin limits for ML application for Xi-"}; + Configurable> binsPtXiPlus{"binsPtXiPlus", std::vector{0., 10.}, "pT bin limits for ML application for Xi+"}; + Configurable> binsPtOmegaMinus{"binsPtOmegaMinus", std::vector{0., 10.}, "pT bin limits for ML application for Omega-"}; + Configurable> binsPtOmegaPlus{"binsPtOmegaPlus", std::vector{0., 10.}, "pT bin limits for ML application for Omega+"}; + + // Number of classes in the ML models. Default is 2 (signal and background) + Configurable nClassesMlXiMinus{"nClassesMlXiMinus", 2, "Number of classes in ML model for Xi-"}; + Configurable nClassesMlXiPlus{"nClassesMlXiPlus", 2, "Number of classes in ML model for Xi+"}; + Configurable nClassesMlOmegaMinus{"nClassesMlOmegaMinus", 2, "Number of classes in ML model for Omega-"}; + Configurable nClassesMlOmegaPlus{"nClassesMlOmegaPlus", 2, "Number of classes in ML model for Omega+"}; } mlConfigurations; // Axis @@ -146,62 +168,35 @@ struct cascademlselection { mlConfigurations.calculateOmegaPlusScores) { if (mlConfigurations.timestampCCDB.value != -1) timeStampML = mlConfigurations.timestampCCDB.value; - LoadMachines(timeStampML); + loadMachines(timeStampML); } } // function to load models for ML-based classifiers - void LoadMachines(int64_t timeStampML) + void loadMachines(int64_t timeStampML) { + auto loadModel = [&](bool doCalculate, o2::analysis::CascadeMlResponse& model, std::vector const& onnxFileNames, std::vector const& pathsCCDB) { + if (!doCalculate) { + return; + } + if (mlConfigurations.loadModelsFromCCDB) { + model.setModelPathsCCDB(onnxFileNames, ccdbApi, pathsCCDB, timeStampML); + } else { + model.setModelPathsLocal(onnxFileNames); + } + model.init(mlConfigurations.enableOptimizations.value); + }; + if (mlConfigurations.loadModelsFromCCDB) { ccdbApi.init(ccdbConfigurations.ccdburl); LOG(info) << "Fetching cascade models for timestamp: " << timeStampML; + } - if (mlConfigurations.calculateXiMinusScores) { - bool retrieveSuccess = ccdbApi.retrieveBlob(mlConfigurations.modelPathCCDB, ".", metadata, timeStampML, false, mlConfigurations.localModelPathXiMinus.value); - if (retrieveSuccess) { - mlModelXiMinus.initModel(mlConfigurations.localModelPathXiMinus.value, mlConfigurations.enableOptimizations.value); - } else { - LOG(fatal) << "Error encountered while fetching/loading the XiMinus model from CCDB! Maybe the model doesn't exist yet for this runnumber/timestamp?"; - } - } - - if (mlConfigurations.calculateXiPlusScores) { - bool retrieveSuccess = ccdbApi.retrieveBlob(mlConfigurations.modelPathCCDB, ".", metadata, timeStampML, false, mlConfigurations.localModelPathXiPlus.value); - if (retrieveSuccess) { - mlModelXiPlus.initModel(mlConfigurations.localModelPathXiPlus.value, mlConfigurations.enableOptimizations.value); - } else { - LOG(fatal) << "Error encountered while fetching/loading the XiPlus model from CCDB! Maybe the model doesn't exist yet for this runnumber/timestamp?"; - } - } - - if (mlConfigurations.calculateOmegaMinusScores) { - bool retrieveSuccess = ccdbApi.retrieveBlob(mlConfigurations.modelPathCCDB, ".", metadata, timeStampML, false, mlConfigurations.localModelPathOmegaMinus.value); - if (retrieveSuccess) { - mlModelOmegaMinus.initModel(mlConfigurations.localModelPathOmegaMinus.value, mlConfigurations.enableOptimizations.value); - } else { - LOG(fatal) << "Error encountered while fetching/loading the OmegaMinus model from CCDB! Maybe the model doesn't exist yet for this runnumber/timestamp?"; - } - } + loadModel(mlConfigurations.calculateXiMinusScores, mlModelXiMinus, mlConfigurations.onnxFileNamesXiMinus, mlConfigurations.modelPathsCCDBXiMinus); + loadModel(mlConfigurations.calculateXiPlusScores, mlModelXiPlus, mlConfigurations.onnxFileNamesXiPlus, mlConfigurations.modelPathsCCDBXiPlus); + loadModel(mlConfigurations.calculateOmegaMinusScores, mlModelOmegaMinus, mlConfigurations.onnxFileNamesOmegaMinus, mlConfigurations.modelPathsCCDBOmegaMinus); + loadModel(mlConfigurations.calculateOmegaPlusScores, mlModelOmegaPlus, mlConfigurations.onnxFileNamesOmegaPlus, mlConfigurations.modelPathsCCDBOmegaPlus); - if (mlConfigurations.calculateOmegaPlusScores) { - bool retrieveSuccess = ccdbApi.retrieveBlob(mlConfigurations.modelPathCCDB, ".", metadata, timeStampML, false, mlConfigurations.localModelPathOmegaPlus.value); - if (retrieveSuccess) { - mlModelOmegaPlus.initModel(mlConfigurations.localModelPathOmegaPlus.value, mlConfigurations.enableOptimizations.value); - } else { - LOG(fatal) << "Error encountered while fetching/loading the OmegaPlus model from CCDB! Maybe the model doesn't exist yet for this runnumber/timestamp?"; - } - } - } else { - if (mlConfigurations.calculateXiMinusScores) - mlModelXiMinus.initModel(mlConfigurations.localModelPathXiMinus.value, mlConfigurations.enableOptimizations.value); - if (mlConfigurations.calculateXiPlusScores) - mlModelXiPlus.initModel(mlConfigurations.localModelPathXiPlus.value, mlConfigurations.enableOptimizations.value); - if (mlConfigurations.calculateOmegaMinusScores) - mlModelOmegaMinus.initModel(mlConfigurations.localModelPathOmegaMinus.value, mlConfigurations.enableOptimizations.value); - if (mlConfigurations.calculateOmegaPlusScores) - mlModelOmegaPlus.initModel(mlConfigurations.localModelPathOmegaPlus.value, mlConfigurations.enableOptimizations.value); - } LOG(info) << "Cascade ML Models loaded."; } @@ -211,45 +206,96 @@ struct cascademlselection { histos.add("hEventVertexZ", "hEventVertexZ", kTH1F, {vertexZ}); ccdb->setURL(ccdbConfigurations.ccdburl); + + // builds a shape-valid but functionally unused cuts array: this task only stores + // raw ML scores (thresholds are applied downstream), so no cut direction is used + auto dummyCuts = [](int nBins, int nClasses) { + std::vector zeros(static_cast(nBins) * static_cast(nClasses), 0.); + return LabeledArray(zeros.data(), nBins, nClasses); + }; + + auto configureModel = [&](o2::analysis::CascadeMlResponse& model, std::vector const& binsPt, int nClasses) { + int nBins = static_cast(binsPt.size()) - 1; + model.configure(binsPt, dummyCuts(nBins, nClasses), std::vector(nClasses, o2::cuts_ml::CutNot), nClasses); + model.cacheInputFeaturesIndices(mlConfigurations.namesInputFeatures); + }; + + auto bookScoreHistos = [&](std::vector>>& target, std::vector const& binsPt, int nClasses, std::string const& particleName) { + int nBins = static_cast(binsPt.size()) - 1; + target.resize(nBins); + for (int iBin = 0; iBin < nBins; iBin++) { + target[iBin].resize(nClasses); + for (int iClass = 0; iClass < nClasses; iClass++) { + target[iBin][iClass] = histos.add(Form("BDTScore/%s/pTbin%d/class%d", particleName.c_str(), iBin, iClass), + Form("%s BDT score, %.2f #leq p_{T} < %.2f GeV/c, class %d;BDT score;entries", particleName.c_str(), binsPt[iBin], binsPt[iBin + 1], iClass), + kTH1F, {{100, 0., 1.}}); + } + } + }; + + if (mlConfigurations.calculateXiMinusScores) { + configureModel(mlModelXiMinus, mlConfigurations.binsPtXiMinus, mlConfigurations.nClassesMlXiMinus); + bookScoreHistos(histScoreXiMinus, mlConfigurations.binsPtXiMinus, mlConfigurations.nClassesMlXiMinus, "XiMinus"); + } + if (mlConfigurations.calculateXiPlusScores) { + configureModel(mlModelXiPlus, mlConfigurations.binsPtXiPlus, mlConfigurations.nClassesMlXiPlus); + bookScoreHistos(histScoreXiPlus, mlConfigurations.binsPtXiPlus, mlConfigurations.nClassesMlXiPlus, "XiPlus"); + } + if (mlConfigurations.calculateOmegaMinusScores) { + configureModel(mlModelOmegaMinus, mlConfigurations.binsPtOmegaMinus, mlConfigurations.nClassesMlOmegaMinus); + bookScoreHistos(histScoreOmegaMinus, mlConfigurations.binsPtOmegaMinus, mlConfigurations.nClassesMlOmegaMinus, "OmegaMinus"); + } + if (mlConfigurations.calculateOmegaPlusScores) { + configureModel(mlModelOmegaPlus, mlConfigurations.binsPtOmegaPlus, mlConfigurations.nClassesMlOmegaPlus); + bookScoreHistos(histScoreOmegaPlus, mlConfigurations.binsPtOmegaPlus, mlConfigurations.nClassesMlOmegaPlus, "OmegaPlus"); + } } - // Process candidate and store properties in object - template - void processCandidate(TCascObject const& cand) + // Finds the pT bin matching the MlResponse internal convention (upper_bound over bin edges); + // returns -1 if pt falls outside the configured range, since evaluating the model in that + // case would otherwise trigger a LOG(fatal) inside MlResponse::getModelOutput + int findPtBin(std::vector const& binsPt, float pt) { - // Select features - // FIXME THIS NEEDS ADJUSTING - std::vector inputFeatures{0.0f, 0.0f, - 0.0f, 0.0f}; - - // calculate scores - if (cand.sign() < 0) { - if (mlConfigurations.calculateXiMinusScores) { - float* xiMinusProbability = mlModelXiMinus.evalModel(inputFeatures); - xiMLSelections(xiMinusProbability[1]); - } else { - xiMLSelections(-1); - } - if (mlConfigurations.calculateOmegaMinusScores) { - float* omegaMinusProbability = mlModelOmegaMinus.evalModel(inputFeatures); - omegaMLSelections(omegaMinusProbability[1]); - } else { - omegaMLSelections(-1); - } + if (pt < binsPt.front() || pt >= binsPt.back()) { + return -1; } - if (cand.sign() > 0) { - if (mlConfigurations.calculateXiPlusScores) { - float* xiPlusProbability = mlModelXiPlus.evalModel(inputFeatures); - xiMLSelections(xiPlusProbability[1]); - } else { - xiMLSelections(-1); - } - if (mlConfigurations.calculateOmegaPlusScores) { - float* omegaPlusProbability = mlModelOmegaPlus.evalModel(inputFeatures); - omegaMLSelections(omegaPlusProbability[1]); - } else { - omegaMLSelections(-1); - } + return static_cast(std::distance(binsPt.begin(), std::upper_bound(binsPt.begin(), binsPt.end(), pt))) - 1; + } + + // Evaluates one particle hypothesis' model for a candidate, fills the per-bin/per-class + // score histograms, and returns the signal (class 1) score to be stored in the output table + template + std::vector evaluateModel(o2::analysis::CascadeMlResponse& model, std::vector>>& scoreHistos, std::vector const& binsPt, bool doCalculate, TCascObject const& casc, float pt, TCollision const& coll) + { + if (!doCalculate) { + return {}; + } + int iBin = findPtBin(binsPt, pt); + if (iBin < 0) { + return {}; + } + + auto inputFeatures = model.getInputFeatures(casc, coll); + std::vector output; + model.isSelectedMl(inputFeatures, pt, output); + + for (std::size_t iClass = 0; iClass < output.size() && iClass < scoreHistos[iBin].size(); iClass++) { + scoreHistos[iBin][iClass]->Fill(output[iClass]); + } + return output; + } + + // Process candidate and store properties in object + template + void processCandidate(TCascObject const& casc, float pt, TCollision const& coll) + { + // calculate scores (cascades only ever carry sign +1 or -1) + if (casc.sign() < 0) { + xiMLSelections(evaluateModel(mlModelXiMinus, histScoreXiMinus, mlConfigurations.binsPtXiMinus, mlConfigurations.calculateXiMinusScores, casc, pt, coll)); + omegaMLSelections(evaluateModel(mlModelOmegaMinus, histScoreOmegaMinus, mlConfigurations.binsPtOmegaMinus, mlConfigurations.calculateOmegaMinusScores, casc, pt, coll)); + } else { + xiMLSelections(evaluateModel(mlModelXiPlus, histScoreXiPlus, mlConfigurations.binsPtXiPlus, mlConfigurations.calculateXiPlusScores, casc, pt, coll)); + omegaMLSelections(evaluateModel(mlModelOmegaPlus, histScoreOmegaPlus, mlConfigurations.binsPtOmegaPlus, mlConfigurations.calculateOmegaPlusScores, casc, pt, coll)); } } @@ -269,11 +315,7 @@ struct cascademlselection { histos.fill(HIST("hEventVertexZ"), collision.posZ()); for (std::size_t i = 0; i < cascadesGrouped[collision.globalIndex()].size(); i++) { auto casc = cascades.rawIteratorAt(cascadesGrouped[collision.globalIndex()][i]); - nCandidates++; - if (nCandidates % 50000 == 0) { - LOG(info) << "Candidates processed: " << nCandidates; - } - processCandidate(casc); + processCandidate(casc, casc.pt(), collision); } } } @@ -293,11 +335,7 @@ struct cascademlselection { histos.fill(HIST("hEventVertexZ"), collision.posZ()); for (std::size_t i = 0; i < cascadesGrouped[collision.globalIndex()].size(); i++) { auto casc = cascades.rawIteratorAt(cascadesGrouped[collision.globalIndex()][i]); - nCandidates++; - if (nCandidates % 50000 == 0) { - LOG(info) << "Candidates processed: " << nCandidates; - } - processCandidate(casc); + processCandidate(casc, casc.pt(), collision); } } } diff --git a/PWGLF/TableProducer/Strangeness/cascderivedqaanalysis.cxx b/PWGLF/TableProducer/Strangeness/cascderivedqaanalysis.cxx index b23c7bf2f70..11129716925 100644 --- a/PWGLF/TableProducer/Strangeness/cascderivedqaanalysis.cxx +++ b/PWGLF/TableProducer/Strangeness/cascderivedqaanalysis.cxx @@ -18,16 +18,16 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" -#include "Framework/AnalysisTask.h" -#include "Framework/runDataProcessing.h" #include #include #include #include +#include #include #include #include #include +#include #include #include @@ -176,7 +176,7 @@ struct CascDerivedQAanalysis { std::vector> cascadesGrouped; template - bool isCascadeSelected(TCascade casc, TCollision collision, float rapidity, bool isXi) + bool isCascadeSelected(const TCascade& casc, const TCollision& collision, float rapidity, bool isXi) // precalculate this information so that a check is one mask operation, not many { @@ -409,7 +409,7 @@ struct CascDerivedQAanalysis { } template - bool isEventAccepted(TCollision collision, bool fillHists) + bool isEventAccepted(const TCollision& collision, bool fillHists) // check whether the collision passes our collision selections { if (fillHists) { diff --git a/PWGLF/TableProducer/Strangeness/cascqaanalysis.cxx b/PWGLF/TableProducer/Strangeness/cascqaanalysis.cxx index 0bea23156b5..ce89b1136b2 100644 --- a/PWGLF/TableProducer/Strangeness/cascqaanalysis.cxx +++ b/PWGLF/TableProducer/Strangeness/cascqaanalysis.cxx @@ -65,6 +65,7 @@ using namespace o2::aod::rctsel; using TrkPidInfo = soa::Join; using DauTracks = soa::Join; using LabeledCascades = soa::Join; +using BCsWithBcSels = soa::Join; struct Cascqaanalysis { @@ -74,6 +75,13 @@ struct Cascqaanalysis { HistogramRegistry registry{"registry"}; + enum EventTypeBin { + kINEL = 0, + kINELgt0, + kINELgt1, + kNEventTypeBins + }; + // Axes ConfigurableAxis ptAxis{"ptAxis", {200, 0.0f, 10.0f}, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis rapidityAxis{"rapidityAxis", {200, -2.0f, 2.0f}, "y"}; @@ -83,18 +91,19 @@ struct Cascqaanalysis { ConfigurableAxis centFV0AAxis{"centFV0AAxis", {VARIABLE_WIDTH, 0., 0.01, 0.05, 0.1, 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 105.5}, "FV0A (%)"}; - ConfigurableAxis eventTypeAxis{"eventTypeAxis", {3, -0.5f, 2.5f}, "Event Type"}; + ConfigurableAxis eventTypeAxis{"eventTypeAxis", {kNEventTypeBins, -0.5f, static_cast(kNEventTypeBins) - 0.5f}, "Event Type"}; ConfigurableAxis nAssocCollAxis{"nAssocCollAxis", {5, -0.5f, 4.5f}, "N_{assoc.}"}; ConfigurableAxis nChargedFT0MGenAxis{"nChargedFT0MGenAxis", {300, 0, 300}, "N_{FT0M, gen.}"}; ConfigurableAxis nChargedFV0AGenAxis{"nChargedFV0AGenAxis", {300, 0, 300}, "N_{FV0A, gen.}"}; ConfigurableAxis multNTracksAxis{"multNTracksAxis", {500, 0, 500}, "N_{tracks}"}; - ConfigurableAxis signalFT0MAxis{"signalFT0MAxis", {10000, 0, 40000}, "FT0M amplitude"}; - ConfigurableAxis signalFV0AAxis{"signalFV0AAxis", {10000, 0, 40000}, "FV0A amplitude"}; + Configurable signalFT0MNBins{"signalFT0MNBins", 1000, "Number of bins for FT0M amplitude QA axis"}; + Configurable signalFV0ANBins{"signalFV0ANBins", 1000, "Number of bins for FV0A amplitude QA axis"}; ConfigurableAxis nCandidates{"nCandidates", {30, -0.5, 29.5}, "N_{cand.}"}; // Event selection criteria Configurable cutzvertex{"cutzvertex", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable cutzvertexGen{"cutzvertexGen", -1.0f, "Accepted generated z-vertex range (cm); negative disables the cut"}; Configurable isVertexITSTPC{"isVertexITSTPC", 0, "Select collisions with at least one ITS-TPC track"}; Configurable isNoSameBunchPileup{"isNoSameBunchPileup", 0, "Same found-by-T0 bunch crossing rejection"}; Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", 0, "z of PV by tracks and z of PV from FT0 A-C time difference cut"}; @@ -103,6 +112,8 @@ struct Cascqaanalysis { Configurable isTriggerTVX{"isTriggerTVX", 1, "TVX trigger"}; Configurable isNoTimeFrameBorder{"isNoTimeFrameBorder", 1, "TF border cut"}; Configurable isNoITSROFrameBorder{"isNoITSROFrameBorder", 1, "ITS ROF border cut"}; + Configurable applyBcBorderCutsOnGen{"applyBcBorderCutsOnGen", false, "Apply enabled BC-level TF and ITS ROF border cuts on generated-level MC collisions"}; + Configurable applyRCTOnGen{"applyRCTOnGen", false, "Apply enabled BC-level RCT run-condition selection on generated-level MC collisions"}; Configurable isNoCollInTimeRangeNarrow{"isNoCollInTimeRangeNarrow", 1, "No collisions in +-2us window"}; Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; @@ -149,11 +160,21 @@ struct Cascqaanalysis { uint8_t typeFlag; } CollisionIndexAndType; + static constexpr unsigned int kNITSLayers = 7; + static constexpr float kGlobalTrackEtaMax = 0.5f; + static constexpr float kFT0CMinEta = -3.3f; + static constexpr float kFT0CMaxEta = -2.1f; + static constexpr float kFT0AMinEta = 3.5f; + static constexpr float kFT0AMaxEta = 4.9f; + static constexpr float kFV0AMinEta = 2.2f; + static constexpr float kFV0AMaxEta = 5.1f; + static constexpr size_t kNContributorsCorrelationSize = 2; + template static int countITSHits(TTrack const& track) { int nHits = 0; - for (unsigned int i = 0; i < 7; ++i) { + for (unsigned int i = 0; i < kNITSLayers; ++i) { if (track.itsClusterMap() & (1 << i)) { ++nHits; } @@ -187,7 +208,7 @@ struct Cascqaanalysis { void init(InitContext const&) { TString hCandidateCounterLabels[4] = {"All candidates", "passed topo cuts", "has associated MC particle", "associated with Xi(Omega)"}; - TString hNEventsMCLabels[6] = {"All", "z vrtx", "INEL", "INEL>0", "INEL>1", "Associated with rec. collision"}; + TString hNEventsMCLabels[8] = {"All", "z vrtx", "BC TF/ITS ROF border", "RCTFlagsChecker", "INEL", "INEL>0", "INEL>1", "Associated with rec. collision"}; TString hNEventsLabels[14] = {"All", "kIsTriggerTVX", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kIsVertexITSTPC", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "isVertexTOFmatched", "kNoCollInTimeRangeNarrow", "z vrtx", "RCTFlagsChecker", "INEL", "INEL>0", "INEL>1"}; registry.add("hNEvents", "hNEvents", {HistType::kTH1D, {{14, 0.f, 14.f}}}); @@ -205,11 +226,13 @@ struct Cascqaanalysis { // Rec. lvl registry.add("fakeEvents", "fakeEvents", {HistType::kTH1F, {{1, -0.5f, 0.5f}}}); // Gen. lvl - registry.add("hNEventsMC", "hNEventsMC", {HistType::kTH1D, {{6, 0.0f, 6.0f}}}); + registry.add("hNEventsMC", "hNEventsMC", {HistType::kTH1D, {{8, 0.0f, 8.0f}}}); for (int n = 1; n <= registry.get(HIST("hNEventsMC"))->GetNbinsX(); n++) { registry.get(HIST("hNEventsMC"))->GetXaxis()->SetBinLabel(n, hNEventsMCLabels[n - 1]); } registry.add("hZCollisionGen", "hZCollisionGen", {HistType::kTH1D, {{200, -20.f, 20.f}}}); + registry.add("hZCollisionRecVsGen", "hZCollisionRecVsGen", {HistType::kTH2D, {{100, -10.f, 10.f, "z_{vtx}^{rec} (cm)"}, {100, -10.f, 10.f, "z_{vtx}^{gen} (cm)"}}}); + registry.add("hEventTypeRecVsGen", "hEventTypeRecVsGen", {HistType::kTH2D, {eventTypeAxis, eventTypeAxis}}); registry.add("hNchFT0MNAssocMCCollisions", "hNchFT0MNAssocMCCollisions", {HistType::kTH3D, {nChargedFT0MGenAxis, nAssocCollAxis, eventTypeAxis}}); registry.add("hNchFT0MNAssocMCCollisionsSameType", "hNchFT0MNAssocMCCollisionsSameType", {HistType::kTH3D, {nChargedFT0MGenAxis, nAssocCollAxis, eventTypeAxis}}); registry.add("hNContributorsCorrelation", "hNContributorsCorrelation", {HistType::kTH2F, {{250, -0.5f, 249.5f, "Secondary Contributor"}, {250, -0.5f, 249.5f, "Main Contributor"}}}); @@ -225,6 +248,8 @@ struct Cascqaanalysis { } if (multQA) { + AxisSpec signalFT0MAxis = {std::max(1, static_cast(signalFT0MNBins)), 0.f, 40000.f, "FT0M amplitude"}; + AxisSpec signalFV0AAxis = {std::max(1, static_cast(signalFV0ANBins)), 0.f, 40000.f, "FV0A amplitude"}; if (isMC) { // Rec. lvl registry.add("hNchFT0Mglobal", "hNchFT0Mglobal", {HistType::kTH3D, {nChargedFT0MGenAxis, multNTracksAxis, eventTypeAxis}}); @@ -239,7 +264,7 @@ struct Cascqaanalysis { registry.add("hFT0MsignalPVContr", "hFT0MsignalPVContr", {HistType::kTH3D, {signalFT0MAxis, multNTracksAxis, eventTypeAxis}}); } - rctChecker.init(cfgEvtRCTFlagCheckerLabel, cfgEvtRCTFlagCheckerZDCCheck, cfgEvtRCTFlagCheckerLimitAcceptAsBad); + rctChecker.init(cfgEvtRCTFlagCheckerLabel, cfgEvtRCTFlagCheckerZDCCheck, cfgEvtRCTFlagCheckerLimitAcceptAsBad, requireRCTFlagChecker || applyRCTOnGen); if (cfgEvtRCTFlagCheckerFV0Check) { rctChecker.set(o2::aod::rctsel::kFV0Bad); } @@ -253,7 +278,7 @@ struct Cascqaanalysis { aod::cascdata::dcacascdaughters < dcacascdau); Partition pvContribTracksIUEta1 = (nabs(aod::track::eta) < 1.0f) && ((aod::track::flags & static_cast(o2::aod::track::PVContributor)) == static_cast(o2::aod::track::PVContributor)); - Partition globalTracksIUEta05 = (nabs(aod::track::eta) < 0.5f) && (requireGlobalTrackInFilter()); + Partition globalTracksIUEta05 = (nabs(aod::track::eta) < kGlobalTrackEtaMax) && (requireGlobalTrackInFilter()); template bool acceptCascCandidate(TCascade const& cascCand, float const& pvx, float const& pvy, float const& pvz) @@ -274,7 +299,7 @@ struct Cascqaanalysis { } template - uint16_t getGenNchInFT0Mregion(TMcParticles particles) + uint16_t getGenNchInFT0Mregion(const TMcParticles& particles) { // Particle counting in FITFT0: -3.3<η<-2.1; 3.5<η<4.9 uint16_t nchFT0 = 0; @@ -289,7 +314,7 @@ struct Cascqaanalysis { if (pdgInfo->Charge() == 0) { continue; } - if (mcParticle.eta() < -3.3 || mcParticle.eta() > 4.9 || (mcParticle.eta() > -2.1 && mcParticle.eta() < 3.5)) { + if (mcParticle.eta() < kFT0CMinEta || mcParticle.eta() > kFT0AMaxEta || (mcParticle.eta() > kFT0CMaxEta && mcParticle.eta() < kFT0AMinEta)) { continue; // select on T0M Nch region } nchFT0++; // increment @@ -298,7 +323,7 @@ struct Cascqaanalysis { } template - uint16_t getGenNchInFV0Aregion(TMcParticles particles) + uint16_t getGenNchInFV0Aregion(const TMcParticles& particles) { // Particle counting in FV0A: 2.2<η<5.1 uint16_t nchFV0A = 0; @@ -313,7 +338,7 @@ struct Cascqaanalysis { if (pdgInfo->Charge() == 0) { continue; } - if (mcParticle.eta() < 2.2 || mcParticle.eta() > 5.1) { + if (mcParticle.eta() < kFV0AMinEta || mcParticle.eta() > kFV0AMaxEta) { continue; // select on V0A Nch region } nchFV0A++; // increment @@ -322,20 +347,19 @@ struct Cascqaanalysis { } template - int getEventTypeFlag(TCollision const& collision) + EventTypeBin getEventTypeBin(TCollision const& collision) { - // 0 - INEL, 1 - INEL>0, 2 - INEL>1 - int evFlag = 0; + EventTypeBin evTypeBin = kINEL; registry.fill(HIST("hNEvents"), 11.5); // INEL if (collision.isInelGt0()) { - evFlag += 1; + evTypeBin = kINELgt0; registry.fill(HIST("hNEvents"), 12.5); // INEL>0 } if (collision.isInelGt1()) { - evFlag += 1; + evTypeBin = kINELgt1; registry.fill(HIST("hNEvents"), 13.5); // INEL>1 } - return evFlag; + return evTypeBin; } template @@ -408,7 +432,7 @@ struct Cascqaanalysis { registry.fill(HIST("hNEvents"), 8.5); } - // Z vertex selection + // z-vertex selection if (std::fabs(collision.posZ()) > cutzvertex) { return false; } @@ -428,6 +452,27 @@ struct Cascqaanalysis { return true; } + template + bool acceptGeneratedEventBcBorderCuts(TBC const& bc) + { + if (!applyBcBorderCutsOnGen) { + return true; + } + if (isNoTimeFrameBorder && !bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (isNoITSROFrameBorder && !bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + return true; + } + + template + bool acceptGeneratedEventRCT(TBC const& bc) + { + return !applyRCTOnGen || rctChecker(bc); + } + void processData(soa::Join::iterator const& collision, @@ -439,7 +484,7 @@ struct Cascqaanalysis { return; } - int evType = getEventTypeFlag(collision); + EventTypeBin evType = getEventTypeBin(collision); auto tracksGroupedPVcontr = pvContribTracksIUEta1->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); int nTracksPVcontr = tracksGroupedPVcontr.size(); @@ -493,7 +538,7 @@ struct Cascqaanalysis { posdau.tpcNClsFound(), negdau.tpcNClsFound(), bachelor.tpcNClsFound(), posdau.tpcNClsCrossedRows(), negdau.tpcNClsCrossedRows(), bachelor.tpcNClsCrossedRows(), posdau.hasTOF(), negdau.hasTOF(), bachelor.hasTOF(), - posdau.pt(), negdau.pt(), bachelor.pt(), -1, -1, casc.bachBaryonCosPA(), casc.bachBaryonDCAxyToPV(), evFlag, 1e3, 1e3); + posdau.pt(), negdau.pt(), bachelor.pt(), 0, -1, casc.bachBaryonCosPA(), casc.bachBaryonDCAxyToPV(), evFlag, 1e3, 1e3); } } } @@ -537,31 +582,30 @@ struct Cascqaanalysis { uint16_t nchFT0 = getGenNchInFT0Mregion(mcPartSlice); uint16_t nchFV0 = getGenNchInFV0Aregion(mcPartSlice); - int evType = 0; - registry.fill(HIST("hNEvents"), 11.5); // INEL - // Rec. collision associated with INEL>0 gen. one + EventTypeBin genEvType = kINEL; if (pwglf::isINELgtNmc(mcPartSlice, 0, pdgDB)) { - registry.fill(HIST("hNEvents"), 12.5); // INEL - evType++; + genEvType = kINELgt0; } - // Rec. collision associated with INEL>1 gen. one if (pwglf::isINELgtNmc(mcPartSlice, 1, pdgDB)) { - registry.fill(HIST("hNEvents"), 13.5); // INEL - evType++; + genEvType = kINELgt1; } - registry.fill(HIST("hCentFT0M_rec"), mcCollision.centFT0M(), evType); + const EventTypeBin recoEvType = getEventTypeBin(collision); + + registry.fill(HIST("hCentFT0M_rec"), mcCollision.centFT0M(), recoEvType); + registry.fill(HIST("hZCollisionRecVsGen"), collision.posZ(), mcCollision.posZ()); + registry.fill(HIST("hEventTypeRecVsGen"), recoEvType, genEvType); if (multQA) { - registry.fill(HIST("hNchFT0MPVContr"), nchFT0, nTracksPVcontr, evType); - registry.fill(HIST("hNchFV0APVContr"), nchFV0, nTracksPVcontr, evType); - registry.fill(HIST("hFT0MpvContr"), mcCollision.centFT0M(), nTracksPVcontr, evType); - registry.fill(HIST("hFV0ApvContr"), 0, nTracksPVcontr, evType); // mcCollision.centFV0A() to be added - registry.fill(HIST("hFT0Mglobal"), mcCollision.centFT0M(), nTracksGlobal, evType); - registry.fill(HIST("hFV0AFT0M"), 0, mcCollision.centFT0M(), evType); // mcCollision.centFV0A() to be added - registry.fill(HIST("hNchFT0Mglobal"), nchFT0, nTracksGlobal, evType); + registry.fill(HIST("hNchFT0MPVContr"), nchFT0, nTracksPVcontr, recoEvType); + registry.fill(HIST("hNchFV0APVContr"), nchFV0, nTracksPVcontr, recoEvType); + registry.fill(HIST("hFT0MpvContr"), mcCollision.centFT0M(), nTracksPVcontr, recoEvType); + registry.fill(HIST("hFV0ApvContr"), 0, nTracksPVcontr, recoEvType); // mcCollision.centFV0A() to be added + registry.fill(HIST("hFT0Mglobal"), mcCollision.centFT0M(), nTracksGlobal, recoEvType); + registry.fill(HIST("hFV0AFT0M"), 0, mcCollision.centFT0M(), recoEvType); // mcCollision.centFV0A() to be added + registry.fill(HIST("hNchFT0Mglobal"), nchFT0, nTracksGlobal, recoEvType); registry.fill(HIST("hFT0MFV0Asignal"), collision.multFT0A() + collision.multFT0C(), collision.multFV0A()); - registry.fill(HIST("hFT0MsignalPVContr"), collision.multFT0A() + collision.multFT0C(), nTracksPVcontr, evType); + registry.fill(HIST("hFT0MsignalPVContr"), collision.multFT0A() + collision.multFT0C(), nTracksPVcontr, recoEvType); } float lEventScale = scalefactor; @@ -575,16 +619,16 @@ struct Cascqaanalysis { registry.fill(HIST("hCandidateCounter"), 1.5); // passed topo cuts nCandSel++; // Check mc association - float lPDG = 1e3; + int mcPdgCode = 0; float genPt = 1e3; float genY = 1e3; float isPrimary = -1; if (casc.has_mcParticle()) { registry.fill(HIST("hCandidateCounter"), 2.5); // has associated MC particle auto cascmc = casc.mcParticle(); + mcPdgCode = cascmc.pdgCode(); if (std::abs(cascmc.pdgCode()) == PDG_t::kXiMinus || std::abs(cascmc.pdgCode()) == PDG_t::kOmegaMinus) { registry.fill(HIST("hCandidateCounter"), 3.5); // associated with Xi or Omega - lPDG = cascmc.pdgCode(); isPrimary = cascmc.isPhysicalPrimary() ? 1 : 0; genPt = cascmc.pt(); genY = cascmc.y(); @@ -615,7 +659,7 @@ struct Cascqaanalysis { posdau.tpcNClsFound(), negdau.tpcNClsFound(), bachelor.tpcNClsFound(), posdau.tpcNClsCrossedRows(), negdau.tpcNClsCrossedRows(), bachelor.tpcNClsCrossedRows(), posdau.hasTOF(), negdau.hasTOF(), bachelor.hasTOF(), - posdau.pt(), negdau.pt(), bachelor.pt(), lPDG, isPrimary, casc.bachBaryonCosPA(), casc.bachBaryonDCAxyToPV(), evFlag, genPt, genY); + posdau.pt(), negdau.pt(), bachelor.pt(), mcPdgCode, isPrimary, casc.bachBaryonCosPA(), casc.bachBaryonDCAxyToPV(), evFlag, genPt, genY); } } } @@ -629,34 +673,44 @@ struct Cascqaanalysis { void processMCgen(soa::Join::iterator const& mcCollision, // mcCollision.centFV0A() to be added aod::McParticles const& mcParticles, const soa::SmallGroups>& collisions, + BCsWithBcSels const&, DauTracks const&) { // All generated collisions registry.fill(HIST("hNEventsMC"), 0.5); - // Generated with accepted z vertex - if (std::fabs(mcCollision.posZ()) > cutzvertex) { + // Generated z-vertex selection + if (cutzvertexGen >= 0.f && std::fabs(mcCollision.posZ()) > cutzvertexGen) { return; } - registry.fill(HIST("hZCollisionGen"), mcCollision.posZ()); registry.fill(HIST("hNEventsMC"), 1.5); + const auto bc = mcCollision.bc_as(); + if (!acceptGeneratedEventBcBorderCuts(bc)) { + return; + } + registry.fill(HIST("hNEventsMC"), 2.5); + if (!acceptGeneratedEventRCT(bc)) { + return; + } + registry.fill(HIST("hZCollisionGen"), mcCollision.posZ()); + registry.fill(HIST("hNEventsMC"), 3.5); // Define the type of generated MC collision - int evType = 0; + EventTypeBin evType = kINEL; uint8_t flagsGen = 0; flagsGen |= o2::aod::myMCcascades::EvFlags::EvINEL; - registry.fill(HIST("hNEventsMC"), 2.5); + registry.fill(HIST("hNEventsMC"), 4.5); // Generated collision is INEL>0 if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { flagsGen |= o2::aod::myMCcascades::EvFlags::EvINELgt0; - evType++; - registry.fill(HIST("hNEventsMC"), 3.5); + evType = kINELgt0; + registry.fill(HIST("hNEventsMC"), 5.5); } // Generated collision is INEL>1 if (pwglf::isINELgtNmc(mcParticles, 1, pdgDB)) { flagsGen |= o2::aod::myMCcascades::EvFlags::EvINELgt1; - evType++; - registry.fill(HIST("hNEventsMC"), 4.5); + evType = kINELgt1; + registry.fill(HIST("hNEventsMC"), 6.5); } registry.fill(HIST("hCentFT0M_genMC"), mcCollision.centFT0M(), evType); @@ -695,7 +749,7 @@ struct Cascqaanalysis { registry.fill(HIST("hNchFT0MNAssocMCCollisions"), nchFT0, nAssocColl, evType); - if (numberOfContributors.size() == 2) { + if (numberOfContributors.size() == kNContributorsCorrelationSize) { std::sort(numberOfContributors.begin(), numberOfContributors.end()); registry.fill(HIST("hNContributorsCorrelation"), numberOfContributors[0], numberOfContributors[1]); } @@ -711,15 +765,15 @@ struct Cascqaanalysis { const auto evtReconstructedAndINELgt1 = std::count_if(selectedEvents.begin(), selectedEvents.end(), isAssocToINELgt1); switch (evType) { - case 0: { + case kINEL: { registry.fill(HIST("hNchFT0MNAssocMCCollisionsSameType"), nchFT0, evtReconstructedAndINEL, evType); break; } - case 1: { + case kINELgt0: { registry.fill(HIST("hNchFT0MNAssocMCCollisionsSameType"), nchFT0, evtReconstructedAndINELgt0, evType); break; } - case 2: { + case kINELgt1: { registry.fill(HIST("hNchFT0MNAssocMCCollisionsSameType"), nchFT0, evtReconstructedAndINELgt1, evType); break; } @@ -731,7 +785,7 @@ struct Cascqaanalysis { uint8_t flagsAssoc = 0; if (evtReconstructedAndINEL) { flagsAssoc |= o2::aod::myMCcascades::EvFlags::EvINEL; - registry.fill(HIST("hNEventsMC"), 5.5); + registry.fill(HIST("hNEventsMC"), 7.5); } if (evtReconstructedAndINELgt0) { flagsAssoc |= o2::aod::myMCcascades::EvFlags::EvINELgt0; diff --git a/PWGLF/TableProducer/Strangeness/doubleCascTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/doubleCascTreeCreator.cxx deleted file mode 100644 index 736cda47318..00000000000 --- a/PWGLF/TableProducer/Strangeness/doubleCascTreeCreator.cxx +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "PWGLF/DataModel/LFDoubleCascTables.h" -#include "PWGLF/DataModel/LFStrangenessTables.h" - -#include "Common/CCDB/EventSelectionParams.h" -#include "Common/Core/Zorro.h" -#include "Common/Core/ZorroSummary.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTPC.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -using Collisions = soa::Join::iterator; -using FullCascades = aod::CascDataExt; -using TracksFull = soa::Join; - -struct doubleCascCand { - float ptCasc1 = -999.f; // signed pt of the cascade - float etaCasc1 = -999.f; - float phiCasc1 = -999.f; - float cascDecLength1 = -999.f; - float omegaMassCasc1 = -999.f; - float xiMassCasc1 = -999.f; - float cosPACasc1 = -999.f; - float dcaBachPVCasc1 = -999.f; - float dcaV0BachCasc1 = -999.f; - float nSigmaKBach1 = -999.f; - - float ptCasc2 = -999.f; - float etaCasc2 = -999.f; - float phiCasc2 = -999.f; - float cascDecLength2 = -999.f; - float omegaMassCasc2 = -999.f; - float xiMassCasc2 = -999.f; - float cosPACasc2 = -999.f; - float dcaBachPVCasc2 = -999.f; - float dcaV0BachCasc2 = -999.f; - float nSigmaKBach2 = -999.f; - float doubleOmegaMass = -999.f; -}; - -struct doubleCascTreeCreator { - Produces doubleCascTable; - std::vector doubleCascCands; - Service ccdb; - o2::vertexing::DCAFitterN<2> fitter; - - int mRunNumber; - - Zorro zorro; - OutputObj zorroSummary{"zorroSummary"}; - - Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; - Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - - ConfigurableAxis centAxis{"centAxis", {106, 0, 106}, "binning for the centrality"}; - ConfigurableAxis zVtxAxis{"zVtxBins", {100, -20.f, 20.f}, "Binning for the vertex z in cm"}; - - // binning of (anti)lambda mass QA histograms - ConfigurableAxis massOmegaAxis{"massOmegaAxis", {400, o2::constants::physics::MassOmegaMinus - 0.05f, o2::constants::physics::MassOmegaMinus + 0.05}, "binning for the Omega invariant-mass"}; - ConfigurableAxis massXiAxis{"massXiAxis", {400, o2::constants::physics::MassXiMinus - 0.05f, o2::constants::physics::MassXiMinus + 0.05f}, "binning for the Xi invariant-mass"}; - - Configurable zVtxMax{"zVtxMax", 10.0f, "maximum z position of the primary vertex"}; - Configurable etaMax{"etaMax", 0.9f, "maximum eta"}; - ConfigurableAxis momAxis{"momAxisFine", {5.e2, 0.f, 5.f}, "momentum axis binning"}; - - Configurable cascPtMin{"cascPtMin", 1.f, "minimum (anti)casc pT (GeV/c)"}; - Configurable cascPtMax{"cascPtMax", 5.f, "maximum (anti)casc pT (GeV/c)"}; - - Configurable minNCrossedRows{"minNCrossedRows", 100, "Minimum number of crossed TPC rows"}; - Configurable minNITSClus{"minNITSClus", 0., "Minimum number of ITS clusters"}; - Configurable minNTPCClus{"minNTPCClus", 80, "Minimum number of TPC clusters"}; - Configurable maxNSharedTPCClus{"maxNSharedTPCClus", 5, "Maximum number of shared TPC clusters"}; - - Configurable minCascCosPA{"minCascCosPA", 0.99f, "Minimum cosine of the pointing angle of the cascade"}; - Configurable nSigmaTPCCut{"nSigmaTPCCut", 3.f, "Number of sigmas for the TPC PID"}; - Configurable dcaBachToPV{"dcaBachToPV", 0.05f, "DCA of the bachelor to the primary vertex"}; - Configurable dcaV0Bach{"dcaV0Bach", 1.f, "DCA between the V0 daughters"}; - Configurable mXiWindow{"mXiWindow", 0.02f, "mXiWindow"}; - Configurable mOmegaWindow{"mOmegaWindow", 0.01f, "mOmegaWindow"}; - - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - template - bool selectTrack(T const& track) - { - if (std::abs(track.eta()) > etaMax) { - return false; - } - if (track.itsNCls() < minNITSClus || - track.tpcNClsFound() < minNTPCClus || - track.tpcNClsCrossedRows() < minNCrossedRows || - track.tpcNClsCrossedRows() < 0.8 * track.tpcNClsFindable() || - track.tpcNClsShared() > maxNSharedTPCClus) { - return false; - } - return true; - } - - template - void initCCDB(Bc const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - auto timestamp = bc.timestamp(); - LOG(info) << "Retrieved GRP for timestamp " << timestamp; - mRunNumber = bc.runNumber(); - if (cfgSkimmedProcessing) { - zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fDoubleOmega,fOmegaXi"); - zorro.populateHistRegistry(histos, bc.runNumber()); - } - } - - void init(o2::framework::InitContext&) - { - mRunNumber = 0; - ccdb->setURL(ccdburl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - - zorroSummary.setObject(zorro.getZorroSummary()); - - // event QA - histos.add("QA/zVtx", ";#it{z}_{vtx} (cm);Entries", HistType::kTH1F, {zVtxAxis}); - histos.add("QA/massXi1", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + #pi^{-}) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massXiAxis}); - histos.add("QA/massOmega1", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Omega + #pi^{-}) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massOmegaAxis}); - histos.add("QA/massXi2", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + #pi^{-}) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massXiAxis}); - histos.add("QA/massOmega2", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Omega + #pi^{-}) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massOmegaAxis}); - } - - template - bool isSelectedCasc(C const& collision, T const&, FullCascades::iterator const& casc) - { - - auto bachelor = casc.bachelor_as(); - auto posDau = casc.posTrack_as(); - auto negDau = casc.negTrack_as(); - - if (!selectTrack(bachelor) || !selectTrack(posDau) || !selectTrack(negDau)) { - return false; - } - if (casc.sign() > 0) { - if (TMath::Abs(posDau.tpcNSigmaPi()) > nSigmaTPCCut || TMath::Abs(negDau.tpcNSigmaPr()) > nSigmaTPCCut) { - return false; - } - } else if (casc.sign() < 0) { - if (TMath::Abs(negDau.tpcNSigmaPi()) > nSigmaTPCCut || TMath::Abs(posDau.tpcNSigmaPr()) > nSigmaTPCCut) { - return false; - } - } - if (TMath::Abs(casc.dcabachtopv()) < dcaBachToPV) { - return false; - } - if (TMath::Abs(casc.dcacascdaughters()) > dcaV0Bach) { - return false; - } - if (casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()) < minCascCosPA) { - return false; - } - if (TMath::Abs(casc.eta()) > etaMax) { - return false; - } - // mass cuts - bool massInWindow = false; - if (casc.mOmega() > o2::constants::physics::MassOmegaMinus - mOmegaWindow && casc.mOmega() < o2::constants::physics::MassOmegaMinus + mOmegaWindow) { - massInWindow = true; - } - if (casc.mXi() > o2::constants::physics::MassXiMinus - mXiWindow && casc.mXi() < o2::constants::physics::MassXiMinus + mXiWindow) { - massInWindow = true; - } - if (!massInWindow) { - return false; - } - return true; - }; - - template - float doubleOmegaMass(T const&, FullCascades::iterator const& casc1, FullCascades::iterator const& casc2) - { - // the fake omega decay is the one with the smaller radius - auto& fakeOmega = casc1.cascradius() < casc2.cascradius() ? casc1 : casc2; - auto& realOmega = casc1.cascradius() < casc2.cascradius() ? casc2 : casc1; - auto kaon = fakeOmega.bachelor_as(); - float momKaon[3] = {kaon.px(), kaon.py(), kaon.pz()}; - float momLambda[3] = {fakeOmega.pxlambda(), fakeOmega.pylambda(), fakeOmega.pzlambda()}; - // now compute real Omega-lambda-kaon mass - float momTot[3] = {momKaon[0] + momLambda[0] + realOmega.px(), momKaon[1] + momLambda[1] + realOmega.py(), momKaon[2] + momLambda[2] + realOmega.pz()}; - float eK = std::sqrt(o2::constants::physics::MassKaonCharged * o2::constants::physics::MassKaonCharged + momKaon[0] * momKaon[0] + momKaon[1] * momKaon[1] + momKaon[2] * momKaon[2]); - float eL = std::sqrt(o2::constants::physics::MassLambda0 * o2::constants::physics::MassLambda0 + momLambda[0] * momLambda[0] + momLambda[1] * momLambda[1] + momLambda[2] * momLambda[2]); - float eO = std::sqrt(o2::constants::physics::MassOmegaMinus * o2::constants::physics::MassOmegaMinus + realOmega.px() * realOmega.px() + realOmega.py() * realOmega.py() + realOmega.pz() * realOmega.pz()); - float eTot = eK + eL + eO; - float mass = std::sqrt(eTot * eTot - momTot[0] * momTot[0] - momTot[1] * momTot[1] - momTot[2] * momTot[2]); - return mass; - } - - template - void fillDoubleCasc(C const& collision, T const& tracks, FullCascades const& cascades) - { - doubleCascCands.clear(); - - for (auto& casc1 : cascades) { - if (!isSelectedCasc(collision, tracks, casc1)) { - continue; - } - histos.fill(HIST("QA/massXi1"), casc1.pt(), casc1.mXi()); - histos.fill(HIST("QA/massOmega1"), casc1.pt(), casc1.mOmega()); - for (auto& casc2 : cascades) { - if (!isSelectedCasc(collision, tracks, casc2)) { - continue; - } - histos.fill(HIST("QA/massXi2"), casc2.pt(), casc2.mXi()); - histos.fill(HIST("QA/massOmega2"), casc2.pt(), casc2.mOmega()); - - // check that the cascades do not share any track - std::vector trackIdsCasc1 = {casc1.posTrackId(), casc1.negTrackId(), casc1.bachelorId()}; - std::vector trackIdsCasc2 = {casc2.posTrackId(), casc2.negTrackId(), casc2.bachelorId()}; - bool shareTrack = false; - for (auto id1 : trackIdsCasc1) { - for (auto id2 : trackIdsCasc2) { - if (id1 == id2) { - shareTrack = true; - break; - } - } - if (shareTrack) { - break; - } - } - if (shareTrack) { - continue; - } - - auto bach1 = casc1.bachelor_as(); - auto bach2 = casc2.bachelor_as(); - - doubleCascCand cand; - cand.ptCasc1 = casc1.pt(); - cand.etaCasc1 = casc1.eta(); - cand.phiCasc1 = casc1.phi(); - cand.cascDecLength1 = std::hypot(casc1.x() - collision.posX(), casc1.y() - collision.posY(), casc1.z() - collision.posZ()); - cand.omegaMassCasc1 = casc1.mOmega(); - cand.xiMassCasc1 = casc1.mXi(); - cand.cosPACasc1 = casc1.v0cosPA(collision.posX(), collision.posY(), collision.posZ()); - cand.dcaBachPVCasc1 = casc1.dcabachtopv(); - cand.dcaV0BachCasc1 = casc1.dcacascdaughters(); - cand.nSigmaKBach1 = bach1.tpcNSigmaKa(); - - cand.ptCasc2 = casc2.pt(); - cand.etaCasc2 = casc2.eta(); - cand.phiCasc2 = casc2.phi(); - cand.cascDecLength2 = std::hypot(casc2.x() - collision.posX(), casc2.y() - collision.posY(), casc2.z() - collision.posZ()); - cand.omegaMassCasc2 = casc2.mOmega(); - cand.xiMassCasc2 = casc2.mXi(); - cand.cosPACasc2 = casc2.v0cosPA(collision.posX(), collision.posY(), collision.posZ()); - cand.dcaBachPVCasc2 = casc2.dcabachtopv(); - cand.dcaV0BachCasc2 = casc2.dcacascdaughters(); - cand.nSigmaKBach2 = bach2.tpcNSigmaKa(); - - cand.doubleOmegaMass = doubleOmegaMass(tracks, casc1, casc2); - - doubleCascCands.push_back(cand); - } - } - }; - - void processData(Collisions const& collision, TracksFull const& tracks, FullCascades const& cascades, aod::BCsWithTimestamps const&) - { - auto bc = collision.bc_as(); - initCCDB(bc); - - if (!collision.sel8()) - return; - - if (std::abs(collision.posZ()) > zVtxMax) - return; - - if (!collision.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) - return; - - if (cfgSkimmedProcessing) { - zorro.isSelected(collision.bc_as().globalBC()); /// Just let Zorro do the accounting - } - histos.fill(HIST("QA/zVtx"), collision.posZ()); - fillDoubleCasc(collision, tracks, cascades); - - for (auto& cand : doubleCascCands) { - doubleCascTable( - cand.ptCasc1, - cand.etaCasc1, - cand.phiCasc1, - cand.cascDecLength1, - cand.omegaMassCasc1, - cand.xiMassCasc1, - cand.cosPACasc1, - cand.dcaBachPVCasc1, - cand.dcaV0BachCasc1, - cand.nSigmaKBach1, - cand.ptCasc2, - cand.etaCasc2, - cand.phiCasc2, - cand.cascDecLength2, - cand.omegaMassCasc2, - cand.xiMassCasc2, - cand.cosPACasc2, - cand.dcaBachPVCasc2, - cand.dcaV0BachCasc2, - cand.nSigmaKBach2, - cand.doubleOmegaMass); - } - } - PROCESS_SWITCH(doubleCascTreeCreator, processData, "process (Run 3)", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; -} diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx new file mode 100644 index 00000000000..459997de976 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaBuilder.cxx @@ -0,0 +1,321 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file doubleOmegaBuilder.cxx +/// \brief Builds the Omega, Lambda and direct-kaon candidates of the double-Omega analysis and stores +/// them in derived tables, consumed on the fly (or from saved derived data) by doubleOmegaTreeCreator +/// for the same-event candidates and the event mixing. + +#include "PWGLF/DataModel/LFDoubleOmegaTables.h" +#include "PWGLF/Utils/doubleOmegaBuilderHelper.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::pwglf::doubleomega; + +using CollisionsTable = soa::Join; +using TracksFull = soa::Join; + +struct doubleOmegaBuilder { + Produces dOmegaColls; + Produces dOmegaTracks; + Produces dOmegaOmegas; + Produces dOmegaLambdas; + Service ccdb{}; + + enum CollisionStep : uint8_t { + kAllCollisions, + kSelectedCollisions, + kWithOmega, + kStoredCollisions, + kNCollisionSteps + }; + + Preslice tracksPerCollision = aod::track::collisionId; + Preslice cascadesPerCollision = aod::cascade::collisionId; + Preslice v0sPerCollision = aod::v0::collisionId; + + DoubleOmegaBuilderHelper helper; + int mRunNumber = 0; + float mBz = 0.f; + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable cfgApplyEventSelection{"cfgApplyEventSelection", true, "Apply the standard collision event selection"}; + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrLUT), "Material correction for the V0/cascade fits"}; + + ConfigurableAxis zVtxAxis{"zVtxBins", {100, -20.f, 20.f}, "Binning for the vertex z in cm"}; + ConfigurableAxis massOmegaAxis{"massOmegaAxis", {400, o2::constants::physics::MassOmegaMinus - 0.05f, o2::constants::physics::MassOmegaMinus + 0.05f}, "binning for the Omega invariant-mass"}; + ConfigurableAxis massXiAxis{"massXiAxis", {400, o2::constants::physics::MassXiMinus - 0.05f, o2::constants::physics::MassXiMinus + 0.05f}, "binning for the Xi invariant-mass"}; + ConfigurableAxis massLambdaAxis{"massLambdaAxis", {400, o2::constants::physics::MassLambda0 - 0.05f, o2::constants::physics::MassLambda0 + 0.05f}, "binning for the Lambda invariant-mass"}; + ConfigurableAxis momAxis{"momAxisFine", {5.e2, 0.f, 5.f}, "momentum axis binning"}; + + // Production selections: loose, they can be tightened when the derived tables are read. + Configurable zVtxMax{"zVtxMax", 10.0f, "maximum z position of the primary vertex"}; + Configurable etaMax{"etaMax", 0.9f, "maximum eta"}; + Configurable minNCrossedRows{"minNCrossedRows", 100, "Minimum number of crossed TPC rows"}; + Configurable minNITSClus{"minNITSClus", 0., "Minimum number of ITS clusters"}; + Configurable minNTPCClus{"minNTPCClus", 80, "Minimum number of TPC clusters"}; + Configurable maxNSharedTPCClus{"maxNSharedTPCClus", 5, "Maximum number of shared TPC clusters"}; + Configurable nSigmaTPCCut{"nSigmaTPCCut", 3.f, "Number of sigmas for the TPC PID"}; + Configurable dcaBachToPV{"dcaBachToPV", 0.05f, "Minimum DCA of a cascade bachelor to the primary vertex"}; + Configurable dcaV0DauToPV{"dcaV0DauToPV", 0.05f, "Minimum DCA of Lambda daughters to the primary vertex"}; + Configurable dcaV0Bach{"dcaV0Bach", 1.f, "Maximum DCA between the V0 and cascade bachelor"}; + Configurable dcaLambdaDaughters{"dcaLambdaDaughters", 1.f, "Maximum DCA between Lambda daughters"}; + Configurable mOmegaWindow{"mOmegaWindow", 0.01f, "Omega mass window"}; + Configurable mLambdaWindow{"mLambdaWindow", 0.01f, "Lambda mass window"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext const&) + { + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + auto& selections = helper.selections; + selections.etaMax = etaMax; + selections.minNITSClus = minNITSClus; + selections.minNTPCClus = minNTPCClus; + selections.minNCrossedRows = minNCrossedRows; + selections.maxNSharedTPCClus = maxNSharedTPCClus; + selections.nSigmaTPCCut = nSigmaTPCCut; + selections.dcaBachToPV = dcaBachToPV; + selections.dcaV0DauToPV = dcaV0DauToPV; + selections.dcaV0Bach = dcaV0Bach; + selections.dcaLambdaDaughters = dcaLambdaDaughters; + selections.mOmegaWindow = mOmegaWindow; + selections.mLambdaWindow = mLambdaWindow; + helper.initFitters(); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); + + zorroSummary.setObject(zorro.getZorroSummary()); + + histos.add("QA/zVtx", ";#it{z}_{vtx} (cm);Entries", HistType::kTH1F, {zVtxAxis}); + histos.add("QA/massXi", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massXiAxis}); + histos.add("QA/massOmega", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + K) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massOmegaAxis}); + histos.add("QA/massLambda", ";#it{p}_{T} (GeV/#it{c});#it{M}(p + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massLambdaAxis}); + histos.add("QA/collisions", ";;Collisions", HistType::kTH1F, {{kNCollisionSteps, -0.5f, static_cast(kNCollisionSteps) - 0.5f}}); + auto collisions = histos.get(HIST("QA/collisions")); + collisions->GetXaxis()->SetBinLabel(kAllCollisions + 1, "All"); + collisions->GetXaxis()->SetBinLabel(kSelectedCollisions + 1, "Event selection"); + collisions->GetXaxis()->SetBinLabel(kWithOmega + 1, "With #Omega"); + collisions->GetXaxis()->SetBinLabel(kStoredCollisions + 1, "With #Omega, #Lambda and K (stored)"); + } + + template + void initCCDB(Bc const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + auto* grpmag = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + if (!grpmag) { + LOG(fatal) << "Could not retrieve GRPMagField for timestamp " << bc.timestamp(); + } + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = o2::base::Propagator::Instance()->getNominalBz(); + helper.setBz(mBz); + + if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { + auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp("GLO/Param/MatLUT", bc.timestamp())); + if (!lut) { + LOG(fatal) << "Could not retrieve material LUT for timestamp " << bc.timestamp(); + } + o2::base::Propagator::Instance()->setMatLUT(lut); + } + + mRunNumber = bc.runNumber(); + if (cfgSkimmedProcessing) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fDoubleOmega,fOmegaXi"); + zorro.populateHistRegistry(histos, bc.runNumber()); + } + } + + template + bool acceptCollision(C const& collision) + { + auto bc = collision.template bc_as(); + initCCDB(bc); + + if (cfgApplyEventSelection && + (!collision.sel8() || + std::abs(collision.posZ()) > zVtxMax || + !collision.selection_bit(aod::evsel::kNoITSROFrameBorder) || + !collision.selection_bit(aod::evsel::kNoTimeFrameBorder))) { + return false; + } + if (cfgSkimmedProcessing) { + zorro.isSelected(bc.globalBC()); + } + histos.fill(HIST("QA/zVtx"), collision.posZ()); + return true; + } + + void fillCandidateQA(CollisionCandidates const& candidates) + { + for (const auto& omega : candidates.omegas) { + const float omegaPt = std::hypot(omega.px, omega.py); + histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); + histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); + } + for (const auto& lambdas : candidates.lambdas) { + for (const auto& lambda : lambdas) { + histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); + } + } + } + + // Writes the candidates of a collision. Tracks are written once per collision: the direct-kaon + // candidates first, then the Omega and Lambda daughters not written yet. + template + void writeCollision(C const& collision, CollisionCandidates const& candidates, T const& tracks) + { + dOmegaColls(collision.posX(), collision.posY(), collision.posZ(), collision.multFT0M(), mRunNumber, mBz); + const auto collisionIndex = static_cast(dOmegaColls.lastIndex()); + + std::unordered_map trackRows; // Track index -> DOmegaTracks row. + const auto writeTrack = [&](int64_t trackId, bool isKaon) { + const auto [row, inserted] = trackRows.try_emplace(trackId, -1); + if (!inserted) { + return row->second; + } + const auto track = tracks.rawIteratorAt(trackId); + float dcaXY = -999.f; + helper.dcaXYToPV(collision, track, dcaXY); + const auto trackParCov = getTrackParCov(track); + const auto& cov = trackParCov.getCov(); + dOmegaTracks(collisionIndex, + trackParCov.getX(), trackParCov.getAlpha(), trackParCov.getY(), trackParCov.getZ(), trackParCov.getSnp(), trackParCov.getTgl(), trackParCov.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + track.hasTPC() ? track.tpcNSigmaKa() : -999.f, + track.hasTOF() ? track.tofNSigmaKa() : -999.f, + static_cast(DoubleOmegaBuilderHelper::detectorMap(track)), + dcaXY, + isKaon); + row->second = static_cast(dOmegaTracks.lastIndex()); + return row->second; + }; + + for (const auto& kaons : candidates.kaons) { + for (const auto& kaon : kaons) { + writeTrack(kaon.trackId, true); + } + } + for (const auto& omega : candidates.omegas) { + const auto posTrackRow = writeTrack(omega.posTrackId, false); + const auto negTrackRow = writeTrack(omega.negTrackId, false); + const auto bachelorRow = writeTrack(omega.bachelorId, false); + const auto& parent = omega.parentTrack; + const auto& cov = parent.getCov(); + dOmegaOmegas(collisionIndex, posTrackRow, negTrackRow, bachelorRow, + parent.getX(), parent.getAlpha(), parent.getY(), parent.getZ(), parent.getSnp(), parent.getTgl(), parent.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + omega.x, omega.y, omega.z, + omega.px, omega.py, omega.pz, + omega.massOmega, omega.massXi, + omega.dcaDaughters, + omega.sign); + } + for (const int8_t charge : {int8_t{-1}, int8_t{1}}) { + for (const auto& lambda : candidates.lambdas[DoubleOmegaBuilderHelper::chargeIndex(charge)]) { + const auto posTrackRow = writeTrack(lambda.posTrackId, false); + const auto negTrackRow = writeTrack(lambda.negTrackId, false); + const auto& parent = lambda.parentTrack; + const auto& cov = parent.getCov(); + dOmegaLambdas(collisionIndex, posTrackRow, negTrackRow, + parent.getX(), parent.getAlpha(), parent.getY(), parent.getZ(), parent.getSnp(), parent.getTgl(), parent.getQ2Pt(), + cov[0], cov[1], cov[2], cov[3], cov[4], cov[5], cov[6], cov[7], cov[8], cov[9], cov[10], cov[11], cov[12], cov[13], cov[14], + lambda.decayVertex[0], lambda.decayVertex[1], lambda.decayVertex[2], + lambda.candidate.px, lambda.candidate.py, lambda.candidate.pz, + lambda.candidate.mass, + lambda.dcaDaughters, + charge); + } + } + } + + void processData(CollisionsTable const& collisions, + TracksFull const& tracks, + aod::V0s const& v0s, + aod::Cascades const& cascades, + aod::BCsWithTimestamps const&) + { + for (const auto& collision : collisions) { + histos.fill(HIST("QA/collisions"), kAllCollisions); + if (!acceptCollision(collision)) { + continue; + } + histos.fill(HIST("QA/collisions"), kSelectedCollisions); + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + auto cascadesThisCollision = cascades.sliceBy(cascadesPerCollision, collision.globalIndex()); + auto v0sThisCollision = v0s.sliceBy(v0sPerCollision, collision.globalIndex()); + v0sThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&v0s); + const auto candidates = helper.buildCollisionCandidates(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, true); + fillCandidateQA(candidates); + if (candidates.omegas.empty()) { + continue; + } + histos.fill(HIST("QA/collisions"), kWithOmega); + if (!DoubleOmegaBuilderHelper::hasOmegaLambdaKaon(candidates)) { + continue; + } + histos.fill(HIST("QA/collisions"), kStoredCollisions); + writeCollision(collision, candidates, tracks); + } + } + PROCESS_SWITCH(doubleOmegaBuilder, processData, "Build the Omega, Lambda and kaon candidates and store them in derived tables", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx new file mode 100644 index 00000000000..8622f2e626a --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/doubleOmegaTreeCreator.cxx @@ -0,0 +1,1519 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "PWGLF/DataModel/LFDoubleOmegaTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/doubleOmegaBuilderHelper.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/Zorro.h" +#include "Common/Core/ZorroSummary.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::pwglf::doubleomega; + +using CollisionsTable = soa::Join; +using Collisions = CollisionsTable::iterator; +using CollisionsMC = soa::Join; +using FullCascades = aod::CascDataExt; +// Innermost-update tracks: DCAs to the PV are recomputed in this task by propagation, +// so neither the propagated tracks nor TracksDCA are needed. +using TracksFull = soa::Join; +using TracksFullMC = soa::Join; + +struct DoubleOmegaMCInfo { + int64_t motherId = -1; + float pt = -999.f; + float eta = -999.f; + float phi = -999.f; + float decayLength = -999.f; + int pdgCode = 0; + // Generated vertices: double-Omega production point (primary vertex), double-Omega + // decay point, and Omega decay point. + std::array genProductionVertex{-999.f, -999.f, -999.f}; + std::array genDecayVertex{-999.f, -999.f, -999.f}; + std::array genOmegaDecayVertex{-999.f, -999.f, -999.f}; + int8_t sign = 0; +}; + +// Same-event double-Omega candidate used as a seed of the candidate-level event mixing. Seeds are +// kept in a pool across dataframes, so they store copies of their daughters, with the collision and +// track indices made unique across dataframes (see uniqueIndex). +struct MixingSeed { + int64_t collisionId = -1; + std::array primaryVertex{}; + float bz = 0.f; + BuiltOmegaCandidate omega; + BuiltLambdaCandidate lambda; + BuiltKaonCandidate kaon; +}; + +struct doubleOmegaTreeCreator { + Produces doubleOmegaTable; + Produces doubleOmegaTableMC; + Produces doubleOmegaMixedTable; + Service ccdb{}; + + static constexpr int kDoubleOmegaPdg = 1060020020; + enum FindabilityStep : uint8_t { + kAllGenerated, + kOmegaLambdaKaon, + kTwoLambdasTwoKaons, + kFinalState, + kFindable, + kFindableSelectedTracks, + kFindableCascadeAndV0, + kFindableCascadeAndV0SelectedTracks, + kNFindabilitySteps + }; + enum MixingType : uint8_t { + kSameEvent = 0, + kMixedOmega, // Lambda and kaon from the same collision, Omega from another one + kMixedLambda, // Omega and kaon from the same collision, Lambda from another one + kMixedKaon, // Omega and Lambda from the same collision, kaon from another one + kMixedAll, // Omega, Lambda and kaon from three different collisions + kNMixingTypes + }; + std::vector reconstructedDoubleOmegaIds; + + Preslice tracksPerCollision = aod::track::collisionId; + Preslice rawCascadesPerCollision = aod::cascade::collisionId; + Preslice rawV0sPerCollision = aod::v0::collisionId; + Preslice derivedTracksPerCollision = aod::domega::dOmegaCollId; + Preslice derivedOmegasPerCollision = aod::domega::dOmegaCollId; + Preslice derivedLambdasPerCollision = aod::domega::dOmegaCollId; + + int mRunNumber = 0; + float mBz = 0.f; + DoubleOmegaBuilderHelper helper; + + Zorro zorro; + OutputObj zorroSummary{"zorroSummary"}; + + Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; + Configurable cfgApplyEventSelection{"cfgApplyEventSelection", true, "Apply the standard collision event selection"}; + Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrLUT), "Material correction for the raw V0/cascade fits"}; + + ConfigurableAxis zVtxAxis{"zVtxBins", {100, -20.f, 20.f}, "Binning for the vertex z in cm"}; + ConfigurableAxis massOmegaAxis{"massOmegaAxis", {400, o2::constants::physics::MassOmegaMinus - 0.05f, o2::constants::physics::MassOmegaMinus + 0.05f}, "binning for the Omega invariant-mass"}; + ConfigurableAxis massXiAxis{"massXiAxis", {400, o2::constants::physics::MassXiMinus - 0.05f, o2::constants::physics::MassXiMinus + 0.05f}, "binning for the Xi invariant-mass"}; + ConfigurableAxis massLambdaAxis{"massLambdaAxis", {400, o2::constants::physics::MassLambda0 - 0.05f, o2::constants::physics::MassLambda0 + 0.05f}, "binning for the Lambda invariant-mass"}; + ConfigurableAxis momAxis{"momAxisFine", {5.e2, 0.f, 5.f}, "momentum axis binning"}; + + Configurable zVtxMax{"zVtxMax", 10.0f, "maximum z position of the primary vertex"}; + Configurable etaMax{"etaMax", 0.9f, "maximum eta"}; + Configurable cascPtMin{"cascPtMin", 1.f, "minimum (anti)cascade pT (GeV/c)"}; + Configurable cascPtMax{"cascPtMax", 5.f, "maximum (anti)cascade pT (GeV/c)"}; + + Configurable minNCrossedRows{"minNCrossedRows", 100, "Minimum number of crossed TPC rows"}; + Configurable minNITSClus{"minNITSClus", 0., "Minimum number of ITS clusters"}; + Configurable minNTPCClus{"minNTPCClus", 80, "Minimum number of TPC clusters"}; + Configurable maxNSharedTPCClus{"maxNSharedTPCClus", 5, "Maximum number of shared TPC clusters"}; + + Configurable nSigmaTPCCut{"nSigmaTPCCut", 3.f, "Number of sigmas for the TPC PID"}; + Configurable dcaBachToPV{"dcaBachToPV", 0.05f, "Minimum DCA of a cascade bachelor to the primary vertex"}; + Configurable dcaKaonToPV{"dcaKaonToPV", 0.05f, "Minimum absolute transverse DCA of the direct kaon to the primary vertex"}; + Configurable dcaOmegaToPV{"dcaOmegaToPV", 0.f, "Minimum absolute transverse DCA of the Omega to the primary vertex"}; + Configurable dcaDirectLambdaToPV{"dcaDirectLambdaToPV", 0.f, "Minimum absolute transverse DCA of the direct Lambda to the primary vertex"}; + Configurable dcaV0DauToPV{"dcaV0DauToPV", 0.05f, "Minimum DCA of Lambda daughters to the primary vertex"}; + Configurable dcaV0Bach{"dcaV0Bach", 1.f, "Maximum DCA between the V0 and cascade bachelor"}; + Configurable dcaLambdaDaughters{"dcaLambdaDaughters", 1.f, "Maximum DCA between Lambda daughters"}; + Configurable mXiWindow{"mXiWindow", 0.02f, "Xi mass window used by the cascade compatibility mode"}; + Configurable mOmegaWindow{"mOmegaWindow", 0.01f, "Omega mass window"}; + Configurable mLambdaWindow{"mLambdaWindow", 0.01f, "Lambda mass window"}; + Configurable maxDoubleOmegaMass{"maxDoubleOmegaMass", 3.6f, "Maximum double-Omega invariant mass (GeV/c^2)"}; + Configurable minCosPAOmega{"minCosPAOmega", -1.f, "Minimum Omega cosPA relative to the double-Omega decay vertex"}; + Configurable minCosPADirectLambda{"minCosPADirectLambda", -1.f, "Minimum direct-Lambda cosPA relative to the double-Omega decay vertex"}; + Configurable minCosPADoubleOmega{"minCosPADoubleOmega", -1.f, "Minimum double-Omega cosPA relative to the primary vertex"}; + Configurable minDoubleOmegaDecayRadius{"minDoubleOmegaDecayRadius", 1.f, "Minimum double-Omega transverse decay radius in cm"}; + + struct : ConfigurableGroup { + std::string prefix = "mixingOpts"; + Configurable nMixingCollisions{"nMixingCollisions", 5, "Number of neighbouring collisions in the same bin each collision is mixed with"}; + Configurable mixingTypes{"mixingTypes", 0b1111, "Bitmask of the mixed backgrounds: 1 Omega, 2 Lambda, 4 kaon from another collision, 8 all from different collisions"}; + Configurable maxDeltaPVz{"maxDeltaPVz", 1.f, "Maximum difference of the primary-vertex z among the mixed collisions (cm)"}; + ConfigurableAxis binsVtxZ{"binsVtxZ", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - primary-vertex z (cm)"}; + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {VARIABLE_WIDTH, 0.f, 20.f, 50.f, 100.f, 200.f, 500.f, 1.e5f}, "Mixing bins - FT0M multiplicity"}; + // Candidate-level mixing (processDerivedMixingCandidates) + Configurable nMixingCandidates{"nMixingCandidates", 10, "Number of same-event candidates from other collisions each candidate is mixed with, per (R, phi, charge) bin"}; + Configurable poolDepth{"poolDepth", 50, "Number of same-event candidates kept in the mixing pool per (R, phi, charge) bin, across dataframes"}; + Configurable monitoringInterval{"monitoringInterval", 50, "Log the mixing pool size, memory and timing every this many dataframes (0: never)"}; + ConfigurableAxis binsRadius{"binsRadius", {VARIABLE_WIDTH, 0.f, 0.25f, 0.5f, 1.f, 2.f, 4.f, 10.f, 40.f}, "Mixing bins - double-Omega decay radius (cm)"}; + ConfigurableAxis binsPhi{"binsPhi", {VARIABLE_WIDTH, -o2::constants::math::PI, -2.f * o2::constants::math::PI / 3.f, -o2::constants::math::PI / 3.f, 0.f, o2::constants::math::PI / 3.f, 2.f * o2::constants::math::PI / 3.f, o2::constants::math::PI}, "Mixing bins - double-Omega momentum azimuth (rad)"}; + } mixingOpts; + + using DerivedMixingBinning = ColumnBinningPolicy; + static constexpr float kBzTolerance = 1.e-3f; // kG + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + template + void initCCDB(Bc const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + auto* grpmag = ccdb->getForTimeStamp("GLO/Config/GRPMagField", bc.timestamp()); + if (!grpmag) { + LOG(fatal) << "Could not retrieve GRPMagField for timestamp " << bc.timestamp(); + } + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = o2::base::Propagator::Instance()->getNominalBz(); + helper.setBz(mBz); + + if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { + auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForTimeStamp("GLO/Param/MatLUT", bc.timestamp())); + if (!lut) { + LOG(fatal) << "Could not retrieve material LUT for timestamp " << bc.timestamp(); + } + o2::base::Propagator::Instance()->setMatLUT(lut); + } + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); + + mRunNumber = bc.runNumber(); + if (cfgSkimmedProcessing) { + zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), "fDoubleOmega,fOmegaXi"); + zorro.populateHistRegistry(histos, bc.runNumber()); + } + } + + void printConfig() const + { + LOG(info) << "doubleOmegaTreeCreator runtime configuration (boolean values: 0=false, 1=true):"; + const auto printValue = [](auto const& config) { + LOG(info) << config.name << " = " << config.value; + }; + const auto printAxis = [](ConfigurableAxis const& axis) { + std::ostringstream values; + values << '['; + for (size_t i = 0; i < axis.value.size(); ++i) { + if (i > 0) { + values << ", "; + } + values << axis.value[i]; + } + values << ']'; + LOG(info) << axis.name << " = " << values.str(); + }; + + printValue(cfgSkimmedProcessing); + printValue(cfgApplyEventSelection); + printValue(ccdburl); + printValue(cfgMaterialCorrection); + printAxis(zVtxAxis); + printAxis(massOmegaAxis); + printAxis(massXiAxis); + printAxis(massLambdaAxis); + printAxis(momAxis); + printValue(zVtxMax); + printValue(etaMax); + printValue(cascPtMin); + printValue(cascPtMax); + printValue(minNCrossedRows); + printValue(minNITSClus); + printValue(minNTPCClus); + printValue(maxNSharedTPCClus); + printValue(nSigmaTPCCut); + printValue(dcaBachToPV); + printValue(dcaKaonToPV); + printValue(dcaOmegaToPV); + printValue(dcaDirectLambdaToPV); + printValue(dcaV0DauToPV); + printValue(dcaV0Bach); + printValue(dcaLambdaDaughters); + printValue(mXiWindow); + printValue(mOmegaWindow); + printValue(mLambdaWindow); + printValue(maxDoubleOmegaMass); + printValue(minCosPAOmega); + printValue(minCosPADirectLambda); + printValue(minCosPADoubleOmega); + printValue(minDoubleOmegaDecayRadius); + printValue(mixingOpts.nMixingCollisions); + printValue(mixingOpts.mixingTypes); + printValue(mixingOpts.maxDeltaPVz); + printAxis(mixingOpts.binsVtxZ); + printAxis(mixingOpts.binsMultiplicity); + printValue(mixingOpts.nMixingCandidates); + printValue(mixingOpts.poolDepth); + printValue(mixingOpts.monitoringInterval); + printAxis(mixingOpts.binsRadius); + printAxis(mixingOpts.binsPhi); + printValue(doprocessData); + printValue(doprocessDataFromCascades); + printValue(doprocessMC); + printValue(doprocessFindableTracks); + printValue(doprocessDerivedData); + printValue(doprocessDerivedMixing); + printValue(doprocessDerivedMixingCandidates); + } + + void init(InitContext const&) + { + printConfig(); + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + auto& selections = helper.selections; + selections.etaMax = etaMax; + selections.minNITSClus = minNITSClus; + selections.minNTPCClus = minNTPCClus; + selections.minNCrossedRows = minNCrossedRows; + selections.maxNSharedTPCClus = maxNSharedTPCClus; + selections.nSigmaTPCCut = nSigmaTPCCut; + selections.dcaBachToPV = dcaBachToPV; + selections.dcaKaonToPV = dcaKaonToPV; + selections.dcaOmegaToPV = dcaOmegaToPV; + selections.dcaDirectLambdaToPV = dcaDirectLambdaToPV; + selections.dcaV0DauToPV = dcaV0DauToPV; + selections.dcaV0Bach = dcaV0Bach; + selections.dcaLambdaDaughters = dcaLambdaDaughters; + selections.mOmegaWindow = mOmegaWindow; + selections.mLambdaWindow = mLambdaWindow; + selections.maxDoubleOmegaMass = maxDoubleOmegaMass; + selections.minCosPAOmega = minCosPAOmega; + selections.minCosPADirectLambda = minCosPADirectLambda; + selections.minCosPADoubleOmega = minCosPADoubleOmega; + selections.minDoubleOmegaDecayRadius = minDoubleOmegaDecayRadius; + helper.initFitters(); + helper.setMatCorrType(static_cast(cfgMaterialCorrection.value)); + + zorroSummary.setObject(zorro.getZorroSummary()); + + histos.add("QA/zVtx", ";#it{z}_{vtx} (cm);Entries", HistType::kTH1F, {zVtxAxis}); + histos.add("QA/massXi", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massXiAxis}); + histos.add("QA/massOmega", ";#it{p}_{T} (GeV/#it{c});#it{M}(#Lambda + K) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massOmegaAxis}); + histos.add("QA/massLambda", ";#it{p}_{T} (GeV/#it{c});#it{M}(p + #pi) (GeV/#it{c}^{2});Entries", HistType::kTH2F, {momAxis, massLambdaAxis}); + histos.add("MC/generatedAndFindable", ";Double-#Omega;Entries", HistType::kTH1F, {{kNFindabilitySteps, -0.5f, static_cast(kNFindabilitySteps) - 0.5f}}); + auto generatedAndFindable = histos.get(HIST("MC/generatedAndFindable")); + generatedAndFindable->GetXaxis()->SetBinLabel(kAllGenerated + 1, "All generated"); + generatedAndFindable->GetXaxis()->SetBinLabel(kOmegaLambdaKaon + 1, "#Omega + #Lambda + K"); + generatedAndFindable->GetXaxis()->SetBinLabel(kTwoLambdasTwoKaons + 1, "2#Lambda + 2K"); + generatedAndFindable->GetXaxis()->SetBinLabel(kFinalState + 1, "2p + 2#pi^{-} + 2K^{-} (and c.c.)"); + generatedAndFindable->GetXaxis()->SetBinLabel(kFindable + 1, "Findable"); + generatedAndFindable->GetXaxis()->SetBinLabel(kFindableSelectedTracks + 1, "Findable, selected tracks"); + generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0 + 1, "Findable cascade + V0"); + generatedAndFindable->GetXaxis()->SetBinLabel(kFindableCascadeAndV0SelectedTracks + 1, "Findable cascade + V0, selected tracks"); + + if (doprocessDerivedMixingCandidates) { + histos.add("Mixing/hSeedRadiusPhi", ";Double-#Omega decay radius (cm);Double-#Omega #varphi (rad);Same-event candidates", HistType::kTH2F, {mixingOpts.binsRadius, mixingOpts.binsPhi}); + // Resource monitoring of the candidate-level mixing, one entry per dataframe. + histos.add("Mixing/hProcessingTime", ";Processing time per dataframe (ms);Dataframes", HistType::kTH1F, {{500, 0.f, 5000.f}}); + histos.add("Mixing/hPoolSeeds", ";Seeds in the mixing pool;Dataframes", HistType::kTH1F, {{500, 0.f, 50000.f}}); + histos.add("Mixing/hPoolMemory", ";Estimated mixing-pool memory (MB);Dataframes", HistType::kTH1F, {{500, 0.f, 50.f}}); + histos.add("Mixing/hProcessingTimeVsPoolSeeds", ";Seeds in the mixing pool;Processing time per dataframe (ms)", HistType::kTH2F, {{100, 0.f, 50000.f}, {100, 0.f, 5000.f}}); + } + if (doprocessDerivedMixing || doprocessDerivedMixingCandidates) { + histos.add("Mixing/hMixedCollisions", ";;Entries", HistType::kTH1F, {{4, -0.5f, 3.5f}}); + auto mixedCollisions = histos.get(HIST("Mixing/hMixedCollisions")); + mixedCollisions->GetXaxis()->SetBinLabel(1, "Pairs"); + mixedCollisions->GetXaxis()->SetBinLabel(2, "Pairs, #Delta#it{z}_{PV}, same #it{B}"); + mixedCollisions->GetXaxis()->SetBinLabel(3, "Triples"); + mixedCollisions->GetXaxis()->SetBinLabel(4, "Triples, #Delta#it{z}_{PV}, same #it{B}"); + histos.add("Mixing/hMixedCandidates", ";;Entries", HistType::kTH1F, {{kNMixingTypes - 1, 0.5f, static_cast(kNMixingTypes) - 0.5f}}); + auto mixedCandidates = histos.get(HIST("Mixing/hMixedCandidates")); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedOmega, "#Omega mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedLambda, "#Lambda mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedKaon, "K mixed"); + mixedCandidates->GetXaxis()->SetBinLabel(kMixedAll, "All mixed"); + } + } + + template + int64_t getLambdaMCLabel(Track const& posTrack, Track const& negTrack, int8_t sign) + { + if (!posTrack.has_mcParticle() || !negTrack.has_mcParticle()) { + return -1; + } + + auto mcPosTrack = posTrack.template mcParticle_as(); + auto mcNegTrack = negTrack.template mcParticle_as(); + const int expectedPosPdg = sign < 0 ? PDG_t::kProton : PDG_t::kPiPlus; + const int expectedNegPdg = sign < 0 ? PDG_t::kPiMinus : PDG_t::kProtonBar; + const int expectedLambdaPdg = sign < 0 ? PDG_t::kLambda0 : PDG_t::kLambda0Bar; + if (mcPosTrack.pdgCode() != expectedPosPdg || mcNegTrack.pdgCode() != expectedNegPdg) { + return -1; + } + + for (const auto& posMother : mcPosTrack.template mothers_as()) { + if (posMother.pdgCode() != expectedLambdaPdg) { + continue; + } + for (const auto& negMother : mcNegTrack.template mothers_as()) { + if (posMother.globalIndex() == negMother.globalIndex()) { + return posMother.globalIndex(); + } + } + } + return -1; + } + + template + int64_t getOmegaMCLabel(Track const& posTrack, Track const& negTrack, Track const& bachelorTrack, + int8_t sign, McParticles const& mcParticles) + { + const int64_t lambdaLabel = getLambdaMCLabel(posTrack, negTrack, sign); + if (lambdaLabel < 0 || !bachelorTrack.has_mcParticle()) { + return -1; + } + + auto mcLambda = mcParticles.rawIteratorAt(lambdaLabel); + auto mcBachelor = bachelorTrack.template mcParticle_as(); + const int expectedBachelorPdg = sign < 0 ? PDG_t::kKMinus : PDG_t::kKPlus; + const int expectedOmegaPdg = sign < 0 ? PDG_t::kOmegaMinus : PDG_t::kOmegaPlusBar; + if (mcBachelor.pdgCode() != expectedBachelorPdg) { + return -1; + } + + for (const auto& lambdaMother : mcLambda.template mothers_as()) { + if (lambdaMother.pdgCode() != expectedOmegaPdg) { + continue; + } + for (const auto& bachelorMother : mcBachelor.template mothers_as()) { + if (lambdaMother.globalIndex() == bachelorMother.globalIndex()) { + return lambdaMother.globalIndex(); + } + } + } + return -1; + } + + template + bool getMCInfo(DoubleOmegaMCInfo& mcInfo, + Track const& omegaPosTrack, Track const& omegaNegTrack, Track const& omegaBachelorTrack, + Track const& lambdaPosTrack, Track const& lambdaNegTrack, Track const& directKaonTrack, + int8_t sign, McParticles const& mcParticles) + { + const int64_t omegaLabel = getOmegaMCLabel(omegaPosTrack, omegaNegTrack, omegaBachelorTrack, sign, mcParticles); + const int64_t lambdaLabel = getLambdaMCLabel(lambdaPosTrack, lambdaNegTrack, sign); + if (omegaLabel < 0 || lambdaLabel < 0 || !directKaonTrack.has_mcParticle()) { + return false; + } + + auto mcOmega = mcParticles.rawIteratorAt(omegaLabel); + auto mcLambda = mcParticles.rawIteratorAt(lambdaLabel); + auto mcKaon = directKaonTrack.template mcParticle_as(); + const int expectedKaonPdg = sign < 0 ? PDG_t::kKMinus : PDG_t::kKPlus; + const int expectedDoubleOmegaPdg = sign < 0 ? kDoubleOmegaPdg : -kDoubleOmegaPdg; + if (mcKaon.pdgCode() != expectedKaonPdg) { + return false; + } + + for (const auto& omegaMother : mcOmega.template mothers_as()) { + if (omegaMother.pdgCode() != expectedDoubleOmegaPdg) { + continue; + } + for (const auto& lambdaMother : mcLambda.template mothers_as()) { + if (omegaMother.globalIndex() != lambdaMother.globalIndex()) { + continue; + } + for (const auto& kaonMother : mcKaon.template mothers_as()) { + if (omegaMother.globalIndex() == kaonMother.globalIndex()) { + mcInfo.motherId = omegaMother.globalIndex(); + mcInfo.pt = omegaMother.pt(); + mcInfo.eta = omegaMother.eta(); + mcInfo.phi = omegaMother.phi(); + mcInfo.decayLength = std::hypot(mcOmega.vx() - omegaMother.vx(), + mcOmega.vy() - omegaMother.vy(), + mcOmega.vz() - omegaMother.vz()); + mcInfo.pdgCode = omegaMother.pdgCode(); + mcInfo.sign = sign; + return true; + } + } + } + } + return false; + } + + template + bool getGeneratedMCInfo(DoubleOmegaMCInfo& info, McParticle const& particle) + { + if (std::abs(particle.pdgCode()) != kDoubleOmegaPdg) { + return false; + } + + const bool isMatter = particle.pdgCode() > 0; + const int expectedOmegaPdg = isMatter ? PDG_t::kOmegaMinus : PDG_t::kOmegaPlusBar; + const int expectedLambdaPdg = isMatter ? PDG_t::kLambda0 : PDG_t::kLambda0Bar; + const int expectedKaonPdg = isMatter ? PDG_t::kKMinus : PDG_t::kKPlus; + bool foundOmega = false; + bool foundLambda = false; + bool foundKaon = false; + std::array decayVertex{0.f, 0.f, 0.f}; + std::array omegaDecayVertex{-999.f, -999.f, -999.f}; + + for (const auto& daughter : particle.template daughters_as()) { + if (daughter.pdgCode() == expectedOmegaPdg) { + foundOmega = true; + decayVertex = {daughter.vx(), daughter.vy(), daughter.vz()}; + // The Omega decay point is the production point of any of its daughters. + for (const auto& omegaDaughter : daughter.template daughters_as()) { + omegaDecayVertex = {omegaDaughter.vx(), omegaDaughter.vy(), omegaDaughter.vz()}; + break; + } + } else if (daughter.pdgCode() == expectedLambdaPdg) { + foundLambda = true; + } else if (daughter.pdgCode() == expectedKaonPdg) { + foundKaon = true; + } + } + if (!foundOmega || !foundLambda || !foundKaon) { + return false; + } + + info.motherId = particle.globalIndex(); + info.pt = particle.pt(); + info.eta = particle.eta(); + info.phi = particle.phi(); + info.decayLength = std::hypot(decayVertex[0] - particle.vx(), + decayVertex[1] - particle.vy(), + decayVertex[2] - particle.vz()); + info.pdgCode = particle.pdgCode(); + info.genProductionVertex = {particle.vx(), particle.vy(), particle.vz()}; + info.genDecayVertex = decayVertex; + info.genOmegaDecayVertex = omegaDecayVertex; + info.sign = isMatter ? -1 : 1; + return true; + } + + template + bool getLambdaFinalStateIds(McParticle const& lambda, int sign, std::array& finalStateIds) + { + bool foundProton = false; + bool foundPion = false; + for (const auto& daughter : lambda.template daughters_as()) { + if (daughter.pdgCode() == sign * 2212 && !foundProton) { + finalStateIds[0] = daughter.globalIndex(); + foundProton = true; + } else if (daughter.pdgCode() == -sign * 211 && !foundPion) { + finalStateIds[1] = daughter.globalIndex(); + foundPion = true; + } + } + return foundProton && foundPion; + } + + void writeDataCandidate(DoubleOmegaCandidate const& cand) + { + doubleOmegaTable(cand.pt * cand.sign, + cand.eta, + cand.phi, + cand.x, + cand.y, + cand.z, + cand.pvX, + cand.pvY, + cand.pvZ, + cand.cosPAOmega, + cand.cosPADirectLambda, + cand.cosPADoubleOmega, + cand.cosPAOmegaToPV, + cand.cosPADirectLambdaToPV, + cand.dcaXYOmegaToPV, + cand.dcaXYDirectLambdaToPV, + cand.dcaXYDirectKaonToPV, + cand.dcaXYOmegaKaonToPV, + cand.dcaXYOmegaProtonToPV, + cand.dcaXYOmegaPionToPV, + cand.dcaXYDirectProtonToPV, + cand.dcaXYDirectPionToPV, + cand.tpcNSigmaDirectKaon, + cand.tofNSigmaDirectKaon, + cand.daughterDetectorMap, + cand.mass, + cand.massOmega, + cand.massXi); + } + + void writeMixedCandidate(DoubleOmegaCandidate const& cand, MixingType mixingType) + { + doubleOmegaMixedTable(cand.pt * cand.sign, + cand.eta, + cand.phi, + cand.x, + cand.y, + cand.z, + cand.pvX, + cand.pvY, + cand.pvZ, + cand.cosPAOmega, + cand.cosPADirectLambda, + cand.cosPADoubleOmega, + cand.cosPAOmegaToPV, + cand.cosPADirectLambdaToPV, + cand.dcaXYOmegaToPV, + cand.dcaXYDirectLambdaToPV, + cand.dcaXYDirectKaonToPV, + cand.dcaXYOmegaKaonToPV, + cand.dcaXYOmegaProtonToPV, + cand.dcaXYOmegaPionToPV, + cand.dcaXYDirectProtonToPV, + cand.dcaXYDirectPionToPV, + cand.tpcNSigmaDirectKaon, + cand.tofNSigmaDirectKaon, + cand.daughterDetectorMap, + cand.mass, + cand.massOmega, + cand.massXi, + mixingType); + } + + void writeMCCandidate(DoubleOmegaCandidate const& cand, DoubleOmegaMCInfo const& mcInfo, bool isReco) + { + doubleOmegaTableMC(cand.pt * cand.sign, + cand.eta, + cand.phi, + cand.x, + cand.y, + cand.z, + cand.pvX, + cand.pvY, + cand.pvZ, + cand.cosPAOmega, + cand.cosPADirectLambda, + cand.cosPADoubleOmega, + cand.cosPAOmegaToPV, + cand.cosPADirectLambdaToPV, + cand.dcaXYOmegaToPV, + cand.dcaXYDirectLambdaToPV, + cand.dcaXYDirectKaonToPV, + cand.dcaXYOmegaKaonToPV, + cand.dcaXYOmegaProtonToPV, + cand.dcaXYOmegaPionToPV, + cand.dcaXYDirectProtonToPV, + cand.dcaXYDirectPionToPV, + cand.tpcNSigmaDirectKaon, + cand.tofNSigmaDirectKaon, + cand.daughterDetectorMap, + cand.mass, + cand.massOmega, + cand.massXi, + mcInfo.pt * mcInfo.sign, + mcInfo.eta, + mcInfo.phi, + mcInfo.decayLength, + mcInfo.pdgCode, + mcInfo.motherId, + isReco); + } + + template + void logVertexComparison(C const& collision, DoubleOmegaCandidate const& cand, + BuiltOmegaCandidate const& omega, DoubleOmegaMCInfo const& mcInfo) + { + auto logVertex = [](const char* label, std::array const& reco, std::array const& gen) { + LOG(debug) << label + << ": reco = (" << reco[0] << ", " << reco[1] << ", " << reco[2] << ") cm" + << " | gen = (" << gen[0] << ", " << gen[1] << ", " << gen[2] << ") cm" + << " | reco - gen = (" << reco[0] - gen[0] << ", " << reco[1] - gen[1] << ", " << reco[2] - gen[2] << ") cm"; + }; + LOG(debug) << "---------------------------------------- vertices, mother " << mcInfo.motherId + << ", pdgCode = " << mcInfo.pdgCode << ", gen pt = " << mcInfo.pt << " GeV/c"; + logVertex("PV ", {cand.pvX, cand.pvY, cand.pvZ}, mcInfo.genProductionVertex); + logVertex("doubleOmega ", {cand.x, cand.y, cand.z}, mcInfo.genDecayVertex); + logVertex("Omega ", {omega.x, omega.y, omega.z}, mcInfo.genOmegaDecayVertex); + LOG(debug) << "decay length: reco = " + << std::hypot(cand.x - cand.pvX, cand.y - cand.pvY, cand.z - cand.pvZ) + << " cm | gen = " << mcInfo.decayLength << " cm"; + LOG(debug) << "collision " << collision.globalIndex() << " (direct kaon), numContrib = " << collision.numContrib(); + } + + void fillCandidateQA(CollisionCandidates const& candidates) + { + for (const auto& omega : candidates.omegas) { + const float omegaPt = std::hypot(omega.px, omega.py); + histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); + histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); + } + for (const auto& lambdas : candidates.lambdas) { + for (const auto& lambda : lambdas) { + histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); + } + } + } + + template + CollisionCandidates buildCollisionCandidates(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, bool requireOmega) + { + auto candidates = helper.buildCollisionCandidates(collision, tracks, cascades, v0s, requireOmega); + fillCandidateQA(candidates); + return candidates; + } + + template + void fillFromCascades(C const& collision, T const&, Cascades const& cascades) + { + for (const auto& omegaRow : cascades) { + BuiltOmegaCandidate omega; + if (!helper.buildOmega(collision, omegaRow.template posTrack_as(), omegaRow.template negTrack_as(), omegaRow.template bachelor_as(), omega)) { + continue; + } + const float omegaPt = std::hypot(omega.px, omega.py); + histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); + histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); + const std::array omegaTrackIds{omega.posTrackId, omega.negTrackId, omega.bachelorId}; + + for (const auto& lambdaKaonSource : cascades) { + if (lambdaKaonSource.globalIndex() == omegaRow.globalIndex() || + lambdaKaonSource.sign() != omega.sign) { + continue; + } + + const std::array sourceTrackIds{ + lambdaKaonSource.posTrackId(), lambdaKaonSource.negTrackId(), lambdaKaonSource.bachelorId()}; + bool sharesTrack = false; + for (const auto& omegaTrackId : omegaTrackIds) { + if (std::find(sourceTrackIds.begin(), sourceTrackIds.end(), omegaTrackId) != sourceTrackIds.end()) { + sharesTrack = true; + break; + } + } + if (sharesTrack) { + continue; + } + + auto kaon = lambdaKaonSource.template bachelor_as(); + if (!helper.isSelectedKaon(kaon, omega.sign)) { + continue; + } + BuiltLambdaCandidate lambda; + if (!helper.buildLambda(collision, lambdaKaonSource.template posTrack_as(), lambdaKaonSource.template negTrack_as(), omega.sign, lambda)) { + continue; + } + histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); + DoubleOmegaCandidate cand; + if (helper.buildDoubleOmega({collision.posX(), collision.posY(), collision.posZ()}, omega, lambda, DoubleOmegaBuilderHelper::buildKaon(kaon), cand)) { + writeDataCandidate(cand); + } + } + } + } + + // Always take the vertex the direct kaon is associated with. To be improved + template + int64_t selectFindableCollision(Kaon const& kaon) + { + return kaon.collisionId(); + } + + template + bool fillFromSelectedTracks(C const& collisions, T const& tracks, + std::array const& daughterTrackIds, + DoubleOmegaMCInfo const& mcInfo) + { + // Daughter slots: Omega kaon, Omega proton/pion, direct proton/pion, direct kaon. + // Swap proton and pion slots for antimatter to pass positive/negative tracks. + const bool isMatter = mcInfo.pdgCode > 0; + const int8_t charge = isMatter ? -1 : 1; + const auto bachelor = tracks.rawIteratorAt(daughterTrackIds[0]); + if (bachelor.sign() != charge) { + return false; + } + const auto omegaPosTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 1 : 2]); + const auto omegaNegTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 2 : 1]); + const auto lambdaPosTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 3 : 4]); + const auto lambdaNegTrack = tracks.rawIteratorAt(daughterTrackIds[isMatter ? 4 : 3]); + const auto kaon = tracks.rawIteratorAt(daughterTrackIds[5]); + if (!helper.isSelectedKaon(kaon, charge)) { + return false; + } + const int64_t collisionId = selectFindableCollision(kaon); + if (collisionId < 0) { + return false; + } + const auto collision = collisions.rawIteratorAt(collisionId); + const auto bc = collision.template bc_as(); + initCCDB(bc); + + BuiltOmegaCandidate omega; + if (!helper.buildOmega(collision, omegaPosTrack, omegaNegTrack, bachelor, omega)) { + return false; + } + const float omegaPt = std::hypot(omega.px, omega.py); + histos.fill(HIST("QA/massXi"), omegaPt, omega.massXi); + histos.fill(HIST("QA/massOmega"), omegaPt, omega.massOmega); + BuiltLambdaCandidate lambda; + if (!helper.buildLambda(collision, lambdaPosTrack, lambdaNegTrack, charge, lambda)) { + return false; + } + histos.fill(HIST("QA/massLambda"), std::hypot(lambda.candidate.px, lambda.candidate.py), lambda.candidate.mass); + DoubleOmegaCandidate cand; + if (!helper.buildDoubleOmega({collision.posX(), collision.posY(), collision.posZ()}, omega, lambda, DoubleOmegaBuilderHelper::buildKaon(kaon), cand)) { + return false; + } + logVertexComparison(collision, cand, omega, mcInfo); + writeMCCandidate(cand, mcInfo, true); + return true; + } + + template + bool acceptCollision(C const& collision, aod::BCsWithTimestamps const&) + { + auto bc = collision.template bc_as(); + initCCDB(bc); + + if (cfgApplyEventSelection && + (!collision.sel8() || + std::abs(collision.posZ()) > zVtxMax || + !collision.selection_bit(aod::evsel::kNoITSROFrameBorder) || + !collision.selection_bit(aod::evsel::kNoTimeFrameBorder))) { + return false; + } + if (cfgSkimmedProcessing) { + zorro.isSelected(bc.globalBC()); + } + histos.fill(HIST("QA/zVtx"), collision.posZ()); + return true; + } + + void mix(CollisionCandidates const& reference, CollisionCandidates const& omegaSource, + CollisionCandidates const& lambdaSource, CollisionCandidates const& kaonSource, MixingType mixingType) + { + if (!(mixingOpts.mixingTypes & (1 << (mixingType - 1)))) { + return; + } + helper.combine(reference, omegaSource, lambdaSource, kaonSource, [&](DoubleOmegaCandidate const& cand, auto const&...) { + histos.fill(HIST("Mixing/hMixedCandidates"), mixingType); + writeMixedCandidate(cand, mixingType); + }); + } + + // Two candidates from one collision and one from the other, in both directions. The collision + // providing two candidates is the reference one. + void mixPair(CollisionCandidates const& candidates0, CollisionCandidates const& candidates1) + { + mix(candidates0, candidates1, candidates0, candidates0, kMixedOmega); + mix(candidates1, candidates0, candidates1, candidates1, kMixedOmega); + mix(candidates0, candidates0, candidates1, candidates0, kMixedLambda); + mix(candidates1, candidates1, candidates0, candidates1, kMixedLambda); + mix(candidates0, candidates0, candidates0, candidates1, kMixedKaon); + mix(candidates1, candidates1, candidates1, candidates0, kMixedKaon); + } + + // All the assignments of the Omega, Lambda and kaon to three different collisions. The collision + // providing the Omega is the reference one. + void mixTriple(CollisionCandidates const& candidates0, CollisionCandidates const& candidates1, CollisionCandidates const& candidates2) + { + const std::array sources{&candidates0, &candidates1, &candidates2}; + std::array roles{0, 1, 2}; // Sources of the Omega, Lambda and kaon. + do { + const auto& omegaSource = *sources[roles[0]]; + mix(omegaSource, omegaSource, *sources[roles[1]], *sources[roles[2]], kMixedAll); + } while (std::next_permutation(roles.begin(), roles.end())); + } + + void processData(Collisions const& collision, + TracksFull const& tracks, + aod::V0s const& v0s, + aod::Cascades const& cascades, + aod::BCsWithTimestamps const& bcs) + { + if (!acceptCollision(collision, bcs)) { + return; + } + const auto candidates = buildCollisionCandidates(collision, tracks, cascades, v0s, true); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, auto const&...) { writeDataCandidate(cand); }); + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processData, "Reconstruct Omega + Lambda + kaon from raw V0/cascade indices", true); + + void processDataFromCascades(Collisions const& collision, + TracksFull const& tracks, + FullCascades const& cascades, + aod::BCsWithTimestamps const& bcs) + { + if (acceptCollision(collision, bcs)) { + fillFromCascades(collision, tracks, cascades); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDataFromCascades, "Reconstruct Omega + Lambda + kaon from two cascade rows", false); + + void processMC(CollisionsMC const& collisions, + TracksFullMC const& tracks, + aod::V0s const& v0s, + aod::Cascades const& cascades, + aod::McParticles const& mcParticles, + aod::BCsWithTimestamps const& bcs) + { + reconstructedDoubleOmegaIds.clear(); + + for (const auto& collision : collisions) { + auto bc = collision.template bc_as(); + initCCDB(bc); + if (!acceptCollision(collision, bcs)) { + continue; + } + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + auto cascadesThisCollision = cascades.sliceBy(rawCascadesPerCollision, collision.globalIndex()); + auto v0sThisCollision = v0s.sliceBy(rawV0sPerCollision, collision.globalIndex()); + v0sThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&tracks); + cascadesThisCollision.bindExternalIndices(&v0s); + const auto candidates = buildCollisionCandidates(collision, tracksThisCollision, cascadesThisCollision, v0sThisCollision, true); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) { + DoubleOmegaMCInfo mcInfo; + if (getMCInfo(mcInfo, + tracks.rawIteratorAt(omega.posTrackId), tracks.rawIteratorAt(omega.negTrackId), tracks.rawIteratorAt(omega.bachelorId), + tracks.rawIteratorAt(lambda.posTrackId), tracks.rawIteratorAt(lambda.negTrackId), tracks.rawIteratorAt(kaon.trackId), + omega.sign, mcParticles)) { + reconstructedDoubleOmegaIds.push_back(mcInfo.motherId); + writeMCCandidate(cand, mcInfo, true); + } + }); + } + for (const auto& mcParticle : mcParticles) { + DoubleOmegaMCInfo mcInfo; + if (!getGeneratedMCInfo(mcInfo, mcParticle)) { + continue; + } + + if (std::find(reconstructedDoubleOmegaIds.begin(), reconstructedDoubleOmegaIds.end(), mcInfo.motherId) != reconstructedDoubleOmegaIds.end()) { + continue; + } + writeMCCandidate(DoubleOmegaCandidate{}, mcInfo, false); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processMC, "Reconstruct and truth-match Omega + Lambda + kaon in MC", false); + + void processFindableTracks(soa::Join const& collisions, + TracksFullMC const& tracks, + aod::Cascades const& cascades, + aod::V0s const& v0s, + aod::McParticles const& mcParticles, + aod::McCollisions const&, + aod::BCsWithTimestamps const&) + { + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.pdgCode()) != kDoubleOmegaPdg) { + continue; + } + histos.fill(HIST("MC/generatedAndFindable"), kAllGenerated); + + const int sign = mcParticle.pdgCode() > 0 ? 1 : -1; + int64_t omegaId = -1; + int64_t directLambdaId = -1; + int64_t directKaonId = -1; + + // First stage: double-Omega -> Omega + Lambda + kaon. + for (const auto& daughter : mcParticle.daughters_as()) { + if (daughter.pdgCode() == sign * 3334 && omegaId < 0) { + omegaId = daughter.globalIndex(); + } else if (daughter.pdgCode() == sign * 3122 && directLambdaId < 0) { + directLambdaId = daughter.globalIndex(); + } else if (daughter.pdgCode() == -sign * 321 && directKaonId < 0) { + directKaonId = daughter.globalIndex(); + } + } + if (omegaId < 0 || directLambdaId < 0 || directKaonId < 0) { + continue; + } + histos.fill(HIST("MC/generatedAndFindable"), kOmegaLambdaKaon); + + // Second stage: Omega -> Lambda + kaon, giving two Lambdas and two kaons. + const auto omega = mcParticles.rawIteratorAt(omegaId); + int64_t omegaLambdaId = -1; + int64_t omegaKaonId = -1; + for (const auto& daughter : omega.daughters_as()) { + if (daughter.pdgCode() == sign * 3122 && omegaLambdaId < 0) { + omegaLambdaId = daughter.globalIndex(); + } else if (daughter.pdgCode() == -sign * 321 && omegaKaonId < 0) { + omegaKaonId = daughter.globalIndex(); + } + } + if (omegaLambdaId < 0 || omegaKaonId < 0) { + continue; + } + histos.fill(HIST("MC/generatedAndFindable"), kTwoLambdasTwoKaons); + + // Third stage: both Lambdas -> proton + pion. + const auto omegaLambda = mcParticles.rawIteratorAt(omegaLambdaId); + const auto directLambda = mcParticles.rawIteratorAt(directLambdaId); + std::array omegaLambdaFinalStateIds{}; + std::array directLambdaFinalStateIds{}; + if (!getLambdaFinalStateIds(omegaLambda, sign, omegaLambdaFinalStateIds)) { + continue; + } + if (!getLambdaFinalStateIds(directLambda, sign, directLambdaFinalStateIds)) { + continue; + } + histos.fill(HIST("MC/generatedAndFindable"), kFinalState); + + const std::array finalStateIds{ + omegaKaonId, + omegaLambdaFinalStateIds[0], + omegaLambdaFinalStateIds[1], + directLambdaFinalStateIds[0], + directLambdaFinalStateIds[1], + directKaonId}; + std::array reconstructedDaughters{}; + std::array selectedReconstructedDaughters{}; + std::array daughterTrackIds{}; + daughterTrackIds.fill(-1); + // Match by generated daughter, independently of reconstructed collision association. + std::array selectedTrackIds{}; + selectedTrackIds.fill(-1); + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + const auto daughterId = std::find(finalStateIds.begin(), finalStateIds.end(), track.mcParticleId()); + if (daughterId != finalStateIds.end()) { + const auto daughterIndex = std::distance(finalStateIds.begin(), daughterId); + reconstructedDaughters[daughterIndex] = true; + if (daughterTrackIds[daughterIndex] < 0) { + daughterTrackIds[daughterIndex] = track.globalIndex(); + } + if (helper.selectTrack(track)) { + selectedReconstructedDaughters[daughterIndex] = true; + if (selectedTrackIds[daughterIndex] < 0) { + selectedTrackIds[daughterIndex] = track.globalIndex(); + } + } + } + } + auto printDaughterTracks = [&](const char* tag) { + LOG(info) << "---------------------------------------- " << tag; + for (std::size_t iDaughter = 0; iDaughter < daughterTrackIds.size(); ++iDaughter) { + LOG(info) << "+++++++"; + const auto daughterTrack = tracks.rawIteratorAt(daughterTrackIds[iDaughter]); + const auto daughterMCParticle = daughterTrack.mcParticle_as(); + LOG(info) << "Dau" << iDaughter + 1 + << ": hasITS = " << daughterTrack.hasITS() + << ", hasTPC = " << daughterTrack.hasTPC() + << ", hasTOF = " << daughterTrack.hasTOF() + << ", hasTRD = " << daughterTrack.hasTRD() + << ", isITSAfterburner = " << daughterTrack.isITSAfterburner(); + LOG(info) << "Dau" << iDaughter + 1 + << ": eta = " << daughterTrack.eta() + << ", pt = " << daughterTrack.pt() + << ", CollisionID = " << daughterTrack.collisionId() + << ", production point = (" << daughterMCParticle.vx() + << ", " << daughterMCParticle.vy() + << ", " << daughterMCParticle.vz() << ") cm"; + } + }; + + bool allDaughtersReconstructed = true; + bool allDaughtersSelected = true; + for (std::size_t iDaughter = 0; iDaughter < reconstructedDaughters.size(); ++iDaughter) { + allDaughtersReconstructed &= reconstructedDaughters[iDaughter]; + allDaughtersSelected &= selectedReconstructedDaughters[iDaughter]; + } + if (allDaughtersReconstructed) { + histos.fill(HIST("MC/generatedAndFindable"), kFindable); + bool omegaInCascade = false; + int64_t cascadeCollisionId = -1; + for (const auto& cascade : cascades) { + const auto cascadeV0 = cascade.v0(); + const std::array cascadeTrackIds{ + cascadeV0.posTrackId(), cascadeV0.negTrackId(), cascade.bachelorId()}; + bool allOmegaTracksInCascade = true; + for (std::size_t iDaughter = 0; iDaughter < 3; ++iDaughter) { + if (std::find(cascadeTrackIds.begin(), cascadeTrackIds.end(), daughterTrackIds[iDaughter]) == cascadeTrackIds.end()) { + allOmegaTracksInCascade = false; + break; + } + } + if (allOmegaTracksInCascade) { + omegaInCascade = true; + cascadeCollisionId = cascade.collisionId(); + break; + } + } + + bool directLambdaInV0 = false; + int64_t directLambdaCollisionId = -1; + for (const auto& v0 : v0s) { + const std::array v0TrackIds{v0.posTrackId(), v0.negTrackId()}; + bool allDirectLambdaTracksInV0 = true; + for (std::size_t iDaughter = 3; iDaughter < 5; ++iDaughter) { + if (std::find(v0TrackIds.begin(), v0TrackIds.end(), daughterTrackIds[iDaughter]) == v0TrackIds.end()) { + allDirectLambdaTracksInV0 = false; + break; + } + } + if (allDirectLambdaTracksInV0) { + directLambdaInV0 = true; + directLambdaCollisionId = v0.collisionId(); + break; + } + } + + if (omegaInCascade && directLambdaInV0) { + histos.fill(HIST("MC/generatedAndFindable"), kFindableCascadeAndV0); + if (allDaughtersSelected) { + histos.fill(HIST("MC/generatedAndFindable"), kFindableCascadeAndV0SelectedTracks); + } + const auto directKaonTrack = tracks.rawIteratorAt(daughterTrackIds[5]); + LOG(info) << "Findable collision IDs: cascade = " << cascadeCollisionId + << ", direct Lambda = " << directLambdaCollisionId + << ", direct kaon = " << directKaonTrack.collisionId(); + if (cascadeCollisionId == directLambdaCollisionId && cascadeCollisionId == directKaonTrack.collisionId()) { + LOG(info) << "All findable daughter tracks are in the same collision."; + } + printDaughterTracks("findable cascade and V0"); + } + } + if (allDaughtersSelected) { + histos.fill(HIST("MC/generatedAndFindable"), kFindableSelectedTracks); + printDaughterTracks("findable selected tracks"); + DoubleOmegaMCInfo mcInfo; + if (!getGeneratedMCInfo(mcInfo, mcParticle)) { + continue; + } + const bool isReco = fillFromSelectedTracks(collisions, tracks, selectedTrackIds, mcInfo); + if (!isReco) { + writeMCCandidate(DoubleOmegaCandidate{}, mcInfo, false); + } + } + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processFindableTracks, "Count findable double-Omega decays and reconstruct selected daughter tracks", false); + + // Bin index of value on a ConfigurableAxis (variable or fixed width), -1 outside the axis. + static int findBin(std::vector const& axis, double value) + { + if (axis.size() < 3) { + return -1; + } + if (axis[0] == VARIABLE_WIDTH) { + const auto edge = std::upper_bound(axis.begin() + 1, axis.end(), value); + if (edge == axis.begin() + 1 || edge == axis.end()) { + return -1; + } + return static_cast(std::distance(axis.begin() + 2, edge)); + } + const int nBins = static_cast(axis[0]); + if (value < axis[1] || value >= axis[2]) { + return -1; + } + return static_cast((value - axis[1]) / (axis[2] - axis[1]) * nBins); + } + + static int nBins(std::vector const& axis) + { + return axis[0] == VARIABLE_WIDTH ? static_cast(axis.size()) - 2 : static_cast(axis[0]); + } + + // Daughter track indices identifying a combination. Seeds sharing a daughter would produce the same + // combination more than once: each combination is written only once. + using MixedCombination = std::array; + + // Mixing pool of same-event candidates per (radius, phi, charge) bin, kept across dataframes. + std::unordered_map> mSeedPool; + std::size_t mPoolSeeds = 0; + int64_t mDataframeCounter = 0; + double mProcessingTimeSum = 0.; + + // Indices are only unique within a dataframe: the dataframe counter goes in the upper bits. + int64_t uniqueIndex(int64_t index) const + { + return index < 0 ? index : (mDataframeCounter << 32) | index; + } + + // The track identifiers read from the derived tables (see trackFingerprint) already identify a track + // in any dataframe; the collision index is made unique across dataframes. + MixingSeed makeSeed(int64_t collisionId, std::array const& primaryVertex, BuiltOmegaCandidate const& omega, + BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) const + { + return {uniqueIndex(collisionId), primaryVertex, mBz, omega, lambda, kaon}; + } + + // Builds and writes one mixed candidate, with the primary vertex of the reference seed. + void mixSeedDaughters(MixingSeed const& reference, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, + BuiltKaonCandidate const& kaon, MixingType mixingType, std::set& written) + { + if (!(mixingOpts.mixingTypes & (1 << (mixingType - 1)))) { + return; + } + const MixedCombination combination{omega.posTrackId, omega.negTrackId, omega.bachelorId, lambda.posTrackId, lambda.negTrackId, kaon.trackId}; + if (!written.insert(combination).second || DoubleOmegaBuilderHelper::sharesTracks(omega, lambda, kaon)) { + return; + } + DoubleOmegaCandidate cand; + if (!helper.buildDoubleOmega(reference.primaryVertex, omega, lambda, kaon, cand)) { + return; + } + histos.fill(HIST("Mixing/hMixedCandidates"), mixingType); + writeMixedCandidate(cand, mixingType); + } + + // One daughter of each seed replaced by the corresponding daughter of the other seed. The seed + // keeping two of its daughters provides the primary vertex. + void mixSeedPair(MixingSeed const& seed0, MixingSeed const& seed1, std::set& written) + { + mixSeedDaughters(seed0, seed1.omega, seed0.lambda, seed0.kaon, kMixedOmega, written); + mixSeedDaughters(seed1, seed0.omega, seed1.lambda, seed1.kaon, kMixedOmega, written); + mixSeedDaughters(seed0, seed0.omega, seed1.lambda, seed0.kaon, kMixedLambda, written); + mixSeedDaughters(seed1, seed1.omega, seed0.lambda, seed1.kaon, kMixedLambda, written); + mixSeedDaughters(seed0, seed0.omega, seed0.lambda, seed1.kaon, kMixedKaon, written); + mixSeedDaughters(seed1, seed1.omega, seed1.lambda, seed0.kaon, kMixedKaon, written); + } + + // Two daughters of a seed replaced by those of two other seeds: Omega, Lambda and kaon from three + // different collisions, in all the assignments. The seed providing the Omega is the reference. + void mixSeedTriple(MixingSeed const& seed0, MixingSeed const& seed1, MixingSeed const& seed2, std::set& written) + { + const std::array seeds{&seed0, &seed1, &seed2}; + std::array roles{0, 1, 2}; // Seeds providing the Omega, Lambda and kaon. + do { + const auto& omegaSeed = *seeds[roles[0]]; + mixSeedDaughters(omegaSeed, omegaSeed.omega, seeds[roles[1]]->lambda, seeds[roles[2]]->kaon, kMixedAll, written); + } while (std::next_permutation(roles.begin(), roles.end())); + } + + // Seeds can be mixed if they come from different collisions with close primary vertices, recorded + // with the same magnetic field. + bool canMix(MixingSeed const& seed0, MixingSeed const& seed1) const + { + return seed0.collisionId != seed1.collisionId && + std::abs(seed0.bz - seed1.bz) < 1.e-3f && + std::abs(seed0.primaryVertex[2] - seed1.primaryVertex[2]) <= mixingOpts.maxDeltaPVz.value; + } + + // Mixes a new seed with the most recent compatible seeds of its pool bin: pairs with each of them, + // triples with each pair of them coming from different collisions. + void mixWithPool(MixingSeed const& seed, std::deque const& pool, std::set& written) + { + const auto nPartners = static_cast(mixingOpts.nMixingCandidates.value); + std::vector partners; + for (auto partner = pool.rbegin(); partner != pool.rend() && partners.size() < nPartners; ++partner) { + histos.fill(HIST("Mixing/hMixedCollisions"), 0); + if (!canMix(seed, *partner)) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 1); + partners.push_back(&*partner); + mixSeedPair(seed, *partner, written); + } + if (!(mixingOpts.mixingTypes & (1 << (kMixedAll - 1)))) { + return; + } + for (std::size_t j = 0; j < partners.size(); ++j) { + for (std::size_t k = j + 1; k < partners.size(); ++k) { + histos.fill(HIST("Mixing/hMixedCollisions"), 2); + if (!canMix(*partners[j], *partners[k])) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 3); + mixSeedTriple(seed, *partners[j], *partners[k], written); + } + } + } + + // Same-event candidates of a collision become seeds, binned in decay radius, momentum azimuth and + // charge (mixingOpts.binsRadius, mixingOpts.binsPhi). The seeds are mixed with the pool first and + // added to it afterwards, so seeds of the same collision are never mixed with each other. + void mixCollisionSeeds(int64_t collisionId, CollisionCandidates const& candidates, std::set& written) + { + const int nPhiBins = nBins(mixingOpts.binsPhi.value); + const auto poolDepth = static_cast(mixingOpts.poolDepth.value); + std::vector> newSeeds; + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) { + const float radius = std::hypot(cand.x, cand.y); + const int radiusBin = findBin(mixingOpts.binsRadius.value, radius); + const int phiBin = findBin(mixingOpts.binsPhi.value, cand.phi); + if (radiusBin < 0 || phiBin < 0) { + return; + } + histos.fill(HIST("Mixing/hSeedRadiusPhi"), radius, cand.phi); + const int key = (radiusBin * nPhiBins + phiBin) * 2 + DoubleOmegaBuilderHelper::chargeIndex(cand.sign); + newSeeds.emplace_back(key, makeSeed(collisionId, candidates.primaryVertex, omega, lambda, kaon)); + }); + for (const auto& [key, seed] : newSeeds) { + mixWithPool(seed, mSeedPool[key], written); + } + for (auto& [key, seed] : newSeeds) { + auto& pool = mSeedPool[key]; + pool.push_back(std::move(seed)); + ++mPoolSeeds; + if (pool.size() > poolDepth) { + pool.pop_front(); + --mPoolSeeds; + } + } + } + + void monitorMixing(std::chrono::steady_clock::time_point const& start) + { + const double processingTime = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + mProcessingTimeSum += processingTime; + // Lower bound: deque and hash-map bookkeeping are not included. + const double poolMemory = static_cast(mPoolSeeds * sizeof(MixingSeed)) / (1024. * 1024.); + histos.fill(HIST("Mixing/hProcessingTime"), processingTime); + histos.fill(HIST("Mixing/hPoolSeeds"), mPoolSeeds); + histos.fill(HIST("Mixing/hPoolMemory"), poolMemory); + histos.fill(HIST("Mixing/hProcessingTimeVsPoolSeeds"), mPoolSeeds, processingTime); + if (mixingOpts.monitoringInterval > 0 && mDataframeCounter % mixingOpts.monitoringInterval == 0) { + LOGP(info, "Candidate mixing, dataframe {}: {} seeds in {} pool bins ({:.2f} MB), {:.1f} ms in this dataframe, {:.1f} ms on average", + mDataframeCounter, mPoolSeeds, mSeedPool.size(), poolMemory, processingTime, mProcessingTimeSum / mDataframeCounter); + } + } + + // --------------------------------------------------------------------------------------------- + // Derived data produced by doubleOmegaBuilder, read on the fly or from saved (merged) derived data. + + // The derived tables store a track once per collision, so the same track can appear in the rows of + // different collisions (V0s and cascades can use tracks associated to another collision). Tracks are + // identified across collisions by their parameters, which are bitwise identical for the same track. + template + static int64_t trackFingerprint(Row const& row) + { + std::size_t hash = 0; + for (const float value : {row.x(), row.alpha(), row.y(), row.z(), row.snp(), row.tgl(), row.signed1Pt()}) { + hash ^= std::hash{}(value) + 0x9e3779b97f4a7c15ULL + (hash << 6) + (hash >> 2); + } + return static_cast(hash); + } + + // Derived collisions store the run number and the magnetic field: the propagator, the material LUT + // and the fitters are configured again only when the field changes. + void initCCDBForDerived(int runNumber, float bz) + { + if (mRunNumber != 0 && std::abs(bz - mBz) < kBzTolerance) { + return; + } + auto* grpmag = ccdb->getForRun("GLO/Config/GRPMagField", runNumber); + if (!grpmag) { + LOG(fatal) << "Could not retrieve GRPMagField for run " << runNumber; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = o2::base::Propagator::Instance()->getNominalBz(); + helper.setBz(mBz); + if (static_cast(cfgMaterialCorrection.value) == o2::base::Propagator::MatCorrType::USEMatCorrLUT) { + auto* lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->getForRun("GLO/Param/MatLUT", runNumber)); + if (!lut) { + LOG(fatal) << "Could not retrieve material LUT for run " << runNumber; + } + o2::base::Propagator::Instance()->setMatLUT(lut); + } + mRunNumber = runNumber; + } + + // Reads the candidates of a derived collision back, applying again the selections that can be + // tightened with respect to the production ones. + template + CollisionCandidates readCollisionCandidates(C const& collision, aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, aod::DOmegaLambdas const& lambdas) + { + CollisionCandidates candidates; + candidates.primaryVertex = {collision.posX(), collision.posY(), collision.posZ()}; + + for (const auto& row : omegas.sliceBy(derivedOmegasPerCollision, collision.globalIndex())) { + const auto posTrack = tracks.rawIteratorAt(row.posTrackId()); + const auto negTrack = tracks.rawIteratorAt(row.negTrackId()); + const auto bachelor = tracks.rawIteratorAt(row.bachelorId()); + const bool isMatter = row.sign() < 0; + const auto& proton = isMatter ? posTrack : negTrack; + const auto& pion = isMatter ? negTrack : posTrack; + const uint32_t lambdaDetectorMap = static_cast(proton.detectorMap()) | (static_cast(pion.detectorMap()) << 3); + const BuiltOmegaCandidate omega{row.px(), row.py(), row.pz(), + row.decayX(), row.decayY(), row.decayZ(), + row.massOmega(), row.massXi(), + row.sign(), + trackFingerprint(posTrack), trackFingerprint(negTrack), trackFingerprint(bachelor), + getTrackParCov(row), + static_cast(bachelor.detectorMap()) | (lambdaDetectorMap << 3), + bachelor.dcaXYToPV(), proton.dcaXYToPV(), pion.dcaXYToPV(), + row.dcaDaughters()}; + if (helper.isSelectedOmega(omega)) { + candidates.omegas.push_back(omega); + } + } + + for (const auto& row : lambdas.sliceBy(derivedLambdasPerCollision, collision.globalIndex())) { + const auto posTrack = tracks.rawIteratorAt(row.posTrackId()); + const auto negTrack = tracks.rawIteratorAt(row.negTrackId()); + const bool isMatter = row.sign() < 0; + const auto& proton = isMatter ? posTrack : negTrack; + const auto& pion = isMatter ? negTrack : posTrack; + BuiltLambdaCandidate lambda; + lambda.candidate = {.px = row.px(), .py = row.py(), .pz = row.pz(), .mass = row.massLambda()}; + lambda.parentTrack = getTrackParCov(row); + lambda.parentTrack.setAbsCharge(0); // Neutral, as created by the V0 fit. + lambda.decayVertex = {row.decayX(), row.decayY(), row.decayZ()}; + lambda.posTrackId = trackFingerprint(posTrack); + lambda.negTrackId = trackFingerprint(negTrack); + lambda.daughterDetectorMap = static_cast(proton.detectorMap()) | (static_cast(pion.detectorMap()) << 3); + lambda.dcaXYProtonToPV = proton.dcaXYToPV(); + lambda.dcaXYPionToPV = pion.dcaXYToPV(); + lambda.dcaDaughters = row.dcaDaughters(); + if (helper.isSelectedLambda(lambda)) { + candidates.lambdas[DoubleOmegaBuilderHelper::chargeIndex(row.sign())].push_back(lambda); + } + } + + for (const auto& row : tracks.sliceBy(derivedTracksPerCollision, collision.globalIndex())) { + if (!row.isKaon()) { + continue; + } + const BuiltKaonCandidate kaon{getTrackParCov(row), + trackFingerprint(row), + static_cast(row.sign()), + row.tpcNSigmaKa(), + row.tofNSigmaKa(), + row.detectorMap()}; + if (helper.isSelectedKaon(kaon)) { + candidates.kaons[DoubleOmegaBuilderHelper::chargeIndex(kaon.sign)].push_back(kaon); + } + } + fillCandidateQA(candidates); + return candidates; + } + + void processDerivedData(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + for (const auto& collision : collisions) { + initCCDBForDerived(collision.runNumber(), collision.bz()); + histos.fill(HIST("QA/zVtx"), collision.posZ()); + const auto candidates = readCollisionCandidates(collision, tracks, omegas, lambdas); + helper.combine(candidates, candidates, candidates, candidates, + [&](DoubleOmegaCandidate const& cand, auto const&...) { writeDataCandidate(cand); }); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedData, "Same-event Omega + Lambda + kaon from the derived tables of doubleOmegaBuilder", false); + + // Mixed-event background from the derived tables. Saved derived data can be merged into large + // dataframes, which provides many more mixing partners than the input AO2D dataframes. Only + // collisions recorded with the same magnetic field are mixed. + void processDerivedMixing(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + std::unordered_map candidatesPerCollision; + for (const auto& collision : collisions) { + candidatesPerCollision.emplace(collision.globalIndex(), readCollisionCandidates(collision, tracks, omegas, lambdas)); + } + + DerivedMixingBinning binning{{mixingOpts.binsVtxZ, mixingOpts.binsMultiplicity}, true}; + for (const auto& [collision0, collision1] : selfPairCombinations(binning, mixingOpts.nMixingCollisions, -1, collisions)) { + histos.fill(HIST("Mixing/hMixedCollisions"), 0); + if (std::abs(collision0.posZ() - collision1.posZ()) > mixingOpts.maxDeltaPVz || + std::abs(collision0.bz() - collision1.bz()) > kBzTolerance) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 1); + initCCDBForDerived(collision0.runNumber(), collision0.bz()); + mixPair(candidatesPerCollision.at(collision0.globalIndex()), candidatesPerCollision.at(collision1.globalIndex())); + } + + if (!(mixingOpts.mixingTypes & (1 << (kMixedAll - 1)))) { + return; + } + for (const auto& [collision0, collision1, collision2] : selfTripleCombinations(binning, mixingOpts.nMixingCollisions, -1, collisions)) { + histos.fill(HIST("Mixing/hMixedCollisions"), 2); + const auto [minPVz, maxPVz] = std::minmax({collision0.posZ(), collision1.posZ(), collision2.posZ()}); + if (maxPVz - minPVz > mixingOpts.maxDeltaPVz || + std::abs(collision0.bz() - collision1.bz()) > kBzTolerance || + std::abs(collision0.bz() - collision2.bz()) > kBzTolerance) { + continue; + } + histos.fill(HIST("Mixing/hMixedCollisions"), 3); + initCCDBForDerived(collision0.runNumber(), collision0.bz()); + mixTriple(candidatesPerCollision.at(collision0.globalIndex()), candidatesPerCollision.at(collision1.globalIndex()), + candidatesPerCollision.at(collision2.globalIndex())); + } + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedMixing, "Mixed-event Omega + Lambda + kaon background from the derived tables of doubleOmegaBuilder", false); + + // Candidate-level mixed-event background from the derived tables of doubleOmegaBuilder, as in + // decay3bodybuilder. The same-event candidates (seeds) are binned in decay radius, momentum azimuth + // and charge, so seeds in the same bin have their decay vertex in the same region of the detector and + // point in the same direction. Mixed candidates are built by swapping one (or two) daughters between + // seeds of different collisions, which keeps the vertex topology without moving any track. Seeds are + // pooled across dataframes. + void processDerivedMixingCandidates(aod::DOmegaColls const& collisions, + aod::DOmegaTracks const& tracks, + aod::DOmegaOmegas const& omegas, + aod::DOmegaLambdas const& lambdas) + { + const auto start = std::chrono::steady_clock::now(); + ++mDataframeCounter; + std::set written; // Every combination has a daughter from this dataframe. + + for (const auto& collision : collisions) { + // Seeds are mixed only with seeds recorded with the same field (canMix), so the field of the + // current collision is the right one for all its mixed candidates. + initCCDBForDerived(collision.runNumber(), collision.bz()); + histos.fill(HIST("QA/zVtx"), collision.posZ()); + const auto candidates = readCollisionCandidates(collision, tracks, omegas, lambdas); + mixCollisionSeeds(collision.globalIndex(), candidates, written); + } + monitorMixing(start); + } + PROCESS_SWITCH(doubleOmegaTreeCreator, processDerivedMixingCandidates, "Candidate-level mixed-event background from the derived tables of doubleOmegaBuilder, seeds binned in decay radius and azimuth", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx index c58c707f72a..7643c9ab979 100644 --- a/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx +++ b/PWGLF/TableProducer/Strangeness/hStrangeCorrelationFilter.cxx @@ -25,6 +25,7 @@ #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -50,7 +51,10 @@ #include #include +#include + #include +#include #include #include @@ -59,12 +63,9 @@ using namespace o2::constants::math; using namespace o2::framework; using namespace o2::framework::expressions; -#define BIT_SET(var, nbit) ((var) |= (1 << (nbit))) -#define BIT_CHECK(var, nbit) ((var) & (1 << (nbit))) - struct HStrangeCorrelationFilter { - const float ctauxi = 4.91; // from PDG - const float ctauomega = 2.461; // from PDG + static constexpr float Xictau = 4.91; // from PDG + static constexpr float Omegactau = 2.461; // from PDG Service ccdb; @@ -82,6 +83,8 @@ struct HStrangeCorrelationFilter { Configurable strangedEdxNSigmaTight{"strangedEdxNSigmaTight", 3, "Nsigmas for strange decay daughters"}; Configurable zorroMask{"zorroMask", "", "zorro trigger class to select on (empty: none)"}; Configurable nSigmaNearXiMassCenter{"nSigmaNearXiMassCenter", 0, "for Oemga analysis only, to check if candidate mass is around Xi"}; + Configurable rejectAmbiguousTracks{"rejectAmbiguousTracks", false, "reject tracks compatible with more than one collision (requires track-to-collision-associator with fillTableOfCollIdsPerTrack)"}; + Configurable rejectAmbiguousAssoc{"rejectAmbiguousAssoc", false, "reject V0/cascade candidates having at least one ambiguous daughter track"}; // used for event selections in Pb-Pb Configurable cfgCutOccupancyHigh{"cfgCutOccupancyHigh", 3000, "High cut on TPC occupancy"}; @@ -123,6 +126,8 @@ struct HStrangeCorrelationFilter { Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in assoc tracks"}; Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; + Configurable requireClusterInITS{"requireClusterInITS", false, "require cluster in ITS for V0 and cascade daughter tracks"}; + Configurable minITSClustersForDaughterTracks{"minITSClustersForDaughterTracks", 1, "Minimum number of ITS clusters for V0 daughter tracks"}; // Associated pion identification Configurable pionMinBayesProb{"pionMinBayesProb", 0.95, "minimal Bayesian probability for pion ID"}; @@ -133,6 +138,10 @@ struct HStrangeCorrelationFilter { // formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + + // perform P-Lambda correlation analysis + Configurable performPLambdacorrelation{"performPLambdacorrelation", 3, "do p-Lambda correlation study"}; + Configurable triggerProtonNSigmaTPCFOF{"triggerProtonNSigmaTPCFOF", 3, "minimal n sigma in TOF and TPC for Proton ID"}; } trackSelections; struct : ConfigurableGroup { // V0 selections @@ -143,6 +152,7 @@ struct HStrangeCorrelationFilter { Configurable dcaPostopv{"dcaPostopv", 0.06, "DCA Pos To PV"}; Configurable dcaBaryonToPV{"dcaBaryonToPV", 0.2, "DCA of baryon daughter track To PV"}; Configurable dcaMesonToPV{"dcaMesonToPV", 0.05, "DCA of meson daughter track To PV"}; + Configurable dcaDaugToPVForK0s{"dcaDaugToPVForK0s", 0, "DCA of K0s daughter tracks To PV"}; Configurable v0RadiusMin{"v0RadiusMin", 0.5, "v0radius"}; Configurable v0RadiusMax{"v0RadiusMax", 200, "v0radius"}; @@ -207,10 +217,10 @@ struct HStrangeCorrelationFilter { // using V0LinkedTagged = soa::Join; // using CascadesLinkedTagged = soa::Join; - using FullTracks = soa::Join; - using FullTracksMC = soa::Join; - using DauTracks = soa::Join; - using DauTracksMC = soa::Join; + using FullTracks = soa::Join; + using FullTracksMC = soa::Join; + using DauTracks = soa::Join; + using DauTracksMC = soa::Join; // using IDTracks= soa::Join; // prepared for Bayesian PID using IDTracks = soa::Join; using IDTracksMC = soa::Join; @@ -246,33 +256,33 @@ struct HStrangeCorrelationFilter { TF1* fXiWidth = new TF1("fXiWidth", "[0]+[1]*x+[2]*std::exp(-[3]*x)"); TF1* fOmegaMean = new TF1("fomegaMean", "[0]+[1]*x+[2]*std::exp(-[3]*x)"); TF1* fOmegaWidth = new TF1("fomegaWidth", "[0]+[1]*x+[2]*std::exp(-[3]*x)"); - TH1F* hK0ShortMean; - TH1F* hK0ShortWidth; - TH1F* hLambdaMean; - TH1F* hLambdaWidth; - TH1F* hXiMean; - TH1F* hXiWidth; - TH1F* hOmegaMean; - TH1F* hOmegaWidth; + TH1F* hK0ShortMean = nullptr; + TH1F* hK0ShortWidth = nullptr; + TH1F* hLambdaMean = nullptr; + TH1F* hLambdaWidth = nullptr; + TH1F* hXiMean = nullptr; + TH1F* hXiWidth = nullptr; + TH1F* hOmegaMean = nullptr; + TH1F* hOmegaWidth = nullptr; Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; - int mRunNumber; + int mRunNumberZorro = -1; + int mRunNumberParameters = -1; struct TriggCandidate { - float pt; - int collisionId; - int trackId; - bool isPhysicalPrimary; - float origPt; + float pt = 0.f; + int collisionId = -1; + int trackId = -1; + bool isPhysicalPrimary = false; + float origPt = 0.f; + uint16_t mcMask = 0; }; - TriggCandidate thisTrigg; std::vector triggerCandidates; void init(InitContext const&) { zorroSummary.setObject(zorro.getZorroSummary()); - mRunNumber = -1; if (useParameterization) { fK0Mean->SetParameters(parameters.massParsK0Mean->at(0), parameters.massParsK0Mean->at(1), parameters.massParsK0Mean->at(2), parameters.massParsK0Mean->at(3)); fK0Width->SetParameters(parameters.massParsK0Width->at(0), parameters.massParsK0Width->at(1), parameters.massParsK0Width->at(2), parameters.massParsK0Width->at(3)); @@ -283,67 +293,76 @@ struct HStrangeCorrelationFilter { fOmegaMean->SetParameters(parameters.massParsOmegaMean->at(0), parameters.massParsOmegaMean->at(1), parameters.massParsOmegaMean->at(2), parameters.massParsOmegaMean->at(3)); fOmegaWidth->SetParameters(parameters.massParsOmegaWidth->at(0), parameters.massParsOmegaWidth->at(1), parameters.massParsOmegaWidth->at(2), parameters.massParsOmegaWidth->at(3)); } else { - hK0ShortMean = 0x0; - hK0ShortWidth = 0x0; - hLambdaMean = 0x0; - hLambdaWidth = 0x0; - hXiMean = 0x0; - hXiWidth = 0x0; - hOmegaMean = 0x0; - hOmegaWidth = 0x0; + hK0ShortMean = nullptr; + hK0ShortWidth = nullptr; + hLambdaMean = nullptr; + hLambdaWidth = nullptr; + hXiMean = nullptr; + hXiWidth = nullptr; + hOmegaMean = nullptr; + hOmegaWidth = nullptr; } if (doprocessV0s || doprocessV0sMC) { histos.add("h3dMassK0Short", "h3dMassK0Short", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisK0ShortMass, axesConfigurations.axisMult}); histos.add("h3dMassLambda", "h3dMassLambda", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisLambdaMass, axesConfigurations.axisMult}); histos.add("h3dMassAntiLambda", "h3dMassAntiLambda", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisLambdaMass, axesConfigurations.axisMult}); } + if (rejectAmbiguousAssoc && (doprocessV0s || doprocessV0sMC)) { + histos.add("hAmbiguousV0Pt", "hAmbiguousV0Pt", kTH1F, {axesConfigurations.axisPtQA}); + } + if (rejectAmbiguousTracks && (doprocessTriggers || doprocessTriggersMC)) { + histos.add("hAmbiguousTriggerPt", "hAmbiguousTriggerPt", kTH1F, {axesConfigurations.axisPtQA}); + } if (doprocessCascades || doprocessCascadesMC) { histos.add("h3dMassXiMinus", "h3dMassXiMinus", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisXiMass, axesConfigurations.axisMult}); histos.add("h3dMassXiPlus", "h3dMassXiPlus", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisXiMass, axesConfigurations.axisMult}); histos.add("h3dMassOmegaMinus", "h3dMassOmegaMinus", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisOmegaMass, axesConfigurations.axisMult}); histos.add("h3dMassOmegaPlus", "h3dMassOmegaPlus", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisOmegaMass, axesConfigurations.axisMult}); } + if (rejectAmbiguousAssoc && (doprocessCascades || doprocessCascadesMC)) { + histos.add("hAmbiguousCascadePt", "hAmbiguousCascadePt", kTH1F, {axesConfigurations.axisPtQA}); + } } void initCCDB(aod::BCsWithTimestamps::iterator const& bc) { - if (mRunNumber == bc.runNumber()) { + if (mRunNumberZorro == bc.runNumber()) { return; } zorro.initCCDB(ccdb.service, bc.runNumber(), bc.timestamp(), zorroMask.value); zorro.populateHistRegistry(histos, bc.runNumber()); - mRunNumber = bc.runNumber(); + mRunNumberZorro = bc.runNumber(); } void initParametersFromCCDB(aod::BCsWithTimestamps::iterator const& bc) { - if (mRunNumber == bc.runNumber()) { + if (mRunNumberParameters == bc.runNumber()) { return; } - mRunNumber = bc.runNumber(); - LOG(info) << "Loading mean and sigma from CCDB for run " << mRunNumber << " now..."; + mRunNumberParameters = bc.runNumber(); + LOG(info) << "Loading mean and sigma from CCDB for run " << mRunNumberParameters << " now..."; auto timeStamp = bc.timestamp(); - TList* listParameters = ccdb->getForTimeStamp(parameterCCDBPath, timeStamp); + auto listParameters = ccdb->getForTimeStamp(parameterCCDBPath, timeStamp); if (!listParameters) { LOG(fatal) << "Problem getting TList object with parameters!"; } if (doprocessV0s || doprocessV0sMC) { - hK0ShortMean = static_cast(listParameters->FindObject("hK0ShortMean")); - hK0ShortWidth = static_cast(listParameters->FindObject("hK0ShortWidth")); - hLambdaMean = static_cast(listParameters->FindObject("hLambdaMean")); - hLambdaWidth = static_cast(listParameters->FindObject("hLambdaWidth")); + hK0ShortMean = dynamic_cast(listParameters->FindObject("hK0ShortMean")); + hK0ShortWidth = dynamic_cast(listParameters->FindObject("hK0ShortWidth")); + hLambdaMean = dynamic_cast(listParameters->FindObject("hLambdaMean")); + hLambdaWidth = dynamic_cast(listParameters->FindObject("hLambdaWidth")); } if (doprocessCascades || doprocessCascadesMC) { - hXiMean = static_cast(listParameters->FindObject("hXiMean")); - hXiWidth = static_cast(listParameters->FindObject("hXiWidth")); - hOmegaMean = static_cast(listParameters->FindObject("hOmegaMean")); - hOmegaWidth = static_cast(listParameters->FindObject("hOmegaWidth")); + hXiMean = dynamic_cast(listParameters->FindObject("hXiMean")); + hXiWidth = dynamic_cast(listParameters->FindObject("hXiWidth")); + hOmegaMean = dynamic_cast(listParameters->FindObject("hOmegaMean")); + hOmegaWidth = dynamic_cast(listParameters->FindObject("hOmegaWidth")); } - LOG(info) << "parameters now loaded for " << mRunNumber; + LOG(info) << "parameters now loaded for " << mRunNumberParameters; } // this function allows for all event selections to be done in a modular way @@ -380,29 +399,50 @@ struct HStrangeCorrelationFilter { // more event selections in Pb-Pb template - bool isCollisionSelectedPbPb(TCollision collision) + bool isCollisionSelectedPbPb(TCollision const& collision) { - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && eventSelections.requireGoodTriggerTVX) /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */ + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && eventSelections.requireGoodTriggerTVX) { /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */ return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) // cut time intervals with dead ITS staves + } + if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll) && eventSelections.requireAllGoodITSLayers) { // cut time intervals with dead ITS staves return false; - if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && eventSelections.requireGoodZvtxFT0vsPV) // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + } + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && eventSelections.requireGoodZvtxFT0vsPV) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference return false; + } auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) /* Below min occupancy and Above max occupancy*/ + if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) { /* Below min occupancy and Above max occupancy*/ return false; - if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) // reject collisions close to Time Frame borders + } + if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { // reject collisions close to Time Frame borders return false; - if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) // reject events affected by the ITS ROF border + } + if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { // reject events affected by the ITS ROF border return false; - if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) // rejects collisions which are associated with the same "found-by-T0" bunch crossing + } + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing return false; + } return true; } + // ambiguous track check: the track is compatible with more than one collision, + // or with a collision different from the one it is assigned to (see PWGCF/TableProducer/dptDptFilter.cxx) + template + bool isAmbiguousTrack(TTrack const& track) + { + if (track.compatibleCollIds().size() == 0) { + return false; // no collision association information: not ambiguous + } + if (track.compatibleCollIds().size() == 1) { + return track.collisionId() != track.compatibleCollIds()[0]; + } + return true; // associated to more than one collision + } + // reco-level trigger quality checks (N.B.: DCA is filtered, not selected) template - bool isValidTrigger(TTrack track) + bool isValidTrigger(TTrack const& track) { if (track.eta() > generalSelections.triggerEtaMax || track.eta() < generalSelections.triggerEtaMin) { return false; @@ -420,13 +460,20 @@ struct HStrangeCorrelationFilter { if (track.tpcNClsShared() > trackSelections.triggerMaxTPCSharedClusters) { return false; // skip, has shared clusters } - if (!(BIT_CHECK(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { + if (!(TESTBIT(track.itsClusterMap(), 0)) && trackSelections.triggerRequireL0) { return false; // skip, doesn't have cluster in ITS L0 } + bool isProton = std::sqrt(track.tofNSigmaPr() * track.tofNSigmaPr() + track.tpcNSigmaPr() * track.tpcNSigmaPr()) <= trackSelections.triggerProtonNSigmaTPCFOF; + if (trackSelections.performPLambdacorrelation <= 1 && !isProton) { + return false; + } + if (trackSelections.performPLambdacorrelation == 2 && isProton) { + return false; + } return true; } template - bool isValidAssocTrack(TTrack assoc) + bool isValidAssocTrack(TTrack const& assoc) { if (assoc.eta() > generalSelections.assocEtaMax || assoc.eta() < generalSelections.assocEtaMin) { return false; @@ -442,30 +489,38 @@ struct HStrangeCorrelationFilter { } // do this only if information is available - float nSigmaTPCTOF[8] = {-10, -10, -10, -10, -10, -10, -10, -10}; + std::array nSigmaTPCTOF = {-10, -10, -10, -10, -10, -10, -10, -10}; if constexpr (requires { assoc.tofSignal(); } && !requires { assoc.mcParticle(); }) { if (assoc.tofSignal() > 0) { - if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > trackSelections.assocPionNSigmaTPCFOF) + if (std::sqrt(assoc.tofNSigmaPi() * assoc.tofNSigmaPi() + assoc.tpcNSigmaPi() * assoc.tpcNSigmaPi()) > trackSelections.assocPionNSigmaTPCFOF) { return false; - if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) + } + if (assoc.tofNSigmaPr() < trackSelections.rejectSigma) { return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + } + if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) { return false; - if (assoc.tofNSigmaKa() < trackSelections.rejectSigma) + } + if (assoc.tofNSigmaKa() < trackSelections.rejectSigma) { return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + } + if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) { return false; + } nSigmaTPCTOF[4] = assoc.tofNSigmaPi(); nSigmaTPCTOF[5] = assoc.tofNSigmaKa(); nSigmaTPCTOF[6] = assoc.tofNSigmaPr(); nSigmaTPCTOF[7] = assoc.tofNSigmaEl(); } else { - if (assoc.tpcNSigmaPi() > trackSelections.assocPionNSigmaTPCFOF) + if (assoc.tpcNSigmaPi() > trackSelections.assocPionNSigmaTPCFOF) { return false; - if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) + } + if (assoc.tpcNSigmaPr() < trackSelections.rejectSigma) { return false; - if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) + } + if (assoc.tpcNSigmaKa() < trackSelections.rejectSigma) { return false; + } } nSigmaTPCTOF[0] = assoc.tpcNSigmaPi(); nSigmaTPCTOF[1] = assoc.tpcNSigmaKa(); @@ -476,12 +531,14 @@ struct HStrangeCorrelationFilter { bool physicalPrimary = false; float origPt = -1; float code = -9999; + uint16_t mcMask = 0; if constexpr (requires { assoc.mcParticle(); }) { if (assoc.has_mcParticle()) { auto mcParticle = assoc.mcParticle(); physicalPrimary = mcParticle.isPhysicalPrimary(); origPt = mcParticle.pt(); code = mcParticle.pdgCode(); + mcMask = assoc.mcMask(); } } @@ -490,7 +547,8 @@ struct HStrangeCorrelationFilter { physicalPrimary, assoc.globalIndex(), origPt, - code); + code, + mcMask); assocPID( nSigmaTPCTOF[0], nSigmaTPCTOF[1], @@ -505,43 +563,54 @@ struct HStrangeCorrelationFilter { // cascadeselection in PbPb template - bool cascadeSelectedPbPb(TCascade casc, float pvx, float pvy, float pvz) + bool cascadeSelectedPbPb(TCascade const& casc, float pvx, float pvy, float pvz) { // bachBaryonCosPA - if (casc.bachBaryonCosPA() < cascSelection.bachBaryonCosPA) + if (casc.bachBaryonCosPA() < cascSelection.bachBaryonCosPA) { return false; + } // bachBaryonDCAxyToPV - if (std::abs(casc.bachBaryonDCAxyToPV()) > cascSelection.bachBaryonDCAxyToPV) + if (std::abs(casc.bachBaryonDCAxyToPV()) > cascSelection.bachBaryonDCAxyToPV) { return false; + } // casccosPA - if (casc.casccosPA(pvx, pvy, pvz) < cascSelection.cascCospa) + if (casc.casccosPA(pvx, pvy, pvz) < cascSelection.cascCospa) { return false; + } // dcacascdaughters float ptDepCut = cascSelection.dcaCacsDauPar0; - if (casc.pt() > cascSelection.lowPtForCascDaugPtDep && casc.pt() < cascSelection.highPtForCascDaugPtDep) + if (casc.pt() > cascSelection.lowPtForCascDaugPtDep && casc.pt() < cascSelection.highPtForCascDaugPtDep) { ptDepCut = cascSelection.dcaCacsDauPar1; - else if (casc.pt() > cascSelection.highPtForCascDaugPtDep) + } else if (casc.pt() > cascSelection.highPtForCascDaugPtDep) { ptDepCut = cascSelection.dcaCacsDauPar2; - if (casc.dcacascdaughters() > ptDepCut) + } + if (casc.dcacascdaughters() > ptDepCut) { return false; + } // dcaV0daughters - if (casc.dcaV0daughters() > cascSelection.cascdcaV0dau) + if (casc.dcaV0daughters() > cascSelection.cascdcaV0dau) { return false; + } // dcav0topv - if (std::abs(casc.dcav0topv(pvx, pvy, pvz)) < cascSelection.cascMindcav0topv) + if (std::abs(casc.dcav0topv(pvx, pvy, pvz)) < cascSelection.cascMindcav0topv) { return false; + } // cascradius - if (casc.cascradius() < cascSelection.cascRadius) + if (casc.cascradius() < cascSelection.cascRadius) { return false; + } // v0radius - if (casc.v0radius() < cascSelection.cascv0RadiusMin) + if (casc.v0radius() < cascSelection.cascv0RadiusMin) { return false; + } // v0cosPA - if (casc.v0cosPA(casc.x(), casc.y(), casc.z()) < cascSelection.cascv0cospa) + if (casc.v0cosPA(casc.x(), casc.y(), casc.z()) < cascSelection.cascv0cospa) { return false; + } // lambdaMassWin - if (std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascSelection.lambdaMassWin) + if (std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascSelection.lambdaMassWin) { return false; + } return true; } @@ -558,8 +627,14 @@ struct HStrangeCorrelationFilter { double leadingPt = -1.; int leadingId = -1; for (auto const& track : tracks) { - if (!isValidTrigger(track)) + if (!isValidTrigger(track)) { continue; + } + if (rejectAmbiguousTracks && isAmbiguousTrack(track)) { + histos.fill(HIST("hAmbiguousTriggerPt"), track.pt()); + continue; + } + TriggCandidate thisTrigg{}; thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -578,7 +653,8 @@ struct HStrangeCorrelationFilter { TriggCandidate.isPhysicalPrimary, TriggCandidate.trackId, TriggCandidate.origPt, - isLeading); + isLeading, + 0); triggerTrackExtra(1); } } @@ -596,8 +672,14 @@ struct HStrangeCorrelationFilter { double leadingPt = -1.; int leadingId = -1; for (auto const& track : tracks) { - if (!isValidTrigger(track)) + if (!isValidTrigger(track)) { continue; + } + if (rejectAmbiguousTracks && isAmbiguousTrack(track)) { + histos.fill(HIST("hAmbiguousTriggerPt"), track.pt()); + continue; + } + TriggCandidate thisTrigg{}; thisTrigg.pt = track.pt(); thisTrigg.trackId = track.globalIndex(); thisTrigg.collisionId = track.collisionId(); @@ -605,6 +687,7 @@ struct HStrangeCorrelationFilter { auto mcParticle = track.mcParticle(); thisTrigg.isPhysicalPrimary = mcParticle.isPhysicalPrimary(); thisTrigg.origPt = mcParticle.pt(); + thisTrigg.mcMask = track.mcMask(); } triggerCandidates.push_back(thisTrigg); if (track.pt() > leadingPt) { @@ -620,7 +703,8 @@ struct HStrangeCorrelationFilter { TriggCandidate.isPhysicalPrimary, TriggCandidate.trackId, TriggCandidate.origPt, - isLeading); + isLeading, + TriggCandidate.mcMask); triggerTrackExtra(1); } } @@ -648,8 +732,9 @@ struct HStrangeCorrelationFilter { /// _________________________________________________ /// Step 1: Populate table with trigger tracks for (auto const& track : tracks) { - if (!isValidAssocTrack(track)) + if (!isValidAssocTrack(track)) { continue; + } } } @@ -676,8 +761,9 @@ struct HStrangeCorrelationFilter { /// _________________________________________________ /// Step 1: Populate table with trigger tracks for (auto const& track : tracks) { - if (!isValidAssocTrack(track)) + if (!isValidAssocTrack(track)) { continue; + } } } @@ -704,8 +790,9 @@ struct HStrangeCorrelationFilter { /// _________________________________________________ /// Step 1: Populate table with trigger tracks for (auto const& track : tracks) { - if (!isValidAssocTrack(track)) + if (!isValidAssocTrack(track)) { continue; + } } } void processAssocHadronsMC(soa::Join::iterator const& collision, soa::Filtered const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) @@ -731,8 +818,9 @@ struct HStrangeCorrelationFilter { /// _________________________________________________ /// Step 1: Populate table with trigger tracks for (auto const& track : tracks) { - if (!isValidAssocTrack(track)) + if (!isValidAssocTrack(track)) { continue; + } } } @@ -760,13 +848,23 @@ struct HStrangeCorrelationFilter { auto posdau = v0.posTrack_as(); auto negdau = v0.negTrack_as(); - if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; - if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + } + if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + continue; + } + if (trackSelections.requireClusterInITS && (posdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { continue; + } + if (rejectAmbiguousAssoc && (isAmbiguousTrack(posdau) || isAmbiguousTrack(negdau))) { + histos.fill(HIST("hAmbiguousV0Pt"), v0.pt()); + continue; + } + float dcaDauCutForK0s = v0Selection.dcaDaugToPVForK0s == 0 ? v0Selection.dcaMesonToPV : v0Selection.dcaDaugToPVForK0s; bool isGoodK0Short = (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short < v0Selection.lifetimecutK0S && - std::abs(v0.dcapostopv()) > v0Selection.dcaMesonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaMesonToPV && + std::abs(v0.dcapostopv()) > dcaDauCutForK0s && std::abs(v0.dcanegtopv()) > dcaDauCutForK0s && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha())); bool isGoodLambda = (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0Selection.lifetimecutLambda && std::abs(v0.dcapostopv()) > v0Selection.dcaBaryonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaMesonToPV); @@ -774,48 +872,48 @@ struct HStrangeCorrelationFilter { std::abs(v0.dcapostopv()) > v0Selection.dcaMesonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaBaryonToPV); if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose) { if (doPPAnalysis || isGoodK0Short) { - BIT_SET(compatibleK0Short, 0); + SETBIT(compatibleK0Short, 0); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigma) { if (doPPAnalysis || isGoodK0Short) { - BIT_SET(compatibleK0Short, 1); + SETBIT(compatibleK0Short, 1); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaTight) { if (doPPAnalysis || isGoodK0Short) { - BIT_SET(compatibleK0Short, 2); + SETBIT(compatibleK0Short, 2); } } if (v0.v0cosPA() > v0Selection.lambdaCospa) { if (std::abs(posdau.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose) { if (doPPAnalysis || isGoodLambda) { - BIT_SET(compatibleLambda, 0); + SETBIT(compatibleLambda, 0); } } if (std::abs(posdau.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigma) { if (doPPAnalysis || isGoodLambda) { - BIT_SET(compatibleLambda, 1); + SETBIT(compatibleLambda, 1); } } if (std::abs(posdau.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaTight) { if (doPPAnalysis || isGoodLambda) { - BIT_SET(compatibleLambda, 2); + SETBIT(compatibleLambda, 2); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negdau.tpcNSigmaPr()) < strangedEdxNSigmaLoose) { if (doPPAnalysis || isGoodAntiLambda) { - BIT_SET(compatibleAntiLambda, 0); + SETBIT(compatibleAntiLambda, 0); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negdau.tpcNSigmaPr()) < strangedEdxNSigma) { if (doPPAnalysis || isGoodAntiLambda) { - BIT_SET(compatibleAntiLambda, 1); + SETBIT(compatibleAntiLambda, 1); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negdau.tpcNSigmaPr()) < strangedEdxNSigmaTight) { if (doPPAnalysis || isGoodAntiLambda) { - BIT_SET(compatibleAntiLambda, 2); + SETBIT(compatibleAntiLambda, 2); } } } @@ -840,12 +938,15 @@ struct HStrangeCorrelationFilter { massNSigmaAntiLambda = (v0.mAntiLambda() - hLambdaMean->Interpolate(v0.pt())) / (hLambdaWidth->Interpolate(v0.pt()) + 1e-6); } } - if (compatibleK0Short) + if (compatibleK0Short > 0) { histos.fill(HIST("h3dMassK0Short"), v0.pt(), v0.mK0Short(), cent); - if (compatibleLambda) + } + if (compatibleLambda > 0) { histos.fill(HIST("h3dMassLambda"), v0.pt(), v0.mLambda(), cent); - if (compatibleAntiLambda) + } + if (compatibleAntiLambda > 0) { histos.fill(HIST("h3dMassAntiLambda"), v0.pt(), v0.mAntiLambda(), cent); + } if (!fillTableOnlyWithCompatible || ( // start major condition check @@ -886,13 +987,23 @@ struct HStrangeCorrelationFilter { auto posdau = v0.posTrack_as(); auto negdau = v0.negTrack_as(); - if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + if (negdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + continue; + } + if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { + continue; + } + if (trackSelections.requireClusterInITS && (posdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negdau.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { continue; - if (posdau.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + } + if (rejectAmbiguousAssoc && (isAmbiguousTrack(posdau) || isAmbiguousTrack(negdau))) { + histos.fill(HIST("hAmbiguousV0Pt"), v0.pt()); continue; + } + float dcaDauCutForK0s = v0Selection.dcaDaugToPVForK0s == 0 ? v0Selection.dcaMesonToPV : v0Selection.dcaDaugToPVForK0s; bool isGoodK0Short = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short < v0Selection.lifetimecutK0S && - std::abs(v0.dcapostopv()) > v0Selection.dcaMesonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaMesonToPV && + std::abs(v0.dcapostopv()) > dcaDauCutForK0s && std::abs(v0.dcanegtopv()) > dcaDauCutForK0s && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); bool isGoodLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0Selection.lifetimecutLambda && std::abs(v0.dcapostopv()) > v0Selection.dcaBaryonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaMesonToPV; @@ -900,49 +1011,49 @@ struct HStrangeCorrelationFilter { std::abs(v0.dcapostopv()) > v0Selection.dcaMesonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaBaryonToPV; if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose) { if (doPPAnalysis || isGoodK0Short) { - BIT_SET(compatibleK0Short, 0); + SETBIT(compatibleK0Short, 0); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigma) { if (doPPAnalysis || isGoodK0Short) { - BIT_SET(compatibleK0Short, 1); + SETBIT(compatibleK0Short, 1); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaTight) { if (doPPAnalysis || isGoodK0Short) { - BIT_SET(compatibleK0Short, 2); + SETBIT(compatibleK0Short, 2); } } if (v0.v0cosPA() > v0Selection.lambdaCospa) { if (std::abs(posdau.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose) { if (doPPAnalysis || isGoodLambda) { - BIT_SET(compatibleLambda, 0); + SETBIT(compatibleLambda, 0); } } if (std::abs(posdau.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigma) { if (doPPAnalysis || isGoodLambda) { - BIT_SET(compatibleLambda, 1); + SETBIT(compatibleLambda, 1); } } if (std::abs(posdau.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(negdau.tpcNSigmaPi()) < strangedEdxNSigmaTight) { if (doPPAnalysis || isGoodLambda) { - BIT_SET(compatibleLambda, 2); + SETBIT(compatibleLambda, 2); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negdau.tpcNSigmaPr()) < strangedEdxNSigmaLoose) { if (doPPAnalysis || isGoodAntiLambda) { - BIT_SET(compatibleAntiLambda, 0); + SETBIT(compatibleAntiLambda, 0); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negdau.tpcNSigmaPr()) < strangedEdxNSigma) { if (doPPAnalysis || isGoodAntiLambda) { - BIT_SET(compatibleAntiLambda, 1); + SETBIT(compatibleAntiLambda, 1); } } if (std::abs(posdau.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negdau.tpcNSigmaPr()) < strangedEdxNSigmaTight) { if (doPPAnalysis || isGoodAntiLambda) { - BIT_SET(compatibleAntiLambda, 2); + SETBIT(compatibleAntiLambda, 2); } } } @@ -973,18 +1084,24 @@ struct HStrangeCorrelationFilter { bool trueLambda = false; bool trueAntiLambda = false; v0PhysicalPrimary = v0.isPhysicalPrimary(); - if (v0.pdgCode() == PDG_t::kK0Short) + if (v0.pdgCode() == PDG_t::kK0Short) { trueK0Short = true; - if (v0.pdgCode() == PDG_t::kLambda0) + } + if (v0.pdgCode() == PDG_t::kLambda0) { trueLambda = true; - if (v0.pdgCode() == PDG_t::kLambda0Bar) + } + if (v0.pdgCode() == PDG_t::kLambda0Bar) { trueAntiLambda = true; - if (compatibleK0Short && (!doTrueSelectionInMass || (trueK0Short && v0PhysicalPrimary))) + } + if (compatibleK0Short > 0 && (!doTrueSelectionInMass || (trueK0Short && v0PhysicalPrimary))) { histos.fill(HIST("h3dMassK0Short"), v0.pt(), v0.mK0Short(), cent); - if (compatibleLambda && (!doTrueSelectionInMass || (trueLambda && v0PhysicalPrimary))) + } + if (compatibleLambda > 0 && (!doTrueSelectionInMass || (trueLambda && v0PhysicalPrimary))) { histos.fill(HIST("h3dMassLambda"), v0.pt(), v0.mLambda(), cent); - if (compatibleAntiLambda && (!doTrueSelectionInMass || (trueAntiLambda && v0PhysicalPrimary))) + } + if (compatibleAntiLambda > 0 && (!doTrueSelectionInMass || (trueAntiLambda && v0PhysicalPrimary))) { histos.fill(HIST("h3dMassAntiLambda"), v0.pt(), v0.mAntiLambda(), cent); + } if (!fillTableOnlyWithCompatible || ( // start major condition check @@ -1020,21 +1137,33 @@ struct HStrangeCorrelationFilter { casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < cascSelection.cascCospa || casc.cascradius() < cascSelection.cascRadius || std::abs(casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ())) < cascSelection.cascMindcav0topv || - std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascSelection.cascV0masswindow)) + std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascSelection.cascV0masswindow)) { continue; + } auto bachTrackCast = casc.bachelor_as(); auto posTrackCast = casc.posTrack_as(); auto negTrackCast = casc.negTrack_as(); // minimum TPC crossed rows - if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; - if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + } + if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; - if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + } + if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; - if (!doPPAnalysis && !cascadeSelectedPbPb(casc, collision.posX(), collision.posY(), collision.posZ())) + } + if (rejectAmbiguousAssoc && (isAmbiguousTrack(bachTrackCast) || isAmbiguousTrack(posTrackCast) || isAmbiguousTrack(negTrackCast))) { + histos.fill(HIST("hAmbiguousCascadePt"), casc.pt()); continue; + } + if (!doPPAnalysis && !cascadeSelectedPbPb(casc, collision.posX(), collision.posY(), collision.posZ())) { + continue; + } + if (trackSelections.requireClusterInITS && (posTrackCast.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negTrackCast.itsNCls() < trackSelections.minITSClustersForDaughterTracks || bachTrackCast.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { + continue; + } bool isGoodNegCascadePbPb = std::abs(casc.dcabachtopv()) > cascSelection.dcaBachToPV && std::abs(casc.dcapostopv()) > cascSelection.cascDcaBaryonToPV && std::abs(casc.dcanegtopv()) > cascSelection.cascDcaMesonToPV; @@ -1047,8 +1176,8 @@ struct HStrangeCorrelationFilter { int compatibleOmegaPlus = 0; float cascpos = std::hypot(casc.x() - collision.posX(), casc.y() - collision.posY(), casc.z() - collision.posZ()); float cascptotmom = std::hypot(casc.px(), casc.py(), casc.pz()); - float ctauXi = o2::constants::physics::MassXiMinus * cascpos / ((cascptotmom + 1e-13) * ctauxi); - float ctauOmega = o2::constants::physics::MassOmegaMinus * cascpos / ((cascptotmom + 1e-13) * ctauomega); + float ctauXi = o2::constants::physics::MassXiMinus * cascpos / ((cascptotmom + 1e-13) * Xictau); + float ctauOmega = o2::constants::physics::MassOmegaMinus * cascpos / ((cascptotmom + 1e-13) * Omegactau); bool isGoodXiPbPb = std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) > cascSelection.rejcomp && ctauXi < cascSelection.proplifetime && std::abs(casc.yXi()) < cascSelection.rapCut; @@ -1056,65 +1185,65 @@ struct HStrangeCorrelationFilter { ctauOmega < cascSelection.proplifetime && std::abs(casc.yOmega()) < cascSelection.rapCut; if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodXiPbPb)) { - BIT_SET(compatibleXiMinus, 0); + SETBIT(compatibleXiMinus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodXiPbPb)) { - BIT_SET(compatibleXiMinus, 1); + SETBIT(compatibleXiMinus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodXiPbPb)) { - BIT_SET(compatibleXiMinus, 2); + SETBIT(compatibleXiMinus, 2); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodXiPbPb)) { - BIT_SET(compatibleXiPlus, 0); + SETBIT(compatibleXiPlus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodXiPbPb)) { - BIT_SET(compatibleXiPlus, 1); + SETBIT(compatibleXiPlus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodXiPbPb)) { - BIT_SET(compatibleXiPlus, 2); + SETBIT(compatibleXiPlus, 2); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaLoose && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaMinus, 0); + SETBIT(compatibleOmegaMinus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigma && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaMinus, 1); + SETBIT(compatibleOmegaMinus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaTight && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaMinus, 2); + SETBIT(compatibleOmegaMinus, 2); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaLoose && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaPlus, 0); + SETBIT(compatibleOmegaPlus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigma && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaPlus, 1); + SETBIT(compatibleOmegaPlus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaTight && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaPlus, 2); + SETBIT(compatibleOmegaPlus, 2); } } float massNSigmaXi = -20.0f; @@ -1133,14 +1262,18 @@ struct HStrangeCorrelationFilter { massNSigmaOmega = (casc.mOmega() - hOmegaMean->Interpolate(casc.pt())) / (hOmegaWidth->Interpolate(casc.pt()) + 1e-6); } } - if (compatibleXiMinus) + if (compatibleXiMinus > 0) { histos.fill(HIST("h3dMassXiMinus"), casc.pt(), casc.mXi(), cent); - if (compatibleXiPlus) + } + if (compatibleXiPlus > 0) { histos.fill(HIST("h3dMassXiPlus"), casc.pt(), casc.mXi(), cent); - if (compatibleOmegaMinus && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) + } + if (compatibleOmegaMinus > 0 && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) { histos.fill(HIST("h3dMassOmegaMinus"), casc.pt(), casc.mOmega(), cent); - if (compatibleOmegaPlus && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) + } + if (compatibleOmegaPlus > 0 && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) { histos.fill(HIST("h3dMassOmegaPlus"), casc.pt(), casc.mOmega(), cent); + } if (!fillTableOnlyWithCompatible || ( // start major condition check @@ -1176,22 +1309,34 @@ struct HStrangeCorrelationFilter { casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < cascSelection.cascCospa || casc.cascradius() < cascSelection.cascRadius || std::abs(casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ())) < cascSelection.cascMindcav0topv || - std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascSelection.cascV0masswindow)) + std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascSelection.cascV0masswindow)) { continue; + } auto bachTrackCast = casc.bachelor_as(); auto posTrackCast = casc.posTrack_as(); auto negTrackCast = casc.negTrack_as(); // minimum TPC crossed rows - if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + if (bachTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; - if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + } + if (posTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; - if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) + } + if (negTrackCast.tpcNClsCrossedRows() < trackSelections.minTPCNCrossedRows) { continue; - if (!doPPAnalysis && !cascadeSelectedPbPb(casc, collision.posX(), collision.posY(), collision.posZ())) + } + if (rejectAmbiguousAssoc && (isAmbiguousTrack(bachTrackCast) || isAmbiguousTrack(posTrackCast) || isAmbiguousTrack(negTrackCast))) { + histos.fill(HIST("hAmbiguousCascadePt"), casc.pt()); continue; + } + if (!doPPAnalysis && !cascadeSelectedPbPb(casc, collision.posX(), collision.posY(), collision.posZ())) { + continue; + } + if (trackSelections.requireClusterInITS && (posTrackCast.itsNCls() < trackSelections.minITSClustersForDaughterTracks || negTrackCast.itsNCls() < trackSelections.minITSClustersForDaughterTracks || bachTrackCast.itsNCls() < trackSelections.minITSClustersForDaughterTracks)) { + continue; + } bool isGoodNegCascadePbPb = (std::abs(casc.dcabachtopv()) > cascSelection.dcaBachToPV && std::abs(casc.dcapostopv()) > cascSelection.cascDcaBaryonToPV && std::abs(casc.dcanegtopv()) > cascSelection.cascDcaMesonToPV); bool isGoodPosCascadePbPb = (std::abs(casc.dcabachtopv()) > cascSelection.dcaBachToPV && std::abs(casc.dcapostopv()) > cascSelection.cascDcaMesonToPV && @@ -1203,8 +1348,8 @@ struct HStrangeCorrelationFilter { int compatibleOmegaPlus = 0; float cascpos = std::hypot(casc.x() - collision.posX(), casc.y() - collision.posY(), casc.z() - collision.posZ()); float cascptotmom = std::hypot(casc.px(), casc.py(), casc.pz()); - float ctauXi = o2::constants::physics::MassXiMinus * cascpos / ((cascptotmom + 1e-13) * ctauxi); - float ctauOmega = o2::constants::physics::MassOmegaMinus * cascpos / ((cascptotmom + 1e-13) * ctauomega); + float ctauXi = o2::constants::physics::MassXiMinus * cascpos / ((cascptotmom + 1e-13) * Xictau); + float ctauOmega = o2::constants::physics::MassOmegaMinus * cascpos / ((cascptotmom + 1e-13) * Omegactau); bool iGoodXiPbPb = std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) > cascSelection.rejcomp && ctauXi < cascSelection.proplifetime && std::abs(casc.yXi()) < cascSelection.rapCut; @@ -1212,65 +1357,65 @@ struct HStrangeCorrelationFilter { ctauOmega < cascSelection.proplifetime && std::abs(casc.yOmega()) < cascSelection.rapCut; if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && iGoodXiPbPb)) { - BIT_SET(compatibleXiMinus, 0); + SETBIT(compatibleXiMinus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && iGoodXiPbPb)) { - BIT_SET(compatibleXiMinus, 1); + SETBIT(compatibleXiMinus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && iGoodXiPbPb)) { - BIT_SET(compatibleXiMinus, 2); + SETBIT(compatibleXiMinus, 2); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && iGoodXiPbPb)) { - BIT_SET(compatibleXiPlus, 0); + SETBIT(compatibleXiPlus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && iGoodXiPbPb)) { - BIT_SET(compatibleXiPlus, 1); + SETBIT(compatibleXiPlus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && iGoodXiPbPb)) { - BIT_SET(compatibleXiPlus, 2); + SETBIT(compatibleXiPlus, 2); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaLoose && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaMinus, 0); + SETBIT(compatibleOmegaMinus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigma && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaMinus, 1); + SETBIT(compatibleOmegaMinus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaTight && casc.sign() < 0) { if (doPPAnalysis || (isGoodNegCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaMinus, 2); + SETBIT(compatibleOmegaMinus, 2); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaLoose && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaLoose && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaLoose && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaPlus, 0); + SETBIT(compatibleOmegaPlus, 0); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigma && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigma && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigma && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaPlus, 1); + SETBIT(compatibleOmegaPlus, 1); } } if (std::abs(posTrackCast.tpcNSigmaPi()) < strangedEdxNSigmaTight && std::abs(negTrackCast.tpcNSigmaPr()) < strangedEdxNSigmaTight && std::abs(bachTrackCast.tpcNSigmaKa()) < strangedEdxNSigmaTight && casc.sign() > 0) { if (doPPAnalysis || (isGoodPosCascadePbPb && isGoodOmegaPbPb)) { - BIT_SET(compatibleOmegaPlus, 2); + SETBIT(compatibleOmegaPlus, 2); } } float massNSigmaXi = 20.0f; @@ -1296,22 +1441,30 @@ struct HStrangeCorrelationFilter { bool trueOmegaMinus = false; bool trueOmegaPlus = false; cascPhysicalPrimary = casc.isPhysicalPrimary(); - if (casc.pdgCode() == PDG_t::kXiMinus) + if (casc.pdgCode() == PDG_t::kXiMinus) { trueXiMinus = true; - if (casc.pdgCode() == PDG_t::kXiPlusBar) + } + if (casc.pdgCode() == PDG_t::kXiPlusBar) { trueXiPlus = true; - if (casc.pdgCode() == PDG_t::kOmegaMinus) + } + if (casc.pdgCode() == PDG_t::kOmegaMinus) { trueOmegaMinus = true; - if (casc.pdgCode() == PDG_t::kOmegaPlusBar) + } + if (casc.pdgCode() == PDG_t::kOmegaPlusBar) { trueOmegaPlus = true; - if (compatibleXiMinus && (!doTrueSelectionInMass || (trueXiMinus && cascPhysicalPrimary))) + } + if (compatibleXiMinus > 0 && (!doTrueSelectionInMass || (trueXiMinus && cascPhysicalPrimary))) { histos.fill(HIST("h3dMassXiMinus"), casc.pt(), casc.mXi(), cent); - if (compatibleXiPlus && (!doTrueSelectionInMass || (trueXiPlus && cascPhysicalPrimary))) + } + if (compatibleXiPlus > 0 && (!doTrueSelectionInMass || (trueXiPlus && cascPhysicalPrimary))) { histos.fill(HIST("h3dMassXiPlus"), casc.pt(), casc.mXi(), cent); - if (compatibleOmegaMinus && (!doTrueSelectionInMass || (trueOmegaMinus && cascPhysicalPrimary)) && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) + } + if (compatibleOmegaMinus > 0 && (!doTrueSelectionInMass || (trueOmegaMinus && cascPhysicalPrimary)) && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) { histos.fill(HIST("h3dMassOmegaMinus"), casc.pt(), casc.mOmega(), cent); - if (compatibleOmegaPlus && (!doTrueSelectionInMass || (trueOmegaPlus && cascPhysicalPrimary)) && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) + } + if (compatibleOmegaPlus > 0 && (!doTrueSelectionInMass || (trueOmegaPlus && cascPhysicalPrimary)) && std::abs(massNSigmaXi) > nSigmaNearXiMassCenter) { histos.fill(HIST("h3dMassOmegaPlus"), casc.pt(), casc.mOmega(), cent); + } if (!fillTableOnlyWithCompatible || ( // start major condition check @@ -1339,8 +1492,8 @@ struct HStrangeCorrelationFilter { PROCESS_SWITCH(HStrangeCorrelationFilter, processAssocHadronsMC, "Produce associated Hadron tables for MC", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx index d8e57ee1494..d3bcbc0a2ad 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaJetPolarizationIons.cxx @@ -72,11 +72,10 @@ #include #include #include -#include #include #include -#include +#include #include #include #include @@ -133,9 +132,6 @@ enum BkgSubtraction { ////////////////////////////////////////////// struct lambdajetpolarizationions { - - // struct : ProducesGroup { - // } products; Produces tableV0s; Produces tableJets; Produces tableLeadParticles; @@ -146,23 +142,20 @@ struct lambdajetpolarizationions { // master analysis switches Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; - Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; // Will work only with Lambdas, in a first analysis + Configurable analyseAntiLambda{"analyseAntiLambda", true, "process AntiLambda-like candidates"}; Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; Configurable irSource{"irSource", "ZNChadronic", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNChadronic)"}; // Renamed David's "ZNC hadronic" to the proper code "ZNChadronic" Configurable centralityEstimatorForQA{"centralityEstimatorForQA", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M - // (Now saving all centralities at the derived data level -- Makes them all available for consumer) - // (But still using this variable for QA histograms) ///////////////////////////////////////////// - Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; + Configurable doEventQA{"doEventQA", true, "do event QA histograms"}; // Configurable qaCentrality{"qaCentrality", false, "qa centrality flag: check base raw values"}; - Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variables QA histograms"}; // Includes doPlainTopoQA from derivedlambdakzeroanalysis - Configurable doV0KinematicQA{"doV0KinematicQA", false, "do kinematic variables QA histograms"}; + Configurable doTopoQA{"doTopoQA", true, "do topological variables QA histograms"}; + Configurable doV0KinematicQA{"doV0KinematicQA", true, "do kinematic variables QA histograms"}; Configurable doArmenterosQA{"doArmenterosQA", false, "do Armenteros QA histograms"}; Configurable doTPCQA{"doTPCQA", false, "do TPC QA histograms"}; Configurable doTOFQA{"doTOFQA", false, "do TOF QA histograms"}; - Configurable doEtaPhiQA{"doEtaPhiQA", false, "do Eta/Phi QA histograms for V0s and daughters"}; Configurable doJetKinematicsQA{"doJetKinematicsQA", false, "do pT,Eta,Phi QA histograms for jets"}; ///////////////////////////////////////////// @@ -184,30 +177,34 @@ struct lambdajetpolarizationions { std::string prefix = "eventSelections"; // JSON group name Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; // part of sel8, actually - Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; // part of sel8, actually - Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; // part of sel8, actually + // Detector status: + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; // part of sel8, actually + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; // part of sel8, actually + Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", false, "require number of inactive chips on all ITS layers to be below maximum allowed values"}; // Based off ITS holes DPG AOT selection + // Vertex quality: Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track (Run 3 only)"}; Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference (Run 3 only)"}; // o2::aod::evsel::kIsGoodZvtxFT0vsPV. Recommended for OO Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF (Run 3 only)"}; Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD (Run 3 only)"}; + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; + // Pileup and cannibalism rejection: Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC (Run 3 only)"}; // o2::aod::evsel::kNoSameBunchPileup. Recommended for OO Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds (Run 3 only)"}; Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds (Run 3 only)"}; Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds (Run 3 only)"}; Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold (Run 3 only)"}; Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF (Run 3 only)"}; - Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; // Only truly useful in pp - Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; - - Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; - + // Physics selections: + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection. Only applied in pp"}; // Only truly useful in pp. Also only ever applied in pp + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection. Only applied in pp"}; + // MC: Configurable useEvtSelInDenomEff{"useEvtSelInDenomEff", false, "Consider event selections in the recoed <-> gen collision association for the denominator (or numerator) of the acc. x eff. (or signal loss)?"}; Configurable applyZVtxSelOnMCPV{"applyZVtxSelOnMCPV", true, "Apply Z-vtx cut on the PV of the generated collision?"}; // I see no reason as to not do this by default + // Occupancy: Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; - // fast check on occupancy Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; - // fast check on interaction rate + // Interaction rate: Configurable minIR{"minIR", -1, "minimum IR collisions"}; Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; } eventSelections; @@ -216,22 +213,21 @@ struct lambdajetpolarizationions { std::string prefix = "v0Selections"; // JSON group name Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable rapidityCut{"rapidityCut", 1.0f, "rapidity"}; // This is actually a physics cut, not a proper acceptance cut // Selection criteria: acceptance - Configurable rapidityCut{"rapidityCut", 1.0f, "rapidity"}; - Configurable v0EtaCut{"v0EtaCut", 0.9f, "eta cut for v0"}; - Configurable daughterEtaCut{"daughterEtaCut", 0.9f, "max eta for daughters"}; // Default is 0.8. Changed to 0.9 to agree with jet selection. TODO: test the impact/biasing of this! + // Configurable daughterEtaCut{"daughterEtaCut", 0.9f, "max eta for daughters"}; // Default is 0.8. Changed to 0.9 to agree with jet selection. TODO: test the impact/biasing of this! // Standard 5 topological criteria -- Closed a bit more for the Lambda analysis Configurable v0cospa{"v0cospa", 0.995, "min V0 CosPA"}; // Default is 0.97 - Configurable dcav0dau{"dcav0dau", 1.0, "max DCA V0 Daughters (cm)"}; // Default is 1.0 + Configurable dcav0dau{"dcav0dau", 1.2, "max DCA V0 Daughters (cm)"}; // Default is 1.0 in v0builder // Configurable dcanegtopv{"dcanegtopv", .2, "min DCA Neg To PV (cm)"}; // Default is .05 // Configurable dcapostopv{"dcapostopv", .05, "min DCA Pos To PV (cm)"}; // Default is .05 // Renamed for better consistency of candidate selection (the cut is not determined by charge, but by mass and how deflected the daughter is): - Configurable dcaPionToPV{"dcaPionToPV", .2, "min DCA pion-like daughter To PV (cm)"}; // Default is .05. Suppresses pion background. + Configurable dcaPionToPV{"dcaPionToPV", .05, "min DCA pion-like daughter To PV (cm)"}; // .2 would suppress primary pion background, but we need to reduce fake-polarization signals enhanced by DCA-to-PV cuts Configurable dcaProtonToPV{"dcaProtonToPV", .05, "min DCA proton-like daughter To PV (cm)"}; // Default is .05 - Configurable v0radius{"v0radius", 1.2, "minimum V0 radius (cm)"}; // Default is 1.2 + Configurable v0radius{"v0radius", 1.0, "minimum V0 radius (cm)"}; // Default is 1.2 Configurable v0radiusMax{"v0radiusMax", 1E5, "maximum V0 radius (cm)"}; - Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (cm)"}; + Configurable lambdaLifetimeCut{"lambdaLifetimeCut", 30., "lifetime cut (c*tau) for Lambda (cm)"}; // For soft Lambdas, expect V0Radius < 30 cm. For hard ones, it can be > 30 cm. // invariant mass selection Configurable compMassRejection{"compMassRejection", -1, "Competing mass rejection (GeV/#it{c}^{2})"}; // This was creating bumps in the pp analysis code's invariant mass. Turned off for now. @@ -256,7 +252,7 @@ struct lambdajetpolarizationions { Configurable phiHighCut{"phiHighCut", "0.1/x+pi/18.0+0.06", "High azimuth cut parametrisation"}; // PID (TPC/TOF) - Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 4, "tpcPidNsigmaCut"}; // Default is 5 + Configurable tpcPidNsigmaCut{"tpcPidNsigmaCut", 5, "tpcPidNsigmaCut"}; // Default is 5 Configurable tofPidNsigmaCutLaPr{"tofPidNsigmaCutLaPr", 1e+6, "tofPidNsigmaCutLaPr"}; Configurable tofPidNsigmaCutLaPi{"tofPidNsigmaCutLaPi", 1e+6, "tofPidNsigmaCutLaPi"}; @@ -272,7 +268,7 @@ struct lambdajetpolarizationions { // Run Condition Table (RCT) configurables struct : ConfigurableGroup { std::string prefix = "rctConfigurations"; // JSON group name - Configurable cfgRCTLabel{"cfgRCTLabel", "", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; + Configurable cfgRCTLabel{"cfgRCTLabel", "CBT", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; Configurable cfgCheckZDC{"cfgCheckZDC", false, "Include ZDC flags in the bit selection (for Pb-Pb only)"}; Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; } rctConfigurations; @@ -303,7 +299,9 @@ struct lambdajetpolarizationions { ctpRateFetcher rateFetcher; int mRunNumber; float magField; - std::map metadata; + // For some binning variables: + int mBinHasRingJet; + int mBinHasRingV0; o2::parameters::GRPMagField* grpmag = nullptr; // Histogram axes configuration: @@ -313,22 +311,38 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPtXi{"axisPtXi", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for feeddown from Xi"}; ConfigurableAxis axisPtCoarse{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, ""}; // Default is {200, 1.101f, 1.131f} + // ConfigurableAxis axisLambdaMassCoarse{"axisLambdaMassCoarse", {200, 1.101f, 1.131f}, ""}; + // Shared axis with the derived consumer, to focus on the peak-vs-sideband trends: + ConfigurableAxis axisLambdaMassSigExtract{ + "axisLambdaMassSigExtract", + {VARIABLE_WIDTH, + // Left sideband (7 bins, 0.004 width) + 1.0800, 1.0840, 1.0880, 1.0920, + 1.0960, 1.1000, 1.1040, 1.1080, + // Fine peak region (8 bins, 0.0016 width) + 1.1096, 1.1112, 1.1128, 1.1144, + 1.1160, 1.1176, 1.1192, 1.1208, + // Right sideband (7 bins, 0.004 width) + 1.1248, 1.1288, 1.1328, 1.1368, + 1.1408, 1.1448, 1.1488}, + "Lambda mass in GeV/c"}; + ConfigurableAxis axisPVz{"axisPVz", {100, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; + ConfigurableAxis axisPVzCoarse{"axisPVzCoarse", {20, -20.0f, +20.0f}, "Primary Vertex Z [cm]"}; + // Centrality/IR/Occupancy: ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}, "Centrality"}; ConfigurableAxis axisNch{"axisNch", {500, 0.0f, +5000.0f}, "Number of charged particles"}; ConfigurableAxis axisIRBinning{"axisIRBinning", {500, 0, 50}, "Binning for the interaction rate (kHz)"}; ConfigurableAxis axisMultFT0M{"axisMultFT0M", {500, 0.0f, +100000.0f}, "Multiplicity FT0M"}; ConfigurableAxis axisMultFT0C{"axisMultFT0C", {500, 0.0f, +10000.0f}, "Multiplicity FT0C"}; ConfigurableAxis axisMultFV0A{"axisMultFV0A", {500, 0.0f, +100000.0f}, "Multiplicity FV0A"}; - ConfigurableAxis axisRawCentrality{"axisRawCentrality", {VARIABLE_WIDTH, 0.000f, 52.320f, 75.400f, 95.719f, 115.364f, 135.211f, 155.791f, 177.504f, 200.686f, 225.641f, 252.645f, 281.906f, 313.850f, 348.302f, 385.732f, 426.307f, 470.146f, 517.555f, 568.899f, 624.177f, 684.021f, 748.734f, 818.078f, 892.577f, 973.087f, 1058.789f, 1150.915f, 1249.319f, 1354.279f, 1465.979f, 1584.790f, 1710.778f, 1844.863f, 1985.746f, 2134.643f, 2291.610f, 2456.943f, 2630.653f, 2813.959f, 3006.631f, 3207.229f, 3417.641f, 3637.318f, 3865.785f, 4104.997f, 4354.938f, 4615.786f, 4885.335f, 5166.555f, 5458.021f, 5762.584f, 6077.881f, 6406.834f, 6746.435f, 7097.958f, 7462.579f, 7839.165f, 8231.629f, 8635.640f, 9052.000f, 9484.268f, 9929.111f, 10389.350f, 10862.059f, 11352.185f, 11856.823f, 12380.371f, 12920.401f, 13476.971f, 14053.087f, 14646.190f, 15258.426f, 15890.617f, 16544.433f, 17218.024f, 17913.465f, 18631.374f, 19374.983f, 20136.700f, 20927.783f, 21746.796f, 22590.880f, 23465.734f, 24372.274f, 25314.351f, 26290.488f, 27300.899f, 28347.512f, 29436.133f, 30567.840f, 31746.818f, 32982.664f, 34276.329f, 35624.859f, 37042.588f, 38546.609f, 40139.742f, 41837.980f, 43679.429f, 45892.130f, 400000.000f}, "raw centrality signal"}; // for QA - ConfigurableAxis axisOccupancy{"axisOccupancy", {VARIABLE_WIDTH, 0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 4500.0f, 6000.0f, 8000.0f, 10000.0f, 50000.0f}, "Occupancy"}; - - // topological variable QA axes - ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {20, 0.0f, 1.0f}, "DCA (cm)"}; - ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; - ConfigurableAxis axisPointingAngle{"axisPointingAngle", {20, 0.0f, 2.0f}, "pointing angle (rad)"}; - ConfigurableAxis axisV0Radius{"axisV0Radius", {20, 0.0f, 60.0f}, "V0 2D radius (cm)"}; + // Topological variable QA axes: + ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {30, 0.0f, 2.0f}, "DCA (cm)"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 1.2f}, "DCA (cm)"}; // This should follow v0Selections.dcav0dau's default + ConfigurableAxis axisPointingAngle{"axisPointingAngle", {20, 0.0f, 0.4f}, "pointing angle (rad)"}; // cos(0.4 rad) is ~0.92 for cosPA + ConfigurableAxis axisV0Radius{"axisV0Radius", {30, 0.0f, 60.0f}, "V0 2D radius (cm)"}; + // PID: ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {200, -10.0f, 10.0f}, "N sigma TPC"}; ConfigurableAxis axisTPCsignal{"axisTPCsignal", {200, 0.0f, 200.0f}, "TPC signal"}; ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "N sigma TOF"}; @@ -337,31 +351,29 @@ struct lambdajetpolarizationions { ConfigurableAxis axisPhiMod{"axisPhiMod", {100, 0.0f, constants::math::TwoPI / 18}, "Azimuth angle wrt TPC sector (rad.)"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; ConfigurableAxis axisRapidity{"axisRapidity", {50, -1.0f, 1.0f}, "y"}; - ConfigurableAxis axisITSchi2{"axisITSchi2", {100, 0.0f, 100.0f}, "#chi^{2} per ITS clusters"}; - ConfigurableAxis axisTPCchi2{"axisTPCchi2", {100, 0.0f, 100.0f}, "#chi^{2} per TPC clusters"}; - ConfigurableAxis axisTPCrowsOverFindable{"axisTPCrowsOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC crossed rows over findable clusters"}; - ConfigurableAxis axisTPCfoundOverFindable{"axisTPCfoundOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC found over findable clusters"}; - ConfigurableAxis axisTPCsharedClusters{"axisTPCsharedClusters", {101, -0.005f, 1.005f}, "Fraction of TPC shared clusters"}; - // AP plot axes ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "V0 AP alpha"}; ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 0.5f}, "V0 AP alpha"}; - // Track quality axes ConfigurableAxis axisTPCrows{"axisTPCrows", {160, 0.0f, 160.0f}, "N TPC rows"}; ConfigurableAxis axisITSclus{"axisITSclus", {7, 0.0f, 7.0f}, "N ITS Clusters"}; - ConfigurableAxis axisITScluMap{"axisITScluMap", {128, -0.5f, 127.5f}, "ITS Cluster map"}; - ConfigurableAxis axisDetMap{"axisDetMap", {16, -0.5f, 15.5f}, "Detector use map"}; - ConfigurableAxis axisITScluMapCoarse{"axisITScluMapCoarse", {16, -3.5f, 12.5f}, "ITS Coarse cluster map"}; - ConfigurableAxis axisDetMapCoarse{"axisDetMapCoarse", {5, -0.5f, 4.5f}, "Detector Coarse user map"}; + // ConfigurableAxis axisITScluMap{"axisITScluMap", {128, -0.5f, 127.5f}, "ITS Cluster map"}; + // ConfigurableAxis axisDetMap{"axisDetMap", {16, -0.5f, 15.5f}, "Detector use map"}; + // ConfigurableAxis axisITScluMapCoarse{"axisITScluMapCoarse", {16, -3.5f, 12.5f}, "ITS Coarse cluster map"}; + // ConfigurableAxis axisDetMapCoarse{"axisDetMapCoarse", {5, -0.5f, 4.5f}, "Detector Coarse user map"}; + ConfigurableAxis axisITSchi2{"axisITSchi2", {100, 0.0f, 100.0f}, "#chi^{2} per ITS clusters"}; + ConfigurableAxis axisTPCchi2{"axisTPCchi2", {100, 0.0f, 100.0f}, "#chi^{2} per TPC clusters"}; + // ConfigurableAxis axisTPCrowsOverFindable{"axisTPCrowsOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC crossed rows over findable clusters"}; + // ConfigurableAxis axisTPCfoundOverFindable{"axisTPCfoundOverFindable", {120, 0.0f, 1.2f}, "Fraction of TPC found over findable clusters"}; + // ConfigurableAxis axisTPCsharedClusters{"axisTPCsharedClusters", {101, -0.005f, 1.005f}, "Fraction of TPC shared clusters"}; // MC coll assoc QA axis ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; // Jet QA axes: - ConfigurableAxis JetsPerEvent{"JetsPerEvent", {20, 0, 20}, "Jets per event"}; - - ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {200, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! + ConfigurableAxis axisJetsPerEvent{"axisJetsPerEvent", {20, 0, 20}, "Jets per event"}; + ConfigurableAxis axisJetConstituents{"axisJetConstituents", {200, 0, 200}, "Number of jet constituents"}; + ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {200, 0.f, 200.f}, "Leading particle p_{T} [GeV/c]"}; // Simpler version! ConfigurableAxis axisJetPt{"axisJetPt", {200, 0.f, 200.f}, "Jet p_{t} (GeV)"}; ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1.f, 1.f}, "cos(#Delta #theta_{jet})"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {50, -constants::math::PI, constants::math::PI}, "#Delta #phi"}; @@ -374,14 +386,14 @@ struct lambdajetpolarizationions { // (TODO: create a reasonable track selection for full, photon, and Z-tagged jet tracks, including detector angular acceptance parameters for EMCal) struct : ConfigurableGroup { std::string prefix = "jetConfigurations"; // JSON group name - Configurable minJetPt{"minJetPt", 30.0f, "Minimum reconstructed pt of the jet (GeV/c)"}; // Something in between pp and PbPb minima. Change for bkgSubtraction true or false! - Configurable radiusJet{"radiusJet", 0.4f, "Jet resolution parameter (R)"}; // (TODO: check if the JE people don't define this as a rescaled int to not lose precision for stricter selections) + Configurable minJetPt{"minJetPt", 20.0f, "Minimum reconstructed pt of the jet (GeV/c)"}; // Something in between pp and PbPb minima. Change for bkgSubtraction true or false! + Configurable radiusJet{"radiusJet", 0.4f, "Jet resolution parameter (R)"}; // Notice that the maximum Eta of the jet will then be 0.9 - R to keep the jet contained within the ITS+TPC barrel. Configurable jetAlgorithm{"jetAlgorithm", kAntiKt, "jet clustering algorithm. 0 = kT, 1 = C/A, 2 = Anti-kT"}; Configurable jetRecombScheme{"jetRecombScheme", kEScheme, "Jet recombination scheme: 0: E_scheme, 1: pT-scheme, 2: pt2-scheme, 7: WTA_pt_scheme"}; // See PWGJE/JetFinders/jetFinder.h for more info. Configurable bkgSubtraction{"bkgSubtraction", kNoSubtraction, "Jet background subtraction: No subtraction (false), Area (true), Constituent (TODO)"}; // Selection bool for background subtraction strategy - Configurable GhostedAreaSpecRapidity{"GhostedAreaSpecRapidity", 1.1, "Max ghost particle rapidity for jet area estimates"}; // At least 1.0 for tracks and jets within the |eta| < 0.9 window of ITS+TPC + Configurable ghostedAreaSpecRapidity{"ghostedAreaSpecRapidity", 1.1, "Max ghost particle rapidity for jet area estimates"}; // At least 1.0 for tracks and jets within the |eta| < 0.9 window of ITS+TPC // Using an enum for readability: Configurable jetType{"jetType", kChargedJet, "Jet type: 0: Charged Jet, 1: Full Jet, 2: Photon-tagged, 3: Z-tagged"}; // TODO: implement full, photon and Z jets // (TODO: check the maximum pT of jets used in my analyses! If it is way too hard, it might not be the best jet to use!) @@ -457,6 +469,9 @@ struct lambdajetpolarizationions { // Configurable minTPCrowsOverFindableClusters{"minTPCrowsOverFindableClusters", -1, "minimum nbr of TPC crossed rows over findable clusters"}; // Configurable minTPCfoundOverFindableClusters{"minTPCfoundOverFindableClusters", 0.8f, "minimum nbr of found over findable TPC clusters"}; + // QA checks for detector asymmetry: + Configurable forceNoITS{"forceNoITS", false, "Use onlt TPC-onwards tracks as pseudoJet candidates to QA asymmetries/pile-up in detector"}; + // Jets typical cuts (suppress non-primary candidates): Configurable doDCAcuts{"doDCAcuts", false, "Apply DCA cuts to jet candidates (biases towards primary-vertex/prompt hadron jets)"}; Configurable maxDCAz{"maxDCAz", 3.2f, "Max DCAz to primary vertex [cm] (remove pileup influence)"}; @@ -470,9 +485,10 @@ struct lambdajetpolarizationions { JetBkgSubUtils backgroundSub; + /// \brief Books every histogram used by the producer and sets up the CCDB/RCT services. void init(InitContext const&) { - // setting CCDB service + // Setting CCDB service ccdb->setURL(ccdbConfigurations.ccdbUrl); ccdb->setCaching(true); ccdb->setFatalWhenNull(false); @@ -481,88 +497,94 @@ struct lambdajetpolarizationions { rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +20.5f}}); + histos.add("hEventSelection", "hEventSelection;;Counts", kTH1D, {{24, -0.5f, +23.5f}}); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(6, "posZ cut"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(6, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(7, "posZ cut"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(8, "kIsVertexITSTPC"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(9, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(10, "kIsVertexTOFmatched"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(11, "kIsVertexTRDmatched"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoSameBunchPileup"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStd"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeStrict"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoCollInTimeRangeNarrow"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStd"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "kNoCollInRofStrict"); if (doPPAnalysis) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "INEL>1"); } else { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "Below min occup."); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "Above max occup."); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "Below min occup."); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Above max occup."); } - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Below min IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(22, "hasRingJet"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(23, "hasRingV0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Below min IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "Above max IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(22, "RCT flags"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(23, "hasRingJet"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(24, "hasRingV0"); // (notice we lack a hasRingJet AND hasRingV0 bin because the tasks run separately on all events!) // (this QA number can be obtained at derived data level with ease) + // Cache the x-values of the last two stages for convenience: + const int nEvSelBins = histos.get(HIST("hEventSelection"))->GetXaxis()->GetNbins(); + mBinHasRingJet = nEvSelBins - 2; + mBinHasRingV0 = nEvSelBins - 1; - histos.add("Centrality/hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); - histos.add("Centrality/hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("Centrality/hEventCentrality", "hEventCentrality;Centrality (%);Counts", kTH1D, {{101, 0.0f, 101.0f}}); + histos.add("Centrality/hCentralityVsNch", "hCentralityVsNch;Centrality (%);N_{ch}", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); if (doEventQA) { - histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{23, -0.5f, +20.5f}, {101, 0.0f, 101.0f}}); + histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality;;Centrality (%)", kTH2D, {{24, -0.5f, +23.5f}, {101, 0.0f, 101.0f}}); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(6, "posZ cut"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(6, "kIsGoodITSLayersAll"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(7, "posZ cut"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(8, "kIsVertexITSTPC"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(9, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(10, "kIsVertexTOFmatched"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(11, "kIsVertexTRDmatched"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(12, "kNoSameBunchPileup"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStd"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeStrict"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(15, "kNoCollInTimeRangeNarrow"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStd"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "kNoCollInRofStrict"); if (doPPAnalysis) { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "INEL>0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "INEL>1"); } else { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "Below min occup."); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "Above max occup."); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "Below min occup."); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "Above max occup."); } - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "Below min IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(22, "hasRingJet"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(23, "hasRingV0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Below min IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "Above max IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(22, "RCT flags"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(23, "hasRingJet"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(24, "hasRingV0"); // Centrality: - histos.add("Centrality/hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); - histos.add("Centrality/hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); - histos.add("Centrality/hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); - histos.add("Centrality/hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); + histos.add("Centrality/hEventCentVsMultFT0M", "hEventCentVsMultFT0M;Centrality (%);Mult. FT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); + histos.add("Centrality/hEventCentVsMultFT0C", "hEventCentVsMultFT0C;Centrality (%);Mult. FT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); + histos.add("Centrality/hEventCentVsMultFV0A", "hEventCentVsMultFV0A;Centrality (%);Mult. FV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); + histos.add("Centrality/hEventMultFT0CvsMultFV0A", "hEventMultFT0CvsMultFV0A;Mult. FT0C;Mult. FV0A", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisMultFV0A}); } - histos.add("hEventPVz", "hEventPVz", kTH1D, {{100, -20.0f, +20.0f}}); - histos.add("hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); + histos.add("hEventPVz", "hEventPVz;PV_{z} [cm];Counts", kTH1D, {axisConfigurations.axisPVz}); + histos.add("hCentralityVsPVz", "hCentralityVsPVz;Centrality (%);PV_{z} [cm]", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisPVz}); // (TODO: add MC centrality vs PVz histos) - histos.add("hEventOccupancy", "hEventOccupancy", kTH1D, {axisConfigurations.axisOccupancy}); - histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisOccupancy}); - histos.add("hInteractionRate", "hInteractionRate", kTH1D, {axisConfigurations.axisIRBinning}); - histos.add("hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisIRBinning}); - histos.add("hInteractionRateVsOccupancy", "hInteractionRateVsOccupancy", kTH2D, {axisConfigurations.axisIRBinning, axisConfigurations.axisOccupancy}); + histos.add("hEventOccupancy", "hEventOccupancy;Occupancy;Counts", kTH1D, {axisConfigurations.axisOccupancy}); + histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy;Centrality (%);Occupancy", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisOccupancy}); + histos.add("hInteractionRate", "hInteractionRate;Interaction Rate;Counts", kTH1D, {axisConfigurations.axisIRBinning}); + histos.add("hCentralityVsInteractionRate", "hCentralityVsInteractionRate;Centrality (%);Interaction Rate", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisIRBinning}); + histos.add("hInteractionRateVsOccupancy", "hInteractionRateVsOccupancy;Interaction Rate;Occupancy", kTH2D, {axisConfigurations.axisIRBinning, axisConfigurations.axisOccupancy}); // for QA and test purposes // auto hRawCentrality = histos.add("Centrality/hRawCentrality", "hRawCentrality", kTH1D, {axisConfigurations.axisRawCentrality}); @@ -625,7 +647,9 @@ struct lambdajetpolarizationions { {p + "TOF #Delta t #pi", v0Selections.maxDeltaTimePion < 1e+9}, {p + "TOF PID p", v0Selections.tofPidNsigmaCutLaPr < 1e+6}, {p + "TOF PID #pi", v0Selections.tofPidNsigmaCutLaPi < 1e+6}, - {p + "c#tau", v0Selections.lambdaLifetimeCut > 0}}); + {p + "c#tau", v0Selections.lambdaLifetimeCut > 0}, + {p + "Ambiguous rejection", true}, + {p + "Final accepted", true}}); }; constexpr bool Lambda = true; // Some constexpr to make it more readable (works at compile level) constexpr bool AntiLambda = false; // "false" is just a flag for this addHypothesis function! It just means fill "AntiLambda" labels @@ -640,12 +664,35 @@ struct lambdajetpolarizationions { lbl = "#color[16]{(off) " + lbl + "}"; hSelectionV0s->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); // First non-underflow bin is bin 1 } + + // TH2D: "selection flow" vs "Lambda invariant mass" (to investigate whether we are removing background or just signal) + auto h2dSelectionLambdaMass = histos.add("GeneralQA/h2dSelectionLambdaMass", "V0 selection flow vs M_{#Lambda}; ; M_{#Lambda} (GeV/#it{c}^{2})", kTH2D, + {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, + axisConfigurations.axisLambdaMassSigExtract}); + for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { + auto lbl = v0LambdaSelectionLabels[i].label; + if (!v0LambdaSelectionLabels[i].enabled) + lbl = "#color[16]{(off) " + lbl + "}"; + h2dSelectionLambdaMass->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); + } + + // Same for AntiLambda mass hypothesis: + auto h2dSelectionAntiLambdaMass = histos.add("GeneralQA/h2dSelectionAntiLambdaMass", "V0 selection flow vs M_{#bar{#Lambda}}; ; M_{#bar{#Lambda}} (GeV/#it{c}^{2})", kTH2D, + {{static_cast(v0LambdaSelectionLabels.size()), -0.5, static_cast(v0LambdaSelectionLabels.size()) - 0.5}, + axisConfigurations.axisLambdaMassSigExtract}); + for (size_t i = 0; i < v0LambdaSelectionLabels.size(); ++i) { + auto lbl = v0LambdaSelectionLabels[i].label; + if (!v0LambdaSelectionLabels[i].enabled) + lbl = "#color[16]{(off) " + lbl + "}"; + h2dSelectionAntiLambdaMass->GetXaxis()->SetBinLabel(i + 1, lbl.c_str()); + } //////////////////////////////////////////////// // Jet track candidate selection flow (analogous to hSelectionV0s): // Each label's "enabled" flag reflects whether the corresponding configurable // makes that cut active, so disabled stages are shown in grey in the output. std::vector jetTrackSelectionLabels = { {"All track candidates", true}, + {"forceNoITS", pseudoJetCandidateTrackSelections.forceNoITS}, {"ITS clusters (min)", pseudoJetCandidateTrackSelections.minITSnCls >= 0}, {"TPC crossed rows (min)", pseudoJetCandidateTrackSelections.minNCrossedRowsTPC > 0}, {"TPC #chi^{2}/N_{cls} (max)", pseudoJetCandidateTrackSelections.maxChi2TPC < 1.e8f}, @@ -667,257 +714,252 @@ struct lambdajetpolarizationions { // Histograms versus mass: if (analyseLambda) { - histos.add("Lambda/h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("Lambda/h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // Non-UPC info - histos.add("Lambda/h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("Lambda/h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality;Centrality (%);N_{#Lambda} per event", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("Lambda/h3dMassLambda", "h3dMassLambda;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // // Non-UPC info + // histos.add("Lambda/h3dMassLambdaHadronic", "h3dMassLambdaHadronic;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); if (doTPCQA) { - histos.add("Lambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dPosTPCsignal", "h3dPosTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dNegTPCsignal", "h3dNegTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("Lambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("Lambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dPosTPCsignal", "h3dPosTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dNegTPCsignal", "h3dNegTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("Lambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("Lambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); } if (doTOFQA) { - histos.add("Lambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("Lambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("Lambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("Lambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("Lambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); } // (TODO: add collision association capabilities in MC) - if (doEtaPhiQA) { - histos.add("Lambda/h5dV0PhiVsEta", "h5dV0PhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("Lambda/h5dPosPhiVsEta", "h5dPosPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("Lambda/h5dNegPhiVsEta", "h5dNegPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - } } if (analyseAntiLambda) { - histos.add("AntiLambda/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); - histos.add("AntiLambda/h3dMassAntiLambda", "h3dMassAntiLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - // Non-UPC info - histos.add("AntiLambda/h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("AntiLambda/h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality;Centrality (%);N_{#bar{#Lambda}} per event", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + histos.add("AntiLambda/h3dMassAntiLambda", "h3dMassAntiLambda;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // // Non-UPC info + // histos.add("AntiLambda/h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic;Centrality (%);p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); if (doTPCQA) { - histos.add("AntiLambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dPosTPCsignal", "h3dPosTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dNegTPCsignal", "h3dNegTPCsignal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); - histos.add("AntiLambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); - histos.add("AntiLambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dPosNsigmaTPC", "h3dPosNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dNegNsigmaTPC", "h3dNegNsigmaTPC;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dPosTPCsignal", "h3dPosTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dNegTPCsignal", "h3dNegTPCsignal;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPtot", "h3dPosNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPtot", "h3dNegNsigmaTPCvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dPosTPCsignalVsTrackPtot", "h3dPosTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dNegTPCsignalVsTrackPtot", "h3dNegTPCsignalVsTrackPtot;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dPosNsigmaTPCvsTrackPt", "h3dPosNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dNegNsigmaTPCvsTrackPt", "h3dNegNsigmaTPCvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TPC}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTPC}); + histos.add("AntiLambda/h3dPosTPCsignalVsTrackPt", "h3dPosTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); + histos.add("AntiLambda/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt;Centrality (%);p_{T} [GeV/c];TPC signal", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); } if (doTOFQA) { - histos.add("AntiLambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); - histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); - } - if (doEtaPhiQA) { - histos.add("AntiLambda/h5dV0PhiVsEta", "h5dV0PhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("AntiLambda/h5dPosPhiVsEta", "h5dPosPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); - histos.add("AntiLambda/h5dNegPhiVsEta", "h5dNegPhiVsEta", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPhi, axisConfigurations.axisEta}); + histos.add("AntiLambda/h3dPosNsigmaTOF", "h3dPosNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dNegNsigmaTOF", "h3dNegNsigmaTOF;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dPosTOFdeltaT", "h3dPosTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dNegTOFdeltaT", "h3dNegTOFdeltaT;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPtot", "h3dPosNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPtot", "h3dNegNsigmaTOFvsTrackPtot;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPtot", "h3dPosTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPtot", "h3dNegTOFdeltaTvsTrackPtot;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dPosNsigmaTOFvsTrackPt", "h3dPosNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dNegNsigmaTOFvsTrackPt", "h3dNegNsigmaTOFvsTrackPt;Centrality (%);p_{T} [GeV/c];N#sigma_{TOF}", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); + histos.add("AntiLambda/h3dPosTOFdeltaTvsTrackPt", "h3dPosTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); + histos.add("AntiLambda/h3dNegTOFdeltaTvsTrackPt", "h3dNegTOFdeltaTvsTrackPt;Centrality (%);p_{T} [GeV/c];TOF #Delta t [ps]", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); } } if (analyseLambda) { - histos.add("hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("Lambda/hLambdasPerEvent", "hLambdasPerEvent", kTH1D, {{15, 0, 15}}); + histos.add("hMassLambda", "hMassLambda;M(p#pi) [GeV/c];Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("Lambda/hLambdasPerEvent", "hLambdasPerEvent;N_{#Lambda} per event;Counts", kTH1D, {{15, 0, 15}}); } if (analyseAntiLambda) { - histos.add("hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent", kTH1D, {{15, 0, 15}}); + histos.add("hMassAntiLambda", "hMassAntiLambda;M(p#pi) [GeV/c];Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("AntiLambda/hAntiLambdasPerEvent", "hAntiLambdasPerEvent;N_{#bar{#Lambda}} per event;Counts", kTH1D, {{15, 0, 15}}); } if (analyseLambda && analyseAntiLambda) { - histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates", kTH1D, {{1, 0, 1}}); - histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent", kTH1D, {{15, 0, 15}}); + // histos.add("hAmbiguousLambdaCandidates", "hAmbiguousLambdaCandidates;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // No longer required: v0 selection flow already considers this + histos.add("hAmbiguousPerEvent", "hAmbiguousPerEvent;N_{ambiguous} per event;Counts", kTH1D, {{15, 0, 15}}); + histos.add("hNonAmbiguousPerEvent", "hNonAmbiguousPerEvent;N_{non-ambiguous} per event;Counts", kTH1D, {{25, 0, 25}}); // To understand the population of correlated Lambda-likes per event + histos.add("hLambdasAndAntiLambdasPerEvent", "hLambdasAndAntiLambdasPerEvent;N_{#Lambda}+N_{#bar{#Lambda}} per event;Counts", kTH1D, {{25, 0, 25}}); // Alternative check that shows how bad is the possibly correlated full population (Ambig+NonAmbig) } // QA histograms if requested if (doV0KinematicQA) { if (analyseLambda) { // --- Basic kinematics --- - histos.add("V0KinematicQA/Lambda/hPt", "Lambda p_{T}", kTH1D, {axisConfigurations.axisPt}); - histos.add("V0KinematicQA/Lambda/hY", "Lambda rapidity", kTH1D, {axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/Lambda/hPhi", "Lambda #varphi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/Lambda/hPt", "Lambda p_{T};p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisPt}); + histos.add("V0KinematicQA/Lambda/hY", "Lambda rapidity;y;Counts", kTH1D, {axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/Lambda/hPhi", "Lambda #varphi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // --- Mass correlations --- - histos.add("V0KinematicQA/Lambda/hMassVsPt", "Lambda mass vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/Lambda/hMassVsY", "Lambda mass vs y", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/Lambda/hMassVsPhi", "Lambda mass vs #varphi", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/Lambda/hMassVsPt", "Lambda mass vs p_{T};p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/Lambda/hMassVsY", "Lambda mass vs y;y;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/Lambda/hMassVsPhi", "Lambda mass vs #varphi;#varphi;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); // --- Kinematic correlations --- - histos.add("V0KinematicQA/Lambda/hYVsPt", "Lambda y vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/Lambda/hPhiVsPt", "Lambda #varphi vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/Lambda/hYVsPt", "Lambda y vs p_{T};p_{T} [GeV/c];y", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/Lambda/hPhiVsPt", "Lambda #varphi vs p_{T};p_{T} [GeV/c];#varphi", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); } if (analyseAntiLambda) { // --- Basic kinematics --- - histos.add("V0KinematicQA/AntiLambda/hPt", "AntiLambda p_{T}", kTH1D, {axisConfigurations.axisPt}); - histos.add("V0KinematicQA/AntiLambda/hY", "AntiLambda rapidity", kTH1D, {axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/AntiLambda/hPhi", "AntiLambda #varphi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/AntiLambda/hPt", "AntiLambda p_{T};p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisPt}); + histos.add("V0KinematicQA/AntiLambda/hY", "AntiLambda rapidity;y;Counts", kTH1D, {axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/AntiLambda/hPhi", "AntiLambda #varphi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // --- Mass correlations --- - histos.add("V0KinematicQA/AntiLambda/hMassVsPt", "AntiLambda mass vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/AntiLambda/hMassVsY", "AntiLambda mass vs y", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); - histos.add("V0KinematicQA/AntiLambda/hMassVsPhi", "AntiLambda mass vs #varphi", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/AntiLambda/hMassVsPt", "AntiLambda mass vs p_{T};p_{T} [GeV/c];M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/AntiLambda/hMassVsY", "AntiLambda mass vs y;y;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisLambdaMass}); + histos.add("V0KinematicQA/AntiLambda/hMassVsPhi", "AntiLambda mass vs #varphi;#varphi;M(p#pi) [GeV/c]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisLambdaMass}); // --- Kinematic correlations --- - histos.add("V0KinematicQA/AntiLambda/hYVsPt", "AntiLambda y vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); - histos.add("V0KinematicQA/AntiLambda/hPhiVsPt", "AntiLambda #varphi vs p_{T}", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); + histos.add("V0KinematicQA/AntiLambda/hYVsPt", "AntiLambda y vs p_{T};p_{T} [GeV/c];y", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisRapidity}); + histos.add("V0KinematicQA/AntiLambda/hPhiVsPt", "AntiLambda #varphi vs p_{T};p_{T} [GeV/c];#varphi", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPhi}); } } - if (doCompleteTopoQA) { - if (analyseLambda) { - histos.add("Lambda/h4dPosDCAToPV", "h4dPosDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/h4dNegDCAToPV", "h4dNegDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/h4dDCADaughters", "h4dDCADaughters", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAdau}); - histos.add("Lambda/h4dPointingAngle", "h4dPointingAngle", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPointingAngle}); - histos.add("Lambda/h4dV0Radius", "h4dV0Radius", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisV0Radius}); - } - if (analyseAntiLambda) { - histos.add("AntiLambda/h4dPosDCAToPV", "h4dPosDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/h4dNegDCAToPV", "h4dNegDCAToPV", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/h4dDCADaughters", "h4dDCADaughters", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisDCAdau}); - histos.add("AntiLambda/h4dPointingAngle", "h4dPointingAngle", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisPointingAngle}); - histos.add("AntiLambda/h4dV0Radius", "h4dV0Radius", kTHnD, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisLambdaMass, axisConfigurations.axisV0Radius}); - } - + if (doTopoQA) { // For all received candidates: - histos.add("V0KinematicQA/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("V0KinematicQA/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("V0KinematicQA/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("V0KinematicQA/hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("V0KinematicQA/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("V0KinematicQA/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("V0KinematicQA/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("V0KinematicQA/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("V0KinematicQA/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("V0KinematicQA/hPosDCAToPV", "hPosDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("V0KinematicQA/hNegDCAToPV", "hNegDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("V0KinematicQA/hDCADaughters", "hDCADaughters;DCAdau [cm];Counts", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("V0KinematicQA/hPointingAngle", "hPointingAngle;Pointing angle [rad];Counts", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("V0KinematicQA/hV0Radius", "hV0Radius;V0 radius [cm];Counts", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("V0KinematicQA/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("V0KinematicQA/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("V0KinematicQA/h2dPositivePtVsPhi", "h2dPositivePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("V0KinematicQA/h2dNegativePtVsPhi", "h2dNegativePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); if (analyseLambda) { - histos.add("Lambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("Lambda/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("Lambda/hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("Lambda/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("Lambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("Lambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("Lambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("Lambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("Lambda/hPosDCAToPV", "hPosDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("Lambda/hNegDCAToPV", "hNegDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("Lambda/hDCADaughters", "hDCADaughters;DCAdau [cm];Counts", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("Lambda/hPointingAngle", "hPointingAngle;Pointing angle [rad];Counts", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("Lambda/hV0Radius", "hV0Radius;V0 radius [cm];Counts", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("Lambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("Lambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("Lambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("Lambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); } if (analyseAntiLambda) { - histos.add("AntiLambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); - histos.add("AntiLambda/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); - histos.add("AntiLambda/hPointingAngle", "hPointingAngle", kTH1D, {axisConfigurations.axisPointingAngle}); - histos.add("AntiLambda/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); - histos.add("AntiLambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("AntiLambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); - histos.add("AntiLambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); - histos.add("AntiLambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("AntiLambda/hPosDCAToPV", "hPosDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("AntiLambda/hNegDCAToPV", "hNegDCAToPV;DCAtoPV [cm];Counts", kTH1D, {axisConfigurations.axisDCAtoPV}); + histos.add("AntiLambda/hDCADaughters", "hDCADaughters;DCAdau [cm];Counts", kTH1D, {axisConfigurations.axisDCAdau}); + histos.add("AntiLambda/hPointingAngle", "hPointingAngle;Pointing angle [rad];Counts", kTH1D, {axisConfigurations.axisPointingAngle}); + histos.add("AntiLambda/hV0Radius", "hV0Radius;V0 radius [cm];Counts", kTH1D, {axisConfigurations.axisV0Radius}); + histos.add("AntiLambda/h2dPositiveITSvsTPCpts", "h2dPositiveITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("AntiLambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts;N TPC crossed rows;N ITS clusters", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); + histos.add("AntiLambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("AntiLambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi;p_{T} [GeV/c];#varphi mod TPC sector", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); } } // Check ambiguous candidates in AP space: - histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosKinematicSelected", "h2dArmenterosKinematicSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelected", "h2dArmenterosFullSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedLambda", "h2dArmenterosFullSelectedLambda", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedAntiLambda", "h2dArmenterosFullSelectedAntiLambda", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous", "h2dArmenterosFullSelectedNonAmbiguous", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); - histos.add("GeneralQA/h2dArmenterosFullSelectedAmbiguous", "h2dArmenterosFullSelectedAmbiguous", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosKinematicSelected", "h2dArmenterosKinematicSelected;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelected", "h2dArmenterosFullSelected;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedLambda", "h2dArmenterosFullSelectedLambda;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedAntiLambda", "h2dArmenterosFullSelectedAntiLambda;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous", "h2dArmenterosFullSelectedNonAmbiguous;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + histos.add("GeneralQA/h2dArmenterosFullSelectedAmbiguous", "h2dArmenterosFullSelectedAmbiguous;Armenteros #alpha;Armenteros q_{T} (GeV/c)", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); // Jets histograms: - // Histogram that needs to be present even out of QA: - histos.add("hEventsWithJet", "hEventsWithJet", kTH1D, {{1, 0, 1}}); - histos.add("hJetsPerEvent", "hJetsPerEvent", kTH1D, {axisConfigurations.JetsPerEvent}); - // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) - // number of jets per event + // Histograms that need to be present even out of extensive QA-mode: + histos.add("hEventsWithJet", "hEventsWithJet;Integrated counts;Counts", kTH1D, {{1, 0, 1}}); // counter of events with jet (could be interesting to compare with the minimum pT cut or between the background subtraction vs no background subtraction cases) + histos.add("hJetsPerEvent", "hJetsPerEvent;Jets per event;Counts", kTH1D, {axisConfigurations.axisJetsPerEvent}); // number of jets per event if (doJetKinematicsQA) { - histos.add("JetKinematicsQA/hJetPt", "hJetPt", kTH1D, {axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/hJetEta", "hJetEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetKinematicsQA/hJetPhi", "hJetPhi", kTH1D, {axisConfigurations.axisPhi}); - - histos.add("JetKinematicsQA/hCosThetaToLeadingJet", "hCosThetaToLeadingJet", kTH1D, {axisConfigurations.axisCosTheta}); - histos.add("JetKinematicsQA/hDeltaPhiToLeadingJet", "hDeltaPhiToLeadingJet", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/hDeltaEtaToLeadingJet", "hDeltaEtaToLeadingJet", kTH1D, {axisConfigurations.axisDeltaEta}); - histos.add("JetKinematicsQA/hDeltaRToLeadingJet", "hDeltaRToLeadingJet", kTH1D, {axisConfigurations.axisDeltaR}); - - histos.add("JetKinematicsQA/hLeadingJetPt", "hLeadingJetPt", kTH1D, {axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetKinematicsQA/hLeadingJetPhi", "hLeadingJetPhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hJetPt", "hJetPt;Jet p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hJetEta", "hJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hJetPhi", "hJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/h2dJetEtaVsJetPhi", "h2dJetEtaVsJetPhi;#eta;#varphi", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPhi}); // An occupancy map of sorts. Checks for detector-dependent jet clustering artifacts + + histos.add("JetKinematicsQA/hCosThetaToLeadingJet", "hCosThetaToLeadingJet;cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add("JetKinematicsQA/hDeltaPhiToLeadingJet", "hDeltaPhiToLeadingJet;#Delta#varphi;Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/hDeltaEtaToLeadingJet", "hDeltaEtaToLeadingJet;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); + // Acceptance-edge QA: same three observables, but filled only for the jets rejected by the |eta| < 0.9 - R cut: + // (Those jets lose constituents on one side, so this measures the pT biasing) + histos.add("JetKinematicsQA/hJetEtaOutOfAcceptance", "hJetEtaOutOfAcceptance;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hDeltaEtaToLeadingJetOutOfAcceptance", "hDeltaEtaToLeadingJetOutOfAcceptance;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/hDeltaRToLeadingJetOutOfAcceptance", "hDeltaRToLeadingJetOutOfAcceptance;#Delta R;Counts", kTH1D, {axisConfigurations.axisDeltaR}); + + histos.add("JetKinematicsQA/hDeltaRToLeadingJet", "hDeltaRToLeadingJet;#Delta R;Counts", kTH1D, {axisConfigurations.axisDeltaR}); + + histos.add("JetKinematicsQA/hLeadingJetPt", "hLeadingJetPt;Jet p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hLeadingJetEta", "hLeadingJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hLeadingJetPhi", "hLeadingJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // 2D correlations: - histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisJetPt}); - histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead", "h2dCosThetaToLeadvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead", "h2dCosThetaToLeadvsDeltaEtaToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaEta}); - histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead", "h2dCosThetaToLeadvsDeltaRToLead", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaR}); - histos.add("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead", "h2dDeltaPhiToLeadvsDeltaEtaToLead", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // to see existence of back-to-back jets, and in which window + histos.add("JetKinematicsQA/h2dJetsPerEventvsLeadJetPt", "h2dJetsPerEventvsLeadJetPt;Jets per event;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsJetPt", "h2dJetsPerEventvsJetPt;Jets per event;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaPhiToLead", "h2dCosThetaToLeadvsDeltaPhiToLead;cos(#Delta#theta_{jet});#Delta#varphi", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaEtaToLead", "h2dCosThetaToLeadvsDeltaEtaToLead;cos(#Delta#theta_{jet});#Delta#eta", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/h2dCosThetaToLeadvsDeltaRToLead", "h2dCosThetaToLeadvsDeltaRToLead;cos(#Delta#theta_{jet});#Delta R", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaR}); + histos.add("JetKinematicsQA/h2dDeltaPhiToLeadvsDeltaEtaToLead", "h2dDeltaPhiToLeadvsDeltaEtaToLead;#Delta#varphi;#Delta#eta", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // to see existence of back-to-back jets, and in which window // Comparisons to jet energy: - histos.add("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead", "h2dJetPtvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead", "h2dJetEnergyvsDeltaPhiToLead", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead", "h2dJetEnergyvsCosThetaToLead", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisCosTheta}); + histos.add("JetKinematicsQA/h2dJetPtvsDeltaPhiToLead", "h2dJetPtvsDeltaPhiToLead;Jet p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetEnergyvsDeltaPhiToLead", "h2dJetEnergyvsDeltaPhiToLead;E_{jet} [GeV];#Delta#varphi", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetEnergyvsCosThetaToLead", "h2dJetEnergyvsCosThetaToLead;E_{jet} [GeV];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisEnergy, axisConfigurations.axisCosTheta}); // Jets per event vs correlation to lead jet - histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead", "h2dJetsPerEventvsDeltaPhiToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaPhi}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead", "h2dJetsPerEventvsDeltaEtaToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaEta}); - histos.add("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead", "h2dJetsPerEventvsCosThetaToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisCosTheta}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead", "h2dJetsPerEventvsDeltaPhiToLead;Jets per event;#Delta#varphi", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead", "h2dJetsPerEventvsDeltaEtaToLead;Jets per event;#Delta#eta", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); + histos.add("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead", "h2dJetsPerEventvsCosThetaToLead;Jets per event;cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); + + // For probing the correlation between number of jet constituents and centrality: + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsCentrality", "Constituents per Jet Vs Centrality;N constituents;Centrality (%)", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisCentrality}); // Not quite kinematics, but kept it under this QAing switch for now + histos.add("JetKinematicsQA/h2dConstituentsPerJetVsJetPt", "Constituents per Jet Vs Jet Pt;N constituents;Jet p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetConstituents, axisConfigurations.axisJetPt}); + + // Correlation with PVz: + histos.add("JetKinematicsQA/h2dJetEtaVsPVz", "Jet #eta Vs Primary Vertex Z;Jet #eta;PVz [cm]", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVzCoarse}); + histos.add("JetKinematicsQA/h2dJetPhiVsPVz", "Jet #phi Vs Primary Vertex Z;Jet #phi;PVz [cm]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPVzCoarse}); //////////////////////////// // Leading particle 1D QA: - histos.add("JetVsLeadingParticleQA/hLeadingParticlePt", "hLeadingParticlePt", kTH1D, {axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/hLeadingParticleEta", "hLeadingParticleEta", kTH1D, {axisConfigurations.axisEta}); - histos.add("JetVsLeadingParticleQA/hLeadingParticlePhi", "hLeadingParticlePhi", kTH1D, {axisConfigurations.axisPhi}); + histos.add("LeadingParticleQA/hLeadingParticlePt", "hLeadingParticlePt;Leading particle p_{T} [GeV/c];Counts", kTH1D, {axisConfigurations.axisLeadingParticlePt}); + histos.add("LeadingParticleQA/hLeadingParticleEta", "hLeadingParticleEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("LeadingParticleQA/hLeadingParticlePhi", "hLeadingParticlePhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); // 1D correlations to lead jet: - histos.add("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet", "hCosThetaLeadParticleToJet", kTH1D, {axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet", "hDeltaPhiLeadParticleToJet", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet", "hDeltaEtaToLeadParticleToJet", kTH1D, {axisConfigurations.axisDeltaEta}); + histos.add("LeadingParticleQA/hCosThetaLeadParticleToJet", "hCosThetaLeadParticleToJet;cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/hDeltaPhiLeadParticleToJet", "hDeltaPhiLeadParticleToJet;#Delta#varphi;Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/hDeltaEtaToLeadParticleToJet", "hDeltaEtaToLeadParticleToJet;#Delta#eta;Counts", kTH1D, {axisConfigurations.axisDeltaEta}); // Leading particle correlations: - histos.add("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", "h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); + histos.add("LeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead", "h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead;#Delta#varphi;#Delta#eta", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEta}); // Jets-per-event vs particle-to-lead correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisDeltaEta}); - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead", "h2dJetsPerEventvsDeltaPhiParticleToLead;Jets per event;#Delta#varphi", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead", "h2dJetsPerEventvsDeltaEtaParticleToLead;Jets per event;#Delta#eta", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisDeltaEta}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead", "h2dJetsPerEventvsCosThetaParticleToLead;Jets per event;cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisCosTheta}); // Main "Leading jet vs leading particle" correlations: - histos.add("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt", kTH2D, {axisConfigurations.JetsPerEvent, axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt", "h2dLeadJetPtvsLeadParticlePt", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisLeadingParticlePt}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead", "h2dLeadJetPtvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead", "h2dLeadParticlePtvsCosThetaParticleToLead", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisCosTheta}); - histos.add("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead", "h2dLeadJetPtvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); - histos.add("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead", "h2dLeadParticlePtvsDeltaPhiParticleToLead", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/h2dJetsPerEventvsLeadParticlePt", "h2dJetsPerEventvsLeadParticlePt;Jets per event;Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetsPerEvent, axisConfigurations.axisLeadingParticlePt}); + histos.add("LeadingParticleQA/h2dLeadJetPtvsLeadParticlePt", "h2dLeadJetPtvsLeadParticlePt;Jet p_{T} [GeV/c];Leading particle p_{T} [GeV/c]", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisLeadingParticlePt}); + histos.add("LeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead", "h2dLeadJetPtvsCosThetaParticleToLead;Jet p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead", "h2dLeadParticlePtvsCosThetaParticleToLead;Leading particle p_{T} [GeV/c];cos(#Delta#theta_{jet})", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisCosTheta}); + histos.add("LeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead", "h2dLeadJetPtvsDeltaPhiParticleToLead;Jet p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisJetPt, axisConfigurations.axisDeltaPhi}); + histos.add("LeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead", "h2dLeadParticlePtvsDeltaPhiParticleToLead;Leading particle p_{T} [GeV/c];#Delta#varphi", kTH2D, {axisConfigurations.axisLeadingParticlePt, axisConfigurations.axisDeltaPhi}); + + // Correlation with PVz: + histos.add("LeadingParticleQA/h2dLeadPEtaVsPVz", "LeadPtc #eta Vs Primary Vertex Z;LeadPtc #eta;PVz [cm]", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVzCoarse}); + histos.add("LeadingParticleQA/h2dLeadPPhiVsPVz", "LeadPtc #phi Vs Primary Vertex Z;LeadPtc #phi;PVz [cm]", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPVzCoarse}); } // inspect histogram sizes, please histos.print(); } + /// \brief Returns the collision centrality for the configured estimator (FT0M/FT0C/FV0A). Returns -1 if none matched. template auto getCentrality(TCollision const& collision) { @@ -930,6 +972,7 @@ struct lambdajetpolarizationions { return -1.f; } + /// \brief Fetches the magnetic field for the current run from CCDB (or uses customMagField, if set). No-op if already initialized for this run. template void initCCDB(TBC const& bc) { @@ -960,28 +1003,38 @@ struct lambdajetpolarizationions { int binValue = -1; // Starts at x=-1, which will go to bin 0 (underflow) in the definition of hSelectionV0s // Made it like this because we use ++binValue when filling, so the first filled // bin will always be x=0 due to operator precedence. - HistogramRegistry* histos = nullptr; // Had to pass the histos group to this struct, as it was not visible to the members of this struct + HistogramRegistry* histos = nullptr; // Had to pass the histos group to this struct, as it was not visible to the members of the struct + float mL = -1.f; // mLambda for current V0, -1 at initialization + float mAL = -1.f; // mAntiLambda for current V0, -1 at initialization + void resetForNewV0(float massLambda, float massAntiLambda) + { + binValue = -1; + mL = massLambda; + mAL = massAntiLambda; + } + void advanceTo(int targetBinX) { binValue = targetBinX - 1; } // next fill() lands at targetBin. Needed to deal with early exits at isLambda vs isAntiLambda checks - void resetForNewV0() { binValue = -1; } - // Advance to targetBinX, filling all intermediate bins. + void fill() + { + histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default + histos->fill(HIST("GeneralQA/h2dSelectionLambdaMass"), binValue, mL); + histos->fill(HIST("GeneralQA/h2dSelectionAntiLambdaMass"), binValue, mAL); + } // Use this for DISABLED cuts within a single hypothesis - // (shows pass-through count as a flat line, making it visually - // clear that the stage was not active). - // (Replaces N dummy fill() calls) + // (shows pass-through count as a flat line, making it visually clear that the stage was not active. Replaces N dummy fill() calls) + // fillUpTo advances through disabled bins, filling all three histograms uniformly. void fillUpTo(int targetBinX) { while (binValue < targetBinX) - histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); + fill(); } - - void advanceTo(int targetBinX) { binValue = targetBinX - 1; } // next fill() lands at targetBin. Needed to deal with early exits at isLambda vs isAntiLambda checks - void fill() { histos->fill(HIST("GeneralQA/hSelectionV0s"), ++binValue); } // Hardcoded hSelectionV0s histogram, as it will not change. Increments before filling, by default }; - V0SelectionFlowCounter V0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) - // Calculating some bins, for convenience: - int nGenericCuts = 31; // x=0 to x=30 - int nHypoCuts = 9; // per hypothesis (x=31..39 for Lambda) - int lambdaHypoEnd = nGenericCuts + nHypoCuts - 1; // x=39 + V0SelectionFlowCounter v0SelCounter{-1, &histos}; // Could initialize with any index (resetForNewV0 is always called for a new V0 anyways) + // Calculating some bins, for convenience: + const int nGenericCuts = 31; // x=0 to x=30 (bins 1 and 31, as the lower-edge is inclusive) + const int nHypoCuts = 11; // per hypothesis (x=31..41 for Lambda) + const int lambdaHypoEnd = nGenericCuts + nHypoCuts - 1; // x=41 (bin 42) + const int antiLambdaHypoEnd = nGenericCuts + 2 * nHypoCuts - 1; // x = 52 (bin 53) // Minimal helper to fill hSelectionJetTracks, mirroring V0SelectionFlowCounter. // Reset once per track candidate, fill once per passed cut stage. @@ -991,7 +1044,7 @@ struct lambdajetpolarizationions { void resetForNewTrack() { binValue = -1; } void fill() { histos->fill(HIST("GeneralQA/hSelectionJetTracks"), ++binValue); } }; - JetTrackSelectionFlowCounter JetTrackSelCounter{-1, &histos}; + JetTrackSelectionFlowCounter jetTrackSelCounter{-1, &histos}; // Short inlined helper to simplify QA inline void fillEventSelectionQA(int bin, float centrality) @@ -1017,11 +1070,12 @@ struct lambdajetpolarizationions { histos.fill(HIST("Centrality/hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); histos.fill(HIST("Centrality/hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); } - return; } ///////////////////////////////////////////// // Computation helper functions: + /// \brief Folds phi into the repeating ~20-degree TPC sector pattern used by the sector-boundary cut functions below. + /// \param sign track charge sign (+1 or -1) -- the fold direction depends on charge and field polarity. double computePhiMod(double phi, int sign) // Compute phi wrt to a TPC sector // Calculation taken from CF: https://github.com/AliceO2Group/O2Physics/blob/376392cb87349886a300c75fa2492b50b7f46725/PWGCF/Flow/Tasks/flowAnalysisGF.cxx#L470 @@ -1037,6 +1091,8 @@ struct lambdajetpolarizationions { return fmod(phi, o2::constants::math::PI / 9.0); } + /// \brief Checks the track against the TPC sector-boundary cut (see computePhiMod()). + /// \return true if the track is far enough from the boundary to be kept; false rejects it. bool isTrackFarFromTPCBoundary(double trackPt, double trackPhi, int sign) // check whether the track passes close to a TPC sector boundary { @@ -1057,10 +1113,19 @@ struct lambdajetpolarizationions { return dot / (magA * magB); } + const float nSigmaScale = 100.f; // The factor for packing the float as an int16_t + inline int16_t packNSigma(float x) + { + return static_cast(std::lround(x * nSigmaScale)); + } + ///////////////////////////////////////////// // Helper functions for event and candidate selection: + /// \brief Runs the full event-selection cutflow. returns false at the first failed cut (in configured order). + /// \param interactionRate output-only: filled with the fetched IR. + /// \param fillHists if true, also fills the per-stage QA histograms and the occupancy/IR/vertex histograms. template - bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, bool fillHists) + bool isEventAccepted(TCollision const& collision, TBC const& bc, float centrality, double& interactionRate, bool fillHists) { // check whether the collision passes our collision selections int selectionIdx = 0; // To loop over QA histograms. First bin is already filled: first call will already increment this index (not actually the bin index, but a value in the X axis). if (eventSelections.requireSel8 && !collision.sel8()) @@ -1079,6 +1144,10 @@ struct lambdajetpolarizationions { return false; if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); + if (eventSelections.requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + return false; + if (fillHists) + fillEventSelectionQA(++selectionIdx, centrality); const float collisionPVz = collision.posZ(); if (std::abs(collisionPVz) > eventSelections.maxZVtxPosition) @@ -1155,8 +1224,11 @@ struct lambdajetpolarizationions { if (fillHists) fillEventSelectionQA(++selectionIdx, centrality); - // Fetch interaction rate only if required (in order to limit ccdb calls) - const double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3 : -1; + // Fetch interaction rate after all inexpensive cuts were performed: + interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + // Optionally, do this only when required by the minIR/maxIR cuts, to further limit ccdb calls: + // const double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) return false; if (fillHists) @@ -1167,11 +1239,10 @@ struct lambdajetpolarizationions { fillEventSelectionQA(++selectionIdx, centrality); if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) return false; - if (fillHists) + if (fillHists) { fillEventSelectionQA(++selectionIdx, centrality); - // Filling histograms previously filled in fillReconstructedEventProperties here, to avoid re-accessing data: - if (fillHists) { + // Filling histograms previously filled in fillReconstructedEventProperties here, to avoid re-accessing data: histos.fill(HIST("hEventOccupancy"), collisionOccupancy); histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); histos.fill(HIST("hInteractionRate"), interactionRate); @@ -1187,87 +1258,94 @@ struct lambdajetpolarizationions { return true; } + /// \brief Track-level cutflow gate for FastJet pseudojet candidates (quality + kinematics + optional DCA cuts). + /// \note Mirrors isEventAccepted()'s early-exit pattern, but for tracks. template bool isCandidateForChargedPseudojetAccepted(JetCandidate const& track) { // (TODO: add an equivalent for photon jets and Z jets, which don't consider charged particles) // if (track.sign() == 0) return false; // Tracks are always either positive or negative, at least in TPC and ITS, which are the ones used (not looking at photon-jets right now) // ITS/TPC cuts: + if (pseudoJetCandidateTrackSelections.forceNoITS) { // reject tracks that have ITS bits (no need to reject hasTPC as we always demand minNCrossedRowsTPC > 70 in all analyses) + const auto detMap = track.detectorMap(); + if (detMap & o2::aod::track::ITS) + return false; + } + jetTrackSelCounter.fill(); // bin: forceNoITS + if (pseudoJetCandidateTrackSelections.minITSnCls >= 0) { if (track.itsNCls() < pseudoJetCandidateTrackSelections.minITSnCls) return false; } - JetTrackSelCounter.fill(); // bin: ITS clusters (min) + jetTrackSelCounter.fill(); // bin: ITS clusters (min) if (track.tpcNClsCrossedRows() < pseudoJetCandidateTrackSelections.minNCrossedRowsTPC) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); if (track.tpcChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2TPC) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); if (track.itsChi2NCl() > pseudoJetCandidateTrackSelections.maxChi2ITS) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); // Kinematics: const float pt = track.pt(); if (pt < pseudoJetCandidateTrackSelections.minCandidatePt) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); if (std::fabs(track.eta()) > pseudoJetCandidateTrackSelections.etaCut) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); // DCA pseudojet candidate selections -- These select primary vertex particles for the jet: if (pseudoJetCandidateTrackSelections.doDCAcuts) { // if (std::fabs(track.dcaXY()) > pseudoJetCandidateTrackSelections.maxDCAxy) return false; if (std::fabs(track.dcaZ()) > pseudoJetCandidateTrackSelections.maxDCAz) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); // Slightly more physics-motivated cut (parametrizes the DCA resolution as function of pt) if (std::fabs(track.dcaXY()) > (pseudoJetCandidateTrackSelections.dcaxyMaxTrackPar0 + pseudoJetCandidateTrackSelections.dcaxyMaxTrackPar1 / std::pow(pt, pseudoJetCandidateTrackSelections.dcaxyMaxTrackPar2))) return false; - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); } else { // Should fill counters an equal number of times to advance indices (future-proofing, but could do it by just advancing indices by hand in JetTrackSelectionFlowCounter) - JetTrackSelCounter.fill(); - JetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); + jetTrackSelCounter.fill(); } return true; } // Lambda selections: - template - bool passesGenericV0Cuts(TV0 const& v0) + /// \brief Charge-independent V0 cuts (topology, ITS/TPC track quality) shared by both the Lambda and AntiLambda hypotheses. + /// \param posTrackExtra,negTrackExtra the V0's daughter tracks, cached once at the process function. + /// \note See passesLambdaLambdaBarHypothesis() for the hypothesis-specific cuts. + template + bool passesGenericV0Cuts(TV0 const& v0, TTrack const& posTrackExtra, TTrack const& negTrackExtra) { // Base topological variables (high rejection, low cost checks) if (v0.v0radius() < v0Selections.v0radius) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0.v0radius() > v0Selections.v0radiusMax) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0.v0cosPA() < v0Selections.v0cospa) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0.dcaV0daughters() > v0Selections.dcav0dau) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); - // pseudorapidity cuts: + // rapidity cuts (this is actually a physics cut, moreso than being an acceptance cut): if (std::fabs(v0.yLambda()) > v0Selections.rapidityCut) return false; - // if (std::fabs(v0.eta()) > v0Selections.v0EtaCut) return false; - V0SelCounter.fill(); - // if (std::fabs(v0.eta()) > v0Selections.daughterEtaCut) return false; // (TODO: properly consider this in daughter selection!) + v0SelCounter.fill(); // competing mass rejection (if compMassRejection < 0, this cut does nothing) if (std::fabs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0Selections.compMassRejection) return false; - V0SelCounter.fill(); - - const auto posTrackExtra = v0.template posTrack_as(); // (TODO: is it worth it to cache these transformations outside of the function? They are reused in the Lambda hypothesis checks) - const auto negTrackExtra = v0.template negTrack_as(); + v0SelCounter.fill(); // ITS quality cuts bool posIsFromAfterburner = posTrackExtra.isITSAfterburner(); @@ -1276,123 +1354,122 @@ struct lambdajetpolarizationions { // check minimum number of ITS clusters + maximum ITS chi2 per clusters + reject or select ITS afterburner tracks if requested if (posTrackExtra.itsNCls() < v0Selections.minITSclusters) return false; // check minimum ITS clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectPosITSafterburner && posIsFromAfterburner) return false; // reject afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.requirePosITSafterburnerOnly && !posIsFromAfterburner) return false; // keep afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.itsNCls() < v0Selections.minITSclusters) return false; // check minimum ITS clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.itsChi2NCl() >= v0Selections.maxITSchi2PerNcls) return false; // check maximum ITS chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectNegITSafterburner && negIsFromAfterburner) return false; // reject afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.requireNegITSafterburnerOnly && !negIsFromAfterburner) return false; // keep afterburner track or not - V0SelCounter.fill(); + v0SelCounter.fill(); // TPC quality cuts if (posTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (posTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.positivept(), v0.positivephi(), +1)) return false; // reject track far from TPC sector boundary or not - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcNClsCrossedRows() < v0Selections.minTPCrows) return false; // check minimum TPC crossed rows - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcChi2NCl() >= v0Selections.maxTPCchi2PerNcls) return false; // check maximum TPC chi2 per clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcCrossedRowsOverFindableCls() < v0Selections.minTPCrowsOverFindableClusters) return false; // check minimum fraction of TPC rows over findable - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcFoundOverFindableCls() < v0Selections.minTPCfoundOverFindableClusters) return false; // check minimum fraction of found over findable TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (negTrackExtra.tpcFractionSharedCls() >= v0Selections.maxFractionTPCSharedClusters) return false; // check the maximum fraction of allowed shared TPC clusters - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.rejectTPCsectorBoundary && !isTrackFarFromTPCBoundary(v0.negativept(), v0.negativephi(), -1)) return false; // reject track far from TPC sector boundary or not - V0SelCounter.fill(); + v0SelCounter.fill(); // ITS only tag if (v0Selections.requirePosITSonly && posTrackExtra.tpcNClsCrossedRows() > 1) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.requireNegITSonly && negTrackExtra.tpcNClsCrossedRows() > 1) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // TPC only tag if (v0Selections.skipTPConly && posTrackExtra.detectorMap() == o2::aod::track::TPC) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (v0Selections.skipTPConly && negTrackExtra.detectorMap() == o2::aod::track::TPC) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); return true; } - // Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. - template - bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, bool Lambda_hypothesis) + /// \brief Tests the hypothesis of the V0 being a Lambda or of it being an antiLambda. + /// \param posTrackExtra,negTrackExtra the V0's daughter tracks, cached once at the process function. + /// \param lambdaHypothesis "true" tests the Lambda (proton+/pion-) hypothesis. "false" tests AntiLambda. + template + bool passesLambdaLambdaBarHypothesis(TV0 const& v0, TCollision const& collision, TTrack const& posTrackExtra, TTrack const& negTrackExtra, bool lambdaHypothesis) { // Remaining topological cuts that were charge-dependent: // (there is no real gain in doing a looser version of these in the passesGenericV0Cuts function. // The DCA check will be done anyways and is very unexpensive) // (even though they are high rejection, they demand a Lambda vs AntiLambda hypothesis, so they // only appear here...) - const float dcaProtonToPV = Lambda_hypothesis ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); + const float dcaProtonToPV = lambdaHypothesis ? std::abs(v0.dcapostopv()) : std::abs(v0.dcanegtopv()); if (dcaProtonToPV < v0Selections.dcaProtonToPV) return false; - V0SelCounter.fill(); - const float dcaPionToPV = Lambda_hypothesis ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); // Checks Lambda_hypothesis twice, but compiler can handle it cleanly. + v0SelCounter.fill(); + const float dcaPionToPV = lambdaHypothesis ? std::abs(v0.dcanegtopv()) : std::abs(v0.dcapostopv()); // Checks lambdaHypothesis twice, but compiler can handle it cleanly. if (dcaPionToPV < v0Selections.dcaPionToPV) return false; - V0SelCounter.fill(); - - const auto posTrackExtra = v0.template posTrack_as(); - const auto negTrackExtra = v0.template negTrack_as(); + v0SelCounter.fill(); // For the PID cuts to be properly applied while also keeping this function // general enough for Lambdas and AntiLambdas, we identify the roles of // proton-like and pion-like for the pos and neg tracks accordingly: - auto const& protonTrack = Lambda_hypothesis ? posTrackExtra : negTrackExtra; - auto const& pionTrack = Lambda_hypothesis ? negTrackExtra : posTrackExtra; + auto const& protonTrack = lambdaHypothesis ? posTrackExtra : negTrackExtra; + auto const& pionTrack = lambdaHypothesis ? negTrackExtra : posTrackExtra; ///// Expensive PID checks come last: // TPC PID if (std::fabs(protonTrack.tpcNSigmaPr()) > v0Selections.tpcPidNsigmaCut) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); if (std::fabs(pionTrack.tpcNSigmaPi()) > v0Selections.tpcPidNsigmaCut) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // Only do TOF checks when actually using TOF subscriptions: // if (doprocessDataWithTOF) { @@ -1403,23 +1480,23 @@ struct lambdajetpolarizationions { const bool pionHasTOF = pionTrack.hasTOF(); // Proton-like track - if (protonHasTOF && std::abs(Lambda_hypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) + if (protonHasTOF && std::abs(lambdaHypothesis ? v0.posTOFDeltaTLaPr() : v0.negTOFDeltaTLaPr()) > v0Selections.maxDeltaTimeProton) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // Pion-like track - if (pionHasTOF && std::abs(Lambda_hypothesis ? v0.negTOFDeltaTLaPi() : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) + if (pionHasTOF && std::abs(lambdaHypothesis ? v0.negTOFDeltaTLaPi() : v0.posTOFDeltaTLaPi()) > v0Selections.maxDeltaTimePion) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // TOF PID in NSigma (TODO: add asymmetric NSigma windows for purity tuning?) // Proton-like track (notice usage of tofNSigmaLaPr vs tofNSigmaALaPr) - if (protonHasTOF && std::fabs(Lambda_hypothesis ? v0.tofNSigmaLaPr() : v0.tofNSigmaALaPr()) > v0Selections.tofPidNsigmaCutLaPr) - return false; // (No need to select which candidate is which with the Lambda_hypothesis. Automatically done already!) - V0SelCounter.fill(); + if (protonHasTOF && std::fabs(lambdaHypothesis ? v0.tofNSigmaLaPr() : v0.tofNSigmaALaPr()) > v0Selections.tofPidNsigmaCutLaPr) + return false; // (No need to select which candidate is which with the lambdaHypothesis. Automatically done already!) + v0SelCounter.fill(); // Pion-like track - if (pionHasTOF && std::fabs(Lambda_hypothesis ? v0.tofNSigmaLaPi() : v0.tofNSigmaALaPi()) > v0Selections.tofPidNsigmaCutLaPi) + if (pionHasTOF && std::fabs(lambdaHypothesis ? v0.tofNSigmaLaPi() : v0.tofNSigmaALaPi()) > v0Selections.tofPidNsigmaCutLaPi) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); // (CAUTION!) You cannot use the getter for raw data's PIDResponseTOF.h instead of LFStrangenessPIDTables.h (as below) // If you do use, TOF will just try to identify that track as a proton from the PV, instead of using the correct path @@ -1428,19 +1505,19 @@ struct lambdajetpolarizationions { // if (protonHasTOF && std::fabs(protonTrack.tofNSigmaPr()) > v0Selections.tofPidNsigmaCutLaPr) return false; // To properly use the LFStrangenessPIDTables version, you need to call o2-analysis-lf-strangenesstofpid too. } else { // Should fill counters an equal number of times to advance indices - V0SelCounter.fillUpTo(V0SelCounter.binValue + 4); // Fills the 4 times "V0SelCounter.fill()" would be called + v0SelCounter.fillUpTo(v0SelCounter.binValue + 4); // Fills the 4 times "v0SelCounter.fill()" would be called } // proper lifetime if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 > v0Selections.lambdaLifetimeCut) return false; - V0SelCounter.fill(); + v0SelCounter.fill(); return true; } // Function to help distinguish ambiguous candidates (via Armenteros) that pass both - // the Lambda_hypothesis true (i.e., a Lambda) or false (i.e., an AntiLambda) checks + // the lambdaHypothesis true (i.e., a Lambda) or false (i.e., an AntiLambda) checks // (This function is only called in about 1-3% of the Lambda-Like V0s which remain ambiguous after all other cuts) // int isCandidateArmenterosLambda(const float alpha, const float qt){ // // Remove K0s band @@ -1463,16 +1540,17 @@ struct lambdajetpolarizationions { // else return -1; // AntiLambda // } + /// \brief Clusters charged tracks into jets (FastJet, with optional background subtraction), fills the RingJets/RingLeadPs tables for this collision, and fills the jet-kinematics QA. template - void jetsProcess(TJetTracks const& tracks, const int ringCollIdx, const float centrality) + void jetsProcess(TJetTracks const& tracks, const int ringCollIdx, const float centrality, const float collisionPVz) { // Loop over reconstructed tracks: std::vector fjParticles; int leadingParticleIdx = -1; // Initialized as -1, but could leave it unitialized as well. We reject any invalid events where this could pose a problem (e.g., pT<=0) float leadingParticlePt = 0; for (auto const& track : tracks) { - JetTrackSelCounter.resetForNewTrack(); // reset bin counter for this candidate - JetTrackSelCounter.fill(); // bin: "All track candidates" + jetTrackSelCounter.resetForNewTrack(); // reset bin counter for this candidate + jetTrackSelCounter.fill(); // bin: "All track candidates" // Require that tracks pass selection criteria if (!isCandidateForChargedPseudojetAccepted(track)) @@ -1491,22 +1569,18 @@ struct lambdajetpolarizationions { leadingParticleIdx = fjParticles.size() - 1; } } - // Reject empty events if (fjParticles.size() < 1) return; - int lastBinEvSel = histos.get(HIST("hEventSelection"))->GetXaxis()->GetNbins(); - auto const& leadingParticle = fjParticles[leadingParticleIdx]; if (leadingParticle.pt() > jetConfigurations.minLeadParticlePt) { // If not, leading particle is probably a bad proxy tableLeadParticles(ringCollIdx, leadingParticle.pt(), leadingParticle.eta(), leadingParticle.phi()); } - // Start jet clusterization: - // Cluster particles using the anti-kt algorithm + // Cluster particles with the configured algorithm: fastjet::JetDefinition jetDef(mapFJAlgorithm(jetConfigurations.jetAlgorithm), jetConfigurations.radiusJet, mapFJRecombScheme(jetConfigurations.jetRecombScheme)); if (jetConfigurations.bkgSubtraction == kAreaBased) { - fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(jetConfigurations.GhostedAreaSpecRapidity)); + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(jetConfigurations.ghostedAreaSpecRapidity)); fastjet::ClusterSequenceArea clustSeq(fjParticles, jetDef, areaDef); // Attributes an area for each pseudojet in the list std::vector jets = fastjet::sorted_by_pt(clustSeq.inclusive_jets()); // No minimum pt before background subtraction if (jets.empty()) @@ -1514,16 +1588,23 @@ struct lambdajetpolarizationions { // Perpendicular cone area subtraction, not the traditional subtraction (TODO: include an option for traditional area subtraction) auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], jetConfigurations.radiusJet); // This uses a geometric, pi*R^2 area, not exactly a ghost-based area! - // Loop over clustered jets: int selectedJets = 0; fastjet::PseudoJet leadingJetSub; float leadingJetPt = -1.f; + // Storing the accepted subtracted jets in memory: + // (the QA loop below uses them, and this is inexpensive enough to store) + std::vector subtractedJets; + if (doJetKinematicsQA) + subtractedJets.reserve(jets.size()); for (const auto& jet : jets) { // Jet must be fully contained in the acceptance (0.9 for ITS+TPC barrel) - const float jet_eta = jet.eta(); - if (std::fabs(jet_eta) > (0.9f - jetConfigurations.radiusJet)) + const float jetEta = jet.eta(); + if (std::fabs(jetEta) > (0.9f - jetConfigurations.radiusJet)) { + if (doJetKinematicsQA) + histos.fill(HIST("JetKinematicsQA/hJetEtaOutOfAcceptance"), jetEta); continue; + } auto jetForSub = jet; // Subtracts same background estimated for highest pt jet, but every jet might have a slightly different area @@ -1534,13 +1615,14 @@ struct lambdajetpolarizationions { if (jetMinusBkg.pt() < jetConfigurations.minJetPt) continue; selectedJets++; + if (doJetKinematicsQA) + subtractedJets.emplace_back(jetMinusBkg); - // Store jet: tableJets(ringCollIdx, jetMinusBkg.pt(), jetMinusBkg.eta(), // Using eta instead of rapidity - jetMinusBkg.phi(), - jetMinusBkg.constituents().size()); + jetMinusBkg.phi()); + // jetMinusBkg.constituents().size()); // Finding the leading jet after subtraction (leading jet is NOT known a priori!): if (jetMinusBkg.pt() > leadingJetPt) { @@ -1554,22 +1636,17 @@ struct lambdajetpolarizationions { histos.fill(HIST("hEventsWithJet"), 0.5); // Another version of this counter, which is already integrated in the Event Selection flow: if (doEventQA) - fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingJet passes + fillEventSelectionQA(mBinHasRingJet, centrality); // hasRingJet passes if (doJetKinematicsQA) { + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), leadingJetSub.constituents().size(), centrality); + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), leadingJetSub.constituents().size(), leadingJetSub.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJetSub.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJetSub.eta()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJetSub.phi()); - // Now looping through jets again to calculate the correlations: - for (const auto& jet : jets) { - // Will recalculated background subtraction during QA to avoid storing jets in memory when running in non-QA cases: - auto jetForSub = jet; - fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); - - if (jetMinusBkg.pt() < jetConfigurations.minJetPt) - continue; - + // Now looping through the accepted subtracted jets to calculate the correlations: + for (const auto& jetMinusBkg : subtractedJets) { float cosTheta = cosThetaJets(leadingJetSub, jetMinusBkg); float deltaPhi = RecoDecay::constrainAngle(leadingJetSub.phi() - jetMinusBkg.phi(), -o2::constants::math::PI); float deltaEta = leadingJetSub.eta() - jetMinusBkg.eta(); @@ -1595,34 +1672,42 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), selectedJets, deltaPhi); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), selectedJets, deltaEta); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), selectedJets, cosTheta); + + // Collision PVz: + histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsPVz"), jetMinusBkg.eta(), collisionPVz); + histos.fill(HIST("JetKinematicsQA/h2dJetPhiVsPVz"), jetMinusBkg.phi(), collisionPVz); } // Leading particle comparisons: - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); float deltaPhiParticleToJet = RecoDecay::constrainAngle(leadingJetSub.phi() - leadingParticle.phi(), -o2::constants::math::PI); float deltaEtaParticleToJet = leadingJetSub.eta() - leadingParticle.eta(); float cosThetaParticleToJet = cosThetaJets(leadingJetSub, leadingParticle); // Takes advantage of the fact that this leading particle is a PseudoJet object - histos.fill(HIST("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), selectedJets, deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), selectedJets, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), selectedJets, cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), selectedJets, deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), selectedJets, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), selectedJets, cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), selectedJets, leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJetSub.pt(), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), selectedJets, leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJetSub.pt(), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJetSub.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJetSub.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJetSub.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJetSub.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); + + // Collision PVz -- Leading Particle: + histos.fill(HIST("LeadingParticleQA/h2dLeadPEtaVsPVz"), leadingParticle.eta(), collisionPVz); + histos.fill(HIST("LeadingParticleQA/h2dLeadPPhiVsPVz"), leadingParticle.phi(), collisionPVz); } } else { // Otherwise, simple jet clustering (TODO: this is the fall back for kConstituentBased while not implemented) fastjet::ClusterSequence clustSeq(fjParticles, jetDef); @@ -1637,32 +1722,43 @@ struct lambdajetpolarizationions { histos.fill(HIST("hEventsWithJet"), 0.5); // Another version of this counter, which is already integrated in the Event Selection flow: if (doEventQA) - fillEventSelectionQA(lastBinEvSel - 1, centrality); // hasRingJet passes + fillEventSelectionQA(mBinHasRingJet, centrality); // hasRingJet passes const auto& leadingJet = jets[0]; for (const auto& jet : jets) { // Jet must be fully contained in the acceptance (0.9 for ITS+TPC barrel) - const float jet_eta = jet.eta(); - if (std::fabs(jet_eta) > (0.9f - jetConfigurations.radiusJet)) + const float jetEta = jet.eta(); + if (std::fabs(jetEta) > (0.9f - jetConfigurations.radiusJet)) { + if (doJetKinematicsQA) { + // QA before the acceptance cut: + const float deltaPhiOut = RecoDecay::constrainAngle(leadingJet.phi() - jet.phi(), -o2::constants::math::PI); + const float deltaEtaOut = leadingJet.eta() - jetEta; + histos.fill(HIST("JetKinematicsQA/hJetEtaOutOfAcceptance"), jetEta); + histos.fill(HIST("JetKinematicsQA/hDeltaEtaToLeadingJetOutOfAcceptance"), deltaEtaOut); + histos.fill(HIST("JetKinematicsQA/hDeltaRToLeadingJetOutOfAcceptance"), std::sqrt(deltaPhiOut * deltaPhiOut + deltaEtaOut * deltaEtaOut)); + } continue; + } tableJets(ringCollIdx, jet.pt(), - jet_eta, // Using eta instead of rapidity - jet.phi(), - jet.constituents().size()); + jetEta, // Using eta instead of rapidity + jet.phi()); + // jet.constituents().size()); // Currently removed from datamodel. + // Other variables can better reveal jet quenching and help identify good selection criteria for quenched jets proxies if (doJetKinematicsQA) { histos.fill(HIST("JetKinematicsQA/hJetPt"), jet.pt()); - histos.fill(HIST("JetKinematicsQA/hJetEta"), jet_eta); + histos.fill(HIST("JetKinematicsQA/hJetEta"), jetEta); histos.fill(HIST("JetKinematicsQA/hJetPhi"), jet.phi()); + histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsJetPhi"), jetEta, jet.phi()); // Calculate angle to leading jet: float cosTheta = cosThetaJets(leadingJet, jet); // Calculate angular separation in projected angles: float deltaPhi = RecoDecay::constrainAngle(leadingJet.phi() - jet.phi(), -o2::constants::math::PI); - float deltaEta = leadingJet.eta() - jet_eta; + float deltaEta = leadingJet.eta() - jetEta; float deltaR = std::sqrt(deltaPhi * deltaPhi + deltaEta * deltaEta); // 2D angular distance in the eta-phi plane histos.fill(HIST("JetKinematicsQA/hCosThetaToLeadingJet"), cosTheta); // Measuring the cosine, not angle, because it is faster! @@ -1685,60 +1781,76 @@ struct lambdajetpolarizationions { histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaPhiToLead"), jetsInEvent, deltaPhi); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsDeltaEtaToLead"), jetsInEvent, deltaEta); histos.fill(HIST("JetKinematicsQA/h2dJetsPerEventvsCosThetaToLead"), jetsInEvent, cosTheta); + + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), jet.constituents().size(), centrality); + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), jet.constituents().size(), jet.pt()); + + // Collision PVz: + histos.fill(HIST("JetKinematicsQA/h2dJetEtaVsPVz"), jet.eta(), collisionPVz); + histos.fill(HIST("JetKinematicsQA/h2dJetPhiVsPVz"), jet.phi(), collisionPVz); } } - if (doJetKinematicsQA) { + if (doJetKinematicsQA) { // Fills even when the leading jet is outside of the (0.9f - jetConfigurations.radiusJet) eta window. Fills at least for the leading jet. + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsCentrality"), leadingJet.constituents().size(), centrality); + histos.fill(HIST("JetKinematicsQA/h2dConstituentsPerJetVsJetPt"), leadingJet.constituents().size(), leadingJet.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPt"), leadingJet.pt()); histos.fill(HIST("JetKinematicsQA/hLeadingJetEta"), leadingJet.eta()); histos.fill(HIST("JetKinematicsQA/hLeadingJetPhi"), leadingJet.phi()); // Leading particle comparisons: - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); - histos.fill(HIST("JetVsLeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePt"), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticleEta"), leadingParticle.eta()); + histos.fill(HIST("LeadingParticleQA/hLeadingParticlePhi"), leadingParticle.phi()); double deltaPhiParticleToJet = RecoDecay::constrainAngle(leadingJet.phi() - leadingParticle.phi(), -o2::constants::math::PI); double deltaEtaParticleToJet = leadingJet.eta() - leadingParticle.eta(); double cosThetaParticleToJet = cosThetaJets(leadingJet, leadingParticle); // Takes advantage of the fact that this leading particle is a PseudoJet object - histos.fill(HIST("JetVsLeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hCosThetaLeadParticleToJet"), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaPhiLeadParticleToJet"), deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/hDeltaEtaToLeadParticleToJet"), deltaEtaParticleToJet); + + histos.fill(HIST("LeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dDeltaPhiParticleToLeadvsDeltaEtaParticleToLead"), deltaPhiParticleToJet, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), jetsInEvent, deltaPhiParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), jetsInEvent, deltaEtaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), jetsInEvent, cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaPhiParticleToLead"), jetsInEvent, deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsDeltaEtaParticleToLead"), jetsInEvent, deltaEtaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsCosThetaParticleToLead"), jetsInEvent, cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), jetsInEvent, leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJet.pt(), leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dJetsPerEventvsLeadParticlePt"), jetsInEvent, leadingParticle.pt()); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsLeadParticlePt"), leadingJet.pt(), leadingParticle.pt()); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJet.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsCosThetaParticleToLead"), leadingJet.pt(), cosThetaParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsCosThetaParticleToLead"), leadingParticle.pt(), cosThetaParticleToJet); + histos.fill(HIST("LeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJet.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) + histos.fill(HIST("LeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadJetPtvsDeltaPhiParticleToLead"), leadingJet.pt(), deltaPhiParticleToJet); // To see if there is any backgound in phi due to soft jets (or soft particles below) - histos.fill(HIST("JetVsLeadingParticleQA/h2dLeadParticlePtvsDeltaPhiParticleToLead"), leadingParticle.pt(), deltaPhiParticleToJet); + // Collision PVz -- Leading Particle: + histos.fill(HIST("LeadingParticleQA/h2dLeadPEtaVsPVz"), leadingParticle.eta(), collisionPVz); + histos.fill(HIST("LeadingParticleQA/h2dLeadPPhiVsPVz"), leadingParticle.phi(), collisionPVz); } } } - // No longer use a separate JetTracks joined table -- it was mostly a subset of DauTracks + TracksIU (which was not used) + /// \brief Main process function: applies event selection, fills the RingCollisions table, calls jetsProcess(), then tests every V0 against the Lambda/AntiLambda hypotheses and fills RingLaV0s. + /// \note No longer use a separate JetTracks joined table -- it was mostly a subset of DauTracks + TracksIU (which was not used) template void dataProcess(TCollision const& collision, TV0Candidates const& V0s, TDaughterTracks const& V0DauTracks, aod::BCsWithTimestamps const& bcs) { float centrality = getCentrality(collision); // Strictly for QA. We save other types of centrality estimators in the derived data! - // For event QA the last two indices never change for NEv_withJets and NEv_withV0s - // (Not the best way to initialize this: runs once per collision! TODO: think of a better way to do it) - int lastBinEvSel = histos.get(HIST("hEventSelection"))->GetXaxis()->GetNbins(); bool validV0AlreadyFound = false; histos.fill(HIST("hEventSelection"), 0. /* all collisions */); - histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); + if (doEventQA) + histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); auto bc = bcs.iteratorAt(collision.bcId()); - if (!isEventAccepted(collision, bc, centrality, doEventQA)) + + // Initialize interactionRate with a sentinel value. It will be stored in the derived data afterwards: + // (We pass this by reference and update it inside isEventAccepted after all less expensive checks were already done) + double interactionRate = -1.0; + if (!isEventAccepted(collision, bc, centrality, interactionRate, doEventQA)) return; // Uses return instead of continue, as there is no explicit loop here if (doEventQA) @@ -1746,26 +1858,39 @@ struct lambdajetpolarizationions { if (v0Selections.rejectTPCsectorBoundary) initCCDB(bc); // Substituted call from collision to bc for raw data - // Fill event table: + // If the event passed but skipped the Pb-Pb block (e.g. it was a pp event), fetch the rate before saving in the datamodel: + if (interactionRate < 0) + interactionRate = rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3; + + const float collisionPVz = collision.posZ(); + tableCollisions(collision.centFT0M(), collision.centFT0C(), - collision.centFV0A()); // (TODO: add InteractionRate info and other useful cuts for later on in the analysis?) + collision.centFV0A(), + collisionPVz, + interactionRate); // Get the derived collision row index for this event: const int ringCollIdx = tableCollisions.lastIndex(); - // Call to jets process: - jetsProcess(V0DauTracks, ringCollIdx, centrality); // V0DauTracks takes the place of jetTracks now + jetsProcess(V0DauTracks, ringCollIdx, centrality, collisionPVz); // V0DauTracks takes the place of jetTracks now - uint NLambdas = 0; // Counting particles per event - uint NAntiLambdas = 0; - uint NAmbiguous = 0; + // Counting particles per event: + uint16_t nLambdas = 0; + uint16_t nAntiLambdas = 0; + uint16_t nNonAmbiguous = 0; + uint16_t nAmbiguous = 0; for (auto const& v0 : V0s) { - V0SelCounter.resetForNewV0(); - V0SelCounter.fill(); // Fill for all v0 candidates + v0SelCounter.resetForNewV0(v0.mLambda(), v0.mAntiLambda()); + v0SelCounter.fill(); // Fill for all v0 candidates if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); // fill AP plot for all V0s - if (!passesGenericV0Cuts(v0)) + + // Daughter tracks cached once per candidate (needed by both hypothesis tests, by the QA and by the table fill) + const auto posTrackExtra = v0.template posTrack_as(); + const auto negTrackExtra = v0.template negTrack_as(); + + if (!passesGenericV0Cuts(v0, posTrackExtra, negTrackExtra)) continue; if (doArmenterosQA) @@ -1775,20 +1900,20 @@ struct lambdajetpolarizationions { bool isLambda = false; bool isAntiLambda = false; if (analyseLambda) - isLambda = passesLambdaLambdaBarHypothesis(v0, collision, true); + isLambda = passesLambdaLambdaBarHypothesis(v0, collision, posTrackExtra, negTrackExtra, true); if (analyseAntiLambda) { if (analyseLambda) // We only need to advance when the Lambda hypothesis had an early exit on the counters - V0SelCounter.advanceTo(lambdaHypoEnd + 1); // sync to bin 41 (x=40 means bin 41, the first #bar{#Lambda} bin) - isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, false); + v0SelCounter.advanceTo(lambdaHypoEnd + 1); // sync to bin 43 (x=42 means bin 43, the first #bar{#Lambda} bin) + isAntiLambda = passesLambdaLambdaBarHypothesis(v0, collision, posTrackExtra, negTrackExtra, false); } if (!isLambda && !isAntiLambda) - continue; // Candidate is not considered to be a Lambda-like + continue; // Candidate is not considered to be Lambda-like if (isLambda) - NLambdas++; + nLambdas++; if (isAntiLambda) - NAntiLambdas++; + nAntiLambdas++; if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelected"), v0.alpha(), v0.qtarm()); // cross-check @@ -1798,13 +1923,15 @@ struct lambdajetpolarizationions { histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAntiLambda"), v0.alpha(), v0.qtarm()); // XOR check: - if (isLambda ^ isAntiLambda) + if (isLambda ^ isAntiLambda) { histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedNonAmbiguous"), v0.alpha(), v0.qtarm()); + nNonAmbiguous++; + } // int lambdaIdx = -1; // No need to pass armenteros if (isLambda && isAntiLambda) { - NAmbiguous++; - histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); + nAmbiguous++; + // histos.fill(HIST("hAmbiguousLambdaCandidates"), 0); if (doArmenterosQA) histos.fill(HIST("GeneralQA/h2dArmenterosFullSelectedAmbiguous"), v0.alpha(), v0.qtarm()); // To know the discerning power of Armenteros in an Ambiguous Lambda vs AntiLambda case @@ -1816,27 +1943,47 @@ struct lambdajetpolarizationions { } // if (lambdaIdx == kIsArmenterosK0) continue; // Should just skip this step then! - if (doEventQA) - fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes - - // // Extra competing mass rejection of Lambdas // (TODO: test competing mass cuts) + // // Extra competing mass rejection of Lambdas // (TODO: test competing mass cuts and implement it here) // v0.mLambda() + // Bookkeeping for the candidates that passed eveything until the ambiguity check: + if (isLambda) { // Go back to Lambda bin to fill it as well: + v0SelCounter.advanceTo(lambdaHypoEnd - 1); // Bin 41: Ambiguous Lambda rejection + v0SelCounter.fill(); + } + if (isAntiLambda) { + v0SelCounter.advanceTo(antiLambdaHypoEnd - 1); // Bin 52: Ambiguous antiLambda rejection + v0SelCounter.fill(); + } + + // Removing ambiguous Lambda candidates for final storage, AFTER some QAing: + if (isLambda && isAntiLambda) + continue; + + // Updating selection counters after ambiguous candidate rejection: + if (isLambda) { + v0SelCounter.advanceTo(lambdaHypoEnd); // Bin 42: Final accepted Lambda V0s + v0SelCounter.fill(); + } else { // i.e., isAntiLambda + v0SelCounter.advanceTo(antiLambdaHypoEnd); // Bin 53: Final accepted anti-Lambda V0s + v0SelCounter.fill(); + } + // Saving the Lambdas into a derived data column: auto const v0pt = v0.pt(); - const auto posTrackExtra = v0.template posTrack_as(); - const auto negTrackExtra = v0.template negTrack_as(); tableV0s(ringCollIdx, v0pt, v0.eta(), v0.phi(), // Using eta instead of rapidity - isLambda, isAntiLambda, - v0.mLambda(), v0.mAntiLambda(), + isLambda, // 0: antiLambda, 1: Lambda. No ambiguous candidates (isLambda && isAntiLambda) are stored + isLambda ? v0.mLambda() : v0.mAntiLambda(), v0.positivept(), v0.positiveeta(), v0.positivephi(), v0.negativept(), v0.negativeeta(), v0.negativephi(), - posTrackExtra.tpcNSigmaPr(), posTrackExtra.tpcNSigmaPi(), - negTrackExtra.tpcNSigmaPr(), negTrackExtra.tpcNSigmaPi(), + packNSigma(isLambda ? posTrackExtra.tpcNSigmaPr() : negTrackExtra.tpcNSigmaPr()), // Proton-like track, packed into int16_t + packNSigma(isLambda ? negTrackExtra.tpcNSigmaPi() : posTrackExtra.tpcNSigmaPi()), // Pion-like track v0.v0cosPA(), v0.v0radius(), v0.dcaV0daughters(), v0.dcapostopv(), v0.dcanegtopv()); + + // Perform extensive QAs for non-ambiguous Lambda/antiLambda candidates only: if (doEventQA && !validV0AlreadyFound) - fillEventSelectionQA(lastBinEvSel, centrality); // hasRingV0 passes + fillEventSelectionQA(mBinHasRingV0, centrality); // hasRingV0 passes validV0AlreadyFound = true; if (doV0KinematicQA) { @@ -1873,8 +2020,17 @@ struct lambdajetpolarizationions { } } - if (doCompleteTopoQA) { - // Remaking these variables outside of the passesLambdaLambdaBarHypothesis. Loses performance, but that should be OK for QA + // Invariant mass spectra: + if (analyseLambda && isLambda) { + histos.fill(HIST("hMassLambda"), v0.mLambda()); + histos.fill(HIST("Lambda/h3dMassLambda"), centrality, v0pt, v0.mLambda()); + } + if (analyseAntiLambda && isAntiLambda) { + histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); + histos.fill(HIST("AntiLambda/h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); + } + + if (doTopoQA) { histos.fill(HIST("V0KinematicQA/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("V0KinematicQA/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("V0KinematicQA/hDCADaughters"), v0.dcaV0daughters()); @@ -1884,9 +2040,7 @@ struct lambdajetpolarizationions { histos.fill(HIST("V0KinematicQA/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcNClsCrossedRows(), negTrackExtra.itsNCls()); histos.fill(HIST("V0KinematicQA/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); histos.fill(HIST("V0KinematicQA/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); - if (isLambda && analyseLambda) { - histos.fill(HIST("hMassLambda"), v0.mLambda()); - histos.fill(HIST("Lambda/h3dMassLambda"), centrality, v0pt, v0.mLambda()); + if (analyseLambda && isLambda) { histos.fill(HIST("Lambda/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("Lambda/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("Lambda/hDCADaughters"), v0.dcaV0daughters()); @@ -1926,15 +2080,8 @@ struct lambdajetpolarizationions { histos.fill(HIST("Lambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPi()); } } - if (doEtaPhiQA) { - histos.fill(HIST("Lambda/h5dV0PhiVsEta"), centrality, v0pt, v0.mLambda(), v0.phi(), v0.eta()); - histos.fill(HIST("Lambda/h5dPosPhiVsEta"), centrality, v0.positivept(), v0.mLambda(), v0.positivephi(), v0.positiveeta()); - histos.fill(HIST("Lambda/h5dNegPhiVsEta"), centrality, v0.negativept(), v0.mLambda(), v0.negativephi(), v0.negativeeta()); - } } - if (isAntiLambda && analyseAntiLambda) { - histos.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); - histos.fill(HIST("AntiLambda/h3dMassAntiLambda"), centrality, v0pt, v0.mAntiLambda()); + if (analyseAntiLambda && isAntiLambda) { histos.fill(HIST("AntiLambda/hPosDCAToPV"), v0.dcapostopv()); histos.fill(HIST("AntiLambda/hNegDCAToPV"), v0.dcanegtopv()); histos.fill(HIST("AntiLambda/hDCADaughters"), v0.dcaV0daughters()); @@ -1974,21 +2121,24 @@ struct lambdajetpolarizationions { histos.fill(HIST("AntiLambda/h3dNegTOFdeltaTvsTrackPt"), centrality, v0.negativept(), v0.negTOFDeltaTLaPr()); } } - if (doEtaPhiQA) { - histos.fill(HIST("AntiLambda/h5dV0PhiVsEta"), centrality, v0pt, v0.mAntiLambda(), v0.phi(), v0.eta()); - histos.fill(HIST("AntiLambda/h5dPosPhiVsEta"), centrality, v0.positivept(), v0.mAntiLambda(), v0.positivephi(), v0.positiveeta()); - histos.fill(HIST("AntiLambda/h5dNegPhiVsEta"), centrality, v0.negativept(), v0.mAntiLambda(), v0.negativephi(), v0.negativeeta()); - } } } // end CompleteTopoQA } // end V0s loop // Fill histograms on a per-event level: - histos.fill(HIST("Lambda/hLambdasPerEvent"), NLambdas); - histos.fill(HIST("AntiLambda/hAntiLambdasPerEvent"), NAntiLambdas); - histos.fill(HIST("hAmbiguousPerEvent"), NAmbiguous); - histos.fill(HIST("Lambda/h2dNbrOfLambdaVsCentrality"), centrality, NLambdas); - histos.fill(HIST("AntiLambda/h2dNbrOfAntiLambdaVsCentrality"), centrality, NAntiLambdas); + if (analyseLambda) { + histos.fill(HIST("Lambda/hLambdasPerEvent"), nLambdas); + histos.fill(HIST("Lambda/h2dNbrOfLambdaVsCentrality"), centrality, nLambdas); + } + if (analyseAntiLambda) { + histos.fill(HIST("AntiLambda/hAntiLambdasPerEvent"), nAntiLambdas); + histos.fill(HIST("AntiLambda/h2dNbrOfAntiLambdaVsCentrality"), centrality, nAntiLambdas); + } + if (analyseLambda && analyseAntiLambda) { + histos.fill(HIST("hAmbiguousPerEvent"), nAmbiguous); + histos.fill(HIST("hNonAmbiguousPerEvent"), nNonAmbiguous); + histos.fill(HIST("hLambdasAndAntiLambdasPerEvent"), nLambdas + nAntiLambdas); + } } void processData(SelCollisions::iterator const& collision, aod::V0Datas const& V0s, DauTracks const& V0DauTracks, aod::BCsWithTimestamps const& bcs) diff --git a/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx b/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx index 6c20a736eda..e1329901149 100644 --- a/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdakzerofinder.cxx @@ -61,6 +61,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx b/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx index b27cb24c68c..3fd13a7aa95 100644 --- a/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdakzeromlselection.cxx @@ -210,19 +210,19 @@ struct lambdakzeromlselection { // calculate classifier output if (PredictLambda) { - float* LambdaProbability = lambda_bdt.evalModel(inputFeatures); + const std::vector LambdaProbability = lambda_bdt.evalModel(inputFeatures); lambdaMLSelections(LambdaProbability[1]); } if (PredictGamma) { - float* GammaProbability = gamma_bdt.evalModel(inputFeatures); + const std::vector GammaProbability = gamma_bdt.evalModel(inputFeatures); gammaMLSelections(GammaProbability[1]); } if (PredictAntiLambda) { - float* AntiLambdaProbability = antilambda_bdt.evalModel(inputFeatures); + const std::vector AntiLambdaProbability = antilambda_bdt.evalModel(inputFeatures); antiLambdaMLSelections(AntiLambdaProbability[1]); } if (PredictKZeroShort) { - float* KZeroShortProbability = kzeroshort_bdt.evalModel(inputFeatures); + const std::vector KZeroShortProbability = kzeroshort_bdt.evalModel(inputFeatures); kzeroShortMLSelections(KZeroShortProbability[1]); } } diff --git a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx index e6946db8f6c..615c4be269d 100644 --- a/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx +++ b/PWGLF/TableProducer/Strangeness/lambdaspincorrelation.cxx @@ -74,7 +74,8 @@ struct lambdaspincorrelation { ConfigurableAxis axisMultiplicityClass{"axisMultiplicityClass", {8, 0, 80}, "multiplicity percentile for bin"}; // events - Configurable cfgEventTypepp{"cfgEventTypepp", true, "Type of collisions"}; + Configurable cfgEventTypepp{"cfgEventTypepp", false, "Type of collisions"}; + Configurable cfgCentTypepp{"cfgCentTypepp", true, "Type of centrality"}; Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgCutCentralityMax{"cfgCutCentralityMax", 80.0f, "Accepted maximum Centrality"}; Configurable cfgCutCentralityMin{"cfgCutCentralityMin", 0.0f, "Accepted minimum Centrality"}; @@ -113,7 +114,7 @@ struct lambdaspincorrelation { { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); AxisSpec thnAxisInvMass{iMNbins, lbinIM, hbinIM, "#it{M} (GeV/#it{c}^{2})"}; - histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{5, 0, 5.0}}); + histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0, 10.0}}); histos.add("hLambdaMass", "hLambdaMass", kTH1F, {thnAxisInvMass}); histos.add("hV0Info", "hV0Info", kTH1F, {{5, 0, 5.0}}); } @@ -151,9 +152,9 @@ struct lambdaspincorrelation { if (std::abs(ctauLambda) > cMaxV0LifeTime) { return false; } - if (std::abs(candidate.yLambda()) > confV0Rap) { - return false; - } + // if (std::abs(candidate.yLambda()) > confV0Rap) { + // return false; + // } return true; } @@ -233,16 +234,58 @@ struct lambdaspincorrelation { return {lambdaTag, aLambdaTag, true}; // Valid candidate } + std::tuple getLambdaTagsMC(const auto& v0, const auto& collision) + { + auto postrack = v0.template posTrack_as(); + auto negtrack = v0.template negTrack_as(); + + int lambdaTag = 0; + int aLambdaTag = 0; + + const auto signpos = postrack.sign(); + const auto signneg = negtrack.sign(); + + if (signpos < 0 || signneg > 0) { + return {0, 0, false}; + } + + if (isSelectedV0Daughter(v0, postrack, 0) && + isSelectedV0Daughter(v0, negtrack, 1) && + v0.mLambda() > lbinIM && + v0.mLambda() < hbinIM) { + lambdaTag = 1; + } + + if (isSelectedV0Daughter(v0, negtrack, 0) && + isSelectedV0Daughter(v0, postrack, 1) && + v0.mAntiLambda() > lbinIM && + v0.mAntiLambda() < hbinIM) { + aLambdaTag = 1; + } + + if (!lambdaTag && !aLambdaTag) { + return {0, 0, false}; + } + + if (!selectionV0(collision, v0)) { + return {0, 0, false}; + } + + return {lambdaTag, aLambdaTag, true}; + } + ROOT::Math::PxPyPzMVector lambda, antiLambda, proton, pion, antiProton, antiPion; ROOT::Math::PxPyPzMVector lambdaDummy, pionDummy, protonDummy; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter centralityFilter = (nabs(aod::cent::centFT0C) < cfgCutCentralityMax && nabs(aod::cent::centFT0C) > cfgCutCentralityMin); + // Filter centralityFilter = (nabs(aod::cent::centFT0C) < cfgCutCentralityMax && nabs(aod::cent::centFT0C) > cfgCutCentralityMin); using EventCandidates = soa::Filtered>; using AllTrackCandidates = soa::Join; using ResoV0s = aod::V0Datas; - + Preslice perCollisionV0s = aod::v0data::collisionId; + using EventCandidatesMC = soa::Join; + using AllTrackCandidatesMC = soa::Join; void processData(EventCandidates::iterator const& collision, AllTrackCandidates const&, ResoV0s const& V0s) { std::vector lambdaMother, protonDaughter, pionDaughter; @@ -255,10 +298,18 @@ struct lambdaspincorrelation { std::vector positiveIndex = {}; std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; + std::vector dcaV0ToPV = {}; + std::vector tpcPosNClsCrossedRows = {}; + std::vector tpcNegNClsCrossedRows = {}; + std::vector ctau = {}; + std::vector tpcNsigmaPos = {}; + std::vector tpcNsigmaNeg = {}; + std::vector mK0Short = {}; + int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); - if (cfgEventTypepp) + if (cfgCentTypepp) centrality = collision.centFT0M(); auto vz = collision.posZ(); int occupancy = collision.trackOccupancyInTimeRange(); @@ -290,7 +341,7 @@ struct lambdaspincorrelation { } if (lambdaTag && aLambdaTag) { doubleStatus.push_back(true); - if (std::abs(v0.mLambda() - 1.1154) < std::abs(v0.mAntiLambda() - 1.1154)) { + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda) < std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda)) { lambdaTag = true; aLambdaTag = false; } else { @@ -306,12 +357,26 @@ struct lambdaspincorrelation { if (aLambdaTag) { histos.fill(HIST("hV0Info"), 3.5); } + + float ctauLambda = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * (o2::constants::physics::MassLambda); + // LOGF(info, "v0 index2: (%d)", v0.index()); auto postrack1 = v0.template posTrack_as(); auto negtrack1 = v0.template negTrack_as(); positiveIndex.push_back(postrack1.globalIndex()); negativeIndex.push_back(negtrack1.globalIndex()); v0Cospa.push_back(v0.v0cosPA()); + dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); + + tpcPosNClsCrossedRows.push_back(postrack1.tpcNClsCrossedRows()); + tpcNegNClsCrossedRows.push_back(negtrack1.tpcNClsCrossedRows()); + ctau.push_back(ctauLambda); + + tpcNsigmaPos.push_back(std::abs(postrack1.tpcNSigmaPr())); + tpcNsigmaNeg.push_back(std::abs(negtrack1.tpcNSigmaPi())); + + mK0Short.push_back(v0.mK0Short()); + v0Radius.push_back(v0.v0radius()); dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); @@ -348,7 +413,7 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5)); + lambdaPair(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5), tpcPosNClsCrossedRows.at(i5), tpcNegNClsCrossedRows.at(i5), ctau.at(i5), tpcNsigmaPos.at(i5), tpcNsigmaNeg.at(i5), mK0Short.at(i5)); } } } @@ -367,10 +432,11 @@ struct lambdaspincorrelation { std::vector positiveIndex = {}; std::vector negativeIndex = {}; std::vector dcaBetweenDaughter = {}; + std::vector dcaV0ToPV = {}; int numbV0 = 0; // LOGF(info, "event collisions: (%d)", collision.index()); auto centrality = collision.centFT0C(); - if (cfgEventTypepp) + if (cfgCentTypepp) centrality = collision.centFT0M(); auto vz = collision.posZ(); int occupancy = collision.trackOccupancyInTimeRange(); @@ -427,6 +493,7 @@ struct lambdaspincorrelation { dcaPositive.push_back(std::abs(v0.dcapostopv())); dcaNegative.push_back(std::abs(v0.dcanegtopv())); dcaBetweenDaughter.push_back(std::abs(v0.dcaV0daughters())); + dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); if (lambdaTag) { v0Status.push_back(0); proton = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), o2::constants::physics::MassProton); @@ -459,12 +526,210 @@ struct lambdaspincorrelation { lambdaDummy = lambdaMother.at(i5); protonDummy = protonDaughter.at(i5); pionDummy = pionDaughter.at(i5); - lambdaPairmc(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5)); + lambdaPairmc(indexEvent, v0Status.at(i5), doubleStatus.at(i5), v0Cospa.at(i5), v0Radius.at(i5), dcaPositive.at(i5), dcaNegative.at(i5), dcaBetweenDaughter.at(i5), lambdaDummy.Pt(), lambdaDummy.Eta(), lambdaDummy.Phi(), lambdaDummy.M(), protonDummy.Pt(), protonDummy.Eta(), protonDummy.Phi(), positiveIndex.at(i5), negativeIndex.at(i5), dcaV0ToPV.at(i5)); } } } } PROCESS_SWITCH(lambdaspincorrelation, processMc, "Process montecarlo", true); + + void processMc2(aod::McCollision const&, + soa::SmallGroups const& collisions, + AllTrackCandidatesMC const&, + ResoV0s const& V0s, + aod::McParticles const&) + { + histos.fill(HIST("hEvtSelInfo"), 0.5); + if (collisions.size() == 1) { + + for (const auto& collision : collisions) { + + std::vector lambdaMother, protonDaughter, pionDaughter; + std::vector v0Status = {}; + std::vector doubleStatus = {}; + std::vector v0Cospa = {}; + std::vector v0Radius = {}; + std::vector dcaPositive = {}; + std::vector dcaNegative = {}; + std::vector positiveIndex = {}; + std::vector negativeIndex = {}; + std::vector dcaBetweenDaughter = {}; + std::vector dcaV0ToPV = {}; + int numbV0 = 0; + + auto centrality = collision.centFT0C(); + if (cfgCentTypepp) { + centrality = collision.centFT0M(); + } + + auto vz = collision.posZ(); + int occupancy = collision.trackOccupancyInTimeRange(); + + histos.fill(HIST("hEvtSelInfo"), 1.5); + + if (std::abs(collision.posZ()) < cfgCutVertex && + (!rctCut.requireRCTFlagChecker || rctChecker(collision)) && + + (!cfgEventTypepp || (collision.selection_bit(aod::evsel::kNoSameBunchPileup) && + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && + collision.selection_bit(aod::evsel::kNoTimeFrameBorder) && + collision.selection_bit(aod::evsel::kNoITSROFrameBorder))) && + + (!useNoCollInTimeRangeStandard || collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) && + collision.sel8() && + (!useGoodITSLayersAll || collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) && + occupancy < cfgCutOccupancy) { + + histos.fill(HIST("hEvtSelInfo"), 2.5); + auto groupedV0s = V0s.sliceBy(perCollisionV0s, collision.globalIndex()); + + for (const auto& v0 : groupedV0s) { + histos.fill(HIST("hEvtSelInfo"), 3.5); // all V0s seen + auto [lambdaTag, aLambdaTag, isValid] = getLambdaTagsMC(v0, collision); + + if (isValid) { + histos.fill(HIST("hEvtSelInfo"), 4.5); // passed getLambdaTagsMC + auto postrack1 = v0.template posTrack_as(); + auto negtrack1 = v0.template negTrack_as(); + + // Reject candidates whose reconstructed daughters are not MC-labelled. + if (!postrack1.has_mcParticle() || !negtrack1.has_mcParticle()) { + histos.fill(HIST("hEvtSelInfo"), 5.5); // rejected: no MC label + continue; + } + + // auto mcPos = postrack1.mcParticle(); + // auto mcNeg = negtrack1.mcParticle(); + + histos.fill(HIST("hEvtSelInfo"), 6.5); // rejected: no MC label + + if (lambdaTag) { + histos.fill(HIST("hV0Info"), 0.5); + } + if (aLambdaTag) { + histos.fill(HIST("hV0Info"), 1.5); + } + + if (lambdaTag && aLambdaTag) { + doubleStatus.push_back(true); + if (std::abs(v0.mLambda() - o2::constants::physics::MassLambda) < + std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda)) { + lambdaTag = true; + aLambdaTag = false; + } else { + lambdaTag = false; + aLambdaTag = true; + } + } else { + doubleStatus.push_back(false); + } + + if (lambdaTag) { + histos.fill(HIST("hV0Info"), 2.5); + } + if (aLambdaTag) { + histos.fill(HIST("hV0Info"), 3.5); + } + + positiveIndex.push_back(postrack1.globalIndex()); + negativeIndex.push_back(negtrack1.globalIndex()); + + v0Cospa.push_back(v0.v0cosPA()); + dcaV0ToPV.push_back(std::abs(v0.dcav0topv())); + v0Radius.push_back(v0.v0radius()); + dcaPositive.push_back(std::abs(v0.dcapostopv())); + dcaNegative.push_back(std::abs(v0.dcanegtopv())); + dcaBetweenDaughter.push_back(std::abs(v0.dcaV0daughters())); + + if (lambdaTag) { + v0Status.push_back(0); + + proton = ROOT::Math::PxPyPzMVector( + v0.pxpos(), + v0.pypos(), + v0.pzpos(), + o2::constants::physics::MassProton); + + antiPion = ROOT::Math::PxPyPzMVector( + v0.pxneg(), + v0.pyneg(), + v0.pzneg(), + o2::constants::physics::MassPionCharged); + + lambda = proton + antiPion; + + lambdaMother.push_back(lambda); + protonDaughter.push_back(proton); + pionDaughter.push_back(antiPion); + + histos.fill(HIST("hLambdaMass"), lambda.M()); + + } else if (aLambdaTag) { + v0Status.push_back(1); + + antiProton = ROOT::Math::PxPyPzMVector( + v0.pxneg(), + v0.pyneg(), + v0.pzneg(), + o2::constants::physics::MassProton); + + pion = ROOT::Math::PxPyPzMVector( + v0.pxpos(), + v0.pypos(), + v0.pzpos(), + o2::constants::physics::MassPionCharged); + + antiLambda = antiProton + pion; + + lambdaMother.push_back(antiLambda); + protonDaughter.push_back(antiProton); + pionDaughter.push_back(pion); + + histos.fill(HIST("hLambdaMass"), antiLambda.M()); + } + + numbV0 = numbV0 + 1; + } + } + + if (numbV0 > 1 && v0Cospa.size() > 1) { + histos.fill(HIST("hEvtSelInfo"), 7.5); + + lambdaEventmc(centrality, vz); + auto indexEvent = lambdaEventmc.lastIndex(); + + for (auto if1 = lambdaMother.begin(); if1 != lambdaMother.end(); ++if1) { + auto i5 = std::distance(lambdaMother.begin(), if1); + + lambdaDummy = lambdaMother.at(i5); + protonDummy = protonDaughter.at(i5); + pionDummy = pionDaughter.at(i5); + + lambdaPairmc(indexEvent, + v0Status.at(i5), + doubleStatus.at(i5), + v0Cospa.at(i5), + v0Radius.at(i5), + dcaPositive.at(i5), + dcaNegative.at(i5), + dcaBetweenDaughter.at(i5), + lambdaDummy.Pt(), + lambdaDummy.Eta(), + lambdaDummy.Phi(), + lambdaDummy.M(), + protonDummy.Pt(), + protonDummy.Eta(), + protonDummy.Phi(), + positiveIndex.at(i5), + negativeIndex.at(i5), + dcaV0ToPV.at(i5)); + } + } + } + } + } + } + PROCESS_SWITCH(lambdaspincorrelation, processMc2, "Process montecarlo2", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx b/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx index 87df3768a0e..7d3fca0718b 100644 --- a/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx +++ b/PWGLF/TableProducer/Strangeness/phiStrangeCorrelator.cxx @@ -13,6 +13,8 @@ /// \brief Table producer for Phi-Strangeness correlation analysis /// \author Stefano Cannito (stefano.cannito@cern.ch) +// o2-linter: disable=name/workflow-file (File contains multiple necessary structs) + #include "PWGLF/DataModel/LFPhiStrangeCorrelationTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/mcCentrality.h" @@ -96,6 +98,8 @@ struct PhiMesonCandProducer { Configurable cfgYAcceptance{"cfgYAcceptance", 0.5f, "Rapidity acceptance"}; } phiConfigs; + Configurable trueGenPhi{"trueGenPhi", false, "Phi trigger in MC Collisions: false - gen K+K- pair, true - gen phi meson"}; + // Configurables on phi pT bins Configurable> binspTPhi{"binspTPhi", {0.4, 0.8, 1.4, 2.0, 2.8, 4.0, 6.0, 10.0}, "pT bin limits for Phi"}; @@ -150,21 +154,27 @@ struct PhiMesonCandProducer { template bool selectionTrackResonance(const T& track) { - if (trackConfigs.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) + if (trackConfigs.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return false; - if (trackConfigs.cfgPVContributor && !track.isPVContributor()) + } + if (trackConfigs.cfgPVContributor && !track.isPVContributor()) { return false; + } - if (track.tpcNClsFound() < trackConfigs.minTPCnClsFound) + if (track.tpcNClsFound() < trackConfigs.minTPCnClsFound) { return false; + } - if (track.pt() < trackConfigs.cMinKaonPtcut) + if (track.pt() < trackConfigs.cMinKaonPtcut) { return false; + } - if (std::abs(track.dcaXY()) > trackConfigs.cMaxDCArToPVPhi->at(0) + (trackConfigs.cMaxDCArToPVPhi->at(1) / std::pow(track.pt(), trackConfigs.cMaxDCArToPVPhi->at(2)))) + if (std::abs(track.dcaXY()) > trackConfigs.cMaxDCArToPVPhi->at(0) + (trackConfigs.cMaxDCArToPVPhi->at(1) / std::pow(track.pt(), trackConfigs.cMaxDCArToPVPhi->at(2)))) { return false; - if (std::abs(track.dcaZ()) > trackConfigs.cMaxDCAzToPVcut) + } + if (std::abs(track.dcaZ()) > trackConfigs.cMaxDCAzToPVcut) { return false; + } return true; } @@ -173,10 +183,12 @@ struct PhiMesonCandProducer { template bool selectionPIDKaonpTdependent(const T& track) { - if (track.pt() < trackConfigs.pTToUseTOF && std::abs(track.tpcNSigmaKa()) < trackConfigs.nSigmaCutTPCKa) + if (track.pt() < trackConfigs.pTToUseTOF && std::abs(track.tpcNSigmaKa()) < trackConfigs.nSigmaCutTPCKa) { return true; - if (track.pt() >= trackConfigs.pTToUseTOF && track.hasTOF() && (std::pow(track.tofNSigmaKa(), 2) + std::pow(track.tpcNSigmaKa(), 2)) < std::pow(trackConfigs.nSigmaCutCombinedKa, 2)) + } + if (track.pt() >= trackConfigs.pTToUseTOF && track.hasTOF() && (std::pow(track.tofNSigmaKa(), 2) + std::pow(track.tpcNSigmaKa(), 2)) < std::pow(trackConfigs.nSigmaCutCombinedKa, 2)) { return true; + } return false; } @@ -197,60 +209,83 @@ struct PhiMesonCandProducer { { // Compile-time selection of the track partition to use based on the type of the analysis (Data or MCReco) auto posThisColl = [&]() { - if constexpr (isMC) + if constexpr (isMC) { return posMCTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - else + } else { return posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + } }(); auto negThisColl = [&]() { - if constexpr (isMC) + if constexpr (isMC) { return negMCTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - else + } else { return negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + } }(); // Loop over positive tracks for (const auto& track1 : posThisColl) { - if (!selectionTrackResonance(track1) || !selectionPIDKaonpTdependent(track1)) + if (!selectionTrackResonance(track1) || !selectionPIDKaonpTdependent(track1)) { continue; + } if constexpr (isMC) { - if (!track1.has_mcParticle()) + if (!track1.has_mcParticle()) { + continue; + } + if (!mcParticlesOpt) { continue; - const auto& mcParticles = mcParticlesOpt.value().get(); + } + + const auto& mcParticles = mcParticlesOpt->get(); const auto track1McParticle = mcParticles.rawIteratorAt(track1.mcParticleId()); - if (track1McParticle.pdgCode() != PDG_t::kKPlus || !track1McParticle.isPhysicalPrimary()) + if (track1McParticle.pdgCode() != PDG_t::kKPlus || !track1McParticle.isPhysicalPrimary()) { continue; + } } // Loop over negative tracks for (const auto& track2 : negThisColl) { - if (!selectionTrackResonance(track2) || !selectionPIDKaonpTdependent(track2)) + if (!selectionTrackResonance(track2) || !selectionPIDKaonpTdependent(track2)) { continue; + } if constexpr (isMC) { - if (!track2.has_mcParticle()) + if (!track2.has_mcParticle()) { continue; - const auto& mcParticles = mcParticlesOpt.value().get(); + } + if (!mcParticlesOpt) { + continue; + } + + const auto& mcParticles = mcParticlesOpt->get(); const auto track2McParticle = mcParticles.rawIteratorAt(track2.mcParticleId()); - if (track2McParticle.pdgCode() != PDG_t::kKMinus || !track2McParticle.isPhysicalPrimary()) + if (track2McParticle.pdgCode() != PDG_t::kKMinus || !track2McParticle.isPhysicalPrimary()) { continue; + } } // Kinematic reconstruction (common for data and MC) ROOT::Math::PxPyPzMVector recPhi = recMother(track1, track2, massKa, massKa); - if (recPhi.Pt() < phiConfigs.minPhiPt) + if (recPhi.Pt() < phiConfigs.minPhiPt) { continue; - if (recPhi.M() > phiConfigs.maxMPhi) + } + if (recPhi.M() > phiConfigs.maxMPhi) { continue; - if (std::abs(recPhi.Rapidity()) > phiConfigs.cfgYAcceptance) + } + if (std::abs(recPhi.Rapidity()) > phiConfigs.cfgYAcceptance) { continue; + } // PDG check and MC truth association for MCReco analysis if constexpr (isMC) { - const auto& mcParticles = mcParticlesOpt.value().get(); + if (!mcParticlesOpt) { + continue; + } + + const auto& mcParticles = mcParticlesOpt->get(); const auto track1McParticle = mcParticles.rawIteratorAt(track1.mcParticleId()); const auto track2McParticle = mcParticles.rawIteratorAt(track2.mcParticleId()); @@ -261,9 +296,10 @@ struct PhiMesonCandProducer { auto genPhiMaybe = [&]() -> std::optional { for (const auto& mother1Index : track1mcPartMotherIndexes) { for (const auto& mother2Index : track2mcPartMotherIndexes) { - if (mother1Index != mother2Index) + if (mother1Index != mother2Index) { continue; - const auto motherMcParticle = mcParticles.rawIteratorAt(mother1Index); + } + auto motherMcParticle = mcParticles.rawIteratorAt(mother1Index); if (std::abs(motherMcParticle.pdgCode()) == o2::constants::physics::Pdg::kPhi) { return motherMcParticle; } @@ -272,9 +308,10 @@ struct PhiMesonCandProducer { return std::nullopt; }(); - if (!genPhiMaybe) + if (!genPhiMaybe) { continue; - const auto genPhi = *genPhiMaybe; + } + const auto& genPhi = *genPhiMaybe; phimesonCandidatesMcReco(collision.globalIndex(), recPhi.M(), genPhi.pt(), genPhi.y(), genPhi.phi()); } else { @@ -315,28 +352,50 @@ struct PhiMesonCandProducer { void processMCGen(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) { - for (const auto& mcParticle1 : mcParticles) { - if (!mcParticle1.isPhysicalPrimary() || std::abs(mcParticle1.eta()) > trackConfigs.etaMax) - continue; - - for (const auto& mcParticle2 : mcParticles) { - if (!mcParticle2.isPhysicalPrimary() || std::abs(mcParticle2.eta()) > trackConfigs.etaMax) + if (trueGenPhi) { + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::Pdg::kPhi) { + continue; + } + if (mcParticle.pt() < phiConfigs.minPhiPt) { + continue; + } + if (std::abs(mcParticle.y()) > phiConfigs.cfgYAcceptance) { continue; + } - if (!(mcParticle1.pdgCode() == PDG_t::kKPlus && mcParticle2.pdgCode() == PDG_t::kKMinus) && - !(mcParticle1.pdgCode() == PDG_t::kKMinus && mcParticle2.pdgCode() == PDG_t::kKPlus)) + phimesonCandidatesMcGen(mcCollision.globalIndex(), 0, mcParticle.pt(), mcParticle.y(), mcParticle.phi()); + } + } else { + for (const auto& mcParticle1 : mcParticles) { + if (!mcParticle1.isPhysicalPrimary() || std::abs(mcParticle1.eta()) > trackConfigs.etaMax) { continue; + } - ROOT::Math::PxPyPzMVector genKPair = recMother(mcParticle1, mcParticle2, massKa, massKa); + for (const auto& mcParticle2 : mcParticles) { + if (!mcParticle2.isPhysicalPrimary() || std::abs(mcParticle2.eta()) > trackConfigs.etaMax) { + continue; + } - if (genKPair.Pt() < phiConfigs.minPhiPt) - continue; - if (genKPair.M() > phiConfigs.maxMPhi) - continue; - if (std::abs(genKPair.Rapidity()) > phiConfigs.cfgYAcceptance) - continue; + if ((mcParticle1.pdgCode() != PDG_t::kKPlus || mcParticle2.pdgCode() != PDG_t::kKMinus) && + (mcParticle1.pdgCode() != PDG_t::kKMinus || mcParticle2.pdgCode() != PDG_t::kKPlus)) { + continue; + } + + ROOT::Math::PxPyPzMVector genKPair = recMother(mcParticle1, mcParticle2, massKa, massKa); + + if (genKPair.Pt() < phiConfigs.minPhiPt) { + continue; + } + if (genKPair.M() > phiConfigs.maxMPhi) { + continue; + } + if (std::abs(genKPair.Rapidity()) > phiConfigs.cfgYAcceptance) { + continue; + } - phimesonCandidatesMcGen(mcCollision.globalIndex(), genKPair.M(), genKPair.Pt(), genKPair.Rapidity(), genKPair.Phi()); + phimesonCandidatesMcGen(mcCollision.globalIndex(), genKPair.M(), genKPair.Pt(), genKPair.Rapidity(), genKPair.Phi()); + } } } } @@ -344,11 +403,21 @@ struct PhiMesonCandProducer { PROCESS_SWITCH(PhiMesonCandProducer, processMCGen, "Process function to select Phi meson candidates in MCGen", false); }; -struct K0sReducedCandProducer { - // Produce the table with the K0s candidates information +struct V0ReducedCandProducer { + enum V0Type { kK0S = 0, + kLambda, + kAntiLambda }; + + // Produce the table with the V0 candidates information Produces k0sReducedCandidatesData; Produces k0sReducedCandidatesMcReco; + Produces lambdaReducedCandidatesData; + Produces lambdaReducedCandidatesMcReco; + + Produces antilambdaReducedCandidatesData; + Produces antilambdaReducedCandidatesMcReco; + HistogramRegistry histos{"k0sReducedCandidates", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Configurable on multiplicity bins @@ -358,6 +427,7 @@ struct K0sReducedCandProducer { struct : ConfigurableGroup { Configurable etaMax{"etaMax", 0.8f, "eta max"}; Configurable nSigmaCutTPCSecPion{"nSigmaCutTPCSecPion", 4.0f, "Value of the TPC Nsigma cut for secondary Pions"}; + Configurable nSigmaCutTPCSecProton{"nSigmaCutTPCSecProton", 4.0f, "Value of the TPC Nsigma cut for secondary Protons"}; Configurable minTPCnClsFound{"minTPCnClsFound", 70, "min number of found TPC clusters"}; Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "min number of TPC crossed rows"}; @@ -366,36 +436,52 @@ struct K0sReducedCandProducer { // Configurables for V0 selection struct : ConfigurableGroup { - Configurable v0SettingCosPA{"v0SettingCosPA", 0.98f, "V0 CosPA"}; - Configurable v0SettingRadius{"v0SettingRadius", 0.5f, "v0radius"}; - Configurable v0SettingDCAV0Dau{"v0SettingDCAV0Dau", 1.0f, "DCA V0 Daughters"}; - Configurable v0SettingDCAPosToPV{"v0SettingDCAPosToPV", 0.1f, "DCA Pos To PV"}; - Configurable v0SettingDCANegToPV{"v0SettingDCANegToPV", 0.1f, "DCA Neg To PV"}; - Configurable v0SettingMinPt{"v0SettingMinPt", 0.1f, "V0 min pt"}; + Configurable decayRadius{"decayRadius", 0.5f, "V0 decay radius"}; + Configurable dcaV0Dau{"dcaV0Dau", 1.0f, "DCA V0 Daughters"}; + Configurable dcaPosToPV{"dcaPosToPV", 0.1f, "DCA Pos To PV"}; + Configurable dcaNegToPV{"dcaNegToPV", 0.1f, "DCA Neg To PV"}; - Configurable cfgFurtherV0Selection{"cfgFurtherV0Selection", false, "Further V0 selection"}; - Configurable ctauK0s{"ctauK0s", 20.0f, "C tau K0s(cm)"}; - Configurable paramArmenterosCut{"paramArmenterosCut", 0.2f, "parameter Armenteros Cut"}; - Configurable v0rejK0s{"v0rejK0s", 0.005f, "V0 rej K0s"}; + Configurable furtherV0Selection{"furtherV0Selection", false, "Further V0 selection"}; - Configurable> rangeMK0sSignal{"rangeMK0sSignal", {0.47f, 0.53f}, "K0S mass range for signal extraction"}; + Configurable yAcceptance{"yAcceptance", 0.5f, "Rapidity acceptance"}; - Configurable cfgYAcceptance{"cfgYAcceptance", 0.5f, "Rapidity acceptance"}; + struct : ConfigurableGroup { + Configurable cosPAK0S{"cosPAK0S", 0.98f, "K0S CosPA"}; + Configurable ctauK0S{"ctauK0S", 20.0f, "C tau K0s(cm)"}; + Configurable rejMassK0S{"rejMassK0S", 0.005f, "V0 rej K0s"}; + + Configurable paramArmenterosCutK0S{"paramArmenterosCutK0S", 0.2f, "Parameter for Armenteros Cut for K0S"}; + + Configurable minPtK0S{"minPtK0S", 0.1f, "K0S min pt"}; + Configurable> binspTK0S{"binspTK0S", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S"}; + Configurable> rangeMK0sSignal{"rangeMK0sSignal", {0.47f, 0.53f}, "K0S mass range for signal extraction"}; + } k0sConfigs; + + struct : ConfigurableGroup { + Configurable cosPALambda{"cosPALambda", 0.98f, "Lambda CosPA"}; + Configurable ctauLambda{"ctauLambda", 7.89f, "C tau Lambda(cm)"}; + Configurable rejMassLambda{"rejMassLambda", 0.005f, "V0 rej Lambda"}; + + Configurable minPtLambda{"minPtLambda", 0.1f, "Lambda min pt"}; + Configurable> binspTLambda{"binspTLambda", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Lambda"}; + Configurable> rangeMLambdaSignal{"rangeMLambdaSignal", {1.11f, 1.12f}, "Lambda mass range for signal extraction"}; + } lambdaConfigs; } v0Configs; - // Configurable on K0S pT bins - Configurable> binspTK0S{"binspTK0S", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S"}; + Configurable> binspTXi{"binspTXi", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi"}; // Constants - static constexpr double massK0S = o2::constants::physics::MassK0Short; - static constexpr double massLambda = o2::constants::physics::MassLambda0; + static constexpr double MassK0S = o2::constants::physics::MassK0Short; + static constexpr double MassLambda = o2::constants::physics::MassLambda0; // Filter on default selected collisions // Filter collisionFilter = aod::lf_selection_default_collision::defaultSel == true; Filter collisionFilter = aod::lf_selection_event::defaultSel == true; // Defining filters on V0s (cannot filter on dynamic columns) - Filter v0PreFilter = (nabs(aod::v0data::dcapostopv) > v0Configs.v0SettingDCAPosToPV && nabs(aod::v0data::dcanegtopv) > v0Configs.v0SettingDCANegToPV && aod::v0data::dcaV0daughters < v0Configs.v0SettingDCAV0Dau); + Filter v0PreFilter = (nabs(aod::v0data::dcapostopv) > v0Configs.dcaPosToPV && + nabs(aod::v0data::dcanegtopv) > v0Configs.dcaNegToPV && + aod::v0data::dcaV0daughters < v0Configs.dcaV0Dau); // Defining the type of the collisions for data and MC using SelCollisions = soa::Join; @@ -408,38 +494,66 @@ struct K0sReducedCandProducer { using FullV0s = soa::Filtered; using FullMCV0s = soa::Filtered>; - using V0DauTracks = soa::Join; + using V0DauTracks = soa::Join; using V0DauMCTracks = soa::Join; void init(InitContext&) { AxisSpec binnedmultAxis{(std::vector)binsMult, "centFT0M"}; - AxisSpec binnedpTK0SAxis{(std::vector)binspTK0S, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTK0SAxis{(std::vector)v0Configs.k0sConfigs.binspTK0S, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTLambdaAxis{(std::vector)v0Configs.lambdaConfigs.binspTLambda, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTXiAxis{(std::vector)binspTXi, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec massK0SAxis = {200, 0.45f, 0.55f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec massLambdaAxis = {200, 1.08f, 1.15f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; histos.add("h3K0sCandidatesMass", "K^{0}_{S} candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTK0SAxis, massK0SAxis}); + histos.add("h3LambdaCandidatesMass", "#Lambda candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, massLambdaAxis}); + histos.add("h3AntiLambdaCandidatesMass", "#bar{#Lambda} candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, massLambdaAxis}); + + histos.add("h2LambdaFromXi", "#Lambda from #Xi", kTH2F, {binnedpTLambdaAxis, binnedpTXiAxis}); + histos.add("h2AntiLambdaFromXi", "#bar{#Lambda} from #Xi", kTH2F, {binnedpTLambdaAxis, binnedpTXiAxis}); + } + + template + constexpr int getPdgCode() + { + if constexpr (v0Type == kK0S) { + return PDG_t::kK0Short; + } else if constexpr (v0Type == kLambda) { + return PDG_t::kLambda0; + } else if constexpr (v0Type == kAntiLambda) { + return PDG_t::kLambda0Bar; + } else { + static_assert(v0Type == kK0S || v0Type == kLambda || v0Type == kAntiLambda, "Unsupported particle type in getPdgCode"); + return 0; + } } // Single track selection for strangeness sector template bool selectionTrackStrangeness(const T& track) { - if (!track.hasTPC()) + if (!track.hasTPC()) { return false; - if (track.tpcNClsFound() < trackConfigs.minTPCnClsFound) + } + if (track.tpcNClsFound() < trackConfigs.minTPCnClsFound) { return false; - if (track.tpcNClsCrossedRows() < trackConfigs.minNCrossedRowsTPC) + } + if (track.tpcNClsCrossedRows() < trackConfigs.minNCrossedRowsTPC) { return false; - if (track.tpcChi2NCl() > trackConfigs.maxChi2TPC) + } + if (track.tpcChi2NCl() > trackConfigs.maxChi2TPC) { return false; + } - if (std::abs(track.eta()) > trackConfigs.etaMax) + if (std::abs(track.eta()) > trackConfigs.etaMax) { return false; + } return true; } // V0 selection - template + template bool selectionV0(const T1& v0, const T2& collision) { using V0DauTrackType = std::conditional_t; @@ -447,72 +561,455 @@ struct K0sReducedCandProducer { const auto& posDaughterTrack = v0.template posTrack_as(); const auto& negDaughterTrack = v0.template negTrack_as(); - if (!selectionTrackStrangeness(posDaughterTrack) || !selectionTrackStrangeness(negDaughterTrack)) + if (!selectionTrackStrangeness(posDaughterTrack) || !selectionTrackStrangeness(negDaughterTrack)) { return false; + } if constexpr (!isMC) { - if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) - return false; - if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) - return false; + if constexpr (v0Type == kK0S) { + if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + } else if constexpr (v0Type == kLambda) { + if (std::abs(posDaughterTrack.tpcNSigmaPr()) > trackConfigs.nSigmaCutTPCSecProton) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + } else if constexpr (v0Type == kAntiLambda) { + if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPr()) > trackConfigs.nSigmaCutTPCSecProton) { + return false; + } + } } - if (v0.v0cosPA() < v0Configs.v0SettingCosPA) + if (v0.v0radius() < v0Configs.decayRadius) { + return false; + } + if (std::abs(v0.rapidity(v0Type)) > v0Configs.yAcceptance) { return false; - if (v0.v0radius() < v0Configs.v0SettingRadius) + } + + const float cutCosPA = (v0Type == kK0S) ? v0Configs.k0sConfigs.cosPAK0S : v0Configs.lambdaConfigs.cosPALambda; + const float cutMinPt = (v0Type == kK0S) ? v0Configs.k0sConfigs.minPtK0S : v0Configs.lambdaConfigs.minPtLambda; + const float cutCtau = (v0Type == kK0S) ? v0Configs.k0sConfigs.ctauK0S : v0Configs.lambdaConfigs.ctauLambda; + + if (v0.v0cosPA() < cutCosPA) { return false; - if (v0.pt() < v0Configs.v0SettingMinPt) + } + if (v0.pt() < cutMinPt) { return false; + } - if (v0Configs.cfgFurtherV0Selection) { - if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massK0S > v0Configs.ctauK0s) - return false; - if (v0.qtarm() < (v0Configs.paramArmenterosCut * std::abs(v0.alpha()))) - return false; - if (std::abs(v0.mLambda() - massLambda) < v0Configs.v0rejK0s) + if (v0Configs.furtherV0Selection) { + constexpr float assumedMass = v0Type == kK0S ? MassK0S : MassLambda; + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * assumedMass > cutCtau) { return false; - } + } - if (std::abs(v0.yK0Short()) > v0Configs.cfgYAcceptance) - return false; + if constexpr (v0Type == kK0S) { + if (std::abs(v0.mLambda() - MassLambda) < v0Configs.k0sConfigs.rejMassK0S || + std::abs(v0.mAntiLambda() - MassLambda) < v0Configs.k0sConfigs.rejMassK0S) { + return false; + } + if (v0.qtarm() < (v0Configs.k0sConfigs.paramArmenterosCutK0S * std::abs(v0.alpha()))) { + return false; + } + } else { + if (std::abs(v0.mK0Short() - MassK0S) < v0Configs.lambdaConfigs.rejMassLambda) { + return false; + } + } + } return true; } + template + void processV0Type(TColl const& collision, TV0 const& v0, std::optional> mcParticlesOpt = std::nullopt) + { + // Cut on V0 dynamic columns + if (!selectionV0(v0, collision)) { + return; + } + + if constexpr (isMC) { + if (!v0.has_mcParticle() || !mcParticlesOpt) { + return; + } + + const auto& mcParticles = mcParticlesOpt->get(); + const auto& v0McParticle = mcParticles.rawIteratorAt(v0.mcParticleId()); + // if (v0McParticle.pdgCode() != getPdgCode() || !v0McParticle.isPhysicalPrimary()) { + // return; + //} + if (v0McParticle.pdgCode() != getPdgCode()) { + return; + } + + if constexpr ((v0Type == kLambda || v0Type == kAntiLambda)) { + if (v0.has_mcMotherParticle()) { + auto v0McMotherParticle = mcParticles.rawIteratorAt(v0.mcMotherParticleId()); + if (std::abs(v0McMotherParticle.y()) <= v0Configs.yAcceptance) { + if constexpr (v0Type == kLambda) { + if (v0McMotherParticle.pdgCode() == PDG_t::kXiMinus || v0McMotherParticle.pdgCode() == o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2LambdaFromXi"), v0McParticle.pt(), v0McMotherParticle.pt()); + } + } else if constexpr (v0Type == kAntiLambda) { + if (v0McMotherParticle.pdgCode() == PDG_t::kXiPlusBar || v0McMotherParticle.pdgCode() == -o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2AntiLambdaFromXi"), v0McParticle.pt(), v0McMotherParticle.pt()); + } + } + } + } + } + + if (!v0McParticle.isPhysicalPrimary()) { + return; + } + + if constexpr (v0Type == kK0S) { + k0sReducedCandidatesMcReco(collision.globalIndex(), v0.mK0Short(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + } else if constexpr (v0Type == kLambda) { + lambdaReducedCandidatesMcReco(collision.globalIndex(), v0.mLambda(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + } else if constexpr (v0Type == kAntiLambda) { + antilambdaReducedCandidatesMcReco(collision.globalIndex(), v0.mAntiLambda(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + } + } else { + if constexpr (v0Type == kK0S) { + histos.fill(HIST("h3K0sCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mK0Short()); + k0sReducedCandidatesData(collision.globalIndex(), v0.mK0Short(), v0.pt(), v0.yK0Short(), v0.phi()); + } else if constexpr (v0Type == kLambda) { + histos.fill(HIST("h3LambdaCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mLambda()); + lambdaReducedCandidatesData(collision.globalIndex(), v0.mLambda(), v0.pt(), v0.yLambda(), v0.phi()); + } else if constexpr (v0Type == kAntiLambda) { + histos.fill(HIST("h3AntiLambdaCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mAntiLambda()); + antilambdaReducedCandidatesData(collision.globalIndex(), v0.mAntiLambda(), v0.pt(), v0.yLambda(), v0.phi()); + // histos.fill(HIST("h3LambdaCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mAntiLambda()); + // lambdaReducedCandidatesData(collision.globalIndex(), v0.mAntiLambda(), v0.pt(), v0.yLambda(), v0.phi()); + } + } + } + + template + void evaluateAllHypotheses(TColl const& collision, TV0 const& v0, std::optional> mcParticlesOpt = std::nullopt) + { + (processV0Type(collision, v0, mcParticlesOpt), ...); + } + // void processData(SelCollisions::iterator const& collision, FullV0s const& V0s, V0DauTracks const&) void processData(FilteredSelCollisions::iterator const& collision, FullV0s const& V0s, V0DauTracks const&) { for (const auto& v0 : V0s) { - // Cut on V0 dynamic columns - if (!selectionV0(v0, collision)) - continue; - - histos.fill(HIST("h3K0sCandidatesMass"), collision.centFT0M(), v0.pt(), v0.mK0Short()); - - k0sReducedCandidatesData(collision.globalIndex(), v0.mK0Short(), v0.pt(), v0.yK0Short(), v0.phi()); + evaluateAllHypotheses(collision, v0); } } - PROCESS_SWITCH(K0sReducedCandProducer, processData, "Process function to select reduced K0s candidates in Data or in McReco (w/o McTruth) analysis", true); + PROCESS_SWITCH(V0ReducedCandProducer, processData, "Process function to select reduced K0S candidates in Data or in McReco (w/o McTruth) analysis", true); // void processMCReco(SimCollisions::iterator const& collision, FullMCV0s const& V0s, V0DauMCTracks const&, aod::McParticles const& mcParticles) void processMCReco(FilteredSimCollisions::iterator const& collision, FullMCV0s const& V0s, V0DauMCTracks const&, aod::McParticles const& mcParticles) { for (const auto& v0 : V0s) { - if (!selectionV0(v0, collision)) - continue; - if (!v0.has_mcParticle()) - continue; + evaluateAllHypotheses(collision, v0, mcParticles); + } + } - const auto& v0McParticle = mcParticles.rawIteratorAt(v0.mcParticleId()); - if (std::abs(v0McParticle.pdgCode()) != PDG_t::kK0Short || !v0McParticle.isPhysicalPrimary()) - continue; + PROCESS_SWITCH(V0ReducedCandProducer, processMCReco, "Process function to select reduced K0s candidates in MCReco w MC truth", false); +}; + +struct CascadeReducedCandProducer { + enum CascadeType { kXi = 0, + kAntiXi, + kOmega, + kAntiOmega }; + + // Produce the table with the Xi candidates information + Produces xiReducedCandidatesData; + Produces xiReducedCandidatesMcReco; + + Produces omegaReducedCandidatesData; + Produces omegaReducedCandidatesMcReco; + + HistogramRegistry histos{"xiReducedCandidates", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // Configurable on multiplicity bins + Configurable> binsMult{"binsMult", {0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}, "Multiplicity bin limits"}; + + // Configurables for tracks selection + struct : ConfigurableGroup { + Configurable etaMax{"etaMax", 0.8f, "eta max"}; + Configurable nSigmaCutTPCSecPion{"nSigmaCutTPCSecPion", 4.0f, "Value of the TPC Nsigma cut for secondary Pions"}; + Configurable nSigmaCutTPCSecKaon{"nSigmaCutTPCSecKaon", 4.0f, "Value of the TPC Nsigma cut for secondary Kaons"}; + Configurable nSigmaCutTPCSecProton{"nSigmaCutTPCSecProton", 4.0f, "Value of the TPC Nsigma cut for secondary Protons"}; + + Configurable minTPCnClsFound{"minTPCnClsFound", 70, "min number of found TPC clusters"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "min number of TPC crossed rows"}; + Configurable maxChi2TPC{"maxChi2TPC", 4.0f, "max chi2 per cluster TPC"}; + } trackConfigs; + + // Configurables for Cascade selection + struct : ConfigurableGroup { + Configurable dcaV0Dau{"dcaV0Dau", 1.0f, "DCA V0 Daughters"}; + Configurable dcaCascDau{"dcaCascDau", 1.0f, "DCA Cascade Daughters"}; + Configurable dcaPosToPV{"dcaPosToPV", 0.1f, "DCA Pos To PV"}; + Configurable dcaNegToPV{"dcaNegToPV", 0.1f, "DCA Neg To PV"}; + Configurable dcaBachToPV{"dcaBachToPV", 0.1f, "DCA Bach To PV"}; + + Configurable yAcceptance{"yAcceptance", 0.5f, "Rapidity acceptance"}; + + struct : ConfigurableGroup { + Configurable v0CosPAXi{"v0CosPAXi", 0.98f, "Xi V0 CosPA"}; + Configurable cosPAXi{"cosPAXi", 0.98f, "Xi CosPA"}; + Configurable v0RadiusXi{"v0RadiusXi", 0.5f, "V0 decay radius"}; + Configurable radiusXi{"radiusXi", 0.5f, "Xi decay radius"}; + + Configurable minPtXi{"minPtXi", 0.8f, "Xi min pt"}; + Configurable> binspTXi{"binspTXi", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi"}; + } xiConfigs; + + struct : ConfigurableGroup { + Configurable v0CosPAOmega{"v0CosPAOmega", 0.98f, "Omega V0 CosPA"}; + Configurable cosPAOmega{"cosPAOmega", 0.98f, "Omega CosPA"}; + Configurable v0RadiusOmega{"v0RadiusOmega", 0.5f, "V0 decay radius"}; + Configurable radiusOmega{"radiusOmega", 0.5f, "Omega decay radius"}; + + Configurable minPtOmega{"minPtOmega", 0.8f, "Omega min pt"}; + Configurable> binspTOmega{"binspTOmega", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Omega"}; + } omegaConfigs; + } cascadeConfigs; + + // Constants + static constexpr double MassXi = o2::constants::physics::MassXiMinus; + static constexpr double MassOmega = o2::constants::physics::MassOmegaMinus; + + // Filter on default selected collisions + Filter collisionFilter = aod::lf_selection_event::defaultSel == true; + + // Defining filters on Cascades (cannot filter on dynamic columns) + Filter cascadePreFilter = (nabs(aod::cascdata::dcapostopv) > cascadeConfigs.dcaPosToPV && + nabs(aod::cascdata::dcanegtopv) > cascadeConfigs.dcaNegToPV && + nabs(aod::cascdata::dcabachtopv) > cascadeConfigs.dcaBachToPV && + aod::cascdata::dcaV0daughters < cascadeConfigs.dcaV0Dau && + aod::cascdata::dcacascdaughters < cascadeConfigs.dcaCascDau); + + // Defining the type of the collisions for data and MC + using SelCollisions = soa::Join; + using SimCollisions = soa::Join; + + using FilteredSelCollisions = soa::Filtered; + using FilteredSimCollisions = soa::Filtered; + + // Defining the type of the Cascades and corresponding daughter and bachelor tracks for data and MC + using FullCascades = soa::Filtered; + using FullMCCascades = soa::Filtered>; + + using DauTracks = soa::Join; + using DauMCTracks = soa::Join; + + void init(InitContext&) + { + AxisSpec binnedmultAxis{(std::vector)binsMult, "centFT0M"}; + AxisSpec binnedpTXiAxis{(std::vector)cascadeConfigs.xiConfigs.binspTXi, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTOmegaAxis{(std::vector)cascadeConfigs.omegaConfigs.binspTOmega, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec massXiAxis = {200, 1.2f, 1.4f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec massOmegaAxis = {200, 1.6f, 1.8f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + + histos.add("h3XiCandidatesMass", "#Xi candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTXiAxis, massXiAxis}); + histos.add("h3OmegaCandidatesMass", "#Omega candidate invariant mass", kTH3F, {binnedmultAxis, binnedpTOmegaAxis, massOmegaAxis}); + } + + template + constexpr int getPdgCode() + { + if constexpr (cascadeType == kXi) { + return PDG_t::kXiMinus; + } else if constexpr (cascadeType == kAntiXi) { + return PDG_t::kXiPlusBar; + } else if constexpr (cascadeType == kOmega) { + return PDG_t::kOmegaMinus; + } else if constexpr (cascadeType == kAntiOmega) { + return PDG_t::kOmegaPlusBar; + } else { + static_assert(cascadeType == kXi || cascadeType == kAntiXi || cascadeType == kOmega || cascadeType == kAntiOmega, "Unsupported particle type in getPdgCode"); + return 0; + } + } + + // Single track selection for strangeness sector + template + bool selectionTrackStrangeness(const T& track) + { + if (!track.hasTPC()) { + return false; + } + if (track.tpcNClsFound() < trackConfigs.minTPCnClsFound) { + return false; + } + if (track.tpcNClsCrossedRows() < trackConfigs.minNCrossedRowsTPC) { + return false; + } + if (track.tpcChi2NCl() > trackConfigs.maxChi2TPC) { + return false; + } - k0sReducedCandidatesMcReco(collision.globalIndex(), v0.mK0Short(), v0McParticle.pt(), v0McParticle.y(), v0McParticle.phi()); + if (std::abs(track.eta()) > trackConfigs.etaMax) { + return false; } + return true; } - PROCESS_SWITCH(K0sReducedCandProducer, processMCReco, "Process function to select reduced K0s candidates in MCReco w MC truth", false); + // Cascade selection + template + bool selectionCascade(const T1& cascade, const T2& collision) + { + if constexpr (cascadeType == kXi || cascadeType == kOmega) { + if (cascade.sign() > 0) { + return false; // Matter must have negative charge + } + } else { + if (cascade.sign() < 0) { + return false; // Anti-matter must have positive charge + } + } + + using DauTrackType = std::conditional_t; + + const auto& posDaughterTrack = cascade.template posTrack_as(); + const auto& negDaughterTrack = cascade.template negTrack_as(); + const auto& bachDaughterTrack = cascade.template bachelor_as(); + + if (!selectionTrackStrangeness(posDaughterTrack) || !selectionTrackStrangeness(negDaughterTrack) || !selectionTrackStrangeness(bachDaughterTrack)) { + return false; + } + + if constexpr (!isMC) { + if constexpr (cascadeType == kXi || cascadeType == kOmega) { + if (std::abs(posDaughterTrack.tpcNSigmaPr()) > trackConfigs.nSigmaCutTPCSecProton) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + } else { + if (std::abs(posDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + if (std::abs(negDaughterTrack.tpcNSigmaPr()) > trackConfigs.nSigmaCutTPCSecProton) { + return false; + } + } + + if constexpr (cascadeType == kXi || cascadeType == kAntiXi) { + if (std::abs(bachDaughterTrack.tpcNSigmaPi()) > trackConfigs.nSigmaCutTPCSecPion) { + return false; + } + } else if constexpr (cascadeType == kOmega || cascadeType == kAntiOmega) { + if (std::abs(bachDaughterTrack.tpcNSigmaKa()) > trackConfigs.nSigmaCutTPCSecKaon) { + return false; + } + } + } + + if (std::abs(cascade.rapidity(cascadeType)) > cascadeConfigs.yAcceptance) { + return false; + } + + const float cutV0CosPA = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.v0CosPAXi : cascadeConfigs.omegaConfigs.v0CosPAOmega; + const float cutCosPA = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.cosPAXi : cascadeConfigs.omegaConfigs.cosPAOmega; + const float cutV0Radius = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.v0RadiusXi : cascadeConfigs.omegaConfigs.v0RadiusOmega; + const float cutRadius = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.radiusXi : cascadeConfigs.omegaConfigs.radiusOmega; + const float cutMinPt = (cascadeType == kXi || cascadeType == kAntiXi) ? cascadeConfigs.xiConfigs.minPtXi : cascadeConfigs.omegaConfigs.minPtOmega; + + const auto& pvx = collision.posX(); + const auto& pvy = collision.posY(); + const auto& pvz = collision.posZ(); + + if (cascade.v0cosPA(pvx, pvy, pvz) < cutV0CosPA) { + return false; + } + if (cascade.casccosPA(pvx, pvy, pvz) < cutCosPA) { + return false; + } + if (cascade.v0radius() < cutV0Radius) { + return false; + } + if (cascade.cascradius() < cutRadius) { + return false; + } + if (cascade.pt() < cutMinPt) { + return false; + } + + return true; + } + + template + void processCascadeType(TColl const& collision, TCascade const& cascade, std::optional> mcParticlesOpt = std::nullopt) + { + // Cut on Cascade dynamic columns + if (!selectionCascade(cascade, collision)) { + return; + } + + if constexpr (isMC) { + if (!cascade.has_mcParticle() || !mcParticlesOpt) { + return; + } + + const auto& mcParticles = mcParticlesOpt->get(); + const auto& cascadeMcParticle = mcParticles.rawIteratorAt(cascade.mcParticleId()); + if (cascadeMcParticle.pdgCode() != getPdgCode() || !cascadeMcParticle.isPhysicalPrimary()) { + return; + } + + if constexpr (cascadeType == kXi || cascadeType == kAntiXi) { + xiReducedCandidatesMcReco(collision.globalIndex(), cascade.mXi(), cascadeMcParticle.pt(), cascadeMcParticle.y(), cascadeMcParticle.phi()); + } else if constexpr (cascadeType == kOmega || cascadeType == kAntiOmega) { + omegaReducedCandidatesMcReco(collision.globalIndex(), cascade.mOmega(), cascadeMcParticle.pt(), cascadeMcParticle.y(), cascadeMcParticle.phi()); + } + } else { + if constexpr (cascadeType == kXi || cascadeType == kAntiXi) { + histos.fill(HIST("h3XiCandidatesMass"), collision.centFT0M(), cascade.pt(), cascade.mXi()); + xiReducedCandidatesData(collision.globalIndex(), cascade.mXi(), cascade.pt(), cascade.yXi(), cascade.phi()); + } else if constexpr (cascadeType == kOmega || cascadeType == kAntiOmega) { + histos.fill(HIST("h3OmegaCandidatesMass"), collision.centFT0M(), cascade.pt(), cascade.mOmega()); + omegaReducedCandidatesData(collision.globalIndex(), cascade.mOmega(), cascade.pt(), cascade.yOmega(), cascade.phi()); + } + } + } + + template + void evaluateAllHypotheses(TColl const& collision, TCascade const& cascade, std::optional> mcParticlesOpt = std::nullopt) + { + (processCascadeType(collision, cascade, mcParticlesOpt), ...); + } + + void + processData(FilteredSelCollisions::iterator const& collision, FullCascades const& cascades, DauTracks const&) + { + for (const auto& cascade : cascades) { + evaluateAllHypotheses(collision, cascade); + } + } + + PROCESS_SWITCH(CascadeReducedCandProducer, processData, "Process function to select reduced Xi candidates in Data or in McReco (w/o McTruth) analysis", true); + + void processMCReco(FilteredSimCollisions::iterator const& collision, FullMCCascades const& cascades, DauMCTracks const&, aod::McParticles const& mcParticles) + { + for (const auto& cascade : cascades) { + evaluateAllHypotheses(collision, cascade, mcParticles); + } + } + + PROCESS_SWITCH(CascadeReducedCandProducer, processMCReco, "Process function to select reduced Xi candidates in MCReco w MC truth", false); }; struct PionTrackProducer { @@ -546,6 +1043,7 @@ struct PionTrackProducer { // Configurable forceTOF{"forceTOF", false, "force the TOF signal for the PID"}; Configurable tofPIDThreshold{"tofPIDThreshold", 0.5, "minimum pT after which TOF PID is applicable"}; Configurable> trkPIDspecies{"trkPIDspecies", std::vector{o2::track::PID::Kaon, o2::track::PID::Proton}, "Trk sel: Particles species for PID rejection, kaon, proton"}; + Configurable applyElRejection{"applyElRejection", false, "Apply or not the electron rejection"}; Configurable> pidRangeTPCEl{"pidRangeTPCEl", {-3.0f, 5.0f}, "nSigma TPC range for electrons"}; Configurable pidTPCMaxHadrons{"pidTPCMaxHadrons", 3.0f, "maximum nSigma TPC for hadrons"}; Configurable pidTOFMaxHadrons{"pidTOFMaxHadrons", 3.0f, "maximum nSigma TOF for hadrons"}; @@ -558,7 +1056,7 @@ struct PionTrackProducer { // Configurable on pion pT bins Configurable> binspTPi{"binspTPi", {0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 3.0}, "pT bin limits for pions"}; - static constexpr double massPi = o2::constants::physics::MassPiPlus; + static constexpr double MassPi = o2::constants::physics::MassPiPlus; // Filter on default selected collisions // Filter collisionFilter = aod::lf_selection_default_collision::defaultSel == true; @@ -632,23 +1130,25 @@ struct PionTrackProducer { template bool pidHypothesesRejection(const T& track) { - // Electron rejection - auto nSigmaTPCEl = aod::pidutils::tpcNSigma(o2::track::PID::Electron, track); - - if (nSigmaTPCEl > trackConfigs.pidRangeTPCEl->first && nSigmaTPCEl < trackConfigs.pidRangeTPCEl->second) { - auto nSigmaTPCPi = aod::pidutils::tpcNSigma(o2::track::PID::Pion, track); - auto nSigmaTPCKa = aod::pidutils::tpcNSigma(o2::track::PID::Kaon, track); - auto nSigmaTPCPr = aod::pidutils::tpcNSigma(o2::track::PID::Proton, track); - - if (std::abs(nSigmaTPCPi) > trackConfigs.pidTPCMaxHadrons && - std::abs(nSigmaTPCKa) > trackConfigs.pidTPCMaxHadrons && - std::abs(nSigmaTPCPr) > trackConfigs.pidTPCMaxHadrons) { - return false; + // Electron rejection (if enabled) + if (trackConfigs.applyElRejection) { + auto nSigmaTPCEl = aod::pidutils::tpcNSigma(o2::track::PID::Electron, track); + + if (nSigmaTPCEl > trackConfigs.pidRangeTPCEl->first && nSigmaTPCEl < trackConfigs.pidRangeTPCEl->second) { + auto nSigmaTPCPi = aod::pidutils::tpcNSigma(o2::track::PID::Pion, track); + auto nSigmaTPCKa = aod::pidutils::tpcNSigma(o2::track::PID::Kaon, track); + auto nSigmaTPCPr = aod::pidutils::tpcNSigma(o2::track::PID::Proton, track); + + if (std::abs(nSigmaTPCPi) > trackConfigs.pidTPCMaxHadrons && + std::abs(nSigmaTPCKa) > trackConfigs.pidTPCMaxHadrons && + std::abs(nSigmaTPCPr) > trackConfigs.pidTPCMaxHadrons) { + return false; + } } } // Other hadron species rejection - for (auto pid : {o2::track::PID::Kaon, o2::track::PID::Proton}) { + for (const auto& pid : {o2::track::PID::Kaon, o2::track::PID::Proton}) { auto nSigmaTPC = aod::pidutils::tpcNSigma(pid, track); if (std::abs(nSigmaTPC) < trackConfigs.pidTPCMaxHadrons) { @@ -683,35 +1183,44 @@ struct PionTrackProducer { template bool selectionPion(const T& track) { - if (!track.isGlobalTrackWoDCA()) + if (!track.isGlobalTrackWoDCA()) { return false; + } - if (track.itsNCls() < trackConfigs.minITSnCls) + if (track.itsNCls() < trackConfigs.minITSnCls) { return false; - if (track.tpcNClsFound() < trackConfigs.minTPCnClsFound) + } + if (track.tpcNClsFound() < trackConfigs.minTPCnClsFound) { return false; + } - if (track.pt() < trackConfigs.cMinPionPtcut) + if (track.pt() < trackConfigs.cMinPionPtcut) { return false; + } - if (std::abs(track.dcaXY()) > trackConfigs.cMaxDCArToPVPion->at(0) + (trackConfigs.cMaxDCArToPVPion->at(1) / std::pow(track.pt(), trackConfigs.cMaxDCArToPVPion->at(2)))) + if (std::abs(track.dcaXY()) > trackConfigs.cMaxDCArToPVPion->at(0) + (trackConfigs.cMaxDCArToPVPion->at(1) / std::pow(track.pt(), trackConfigs.cMaxDCArToPVPion->at(2)))) { return false; + } if (trackConfigs.cfgIsDCAzParameterized) { - if (std::abs(track.dcaZ()) > trackConfigs.cMaxDCAzToPVPion->at(0) + (trackConfigs.cMaxDCAzToPVPion->at(1) / std::pow(track.pt(), trackConfigs.cMaxDCAzToPVPion->at(2)))) + if (std::abs(track.dcaZ()) > trackConfigs.cMaxDCAzToPVPion->at(0) + (trackConfigs.cMaxDCAzToPVPion->at(1) / std::pow(track.pt(), trackConfigs.cMaxDCAzToPVPion->at(2)))) { return false; + } } else { - if (std::abs(track.dcaZ()) > trackConfigs.cMaxDCAzToPVcut) + if (std::abs(track.dcaZ()) > trackConfigs.cMaxDCAzToPVcut) { return false; + } } - if (trackConfigs.cfgIsTOFChecked && track.pt() >= trackConfigs.tofPIDThreshold && !track.hasTOF()) + if (trackConfigs.cfgIsTOFChecked && track.pt() >= trackConfigs.tofPIDThreshold && !track.hasTOF()) { return false; + } /*if (analysisMode == 1 && !pidSelectionPion(track)) return false;*/ - if (!pidHypothesesRejection(track)) + if (!pidHypothesesRejection(track)) { return false; + } /* if (analysisMode == 1) { @@ -722,8 +1231,9 @@ struct PionTrackProducer { } */ - if (std::abs(track.rapidity(massPi)) > trackConfigs.cfgYAcceptance) + if (std::abs(track.rapidity(MassPi)) > trackConfigs.cfgYAcceptance) { return false; + } return true; } @@ -732,13 +1242,14 @@ struct PionTrackProducer { void processData(FilteredSelCollisions::iterator const& collision, FullTracks const& fullTracks) { for (const auto& track : fullTracks) { - if (!selectionPion(track)) + if (!selectionPion(track)) { continue; + } histos.fill(HIST("h3PionTPCnSigma"), collision.centFT0M(), track.pt(), track.tpcNSigmaPi()); histos.fill(HIST("h3PionTOFnSigma"), collision.centFT0M(), track.pt(), track.tofNSigmaPi()); - pionTracksData(collision.globalIndex(), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt(), track.rapidity(massPi), track.phi(), track.hasTOF()); + pionTracksData(collision.globalIndex(), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt(), track.rapidity(MassPi), track.phi(), track.hasTOF()); } } @@ -748,14 +1259,17 @@ struct PionTrackProducer { void processMCReco(FilteredSimCollisions::iterator const& collision, FullMCTracks const& fullTracks, aod::McParticles const& mcParticles) { for (const auto& track : fullTracks) { - if (!selectionPion(track)) + if (!selectionPion(track)) { continue; - if (!track.has_mcParticle()) + } + if (!track.has_mcParticle()) { continue; + } const auto trackMcParticle = mcParticles.rawIteratorAt(track.mcParticleId()); - if (std::abs(trackMcParticle.pdgCode()) != PDG_t::kPiPlus) + if (std::abs(trackMcParticle.pdgCode()) != PDG_t::kPiPlus) { continue; + } if (trackMcParticle.isPhysicalPrimary()) { histos.fill(HIST("h2RecMCDCAxyPrimPi"), track.pt(), track.dcaXY()); @@ -768,7 +1282,7 @@ struct PionTrackProducer { continue; } - pionTracksMcReco(collision.globalIndex(), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt(), track.rapidity(massPi), track.phi(), track.hasTOF()); + pionTracksMcReco(collision.globalIndex(), track.tpcNSigmaPi(), track.tofNSigmaPi(), track.pt(), track.rapidity(MassPi), track.phi(), track.hasTOF()); } } @@ -806,6 +1320,8 @@ struct EventSelectionProducer { Configurable maxMPhiSignal{"maxMPhiSignal", 1.029f, "Upper limits on Phi mass for signal extraction"}; } phiConfigs; + Configurable withTrueGenPhi{"withTrueGenPhi", false, "Require the presence of a true generated phi meson in the event"}; + // Defining the type of the collisions for data and MC using SelCollisions = soa::Join; using SimCollisions = soa::Join; @@ -816,7 +1332,7 @@ struct EventSelectionProducer { using FullMCTracks = soa::Join; // Necessary service to flag INEL>0 events in GenMC - Service pdgDB; + Service pdgDB{}; void init(InitContext&) { @@ -862,14 +1378,17 @@ struct EventSelectionProducer { if constexpr (!isMC) { // data event histos.fill(HIST("hEventSelectionData"), 0); // all collisions - if (!collision.sel8()) + if (!collision.sel8()) { return false; + } histos.fill(HIST("hEventSelectionData"), 1); // sel8 collisions - if (std::abs(collision.posZ()) >= cutZVertex) + if (std::abs(collision.posZ()) >= cutZVertex) { return false; + } histos.fill(HIST("hEventSelectionData"), 2); // vertex-Z selected - if (!collision.isInelGt0()) + if (!collision.isInelGt0()) { return false; + } histos.fill(HIST("hEventSelectionData"), 3); // INEL>0 collisions multPercentile = collision.centFT0M(); @@ -877,23 +1396,29 @@ struct EventSelectionProducer { static_assert(!std::is_same_v, "Need to set MC_T to MCCollisions for isMC = true"); histos.fill(HIST("hEventSelectionMC"), 0); // all collisions - if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; + } histos.fill(HIST("hEventSelectionMC"), 1); // kIsTriggerTVX collisions - if (!collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + if (!collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; + } histos.fill(HIST("hEventSelectionMC"), 2); // kNoTimeFrameBorder collisions - if (cfgiskNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + if (cfgiskNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; + } histos.fill(HIST("hEventSelectionMC"), 3); // kNoITSROFrameBorder collisions (by default not requested by the selection) - if (std::abs(collision.posZ()) > cutZVertex) + if (std::abs(collision.posZ()) > cutZVertex) { return false; + } histos.fill(HIST("hEventSelectionMC"), 4); // vertex-Z selected - if (!collision.isInelGt0()) + if (!collision.isInelGt0()) { return false; + } histos.fill(HIST("hEventSelectionMC"), 5); // INEL>0 collisions - if (!collision.has_mcCollision()) + if (!collision.has_mcCollision()) { return false; + } histos.fill(HIST("hEventSelectionMC"), 6); // with at least a gen collision const auto& mcCollision = collision.template mcCollision_as(); @@ -911,23 +1436,30 @@ struct EventSelectionProducer { template bool eventHasPhi(const T1& collision, const T2& phiCandidates) { - uint16_t nPhi{0}; + if (withTrueGenPhi) { + if (phiCandidates.size() < 1) { + return false; + } + } else { + uint16_t nPhi{0}; - for (const auto& phiCand : phiCandidates) { + for (const auto& phiCand : phiCandidates) { + if (phiCand.inMassRegion(phiConfigs.minMPhiSignal, phiConfigs.maxMPhiSignal)) { + nPhi++; + } - if (phiCand.inMassRegion(phiConfigs.minMPhiSignal, phiConfigs.maxMPhiSignal)) - nPhi++; + // histos.fill(HIST("hEta"), track1.eta()); + // histos.fill(HIST("hNsigmaKaonTPC"), track1.tpcInnerParam(), track1.tpcNSigmaKa()); + // histos.fill(HIST("hNsigmaKaonTOF"), track1.tpcInnerParam(), track1.tofNSigmaKa()); + // histos.fill(HIST("h2DauTracksPhiDCAxy"), track1.pt(), track1.dcaXY()); + // histos.fill(HIST("h2DauTracksPhiDCAz"), track1.pt(), track1.dcaZ()); + } - // histos.fill(HIST("hEta"), track1.eta()); - // histos.fill(HIST("hNsigmaKaonTPC"), track1.tpcInnerParam(), track1.tpcNSigmaKa()); - // histos.fill(HIST("hNsigmaKaonTOF"), track1.tpcInnerParam(), track1.tofNSigmaKa()); - // histos.fill(HIST("h2DauTracksPhiDCAxy"), track1.pt(), track1.dcaXY()); - // histos.fill(HIST("h2DauTracksPhiDCAz"), track1.pt(), track1.dcaZ()); + if (nPhi == 0) { + return false; + } } - if (nPhi == 0) - return false; - float multPercentile{0.0f}; if constexpr (!isMC) { @@ -1010,7 +1542,8 @@ struct EventSelectionProducer { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc), - adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx index 62e7b0f3790..cb51f93b6d3 100644 --- a/PWGLF/TableProducer/Strangeness/sigma0builder.cxx +++ b/PWGLF/TableProducer/Strangeness/sigma0builder.cxx @@ -9,16 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// This is a task that employs the standard V0 tables and attempts to combine -// two V0s into a Sigma0 -> Lambda + gamma candidate. -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* -// Sigma0 builder task -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* -// -// Comments, questions, complaints, suggestions? -// Please write to: -// gianni.shigeru.setoue.liveraro@cern.ch -// + +/// \file sigma0builder.cxx +/// \brief This is a task that employs the standard V0 tables and attempts to combine two V0s into a Sigma0 -> Lambda + gamma candidate. +/// \author Gianni Shigeru Setoue Liveraro +/// \author Oussama Benchikhi #include "PWGEM/PhotonMeson/Utils/MCUtilities.h" #include "PWGJE/DataModel/EMCALClusters.h" @@ -30,20 +25,26 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" +#include "Common/Tools/StandardCCDBLoader.h" #include #include #include +#include +#include #include #include #include #include +#include #include #include #include #include #include #include +#include +#include #include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) #include @@ -77,7 +78,10 @@ static const std::vector DirList = {"V0BeforeSel", "PhotonSel", "La static const std::vector DirList2 = {"EMCalPhotonBeforeSel", "EMCalPhotonSel"}; struct sigma0builder { + o2::common::StandardCCDBLoaderConfigurables standardCCDBLoaderConfigurables; + o2::common::StandardCCDBLoader ccdbLoader; Service ccdb; + int mRunNumber = 0; ctpRateFetcher rateFetcher; //___________________________________________________ @@ -109,6 +113,15 @@ struct sigma0builder { Produces pi0Gens; // Generated pi0s Produces pi0GenCollRefs; // references collisions from pi0Gens + //__________________________________________________ + // Xi0 specific + Produces xi0cores; // xi0 candidates info for analysis + Produces xi0collRefs; // references to straCollision + Produces xi0Indices; // references to V0Cores + Produces xi0mccores; // Reco xi0 MC properties + Produces xi0mccollRefs; // references to straMCCollision + Produces xi0mcIndices; // references to V0MCCores + //__________________________________________________ // pack track quality but separte also afterburner // dynamic range: 0-31 @@ -121,6 +134,9 @@ struct sigma0builder { // Histogram registry HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Species selection + Configurable doLambdaStar{"doLambdaStar", false, "Build Lambda(1520) instead of Sigma0"}; + Configurable fFillV03DPositionHistos{"fFillV03DPositionHistos", false, "Fill XYZ histo for Photons and Lambdas."}; Configurable fFillNoSelV0Histos{"fFillNoSelV0Histos", false, "Fill QA histos for input V0s."}; Configurable fFillSelPhotonHistos{"fFillSelPhotonHistos", true, "Fill QA histos for sel photons."}; @@ -177,6 +193,7 @@ struct sigma0builder { Configurable fillPi0Tables{"fillPi0Tables", false, "fill pi0 tables for QA"}; Configurable fillSigma0Tables{"fillSigma0Tables", true, "fill sigma0 tables for analysis"}; Configurable fillKStarTables{"fillKStarTables", true, "fill kstar tables for analysis"}; + Configurable fillXi0Tables{"fillXi0Tables", false, "fill xi0 tables for analysis"}; // For ML Selection Configurable useMLScores{"useMLScores", false, "use ML scores to select candidates"}; @@ -282,8 +299,22 @@ struct sigma0builder { Configurable KShortRejectPosITSafterburner{"KShortRejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; Configurable KShortRejectNegITSafterburner{"KShortRejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; Configurable KShortArmenterosCoefficient{"KShortArmenterosCoefficient", 0.2, "Armenteros-Podolanski coefficient to reject lambdas"}; + Configurable KShortMaxTPCNSigmas{"KShortMaxTPCNSigmas", 1e+9, "Max |TPC NSigma| (pion hypothesis) for K0S daughters"}; } kshortSelections; + // Xi0 criteria: + struct : ConfigurableGroup { + std::string prefix = "cascadeSelections"; // JSON group name + Configurable maxPi0Rapidity{"maxPi0Rapidity", 1.0, "Maximum rapidity of pi0"}; + Configurable massPi0Window{"massPi0Window", 0.115, "Maximum pi0 mass (GeV/c)"}; + Configurable dcaPi0daughters{"dcaPi0daughters", 1., "Maximum DCA between pi0 daughters"}; + Configurable radiusPi0{"radiusPi0", 2.5, "Max DCA V0 Daughters (cm)"}; + Configurable dcaPi0ToPV{"dcaPi0ToPV", 0.0, "Minimum Pi0 DCA to PV(cm)"}; + Configurable dcaCascadeDaughters{"dcaCascadeDaughters", 1., "Maximum DCA between cascade daughters"}; + Configurable radiusCascade{"radiusCascade", 2.5, "Minimum cascade decay radius (cm)"}; + Configurable cosPACascade{"cosPACascade", 0.95, "Min cascade CosPA"}; + } cascadeSelections; + // KStar criteria: Configurable KStarWindow{"KStarWindow", 0.1, "Mass window around expected (in GeV/c2)"}; Configurable KStarMaxRap{"KStarMaxRap", 0.8, "Max kstar rapidity"}; @@ -307,46 +338,74 @@ struct sigma0builder { Configurable mc_rapidityMax{"mc_rapidityMax", 0.5, "Max generated particle rapidity"}; } genSelections; - // Axis - // base properties - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for analysis"}; - ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Centrality"}; - ConfigurableAxis axisNch{"axisNch", {300, 0.0f, 3000.0f}, "N_{ch}"}; - - // Invariant Mass - ConfigurableAxis axisSigmaMass{"axisSigmaMass", {500, 1.10f, 1.30f}, "M_{#Sigma^{0}} (GeV/c^{2})"}; - ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, "M_{#Lambda} (GeV/c^{2})"}; - ConfigurableAxis axisPhotonMass{"axisPhotonMass", {200, 0.0f, 0.3f}, "M_{#Gamma}"}; - ConfigurableAxis axisK0SMass{"axisK0SMass", {200, 0.4f, 0.6f}, "M_{K^{0}}"}; - ConfigurableAxis axisKStarMass{"axisKStarMass", {500, 0.6f, 1.6f}, "M_{K^{*}} (GeV/c^{2})"}; - - // AP plot axes - ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "V0 AP alpha"}; - ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 0.5f}, "V0 AP alpha"}; - - // topological variable QA axes - ConfigurableAxis axisTPCrows{"axisTPCrows", {160, 0.0f, 160.0f}, "N TPC rows"}; - ConfigurableAxis axisNCls{"axisNCls", {8, -0.5, 7.5}, "NCls"}; - ConfigurableAxis axisTPCNSigma{"axisTPCNSigma", {40, -10, 10}, "TPC NSigma"}; - ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {500, 0.0f, 50.0f}, "DCA (cm)"}; - ConfigurableAxis axisXY{"axisXY", {120, -120.0f, 120.0f}, "XY axis"}; - ConfigurableAxis axisZ{"axisZ", {120, -120.0f, 120.0f}, "V0 Z position (cm)"}; - ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; - ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; - ConfigurableAxis axisRadius{"axisRadius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; - ConfigurableAxis axisPhi{"axisPhi", {200, 0, 2 * o2::constants::math::PI}, "Phi for photons"}; - ConfigurableAxis axisPA{"axisPA", {100, 0.0f, 1}, "Pointing angle"}; - ConfigurableAxis axisRapidity{"axisRapidity", {100, -2.0f, 2.0f}, "Rapidity"}; - ConfigurableAxis axisCandSel{"axisCandSel", {15, 0.5f, +15.5f}, "Candidate Selection"}; - ConfigurableAxis axisIRBinning{"axisIRBinning", {151, -10, 1500}, "Binning for the interaction rate (kHz)"}; - ConfigurableAxis axisLifetime{"axisLifetime", {200, 0, 50}, "Lifetime"}; - - // EMCal-specifc - ConfigurableAxis axisClrDefinition{"axisClrDefinition", {51, -0.5, 50.5}, "Cluster Definition"}; - ConfigurableAxis axisClrNCells{"axisClrNCells", {25, 0.0, 25}, "N cells per cluster"}; - ConfigurableAxis axisClrEnergy{"axisClrEnergy", {400, 0.0, 10}, "Energy per cluster"}; - ConfigurableAxis axisClrTime{"axisClrTime", {300, -30.0, 30.0}, "cluster time (ns)"}; - ConfigurableAxis axisClrShape{"axisClrShape", {100, 0.0, 1.0}, "cluster shape"}; + struct : ConfigurableGroup { + // base properties + std::string prefix = "axisConfig"; // JSON group name + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for analysis"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Centrality"}; + ConfigurableAxis axisNch{"axisNch", {300, 0.0f, 3000.0f}, "N_{ch}"}; + + // Invariant Mass + ConfigurableAxis axisSigmaMass{"axisSigmaMass", {500, 1.10f, 1.30f}, "#it{M}_{#Sigma^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, "#it{M}_{#Lambda} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPhotonMass{"axisPhotonMass", {200, 0.0f, 0.3f}, "#it{M}_{#Gamma} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisK0SMass{"axisK0SMass", {200, 0.4f, 0.6f}, "#it{M}_{K^{0}_{S}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisKStarMass{"axisKStarMass", {500, 0.6f, 1.6f}, "#it{M}_{K^{*}} (GeV/#it{c}}^{2})"}; + ConfigurableAxis axisPi0Mass{"axisPi0Mass", {100, 0.08f, 0.18f}, "#it{M}_{#pi^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisXi0Mass{"axisXi0Mass", {100, 1.27f, 1.37f}, "#it{M}_{#Xi^{0}} (GeV/#it{c}^{2})"}; + + // AP plot axes + ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "V0 AP alpha"}; + ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 0.5f}, "V0 AP alpha"}; + + // topological variable QA axes + ConfigurableAxis axisTPCrows{"axisTPCrows", {160, 0.0f, 160.0f}, "N TPC rows"}; + ConfigurableAxis axisNCls{"axisNCls", {8, -0.5, 7.5}, "NCls"}; + ConfigurableAxis axisTPCNSigma{"axisTPCNSigma", {40, -10, 10}, "TPC NSigma"}; + ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {500, 0.0f, 50.0f}, "DCA (cm)"}; + ConfigurableAxis axisXY{"axisXY", {120, -120.0f, 120.0f}, "XY axis"}; + ConfigurableAxis axisZ{"axisZ", {120, -120.0f, 120.0f}, "V0 Z position (cm)"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; + ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; + ConfigurableAxis axisRadius{"axisRadius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; + ConfigurableAxis axisPhi{"axisPhi", {200, 0, 2 * o2::constants::math::PI}, "Phi for photons"}; + ConfigurableAxis axisPA{"axisPA", {100, 0.0f, 1}, "Pointing angle"}; + ConfigurableAxis axisRapidity{"axisRapidity", {100, -2.0f, 2.0f}, "Rapidity"}; + ConfigurableAxis axisCandSel{"axisCandSel", {15, 0.5f, +15.5f}, "Candidate Selection"}; + ConfigurableAxis axisIRBinning{"axisIRBinning", {151, -10, 1500}, "Binning for the interaction rate (kHz)"}; + ConfigurableAxis axisLifetime{"axisLifetime", {200, 0, 50}, "Lifetime"}; + + // EMCal-specifc + ConfigurableAxis axisClrDefinition{"axisClrDefinition", {51, -0.5, 50.5}, "Cluster Definition"}; + ConfigurableAxis axisClrNCells{"axisClrNCells", {25, 0.0, 25}, "N cells per cluster"}; + ConfigurableAxis axisClrEnergy{"axisClrEnergy", {400, 0.0, 10}, "Energy per cluster"}; + ConfigurableAxis axisClrTime{"axisClrTime", {300, -30.0, 30.0}, "cluster time (ns)"}; + ConfigurableAxis axisClrShape{"axisClrShape", {100, 0.0, 1.0}, "cluster shape"}; + } axisConfig; + + struct : ConfigurableGroup { + std::string prefix = "fitterConfiguration"; // JSON group name + Configurable propagateToPCA{"propagateToPCA", true, "Propagate to PCA?"}; + Configurable minParamChange{"minParamChange", 4., "Stop minimization iterations if largest change of any X is smaller than this."}; + Configurable minRelChi2Change{"minRelChi2Change", 0.9, "Stop iterations is chi2/chi2old > this"}; + Configurable maxR{"maxR", 200., "Don't consider as a seed (circles intersection) if its R exceeds this"}; + Configurable maxDXYIni{"maxDXYIni", 4.0f, "Don't consider as a seed (circles intersection) if XY distance exceeds this"}; + Configurable maxDZIni{"maxDZIni", 1e9, "Don't consider as a seed (circles intersection) if Z distance exceeds this"}; + Configurable maxChi2{"maxChi2", 1e9, "Max dca from prongs to vertex"}; + Configurable useAbsDCA{"useAbsDCA", true, "Use abs dca minimization"}; + Configurable useWeightedFinalPCA{"useWeightedFinalPCA", false, "Max generated particle rapidity"}; + } fitterConfiguration; + + // CCDB options + struct : ConfigurableGroup { + // manual + Configurable useCustomRunNumber{"ccdbConfigurations.useCustomRunNumber", false, "Use custom run number"}; + Configurable customRunNumber{"ccdbConfigurations.customRunNumber", 544122, "Manually set the run number "}; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + } ccdbConfigurations; + + o2::vertexing::DCAFitterN<2> fitter2Prongs; // 2-prong o2 dca fitter + o2::vertexing::DCAFitterN<3> fitter3Prongs; // 3-prong o2 dca fitter void init(InitContext const&) { @@ -364,11 +423,11 @@ struct sigma0builder { } // setting CCDB service - ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); - ccdb->setFatalWhenNull(false); + ccdb->setLocalObjectValidityChecking(); + ccdb->setURL(ccdbConfigurations.ccdburl.value); - histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisConfig.axisCentrality}); if (eventSelections.fUseEventSelection) { histos.add("hEventSelection", "hEventSelection", kTH1D, {{21, -0.5f, +20.5f}}); @@ -400,8 +459,8 @@ struct sigma0builder { if (fGetIR) { histos.add("GeneralQA/hRunNumberNegativeIR", "", kTH1D, {{1, 0., 1.}}); - histos.add("GeneralQA/hInteractionRate", "hInteractionRate", kTH1D, {axisIRBinning}); - histos.add("GeneralQA/hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {axisCentrality, axisIRBinning}); + histos.add("GeneralQA/hInteractionRate", "hInteractionRate", kTH1D, {axisConfig.axisIRBinning}); + histos.add("GeneralQA/hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {axisConfig.axisCentrality, axisConfig.axisIRBinning}); } } @@ -415,51 +474,51 @@ struct sigma0builder { continue; } - histos.add(histodir + "/hpT", "hpT", kTH1D, {axisPt}); + histos.add(histodir + "/hpT", "hpT", kTH1D, {axisConfig.axisPt}); histos.add(histodir + "/hV0Type", "hV0Type", kTH1D, {{8, 0.5f, 8.5f}}); - histos.add(histodir + "/hNegEta", "hNegEta", kTH1D, {axisRapidity}); - histos.add(histodir + "/hPosEta", "hPosEta", kTH1D, {axisRapidity}); - histos.add(histodir + "/hDCANegToPV", "hDCANegToPV", kTH1D, {axisDCAtoPV}); - histos.add(histodir + "/hDCAPosToPV", "hDCAPosToPV", kTH1D, {axisDCAtoPV}); - histos.add(histodir + "/hDCADau", "hnDCADau", kTH1D, {axisDCAdau}); - histos.add(histodir + "/hRadius", "hnRadius", kTH1D, {axisRadius}); - histos.add(histodir + "/hZ", "hZ", kTH1D, {axisZ}); - histos.add(histodir + "/hCosPA", "hCosPA", kTH1D, {axisCosPA}); - histos.add(histodir + "/hPhi", "hPhi", kTH1D, {axisPhi}); - histos.add(histodir + "/hPosTPCCR", "hPosTPCCR", kTH1D, {axisTPCrows}); - histos.add(histodir + "/hNegTPCCR", "hNegTPCCR", kTH1D, {axisTPCrows}); - histos.add(histodir + "/hPosITSNCls", "hPosITSNCls", kTH1D, {axisNCls}); - histos.add(histodir + "/hNegITSNCls", "hNegITSNCls", kTH1D, {axisNCls}); - histos.add(histodir + "/hPosTPCNSigmaEl", "hPosTPCNSigmaEl", kTH1D, {axisTPCNSigma}); - histos.add(histodir + "/hNegTPCNSigmaEl", "hNegTPCNSigmaEl", kTH1D, {axisTPCNSigma}); - histos.add(histodir + "/hPosTPCNSigmaPi", "hPosTPCNSigmaPi", kTH1D, {axisTPCNSigma}); - histos.add(histodir + "/hNegTPCNSigmaPi", "hNegTPCNSigmaPi", kTH1D, {axisTPCNSigma}); - histos.add(histodir + "/hPosTPCNSigmaPr", "hPosTPCNSigmaPr", kTH1D, {axisTPCNSigma}); - histos.add(histodir + "/hNegTPCNSigmaPr", "hNegTPCNSigmaPr", kTH1D, {axisTPCNSigma}); - histos.add(histodir + "/h2dArmenteros", "h2dArmenteros", kTH2D, {axisAPAlpha, axisAPQt}); - - histos.add(histodir + "/hPhotonY", "hPhotonY", kTH1D, {axisRapidity}); - histos.add(histodir + "/hPhotonMass", "hPhotonMass", kTH1D, {axisPhotonMass}); - histos.add(histodir + "/h2dMassPhotonVsK0S", "h2dMassPhotonVsK0S", kTH2D, {axisPhotonMass, axisK0SMass}); - histos.add(histodir + "/h2dMassPhotonVsLambda", "h2dMassPhotonVsLambda", kTH2D, {axisPhotonMass, axisLambdaMass}); - histos.add(histodir + "/hLambdaLifeTime", "hLambdaLifeTime", kTH1D, {axisLifetime}); - histos.add(histodir + "/hLambdaY", "hLambdaY", kTH1D, {axisRapidity}); - histos.add(histodir + "/hLambdaMass", "hLambdaMass", kTH1D, {axisLambdaMass}); - histos.add(histodir + "/hALambdaMass", "hALambdaMass", kTH1D, {axisLambdaMass}); - histos.add(histodir + "/h2dMassLambdaVsK0S", "h2dMassLambdaVsK0S", kTH2D, {axisLambdaMass, axisK0SMass}); - histos.add(histodir + "/h2dMassLambdaVsGamma", "h2dMassLambdaVsGamma", kTH2D, {axisLambdaMass, axisPhotonMass}); - histos.add(histodir + "/hKShortLifeTime", "hKShortLifeTime", kTH1D, {axisLifetime}); - histos.add(histodir + "/hKShortY", "hKShortY", kTH1D, {axisRapidity}); - histos.add(histodir + "/hKShortMass", "hKShortMass", kTH1D, {axisK0SMass}); - histos.add(histodir + "/h2dMassK0SvsLambda", "h2dMassK0SvsLambda", kTH2D, {axisK0SMass, axisLambdaMass}); - histos.add(histodir + "/h2dMassK0SVsGamma", "h2dMassK0SVsGamma", kTH2D, {axisK0SMass, axisPhotonMass}); + histos.add(histodir + "/hNegEta", "hNegEta", kTH1D, {axisConfig.axisRapidity}); + histos.add(histodir + "/hPosEta", "hPosEta", kTH1D, {axisConfig.axisRapidity}); + histos.add(histodir + "/hDCANegToPV", "hDCANegToPV", kTH1D, {axisConfig.axisDCAtoPV}); + histos.add(histodir + "/hDCAPosToPV", "hDCAPosToPV", kTH1D, {axisConfig.axisDCAtoPV}); + histos.add(histodir + "/hDCADau", "hnDCADau", kTH1D, {axisConfig.axisDCAdau}); + histos.add(histodir + "/hRadius", "hnRadius", kTH1D, {axisConfig.axisRadius}); + histos.add(histodir + "/hZ", "hZ", kTH1D, {axisConfig.axisZ}); + histos.add(histodir + "/hCosPA", "hCosPA", kTH1D, {axisConfig.axisCosPA}); + histos.add(histodir + "/hPhi", "hPhi", kTH1D, {axisConfig.axisPhi}); + histos.add(histodir + "/hPosTPCCR", "hPosTPCCR", kTH1D, {axisConfig.axisTPCrows}); + histos.add(histodir + "/hNegTPCCR", "hNegTPCCR", kTH1D, {axisConfig.axisTPCrows}); + histos.add(histodir + "/hPosITSNCls", "hPosITSNCls", kTH1D, {axisConfig.axisNCls}); + histos.add(histodir + "/hNegITSNCls", "hNegITSNCls", kTH1D, {axisConfig.axisNCls}); + histos.add(histodir + "/hPosTPCNSigmaEl", "hPosTPCNSigmaEl", kTH1D, {axisConfig.axisTPCNSigma}); + histos.add(histodir + "/hNegTPCNSigmaEl", "hNegTPCNSigmaEl", kTH1D, {axisConfig.axisTPCNSigma}); + histos.add(histodir + "/hPosTPCNSigmaPi", "hPosTPCNSigmaPi", kTH1D, {axisConfig.axisTPCNSigma}); + histos.add(histodir + "/hNegTPCNSigmaPi", "hNegTPCNSigmaPi", kTH1D, {axisConfig.axisTPCNSigma}); + histos.add(histodir + "/hPosTPCNSigmaPr", "hPosTPCNSigmaPr", kTH1D, {axisConfig.axisTPCNSigma}); + histos.add(histodir + "/hNegTPCNSigmaPr", "hNegTPCNSigmaPr", kTH1D, {axisConfig.axisTPCNSigma}); + histos.add(histodir + "/h2dArmenteros", "h2dArmenteros", kTH2D, {axisConfig.axisAPAlpha, axisConfig.axisAPQt}); + + histos.add(histodir + "/hPhotonY", "hPhotonY", kTH1D, {axisConfig.axisRapidity}); + histos.add(histodir + "/hPhotonMass", "hPhotonMass", kTH1D, {axisConfig.axisPhotonMass}); + histos.add(histodir + "/h2dMassPhotonVsK0S", "h2dMassPhotonVsK0S", kTH2D, {axisConfig.axisPhotonMass, axisConfig.axisK0SMass}); + histos.add(histodir + "/h2dMassPhotonVsLambda", "h2dMassPhotonVsLambda", kTH2D, {axisConfig.axisPhotonMass, axisConfig.axisLambdaMass}); + histos.add(histodir + "/hLambdaLifeTime", "hLambdaLifeTime", kTH1D, {axisConfig.axisLifetime}); + histos.add(histodir + "/hLambdaY", "hLambdaY", kTH1D, {axisConfig.axisRapidity}); + histos.add(histodir + "/hLambdaMass", "hLambdaMass", kTH1D, {axisConfig.axisLambdaMass}); + histos.add(histodir + "/hALambdaMass", "hALambdaMass", kTH1D, {axisConfig.axisLambdaMass}); + histos.add(histodir + "/h2dMassLambdaVsK0S", "h2dMassLambdaVsK0S", kTH2D, {axisConfig.axisLambdaMass, axisConfig.axisK0SMass}); + histos.add(histodir + "/h2dMassLambdaVsGamma", "h2dMassLambdaVsGamma", kTH2D, {axisConfig.axisLambdaMass, axisConfig.axisPhotonMass}); + histos.add(histodir + "/hKShortLifeTime", "hKShortLifeTime", kTH1D, {axisConfig.axisLifetime}); + histos.add(histodir + "/hKShortY", "hKShortY", kTH1D, {axisConfig.axisRapidity}); + histos.add(histodir + "/hKShortMass", "hKShortMass", kTH1D, {axisConfig.axisK0SMass}); + histos.add(histodir + "/h2dMassK0SvsLambda", "h2dMassK0SvsLambda", kTH2D, {axisConfig.axisK0SMass, axisConfig.axisLambdaMass}); + histos.add(histodir + "/h2dMassK0SVsGamma", "h2dMassK0SVsGamma", kTH2D, {axisConfig.axisK0SMass, axisConfig.axisPhotonMass}); if (histodir != "V0BeforeSel" && fFillV03DPositionHistos) // We dont want this for all reco v0s! - histos.add(histodir + "/h3dV0XYZ", "h3dV0XYZ", kTH3D, {axisXY, axisXY, axisZ}); + histos.add(histodir + "/h3dV0XYZ", "h3dV0XYZ", kTH3D, {axisConfig.axisXY, axisConfig.axisXY, axisConfig.axisZ}); } if (fUsePCMPhoton || doprocessPCMVsEMCalQA) { - histos.add("PhotonSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisCandSel}); + histos.add("PhotonSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(1, "No Sel"); histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(2, "Mass"); histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(3, "Y"); @@ -471,11 +530,12 @@ struct sigma0builder { histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(9, "Z"); histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(10, "CosPA"); histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(11, "Phi"); - histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(12, "TPCCR"); - histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(13, "TPC NSigma"); + histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(12, "Armenteros"); + histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(13, "TPCCR"); + histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(14, "TPC NSigma"); if (doprocessPCMVsEMCalQA) { - histos.add("EMCalPhotonSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisCandSel}); + histos.add("EMCalPhotonSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); histos.get(HIST("EMCalPhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(1, "No Sel"); histos.get(HIST("EMCalPhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(2, "Definition"); histos.get(HIST("EMCalPhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(3, "MinCell"); @@ -488,16 +548,16 @@ struct sigma0builder { } else { for (const auto& histodir : DirList2) { - histos.add(histodir + "/hDefinition", "hDefinition", kTH1D, {axisClrDefinition}); - histos.add(histodir + "/h2dNCells", "h2dNCells", kTH2D, {axisPt, axisClrNCells}); - histos.add(histodir + "/h2dEnergy", "h2dEnergy", kTH2D, {axisPt, axisClrEnergy}); - histos.add(histodir + "/h2dEtaVsPhi", "h2dEtaVsPhi", kTH2D, {axisRapidity, axisPhi}); - histos.add(histodir + "/h2dTime", "h2dTime", kTH2D, {axisPt, axisClrTime}); + histos.add(histodir + "/hDefinition", "hDefinition", kTH1D, {axisConfig.axisClrDefinition}); + histos.add(histodir + "/h2dNCells", "h2dNCells", kTH2D, {axisConfig.axisPt, axisConfig.axisClrNCells}); + histos.add(histodir + "/h2dEnergy", "h2dEnergy", kTH2D, {axisConfig.axisPt, axisConfig.axisClrEnergy}); + histos.add(histodir + "/h2dEtaVsPhi", "h2dEtaVsPhi", kTH2D, {axisConfig.axisRapidity, axisConfig.axisPhi}); + histos.add(histodir + "/h2dTime", "h2dTime", kTH2D, {axisConfig.axisPt, axisConfig.axisClrTime}); histos.add(histodir + "/hExotic", "hExotic", kTH1D, {{2, -0.5f, 1.5f}}); - histos.add(histodir + "/h2dShape", "h2dShape", kTH2D, {axisPt, axisClrShape}); + histos.add(histodir + "/h2dShape", "h2dShape", kTH2D, {axisConfig.axisPt, axisConfig.axisClrShape}); } - histos.add("EMCalPhotonSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisCandSel}); + histos.add("EMCalPhotonSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); histos.get(HIST("EMCalPhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(1, "No Sel"); histos.get(HIST("EMCalPhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(2, "Definition"); histos.get(HIST("EMCalPhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(3, "MinCell"); @@ -508,7 +568,7 @@ struct sigma0builder { histos.get(HIST("EMCalPhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(8, "Shape"); } - histos.add("LambdaSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisCandSel}); + histos.add("LambdaSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); histos.get(HIST("LambdaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(1, "No Sel"); histos.get(HIST("LambdaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(2, "Mass"); histos.get(HIST("LambdaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(3, "Y"); @@ -524,7 +584,7 @@ struct sigma0builder { histos.get(HIST("LambdaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(13, "ITSNCls"); histos.get(HIST("LambdaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(14, "Lifetime"); - histos.add("KShortSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisCandSel}); + histos.add("KShortSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(1, "No Sel"); histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(2, "Mass"); histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(3, "Y"); @@ -539,40 +599,47 @@ struct sigma0builder { histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(12, "TPCCR"); histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(13, "ITSNCls"); histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(14, "Lifetime"); + histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(15, "TPC NSigma"); if (doprocessRealData || doprocessRealDataWithTOF || doprocessRealDataWithEMCal || doprocessMonteCarlo || doprocessMonteCarloWithTOF || doprocessMonteCarloWithEMCal) { histos.add("SigmaSel/hSigma0DauDeltaIndex", "hSigma0DauDeltaIndex", kTH1F, {{100, -49.5f, 50.5f}}); - histos.add("SigmaSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisCandSel}); + histos.add("SigmaSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); histos.get(HIST("SigmaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(1, "No Sel"); histos.get(HIST("SigmaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(2, "Sigma Mass Window"); histos.get(HIST("SigmaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(3, "Sigma Y Window"); - histos.add("SigmaSel/hSigmaMassBeforeSel", "hSigmaMassBeforeSel", kTH1F, {axisSigmaMass}); - histos.add("SigmaSel/hSigmaMassSelected", "hSigmaMassSelected", kTH1F, {axisSigmaMass}); + histos.add("SigmaSel/hSigmaMassBeforeSel", "hSigmaMassBeforeSel", kTH1F, {axisConfig.axisSigmaMass}); + histos.add("SigmaSel/hSigmaMassSelected", "hSigmaMassSelected", kTH1F, {axisConfig.axisSigmaMass}); - histos.add("KStarSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisCandSel}); + histos.add("KStarSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); histos.get(HIST("KStarSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(1, "No Sel"); histos.get(HIST("KStarSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(2, "KStar Mass Window"); histos.get(HIST("KStarSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(3, "KStar Y Window"); - histos.add("KStarSel/hKStarMassSelected", "hKStarMassSelected", kTH1F, {axisKStarMass}); + histos.add("KStarSel/hKStarMassSelected", "hKStarMassSelected", kTH1F, {axisConfig.axisKStarMass}); + } + + if (fillXi0Tables) { + histos.add("Xi0Sel/hPi0Mass", "hPi0Mass", kTH1F, {axisConfig.axisPi0Mass}); + histos.add("Xi0Sel/hXi0Mass", "hXi0Mass", kTH1F, {axisConfig.axisXi0Mass}); } if (doAssocStudy && (doprocessMonteCarlo || doprocessMonteCarloWithTOF)) { - histos.add("V0AssoQA/h2dIRVsPt_TrueGamma", "h2dIRVsPt_TrueGamma", kTH2F, {axisIRBinning, axisPt}); - histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueGamma", "h3dPAVsIRVsPt_TrueGamma", kTH3F, {axisPA, axisIRBinning, axisPt}); - histos.add("V0AssoQA/h2dIRVsPt_TrueGamma_BadCollAssig", "h2dIRVsPt_TrueGamma_BadCollAssig", kTH2F, {axisIRBinning, axisPt}); - histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueGamma_BadCollAssig", "h3dPAVsIRVsPt_TrueGamma_BadCollAssig", kTH3F, {axisPA, axisIRBinning, axisPt}); + histos.add("V0AssoQA/h2dIRVsPt_TrueGamma", "h2dIRVsPt_TrueGamma", kTH2F, {axisConfig.axisIRBinning, axisConfig.axisPt}); + histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueGamma", "h3dPAVsIRVsPt_TrueGamma", kTH3F, {axisConfig.axisPA, axisConfig.axisIRBinning, axisConfig.axisPt}); + histos.add("V0AssoQA/h2dIRVsPt_TrueGamma_BadCollAssig", "h2dIRVsPt_TrueGamma_BadCollAssig", kTH2F, {axisConfig.axisIRBinning, axisConfig.axisPt}); + histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueGamma_BadCollAssig", "h3dPAVsIRVsPt_TrueGamma_BadCollAssig", kTH3F, {axisConfig.axisPA, axisConfig.axisIRBinning, axisConfig.axisPt}); - histos.add("V0AssoQA/h2dIRVsPt_TrueLambda", "h2dIRVsPt_TrueLambda", kTH2F, {axisIRBinning, axisPt}); - histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueLambda", "h3dPAVsIRVsPt_TrueLambda", kTH3F, {axisPA, axisIRBinning, axisPt}); - histos.add("V0AssoQA/h2dIRVsPt_TrueLambda_BadCollAssig", "h2dIRVsPt_TrueLambda_BadCollAssig", kTH2F, {axisIRBinning, axisPt}); - histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueLambda_BadCollAssig", "h3dPAVsIRVsPt_TrueLambda_BadCollAssig", kTH3F, {axisPA, axisIRBinning, axisPt}); + histos.add("V0AssoQA/h2dIRVsPt_TrueLambda", "h2dIRVsPt_TrueLambda", kTH2F, {axisConfig.axisIRBinning, axisConfig.axisPt}); + histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueLambda", "h3dPAVsIRVsPt_TrueLambda", kTH3F, {axisConfig.axisPA, axisConfig.axisIRBinning, axisConfig.axisPt}); + histos.add("V0AssoQA/h2dIRVsPt_TrueLambda_BadCollAssig", "h2dIRVsPt_TrueLambda_BadCollAssig", kTH2F, {axisConfig.axisIRBinning, axisConfig.axisPt}); + histos.add("V0AssoQA/h3dPAVsIRVsPt_TrueLambda_BadCollAssig", "h3dPAVsIRVsPt_TrueLambda_BadCollAssig", kTH3F, {axisConfig.axisPA, axisConfig.axisIRBinning, axisConfig.axisPt}); } // MC if (doprocessMonteCarlo || doprocessMonteCarloWithTOF || doprocessMonteCarloWithEMCal) { histos.add("MCQA/h2dPhotonNMothersVsPDG", "h2dPhotonNMothersVsPDG", kTHnSparseD, {{10, -0.5f, +9.5f}, {10001, -5000.5f, +5000.5f}}); + histos.add("MCQA/h2dTrueDaughtersMatrix", "h2dTrueDaughtersMatrix", kTHnSparseD, {{10001, -5000.5f, +5000.5f}, {10001, -5000.5f, +5000.5f}}); histos.add("MCQA/h2dPhotonNMothersVsMCProcess", "h2dPhotonNMothersVsMCProcess", kTH2D, {{10, -0.5f, +9.5f}, {50, -0.5f, 49.5f}}); histos.add("MCQA/hPhotonMotherSize", "hPhotonMotherSize", kTH1D, {{10, -0.5f, +9.5f}}); histos.add("MCQA/hPhotonMCProcess", "hPhotonMCProcess", kTH1D, {{50, -0.5f, 49.5f}}); @@ -600,33 +667,33 @@ struct sigma0builder { } if (doprocessPCMVsEMCalQA) { - histos.add("PhotonMCQA/hPCMPhotonMCpT", "hPCMPhotonMCpT", kTH1D, {axisPt}); - histos.add("PhotonMCQA/h2dPCMPhotonMCpTResolution", "h2dPCMPhotonMCpTResolution", kTH2D, {axisPt, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/hPCMSigma0PhotonMCpT", "hPCMSigma0PhotonMCpT", kTH1D, {axisPt}); - histos.add("PhotonMCQA/h2dPCMSigma0PhotonMCpTResolution", "h2dPCMSigma0PhotonMCpTResolution", kTH2D, {axisPt, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/hPCMPhotonMCpT", "hPCMPhotonMCpT", kTH1D, {axisConfig.axisPt}); + histos.add("PhotonMCQA/h2dPCMPhotonMCpTResolution", "h2dPCMPhotonMCpTResolution", kTH2D, {axisConfig.axisPt, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/hPCMSigma0PhotonMCpT", "hPCMSigma0PhotonMCpT", kTH1D, {axisConfig.axisPt}); + histos.add("PhotonMCQA/h2dPCMSigma0PhotonMCpTResolution", "h2dPCMSigma0PhotonMCpTResolution", kTH2D, {axisConfig.axisPt, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/hEMCalPhotonMCpT", "hEMCalPhotonMCpT", kTH1D, {axisPt}); - histos.add("PhotonMCQA/h2dEMCalPhotonMCpTResolution", "h2dEMCalPhotonMCpTResolution", kTH2D, {axisPt, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalPhotonMCEnergyResolution", "h2dEMCalPhotonMCEnergyResolution", kTH2D, {axisClrEnergy, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalPhotonMCEtaResolution", "h2dEMCalPhotonMCEtaResolution", kTH2D, {axisRapidity, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalPhotonMCPhiResolution", "h2dEMCalPhotonMCPhiResolution", kTH2D, {axisPhi, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalPhotonMCFractionEnergy", "h2dEMCalPhotonMCFractionEnergy", kTH2D, {axisPt, {100, -1.0f, 1.0f}}); + histos.add("PhotonMCQA/hEMCalPhotonMCpT", "hEMCalPhotonMCpT", kTH1D, {axisConfig.axisPt}); + histos.add("PhotonMCQA/h2dEMCalPhotonMCpTResolution", "h2dEMCalPhotonMCpTResolution", kTH2D, {axisConfig.axisPt, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalPhotonMCEnergyResolution", "h2dEMCalPhotonMCEnergyResolution", kTH2D, {axisConfig.axisClrEnergy, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalPhotonMCEtaResolution", "h2dEMCalPhotonMCEtaResolution", kTH2D, {axisConfig.axisRapidity, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalPhotonMCPhiResolution", "h2dEMCalPhotonMCPhiResolution", kTH2D, {axisConfig.axisPhi, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalPhotonMCFractionEnergy", "h2dEMCalPhotonMCFractionEnergy", kTH2D, {axisConfig.axisPt, {100, -1.0f, 1.0f}}); - histos.add("PhotonMCQA/hEMCalSigma0PhotonMCpT", "hEMCalSigma0PhotonMCpT", kTH1D, {axisPt}); - histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCpTResolution", "h2dEMCalSigma0PhotonMCpTResolution", kTH2D, {axisPt, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCEnergyResolution", "h2dEMCalSigma0PhotonMCEnergyResolution", kTH2D, {axisClrEnergy, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCEtaResolution", "h2dEMCalSigma0PhotonMCEtaResolution", kTH2D, {axisRapidity, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCPhiResolution", "h2dEMCalSigma0PhotonMCPhiResolution", kTH2D, {axisPhi, {100, -2.0f, 2.0f}}); - histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCFractionEnergy", "h2dEMCalSigma0PhotonMCFractionEnergy", kTH2D, {axisPt, {100, -1.0f, 1.0f}}); + histos.add("PhotonMCQA/hEMCalSigma0PhotonMCpT", "hEMCalSigma0PhotonMCpT", kTH1D, {axisConfig.axisPt}); + histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCpTResolution", "h2dEMCalSigma0PhotonMCpTResolution", kTH2D, {axisConfig.axisPt, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCEnergyResolution", "h2dEMCalSigma0PhotonMCEnergyResolution", kTH2D, {axisConfig.axisClrEnergy, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCEtaResolution", "h2dEMCalSigma0PhotonMCEtaResolution", kTH2D, {axisConfig.axisRapidity, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCPhiResolution", "h2dEMCalSigma0PhotonMCPhiResolution", kTH2D, {axisConfig.axisPhi, {100, -2.0f, 2.0f}}); + histos.add("PhotonMCQA/h2dEMCalSigma0PhotonMCFractionEnergy", "h2dEMCalSigma0PhotonMCFractionEnergy", kTH2D, {axisConfig.axisPt, {100, -1.0f, 1.0f}}); - histos.add("PhotonMCQA/hGenPhoton", "hGenPhoton", kTH1D, {axisPt}); - histos.add("PhotonMCQA/hGenSigma0Photon", "hGenSigma0Photon", kTH1D, {axisPt}); + histos.add("PhotonMCQA/hGenPhoton", "hGenPhoton", kTH1D, {axisConfig.axisPt}); + histos.add("PhotonMCQA/hGenSigma0Photon", "hGenSigma0Photon", kTH1D, {axisConfig.axisPt}); } if (doprocessGeneratedRun3 && genSelections.doQA) { // Pi0s - histos.add("GenQA/hGenPi0", "hGenPi0", kTH1D, {axisPt}); + histos.add("GenQA/hGenPi0", "hGenPi0", kTH1D, {axisConfig.axisPt}); auto hPrimaryPi0s = histos.add("GenQA/hPrimaryPi0s", "hPrimaryPi0s", kTH1D, {{2, -0.5f, 1.5f}}); hPrimaryPi0s->GetXaxis()->SetBinLabel(1, "All Pi0s"); @@ -643,16 +710,16 @@ struct sigma0builder { // ______________________________________________________ // Sigma0s - histos.add("GenQA/hGenSigma0", "hGenSigma0", kTH1D, {axisPt}); - histos.add("GenQA/hGenAntiSigma0", "hGenAntiSigma0", kTH1D, {axisPt}); + histos.add("GenQA/hGenSigma0", "hGenSigma0", kTH1D, {axisConfig.axisPt}); + histos.add("GenQA/hGenAntiSigma0", "hGenAntiSigma0", kTH1D, {axisConfig.axisPt}); - histos.add("GenQA/h3dGenSigma0_pTMap", "h3dGenSigma0_pTMap", kTH3D, {axisPt, axisPt, axisPt}); - histos.add("GenQA/h3dGenASigma0_pTMap", "h3dGenASigma0_pTMap", kTH3D, {axisPt, axisPt, axisPt}); + histos.add("GenQA/h3dGenSigma0_pTMap", "h3dGenSigma0_pTMap", kTH3D, {axisConfig.axisPt, axisConfig.axisPt, axisConfig.axisPt}); + histos.add("GenQA/h3dGenASigma0_pTMap", "h3dGenASigma0_pTMap", kTH3D, {axisConfig.axisPt, axisConfig.axisPt, axisConfig.axisPt}); - histos.add("GenQA/h2dGenSigma0xy_Generator", "hGenSigma0xy_Generator", kTH2D, {axisXY, axisXY}); - histos.add("GenQA/h2dGenSigma0xy_Transport", "hGenSigma0xy_Transport", kTH2D, {axisXY, axisXY}); - histos.add("GenQA/hGenSigma0Radius_Generator", "hGenSigma0Radius_Generator", kTH1D, {axisRadius}); - histos.add("GenQA/hGenSigma0Radius_Transport", "hGenSigma0Radius_Transport", kTH1D, {axisRadius}); + histos.add("GenQA/h2dGenSigma0xy_Generator", "hGenSigma0xy_Generator", kTH2D, {axisConfig.axisXY, axisConfig.axisXY}); + histos.add("GenQA/h2dGenSigma0xy_Transport", "hGenSigma0xy_Transport", kTH2D, {axisConfig.axisXY, axisConfig.axisXY}); + histos.add("GenQA/hGenSigma0Radius_Generator", "hGenSigma0Radius_Generator", kTH1D, {axisConfig.axisRadius}); + histos.add("GenQA/hGenSigma0Radius_Transport", "hGenSigma0Radius_Transport", kTH1D, {axisConfig.axisRadius}); histos.add("GenQA/h2dSigma0MCSourceVsPDGMother", "h2dSigma0MCSourceVsPDGMother", kTHnSparseD, {{2, -0.5f, 1.5f}, {10001, -5000.5f, +5000.5f}}); histos.add("GenQA/h2dSigma0NDaughtersVsPDG", "h2dSigma0NDaughtersVsPDG", kTHnSparseD, {{10, -0.5f, +9.5f}, {10001, -5000.5f, +5000.5f}}); @@ -678,12 +745,12 @@ struct sigma0builder { // ______________________________________________________ // KStar - histos.add("GenQA/hGenKStar", "hGenKStar", kTH1D, {axisPt}); + histos.add("GenQA/hGenKStar", "hGenKStar", kTH1D, {axisConfig.axisPt}); - histos.add("GenQA/h2dGenKStarxy_Generator", "hGenKStarxy_Generator", kTH2D, {axisXY, axisXY}); - histos.add("GenQA/h2dGenKStarxy_Transport", "hGenKStarxy_Transport", kTH2D, {axisXY, axisXY}); - histos.add("GenQA/hGenKStarRadius_Generator", "hGenKStarRadius_Generator", kTH1D, {axisRadius}); - histos.add("GenQA/hGenKStarRadius_Transport", "hGenKStarRadius_Transport", kTH1D, {axisRadius}); + histos.add("GenQA/h2dGenKStarxy_Generator", "hGenKStarxy_Generator", kTH2D, {axisConfig.axisXY, axisConfig.axisXY}); + histos.add("GenQA/h2dGenKStarxy_Transport", "hGenKStarxy_Transport", kTH2D, {axisConfig.axisXY, axisConfig.axisXY}); + histos.add("GenQA/hGenKStarRadius_Generator", "hGenKStarRadius_Generator", kTH1D, {axisConfig.axisRadius}); + histos.add("GenQA/hGenKStarRadius_Transport", "hGenKStarRadius_Transport", kTH1D, {axisConfig.axisRadius}); histos.add("GenQA/h2dKStarMCSourceVsPDGMother", "h2dKStarMCSourceVsPDGMother", kTHnSparseD, {{2, -0.5f, 1.5f}, {10001, -5000.5f, +5000.5f}}); histos.add("GenQA/h2dKStarNDaughtersVsPDG", "h2dKStarNDaughtersVsPDG", kTHnSparseD, {{10, -0.5f, +9.5f}, {10001, -5000.5f, +5000.5f}}); @@ -709,86 +776,134 @@ struct sigma0builder { if (doprocessV0QA || doprocessV0MCQA) { // Event selection: - histos.add("V0QA/hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); + histos.add("V0QA/hEventCentrality", "hEventCentrality", kTH1D, {axisConfig.axisCentrality}); // Photon part: - histos.add("V0QA/h3dPhotonMass", "h3dPhotonMass", kTH3D, {axisCentrality, axisPt, axisPhotonMass}); - histos.add("V0QA/h3dYPhotonMass", "h3dYPhotonMass", kTH3D, {axisRapidity, axisPt, axisPhotonMass}); - histos.add("V0QA/h3dYPhotonRadius", "h3dYPhotonRadius", kTH3D, {axisRapidity, axisPt, axisRadius}); + histos.add("V0QA/h3dPhotonMass", "h3dPhotonMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisPhotonMass}); + histos.add("V0QA/h3dYPhotonMass", "h3dYPhotonMass", kTH3D, {axisConfig.axisRapidity, axisConfig.axisPt, axisConfig.axisPhotonMass}); + histos.add("V0QA/h3dYPhotonRadius", "h3dYPhotonRadius", kTH3D, {axisConfig.axisRapidity, axisConfig.axisPt, axisConfig.axisRadius}); - histos.add("V0QA/h3dTruePhotonMass", "h3dTruePhotonMass", kTH3D, {axisCentrality, axisPt, axisPhotonMass}); - histos.add("V0QA/h2dTrueSigma0PhotonMass", "h2dTrueSigma0PhotonMass", kTH2D, {axisPt, axisPhotonMass}); - histos.add("V0QA/h2dTrueKStarPhotonMass", "h2dTrueKStarPhotonMass", kTH2D, {axisPt, axisPhotonMass}); + histos.add("V0QA/h3dTruePhotonMass", "h3dTruePhotonMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisPhotonMass}); + histos.add("V0QA/h2dTrueSigma0PhotonMass", "h2dTrueSigma0PhotonMass", kTH2D, {axisConfig.axisPt, axisConfig.axisPhotonMass}); + histos.add("V0QA/h2dTrueKStarPhotonMass", "h2dTrueKStarPhotonMass", kTH2D, {axisConfig.axisPt, axisConfig.axisPhotonMass}); // Lambda part: - histos.add("V0QA/h3dLambdaMass", "h3dLambdaMass", kTH3D, {axisCentrality, axisPt, axisLambdaMass}); - histos.add("V0QA/h3dTrueLambdaMass", "h3dTrueLambdaMass", kTH3D, {axisCentrality, axisPt, axisLambdaMass}); - histos.add("V0QA/h3dYLambdaMass", "h3dYLambdaMass", kTH3D, {axisRapidity, axisPt, axisLambdaMass}); - histos.add("V0QA/h3dYRLambdaMass", "h3dYRLambdaMass", kTH3D, {axisRapidity, axisRadius, axisLambdaMass}); + histos.add("V0QA/h3dLambdaMass", "h3dLambdaMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisLambdaMass}); + histos.add("V0QA/h3dTrueLambdaMass", "h3dTrueLambdaMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisLambdaMass}); + histos.add("V0QA/h3dYLambdaMass", "h3dYLambdaMass", kTH3D, {axisConfig.axisRapidity, axisConfig.axisPt, axisConfig.axisLambdaMass}); + histos.add("V0QA/h3dYRLambdaMass", "h3dYRLambdaMass", kTH3D, {axisConfig.axisRapidity, axisConfig.axisRadius, axisConfig.axisLambdaMass}); - histos.add("V0QA/h2dTrueSigma0LambdaMass", "h2dTrueSigma0LambdaMass", kTH2D, {axisPt, axisLambdaMass}); + histos.add("V0QA/h2dTrueSigma0LambdaMass", "h2dTrueSigma0LambdaMass", kTH2D, {axisConfig.axisPt, axisConfig.axisLambdaMass}); // AntiLambda part: - histos.add("V0QA/h3dALambdaMass", "h3dALambdaMass", kTH3D, {axisCentrality, axisPt, axisLambdaMass}); - histos.add("V0QA/h3dTrueALambdaMass", "h3dTrueALambdaMass", kTH3D, {axisCentrality, axisPt, axisLambdaMass}); - histos.add("V0QA/h3dYALambdaMass", "h3dYALambdaMass", kTH3D, {axisRapidity, axisPt, axisLambdaMass}); - histos.add("V0QA/h3dYRALambdaMass", "h3dYRALambdaMass", kTH3D, {axisRapidity, axisRadius, axisLambdaMass}); + histos.add("V0QA/h3dALambdaMass", "h3dALambdaMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisLambdaMass}); + histos.add("V0QA/h3dTrueALambdaMass", "h3dTrueALambdaMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisLambdaMass}); + histos.add("V0QA/h3dYALambdaMass", "h3dYALambdaMass", kTH3D, {axisConfig.axisRapidity, axisConfig.axisPt, axisConfig.axisLambdaMass}); + histos.add("V0QA/h3dYRALambdaMass", "h3dYRALambdaMass", kTH3D, {axisConfig.axisRapidity, axisConfig.axisRadius, axisConfig.axisLambdaMass}); - histos.add("V0QA/h2dTrueASigma0ALambdaMass", "h2dTrueASigma0ALambdaMass", kTH2D, {axisPt, axisLambdaMass}); + histos.add("V0QA/h2dTrueASigma0ALambdaMass", "h2dTrueASigma0ALambdaMass", kTH2D, {axisConfig.axisPt, axisConfig.axisLambdaMass}); // KShort part: - histos.add("V0QA/h3dKShortMass", "h3dKShortMass", kTH3D, {axisCentrality, axisPt, axisK0SMass}); - histos.add("V0QA/h3dTrueKShortMass", "h3dTrueKShortMass", kTH3D, {axisCentrality, axisPt, axisK0SMass}); - histos.add("V0QA/h3dYKShortMass", "h3dYKShortMass", kTH3D, {axisRapidity, axisPt, axisK0SMass}); - histos.add("V0QA/h3dYRKShortMass", "h3dYRKShortMass", kTH3D, {axisRapidity, axisRadius, axisK0SMass}); - histos.add("V0QA/h2dTrueKStarKShortMass", "h2dTrueKStarKShortMass", kTH2D, {axisPt, axisK0SMass}); + histos.add("V0QA/h3dKShortMass", "h3dKShortMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisK0SMass}); + histos.add("V0QA/h3dTrueKShortMass", "h3dTrueKShortMass", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisK0SMass}); + histos.add("V0QA/h3dYKShortMass", "h3dYKShortMass", kTH3D, {axisConfig.axisRapidity, axisConfig.axisPt, axisConfig.axisK0SMass}); + histos.add("V0QA/h3dYRKShortMass", "h3dYRKShortMass", kTH3D, {axisConfig.axisRapidity, axisConfig.axisRadius, axisConfig.axisK0SMass}); + histos.add("V0QA/h2dTrueKStarKShortMass", "h2dTrueKStarKShortMass", kTH2D, {axisConfig.axisPt, axisConfig.axisK0SMass}); } if (doprocessV0Generated) { - histos.add("V0QA/hGenEvents", "hGenEvents", kTH2D, {{axisNch}, {2, -0.5f, +1.5f}}); + histos.add("V0QA/hGenEvents", "hGenEvents", kTH2D, {{axisConfig.axisNch}, {2, -0.5f, +1.5f}}); histos.get(HIST("V0QA/hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); histos.get(HIST("V0QA/hGenEvents"))->GetYaxis()->SetBinLabel(2, "Gen. with at least 1 rec. events"); histos.add("V0QA/hGenEventCentrality", "hGenEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); - histos.add("V0QA/hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", kTH2D, {axisCentrality, {50, -0.5f, 49.5f}}); - histos.add("V0QA/hCentralityVsNcoll_afterEvSel", "hCentralityVsNcoll_afterEvSel", kTH2D, {axisCentrality, {50, -0.5f, 49.5f}}); + histos.add("V0QA/hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", kTH2D, {axisConfig.axisCentrality, {50, -0.5f, 49.5f}}); + histos.add("V0QA/hCentralityVsNcoll_afterEvSel", "hCentralityVsNcoll_afterEvSel", kTH2D, {axisConfig.axisCentrality, {50, -0.5f, 49.5f}}); - histos.add("V0QA/hCentralityVsMultMC", "hCentralityVsMultMC", kTH2D, {{101, 0.0f, 101.0f}, axisNch}); + histos.add("V0QA/hCentralityVsMultMC", "hCentralityVsMultMC", kTH2D, {{101, 0.0f, 101.0f}, axisConfig.axisNch}); histos.add("V0QA/hEventPVzMC", "hEventPVzMC", kTH1D, {{100, -20.0f, +20.0f}}); histos.add("V0QA/hCentralityVsPVzMC", "hCentralityVsPVzMC", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); - histos.add("V0QA/h2dGenPhoton", "h2dGenPhoton", kTH2D, {axisCentrality, axisPt}); - histos.add("V0QA/h2dGenLambda", "h2dGenLambda", kTH2D, {axisCentrality, axisPt}); - histos.add("V0QA/h2dGenAntiLambda", "h2dGenAntiLambda", kTH2D, {axisCentrality, axisPt}); + histos.add("V0QA/h2dGenPhoton", "h2dGenPhoton", kTH2D, {axisConfig.axisCentrality, axisConfig.axisPt}); + histos.add("V0QA/h2dGenLambda", "h2dGenLambda", kTH2D, {axisConfig.axisCentrality, axisConfig.axisPt}); + histos.add("V0QA/h2dGenAntiLambda", "h2dGenAntiLambda", kTH2D, {axisConfig.axisCentrality, axisConfig.axisPt}); - histos.add("V0QA/h2dGenPhotonVsMultMC_RecoedEvt", "h2dGenPhotonVsMultMC_RecoedEvt", kTH2D, {axisNch, axisPt}); - histos.add("V0QA/h2dGenLambdaVsMultMC_RecoedEvt", "h2dGenLambdaVsMultMC_RecoedEvt", kTH2D, {axisNch, axisPt}); - histos.add("V0QA/h2dGenAntiLambdaVsMultMC_RecoedEvt", "h2dGenAntiLambdaVsMultMC_RecoedEvt", kTH2D, {axisNch, axisPt}); + histos.add("V0QA/h2dGenPhotonVsMultMC_RecoedEvt", "h2dGenPhotonVsMultMC_RecoedEvt", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); + histos.add("V0QA/h2dGenLambdaVsMultMC_RecoedEvt", "h2dGenLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); + histos.add("V0QA/h2dGenAntiLambdaVsMultMC_RecoedEvt", "h2dGenAntiLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); - histos.add("V0QA/h2dGenPhotonVsMultMC", "h2dGenPhotonVsMultMC", kTH2D, {axisNch, axisPt}); - histos.add("V0QA/h2dGenLambdaVsMultMC", "h2dGenLambdaVsMultMC", kTH2D, {axisNch, axisPt}); - histos.add("V0QA/h2dGenAntiLambdaVsMultMC", "h2dGenAntiLambdaVsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("V0QA/h2dGenPhotonVsMultMC", "h2dGenPhotonVsMultMC", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); + histos.add("V0QA/h2dGenLambdaVsMultMC", "h2dGenLambdaVsMultMC", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); + histos.add("V0QA/h2dGenAntiLambdaVsMultMC", "h2dGenAntiLambdaVsMultMC", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); - histos.add("V0QA/h2dGenKShort", "h2dGenKShort", kTH2D, {axisCentrality, axisPt}); + histos.add("V0QA/h2dGenKShort", "h2dGenKShort", kTH2D, {axisConfig.axisCentrality, axisConfig.axisPt}); - histos.add("V0QA/h2dGenKShortVsMultMC_RecoedEvt", "h2dGenKShortVsMultMC_RecoedEvt", kTH2D, {axisNch, axisPt}); + histos.add("V0QA/h2dGenKShortVsMultMC_RecoedEvt", "h2dGenKShortVsMultMC_RecoedEvt", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); - histos.add("V0QA/h2dGenKShortVsMultMC", "h2dGenKShortVsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("V0QA/h2dGenKShortVsMultMC", "h2dGenKShortVsMultMC", kTH2D, {axisConfig.axisNch, axisConfig.axisPt}); } + fitter2Prongs.setPropagateToPCA(fitterConfiguration.propagateToPCA); + fitter2Prongs.setMaxR(fitterConfiguration.maxR); + fitter2Prongs.setMinParamChange(fitterConfiguration.minParamChange); + fitter2Prongs.setMinRelChi2Change(fitterConfiguration.minRelChi2Change); + fitter2Prongs.setMaxDZIni(fitterConfiguration.maxDZIni); + fitter2Prongs.setMaxDXYIni(fitterConfiguration.maxDXYIni); + fitter2Prongs.setMaxChi2(fitterConfiguration.maxChi2); + fitter2Prongs.setUseAbsDCA(fitterConfiguration.useAbsDCA); + fitter2Prongs.setWeightedFinalPCA(fitterConfiguration.useWeightedFinalPCA); + + fitter3Prongs.setPropagateToPCA(fitterConfiguration.propagateToPCA); + fitter3Prongs.setMaxR(fitterConfiguration.maxR); + fitter3Prongs.setMinParamChange(fitterConfiguration.minParamChange); + fitter3Prongs.setMinRelChi2Change(fitterConfiguration.minRelChi2Change); + fitter3Prongs.setMaxDZIni(fitterConfiguration.maxDZIni); + fitter3Prongs.setMaxDXYIni(fitterConfiguration.maxDXYIni); + fitter3Prongs.setMaxChi2(fitterConfiguration.maxChi2); + fitter3Prongs.setUseAbsDCA(fitterConfiguration.useAbsDCA); + fitter3Prongs.setWeightedFinalPCA(fitterConfiguration.useWeightedFinalPCA); + + fitter2Prongs.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + fitter3Prongs.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + + // mag field has to be set later + fitter2Prongs.setBz(-999.9f); // will NOT make sense if not changed + fitter3Prongs.setBz(-999.9f); // will NOT make sense if not changed + // inspect histogram sizes, please histos.print(); } + template // TCollision should be of the type: soa::Join::iterator or so + void initCCDB(TCollision const& collision) + { + if (mRunNumber == collision.runNumber() || (ccdbConfigurations.useCustomRunNumber && mRunNumber == ccdbConfigurations.customRunNumber)) { + return; + } + + mRunNumber = ccdbConfigurations.useCustomRunNumber ? ccdbConfigurations.customRunNumber : collision.runNumber(); + + ccdbLoader.initCCDB(standardCCDBLoaderConfigurables, ccdb, mRunNumber); + + auto magneticField = o2::base::Propagator::Instance()->getNominalBz(); + // Set magnetic field value once known + fitter2Prongs.setBz(magneticField); + fitter3Prongs.setBz(magneticField); + } + // ______________________________________________________ // Struct to store V0Pair properties struct V0PairTopoInfo { - float X = -999.f; - float Y = -999.f; - float Z = -999.f; - float DCADau = -999.f; - float CosPA = -1.f; + std::array position{-999.f, -999.f, -999.f}; + float dcaPi0ToPV = 999.f; + float daughterDCA = 999.f; + float cosPA = -1.f; + float pi0Mass = 999.f; + float pi0Y = 999.f; + + int v01Index = 0; // index to de-reference V0Cores table + int v02Index = 0; // index to de-reference V0Cores table }; // ______________________________________________________ @@ -807,6 +922,10 @@ struct sigma0builder { int V02PDGCode = 0; int V01PDGCodeMother = 0; int V02PDGCodeMother = 0; + int V01PDGCodeGrandMother = 0; + int V02PDGCodeGrandMother = 0; + int V01GlobalIndexGrandMother = 0; + int V02GlobalIndexGrandMother = 0; int V0PairPDGCode = 0; int V0PairPDGCodeMother = 0; int V0PairMCProcess = -1; @@ -832,6 +951,8 @@ struct sigma0builder { bool IsSigma0 = false; bool IsAntiSigma0 = false; bool IsKStar = false; + bool IsLambdaStar = false; + bool IsAntiLambdaStar = false; bool IsProducedByGenerator = false; bool IsSterile = false; int MCProcess = -1; @@ -845,6 +966,79 @@ struct sigma0builder { float MCvy = 999.f; }; + // ______________________________________________________ + // Struct to store V0Pair properties + struct Xi0Info { + std::array gamma1Momentum{999.f, 999.f, 999.f}; + std::array gamma2Momentum{999.f, 999.f, 999.f}; + std::array lambdaMomentum{999.f, 999.f, 999.f}; + std::array pi0Position{0., 0., 0.}; + std::array cascadePosition{0., 0., 0.}; + float xi0Mass = 999.f; + float dcaPi0ToPV = 999.f; + float pi0DaughterDCA = 999.f; + float cascadeDaughterDCA = 999.f; + float cascadeDCAxy = 999.f; + float cascadeDCAz = 999.f; + + int collisionId = 0; + int gamma1Index = 0; // index to de-reference V0Cores table + int gamma2Index = 0; // index to de-reference V0Cores table + int lambdaIndex = 0; // index to de-reference V0Cores table + + std::array covariance{0.}; + }; + + struct Xi0MCInfo { + int gamma1Index = 0; // index to de-reference V0MCCores table + int gamma2Index = 0; // index to de-reference V0MCCores table + int lambdaIndex = 0; // index to de-reference V0MCCores table + + // Basic kinematic info + float gamma1MCpx = -999.f; + float gamma1MCpy = -999.f; + float gamma1MCpz = -999.f; + float gamma2MCpx = -999.f; + float gamma2MCpy = -999.f; + float gamma2MCpz = -999.f; + float lambdaMCpx = -999.f; + float lambdaMCpy = -999.f; + float lambdaMCpz = -999.f; + + // MC association info + bool gamma1IsPhysicalPrimary = false; + bool gamma2IsPhysicalPrimary = false; + bool lambdaIsPhysicalPrimary = false; + int gamma1PDGCodePos = 0; + int gamma1PDGCodeNeg = 0; + int gamma2PDGCodePos = 0; + int gamma2PDGCodeNeg = 0; + int lambdaPDGCodePos = 0; + int lambdaPDGCodeNeg = 0; + int gamma1PDGCode = 0; + int gamma2PDGCode = 0; + int lambdaPDGCode = 0; + int gamma1PDGCodeMother = 0; + int gamma2PDGCodeMother = 0; + int lambdaPDGCodeMother = 0; + + int pi0PDGCode = 0; + int pi0MCProcess = 0; + bool pi0IsPhysicalPrimary = false; + float pi0X = -999.f; + float pi0Y = -999.f; + float pi0Z = -999.f; + + int xi0PDGCode = 0; + int xi0MCProcess = 0; + bool xi0IsPhysicalPrimary = false; + float xi0X = -999.f; + float xi0Y = -999.f; + float xi0Z = -999.f; + + int xi0PDGCodeMother = 0; + }; + template V0PairTopoInfo propagateV0PairToDCA(TV01 const& v01, TV02 const& v02) { @@ -875,18 +1069,18 @@ struct sigma0builder { ROOT::Math::XYZVector PCA = 0.5 * (pointOn1 + pointOn2); // Calculate properties and fill struct - info.DCADau = (cross.Mag2() > 0) ? std::abs(posdiff.Dot(cross)) / cross.R() : 999.f; - info.CosPA = v01momentumNorm.Dot(v02momentumNorm); + info.daughterDCA = (cross.Mag2() > 0) ? std::abs(posdiff.Dot(cross)) / cross.R() : 999.f; + info.cosPA = v01momentumNorm.Dot(v02momentumNorm); float Min_threshold = 1e-5f; // Threshold to consider lines as parallel, can be tuned if (d < Min_threshold) { // Parallel or nearly parallel lines - info.X = info.Y = info.Z = 0.f; // should we use another dummy value? Perhaps 999.f? + info.position[0] = info.position[1] = info.position[2] = 0.f; // should we use another dummy value? Perhaps 999.f? return info; } - info.X = PCA.X(); - info.Y = PCA.Y(); - info.Z = PCA.Z(); + info.position[0] = PCA.X(); + info.position[1] = PCA.Y(); + info.position[2] = PCA.Z(); return info; } @@ -905,8 +1099,9 @@ struct sigma0builder { auto v02MC = v02.template v0MCCore_as>(); // Sanity check: Is V0Pair <-> Mother assignment correct? + int expectedPairPDG = doLambdaStar ? 3124 : PDG_t::kSigma0; bool fIsSigma0 = false; - if ((v01MC.pdgCode() == PDG_t::kGamma) && (v01MC.pdgCodeMother() == PDG_t::kSigma0) && (v02MC.pdgCode() == PDG_t::kLambda0) && (v02MC.pdgCodeMother() == PDG_t::kSigma0) && (v01.motherMCPartId() == v02.motherMCPartId())) + if ((v01MC.pdgCode() == PDG_t::kGamma) && (v01MC.pdgCodeMother() == expectedPairPDG) && (v02MC.pdgCode() == PDG_t::kLambda0) && (v02MC.pdgCodeMother() == expectedPairPDG) && (v01.motherMCPartId() == v02.motherMCPartId())) fIsSigma0 = true; bool fIsKStar = false; @@ -952,6 +1147,46 @@ struct sigma0builder { auto const& MCMother_v01 = MCMothersList_v01.front(); // First mother auto const& MCMother_v02 = MCMothersList_v02.front(); // First mother + // Add the grandmothers + auto const& GrandMothersList_v01 = MCMother_v01.template mothers_as(); + if (!GrandMothersList_v01.empty()) { + MCinfo.V01PDGCodeGrandMother = GrandMothersList_v01.front().pdgCode(); + MCinfo.V01GlobalIndexGrandMother = GrandMothersList_v01.front().globalIndex(); + } + + auto const& GrandMothersList_v02 = MCMother_v02.template mothers_as(); + if (!GrandMothersList_v02.empty()) { + MCinfo.V02PDGCodeGrandMother = GrandMothersList_v02.front().pdgCode(); + MCinfo.V02GlobalIndexGrandMother = GrandMothersList_v02.front().globalIndex(); + } + + // check grandmothers and fill histograms + int kShortMotherCode = 0; + int photonMotherCode = 0; + if ((std::abs(MCParticle_v01.pdgCode()) == PDG_t::kGamma) && (std::abs(MCParticle_v02.pdgCode()) == PDG_t::kK0Short) && (!fIsKStar)) { + + kShortMotherCode = MCMother_v02.pdgCode(); + + // If the KShort mother is a (anti)Kaon, use the grandmother instead + if (std::abs(kShortMotherCode) == PDG_t::kK0) { + auto const& kShortGrandMothers = MCMother_v02.template mothers_as(); + if (!kShortGrandMothers.empty()) { + kShortMotherCode = kShortGrandMothers.front().pdgCode(); + } + } + + photonMotherCode = MCMother_v01.pdgCode(); + // If the photon mother is a pi0, climb to grandmother + if (std::abs(photonMotherCode) == PDG_t::kPi0) { + auto const& photonGrandMothers = MCMother_v01.template mothers_as(); + if (!photonGrandMothers.empty()) { + photonMotherCode = photonGrandMothers.front().pdgCode(); + } + } + + histos.fill(HIST("MCQA/h2dTrueDaughtersMatrix"), kShortMotherCode, photonMotherCode); + } + if (MCMother_v01.globalIndex() == MCMother_v02.globalIndex()) { // Is it the same mother? MCinfo.fV0PairProducedByGenerator = MCMother_v01.producedByGenerator(); @@ -1017,11 +1252,11 @@ struct sigma0builder { } } // Check association correctness - if (fIsSigma0 && (MCinfo.V0PairPDGCode == PDG_t::kSigma0)) + if (fIsSigma0 && (MCinfo.V0PairPDGCode == expectedPairPDG)) histos.fill(HIST("MCQA/hSigma0MCCheck"), 1); // match - if (fIsSigma0 && !(MCinfo.V0PairPDGCode == PDG_t::kSigma0)) + if (fIsSigma0 && !(MCinfo.V0PairPDGCode == expectedPairPDG)) histos.fill(HIST("MCQA/hSigma0MCCheck"), 2); // mismatch - if (!fIsSigma0 && (MCinfo.V0PairPDGCode == PDG_t::kSigma0)) + if (!fIsSigma0 && (MCinfo.V0PairPDGCode == expectedPairPDG)) histos.fill(HIST("MCQA/hSigma0MCCheck"), 3); // mismatch // Check association correctness @@ -1197,6 +1432,105 @@ struct sigma0builder { return MCinfo; } + template + Xi0MCInfo getXi0MCInfo(TV0 const& gamma1, TV0 const& gamma2, TV0 const& lambda, TMCParticles const& mcparticles) + { + Xi0MCInfo MCinfo; + + if (!gamma1.has_v0MCCore() || !gamma2.has_v0MCCore() || !lambda.has_v0MCCore()) { + return MCinfo; + } + + auto gamma1MC = gamma1.template v0MCCore_as>(); + auto gamma2MC = gamma2.template v0MCCore_as>(); + auto lambdaMC = lambda.template v0MCCore_as>(); + + // Basic kinematic info + MCinfo.gamma1MCpx = gamma1MC.pxMC(); + MCinfo.gamma1MCpy = gamma1MC.pyMC(); + MCinfo.gamma1MCpz = gamma1MC.pzMC(); + MCinfo.gamma2MCpx = gamma2MC.pxMC(); + MCinfo.gamma2MCpy = gamma2MC.pyMC(); + MCinfo.gamma2MCpz = gamma2MC.pzMC(); + MCinfo.lambdaMCpx = lambdaMC.pxMC(); + MCinfo.lambdaMCpy = lambdaMC.pyMC(); + MCinfo.lambdaMCpz = lambdaMC.pzMC(); + + // MC association info + MCinfo.gamma1Index = gamma1MC.globalIndex(); + MCinfo.gamma2Index = gamma2MC.globalIndex(); + MCinfo.lambdaIndex = lambdaMC.globalIndex(); + MCinfo.gamma1IsPhysicalPrimary = gamma1MC.isPhysicalPrimary(); + MCinfo.gamma2IsPhysicalPrimary = gamma2MC.isPhysicalPrimary(); + MCinfo.lambdaIsPhysicalPrimary = lambdaMC.isPhysicalPrimary(); + MCinfo.gamma1PDGCodePos = gamma1MC.pdgCodePositive(); + MCinfo.gamma1PDGCodeNeg = gamma1MC.pdgCodeNegative(); + MCinfo.gamma2PDGCodePos = gamma2MC.pdgCodePositive(); + MCinfo.gamma2PDGCodeNeg = gamma2MC.pdgCodeNegative(); + MCinfo.lambdaPDGCodePos = lambdaMC.pdgCodePositive(); + MCinfo.lambdaPDGCodeNeg = lambdaMC.pdgCodeNegative(); + MCinfo.gamma1PDGCode = gamma1MC.pdgCode(); + MCinfo.gamma2PDGCode = gamma2MC.pdgCode(); + MCinfo.lambdaPDGCode = lambdaMC.pdgCode(); + + // Get corresponding entries in MCParticles table + auto MCParticle_gamma1 = mcparticles.rawIteratorAt(gamma1MC.particleIdMC()); + auto MCParticle_gamma2 = mcparticles.rawIteratorAt(gamma2MC.particleIdMC()); + auto MCParticle_lambda = mcparticles.rawIteratorAt(lambdaMC.particleIdMC()); + + if ((std::abs(MCParticle_gamma1.pdgCode()) == PDG_t::kGamma) && + (std::abs(MCParticle_gamma2.pdgCode()) == PDG_t::kGamma) && + (std::abs(MCParticle_lambda.pdgCode()) == PDG_t::kLambda0)) { + + // Get MC Mothers + auto const& MCMothersList_gamma1 = MCParticle_gamma1.template mothers_as(); + auto const& MCMothersList_gamma2 = MCParticle_gamma2.template mothers_as(); + auto const& MCMothersList_lambda = MCParticle_lambda.template mothers_as(); + + if (!MCMothersList_gamma1.empty() && !MCMothersList_gamma2.empty() && !MCMothersList_lambda.empty()) { // Are there mothers? + auto const& MCMother_gamma1 = MCMothersList_gamma1.front(); // First mother + auto const& MCMother_gamma2 = MCMothersList_gamma2.front(); // First mother + auto const& MCMother_lambda = MCMothersList_lambda.front(); // First mother + + MCinfo.gamma1PDGCodeMother = MCMother_gamma1.pdgCode(); + MCinfo.gamma2PDGCodeMother = MCMother_gamma2.pdgCode(); + MCinfo.lambdaPDGCodeMother = MCMother_lambda.pdgCode(); + + if (MCMother_gamma1.globalIndex() == MCMother_gamma2.globalIndex() && std::abs(MCinfo.gamma2PDGCodeMother) == PDG_t::kPi0) { // check that gamma1 and gamma2 have the same pi0 mother + + MCinfo.pi0PDGCode = MCMother_gamma1.pdgCode(); + MCinfo.pi0MCProcess = MCMother_gamma1.getProcess(); + MCinfo.pi0IsPhysicalPrimary = MCMother_gamma1.isPhysicalPrimary(); + MCinfo.pi0X = MCMother_gamma1.vx(); + MCinfo.pi0Y = MCMother_gamma1.vy(); + MCinfo.pi0Z = MCMother_gamma1.vz(); + + auto const& MCMothersList_pi0 = MCMother_gamma1.template mothers_as(); // get pi0 mother list + if (!MCMothersList_pi0.empty()) { // Are there mothers? + auto const& MCMother_pi0 = MCMothersList_pi0.front(); // get pi0 mother + + if (MCMother_pi0.globalIndex() == MCMother_lambda.globalIndex()) { // check that lambda and pi0 have the same mother. + MCinfo.xi0PDGCode = MCMother_pi0.pdgCode(); + MCinfo.xi0MCProcess = MCMother_pi0.getProcess(); + MCinfo.xi0IsPhysicalPrimary = MCMother_pi0.isPhysicalPrimary(); + MCinfo.xi0X = MCMother_pi0.vx(); + MCinfo.xi0Y = MCMother_pi0.vy(); + MCinfo.xi0Z = MCMother_pi0.vz(); + + auto const& v0pairmothers = MCMother_pi0.template mothers_as(); // Get mothers + if (!v0pairmothers.empty()) { + auto& v0PairMother = v0pairmothers.front(); // V0Pair mother, V0s grandmother + MCinfo.xi0PDGCodeMother = v0PairMother.pdgCode(); + } + } + } + } + } + } + + return MCinfo; + } + // ______________________________________________________ // Check whether the collision passes our collision selections // Should work with collisions, mccollisions, stracollisions and stramccollisions tables! @@ -1326,13 +1660,11 @@ struct sigma0builder { if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); - - // Fill centrality histogram after event selection - if (fillHists) + // Fill centrality histogram after event selection histos.fill(HIST("hEventCentrality"), centrality); - + } return true; } @@ -1463,7 +1795,7 @@ struct sigma0builder { else if (std::abs(v0MC.pdgCode()) == PDG_t::kLambda0) ymc = v0MC.rapidityMC(1); else if (v0MC.pdgCode() == PDG_t::kK0Short) - ymc = v0MC.rapidityMC(2); // what is the 2 here? + ymc = v0MC.rapidityMC(0); // 0 = K0 mass hypothesis (1/2 = Lambda); see LFStrangenessTables.h:829 if ((ymc < genSelections.mc_rapidityMin) || (ymc > genSelections.mc_rapidityMax)) continue; @@ -1573,13 +1905,15 @@ struct sigma0builder { // Fill with properties GenInfo.IsPrimary = mcParticle.isPhysicalPrimary(); - GenInfo.IsV0Lambda = mcParticle.pdgCode() == PDG_t::kLambda0; // 3122 - GenInfo.IsV0AntiLambda = mcParticle.pdgCode() == PDG_t::kLambda0Bar; //-3122 - GenInfo.IsV0KShort = mcParticle.pdgCode() == PDG_t::kK0Short; // 310 - GenInfo.IsPi0 = mcParticle.pdgCode() == PDG_t::kPi0; // 111; - GenInfo.IsSigma0 = mcParticle.pdgCode() == PDG_t::kSigma0; // PDG_t::kSigma0 - GenInfo.IsAntiSigma0 = mcParticle.pdgCode() == PDG_t::kSigma0Bar; //-3212 - GenInfo.IsKStar = mcParticle.pdgCode() == o2::constants::physics::Pdg::kK0Star892; // 313; + GenInfo.IsV0Lambda = mcParticle.pdgCode() == PDG_t::kLambda0; // 3122 + GenInfo.IsV0AntiLambda = mcParticle.pdgCode() == PDG_t::kLambda0Bar; //-3122 + GenInfo.IsV0KShort = std::abs(mcParticle.pdgCode()) == PDG_t::kK0Short; // 310 + GenInfo.IsPi0 = mcParticle.pdgCode() == PDG_t::kPi0; // 111; + GenInfo.IsSigma0 = mcParticle.pdgCode() == PDG_t::kSigma0; // PDG_t::kSigma0 + GenInfo.IsAntiSigma0 = mcParticle.pdgCode() == PDG_t::kSigma0Bar; //-3212 + GenInfo.IsLambdaStar = mcParticle.pdgCode() == 3124; // 102134 (PYTHIA8) + GenInfo.IsAntiLambdaStar = mcParticle.pdgCode() == -3124; // -102134 + GenInfo.IsKStar = std::abs(mcParticle.pdgCode()) == o2::constants::physics::Pdg::kK0Star892; // 313; GenInfo.IsProducedByGenerator = mcParticle.producedByGenerator(); GenInfo.MCProcess = mcParticle.getProcess(); GenInfo.MCPt = mcParticle.pt(); @@ -1590,7 +1924,7 @@ struct sigma0builder { GenInfo.MCCollId = mcParticle.mcCollisionId(); // save this reference, please // Checking decay mode if sigma0 or pi0 (it is easier here) - if (GenInfo.IsSigma0 || GenInfo.IsAntiSigma0 || GenInfo.IsPi0 || GenInfo.IsKStar) { + if (GenInfo.IsSigma0 || GenInfo.IsAntiSigma0 || GenInfo.IsPi0 || GenInfo.IsKStar || GenInfo.IsLambdaStar || GenInfo.IsAntiLambdaStar) { // This is a costly operation, so we do it only for pi0s and sigma0s auto const& daughters = mcParticle.template daughters_as(); @@ -1600,7 +1934,7 @@ struct sigma0builder { auto const& GenMothersList = mcParticle.template mothers_as(); GenInfo.PDGCodeMother = (!GenMothersList.empty()) ? GenMothersList.front().pdgCode() : 0; - if ((GenInfo.IsSigma0 || GenInfo.IsAntiSigma0) && genSelections.doQA) { + if ((doLambdaStar ? (GenInfo.IsLambdaStar || GenInfo.IsAntiLambdaStar) : (GenInfo.IsSigma0 || GenInfo.IsAntiSigma0)) && genSelections.doQA) { histos.fill(HIST("GenQA/h2dSigma0MCSourceVsPDGMother"), GenInfo.IsProducedByGenerator, GenInfo.PDGCodeMother); // Checking decay modes and getting daughter pTs @@ -1663,7 +1997,7 @@ struct sigma0builder { histos.fill(HIST("GenQA/hGenSpeciesKStar"), 0); // Checking decay mode - if (GenInfo.IsSigma0 || GenInfo.IsAntiSigma0) { + if (doLambdaStar ? (GenInfo.IsLambdaStar || GenInfo.IsAntiLambdaStar) : (GenInfo.IsSigma0 || GenInfo.IsAntiSigma0)) { histos.fill(HIST("GenQA/hSigma0NDau"), GenInfo.NDaughters); histos.fill(HIST("GenQA/h2dSigma0NDauVsProcess"), GenInfo.NDaughters, GenInfo.MCProcess); @@ -1683,7 +2017,7 @@ struct sigma0builder { histos.fill(HIST("GenQA/h2DGenSigma0TypeVsProducedByGen"), typeIndex, genIndex); // Fill histograms - if (GenInfo.IsSigma0) { + if (doLambdaStar ? GenInfo.IsLambdaStar : GenInfo.IsSigma0) { histos.fill(HIST("GenQA/hGenSpecies"), 2); histos.fill(HIST("GenQA/hGenSigma0"), GenInfo.MCPt); histos.fill(HIST("GenQA/h3dGenSigma0_pTMap"), GenInfo.MCPt, GenInfo.MCDau1Pt, GenInfo.MCDau2Pt); @@ -1692,7 +2026,7 @@ struct sigma0builder { if (GenInfo.IsPrimary) histos.fill(HIST("GenQA/hPrimarySigma0s"), 1); } - if (GenInfo.IsAntiSigma0) { + if (doLambdaStar ? GenInfo.IsAntiLambdaStar : GenInfo.IsAntiSigma0) { histos.fill(HIST("GenQA/hGenSpecies"), 3); histos.fill(HIST("GenQA/hGenAntiSigma0"), GenInfo.MCPt); histos.fill(HIST("GenQA/h3dGenASigma0_pTMap"), GenInfo.MCPt, GenInfo.MCDau1Pt, GenInfo.MCDau2Pt); @@ -1769,9 +2103,10 @@ struct sigma0builder { pi0GenCollRefs(MCGenInfo.MCCollId); // link to stramccollision table } - // Sigma0/ASigma0 - if (fillSigma0Tables && (MCGenInfo.IsSigma0 || MCGenInfo.IsAntiSigma0)) { - sigma0Gens(MCGenInfo.IsSigma0, MCGenInfo.IsProducedByGenerator, MCGenInfo.MCPt, mcParticle.y()); + // Sigma0/ASigma0 (Lambda(1520)/ALambda(1520)) + bool fIsGenSigma0Like = doLambdaStar ? (MCGenInfo.IsLambdaStar || MCGenInfo.IsAntiLambdaStar) : (MCGenInfo.IsSigma0 || MCGenInfo.IsAntiSigma0); + if (fillSigma0Tables && fIsGenSigma0Like) { + sigma0Gens(doLambdaStar ? MCGenInfo.IsLambdaStar : MCGenInfo.IsSigma0, MCGenInfo.IsProducedByGenerator, MCGenInfo.MCPt, mcParticle.y()); sigma0GenCollRefs(MCGenInfo.MCCollId); // link to stramccollision table } @@ -2199,6 +2534,11 @@ struct sigma0builder { return false; histos.fill(HIST("KShortSel/hSelectionStatistics"), 14.); + if (((TMath::Abs(posTrackKShort.tpcNSigmaPi()) > kshortSelections.KShortMaxTPCNSigmas) || + (TMath::Abs(negTrackKShort.tpcNSigmaPi()) > kshortSelections.KShortMaxTPCNSigmas))) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 15.); } return true; } @@ -2252,7 +2592,7 @@ struct sigma0builder { pi0MCInfo.fIsV02Primary, pi0MCInfo.V02PDGCode, pi0MCInfo.V02PDGCodeMother, pi0MCInfo.fIsV02CorrectlyAssign); } - pi0cores(pi0TopoInfo.X, pi0TopoInfo.Y, pi0TopoInfo.Z, pi0TopoInfo.DCADau, pi0TopoInfo.CosPA, + pi0cores(pi0TopoInfo.position[0], pi0TopoInfo.position[1], pi0TopoInfo.position[2], pi0TopoInfo.daughterDCA, pi0TopoInfo.cosPA, gamma1.px(), gamma1.py(), gamma1.pz(), gamma1.mGamma(), gamma1.qtarm(), gamma1.alpha(), gamma1.dcapostopv(), gamma1.dcanegtopv(), gamma1.dcaV0daughters(), gamma1.negativeeta(), gamma1.positiveeta(), gamma1.v0cosPA(), gamma1.v0radius(), gamma1.z(), @@ -2267,6 +2607,280 @@ struct sigma0builder { return true; } + //_______________________________________________ + // Build pi0 candidate for QA + template + bool buildPi0ForXi0(TV0Object const& gamma1, TV0Object const& gamma2, TCollision const& collision, V0PairTopoInfo& info) + { + //_______________________________________________ + // Check if both V0s are made of the same tracks + if (gamma1.posTrackExtraId() == gamma2.posTrackExtraId() || + gamma1.negTrackExtraId() == gamma2.negTrackExtraId()) { + return false; + } + + //_______________________________________________ + // Calculate pi0 properties + const std::array vtxGamma1 = {gamma1.x(), gamma1.y(), gamma1.z()}; + const std::array vtxGamma2 = {gamma2.x(), gamma2.y(), gamma2.z()}; + + const std::array momGamma1 = {gamma1.px(), gamma1.py(), gamma1.pz()}; + const std::array momGamma2 = {gamma2.px(), gamma2.py(), gamma2.pz()}; + + const std::array covGamma1 = {999.}; + const std::array covGamma2 = {999.}; + + std::array pVecGamma1{gamma1.px(), gamma1.py(), gamma1.pz()}; + std::array pVecGamma2{gamma2.px(), gamma2.py(), gamma2.pz()}; + std::array arrpi0{pVecGamma1, pVecGamma2}; + info.pi0Mass = RecoDecay::m(arrpi0, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassPhoton}); + info.pi0Y = RecoDecay::y(std::array{gamma1.px() + gamma2.px(), gamma1.py() + gamma2.py(), gamma1.pz() + gamma2.pz()}, o2::constants::physics::MassPi0); + + info.v01Index = gamma1.globalIndex(); + info.v02Index = gamma2.globalIndex(); + + //_______________________________________________ + // Pi0-specific selections: + if (std::abs(info.pi0Y) > cascadeSelections.maxPi0Rapidity) + return false; + + if (std::abs(info.pi0Mass - o2::constants::physics::MassPi0) > cascadeSelections.massPi0Window) + return false; + + o2::track::TrackParCov gammaTrack1(vtxGamma1, momGamma1, covGamma1, 0, true); + gammaTrack1.setAbsCharge(0); + gammaTrack1.setPID(o2::track::PID::Photon); + o2::track::TrackParCov gammaTrack2(vtxGamma2, momGamma2, covGamma2, 0, true); + gammaTrack2.setAbsCharge(0); + gammaTrack2.setPID(o2::track::PID::Photon); + + // First build Pi0 to select secondary pi0s + int nCandPi0 = 0; + try { + nCandPi0 = fitter2Prongs.process(gammaTrack1, gammaTrack2); + } catch (...) { + return false; + } + if (nCandPi0 == 0) { + return false; + } + + fitter2Prongs.propagateTracksToVertex(); // propagate e and K to D vertex + if (!fitter2Prongs.isPropagateTracksToVertexDone()) { + return false; + } + + info.daughterDCA = TMath::Sqrt(fitter2Prongs.getChi2AtPCACandidate()); + + if (info.daughterDCA > cascadeSelections.dcaPi0daughters) { + return false; + } + + // get decay vertex coordinates + const auto& vtxPi0 = fitter2Prongs.getPCACandidate(); + for (int i = 0; i < 3; i++) { + info.position[i] = vtxPi0[i]; + } + if (std::hypot(info.position[0], info.position[1]) < cascadeSelections.radiusPi0) { + return false; + } + + float x = vtxPi0[0]; + float y = vtxPi0[1]; + float z = vtxPi0[2]; + float px = pVecGamma1[0] + pVecGamma2[0]; + float py = pVecGamma1[1] + pVecGamma2[1]; + float pz = pVecGamma1[2] + pVecGamma2[2]; + info.dcaPi0ToPV = std::sqrt((std::pow((collision.posY() - y) * pz - (collision.posZ() - z) * py, 2) + std::pow((collision.posX() - x) * pz - (collision.posZ() - z) * px, 2) + std::pow((collision.posX() - x) * py - (collision.posY() - y) * px, 2)) / (px * px + py * py + pz * pz)); + if (info.dcaPi0ToPV < cascadeSelections.dcaPi0ToPV) { + return false; + } + + histos.fill(HIST("Xi0Sel/hPi0Mass"), info.pi0Mass); + + return true; + } + + //_______________________________________________ + // Build Xi0 candidate for analysis + template + bool buildXi0(TV0Object const& gamma1, TV0Object const& gamma2, TV0Object const& lambda, V0PairTopoInfo& pi0Info, TCollision const& collision, TMCParticles const& mcparticles) + { + Xi0Info cascade; + //_______________________________________________ + // Check if the 3 V0s are made of the same tracks + if (gamma1.posTrackExtraId() == gamma2.posTrackExtraId() || + gamma1.negTrackExtraId() == gamma2.negTrackExtraId() || + gamma1.posTrackExtraId() == lambda.posTrackExtraId() || + gamma1.negTrackExtraId() == lambda.negTrackExtraId() || + gamma2.posTrackExtraId() == lambda.posTrackExtraId() || + gamma2.negTrackExtraId() == lambda.negTrackExtraId()) { + return false; + } + + const std::array vtxGamma1 = {gamma1.x(), gamma1.y(), gamma1.z()}; + const std::array vtxGamma2 = {gamma2.x(), gamma2.y(), gamma2.z()}; + const std::array vtxLambda = {lambda.x(), lambda.y(), lambda.z()}; + + const std::array momGamma1 = {gamma1.px(), gamma1.py(), gamma1.pz()}; + const std::array momGamma2 = {gamma2.px(), gamma2.py(), gamma2.pz()}; + const std::array momLambda = {lambda.px(), lambda.py(), lambda.pz()}; + + const std::array covGamma1 = {999.}; + const std::array covGamma2 = {999.}; + const std::array covLambda = {999.}; + + o2::track::TrackParCov gammaTrack1(vtxGamma1, momGamma1, covGamma1, 0, true); + gammaTrack1.setAbsCharge(0); + gammaTrack1.setPID(o2::track::PID::Photon); + o2::track::TrackParCov gammaTrack2(vtxGamma2, momGamma2, covGamma2, 0, true); + gammaTrack2.setAbsCharge(0); + gammaTrack2.setPID(o2::track::PID::Photon); + o2::track::TrackParCov lambdaTrack(vtxLambda, momLambda, covLambda, 0, true); + lambdaTrack.setAbsCharge(0); + lambdaTrack.setPID(o2::track::PID::Lambda); + + // build Xi0 --> pi0 Lambda --> gamma gamma Lambda + int nCandXi0 = 0; + try { + nCandXi0 = fitter3Prongs.process(gammaTrack1, gammaTrack2, lambdaTrack); + } catch (...) { + return false; + } + if (nCandXi0 == 0) { + return false; + } + + fitter3Prongs.propagateTracksToVertex(); // propagate e and K to D vertex + if (!fitter3Prongs.isPropagateTracksToVertexDone()) { + return false; + } + + auto lGamma1Track = fitter3Prongs.getTrack(0); + auto lGamma2Track = fitter3Prongs.getTrack(1); + auto lLambdaTrack = fitter3Prongs.getTrack(2); + + cascade.cascadeDaughterDCA = TMath::Sqrt(fitter3Prongs.getChi2AtPCACandidate()); + if (cascade.cascadeDaughterDCA > cascadeSelections.dcaCascadeDaughters) { + return false; + } + + cascade.pi0DaughterDCA = pi0Info.daughterDCA; + + lGamma1Track.getPxPyPzGlo(cascade.gamma1Momentum); + lGamma2Track.getPxPyPzGlo(cascade.gamma2Momentum); + lLambdaTrack.getPxPyPzGlo(cascade.lambdaMomentum); + // get decay vertex coordinates + const auto& vtx = fitter3Prongs.getPCACandidate(); + for (int i = 0; i < 3; i++) { + cascade.cascadePosition[i] = vtx[i]; + } + if (std::hypot(cascade.cascadePosition[0], cascade.cascadePosition[1]) < cascadeSelections.radiusCascade) { + return false; + } + + cascade.pi0Position[0] = pi0Info.position[0]; + cascade.pi0Position[1] = pi0Info.position[1]; + cascade.pi0Position[2] = pi0Info.position[2]; + + double cosPA = RecoDecay::cpa( + std::array{collision.posX(), collision.posY(), collision.posZ()}, + std::array{cascade.cascadePosition[0], cascade.cascadePosition[1], cascade.cascadePosition[2]}, + std::array{cascade.gamma1Momentum[0] + cascade.gamma2Momentum[0] + cascade.lambdaMomentum[0], + cascade.gamma1Momentum[1] + cascade.gamma2Momentum[1] + cascade.lambdaMomentum[1], + cascade.gamma1Momentum[2] + cascade.gamma2Momentum[2] + cascade.lambdaMomentum[2]}); + if (cosPA < cascadeSelections.cosPACascade) { + return false; + } + + // Calculate DCAxy of the cascade (with bending) + auto lCascadeTrack = fitter3Prongs.createParentTrackParCov(); + lCascadeTrack.setAbsCharge(0); // to be sure + lCascadeTrack.setPID(o2::track::PID::XiMinus); // FIXME: not OK for omegas + std::array dcaInfo{999.f, 999.f}; + + o2::base::Propagator::Instance()->propagateToDCABxByBz({collision.posX(), collision.posY(), collision.posZ()}, lCascadeTrack, 2.f, fitter3Prongs.getMatCorrType(), &dcaInfo); + cascade.cascadeDCAxy = dcaInfo[0]; + cascade.cascadeDCAz = dcaInfo[1]; + + // Populate information + // cascadecandidate.v0Id = v0index.globalIndex(); + cascade.collisionId = collision.globalIndex(); + cascade.gamma1Index = gamma1.globalIndex(); + cascade.gamma2Index = gamma2.globalIndex(); + cascade.lambdaIndex = lambda.globalIndex(); + + std::array momPi0{gamma1.px() + gamma2.px(), gamma1.py() + gamma2.py(), gamma1.pz() + gamma2.pz()}; + auto arrMom = std::array{momPi0, momLambda}; + cascade.xi0Mass = RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPi0, o2::constants::physics::MassLambda0}); + + // Calculate position covariance matrix + auto covVtxV = fitter3Prongs.calcPCACovMatrix(0); + // std::array positionCovariance; + float positionCovariance[6]; + positionCovariance[0] = covVtxV(0, 0); + positionCovariance[1] = covVtxV(1, 0); + positionCovariance[2] = covVtxV(1, 1); + positionCovariance[3] = covVtxV(2, 0); + positionCovariance[4] = covVtxV(2, 1); + positionCovariance[5] = covVtxV(2, 2); + // store momentum covariance matrix + std::array covTgamma1 = {0.}; + std::array covTgamma2 = {0.}; + std::array covTlambda = {0.}; + // std::array momentumCovariance; + lGamma1Track.getCovXYZPxPyPzGlo(covTgamma1); + lGamma2Track.getCovXYZPxPyPzGlo(covTgamma2); + lLambdaTrack.getCovXYZPxPyPzGlo(covTlambda); + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component + for (int i = 0; i < 21; i++) { + cascade.covariance[i] = 0.0f; + } + for (int i = 0; i < 6; i++) { + cascade.covariance[i] = positionCovariance[i]; + cascade.covariance[MomInd[i]] = covTgamma1[MomInd[i]] + covTgamma2[MomInd[i]] + covTlambda[MomInd[i]]; + } + + // Check if MC data and populate corresponding table + if constexpr (requires { collision.straMCCollisionId(); gamma1.motherMCPartId(); gamma2.motherMCPartId(); lambda.motherMCPartId(); }) { + auto xi0MCInfo = getXi0MCInfo(gamma1, gamma2, lambda, mcparticles); + + xi0mccores(xi0MCInfo.gamma1MCpx, xi0MCInfo.gamma1MCpy, xi0MCInfo.gamma1MCpz, + xi0MCInfo.gamma1IsPhysicalPrimary, xi0MCInfo.gamma1PDGCodePos, xi0MCInfo.gamma1PDGCodeNeg, xi0MCInfo.gamma1PDGCode, xi0MCInfo.gamma1PDGCodeMother, + xi0MCInfo.gamma2MCpx, xi0MCInfo.gamma2MCpy, xi0MCInfo.gamma2MCpz, + xi0MCInfo.gamma2IsPhysicalPrimary, xi0MCInfo.gamma2PDGCodePos, xi0MCInfo.gamma2PDGCodeNeg, xi0MCInfo.gamma2PDGCode, xi0MCInfo.gamma2PDGCodeMother, + xi0MCInfo.lambdaMCpx, xi0MCInfo.lambdaMCpy, xi0MCInfo.lambdaMCpz, + xi0MCInfo.lambdaIsPhysicalPrimary, xi0MCInfo.lambdaPDGCodePos, xi0MCInfo.lambdaPDGCodeNeg, xi0MCInfo.lambdaPDGCode, xi0MCInfo.lambdaPDGCodeMother, + xi0MCInfo.pi0X, xi0MCInfo.pi0Y, xi0MCInfo.pi0Z, xi0MCInfo.pi0IsPhysicalPrimary, xi0MCInfo.pi0MCProcess, xi0MCInfo.pi0PDGCode, + xi0MCInfo.xi0X, xi0MCInfo.xi0Y, xi0MCInfo.xi0Z, xi0MCInfo.xi0IsPhysicalPrimary, xi0MCInfo.xi0MCProcess, xi0MCInfo.xi0PDGCode, xi0MCInfo.xi0PDGCodeMother); + + xi0mcIndices(xi0MCInfo.gamma1Index, xi0MCInfo.gamma2Index, xi0MCInfo.lambdaIndex); + + int mcCollisionIndex = -1; + if (collision.has_straMCCollision()) { + auto mcCollision = collision.template straMCCollision_as>(); + mcCollisionIndex = mcCollision.globalIndex(); + } + xi0mccollRefs(mcCollisionIndex); + } + + xi0cores(cascade.cascadePosition[0], cascade.cascadePosition[1], cascade.cascadePosition[2], + cascade.pi0Position[0], cascade.pi0Position[1], cascade.pi0Position[2], + vtxLambda[0], vtxLambda[1], vtxLambda[2], + cascade.cascadeDaughterDCA, cascade.pi0DaughterDCA, cascade.cascadeDCAxy, cascade.cascadeDCAz, + cascade.gamma1Momentum[0], cascade.gamma1Momentum[1], cascade.gamma1Momentum[2], + cascade.gamma2Momentum[0], cascade.gamma2Momentum[1], cascade.gamma2Momentum[2], + cascade.lambdaMomentum[0], cascade.lambdaMomentum[1], cascade.lambdaMomentum[2]); + + xi0Indices(cascade.gamma1Index, cascade.gamma2Index, cascade.lambdaIndex); + + xi0collRefs(collision.globalIndex()); + + histos.fill(HIST("Xi0Sel/hXi0Mass"), cascade.xi0Mass); + + return true; + } + //_______________________________________________ // Build sigma0 candidate with PCM photons template @@ -2293,14 +2907,15 @@ struct sigma0builder { auto arrMom = std::array{pVecPhotons, pVecLambda}; float sigmaMass = RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassLambda0}); float sigmaY = -999.f; + float TheoreticalMass = doLambdaStar ? o2::constants::physics::MassLambda1520 : o2::constants::physics::MassSigma0; if constexpr (requires { gamma.pxMC(); lambda.pxMC(); }) // If MC - sigmaY = RecoDecay::y(std::array{gamma.pxMC() + lambda.pxMC(), gamma.pyMC() + lambda.pyMC(), gamma.pzMC() + lambda.pzMC()}, o2::constants::physics::MassSigma0); + sigmaY = RecoDecay::y(std::array{gamma.pxMC() + lambda.pxMC(), gamma.pyMC() + lambda.pyMC(), gamma.pzMC() + lambda.pzMC()}, TheoreticalMass); else // If DATA - sigmaY = RecoDecay::y(std::array{gamma.px() + lambda.px(), gamma.py() + lambda.py(), gamma.pz() + lambda.pz()}, o2::constants::physics::MassSigma0); + sigmaY = RecoDecay::y(std::array{gamma.px() + lambda.px(), gamma.py() + lambda.py(), gamma.pz() + lambda.pz()}, TheoreticalMass); histos.fill(HIST("SigmaSel/hSelectionStatistics"), 1.); - if (TMath::Abs(sigmaMass - o2::constants::physics::MassSigma0) > Sigma0Window) + if (TMath::Abs(sigmaMass - TheoreticalMass) > Sigma0Window) return false; histos.fill(HIST("SigmaSel/hSelectionStatistics"), 2.); @@ -2315,7 +2930,7 @@ struct sigma0builder { // Sigma0 topological info auto sigma0TopoInfo = propagateV0PairToDCA(gamma, lambda); - sigma0cores(gamma.globalIndex(), lambda.globalIndex(), sigma0TopoInfo.X, sigma0TopoInfo.Y, sigma0TopoInfo.Z, sigma0TopoInfo.DCADau, + sigma0cores(gamma.globalIndex(), lambda.globalIndex(), sigma0TopoInfo.position[0], sigma0TopoInfo.position[1], sigma0TopoInfo.position[2], sigma0TopoInfo.daughterDCA, gamma.px(), gamma.py(), gamma.pz(), gamma.mGamma(), lambda.px(), lambda.py(), lambda.pz(), lambda.mLambda(), lambda.mAntiLambda()); // MC properties @@ -2415,14 +3030,15 @@ struct sigma0builder { auto arrMom = std::array{pVecPhotons, pVecLambda}; float sigmaMass = RecoDecay::m(arrMom, std::array{o2::constants::physics::MassPhoton, o2::constants::physics::MassLambda0}); + float TheoreticalMass = doLambdaStar ? o2::constants::physics::MassLambda1520 : o2::constants::physics::MassSigma0; // N.B. At this stage, we are only using the reconstructed rapidity (ideally with a very loose cut) // A proper selection should be done in the sigmaanalysis - float sigmaY = RecoDecay::y(std::array{gammapx + lambda.px(), gammapy + lambda.py(), gammapz + lambda.pz()}, o2::constants::physics::MassSigma0); + float sigmaY = RecoDecay::y(std::array{gammapx + lambda.px(), gammapy + lambda.py(), gammapz + lambda.pz()}, TheoreticalMass); histos.fill(HIST("SigmaSel/hSelectionStatistics"), 1.); histos.fill(HIST("SigmaSel/hSigmaMassBeforeSel"), sigmaMass); - if (TMath::Abs(sigmaMass - o2::constants::physics::MassSigma0) > Sigma0Window) + if (TMath::Abs(sigmaMass - TheoreticalMass) > Sigma0Window) return false; histos.fill(HIST("SigmaSel/hSelectionStatistics"), 2.); @@ -2541,7 +3157,7 @@ struct sigma0builder { auto kstarTopoInfo = propagateV0PairToDCA(gamma, kshort); - kstarcores(kstarTopoInfo.X, kstarTopoInfo.Y, kstarTopoInfo.Z, kstarTopoInfo.DCADau, + kstarcores(gamma.globalIndex(), kshort.globalIndex(), kstarTopoInfo.position[0], kstarTopoInfo.position[1], kstarTopoInfo.position[2], kstarTopoInfo.daughterDCA, gamma.px(), gamma.py(), gamma.pz(), gamma.mGamma(), kshort.px(), kshort.py(), kshort.pz(), kshort.mK0Short()); // MC properties @@ -2550,9 +3166,9 @@ struct sigma0builder { kstarmccores(kstarMCInfo.V0PairMCRadius, kstarMCInfo.V0PairPDGCode, kstarMCInfo.V0PairPDGCodeMother, kstarMCInfo.V0PairMCProcess, kstarMCInfo.fV0PairProducedByGenerator, kstarMCInfo.V01MCpx, kstarMCInfo.V01MCpy, kstarMCInfo.V01MCpz, - kstarMCInfo.fIsV01Primary, kstarMCInfo.V01PDGCode, kstarMCInfo.V01PDGCodeMother, kstarMCInfo.fIsV01CorrectlyAssign, + kstarMCInfo.fIsV01Primary, kstarMCInfo.V01PDGCode, kstarMCInfo.V01PDGCodeMother, kstarMCInfo.V01PDGCodeGrandMother, kstarMCInfo.V01GlobalIndexGrandMother, kstarMCInfo.fIsV01CorrectlyAssign, kstarMCInfo.V02MCpx, kstarMCInfo.V02MCpy, kstarMCInfo.V02MCpz, - kstarMCInfo.fIsV02Primary, kstarMCInfo.V02PDGCode, kstarMCInfo.V02PDGCodeMother, kstarMCInfo.fIsV02CorrectlyAssign); + kstarMCInfo.fIsV02Primary, kstarMCInfo.V02PDGCode, kstarMCInfo.V02PDGCodeMother, kstarMCInfo.V02PDGCodeGrandMother, kstarMCInfo.V02GlobalIndexGrandMother, kstarMCInfo.fIsV02CorrectlyAssign); } // KStar -> stracollisions link @@ -2660,6 +3276,8 @@ struct sigma0builder { //_______________________________________________ // Collisions loop for (const auto& coll : collisions) { + initCCDB(coll); + // Event selection if (eventSelections.fUseEventSelection) { if (!IsEventAccepted(coll, true)) @@ -2754,9 +3372,9 @@ struct sigma0builder { } } - //_______________________________________________ - // KStar loop if constexpr (!soa::is_table) { // Don't use EMCal clusters here + //_______________________________________________ + // KStar loop if (fillKStarTables) { auto gamma1 = fullV0s.rawIteratorAt(bestGammasArray[i]); for (size_t j = 0; j < bestKShortsArray.size(); ++j) { @@ -2767,11 +3385,9 @@ struct sigma0builder { continue; } } - } - //_______________________________________________ - // pi0 loop - if constexpr (!soa::is_table) { // Don't use EMCal clusters here + //_______________________________________________ + // pi0 loop if (fillPi0Tables) { auto gamma1 = fullV0s.rawIteratorAt(bestGammasArray[i]); for (size_t j = i + 1; j < bestGammasArray.size(); ++j) { @@ -2784,6 +3400,36 @@ struct sigma0builder { } } } + + //_______________________________________________ + // Xi0 loop + if (fillXi0Tables) { + // First find pi0 candidates + std::vector pi0Candidates; + for (size_t i = 0; i < bestGammasArray.size(); ++i) { // loop over photons + auto gamma1 = fullV0s.rawIteratorAt(bestGammasArray[i]); + for (size_t j = i + 1; j < bestGammasArray.size(); ++j) { // loop over photons + auto gamma2 = fullV0s.rawIteratorAt(bestGammasArray[j]); + V0PairTopoInfo pi0Info; + if (!buildPi0ForXi0(gamma1, gamma2, coll, pi0Info)) { + continue; + } + pi0Candidates.push_back(pi0Info); + } + } + + // second build Xi0 candidates + for (size_t i = 0; i < bestLambdasArray.size(); ++i) { // loop over Lambda + auto lambda = fullV0s.rawIteratorAt(bestLambdasArray[i]); + for (size_t j = 0; j < pi0Candidates.size(); ++j) { // loop over pi0 + auto gamma1 = fullV0s.rawIteratorAt(pi0Candidates[j].v01Index); + auto gamma2 = fullV0s.rawIteratorAt(pi0Candidates[j].v02Index); + // Building pi0 candidate & filling tables + if (!buildXi0(gamma1, gamma2, lambda, pi0Candidates[j], coll, mcparticles)) + continue; + } + } + } } } @@ -3123,32 +3769,32 @@ struct sigma0builder { } // Sigma0 processing part - void processRealData(soa::Join const& collisions, V0StandardDerivedDatas const& fullV0s, dauTracks const&) + void processRealData(soa::Join const& collisions, V0StandardDerivedDatas const& fullV0s, dauTracks const&) { dataProcess(collisions, fullV0s, nullptr, nullptr, nullptr); } - void processRealDataWithTOF(soa::Join const& collisions, V0TOFStandardDerivedDatas const& fullV0s, dauTracks const&) + void processRealDataWithTOF(soa::Join const& collisions, V0TOFStandardDerivedDatas const& fullV0s, dauTracks const&) { dataProcess(collisions, fullV0s, nullptr, nullptr, nullptr); } - void processRealDataWithEMCal(soa::Join const& collisions, V0StandardDerivedDatas const& fullV0s, dauTracks const&, aod::EMCALClusters const& fullEMCalClusters, aod::EMCALMatchedTracks const& emcmatchedtracks) + void processRealDataWithEMCal(soa::Join const& collisions, V0StandardDerivedDatas const& fullV0s, dauTracks const&, aod::EMCALClusters const& fullEMCalClusters, aod::EMCALMatchedTracks const& emcmatchedtracks) { dataProcess(collisions, fullV0s, fullEMCalClusters, emcmatchedtracks, nullptr); } - void processMonteCarlo(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&) + void processMonteCarlo(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&) { dataProcess(collisions, fullV0s, nullptr, nullptr, mcParticles); } - void processMonteCarloWithTOF(soa::Join const& collisions, V0TOFDerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&) + void processMonteCarloWithTOF(soa::Join const& collisions, V0TOFDerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&) { dataProcess(collisions, fullV0s, nullptr, nullptr, mcParticles); } - void processMonteCarloWithEMCal(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&, EMCalMCClusters const& fullEMCalMCClusters, aod::EMCALMatchedTracks const& emcmatchedtracks) + void processMonteCarloWithEMCal(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&, EMCalMCClusters const& fullEMCalMCClusters, aod::EMCALMatchedTracks const& emcmatchedtracks) { dataProcess(collisions, fullV0s, fullEMCalMCClusters, emcmatchedtracks, mcParticles); } @@ -3159,22 +3805,22 @@ struct sigma0builder { } // Photon and lambda-specific part (QA) - void processV0QA(soa::Join const& collisions, V0StandardDerivedDatas const& fullV0s, dauTracks const&) + void processV0QA(soa::Join const& collisions, V0StandardDerivedDatas const& fullV0s, dauTracks const&) { runV0QA(collisions, fullV0s); } - void processV0MCQA(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&) + void processV0MCQA(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&) { runV0QA(collisions, fullV0s); } - void processV0Generated(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& collisions) + void processV0Generated(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& collisions) { runGenV0QA(mcCollisions, V0MCCores, collisions); } - void processPCMVsEMCalQA(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&, EMCalMCClusters const& fullEMCalMCClusters, aod::EMCALMatchedTracks const& emcmatchedtracks) + void processPCMVsEMCalQA(soa::Join const& collisions, V0DerivedMCDatas const& fullV0s, aod::McParticles const& mcParticles, dauTracks const&, aod::MotherMCParts const&, soa::Join const&, soa::Join const&, EMCalMCClusters const& fullEMCalMCClusters, aod::EMCALMatchedTracks const& emcmatchedtracks) { runPCMVsEMCalQA(collisions, fullV0s, fullEMCalMCClusters, emcmatchedtracks, mcParticles); } diff --git a/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx b/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx index 3eaee6351b3..782f8c68bfc 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaHadCorr.cxx @@ -17,6 +17,7 @@ #include "PWGLF/DataModel/LFSigmaHadTables.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -36,10 +37,13 @@ #include #include -#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include #include #include +#include #include #include #include @@ -50,10 +54,10 @@ using namespace o2::framework::expressions; using TracksFull = soa::Join; using TracksFullMC = soa::Join; -using CollisionsFull = soa::Join; -using CollisionsFullMC = soa::Join; +using CollisionsFull = soa::Join; +using CollisionsFullMC = soa::Join; -struct sigmaHadCand { +struct SigmaHadCand { float ptHad() const { @@ -86,24 +90,31 @@ struct sigmaHadCand { int kinkDauID = -1; // ID of the pion from Sigma decay in MC int sigmaID = -1; // ID of the Sigma candidate in MC int hadID = -1; // ID of the hadron candidate in MC + + int sigmaMotherPDG = -999; // PDG of the direct mother of the Sigma in MC + int sigmaPartonicMotherPDG = -999; // PDG of the first or last partonic ancestor of the Sigma in MC + + float multiplicity = -1.f; }; -struct sigmaHadCorrTask { +struct SigmaHadCorr { - std::vector sigmaHadCandidates; // Vector to store Sigma-hadron candidates Produces outputDataTable; // Output table for Sigma-hadron candidates Produces outputDataTableMC; // Output table for Sigma-hadron candidates in MC + Produces outputKinkCandsMC; // Single-Sigma-level MC truth record, filled before hadron pairing // Histograms are defined with HistogramRegistry HistogramRegistry rEventSelection{"eventSelection", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry rSigmaHad{"sigmaHad", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Configurable for event selection - Configurable cutzvertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable cutZVertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; Configurable doSigmaPion{"doSigmaPion", false, "If true, pair Sigma with pions instead of protons"}; Configurable doSigmaMinus{"doSigmaMinus", true, "If true, pair Sigma- candidates, else Sigma+"}; Configurable cutMaxKStar{"cutMaxKStar", 1.5, "Maximum k* for Sigma-hadron pairs (GeV/c)"}; Configurable minPtSigma{"minPtSigma", 1.f, "Minimum pT for Sigma candidates (GeV/c)"}; + Configurable cutRapMotherMC{"cutRapMotherMC", 1.0f, "Rapidity cut for generated mother Sigma in MC"}; + Configurable cutPtGenMC{"cutPtGenMC", 0.5f, "Minimum pT for generated Sigma particles in MC"}; Configurable useRecalculatedSigmaMomentum{"useRecalculatedSigmaMomentum", true, "If true, compute k* using Sigma momentum recalculated from daughter kinematics"}; Configurable cutDCAtoPVSigma{"cutDCAtoPVSigma", 0.1f, "Max DCA to primary vertex for Sigma candidates (cm)"}; Configurable cutSigmaRadius{"cutSigmaRadius", 20.f, "Minimum radius for Sigma candidates (cm)"}; @@ -111,7 +122,7 @@ struct sigmaHadCorrTask { Configurable alphaAPCut{"alphaAPCut", 0., "Alpha AP cut for Sigma candidates"}; Configurable qtAPCutLow{"qtAPCutLow", 0.15, "Lower qT AP cut for Sigma candidates (GeV/c)"}; Configurable qtAPCutHigh{"qtAPCutHigh", 0.2, "Upper qT AP cut for Sigma candidates (GeV/c)"}; - Configurable cutEtaDaught{"cutEtaDaughter", 0.8f, "Eta cut for daughter tracks"}; + Configurable cutEtaDaughter{"cutEtaDaughter", 0.8f, "Eta cut for daughter tracks"}; Configurable ptMinTOFKinkDau{"ptMinTOFKinkDau", 0.75f, "Minimum pT to require TOF for kink daughter PID (GeV/c)"}; Configurable applyTOFPIDKinkDaughter{"applyTOFPIDKinkDaughter", false, "If true, apply TOF PID cut to the kink daughter track"}; @@ -124,9 +135,12 @@ struct sigmaHadCorrTask { Configurable fillOutputTree{"fillOutputTree", true, "If true, fill the output tree with Sigma-hadron candidates"}; Configurable fillSparseInvMassKstar{"fillSparseInvMassKstar", false, "If true, fill THn with invmass, k*, sigma charge, proton charge, sigma decay radius, cosPA, sigma pt"}; + Configurable saveMultiplicity{"saveMultiplicity", false, "If true, save collision multiplicity in the output table"}; + Configurable useMultNTracksPV{"useMultNTracksPV", false, "If true, use multNTracksPV for multiplicity and mixing bins; if false, use numContrib"}; + Configurable findLastPartonicMother{"findLastPartonicMother", true, "If true, store the initial hard-scattering parton (last partonic mother). If false, store the last parton before hadronization (first partonic mother)"}; - ConfigurableAxis CfgVtxBins{"CfgVtxBins", {10, -10, 10}, "Mixing bins - z-vertex"}; - ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0.0, 40.0, 80.0, 500.0}, "Mixing bins - number of contributor"}; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {10, -10, 10}, "Mixing bins - z-vertex"}; + ConfigurableAxis cfgMultBins{"cfgMultBins", {VARIABLE_WIDTH, 0.0, 40.0, 80.0, 500.0}, "Mixing bins - number of contributor"}; Configurable nEvtMixingBkg{"nEvtMixingBkg", 5, "Number of events to mix for background reconstruction"}; Preslice kinkCandsPerCollisionPreslice = aod::kinkcand::collisionId; @@ -211,12 +225,13 @@ struct sigmaHadCorrTask { { // Sigma- -> n + pi- (charged daughter = pion, neutral daughter = neutron) // Sigma+ -> p + pi0 (charged daughter = proton, neutral daughter = pi0) + const float epsilon = 1e-6f; float massChargedDau = doSigmaMinus ? o2::constants::physics::MassPionCharged : o2::constants::physics::MassProton; float massNeutralDau = doSigmaMinus ? o2::constants::physics::MassNeutron : o2::constants::physics::MassPionNeutral; float massSigma = doSigmaMinus ? o2::constants::physics::MassSigmaMinus : o2::constants::physics::MassSigmaPlus; float pMother = std::sqrt(sigmaPx * sigmaPx + sigmaPy * sigmaPy + sigmaPz * sigmaPz); - if (pMother < 1e-6f) { + if (pMother < epsilon) { return -999.f; } float versorX = sigmaPx / pMother; @@ -224,29 +239,29 @@ struct sigmaHadCorrTask { float versorZ = sigmaPz / pMother; float eChDau = std::sqrt(massChargedDau * massChargedDau + sigmaDauPx * sigmaDauPx + sigmaDauPy * sigmaDauPy + sigmaDauPz * sigmaDauPz); float a = versorX * sigmaDauPx + versorY * sigmaDauPy + versorZ * sigmaDauPz; - float K = massSigma * massSigma + massChargedDau * massChargedDau - massNeutralDau * massNeutralDau; - float A = 4.f * (eChDau * eChDau - a * a); - float B = -4.f * a * K; - float C = 4.f * eChDau * eChDau * massSigma * massSigma - K * K; - if (std::abs(A) < 1e-6f) { + float coefK = massSigma * massSigma + massChargedDau * massChargedDau - massNeutralDau * massNeutralDau; + float coefA = 4.f * (eChDau * eChDau - a * a); + float coefB = -4.f * a * coefK; + float coefC = 4.f * eChDau * eChDau * massSigma * massSigma - coefK * coefK; + if (std::abs(coefA) < epsilon) { return -999.f; } - float D = B * B - 4.f * A * C; - if (D < 0.f) { + float discriminant = coefB * coefB - 4.f * coefA * coefC; + if (discriminant < 0.f) { return -999.f; } - float sqrtD = std::sqrt(D); - float P1 = (-B + sqrtD) / (2.f * A); - float P2 = (-B - sqrtD) / (2.f * A); - if (P2 < 0.f && P1 < 0.f) { + float sqrtDiscriminant = std::sqrt(discriminant); + float root1 = (-coefB + sqrtDiscriminant) / (2.f * coefA); + float root2 = (-coefB - sqrtDiscriminant) / (2.f * coefA); + if (root2 < 0.f && root1 < 0.f) { return -999.f; } - if (P2 < 0.f) { - return P1; + if (root2 < 0.f) { + return root1; } - float p1Diff = std::abs(P1 - pMother); - float p2Diff = std::abs(P2 - pMother); - return (p1Diff < p2Diff) ? P1 : P2; + float p1Diff = std::abs(root1 - pMother); + float p2Diff = std::abs(root2 - pMother); + return (p1Diff < p2Diff) ? root1 : root2; } std::array getSigmaMomentumForKstar(float sigmaPx, float sigmaPy, float sigmaPz, float sigmaDauPx, float sigmaDauPy, float sigmaDauPz) @@ -278,6 +293,104 @@ struct sigmaHadCorrTask { return doSigmaPion ? o2::constants::physics::MassPionCharged : o2::constants::physics::MassProton; } + // Section of functions to retrieve MC truth information for the Sigma candidate (adapted from PWGCF/Femto) + // Get the PDG code of the direct mother of the Sigma candidate + template + int getSigmaMotherPDG(const TMcParticle& mcParticle, const TMcParticles& mcParticles) + { + if (!mcParticle.has_mothers()) { + return -999; + } + auto motherIds = mcParticle.mothersIds(); + int mcMotherIndex = motherIds.front(); + if (mcMotherIndex < 0 || mcMotherIndex >= static_cast(mcParticles.size())) { + return -999; + } + return mcParticles.iteratorAt(mcMotherIndex).pdgCode(); + } + + // Walk up the decay chain and return the PDG of the first quark or gluon ancestor found + template + int findFirstPartonicMotherPDG(const TMcParticle& mcParticle, const TMcParticles& mcParticles) + { + const int notFoundPdg = -999; + if (!mcParticle.has_mothers()) { + return notFoundPdg; + } + auto motherIds = mcParticle.mothersIds(); + const int defaultMotherSize = 2; + std::vector allMotherIds; + if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { + for (int i = motherIds[0]; i <= motherIds[1]; i++) + allMotherIds.push_back(i); + } else { + for (const int& id : motherIds) + allMotherIds.push_back(id); + } + for (const int& i : allMotherIds) { + if (i < 0 || i >= static_cast(mcParticles.size())) + continue; + const auto& mother = mcParticles.iteratorAt(i); + int pdgAbs = std::abs(mother.pdgCode()); + if (pdgAbs <= PDG_t::kTop || pdgAbs == PDG_t::kGluon) { + return mother.pdgCode(); + } + int found = findFirstPartonicMotherPDG(mother, mcParticles); + if (found != notFoundPdg) + return found; + } + return notFoundPdg; + } + + // Walk up the decay chain iteratively and return the PDG of the last quark or gluon before beam remnants + template + int findLastPartonicMotherPDG(const TMcParticle& mcParticle, const TMcParticles& mcParticles) + { + int lastPartonPDG = -999; + int64_t currentIndex = mcParticle.globalIndex(); + while (currentIndex >= 0 && currentIndex < static_cast(mcParticles.size())) { + const auto& current = mcParticles.iteratorAt(currentIndex); + if (!current.has_mothers()) + break; + auto motherIds = current.mothersIds(); + int nextIndex = -1; + const int defaultMotherSize = 2; + if (motherIds.size() == defaultMotherSize && motherIds[1] > motherIds[0]) { + nextIndex = motherIds[0]; + } else { + for (const int& id : motherIds) { + if (id >= 0 && id < static_cast(mcParticles.size())) { + nextIndex = id; + break; + } + } + } + if (nextIndex < 0 || nextIndex >= static_cast(mcParticles.size())) + break; + const auto& mother = mcParticles.iteratorAt(nextIndex); + int pdgAbs = std::abs(mother.pdgCode()); + int status = std::abs(o2::mcgenstatus::getGenStatusCode(mother.statusCode())); + bool isParton = (pdgAbs <= PDG_t::kTop || pdgAbs == PDG_t::kGluon); + const int isBeamParticleLowerLimit = 11; + const int isBeamParticleUpperLimit = 19; + bool isBeam = (status >= isBeamParticleLowerLimit && status <= isBeamParticleUpperLimit); + if (isBeam) + return lastPartonPDG; + if (isParton) + lastPartonPDG = mother.pdgCode(); + currentIndex = nextIndex; + } + return lastPartonPDG; + } + + int getPartonicMotherPDG(const auto& mcParticle, const auto& mcParticles) + { + if (findLastPartonicMother.value) { + return findLastPartonicMotherPDG(mcParticle, mcParticles); + } + return findFirstPartonicMotherPDG(mcParticle, mcParticles); + } + float getSigmaMassForKstar() { return doSigmaMinus ? o2::constants::physics::MassSigmaMinus : o2::constants::physics::MassSigmaPlus; @@ -295,24 +408,19 @@ struct sigmaHadCorrTask { return doSigmaPion ? track.tofNSigmaPi() : track.tofNSigmaPr(); } - TLorentzVector trackSum, PartOneCMS, PartTwoCMS, trackRelK; float getKStar(float sigmaPx, float sigmaPy, float sigmaPz, float pxHad, float pyHad, float pzHad) { - TLorentzVector part1; // Sigma - TLorentzVector part2; // Hadron track (proton/pion) - part1.SetXYZM(sigmaPx, sigmaPy, sigmaPz, getSigmaMassForKstar()); - part2.SetXYZM(pxHad, pyHad, pzHad, getHadTrackMass()); - trackSum = part1 + part2; + ROOT::Math::PxPyPzMVector part1(sigmaPx, sigmaPy, sigmaPz, getSigmaMassForKstar()); // Sigma + ROOT::Math::PxPyPzMVector part2(pxHad, pyHad, pzHad, getHadTrackMass()); // Hadron track (proton/pion) + ROOT::Math::PxPyPzMVector trackSum = part1 + part2; const float beta = trackSum.Beta(); const float betax = beta * std::cos(trackSum.Phi()) * std::sin(trackSum.Theta()); const float betay = beta * std::sin(trackSum.Phi()) * std::sin(trackSum.Theta()); const float betaz = beta * std::cos(trackSum.Theta()); - PartOneCMS.SetXYZM(part1.Px(), part1.Py(), part1.Pz(), part1.M()); - PartTwoCMS.SetXYZM(part2.Px(), part2.Py(), part2.Pz(), part2.M()); const ROOT::Math::Boost boostPRF = ROOT::Math::Boost(-betax, -betay, -betaz); - PartOneCMS = boostPRF(PartOneCMS); - PartTwoCMS = boostPRF(PartTwoCMS); - trackRelK = PartOneCMS - PartTwoCMS; + ROOT::Math::PxPyPzMVector partOneCMS = boostPRF(part1); + ROOT::Math::PxPyPzMVector partTwoCMS = boostPRF(part2); + ROOT::Math::PxPyPzMVector trackRelK = partOneCMS - partTwoCMS; return 0.5 * trackRelK.P(); } @@ -322,7 +430,7 @@ struct sigmaHadCorrTask { if (candidate.pt() < ptMinHad) { return false; } - if (std::abs(getTPCNSigmaHad(candidate)) > cutNSigmaTPC || candidate.tpcNClsFound() < cutNTPCClusHad || std::abs(candidate.eta()) > cutEtaDaught) { + if (std::abs(getTPCNSigmaHad(candidate)) > cutNSigmaTPC || candidate.tpcNClsFound() < cutNTPCClusHad || std::abs(candidate.eta()) > cutEtaDaughter) { return false; } @@ -392,9 +500,10 @@ struct sigmaHadCorrTask { return true; } - template - void fillTreeAndHistograms(aod::KinkCands const& kinkCands, Ttrack const& tracksDauSigma, Ttrack const& tracks, Tcollision const& collision, bool isMC) + template + std::vector fillTreeAndHistograms(aod::KinkCands const& kinkCands, Ttrack const& tracksDauSigma, Ttrack const& tracks, Tcollision const& collision) { + std::vector sigmaHadCandidates; for (const auto& sigmaCand : kinkCands) { auto kinkDauTrack = tracksDauSigma.rawIteratorAt(sigmaCand.trackDaugId()); if (!selectSigma(sigmaCand, kinkDauTrack)) { @@ -420,6 +529,30 @@ struct sigmaHadCorrTask { rSigmaHad.fill(HIST("QA/hSigmaPtRecal"), sigmaPtRecal); rSigmaHad.fill(HIST("QA/h2InvMassVsPtSigma"), sigmaPtRecal, sigmaMassForQa); + // single-Sigma-level MC truth record, filled once per accepted candidate, independent of hadron pairing + if constexpr (IsMC) { + auto mothTrack = tracksDauSigma.rawIteratorAt(sigmaCand.trackMothId()); + if (mothTrack.has_mcParticle() && kinkDauTrack.has_mcParticle()) { + auto mcMoth = mothTrack.template mcParticle_as(); + auto mcDaug = kinkDauTrack.template mcParticle_as(); + float massMC = std::sqrt(mcMoth.e() * mcMoth.e() - mcMoth.p() * mcMoth.p()); + float decayRadiusMC = std::hypot(mcDaug.vx() - mcMoth.vx(), mcDaug.vy() - mcMoth.vy()); + bool collisionIdCheck = false; + if (collision.has_mcCollision()) { + collisionIdCheck = collision.mcCollision().globalIndex() == mcDaug.mcCollisionId(); + } + outputKinkCandsMC(sigmaCand.xDecVtx(), sigmaCand.yDecVtx(), sigmaCand.zDecVtx(), + sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth(), + sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug(), + sigmaCand.dcaMothPv(), sigmaCand.dcaDaugPv(), sigmaCand.dcaKinkTopo(), + sigmaCand.mothSign(), + kinkDauTrack.tpcNSigmaPi(), kinkDauTrack.tpcNSigmaPr(), -999.f, + kinkDauTrack.tofNSigmaPi(), kinkDauTrack.tofNSigmaPr(), -999.f, + mcMoth.pdgCode(), mcDaug.pdgCode(), + mcMoth.pt(), mcMoth.pz(), massMC, decayRadiusMC, collisionIdCheck); + } + } + for (const auto& hadTrack : tracks) { if (hadTrack.globalIndex() == sigmaCand.trackDaugId()) { continue; @@ -429,7 +562,7 @@ struct sigmaHadCorrTask { continue; } - sigmaHadCand candidate; + SigmaHadCand candidate; candidate.sigmaCharge = sigmaCand.mothSign(); candidate.sigmaPx = sigmaCand.pxMoth(); candidate.sigmaPy = sigmaCand.pyMoth(); @@ -455,6 +588,7 @@ struct sigmaHadCorrTask { candidate.sigmaID = sigmaCand.trackMothId(); candidate.kinkDauID = sigmaCand.trackDaugId(); candidate.hadID = hadTrack.globalIndex(); + candidate.multiplicity = saveMultiplicity.value ? (useMultNTracksPV.value ? collision.multNTracksPV() : collision.numContrib()) : -1.f; float kStar = getKStar(sigmaPRecal[0], sigmaPRecal[1], sigmaPRecal[2], candidate.pxHad, candidate.pyHad, candidate.pzHad); if (kStar > cutMaxKStar) { @@ -466,33 +600,35 @@ struct sigmaHadCorrTask { if (hadTrack.hasTOF()) { rSigmaHad.fill(HIST("QA/h2TOFNSigmaHadVsPtHad"), candidate.ptHad(), candidate.nSigmaTOFHad); } - if (fillSparseInvMassKstar && !isMC) { - rSigmaHad.fill(HIST("hSparseSigmaHad"), - candidate.sigmaMass, - kStar, - candidate.sigmaCharge, - candidate.chargeHad, - candidate.sigmaDecRadius, - candidate.sigmaCosPA, - sigmaPtRecal); + if constexpr (!IsMC) { + if (fillSparseInvMassKstar) { + rSigmaHad.fill(HIST("hSparseSigmaHad"), + candidate.sigmaMass, + kStar, + candidate.sigmaCharge, + candidate.chargeHad, + candidate.sigmaDecRadius, + candidate.sigmaCosPA, + sigmaPtRecal); + } } sigmaHadCandidates.push_back(candidate); } } + return sigmaHadCandidates; } void processSameEvent(CollisionsFull const& collisions, aod::KinkCands const& kinkCands, TracksFull const& tracks) { for (auto const& collision : collisions) { - sigmaHadCandidates.clear(); - auto kinkCands_c = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision.globalIndex()); - auto tracks_c = tracks.sliceBy(tracksPerCollisionPreslice, collision.globalIndex()); - if (std::abs(collision.posZ()) > cutzvertex || !collision.sel8()) { + auto kinkCandsC = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision.globalIndex()); + auto tracksC = tracks.sliceBy(tracksPerCollisionPreslice, collision.globalIndex()); + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { continue; } rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); - fillTreeAndHistograms(kinkCands_c, tracks, tracks_c, collision, false); + auto sigmaHadCandidates = fillTreeAndHistograms(kinkCandsC, tracks, tracksC, collision); if (fillOutputTree) { // Fill output table for (const auto& candidate : sigmaHadCandidates) { @@ -510,75 +646,81 @@ struct sigmaHadCorrTask { candidate.pyHad, candidate.pzHad, candidate.nSigmaTPCHad, - candidate.nSigmaTOFHad); + candidate.nSigmaTOFHad, + candidate.multiplicity); } } } } - PROCESS_SWITCH(sigmaHadCorrTask, processSameEvent, "Process Same event", true); + PROCESS_SWITCH(SigmaHadCorr, processSameEvent, "Process Same event", true); // Processing Event Mixing SliceCache cache; - using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; + using BinningTypeNumContrib = ColumnBinningPolicy; + using BinningTypeMultNTracksPV = ColumnBinningPolicy; void processMixedEvent(const CollisionsFull& collisions, const aod::KinkCands& kinkCands, const TracksFull& tracks) { - for (auto const& [collision1, collision2] : - selfCombinations(colBinning, nEvtMixingBkg, -1, collisions, collisions)) { - if (collision1.index() == collision2.index()) - continue; - - sigmaHadCandidates.clear(); - if (std::abs(collision1.posZ()) > cutzvertex || !collision1.sel8()) { - continue; - } - if (std::abs(collision2.posZ()) > cutzvertex || !collision2.sel8()) { - continue; + if (useMultNTracksPV.value) { + for (auto const& [collision1, collision2] : + selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { + if (collision1.index() == collision2.index()) + continue; + if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) + continue; + if (std::abs(collision2.posZ()) > cutZVertex || !collision2.sel8()) + continue; + auto kinkCandsC1 = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision1.globalIndex()); + auto tracksC2 = tracks.sliceBy(tracksPerCollisionPreslice, collision2.globalIndex()); + auto sigmaHadCandidates = fillTreeAndHistograms(kinkCandsC1, tracks, tracksC2, collision1); + if (fillOutputTree) { + for (const auto& candidate : sigmaHadCandidates) { + outputDataTable(candidate.sigmaCharge, candidate.sigmaPx, candidate.sigmaPy, candidate.sigmaPz, + candidate.sigmaDauPx, candidate.sigmaDauPy, candidate.sigmaDauPz, + candidate.sigmaDecRadius, candidate.sigmaCosPA, candidate.chargeHad, + candidate.pxHad, candidate.pyHad, candidate.pzHad, + candidate.nSigmaTPCHad, candidate.nSigmaTOFHad, candidate.multiplicity); + } + } } - auto kinkCands_c1 = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision1.globalIndex()); - auto tracks_c1 = tracks.sliceBy(tracksPerCollisionPreslice, collision1.globalIndex()); - auto tracks_c2 = tracks.sliceBy(tracksPerCollisionPreslice, collision2.globalIndex()); - fillTreeAndHistograms(kinkCands_c1, tracks, tracks_c2, collision1, false); - - if (fillOutputTree) { - // Fill output table - for (const auto& candidate : sigmaHadCandidates) { - outputDataTable(candidate.sigmaCharge, - candidate.sigmaPx, - candidate.sigmaPy, - candidate.sigmaPz, - candidate.sigmaDauPx, - candidate.sigmaDauPy, - candidate.sigmaDauPz, - candidate.sigmaDecRadius, - candidate.sigmaCosPA, - candidate.chargeHad, - candidate.pxHad, - candidate.pyHad, - candidate.pzHad, - candidate.nSigmaTPCHad, - candidate.nSigmaTOFHad); + } else { + for (auto const& [collision1, collision2] : + selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { + if (collision1.index() == collision2.index()) + continue; + if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) + continue; + if (std::abs(collision2.posZ()) > cutZVertex || !collision2.sel8()) + continue; + auto kinkCandsC1 = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision1.globalIndex()); + auto tracksC2 = tracks.sliceBy(tracksPerCollisionPreslice, collision2.globalIndex()); + auto sigmaHadCandidates = fillTreeAndHistograms(kinkCandsC1, tracks, tracksC2, collision1); + if (fillOutputTree) { + for (const auto& candidate : sigmaHadCandidates) { + outputDataTable(candidate.sigmaCharge, candidate.sigmaPx, candidate.sigmaPy, candidate.sigmaPz, + candidate.sigmaDauPx, candidate.sigmaDauPy, candidate.sigmaDauPz, + candidate.sigmaDecRadius, candidate.sigmaCosPA, candidate.chargeHad, + candidate.pxHad, candidate.pyHad, candidate.pzHad, + candidate.nSigmaTPCHad, candidate.nSigmaTOFHad, candidate.multiplicity); + } } } } - LOG(debug) << "Processing mixed event"; } - PROCESS_SWITCH(sigmaHadCorrTask, processMixedEvent, "Process Mixed event", false); + PROCESS_SWITCH(SigmaHadCorr, processMixedEvent, "Process Mixed event", false); - void processSameEventMC(CollisionsFullMC const& collisions, aod::KinkCands const& kinkCands, TracksFullMC const& tracks, aod::McParticles const&) + void processSameEventMC(CollisionsFullMC const& collisions, aod::KinkCands const& kinkCands, TracksFullMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisions const&) { for (auto const& collision : collisions) { - sigmaHadCandidates.clear(); - auto kinkCands_c = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision.globalIndex()); - auto tracks_c = tracks.sliceBy(tracksMCPerCollisionPreslice, collision.globalIndex()); + auto kinkCandsC = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision.globalIndex()); + auto tracksC = tracks.sliceBy(tracksMCPerCollisionPreslice, collision.globalIndex()); - if (std::abs(collision.posZ()) > cutzvertex || !collision.sel8()) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { continue; } rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); - fillTreeAndHistograms(kinkCands_c, tracks, tracks_c, collision, true); + auto sigmaHadCandidates = fillTreeAndHistograms(kinkCandsC, tracks, tracksC, collision); for (const auto& candidate : sigmaHadCandidates) { auto mcLabelSigma = tracks.rawIteratorAt(candidate.sigmaID); auto mcLabelSigmaDau = tracks.rawIteratorAt(candidate.kinkDauID); @@ -594,6 +736,8 @@ struct sigmaHadCorrTask { auto pdgSigma = mcPartSigma.pdgCode(); auto pdgSigmaDau = mcLabelSigmaDau.has_mcParticle() ? mcPartSigmaDau.pdgCode() : -999; auto pdgHad = mcLabelHad.has_mcParticle() ? mcPartHad.pdgCode() : -999; + auto sigmaMotherPDG = getSigmaMotherPDG(mcPartSigma, mcParticles); + auto sigmaPartonicMotherPDG = getPartonicMotherPDG(mcPartSigma, mcParticles); float sigmaPtGen = std::hypot(mcPartSigma.px(), mcPartSigma.py()); float hadPtGen = std::hypot(mcPartHad.px(), mcPartHad.py()); @@ -631,103 +775,166 @@ struct sigmaHadCorrTask { candidate.pzHad, candidate.nSigmaTPCHad, candidate.nSigmaTOFHad, + candidate.multiplicity, pdgSigma, pdgSigmaDau, pdgHad, + sigmaMotherPDG, + sigmaPartonicMotherPDG, sigmaPtGen, hadPtGen, kStarGen); } } } - } - PROCESS_SWITCH(sigmaHadCorrTask, processSameEventMC, "Process Same event MC", false); - void processMixedEventMC(const CollisionsFullMC& collisions, const aod::KinkCands& kinkCands, const TracksFullMC& tracks, const aod::McParticles&) - { - for (auto const& [collision1, collision2] : - selfCombinations(colBinning, nEvtMixingBkg, -1, collisions, collisions)) { - if (collision1.index() == collision2.index()) + // all generated Sigma -> chargedDau + neutralDau decays + int pdgChargedDauAbs = doSigmaMinus ? PDG_t::kPiPlus : PDG_t::kProton; + for (const auto& mcPart : mcParticles) { + int pdgMothAbs = std::abs(mcPart.pdgCode()); + bool isValidMother = doSigmaMinus ? (pdgMothAbs == PDG_t::kSigmaMinus || pdgMothAbs == PDG_t::kSigmaPlus) : (pdgMothAbs == PDG_t::kSigmaPlus); + if (!isValidMother) { continue; - - sigmaHadCandidates.clear(); - if (std::abs(collision1.posZ()) > cutzvertex || !collision1.sel8()) { + } + if (std::abs(mcPart.y()) > cutRapMotherMC) { continue; } - if (std::abs(collision2.posZ()) > cutzvertex || !collision2.sel8()) { + if (mcPart.pt() < cutPtGenMC) { continue; } - auto kinkCands_c1 = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision1.globalIndex()); - auto tracks_c1 = tracks.sliceBy(tracksPerCollisionPreslice, collision1.globalIndex()); - auto tracks_c2 = tracks.sliceBy(tracksPerCollisionPreslice, collision2.globalIndex()); - fillTreeAndHistograms(kinkCands_c1, tracks, tracks_c2, collision1, true); - - for (const auto& candidate : sigmaHadCandidates) { - auto mcLabelSigma = tracks.rawIteratorAt(candidate.sigmaID); - auto mcLabelSigmaDau = tracks.rawIteratorAt(candidate.kinkDauID); - auto mcLabelHad = tracks.rawIteratorAt(candidate.hadID); - - if (!mcLabelSigma.has_mcParticle() || !mcLabelSigmaDau.has_mcParticle() || !mcLabelHad.has_mcParticle()) { - continue; // Skip candidates where MC truth is not available + bool hasChargedDaughter = false; + std::array genDecVtx{-999.f, -999.f, -999.f}; + int daugPdgCode = 0; + for (const auto& daughter : mcPart.daughters_as()) { + if (std::abs(daughter.pdgCode()) == pdgChargedDauAbs) { + hasChargedDaughter = true; + genDecVtx = {daughter.vx(), daughter.vy(), daughter.vz()}; + daugPdgCode = daughter.pdgCode(); + break; } + } + if (!hasChargedDaughter) { + continue; + } + float massMC = std::sqrt(mcPart.e() * mcPart.e() - mcPart.p() * mcPart.p()); + float decayRadiusMC = std::hypot(genDecVtx[0] - mcPart.vx(), genDecVtx[1] - mcPart.vy()); + outputKinkCandsMC(-999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + mcPart.pdgCode() > 0 ? 1 : -1, + -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + mcPart.pdgCode(), daugPdgCode, + mcPart.pt(), mcPart.pz(), massMC, decayRadiusMC, false); + } + } + PROCESS_SWITCH(SigmaHadCorr, processSameEventMC, "Process Same event MC", false); - auto mcPartSigma = mcLabelSigma.mcParticle_as(); - auto mcPartSigmaDau = mcLabelSigmaDau.mcParticle_as(); - auto mcPartHad = mcLabelHad.mcParticle_as(); - auto pdgSigma = mcPartSigma.pdgCode(); - auto pdgSigmaDau = mcLabelSigmaDau.has_mcParticle() ? mcPartSigmaDau.pdgCode() : -999; - auto pdgHad = mcLabelHad.has_mcParticle() ? mcPartHad.pdgCode() : -999; - float sigmaPtGen = std::hypot(mcPartSigma.px(), mcPartSigma.py()); - float hadPtGen = std::hypot(mcPartHad.px(), mcPartHad.py()); - float kStarGen = getKStar(mcPartSigma.px(), mcPartSigma.py(), mcPartSigma.pz(), mcPartHad.px(), mcPartHad.py(), mcPartHad.pz()); - - if (fillSparseInvMassKstar) { - auto sigmaMomForKstar = getSigmaMomentumForKstar(candidate.sigmaPx, candidate.sigmaPy, candidate.sigmaPz, - candidate.sigmaDauPx, candidate.sigmaDauPy, candidate.sigmaDauPz); - float kStarRec = getKStar(sigmaMomForKstar[0], sigmaMomForKstar[1], sigmaMomForKstar[2], candidate.pxHad, candidate.pyHad, candidate.pzHad); - float sigmaPtUsed = std::hypot(sigmaMomForKstar[0], sigmaMomForKstar[1]); - rSigmaHad.fill(HIST("hSparseSigmaHadMC"), - candidate.sigmaMass, - kStarRec, - candidate.sigmaCharge, - candidate.chargeHad, - candidate.sigmaDecRadius, - candidate.sigmaCosPA, - sigmaPtUsed, - kStarGen); + void processMixedEventMC(const CollisionsFullMC& collisions, const aod::KinkCands& kinkCands, const TracksFullMC& tracks, const aod::McParticles& mcParticles, aod::McCollisions const&) + { + if (useMultNTracksPV.value) { + for (auto const& [collision1, collision2] : + selfCombinations(BinningTypeMultNTracksPV{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { + if (collision1.index() == collision2.index()) + continue; + if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) + continue; + if (std::abs(collision2.posZ()) > cutZVertex || !collision2.sel8()) + continue; + auto kinkCandsC1 = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision1.globalIndex()); + auto tracksC2 = tracks.sliceBy(tracksPerCollisionPreslice, collision2.globalIndex()); + auto sigmaHadCandidates = fillTreeAndHistograms(kinkCandsC1, tracks, tracksC2, collision1); + for (const auto& candidate : sigmaHadCandidates) { + auto mcLabelSigma = tracks.rawIteratorAt(candidate.sigmaID); + auto mcLabelSigmaDau = tracks.rawIteratorAt(candidate.kinkDauID); + auto mcLabelHad = tracks.rawIteratorAt(candidate.hadID); + if (!mcLabelSigma.has_mcParticle() || !mcLabelSigmaDau.has_mcParticle() || !mcLabelHad.has_mcParticle()) + continue; + auto mcPartSigma = mcLabelSigma.mcParticle_as(); + auto mcPartSigmaDau = mcLabelSigmaDau.mcParticle_as(); + auto mcPartHad = mcLabelHad.mcParticle_as(); + auto pdgSigma = mcPartSigma.pdgCode(); + auto pdgSigmaDau = mcLabelSigmaDau.has_mcParticle() ? mcPartSigmaDau.pdgCode() : -999; + auto pdgHad = mcLabelHad.has_mcParticle() ? mcPartHad.pdgCode() : -999; + auto sigmaMotherPDG = getSigmaMotherPDG(mcPartSigma, mcParticles); + auto sigmaPartonicMotherPDG = getPartonicMotherPDG(mcPartSigma, mcParticles); + float sigmaPtGen = std::hypot(mcPartSigma.px(), mcPartSigma.py()); + float hadPtGen = std::hypot(mcPartHad.px(), mcPartHad.py()); + float kStarGen = getKStar(mcPartSigma.px(), mcPartSigma.py(), mcPartSigma.pz(), mcPartHad.px(), mcPartHad.py(), mcPartHad.pz()); + if (fillSparseInvMassKstar) { + auto sigmaMomForKstar = getSigmaMomentumForKstar(candidate.sigmaPx, candidate.sigmaPy, candidate.sigmaPz, + candidate.sigmaDauPx, candidate.sigmaDauPy, candidate.sigmaDauPz); + float kStarRec = getKStar(sigmaMomForKstar[0], sigmaMomForKstar[1], sigmaMomForKstar[2], candidate.pxHad, candidate.pyHad, candidate.pzHad); + float sigmaPtUsed = std::hypot(sigmaMomForKstar[0], sigmaMomForKstar[1]); + rSigmaHad.fill(HIST("hSparseSigmaHadMC"), candidate.sigmaMass, kStarRec, candidate.sigmaCharge, + candidate.chargeHad, candidate.sigmaDecRadius, candidate.sigmaCosPA, sigmaPtUsed, kStarGen); + } + if (fillOutputTree) { + outputDataTableMC(candidate.sigmaCharge, candidate.sigmaPx, candidate.sigmaPy, candidate.sigmaPz, + candidate.sigmaDauPx, candidate.sigmaDauPy, candidate.sigmaDauPz, + candidate.sigmaDecRadius, candidate.sigmaCosPA, candidate.chargeHad, + candidate.pxHad, candidate.pyHad, candidate.pzHad, + candidate.nSigmaTPCHad, candidate.nSigmaTOFHad, candidate.multiplicity, + pdgSigma, pdgSigmaDau, pdgHad, sigmaMotherPDG, sigmaPartonicMotherPDG, + sigmaPtGen, hadPtGen, kStarGen); + } } - - if (fillOutputTree) { - outputDataTableMC(candidate.sigmaCharge, - candidate.sigmaPx, - candidate.sigmaPy, - candidate.sigmaPz, - candidate.sigmaDauPx, - candidate.sigmaDauPy, - candidate.sigmaDauPz, - candidate.sigmaDecRadius, - candidate.sigmaCosPA, - candidate.chargeHad, - candidate.pxHad, - candidate.pyHad, - candidate.pzHad, - candidate.nSigmaTPCHad, - candidate.nSigmaTOFHad, - pdgSigma, - pdgSigmaDau, - pdgHad, - sigmaPtGen, - hadPtGen, - kStarGen); + } + } else { + for (auto const& [collision1, collision2] : + selfCombinations(BinningTypeNumContrib{{cfgVtxBins, cfgMultBins}}, nEvtMixingBkg, -1, collisions, collisions)) { + if (collision1.index() == collision2.index()) + continue; + if (std::abs(collision1.posZ()) > cutZVertex || !collision1.sel8()) + continue; + if (std::abs(collision2.posZ()) > cutZVertex || !collision2.sel8()) + continue; + auto kinkCandsC1 = kinkCands.sliceBy(kinkCandsPerCollisionPreslice, collision1.globalIndex()); + auto tracksC2 = tracks.sliceBy(tracksPerCollisionPreslice, collision2.globalIndex()); + auto sigmaHadCandidates = fillTreeAndHistograms(kinkCandsC1, tracks, tracksC2, collision1); + for (const auto& candidate : sigmaHadCandidates) { + auto mcLabelSigma = tracks.rawIteratorAt(candidate.sigmaID); + auto mcLabelSigmaDau = tracks.rawIteratorAt(candidate.kinkDauID); + auto mcLabelHad = tracks.rawIteratorAt(candidate.hadID); + if (!mcLabelSigma.has_mcParticle() || !mcLabelSigmaDau.has_mcParticle() || !mcLabelHad.has_mcParticle()) + continue; + auto mcPartSigma = mcLabelSigma.mcParticle_as(); + auto mcPartSigmaDau = mcLabelSigmaDau.mcParticle_as(); + auto mcPartHad = mcLabelHad.mcParticle_as(); + auto pdgSigma = mcPartSigma.pdgCode(); + auto pdgSigmaDau = mcLabelSigmaDau.has_mcParticle() ? mcPartSigmaDau.pdgCode() : -999; + auto pdgHad = mcLabelHad.has_mcParticle() ? mcPartHad.pdgCode() : -999; + auto sigmaMotherPDG = getSigmaMotherPDG(mcPartSigma, mcParticles); + auto sigmaPartonicMotherPDG = getPartonicMotherPDG(mcPartSigma, mcParticles); + float sigmaPtGen = std::hypot(mcPartSigma.px(), mcPartSigma.py()); + float hadPtGen = std::hypot(mcPartHad.px(), mcPartHad.py()); + float kStarGen = getKStar(mcPartSigma.px(), mcPartSigma.py(), mcPartSigma.pz(), mcPartHad.px(), mcPartHad.py(), mcPartHad.pz()); + if (fillSparseInvMassKstar) { + auto sigmaMomForKstar = getSigmaMomentumForKstar(candidate.sigmaPx, candidate.sigmaPy, candidate.sigmaPz, + candidate.sigmaDauPx, candidate.sigmaDauPy, candidate.sigmaDauPz); + float kStarRec = getKStar(sigmaMomForKstar[0], sigmaMomForKstar[1], sigmaMomForKstar[2], candidate.pxHad, candidate.pyHad, candidate.pzHad); + float sigmaPtUsed = std::hypot(sigmaMomForKstar[0], sigmaMomForKstar[1]); + rSigmaHad.fill(HIST("hSparseSigmaHadMC"), candidate.sigmaMass, kStarRec, candidate.sigmaCharge, + candidate.chargeHad, candidate.sigmaDecRadius, candidate.sigmaCosPA, sigmaPtUsed, kStarGen); + } + if (fillOutputTree) { + outputDataTableMC(candidate.sigmaCharge, candidate.sigmaPx, candidate.sigmaPy, candidate.sigmaPz, + candidate.sigmaDauPx, candidate.sigmaDauPy, candidate.sigmaDauPz, + candidate.sigmaDecRadius, candidate.sigmaCosPA, candidate.chargeHad, + candidate.pxHad, candidate.pyHad, candidate.pzHad, + candidate.nSigmaTPCHad, candidate.nSigmaTOFHad, candidate.multiplicity, + pdgSigma, pdgSigmaDau, pdgHad, sigmaMotherPDG, sigmaPartonicMotherPDG, + sigmaPtGen, hadPtGen, kStarGen); + } } } } - LOG(debug) << "Processing mixed event MC"; } - PROCESS_SWITCH(sigmaHadCorrTask, processMixedEventMC, "Process Mixed event MC", false); + PROCESS_SWITCH(SigmaHadCorr, processMixedEventMC, "Process Mixed event MC", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/TableProducer/Strangeness/sigmaminustask.cxx b/PWGLF/TableProducer/Strangeness/sigmaminustask.cxx index 828ef0240cd..cd4c6f690c3 100644 --- a/PWGLF/TableProducer/Strangeness/sigmaminustask.cxx +++ b/PWGLF/TableProducer/Strangeness/sigmaminustask.cxx @@ -35,20 +35,20 @@ #include #include #include +#include #include #include #include #include -#include - #include #include #include #include #include #include +#include #include #include #include diff --git a/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx new file mode 100644 index 00000000000..9d342f942f8 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/sigmaplusbuilder.cxx @@ -0,0 +1,2183 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file sigmaplusbuilder.cxx +/// \brief Task for Sigma+ -> p + pi0 reconstruction, pi0 reconstructed via one converted photon (PCM) +/// \author Henrik Fribert (TUM) + +#include "PWGEM/Dilepton/Utils/PairUtilities.h" +#include "PWGEM/PhotonMeson/Utils/PCMUtilities.h" +#include "PWGLF/DataModel/LFKinkDecayTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/svPoolCreator.h" + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/TPCVDriftManager.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using TracksFull = soa::Join; +using TracksFullMC = soa::Join; +using CollisionsFull = soa::Join; +using CollisionsFullMC = soa::Join; + +struct Sigmaplusbuilder { + + // event selection + Configurable cutZVertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; + + // photon (PCM) selection + Configurable photonMaxMass{"photonMaxMass", 0.05, "Max photon mass (GeV/c^2)"}; + Configurable photonMinRapidity{"photonMinRapidity", -1.0, "Min photon rapidity"}; + Configurable photonMaxRapidity{"photonMaxRapidity", 1.0, "Max photon rapidity"}; + Configurable photonDauEtaMin{"photonDauEtaMin", -1.0, "Min eta of photon daughter tracks"}; + Configurable photonDauEtaMax{"photonDauEtaMax", 1.0, "Max eta of photon daughter tracks"}; + Configurable photonMinRadius{"photonMinRadius", 3.0, "Min photon conversion radius (cm)"}; + Configurable photonMaxRadius{"photonMaxRadius", 115., "Max photon conversion radius (cm)"}; + Configurable photonMinV0cospa{"photonMinV0cospa", 0.9, "Min photon cosine of pointing angle to the PV"}; + Configurable photonMaxDCAV0Dau{"photonMaxDCAV0Dau", 1.5, "Max DCA between photon daughters (cm)"}; + Configurable photonMaxOpeningAngle{"photonMaxOpeningAngle", 0.4, "Max opening angle between the photon's e+/e- daughter momenta (rad)"}; + Configurable photonMaxDeltaTheta{"photonMaxDeltaTheta", 0.15, "Max |theta_pos - theta_neg| of the photon's daughter tracks (rad)"}; + Configurable photonMaxQt{"photonMaxQt", 0.05, "Max Armenteros qT for photons (GeV/c)"}; + Configurable photonMaxAlpha{"photonMaxAlpha", 0.95, "Max |Armenteros alpha| for photons"}; + Configurable photonDauMinTPCNSigmaEl{"photonDauMinTPCNSigmaEl", -3., "Min TPC nSigma_el of the photon daughters"}; + Configurable photonDauMaxTPCNSigmaEl{"photonDauMaxTPCNSigmaEl", 3., "Max TPC nSigma_el of the photon daughters"}; + Configurable photonDauMaxPt{"photonDauMaxPt", 0.5, "Max pT of the photon daughters (GeV/c)"}; + Configurable photonDauMinTpcNCls{"photonDauMinTpcNCls", 30, "Min number of found TPC clusters for the photon daughter tracks"}; + + // photon daughters paired by this task instead of taken from V0Datas (V0Datas assumes photons from primary vertex) + Configurable useCustomVertexer{"useCustomVertexer", false, "Pair the photon's e+/e- daughter tracks in this task instead of reading V0Datas"}; + Configurable photonSkipAmbiTracks{"photonSkipAmbiTracks", false, "Skip ambiguous tracks when pairing photon daughters"}; + Configurable photonPoolTimeMarginNS{"photonPoolTimeMarginNS", 800., "Time margin (ns) added to a daughter track's time range when matching it to collisions"}; + Configurable photonMaxDXYIni{"photonMaxDXYIni", 4., "Max xy distance (cm) between the two daughter tracks at the start of the photon vertex fit"}; + Configurable photonMaxCircleTouchDist{"photonMaxCircleTouchDist", 4., "Max |circle centre distance - (R1 + R2)| (cm) of the two daughter tracks, applied before the fit"}; + + // proton selection + Configurable protonMinPt{"protonMinPt", 0.4, "Minimum proton pT (GeV/c)"}; + Configurable protonMaxEta{"protonMaxEta", 0.9, "Maximum |eta| for proton track"}; + Configurable protonMinTpcNCls{"protonMinTpcNCls", 80, "Min number of found TPC clusters for the proton track"}; + Configurable protonMaxTPCNSigma{"protonMaxTPCNSigma", 4, "Max |TPC nSigma_pr| for proton"}; + Configurable protonMaxTOFNSigma{"protonMaxTOFNSigma", 4, "Max |TOF nSigma_pr| for proton, if TOF available"}; + Configurable protonPtMinRequireTOF{"protonPtMinRequireTOF", 0.75, "Above this pT, require TOF PID for proton"}; + Configurable protonRequireTofHit{"protonRequireTofHit", false, "Above protonPtMinRequireTOF, reject a proton with no TOF hit at all"}; + Configurable protonMinDcaToPV{"protonMinDcaToPV", 0.005, "Min DCAxy of the proton track to the PV (cm)"}; + Configurable protonMaxDcaToPV{"protonMaxDcaToPV", 5.0, "Max DCAxy of the proton track to the PV (cm)"}; + Configurable protonMaxDcaZToPV{"protonMaxDcaZToPV", 0.3, "Max |DCAz| of the proton track to the PV (cm)"}; + + // proton-photon candidate selection + Configurable candVertexProtonWeight{"candVertexProtonWeight", 1.0, "Sigma+ vertex between the proton's (1) and the photon's (0) point of closest approach"}; + Configurable candMaxDcaProtonGamma{"candMaxDcaProtonGamma", 1.5, "Max DCA between proton and photon at the fitted vertex (cm)"}; + Configurable candMaxDcaToPV{"candMaxDcaToPV", 20., "Max DCA of the candidate's total (reconstructed) momentum line to the PV (cm)"}; + Configurable candRejectNegRootCenter{"candRejectNegRootCenter", true, "Reject candidates with rootCenter<0"}; + Configurable candMaxRootCenter{"candMaxRootCenter", 15, "Max rootCenter=-coefB/(2*coefA) (GeV/c)"}; + Configurable candMinAntiSigmaPointingAngle{"candMinAntiSigmaPointingAngle", 0.0, "Min AntiSigmaPointingAngle (rad)"}; + Configurable candMaxAntiSigmaPointingAngle{"candMaxAntiSigmaPointingAngle", 10., "Max AntiSigmaPointingAngle (rad)"}; + Configurable candMaxSigmaMass{"candMaxSigmaMass", 1.35, "Max reconstructed Sigma+ candidate mass (GeV/c^2)"}; + Configurable candMaxRapidity{"candMaxRapidity", 0.9, "Max |rapidity| of the reconstructed Sigma+ candidate"}; + Configurable candMinRadius{"candMinRadius", 1.0, "Min candidate decay radius (cm)"}; + Configurable candMaxRadius{"candMaxRadius", 100., "Max candidate decay radius (cm)"}; + Configurable candMinFlightDistance{"candMinFlightDistance", 0.0, "Min 3D distance from PV to candidate decay vertex (cm)"}; + Configurable candMaxFlightDistance{"candMaxFlightDistance", 250.0, "Max 3D distance from PV to candidate decay vertex (cm)"}; + Configurable candMaxPhotonOpeningAngle{"candMaxPhotonOpeningAngle", 3.15, "Max photon opening angle (rad), recomputed from the daughters' track momenta"}; + Configurable candMaxPhotonPointingAngle{"candMaxPhotonPointingAngle", 0.5, "Max angle between the photon momentum and the decay-vertex-to-conversion-point line (rad)"}; + Configurable candMaxPhotonDcaToPV{"candMaxPhotonDcaToPV", 250.0, "Max DCA of the photon's flight line to the PV (cm)"}; + Configurable candMaxPhotonDcaZToPV{"candMaxPhotonDcaZToPV", 2.0, "Max |DCAz| of the photon's flight line to the PV at its transverse DCA (cm), applied to the photon before the pairing with the proton"}; + Configurable candDeduplicatePhotons{"candDeduplicatePhotons", true, "Per timeframe, write only the candidate with the smallest DCA to PV among candidates sharing the same photon"}; + + // tilt search: if the measured flight direction gives no real solution for the missing photon, + // tilted flight directions are tried starting with the smallest tilt + Configurable candMaxTilt{"candMaxTilt", 0.05, "Max tilt of the flight direction searched for a real root (rad)"}; + Configurable candTiltStep{"candTiltStep", 0.005, "Tilt step between the rings of tried flight directions (rad)"}; + Configurable candTiltNAzimuth{"candTiltNAzimuth", 36, "Flight directions tried per ring"}; + + // MC + Configurable cutRapMotherMC{"cutRapMotherMC", 1.0f, "Rapidity cut for generated mother Sigma+ in MC"}; + Configurable cutPtGenMC{"cutPtGenMC", 0.5f, "Minimum pT for generated Sigma+ in MC"}; + + Configurable fillSlimTables{"fillSlimTables", false, "write the slim candidate tables instead of the full ones"}; + + // BDT score (MlScore column) of every written candidate, inputs in the order of the training (see computeMlScore) + Configurable applyMl{"applyMl", false, "Compute the BDT score of every written candidate (otherwise MlScore = -1)"}; + Configurable mlLoadModelFromCcdb{"mlLoadModelFromCcdb", true, "Load the ONNX model from the CCDB instead of a local file"}; + Configurable mlModelPathCcdb{"mlModelPathCcdb", "Users/h/hfribert/SigmaPlusPcm/BDT_nominalB", "CCDB path of the ONNX model (nominal field: .../BDT_nominalB, low field: .../BDT_lowB)"}; + Configurable mlOnnxFileName{"mlOnnxFileName", "model.onnx", "ONNX file name (local file, or name of the file retrieved from the CCDB)"}; + Configurable mlTimestampCcdb{"mlTimestampCcdb", -1, "CCDB timestamp of the ONNX model (-1: now)"}; + + Configurable ccdbPath{"ccdbPath", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "CCDB path of the material budget LUT"}; + + Produces sigmaPlusCands; + Produces sigmaPlusCandsMC; + Produces slimSigmaPlusCands; + Produces slimSigmaPlusCandsMC; + + Service ccdb{}; + o2::vertexing::DCAFitterN<2> fitter; + int mRunNumber = 0; + float mBz = 0; + o2::base::MatLayerCylSet* mLut = nullptr; + o2::aod::common::TPCVDriftManager mVDriftMgr; + o2::analysis::MlResponse mlResponse; + + // daughter pairing (electrons and positrons with the range of collisions each is compatible with) + svPoolCreator svPhotonPoolCreator{PDG_t::kElectron, PDG_t::kPositron}; + std::vector mElectronPool; + std::vector mPositronPool; + std::vector mGoodCollision; // collisions passing the event selection + + // values of a candidate, kept until the end of the timeframe and then written to the tables + struct SigmaPlusCandidate { + uint64_t photonId = 0; + int collisionId = -1; + int protonTrackId = -1; + int photonPosTrackId = -1; + int photonNegTrackId = -1; + bool isSignal = false; + int photonLegsWithoutIts = 0; + int matchedSigmaId = -1; + std::array decVtx{}; + float radius = 0.f; + float flightDistance = 0.f; + float dcaProtonGamma = 0.f; + float dcaToPV = 0.f; + float tiltAngle = 0.f; + float rootCenter = 0.f; + float antiSigmaPointingAngle = 0.f; + std::array pProton{}; + std::array pGamma1{}; + std::array pGamma2{}; + float protonTpcNSigma = 0.f; + float protonTofNSigma = 0.f; + int protonSign = 0; + uint8_t protonItsNCls = 0; + int16_t protonTpcNCls = 0; + float protonDcaXY = 0.f; + float protonDcaZ = 0.f; + float photonMass = 0.f; + float photonAlpha = 0.f; + float photonQt = 0.f; + float photonRadius = 0.f; + float photonOpeningAngle = 0.f; + float photonPointingAngle = 0.f; + float photonDcaDau = 0.f; + float photonCosPAToPV = 0.f; + float photonDcaXYToPV = 0.f; + float photonDcaZToPV = 0.f; + float photonPsiPair = 0.f; + float posTpcNSigmaEl = 0.f; + float negTpcNSigmaEl = 0.f; + uint8_t posItsNCls = 0; + uint8_t negItsNCls = 0; + int16_t posTpcNCls = 0; + int16_t negTpcNCls = 0; + int16_t posTpcNClsFindable = 0; + int16_t negTpcNClsFindable = 0; + float posTpcChi2NCl = 0.f; + float negTpcChi2NCl = 0.f; + float posDcaXY = 0.f; + float posDcaZ = 0.f; + float negDcaXY = 0.f; + float negDcaZ = 0.f; + // MC + bool collisionIdCheck = false; + bool protonIsSignal = false; + bool photonIsSignal = false; + std::array decVtxMC{}; + std::array pProtonMC{}; + std::array pGammaMC{}; + std::array pSigmaPlusMC{}; + float decayRadiusMC = -999.f; + float massMC = -999.f; + }; + std::vector mCandidatesOfTimeframe; + + // MC: generated Sigma+ -> p pi0 in acceptance of current timeframe and whether a true candidate of it was written + std::unordered_map mGenSigmaWritten; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Preslice v0PerCollision = aod::v0data::collisionId; + Preslice tracksPerCollision = aod::track::collisionId; + Preslice tracksPerCollisionMC = aod::track::collisionId; + + void init(InitContext const&) + { + ccdb->setURL(ccdbPath); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + + fitter.setPropagateToPCA(true); + fitter.setMaxR(200.); + fitter.setMinParamChange(1e-3); + fitter.setMinRelChi2Change(0.9); + fitter.setMaxDZIni(1e9); + fitter.setMaxDXYIni(1e9); + fitter.setMaxChi2(1e9); + fitter.setUseAbsDCA(true); + + mVDriftMgr.init(&ccdb->instance()); + if (applyMl) { + // one model with two classes (background, signal) and no cut on the scores + const std::array noCuts{0., 0.}; + mlResponse.configure({0., 1.e6}, LabeledArray(noCuts.data(), 1, 2), {o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, 2); + if (mlLoadModelFromCcdb) { + o2::ccdb::CcdbApi ccdbApi; + ccdbApi.init(ccdbPath.value); + mlResponse.setModelPathsCCDB({mlOnnxFileName.value}, ccdbApi, {mlModelPathCcdb.value}, mlTimestampCcdb.value); + } else { + mlResponse.setModelPathsLocal({mlOnnxFileName.value}); + } + mlResponse.init(); + } + svPhotonPoolCreator.setTimeMargin(photonPoolTimeMarginNS); + if (photonSkipAmbiTracks) { + svPhotonPoolCreator.setSkipAmbiTracks(); + } + + const AxisSpec axisVertexZ{100, -15., 15., "vrtx_{Z} (cm)"}; + + const AxisSpec axisPhotonSel{14, -0.5, 13.5, "selection step"}; + const AxisSpec axisPhotonMass{200, 0., 0.3, "m_{#gamma} (GeV/c^{2})"}; + const AxisSpec axisPhotonPt{100, 0., 5., "#it{p}_{T,#gamma} (GeV/c)"}; + const AxisSpec axisPhotonRadius{200, 0., 200., "R_{conv} (cm)"}; + const AxisSpec axisAlpha{100, -1., 1., "#alpha_{AP}"}; + const AxisSpec axisQt{100, 0., 0.3, "q_{T,AP} (GeV/c)"}; + const AxisSpec axisConvXY{200, -100., 100., "conv. point (cm)"}; + const AxisSpec axisNSigmaEl{100, -10., 10., "n#sigma_{el}"}; + const AxisSpec axisTpcNCls{160, -0.5, 159.5, "TPC clusters"}; + const AxisSpec axisPhotonOpeningAngle{180, 0., 3.15, "opening angle (rad)"}; + const AxisSpec axisPhotonDeltaTheta{200, -1., 1., "#Delta#theta (rad)"}; + const AxisSpec axisPhotonDcaV0Dau{200, 0., 10., "DCA between photon daughters (cm)"}; + const AxisSpec axisCollisionsPerPhoton{100, 0.5, 100.5, "collisions a photon is kept in"}; + const AxisSpec axisTpcTimeRangeNColl{200, 0.5, 200.5, "collisions in the TPC time range of a TPC-only daughter"}; + + const AxisSpec axisProtonSel{7, -0.5, 6.5, "selection step"}; + const AxisSpec axisProtonPt{100, 0., 5., "#it{p}_{T,p} (GeV/c)"}; + const AxisSpec axisNSigma{100, -5., 5., "n#sigma"}; + const AxisSpec axisProtonDcaToPV{1000, 0., 5., "DCA_{xy,p} to PV (cm)"}; + + const AxisSpec axisCandSel{15, -0.5, 14.5, "selection step"}; + const AxisSpec axisPhotonLegType{3, -0.5, 2.5, "photon daughters without ITS"}; + const AxisSpec axisCandPhotonDcaToPV{250, 0., 250., "DCA_{#gamma-line} to PV (cm)"}; + const AxisSpec axisDca{100, 0., 10., "DCA(p,#gamma) (cm)"}; + const AxisSpec axisDcaToPV{500, 0., 0.5, "DCA_{cand} to PV (cm)"}; + const AxisSpec axisCandRadius{200, 0., 200., "R_{dec} (cm)"}; + const AxisSpec axisFlightDistance{250, 0., 250., "|SV-PV| (cm)"}; + const AxisSpec axisTilt{200, 0., 0.5, "flight direction tilt for a real root (rad)"}; + const AxisSpec axisRootCenter{400, -10., 10., "-b/(2a) (GeV/#it{c})"}; + const AxisSpec axisAntiSigmaPA{180, 0., 3.15, "AntiPA (rad)"}; + const AxisSpec axisMassSigma{200, 1.0, 1.4, "m_{p#gamma#gamma} (GeV/#it{c}^{2})"}; + const AxisSpec axisRapidity{200, -2., 2., "y_{#Sigma^{+}}"}; + const AxisSpec axisSigmaPt{100, 0., 6., "#it{p}_{T,#Sigma^{+}} (GeV/#it{c})"}; + const AxisSpec axisCandidatesPerPhoton{50, 0.5, 50.5, "candidates sharing one photon"}; + + const AxisSpec axisSigmaSel{6, -0.5, 5.5, "step"}; + + const std::vector photonSteps{"All", "Neg eta", "Pos eta", "TPC clusters", "TPC nSigma_{el}, p_{T}", "DCA daughters", "Radius", + "Opening angle", "Delta theta", "CosPA, DCA_{z}", "Rapidity", "Qt", "Alpha", "Mass"}; + const std::vector protonSteps{"All", "Pt", "Eta", "TPC clusters", "TPC nSigma", "TOF nSigma", "DCA to PV"}; + const std::vector candSteps{"All pairs", "Autocorrelation", "Vertex fit", "DCA(p,#gamma)", "Radius", "Flight distance", "Real root", + "Valid root", "DCA to PV", "Mass", "Rapidity", "Photon opening angle", "Photon pointing angle", "Photon DCA to PV", "Written"}; + const std::vector sigmaSteps{"Generated", "Proton reconstructed", "Proton selected", "Photon daughters reconstructed", "Photon selected", "Candidate written"}; + auto setBinLabels = [](TAxis* axis, const std::vector& labels) { + for (size_t i = 0; i < labels.size(); ++i) { + axis->SetBinLabel(i + 1, labels[i].c_str()); + } + }; + histos.add("hVertexZ", "hVertexZ", kTH1F, {axisVertexZ}); + + setBinLabels(histos.add("Photon/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisPhotonSel})->GetXaxis(), photonSteps); + histos.add("Photon/Inclusive/h2TPCNClsPosVsPt", "h2TPCNClsPosVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); + histos.add("Photon/Inclusive/h2TPCNClsNegVsPt", "h2TPCNClsNegVsPt", kTH2F, {axisPhotonPt, axisTpcNCls}); + histos.add("Photon/Inclusive/h2TPCNSigmaElPosVsPt", "h2TPCNSigmaElPosVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); + histos.add("Photon/Inclusive/h2TPCNSigmaElNegVsPt", "h2TPCNSigmaElNegVsPt", kTH2F, {axisPhotonPt, axisNSigmaEl}); + histos.add("Photon/Inclusive/hDcaV0Daughters", "hDcaV0Daughters", kTH1F, {axisPhotonDcaV0Dau}); + histos.add("Photon/Inclusive/hOpeningAngle", "hOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Photon/Inclusive/hDeltaTheta", "hDeltaTheta", kTH1F, {axisPhotonDeltaTheta}); + histos.add("Photon/Inclusive/hMass", "hMass", kTH1F, {axisPhotonMass}); + histos.add("Photon/Inclusive/hPt", "hPt", kTH1F, {axisPhotonPt}); + histos.add("Photon/Inclusive/hRadius", "hRadius", kTH1F, {axisPhotonRadius}); + histos.add("Photon/Inclusive/h2ArmenterosPodolanski", "h2ArmenterosPodolanski", kTH2F, {axisAlpha, axisQt}); + histos.add("Photon/Inclusive/h2ConvPointXY", "h2ConvPointXY", kTH2F, {axisConvXY, axisConvXY}); + histos.add("Photon/Inclusive/hCollisionsPerPhoton", "hCollisionsPerPhoton", kTH1F, {axisCollisionsPerPhoton}); + + setBinLabels(histos.add("Proton/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisProtonSel})->GetXaxis(), protonSteps); + histos.add("Proton/Inclusive/hPt", "hPt", kTH1F, {axisProtonPt}); + histos.add("Proton/Inclusive/h2TPCNSigmaVsPt", "h2TPCNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); + histos.add("Proton/Inclusive/h2TOFNSigmaVsPt", "h2TOFNSigmaVsPt", kTH2F, {axisProtonPt, axisNSigma}); + histos.add("Proton/Inclusive/h2TPCNClsVsPt", "h2TPCNClsVsPt", kTH2F, {axisProtonPt, axisTpcNCls}); + histos.add("Proton/Inclusive/h2DcaToPVVsPt", "h2DcaToPVVsPt", kTH2F, {axisProtonPt, axisProtonDcaToPV}); + + setBinLabels(histos.add("Candidate/Inclusive/hSelectionCounter", "hSelectionCounter", kTH1D, {axisCandSel})->GetXaxis(), candSteps); + setBinLabels(histos.add("Candidate/Inclusive/h2SelectionCounterVsLegType", "h2SelectionCounterVsLegType", kTH2D, {axisCandSel, axisPhotonLegType})->GetXaxis(), candSteps); + histos.add("Candidate/Inclusive/hDcaProtonGamma", "hDcaProtonGamma", kTH1F, {axisDca}); + histos.add("Candidate/Inclusive/hRadius", "hRadius", kTH1F, {axisCandRadius}); + histos.add("Candidate/Inclusive/hFlightDistance", "hFlightDistance", kTH1F, {axisFlightDistance}); + histos.add("Candidate/Inclusive/hTiltAngle", "hTiltAngle", kTH1F, {axisTilt}); + histos.add("Candidate/Inclusive/hRootCenter", "hRootCenter", kTH1F, {axisRootCenter}); + histos.add("Candidate/Inclusive/hDcaToPV", "hDcaToPV", kTH1F, {axisDcaToPV}); + histos.add("Candidate/Inclusive/hAntiSigmaPointingAngle", "hAntiSigmaPointingAngle", kTH1F, {axisAntiSigmaPA}); + histos.add("Candidate/Inclusive/hMassSigmaPlus", "hMassSigmaPlus", kTH1F, {axisMassSigma}); + histos.add("Candidate/Inclusive/h2MassVsPt", "h2MassVsPt", kTH2F, {axisSigmaPt, axisMassSigma}); + histos.add("Candidate/Inclusive/h2MassVsTilt", "h2MassVsTilt", kTH2F, {axisTilt, axisMassSigma}); + histos.add("Candidate/Inclusive/hRapidity", "hRapidity", kTH1F, {axisRapidity}); + histos.add("Candidate/Inclusive/hPhotonOpeningAngle", "hPhotonOpeningAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Candidate/Inclusive/hPhotonPointingAngle", "hPhotonPointingAngle", kTH1F, {axisPhotonOpeningAngle}); + histos.add("Candidate/Inclusive/hPhotonDcaToPV", "hPhotonDcaToPV", kTH1F, {axisCandPhotonDcaToPV}); + + if (doprocessMc || doprocessFindable) { + histos.addClone("Photon/Inclusive/", "Photon/True/"); + } + if (doprocessMc) { + histos.addClone("Proton/Inclusive/", "Proton/True/"); + histos.addClone("Candidate/Inclusive/", "Candidate/True/"); + } + + histos.add("Photon/Inclusive/hTpcTimeRangeNColl", "hTpcTimeRangeNColl", kTH1F, {axisTpcTimeRangeNColl}); + histos.add("Candidate/Inclusive/hCandidatesPerPhoton", "hCandidatesPerPhoton", kTH1F, {axisCandidatesPerPhoton}); + + if (doprocessMc) { + histos.add("MC/hGenSigmaPlusPt", "MC/hGenSigmaPlusPt", kTH1F, {axisSigmaPt}); + setBinLabels(histos.add("MC/hSigmaPlusCounter", "MC/hSigmaPlusCounter", kTH1D, {axisSigmaSel})->GetXaxis(), sigmaSteps); + } + + if (doprocessFindable) { + const AxisSpec axisMomentum{100, 0., 10., "#it{p} (GeV/#it{c})"}; + const AxisSpec axisDetectorPresence{3, -0.5, 2.5, "detector"}; + const AxisSpec axisDuplicateTrack{2, -0.5, 1.5, "track"}; + const AxisSpec axisTPCClusters{160, -0.5, 159.5, "TPC clusters"}; + const AxisSpec axisPhotonMomResolution{200, -1., 1., "(#it{p}_{reco,#gamma} - #it{p}_{MC,#gamma})/#it{p}_{MC,#gamma}"}; + const AxisSpec axisPhotonCosPA{200, -1., 1., "cosPA_{#gamma}"}; + const AxisSpec axisPairMass{200, 0., 0.1, "m_{e^{+}e^{-}} (GeV/#it{c}^{2})"}; + const AxisSpec axisV0Presence{2, -0.5, 1.5, "V0 match"}; + histos.add("Findable/hSigmaPlusPt", "Findable/hSigmaPlusPt", kTH1F, {axisSigmaPt}); + histos.add("Findable/h2ProtonPtVsSigmaPlusPt", "Findable/h2ProtonPtVsSigmaPlusPt", kTH2F, {axisSigmaPt, axisMomentum}); + histos.add("Findable/hElectronPt", "Findable/hElectronPt", kTH1F, {axisMomentum}); + histos.add("Findable/hPositronPt", "Findable/hPositronPt", kTH1F, {axisMomentum}); + histos.add("Findable/hElectronPositronMass", "Findable/hElectronPositronMass", kTH1F, {axisPairMass}); + histos.add("Findable/hPhotonConversionRadius", "Findable/hPhotonConversionRadius", kTH1F, {axisPhotonRadius}); + histos.add("Findable/hPhotonMomentumResolution", "Findable/hPhotonMomentumResolution", kTH1F, {axisPhotonMomResolution}); + histos.add("Findable/hPhotonCosPA", "Findable/hPhotonCosPA", kTH1F, {axisPhotonCosPA}); + histos.add("Findable/hConversionPairV0Presence", "Findable/hConversionPairV0Presence", kTH1F, {axisV0Presence}); + histos.add("Findable/hPhotonSearchPresence", "Findable/hPhotonSearchPresence", kTH1F, {axisV0Presence}); + histos.add("Findable/hDuplicateConversionTrackCounter", "Findable/hDuplicateConversionTrackCounter", kTH1F, {axisDuplicateTrack}); + histos.add("Findable/hDuplicateElectronTPCNClsFound", "Findable/hDuplicateElectronTPCNClsFound", kTH1F, {axisTPCClusters}); + histos.add("Findable/hDuplicatePositronTPCNClsFound", "Findable/hDuplicatePositronTPCNClsFound", kTH1F, {axisTPCClusters}); + histos.add("Findable/hElectronDetectorPresence", "Findable/hElectronDetectorPresence", kTH1F, {axisDetectorPresence}); + histos.add("Findable/hPositronDetectorPresence", "Findable/hPositronDetectorPresence", kTH1F, {axisDetectorPresence}); + + setBinLabels(histos.get(HIST("Findable/hConversionPairV0Presence"))->GetXaxis(), {"valid pair", "in V0"}); + setBinLabels(histos.get(HIST("Findable/hPhotonSearchPresence"))->GetXaxis(), {"valid pair", "passed photon selection"}); + setBinLabels(histos.get(HIST("Findable/hDuplicateConversionTrackCounter"))->GetXaxis(), {"e^{-}", "e^{+}"}); + setBinLabels(histos.get(HIST("Findable/hElectronDetectorPresence"))->GetXaxis(), {"ITS", "TPC", "TOF"}); + setBinLabels(histos.get(HIST("Findable/hPositronDetectorPresence"))->GetXaxis(), {"ITS", "TPC", "TOF"}); + } + } + + // photon (electron/positron conversion pair) candidate + template + struct PhotonCand { + float x = 0.f, y = 0.f, z = 0.f; + float px = 0.f, py = 0.f, pz = 0.f; + float mGamma = 0.f; + float alpha = 0.f; + float qtarm = 0.f; + float radius = 0.f; + float dcaDau = 0.f; + float cosPAToPV = 0.f; + TTrack negTrack; + TTrack posTrack; + }; + + // longitudinal distance of the photon's flight line to the PV at its transverse DCA (cm) + static float photonDcaZ(const std::array& conv, const std::array& pGamma, const std::array& pv) + { + float pt2 = pGamma[0] * pGamma[0] + pGamma[1] * pGamma[1]; + float tAtDcaXY = pt2 > 0.f ? ((pv[0] - conv[0]) * pGamma[0] + (pv[1] - conv[1]) * pGamma[1]) / pt2 : 0.f; + return conv[2] + tAtDcaXY * pGamma[2] - pv[2]; + } + + // MC counter of generated Sigma+ -> p pi0 in acceptance + void fillSigmaCounter(int sigmaId, int step) + { + if (mGenSigmaWritten.contains(sigmaId)) { + histos.fill(HIST("MC/hSigmaPlusCounter"), step); + } + } + + template + void fillPhotonHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Photon/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Photon/True/") + name, values...); + } + } + + template + void fillProtonHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Proton/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Proton/True/") + name, values...); + } + } + + template + void fillCandHist(const TName& name, bool isSignal, Ts... values) + { + histos.fill(HIST("Candidate/Inclusive/") + name, values...); + if (isSignal) { + histos.fill(HIST("Candidate/True/") + name, values...); + } + } + + // photon candidates from V0Datas + template + std::vector> findPhotonsFromV0s(const TV0s& v0s, const TTracks&, const std::array& pv) + { + std::vector> photons; + + for (const auto& v0 : v0s) { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + + int sigmaId = -1; + if constexpr (IsMC) { + if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { + sigmaId = findSigmaPlusMotherOfPhoton(posTrack.template mcParticle_as(), negTrack.template mcParticle_as()); + } + } + bool isSignal = sigmaId >= 0; + + auto fillPhotonStep = [&](int step) { + fillPhotonHist(HIST("hSelectionCounter"), isSignal, step); + }; + fillPhotonStep(0); + + if (negTrack.eta() < photonDauEtaMin || negTrack.eta() > photonDauEtaMax) { + continue; + } + fillPhotonStep(1); + + if (posTrack.eta() < photonDauEtaMin || posTrack.eta() > photonDauEtaMax) { + continue; + } + fillPhotonStep(2); + + fillPhotonHist(HIST("h2TPCNClsPosVsPt"), isSignal, posTrack.pt(), posTrack.tpcNClsFound()); + fillPhotonHist(HIST("h2TPCNClsNegVsPt"), isSignal, negTrack.pt(), negTrack.tpcNClsFound()); + if (posTrack.tpcNClsFound() < photonDauMinTpcNCls || negTrack.tpcNClsFound() < photonDauMinTpcNCls) { + continue; + } + fillPhotonStep(3); + + fillPhotonHist(HIST("h2TPCNSigmaElPosVsPt"), isSignal, posTrack.pt(), posTrack.tpcNSigmaEl()); + fillPhotonHist(HIST("h2TPCNSigmaElNegVsPt"), isSignal, negTrack.pt(), negTrack.tpcNSigmaEl()); + if (posTrack.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || posTrack.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl || + negTrack.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || negTrack.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl) { + continue; + } + if (posTrack.pt() > photonDauMaxPt || negTrack.pt() > photonDauMaxPt) { + continue; + } + fillPhotonStep(4); + + fillPhotonHist(HIST("hDcaV0Daughters"), isSignal, v0.dcaV0daughters()); + if (v0.dcaV0daughters() > photonMaxDCAV0Dau) { + continue; + } + fillPhotonStep(5); + + std::array secVtx{v0.x(), v0.y(), v0.z()}; + float radius = v0.v0radius(); + if (radius < photonMinRadius || radius > photonMaxRadius) { + continue; + } + fillPhotonStep(6); + + std::array pNeg{v0.pxneg(), v0.pyneg(), v0.pzneg()}; + std::array pPos{v0.pxpos(), v0.pypos(), v0.pzpos()}; + + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(pPos[0], pPos[1], pPos[2], pNeg[0], pNeg[1], pNeg[2]); + fillPhotonHist(HIST("hOpeningAngle"), isSignal, photonOpeningAngle); + if (photonOpeningAngle > photonMaxOpeningAngle) { + continue; + } + fillPhotonStep(7); + + // difference of the daughter tracks' polar angles + float photonDeltaTheta = 2.f * std::atan(std::exp(-posTrack.eta())) - 2.f * std::atan(std::exp(-negTrack.eta())); + fillPhotonHist(HIST("hDeltaTheta"), isSignal, photonDeltaTheta); + if (std::abs(photonDeltaTheta) > photonMaxDeltaTheta) { + continue; + } + fillPhotonStep(8); + + std::array pGamma{pNeg[0] + pPos[0], pNeg[1] + pPos[1], pNeg[2] + pPos[2]}; + float cosPA = RecoDecay::cpa(pv, secVtx, pGamma); + if (cosPA < photonMinV0cospa || std::abs(photonDcaZ(secVtx, pGamma, pv)) > candMaxPhotonDcaZToPV) { + continue; + } + fillPhotonStep(9); + + float photonY = RecoDecay::y(pGamma, o2::constants::physics::MassGamma); + if (photonY < photonMinRapidity || photonY > photonMaxRapidity) { + continue; + } + fillPhotonStep(10); + + float qtarm = v0.qtarm(); + float alpha = v0.alpha(); + if (qtarm > photonMaxQt) { + continue; + } + fillPhotonStep(11); + + if (std::abs(alpha) > photonMaxAlpha) { + continue; + } + fillPhotonStep(12); + + float mGamma = v0.mGamma(); + if (mGamma > photonMaxMass) { + continue; + } + fillPhotonStep(13); + fillSigmaCounter(sigmaId, 4); + + fillPhotonHist(HIST("hMass"), isSignal, mGamma); + fillPhotonHist(HIST("hPt"), isSignal, std::hypot(pGamma[0], pGamma[1])); + fillPhotonHist(HIST("hRadius"), isSignal, radius); + fillPhotonHist(HIST("h2ArmenterosPodolanski"), isSignal, alpha, qtarm); + fillPhotonHist(HIST("h2ConvPointXY"), isSignal, secVtx[0], secVtx[1]); + + photons.push_back({secVtx[0], secVtx[1], secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, v0.dcaV0daughters(), cosPA, negTrack, posTrack}); + } + + return photons; + } + + static uint64_t photonId(int64_t posTrackId, int64_t negTrackId) + { + return (static_cast(posTrackId) << 32) | static_cast(negTrackId); + } + + // psi_pair of the photon daughters (similar to PsiPair in PWGEM) + template + float photonPsiPair(const TTrack& posTrack, const TTrack& negTrack, float convRadius) + { + for (const float& offsetR : {60.f, 30.f, 10.f}) { + auto posTrackPar = getTrackParCov(posTrack); + auto negTrackPar = getTrackParCov(negTrack); + posTrackPar.setPID(o2::track::PID::Electron); + negTrackPar.setPID(o2::track::PID::Electron); + if (!o2::base::Propagator::Instance()->propagateToR(posTrackPar, convRadius + offsetR) || !o2::base::Propagator::Instance()->propagateToR(negTrackPar, convRadius + offsetR)) { + continue; + } + std::array pPos{}; + std::array pNeg{}; + posTrackPar.getPxPyPzGlo(pPos); + negTrackPar.getPxPyPzGlo(pNeg); + return o2::aod::pwgem::dilepton::utils::pairutil::getPsiPair(pPos[0], pPos[1], pPos[2], pNeg[0], pNeg[1], pNeg[2]); + } + return 999.f; + } + + template + void markGoodCollisions(const TCollisions& collisions) + { + mGoodCollision.assign(collisions.size(), false); + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + mGoodCollision[collision.globalIndex()] = true; + } + } + + // photon daughter selection of the self-pairing (TPC histograms filled before cuts) + template + bool selectPhotonDaughter(const TTrack& track, bool isSignalLeg) + { + if (track.eta() < photonDauEtaMin || track.eta() > photonDauEtaMax) { + return false; + } + if (track.sign() > 0) { + fillPhotonHist(HIST("h2TPCNClsPosVsPt"), isSignalLeg, track.pt(), track.tpcNClsFound()); + } else { + fillPhotonHist(HIST("h2TPCNClsNegVsPt"), isSignalLeg, track.pt(), track.tpcNClsFound()); + } + if (track.tpcNClsFound() < photonDauMinTpcNCls) { + return false; + } + if (track.sign() > 0) { + fillPhotonHist(HIST("h2TPCNSigmaElPosVsPt"), isSignalLeg, track.pt(), track.tpcNSigmaEl()); + } else { + fillPhotonHist(HIST("h2TPCNSigmaElNegVsPt"), isSignalLeg, track.pt(), track.tpcNSigmaEl()); + } + if (track.tpcNSigmaEl() < photonDauMinTPCNSigmaEl || track.tpcNSigmaEl() > photonDauMaxTPCNSigmaEl) { + return false; + } + return track.pt() <= photonDauMaxPt; + } + + // collisions of a track with only TPC time (all inside time range [t - backward, t + forward] plus a margin) + template + TrackCand tpcOnlyTrackCand(const TTrack& track, const std::vector& collisionBcNS, const std::vector& collisionTimeNS) + { + o2::aod::track::extensions::TPCTimeErrEncoding timeEncoding{}; + timeEncoding.encoding.timeErr = track.trackTimeRes(); + double trackTimeNS = collisionBcNS[track.collisionId()] + track.trackTime(); // trackTime() is relative to the BC of its collision + double timeMin = trackTimeNS - timeEncoding.getDeltaTBwd() - photonPoolTimeMarginNS; + double timeMax = trackTimeNS + timeEncoding.getDeltaTFwd() + photonPoolTimeMarginNS; + int firstCollIdx = track.collisionId(); + int lastCollIdx = firstCollIdx; + for (int collIdx = 0; collIdx < static_cast(collisionTimeNS.size()); ++collIdx) { + if (collisionTimeNS[collIdx] >= timeMin && collisionTimeNS[collIdx] <= timeMax) { + firstCollIdx = std::min(firstCollIdx, collIdx); + lastCollIdx = std::max(lastCollIdx, collIdx); + } + } + histos.fill(HIST("Photon/Inclusive/hTpcTimeRangeNColl"), lastCollIdx - firstCollIdx + 1); + return TrackCand{.Idxtr = static_cast(track.globalIndex()), .collBracket = {firstCollIdx, lastCollIdx}}; + } + + // electron-positron pairs with |theta+ - theta-| <= photonMaxDeltaTheta that share at least one collision (theta should be similar) + std::vector findPairsByPolarAngle(const std::vector& thetaOfTrack, int nCollisions) + { + // electrons of each collision sorted by polar angle, so a positron only looks at those inside its theta window + std::vector>> electronsByCollision(nCollisions); + for (int iElectron = 0; iElectron < static_cast(mElectronPool.size()); ++iElectron) { + const auto& electron = mElectronPool[iElectron]; + for (int collIdx = electron.collBracket.getMin(); collIdx <= electron.collBracket.getMax(); ++collIdx) { + electronsByCollision[collIdx].push_back({thetaOfTrack[electron.Idxtr], iElectron}); + } + } + for (size_t collIdx = 0; collIdx < electronsByCollision.size(); ++collIdx) { + std::sort(electronsByCollision[collIdx].begin(), electronsByCollision[collIdx].end()); + } + + std::vector pairs; + std::vector lastPositronPaired(mElectronPool.size(), -1); // an electron in several collisions is paired once per positron + for (int iPositron = 0; iPositron < static_cast(mPositronPool.size()); ++iPositron) { + const auto& positron = mPositronPool[iPositron]; + float thetaMin = thetaOfTrack[positron.Idxtr] - photonMaxDeltaTheta; + float thetaMax = thetaOfTrack[positron.Idxtr] + photonMaxDeltaTheta; + for (int collIdx = positron.collBracket.getMin(); collIdx <= positron.collBracket.getMax(); ++collIdx) { + const auto& electronsOfCollision = electronsByCollision[collIdx]; + auto electronIt = std::lower_bound(electronsOfCollision.begin(), electronsOfCollision.end(), std::make_pair(thetaMin, -1)); + for (; electronIt != electronsOfCollision.end() && electronIt->first <= thetaMax; ++electronIt) { + int iElectron = electronIt->second; + if (lastPositronPaired[iElectron] == iPositron) { + continue; + } + lastPositronPaired[iElectron] = iPositron; + const auto& electron = mElectronPool[iElectron]; + pairs.push_back(SVCand{.tr0Idx = electron.Idxtr, .tr1Idx = positron.Idxtr, .collBracket = electron.collBracket.getOverlap(positron.collBracket)}); + } + } + } + return pairs; + } + + // cuts before the fit (similar polar angles, and the circles of the two tracks in xy touching at the conversion point) + template + bool passesPreFitCuts(const TTrack& posTrack, const TTrack& negTrack, float deltaTheta, bool isSignal) + { + fillPhotonHist(HIST("hDeltaTheta"), isSignal, deltaTheta); + if (std::abs(deltaTheta) > photonMaxDeltaTheta) { + return false; + } + float sna = 0.f; + float csa = 0.f; + o2::math_utils::CircleXYf_t posCircle; + o2::math_utils::CircleXYf_t negCircle; + getTrackParCov(posTrack).getCircleParams(mBz, posCircle, sna, csa); + getTrackParCov(negTrack).getCircleParams(mBz, negCircle, sna, csa); + float circleTouchDist = std::abs(std::hypot(posCircle.xC - negCircle.xC, posCircle.yC - negCircle.yC) - (posCircle.rC + negCircle.rC)); + return circleTouchDist <= photonMaxCircleTouchDist; + } + + static constexpr int LastDaughterCutStep = 4; + + // photon vertex fitted in one collision + struct PhotonFit { + float dca = 0.f; + std::array secVtx{}; + std::array pPos{}; + std::array pNeg{}; + float posTrackZ = 0.f; // z of the positive daughter at the fit, after moving it to the collision's time + }; + + // moves a track with only a TPC time to the time of the collision + template + bool moveToCollision(const TCollision& collision, const TTrack& track, o2::track::TrackParCov& trackParCov) + { + if (!(track.flags() & o2::aod::track::TrackTimeAsym)) { + return true; + } + return mVDriftMgr.moveTPCTrack(collision, track, trackParCov); + } + + // vertex fit of the photon daughters, in the collinear mode if a daughter has no ITS + bool fitPhoton(o2::track::TrackParCov posTrackParCov, o2::track::TrackParCov negTrackParCov, bool collinear, PhotonFit& fit) + { + posTrackParCov.setPID(o2::track::PID::Electron); + negTrackParCov.setPID(o2::track::PID::Electron); + + // photon fit settings, reset afterwards since the fitter is shared with the Sigma+ vertex fit + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + fitter.setMaxDXYIni(photonMaxDXYIni); + fitter.setCollinear(collinear); + int nCand = 0; + try { + nCand = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + nCand = 0; + } + fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrNONE); + fitter.setMaxDXYIni(1e9); + fitter.setCollinear(false); + if (nCand == 0 || !fitter.propagateTracksToVertex()) { + return false; + } + + fit.dca = std::sqrt(fitter.getChi2AtPCACandidate()); + fit.secVtx = fitter.getPCACandidatePos(); + fitter.getTrack(0).getPxPyPzGlo(fit.pPos); + fitter.getTrack(1).getPxPyPzGlo(fit.pNeg); + fit.posTrackZ = posTrackParCov.getZ(); + return true; + } + + // a photon is kept in every compatible collision where it passes the cuts, and the proton of that collision then decides + template + std::vector>> findPhotonsSelfPaired(const TCollisions& collisions, const TTracks& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + std::vector>> photonsByCollision(collisions.size()); + + svPhotonPoolCreator.clearPools(); + svPhotonPoolCreator.fillBC2Coll(collisions, bcs); + mElectronPool.clear(); + mPositronPool.clear(); + + // BC start and time of every collision, in ns relative to the BC of the first collision + std::vector collisionBcNS(collisions.size()); + std::vector collisionTimeNS(collisions.size()); + uint64_t firstGlobalBC = collisions.begin().template bc_as().globalBC(); + for (const auto& collision : collisions) { + int64_t bcDiff = static_cast(collision.template bc_as().globalBC()) - static_cast(firstGlobalBC); + collisionBcNS[collision.globalIndex()] = bcDiff * o2::constants::lhc::LHCBunchSpacingNS; + collisionTimeNS[collision.globalIndex()] = collisionBcNS[collision.globalIndex()] + collision.collisionTime(); + } + + // select the daughter tracks and find the collisions each is compatible with + std::vector thetaOfTrack(tracks.size(), 0.f); + std::vector mcPhotonOfTrack(tracks.size(), -1); // MC: Sigma+ photon the track comes from + std::vector mcSigmaOfTrack(tracks.size(), -1); // MC: Sigma+ the track comes from + for (const auto& track : tracks) { + int mcPhoton = -1; + int mcSigma = -1; + if constexpr (IsMC) { + if (track.has_mcParticle()) { + mcSigma = findSigmaPlusAncestorOfPhotonDaughter(track.template mcParticle_as(), mcPhoton); + } + } + if (!selectPhotonDaughter(track, mcSigma >= 0)) { + continue; + } + thetaOfTrack[track.globalIndex()] = 2.f * std::atan(std::exp(-track.eta())); + mcPhotonOfTrack[track.globalIndex()] = mcPhoton; + mcSigmaOfTrack[track.globalIndex()] = mcSigma; + + if (track.flags() & o2::aod::track::TrackTimeAsym) { + // track with only a TPC time + if (!track.has_collision()) { + continue; + } + if (track.sign() < 0) { + mElectronPool.push_back(tpcOnlyTrackCand(track, collisionBcNS, collisionTimeNS)); + } else { + mPositronPool.push_back(tpcOnlyTrackCand(track, collisionBcNS, collisionTimeNS)); + } + } else { + // track with an ITS time (matched to collisions by svPoolCreator) + svPhotonPoolCreator.appendTrackCand(track, collisions, track.sign() < 0 ? PDG_t::kElectron : PDG_t::kPositron, ambiguousTracks, bcs); + } + } + + // svPoolCreator pools are ordered dau0 pos, dau0 neg, dau1 pos, dau1 neg: electrons are in 1, positrons are in 2 + auto timeMatchedPools = svPhotonPoolCreator.getTrackCandPool(); + mElectronPool.insert(mElectronPool.end(), timeMatchedPools[1].begin(), timeMatchedPools[1].end()); + mPositronPool.insert(mPositronPool.end(), timeMatchedPools[2].begin(), timeMatchedPools[2].end()); + + auto pairs = findPairsByPolarAngle(thetaOfTrack, collisions.size()); + + // fit and select each pair in every good collision it is compatible with + uint64_t nTruePairs = 0; + for (const auto& pair : pairs) { + int electronIdx = pair.tr0Idx; + int positronIdx = pair.tr1Idx; + int sigmaId = -1; + if constexpr (IsMC) { + if (mcPhotonOfTrack[electronIdx] >= 0 && mcPhotonOfTrack[electronIdx] == mcPhotonOfTrack[positronIdx]) { + sigmaId = mcSigmaOfTrack[electronIdx]; + nTruePairs++; + } + } + bool isSignal = sigmaId >= 0; + + auto negTrack = tracks.rawIteratorAt(electronIdx); + auto posTrack = tracks.rawIteratorAt(positronIdx); + if (!passesPreFitCuts(posTrack, negTrack, thetaOfTrack[positronIdx] - thetaOfTrack[electronIdx], isSignal)) { + continue; + } + + // both daughters with only a TPC time on the same TPC side: moving to another collision shifts both by the same z, + // so the pair is fitted once and the vertex is only shifted in z for the other collisions + bool posTpcTime = posTrack.flags() & o2::aod::track::TrackTimeAsym; + bool negTpcTime = negTrack.flags() & o2::aod::track::TrackTimeAsym; + bool sameShiftForAllCollisions = posTpcTime && negTpcTime && ((posTrack.tgl() > 0.f) == (negTrack.tgl() > 0.f)); + bool collinearFit = !posTrack.hasITS() || !negTrack.hasITS(); + std::optional referenceFit; + + int maxStepReached = LastDaughterCutStep; // furthest selection step reached in any collision (for selection counter histograms) + auto passStep = [&](int step) { maxStepReached = std::max(maxStepReached, step); }; + bool anyFitOk = false; + float smallestDca = 1e9f; + bool openingAngleFilled = false; + int nCopies = 0; + + for (int collIdx = pair.collBracket.getMin(); collIdx <= pair.collBracket.getMax(); ++collIdx) { + if (!mGoodCollision[collIdx]) { + continue; + } + auto collision = collisions.rawIteratorAt(collIdx); + + PhotonFit fit; + if (sameShiftForAllCollisions && referenceFit) { + auto posTrackParCov = getTrackParCov(posTrack); + if (!moveToCollision(collision, posTrack, posTrackParCov)) { + continue; + } + fit = *referenceFit; + fit.secVtx[2] += posTrackParCov.getZ() - referenceFit->posTrackZ; + } else { + auto posTrackParCov = getTrackParCov(posTrack); + auto negTrackParCov = getTrackParCov(negTrack); + if (!moveToCollision(collision, posTrack, posTrackParCov) || !moveToCollision(collision, negTrack, negTrackParCov)) { + continue; + } + bool fitOk = fitPhoton(posTrackParCov, negTrackParCov, collinearFit, fit); + if (fitOk) { + anyFitOk = true; + smallestDca = std::min(smallestDca, fit.dca); + } + if (!fitOk || fit.dca > photonMaxDCAV0Dau) { + if (sameShiftForAllCollisions) { + break; // same result in every collision + } + continue; + } + if (sameShiftForAllCollisions) { + referenceFit = fit; + } + } + passStep(5); + + float radius = std::hypot(fit.secVtx[0], fit.secVtx[1]); + if (radius < photonMinRadius || radius > photonMaxRadius) { + continue; + } + passStep(6); + + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + if (!openingAngleFilled) { // once per pair + openingAngleFilled = true; + fillPhotonHist(HIST("hOpeningAngle"), isSignal, photonOpeningAngle); + } + if (photonOpeningAngle > photonMaxOpeningAngle) { + continue; + } + passStep(8); // opening angle, and delta theta that is cut before the fit + + std::array pGamma{fit.pNeg[0] + fit.pPos[0], fit.pNeg[1] + fit.pPos[1], fit.pNeg[2] + fit.pPos[2]}; + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + float cosPA = RecoDecay::cpa(pv, fit.secVtx, pGamma); + if (cosPA < photonMinV0cospa || std::abs(photonDcaZ(fit.secVtx, pGamma, pv)) > candMaxPhotonDcaZToPV) { + continue; + } + passStep(9); + + float photonY = RecoDecay::y(pGamma, o2::constants::physics::MassGamma); + if (photonY < photonMinRapidity || photonY > photonMaxRapidity) { + continue; + } + passStep(10); + + float qtarm = v0Qt(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + float alpha = v0Alpha(fit.pPos[0], fit.pPos[1], fit.pPos[2], fit.pNeg[0], fit.pNeg[1], fit.pNeg[2]); + if (qtarm > photonMaxQt) { + continue; + } + passStep(11); + + if (std::abs(alpha) > photonMaxAlpha) { + continue; + } + passStep(12); + + float mGamma = RecoDecay::m(std::array{fit.pPos, fit.pNeg}, std::array{o2::constants::physics::MassElectron, o2::constants::physics::MassElectron}); + if (mGamma > photonMaxMass) { + continue; + } + passStep(13); + + fillPhotonHist(HIST("hMass"), isSignal, mGamma); + fillPhotonHist(HIST("hPt"), isSignal, std::hypot(pGamma[0], pGamma[1])); + fillPhotonHist(HIST("hRadius"), isSignal, radius); + fillPhotonHist(HIST("h2ArmenterosPodolanski"), isSignal, alpha, qtarm); + fillPhotonHist(HIST("h2ConvPointXY"), isSignal, fit.secVtx[0], fit.secVtx[1]); + + nCopies++; + photonsByCollision[collIdx].push_back({fit.secVtx[0], fit.secVtx[1], fit.secVtx[2], pGamma[0], pGamma[1], pGamma[2], mGamma, alpha, qtarm, radius, fit.dca, cosPA, negTrack, posTrack}); + } + + if (anyFitOk) { + fillPhotonHist(HIST("hDcaV0Daughters"), isSignal, smallestDca); + } + if (nCopies > 0) { + fillPhotonHist(HIST("hCollisionsPerPhoton"), isSignal, nCopies); + fillSigmaCounter(sigmaId, 4); + } + for (int step = 5; step <= maxStepReached; ++step) { + fillPhotonHist(HIST("hSelectionCounter"), isSignal, step); + } + } + + // steps 0-4 are the daughter-track cuts applied before the pairing, so they count all pairs + for (int step = 0; step <= LastDaughterCutStep; ++step) { + histos.fill(HIST("Photon/Inclusive/hSelectionCounter"), step, static_cast(pairs.size())); + if constexpr (IsMC) { + histos.fill(HIST("Photon/True/hSelectionCounter"), step, static_cast(nTruePairs)); + } + } + + return photonsByCollision; + } + + // proton candidate selection + template + bool selectProton(const TTrack& track) + { + bool isSignal = false; + if constexpr (IsMC) { + if (track.has_mcParticle()) { + isSignal = findSigmaPlusMotherOfProton(track.template mcParticle_as()) >= 0; + } + } + auto fillProtonStep = [&](int step) { + fillProtonHist(HIST("hSelectionCounter"), isSignal, step); + }; + fillProtonStep(0); + + if (track.pt() < protonMinPt) { + return false; + } + fillProtonStep(1); + + if (std::abs(track.eta()) > protonMaxEta) { + return false; + } + fillProtonStep(2); + + fillProtonHist(HIST("h2TPCNClsVsPt"), isSignal, track.pt(), track.tpcNClsFound()); + if (track.tpcNClsFound() < protonMinTpcNCls) { + return false; + } + fillProtonStep(3); + + if (std::abs(track.tpcNSigmaPr()) > protonMaxTPCNSigma) { + return false; + } + fillProtonStep(4); + + if (track.pt() > protonPtMinRequireTOF) { + if (track.hasTOF()) { + if (std::abs(track.tofNSigmaPr()) > protonMaxTOFNSigma) { + return false; + } + } else if (protonRequireTofHit) { + return false; + } + } + fillProtonStep(5); + + fillProtonHist(HIST("h2DcaToPVVsPt"), isSignal, track.pt(), std::abs(track.dcaXY())); + if (std::abs(track.dcaXY()) < protonMinDcaToPV || std::abs(track.dcaXY()) > protonMaxDcaToPV || std::abs(track.dcaZ()) > protonMaxDcaZToPV) { + return false; + } + fillProtonStep(6); + + fillProtonHist(HIST("hPt"), isSignal, track.pt()); + fillProtonHist(HIST("h2TPCNSigmaVsPt"), isSignal, track.pt(), track.tpcNSigmaPr()); + if (track.hasTOF()) { + fillProtonHist(HIST("h2TOFNSigmaVsPt"), isSignal, track.pt(), track.tofNSigmaPr()); + } + + return true; + } + + static float dot3(const std::array& a, const std::array& b) + { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + static std::array cross3(const std::array& a, const std::array& b) + { + return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]}; + } + static std::array normalize3(const std::array& a) + { + float norm = std::sqrt(dot3(a, a)); + return {a[0] / norm, a[1] / norm, a[2] / norm}; + } + + // MC truth + template + int findSigmaPlusMotherOfProton(const TMcPart& mcProton) + { + if (std::abs(mcProton.pdgCode()) != PDG_t::kProton) { + return -1; + } + auto const& mothers = mcProton.template mothers_as(); + if (mothers.empty() || std::abs(mothers.front().pdgCode()) != PDG_t::kSigmaPlus) { + return -1; + } + return mothers.front().globalIndex(); + } + + // MC: for an e+ or e- from Sigma+ -> pi0 -> photon -> e+e-, returns the Sigma+ index and sets photonIndex (-1 otherwise) + template + int findSigmaPlusAncestorOfPhotonDaughter(const TMcPart& mcDaughter, int& photonIndex) + { + auto const& mothers = mcDaughter.template mothers_as(); + if (mothers.empty() || mothers.front().pdgCode() != PDG_t::kGamma) { + return -1; + } + auto const& pi0Mothers = mothers.front().template mothers_as(); + if (pi0Mothers.empty() || std::abs(pi0Mothers.front().pdgCode()) != PDG_t::kPi0) { + return -1; + } + auto const& sigmaMothers = pi0Mothers.front().template mothers_as(); + if (sigmaMothers.empty() || std::abs(sigmaMothers.front().pdgCode()) != PDG_t::kSigmaPlus) { + return -1; + } + photonIndex = mothers.front().globalIndex(); + return sigmaMothers.front().globalIndex(); + } + + template + int findSigmaPlusMotherOfPhoton(const TMcPart& mcPos, const TMcPart& mcNeg) + { + int posPhoton = -1; + int negPhoton = -1; + int sigmaId = findSigmaPlusAncestorOfPhotonDaughter(mcPos, posPhoton); + if (sigmaId < 0 || findSigmaPlusAncestorOfPhotonDaughter(mcNeg, negPhoton) < 0 || posPhoton != negPhoton) { + return -1; + } + return sigmaId; + } + + template + bool isSigmaPlusToProtonPi0(const TMcPart& mcPart) + { + if (std::abs(mcPart.pdgCode()) != PDG_t::kSigmaPlus) { + return false; + } + int pdgProton = mcPart.pdgCode() > 0 ? PDG_t::kProton : PDG_t::kProtonBar; + bool hasProton = false; + bool hasPi0 = false; + for (const auto& daughter : mcPart.template daughters_as()) { + hasProton |= (daughter.pdgCode() == pdgProton); + hasPi0 |= (std::abs(daughter.pdgCode()) == PDG_t::kPi0); + } + return hasProton && hasPi0; + } + + template + void fillFindableTrackDetectors(const TTrack& track) + { + auto fillDetector = [&](int detector) { + if constexpr (IsElectron) { + histos.fill(HIST("Findable/hElectronDetectorPresence"), detector); + } else { + histos.fill(HIST("Findable/hPositronDetectorPresence"), detector); + } + }; + + if (track.hasITS()) { + fillDetector(0); + } + if (track.hasTPC()) { + fillDetector(1); + } + if (track.hasTOF()) { + fillDetector(2); + } + } + + template + int findSigmaPlusMotherOfConversionElectron(const TMcPart& mcElectron, int& gammaIndex, std::array& conversionVertex) + { + if (std::abs(mcElectron.pdgCode()) != PDG_t::kElectron || mcElectron.getProcess() != TMCProcess::kPPair) { + return -1; + } + + auto const& gammaMothers = mcElectron.template mothers_as(); + if (gammaMothers.empty() || gammaMothers.front().pdgCode() != PDG_t::kGamma) { + return -1; + } + + auto mcGamma = gammaMothers.front(); + if (mcGamma.getProcess() != TMCProcess::kPDecay) { + return -1; + } + + auto const& pi0Mothers = mcGamma.template mothers_as(); + if (pi0Mothers.empty() || std::abs(pi0Mothers.front().pdgCode()) != PDG_t::kPi0) { + return -1; + } + + auto mcPi0 = pi0Mothers.front(); + auto const& sigmaMothers = mcPi0.template mothers_as(); + if (sigmaMothers.empty() || std::abs(sigmaMothers.front().pdgCode()) != PDG_t::kSigmaPlus) { + return -1; + } + + gammaIndex = mcGamma.globalIndex(); + conversionVertex = {mcElectron.vx(), mcElectron.vy(), mcElectron.vz()}; + return sigmaMothers.front().globalIndex(); + } + + // Build a Sigma+ -> p pi0 candidate from a proton track and a PCM photon + template + void buildSigmaPlusCandidate(const TTrack& protonTrack, const PhotonCand& photon, int collisionIndex, const std::array& pv) + { + auto posTrack = photon.posTrack; + auto negTrack = photon.negTrack; + int photonLegsWithoutIts = (posTrack.hasITS() ? 0 : 1) + (negTrack.hasITS() ? 0 : 1); + + bool isSignal = false; + bool protonIsSignal = false; + bool photonIsSignal = false; + bool collisionIdCheck = false; // MC only: true if the proton's MC collision matches the reconstructed collision + std::array mcTrueVtx{}; + std::array mcTrueMomProton{}; + std::array mcTrueMomGamma{}; + std::array mcTrueMomSigmaPlus{}; + float decayRadiusMC = -999.f; + float massMC = -999.f; + int matchedSigmaId = -1; + if constexpr (IsMC) { + if (protonTrack.has_mcParticle()) { + auto mcProton = protonTrack.template mcParticle_as(); + + auto protonCollision = protonTrack.template collision_as(); + if (protonCollision.has_mcCollision()) { + collisionIdCheck = protonCollision.mcCollision().globalIndex() == mcProton.mcCollisionId(); + } + + int protonSigmaIdx = findSigmaPlusMotherOfProton(mcProton); + protonIsSignal = protonSigmaIdx >= 0; + + if (posTrack.has_mcParticle() && negTrack.has_mcParticle()) { + auto mcPos = posTrack.template mcParticle_as(); + auto mcNeg = negTrack.template mcParticle_as(); + + auto const& posMothers = mcPos.template mothers_as(); + if (!posMothers.empty()) { + auto mcGamma = posMothers.front(); + mcTrueMomGamma = {mcGamma.px(), mcGamma.py(), mcGamma.pz()}; + } + + int photonSigmaIdx = findSigmaPlusMotherOfPhoton(mcPos, mcNeg); + photonIsSignal = photonSigmaIdx >= 0; + isSignal = protonIsSignal && photonIsSignal && photonSigmaIdx == protonSigmaIdx; + } + + if (isSignal) { + auto mcSigmaPlusMother = mcProton.template mothers_as().front(); + mcTrueVtx = {mcProton.vx(), mcProton.vy(), mcProton.vz()}; + mcTrueMomProton = {mcProton.px(), mcProton.py(), mcProton.pz()}; + mcTrueMomSigmaPlus = {mcSigmaPlusMother.px(), mcSigmaPlusMother.py(), mcSigmaPlusMother.pz()}; + matchedSigmaId = mcSigmaPlusMother.globalIndex(); + decayRadiusMC = std::hypot(mcTrueVtx[0] - mcSigmaPlusMother.vx(), mcTrueVtx[1] - mcSigmaPlusMother.vy()); + massMC = std::sqrt(mcSigmaPlusMother.e() * mcSigmaPlusMother.e() - mcSigmaPlusMother.p() * mcSigmaPlusMother.p()); + } + } + } + + auto fillCandStep = [&](int step) { + fillCandHist(HIST("hSelectionCounter"), isSignal, step); + fillCandHist(HIST("h2SelectionCounterVsLegType"), isSignal, step, photonLegsWithoutIts); + }; + fillCandStep(0); // all pairs + + // reject the pair if the proton track is one of the photon's own daughter tracks + if (protonTrack.globalIndex() == posTrack.globalIndex() || protonTrack.globalIndex() == negTrack.globalIndex()) { + return; + } + fillCandStep(1); // autocorrelation + + auto protonTrackParCov = getTrackParCov(protonTrack); + std::array protonOrigPos{}; + protonTrackParCov.getXYZGlo(protonOrigPos); + + std::array zeroCov{}; + auto photonTrackParCov = o2::track::TrackParCov({photon.x, photon.y, photon.z}, {photon.px, photon.py, photon.pz}, zeroCov, 0, true); + photonTrackParCov.setAbsCharge(0); + photonTrackParCov.setPID(o2::track::PID::Photon); + + int nCand = 0; + try { + nCand = fitter.process(protonTrackParCov, photonTrackParCov); + } catch (...) { + return; + } + if (nCand == 0 || !fitter.propagateTracksToVertex()) { + return; + } + fillCandStep(2); // vertex fit + std::array protonAtPca{}; + std::array photonAtPca{}; + fitter.getTrack(0).getXYZGlo(protonAtPca); + fitter.getTrack(1).getXYZGlo(photonAtPca); + + float dcaProtonGamma = std::sqrt(fitter.getChi2AtPCACandidate()); + fillCandHist(HIST("hDcaProtonGamma"), isSignal, dcaProtonGamma); + if (dcaProtonGamma > candMaxDcaProtonGamma) { + return; + } + fillCandStep(3); // DCA(p,gamma) + + // decay vertex between the proton's and the photon's point of closest approach (relative weight given by candVertexProtonWeight) + std::array secVtx{}; + for (size_t i = 0; i < secVtx.size(); ++i) { + secVtx[i] = candVertexProtonWeight * protonAtPca[i] + (1.f - candVertexProtonWeight) * photonAtPca[i]; + } + float radius = std::hypot(secVtx[0], secVtx[1]); + fillCandHist(HIST("hRadius"), isSignal, radius); + if (radius < candMinRadius || radius > candMaxRadius) { + return; + } + fillCandStep(4); // radius + + std::array flightVec{secVtx[0] - pv[0], secVtx[1] - pv[1], secVtx[2] - pv[2]}; + std::array nHatOrig = normalize3(flightVec); + float flightDistance = std::sqrt(dot3(flightVec, flightVec)); + + fillCandHist(HIST("hFlightDistance"), isSignal, flightDistance); + if (flightDistance < candMinFlightDistance || flightDistance > candMaxFlightDistance) { + return; + } + fillCandStep(5); // flight distance + + std::array pProton{}; + std::array pGamma1{}; + fitter.getTrack(0).getPxPyPzGlo(pProton); + fitter.getTrack(1).getPxPyPzGlo(pGamma1); + + float e1 = std::sqrt(dot3(pGamma1, pGamma1)); + float massPi0 = o2::constants::physics::MassPionNeutral; + constexpr float CoefADegenerateThreshold = 1e-6f; + + std::array nHat{}; + std::array eOut{}; + std::array eIn{}; + float coefA = 0.f, coefB = 0.f; + float pGamma2In = 0.f, pGamma2Out = 0.f, tPerp2 = 0.f; + + auto solveForFlightDir = [&](const std::array& nHatUse) { + nHat = nHatUse; + eOut = normalize3(cross3(pProton, nHat)); // normal to the decay plane + eIn = cross3(eOut, nHat); // in the decay plane, transverse to n + + float pProtonIn = dot3(pProton, eIn); + float pGamma1Long = dot3(pGamma1, nHat); + float pGamma1In = dot3(pGamma1, eIn); + float pGamma1Out = dot3(pGamma1, eOut); + + pGamma2In = -(pProtonIn + pGamma1In); + pGamma2Out = -pGamma1Out; + tPerp2 = pGamma2In * pGamma2In + pGamma2Out * pGamma2Out; + + // m_pi0^2 = 2*(|p_gamma1||p_gamma2| - p_gamma1.p_gamma2) as coefA*x^2 + coefB*x + coefC = 0 + float coefK = massPi0 * massPi0 + 2.f * (pGamma1In * pGamma2In + pGamma1Out * pGamma2Out); + float coefL = 2.f * pGamma1Long; + coefA = 4.f * e1 * e1 - coefL * coefL; + if (std::abs(coefA) < CoefADegenerateThreshold) { + return -1.f; + } + coefB = -2.f * coefK * coefL; + float coefC = 4.f * e1 * e1 * tPerp2 - coefK * coefK; + return coefB * coefB - 4.f * coefA * coefC; + }; + + // if the measured flight direction gives no real solution, flight directions on rings of increasing + // tilt around it are tried (the first ring with a real solution is taken, and on it the direction with the largest discriminant) + float tiltAngle = 0.f; + float discriminant = solveForFlightDir(nHatOrig); + if (discriminant < 0.f) { + constexpr float MaxAbsNzForZHelperAxis = 0.9f; + std::array helperAxis = std::abs(nHatOrig[2]) < MaxAbsNzForZHelperAxis ? std::array{0.f, 0.f, 1.f} : std::array{1.f, 0.f, 0.f}; + std::array tiltAxisU = normalize3(cross3(nHatOrig, helperAxis)); + std::array tiltAxisV = cross3(nHatOrig, tiltAxisU); + int nRings = static_cast(std::lround(candMaxTilt / candTiltStep)); + for (int ring = 1; ring <= nRings && discriminant < 0.f; ++ring) { + float tilt = ring * candTiltStep; + float bestRingDisc = -1.f; + std::array bestRingDir{}; + for (int iAzimuth = 0; iAzimuth < candTiltNAzimuth; ++iAzimuth) { + float azimuth = o2::constants::math::TwoPI * iAzimuth / candTiltNAzimuth; + std::array tiltDir{}; + for (size_t i = 0; i < tiltDir.size(); ++i) { + tiltDir[i] = std::cos(tilt) * nHatOrig[i] + std::sin(tilt) * (std::cos(azimuth) * tiltAxisU[i] + std::sin(azimuth) * tiltAxisV[i]); + } + float disc = solveForFlightDir(tiltDir); + if (disc >= 0.f && disc > bestRingDisc) { + bestRingDisc = disc; + bestRingDir = tiltDir; + } + } + if (bestRingDisc >= 0.f) { + discriminant = solveForFlightDir(bestRingDir); + tiltAngle = tilt; + } + } + } + + float tiltForHist = discriminant >= 0.f ? tiltAngle : 0.4999f; // no real solution + fillCandHist(HIST("hTiltAngle"), isSignal, tiltForHist); + if (discriminant < 0.f) { + return; + } + fillCandStep(6); // real root + + // of the two roots, keep the one giving the mass closest to the nominal Sigma+ mass + float sqrtDisc = std::sqrt(discriminant); + std::array roots{(-coefB + sqrtDisc) / (2.f * coefA), (-coefB - sqrtDisc) / (2.f * coefA)}; + + float rootCenter = -coefB / (2.f * coefA); + fillCandHist(HIST("hRootCenter"), isSignal, rootCenter); + if (candRejectNegRootCenter && rootCenter < 0.f) { + return; + } + if (rootCenter > candMaxRootCenter) { + return; + } + + bool haveCandidate = false; + float bestMass = -999.f; + std::array bestMomGamma2{}; + for (const float& pGamma2Long : roots) { + std::array pGamma2{ + eIn[0] * pGamma2In + eOut[0] * pGamma2Out + nHat[0] * pGamma2Long, + eIn[1] * pGamma2In + eOut[1] * pGamma2Out + nHat[1] * pGamma2Long, + eIn[2] * pGamma2In + eOut[2] * pGamma2Out + nHat[2] * pGamma2Long}; + float eGamma2 = std::sqrt(tPerp2 + pGamma2Long * pGamma2Long); + float eProton = std::sqrt(dot3(pProton, pProton) + o2::constants::physics::MassProton * o2::constants::physics::MassProton); + std::array pTotal{pProton[0] + pGamma1[0] + pGamma2[0], pProton[1] + pGamma1[1] + pGamma2[1], pProton[2] + pGamma1[2] + pGamma2[2]}; + float eTotal = eProton + e1 + eGamma2; + float mass2 = eTotal * eTotal - dot3(pTotal, pTotal); + if (mass2 < 0.f) { + continue; + } + float mass = std::sqrt(mass2); + if (!haveCandidate || std::abs(mass - o2::constants::physics::MassSigmaPlus) < std::abs(bestMass - o2::constants::physics::MassSigmaPlus)) { + haveCandidate = true; + bestMass = mass; + bestMomGamma2 = pGamma2; + } + } + if (!haveCandidate) { + return; + } + fillCandStep(7); // valid root + + std::array pSigma{pProton[0] + pGamma1[0] + bestMomGamma2[0], pProton[1] + pGamma1[1] + bestMomGamma2[1], pProton[2] + pGamma1[2] + bestMomGamma2[2]}; + float ptSigma = std::hypot(pSigma[0], pSigma[1]); + + o2::track::TrackPar sigmaTrackPar({secVtx[0], secVtx[1], secVtx[2]}, {pSigma[0], pSigma[1], pSigma[2]}, protonTrack.sign(), true); + std::array dcaSigmaToPv{}; + o2::base::Propagator::Instance()->propagateToDCA(o2::math_utils::Point3D{pv[0], pv[1], pv[2]}, sigmaTrackPar, mBz, 2.f, + o2::base::Propagator::MatCorrType::USEMatCorrNONE, &dcaSigmaToPv); + float candDcaToPV = std::abs(dcaSigmaToPv[0]); + fillCandHist(HIST("hDcaToPV"), isSignal, candDcaToPV); + if (candDcaToPV > candMaxDcaToPV) { + return; + } + fillCandStep(8); // DCA to PV + + // angle between the proton momentum and the flight direction, both rotated back to undo the bending + // of the proton between its reference point and the decay vertex + std::array protonPath{secVtx[0] - protonOrigPos[0], secVtx[1] - protonOrigPos[1], secVtx[2] - protonOrigPos[2]}; + float protonPathLength = std::sqrt(dot3(protonPath, protonPath)); + float svRadiusFromPv = std::hypot(secVtx[0] - pv[0], secVtx[1] - pv[1]); + float protonOrigRadiusFromPv = std::hypot(protonOrigPos[0] - pv[0], protonOrigPos[1] - pv[1]); + float propDir = (svRadiusFromPv < protonOrigRadiusFromPv) ? -1.f : 1.f; + float qProton = (protonTrack.sign() < 0) ? -1.f : 1.f; + float protonP = std::sqrt(dot3(pProton, pProton)); + float rCurve = protonP * 1000.f / (0.2998f * std::abs(mBz)); + float bzSign = (mBz < 0) ? -1.f : 1.f; + float alphaRot = -propDir * qProton * bzSign * o2::constants::math::PI; + if (protonPathLength / (2.f * rCurve) < 1.f) { + alphaRot = -2.f * propDir * qProton * bzSign * std::asin(protonPathLength / (2.f * rCurve)); + } + std::array pProtonRot{ + pProton[0] * std::cos(alphaRot) - pProton[1] * std::sin(alphaRot), + pProton[0] * std::sin(alphaRot) + pProton[1] * std::cos(alphaRot), + pProton[2]}; + float alphaVtxRot = -propDir * qProton * bzSign * o2::constants::math::PI * 0.5f; + if (flightDistance / (2.f * rCurve) < 1.f) { + alphaVtxRot = -propDir * qProton * bzSign * std::asin(flightDistance / (2.f * rCurve)); + } + std::array sigmaVertexVecRot{ + flightVec[0] * std::cos(alphaVtxRot) - flightVec[1] * std::sin(alphaVtxRot), + flightVec[0] * std::sin(alphaVtxRot) + flightVec[1] * std::cos(alphaVtxRot), + flightVec[2]}; + float antiSigmaPointingAngle = std::acos(std::clamp(dot3(pProtonRot, sigmaVertexVecRot) / std::sqrt(dot3(pProtonRot, pProtonRot) * dot3(sigmaVertexVecRot, sigmaVertexVecRot)), -1.f, 1.f)); + fillCandHist(HIST("hAntiSigmaPointingAngle"), isSignal, antiSigmaPointingAngle); + if (antiSigmaPointingAngle < candMinAntiSigmaPointingAngle || antiSigmaPointingAngle > candMaxAntiSigmaPointingAngle) { + return; + } + + fillCandHist(HIST("hMassSigmaPlus"), isSignal, bestMass); + fillCandHist(HIST("h2MassVsPt"), isSignal, ptSigma, bestMass); + fillCandHist(HIST("h2MassVsTilt"), isSignal, tiltAngle, bestMass); + if (bestMass > candMaxSigmaMass) { + return; + } + fillCandStep(9); // mass + + float candRapidity = RecoDecay::y(pSigma, o2::constants::physics::MassSigmaPlus); + fillCandHist(HIST("hRapidity"), isSignal, candRapidity); + if (std::abs(candRapidity) > candMaxRapidity) { + return; + } + fillCandStep(10); // rapidity + + // photon opening angle from the daughters' track momenta + float photonOpeningAngle = o2::aod::pwgem::dilepton::utils::pairutil::getOpeningAngle(posTrack.px(), posTrack.py(), posTrack.pz(), negTrack.px(), negTrack.py(), negTrack.pz()); + fillCandHist(HIST("hPhotonOpeningAngle"), isSignal, photonOpeningAngle); + if (photonOpeningAngle > candMaxPhotonOpeningAngle) { + return; + } + fillCandStep(11); // photon opening angle + + // angle between the photon momentum and the line from the decay vertex to the conversion point + std::array decVtxToConv{photon.x - secVtx[0], photon.y - secVtx[1], photon.z - secVtx[2]}; + float photonPointingAngle = std::acos(std::clamp(dot3(pGamma1, decVtxToConv) / std::sqrt(dot3(pGamma1, pGamma1) * dot3(decVtxToConv, decVtxToConv)), -1.f, 1.f)); + fillCandHist(HIST("hPhotonPointingAngle"), isSignal, photonPointingAngle); + if (photonPointingAngle > candMaxPhotonPointingAngle) { + return; + } + fillCandStep(12); // photon pointing angle + + std::array photonDir = normalize3({photon.px, photon.py, photon.pz}); + std::array pvToConv{pv[0] - photon.x, pv[1] - photon.y, pv[2] - photon.z}; + std::array pvToConvCrossDir = cross3(pvToConv, photonDir); + float photonDcaToPV = std::sqrt(dot3(pvToConvCrossDir, pvToConvCrossDir)); + fillCandHist(HIST("hPhotonDcaToPV"), isSignal, photonDcaToPV); + float photonPt2 = photon.px * photon.px + photon.py * photon.py; + float tAtDcaXY = photonPt2 > 0.f ? ((pv[0] - photon.x) * photon.px + (pv[1] - photon.y) * photon.py) / photonPt2 : 0.f; + float photonDcaXYToPV = std::hypot(photon.x + tAtDcaXY * photon.px - pv[0], photon.y + tAtDcaXY * photon.py - pv[1]); + float photonDcaZToPV = photon.z + tAtDcaXY * photon.pz - pv[2]; + if (photonDcaToPV > candMaxPhotonDcaToPV || std::abs(photonDcaZToPV) > candMaxPhotonDcaZToPV) { + return; + } + fillCandStep(13); // photon DCA to PV + float psiPair = photonPsiPair(posTrack, negTrack, photon.radius); + + SigmaPlusCandidate cand; + cand.photonId = photonId(posTrack.globalIndex(), negTrack.globalIndex()); + cand.collisionId = collisionIndex; + cand.protonTrackId = protonTrack.globalIndex(); + cand.photonPosTrackId = posTrack.globalIndex(); + cand.photonNegTrackId = negTrack.globalIndex(); + cand.isSignal = isSignal; + cand.photonLegsWithoutIts = photonLegsWithoutIts; + cand.matchedSigmaId = matchedSigmaId; + cand.decVtx = secVtx; + cand.radius = radius; + cand.flightDistance = flightDistance; + cand.dcaProtonGamma = dcaProtonGamma; + cand.dcaToPV = candDcaToPV; + cand.tiltAngle = tiltAngle; + cand.rootCenter = rootCenter; + cand.antiSigmaPointingAngle = antiSigmaPointingAngle; + cand.pProton = pProton; + cand.pGamma1 = pGamma1; + cand.pGamma2 = bestMomGamma2; + cand.protonTpcNSigma = protonTrack.tpcNSigmaPr(); + cand.protonTofNSigma = protonTrack.tofNSigmaPr(); + cand.protonSign = protonTrack.sign(); + cand.protonItsNCls = protonTrack.itsNCls(); + cand.protonTpcNCls = protonTrack.tpcNClsFound(); + cand.protonDcaXY = protonTrack.dcaXY(); + cand.protonDcaZ = protonTrack.dcaZ(); + cand.photonMass = photon.mGamma; + cand.photonAlpha = photon.alpha; + cand.photonQt = photon.qtarm; + cand.photonRadius = photon.radius; + cand.photonOpeningAngle = photonOpeningAngle; + cand.photonPointingAngle = photonPointingAngle; + cand.photonDcaDau = photon.dcaDau; + cand.photonCosPAToPV = photon.cosPAToPV; + cand.photonDcaXYToPV = photonDcaXYToPV; + cand.photonDcaZToPV = photonDcaZToPV; + cand.photonPsiPair = psiPair; + cand.posTpcNSigmaEl = posTrack.tpcNSigmaEl(); + cand.negTpcNSigmaEl = negTrack.tpcNSigmaEl(); + cand.posItsNCls = posTrack.itsNCls(); + cand.negItsNCls = negTrack.itsNCls(); + cand.posTpcNCls = posTrack.tpcNClsFound(); + cand.negTpcNCls = negTrack.tpcNClsFound(); + cand.posTpcNClsFindable = posTrack.tpcNClsFindable(); + cand.negTpcNClsFindable = negTrack.tpcNClsFindable(); + cand.posTpcChi2NCl = posTrack.tpcChi2NCl(); + cand.negTpcChi2NCl = negTrack.tpcChi2NCl(); + cand.posDcaXY = posTrack.dcaXY(); + cand.posDcaZ = posTrack.dcaZ(); + cand.negDcaXY = negTrack.dcaXY(); + cand.negDcaZ = negTrack.dcaZ(); + cand.collisionIdCheck = collisionIdCheck; + cand.protonIsSignal = protonIsSignal; + cand.photonIsSignal = photonIsSignal; + cand.decVtxMC = mcTrueVtx; + cand.pProtonMC = mcTrueMomProton; + cand.pGammaMC = mcTrueMomGamma; + cand.pSigmaPlusMC = mcTrueMomSigmaPlus; + cand.decayRadiusMC = decayRadiusMC; + cand.massMC = massMC; + mCandidatesOfTimeframe.push_back(cand); + } + + // BDT score + float computeMlScore(const SigmaPlusCandidate& cand) + { + constexpr float NoTofNSigmaThreshold = -900.f; // tofNSigma is -999 without TOF, NaN for the BDT + std::vector inputs{std::abs(cand.photonDcaZToPV), std::abs(cand.protonDcaXY), std::abs(cand.protonDcaZ), cand.dcaToPV, + cand.protonTpcNSigma, cand.protonTofNSigma < NoTofNSigmaThreshold ? std::numeric_limits::quiet_NaN() : cand.protonTofNSigma, + cand.antiSigmaPointingAngle, cand.dcaProtonGamma, cand.photonDcaXYToPV, cand.photonCosPAToPV, cand.tiltAngle}; + return mlResponse.getModelOutput(inputs, 0)[1]; + } + + template + void fillCandidateTables(const SigmaPlusCandidate& cand) + { + const float mlScore = applyMl ? computeMlScore(cand) : -1.f; + if constexpr (IsMC) { + if (fillSlimTables) { + slimSigmaPlusCandsMC(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.matchedSigmaId, + cand.radius, + cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, + cand.protonSign, + cand.protonDcaXY, cand.protonDcaZ, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore, + cand.collisionIdCheck, + cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, + cand.decayRadiusMC, cand.massMC, + cand.pSigmaPlusMC[0], cand.pSigmaPlusMC[1], cand.pSigmaPlusMC[2]); + return; + } + sigmaPlusCandsMC(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.matchedSigmaId, + cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + cand.flightDistance, cand.dcaProtonGamma, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, cand.photonAlpha, cand.photonQt, cand.photonRadius, + cand.photonOpeningAngle, cand.photonPointingAngle, + cand.rootCenter, cand.antiSigmaPointingAngle, cand.dcaToPV, cand.tiltAngle, + cand.protonSign, + cand.protonItsNCls, cand.protonTpcNCls, cand.protonDcaXY, cand.protonDcaZ, + cand.posItsNCls, cand.posTpcNCls, cand.negItsNCls, cand.negTpcNCls, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore, + cand.collisionIdCheck, + cand.isSignal, cand.protonIsSignal, cand.photonIsSignal, + cand.decVtxMC[0], cand.decVtxMC[1], cand.decVtxMC[2], + cand.pProtonMC[0], cand.pProtonMC[1], cand.pProtonMC[2], + cand.pGammaMC[0], cand.pGammaMC[1], cand.pGammaMC[2], + cand.pSigmaPlusMC[0], cand.pSigmaPlusMC[1], cand.pSigmaPlusMC[2], + cand.decayRadiusMC, cand.massMC); + } else { + if (fillSlimTables) { + slimSigmaPlusCands(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.radius, + cand.dcaToPV, cand.tiltAngle, cand.dcaProtonGamma, + cand.protonSign, + cand.protonDcaXY, cand.protonDcaZ, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore); + return; + } + sigmaPlusCands(cand.collisionId, cand.protonTrackId, cand.photonPosTrackId, cand.photonNegTrackId, + cand.decVtx[0], cand.decVtx[1], cand.decVtx[2], + cand.flightDistance, cand.dcaProtonGamma, + cand.pProton[0], cand.pProton[1], cand.pProton[2], + cand.pGamma1[0], cand.pGamma1[1], cand.pGamma1[2], + cand.pGamma2[0], cand.pGamma2[1], cand.pGamma2[2], + cand.protonTpcNSigma, cand.protonTofNSigma, + cand.posTpcNSigmaEl, cand.negTpcNSigmaEl, + cand.photonMass, cand.photonAlpha, cand.photonQt, cand.photonRadius, + cand.photonOpeningAngle, cand.photonPointingAngle, + cand.rootCenter, cand.antiSigmaPointingAngle, cand.dcaToPV, cand.tiltAngle, + cand.protonSign, + cand.protonItsNCls, cand.protonTpcNCls, cand.protonDcaXY, cand.protonDcaZ, + cand.posItsNCls, cand.posTpcNCls, cand.negItsNCls, cand.negTpcNCls, + cand.photonDcaDau, cand.photonCosPAToPV, cand.photonDcaXYToPV, cand.photonDcaZToPV, cand.photonPsiPair, + cand.posDcaXY, cand.posDcaZ, cand.negDcaXY, cand.negDcaZ, + cand.posTpcNClsFindable, cand.negTpcNClsFindable, cand.posTpcChi2NCl, cand.negTpcChi2NCl, + mlScore); + } + } + + // The same photon can be in candidates of several collisions + template + void deduplicateAndWriteCandidates() + { + std::unordered_map> candsByPhoton; + for (int iCand = 0; iCand < static_cast(mCandidatesOfTimeframe.size()); ++iCand) { + candsByPhoton[mCandidatesOfTimeframe[iCand].photonId].push_back(iCand); + } + std::vector keep(mCandidatesOfTimeframe.size(), !candDeduplicatePhotons); + for (const auto& [photon, cands] : candsByPhoton) { + histos.fill(HIST("Candidate/Inclusive/hCandidatesPerPhoton"), cands.size()); + if (candDeduplicatePhotons) { + int best = cands[0]; + for (const int& iCand : cands) { + if (mCandidatesOfTimeframe[iCand].dcaToPV < mCandidatesOfTimeframe[best].dcaToPV) { + best = iCand; + } + } + keep[best] = true; + } + } + + for (int iCand = 0; iCand < static_cast(mCandidatesOfTimeframe.size()); ++iCand) { + if (!keep[iCand]) { + continue; + } + const auto& cand = mCandidatesOfTimeframe[iCand]; + fillCandHist(HIST("hSelectionCounter"), cand.isSignal, 14); + fillCandHist(HIST("h2SelectionCounterVsLegType"), cand.isSignal, 14, cand.photonLegsWithoutIts); + if constexpr (IsMC) { + if (cand.isSignal) { + fillSigmaCounter(cand.matchedSigmaId, 5); + auto genIt = mGenSigmaWritten.find(cand.matchedSigmaId); + if (genIt != mGenSigmaWritten.end()) { + genIt->second = true; + } + } + } + fillCandidateTables(cand); + } + mCandidatesOfTimeframe.clear(); + } + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + auto* grpmag = ccdb->getForRun(grpmagPath, mRunNumber); + o2::base::Propagator::initFieldFromGRP(grpmag); + mBz = grpmag->getNominalL3Field(); + fitter.setBz(mBz); + if (!mLut) { + mLut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->get(lutPath)); + } + o2::base::Propagator::Instance()->setMatLUT(mLut); + LOG(info) << "Task initialized for run " << mRunNumber << " with magnetic field " << mBz << " kZG"; + } + + void processData(CollisionsFull const& collisions, aod::V0Datas const& v0s, TracksFull const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs) + { + markGoodCollisions(collisions); + std::vector>> photonsByCollision; + if (useCustomVertexer && collisions.size() > 0) { + auto firstBC = collisions.begin().bc_as(); + initCCDB(firstBC); + mVDriftMgr.update(firstBC.timestamp()); + photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + initCCDB(collision.bc_as()); + histos.fill(HIST("hVertexZ"), collision.posZ()); + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + + auto tracksThisCollision = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + + std::vector> acceptedPhotons; + if (useCustomVertexer) { + acceptedPhotons = photonsByCollision[collision.globalIndex()]; + } else { + auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); + acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); + } + + std::vector acceptedProtons; + for (const auto& track : tracksThisCollision) { + if (selectProton(track)) { + acceptedProtons.push_back(track); + } + } + + for (const auto& photon : acceptedPhotons) { + for (const auto& proton : acceptedProtons) { + buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); + } + } + } + deduplicateAndWriteCandidates(); + } + PROCESS_SWITCH(Sigmaplusbuilder, processData, "Process data", true); + + void processMc(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::AmbiguousTracks const& ambiguousTracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const& mcParticles, aod::McCollisions const&) + { + // generated Sigma+ -> p pi0 in acceptance + mGenSigmaWritten.clear(); + for (const auto& mcPart : mcParticles) { + if (!isSigmaPlusToProtonPi0(mcPart) || std::abs(mcPart.y()) > cutRapMotherMC || mcPart.pt() < cutPtGenMC) { + continue; + } + mGenSigmaWritten[mcPart.globalIndex()] = false; + histos.fill(HIST("MC/hGenSigmaPlusPt"), mcPart.pt()); + histos.fill(HIST("MC/hSigmaPlusCounter"), 0); + } + + constexpr int ElectronLeg = 1; + constexpr int PositronLeg = 2; + std::unordered_map> legsOfPhoton; + for (const auto& track : tracks) { + if (!track.has_mcParticle()) { + continue; + } + auto mcParticle = track.template mcParticle_as(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + fillSigmaCounter(findSigmaPlusMotherOfProton(mcParticle), 1); + } else if (std::abs(mcParticle.pdgCode()) == PDG_t::kElectron) { + int photonIndex = -1; + std::array conversionVertex{}; + int sigmaId = findSigmaPlusMotherOfConversionElectron(mcParticle, photonIndex, conversionVertex); + if (sigmaId >= 0) { + auto& legs = legsOfPhoton[photonIndex]; + legs.first = sigmaId; + legs.second |= (mcParticle.pdgCode() == PDG_t::kElectron ? ElectronLeg : PositronLeg); + } + } + } + for (const auto& [photonIndex, legs] : legsOfPhoton) { + if (legs.second == (ElectronLeg | PositronLeg)) { + fillSigmaCounter(legs.first, 3); + } + } + + markGoodCollisions(collisions); + std::vector>> photonsByCollision; + if (useCustomVertexer && collisions.size() > 0) { + auto firstBC = collisions.begin().bc_as(); + initCCDB(firstBC); + mVDriftMgr.update(firstBC.timestamp()); + photonsByCollision = findPhotonsSelfPaired(collisions, tracks, ambiguousTracks, bcs); + } + + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + initCCDB(collision.bc_as()); + histos.fill(HIST("hVertexZ"), collision.posZ()); + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + + auto tracksThisCollision = tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); + + std::vector> acceptedPhotons; + if (useCustomVertexer) { + acceptedPhotons = photonsByCollision[collision.globalIndex()]; + } else { + auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); + acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); + } + + std::vector acceptedProtons; + for (const auto& track : tracksThisCollision) { + if (selectProton(track)) { + acceptedProtons.push_back(track); + if (track.has_mcParticle()) { + fillSigmaCounter(findSigmaPlusMotherOfProton(track.template mcParticle_as()), 2); + } + } + } + + for (const auto& photon : acceptedPhotons) { + for (const auto& proton : acceptedProtons) { + buildSigmaPlusCandidate(proton, photon, collision.globalIndex(), pv); + } + } + } + deduplicateAndWriteCandidates(); + + // a table row with the true values for each generated Sigma+ in acceptance without a written candidate + for (const auto& mcPart : mcParticles) { + auto genIt = mGenSigmaWritten.find(mcPart.globalIndex()); + if (genIt == mGenSigmaWritten.end() || genIt->second) { + continue; + } + + int pdgProton = mcPart.pdgCode() > 0 ? PDG_t::kProton : PDG_t::kProtonBar; + std::array genDecVtx{-999.f, -999.f, -999.f}; + std::array genMomProton{-999.f, -999.f, -999.f}; + for (const auto& daughter : mcPart.template daughters_as()) { + if (daughter.pdgCode() == pdgProton) { + genDecVtx = {daughter.vx(), daughter.vy(), daughter.vz()}; + genMomProton = {daughter.px(), daughter.py(), daughter.pz()}; + break; + } + } + float genDecayRadiusMC = std::hypot(genDecVtx[0] - mcPart.vx(), genDecVtx[1] - mcPart.vy()); + float genMassMC = std::sqrt(mcPart.e() * mcPart.e() - mcPart.p() * mcPart.p()); + + if (fillSlimTables) { + slimSigmaPlusCandsMC(-1, -1, -1, -1, + mcPart.globalIndex(), + -999.f, + -999.f, -999.f, -999.f, + 0, + -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, + -999.f, -999.f, + -999.f, + -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + 0, 0, -999.f, -999.f, + -1.f, + false, + false, false, false, + genDecayRadiusMC, genMassMC, + mcPart.px(), mcPart.py(), mcPart.pz()); + continue; + } + + sigmaPlusCandsMC(-1, -1, -1, -1, + mcPart.globalIndex(), + -999.f, -999.f, -999.f, + -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, + -999.f, -999.f, + -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + 0, + 0, -999, -999.f, -999.f, + 0, -999, 0, -999, + -999.f, -999.f, -999.f, -999.f, -999.f, + -999.f, -999.f, -999.f, -999.f, + 0, 0, -999.f, -999.f, + -1.f, + false, + false, false, false, + genDecVtx[0], genDecVtx[1], genDecVtx[2], + genMomProton[0], genMomProton[1], genMomProton[2], + -999.f, -999.f, -999.f, + mcPart.px(), mcPart.py(), mcPart.pz(), + genDecayRadiusMC, genMassMC); + } + } + PROCESS_SWITCH(Sigmaplusbuilder, processMc, "Process MC", false); + + void processFindable(CollisionsFullMC const& collisions, aod::V0Datas const& v0s, TracksFullMC const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + mGenSigmaWritten.clear(); + constexpr int MinDauTpcCls = 90; + for (const auto& collision : collisions) { + if (std::abs(collision.posZ()) > cutZVertex || !collision.sel8()) { + continue; + } + initCCDB(collision.bc_as()); + auto tracksThisCollision = tracks.sliceBy(tracksPerCollisionMC, collision.globalIndex()); + auto v0sThisCollision = v0s.sliceBy(v0PerCollision, collision.globalIndex()); + std::array pv{collision.posX(), collision.posY(), collision.posZ()}; + auto acceptedPhotons = findPhotonsFromV0s(v0sThisCollision, tracksThisCollision, pv); + + for (const auto& protonTrack : tracksThisCollision) { + if (!protonTrack.has_mcParticle()) { + continue; + } + + auto mcProton = protonTrack.template mcParticle_as(); + int sigmaIndex = findSigmaPlusMotherOfProton(mcProton); + if (sigmaIndex < 0) { + continue; + } + + auto const& protonMothers = mcProton.template mothers_as(); + if (protonMothers.empty()) { + continue; + } + auto mcSigmaPlus = protonMothers.front(); + if (std::abs(mcSigmaPlus.y()) > 1) { + continue; + } + histos.fill(HIST("Findable/h2ProtonPtVsSigmaPlusPt"), mcSigmaPlus.pt(), protonTrack.pt()); + std::vector seenElectronMcIds; + std::vector seenElectronTrackIds; + std::vector seenPositronMcIds; + std::vector seenPositronTrackIds; + + for (const auto& electronTrack : tracksThisCollision) { + if (!electronTrack.has_mcParticle()) { + continue; + } + + if (electronTrack.tpcNClsFound() < MinDauTpcCls || electronTrack.sign() > 0) { + continue; + } + + auto mcElectron = electronTrack.template mcParticle_as(); + if (mcElectron.pdgCode() != PDG_t::kElectron) { + continue; + } + + int electronGammaIndex = -1; + std::array electronVertex{}; + if (findSigmaPlusMotherOfConversionElectron(mcElectron, electronGammaIndex, electronVertex) != sigmaIndex) { + continue; + } + + for (const auto& positronTrack : tracksThisCollision) { + if (!positronTrack.has_mcParticle()) { + continue; + } + + if (positronTrack.tpcNClsFound() < MinDauTpcCls || positronTrack.sign() < 0) { + continue; + } + + auto mcPositron = positronTrack.template mcParticle_as(); + if (mcPositron.pdgCode() != PDG_t::kPositron) { + continue; + } + + int positronGammaIndex = -1; + std::array positronVertex{}; + if (findSigmaPlusMotherOfConversionElectron(mcPositron, positronGammaIndex, positronVertex) != sigmaIndex || positronGammaIndex != electronGammaIndex) { + continue; + } + + bool sameConversionPoint = electronVertex[0] == positronVertex[0] && + electronVertex[1] == positronVertex[1] && + electronVertex[2] == positronVertex[2]; + if (!sameConversionPoint) { + continue; + } + + int electronMcId = mcElectron.globalIndex(); + int electronTrackId = electronTrack.globalIndex(); + int positronMcId = mcPositron.globalIndex(); + int positronTrackId = positronTrack.globalIndex(); + + bool seenElectronMc = false; + bool duplicateElectronTrack = false; + for (int iSeen = 0; iSeen < static_cast(seenElectronMcIds.size()); ++iSeen) { + if (seenElectronMcIds[iSeen] == electronMcId) { + seenElectronMc = true; + duplicateElectronTrack |= (seenElectronTrackIds[iSeen] != electronTrackId); + } + } + if (duplicateElectronTrack) { + histos.fill(HIST("Findable/hDuplicateConversionTrackCounter"), 0); + histos.fill(HIST("Findable/hDuplicateElectronTPCNClsFound"), electronTrack.tpcNClsFound()); + } + if (!seenElectronMc) { + seenElectronMcIds.push_back(electronMcId); + seenElectronTrackIds.push_back(electronTrackId); + } + + bool seenPositronMc = false; + bool duplicatePositronTrack = false; + for (int iSeen = 0; iSeen < static_cast(seenPositronMcIds.size()); ++iSeen) { + if (seenPositronMcIds[iSeen] == positronMcId) { + seenPositronMc = true; + duplicatePositronTrack |= (seenPositronTrackIds[iSeen] != positronTrackId); + } + } + if (duplicatePositronTrack) { + histos.fill(HIST("Findable/hDuplicateConversionTrackCounter"), 1); + histos.fill(HIST("Findable/hDuplicatePositronTPCNClsFound"), positronTrack.tpcNClsFound()); + } + if (!seenPositronMc) { + seenPositronMcIds.push_back(positronMcId); + seenPositronTrackIds.push_back(positronTrackId); + } + if (duplicateElectronTrack || duplicatePositronTrack) { + continue; + } + + std::array recoGammaMom{electronTrack.px() + positronTrack.px(), electronTrack.py() + positronTrack.py(), electronTrack.pz() + positronTrack.pz()}; + float recoGammaP = std::sqrt(dot3(recoGammaMom, recoGammaMom)); + constexpr float ElectronMass = o2::constants::physics::MassElectron; + float electronP2 = electronTrack.px() * electronTrack.px() + electronTrack.py() * electronTrack.py() + electronTrack.pz() * electronTrack.pz(); + float positronP2 = positronTrack.px() * positronTrack.px() + positronTrack.py() * positronTrack.py() + positronTrack.pz() * positronTrack.pz(); + float pairEnergy = std::sqrt(electronP2 + ElectronMass * ElectronMass) + std::sqrt(positronP2 + ElectronMass * ElectronMass); + float pairMass2 = pairEnergy * pairEnergy - recoGammaP * recoGammaP; + histos.fill(HIST("Findable/hElectronPositronMass"), std::sqrt(std::max(pairMass2, 0.f))); + + auto const& gammaMothers = mcElectron.template mothers_as(); + if (!gammaMothers.empty()) { + auto mcGamma = gammaMothers.front(); + std::array trueGammaMom{mcGamma.px(), mcGamma.py(), mcGamma.pz()}; + float trueGammaP = std::sqrt(dot3(trueGammaMom, trueGammaMom)); + if (trueGammaP > 0.f) { + histos.fill(HIST("Findable/hPhotonMomentumResolution"), (recoGammaP - trueGammaP) / trueGammaP); + } + } + + std::array photonFlightVec{electronVertex[0] - collision.posX(), electronVertex[1] - collision.posY(), electronVertex[2] - collision.posZ()}; + float flightNorm = std::sqrt(dot3(photonFlightVec, photonFlightVec)); + if (flightNorm > 0.f && recoGammaP > 0.f) { + histos.fill(HIST("Findable/hPhotonCosPA"), dot3(photonFlightVec, recoGammaMom) / (flightNorm * recoGammaP)); + } + + histos.fill(HIST("Findable/hSigmaPlusPt"), mcSigmaPlus.pt()); + histos.fill(HIST("Findable/hConversionPairV0Presence"), 0); + for (const auto& v0 : v0sThisCollision) { + auto posTrack = v0.template posTrack_as(); + auto negTrack = v0.template negTrack_as(); + bool sameChargeMatched = posTrack.globalIndex() == positronTrack.globalIndex() && negTrack.globalIndex() == electronTrack.globalIndex(); + if (sameChargeMatched) { + histos.fill(HIST("Findable/hConversionPairV0Presence"), 1); + break; + } + } + + histos.fill(HIST("Findable/hPhotonSearchPresence"), 0); + for (const auto& photon : acceptedPhotons) { + bool sameChargeMatched = photon.posTrack.globalIndex() == positronTrack.globalIndex() && photon.negTrack.globalIndex() == electronTrack.globalIndex(); + if (sameChargeMatched) { + histos.fill(HIST("Findable/hPhotonSearchPresence"), 1); + break; + } + } + histos.fill(HIST("Findable/hElectronPt"), electronTrack.pt()); + histos.fill(HIST("Findable/hPositronPt"), positronTrack.pt()); + histos.fill(HIST("Findable/hPhotonConversionRadius"), std::hypot(electronVertex[0], electronVertex[1])); + fillFindableTrackDetectors(electronTrack); + fillFindableTrackDetectors(positronTrack); + } + } + } + } + } + PROCESS_SWITCH(Sigmaplusbuilder, processFindable, "Process findable MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/stracents.cxx b/PWGLF/TableProducer/Strangeness/stracents.cxx index a50905d6a41..ad34db13e92 100644 --- a/PWGLF/TableProducer/Strangeness/stracents.cxx +++ b/PWGLF/TableProducer/Strangeness/stracents.cxx @@ -38,6 +38,7 @@ #include #include #include +#include #include using namespace o2; @@ -107,7 +108,7 @@ struct straCents { float mMCScalePars[6] = {0.0}; TFormula* mMCScale = nullptr; explicit CalibrationInfo(std::string name) - : name(name), + : name(std::move(name)), mCalibrationStored(false), mhMultSelCalib(nullptr), mMCScalePars{0.0}, @@ -216,7 +217,7 @@ struct straCents { } template - void initCCDB(TCollision collision) + void initCCDB(const TCollision& collision) { if (mRunNumber == collision.runNumber()) { return; @@ -348,7 +349,7 @@ struct straCents { } } else { // we are in Run 3 - auto getccdb = [lCalibObjects_Centrality, collision](struct CalibrationInfo& estimator, const Configurable generatorName, const Configurable notCrashOnNull) { // TODO: to consider the name inside the estimator structure + auto getccdb = [lCalibObjects_Centrality, collision](struct CalibrationInfo& estimator, const Configurable& generatorName, const Configurable& notCrashOnNull) { // TODO: to consider the name inside the estimator structure estimator.mhMultSelCalib = reinterpret_cast(lCalibObjects_Centrality->FindObject(TString::Format("hCalibZeq%s", estimator.name.c_str()).Data())); estimator.mMCScale = reinterpret_cast(lCalibObjects_Centrality->FindObject(TString::Format("%s-%s", generatorName->c_str(), estimator.name.c_str()).Data())); if (estimator.mhMultSelCalib != nullptr) { diff --git a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx index f4afc11f227..8007e538f89 100644 --- a/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangederivedbuilder.cxx @@ -9,11 +9,15 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // +/// \file strangederivedbuilder.cxx +/// \brief this task provides general links between collisions and strange objects reconstructed in various ways. +/// It is meant to help with providing auxiliary information when dealing with derived data. +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// //__________________________________________________ -// this task provides general links between collisions -// and strange objects reconstructed in various ways. -// It is meant to help with providing auxiliary information -// when dealing with derived data. +// #include "PWGLF/DataModel/EPCalibrationTables.h" #include "PWGLF/DataModel/LFStrangenessPIDTables.h" @@ -21,6 +25,8 @@ #include "PWGLF/DataModel/SPCalibrationTables.h" #include "PWGUD/DataModel/UDTables.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" @@ -52,6 +58,7 @@ #include #include +#include #include #include #include @@ -149,6 +156,10 @@ struct strangederivedbuilder { //__________________________________________________ // Debug Produces straOrigin; + + //__________________________________________________ + // Keep track of analysed collisions + Produces straSelection; } products; // histogram registry for bookkeeping @@ -180,6 +191,24 @@ struct strangederivedbuilder { Configurable fillEmptyCollisions{"fillEmptyCollisions", false, "fill collision entries without candidates"}; + // Configurable options for event selections + // --> configuration inherited from the strangenessBuilderModule in the propagationService + Configurable inheritEvtSelFromTask{"inheritEvtSelFromTask", "propagation-service", "Inherit event selection parameters from which task?"}; + Configurable inheritEvtSelFromTaskCfgGroup{"inheritEvtSelFromTaskCfgGroup", "eventSelectOpts", "What is the configurable group prefix? If none, put nothing"}; + Configurable inheritEvtSelFromTaskVerbose{"inheritEvtSelFromTaskVerbose", false, "Enable verbose mode on the getTaskOption utility"}; + bool fillOnlySelectedCollisions = false; // Fill only tables for selected collisions? + bool requireTriggerTVX = false; // require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level (Run 3 only) + bool rejectITSROFBorder = false; // reject events at ITS ROF border (Run 3 only) + bool rejectTFBorder = false; // reject events at TF border (Run 3 only) + bool rejectSameBunchPileup = false; // reject collisions in case of pileup with another collision in the same foundBC (Run 3 only) + float maxZVtxPosition = 10.; // max Z vtx position (cm) + bool cfgApplyRCTrequirement = false; // Apply RCT requirement? + std::string cfgRCTLabel = ""; // Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo) + bool cfgCheckZDC = false; // Include ZDC flags in the bit selection (for Pb-Pb only) + bool cfgTreatLimitedAcceptanceAsBad = false; // reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance + + o2::aod::rctsel::RCTFlagsChecker rctFlagsChecker; + // round Nsigma variables up to a certain level of precision if requested // useful to keep derived data sizes under control // variables that are rounded include the DCAs but not the CosPA (precision needed) @@ -216,6 +245,7 @@ struct strangederivedbuilder { } axisDetectors; // For manual sliceBy + Preslice TrackperCollision = o2::aod::track::collisionId; Preslice V0perCollision = o2::aod::v0data::collisionId; Preslice CascperCollision = o2::aod::cascdata::collisionId; Preslice KFCascperCollision = o2::aod::cascdata::collisionId; @@ -233,6 +263,13 @@ struct strangederivedbuilder { std::vector genOmegaMinus; std::vector genOmegaPlus; + // create collision indices beforehand + std::vector TrackGlobalBc; + std::vector V0CollIndices; // index -1: no collision + std::vector CascadeCollIndices; // index -1: no collision + std::vector KFCascadeCollIndices; // index -1: no collision + std::vector TraCascadeCollIndices; // index -1: no collision + float roundToPrecision(float number, float step = 0.01) { // this function rounds a certain number in an axis that is quantized by @@ -242,7 +279,52 @@ struct strangederivedbuilder { return step * static_cast(static_cast((number) / step)) + TMath::Sign(1.0f, number) * (0.5f) * step; } - void init(InitContext&) + template + void getCfg(o2::framework::InitContext& initContext, const std::string& name, VType& v, const std::string& task) + { + if (!o2::common::core::getTaskOptionValue(initContext, task, name, v, inheritEvtSelFromTaskVerbose)) { + LOG(fatal) << "Could not get " << name << " from " << task << " task"; + } + } + + template + bool isCollisionAccepted(TCollision const& collision, std::array& nSelected) + // check whether the collision passes our collision selections + { + if (requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + nSelected[o2::aod::straselections::kIsTriggerTVX]++; + + if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + nSelected[o2::aod::straselections::kNoITSROFrameBorder]++; + + if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + nSelected[o2::aod::straselections::kNoTimeFrameBorder]++; + + if (std::abs(collision.posZ()) > maxZVtxPosition) { + return false; + } + nSelected[o2::aod::straselections::kIsGoodZvtxAcceptance]++; + + if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + nSelected[o2::aod::straselections::kNoSameBunchPileup]++; + + if (!cfgApplyRCTrequirement && !rctFlagsChecker(collision)) { + return false; + } + nSelected[o2::aod::straselections::kIsGoodRCT]++; + + return true; + } + + void init(InitContext& initContext) { LOGF(info, "Initializing now: cross-checking correctness..."); if (doprocessCollisionsRun3 + @@ -380,7 +462,34 @@ struct strangederivedbuilder { } else { LOGF(info, "Process strange mothers...........: no"); } - LOGF(info, "=================================================================="); + LOGF(info, "====] Event selection information processing [===================="); + std::string eventSelectOptsPrefix = inheritEvtSelFromTaskCfgGroup.value; + if (!inheritEvtSelFromTaskCfgGroup.value.empty()) { + eventSelectOptsPrefix += "."; + } + + getCfg(initContext, eventSelectOptsPrefix + "fillOnlySelectedCollisions", fillOnlySelectedCollisions, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "requireTriggerTVX", requireTriggerTVX, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "rejectITSROFBorder", rejectITSROFBorder, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "rejectTFBorder", rejectTFBorder, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "rejectSameBunchPileup", rejectSameBunchPileup, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "maxZVtxPosition", maxZVtxPosition, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "cfgApplyRCTrequirement", cfgApplyRCTrequirement, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "cfgRCTLabel", cfgRCTLabel, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "cfgCheckZDC", cfgCheckZDC, inheritEvtSelFromTask); + getCfg(initContext, eventSelectOptsPrefix + "cfgTreatLimitedAcceptanceAsBad", cfgTreatLimitedAcceptanceAsBad, inheritEvtSelFromTask); + + LOG(info) << "Inherit event selections from : " << inheritEvtSelFromTask; + LOG(info) << "Fill only selected position........: " << (fillOnlySelectedCollisions ? "yes" : "no"); + LOG(info) << "Require TriggerTVX.................: " << (requireTriggerTVX ? "yes" : "no"); + LOG(info) << "Reject ITS ROF border..............: " << (rejectITSROFBorder ? "yes" : "no"); + LOG(info) << "Reject TF border...................: " << (rejectTFBorder ? "yes" : "no"); + LOG(info) << "Reject same bunch pile-up..........: " << (rejectSameBunchPileup ? "yes" : "no"); + LOG(info) << "Apply RCT requirement..............: " << (cfgApplyRCTrequirement ? "yes" : "no"); + LOG(info) << "RCT requirement....................: " << cfgRCTLabel; + LOG(info) << "Check ZDC in RCT requirement.......: " << (cfgCheckZDC ? "yes" : "no"); + LOG(info) << "Treat limited acceptance as bad....: " << (cfgCheckZDC ? "yes" : "no"); + LOG(info) << "================================================================="; // setup map for fast checking if enabled static_for<0, nSpecies - 1>([&](auto i) { @@ -413,6 +522,11 @@ struct strangederivedbuilder { auto hZNATimeVsZNAUD = histos.add("hZNATimeVsZNAUD", "hZNAMultVsZNAUD; ZNA time; ZNA UD", kTH2F, {axisDetectors.axisZNAtime, axisDetectors.axisZNAtime}); auto hZNCTimeVsZNCUD = histos.add("hZNCTimeVsZNCUD", "hZNCMultVsZNCUD; ZNC time; ZNC UD", kTH2F, {axisDetectors.axisZNCtime, axisDetectors.axisZNCtime}); + // Initialise the RCTFlagsChecker + if (cfgApplyRCTrequirement) { + rctFlagsChecker.init(cfgRCTLabel, cfgCheckZDC, cfgTreatLimitedAcceptanceAsBad); + } + for (int ii = 1; ii < 101; ii++) { float value = 100.5f - static_cast(ii); hRawCentrality->SetBinContent(ii, value); @@ -434,19 +548,31 @@ struct strangederivedbuilder { } // master function to process a collision - template - void populateCollisionTables(coll const& collisions, udcoll const& udCollisions, v0d const& V0s, cad const& Cascades, kfcad const& KFCascades, tracad const& TraCascades, bcType const& /*bcs*/) + template + void populateCollisionTables(coll const& collisions, udcoll const& udCollisions, tracks const& Tracks, v0d const& V0s, cad const& Cascades, kfcad const& KFCascades, tracad const& TraCascades, bcType const& /*bcs*/) { // create collision indices beforehand - std::vector V0CollIndices(V0s.size(), -1); // index -1: no collision - std::vector CascadeCollIndices(Cascades.size(), -1); // index -1: no collision - std::vector KFCascadeCollIndices(KFCascades.size(), -1); // index -1: no collision - std::vector TraCascadeCollIndices(TraCascades.size(), -1); // index -1: no collision + TrackGlobalBc.clear(); + V0CollIndices.clear(); + CascadeCollIndices.clear(); + KFCascadeCollIndices.clear(); + TraCascadeCollIndices.clear(); + + TrackGlobalBc.resize(Tracks.size(), 0); + V0CollIndices.resize(V0s.size(), -1); // index -1: no collision + CascadeCollIndices.resize(Cascades.size(), -1); // index -1: no collision + KFCascadeCollIndices.resize(KFCascades.size(), -1); // index -1: no collision + TraCascadeCollIndices.resize(TraCascades.size(), -1); // index -1: no collision + + int totalNbrCollisions = collisions.size(); + int totalNbrSelectedCollisions = 0; + std::array totalNbrCollisionsPerSelection = {}; // +-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+ for (const auto& collision : collisions) { const uint64_t collIdx = collision.globalIndex(); + auto TrackTable_thisColl = Tracks.sliceBy(TrackperCollision, collIdx); auto V0Table_thisColl = V0s.sliceBy(V0perCollision, collIdx); auto CascTable_thisColl = Cascades.sliceBy(CascperCollision, collIdx); auto KFCascTable_thisColl = KFCascades.sliceBy(KFCascperCollision, collIdx); @@ -481,7 +607,7 @@ struct strangederivedbuilder { if constexpr (requires { udCollIterator.gapSide(); }) { // check if this table is the expected one auto udCollision = udCollisions.sliceBy(udCollisionsPerCollision, collIdx); if (udCollision.size() == 1) { // check that the slicing provide a unique UD collision - for (auto& udColl : udCollision) { + for (const auto& udColl : udCollision) { gapSide = udColl.gapSide(); totalFT0AmplitudeA = udColl.totalFT0AmplitudeA(); totalFT0AmplitudeC = udColl.totalFT0AmplitudeC(); @@ -518,6 +644,13 @@ struct strangederivedbuilder { } } + for (const auto& track : TrackTable_thisColl) + TrackGlobalBc[track.globalIndex()] = bc.globalBC(); + + if (fillOnlySelectedCollisions && !isCollisionAccepted(collision, totalNbrCollisionsPerSelection)) { + continue; + } + // +-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+-<*>-+ // fill collision tables if (strange || fillEmptyCollisions) { @@ -569,31 +702,31 @@ struct strangederivedbuilder { collision.multNTracksPVeta1() * static_cast(fillTruncationOptions.fillRawNTracksEta1), collision.multPVTotalContributors() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), collision.multNTracksGlobal() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), - collision.multNTracksITSTPC() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), - collision.multAllTracksTPCOnly() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), - collision.multAllTracksITSTPC() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), - collision.multZNA() * static_cast(fillTruncationOptions.fillRawZDC), - collision.multZNC() * static_cast(fillTruncationOptions.fillRawZDC), - collision.multZEM1() * static_cast(fillTruncationOptions.fillRawZDC), - collision.multZEM2() * static_cast(fillTruncationOptions.fillRawZDC), - collision.multZPA() * static_cast(fillTruncationOptions.fillRawZDC), - collision.multZPC() * static_cast(fillTruncationOptions.fillRawZDC), - collision.trackOccupancyInTimeRange(), - collision.ft0cOccupancyInTimeRange(), - // UPC info - gapSide, - totalFT0AmplitudeA, totalFT0AmplitudeC, totalFV0AmplitudeA, - totalFDDAmplitudeA, totalFDDAmplitudeC, - energyCommonZNA, energyCommonZNC, // Collision flags collision.flags(), collision.alias_raw(), collision.rct_raw()); - products.strangeEvSelExtras(timeZNA, timeZNC, // ZDC info - timeFDDA, timeFDDC, // FDD info - timeFV0A, // FV0A info - timeFT0A, timeFT0C, ft0TriggerMask); // FT0 info - } else { // We are in Run 2 + products.strangeEvSelExtras(collision.multZNA() * static_cast(fillTruncationOptions.fillRawZDC), + collision.multZNC() * static_cast(fillTruncationOptions.fillRawZDC), + collision.multZEM1() * static_cast(fillTruncationOptions.fillRawZDC), + collision.multZEM2() * static_cast(fillTruncationOptions.fillRawZDC), + collision.multZPA() * static_cast(fillTruncationOptions.fillRawZDC), + collision.multZPC() * static_cast(fillTruncationOptions.fillRawZDC), + collision.multNTracksITSTPC() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), + collision.multAllTracksTPCOnly() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), + collision.multAllTracksITSTPC() * static_cast(fillTruncationOptions.fillRawNTracksForCorrelation), + collision.trackOccupancyInTimeRange(), + collision.ft0cOccupancyInTimeRange(), + timeFDDA, timeFDDC, // FDD info + timeFV0A, // FV0A info + timeFT0A, timeFT0C, ft0TriggerMask, // FT0 info + // UPC info + gapSide, + totalFT0AmplitudeA, totalFT0AmplitudeC, totalFV0AmplitudeA, + totalFDDAmplitudeA, totalFDDAmplitudeC, + timeZNA, timeZNC, // ZDC info + energyCommonZNA, energyCommonZNC); + } else { // We are in Run 2 products.strangeCentsRun2(collision.centRun2V0M(), collision.centRun2V0A(), collision.centRun2SPDTracklets(), collision.centRun2SPDClusters()); products.strangeEvSelsRun2(collision.sel8(), collision.sel7(), collision.selection_raw(), @@ -620,6 +753,7 @@ struct strangederivedbuilder { collision.multZPC() * static_cast(fillTruncationOptions.fillRawZDC), collision.alias_raw()); } + totalNbrSelectedCollisions++; } for (const auto& v0 : V0Table_thisColl) V0CollIndices[v0.globalIndex()] = products.strangeColl.lastIndex(); @@ -645,6 +779,13 @@ struct strangederivedbuilder { for (const auto& casc : TraCascades) { products.tracasccollref(TraCascadeCollIndices[casc.globalIndex()]); } + products.straSelection(totalNbrCollisions, totalNbrSelectedCollisions, + totalNbrCollisionsPerSelection[o2::aod::straselections::kIsTriggerTVX], + totalNbrCollisionsPerSelection[o2::aod::straselections::kNoITSROFrameBorder], + totalNbrCollisionsPerSelection[o2::aod::straselections::kNoTimeFrameBorder], + totalNbrCollisionsPerSelection[o2::aod::straselections::kIsGoodZvtxAcceptance], + totalNbrCollisionsPerSelection[o2::aod::straselections::kNoSameBunchPileup], + totalNbrCollisionsPerSelection[o2::aod::straselections::kIsGoodRCT]); } // helper function to estimate collision time @@ -652,21 +793,31 @@ struct strangederivedbuilder { void populateEventTimes(coll const& collisions, TTracks const& tracks) { std::vector collisionEventTime(collisions.size(), 0.0); + std::vector collisionEventTimeErr(collisions.size(), 0.0); std::vector collisionNtracks(collisions.size(), 0); + + std::array totalNbrCollisionsPerSelection = {}; for (const auto& track : tracks) { if (track.hasTOF() && track.collisionId() >= 0) { collisionEventTime[track.collisionId()] += track.tofEvTime(); + // Take the average of the error instead of propagating the error as all event time error from tracks are fully correlated + collisionEventTimeErr[track.collisionId()] += track.tofEvTimeErr(); collisionNtracks[track.collisionId()]++; } } for (const auto& collision : collisions) { if (collisionNtracks[collision.globalIndex()] > 0) { collisionEventTime[collision.globalIndex()] /= static_cast(collisionNtracks[collision.globalIndex()]); + collisionEventTimeErr[collision.globalIndex()] /= static_cast(collisionNtracks[collision.globalIndex()]); } else { - collisionEventTime[collision.globalIndex()] = -1e+6; // undefined + collisionEventTime[collision.globalIndex()] = -1e+6; // undefined + collisionEventTimeErr[collision.globalIndex()] = -1e-6; // undefined + } + if (fillOnlySelectedCollisions && !isCollisionAccepted(collision, totalNbrCollisionsPerSelection)) { + continue; } histos.fill(HIST("h2dCollisionTimesVsNTracks"), collisionNtracks[collision.globalIndex()], collisionEventTime[collision.globalIndex()]); - products.straEvTimes(collisionEventTime[collision.globalIndex()]); + products.straEvTimes(collisionEventTime[collision.globalIndex()], collisionEventTimeErr[collision.globalIndex()]); } } @@ -703,6 +854,7 @@ struct strangederivedbuilder { products.strangeMCColl(mccollision.posX(), mccollision.posY(), mccollision.posZ(), mccollision.impactParameter(), mccollision.eventPlaneAngle(), mccollision.generatorsID()); products.strangeMCMults(mccollision.multMCFT0A(), mccollision.multMCFT0C(), + mccollision.multMCFV0A(), mccollision.multMCFDDA(), mccollision.multMCFDDC(), mccollision.multMCNParticlesEta05(), mccollision.multMCNParticlesEta08(), mccollision.multMCNParticlesEta10(), @@ -710,37 +862,37 @@ struct strangederivedbuilder { } } - void processCollisionsRun3(soa::Join const& collisions, aod::V0Datas const& V0s, aod::CascDatas const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&) + void processCollisionsRun3(soa::Join const& collisions, aod::Tracks const& tracks, aod::V0Datas const& V0s, aod::CascDatas const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&) { - populateCollisionTables(collisions, collisions, V0s, Cascades, KFCascades, TraCascades, bcs); + populateCollisionTables(collisions, collisions, tracks, V0s, Cascades, KFCascades, TraCascades, bcs); } - void processCollisionsRun3WithUD(soa::Join const& collisions, aod::V0Datas const& V0s, aod::CascDatas const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&, UDCollisionsFull const& udCollisions) + void processCollisionsRun3WithUD(soa::Join const& collisions, aod::Tracks const& tracks, aod::V0Datas const& V0s, aod::CascDatas const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&, UDCollisionsFull const& udCollisions) { - populateCollisionTables(collisions, udCollisions, V0s, Cascades, KFCascades, TraCascades, bcs); + populateCollisionTables(collisions, udCollisions, tracks, V0s, Cascades, KFCascades, TraCascades, bcs); } - void processCollisionsRun3WithMC(soa::Join const& collisions, soa::Join const& V0s, soa::Join const& /*V0MCCores*/, soa::Join const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) + void processCollisionsRun3WithMC(soa::Join const& collisions, aod::Tracks const& tracks, soa::Join const& V0s, soa::Join const& /*V0MCCores*/, soa::Join const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) { populateMCCollisionTable(mcCollisions, mcParticles); - populateCollisionTables(collisions, collisions, V0s, Cascades, KFCascades, TraCascades, bcs); + populateCollisionTables(collisions, collisions, tracks, V0s, Cascades, KFCascades, TraCascades, bcs); } - void processCollisionsRun3WithUDWithMC(soa::Join const& collisions, soa::Join const& V0s, soa::Join const& /*V0MCCores*/, soa::Join const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&, UDCollisionsFull const& udCollisions, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) + void processCollisionsRun3WithUDWithMC(soa::Join const& collisions, aod::Tracks const& tracks, soa::Join const& V0s, soa::Join const& /*V0MCCores*/, soa::Join const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, soa::Join const& bcs, aod::FT0s const&, aod::FV0As const&, aod::FDDs const&, aod::Zdcs const&, UDCollisionsFull const& udCollisions, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) { populateMCCollisionTable(mcCollisions, mcParticles); - populateCollisionTables(collisions, udCollisions, V0s, Cascades, KFCascades, TraCascades, bcs); + populateCollisionTables(collisions, udCollisions, tracks, V0s, Cascades, KFCascades, TraCascades, bcs); } - void processCollisionsRun2(soa::Join const& collisions, aod::V0Datas const& V0s, aod::CascDatas const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, BCsWithTimestampsAndRun2Infos const& bcs) + void processCollisionsRun2(soa::Join const& collisions, aod::Tracks const& tracks, aod::V0Datas const& V0s, aod::CascDatas const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, BCsWithTimestampsAndRun2Infos const& bcs) { - populateCollisionTables(collisions, collisions, V0s, Cascades, KFCascades, TraCascades, bcs); + populateCollisionTables(collisions, collisions, tracks, V0s, Cascades, KFCascades, TraCascades, bcs); } - void processCollisionsRun2WithMC(soa::Join const& collisions, soa::Join const& V0s, soa::Join const& /*V0MCCores*/, soa::Join const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, BCsWithTimestampsAndRun2Infos const& bcs, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) + void processCollisionsRun2WithMC(soa::Join const& collisions, aod::Tracks const& tracks, soa::Join const& V0s, soa::Join const& /*V0MCCores*/, soa::Join const& Cascades, aod::KFCascDatas const& KFCascades, aod::TraCascDatas const& TraCascades, BCsWithTimestampsAndRun2Infos const& bcs, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) { populateMCCollisionTable(mcCollisions, mcParticles); - populateCollisionTables(collisions, collisions, V0s, Cascades, KFCascades, TraCascades, bcs); + populateCollisionTables(collisions, collisions, tracks, V0s, Cascades, KFCascades, TraCascades, bcs); } void processTrackExtrasV0sOnly(aod::V0Datas const& V0s, TracksWithExtra const& tracksExtra) @@ -896,7 +1048,7 @@ struct strangederivedbuilder { aod::dautrack::packing::packInInt8(tr.tpcNSigmaPr())); // populate daughter-level TOF information if (tr.hasTOF()) { - products.dauTrackTOFPIDs(tr.collisionId(), products.dauTrackExtras.lastIndex(), tr.tofSignal(), tr.tofEvTime(), tr.length(), tr.tofExpMom()); + products.dauTrackTOFPIDs(TrackGlobalBc[tr.globalIndex()], products.dauTrackExtras.lastIndex(), tr.tofSignal(), tr.tofEvTime(), tr.tofEvTimeErr(), tr.length(), tr.tofExpMom()); } } else { // populate with empty fully-compatible Nsigmas if no corresponding table available @@ -907,7 +1059,7 @@ struct strangederivedbuilder { // done! } - void processPopulateEventTimes(aod::Collisions const& collisions, soa::Join const& tracks) + void processPopulateEventTimes(soa::Join const& collisions, soa::Join const& tracks) { populateEventTimes(collisions, tracks); } @@ -1029,8 +1181,8 @@ struct strangederivedbuilder { void processPureSimulation(aod::McParticles const& mcParticles) { - for (auto& mcp : mcParticles) { - if (TMath::Abs(mcp.y()) < 0.5) { + for (auto const& mcp : mcParticles) { + if (std::abs(mcp.y()) < 0.5) { static_for<0, nSpecies - 1>([&](auto i) { constexpr int index = i.value; if (mcp.pdgCode() == particlePDGCodes[index] && bitcheck(enabledBits, index)) { @@ -1047,7 +1199,7 @@ struct strangederivedbuilder { // identify best-of collision int biggestNContribs = -1; float bestCentrality = 100.5; - for (auto& collision : collisions) { + for (auto const& collision : collisions) { if (biggestNContribs < collision.numContrib()) { biggestNContribs = collision.numContrib(); bestCentrality = collision.centFT0C(); @@ -1059,8 +1211,8 @@ struct strangederivedbuilder { } histos.fill(HIST("h2dNVerticesVsCentrality"), bestCentrality, collisions.size()); - for (auto& mcp : mcParticles) { - if (TMath::Abs(mcp.y()) < 0.5 && mcp.isPhysicalPrimary()) { + for (auto const& mcp : mcParticles) { + if (std::abs(mcp.y()) < 0.5 && mcp.isPhysicalPrimary()) { static_for<0, nSpecies - 1>([&](auto i) { constexpr int index = i.value; if (mcp.pdgCode() == particlePDGCodes[index] && bitcheck(enabledBits, index)) { @@ -1083,29 +1235,29 @@ struct strangederivedbuilder { std::fill(genOmegaPlus.begin(), genOmegaPlus.end(), 0); // this process function also checks if a given collision was reconstructed and checks explicitly for splitting, etc - for (auto& mcCollision : mcCollisions) { + for (auto const& mcCollision : mcCollisions) { const uint64_t mcCollIndex = mcCollision.globalIndex(); // use one of the generated histograms as the bin finder auto hBinFinder = histos.get(HIST("h2dGeneratedK0Short")); auto mcParticles = mcParticlesEntireTable.sliceBy(mcParticlePerMcCollision, mcCollIndex); - for (auto& mcp : mcParticles) { - if (TMath::Abs(mcp.y()) < 0.5 && mcp.isPhysicalPrimary()) { + for (auto const& mcp : mcParticles) { + if (std::abs(mcp.y()) < 0.5 && mcp.isPhysicalPrimary()) { auto binNumber = hBinFinder->FindBin(mcCollision.bestCollisionCentFT0C(), mcp.pt()); // caution: pack - if (mcp.pdgCode() == 310) + if (mcp.pdgCode() == PDG_t::kK0Short) genK0Short[binNumber]++; - if (mcp.pdgCode() == 3122) + if (mcp.pdgCode() == PDG_t::kLambda0) genLambda[binNumber]++; - if (mcp.pdgCode() == -3122) + if (mcp.pdgCode() == PDG_t::kLambda0Bar) genAntiLambda[binNumber]++; - if (mcp.pdgCode() == 3312) + if (mcp.pdgCode() == PDG_t::kXiMinus) genXiMinus[binNumber]++; - if (mcp.pdgCode() == -3312) + if (mcp.pdgCode() == PDG_t::kXiPlusBar) genXiPlus[binNumber]++; - if (mcp.pdgCode() == 3334) + if (mcp.pdgCode() == PDG_t::kOmegaMinus) genOmegaMinus[binNumber]++; - if (mcp.pdgCode() == -3334) + if (mcp.pdgCode() == PDG_t::kOmegaPlusBar) genOmegaPlus[binNumber]++; } } diff --git a/PWGLF/TableProducer/Strangeness/strangenessMcCentrality.cxx b/PWGLF/TableProducer/Strangeness/strangenessMcCentrality.cxx new file mode 100644 index 00000000000..31c969b5303 --- /dev/null +++ b/PWGLF/TableProducer/Strangeness/strangenessMcCentrality.cxx @@ -0,0 +1,71 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file mcCentrality.cxx +/// \author Romain Schotter romain.schotter@cern.ch +/// \brief Task to produce the MC centrality table for strangeness derived data +/// + +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/mcCentralityModule.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +/// Task to produce the response table +struct StrangenessMcCentrality { + // Input parameters + o2::framework::Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Service ccdb; + + o2::pwglf::mccentrality::products products; + o2::pwglf::mccentrality::coreConfigurables baseOpts; + o2::pwglf::mccentrality::BuilderModule mcCentralityBuilderModule; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(o2::framework::InitContext& initContext) + { + // Set up the CCDB + ccdb->setURL(ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + mcCentralityBuilderModule.init(baseOpts, histos, initContext); + } + + // Full tables (independent on central calibrations) + void process(aod::StraMCCollMults const& mcCollisions, + aod::StraStamps const& bcs) + { + mcCentralityBuilderModule.dataProcess(ccdb, histos, bcs, mcCollisions, products); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx index df63da1d88e..493244cb856 100644 --- a/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx +++ b/PWGLF/TableProducer/Strangeness/strangenessbuilder.cxx @@ -52,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -686,7 +687,7 @@ struct StrangenessBuilder { LOGF(info, "Configuring tables to generate"); auto& workflows = context.services().get(); - TString listOfRequestors[nTables]; + std::array listOfRequestors{}; for (int i = 0; i < nTables; i++) { // adjust bookkeeping histogram h->GetXaxis()->SetBinLabel(i + 1, tableNames[i].c_str()); @@ -1005,7 +1006,7 @@ struct StrangenessBuilder { AvgPA, // 6. Avg Pointing Angle static_cast(v0zRanks[ic])}; // 7. V0 Vtx z Rank - float* BDTProbability = deduplication_bdt.evalModel(inputFeatures); + const std::vector BDTProbability = deduplication_bdt.evalModel(inputFeatures); if (BDTProbability[1] > bestMLScore) { bestMLScore = BDTProbability[1]; @@ -2535,7 +2536,7 @@ struct StrangenessBuilder { thisCascInfo.xyz[2] = dau.vz(); thisCascInfo.mcParticleBachelor = dau.globalIndex(); } - if (std::abs(dau.pdgCode()) == 2212) { + if (std::abs(dau.pdgCode()) == 3122) { thisCascInfo.pdgCodeV0 = dau.pdgCode(); for (const auto& v0Dau : dau.template daughters_as()) { diff --git a/PWGLF/TableProducer/Strangeness/strangenesstofpid.cxx b/PWGLF/TableProducer/Strangeness/strangenesstofpid.cxx index cd189e45c83..5284394277e 100644 --- a/PWGLF/TableProducer/Strangeness/strangenesstofpid.cxx +++ b/PWGLF/TableProducer/Strangeness/strangenesstofpid.cxx @@ -13,6 +13,7 @@ // Strangeness TOF PID // *+-+*+-+*+-+*+-+*+-+*+-+* // +/// \file strangenesstofpid.cxx /// \author Nicolò Jacazio /// \author David Dobrigkeit Chinellato /// \since 11/05/2023 @@ -26,6 +27,7 @@ #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "Common/Core/PID/PIDTOFParamService.h" #include "Common/Core/TableHelper.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -48,7 +50,9 @@ #include #include #include +#include #include +#include #include #include @@ -57,8 +61,6 @@ #include #include -#include - #include #include #include @@ -76,7 +78,7 @@ using std::array; // For original data loops using V0OriginalDatas = soa::Join; using CascOriginalDatas = soa::Join; -using TracksWithAllExtras = soa::Join; +using TracksWithAllExtras = soa::Join; // For derived data analysis using dauTracks = soa::Join; @@ -94,7 +96,11 @@ struct strangenesstofpid { Produces casctofpids; // cascades: table with base info Produces casctofnsigmas; // cascades: table with Nsigmas + Produces v0tofnsigmasall; // table with nsigmas including wrong hypothesis + Produces casctofnsigmasall; // cascades: table with Nsigmas including wrong hypothesis + Service ccdb; + Service mTOFResponse; // mean vertex position to be used if no collision associated o2::dataformats::MeanVertexObject* mVtx = nullptr; @@ -108,10 +114,14 @@ struct strangenesstofpid { Configurable calculationMethod{"calculationMethod", 0, "algorithm for TOF calculation. 0: fast analytical withouot eloss, 1: O2 Propagator + trackLTIntegral (slow), 2: both methods and do comparison studies (slow)"}; Configurable calculateV0s{"calculateV0s", -1, "calculate V0-related TOF PID (0: no, 1: yes, -1: auto)"}; Configurable calculateCascades{"calculateCascades", -1, "calculate cascade-related TOF PID (0: no, 1: yes, -1: auto)"}; + Configurable useNsigmaCalibStrTOF{"useNsigmaCalibStrTOF", true, "if true, use manual strangeness TOF PID calibration"}; Configurable reassociateTracks{"reassociateTracks", true, "if true, reassociate tracks to the collision the V0 or cascade belongs to. Relevant especially at high IR"}; Configurable doBCshift{"doBCshift", true, "if true, perform time shift for collisions in different BCs when reassigning"}; Configurable rejectUndefinedTof{"rejectUndefinedTof", true, "if true, reject tracks with TOF signal 0.000f for safety"}; + Configurable calculateV0sNSigmaAll{"calculateV0sNSigmaAll", -1, "calculate all V0-related TOF n sigma(0: no, 1: yes, -1: auto)"}; + Configurable calculateCascadesNSigmaAll{"calculateCascadesNSigmaAll", -1, "calculate all cascade-related TOF n sigma(0: no, 1: yes, -1: auto)"}; + // auxiliary / debug tables as desired Configurable calculateV0TOFPIDs{"calculateV0TOFPIDs", -1, "calculate V0TOFPIDs table (0: no, 1: yes, -1: auto)"}; Configurable calculateV0TOFBetas{"calculateV0TOFBetas", -1, "calculate V0TOFBetas table (0: no, 1: yes, -1: auto)"}; @@ -150,15 +160,41 @@ struct strangenesstofpid { // CCDB options struct : ConfigurableGroup { - std::string prefix = "ccdb"; + // std::string prefix = "ccdb"; Configurable ccdburl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; Configurable nSigmaPath{"nSigmaPath", "Users/d/ddobrigk/stratof", "Path of information for n-sigma calculation"}; Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; + // necessary for TOFResponse + Configurable grpLhcIfPath{"ccdb-path-grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + Configurable timestamp{"ccdb-timestamp", -1, "timestamp of the object"}; + Configurable timeShiftCCDBPathPos{"timeShiftCCDBPathPos", "Analysis/PID/TOFOffsetPos", "Path of the TOF time shift vs eta for pos. tracks. If empty none is taken"}; + Configurable timeShiftCCDBPathNeg{"timeShiftCCDBPathNeg", "Analysis/PID/TOFOffsetNeg", "Path of the TOF time shift vs eta for neg. tracks. If empty none is taken"}; + Configurable timeShiftCCDBPathPosMC{"timeShiftCCDBPathPosMC", "", "Path of the TOF time shift for MC vs eta for pos. tracks. If empty none is taken"}; + Configurable timeShiftCCDBPathNegMC{"timeShiftCCDBPathNegMC", "", "Path of the TOF time shift for MC vs eta for neg. tracks. If empty none is taken"}; + Configurable paramFileName{"paramFileName", "", "Path to the parametrization object. If empty the parametrization is not taken from file"}; + Configurable parametrizationPath{"parametrizationPath", "TOF/Calib/Params", "Path of the TOF parametrization on the CCDB or in the file, if the paramFileName is not empty"}; + Configurable reconstructionPass{"reconstructionPass", "metadata", {"Apass to use when fetching the calibration tables. Empty (default) does not check for any pass. Use `metadata` to fetch it from the AO2D metadata. Otherwise it will override the metadata."}}; + Configurable reconstructionPassDefault{"reconstructionPassDefault", "unanchored", {"Default pass to get if the standard one is not found"}}; + Configurable fatalOnPassNotAvailable{"fatalOnPassNotAvailable", false, "Flag to throw a fatal if the pass is not available in the retrieved CCDB object"}; + Configurable enableTimeDependentResponse{"enableTimeDependentResponse", true, "Flag to use the collision timestamp to fetch the PID Response"}; + Configurable collisionSystem{"collisionSystem", -1, "Collision system: -1 (autoset), 0 (pp), 1 (PbPb), 2 (XeXe), 3 (pPb)"}; + Configurable autoSetProcessFunctions{"autoSetProcessFunctions", true, "Flag to autodetect the process functions to use"}; } ccdbConfigurations; + // Metadata options + struct : ConfigurableGroup { + std::string prefix = "metadata"; + // necessary for TOFResponse when running over derived data (where no metadata can be found) + Configurable overrideMetadata{"overrideMetadata", false, "Override metadataInfo with custom information. Enable when running over derived data"}; + Configurable Run{"Run", "3", "Is it Run 3 ? If yes: 3 ; otherwise: 2"}; + Configurable DataType{"DataType", "Data", "Is it Data? If yes: Data ; otherwise: MC"}; + Configurable AnchorPassName{"AnchorPassName", "apass1", "Anchor pass name (only if needed for DataType = MC)"}; + Configurable RecoPassName{"RecoPassName", "apass1", "Reco pass name (only if needed for DataType = Data)"}; + } metadataConfigurations; + // manual Configurable useCustomRunNumber{"useCustomRunNumber", false, "Use custom timestamp"}; Configurable manualRunNumber{"manualRunNumber", 544122, "manual run number if no collisions saved"}; @@ -208,10 +244,10 @@ struct strangenesstofpid { TH1 *hMeanNegOmPr = nullptr, *hSigmaNegOmPr = nullptr; TH1 *hMeanBachOmKa = nullptr, *hSigmaBachOmKa = nullptr; - int mRunNumber; - float d_bz; - float maxSnp; // max sine phi for propagation - float maxStep; // max step size (cm) for propagation + int mRunNumber = 0; + float d_bz = 0.; + float maxSnp = 0.85; // max sine phi for propagation + float maxStep = 2.00; // max step size (cm) for propagation // enum to keep track of the TOF-related properties for V0s enum tofEnum { kLength = 0, @@ -290,7 +326,7 @@ struct strangenesstofpid { cosAngle1 /= modulus1; sinAngle1 /= modulus1; length1 = trcCircle.rC * TMath::ACos(cosAngle1); - length1 *= sqrt(1.0f + track.getTgl() * track.getTgl()); + length1 *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); modulus2 = std::hypot(interceptX2 - trcCircle.xC, interceptY2 - trcCircle.yC) * std::hypot(startPoint[0] - trcCircle.xC, startPoint[1] - trcCircle.yC); cosAngle2 = (interceptX2 - trcCircle.xC) * (startPoint[0] - trcCircle.xC) + (interceptY2 - trcCircle.yC) * (startPoint[1] - trcCircle.yC); @@ -298,7 +334,7 @@ struct strangenesstofpid { cosAngle2 /= modulus2; sinAngle2 /= modulus2; length2 = trcCircle.rC * TMath::ACos(cosAngle2); - length2 *= sqrt(1.0f + track.getTgl() * track.getTgl()); + length2 *= std::sqrt(1.0f + track.getTgl() * track.getTgl()); // rotate transverse momentum vector such that it is at intercepts float angle1 = TMath::ACos(cosAngle1); @@ -400,6 +436,21 @@ struct strangenesstofpid { } } + if (calculateV0sNSigmaAll.value < 0) { + // check if TOF information is required, enable if so + calculateV0sNSigmaAll.value = isTableRequiredInWorkflow(initContext, "V0TOFNSigmasAll"); + if (calculateV0sNSigmaAll.value > 0) { + LOGF(info, "Strangeness TOF PID: all V0 TOF PID calculations enabled automatically"); + } + } + if (calculateCascadesNSigmaAll.value < 0) { + // check if TOF information is required, enable if so + calculateCascadesNSigmaAll.value = isTableRequiredInWorkflow(initContext, "CascTOFNSigmasAll"); + if (calculateCascadesNSigmaAll.value > 0) { + LOGF(info, "Strangeness TOF PID: all Cascade TOF PID calculations enabled automatically"); + } + } + nSigmaCalibLoaded = false; nSigmaCalibObjects = nullptr; @@ -427,6 +478,17 @@ struct strangenesstofpid { ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); + LOGF(info, "intializing TOFResponse"); + std::string taskName = initContext.services().get().name; + if (doprocessDerivedData || metadataConfigurations.overrideMetadata) { + LOG(info) << "Overriding metadata info. Run = " << metadataConfigurations.Run.value << " ; DataType = " << metadataConfigurations.DataType.value << " ; AnchorPassName (only if DataType = MC) = " << metadataConfigurations.AnchorPassName.value << " ; RecoPassName (only if DataType = Data) = " << metadataConfigurations.RecoPassName.value; + mTOFResponse->metadataInfo.set("Run", metadataConfigurations.Run.value); + mTOFResponse->metadataInfo.set("DataType", metadataConfigurations.DataType.value); + mTOFResponse->metadataInfo.set("AnchorPassName", metadataConfigurations.AnchorPassName.value); + mTOFResponse->metadataInfo.set("RecoPassName", metadataConfigurations.RecoPassName.value); + } + mTOFResponse->initSetup(ccdb, initContext, taskName); + // per event histos.add("hCandidateCounter", "hCandidateCounter", kTH1F, {{500, -0.5f, 499.5f}}); @@ -447,6 +509,7 @@ struct strangenesstofpid { histos.add("h2dTOFSignalCascadeBachelor", "h2dTOFSignalCascadeBachelor", kTH2F, {axes.axisTimeLong, axes.axisBCshift}); histos.add("hCollisionTimes", "hCollisionTimes", kTH1F, {{2000, -1000.0f, 1000.0f}}); + histos.add("hCollisionTimesError", "hCollisionTimesError", kTH1F, {{2000, -1000.0f, 1000.0f}}); // measured vs expected total time QA if (doQA) { @@ -532,6 +595,8 @@ struct strangenesstofpid { histos.add("h2dNSigmaNegativeLambdaPr", "h2dNSigmaNegativeLambdaPr", {HistType::kTH2F, {axes.axisP, axes.axisNSigma}}); histos.add("h2dNSigmaPositiveK0ShortPi", "h2dNSigmaPositiveK0ShortPi", {HistType::kTH2F, {axes.axisP, axes.axisNSigma}}); histos.add("h2dNSigmaNegativeK0ShortPi", "h2dNSigmaNegativeK0ShortPi", {HistType::kTH2F, {axes.axisP, axes.axisNSigma}}); + histos.add("h2dNSigmaPositivePhotonEl", "h2dNSigmaPositivePhotonEl", {HistType::kTH2F, {axes.axisP, axes.axisNSigma}}); + histos.add("h2dNSigmaNegativePhotonEl", "h2dNSigmaNegativePhotonEl", {HistType::kTH2F, {axes.axisP, axes.axisNSigma}}); } if (calculateCascades.value > 0) { @@ -559,7 +624,7 @@ struct strangenesstofpid { if (propagationConfiguration.d_bz_input > -990) { d_bz = propagationConfiguration.d_bz_input; o2::parameters::GRPMagField grpmag; - if (fabs(d_bz) > 1e-5) { + if (std::fabs(d_bz) > 1e-5) { grpmag.setL3Current(30000.f / (d_bz / 5.0f)); } o2::base::Propagator::initFieldFromGRP(&grpmag); @@ -587,80 +652,6 @@ struct strangenesstofpid { LOG(info) << "Retrieved GRP for run " << runNumber << " with magnetic field of " << d_bz << " kZG"; } - // if TOF Nsigma desired - if (doNSigmas) { - nSigmaCalibObjects = ccdb->getForRun(ccdbConfigurations.nSigmaPath, runNumber); - if (nSigmaCalibObjects) { - LOGF(info, "loaded TList with this many objects: %i", nSigmaCalibObjects->GetEntries()); - nSigmaCalibLoaded = true; // made it thus far, mark loaded - - if (calculateV0s.value) { - hMeanPosLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosLaPi")); - hMeanPosLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosLaPr")); - hMeanNegLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegLaPi")); - hMeanNegLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegLaPr")); - hMeanPosK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosK0Pi")); - hMeanNegK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegK0Pi")); - - hSigmaPosLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosLaPi")); - hSigmaPosLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosLaPr")); - hSigmaNegLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegLaPi")); - hSigmaNegLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegLaPr")); - hSigmaPosK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosK0Pi")); - hSigmaNegK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegK0Pi")); - - if (!hMeanPosLaPi) - LOG(info) << "Problems finding mean histogram hMeanPosLaPi!"; - if (!hMeanPosLaPr) - LOG(info) << "Problems finding mean histogram hMeanPosLaPr!"; - if (!hMeanNegLaPi) - LOG(info) << "Problems finding mean histogram hMeanNegLaPi!"; - if (!hMeanNegLaPr) - LOG(info) << "Problems finding mean histogram hMeanNegLaPr!"; - if (!hMeanPosK0Pi) - LOG(info) << "Problems finding mean histogram hMeanPosK0Pi!"; - if (!hMeanNegK0Pi) - LOG(info) << "Problems finding mean histogram hMeanNegK0Pi!"; - if (!hSigmaPosK0Pi || !hSigmaNegK0Pi || !hSigmaPosLaPi || !hSigmaPosLaPr || !hSigmaNegLaPi || !hSigmaNegLaPr) { - LOG(info) << "Problems finding sigma histograms!"; - } - } - - if (calculateCascades.value) { - hMeanPosXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosXiPi")); - hMeanPosXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosXiPr")); - hMeanNegXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegXiPi")); - hMeanNegXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegXiPr")); - hMeanBachXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanBachXiPi")); - hMeanPosOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosOmPi")); - hMeanPosOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosOmPr")); - hMeanNegOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegOmPi")); - hMeanNegOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegOmPr")); - hMeanBachOmKa = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanBachOmKa")); - - hSigmaPosXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosXiPi")); - hSigmaPosXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosXiPr")); - hSigmaNegXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegXiPi")); - hSigmaNegXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegXiPr")); - hSigmaBachXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaBachXiPi")); - hSigmaPosOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosOmPi")); - hSigmaPosOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosOmPr")); - hSigmaNegOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegOmPi")); - hSigmaNegOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegOmPr")); - hSigmaBachOmKa = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaBachOmKa")); - - if (!hMeanPosXiPi || !hMeanPosXiPr || !hMeanNegXiPi || !hMeanNegXiPr || !hMeanBachXiPi) - LOG(info) << "Problems finding xi mean histograms!"; - if (!hMeanPosOmPi || !hMeanPosOmPr || !hMeanNegOmPi || !hMeanNegOmPr || !hMeanBachOmKa) - LOG(info) << "Problems finding omega sigma histograms!"; - if (!hSigmaPosXiPi || !hSigmaPosXiPr || !hSigmaNegXiPi || !hSigmaNegXiPr || !hSigmaBachXiPi) - LOG(info) << "Problems finding xi sigma histograms!"; - if (!hSigmaPosOmPi || !hSigmaPosOmPr || !hSigmaNegOmPi || !hSigmaNegOmPr || !hSigmaBachOmKa) - LOG(info) << "Problems finding omega sigma histograms!"; - } - } - } - // if (calculationMethod.value > 0 && !lut) { // // setMatLUT only after magfield has been initalized // // (setMatLUT has implicit and problematic init field call if not) @@ -671,6 +662,84 @@ struct strangenesstofpid { // LOG(info) << "Material look-up table loaded!"; // } mRunNumber = runNumber; + + // if TOF Nsigma desired + if (useNsigmaCalibStrTOF) { + if (doNSigmas) { + nSigmaCalibObjects = ccdb->getForRun(ccdbConfigurations.nSigmaPath, runNumber); + if (nSigmaCalibObjects) { + LOGF(info, "loaded TList with this many objects: %i", nSigmaCalibObjects->GetEntries()); + nSigmaCalibLoaded = true; // made it thus far, mark loaded + + if (calculateV0s.value) { + hMeanPosLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosLaPi")); + hMeanPosLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosLaPr")); + hMeanNegLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegLaPi")); + hMeanNegLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegLaPr")); + hMeanPosK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosK0Pi")); + hMeanNegK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegK0Pi")); + + hSigmaPosLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosLaPi")); + hSigmaPosLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosLaPr")); + hSigmaNegLaPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegLaPi")); + hSigmaNegLaPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegLaPr")); + hSigmaPosK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosK0Pi")); + hSigmaNegK0Pi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegK0Pi")); + + if (!hMeanPosLaPi) + LOG(info) << "Problems finding mean histogram hMeanPosLaPi!"; + if (!hMeanPosLaPr) + LOG(info) << "Problems finding mean histogram hMeanPosLaPr!"; + if (!hMeanNegLaPi) + LOG(info) << "Problems finding mean histogram hMeanNegLaPi!"; + if (!hMeanNegLaPr) + LOG(info) << "Problems finding mean histogram hMeanNegLaPr!"; + if (!hMeanPosK0Pi) + LOG(info) << "Problems finding mean histogram hMeanPosK0Pi!"; + if (!hMeanNegK0Pi) + LOG(info) << "Problems finding mean histogram hMeanNegK0Pi!"; + if (!hSigmaPosK0Pi || !hSigmaNegK0Pi || !hSigmaPosLaPi || !hSigmaPosLaPr || !hSigmaNegLaPi || !hSigmaNegLaPr) { + LOG(info) << "Problems finding sigma histograms!"; + } + } + + if (calculateCascades.value) { + hMeanPosXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosXiPi")); + hMeanPosXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosXiPr")); + hMeanNegXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegXiPi")); + hMeanNegXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegXiPr")); + hMeanBachXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanBachXiPi")); + hMeanPosOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosOmPi")); + hMeanPosOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanPosOmPr")); + hMeanNegOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegOmPi")); + hMeanNegOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanNegOmPr")); + hMeanBachOmKa = reinterpret_cast(nSigmaCalibObjects->FindObject("hMeanBachOmKa")); + + hSigmaPosXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosXiPi")); + hSigmaPosXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosXiPr")); + hSigmaNegXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegXiPi")); + hSigmaNegXiPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegXiPr")); + hSigmaBachXiPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaBachXiPi")); + hSigmaPosOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosOmPi")); + hSigmaPosOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaPosOmPr")); + hSigmaNegOmPi = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegOmPi")); + hSigmaNegOmPr = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaNegOmPr")); + hSigmaBachOmKa = reinterpret_cast(nSigmaCalibObjects->FindObject("hSigmaBachOmKa")); + + if (!hMeanPosXiPi || !hMeanPosXiPr || !hMeanNegXiPi || !hMeanNegXiPr || !hMeanBachXiPi) + LOG(info) << "Problems finding xi mean histograms!"; + if (!hMeanPosOmPi || !hMeanPosOmPr || !hMeanNegOmPi || !hMeanNegOmPr || !hMeanBachOmKa) + LOG(info) << "Problems finding omega sigma histograms!"; + if (!hSigmaPosXiPi || !hSigmaPosXiPr || !hSigmaNegXiPi || !hSigmaNegXiPr || !hSigmaBachXiPi) + LOG(info) << "Problems finding xi sigma histograms!"; + if (!hSigmaPosOmPi || !hSigmaPosOmPr || !hSigmaNegOmPi || !hSigmaNegOmPr || !hSigmaBachOmKa) + LOG(info) << "Problems finding omega sigma histograms!"; + } + } else { + LOG(warn) << "Problems finding calibration objects for run " << runNumber << " at " << ccdbConfigurations.nSigmaPath << ". Proceed without it"; + } + } + } } float velocity(float lMomentum, float lMass) @@ -685,15 +754,20 @@ struct strangenesstofpid { struct v0TofInfo { // holds processed information regarding TOF for V0s float timeK0Short = o2::aod::v0data::kNoTOFValue; float timeLambda = o2::aod::v0data::kNoTOFValue; + float timePhoton = o2::aod::v0data::kNoTOFValue; float timePositivePr = o2::aod::v0data::kNoTOFValue; float timePositivePi = o2::aod::v0data::kNoTOFValue; + float timePositiveEl = o2::aod::v0data::kNoTOFValue; float timeNegativePr = o2::aod::v0data::kNoTOFValue; float timeNegativePi = o2::aod::v0data::kNoTOFValue; + float timeNegativeEl = o2::aod::v0data::kNoTOFValue; float timeAsPrimaryPositivePr = o2::aod::v0data::kNoTOFValue; float timeAsPrimaryPositivePi = o2::aod::v0data::kNoTOFValue; + float timeAsPrimaryPositiveEl = o2::aod::v0data::kNoTOFValue; float timeAsPrimaryNegativePr = o2::aod::v0data::kNoTOFValue; float timeAsPrimaryNegativePi = o2::aod::v0data::kNoTOFValue; + float timeAsPrimaryNegativeEl = o2::aod::v0data::kNoTOFValue; float deltaTimePositiveLambdaPi = o2::aod::v0data::kNoTOFValue; float deltaTimeNegativeLambdaPi = o2::aod::v0data::kNoTOFValue; @@ -701,6 +775,8 @@ struct strangenesstofpid { float deltaTimeNegativeLambdaPr = o2::aod::v0data::kNoTOFValue; float deltaTimePositiveK0ShortPi = o2::aod::v0data::kNoTOFValue; float deltaTimeNegativeK0ShortPi = o2::aod::v0data::kNoTOFValue; + float deltaTimePositivePhotonEl = o2::aod::v0data::kNoTOFValue; + float deltaTimeNegativePhotonEl = o2::aod::v0data::kNoTOFValue; float nSigmaPositiveLambdaPi = o2::aod::v0data::kNoTOFValue; float nSigmaPositiveLambdaPr = o2::aod::v0data::kNoTOFValue; @@ -708,15 +784,38 @@ struct strangenesstofpid { float nSigmaNegativeLambdaPr = o2::aod::v0data::kNoTOFValue; float nSigmaPositiveK0ShortPi = o2::aod::v0data::kNoTOFValue; float nSigmaNegativeK0ShortPi = o2::aod::v0data::kNoTOFValue; + float nSigmaPositivePhotonEl = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativePhotonEl = o2::aod::v0data::kNoTOFValue; + + // n sigma with wrong hypothesis + float nSigmaPositiveLambdaEl = o2::aod::v0data::kNoTOFValue; + float nSigmaPositiveLambdaKa = o2::aod::v0data::kNoTOFValue; + float nSigmaPositiveK0ShortEl = o2::aod::v0data::kNoTOFValue; + float nSigmaPositiveK0ShortKa = o2::aod::v0data::kNoTOFValue; + float nSigmaPositiveK0ShortPr = o2::aod::v0data::kNoTOFValue; + float nSigmaPositivePhotonPi = o2::aod::v0data::kNoTOFValue; + float nSigmaPositivePhotonKa = o2::aod::v0data::kNoTOFValue; + float nSigmaPositivePhotonPr = o2::aod::v0data::kNoTOFValue; + + float nSigmaNegativeLambdaEl = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativeLambdaKa = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativeK0ShortEl = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativeK0ShortKa = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativeK0ShortPr = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativePhotonPi = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativePhotonKa = o2::aod::v0data::kNoTOFValue; + float nSigmaNegativePhotonPr = o2::aod::v0data::kNoTOFValue; // extra auxiliary variables float deltaDecayTimeLambda = o2::aod::v0data::kNoTOFValue; float deltaDecayTimeAntiLambda = o2::aod::v0data::kNoTOFValue; float deltaDecayTimeK0Short = o2::aod::v0data::kNoTOFValue; + float deltaDecayTimePhoton = o2::aod::v0data::kNoTOFValue; float betaLambda = o2::aod::v0data::kNoTOFValue; float betaAntiLambda = o2::aod::v0data::kNoTOFValue; float betaK0Short = o2::aod::v0data::kNoTOFValue; + float betaPhoton = o2::aod::v0data::kNoTOFValue; }; // structs to hold information @@ -748,6 +847,33 @@ struct strangenesstofpid { float nSigmaOmLaPr = o2::aod::cascdata::kNoTOFValue; float nSigmaOmLaPi = o2::aod::cascdata::kNoTOFValue; float nSigmaOmKa = o2::aod::cascdata::kNoTOFValue; + + // n sigma with wrong hypothesis + float nSigmaXiPositiveLaEl = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiNegativeLaEl = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiBachelorEl = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiPositiveLaPi = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiNegativeLaPi = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiBachelorPi = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiPositiveLaKa = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiNegativeLaKa = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiBachelorKa = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiPositiveLaPr = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiNegativeLaPr = o2::aod::cascdata::kNoTOFValue; + float nSigmaXiBachelorPr = o2::aod::cascdata::kNoTOFValue; + + float nSigmaOmPositiveLaEl = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmNegativeLaEl = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmBachelorEl = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmPositiveLaPi = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmNegativeLaPi = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmBachelorPi = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmPositiveLaKa = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmNegativeLaKa = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmBachelorKa = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmPositiveLaPr = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmNegativeLaPr = o2::aod::cascdata::kNoTOFValue; + float nSigmaOmBachelorPr = o2::aod::cascdata::kNoTOFValue; }; struct trackTofInfo { // holds input track info @@ -758,9 +884,11 @@ struct strangenesstofpid { float tofExpMom = 0.0f; float tofSignal = 0.0f; float tofEvTime = 0.0f; + float tofEvTimeErr = 0.0f; float length = 0.0f; // save TPC PID here for completeness too + float tpcNSigmaEl = 0.0f; float tpcNSigmaPi = 0.0f; float tpcNSigmaKa = 0.0f; float tpcNSigmaPr = 0.0f; @@ -774,8 +902,8 @@ struct strangenesstofpid { template v0TofInfo calculateTofInfoV0(TCollisions const& collisions, int const& collisionId, TV0 const& v0, TTOFInfo const& pTof, TTOFInfo const& nTof) { - v0TofInfo v0tof; // return this struct - auto collision = collisions.rawIteratorAt(collisionId); + v0TofInfo v0tof; // return this struct + auto collision = collisions.rawIteratorAt(collisionId); // collisionId must be collisionId of V0. //_____________________________________________________________________________________________ // daughter tracks: initialize from V0 position and momenta @@ -787,8 +915,10 @@ struct strangenesstofpid { float lengthV0 = std::hypot(v0.x() - collision.posX(), v0.y() - collision.posY(), v0.z() - collision.posZ()); float velocityK0Short = velocity(v0.p(), o2::constants::physics::MassKaonNeutral); float velocityLambda = velocity(v0.p(), o2::constants::physics::MassLambda); - v0tof.timeK0Short = lengthV0 / velocityK0Short; // in picoseconds - v0tof.timeLambda = lengthV0 / velocityLambda; // in picoseconds + float velocityPhoton = TMath::C() * 1e-12 * 1e+2; // cm/ps + v0tof.timeK0Short = lengthV0 / velocityK0Short; // in picoseconds + v0tof.timeLambda = lengthV0 / velocityLambda; // in picoseconds + v0tof.timePhoton = lengthV0 / velocityPhoton; // in picoseconds //_____________________________________________________________________________________________ // define simple checks @@ -802,21 +932,26 @@ struct strangenesstofpid { bool k0ShortCandidate = std::abs(v0.mK0Short() - o2::constants::physics::MassKaonNeutral) < v0Group.qaMassWindow && std::abs(pTof.tpcNSigmaPi) < v0Group.qaTPCNSigma && std::abs(nTof.tpcNSigmaPi) < v0Group.qaTPCNSigma; + bool photonCandidate = std::abs(v0.mGamma() - o2::constants::physics::MassPhoton) < v0Group.qaMassWindow && + std::abs(pTof.tpcNSigmaEl) < v0Group.qaTPCNSigma && + std::abs(nTof.tpcNSigmaEl) < v0Group.qaTPCNSigma; bool pValidTOF = rejectUndefinedTof.value ? static_cast(std::fabs(pTof.tofSignal) > o2::aod::v0data::kEpsilon) : true; bool nValidTOF = rejectUndefinedTof.value ? static_cast(std::fabs(nTof.tofSignal) > o2::aod::v0data::kEpsilon) : true; //_____________________________________________________________________________________________ // Actual calculation - float velocityPositivePr, velocityPositivePi, lengthPositive; - velocityPositivePr = velocityPositivePi = lengthPositive = o2::aod::v0data::kNoTOFValue; + float velocityPositiveEl, velocityPositivePr, velocityPositivePi, lengthPositive; + velocityPositiveEl = velocityPositivePr = velocityPositivePi = lengthPositive = o2::aod::v0data::kNoTOFValue; if (pTof.hasTOF && pTof.tofEvTime > -1e+5 && pValidTOF) { // method 0: legacy standalone without use of primary particle TOF if (calculationMethod.value == 0) { + velocityPositiveEl = velocity(posTrack.getP(), o2::constants::physics::MassElectron); velocityPositivePr = velocity(posTrack.getP(), o2::constants::physics::MassProton); velocityPositivePi = velocity(posTrack.getP(), o2::constants::physics::MassPionCharged); lengthPositive = findInterceptLength(posTrack, d_bz); + v0tof.timePositiveEl = lengthPositive / velocityPositiveEl; v0tof.timePositivePr = lengthPositive / velocityPositivePr; v0tof.timePositivePi = lengthPositive / velocityPositivePi; } @@ -835,25 +970,56 @@ struct strangenesstofpid { } if (successPropag) { lengthPositive = pTof.length - ltIntegral.getL(); + v0tof.timePositiveEl = o2::framework::pid::tof::MassToExpTime(pTof.tofExpMom, lengthPositive, o2::constants::physics::MassElectron * o2::constants::physics::MassElectron); v0tof.timePositivePr = o2::framework::pid::tof::MassToExpTime(pTof.tofExpMom, lengthPositive, o2::constants::physics::MassProton * o2::constants::physics::MassProton); v0tof.timePositivePi = o2::framework::pid::tof::MassToExpTime(pTof.tofExpMom, lengthPositive, o2::constants::physics::MassPionCharged * o2::constants::physics::MassPionCharged); // as primary + v0tof.timeAsPrimaryPositiveEl = o2::framework::pid::tof::MassToExpTime(pTof.tofExpMom, pTof.length, o2::constants::physics::MassElectron * o2::constants::physics::MassElectron); v0tof.timeAsPrimaryPositivePr = o2::framework::pid::tof::MassToExpTime(pTof.tofExpMom, pTof.length, o2::constants::physics::MassProton * o2::constants::physics::MassProton); v0tof.timeAsPrimaryPositivePi = o2::framework::pid::tof::MassToExpTime(pTof.tofExpMom, pTof.length, o2::constants::physics::MassPionCharged * o2::constants::physics::MassPionCharged); } } } if (lengthPositive > 0.0f) { + v0tof.deltaTimePositivePhotonEl = (pTof.tofSignal - pTof.tofEvTime) - (v0tof.timePhoton + v0tof.timePositiveEl); v0tof.deltaTimePositiveLambdaPr = (pTof.tofSignal - pTof.tofEvTime) - (v0tof.timeLambda + v0tof.timePositivePr); v0tof.deltaTimePositiveLambdaPi = (pTof.tofSignal - pTof.tofEvTime) - (v0tof.timeLambda + v0tof.timePositivePi); v0tof.deltaTimePositiveK0ShortPi = (pTof.tofSignal - pTof.tofEvTime) - (v0tof.timeK0Short + v0tof.timePositivePi); - // de facto nsigma - if (nSigmaCalibLoaded) { - v0tof.nSigmaPositiveLambdaPi = (v0tof.deltaTimePositiveLambdaPi - hMeanPosLaPi->Interpolate(v0.p())) / hSigmaPosLaPi->Interpolate(v0.p()); - v0tof.nSigmaPositiveLambdaPr = (v0tof.deltaTimePositiveLambdaPr - hMeanPosLaPr->Interpolate(v0.p())) / hSigmaPosLaPr->Interpolate(v0.p()); - v0tof.nSigmaPositiveK0ShortPi = (v0tof.deltaTimePositiveK0ShortPi - hMeanPosK0Pi->Interpolate(v0.p())) / hSigmaPosK0Pi->Interpolate(v0.p()); + if (useNsigmaCalibStrTOF) { + // de facto nsigma + if (nSigmaCalibLoaded) { + v0tof.nSigmaPositivePhotonEl = -999.; + v0tof.nSigmaPositiveLambdaPi = (v0tof.deltaTimePositiveLambdaPi - hMeanPosLaPi->Interpolate(v0.p())) / hSigmaPosLaPi->Interpolate(v0.p()); + v0tof.nSigmaPositiveLambdaPr = (v0tof.deltaTimePositiveLambdaPr - hMeanPosLaPr->Interpolate(v0.p())) / hSigmaPosLaPr->Interpolate(v0.p()); + v0tof.nSigmaPositiveK0ShortPi = (v0tof.deltaTimePositiveK0ShortPi - hMeanPosK0Pi->Interpolate(v0.p())) / hSigmaPosK0Pi->Interpolate(v0.p()); + // with wrong hypothesis + v0tof.nSigmaPositivePhotonPi = -999.; + v0tof.nSigmaPositivePhotonKa = -999.; + v0tof.nSigmaPositivePhotonPr = -999.; + v0tof.nSigmaPositiveLambdaEl = -999.; + v0tof.nSigmaPositiveLambdaKa = -999.; + v0tof.nSigmaPositiveK0ShortEl = -999.; + v0tof.nSigmaPositiveK0ShortKa = -999.; + v0tof.nSigmaPositiveK0ShortPr = -999.; + } + } else { + // use nSigma function from O2Physics/Common/Core/PID/PIDTOFParamService.h + v0tof.nSigmaPositivePhotonEl = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timePhoton, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveLambdaPi = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeLambda, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveLambdaPr = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeLambda, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveK0ShortPi = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeK0Short, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + + // with wrong hypothesis + v0tof.nSigmaPositivePhotonPi = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timePhoton, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositivePhotonKa = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timePhoton, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositivePhotonPr = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timePhoton, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveLambdaEl = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeLambda, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveLambdaKa = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeLambda, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveK0ShortEl = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeK0Short, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveK0ShortKa = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeK0Short, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + v0tof.nSigmaPositiveK0ShortPr = mTOFResponse->nSigma(pTof.tofSignal - v0tof.timeK0Short, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); } // do QA histograms (calibration / QC) @@ -883,18 +1049,25 @@ struct strangenesstofpid { histos.fill(HIST("h2dNSigmaPositiveK0ShortPi"), v0.pt(), v0tof.nSigmaPositiveK0ShortPi); } } + if (photonCandidate) { + if (doQANSigma && std::fabs(v0tof.nSigmaPositivePhotonEl - o2::aod::v0data::kNoTOFValue) > o2::aod::v0data::kEpsilon) { + histos.fill(HIST("h2dNSigmaPositivePhotonEl"), v0.pt(), v0tof.nSigmaPositivePhotonEl); + } + } } } } } - float velocityNegativePr, velocityNegativePi, lengthNegative; - velocityNegativePr = velocityNegativePi = lengthNegative = o2::aod::v0data::kNoTOFValue; + float velocityNegativeEl, velocityNegativePr, velocityNegativePi, lengthNegative; + velocityNegativeEl = velocityNegativePr = velocityNegativePi = lengthNegative = o2::aod::v0data::kNoTOFValue; if (nTof.hasTOF && nTof.tofEvTime > -1e+5 && nValidTOF) { // method 0: legacy standalone without use of primary particle TOF if (calculationMethod.value == 0) { + velocityNegativeEl = velocity(negTrack.getP(), o2::constants::physics::MassElectron); velocityNegativePr = velocity(negTrack.getP(), o2::constants::physics::MassProton); velocityNegativePi = velocity(negTrack.getP(), o2::constants::physics::MassPionCharged); lengthNegative = findInterceptLength(negTrack, d_bz); + v0tof.timeNegativeEl = lengthNegative / velocityNegativeEl; v0tof.timeNegativePr = lengthNegative / velocityNegativePr; v0tof.timeNegativePi = lengthNegative / velocityNegativePi; } @@ -913,25 +1086,56 @@ struct strangenesstofpid { } if (successPropag) { lengthNegative = nTof.length - ltIntegral.getL(); + v0tof.timeNegativeEl = o2::framework::pid::tof::MassToExpTime(nTof.tofExpMom, lengthNegative, o2::constants::physics::MassElectron * o2::constants::physics::MassElectron); v0tof.timeNegativePr = o2::framework::pid::tof::MassToExpTime(nTof.tofExpMom, lengthNegative, o2::constants::physics::MassProton * o2::constants::physics::MassProton); v0tof.timeNegativePi = o2::framework::pid::tof::MassToExpTime(nTof.tofExpMom, lengthNegative, o2::constants::physics::MassPionCharged * o2::constants::physics::MassPionCharged); // as primary + v0tof.timeAsPrimaryNegativeEl = o2::framework::pid::tof::MassToExpTime(nTof.tofExpMom, nTof.length, o2::constants::physics::MassElectron * o2::constants::physics::MassElectron); v0tof.timeAsPrimaryNegativePr = o2::framework::pid::tof::MassToExpTime(nTof.tofExpMom, nTof.length, o2::constants::physics::MassProton * o2::constants::physics::MassProton); v0tof.timeAsPrimaryNegativePi = o2::framework::pid::tof::MassToExpTime(nTof.tofExpMom, nTof.length, o2::constants::physics::MassPionCharged * o2::constants::physics::MassPionCharged); } } } if (lengthNegative > 0.0f) { + v0tof.deltaTimeNegativePhotonEl = (nTof.tofSignal - nTof.tofEvTime) - (v0tof.timePhoton + v0tof.timeNegativeEl); v0tof.deltaTimeNegativeLambdaPr = (nTof.tofSignal - nTof.tofEvTime) - (v0tof.timeLambda + v0tof.timeNegativePr); v0tof.deltaTimeNegativeLambdaPi = (nTof.tofSignal - nTof.tofEvTime) - (v0tof.timeLambda + v0tof.timeNegativePi); v0tof.deltaTimeNegativeK0ShortPi = (nTof.tofSignal - nTof.tofEvTime) - (v0tof.timeK0Short + v0tof.timeNegativePi); - // de facto nsigma - if (nSigmaCalibLoaded) { - v0tof.nSigmaNegativeLambdaPi = (v0tof.deltaTimeNegativeLambdaPi - hMeanNegLaPi->Interpolate(v0.p())) / hSigmaNegLaPi->Interpolate(v0.p()); - v0tof.nSigmaNegativeLambdaPr = (v0tof.deltaTimeNegativeLambdaPr - hMeanNegLaPr->Interpolate(v0.p())) / hSigmaNegLaPr->Interpolate(v0.p()); - v0tof.nSigmaNegativeK0ShortPi = (v0tof.deltaTimeNegativeK0ShortPi - hMeanNegK0Pi->Interpolate(v0.p())) / hSigmaNegK0Pi->Interpolate(v0.p()); + if (useNsigmaCalibStrTOF) { + // de facto nsigma + if (nSigmaCalibLoaded) { + v0tof.nSigmaNegativePhotonEl = -999.; + v0tof.nSigmaNegativeLambdaPi = (v0tof.deltaTimeNegativeLambdaPi - hMeanNegLaPi->Interpolate(v0.p())) / hSigmaNegLaPi->Interpolate(v0.p()); + v0tof.nSigmaNegativeLambdaPr = (v0tof.deltaTimeNegativeLambdaPr - hMeanNegLaPr->Interpolate(v0.p())) / hSigmaNegLaPr->Interpolate(v0.p()); + v0tof.nSigmaNegativeK0ShortPi = (v0tof.deltaTimeNegativeK0ShortPi - hMeanNegK0Pi->Interpolate(v0.p())) / hSigmaNegK0Pi->Interpolate(v0.p()); + // with wrong hypothesis + v0tof.nSigmaNegativePhotonPi = -999.; + v0tof.nSigmaNegativePhotonKa = -999.; + v0tof.nSigmaNegativePhotonPr = -999.; + v0tof.nSigmaNegativeLambdaEl = -999.; + v0tof.nSigmaNegativeLambdaKa = -999.; + v0tof.nSigmaNegativeK0ShortEl = -999.; + v0tof.nSigmaNegativeK0ShortKa = -999.; + v0tof.nSigmaNegativeK0ShortPr = -999.; + } + } else { + // use nSigma function from O2Physics/Common/Core/PID/PIDTOFParamService.h + v0tof.nSigmaNegativePhotonEl = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timePhoton, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeLambdaPi = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeLambda, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeLambdaPr = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeLambda, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeK0ShortPi = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeK0Short, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + + // with wrong hypothesis + v0tof.nSigmaNegativePhotonPi = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timePhoton, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativePhotonKa = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timePhoton, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativePhotonPr = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timePhoton, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeLambdaEl = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeLambda, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeLambdaKa = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeLambda, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeK0ShortEl = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeK0Short, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeK0ShortKa = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeK0Short, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + v0tof.nSigmaNegativeK0ShortPr = mTOFResponse->nSigma(nTof.tofSignal - v0tof.timeK0Short, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); } // do QA histograms (calibration / QC) @@ -961,6 +1165,11 @@ struct strangenesstofpid { histos.fill(HIST("h2dNSigmaNegativeK0ShortPi"), v0.pt(), v0tof.nSigmaNegativeK0ShortPi); } } + if (photonCandidate) { + if (doQANSigma && std::fabs(v0tof.nSigmaNegativePhotonEl - o2::aod::v0data::kNoTOFValue) > o2::aod::v0data::kEpsilon) { + histos.fill(HIST("h2dNSigmaNegativePhotonEl"), v0.pt(), v0tof.nSigmaNegativePhotonEl); + } + } } } } @@ -983,17 +1192,20 @@ struct strangenesstofpid { // } // calculation of delta-decay-time (no reliance on event time) - if (nTof.hasTOF && pTof.hasTOF > 0) { // does not depend on event time + if (nTof.hasTOF && pTof.hasTOF) { // does not depend on event time + v0tof.deltaDecayTimePhoton = (pTof.tofSignal - v0tof.timePositiveEl) - (nTof.tofSignal - v0tof.timeNegativeEl); v0tof.deltaDecayTimeLambda = (pTof.tofSignal - v0tof.timePositivePr) - (nTof.tofSignal - v0tof.timeNegativePi); v0tof.deltaDecayTimeAntiLambda = (pTof.tofSignal - v0tof.timePositivePi) - (nTof.tofSignal - v0tof.timeNegativePr); v0tof.deltaDecayTimeK0Short = (pTof.tofSignal - v0tof.timePositivePi) - (nTof.tofSignal - v0tof.timeNegativePi); float evTimeMean = 0.5f * (pTof.tofEvTime + nTof.tofEvTime); + float decayTimePhoton = 0.5f * ((pTof.tofSignal - v0tof.timePositiveEl) + (nTof.tofSignal - v0tof.timeNegativeEl)) - evTimeMean; float decayTimeLambda = 0.5f * ((pTof.tofSignal - v0tof.timePositivePr) + (nTof.tofSignal - v0tof.timeNegativePi)) - evTimeMean; float decayTimeAntiLambda = 0.5f * ((pTof.tofSignal - v0tof.timePositivePi) + (nTof.tofSignal - v0tof.timeNegativePr)) - evTimeMean; float decayTimeK0Short = 0.5f * ((pTof.tofSignal - v0tof.timePositivePi) + (nTof.tofSignal - v0tof.timeNegativePi)) - evTimeMean; constexpr float lightSpeed = 0.0299792458; // in cm/ps + v0tof.betaPhoton = (lengthV0 / decayTimePhoton) / lightSpeed; v0tof.betaLambda = (lengthV0 / decayTimeLambda) / lightSpeed; v0tof.betaAntiLambda = (lengthV0 / decayTimeAntiLambda) / lightSpeed; v0tof.betaK0Short = (lengthV0 / decayTimeK0Short) / lightSpeed; @@ -1006,8 +1218,8 @@ struct strangenesstofpid { template cascTofInfo calculateTofInfoCascade(TCollisions const& collisions, int const& collisionId, TCascade const& cascade, TTOFInfo const& pTof, TTOFInfo const& nTof, TTOFInfo const& bTof) { - cascTofInfo casctof; // return this struct - auto collision = collisions.rawIteratorAt(collisionId); + cascTofInfo casctof; // return this struct + auto collision = collisions.rawIteratorAt(collisionId); // collisionId must be collisionId of cascade. //_____________________________________________________________________________________________ // daughter tracks: initialize from V0 position and momenta @@ -1041,7 +1253,7 @@ struct strangenesstofpid { // d3d = std::hypot(cascade.x() - cascCloseToPVPosition[0], cascade.y() - cascCloseToPVPosition[1], cascade.z() - cascCloseToPVPosition[2]); // cross-check variable float sinThetaOverTwo = d / (2.0f * trcCircleCascade.rC); lengthCascade = 2.0f * trcCircleCascade.rC * TMath::ASin(sinThetaOverTwo); - lengthCascade *= sqrt(1.0f + cascTrack.getTgl() * cascTrack.getTgl()); + lengthCascade *= std::sqrt(1.0f + cascTrack.getTgl() * cascTrack.getTgl()); } if (!successPropag) { @@ -1123,14 +1335,40 @@ struct strangenesstofpid { casctof.posDeltaTimeAsOmPi = (pTof.tofSignal - pTof.tofEvTime) - (omFlight + lambdaFlight + casctof.posFlightPi); casctof.posDeltaTimeAsOmPr = (pTof.tofSignal - pTof.tofEvTime) - (omFlight + lambdaFlight + casctof.posFlightPr); - // de facto nsigma - if (nSigmaCalibLoaded) { - if (cascade.sign() < 0) { - casctof.nSigmaXiLaPr = (casctof.posDeltaTimeAsXiPr - hMeanPosXiPr->Interpolate(cascade.p())) / hSigmaPosXiPr->Interpolate(cascade.p()); - casctof.nSigmaOmLaPr = (casctof.posDeltaTimeAsOmPr - hMeanPosOmPr->Interpolate(cascade.p())) / hSigmaPosOmPr->Interpolate(cascade.p()); + if (cascade.sign() < 0) { + if (useNsigmaCalibStrTOF) { + if (nSigmaCalibLoaded) { + casctof.nSigmaXiLaPr = (casctof.posDeltaTimeAsXiPr - hMeanPosXiPr->Interpolate(cascade.p())) / hSigmaPosXiPr->Interpolate(cascade.p()); + casctof.nSigmaOmLaPr = (casctof.posDeltaTimeAsOmPr - hMeanPosOmPr->Interpolate(cascade.p())) / hSigmaPosOmPr->Interpolate(cascade.p()); + } } else { - casctof.nSigmaXiLaPi = (casctof.posDeltaTimeAsXiPi - hMeanPosXiPi->Interpolate(cascade.p())) / hSigmaPosXiPi->Interpolate(cascade.p()); - casctof.nSigmaOmLaPi = (casctof.posDeltaTimeAsOmPi - hMeanPosOmPi->Interpolate(cascade.p())) / hSigmaPosOmPi->Interpolate(cascade.p()); + casctof.nSigmaXiLaPr = mTOFResponse->nSigma(pTof.tofSignal - xiFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaOmLaPr = mTOFResponse->nSigma(pTof.tofSignal - omFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + } + } else { + if (useNsigmaCalibStrTOF) { + if (nSigmaCalibLoaded) { + casctof.nSigmaXiLaPi = (casctof.posDeltaTimeAsXiPi - hMeanPosXiPi->Interpolate(cascade.p())) / hSigmaPosXiPi->Interpolate(cascade.p()); + casctof.nSigmaOmLaPi = (casctof.posDeltaTimeAsOmPi - hMeanPosOmPi->Interpolate(cascade.p())) / hSigmaPosOmPi->Interpolate(cascade.p()); + } + } else { + casctof.nSigmaXiLaPi = mTOFResponse->nSigma(pTof.tofSignal - xiFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaOmLaPi = mTOFResponse->nSigma(pTof.tofSignal - omFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + } + } + + if (calculateCascadesNSigmaAll.value > 0) { + if (!useNsigmaCalibStrTOF) { + // wrong hypothesis + casctof.nSigmaXiPositiveLaEl = mTOFResponse->nSigma(pTof.tofSignal - xiFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaXiPositiveLaPi = mTOFResponse->nSigma(pTof.tofSignal - xiFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaXiPositiveLaKa = mTOFResponse->nSigma(pTof.tofSignal - xiFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaXiPositiveLaPr = mTOFResponse->nSigma(pTof.tofSignal - xiFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + + casctof.nSigmaOmPositiveLaEl = mTOFResponse->nSigma(pTof.tofSignal - omFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaOmPositiveLaPi = mTOFResponse->nSigma(pTof.tofSignal - omFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaOmPositiveLaKa = mTOFResponse->nSigma(pTof.tofSignal - omFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); + casctof.nSigmaOmPositiveLaPr = mTOFResponse->nSigma(pTof.tofSignal - omFlight - lambdaFlight, pTof.tofExpMom, lengthPositive, posTrack.getP(), posTrack.getEta(), pTof.tofEvTime, pTof.tofEvTimeErr); } } @@ -1215,14 +1453,40 @@ struct strangenesstofpid { casctof.negDeltaTimeAsOmPi = (nTof.tofSignal - nTof.tofEvTime) - (omFlight + lambdaFlight + casctof.negFlightPi); casctof.negDeltaTimeAsOmPr = (nTof.tofSignal - nTof.tofEvTime) - (omFlight + lambdaFlight + casctof.negFlightPr); - // de facto nsigma - if (nSigmaCalibLoaded) { - if (cascade.sign() < 0) { - casctof.nSigmaXiLaPi = (casctof.negDeltaTimeAsXiPi - hMeanNegXiPi->Interpolate(cascade.p())) / hSigmaNegXiPi->Interpolate(cascade.p()); - casctof.nSigmaOmLaPi = (casctof.negDeltaTimeAsOmPi - hMeanNegOmPi->Interpolate(cascade.p())) / hSigmaNegOmPi->Interpolate(cascade.p()); + if (cascade.sign() < 0) { + if (useNsigmaCalibStrTOF) { + if (nSigmaCalibLoaded) { + casctof.nSigmaXiLaPi = (casctof.negDeltaTimeAsXiPi - hMeanNegXiPi->Interpolate(cascade.p())) / hSigmaNegXiPi->Interpolate(cascade.p()); + casctof.nSigmaOmLaPi = (casctof.negDeltaTimeAsOmPi - hMeanNegOmPi->Interpolate(cascade.p())) / hSigmaNegOmPi->Interpolate(cascade.p()); + } } else { - casctof.nSigmaXiLaPr = (casctof.negDeltaTimeAsXiPr - hMeanNegXiPr->Interpolate(cascade.p())) / hSigmaNegXiPr->Interpolate(cascade.p()); - casctof.nSigmaOmLaPr = (casctof.negDeltaTimeAsOmPr - hMeanNegOmPr->Interpolate(cascade.p())) / hSigmaNegOmPr->Interpolate(cascade.p()); + casctof.nSigmaXiLaPi = mTOFResponse->nSigma(nTof.tofSignal - xiFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaOmLaPi = mTOFResponse->nSigma(nTof.tofSignal - omFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + } + } else { + if (useNsigmaCalibStrTOF) { + if (nSigmaCalibLoaded) { + casctof.nSigmaXiLaPr = (casctof.negDeltaTimeAsXiPr - hMeanNegXiPr->Interpolate(cascade.p())) / hSigmaNegXiPr->Interpolate(cascade.p()); + casctof.nSigmaOmLaPr = (casctof.negDeltaTimeAsOmPr - hMeanNegOmPr->Interpolate(cascade.p())) / hSigmaNegOmPr->Interpolate(cascade.p()); + } + } else { + casctof.nSigmaXiLaPr = mTOFResponse->nSigma(nTof.tofSignal - xiFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaOmLaPr = mTOFResponse->nSigma(nTof.tofSignal - omFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + } + } + + if (calculateCascadesNSigmaAll.value > 0) { + if (!useNsigmaCalibStrTOF) { + // wrong hypothesis + casctof.nSigmaXiNegativeLaEl = mTOFResponse->nSigma(nTof.tofSignal - xiFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaXiNegativeLaPi = mTOFResponse->nSigma(nTof.tofSignal - xiFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaXiNegativeLaKa = mTOFResponse->nSigma(nTof.tofSignal - xiFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaXiNegativeLaPr = mTOFResponse->nSigma(nTof.tofSignal - xiFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + + casctof.nSigmaOmNegativeLaEl = mTOFResponse->nSigma(nTof.tofSignal - omFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaOmNegativeLaPi = mTOFResponse->nSigma(nTof.tofSignal - omFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaOmNegativeLaKa = mTOFResponse->nSigma(nTof.tofSignal - omFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); + casctof.nSigmaOmNegativeLaPr = mTOFResponse->nSigma(nTof.tofSignal - omFlight - lambdaFlight, nTof.tofExpMom, lengthNegative, negTrack.getP(), negTrack.getEta(), nTof.tofEvTime, nTof.tofEvTimeErr); } } @@ -1305,15 +1569,29 @@ struct strangenesstofpid { casctof.bachDeltaTimeAsXiPi = (bTof.tofSignal - bTof.tofEvTime) - (xiFlight + casctof.bachFlightPi); casctof.bachDeltaTimeAsOmKa = (bTof.tofSignal - bTof.tofEvTime) - (omFlight + casctof.bachFlightKa); - // de facto nsigma - if (nSigmaCalibLoaded) { - if (cascade.sign() < 0) { - casctof.nSigmaXiPi = (casctof.bachDeltaTimeAsXiPi - hMeanBachXiPi->Interpolate(cascade.p())) / hSigmaBachXiPi->Interpolate(cascade.p()); - casctof.nSigmaOmKa = (casctof.bachDeltaTimeAsOmKa - hMeanBachOmKa->Interpolate(cascade.p())) / hSigmaBachOmKa->Interpolate(cascade.p()); - } else { + if (useNsigmaCalibStrTOF) { + if (nSigmaCalibLoaded) { casctof.nSigmaXiPi = (casctof.bachDeltaTimeAsXiPi - hMeanBachXiPi->Interpolate(cascade.p())) / hSigmaBachXiPi->Interpolate(cascade.p()); casctof.nSigmaOmKa = (casctof.bachDeltaTimeAsOmKa - hMeanBachOmKa->Interpolate(cascade.p())) / hSigmaBachOmKa->Interpolate(cascade.p()); } + } else { + casctof.nSigmaXiPi = mTOFResponse->nSigma(bTof.tofSignal - xiFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + casctof.nSigmaOmKa = mTOFResponse->nSigma(bTof.tofSignal - omFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + } + + if (calculateCascadesNSigmaAll.value > 0) { + if (!useNsigmaCalibStrTOF) { + // wrong hypothesis + casctof.nSigmaXiBachelorEl = mTOFResponse->nSigma(bTof.tofSignal - xiFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + casctof.nSigmaXiBachelorPi = mTOFResponse->nSigma(bTof.tofSignal - xiFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + casctof.nSigmaXiBachelorKa = mTOFResponse->nSigma(bTof.tofSignal - xiFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + casctof.nSigmaXiBachelorPr = mTOFResponse->nSigma(bTof.tofSignal - xiFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + + casctof.nSigmaOmBachelorEl = mTOFResponse->nSigma(bTof.tofSignal - omFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + casctof.nSigmaOmBachelorPi = mTOFResponse->nSigma(bTof.tofSignal - omFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + casctof.nSigmaOmBachelorKa = mTOFResponse->nSigma(bTof.tofSignal - omFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + casctof.nSigmaOmBachelorPr = mTOFResponse->nSigma(bTof.tofSignal - omFlight, bTof.tofExpMom, lengthBachelor, bachTrack.getP(), bachTrack.getEta(), bTof.tofEvTime, bTof.tofEvTimeErr); + } } // do QA histograms (calibration / QC) @@ -1360,7 +1638,7 @@ struct strangenesstofpid { return casctof; } - void processStandardData(aod::BCs const& bcs, aod::Collisions const& collisions, V0OriginalDatas const& V0s, CascOriginalDatas const& cascades, TracksWithAllExtras const& tracks, aod::BCsWithTimestamps const& /*bcs*/) + void processStandardData(/*aod::BCs const& bcs,*/ aod::Collisions const& collisions, V0OriginalDatas const& V0s, CascOriginalDatas const& cascades, TracksWithAllExtras const& tracks, aod::BCsWithTimestamps const& bcs) { // Fire up CCDB with first collision in record. If no collisions, bypass if (useCustomRunNumber || collisions.size() < 1) { @@ -1371,21 +1649,28 @@ struct strangenesstofpid { initCCDB(bc.runNumber()); } + mTOFResponse->processSetup(bcs.iteratorAt(0)); + //________________________________________________________________________ // estimate event times (only necessary for original data) std::vector collisionEventTime(collisions.size(), 0.0); + std::vector collisionEventTimeErr(collisions.size(), 0.0); std::vector collisionNtracks(collisions.size(), 0); for (const auto& track : tracks) { - if (track.hasTOF() && track.has_collision()) { + if (track.hasTOF() && track.collisionId() >= 0) { collisionEventTime[track.collisionId()] += track.tofEvTime(); + // Take the average of the error instead of propagating the error as all event time error from tracks are fully correlated + collisionEventTimeErr[track.collisionId()] += track.tofEvTimeErr(); collisionNtracks[track.collisionId()]++; } } for (const auto& collision : collisions) { if (collisionNtracks[collision.globalIndex()] > 0) { collisionEventTime[collision.globalIndex()] /= static_cast(collisionNtracks[collision.globalIndex()]); + collisionEventTimeErr[collision.globalIndex()] /= static_cast(collisionNtracks[collision.globalIndex()]); } else { - collisionEventTime[collision.globalIndex()] = -1e+6; // undefined + collisionEventTime[collision.globalIndex()] = -1e+6; // undefined + collisionEventTimeErr[collision.globalIndex()] = -1e+6; // undefined } histos.fill(HIST("hCollisionTimes"), collisionEventTime[collision.globalIndex()]); } @@ -1424,8 +1709,11 @@ struct strangenesstofpid { pTof.hasTOF = pTra.hasTOF(); pTof.tofExpMom = pTra.tofExpMom(); pTof.tofEvTime = reassociateTracks ? collisionEventTime[V0.collisionId()] : pTra.tofEvTime(); - pTof.tofSignal = pTra.tofSignal() + (doBCshift ? deltaTimePos : 0.0f); + pTof.tofEvTimeErr = reassociateTracks ? collisionEventTimeErr[V0.collisionId()] : pTra.tofEvTimeErr(); + // pTof.tofSignal = pTra.tofSignal() + (doBCshift ? deltaTimePos : 0.0f); + pTof.tofSignal = doBCshift && pTra.has_collision() ? pTra.tofSignalInAnotherBC(pTra.collision().bc_as().globalBC(), V0.collision().bc_as().globalBC()) : pTra.tofSignal(); pTof.length = pTra.length(); + pTof.tpcNSigmaEl = pTra.tpcNSigmaEl(); pTof.tpcNSigmaPi = pTra.tpcNSigmaPi(); pTof.tpcNSigmaPr = pTra.tpcNSigmaPr(); @@ -1435,8 +1723,11 @@ struct strangenesstofpid { nTof.hasTOF = nTra.hasTOF(); nTof.tofExpMom = nTra.tofExpMom(); nTof.tofEvTime = reassociateTracks ? collisionEventTime[V0.collisionId()] : nTra.tofEvTime(); - nTof.tofSignal = nTra.tofSignal() + (doBCshift ? deltaTimeNeg : 0.0f); + nTof.tofEvTimeErr = reassociateTracks ? collisionEventTimeErr[V0.collisionId()] : nTra.tofEvTimeErr(); + // nTof.tofSignal = nTra.tofSignal() + (doBCshift ? deltaTimeNeg : 0.0f); + nTof.tofSignal = doBCshift && nTra.has_collision() ? nTra.tofSignalInAnotherBC(nTra.collision().bc_as().globalBC(), V0.collision().bc_as().globalBC()) : nTra.tofSignal(); nTof.length = nTra.length(); + nTof.tpcNSigmaEl = nTra.tpcNSigmaEl(); nTof.tpcNSigmaPi = nTra.tpcNSigmaPi(); nTof.tpcNSigmaPr = nTra.tpcNSigmaPr(); @@ -1457,6 +1748,18 @@ struct strangenesstofpid { v0tof.nSigmaPositiveLambdaPr, v0tof.nSigmaNegativeLambdaPi, v0tof.nSigmaNegativeLambdaPr, v0tof.nSigmaPositiveLambdaPi, v0tof.nSigmaPositiveK0ShortPi, v0tof.nSigmaNegativeK0ShortPi); + + if (calculateV0sNSigmaAll.value > 0) { + v0tofnsigmasall( + v0tof.nSigmaPositivePhotonEl, v0tof.nSigmaPositiveK0ShortEl, v0tof.nSigmaPositiveLambdaEl, + v0tof.nSigmaNegativePhotonEl, v0tof.nSigmaNegativeK0ShortEl, v0tof.nSigmaNegativeLambdaEl, + v0tof.nSigmaPositivePhotonPi, v0tof.nSigmaPositiveK0ShortPi, v0tof.nSigmaPositiveLambdaPi, + v0tof.nSigmaNegativePhotonPi, v0tof.nSigmaNegativeK0ShortPi, v0tof.nSigmaNegativeLambdaPi, + v0tof.nSigmaPositivePhotonKa, v0tof.nSigmaPositiveK0ShortEl, v0tof.nSigmaPositiveLambdaEl, + v0tof.nSigmaNegativePhotonKa, v0tof.nSigmaNegativeK0ShortEl, v0tof.nSigmaNegativeLambdaEl, + v0tof.nSigmaPositivePhotonPr, v0tof.nSigmaPositiveK0ShortPr, v0tof.nSigmaPositiveLambdaPr, + v0tof.nSigmaNegativePhotonPr, v0tof.nSigmaNegativeK0ShortPr, v0tof.nSigmaNegativeLambdaPr); + } } if (calculateV0TOFPIDs.value) { v0tofpid(v0tof.deltaTimePositiveLambdaPi, v0tof.deltaTimePositiveLambdaPr, @@ -1520,7 +1823,9 @@ struct strangenesstofpid { pTof.hasTOF = pTra.hasTOF(); pTof.tofExpMom = pTra.tofExpMom(); pTof.tofEvTime = reassociateTracks ? collisionEventTime[cascade.collisionId()] : pTra.tofEvTime(); - pTof.tofSignal = pTra.tofSignal() + (doBCshift ? deltaTimePos : 0.0f); + pTof.tofEvTimeErr = reassociateTracks ? collisionEventTimeErr[cascade.collisionId()] : pTra.tofEvTimeErr(); + // pTof.tofSignal = pTra.tofSignal() + (doBCshift ? deltaTimePos : 0.0f); + pTof.tofSignal = doBCshift && pTra.has_collision() ? pTra.tofSignalInAnotherBC(pTra.collision().bc_as().globalBC(), cascade.collision().bc_as().globalBC()) : pTra.tofSignal(); pTof.length = pTra.length(); pTof.tpcNSigmaPi = pTra.tpcNSigmaPi(); pTof.tpcNSigmaPr = pTra.tpcNSigmaPr(); @@ -1531,7 +1836,9 @@ struct strangenesstofpid { nTof.hasTOF = nTra.hasTOF(); nTof.tofExpMom = nTra.tofExpMom(); nTof.tofEvTime = reassociateTracks ? collisionEventTime[cascade.collisionId()] : nTra.tofEvTime(); - nTof.tofSignal = nTra.tofSignal() + (doBCshift ? deltaTimeNeg : 0.0f); + nTof.tofEvTimeErr = reassociateTracks ? collisionEventTimeErr[cascade.collisionId()] : nTra.tofEvTimeErr(); + // nTof.tofSignal = nTra.tofSignal() + (doBCshift ? deltaTimeNeg : 0.0f); + nTof.tofSignal = doBCshift && nTra.has_collision() ? nTra.tofSignalInAnotherBC(nTra.collision().bc_as().globalBC(), cascade.collision().bc_as().globalBC()) : nTra.tofSignal(); nTof.length = nTra.length(); nTof.tpcNSigmaPi = nTra.tpcNSigmaPi(); nTof.tpcNSigmaPr = nTra.tpcNSigmaPr(); @@ -1542,7 +1849,9 @@ struct strangenesstofpid { bTof.hasTOF = bTra.hasTOF(); bTof.tofExpMom = bTra.tofExpMom(); bTof.tofEvTime = reassociateTracks ? collisionEventTime[cascade.collisionId()] : bTra.tofEvTime(); - bTof.tofSignal = bTra.tofSignal() + (doBCshift ? deltaTimeBach : 0.0f); + bTof.tofEvTimeErr = reassociateTracks ? collisionEventTimeErr[cascade.collisionId()] : bTra.tofEvTimeErr(); + // bTof.tofSignal = bTra.tofSignal() + (doBCshift ? deltaTimeBach : 0.0f); + bTof.tofSignal = doBCshift && bTra.has_collision() ? bTra.tofSignalInAnotherBC(bTra.collision().bc_as().globalBC(), cascade.collision().bc_as().globalBC()) : bTra.tofSignal(); bTof.length = bTra.length(); bTof.tpcNSigmaPi = bTra.tpcNSigmaPi(); bTof.tpcNSigmaKa = bTra.tpcNSigmaKa(); @@ -1563,6 +1872,18 @@ struct strangenesstofpid { casctofnsigmas( casctof.nSigmaXiLaPi, casctof.nSigmaXiLaPr, casctof.nSigmaXiPi, casctof.nSigmaOmLaPi, casctof.nSigmaOmLaPr, casctof.nSigmaOmKa); + + if (calculateCascadesNSigmaAll.value > 0) { + casctofnsigmasall( + casctof.nSigmaXiPositiveLaEl, casctof.nSigmaXiNegativeLaEl, casctof.nSigmaXiBachelorEl, + casctof.nSigmaOmPositiveLaEl, casctof.nSigmaOmNegativeLaEl, casctof.nSigmaOmBachelorEl, + casctof.nSigmaXiPositiveLaPi, casctof.nSigmaXiNegativeLaPi, casctof.nSigmaXiBachelorPi, + casctof.nSigmaOmPositiveLaPi, casctof.nSigmaOmNegativeLaPi, casctof.nSigmaOmBachelorPi, + casctof.nSigmaXiPositiveLaKa, casctof.nSigmaXiNegativeLaKa, casctof.nSigmaXiBachelorKa, + casctof.nSigmaOmPositiveLaKa, casctof.nSigmaOmNegativeLaKa, casctof.nSigmaOmBachelorKa, + casctof.nSigmaXiPositiveLaPr, casctof.nSigmaXiNegativeLaPr, casctof.nSigmaXiBachelorPr, + casctof.nSigmaOmPositiveLaPr, casctof.nSigmaOmNegativeLaPr, casctof.nSigmaOmBachelorPr); + } } if (calculateCascTOFPIDs.value) { casctofpids( @@ -1581,12 +1902,12 @@ struct strangenesstofpid { for (const auto& collision : collisions) { histos.fill(HIST("hCollisionTimes"), collision.eventTime()); + histos.fill(HIST("hCollisionTimesError"), collision.eventTimeErr()); } // auto-determine if using old format if (dauTrackTOFPIDs.size() != 0) { - auto firstTOFPID = dauTrackTOFPIDs.rawIteratorAt(0); - isNewTOFFormat = firstTOFPID.straCollisionId() < 0 ? false : true; + isNewTOFFormat = dauTrackTable.size() == dauTrackTOFPIDs.size() ? false : true; } if (!isNewTOFFormat && calculationMethod.value > 0) { @@ -1601,13 +1922,19 @@ struct strangenesstofpid { initCCDB(collision.runNumber()); } + mTOFResponse->processSetup(collisions.iteratorAt(0)); + // hold indices std::vector tofIndices(dauTrackTable.size(), -1); if (isNewTOFFormat) { // re-index for (const auto& dauTrackTOFPID : dauTrackTOFPIDs) { - tofIndices[dauTrackTOFPID.dauTrackExtraId()] = dauTrackTOFPID.globalIndex(); + if (dauTrackTOFPID.dauTrackExtraId() >= 0) { + tofIndices[dauTrackTOFPID.dauTrackExtraId()] = dauTrackTOFPID.globalIndex(); + } else { + LOGF(warning, "dauTrackTOFPID points to no entry in the DauTrackExtras table (dauTrackExtraId = %i)! This could be intentional (for example, using converters) but please be careful.", dauTrackTOFPID.dauTrackExtraId()); + } } } else { // they are actually joinable @@ -1632,22 +1959,19 @@ struct strangenesstofpid { pTof.tpcNSigmaPr = pTra.tpcNSigmaPr(); if (tofIndices[V0.posTrackExtraId()] >= 0 && collision.eventTime() > -1e+5) { auto pTofExt = dauTrackTOFPIDs.rawIteratorAt(tofIndices[V0.posTrackExtraId()]); - - if (pTofExt.straCollisionId() >= 0) { - // extract BC for BC time shift - auto collisionTrack = collisions.rawIteratorAt(pTofExt.straCollisionId()); - const int64_t deltaBc = collisionTrack.globalBC() - collision.globalBC(); - const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; - histos.fill(HIST("hV0PositiveBCShift"), deltaTimeBc); - deltaTimeBcPos = deltaTimeBc; - - // assign variables - pTof.collisionId = pTofExt.straCollisionId(); - pTof.tofExpMom = pTofExt.tofExpMom(); - pTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : pTofExt.tofEvTime(); - pTof.tofSignal = pTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); - pTof.length = pTofExt.length(); - } + // extract BC for BC time shift + const int64_t deltaBc = pTofExt.globalBC() - collision.globalBC(); + const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; + histos.fill(HIST("hV0PositiveBCShift"), deltaTimeBc); + deltaTimeBcPos = deltaTimeBc; + + // assign variables + pTof.tofExpMom = pTofExt.tofExpMom(); + pTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : pTofExt.tofEvTime(); + pTof.tofEvTimeErr = reassociateTracks.value ? collision.eventTimeErr() : pTofExt.tofEvTimeErr(); + // pTof.tofEvTimeErr = pTofExt.tofEvTimeErr(); + pTof.tofSignal = pTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); + pTof.length = pTofExt.length(); } nTof.hasITS = nTra.hasITS(); @@ -1657,22 +1981,19 @@ struct strangenesstofpid { nTof.tpcNSigmaPr = nTra.tpcNSigmaPr(); if (tofIndices[V0.negTrackExtraId()] >= 0 && collision.eventTime() > -1e+5) { auto nTofExt = dauTrackTOFPIDs.rawIteratorAt(tofIndices[V0.negTrackExtraId()]); - - if (nTofExt.straCollisionId() >= 0) { - // extract BC for BC time shift - auto collisionTrack = collisions.rawIteratorAt(nTofExt.straCollisionId()); - const int64_t deltaBc = collisionTrack.globalBC() - collision.globalBC(); - const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; - histos.fill(HIST("hV0NegativeBCShift"), deltaTimeBc); - deltaTimeBcNeg = deltaTimeBc; - - // assign variables - nTof.collisionId = nTofExt.straCollisionId(); - nTof.tofExpMom = nTofExt.tofExpMom(); - nTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : nTofExt.tofEvTime(); - nTof.tofSignal = nTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); - nTof.length = nTofExt.length(); - } + // extract BC for BC time shift + const int64_t deltaBc = nTofExt.globalBC() - collision.globalBC(); + const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; + histos.fill(HIST("hV0NegativeBCShift"), deltaTimeBc); + deltaTimeBcNeg = deltaTimeBc; + + // assign variables + nTof.tofExpMom = nTofExt.tofExpMom(); + nTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : nTofExt.tofEvTime(); + nTof.tofEvTimeErr = reassociateTracks.value ? collision.eventTimeErr() : nTofExt.tofEvTimeErr(); + // nTof.tofEvTimeErr = nTofExt.tofEvTimeErr(); + nTof.tofSignal = nTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); + nTof.length = nTofExt.length(); } if (pTof.hasTOF) { histos.fill(HIST("hTOFSignalPositive"), pTof.tofSignal); @@ -1691,6 +2012,18 @@ struct strangenesstofpid { v0tof.nSigmaPositiveLambdaPr, v0tof.nSigmaNegativeLambdaPi, v0tof.nSigmaNegativeLambdaPr, v0tof.nSigmaPositiveLambdaPi, v0tof.nSigmaPositiveK0ShortPi, v0tof.nSigmaNegativeK0ShortPi); + + if (calculateV0sNSigmaAll.value > 0) { + v0tofnsigmasall( + v0tof.nSigmaPositivePhotonEl, v0tof.nSigmaPositiveK0ShortEl, v0tof.nSigmaPositiveLambdaEl, + v0tof.nSigmaNegativePhotonEl, v0tof.nSigmaNegativeK0ShortEl, v0tof.nSigmaNegativeLambdaEl, + v0tof.nSigmaPositivePhotonPi, v0tof.nSigmaPositiveK0ShortPi, v0tof.nSigmaPositiveLambdaPi, + v0tof.nSigmaNegativePhotonPi, v0tof.nSigmaNegativeK0ShortPi, v0tof.nSigmaNegativeLambdaPi, + v0tof.nSigmaPositivePhotonKa, v0tof.nSigmaPositiveK0ShortEl, v0tof.nSigmaPositiveLambdaEl, + v0tof.nSigmaNegativePhotonKa, v0tof.nSigmaNegativeK0ShortEl, v0tof.nSigmaNegativeLambdaEl, + v0tof.nSigmaPositivePhotonPr, v0tof.nSigmaPositiveK0ShortPr, v0tof.nSigmaPositiveLambdaPr, + v0tof.nSigmaNegativePhotonPr, v0tof.nSigmaNegativeK0ShortPr, v0tof.nSigmaNegativeLambdaPr); + } } if (calculateV0TOFPIDs.value) { v0tofpid(v0tof.deltaTimePositiveLambdaPi, v0tof.deltaTimePositiveLambdaPr, @@ -1725,21 +2058,18 @@ struct strangenesstofpid { pTof.tpcNSigmaPr = pTra.tpcNSigmaPr(); if (tofIndices[cascade.posTrackExtraId()] >= 0 && collision.eventTime() > -1e+5) { auto pTofExt = dauTrackTOFPIDs.rawIteratorAt(tofIndices[cascade.posTrackExtraId()]); - - if (pTofExt.straCollisionId() >= 0) { - // extract BC for BC time shift - auto collisionTrack = collisions.rawIteratorAt(pTofExt.straCollisionId()); - const int64_t deltaBc = collisionTrack.globalBC() - collision.globalBC(); - const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; - histos.fill(HIST("hCascadePositiveBCShift"), deltaTimeBc); - histos.fill(HIST("h2dTOFSignalCascadePositive"), pTof.tofSignal, deltaTimeBc); - - pTof.collisionId = pTofExt.straCollisionId(); - pTof.tofExpMom = pTofExt.tofExpMom(); - pTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : pTofExt.tofEvTime(); - pTof.tofSignal = pTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); - pTof.length = pTofExt.length(); - } + // extract BC for BC time shift + const int64_t deltaBc = pTofExt.globalBC() - collision.globalBC(); + const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; + histos.fill(HIST("hCascadePositiveBCShift"), deltaTimeBc); + histos.fill(HIST("h2dTOFSignalCascadePositive"), pTof.tofSignal, deltaTimeBc); + + pTof.tofExpMom = pTofExt.tofExpMom(); + pTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : pTofExt.tofEvTime(); + pTof.tofEvTimeErr = reassociateTracks.value ? collision.eventTimeErr() : pTofExt.tofEvTimeErr(); + // pTof.tofEvTimeErr = pTofExt.tofEvTimeErr(); + pTof.tofSignal = pTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); + pTof.length = pTofExt.length(); } nTof.hasITS = nTra.hasITS(); @@ -1749,21 +2079,18 @@ struct strangenesstofpid { nTof.tpcNSigmaPr = nTra.tpcNSigmaPr(); if (tofIndices[cascade.negTrackExtraId()] >= 0 && collision.eventTime() > -1e+5) { auto nTofExt = dauTrackTOFPIDs.rawIteratorAt(tofIndices[cascade.negTrackExtraId()]); - - if (nTofExt.straCollisionId() >= 0) { - // extract BC for BC time shift - auto collisionTrack = collisions.rawIteratorAt(nTofExt.straCollisionId()); - const int64_t deltaBc = collisionTrack.globalBC() - collision.globalBC(); - const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; - histos.fill(HIST("hCascadeNegativeBCShift"), deltaTimeBc); - histos.fill(HIST("h2dTOFSignalCascadeNegative"), nTof.tofSignal, deltaTimeBc); - - nTof.collisionId = nTofExt.straCollisionId(); - nTof.tofExpMom = nTofExt.tofExpMom(); - nTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : nTofExt.tofEvTime(); - nTof.tofSignal = nTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); - nTof.length = nTofExt.length(); - } + // extract BC for BC time shift + const int64_t deltaBc = nTofExt.globalBC() - collision.globalBC(); + const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; + histos.fill(HIST("hCascadeNegativeBCShift"), deltaTimeBc); + histos.fill(HIST("h2dTOFSignalCascadeNegative"), nTof.tofSignal, deltaTimeBc); + + nTof.tofExpMom = nTofExt.tofExpMom(); + nTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : nTofExt.tofEvTime(); + nTof.tofEvTimeErr = reassociateTracks.value ? collision.eventTimeErr() : nTofExt.tofEvTimeErr(); + // nTof.tofEvTimeErr = nTofExt.tofEvTimeErr(); + nTof.tofSignal = nTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); + nTof.length = nTofExt.length(); } bTof.hasITS = bTra.hasITS(); @@ -1773,21 +2100,18 @@ struct strangenesstofpid { bTof.tpcNSigmaKa = bTra.tpcNSigmaKa(); if (tofIndices[cascade.bachTrackExtraId()] >= 0 && collision.eventTime() > -1e+5) { auto bTofExt = dauTrackTOFPIDs.rawIteratorAt(tofIndices[cascade.bachTrackExtraId()]); - - if (bTofExt.straCollisionId() >= 0) { - // extract BC for BC time shift - auto collisionTrack = collisions.rawIteratorAt(bTofExt.straCollisionId()); - const int64_t deltaBc = collisionTrack.globalBC() - collision.globalBC(); - const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; - histos.fill(HIST("hCascadeBachelorBCShift"), deltaTimeBc); - histos.fill(HIST("h2dTOFSignalCascadeBachelor"), bTof.tofSignal, deltaTimeBc); - - bTof.collisionId = bTofExt.straCollisionId(); - bTof.tofExpMom = bTofExt.tofExpMom(); - bTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : bTofExt.tofEvTime(); - bTof.tofSignal = bTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); - bTof.length = bTofExt.length(); - } + // extract BC for BC time shift + const int64_t deltaBc = bTofExt.globalBC() - collision.globalBC(); + const double deltaTimeBc = o2::constants::lhc::LHCBunchSpacingNS * deltaBc * 1000.0f; + histos.fill(HIST("hCascadeBachelorBCShift"), deltaTimeBc); + histos.fill(HIST("h2dTOFSignalCascadeBachelor"), bTof.tofSignal, deltaTimeBc); + + bTof.tofExpMom = bTofExt.tofExpMom(); + bTof.tofEvTime = reassociateTracks.value ? collision.eventTime() : bTofExt.tofEvTime(); + bTof.tofEvTimeErr = reassociateTracks.value ? collision.eventTimeErr() : bTofExt.tofEvTimeErr(); + // bTof.tofEvTimeErr = bTofExt.tofEvTimeErr(); + bTof.tofSignal = bTofExt.tofSignal() + (doBCshift.value ? deltaTimeBc : 0.0f); + bTof.length = bTofExt.length(); } cascTofInfo casctof = calculateTofInfoCascade(collisions, cascade.straCollisionId(), cascade, pTof, nTof, bTof); @@ -1796,6 +2120,18 @@ struct strangenesstofpid { casctofnsigmas( casctof.nSigmaXiLaPi, casctof.nSigmaXiLaPr, casctof.nSigmaXiPi, casctof.nSigmaOmLaPi, casctof.nSigmaOmLaPr, casctof.nSigmaOmKa); + + if (calculateCascadesNSigmaAll.value > 0) { + casctofnsigmasall( + casctof.nSigmaXiPositiveLaEl, casctof.nSigmaXiNegativeLaEl, casctof.nSigmaXiBachelorEl, + casctof.nSigmaOmPositiveLaEl, casctof.nSigmaOmNegativeLaEl, casctof.nSigmaOmBachelorEl, + casctof.nSigmaXiPositiveLaPi, casctof.nSigmaXiNegativeLaPi, casctof.nSigmaXiBachelorPi, + casctof.nSigmaOmPositiveLaPi, casctof.nSigmaOmNegativeLaPi, casctof.nSigmaOmBachelorPi, + casctof.nSigmaXiPositiveLaKa, casctof.nSigmaXiNegativeLaKa, casctof.nSigmaXiBachelorKa, + casctof.nSigmaOmPositiveLaKa, casctof.nSigmaOmNegativeLaKa, casctof.nSigmaOmBachelorKa, + casctof.nSigmaXiPositiveLaPr, casctof.nSigmaXiNegativeLaPr, casctof.nSigmaXiBachelorPr, + casctof.nSigmaOmPositiveLaPr, casctof.nSigmaOmNegativeLaPr, casctof.nSigmaOmBachelorPr); + } } if (calculateCascTOFPIDs.value) { casctofpids( @@ -1814,6 +2150,7 @@ struct strangenesstofpid { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { + o2::pid::tof::TOFResponseImpl::metadataInfo.initMetadata(cfgc); return WorkflowSpec{ adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/TableProducer/Strangeness/v0qaanalysis.cxx b/PWGLF/TableProducer/Strangeness/v0qaanalysis.cxx index 771b2ab4a18..269bb4a5098 100644 --- a/PWGLF/TableProducer/Strangeness/v0qaanalysis.cxx +++ b/PWGLF/TableProducer/Strangeness/v0qaanalysis.cxx @@ -20,6 +20,7 @@ #include "PWGLF/Utils/inelGt.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -43,12 +44,14 @@ #include +#include #include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; void customize(std::vector& workflowOptions) { @@ -61,6 +64,7 @@ void customize(std::vector& workflowOptions) using DauTracks = soa::Join; using DauTracksMC = soa::Join; using V0Collisions = soa::Join; +using BCsWithBcSels = soa::Join; struct LfV0qaanalysis { @@ -106,10 +110,12 @@ struct LfV0qaanalysis { registry.add("hCentFT0M_GenRecoColl_MC_INELgt0", "hCentFT0M_GenRecoColl_MC_INELgt0", {HistType::kTH1F, {{1000, 0.f, 100.f}}}); registry.add("hCentFT0M_GenColl_MC", "hCentFT0M_GenColl_MC", {HistType::kTH1F, {{1000, 0.f, 100.f}}}); registry.add("hCentFT0M_GenColl_MC_INELgt0", "hCentFT0M_GenColl_MC_INELgt0", {HistType::kTH1F, {{1000, 0.f, 100.f}}}); - registry.add("hNEventsMCGen", "hNEventsMCGen", {HistType::kTH1D, {{4, 0.f, 4.f}}}); + registry.add("hNEventsMCGen", "hNEventsMCGen", {HistType::kTH1D, {{5, 0.f, 5.f}}}); registry.get(HIST("hNEventsMCGen"))->GetXaxis()->SetBinLabel(1, "all"); registry.get(HIST("hNEventsMCGen"))->GetXaxis()->SetBinLabel(2, "zvertex_true"); - registry.get(HIST("hNEventsMCGen"))->GetXaxis()->SetBinLabel(3, "INELgt0_true"); + registry.get(HIST("hNEventsMCGen"))->GetXaxis()->SetBinLabel(3, "BC TF/ITS ROF border"); + registry.get(HIST("hNEventsMCGen"))->GetXaxis()->SetBinLabel(4, "RCTFlagsChecker"); + registry.get(HIST("hNEventsMCGen"))->GetXaxis()->SetBinLabel(5, "INELgt0_true"); registry.add("hNEventsMCGenReco", "hNEventsMCGenReco", {HistType::kTH1D, {{2, 0.f, 2.f}}}); registry.get(HIST("hNEventsMCGenReco"))->GetXaxis()->SetBinLabel(1, "INEL"); registry.get(HIST("hNEventsMCGenReco"))->GetXaxis()->SetBinLabel(2, "INELgt0"); @@ -153,6 +159,7 @@ struct LfV0qaanalysis { registry.add("Generated_MCGenColl_INELgt0_Lambda", "Generated_MCGenColl_INELgt0_Lambda", {HistType::kTH2F, {{250, 0.f, 25.f}, {1000, 0.f, 100.f}}}); registry.add("Generated_MCGenColl_INELgt0_AntiLambda", "Generated_MCGenColl_INELgt0_AntiLambda", {HistType::kTH2F, {{250, 0.f, 25.f}, {1000, 0.f, 100.f}}}); } + rctChecker.init(cfgEvtRCTFlagCheckerLabel, cfgEvtRCTFlagCheckerZDCCheck, cfgEvtRCTFlagCheckerLimitAcceptAsBad, applyRCTOnGen); registry.print(); } @@ -164,11 +171,18 @@ struct LfV0qaanalysis { Configurable isTriggerTVX{"isTriggerTVX", 1, "Is Trigger TVX"}; Configurable isNoTimeFrameBorder{"isNoTimeFrameBorder", 1, "Is No Time Frame Border"}; Configurable isNoITSROFrameBorder{"isNoITSROFrameBorder", 1, "Is No ITS Readout Frame Border"}; + Configurable applyBcBorderCutsOnGen{"applyBcBorderCutsOnGen", false, "Apply enabled BC-level TF and ITS ROF border cuts on generated-level MC collisions"}; + Configurable applyRCTOnGen{"applyRCTOnGen", false, "Apply enabled BC-level RCT run-condition selection on generated-level MC collisions"}; Configurable isVertexTOFmatched{"isVertexTOFmatched", 0, "Is Vertex TOF matched"}; Configurable isNoSameBunchPileup{"isNoSameBunchPileup", 0, "isNoSameBunchPileup"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; Configurable NotITSAfterburner{"NotITSAfterburner", 0, "NotITSAfterburner"}; + RCTFlagsChecker rctChecker; + // V0 selection criteria Configurable v0cospa{"v0cospa", 0.97, "V0 CosPA"}; Configurable dcav0dau{"dcav0dau", 10, "DCA V0 Daughters"}; @@ -214,6 +228,27 @@ struct LfV0qaanalysis { return true; } + template + bool acceptGeneratedEventBcBorderCuts(TBC const& bc) + { + if (!applyBcBorderCutsOnGen) { + return true; + } + if (isNoTimeFrameBorder && !bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (isNoITSROFrameBorder && !bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + return true; + } + + template + bool acceptGeneratedEventRCT(TBC const& bc) + { + return !applyRCTOnGen || rctChecker(bc); + } + Filter preFilterV0 = nabs(aod::v0data::dcapostopv) > dcapostopv&& nabs(aod::v0data::dcanegtopv) > dcanegtopv&& aod::v0data::dcaV0daughters < dcav0dau; @@ -230,7 +265,7 @@ struct LfV0qaanalysis { registry.fill(HIST("hCentFT0M"), collision.centFT0M()); registry.fill(HIST("hCentNGlobals"), collision.centNGlobal()); - for (auto& v0 : V0s) { // loop over V0s + for (const auto& v0 : V0s) { // loop over V0s if (v0.v0Type() != v0TypeSelection) { continue; @@ -323,7 +358,7 @@ struct LfV0qaanalysis { } auto v0sThisCollision = V0s.sliceBy(perCol, collision.globalIndex()); - for (auto& v0 : v0sThisCollision) { // loop over V0s + for (const auto& v0 : v0sThisCollision) { // loop over V0s if (!v0.has_mcParticle()) { continue; @@ -371,8 +406,8 @@ struct LfV0qaanalysis { lPDG = v0mcparticle.pdgCode(); isprimary = v0mcparticle.isPhysicalPrimary(); } - for (auto& mcparticleDaughter0 : v0mcparticle.daughters_as()) { - for (auto& mcparticleDaughter1 : v0mcparticle.daughters_as()) { + for (const auto& mcparticleDaughter0 : v0mcparticle.daughters_as()) { + for (const auto& mcparticleDaughter1 : v0mcparticle.daughters_as()) { if (mcparticleDaughter0.pdgCode() == 211 && mcparticleDaughter1.pdgCode() == -211) { isDauK0Short = true; } @@ -389,7 +424,7 @@ struct LfV0qaanalysis { float pdgMother = 0.; if (std::abs(v0mcparticle.pdgCode()) == 3122 && v0mcparticle.has_mothers()) { - for (auto& mcparticleMother0 : v0mcparticle.mothers_as()) { + for (const auto& mcparticleMother0 : v0mcparticle.mothers_as()) { if (std::abs(mcparticleMother0.pdgCode()) == 3312 || std::abs(mcparticleMother0.pdgCode()) == 3322) { ptMotherMC = mcparticleMother0.pt(); pdgMother = mcparticleMother0.pdgCode(); @@ -435,7 +470,7 @@ struct LfV0qaanalysis { // Generated particles const auto particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache1); - for (auto& mcParticle : particlesInCollision) { + for (const auto& mcParticle : particlesInCollision) { if (!mcParticle.isPhysicalPrimary()) { continue; } @@ -469,7 +504,8 @@ struct LfV0qaanalysis { void processMCGen(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles, - soa::SmallGroups> const& collisions) + soa::SmallGroups> const& collisions, + BCsWithBcSels const&) { //==================================== //===== Event Loss Denominator ======= @@ -481,13 +517,22 @@ struct LfV0qaanalysis { return; } registry.fill(HIST("hNEventsMCGen"), 1.5); + const auto bc = mcCollision.bc_as(); + if (!acceptGeneratedEventBcBorderCuts(bc)) { + return; + } + registry.fill(HIST("hNEventsMCGen"), 2.5); + if (!acceptGeneratedEventRCT(bc)) { + return; + } + registry.fill(HIST("hNEventsMCGen"), 3.5); registry.fill(HIST("hCentFT0M_GenColl_MC"), mcCollision.centFT0M()); bool isINELgt0true = false; if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { isINELgt0true = true; - registry.fill(HIST("hNEventsMCGen"), 2.5); + registry.fill(HIST("hNEventsMCGen"), 4.5); registry.fill(HIST("hCentFT0M_GenColl_MC_INELgt0"), mcCollision.centFT0M()); } @@ -495,7 +540,7 @@ struct LfV0qaanalysis { //===== Signal Loss Denominator ======= //===================================== - for (auto& mcParticle : mcParticles) { + for (const auto& mcParticle : mcParticles) { if (!mcParticle.isPhysicalPrimary()) { continue; @@ -526,7 +571,7 @@ struct LfV0qaanalysis { int recoCollIndex_INEL = 0; int recoCollIndex_INELgt0 = 0; - for (auto& collision : collisions) { // loop on reconstructed collisions + for (const auto& collision : collisions) { // loop on reconstructed collisions //===================================== //====== Event Split Numerator ======== @@ -553,7 +598,7 @@ struct LfV0qaanalysis { //======== Sgn Split Numerator ======== //===================================== - for (auto& mcParticle : mcParticles) { + for (const auto& mcParticle : mcParticles) { if (!mcParticle.isPhysicalPrimary()) { continue; @@ -605,7 +650,7 @@ struct LfV0qaanalysis { //===== Signal Loss Numerator ========= //===================================== - for (auto& mcParticle : mcParticles) { + for (const auto& mcParticle : mcParticles) { if (!mcParticle.isPhysicalPrimary()) { continue; @@ -682,7 +727,7 @@ struct LfMyV0s { void process(aod::MyV0Candidates const& myv0s) { - for (auto& candidate : myv0s) { + for (const auto& candidate : myv0s) { registry.fill(HIST("hMassLambda"), candidate.masslambda()); registry.fill(HIST("hPt"), candidate.v0pt()); diff --git a/PWGLF/Tasks/GlobalEventProperties/CMakeLists.txt b/PWGLF/Tasks/GlobalEventProperties/CMakeLists.txt index cde1964f9e2..ed3beadecc5 100644 --- a/PWGLF/Tasks/GlobalEventProperties/CMakeLists.txt +++ b/PWGLF/Tasks/GlobalEventProperties/CMakeLists.txt @@ -43,3 +43,8 @@ o2physics_add_dpl_workflow(nch-studypp SOURCES nchStudypp.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(dndeta-hi + SOURCES dndetaHi.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::Framework + COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx index 3d6c10e5495..8390f501389 100644 --- a/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/PseudorapidityDensityMFT.cxx @@ -50,7 +50,9 @@ #include #include #include +#include #include +#include #include #include @@ -69,21 +71,20 @@ using namespace o2::framework::expressions; using namespace o2::aod::track; using namespace o2::aod::rctsel; -AxisSpec ptAxis = {2001, -0.005, 20.005}; -AxisSpec deltazAxis = {6100, -6.1, 6.1}; -AxisSpec zAxis = {3001, -30.1, 30.1}; -AxisSpec phiAxis = {629, 0, o2::constants::math::TwoPI, "Rad", "phi axis"}; -AxisSpec etaAxis = {18, -4.6, -1.}; +const AxisSpec ptAxis = {2001, -0.005, 20.005}; +const AxisSpec deltazAxis = {6100, -6.1, 6.1}; +const AxisSpec zAxis = {3001, -30.1, 30.1}; +const AxisSpec phiAxis = {629, 0, o2::constants::math::TwoPI, "Rad", "phi axis"}; +const AxisSpec etaAxis = {18, -4.6, -1.}; // AxisSpec dcaXyAxis = {2000, -10, 10}; // AxisSpec dcaZAxis = {2000, -10, 10}; // AxisSpec dcaXAxis = {2000, -10, 10}; // AxisSpec dcaYAxis = {2000, -10, 10};// previous AxisSpec dcaYAxis = {2000, -10, 10}; -AxisSpec dcaXyAxis = {6000, -30, 30}; -AxisSpec dcaZAxis = {6000, -30, 30}; -AxisSpec dcaXAxis = {6000, -30, 30}; -AxisSpec dcaYAxis = {6000, -30, 30}; // previous AxisSpec dcaYAxis = {2000, -10, 10}; - +const AxisSpec dcaXyAxis = {6000, -30, 30}; +const AxisSpec dcaZAxis = {6000, -30, 30}; +const AxisSpec dcaXAxis = {6000, -30, 30}; +const AxisSpec dcaYAxis = {6000, -30, 30}; // previous AxisSpec dcaYAxis = {2000, -10, 10}; // AxisSpec dcaXyAxis = {600, -0.15f, 0.15f}; // AxisSpec dcaZAxis = {600, -0.15f, 0.15f}; // AxisSpec dcaXAxis = {600, -0.15f, 0.15f}; @@ -91,18 +92,17 @@ AxisSpec dcaYAxis = {6000, -30, 30}; // previous AxisSpec dcaYAxis = {2000, -10 // bin width 0.0005 cm: range [-30, 30] cm => 60/0.0005 = 120000 bins // Keep bin width = 0.0005 cm (5 um): range [-1, 1] cm => 2.0/0.0005 = 4000 bins // AxisSpec axisBinsDCA = {600, -0.15f, 0.15f, "#it{dca}_{xy} (cm)"}; - -AxisSpec centAxis = {{0, 10, 20, 30, 40, 50, 60, 70, 80, 100}}; +const AxisSpec centAxis = {{0, 10, 20, 30, 40, 50, 60, 70, 80, 100}}; // Vertex position axes (cm) -AxisSpec vxAxis = {200, -0.5, 0.5, "V_{x} (cm)"}; -AxisSpec vyAxis = {200, -0.5, 0.5, "V_{y} (cm)"}; +const AxisSpec vxAxis = {200, -0.5, 0.5, "V_{x} (cm)"}; +const AxisSpec vyAxis = {200, -0.5, 0.5, "V_{y} (cm)"}; // Status axis for reco/truth (1=reco, 2=true) -AxisSpec recoTruthStatusAxis = {2, 0.5, 2.5, "status"}; +const AxisSpec recoTruthStatusAxis = {2, 0.5, 2.5, "status"}; // Delta-vertex axes (reco - true) in cm -AxisSpec deltaVxAxis = {400, -0.5, 0.5, "#DeltaV_{x} = V_{x}^{rec}-V_{x}^{true} (cm)"}; -AxisSpec deltaVyAxis = {400, -0.5, 0.5, "#DeltaV_{y} = V_{y}^{rec}-V_{y}^{true} (cm)"}; +const AxisSpec deltaVxAxis = {400, -0.5, 0.5, "#DeltaV_{x} = V_{x}^{rec}-V_{x}^{true} (cm)"}; +const AxisSpec deltaVyAxis = {400, -0.5, 0.5, "#DeltaV_{y} = V_{y}^{rec}-V_{y}^{true} (cm)"}; static constexpr TrackSelectionFlags::flagtype TrackSelectionIts = TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | @@ -119,15 +119,21 @@ static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = // using MFTTracksLabeled = soa::Join; // replace your alias with the extension included: using FullBCs = soa::Join; - using MFTTracksLabeled = soa::Join; + using MFTTracksLabeled3d = soa::Join; +// using MFTTracksLabeledGenReco3d =soa::SmallGroups>; +// using MFTTracksLabeledGenReco2d =soa::SmallGroups>; + +using MFTTracksLabeledGenReco3d = soa::Join; +using MFTTracksLabeledGenReco2d = soa::Join; + using MFTTracksLabeled2d = soa::Join; @@ -136,6 +142,8 @@ using MFTTracksLabeledOrg = soa::Join; +// using MFTTracksLabeledOrg = soa::Filtered>; + // using McCollisionsWithExtra = soa::Join; using McCollisionsWithExtra = o2::soa::Join; @@ -146,10 +154,10 @@ struct PseudorapidityDensityMFT { Preslice perCol = o2::aod::fwdtrack::collisionId; Preslice perMcCol = aod::mcparticle::mcCollisionId; Preslice perColCentral = aod::track::collisionId; - Service pdg; + Service pdg{}; // --- CCDB magnetic field (needed for propagateToDCAhelix in this device) --- - Service ccdbMgr; + Service ccdbMgr{}; Configurable ccdburlMag{"ccdburlMag", "http://alice-ccdb.cern.ch", "CCDB url for GRPMagField"}; Configurable grpmagPathMag{"grpmagPathMag", "GLO/Config/GRPMagField", @@ -158,12 +166,16 @@ struct PseudorapidityDensityMFT { int magRunNumber = -1; float bzMFT = 0.f; o2::parameters::GRPMagField* grpmag = nullptr; - static constexpr double CenterMft[3] = {0., 0., -61.4}; + static constexpr std::array CenterMft{0., 0., -61.4}; enum class GenRecoCutBin : int { AllRecoCollisions = 1, - UseContBestCollisionIndex, HasMcCollision, + RctMFT, + UseGoodItsLayersAll, + UseContBestCollisionIndex, + VzWindow, + InelGt0, IsTriggerTVX, NoTimeFrameBorder, NoITSROFrameBorder, @@ -173,10 +185,25 @@ struct PseudorapidityDensityMFT { NoCollInRofStrict, NoCollInTimeRangeStandard, NoCollInTimeRangeStrict, - NoHighMultCollInPrevRof, - RctMFT, + NoHighMultCollInPrevRof + }; + + enum class DataCutBin : int { + All = 1, VzWindow, - InelGt0 + PerCollisionSampleGt0, + InelGt0, + IsTriggerTVX, + NoTimeFrameBorder, + NoITSROFrameBorder, + NoSameBunchPileup, + UseGoodItsLayersAll, + GoodZvtxFT0vsPV, + NoCollInRofStandard, + NoCollInRofStrict, + NoCollInTimeRangeStandard, + NoCollInTimeRangeStrict, + NoHighMultCollInPrevRof }; enum class TrackLabelSummaryBin : int { @@ -270,9 +297,6 @@ struct PseudorapidityDensityMFT { static constexpr int NoCompatibleCollisions = 0; static constexpr int SingleCompatibleCollision = 1; - static constexpr int OrphanAmbDegree = 0; - static constexpr int NonAmbiguousAmbDegree = 1; - static constexpr int ChargeUnitTimesThree = 3; void initMagField(FullBCs::iterator const& bc) @@ -293,9 +317,10 @@ struct PseudorapidityDensityMFT { o2::base::Propagator::initFieldFromGRP(grpmag); magRunNumber = bc.runNumber(); - auto* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + auto* field = dynamic_cast( + TGeoGlobalMagField::Instance()->GetField()); if (field) { - bzMFT = field->getBz(CenterMft); + bzMFT = field->getBz(CenterMft.data()); LOGP(info, "Initialized magnetic field for run {}: bzMFT={} kG", magRunNumber, bzMFT); } else { LOGF(warning, "TGeoGlobalMagField has no field even after initFieldFromGRP; bzMFT=0"); @@ -312,9 +337,11 @@ struct PseudorapidityDensityMFT { Configurable estimatorEta{"estimatorEta", 1.0, "eta range for INEL>0 sample definition"}; - + Configurable usePerCollisionSampleGt0Cut{"usePerCollisionSampleGt0Cut", true, "Require at least one central-barrel track in the per-collision sample"}; + Configurable useMidtracksAndPerCollisionSampleGt0Cut{"useMidtracksAndPerCollisionSampleGt0Cut", true, "Require both INEL>0 midtracks and at least one central-barrel track in the per-collision sample"}; Configurable useEvSel{"useEvSel", true, "use event selection"}; Configurable useNoSameBunchPileup{"useNoSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable useGoodItsLayersAll{"useGoodItsLayersAll", true, "all ITS layers are in a good state"}; Configurable useNoCollInRofStandard{"useNoCollInRofStandard", true, "Require evsel::kNoCollInRofStrict in processGenReco"}; Configurable useNoCollInRofStrict{"useNoCollInRofStrict", true, "Require evsel::kNoCollInRofStrict in processGenReco"}; Configurable useNoCollInTimeRangeStrict{"useNoCollInTimeRangeStrict", true, "Require evsel::kNoCollInTimeRangeStrict in processGenReco"}; @@ -364,6 +391,15 @@ struct PseudorapidityDensityMFT { Configurable cfgGenRecoTimeComTrackMode{"cfgGenRecoTimeComTrackMode", static_cast(GenRecoTimeComTrackMode::AllNonOrphan), "processGenRecoTimeCom track mode: AllNonOrphan=0, NonOrphanNonAmbiguous=1, NonOrphanAmbiguous=2"}; + Configurable useTriggerTVX{"useTriggerTVX", true, "Require kIsTriggerTVX in processGenReco"}; + Configurable useNoTimeFrameBorderCut{"useNoTimeFrameBorderCut", true, "Require kNoTimeFrameBorder in processGenReco"}; + Configurable useNoITSROFrameBorderCut{"useNoITSROFrameBorderCut", true, "Require kNoITSROFrameBorder in processGenReco"}; + AxisSpec multAxisRecoMFT = {multBinning, "N_{ch}^{reco,MFT}"}; + AxisSpec multAxisGenMFT = {multBinning, "N_{ch}^{gen,MFT}"}; + // Namespace plus invocation/reco IDs makes offline event keys independent of + // local collision-index reuse across dataframes and separate worker outputs. + TUUID associationKeyNamespace; + uint64_t associationInvocation{0}; HistogramRegistry registry{ "registry", @@ -408,20 +444,43 @@ struct PseudorapidityDensityMFT { "enabled!"); } AxisSpec multAxis = {multBinning, "N_{trk}"}; - auto hstat = registry.get(HIST("EventSelection")); - auto* x = hstat->GetXaxis(); - x->SetBinLabel(static_cast(EventSelectionBin::All), "All"); - x->SetBinLabel(static_cast(EventSelectionBin::Vz), "Vz"); - x->SetBinLabel(static_cast(EventSelectionBin::VzItsRof), "Vz+ITSRof"); - x->SetBinLabel(static_cast(EventSelectionBin::VzSelected), "Vz+Selected"); - x->SetBinLabel(static_cast(EventSelectionBin::Sel8VzInelGt0), "Sel8+Vz+INEL>0"); - x->SetBinLabel(static_cast(EventSelectionBin::SelInelInelFwdGt0), "Sel INEL,INEL_fwd>0"); - x->SetBinLabel(static_cast(EventSelectionBin::Rejected), "Rejected"); - x->SetBinLabel(static_cast(EventSelectionBin::GoodBCs), "Good BCs"); - x->SetBinLabel(static_cast(EventSelectionBin::BCsWithCollisions), "BCs with collisions"); - x->SetBinLabel(static_cast(EventSelectionBin::BCsWithPileupSplitting), "BCs with pile-up/splitting"); - x->SetBinLabel(static_cast(EventSelectionBin::PerCollisionSampleGt0), "percollisionSample>0"); - x->SetBinLabel(static_cast(EventSelectionBin::MidtracksAndPerCollisionSampleGt0), "midtracks+percollisionSample>0"); + auto eventSelectionHist = registry.get(HIST("EventSelection")); + auto* eventSelectionAxis = eventSelectionHist->GetXaxis(); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::All), "All"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::Vz), "Vz"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::VzItsRof), "Vz+ITSRof"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::VzSelected), "Vz+Selected"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::Sel8VzInelGt0), "Sel8+Vz+INEL>0"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::SelInelInelFwdGt0), "Sel INEL,INEL_fwd>0"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::Rejected), "Rejected"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::GoodBCs), "Good BCs"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::BCsWithCollisions), "BCs with collisions"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::BCsWithPileupSplitting), "BCs with pile-up/splitting"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::PerCollisionSampleGt0), "percollisionSample>0"); + eventSelectionAxis->SetBinLabel(static_cast(EventSelectionBin::MidtracksAndPerCollisionSampleGt0), "midtracks+percollisionSample>0"); + registry.add({"EventSelectionData", + ";cut;events", + {HistType::kTH1F, {{16, 0.5, 16.5}}}}); + { + auto h = registry.get(HIST("EventSelectionData")); + auto* x = h->GetXaxis(); + x->SetBinLabel(static_cast(DataCutBin::All), "All events (loop entry)"); + x->SetBinLabel(static_cast(DataCutBin::VzWindow), "Vz window"); + x->SetBinLabel(static_cast(DataCutBin::PerCollisionSampleGt0), "INEL>0 "); + x->SetBinLabel(static_cast(DataCutBin::InelGt0), "INEL>0 (midtracks>0)"); + x->SetBinLabel(static_cast(DataCutBin::IsTriggerTVX), "kIsTriggerTVX (if useEvSel)"); + x->SetBinLabel(static_cast(DataCutBin::NoTimeFrameBorder), "kNoTimeFrameBorder (if useEvSel)"); + x->SetBinLabel(static_cast(DataCutBin::NoITSROFrameBorder), "kNoITSROFrameBorder (if useEvSel)"); + x->SetBinLabel(static_cast(DataCutBin::NoSameBunchPileup), "kNoSameBunchPileup"); + x->SetBinLabel(static_cast(DataCutBin::UseGoodItsLayersAll), "kIsGoodITSLayersAll"); + x->SetBinLabel(static_cast(DataCutBin::GoodZvtxFT0vsPV), "kIsGoodZvtxFT0vsPV"); + x->SetBinLabel(static_cast(DataCutBin::NoCollInRofStandard), "kNoCollInRofStandard (cfg)"); + x->SetBinLabel(static_cast(DataCutBin::NoCollInRofStrict), "kNoCollInRofStrict (cfg)"); + x->SetBinLabel(static_cast(DataCutBin::NoCollInTimeRangeStandard), "kNoCollInTimeRangeStandard (cfg)"); + x->SetBinLabel(static_cast(DataCutBin::NoCollInTimeRangeStrict), "kNoCollInTimeRangeStrict (cfg)"); + x->SetBinLabel(static_cast(DataCutBin::NoHighMultCollInPrevRof), "kNoHighMultCollInPrevRof (cfg)"); + } + registry.add({"EventsNtrkZvtx", "; N_{trk}; #it{z}_{vtx} (cm); events", {HistType::kTH2F, {multAxis, zAxis}}}); @@ -445,6 +504,40 @@ struct PseudorapidityDensityMFT { {HistType::kTH1F, {dcaXyAxis}}}); if (doprocessGenReco3d || doprocessGenReco2d || doprocessGenRecoTimeCom) { + // Additional inputs for offline vertex/DCA studies. Existing histograms are unchanged. + if (doprocessGenReco3d) { + registry.add({"Association/RightTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"Association/WrongTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"Association/UnclassifiedTrackSparseBeforeDCA", + ";#eta;z_{chosen}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + // Key components are exact 16-bit integers. Six namespace components + // retain 96 UUID bits; then two invocation and two chosen-reco-ID parts. + const AxisSpec key16{65536, -0.5, 65535.5}; + registry.add({"Association/EventTrackKeysBeforeDCA", + ";#eta;z_{chosen}^{reco};DCAxy;DCAz;track category;association (0:right,1:wrong,2:unclassified);namespace0;namespace1;namespace2;namespace3;namespace4;namespace5;callLow;callHigh;recoLow;recoHigh", + {HistType::kTHnSparseD, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}, {3, -0.5, 2.5}, key16, key16, key16, key16, key16, key16, key16, key16, key16, key16}}}); + registry.add({"Association/hNumRecoEventsWithRightTracks", ";Selected reco collisions with right-associated tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Association/hNumRecoEventsWithWrongTracks", ";Selected reco collisions with wrong-associated tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Association/RecoEventVzWithRightTracks", ";z_{chosen}^{reco} (cm);Selected reco collisions", {HistType::kTH1D, {zAxis}}}); + registry.add({"Association/RecoEventVzWithWrongTracks", ";z_{chosen}^{reco} (cm);Selected reco collisions", {HistType::kTH1D, {zAxis}}}); + registry.add({"OfflineVariations/RecoTrackSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + registry.add({"OfflineVariations/RecoStrangeSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + } + registry.add({"OfflineVariations/SelectedRecoEventVertexResponse", + ";z_{vtx}^{true} (cm);z_{vtx}^{reco} (cm);selected reco collisions", + {HistType::kTHnSparseD, {zAxis, zAxis}}}); + registry.add({"OfflineVariations/GenPrimaryEtaTrueZRecoZ", + ";#eta_{gen};z_{vtx}^{true} (cm);z_{vtx}^{reco} (cm)", + {HistType::kTHnSparseD, {etaBinning, zAxis, zAxis}}}); registry.add({"EventsRecoCuts_GenReco", ";cut;events", {HistType::kTH1F, {{16, 0.5, 16.5}}}}); @@ -452,8 +545,12 @@ struct PseudorapidityDensityMFT { auto h = registry.get(HIST("EventsRecoCuts_GenReco")); auto* x = h->GetXaxis(); x->SetBinLabel(static_cast(GenRecoCutBin::AllRecoCollisions), "All reco collisions (loop entry)"); + x->SetBinLabel(static_cast(GenRecoCutBin::RctMFT), "myChecker (cfg)"); + x->SetBinLabel(static_cast(GenRecoCutBin::UseGoodItsLayersAll), "kIsGoodITSLayersAll"); x->SetBinLabel(static_cast(GenRecoCutBin::UseContBestCollisionIndex), "useContBestcollisionIndex"); x->SetBinLabel(static_cast(GenRecoCutBin::HasMcCollision), "has_mcCollision()"); + x->SetBinLabel(static_cast(GenRecoCutBin::VzWindow), "Vz window"); + x->SetBinLabel(static_cast(GenRecoCutBin::InelGt0), "INEL>0 (midtracks>0)"); x->SetBinLabel(static_cast(GenRecoCutBin::IsTriggerTVX), "kIsTriggerTVX (if useEvSel)"); x->SetBinLabel(static_cast(GenRecoCutBin::NoTimeFrameBorder), "kNoTimeFrameBorder (if useEvSel)"); x->SetBinLabel(static_cast(GenRecoCutBin::NoITSROFrameBorder), "kNoITSROFrameBorder (if useEvSel)"); @@ -464,9 +561,7 @@ struct PseudorapidityDensityMFT { x->SetBinLabel(static_cast(GenRecoCutBin::NoCollInTimeRangeStandard), "kNoCollInTimeRangeStandard (cfg)"); x->SetBinLabel(static_cast(GenRecoCutBin::NoCollInTimeRangeStrict), "kNoCollInTimeRangeStrict (cfg)"); x->SetBinLabel(static_cast(GenRecoCutBin::NoHighMultCollInPrevRof), "kNoHighMultCollInPrevRof (cfg)"); - x->SetBinLabel(static_cast(GenRecoCutBin::RctMFT), "myChecker (cfg)"); - x->SetBinLabel(static_cast(GenRecoCutBin::VzWindow), "Vz window"); - x->SetBinLabel(static_cast(GenRecoCutBin::InelGt0), "INEL>0 (midtracks>0)"); + // x->SetBinLabel(11, "rctChecker (cfg)"); // x->SetBinLabel(15, "Passed all event cuts"); @@ -500,7 +595,15 @@ struct PseudorapidityDensityMFT { h->GetXaxis()->SetBinLabel(static_cast(WrongVertexRecoExistsBin::RecoOfTrueMissing), "Reco of true missing"); } } - + registry.add({"Tracks/ReAssignedTracks", + ";status;Counts", + {HistType::kTH1F, {{1, 0.5, 1.5}}}}); + registry.add({"Tracks/NotReAssignedTracks", + ";status;Counts", + {HistType::kTH1F, {{1, 0.5, 1.5}}}}); + registry.add({"Tracks/NotAssignedTracks", + ";status;Counts", + {HistType::kTH1F, {{1, 0.5, 1.5}}}}); registry.add({"Purity/HashTableRowCounts", ";status;counts", {HistType::kTH1F, {{3, 0.5, 3.5}}}}); @@ -562,27 +665,28 @@ struct PseudorapidityDensityMFT { registry.add({"Purity/reco/PNchMFT_afterCuts", ";N_{trk}^{MFT} (selected);events", {HistType::kTH1F, {multAxis}}}); + // MC P(Nch) objects for MFT multiplicity unfolding/correction. + // Generator multiplicity: primary charged particles in the MFT acceptance, + // with generated INEL>0 defined from the central estimator. + // Reco multiplicity: selected reassociated MFT tracks for matched accepted reco events. + registry.add({"PNchMC/gen_inelgt0", + ";N_{ch}^{gen,MFT};events", + {HistType::kTH1F, {multAxisGenMFT}}}); + + registry.add({"PNchMC/reco_sel8_inelgt0", + ";N_{ch}^{reco,MFT};events", + {HistType::kTH1F, {multAxisRecoMFT}}}); + + registry.add({"PNchMC/responseMatrix", + ";N_{ch}^{reco,MFT};N_{ch}^{gen,MFT};events", + {HistType::kTH2F, {multAxisRecoMFT, multAxisGenMFT}}}); + registry.add({"Purity/DCAyVsDCAx_Right", ";DCA_{x} (cm);DCA_{y} (cm)", {HistType::kTH2F, {dcaXAxis, dcaYAxis}}}); - registry.add({"Purity/reco/woOrp/All", - ";bin;counts", - {HistType::kTH1F, {{1, 0.5, 1.5}}}}); - registry.add({"Purity/reco/woOrp/AllEta", - ";#eta;counts", - {HistType::kTH1F, {etaBinning}}}); - registry.add({"Purity/SelectedAfterDCAxy/PrimaryAll", - ";bin;counts", - {HistType::kTH1F, {{1, 0.5, 1.5}}}}); - registry.add({"Purity/SelectedAfterDCAxy/PrimaryAllEta", - ";#eta;counts", - {HistType::kTH1F, {etaBinning}}}); registry.add({"TracksToPartPhiEta", "; #varphi; #eta; tracks", {HistType::kTH2F, {phiAxis, etaBinning}}}); - registry.add({"EventsReco", - "; status; events", - {HistType::kTH1F, {{7, 0.5, 7.5}}}}); // Additional histograms for processGenReco (w/o orphan), grouped under Purity/reco/ registry.add({"Purity/reco/weakStrange/SelectedTracksEta", @@ -591,9 +695,38 @@ struct PseudorapidityDensityMFT { registry.add({"Purity/reco/weakStrange/SelectedTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); selected reco tracks from weak strange decays", {HistType::kTH2F, {etaBinning, zAxis}}}); - registry.add({"Purity/reco/SelectedTracksEta", - "; #eta; selected reco tracks", - {HistType::kTH1F, {etaBinning}}}); + // Selected non-fake secondary tracks with a recognised immediate + // strange mother. Parent species: 1 = K0S, 2 = Lambda, 3 = anti-Lambda. + registry.add({"Strangeness/Fwd/RecoStrangeEtaZvtx", + ";#eta_{reco};z_{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);parent species", + {HistType::kTHnSparseF, + {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {3, 0.5, 3.5}}}}); + { + auto histogram = registry.get(HIST("Strangeness/Fwd/RecoStrangeEtaZvtx")); + auto* parentAxis = histogram->GetAxis(4); + parentAxis->SetBinLabel(1, "K0S"); + parentAxis->SetBinLabel(2, "Lambda"); + parentAxis->SetBinLabel(3, "anti-Lambda"); + } + // Central eta has its own axis: the MFT eta binning is forward-only. + const AxisSpec midEtaAxis = { + 200, -static_cast(estimatorEta.value), + static_cast(estimatorEta.value)}; + registry.add({"Strangeness/Mid/RecoAllEtaZvtx", + ";#eta_{reco};z_{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm)", + {HistType::kTHnSparseF, + {midEtaAxis, zAxis, dcaXyAxis, dcaZAxis}}}); + registry.add({"Strangeness/Mid/RecoStrangeEtaZvtx", + ";#eta_{reco};z_{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);parent species", + {HistType::kTHnSparseF, + {midEtaAxis, zAxis, dcaXyAxis, dcaZAxis, {3, 0.5, 3.5}}}}); + { + auto histogram = registry.get(HIST("Strangeness/Mid/RecoStrangeEtaZvtx")); + auto* parentAxis = histogram->GetAxis(4); + parentAxis->SetBinLabel(1, "K0S"); + parentAxis->SetBinLabel(2, "Lambda"); + parentAxis->SetBinLabel(3, "anti-Lambda"); + } registry.add({"Purity/reco/woOrp/nTrk", " ; N_{Trk}^{all}", {HistType::kTH1F, {{701, -0.5, 700.5}}}}); @@ -719,33 +852,6 @@ struct PseudorapidityDensityMFT { registry.add({"Purity/mc/PrimaryTracksPhiEta", "; #varphi; #eta; tracks", {HistType::kTH2F, {phiAxis, etaBinning}}}); - // --- MC-side (NO-DCA) truth histograms for pre-DCA accounting --- - registry.add({"Purity/mc_noDCA/PrimaryAll", - ";bin;counts", - {HistType::kTH1F, {{1, 0.5, 1.5}}}}); - registry.add({"Purity/mc_noDCA/PrimaryAllEta", - ";#eta;counts", - {HistType::kTH1F, {etaBinning}}}); - registry.add({"Purity/mc_noDCA/PrimaryTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); tracks", - {HistType::kTH2F, {etaBinning, zAxis}}}); - registry.add({"Purity/mc_noDCA/PrimaryTracksEtaZvtx_gt0", - "; #eta; #it{z}_{vtx} (cm); tracks", - {HistType::kTH2F, {etaBinning, zAxis}}}); - registry.add({"Purity/mc_noDCA/PrimaryTracksPtZvtx_gt0", - "; p_{T} (GeV/c); #it{z}_{vtx} (cm); primary tracks", - {HistType::kTH2F, {ptAxis, zAxis}}}); - registry.add({"Purity/mc_noDCA/PrimaryTracksPhiEta", - "; #varphi; #eta; tracks", - {HistType::kTH2F, {phiAxis, etaBinning}}}); - - // Book keeping for pre-DCA selection counters (for symmetry with AfterDCAxy) - registry.add({"Purity/SelectedBeforeDCAxy/PrimaryAll", - ";bin;counts", - {HistType::kTH1F, {{1, 0.5, 1.5}}}}); - registry.add({"Purity/SelectedBeforeDCAxy/PrimaryAllEta", - ";#eta;counts", - {HistType::kTH1F, {etaBinning}}}); // --- Fake-track counters (reco side after DCA selections) --- registry.add({"Purity/Fakes/All", ";bin;counts", @@ -761,13 +867,6 @@ struct PseudorapidityDensityMFT { {HistType::kTH2F, {phiAxis, etaBinning}}}); // Fake reason breakdown: 1-INEL>0 true (vz), 2-Reco sel (woOrp), 3-has_mcParticle, 4-isPrimary, 5-rightVertex - // --- Purity calculation histograms (as profiles: purity = ) --- - registry.add({"Purity/PurityOverall", - ";bin;purity", - {HistType::kTProfile, {{1, 0.5, 1.5}}}}); - registry.add({"Purity/PurityVsEta", - ";#eta;purity", - {HistType::kTProfile, {etaBinning}}}); registry.add({"RightWrong", ";category;counts", {HistType::kTH1F, {{4, 0.5, 4.5}}}}); @@ -795,6 +894,20 @@ struct PseudorapidityDensityMFT { hrw1->GetXaxis()->SetBinLabel(static_cast(RightWrongBin::Neither), "neither"); hrw1->GetXaxis()->SetBinLabel(static_cast(RightWrongBin::Both), "both"); + registry.add({"ReassignedTracksEtaZvtx", + "; #eta; #it{z}_{vtx} (cm); tracks", + {HistType::kTH2F, {etaBinning, zAxis}}}); + registry.add({"ReassignedVertexCorr", + "; #it{z}_{vtx}^{orig} (cm); #it{z}_{vtx}^{re} (cm)", + {HistType::kTH2F, {zAxis, zAxis}}}); + + registry.add({"notReassignedTracksEtaZvtx", + "; #eta; #it{z}_{vtx} (cm); tracks", + {HistType::kTH2F, {etaBinning, zAxis}}}); + registry.add({"notReassignedVertexCorr", + "; #it{z}_{vtx}^{orig} (cm); #it{z}_{vtx}^{re} (cm)", + {HistType::kTH2F, {zAxis, zAxis}}}); + // Vertex-position difference for wrong-vertex associations (reco - true MC) registry.add({"deltaVZ_fromReco", ";#Delta z_{vtx}^{reco-true} (cm);tracks", @@ -874,6 +987,14 @@ struct PseudorapidityDensityMFT { {HistType::kTHnSparseF, {etaBinning, dcaXyAxis, dcaZAxis, dcaXAxis, dcaYAxis}}}); + // Matched reconstructed physical primaries after all event and track + // selections, binned in generated eta for an efficiency-vs-eta + // numerator without reconstructed-eta migration. + registry.add({"Purity/RecoSparsePrimaryTruthEta", + ";#eta_{true};DCA_{xy}^{reco} (cm);DCA_{z}^{reco} (cm)", + {HistType::kTHnSparseF, + {etaBinning, dcaXyAxis, dcaZAxis}}}); + registry.add({"Purity/RecoSparseSecondary", ";#eta;DCA_{xy} (cm);DCA_{z} (cm);DCA_{x} (cm);DCA_{y} (cm)", {HistType::kTHnSparseF, @@ -981,6 +1102,14 @@ struct PseudorapidityDensityMFT { {HistType::kTHnSparseF, {dcaXyAxis, dcaZAxis, dcaXAxis, dcaYAxis}}}); + // Category-specific selected reconstructed-collision counts (not generated events). + registry.add({"Purity/hNumCollisionsNonAmb_InelMFT", + ";Selected reco collisions with nonambiguous tracks;Count", + {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Purity/hNumCollisionsAmb_InelMFT", + ";Selected reco collisions with ambiguous tracks;Count", + {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"Purity/RecoSparseAll_EventCount", ";events;counts", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); @@ -1000,20 +1129,34 @@ struct PseudorapidityDensityMFT { ";events;counts", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); } - - if (doprocessGen) { + if (doprocessGen || doprocessGenReco3d || doprocessGenReco2d) { registry.add({"EventsNtrkZvtxGen", "; N_{trk}; #it{z}_{vtx} (cm); events", {HistType::kTH2F, {multAxis, zAxis}}}); registry.add({"EventsNtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); events", {HistType::kTH2F, {multAxis, zAxis}}}); - registry.add({"EventsNtrkZvtxGen_gt0", + registry.add({"EventsNtrkZvtxGen_gt0t", "; N_{trk}; #it{z}_{vtx} (cm); events", {HistType::kTH2F, {multAxis, zAxis}}}); - registry.add({"EventsNtrkZvtxGen_gt0t", + registry.add({"EventsNtrkZvtxGen_gt0", "; N_{trk}; #it{z}_{vtx} (cm); events", {HistType::kTH2F, {multAxis, zAxis}}}); + registry.add({"EventsZposDiff", + " ; Z_{rec} - Z_{gen} (cm)", + {HistType::kTH1F, {deltazAxis}}}); + registry.add({"TracksEtaZvtxGen_gt0_primary", + "; #eta; #it{z}_{vtx} (cm); primary tracks", + {HistType::kTH2F, {etaBinning, zAxis}}}); + registry.add({"TracksEtaZvtxGen_gt0t_primary", + "; #eta; #it{z}_{vtx} (cm); primary tracks from true INEL>0 events", + {HistType::kTH2F, {etaBinning, zAxis}}}); + registry.add({"TracksPhiEtaGen_gt0_primary", + "; #varphi; #eta; primary tracks", + {HistType::kTH2F, {phiAxis, etaBinning}}}); + registry.add({"TracksPtZvtxGen_gt0_primary", + "; p_{T} (GeV/c); #it{z}_{vtx} (cm); primary tracks", + {HistType::kTH2F, {ptAxis, zAxis}}}); registry.add({"TracksEtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); tracks", {HistType::kTH2F, {etaBinning, zAxis}}}); @@ -1026,54 +1169,62 @@ struct PseudorapidityDensityMFT { registry.add({"TracksEtaZvtxGen_gt0t", "; #eta; #it{z}_{vtx} (cm); tracks", {HistType::kTH2F, {etaBinning, zAxis}}}); - registry.add({"TracksPhiEtaGen", + registry.add({"TracksPtEtaGen_t", + " ; p_{T} (GeV/c); #eta", + {HistType::kTH2F, {ptAxis, etaBinning}}}); + registry.add({"TracksPhiEtaGen_gt0t", "; #varphi; #eta; tracks", {HistType::kTH2F, {phiAxis, etaBinning}}}); - registry.add({"TracksPhiEtaGen_gt0", + registry.add({"TracksPtEtaGen", + " ; p_{T} (GeV/c); #eta", + {HistType::kTH2F, {ptAxis, etaBinning}}}); + registry.add({"TracksPhiEtaGen", "; #varphi; #eta; tracks", {HistType::kTH2F, {phiAxis, etaBinning}}}); - registry.add({"TracksPhiEtaGen_gt0t", + registry.add({"TracksPhiEtaGen_gt0", "; #varphi; #eta; tracks", {HistType::kTH2F, {phiAxis, etaBinning}}}); registry.add({"TracksPhiZvtxGen", "; #varphi; #it{z}_{vtx} (cm); tracks", {HistType::kTH2F, {phiAxis, zAxis}}}); // + registry.add({"EventEfficiencymc", + ";status;centrality;events", + {HistType::kTH1F, {{5, 0.5, 5.5}}}}); + auto heff = registry.get(HIST("EventEfficiencymc")); + auto* eventEfficiencyAxis = heff->GetXaxis(); + eventEfficiencyAxis->SetBinLabel(static_cast(EventEfficiencyBin::Generated), "Generated"); + eventEfficiencyAxis->SetBinLabel(static_cast(EventEfficiencyBin::GeneratedInelGt0), "Generated INEL>0"); + eventEfficiencyAxis->SetBinLabel(static_cast(EventEfficiencyBin::Reconstructed), "Reconstructed"); + eventEfficiencyAxis->SetBinLabel(static_cast(EventEfficiencyBin::Selected), "Selected"); + eventEfficiencyAxis->SetBinLabel(static_cast(EventEfficiencyBin::SelectedInelGt0), "Selected INEL>0"); + } + + if (doprocessGen) { + + registry.add({"EventsNtrkZvtxGen_t", + "; N_{trk}; #it{z}_{vtx} (cm); events", + {HistType::kTH2F, {multAxis, zAxis}}}); registry.add({"TracksToPartPtEta", " ; p_{T} (GeV/c); #eta", {HistType::kTH2F, {ptAxis, etaBinning}}}); // - registry.add({"TracksPtEtaGen", - " ; p_{T} (GeV/c); #eta", - {HistType::kTH2F, {ptAxis, etaBinning}}}); - registry.add({"TracksPtEtaGen_t", - " ; p_{T} (GeV/c); #eta", - {HistType::kTH2F, {ptAxis, etaBinning}}}); registry.add({"NotFoundEventZvtx", " ; #it{z}_{vtx} (cm)", {HistType::kTH1F, {zAxis}}}); - registry.add({"EventsZposDiff", - " ; Z_{rec} - Z_{gen} (cm)", - {HistType::kTH1F, {deltazAxis}}}); - registry.add({"TracksEtaZvtxGen_gt0_primary", - "; #eta; #it{z}_{vtx} (cm); primary tracks", - {HistType::kTH2F, {etaBinning, zAxis}}}); - registry.add({"TracksPhiEtaGen_gt0_primary", - "; #varphi; #eta; primary tracks", - {HistType::kTH2F, {phiAxis, etaBinning}}}); - registry.add({"TracksPtZvtxGen_gt0_primary", - "; p_{T} (GeV/c); #it{z}_{vtx} (cm); primary tracks", - {HistType::kTH2F, {ptAxis, zAxis}}}); registry.add({"EventsSplitMult", " ; N_{gen}", {HistType::kTH1F, {multAxis}}}); - auto heff = registry.get(HIST("EventEfficiency")); - x = heff->GetXaxis(); - x->SetBinLabel(static_cast(EventEfficiencyBin::Generated), "Generated"); - x->SetBinLabel(static_cast(EventEfficiencyBin::GeneratedInelGt0), "Generated INEL>0"); - x->SetBinLabel(static_cast(EventEfficiencyBin::Reconstructed), "Reconstructed"); - x->SetBinLabel(static_cast(EventEfficiencyBin::Selected), "Selected"); - x->SetBinLabel(static_cast(EventEfficiencyBin::SelectedInelGt0), "Selected INEL>0"); } if (doprocessMultReassoc || doprocessMultReassoc3d) { + // Offline DCA variations need tracks before either DCA rejection. + // Only the 3D process supplies a measured bestDCAZ; do not fill fake zeros + // for the 2D process. Category 0 = nonambiguous, 1 = ambiguous; no orphans. + if (doprocessMultReassoc3d) { + registry.add({"Tracks/2Danalysis/DataTrackSparseBeforeDCA", + ";#eta;z_{vtx}^{reco} (cm);DCA_{xy} (cm);DCA_{z} (cm);track category (0: nonamb, 1: amb)", + {HistType::kTHnSparseF, + {etaBinning, zAxis, dcaXyAxis, dcaZAxis, {2, -0.5, 1.5}}}}); + } + // Purity denominator histograms (reco side after DCAxy selection) registry.add({"Tracks/Control/DeltaZ", @@ -1123,6 +1274,27 @@ struct PseudorapidityDensityMFT { " ; N_{Trk}^{nonamb}", {HistType::kTH1F, {{701, -0.5, 700.5}}}}); // + // Event-level P(Nch) distributions for reassociated MFT tracks. + // Fill once per collision after counting selected reassociated MFT tracks. + registry.add({"PNch/MFT_sel8", + ";N_{ch}^{MFT};events", + {HistType::kTH1F, {multAxis}}}); + registry.add({"PNch/MFT_sel8_inelgt0", + ";N_{ch}^{MFT};events", + {HistType::kTH1F, {multAxis}}}); + registry.add({"PNch/MFT_sel8_inelfwdgt0", + ";N_{ch}^{MFT};events", + {HistType::kTH1F, {multAxis}}}); + registry.add({"PNch/MFT_sel8_inelgt0_nonamb", + ";N_{ch}^{MFT, nonamb};events", + {HistType::kTH1F, {multAxis}}}); + registry.add({"PNch/MFT_sel8_inelgt0_amb", + ";N_{ch}^{MFT, amb};events", + {HistType::kTH1F, {multAxis}}}); + registry.add({"PNch/MFTZvtx_sel8_inelgt0", + ";N_{ch}^{MFT};#it{z}_{vtx} (cm);events", + {HistType::kTH2F, {multAxis, zAxis}}}); + registry.add({"Tracks/Control/amb/AmbTracksPhiEta", "; #varphi; #eta; tracks", {HistType::kTH2F, {phiAxis, etaBinning}}}); // @@ -1204,8 +1376,8 @@ struct PseudorapidityDensityMFT { // {HistType::kTH1F,{{100000, 0.5, 100000.0}}}}); // {HistType::kTH1F, {{100000, -50000.0, 50000.0}}}}); // registry.add({"NonambEventCounts", " ; EventCounts Nonamb", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // - registry.add({"hNumCollisionsNonAmb_InelMFT", " ; Number of Collisions with Non-Ambiguous Tracks;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // - registry.add({"hNumCollisionsAmb_InelMFT", " ; Number of Collisions with Non-Ambiguous Tracks;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // + registry.add({"hNumCollisionsNonAmb_InelMFT", ";Selected collisions with nonambiguous tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); + registry.add({"hNumCollisionsAmb_InelMFT", ";Selected collisions with ambiguous tracks;Count", {HistType::kTH1D, {{1, 0.5, 1.5}}}}); registry.add({"hNumCollisions_InelMFT", " ; Number of selected events with Inel>0 and MFT>0;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // registry.add({"hNumCollisions_Inel", " ; Number of selected events with Inel>0;Count;Frequency", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // registry.add({"ambEventCounts", " ; EventCounts Nonamb", {HistType::kTH1F, {{1, 0.5, 1.5}}}}); // @@ -1296,8 +1468,8 @@ struct PseudorapidityDensityMFT { std::vector::iterator> cols; for (const auto& bc : bcs) { if (!useEvSel || - (useEvSel && ((bc.selection_bit(aod::evsel::kIsBBT0A) && - bc.selection_bit(aod::evsel::kIsBBT0C)) != 0))) { + (bc.selection_bit(aod::evsel::kIsBBT0A) && + bc.selection_bit(aod::evsel::kIsBBT0C))) { registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::GoodBCs)); cols.clear(); for (const auto& collision : collisions) { @@ -1381,13 +1553,19 @@ struct PseudorapidityDensityMFT { soa::Join>; // central tracks for INEL>0 + // MC-only input for the central contamination study. Keep FiCentralTracks + // unchanged so data and reconstructed INEL>0 selections are unaffected. + using CentralTracksMC = soa::Join< + aod::Tracks, aod::TracksExtra, aod::TrackSelection, + aod::TracksDCA, aod::McTrackLabels>; + void processMult(CollwEv::iterator const& collision, aod::MFTTracks const& tracks, FiCentralTracks const& midtracks, aod::Tracks const&) { registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::All)); - if (!useEvSel || (useEvSel && collision.sel8())) { + if (!useEvSel || collision.sel8()) { registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::VzSelected)); auto z = collision.posZ(); auto perCollisionSample = sampleCentral->sliceByCached( @@ -1413,8 +1591,9 @@ struct PseudorapidityDensityMFT { ((phi > o2::constants::math::PI - cfgPhiCut) && (phi < o2::constants::math::PI + cfgPhiCut)) || (phi > o2::constants::math::TwoPI - cfgPhiCut) || ((phi > ((o2::constants::math::PIHalf - 0.1) * o2::constants::math::PI) - cfgPhiCut) && - (phi < ((o2::constants::math::PIHalf - 0.1) * o2::constants::math::PI) + cfgPhiCut))) + (phi < ((o2::constants::math::PIHalf - 0.1) * o2::constants::math::PI) + cfgPhiCut))) { continue; + } } registry.fill(HIST("TracksEtaZvtx"), track.eta(), z); @@ -1444,267 +1623,295 @@ struct PseudorapidityDensityMFT { RetracksT const& retracks, FiCentralTracks const& midtracks, aod::Tracks const&) { - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::All)); + std::unordered_set uniqueEvents; + std::unordered_set uniqueEventsAmb; + std::unordered_set eventsInelMFT; + std::unordered_set eventsInel; + + int64_t nMFTSelected{0}; + int64_t nMFTSelectedAmb{0}; + int64_t nMFTSelectedNonAmb{0}; + + const auto fillDataCut = [&](DataCutBin bin) { + registry.fill(HIST("EventSelectionData"), static_cast(bin)); + }; + fillDataCut(DataCutBin::All); + + // registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::All)); auto perCollisionSample = sampleCentral->sliceByCached( o2::aod::track::collisionId, collision.globalIndex(), cache); auto nTrk = perCollisionSample.size(); auto z = collision.posZ(); registry.fill(HIST("EventsNtrkZvtx"), nTrk, z); - if ((z >= cfgVzCut1) && (z <= cfgVzCut2)) { - registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_all"), nTrk, z); - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::Vz)); - for (const auto& retrack : retracks) { - auto track = retrack.mfttrack(); - float ndf = getTrackNdf(track); - float chi2ndf = track.chi2() / ndf; - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (usePhiCut) { - if ((phi <= PhiVetoLow) || - ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || - (phi >= PhiVetoHigh)) { - continue; - } - } - float dcaXyCut = retrack.bestDCAXY(); - if (useDCAxyCut) { - if (dcaXyCut > maxDCAxy) - continue; - } - if constexpr (std::is_same_v>) { - float dcaZCut = retrack.bestDCAZ(); - if (useDCAzCut) { - if (dcaZCut > maxDCAz) - continue; - } - } - if ((cfgnEta1 < track.eta()) && (track.eta() < cfgnEta2) && track.nClusters() >= cfgnCluster && retrack.ambDegree() > 0 && chi2ndf < cfgChi2NDFMax && (phi > cfgPhiCut1 && phi < cfgPhiCut2)) { - registry.fill(HIST("Tracks/2Danalysis/EtaZvtx"), track.eta(), z); - } - } - if (!disableITSROFCut && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - return; - } - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::VzItsRof)); - if (!useEvSel || (useEvSel && collision.selection_bit(aod::evsel::kIsTriggerTVX) && collision.selection_bit(aod::evsel::kNoTimeFrameBorder) && collision.selection_bit(aod::evsel::kNoSameBunchPileup))) { - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::VzSelected)); - registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_sel8"), nTrk, z); - std::unordered_set uniqueEvents; - std::unordered_set uniqueEventsAmb; - std::unordered_set uniqueCollisions; - std::unordered_set uniqueCollisionsAmb; - std::unordered_set eventsInelMFT; - std::unordered_set eventsInel; - if (midtracks.size() > 0) { - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::Sel8VzInelGt0)); - registry.fill(HIST("EventsNtrkZvtx_gt0"), nTrk, z); - registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_sel8_inelgt0"), nTrk, z); - eventsInel.insert(collision.globalIndex()); - } - if (perCollisionSample.size() > 0) { - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::PerCollisionSampleGt0)); - } - if (midtracks.size() > 0 && perCollisionSample.size() > 0) { - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::MidtracksAndPerCollisionSampleGt0)); - } - int64_t i = 0.0, j = 0.0, k = 0.0; - for (const auto& retrack : retracks) { - auto track = retrack.mfttrack(); - float ndf = getTrackNdf(track); - float chi2ndf = track.chi2() / ndf; - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (usePhiCut) { - if ((phi <= PhiVetoLow) || - ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || - (phi >= PhiVetoHigh)) { - continue; - } - } - float dcaXyCut = retrack.bestDCAXY(); - if (useDCAxyCut) { - if (dcaXyCut > maxDCAxy) - continue; - } - if constexpr (std::is_same_v>) { - float dcaZCut = retrack.bestDCAZ(); - if (useDCAzCut) { - if (dcaZCut > maxDCAz) - continue; - } - } - if ((cfgnEta1 < track.eta()) && (track.eta() < cfgnEta2) && track.nClusters() >= cfgnCluster && retrack.ambDegree() > 0 && chi2ndf < cfgChi2NDFMax && (phi > cfgPhiCut1 && phi < cfgPhiCut2)) { - registry.fill(HIST("Tracks/Control/Chi2NDF"), chi2ndf); - registry.fill(HIST("Tracks/2Danalysis/EtaZvtx_sel8"), track.eta(), z); - if (midtracks.size() > 0 && retrack.ambDegree() > 0) { - registry.fill(HIST("Tracks/2Danalysis/EtaZvtx_sel8_inelgt0"), track.eta(), z); - } - } - } - if (retracks.size() > 0) { - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::SelInelInelFwdGt0)); - if (midtracks.size() > 0) { - registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_sel8_inelfwdgt0"), nTrk, z); - } - for (const auto& retrack : retracks) { - auto track = retrack.mfttrack(); - float ndf = getTrackNdf(track); - float chi2ndf = track.chi2() / ndf; - float phi = track.phi(); - float dcaXyCut = retrack.bestDCAXY(); - o2::math_utils::bringTo02Pi(phi); - // Declare dcaZCut only if needed below. - if ((cfgnEta1 < track.eta()) && (track.eta() < cfgnEta2) && track.nClusters() >= cfgnCluster && chi2ndf < cfgChi2NDFMax && (phi > cfgPhiCut1 && phi < cfgPhiCut2)) { - if (usePhiCut) { - if ((phi <= PhiVetoLow) || - ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || - (phi >= PhiVetoHigh)) { - continue; - } - } - if (useDCAxyCut) { - if (dcaXyCut > maxDCAxy) - continue; - } - if constexpr (std::is_same_v>) { - float dcaZCut = retrack.bestDCAZ(); - if (useDCAzCut) { - if (dcaZCut > maxDCAz) - continue; - } - } + // const auto z = collision.posZ(); + if ((z < cfgVzCut1) || (z > cfgVzCut2)) { + return; + } + // if ((z >= cfgVzCut1) && (z <= cfgVzCut2)) { + registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_all"), nTrk, z); + fillDataCut(DataCutBin::VzWindow); + // registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::Vz)); + const bool hasPerCollisionSample = perCollisionSample.size() > 0; + const bool hasMidtracks = midtracks.size() > 0; + if (midtracks.size() > 0) { + // registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::Sel8VzInelGt0)); + eventsInel.insert(collision.globalIndex()); + } + if (usePerCollisionSampleGt0Cut && !hasPerCollisionSample) { + return; + } + fillDataCut(DataCutBin::PerCollisionSampleGt0); - registry.fill(HIST("TracksEtaZvtx"), track.eta(), z); - if (midtracks.size() > 0 && retrack.ambDegree() > 0) { - registry.fill(HIST("Tracks/EtaZvtx_gt0"), track.eta(), z); - registry.fill(HIST("Tracks/2Danalysis/EtaZvtx_sel8_inelfwdgt0"), track.eta(), z); - eventsInelMFT.insert(retrack.bestCollisionId()); - } - if (retrack.ambDegree() != 0) { - registry.fill(HIST("Tracks/Control/woOrp/woOrpEtaZvtx_gt0"), track.eta(), z); - ++k; - } - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - registry.fill(HIST("TracksPhiEta"), phi, track.eta()); - registry.fill(HIST("TracksPtEta"), track.pt(), track.eta()); - if ((track.eta() < ForwardEtaMax) && (track.eta() > ForwardEtaMin)) { - registry.fill(HIST("TracksPhiZvtx"), phi, z); - } - if (track.collisionId() > InvalidCollisionId && - retrack.ambDegree() == SingleCompatibleCollision) { - registry.fill(HIST("collisionID"), track.collisionId()); - } - if (track.collisionId() > InvalidCollisionId && - retrack.ambDegree() > SingleCompatibleCollision) { - registry.fill(HIST("collisionIDamb"), track.collisionId()); - } - if (track.collisionId() != retrack.bestCollisionId()) { - registry.fill(HIST("Tracks/Control/ReassignedTracksEtaZvtx"), - track.eta(), z); - registry.fill(HIST("Tracks/Control/ReassignedTracksPhiEta"), phi, - track.eta()); - registry.fill(HIST("Tracks/Control/ReassignedVertexCorr"), - track.template collision_as().posZ(), z); - - registry.fill(HIST("Tracks/Control/DeltaZ"), - track.template collision_as().posZ() - - collision.posZ()); - } - if (track.collisionId() == retrack.bestCollisionId()) { - registry.fill(HIST("Tracks/Control/notReassignedTracksEtaZvtx"), - track.eta(), z); - registry.fill(HIST("Tracks/Control/notReassignedTracksPhiEta"), phi, - track.eta()); - registry.fill(HIST("Tracks/Control/notReassignedVertexCorr"), - track.template collision_as().posZ(), z); - } + if (useMidtracksAndPerCollisionSampleGt0Cut && !(hasMidtracks && hasPerCollisionSample)) { + return; + } + // registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::MidtracksAndPerCollisionSampleGt0)); + fillDataCut(DataCutBin::InelGt0); - registry.fill(HIST("Tracks/Control/TrackAmbDegree"), - retrack.ambDegree()); - registry.fill(HIST("Tracks/Control/DCAXY"), retrack.bestDCAXY()); - if constexpr (std::is_same_v>) { - registry.fill(HIST("Tracks/Control/DCAZ"), retrack.bestDCAZ()); - } - int isAmbiguous = 0; - - if (retrack.ambDegree() > 1 && retrack.ambDegree() != 0) { - isAmbiguous = 1; - ++i; - - registry.fill(HIST("Tracks/Control/amb/EtaZvtxAmb_gt0"), track.eta(), z); - - registry.fill(HIST("Tracks/Control/amb/AmbTracksEtaZvtx"), - track.eta(), z); - registry.fill(HIST("Tracks/Control/amb/AmbTracksPhiEta"), phi, - track.eta()); - registry.fill(HIST("Tracks/Control/amb/AmbVertexCorr"), - track.template collision_as().posZ(), z); - registry.fill(HIST("Tracks/Control/amb/DCAxy_amb"), retrack.bestDCAXY()); - if constexpr (std::is_same_v>) { - registry.fill(HIST("Tracks/Control/amb/DCAz_amb"), retrack.bestDCAZ()); - } - uniqueEventsAmb.insert(retrack.bestCollisionId()); - } - if (midtracks.size() > 0 && retrack.ambDegree() > 1 && retrack.ambDegree() != 0) { - uniqueCollisionsAmb.insert(collision.globalIndex()); - } + const auto passEventSelection = [&](auto const& collision) { + struct EvSelStep { + bool enabled{false}; + decltype(aod::evsel::kIsTriggerTVX) bit{}; + DataCutBin bin{DataCutBin::All}; + }; - registry.fill(HIST("Tracks/Control/TrackIsAmb"), isAmbiguous); - if (retrack.ambDegree() == 1 && retrack.ambDegree() != 0) { - ++j; - registry.fill(HIST("Tracks/Control/nonamb/EtaZvtxNonAmb_gt0"), track.eta(), z); - registry.fill(HIST("Tracks/Control/nonamb/nonAmbTracksEtaZvtx"), - track.eta(), z); - registry.fill(HIST("Tracks/Control/nonamb/nonAmbTracksPhiEta"), phi, - track.eta()); - registry.fill(HIST("Tracks/Control/nonamb/nonAmbVertexCorr"), - track.template collision_as().posZ(), z); - registry.fill(HIST("Tracks/Control/nonamb/DCAxy_nonamb"), retrack.bestDCAXY()); - if constexpr (std::is_same_v>) { - registry.fill(HIST("Tracks/Control/nonamb/DCAz_nonamb"), retrack.bestDCAZ()); - } - uniqueEvents.insert(retrack.bestCollisionId()); - } - if (midtracks.size() > 0 && retrack.ambDegree() == 1 && retrack.ambDegree() != 0) { - uniqueCollisions.insert(collision.globalIndex()); - } - if (retrack.ambDegree() != 0) { - registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksEtaZvtx"), - track.eta(), z); - registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksPhiEta"), phi, - track.eta()); - registry.fill(HIST("Tracks/Control/woOrp/woOrpVertexCorr"), - track.template collision_as().posZ(), z); - registry.fill(HIST("Tracks/Control/woOrp/DCAxy_woOrp"), retrack.bestDCAXY()); - if constexpr (std::is_same_v>) { - registry.fill(HIST("Tracks/Control/woOrp/DCAz_woOrp"), retrack.bestDCAZ()); - } - } - } - } - registry.fill(HIST("ambEventCounts"), 1, uniqueEventsAmb.size()); - registry.fill(HIST("NonambEventCounts"), 1, uniqueEvents.size()); - registry.fill(HIST("hNumCollisionsNonAmb_InelMFT"), 1, uniqueCollisions.size()); - registry.fill(HIST("hNumCollisionsAmb_InelMFT"), 1, uniqueCollisionsAmb.size()); - registry.fill(HIST("hNumCollisions_InelMFT"), 1, eventsInelMFT.size()); + const std::array steps = {{ + {useTriggerTVX, aod::evsel::kIsTriggerTVX, DataCutBin::IsTriggerTVX}, + {useNoTimeFrameBorderCut, aod::evsel::kNoTimeFrameBorder, DataCutBin::NoTimeFrameBorder}, + {useNoITSROFrameBorderCut, aod::evsel::kNoITSROFrameBorder, DataCutBin::NoITSROFrameBorder}, + {useNoSameBunchPileup, aod::evsel::kNoSameBunchPileup, DataCutBin::NoSameBunchPileup}, + {useGoodItsLayersAll, aod::evsel::kIsGoodITSLayersAll, DataCutBin::UseGoodItsLayersAll}, + {useGoodZvtxFT0vsPV, aod::evsel::kIsGoodZvtxFT0vsPV, DataCutBin::GoodZvtxFT0vsPV}, + {useNoCollInRofStandard, aod::evsel::kNoCollInRofStandard, DataCutBin::NoCollInRofStandard}, + {useNoCollInRofStrict, aod::evsel::kNoCollInRofStrict, DataCutBin::NoCollInRofStrict}, + {useNoCollInTimeRangeStandard, aod::evsel::kNoCollInTimeRangeStandard, DataCutBin::NoCollInTimeRangeStandard}, + {useNoCollInTimeRangeStrict, aod::evsel::kNoCollInTimeRangeStrict, DataCutBin::NoCollInTimeRangeStrict}, + {useNoHighMultCollInPrevRof, aod::evsel::kNoHighMultCollInPrevRof, DataCutBin::NoHighMultCollInPrevRof}, + }}; + for (const auto& step : steps) { + if (!step.enabled) { + fillDataCut(step.bin); + continue; + } + + if (!collision.selection_bit(step.bit)) { + return false; } - registry.fill(HIST("Tracks/Control/amb/nTrkAmb"), i); - registry.fill(HIST("Tracks/Control/nonamb/nTrkNonAmb"), j); - registry.fill(HIST("Tracks/Control/woOrp/nTrk"), k); - registry.fill(HIST("hNumCollisions_Inel"), 1, eventsInel.size()); + fillDataCut(step.bin); } - } else { - registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::Rejected)); + + return true; + }; + if (!passEventSelection(collision)) { + return; } - } - void processMultReassoc(CollwEv::iterator const& collision, - o2::aod::MFTTracks const& mft, - soa::SmallGroups const& retracks, - FiCentralTracks const& midtracks, aod::Tracks const& trk) - { - processMultReassocCommon(collision, mft, retracks, midtracks, trk); + // Selected reconstructed INEL>0 events. Keep this event population aligned + // with Tracks/2Danalysis/EtaZvtx_sel8_inelgt0, which is filled below only + // for events with selected central-barrel tracks. + if (hasMidtracks) { + registry.fill(HIST("EventsNtrkZvtx_gt0"), nTrk, z); + registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_sel8_inelgt0"), nTrk, z); + } + + // registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::VzItsRof)); + // registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::VzSelected)); + registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_sel8"), nTrk, z); + + int64_t i = 0.0, j = 0.0, k = 0.0; + int isAmbiguous = 0; + + for (const auto& retrack : retracks) { + auto track = retrack.mfttrack(); + const float ndf = getTrackNdf(track); + const float chi2ndf = track.chi2() / ndf; + float phi = track.phi(); + float ptCut = track.pt(); + o2::math_utils::bringTo02Pi(phi); + const float dcaXyCut = retrack.bestDCAXY(); + + const bool failTrackQualityCuts = + track.nClusters() < cfgnCluster || + track.eta() <= cfgnEta1 || + track.eta() >= cfgnEta2 || + retrack.ambDegree() <= 0 || + chi2ndf >= cfgChi2NDFMax || + phi <= cfgPhiCut1 || + phi >= cfgPhiCut2 || + (usePhiCut && + ((phi <= PhiVetoLow) || + ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || + (phi >= PhiVetoHigh))) || + (usePtCut && ptCut > cfgnPt); + + if (failTrackQualityCuts) { + continue; + } + + // Selected INEL>0 events and quality-selected non-orphan tracks, before + // both DCA cuts. This keeps the existing post-DCA spectra unchanged. + if constexpr (std::is_same_v>) { + if (hasMidtracks) { + const int category = retrack.ambDegree() == SingleCompatibleCollision ? 0 : 1; + registry.fill(HIST("Tracks/2Danalysis/DataTrackSparseBeforeDCA"), + track.eta(), z, dcaXyCut, retrack.bestDCAZ(), category); + } + } + + bool failTrackCuts = useDCAxyCut && std::abs(dcaXyCut) > maxDCAxy; + if constexpr (std::is_same_v>) { + const float dcaZCut = retrack.bestDCAZ(); + failTrackCuts = failTrackCuts || (useDCAzCut && std::abs(dcaZCut) > maxDCAz); + } + + if (failTrackCuts) { + continue; + } + ++nMFTSelected; + if (retrack.ambDegree() > SingleCompatibleCollision) { + ++nMFTSelectedAmb; + } else if (retrack.ambDegree() == SingleCompatibleCollision) { + ++nMFTSelectedNonAmb; + } + + registry.fill(HIST("Tracks/Control/TrackAmbDegree"), + retrack.ambDegree()); + registry.fill(HIST("Tracks/Control/DCAXY"), retrack.bestDCAXY()); + if constexpr (std::is_same_v>) { + registry.fill(HIST("Tracks/Control/DCAZ"), retrack.bestDCAZ()); + } + + registry.fill(HIST("Tracks/2Danalysis/EtaZvtx"), track.eta(), z); + registry.fill(HIST("TracksEtaZvtx"), track.eta(), z); + registry.fill(HIST("Tracks/Control/Chi2NDF"), chi2ndf); + registry.fill(HIST("Tracks/2Danalysis/EtaZvtx_sel8"), track.eta(), z); + if (midtracks.size() > 0) { + registry.fill(HIST("Tracks/2Danalysis/EtaZvtx_sel8_inelgt0"), track.eta(), z); + registry.fill(HIST("Tracks/EtaZvtx_gt0"), track.eta(), z); + registry.fill(HIST("Tracks/2Danalysis/EtaZvtx_sel8_inelfwdgt0"), track.eta(), z); + eventsInelMFT.insert(retrack.bestCollisionId()); + } + if (retracks.size() > 0) { + // registry.fill(HIST("EventSelection"), static_cast(EventSelectionBin::SelInelInelFwdGt0)); + if (midtracks.size() > 0) { + registry.fill(HIST("Tracks/2Danalysis/EventsNtrkZvtx_sel8_inelfwdgt0"), nTrk, z); + } + } + registry.fill(HIST("Tracks/Control/woOrp/woOrpEtaZvtx_gt0"), track.eta(), z); + ++k; + registry.fill(HIST("TracksPhiEta"), phi, track.eta()); + registry.fill(HIST("TracksPtEta"), track.pt(), track.eta()); + registry.fill(HIST("TracksPhiZvtx"), phi, z); + + if (retrack.ambDegree() == SingleCompatibleCollision) { + registry.fill(HIST("collisionID"), track.collisionId()); + } + if (retrack.ambDegree() > SingleCompatibleCollision) { + registry.fill(HIST("collisionIDamb"), track.collisionId()); + } + if (track.collisionId() != retrack.bestCollisionId()) { + registry.fill(HIST("Tracks/Control/ReassignedTracksEtaZvtx"), + track.eta(), z); + registry.fill(HIST("Tracks/Control/ReassignedTracksPhiEta"), phi, + track.eta()); + registry.fill(HIST("Tracks/Control/ReassignedVertexCorr"), + track.template collision_as().posZ(), z); + + registry.fill(HIST("Tracks/Control/DeltaZ"), + track.template collision_as().posZ() - + collision.posZ()); + } + if (track.collisionId() == retrack.bestCollisionId()) { + registry.fill(HIST("Tracks/Control/notReassignedTracksEtaZvtx"), + track.eta(), z); + registry.fill(HIST("Tracks/Control/notReassignedTracksPhiEta"), phi, + track.eta()); + registry.fill(HIST("Tracks/Control/notReassignedVertexCorr"), + track.template collision_as().posZ(), z); + } + + if (retrack.ambDegree() > 1) { + isAmbiguous = 1; + ++i; + registry.fill(HIST("Tracks/Control/amb/EtaZvtxAmb_gt0"), track.eta(), z); + + registry.fill(HIST("Tracks/Control/amb/AmbTracksEtaZvtx"), + track.eta(), z); + registry.fill(HIST("Tracks/Control/amb/AmbTracksPhiEta"), phi, + track.eta()); + registry.fill(HIST("Tracks/Control/amb/AmbVertexCorr"), + track.template collision_as().posZ(), z); + registry.fill(HIST("Tracks/Control/amb/DCAxy_amb"), retrack.bestDCAXY()); + if constexpr (std::is_same_v>) { + registry.fill(HIST("Tracks/Control/amb/DCAz_amb"), retrack.bestDCAZ()); + } + uniqueEventsAmb.insert(retrack.bestCollisionId()); + } + registry.fill(HIST("Tracks/Control/TrackIsAmb"), isAmbiguous); + if (retrack.ambDegree() == 1) { + ++j; + registry.fill(HIST("Tracks/Control/nonamb/EtaZvtxNonAmb_gt0"), track.eta(), z); + registry.fill(HIST("Tracks/Control/nonamb/nonAmbTracksEtaZvtx"), + track.eta(), z); + registry.fill(HIST("Tracks/Control/nonamb/nonAmbTracksPhiEta"), phi, + track.eta()); + registry.fill(HIST("Tracks/Control/nonamb/nonAmbVertexCorr"), + track.template collision_as().posZ(), z); + registry.fill(HIST("Tracks/Control/nonamb/DCAxy_nonamb"), retrack.bestDCAXY()); + if constexpr (std::is_same_v>) { + registry.fill(HIST("Tracks/Control/nonamb/DCAz_nonamb"), retrack.bestDCAZ()); + } + uniqueEvents.insert(retrack.bestCollisionId()); + } + registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksEtaZvtx"), + track.eta(), z); + registry.fill(HIST("Tracks/Control/woOrp/woOrpTracksPhiEta"), phi, + track.eta()); + registry.fill(HIST("Tracks/Control/woOrp/woOrpVertexCorr"), + track.template collision_as().posZ(), z); + registry.fill(HIST("Tracks/Control/woOrp/DCAxy_woOrp"), retrack.bestDCAXY()); + if constexpr (std::is_same_v>) { + registry.fill(HIST("Tracks/Control/woOrp/DCAz_woOrp"), retrack.bestDCAZ()); + } + registry.fill(HIST("ambEventCounts"), 1, uniqueEventsAmb.size()); + registry.fill(HIST("NonambEventCounts"), 1, uniqueEvents.size()); + registry.fill(HIST("hNumCollisions_InelMFT"), 1, eventsInelMFT.size()); + } + + // Count selected reconstructed collisions once, not once per track. + // Mixed events contribute to both categories; orphan tracks were rejected above. + if (midtracks.size() > 0) { + if (nMFTSelectedNonAmb > 0) { + registry.fill(HIST("hNumCollisionsNonAmb_InelMFT"), 1.); + } + if (nMFTSelectedAmb > 0) { + registry.fill(HIST("hNumCollisionsAmb_InelMFT"), 1.); + } + } + + registry.fill(HIST("Tracks/Control/amb/nTrkAmb"), i); + registry.fill(HIST("Tracks/Control/nonamb/nTrkNonAmb"), j); + registry.fill(HIST("Tracks/Control/woOrp/nTrk"), k); + registry.fill(HIST("hNumCollisions_Inel"), 1, eventsInel.size()); + + registry.fill(HIST("PNch/MFT_sel8"), nMFTSelected); + if (midtracks.size() > 0) { + registry.fill(HIST("PNch/MFT_sel8_inelgt0"), nMFTSelected); + registry.fill(HIST("PNch/MFTZvtx_sel8_inelgt0"), nMFTSelected, z); + registry.fill(HIST("PNch/MFT_sel8_inelgt0_nonamb"), nMFTSelectedNonAmb); + registry.fill(HIST("PNch/MFT_sel8_inelgt0_amb"), nMFTSelectedAmb); + if (nMFTSelected > 0) { + registry.fill(HIST("PNch/MFT_sel8_inelfwdgt0"), nMFTSelected); + } + } + } + + void processMultReassoc(CollwEv::iterator const& collision, + o2::aod::MFTTracks const& mft, + soa::SmallGroups const& retracks, + FiCentralTracks const& midtracks, aod::Tracks const& trk) + { + processMultReassocCommon(collision, mft, retracks, midtracks, trk); } void processMultReassoc3d(CollwEv::iterator const& collision, @@ -1747,8 +1954,9 @@ struct PseudorapidityDensityMFT { ((phi > o2::constants::math::PI - cfgPhiCut) && (phi < o2::constants::math::PI + cfgPhiCut)) || (phi > o2::constants::math::TwoPI - cfgPhiCut) || ((phi > ((o2::constants::math::PIHalf - 0.1) * o2::constants::math::PI) - cfgPhiCut) && - (phi < ((o2::constants::math::PIHalf - 0.1) * o2::constants::math::PI) + cfgPhiCut))) + (phi < ((o2::constants::math::PIHalf - 0.1) * o2::constants::math::PI) + cfgPhiCut))) { continue; + } } registry.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c); @@ -1765,6 +1973,7 @@ struct PseudorapidityDensityMFT { "Count tracks in centrality bins", false); using Particles = soa::Filtered; + using ParticlesGenReco = aod::McParticles; expressions::Filter primaries = (aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == @@ -1773,6 +1982,8 @@ struct PseudorapidityDensityMFT { Partition mcSample = nabs(aod::mcparticle::eta) < McSampleEtaMax; Partition mcSampleCentral = nabs(aod::mcparticle::eta) < estimatorEta; + Partition mcSampleGenReco = nabs(aod::mcparticle::eta) < McSampleEtaMax; + Partition mcSampleCentralGenReco = nabs(aod::mcparticle::eta) < estimatorEta; void processGen( aod::McCollisions::iterator const& mcCollision, @@ -1886,12 +2097,14 @@ struct PseudorapidityDensityMFT { if (usePhiCut) { if ((phi <= PhiVetoLow) || ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || - (phi >= PhiVetoHigh)) + (phi >= PhiVetoHigh)) { continue; + } } if (usePtCut) { - if (ptCut > cfgnPt) + if (ptCut > cfgnPt) { continue; + } } if (cfgnEta1 < particle.eta() && particle.eta() < cfgnEta2 && (phi > cfgPhiCut1 && phi < cfgPhiCut2)) { registry.fill(HIST("TracksEtaZvtxGen_t"), particle.eta(), @@ -1949,7 +2162,7 @@ struct PseudorapidityDensityMFT { if constexpr (ExColsGenCent::template contains()) { cRec = collision.centFT0C(); } - if (!useEvSel || (useEvSel && collision.sel8())) { + if (!useEvSel || collision.sel8()) { if constexpr (ExColsGenCent::template contains()) { if (!atLeastOne) { cGen = cRec; @@ -2025,7 +2238,7 @@ struct PseudorapidityDensityMFT { soa::Join::iterator const& collision, MFTTracksLabeled const& tracks, aod::McParticles const&) { - if (!useEvSel || (useEvSel && collision.sel8())) { + if (!useEvSel || collision.sel8()) { for (const auto& track : tracks) { if (!track.has_mcParticle()) { continue; @@ -2067,9 +2280,9 @@ struct PseudorapidityDensityMFT { const auto countAndPassEvSelGenReco = [&](auto const& collision) { struct EvSelStep { - bool enabled; - decltype(aod::evsel::kIsTriggerTVX) bit; - GenRecoCutBin bin; + bool enabled{false}; + decltype(aod::evsel::kIsTriggerTVX) bit{aod::evsel::kIsTriggerTVX}; + GenRecoCutBin bin{GenRecoCutBin::AllRecoCollisions}; }; const std::array steps = {{ @@ -2104,12 +2317,6 @@ struct PseudorapidityDensityMFT { } fillGenRecoCut(step.bin); } - - if (useRctMFT && !myChecker(collision)) { - return false; - } - fillGenRecoCut(GenRecoCutBin::RctMFT); - return true; }; @@ -2117,22 +2324,29 @@ struct PseudorapidityDensityMFT { int nSavedRows = 0; std::unordered_set uniqueRecoColsSaved; - registry.fill(HIST("Purity/reco/CollisionNumContrib"), collision.numContrib()); + if (!collision.has_mcCollision()) { + LOGP(warning, "Reco collision {} has no MC collision label, skipping", collision.globalIndex()); + continue; + } + fillGenRecoCut(GenRecoCutBin::HasMcCollision); - if (useCont && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (useRctMFT && !myChecker(collision)) { continue; } - fillGenRecoCut(GenRecoCutBin::UseContBestCollisionIndex); + fillGenRecoCut(GenRecoCutBin::RctMFT); - if (!collision.has_mcCollision()) { - LOGP(warning, "Reco collision {} has no MC collision label, skipping", collision.globalIndex()); + if (useGoodItsLayersAll && + !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { continue; } - fillGenRecoCut(GenRecoCutBin::HasMcCollision); + fillGenRecoCut(GenRecoCutBin::UseGoodItsLayersAll); - if (!countAndPassEvSelGenReco(collision)) { + registry.fill(HIST("Purity/reco/CollisionNumContrib"), collision.numContrib()); + + if (useCont && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } + fillGenRecoCut(GenRecoCutBin::UseContBestCollisionIndex); const auto z = collision.posZ(); if ((z < cfgVzCut1) || (z > cfgVzCut2)) { @@ -2146,6 +2360,10 @@ struct PseudorapidityDensityMFT { } fillGenRecoCut(GenRecoCutBin::InelGt0); + if (!countAndPassEvSelGenReco(collision)) { + continue; + } + const int recoCol = collision.globalIndex(); const int mcCol = collision.mcCollisionId(); @@ -2195,7 +2413,7 @@ struct PseudorapidityDensityMFT { } const int recoCol = track.collisionId(); - if (acceptedRecoCols.find(recoCol) == acceptedRecoCols.end()) { + if (!acceptedRecoCols.contains(recoCol)) { continue; } @@ -2210,7 +2428,7 @@ struct PseudorapidityDensityMFT { int bin = static_cast(RightWrongBin::Neither); bool recoOfTrueExists = false; - /// const int bestColID = track.bestCollisionId(); + /// const int bestColID = track.bestCollisionId( const int mcOfTrack = isTrueByLabel ? track.mcParticle().mcCollisionId() : InvalidCollisionId; if (isTrueByLabel) { @@ -2314,15 +2532,26 @@ struct PseudorapidityDensityMFT { // soa::Join::iterator const& mcCollision // aod::McCollisions::iterator const& mcCollision // McCollisionsWithExtra::iterator const& mcCollision - + // Configurables for processGenReco event selection steps + Preslice perMcCollision = aod::mcparticle::mcCollisionId; + using ParticlesGen = soa::Filtered; template + // Configurables for processGenReco event selection steps void processGenReco(McCollisionsWithExtra::iterator const& mcCollision, o2::soa::SmallGroups> const& collisions, - FullBCs const& bcs, + FullBCs const& bcs, MFTTracksLabeledOrg const& originalTracks, MFTTracksT const& tracks, FiCentralTracks const& midtracks, - aod::McParticles const&) + CentralTracksMC const& centralTracksMC, + aod::McParticles const& mcParticles, /*ParticlesGen const& particlesgen,*/ + bool useOriginalPropagatedDcaZ = false) { + + bool atLeastOneSel8VzGt0 = false; // EtaZvtxGen_gt0 + bool hasRecoCollisionForPNch{false}; + int64_t selectedRecoNtrkForEventLoss{0}; + float selectedRecoZvtxForEventLoss{0.f}; + const auto fillGenRecoCut = [&](GenRecoCutBin bin) { registry.fill(HIST("EventsRecoCuts_GenReco"), static_cast(bin)); }; @@ -2357,17 +2586,36 @@ struct PseudorapidityDensityMFT { LOGF(fatal, "BC table is empty: cannot initialize magnetic field"); } + // Classify the generated event before filling the reconstructed-primary + // efficiency numerator. This is the same true-INEL>0 definition used + // below for TracksEtaZvtxGen_gt0_primary. + auto perCollisionMCSampleCentral = mcSampleCentralGenReco->sliceByCached( + aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + int nChargedCentral = 0; + for (const auto& particle : perCollisionMCSampleCentral) { + auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (!pdgParticle || + std::abs(pdgParticle->Charge()) < ChargeUnitTimesThree || + !particle.isPhysicalPrimary()) { + continue; + } + ++nChargedCentral; + } + const bool isTrueINELgt0ForEfficiency = + nChargedCentral > 0 && + mcCollision.posZ() >= cfgVzCut1 && mcCollision.posZ() <= cfgVzCut2; + const auto countAndPassEvSelGenReco = [&](auto const& collision) { struct EvSelStep { - bool enabled; - decltype(aod::evsel::kIsTriggerTVX) bit; - GenRecoCutBin bin; + bool enabled{false}; + decltype(aod::evsel::kIsTriggerTVX) bit{aod::evsel::kIsTriggerTVX}; + GenRecoCutBin bin{GenRecoCutBin::AllRecoCollisions}; }; const std::array steps = {{ - {true, aod::evsel::kIsTriggerTVX, GenRecoCutBin::IsTriggerTVX}, - {true, aod::evsel::kNoTimeFrameBorder, GenRecoCutBin::NoTimeFrameBorder}, - {true, aod::evsel::kNoITSROFrameBorder, GenRecoCutBin::NoITSROFrameBorder}, + {useTriggerTVX, aod::evsel::kIsTriggerTVX, GenRecoCutBin::IsTriggerTVX}, + {useNoTimeFrameBorderCut, aod::evsel::kNoTimeFrameBorder, GenRecoCutBin::NoTimeFrameBorder}, + {useNoITSROFrameBorderCut, aod::evsel::kNoITSROFrameBorder, GenRecoCutBin::NoITSROFrameBorder}, {useNoSameBunchPileup, aod::evsel::kNoSameBunchPileup, GenRecoCutBin::NoSameBunchPileup}, {useGoodZvtxFT0vsPV, aod::evsel::kIsGoodZvtxFT0vsPV, GenRecoCutBin::GoodZvtxFT0vsPV}, {useNoCollInRofStandard, aod::evsel::kNoCollInRofStandard, GenRecoCutBin::NoCollInRofStandard}, @@ -2377,14 +2625,6 @@ struct PseudorapidityDensityMFT { {useNoHighMultCollInPrevRof, aod::evsel::kNoHighMultCollInPrevRof, GenRecoCutBin::NoHighMultCollInPrevRof}, }}; - if (!useEvSel) { - for (const auto& step : steps) { - fillGenRecoCut(step.bin); - } - fillGenRecoCut(GenRecoCutBin::RctMFT); - return true; - } - for (const auto& step : steps) { if (!step.enabled) { fillGenRecoCut(step.bin); @@ -2397,11 +2637,6 @@ struct PseudorapidityDensityMFT { fillGenRecoCut(step.bin); } - if (useRctMFT && !myChecker(collision)) { - return false; - } - fillGenRecoCut(GenRecoCutBin::RctMFT); - return true; }; @@ -2409,22 +2644,32 @@ struct PseudorapidityDensityMFT { int nSavedRows = 0; std::unordered_set uniqueRecoColsSaved; - registry.fill(HIST("Purity/reco/CollisionNumContrib"), collision.numContrib()); + if (!collision.has_mcCollision()) { + LOGP(warning, "Reco collision {} has no MC collision label, skipping", collision.globalIndex()); + continue; + } + fillGenRecoCut(GenRecoCutBin::HasMcCollision); - if (useCont && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (useRctMFT && !myChecker(collision)) { continue; } - fillGenRecoCut(GenRecoCutBin::UseContBestCollisionIndex); + fillGenRecoCut(GenRecoCutBin::RctMFT); - if (!collision.has_mcCollision()) { - LOGP(warning, "Reco collision {} has no MC collision label, skipping", collision.globalIndex()); + if (useGoodItsLayersAll && + !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { continue; } - fillGenRecoCut(GenRecoCutBin::HasMcCollision); + fillGenRecoCut(GenRecoCutBin::UseGoodItsLayersAll); - if (!countAndPassEvSelGenReco(collision)) { + registry.fill(HIST("Purity/reco/CollisionNumContrib"), collision.numContrib()); + + if (useCont && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } + fillGenRecoCut(GenRecoCutBin::UseContBestCollisionIndex); + + auto perCollisionSample = originalTracks.sliceBy( + perCol, collision.globalIndex()); const auto z = collision.posZ(); if ((z < cfgVzCut1) || (z > cfgVzCut2)) { @@ -2433,17 +2678,79 @@ struct PseudorapidityDensityMFT { fillGenRecoCut(GenRecoCutBin::VzWindow); auto perCollisionSampleCentral = midtracks.sliceBy(perColCentral, collision.globalIndex()); + registry.fill(HIST("EventsZposDiff"), + collision.posZ() - mcCollision.posZ()); + if (useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > maxZDiff) { + continue; + } + } + registry.fill(HIST("EventsNtrkZvtxGen"), perCollisionSample.size(), + collision.posZ()); if (perCollisionSampleCentral.size() <= 0) { continue; } fillGenRecoCut(GenRecoCutBin::InelGt0); + if (!countAndPassEvSelGenReco(collision)) { + continue; + } + + // The reconstructed event has now passed the z window, reconstructed + // INEL>0, reco-gen z matching and the complete event selection. + if (!atLeastOneSel8VzGt0) { + selectedRecoNtrkForEventLoss = perCollisionSample.size(); + selectedRecoZvtxForEventLoss = collision.posZ(); + } + atLeastOneSel8VzGt0 = true; + + // Diagnostic-only central study for this accepted reconstructed event. + // Match the existing INEL>0 central track filter explicitly; none of + // these track-level continues rejects the event or changes MFT fills. + auto centralTracksForStudy = centralTracksMC.sliceBy( + perColCentral, collision.globalIndex()); + for (const auto& centralTrack : centralTracksForStudy) { + const auto flags = centralTrack.trackCutFlag(); + const bool hasTpc = + (centralTrack.detectorMap() & static_cast(aod::track::TPC)) != 0; + if ((flags & TrackSelectionIts) != TrackSelectionIts || + (hasTpc && (flags & TrackSelectionTpc) != TrackSelectionTpc) || + (flags & TrackSelectionDca) != TrackSelectionDca || + std::abs(centralTrack.eta()) >= estimatorEta) { + continue; + } + + // Inclusive denominator: include fake and unlabelled selected tracks. + registry.fill(HIST("Strangeness/Mid/RecoAllEtaZvtx"), + centralTrack.eta(), z, + centralTrack.dcaXY(), centralTrack.dcaZ()); + if (!centralTrack.has_mcParticle() || centralTrack.mcMask() != 0) { + continue; + } + const auto particle = centralTrack.template mcParticle_as(); + if (particle.isPhysicalPrimary() || !particle.has_mothers()) { + continue; + } + // Same immediate-mother species definition as the forward template. + const auto mother = particle.template mothers_as().front(); + const int parentPdg = mother.pdgCode(); + if (parentPdg != PDG_t::kK0Short && + std::abs(parentPdg) != PDG_t::kLambda0) { + continue; + } + const int parentSpecies = parentPdg == PDG_t::kK0Short ? 1 : (parentPdg == PDG_t::kLambda0 ? 2 : 3); + registry.fill(HIST("Strangeness/Mid/RecoStrangeEtaZvtx"), + centralTrack.eta(), z, + centralTrack.dcaXY(), centralTrack.dcaZ(), parentSpecies); + } + const int recoCol = collision.globalIndex(); const int mcCol = collision.mcCollisionId(); acceptedRecoCols.insert(recoCol); recoCollisionIds.insert(recoCol); trueMCCollisionIds.insert(mcCol); + hasRecoCollisionForPNch = true; if (mcCol >= 0) { recoToMc[recoCol] = mcCol; @@ -2463,6 +2770,10 @@ struct PseudorapidityDensityMFT { recoVtxByRecoId[recoCol] = {collision.posX(), collision.posY(), collision.posZ()}; recoVtxByMcId[mcCol] = {mcCollision.posX(), mcCollision.posY(), mcCollision.posZ()}; + // One entry per accepted reconstructed collision, including zero-MFT events. + registry.fill(HIST("OfflineVariations/SelectedRecoEventVertexResponse"), + mcCollision.posZ(), collision.posZ()); + registry.fill(HIST("Purity/xReco"), collision.posX()); registry.fill(HIST("Purity/xTrue"), mcCollision.posX()); registry.fill(HIST("Purity/yReco"), collision.posY()); @@ -2480,35 +2791,142 @@ struct PseudorapidityDensityMFT { int64_t woOrpCount = 0; bool filledRight = false; bool filledWrong = false; - int nMftSelectedAfterCuts = 0; + int64_t nMftSelectedAfterCuts{0}; + int64_t nRecoMFTSelectedForPNch{0}; + int64_t nGenPrimaryChargedMFT{0}; std::unordered_set uniqueBestRecoCols; + std::unordered_set recoColsWithNonAmbTracks; + std::unordered_set recoColsWithAmbTracks; + + std::unordered_set recoEventsWithRightTracks; + std::unordered_set recoEventsWithWrongTracks; + if (associationInvocation >= 0xffffffffULL) { + LOGF(fatal, "Association offline event-key invocation range exhausted"); + } + const auto associationCall = static_cast(associationInvocation++); + std::array associationUuid{}; + associationKeyNamespace.GetUUID(associationUuid.data()); + std::array associationNamespaceParts{}; + for (size_t part = 0; part < associationNamespaceParts.size(); ++part) { + associationNamespaceParts[part] = + (static_cast(associationUuid[2 * part]) << 8) | associationUuid[2 * part + 1]; + } + + // Separate pre-DCA pass: intentionally does not alter any existing track + // cuts, counters or fills. The 3D process supplies both measured DCA values. + if constexpr (requires { tracks.begin().bestDCAZ(); }) { + for (const auto& offlineTrack : tracks) { + const auto original = offlineTrack.template mfttrack_as(); + float offlinePhi = original.phi(); + o2::math_utils::bringTo02Pi(offlinePhi); + const auto compatibleCount = offlineTrack.compatibleCollIds().size(); + if (compatibleCount == 0 || original.nClusters() < cfgnCluster || + original.eta() <= cfgnEta1 || original.eta() >= cfgnEta2 || + original.chi2() / getTrackNdf(original) >= cfgChi2NDFMax || + offlinePhi <= cfgPhiCut1 || offlinePhi >= cfgPhiCut2 || + (usePhiCut && (offlinePhi <= PhiVetoLow || + (offlinePhi >= PhiVetoPiMin && offlinePhi <= PhiVetoPiMax) || + offlinePhi >= PhiVetoHigh)) || + (usePtCut && original.pt() > cfgnPt) || !passGenRecoTrackMode(offlineTrack)) { + continue; + } + // Same original-collision association as the existing reco denominator. + const int offlineRecoId = original.collisionId(); + if (!acceptedRecoCols.contains(offlineRecoId)) { + continue; + } + auto offlineVz = recoVtxZ.find(offlineRecoId); + if (offlineVz == recoVtxZ.end()) { + continue; + } + const int category = compatibleCount == SingleCompatibleCollision ? 0 : 1; + registry.fill(HIST("OfflineVariations/RecoTrackSparseBeforeDCA"), + original.eta(), offlineVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + const int chosenId = offlineTrack.bestCollisionId(); + auto chosenVz = recoVtxZ.find(chosenId); + auto chosenMc = recoToMc.find(chosenId); + // Only selected chosen reco collisions have a meaningful event denominator. + if (chosenId >= 0 && chosenVz != recoVtxZ.end() && chosenMc != recoToMc.end()) { + int association = 2; // Neither fake nor missing labels are forced into "wrong". + if (offlineTrack.has_mcParticle() && offlineTrack.mcMask() == 0) { + association = chosenMc->second == offlineTrack.mcParticle().mcCollisionId() ? 0 : 1; + } + if (association == 0) { + registry.fill(HIST("Association/RightTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } else if (association == 1) { + registry.fill(HIST("Association/WrongTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } else { + registry.fill(HIST("Association/UnclassifiedTrackSparseBeforeDCA"), original.eta(), chosenVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } + const auto chosenKey = static_cast(chosenId); + registry.fill(HIST("Association/EventTrackKeysBeforeDCA"), + original.eta(), chosenVz->second, offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), + category, association, + associationNamespaceParts[0], associationNamespaceParts[1], associationNamespaceParts[2], + associationNamespaceParts[3], associationNamespaceParts[4], associationNamespaceParts[5], + associationCall & 0xffffU, associationCall >> 16, + chosenKey & 0xffffU, chosenKey >> 16); + const bool passesConfiguredDCA = + (!useDCAxyCut || std::abs(offlineTrack.bestDCAXY()) <= maxDCAxy) && + (!useDCAzCut || std::abs(offlineTrack.bestDCAZ()) <= maxDCAz); + if (passesConfiguredDCA) { + if (association == 0) { + recoEventsWithRightTracks.insert(chosenId); + } + if (association == 1) { + recoEventsWithWrongTracks.insert(chosenId); + } + } + } + + if (offlineTrack.has_mcParticle() && offlineTrack.mcMask() == 0) { + const auto particle = offlineTrack.mcParticle(); + if (!particle.isPhysicalPrimary() && particle.has_mothers()) { + const auto mother = particle.template mothers_as().front(); + if (mother.pdgCode() == PDG_t::kK0Short || std::abs(mother.pdgCode()) == PDG_t::kLambda0) { + registry.fill(HIST("OfflineVariations/RecoStrangeSparseBeforeDCA"), + original.eta(), offlineVz->second, + offlineTrack.bestDCAXY(), offlineTrack.bestDCAZ(), category); + } + } + } + } + } if (tracks.size() > 0) { - bool countedPrimary = false; for (const auto& track : tracks) { - float ndf = getTrackNdf(track); - float chi2ndf = track.chi2() / ndf; - float phi = track.phi(); + const auto originalTrack = track.template mfttrack_as(); + float ndf = getTrackNdf(originalTrack); + float chi2ndf = originalTrack.chi2() / ndf; + float phi = originalTrack.phi(); float dcaXyCut = track.bestDCAXY(); float dcaZCut = 0.f; bool failDCAzCut = false; - float ptCut = track.pt(); - constexpr bool hasBestDCAZ = requires { track.bestDCAZ(); }; + float ptCut = originalTrack.pt(); + constexpr bool HasBestDCAZ = requires { track.bestDCAZ(); }; + const bool needsOriginalPropagatedDcaZ = useOriginalPropagatedDcaZ && !HasBestDCAZ; - if constexpr (hasBestDCAZ) { + if constexpr (HasBestDCAZ) { dcaZCut = track.bestDCAZ(); failDCAzCut = useDCAzCut && (std::abs(dcaZCut) > maxDCAz); } o2::math_utils::bringTo02Pi(phi); - const float etaReco = track.eta(); + const float etaReco = originalTrack.eta(); const float dcaXYReco = dcaXyCut; - const float dcaZReco = hasBestDCAZ ? dcaZCut : 0.f; + float dcaZReco = HasBestDCAZ ? dcaZCut : 0.f; const float dcaXReco = dcaXYReco * std::cos(phi); const float dcaYReco = dcaXYReco * std::sin(phi); + const bool isReassignedTrack = originalTrack.collisionId() != track.bestCollisionId(); + const bool isNotReassignedTrack = originalTrack.collisionId() == track.bestCollisionId(); + const bool isNotAssignedTrack = !isNotReassignedTrack && !isReassignedTrack; const bool failTrackCuts = - track.nClusters() < cfgnCluster || + originalTrack.nClusters() < cfgnCluster || etaReco <= cfgnEta1 || etaReco >= cfgnEta2 || chi2ndf >= cfgChi2NDFMax || @@ -2518,7 +2936,7 @@ struct PseudorapidityDensityMFT { ((phi <= PhiVetoLow) || ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || (phi >= PhiVetoHigh))) || - (useDCAxyCut && dcaXyCut > maxDCAxy) || + (useDCAxyCut && std::abs(dcaXyCut) > maxDCAxy) || failDCAzCut || (usePtCut && ptCut > cfgnPt); @@ -2529,9 +2947,10 @@ struct PseudorapidityDensityMFT { if (!passGenRecoTrackMode(track)) { continue; } + // std::cout << " track.collisionId() " << track.collisionId() << "track.bestCollisionId()" << track.bestCollisionId() << std::endl; // " track.globalIndex() "<second; + const double bZ = o2::base::Propagator::Instance()->getNominalBz(); + std::vector v1; + SMatrix55 tcovs(v1.begin(), v1.end()); + SMatrix5 tpars(originalTrack.x(), originalTrack.y(), originalTrack.phi(), originalTrack.tgl(), originalTrack.signed1Pt()); + o2::track::TrackParCovFwd trackPar0{originalTrack.z(), tpars, tcovs, originalTrack.chi2()}; + + if (needsOriginalPropagatedDcaZ) { + auto itVtxOrig = recoVtxByRecoId.find(recoCol); + if (itVtxOrig == recoVtxByRecoId.end()) { + continue; + } + + std::array dcaInfOrigForCut{999., 999., 999.}; + auto trackParForOrig = trackPar0; + trackParForOrig.propagateToDCAhelix(bZ, itVtxOrig->second, dcaInfOrigForCut); + dcaZCut = dcaInfOrigForCut[2]; + dcaZReco = dcaZCut; + failDCAzCut = useDCAzCut && (std::abs(dcaZCut) > maxDCAz); + // std::cout <<" dcaZReco " < SingleCompatibleCollision) { + recoColsWithAmbTracks.insert(recoCol); + } + + ++nRecoMFTSelectedForPNch; + + // Inclusive reconstructed-MC denominator: fill every selected track, + // including tracks without an MC-particle label. + registry.fill(HIST("Purity/reco/woOrp/woOrpTracksEtaZvtx"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp/woOrpTracksPtZvtx"), originalTrack.pt(), z); + registry.fill(HIST("Purity/reco/woOrp/woOrpEtaZvtx_gt0"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp/woOrpPtZvtx_gt0"), originalTrack.pt(), z); + registry.fill(HIST("Purity/reco/woOrp/woOrpTracksDCAxyZvtx_gt0"), dcaXyCut, z); + registry.fill(HIST("Purity/reco/woOrp/woOrpTracksDCAzZvtx_gt0"), dcaZCut, z); + registry.fill(HIST("Purity/reco/woOrp/woOrpTracksPhiEta"), phi, originalTrack.eta()); + ++woOrpCount; const bool hasMcLabel = track.has_mcParticle(); const bool isFakeByLabel = hasMcLabel ? (track.mcMask() != 0) : false; @@ -2595,8 +3060,16 @@ struct PseudorapidityDensityMFT { auto mcpartMother = mcPartForMother.template mothers_as().front(); if (mcpartMother.pdgCode() == PDG_t::kK0Short || std::abs(mcpartMother.pdgCode()) == PDG_t::kLambda0) { - registry.fill(HIST("Purity/reco/weakStrange/SelectedTracksEta"), track.eta()); - registry.fill(HIST("Purity/reco/weakStrange/SelectedTracksEtaZvtx"), track.eta(), z); + registry.fill(HIST("Purity/reco/weakStrange/SelectedTracksEta"), originalTrack.eta()); + registry.fill(HIST("Purity/reco/weakStrange/SelectedTracksEtaZvtx"), originalTrack.eta(), z); + // Keep the legacy histograms unchanged; the new SEF template + // excludes fake labels and counts only matched secondaries. + if (isSecondaryCharged) { + const int parentSpecies = + mcpartMother.pdgCode() == PDG_t::kK0Short ? 1 : (mcpartMother.pdgCode() == PDG_t::kLambda0 ? 2 : 3); + registry.fill(HIST("Strangeness/Fwd/RecoStrangeEtaZvtx"), + etaReco, z, dcaXYReco, dcaZReco, parentSpecies); + } } } } @@ -2630,11 +3103,11 @@ struct PseudorapidityDensityMFT { const int mcOfTrack = isTrueByLabel ? track.mcParticle().mcCollisionId() : InvalidCollisionId; const bool foundRecoColInRecoList = - recoCollisionIds.find(recoCol) != recoCollisionIds.end(); + recoCollisionIds.contains(recoCol); const bool foundBestColInRecoList = - recoCollisionIds.find(bestColID) != recoCollisionIds.end(); + recoCollisionIds.contains(bestColID); const bool foundInMCTrueList = - isTrueByLabel && (trueMCCollisionIds.find(mcOfTrack) != trueMCCollisionIds.end()); + isTrueByLabel && trueMCCollisionIds.contains(mcOfTrack); static constexpr int RecoColMissingBin = 1; static constexpr int BestRecoColMissingBin = 2; @@ -2702,6 +3175,19 @@ struct PseudorapidityDensityMFT { registry.fill(HIST("Purity/RecoOfTrueInCompatible"), recoOfTrueInCompatible ? static_cast(BoolBin::Yes) : static_cast(BoolBin::No)); + if (isNotReassignedTrack) { + registry.fill(HIST("Tracks/NotReAssignedTracks"), static_cast(SingleCountBin::Count)); + } + + if (isReassignedTrack) { + + registry.fill(HIST("Tracks/ReAssignedTracks"), static_cast(SingleCountBin::Count)); + } + + if (isNotAssignedTrack) { + + registry.fill(HIST("Tracks/NotAssignedTracks"), static_cast(SingleCountBin::Count)); + } if (bestColID >= 0) { uniqueBestRecoCols.insert(bestColID); @@ -2734,6 +3220,10 @@ struct PseudorapidityDensityMFT { if (isPrimaryCharged) { registry.fill(HIST("Purity/RecoSparsePrimary"), etaReco, dcaXYReco, dcaZReco, dcaXReco, dcaYReco); + if (isTrueINELgt0ForEfficiency) { + registry.fill(HIST("Purity/RecoSparsePrimaryTruthEta"), + track.mcParticle().eta(), dcaXYReco, dcaZReco); + } } else { registry.fill(HIST("Purity/RecoSparseSecondary"), etaReco, dcaXYReco, dcaZReco, dcaXReco, dcaYReco); @@ -2768,7 +3258,7 @@ struct PseudorapidityDensityMFT { const auto dcaXtruth = mcPart.vx() - mcColObj.posX(); const auto dcaYtruth = mcPart.vy() - mcColObj.posY(); - const auto dcaZtruth = hasBestDCAZ ? (mcPart.vz() - mcColObj.posZ()) : 0.f; + const auto dcaZtruth = HasBestDCAZ ? (mcPart.vz() - mcColObj.posZ()) : 0.f; const auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); const float etaTruth = mcPart.eta(); @@ -2784,45 +3274,35 @@ struct PseudorapidityDensityMFT { etaTruth, dcaXYtruth, dcaZtruth, dcaXtruth, dcaYtruth); } - registry.fill(HIST("Purity/reco/woOrp/woOrpTracksEtaZvtx"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp/woOrpTracksPtZvtx"), track.pt(), z); - registry.fill(HIST("Purity/reco/woOrp/woOrpEtaZvtx_gt0"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp/woOrpPtZvtx_gt0"), track.pt(), z); - registry.fill(HIST("Purity/reco/woOrp/woOrpTracksDCAxyZvtx_gt0"), dcaXyCut, z); - registry.fill(HIST("Purity/reco/woOrp/woOrpTracksDCAzZvtx_gt0"), dcaZCut, z); - registry.fill(HIST("Purity/reco/woOrp/woOrpTracksPhiEta"), phi, track.eta()); - if (isFakeByLabel) { - registry.fill(HIST("Purity/reco/woOrp_fake/woOrpTracksEtaZvtx"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_fake/woOrpTracksPtZvtx"), track.pt(), z); - registry.fill(HIST("Purity/reco/woOrp_fake/woOrpTracksPhiEta"), phi, track.eta()); - registry.fill(HIST("Purity/reco/woOrp_fake/woOrpEtaZvtx_gt0"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_fake/woOrpPtZvtx_gt0"), track.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_fake/woOrpTracksEtaZvtx"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_fake/woOrpTracksPtZvtx"), originalTrack.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_fake/woOrpTracksPhiEta"), phi, originalTrack.eta()); + registry.fill(HIST("Purity/reco/woOrp_fake/woOrpEtaZvtx_gt0"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_fake/woOrpPtZvtx_gt0"), originalTrack.pt(), z); } if (isTrueByLabel) { - registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpTracksEtaZvtx"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpTracksPtZvtx"), track.pt(), z); - registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpTracksPhiEta"), phi, track.eta()); - registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpEtaZvtx_gt0"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpPtZvtx_gt0"), track.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpTracksEtaZvtx"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpTracksPtZvtx"), originalTrack.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpTracksPhiEta"), phi, originalTrack.eta()); + registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpEtaZvtx_gt0"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_hasMC/woOrpPtZvtx_gt0"), originalTrack.pt(), z); } if (isSecondaryCharged) { - registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpTracksEtaZvtx"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpTracksPtZvtx"), track.pt(), z); - registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpTracksPhiEta"), phi, track.eta()); - registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpEtaZvtx_gt0"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpPtZvtx_gt0"), track.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpTracksEtaZvtx"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpTracksPtZvtx"), originalTrack.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpTracksPhiEta"), phi, originalTrack.eta()); + registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpEtaZvtx_gt0"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_secondary/woOrpPtZvtx_gt0"), originalTrack.pt(), z); } if (isPrimaryCharged) { - registry.fill(HIST("Purity/reco/woOrp_primary/woOrpTracksEtaZvtx"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_primary/woOrpTracksPtZvtx"), track.pt(), z); - registry.fill(HIST("Purity/reco/woOrp_primary/woOrpTracksPhiEta"), phi, track.eta()); - registry.fill(HIST("Purity/reco/woOrp_primary/woOrpEtaZvtx_gt0"), track.eta(), z); - registry.fill(HIST("Purity/reco/woOrp_primary/woOrpPtZvtx_gt0"), track.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_primary/woOrpTracksEtaZvtx"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_primary/woOrpTracksPtZvtx"), originalTrack.pt(), z); + registry.fill(HIST("Purity/reco/woOrp_primary/woOrpTracksPhiEta"), phi, originalTrack.eta()); + registry.fill(HIST("Purity/reco/woOrp_primary/woOrpEtaZvtx_gt0"), originalTrack.eta(), z); + registry.fill(HIST("Purity/reco/woOrp_primary/woOrpPtZvtx_gt0"), originalTrack.pt(), z); } - ++woOrpCount; - const float xTrue = mcColObj.posX(); const float yTrue = mcColObj.posY(); const float zTrue = mcColObj.posZ(); @@ -2832,13 +3312,6 @@ struct PseudorapidityDensityMFT { std::array dcaRight{999., 999.}; std::array dcaChosenXYZ{999., 999., 999.}; - const double bZ = o2::base::Propagator::Instance()->getNominalBz(); - - std::vector v1; - SMatrix55 tcovs(v1.begin(), v1.end()); - SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt()); - o2::track::TrackParCovFwd trackPar0{track.z(), tpars, tcovs, track.chi2()}; - auto trackPar = trackPar0; dcaInfOrig = {999., 999., 999.}; auto itVtxChosen = recoVtxByRecoId.find(bestColID); @@ -2948,52 +3421,190 @@ struct PseudorapidityDensityMFT { } } - registry.fill(HIST("Purity/mc/PrimaryAll"), static_cast(SingleCountBin::Count)); - registry.fill(HIST("Purity/mc/PrimaryAllEta"), mcPart.eta()); - registry.fill(HIST("Purity/mc/PrimaryTracksEtaZvtx"), mcPart.eta(), mcCollision.posZ()); - registry.fill(HIST("Purity/mc/PrimaryTracksEtaZvtx_gt0"), mcPart.eta(), mcCollision.posZ()); - registry.fill(HIST("Purity/mc/PrimaryTracksPtZvtx_gt0"), mcPart.pt(), mcCollision.posZ()); - registry.fill(HIST("Purity/mc/PrimaryTracksDCAxyZvtx_gt0"), dcaXyCut, mcCollision.posZ()); - registry.fill(HIST("Purity/mc/PrimaryTracksDCAzZvtx_gt0"), dcaZCut, mcCollision.posZ()); - registry.fill(HIST("Purity/mc/PrimaryTracksPhiEta"), mcPart.phi(), mcPart.eta()); - registry.fill(HIST("Purity/SelectedAfterDCAxy/PrimaryAll"), static_cast(SingleCountBin::Count)); - registry.fill(HIST("Purity/SelectedAfterDCAxy/PrimaryAllEta"), mcPart.eta()); - countedPrimary = true; - - registry.fill(HIST("Purity/PurityOverall"), - static_cast(SingleCountBin::Count), - countedPrimary ? static_cast(BoolBin::Yes) - : static_cast(BoolBin::No)); - registry.fill(HIST("Purity/PurityVsEta"), track.eta(), - countedPrimary ? static_cast(BoolBin::Yes) - : static_cast(BoolBin::No)); - } + } // hasmclable + } // track loop + } // track>mid + if constexpr (requires { tracks.begin().bestDCAZ(); }) { + if (!recoEventsWithRightTracks.empty()) { + registry.fill(HIST("Association/hNumRecoEventsWithRightTracks"), 1., + static_cast(recoEventsWithRightTracks.size())); + } + if (!recoEventsWithWrongTracks.empty()) { + registry.fill(HIST("Association/hNumRecoEventsWithWrongTracks"), 1., + static_cast(recoEventsWithWrongTracks.size())); + } + for (const int id : recoEventsWithRightTracks) { + registry.fill(HIST("Association/RecoEventVzWithRightTracks"), recoVtxZ.at(id)); + } + for (const int id : recoEventsWithWrongTracks) { + registry.fill(HIST("Association/RecoEventVzWithWrongTracks"), recoVtxZ.at(id)); } } + // Sets contain distinct reconstructed collisions, even when several reco + // collisions belong to this generated event. Mixed events enter both sets. + if (!recoColsWithNonAmbTracks.empty()) { + registry.fill(HIST("Purity/hNumCollisionsNonAmb_InelMFT"), 1., + static_cast(recoColsWithNonAmbTracks.size())); + } + if (!recoColsWithAmbTracks.empty()) { + registry.fill(HIST("Purity/hNumCollisionsAmb_InelMFT"), 1., + static_cast(recoColsWithAmbTracks.size())); + } registry.fill(HIST("Purity/HashTableRowCounts"), static_cast(HashTableRowCountsBin::UniqueBestRecoCols), uniqueBestRecoCols.size()); registry.fill(HIST("Purity/reco/woOrp/nTrk"), woOrpCount); registry.fill(HIST("Purity/reco/PNchMFT_afterCuts"), nMftSelectedAfterCuts); + + auto perCollisionMCSample = mcSampleGenReco->sliceByCached( + aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nCharged = 0; + if ((mcCollision.posZ() >= cfgVzCut1) && (mcCollision.posZ() <= cfgVzCut2)) { + for (const auto& particle : perCollisionMCSample) { + auto charge = 0.; + auto p = pdg->GetParticle(particle.pdgCode()); + if (p != nullptr) { + charge = p->Charge(); + } + if (std::abs(charge) < ChargeUnitTimesThree) { + continue; + } + nCharged++; + } + registry.fill(HIST("EventsNtrkZvtxGen_t"), nCharged, mcCollision.posZ()); + } + if ((mcCollision.posZ() >= cfgVzCut1) && (mcCollision.posZ() <= cfgVzCut2)) { + if (nChargedCentral > 0) { + registry.fill(HIST("EventEfficiencymc"), static_cast(EventEfficiencyBin::GeneratedInelGt0)); + registry.fill(HIST("EventsNtrkZvtxGen_gt0t"), nCharged, + mcCollision.posZ()); + if (atLeastOneSel8VzGt0) { + registry.fill(HIST("EventsNtrkZvtxGen_gt0"), + selectedRecoNtrkForEventLoss, + selectedRecoZvtxForEventLoss); + } + } + } + + for (const auto& particle : mcParticles) + // for (const auto& particle : perCollisionMCSample) + { + + auto p = pdg->GetParticle(particle.pdgCode()); + auto charge = 0; + if (p != nullptr) { + charge = static_cast(p->Charge()); + } + if (std::abs(charge) < ChargeUnitTimesThree) { + continue; + } + float phi = particle.phi(); + o2::math_utils::bringTo02Pi(phi); + float ptCut = particle.pt(); + + if (usePhiCut) { + if ((phi <= PhiVetoLow) || + ((phi >= PhiVetoPiMin) && (phi <= PhiVetoPiMax)) || + (phi >= PhiVetoHigh)) { + continue; + } + } + if (usePtCut) { + if (ptCut > cfgnPt) { + continue; + } + } + if (cfgnEta1 < particle.eta() && particle.eta() < cfgnEta2 && (phi > cfgPhiCut1 && phi < cfgPhiCut2)) { + if (mcCollision.posZ() >= cfgVzCut1 && mcCollision.posZ() <= cfgVzCut2) { + registry.fill(HIST("TracksEtaZvtxGen_t"), particle.eta(), + mcCollision.posZ()); + registry.fill(HIST("TracksPtEtaGen_t"), particle.pt(), particle.eta()); + } + if (mcCollision.posZ() >= cfgVzCut1 && mcCollision.posZ() <= cfgVzCut2) { + registry.fill(HIST("TracksEtaZvtxGen_gt0t"), particle.eta(), + mcCollision.posZ()); + registry.fill(HIST("TracksPhiEtaGen_gt0t"), particle.phi(), particle.eta()); + + if (nChargedCentral > 0) { + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("TracksEtaZvtxGen_gt0t_primary"), particle.eta(), mcCollision.posZ()); + } + registry.fill(HIST("TracksEtaZvtxGen"), particle.eta(), + mcCollision.posZ()); + registry.fill(HIST("TracksPtEtaGen"), particle.pt(), particle.eta()); + registry.fill(HIST("TracksPhiEtaGen"), particle.phi(), particle.eta()); + registry.fill(HIST("TracksPhiZvtxGen"), particle.phi(), + mcCollision.posZ()); + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("Purity/mc/PrimaryAll"), static_cast(SingleCountBin::Count)); + registry.fill(HIST("Purity/mc/PrimaryAllEta"), particle.eta()); + registry.fill(HIST("Purity/mc/PrimaryTracksEtaZvtx"), particle.eta(), mcCollision.posZ()); + registry.fill(HIST("Purity/mc/PrimaryTracksPhiEta"), particle.phi(), particle.eta()); + for (const auto& [offlineRecoId, offlineRecoZ] : recoVtxZ) { + registry.fill(HIST("OfflineVariations/GenPrimaryEtaTrueZRecoZ"), + particle.eta(), mcCollision.posZ(), offlineRecoZ); + } + } + + if (atLeastOneSel8VzGt0) { + registry.fill(HIST("TracksEtaZvtxGen_gt0"), particle.eta(), + mcCollision.posZ()); + registry.fill(HIST("TracksPhiEtaGen_gt0"), particle.phi(), particle.eta()); + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("TracksEtaZvtxGen_gt0_primary"), particle.eta(), mcCollision.posZ()); + registry.fill(HIST("TracksPhiEtaGen_gt0_primary"), particle.phi(), particle.eta()); + registry.fill(HIST("TracksPtZvtxGen_gt0_primary"), particle.pt(), mcCollision.posZ()); + registry.fill(HIST("Purity/mc/PrimaryTracksEtaZvtx_gt0"), particle.eta(), mcCollision.posZ()); + registry.fill(HIST("Purity/mc/PrimaryTracksPtZvtx_gt0"), particle.pt(), mcCollision.posZ()); + // registry.fill(HIST("Purity/mc/PrimaryTracksDCAxyZvtx_gt0"), dcaXyCut, mcCollision.posZ()); + // registry.fill(HIST("Purity/mc/PrimaryTracksDCAzZvtx_gt0"), dcaZCut, mcCollision.posZ()); + } + } + } + } + if (particle.isPhysicalPrimary()) { + + ++nGenPrimaryChargedMFT; + } + } + } + if ((mcCollision.posZ() >= cfgVzCut1) && (mcCollision.posZ() <= cfgVzCut2)) { + if (nChargedCentral > 0) { + + registry.fill(HIST("PNchMC/gen_inelgt0"), + nGenPrimaryChargedMFT); + + if (hasRecoCollisionForPNch) { + registry.fill(HIST("PNchMC/reco_sel8_inelgt0"), + nRecoMFTSelectedForPNch); + + registry.fill(HIST("PNchMC/responseMatrix"), + nRecoMFTSelectedForPNch, + nGenPrimaryChargedMFT); + } + } + } } void processGenReco3d(McCollisionsWithExtra::iterator const& mcCollision, o2::soa::SmallGroups> const& collisions, - FullBCs const& bcs, - MFTTracksLabeled3d const& tracks, + FullBCs const& bcs, MFTTracksLabeledOrg const& originalTracks, + MFTTracksLabeledGenReco3d const& tracks, FiCentralTracks const& midtracks, - aod::McParticles const& mcParticles) + CentralTracksMC const& centralTracksMC, + aod::McParticles const& mcParticles /*,ParticlesGen const& particlesgen*/) { - processGenReco(mcCollision, collisions, bcs, tracks, midtracks, mcParticles); + processGenReco(mcCollision, collisions, bcs, originalTracks, tracks, midtracks, centralTracksMC, mcParticles, /*particlesgen,*/ false); } void processGenReco2d(McCollisionsWithExtra::iterator const& mcCollision, o2::soa::SmallGroups> const& collisions, - FullBCs const& bcs, - MFTTracksLabeled2d const& tracks, + FullBCs const& bcs, MFTTracksLabeledOrg const& originalTracks, + MFTTracksLabeledGenReco2d const& tracks, FiCentralTracks const& midtracks, - aod::McParticles const& mcParticles) + CentralTracksMC const& centralTracksMC, + aod::McParticles const& mcParticles /*, ParticlesGen const& particlesgen*/) { - processGenReco(mcCollision, collisions, bcs, tracks, midtracks, mcParticles); + processGenReco(mcCollision, collisions, bcs, originalTracks, tracks, midtracks, centralTracksMC, mcParticles, /*particlesgen,*/ true); } PROCESS_SWITCH(PseudorapidityDensityMFT, processGenReco3d, "Process gen-reco info with BestCollisionsFwd3d", true); diff --git a/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx b/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx new file mode 100644 index 00000000000..2da575aed40 --- /dev/null +++ b/PWGLF/Tasks/GlobalEventProperties/dndetaHi.cxx @@ -0,0 +1,906 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file dndetaHi.cxx +/// \brief Task for dN/deta analysis in heavy ion collisions +/// \author hyungjun lee , joonsuk bae + +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGMM/Mult/DataModel/Index.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::track; +using namespace o2::aod; + +using BCsRun3 = soa::Join; +using MyCollisions = soa::Join; +using MyCollisionsCent = soa::Join; +using FullBCs = soa::Join; +using DaughterTrack = soa::Join; +using ExTracks = soa::Join; +using FiTracks = soa::Filtered; +using Particles = soa::Join; +using LabeledTracks = soa::Join; +using LabeledTracksEx = soa::Join; +using FiLTracks = soa::Filtered; +using DaughterTracks = soa::Join; +using BoolVector = std::vector; +using MyCollisionsMcLabels = soa::Join; + +enum EventCategory { + EvtBegin = 0, + EvtData, + EvtInel, + EvtInelg0, + EvtDd, + EvtSd, + EvtNd, + EvtEnd +}; + +enum TriggerClass { + TrigBegin = 0, + TrigSel8, + TrigSel8g0, + TrigEnd +}; + +enum Species { + SpecBegin = 0, + SpecK0short, + SpecLambda, + SpecAntilambda, + SpecEnd +}; + +enum TrackSign { + SignBegin = 0, + SignPositive, + SignNegative, + SignEnd +}; + +enum AnalysisStep { + StepBegin = 0, + StepAll, + StepBasiccut, + StepMasscut, + StepEnd +}; + +enum ParticleType { + PartBegin = 0, + PartData, + PartMotherStrange, + PartBkg, + PartNotPrimary, + PartPion, + PartKaon, + PartProton, + PartOther, + PartEnd +}; + +enum PtVariation { + PtVarBegin = 0, + PtVarNone, + PtVarUp, + PtVarDown, + PtVarEnd +}; + +namespace +{ + +constexpr float CutZ = 10.0f; +constexpr float CutEta1 = 1.0f; +constexpr float CutEta08 = 0.8f; +constexpr float CutEta05 = 0.5f; +constexpr float CutEta09 = 0.9f; +constexpr int CutCharge = 3; +constexpr float CutPt = 0.1f; + +constexpr int ProcIdND = 101; +constexpr int ProcIdSD1 = 103; +constexpr int ProcIdSD2 = 104; +constexpr int ProcIdDD1 = 105; +constexpr int ProcIdDD2 = 106; + +// Linear pT-extrapolation weights applied when pt < CutPt: weight = slope * pt + intercept +constexpr float PtWeightSlopeUp = -10.0f; +constexpr float PtWeightInterceptUp = 2.0f; +constexpr float PtWeightSlopeDown = 5.0f; +constexpr float PtWeightInterceptDown = 0.5f; +// Sentinel value used as centrality when no centrality information is available +constexpr float NoCentrality = -1.0f; + +// Bin indices of the "Selection" counter histogram — values must match SetBinLabel calls in init() +enum SelectionStep { + SelBegin = 0, + SelAll, + SelSel8, + SelSel8z10, + SelGeneratedInelGt0, + SelGoodBCs, + SelBCsWithCollisions, + SelBCsWithPileup, + SelInelSel8Mcz10, + SelInel, + SelInelg0, + SelInelGenz10, + SelInelg0Genz10, + SelInelSel8, + SelInelg0Sel8g0, + SelInelSel8Recz10, + SelInelg0Sel8g0Recz10, + SelK0sz10Eta05 +}; + +static constexpr TrackSelectionFlags::flagtype TrackSelectionITS = + TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | + TrackSelectionFlags::kITSHits; + +static constexpr TrackSelectionFlags::flagtype TrackSelectionTPC = + TrackSelectionFlags::kTPCNCls | + TrackSelectionFlags::kTPCCrossedRowsOverNCls | + TrackSelectionFlags::kTPCChi2NDF; + +static constexpr TrackSelectionFlags::flagtype TrackSelectionDCA = + TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; +} // namespace + +struct DndetaHi { + SliceCache cache; + Preslice perMCCol = aod::mcparticle::mcCollisionId; + Preslice perCol = aod::track::collisionId; + + Service pdg; + + Configurable estimatorEta{"estimatorEta", 2.0, "eta range for INEL>0 sample definition"}; + Configurable useEvSel{"useEvSel", true, "use event selection"}; + Configurable isPbPb{"isPbPb", false, "Is Pb-Pb"}; + + Configurable dcaV0Dau{"dcaV0Dau", 1.5, "DCA V0 Daughters"}; + Configurable dcaNegToPV{"dcaNegToPV", 0.06, "DCA Neg To PV"}; + Configurable dcaPosToPV{"dcaPosToPV", 0.06, "DCA Pos To PV"}; + Configurable v0CosPA{"v0CosPA", 0.97, "V0 CosPA"}; + Configurable v0Radius{"v0Radius", 0.5, "Radius"}; + Configurable etaDau{"etaDau", 4, "Eta Daughters"}; + Configurable v0Rapidity{"v0Rapidity", 0.5, "V0 rapidity"}; + ConfigurableAxis centrality{"centrality", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 22, 24, 26, 28, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100}, ""}; + Configurable zvtxcut{"zvtxcut", false, "z vtx cut < 10cm"}; + + HistogramRegistry registry{ + "registry", + {{"Selection", ";status;events", {HistType::kTH1F, {{17, 0.5, 17.5}}}}}}; + + AxisSpec zAxis = {{-30, -20, -15, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 30}, "Z (cm)", "zaxis"}; + AxisSpec deltaZAxis = {61, -6.1, 6.1, "", "deltaz axis"}; + AxisSpec dcaAxis = {601, -3.01, 3.01, "", "DCA axis"}; + AxisSpec etaAxis = {80, -4.0, 4.0, "#eta", "eta axis"}; + AxisSpec v0EtaAxis = {20, -1.0, 1.0, "#etav0", "eta axis"}; + AxisSpec phiAxis = {629, 0, 2 * o2::constants::math::PI, "Rad", "phi axis"}; + AxisSpec ptVarAxis = {static_cast(PtVarEnd) - 1, static_cast(PtVarBegin) + 0.5f, static_cast(PtVarEnd) - 0.5f, "", "ptvar axis"}; + AxisSpec evtClassAxis = {static_cast(EvtEnd) - 1, static_cast(EvtBegin) + 0.5f, static_cast(EvtEnd) - 0.5f, "", "event class"}; + AxisSpec trigClassAxis = {static_cast(TrigEnd) - 1, static_cast(TrigBegin) + 0.5f, static_cast(TrigEnd) - 0.5f, "", "trigger class"}; + AxisSpec particleTypeAxis = {static_cast(PartEnd) - 1, static_cast(PartBegin) + 0.5f, static_cast(PartEnd) - 0.5f, "", "Particle type"}; + AxisSpec speciesAxis = {static_cast(SpecEnd) - 1, static_cast(SpecBegin) + 0.5f, static_cast(SpecEnd) - 0.5f, "", "species class"}; + AxisSpec massAxis = {600, 0.3f, 1.3f, "Mass (GeV/c^{2})", "Inv. Mass (GeV/c^{2})"}; + AxisSpec signAxis = {static_cast(SignEnd) - 1, static_cast(SignBegin) + 0.5f, static_cast(SignEnd) - 0.5f, "", "sign"}; + AxisSpec stepAxis = {static_cast(StepEnd) - 1, static_cast(StepBegin) + 0.5f, static_cast(StepEnd) - 0.5f, "", "step"}; + AxisSpec testAxis = {101, -0.5, 100.5, "", "test"}; + AxisSpec multAxis = {1001, -0.5, 1000.5, "", "Ntrks"}; + AxisSpec statusCodeAxis = {3, -1.5, 2.5, "", "StatusCode"}; + AxisSpec processCodeAxis = {45, -1.5, 44.5, "", "StatusCode"}; + AxisSpec phiBin = {{0, o2::constants::math::PI / 2, o2::constants::math::PI, o2::constants::math::PI * 3. / 2, 2 * o2::constants::math::PI}, "#phi", "phi bin"}; + + expressions::Filter trackSelectionProper = + ((aod::track::trackCutFlag & TrackSelectionITS) == TrackSelectionITS) && + ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), + ncheckbit(aod::track::trackCutFlag, TrackSelectionTPC), + true) && + ncheckbit(aod::track::trackCutFlag, TrackSelectionDCA); + + expressions::Filter preFilterV0 = + nabs(aod::v0data::dcapostopv) > dcaPosToPV&& nabs(aod::v0data::dcanegtopv) > dcaNegToPV&& aod::v0data::dcaV0daughters < dcaV0Dau; + + Partition mcSample = nabs(aod::mcparticle::eta) < estimatorEta; + Partition tSample = nabs(aod::track::eta) < estimatorEta; + Partition tSample3 = nabs(aod::track::eta) < estimatorEta; + + void init(InitContext&) + { + AxisSpec centAxis = {centrality, "", "centrality"}; + AxisSpec centAxisPbPb = {centrality, "", "centrality"}; + registry.add({"hetaresponse", ";etaresponse", {HistType::kTH2D, {{80, -4, 4}, {80, -4, 4}}}}); + registry.add({"hft0multiplicity", ";multiplicity", {HistType::kTH1D, {{10000, 0, 100000}}}}); + registry.add({"hcentrality", isPbPb ? " ; centrality_FT0C (%) " : "; centrality_FT0M", {HistType::kTH1F, {{10000, 0, 100}}}}); + registry.add({"hcentralityvscentraldndeta", isPbPb ? " ; centrality_FT0C (%) " : "; centrality_FT0M", {HistType::kTH2F, {{100, 0, 100}, {100, 0, 100}}}}); + registry.add({"hrecdndeta", "evntclass; triggerclass; zvtex, eta", {HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, etaAxis, isPbPb ? centAxisPbPb : centAxis, particleTypeAxis, phiBin}}}); + registry.add({"hreczvtx", "evntclass; triggerclass; zvtex", {HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, isPbPb ? centAxisPbPb : centAxis}}}); + registry.add({"hphieta", "; #varphi; #eta; tracks", {HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, phiAxis, etaAxis, isPbPb ? centAxisPbPb : centAxis}}}); + registry.add({"hrecdndetamissing", "evntclass; triggerclass; zvtex, eta", {HistType::kTHnSparseD, {evtClassAxis, trigClassAxis, zAxis, etaAxis, isPbPb ? centAxisPbPb : centAxis}}}); + registry.add({"hgendndeta", "evntclass; zvtex, eta", {HistType::kTHnSparseD, {evtClassAxis, zAxis, etaAxis, isPbPb ? centAxisPbPb : centAxis, particleTypeAxis, ptVarAxis, phiBin}}}); + registry.add({"hgenzvtx", "evntclass; zvtex", {HistType::kTHnSparseD, {evtClassAxis, zAxis, isPbPb ? centAxisPbPb : centAxis}}}); + registry.add({"hv0mass", "etaaxis; invmass", {HistType::kTHnSparseD, {isPbPb ? centAxisPbPb : centAxis, speciesAxis, v0EtaAxis, massAxis}}}); + registry.add({"hv0k0s", "invmass", {HistType::kTH1D, {{100, 0.4, 0.6}}}}); + + registry.add({"recetaINELg0Sel8recz10", ";etaresponse", {HistType::kTH2D, {etaAxis, zAxis}}}); + registry.add({"genetaINELg0Sel8recz10", ";etaresponse", {HistType::kTH2D, {etaAxis, zAxis}}}); + registry.add({"genetaINELg0Sel8genz10", ";etaresponse", {HistType::kTH2D, {etaAxis, zAxis}}}); + registry.add({"genetaINELg0genz10", ";etaresponse", {HistType::kTH2D, {etaAxis, zAxis}}}); + + registry.add({"reczINELg0Sel8", ";z", {HistType::kTH1D, {zAxis}}}); + registry.add({"genzINELg0Sel8", ";z", {HistType::kTH1D, {zAxis}}}); + registry.add({"genzINELg0", ";z", {HistType::kTH1D, {zAxis}}}); + registry.add({"hcentmult", ";status;events", {HistType::kTH1D, {{100, 0, 100}}}}); + + constexpr int NLogBins = 50; + constexpr double PtLogLow = 0.01; + constexpr double PtLogHigh = 10.0; + std::vector logBins(NLogBins + 1, 0.0); + double logBinWidth = (std::log(PtLogHigh) - std::log(PtLogLow)) / NLogBins; + for (int i = 0; i <= NLogBins; i++) { + logBins[i] = PtLogLow * std::exp(i * logBinWidth); + } + AxisSpec ptBins = {logBins, "pT (GeV/c)", "pt bin"}; + + registry.add({"hrecdndpt", " pt", {HistType::kTH1D, {ptBins}}}); + registry.add({"hdndptefficiency", " pt", {HistType::kTH1D, {ptBins}}}); + registry.add({"hgendndpt", " pt", {HistType::kTH1D, {ptBins}}}); + registry.add({"hgendndpt2", " pt", {HistType::kTH1D, {ptBins}}}); + registry.add({"hgendndpt05", " pt", {HistType::kTH1D, {ptBins}}}); + + auto hstat = registry.get(HIST("Selection")); + auto* x = hstat->GetXaxis(); + x->SetBinLabel(SelAll, "All"); + x->SetBinLabel(SelSel8, "Sel8"); + x->SetBinLabel(SelSel8z10, "Sel8z10"); + x->SetBinLabel(SelGeneratedInelGt0, "Generated INEL>0"); + x->SetBinLabel(SelGoodBCs, "Good BCs"); + x->SetBinLabel(SelBCsWithCollisions, "BCs with collisions"); + x->SetBinLabel(SelBCsWithPileup, "BCs with pile-up/splitting"); + x->SetBinLabel(SelInelSel8Mcz10, "INEL&Sel8&mcz10"); + x->SetBinLabel(SelInel, "INEL"); + x->SetBinLabel(SelInelg0, "INELg0"); + x->SetBinLabel(SelInelGenz10, "INELgenz10"); + x->SetBinLabel(SelInelg0Genz10, "INELg0genz10"); + x->SetBinLabel(SelInelSel8, "INELSel8"); + x->SetBinLabel(SelInelg0Sel8g0, "INELg0Sel8g0"); + x->SetBinLabel(SelInelSel8Recz10, "INELSel8recz10"); + x->SetBinLabel(SelInelg0Sel8g0Recz10, "INELg0Sel8g0recz10"); + x->SetBinLabel(SelK0sz10Eta05, "K0Sz10eta05"); + } + + void processEventStat( + const FullBCs& bcs, + const soa::Join& collisions) + { + std::vector::iterator> cols; + for (const auto& bc : bcs) { + if (!useEvSel || (bc.selection_bit(o2::aod::evsel::kIsBBT0A) && + bc.selection_bit(o2::aod::evsel::kIsBBT0C)) != 0) { + registry.fill(HIST("Selection"), SelGoodBCs); + cols.clear(); + for (const auto& collision : collisions) { + if (collision.has_foundBC()) { + if (collision.foundBCId() == bc.globalIndex()) { + cols.emplace_back(collision); + } + } else if (collision.bcId() == bc.globalIndex()) { + cols.emplace_back(collision); + } + } + LOGP(debug, "BC {} has {} collisions", bc.globalBC(), cols.size()); + if (!cols.empty()) { + registry.fill(HIST("Selection"), SelBCsWithCollisions); + if (cols.size() > 1) { + registry.fill(HIST("Selection"), SelBCsWithPileup); + } + } + } + } + } + + PROCESS_SWITCH(DndetaHi, processEventStat, "Collect event sample stats", false); + + template + void runCounting( + const C& collisions, + const FiTracks& /*tracks*/) + { + for (const auto& collision : collisions) { + BoolVector btrigc(TrigEnd, false); + registry.fill(HIST("Selection"), SelAll); + auto z = collision.posZ(); + auto pertracks = tSample3->sliceBy(perCol, collision.globalIndex()); + auto nTrk = 0; + + if (collision.sel8()) { + btrigc[TrigSel8] = true; + registry.fill(HIST("Selection"), SelSel8); + if (std::abs(z) < CutZ) { + registry.fill(HIST("Selection"), SelSel8z10); + } + } + if (btrigc[TrigSel8] && std::abs(z) < CutZ) { + registry.fill(HIST("hft0multiplicity"), collision.multFT0C()); + } + + for (const auto& track : pertracks) { + [[maybe_unused]] int dummy = track.globalIndex(); + if (std::abs(track.eta()) < CutEta1) { + nTrk++; + } + } + if (nTrk > 0) { + if (btrigc[TrigSel8]) { + btrigc[TrigSel8g0] = true; + } + } + if (btrigc[TrigSel8g0]) { + registry.fill(HIST("reczINELg0Sel8"), z); + } + + auto cent = -1.f; + if (isPbPb) { + if constexpr (C::template contains()) { + cent = collision.centFT0C(); + } + } else { + if constexpr (C::template contains()) { + cent = collision.centFT0M(); + } + } + + if (isPbPb) { + if (std::abs(z) < CutZ && btrigc[TrigSel8]) { + registry.fill(HIST("hcentrality"), cent); + } + } else { + if (std::abs(z) < CutZ && btrigc[TrigSel8g0]) { + registry.fill(HIST("hcentrality"), cent); + registry.fill(HIST("hcentralityvscentraldndeta"), cent, nTrk); + } + } + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); itrigc++) { + if (btrigc[itrigc]) { + registry.fill(HIST("hreczvtx"), EvtData, itrigc, z, cent); + } + } + + for (const auto& track : pertracks) { + if (btrigc[TrigSel8] && std::abs(track.eta()) < CutEta08 && std::abs(z) < CutZ) { + registry.fill(HIST("hrecdndpt"), track.pt()); + } + if (btrigc[TrigSel8]) { + registry.fill(HIST("recetaINELg0Sel8recz10"), track.eta(), z); + } + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); itrigc++) { + if (btrigc[itrigc]) { + registry.fill(HIST("hphieta"), EvtData, itrigc, track.phi(), track.eta(), cent); + registry.fill(HIST("hrecdndeta"), EvtData, itrigc, z, track.eta(), cent, PartData, track.phi()); + } + } + } + } + } + + PresliceUnsorted> perMcCol = o2::aod::mccollisionlabel::mcCollisionId; + Preslice perMCColparticles = aod::mcparticle::mcCollisionId; + Preslice perColFiLTracks = aod::track::collisionId; + using MCex = soa::Join; + + void processMCCounting( + const MCex& mcCollisions, + const soa::Join& collisions, + const Particles& mcParticles, + const FiLTracks& tracks) + { + for (const auto& mcCollision : mcCollisions) { + BoolVector bevtc(EvtEnd, false); + bevtc[EvtInel] = true; + auto procId = mcCollision.processId(); + if (procId == ProcIdND) { + bevtc[EvtNd] = true; + } else if (procId == ProcIdSD1 || procId == ProcIdSD2) { + bevtc[EvtSd] = true; + } else if (procId == ProcIdDD1 || procId == ProcIdDD2) { + bevtc[EvtDd] = true; + } + registry.fill(HIST("Selection"), SelAll); + + auto mcz = mcCollision.posZ(); + auto genz = mcz; + auto nTrkGen = 0; + auto particles = mcParticles.sliceBy(perMCCol, mcCollision.globalIndex()); + + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto kp = pdg->GetParticle(particle.pdgCode()); + if (kp != nullptr) { + if (std::abs(kp->Charge()) >= CutCharge) { + if (std::abs(particle.eta()) < CutEta1) { + nTrkGen++; + } + } + } + } + if (nTrkGen > 0) { + registry.fill(HIST("Selection"), SelGeneratedInelGt0); + bevtc[EvtInelg0] = true; + } + if (bevtc[EvtInel]) { + registry.fill(HIST("Selection"), SelInel); + } + if (bevtc[EvtInel] && std::abs(mcz) < CutZ) { + registry.fill(HIST("Selection"), SelInelGenz10); + } + if (bevtc[EvtInelg0]) { + registry.fill(HIST("Selection"), SelInelg0); + } + if (bevtc[EvtInelg0] && std::abs(mcz) < CutZ) { + registry.fill(HIST("Selection"), SelInelg0Genz10); + } + + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto kp = pdg->GetParticle(particle.pdgCode()); + if (kp != nullptr) { + if (std::abs(kp->Charge()) >= CutCharge) { + if (bevtc[EvtInel] && std::abs(particle.eta()) < CutEta08 && std::abs(mcz) < CutZ) { + registry.fill(HIST("hgendndpt"), particle.pt()); + if (particle.pt() < CutPt) { + registry.fill(HIST("hgendndpt2"), particle.pt(), PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp); + registry.fill(HIST("hgendndpt05"), particle.pt(), PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown); + } else { + registry.fill(HIST("hgendndpt2"), particle.pt()); + registry.fill(HIST("hgendndpt05"), particle.pt()); + } + } + } + } + } + + auto collisionsample = collisions.sliceBy(perMcCol, mcCollision.globalIndex()); + auto centMC = -1.f; + if (collisionsample.size() != 1) { + centMC = -1.0; + } else { + for (const auto& collision : collisionsample) { + if (isPbPb) { + if constexpr (MyCollisionsCent::template contains()) { + centMC = collision.centFT0C(); + } + } else { + if constexpr (MyCollisionsCent::template contains()) { + centMC = collision.centFT0M(); + } + } + } + } + + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { + if (bevtc[ievtc]) { + registry.fill(HIST("hgenzvtx"), ievtc, genz, centMC); + } + } + + int pid = 0; + std::vector particleetas; + for (const auto& particle : particles) { + auto p = pdg->GetParticle(particle.pdgCode()); + if (std::abs(particle.pdgCode()) == kK0Short && std::abs(particle.eta()) < CutEta05 && std::abs(genz) < CutZ) { + registry.fill(HIST("Selection"), SelK0sz10Eta05); + } + if (!particle.isPhysicalPrimary()) { + continue; + } + switch (std::abs(particle.pdgCode())) { + case kPiPlus: + pid = PartPion; + break; + case kKPlus: + pid = PartKaon; + break; + case kProton: + pid = PartProton; + break; + default: + pid = PartOther; + break; + } + + if (p != nullptr) { + if (std::abs(p->Charge()) >= CutCharge) { + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { + if (bevtc[ievtc]) { + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarNone, particle.phi()); + if (particle.pt() < CutPt) { + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarUp, particle.phi(), PtWeightSlopeUp * particle.pt() + PtWeightInterceptUp); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarDown, particle.phi(), PtWeightSlopeDown * particle.pt() + PtWeightInterceptDown); + } else { + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarUp, particle.phi()); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), centMC, pid, PtVarDown, particle.phi()); + } + } + } + if (pid >= PartPion && pid <= PartOther) { + particleetas.push_back(particle.eta()); + } + } + } + } + + for (const auto& collision : collisionsample) { + auto centRec = -1.f; + if (isPbPb) { + if constexpr (MyCollisionsCent::template contains()) { + centRec = collision.centFT0C(); + } + } else { + if constexpr (MyCollisionsCent::template contains()) { + centRec = collision.centFT0M(); + } + } + + BoolVector btrigc(TrigEnd, false); + auto z = collision.posZ(); + if (collision.sel8()) { + btrigc[TrigSel8] = true; + registry.fill(HIST("Selection"), SelSel8); + if (std::abs(z) < CutZ) { + registry.fill(HIST("Selection"), SelSel8z10); + } + } + if (bevtc[EvtInel] && btrigc[TrigSel8] && std::abs(z) < CutZ) { + registry.fill(HIST("hft0multiplicity"), collision.multFT0C()); + registry.fill(HIST("Selection"), SelInelSel8Mcz10); + } + if (collisionsample.size() == 1 && bevtc[EvtInelg0] && btrigc[TrigSel8]) { + for (const auto& eta : particleetas) { + registry.fill(HIST("genetaINELg0Sel8recz10"), eta, z); + registry.fill(HIST("genetaINELg0Sel8genz10"), eta, mcz); + } + registry.fill(HIST("reczINELg0Sel8"), z); + registry.fill(HIST("genzINELg0Sel8"), genz); + } + if (collisionsample.size() == 1 && bevtc[EvtInelg0]) { + for (const auto& eta : particleetas) { + registry.fill(HIST("genetaINELg0genz10"), eta, mcz); + } + registry.fill(HIST("genzINELg0"), genz); + } + + auto nTrkRec = 0; + auto trackspart = tracks.sliceBy(perColFiLTracks, collision.globalIndex()); + for (const auto& track : trackspart) { + if (std::abs(track.eta()) < CutEta1) { + nTrkRec++; + } + } + + if (nTrkRec > 0) { + if (btrigc[TrigSel8]) { + btrigc[TrigSel8g0] = true; + } + } + if (isPbPb) { + if (std::abs(z) < CutZ && btrigc[TrigSel8]) { + registry.fill(HIST("hcentrality"), centRec); + } + } else { + if (std::abs(z) < CutZ && btrigc[TrigSel8g0]) { + registry.fill(HIST("hcentrality"), centRec); + } + } + + if (bevtc[EvtInel] && btrigc[TrigSel8]) { + registry.fill(HIST("Selection"), SelInelSel8); + } + if (bevtc[EvtInel] && btrigc[TrigSel8] && std::abs(z) < CutZ) { + registry.fill(HIST("Selection"), SelInelSel8Recz10); + } + if (bevtc[EvtInelg0] && btrigc[TrigSel8g0]) { + registry.fill(HIST("Selection"), SelInelg0Sel8g0); + } + if (bevtc[EvtInelg0] && btrigc[TrigSel8g0] && std::abs(z) < CutZ) { + registry.fill(HIST("Selection"), SelInelg0Sel8g0Recz10); + } + + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); itrigc++) { + if (bevtc[ievtc] && btrigc[itrigc]) { + registry.fill(HIST("hreczvtx"), ievtc, itrigc, z, centRec); + } + } + } + + std::vector mclabels; + for (const auto& track : trackspart) { + if (track.has_mcParticle()) { + int trackPid = PartBkg; + auto particle = track.template mcParticle_as(); + if (particle.isPhysicalPrimary()) { + switch (std::abs(particle.pdgCode())) { + case kPiPlus: + trackPid = PartPion; + break; + case kKPlus: + trackPid = PartKaon; + break; + case kProton: + trackPid = PartProton; + break; + default: + trackPid = PartOther; + break; + } + } else { + trackPid = PartNotPrimary; + } + for (auto motherIds = particle.mothersIds().front(); motherIds <= particle.mothersIds().back(); motherIds++) { + auto mother = mcParticles.rawIteratorAt(motherIds); + auto pdgMother = mother.pdgCode(); + if (pdgMother == kK0Short || std::abs(pdgMother) == kLambda0) { + trackPid = PartMotherStrange; + } + } + if (std::find(mclabels.begin(), mclabels.end(), track.mcParticleId()) != mclabels.end()) { + trackPid = PartBkg; + } + mclabels.push_back(track.mcParticleId()); + registry.fill(HIST("hetaresponse"), particle.eta(), track.eta(), centRec); + if (bevtc[EvtInel] && btrigc[TrigSel8] && + std::abs(track.eta()) < CutEta08 && std::abs(z) < CutZ && + trackPid != PartBkg && trackPid != PartNotPrimary) { + registry.fill(HIST("hdndptefficiency"), particle.pt()); + } + if (btrigc[TrigSel8] && bevtc[EvtInelg0]) { + registry.fill(HIST("recetaINELg0Sel8recz10"), track.eta(), z); + } + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); itrigc++) { + if (bevtc[ievtc] && btrigc[itrigc]) { + registry.fill(HIST("hrecdndeta"), ievtc, itrigc, z, particle.eta(), centRec, trackPid, particle.phi()); + registry.fill(HIST("hphieta"), ievtc, itrigc, track.phi(), track.eta(), centRec); + } + } + } + } else { + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { + for (auto itrigc = 1u; itrigc < static_cast(TrigEnd); itrigc++) { + if (bevtc[ievtc] && btrigc[itrigc]) { + registry.fill(HIST("hrecdndeta"), ievtc, itrigc, z, track.eta(), centRec, PartBkg, track.phi()); + } + } + } + } + } + } + } + } + + PROCESS_SWITCH(DndetaHi, processMCCounting, "MC Count tracks", false); + + // void processTrackEfficiencyGeneral( + // MyCollisionsMcLabels::iterator const& collision, + // const aod::McCollisions& /*mcCollisions*/, + // const Particles& particles, + // const FiLTracks& /*tracks*/) + // { + // if (!collision.sel8()) { + // return; + // } + // if (!collision.has_mcCollision()) { + // return; + // } + + // auto mcCollision = collision.mcCollision(); + // [[maybe_unused]] auto particlesPerCol = particles.sliceBy(perMCCol, mcCollision.globalIndex()); + // } + // PROCESS_SWITCH(DndetaHi, processTrackEfficiencyGeneral, "MC Count tracks", false); + + void processCounting( + const MyCollisionsCent& collisions, + const FiTracks& tracks) + { + runCounting(collisions, tracks); + } + PROCESS_SWITCH(DndetaHi, processCounting, "Count tracks with Centrality", false); + + void processGen( + const aod::McCollisions::iterator& mcCollision, + const Particles& mcParticles) + { + auto genz = mcCollision.posZ(); + BoolVector bevtc(EvtEnd, false); + bevtc[EvtInel] = true; + for (const auto& particle : mcParticles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto p = pdg->GetParticle(particle.pdgCode()); + if (p != nullptr) { + if (std::abs(p->Charge()) >= CutCharge) { + if (std::abs(particle.eta()) < CutEta1) { + bevtc[EvtInelg0] = true; + } + } + } + } + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { + if (bevtc[ievtc]) { + registry.fill(HIST("hgenzvtx"), ievtc, genz, NoCentrality); + } + } + int pid = 0; + for (const auto& particle : mcParticles) { + auto p = pdg->GetParticle(particle.pdgCode()); + if (std::abs(particle.pdgCode()) == kK0Short && std::abs(particle.eta()) < CutEta05 && std::abs(genz) < CutZ) { + registry.fill(HIST("Selection"), SelK0sz10Eta05); + } + if (!particle.isPhysicalPrimary()) { + continue; + } + switch (std::abs(particle.pdgCode())) { + case kPiPlus: + pid = PartPion; + break; + case kKPlus: + pid = PartKaon; + break; + case kProton: + pid = PartProton; + break; + default: + pid = PartOther; + break; + } + + if (p != nullptr) { + if (std::abs(p->Charge()) >= CutCharge) { + for (auto ievtc = 1u; ievtc < static_cast(EvtEnd); ievtc++) { + if (bevtc[ievtc]) { + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), NoCentrality, pid, PtVarNone, particle.phi()); + if (particle.pt() < CutPt) { + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), NoCentrality, pid, PtVarUp, particle.phi(), PtWeightInterceptUp); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), NoCentrality, pid, PtVarDown, particle.phi(), PtWeightInterceptDown); + } else { + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), NoCentrality, pid, PtVarUp, particle.phi()); + registry.fill(HIST("hgendndeta"), ievtc, genz, particle.eta(), NoCentrality, pid, PtVarDown, particle.phi()); + } + } + } + } + } + } + } + PROCESS_SWITCH(DndetaHi, processGen, "Process generator-level info", false); + + Preslice perCollisionV0 = o2::aod::v0data::collisionId; + + void processV0Counting( + const MyCollisionsCent& collisions, + const aod::V0Datas& fullV0s, + const FiTracks& /*tracks*/, + const DaughterTracks& /*dauTrks*/) + { + for (const auto& collision : collisions) { + if (!collision.sel8()) { + continue; + } + auto z = collision.posZ(); + auto cent = -1.f; + if (isPbPb) { + if constexpr (MyCollisionsCent::template contains()) { + cent = collision.centFT0C(); + } + } else { + if constexpr (MyCollisionsCent::template contains()) { + cent = collision.centFT0M(); + } + } + + auto v0sPerColl = fullV0s.sliceBy(perCollisionV0, collision.globalIndex()); + for (const auto& v0 : v0sPerColl) { + auto pTrack = v0.template posTrack_as(); + auto nTrack = v0.template negTrack_as(); + if (std::abs(z) < CutZ) { + if (v0.v0radius() > v0Radius) { + continue; + } + if (v0.dcapostopv() > dcaPosToPV) { + continue; + } + if (v0.dcanegtopv() > dcaNegToPV) { + continue; + } + if (v0.v0cosPA() < v0CosPA) { + continue; + } + if (std::fabs(pTrack.eta()) > CutEta09) { + continue; + } + if (std::fabs(nTrack.eta()) > CutEta09) { + continue; + } + if (std::fabs(v0.eta()) < CutEta05) { + registry.fill(HIST("hv0k0s"), v0.mK0Short()); + } + registry.fill(HIST("hv0mass"), cent, SpecK0short, v0.eta(), v0.mK0Short()); + registry.fill(HIST("hv0mass"), cent, SpecLambda, v0.eta(), v0.mLambda()); + registry.fill(HIST("hv0mass"), cent, SpecAntilambda, v0.eta(), v0.mAntiLambda()); + } + } + } + } + PROCESS_SWITCH(DndetaHi, processV0Counting, "MC V0Count tracks without Centrality", false); + + // void processMCV0Counting( + // const soa::Join& collisions, + // const aod::McCollisions& /*mcCollisions*/, + // const Particles& /*mcParticles*/, + // const soa::Filtered& /*fullV0s*/, + // const soa::Filtered& /*tracks*/, + // const DaughterTracks& /*dauTrks*/) + // { + // for (const auto& collision : collisions) { + // if (useEvSel && !collision.sel8()) { + // continue; + // } + // if (!collision.has_mcCollision()) { + // continue; + // } + // } + // } + // PROCESS_SWITCH(DndetaHi, processMCV0Counting, "MC V0Count tracks without Centrality", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx b/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx index 8820bc3f156..e7d70cf152d 100644 --- a/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx @@ -27,6 +27,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/FT0Corrected.h" +#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -55,16 +56,15 @@ #include #include #include -#include #include #include -#include #include +#include + #include #include -#include #include #include #include @@ -74,6 +74,7 @@ #include #include #include +#include #include using std::string; @@ -83,47 +84,40 @@ using namespace o2::constants::math; using namespace o2::aod::rctsel; auto static constexpr CminCharge = 3.f; -static constexpr float CnullInt = 0; static constexpr float Cnull = 0.0f; -static constexpr float ConeInt = 1; static constexpr float Cone = 1.0f; // FV0 specific constants static constexpr int CmaxRingsFV0 = 5; static constexpr int CnCellsFV0 = 48; static constexpr int CinnerFV0 = 32; -static constexpr float Cfv0IndexPhi[5] = {0., 8., 16., 24., 32.}; +const std::array cFV0IndexPhi{0, 8, 16, 24, 32}; static constexpr float CmaxEtaFV0 = 5.1; static constexpr float CminEtaFV0 = 2.2; static constexpr float CdEtaFV0 = (CmaxEtaFV0 - CminEtaFV0) / CmaxRingsFV0; -auto static constexpr CminAccFT0A = 3.5f; -auto static constexpr CmaxAccFT0A = 4.9f; -auto static constexpr CminAccFT0C = -3.3f; -auto static constexpr CmaxAccFT0C = -2.1f; + // PID names static constexpr int CprocessIdWeak = 4; -static constexpr int Ncharges = 2; -static constexpr o2::track::PID::ID Npart = 5; -static constexpr o2::track::PID::ID NpartChrg = Npart * Ncharges; -static constexpr int PDGs[] = {11, 13, 211, 321, 2212}; -static constexpr int PidSgn[NpartChrg] = {11, 13, 211, 321, 2212, -11, -13, -211, -321, -2212}; -static constexpr const char* Pid[Npart] = {"el", "mu", "pi", "ka", "pr"}; -static constexpr const char* PidChrg[NpartChrg] = {"e^{-}", "#mu^{-}", "#pi^{+}", "K^{+}", "p", "e^{+}", "#mu^{+}", "#pi^{-}", "K^{-}", "#bar{p}"}; -static constexpr std::string_view CspeciesAll[Npart] = {"El", "Mu", "Pi", "Ka", "Pr"}; +static constexpr o2::track::PID::ID Npart = 3; +const std::array pDGs{211, 321, 2212}; +static constexpr std::array CspeciesAll{"Pi", "Ka", "Pr"}; + // histogram naming -static constexpr std::string_view PidDir[] = {"el/", "pi/", "pr/"}; -static constexpr std::string_view V0Dir[] = {"Ga/", "K0s/", "La/", "ALa/"}; -static constexpr std::string_view Ccharge[] = {"all/", "pos/", "neg/"}; +static constexpr std::array PidDir{"el/", "pi/", "pr/"}; +static constexpr std::array V0Dir{"Ga/", "K0s/", "La/", "ALa/"}; +static constexpr std::array Ccharge{"all/", "pos/", "neg/"}; static constexpr std::string_view Cprefix = "Tracks/"; static constexpr std::string_view CprefixCleanTof = "Tracks/CleanTof/"; static constexpr std::string_view CprefixCleanV0 = "Tracks/CleanV0/"; static constexpr std::string_view CprefixV0qa = "Tracks/V0qa/"; -static constexpr std::string_view Cstatus[] = {"preSel/", "postSel/"}; -static constexpr std::string_view CstatCalib[] = {"preCalib/", "postCalib/"}; +static constexpr std::array Cstatus{"preSel/", "postSel/"}; +static constexpr std::array CstatCalib{"preCalib/", "postCalib/"}; static constexpr std::string_view CdEdxMcRecPrim = "/hdEdxMcRecPrim"; static constexpr std::string_view CdEdxMcRecPrimF = "Tracks/{}/hdEdxMcRecPrim"; static constexpr std::string_view CdEdxMcRecPrimSel = "/hdEdxMcRecPrimSel"; static constexpr std::string_view CdEdxMcRecPrimSelF = "Tracks/{}/hdEdxMcRecPrimSel"; +static constexpr std::string_view CEtaVsPtVsPMcRecPrimSel = "/hEtaVsPtVsPMcRecPrimSel"; +static constexpr std::string_view CEtaVsPtVsPMcRecPrimSelF = "Tracks/{}/hEtaVsPtVsPMcRecPrimSel"; static constexpr std::string_view CpTvsDCAxy = "/hPtVsDCAxy"; static constexpr std::string_view CpTvsDCAxyF = "Tracks/{}/hPtVsDCAxy"; static constexpr std::string_view CpTvsDCAxyAll = "/hPtVsDCAxyAll"; @@ -177,6 +171,7 @@ enum TrkSel { trkSelEta, trkSelPt, trkSelDCA, + trkSelCustomDCA, trkNRowsTPC, trkSelNClsFound, trkSelNClsPID, @@ -190,7 +185,6 @@ enum V0Sel { v0SelRejectV0sAtTPCSector, v0SelCosPA, v0SelV0radius, - v0SelDCAposToPV, v0SelDaughters, v0SelDCAv0daughter, nV0Sel @@ -211,202 +205,203 @@ enum EvtSel { nEvtSel }; +enum class MCEventStatus { + kMcEvtAll = 0, + kEvtSelected, + kEvtHasMcColl, + kEvtSplitVtxRemoved, + nEvtMcEventStatus +}; + struct MultE { static constexpr int CnoMult = 0; - static constexpr int CmultFT0M = 1; - static constexpr int CmultTPC = 2; + static constexpr int CmultFT0C = 1; + static constexpr int CmultFT0M = 2; + static constexpr int CmultTPC = 3; }; - -std::array rhoLatticeFV0{0}; -std::array fv0AmplitudeWoCalib{0}; -std::array, NpartChrg> hPtGenRecEvt{}; -std::array, NpartChrg> hPtGenPrimRecEvt{}; -std::array, NpartChrg> hPtGenRecEvtGtZero{}; -std::array, NpartChrg> hPtGenPrimRecEvtGtZero{}; -std::array, NpartChrg> hPtEffRec{}; -std::array, NpartChrg> hPtEffGen{}; -std::array, NpartChrg> hPtEffRecGoodCollPrim{}; -std::array, NpartChrg> hPtEffRecGoodCollWeak{}; -std::array, NpartChrg> hPtEffRecGoodCollMat{}; -std::array, NpartChrg> hPtEffGenPrim{}; -std::array, NpartChrg> hPtEffGenWeak{}; -std::array, NpartChrg> hPtEffGenMat{}; -std::array, NpartChrg> hPtEffGenPrimEvtSelGen{}; -std::array, NpartChrg> hPtEffGenWeakEvtSelGen{}; -std::array, NpartChrg> hPtEffGenMatEvtSelGen{}; - -std::array, NpartChrg> hDCAxyRecBadCollPrim{}; -std::array, NpartChrg> hDCAxyRecBadCollWeak{}; -std::array, NpartChrg> hDCAxyRecBadCollMat{}; -std::array, NpartChrg> hPtVsDCAxyRecGoodCollPrim{}; -std::array, NpartChrg> hPtVsDCAxyRecGoodCollWeak{}; -std::array, NpartChrg> hPtVsDCAxyRecGoodCollMat{}; - +/* +template +concept hasFT0C = requires(C::iterator const& c) { + c.centFT0C(); +}; +template +concept hasFT0M = requires(C::iterator const& c) { + c.centFT0M(); +}; +*/ struct FlattenictyPikp { HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - OutputObj listEfficiency{"Efficiency"}; - Service pdg; + Service pdg{}; - std::vector fv0AmplCorr{}; + std::vector fv0AmplCorr; TProfile2D* zVtxMap = nullptr; - float magField; + float magField{0.}; int runNumber{-1}; o2::parameters::GRPMagField* grpmag = nullptr; - Configurable multEst{"multEst", 1, "0: without multiplicity; 1: MultFT0M; 2: MultTPC"}; - Configurable applyCalibGain{"applyCalibGain", false, "equalize detector amplitudes"}; - Configurable applyCalibVtx{"applyCalibVtx", false, "equalize Amp vs vtx"}; - Configurable applyCalibDeDx{"applyCalibDeDx", false, "calibration of dedx signal"}; - Configurable applyCalibDeDxFromCCDB{"applyCalibDeDxFromCCDB", false, "use CCDB-based calibration of dedx signal"}; - Configurable cfgFillTrackQaHist{"cfgFillTrackQaHist", false, "fill track QA histograms"}; - Configurable cfgFillNclVsPhiCutQaHist{"cfgFillNclVsPhiCutQaHist", false, "fill TPC cluster vs geometrical cut QA histograms"}; - Configurable cfgFilldEdxCalibHist{"cfgFilldEdxCalibHist", false, "fill dEdx calibration histograms"}; - Configurable cfgFilldEdxQaHist{"cfgFilldEdxQaHist", false, "fill dEdx QA histograms"}; - Configurable cfgFillDCAxyHist{"cfgFillDCAxyHist", false, "fill nsigma QA histograms"}; - Configurable cfgFillV0Hist{"cfgFillV0Hist", false, "fill V0 histograms"}; - Configurable cfgFillChrgType{"cfgFillChrgType", false, "fill histograms per charge types"}; - Configurable cfgFillChrgTypeV0s{"cfgFillChrgTypeV0s", false, "fill V0s histograms per charge types"}; - Configurable cfgCalibDeDxFunction{"cfgCalibDeDxFunction", "pol8", "Functional form for dEdx calibration"}; - Configurable> paramsFuncMIPposEtaP{"paramsFuncMIPposEtaP", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncMIPnegEtaP{"paramsFuncMIPnegEtaP", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncMIPallEtaP{"paramsFuncMIPallEtaP", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncMIPposEtaN{"paramsFuncMIPposEtaN", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncMIPnegEtaN{"paramsFuncMIPnegEtaN", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncMIPallEtaN{"paramsFuncMIPallEtaN", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncPlateaUposEtaP{"paramsFuncPlateaUposEtaP", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncPlateaUnegEtaP{"paramsFuncPlateaUnegEtaP", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncPlateaUallEtaP{"paramsFuncPlateaUallEtaP", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncPlateaUposEtaN{"paramsFuncPlateaUposEtaN", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncPlateaUnegEtaN{"paramsFuncPlateaUnegEtaN", std::vector{-1.f}, "function parameters"}; - Configurable> paramsFuncPlateaUallEtaN{"paramsFuncPlateaUallEtaN", std::vector{-1.f}, "function parameters"}; - Configurable cfgGainEqCcdbPath{"cfgGainEqCcdbPath", "Users/g/gbencedi/flattenicity/GainEq", "CCDB path for gain equalization constants"}; - Configurable cfgVtxEqCcdbPath{"cfgVtxEqCcdbPath", "Users/g/gbencedi/flattenicity/ZvtxEq", "CCDB path for z-vertex equalization constants"}; - Configurable cfgDeDxCalibCcdbPath{"cfgDeDxCalibCcdbPath", "Users/g/gbencedi/flattenicity/dEdxCalib", "CCDB path for dEdx calibration"}; - Configurable cfgUseCcdbForRun{"cfgUseCcdbForRun", true, "Get ccdb object based on run number instead of timestamp"}; - Configurable cfgStoreThnSparse{"cfgStoreThnSparse", false, "Store histograms as THnSparse"}; + struct : ConfigurableGroup { + Configurable multEst{"multEst", 2, "0: without multiplicity; 1: MultFT0C; 2: MultFT0M; 3: MultTPC"}; + Configurable applyCalibGainFromCCDB{"applyCalibGainFromCCDB", false, "equalize detector amplitudes"}; + Configurable applyCalibVtxFromCCDB{"applyCalibVtxFromCCDB", false, "equalize Amp vs vtx"}; + Configurable applyCalibDeDx{"applyCalibDeDx", false, "calibration of dedx signal"}; + Configurable applyCalibDeDxFromCCDB{"applyCalibDeDxFromCCDB", false, "use CCDB-based calibration of dedx signal"}; + Configurable applyDCAParam{"applyDCAParam", false, "use parametrization of DCAxy and DCAz"}; + Configurable applyDCAParamFromCCDB{"applyDCAParamFromCCDB", false, "use CCDB-based parametrization of DCAxy and DCAz"}; + Configurable fillTrackQaHist{"fillTrackQaHist", false, "fill track QA histograms"}; + Configurable fillNclVsPhiCutQaHist{"fillNclVsPhiCutQaHist", false, "fill TPC cluster vs geometrical cut QA histograms"}; + Configurable filldEdxCalibHist{"filldEdxCalibHist", false, "fill dEdx calibration histograms"}; + Configurable filldEdxQaHist{"filldEdxQaHist", false, "fill dEdx QA histograms"}; + Configurable fillDCAxyHist{"fillDCAxyHist", false, "fill nsigma QA histograms"}; + Configurable fillV0Hist{"fillV0Hist", false, "fill V0 histograms"}; + Configurable fillChrgType{"fillChrgType", false, "fill histograms per charge types"}; + Configurable fillChrgTypeV0s{"fillChrgTypeV0s", false, "fill V0s histograms per charge types"}; + Configurable calibDeDxFunction{"calibDeDxFunction", "pol8", "Functional form for dEdx calibration"}; + Configurable> paramsFuncMIPposEtaP{"paramsFuncMIPposEtaP", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncMIPnegEtaP{"paramsFuncMIPnegEtaP", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncMIPallEtaP{"paramsFuncMIPallEtaP", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncMIPposEtaN{"paramsFuncMIPposEtaN", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncMIPnegEtaN{"paramsFuncMIPnegEtaN", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncMIPallEtaN{"paramsFuncMIPallEtaN", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncPlateaUposEtaP{"paramsFuncPlateaUposEtaP", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncPlateaUnegEtaP{"paramsFuncPlateaUnegEtaP", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncPlateaUallEtaP{"paramsFuncPlateaUallEtaP", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncPlateaUposEtaN{"paramsFuncPlateaUposEtaN", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncPlateaUnegEtaN{"paramsFuncPlateaUnegEtaN", std::vector{-1.f}, "function parameters"}; + Configurable> paramsFuncPlateaUallEtaN{"paramsFuncPlateaUallEtaN", std::vector{-1.f}, "function parameters"}; + Configurable gainEqCcdbPath{"gainEqCcdbPath", "Users/g/gbencedi/flattenicity/GainEq", "CCDB path for gain equalization constants"}; + Configurable vtxEqCcdbPath{"vtxEqCcdbPath", "Users/g/gbencedi/flattenicity/ZvtxEq", "CCDB path for z-vertex equalization constants"}; + Configurable dEdxCalibCcdbPath{"dEdxCalibCcdbPath", "Users/g/gbencedi/flattenicity/dEdxCalib", "CCDB path for dEdx calibration"}; + Configurable dcaParamCcdbPath{"dcaParamCcdbPath", "Users/g/gbencedi/flattenicity/DCAParam", "CCDB path for DCAxy and DCAz parametrization"}; + Configurable storeThnSparse{"storeThnSparse", true, "Store histograms as THnSparse"}; + } defOpt; struct : ConfigurableGroup { - Configurable cfgCustomTVX{"cfgCustomTVX", false, "Ask for custom TVX instead of sel8"}; - Configurable cfgRemoveNoTimeFrameBorder{"cfgRemoveNoTimeFrameBorder", false, "Bunch crossing is far from Time Frame borders"}; - Configurable cfgRemoveITSROFrameBorder{"cfgRemoveITSROFrameBorder", false, "Bunch crossing is far from ITS RO Frame border"}; - Configurable cfgCutVtxZ{"cfgCutVtxZ", 10.0f, "Accepted z-vertex range"}; - Configurable useZVtxCutMC{"useZVtxCutMC", true, "use Zvtx cut in MC"}; + Configurable customTVX{"customTVX", false, "Ask for custom TVX instead of sel8"}; + Configurable removeNoTimeFrameBorder{"removeNoTimeFrameBorder", false, "Bunch crossing is far from Time Frame borders"}; + Configurable removeITSROFrameBorder{"removeITSROFrameBorder", false, "Bunch crossing is far from ITS RO Frame border"}; + Configurable cutVtxZ{"cutVtxZ", 10.0f, "Accepted z-vertex range"}; + Configurable zVtxCutMC{"zVtxCutMC", true, "use Zvtx cut in MC"}; Configurable useINELCutMC{"useINELCutMC", true, "use INEL>0 cut in MC"}; - Configurable cfgRemoveNoSameBunchPileup{"cfgRemoveNoSameBunchPileup", true, "Reject collisions in case of pileup with another collision in the same foundBC"}; - Configurable cfgRequireIsGoodZvtxFT0vsPV{"cfgRequireIsGoodZvtxFT0vsPV", true, "Small difference between z-vertex from PV and from FT0"}; - Configurable cfgRequireIsVertexITSTPC{"cfgRequireIsVertexITSTPC", false, "At least one ITS-TPC track (reject vertices built from ITS-only tracks)"}; - Configurable cfgRequirekIsVertexTOFmatched{"cfgRequirekIsVertexTOFmatched", false, "Require kIsVertexTOFmatched: at least one of vertex contributors is matched to TOF"}; + Configurable removeNoSameBunchPileup{"removeNoSameBunchPileup", false, "Reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable requireIsGoodZvtxFT0vsPV{"requireIsGoodZvtxFT0vsPV", false, "Small difference between z-vertex from PV and from FT0"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "At least one ITS-TPC track (reject vertices built from ITS-only tracks)"}; + Configurable requirekIsVertexTOFmatched{"requirekIsVertexTOFmatched", false, "Require kIsVertexTOFmatched: at least one of vertex contributors is matched to TOF"}; Configurable useMultMCmidrap{"useMultMCmidrap", true, "use generated Nch in ∣eta∣ < 0.8"}; - Configurable cfgUseInelgt0wTVX{"cfgUseInelgt0wTVX", true, "Use INEL > 0 condition with TVX trigger, i.e. FT0A and FT0C acceptance"}; - Configurable cfgRemoveSplitVertex{"cfgRemoveSplitVertex", true, "Remove split vertices"}; + Configurable useInelgt0wTVX{"useInelgt0wTVX", true, "Use INEL > 0 condition with TVX trigger, i.e. FT0A and FT0C acceptance"}; + Configurable removeSplitVertex{"removeSplitVertex", true, "Remove split vertices"}; + // Configurable customGenCent{"customGenCent", false, "Use custom generated MC centrality estimation"}; } evtSelOpt; struct : ConfigurableGroup { - ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.2, 3.4, 3.6, 3.8, 4, 4.4, 4.8, 5.2, 5.6, 6, 6.5, 7, 7.5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}, "pT binning"}; - ConfigurableAxis axisPtV0s{"axisPtV0s", {VARIABLE_WIDTH, 0, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.2, 3.4, 3.6, 3.8, 4, 4.4, 4.8, 5.2, 5.6, 6, 6.4, 6.8, 7.2, 7.6, 8, 8.4, 8.8, 9.2, 9.6, 10, 10.5, 11, 11.5, 12, 12.5, 13, 13.5, 14, 14.5, 15, 20}, "pT V0s binning"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2.2, 2.4, 2.6, 2.8, 3, 3.2, 3.4, 3.6, 3.8, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50}, "pT binning"}; + ConfigurableAxis axisPtV0s{"axisPtV0s", {VARIABLE_WIDTH, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 9, 12, 15, 18, 24, 30}, "pT V0s binning"}; + ConfigurableAxis axisPtFine{"axisPtFine", {250, 0.1f, 20.1f}, "pT fine binning"}; ConfigurableAxis axisFlatPerc{"axisFlatPerc", {102, -0.01, 1.01}, "Flattenicity percentiles binning"}; - ConfigurableAxis axisMultPerc{"axisMultPerc", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "T0 percentiles binning"}; + ConfigurableAxis axisMultPerc{"axisMultPerc", {VARIABLE_WIDTH, 0., 1., 5., 10., 15., 20., 30., 40., 50., 70., 100.}, "Multiplicity percentiles binning"}; ConfigurableAxis axisVertexZ{"axisVertexZ", {80, -20., 20.}, "Vertex z binning"}; ConfigurableAxis axisMult{"axisMult", {301, -0.5, 300.5}, "Multiplicity binning"}; - ConfigurableAxis axisDCAxy{"axisDCAxy", {200, -5., 5.}, "DCAxy binning"}; - ConfigurableAxis axisDCAz{"axisDCAz", {200, -5., 5.}, "DCAz binning"}; + ConfigurableAxis axisDCAxy{"axisDCAxy", {210, -1.05f, 1.05f}, "DCAxy binning"}; + ConfigurableAxis axisDCAz{"axisDCAz", {210, -1.05f, 1.05f}, "DCAz binning"}; ConfigurableAxis axisPhi = {"axisPhi", {60, 0, constants::math::TwoPI}, "#varphi binning"}; ConfigurableAxis axisPhiMod = {"axisPhiMod", {100, 0, constants::math::PI / 9}, "fmod(#varphi,#pi/9)"}; ConfigurableAxis axisEta = {"axisEta", {50, -1.0, 1.0}, "#eta binning"}; ConfigurableAxis axisRapidity = {"axisRapidity", {50, -1.0, 1.0}, "#it{y} binning"}; - ConfigurableAxis axisDedx{"axisDedx", {100, 0, 100}, "dE/dx binning"}; - ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {200, -10, 10}, "nsigmaTPC binning"}; - ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10, 10}, "nsigmaTOF binning"}; + ConfigurableAxis axisDedx{"axisDedx", {200, 0, 200}, "dE/dx binning"}; + ConfigurableAxis axisNsigmaTPC{"axisNsigmaTPC", {200, -10.0f, 10.0f}, "nsigmaTPC binning"}; + ConfigurableAxis axisNsigmaTOF{"axisNsigmaTOF", {200, -10.0f, 10.0f}, "nsigmaTOF binning"}; ConfigurableAxis axisAmplFV0{"axisAmplFV0", {4096, 0, 4096}, "FV0 amplitude (ADC) binning"}; - ConfigurableAxis axisAmplFV0Sum{"axisAmplFV0Sum", {4096, 0, 4096 * 49}, "FV0 amplitude sum (ADC) binning"}; + ConfigurableAxis axisAmplFV0Sum{"axisAmplFV0Sum", {4096, 0, 4096}, "FV0 amplitude sum (ADC) binning"}; ConfigurableAxis axisChannelFV0{"axisChannelFV0", {49, 0., 49.}, "FV0 channel ID binning"}; } binOpt; struct : ConfigurableGroup { - Configurable cfgTrkEtaMax{"cfgTrkEtaMax", 0.8f, "Eta range for tracks"}; - Configurable cfgRapMax{"cfgRapMax", 0.5f, "Maximum range of rapidity for tracks"}; - Configurable cfgTrkPtMin{"cfgTrkPtMin", 0.1f, "Minimum pT of tracks"}; - Configurable cfgApplyNcl{"cfgApplyNcl", false, "Apply cut on TPC clusters"}; - Configurable cfgNclTPCMin{"cfgNclTPCMin", 135.0f, "Minimum of number of TPC found clusters"}; - Configurable cfgApplyNclPID{"cfgApplyNclPID", true, "Apply cut on TPC PID clusters"}; - Configurable cfgNclPidTPCMin{"cfgNclPidTPCMin", 135.0f, "Minimum of number of TPC PID clusters"}; - Configurable cfgPhiCutPtMin{"cfgPhiCutPtMin", 2.0f, "Minimum pT for phi cut"}; - Configurable cfgTOFBetaPion{"cfgTOFBetaPion", 1.0f, "Minimum beta for TOF pions"}; - Configurable cfgTofBetaPiMax{"cfgTofBetaPiMax", 5E-5, "Maximum beta for TOF pion selection"}; - Configurable cfgRejectTrkAtTPCSector{"cfgRejectTrkAtTPCSector", true, "Reject tracks close to the TPC sector boundaries"}; - Configurable cfgGeoTrkCutMin{"cfgGeoTrkCutMin", "0.06/x+pi/18.0-0.06", "ROOT TF1 formula for minimum phi cut in TPC"}; - Configurable cfgGeoTrkCutMax{"cfgGeoTrkCutMax", "0.1/x+pi/18.0+0.06", "ROOT TF1 formula for maximum phi cut in TPC"}; - Configurable cfgMomMIPMax{"cfgMomMIPMax", 0.6f, "Maximum momentum of MIP pions"}; - Configurable cfgMomMIPMin{"cfgMomMIPMin", 0.4f, "Minimum momentum of MIP pions"}; - Configurable cfgDeDxMIPMax{"cfgDeDxMIPMax", 60.0f, "Maximum range of MIP dedx"}; - Configurable cfgDeDxMIPMin{"cfgDeDxMIPMin", 40.0f, "Maximum range of MIP dedx"}; - Configurable cfgNsigmaMax{"cfgNsigmaMax", 100.0f, "Maximum range of nsgima for tracks"}; - Configurable cfgDcaNsigmaCombinedMax{"cfgDcaNsigmaCombinedMax", 3.0f, "Maximum range of combined nsgima of tracks for DCA"}; - Configurable cfgMomSelPiTOF{"cfgMomSelPiTOF", 0.4f, "Minimum momentum cut for TOF pions"}; - Configurable cfgNsigSelKaTOF{"cfgNsigSelKaTOF", 3.0f, "Nsigma cut for TOF kaons"}; - Configurable cfgBetaPlateuMax{"cfgBetaPlateuMax", 0.1f, "Beta max for Plateau electrons"}; + Configurable trkEtaMax{"trkEtaMax", 0.8f, "Eta range for tracks"}; + Configurable rapMax{"rapMax", 0.5f, "Maximum range of rapidity for tracks"}; + Configurable trkPtMin{"trkPtMin", 0.1f, "Minimum pT of tracks"}; + Configurable applyNcl{"applyNcl", false, "Apply cut on TPC clusters"}; + Configurable nclTPCMin{"nclTPCMin", 130, "Minimum number of TPC found clusters"}; + Configurable applyNclPID{"applyNclPID", true, "Apply cut on TPC PID clusters"}; + Configurable nclPidTPCMin{"nclPidTPCMin", 130, "Minimum number of TPC PID clusters"}; + Configurable phiCutPtMin{"phiCutPtMin", 2.0f, "Minimum pT for phi cut"}; + Configurable tofBetaPion{"tofBetaPion", 1.0f, "Minimum beta for TOF pions"}; + Configurable useSelTOFBeta{"useSelTOFBeta", true, "Apply selection of TOF Beta"}; + Configurable useSelTOFnSigma{"useSelTOFnSigma", false, "Apply Nsigma selection of TOF"}; + Configurable tofBetaPiMax{"tofBetaPiMax", 5E-5, "Maximum beta for TOF pion selection"}; + Configurable rejectTrkAtTPCSector{"rejectTrkAtTPCSector", true, "Reject tracks close to the TPC sector boundaries"}; + Configurable geoTrkCutMin{"geoTrkCutMin", "0.06/x+pi/18.0-0.06", "ROOT TF1 formula for minimum phi cut in TPC"}; + Configurable geoTrkCutMax{"geoTrkCutMax", "0.1/x+pi/18.0+0.06", "ROOT TF1 formula for maximum phi cut in TPC"}; + Configurable dEdxMIPnominal{"dEdxMIPnominal", 50.0f, "Nominal value of MIP pions"}; + Configurable momMIPMax{"momMIPMax", 0.6f, "Maximum momentum of MIP pions"}; + Configurable momMIPMin{"momMIPMin", 0.4f, "Minimum momentum of MIP pions"}; + Configurable dEdxMIPMax{"dEdxMIPMax", 60.0f, "Maximum range of MIP dedx"}; + Configurable dEdxMIPMin{"dEdxMIPMin", 40.0f, "Maximum range of MIP dedx"}; + Configurable dcaNsigmaCombinedMax{"dcaNsigmaCombinedMax", 3.0f, "Maximum range of combined nsgima of tracks for DCA"}; + Configurable momSelPiTOF{"momSelPiTOF", 0.4f, "Minimum momentum cut for TOF pions"}; + Configurable betaPlateuMax{"betaPlateuMax", 0.1f, "Beta max for Plateau electrons"}; + Configurable> dcaXY{"dcaXY", std::vector{0.1f, 0.0f, 0.0f}, "abs dcaXY selection: [0] + [1] * pT^[2]"}; + Configurable> dcaZ{"dcaZ", std::vector{0.1f, 0.0f, 0.0f}, "abs dcaZ selection: [0] + [1] * pT^[2]"}; + Configurable nsigmaDCAxy{"nsigmaDCAxy", 1.0f, "Nsigma cut on DCAxy"}; + Configurable nsigmaDCAz{"nsigmaDCAz", 1.0f, "Nsigma cut on DCAz"}; + Configurable requireGoodTOFMatch{"requireGoodTOFMatch", true, "Require good TOF match"}; } trkSelOpt; struct : ConfigurableGroup { // common selection - Configurable cfgV0TypeSel{"cfgV0TypeSel", 1, "select on a certain V0 type (leave negative if no selection desired)"}; - Configurable cfgV0Ymax{"cfgV0Ymax", 0.5f, "Maximum rapidity of V0s"}; - Configurable cfgRejectV0sAtTPCSector{"cfgRejectV0sAtTPCSector", true, "Reject V0s close to the TPC sector boundaries"}; - Configurable cfgRequireITS{"cfgRequireITS", true, "Additional cut on the ITS requirement"}; - Configurable cfgNsigmaElTPC{"cfgNsigmaElTPC", 5.0, "max nsigma of TPC for electorn"}; - Configurable cfgNsigmaPiTPC{"cfgNsigmaPiTPC", 5.0, "max nsigma of TPC for pion"}; - Configurable cfgNsigmaPrTPC{"cfgNsigmaPrTPC", 5.0, "max nsigma of TPC for proton"}; - Configurable cfgNsigmaElTOF{"cfgNsigmaElTOF", 3.0, "max nsigma of TOF for electorn"}; - Configurable cfgNsigmaPiTOF{"cfgNsigmaPiTOF", 3.0, "max nsigma of TOF for pion"}; - Configurable cfgNsigmaPrTOF{"cfgNsigmaPrTOF", 3.0, "max nsigma of TOF for proton"}; + Configurable v0TypeSel{"v0TypeSel", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable v0Ymax{"v0Ymax", 0.8f, "Maximum rapidity of V0s"}; + Configurable rejectV0sAtTPCSector{"rejectV0sAtTPCSector", true, "Reject V0s close to the TPC sector boundaries"}; + Configurable nsigmaElTPC{"nsigmaElTPC", 3.0, "max nsigma of TPC for electorn"}; + Configurable nsigmaPiTPC{"nsigmaPiTPC", 3.0, "max nsigma of TPC for pion"}; + Configurable nsigmaPrTPC{"nsigmaPrTPC", 3.0, "max nsigma of TPC for proton"}; + Configurable nsigmaElTOF{"nsigmaElTOF", 3.0, "max nsigma of TOF for electorn"}; + Configurable nsigmaPiTOF{"nsigmaPiTOF", 3.0, "max nsigma of TOF for pion"}; + Configurable nsigmaPrTOF{"nsigmaPrTOF", 3.0, "max nsigma of TOF for proton"}; ConfigurableAxis axisArmPodAlpha{"axisArmPodAlpha", {200, -1.0, 1.0}, "Armenteros-Podolanski alpha"}; ConfigurableAxis axisArmPodqT{"axisArmPodqT", {600, 0.0f, 0.3f}, "Armenteros-Podolanski qT"}; // standad parameters for V0 selection - Configurable cfgV0etamin{"cfgV0etamin", -0.8f, "min eta of V0s"}; - Configurable cfgV0etamax{"cfgV0etamax", +0.8f, "max eta of V0s"}; - Configurable cfgminNCrossedRowsTPC{"cfgminNCrossedRowsTPC", 70.f, "Additional cut on the minimum number of crossed rows in the TPC"}; - Configurable cfgApplyV0sNclFound{"cfgApplyV0sNclFound", false, "Apply cut on TPC Found clusters"}; - Configurable cfgV0NclTPCMin{"cfgV0NclTPCMin", 135.0f, "Minimum of number of TPC found clusters"}; - Configurable cfgApplyV0sNclPID{"cfgApplyV0sNclPID", true, "Apply cut on TPC PID clusters"}; - Configurable cfgV0NclPidTPCMin{"cfgV0NclPidTPCMin", 135.0f, "Minimum of number of TPC PID clusters"}; - Configurable cfgmaxChi2PerClusterTPC{"cfgmaxChi2PerClusterTPC", 4.f, "Additional cut on the maximum value of the chi2 per cluster in the TPC"}; - Configurable cfgmaxChi2PerClusterITS{"cfgmaxChi2PerClusterITS", 36.f, "Additional cut on the maximum value of the chi2 per cluster in the ITS"}; - Configurable cfgminITSnClusters{"cfgminITSnClusters", 4, "minimum number of found ITS clusters"}; - Configurable cfgminNCrossedRowsOverFindableClustersTPC{"cfgminNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; - Configurable cfgDCAv0daughter{"cfgDCAv0daughter", 1.0, "max DCA of V0 daughter tracks (cm)"}; - Configurable cfgv0cospa{"cfgv0cospa", 0.995, "min V0 CosPA"}; - Configurable cfgDCAposToPV{"cfgDCAposToPV", 0.05f, "min DCA Pos To PV (cm)"}; - Configurable cfgDCAnegToPV{"cfgDCAnegToPV", 0.05f, "min DCA Neg To PV (cm)"}; - Configurable cfgv0Rmin{"cfgv0Rmin", 1.2, "min V0 radius (cm)"}; - Configurable cfgv0Rmax{"cfgv0Rmax", 1E5, "max V0 radius (cm)"}; + Configurable v0etamin{"v0etamin", -0.8f, "min eta of V0s"}; + Configurable v0etamax{"v0etamax", +0.8f, "max eta of V0s"}; + Configurable v0ptmin{"v0ptmin", 0.1f, "min pT of V0s"}; + Configurable v0ptmax{"v0ptmax", 1e10f, "max pT of V0s"}; + Configurable v0minNCrossedRowsTPC{"v0minNCrossedRowsTPC", 70, "Additional cut on the minimum number of crossed rows in the TPC"}; + Configurable applyV0sNclFound{"applyV0sNclFound", false, "Apply cut on TPC Found clusters"}; + Configurable v0NclTPCMin{"v0NclTPCMin", 130, "Minimum number of TPC found clusters"}; + Configurable applyV0sNclPID{"applyV0sNclPID", true, "Apply cut on TPC PID clusters"}; + Configurable v0NclPidTPCMin{"v0NclPidTPCMin", 130, "Minimum number of TPC PID clusters"}; + Configurable applyV0sNclShared{"applyV0sNclShared", true, "Apply cut on TPC shared clusters"}; + Configurable v0NclSharedTPCMax{"v0NclSharedTPCMax", 5, "Maximum number of TPC shared clusters"}; + Configurable dcaV0daughter{"dcaV0daughter", 0.01, "max DCA of V0 daughter tracks (cm)"}; + Configurable v0cospa{"v0cospa", 0.999, "min V0 CosPA"}; + Configurable v0Rmin{"v0Rmin", 1.2, "min V0 radius (cm)"}; + Configurable v0Rmax{"v0Rmax", 1E5, "max V0 radius (cm)"}; // parameters for selection KOs - Configurable cfgcTauK0s{"cfgcTauK0s", 20, "v0ctau for K0s"}; - Configurable cfgCosPAK0s{"cfgCosPAK0s", 0.995, "V0 CosPA for K0s"}; - Configurable cfgV0radiusK0s{"cfgV0radiusK0s", 0.5, "v0radius for K0s"}; - Configurable cfgdmassK{"cfgdmassK", 0.01f, "Competing Mass Rejection cut for K0s"}; - Configurable cfgArmPodK0s{"cfgArmPodK0s", 5.0f, "pT * (cut) > |alpha|, Armenteros-Podolanski cut for K0s"}; - ConfigurableAxis axisK0sMass{"axisK0sMass", {200, 0.4f, 0.6f}, "K0Short mass binning"}; + Configurable cTauK0s{"cTauK0s", 20.0, "v0ctau for K0s"}; + Configurable dmassK{"dmassK", 0.01f, "Competing Mass Rejection cut for K0s"}; + Configurable armPodK0s{"armPodK0s", 5.0f, "pT * (cut) > |alpha|, Armenteros-Podolanski cut for K0s"}; + ConfigurableAxis axisK0sMass{"axisK0sMass", {80, 0.4f, 0.6f}, "K0Short mass binning"}; + Configurable v0dcaK0s{"v0dcaK0s", 0.1f, "Minimum DCA To PV (cm) for K0Short"}; // parameters for selection Lambda / antiLambda - Configurable cfgcTauLambda{"cfgcTauLambda", 30, "v0ctau for Lambda"}; - Configurable cfgCosPALambda{"cfgCosPALambda", 0.995, "V0 CosPA for Lambda"}; - Configurable cfgV0radiusLambda{"cfgV0radiusLambda", 0.5, "v0radius for Lambda"}; - Configurable cfgdmassL{"cfgdmassL", 0.01f, "Competing Mass Rejection cut for Lambda"}; - ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, "Lambda mass binning"}; + Configurable cTauLambda{"cTauLambda", 30.0, "v0ctau for Lambda"}; + Configurable dmassL{"dmassL", 0.01f, "Competing Mass Rejection cut for Lambda"}; + Configurable v0dcaProtonFromLam{"v0dcaProtonFromLam", 0.05f, "Minimum DCA To PV (cm) for Protons (from Lambda)"}; + Configurable v0dcaPionFromLam{"v0dcaPionFromLam", 0.2f, "Minimum DCA To PV (cm) for Pions (from Lambda)"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {50, 1.09f, 1.14f}, "Lambda mass binning"}; // parameters for selection Gamma - Configurable cfgdmassG{"cfgdmassG", 0.1f, "max mass for Gammas"}; - Configurable cfgArmPodGammasalpha{"cfgArmPodGammasalpha", 0.45f, "Armenteros-Podolanski alpha cut for Gammas"}; - Configurable cfgArmPodGammasqT{"cfgArmPodGammasqT", 0.01f, "Armenteros-Podolanski qT cut for Gammas"}; - ConfigurableAxis axisGammaMass{"axisGammaMass", {200, 0.0f, 0.5f}, "Gamma mass binning"}; - Configurable cfgdEdxPlateauSel{"cfgdEdxPlateauSel", 50, "dEdx selection sensitivity for electrons"}; + Configurable dmassG{"dmassG", 0.1f, "max mass for Gammas"}; + Configurable armPodGammasalpha{"armPodGammasalpha", 1.0f, "Armenteros-Podolanski alpha cut for Gammas"}; + Configurable armPodGammasqT{"armPodGammasqT", 0.1f, "Armenteros-Podolanski qT cut for Gammas"}; + ConfigurableAxis axisGammaMass{"axisGammaMass", {50, 0.0f, 0.1f}, "Gamma mass binning"}; + Configurable v0dcaElectronFromGamma{"v0dcaElectronFromGamma", 0.1f, "Minimum DCA To PV (cm) for Electrons (from Gamma)"}; + Configurable dEdxPlateauSel{"dEdxPlateauSel", 50.0f, "dEdx selection sensitivity for electrons"}; } v0SelOpt; - Service ccdb; + Service ccdb{}; struct : ConfigurableGroup { - Configurable cfgMagField{"cfgMagField", 99999, "Configurable magnetic field;default CCDB will be queried"}; + Configurable magField{"magField", 99999, "Configurable magnetic field;default CCDB will be queried"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; @@ -423,11 +418,18 @@ struct FlattenictyPikp { bool lCalibLoaded = false; } dedxcalib; + struct DCAParam { + TList* lCalibObjects = nullptr; + TH1F* hDCAxyParam = nullptr; + TH1F* hDCAzParam = nullptr; + bool lCalibLoaded = false; + } dcaparam; + struct : ConfigurableGroup { Configurable requireRCTFlagChecker{"requireRCTFlagChecker", false, "Check event quality in run condition table"}; - Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; - Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; - Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + Configurable evtRCTFlagCheckerLabel{"evtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable evtRCTFlagCheckerZDCCheck{"evtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable evtRCTFlagCheckerLimitAcceptAsBad{"evtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; } rctCuts; RCTFlagsChecker rctChecker; @@ -443,13 +445,13 @@ struct FlattenictyPikp { Configurable requireITS{"requireITS", true, "Additional cut on the ITS requirement"}; Configurable requireTPC{"requireTPC", true, "Additional cut on the TPC requirement"}; Configurable requireGoldenChi2{"requireGoldenChi2", true, "Additional cut on the GoldenChi2"}; - Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.f, "Additional cut on the minimum number of crossed rows in the TPC"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70, "Additional cut on the minimum number of crossed rows in the TPC"}; Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; Configurable maxChi2PerClusterTPC{"maxChi2PerClusterTPC", 4.f, "Additional cut on the maximum value of the chi2 per cluster in the TPC"}; Configurable maxChi2PerClusterITS{"maxChi2PerClusterITS", 36.f, "Additional cut on the maximum value of the chi2 per cluster in the ITS"}; Configurable minITSnClusters{"minITSnClusters", 5, "minimum number of found ITS clusters"}; Configurable maxDcaXYFactor{"maxDcaXYFactor", 1.f, "Multiplicative factor on the maximum value of the DCA xy"}; - Configurable maxDcaZ{"maxDcaZ", 2.f, "Additional cut on the maximum value of the DCA z"}; + Configurable maxDcaZ{"maxDcaZ", 0.1f, "Additional cut on the maximum value of the DCA z"}; TF1* fPhiCutLow = nullptr; TF1* fPhiCutHigh = nullptr; @@ -462,8 +464,9 @@ struct FlattenictyPikp { using MyCollisions = soa::Join; using Colls = soa::Join; using CollsGen = soa::Join; - using MCColls = soa::Join; - using CollsMCExtraMult = soa::Join; + using MCColls = soa::Join; + using CollsMCExtraMult = soa::Join; + // using CollsMCExtraMultPercentile = soa::Join; using CollsGenSgn = soa::SmallGroups>; using MyPIDTracks = soa::Join; using MyLabeledTracks = soa::Join; @@ -472,19 +475,19 @@ struct FlattenictyPikp { void init(InitContext&) { - auto vecParamsMIPposEtaP = (std::vector)paramsFuncMIPposEtaP; - auto vecParamsMIPposEtaN = (std::vector)paramsFuncMIPposEtaN; - auto vecParamsMIPnegEtaP = (std::vector)paramsFuncMIPnegEtaP; - auto vecParamsMIPnegEtaN = (std::vector)paramsFuncMIPnegEtaN; - auto vecParamsMIPallEtaP = (std::vector)paramsFuncMIPallEtaP; - auto vecParamsMIPallEtaN = (std::vector)paramsFuncMIPallEtaN; - - auto vecParamsPLAposEtaP = (std::vector)paramsFuncPlateaUposEtaP; - auto vecParamsPLAposEtaN = (std::vector)paramsFuncPlateaUposEtaN; - auto vecParamsPLAnegEtaP = (std::vector)paramsFuncPlateaUnegEtaP; - auto vecParamsPLAnegEtaN = (std::vector)paramsFuncPlateaUnegEtaN; - auto vecParamsPLAallEtaP = (std::vector)paramsFuncPlateaUallEtaP; - auto vecParamsPLAallEtaN = (std::vector)paramsFuncPlateaUallEtaN; + auto vecParamsMIPposEtaP = (std::vector)defOpt.paramsFuncMIPposEtaP; + auto vecParamsMIPposEtaN = (std::vector)defOpt.paramsFuncMIPposEtaN; + auto vecParamsMIPnegEtaP = (std::vector)defOpt.paramsFuncMIPnegEtaP; + auto vecParamsMIPnegEtaN = (std::vector)defOpt.paramsFuncMIPnegEtaN; + auto vecParamsMIPallEtaP = (std::vector)defOpt.paramsFuncMIPallEtaP; + auto vecParamsMIPallEtaN = (std::vector)defOpt.paramsFuncMIPallEtaN; + + auto vecParamsPLAposEtaP = (std::vector)defOpt.paramsFuncPlateaUposEtaP; + auto vecParamsPLAposEtaN = (std::vector)defOpt.paramsFuncPlateaUposEtaN; + auto vecParamsPLAnegEtaP = (std::vector)defOpt.paramsFuncPlateaUnegEtaP; + auto vecParamsPLAnegEtaN = (std::vector)defOpt.paramsFuncPlateaUnegEtaN; + auto vecParamsPLAallEtaP = (std::vector)defOpt.paramsFuncPlateaUallEtaP; + auto vecParamsPLAallEtaN = (std::vector)defOpt.paramsFuncPlateaUallEtaN; auto addVec = [&](std::vector>& targetVec, const std::string& name, bool isMIP) { if (isMIP) { @@ -494,9 +497,6 @@ struct FlattenictyPikp { targetVec.emplace_back(vecParamsMIPposEtaN); targetVec.emplace_back(vecParamsMIPnegEtaN); targetVec.emplace_back(vecParamsMIPallEtaN); - if (!vecParamsMIP.size()) { - LOG(info) << "size of " << name << "is zero."; - } } else { targetVec.emplace_back(vecParamsPLAposEtaP); targetVec.emplace_back(vecParamsPLAnegEtaP); @@ -504,9 +504,9 @@ struct FlattenictyPikp { targetVec.emplace_back(vecParamsPLAposEtaN); targetVec.emplace_back(vecParamsPLAnegEtaN); targetVec.emplace_back(vecParamsPLAallEtaN); - if (!vecParamsPLA.size()) { - LOG(info) << "size of " << name << "is zero."; - } + } + if (vecParamsMIP.empty() || vecParamsPLA.empty()) { + LOG(info) << "size of " << name << "is zero."; } }; addVec(vecParamsMIP, "vecParamsMIP", true); @@ -524,7 +524,7 @@ struct FlattenictyPikp { ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); ccdb->setFatalWhenNull(false); - rctChecker.init(rctCuts.cfgEvtRCTFlagCheckerLabel, rctCuts.cfgEvtRCTFlagCheckerZDCCheck, rctCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + rctChecker.init(rctCuts.evtRCTFlagCheckerLabel, rctCuts.evtRCTFlagCheckerZDCCheck, rctCuts.evtRCTFlagCheckerLimitAcceptAsBad); if (isCustomTracks.value) { selTrkGlobal = getGlobalTrackSelectionRun3ITSMatch(setITSreq.value); @@ -558,6 +558,7 @@ struct FlattenictyPikp { const AxisSpec phiAxisMod{binOpt.axisPhiMod, "fmod(#varphi,#pi/9)"}; const AxisSpec pAxis{binOpt.axisPt, "#it{p} (GeV/#it{c})"}; const AxisSpec ptAxis{binOpt.axisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec ptFineAxis{binOpt.axisPtFine, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec ptAxisV0s{binOpt.axisPtV0s, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec dcaXYAxis{binOpt.axisDCAxy, "DCA_{xy} (cm)"}; const AxisSpec dcaZAxis{binOpt.axisDCAz, "DCA_{z} (cm)"}; @@ -571,9 +572,12 @@ struct FlattenictyPikp { AxisSpec multAxis{binOpt.axisMultPerc, "multiplicity estimator"}; - switch (multEst) { + switch (defOpt.multEst.value) { case MultE::CnoMult: break; + case MultE::CmultFT0C: + multAxis.name = "multFT0C"; + break; case MultE::CmultFT0M: multAxis.name = "multFT0M"; break; @@ -581,12 +585,12 @@ struct FlattenictyPikp { multAxis.name = "multTPC"; break; default: - LOG(fatal) << "No valid option for mult estimator " << multEst; + LOGF(fatal, "No valid option for mult estimator %d", defOpt.multEst.value); } - if (trkSelOpt.cfgRejectTrkAtTPCSector || v0SelOpt.cfgRejectV0sAtTPCSector) { - fPhiCutLow = new TF1("fPhiCutLow", trkSelOpt.cfgGeoTrkCutMin.value.c_str(), 0, 100); - fPhiCutHigh = new TF1("fPhiCutHigh", trkSelOpt.cfgGeoTrkCutMax.value.c_str(), 0, 100); + if (trkSelOpt.rejectTrkAtTPCSector || v0SelOpt.rejectV0sAtTPCSector) { + fPhiCutLow = new TF1("fPhiCutLow", trkSelOpt.geoTrkCutMin.value.c_str(), 0, 100); + fPhiCutHigh = new TF1("fPhiCutHigh", trkSelOpt.geoTrkCutMax.value.c_str(), 0, 100); } registryQC.add("Events/hVtxZ", "Measured vertex z position", kTH1F, {vtxzAxis}); @@ -604,6 +608,17 @@ struct FlattenictyPikp { registryQC.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(evtSelVtxZ + 1, "Vtx-z pos"); registryQC.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(evtSelINELgt0 + 1, "INEL>0"); registryQC.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(evtSelRCTFlagChecker + 1, "RCT Flag Checker"); + // Track counter + registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {kTH1F, {{nTrkSel, 0, nTrkSel}}}); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelAll + 1, "All"); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelEta + 1, "Eta"); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelPt + 1, "Pt"); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelDCA + 1, "DCA"); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelCustomDCA + 1, "Custom DCA sel."); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkNRowsTPC + 1, "trkNRowsTPC"); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelNClsFound + 1, "NClsTPCFound"); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelNClsPID + 1, "NClsTPCPid"); + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelTPCBndr + 1, "TPC Boundary"); // Number of tracks vs centrality registryQC.add("Events/hNchVsCent", "Measured Nch vs Cent; centrality; Nch (|#eta|<0.8)", {kTH2F, {nChAxis, multAxis}}); // FV0 QA @@ -613,36 +628,15 @@ struct FlattenictyPikp { registryQC.add("FV0/hFV0amp", "", {kTH2F, {channelFV0Axis, amplitudeFV0}}); registryQC.add("FV0/pFV0amp", "", kTProfile, {channelFV0Axis}); registryQC.add("FV0/hFV0ampCorr", "", {kTH2F, {channelFV0Axis, amplitudeFV0}}); + registryQC.add("FV0/pFV0ampCorr", "", kTProfile, {channelFV0Axis}); LOG(info) << "Size of the QC histograms:"; registryQC.print(); if (doprocessFlat) { - // Track counter - registryData.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {kTH1F, {{nTrkSel, 0, nTrkSel}}}); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelAll + 1, "All"); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelEta + 1, "Eta"); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelPt + 1, "Pt"); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelDCA + 1, "DCA"); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkNRowsTPC + 1, "trkNRowsTPC"); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelNClsFound + 1, "NClsTPCFound"); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelNClsPID + 1, "NClsTPCPid"); - registryData.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(trkSelTPCBndr + 1, "TPC Boundary"); - // V0 counter - registryData.add("Tracks/V0qa/hV0Sel", "Number of V0s; Cut; #Tracks Passed Cut", {kTH1F, {{nV0Sel, 0, nV0Sel}}}); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelAll + 1, "All"); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelRejectSameSign + 1, "Reject same sign"); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelRejectV0sAtTPCSector + 1, "Reject V0s at TPC sector"); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelCosPA + 1, "Cos PA"); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelV0radius + 1, "V0 radius"); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelDCAposToPV + 1, "DCA pos to PV"); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelDaughters + 1, "V0 daughters' sel."); - registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelDCAv0daughter + 1, "DCA v0 daughter"); - registryData.add("Events/hFlatVsMultEst", "hFlatVsMultEst", kTH2F, {flatAxis, multAxis}); - registryData.add("Tracks/postSel/hPVsPtEta", "; #it{p} (GeV/#it{c}); #it{p}_{T} (GeV/#it{c}); #eta;", {kTH3F, {pAxis, ptAxis, etaAxis}}); - if (cfgFillNclVsPhiCutQaHist || cfgFillTrackQaHist || cfgFilldEdxQaHist || cfgFillDCAxyHist) { - if (cfgFillNclVsPhiCutQaHist) { + if (defOpt.fillNclVsPhiCutQaHist || defOpt.fillTrackQaHist || defOpt.filldEdxQaHist || defOpt.fillDCAxyHist) { + if (defOpt.fillNclVsPhiCutQaHist) { registryData.add("Tracks/postSel/hPtPhi", "; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9)", {kTH2F, {ptAxis, phiAxisMod}}); registryData.add("Tracks/postSel/hPtPhiNclTPC", "; #{eta}; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9); N_{cluster}", {kTHnSparseF, {etaAxis, ptAxis, phiAxisMod, clTpcAxis}}); registryData.add("Tracks/postSel/hPtPhiNclPIDTPC", "; #{eta}; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9); N_{PID cluster}", {kTHnSparseF, {etaAxis, ptAxis, phiAxisMod, clTpcAxis}}); @@ -651,19 +645,21 @@ struct FlattenictyPikp { registryData.add("Tracks/postSel/hPtNclPIDTPC", "; #it{p}_{T} (GeV/#it{c}); N_{PID cluster}", {kTH2F, {ptAxis, clTpcAxis}}); registryData.add("Tracks/postSel/pPtNclPIDTPC", "; #it{p}_{T} (GeV/#it{c}); N_{PID cluster}", {kTProfile, {ptAxis}}); } - if (cfgFillTrackQaHist) { + if (defOpt.fillTrackQaHist) { registryData.add("Tracks/postSel/hShTpcClvsPt", "", {kTH2F, {ptAxis, shCluserAxis}}); registryData.add("Tracks/postSel/hNclTPCFoundvsPt", "", {kTH2F, {ptAxis, clTpcAxis}}); registryData.add("Tracks/postSel/hNClTPCPidvsPt", "", {kTH2F, {ptAxis, clTpcAxis}}); registryData.add("Tracks/postSel/hNclTPCFoundvsEta", "", {kTH2F, {etaAxis, clTpcAxis}}); registryData.add("Tracks/postSel/hNClTPCPidvsEta", "", {kTH2F, {etaAxis, clTpcAxis}}); registryData.add("Tracks/postSel/hTPCCluster", "N_{cluster}", kTH1F, {clTpcAxis}); - registryData.add("Tracks/postSel/hPtVsWOcutDCA", "hPtVsWOcutDCA", kTH2F, {ptAxis, dcaXYAxis}); + registryData.add("Tracks/postSel/hPtVsWOcutDCA", "hPtVsWOcutDCA", kTH2F, {ptFineAxis, dcaXYAxis}); registryData.add("Tracks/postSel/hPt", "", kTH1F, {ptAxis}); registryData.add("Tracks/postSel/hPhi", "", kTH1F, {phiAxis}); registryData.add("Tracks/postSel/hEta", "", kTH1F, {etaAxis}); - registryData.add("Tracks/postSel/hDCAXYvsPt", "", kTH2F, {ptAxis, dcaXYAxis}); - registryData.add("Tracks/postSel/hDCAZvsPt", "", kTH2F, {ptAxis, dcaZAxis}); + registryData.add("Tracks/postSel/hPVsPtEta", "; #it{p} (GeV/#it{c}); #it{p}_{T} (GeV/#it{c}); #eta;", {kTHnSparseF, {pAxis, ptAxis, etaAxis}}); + registryData.add("Tracks/postSel/hTpcInnerParamVsP", ";Global track p (GeV/#it{c});Track p at inner wall of the TPC (GeV/#it{c});", kTH2F, {pAxis, pAxis}); + registryData.add("Tracks/postSel/hDCAXYvsPt", "", kTH2F, {ptFineAxis, dcaXYAxis}); + registryData.add("Tracks/postSel/hDCAZvsPt", "", kTH2F, {ptFineAxis, dcaZAxis}); // tpc registryData.add("Tracks/postSel/hTPCnClsShared", " ; # shared TPC clusters TPC", kTH1F, {{165, -0.5, 164.5}}); registryData.add("Tracks/postSel/hTPCcrossedRows", " ; # crossed TPC rows", kTH1F, {{165, -0.5, 164.5}}); @@ -675,8 +671,8 @@ struct FlattenictyPikp { registryData.add("Tracks/postSel/hTOFPvsBeta", "Beta from TOF; #it{p} (GeV/#it{c}); #beta", {kTH2F, {pAxis, {120, 0.0, 1.2}}}); registryData.add("Tracks/postSel/hTOFpi", "Primary Pions from TOF; #eta; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, pAxis, dEdxAxis}}); } - if (cfgFilldEdxQaHist) { - if (cfgStoreThnSparse) { + if (defOpt.filldEdxQaHist) { + if (defOpt.storeThnSparse) { registryData.add("Tracks/postCalib/all/hMIP", "; mult; flat; #eta; #LT dE/dx #GT_{MIP, primary tracks};", {kTHnSparseF, {multAxis, flatAxis, etaAxis, dEdxAxis}}); registryData.add("Tracks/postCalib/all/hPlateau", "; mult; flat; #eta; #LT dE/dx #GT_{Plateau, primary tracks};", {kTHnSparseF, {multAxis, flatAxis, etaAxis, dEdxAxis}}); } else { @@ -685,65 +681,91 @@ struct FlattenictyPikp { } registryData.add("Tracks/postCalib/all/hMIPVsPhi", "; #varphi; #LT dE/dx #GT_{MIP, primary tracks};", {kTH2F, {phiAxis, dEdxAxis}}); registryData.add("Tracks/postCalib/all/pMIPVsPhi", "; #varphi; #LT dE/dx #GT_{MIP, primary tracks};", {kTProfile, {phiAxis}}); - registryData.add("Tracks/postCalib/all/hMIPVsPhiVsEta", "; #varphi; #LT dE/dx #GT_{MIP, primary tracks}; #eta;", {kTH3F, {phiAxis, dEdxAxis, etaAxis}}); + registryData.add("Tracks/postCalib/all/hMIPVsPhiVsEta", "; #varphi; #LT dE/dx #GT_{MIP, primary tracks}; #eta;", {kTHnSparseF, {phiAxis, dEdxAxis, etaAxis}}); + registryData.add("Tracks/postCalib/all/hMIPNClTPCPidvsEta", ";#eta; Ncl pid", {kTH2F, {etaAxis, clTpcAxis}}); registryData.add("Tracks/postCalib/all/hPlateauVsPhi", "; #varphi; #LT dE/dx #GT_{Plateau, primary tracks};", {kTH2F, {phiAxis, dEdxAxis}}); registryData.add("Tracks/postCalib/all/pPlateauVsPhi", "; #varphi; #LT dE/dx #GT_{Plateau, primary tracks};", {kTProfile, {phiAxis}}); - registryData.add("Tracks/postCalib/all/hPlateauVsPhiVsEta", "; #varphi; #LT dE/dx #GT_{Plateau, primary tracks}; #eta;", {kTH3F, {phiAxis, dEdxAxis, etaAxis}}); + registryData.add("Tracks/postCalib/all/hPlateauVsPhiVsEta", "; #varphi; #LT dE/dx #GT_{Plateau, primary tracks}; #eta;", {kTHnSparseF, {phiAxis, dEdxAxis, etaAxis}}); + registryData.add("Tracks/postCalib/all/hPlateauNClTPCPidvsEta", ";#eta; Ncl pid", {kTH2F, {etaAxis, clTpcAxis}}); registryData.addClone("Tracks/postCalib/all/", "Tracks/preCalib/all/"); - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { registryData.addClone("Tracks/postCalib/all/", "Tracks/postCalib/pos/"); registryData.addClone("Tracks/postCalib/all/", "Tracks/postCalib/neg/"); registryData.addClone("Tracks/preCalib/all/", "Tracks/preCalib/pos/"); registryData.addClone("Tracks/preCalib/all/", "Tracks/preCalib/neg/"); } } - if (cfgFillDCAxyHist) { + if (defOpt.fillDCAxyHist) { for (int i = 0; i < Npart; i++) { - registryData.add({fmt::format(CpTvsDCAxyF.data(), CspeciesAll[i]).c_str(), "; mult; flat; #it{p} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); + registryData.add({fmt::format(CpTvsDCAxyF.data(), CspeciesAll[i].data()).c_str(), "; mult; flat; #it{p} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); } } } registryData.addClone("Tracks/postSel/", "Tracks/preSel/"); - // V0's QA - registryData.add("Tracks/V0qa/hV0Pt", "pT", kTH1F, {ptAxisV0s}); - registryData.add("Tracks/V0qa/hV0ArmPod", ";#alpha; #it{q}_T (GeV/c)", kTH2F, {v0SelOpt.axisArmPodAlpha, v0SelOpt.axisArmPodqT}); - // daughters' QA - registryData.add("Tracks/V0qa/el/Ga/hArmPod", ";#alpha; #it{q}_T (GeV/c)", kTH2F, {v0SelOpt.axisArmPodAlpha, v0SelOpt.axisArmPodqT}); - registryData.add("Tracks/V0qa/pi/K0s/hArmPod", ";#alpha; #it{q}_T (GeV/c)", kTH2F, {v0SelOpt.axisArmPodAlpha, v0SelOpt.axisArmPodqT}); - registryData.add("Tracks/V0qa/el/Ga/hNclVsEta", ";#eta; #it{N}^{TPC}_cl", kTH2F, {etaAxis, clTpcAxis}); - registryData.add("Tracks/V0qa/pi/K0s/hNclVsEta", ";#eta; #it{N}^{TPC}_cl", kTH2F, {etaAxis, clTpcAxis}); - registryData.add("Tracks/V0qa/el/Ga/hdEdxMIPVsEta", ";#eta; dE/dx", kTH2F, {etaAxis, dEdxAxis}); - registryData.add("Tracks/V0qa/pi/K0s/hdEdxMIPVsEta", ";#eta; dE/dx", kTH2F, {etaAxis, dEdxAxis}); - registryData.addClone("Tracks/V0qa/pi/K0s/", "Tracks/V0qa/pi/La/"); - registryData.addClone("Tracks/V0qa/pi/K0s/", "Tracks/V0qa/pi/ALa/"); - registryData.addClone("Tracks/V0qa/pi/La/", "Tracks/V0qa/pr/La/"); - registryData.addClone("Tracks/V0qa/pi/ALa/", "Tracks/V0qa/pr/ALa/"); - // dEdx PID - registryData.add({"Tracks/all/hdEdx", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); + registryData.add({"Tracks/all/hdEdx", "; #eta; mult; flat; #it{p} (GeV/#it{c}); #it{p}_{T} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, pAxis, dEdxAxis}}}); // Clean samples - if (cfgFillV0Hist) { - if (cfgStoreThnSparse) { - registryData.add({"Tracks/CleanTof/all/hPiTof", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); - registryData.add({"Tracks/CleanV0/all/hEV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); - registryData.add({"Tracks/CleanV0/all/hPiV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); - registryData.add({"Tracks/CleanV0/all/hPV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); + if (defOpt.fillV0Hist) { + // V0 counter + registryData.add("Tracks/V0qa/hV0Sel", "Number of V0s; Cut; #Tracks Passed Cut", {kTH1F, {{nV0Sel, 0, nV0Sel}}}); + registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelAll + 1, "All"); + registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelRejectSameSign + 1, "Reject same sign"); + registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelRejectV0sAtTPCSector + 1, "Reject V0s at TPC sector"); + registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelCosPA + 1, "Cos PA"); + registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelV0radius + 1, "V0 radius"); + registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelDaughters + 1, "V0 daughters' sel."); + registryData.get(HIST("Tracks/V0qa/hV0Sel"))->GetXaxis()->SetBinLabel(v0SelDCAv0daughter + 1, "DCA v0 daughter"); + // V0's QA + registryData.add("Tracks/V0qa/hV0Pt", "pT", kTH1F, {ptAxisV0s}); + registryData.add("Tracks/V0qa/hV0ArmPod", ";#alpha; #it{q}_T (GeV/c)", kTH2F, {v0SelOpt.axisArmPodAlpha, v0SelOpt.axisArmPodqT}); + // daughters' QA + registryData.add("Tracks/V0qa/hNsigmaGaVsPt", ";#it{n#sigma}; #it{p}_{T} (GeV/#it{c})", kTH2F, {ptAxisV0s, nSigmaTPCAxis}); + registryData.add("Tracks/V0qa/hNsigmaK0sVsPt", ";#it{n#sigma}; #it{p}_{T} (GeV/#it{c})", kTH2F, {ptAxisV0s, nSigmaTPCAxis}); + registryData.add("Tracks/V0qa/hNsigmaPiFromLaVsPt", ";#it{n#sigma}; #it{p}_{T} (GeV/#it{c})", kTH2F, {ptAxisV0s, nSigmaTPCAxis}); + registryData.add("Tracks/V0qa/hNsigmaPrFromLaVsPt", ";#it{n#sigma}; #it{p}_{T} (GeV/#it{c})", kTH2F, {ptAxisV0s, nSigmaTPCAxis}); + registryData.add("Tracks/V0qa/hNsigmaPiFromALaVsPt", ";#it{n#sigma}; #it{p}_{T} (GeV/#it{c})", kTH2F, {ptAxisV0s, nSigmaTPCAxis}); + registryData.add("Tracks/V0qa/hNsigmaPrFromALaVsPt", ";#it{n#sigma}; #it{p}_{T} (GeV/#it{c})", kTH2F, {ptAxisV0s, nSigmaTPCAxis}); + registryData.add("Tracks/V0qa/hMassGaVsPt", ";Inv. Mass (GeV/#it{c}^{2}); #it{p}_{T} (GeV/#it{c})", kTH2F, {v0SelOpt.axisGammaMass, ptAxisV0s}); + registryData.add("Tracks/V0qa/hMassK0sVsPt", ";Inv. Mass (GeV/#it{c}^{2}); #it{p}_{T} (GeV/#it{c})", kTH2F, {v0SelOpt.axisK0sMass, ptAxisV0s}); + registryData.add("Tracks/V0qa/hMassLaVsPt", ";Inv. Mass (GeV/#it{c}^{2}); #it{p}_{T} (GeV/#it{c})", kTH2F, {v0SelOpt.axisLambdaMass, ptAxisV0s}); + registryData.add("Tracks/V0qa/hMassALaVsPt", ";Inv. Mass (GeV/#it{c}^{2}); #it{p}_{T} (GeV/#it{c})", kTH2F, {v0SelOpt.axisLambdaMass, ptAxisV0s}); + registryData.add("Tracks/V0qa/el/Ga/hEtaVsRap", ";#eta; #it{y}", kTH2F, {etaAxis, etaAxis}); + registryData.add("Tracks/V0qa/pi/K0s/hEtaVsRap", ";#eta; #it{y}", kTH2F, {etaAxis, etaAxis}); + registryData.add("Tracks/V0qa/el/Ga/hArmPod", ";#alpha; #it{q}_T (GeV/c)", kTH2F, {v0SelOpt.axisArmPodAlpha, v0SelOpt.axisArmPodqT}); + registryData.add("Tracks/V0qa/pi/K0s/hArmPod", ";#alpha; #it{q}_T (GeV/c)", kTH2F, {v0SelOpt.axisArmPodAlpha, v0SelOpt.axisArmPodqT}); + registryData.add("Tracks/V0qa/el/Ga/hNclVsEta", ";#eta; #it{N}^{TPC}_cl", kTH2F, {etaAxis, clTpcAxis}); + registryData.add("Tracks/V0qa/pi/K0s/hNclVsEta", ";#eta; #it{N}^{TPC}_cl", kTH2F, {etaAxis, clTpcAxis}); + registryData.add("Tracks/V0qa/el/Ga/hNclVsPt", ";#it{p}_{T} (GeV/#it{c}); #it{N}^{TPC}_cl", kTH2F, {ptAxis, clTpcAxis}); + registryData.add("Tracks/V0qa/pi/K0s/hNclVsPt", ";#it{p}_{T} (GeV/#it{c}); #it{N}^{TPC}_cl", kTH2F, {ptAxis, clTpcAxis}); + registryData.add({"Tracks/V0qa/el/Ga/hdEdxVsPVsEta", ";#eta; dE/dx", {kTHnSparseF, {dEdxAxis, ptAxisV0s, etaAxis}}}); + registryData.add({"Tracks/V0qa/pi/K0s/hdEdxVsPVsEta", ";#eta; dE/dx", {kTHnSparseF, {dEdxAxis, ptAxisV0s, etaAxis}}}); + registryData.add("Tracks/V0qa/el/Ga/hDCAxyVsPt", ";DCA_{xy} (cm); #it{p}_{T} (GeV/#it{c})", kTH2F, {dcaXYAxis, ptAxis}); + registryData.add("Tracks/V0qa/pi/K0s/hDCAxyVsPt", ";DCA_{xy} (cm); #it{p}_{T} (GeV/#it{c})", kTH2F, {dcaXYAxis, ptAxis}); + registryData.addClone("Tracks/V0qa/pi/K0s/", "Tracks/V0qa/pi/La/"); + registryData.addClone("Tracks/V0qa/pi/K0s/", "Tracks/V0qa/pi/ALa/"); + registryData.addClone("Tracks/V0qa/pi/La/", "Tracks/V0qa/pr/La/"); + registryData.addClone("Tracks/V0qa/pi/ALa/", "Tracks/V0qa/pr/ALa/"); + if (defOpt.storeThnSparse) { + registryData.add({"Tracks/CleanTof/all/hPiTof", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, ptAxisV0s, dEdxAxis}}}); + registryData.add({"Tracks/CleanV0/all/hEV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, ptAxisV0s, dEdxAxis}}}); + registryData.add({"Tracks/CleanV0/all/hPiV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, ptAxisV0s, dEdxAxis}}}); + registryData.add({"Tracks/CleanV0/all/hPV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, ptAxisV0s, dEdxAxis}}}); } else { - registryData.add({"Tracks/CleanTof/all/hPiTof", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, pAxis, dEdxAxis}}}); - registryData.add({"Tracks/CleanV0/all/hEV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, pAxis, dEdxAxis}}}); - registryData.add({"Tracks/CleanV0/all/hPiV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, pAxis, dEdxAxis}}}); - registryData.add({"Tracks/CleanV0/all/hPV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, pAxis, dEdxAxis}}}); + registryData.add({"Tracks/CleanTof/all/hPiTof", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, ptAxisV0s, dEdxAxis}}}); + registryData.add({"Tracks/CleanV0/all/hEV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, ptAxisV0s, dEdxAxis}}}); + registryData.add({"Tracks/CleanV0/all/hPiV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, ptAxisV0s, dEdxAxis}}}); + registryData.add({"Tracks/CleanV0/all/hPV0", "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTH3F, {etaAxis, ptAxisV0s, dEdxAxis}}}); } registryData.add("Tracks/CleanTof/all/hBetaVsP", ";Momentum (GeV/#it{c}); #beta", kTH2F, {{{ptAxisV0s}, {120, 0., 1.2}}}); registryData.add("Tracks/CleanTof/all/hTofExpPi", ";Momentum (GeV/#it{c});#it{t}^{#pi}_{Exp}/#it{t}_{TOF}", kTH2F, {{{ptAxisV0s}, {100, 0.2, 1.2}}}); - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { registryData.addClone("Tracks/CleanTof/all/", "Tracks/CleanTof/pos/"); registryData.addClone("Tracks/CleanTof/all/", "Tracks/CleanTof/neg/"); registryData.addClone("Tracks/CleanV0/all/", "Tracks/CleanV0/pos/"); registryData.addClone("Tracks/CleanV0/all/", "Tracks/CleanV0/neg/"); } } - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { registryData.addClone("Tracks/all/", "Tracks/pos/"); registryData.addClone("Tracks/all/", "Tracks/neg/"); } @@ -751,108 +773,86 @@ struct FlattenictyPikp { registryData.print(); } - if (doprocessMC) { + if (doprocessMcEfficiency || doprocessMcRecTrack) { + if (defOpt.fillNclVsPhiCutQaHist) { + registryMC.add("Tracks/postSel/hPtPhi", "; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9)", {kTH2F, {ptAxis, phiAxisMod}}); + registryMC.add("Tracks/postSel/hPtPhiNclTPC", "; #eta; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9); N_{cluster}", {kTHnSparseF, {etaAxis, ptAxis, phiAxisMod, clTpcAxis}}); + registryMC.add("Tracks/postSel/hPtPhiNclPIDTPC", "; #eta; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9); N_{PID cluster}", {kTHnSparseF, {etaAxis, ptAxis, phiAxisMod, clTpcAxis}}); + registryMC.add("Tracks/postSel/hPtNclTPC", "; #it{p}_{T} (GeV/#it{c}); N_{cluster}", {kTH2F, {ptAxis, clTpcAxis}}); + registryMC.add("Tracks/postSel/pPtNclTPC", "; #it{p}_{T} (GeV/#it{c}); N_{cluster}", {kTProfile, {ptAxis}}); + registryMC.add("Tracks/postSel/hPtNclPIDTPC", "; #it{p}_{T} (GeV/#it{c}); N_{PID cluster}", {kTH2F, {ptAxis, clTpcAxis}}); + registryMC.add("Tracks/postSel/pPtNclPIDTPC", "; #it{p}_{T} (GeV/#it{c}); N_{PID cluster}", {kTProfile, {ptAxis}}); + } + registryMC.addClone("Tracks/postSel/", "Tracks/preSel/"); + } + + if (doprocessMcEfficiency) { registryMC.add({"Events/ResponseGen", ";N_{ch,FV0};1-#rho_{FV0};", {kTHnSparseF, {multAxis, flatAxis}}}); registryMC.add("Events/h1flatencityFV0MCGen", "", {kTH1F, {flatAxis}}); + registryMC.add("Events/hVtxZRec", "MC Rec vertex z position", kTH1F, {vtxzAxis}); + registryMC.add("Events/hVtxZGenGtOneRec", "MC Rec vertex z position w/ Nrec > 0", kTH1F, {vtxzAxis}); + registryMC.add("Events/hVtxZGen", "Generated vertex z position", kTH1F, {vtxzAxis}); + registryMC.add("Events/hMCEventStatus", "Number of events; Cut; #Evt Passed Cut", {HistType::kTHnSparseF, {{static_cast(MCEventStatus::nEvtMcEventStatus), -0.5, +static_cast(MCEventStatus::nEvtMcEventStatus) - 0.5}, multAxis, flatAxis}}); + std::array(MCEventStatus::nEvtMcEventStatus)> labelEvtSel{ + "All", + "Selected", + "Has Mc Coll", + "Split Vtx Removed"}; + for (int iBin = 0; iBin < static_cast(MCEventStatus::nEvtMcEventStatus); iBin++) { + registryMC.get(HIST("Events/hMCEventStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); + } + registryMC.add({"Tracks/hPtFakes", "Fake tracks; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); + registryMC.add("Tracks/hPtRes", "#it{p}_{T} resolution;;(#it{p}_{T}_{rec} - #it{p}_{T}_{gen})/#it{p}_{T}_{gen};", kTH2F, {ptAxis, {100, -1.0, 1.0}}); + + for (int i = 0; i < Npart; i++) { + registryMC.add({fmt::format(CpTeffGenPrimRecEvtF.data(), CspeciesAll[i].data()).c_str(), "Gen evt w/ Nrec > 0; mult; flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {multAxis, flatAxis, ptAxis}}}); + registryMC.add({fmt::format(CpTeffPrimRecEvtF.data(), CspeciesAll[i].data()).c_str(), "Gen evt w/ Nrec > 0 + Evt sel; mult; flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {multAxis, flatAxis, ptAxis}}}); + registryMC.add({fmt::format(CpTmcClosureRecF.data(), CspeciesAll[i].data()).c_str(), "Gen Nch w/ Nrec > 0 + Evt. sel; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); + } + LOG(info) << "processMcEfficiency -> Size of the MC histograms:"; + registryMC.print(); + } + + if (doprocessMcSgnEvtLoss) { registryMC.add("Events/hFlatMCGen", "Events/hFlatMCGen", {kTH1F, {flatAxis}}); - registryMC.add("Events/hFlatMCRec", "Events/hFlatMCRec", {kTH1F, {flatAxis}}); - // Event counter - auto h = registryMC.add("Events/hEvtGenRec", "Generated and Reconstructed MC Collisions", kTH1F, {{3, 0.5, 3.5}}); - h->GetXaxis()->SetBinLabel(1, "Gen coll"); - h->GetXaxis()->SetBinLabel(2, "Rec coll"); - h->GetXaxis()->SetBinLabel(3, "INEL>0"); registryMC.add("Events/hEvtMcGen", "Events/hEvtMcGen", {kTH1F, {{4, 0.f, 4.f}}}); registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(1, "all"); registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(2, "z-vtx"); registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(3, "INELgt0"); registryMC.get(HIST("Events/hEvtMcGen"))->GetXaxis()->SetBinLabel(4, "INELgt0TVX"); - registryMC.add("Events/hEvtMCRec", "Events/hEvtMCRec", {kTH1F, {{3, 0.f, 3.f}}}); - registryMC.get(HIST("Events/hEvtMCRec"))->GetXaxis()->SetBinLabel(1, "all"); - registryMC.get(HIST("Events/hEvtMCRec"))->GetXaxis()->SetBinLabel(2, "evt sel"); - registryMC.get(HIST("Events/hEvtMCRec"))->GetXaxis()->SetBinLabel(3, "INELgt0"); - registryMC.add("Events/hEvtMcGenColls", "Number of events; Cut; #Events Passed Cut", {kTH1F, {{4, 0.5, 4.5}}}); - registryMC.get(HIST("Events/hEvtMcGenColls"))->GetXaxis()->SetBinLabel(1, "Gen. coll"); - registryMC.get(HIST("Events/hEvtMcGenColls"))->GetXaxis()->SetBinLabel(2, "At least 1 reco"); - registryMC.get(HIST("Events/hEvtMcGenColls"))->GetXaxis()->SetBinLabel(3, "Reco. coll."); - registryMC.get(HIST("Events/hEvtMcGenColls"))->GetXaxis()->SetBinLabel(4, "Reco. good coll."); // + registryMC.add("Events/hNchGen", "Gen Nch; Gen Nch (|#eta|<0.8)", {kTH1F, {nChAxis}}); + registryMC.add("Events/hNchGenCent", "Gen cent; mult", {kTH1F, {multAxis}}); registryMC.add("Events/hNchGenVsCent", "Gen Nch vs Cent; mult; Gen Nch (|#eta|<0.8)", {kTH2F, {nChAxis, multAxis}}); - registryMC.add("Events/hVtxZRec", "MC Rec vertex z position", kTH1F, {vtxzAxis}); - registryMC.add("Events/hVtxZGen", "Generated vertex z position", kTH1F, {vtxzAxis}); - registryMC.add("Events/hNchTVX", "Nch in FT0A+FT0C; Nch; status", {kTH2F, {nChAxis, {2, 0, 2}}}); - registryMC.add({"Tracks/hPtFakes", "Fake tracks; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); // Event loss registryMC.add("Events/hNchVsFlatGenINELgt0", "Gen Nch w/o Evt sel; Gen Nch (|#eta|<0.8); flat", {kTH2F, {nChAxis, flatAxis}}); registryMC.add("Events/hNchVsFlatGenINELgt0wRecEvtSel", "Gen Nch w/ Nrec > 0 + Evt sel; Gen Nch (|#eta|<0.8); flat", {kTH2F, {nChAxis, flatAxis}}); + // Response + registryMC.add("Events/hFlatResponse", "Flattenicity response; flat ture; flat measured", {kTH2F, {flatAxis, flatAxis}}); // Event split registryMC.add("Events/hCentVsFlatRecINELgt0", "Gen evt w/o Evt sel; mult; flat", {kTH2F, {multAxis, flatAxis}}); - registryMC.add("Events/hCentVsFlatRecINELgt0wRecEvt", "Gen evt w/ Nrec > 0; mult; flat", {kTH2F, {multAxis, flatAxis}}); registryMC.add("Events/hCentVsFlatRecINELgt0wRecEvtSel", "Gen evt w/ Nrec > 0 + Evt sel; mult; flat", {kTH2F, {multAxis, flatAxis}}); + // Signal loss for (int i = 0; i < Npart; ++i) { - // Signal loss - registryMC.add({fmt::format(CpTgenPrimSgnF.data(), CspeciesAll[i]).c_str(), "Gen evt w/o Evt sel; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); - registryMC.add({fmt::format(CpTrecCollPrimSgnF.data(), CspeciesAll[i]).c_str(), "Gen Nch w/ Nrec > 0; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); - // Closure test - registryMC.add({fmt::format(CpTmcClosureGenPrimF.data(), CspeciesAll[i]).c_str(), "Gen evt w/o Evt sel; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); - registryMC.add({fmt::format(CpTmcClosureRecF.data(), CspeciesAll[i]).c_str(), "Gen Nch w/ Nrec > 0 + Evt. sel; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); - } - if (cfgFillNclVsPhiCutQaHist) { - registryMC.add("Tracks/postSel/hPtPhi", "; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9)", {kTH2F, {ptAxis, phiAxisMod}}); - registryMC.add("Tracks/postSel/hPtPhiNclTPC", "; #eta; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9); N_{cluster}", {kTHnSparseF, {etaAxis, ptAxis, phiAxisMod, clTpcAxis}}); - registryMC.add("Tracks/postSel/hPtPhiNclPIDTPC", "; #eta; #it{p}_{T} (GeV/#it{c}); fmod(#varphi,#pi/9); N_{PID cluster}", {kTHnSparseF, {etaAxis, ptAxis, phiAxisMod, clTpcAxis}}); - registryMC.add("Tracks/postSel/hPtNclTPC", "; #it{p}_{T} (GeV/#it{c}); N_{cluster}", {kTH2F, {ptAxis, clTpcAxis}}); - registryMC.add("Tracks/postSel/pPtNclTPC", "; #it{p}_{T} (GeV/#it{c}); N_{cluster}", {kTProfile, {ptAxis}}); - registryMC.add("Tracks/postSel/hPtNclPIDTPC", "; #it{p}_{T} (GeV/#it{c}); N_{PID cluster}", {kTH2F, {ptAxis, clTpcAxis}}); - registryMC.add("Tracks/postSel/pPtNclPIDTPC", "; #it{p}_{T} (GeV/#it{c}); N_{PID cluster}", {kTProfile, {ptAxis}}); - } - registryMC.addClone("Tracks/postSel/", "Tracks/preSel/"); - - for (int i = 0; i < NpartChrg; i++) { - const std::string strID = Form("/%s/%s", (i < Npart) ? "pos" : "neg", Pid[i % Npart]); - hPtGenRecEvt[i] = registryMC.add("Tracks/hPtGenRecEvt" + strID, "Gen evt w/ Nrec > 0; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtGenPrimRecEvt[i] = registryMC.add("Tracks/hPtGenPrimRecEvt" + strID, "Gen evt w/ Nrec > 0 (primary); mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtGenRecEvtGtZero[i] = registryMC.add("Tracks/hPtGenRecEvtGtZero" + strID, "Gen evt w/ Nrec > 0; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtGenPrimRecEvtGtZero[i] = registryMC.add("Tracks/hPtGenPrimRecEvtGtZero" + strID, "Gen evt w/ Nrec > 0 (primary); mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffGenPrim[i] = registryMC.add("Tracks/hPtEffGenPrim" + strID, "Gen evt w/o rec Evt; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffGenWeak[i] = registryMC.add("Tracks/hPtEffGenWeak" + strID, "Gen evt w/o rec Evt; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffGenMat[i] = registryMC.add("Tracks/hPtEffGenMat" + strID, "Gen evt w/o rec Evt; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffGenPrimEvtSelGen[i] = registryMC.add("Tracks/hPtEffGenPrimEvtSelGen" + strID, "Gen evt w/o rec Evt; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffGenWeakEvtSelGen[i] = registryMC.add("Tracks/hPtEffGenWeakEvtSelGen" + strID, "Gen evt w/o rec Evt; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffGenMatEvtSelGen[i] = registryMC.add("Tracks/hPtEffGenMatEvtSelGen" + strID, "Gen evt w/o rec Evt; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffRecGoodCollPrim[i] = registryMC.add("Tracks/hPtEffRecGoodCollPrim" + strID, "; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffRecGoodCollWeak[i] = registryMC.add("Tracks/hPtEffRecGoodCollWeak" + strID, "; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hPtEffRecGoodCollMat[i] = registryMC.add("Tracks/hPtEffRecGoodCollMat" + strID, "; mult; flat; #it{p}_{T} (GeV/#it{c})", kTHnSparseF, {multAxis, flatAxis, ptAxis}); - hDCAxyRecBadCollPrim[i] = registryMC.add("Tracks/hDCAxyRecBadCollPrim" + strID, "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {ptAxis, dcaXYAxis}); - hDCAxyRecBadCollWeak[i] = registryMC.add("Tracks/hDCAxyRecBadCollWeak" + strID, "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {ptAxis, dcaXYAxis}); - hDCAxyRecBadCollMat[i] = registryMC.add("Tracks/hDCAxyRecBadCollMat" + strID, "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {ptAxis, dcaXYAxis}); - hPtVsDCAxyRecGoodCollPrim[i] = registryMC.add("Tracks/hPtVsDCAxyRecGoodCollPrim" + strID, "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {ptAxis, dcaXYAxis}); - hPtVsDCAxyRecGoodCollWeak[i] = registryMC.add("Tracks/hPtVsDCAxyRecGoodCollWeak" + strID, "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {ptAxis, dcaXYAxis}); - hPtVsDCAxyRecGoodCollMat[i] = registryMC.add("Tracks/hPtVsDCAxyRecGoodCollMat" + strID, "; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", kTH2F, {ptAxis, dcaXYAxis}); + registryMC.add({fmt::format(CpTgenPrimSgnF.data(), CspeciesAll[i].data()).c_str(), "Gen evt w/o Evt sel; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); + registryMC.add({fmt::format(CpTrecCollPrimSgnF.data(), CspeciesAll[i].data()).c_str(), "Gen Nch w/ Nrec > 0; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); + registryMC.add({fmt::format(CpTmcClosureGenPrimF.data(), CspeciesAll[i].data()).c_str(), "Gen evt w/o Evt sel; Gen Nch (|#eta|<0.8); flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {nChAxis, flatAxis, ptAxis}}}); } + LOG(info) << "processMcSgnEvtLoss -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcRecTrack) { for (int i = 0; i < Npart; i++) { - registryMC.add({fmt::format(CpTeffGenPrimRecEvtF.data(), CspeciesAll[i]).c_str(), "Gen evt w/ Nrec > 0; mult; flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {multAxis, flatAxis, ptAxis}}}); - registryMC.add({fmt::format(CpTeffPrimRecEvtF.data(), CspeciesAll[i]).c_str(), "Gen evt w/ Nrec > 0 + Evt sel; mult; flat; #it{p}_{T} (GeV/#it{c})", {kTHnSparseF, {multAxis, flatAxis, ptAxis}}}); - registryMC.add({fmt::format(CpTvsDCAxyAllF.data(), CspeciesAll[i]).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); - registryMC.add({fmt::format(CpTvsDCAxyPrimAllF.data(), CspeciesAll[i]).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); - registryMC.add({fmt::format(CpTvsDCAxyWeakAllF.data(), CspeciesAll[i]).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); - registryMC.add({fmt::format(CpTvsDCAxyMatAllF.data(), CspeciesAll[i]).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); - registryMC.add({fmt::format(CdEdxMcRecPrimF.data(), CspeciesAll[i]).c_str(), "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); - registryMC.add({fmt::format(CdEdxMcRecPrimSelF.data(), CspeciesAll[i]).c_str(), "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); - } - - // Hash list for efficiency - listEfficiency.setObject(new THashList); - static_for<0, 1>([&](auto pidSgn) { - bookMcHist(); - bookMcHist(); - bookMcHist(); - initEfficiency(); - initEfficiency(); - initEfficiency(); - }); - - LOG(info) << "Size of the MC histograms:"; + registryMC.add({fmt::format(CdEdxMcRecPrimF.data(), CspeciesAll[i].data()).c_str(), "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); + registryMC.add({fmt::format(CdEdxMcRecPrimSelF.data(), CspeciesAll[i].data()).c_str(), "; #eta; mult; flat; #it{p} (GeV/#it{c}); dEdx", {kTHnSparseF, {etaAxis, multAxis, flatAxis, pAxis, dEdxAxis}}}); + registryMC.add({fmt::format(CEtaVsPtVsPMcRecPrimSelF.data(), CspeciesAll[i].data()).c_str(), "; #eta; #it{p}_{T} (GeV/#it{c}); #it{p} (GeV/#it{c})", {kTHnSparseF, {etaAxis, ptAxis, pAxis}}}); + registryMC.add({fmt::format(CpTvsDCAxyAllF.data(), CspeciesAll[i].data()).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); + registryMC.add({fmt::format(CpTvsDCAxyPrimAllF.data(), CspeciesAll[i].data()).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); + registryMC.add({fmt::format(CpTvsDCAxyWeakAllF.data(), CspeciesAll[i].data()).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); + registryMC.add({fmt::format(CpTvsDCAxyMatAllF.data(), CspeciesAll[i].data()).c_str(), "; mult; flat; #it{p}_{T} (GeV/#it{c}); DCA_{xy} (cm)", {kTHnSparseF, {multAxis, flatAxis, ptAxis, dcaXYAxis}}}); + } + LOG(info) << "processMcRecTrack -> Size of the MC histograms:"; registryMC.print(); } } @@ -861,14 +861,9 @@ struct FlattenictyPikp { { fv0AmplCorr.clear(); fv0AmplCorr = {}; - std::string fullPathCalibGain; - std::string fullPathCalibVtx; - std::string fullPathCalibDeDxMip; - std::string fullPathCalibDeDxPlateau; - auto timestamp = bc.timestamp(); auto runnumber = bc.runNumber(); - if (trkSelOpt.cfgRejectTrkAtTPCSector || v0SelOpt.cfgRejectV0sAtTPCSector) { + if (trkSelOpt.rejectTrkAtTPCSector || v0SelOpt.rejectV0sAtTPCSector) { grpmag = ccdb->getForRun(ccdbConf.grpmagPath, runnumber); if (!grpmag) { LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConf.grpmagPath << " of object GRPMagField and " << ccdbConf.grpPath << " of object GRPObject for run " << runnumber; @@ -876,10 +871,10 @@ struct FlattenictyPikp { magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); LOG(info) << "Retrieved GRP for run " << runnumber << " with magnetic field of " << magField << " kZG"; } - if (applyCalibGain) { - fullPathCalibGain = cfgGainEqCcdbPath; + if (defOpt.applyCalibGainFromCCDB) { + std::string fullPathCalibGain = defOpt.gainEqCcdbPath; fullPathCalibGain += "/FV0"; - const auto* objfv0Gain = getForTsOrRun>(fullPathCalibGain, timestamp, runnumber); + const auto* objfv0Gain = ccdb->getForRun>(fullPathCalibGain, runnumber); if (!objfv0Gain) { for (auto i{0u}; i < CnCellsFV0; i++) { fv0AmplCorr.push_back(1.); @@ -889,49 +884,64 @@ struct FlattenictyPikp { fv0AmplCorr = *(objfv0Gain); } } - if (applyCalibVtx) { - fullPathCalibVtx = cfgVtxEqCcdbPath; + if (defOpt.applyCalibVtxFromCCDB) { + std::string fullPathCalibVtx = defOpt.vtxEqCcdbPath; fullPathCalibVtx += "/FV0"; - zVtxMap = getForTsOrRun(fullPathCalibVtx, timestamp, runnumber); + zVtxMap = ccdb->getForRun(fullPathCalibVtx, runnumber); } - - if (applyCalibDeDxFromCCDB) { - fullPathCalibDeDxMip = cfgDeDxCalibCcdbPath; - fullPathCalibDeDxMip += "/MIP"; - fullPathCalibDeDxPlateau = cfgDeDxCalibCcdbPath; - fullPathCalibDeDxPlateau += "/Plateau"; + if (defOpt.applyCalibDeDxFromCCDB) { + std::string fullPathCalibDeDxMip = defOpt.dEdxCalibCcdbPath; if (!fullPathCalibDeDxMip.empty()) { - dedxcalib.lCalibObjects = getForTsOrRun(fullPathCalibDeDxMip, timestamp, runnumber); - if (dedxcalib.lCalibObjects) { - LOG(info) << "CCDB objects loaded successfully"; - dedxcalib.hMIPcalibPos = static_cast(dedxcalib.lCalibObjects->FindObject("hMIPcalibPos")); - dedxcalib.hMIPcalibNeg = static_cast(dedxcalib.lCalibObjects->FindObject("hMIPcalibNeg")); - dedxcalib.hMIPcalibAll = static_cast(dedxcalib.lCalibObjects->FindObject("hMIPcalibAll")); - dedxcalib.lCalibLoaded = true; - if (!dedxcalib.hMIPcalibPos || !dedxcalib.hMIPcalibNeg || !dedxcalib.hMIPcalibAll) { - LOGF(error, "Problem loading CCDB objects! Please check"); - dedxcalib.lCalibLoaded = false; - } - } else { - LOGF(fatal, "Could not load hMIPcalib from %s", fullPathCalibDeDxMip.c_str()); + fullPathCalibDeDxMip += "/MIP"; + } + std::string fullPathCalibDeDxPlateau = defOpt.dEdxCalibCcdbPath; + if (!fullPathCalibDeDxPlateau.empty()) { + fullPathCalibDeDxPlateau += "/Plateau"; + } + dedxcalib.lCalibObjects = ccdb->getForRun(fullPathCalibDeDxMip, runnumber); + if (dedxcalib.lCalibObjects) { + LOG(info) << "CCDB objects loaded successfully"; + dedxcalib.hMIPcalibPos = dynamic_cast(dedxcalib.lCalibObjects->FindObject("hMIPcalibPos")); + dedxcalib.hMIPcalibNeg = dynamic_cast(dedxcalib.lCalibObjects->FindObject("hMIPcalibNeg")); + dedxcalib.hMIPcalibAll = dynamic_cast(dedxcalib.lCalibObjects->FindObject("hMIPcalibAll")); + dedxcalib.lCalibLoaded = true; + if (!dedxcalib.hMIPcalibPos || !dedxcalib.hMIPcalibNeg || !dedxcalib.hMIPcalibAll) { + LOGF(error, "Problem loading CCDB objects! Please check"); dedxcalib.lCalibLoaded = false; } + } else { + LOGF(fatal, "Could not load hMIPcalib from %s", fullPathCalibDeDxMip.c_str()); + dedxcalib.lCalibLoaded = false; + } + dedxcalib.lCalibObjects = ccdb->getForRun(fullPathCalibDeDxPlateau, runnumber); + if (dedxcalib.lCalibObjects) { + LOG(info) << "CCDB objects loaded successfully"; + dedxcalib.hPlateauCalibPos = dynamic_cast(dedxcalib.lCalibObjects->FindObject("hPlateauCalibPos")); + dedxcalib.hPlateauCalibNeg = dynamic_cast(dedxcalib.lCalibObjects->FindObject("hPlateauCalibNeg")); + dedxcalib.hPlateauCalibAll = dynamic_cast(dedxcalib.lCalibObjects->FindObject("hPlateauCalibAll")); + dedxcalib.lCalibLoaded = true; + if (!dedxcalib.hPlateauCalibPos || !dedxcalib.hPlateauCalibNeg || !dedxcalib.hPlateauCalibAll) { + LOGF(error, "Problem loading CCDB objects! Please check"); + dedxcalib.lCalibLoaded = false; + } + } else { + LOGF(fatal, "Could not load hPlateauCalib from %s", fullPathCalibDeDxPlateau.c_str()); } - if (!fullPathCalibDeDxPlateau.empty()) { - dedxcalib.lCalibObjects = getForTsOrRun(fullPathCalibDeDxPlateau, timestamp, runnumber); - if (dedxcalib.lCalibObjects) { - LOG(info) << "CCDB objects loaded successfully"; - dedxcalib.hPlateauCalibPos = static_cast(dedxcalib.lCalibObjects->FindObject("hPlateauCalibPos")); - dedxcalib.hPlateauCalibNeg = static_cast(dedxcalib.lCalibObjects->FindObject("hPlateauCalibNeg")); - dedxcalib.hPlateauCalibAll = static_cast(dedxcalib.lCalibObjects->FindObject("hPlateauCalibAll")); - dedxcalib.lCalibLoaded = true; - if (!dedxcalib.hPlateauCalibPos || !dedxcalib.hPlateauCalibNeg || !dedxcalib.hPlateauCalibAll) { - LOGF(error, "Problem loading CCDB objects! Please check"); - dedxcalib.lCalibLoaded = false; - } - } else { - LOGF(fatal, "Could not load hPlateauCalib from %s", fullPathCalibDeDxPlateau.c_str()); + } + if (defOpt.applyDCAParamFromCCDB) { + dcaparam.lCalibObjects = ccdb->getForRun(defOpt.dcaParamCcdbPath, runnumber); + if (dcaparam.lCalibObjects) { + LOG(info) << "CCDB objects loaded successfully"; + dcaparam.hDCAxyParam = dynamic_cast(dcaparam.lCalibObjects->FindObject("hfitDCAxyParams")); + dcaparam.hDCAzParam = dynamic_cast(dcaparam.lCalibObjects->FindObject("hfitDCAzParams")); + dcaparam.lCalibLoaded = true; + if (!dcaparam.hDCAxyParam || !dcaparam.hDCAzParam) { + LOGF(error, "Problem loading CCDB objects! Please check"); + dcaparam.lCalibLoaded = false; } + } else { + LOGF(fatal, "Could not load DCAParam from %s", defOpt.dcaParamCcdbPath.value.c_str()); + dcaparam.lCalibLoaded = false; } } } @@ -939,7 +949,8 @@ struct FlattenictyPikp { template std::unique_ptr setFuncPars(T const& vecPars) { - std::unique_ptr fCalibDeDxFunc(new TF1("fCalibDeDxFunc", cfgCalibDeDxFunction.value.c_str(), -1., 1.)); + static_assert(!std::is_pointer::value, "Pointers to pointers not allowed here"); + std::unique_ptr fCalibDeDxFunc(new TF1("fCalibDeDxFunc", defOpt.calibDeDxFunction.value.c_str(), -1., 1.)); if (vecPars.size() >= 1) { for (typename T::size_type i = 0; i < vecPars.size(); i++) { fCalibDeDxFunc->SetParameter(i, vecPars[i]); @@ -948,39 +959,45 @@ struct FlattenictyPikp { return fCalibDeDxFunc; } - template - bool isPID(const P& mcParticle) - { - static_assert(pidSgn == CnullInt || pidSgn == ConeInt); - static_assert(id > CnullInt || id < Npart); - constexpr int Cidx = id + pidSgn * Npart; - return mcParticle.pdgCode() == PidSgn[Cidx]; - } - template bool selTOFPi(T const& track) { - if (track.hasTOF() && track.goodTOFMatch()) { - const float tTOF = track.tofSignal(); - const float trkLength = track.length(); - const float tExpPiTOF = track.tofExpSignalPi(tTOF); - if (track.p() >= trkSelOpt.cfgMomSelPiTOF && trkLength > Cnull && tTOF > Cnull) { - registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[chrg]) + HIST("hTofExpPi"), track.p(), tExpPiTOF / tTOF); - if (std::abs((tExpPiTOF / tTOF) - Cone) < trkSelOpt.cfgTofBetaPiMax) { - registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[chrg]) + HIST("hBetaVsP"), track.p(), track.beta()); - // if (std::abs(track.tpcNSigmaPi()) < v0SelOpt.cfgNsigmaPiTPC && std::abs(track.tofNSigmaPi()) < v0SelOpt.cfgNsigmaPiTOF) { - return true; - // } - } - } + if (!track.hasTOF()) { + return false; } - return false; + if (trkSelOpt.requireGoodTOFMatch && !track.goodTOFMatch()) { + return false; + } + if (track.p() < trkSelOpt.momSelPiTOF) { + return false; + } + if (track.length() == Cnull) { + return false; + } + const float& tTOF = track.tofSignal(); + const float& tExpPiTOF = track.tofExpSignalPi(tTOF); + if (tTOF <= Cnull) { + return false; + } + registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[chrg]) + HIST("hTofExpPi"), track.p(), tExpPiTOF / tTOF); + + const bool isSelTOFBeta = std::abs((tExpPiTOF / tTOF) - Cone) < trkSelOpt.tofBetaPiMax; + const bool isSelNsigmaPi = std::sqrt(std::pow(std::fabs(track.tofNSigmaPi()), 2.) + std::pow(std::fabs(track.tpcNSigmaPi()), 2.)) < trkSelOpt.dcaNsigmaCombinedMax; + + if (trkSelOpt.useSelTOFBeta && !isSelTOFBeta) { + return false; + } + if (trkSelOpt.useSelTOFnSigma && !isSelNsigmaPi) { + return false; + } + registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[chrg]) + HIST("hBetaVsP"), track.p(), track.beta()); + return true; } template void fillDCA(T const& tracks, C const& collision, aod::BCsWithTimestamps const& /*bcs*/) { - if (trkSelOpt.cfgRejectTrkAtTPCSector) { + if (trkSelOpt.rejectTrkAtTPCSector) { auto bc = collision.template bc_as(); int currentRun = bc.runNumber(); if (runNumber != currentRun) { @@ -991,27 +1008,16 @@ struct FlattenictyPikp { const float mult = getMult(collision); const float flat = fillFlat(collision); for (const auto& track : tracks) { - if (std::abs(track.eta()) > trkSelOpt.cfgTrkEtaMax) { - continue; - } - if (track.pt() < trkSelOpt.cfgTrkPtMin) { - continue; - } - if (trkSelOpt.cfgApplyNcl && track.tpcNClsFound() < trkSelOpt.cfgNclTPCMin) { + if (!isGoodTrack(track, magField)) { continue; } - if (trkSelOpt.cfgApplyNclPID && track.tpcNClsPID() < trkSelOpt.cfgNclPidTPCMin) { + if (!track.hasTOF()) { continue; } - float phiModn = track.phi(); - phiMod(phiModn, magField, track.sign()); - if (trkSelOpt.cfgRejectTrkAtTPCSector && (track.pt() >= trkSelOpt.cfgPhiCutPtMin && phiModn < fPhiCutHigh->Eval(track.pt()) && phiModn > fPhiCutLow->Eval(track.pt()))) { + if (trkSelOpt.requireGoodTOFMatch && !track.goodTOFMatch()) { continue; } - if (!isDCAxyWoCut(track)) { - continue; - } - if (track.hasTOF() && (std::sqrt(std::pow(std::fabs(o2::aod::pidutils::tpcNSigma(track)), 2) + std::pow(std::fabs(o2::aod::pidutils::tofNSigma(track)), 2) < trkSelOpt.cfgDcaNsigmaCombinedMax))) { + if (std::sqrt(std::pow(std::fabs(o2::aod::pidutils::tpcNSigma(track)), 2.) + std::pow(std::fabs(o2::aod::pidutils::tofNSigma(track)), 2.) < trkSelOpt.dcaNsigmaCombinedMax)) { registryData.fill(HIST(Cprefix) + HIST(CspeciesAll[id]) + HIST(CpTvsDCAxy), mult, flat, track.pt(), track.dcaXY()); } } @@ -1020,7 +1026,7 @@ struct FlattenictyPikp { template void filldEdx(T const& tracks, V const& v0s, C const& collision, aod::BCsWithTimestamps const& bcs) { - if (trkSelOpt.cfgRejectTrkAtTPCSector || v0SelOpt.cfgRejectV0sAtTPCSector || applyCalibGain || applyCalibVtx) { + if (trkSelOpt.rejectTrkAtTPCSector || v0SelOpt.rejectV0sAtTPCSector || defOpt.applyCalibGainFromCCDB || defOpt.applyCalibVtxFromCCDB) { auto bc = collision.template bc_as(); int currentRun = bc.runNumber(); if (runNumber != currentRun) { @@ -1037,17 +1043,17 @@ struct FlattenictyPikp { for (const auto& track : tracks) { float dEdx = track.tpcSignal(); - if (cfgFillTrackQaHist) { + if (defOpt.fillTrackQaHist) { fillTrackQA(track); } - if (!isGoodTrack(track, magField)) { + if (!isGoodTrack(track, magField)) { continue; } - if (cfgFillTrackQaHist) { + if (defOpt.fillTrackQaHist) { fillTrackQA(track); } - if (cfgFilldEdxCalibHist && cfgFilldEdxQaHist) { - if (cfgFillChrgType) { + if (defOpt.filldEdxCalibHist && defOpt.filldEdxQaHist) { + if (defOpt.fillChrgType) { if (track.sign() * track.p() > Cnull) { filldEdxQA(track, collision, dEdx); } else { @@ -1057,69 +1063,69 @@ struct FlattenictyPikp { filldEdxQA(track, collision, dEdx); } } - if (applyCalibDeDx) { - if (cfgFillChrgType) { + if (defOpt.applyCalibDeDx) { + if (defOpt.fillChrgType) { if (track.sign() * track.p() > Cnull) { - if (applyCalibDeDxFromCCDB) { - dEdx *= (50.0 / dedxcalib.hMIPcalibPos->GetBinContent(dedxcalib.hMIPcalibPos->FindBin(track.eta()))); + if (defOpt.applyCalibDeDxFromCCDB && dedxcalib.lCalibLoaded) { + dEdx *= (trkSelOpt.dEdxMIPnominal / dedxcalib.hMIPcalibPos->GetBinContent(dedxcalib.hMIPcalibPos->FindBin(track.eta()))); } else { - dEdx *= (50.0 / getCalibration(fDeDxVsEta, track)); + dEdx *= (trkSelOpt.dEdxMIPnominal / getCalibration(fDeDxVsEta, track)); } - if (cfgFilldEdxQaHist) { + if (defOpt.filldEdxQaHist) { filldEdxQA(track, collision, dEdx); } } else { - if (applyCalibDeDxFromCCDB) { - dEdx *= (50.0 / dedxcalib.hMIPcalibNeg->GetBinContent(dedxcalib.hMIPcalibNeg->FindBin(track.eta()))); + if (defOpt.applyCalibDeDxFromCCDB && dedxcalib.lCalibLoaded) { + dEdx *= (trkSelOpt.dEdxMIPnominal / dedxcalib.hMIPcalibNeg->GetBinContent(dedxcalib.hMIPcalibNeg->FindBin(track.eta()))); } else { - dEdx *= (50.0 / getCalibration(fDeDxVsEta, track)); + dEdx *= (trkSelOpt.dEdxMIPnominal / getCalibration(fDeDxVsEta, track)); } - if (cfgFilldEdxQaHist) { + if (defOpt.filldEdxQaHist) { filldEdxQA(track, collision, dEdx); } } } else { - if (applyCalibDeDxFromCCDB) { - dEdx *= (50.0 / dedxcalib.hMIPcalibAll->GetBinContent(dedxcalib.hMIPcalibAll->FindBin(track.eta()))); + if (defOpt.applyCalibDeDxFromCCDB && dedxcalib.lCalibLoaded) { + dEdx *= (trkSelOpt.dEdxMIPnominal / dedxcalib.hMIPcalibAll->GetBinContent(dedxcalib.hMIPcalibAll->FindBin(track.eta()))); } else { - dEdx *= (50.0 / getCalibration(fDeDxVsEta, track)); + dEdx *= (trkSelOpt.dEdxMIPnominal / getCalibration(fDeDxVsEta, track)); } - if (cfgFilldEdxQaHist) { + if (defOpt.filldEdxQaHist) { filldEdxQA(track, collision, dEdx); } } } // PID TPC dEdx - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { if (track.sign() * track.p() > Cnull) { - registryData.fill(HIST(Cprefix) + HIST(Ccharge[kPos]) + HIST("hdEdx"), track.eta(), mult, flat, track.p(), dEdx); + registryData.fill(HIST(Cprefix) + HIST(Ccharge[kPos]) + HIST("hdEdx"), track.eta(), mult, flat, track.p(), track.pt(), dEdx); } else { - registryData.fill(HIST(Cprefix) + HIST(Ccharge[kNeg]) + HIST("hdEdx"), track.eta(), mult, flat, track.p(), dEdx); + registryData.fill(HIST(Cprefix) + HIST(Ccharge[kNeg]) + HIST("hdEdx"), track.eta(), mult, flat, track.p(), track.pt(), dEdx); } } else { - registryData.fill(HIST(Cprefix) + HIST(Ccharge[kAll]) + HIST("hdEdx"), track.eta(), mult, flat, track.p(), dEdx); + registryData.fill(HIST(Cprefix) + HIST(Ccharge[kAll]) + HIST("hdEdx"), track.eta(), mult, flat, track.p(), track.pt(), dEdx); } // TOF pions - if (cfgFillV0Hist) { + if (defOpt.fillV0Hist) { if (selTOFPi(track)) { - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { if (track.sign() * track.p() > Cnull) { - if (cfgStoreThnSparse) { + if (defOpt.storeThnSparse) { registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[kPos]) + HIST("hPiTof"), track.eta(), mult, flat, track.p(), dEdx); } else { registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[kPos]) + HIST("hPiTof"), track.eta(), track.p(), dEdx); } } else { - if (cfgStoreThnSparse) { + if (defOpt.storeThnSparse) { registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[kNeg]) + HIST("hPiTof"), track.eta(), mult, flat, track.p(), dEdx); } else { registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[kNeg]) + HIST("hPiTof"), track.eta(), track.p(), dEdx); } } } else { - if (cfgStoreThnSparse) { + if (defOpt.storeThnSparse) { registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[kAll]) + HIST("hPiTof"), track.eta(), mult, flat, track.p(), dEdx); } else { registryData.fill(HIST(CprefixCleanTof) + HIST(Ccharge[kAll]) + HIST("hPiTof"), track.eta(), track.p(), dEdx); @@ -1130,9 +1136,9 @@ struct FlattenictyPikp { } // V0s - if (cfgFillV0Hist) { + if (defOpt.fillV0Hist) { for (const auto& v0 : v0s) { - if (v0.v0Type() != v0SelOpt.cfgV0TypeSel && v0SelOpt.cfgV0TypeSel > -1) { + if (v0.v0Type() != v0SelOpt.v0TypeSel && v0SelOpt.v0TypeSel > -1) { continue; } if (!isGoodV0Track(v0, tracks, magField)) { @@ -1144,22 +1150,22 @@ struct FlattenictyPikp { float dEdxPos = posTrack.tpcSignal(); float dEdxNeg = negTrack.tpcSignal(); - if (applyCalibDeDx) { - if (cfgFillChrgTypeV0s) { - if (applyCalibDeDxFromCCDB) { - dEdxPos *= (50.0 / dedxcalib.hMIPcalibPos->GetBinContent(dedxcalib.hMIPcalibPos->FindBin(posTrack.eta()))); - dEdxNeg *= (50.0 / dedxcalib.hMIPcalibNeg->GetBinContent(dedxcalib.hMIPcalibNeg->FindBin(negTrack.eta()))); + if (defOpt.applyCalibDeDx) { + if (defOpt.fillChrgTypeV0s) { + if (defOpt.applyCalibDeDxFromCCDB && dedxcalib.lCalibLoaded) { + dEdxPos *= (trkSelOpt.dEdxMIPnominal / dedxcalib.hMIPcalibPos->GetBinContent(dedxcalib.hMIPcalibPos->FindBin(posTrack.eta()))); + dEdxNeg *= (trkSelOpt.dEdxMIPnominal / dedxcalib.hMIPcalibNeg->GetBinContent(dedxcalib.hMIPcalibNeg->FindBin(negTrack.eta()))); } else { - dEdxPos *= (50.0 / getCalibration(fDeDxVsEta, posTrack)); - dEdxNeg *= (50.0 / getCalibration(fDeDxVsEta, negTrack)); + dEdxPos *= (trkSelOpt.dEdxMIPnominal / getCalibration(fDeDxVsEta, posTrack)); + dEdxNeg *= (trkSelOpt.dEdxMIPnominal / getCalibration(fDeDxVsEta, negTrack)); } } else { - if (applyCalibDeDxFromCCDB) { - dEdxPos *= (50.0 / dedxcalib.hMIPcalibAll->GetBinContent(dedxcalib.hMIPcalibAll->FindBin(posTrack.eta()))); - dEdxNeg *= (50.0 / dedxcalib.hMIPcalibAll->GetBinContent(dedxcalib.hMIPcalibAll->FindBin(negTrack.eta()))); + if (defOpt.applyCalibDeDxFromCCDB && dedxcalib.lCalibLoaded) { + dEdxPos *= (trkSelOpt.dEdxMIPnominal / dedxcalib.hMIPcalibAll->GetBinContent(dedxcalib.hMIPcalibAll->FindBin(posTrack.eta()))); + dEdxNeg *= (trkSelOpt.dEdxMIPnominal / dedxcalib.hMIPcalibAll->GetBinContent(dedxcalib.hMIPcalibAll->FindBin(negTrack.eta()))); } else { - dEdxPos *= (50.0 / getCalibration(fDeDxVsEta, posTrack)); - dEdxNeg *= (50.0 / getCalibration(fDeDxVsEta, negTrack)); + dEdxPos *= (trkSelOpt.dEdxMIPnominal / getCalibration(fDeDxVsEta, posTrack)); + dEdxNeg *= (trkSelOpt.dEdxMIPnominal / getCalibration(fDeDxVsEta, negTrack)); } } } @@ -1167,39 +1173,40 @@ struct FlattenictyPikp { if (selectTypeV0s(collision, v0, posTrack, negTrack) == kGa) { // Gamma selection fillV0QA(v0, posTrack); fillV0QA(v0, negTrack); - if (applyCalibDeDx) { - if (cfgFillChrgTypeV0s) { - if (applyCalibDeDxFromCCDB) { - const float dEdxPosGa = dedxcalib.hMIPcalibPos->GetBinContent(dedxcalib.hMIPcalibPos->FindBin(posTrack.eta())); - const float dEdxNegGa = dedxcalib.hMIPcalibNeg->GetBinContent(dedxcalib.hMIPcalibNeg->FindBin(negTrack.eta())); - if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.cfgdEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.cfgdEdxPlateauSel) { + // float dEdxPosGa{-1.}, dEdxNegGa{-1.}; + if (defOpt.applyCalibDeDx) { + if (defOpt.fillChrgTypeV0s) { + if (defOpt.applyCalibDeDxFromCCDB && dedxcalib.lCalibLoaded) { + float dEdxPosGa = dedxcalib.hMIPcalibPos->GetBinContent(dedxcalib.hMIPcalibPos->FindBin(posTrack.eta())); + float dEdxNegGa = dedxcalib.hMIPcalibNeg->GetBinContent(dedxcalib.hMIPcalibNeg->FindBin(negTrack.eta())); + if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.dEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.dEdxPlateauSel) { continue; } } else { - const float dEdxPosGa = getCalibration(fEDeDxVsEta, posTrack); - const float dEdxNegGa = getCalibration(fEDeDxVsEta, negTrack); - if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.cfgdEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.cfgdEdxPlateauSel) { + float dEdxPosGa = getCalibration(fEDeDxVsEta, posTrack); + float dEdxNegGa = getCalibration(fEDeDxVsEta, negTrack); + if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.dEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.dEdxPlateauSel) { continue; } } } else { - if (applyCalibDeDxFromCCDB) { - const float dEdxPosGa = dedxcalib.hPlateauCalibAll->GetBinContent(dedxcalib.hPlateauCalibAll->FindBin(posTrack.eta())); - const float dEdxNegGa = dedxcalib.hPlateauCalibAll->GetBinContent(dedxcalib.hPlateauCalibAll->FindBin(negTrack.eta())); - if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.cfgdEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.cfgdEdxPlateauSel) { + if (defOpt.applyCalibDeDxFromCCDB && dedxcalib.lCalibLoaded) { + float dEdxPosGa = dedxcalib.hPlateauCalibAll->GetBinContent(dedxcalib.hPlateauCalibAll->FindBin(posTrack.eta())); + float dEdxNegGa = dedxcalib.hPlateauCalibAll->GetBinContent(dedxcalib.hPlateauCalibAll->FindBin(negTrack.eta())); + if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.dEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.dEdxPlateauSel) { continue; } } else { - const float dEdxPosGa = getCalibration(fEDeDxVsEta, posTrack); - const float dEdxNegGa = getCalibration(fEDeDxVsEta, negTrack); - if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.cfgdEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.cfgdEdxPlateauSel) { + float dEdxPosGa = getCalibration(fEDeDxVsEta, posTrack); + float dEdxNegGa = getCalibration(fEDeDxVsEta, negTrack); + if (std::abs(dEdxPos - dEdxPosGa) >= v0SelOpt.dEdxPlateauSel || std::abs(dEdxNeg - dEdxNegGa) >= v0SelOpt.dEdxPlateauSel) { continue; } } } } - if (cfgStoreThnSparse) { - if (cfgFillChrgType) { + if (defOpt.storeThnSparse) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hEV0"), posTrack.eta(), mult, flat, posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hEV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1207,7 +1214,7 @@ struct FlattenictyPikp { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kAll]) + HIST("hEV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } } else { - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hEV0"), posTrack.eta(), posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hEV0"), negTrack.eta(), negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1219,8 +1226,8 @@ struct FlattenictyPikp { if (selectTypeV0s(collision, v0, posTrack, negTrack) == kKz) { // K0S -> pi + pi fillV0QA(v0, posTrack); fillV0QA(v0, negTrack); - if (cfgStoreThnSparse) { - if (cfgFillChrgType) { + if (defOpt.storeThnSparse) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hPiV0"), posTrack.eta(), mult, flat, posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hPiV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1228,7 +1235,7 @@ struct FlattenictyPikp { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kAll]) + HIST("hPiV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } } else { - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hPiV0"), posTrack.eta(), posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hPiV0"), negTrack.eta(), negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1240,8 +1247,10 @@ struct FlattenictyPikp { if (selectTypeV0s(collision, v0, posTrack, negTrack) == kLam) { // L -> p + pi- fillV0QA(v0, negTrack); fillV0QA(v0, posTrack); - if (cfgStoreThnSparse) { - if (cfgFillChrgType) { + registryData.fill(HIST(CprefixV0qa) + HIST("hNsigmaPiFromLaVsPt"), negTrack.pt(), negTrack.tpcNSigmaPi()); + registryData.fill(HIST(CprefixV0qa) + HIST("hNsigmaPrFromLaVsPt"), posTrack.pt(), posTrack.tpcNSigmaPr()); + if (defOpt.storeThnSparse) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hPV0"), posTrack.eta(), mult, flat, posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hPiV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1249,7 +1258,7 @@ struct FlattenictyPikp { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kAll]) + HIST("hPiV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } } else { - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hPV0"), posTrack.eta(), posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hPiV0"), negTrack.eta(), negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1261,8 +1270,10 @@ struct FlattenictyPikp { if (selectTypeV0s(collision, v0, posTrack, negTrack) == kaLam) { // antiLambda -> pbar + pi+ fillV0QA(v0, posTrack); fillV0QA(v0, negTrack); - if (cfgStoreThnSparse) { - if (cfgFillChrgType) { + registryData.fill(HIST(CprefixV0qa) + HIST("hNsigmaPiFromALaVsPt"), posTrack.pt(), posTrack.tpcNSigmaPi()); + registryData.fill(HIST(CprefixV0qa) + HIST("hNsigmaPrFromALaVsPt"), negTrack.pt(), negTrack.tpcNSigmaPr()); + if (defOpt.storeThnSparse) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hPiV0"), posTrack.eta(), mult, flat, posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hPV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1270,7 +1281,7 @@ struct FlattenictyPikp { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kAll]) + HIST("hPV0"), negTrack.eta(), mult, flat, negTrack.sign() * negTrack.p(), dEdxNeg); } } else { - if (cfgFillChrgType) { + if (defOpt.fillChrgType) { registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kPos]) + HIST("hPiV0"), posTrack.eta(), posTrack.sign() * posTrack.p(), dEdxPos); registryData.fill(HIST(CprefixCleanV0) + HIST(Ccharge[kNeg]) + HIST("hPV0"), negTrack.eta(), negTrack.sign() * negTrack.p(), dEdxNeg); } else { @@ -1321,62 +1332,10 @@ struct FlattenictyPikp { return std::abs(charge) >= CminCharge; } - template - int countPart(P const& particles) - { - auto nCharged = 0; - for (auto const& particle : particles) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (!particle.isPhysicalPrimary()) { - continue; - } - if (std::abs(particle.eta()) > trkSelOpt.cfgTrkEtaMax) { - continue; - } - nCharged++; - } - return nCharged; - } - - template - bool isInelGt0wTVX(P const& particles) - { - int nChrgMc = 0; - int nChrgFT0A = 0; - int nChrgFT0C = 0; - for (auto const& particle : particles) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (!particle.isPhysicalPrimary()) { - continue; - } - // trigger TVX - if (particle.eta() > CminAccFT0A && particle.eta() < CmaxAccFT0A) { - nChrgFT0A++; - } - if (particle.eta() > CminAccFT0C && particle.eta() < CmaxAccFT0C) { - nChrgFT0C++; - } - nChrgMc++; - } - if (nChrgFT0A == CnullInt || nChrgFT0C == CnullInt) { - registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 0.5); - return false; - } - registryMC.fill(HIST("Events/hNchTVX"), nChrgMc, 1.5); - if (nChrgMc == CnullInt) { - return false; - } - return true; - } - - template + template int countTracks(T const& tracks, C const& collision, aod::BCsWithTimestamps const& /*bcs*/, float mult) { - if (trkSelOpt.cfgRejectTrkAtTPCSector || v0SelOpt.cfgRejectV0sAtTPCSector || applyCalibGain || applyCalibVtx) { + if (trkSelOpt.rejectTrkAtTPCSector || v0SelOpt.rejectV0sAtTPCSector || defOpt.applyCalibGainFromCCDB || defOpt.applyCalibVtxFromCCDB) { auto bc = collision.template bc_as(); int currentRun = bc.runNumber(); if (runNumber != currentRun) { @@ -1387,26 +1346,28 @@ struct FlattenictyPikp { auto nTrk = 0; for (auto const& track : tracks) { - if (!isGoodTrack(track, magField)) { + if (!isGoodTrack(track, magField)) { continue; } nTrk++; } - if (fillHis) { + if constexpr (fillHist) { registryQC.fill(HIST("Events/hNchVsCent"), nTrk, mult); } return nTrk; } - void phiMod(float& phimodn, const int& mag, const int& charge) + void phiMod(float& phimodn, const float& mag, const int& charge) { - if (mag < Cnull) // for negative polarity field + if (mag < Cnull) { // for negative polarity field phimodn = o2::constants::math::TwoPI - phimodn; - if (charge < Cnull) // for negative charge + } + if (charge < Cnull) { // for negative charge phimodn = o2::constants::math::TwoPI - phimodn; - if (phimodn < Cnull) + } + if (phimodn < Cnull) { LOGF(warning, "phi < Cnull: %g", phimodn); - + } phimodn += o2::constants::math::PI / 18.0f; // to center gap in the middle phimodn = std::fmod(phimodn, o2::constants::math::PI / 9.0f); } @@ -1414,7 +1375,7 @@ struct FlattenictyPikp { template inline void fillNclVsPhiCutQaHist(T const& track, const float phimodn) { - if (cfgFillNclVsPhiCutQaHist) { + if (defOpt.fillNclVsPhiCutQaHist) { registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPtPhi"), track.pt(), phimodn); registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPtPhiNclTPC"), track.eta(), track.pt(), phimodn, track.tpcNClsFound()); registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPtPhiNclPIDTPC"), track.eta(), track.pt(), phimodn, track.tpcNClsPID()); @@ -1425,97 +1386,137 @@ struct FlattenictyPikp { } } - template - bool isGoodTrack(T const& track, const int magfield) + template + bool isGoodTrack(T const& track, const float magfield) { - registryData.fill(HIST("Tracks/hTrkSel"), trkSelAll); - if (std::abs(track.eta()) > trkSelOpt.cfgTrkEtaMax) { - return false; + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelAll); } - registryData.fill(HIST("Tracks/hTrkSel"), trkSelEta); - if (track.pt() < trkSelOpt.cfgTrkPtMin) { + if (std::abs(track.eta()) > trkSelOpt.trkEtaMax) { return false; } - registryData.fill(HIST("Tracks/hTrkSel"), trkSelPt); - if (!isDCAxyCut(track)) { + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelEta); + } + if (track.pt() < trkSelOpt.trkPtMin) { return false; } - registryData.fill(HIST("Tracks/hTrkSel"), trkSelDCA); + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelPt); + } + if constexpr (selDCA) { + if (!isDCAxyCut(track)) { + return false; + } + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelDCA); + } + if (defOpt.applyDCAParam) { + if (defOpt.applyDCAParamFromCCDB) { + if (!isDCAParamCut(track)) { + return false; + } + } else { + if (std::fabs(track.dcaXY()) > trkSelOpt.dcaXY.value[0] + trkSelOpt.dcaXY.value[1] * std::pow(track.pt(), trkSelOpt.dcaXY.value[2]) || std::fabs(track.dcaZ()) > trkSelOpt.dcaZ.value[0] + trkSelOpt.dcaZ.value[1] * std::pow(track.pt(), trkSelOpt.dcaZ.value[2])) { + return false; + } + } + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelCustomDCA); + } + } + } if (track.tpcNClsCrossedRows() < minNCrossedRowsTPC) { return false; } - registryData.fill(HIST("Tracks/hTrkSel"), trkNRowsTPC); - if (trkSelOpt.cfgApplyNcl && track.tpcNClsFound() < trkSelOpt.cfgNclTPCMin) { + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkNRowsTPC); + } + if (trkSelOpt.applyNcl && track.tpcNClsFound() < trkSelOpt.nclTPCMin) { return false; } - registryData.fill(HIST("Tracks/hTrkSel"), trkSelNClsFound); - - if (trkSelOpt.cfgApplyNclPID && track.tpcNClsPID() < trkSelOpt.cfgNclPidTPCMin) { + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelNClsFound); + } + if (trkSelOpt.applyNclPID && track.tpcNClsPID() < trkSelOpt.nclPidTPCMin) { return false; } - registryData.fill(HIST("Tracks/hTrkSel"), trkSelNClsPID); - + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelNClsPID); + } float phimodn = track.phi(); phiMod(phimodn, magfield, track.sign()); - if (cfgFillNclVsPhiCutQaHist) { + if (defOpt.fillNclVsPhiCutQaHist) { fillNclVsPhiCutQaHist(track, phimodn); } - if (trkSelOpt.cfgRejectTrkAtTPCSector && (track.pt() >= trkSelOpt.cfgPhiCutPtMin && phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt()))) { + if (trkSelOpt.rejectTrkAtTPCSector && (track.pt() >= trkSelOpt.phiCutPtMin && phimodn < fPhiCutHigh->Eval(track.pt()) && phimodn > fPhiCutLow->Eval(track.pt()))) { return false; } - if (cfgFillNclVsPhiCutQaHist) { + if (defOpt.fillNclVsPhiCutQaHist) { fillNclVsPhiCutQaHist(track, phimodn); } - registryData.fill(HIST("Tracks/hTrkSel"), trkSelTPCBndr); + if constexpr (fillHist) { + registryQC.fill(HIST("Tracks/hTrkSel"), trkSelTPCBndr); + } return true; } template - void fillV0QA(V const& v0, U const& track) + inline void fillV0QA(V const& v0, U const& track) { + if (typeMother == kGa) { + const float rap = RecoDecay::y(std::array{v0.px(), v0.py(), v0.pz()}, o2::constants::physics::MassGamma); + registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hEtaVsRap"), track.eta(), rap); + registryData.fill(HIST(CprefixV0qa) + HIST("hNsigmaGaVsPt"), track.pt(), track.tpcNSigmaEl()); + registryData.fill(HIST(CprefixV0qa) + HIST("hMassGaVsPt"), v0.mGamma(), v0.pt()); + } + if (typeMother == kKz) { + registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hEtaVsRap"), track.eta(), v0.yK0Short()); + registryData.fill(HIST(CprefixV0qa) + HIST("hNsigmaK0sVsPt"), track.pt(), track.tpcNSigmaPi()); + registryData.fill(HIST(CprefixV0qa) + HIST("hMassK0sVsPt"), v0.mK0Short(), v0.pt()); + } + if (typeMother == kLam || typeMother == kaLam) { + registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hEtaVsRap"), track.eta(), v0.yLambda()); + if (typeMother == kLam) { + registryData.fill(HIST(CprefixV0qa) + HIST("hMassLaVsPt"), v0.mLambda(), v0.pt()); + } else { + registryData.fill(HIST(CprefixV0qa) + HIST("hMassALaVsPt"), v0.mAntiLambda(), v0.pt()); + } + } registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hArmPod"), v0.alpha(), v0.qtarm()); registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hNclVsEta"), track.eta(), track.tpcNClsPID()); - registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hdEdxMIPVsEta"), track.eta(), track.tpcSignal()); + registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hNclVsPt"), track.pt(), track.tpcNClsPID()); + registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hdEdxVsPVsEta"), track.tpcSignal(), track.p(), track.eta()); + registryData.fill(HIST(CprefixV0qa) + HIST(PidDir[id]) + HIST(V0Dir[typeMother]) + HIST("hDCAxyVsPt"), v0.dcapostopv(), track.pt()); } template int selectTypeV0s(C const& collision, T1 const& v0, T2 const& postrk, T2 const& negtrk) { - const float dMassK0s = std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short); - const float dMassL = std::abs(v0.mLambda() - o2::constants::physics::MassLambda0); - const float dMassAL = std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0); - const float dMassG = std::abs(v0.mGamma() - o2::constants::physics::MassGamma); - bool isMassG = false; bool isMassK0s = false; bool isMassL = false; bool isMassAL = false; - if (dMassK0s > v0SelOpt.cfgdmassK && dMassL > v0SelOpt.cfgdmassL && dMassAL > v0SelOpt.cfgdmassL && dMassG < v0SelOpt.cfgdmassG) { + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) > v0SelOpt.dmassK && std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) > v0SelOpt.dmassL && std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) > v0SelOpt.dmassL && std::abs(v0.mGamma() - o2::constants::physics::MassGamma) < v0SelOpt.dmassG) { isMassG = true; } - if (dMassK0s < v0SelOpt.cfgdmassK && dMassL > v0SelOpt.cfgdmassL && dMassAL > v0SelOpt.cfgdmassL && dMassG > v0SelOpt.cfgdmassG) { + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < v0SelOpt.dmassK && std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) > v0SelOpt.dmassL && std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) > v0SelOpt.dmassL && std::abs(v0.mGamma() - o2::constants::physics::MassGamma) > v0SelOpt.dmassG) { isMassK0s = true; } - if (dMassK0s > v0SelOpt.cfgdmassK && dMassL < v0SelOpt.cfgdmassL && dMassG > v0SelOpt.cfgdmassG) { + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) > v0SelOpt.dmassK && std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < v0SelOpt.dmassL && std::abs(v0.mGamma() - o2::constants::physics::MassGamma) > v0SelOpt.dmassG) { isMassL = true; } - if (dMassK0s > v0SelOpt.cfgdmassK && dMassAL < v0SelOpt.cfgdmassL && dMassG > v0SelOpt.cfgdmassG) { + if (std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) > v0SelOpt.dmassK && std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < v0SelOpt.dmassL && std::abs(v0.mGamma() - o2::constants::physics::MassGamma) > v0SelOpt.dmassG) { isMassAL = true; } // Gamma selection - const float yGamma = RecoDecay::y(std::array{v0.px(), v0.py(), v0.pz()}, o2::constants::physics::MassGamma); if (isMassG) { - if (std::abs(yGamma) < v0SelOpt.cfgV0Ymax) { // rapidity cut - if (std::abs(v0.alpha()) < v0SelOpt.cfgArmPodGammasalpha && v0.qtarm() < v0SelOpt.cfgArmPodGammasqT) { // - if (postrk.hasTPC() && std::abs(postrk.tpcNSigmaEl()) < v0SelOpt.cfgNsigmaElTPC) { - if (postrk.hasTOF() && std::abs(postrk.tofNSigmaEl()) < v0SelOpt.cfgNsigmaElTOF) { - return kGa; - } - } - if (negtrk.hasTPC() && std::abs(negtrk.tpcNSigmaEl()) < v0SelOpt.cfgNsigmaElTPC) { - if (negtrk.hasTOF() && std::abs(negtrk.tofNSigmaEl()) < v0SelOpt.cfgNsigmaElTOF) { + if (std::abs(RecoDecay::y(std::array{v0.px(), v0.py(), v0.pz()}, o2::constants::physics::MassGamma)) < v0SelOpt.v0Ymax) { // rapidity cut + if (std::abs(v0.alpha()) < v0SelOpt.armPodGammasalpha && v0.qtarm() < v0SelOpt.armPodGammasqT) { // Arm.-Pod. cut + if (std::abs(v0.dcapostopv()) > v0SelOpt.v0dcaElectronFromGamma && std::abs(v0.dcanegtopv()) > v0SelOpt.v0dcaElectronFromGamma) { // dca to PV + if (std::abs(postrk.tpcNSigmaEl()) < v0SelOpt.nsigmaElTPC && std::abs(negtrk.tpcNSigmaEl()) < v0SelOpt.nsigmaElTPC) { // TPC nsigma cut return kGa; } } @@ -1524,16 +1525,11 @@ struct FlattenictyPikp { } // K0S selection, K0S -> pi + pi if (isMassK0s) { - if (std::abs(v0.yK0Short()) < v0SelOpt.cfgV0Ymax) { // rapidity cut - if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short < v0SelOpt.cfgcTauK0s) { // ctau cut - if (v0.v0cosPA() >= v0SelOpt.cfgCosPAK0s && v0.v0radius() >= v0SelOpt.cfgV0radiusK0s && v0.qtarm() * v0SelOpt.cfgArmPodK0s > std::abs(v0.alpha())) { // - if (postrk.hasTPC() && std::abs(postrk.tpcNSigmaPi()) < v0SelOpt.cfgNsigmaPiTPC) { - if (postrk.hasTOF() && std::abs(postrk.tofNSigmaPi()) < v0SelOpt.cfgNsigmaPiTOF) { - return kKz; - } - } - if (negtrk.hasTPC() && std::abs(negtrk.tpcNSigmaPi()) < v0SelOpt.cfgNsigmaPiTPC) { - if (negtrk.hasTOF() && std::abs(negtrk.tofNSigmaPi()) < v0SelOpt.cfgNsigmaPiTOF) { + if (std::abs(v0.yK0Short()) < v0SelOpt.v0Ymax) { // rapidity cut + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short < v0SelOpt.cTauK0s) { // ctau cut + if (v0.qtarm() * v0SelOpt.armPodK0s > std::abs(v0.alpha())) { // Arm.-Pod. cut + if (std::abs(v0.dcapostopv()) > v0SelOpt.v0dcaK0s && std::abs(v0.dcanegtopv()) > v0SelOpt.v0dcaK0s) { // dca to PV + if (std::abs(postrk.tpcNSigmaPi()) < v0SelOpt.nsigmaPiTPC && std::abs(negtrk.tpcNSigmaPi()) < v0SelOpt.nsigmaPiTPC) { // TPC nsigma cut return kKz; } } @@ -1543,13 +1539,11 @@ struct FlattenictyPikp { } // Lambda selection, L -> p + pi- if (isMassL) { - if (std::abs(v0.yLambda()) < v0SelOpt.cfgV0Ymax) { // rapidity cut - if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0SelOpt.cfgcTauLambda) { // ctau cut - if (v0.v0cosPA() >= v0SelOpt.cfgCosPALambda && v0.v0radius() >= v0SelOpt.cfgV0radiusLambda) { // - if (postrk.hasTPC() && std::abs(postrk.tpcNSigmaPr()) < v0SelOpt.cfgNsigmaPrTPC && negtrk.hasTPC() && std::abs(negtrk.tpcNSigmaPi()) < v0SelOpt.cfgNsigmaPiTPC) { - if (postrk.hasTOF() && std::abs(postrk.tofNSigmaPr()) < v0SelOpt.cfgNsigmaPrTOF && negtrk.hasTOF() && std::abs(negtrk.tofNSigmaPi()) < v0SelOpt.cfgNsigmaPiTOF) { - return kLam; - } + if (std::abs(v0.yLambda()) < v0SelOpt.v0Ymax) { // rapidity cut + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0SelOpt.cTauLambda) { // ctau cut + if (std::abs(v0.dcapostopv()) > v0SelOpt.v0dcaProtonFromLam && std::abs(v0.dcanegtopv()) > v0SelOpt.v0dcaPionFromLam) { // dca to PV + if (std::abs(postrk.tpcNSigmaPr()) < v0SelOpt.nsigmaPrTPC && std::abs(negtrk.tpcNSigmaPi()) < v0SelOpt.nsigmaPiTPC) { // TPC nsigma cut + return kLam; } } } @@ -1557,13 +1551,11 @@ struct FlattenictyPikp { } // antiLambda -> pbar + pi+ if (isMassAL) { - if (std::abs(v0.yLambda()) < v0SelOpt.cfgV0Ymax) { // rapidity cut - if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0SelOpt.cfgcTauLambda) { // ctau cut - if (v0.v0cosPA() >= v0SelOpt.cfgCosPALambda && v0.v0radius() >= v0SelOpt.cfgV0radiusLambda) { // - if (postrk.hasTPC() && std::abs(postrk.tpcNSigmaPi()) < v0SelOpt.cfgNsigmaPiTPC && negtrk.hasTPC() && std::abs(negtrk.tpcNSigmaPr()) < v0SelOpt.cfgNsigmaPrTPC) { - if (postrk.hasTOF() && std::abs(postrk.tofNSigmaPi()) < v0SelOpt.cfgNsigmaPiTOF && negtrk.hasTOF() && std::abs(negtrk.tofNSigmaPr()) < v0SelOpt.cfgNsigmaPrTOF) { - return kaLam; - } + if (std::abs(v0.yLambda()) < v0SelOpt.v0Ymax) { // rapidity cut + if (v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0 < v0SelOpt.cTauLambda) { // ctau cut + if (std::abs(v0.dcapostopv()) > v0SelOpt.v0dcaPionFromLam && std::abs(v0.dcanegtopv()) > v0SelOpt.v0dcaProtonFromLam) { // dca to PV + if (std::abs(postrk.tpcNSigmaPi()) < v0SelOpt.nsigmaPiTPC && std::abs(negtrk.tpcNSigmaPr()) < v0SelOpt.nsigmaPrTPC) { // TPC nsigma cut + return kaLam; } } } @@ -1573,7 +1565,7 @@ struct FlattenictyPikp { } template - bool isGoodV0Track(T1 const& v0, T2 const& /*track*/, const int magfield) + bool isGoodV0Track(T1 const& v0, T2 const& /*track*/, const float magfield) { const auto& posTrack = v0.template posTrack_as(); const auto& negTrack = v0.template negTrack_as(); @@ -1588,31 +1580,27 @@ struct FlattenictyPikp { float negTrackPhiModn = negTrack.phi(); phiMod(posTrackPhiModn, magfield, posTrack.sign()); phiMod(negTrackPhiModn, magfield, negTrack.sign()); - if (v0SelOpt.cfgRejectV0sAtTPCSector) { - if ((posTrack.pt() >= trkSelOpt.cfgPhiCutPtMin && posTrackPhiModn < fPhiCutHigh->Eval(posTrack.pt()) && posTrackPhiModn > fPhiCutLow->Eval(posTrack.pt())) && (negTrack.pt() >= trkSelOpt.cfgPhiCutPtMin && negTrackPhiModn < fPhiCutHigh->Eval(negTrack.pt()) && negTrackPhiModn > fPhiCutLow->Eval(negTrack.pt()))) { + if (v0SelOpt.rejectV0sAtTPCSector) { + if ((posTrack.pt() >= trkSelOpt.phiCutPtMin && posTrackPhiModn < fPhiCutHigh->Eval(posTrack.pt()) && posTrackPhiModn > fPhiCutLow->Eval(posTrack.pt())) && (negTrack.pt() >= trkSelOpt.phiCutPtMin && negTrackPhiModn < fPhiCutHigh->Eval(negTrack.pt()) && negTrackPhiModn > fPhiCutLow->Eval(negTrack.pt()))) { return false; } } registryData.fill(HIST("Tracks/V0qa/hV0Sel"), v0SelRejectV0sAtTPCSector); // V0 topological selections - if (v0.v0cosPA() < v0SelOpt.cfgv0cospa) { + if (v0.v0cosPA() < v0SelOpt.v0cospa) { return false; } registryData.fill(HIST("Tracks/V0qa/hV0Sel"), v0SelCosPA); - if (v0.v0radius() < v0SelOpt.cfgv0Rmin || v0.v0radius() > v0SelOpt.cfgv0Rmax) { + if (v0.v0radius() < v0SelOpt.v0Rmin || v0.v0radius() > v0SelOpt.v0Rmax) { return false; } registryData.fill(HIST("Tracks/V0qa/hV0Sel"), v0SelV0radius); - if (std::abs(v0.dcapostopv()) < v0SelOpt.cfgDCAposToPV || std::abs(v0.dcanegtopv()) < v0SelOpt.cfgDCAnegToPV) { - return false; - } - registryData.fill(HIST("Tracks/V0qa/hV0Sel"), v0SelDCAposToPV); // selection of V0 daughters if (!(isGoodV0DaughterTrack(posTrack) && isGoodV0DaughterTrack(negTrack))) { return false; } registryData.fill(HIST("Tracks/V0qa/hV0Sel"), v0SelDaughters); - if (v0.dcaV0daughters() > v0SelOpt.cfgDCAv0daughter) { + if (v0.dcaV0daughters() > v0SelOpt.dcaV0daughter) { return false; } registryData.fill(HIST("Tracks/V0qa/hV0Sel"), v0SelDCAv0daughter); @@ -1626,39 +1614,29 @@ struct FlattenictyPikp { template bool isGoodV0DaughterTrack(const T& track) { - if (track.eta() < v0SelOpt.cfgV0etamin || track.eta() > v0SelOpt.cfgV0etamax) { + if (track.eta() < v0SelOpt.v0etamin || track.eta() > v0SelOpt.v0etamax) { return false; } - if (!track.hasTPC()) { + if (track.pt() < v0SelOpt.v0ptmin || track.pt() > v0SelOpt.v0ptmax) { return false; } - if (track.tpcNClsCrossedRows() < v0SelOpt.cfgminNCrossedRowsTPC) { + if (track.tpcNClsCrossedRows() < v0SelOpt.v0minNCrossedRowsTPC) { return false; } - if (track.tpcCrossedRowsOverFindableCls() < v0SelOpt.cfgminNCrossedRowsOverFindableClustersTPC) { - return false; - } - if (v0SelOpt.cfgApplyV0sNclFound) { - if (track.tpcNClsFound() < v0SelOpt.cfgV0NclTPCMin) { + if (v0SelOpt.applyV0sNclFound) { + if (track.tpcNClsFound() < v0SelOpt.v0NclTPCMin) { return false; } } - if (v0SelOpt.cfgApplyV0sNclPID) { - if (track.tpcNClsPID() < v0SelOpt.cfgV0NclPidTPCMin) { + if (v0SelOpt.applyV0sNclPID) { + if (track.tpcNClsPID() < v0SelOpt.v0NclPidTPCMin) { return false; } } - if (track.tpcChi2NCl() > v0SelOpt.cfgmaxChi2PerClusterTPC) { - return false; - } - if (v0SelOpt.cfgRequireITS && (!track.hasITS())) { - return false; - } - if (v0SelOpt.cfgRequireITS && track.itsNCls() < v0SelOpt.cfgminITSnClusters) { - return false; - } - if (track.itsChi2NCl() > v0SelOpt.cfgmaxChi2PerClusterITS) { - return false; + if (v0SelOpt.applyV0sNclShared) { + if (track.tpcNClsShared() > v0SelOpt.v0NclSharedTPCMax) { + return false; + } } return true; } @@ -1670,6 +1648,7 @@ struct FlattenictyPikp { registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPt"), track.pt()); registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPhi"), track.phi()); registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hEta"), track.eta()); + registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hTpcInnerParamVsP"), track.tpcInnerParam(), track.p()); registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hDCAXYvsPt"), track.pt(), track.dcaXY()); registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hDCAZvsPt"), track.pt(), track.dcaZ()); @@ -1688,17 +1667,17 @@ struct FlattenictyPikp { } registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hTOFPvsBeta"), track.p(), track.beta()); - if (track.beta() > trkSelOpt.cfgTOFBetaPion && track.beta() < trkSelOpt.cfgTOFBetaPion + 0.05) { // TOF pions + if (track.beta() > trkSelOpt.tofBetaPion && track.beta() < trkSelOpt.tofBetaPion + 0.05) { // TOF pions registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hTOFpi"), track.eta(), track.p(), track.tpcSignal()); } - if (std::abs(track.eta()) < trkSelOpt.cfgTrkEtaMax) { + if (std::abs(track.eta()) < trkSelOpt.trkEtaMax) { if (isDCAxyWoCut(track)) { registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPtVsWOcutDCA"), track.pt(), track.dcaXY()); } } + registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPVsPtEta"), track.p(), track.pt(), track.eta()); } - registryData.fill(HIST(Cprefix) + HIST(Cstatus[ft]) + HIST("hPVsPtEta"), track.p(), track.pt(), track.eta()); } template @@ -1707,27 +1686,29 @@ struct FlattenictyPikp { const float mult = getMult(collision); const float flat = fillFlat(collision); if constexpr (fillHist) { - if (track.p() >= trkSelOpt.cfgMomMIPMin && track.p() <= trkSelOpt.cfgMomMIPMax) { - if (dEdx > trkSelOpt.cfgDeDxMIPMin && dEdx < trkSelOpt.cfgDeDxMIPMax) { // MIP pions - if (cfgStoreThnSparse) { + if (track.p() >= trkSelOpt.momMIPMin && track.p() <= trkSelOpt.momMIPMax) { + if (dEdx > trkSelOpt.dEdxMIPMin && dEdx < trkSelOpt.dEdxMIPMax) { // MIP pions + if (defOpt.storeThnSparse) { registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hMIP"), mult, flat, track.eta(), dEdx); } else { registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hMIP"), track.eta(), dEdx); } registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hMIPVsPhi"), track.phi(), dEdx); - registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hMIPVsPhiVsEta"), track.phi(), dEdx, track.eta()); registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("pMIPVsPhi"), track.phi(), dEdx); + registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hMIPVsPhiVsEta"), track.phi(), dEdx, track.eta()); + registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hMIPNClTPCPidvsEta"), track.eta(), track.tpcNClsPID()); } - if (dEdx > trkSelOpt.cfgDeDxMIPMax + 10. && dEdx < trkSelOpt.cfgDeDxMIPMax + 30.) { // Plateau electrons - if (std::abs(track.beta() - 1) < trkSelOpt.cfgBetaPlateuMax) { - if (cfgStoreThnSparse) { + if (dEdx > trkSelOpt.dEdxMIPMax + 10. && dEdx < trkSelOpt.dEdxMIPMax + 30.) { // Plateau electrons + if (std::abs(track.beta() - 1) < trkSelOpt.betaPlateuMax) { + if (defOpt.storeThnSparse) { registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hPlateau"), mult, flat, track.eta(), dEdx); } else { registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hPlateau"), track.eta(), dEdx); } registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hPlateauVsPhi"), track.phi(), dEdx); - registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hPlateauVsPhiVsEta"), track.phi(), dEdx, track.eta()); registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("pPlateauVsPhi"), track.phi(), dEdx); + registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hPlateauVsPhiVsEta"), track.phi(), dEdx, track.eta()); + registryData.fill(HIST(Cprefix) + HIST(CstatCalib[ft]) + HIST(Ccharge[chrg]) + HIST("hPlateauNClTPCPidvsEta"), track.eta(), track.tpcNClsPID()); } } } @@ -1740,7 +1721,7 @@ struct FlattenictyPikp { if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelAll); } - if (evtSelOpt.cfgCustomTVX) { + if (evtSelOpt.customTVX) { if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } @@ -1752,43 +1733,43 @@ struct FlattenictyPikp { if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelSel8); } - if (evtSelOpt.cfgRemoveITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + if (evtSelOpt.removeITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; } if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelNoITSROFrameBorder); } - if (evtSelOpt.cfgRemoveNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + if (evtSelOpt.removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; } if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelkNoTimeFrameBorder); } - if (evtSelOpt.cfgRemoveNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + if (evtSelOpt.removeNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelkNoSameBunchPileup); } - if (evtSelOpt.cfgRequireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + if (evtSelOpt.requireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelkIsGoodZvtxFT0vsPV); } - if (evtSelOpt.cfgRequireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + if (evtSelOpt.requireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { return false; } if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelkIsVertexITSTPC); } - if (evtSelOpt.cfgRequirekIsVertexTOFmatched && !collision.selection_bit(aod::evsel::kIsVertexTOFmatched)) { + if (evtSelOpt.requirekIsVertexTOFmatched && !collision.selection_bit(aod::evsel::kIsVertexTOFmatched)) { return false; } if constexpr (fillHist) { registryQC.fill(HIST("Events/hEvtSel"), evtSelkIsVertexTOFmatched); } - if (std::abs(collision.posZ()) > evtSelOpt.cfgCutVtxZ) { + if (std::abs(collision.posZ()) > evtSelOpt.cutVtxZ) { return false; } if constexpr (fillHist) { @@ -1814,105 +1795,128 @@ struct FlattenictyPikp { { int iRing = -1; - if (i_ch >= Cfv0IndexPhi[0] && i_ch < Cfv0IndexPhi[1]) { - if (i_ch < Cfv0IndexPhi[0] + 4) { + if (i_ch >= cFV0IndexPhi[0] && i_ch < cFV0IndexPhi[1]) { + if (i_ch < cFV0IndexPhi[0] + 4) { iRing = i_ch; } else { - if (i_ch == Cfv0IndexPhi[1] - 1) { + if (i_ch == cFV0IndexPhi[1] - 1) { iRing = i_ch - 3; // 4; - } else if (i_ch == Cfv0IndexPhi[1] - 2) { + } else if (i_ch == cFV0IndexPhi[1] - 2) { iRing = i_ch - 1; // 5; - } else if (i_ch == Cfv0IndexPhi[1] - 3) { + } else if (i_ch == cFV0IndexPhi[1] - 3) { iRing = i_ch + 1; // 6; - } else if (i_ch == Cfv0IndexPhi[1] - 4) { + } else if (i_ch == cFV0IndexPhi[1] - 4) { iRing = i_ch + 3; // 7; } } - } else if (i_ch >= Cfv0IndexPhi[1] && i_ch < Cfv0IndexPhi[2]) { - if (i_ch < Cfv0IndexPhi[2] - 4) { + } else if (i_ch >= cFV0IndexPhi[1] && i_ch < cFV0IndexPhi[2]) { + if (i_ch < cFV0IndexPhi[2] - 4) { iRing = i_ch; } else { - if (i_ch == Cfv0IndexPhi[2] - 1) { + if (i_ch == cFV0IndexPhi[2] - 1) { iRing = i_ch - 3; // 12; - } else if (i_ch == Cfv0IndexPhi[2] - 2) { + } else if (i_ch == cFV0IndexPhi[2] - 2) { iRing = i_ch - 1; // 13; - } else if (i_ch == Cfv0IndexPhi[2] - 3) { + } else if (i_ch == cFV0IndexPhi[2] - 3) { iRing = i_ch + 1; // 14; - } else if (i_ch == Cfv0IndexPhi[2] - 4) { + } else if (i_ch == cFV0IndexPhi[2] - 4) { iRing = i_ch + 3; // 15; } } - } else if (i_ch >= Cfv0IndexPhi[2] && i_ch < Cfv0IndexPhi[3]) { - if (i_ch < Cfv0IndexPhi[3] - 4) { + } else if (i_ch >= cFV0IndexPhi[2] && i_ch < cFV0IndexPhi[3]) { + if (i_ch < cFV0IndexPhi[3] - 4) { iRing = i_ch; } else { - if (i_ch == Cfv0IndexPhi[3] - 1) { + if (i_ch == cFV0IndexPhi[3] - 1) { iRing = i_ch - 3; // 20; - } else if (i_ch == Cfv0IndexPhi[3] - 2) { + } else if (i_ch == cFV0IndexPhi[3] - 2) { iRing = i_ch - 1; // 21; - } else if (i_ch == Cfv0IndexPhi[3] - 3) { + } else if (i_ch == cFV0IndexPhi[3] - 3) { iRing = i_ch + 1; // 22; - } else if (i_ch == Cfv0IndexPhi[3] - 4) { + } else if (i_ch == cFV0IndexPhi[3] - 4) { iRing = i_ch + 3; // 23; } } - } else if (i_ch >= Cfv0IndexPhi[3] && i_ch < Cfv0IndexPhi[4]) { - if (i_ch < Cfv0IndexPhi[3] + 4) { + } else if (i_ch >= cFV0IndexPhi[3] && i_ch < cFV0IndexPhi[4]) { + if (i_ch < cFV0IndexPhi[3] + 4) { iRing = i_ch; } else { - if (i_ch == Cfv0IndexPhi[4] - 5) { + if (i_ch == cFV0IndexPhi[4] - 5) { iRing = i_ch - 3; // 28; - } else if (i_ch == Cfv0IndexPhi[4] - 6) { + } else if (i_ch == cFV0IndexPhi[4] - 6) { iRing = i_ch - 1; // 29; - } else if (i_ch == Cfv0IndexPhi[4] - 7) { + } else if (i_ch == cFV0IndexPhi[4] - 7) { iRing = i_ch + 1; // 30; - } else if (i_ch == Cfv0IndexPhi[4] - 8) { + } else if (i_ch == cFV0IndexPhi[4] - 8) { iRing = i_ch + 3; // 31; } } - } else if (i_ch == Cfv0IndexPhi[4]) { - iRing = Cfv0IndexPhi[4]; - } else if (i_ch == Cfv0IndexPhi[4] + 8) { + } else if (i_ch == cFV0IndexPhi[4]) { + iRing = cFV0IndexPhi[4]; + } else if (i_ch == cFV0IndexPhi[4] + 8) { iRing = i_ch - 7; // 33; - } else if (i_ch == Cfv0IndexPhi[4] + 1) { + } else if (i_ch == cFV0IndexPhi[4] + 1) { iRing = i_ch + 1; // 34; - } else if (i_ch == Cfv0IndexPhi[4] + 9) { + } else if (i_ch == cFV0IndexPhi[4] + 9) { iRing = i_ch - 6; // 35; - } else if (i_ch == Cfv0IndexPhi[4] + 2) { + } else if (i_ch == cFV0IndexPhi[4] + 2) { iRing = i_ch + 2; // 36; - } else if (i_ch == Cfv0IndexPhi[4] + 10) { + } else if (i_ch == cFV0IndexPhi[4] + 10) { iRing = i_ch - 5; // 37; - } else if (i_ch == Cfv0IndexPhi[4] + 3) { + } else if (i_ch == cFV0IndexPhi[4] + 3) { iRing = i_ch + 3; // 38; - } else if (i_ch == Cfv0IndexPhi[4] + 11) { + } else if (i_ch == cFV0IndexPhi[4] + 11) { iRing = i_ch - 4; // 39; - } else if (i_ch == Cfv0IndexPhi[4] + 15) { + } else if (i_ch == cFV0IndexPhi[4] + 15) { iRing = i_ch - 7; // 40; - } else if (i_ch == Cfv0IndexPhi[4] + 7) { + } else if (i_ch == cFV0IndexPhi[4] + 7) { iRing = i_ch + 2; // 41; - } else if (i_ch == Cfv0IndexPhi[4] + 14) { + } else if (i_ch == cFV0IndexPhi[4] + 14) { iRing = i_ch - 4; // 42; - } else if (i_ch == Cfv0IndexPhi[4] + 6) { + } else if (i_ch == cFV0IndexPhi[4] + 6) { iRing = i_ch + 5; // 43; - } else if (i_ch == Cfv0IndexPhi[4] + 13) { + } else if (i_ch == cFV0IndexPhi[4] + 13) { iRing = i_ch - 1; // 44; - } else if (i_ch == Cfv0IndexPhi[4] + 5) { + } else if (i_ch == cFV0IndexPhi[4] + 5) { iRing = i_ch + 8; // 45; - } else if (i_ch == Cfv0IndexPhi[4] + 12) { + } else if (i_ch == cFV0IndexPhi[4] + 12) { iRing = i_ch + 2; // 46; - } else if (i_ch == Cfv0IndexPhi[4] + 4) { + } else if (i_ch == cFV0IndexPhi[4] + 4) { iRing = i_ch + 11; // 47; } return iRing; } + template + float getGenCent(C const& collision) + { + float val = -999.0; + switch (defOpt.multEst.value) { + case MultE::CnoMult: + return val; + case MultE::CmultFT0C: + return collision.centFT0C(); + case MultE::CmultFT0M: + return collision.centFT0M(); + default: + LOGF(fatal, "No valid centrality estimator: %s", defOpt.multEst.value); + return val; + } + } + template float getMult(C const& collision) { float val = -999.0; - switch (multEst) { + switch (defOpt.multEst.value) { case MultE::CnoMult: return val; + case MultE::CmultFT0C: + if constexpr (!isMC) { + return collision.centFT0C(); + } else { + return collision.multMCFT0C(); + } break; case MultE::CmultFT0M: if constexpr (!isMC) { @@ -1925,13 +1929,12 @@ struct FlattenictyPikp { if constexpr (!isMC) { return collision.multTPC(); } else { - LOG(fatal) << "No valid multiplicity estimator: " << multEst; - return val; + return collision.multMCNParticlesEta08(); } break; default: - return collision.centFT0M(); - break; + LOGF(fatal, "No valid multiplicity estimator: %s", defOpt.multEst.value); + return val; } } @@ -1979,13 +1982,12 @@ struct FlattenictyPikp { template float fillFlat(C const& collision) { - rhoLatticeFV0.fill(0); - fv0AmplitudeWoCalib.fill(0); + float oneMinusFlat{9999}; if (collision.has_foundFV0()) { auto fv0 = collision.foundFV0(); - std::bitset<8> fV0Triggers = fv0.triggerMask(); - bool isOkFV0OrA = fV0Triggers[o2::fit::Triggers::bitA]; - if (isOkFV0OrA) { + if (TESTBIT(fv0.triggerMask(), o2::fit::Triggers::bitA)) { + std::array rhoLatticeFV0{0.}; + std::array fv0AmplitudeWoCalib{0.}; for (std::size_t ich = 0; ich < fv0.channel().size(); ich++) { float amplCh = fv0.amplitude()[ich]; int chv0 = fv0.channel()[ich]; @@ -1993,12 +1995,13 @@ struct FlattenictyPikp { if constexpr (fillHist) { registryQC.fill(HIST("FV0/hFV0amp"), chv0, amplCh); registryQC.fill(HIST("FV0/pFV0amp"), chv0, amplCh); - if (applyCalibGain) { + if (defOpt.applyCalibGainFromCCDB) { registryQC.fill(HIST("FV0/hFV0ampCorr"), chv0, amplCh / fv0AmplCorr[chv0]); + registryQC.fill(HIST("FV0/pFV0ampCorr"), chv0, amplCh / fv0AmplCorr[chv0]); } } if (amplCh > Cnull) { - if (applyCalibGain) { // equalize gain channel-by-channel + if (defOpt.applyCalibGainFromCCDB) { // equalize gain channel-by-channel amplCh /= fv0AmplCorr[chv0]; } if (chv0phi > Cnull) { @@ -2014,7 +2017,7 @@ struct FlattenictyPikp { if constexpr (fillHist) { registryQC.fill(HIST("FV0/hFV0AmplvsVtxzWoCalib"), collision.posZ(), rhoLatticeFV0[chv0phi]); } - if (applyCalibVtx) { + if (defOpt.applyCalibVtxFromCCDB) { rhoLatticeFV0[chv0phi] *= zVtxMap->GetBinContent(zVtxMap->GetXaxis()->FindBin(chv0phi), zVtxMap->GetYaxis()->FindBin(collision.posZ())); if constexpr (fillHist) { registryQC.fill(HIST("FV0/hFV0AmplvsVtxzCalib"), collision.posZ(), rhoLatticeFV0[chv0phi]); @@ -2024,13 +2027,31 @@ struct FlattenictyPikp { } } float flattenicityFV0 = calcFlatenicity(rhoLatticeFV0); - return 1. - flattenicityFV0; - } else { - return 9999; + oneMinusFlat = 1. - flattenicityFV0; + } + } + return oneMinusFlat; + } + + template + bool isDCAParamCut(T const& track) const + { + const float dcaXY = track.dcaXY(); + const float dcaZ = track.dcaZ(); + const float pt = track.pt(); + float selDCAxy = 0.f; + float selDCAz = 0.f; + + if (dcaparam.lCalibLoaded) { + selDCAxy = dcaparam.hDCAxyParam->GetBinContent(1) + dcaparam.hDCAxyParam->GetBinContent(2) / std::pow(std::abs(pt), dcaparam.hDCAxyParam->GetBinContent(3)); + selDCAxy *= trkSelOpt.nsigmaDCAxy.value; + selDCAz = dcaparam.hDCAzParam->GetBinContent(1) + dcaparam.hDCAzParam->GetBinContent(2) / std::pow(std::abs(pt), dcaparam.hDCAzParam->GetBinContent(3)); + selDCAz *= trkSelOpt.nsigmaDCAz.value; + if (std::abs(dcaXY) > selDCAxy || std::abs(dcaZ) > selDCAz) { + return false; } - } else { - return 9999; } + return true; } template @@ -2087,8 +2108,8 @@ struct FlattenictyPikp { auto v0sPerCollision = v0s.sliceBy(perColV0s, collision.globalIndex()); v0sPerCollision.bindExternalIndices(&tracks); filldEdx(tracksPerCollision, v0sPerCollision, collision, bcs); - if (cfgFillDCAxyHist) { - static_for<0, 4>([&](auto i) { + if (defOpt.fillDCAxyHist) { + static_for<0, 2>([&](auto i) { fillDCA(tracksPerCollision, collision, bcs); }); } @@ -2114,8 +2135,8 @@ struct FlattenictyPikp { float maxPhi = 0; float dPhi = 0; - float etaMinFV0bins[CmaxRingsFV0] = {0.0}; - float etaMaxFV0bins[CmaxRingsFV0] = {0.0}; + std::array etaMinFV0bins{0.}; + std::array etaMaxFV0bins{0.}; for (int i = 0; i < CmaxRingsFV0; ++i) { etaMaxFV0bins[i] = CmaxEtaFV0 - i * CdEtaFV0; if (i < CmaxRingsFV0 - 1) { @@ -2125,7 +2146,7 @@ struct FlattenictyPikp { } } - rhoLatticeFV0.fill(0); + std::array rhoLatticeFV0{0.}; std::vector vNch; float nChFV0{0}; for (const auto& mcPart : mcparts) { @@ -2176,218 +2197,218 @@ struct FlattenictyPikp { return flatFV0; } - template - void bookMcHist() - { - AxisSpec ptAxis{binOpt.axisPt, "#it{p}_{T} (GeV/#it{c})"}; - constexpr int ChistIdx = id + pidSgn * Npart; - auto idx = static_cast(id); - const std::string strID = Form("/%s/%s", (pidSgn == CnullInt && id < Npart) ? "pos" : "neg", Pid[idx]); - hPtEffRec[ChistIdx] = registryMC.add("Tracks/hPtEffRec" + strID, " ; #it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis}); - hPtEffGen[ChistIdx] = registryMC.add("Tracks/hPtEffGen" + strID, " ; #it{p}_{T} (GeV/#it{c})", kTH1F, {ptAxis}); - } - - template - void initEfficiency() - { - static_assert(pidSgn == CnullInt || pidSgn == ConeInt); - static_assert(id > CnullInt || id < Npart); - constexpr int Cidx = id + pidSgn * Npart; - const TString partName = PidChrg[Cidx]; - THashList* lhash = new THashList(); - lhash->SetName(partName); - listEfficiency->Add(lhash); - - auto bookEff = [&](const TString eName, auto h) { - const TAxis* axis = h->GetXaxis(); - TString eTitle = h->GetTitle(); - eTitle.ReplaceAll("Numerator", "").Strip(TString::kBoth); - eTitle = Form("%s;%s;Efficiency", eTitle.Data(), axis->GetTitle()); - lhash->Add(new TEfficiency(eName, eTitle, axis->GetNbins(), axis->GetXbins()->GetArray())); - }; - - const int idx = id + pidSgn * Npart; - bookEff("hEffvsPt", hPtEffRec[idx]); - } + Preslice perCollTrk = aod::track::collisionId; - template - void fillEfficiency() + void processMcEfficiency(CollsMCExtraMult::iterator const& mcCollision, + soa::SmallGroups const& collisions, + aod::BCsWithTimestamps const& /*bcs*/, + aod::FV0As const& /*fv0s*/, + aod::McParticles const& particles, + MyLabeledPIDTracks const& tracks) { - static_assert(pidSgn == CnullInt || pidSgn == ConeInt); - constexpr int ChistIdx = id + pidSgn * Npart; - const char* partName = PidChrg[ChistIdx]; - THashList* lhash = static_cast(listEfficiency->FindObject(partName)); - if (!lhash) { - LOG(warning) << "No efficiency object found for particle " << partName; - return; + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto multMC = -999.; + if (evtSelOpt.useMultMCmidrap) { + multMC = mcCollision.multMCNParticlesEta08(); + } else { + multMC = getMultMC(mcCollision); } + const float flatMC = fillFlatMC(particles); - auto fillEff = [&](const TString eName, auto num, auto den) { - TEfficiency* eff = static_cast(lhash->FindObject(eName)); - if (!eff) { - LOG(warning) << "Cannot find TEfficiency " << eName; - return; + auto multRec = -999.; + auto flatRec = -999.; + bool gtOneRec = false; + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; } - eff->SetTotalHistogram(*den, "f"); - eff->SetPassedHistogram(*num, "f"); - }; - fillEff("hEffvsPt", hPtEffRec[ChistIdx], hPtEffGen[ChistIdx]); - } - - template - void fillMCRecTrack(MyLabeledPIDTracks::iterator const& track, const float mult, const float flat) - { - static_assert(pidSgn == CnullInt || pidSgn == ConeInt); - constexpr int ChistIdx = id + pidSgn * Npart; - // LOG(debug) << "fillMCRecTrack for pidSgn '" << pidSgn << "' and id '" << static_cast(id) << " with index " << ChistIdx; - const aod::McParticles::iterator& mcParticle = track.mcParticle(); - const CollsGen::iterator& collision = track.collision_as>(); - // const CollsGen::iterator& collision = track.collision_as(); - - if (!isChrgParticle(mcParticle.pdgCode())) { - return; - } - if (std::abs(mcParticle.eta()) > trkSelOpt.cfgTrkEtaMax) { - return; + if (evtSelOpt.removeSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + gtOneRec = true; + multRec = getMult(collision); + flatRec = fillFlat(collision); } - if (mcParticle.pt() < trkSelOpt.cfgTrkPtMin) { - return; + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ()); + if (gtOneRec) { + registryMC.fill(HIST("Events/hVtxZGenGtOneRec"), mcCollision.posZ()); } - if (!isPID(mcParticle)) { - return; + /* Tracking eff. den */ + for (const auto& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (!particle.isPhysicalPrimary()) { + continue; + } + if (std::abs(particle.eta()) > trkSelOpt.trkEtaMax) { + continue; + } + if (gtOneRec) { + static_for<0, 2>([&](auto i) { + constexpr int Cidx = i.value; + if (std::fabs(particle.pdgCode()) == pDGs[Cidx]) { + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTeffGenPrimRecEvt), multRec, flatRec, particle.pt()); + } + }); + } } - - if ((collision.has_mcCollision() && (mcParticle.mcCollisionId() != collision.mcCollisionId())) || !collision.has_mcCollision()) { - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == CprocessIdWeak) { - hDCAxyRecBadCollWeak[ChistIdx]->Fill(track.pt(), track.dcaXY()); - } else { - hDCAxyRecBadCollMat[ChistIdx]->Fill(track.pt(), track.dcaXY()); + /* Tracking eff. num */ + for (const auto& collision : collisions) { + if (trkSelOpt.rejectTrkAtTPCSector || defOpt.applyCalibGainFromCCDB || defOpt.applyCalibVtxFromCCDB) { + auto bc = collision.bc_as(); + int currentRun = bc.runNumber(); + if (runNumber != currentRun) { + initCCDB(bc); + runNumber = currentRun; } - } else { - hDCAxyRecBadCollPrim[ChistIdx]->Fill(track.pt(), track.dcaXY()); } - } + registryMC.fill(HIST("Events/hMCEventStatus"), static_cast(MCEventStatus::kMcEvtAll), multRec, flatRec); + if (!isGoodEvent(collision)) { + continue; + } + registryMC.fill(HIST("Events/hMCEventStatus"), static_cast(MCEventStatus::kEvtSelected), multRec, flatRec); + if (!collision.has_mcCollision()) { + continue; + } + registryMC.fill(HIST("Events/hMCEventStatus"), static_cast(MCEventStatus::kEvtHasMcColl), multRec, flatRec); + if (evtSelOpt.removeSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + registryMC.fill(HIST("Events/hMCEventStatus"), static_cast(MCEventStatus::kEvtSplitVtxRemoved), multRec, flatRec); + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ()); - if (collision.has_mcCollision() && (mcParticle.mcCollisionId() == collision.mcCollisionId())) { - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == CprocessIdWeak) { - hPtEffRecGoodCollWeak[ChistIdx]->Fill(mult, flat, track.pt()); - hPtVsDCAxyRecGoodCollWeak[ChistIdx]->Fill(track.pt(), track.dcaXY()); - } else { - hPtEffRecGoodCollMat[ChistIdx]->Fill(mult, flat, track.pt()); - hPtVsDCAxyRecGoodCollMat[ChistIdx]->Fill(track.pt(), track.dcaXY()); + const auto& groupedTrks = tracks.sliceBy(perCollTrk, collision.globalIndex()); + int nTrk = 0; + for (const auto& track : groupedTrks) { + if (!isGoodTrack(track, magField)) { + continue; } - } else { - hPtEffRecGoodCollPrim[ChistIdx]->Fill(mult, flat, track.pt()); - hPtVsDCAxyRecGoodCollPrim[ChistIdx]->Fill(track.pt(), track.dcaXY()); - if (isDCAxyCut(track)) { - hPtEffRec[ChistIdx]->Fill(mcParticle.pt()); + if (!track.has_mcParticle()) { + registryMC.fill(HIST("Tracks/hPtFakes"), multRec, flatRec, track.pt()); + continue; + } + const auto& particle = track.mcParticle_as(); + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (!particle.isPhysicalPrimary()) { + continue; } + static_for<0, 2>([&](auto i) { + constexpr int Cidx = i.value; + if (std::fabs(particle.pdgCode()) == pDGs[Cidx]) { + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTeffPrimRecEvt), multRec, flatRec, track.pt()); + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTmcClosureRec), multMC, flatMC, track.pt()); // closure + } + }); + nTrk++; + registryMC.fill(HIST("Tracks/hPtRes"), particle.pt(), (track.pt() - particle.pt()) / particle.pt()); } + registryQC.fill(HIST("Events/hNchVsCent"), nTrk, multRec); } } + PROCESS_SWITCH(FlattenictyPikp, processMcEfficiency, "process tracking efficiency", false); - template - void fillMCGenRecEvt(aod::McParticles::iterator const& mcParticle, const float mult, const float flat) + void processMcSgnEvtLoss(CollsMCExtraMult::iterator const& mcCollision, + soa::SmallGroups const& collisions, + aod::BCsWithTimestamps const& /*bcs*/, + aod::FV0As const& /*fv0s*/, + aod::McParticles const& particles) { - static_assert(pidSgn == CnullInt || pidSgn == ConeInt); - constexpr int ChistIdx = id + pidSgn * Npart; + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto multMC = -999; + if (evtSelOpt.useMultMCmidrap) { + multMC = mcCollision.multMCNParticlesEta08(); + } else { + multMC = getMultMC(mcCollision); + } + /* + cauto centMcGen = -1.; + if (evtSelOpt.customGenCent) { + if constexpr (hasFT0C) { + centMcGen = mcCollision.mcpercft0c(); + } else if (hasFT0M) { + centMcGen = mcCollision.mcpercft0m(); + } + } else { + if (defOpt.multEst == MultE::CmultFT0C) { + centMcGen = mcCollision.centFT0C(); + } else if (defOpt.multEst == MultE::CmultFT0M) { + centMcGen = mcCollision.centFT0M(); + } else { + centMcGen = -1.; + } + } + */ + registryMC.fill(HIST("Events/hNchGen"), multMC); + registryMC.fill(HIST("Events/hNchGenCent"), getGenCent(mcCollision)); + const float flatMC = fillFlatMC(particles); + registryMC.fill(HIST("Events/hFlatMCGen"), flatMC); - if (!isPID(mcParticle)) { + /* Signal/event loss den */ + registryMC.fill(HIST("Events/hEvtMcGen"), 0.5); + if (evtSelOpt.zVtxCutMC && std::abs(mcCollision.posZ()) > evtSelOpt.cutVtxZ) { return; } - if constexpr (isGtZeroColl) { - hPtGenRecEvtGtZero[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - if (mcParticle.isPhysicalPrimary()) { - hPtGenPrimRecEvtGtZero[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - hPtEffGen[ChistIdx]->Fill(mcParticle.pt()); - } - } else { - hPtGenRecEvt[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - if (mcParticle.isPhysicalPrimary()) { - hPtGenPrimRecEvt[ChistIdx]->Fill(mult, flat, mcParticle.pt()); + registryMC.fill(HIST("Events/hEvtMcGen"), 1.5); + if (evtSelOpt.useINELCutMC) { + if (!o2::pwglf::isINELgt0mc(particles, pdg)) { + return; } } - } - - template - void fillMCGen(aod::McParticles::iterator const& mcParticle, const float mult, const float flat) - { - static_assert(pidSgn == CnullInt || pidSgn == ConeInt); - constexpr int ChistIdx = id + pidSgn * Npart; - - if (!isPID(mcParticle)) { + registryMC.fill(HIST("Events/hEvtMcGen"), 2.5); + if (evtSelOpt.useInelgt0wTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { return; } + registryMC.fill(HIST("Events/hEvtMcGen"), 3.5); + registryMC.fill(HIST("Events/hNchVsFlatGenINELgt0"), multMC, flatMC); // Evt loss den - if constexpr (evtSel) { - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == CprocessIdWeak) { - hPtEffGenWeakEvtSelGen[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - } else { - hPtEffGenMatEvtSelGen[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - } - } else { - hPtEffGenPrimEvtSelGen[ChistIdx]->Fill(mult, flat, mcParticle.pt()); + for (const auto& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { + continue; } - } else { - if (!mcParticle.isPhysicalPrimary()) { - if (mcParticle.getProcess() == CprocessIdWeak) { - hPtEffGenWeak[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - } else { - hPtEffGenMat[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - } - } else { - hPtEffGenPrim[ChistIdx]->Fill(mult, flat, mcParticle.pt()); - // hPtEffGen[ChistIdx]->Fill(mcParticle.pt()); + if (!particle.isPhysicalPrimary()) { + continue; } + if (std::abs(particle.eta()) > trkSelOpt.trkEtaMax) { + continue; + } + static_for<0, 2>([&](auto i) { + constexpr int Cidx = i.value; + if (std::fabs(particle.pdgCode()) == pDGs[Cidx]) { + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTgenPrimSgn), multMC, flatMC, particle.pt()); // Sgn loss den + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTmcClosureGenPrim), multMC, flatMC, particle.pt()); // closure + } + }); } - } - - Preslice perCollTrk = aod::track::collisionId; - - void processMC(CollsMCExtraMult::iterator const& mcCollision, - soa::SmallGroups const& collisions, - aod::BCsWithTimestamps const& /*bcs*/, - aod::FV0As const& /*fv0s*/, - aod::McParticles const& particles, - MyLabeledPIDTracks const& tracks) - { - LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); - auto multMC = -1.; - if (evtSelOpt.useMultMCmidrap || multEst == 2) { // use generated Nch in ∣eta∣ < 0.8 - multMC = countPart(particles); - } else { - multMC = getMultMC(mcCollision); // using McCentFT0Ms - } - const float flatMC = fillFlatMC(particles); - registryMC.fill(HIST("Events/hFlatMCGen"), flatMC); - // Loop on rec collisions - // Obtain here: Denominator of tracking efficiency; Numerator event and signal loss - // - bool gtZeroColl = false; - auto multRecGt1 = -999; - auto flatRec = -999; + /* Signal/event loss num */ + auto multRec = -999.; + auto flatRec = -999.; + bool gtOneRec = false; for (const auto& collision : collisions) { - if (!isGoodEvent(collision)) { + if (!isGoodEvent(collision)) { continue; } - registryMC.fill(HIST("Events/hCentVsFlatRecINELgt0"), multRecGt1, flatRec); // Evt split den - if (evtSelOpt.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (evtSelOpt.removeSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - registryMC.fill(HIST("Events/hCentVsFlatRecINELgt0wRecEvt"), multRecGt1, flatRec); // Evt split num, w/ Nrec > 0 - gtZeroColl = true; - multRecGt1 = getMult(collision); - flatRec = fillFlat(collision); + gtOneRec = true; + multRec = getMult(collision); + flatRec = fillFlat(collision); } - if (gtZeroColl) { - registryMC.fill(HIST("Events/hCentVsFlatRecINELgt0wRecEvtSel"), multRecGt1, flatRec); // Evt split num, w/ Nrec > 0 + Evt. sel - registryMC.fill(HIST("Events/hNchGenVsCent"), multMC, multRecGt1); - registryMC.fill(HIST("Events/hNchVsFlatGenINELgt0wRecEvtSel"), multMC, flatMC); // Evt loss num, w/ Nrec > 0 + Evt. sel + registryMC.fill(HIST("Events/hCentVsFlatRecINELgt0"), multRec, flatRec); // Evt split den + + if (gtOneRec) { + registryMC.fill(HIST("Events/hCentVsFlatRecINELgt0wRecEvtSel"), multRec, flatRec); // Evt split num + registryMC.fill(HIST("Events/hNchGenVsCent"), multMC, multRec); + registryMC.fill(HIST("Events/hNchVsFlatGenINELgt0wRecEvtSel"), multMC, flatMC); // Evt loss num + registryMC.fill(HIST("Events/hFlatResponse"), flatMC, flatRec); } for (const auto& particle : particles) { @@ -2397,38 +2418,30 @@ struct FlattenictyPikp { if (!particle.isPhysicalPrimary()) { continue; } - if (std::abs(particle.eta()) > trkSelOpt.cfgTrkEtaMax) { + if (std::abs(particle.eta()) > trkSelOpt.trkEtaMax) { continue; } - if (particle.pt() < trkSelOpt.cfgTrkPtMin) { - continue; - } - if (gtZeroColl) { - static_for<0, 1>([&](auto pidSgn) { - fillMCGenRecEvt(particle, multMC, flatMC); - fillMCGenRecEvt(particle, multMC, flatMC); - fillMCGenRecEvt(particle, multMC, flatMC); - }); - static_for<0, 4>([&](auto i) { + if (gtOneRec) { + static_for<0, 2>([&](auto i) { constexpr int Cidx = i.value; - if (std::fabs(particle.pdgCode()) == PDGs[Cidx]) { - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTrecCollPrimSgn), multMC, flatMC, particle.pt()); // Sgn loss num - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTeffGenPrimRecEvt), multRecGt1, flatRec, particle.pt()); // Tracking eff. den + if (std::fabs(particle.pdgCode()) == pDGs[Cidx]) { + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTrecCollPrimSgn), multMC, flatMC, particle.pt()); // Sgn loss num } }); - } else { - static_for<0, 1>([&](auto pidSgn) { - fillMCGenRecEvt(particle, multMC, flatMC); - fillMCGenRecEvt(particle, multMC, flatMC); - fillMCGenRecEvt(particle, multMC, flatMC); - }); } } - - // Loop on rec collisions - // Obtain here: Numerator of tracking efficiency; Secondary contamination correction + } + PROCESS_SWITCH(FlattenictyPikp, processMcSgnEvtLoss, "process signal/event loss", false); + + void processMcRecTrack(CollsMCExtraMult::iterator const& mcCollision, + soa::SmallGroups const& collisions, + aod::BCsWithTimestamps const& /*bcs*/, + aod::FV0As const& /*fv0s*/, + aod::McParticles const& /*particles*/, + MyLabeledPIDTracks const& tracks) + { for (const auto& collision : collisions) { - if (trkSelOpt.cfgRejectTrkAtTPCSector || applyCalibGain || applyCalibVtx) { + if (trkSelOpt.rejectTrkAtTPCSector || defOpt.applyCalibGainFromCCDB || defOpt.applyCalibVtxFromCCDB) { auto bc = collision.bc_as(); int currentRun = bc.runNumber(); if (runNumber != currentRun) { @@ -2436,43 +2449,24 @@ struct FlattenictyPikp { runNumber = currentRun; } } - if (!isGoodEvent(collision)) { + if (evtSelOpt.removeSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ()); - if (evtSelOpt.cfgRemoveSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + if (!isGoodEvent(collision)) { continue; } - // Rec tracks; track selection w/ DCA open (for secondaries), w/ DCA close (for efficiency) - // Obtain here: DCAxy for sec contamination, MC closure + const float multRec = getMult(collision); + const float flatRec = fillFlat(collision); + const auto& groupedTrks = tracks.sliceBy(perCollTrk, collision.globalIndex()); - int nTrk = 0; for (const auto& track : groupedTrks) { if (!track.has_collision()) { continue; } - if (std::abs(track.eta()) > trkSelOpt.cfgTrkEtaMax) { - continue; - } - if (track.pt() < trkSelOpt.cfgTrkPtMin) { - continue; - } - if (trkSelOpt.cfgApplyNcl && track.tpcNClsFound() < trkSelOpt.cfgNclTPCMin) { - continue; - } - if (trkSelOpt.cfgApplyNclPID && track.tpcNClsPID() < trkSelOpt.cfgNclPidTPCMin) { - continue; - } - float phiModn = track.phi(); - phiMod(phiModn, magField, track.sign()); - if (trkSelOpt.cfgRejectTrkAtTPCSector && (track.pt() >= trkSelOpt.cfgPhiCutPtMin && phiModn < fPhiCutHigh->Eval(track.pt()) && phiModn > fPhiCutLow->Eval(track.pt()))) { - continue; - } - if (!isDCAxyWoCut(track)) { + if (!isGoodTrack(track, magField)) { continue; } if (!track.has_mcParticle()) { - registryMC.fill(HIST("Tracks/hPtFakes"), multMC, flatMC, track.pt()); continue; } auto particle = track.mcParticle_as(); @@ -2482,134 +2476,40 @@ struct FlattenictyPikp { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (std::abs(particle.eta()) > trkSelOpt.cfgTrkEtaMax) { + if (std::abs(particle.eta()) > trkSelOpt.trkEtaMax) { continue; } - if (particle.pt() < trkSelOpt.cfgTrkPtMin) { - continue; - } - static_for<0, 1>([&](auto pidSgn) { // for checking purposes only: use gen Nch, gen Flat - fillMCRecTrack(track, multMC, flatMC); - fillMCRecTrack(track, multMC, flatMC); - fillMCRecTrack(track, multMC, flatMC); - }); - static_for<0, 4>([&](auto i) { + static_for<0, 2>([&](auto i) { constexpr int Cidx = i.value; - if (std::sqrt(std::pow(std::fabs(o2::aod::pidutils::tpcNSigma(track)), 2) + std::pow(std::fabs(o2::aod::pidutils::tofNSigma(track)), 2) < trkSelOpt.cfgDcaNsigmaCombinedMax)) { - if (std::fabs(particle.pdgCode()) == PDGs[Cidx]) { - if (!particle.isPhysicalPrimary()) { - if (particle.getProcess() == CprocessIdWeak) { - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyWeakAll), multRecGt1, flatRec, track.pt(), track.dcaXY()); - } else { - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyMatAll), multRecGt1, flatRec, track.pt(), track.dcaXY()); - } + if (std::fabs(particle.pdgCode()) == pDGs[Cidx]) { + if (!particle.isPhysicalPrimary()) { + if (particle.getProcess() == CprocessIdWeak) { + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyWeakAll), multRec, flatRec, track.pt(), track.dcaXY()); } else { - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyPrimAll), multRecGt1, flatRec, track.pt(), track.dcaXY()); - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CdEdxMcRecPrim), track.eta(), multRecGt1, flatRec, track.p(), track.tpcSignal()); + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyMatAll), multRec, flatRec, track.pt(), track.dcaXY()); } - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyAll), multRecGt1, flatRec, track.pt(), track.dcaXY()); + } else { + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyPrimAll), multRec, flatRec, track.pt(), track.dcaXY()); + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CdEdxMcRecPrim), track.eta(), multRec, flatRec, track.p(), track.tpcSignal()); } + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTvsDCAxyAll), multRec, flatRec, track.pt(), track.dcaXY()); } }); - if (isDCAxyCut(track)) { - static_for<0, 4>([&](auto i) { + if (isGoodTrack(track, magField)) { + static_for<0, 2>([&](auto i) { constexpr int Cidx = i.value; - if (std::fabs(particle.pdgCode()) == PDGs[Cidx]) { + if (std::fabs(particle.pdgCode()) == pDGs[Cidx]) { if (particle.isPhysicalPrimary()) { - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CdEdxMcRecPrimSel), track.eta(), multRecGt1, flatRec, track.p(), track.tpcSignal()); - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTeffPrimRecEvt), multRecGt1, flatRec, track.pt()); // Tracking eff. num - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTmcClosureRec), multMC, flatMC, track.pt()); // closure + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CdEdxMcRecPrimSel), track.eta(), multRec, flatRec, track.p(), track.tpcSignal()); + registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CEtaVsPtVsPMcRecPrimSel), track.eta(), track.pt(), track.p()); } } }); - nTrk++; } } - registryQC.fill(HIST("Events/hNchVsCent"), nTrk, multRecGt1); - } - static_for<0, 1>([&](auto pidSgn) { - fillEfficiency(); - fillEfficiency(); - fillEfficiency(); - }); - - // Loop on generated particles (no requirement on availaability of reconstructed collision; no event selection) - // - for (const auto& particle : particles) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (std::abs(particle.eta()) > trkSelOpt.cfgTrkEtaMax) { - continue; - } - if (particle.pt() < trkSelOpt.cfgTrkPtMin) { - continue; - } - static_for<0, 1>([&](auto pidSgn) { - fillMCGen(particle, multMC, flatMC); - fillMCGen(particle, multMC, flatMC); - fillMCGen(particle, multMC, flatMC); - }); - } - - // Obtain here: Denominator of signal loss and event loss; MC closure - // - registryMC.fill(HIST("Events/hEvtMcGen"), 0.5); - if (evtSelOpt.useZVtxCutMC && std::abs(mcCollision.posZ()) > evtSelOpt.cfgCutVtxZ) { - return; - } - registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ()); - registryMC.fill(HIST("Events/hEvtMcGen"), 1.5); - if (evtSelOpt.useINELCutMC) { - if (!o2::pwglf::isINELgt0mc(particles, pdg)) { - return; - } - } - registryMC.fill(HIST("Events/hEvtMcGen"), 2.5); - if (evtSelOpt.cfgUseInelgt0wTVX && !isInelGt0wTVX(particles)) { // TVX trigger: FT0A + FT0C acceptance - return; - } - registryMC.fill(HIST("Events/hEvtMcGen"), 3.5); - registryMC.fill(HIST("Events/hNchVsFlatGenINELgt0"), multMC, flatMC); // Evt loss den - - for (const auto& particle : particles) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (std::abs(particle.eta()) > trkSelOpt.cfgTrkEtaMax) { - continue; - } - if (particle.pt() < trkSelOpt.cfgTrkPtMin) { - continue; - } - static_for<0, 1>([&](auto pidSgn) { - fillMCGen(particle, multMC, flatMC); - fillMCGen(particle, multMC, flatMC); - fillMCGen(particle, multMC, flatMC); - }); - static_for<0, 4>([&](auto i) { - constexpr int Cidx = i.value; - // LOG(debug) << "fillMCGen for pidSgn '" << pidSgn << "' and id '" << static_cast(id) << " with index " << ChistIdx; - if (particle.isPhysicalPrimary()) { - if (std::fabs(particle.pdgCode()) == PDGs[Cidx]) { - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTgenPrimSgn), multMC, flatMC, particle.pt()); // Sgn loss den - registryMC.fill(HIST(Cprefix) + HIST(CspeciesAll[Cidx]) + HIST(CpTmcClosureGenPrim), multMC, flatMC, particle.pt()); // closure - } - } - }); - } - } - PROCESS_SWITCH(FlattenictyPikp, processMC, "process MC", false); - - template - ObjType* getForTsOrRun(std::string const& fullPath, int64_t timestamp, int runNumber) - { - if (cfgUseCcdbForRun) { - return ccdb->getForRun(fullPath, runNumber); - } else { - return ccdb->getForTimeStamp(fullPath, timestamp); } } + PROCESS_SWITCH(FlattenictyPikp, processMcRecTrack, "process MC rec tracks", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/GlobalEventProperties/heavyionMultiplicity.cxx b/PWGLF/Tasks/GlobalEventProperties/heavyionMultiplicity.cxx index 3357335f36e..cb9f407e7e1 100644 --- a/PWGLF/Tasks/GlobalEventProperties/heavyionMultiplicity.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/heavyionMultiplicity.cxx @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -55,12 +56,12 @@ using namespace o2::framework::expressions; using namespace o2::aod::track; using namespace o2::aod::evsel; -using CollisionDataTable = soa::Join; +using CollisionDataTable = soa::Join; using TrackDataTable = soa::Join; using FilTrackDataTable = soa::Filtered; using CollisionMCTrueTable = aod::McCollisions; using TrackMCTrueTable = aod::McParticles; -using CollisionMCRecTable = soa::SmallGroups>; +using CollisionMCRecTable = soa::SmallGroups>; using TrackMCRecTable = soa::Join; using FilTrackMCRecTable = soa::Filtered; using V0TrackCandidates = soa::Join; @@ -126,8 +127,7 @@ static constexpr TrackSelectionFlags::flagtype TrackSelectionTpc = TrackSelectionFlags::kTPCNCls | TrackSelectionFlags::kTPCCrossedRowsOverNCls | TrackSelectionFlags::kTPCChi2NDF; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = - TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; +// static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = TrackSelectionFlags::kDCAxy; @@ -158,7 +158,7 @@ struct HeavyionMultiplicity { Configurable etaRange{"etaRange", 1.0f, "Eta range to consider"}; Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; - Configurable dcaZ{"dcaZ", 0.2f, "Custom DCA Z cut (ignored if negative)"}; + Configurable dcaZ{"dcaZ", 0.2f, "Custom DCA Z cut"}; Configurable v0radiusK0SCut{"v0radiusK0SCut", 1.2f, "K0S RadiusCut"}; Configurable dcapostopvK0SCut{"dcapostopvK0SCut", 0.05f, "K0S dcapostopvCut"}; Configurable dcanegtopvK0SCut{"dcanegtopvK0SCut", 0.05f, "K0S dcanegtopvCut"}; @@ -207,6 +207,9 @@ struct HeavyionMultiplicity { Configurable isApplyCentFT0CVariant1{"isApplyCentFT0CVariant1", false, "Centrality based on FT0C variant1"}; Configurable isApplyCentFT0CVariant2{"isApplyCentFT0CVariant2", false, "Centrality based on FT0C variant2 (Run2 like truncation)"}; Configurable isApplyCentFT0M{"isApplyCentFT0M", false, "Centrality based on FT0A + FT0C"}; + Configurable isApplyCentFT0MAnchorCol{"isApplyCentFT0MAnchorCol", false, "Centrality collision based, with anchor point"}; + Configurable isApplyCentFT0MAnchorBC{"isApplyCentFT0MAnchorBC", false, "Centrality bunch crossing based"}; + Configurable isApplyCentNGlobal{"isApplyCentNGlobal", false, "Centrality based on global tracks"}; Configurable isApplyCentMFT{"isApplyCentMFT", false, "Centrality based on MFT tracks"}; Configurable isApplyTVX{"isApplyTVX", false, "Enable TVX trigger sel"}; @@ -217,6 +220,14 @@ struct HeavyionMultiplicity { Configurable selectCollidingBCs{"selectCollidingBCs", true, "BC analysis: select colliding BCs"}; Configurable selectTVX{"selectTVX", true, "BC analysis: select TVX"}; Configurable selectFV0OrA{"selectFV0OrA", true, "BC analysis: select FV0OrA"}; + Configurable isApplyDCAstandardcuts{"isApplyDCAstandardcuts", true, "Apply DCA standard run 2 cuts"}; + Configurable isApplyDCAcustomcuts{"isApplyDCAcustomcuts", false, "Apply DCA custom cuts"}; + Configurable cDcazP0{"cDcazP0", 1.0f, "dcaz parameter0"}; + Configurable cDcazP1{"cDcazP1", 1.0f, "dcaz parameter1"}; + Configurable cDcazP2{"cDcazP2", 1.0f, "dcaz parameter2"}; + Configurable cDcaxyP0{"cDcaxyP0", 1.0f, "dcaxy parameter0"}; + Configurable cDcaxyP1{"cDcaxyP1", 1.0f, "dcaxy parameter1"}; + Configurable cDcaxyP2{"cDcaxyP2", 1.0f, "dcaxy parameter2"}; void init(InitContext const&) { @@ -249,6 +260,18 @@ struct HeavyionMultiplicity { x->SetBinLabel(9, "ApplyNoHighMultCollInPrevRof"); x->SetBinLabel(10, "INEL > 0"); + if (doprocessDCAvsptData) { + histos.add("hdcaxyvspt", "dca to pv in the xy plane", kTH2D, {dcaAxis, axisPt}, false); + histos.add("hdcazvspt", "dca to pv in the z axis", kTH2D, {dcaAxis, axisPt}, false); + } + + if (doprocessDCAvsptMC) { + histos.add("hdcaxyvsptMC", "dca to pv in the xy plane MC", kTH2D, {dcaAxis, axisPt}, false); + histos.add("hdcazvsptMC", "dca to pv in the z axis MC", kTH2D, {dcaAxis, axisPt}, false); + histos.add("hdcaxyvsptMCprimary", "dca to pv in the xy plane MC primary particles", kTH2D, {dcaAxis, axisPt}, false); + histos.add("hdcazvsptMCprimary", "dca to pv in the z axis MC primary particles", kTH2D, {dcaAxis, axisPt}, false); + } + if (doprocessData) { histos.add("hdcaxy", "dca to pv in the xy plane", kTH1D, {dcaAxis}, false); histos.add("hdcaz", "dca to pv in the z axis", kTH1D, {dcaAxis}, false); @@ -450,6 +473,12 @@ struct HeavyionMultiplicity { if (isApplyCentFT0M) { cent = col.centFT0M(); } + if (isApplyCentFT0MAnchorCol) { + cent = col.centFT0MAnchorCol(); + } + if (isApplyCentFT0MAnchorBC) { + cent = col.centFT0MAnchorBC(); + } if (isApplyCentNGlobal) { cent = col.centNGlobal(); } @@ -465,7 +494,7 @@ struct HeavyionMultiplicity { auto cent = -1; if (isApplyCentFT0C) { cent = col.multMCFT0C(); - } else if (isApplyCentFT0M) { + } else if (isApplyCentFT0M || isApplyCentFT0MAnchorCol || isApplyCentFT0MAnchorBC) { cent = col.multMCFT0C() + col.multMCFT0A(); } else if (isApplyCentFV0A) { cent = col.multMCFV0A(); @@ -483,6 +512,12 @@ struct HeavyionMultiplicity { template bool isTrackSelected(CheckTrack const& track) { + if (isApplyDCAcustomcuts) { + if (std::abs(track.dcaXY()) > cDcaxyP0 + cDcaxyP1 / pow(track.pt(), cDcaxyP2)) + return false; + if (std::abs(track.dcaZ()) > cDcazP0 + cDcazP1 / pow(track.pt(), cDcazP2)) + return false; + } if (std::abs(track.eta()) >= etaRange) { return false; } @@ -521,8 +556,57 @@ struct HeavyionMultiplicity { ncheckbit(aod::track::trackCutFlag, TrackSelectionIts) && ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), ncheckbit(aod::track::trackCutFlag, TrackSelectionTpc), true) && - ifnode(dcaZ.node() > 0.f, nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), - ncheckbit(aod::track::trackCutFlag, TrackSelectionDca)); + ifnode(isApplyDCAstandardcuts.node(), nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), true); + + void processDCAvsptData(CollisionDataTable::iterator const& cols, FilTrackDataTable const& tracks) + { + if (!isEventSelected(cols)) { + return; + } + + for (const auto& track : tracks) { + + if (!isTrackSelected(track)) + continue; + + histos.fill(HIST("hdcaxyvspt"), track.dcaXY(), track.pt()); + histos.fill(HIST("hdcazvspt"), track.dcaZ(), track.pt()); + } + } + + void processDCAvsptMC(soa::Join::iterator const& mcCollision, CollisionMCRecTable const& RecCols, TrackMCTrueTable const&, FilTrackMCRecTable const& RecTracks) + { + + for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { + continue; + } + if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + + auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); + + for (const auto& Rectrack : recTracksPart) { + + if (!isTrackSelected(Rectrack)) + continue; + + if (!Rectrack.has_mcParticle()) + continue; + + histos.fill(HIST("hdcaxyvsptMC"), Rectrack.dcaXY(), Rectrack.pt()); + histos.fill(HIST("hdcazvsptMC"), Rectrack.dcaZ(), Rectrack.pt()); + + auto mcpart = Rectrack.mcParticle(); + + if (isGenTrackSelected(mcpart)) { + histos.fill(HIST("hdcaxyvsptMCprimary"), Rectrack.dcaXY(), Rectrack.pt()); + histos.fill(HIST("hdcazvsptMCprimary"), Rectrack.dcaZ(), Rectrack.pt()); + } + } + } + } void processData(CollisionDataTable::iterator const& cols, FilTrackDataTable const& tracks) { @@ -1007,7 +1091,7 @@ struct HeavyionMultiplicity { } // collision loop } - void processBcData(soa::Join::iterator const& multbc) + void processBcData(soa::Join::iterator const& multbc) { histos.fill(HIST("BcHist"), 1); // all BCs if (selectCollidingBCs && !multbc.multCollidingBC()) @@ -1032,6 +1116,8 @@ struct HeavyionMultiplicity { histos.fill(HIST("BcCentFT0MHist"), multbc.centFT0M()); } + PROCESS_SWITCH(HeavyionMultiplicity, processDCAvsptData, "process DCA data", false); + PROCESS_SWITCH(HeavyionMultiplicity, processDCAvsptMC, "process DCA MC", false); PROCESS_SWITCH(HeavyionMultiplicity, processData, "process data CentFT0C", false); PROCESS_SWITCH(HeavyionMultiplicity, processCorrelation, "do correlation study in data", false); PROCESS_SWITCH(HeavyionMultiplicity, processMonteCarlo, "process MC CentFT0C", false); diff --git a/PWGLF/Tasks/GlobalEventProperties/nchStudypp.cxx b/PWGLF/Tasks/GlobalEventProperties/nchStudypp.cxx index 0518ebe9f9f..ddd61749b7e 100644 --- a/PWGLF/Tasks/GlobalEventProperties/nchStudypp.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/nchStudypp.cxx @@ -257,7 +257,7 @@ struct NchStudypp { histos.add("hgendndetaVsMultEta05BeforeEvtSel", "hgendndetaBeforeEvtSel vs multiplicity in eta<0.5", kTH2F, {axisEta, multAxis}); histos.add("hgendndetaVsMultEta05AfterEvtSel", "hgendndetaAfterEvtSel vs multiplicity in eta<0.5", kTH2F, {axisEta, multAxis}); histos.add("hGenCent", "Centrality of generated MC events", kTH1F, {axisCent}); - histos.add("hGenAssoRecCent", "Centrality of selected MC events", kTH1F, {axisCent}); + histos.add("hGenAssocRecCent", "Centrality of selected MC events", kTH1F, {axisCent}); histos.add("hgendndetaBeforeEvtSel", "Eta of all generated particles", kTH1F, {axisEta}); histos.add("hgendndetaAfterEvtSel", "Eta of generated particles after EvtSel", kTH1F, {axisEta}); histos.add("hgendndetaVscentBeforeEvtSel", "hgendndetaBeforeEvtSel vs centrality", kTH2F, {axisEta, centAxis}); diff --git a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx index 667a10519b1..6de0a7a250f 100644 --- a/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/ptmultCorr.cxx @@ -16,9 +16,9 @@ /// \since October 01, 2025 #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -36,12 +35,15 @@ #include #include +#include #include #include #include #include +#include #include +#include #include using namespace o2; @@ -49,21 +51,20 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::aod::track; using namespace o2::aod::evsel; +using namespace o2::aod::rctsel; using ColDataTablePbPb = soa::Join; using ColDataTablepp = soa::Join; using ColMCRecTablePbPb = soa::SmallGroups>; using ColMCRecTablepp = soa::SmallGroups>; -using ColMCTrueTable = soa::Join; +using ColMCTrueTable = soa::Join; using TrackDataTable = soa::Join; -using FilTrackDataTable = soa::Filtered; using TrackMCRecTable = soa::Join; -using FilTrackMCRecTable = soa::Filtered; using TrackMCTrueTable = aod::McParticles; enum { kTrackTypebegin = 0, - kGlobalplusITS = 1, + kGlobalplusITS, kGlobalonly, kITSonly, kTrackTypeend @@ -71,25 +72,33 @@ enum { enum { kGenpTbegin = 0, - kNoGenpTVar = 1, + kNoGenpTVar, kGenpTup, kGenpTdown, kGenpTend }; +// ── CHANGE 1 ──────────────────────────────────────────────────────────────── +// Added kGenAllCharged as the new last bin before kGenTrkTypeend. +// Numerator : kGenAll → MC truth physical primaries +// Denominator: kGenAllCharged → MC truth all charged (primaries + secondaries) +// Primary fraction from MC = kGenAll / kGenAllCharged per pT bin. +// axisGenTrkType (built from kGenTrkTypeend) auto-expands from 5 → 6 bins. +// ──────────────────────────────────────────────────────────────────────────── enum { kGenTrkTypebegin = 0, - kGenAll = 1, + kGenAll, kGenPion, kGenKaon, kGenProton, kGenOther, + kGenAllCharged, // all charged particles (primaries + secondaries) — denominator for primary fraction kGenTrkTypeend }; enum { kRecTrkTypebegin = 0, - kRecoAll = 1, + kRecoAll, kRecoHasmc, kRecoPrimary, kRecoPion, @@ -98,85 +107,178 @@ enum { kRecoOther, kRecoSecondary, kRecoWeakDecay, + kRecoMaterial, kRecoFake, kRecoBkg, kRecTrkTypeend }; -static constexpr TrackSelectionFlags::flagtype TrackSelectionIts = - TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | - TrackSelectionFlags::kITSHits; -static constexpr TrackSelectionFlags::flagtype TrackSelectionTpc = - TrackSelectionFlags::kTPCNCls | - TrackSelectionFlags::kTPCCrossedRowsOverNCls | - TrackSelectionFlags::kTPCChi2NDF; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = - TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = - TrackSelectionFlags::kDCAxy; - -AxisSpec axisEvent{15, 0.5, 15.5, "#Event", "EventAxis"}; -AxisSpec axisVtxZ{40, -20, 20, "Vertex Z", "VzAxis"}; -AxisSpec axisEta{40, -2, 2, "#eta", "EtaAxis"}; -AxisSpec axisPhi{{0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf, o2::constants::math::PIQuarter * 3., o2::constants::math::PI, o2::constants::math::PIQuarter * 5., o2::constants::math::PIHalf * 3., o2::constants::math::PIQuarter * 7., o2::constants::math::TwoPI}, "#phi", "PhiAxis"}; -AxisSpec axisPhi2{629, 0, o2::constants::math::TwoPI, "#phi"}; -AxisSpec axisCent{100, 0, 100, "#Cent"}; -AxisSpec axisDeltaPt{50, -1.0, +1.0, "#Delta(pT)"}; -AxisSpec axisGenPtVary = {kGenpTend - 1, +kGenpTbegin + 0.5, +kGenpTend - 0.5, "", "GenpTVaryAxis"}; -AxisSpec axisGenTrkType = {kGenTrkTypeend - 1, +kGenTrkTypebegin + 0.5, +kGenTrkTypeend - 0.5, "", "GenTrackTypeAxis"}; -AxisSpec axisRecTrkType = {kRecTrkTypeend - 1, +kRecTrkTypebegin + 0.5, +kRecTrkTypeend - 0.5, "", "RecTrackTypeAxis"}; -AxisSpec axisTrackType = {kTrackTypeend - 1, +kTrackTypebegin + 0.5, +kTrackTypeend - 0.5, "", "TrackTypeAxis"}; +const AxisSpec axisEvent{15, 0.5, 15.5, "#Event", "EventAxis"}; +const AxisSpec axisVtxZ{40, -20, 20, "Vertex Z", "VzAxis"}; +const AxisSpec axisEta{40, -2, 2, "#eta", "EtaAxis"}; +const AxisSpec axisPhi{{0, o2::constants::math::PIQuarter, o2::constants::math::PIHalf, o2::constants::math::PIQuarter * 3., o2::constants::math::PI, o2::constants::math::PIQuarter * 5., o2::constants::math::PIHalf * 3., o2::constants::math::PIQuarter * 7., o2::constants::math::TwoPI}, "#phi", "PhiAxis"}; +const AxisSpec axisPhi2{629, 0, o2::constants::math::TwoPI, "#phi"}; +const AxisSpec axisCent{100, 0, 100, "#Cent"}; +const AxisSpec axisDeltaPt{50, -1.0, +1.0, "#Delta(pT)"}; +const AxisSpec axisGenPtVary = {kGenpTend - 1, +kGenpTbegin + 0.5, +kGenpTend - 0.5, "", "GenpTVaryAxis"}; +const AxisSpec axisGenTrkType = {kGenTrkTypeend - 1, +kGenTrkTypebegin + 0.5, +kGenTrkTypeend - 0.5, "", "GenTrackTypeAxis"}; +const AxisSpec axisRecTrkType = {kRecTrkTypeend - 1, +kRecTrkTypebegin + 0.5, +kRecTrkTypeend - 0.5, "", "RecTrackTypeAxis"}; +const AxisSpec axisTrackType = {kTrackTypeend - 1, +kTrackTypebegin + 0.5, +kTrackTypeend - 0.5, "", "TrackTypeAxis"}; auto static constexpr KminCharge = 3.f; -auto static constexpr KminPtCut = 0.1f; +static constexpr float kZeroFloat{0.f}; struct PtmultCorr { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Service pdg; + Service pdg{}; Preslice perCollision = aod::track::collisionId; - Configurable etaRange{"etaRange", 1.0f, "Eta range to consider"}; + Configurable etaRange{"etaRange", 0.8f, "Eta range to consider"}; Configurable vtxRange{"vtxRange", 10.0f, "Vertex Z range to consider"}; Configurable occuRange{"occuRange", 500.0f, "Occupancy range to consider"}; - Configurable dcaZ{"dcaZ", 0.2f, "Custom DCA Z cut (ignored if negative)"}; - Configurable cfgPtCutMin{"cfgPtCutMin", 0.15f, "minimum accepted track pT"}; + Configurable cfgPtCutMin{"cfgPtCutMin", 0.1f, "minimum accepted track pT"}; + Configurable cfgPtCutMax{"cfgPtCutMax", 1e9f, "maximum accepted track pT"}; Configurable extraphicut1{"extraphicut1", 3.07666f, "Extra Phi cut 1"}; Configurable extraphicut2{"extraphicut2", 3.12661f, "Extra Phi cut 2"}; Configurable extraphicut3{"extraphicut3", 0.03f, "Extra Phi cut 3"}; Configurable extraphicut4{"extraphicut4", 6.253f, "Extra Phi cut 4"}; + Configurable isZvtxPosSelMC{"isZvtxPosSelMC", true, "Zvtx position selection for MC events"}; + + Configurable cfgMinNCrossedRows{"cfgMinNCrossedRows", 70, "Minimum TPC crossed rows"}; + Configurable cfgUseNclsPID{"cfgUseNclsPID", false, "Use NclsPID instead of NclsFound for the Ncls cut"}; + Configurable cfgMinChi2ClsTPC{"cfgMinChi2ClsTPC", 0.5f, "Minimum TPC chi2/cls"}; + Configurable cfgMaxChi2ClsTPC{"cfgMaxChi2ClsTPC", 4.0f, "Maximum TPC chi2/cls"}; + Configurable cfgMaxChi2ClsITS{"cfgMaxChi2ClsITS", 36.0f, "Maximum ITS chi2/cls"}; + Configurable cfgDCAz{"cfgDCAz", 0.04, "DCAz cut in cm"}; + + // ── pT-dependent DCAxy cut ────────────────────────────────────────────────── + // The THnSparses keep the FULL DCAxy distribution (needed for the template fit). + // When the flag is on, additional *DcaCut histograms (no DCAxy axis) are + // filled only with tracks passing |DCAxy| < N * (A + B/pT^C), using reco pT. + Configurable cfgApplyPtDepDcaXY{"cfgApplyPtDepDcaXY", false, "fill *DcaCut spectra with |DCAxy| < N*(A + B/pT^C)"}; + Configurable cfgDcaXYSigmaA{"cfgDcaXYSigmaA", 0.0015f, "sigma_DCAxy(pT) = A + B/pT^C : A (cm)"}; + Configurable cfgDcaXYSigmaB{"cfgDcaXYSigmaB", 0.0050f, "sigma_DCAxy(pT) = A + B/pT^C : B (cm (GeV/c)^C)"}; + Configurable cfgDcaXYSigmaC{"cfgDcaXYSigmaC", 1.1f, "sigma_DCAxy(pT) = A + B/pT^C : C"}; + Configurable cfgDcaXYNSigma{"cfgDcaXYNSigma", 7.f, "number of sigmas for the DCAxy cut"}; + + // DCAxy binning of the THnSparses, by regions in |DCAxy| from 0 outwards: + // region i spans (edge[i-1], edge[i]] with bin width width[i]. + // Default = previous uniform binning (20 um over +-5 cm). + // Finer core, e.g.: edges {0.05, 0.5, 5.0}, widths {0.0005, 0.005, 0.1} + Configurable> cfgDcaXYRegionEdges{"cfgDcaXYRegionEdges", {5.0}, "DCAxy binning: outer |DCAxy| edge of each region (cm), increasing"}; + Configurable> cfgDcaXYRegionWidths{"cfgDcaXYRegionWidths", {0.002}, "DCAxy binning: bin width in each region (cm)"}; + + // Fine DCAxy vs reco pT of primaries (sigma(pT) extraction, MC only) + ConfigurableAxis binsDcaXYFine{"binsDcaXYFine", {4000, -0.4, 0.4}, "fine DCAxy binning for sigma(pT) (cm)"}; + ConfigurableAxis binsPtDcaFine{"binsPtDcaFine", {VARIABLE_WIDTH, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.6, 4.0, 5.0, 6.0, 8.0, 10.0, 13.0, 20.0, 30.0, 50.0}, "reco pT binning of the fine DCAxy histograms"}; + + bool passDcaXY(float pt, float dcaXY) const + { + if (pt <= kZeroFloat) { + return false; + } + const float sigma = cfgDcaXYSigmaA.value + cfgDcaXYSigmaB.value / std::pow(pt, cfgDcaXYSigmaC.value); + return std::fabs(dcaXY) < cfgDcaXYNSigma.value * sigma; + } + + static std::vector makeDcaXYEdges(const std::vector& regionEdges, const std::vector& regionWidths) + { + if (regionEdges.empty() || regionEdges.size() != regionWidths.size()) { + LOGF(fatal, "DCAxy binning: %zu region edges vs %zu widths", regionEdges.size(), regionWidths.size()); + } + std::vector pos{0.}; + double x0 = 0.; + for (size_t i = 0; i < regionEdges.size(); ++i) { + const double xMax = regionEdges[i]; + const double w = regionWidths[i]; + if (xMax <= x0 || w <= kZeroFloat) { + LOGF(fatal, "DCAxy binning: region %zu invalid (edge %g after %g, width %g)", i, xMax, x0, w); + } + const int n = std::max(1, static_cast(std::lround((xMax - x0) / w))); + for (int j = 1; j <= n; ++j) { + pos.push_back(x0 + j * (xMax - x0) / n); // integer steps: region edges are exact + } + if (std::fabs((xMax - x0) / n - w) > kWidthRelTol * w) { + LOGF(warning, "DCAxy binning: region %zu width adjusted from %g to %g cm to fit the range", i, w, (xMax - x0) / n); + } + x0 = xMax; + } + std::vector edges; + for (auto it = pos.rbegin(); it != pos.rend(); ++it) { + if (*it > kZeroFloat) { + edges.push_back(-*it); + } + } + edges.insert(edges.end(), pos.begin(), pos.end()); + LOGF(info, "DCAxy axis: %zu bins in [%g, %g] cm", edges.size() - 1, edges.front(), edges.back()); + return edges; + } + ConfigurableAxis ptHistBin{"ptHistBin", {200, 0., 20.}, ""}; ConfigurableAxis centralityBinning{"centralityBinning", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, ""}; ConfigurableAxis binsImpactPar{"binsImpactPar", {VARIABLE_WIDTH, 0.0, 3.00065, 4.28798, 6.14552, 7.6196, 8.90942, 10.0897, 11.2002, 12.2709, 13.3167, 14.4173, 23.2518}, "Binning of the impact parameter axis"}; - Configurable isApplySameBunchPileup{"isApplySameBunchPileup", false, "Enable SameBunchPileup cut"}; - Configurable isApplyGoodZvtxFT0vsPV{"isApplyGoodZvtxFT0vsPV", false, "Enable GoodZvtxFT0vsPV cut"}; - Configurable isApplyExtraPhiCut{"isApplyExtraPhiCut", false, "Enable extra phi cut"}; - Configurable isApplyNoCollInTimeRangeStandard{"isApplyNoCollInTimeRangeStandard", true, "Enable NoCollInTimeRangeStandard cut"}; - Configurable isApplyNoCollInRofStandard{"isApplyNoCollInRofStandard", false, "Enable NoCollInRofStandard cut"}; - Configurable isApplyNoHighMultCollInPrevRof{"isApplyNoHighMultCollInPrevRof", false, "Enable NoHighMultCollInPrevRof cut"}; - Configurable isApplyFT0CbasedOccupancy{"isApplyFT0CbasedOccupancy", false, "Enable FT0CbasedOccupancy cut"}; - Configurable isApplyInelgt0{"isApplyInelgt0", false, "Enable INEL > 0 condition"}; - Configurable isApplyOccuCut{"isApplyOccuCut", false, "Enable occupancy selection"}; + Configurable isSameBunchPileup{"isSameBunchPileup", false, "Enable SameBunchPileup cut"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", false, "Enable GoodZvtxFT0vsPV cut"}; + Configurable applyExtraPhiCut{"applyExtraPhiCut", false, "Enable extra phi cut"}; + Configurable isNoCollInTimeRangeStandard{"isNoCollInTimeRangeStandard", false, "Enable NoCollInTimeRangeStandard cut"}; + Configurable isNoCollInTimeRangeStrict{"isNoCollInTimeRangeStrict", false, "use isNoCollInTimeRangeStrict?"}; + + Configurable selHasBC{"selHasBC", true, "Require has_foundBC"}; + Configurable selHasFT0{"selHasFT0", true, "Require has_foundFT0"}; + + Configurable isNoCollInRofStandard{"isNoCollInRofStandard", false, "Enable NoCollInRofStandard cut"}; + Configurable isNoCollInRofStrict{"isNoCollInRofStrict", false, "use isNoCollInRofStrict?"}; + + Configurable isNoHighMultCollInPrevRof{"isNoHighMultCollInPrevRof", false, "use isNoHighMultCollInPrevRof?"}; + Configurable isNoCollInTimeRangeNarrow{"isNoCollInTimeRangeNarrow", false, "use isNoCollInTimeRangeNarrow?"}; + + Configurable applyFT0CbasedOccupancy{"applyFT0CbasedOccupancy", false, "Enable FT0CbasedOccupancy cut"}; + Configurable applyInelgt0{"applyInelgt0", true, "Enable INEL > 0 condition"}; + Configurable applyOccuCut{"applyOccuCut", true, "Enable occupancy selection"}; + + Configurable rctLabel{"rctLabel", "CBT_hadronPID", "RCT selection flag"}; + Configurable rctCheckZDC{"rctCheckZDC", false, "Check ZDC in RCT"}; + Configurable rctTreatLimitedAcceptanceAsBad{"rctTreatLimitedAcceptanceAsBad", false, "Treat limited acceptance as bad"}; + Configurable requireGoodRct{"requireGoodRct", true, "Apply RCT selection"}; + + // RCT checker instance + RCTFlagsChecker rctChecker; + + static constexpr int kZeroInt{0}; + static constexpr float kOne{1.0f}; + static constexpr double kWidthRelTol{1e-3}; void init(InitContext const&) { + if (requireGoodRct) { + rctChecker.init(rctLabel.value, rctCheckZDC.value, rctTreatLimitedAcceptanceAsBad.value); + } AxisSpec centAxis = {centralityBinning, "Centrality", "CentralityAxis"}; AxisSpec axisPt = {ptHistBin, "pT", "pTAxis"}; AxisSpec impactParAxis = {binsImpactPar, "Impact Parameter"}; + const AxisSpec axisDCAxy{makeDcaXYEdges(cfgDcaXYRegionEdges.value, cfgDcaXYRegionWidths.value), "DCA_{xy} (cm)", "DCAxyAxis"}; + const AxisSpec axisDcaXYFine{binsDcaXYFine, "DCA_{xy} (cm)", "DCAxyFineAxis"}; + const AxisSpec axisPtDcaFine{binsPtDcaFine, "#it{p}_{T}^{rec} (GeV/#it{c})", "pTDcaFineAxis"}; histos.add("EventHist", "EventHist", kTH1D, {axisEvent}, false); histos.add("VtxZHist", "VtxZHist", kTH1D, {axisVtxZ}, false); histos.add("CentPercentileHist", "CentPercentileHist", kTH1D, {axisCent}, false); - histos.add("CentPercentileMCRecHist", "CentPercentileMCRecHist", kTH1D, {axisCent}, false); histos.add("PhiVsEtaHistNoCut", "PhiVsEtaHistNoCut", kTH2D, {axisPhi2, axisEta}, false); histos.add("PhiVsEtaHistWithCut", "PhiVsEtaHistWithCut", kTH2D, {axisPhi2, axisEta}, false); if (doprocessDataPbPb) { histos.add("hdatazvtxcent", "hdatazvtxcent", kTH2D, {axisVtxZ, centAxis}, false); - histos.add("hdatahistPbPb", "hdatahistPbPb", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisTrackType}, false); + histos.add("hdatahistPbPb", "hdatahistPbPb", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hdatahistPbPbDcaCut", "hdatahistPbPbDcaCut", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisTrackType}, false); + } } if (doprocessDatapp) { - histos.add("hdatahistpp", "hdatahistpp", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisTrackType}, false); + histos.add("hdatahistpp", "hdatahistpp", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hdatahistppDcaCut", "hdatahistppDcaCut", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisTrackType}, false); + } } if (doprocessMCeffPbPb) { @@ -185,24 +287,38 @@ struct PtmultCorr { histos.add("hPbPbGenMCAssoRecvtxz", "hPbPbGenMCAssoRecvtxz", kTH1D, {axisVtxZ}, false); histos.add("hPbPbGenMCAssoRecvtxzcent", "hPbPbGenMCAssoRecvtxzcent", kTH2D, {axisVtxZ, centAxis}, false); histos.add("hPbPbGenMCdndpt", "hPbPbGenMCdndpt", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi}, false); + // axisGenTrkType now has 6 bins: kGenAll(1)…kGenOther(5), kGenAllCharged(6) histos.add("hPbPbGenMCAssoRecdndpt", "hPbPbGenMCAssoRecdndpt", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisGenTrkType, axisGenPtVary}, false); histos.add("hPbPbRecMCvtxz", "hPbPbRecMCvtxz", kTH1D, {axisVtxZ}, false); histos.add("hPbPbRecMCvtxzcent", "hPbPbRecMCvtxzcent", kTH2D, {axisVtxZ, centAxis}, false); histos.add("hPbPbRecMCcent", "hPbPbRecMCcent", kTH1D, {axisCent}, false); - histos.add("hPbPbRecMCdndpt", "hPbPbRecMCdndpt", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisRecTrkType}, false); + histos.add("hPbPbRecMCdndpt", "hPbPbRecMCdndpt", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisRecTrkType, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hPbPbRecMCdndptDcaCut", "hPbPbRecMCdndptDcaCut", kTHnSparseD, {axisVtxZ, centAxis, axisPt, axisPhi, axisRecTrkType, axisTrackType}, false); + } histos.add("hPbPbEtaReso", "hPbPbEtaReso", kTH2D, {axisPt, axisDeltaPt}); + // DCAxy of reco primaries (global tracks, same MC collision) vs RECO pT and centrality + histos.add("hPbPbDcaXYPrimFine", "hPbPbDcaXYPrimFine", kTH3F, {axisPtDcaFine, axisDcaXYFine, centAxis}, false); + // primaries whose MC particle belongs to another MC collision (mis-association) + histos.add("hPbPbDcaXYPrimWrongColl", "hPbPbDcaXYPrimWrongColl", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); } if (doprocessMCeffpp) { histos.add("hppGenMCvtxz", "hppGenMCvtxz", kTH1D, {axisVtxZ}, false); histos.add("hppGenMCAssoRecvtxz", "hppGenMCAssoRecvtxz", kTH1D, {axisVtxZ}, false); histos.add("hppGenMCdndpt", "hppGenMCdndpt", kTHnSparseD, {axisVtxZ, axisPt, axisPhi}, false); + // axisGenTrkType now has 6 bins: kGenAll(1)…kGenOther(5), kGenAllCharged(6) histos.add("hppGenMCAssoRecdndpt", "hppGenMCAssoRecdndpt", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisGenTrkType, axisGenPtVary}, false); histos.add("hppRecMCvtxz", "hppRecMCvtxz", kTH1D, {axisVtxZ}, false); - histos.add("hppRecMCdndpt", "hppRecMCdndpt", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisRecTrkType}, false); + histos.add("hppRecMCdndpt", "hppRecMCdndpt", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisRecTrkType, axisTrackType, axisDCAxy}, false); + if (cfgApplyPtDepDcaXY) { + histos.add("hppRecMCdndptDcaCut", "hppRecMCdndptDcaCut", kTHnSparseD, {axisVtxZ, axisPt, axisPhi, axisRecTrkType, axisTrackType}, false); + } histos.add("hppEtaReso", "hppEtaReso", kTH2D, {axisPt, axisDeltaPt}); + histos.add("hppDcaXYPrimFine", "hppDcaXYPrimFine", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); + histos.add("hppDcaXYPrimWrongColl", "hppDcaXYPrimWrongColl", kTH2F, {axisPtDcaFine, axisDcaXYFine}, false); } if (doprocessEvtLossSigLossMCpp || doprocessEvtLossSigLossMCPbPb) { @@ -218,20 +334,60 @@ struct PtmultCorr { histos.add("hImpactParameterGen", "Impact parameter of generated MC events", kTH1F, {impactParAxis}); histos.add("hImpactParameterRec", "Impact parameter of selected MC events", kTH1F, {impactParAxis}); histos.add("hImpactParvsCentrRec", "Impact parameter of selected MC events vs centrality", kTH2F, {axisCent, impactParAxis}); + + // CO map: centrality vs generated Nch (for N_rec_at_least_once events) + histos.add("hNchVsCent_WithRecoEvt", "hNchVsCent_WithRecoEvt", + kTH2F, {axisCent, {500, 0, 500, "Gen N_{ch} |#eta|<0.8"}}); + + // Event loss denominator: Nch distribution of all generated events + histos.add("hNchMC_AllGen", "hNchMC_AllGen", + kTH1F, {{500, 0, 500, "Gen N_{ch} |#eta|<0.8"}}); + + // Event loss numerator: Nch distribution of N_rec_at_least_once events + histos.add("hNchMC_WithRecoEvt", "hNchMC_WithRecoEvt", + kTH1F, {{500, 0, 500, "Gen N_{ch} |#eta|<0.8"}}); + + // Event splitting: centrality distributions + histos.add("hCent_WRecoEvtWSelCri", "N_rec_at_least_once (with sel)", kTH1F, {axisCent}); + histos.add("hCent_AllRecoEvt", "N_rec (with sel)", kTH1F, {axisCent}); + + // Signal loss: pT vs Nch (2D), for all gen events and N_rec_at_least_once + histos.add("hPtVsNchMC_AllGen", "hPtVsNchMC_AllGen", + kTH2F, {{axisPt}, {500, 0, 500, "Gen N_{ch} |#eta|<0.8"}}); + histos.add("hPtVsNchMC_WithRecoEvt", "hPtVsNchMC_WithRecoEvt", + kTH2F, {{axisPt}, {500, 0, 500, "Gen N_{ch} |#eta|<0.8"}}); } + if (doprocessEvtLossSigLossMCpp) { + histos.add("hNch_AllRecoEvt", + "All reco collisions passing partial cuts (event split denom pp)", + kTH1F, {{500, 0, 500, "N_{ch} |#eta|<0.8"}}); + histos.add("hNch_WRecoEvtWSelCri", + "Best reco collision passing full selection (event split num pp)", + kTH1F, {{500, 0, 500, "N_{ch} |#eta|<0.8"}}); + } + if (doprocessDatapp || doprocessDataPbPb) { + histos.add("RCTSel", "All=1 | RCT passed=2", kTH1F, {{2, 0.5, 2.5}}); + auto hrct = histos.get(HIST("RCTSel")); + hrct->GetXaxis()->SetBinLabel(1, "All"); + hrct->GetXaxis()->SetBinLabel(2, "RCT passed"); + } auto hstat = histos.get(HIST("EventHist")); auto* x = hstat->GetXaxis(); x->SetBinLabel(1, "All events"); - x->SetBinLabel(2, "sel8"); - x->SetBinLabel(3, "kNoSameBunchPileup"); // reject collisions in case of pileup with another collision in the same foundBC - x->SetBinLabel(4, "kIsGoodZvtxFT0vsPV"); // small difference between z-vertex from PV and from FT0 - x->SetBinLabel(5, "ApplyNoCollInTimeRangeStandard"); - x->SetBinLabel(6, "ApplyNoCollInRofStandard"); - x->SetBinLabel(7, "ApplyNoHighMultCollInPrevRof"); - x->SetBinLabel(8, "INEL > 0"); - x->SetBinLabel(9, "|vz|<10"); - x->SetBinLabel(10, "Occupancy<500"); + x->SetBinLabel(2, "has_foundBC"); + x->SetBinLabel(3, "has_foundFT0"); + x->SetBinLabel(4, "kIsTriggerTVX"); + x->SetBinLabel(5, "kNoITSROFrameBorder"); + x->SetBinLabel(6, "kNoTimeFrameBorder"); + x->SetBinLabel(7, "|vz|SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + x->SetBinLabel(9, "kNoSameBunchPileup"); + x->SetBinLabel(10, "kNoCollInTimeRangeStrict"); + x->SetBinLabel(11, "kNoCollInRofStrict"); + x->SetBinLabel(12, "kNoHighMultCollInPrevRof"); + x->SetBinLabel(13, "INEL>0"); + x->SetBinLabel(14, "Occupancy"); } template @@ -242,72 +398,142 @@ struct PtmultCorr { if (!col.sel8()) { return false; } - histos.fill(HIST("EventHist"), 2); + // TVX trigger (replaces sel8 as primary trigger requirement) + // if (!col.selection_bit(o2::aod::evsel::kIsTriggerTVX)) + // return false; + // histos.fill(HIST("EventHist"), 4); - if (isApplySameBunchPileup && !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return false; - } - histos.fill(HIST("EventHist"), 3); - - if (isApplyGoodZvtxFT0vsPV && !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return false; - } - histos.fill(HIST("EventHist"), 4); - - if (isApplyNoCollInTimeRangeStandard && !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // ITS ROF border + if (!col.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { return false; } histos.fill(HIST("EventHist"), 5); - if (isApplyNoCollInRofStandard && !col.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // Time frame border + if (!col.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { return false; } histos.fill(HIST("EventHist"), 6); - if (isApplyNoHighMultCollInPrevRof && !col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // vtxZ + if (std::abs(col.posZ()) > vtxRange) { return false; } histos.fill(HIST("EventHist"), 7); - if (isApplyInelgt0 && !col.isInelGt0()) { + // Good ZvtxFT0vsPV + if (isGoodZvtxFT0vsPV && + !col.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } histos.fill(HIST("EventHist"), 8); - if (std::abs(col.posZ()) >= vtxRange) { + // No same bunch pileup + if (isSameBunchPileup && + !col.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } histos.fill(HIST("EventHist"), 9); - auto occu = isApplyFT0CbasedOccupancy ? col.ft0cOccupancyInTimeRange() : col.trackOccupancyInTimeRange(); - if (isApplyOccuCut && occu > occuRange) { + // Time range isolation — Strict (replaces Standard) + if (isNoCollInTimeRangeStrict && + !col.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } histos.fill(HIST("EventHist"), 10); + + // ROF isolation — Strict (replaces Standard) + if (isNoCollInRofStrict && + !col.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + histos.fill(HIST("EventHist"), 11); + + // No high mult collision in previous ROF + if (isNoHighMultCollInPrevRof && + !col.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return false; + } + histos.fill(HIST("EventHist"), 12); + + // INEL > 0 + if (applyInelgt0 && !col.isInelGt0()) { + return false; + } + histos.fill(HIST("EventHist"), 13); + + // Occupancy + auto occu = applyFT0CbasedOccupancy + ? col.ft0cOccupancyInTimeRange() + : col.trackOccupancyInTimeRange(); + if (applyOccuCut && occu > occuRange) { + return false; + } + histos.fill(HIST("EventHist"), 14); + return true; } + // All track cuts EXCEPT the pT-dependent DCAxy cut (the flat DCAz cut is included). + // The DCAxy cut is applied at the fill points via passDcaXY(), so that the + // THnSparses keep the full DCAxy distribution for the template fit. template bool isTrackSelected(CheckTrack const& track) { + // --- eta (applied to all track types) --- if (std::abs(track.eta()) >= etaRange) { return false; } + + // --- pT (applied to all track types) --- + if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { + return false; + } + + // ITS inner-barrel hit: must fire layer 0 or layer 1 + if (!(track.itsClusterMap() & 0x01) && !(track.itsClusterMap() & 0x02)) { + return false; + } + + // ITS chi2 cut (also part of ITS cuts) + if (track.itsChi2NCl() > cfgMaxChi2ClsITS) { + return false; + } + + if (track.hasTPC()) { + + if (track.tpcNClsCrossedRows() < cfgMinNCrossedRows) { + return false; + } + + if (track.tpcChi2NCl() < cfgMinChi2ClsTPC || track.tpcChi2NCl() > cfgMaxChi2ClsTPC) { + return false; + } + } + + if (std::abs(track.dcaZ()) > cfgDCAz) { + return false; + } + + // --- optional phi cut (applied to all track types) --- histos.fill(HIST("PhiVsEtaHistNoCut"), track.phi(), track.eta()); - if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + if (applyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { return false; } histos.fill(HIST("PhiVsEtaHistWithCut"), track.phi(), track.eta()); + return true; } + // Selects MC truth physical primaries (numerator for primary fraction). + // Requires: isPhysicalPrimary + producedByGenerator + charged + |eta| < etaRange template bool isGenTrackSelected(CheckGenTrack const& track) { if (!track.isPhysicalPrimary()) { return false; } - if (!track.producedByGenerator()) { + if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { return false; } auto pdgTrack = pdg->GetParticle(track.pdgCode()); @@ -320,22 +546,49 @@ struct PtmultCorr { if (std::abs(track.eta()) >= etaRange) { return false; } - if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + if (applyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { return false; } return true; } - Filter fTrackSelectionITS = ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && - ncheckbit(aod::track::trackCutFlag, TrackSelectionIts); - Filter fTrackSelectionTPC = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), - ncheckbit(aod::track::trackCutFlag, TrackSelectionTpc), true); - Filter fTrackSelectionDCA = ifnode(dcaZ.node() > 0.f, nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), - ncheckbit(aod::track::trackCutFlag, TrackSelectionDca)); - Filter fTracksPt = aod::track::pt > cfgPtCutMin; + // ── CHANGE 2 ────────────────────────────────────────────────────────────── + // New helper: selects ALL charged particles in acceptance (denominator). + // Identical to isGenTrackSelected but WITHOUT the isPhysicalPrimary() check, + // so secondaries (weak-decay daughters, material interactions) also pass. + // Used to fill kGenAllCharged → primary fraction = kGenAll / kGenAllCharged. + // ────────────────────────────────────────────────────────────────────────── + template + bool isGenChargedTrackSelected(CheckGenTrack const& track) + { + if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { + return false; + } + auto pdgTrack = pdg->GetParticle(track.pdgCode()); + if (pdgTrack == nullptr) { + return false; + } + if (std::abs(pdgTrack->Charge()) < KminCharge) { + return false; + } + if (std::abs(track.eta()) >= etaRange) { + return false; + } + if (applyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + return false; + } + return true; + } - void processDataPbPb(ColDataTablePbPb::iterator const& cols, FilTrackDataTable const& tracks) + void processDataPbPb(ColDataTablePbPb::iterator const& cols, TrackDataTable const& tracks) { + if (requireGoodRct) { + histos.fill(HIST("RCTSel"), 1); // all events + if (!rctChecker(cols)) { + return; + } + histos.fill(HIST("RCTSel"), 2); // passed RCT + } if (!isEventSelected(cols)) { return; } @@ -347,17 +600,27 @@ struct PtmultCorr { if (!isTrackSelected(track)) { continue; } - histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalplusITS); - if (track.hasTPC()) { - histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalonly); - } else { - histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kITSonly); + auto trkType = track.hasTPC() ? kGlobalonly : kITSonly; + // full DCAxy distribution (template fit) + histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalplusITS, track.dcaXY()); + histos.fill(HIST("hdatahistPbPb"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), trkType, track.dcaXY()); + // spectrum with the pT-dependent DCAxy cut + if (cfgApplyPtDepDcaXY && passDcaXY(track.pt(), track.dcaXY())) { + histos.fill(HIST("hdatahistPbPbDcaCut"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), kGlobalplusITS); + histos.fill(HIST("hdatahistPbPbDcaCut"), cols.posZ(), cols.centFT0C(), track.pt(), track.phi(), trkType); } } } - void processDatapp(ColDataTablepp::iterator const& cols, FilTrackDataTable const& tracks) + void processDatapp(ColDataTablepp::iterator const& cols, TrackDataTable const& tracks) { + if (requireGoodRct) { + histos.fill(HIST("RCTSel"), 1); // all events + if (!rctChecker(cols)) { + return; + } + histos.fill(HIST("RCTSel"), 2); // passed RCT + } if (!isEventSelected(cols)) { return; } @@ -367,26 +630,40 @@ struct PtmultCorr { if (!isTrackSelected(track)) { continue; } - histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kGlobalplusITS); - if (track.hasTPC()) { - histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kGlobalonly); - } else { - histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kITSonly); + auto trkType = track.hasTPC() ? kGlobalonly : kITSonly; + // full DCAxy distribution (template fit) + histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), kGlobalplusITS, track.dcaXY()); + histos.fill(HIST("hdatahistpp"), cols.posZ(), track.pt(), track.phi(), trkType, track.dcaXY()); + // spectrum with the pT-dependent DCAxy cut + if (cfgApplyPtDepDcaXY && passDcaXY(track.pt(), track.dcaXY())) { + histos.fill(HIST("hdatahistppDcaCut"), cols.posZ(), track.pt(), track.phi(), kGlobalplusITS); + histos.fill(HIST("hdatahistppDcaCut"), cols.posZ(), track.pt(), track.phi(), trkType); } } } - void processMCeffPbPb(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablePbPb const& RecCols, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + void processMCeffPbPb(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablePbPb const& RecCols, TrackMCTrueTable const& GenParticles, TrackMCRecTable const& RecTracks) { - auto gencent = -999; + float gencent = -999.f; bool atLeastOne = false; + int bestCollisionIndex = -1; + int biggestNContribs = -1; for (const auto& RecCol : RecCols) { + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + } + for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) { continue; } + atLeastOne = true; gencent = RecCol.centFT0C(); } @@ -396,14 +673,37 @@ struct PtmultCorr { histos.fill(HIST("hPbPbGenMCAssoRecvtxz"), mcCollision.posZ()); histos.fill(HIST("hPbPbGenMCAssoRecvtxzcent"), mcCollision.posZ(), gencent); } + + // ── CHANGE 3 ──────────────────────────────────────────────────────────── + // Restructured PbPb gen particle loop for primary fraction from MC truth. + // + // Pass 1 (loose): isGenChargedTrackSelected — all charged in acceptance. + // → fills kGenAllCharged (denominator) when associated with a reco event. + // + // Pass 2 (tight): additionally require isPhysicalPrimary (via continue). + // → fills kGenAll and species bins (numerator) — identical to before. + // + // Primary fraction (PbPb) = hPbPbGenMCAssoRecdndpt[kGenAll] + // / hPbPbGenMCAssoRecdndpt[kGenAllCharged] per pT bin + // ──────────────────────────────────────────────────────────────────────── for (const auto& particle : GenParticles) { - if (!isGenTrackSelected(particle)) { + // Loose check: charged + |eta| + phi (no isPhysicalPrimary requirement) + if (!isGenChargedTrackSelected(particle)) { + continue; + } + // Fill denominator: all charged particles (primaries + secondaries) + if (atLeastOne) { + histos.fill(HIST("hPbPbGenMCAssoRecdndpt"), mcCollision.posZ(), gencent, particle.pt(), particle.phi(), static_cast(kGenAllCharged), kNoGenpTVar); + } + // Tight check: physical primaries only — skip secondaries from this point on + if (!particle.isPhysicalPrimary()) { continue; } + // Fill primary-only histograms (numerator + species breakdown) histos.fill(HIST("hPbPbGenMCdndpt"), mcCollision.posZ(), gencent, particle.pt(), particle.phi()); if (atLeastOne) { histos.fill(HIST("hPbPbGenMCAssoRecdndpt"), mcCollision.posZ(), gencent, particle.pt(), particle.phi(), static_cast(kGenAll), kNoGenpTVar); - if (particle.pt() < KminPtCut) { + if (particle.pt() < cfgPtCutMin) { histos.fill(HIST("hPbPbGenMCAssoRecdndpt"), mcCollision.posZ(), gencent, particle.pt(), particle.phi(), static_cast(kGenAll), kGenpTup, -10.0 * particle.pt() + 2); histos.fill(HIST("hPbPbGenMCAssoRecdndpt"), mcCollision.posZ(), gencent, particle.pt(), particle.phi(), static_cast(kGenAll), kGenpTdown, 5.0 * particle.pt() + 0.5); } else { @@ -430,29 +730,56 @@ struct PtmultCorr { } // track (mcgen) loop for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) { continue; } + histos.fill(HIST("hPbPbRecMCvtxz"), RecCol.posZ()); histos.fill(HIST("hPbPbRecMCcent"), RecCol.centFT0C()); histos.fill(HIST("hPbPbRecMCvtxzcent"), RecCol.posZ(), RecCol.centFT0C()); auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); std::vector mclabels; for (const auto& Rectrack : recTracksPart) { - if (!isTrackSelected(Rectrack)) { + if (!isTrackSelected(Rectrack)) { // all cuts except the pT-dependent DCAxy cut continue; } - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll)); + auto trkType = Rectrack.hasTPC() ? kGlobalonly : kITSonly; + // DCAxy cut always evaluated with RECO pT (as in data) + const bool passCut = cfgApplyPtDepDcaXY && passDcaXY(Rectrack.pt(), Rectrack.dcaXY()); + + // fills the full-DCAxy THnSparse (templates) and, for tracks passing + // the DCAxy cut, the *DcaCut copy (spectrum / efficiency numerator) + auto fillRec = [&](float pt, float phi, int cls) { + histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), kGlobalplusITS, Rectrack.dcaXY()); + histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), trkType, Rectrack.dcaXY()); + if (passCut) { + histos.fill(HIST("hPbPbRecMCdndptDcaCut"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), kGlobalplusITS); + histos.fill(HIST("hPbPbRecMCdndptDcaCut"), RecCol.posZ(), RecCol.centFT0C(), pt, phi, static_cast(cls), trkType); + } + }; + + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoAll); + if (Rectrack.has_mcParticle()) { int pid = 0; auto mcpart = Rectrack.mcParticle(); histos.fill(HIST("hPbPbEtaReso"), Rectrack.pt(), Rectrack.pt() - mcpart.pt()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc)); + fillRec(mcpart.pt(), mcpart.phi(), kRecoHasmc); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary)); + // fine DCAxy for sigma(pT): global tracks, reco pT, no DCAxy cut + if (Rectrack.hasTPC()) { + if (mcpart.mcCollisionId() == mcCollision.globalIndex()) { + histos.fill(HIST("hPbPbDcaXYPrimFine"), Rectrack.pt(), Rectrack.dcaXY(), RecCol.centFT0C()); + } else { + histos.fill(HIST("hPbPbDcaXYPrimWrongColl"), Rectrack.pt(), Rectrack.dcaXY()); + } + } + fillRec(mcpart.pt(), mcpart.phi(), kRecoPrimary); switch (std::abs(mcpart.pdgCode())) { case PDG_t::kPiPlus: pid = kRecoPion; @@ -468,50 +795,78 @@ struct PtmultCorr { break; } } else { - pid = kRecoSecondary; - } - if (mcpart.has_mothers()) { - auto mcpartMother = mcpart.template mothers_as().front(); - if (mcpartMother.pdgCode() == PDG_t::kK0Short || std::abs(mcpartMother.pdgCode()) == PDG_t::kLambda0) { + // non-primary → split weak-decay vs material by production mechanism + if (mcpart.getProcess() == TMCProcess::kPDecay) { pid = kRecoWeakDecay; + } else { + pid = kRecoMaterial; } + // also fill the union bin as a cross-check (weak + material) + fillRec(mcpart.pt(), mcpart.phi(), kRecoSecondary); } + + // duplicate-track (split) relabel — overrides origin, as before if (find(mclabels.begin(), mclabels.end(), Rectrack.mcParticleId()) != mclabels.end()) { pid = kRecoFake; } mclabels.push_back(Rectrack.mcParticleId()); - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), mcpart.pt(), mcpart.phi(), static_cast(pid)); + + fillRec(mcpart.pt(), mcpart.phi(), pid); } else { - histos.fill(HIST("hPbPbRecMCdndpt"), RecCol.posZ(), RecCol.centFT0C(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg)); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoBkg); } - } // track (mcrec) loop + } } // collision loop } - void processMCeffpp(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablepp const& RecCols, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + void processMCeffpp(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablepp const& RecCols, TrackMCTrueTable const& GenParticles, TrackMCRecTable const& RecTracks) { bool atLeastOne = false; + int bestCollisionIndex = -1; + int biggestNContribs = -1; + for (const auto& RecCol : RecCols) { + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + } for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) { continue; } + atLeastOne = true; } histos.fill(HIST("hppGenMCvtxz"), mcCollision.posZ()); if (atLeastOne) { histos.fill(HIST("hppGenMCAssoRecvtxz"), mcCollision.posZ()); } + + // ── CHANGE 4 ──────────────────────────────────────────────────────────── + // Restructured pp gen particle loop for primary fraction from MC truth. + // Pass 1 (loose): isGenChargedTrackSelected → fills kGenAllCharged (denom). + // Pass 2 (tight): + isPhysicalPrimary → fills kGenAll + species (num). + // Primary fraction (pp) = kGenAll / kGenAllCharged per pT bin. + // ──────────────────────────────────────────────────────────────────────── for (const auto& particle : GenParticles) { - if (!isGenTrackSelected(particle)) { + if (!isGenChargedTrackSelected(particle)) { + continue; + } + if (atLeastOne) { + histos.fill(HIST("hppGenMCAssoRecdndpt"), mcCollision.posZ(), particle.pt(), particle.phi(), static_cast(kGenAllCharged), kNoGenpTVar); + } + if (!particle.isPhysicalPrimary()) { continue; } histos.fill(HIST("hppGenMCdndpt"), mcCollision.posZ(), particle.pt(), particle.phi()); if (atLeastOne) { histos.fill(HIST("hppGenMCAssoRecdndpt"), mcCollision.posZ(), particle.pt(), particle.phi(), static_cast(kGenAll), kNoGenpTVar); - if (particle.pt() < KminPtCut) { + if (particle.pt() < cfgPtCutMin) { histos.fill(HIST("hppGenMCAssoRecdndpt"), mcCollision.posZ(), particle.pt(), particle.phi(), static_cast(kGenAll), kGenpTup, -10.0 * particle.pt() + 2); histos.fill(HIST("hppGenMCAssoRecdndpt"), mcCollision.posZ(), particle.pt(), particle.phi(), static_cast(kGenAll), kGenpTdown, 5.0 * particle.pt() + 0.5); } else { @@ -538,27 +893,52 @@ struct PtmultCorr { } // track (mcgen) loop for (const auto& RecCol : RecCols) { + if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) { continue; } + histos.fill(HIST("hppRecMCvtxz"), RecCol.posZ()); auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); std::vector mclabels; for (const auto& Rectrack : recTracksPart) { - if (!isTrackSelected(Rectrack)) { + if (!isTrackSelected(Rectrack)) { // all cuts except the pT-dependent DCAxy cut continue; } - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoAll)); + auto trkType = Rectrack.hasTPC() ? kGlobalonly : kITSonly; + // DCAxy cut always evaluated with RECO pT (as in data) + const bool passCut = cfgApplyPtDepDcaXY && passDcaXY(Rectrack.pt(), Rectrack.dcaXY()); + + auto fillRec = [&](float pt, float phi, int cls) { + histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), pt, phi, static_cast(cls), kGlobalplusITS, Rectrack.dcaXY()); + histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), pt, phi, static_cast(cls), trkType, Rectrack.dcaXY()); + if (passCut) { + histos.fill(HIST("hppRecMCdndptDcaCut"), RecCol.posZ(), pt, phi, static_cast(cls), kGlobalplusITS); + histos.fill(HIST("hppRecMCdndptDcaCut"), RecCol.posZ(), pt, phi, static_cast(cls), trkType); + } + }; + + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoAll); + if (Rectrack.has_mcParticle()) { int pid = 0; auto mcpart = Rectrack.mcParticle(); histos.fill(HIST("hppEtaReso"), Rectrack.pt(), Rectrack.pt() - mcpart.pt()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoHasmc)); + fillRec(mcpart.pt(), mcpart.phi(), kRecoHasmc); if (mcpart.isPhysicalPrimary()) { - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(kRecoPrimary)); + // fine DCAxy for sigma(pT): global tracks, reco pT, no DCAxy cut + if (Rectrack.hasTPC()) { + if (mcpart.mcCollisionId() == mcCollision.globalIndex()) { + histos.fill(HIST("hppDcaXYPrimFine"), Rectrack.pt(), Rectrack.dcaXY()); + } else { + histos.fill(HIST("hppDcaXYPrimWrongColl"), Rectrack.pt(), Rectrack.dcaXY()); + } + } + fillRec(mcpart.pt(), mcpart.phi(), kRecoPrimary); switch (std::abs(mcpart.pdgCode())) { case PDG_t::kPiPlus: pid = kRecoPion; @@ -574,57 +954,136 @@ struct PtmultCorr { break; } } else { - pid = kRecoSecondary; - } - if (mcpart.has_mothers()) { - auto mcpartMother = mcpart.template mothers_as().front(); - if (mcpartMother.pdgCode() == PDG_t::kK0Short || std::abs(mcpartMother.pdgCode()) == PDG_t::kLambda0) { + // non-primary → split weak-decay vs material by production mechanism + if (mcpart.getProcess() == TMCProcess::kPDecay) { pid = kRecoWeakDecay; + } else { + pid = kRecoMaterial; } + // union cross-check bin (weak + material) + fillRec(mcpart.pt(), mcpart.phi(), kRecoSecondary); } + // duplicate-track (split) relabel — overrides origin, as before if (find(mclabels.begin(), mclabels.end(), Rectrack.mcParticleId()) != mclabels.end()) { pid = kRecoFake; } mclabels.push_back(Rectrack.mcParticleId()); - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), mcpart.pt(), mcpart.phi(), static_cast(pid)); + fillRec(mcpart.pt(), mcpart.phi(), pid); } else { - histos.fill(HIST("hppRecMCdndpt"), RecCol.posZ(), Rectrack.pt(), Rectrack.phi(), static_cast(kRecoBkg)); + fillRec(Rectrack.pt(), Rectrack.phi(), kRecoBkg); } } // track (mcrec) loop } // collision loop } - void processEvtLossSigLossMCpp(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablepp const& RecCols, TrackMCTrueTable const& GenParticles) { - if (isApplyInelgt0 && !mcCollision.isInelGt0()) { - return; + + // ── Count generated Nch in |eta| < etaRange for event loss ────────────── + int nChMC = 0; + int nChMCEta1 = 0; + for (const auto& particle : GenParticles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (!pdgParticle) { + continue; + } + if (std::abs(pdgParticle->Charge()) < KminCharge) { + continue; + } + if (std::abs(particle.eta()) < etaRange) { + nChMC++; + } + if (std::abs(particle.eta()) > kOne) { + continue; + } + nChMCEta1++; } - if (std::abs(mcCollision.posZ()) >= vtxRange) { + + if (isZvtxPosSelMC && (std::fabs(mcCollision.posZ()) > vtxRange)) { return; } + + //--------------------------- + // Select only INEL > 0 generated events? + //--------------------------- + if (applyInelgt0) { + if (!(nChMCEta1 > kZeroInt)) { + return; + } + } + bool atLeastOne = false; + int bestCollisionIndex = -1; + int biggestNContribs = -1; + + for (const auto& RecCol : RecCols) { + + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + + if (!RecCol.sel8()) { + continue; + } + + if (!RecCol.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + continue; + } + if (!RecCol.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + continue; + } + if (std::fabs(RecCol.posZ()) > vtxRange) { + continue; + } + if (isGoodZvtxFT0vsPV && + !RecCol.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + continue; + } + if (isSameBunchPileup && + !RecCol.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } + + histos.fill(HIST("hNch_AllRecoEvt"), nChMC); // denominator for event splitting + } + + // ── Find best collision, apply full selection ──────────────────────────── + for (const auto& RecCol : RecCols) { if (!isEventSelected(RecCol)) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (RecCol.globalIndex() != bestCollisionIndex) { continue; } atLeastOne = true; + histos.fill(HIST("hNch_WRecoEvtWSelCri"), nChMC); // numerator for event splitting } - // All generated events + + // ── Event loss histograms ──────────────────────────────────────────────── histos.fill(HIST("MCEventHist"), 1); + histos.fill(HIST("hNchMC_AllGen"), nChMC); // denominator for event loss + if (atLeastOne) { histos.fill(HIST("MCEventHist"), 2); + histos.fill(HIST("hNchMC_WithRecoEvt"), nChMC); // numerator for event loss } + + // ── Signal loss particle loop ──────────────────────────────────────────── for (const auto& particle : GenParticles) { if (!isGenTrackSelected(particle)) { continue; } - // All generated particles + + histos.fill(HIST("hPtVsNchMC_AllGen"), particle.pt(), nChMC); // denominator: all generated events histos.fill(HIST("hgenptBeforeEvtSel"), particle.pt()); + if (atLeastOne) { - // All generated particles with at least one reconstructed collision (signal loss estimation) + histos.fill(HIST("hPtVsNchMC_WithRecoEvt"), particle.pt(), nChMC); // numerator: ≥1 reco collision passing selection histos.fill(HIST("hgenptAfterEvtSel"), particle.pt()); } } @@ -632,40 +1091,122 @@ struct PtmultCorr { void processEvtLossSigLossMCPbPb(ColMCTrueTable::iterator const& mcCollision, ColMCRecTablePbPb const& RecCols, TrackMCTrueTable const& GenParticles) { - if (isApplyInelgt0 && !mcCollision.isInelGt0()) { - return; + // ── Count generated Nch in |eta| < etaRange for event loss ────────────── + int nChMC = 0; + + int nChMCEta1 = 0; + for (const auto& particle : GenParticles) { + if (!particle.isPhysicalPrimary()) { + continue; + } + auto pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (!pdgParticle) { + continue; + } + if (std::abs(pdgParticle->Charge()) < KminCharge) { + continue; + } + if (std::abs(particle.eta()) < etaRange) { + nChMC++; + } + + if (std::abs(particle.eta()) > kOne) { + continue; + } + nChMCEta1++; } - if (std::abs(mcCollision.posZ()) >= vtxRange) { + + if (isZvtxPosSelMC && (std::fabs(mcCollision.posZ()) > vtxRange)) { return; } + + //--------------------------- + // Select only INEL > 0 generated events? + //--------------------------- + if (applyInelgt0) { + if (!(nChMCEta1 > kZeroInt)) { + return; + } + } + + // ── Event splitting denominator ────────────────────────────────────────── bool atLeastOne = false; - auto centrality = -999.; + int bestCollisionIndex = -1; + int biggestNContribs = -1; + for (const auto& RecCol : RecCols) { - if (!isEventSelected(RecCol)) { + + if (biggestNContribs < RecCol.numContrib()) { + biggestNContribs = RecCol.numContrib(); + bestCollisionIndex = RecCol.globalIndex(); + } + + if (!RecCol.sel8()) { continue; } - if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { + + if (!RecCol.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { continue; } - centrality = RecCol.centFT0C(); + if (!RecCol.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + continue; + } + if (std::fabs(RecCol.posZ()) > vtxRange) { + continue; + } + if (isGoodZvtxFT0vsPV && + !RecCol.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + continue; + } + if (isSameBunchPileup && + !RecCol.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } + + histos.fill(HIST("hCent_AllRecoEvt"), RecCol.centFT0C()); // denominator for event splitting + } + + // ── Find best collision, apply full selection ──────────────────────────── + + auto centrality = -999.f; + for (const auto& RecCol : RecCols) { + if (RecCol.globalIndex() != bestCollisionIndex) { + continue; + } + + if (!isEventSelected(RecCol)) { + continue; + } + atLeastOne = true; + centrality = RecCol.centFT0C(); + histos.fill(HIST("hCent_WRecoEvtWSelCri"), RecCol.centFT0C()); // numerator for event splitting } - // All generated events + + // ── Event loss histograms ──────────────────────────────────────────────── histos.fill(HIST("MCEventHist"), 1); histos.fill(HIST("hImpactParameterGen"), mcCollision.impactParameter()); + histos.fill(HIST("hNchMC_AllGen"), nChMC); // denominator for event loss + if (atLeastOne) { histos.fill(HIST("MCEventHist"), 2); histos.fill(HIST("hImpactParameterRec"), mcCollision.impactParameter()); histos.fill(HIST("hImpactParvsCentrRec"), centrality, mcCollision.impactParameter()); + histos.fill(HIST("hNchMC_WithRecoEvt"), nChMC); // numerator for event loss + histos.fill(HIST("hNchVsCent_WithRecoEvt"), centrality, nChMC); // CO map } + + // ── Signal loss particle loop ──────────────────────────────────────────── for (const auto& particle : GenParticles) { if (!isGenTrackSelected(particle)) { continue; } - // All generated particles + + histos.fill(HIST("hPtVsNchMC_AllGen"), particle.pt(), nChMC); // denominator: all generated particles from all events histos.fill(HIST("hgenptBeforeEvtSelPbPb"), particle.pt(), mcCollision.impactParameter()); + if (atLeastOne) { - // All generated particles with at least one reconstructed collision (signal loss estimation) + histos.fill(HIST("hPtVsNchMC_WithRecoEvt"), particle.pt(), nChMC); // numerator: particles from events with ≥1 reco collision passing selection histos.fill(HIST("hgenptAfterEvtSelPbPb"), particle.pt(), mcCollision.impactParameter()); } } diff --git a/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx b/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx index 6af85ce4ad6..eb51bf09b1b 100644 --- a/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx +++ b/PWGLF/Tasks/GlobalEventProperties/studyPnch.cxx @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -63,8 +64,7 @@ static constexpr TrackSelectionFlags::flagtype TrackSelectionTpc = TrackSelectionFlags::kTPCNCls | TrackSelectionFlags::kTPCCrossedRowsOverNCls | TrackSelectionFlags::kTPCChi2NDF; -static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = - TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; +// static constexpr TrackSelectionFlags::flagtype TrackSelectionDca = TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; static constexpr TrackSelectionFlags::flagtype TrackSelectionDcaxyOnly = TrackSelectionFlags::kDCAxy; @@ -74,6 +74,7 @@ AxisSpec axisEta{40, -2, 2, "#eta", "EtaAxis"}; AxisSpec axisPhi{629, 0, o2::constants::math::TwoPI, "#phi"}; AxisSpec axisCollSel{5, 0.5, 5.5, "#Event", "CollSelAxis"}; auto static constexpr kMinCharge = 3.f; +auto static constexpr pTminCut = 0.1f; struct StudyPnch { @@ -105,6 +106,21 @@ struct StudyPnch { Configurable isApplyTVX{"isApplyTVX", false, "Enable TVX trigger sel"}; Configurable isApplyCheckID{"isApplyCheckID", true, "Select Tracks evaluating Collision ID"}; Configurable isApplyDuplicatedTrack{"isApplyDuplicatedTrack", true, "Select tracks that are not duplicated"}; + Configurable isApplyPhiSelection{"isApplyPhiSelection", false, "Select tracks in specific phi range"}; + Configurable minPhi{"minPhi", 0.f, "Minimum phi value for track selection"}; + Configurable maxPhi{"maxPhi", 6.283185f, "Maximum phi value for track selection"}; + Configurable isPtincrease{"isPtincrease", false, "Varies low pT particles by a conservative amount of +100%"}; + Configurable isPtdecrease{"isPtdecrease", false, "Varies low pT particles by a conservative amount of -50%"}; + Configurable cPrint{"cPrint", false, "Enable printing information for debugging"}; + Configurable isApplyStrangenessSysUncert{"isApplyStrangenessSysUncert", false, "Enable the evaluation of systematics due to strange particle contribution"}; + Configurable isApplyDCAstandardcuts{"isApplyDCAstandardcuts", true, "Apply DCA standard run 2 cuts"}; + Configurable isApplyDCAcustomcuts{"isApplyDCAcustomcuts", false, "Apply DCA custom cuts"}; + Configurable cDcazP0{"cDcazP0", 1.0f, "dcaz parameter0"}; + Configurable cDcazP1{"cDcazP1", 1.0f, "dcaz parameter1"}; + Configurable cDcazP2{"cDcazP2", 1.0f, "dcaz parameter2"}; + Configurable cDcaxyP0{"cDcaxyP0", 1.0f, "dcaxy parameter0"}; + Configurable cDcaxyP1{"cDcaxyP1", 1.0f, "dcaxy parameter1"}; + Configurable cDcaxyP2{"cDcaxyP2", 1.0f, "dcaxy parameter2"}; void init(InitContext const&) { @@ -114,7 +130,6 @@ struct StudyPnch { AxisSpec axisFt0aMult = {ft0aMultHistBin, "ft0a", "FT0AMultAxis"}; AxisSpec axisFt0cMult = {ft0cMultHistBin, "ft0c", "FT0CMultAxis"}; AxisSpec axisPt = {ptHistBin, "pT", "pTAxis"}; - AxisSpec axisCountNumberTracks = {countNumberTracks, "Count", "CountAxis"}; AxisSpec dcaAxis = {binsDCA, "DCA vs PV"}; histos.add("EventHist", "EventHist", kTH1D, {axisEvent}, false); @@ -130,13 +145,6 @@ struct StudyPnch { x->SetBinLabel(6, "INEL > 0"); x->SetBinLabel(7, "|vz| < 10"); - histos.add("SelCollsHist", "SelCollsHist", kTH1D, {axisCollSel}, false); - auto hstat_colls = histos.get(HIST("SelCollsHist")); - auto* xColls = hstat_colls->GetXaxis(); - xColls->SetBinLabel(1, "All collisions"); - xColls->SetBinLabel(2, "Best Collision Selection"); - xColls->SetBinLabel(3, "Has MC Collision Selection"); - if (doprocessData || doprocessCorrelation || doprocessMonteCarlo) { histos.add("PhiVsEtaHist", "PhiVsEtaHist", kTH2F, {axisPhi, axisEta}, false); histos.add("EtaHist", "EtaHist", kTH1D, {axisEta}, false); @@ -146,6 +154,7 @@ struct StudyPnch { } if (doprocessData) { histos.add("hMultiplicityData", "hMultiplicityData", kTH1F, {axisMult}, true); + histos.add("hMultiplicityDataINELgt0", "hMultiplicityDataINELgt0", kTH1F, {axisMult}, true); } if (doprocessCorrelation) { histos.add("GlobalMult_vs_FT0A", "GlobalMult_vs_FT0A", kTH2F, {axisMult, axisFt0aMult}, true); @@ -161,14 +170,19 @@ struct StudyPnch { histos.add("hMultiplicityMCrec", "hMultiplicityMCrec", kTH1F, {axisMult}, true); histos.add("hMultiplicityMCgen", "hMultiplicityMCgen", kTH1F, {axisMult}, true); histos.add("hResponseMatrix", "hResponseMatrix", kTH2F, {axisMult, axisMult}, true); - histos.add("hCountNTracks", "hCountNTracks", kTH1F, {axisCountNumberTracks}, true); + histos.add("hMultiplicityMCgenPtCut", "hMultiplicityMCgenPtCut", kTH1F, {axisMult}, true); + histos.add("hResponseMatrixPtCut", "hResponseMatrixPtCut", kTH2F, {axisMult, axisMult}, true); + } + if (isApplyStrangenessSysUncert) { + histos.add("hMultiplicityMCSubStrDecay", "hMultiplicityMCSubStrDecay", kTH1F, {axisMult}, true); + histos.add("hResponseMatrixSubStrDecay", "hResponseMatrixSubStrDecay", kTH2F, {axisMult, axisMult}, true); } if (doprocessEvtLossSigLossMC) { histos.add("MCEventHist", "MCEventHist", kTH1F, {axisEvent}, false); - auto hstat = histos.get(HIST("MCEventHist")); - auto* x = hstat->GetXaxis(); - x->SetBinLabel(1, "All MC events"); - x->SetBinLabel(2, "MC events with atleast one reco event"); + auto hstatevloss = histos.get(HIST("MCEventHist")); + auto* xevloss = hstatevloss->GetXaxis(); + xevloss->SetBinLabel(1, "All MC events"); + xevloss->SetBinLabel(2, "MC events with atleast one reco event"); histos.add("hMultiplicityMCgenAll", "hMultiplicityMCgenAll", kTH1F, {axisMult}, true); histos.add("hMultiplicityMCgenSel", "hMultiplicityMCgenSel", kTH1F, {axisMult}, true); } @@ -209,6 +223,12 @@ struct StudyPnch { template bool isTrackSelected(CheckTrack const& track) { + if (isApplyDCAcustomcuts) { + if (std::abs(track.dcaXY()) > cDcaxyP0 + cDcaxyP1 / std::pow(track.pt(), cDcaxyP2)) + return false; + if (std::abs(track.dcaZ()) > cDcazP0 + cDcazP1 / std::pow(track.pt(), cDcazP2)) + return false; + } if (std::abs(track.eta()) >= etaRange) { return false; } @@ -218,6 +238,27 @@ struct StudyPnch { return true; } + template + bool isInegt0Selected(CheckInelgt0 const& track) + { + if (isApplyDCAcustomcuts) { + if (std::abs(track.dcaXY()) > cDcaxyP0 + cDcaxyP1 / std::pow(track.pt(), cDcaxyP2)) + return false; + if (std::abs(track.dcaZ()) > cDcazP0 + cDcazP1 / std::pow(track.pt(), cDcazP2)) + return false; + } + if (!isApplyInelgt0) { + return false; + } + if (std::abs(track.eta()) >= 1.0f) { + return false; + } + if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + return false; + } + return true; + } + template bool isGenTrackSelected(CheckGenTrack const& track) { @@ -243,6 +284,33 @@ struct StudyPnch { return true; } + template + bool isInelgt0GenSelected(CheckGenInelgt0 const& track) + { + if (!isApplyInelgt0) { + return false; + } + if (!track.isPhysicalPrimary()) { + return false; + } + if (!track.producedByGenerator()) { + return false; + } + auto pdgTrack = pdg->GetParticle(track.pdgCode()); + if (pdgTrack == nullptr) { + return false; + } + if (std::abs(pdgTrack->Charge()) < kMinCharge) { + return false; + } + if (std::abs(track.eta()) >= 1.0f) { + return false; + } + if (isApplyExtraPhiCut && ((track.phi() > extraphicut1 && track.phi() < extraphicut2) || track.phi() <= extraphicut3 || track.phi() >= extraphicut4)) { + return false; + } + return true; + } template int countNTracks(countTrk const& tracks) { @@ -251,6 +319,9 @@ struct StudyPnch { if (!isTrackSelected(track)) { continue; } + if (isApplyPhiSelection && (track.phi() < minPhi || track.phi() > maxPhi)) { + continue; + } histos.fill(HIST("hdcaxy"), track.dcaXY()); histos.fill(HIST("hdcaz"), track.dcaZ()); histos.fill(HIST("EtaHist"), track.eta()); @@ -261,6 +332,22 @@ struct StudyPnch { return nTrk; } + template + int countINELTracks(countInelgt0Trk const& tracks) + { + auto nTrkinel = 0; + for (const auto& track : tracks) { + if (!isInegt0Selected(track)) { + continue; + } + if (isApplyPhiSelection && (track.phi() < minPhi || track.phi() > maxPhi)) { + continue; + } + nTrkinel++; + } + return nTrkinel; + } + template int countGenTracks(countTrk const& tracks, McColType const& McCol) { @@ -272,6 +359,9 @@ struct StudyPnch { if (track.mcCollisionId() != McCol.mcCollisionId()) { continue; } + if (isApplyPhiSelection && (track.phi() < minPhi || track.phi() > maxPhi)) { + continue; + } histos.fill(HIST("EtaGenHist"), track.eta()); histos.fill(HIST("PhiGenHist"), track.phi()); histos.fill(HIST("PhiVsEtaGenHist"), track.phi(), track.eta()); @@ -280,6 +370,25 @@ struct StudyPnch { return nTrk; } + template + int countINELGenTracks(countInelgt0GenTrk const& tracks, McColType const& McCol) + { + auto nTrkgeninel = 0; + for (const auto& track : tracks) { + if (!isInelgt0GenSelected(track)) { + continue; + } + if (track.mcCollisionId() != McCol.mcCollisionId()) { + continue; + } + if (isApplyPhiSelection && (track.phi() < minPhi || track.phi() > maxPhi)) { + continue; + } + nTrkgeninel++; + } + return nTrkgeninel; + } + template int countNTracksMcCol(countTrk const& tracks, McColType const& McCol) { @@ -298,6 +407,9 @@ struct StudyPnch { continue; } mcRecIDs.push_back(particle.globalIndex()); + if (isApplyPhiSelection && (track.phi() < minPhi || track.phi() > maxPhi)) { + continue; + } nTrk++; } histos.fill(HIST("hdcaxy"), track.dcaXY()); @@ -309,12 +421,102 @@ struct StudyPnch { return nTrk; } + template + int countINELMcCol(countInelgt0RecTrk const& tracks, McColType const& McCol) + { + auto nTrkrecinel = 0; + std::vector mcRecIDs; + for (const auto& track : tracks) { + if (!isInegt0Selected(track)) { + continue; + } + if (track.has_mcParticle()) { + auto particle = track.mcParticle(); + if (isApplyCheckID && particle.mcCollisionId() != McCol.mcCollisionId()) { + continue; + } + if (isApplyDuplicatedTrack && find(mcRecIDs.begin(), mcRecIDs.end(), particle.globalIndex()) != mcRecIDs.end()) { + continue; + } + mcRecIDs.push_back(particle.globalIndex()); + if (isApplyPhiSelection && (track.phi() < minPhi || track.phi() > maxPhi)) { + continue; + } + nTrkrecinel++; + } + } + return nTrkrecinel; + } + + template + int countStrangeTracksMcCol(countTrk const& tracks, McColType const& McCol) + { + auto nTrkStrange = 0; + auto nTrk = 0; + std::vector mcRecIDs; + for (const auto& track : tracks) { + if (!isTrackSelected(track)) { + continue; + } + if (track.has_mcParticle()) { + auto particle = track.mcParticle(); + if (isApplyCheckID && particle.mcCollisionId() != McCol.mcCollisionId()) { + continue; + } + if (isApplyDuplicatedTrack && find(mcRecIDs.begin(), mcRecIDs.end(), particle.globalIndex()) != mcRecIDs.end()) { + continue; + } + mcRecIDs.push_back(particle.globalIndex()); + if (particle.has_mothers()) { + auto mcMother = particle.template mothers_as().front(); + if (mcMother.pdgCode() == PDG_t::kK0Short || std::abs(mcMother.pdgCode()) == PDG_t::kLambda0) { + nTrkStrange++; + } + } + nTrk++; + } + } + return nTrk - nTrkStrange; + } + + template + float countTracksPtCut(countTrk const& tracks, McColType const& McCol) + { + float nTrk = 0.0; + auto count = 0; + for (const auto& track : tracks) { + if (!isGenTrackSelected(track)) { + continue; + } + if (track.mcCollisionId() != McCol.mcCollisionId()) { + continue; + } + if (track.pt() > pTminCut) { + count++; + continue; + } + if (isPtincrease) { + nTrk += 2 - 10 * track.pt() - 1; + } else { + nTrk += 0.5 + 5 * track.pt() - 1; + } + if (cPrint) { + LOG(info) << "nTrk: " << nTrk; + LOG(info) << "low pT = " << track.pt(); + } + } + if (nTrk != 0 && cPrint) { + LOG(info) << "counts standard pT: " << count; + LOG(info) << "Result of the function: " << nTrk; + } + return nTrk; + } + Filter fTrackSelectionITS = ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && ncheckbit(aod::track::trackCutFlag, TrackSelectionIts); Filter fTrackSelectionTPC = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), ncheckbit(aod::track::trackCutFlag, TrackSelectionTpc), true); - Filter fTrackSelectionDCA = ifnode(dcaZ.node() > 0.f, nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), - ncheckbit(aod::track::trackCutFlag, TrackSelectionDca)); + Filter fTrackSelectionDCA = ifnode(isApplyDCAstandardcuts.node(), nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDcaxyOnly), true); void processData(ColDataTable::iterator const& cols, FilTrackDataTable const& tracks) { @@ -322,9 +524,11 @@ struct StudyPnch { return; } auto mult = countNTracks(tracks); - if (mult > 0) { - histos.fill(HIST("hMultiplicityData"), mult); + auto multINELgt0 = countINELTracks(tracks); + if (isApplyInelgt0 && multINELgt0 == 0) { + return; } + histos.fill(HIST("hMultiplicityData"), mult); } void processCorrelation(ColDataTable::iterator const& cols, FilTrackDataTable const& tracks) @@ -340,40 +544,53 @@ struct StudyPnch { histos.fill(HIST("NPVtracks_vs_GlobalMult"), cols.multNTracksPV(), mult); } - void processMonteCarlo(soa::Join::iterator const& mcCollision, ColMCRecTable const& RecCols, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + void processMonteCarlo(soa::Join::iterator const& mcCollision, ColMCRecTable const& RecCols, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { for (const auto& RecCol : RecCols) { if (!isEventSelected(RecCol)) { continue; } - histos.fill(HIST("SelCollsHist"), 1); // Evaluation of reconstructed collisions with more than 1 contributor if (RecCol.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - histos.fill(HIST("SelCollsHist"), 2); if (!RecCol.has_mcCollision()) { continue; } - histos.fill(HIST("SelCollsHist"), 3); auto recTracksPart = RecTracks.sliceBy(perCollision, RecCol.globalIndex()); auto multrec = countNTracksMcCol(recTracksPart, RecCol); - if (multrec > 0) { - histos.fill(HIST("hMultiplicityMCrec"), multrec); + auto multrecinelgt = countINELMcCol(recTracksPart, RecCol); + float multgen = countGenTracks(GenParticles, RecCol); + float multgeninelgt = countINELGenTracks(GenParticles, RecCol); + float nTrkPtCut = countTracksPtCut(GenParticles, RecCol); + if (isApplyInelgt0) { + if (multrecinelgt == 0 || multgeninelgt == 0) { + continue; + } + } + histos.fill(HIST("hMultiplicityMCrec"), multrec); + histos.fill(HIST("hMultiplicityMCgen"), multgen); + histos.fill(HIST("hResponseMatrix"), multrec, multgen); + if (cPrint) { + LOG(info) << "Generated Particles with standard pT:" << multgen; } - auto multgen = countGenTracks(GenParticles, RecCol); - if (multgen > 0 && multrec > 0) { - histos.fill(HIST("hMultiplicityMCgen"), multgen); - histos.fill(HIST("hResponseMatrix"), multrec, multgen); + nTrkPtCut = multgen + nTrkPtCut; + if (cPrint) { + LOG(info) << "After Counting low pT: " << nTrkPtCut; + LOG(info) << "########################"; + } + histos.fill(HIST("hMultiplicityMCgenPtCut"), nTrkPtCut); + histos.fill(HIST("hResponseMatrixPtCut"), multrec, nTrkPtCut); + if (isApplyStrangenessSysUncert) { + auto nSubtractStrange = countStrangeTracksMcCol(recTracksPart, RecCol); + histos.fill(HIST("hMultiplicityMCSubStrDecay"), nSubtractStrange); + histos.fill(HIST("hResponseMatrixSubStrDecay"), nSubtractStrange, multgen); } } } void processEvtLossSigLossMC(soa::Join::iterator const& mcCollision, ColMCRecTable const& RecCols, TrackMCTrueTable const& GenParticles) { - if (isApplyInelgt0 && !mcCollision.isInelGt0()) { - return; - } if (isApplyTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { return; } @@ -383,15 +600,19 @@ struct StudyPnch { // All generated events histos.fill(HIST("MCEventHist"), 1); auto nTrk_multAll = 0; + auto nTrk_multInelAll = 0; for (const auto& GenParticle : GenParticles) { - if (!isGenTrackSelected(GenParticle)) { - continue; + if (isInelgt0GenSelected(GenParticle)) { + nTrk_multInelAll++; + } + if (isGenTrackSelected(GenParticle)) { + nTrk_multAll++; } - nTrk_multAll++; } - if (nTrk_multAll > 0) { - histos.fill(HIST("hMultiplicityMCgenAll"), nTrk_multAll); + if (isApplyInelgt0 && nTrk_multInelAll == 0) { + return; } + histos.fill(HIST("hMultiplicityMCgenAll"), nTrk_multAll); bool atLeastOne = false; auto numcontributors = -999; @@ -410,15 +631,19 @@ struct StudyPnch { if (atLeastOne) { histos.fill(HIST("MCEventHist"), 2); auto nTrk_multSel = 0; + auto nTrk_multInelSel = 0; for (const auto& GenParticle : GenParticles) { - if (!isGenTrackSelected(GenParticle)) { - continue; + if (isInelgt0GenSelected(GenParticle)) { + nTrk_multInelSel++; + } + if (isGenTrackSelected(GenParticle)) { + nTrk_multSel++; } - nTrk_multSel++; } - if (nTrk_multSel > 0) { - histos.fill(HIST("hMultiplicityMCgenSel"), nTrk_multSel); + if (isApplyInelgt0 && nTrk_multInelSel == 0) { + return; } + histos.fill(HIST("hMultiplicityMCgenSel"), nTrk_multSel); } } diff --git a/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx b/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx index 50dd28efbdc..ce410a7dd57 100644 --- a/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx +++ b/PWGLF/Tasks/Nuspex/AntimatterAbsorptionHMPID.cxx @@ -44,6 +44,7 @@ #include #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGLF/Tasks/Nuspex/CMakeLists.txt b/PWGLF/Tasks/Nuspex/CMakeLists.txt index 71a38eb1c9f..43450b1a161 100644 --- a/PWGLF/Tasks/Nuspex/CMakeLists.txt +++ b/PWGLF/Tasks/Nuspex/CMakeLists.txt @@ -10,10 +10,15 @@ # or submit itself to any jurisdiction. o2physics_add_dpl_workflow(nuclei-batask - SOURCES LFNucleiBATask.cxx + SOURCES lfNucleiBATask.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(antitriton-analysis + SOURCES antitritonAnalysis.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(hypertritonanalysis SOURCES hypertritonAnalysis.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore @@ -49,6 +54,12 @@ o2physics_add_dpl_workflow(spectra-tof PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +#New +o2physics_add_dpl_workflow(spectra-tof-light + SOURCES spectraTOFLight.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(spectra-tof-run2 SOURCES spectraTOFRun2.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -70,7 +81,7 @@ o2physics_add_dpl_workflow(spectra-tpc-tiny COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(nuclei-tpcspectra - SOURCES nucleitpcpbpb.cxx + SOURCES nucleitpcPbPb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -144,6 +155,11 @@ o2physics_add_dpl_workflow(nuclei-from-hypertriton-map PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(antinuclei-in-jets-cdf + SOURCES antinucleiInJetsCDF.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + if(FastJet_FOUND) o2physics_add_dpl_workflow(angular-correlations-in-jets SOURCES angularCorrelationsInJets.cxx @@ -185,8 +201,13 @@ o2physics_add_dpl_workflow(multiplicity-pt PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(flattenicity-pt-spectra + SOURCES flattenicityPtSpectra.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(deuteron-in-jets-trg-pt - SOURCES DeuteronInJetsTrgPt.cxx + SOURCES deuteronInJetsTrgPt.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Nuspex/NucleiHistTask.cxx b/PWGLF/Tasks/Nuspex/NucleiHistTask.cxx index 6ca24667e9a..6239a7b32c3 100644 --- a/PWGLF/Tasks/Nuspex/NucleiHistTask.cxx +++ b/PWGLF/Tasks/Nuspex/NucleiHistTask.cxx @@ -583,7 +583,7 @@ struct NucleiHistTask { if (!isEventSelected(event)) return; - for (auto track : tracks) { // start loop over all tracks + for (const auto& track : tracks) { // start loop over all tracks histTrackcuts_data_spectra->AddBinContent(1); if (event_selection_sel8 && !event.sel8()) @@ -665,7 +665,7 @@ struct NucleiHistTask { auto par = (std::vector)parShiftPt; Particle_Tpc_nSigma_shift->SetParameters(par[0], par[1], par[2], par[3], par[4], par[5]); } - for (auto track : tracks) { + for (const auto& track : tracks) { float TPCnSigma_particle = -100; float TOFnSigma_particle = -100; @@ -886,7 +886,7 @@ struct NucleiHistTask { if (!event_selection_sel8) spectra_reg.fill(HIST("histCentrality"), event.centFT0C()); - for (auto track : tracks) { + for (const auto& track : tracks) { if ((event_selection_sel8 && !event.sel8()) || (enable_Centrality_cut_global && (event.centFT0C() < minCentrality) && (event.centFT0C() > maxCentrality))) continue; diff --git a/PWGLF/Tasks/Nuspex/QAHistTask.cxx b/PWGLF/Tasks/Nuspex/QAHistTask.cxx index 061cad99809..c8ebb7ca97f 100644 --- a/PWGLF/Tasks/Nuspex/QAHistTask.cxx +++ b/PWGLF/Tasks/Nuspex/QAHistTask.cxx @@ -314,7 +314,7 @@ struct QAHistTask { if (!isEventSelected(event)) return; - for (auto track : tracks) { // start loop over all tracks + for (const auto& track : tracks) { // start loop over all tracks if (event_selection_sel8 && !event.sel8()) continue; @@ -564,7 +564,7 @@ struct QAHistTask { if (!isEventSelected(event)) return; - for (auto track : tracks) { + for (const auto& track : tracks) { if (event_selection_sel8 && !event.sel8()) { continue; diff --git a/PWGLF/Tasks/Nuspex/angularCorrelationsInJets.cxx b/PWGLF/Tasks/Nuspex/angularCorrelationsInJets.cxx index a66555f0246..59c8a005ea6 100644 --- a/PWGLF/Tasks/Nuspex/angularCorrelationsInJets.cxx +++ b/PWGLF/Tasks/Nuspex/angularCorrelationsInJets.cxx @@ -618,7 +618,7 @@ struct AngularCorrelationsInJets { } } - void fillMixedEventDeltas(const auto& track, const auto& buffer, int particleType, const TVector3 jetAxis) // correlate tracks from current event with tracks from buffer, i.e. other events + void fillMixedEventDeltas(const auto& track, const auto& buffer, int particleType, const TVector3& jetAxis) // correlate tracks from current event with tracks from buffer, i.e. other events { if (buffer.size() == 0) return; @@ -680,7 +680,7 @@ struct AngularCorrelationsInJets { } // for (int i = 0; i < static_cast(buffer.size()); i++) } - void doCorrelations(const auto& particleVector, const auto& buffer, auto& tempBuffer, int particleType, const TVector3 jetAxis) + void doCorrelations(const auto& particleVector, const auto& buffer, auto& tempBuffer, int particleType, const TVector3& jetAxis) { if (std::isnan(jetAxis.Phi())) return; @@ -743,7 +743,7 @@ struct AngularCorrelationsInJets { } // for (int i = 0; i < static_cast(particleVector.size()); i++) } - void doCorrelationsAnti(const auto& particleVector, const auto& particleVectorAnti, const auto& bufferAnti, auto& tempBuffer, const TVector3 jetAxis) // correlations between proton/antiproton - same story as doCorrelations but different track vectors are correlated + void doCorrelationsAnti(const auto& particleVector, const auto& particleVectorAnti, const auto& bufferAnti, auto& tempBuffer, const TVector3& jetAxis) // correlations between proton/antiproton - same story as doCorrelations but different track vectors are correlated { if (std::isnan(jetAxis.Phi())) return; @@ -775,7 +775,7 @@ struct AngularCorrelationsInJets { } } - void getPerpendicularAxis(TVector3 p, TVector3& u, double sign) + void getPerpendicularAxis(const TVector3& p, TVector3& u, double sign) { // Initialization double ux(0), uy(0), uz(0); @@ -822,7 +822,7 @@ struct AngularCorrelationsInJets { return; } - int analyseJet(int jetCounter, fastjet::PseudoJet jet, const auto& particles, auto& jetProtons, auto& jetAntiprotons, auto& jetPiPlus, auto& jetPiMinus, auto& jetAll, double rhoPerp, double rhoMPerp) + int analyseJet(int jetCounter, const fastjet::PseudoJet& jet, const auto& particles, auto& jetProtons, auto& jetAntiprotons, auto& jetPiPlus, auto& jetPiMinus, auto& jetAll, double rhoPerp, double rhoMPerp) { if (!jet.has_constituents()) return jetCounter; diff --git a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx index 56872bad8ed..ec1b776da41 100644 --- a/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx +++ b/PWGLF/Tasks/Nuspex/antinucleiInJets.cxx @@ -17,12 +17,10 @@ #include "PWGJE/Core/JetBkgSubUtils.h" #include "PWGJE/Core/JetUtilities.h" -#include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" -#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -30,7 +28,6 @@ #include "Common/DataModel/TrackSelectionTables.h" #include -#include #include #include #include @@ -64,10 +61,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -83,9 +78,8 @@ using namespace o2::constants::math; using std::array; // Define convenient aliases for commonly used table joins -using SelectedCollisions = soa::Join; -using RecCollisionsMc = soa::Join; -using GenCollisionsMc = soa::Join; +using SelectedCollisions = soa::Join; +using RecCollisionsMc = soa::Join; using AntiNucleiTracks = soa::Join; using AntiNucleiTracksMc = soa::Join; using LorentzVector = ROOT::Math::PxPyPzEVector; @@ -132,16 +126,15 @@ struct JetMatching { struct AntinucleiInJets { - // Random engine + // Random Engine (Mersenne Twister) std::mt19937 rng; std::uniform_int_distribution generateRandomNr{0, 1}; - // Histogram registries for data, MC, quality control, multiplicity and correlations + // Histogram registries for data, MC, quality control, and multiplicity HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMult{"registryMult", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryCorr{"registryCorr", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Random generator for subsample assignment TRandom3 mRand; @@ -167,15 +160,13 @@ struct AntinucleiInJets { // Jet selection and event filtering parameters Configurable minJetPt{"minJetPt", 10.0, "Minimum pt of the jet after bkg subtraction"}; - Configurable maxJetPt{"maxJetPt", 1e+06, "Maximum pt of the jet after bkg subtraction"}; + Configurable maxJetPt{"maxJetPt", 1e+08, "Maximum pt of the jet after bkg subtraction"}; Configurable ptLeadingMin{"ptLeadingMin", 5.0, "pt Leading Min"}; Configurable rJet{"rJet", 0.4, "Jet resolution parameter R"}; Configurable zVtx{"zVtx", 10.0, "Maximum zVertex"}; - Configurable applyAreaCut{"applyAreaCut", true, "apply area cut"}; - Configurable maxNormalizedJetArea{"maxNormalizedJetArea", 1.0, "area cut"}; - Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from the edge"}; + Configurable applyAreaCut{"applyAreaCut", true, "apply area cut A > f * pi R^2"}; + Configurable deltaEtaEdge{"deltaEtaEdge", 0.0, "eta gap from the edge"}; Configurable nSyst{"nSyst", 50, "number of systematic variations"}; - Configurable nSubsamples{"nSubsamples", 50, "number of subsamples"}; // Track quality, kinematic, and PID selection parameters Configurable requirePvContributor{"requirePvContributor", false, "require that the track is a PV contributor"}; @@ -206,39 +197,26 @@ struct AntinucleiInJets { // Configuration parameters for CCDB access and reweighting input files Configurable applyReweighting{"applyReweighting", true, "enable reweighting for efficiency"}; Configurable urlToCcdb{"urlToCcdb", "http://alice-ccdb.cern.ch/", "url of the personal ccdb"}; - Configurable pathToFile{"pathToFile", "Users/a/alcaliva/reweightingHistogramsAnalysis/", "path to file"}; - Configurable weightsProton{"weightsProton", "weightsProton", "weightsProton"}; - Configurable weightsLambda{"weightsLambda", "weightsLambda", "weightsLambda"}; - Configurable weightsSigma{"weightsSigma", "weightsSigma", "weightsSigma"}; - Configurable weightsXi{"weightsXi", "weightsXi", "weightsXi"}; - Configurable weightsOmega{"weightsOmega", "weightsOmega", "weightsOmega"}; - Configurable weightsJet{"weightsJet", "weightsJet", "weightsJet"}; - Configurable weightsUe{"weightsUe", "weightsUe", "weightsUe"}; - - // Number of events - Configurable shrinkInterval{"shrinkInterval", 1000, "variable that controls how often shrinking happens"}; - - // Range of systematic variations to be processed - Configurable systIndexStart{"systIndexStart", 0, "First systematic index (inclusive)"}; - Configurable systIndexEnd{"systIndexEnd", 50, "Last systematic index (exclusive)"}; + Configurable pathToFile{"pathToFile", "Users/a/alcaliva/reweightingHistogramsAntinucleiInJets/", "path to file"}; // Coalescence momentum Configurable coalescenceMomentum{"coalescenceMomentum", 0.15, "p0 (GeV/c)"}; // Reweighting histograms - TH1F* primaryAntiprotons; - TH1F* primaryAntiLambda; - TH1F* primaryAntiSigma; - TH1F* primaryAntiXi; - TH1F* primaryAntiOmega; - TH1F* antiprotonsInsideJets; - TH1F* antiprotonsPerpCone; + TH1F* primaryAntiprotons = nullptr; + TH1F* primaryAntiLambda = nullptr; + TH1F* primaryAntiSigma = nullptr; + TH1F* primaryAntiXi = nullptr; + TH1F* primaryAntiOmega = nullptr; + TH1F* antiprotonsInsideJets = nullptr; + TH1F* antiprotonsPerpCone = nullptr; + TH1F* antideuteronsInsideJets = nullptr; + TH1F* antideuteronsPerpCone = nullptr; + TH1F* recoJetPtReweightingFactor = nullptr; + TH1F* genJetPtReweightingFactor = nullptr; // CCDB manager service for accessing condition data - Service ccdb; - - // Direct interface to the CCDB API for manual data access - o2::ccdb::CcdbApi ccdbApi; + Service ccdb{}; // Instantiate the main Zorro processing object and define an output to store summary information Zorro zorro; @@ -279,16 +257,16 @@ struct AntinucleiInJets { rng.seed(12345); // Load reweighting histograms from CCDB if antinuclei efficiency processing is enabled - if (doprocessAntinucleiEfficiency || doprocessJetsMCgen || doprocessJetsMCrec) { + if (applyReweighting && (doprocessAntinucleiEfficiency || doprocessJetsMCgen || doprocessJetsMCrec)) { ccdb->setURL(urlToCcdb.value); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); ccdb->setFatalWhenNull(false); - getReweightingHistograms(ccdb, TString(pathToFile), TString(weightsProton), TString(weightsLambda), TString(weightsSigma), TString(weightsXi), TString(weightsOmega), TString(weightsJet), TString(weightsUe)); + getReweightingHistograms(ccdb, TString(pathToFile)); } - // Binning + // pt binning double min = 0.0; double max = 6.0; int nbins = 120; @@ -332,6 +310,9 @@ struct AntinucleiInJets { registryData.add("jetEffectiveAreaOverPiR2", "jet effective area / piR^2", HistType::kTH1F, {{2000, 0, 2, "Area/#piR^{2}"}}); registryData.add("jetArea", "jetArea", HistType::kTH1F, {{5000, 0, 5, "area"}}); + // Jet pt distribution in data + registryData.add("jetPtData", "jet pt distribution in data", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // angle between track and jet axis registryData.add("theta_track_jet", "theta_track_jet", HistType::kTH2F, {{100, 0, 100, "#it{p}^{jet}_{T} (GeV/#it{c})"}, {400, 0, 20.0, "#theta_{track-jet} (deg)"}}); @@ -374,6 +355,8 @@ struct AntinucleiInJets { registryData.add("tpcsignal_data", "Specific energy loss", HistType::kTH2F, {{600, -6., 6., "#it{p} (GeV/#it{c})"}, {1400, 0, 1400, "d#it{E} / d#it{X} (a. u.)"}}); registryData.add("antihelium3_jet_tpc_custom", "antihelium3_jet_tpc_custom", HistType::kTH2F, {{nbins, 3 * min, 3 * max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC} custom"}}); registryData.add("antihelium3_ue_tpc_custom", "antihelium3_ue_tpc_custom", HistType::kTH2F, {{nbins, 3 * min, 3 * max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC} custom"}}); + registryData.add("helium3_jet_tpc_custom", "helium3_jet_tpc_custom", HistType::kTH2F, {{nbins, 3 * min, 3 * max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC} custom"}}); + registryData.add("helium3_ue_tpc_custom", "helium3_ue_tpc_custom", HistType::kTH2F, {{nbins, 3 * min, 3 * max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC} custom"}}); } // Generated antiproton spectra in jets and UE from MC truth @@ -383,6 +366,9 @@ struct AntinucleiInJets { registryMC.add("genEvents", "number of generated events in mc", HistType::kTH1F, {{10, 0, 10, "counter"}}); registryMC.add("genJets", "number of generated jets", HistType::kTH1F, {{10, 0, 10, "counter"}}); + // Jet pt distribution in gen MC + registryMC.add("jetPtMCgenerated", "jet pt distribution in gen MC", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // Size of particle array registryMC.add("sizeParticleArray", "number of particles", HistType::kTH1F, {{1000, 0, 1000, "#it{N}_{part}"}}); @@ -391,6 +377,12 @@ struct AntinucleiInJets { registryMC.add("antiproton_gen_ue", "antiproton_gen_ue", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("antiproton_gen_full", "antiproton_gen_full", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + // Generated spectra of antideuterons + registryMC.add("antideuteron_gen_jet", "antideuteron_gen_jet", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuteron_gen_ue", "antideuteron_gen_ue", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuteron_gen_jet_noweight", "antideuteron_gen_jet_noweight", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuteron_gen_ue_noweight", "antideuteron_gen_ue_noweight", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + // Generated spectra of antiprotons for closure test registryMC.add("antiproton_gen_jet_data", "antiproton_gen_jet_data", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("antiproton_gen_ue_data", "antiproton_gen_ue_data", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); @@ -413,6 +405,9 @@ struct AntinucleiInJets { registryMC.add("recEvents", "number of reconstructed events in mc", HistType::kTH1F, {{20, 0, 20, "counter"}}); registryMC.add("recJets", "number of reconstructed jets", HistType::kTH1F, {{10, 0, 10, "counter"}}); + // Jet pt distribution in rec MC + registryMC.add("jetPtMCreconstructed", "jet pt distribution in rec MC", HistType::kTH1F, {{2000, 0, 200, "#it{p}^{jet}_{T} (GeV/#it{c})"}}); + // Configuration registryMC.add("settingMC", "settingMC", HistType::kTH2F, {{100, 0.0, 50.0, "min #it{p}^{jet}_{T} (GeV/#it{c})"}, {20, 0.0, 1.0, "#it{R}_{jet}"}}); @@ -424,6 +419,12 @@ struct AntinucleiInJets { registryMC.add("antiproton_rec_tpc_full", "antiproton_rec_tpc_full", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("antiproton_rec_tof_full", "antiproton_rec_tof_full", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + // Reconstructed spectra of antideuterons + registryMC.add("antideuteron_rec_tpc_jet", "antideuteron_rec_tpc_jet", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuteron_rec_tof_jet", "antideuteron_rec_tof_jet", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuteron_rec_tpc_ue", "antideuteron_rec_tpc_ue", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuteron_rec_tof_ue", "antideuteron_rec_tof_ue", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + // Reconstructed spectra of antiprotons for closure test registryMC.add("antiproton_rec_tpc_jet_data", "antiproton_rec_tpc_jet_data", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("antiproton_rec_tof_jet_data", "antiproton_rec_tof_jet_data", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); @@ -463,8 +464,8 @@ struct AntinucleiInJets { // Generated spectra of (anti)deuterons registryMC.add("deuteron_gen_jet", "deuteron_gen_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("deuteron_gen_ue", "deuteron_gen_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antideuteron_gen_jet", "antideuteron_gen_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antideuteron_gen_ue", "antideuteron_gen_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuterons_gen_jet", "antideuterons_gen_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuterons_gen_ue", "antideuterons_gen_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); // Generated spectra of (anti)helium3 registryMC.add("helium3_gen_jet", "helium3_gen_jet", HistType::kTH1F, {{nbins, 3 * min, 3 * max, "#it{p}_{T} (GeV/#it{c})"}}); @@ -483,10 +484,10 @@ struct AntinucleiInJets { registryMC.add("deuteron_rec_tof_jet", "deuteron_rec_tof_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("deuteron_rec_tpc_ue", "deuteron_rec_tpc_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("deuteron_rec_tof_ue", "deuteron_rec_tof_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antideuteron_rec_tpc_jet", "antideuteron_rec_tpc_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antideuteron_rec_tof_jet", "antideuteron_rec_tof_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antideuteron_rec_tpc_ue", "antideuteron_rec_tpc_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antideuteron_rec_tof_ue", "antideuteron_rec_tof_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuterons_rec_tpc_jet", "antideuterons_rec_tpc_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuterons_rec_tof_jet", "antideuterons_rec_tof_jet", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuterons_rec_tpc_ue", "antideuterons_rec_tpc_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antideuterons_rec_tof_ue", "antideuterons_rec_tof_ue", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); // Reconstructed spectra of (anti)helium3 registryMC.add("helium3_rec_tpc_jet", "helium3_rec_tpc_jet", HistType::kTH1F, {{nbins, 3 * min, 3 * max, "#it{p}_{T} (GeV/#it{c})"}}); @@ -532,38 +533,6 @@ struct AntinucleiInJets { registryMC.add("jetPtResolution", "jet Pt Resolution", HistType::kTH2F, {{1000, 0, 100, "#it{p}^{jet}_{T,true} (GeV/#it{c})"}, {1000, -20, 20, "#Delta #it{p}^{jet}_{T} (GeV/#it{c})"}}); } - // Coalescence and Correlation analysis - if (doprocessCoalescenceCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - - registryMC.add("genEventsCoalescenceCorr", "genEventsCoalescenceCorr", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryMC.add("antideuteron_fullEvent_CoalescenceCorr", "antideuteron_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, 2 * min, 2 * max, "#it{p}_{T} (GeV/#it{c})"}}); - registryMC.add("antiproton_fullEvent_CoalescenceCorr", "antiproton_fullEvent_CoalescenceCorr", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); - - // Counter histograms - registryCorr.add("eventCounter_CoalescenceCorr", "number of events in Coalescence simulation", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent_CoalescenceCorr", "Number of events per centrality (Full Event) in Coalescence simulation", HistType::kTH1F, {multiplicityAxis}); - - // Correlation histograms - registryCorr.add("rho_fullEvent_CoalescenceCorr", "rho_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - registryCorr.add("rho_netP_netD_fullEvent_CoalescenceCorr", "rho_netP_netD_fullEvent_CoalescenceCorr", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent_CoalescenceCorr", "q1d_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - registryCorr.add("q1p_fullEvent_CoalescenceCorr", "q1p_fullEvent_CoalescenceCorr", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - registryCorr.add("q1d_square_fullEvent_CoalescenceCorr", "q1d_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - registryCorr.add("q1p_square_fullEvent_CoalescenceCorr", "q1p_square_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - registryCorr.add("q1d_q1p_fullEvent_CoalescenceCorr", "q1d_q1p_fullEvent_CoalescenceCorr", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - } - // Systematic uncertainties (Data) if (doprocessSystData) { registryData.add("number_of_events_data_syst", "event counter", HistType::kTH1F, {{20, 0, 20, "counter"}}); @@ -594,111 +563,46 @@ struct AntinucleiInJets { registryMC.add("antiproton_incl_syst", "antiproton_incl_syst", HistType::kTH2F, {{50, 0, 50, "systematic uncertainty"}, {nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); registryMC.add("antiproton_prim_syst", "antiproton_prim_syst", HistType::kTH2F, {{50, 0, 50, "systematic uncertainty"}, {nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); } - - // Correlation analysis - if (doprocessCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec subsampleAxis{nSubsamples, 0, static_cast(nSubsamples), "Subsample Index"}; - - // Event counter - registryCorr.add("eventCounter", "number of events", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent", "Number of events per centrality (Full Event)", HistType::kTH2F, {multiplicityAxis, subsampleAxis}); - // registryCorr.add("eventCounter_centrality_jet", "Number of events per centrality (Jet)", HistType::kTH1F, {multiplicityAxis}); - // registryCorr.add("eventCounter_centrality_ue", "Number of events per centrality (Underlying Event)", HistType::kTH1F, {multiplicityAxis}); - - // Correlation histograms: antiproton vs. antideuteron number vs. event multiplicity - // registryCorr.add("rho_jet", "rho_jet", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - // registryCorr.add("rho_ue", "rho_ue", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis}); - registryCorr.add("rho_fullEvent", "rho_fullEvent", HistType::kTHnSparseD, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis, subsampleAxis}); - - // Correlation histograms: net antiproton vs. net antideuteron numbers - // registryCorr.add("rho_netP_netD_jet", "rho_netP_netD_jet", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - // registryCorr.add("rho_netP_netD_ue", "rho_netP_netD_ue", HistType::kTH2F, {nAntideuteronsAxis, nAntiprotonsAxis}); - registryCorr.add("rho_netP_netD_fullEvent", "rho_netP_netD_fullEvent", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, subsampleAxis}); - - // Efficiency histograms jet - // registryCorr.add("q1d_jet", "q1d_jet", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1p_jet", "q1p_jet", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1d_square_jet", "q1d_square_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - // registryCorr.add("q1p_square_jet", "q1p_square_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - // registryCorr.add("q1d_q1p_jet", "q1d_q1p_jet", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - - // Efficiency histograms UE - // registryCorr.add("q1d_ue", "q1d_ue", HistType::kTH3F, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1p_ue", "q1p_ue", HistType::kTH3F, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis}); - // registryCorr.add("q1d_square_ue", "q1d_square_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis}); - // registryCorr.add("q1p_square_ue", "q1p_square_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis}); - // registryCorr.add("q1d_q1p_ue", "q1d_q1p_ue", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent", "q1d_fullEvent", HistType::kTHnSparseD, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1p_fullEvent", "q1p_fullEvent", HistType::kTHnSparseD, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1d_square_fullEvent", "q1d_square_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1p_square_fullEvent", "q1p_square_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis, subsampleAxis}); - registryCorr.add("q1d_q1p_fullEvent", "q1d_q1p_fullEvent", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis, subsampleAxis}); - } - - // Systematic uncertainties on correlation analysis - if (doprocessSystCorr) { - - // Axes definitions for multidimensional histogram binning - const AxisSpec multiplicityAxis{100, 0.0, 100.0, "multiplicity percentile"}; - const AxisSpec ptPerNucleonAxis{5, 0.4, 0.9, "{p}_{T}/A (GeV/#it{c})"}; - const AxisSpec nAntideuteronsAxis{10, 0.0, 10.0, "N_{#bar{d}}"}; - const AxisSpec nAntiprotonsAxis{10, 0.0, 10.0, "N_{#bar{p}}"}; - const AxisSpec nBarD2Axis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{d}}^{j}"}; - const AxisSpec nBarP2Axis{100, 0.0, 100.0, "N_{#bar{p}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec nBarDnBarPAxis{100, 0.0, 100.0, "N_{#bar{d}}^{i} #times N_{#bar{p}}^{j}"}; - const AxisSpec systAxis{nSyst, 0, static_cast(nSyst), "Systematic Variation Index"}; - - registryCorr.add("eventCounter_syst", "number of events syst", HistType::kTH1F, {{20, 0, 20, "counter"}}); - registryCorr.add("eventCounter_centrality_fullEvent_syst", "Number of events per centrality (Full Event) Syst", HistType::kTH2F, {multiplicityAxis, systAxis}); - - // Correlation histograms - registryCorr.add("rho_fullEvent_syst", "rho_fullEvent_syst", HistType::kTHnSparseD, {nAntideuteronsAxis, nAntiprotonsAxis, multiplicityAxis, systAxis}); - registryCorr.add("rho_netP_netD_fullEvent_syst", "rho_netP_netD_fullEvent_syst", HistType::kTH3F, {nAntideuteronsAxis, nAntiprotonsAxis, systAxis}); - - // Efficiency histograms full event - registryCorr.add("q1d_fullEvent_syst", "q1d_fullEvent_syst", HistType::kTHnSparseD, {nAntideuteronsAxis, ptPerNucleonAxis, multiplicityAxis, systAxis}); - registryCorr.add("q1p_fullEvent_syst", "q1p_fullEvent_syst", HistType::kTHnSparseD, {nAntiprotonsAxis, ptPerNucleonAxis, multiplicityAxis, systAxis}); - registryCorr.add("q1d_square_fullEvent_syst", "q1d_square_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarD2Axis, multiplicityAxis, systAxis}); - registryCorr.add("q1p_square_fullEvent_syst", "q1p_square_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarP2Axis, multiplicityAxis, systAxis}); - registryCorr.add("q1d_q1p_fullEvent_syst", "q1d_q1p_fullEvent_syst", HistType::kTHnSparseD, {ptPerNucleonAxis, ptPerNucleonAxis, nBarDnBarPAxis, multiplicityAxis, systAxis}); - } } - void getReweightingHistograms(o2::framework::Service const& ccdbObj, TString filepath, TString antip, TString antilambda, TString antisigma, TString antixi, TString antiomega, TString jet, TString ue) + void getReweightingHistograms(o2::framework::Service const& ccdbObj, TString const& filepath) { TList* list = ccdbObj->get(filepath.Data()); if (!list) { - LOGP(error, "Could not retrieve the list from CCDB"); + LOGP(fatal, "Could not retrieve the list from CCDB"); return; } // Get reweighting histograms for primary fraction - primaryAntiprotons = static_cast(list->FindObject(antip)); - primaryAntiLambda = static_cast(list->FindObject(antilambda)); - primaryAntiSigma = static_cast(list->FindObject(antisigma)); - primaryAntiXi = static_cast(list->FindObject(antixi)); - primaryAntiOmega = static_cast(list->FindObject(antiomega)); + primaryAntiprotons = dynamic_cast(list->FindObject("weightsProton")); + primaryAntiLambda = dynamic_cast(list->FindObject("weightsLambda")); + primaryAntiSigma = dynamic_cast(list->FindObject("weightsSigma")); + primaryAntiXi = dynamic_cast(list->FindObject("weightsXi")); + primaryAntiOmega = dynamic_cast(list->FindObject("weightsOmega")); if (!primaryAntiprotons || !primaryAntiSigma || !primaryAntiLambda || !primaryAntiXi || !primaryAntiOmega) { - LOGP(error, "Missing one or more reweighting histograms for primary fraction in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for primary fraction in CCDB list"); } - // Get reweighting histograms for efficiency - antiprotonsInsideJets = static_cast(list->FindObject(jet)); - antiprotonsPerpCone = static_cast(list->FindObject(ue)); + // Get reweighting histograms for antiproton efficiency + antiprotonsInsideJets = dynamic_cast(list->FindObject("weightsJet")); + antiprotonsPerpCone = dynamic_cast(list->FindObject("weightsUe")); if (!antiprotonsInsideJets || !antiprotonsPerpCone) { - LOGP(error, "Missing one or more reweighting histograms for efficiency in CCDB list"); + LOGP(fatal, "Missing one or more reweighting histograms for antiproton efficiency in CCDB list"); + } + + // Get reweighting histograms for antideuteron efficiency + antideuteronsInsideJets = dynamic_cast(list->FindObject("weightsAntidJet")); + antideuteronsPerpCone = dynamic_cast(list->FindObject("weightsAntidUe")); + if (!antideuteronsInsideJets || !antideuteronsPerpCone) { + LOGP(fatal, "Missing one or more reweighting histograms for antideuteron efficiency in CCDB list"); + } + + // Get reco jet pt reweighting histogram + recoJetPtReweightingFactor = dynamic_cast(list->FindObject("recoJetPtReweightingFactor")); + genJetPtReweightingFactor = dynamic_cast(list->FindObject("genJetPtReweightingFactor")); + if (!recoJetPtReweightingFactor || !genJetPtReweightingFactor) { + LOGP(fatal, "Missing one of jet pt reweighting histograms in CCDB list"); } LOGP(info, "Successfully loaded reweighting histograms from CCDB path"); @@ -734,9 +638,9 @@ struct AntinucleiInJets { // Check if particle is a physical primary or a decay product of a heavy-flavor hadron bool isPhysicalPrimaryOrFromHF(aod::McParticle const& particle, aod::McParticles const& mcParticles) { - // Keep only pi, K, p, e, mu + // Keep only pi, K, p, d, e, mu int pdg = std::abs(particle.pdgCode()); - if (!(pdg == PDG_t::kPiPlus || pdg == PDG_t::kKPlus || pdg == PDG_t::kProton || pdg == PDG_t::kElectron || pdg == PDG_t::kMuonMinus)) + if (!(pdg == PDG_t::kPiPlus || pdg == PDG_t::kKPlus || pdg == PDG_t::kProton || pdg == o2::constants::physics::Pdg::kDeuteron || pdg == PDG_t::kElectron || pdg == PDG_t::kMuonMinus)) return false; // Constants for identifying heavy-flavor (charm and bottom) content from PDG codes @@ -757,9 +661,34 @@ struct AntinucleiInJets { return (particle.isPhysicalPrimary() || fromHF); } + // Check if particle is from heavy-flavor hadron decay + bool isFromHF(aod::McParticle const& particle, aod::McParticles const& mcParticles) + { + // Keep only pi, K, p, d, e, mu + int pdg = std::abs(particle.pdgCode()); + if (!(pdg == PDG_t::kPiPlus || pdg == PDG_t::kKPlus || pdg == PDG_t::kProton || pdg == o2::constants::physics::Pdg::kDeuteron || pdg == PDG_t::kElectron || pdg == PDG_t::kMuonMinus)) + return false; + + // Constants for identifying heavy-flavor (charm and bottom) content from PDG codes + static constexpr int CharmQuark = 4; + static constexpr int BottomQuark = 5; + static constexpr int Hundreds = 100; + static constexpr int Thousands = 1000; + + // Check if particle is from heavy-flavor decay + bool fromHF = false; + if (particle.has_mothers()) { + auto mother = mcParticles.iteratorAt(particle.mothersIds()[0]); + int motherPdg = std::abs(mother.pdgCode()); + fromHF = (motherPdg / Hundreds == CharmQuark || motherPdg / Hundreds == BottomQuark || motherPdg / Thousands == CharmQuark || motherPdg / Thousands == BottomQuark); + } + + return fromHF; + } + // Evaluate proton–neutron coalescence for deuteron formation template - bool passDeuteronCoalescence(const ReducedPart& p, const ReducedPart& n, double p0, TRandom3& mRand) + bool passDeuteronCoalescence(const ReducedPart& p, const ReducedPart& n, double p0, TRandom3& random) { // Nucleon masses const double mp = o2::constants::physics::MassProton; @@ -799,7 +728,7 @@ struct AntinucleiInJets { } // Spin-statistical acceptance - if (mRand.Uniform() > SpinFactor) { + if (random.Uniform() > SpinFactor) { return false; } return true; @@ -884,17 +813,6 @@ struct AntinucleiInJets { return deltaPhi; } - // Find bin - int findBin(const std::vector& edges, double value) - { - auto it = std::upper_bound(edges.begin(), edges.end(), value); - int index = static_cast(it - edges.begin()) - 1; - if (index < 0 || index >= static_cast(edges.size()) - 1) { - return -1; // value is out of bounds - } - return index; - } - // ITS hit template bool hasITSHit(const TrackIts& track, int layer) @@ -1042,104 +960,6 @@ struct AntinucleiInJets { return false; } - // Selection of (anti)protons - template - bool isProton(const ProtonTrack& track) - { - // Constants - static constexpr double PtThreshold = 0.6; - static constexpr double NsigmaMax = 3.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaPr(); - const double nsigmaTOF = track.tofNSigmaPr(); - const double pt = track.pt(); - - // Apply TPC PID cut - if (std::abs(nsigmaTPC) > NsigmaMax) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID - return (track.hasTOF() && std::abs(nsigmaTOF) < NsigmaMax); - } - - // Selection of (anti)deuterons - template - bool isDeuteron(const DeuteronTrack& track) - { - // Constants - static constexpr double PtThreshold = 1.0; - static constexpr double NsigmaMax = 3.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaDe(); - const double nsigmaTOF = track.tofNSigmaDe(); - const double pt = track.pt(); - - // Apply TPC PID cut - if (std::abs(nsigmaTPC) > NsigmaMax) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID - return (track.hasTOF() && std::abs(nsigmaTOF) < NsigmaMax); - } - - // Selection of (anti)protons with systematic variations - template - bool isProtonSyst(const ProtonTrack& track, double minSigTPC, double maxSigTPC, double minSigTOF, double maxSigTOF) - { - // Constant - static constexpr double PtThreshold = 0.6; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaPr(); - const double nsigmaTOF = track.tofNSigmaPr(); - const double pt = track.pt(); - - // Apply TPC PID cut (with systematic variations) - if (nsigmaTPC < minSigTPC || nsigmaTPC > maxSigTPC) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID (with systematic variations) - return (track.hasTOF() && nsigmaTOF > minSigTOF && nsigmaTOF < maxSigTOF); - } - - // Selection of (anti)deuterons with systematic variations - template - bool isDeuteronSyst(const DeuteronTrack& track, double minSigTPC, double maxSigTPC, double minSigTOF, double maxSigTOF) - { - // Constant - static constexpr double PtThreshold = 1.0; - - // PID variables and transverse momentum of the track - const double nsigmaTPC = track.tpcNSigmaDe(); - const double nsigmaTOF = track.tofNSigmaDe(); - const double pt = track.pt(); - - // Apply TPC PID cut (with systematic variations) - if (nsigmaTPC < minSigTPC || nsigmaTPC > maxSigTPC) - return false; - - // Low-pt: TPC PID is sufficient - if (pt < PtThreshold) - return true; - - // High-pt: require valid TOF match and pass TOF PID (with systematic variations) - return (track.hasTOF() && nsigmaTOF > minSigTOF && nsigmaTOF < maxSigTOF); - } - // Process Data void processData(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks, aod::BCsWithTimestamps const&) { @@ -1288,21 +1108,25 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Fill jet pt distribution + registryData.fill(HIST("jetPtData"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) continue; // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && (!isppRefAnalysis) && normalizedJetArea > maxNormalizedJetArea) - continue; - if (isppRefAnalysis && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) + if (applyAreaCut && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) continue; isAtLeastOneJetSelected = true; + // Fill histograms with jet effective area / piR^2 for normalization + registryData.fill(HIST("jetEffectiveAreaOverPiR2"), jet.area() / (PI * rJet * rJet)); + registryData.fill(HIST("jetArea"), jet.area()); + // Perpendicular cones - double coneRadius = std::sqrt(jet.area() / PI); TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); @@ -1310,10 +1134,6 @@ struct AntinucleiInJets { continue; } - // Fill histogram with jet effective area / piR^2 - registryData.fill(HIST("jetEffectiveAreaOverPiR2"), jet.area() / (PI * rJet * rJet)); - registryData.fill(HIST("jetArea"), jet.area()); - // Get jet constituents std::vector jetConstituents = jet.constituents(); @@ -1401,6 +1221,11 @@ struct AntinucleiInJets { } if (passedItsPidHel) { registryData.fill(HIST("helium3_jet_tpc"), 2.0 * pt, nsigmaTPCHe); + // custom nsigma He3 based on bethe bloch fit of TPC signal + double tpcSignal = track.tpcSignal(); + double expectedSignalHe3 = common::BetheBlochAleph(static_cast(track.tpcInnerParam() * 2. / o2::constants::physics::MassHelium3), cfgBetheBlochParams.value[0], cfgBetheBlochParams.value[1], cfgBetheBlochParams.value[2], cfgBetheBlochParams.value[3], cfgBetheBlochParams.value[4]); + double nSigmaTPCHe3Custom = ((tpcSignal / expectedSignalHe3) - 1.) / 0.045; + registryData.fill(HIST("helium3_jet_tpc_custom"), 2.0 * pt, nSigmaTPCHe3Custom); } } } @@ -1420,14 +1245,8 @@ struct AntinucleiInJets { double deltaPhiUe2 = getDeltaPhi(track.phi(), ueAxis2.Phi()); double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - // Determine the maximum allowed distance from UE axes for particle selection - double maxConeRadius = coneRadius; - if (applyAreaCut) { - maxConeRadius = std::sqrt(maxNormalizedJetArea) * rJet; - } - // Reject tracks that lie outside the maxConeRadius from both UE axes - if (deltaRUe1 > maxConeRadius && deltaRUe2 > maxConeRadius) + if (deltaRUe1 > rJet && deltaRUe2 > rJet) continue; // Define variables @@ -1496,6 +1315,11 @@ struct AntinucleiInJets { } if (passedItsPidHel) { registryData.fill(HIST("helium3_ue_tpc"), 2.0 * pt, nsigmaTPCHe); + // custom nsigma He3 based on bethe bloch fit of TPC signal + double tpcSignal = track.tpcSignal(); + double expectedSignalHe3 = common::BetheBlochAleph(static_cast(track.tpcInnerParam() * 2. / o2::constants::physics::MassHelium3), cfgBetheBlochParams.value[0], cfgBetheBlochParams.value[1], cfgBetheBlochParams.value[2], cfgBetheBlochParams.value[3], cfgBetheBlochParams.value[4]); + double nSigmaTPCHe3Custom = ((tpcSignal / expectedSignalHe3) - 1.) / 0.045; + registryData.fill(HIST("helium3_ue_tpc_custom"), 2.0 * pt, nSigmaTPCHe3Custom); } } } @@ -1578,8 +1402,7 @@ struct AntinucleiInJets { continue; // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && normalizedJetArea > maxNormalizedJetArea) + if (applyAreaCut && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) continue; isAtLeastOneJetSelected = true; } @@ -1660,7 +1483,6 @@ struct AntinucleiInJets { // Jet properties and perpendicular cone std::vector jetConstituents = jet.constituents(); TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - double coneRadius = std::sqrt(jet.area() / PI); TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { @@ -1692,7 +1514,7 @@ struct AntinucleiInJets { double deltaEtaUe2 = track.eta() - ueAxis2.Eta(); double deltaPhiUe2 = getDeltaPhi(track.phi(), ueAxis2.Phi()); double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - if (deltaRUe1 > coneRadius && deltaRUe2 > coneRadius) + if (deltaRUe1 > rJet && deltaRUe2 > rJet) continue; ptPerp = ptPerp + track.pt(); @@ -1716,7 +1538,7 @@ struct AntinucleiInJets { Preslice mcParticlesPerMcCollision = o2::aod::mcparticle::mcCollisionId; // Antinuclei reconstruction efficiency - void processAntinucleiEfficiency(GenCollisionsMc const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) + void processAntinucleiEfficiency(aod::McCollisions const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { // Loop over generated collisions for (const auto& collision : genCollisions) { @@ -1751,8 +1573,8 @@ struct AntinucleiInJets { registryMC.fill(HIST("deuteron_gen_ue"), particle.pt()); break; case -o2::constants::physics::Pdg::kDeuteron: - registryMC.fill(HIST("antideuteron_gen_jet"), particle.pt()); - registryMC.fill(HIST("antideuteron_gen_ue"), particle.pt()); + registryMC.fill(HIST("antideuterons_gen_jet"), particle.pt()); + registryMC.fill(HIST("antideuterons_gen_ue"), particle.pt()); break; case o2::constants::physics::Pdg::kHelium3: registryMC.fill(HIST("helium3_gen_jet"), particle.pt()); @@ -1775,6 +1597,8 @@ struct AntinucleiInJets { case PDG_t::kSigmaBarMinus: registryMC.fill(HIST("sigmaBar"), particle.pt()); break; + default: + break; } } } @@ -1983,12 +1807,12 @@ struct AntinucleiInJets { // Fill histograms of antideuterons if (track.sign() < 0 && particle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron && passedItsPidDeut) { if (nsigmaTPCDe > minNsigmaTpc && nsigmaTPCDe < maxNsigmaTpc) { - registryMC.fill(HIST("antideuteron_rec_tpc_jet"), track.pt()); - registryMC.fill(HIST("antideuteron_rec_tpc_ue"), track.pt()); + registryMC.fill(HIST("antideuterons_rec_tpc_jet"), track.pt()); + registryMC.fill(HIST("antideuterons_rec_tpc_ue"), track.pt()); if (track.hasTOF() && nsigmaTOFDe > minNsigmaTof && nsigmaTOFDe < maxNsigmaTof) { - registryMC.fill(HIST("antideuteron_rec_tof_jet"), track.pt()); - registryMC.fill(HIST("antideuteron_rec_tof_ue"), track.pt()); + registryMC.fill(HIST("antideuterons_rec_tof_jet"), track.pt()); + registryMC.fill(HIST("antideuterons_rec_tof_ue"), track.pt()); } } } @@ -2026,14 +1850,12 @@ struct AntinucleiInJets { PROCESS_SWITCH(AntinucleiInJets, processAntinucleiEfficiency, "process antinuclei efficiency", false); // Generated events - void processJetsMCgen(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) + void processJetsMCgen(aod::McCollisions const& collisions, aod::McParticles const& mcParticles) { // Define per-event particle containers std::vector fjParticles; std::vector protonMomentum; - - // Event counter - int eventCounter = 0; + std::vector deuteronMomentum; // Jet and area definitions fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); @@ -2042,12 +1864,10 @@ struct AntinucleiInJets { // Loop over all simulated collisions for (const auto& collision : collisions) { - // Increment event counter - eventCounter++; - // Clear containers at the start of the event loop fjParticles.clear(); protonMomentum.clear(); + deuteronMomentum.clear(); // Event counter: before event selection registryMC.fill(HIST("genEvents"), 0.5); @@ -2085,6 +1905,12 @@ struct AntinucleiInJets { registryMC.fill(HIST("antiproton_gen_full"), particle.pt()); } + // Store 3-momentum vectors of antideuterons for further analysis + if (particle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { + TVector3 pVec(particle.px(), particle.py(), particle.pz()); + deuteronMomentum.emplace_back(pVec); + } + // 4-momentum representation of a particle double energy = std::sqrt(particle.p() * particle.p() + MassPionCharged * MassPionCharged); fastjet::PseudoJet fourMomentum(particle.px(), particle.py(), particle.pz(), energy); @@ -2118,16 +1944,27 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Get generated jet pt reweighting factor + double genJetPtWeight(1.0); + if (applyReweighting) { + double jetPt = jetMinusBkg.pt(); + if (jetPt >= genJetPtReweightingFactor->GetXaxis()->GetXmin() && jetPt < genJetPtReweightingFactor->GetXaxis()->GetXmax()) { + int jetPtBin = genJetPtReweightingFactor->FindBin(jetPt); + genJetPtWeight = genJetPtReweightingFactor->GetBinContent(jetPtBin); + } + } + + // Fill jet pt distribution + registryMC.fill(HIST("jetPtMCgenerated"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) continue; // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && (!isppRefAnalysis) && normalizedJetArea > maxNormalizedJetArea) - continue; - if (isppRefAnalysis && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) + if (applyAreaCut && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) continue; isAtLeastOneJetSelected = true; @@ -2137,46 +1974,67 @@ struct AntinucleiInJets { // Analyze jet constituents std::vector jetConstituents = jet.constituents(); for (const auto& particle : jetConstituents) { - if (particle.user_index() != PDG_t::kProtonBar) - continue; + // Particle selection based on PDG + bool isAntip = particle.user_index() == PDG_t::kProtonBar; + bool isAntid = particle.user_index() == -o2::constants::physics::Pdg::kDeuteron; + + // Pseudorapidity selection if (particle.eta() < minEta || particle.eta() > maxEta) continue; - // Fill normalization histogram - registryMC.fill(HIST("antiproton_deltay_deltaphi_jet"), particle.eta() - jet.eta(), getDeltaPhi(particle.phi(), jet.phi())); + // Fill antiproton spectra + if (isAntip) { + // Fill normalization histogram + registryMC.fill(HIST("antiproton_deltay_deltaphi_jet"), particle.eta() - jet.eta(), getDeltaPhi(particle.phi(), jet.phi())); + + // Calculate weight + double weightJet = genJetPtWeight; + if (applyReweighting) { + if (particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(particle.pt()); + weightJet *= antiprotonsInsideJets->GetBinContent(ipt); + } + } - // Calculate weight - double weightJet(1.0); - if (applyReweighting && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antiprotonsInsideJets->FindBin(particle.pt()); - weightJet = antiprotonsInsideJets->GetBinContent(ipt); - } + // Fill histogram for generated antiprotons + registryMC.fill(HIST("antiproton_gen_jet"), particle.pt(), weightJet); + + // Fill histograms for generated antiprotons for closure test + if (isPseudoData) { + registryMC.fill(HIST("antiproton_gen_jet_data"), particle.pt()); + } else { + registryMC.fill(HIST("antiproton_gen_jet_mc"), particle.pt()); + } - // Fill histogram for generated antiprotons - registryMC.fill(HIST("antiproton_gen_jet"), particle.pt(), weightJet); + // Fill 2d (pt,eta) distribution of antiprotons + registryMC.fill(HIST("antiproton_eta_pt_jet"), particle.pt(), particle.eta(), weightJet); + } // end if antip + + // Fill antideuteron spectra + if (isAntid) { + double weightJetAntid = genJetPtWeight; + if (applyReweighting) { + if (particle.pt() >= antideuteronsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antideuteronsInsideJets->FindBin(particle.pt()); + weightJetAntid *= antideuteronsInsideJets->GetBinContent(ipt); + } + } - // Fill histograms for generated antiprotons for closure test - if (isPseudoData) { - registryMC.fill(HIST("antiproton_gen_jet_data"), particle.pt()); - } else { - registryMC.fill(HIST("antiproton_gen_jet_mc"), particle.pt()); + registryMC.fill(HIST("antideuteron_gen_jet"), particle.pt(), weightJetAntid); + registryMC.fill(HIST("antideuteron_gen_jet_noweight"), particle.pt()); } - - // Fill 2d (pt,eta) distribution of antiprotons - registryMC.fill(HIST("antiproton_eta_pt_jet"), particle.pt(), particle.eta(), weightJet); } // Set up two perpendicular cone axes for underlying event estimation TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - double coneRadius = std::sqrt(jet.area() / PI); TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { continue; } - // Loop over MC particles to analyze underlying event region + // Loop over antiprotons to analyze underlying event region for (const auto& protonVec : protonMomentum) { // Compute distance of particle from both perpendicular cone axes @@ -2187,14 +2045,8 @@ struct AntinucleiInJets { double deltaPhiUe2 = getDeltaPhi(protonVec.Phi(), ueAxis2.Phi()); double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - // Determine the maximum allowed distance from UE axes for particle selection - double maxConeRadius = coneRadius; - if (applyAreaCut) { - maxConeRadius = std::sqrt(maxNormalizedJetArea) * rJet; - } - // Reject tracks that lie outside the maxConeRadius from both UE axes - if (deltaRUe1 > maxConeRadius && deltaRUe2 > maxConeRadius) + if (deltaRUe1 > rJet && deltaRUe2 > rJet) continue; // Fill normalization histogram @@ -2202,10 +2054,12 @@ struct AntinucleiInJets { registryMC.fill(HIST("antiproton_deltay_deltaphi_ue"), protonVec.Eta() - ueAxis2.Eta(), getDeltaPhi(protonVec.Phi(), ueAxis2.Phi())); // Calculate weight - double weightUe(1.0); - if (applyReweighting && protonVec.Pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antiprotonsPerpCone->FindBin(protonVec.Pt()); - weightUe = antiprotonsPerpCone->GetBinContent(ipt); + double weightUe = genJetPtWeight; + if (applyReweighting) { + if (protonVec.Pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && protonVec.Pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(protonVec.Pt()); + weightUe *= antiprotonsPerpCone->GetBinContent(ipt); + } } // Fill histogram for antiprotons in the UE @@ -2221,6 +2075,35 @@ struct AntinucleiInJets { // Fill 2d (pt,eta) distribution of antiprotons registryMC.fill(HIST("antiproton_eta_pt_ue"), protonVec.Pt(), protonVec.Eta(), weightUe); } + + // Loop over antideuterons to analyze underlying event region + for (const auto& deuteronVec : deuteronMomentum) { + + // Compute distance of particle from both perpendicular cone axes + double deltaEtaUe1 = deuteronVec.Eta() - ueAxis1.Eta(); + double deltaPhiUe1 = getDeltaPhi(deuteronVec.Phi(), ueAxis1.Phi()); + double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); + double deltaEtaUe2 = deuteronVec.Eta() - ueAxis2.Eta(); + double deltaPhiUe2 = getDeltaPhi(deuteronVec.Phi(), ueAxis2.Phi()); + double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + + // Reject tracks that lie outside the maxConeRadius from both UE axes + if (deltaRUe1 > rJet && deltaRUe2 > rJet) + continue; + + // Calculate weight + double weightUeAntid = genJetPtWeight; + if (applyReweighting) { + if (deuteronVec.Pt() >= antideuteronsPerpCone->GetXaxis()->GetXmin() && deuteronVec.Pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antideuteronsPerpCone->FindBin(deuteronVec.Pt()); + weightUeAntid *= antideuteronsPerpCone->GetBinContent(ipt); + } + } + + // Fill histogram for antideuterons in the UE + registryMC.fill(HIST("antideuteron_gen_ue"), deuteronVec.Pt(), weightUeAntid); + registryMC.fill(HIST("antideuteron_gen_ue_noweight"), deuteronVec.Pt()); + } } if (isAtLeastOneJetSelected) { registryMC.fill(HIST("genEvents"), 3.5); @@ -2230,12 +2113,6 @@ struct AntinucleiInJets { registryMC.fill(HIST("genEvents"), 5.5); } } - - // Shrink large vectors - if (eventCounter % shrinkInterval == 0) { - std::vector().swap(fjParticles); - std::vector().swap(protonMomentum); - } } } PROCESS_SWITCH(AntinucleiInJets, processJetsMCgen, "process jets mc gen", false); @@ -2246,26 +2123,22 @@ struct AntinucleiInJets { // Define per-event containers std::vector fjParticles; std::vector antiprotonTrackIndex; + std::vector antideuteronTrackIndex; // Jet and area definitions fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - // Event counter - int eventCounter = 0; - // Loop over all reconstructed collisions for (const auto& collision : collisions) { // Configuration registryMC.fill(HIST("settingMC"), minJetPt.value, rJet.value); - // Increment event counter - eventCounter++; - // Clear containers at the start of the event loop fjParticles.clear(); antiprotonTrackIndex.clear(); + antideuteronTrackIndex.clear(); // Event counter: before event selection registryMC.fill(HIST("recEvents"), 0.5); @@ -2342,6 +2215,11 @@ struct AntinucleiInJets { } } + // Store track index for antideuteron tracks + if (passedTrackSelection(track) && track.sign() < 0 && mcparticle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { + antideuteronTrackIndex.emplace_back(id); + } + // Apply track selection for jet reconstruction if (!passedTrackSelectionForJetReconstruction(track)) continue; @@ -2375,16 +2253,27 @@ struct AntinucleiInJets { // Jet pt must be larger than threshold auto jetForSub = jet; fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Get reconstructed jet pt reweighting factor + double recoJetPtWeight(1.0); + if (applyReweighting) { + double jetPt = jetMinusBkg.pt(); + if (jetPt >= recoJetPtReweightingFactor->GetXaxis()->GetXmin() && jetPt < recoJetPtReweightingFactor->GetXaxis()->GetXmax()) { + int jetPtBin = recoJetPtReweightingFactor->FindBin(jetPt); + recoJetPtWeight = recoJetPtReweightingFactor->GetBinContent(jetPtBin); + } + } + + // Fill jet pt distribution + registryMC.fill(HIST("jetPtMCreconstructed"), jetMinusBkg.pt()); + if (isppRefAnalysis && (jet.pt() < minJetPt || jet.pt() > maxJetPt)) continue; if (!isppRefAnalysis && (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt)) continue; // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && (!isppRefAnalysis) && normalizedJetArea > maxNormalizedJetArea) - continue; - if (isppRefAnalysis && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) + if (applyAreaCut && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) continue; isAtLeastOneJetSelected = true; @@ -2392,7 +2281,6 @@ struct AntinucleiInJets { registryMC.fill(HIST("recJets"), 0.5); // Set up two perpendicular cone axes for underlying event estimation - double coneRadius = std::sqrt(jet.area() / PI); TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); @@ -2423,6 +2311,8 @@ struct AntinucleiInJets { // Define variables double nsigmaTPCPr = track.tpcNSigmaPr(); double nsigmaTOFPr = track.tofNSigmaPr(); + double nsigmaTPCDe = track.tpcNSigmaDe(); + double nsigmaTOFDe = track.tofNSigmaDe(); double pt = track.pt(); double dcaxy = track.dcaXY(); double dcaz = track.dcaZ(); @@ -2432,71 +2322,105 @@ struct AntinucleiInJets { registryMC.fill(HIST("antiproton_nsigma_tof_jet_mc"), pt, nsigmaTOFPr); } - // Antiproton selection based on the PDG - if (mcparticle.pdgCode() != PDG_t::kProtonBar) - continue; + // Particle selection based on PDG + bool isAntip = mcparticle.pdgCode() == PDG_t::kProtonBar; + bool isAntid = mcparticle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron; - // Fill DCA templates - if (std::fabs(dcaz) < maxDcaz) { - if (mcparticle.isPhysicalPrimary()) { - registryMC.fill(HIST("antiproton_prim_dca_jet"), pt, dcaxy); - } else { - registryMC.fill(HIST("antiproton_all_dca_jet"), pt, dcaxy); + // Fill spectra for antiprotons + if (isAntip) { + // Fill DCA templates + if (std::fabs(dcaz) < maxDcaz) { + if (mcparticle.isPhysicalPrimary()) { + registryMC.fill(HIST("antiproton_prim_dca_jet"), pt, dcaxy); + } else { + registryMC.fill(HIST("antiproton_all_dca_jet"), pt, dcaxy); + } } - } - - // Apply DCA selections - if (std::fabs(dcaxy) > maxDcaxy || std::fabs(dcaz) > maxDcaz) - continue; - - // nsigmaITS for antiprotons - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - // Particle identification using the ITS cluster size - bool passedItsPidProt(true); - if (applyItsPid && pt < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } + // Apply DCA selections + if (std::fabs(dcaxy) > maxDcaxy || std::fabs(dcaz) > maxDcaz) + continue; - // Fill inclusive antiproton spectrum - registryMC.fill(HIST("antiproton_incl_jet"), pt); + // nsigmaITS for antiprotons + double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - // Select physical primary antiprotons - if (!mcparticle.isPhysicalPrimary()) - continue; + // Particle identification using the ITS cluster size + bool passedItsPidProt(true); + if (applyItsPid && pt < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { + passedItsPidProt = false; + } - // Fill antiproton spectrum for physical primaries - registryMC.fill(HIST("antiproton_prim_jet"), pt); + // Fill inclusive antiproton spectrum + registryMC.fill(HIST("antiproton_incl_jet"), pt, recoJetPtWeight); - // Calculate weight - double weightJet(1.0); - if (applyReweighting && mcparticle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { - int ipt = antiprotonsInsideJets->FindBin(mcparticle.pt()); - weightJet = antiprotonsInsideJets->GetBinContent(ipt); - } + // Select physical primary antiprotons + if (!mcparticle.isPhysicalPrimary()) + continue; - // Fill histograms (TPC and TOF) only for selected candidates - if (passedItsPidProt && nsigmaTPCPr > minNsigmaTpc && nsigmaTPCPr < maxNsigmaTpc) { - registryMC.fill(HIST("antiproton_rec_tpc_jet"), pt, weightJet); + // Fill antiproton spectrum for physical primaries + registryMC.fill(HIST("antiproton_prim_jet"), pt, recoJetPtWeight); - // Fill histograms for reconstructed antiprotons for closure test - if (isPseudoData) { - registryMC.fill(HIST("antiproton_rec_tpc_jet_data"), pt); - } else { - registryMC.fill(HIST("antiproton_rec_tpc_jet_mc"), pt); + // Calculate weights + double weightJet = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && mcparticle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(mcparticle.pt()); + weightJet *= antiprotonsInsideJets->GetBinContent(ipt); + } } - if (track.hasTOF() && nsigmaTOFPr > minNsigmaTof && nsigmaTOFPr < maxNsigmaTof) { - registryMC.fill(HIST("antiproton_rec_tof_jet"), pt, weightJet); + // Fill histograms (TPC and TOF) only for selected candidates + if (passedItsPidProt && nsigmaTPCPr > minNsigmaTpc && nsigmaTPCPr < maxNsigmaTpc) { + registryMC.fill(HIST("antiproton_rec_tpc_jet"), pt, weightJet); // Fill histograms for reconstructed antiprotons for closure test if (isPseudoData) { - registryMC.fill(HIST("antiproton_rec_tof_jet_data"), pt); + registryMC.fill(HIST("antiproton_rec_tpc_jet_data"), pt); } else { - registryMC.fill(HIST("antiproton_rec_tof_jet_mc"), pt); + registryMC.fill(HIST("antiproton_rec_tpc_jet_mc"), pt); + } + + if (track.hasTOF() && nsigmaTOFPr > minNsigmaTof && nsigmaTOFPr < maxNsigmaTof) { + registryMC.fill(HIST("antiproton_rec_tof_jet"), pt, weightJet); + + // Fill histograms for reconstructed antiprotons for closure test + if (isPseudoData) { + registryMC.fill(HIST("antiproton_rec_tof_jet_data"), pt); + } else { + registryMC.fill(HIST("antiproton_rec_tof_jet_mc"), pt); + } } } - } + } // end of isAntip + + // Fill antideuteron spectra + if (isAntid) { + // Apply DCA selections + if (std::fabs(dcaxy) > maxDcaxy || std::fabs(dcaz) > maxDcaz) + continue; + + // Select physical primary antideuterons + if (!mcparticle.isPhysicalPrimary()) + continue; + + // Calculate weight + double weightJetAntid = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antideuteronsInsideJets->GetXaxis()->GetXmin() && mcparticle.pt() < antideuteronsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antideuteronsInsideJets->FindBin(mcparticle.pt()); + weightJetAntid *= antideuteronsInsideJets->GetBinContent(ipt); + } + } + + // Fill histograms (TPC and TOF) only for selected candidates + if (nsigmaTPCDe > minNsigmaTpc && nsigmaTPCDe < maxNsigmaTpc) { + registryMC.fill(HIST("antideuteron_rec_tpc_jet"), pt, weightJetAntid); + + if (track.hasTOF() && nsigmaTOFDe > minNsigmaTof && nsigmaTOFDe < maxNsigmaTof) { + registryMC.fill(HIST("antideuteron_rec_tof_jet"), pt, weightJetAntid); + } + } + } // end of isAntid } // Loop over tracks in the underlying event @@ -2538,14 +2462,8 @@ struct AntinucleiInJets { double deltaPhiUe2 = getDeltaPhi(track.phi(), ueAxis2.Phi()); double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - // Determine the maximum allowed distance from UE axes for particle selection - double maxConeRadius = coneRadius; - if (applyAreaCut) { - maxConeRadius = std::sqrt(maxNormalizedJetArea) * rJet; - } - // Reject tracks that lie outside the maxConeRadius from both UE axes - if (deltaRUe1 > maxConeRadius && deltaRUe2 > maxConeRadius) + if (deltaRUe1 > rJet && deltaRUe2 > rJet) continue; // Particle identification using the ITS cluster size @@ -2556,20 +2474,22 @@ struct AntinucleiInJets { } // Fill inclusive antiproton spectrum - registryMC.fill(HIST("antiproton_incl_ue"), pt); + registryMC.fill(HIST("antiproton_incl_ue"), pt, recoJetPtWeight); // Select physical primary antiprotons if (!mcparticle.isPhysicalPrimary()) continue; // Fill antiproton spectrum for physical primaries - registryMC.fill(HIST("antiproton_prim_ue"), pt); - - // Calculate weight - double weightUe(1.0); - if (applyReweighting && mcparticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { - int ipt = antiprotonsPerpCone->FindBin(mcparticle.pt()); - weightUe = antiprotonsPerpCone->GetBinContent(ipt); + registryMC.fill(HIST("antiproton_prim_ue"), pt, recoJetPtWeight); + + // Calculate weights + double weightUe = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && mcparticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(mcparticle.pt()); + weightUe *= antiprotonsPerpCone->GetBinContent(ipt); + } } // Fill histograms (TPC and TOF) only for selected candidates @@ -2595,42 +2515,93 @@ struct AntinucleiInJets { } } } - } - if (isAtLeastOneJetSelected) { - registryMC.fill(HIST("recEvents"), 9.5); - if (isPseudoData) { - registryMC.fill(HIST("recEvents"), 10.5); - } else { - registryMC.fill(HIST("recEvents"), 11.5); - } - } - // Shrink large vectors - if (eventCounter % shrinkInterval == 0) { - std::vector().swap(fjParticles); - std::vector().swap(antiprotonTrackIndex); - } - } - } - PROCESS_SWITCH(AntinucleiInJets, processJetsMCrec, "process jets MC rec", false); - - // Process real data with systematic variations of analysis parameters - void processSystData(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) - { - // Event counter: before event selection - registryData.fill(HIST("number_of_events_data_syst"), 0.5); + // Loop over tracks in the underlying event + for (auto const& index : antideuteronTrackIndex) { - // Apply standard event selection - if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) - return; + // retrieve track associated to index + auto const& track = mcTracksThisMcColl.iteratorAt(index); - // Event counter: after event selection - registryData.fill(HIST("number_of_events_data_syst"), 1.5); + // Get corresponding MC particle + if (!track.has_mcParticle()) + continue; + const auto mcparticle = track.mcParticle(); - // Reject events near the ITS Read-Out Frame border - if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - return; - registryData.fill(HIST("number_of_events_data_syst"), 2.5); + // Define variables + double nsigmaTPCDe = track.tpcNSigmaDe(); + double nsigmaTOFDe = track.tofNSigmaDe(); + double pt = track.pt(); + double dcaxy = track.dcaXY(); + double dcaz = track.dcaZ(); + + // Apply DCA selection + if (std::fabs(dcaxy) > maxDcaxy || std::fabs(dcaz) > maxDcaz) + continue; + + // Calculate the angular distance between the track and underlying event axes in eta-phi space + double deltaEtaUe1 = track.eta() - ueAxis1.Eta(); + double deltaPhiUe1 = getDeltaPhi(track.phi(), ueAxis1.Phi()); + double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); + double deltaEtaUe2 = track.eta() - ueAxis2.Eta(); + double deltaPhiUe2 = getDeltaPhi(track.phi(), ueAxis2.Phi()); + double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + + // Reject tracks that lie outside the maxConeRadius from both UE axes + if (deltaRUe1 > rJet && deltaRUe2 > rJet) + continue; + + // Select physical primary antideuterons + if (!mcparticle.isPhysicalPrimary()) + continue; + + // Calculate weight + double weightUeAntid = recoJetPtWeight; + if (applyReweighting) { + if (mcparticle.pt() >= antideuteronsPerpCone->GetXaxis()->GetXmin() && mcparticle.pt() < antideuteronsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antideuteronsPerpCone->FindBin(mcparticle.pt()); + weightUeAntid *= antideuteronsPerpCone->GetBinContent(ipt); + } + } + + // Fill histograms (TPC and TOF) only for selected candidates + if (nsigmaTPCDe > minNsigmaTpc && nsigmaTPCDe < maxNsigmaTpc) { + registryMC.fill(HIST("antideuteron_rec_tpc_ue"), pt, weightUeAntid); + + if (track.hasTOF() && nsigmaTOFDe > minNsigmaTof && nsigmaTOFDe < maxNsigmaTof) { + registryMC.fill(HIST("antideuteron_rec_tof_ue"), pt, weightUeAntid); + } + } + } + } + if (isAtLeastOneJetSelected) { + registryMC.fill(HIST("recEvents"), 9.5); + if (isPseudoData) { + registryMC.fill(HIST("recEvents"), 10.5); + } else { + registryMC.fill(HIST("recEvents"), 11.5); + } + } + } + } + PROCESS_SWITCH(AntinucleiInJets, processJetsMCrec, "process jets MC rec", false); + + // Process real data with systematic variations of analysis parameters + void processSystData(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) + { + // Event counter: before event selection + registryData.fill(HIST("number_of_events_data_syst"), 0.5); + + // Apply standard event selection + if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) + return; + + // Event counter: after event selection + registryData.fill(HIST("number_of_events_data_syst"), 1.5); + + // Reject events near the ITS Read-Out Frame border + if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) + return; + registryData.fill(HIST("number_of_events_data_syst"), 2.5); // Reject events at the Time Frame border if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) @@ -2764,7 +2735,7 @@ struct AntinucleiInJets { PROCESS_SWITCH(AntinucleiInJets, processSystData, "Process syst data", false); // Process MC with systematic variations of analysis parameters - void processSystEff(GenCollisionsMc const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) + void processSystEff(aod::McCollisions const& genCollisions, RecCollisionsMc const& recCollisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { // Cut settings static std::vector maxDcaxySyst = { @@ -2855,6 +2826,8 @@ struct AntinucleiInJets { case -o2::constants::physics::Pdg::kHelium3: registryMC.fill(HIST("antihelium3_gen_syst"), particle.pt()); break; + default: + break; } } } @@ -2974,790 +2947,225 @@ struct AntinucleiInJets { } PROCESS_SWITCH(AntinucleiInJets, processSystEff, "process syst mc", false); - // Process correlation - void processCorr(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) + // Process coalescence + void processCoalescence(aod::McCollisions const& collisions, aod::McParticles const& mcParticles) { - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter"), 0.5); + // Deuteron Mass and minimum pt + double massDeut = o2::constants::physics::MassDeuteron; + static constexpr double MinPtParticle = 0.1; - // Apply standard event selection - if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) - return; + // Define per-event particle containers + std::vector chargedParticles; + std::vector protons; + std::vector protonsFromHF; + std::vector neutrons; + std::vector fjParticles; - // Event counter: after event selection - registryCorr.fill(HIST("eventCounter"), 1.5); + // Jet and area definitions + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - // Reject events near the ITS Read-Out Frame border - if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter"), 2.5); + // Loop over all simulated collisions + for (const auto& collision : collisions) { - // Reject events at the Time Frame border - if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter"), 3.5); + // Clear containers at the start of the event loop + chargedParticles.clear(); + protons.clear(); + protonsFromHF.clear(); + neutrons.clear(); + fjParticles.clear(); - // Require at least one ITS-TPC matched track - if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return; - registryCorr.fill(HIST("eventCounter"), 4.5); + // Event counter: before event selection + registryMC.fill(HIST("genEventsCoalescence"), 0.5); - // Reject events with same-bunch pileup - if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return; - registryCorr.fill(HIST("eventCounter"), 5.5); + // Apply event selection: require vertex position to be within the allowed z range + if (std::fabs(collision.posZ()) > zVtx) + continue; - // Require consistent FT0 vs PV z-vertex - if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - return; - registryCorr.fill(HIST("eventCounter"), 6.5); + // Event counter: after event selection + registryMC.fill(HIST("genEventsCoalescence"), 1.5); - // Require TOF match for at least one vertex track - if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) - return; - registryCorr.fill(HIST("eventCounter"), 7.5); + // Get particles in this MC collision + const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); - // Assign event to a random subsample (0-19) - double sampleId = mRand.Integer(nSubsamples) + 0.5; + // Loop over MC particles + for (const auto& particle : mcParticlesThisMcColl) { - // Multiplicity percentile - const float multiplicity = collision.centFT0M(); + // Monte Carlo index + int mcId = particle.globalIndex(); - // Fill event counter vs centrality (full Event region) - registryCorr.fill(HIST("eventCounter_centrality_fullEvent"), multiplicity, sampleId); + // Store Protons + if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kProton) { + protons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // pt/A bins - std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; + // Store Protons from HF decays + if (isFromHF(particle, mcParticles) && std::abs(particle.pdgCode()) == PDG_t::kProton) { + protonsFromHF.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // Particle counters - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); + // Store Neutrons + if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kNeutron) { + neutrons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // Loop over reconstructed tracks - for (auto const& track : tracks) { + // Select physical primary particles or HF decay products + if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) + continue; - // Apply track selection - if (!passedTrackSelection(track)) - continue; + // Select particles within acceptance and skip protons (will be added after coalescence) + if (particle.eta() < minEta || particle.eta() > maxEta || particle.pt() < MinPtParticle || std::abs(particle.pdgCode()) == PDG_t::kProton) + continue; + chargedParticles.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); + } - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; + // Build deuterons + for (int ip = 0; ip < static_cast(protons.size()); ip++) { + auto& proton = protons[ip]; + if (proton.used) + continue; - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); + for (int in = 0; in < static_cast(neutrons.size()); in++) { + auto& neutron = neutrons[in]; + if (neutron.used) + continue; - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; + if (passDeuteronCoalescence(proton, neutron, coalescenceMomentum, mRand)) { + + // If proton and neutron are used by coalescence they are removed + neutron.used = true; + proton.used = true; + + int sign = (proton.pdgCode > 0) ? +1 : -1; + int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; + + double pxDeut = proton.px + neutron.px; + double pyDeut = proton.py + neutron.py; + double pzDeut = proton.pz + neutron.pz; + double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); + LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); + if (pd.Eta() < minEta || pd.Eta() > maxEta || pd.Pt() < MinPtParticle) + break; + + // Store Deuteron + chargedParticles.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, proton.mcIndex, false}); + break; + } + } } - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { + // Add unused protons + for (int ip = 0; ip < static_cast(protons.size()); ip++) { + auto& proton = protons[ip]; + if (proton.used) continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { + if (proton.eta() < minEta || proton.eta() > maxEta || proton.pt() < MinPtParticle) continue; - } - } else { - continue; + chargedParticles.push_back({proton.px, proton.py, proton.pz, proton.pdgCode, proton.mcIndex, proton.used}); } - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonFullEvent++; - } else if (track.sign() < 0) { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonFullEvent[ibin]++; - } + // Add protons from HF decays + for (int ip = 0; ip < static_cast(protonsFromHF.size()); ip++) { + auto& proton = protonsFromHF[ip]; + if (proton.used) + continue; + if (proton.eta() < minEta || proton.eta() > maxEta || proton.pt() < MinPtParticle) + continue; + chargedParticles.push_back({proton.px, proton.py, proton.pz, proton.pdgCode, proton.mcIndex, proton.used}); } - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); + // Reject empty events + if (chargedParticles.empty()) + continue; + registryMC.fill(HIST("genEventsCoalescence"), 2.5); + + for (const auto& part : chargedParticles) { + if (part.used) + continue; - if (track.sign() > 0) { - nTotDeuteronFullEvent++; - } else if (track.sign() < 0) { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronFullEvent[ibin]++; + // Fill histograms for Full Event + if (part.pdgCode == PDG_t::kProtonBar) { + registryMC.fill(HIST("antiproton_coal_fullEvent"), part.pt()); + } + if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { + registryMC.fill(HIST("antideuteron_coal_fullEvent"), part.pt()); } } - } - // Fill correlation histograms - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity, sampleId); - registryCorr.fill(HIST("rho_netP_netD_fullEvent"), netDeuteronFullEvent, netProtonFullEvent, sampleId); + // Fill particle array to feed to Fastjet + for (const auto& part : chargedParticles) { - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); + if (part.used) + continue; - registryCorr.fill(HIST("q1d_fullEvent"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity, sampleId); - registryCorr.fill(HIST("q1p_fullEvent"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity, sampleId); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_fullEvent"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity, sampleId); - registryCorr.fill(HIST("q1p_square_fullEvent"), ptAcenteri, ptAcenterj, (nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, sampleId); - registryCorr.fill(HIST("q1d_q1p_fullEvent"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, sampleId); + double energy = std::sqrt(part.px * part.px + part.py * part.py + part.pz * part.pz + MassPionCharged * MassPionCharged); + fastjet::PseudoJet fourMomentum(part.px, part.py, part.pz, energy); + fourMomentum.set_user_index(part.pdgCode); + fjParticles.emplace_back(fourMomentum); } - } - /* - // Loop over reconstructed tracks (refactoring: this part can be incorporated above) - int id(-1); - std::vector fjParticles; - for (auto const& track : tracks) { - id++; - if (!passedTrackSelectionForJetReconstruction(track)) + // Reject empty events + if (fjParticles.empty()) continue; + registryMC.fill(HIST("genEventsCoalescence"), 3.5); - // 4-momentum representation of a particle - fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(MassPionCharged)); - fourMomentum.set_user_index(id); - fjParticles.emplace_back(fourMomentum); - } - - // Reject empty events - if (fjParticles.empty()) - return; - registryCorr.fill(HIST("eventCounter"), 8.5); + // Cluster MC particles into jets using anti-kt algorithm + fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); + std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); + if (jets.empty()) + continue; + registryMC.fill(HIST("genEventsCoalescence"), 4.5); + auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); - // Cluster particles using the anti-kt algorithm - fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); - fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); - std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); - auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); + // Loop over clustered jets + bool isAtLeastOneJetSelected = false; + for (const auto& jet : jets) { - // Loop over reconstructed jets - bool isAtLeastOneJetSelected = false; - for (const auto& jet : jets) { + // Jet must be fully contained in the acceptance + if ((std::fabs(jet.eta()) + rJet) > (maxEta - deltaEtaEdge)) + continue; - // Jet must be fully contained in the acceptance - if ((std::fabs(jet.eta()) + rJet) > (maxEta - deltaEtaEdge)) - continue; + // Jet pt must be larger than threshold + auto jetForSub = jet; + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + if (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt) + continue; - // Jet pt must be larger than threshold - auto jetForSub = jet; - fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); - if (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt) - continue; - - // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && normalizedJetArea > maxNormalizedJetArea) - continue; - isAtLeastOneJetSelected = true; - - // Perpendicular cones - double coneRadius = std::sqrt(jet.area() / PI); - TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); - getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); - if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { - continue; - } - - // Get jet constituents - std::vector jetConstituents = jet.constituents(); - - // Particle counters - std::vector nAntiprotonJet(nBins, 0); - std::vector nAntideuteronJet(nBins, 0); - int nTotProtonJet(0); - int nTotDeuteronJet(0); - int nTotAntiprotonJet(0); - int nTotAntideuteronJet(0); - - // Loop over jet constituents - for (const auto& particle : jetConstituents) { - - // Get corresponding track and apply track selection criteria - auto const& track = tracks.iteratorAt(particle.user_index()); - if (!passedTrackSelection(track)) - continue; - - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; - - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; - } - - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { - continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { - continue; - } - } else { - continue; - } - - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonJet++; - } else if (track.sign() < 0) { - nTotAntiprotonJet++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonJet[ibin]++; - } - } - - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); - - if (track.sign() > 0) { - nTotDeuteronJet++; - } else if (track.sign() < 0) { - nTotAntideuteronJet++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronJet[ibin]++; - } - } - } // end of loop over constituents - - // Fill correlation histograms - int netProtonJet = nTotProtonJet - nTotAntiprotonJet; - int netDeuteronJet = nTotDeuteronJet - nTotAntideuteronJet; - registryCorr.fill(HIST("rho_jet"), nTotAntideuteronJet, nTotAntiprotonJet, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_jet"), netDeuteronJet, netProtonJet); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_jet"), nAntideuteronJet[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_jet"), nAntiprotonJet[i], ptAcenteri, multiplicity); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_jet"), ptAcenteri, ptAcenterj, nAntideuteronJet[i] * nAntideuteronJet[j], multiplicity); - registryCorr.fill(HIST("q1p_square_jet"), ptAcenteri, ptAcenterj, nAntiprotonJet[i] * nAntiprotonJet[j], multiplicity); - registryCorr.fill(HIST("q1d_q1p_jet"), ptAcenteri, ptAcenterj, nAntideuteronJet[i] * nAntiprotonJet[j], multiplicity); - } - } - - // Particle counters - std::vector nAntiprotonUE(nBins, 0); - std::vector nAntideuteronUE(nBins, 0); - int nTotProtonUE(0); - int nTotDeuteronUE(0); - int nTotAntiprotonUE(0); - int nTotAntideuteronUE(0); - - // Loop over tracks in the underlying event - for (auto const& track : tracks) { - - // Get corresponding track and apply track selection criteria - if (!passedTrackSelection(track)) - continue; - - // Apply DCA selections - if (std::fabs(track.dcaXY()) > maxDcaxy || std::fabs(track.dcaZ()) > maxDcaz) - continue; - - // Calculate the angular distance between the track and underlying event axes in eta-phi space - double deltaEtaUe1 = track.eta() - ueAxis1.Eta(); - double deltaPhiUe1 = getDeltaPhi(track.phi(), ueAxis1.Phi()); - double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); - double deltaEtaUe2 = track.eta() - ueAxis2.Eta(); - double deltaPhiUe2 = getDeltaPhi(track.phi(), ueAxis2.Phi()); - double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - - // Determine the maximum allowed distance from UE axes for particle selection - double maxConeRadius = coneRadius; - if (applyAreaCut) { - maxConeRadius = std::sqrt(maxNormalizedJetArea) * rJet; - } - - // Reject tracks that lie outside the maxConeRadius from both UE axes - if (deltaRUe1 > maxConeRadius && deltaRUe2 > maxConeRadius) - continue; - - // Particle identification using the ITS cluster size - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMin || nSigmaITSprot > nSigmaItsMax)) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMin || nSigmaITSdeut > nSigmaItsMax)) { - passedItsPidDeut = false; - } - - // Kinematic range selection - if (isProton(track) && passedItsPidProt) { - if (track.pt() < ptOverAbins[0] || track.pt() >= ptOverAbins[nBins]) { - continue; - } - } else if (isDeuteron(track) && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) { - continue; - } - } else { - continue; - } - - // (Anti)protons - if (isProton(track) && passedItsPidProt) { - if (track.sign() > 0) { - nTotProtonUE++; - } else if (track.sign() < 0) { - nTotAntiprotonUE++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonUE[ibin]++; - } - } - - // (Anti)deuterons - if (isDeuteron(track) && passedItsPidDeut) { - const double ptPerNucleon = 0.5 * track.pt(); - - if (track.sign() > 0) { - nTotDeuteronUE++; - } else if (track.sign() < 0) { - nTotAntideuteronUE++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronUE[ibin]++; - } - } - } - - // Fill correlation histograms - int netProtonUE = nTotProtonUE - nTotAntiprotonUE; - int netDeuteronUE = nTotDeuteronUE - nTotAntideuteronUE; - registryCorr.fill(HIST("rho_ue"), nTotAntideuteronUE, nTotAntiprotonUE, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_ue"), netDeuteronUE, netProtonUE); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_ue"), nAntideuteronUE[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_ue"), nAntiprotonUE[i], ptAcenteri, multiplicity); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_ue"), ptAcenteri, ptAcenterj, nAntideuteronUE[i] * nAntideuteronUE[j], multiplicity); - registryCorr.fill(HIST("q1p_square_ue"), ptAcenteri, ptAcenterj, nAntiprotonUE[i] * nAntiprotonUE[j], multiplicity); - registryCorr.fill(HIST("q1d_q1p_ue"), ptAcenteri, ptAcenterj, nAntideuteronUE[i] * nAntiprotonUE[j], multiplicity); - } - } - // Fill event counter vs centrality (ue region) - registryCorr.fill(HIST("eventCounter_centrality_ue"), multiplicity); - } - // Event counter: events with at least one jet selected - if (isAtLeastOneJetSelected) { - registryCorr.fill(HIST("eventCounter"), 9.5); - registryCorr.fill(HIST("eventCounter_centrality_jet"), multiplicity); - } - */ - } - PROCESS_SWITCH(AntinucleiInJets, processCorr, "Process Correlation analysis", false); - - // Process correlation analysis with systematic variations of analysis parameters - void processSystCorr(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) - { - // cut settings (from processSystData) - static std::vector maxDcaxySyst = { - 0.071, 0.060, 0.066, 0.031, 0.052, 0.078, 0.045, 0.064, 0.036, 0.074, - 0.079, 0.043, 0.067, 0.059, 0.032, 0.070, 0.048, 0.077, 0.062, 0.034, - 0.057, 0.055, 0.073, 0.038, 0.050, 0.075, 0.041, 0.061, 0.033, 0.069, - 0.035, 0.044, 0.076, 0.049, 0.037, 0.054, 0.072, 0.046, 0.058, 0.040, - 0.068, 0.042, 0.056, 0.039, 0.047, 0.065, 0.051, 0.053, 0.063, 0.030}; - - static std::vector maxDcazSyst = { - 0.064, 0.047, 0.032, 0.076, 0.039, 0.058, 0.043, 0.069, 0.050, 0.035, - 0.074, 0.061, 0.045, 0.033, 0.068, 0.055, 0.037, 0.071, 0.042, 0.053, - 0.077, 0.038, 0.065, 0.049, 0.036, 0.059, 0.044, 0.067, 0.041, 0.034, - 0.073, 0.052, 0.040, 0.063, 0.046, 0.031, 0.070, 0.054, 0.037, 0.062, - 0.048, 0.035, 0.075, 0.051, 0.039, 0.066, 0.043, 0.060, 0.032, 0.056}; - - static std::vector nSigmaItsMinSyst = { - -2.9, -2.8, -3.0, -3.4, -2.7, -3.3, -3.0, -3.1, -3.4, -3.1, - -3.0, -2.8, -3.2, -2.6, -2.7, -3.4, -2.9, -3.0, -3.0, -2.7, - -2.9, -3.3, -3.0, -3.1, -3.2, -3.0, -2.9, -2.7, -3.3, -3.0, - -2.8, -3.3, -2.7, -3.3, -2.8, -3.4, -2.8, -3.4, -2.9, -3.1, - -3.2, -2.6, -3.1, -2.9, -3.1, -2.8, -2.9, -3.3, -3.0, -2.8}; - - static std::vector nSigmaItsMaxSyst = { - 2.9, 2.8, 3.0, 3.4, 2.7, 3.3, 3.0, 3.1, 3.4, 3.1, 3.0, 2.8, 3.2, - 2.6, 2.7, 3.4, 2.9, 3.0, 3.0, 2.7, 2.9, 3.3, 3.0, 3.1, 3.2, 3.0, - 2.9, 2.7, 3.3, 3.0, 2.8, 3.3, 2.7, 3.3, 2.8, 3.4, 2.8, 3.4, 2.9, - 3.1, 3.2, 2.6, 3.1, 2.9, 3.1, 2.8, 2.9, 3.3, 3.0, 2.8}; - - static std::vector minNsigmaTpcSyst = { - -3.2, -2.9, -3.1, -2.9, -3.5, -2.6, -3.3, -3.0, -3.5, -2.7, - -3.0, -2.6, -3.3, -3.4, -2.8, -3.1, -2.6, -3.2, -3.1, -2.8, - -3.4, -2.7, -3.4, -2.9, -3.0, -2.5, -3.3, -2.8, -3.1, -2.7, - -3.4, -2.8, -3.3, -2.6, -3.1, -2.5, -3.4, -3.0, -3.2, -2.6, - -3.4, -2.8, -3.1, -2.6, -3.3, -2.7, -3.2, -2.7, -3.4, -2.9}; - - static std::vector maxNsigmaTpcSyst = { - 3.2, 2.9, 3.1, 2.9, 3.5, 2.6, 3.3, 3.0, 3.5, 2.7, 3.0, 2.6, 3.3, - 3.4, 2.8, 3.1, 2.6, 3.2, 3.1, 2.8, 3.4, 2.7, 3.4, 2.9, 3.0, 2.5, - 3.3, 2.8, 3.1, 2.7, 3.4, 2.8, 3.3, 2.6, 3.1, 2.5, 3.4, 3.0, 3.2, - 2.6, 3.4, 2.8, 3.1, 2.6, 3.3, 2.7, 3.2, 2.7, 3.4, 2.9}; - - static std::vector minNsigmaTofSyst = { - -3.2, -2.9, -3.1, -2.9, -3.5, -2.6, -3.3, -3.0, -3.5, -2.7, - -3.0, -2.6, -3.3, -3.4, -2.8, -3.1, -2.6, -3.2, -3.1, -2.8, - -3.4, -2.7, -3.4, -2.9, -3.0, -2.5, -3.3, -2.8, -3.1, -2.7, - -3.4, -2.8, -3.3, -2.6, -3.1, -2.5, -3.4, -3.0, -3.2, -2.6, - -3.4, -2.8, -3.1, -2.6, -3.3, -2.7, -3.2, -2.7, -3.4, -2.9}; - - static std::vector maxNsigmaTofSyst = { - 3.9, 3.6, 3.8, 3.2, 3.2, 3.5, 3.1, 3.8, 3.5, 3.4, 3.9, 3.8, 3.7, - 3.0, 3.6, 3.1, 3.7, 3.4, 4.0, 3.0, 3.7, 3.3, 3.9, 3.1, 3.3, 3.5, - 3.6, 3.2, 3.5, 3.3, 3.9, 3.0, 3.4, 3.2, 3.1, 3.9, 3.6, 3.1, 3.2, - 4.0, 3.1, 3.7, 3.6, 3.1, 3.3, 3.5, 3.3, 3.4, 3.1, 3.8}; - - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter_syst"), 0.5); - - // Apply standard event selection (Same as processCorr) - if (!collision.sel8() || std::fabs(collision.posZ()) > zVtx) - return; - - registryCorr.fill(HIST("eventCounter_syst"), 1.5); - - if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 2.5); - - if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 3.5); - - if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 4.5); - - if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 5.5); - - if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 6.5); - - if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) - return; - registryCorr.fill(HIST("eventCounter_syst"), 7.5); - - const float multiplicity = collision.centFT0M(); - - // Bins setup - static const std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; - - // Clamp systematic index range - const int systStart = std::max(0, static_cast(systIndexStart.value)); - const int systEnd = std::min(static_cast(nSyst), static_cast(systIndexEnd.value)); - - // Loop over systematic variations - for (int isyst = systStart; isyst < systEnd; isyst++) { - - // Fill event counter for this systematic - registryCorr.fill(HIST("eventCounter_centrality_fullEvent_syst"), multiplicity, static_cast(isyst)); - - // Particle counters for this specific cut setting - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); - - // Loop over tracks - for (auto const& track : tracks) { - - // Apply track selection (from processSystData) - if (!passedTrackSelectionSyst(track, isyst)) - continue; - - // Apply DCA selections (from processSystData) - if (std::fabs(track.dcaXY()) > maxDcaxySyst[isyst] || std::fabs(track.dcaZ()) > maxDcazSyst[isyst]) + // Apply area cut if required + if (applyAreaCut && (jet.area() < cfgAreaFrac * PI * rJet * rJet)) continue; + isAtLeastOneJetSelected = true; - // Particle identification using the ITS cluster size (vary also PID ITS) (from processSystData) - bool passedItsPidProt(true), passedItsPidDeut(true); - double nSigmaITSprot = static_cast(itsResponse.nSigmaITS(track)); - double nSigmaITSdeut = static_cast(itsResponse.nSigmaITS(track)); - - if (applyItsPid && track.pt() < ptMaxItsPidProt && (nSigmaITSprot < nSigmaItsMinSyst[isyst] || nSigmaITSprot > nSigmaItsMaxSyst[isyst])) { - passedItsPidProt = false; - } - if (applyItsPid && track.pt() < ptMaxItsPidDeut && (nSigmaITSdeut < nSigmaItsMinSyst[isyst] || nSigmaITSdeut > nSigmaItsMaxSyst[isyst])) { - passedItsPidDeut = false; - } + // Analyze jet constituents + std::vector jetConstituents = jet.constituents(); + for (const auto& particle : jetConstituents) { - // Check Identity (Proton/Deuteron) using TPC/TOF - bool isProt = isProtonSyst(track, minNsigmaTpcSyst[isyst], maxNsigmaTpcSyst[isyst], minNsigmaTofSyst[isyst], maxNsigmaTofSyst[isyst]); - bool isDeut = isDeuteronSyst(track, minNsigmaTpcSyst[isyst], maxNsigmaTpcSyst[isyst], minNsigmaTofSyst[isyst], maxNsigmaTofSyst[isyst]); - - // Kinematic range selection & counting - // (Anti)protons - if (isProt && passedItsPidProt) { - if (track.pt() >= ptOverAbins[0] && track.pt() < ptOverAbins[nBins]) { - if (track.sign() > 0) { - nTotProtonFullEvent++; - } else if (track.sign() < 0) { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, track.pt()); - nAntiprotonFullEvent[ibin]++; + // Calculate weight for antiprotons + double wAntipJet = 1.0; + if (applyReweighting) { + if (particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(particle.pt()); + wAntipJet = antiprotonsInsideJets->GetBinContent(ipt); } } - } - // (Anti)deuterons - if (isDeut && passedItsPidDeut) { - double ptPerNucleon = 0.5 * track.pt(); - if (ptPerNucleon >= ptOverAbins[0] && ptPerNucleon < ptOverAbins[nBins]) { - if (track.sign() > 0) { - nTotDeuteronFullEvent++; - } else if (track.sign() < 0) { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - nAntideuteronFullEvent[ibin]++; + // Calculate weight for antideuterons + double wAntidJet = 1.0; + if (applyReweighting) { + if (0.5 * particle.pt() >= antiprotonsInsideJets->GetXaxis()->GetXmin() && 0.5 * particle.pt() < antiprotonsInsideJets->GetXaxis()->GetXmax()) { + int ipt = antiprotonsInsideJets->FindBin(0.5 * particle.pt()); + wAntidJet = antiprotonsInsideJets->GetBinContent(ipt) * antiprotonsInsideJets->GetBinContent(ipt); } } - } - } - - // Fill Correlation Histograms for systematic variations - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent_syst"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("rho_netP_netD_fullEvent_syst"), netDeuteronFullEvent, netProtonFullEvent, static_cast(isyst)); - - // Fill efficiency histograms for systematic variations - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_fullEvent_syst"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1p_fullEvent_syst"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity, static_cast(isyst)); - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - registryCorr.fill(HIST("q1d_square_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1p_square_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, static_cast(isyst)); - registryCorr.fill(HIST("q1d_q1p_fullEvent_syst"), ptAcenteri, ptAcenterj, (nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity, static_cast(isyst)); - } - } - } - } - PROCESS_SWITCH(AntinucleiInJets, processSystCorr, "Process Correlation systematics", false); - - // Process coalescence - void processCoalescence(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) - { - // Deuteron Mass and minimum pt - double massDeut = o2::constants::physics::MassDeuteron; - static constexpr double MinPtParticle = 0.1; - - // Define per-event particle containers - std::vector chargedParticles; - std::vector protons; - std::vector neutrons; - std::vector fjParticles; - - // Jet and area definitions - fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); - fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); - - // Loop over all simulated collisions - for (const auto& collision : collisions) { - - // Clear containers at the start of the event loop - chargedParticles.clear(); - protons.clear(); - neutrons.clear(); - fjParticles.clear(); - // Event counter: before event selection - registryMC.fill(HIST("genEventsCoalescence"), 0.5); - - // Apply event selection: require vertex position to be within the allowed z range - if (std::fabs(collision.posZ()) > zVtx) - continue; - - // Event counter: after event selection - registryMC.fill(HIST("genEventsCoalescence"), 1.5); - - // Get particles in this MC collision - const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); - - // Loop over MC particles - for (const auto& particle : mcParticlesThisMcColl) { - - // Monte Carlo index - int mcId = particle.globalIndex(); - - // Store Protons - if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kProton) { - protons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); - } - - // Store Neutrons - if (particle.isPhysicalPrimary() && std::abs(particle.pdgCode()) == PDG_t::kNeutron) { - neutrons.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); - } - - // Select physical primary particles or HF decay products - if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) - continue; - - // Select particles within acceptance - if (particle.eta() < minEta || particle.eta() > maxEta || particle.pt() < MinPtParticle) - continue; - chargedParticles.push_back({particle.px(), particle.py(), particle.pz(), particle.pdgCode(), mcId, false}); - } - - // Reject empty events - if (chargedParticles.empty()) - continue; - registryMC.fill(HIST("genEventsCoalescence"), 2.5); - - // Build deuterons - for (int ip = 0; ip < static_cast(protons.size()); ip++) { - auto& proton = protons[ip]; - if (proton.used) - continue; - - for (int in = 0; in < static_cast(neutrons.size()); in++) { - auto& neutron = neutrons[in]; - if (neutron.used) - continue; - - if (passDeuteronCoalescence(proton, neutron, coalescenceMomentum, mRand)) { - - int sign = (proton.pdgCode > 0) ? +1 : -1; - int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; - - double pxDeut = proton.px + neutron.px; - double pyDeut = proton.py + neutron.py; - double pzDeut = proton.pz + neutron.pz; - double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); - LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); - if (pd.Eta() < minEta || pd.Eta() > maxEta || pd.Pt() < MinPtParticle) - continue; - - // Store Deuteron - chargedParticles.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, proton.mcIndex, false}); - - neutron.used = true; - proton.used = true; - break; - } - } - } - - for (const auto& part : chargedParticles) { - if (part.used) - continue; - - // Fill histograms for Full Event - if (part.pdgCode == PDG_t::kProtonBar) { - registryMC.fill(HIST("antiproton_coal_fullEvent"), part.pt()); - } - if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { - registryMC.fill(HIST("antideuteron_coal_fullEvent"), part.pt()); - } - } - - // Fill particle array to feed to Fastjet - for (const auto& part : chargedParticles) { - - if (part.used) - continue; - - double energy = std::sqrt(part.px * part.px + part.py * part.py + part.pz * part.pz + MassPionCharged * MassPionCharged); - fastjet::PseudoJet fourMomentum(part.px, part.py, part.pz, energy); - fourMomentum.set_user_index(part.pdgCode); - fjParticles.emplace_back(fourMomentum); - } - - // Reject empty events - if (fjParticles.empty()) - continue; - registryMC.fill(HIST("genEventsCoalescence"), 3.5); - - // Cluster MC particles into jets using anti-kt algorithm - fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); - std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); - if (jets.empty()) - continue; - registryMC.fill(HIST("genEventsCoalescence"), 4.5); - auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); - - // Loop over clustered jets - bool isAtLeastOneJetSelected = false; - for (const auto& jet : jets) { - - // Jet must be fully contained in the acceptance - if ((std::fabs(jet.eta()) + rJet) > (maxEta - deltaEtaEdge)) - continue; - - // Jet pt must be larger than threshold - auto jetForSub = jet; - fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); - if (jetMinusBkg.pt() < minJetPt || jetMinusBkg.pt() > maxJetPt) - continue; - - // Apply area cut if required - double normalizedJetArea = jet.area() / (PI * rJet * rJet); - if (applyAreaCut && normalizedJetArea > maxNormalizedJetArea) - continue; - isAtLeastOneJetSelected = true; - - // Analyze jet constituents - std::vector jetConstituents = jet.constituents(); - for (const auto& particle : jetConstituents) { if (particle.user_index() == PDG_t::kProtonBar) - registryMC.fill(HIST("antiproton_coal_jet"), particle.pt()); + registryMC.fill(HIST("antiproton_coal_jet"), particle.pt(), wAntipJet); if (particle.user_index() == -o2::constants::physics::Pdg::kDeuteron) - registryMC.fill(HIST("antideuteron_coal_jet"), particle.pt()); + registryMC.fill(HIST("antideuteron_coal_jet"), particle.pt(), wAntidJet); } // Set up two perpendicular cone axes for underlying event estimation TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - double coneRadius = std::sqrt(jet.area() / PI); TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { @@ -3779,21 +3187,33 @@ struct AntinucleiInJets { double deltaPhiUe2 = getDeltaPhi(chParticle.phi(), ueAxis2.Phi()); double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - // Determine the maximum allowed distance from UE axes for particle selection - double maxConeRadius = coneRadius; - if (applyAreaCut) { - maxConeRadius = std::sqrt(maxNormalizedJetArea) * rJet; - } - // Reject tracks that lie outside the maxConeRadius from both UE axes - if (deltaRUe1 > maxConeRadius && deltaRUe2 > maxConeRadius) + if (deltaRUe1 > rJet && deltaRUe2 > rJet) continue; + // Calculate weight for antiprotons + double wAntipUE = 1.0; + if (applyReweighting) { + if (chParticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && chParticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(chParticle.pt()); + wAntipUE = antiprotonsPerpCone->GetBinContent(ipt); + } + } + + // Calculate weight for antideuterons + double wAntidUE = 1.0; + if (applyReweighting) { + if (0.5 * chParticle.pt() >= antiprotonsPerpCone->GetXaxis()->GetXmin() && 0.5 * chParticle.pt() < antiprotonsPerpCone->GetXaxis()->GetXmax()) { + int ipt = antiprotonsPerpCone->FindBin(0.5 * chParticle.pt()); + wAntidUE = antiprotonsPerpCone->GetBinContent(ipt) * antiprotonsPerpCone->GetBinContent(ipt); + } + } + // Fill histograms for UE if (chParticle.pdgCode == PDG_t::kProtonBar) - registryMC.fill(HIST("antiproton_coal_ue"), chParticle.pt()); + registryMC.fill(HIST("antiproton_coal_ue"), chParticle.pt(), wAntipUE); if (chParticle.pdgCode == -o2::constants::physics::Pdg::kDeuteron) - registryMC.fill(HIST("antideuteron_coal_ue"), chParticle.pt()); + registryMC.fill(HIST("antideuteron_coal_ue"), chParticle.pt(), wAntidUE); } } if (isAtLeastOneJetSelected) { @@ -3803,209 +3223,6 @@ struct AntinucleiInJets { } PROCESS_SWITCH(AntinucleiInJets, processCoalescence, "process coalescence", false); - // process Coalescence and Correlation Analysis - void processCoalescenceCorr(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) - { - // Deuteron Mass and minimum pt - double massDeut = o2::constants::physics::MassDeuteron; - static constexpr double MinPtPerNucleon = 0.1; // Cut on pt/A - - // Containers for candidates (before coalescence) - std::vector protonCandidates; - std::vector neutronCandidates; - - // Final containers for analysis (after coalescence) - std::vector finalProtons; - std::vector finalDeuterons; - - // pt/A bins - std::vector ptOverAbins = {0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; - const int nBins = ptOverAbins.size() - 1; - - // Loop over all simulated collisions - for (const auto& collision : collisions) { - - // Clear containers - protonCandidates.clear(); - neutronCandidates.clear(); - finalProtons.clear(); - finalDeuterons.clear(); - - // Event counter: before event selection - registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 0.5); - registryMC.fill(HIST("genEventsCoalescenceCorr"), 0.5); - - // Apply event selection: require vertex position to be within the allowed z range - if (std::fabs(collision.posZ()) > zVtx) - continue; - - // Event counter: after event selection - registryCorr.fill(HIST("eventCounter_CoalescenceCorr"), 1.5); - registryMC.fill(HIST("genEventsCoalescenceCorr"), 1.5); - - // Multiplicity percentile - const float multiplicity = collision.centFT0M(); - - // Fill event counter vs centrality - registryCorr.fill(HIST("eventCounter_centrality_fullEvent_CoalescenceCorr"), multiplicity); - - // Get particles in this MC collision - const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); - - // Loop over MC particles - for (const auto& particle : mcParticlesThisMcColl) { - - // Monte Carlo index - int mcId = particle.globalIndex(); - int pdg = particle.pdgCode(); - int absPdg = std::abs(pdg); - - // Store Protons - if (particle.isPhysicalPrimary()) { - if (absPdg == PDG_t::kProton) { - protonCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); - } else if (absPdg == PDG_t::kNeutron) { // Store Neutrons - neutronCandidates.push_back({particle.px(), particle.py(), particle.pz(), pdg, mcId, false}); - } - } - } - - // Reject empty events - if (protonCandidates.empty() && neutronCandidates.empty()) - continue; - - registryMC.fill(HIST("genEventsCoalescenceCorr"), 2.5); - - // Build deuterons - for (size_t iP = 0; iP < protonCandidates.size(); ++iP) { - if (protonCandidates[iP].used) - continue; - - for (size_t iN = 0; iN < neutronCandidates.size(); ++iN) { - if (neutronCandidates[iN].used) - continue; - - // Physics consistency check - if (protonCandidates[iP].pdgCode * neutronCandidates[iN].pdgCode < 0) - continue; - - if (passDeuteronCoalescence(protonCandidates[iP], neutronCandidates[iN], coalescenceMomentum, mRand)) { - neutronCandidates[iN].used = true; - protonCandidates[iP].used = true; - - int sign = (protonCandidates[iP].pdgCode > 0) ? +1 : -1; - int deuteronPdg = sign * o2::constants::physics::Pdg::kDeuteron; - - double pxDeut = protonCandidates[iP].px + neutronCandidates[iN].px; - double pyDeut = protonCandidates[iP].py + neutronCandidates[iN].py; - double pzDeut = protonCandidates[iP].pz + neutronCandidates[iN].pz; - double energyDeut = std::sqrt(pxDeut * pxDeut + pyDeut * pyDeut + pzDeut * pzDeut + massDeut * massDeut); - LorentzVector pd(pxDeut, pyDeut, pzDeut, energyDeut); - if (pd.Eta() >= minEta && pd.Eta() <= maxEta && (0.5 * pd.Pt()) >= MinPtPerNucleon) { - // Store Deuteron - finalDeuterons.push_back({pxDeut, pyDeut, pzDeut, deuteronPdg, protonCandidates[iP].mcIndex, false}); - } - - break; - } - } - } - - // Add unused protons to final vectors - for (const auto& proton : protonCandidates) { - if (!proton.used) { - finalProtons.push_back(proton); - } - } - - // Correlation Analysis - std::vector nAntiprotonFullEvent(nBins, 0); - std::vector nAntideuteronFullEvent(nBins, 0); - int nTotProtonFullEvent(0); - int nTotDeuteronFullEvent(0); - int nTotAntiprotonFullEvent(0); - int nTotAntideuteronFullEvent(0); - - // Loop over final protons - for (const auto& part : finalProtons) { - double pt = std::hypot(part.px, part.py); - - if (part.eta() < minEta || part.eta() > maxEta) - continue; - - // Standard histograms for antiprotons - if (part.pdgCode == PDG_t::kProtonBar) { - registryMC.fill(HIST("antiproton_fullEvent_CoalescenceCorr"), pt); - } - - // Kinematic selection and Multiplicity counting - if (pt < ptOverAbins[0] || pt >= ptOverAbins[nBins]) - continue; - - if (part.pdgCode > 0) { - nTotProtonFullEvent++; - } else { - nTotAntiprotonFullEvent++; - int ibin = findBin(ptOverAbins, pt); - if (ibin >= 0 && ibin < nBins) - nAntiprotonFullEvent[ibin]++; - } - } - - // Loop over final deuterons - for (const auto& part : finalDeuterons) { - double pt = std::hypot(part.px, part.py); - double ptPerNucleon = 0.5 * pt; - - // Apply detector acceptance cuts (to match real data) - if (part.eta() < minEta || part.eta() > maxEta) - continue; - - // Standard histograms for antideuterons - if (part.pdgCode == -o2::constants::physics::Pdg::kDeuteron) { - registryMC.fill(HIST("antideuteron_fullEvent_CoalescenceCorr"), pt); - } - - // Kinematic selection and Multiplicity counting - if (ptPerNucleon < ptOverAbins[0] || ptPerNucleon >= ptOverAbins[nBins]) - continue; - - if (part.pdgCode > 0) { - nTotDeuteronFullEvent++; - } else { - nTotAntideuteronFullEvent++; - int ibin = findBin(ptOverAbins, ptPerNucleon); - if (ibin >= 0 && ibin < nBins) - nAntideuteronFullEvent[ibin]++; - } - } - - // Fill correlation histograms - int netProtonFullEvent = nTotProtonFullEvent - nTotAntiprotonFullEvent; - int netDeuteronFullEvent = nTotDeuteronFullEvent - nTotAntideuteronFullEvent; - - registryCorr.fill(HIST("rho_fullEvent_CoalescenceCorr"), nTotAntideuteronFullEvent, nTotAntiprotonFullEvent, multiplicity); - registryCorr.fill(HIST("rho_netP_netD_fullEvent_CoalescenceCorr"), netDeuteronFullEvent, netProtonFullEvent); - - // Fill efficiency histograms - for (int i = 0; i < nBins; i++) { - double ptAcenteri = 0.5 * (ptOverAbins[i] + ptOverAbins[i + 1]); - - registryCorr.fill(HIST("q1d_fullEvent_CoalescenceCorr"), nAntideuteronFullEvent[i], ptAcenteri, multiplicity); - registryCorr.fill(HIST("q1p_fullEvent_CoalescenceCorr"), nAntiprotonFullEvent[i], ptAcenteri, multiplicity); - - for (int j = 0; j < nBins; j++) { - double ptAcenterj = 0.5 * (ptOverAbins[j] + ptOverAbins[j + 1]); - - registryCorr.fill(HIST("q1d_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntideuteronFullEvent[i] * nAntideuteronFullEvent[j]), multiplicity); - registryCorr.fill(HIST("q1p_square_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntiprotonFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); - registryCorr.fill(HIST("q1d_q1p_fullEvent_CoalescenceCorr"), ptAcenteri, ptAcenterj, static_cast(nAntideuteronFullEvent[i] * nAntiprotonFullEvent[j]), multiplicity); - } - } - } - } - PROCESS_SWITCH(AntinucleiInJets, processCoalescenceCorr, "process coalescence correlation", false); - // Jet Pt resolution void processJetPtResolution(RecCollisionsMc const& collisions, AntiNucleiTracksMc const& mcTracks, aod::McParticles const& mcParticles) { @@ -4109,7 +3326,7 @@ struct AntinucleiInJets { continue; // Apply area cut if required - if (applyAreaCut && (jetRec.area() / (PI * rJet * rJet)) > maxNormalizedJetArea) + if (applyAreaCut && (jetRec.area() < cfgAreaFrac * PI * rJet * rJet)) continue; // Clear jet-pair container @@ -4122,7 +3339,7 @@ struct AntinucleiInJets { continue; // Apply area cut if required - if (applyAreaCut && (jetGen.area() / (PI * rJet * rJet)) > maxNormalizedJetArea) + if (applyAreaCut && (jetGen.area() < cfgAreaFrac * PI * rJet * rJet)) continue; double deltaEta = jetGen.eta() - jetRec.eta(); diff --git a/PWGLF/Tasks/Nuspex/antinucleiInJetsCDF.cxx b/PWGLF/Tasks/Nuspex/antinucleiInJetsCDF.cxx new file mode 100644 index 00000000000..04d33e85973 --- /dev/null +++ b/PWGLF/Tasks/Nuspex/antinucleiInJetsCDF.cxx @@ -0,0 +1,736 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file antinucleiInJetsCDF.cxx +/// +/// \brief Analysis task for antinuclei in jets using the CDF technique +/// \author Alberto Caliva (alberto.caliva@cern.ch) +/// \since August 22, 2024 + +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include + +using namespace std; +using namespace o2; +using namespace o2::soa; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; +using namespace o2::constants::math; + +// Define convenient aliases for commonly used table joins +using SelectedCollisions = soa::Join; +using RecCollisionsMc = soa::Join; +using GenCollisionsMc = soa::Join; +using AntiNucleiTracks = soa::Join; +using AntiNucleiTracksMc = soa::Join; + +// Lightweight particle container +struct ReducedParticle { + double px; + double py; + double pz; + double y; + double nsigmaTPC; + double nsigmaTOF; + bool hasTOF; + bool isPhysPrim; + + // Transverse Momentum + double pt() const + { + return std::sqrt(px * px + py * py); + } +}; + +struct AntinucleiInJetsCDF { + + // Histogram registries for real-data and MC analyses + HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // Event selection criteria + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "Reject events near the ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "Reject events near the TF border"}; + Configurable requireVtxITSTPC{"requireVtxITSTPC", true, "Require at least one ITS-TPC matched track"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "Reject events with same-bunch pileup collisions"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "Require consistent FT0 vs PV z-vertex"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "Require vtx track matched to TOF"}; + + // Global Parameters + Configurable ptLeadingMin{"ptLeadingMin", 5.0, "pt Leading Min"}; + Configurable zVtx{"zVtx", 10.0, "Maximum zVertex"}; + + // Track quality, kinematic, and PID selection parameters + Configurable requirePvContributor{"requirePvContributor", false, "require that the track is a PV contributor"}; + Configurable minItsNclusters{"minItsNclusters", 5, "minimum number of ITS clusters"}; + Configurable minTpcNcrossedRows{"minTpcNcrossedRows", 100, "minimum number of TPC crossed pad rows"}; + Configurable minChiSquareTpc{"minChiSquareTpc", 0.0, "minimum TPC chi^2/Ncls"}; + Configurable maxChiSquareTpc{"maxChiSquareTpc", 4.0, "maximum TPC chi^2/Ncls"}; + Configurable maxChiSquareIts{"maxChiSquareIts", 36.0, "maximum ITS chi^2/Ncls"}; + Configurable minPt{"minPt", 0.3, "minimum pt of the tracks"}; + Configurable minEta{"minEta", -0.8, "minimum eta"}; + Configurable maxEta{"maxEta", +0.8, "maximum eta"}; + Configurable minY{"minY", -0.5, "minimum rapidity"}; + Configurable maxY{"maxY", +0.5, "maximum rapidity"}; + Configurable maxDcaxy{"maxDcaxy", 0.05, "Maximum DCAxy"}; + Configurable maxDcaz{"maxDcaz", 0.05, "Maximum DCAz"}; + Configurable minNsigmaTpc{"minNsigmaTpc", -3.0, "Minimum nsigma TPC"}; + Configurable maxNsigmaTpc{"maxNsigmaTpc", +3.0, "Maximum nsigma TPC"}; + Configurable minNsigmaTof{"minNsigmaTof", -3.0, "Minimum nsigma TOF"}; + Configurable maxNsigmaTof{"maxNsigmaTof", +3.5, "Maximum nsigma TOF"}; + + void init(InitContext const&) + { + // Binning + double min = 0.0; + double max = 6.0; + int nbins = 120; + + // Process real data + if (doprocessData) { + + // Event counter + registryData.add("number_of_events_data", "number of events in data", HistType::kTH1F, {{20, 0, 20, "counter"}}); + + // Antiproton histograms + registryData.add("antip_toward_tpc", "antip_toward_tpc", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antip_toward_tof", "antip_toward_tof", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("antip_transv_tpc", "antip_transv_tpc", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antip_transv_tof", "antip_transv_tof", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); + + // Antideuteron histograms + registryData.add("antid_toward_tpc", "antid_toward_tpc", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antid_toward_tof", "antid_toward_tof", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("antid_transv_tpc", "antid_transv_tpc", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antid_transv_tof", "antid_transv_tof", HistType::kTH2F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); + } + + // Process generated MC + if (doprocessGenMC) { + + // Event counter + registryMC.add("genEvents", "number of generated events in mc", HistType::kTH1F, {{10, 0, 10, "counter"}}); + + // Histograms for generated particles in the full event + registryMC.add("antip_gen", "antip_gen", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_gen", "antid_gen", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + + // Histograms for generated particles in different azimuthal regions + registryMC.add("antip_toward_gen", "antip_toward_gen", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_transv_gen", "antip_transv_gen", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_toward_gen", "antid_toward_gen", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_transv_gen", "antid_transv_gen", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + } + + // Process reconstructed MC + if (doprocessRecMC) { + + // Event counter + registryMC.add("recEvents", "number of reconstructed events in mc", HistType::kTH1F, {{10, 0, 10, "counter"}}); + + // Histograms for reconstructed particles in the full event + registryMC.add("antip_rec_tpc", "antip_rec_tpc", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_rec_tof", "antip_rec_tof", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_rec_tpc", "antid_rec_tpc", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_rec_tof", "antid_rec_tof", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + + // Histograms for reconstructed particles in different azimuthal regions + registryMC.add("antip_toward_rec_tpc", "antip_toward_rec_tpc", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_toward_rec_tof", "antip_toward_rec_tof", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_transv_rec_tpc", "antip_transv_rec_tpc", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_transv_rec_tof", "antip_transv_rec_tof", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_toward_rec_tpc", "antid_toward_rec_tpc", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_toward_rec_tof", "antid_toward_rec_tof", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_transv_rec_tpc", "antid_transv_rec_tpc", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antid_transv_rec_tof", "antid_transv_rec_tof", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + + // Histograms for secondary antiprotons + registryMC.add("antip_toward_prim", "antip_toward_prim", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_toward_all", "antip_toward_all", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_transv_prim", "antip_transv_prim", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + registryMC.add("antip_transv_all", "antip_transv_all", HistType::kTH1F, {{nbins, min, max, "#it{p}_{T} (GeV/#it{c})"}}); + } + } + + // ITS hit + template + bool hasITSHit(const TrackIts& track, int layer) + { + int ibit = layer - 1; + return (track.itsClusterMap() & (1 << ibit)); + } + + // Single-Track Selection for leading track + template + bool passedTrackSelectionLeadingTrack(const JetTrack& track) + { + static constexpr int MinTpcCr = 70; + static constexpr double MaxChi2Tpc = 4.0; + static constexpr double MaxChi2Its = 36.0; + static constexpr double MaxPseudorap = 0.8; + static constexpr double MinPtTrack = 0.3; + static constexpr double DcaxyMaxTrackPar0 = 0.0105; + static constexpr double DcaxyMaxTrackPar1 = 0.035; + static constexpr double DcaxyMaxTrackPar2 = 1.1; + static constexpr double DcazMaxTrack = 2.0; + + if (!track.hasITS()) + return false; + if ((!hasITSHit(track, 1)) && (!hasITSHit(track, 2)) && (!hasITSHit(track, 3))) + return false; + if (!track.hasTPC()) + return false; + if (track.tpcNClsCrossedRows() < MinTpcCr) + return false; + if (track.tpcChi2NCl() > MaxChi2Tpc) + return false; + if (track.itsChi2NCl() > MaxChi2Its) + return false; + if (std::abs(track.eta()) > MaxPseudorap) + return false; + if (track.pt() < MinPtTrack) + return false; + if (std::abs(track.dcaXY()) > (DcaxyMaxTrackPar0 + DcaxyMaxTrackPar1 / std::pow(track.pt(), DcaxyMaxTrackPar2))) + return false; + if (std::abs(track.dcaZ()) > DcazMaxTrack) + return false; + return true; + } + + // Single-track selection for antinuclei + template + bool passedTrackSelection(const AntinucleusTrack& track) + { + if (track.sign() >= 0) + return false; + if (requirePvContributor && !(track.isPVContributor())) + return false; + if (!track.hasITS()) + return false; + if ((!hasITSHit(track, 1)) && (!hasITSHit(track, 2)) && (!hasITSHit(track, 3))) + return false; + if (track.itsNCls() < minItsNclusters) + return false; + if (!track.hasTPC()) + return false; + if (track.tpcNClsCrossedRows() < minTpcNcrossedRows) + return false; + if (track.tpcChi2NCl() < minChiSquareTpc) + return false; + if (track.tpcChi2NCl() > maxChiSquareTpc) + return false; + if (track.itsChi2NCl() > maxChiSquareIts) + return false; + if (track.eta() < minEta || track.eta() > maxEta) + return false; + if (track.pt() < minPt) + return false; + + return true; + } + + // Rapidity + double getRapidity(double px, double py, double pz, double mass) + { + const double energy = std::sqrt(px * px + py * py + pz * pz + mass * mass); + ROOT::Math::PxPyPzEVector lorentzVect(px, py, pz, energy); + return lorentzVect.Rapidity(); + } + + // Check if a particle is in the toward region with respect to the leading particle + bool isParticleInTowardRegion(const TVector3& particle, const TVector3& leadingParticle) + { + const double deltaPhi = std::remainder(particle.Phi() - leadingParticle.Phi(), o2::constants::math::TwoPI); + return std::abs(deltaPhi) < o2::constants::math::PIThird; + } + + // Check if a particle is in the transverse region with respect to the leading particle + bool isParticleInTransverseRegion(const TVector3& particle, const TVector3& leadingParticle) + { + static constexpr double Two = 2.0; + const double deltaPhi = std::remainder(particle.Phi() - leadingParticle.Phi(), o2::constants::math::TwoPI); + return (std::abs(deltaPhi) >= o2::constants::math::PIThird && std::abs(deltaPhi) < Two * o2::constants::math::PIThird); + } + + // Check if particle is a physical primary or a decay product of a heavy-flavor hadron + bool isPhysicalPrimaryOrFromHF(aod::McParticle const& particle, aod::McParticles const& mcParticles) + { + // Keep only pi, K, p, d, e, mu + int pdg = std::abs(particle.pdgCode()); + if (!(pdg == PDG_t::kPiPlus || pdg == PDG_t::kKPlus || pdg == PDG_t::kProton || pdg == o2::constants::physics::Pdg::kDeuteron || pdg == PDG_t::kElectron || pdg == PDG_t::kMuonMinus)) + return false; + + // Constants for identifying heavy-flavor (charm and bottom) content from PDG codes + static constexpr int CharmQuark = 4; + static constexpr int BottomQuark = 5; + static constexpr int Hundreds = 100; + static constexpr int Thousands = 1000; + + // Check if particle is from heavy-flavor decay + bool fromHF = false; + if (particle.has_mothers()) { + auto mother = mcParticles.iteratorAt(particle.mothersIds()[0]); + int motherPdg = std::abs(mother.pdgCode()); + fromHF = (motherPdg / Hundreds == CharmQuark || motherPdg / Hundreds == BottomQuark || motherPdg / Thousands == CharmQuark || motherPdg / Thousands == BottomQuark); + } + + // Select only physical primary particles or from heavy-flavor + return (particle.isPhysicalPrimary() || fromHF); + } + + // Process data + void processData(SelectedCollisions::iterator const& collision, AntiNucleiTracks const& tracks) + { + // Event counter: before event selection + registryData.fill(HIST("number_of_events_data"), 0.5); + + // Standard event selection + if (!collision.sel8() || std::abs(collision.posZ()) > zVtx) + return; + registryData.fill(HIST("number_of_events_data"), 1.5); + + // Reject events near the ITS Read-Out Frame border + if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) + return; + registryData.fill(HIST("number_of_events_data"), 2.5); + + // Reject events at the time frame border + if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) + return; + registryData.fill(HIST("number_of_events_data"), 3.5); + + // Require at least one ITS-TPC matched track + if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) + return; + registryData.fill(HIST("number_of_events_data"), 4.5); + + // Reject events with same-bunch pileup + if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + return; + registryData.fill(HIST("number_of_events_data"), 5.5); + + // Require consistent FT0 vs PV z-vertex + if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + return; + registryData.fill(HIST("number_of_events_data"), 6.5); + + // Require TOF match for at least one vertex track + if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) + return; + registryData.fill(HIST("number_of_events_data"), 7.5); + + // Leading track pt and momentum + double ptMax(0); + TVector3 leadingTrackVec; + + // Loop over reconstructed tracks + for (auto const& track : tracks) { + if (passedTrackSelectionLeadingTrack(track) && track.pt() > ptMax) { + leadingTrackVec.SetXYZ(track.px(), track.py(), track.pz()); + ptMax = track.pt(); + } + } + + // Event counter: skip events with pt < pt_leading_min + if (ptMax < ptLeadingMin) + return; + registryData.fill(HIST("number_of_events_data"), 8.5); + + // Loop over reconstructed tracks + for (auto const& track : tracks) { + + // Apply track and DCA selections + if (!passedTrackSelection(track)) + continue; + if (std::abs(track.dcaXY()) > maxDcaxy) + continue; + if (std::abs(track.dcaZ()) > maxDcaz) + continue; + + // Variables + const double nsigmaTPCPr = track.tpcNSigmaPr(); + const double nsigmaTOFPr = track.tofNSigmaPr(); + const double nsigmaTPCDe = track.tpcNSigmaDe(); + const double nsigmaTOFDe = track.tofNSigmaDe(); + const double yp = getRapidity(track.px(), track.py(), track.pz(), o2::constants::physics::MassProton); + const double yd = getRapidity(track.px(), track.py(), track.pz(), o2::constants::physics::MassDeuteron); + const TVector3 trackVec(track.px(), track.py(), track.pz()); + + // Find azimuthal region + const bool isToward = isParticleInTowardRegion(trackVec, leadingTrackVec); + const bool isTransv = isParticleInTransverseRegion(trackVec, leadingTrackVec); + + // Antiprotons in the toward region + if (isToward && yp >= minY && yp <= maxY) { + registryData.fill(HIST("antip_toward_tpc"), track.pt(), nsigmaTPCPr); + if (track.hasTOF() && nsigmaTPCPr >= minNsigmaTpc && nsigmaTPCPr <= maxNsigmaTpc) + registryData.fill(HIST("antip_toward_tof"), track.pt(), nsigmaTOFPr); + } + + // Antiprotons in the transverse region + if (isTransv && yp >= minY && yp <= maxY) { + registryData.fill(HIST("antip_transv_tpc"), track.pt(), nsigmaTPCPr); + if (track.hasTOF() && nsigmaTPCPr >= minNsigmaTpc && nsigmaTPCPr <= maxNsigmaTpc) + registryData.fill(HIST("antip_transv_tof"), track.pt(), nsigmaTOFPr); + } + + // Antideuterons in the toward region + if (isToward && yd >= minY && yd <= maxY) { + registryData.fill(HIST("antid_toward_tpc"), track.pt(), nsigmaTPCDe); + if (track.hasTOF() && nsigmaTPCDe >= minNsigmaTpc && nsigmaTPCDe <= maxNsigmaTpc) + registryData.fill(HIST("antid_toward_tof"), track.pt(), nsigmaTOFDe); + } + + // Antideuterons in the transverse region + if (isTransv && yd >= minY && yd <= maxY) { + registryData.fill(HIST("antid_transv_tpc"), track.pt(), nsigmaTPCDe); + if (track.hasTOF() && nsigmaTPCDe >= minNsigmaTpc && nsigmaTPCDe <= maxNsigmaTpc) + registryData.fill(HIST("antid_transv_tof"), track.pt(), nsigmaTOFDe); + } + } + } + PROCESS_SWITCH(AntinucleiInJetsCDF, processData, "Process real-data analysis", true); + + // Define preslice to group MC particles by their associated MC collision + Preslice mcParticlesPerMcCollision = o2::aod::mcparticle::mcCollisionId; + + // Process generated MC + void processGenMC(GenCollisionsMc const& collisions, aod::McParticles const& mcParticles) + { + // Define per-event particle containers + std::vector antip; + std::vector antid; + + // Loop over generated collisions + for (const auto& collision : collisions) { + + // Clear containers at the start of the event loop + antip.clear(); + antid.clear(); + + // Event counter: before event selection + registryMC.fill(HIST("genEvents"), 0.5); + + // Apply event selection: require vertex position to be within the allowed z range + if (std::abs(collision.posZ()) > zVtx) + continue; + registryMC.fill(HIST("genEvents"), 1.5); + + // Get particles in this MC collision + const auto mcParticlesThisMcColl = mcParticles.sliceBy(mcParticlesPerMcCollision, collision.globalIndex()); + + // Leading particle momentum + double ptMax(0); + TVector3 leadingParticleVec; + + // Loop over MC particles + for (const auto& particle : mcParticlesThisMcColl) { + + // Select physical primary particles or HF decay products + if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) + continue; + + // Select particles within acceptance + if (particle.eta() < minEta || particle.eta() > maxEta) + continue; + + // Store leading particle momentum + if (particle.pt() > ptMax) { + ptMax = particle.pt(); + leadingParticleVec.SetXYZ(particle.px(), particle.py(), particle.pz()); + } + + // Physical primary seletion + if (!particle.isPhysicalPrimary()) + continue; + + // Rapidity selection + if (particle.y() < minY || particle.y() > maxY) + continue; + + // Momentum vector + TVector3 pVec(particle.px(), particle.py(), particle.pz()); + + // Fill histogram for generated antiprotons and store momentum + if (particle.pdgCode() == PDG_t::kProtonBar) { + registryMC.fill(HIST("antip_gen"), particle.pt()); + antip.emplace_back(pVec); + } + + // Fill histogram for generated antideuterons and store momentum + if (particle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { + registryMC.fill(HIST("antid_gen"), particle.pt()); + antid.emplace_back(pVec); + } + } + + // Event Counter: skip events with pt < pt_leading_min + if (ptMax < ptLeadingMin) + continue; + registryMC.fill(HIST("genEvents"), 2.5); + + // Loop over antiprotons + for (const auto& antipVec : antip) { + if (isParticleInTowardRegion(antipVec, leadingParticleVec)) + registryMC.fill(HIST("antip_toward_gen"), antipVec.Pt()); + if (isParticleInTransverseRegion(antipVec, leadingParticleVec)) + registryMC.fill(HIST("antip_transv_gen"), antipVec.Pt()); + } + + // Loop over antideuterons + for (const auto& antidVec : antid) { + if (isParticleInTowardRegion(antidVec, leadingParticleVec)) + registryMC.fill(HIST("antid_toward_gen"), antidVec.Pt()); + if (isParticleInTransverseRegion(antidVec, leadingParticleVec)) + registryMC.fill(HIST("antid_transv_gen"), antidVec.Pt()); + } + } + } + PROCESS_SWITCH(AntinucleiInJetsCDF, processGenMC, "Process gen MC", false); + + // Define preslice to group reconstructed MC tracks by collision + Preslice mcTracksPerMcCollision = o2::aod::track::collisionId; + + // Process reconstructed MC + void processRecMC(RecCollisionsMc const& collisions, AntiNucleiTracksMc const& mcTracks, McParticles const&) + { + // Define per-event particle containers + std::vector antip; + std::vector antid; + + // Loop over all reconstructed collisions + for (const auto& collision : collisions) { + + // Clear containers at the start of the event loop + antip.clear(); + antid.clear(); + + // Event counter: before event selection + registryMC.fill(HIST("recEvents"), 0.5); + + // Standard event selection + if (!collision.sel8() || std::abs(collision.posZ()) > zVtx) + continue; + registryMC.fill(HIST("recEvents"), 1.5); + + // Reject events near the ITS read-out frame border + if (rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) + continue; + registryMC.fill(HIST("recEvents"), 2.5); + + // Reject events at the time frame border + if (rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) + continue; + registryMC.fill(HIST("recEvents"), 3.5); + + // Require at least one ITS-TPC matched track + if (requireVtxITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) + continue; + registryMC.fill(HIST("recEvents"), 4.5); + + // Reject events with same-bunch pileup + if (rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + continue; + registryMC.fill(HIST("recEvents"), 5.5); + + // Require consistent FT0 vs PV z-vertex + if (requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + continue; + registryMC.fill(HIST("recEvents"), 6.5); + + // Require TOF match for at least one vertex track + if (requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) + continue; + registryMC.fill(HIST("recEvents"), 7.5); + + // Get tracks in this MC collision + const auto mcTracksThisMcColl = mcTracks.sliceBy(mcTracksPerMcCollision, collision.globalIndex()); + + // Leading-track momentum + TVector3 leadingTrackVec; + double ptMax(0); + + // Loop over reconstructed tracks + for (auto const& track : mcTracksThisMcColl) { + + // Select leading particle + if (passedTrackSelectionLeadingTrack(track) && track.pt() > ptMax) { + leadingTrackVec.SetXYZ(track.px(), track.py(), track.pz()); + ptMax = track.pt(); + } + + // Get corresponding MC particle + if (!track.has_mcParticle()) + continue; + const auto mcparticle = track.mcParticle(); + + // Track and DCA selections + if (!passedTrackSelection(track)) + continue; + if (std::abs(track.dcaXY()) > maxDcaxy) + continue; + if (std::abs(track.dcaZ()) > maxDcaz) + continue; + + // Variables + const double nsigmaTPCPr = track.tpcNSigmaPr(); + const double nsigmaTOFPr = track.tofNSigmaPr(); + const double nsigmaTPCDe = track.tpcNSigmaDe(); + const double nsigmaTOFDe = track.tofNSigmaDe(); + const bool hasTOF = track.hasTOF(); + const bool isAntip = mcparticle.pdgCode() == PDG_t::kProtonBar; + const bool isAntid = mcparticle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron; + const bool isPhysPrim = mcparticle.isPhysicalPrimary(); + const double yp = getRapidity(track.px(), track.py(), track.pz(), o2::constants::physics::MassProton); + const double yd = getRapidity(track.px(), track.py(), track.pz(), o2::constants::physics::MassDeuteron); + + // Fill antiproton and antideuteron vectors + if (isAntip) + antip.push_back({track.px(), track.py(), track.pz(), yp, nsigmaTPCPr, nsigmaTOFPr, hasTOF, isPhysPrim}); + if (isAntid) + antid.push_back({track.px(), track.py(), track.pz(), yd, nsigmaTPCDe, nsigmaTOFDe, hasTOF, isPhysPrim}); + + // Selection of physical primary + if (!isPhysPrim) + continue; + + // Fill antiprotons for full-event efficiency + if (isAntip && yp >= minY && yp <= maxY && nsigmaTPCPr >= minNsigmaTpc && nsigmaTPCPr <= maxNsigmaTpc) { + registryMC.fill(HIST("antip_rec_tpc"), track.pt()); + if (hasTOF && nsigmaTOFPr >= minNsigmaTof && nsigmaTOFPr <= maxNsigmaTof) { + registryMC.fill(HIST("antip_rec_tof"), track.pt()); + } + } + + // Fill antideuterons for full-event efficiency + if (isAntid && yd >= minY && yd <= maxY && nsigmaTPCDe >= minNsigmaTpc && nsigmaTPCDe <= maxNsigmaTpc) { + registryMC.fill(HIST("antid_rec_tpc"), track.pt()); + if (hasTOF && nsigmaTOFDe >= minNsigmaTof && nsigmaTOFDe <= maxNsigmaTof) { + registryMC.fill(HIST("antid_rec_tof"), track.pt()); + } + } + } + + // Event Counter: skip events with pt < pt_leading_min + if (ptMax < ptLeadingMin) + continue; + registryMC.fill(HIST("recEvents"), 8.5); + + // Loop over antiprotons + for (const auto& antipVec : antip) { + + TVector3 trackVec(antipVec.px, antipVec.py, antipVec.pz); + const bool isToward = isParticleInTowardRegion(trackVec, leadingTrackVec); + const bool isTransv = isParticleInTransverseRegion(trackVec, leadingTrackVec); + + // Antiprotons in Toward Region + if (isToward && antipVec.y >= minY && antipVec.y <= maxY) { + registryMC.fill(HIST("antip_toward_all"), antipVec.pt()); + if (antipVec.isPhysPrim) + registryMC.fill(HIST("antip_toward_prim"), antipVec.pt()); + } + + // Antiprotons in transverse Region + if (isTransv && antipVec.y >= minY && antipVec.y <= maxY) { + registryMC.fill(HIST("antip_transv_all"), antipVec.pt()); + if (antipVec.isPhysPrim) + registryMC.fill(HIST("antip_transv_prim"), antipVec.pt()); + } + + // Select physical primary + if (!antipVec.isPhysPrim) + continue; + + // Select antiprotons inside rapidity window + if (antipVec.y < minY || antipVec.y > maxY) + continue; + + // Antiprotons in Toward Region + if (isToward && antipVec.nsigmaTPC >= minNsigmaTpc && antipVec.nsigmaTPC <= maxNsigmaTpc) { + registryMC.fill(HIST("antip_toward_rec_tpc"), antipVec.pt()); + if (antipVec.hasTOF && antipVec.nsigmaTOF >= minNsigmaTof && antipVec.nsigmaTOF <= maxNsigmaTof) + registryMC.fill(HIST("antip_toward_rec_tof"), antipVec.pt()); + } + + // Antiprotons in transverse Region + if (isTransv && antipVec.nsigmaTPC >= minNsigmaTpc && antipVec.nsigmaTPC <= maxNsigmaTpc) { + registryMC.fill(HIST("antip_transv_rec_tpc"), antipVec.pt()); + if (antipVec.hasTOF && antipVec.nsigmaTOF >= minNsigmaTof && antipVec.nsigmaTOF <= maxNsigmaTof) + registryMC.fill(HIST("antip_transv_rec_tof"), antipVec.pt()); + } + } + + // Loop over antideuterons + for (const auto& antidVec : antid) { + + TVector3 trackVec(antidVec.px, antidVec.py, antidVec.pz); + const bool isToward = isParticleInTowardRegion(trackVec, leadingTrackVec); + const bool isTransv = isParticleInTransverseRegion(trackVec, leadingTrackVec); + + // Select physical primary + if (!antidVec.isPhysPrim) + continue; + + // Select antideuterons inside rapiditry window + if (antidVec.y < minY || antidVec.y > maxY) + continue; + + // Antideuterons in Toward Region + if (isToward && antidVec.nsigmaTPC >= minNsigmaTpc && antidVec.nsigmaTPC <= maxNsigmaTpc) { + registryMC.fill(HIST("antid_toward_rec_tpc"), antidVec.pt()); + if (antidVec.hasTOF && antidVec.nsigmaTOF >= minNsigmaTof && antidVec.nsigmaTOF <= maxNsigmaTof) + registryMC.fill(HIST("antid_toward_rec_tof"), antidVec.pt()); + } + + // Antideuterons in transverse Region + if (isTransv && antidVec.nsigmaTPC >= minNsigmaTpc && antidVec.nsigmaTPC <= maxNsigmaTpc) { + registryMC.fill(HIST("antid_transv_rec_tpc"), antidVec.pt()); + if (antidVec.hasTOF && antidVec.nsigmaTOF >= minNsigmaTof && antidVec.nsigmaTOF <= maxNsigmaTof) + registryMC.fill(HIST("antid_transv_rec_tof"), antidVec.pt()); + } + } + } + } + PROCESS_SWITCH(AntinucleiInJetsCDF, processRecMC, "Process reconstructed MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Nuspex/antitritonAnalysis.cxx b/PWGLF/Tasks/Nuspex/antitritonAnalysis.cxx new file mode 100644 index 00000000000..63bdfebde78 --- /dev/null +++ b/PWGLF/Tasks/Nuspex/antitritonAnalysis.cxx @@ -0,0 +1,483 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file antitritonAnalysis.cxx +/// \brief Analysis of (anti)tritons, based on PWGCF/Femto3D/DataModel/singletrackselector.h +/// \author Nikita Gladin + +#include "PWGCF/Femto3D/Core/femto3dPairTask.h" +#include "PWGCF/Femto3D/DataModel/PIDutils.h" +#include "PWGCF/Femto3D/DataModel/singletrackselector.h" + +#include "Common/DataModel/PIDResponseITS.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::soa; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct AntitritonAnalysis { + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry registryTrue{"registryTrue", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Service pdgDB{}; + float particleMass = 0.f; + + Configurable isMC{"isMC", false, ""}; + + Configurable removeSameBunchPileup{"removeSameBunchPileup", false, ""}; + Configurable requestGoodZvtxFT0vsPV{"requestGoodZvtxFT0vsPV", false, ""}; + Configurable requestVertexITSTPC{"requestVertexITSTPC", false, ""}; + Configurable requestVertexTOFmatched{"requestVertexTOFmatched", 0, "0 -> no selection; 1 -> vertex is matched to TOF or TRD; 2 -> matched to both;"}; + Configurable requestNoCollInTimeRangeStandard{"requestNoCollInTimeRangeStandard", false, ""}; + Configurable requestIsGoodITSLayersAll{"requestIsGoodITSLayersAll", false, "cut time intervals with dead ITS staves"}; + Configurable> irCut{"irCut", std::pair{0.f, 100.f}, "[min., max.] IR range to keep events within"}; + Configurable> occupancyCut{"occupancyCut", std::pair{0, 10000}, "[min., max.] occupancy range to keep events within"}; + + Configurable vertexZ{"vertexZ", 10.0, "abs vertexZ value limit"}; + Configurable minP{"minP", 0.0, "lower mometum limit"}; + Configurable maxP{"maxP", 100.0, "upper mometum limit"}; + Configurable eta{"eta", 100.0, "abs eta value limit"}; + Configurable y{"y", 100.0, "abs y value limit"}; + Configurable> dcaXY{"dcaXY", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaXY value limit; formula: [0] + [1]*pT^[2]"}; + Configurable> dcaZ{"dcaZ", std::vector{0.3f, 0.0f, 0.0f}, "abs dcaZ value limit; formula: [0] + [1]*pT^[2]"}; + Configurable minTpcNClsFound{"minTpcNClsFound", 0, "minimum allowed number of TPC clasters"}; + Configurable tpcChi2NCl{"tpcChi2NCl", 100.0, "upper limit for chi2 value of a fit over TPC clasters"}; + Configurable tpcCrossedRowsOverFindableCls{"tpcCrossedRowsOverFindableCls", 0, "lower limit of TPC CrossedRows/FindableCls value"}; + Configurable maxTpcFractionSharedCls{"maxTpcFractionSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; + Configurable minItsNCls{"minItsNCls", 0, "minimum allowed number of ITS clasters for a track"}; + Configurable itsChi2NCl{"itsChi2NCl", 100.0, "upper limit for chi2 value of a fit over ITS clasters for a track"}; + Configurable particlePDG{"particlePDG", o2::constants::physics::Pdg::kTriton, "PDG code of a particle to perform PID for"}; + Configurable> tpcNSigma{"tpcNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TPC before the TOF is used"}; + Configurable> itsNSigma{"itsNSigma", std::vector{-10.0f, 10.0f}, "Nsigma range in ITS to use along with TPC"}; + Configurable pidTrshld{"pidTrshld", 0.0, "value of momentum from which the PID is done with TOF (before that only TPC is used)"}; + Configurable massCuttrshld{"massCuttrshld", 0.0, "value of momentum from which the mass cut is done with TOF"}; + Configurable> itsNsigNL{"itsNsigNL", std::vector{-100.0f, 100.0f}, "Nsigma p-dependent cut in ITS ; formula: [0] + [1]*pT"}; + + Configurable> tofNSigma{"tofNSigma", std::vector{-4.0f, 4.0f}, "Nsigma range in TOF"}; + Configurable> tpcNSigmaResidual{"tpcNSigmaResidual", std::vector{-5.0f, 5.0f}, "residual TPC Nsigma cut to use with the TOF"}; + + Configurable> particlePDGtoReject{"particlePDGtoReject", std::vector{PDG_t::kProton}, "PDG codes of perticles that will be rejected with TPC"}; + Configurable> rejectWithinNsigmaTOF{"rejectWithinNsigmaTOF", std::vector{-0.0f, 0.0f}, "TOF rejection Nsigma range for particles specified with PDG to be rejected"}; + Configurable> rejectWithinNsigmaTPC{"rejectWithinNsigmaTPC", std::vector{-0.0f, 0.0f}, "TPC rejection Nsigma range for particles specified with PDG to be rejected"}; + + Configurable> centCut{"centCut", std::pair{0.f, 100.f}, "[min., max.] centrality range to keep tracks within"}; + + Configurable> dcaBinning{"dcaBinning", std::vector{501, 0.5f, 1}, "setup for variable binning (geometric progression is used): 1st (int) -- N_bins (must be odd, otherwise will be increased by 1); 2nd (float) -- abs value of the edge of axises in histos (-2nd, +2nd); 3d (int) -- desired ratio between w_bin at the edges and at 0;"}; + Configurable> tofMass{"tofMass", std::vector{3.0f, 3.0f}, "TOF Mass bounds"}; + + std::pair> itsCuts; + std::pair> tpcCuts; + std::pair> tofCuts; + + Filter pFilter = o2::aod::singletrackselector::p > minP&& o2::aod::singletrackselector::p < maxP; + Filter etaFilter = nabs(o2::aod::singletrackselector::eta) < eta; + Filter tpcTrkFilter = o2::aod::singletrackselector::tpcNClsFound >= minTpcNClsFound && o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedTpcChi2NCl) < tpcChi2NCl && o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedTpcCrossedRowsOverFindableCls) > tpcCrossedRowsOverFindableCls; + Filter itsTrkFilter = o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedItsChi2NCl) < itsChi2NCl; + Filter vertexFilter = nabs(o2::aod::singletrackselector::posZ) < vertexZ; + + struct HistSet { + std::shared_ptr eta; + std::shared_ptr etaToY; + std::shared_ptr y; + std::shared_ptr phi; + std::shared_ptr p; + std::shared_ptr pt; + std::shared_ptr dcaxyToP; + std::shared_ptr dcaxyToPt; + std::shared_ptr dcazToP; + std::shared_ptr dcazToPt; + std::shared_ptr tpcClusters; + std::shared_ptr itsClusters; + std::shared_ptr tpcSignal; + std::shared_ptr tofSignal; + std::shared_ptr massTOF; + std::shared_ptr nsigmaITS; + std::shared_ptr nsigmaTPC; + std::shared_ptr nsigmaTOF; + std::shared_ptr tofOverTPC; + std::shared_ptr innerParamToP; + std::shared_ptr ptGenPtRec; + std::shared_ptr dcaxyDcazToPt; + std::shared_ptr dcaxyToDcaz; + std::shared_ptr origin; + }; + + static inline const std::vector stageDirs = { + "init", + "No_Cuts", + "ITS_Cuts", + "TPC_Cuts", + "TOF_Cuts", + "ITSTPC_Cut", + "PID_Cuts", + "TOFmass_Cut", + "Rejection_Cut", + "TPCLinear_Cut", + }; + + // signDirs[0] = "t" for sign > 0 (triton), signDirs[1] = "at" for sign < 0 (antitriton). + static inline const std::array signDirs = {"t", "at"}; + + std::vector> hSets; + std::vector> hSetsTrue; + + HistSet makeHistSet(HistogramRegistry& reg, const std::string& dir, int pdgForPid = 0) + { + HistSet h; + + h.eta = reg.add((dir + "/eta").c_str(), "#eta;#eta;Entries", kTH1F, {{200, -2.5, 2.5}}); + h.y = reg.add((dir + "/y").c_str(), "y;y;Entries", kTH1F, {{200, -2.5, 2.5}}); + h.etaToY = reg.add((dir + "/eta_to_y").c_str(), "#eta;y;Entries", kTH2F, {{200, -2.5, 2.5}, {200, -2.5, 2.5}}); + h.phi = reg.add((dir + "/phi").c_str(), "#phi;#phi;Entries", kTH1F, {{200, 0., o2::constants::math::TwoPI}}); + h.p = reg.add((dir + "/p").c_str(), "p;p (GeV/#it{c});Entries", kTH1F, {{100, 0., 5.}}); + h.pt = reg.add((dir + "/pt").c_str(), "p_{T};p_{T} (GeV/#it{c});Entries", kTH1F, {{100, 0., 5.}}); + h.dcaxyToP = reg.add((dir + "/dcaxy_to_p").c_str(), "DCA_{xy} vs p;p (GeV/#it{c});DCA_{xy} (cm)", kTH2F, {{100, 0., 5.0}, {501, -0.5, 0.5}}); + h.dcaxyToPt = reg.add((dir + "/dcaxy_to_pt").c_str(), "DCA_{xy} vs p_{T};p_{T} (GeV/#it{c});DCA_{xy} (cm)", kTH2F, {{100, 0., 5.0}, {501, -0.5, 0.5}}); + h.dcazToP = reg.add((dir + "/dcaz_to_p").c_str(), "DCA_{z} vs p;p (GeV/#it{c});DCA_{z} (cm)", kTH2F, {{100, 0., 5.0}, {501, -0.5, 0.5}}); + h.dcazToPt = reg.add((dir + "/dcaz_to_pt").c_str(), "DCA_{z} vs p_{T};p_{T} (GeV/#it{c});DCA_{z} (cm)", kTH2F, {{100, 0., 5.0}, {501, -0.5, 0.5}}); + h.tpcClusters = reg.add((dir + "/TPCClusters").c_str(), "TPC Clusters;N_{TPC clusters};Entries", kTH1F, {{163, -0.5, 162.5}}); + h.itsClusters = reg.add((dir + "/ITSClusters").c_str(), "ITS Clusters;N_{ITS clusters};Entries", kTH1F, {{10, -0.5, 9.5}}); + h.tpcSignal = reg.add((dir + "/TPCSignal").c_str(), "TPC Signal;#it{p}_{inner} (GeV/#it{c});dE/dx in TPC (arbitrary units)", kTH2F, {{200, 0., 5.0}, {1000, 0., 1000.0}}); + h.tofSignal = reg.add((dir + "/TOFSignal").c_str(), "TOF Signal;#it{p} (GeV/#it{c});#beta", kTH2F, {{200, 0., 5.0}, {100, 0., 1.5}}); + h.massTOF = reg.add((dir + "/mTOF").c_str(), "m_{TOF};p_{T} (GeV/#it{c});m_{TOF}(GeV/#it{c}^{2})", kTH2F, {{120, 0., 6.}, {1000, -1.5, 5.0}}); + h.innerParamToP = reg.add((dir + "/ip_to_p").c_str(), ";#it{p} (GeV/#it{c});ip_to_p", kTH2F, {{100, 0., 5.0}, {100, 0.0, 2.0}}); + h.ptGenPtRec = reg.add((dir + "/ptgenptrec").c_str(), ";#it{p_{T, rec}} (GeV/#it{c});#it{p_{T, rec}} - #it{p_{T, gen}}(GeV/#it{c})", kTH2F, {{100, 0., 5.0}, {100, -2.5, 2.5}}); + h.dcaxyDcazToPt = reg.add( + (dir + "/dcaxy_dcaz_to_pt").c_str(), + "DCA_{xy} vs DCA_{z} vs p_{T};DCA_{xy} (cm);DCA_{z} (cm);p_{T} (GeV/#it{c})", + kTH3F, + { + {101, -0.5, 0.5}, // x axis: DCA_xy + {101, -0.5, 0.5}, // y axis: DCA_z + {20, 0., 5.0} // z axis: pT + }); + h.dcaxyToDcaz = reg.add( + (dir + "/dcaxy_to_dcaz").c_str(), + "DCA_{z} vs DCA_{xy};DCA_{xy} (cm);DCA_{z} (cm);Counts", + kTH2F, + { + {501, -0.5, 0.5}, // x axis: DCAxy + {501, -0.5, 0.5} // y axis: DCAz + }); + h.origin = reg.add((dir + "/origin").c_str(), "MC origin;origin (-1: unmatched, 0: primary, 1: weak decay, 2: material);Entries", kTH1F, {{4, -1.5, 2.5}}); + + if (pdgForPid != 0) { + h.tofOverTPC = reg.add((dir + Form("/TOFoverTPC_PDG%i", pdgForPid)).c_str(), "n#sigma_{TOF};n#sigma_{TPC};", kTH2F, {{100, -10., 10.}, {100, -10., 10.}}); + h.nsigmaITS = reg.add((dir + Form("/nsigmaITS_PDG%i", pdgForPid)).c_str(), Form("n#sigma_{ITS};p (GeV/#it{c});n#sigma_{ITS}"), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + h.nsigmaTPC = reg.add((dir + Form("/nsigmaTPC_PDG%i", pdgForPid)).c_str(), Form("n#sigma_{TPC};p (GeV/#it{c});n#sigma_{TPC}"), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + h.nsigmaTOF = reg.add((dir + Form("/nsigmaTOF_PDG%i", pdgForPid)).c_str(), Form("n#sigma_{TOF};p (GeV/#it{c});n#sigma_{TOF}"), kTH2F, {{100, 0., 5.}, {100, -10., 10.}}); + } + + return h; + } + + template + static float getMassTOF(const TrackType& track) + { + const float beta = track.beta(); + if (beta <= 0.f || beta >= 1.f) { + return -1.f; + } + return track.p() * std::sqrt(1.f / (beta * beta) - 1.f); + } + + template + void fillHistSet(HistSet& h, const TrackType& track, int pdg) + { + const auto rapidity = track.rapidity(particleMass); + h.eta->Fill(track.eta()); + h.etaToY->Fill(track.eta(), rapidity); + h.y->Fill(rapidity); + h.phi->Fill(track.phi()); + h.p->Fill(track.p()); + h.pt->Fill(track.pt()); + h.dcaxyToP->Fill(track.p(), track.dcaXY()); + h.dcaxyToPt->Fill(track.pt(), track.dcaXY()); + h.dcazToP->Fill(track.p(), track.dcaZ()); + h.dcazToPt->Fill(track.pt(), track.dcaZ()); + h.dcaxyDcazToPt->Fill(track.dcaXY(), track.dcaZ(), track.pt()); + h.dcaxyToDcaz->Fill(track.dcaXY(), track.dcaZ()); + h.tpcClusters->Fill(track.tpcNClsFound()); + h.itsClusters->Fill(track.itsNCls()); + h.nsigmaITS->Fill(track.p(), o2::aod::singletrackselector::getITSNsigma(track, pdg)); + h.nsigmaTPC->Fill(track.p(), o2::aod::singletrackselector::getTPCNsigma(track, pdg)); + h.nsigmaTOF->Fill(track.p(), o2::aod::singletrackselector::getTOFNsigma(track, pdg)); + h.tofOverTPC->Fill(o2::aod::singletrackselector::getTPCNsigma(track, pdg), o2::aod::singletrackselector::getTOFNsigma(track, pdg)); + } + + template + void fillHistSetExtra(HistSet& h, const TrackType& track) + { + h.tpcSignal->Fill(track.tpcInnerParam(), track.tpcSignal()); + h.tofSignal->Fill(track.p(), track.beta()); + h.massTOF->Fill(track.pt(), getMassTOF(track)); + h.innerParamToP->Fill(track.p(), track.tpcInnerParam() / track.p()); + if constexpr (HasMC) { + h.ptGenPtRec->Fill(track.pt(), track.pt() - track.pt_MC()); + } + } + + template + void fillSet(HistSet& set, HistSet& mcSet, const TrackType& track, int pdg) + { + fillHistSet(set, track, pdg); + if constexpr (FillExtra) { + fillHistSetExtra(set, track); + } + + if constexpr (HasMC) { + if ((std::abs(track.pdgCode()) == pdg) && (track.pdgCode() / std::abs(track.pdgCode()) == track.sign())) { + mcSet.origin->Fill(track.origin()); + fillHistSet(mcSet, track, pdg); + if constexpr (FillExtra) { + fillHistSetExtra(mcSet, track); + } + } + } + } + + void init(o2::framework::InitContext&) + { + + if (isMC.value) { + o2::aod::ITSResponse::setMCDefaultParameters(); // set MC parametrisation for the ITS PID + } + + particleMass = static_cast(pdgDB->Mass(particlePDG.value)); + + itsCuts = std::make_pair(particlePDG.value, itsNSigma); + tpcCuts = std::make_pair(particlePDG.value, tpcNSigma); + tofCuts = std::make_pair(particlePDG.value, tofNSigma); + + int nBins = static_cast(dcaBinning.value[0]); // number of bins -- must be odd otherwise will be increased by 1 + if ((nBins & 1) == 0) { // bin count must be odd + nBins += 1; + } + + auto dcaBins = (static_cast(dcaBinning.value[2]) != 1.0) + ? calc_var_bins(nBins + 1, dcaBinning.value[1], static_cast(dcaBinning.value[2])) + : calc_const_bins(nBins, -dcaBinning.value[1], dcaBinning.value[1]); + auto constBinsP = calc_const_bins(100, 0., 5.0); + + hSets.resize(stageDirs.size()); + hSetsTrue.resize(stageDirs.size()); + for (size_t i = 0; i < stageDirs.size(); i++) { + for (size_t s = 0; s < signDirs.size(); s++) { + const std::string dir = stageDirs[i] + "/" + signDirs[s]; + hSets[i][s] = makeHistSet(registry, dir, particlePDG.value); + hSetsTrue[i][s] = makeHistSet(registryTrue, dir + "/true", particlePDG.value); + } + } + + const AxisSpec axisMult{5001, -0.5, 5000.5, "mult."}; + const AxisSpec axisPerc{101, -0.5, 100.5, "percentile"}; + registry.add("posZ", "posZ", kTH1F, {{300, -16., 16., "posZ"}}); + registry.add("mult", "mult", kTH1F, {axisMult}); + registry.add("MultVsCent", "MultVsCent", kTH2F, {axisMult, axisPerc}); + registry.add("IRvsOccupancy", "IRvsOccupancy", kTH2I, {{10000, 0, 10000, "Occupancy"}, {50, 0, 50, "IR, kHz"}}); + } + + template + void fillHistograms(ColsType const& collisions, TracksType const& tracks) + { + for (const auto& track : tracks) { + const int signIdx = (-track.sign() + 1) / 2; // 0: triton, 1: antitriton + fillSet(hSets[1][signIdx], hSetsTrue[1][signIdx], track, particlePDG.value); // nocuts + } + for (const auto& collision : collisions) { + if (removeSameBunchPileup && !collision.isNoSameBunchPileup()) { + continue; + } + if (requestGoodZvtxFT0vsPV && !collision.isGoodZvtxFT0vsPV()) { + continue; + } + if (requestVertexITSTPC && !collision.isVertexITSTPC()) { + continue; + } + if (requestVertexTOFmatched > collision.isVertexTOForTRDmatched()) { + continue; + } + if (requestNoCollInTimeRangeStandard && !collision.noCollInTimeRangeStandard()) { + continue; + } + if (requestIsGoodITSLayersAll && !collision.isGoodITSLayersAll()) { + continue; + } + if (collision.multPerc() < centCut.value.first || collision.multPerc() >= centCut.value.second) { + continue; + } + if (collision.hadronicRate() < irCut.value.first || collision.hadronicRate() >= irCut.value.second) { + continue; + } + if (collision.occupancy() < occupancyCut.value.first || collision.occupancy() >= occupancyCut.value.second) { + continue; + } + + registry.fill(HIST("posZ"), collision.posZ()); + registry.fill(HIST("mult"), collision.mult()); + registry.fill(HIST("MultVsCent"), collision.mult(), collision.multPerc()); + registry.fill(HIST("IRvsOccupancy"), collision.occupancy(), collision.hadronicRate()); + } + + for (const auto& track : tracks) { + const auto& coll = track.template singleCollSel_as(); + const int signIdx = (-track.sign() + 1) / 2; // 0: triton, 1: antitriton + + if (removeSameBunchPileup && !coll.isNoSameBunchPileup()) { + continue; + } + if (requestGoodZvtxFT0vsPV && !coll.isGoodZvtxFT0vsPV()) { + continue; + } + if (requestVertexITSTPC && !coll.isVertexITSTPC()) { + continue; + } + if (requestVertexTOFmatched > coll.isVertexTOForTRDmatched()) { + continue; + } + if (requestNoCollInTimeRangeStandard && !coll.noCollInTimeRangeStandard()) { + continue; + } + if (requestIsGoodITSLayersAll && !coll.isGoodITSLayersAll()) { + continue; + } + if (std::fabs(coll.posZ()) > vertexZ) { + continue; + } + if (coll.multPerc() < centCut.value.first || coll.multPerc() >= centCut.value.second) { + continue; + } + if (coll.hadronicRate() < irCut.value.first || coll.hadronicRate() >= irCut.value.second) { + continue; + } + if (coll.occupancy() < occupancyCut.value.first || coll.occupancy() >= occupancyCut.value.second) { + continue; + } + if (track.tpcFractionSharedCls() > maxTpcFractionSharedCls) { + continue; + } + if (track.itsNCls() < minItsNCls) { + continue; + } + if (std::fabs(track.dcaXY()) > dcaXY.value[0] + dcaXY.value[1] * std::pow(track.pt(), dcaXY.value[2])) { + continue; + } + if (std::fabs(track.dcaZ()) > dcaZ.value[0] + dcaZ.value[1] * std::pow(track.pt(), dcaZ.value[2])) { + continue; + } + + if (o2::aod::singletrackselector::ITSselection(track, itsCuts)) { + fillSet(hSets[2][signIdx], hSetsTrue[2][signIdx], track, particlePDG.value); // ITSCuts + } + if (o2::aod::singletrackselector::TPCselection(track, tpcCuts, itsNSigma.value)) { + fillSet(hSets[3][signIdx], hSetsTrue[3][signIdx], track, particlePDG.value); // TPCCuts + } + if (o2::aod::singletrackselector::TOFselection(track, tofCuts, tpcNSigmaResidual.value)) { + fillSet(hSets[4][signIdx], hSetsTrue[4][signIdx], track, particlePDG.value); // TOFCuts + } + if (o2::aod::singletrackselector::TPCselection(track, tpcCuts, itsNSigma.value)) { + fillSet(hSets[5][signIdx], hSetsTrue[5][signIdx], track, particlePDG.value); // ITSTPC + } + + const bool pidOk = + (track.p() < pidTrshld) + ? o2::aod::singletrackselector::TPCselection(track, tpcCuts, itsNSigma.value) + : o2::aod::singletrackselector::TPCselection(track, std::make_pair(particlePDG.value, tpcNSigmaResidual.value), tpcNSigmaResidual.value); + if (!pidOk) { + continue; + } + if (std::fabs(track.rapidity(particleMass)) > y) { + continue; + } + + fillSet(hSets[6][signIdx], hSetsTrue[6][signIdx], track, particlePDG.value); // PIDcuts + if constexpr (FillExtra) { + if (track.p() >= massCuttrshld && (getMassTOF(track) <= tofMass.value[0] || getMassTOF(track) >= tofMass.value[1])) { + continue; + } + } + fillSet(hSets[7][signIdx], hSetsTrue[7][signIdx], track, particlePDG.value); // masscut + + bool rejectedByPDG = false; + for (const int& pdgToReject : particlePDGtoReject.value) { + if (o2::aod::singletrackselector::TPCselection(track, std::make_pair(pdgToReject, rejectWithinNsigmaTPC.value))) { + rejectedByPDG = true; + break; + } + } + if (rejectedByPDG) { + continue; + } + fillSet(hSets[8][signIdx], hSetsTrue[8][signIdx], track, particlePDG.value); // rd + + if (getITSNsigma(track, particlePDG.value) <= itsNsigNL.value[0] + itsNsigNL.value[1] * track.p()) { + continue; + } + fillSet(hSets[9][signIdx], hSetsTrue[9][signIdx], track, particlePDG.value); // nl + } + } + + void processDefault(soa::Filtered> const& collisions, soa::Filtered> const& tracks) + { + fillHistograms(collisions, tracks); + } + PROCESS_SWITCH(AntitritonAnalysis, processDefault, "process default", true); + + void processExtra(soa::Filtered> const& collisions, soa::Filtered> const& tracks) + { + fillHistograms(collisions, tracks); + } + PROCESS_SWITCH(AntitritonAnalysis, processExtra, "process extra", false); + + void processMC(soa::Filtered> const& collisions, soa::Filtered> const& tracks) + { + fillHistograms(collisions, tracks); + } + PROCESS_SWITCH(AntitritonAnalysis, processMC, "process MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx index 37eb3e0ef6a..4eda03292a3 100644 --- a/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/dedxPidAnalysis.cxx @@ -43,13 +43,14 @@ #include #include +#include #include #include #include #include #include -#include +#include #include using namespace o2; @@ -59,35 +60,42 @@ using namespace constants::physics; using PIDTracks = soa::Join< aod::Tracks, aod::TracksExtra, aod::TrackSelectionExtension, aod::TracksDCA, aod::TrackSelection, - aod::pidTOFFullPi, aod::pidTOFFullPr, aod::pidTOFFullEl, aod::pidTOFbeta, aod::pidTPCPi, aod::pidTPCPr, aod::pidTPCEl>; + aod::pidTOFFullPi, aod::pidTOFFullPr, aod::pidTOFFullEl, aod::pidTOFbeta, aod::pidTPCPi, aod::pidTPCPr, aod::pidTPCEl, aod::pidTOFFlags>; using SelectedCollisions = soa::Join; using BCsRun3 = soa::Join; -static constexpr int NCentHists{10}; -std::array, NCentHists> hDedxVsMomentumVsCentPos{}; -std::array, NCentHists> hDedxVsMomentumVsCentNeg{}; - struct DedxPidAnalysis { // dE/dx for all charged particles - HistogramRegistry registryDeDx{ - "registryDeDx", - {}, - OutputObjHandlingPolicy::AnalysisObject, - true, - true}; + HistogramRegistry registryDeDx{"registryDeDx", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Constant values static constexpr int EtaIntervals = 8; static constexpr int ParticlesType = 4; static constexpr int CentralityClasses = 10; - float tpcCut = 0.6; - float pionMin = 0.35; - float pionMax = 0.45; - float elTofCut = 0.1; - float pionTofCut = 1.0; - float pTcut = 2.0; + std::array, CentralityClasses> hDedxVsMomentumVsCentPos{}; + std::array, CentralityClasses> hDedxVsMomentumVsCentNeg{}; + std::array, CentralityClasses> hDedxVsMomentumVsCent{}; + std::array, ParticlesType> hDedxvsMomentumPos{}; + std::array, ParticlesType> hDedxvsMomentumNeg{}; + std::array, ParticlesType> hDedxvsMomentum{}; + std::array, CentralityClasses + 1> hDedxVspTVsCent{}; + std::array, CentralityClasses + 1> hDedxVspTVsCentCh{}; + std::array, CentralityClasses + 1> hMomentumVsEtaPos{}; + std::array, CentralityClasses + 1> hMomentumVsEtaNeg{}; + std::array, CentralityClasses + 1> hMomentumVsEta{}; + std::array, CentralityClasses + 1> hpTVsEtaPos{}; + std::array, CentralityClasses + 1> hpTVsEtaNeg{}; + std::array, CentralityClasses + 1> hpTVsEta{}; + std::array, EtaIntervals> hNclFoundTPCPosBefore; + std::array, EtaIntervals> hNclFoundTPCNegBefore; + std::array, EtaIntervals> hNclFoundTPCPosAfter; + std::array, EtaIntervals> hNclFoundTPCNegAfter; + std::array, EtaIntervals> hNclPIDTPCPosBefore; + std::array, EtaIntervals> hNclPIDTPCNegBefore; + std::array, EtaIntervals> hNclPIDTPCPosAfter; + std::array, EtaIntervals> hNclPIDTPCNegAfter; bool fillHist = false; @@ -112,6 +120,12 @@ struct DedxPidAnalysis { }; + enum V0SigmaMode { + V0NoSigmaCut = 0, + V0SigmaOnly = 1, + V0TOFAndSigma = 2 + }; + enum NINELSelectionMode : int { NoSelINEL = 1, SelINELgt0 = 2, @@ -119,6 +133,11 @@ struct DedxPidAnalysis { }; + enum NDCAzSelectionMode : int { + DCAz1 = 1, + DCAzpT = 2, + }; + enum MomentumMode : int { TpcInnerParam = 1, TotalMomentum = 2 @@ -131,6 +150,7 @@ struct DedxPidAnalysis { ZVtxCut, NoSameBunchPileup, GoodZvtxFT0vsPV, + GoodITS, INELgt }; @@ -195,8 +215,25 @@ struct DedxPidAnalysis { "max z distance to IP"}; Configurable etaMin{"etaMin", -0.8f, "etaMin"}; Configurable etaMax{"etaMax", +0.8f, "etaMax"}; + // Configurables para activar/desactivar cortes + Configurable requireITSRefit{"requireITSRefit", true, "Require ITS refit"}; + Configurable requireTPCRefit{"requireTPCRefit", true, "Require TPC refit"}; + Configurable requireMinNCrossedRowsOverFindableClustersTPC{"requireMinNCrossedRowsOverFindableClustersTPC", true, "Require min N crossed rows / findable clusters TPC"}; Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; + Configurable nSigmaDCAxy{"nSigmaDCAxy", 3.0, "nSigma DCAxy selection"}; + Configurable dcaXYp0{"dcaXYp0", 0.0105f, "DCAxy formula: p0 + p1/pt^p2"}; + Configurable dcaXYp1{"dcaXYp1", 0.0350f, "DCAxy p1 parameter"}; + Configurable dcaXYp2{"dcaXYp2", 1.1f, "DCAxy p2 parameter"}; + Configurable dcaZp0{"dcaZp0", 0.0105f, "DCAz formula: p0 + p1/pt^p2"}; + Configurable dcaZp1{"dcaZp1", 0.0350f, "DCAz p1 parameter"}; + Configurable dcaZp2{"dcaZp2", 1.1f, "DCAz p2 parameter"}; + Configurable nSigmaDCAz{"nSigmaDCAz", 3.0, "nSigma DCAz selection"}; Configurable maxDCAz{"maxDCAz", 0.1f, "maxDCAz"}; + Configurable pionMin{"pionMin", 0.35f, "pionMin"}; + Configurable pionMax{"pionMax", 0.45f, "pionMax"}; + Configurable elTofCut{"elTofCut", 0.1f, "elTofCut"}; + Configurable pionTofCut{"pionTofCut", 1.0f, "pionTofCut"}; + Configurable pTcut{"pTcut", 2.f, "pTcut"}; // v0 cuts Configurable v0cospaMin{"v0cospaMin", 0.999f, "Minimum V0 CosPA"}; Configurable minimumV0Radius{"minimumV0Radius", 1.2f, @@ -214,17 +251,23 @@ struct DedxPidAnalysis { Configurable v0rapidityCut{"v0rapidityCut", 0.5f, "V0 rapidity cut"}; Configurable v0ProperLifetimeCutK0s{"v0ProperLifetimeCutK0s", 20.f, "V0 proper lifetime cut for K0s"}; Configurable v0ProperLifetimeCutLambda{"v0ProperLifetimeCutLambda", 30.f, "V0 proper lifetime cut for Lambda"}; - Configurable nsigmaTOFmax{"nsigmaTOFmax", 3.0f, "Maximum nsigma TOF"}; + Configurable nsigmaMax{"nsigmaMax", 3.0f, "Maximum nsigma cut"}; + Configurable nsigmaMaxTOF{"nsigmaMaxTOF", 3.0f, "Maximum nsigma cut for TOF"}; + Configurable tpcMomentumCut{"tpcMomentumCut", 0.6f, "Momentum threshold above which TOF is required"}; + Configurable v0SigmaMode{"v0SigmaMode", 0, "0: no cut, 1: sigma only, 2: TOF + sigma above tpcMomentumCut"}; Configurable invMassCutK0s{"invMassCutK0s", 0.015f, "invariant Mass Cut for K0s"}; Configurable invMassCutLambda{"invMassCutLambda", 0.015f, "invariant Mass Cut for Lambda"}; Configurable invMassCutGamma{"invMassCutGamma", 0.015f, "invariant Mass Cut for Gamma"}; Configurable calibrationMode{"calibrationMode", false, "calibration mode"}; + Configurable separateCharges{"separateCharges", true, "true: fill Pos and Neg separately; false: fill both charges in one histogram"}; Configurable phiVarCut{"phiVarCut", true, "phi var cut"}; Configurable nClTPCFoundCut{"nClTPCFoundCut", false, "number of found clusters in TPC cut"}; Configurable nClTPCPIDCut{"nClTPCPIDCut", true, "number of PID clusters in TPC cut"}; Configurable nGoodZvtx{"nGoodZvtx", true, "Rejects events with no vertex match between FT0 and PV"}; // Configurable nPileUp{"nPileUp", true, "Rejects events with pileup in the same bunch crossing"}; Configurable nINELSelectionMode{"nINELSelectionMode", 2, "INEL event selection: 1 no sel, 2 INEL>0, 3 INEL>1"}; + Configurable nGoodITS{"nGoodITS", true, "Numbers of inactive chips on all ITS layers are below maximum allowed values"}; + Configurable nDCAzOption{"nDCAzOption", 1, "DCA selection: 1: from -0.1 to 0.1, 2: pT dependent cut"}; Configurable v0SelectionMode{"v0SelectionMode", 3, "V0 Selection base on TPC: 1, TOF:2 ,Both:3"}; Configurable momentumMode{"momentumMode", 2, "1: TPC inner param, 2: Total momentum p"}; Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; @@ -235,47 +278,42 @@ struct DedxPidAnalysis { Configurable highParam1{"highParam1", 0.16685, "First parameter for high phi cut"}; Configurable highParam2{"highParam2", 0.00981942, "Second parameter for high phi cut"}; Configurable armPodCut{"armPodCut", 5.0f, "pT * (cut) > |alpha|"}; - // Histograms names - static constexpr std::string_view DedxvsMomentumPos[ParticlesType] = {"dEdx_vs_Momentum_all_Pos", "dEdx_vs_Momentum_Pi_v0_Pos", "dEdx_vs_Momentum_Pr_v0_Pos", "dEdx_vs_Momentum_El_v0_Pos"}; - static constexpr std::string_view DedxvsMomentumNeg[ParticlesType] = {"dEdx_vs_Momentum_all_Neg", "dEdx_vs_Momentum_Pi_v0_Neg", "dEdx_vs_Momentum_Pr_v0_Neg", "dEdx_vs_Momentum_El_v0_Neg"}; - static constexpr std::string_view DedxvsMomentumvsCentPos[CentralityClasses] = {"dEdx_vs_Momentum_Cent0_1_Pos", "dEdx_vs_Momentum_Cent1_5_Pos", "dEdx_vs_Momentum_Cent5_10_Pos", "dEdx_vs_Momentum_Cent10_15_Pos", "dEdx_vs_Momentum_Cent15_20_Pos", "dEdx_vs_Momentum_Cent20_30_Pos", "dEdx_vs_Momentum_Cent30_40_Pos", "dEdx_vs_Momentum_Cent40_50_Pos", "dEdx_vs_Momentum_Cent50_70_Pos", "dEdx_vs_Momentum_Cent70_100_Pos"}; - static constexpr std::string_view DedxvsMomentumvsCentNeg[CentralityClasses] = {"dEdx_vs_Momentum_Cent0_1_Neg", "dEdx_vs_Momentum_Cent1_5_Neg", "dEdx_vs_Momentum_Cent5_10_Neg", "dEdx_vs_Momentum_Cent10_15_Neg", "dEdx_vs_Momentum_Cent15_20_Neg", "dEdx_vs_Momentum_Cent20_30_Neg", "dEdx_vs_Momentum_Cent30_40_Neg", "dEdx_vs_Momentum_Cent40_50_Neg", "dEdx_vs_Momentum_Cent50_70_Neg", "dEdx_vs_Momentum_Cent70_100_Neg"}; - // Ncl TPC - static constexpr std::string_view NclTPCDedxMomentumNegBefore[EtaIntervals] = {"Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_1_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_2_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_3_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_4_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_5_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_6_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_7_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_8_Before"}; - static constexpr std::string_view NclTPCDedxMomentumPosBefore[EtaIntervals] = {"Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_1_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_2_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_3_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_4_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_5_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_6_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_7_Before", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_8_Before"}; - static constexpr std::string_view NclTPCDedxMomentumNegAfter[EtaIntervals] = {"Ncl_TFoundPC_vs_dEdx_vs_Momentum_Neg_1_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_2_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_3_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_4_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_5_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_6_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_7_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_8_After"}; - static constexpr std::string_view NclTPCDedxMomentumPosAfter[EtaIntervals] = {"Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_1_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_2_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_3_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_4_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_5_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_6_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_7_After", "Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_8_After"}; - // Ncl PID TPC - static constexpr std::string_view NclPIDTPCDedxMomentumNegBefore[EtaIntervals] = {"Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_1_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_2_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_3_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_4_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_5_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_6_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_7_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_8_Before"}; - static constexpr std::string_view NclPIDTPCDedxMomentumPosBefore[EtaIntervals] = {"Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_1_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_2_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_3_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_4_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_5_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_6_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_7_Before", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_8_Before"}; - static constexpr std::string_view NclPIDTPCDedxMomentumNegAfter[EtaIntervals] = {"Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_1_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_2_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_3_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_4_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_5_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_6_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_7_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_8_After"}; - static constexpr std::string_view NclPIDTPCDedxMomentumPosAfter[EtaIntervals] = {"Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_1_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_2_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_3_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_4_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_5_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_6_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_7_After", "Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_8_After"}; - static constexpr double EtaCut[EtaIntervals + 1] = {-0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}; - static constexpr double CentClasses[CentralityClasses + 1] = {0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}; + + static constexpr std::array EtaCut = {-0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}; + static constexpr std::array CentClasses = {0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0}; Configurable> calibrationFactorNeg{"calibrationFactorNeg", {50.4011, 50.4764, 50.186, 49.2955, 48.8222, 49.4273, 49.9292, 50.0556}, "negative calibration factors"}; Configurable> calibrationFactorPos{"calibrationFactorPos", {50.5157, 50.6359, 50.3198, 49.3345, 48.9197, 49.4931, 50.0188, 50.1406}, "positive calibration factors"}; ConfigurableAxis binP{"binP", {VARIABLE_WIDTH, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0}, ""}; - ConfigurableAxis centBins{"centBins", {100, 0, 100}, "Binning for centralidad"}; + ConfigurableAxis centBins{"centBins", {100, 0, 100}, "Binning for centralidad plots"}; + ConfigurableAxis dedxBins{"dedxBins", {100, 0, 100}, "Binning for dedx plots"}; + ConfigurableAxis pFineBins{"pFineBins", {1995, 0.1, 40}, "Binning for momentum plots"}; + ConfigurableAxis dcaBins{"dcaBins", {500, -0.5, 0.5}, "Binning for DCA plots"}; // phi cut fits TF1* fphiCutHigh = nullptr; TF1* fphiCutLow = nullptr; - Service ccdb; + Service ccdb{}; TrackSelection myTrackSelection() { TrackSelection selectedTracks; selectedTracks.SetPtRange(0.1f, 1e10f); selectedTracks.SetEtaRange(etaMin, etaMax); - selectedTracks.SetRequireITSRefit(true); - selectedTracks.SetRequireTPCRefit(true); - selectedTracks.SetMinNCrossedRowsTPC(minNCrossedRowsTPC); - selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC); + if (requireITSRefit.value) { + selectedTracks.SetRequireITSRefit(true); + } + if (requireTPCRefit.value) { + selectedTracks.SetRequireTPCRefit(true); + } + selectedTracks.SetMinNCrossedRowsTPC(static_cast(minNCrossedRowsTPC.value)); + if (requireMinNCrossedRowsOverFindableClustersTPC.value) { + selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); + } selectedTracks.SetMaxChi2PerClusterTPC(maxChi2TPC); selectedTracks.SetRequireHitsInITSLayers(1, {0, 1, 2}); selectedTracks.SetMaxChi2PerClusterITS(maxChi2ITS); - selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / std::pow(pt, 1.1f); }); - selectedTracks.SetMaxDcaZ(maxDCAz); + // selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / std::pow(pt, 1.1f); }); + // selectedTracks.SetMaxDcaZ(maxDCAz); selectedTracks.SetRequireGoldenChi2(true); return selectedTracks; @@ -296,8 +334,36 @@ struct DedxPidAnalysis { } else { LOGF(info, "GoodZvtxFT0vsPV cut disabled"); } + if (requireITSRefit) { + LOGF(info, "Applying ITSRefit cut"); + } else { + LOGF(info, "ITSRefit cut disabled"); + } + + if (requireTPCRefit) { + LOGF(info, "Applying TPCRefit cut"); + } else { + LOGF(info, "TPCRefit cut disabled"); + } + if (requireMinNCrossedRowsOverFindableClustersTPC) { + LOGF(info, "Applying MinNCrossedRowsOverFindableClustersTPC cut"); + } else { + LOGF(info, "MinNCrossedRowsOverFindableClustersTPC cut disabled"); + } + + if (nGoodITS) { + LOGF(info, "Applying GoodITSLayersAll cut"); + } else { + LOGF(info, "GoodITSLayersAll cut disabled"); + } + if (nDCAzOption == DCAz1) { + LOGF(info, "Applying -0.1 to 0.1 cut"); + } else if (nDCAzOption == DCAzpT) { + LOGF(info, "Applying pT-dependet cut"); + } if (nINELSelectionMode == NoSelINEL) { - LOGF(info, "INEL cut disabled"); + LOGF(info, "Applying just INEL"); + label = "INEL"; } else if (nINELSelectionMode == SelINELgt0) { LOGF(info, "Applying INEL > 0 cut"); label = "INEL > 0"; @@ -312,12 +378,25 @@ struct DedxPidAnalysis { } else if (v0SelectionMode == V0TPCTOF) { LOGF(info, "V0 seleccion using TOF + TPC"); } + if (v0SigmaMode == V0NoSigmaCut) { + LOGF(info, "V0 sigma mode: no cut"); + } else if (v0SigmaMode == V0SigmaOnly) { + LOGF(info, "V0 sigma mode: sigma only"); + } else if (v0SigmaMode == V0TOFAndSigma) { + LOGF(info, "V0 sigma mode: TOF + sigma above tpcMomentumCut"); + } if (calibrationMode) { LOGF(info, "Calibration mode activated"); } else { LOGF(info, "Running data applying calibration values"); } + if (separateCharges) { + LOGF(info, "Charge mode: Pos and Neg histograms filled separately"); + } else { + LOGF(info, "Charge mode: Pos and Neg combined in a single histogram"); + } + if (phiVarCut) { LOGF(info, "Applying phi prime cut"); } else { @@ -342,12 +421,15 @@ struct DedxPidAnalysis { } LOGF(info, "Centrality clases between %.1f - %.1f", CentClasses[0], CentClasses[10]); - AxisSpec dedxAxis{100, 0.0, 100.0, "dE/dx (a. u.)"}; - AxisSpec ptAxis = {binP, "pT (GeV/c)"}; + AxisSpec dedxAxis = {dedxBins, "dE/dx (a. u.)"}; + AxisSpec ptAxis = {binP, "#it{p}_{T} (GeV/c)"}; AxisSpec etaAxis{8, -0.8, 0.8, "#eta"}; AxisSpec pAxis = {binP, "#it{p}/Z (GeV/c)"}; AxisSpec pAxisTrack = {binP, "#it{p} (GeV/c)"}; AxisSpec centAxis{centBins, "Undefined multiplicity estimator"}; + AxisSpec pFineAxis{pFineBins, "#it{p} (GeV/c)"}; + AxisSpec pTFineAxis{pFineBins, "#it{p}_{T} (GeV/c)"}; + AxisSpec dcaAxis{dcaBins, ""}; switch (multiplicityEstimator) { case MultSelectionMode::NoMultiplicity: // No multiplicity LOGF(info, "No multiplicity estimator applied"); @@ -405,78 +487,191 @@ struct DedxPidAnalysis { LOGF(info, "=== Phi Cut Parameters ==="); LOGF(info, "Low cut: %.6f/x² + pi/18 - %.6f", lowParam1.value, lowParam2.value); LOGF(info, "High cut: %.6f/x + pi/18 + %.6f", highParam1.value, highParam2.value); + const std::array centNames = { + "Cent0_1", "Cent1_5", "Cent5_10", "Cent10_15", "Cent15_20", + "Cent20_30", "Cent30_40", "Cent40_50", "Cent50_70", "Cent70_100", "MB"}; + const std::array v0Names = { + "all", "Pi_v0", "Pr_v0", "El_v0"}; if (calibrationMode) { - // MIP for pions - registryDeDx.add( - "hdEdx_vs_eta_Neg_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_Pos_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); - // MIP for electrons - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - // Pions from TOF - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); - - } else { - // MIP for pions - registryDeDx.add( - "hdEdx_vs_eta_Neg_calibrated_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); + if (separateCharges) { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Neg_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_Pos_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); - registryDeDx.add( - "hdEdx_vs_eta_Pos_calibrated_Pi", "dE/dx", HistType::kTH2F, - {{etaAxis}, {dedxAxis}}); + } else { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + } - // MIP for electrons - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + } else { + if (separateCharges) { + + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_Neg_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_Pos_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // pt vs p + registryDeDx.add( + "heta_vs_pt_vs_p_all_Neg", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + registryDeDx.add( + "heta_vs_pt_vs_p_all_Pos", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + + // De/Dx for ch and v0 particles + for (int i = 0; i < ParticlesType; ++i) { + const auto& part = v0Names[i]; + hDedxvsMomentumPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", part.c_str()), + "dE/dx vs Momentum Positive", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxvsMomentumNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", part.c_str()), + "dE/dx vs Momentum Negative", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_El", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + for (int i = 0; i < CentralityClasses; ++i) { + const auto& cent = centNames[i]; + hDedxVsMomentumVsCentPos[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Pos", cent.c_str()), "dE/dx vs Momentum Positive", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + hDedxVsMomentumVsCentNeg[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s_Neg", cent.c_str()), "dE/dx vs Momentum Negative", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - // Pions from TOF - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Neg_calibrated_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hDedxVspTVsCent[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hMomentumVsEtaPos[i] = registryDeDx.add(Form("p_vs_eta_%s_Pos", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hMomentumVsEtaNeg[i] = registryDeDx.add(Form("p_vs_eta_%s_Neg", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hpTVsEtaPos[i] = registryDeDx.add(Form("pT_vs_eta_%s_Pos", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + hpTVsEtaNeg[i] = registryDeDx.add(Form("pT_vs_eta_%s_Neg", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + } - registryDeDx.add( - "hdEdx_vs_eta_vs_p_Pos_calibrated_TOF", "dE/dx", HistType::kTH3F, - {{etaAxis}, {dedxAxis}, {pAxis}}); + } else { + // MIP for pions + registryDeDx.add( + "hdEdx_vs_eta_calibrated_Pi", "dE/dx", HistType::kTH2F, + {{etaAxis}, {dedxAxis}}); + + // MIP for electrons + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_El", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // Pions from TOF + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + registryDeDx.add( + "hdEdx_vs_eta_vs_p_calibrated_TOF2", "dE/dx", HistType::kTH3F, + {{etaAxis}, {dedxAxis}, {pAxis}}); + + // pt vs p + + registryDeDx.add( + "heta_vs_pt_vs_p_all", "eta_vs_pT_vs_p", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxisTrack}}); + + // De/Dx for ch and v0 particles + for (int i = 0; i < ParticlesType; ++i) { + const auto& part = v0Names[i]; + hDedxvsMomentum[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s", part.c_str()), + "dE/dx vs Momentum", kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - // pt vs p - registryDeDx.add( - "heta_vs_pt_vs_p_all_Neg", "eta_vs_pT_vs_p", HistType::kTH3F, - {{etaAxis}, {ptAxis}, {pAxisTrack}}); - registryDeDx.add( - "heta_vs_pt_vs_p_all_Pos", "eta_vs_pT_vs_p", HistType::kTH3F, - {{etaAxis}, {ptAxis}, {pAxisTrack}}); - - // De/Dx for ch and v0 particles - for (int i = 0; i < ParticlesType; ++i) { - registryDeDx.add(DedxvsMomentumPos[i].data(), "dE/dx", HistType::kTH3F, - {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - registryDeDx.add(DedxvsMomentumNeg[i].data(), "dE/dx", HistType::kTH3F, - {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - } + for (int i = 0; i < CentralityClasses; ++i) { + const auto& cent = centNames[i]; + hDedxVsMomentumVsCent[i] = registryDeDx.add(Form("dEdx_vs_Momentum_%s", cent.c_str()), "dE/dx vs Momentum", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + } - for (int i = 0; i < CentralityClasses; ++i) { - hDedxVsMomentumVsCentPos[i] = registryDeDx.add(DedxvsMomentumvsCentPos[i].data(), "dE/dx", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); - hDedxVsMomentumVsCentNeg[i] = registryDeDx.add(DedxvsMomentumvsCentNeg[i].data(), "dE/dx", HistType::kTH3F, {{pAxisTrack}, {dedxAxis}, {etaAxis}}); + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hDedxVspTVsCentCh[i] = registryDeDx.add(Form("dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hMomentumVsEta[i] = registryDeDx.add(Form("p_vs_eta_%s", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hpTVsEta[i] = registryDeDx.add(Form("pT_vs_eta_%s", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + } } + // Invariant Mass + registryDeDx.add("hMassK0s", "Invariant mass K0s;m_{#pi#pi} (GeV/c^{2});Counts", + HistType::kTH1F, {{200, 0.4, 0.6}}); + registryDeDx.add("hMassLambda", "Invariant mass #Lambda;m_{p#pi} (GeV/c^{2});Counts", + HistType::kTH1F, {{200, 1.08, 1.16}}); + registryDeDx.add("hMassAntiLambda", "Invariant mass #bar{#Lambda};m_{#bar{p}#pi} (GeV/c^{2});Counts", + HistType::kTH1F, {{200, 1.08, 1.16}}); + registryDeDx.add("hMassGamma", "Invariant mass #gamma;m_{ee} (GeV/c^{2});Counts", + HistType::kTH1F, {{200, 0.0, 0.1}}); + + // Armenteros-Podolanski plot + registryDeDx.add("hArmenterosAll", "Armenteros-Podolanski (all V0s);#alpha;q_{T} (GeV/c)", + HistType::kTH2F, {{200, -1, 1}, {200, 0, 0.3}}); + + // Armenteros-Podolanski plot by particle + registryDeDx.add("hArmenterosK0s", "Armenteros-Podolanski K0s;#alpha;q_{T} (GeV/c)", + HistType::kTH2F, {{200, -1, 1}, {200, 0, 0.3}}); + registryDeDx.add("hArmenterosLambda", "Armenteros-Podolanski #Lambda;#alpha;q_{T} (GeV/c)", + HistType::kTH2F, {{200, -1, 1}, {200, 0, 0.3}}); + registryDeDx.add("hArmenterosAntiLambda", "Armenteros-Podolanski #bar{#Lambda};#alpha;q_{T} (GeV/c)", + HistType::kTH2F, {{200, -1, 1}, {200, 0, 0.3}}); + registryDeDx.add("hArmenterosGamma", "Armenteros-Podolanski #gamma;#alpha;q_{T} (GeV/c)", + HistType::kTH2F, {{200, -1, 1}, {200, 0, 0.3}}); } registryDeDx.add( @@ -518,33 +713,41 @@ struct DedxPidAnalysis { "hp_vs_phi_NclPID_TPC_Before", "phi cut vs p", HistType::kTH3F, {{pAxis}, {100, 0.0, 0.4, "#varphi^{'}"}, {100, 0, 160, "N_{cl, PID}"}}); } - // Ncl vs de/dx TPC + // Ncl vs de/dx TPC (found) if (nClTPCFoundCut) { for (int i = 0; i < EtaIntervals; ++i) { - registryDeDx.add(NclTPCDedxMomentumPosBefore[i].data(), "Ncl found TPC vs dE/dx vs Momentum Positive before", HistType::kTH3F, - {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); - registryDeDx.add(NclTPCDedxMomentumNegBefore[i].data(), "Ncl found TPC vs dE/dx vs Momentum Negative before", HistType::kTH3F, - {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); - - registryDeDx.add(NclTPCDedxMomentumPosAfter[i].data(), "Ncl found TPC vs dE/dx vs Momentum Positive after", HistType::kTH3F, - {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); - registryDeDx.add(NclTPCDedxMomentumNegAfter[i].data(), "Ncl found TPC vs dE/dx vs Momentum Negative after", HistType::kTH3F, - {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); + hNclFoundTPCPosBefore[i] = registryDeDx.add(Form("Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_%d_Before", i + 1), + "Ncl found TPC vs dE/dx vs Momentum Positive before", HistType::kTH3F, + {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); + hNclFoundTPCNegBefore[i] = registryDeDx.add(Form("Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_%d_Before", i + 1), + "Ncl found TPC vs dE/dx vs Momentum Negative before", HistType::kTH3F, + {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); + + hNclFoundTPCPosAfter[i] = registryDeDx.add(Form("Ncl_FoundTPC_vs_dEdx_vs_Momentum_Pos_%d_After", i + 1), + "Ncl found TPC vs dE/dx vs Momentum Positive after", HistType::kTH3F, + {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); + hNclFoundTPCNegAfter[i] = registryDeDx.add(Form("Ncl_FoundTPC_vs_dEdx_vs_Momentum_Neg_%d_After", i + 1), + "Ncl found TPC vs dE/dx vs Momentum Negative after", HistType::kTH3F, + {{100, 0, 160, "N_{cl, found}^{TPC}"}, {dedxAxis}, {pAxis}}); } } - // Ncl vs de/dx ITS + // Ncl vs de/dx PID TPC if (nClTPCPIDCut) { for (int i = 0; i < EtaIntervals; ++i) { - registryDeDx.add(NclPIDTPCDedxMomentumPosBefore[i].data(), "Ncl PID TPC vs dE/dx vs Momentum Positive before", HistType::kTH3F, - {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); - registryDeDx.add(NclPIDTPCDedxMomentumNegBefore[i].data(), "Ncl PID TPC vs dE/dx vs Momentum Negative before", HistType::kTH3F, - {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); - - registryDeDx.add(NclPIDTPCDedxMomentumPosAfter[i].data(), "Ncl PID TPC vs dE/dx vs Momentum Positive after", HistType::kTH3F, - {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); - registryDeDx.add(NclPIDTPCDedxMomentumNegAfter[i].data(), "Ncl PID TPC vs dE/dx vs Momentum Negative after", HistType::kTH3F, - {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); + hNclPIDTPCPosBefore[i] = registryDeDx.add(Form("Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_%d_Before", i + 1), + "Ncl PID TPC vs dE/dx vs Momentum Positive before", HistType::kTH3F, + {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); + hNclPIDTPCNegBefore[i] = registryDeDx.add(Form("Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_%d_Before", i + 1), + "Ncl PID TPC vs dE/dx vs Momentum Negative before", HistType::kTH3F, + {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); + + hNclPIDTPCPosAfter[i] = registryDeDx.add(Form("Ncl_PIDTPC_vs_dEdx_vs_Momentum_Pos_%d_After", i + 1), + "Ncl PID TPC vs dE/dx vs Momentum Positive after", HistType::kTH3F, + {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); + hNclPIDTPCNegAfter[i] = registryDeDx.add(Form("Ncl_PIDTPC_vs_dEdx_vs_Momentum_Neg_%d_After", i + 1), + "Ncl PID TPC vs dE/dx vs Momentum Negative after", HistType::kTH3F, + {{100, 0, 160, "N_{cl, PID}^{TPC}"}, {dedxAxis}, {pAxis}}); } } // eta @@ -639,13 +842,20 @@ struct DedxPidAnalysis { {{pAxisTrack}, {100, 0, 160, "N_{cl, PID}"}}); // beta plot - registryDeDx.add( - "hbeta_vs_p_Neg", "beta", HistType::kTH2F, - {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + if (separateCharges) { + registryDeDx.add( + "hbeta_vs_p_Neg", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); - registryDeDx.add( - "hbeta_vs_p_Pos", "beta", HistType::kTH2F, - {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + registryDeDx.add( + "hbeta_vs_p_Pos", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + } else { + + registryDeDx.add( + "hbeta_vs_p", "beta", HistType::kTH2F, + {{pAxis}, {100, 0.0, 1.1, "#beta"}}); + } // Event Counter registryDeDx.add("histRecVtxZData", "collision z position", HistType::kTH1F, {{100, -20.0, +20.0, "z_{vtx} (cm)"}}); @@ -657,8 +867,29 @@ struct DedxPidAnalysis { registryDeDx.add("Tracks_vs_pT_all", "pT All", HistType::kTH1F, {{ptAxis}}); registryDeDx.add("Tracks_vs_pT_all_cuts", "pT All + cuts", HistType::kTH1F, {{ptAxis}}); + // NCluster distributions + registryDeDx.add("hTPCClustersBefore", "N clusters TPC found Before", HistType::kTH1F, {{200, 0, 200, "N_{cl,found, Before}"}}); + registryDeDx.add("hTPCClustersAfter", "N clusters TPC found After", HistType::kTH1F, {{200, 0, 200, "N_{cl,found, After}"}}); + + registryDeDx.add("hTPCPIDBefore", "N clusters TPC PID Before", HistType::kTH1F, {{200, 0, 200, "N_{cl,PID, Before}"}}); + registryDeDx.add("hTPCPIDAfter", "N clusters TPC PID After", HistType::kTH1F, {{200, 0, 200, "N_{cl,PID, After}"}}); + + // DCA cut + registryDeDx.add("hDCAxyVsPt_beforeAnyCut", "DCAxy vs pT before any tkr cut;#it{p}_{T} (GeV/c);DCA_{xy} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registryDeDx.add("hDCAzVsPt_beforeAnyCut", "DCAz vs pT before any tkr cut;#it{p}_{T} (GeV/c);DCA_{z} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registryDeDx.add("hDCAxyVsPt_before", "DCAxy vs pT before cut;#it{p}_{T} (GeV/c);DCA_{xy} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registryDeDx.add("hDCAzVsPt_before", "DCAz vs pT before cut;#it{p}_{T} (GeV/c);DCA_{z} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registryDeDx.add("hDCAxyVsPt_after", "DCAxy vs pT after cut;#it{p}_{T} (GeV/c);DCA_{xy} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registryDeDx.add("hDCAzVsPt_after", "DCAz vs pT after cut;#it{p}_{T} (GeV/c);DCA_{z} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + // Event Counter - registryDeDx.add("evsel", "events selected", HistType::kTH1F, {{6, 0.5, 6.5, ""}}); + registryDeDx.add("evsel", "events selected", HistType::kTH1F, {{7, 0.5, 7.5, ""}}); auto hstat = registryDeDx.get(HIST("evsel")); auto* x = hstat->GetXaxis(); x->SetBinLabel(AllEv, "AllEv"); @@ -667,6 +898,7 @@ struct DedxPidAnalysis { x->SetBinLabel(NoSameBunchPileup, "NoSameBunchPileup"); x->SetBinLabel(GoodZvtxFT0vsPV, "GoodZvtxFT0vsPV"); x->SetBinLabel(INELgt, label); + x->SetBinLabel(GoodITS, "GoodITSLayersAll"); // Track Prim Counter registryDeDx.add("trackselAll", "track selected all particles", HistType::kTH1F, {{5, 0.5, 5.5, ""}}); @@ -715,6 +947,7 @@ struct DedxPidAnalysis { xSec->SetBinLabel(V0TypeGamma, "V0TypeGamma"); xSec->SetBinLabel(V0RapidityGamma, "V0RapidityGamma"); xSec->SetBinLabel(MassCutGamma, "MassCutGamma"); + mySelectionPrim = myTrackSelection(); } @@ -734,7 +967,25 @@ struct DedxPidAnalysis { return true; } - + // DCA xy cut + template + bool passesDCAxyCut(const T1& track) const + { + const float maxDcaXY = nSigmaDCAxy.value * (dcaXYp0.value + dcaXYp1.value / std::pow(track.pt(), dcaXYp2.value)) / 3.0; + return std::abs(track.dcaXY()) < maxDcaXY; + } + // DCA z cut + template + bool passesDCAzCut(const T1& track) const + { + if (nDCAzOption.value == DCAz1) { + return std::abs(track.dcaZ()) < maxDCAz.value; + } else if (nDCAzOption.value == DCAzpT) { + const float maxiDcaZ = nSigmaDCAz.value * (dcaZp0.value + dcaZp1.value / std::pow(track.pt(), dcaZp2.value)) / 3.0; + return std::abs(track.dcaZ()) < maxiDcaZ; + } + return true; + } // Momentum template float getMomentum(const T1& track) @@ -774,7 +1025,7 @@ struct DedxPidAnalysis { return false; if (!passedSingleTrackSelection(ntrack, collision)) return false; - /*double sigmap = 0.0; + double sigmap = 0.0; double sigman = 0.0; if (v0SelectionMode == V0TPC) { @@ -788,19 +1039,28 @@ struct DedxPidAnalysis { sigman = std::hypot(ntrack.tpcNSigmaPi(), ntrack.tofNSigmaPi()); } - if (ptrack.tpcInnerParam() > tpcCut) { - if (!ptrack.hasTOF()) + if (v0SigmaMode == V0TOFAndSigma) { + // TOF + sigma + if (ptrack.tpcInnerParam() > tpcMomentumCut) { + if (!ptrack.hasTOF()) + return false; + if (std::abs(sigmap) > nsigmaMax) + return false; + } + if (ntrack.tpcInnerParam() > tpcMomentumCut) { + if (!ntrack.hasTOF()) + return false; + if (std::abs(sigman) > nsigmaMax) + return false; + } + } else if (v0SigmaMode == V0SigmaOnly) { + // sigma only + if (std::abs(sigmap) > nsigmaMax) return false; - if (std::abs(sigmap) > nsigmaTOFmax) + if (std::abs(sigman) > nsigmaMax) return false; } - if (ntrack.tpcInnerParam() > tpcCut) { - if (!ntrack.hasTOF()) - return false; - if (std::abs(sigman) > nsigmaTOFmax) - return false; - }*/ if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::SingleTrackSelectionK0s); @@ -854,7 +1114,7 @@ struct DedxPidAnalysis { if (!passedSingleTrackSelection(ntrack, collision)) return false; - /*double sigmap = 0.0; + double sigmap = 0.0; double sigman = 0.0; if (v0SelectionMode == V0TPC) { @@ -868,20 +1128,28 @@ struct DedxPidAnalysis { sigman = std::hypot(ntrack.tpcNSigmaPi(), ntrack.tofNSigmaPi()); } - if (ptrack.tpcInnerParam() > tpcCut) { - if (!ptrack.hasTOF()) + if (v0SigmaMode == V0TOFAndSigma) { + // TOF + sigma + if (ptrack.tpcInnerParam() > tpcMomentumCut) { + if (!ptrack.hasTOF()) + return false; + if (std::abs(sigmap) > nsigmaMax) + return false; + } + if (ntrack.tpcInnerParam() > tpcMomentumCut) { + if (!ntrack.hasTOF()) + return false; + if (std::abs(sigman) > nsigmaMax) + return false; + } + } else if (v0SigmaMode == V0SigmaOnly) { + // sigma only + if (std::abs(sigmap) > nsigmaMax) return false; - if (std::abs(sigmap) > nsigmaTOFmax) + if (std::abs(sigman) > nsigmaMax) return false; } - if (ntrack.tpcInnerParam() > tpcCut) { - if (!ntrack.hasTOF()) - return false; - if (std::abs(sigman) > nsigmaTOFmax) - return false; - }*/ - if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::SingleTrackSelectionLambda); @@ -929,7 +1197,7 @@ struct DedxPidAnalysis { return false; if (!passedSingleTrackSelection(ntrack, collision)) return false; - /*double sigmap = 0.0; + double sigmap = 0.0; double sigman = 0.0; if (v0SelectionMode == V0TPC) { @@ -942,19 +1210,28 @@ struct DedxPidAnalysis { sigmap = std::hypot(ptrack.tpcNSigmaPi(), ptrack.tofNSigmaPi()); sigman = std::hypot(ntrack.tpcNSigmaPr(), ntrack.tofNSigmaPr()); } - if (ptrack.tpcInnerParam() > tpcCut) { - if (!ptrack.hasTOF()) - return false; - if (std::abs(sigmap) > nsigmaTOFmax) - return false; - } - if (ntrack.tpcInnerParam() > tpcCut) { - if (!ntrack.hasTOF()) + if (v0SigmaMode == V0TOFAndSigma) { + // TOF + sigma + if (ptrack.tpcInnerParam() > tpcMomentumCut) { + if (!ptrack.hasTOF()) + return false; + if (std::abs(sigmap) > nsigmaMax) + return false; + } + if (ntrack.tpcInnerParam() > tpcMomentumCut) { + if (!ntrack.hasTOF()) + return false; + if (std::abs(sigman) > nsigmaMax) + return false; + } + } else if (v0SigmaMode == V0SigmaOnly) { + // sigma only + if (std::abs(sigmap) > nsigmaMax) return false; - if (std::abs(sigman) > nsigmaTOFmax) + if (std::abs(sigman) > nsigmaMax) return false; - }*/ + } if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::SingleTrackSelectionAntiLambda); @@ -1005,7 +1282,7 @@ struct DedxPidAnalysis { if (!passedSingleTrackSelection(ntrack, collision)) return false; - /*double sigmap = 0.0; + double sigmap = 0.0; double sigman = 0.0; if (v0SelectionMode == V0TPC) { @@ -1019,21 +1296,28 @@ struct DedxPidAnalysis { sigman = std::hypot(ntrack.tpcNSigmaEl(), ntrack.tofNSigmaEl()); } - if (ptrack.tpcInnerParam() > tpcCut) { - if (!ptrack.hasTOF()) + if (v0SigmaMode == V0TOFAndSigma) { + // TOF + sigma + if (ptrack.tpcInnerParam() > tpcMomentumCut) { + if (!ptrack.hasTOF()) + return false; + if (std::abs(sigmap) > nsigmaMax) + return false; + } + if (ntrack.tpcInnerParam() > tpcMomentumCut) { + if (!ntrack.hasTOF()) + return false; + if (std::abs(sigman) > nsigmaMax) + return false; + } + } else if (v0SigmaMode == V0SigmaOnly) { + // sigma only + if (std::abs(sigmap) > nsigmaMax) return false; - if (std::abs(sigmap) > nsigmaTOFmax) + if (std::abs(sigman) > nsigmaMax) return false; } - if (ntrack.tpcInnerParam() > tpcCut) { - if (!ntrack.hasTOF()) - return false; - if (std::abs(sigman) > nsigmaTOFmax) - return false; - }*/ - const float gammaMass = 2 * MassElectron; // GeV/c^2 - if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::SingleTrackSelectionGamma); @@ -1048,7 +1332,7 @@ struct DedxPidAnalysis { if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::V0RapidityGamma); - if (std::abs(v0.mGamma() - gammaMass) > invMassCutGamma) // Invarian mass + if (std::abs(v0.mGamma() - MassGamma) > invMassCutGamma) // Invarian mass return false; if (fillHist) registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::MassCutGamma); @@ -1071,7 +1355,7 @@ struct DedxPidAnalysis { } // Phi cut template - bool passedPhiCutPri(const T& trk, float magField, const TF1& fphiCutLow, const TF1& fphiCutHigh) + bool passedPhiCutPri(const T& trk, float magField) { float p = trk.p(); float pt = trk.pt(); @@ -1098,7 +1382,7 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("hp_vs_phi_NclPID_TPC_Before"), p, phi, nTPCPIDCl); // cut phi - if (phi < fphiCutHigh.Eval(pt) && phi > fphiCutLow.Eval(pt)) + if (phi < fphiCutHigh->Eval(pt) && phi > fphiCutLow->Eval(pt)) return false; // reject track registryDeDx.fill(HIST("hpt_vs_phi_NclFound_TPC_After"), pt, phi, nTPCCl); @@ -1117,107 +1401,29 @@ struct DedxPidAnalysis { float sigP = trk.sign() * getMomentum(trk); auto nTPCCl = trk.tpcNClsFound(); - if (eta >= EtaCut[0] && eta < EtaCut[1]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[0]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[0]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[1] && eta < EtaCut[2]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[1]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[1]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[2] && eta < EtaCut[3]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[2]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[2]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[3] && eta < EtaCut[4]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[3]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[3]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[4] && eta < EtaCut[5]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[4]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[4]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[5] && eta < EtaCut[6]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[5]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[5]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[6] && eta < EtaCut[7]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[6]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[6]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[7] && eta < EtaCut[8]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegBefore[7]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosBefore[7]), nTPCCl, trk.tpcSignal(), sigP); + int etaIndex = -1; + for (int i = 0; i < EtaIntervals; ++i) { + if (eta >= EtaCut[i] && eta < EtaCut[i + 1]) { + etaIndex = i; + break; } } + if (etaIndex == -1) + return false; + + if (sigP < 0) { + hNclFoundTPCNegBefore[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), std::abs(sigP)); + } else { + hNclFoundTPCPosBefore[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), sigP); + } if (nTPCCl < minTPCnClsFound) return false; - if (eta >= EtaCut[0] && eta < EtaCut[1]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[0]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[0]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[1] && eta < EtaCut[2]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[1]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[1]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[2] && eta < EtaCut[3]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[2]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[2]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[3] && eta < EtaCut[4]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[3]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[3]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[4] && eta < EtaCut[5]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[4]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[4]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[5] && eta < EtaCut[6]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[5]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[5]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[6] && eta < EtaCut[7]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[6]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[6]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[7] && eta < EtaCut[8]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclTPCDedxMomentumNegAfter[7]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclTPCDedxMomentumPosAfter[7]), nTPCCl, trk.tpcSignal(), sigP); - } + if (sigP < 0) { + hNclFoundTPCNegAfter[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), std::abs(sigP)); + } else { + hNclFoundTPCPosAfter[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), sigP); } return true; @@ -1231,107 +1437,29 @@ struct DedxPidAnalysis { float sigP = trk.sign() * getMomentum(trk); auto nTPCCl = trk.tpcNClsPID(); - if (eta >= EtaCut[0] && eta < EtaCut[1]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[0]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[0]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[1] && eta < EtaCut[2]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[1]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[1]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[2] && eta < EtaCut[3]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[2]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[2]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[3] && eta < EtaCut[4]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[3]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[3]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[4] && eta < EtaCut[5]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[4]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[4]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[5] && eta < EtaCut[6]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[5]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[5]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[6] && eta < EtaCut[7]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[6]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[6]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[7] && eta < EtaCut[8]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegBefore[7]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosBefore[7]), nTPCCl, trk.tpcSignal(), sigP); + int etaIndex = -1; + for (int i = 0; i < EtaIntervals; ++i) { + if (eta >= EtaCut[i] && eta < EtaCut[i + 1]) { + etaIndex = i; + break; } } + if (etaIndex == -1) + return false; + + if (sigP < 0) { + hNclPIDTPCNegBefore[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), std::abs(sigP)); + } else { + hNclPIDTPCPosBefore[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), sigP); + } if (nTPCCl < minTPCnClsPID) return false; - if (eta >= EtaCut[0] && eta < EtaCut[1]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[0]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[0]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[1] && eta < EtaCut[2]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[1]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[1]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[2] && eta < EtaCut[3]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[2]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[2]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[3] && eta < EtaCut[4]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[3]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[3]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[4] && eta < EtaCut[5]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[4]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[4]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[5] && eta < EtaCut[6]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[5]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[5]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[6] && eta < EtaCut[7]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[6]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[6]), nTPCCl, trk.tpcSignal(), sigP); - } - } else if (eta >= EtaCut[7] && eta < EtaCut[8]) { - if (sigP < 0) { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumNegAfter[7]), nTPCCl, trk.tpcSignal(), std::abs(sigP)); - } else { - registryDeDx.fill(HIST(NclPIDTPCDedxMomentumPosAfter[7]), nTPCCl, trk.tpcSignal(), sigP); - } + if (sigP < 0) { + hNclPIDTPCNegAfter[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), std::abs(sigP)); + } else { + hNclPIDTPCPosAfter[etaIndex]->Fill(nTPCCl, trk.tpcSignal(), sigP); } return true; @@ -1339,7 +1467,7 @@ struct DedxPidAnalysis { // Phi cut Secondaries template - bool passedPhiCutSecondaries(const T& trk, float magField, const TF1& fphiCutLow, const TF1& fphiCutHigh) + bool passedPhiCutSecondaries(const T& trk, float magField) { float pt = trk.pt(); float phi = trk.phi(); @@ -1357,36 +1485,24 @@ struct DedxPidAnalysis { phi += o2::constants::math::PI / 18.0f; phi = std::fmod(phi, o2::constants::math::PI / 9.0f); - // cut phi - if (phi < fphiCutHigh.Eval(pt) && phi > fphiCutLow.Eval(pt)) - return false; // reject track - - return true; + // cut phi, reject track if inside the gap + return (phi >= fphiCutHigh->Eval(pt)) || (phi <= fphiCutLow->Eval(pt)); } // NclCutTPC template bool passedNClTPCFoundCutSecondaries(const T& trk) { - auto nTPCCl = trk.tpcNClsFound(); - - if (nTPCCl < minTPCnClsFound) - return false; - - return true; + return trk.tpcNClsFound() >= minTPCnClsFound; } // NclCutPIDTPC secondary template bool passedNClTPCPIDCutSecondaries(const T& trk) { - auto nTPCCl = trk.tpcNClsPID(); - - if (nTPCCl < minTPCnClsPID) - return false; - - return true; + return trk.tpcNClsPID() >= minTPCnClsPID; } + // Get Multiplicity template float getMultiplicity(const T& collision) @@ -1455,7 +1571,14 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("evsel"), EvCutLabel::GoodZvtxFT0vsPV); } + if (nGoodITS) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + return; + registryDeDx.fill(HIST("evsel"), EvCutLabel::GoodITS); + } + if (nINELSelectionMode == NoSelINEL) { + registryDeDx.fill(HIST("evsel"), EvCutLabel::INELgt); } else if (nINELSelectionMode == SelINELgt0) { if (!collision.isInelGt0()) return; @@ -1484,17 +1607,39 @@ struct DedxPidAnalysis { for (const auto& trk : tracks) { registryDeDx.fill(HIST("trackselAll"), TrkPriCutLabel::AllPri); - // track Selection + // Ncl distribution before cuts + registryDeDx.fill(HIST("hTPCClustersBefore"), trk.tpcNClsFound()); + registryDeDx.fill(HIST("hTPCPIDBefore"), trk.tpcNClsPID()); + + // Before DCA any tkr cuts + registryDeDx.fill(HIST("hDCAxyVsPt_beforeAnyCut"), trk.pt(), trk.dcaXY()); + registryDeDx.fill(HIST("hDCAzVsPt_beforeAnyCut"), trk.pt(), trk.dcaZ()); + + // track Selection wo DCA if (!mySelectionPrim.IsSelected(trk)) continue; + // Before DCA cuts + registryDeDx.fill(HIST("hDCAxyVsPt_before"), trk.pt(), trk.dcaXY()); + registryDeDx.fill(HIST("hDCAzVsPt_before"), trk.pt(), trk.dcaZ()); + + // DCA cuts + if (!passesDCAxyCut(trk)) + continue; + if (!passesDCAzCut(trk)) + continue; + + // After DCA cuts + registryDeDx.fill(HIST("hDCAxyVsPt_after"), trk.pt(), trk.dcaXY()); + registryDeDx.fill(HIST("hDCAzVsPt_after"), trk.pt(), trk.dcaZ()); + registryDeDx.fill(HIST("trackselAll"), TrkPriCutLabel::SelectionPrim); // For pt registryDeDx.fill(HIST("Tracks_vs_pT_all"), trk.pt()); // phi and Ncl cut if (phiVarCut) { - if (!passedPhiCutPri(trk, magField, *fphiCutLow, *fphiCutHigh)) + if (!passedPhiCutPri(trk, magField)) continue; registryDeDx.fill(HIST("trackselAll"), TrkPriCutLabel::PhiVarCutPri); } @@ -1521,50 +1666,84 @@ struct DedxPidAnalysis { // For pt + cuts registryDeDx.fill(HIST("Tracks_vs_pT_all_cuts"), trk.pt()); - float signedP = trk.sign() * getMomentum(trk); + // Ncl distribution After all cuts + registryDeDx.fill(HIST("hTPCClustersAfter"), trk.tpcNClsFound()); + registryDeDx.fill(HIST("hTPCPIDAfter"), trk.tpcNClsPID()); + + float p = getMomentum(trk); + float pT = trk.pt(); // MIP calibration for pions if (getMomentum(trk) >= pionMin && getMomentum(trk) <= pionMax) { if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_Pi"), trk.eta(), trk.tpcSignal()); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_Pi"), trk.eta(), trk.tpcSignal()); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_Pi"), trk.eta(), trk.tpcSignal()); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_Pi"), trk.eta(), trk.tpcSignal()); + registryDeDx.fill(HIST("hdEdx_vs_eta_Pi"), trk.eta(), trk.tpcSignal()); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_Neg_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_Pos_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i)); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_calibrated_Pi"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i)); + } } } } } } // Beta from TOF - if (signedP < 0) { - registryDeDx.fill(HIST("hbeta_vs_p_Neg"), std::abs(signedP), trk.beta()); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hbeta_vs_p_Neg"), p, trk.beta()); + } else { + registryDeDx.fill(HIST("hbeta_vs_p_Pos"), p, trk.beta()); + } } else { - registryDeDx.fill(HIST("hbeta_vs_p_Pos"), signedP, trk.beta()); + registryDeDx.fill(HIST("hbeta_vs_p"), p, trk.beta()); } + // Electrons from TOF if (std::abs(trk.beta() - 1) < elTofCut) { // beta cut if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_El"), trk.eta(), trk.tpcSignal(), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_El"), trk.eta(), trk.tpcSignal(), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_El"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_El"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } } } @@ -1573,18 +1752,56 @@ struct DedxPidAnalysis { // pions from TOF if (trk.beta() > pionTofCut && trk.beta() < pionTofCut + 0.05) { // beta cut if (calibrationMode) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_TOF"), trk.eta(), trk.tpcSignal(), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_TOF"), trk.eta(), trk.tpcSignal(), signedP); + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_TOF"), trk.eta(), trk.tpcSignal(), p); } } else { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP < 0) { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), std::abs(signedP)); + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } } else { - registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), signedP); + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } + } + } + } + } + } + + // pions from TOF w nsigma + if (!calibrationMode) { + if (trk.hasTOF() && trk.goodTOFMatch()) { + const double sigmaPi = std::hypot(trk.tpcNSigmaPi(), trk.tofNSigmaPi()); + if (std::abs(sigmaPi) < nsigmaMaxTOF) { + for (int i = 0; i < EtaIntervals; ++i) { + if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { + if (separateCharges) { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Neg_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_Pos_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } + } else { + if (trk.sign() < 0) { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), p); + } else { + registryDeDx.fill(HIST("hdEdx_vs_eta_vs_p_calibrated_TOF2"), trk.eta(), trk.tpcSignal() * 50 / calibrationFactorPos->at(i), p); + } + } } } } @@ -1606,14 +1823,45 @@ struct DedxPidAnalysis { for (int i = 0; i < EtaIntervals; ++i) { if (trk.eta() >= EtaCut[i] && trk.eta() < EtaCut[i + 1]) { - if (signedP > 0) { - registryDeDx.fill(HIST(DedxvsMomentumPos[0]), signedP, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); - registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Pos"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentPos[centIndex]->Fill(signedP, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + if (separateCharges) { + if (trk.sign() > 0) { + hDedxvsMomentumPos[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Pos"), trk.eta(), trk.pt(), trk.p()); + hDedxVsMomentumVsCentPos[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hMomentumVsEtaPos[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaPos[10]->Fill(trk.eta(), p); + hpTVsEtaPos[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaPos[10]->Fill(trk.eta(), pT); + } else { + hDedxvsMomentumNeg[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Neg"), trk.eta(), trk.pt(), trk.p()); + hDedxVsMomentumVsCentNeg[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCent[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hMomentumVsEtaNeg[centIndex]->Fill(trk.eta(), p); + hMomentumVsEtaNeg[10]->Fill(trk.eta(), p); + hpTVsEtaNeg[centIndex]->Fill(trk.eta(), pT); + hpTVsEtaNeg[10]->Fill(trk.eta(), pT); + } } else { - registryDeDx.fill(HIST(DedxvsMomentumNeg[0]), std::abs(signedP), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); - registryDeDx.fill(HIST("heta_vs_pt_vs_p_all_Neg"), trk.eta(), trk.pt(), trk.p()); - hDedxVsMomentumVsCentNeg[centIndex]->Fill(std::abs(signedP), trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + registryDeDx.fill(HIST("heta_vs_pt_vs_p_all"), trk.eta(), trk.pt(), trk.p()); + hMomentumVsEta[centIndex]->Fill(trk.eta(), p); + hMomentumVsEta[10]->Fill(trk.eta(), p); + hpTVsEta[centIndex]->Fill(trk.eta(), pT); + hpTVsEta[10]->Fill(trk.eta(), pT); + if (trk.sign() > 0) { + hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorPos->at(i), trk.eta()); + } else { + hDedxvsMomentum[0]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVsMomentumVsCent[centIndex]->Fill(p, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCentCh[centIndex]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + hDedxVspTVsCentCh[10]->Fill(pT, trk.tpcSignal() * 50 / calibrationFactorNeg->at(i), trk.eta()); + } } } } @@ -1643,13 +1891,13 @@ struct DedxPidAnalysis { // registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::TPCRefit); // phi and Ncl cut if (phiVarCut) { - if (!passedPhiCutSecondaries(posTrack, magField, *fphiCutLow, *fphiCutHigh)) + if (!passedPhiCutSecondaries(posTrack, magField)) continue; - if (!passedPhiCutSecondaries(negTrack, magField, *fphiCutLow, *fphiCutHigh)) + if (!passedPhiCutSecondaries(negTrack, magField)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::PhiVarCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::PhiVarCutSec); } fillHist = false; @@ -1689,7 +1937,7 @@ struct DedxPidAnalysis { if (!passedNClTPCFoundCutSecondaries(negTrack)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::NClTPCFoundCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::NClTPCFoundCutSec); } if (nClTPCPIDCut) { @@ -1699,7 +1947,7 @@ struct DedxPidAnalysis { if (!passedNClTPCPIDCutSecondaries(negTrack)) continue; - registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel ::NClTPCPIDCutSec); + registryDeDx.fill(HIST("trackselSec"), TrkSecCutLabel::NClTPCPIDCutSec); } float signedPpos = posTrack.sign() * getMomentum(posTrack); @@ -1707,18 +1955,31 @@ struct DedxPidAnalysis { fillHist = true; + // Armenteros for all V0 + registryDeDx.fill(HIST("hArmenterosAll"), v0.alpha(), v0.qtarm()); + // K0s Selection if (passedK0Selection(v0, negTrack, posTrack, collision)) { registryDeDx.fill(HIST("heta_vs_NclPID_TPC_After_PionsK0s"), posTrack.eta(), posTrack.tpcNClsPID()); registryDeDx.fill(HIST("heta_vs_NclPID_TPC_After_PionsK0s"), negTrack.eta(), negTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_PionsK0s"), posTrack.p(), posTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_PionsK0s"), negTrack.p(), negTrack.tpcNClsPID()); + registryDeDx.fill(HIST("hMassK0s"), v0.mK0Short()); + registryDeDx.fill(HIST("hArmenterosK0s"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumNeg[1]), std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumPos[1]), signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1729,12 +1990,22 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("heta_vs_NclPID_TPC_After_PionsLambda"), negTrack.eta(), negTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_ProtonsLambda"), posTrack.p(), posTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_PionsLambda"), negTrack.p(), negTrack.tpcNClsPID()); + registryDeDx.fill(HIST("hMassLambda"), v0.mLambda()); + registryDeDx.fill(HIST("hArmenterosLambda"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumNeg[1]), std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumPos[2]), signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[2]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1745,12 +2016,22 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("heta_vs_NclPID_TPC_After_ProtonsLambda"), negTrack.eta(), negTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_PionsLambda"), posTrack.p(), posTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_ProtonsLambda"), negTrack.p(), negTrack.tpcNClsPID()); + registryDeDx.fill(HIST("hMassAntiLambda"), v0.mAntiLambda()); + registryDeDx.fill(HIST("hArmenterosAntiLambda"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumNeg[2]), std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[2]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumPos[1]), signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[1]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } @@ -1761,12 +2042,22 @@ struct DedxPidAnalysis { registryDeDx.fill(HIST("heta_vs_NclPID_TPC_After_ElectronsGamma"), negTrack.eta(), negTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_ElectronsGamma"), posTrack.p(), posTrack.tpcNClsPID()); registryDeDx.fill(HIST("hp_vs_NclPID_TPC_After_ElectronsGamma"), negTrack.p(), negTrack.tpcNClsPID()); + registryDeDx.fill(HIST("hMassGamma"), v0.mGamma()); + registryDeDx.fill(HIST("hArmenterosGamma"), v0.alpha(), v0.qtarm()); for (int i = 0; i < EtaIntervals; ++i) { if (negTrack.eta() > EtaCut[i] && negTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumNeg[3]), std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + if (separateCharges) { + hDedxvsMomentumNeg[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } else { + hDedxvsMomentum[3]->Fill(std::abs(signedPneg), negTrack.tpcSignal() * 50 / calibrationFactorNeg->at(i), negTrack.eta()); + } } if (posTrack.eta() > EtaCut[i] && posTrack.eta() < EtaCut[i + 1]) { - registryDeDx.fill(HIST(DedxvsMomentumPos[3]), signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + if (separateCharges) { + hDedxvsMomentumPos[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } else { + hDedxvsMomentum[3]->Fill(signedPpos, posTrack.tpcSignal() * 50 / calibrationFactorPos->at(i), posTrack.eta()); + } } } } diff --git a/PWGLF/Tasks/Nuspex/DeuteronInJetsTrgPt.cxx b/PWGLF/Tasks/Nuspex/deuteronInJetsTrgPt.cxx similarity index 83% rename from PWGLF/Tasks/Nuspex/DeuteronInJetsTrgPt.cxx rename to PWGLF/Tasks/Nuspex/deuteronInJetsTrgPt.cxx index 903b319c1aa..82123748283 100644 --- a/PWGLF/Tasks/Nuspex/DeuteronInJetsTrgPt.cxx +++ b/PWGLF/Tasks/Nuspex/deuteronInJetsTrgPt.cxx @@ -9,7 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// Task for analysing (anti)deuteron production in jets using pT-triggered data - update: 05-02-2026 +/// \file deuteronInJetsTrgPt.cxx +/// \brief d and antid production in and out of jets using pTjet triggered data +/// +/// \author Cristian Moscatelli +/// \since 02/2026 +// ================ // // Executable : o2-analysis-lf-deuteron-in-jets-trg-pt @@ -73,16 +78,15 @@ struct DeuteronInJetsTrgPt { // Setting default selection criteria to select tracks. May be changed when configuring the analysis. struct : o2::framework::ConfigurableGroup { - std::string prefix{"cfgTrackCut"}; // General specific Configurable requirePvContributor{"requirePvContributor", false, "Require that the track is a PV contributor"}; - Configurable EtaMax{"EtaMax", 0.9f, "Max Eta for track acceptance"}; + Configurable etaMax{"etaMax", 0.9f, "Max Eta for track acceptance"}; Configurable minPt{"minPt", 0.3, "Minimum pt of the tracks"}; Configurable maxDcaxy{"maxDcaxy", 0.05, "Maximum DCAxy"}; Configurable maxDcaz{"maxDcaz", 0.05, "Maximum DCAz"}; // Part relative to ITS - Configurable ITSnClusMin{"ITSnClsMin", 6, "Minimum number of ITS clusters"}; - Configurable ITSchi2ClusMax{"ITSchi2ClusMax", 36.f, "Max ITS Chi2 per cluster"}; + Configurable itsNClusMin{"itsNClusMin", 6, "Minimum number of ITS clusters"}; + Configurable itsChi2ClusMax{"itsChi2ClusMax", 36.f, "Max ITS Chi2 per cluster"}; Configurable applyItsPid{"applyItsPid", false, "apply ITS PID"}; Configurable setMCDefaultItsParams{"setMCDefaultItsParams", true, "Set MC default parameters for ITS PID"}; Configurable nSigmaItsMin{"nSigmaItsMin", -3.0, "nSigmaITS min"}; @@ -90,24 +94,18 @@ struct DeuteronInJetsTrgPt { Configurable ptMaxItsPidProt{"ptMaxItsPidProt", 1.0, "maximum pt for ITS PID for protons"}; Configurable ptMaxItsPidDeut{"ptMaxItsPidDeut", 1.0, "maximum pt for ITS PID for deuterons"}; // Part relative to TPC - Configurable TPCnClsMin{"TPCnClsMin", 100, "Minimum number of TPC clusters"}; - Configurable TPCchi2ClusMin{"TPCchi2ClusMin", 0.f, "Min TPC Chi2 per cluster"}; - Configurable TPCchi2ClusMax{"TPCchi2ClusMax", 4.f, "Max TPC Chi2 per cluster"}; - Configurable TPCnCrossedRowsMin{"TPCnCrossedRowsMin", 100, "Minimum number of TPC crossed rows"}; - Configurable Rtpc{"minRtpc", 0.8, "Minimum value of TPC crossed rows/TPC n cluster findable"}; - Configurable TPCrigidityMin{"TPCrigidityMin", 0.3f, "Minimum TPC rigidity (p/Z) for track"}; - Configurable minNsigmaTpc{"minNsigmaTpc", -3.0, "Minimum nsigma TPC"}; - Configurable maxNsigmaTpc{"maxNsigmaTpc", +3.0, "Maximum nsigma TPC"}; - // Part relatuive to TOF - Configurable minNsigmaTof{"minNsigmaTof", -3.0, "Minimum nsigma TOF"}; - Configurable maxNsigmaTof{"maxNsigmaTof", +3.5, "Maximum nsigma TOF"}; + Configurable tpcNClsMin{"tpcNClsMin", 100, "Minimum number of TPC clusters"}; + Configurable tpcChi2ClusMin{"tpcChi2ClusMin", 0.f, "Min TPC Chi2 per cluster"}; + Configurable tpcChi2ClusMax{"tpcChi2ClusMax", 4.f, "Max TPC Chi2 per cluster"}; + Configurable tpcNCrossedRowsMin{"tpcNCrossedRowsMin", 100, "Minimum number of TPC crossed rows"}; + Configurable minNsigmaTpc{"minNsigmaTpc", -3.0, "Minimum nsigma TPC for TOF analysis"}; + Configurable maxNsigmaTpc{"maxNsigmaTpc", +3.0, "Maximum nsigma TPC for TOF analysis"}; } cfgTrackCut; // Setting default selection criteria for events. May be changes when configuring the analysis. struct : o2::framework::ConfigurableGroup { - std::string prefix{"cgfEventCut"}; Configurable zVtx{"zVtx", 10.0, "Maximum z vertex"}; - } cfgEvCut; + } cfgEventCut; // Skimmed data flag and list of active triggers for processing Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Skimmed dataset processing"}; @@ -115,16 +113,15 @@ struct DeuteronInJetsTrgPt { // Setting default selection criteria fr jet identification. May be changes when configuring the analysis. struct : o2::framework::ConfigurableGroup { - std::string prefix{"cgfJetCut"}; Configurable minJetPt{"minJetPt", 10.0, "Minimum pt of the jet after bkg subtraction"}; Configurable rJet{"rJet", 0.3, "Jet parameter R"}; Configurable deltaEtaEdge{"deltaEtaEdge", 0.05, "eta gap from the edge"}; } cfgJetCut; // Setting the number of bins and min and max value for the nsigma distribution - Configurable cfgNbins{"Nbins", 120, "Number of pT-bins"}; - Configurable cfgpt_min{"pt_min", 0.0, "Min pT value of pT-axis"}; - Configurable cfgpt_max{"pt_max", 6.0, "Max pT value of pT-axis"}; + Configurable cfgNbins{"cfgNbins", 120, "Number of pT-bins"}; + Configurable cfgPtMin{"cfgPtMin", 0.0, "Min pT value of pT-axis"}; + Configurable cfgPtMax{"cfgPtMax", 6.0, "Max pT value of pT-axis"}; // CCDB manager service for accessing condition data Service ccdb; @@ -159,45 +156,45 @@ struct DeuteronInJetsTrgPt { itsResponse.setMCDefaultParameters(); // Initialize random seed using high-resolution clock to ensure unique sequences across parallel Grid jobs - auto time_seed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - mRand.SetSeed(time_seed); + auto timeSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + mRand.SetSeed(timeSeed); // Histrograms for real data if (doprocessData) { registryData.add("number_of_events_data", "number of events in data", HistType::kTH1F, {{4, 0, 4, "counter"}}); // Event counters - registryData.add("settingData", "settingData", HistType::kTH2F, {{100, 0.0, 50.0, "min #it{p}^{jet}_{T} [GeV/#it{c}]"}, {20, 0.0, 1.0, "#it{R}_{jet}"}}); // Configuration + registryData.add("settingData", "settingData", HistType::kTH2F, {{100, 0.0, 50.0, "min #it{p}^{jet}_{T} (GeV/#it{c})"}, {20, 0.0, 1.0, "#it{R}_{jet}"}}); // Configuration registryData.add("jetEffectiveAreaOverPiR2", "jet effective area / piR^2", HistType::kTH1F, {{2000, 0, 2, "Area/#piR^{2}"}}); // Jet effective area over piR^2 // Antiprotons - registryData.add("antiproton_jet_tpc", "antiproton_jet_tpc", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("antiproton_jet_tof", "antiproton_jet_tof", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); - registryData.add("antiproton_ue_tpc", "antiproton_ue_tpc", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("antiproton_ue_tof", "antiproton_ue_tof", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); - registryData.add("antiproton_dca_jet", "antiproton_dca_jet", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {200, -1.0, 1.0, "DCA_{xy} [cm]"}}); - registryData.add("antiproton_dca_ue", "antiproton_dca_ue", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {200, -1.0, 1.0, "DCA_{xy} [cm]"}}); + registryData.add("antiproton_jet_tpc", "antiproton_jet_tpc", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antiproton_jet_tof", "antiproton_jet_tof", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("antiproton_ue_tpc", "antiproton_ue_tpc", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antiproton_ue_tof", "antiproton_ue_tof", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("antiproton_dca_jet", "antiproton_dca_jet", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {2000, -1.0, 1.0, "DCA_{xy} (cm)"}}); + registryData.add("antiproton_dca_ue", "antiproton_dca_ue", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {2000, -1.0, 1.0, "DCA_{xy} (cm)"}}); // protons - registryData.add("proton_jet_tpc", "proton_jet_tpc", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("proton_jet_tof", "proton_jet_tof", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); - registryData.add("proton_ue_tpc", "proton_ue_tpc", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("proton_ue_tof", "proton_ue_tof", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); - registryData.add("proton_dca_jet", "proton_dca_jet", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {200, -1.0, 1.0, "DCA_{xy} [cm]"}}); - registryData.add("proton_dca_ue", "proton_dca_ue", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {200, -1.0, 1.0, "DCA_{xy} [cm]"}}); + registryData.add("proton_jet_tpc", "proton_jet_tpc", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("proton_jet_tof", "proton_jet_tof", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("proton_ue_tpc", "proton_ue_tpc", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("proton_ue_tof", "proton_ue_tof", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("proton_dca_jet", "proton_dca_jet", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {2000, -1.0, 1.0, "DCA_{xy} (cm)"}}); + registryData.add("proton_dca_ue", "proton_dca_ue", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {2000, -1.0, 1.0, "DCA_{xy} (cm)"}}); // Antideuterons - registryData.add("antideuteron_jet_tpc", "antideuteron_jet_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("antideuteron_jet_tof", "antideuteron_jet_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); - registryData.add("antideuteron_ue_tpc", "antideuteron_ue_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("antideuteron_ue_tof", "antideuteron_ue_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("antideuteron_jet_tpc", "antideuteron_jet_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antideuteron_jet_tof", "antideuteron_jet_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("antideuteron_ue_tpc", "antideuteron_ue_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("antideuteron_ue_tof", "antideuteron_ue_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); // Deuterons - registryData.add("deuteron_jet_tpc", "deuteron_jet_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("deuteron_jet_tof", "deuteron_jet_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); - registryData.add("deuteron_ue_tpc", "deuteron_ue_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TPC}"}}); - registryData.add("deuteron_ue_tof", "deuteron_ue_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgpt_min, 2 * cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("deuteron_jet_tpc", "deuteron_jet_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("deuteron_jet_tof", "deuteron_jet_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); + registryData.add("deuteron_ue_tpc", "deuteron_ue_tpc", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TPC}"}}); + registryData.add("deuteron_ue_tof", "deuteron_ue_tof", HistType::kTH2F, {{cfgNbins, 2 * cfgPtMin, 2 * cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{TOF}"}}); // nsigmaITS for antiproton candidates - registryData.add("antiproton_nsigma_its_data", "antiproton_nsigma_its_data", HistType::kTH2F, {{cfgNbins, cfgpt_min, cfgpt_max, "#it{p}_{T} [GeV/#it{c}]"}, {400, -20.0, 20.0, "n#sigma_{ITS}"}}); + registryData.add("antiproton_nsigma_its_data", "antiproton_nsigma_its_data", HistType::kTH2F, {{cfgNbins, cfgPtMin, cfgPtMax, "#it{p}_{T} (GeV/#it{c})"}, {1000, -20.0, 20.0, "n#sigma_{ITS}"}}); } } @@ -286,7 +283,7 @@ struct DeuteronInJetsTrgPt { static constexpr double DcazMaxTrack = 2.0; // General part - if (std::fabs(track.eta()) > cfgTrackCut.EtaMax) + if (std::fabs(track.eta()) > cfgTrackCut.etaMax) return false; if (track.pt() < MinPtTrack) return false; @@ -319,7 +316,7 @@ struct DeuteronInJetsTrgPt { // General part if (cfgTrackCut.requirePvContributor && !(track.isPVContributor())) return false; // Flag to check if the track contributed to the collision vertex fit - if (std::fabs(track.eta()) > cfgTrackCut.EtaMax) + if (std::fabs(track.eta()) > cfgTrackCut.etaMax) return false; // Eta if (track.pt() < cfgTrackCut.minPt) return false; @@ -328,23 +325,21 @@ struct DeuteronInJetsTrgPt { return false; // Flag to check if track has a ITS match if ((!hasHitITS(track, 1)) && (!hasHitITS(track, 2)) && (!hasHitITS(track, 3))) return false; // Require IB hit - if (track.itsNCls() < cfgTrackCut.ITSnClusMin) + if (track.itsNCls() < cfgTrackCut.itsNClusMin) return false; // Minimum number of ITS cluster - if (track.itsChi2NCl() > cfgTrackCut.ITSchi2ClusMax) + if (track.itsChi2NCl() > cfgTrackCut.itsChi2ClusMax) return false; // Minimum chi2 per cluster in ITS // Part relative to TPC if (!track.hasTPC()) return false; // Flag to check if track has a ITS match - if (track.tpcNClsFound() < cfgTrackCut.TPCnClsMin) + if (track.tpcNClsFound() < cfgTrackCut.tpcNClsMin) return false; // Minimum number of TPC cluster - if (track.tpcNClsCrossedRows() < cfgTrackCut.TPCnCrossedRowsMin) + if (track.tpcNClsCrossedRows() < cfgTrackCut.tpcNCrossedRowsMin) return false; // Minimum number of crossed rows in TPC - if (track.tpcChi2NCl() < cfgTrackCut.TPCchi2ClusMin) + if (track.tpcChi2NCl() < cfgTrackCut.tpcChi2ClusMin) return false; // Minimum chi2 per cluster in TPC - if (track.tpcChi2NCl() > cfgTrackCut.TPCchi2ClusMax) + if (track.tpcChi2NCl() > cfgTrackCut.tpcChi2ClusMax) return false; // Maximum chi2 per cluster in TPC - if (track.tpcCrossedRowsOverFindableCls() < cfgTrackCut.Rtpc) - return false; // R_{TPC} > 0.8 return true; } @@ -359,13 +354,13 @@ struct DeuteronInJetsTrgPt { auto bc = collision.template bc_as(); initCCDB(bc); - // If skimmed processing is enabled, aplly Zorro trigger selection + // If skimmed processing is enabled, apply Zorro trigger selection if (cfgSkimmedProcessing && !zorro.isSelected(collision.template bc_as().globalBC())) return; registryData.fill(HIST("number_of_events_data"), 1.5); // Apply standard event selection - if (!collision.sel8() || std::fabs(collision.posZ()) >= cfgEvCut.zVtx) + if (!collision.sel8() || std::fabs(collision.posZ()) >= cfgEventCut.zVtx) return; registryData.fill(HIST("number_of_events_data"), 2.5); // Save number of collisions that passed standard selections @@ -397,7 +392,7 @@ struct DeuteronInJetsTrgPt { bool isAtLeastOneJetSelected = false; for (auto const& jet : jets) { - if ((std::fabs(jet.eta()) + cfgJetCut.rJet) > (cfgTrackCut.EtaMax - cfgJetCut.deltaEtaEdge)) + if ((std::fabs(jet.eta()) + cfgJetCut.rJet) > (cfgTrackCut.etaMax - cfgJetCut.deltaEtaEdge)) continue; // Jet must be fully contained in the acceptance // Jet pt must be larger than threshold diff --git a/PWGLF/Tasks/Nuspex/flattenicityPtSpectra.cxx b/PWGLF/Tasks/Nuspex/flattenicityPtSpectra.cxx new file mode 100644 index 00000000000..f3af6e7b3ee --- /dev/null +++ b/PWGLF/Tasks/Nuspex/flattenicityPtSpectra.cxx @@ -0,0 +1,313 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \author Dushmanta Sahu (dushmanta.sahus@cern.ch) +/// \since September 1, 2026 +/// \file flattenicityPtSpectra.cxx +/// \brief Analysis to do flattenicity pt spectra + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FT0Corrected.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using MyTracks = soa::Join; + +using MyCollisions = soa::Join; + +struct FlattenicityPtSpectra { + + Configurable etaMin{"etaMin", -0.8f, "Min eta for mid-rapidity tracks"}; + Configurable etaMax{"etaMax", 0.8f, "Max eta for mid-rapidity tracks"}; + Configurable ptMin{"ptMin", 0.15f, "Min pT (GeV/c)"}; + Configurable ptMax{"ptMax", 20.0f, "Max pT (GeV/c)"}; + Configurable minTPCnClsFound{"minTPCnClsFound", 70, "Min TPC found clusters"}; + Configurable maxDCAz{"maxDCAz", 2.0f, "Max |DCAz| (cm)"}; + Configurable maxVtxZ{"maxVtxZ", 10.0f, "Max |vtx z| (cm)"}; + + Configurable minActiveFT0Ch{"minActiveFT0Ch", 4, "Min active FT0 channels for flattenicity"}; + + static constexpr int NChA = 96; + static constexpr int NChC = 96; + static constexpr int NCells = NChA + NChC; // 192 total + + HistogramRegistry registry{ + "registry", + {}, + OutputObjHandlingPolicy::AnalysisObject, + true, + true}; + + template + bool isGoodTrack(TrackType const& track) const + { + if (!track.hasTPC()) + return false; + if (track.tpcNClsFound() < minTPCnClsFound.value) + return false; + if (track.eta() < etaMin.value || track.eta() > etaMax.value) + return false; + if (std::abs(track.dcaZ()) > maxDCAz.value) + return false; + if (track.pt() < ptMin.value || track.pt() > ptMax.value) + return false; + return true; + } + + float calculateFlattenicityFT0(aod::FT0 const& ft0, int minActive) const + { + std::array signals{}; + signals.fill(0.f); + + int nActive = 0; + + int chIdx = 0; + for (const auto& a : ft0.amplitudeA()) { + if (chIdx < NChA) { + float amp = static_cast(a); + if (amp > 0.f) { + signals[chIdx] = amp; + ++nActive; + } + } + ++chIdx; + } + + chIdx = 0; + for (const auto& a : ft0.amplitudeC()) { + if (chIdx < NChC) { + float amp = static_cast(a); + if (amp > 0.f) { + signals[NChA + chIdx] = amp; + ++nActive; + } + } + ++chIdx; + } + + if (nActive < minActive) + return -1.f; + + float mRho = 0.f; + for (int i = 0; i < NCells; ++i) { + mRho += signals[i]; + } + mRho /= static_cast(NCells); + if (mRho <= 0.f) + return -1.f; + + float sRhoTmp = 0.f; + for (int i = 0; i < NCells; ++i) { + if (signals[i] > 0.f) { + float d = signals[i] - mRho; + sRhoTmp += d * d; + } + } + sRhoTmp /= static_cast(NCells) * static_cast(NCells); + float sRho = std::sqrt(sRhoTmp); + + // flattenicity = 1 - sigma/mean, clamped to [0, 1] + float cv = sRho / mRho; + return std::max(0.f, std::min(1.f, 1.f - cv)); + } + + void init(InitContext const&) + { + AxisSpec ptAxis{200, 0.0f, 20.0f, "#it{p}_{T} (GeV/c)"}; + AxisSpec flatAxis{102, -0.01f, 1.01f, "Flattenicity"}; + AxisSpec multAxis{300, 0.f, 3000.f, "FT0M amplitude (a.u.)"}; + AxisSpec nchAxis{500, -0.5f, 499.5f, "Raw N_{ch} (|#eta|<0.8, p_{T}>0.15)"}; + AxisSpec vtxzAxis{200, -20.f, 20.f, "v_{z} (cm)"}; + + registry.add("hEventCounter", + "Event counter", + HistType::kTH1F, {{5, 0.5, 5.5}}); + registry.get(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(1, "All"); + registry.get(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(2, "vtxZ"); + registry.get(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(3, "hasFT0"); + registry.get(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(4, "FT0signal"); + registry.get(HIST("hEventCounter"))->GetXaxis()->SetBinLabel(5, "goodFlat"); + + registry.add("hVtxZ", + "Primary vertex z", + HistType::kTH1F, {vtxzAxis}); + + registry.add("hFT0MMultiplicity", + "FT0M amplitude sum", + HistType::kTH1F, {multAxis}); + + registry.add("hFlattenicityDistribution", + "Flattenicity (Gyula method, 192 FT0 ch)", + HistType::kTH1F, {flatAxis}); + + registry.add("hFT0AAmplitude", + "Total FT0A amplitude", + HistType::kTH1F, {{200, 0.f, 2000.f, "Amplitude (a.u.)"}}); + + registry.add("hFT0CAmplitude", + "Total FT0C amplitude", + HistType::kTH1F, {{200, 0.f, 2000.f, "Amplitude (a.u.)"}}); + + // QA: active channel count — use this to tune minActiveFT0Ch + registry.add("hNActiveFT0Channels", + "Active FT0 channels per event (amp > 0)", + HistType::kTH1F, {{193, -0.5f, 192.5f, "N active channels"}}); + + registry.add("hEtaDistribution", + "Eta distribution (selected tracks)", + HistType::kTH1F, {{100, -1.0f, 1.0f, "#eta"}}); + + registry.add("hPhiDistribution", + "Phi distribution (selected tracks)", + HistType::kTH1F, {{100, 0.f, o2::constants::math::TwoPI, "#phi"}}); + + registry.add("hPtSpectrum_All", + "pT spectrum (all selected events)", + HistType::kTH1F, {ptAxis}); + + registry.add("hFT0MMultVsFlattenicity", + "FT0M multiplicity vs Flattenicity;" + "FT0M amplitude (a.u.);Flattenicity", + HistType::kTH2F, {multAxis, flatAxis}); + + registry.add("hFT0MMultVsFlattenicityVsPt", + "FT0M multiplicity vs Flattenicity vs #it{p}_{T};" + "FT0M amplitude (a.u.);Flattenicity;#it{p}_{T} (GeV/c)", + HistType::kTH3F, {multAxis, flatAxis, ptAxis}); + + registry.add("hNchDistribution", + "Raw charged particle multiplicity", + HistType::kTH1F, {nchAxis}); + + registry.add("hNchVsFlattenicity", + "Raw N_{ch} vs Flattenicity;" + "Raw N_{ch};Flattenicity", + HistType::kTH2F, {nchAxis, flatAxis}); + + registry.add("hNchVsFlattenicityVsPt", + "Raw N_{ch} vs Flattenicity vs #it{p}_{T};" + "Raw N_{ch};Flattenicity;#it{p}_{T} (GeV/c)", + HistType::kTH3F, {nchAxis, flatAxis, ptAxis}); + + LOG(info) << "FlattenicityPtSpectra::init — done" + << " NCells=" << NCells + << " minActiveFT0Ch=" << minActiveFT0Ch.value + << " Flattenicity method: Gyula (fixed denominator = total channels)"; + } + + void process(MyCollisions::iterator const& collision, + MyTracks const& tracks, + aod::FT0s const& /*ft0s*/) + { + registry.fill(HIST("hEventCounter"), 1.f); + + if (std::abs(collision.posZ()) > maxVtxZ.value) + return; + registry.fill(HIST("hEventCounter"), 2.f); + registry.fill(HIST("hVtxZ"), collision.posZ()); + + if (!collision.has_foundFT0()) + return; + registry.fill(HIST("hEventCounter"), 3.f); + + auto ft0 = collision.foundFT0(); + + float sumA = 0.f, sumC = 0.f; + int nActive = 0; + for (const auto& a : ft0.amplitudeA()) { + if (a > 0.f) { + sumA += a; + ++nActive; + } + } + for (const auto& a : ft0.amplitudeC()) { + if (a > 0.f) { + sumC += a; + ++nActive; + } + } + + registry.fill(HIST("hFT0AAmplitude"), sumA); + registry.fill(HIST("hFT0CAmplitude"), sumC); + registry.fill(HIST("hNActiveFT0Channels"), static_cast(nActive)); + + if (sumA + sumC <= 0.f) + return; + registry.fill(HIST("hEventCounter"), 4.f); + + float flattenicity = calculateFlattenicityFT0(ft0, minActiveFT0Ch.value); + if (flattenicity < 0.f) + return; + registry.fill(HIST("hEventCounter"), 5.f); + + float multFT0M = collision.multFT0M(); + + registry.fill(HIST("hFT0MMultiplicity"), multFT0M); + registry.fill(HIST("hFlattenicityDistribution"), flattenicity); + registry.fill(HIST("hFT0MMultVsFlattenicity"), multFT0M, flattenicity); + + std::vector goodTracks; + goodTracks.reserve(1000); + + for (const auto& track : tracks) { + if (!isGoodTrack(track)) + continue; + goodTracks.push_back(track); + } + + int rawNch = goodTracks.size(); + + // Fill per-event histograms with Nch + registry.fill(HIST("hNchDistribution"), rawNch); + registry.fill(HIST("hNchVsFlattenicity"), rawNch, flattenicity); + + // Fill per-track histograms using the collected good tracks + for (const auto& track : goodTracks) { + registry.fill(HIST("hEtaDistribution"), track.eta()); + registry.fill(HIST("hPhiDistribution"), track.phi()); + registry.fill(HIST("hPtSpectrum_All"), track.pt()); + registry.fill(HIST("hFT0MMultVsFlattenicityVsPt"), + multFT0M, flattenicity, track.pt()); + + // NEW: Fill Nch-based 3D histogram + registry.fill(HIST("hNchVsFlattenicityVsPt"), + rawNch, flattenicity, track.pt()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx index 6aed03d9094..71d3dcd51ce 100644 --- a/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx +++ b/PWGLF/Tasks/Nuspex/hadronnucleicorrelation.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // +/// \file hadronnucleicorrelation.cxx /// \brief Hadron-nuclei correlation analysis task /// \author Francesca Ercolessi /// \since 21 April 2024 @@ -17,7 +18,6 @@ #include "PWGCF/Femto3D/DataModel/singletrackselector.h" #include "Common/Core/RecoDecay.h" -#include "Common/DataModel/Multiplicity.h" #include #include @@ -36,19 +36,24 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include #include #include +#include #include #include #include +#include #include #include @@ -69,103 +74,176 @@ enum Modes { kPPbar }; +enum Origin { + kPrimary = 0, + kWeakDecay, + kMaterial +}; + struct HadronNucleiCorrelation { - static constexpr int betahasTOFthr = -100; + static constexpr int BetahasTOFthr = -100; SliceCache cache; Configurable mode{"mode", 0, "0: antid-antip, 1: d-p, 2: antid-p, 3: d-antip, 4: antip-p, 5: antip-antip, 6: p-p, 7: p-antip"}; - Configurable dorapidity{"dorapidity", false, "do rapidity dependent analysis"}; + Configurable doRapidity{"doRapidity", false, "do rapidity dependent analysis"}; Configurable doQA{"doQA", true, "save QA histograms"}; Configurable doMCQA{"doMCQA", false, "save MC QA histograms"}; Configurable isMC{"isMC", false, "is MC"}; - Configurable isMCGen{"isMCGen", false, "is isMCGen"}; Configurable isPrim{"isPrim", true, "is isPrim"}; Configurable doCorrection{"doCorrection", false, "do efficiency correction"}; + Configurable doQuadraticPID{"doQuadraticPID", false, "do PID with sum in quadrature of TOF and TPC"}; + + struct : ConfigurableGroup { + std::string prefix = "Coalescence"; // JSON group name + Configurable doMCGenCoalescence{"doMCGenCoalescence", false, "do a simple coalescence on the generated level"}; + // Coalescence parameters used in Eur. Phys. J. A 59 (2023) 72: + // p_max = 0.148 GeV/c + // r_max = 2 fm + Configurable pMax{"pMax", 0.148, "maximum momentum for coalescence"}; + Configurable rMax{"rMax", 2.0, "maximum radius for coalescence"}; + } settingsCoalescence; Configurable fCorrectionPath{"fCorrectionPath", "", "Correction path to file"}; Configurable fCorrectionHisto{"fCorrectionHisto", "", "Correction histogram"}; - Configurable cfgUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; // Event selection Configurable cutzVertex{"cutzVertex", 10.0, "|vertexZ| value limit"}; + Configurable removeSameBunchPileup{"removeSameBunchPileup", false, "remove Same Bunch Pileup"}; // Track selection - Configurable par0{"par0", 0.004, "par 0"}; - Configurable par1{"par1", 0.013, "par 1"}; - Configurable min_TPC_nClusters{"min_TPC_nClusters", 80, "minimum number of found TPC clusters"}; - Configurable min_TPC_nCrossedRowsOverFindableCls{"min_TPC_nCrossedRowsOverFindableCls", 0.8, "n TPC Crossed Rows Over Findable Cls"}; - Configurable max_chi2_TPC{"max_chi2_TPC", 4.0f, "maximum TPC chi^2/Ncls"}; - Configurable max_chi2_ITS{"max_chi2_ITS", 36.0f, "maximum ITS chi^2/Ncls"}; + Configurable doClosePairRejection{"doClosePairRejection", false, "doClosePairRejection for non identical particles"}; + Configurable dcaPar0{"dcaPar0", 0.004, "par 0"}; + Configurable dcaPar1{"dcaPar1", 0.013, "par 1"}; + Configurable doDCAZ{"doDCAZ", true, "do DCA z cut"}; + Configurable minTPCnClusters{"minTPCnClusters", 80, "minimum number of found TPC clusters"}; + Configurable minTPCnCrossedRowsOverFindableCls{"minTPCnCrossedRowsOverFindableCls", 0.8, "n TPC Crossed Rows Over Findable Cls"}; + Configurable maxchi2TPC{"maxchi2TPC", 4.0f, "maximum TPC chi^2/Ncls"}; + Configurable maxchi2ITS{"maxchi2ITS", 36.0f, "maximum ITS chi^2/Ncls"}; Configurable etaCut{"etaCut", 0.8f, "eta cut"}; - Configurable max_DCAxy{"max_DCAxy", 0.14f, "Maximum DCAxy"}; - Configurable max_DCAz{"max_DCAz", 0.1f, "Maximum DCAz"}; + Configurable maxDCAxy{"maxDCAxy", 0.14f, "Maximum DCAxy"}; + Configurable maxDCAz{"maxDCAz", 0.1f, "Maximum DCAz"}; Configurable nsigmaTPC{"nsigmaTPC", 3.0f, "cut nsigma TPC"}; Configurable nsigmaElPr{"nsigmaElPr", 1.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaElDe{"nsigmaElDe", 3.0f, "cut nsigma TPC El for protons"}; Configurable nsigmaTOF{"nsigmaTOF", 3.5f, "cut nsigma TOF"}; + Configurable nsigmaTPCPrRej{"nsigmaTPCPrRej", 3.f, "cut nsigma TPC for proton rejection"}; + Configurable nsigmaTOFPrRej{"nsigmaTOFPrRej", 3.f, "cut nsigma TOF for proton rejection"}; + Configurable doPrRej{"doPrRej", true, "do TPC proton rejection"}; + Configurable nsigmaQuadratic{"nsigmaQuadratic", 3.0f, "cut on sqrt(nsigmaTPC^2 + nsigmaTOF^2), used above the TOF pT threshold when doQuadraticPID is on"}; Configurable nsigmaITSPr{"nsigmaITSPr", -2.0f, "cut nsigma ITS Pr"}; Configurable nsigmaITSDe{"nsigmaITSDe", -2.0f, "cut nsigma ITS De"}; Configurable doITSPID{"doITSPID", true, "do ITS PID"}; - Configurable pTthrpr_TOF{"pTthrpr_TOF", 0.8f, "threshold pT proton to use TOF"}; - Configurable pTthrpr_TPCEl{"pTthrpr_TPCEl", 1.0f, "threshold pT proton to use TPC El rejection"}; - Configurable pTthrde_TOF{"pTthrde_TOF", 1.0f, "threshold pT deuteron to use TOF"}; - Configurable pTthrde_TPCEl{"pTthrde_TPCEl", 1.0f, "threshold pT deuteron to use TPC El rejection"}; + Configurable pTthrprTOF{"pTthrprTOF", 0.8f, "threshold pT proton to use TOF"}; + Configurable pTthrdeTOF{"pTthrdeTOF", 1.0f, "threshold pT deuteron to use TOF"}; + Configurable pTthrdeTPCEl{"pTthrdeTPCEl", 1.0f, "threshold pT deuteron to use TPC El rejection"}; Configurable rejectionEl{"rejectionEl", true, "use TPC El rejection"}; - Configurable max_tpcSharedCls{"max_tpcSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; - Configurable min_itsNCls{"min_itsNCls", 0, "minimum allowed number of ITS clasters"}; + Configurable maxtpcSharedCls{"maxtpcSharedCls", 0.4, "maximum fraction of TPC shared clasters"}; + Configurable minitsNCls{"minitsNCls", 0, "minimum allowed number of ITS clasters"}; Configurable maxmixcollsGen{"maxmixcollsGen", 100, "maxmixcollsGen"}; Configurable radiusTPC{"radiusTPC", 1.2, "TPC radius to calculate phi_star for"}; Configurable dEta{"dEta", 0.01, "minimum allowed difference in eta between two tracks in a pair"}; Configurable dPhi{"dPhi", 0.01, "minimum allowed difference in phi_star between two tracks in a pair"}; + Configurable yRap{"yRap", 0.5, "rapidity"}; // Mixing parameters ConfigurableAxis confMultBins{"confMultBins", {VARIABLE_WIDTH, 0.0f, 4.0f, 8.0f, 12.0f, 16.0f, 20.0f, 24.0f, 28.0f, 50.0f, 100.0f, 99999.f}, "Mixing bins - multiplicity"}; ConfigurableAxis confVtxBins{"confVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}, true}; - ColumnBinningPolicy colBinningGen{{confVtxBins, confMultBins}, true}; + ColumnBinningPolicy colBinning{{confVtxBins, confMultBins}}; // pT/A bins Configurable> pTBins{"pTBins", {0.6f, 1.0f, 1.2f, 2.f}, "p_{T} bins"}; - ConfigurableAxis AxisNSigma{"AxisNSigma", {35, -7.f, 7.f}, "n#sigma"}; - ConfigurableAxis DeltaPhiAxis = {"DeltaPhiAxis", {46, -1 * o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}, "#Delta#phi (rad)"}; + struct : ConfigurableGroup { + std::string prefix = "axes"; // JSON group name + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {300, -1.5, 1.5}, "#Delta#eta"}; + ConfigurableAxis axisDeltaRap{"axisDeltaRap", {300, -1.5, 1.5}, "#Delta y"}; + ConfigurableAxis axisDeltaPhi{"deltaPhiAxis", {46, -1 * o2::constants::math::PIHalf, 3 * o2::constants::math::PIHalf}, "#Delta #phi"}; + ConfigurableAxis axisNSigma{"axisNSigma", {35, -7.f, 7.f}, "n#sigma"}; + } settingsAxes; using FilteredCollisions = soa::Filtered; - using SimCollisions = soa::Filtered>; + using FilteredCollisionsExtra = soa::Filtered>; + using SimCollisions = soa::Filtered; using SimParticles = aod::McParticles; using FilteredTracks = soa::Filtered>; // new tables (v3) using FilteredTracksMC = soa::Filtered>; // new tables (v3) HistogramRegistry registry{"registry"}; - HistogramRegistry QA{"QA"}; + HistogramRegistry registryQa{"registryQa"}; - using trkType = const FilteredTracks::iterator*; - // using trkTypeMC = const FilteredTracksMC::iterator*; - // typedef std::shared_ptr colType; + using TrkType = const FilteredTracks::iterator*; + // using TrkTypeMC = const FilteredTracksMC::iterator*; + // typedef std::shared_ptr ColType; // typedef std::shared_ptr MCcolType; - std::unique_ptr> Pair = std::make_unique>(); - // std::unique_ptr> PairMC = std::make_unique>(); + std::unique_ptr> pair = std::make_unique>(); + // std::unique_ptr> PairMC = std::make_unique>(); // Data histograms - std::vector> hEtaPhi_SE; - std::vector> hEtaPhi_ME; - std::vector> hCorrEtaPhi_SE; - std::vector> hCorrEtaPhi_ME; - - int nBinspT; - TH2F* hEffpTEta_proton; - TH2F* hEffpTEta_antiproton; - TH2F* hEffpTEta_deuteron; - TH2F* hEffpTEta_antideuteron; - - Service ccdb; + std::vector> hDeltaPhiSameEv; + std::vector> hDeltaPhiMixdEv; + std::vector> hCorrDeltaPhiSameEv; + std::vector> hCorrDeltaPhiMixdEv; + + int nBinspT = 0; + TH2* hEffPtEtaProton = nullptr; + TH2* hEffPtEtaAntiProton = nullptr; + TH2* hEffPtEtaDeuteron = nullptr; + TH2* hEffPtEtaAntiDeuteron = nullptr; + bool correctionsLoaded = false; // true only if all four efficiency histograms above were retrieved successfully + + // Generated-level pairing: PDG codes of the two species selected by `mode` (set in init) + int pdgPart0 = 0; + int pdgPart1 = 0; + std::pair massPart0Part1; // masses of the two species selected by `mode` (set in init) + + // Lightweight copy of a generated particle, so that the generated-level pairing runs on + // compact candidate vectors instead of re-reading the full MC particle table for every pair + struct GenCandidate { + float ptVal, etaVal, phiVal; + [[nodiscard]] float pt() const { return ptVal; } + [[nodiscard]] float eta() const { return etaVal; } + [[nodiscard]] float phi() const { return phiVal; } + [[nodiscard]] float pz() const { return ptVal * std::sinh(etaVal); } + [[nodiscard]] float p() const { return std::hypot(ptVal, pz()); } + [[nodiscard]] float energyForMass(const float mass) const { return std::hypot(p(), mass); } + [[nodiscard]] float rapidityForMass(const float mass) const + { + const float e = energyForMass(mass); + return 0.5f * std::log((e + pz()) / (e - pz())); + }; + [[nodiscard]] float mass(const int pdg) const + { + switch (pdg) { + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + return o2::track::PID::getMass(o2::track::PID::Deuteron); + case PDG_t::kProton: + case -PDG_t::kProton: + return o2::track::PID::getMass(o2::track::PID::Proton); + default: + LOG(fatal) << "Unhandled pdg " << pdg; + return 0.f; + } + } + [[nodiscard]] float rapidityForPdg(const int pdg) const { return rapidityForMass(mass(pdg)); } + }; + + // Per-collision information needed for generated-level same- and mixed-event pairing + struct GenCollisionCache { + float mult = 0.f; // charged primaries with |eta| < 1, used for the mixing bins + std::vector cand0; // particles matching pdgPart0 + std::vector cand1; // particles matching pdgPart1 + }; + + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; - Service pdgDB; + Service pdgDB{}; void init(o2::framework::InitContext&) { @@ -176,396 +254,439 @@ struct HadronNucleiCorrelation { ccdb->setFatalWhenNull(false); if (doCorrection) { - GetCorrection(ccdb, TString(fCorrectionPath), TString(fCorrectionHisto)); - } else { - hEffpTEta_proton = nullptr; - hEffpTEta_antiproton = nullptr; - hEffpTEta_deuteron = nullptr; - hEffpTEta_antideuteron = nullptr; + getCorrection(ccdb, TString(fCorrectionPath), TString(fCorrectionHisto)); + if (!correctionsLoaded) { + LOG(fatal) << "doCorrection is enabled but the efficiency histograms could not be retrieved from " << fCorrectionPath.value; + } } - AxisSpec ptBinnedAxis = {pTBins, "#it{p}_{T} of #bar{p} (GeV/c)"}; - AxisSpec etaAxis = {100, -1., 1., "#eta"}; - AxisSpec phiAxis = {157, 0., o2::constants::math::TwoPI, "#phi (rad)"}; - AxisSpec pTAxis = {200, -10.f, 10.f, "p_{T} GeV/c"}; - AxisSpec pTAxis_small = {100, -5.f, 5.f, "p_{T} GeV/c"}; - - AxisSpec DeltaEtaAxis = {300, -1.5, 1.5, "#Delta#eta"}; - AxisSpec DeltaRapAxis = {300, -1.5, 1.5, "#Delta y"}; - - registry.add("hNEvents", "hNEvents", {HistType::kTH1D, {{7, 0.f, 7.f}}}); - registry.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(1, "Selected"); - registry.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(2, "Mixing"); + const AxisSpec ptBinnedAxis = {pTBins, "#it{p}_{T} of #bar{p} (GeV/#it{c})"}; + const AxisSpec etaAxis = {100, -1., 1., "#eta"}; + const AxisSpec phiAxis = {157, 0., o2::constants::math::TwoPI, "#phi (rad)"}; + const AxisSpec ptAxis = {200, -10.f, 10.f, "#it{p}_{T} GeV/#it{c}"}; + const AxisSpec ptAxisSmall = {100, -5.f, 5.f, "#it{p}_{T} GeV/#it{c}"}; + + const AxisSpec deltaEtaAxis = {settingsAxes.axisDeltaEta, "#Delta#eta"}; + LOG(info) << "DeltaEta axis: from " << deltaEtaAxis.binEdges[0] << " to " << deltaEtaAxis.binEdges[1]; + const AxisSpec deltaRapAxis = {settingsAxes.axisDeltaRap, "#Delta y"}; + LOG(info) << "DeltaRap axis: from " << deltaRapAxis.binEdges[0] << " to " << deltaRapAxis.binEdges[1]; + const AxisSpec deltaPhiAxis = {settingsAxes.axisDeltaPhi, "#Delta#phi"}; + + if (doprocessSameEvent || doprocessSameEventEvSel) { + registry.add("hNEvents", "hNEvents", {HistType::kTH1D, {{7, 0.f, 7.f}}}); + registry.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(1, "Selected"); + registry.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(2, "Mixing"); + } - registry.add("hNtrig_total", "hNtrig_total", {HistType::kTH1D, {ptBinnedAxis}}); + // Not used, commented out for now + // registry.add("hNtrig_total", "hNtrig_total", {HistType::kTH1D, {ptBinnedAxis}}); nBinspT = pTBins.value.size() - 1; - TString name = "AntiDeAntiPr"; + TString name = "Undefined"; switch (mode) { - case kDP: + case kDbarPbar: // 0 + name = "AntiDeAntiPr"; + pdgPart0 = -o2::constants::physics::Pdg::kDeuteron; + pdgPart1 = -PDG_t::kProton; + break; + case kDP: // 1 name = "DePr"; + pdgPart0 = o2::constants::physics::Pdg::kDeuteron; + pdgPart1 = PDG_t::kProton; break; - case kDbarP: + case kDbarP: // 2 name = "AntiDePr"; + pdgPart0 = -o2::constants::physics::Pdg::kDeuteron; + pdgPart1 = PDG_t::kProton; break; - case kDPbar: + case kDPbar: // 3 name = "DeAntiPr"; + pdgPart0 = o2::constants::physics::Pdg::kDeuteron; + pdgPart1 = -PDG_t::kProton; break; - case kPbarP: + case kPbarP: // 4 name = "AntiPrPr"; + pdgPart0 = -PDG_t::kProton; + pdgPart1 = PDG_t::kProton; break; - case kPbarPbar: + case kPbarPbar: // 5 name = "AntiPrAntiPr"; + pdgPart0 = -PDG_t::kProton; + pdgPart1 = -PDG_t::kProton; break; - case kPP: + case kPP: // 6 name = "PrPr"; + pdgPart0 = PDG_t::kProton; + pdgPart1 = PDG_t::kProton; break; - case kPPbar: + case kPPbar: // 7 name = "PrAntiPr"; + pdgPart0 = PDG_t::kProton; + pdgPart1 = -PDG_t::kProton; + break; + default: + LOG(fatal) << "Unhandled case " << mode; + } + // Set the masses of the two species selected by `mode` + switch (pdgPart0) { + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + massPart0Part1.first = o2::track::PID::getMass(o2::track::PID::Deuteron); + break; + case PDG_t::kProton: + case -PDG_t::kProton: + massPart0Part1.first = o2::track::PID::getMass(o2::track::PID::Proton); break; + default: + LOG(fatal) << "Unhandled pdgPart0 " << pdgPart0; + } + switch (pdgPart1) { + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + massPart0Part1.second = o2::track::PID::getMass(o2::track::PID::Deuteron); + break; + case PDG_t::kProton: + case -PDG_t::kProton: + massPart0Part1.second = o2::track::PID::getMass(o2::track::PID::Proton); + break; + default: + LOG(fatal) << "Unhandled pdgPart1 " << pdgPart1; + } + if (massPart0Part1.first <= 0.f || massPart0Part1.second <= 0.f) { + LOG(fatal) << "Masses of the two species selected by `mode` are not set correctly: " << massPart0Part1.first << ", " << massPart0Part1.second; } if (!isMC) { for (int i = 0; i < nBinspT; i++) { - - if (dorapidity) { - - auto htempSE_AntiDeAntiPr = registry.add(Form("hEtaPhi_%s_SE_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("Raw #Delta y #Delta#phi (%.1f(Form("hEtaPhi_%s_ME_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("Raw #Delta y #Delta#phi (%.1f(Form("hCorrEtaPhi_%s_SE_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("#Delta y #Delta#phi (%.1f(Form("hCorrEtaPhi_%s_ME_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("#Delta y #Delta#phi (%.1f(Form("hEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "Raw #Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); + hDeltaPhiMixdEv.push_back(registry.add(Form("hEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "Raw #Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); + + hCorrDeltaPhiSameEv.push_back(registry.add(Form("hCorrEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "#Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); + hCorrDeltaPhiMixdEv.push_back(registry.add(Form("hCorrEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "#Delta y #Delta#phi " + ptInterval, {HistType::kTH3F, {deltaRapAxis, deltaPhiAxis, ptBinnedAxis}})); } else { + hDeltaPhiSameEv.push_back(registry.add(Form("hEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "Raw #Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); + hDeltaPhiMixdEv.push_back(registry.add(Form("hEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "Raw #Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); - auto htempSE_AntiDeAntiPr = registry.add(Form("hEtaPhi_%s_SE_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("Raw #Delta#eta#Delta#phi (%.1f(Form("hEtaPhi_%s_ME_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("Raw #Delta#eta#Delta#phi (%.1f(Form("hCorrEtaPhi_%s_SE_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("#Delta#eta#Delta#phi (%.1f(Form("hCorrEtaPhi_%s_ME_pt%02.0f%02.0f", name.Data(), pTBins.value.at(i) * 10, pTBins.value.at(i + 1) * 10), Form("#Delta#eta#Delta#phi (%.1f(Form("hCorrEtaPhi_%s_SE_%s", name.Data(), ptTag.Data()), "#Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); + hCorrDeltaPhiMixdEv.push_back(registry.add(Form("hCorrEtaPhi_%s_ME_%s", name.Data(), ptTag.Data()), "#Delta#eta#Delta#phi " + ptInterval, {HistType::kTH3F, {deltaEtaAxis, deltaPhiAxis, ptBinnedAxis}})); } } } - registry.add("hPrDCAxy", "DCAxy p", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hDeDCAxy", "DCAxy d", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); + const AxisSpec dcaPtAxis = {100, 0.f, 10.f, "#it{p}_{T} GeV/#it{c}"}; + if (doprocessSameEvent || doprocessSameEventEvSel || doprocessMC) { + const AxisSpec dcaXyAxis = {600, -3.f, 3.f, "DCA xy (cm)"}; + registry.add("hPrDCAxy", "DCAxy p", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registry.add("hAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registry.add("hDeDCAxy", "DCAxy d", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registry.add("hAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + } registry.add("hMult", "multiplicity", {HistType::kTH1D, {{200, 0.f, 200.f, "N_{ch}"}}}); - if (doQA) { + const AxisSpec tofNSigmaAxis = {settingsAxes.axisNSigma, "n#sigma TOF"}; + const AxisSpec tpcNSigmaAxis = {settingsAxes.axisNSigma, "n#sigma TPC"}; + const AxisSpec itsNSigmaAxis = {settingsAxes.axisNSigma, "n#sigma ITS"}; + + if (doQA && (doprocessSameEvent || doprocessSameEventEvSel || doprocessMC)) { + const AxisSpec dcaXyAxis = {200, -0.2f, 0.2f, "DCA xy (cm)"}; + const AxisSpec dcaZAxis = {200, -0.2f, 0.2f, "DCA z (cm)"}; + // Track QA - QA.add("QA/hVtxZ_trk", "#it{z}_{vtx}", {HistType::kTH1D, {{150, -15.f, 15.f, "#it{z}_{vtx} (cm)"}}}); - QA.add("QA/hTPCnClusters", "N TPC Clusters; N TPC Clusters", {HistType::kTH1D, {{200, 0.f, 200.f}}}); - QA.add("QA/hTPCSharedClusters", "N TPC Shared Clusters; N TPC SharedClusters", {HistType::kTH1D, {{100, 0.f, 1.f}}}); - QA.add("QA/hTPCchi2", "TPC chi2/Ncls; TPC chi2/Ncls", {HistType::kTH1D, {{100, 0.f, 10.f}}}); - QA.add("QA/hTPCcrossedRowsOverFindableCls", "TPC crossed Rows Over Findable Cls; TPC Crossed Rows Over Findable Cls", {HistType::kTH1D, {{100, 0.f, 2.f}}}); - QA.add("QA/hITSchi2", "ITS chi2/Ncls; ITS chi2/Ncls", {HistType::kTH1D, {{100, 0.f, 20.f}}}); - QA.add("QA/hDCAxy", "DCAxy", {HistType::kTH2D, {{200, -0.2f, 0.2f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - QA.add("QA/hDCAz", "DCAz", {HistType::kTH2D, {{200, -0.2f, 0.2f, "DCA z (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - QA.add("QA/TPCChi2VsPZ", "TPCChi2VsPZ", {HistType::kTH2D, {{100, 0.f, 10.f, "p_{TPC}/Z (GeV/c)"}, {120, 0.f, 6.f, "TPC Chi2"}}}); - QA.add("QA/hnSigmaTPCVsPt_El", "n#sigma TPC vs p_{T} for e hypothesis (all tracks); p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaTPCVsPt_Pr", "n#sigma TPC vs p_{T} for p hypothesis (all tracks); p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaTPCVsPt_De", "n#sigma TPC vs p_{T} for d hypothesis (all tracks); p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaTOFVsPt_Pr", "n#sigma TOF vs p_{T} for p hypothesis (all tracks); p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaTOFVsPt_De", "n#sigma TOF vs p_{T} for d hypothesis (all tracks); p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaITSVsPt_Pr", "n#sigma ITS vs p_{T} for p hypothesis (all tracks); p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaITSVsPt_De", "n#sigma ITS vs p_{T} for d hypothesis (all tracks); p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hdEtadPhistar", ";dPhi*;dEta ", {HistType::kTH2D, {{101, -0.2, 0.2, "dPhi*"}, {101, -0.2, 0.2, "dEta"}}}); + registryQa.add("QA/hVtxZ_trk", "#it{z}_{vtx}", {HistType::kTH1D, {{150, -15.f, 15.f, "#it{z}_{vtx} (cm)"}}}); + registryQa.add("QA/hTPCnClusters", "N TPC Clusters; N TPC Clusters", {HistType::kTH1D, {{200, 0.f, 200.f}}}); + registryQa.add("QA/hTPCSharedClusters", "N TPC Shared Clusters; N TPC SharedClusters", {HistType::kTH1D, {{100, 0.f, 1.f}}}); + registryQa.add("QA/hTPCchi2", "TPC chi2/Ncls; TPC chi2/Ncls", {HistType::kTH1D, {{100, 0.f, 10.f}}}); + registryQa.add("QA/hTPCcrossedRowsOverFindableCls", "TPC crossed Rows Over Findable Cls; TPC Crossed Rows Over Findable Cls", {HistType::kTH1D, {{100, 0.f, 2.f}}}); + registryQa.add("QA/hITSchi2", "ITS chi2/Ncls; ITS chi2/Ncls", {HistType::kTH1D, {{100, 0.f, 20.f}}}); + registryQa.add("QA/hDCAxy", "DCAxy", {HistType::kTH2D, {dcaXyAxis, dcaPtAxis}}); + registryQa.add("QA/hDCAz", "DCAz", {HistType::kTH2D, {dcaZAxis, dcaPtAxis}}); + registryQa.add("QA/TPCChi2VsPZ", "TPCChi2VsPZ", {HistType::kTH2D, {{100, 0.f, 10.f, "p_{TPC}/Z (GeV/c)"}, {120, 0.f, 6.f, "TPC Chi2"}}}); + registryQa.add("QA/h3dTPCTOF_Pr", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_AntiPr", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_De", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_AntiDe", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_De_Rej", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/h3dTPCTOF_AntiDe_Rej", "n#sigma TPC vs n#sigma TOF", {HistType::kTH3D, {tpcNSigmaAxis, tofNSigmaAxis, ptAxis}}); + registryQa.add("QA/hnSigmaTPCVsPt_El", "n#sigma TPC vs p_{T} for e hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); + registryQa.add("QA/hnSigmaTPCVsPt_Pr", "n#sigma TPC vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); + registryQa.add("QA/hnSigmaTPCVsPt_De", "n#sigma TPC vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); + registryQa.add("QA/hnSigmaTOFVsPt_Pr", "n#sigma TOF vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tofNSigmaAxis}}); + registryQa.add("QA/hnSigmaTOFVsPt_De", "n#sigma TOF vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tofNSigmaAxis}}); + registryQa.add("QA/hnSigmaITSVsPt_Pr", "n#sigma ITS vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, itsNSigmaAxis}}); + registryQa.add("QA/hnSigmaITSVsPt_De", "n#sigma ITS vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, itsNSigmaAxis}}); + registryQa.add("QA/hdEtadPhistar", ";dPhi*;dEta ", {HistType::kTH2D, {{101, -0.2, 0.2, "dPhi*"}, {101, -0.2, 0.2, "dEta"}}}); if (!isMC) { - QA.add("QA/hEtaPr", Form("#eta ditribution for p"), {HistType::kTH1F, {etaAxis}}); - QA.add("QA/hPhiPr", Form("#phi ditribution for p"), {HistType::kTH1F, {phiAxis}}); - QA.add("QA/hEtaAntiPr", Form("#eta ditribution for #bar{p}"), {HistType::kTH1F, {etaAxis}}); - QA.add("QA/hPhiAntiPr", Form("#phi ditribution for #bar{p}"), {HistType::kTH1F, {phiAxis}}); - QA.add("QA/hEtaDe", Form("#eta ditribution for d"), {HistType::kTH1F, {etaAxis}}); - QA.add("QA/hPhiDe", Form("#phi ditribution for d"), {HistType::kTH1F, {phiAxis}}); - QA.add("QA/hEtaAntiDe", Form("#eta ditribution for #bar{d}"), {HistType::kTH1F, {etaAxis}}); - QA.add("QA/hPhiAntiDe", Form("#phi ditribution for #bar{d}"), {HistType::kTH1F, {phiAxis}}); - - QA.add("QA/hnSigmaTPCVsPt_Pr_AfterSel", "n#sigma TPC vs p_{T} for p hypothesis (all tracks); p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaTPCVsPt_De_AfterSel", "n#sigma TPC vs p_{T} for d hypothesis (all tracks); p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaTOFVsPt_Pr_AfterSel", "n#sigma TOF vs p_{T} for p hypothesis (all tracks); p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaTOFVsPt_De_AfterSel", "n#sigma TOF vs p_{T} for d hypothesis (all tracks); p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaITSVsPt_Pr_AfterSel", "n#sigma ITS vs p_{T} for p hypothesis (all tracks); p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {pTAxis, AxisNSigma}}); - QA.add("QA/hnSigmaITSVsPt_De_AfterSel", "n#sigma ITS vs p_{T} for d hypothesis (all tracks); p_{T} (GeV/c); n#sigma ITS", {HistType::kTH2D, {pTAxis, AxisNSigma}}); + registryQa.add("QA/hEtaPr", Form("#eta ditribution for p"), {HistType::kTH1F, {etaAxis}}); + registryQa.add("QA/hPhiPr", Form("#phi ditribution for p"), {HistType::kTH1F, {phiAxis}}); + registryQa.add("QA/hEtaAntiPr", Form("#eta ditribution for #bar{p}"), {HistType::kTH1F, {etaAxis}}); + registryQa.add("QA/hPhiAntiPr", Form("#phi ditribution for #bar{p}"), {HistType::kTH1F, {phiAxis}}); + registryQa.add("QA/hEtaDe", Form("#eta ditribution for d"), {HistType::kTH1F, {etaAxis}}); + registryQa.add("QA/hPhiDe", Form("#phi ditribution for d"), {HistType::kTH1F, {phiAxis}}); + registryQa.add("QA/hEtaAntiDe", Form("#eta ditribution for #bar{d}"), {HistType::kTH1F, {etaAxis}}); + registryQa.add("QA/hPhiAntiDe", Form("#phi ditribution for #bar{d}"), {HistType::kTH1F, {phiAxis}}); + + registryQa.add("QA/hnSigmaTPCVsPt_Pr_AfterSel", "n#sigma TPC vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); + registryQa.add("QA/hnSigmaTPCVsPt_De_AfterSel", "n#sigma TPC vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tpcNSigmaAxis}}); + registryQa.add("QA/hnSigmaTOFVsPt_Pr_AfterSel", "n#sigma TOF vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tofNSigmaAxis}}); + registryQa.add("QA/hnSigmaTOFVsPt_De_AfterSel", "n#sigma TOF vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, tofNSigmaAxis}}); + registryQa.add("QA/hnSigmaITSVsPt_Pr_AfterSel", "n#sigma ITS vs p_{T} for p hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, itsNSigmaAxis}}); + registryQa.add("QA/hnSigmaITSVsPt_De_AfterSel", "n#sigma ITS vs p_{T} for d hypothesis (all tracks)", {HistType::kTH2D, {ptAxis, itsNSigmaAxis}}); } } - if (isMC) { - registry.add("hPrimPrDCAxy", "DCAxy p", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hPrimAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hPrimDeDCAxy", "DCAxy d", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hPrimAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecMatPrDCAxy", "DCAxy p", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecMatAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecMatDeDCAxy", "DCAxy d", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecMatAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecWeakPrDCAxy", "DCAxy p", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecWeakAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecWeakDeDCAxy", "DCAxy d", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - registry.add("hSecWeakAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {{600, -3.f, 3.f, "DCA xy (cm)"}, {100, 0.f, 10.f, "p_{T} GeV/c"}}}); - - registry.add("hReco_EtaPhiPt_Proton", "Gen (anti)protons in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hReco_EtaPhiPt_Deuteron", "Gen (anti)deuteron in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hReco_PID_EtaPhiPt_Proton", "Gen (anti)protons + PID in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hReco_PID_EtaPhiPt_Deuteron", "Gen (anti)deuteron + PID in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hReco_EtaPhiPtMC_Proton", "Gen (anti)protons in reco collisions (MC info used)", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hReco_EtaPhiPtMC_Deuteron", "Gen (anti)deuteron in reco collisions (MC info used)", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hReco_Pt_Proton", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Deuteron", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - - registry.add("hSec_EtaPhiPt_Proton", "Secondary (anti)protons", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hPrimSec_EtaPhiPt_Proton", "Primary + Secondary (anti)protons", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - - registry.add("hnSigmaTPCVsPt_Pr_MC", "n#sigma TPC vs p_{T} for p hypothesis true MC; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2F, {pTAxis, AxisNSigma}}); - registry.add("hnSigmaTPCVsPt_De_MC", "n#sigma TPC vs p_{T} for d hypothesis true MC; p_{T} (GeV/c); n#sigma TPC", {HistType::kTH2F, {pTAxis, AxisNSigma}}); - registry.add("hnSigmaTOFVsPt_Pr_MC", "n#sigma TOF vs p_{T} for p hypothesis true MC; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2F, {pTAxis, AxisNSigma}}); - registry.add("hnSigmaTOFVsPt_De_MC", "n#sigma TOF vs p_{T} for d hypothesis true MC; p_{T} (GeV/c); n#sigma TOF", {HistType::kTH2F, {pTAxis, AxisNSigma}}); - - registry.add("hResPt_Proton", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); - registry.add("hResPt_Deuteron", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); - registry.add("hResPt_AntiProton", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); - registry.add("hResPt_AntiDeuteron", "; p_{T}(gen) [GeV/c]; p_{T}(reco) - p_{T}(gen) ", {HistType::kTH2F, {{100, 0.f, 10.f, "p_{T}(gen) GeV/c"}, {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) "}}}); - - registry.add("hNumeratorPurity_Proton", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Deuteron", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Proton", " p(#bar{p}); p_{T} (GeV/c);(S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Deuteron", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); + if (isMC && doprocessMC) { + const AxisSpec dcaAxis = {600, -3.f, 3.f, "DCA xy (cm)"}; + const AxisSpec mcPtAxis = {100, 0.f, 10.f, "#it{p}_{T} GeV/#it{c}"}; + registry.add("hPrimPrDCAxy", "DCAxy p", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hPrimAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hPrimDeDCAxy", "DCAxy d", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hPrimAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecMatPrDCAxy", "DCAxy p", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecMatAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecMatDeDCAxy", "DCAxy d", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecMatAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecWeakPrDCAxy", "DCAxy p", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecWeakAntiPrDCAxy", "DCAxy #bar{p}", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecWeakDeDCAxy", "DCAxy d", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + registry.add("hSecWeakAntiDeDCAxy", "DCAxy #bar{d}", {HistType::kTH2D, {dcaAxis, mcPtAxis}}); + + registry.add("hReco_EtaPhiPt_Proton", "Gen (anti)protons in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hReco_EtaPhiPt_Deuteron", "Gen (anti)deuteron in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hReco_PID_EtaPhiPt_Proton", "Gen (anti)protons + PID in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hReco_PID_EtaPhiPt_Deuteron", "Gen (anti)deuteron + PID in reco collisions", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hReco_EtaPhiPtMC_Proton", "Gen (anti)protons in reco collisions (MC info used)", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hReco_EtaPhiPtMC_Deuteron", "Gen (anti)deuteron in reco collisions (MC info used)", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hReco_Pt_Proton", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + + registry.add("hSec_EtaPhiPt_Proton", "Secondary (anti)protons", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hPrimSec_EtaPhiPt_Proton", "Primary + Secondary (anti)protons", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + + registry.add("hnSigmaTPCVsPt_Pr_MC", "n#sigma TPC vs p_{T} for p hypothesis true MC", {HistType::kTH2F, {ptAxis, tpcNSigmaAxis}}); + registry.add("hnSigmaTPCVsPt_De_MC", "n#sigma TPC vs p_{T} for d hypothesis true MC", {HistType::kTH2F, {ptAxis, tpcNSigmaAxis}}); + registry.add("hnSigmaTOFVsPt_Pr_MC", "n#sigma TOF vs p_{T} for p hypothesis true MC", {HistType::kTH2F, {ptAxis, tofNSigmaAxis}}); + registry.add("hnSigmaTOFVsPt_De_MC", "n#sigma TOF vs p_{T} for d hypothesis true MC", {HistType::kTH2F, {ptAxis, tofNSigmaAxis}}); + + const AxisSpec genPtAxis = {100, 0.f, 10.f, "p_{T}(gen) (GeV/c)"}; + const AxisSpec resPtAxis = {200, -1.f, 1.f, "p_{T}(reco) - p_{T}(gen) (GeV/c)"}; + registry.add("hResPt_Proton", "p pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); + registry.add("hResPt_Deuteron", "d pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); + registry.add("hResPt_AntiProton", "#bar{p} pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); + registry.add("hResPt_AntiDeuteron", "#bar{d} pT residual", {HistType::kTH2F, {genPtAxis, resPtAxis}}); + + registry.add("hNumeratorPurity_Proton", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); if (doMCQA) { - registry.add("hResEta_Proton", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResEta_Deuteron", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResPhi_Proton", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - registry.add("hResPhi_Deuteron", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - registry.add("hResEta_AntiProton", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResEta_AntiDeuteron", "; #eta(gen); #eta(reco) - #eta(gen) ", {HistType::kTH2F, {{100, -1.f, 1.f, "#eta(gen)"}, {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}}}); - registry.add("hResPhi_AntiProton", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - registry.add("hResPhi_AntiDeuteron", "; #phi(gen); #phi(reco) - #phi(gen)", {HistType::kTH2F, {{100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}, {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}}}); - - registry.add("hNumeratorPurity_Proton_TPC", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Deuteron_TPC", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Proton_TPCTOF", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Deuteron_TPCTOF", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Proton_TPC_or_TOF", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Deuteron_TPC_or_TOF", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Proton_TPCEl_or_TOF", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Proton_TPCEl", " p(#bar{p}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Deuteron_TPCEl", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hNumeratorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d}); p_{T} (GeV/c);S", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Proton_TPC", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Deuteron_TPC", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Proton_TPCTOF", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Deuteron_TPCTOF", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Proton_TPC_or_TOF", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Deuteron_TPC_or_TOF", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Proton_TPCEl", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Proton_TPCEl_or_TOF", " p(#bar{p}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Deuteron_TPCEl", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hDenominatorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d}); p_{T} (GeV/c); (S + B)", {HistType::kTH1F, {pTAxis_small}}); - - registry.add("hReco_Pt_Proton_TPC", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Deuteron_TPC", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Proton_TPCTOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Deuteron_TPCTOF", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Proton_TPC_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Deuteron_TPC_or_TOF", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Proton_TPCEl", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Proton_TPCEl_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Deuteron_TPCEl", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); - registry.add("hReco_Pt_Deuteron_TPCEl_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {pTAxis_small}}); + const AxisSpec genEtaAxis = {100, -1.f, 1.f, "#eta(gen)"}; + const AxisSpec resEtaAxis = {200, -0.5f, 0.5f, "#eta(reco) - #eta(gen) "}; + + const AxisSpec genPhiAxis = {100, 0.f, o2::constants::math::TwoPI, "#phi(gen)"}; + const AxisSpec resPhiAxis = {200, -0.5f, 0.5f, "#phi(reco) - #phi(gen)"}; + registry.add("hResEta_Proton", "p eta residual", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResEta_Deuteron", "d eta residual ", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResPhi_Proton", "p phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + registry.add("hResPhi_Deuteron", "d phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + registry.add("hResEta_AntiProton", "p eta residual", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResEta_AntiDeuteron", "p eta residual ", {HistType::kTH2F, {genEtaAxis, resEtaAxis}}); + registry.add("hResPhi_AntiProton", "p phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + registry.add("hResPhi_AntiDeuteron", "d phi residual", {HistType::kTH2F, {genPhiAxis, resPhiAxis}}); + + registry.add("hNumeratorPurity_Proton_TPC", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPC", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPCTOF", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPCTOF", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPC_or_TOF", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPC_or_TOF", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPCEl_or_TOF", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Proton_TPCEl", " p(#bar{p});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPCEl", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hNumeratorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d});;S", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPC", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPC", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPCTOF", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPCTOF", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPC_or_TOF", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPC_or_TOF", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPCEl", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Proton_TPCEl_or_TOF", " p(#bar{p});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPCEl", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hDenominatorPurity_Deuteron_TPCEl_or_TOF", " d(#bar{d});;(S + B)", {HistType::kTH1F, {ptAxisSmall}}); + + registry.add("hReco_Pt_Proton_TPC", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron_TPC", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Proton_TPCTOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron_TPCTOF", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Proton_TPC_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron_TPC_or_TOF", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Proton_TPCEl", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Proton_TPCEl_or_TOF", "Reco (anti)protons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron_TPCEl", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); + registry.add("hReco_Pt_Deuteron_TPCEl_or_TOF", "Reco (anti)deuterons in reco collisions", {HistType::kTH1F, {ptAxisSmall}}); } } - if (isMCGen) { + if (doprocessMixedEventGen || doprocessSameEventGen) { registry.add("Generated/hNEventsMC", "hNEventsMC", {HistType::kTH1D, {{1, 0.f, 1.f}}}); registry.get(HIST("Generated/hNEventsMC"))->GetXaxis()->SetBinLabel(1, "All"); - registry.add("hGen_EtaPhiPt_Proton", "Gen (anti)protons in gen collisions", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - registry.add("hGen_EtaPhiPt_Deuteron", "Gen (anti)deuteron in gen collisions", {HistType::kTH3F, {etaAxis, phiAxis, pTAxis_small}}); - - registry.add("Generated/hQAProtons", "hQAProtons", {HistType::kTH1D, {{5, 0.f, 5.f}}}); - registry.get(HIST("Generated/hQAProtons"))->GetXaxis()->SetBinLabel(1, "All"); - registry.get(HIST("Generated/hQAProtons"))->GetXaxis()->SetBinLabel(2, "PhysicalPrimary"); - registry.get(HIST("Generated/hQAProtons"))->GetXaxis()->SetBinLabel(3, "|#eta|<0.8"); - registry.get(HIST("Generated/hQAProtons"))->GetXaxis()->SetBinLabel(4, "no daughters"); - registry.get(HIST("Generated/hQAProtons"))->GetXaxis()->SetBinLabel(5, "d daughter"); - - registry.add("Generated/hQADeuterons", "hQADeuterons", {HistType::kTH1D, {{3, 0.f, 3.f}}}); - registry.get(HIST("Generated/hQADeuterons"))->GetXaxis()->SetBinLabel(1, "All"); - registry.get(HIST("Generated/hQADeuterons"))->GetXaxis()->SetBinLabel(2, "PhysicalPrimary"); - registry.get(HIST("Generated/hQADeuterons"))->GetXaxis()->SetBinLabel(3, "|#eta|<0.8"); - - registry.add("Generated/hDeuteronsVsPt", "hDeuteronsVsPt; p_{T} (GeV/c);", {HistType::kTH1D, {{100, 0.f, 10.f}}}); - registry.add("Generated/hAntiDeuteronsVsPt", "hAntiDeuteronsVsPt; p_{T} (GeV/c);", {HistType::kTH1D, {{100, 0.f, 10.f}}}); + registry.add("hGen_EtaPhiPt_Proton", "Gen (anti)protons in gen collisions", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + registry.add("hGen_EtaPhiPt_Deuteron", "Gen (anti)deuteron in gen collisions", {HistType::kTH3F, {etaAxis, phiAxis, ptAxisSmall}}); + + auto hp = registry.add("Generated/hQAProtons", "hQAProtons", {HistType::kTH1D, {{5, 0.f, 5.f}}}); + hp->GetXaxis()->SetBinLabel(1, "All"); + hp->GetXaxis()->SetBinLabel(2, "PhysicalPrimary"); + hp->GetXaxis()->SetBinLabel(3, "|#eta|<0.8"); + hp->GetXaxis()->SetBinLabel(4, "no daughters"); + hp->GetXaxis()->SetBinLabel(5, "d daughter"); + registry.addClone("Generated/hQAProtons", "Generated/hQAAntiProtons"); + + registry.addClone("Generated/hQAProtons", "Generated/hQANeutrons"); + registry.addClone("Generated/hQAProtons", "Generated/hQAAntiNeutrons"); + + auto hd = registry.add("Generated/hQADeuterons", "hQADeuterons", {HistType::kTH1D, {{3, 0.f, 3.f}}}); + hd->GetXaxis()->SetBinLabel(1, "All"); + hd->GetXaxis()->SetBinLabel(2, "PhysicalPrimary"); + hd->GetXaxis()->SetBinLabel(3, "|#eta|<0.8"); + registry.addClone("Generated/hQADeuterons", "Generated/hQAAntiDeuterons"); + + const AxisSpec ptAxisGen = {100, 0.f, 10.f, "#it{p}_{T} GeV/#it{c}"}; + registry.add("Generated/hDeuteronsVsPt", "hDeuteronsVsPt", {HistType::kTH1D, {ptAxisGen}}); + registry.add("Generated/hAntiDeuteronsVsPt", "hAntiDeuteronsVsPt", {HistType::kTH1D, {ptAxisGen}}); + registry.add("Generated/hProtonsVsPt", "hProtonsVsPt", {HistType::kTH1D, {ptAxisGen}}); + registry.add("Generated/hAntiProtonsVsPt", "hAntiProtonsVsPt", {HistType::kTH1D, {ptAxisGen}}); + } else if (settingsCoalescence.doMCGenCoalescence.value) { + LOG(fatal) << "This is not a Gen Run but the Gen coalescence is required. Turn off doMCGenCoalescence"; } } // Filters Filter vertexFilter = nabs(o2::aod::singletrackselector::posZ) <= cutzVertex; - Filter trackFilter = o2::aod::singletrackselector::tpcNClsFound >= min_TPC_nClusters && - o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedTpcChi2NCl) <= max_chi2_TPC && - o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedTpcCrossedRowsOverFindableCls) >= min_TPC_nCrossedRowsOverFindableCls && - o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedItsChi2NCl) <= max_chi2_ITS && - nabs(o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedDcaXY)) <= max_DCAxy && - nabs(o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedDcaXY)) <= max_DCAz && + Filter trackFilter = o2::aod::singletrackselector::tpcNClsFound >= minTPCnClusters && + o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedTpcChi2NCl) <= maxchi2TPC && + o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedTpcCrossedRowsOverFindableCls) >= minTPCnCrossedRowsOverFindableCls && + o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedItsChi2NCl) <= maxchi2ITS && + nabs(o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedDcaXY)) <= maxDCAxy && + nabs(o2::aod::singletrackselector::unPack(o2::aod::singletrackselector::storedDcaZ)) <= maxDCAz && nabs(o2::aod::singletrackselector::eta) <= etaCut; Filter simvertexFilter = nabs(o2::aod::mccollision::posZ) <= cutzVertex; template - bool IsProton(Type const& track, int sign) + bool isProton(Type const& track, const int sign) const { - bool isProton = false; - bool isTPCPID = std::abs(track.tpcNSigmaPr()) < nsigmaTPC; - bool isTOFPID = std::abs(track.tofNSigmaPr()) < nsigmaTOF; - bool isTPCElRejection = rejectionEl && track.beta() < betahasTOFthr && track.pt() < pTthrpr_TPCEl && track.tpcNSigmaEl() >= nsigmaElPr; - bool isITSPID = track.itsNSigmaPr() > nsigmaITSPr; - - if (isTPCPID) { - if (track.pt() < pTthrpr_TOF) { - if (!doITSPID || isITSPID) { - if (sign > 0) { - if (track.sign() > 0) { - isProton = true; - } else if (track.sign() < 0) { - isProton = false; - } - } else if (sign < 0) { - if (track.sign() > 0) { - isProton = false; - } else if (track.sign() < 0) { - isProton = true; - } - } - } - } else if (isTPCElRejection) { - if (sign > 0) { - if (track.sign() > 0) { - isProton = true; - } else if (track.sign() < 0) { - isProton = false; - } - } else if (sign < 0) { - if (track.sign() > 0) { - isProton = false; - } else if (track.sign() < 0) { - isProton = true; + const bool isTPCPID = std::abs(track.tpcNSigmaPr()) < nsigmaTPC.value; + const bool isTOFPID = std::abs(track.tofNSigmaPr()) < nsigmaTOF.value; + const bool isITSPID = track.itsNSigmaPr() > nsigmaITSPr.value; + + const bool isQuadraticPID = std::hypot(track.tpcNSigmaPr(), track.tofNSigmaPr()) < nsigmaQuadratic.value; + + // Check if the sign of the track matches the expected sign for protons or antiprotons + const bool signCheck = (sign > 0 && track.sign() > 0) || (sign < 0 && track.sign() < 0); + if (!doQuadraticPID.value) { + if (isTPCPID) { + if (track.pt() < pTthrprTOF.value) { + if (!doITSPID.value || isITSPID) { + return signCheck; } + } else if (isTOFPID) { + return signCheck; } - } else if (isTOFPID) { - if (sign > 0) { - if (track.sign() > 0) { - isProton = true; - } else if (track.sign() < 0) { - isProton = false; - } - } else if (sign < 0) { - if (track.sign() > 0) { - isProton = false; - } else if (track.sign() < 0) { - isProton = true; + } + } else { + if (track.pt() < pTthrprTOF.value) { + if (isTPCPID) { + if (!doITSPID.value || isITSPID) { + return signCheck; } } + } else if (isQuadraticPID) { + return signCheck; } } - return isProton; + return false; } template - bool IsDeuteron(Type const& track, int sign) + bool isDeuteron(Type const& track, const int sign) const { - bool isDeuteron = false; - bool isTPCPID = std::abs(track.tpcNSigmaDe()) < nsigmaTPC; - bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF; - bool isTPCElRejection = rejectionEl && track.beta() < betahasTOFthr && track.pt() < pTthrde_TPCEl && track.tpcNSigmaEl() >= nsigmaElDe; - bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe; - - if (isTPCPID) { - if (track.pt() < pTthrde_TOF) { - if (!doITSPID || isITSPID) { - if (sign > 0) { - if (track.sign() > 0) { - isDeuteron = true; - } else if (track.sign() < 0) { - isDeuteron = false; - } - } else if (sign < 0) { - if (track.sign() > 0) { - isDeuteron = false; - } else if (track.sign() < 0) { - isDeuteron = true; - } - } - } - } else if (isTPCElRejection) { - if (sign > 0) { - if (track.sign() > 0) { - isDeuteron = true; - } else if (track.sign() < 0) { - isDeuteron = false; - } - } else if (sign < 0) { - if (track.sign() > 0) { - isDeuteron = false; - } else if (track.sign() < 0) { - isDeuteron = true; + const bool isTPCPID = std::abs(track.tpcNSigmaDe()) < nsigmaTPC.value; + const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; + const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; + const bool isITSPID = track.itsNSigmaDe() > nsigmaITSDe.value; + const bool isPr = (std::abs(track.tpcNSigmaPr()) < nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) < nsigmaTOFPrRej.value) && doPrRej.value; + + const bool isQuadraticPID = std::hypot(track.tpcNSigmaDe(), track.tofNSigmaDe()) < nsigmaQuadratic.value; + + // Check if the sign of the track matches the expected sign for deuterons or antideuterons + const bool signCheck = (sign > 0 && track.sign() > 0) || (sign < 0 && track.sign() < 0); + if (isPr) { + return false; + } + if (!doQuadraticPID.value) { + if (isTPCPID) { + if (track.pt() < pTthrdeTOF.value) { + if (!doITSPID.value || isITSPID) { + return signCheck; } + } else if (isTOFPID || (isTPCElRejection && (!doITSPID.value || isITSPID))) { + return signCheck; } - } else if (isTOFPID) { - if (sign > 0) { - if (track.sign() > 0) { - isDeuteron = true; - } else if (track.sign() < 0) { - isDeuteron = false; - } - } else if (sign < 0) { - if (track.sign() > 0) { - isDeuteron = false; - } else if (track.sign() < 0) { - isDeuteron = true; + } + } else { + if (track.pt() < pTthrdeTOF.value) { + if (isTPCPID) { + if (!doITSPID.value || isITSPID) { + return signCheck; } } + } else if (isQuadraticPID) { + return signCheck; } } - return isDeuteron; + return false; } template - bool applyDCAcut(const T1& track) + bool applyDCAcut(const T1& track) const { - bool passcut = true; // pt-dependent selection - if (std::abs(track.dcaXY()) > (par0 + par1 / track.pt())) - passcut = false; - if (std::abs(track.dcaZ()) > (par0 + par1 / track.pt())) - passcut = false; - - return passcut; + if (std::abs(track.dcaXY()) > (dcaPar0.value + dcaPar1.value / track.pt())) { + return false; + } + if (doDCAZ.value && std::abs(track.dcaZ()) > (dcaPar0.value + dcaPar1.value / track.pt())) { + return false; + } + return true; } template void fillHistograms(T1 const& part0, T1 const& part1, bool ME, bool isIdentical) { - Pair->SetPair(&part0, &part1); - Pair->SetIdentical(isIdentical); - if (isIdentical && Pair->IsClosePair(dEta, dPhi, radiusTPC)) { - QA.fill(HIST("QA/hdEtadPhistar"), Pair->GetPhiStarDiff(radiusTPC), Pair->GetEtaDiff()); + pair->SetPair(&part0, &part1); + pair->SetIdentical(isIdentical); + if (isIdentical && pair->IsClosePair(dEta, dPhi, radiusTPC)) { return; } - float deltaEta = part0.eta() - part1.eta(); - float deltaPhi = part0.phi() - part1.phi(); - deltaPhi = RecoDecay::constrainAngle(deltaPhi, -1 * o2::constants::math::PIHalf); + if (doClosePairRejection && pair->IsClosePair(dEta, dPhi, radiusTPC)) { + return; + } + registryQa.fill(HIST("QA/hdEtadPhistar"), pair->GetPhiStarDiff(radiusTPC), pair->GetEtaDiff()); + + const float deltaEta = part0.eta() - part1.eta(); + const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); + const float deltaRapidity = part0.rapidity(massPart0Part1.first) - part1.rapidity(massPart0Part1.second); + // Use rapidity difference if doRapidity is true, otherwise use pseudorapidity difference + const float x = doRapidity ? deltaRapidity : deltaEta; for (int k = 0; k < nBinspT; k++) { @@ -573,103 +694,109 @@ struct HadronNucleiCorrelation { float corr0 = 1, corr1 = 1; - if (doCorrection) { // Apply corrections + if (doCorrection && correctionsLoaded) { // Apply corrections (only if all efficiency histograms were retrieved) switch (mode) { - case 0: - corr0 = hEffpTEta_antideuteron->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_antiproton->Interpolate(part1.pt(), part1.eta()); + case kDbarPbar: + corr0 = hEffPtEtaAntiDeuteron->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaAntiProton->Interpolate(part1.pt(), part1.eta()); break; - case 1: - corr0 = hEffpTEta_deuteron->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_proton->Interpolate(part1.pt(), part1.eta()); + case kDP: + corr0 = hEffPtEtaDeuteron->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaProton->Interpolate(part1.pt(), part1.eta()); break; - case 2: - corr0 = hEffpTEta_antideuteron->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_proton->Interpolate(part1.pt(), part1.eta()); + case kDbarP: + corr0 = hEffPtEtaAntiDeuteron->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaProton->Interpolate(part1.pt(), part1.eta()); break; - case 3: - corr0 = hEffpTEta_deuteron->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_antiproton->Interpolate(part1.pt(), part1.eta()); + case kDPbar: + corr0 = hEffPtEtaDeuteron->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaAntiProton->Interpolate(part1.pt(), part1.eta()); break; - case 4: - corr0 = hEffpTEta_antiproton->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_proton->Interpolate(part1.pt(), part1.eta()); + case kPbarP: + corr0 = hEffPtEtaAntiProton->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaProton->Interpolate(part1.pt(), part1.eta()); break; - case 5: - corr0 = hEffpTEta_antiproton->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_antiproton->Interpolate(part1.pt(), part1.eta()); + case kPbarPbar: + corr0 = hEffPtEtaAntiProton->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaAntiProton->Interpolate(part1.pt(), part1.eta()); break; - case 6: - corr0 = hEffpTEta_proton->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_proton->Interpolate(part1.pt(), part1.eta()); + case kPP: + corr0 = hEffPtEtaProton->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaProton->Interpolate(part1.pt(), part1.eta()); break; - case 7: - corr0 = hEffpTEta_proton->Interpolate(part0.pt(), part0.eta()); - corr1 = hEffpTEta_antiproton->Interpolate(part1.pt(), part1.eta()); + case kPPbar: + corr0 = hEffPtEtaProton->Interpolate(part0.pt(), part0.eta()); + corr1 = hEffPtEtaAntiProton->Interpolate(part1.pt(), part1.eta()); + break; + default: + LOG(error) << "Unknown mode for efficiency correction: " << mode; break; } } if (ME) { - hEtaPhi_ME[k]->Fill(deltaEta, deltaPhi, part1.pt()); - hCorrEtaPhi_ME[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + hDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt()); + if (corr0 != 0 && corr1 != 0) { + hCorrDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + } } else { - hEtaPhi_SE[k]->Fill(deltaEta, deltaPhi, part1.pt()); - hCorrEtaPhi_SE[k]->Fill(deltaEta, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + hDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt()); + if (corr0 != 0 && corr1 != 0) { + hCorrDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt(), 1. / (corr0 * corr1)); + } } // SE } // pT condition } // nBinspT loop - Pair->ResetPair(); + pair->ResetPair(); } template - void fillHistogramsGen(T1 const& part0, T1 const& part1, bool ME) + void fillHistogramsGen(T1 const& part0, T1 const& part1, const bool ME) { - float deltaEta = part0.eta() - part1.eta(); - float deltaPhi = part0.phi() - part1.phi(); - deltaPhi = RecoDecay::constrainAngle(deltaPhi, -1 * o2::constants::math::PIHalf); - + const float deltaEta = part0.eta() - part1.eta(); + const float deltaPhi = RecoDecay::constrainAngle(part0.phi() - part1.phi(), -1 * o2::constants::math::PIHalf); + const float deltaRapidity = part0.rapidityForPdg(pdgPart0) - part1.rapidityForPdg(pdgPart1); + // Use rapidity difference if doRapidity is true, otherwise use pseudorapidity difference + const float x = doRapidity ? deltaRapidity : deltaEta; for (int k = 0; k < nBinspT; k++) { - if (part0.pt() >= pTBins.value.at(k) && part0.pt() < pTBins.value.at(k + 1)) { - if (ME) { - hEtaPhi_ME[k]->Fill(deltaEta, deltaPhi, part1.pt()); - hCorrEtaPhi_ME[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt()); + hCorrDeltaPhiMixdEv[k]->Fill(x, deltaPhi, part1.pt()); } else { - hEtaPhi_SE[k]->Fill(deltaEta, deltaPhi, part1.pt()); - hCorrEtaPhi_SE[k]->Fill(deltaEta, deltaPhi, part1.pt()); + hDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt()); + hCorrDeltaPhiSameEv[k]->Fill(x, deltaPhi, part1.pt()); } // SE } // pT condition } // nBinspT loop } - void GetCorrection(o2::framework::Service const& ccdbObj, TString filepath, TString histname) + void getCorrection(o2::framework::Service const& ccdbObj, const TString& filepath, const TString& histname) { - TList* l = ccdbObj->get(filepath.Data()); + auto* l = ccdbObj->get(filepath.Data()); if (!l) { LOGP(error, "Could not open corrections file {}", Form("%s", filepath.Data())); return; } - hEffpTEta_proton = static_cast(l->FindObject(Form("%s_proton", histname.Data()))); - if (!hEffpTEta_proton) { + hEffPtEtaProton = dynamic_cast(l->FindObject(Form("%s_proton", histname.Data()))); + if (!hEffPtEtaProton) { LOGP(error, "Could not open histogram {}", Form("%s_proton", histname.Data())); return; } - hEffpTEta_antiproton = static_cast(l->FindObject(Form("%s_antiproton", histname.Data()))); - if (!hEffpTEta_antiproton) { + hEffPtEtaAntiProton = dynamic_cast(l->FindObject(Form("%s_antiproton", histname.Data()))); + if (!hEffPtEtaAntiProton) { LOGP(error, "Could not open histogram {}", Form("%s_antiproton", histname.Data())); return; } - hEffpTEta_deuteron = static_cast(l->FindObject(Form("%s_deuteron", histname.Data()))); - if (!hEffpTEta_deuteron) { + hEffPtEtaDeuteron = dynamic_cast(l->FindObject(Form("%s_deuteron", histname.Data()))); + if (!hEffPtEtaDeuteron) { LOGP(error, "Could not open histogram {}", Form("%s_deuteron", histname.Data())); return; } - hEffpTEta_antideuteron = static_cast(l->FindObject(Form("%s_antideuteron", histname.Data()))); - if (!hEffpTEta_antideuteron) { + hEffPtEtaAntiDeuteron = dynamic_cast(l->FindObject(Form("%s_antideuteron", histname.Data()))); + if (!hEffPtEtaAntiDeuteron) { LOGP(error, "Could not open histogram {}", Form("%s_antideuteron", histname.Data())); return; } @@ -677,6 +804,178 @@ struct HadronNucleiCorrelation { LOGP(info, "Opened histogram {}", Form("%s_antiproton", histname.Data())); LOGP(info, "Opened histogram {}", Form("%s_deuteron", histname.Data())); LOGP(info, "Opened histogram {}", Form("%s_antideuteron", histname.Data())); + correctionsLoaded = true; + } + + // Generated-level candidate selection, shared by same- and mixed-event processing: + // same cuts as the pairing (primary if requested, |eta| <= etaCut, PDG code of one of the two species) + template + void addGenCandidate(TParticle const& particle, GenCollisionCache& colCache) + { + if (isPrim && !particle.isPhysicalPrimary()) { + return; + } + if (doRapidity) { + if (std::abs(particle.y()) > etaCut) { + return; + } + } else { + if (std::abs(particle.eta()) > etaCut) { + return; + } + } + const int pdg = particle.pdgCode(); + if (pdg != pdgPart0 && pdg != pdgPart1) { + return; + } + const GenCandidate cand{particle.pt(), particle.eta(), particle.phi()}; + if (pdg == pdgPart0) { + colCache.cand0.push_back(cand); + } + if (pdg == pdgPart1) { + colCache.cand1.push_back(cand); + } + } + + // Single pass over the particles of one generated collision: charged-primary multiplicity + // (|eta| < 1) for the mixing bins and the pairing candidates + template + GenCollisionCache buildGenCollisionCache(TParticles const& particles) + { + GenCollisionCache colCache; + for (const auto& mcParticle : particles) { + addGenCandidate(mcParticle, colCache); + + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::abs(mcParticle.eta()) > 1.0f) { + continue; + } + TParticlePDG* p = pdgDB->GetParticle(mcParticle.pdgCode()); + if (!p) { // unknown PDG code (e.g. exotic fragments not in the database) + continue; + } + if (std::abs(p->Charge()) > 1E-3) { + colCache.mult++; + } + } + return colCache; + } + + template + bool selectPair(T const& part0, T const& part1) const + { + if constexpr (eventSelection) { + if (removeSameBunchPileup.value) { + if (!part0.template singleCollSel_as().isNoSameBunchPileup()) { + return false; + } + if (!part1.template singleCollSel_as().isNoSameBunchPileup()) { + return false; + } + } + } + if (part0.tpcFractionSharedCls() > maxtpcSharedCls.value) { + return false; + } + if (part0.itsNCls() < minitsNCls.value) { + return false; + } + if (part1.tpcFractionSharedCls() > maxtpcSharedCls.value) { + return false; + } + if (part1.itsNCls() < minitsNCls.value) { + return false; + } + if (!applyDCAcut(part0)) { + return false; + } + if (!applyDCAcut(part1)) { + return false; + } + // remove tracks outside pt bins + if (part0.pt() < pTBins.value.at(0) || part0.pt() >= pTBins.value.at(nBinspT)) { + return false; + } + if (part1.pt() < pTBins.value.at(0) || part1.pt() >= pTBins.value.at(nBinspT)) { + return false; + } + return true; + }; + + template + bool selectPairPid(const T& part0, const T& part1) const + { + switch (mode.value) { + case kPP: // mode 6 + if (!isProton(part0, +1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kPbarPbar: // mode 5 + if (!isProton(part0, -1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + case kDbarPbar: + if (!isDeuteron(part0, -1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + case kDP: + if (!isDeuteron(part0, +1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kDbarP: + if (!isDeuteron(part0, -1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kDPbar: + if (!isDeuteron(part0, +1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + case kPbarP: + if (!isProton(part0, -1)) { + return false; + } + if (!isProton(part1, +1)) { + return false; + } + break; + case kPPbar: + if (!isProton(part0, +1)) { + return false; + } + if (!isProton(part1, -1)) { + return false; + } + break; + default: + LOG(fatal) << "Unknown mode: " << mode; + } + return true; } void processSameEvent(FilteredCollisions::iterator const& collision, FilteredTracks const& tracks) @@ -686,106 +985,125 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hMult"), collision.mult()); for (const auto& track : tracks) { - if (track.tpcFractionSharedCls() > max_tpcSharedCls) + + if (track.tpcFractionSharedCls() > maxtpcSharedCls) { continue; - if (track.itsNCls() < min_itsNCls) + } + if (track.itsNCls() < minitsNCls) { continue; + } - if (IsProton(track, +1)) + if (isProton(track, +1)) { registry.fill(HIST("hPrDCAxy"), track.dcaXY(), track.pt()); - if (IsProton(track, -1)) + } + if (isProton(track, -1)) { registry.fill(HIST("hAntiPrDCAxy"), track.dcaXY(), track.pt()); - if (IsDeuteron(track, +1)) + } + if (isDeuteron(track, +1)) { registry.fill(HIST("hDeDCAxy"), track.dcaXY(), track.pt()); - if (IsDeuteron(track, -1)) + } + if (isDeuteron(track, -1)) { registry.fill(HIST("hAntiDeDCAxy"), track.dcaXY(), track.pt()); + } - if (!applyDCAcut(track)) + if (!applyDCAcut(track)) { continue; + } if (doQA) { - QA.fill(HIST("QA/hTPCnClusters"), track.tpcNClsFound()); - QA.fill(HIST("QA/hTPCSharedClusters"), track.tpcFractionSharedCls()); - QA.fill(HIST("QA/hTPCchi2"), track.tpcChi2NCl()); - QA.fill(HIST("QA/hTPCcrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls()); - QA.fill(HIST("QA/hITSchi2"), track.itsChi2NCl()); - QA.fill(HIST("QA/hDCAxy"), track.dcaXY(), track.pt()); - QA.fill(HIST("QA/hDCAz"), track.dcaZ(), track.pt()); - QA.fill(HIST("QA/TPCChi2VsPZ"), track.tpcInnerParam() / track.sign(), track.tpcChi2NCl()); - QA.fill(HIST("QA/hVtxZ_trk"), collision.posZ()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_El"), track.pt() * track.sign(), track.tpcNSigmaEl()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_Pr"), track.pt() * track.sign(), track.tpcNSigmaPr()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_De"), track.pt() * track.sign(), track.tpcNSigmaDe()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_Pr"), track.pt() * track.sign(), track.tofNSigmaPr()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_De"), track.pt() * track.sign(), track.tofNSigmaDe()); - QA.fill(HIST("QA/hnSigmaITSVsPt_Pr"), track.pt() * track.sign(), track.itsNSigmaPr()); - QA.fill(HIST("QA/hnSigmaITSVsPt_De"), track.pt() * track.sign(), track.itsNSigmaDe()); - - if (IsProton(track, +1)) { - QA.fill(HIST("QA/hEtaAntiPr"), track.eta()); - QA.fill(HIST("QA/hPhiAntiPr"), track.phi()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tofNSigmaPr()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaPr()); - QA.fill(HIST("QA/hnSigmaITSVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.itsNSigmaPr()); - } - if (IsProton(track, -1)) { - QA.fill(HIST("QA/hEtaPr"), track.eta()); - QA.fill(HIST("QA/hPhiPr"), track.phi()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tofNSigmaPr()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaPr()); - QA.fill(HIST("QA/hnSigmaITSVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.itsNSigmaPr()); - } - if (IsDeuteron(track, +1)) { - QA.fill(HIST("QA/hEtaAntiDe"), track.eta()); - QA.fill(HIST("QA/hPhiAntiDe"), track.phi()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_De_AfterSel"), track.pt() * track.sign(), track.tofNSigmaDe()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_De_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaDe()); - QA.fill(HIST("QA/hnSigmaITSVsPt_De_AfterSel"), track.pt() * track.sign(), track.itsNSigmaDe()); - } - if (IsDeuteron(track, -1)) { - QA.fill(HIST("QA/hEtaDe"), track.eta()); - QA.fill(HIST("QA/hPhiDe"), track.phi()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_De_AfterSel"), track.pt() * track.sign(), track.tofNSigmaDe()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_De_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaDe()); - QA.fill(HIST("QA/hnSigmaITSVsPt_De_AfterSel"), track.pt() * track.sign(), track.itsNSigmaDe()); + registryQa.fill(HIST("QA/hTPCnClusters"), track.tpcNClsFound()); + registryQa.fill(HIST("QA/hTPCSharedClusters"), track.tpcFractionSharedCls()); + registryQa.fill(HIST("QA/hTPCchi2"), track.tpcChi2NCl()); + registryQa.fill(HIST("QA/hTPCcrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls()); + registryQa.fill(HIST("QA/hITSchi2"), track.itsChi2NCl()); + registryQa.fill(HIST("QA/hDCAxy"), track.dcaXY(), track.pt()); + registryQa.fill(HIST("QA/hDCAz"), track.dcaZ(), track.pt()); + registryQa.fill(HIST("QA/TPCChi2VsPZ"), track.tpcInnerParam() / track.sign(), track.tpcChi2NCl()); + registryQa.fill(HIST("QA/hVtxZ_trk"), collision.posZ()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_El"), track.pt() * track.sign(), track.tpcNSigmaEl()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_Pr"), track.pt() * track.sign(), track.tpcNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_De"), track.pt() * track.sign(), track.tpcNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_Pr"), track.pt() * track.sign(), track.tofNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De"), track.pt() * track.sign(), track.tofNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr"), track.pt() * track.sign(), track.itsNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_De"), track.pt() * track.sign(), track.itsNSigmaDe()); + + const bool isTPCElRejection = rejectionEl.value && track.beta() < BetahasTOFthr && track.pt() < pTthrdeTPCEl.value && track.tpcNSigmaEl() >= nsigmaElDe.value; + const bool isTOFPID = std::abs(track.tofNSigmaDe()) < nsigmaTOF.value; + const bool isPr = (std::abs(track.tpcNSigmaPr()) < nsigmaTPCPrRej.value && std::abs(track.tofNSigmaPr()) < nsigmaTOFPrRej.value) && doPrRej.value; + + if (track.sign() > 0) { + + if (track.pt() >= pTthrprTOF.value || !doITSPID.value || track.itsNSigmaPr() > nsigmaITSPr.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_Pr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); + } + if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { + if (!isPr) { + registryQa.fill(HIST("QA/h3dTPCTOF_De"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isTOFPID || (isTPCElRejection && (!doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value))) { + registryQa.fill(HIST("QA/h3dTPCTOF_De_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } + } + } + } + if (track.sign() < 0) { + if (track.pt() >= pTthrprTOF.value || !doITSPID.value || track.itsNSigmaPr() > nsigmaITSPr.value) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiPr"), track.tpcNSigmaPr(), track.tofNSigmaPr(), track.pt()); + } + if (track.pt() >= pTthrdeTOF.value || !doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value) { + if (!isPr) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + if (isTOFPID || (isTPCElRejection && (!doITSPID.value || track.itsNSigmaDe() > nsigmaITSDe.value))) { + registryQa.fill(HIST("QA/h3dTPCTOF_AntiDe_Rej"), track.tpcNSigmaDe(), track.tofNSigmaDe(), track.pt()); + } + } + } + } + + if (isProton(track, -1)) { + registryQa.fill(HIST("QA/hEtaAntiPr"), track.eta()); + registryQa.fill(HIST("QA/hPhiAntiPr"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tofNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.itsNSigmaPr()); + } + if (isProton(track, +1)) { + registryQa.fill(HIST("QA/hEtaPr"), track.eta()); + registryQa.fill(HIST("QA/hPhiPr"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tofNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.itsNSigmaPr()); + } + if (isDeuteron(track, -1)) { + registryQa.fill(HIST("QA/hEtaAntiDe"), track.eta()); + registryQa.fill(HIST("QA/hPhiAntiDe"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De_AfterSel"), track.pt() * track.sign(), track.tofNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_De_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_De_AfterSel"), track.pt() * track.sign(), track.itsNSigmaDe()); + } + if (isDeuteron(track, +1)) { + registryQa.fill(HIST("QA/hEtaDe"), track.eta()); + registryQa.fill(HIST("QA/hPhiDe"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De_AfterSel"), track.pt() * track.sign(), track.tofNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_De_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_De_AfterSel"), track.pt() * track.sign(), track.itsNSigmaDe()); } } } - Pair->SetMagField1(collision.magField()); - Pair->SetMagField2(collision.magField()); + pair->SetMagField1(collision.magField()); + pair->SetMagField2(collision.magField()); if (mode == kPbarPbar || mode == kPP) { // Identical particle combinations for (const auto& [part0, part1] : combinations(CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { - if (part0.tpcFractionSharedCls() > max_tpcSharedCls) - continue; - if (part0.itsNCls() < min_itsNCls) - continue; - if (part1.tpcFractionSharedCls() > max_tpcSharedCls) - continue; - if (part1.itsNCls() < min_itsNCls) + if (!selectPair(part0, part1)) { continue; + } - if (!applyDCAcut(part0)) - continue; - if (!applyDCAcut(part1)) + if (!selectPairPid(part0, part1)) { continue; - - // mode 6 - if (mode == kPP) { - if (!IsProton(part0, +1)) - continue; - if (!IsProton(part1, +1)) - continue; - } - // mode 5 - if (mode == kPbarPbar) { - if (!IsProton(part0, -1)) - continue; - if (!IsProton(part1, -1)) - continue; } fillHistograms(part0, part1, false, true); @@ -795,63 +1113,147 @@ struct HadronNucleiCorrelation { for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { - if (part0.tpcFractionSharedCls() > max_tpcSharedCls) - continue; - if (part0.itsNCls() < min_itsNCls) - continue; - if (part1.tpcFractionSharedCls() > max_tpcSharedCls) - continue; - if (part1.itsNCls() < min_itsNCls) + if (part0.globalIndex() == part1.globalIndex()) { continue; + } - if (!applyDCAcut(part0)) + if (!selectPair(part0, part1)) { continue; - if (!applyDCAcut(part1)) + } + + if (!selectPairPid(part0, part1)) { continue; + } - // modes 0,1,2,3,4,7 - if (mode == kDbarPbar) { - if (!IsDeuteron(part0, -1)) - continue; - if (!IsProton(part1, -1)) - continue; + fillHistograms(part0, part1, false, false); + } + } + } + PROCESS_SWITCH(HadronNucleiCorrelation, processSameEvent, "processSameEvent", true); + + void processSameEventEvSel(FilteredCollisionsExtra::iterator const& collision, FilteredTracks const& tracks) + { + + registry.fill(HIST("hNEvents"), 0.5); + registry.fill(HIST("hMult"), collision.mult()); + + for (const auto& track : tracks) { + + if (removeSameBunchPileup && !track.template singleCollSel_as().isNoSameBunchPileup()) { + continue; + } + + if (track.tpcFractionSharedCls() > maxtpcSharedCls) { + continue; + } + if (track.itsNCls() < minitsNCls) { + continue; + } + + if (isProton(track, +1)) { + registry.fill(HIST("hPrDCAxy"), track.dcaXY(), track.pt()); + } + if (isProton(track, -1)) { + registry.fill(HIST("hAntiPrDCAxy"), track.dcaXY(), track.pt()); + } + if (isDeuteron(track, +1)) { + registry.fill(HIST("hDeDCAxy"), track.dcaXY(), track.pt()); + } + if (isDeuteron(track, -1)) { + registry.fill(HIST("hAntiDeDCAxy"), track.dcaXY(), track.pt()); + } + + if (!applyDCAcut(track)) { + continue; + } + + if (doQA) { + registryQa.fill(HIST("QA/hTPCnClusters"), track.tpcNClsFound()); + registryQa.fill(HIST("QA/hTPCSharedClusters"), track.tpcFractionSharedCls()); + registryQa.fill(HIST("QA/hTPCchi2"), track.tpcChi2NCl()); + registryQa.fill(HIST("QA/hTPCcrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls()); + registryQa.fill(HIST("QA/hITSchi2"), track.itsChi2NCl()); + registryQa.fill(HIST("QA/hDCAxy"), track.dcaXY(), track.pt()); + registryQa.fill(HIST("QA/hDCAz"), track.dcaZ(), track.pt()); + registryQa.fill(HIST("QA/TPCChi2VsPZ"), track.tpcInnerParam() / track.sign(), track.tpcChi2NCl()); + registryQa.fill(HIST("QA/hVtxZ_trk"), collision.posZ()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_El"), track.pt() * track.sign(), track.tpcNSigmaEl()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_Pr"), track.pt() * track.sign(), track.tpcNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_De"), track.pt() * track.sign(), track.tpcNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_Pr"), track.pt() * track.sign(), track.tofNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De"), track.pt() * track.sign(), track.tofNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr"), track.pt() * track.sign(), track.itsNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_De"), track.pt() * track.sign(), track.itsNSigmaDe()); + + if (isProton(track, -1)) { + registryQa.fill(HIST("QA/hEtaAntiPr"), track.eta()); + registryQa.fill(HIST("QA/hPhiAntiPr"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tofNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.itsNSigmaPr()); + } + if (isProton(track, +1)) { + registryQa.fill(HIST("QA/hEtaPr"), track.eta()); + registryQa.fill(HIST("QA/hPhiPr"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tofNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_Pr_AfterSel"), track.pt() * track.sign(), track.itsNSigmaPr()); + } + if (isDeuteron(track, -1)) { + registryQa.fill(HIST("QA/hEtaAntiDe"), track.eta()); + registryQa.fill(HIST("QA/hPhiAntiDe"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De_AfterSel"), track.pt() * track.sign(), track.tofNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_De_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_De_AfterSel"), track.pt() * track.sign(), track.itsNSigmaDe()); + } + if (isDeuteron(track, +1)) { + registryQa.fill(HIST("QA/hEtaDe"), track.eta()); + registryQa.fill(HIST("QA/hPhiDe"), track.phi()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De_AfterSel"), track.pt() * track.sign(), track.tofNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_De_AfterSel"), track.pt() * track.sign(), track.tpcNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaITSVsPt_De_AfterSel"), track.pt() * track.sign(), track.itsNSigmaDe()); } - if (mode == kDP) { - if (!IsDeuteron(part0, +1)) - continue; - if (!IsProton(part1, +1)) - continue; + } + } + + pair->SetMagField1(collision.magField()); + pair->SetMagField2(collision.magField()); + + if (mode == kPbarPbar || mode == kPP) { // Identical particle combinations (only pp or pbarpbar) + + for (const auto& [part0, part1] : combinations(CombinationsStrictlyUpperIndexPolicy(tracks, tracks))) { + + if (!selectPair(part0, part1)) { + continue; } - if (mode == kDbarP) { - if (!IsDeuteron(part0, -1)) - continue; - if (!IsProton(part1, +1)) - continue; + + if (!selectPairPid(part0, part1)) { + continue; } - if (mode == kDPbar) { - if (!IsDeuteron(part0, +1)) - continue; - if (!IsProton(part1, -1)) - continue; + + fillHistograms(part0, part1, false, true); + } + + } else { + + for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + if (part0.globalIndex() == part1.globalIndex()) { + continue; } - if (mode == kPbarP) { - if (!IsProton(part0, -1)) - continue; - if (!IsProton(part1, +1)) - continue; + if (!selectPair(part0, part1)) { + continue; } - if (mode == kPPbar) { - if (!IsProton(part0, +1)) - continue; - if (!IsProton(part1, -1)) - continue; + + if (!selectPairPid(part0, part1)) { + continue; } fillHistograms(part0, part1, false, false); } } } - PROCESS_SWITCH(HadronNucleiCorrelation, processSameEvent, "processSameEvent", true); + PROCESS_SWITCH(HadronNucleiCorrelation, processSameEventEvSel, "processSameEventEvSel", false); void processMixedEvent(FilteredCollisions const& collisions, FilteredTracks const& tracks) { @@ -866,180 +1268,178 @@ struct HadronNucleiCorrelation { const auto& magFieldTesla1 = collision1.magField(); const auto& magFieldTesla2 = collision2.magField(); - if (magFieldTesla1 != magFieldTesla2) { + const float limit = 1e-4; + + if (std::abs(magFieldTesla1 - magFieldTesla2) > limit) { continue; } - Pair->SetMagField1(magFieldTesla1); - Pair->SetMagField2(magFieldTesla2); + pair->SetMagField1(magFieldTesla1); + pair->SetMagField2(magFieldTesla2); for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { - if (part0.tpcFractionSharedCls() > max_tpcSharedCls) - continue; - if (part0.itsNCls() < min_itsNCls) - continue; - if (part1.tpcFractionSharedCls() > max_tpcSharedCls) - continue; - if (part1.itsNCls() < min_itsNCls) + if (!selectPair(part0, part1)) { continue; + } - if (!applyDCAcut(part0)) - continue; - if (!applyDCAcut(part1)) + if (!selectPairPid(part0, part1)) { continue; - - //{"mode", 0, "0: antid-antip, 1: d-p, 2: antid-p, 3: d-antip, 4: antip-p, 5: antip-antip, 6: p-p, 7: p-antip"}; - if (mode == kDbarPbar) { - if (!IsDeuteron(part0, -1)) - continue; - if (!IsProton(part1, -1)) - continue; - } - if (mode == kDP) { - if (!IsDeuteron(part0, +1)) - continue; - if (!IsProton(part1, +1)) - continue; - } - if (mode == kDbarP) { - if (!IsDeuteron(part0, -1)) - continue; - if (!IsProton(part1, +1)) - continue; - } - if (mode == kDPbar) { - if (!IsDeuteron(part0, +1)) - continue; - if (!IsProton(part1, -1)) - continue; - } - if (mode == kPbarP) { - if (!IsProton(part0, -1)) - continue; - if (!IsProton(part1, +1)) - continue; - } - if (mode == kPbarPbar) { - if (!IsProton(part0, -1)) - continue; - if (!IsProton(part1, -1)) - continue; - } - if (mode == kPP) { - if (!IsProton(part0, +1)) - continue; - if (!IsProton(part1, +1)) - continue; - } - if (mode == kPPbar) { - if (!IsProton(part0, +1)) - continue; - if (!IsProton(part1, -1)) - continue; } - bool isIdentical = false; - if (mode == kPbarPbar || mode == kPP) - isIdentical = true; - + const bool isIdentical = (mode == kPbarPbar || mode == kPP); fillHistograms(part0, part1, true, isIdentical); } } } PROCESS_SWITCH(HadronNucleiCorrelation, processMixedEvent, "processMixedEvent", true); + void processMixedEventEvSel(FilteredCollisionsExtra const& collisions, FilteredTracks const& tracks) + { + + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinning, 5, -1, collisions, collisions)) { + + // LOGF(info, "Mixed event collisions: (%d, %d) zvtx (%.1f, %.1f) mult (%d, %d)", collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), collision1.mult(), collision2.mult()); + + auto groupPartsOne = tracks.sliceByCached(o2::aod::singletrackselector::singleCollSelId, collision1.globalIndex(), cache); + auto groupPartsTwo = tracks.sliceByCached(o2::aod::singletrackselector::singleCollSelId, collision2.globalIndex(), cache); + + const auto& magFieldTesla1 = collision1.magField(); + const auto& magFieldTesla2 = collision2.magField(); + + constexpr float limit = 1e-4; + + if (std::abs(magFieldTesla1 - magFieldTesla2) > limit) { + continue; + } + + pair->SetMagField1(magFieldTesla1); + pair->SetMagField2(magFieldTesla2); + + for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + + if (!selectPair(part0, part1)) { + continue; + } + + if (!selectPairPid(part0, part1)) { + continue; + } + const bool isIdentical = (mode == kPbarPbar || mode == kPP); + fillHistograms(part0, part1, true, isIdentical); + } + } + } + PROCESS_SWITCH(HadronNucleiCorrelation, processMixedEventEvSel, "processMixedEventEvSel", false); + void processMC(FilteredCollisions const&, FilteredTracksMC const& tracks) { for (const auto& track : tracks) { - if (std::abs(track.template singleCollSel_as().posZ()) > cutzVertex) + if (std::abs(track.template singleCollSel_as().posZ()) > cutzVertex) { continue; + } - if (track.tpcFractionSharedCls() > max_tpcSharedCls) + if (track.tpcFractionSharedCls() > maxtpcSharedCls) { continue; - if (track.itsNCls() < min_itsNCls) + } + if (track.itsNCls() < minitsNCls) { continue; + } - if (IsProton(track, +1) && track.pdgCode() == PDG_t::kProton) { + if (isProton(track, +1) && track.pdgCode() == PDG_t::kProton) { registry.fill(HIST("hPrDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 0) + if (track.origin() == kPrimary) { registry.fill(HIST("hPrimPrDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 1) + } + if (track.origin() == kWeakDecay) { registry.fill(HIST("hSecWeakPrDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 2) + } + if (track.origin() == kMaterial) { registry.fill(HIST("hSecMatPrDCAxy"), track.dcaXY(), track.pt()); + } } - if (IsProton(track, -1) && track.pdgCode() == -PDG_t::kProton) { + if (isProton(track, -1) && track.pdgCode() == -PDG_t::kProton) { registry.fill(HIST("hAntiPrDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 0) + if (track.origin() == kPrimary) { registry.fill(HIST("hPrimAntiPrDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 1) + } + if (track.origin() == kWeakDecay) { registry.fill(HIST("hSecWeakAntiPrDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 2) + } + if (track.origin() == kMaterial) { registry.fill(HIST("hSecMatAntiPrDCAxy"), track.dcaXY(), track.pt()); + } } - if (IsDeuteron(track, +1) && track.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { + if (isDeuteron(track, +1) && track.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { registry.fill(HIST("hDeDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 0) + if (track.origin() == kPrimary) { registry.fill(HIST("hPrimDeDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 1) + } + if (track.origin() == kWeakDecay) { registry.fill(HIST("hSecWeakDeDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 2) + } + if (track.origin() == kMaterial) { registry.fill(HIST("hSecMatDeDCAxy"), track.dcaXY(), track.pt()); + } } - if (IsDeuteron(track, -1) && track.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { + if (isDeuteron(track, -1) && track.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { registry.fill(HIST("hAntiDeDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 0) + if (track.origin() == kPrimary) { registry.fill(HIST("hPrimAntiDeDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 1) + } + if (track.origin() == kWeakDecay) { registry.fill(HIST("hSecWeakAntiDeDCAxy"), track.dcaXY(), track.pt()); - if (track.origin() == 2) + } + if (track.origin() == kMaterial) { registry.fill(HIST("hSecMatAntiDeDCAxy"), track.dcaXY(), track.pt()); + } } - if (!applyDCAcut(track)) + if (!applyDCAcut(track)) { continue; + } // Keep only protons and deuterons // if (std::abs(track.pdgCode()) != PDG_t::kProton && std::abs(track.pdgCode()) != o2::constants::physics::Pdg::kDeuteron) // continue; if (doQA) { - QA.fill(HIST("QA/hTPCnClusters"), track.tpcNClsFound()); - QA.fill(HIST("QA/hTPCSharedClusters"), track.tpcFractionSharedCls()); - QA.fill(HIST("QA/hTPCchi2"), track.tpcChi2NCl()); - QA.fill(HIST("QA/hTPCcrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls()); - QA.fill(HIST("QA/hITSchi2"), track.itsChi2NCl()); - QA.fill(HIST("QA/hDCAxy"), track.dcaXY(), track.pt()); - QA.fill(HIST("QA/hDCAz"), track.dcaZ(), track.pt()); - QA.fill(HIST("QA/hVtxZ_trk"), track.template singleCollSel_as().posZ()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_El"), track.pt() * track.sign(), track.tpcNSigmaEl()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_Pr"), track.pt() * track.sign(), track.tpcNSigmaPr()); - QA.fill(HIST("QA/hnSigmaTPCVsPt_De"), track.pt() * track.sign(), track.tpcNSigmaDe()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_Pr"), track.pt() * track.sign(), track.tofNSigmaPr()); - QA.fill(HIST("QA/hnSigmaTOFVsPt_De"), track.pt() * track.sign(), track.tofNSigmaDe()); - } - - bool isPr = (IsProton(track, +1) && track.pdgCode() == PDG_t::kProton); - bool isAntiPr = (IsProton(track, -1) && track.pdgCode() == -PDG_t::kProton); - bool isDe = (IsDeuteron(track, +1) && track.pdgCode() == o2::constants::physics::Pdg::kDeuteron); - bool isAntiDe = (IsDeuteron(track, -1) && track.pdgCode() == -o2::constants::physics::Pdg::kDeuteron); + registryQa.fill(HIST("QA/hTPCnClusters"), track.tpcNClsFound()); + registryQa.fill(HIST("QA/hTPCSharedClusters"), track.tpcFractionSharedCls()); + registryQa.fill(HIST("QA/hTPCchi2"), track.tpcChi2NCl()); + registryQa.fill(HIST("QA/hTPCcrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls()); + registryQa.fill(HIST("QA/hITSchi2"), track.itsChi2NCl()); + registryQa.fill(HIST("QA/hDCAxy"), track.dcaXY(), track.pt()); + registryQa.fill(HIST("QA/hDCAz"), track.dcaZ(), track.pt()); + registryQa.fill(HIST("QA/hVtxZ_trk"), track.template singleCollSel_as().posZ()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_El"), track.pt() * track.sign(), track.tpcNSigmaEl()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_Pr"), track.pt() * track.sign(), track.tpcNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTPCVsPt_De"), track.pt() * track.sign(), track.tpcNSigmaDe()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_Pr"), track.pt() * track.sign(), track.tofNSigmaPr()); + registryQa.fill(HIST("QA/hnSigmaTOFVsPt_De"), track.pt() * track.sign(), track.tofNSigmaDe()); + } + + bool isPr = (isProton(track, +1) && track.pdgCode() == PDG_t::kProton); + bool isAntiPr = (isProton(track, -1) && track.pdgCode() == -PDG_t::kProton); + bool isDe = (isDeuteron(track, +1) && track.pdgCode() == o2::constants::physics::Pdg::kDeuteron); + bool isAntiDe = (isDeuteron(track, -1) && track.pdgCode() == -o2::constants::physics::Pdg::kDeuteron); if (isPr) { registry.fill(HIST("hPrimSec_EtaPhiPt_Proton"), track.eta(), track.phi(), track.pt() * +1); - if (track.origin() == 1 || track.origin() == 2) { // secondaries + if (track.origin() == kWeakDecay || track.origin() == kMaterial) { // secondaries registry.fill(HIST("hSec_EtaPhiPt_Proton"), track.eta(), track.phi(), track.pt() * +1); } } if (isAntiPr) { registry.fill(HIST("hPrimSec_EtaPhiPt_Proton"), track.eta(), track.phi(), track.pt() * -1); - if (track.origin() == 1 || track.origin() == 2) { + if (track.origin() == kWeakDecay || track.origin() == kMaterial) { registry.fill(HIST("hSec_EtaPhiPt_Proton"), track.eta(), track.phi(), track.pt() * -1); } } - if (track.origin() != 0) + if (track.origin() != 0) { continue; + } if (track.pdgCode() == PDG_t::kProton) { registry.fill(HIST("hReco_EtaPhiPt_Proton"), track.eta(), track.phi(), track.pt()); @@ -1116,14 +1516,18 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Deuteron"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Deuteron"), track.pt() * -1); } - if (IsProton(track, +1)) + if (isProton(track, +1)) { registry.fill(HIST("hDenominatorPurity_Proton"), track.pt()); - if (IsProton(track, -1)) + } + if (isProton(track, -1)) { registry.fill(HIST("hDenominatorPurity_Proton"), track.pt() * -1); - if (IsDeuteron(track, +1)) + } + if (isDeuteron(track, +1)) { registry.fill(HIST("hDenominatorPurity_Deuteron"), track.pt()); - if (IsDeuteron(track, -1)) + } + if (isDeuteron(track, -1)) { registry.fill(HIST("hDenominatorPurity_Deuteron"), track.pt() * -1); + } if (doMCQA) { // Proton @@ -1136,8 +1540,8 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Proton_TPCTOF"), track.pt()); registry.fill(HIST("hReco_Pt_Proton_TPCTOF"), track.pt()); } - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && track.pdgCode() == PDG_t::kProton) { registry.fill(HIST("hNumeratorPurity_Proton_TPC_or_TOF"), track.pt()); registry.fill(HIST("hReco_Pt_Proton_TPC_or_TOF"), track.pt()); @@ -1147,8 +1551,8 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Proton_TPCEl"), track.pt()); registry.fill(HIST("hReco_Pt_Proton_TPCEl"), track.pt()); } - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && track.pdgCode() == PDG_t::kProton) { registry.fill(HIST("hNumeratorPurity_Proton_TPCEl_or_TOF"), track.pt()); registry.fill(HIST("hReco_Pt_Proton_TPCEl_or_TOF"), track.pt()); @@ -1164,8 +1568,8 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Proton_TPCTOF"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Proton_TPCTOF"), track.pt() * -1); } - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && track.pdgCode() == -PDG_t::kProton) { registry.fill(HIST("hNumeratorPurity_Proton_TPC_or_TOF"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Proton_TPC_or_TOF"), track.pt() * -1); @@ -1175,8 +1579,8 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Proton_TPCEl"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Proton_TPCEl"), track.pt() * -1); } - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && track.pdgCode() == -PDG_t::kProton) { registry.fill(HIST("hNumeratorPurity_Proton_TPCEl_or_TOF"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Proton_TPCEl_or_TOF"), track.pt() * -1); @@ -1192,8 +1596,8 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Deuteron_TPCTOF"), track.pt()); registry.fill(HIST("hReco_Pt_Deuteron_TPCTOF"), track.pt()); } - if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && track.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { registry.fill(HIST("hNumeratorPurity_Deuteron_TPC_or_TOF"), track.pt()); registry.fill(HIST("hReco_Pt_Deuteron_TPC_or_TOF"), track.pt()); @@ -1203,8 +1607,8 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Deuteron_TPCEl"), track.pt()); registry.fill(HIST("hReco_Pt_Deuteron_TPCEl"), track.pt()); } - if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && track.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { registry.fill(HIST("hNumeratorPurity_Deuteron_TPCEl_or_TOF"), track.pt()); registry.fill(HIST("hReco_Pt_Deuteron_TPCEl_or_TOF"), track.pt()); @@ -1220,8 +1624,8 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Deuteron_TPCTOF"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Deuteron_TPCTOF"), track.pt() * -1); } - if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && track.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { registry.fill(HIST("hNumeratorPurity_Deuteron_TPC_or_TOF"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Deuteron_TPC_or_TOF"), track.pt() * -1); @@ -1231,56 +1635,64 @@ struct HadronNucleiCorrelation { registry.fill(HIST("hNumeratorPurity_Deuteron_TPCEl"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Deuteron_TPCEl"), track.pt() * -1); } - if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && track.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { registry.fill(HIST("hNumeratorPurity_Deuteron_TPCEl_or_TOF"), track.pt() * -1); registry.fill(HIST("hReco_Pt_Deuteron_TPCEl_or_TOF"), track.pt() * -1); } // Denominators - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.sign() > 0) + if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPC"), track.pt()); - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF && track.sign() > 0) + } + if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPCTOF"), track.pt()); - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && - track.sign() > 0) + } + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPC_or_TOF"), track.pt()); + } if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPCEl"), track.pt()); } - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPCEl_or_TOF"), track.pt()); } - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.sign() < 0) + if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPC"), track.pt() * -1); - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF && track.sign() < 0) + } + if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF && track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPCTOF"), track.pt() * -1); - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && - track.sign() < 0) + } + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPC_or_TOF"), track.pt() * -1); + } if (std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPCEl"), track.pt() * -1); } - if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && + if (((std::abs(track.tpcNSigmaPr()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElPr && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaPr()) < nsigmaTPC && std::abs(track.tofNSigmaPr()) < nsigmaTOF)) && track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Proton_TPCEl_or_TOF"), track.pt() * -1); } - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.sign() > 0) + if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPC"), track.pt()); - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF && track.sign() > 0) + } + if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPCTOF"), track.pt()); - if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + } + if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPC_or_TOF"), track.pt()); } @@ -1288,28 +1700,33 @@ struct HadronNucleiCorrelation { track.tpcNSigmaEl() >= nsigmaElDe && track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPCEl"), track.pt()); } - if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && - track.sign() > 0) + if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + track.sign() > 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPCEl_or_TOF"), track.pt()); + } - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.sign() < 0) + if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPC"), track.pt() * -1); - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF && track.sign() < 0) + } + if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF && track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPCTOF"), track.pt() * -1); + } if (( - (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && - track.sign() < 0) + (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPC_or_TOF"), track.pt() * -1); + } if (std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPCEl"), track.pt() * -1); } - if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < betahasTOFthr) || - (track.beta() > betahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && - track.sign() < 0) + if (((std::abs(track.tpcNSigmaDe()) < nsigmaTPC && track.tpcNSigmaEl() >= nsigmaElDe && track.beta() < BetahasTOFthr) || + (track.beta() > BetahasTOFthr && std::abs(track.tpcNSigmaDe()) < nsigmaTPC && std::abs(track.tofNSigmaDe()) < nsigmaTOF)) && + track.sign() < 0) { registry.fill(HIST("hDenominatorPurity_Deuteron_TPCEl_or_TOF"), track.pt() * -1); + } } } // track } @@ -1320,146 +1737,350 @@ struct HadronNucleiCorrelation { registry.fill(HIST("Generated/hNEventsMC"), 0.5); - for (const auto& particle : mcParticles) { - - if (particle.pdgCode() == PDG_t::kProton) { - registry.fill(HIST("Generated/hQAProtons"), 0.5); + // Local representation of a generated particle. + struct GenCoalescenceCandidate { + GenCoalescenceCandidate(float pt, + float eta, + float phi, + int pdg, + float vx, + float vy, + float vz, + float vt, + bool physicalPrimary) + : mKinematics{pt, eta, phi}, + mPdg{pdg}, + mVx{vx}, + mVy{vy}, + mVz{vz}, + mVt{vt}, + mPhysicalPrimary{physicalPrimary} + { } - if (particle.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { - registry.fill(HIST("Generated/hQADeuterons"), 0.5); + [[nodiscard]] float pt() const { return mKinematics.pt(); } + [[nodiscard]] float eta() const { return mKinematics.eta(); } + [[nodiscard]] float phi() const { return mKinematics.phi(); } + + [[nodiscard]] float px() const { return pt() * std::cos(phi()); } + [[nodiscard]] float py() const { return pt() * std::sin(phi()); } + [[nodiscard]] float pz() const { return mKinematics.pz(); } + + [[nodiscard]] float vx() const { return mVx; } + [[nodiscard]] float vy() const { return mVy; } + [[nodiscard]] float vz() const { return mVz; } + [[nodiscard]] float vt() const { return mVt; } + + [[nodiscard]] float mass() const + { + switch (std::abs(mPdg)) { + case PDG_t::kProton: + return o2::track::PID::getMass(o2::track::PID::Proton); + case PDG_t::kNeutron: + return o2::constants::physics::MassNeutron; + case o2::constants::physics::Pdg::kDeuteron: + return o2::track::PID::getMass(o2::track::PID::Deuteron); + default: + LOG(fatal) << "Unhandled pdg " << mPdg; + return 0.f; + } } + [[nodiscard]] float energy() const { return std::hypot(mKinematics.p(), mass()); } + [[nodiscard]] float rapidity() const { return mKinematics.rapidityForMass(mass()); } + [[nodiscard]] float y() const { return rapidity(); } + [[nodiscard]] bool isPhysicalPrimary() const { return mPhysicalPrimary; } + [[nodiscard]] int pdgCode() const { return mPdg; } - if (isPrim && !particle.isPhysicalPrimary()) { - continue; - } - if (particle.pdgCode() == PDG_t::kProton) { - registry.fill(HIST("Generated/hQAProtons"), 1.5); - } - if (particle.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { - registry.fill(HIST("Generated/hQADeuterons"), 1.5); - } + private: + GenCandidate mKinematics; - if (particle.pdgCode() == o2::constants::physics::Pdg::kDeuteron && std::abs(particle.y()) < 0.5) { - registry.fill(HIST("Generated/hDeuteronsVsPt"), particle.pt()); - } - if (particle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron && std::abs(particle.y()) < 0.5) { - registry.fill(HIST("Generated/hAntiDeuteronsVsPt"), particle.pt()); - } + int mPdg = 0; + float mVx = 0.f; + float mVy = 0.f; + float mVz = 0.f; + float mVt = 0.f; - if (std::abs(particle.eta()) > etaCut) { - continue; - } - if (particle.pdgCode() == PDG_t::kProton) { - registry.fill(HIST("Generated/hQAProtons"), 2.5); - } - if (particle.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { - registry.fill(HIST("Generated/hQADeuterons"), 2.5); - } + bool mPhysicalPrimary = false; + }; - if (particle.pdgCode() == o2::constants::physics::Pdg::kDeuteron) { - registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), particle.eta(), particle.phi(), particle.pt()); - } - if (particle.pdgCode() == -o2::constants::physics::Pdg::kDeuteron) { - registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), particle.eta(), particle.phi(), -1. * particle.pt()); - } - if (particle.pdgCode() == PDG_t::kProton) { - registry.fill(HIST("hGen_EtaPhiPt_Proton"), particle.eta(), particle.phi(), particle.pt()); - } - if (particle.pdgCode() == -PDG_t::kProton) { - registry.fill(HIST("hGen_EtaPhiPt_Proton"), particle.eta(), particle.phi(), -1. * particle.pt()); + std::vector particlesToProcess; + particlesToProcess.reserve(mcParticles.size()); + + for (const auto& particle : mcParticles) { + switch (particle.pdgCode()) { + case PDG_t::kProton: + case PDG_t::kProtonBar: + case PDG_t::kNeutron: + case PDG_t::kNeutronBar: + case o2::constants::physics::Pdg::kDeuteron: + case -o2::constants::physics::Pdg::kDeuteron: + particlesToProcess.emplace_back(particle.pt(), + particle.eta(), + particle.phi(), + particle.pdgCode(), + particle.vx(), + particle.vy(), + particle.vz(), + particle.vt(), + particle.isPhysicalPrimary()); + break; + default: + break; } } - if (mode == kPbarPbar || mode == kPP) { // Identical particle combinations + if (settingsCoalescence.doMCGenCoalescence.value) { + std::vector consumed(particlesToProcess.size(), false); + std::vector deuterons; - for (const auto& [part0, part1] : combinations(CombinationsStrictlyUpperIndexPolicy(mcParticles, mcParticles))) { + // Try p+n -> d and pbar+nbar -> dbar independently. + for (const int sign : {+1, -1}) { + const int protonPDG = sign * PDG_t::kProton; + const int neutronPDG = sign * PDG_t::kNeutron; + const int deuteronPDG = sign * o2::constants::physics::Pdg::kDeuteron; - if (isPrim && !part0.isPhysicalPrimary()) { - continue; - } - if (isPrim && !part1.isPhysicalPrimary()) { - continue; - } - if (std::abs(part0.eta()) > etaCut) { - continue; - } - if (std::abs(part1.eta()) > etaCut) { - continue; - } - - // mode 6 - if (mode == kPP) { - if (part0.pdgCode() != PDG_t::kProton) - continue; - if (part1.pdgCode() != PDG_t::kProton) + for (size_t ip = 0; ip < particlesToProcess.size(); ++ip) { + if (consumed[ip] || particlesToProcess[ip].pdgCode() != protonPDG) { continue; + } + + const auto& proton = particlesToProcess[ip]; + + for (size_t in = 0; in < particlesToProcess.size(); ++in) { + if (consumed[in] || particlesToProcess[in].pdgCode() != neutronPDG) { + continue; + } + + const auto& neutron = particlesToProcess[in]; + + const float ep = proton.energy(); + const float en = neutron.energy(); + + // Propagate the particle freezing out first to the freeze-out time of the other particle + float xp = proton.vx(); + float yp = proton.vy(); + float zp = proton.vz(); + const float tp = proton.vt(); + + float xn = neutron.vx(); + float yn = neutron.vy(); + float zn = neutron.vz(); + const float tn = neutron.vt(); + + const float commonTime = std::max(static_cast(tp), static_cast(tn)); + + if (tp < commonTime) { + const float dt = commonTime - tp; + xp += proton.px() / ep * dt; + yp += proton.py() / ep * dt; + zp += proton.pz() / ep * dt; + } + + if (tn < commonTime) { + const float dt = commonTime - tn; + xn += neutron.px() / en * dt; + yn += neutron.py() / en * dt; + zn += neutron.pz() / en * dt; + } + + // Velocity of the p-n centre-of-mass frame. + const float totalE = ep + en; + const float totalPx = proton.px() + neutron.px(); + const float totalPy = proton.py() + neutron.py(); + const float totalPz = proton.pz() + neutron.pz(); + + const float bx = totalPx / totalE; + const float by = totalPy / totalE; + const float bz = totalPz / totalE; + + const float beta2 = bx * bx + by * by + bz * bz; + if (beta2 >= 1.) { + continue; + } + + const float gamma = 1. / std::sqrt(1. - beta2); + + // Boost the proton momentum into the p-n rest frame. + // In that frame p_p* = -p_n*, therefore |p_p*| is the relative momentum entering the coalescence cut. + float pxStar = proton.px(); + float pyStar = proton.py(); + float pzStar = proton.pz(); + + if (beta2 > 0.) { + const float betaDotP = bx * proton.px() + by * proton.py() + bz * proton.pz(); + + const float factor = ((gamma - 1.) * betaDotP / beta2) - gamma * ep; + + pxStar += factor * bx; + pyStar += factor * by; + pzStar += factor * bz; + } + + const float pRelative = std::sqrt(pxStar * pxStar + pyStar * pyStar + pzStar * pzStar); + + if (pRelative >= settingsCoalescence.pMax.value) { + continue; + } + + // The two particles have already been propagated to equal time in the lab. Transform their spatial separation to the pair CM frame. + float dx = xp - xn; + float dy = yp - yn; + float dz = zp - zn; + + if (beta2 > 0.) { + const float betaDotR = bx * dx + by * dy + bz * dz; + const float factor = (gamma - 1.) * betaDotR / beta2; + + dx += factor * bx; + dy += factor * by; + dz += factor * bz; + } + + const float rRelative = std::sqrt(dx * dx + dy * dy + dz * dz); + + if (rRelative >= settingsCoalescence.rMax.value) { + continue; + } + + // Successful coalescence: remove the constituent proton and neutron and replace them by a deuteron carrying their total three-momentum + consumed[ip] = true; + consumed[in] = true; + + const float deuteronPt = std::hypot(totalPx, totalPy); + const float deuteronPhi = std::atan2(totalPy, totalPx); + const float deuteronEta = std::asinh(totalPz / std::max(deuteronPt, 1.e-12f)); + + deuterons.emplace_back(deuteronPt, + deuteronEta, + deuteronPhi, + deuteronPDG, + 0.5f * (xp + xn), + 0.5f * (yp + yn), + 0.5f * (zp + zn), + commonTime, + proton.isPhysicalPrimary() && neutron.isPhysicalPrimary()); + break; + } } - // mode 5 - if (mode == kPbarPbar) { - if (part0.pdgCode() != -PDG_t::kProton) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; + } + + // Construct the post-coalescence particle list. + std::vector coalescedParticles; + coalescedParticles.reserve(particlesToProcess.size() + deuterons.size()); + + for (size_t i = 0; i < particlesToProcess.size(); ++i) { + if (!consumed[i]) { + coalescedParticles.push_back(std::move(particlesToProcess[i])); } + } - fillHistogramsGen(part0, part1, false); + for (auto& deuteron : deuterons) { + coalescedParticles.push_back(std::move(deuteron)); } - } else { + particlesToProcess = std::move(coalescedParticles); + } - for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(mcParticles, mcParticles))) { + // Pairing candidates are collected during the QA loop below (which already visits every particle) + GenCollisionCache genCache; + + for (const auto& particle : particlesToProcess) { + auto fillGeneratedQa = [this, &particle](const float binPosition) { + switch (particle.pdgCode()) { + case PDG_t::kProton: + registry.fill(HIST("Generated/hQAProtons"), binPosition); + return true; + case PDG_t::kProtonBar: + registry.fill(HIST("Generated/hQAAntiProtons"), binPosition); + return true; + case PDG_t::kNeutron: + registry.fill(HIST("Generated/hQANeutrons"), binPosition); + return true; + case PDG_t::kNeutronBar: + registry.fill(HIST("Generated/hQAAntiNeutrons"), binPosition); + return true; + case o2::constants::physics::Pdg::kDeuteron: + registry.fill(HIST("Generated/hQADeuterons"), binPosition); + return true; + case -o2::constants::physics::Pdg::kDeuteron: + registry.fill(HIST("Generated/hQAAntiDeuterons"), binPosition); + return true; + default: + return false; + } + }; + if (!fillGeneratedQa(0.5)) { + continue; + } + if (isPrim && !particle.isPhysicalPrimary()) { + continue; + } - if (isPrim && !part0.isPhysicalPrimary()) { - continue; - } - if (isPrim && !part1.isPhysicalPrimary()) { - continue; - } - if (std::abs(part0.eta()) > etaCut) { - continue; - } - if (std::abs(part1.eta()) > etaCut) { - continue; + fillGeneratedQa(1.5); + + if (std::abs(particle.y()) < yRap) { + switch (particle.pdgCode()) { + case PDG_t::kProton: + registry.fill(HIST("Generated/hProtonsVsPt"), particle.pt()); + break; + case -PDG_t::kProton: + registry.fill(HIST("Generated/hAntiProtonsVsPt"), particle.pt()); + break; + case o2::constants::physics::Pdg::kDeuteron: + registry.fill(HIST("Generated/hDeuteronsVsPt"), particle.pt()); + break; + case -o2::constants::physics::Pdg::kDeuteron: + registry.fill(HIST("Generated/hAntiDeuteronsVsPt"), particle.pt()); + break; + default: + break; } + } - if (mode == kDbarPbar) { - if (part0.pdgCode() != -o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; - } - if (mode == kDP) { - if (part0.pdgCode() != o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != PDG_t::kProton) - continue; - } - if (mode == kDbarP) { - if (part0.pdgCode() != -o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != PDG_t::kProton) - continue; - } - if (mode == kDPbar) { - if (part0.pdgCode() != o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; - } - if (mode == kPbarP) { - if (part0.pdgCode() != -PDG_t::kProton) - continue; - if (part1.pdgCode() != PDG_t::kProton) - continue; + if (std::abs(particle.eta()) < etaCut) { + fillGeneratedQa(2.5); + } + + // (anti)neutrons are accepted by fillGeneratedQa for QA counting only: they have no + // eta-phi-pt histogram and must not reach the switch below (whose default is fatal) + if (std::abs(particle.pdgCode()) == PDG_t::kNeutron) { + continue; + } + + addGenCandidate(particle, genCache); + + switch (particle.pdgCode()) { + case PDG_t::kProton: + registry.fill(HIST("hGen_EtaPhiPt_Proton"), particle.eta(), particle.phi(), particle.pt()); + break; + case -PDG_t::kProton: + registry.fill(HIST("hGen_EtaPhiPt_Proton"), particle.eta(), particle.phi(), -1. * particle.pt()); + break; + case o2::constants::physics::Pdg::kDeuteron: + registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), particle.eta(), particle.phi(), particle.pt()); + break; + case -o2::constants::physics::Pdg::kDeuteron: + registry.fill(HIST("hGen_EtaPhiPt_Deuteron"), particle.eta(), particle.phi(), -1. * particle.pt()); + break; + default: + LOG(fatal) << "Unhandled PDG code, should not happen, check the code! " << particle.pdgCode(); + break; + } + } + + // Pairing on the compact candidate vectors only (table order is preserved, so the + // part0/part1 assignment is the same as with the full-table combinations) + if (pdgPart0 == pdgPart1) { // Identical particle combinations: each unordered pair once + const auto& cands = genCache.cand0; + for (size_t i = 0; i < cands.size(); i++) { + for (size_t j = i + 1; j < cands.size(); j++) { + fillHistogramsGen(cands[i], cands[j], false); } - if (mode == kPPbar) { - if (part0.pdgCode() != PDG_t::kProton) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; + } + } else { + for (const auto& part0 : genCache.cand0) { + for (const auto& part1 : genCache.cand1) { + fillHistogramsGen(part0, part1, false); } - - fillHistogramsGen(part0, part1, false); } } } @@ -1470,80 +2091,33 @@ struct HadronNucleiCorrelation { void processMixedEventGen(SimCollisions const& mcCollisions, SimParticles const& mcParticles) { - for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningGen, 5, -1, mcCollisions, mcCollisions)) { - - // LOGF(info, "Mixed event collisions: (%d, %d) zvtx (%.1f, %.1f) mult (%d, %d)", collision1.globalIndex(), collision2.globalIndex(), collision1.posZ(), collision2.posZ(), collision1.multMCNParticlesEta10(), collision2.multMCNParticlesEta10()); + // One pass per collision: multiplicity for the mixing bins + pairing candidates. + // The mixing loop below never touches the MC particle table again. + std::unordered_map genCaches; + genCaches.reserve(mcCollisions.size()); + for (const auto& collision : mcCollisions) { + auto particlesPerCol = mcParticles.sliceBy(perMcCollision, collision.globalIndex()); + auto colCache = buildGenCollisionCache(particlesPerCol); + registry.fill(HIST("hMult"), colCache.mult); + genCaches.emplace(collision.globalIndex(), std::move(colCache)); + } - auto groupPartsOne = mcParticles.sliceBy(perMcCollision, collision1.globalIndex()); - auto groupPartsTwo = mcParticles.sliceBy(perMcCollision, collision2.globalIndex()); + auto getMultiplicity = [&genCaches](SimCollisions::iterator const& collision) { + return genCaches.at(collision.globalIndex()).mult; + }; - registry.fill(HIST("hMult"), collision1.multMCNParticlesEta10()); + using BinningTypeMC = FlexibleBinningPolicy, aod::mccollision::PosZ, decltype(getMultiplicity)>; + BinningTypeMC colBinningGen{{getMultiplicity}, {confVtxBins, confMultBins}}; - for (const auto& [part0, part1] : combinations(CombinationsFullIndexPolicy(groupPartsOne, groupPartsTwo))) { + for (const auto& [collision1, collision2] : soa::selfCombinations(colBinningGen, maxmixcollsGen.value, -1, mcCollisions, mcCollisions)) { + const auto& cache1 = genCaches.at(collision1.globalIndex()); + const auto& cache2 = genCaches.at(collision2.globalIndex()); - if (isPrim && !part0.isPhysicalPrimary()) { - continue; - } - if (isPrim && !part1.isPhysicalPrimary()) { - continue; + // For identical species cand1 holds the same particles as cand0, so this covers both cases + for (const auto& part0 : cache1.cand0) { + for (const auto& part1 : cache2.cand1) { + fillHistogramsGen(part0, part1, true); } - if (std::abs(part0.eta()) > etaCut) { - continue; - } - if (std::abs(part1.eta()) > etaCut) { - continue; - } - - if (mode == kDbarPbar) { - if (part0.pdgCode() != -o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; - } - if (mode == kDP) { - if (part0.pdgCode() != o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != PDG_t::kProton) - continue; - } - if (mode == kDbarP) { - if (part0.pdgCode() != -o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != PDG_t::kProton) - continue; - } - if (mode == kDPbar) { - if (part0.pdgCode() != o2::constants::physics::Pdg::kDeuteron) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; - } - if (mode == kPbarP) { - if (part0.pdgCode() != -PDG_t::kProton) - continue; - if (part1.pdgCode() != PDG_t::kProton) - continue; - } - if (mode == kPbarPbar) { - if (part0.pdgCode() != -PDG_t::kProton) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; - } - if (mode == kPP) { - if (part0.pdgCode() != PDG_t::kProton) - continue; - if (part1.pdgCode() != PDG_t::kProton) - continue; - } - if (mode == kPPbar) { - if (part0.pdgCode() != PDG_t::kProton) - continue; - if (part1.pdgCode() != -PDG_t::kProton) - continue; - } - - fillHistogramsGen(part0, part1, true); } } } diff --git a/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx b/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx index afe3814afd9..52b0e7cc552 100644 --- a/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/he3LambdaDerivedAnalysis.cxx @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,11 @@ std::shared_ptr hInvariantMassLambda[2]; std::shared_ptr hCosPALambda; std::shared_ptr hNsigmaHe3; std::shared_ptr hNsigmaProton; +std::shared_ptr hInvariantMassHe3K0s[2]; // 0: anti-He3, 1: He3 +std::shared_ptr hRotationInvariantMassHe3K0s[2]; +std::shared_ptr hInvariantMassK0s[2]; // Before and after selection +std::shared_ptr hCosPAK0s; +std::shared_ptr hCtK0s; }; // namespace using namespace o2; @@ -59,6 +65,15 @@ struct he3LambdaDerivedAnalysis { Configurable cfgMinLambdaPt{"cfgMinLambdaPt", 0.4, "Minimum pT for Lambda candidates"}; Configurable cfgMaxLambdaDeltaM{"cfgMaxLambdaDeltaM", 10.0e-3, "Maximum deltaM for Lambda candidates"}; + Configurable cfgMinK0sPt{"cfgMinK0sPt", 0.5f, "Minimum K0s pT"}; + Configurable cfgMaxK0sPt{"cfgMaxK0sPt", 10.f, "Maximum K0s pT"}; + Configurable cfgMinK0sCt{"cfgMinK0sCt", 0.f, "Minimum K0s proper decay length (cm)"}; + Configurable cfgMaxK0sCt{"cfgMaxK0sCt", 20.f, "Maximum K0s proper decay length (cm)"}; + Configurable cfgMinK0sCosPA{"cfgMinK0sCosPA", 0.99f, "Minimum K0s cosPA"}; + Configurable cfgMaxK0sDeltaM{"cfgMaxK0sDeltaM", 10.e-3f, "Maximum K0s mass deviation (GeV/c^2)"}; + Configurable cfgMaxNSigmaTPCPionK0s{"cfgMaxNSigmaTPCPionK0s", 4.f, "Maximum daughter pion TPC nSigma for K0s"}; + ConfigurableAxis cfgHe3K0sMassAxis{"cfgHe3K0sMassAxis", {200, MassHelium3 + MassK0Short, MassHelium3 + MassK0Short + 1.}, "He3-K0s pair invariant mass (GeV/c^2)"}; + void init(InitContext const&) { constexpr double ConstituentsMass = o2::constants::physics::MassProton + o2::constants::physics::MassNeutron * 2 + o2::constants::physics::MassSigmaPlus; @@ -70,12 +85,26 @@ struct he3LambdaDerivedAnalysis { hInvariantMassLambda[i] = mRegistry.add(Form("hInvariantMassLambda%i", i), "Invariant Mass Lambda", {HistType::kTH2D, {{50, 0., 10.}, {30, o2::constants::physics::MassLambda0 - 0.015, o2::constants::physics::MassLambda0 + 0.015}}}); hRotationInvariantMassAntiLSeta[i] = mRegistry.add(Form("hRotationInvariantMassAntiLSeta%i", i), "Rotation Invariant Mass Anti-Lambda", {HistType::kTH2D, {{45, 1., 10}, {100, ConstituentsMass - 0.05, ConstituentsMass + 0.05}}}); } + if (doprocessSameEvent && doprocessSameEventLegacy) { + LOG(fatal) << "Enable only one Lambda table version: processSameEvent or processSameEventLegacy"; + } + if (doprocessK0s) { + const AxisSpec pairMassAxis{cfgHe3K0sMassAxis, "m(He3 K0s) (GeV/c^{2})"}; + for (int i = 0; i < 2; ++i) { + hInvariantMassHe3K0s[i] = mRegistry.add(Form("hInvariantMassHe3K0s%i", i), "Same-event He3-K0s", {HistType::kTH2D, {{45, 1., 10.}, pairMassAxis}}); + hRotationInvariantMassHe3K0s[i] = mRegistry.add(Form("hRotationInvariantMassHe3K0s%i", i), "Rotated He3-K0s", {HistType::kTH2D, {{45, 1., 10.}, pairMassAxis}}); + hInvariantMassK0s[i] = mRegistry.add(Form("hInvariantMassK0s%i", i), "K0s;pT (GeV/c);mass (GeV/c^{2})", {HistType::kTH2D, {{50, 0., 10.}, {100, MassK0Short - 0.015, MassK0Short + 0.015}}}); + } + hCosPAK0s = mRegistry.add("hCosPAK0s", "K0s;pT (GeV/c);cosPA", {HistType::kTH2D, {{50, 0., 10.}, {500, 0.9, 1.}}}); + hCtK0s = mRegistry.add("hCtK0s", "K0s;pT (GeV/c);ct (cm)", {HistType::kTH2D, {{50, 0., 10.}, {100, 0., 30.}}}); + } hCosPALambda = mRegistry.add("hCosPALambda", "Cosine of Pointing Angle for Lambda", {HistType::kTH2D, {{50, 0., 10.}, {500, 0.9, 1.}}}); hNsigmaHe3 = mRegistry.add("hNsigmaHe3", "nSigma TPC for He3", {HistType::kTH2D, {{100, -10., 10.}, {200, -5, 5.}}}); hNsigmaProton = mRegistry.add("hNsigmaProton", "nSigma TPC for Proton", {HistType::kTH2D, {{100, -10., 10.}, {200, -5, 5.}}}); } - void processSameEvent(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_000 const& he3s, o2::aod::LFLambda_000 const& lambdas) + template + void analyseLambda(o2::aod::LFEvents::iterator const& collision, He3Table const& he3s, LambdaTable const& lambdas) { std::vector he3Candidates; he3Candidates.reserve(he3s.size()); @@ -111,6 +140,7 @@ struct he3LambdaDerivedAnalysis { candidate.mass = lambda.mass(); candidate.cosPA = lambda.cosPA(); candidate.dcaV0Daughters = lambda.dcaDaughters(); + candidate.sign = lambda.sign(); hCosPALambda->Fill(lambda.pt(), candidate.cosPA); // hNsigmaProton->Fill(lambda.pt() * lambda.sign(), lambda.protonNSigmaTPC()); hInvariantMassLambda[0]->Fill(lambda.pt(), lambda.mass()); @@ -128,7 +158,7 @@ struct he3LambdaDerivedAnalysis { (he3.sign * lambda.sign > 0 ? hInvariantMassLS : hInvariantMassUS)[he3.sign > 0]->Fill(pairMomentum.Pt(), pairMomentum.M()); } for (int iEta{0}; iEta <= cfgMirrorEta; ++iEta) { - for (int iR{0}; iR <= cfgNrotations; ++iR) { + for (int iR{0}; cfgNrotations > 0 && iR <= cfgNrotations; ++iR) { auto he3Momentum = ROOT::Math::LorentzVector>(he3.momentum.Pt(), (1. - iEta * 2.) * he3.momentum.Eta(), he3.momentum.Phi() + TMath::Pi() * (0.75 + 0.5 * iR / cfgNrotations), he3.momentum.M()); for (const auto& lambda : lambdaCandidates) { auto pairMomentum = lambda.momentum + he3Momentum; // Calculate invariant mass @@ -141,7 +171,63 @@ struct he3LambdaDerivedAnalysis { } } } - PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEvent, "Process same event", true); + void processSameEvent(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_001 const& he3s, o2::aod::LFLambda_001 const& lambdas) + { + analyseLambda(collision, he3s, lambdas); + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEvent, "Process same-event He3-Lambda pairs (version 001)", true); + + void processSameEventLegacy(o2::aod::LFEvents::iterator const& collision, o2::aod::LFHe3_000 const& he3s, o2::aod::LFLambda_000 const& lambdas) + { + analyseLambda(collision, he3s, lambdas); + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processSameEventLegacy, "Process same-event He3-Lambda pairs (version 000)", false); + + void processK0s(o2::aod::LFEvents::iterator const&, o2::aod::LFHe3_001 const& he3s, o2::aod::LFK0s const& k0ss) + { + std::vector k0sCandidates; + k0sCandidates.reserve(k0ss.size()); + for (const auto& k0s : k0ss) { + hInvariantMassK0s[0]->Fill(k0s.pt(), k0s.mass()); + hCosPAK0s->Fill(k0s.pt(), k0s.cosPA()); + hCtK0s->Fill(k0s.pt(), k0s.ct()); + if (k0s.pt() < cfgMinK0sPt || k0s.pt() > cfgMaxK0sPt || k0s.ct() < cfgMinK0sCt || k0s.ct() > cfgMaxK0sCt || + k0s.cosPA() < cfgMinK0sCosPA || std::abs(k0s.mass() - MassK0Short) > cfgMaxK0sDeltaM || + std::abs(k0s.nSigmaTPCPosPion()) > cfgMaxNSigmaTPCPionK0s || std::abs(k0s.nSigmaTPCNegPion()) > cfgMaxNSigmaTPCPionK0s) { + continue; + } + k0sCandidate candidate; + // Use the nominal K0s mass for pair kinematics, as for Lambda. + candidate.momentum.SetCoordinates(k0s.pt(), k0s.eta(), k0s.phi(), MassK0Short); + k0sCandidates.push_back(candidate); + hInvariantMassK0s[1]->Fill(k0s.pt(), k0s.mass()); + } + for (const auto& he3 : he3s) { + if (!doprocessSameEvent && !doprocessSameEventLegacy) { + hNsigmaHe3->Fill(he3.pt() * he3.sign(), he3.nSigmaTPC()); + } + if (std::abs(he3.nSigmaTPC()) > cfgMaxNSigmaTPCHe3) { + continue; + } + const ROOT::Math::LorentzVector> he3Momentum(he3.pt(), he3.eta(), he3.phi(), MassHelium3); + const int signIndex = he3.sign() > 0; + for (const auto& k0s : k0sCandidates) { + const auto pairMomentum = he3Momentum + k0s.momentum; + hInvariantMassHe3K0s[signIndex]->Fill(pairMomentum.Pt(), pairMomentum.M()); + } + // Match the Lambda background's rotation angles and optional eta mirroring. + for (int iEta = 0; iEta <= cfgMirrorEta; ++iEta) { + for (int iR = 0; cfgNrotations > 0 && iR <= cfgNrotations; ++iR) { + const ROOT::Math::LorentzVector> rotatedHe3(he3.pt(), (1. - iEta * 2.) * he3.eta(), he3.phi() + TMath::Pi() * (0.75 + 0.5 * iR / cfgNrotations), MassHelium3); + for (const auto& k0s : k0sCandidates) { + const auto pairMomentum = rotatedHe3 + k0s.momentum; + hRotationInvariantMassHe3K0s[signIndex]->Fill(pairMomentum.Pt(), pairMomentum.M()); + } + } + } + } + } + PROCESS_SWITCH(he3LambdaDerivedAnalysis, processK0s, "Process same-event He3-K0s pairs (version 001)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/Nuspex/helium_flow.cxx b/PWGLF/Tasks/Nuspex/helium_flow.cxx index 74f76d1c18e..5dab83c0db3 100644 --- a/PWGLF/Tasks/Nuspex/helium_flow.cxx +++ b/PWGLF/Tasks/Nuspex/helium_flow.cxx @@ -183,7 +183,7 @@ struct helium_flow { bool containsParticleOfInterest(false); // Loop over Reconstructed Tracks - for (auto track : tracks) { + for (const auto& track : tracks) { // Track Selection if (!passedTrackSelection(track)) diff --git a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx index 1d2d5c11ec0..bbb966da90c 100644 --- a/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx +++ b/PWGLF/Tasks/Nuspex/hypertritonAnalysis.cxx @@ -46,7 +46,9 @@ #include #include +#include #include +#include using namespace o2; using namespace o2::framework; @@ -191,8 +193,8 @@ struct hypertritonAnalysis { kEvSelAllSteps }; // Helper to do bookkeeping and late filling of QA histos - std::array stats; - std::array evselstats; + std::array stats; + std::array evselstats; void resetHistos() { diff --git a/PWGLF/Tasks/Nuspex/LFNucleiBATask.cxx b/PWGLF/Tasks/Nuspex/lfNucleiBATask.cxx similarity index 89% rename from PWGLF/Tasks/Nuspex/LFNucleiBATask.cxx rename to PWGLF/Tasks/Nuspex/lfNucleiBATask.cxx index 17173fe5276..27d7abcbddc 100644 --- a/PWGLF/Tasks/Nuspex/LFNucleiBATask.cxx +++ b/PWGLF/Tasks/Nuspex/lfNucleiBATask.cxx @@ -10,12 +10,13 @@ // or submit itself to any jurisdiction. /// -/// \file LFNucleiBATask.cxx +/// \file lfNucleiBATask.cxx /// /// \brief Analysis task for the measurement of the coalescence parameter B2/B3 in pp collisions for (anti)deuteron/(anti)helium-3 /// /// \author Giovanni Malfattore and Rutuparna Rath -/// + +// o2-linter: disable=name/workflow-file (temporary fix) #include "PWGLF/DataModel/LFNucleiTables.h" #include "PWGLF/DataModel/LFParticleIdentification.h" @@ -53,6 +54,7 @@ #include #include +#include #include #include #include @@ -66,17 +68,17 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -struct LFNucleiBATask { - Service ccdb; - Service pdgDB; +struct lfNucleiBATask { + Service ccdb{}; + Service pdgDB{}; Zorro zorro; OutputObj zorroSummary{"zorroSummary"}; // Efficiency configurator std::unordered_set effEvtSet; - bool effEvtSetReady = false; - Configurable enableEffEvtSet{"enableEffEvtSet", true, "If true, MCGen uses the event-set built by MCReco; if false, MCGen runs stand-alone."}; + // bool effEvtSetReady = false; + Configurable requireRecoEventForGen{"requireRecoEventForGen", true, "Require at least one reco collision passing the event selection in processMCGen"}; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry spectraGen{"spectraGen", {}, OutputObjHandlingPolicy::AnalysisObject, false, true}; @@ -92,8 +94,8 @@ struct LFNucleiBATask { Configurable enableHe{"enableHe", true, "Flag to enable helium-3 analysis."}; Configurable enableAl{"enableAl", true, "Flag to enable alpha analysis."}; - Configurable enableTrackingEff{"enableTrackingEff", 0, "Flag to enable tracking efficiency histos."}; - Configurable ccdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable enableTrackingEff{"enableTrackingEff", false, "Flag to enable tracking efficiency histos."}; + Configurable ccdbUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; // o2-linter: disable=name/configurable (fast fix) // Set the triggered events skimming scheme struct : ConfigurableGroup { @@ -107,7 +109,10 @@ struct LFNucleiBATask { Configurable useTVXtrigger{"useTVXtrigger", false, "Use TVX for Event Selection (default w/ Sel8)"}; Configurable removeTFBorder{"removeTFBorder", false, "Remove TimeFrame border (default w/ Sel8)"}; Configurable removeITSROFBorder{"removeITSROFBorder", false, "Remove ITS Read-Out Frame border (default w/ Sel8)"}; + Configurable useINELgt0cut{"useINELgt0cut", false, "Apply INEL>0 Event Selection in processData o processMC"}; + Configurable useINELgt1cut{"useINELgt1cut", false, "Apply INEL>1 Event Selection in processData o processMC"}; Configurable enableGenVzCut{"enableGenVzCut", true, "Apply cut in z-Vertex in the processGen function"}; + Configurable askGenFT0Coincidence{"askGenFT0Coincidence", false, "Require a generated charged-particle coincidence in FT0A and FT0C"}; } evselOptions; // Set the multiplity event limits @@ -121,9 +126,9 @@ struct LFNucleiBATask { // Set the quality cuts for tracks struct : ConfigurableGroup { Configurable rejectFakeTracks{"rejectFakeTracks", false, "Flag to reject ITS-TPC fake tracks (for MC)"}; - Configurable cfgCutITSClusters{"cfgCutITSClusters", -1.f, "Minimum number of ITS clusters"}; - Configurable cfgCutTPCXRows{"cfgCutTPCXRows", -1.f, "Minimum number of crossed TPC rows"}; - Configurable cfgCutTPCClusters{"cfgCutTPCClusters", -1.f, "Minimum number of found TPC clusters"}; + Configurable cfgCutITSClusters{"cfgCutITSClusters", 3.f, "Minimum number of ITS clusters; set to -1 to disable"}; + Configurable cfgCutTPCXRows{"cfgCutTPCXRows", 70.f, "Minimum number of crossed TPC rows"}; + Configurable cfgCutTPCClusters{"cfgCutTPCClusters", 120.f, "Minimum number of found TPC clusters"}; Configurable cfgCutTPCCROFnd{"cfgCutTPCCROFnd", 0.8, "Minimum ratio of crossed TPC clusters over findable"}; Configurable> tpcChi2NclCuts{"tpcChi2NclCuts", {0.5, 4}, "Range of accepted of Chi2/TPC clusters"}; Configurable> itsChi2NclCuts{"itsChi2NclCuts", {0.f, 36}, "Range of accepted of Chi2/ITS clusters"}; @@ -132,10 +137,10 @@ struct LFNucleiBATask { // Set the kinematic and PID cuts for tracks struct : ConfigurableGroup { - Configurable cfgMomentumCut{"cfgMomentumCut", 0.3f, "Value of the p selection for spectra (default 0.3)"}; + Configurable cfgMomentumCut{"cfgMomentumCut", 0.42f, "Value of the p selection for spectra (default 0.3)"}; Configurable cfgEtaCut{"cfgEtaCut", 0.8f, "Value of the eta selection for spectra (default 0.8)"}; - Configurable cfgRapidityCutLow{"cfgRapidityCutLow", -1.0f, "Value of the low rapidity selection for spectra (default -1.0)"}; - Configurable cfgRapidityCutHigh{"cfgRapidityCutHigh", 1.0f, "Value of the high rapidity selection for spectra (default 1.0)"}; + Configurable cfgRapidityCutLow{"cfgRapidityCutLow", -0.5f, "Value of the low rapidity selection for spectra (default -1.0)"}; + Configurable cfgRapidityCutHigh{"cfgRapidityCutHigh", 0.5f, "Value of the high rapidity selection for spectra (default 1.0)"}; } kinemOptions; Configurable isPVContributorCut{"isPVContributorCut", false, "Flag to enable isPVContributor cut."}; @@ -143,17 +148,17 @@ struct LFNucleiBATask { struct : ConfigurableGroup { Configurable nsigmaTPCPr{"nsigmaTPCPr", 3.f, "Value of the Nsigma TPC cut for protons"}; - Configurable nsigmaTPCDe{"nsigmaTPCDe", 3.f, "Value of the Nsigma TPC cut for deuterons"}; - Configurable nsigmaTPCTr{"nsigmaTPCTr", 3.f, "Value of the Nsigma TPC cut for tritons"}; - Configurable nsigmaTPCHe{"nsigmaTPCHe", 3.f, "Value of the Nsigma TPC cut for helium-3"}; - Configurable nsigmaTPCAl{"nsigmaTPCAl", 3.f, "Value of the Nsigma TPC cut for alpha"}; + Configurable nsigmaTPCDe{"nsigmaTPCDe", 4.f, "Value of the Nsigma TPC cut for deuterons"}; + Configurable nsigmaTPCTr{"nsigmaTPCTr", 4.f, "Value of the Nsigma TPC cut for tritons"}; + Configurable nsigmaTPCHe{"nsigmaTPCHe", 5.f, "Value of the Nsigma TPC cut for helium-3"}; + Configurable nsigmaTPCAl{"nsigmaTPCAl", 5.f, "Value of the Nsigma TPC cut for alpha"}; } nsigmaTPCvar; struct : ConfigurableGroup { Configurable useITSDeCut{"useITSDeCut", false, "Select Deuteron if compatible with deuteron hypothesis (via SigmaITS)"}; - Configurable useITSHeCut{"useITSHeCut", false, "Select Helium if compatible with helium hypothesis (via SigmaITS)"}; - Configurable nsigmaITSDe{"nsigmaITSDe", -1.f, "Value of the Nsigma ITS cut for deuteron ( > nSigmaITSHe)"}; - Configurable nsigmaITSHe{"nsigmaITSHe", -1.f, "Value of the Nsigma ITS cut for helium-3 ( > nSigmaITSHe)"}; + Configurable useITSHeCut{"useITSHeCut", true, "Select Helium if compatible with helium hypothesis (via SigmaITS)"}; + Configurable nsigmaITSDe{"nsigmaITSDe", -3.f, "Value of the Nsigma ITS cut for deuteron ( > nSigmaITSHe)"}; + Configurable nsigmaITSHe{"nsigmaITSHe", -5.f, "Value of the Nsigma ITS cut for helium-3 ( > nSigmaITSHe)"}; Configurable showAverageClusterSize{"showAverageClusterSize", false, "Show average cluster size"}; } nsigmaITSvar; @@ -163,20 +168,21 @@ struct LFNucleiBATask { // Set the axis used in this task ConfigurableAxis binsPercentile{"binsPercentile", {100, 0, 100}, "Centrality FT0M"}; ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.425, 0.45, 0.475, 0.5, 0.5125, 0.525, 0.5375, 0.55, 0.5625, 0.575, 0.5875, 0.6, 0.6125, 0.625, 0.6375, 0.65, 0.6625, 0.675, 0.6875, 0.7, 0.7125, 0.725, 0.7375, 0.75, 0.7625, 0.775, 0.7875, 0.8, 0.8125, 0.825, 0.8375, 0.85, 0.8625, 0.875, 0.8875, 0.9, 0.9125, 0.925, 0.9375, 0.95, 0.9625, 0.975, 0.9875, 1.0, 1.0125, 1.025, 1.0375, 1.05, 1.0625, 1.075, 1.0875, 1.1, 1.1125, 1.125, 1.1375, 1.15, 1.1625, 1.175, 1.1875, 1.2, 1.2125, 1.225, 1.2375, 1.25, 1.2625, 1.275, 1.2875, 1.3, 1.3125, 1.325, 1.3375, 1.35, 1.3625, 1.375, 1.3875, 1.4, 1.4125, 1.425, 1.4375, 1.45, 1.4625, 1.475, 1.4875, 1.5, 1.5125, 1.525, 1.5375, 1.55, 1.5625, 1.575, 1.5875, 1.6, 1.6125, 1.625, 1.6375, 1.65, 1.6625, 1.675, 1.6875, 1.7, 1.7125, 1.725, 1.7375, 1.75, 1.7625, 1.775, 1.7875, 1.8, 1.8125, 1.825, 1.8375, 1.85, 1.8625, 1.875, 1.8875, 1.9, 1.9125, 1.925, 1.9375, 1.95, 1.9625, 1.975, 1.9875, 2.0, 2.0625, 2.125, 2.1875, 2.25, 2.3125, 2.375, 2.4375, 2.5, 2.625, 2.75, 2.875, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0}, ""}; - ConfigurableAxis binsPtHe{"binsPtHe", {VARIABLE_WIDTH, 1.0, 1.25, 1.50, 1.75, 2.0, 2.25, 2.50, 2.75, 3.0, 3.25, 3.50, 3.75, 4.0, 4.50, 5.0, 6.0, 7.0, 8.0}, ""}; - ConfigurableAxis binsPtZHe{"binsPtZHe", {VARIABLE_WIDTH, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 1.875, 2.0, 2.25, 2.5, 3.0, 3.5, 4.0}, ""}; + ConfigurableAxis binsPtHe{"binsPtHe", {VARIABLE_WIDTH, 0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.50, 1.75, 2.0, 2.25, 2.50, 2.75, 3.0, 3.25, 3.50, 3.75, 4.0, 4.50, 5.0, 6.0, 7.0, 8.0}, ""}; + ConfigurableAxis binsPtZHe{"binsPtZHe", {VARIABLE_WIDTH, 0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 1.875, 2.0, 2.25, 2.5, 3.0, 3.5, 4.0}, ""}; + ConfigurableAxis binsPtHeMigration{"binsPtHeMigration", {VARIABLE_WIDTH, 0.0, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.10, 2.20, 2.30, 2.40, 2.50, 2.60, 2.70, 2.80, 2.90, 3.00, 3.25, 3.50, 3.75, 4.00, 4.50, 5.00, 6.00, 7.00, 8.00}, ""}; ConfigurableAxis binsdEdx{"binsdEdx", {600, 0.f, 3000.f}, ""}; ConfigurableAxis binsBeta{"binsBeta", {120, 0.0, 1.2}, ""}; ConfigurableAxis binsDCA{"binsDCA", {400, -1.f, 1.f}, ""}; - ConfigurableAxis binsSigmaITS{"binsSigmaITS", {200, -20, 20}, ""}; - ConfigurableAxis binsSigmaTPC{"binsSigmaTPC", {1000, -100, 100}, ""}; - ConfigurableAxis binsSigmaTOF{"binsSigmaTOF", {1000, -100, 100}, ""}; - ConfigurableAxis binsMassPr{"binsMassPr", {100, -1., 1.f}, ""}; - ConfigurableAxis binsMassDe{"binsMassDe", {180, -1.8, 1.8f}, ""}; - ConfigurableAxis binsMassTr{"binsMassTr", {250, -2.5, 2.5f}, ""}; - ConfigurableAxis binsMassHe{"binsMassHe", {300, -3., 3.f}, ""}; - ConfigurableAxis avClsBins{"avClsBins", {200, 0, 20}, "Binning in average cluster size"}; + ConfigurableAxis binsSigmaITS{"binsSigmaITS", {150, -15, 5}, ""}; + ConfigurableAxis binsSigmaTPC{"binsSigmaTPC", {290, -17, 12}, ""}; + ConfigurableAxis binsSigmaTOF{"binsSigmaTOF", {300, -15, 15}, ""}; + ConfigurableAxis binsMassPr{"binsMassPr", {400, -2., 2.f}, ""}; + ConfigurableAxis binsMassDe{"binsMassDe", {360, -1.8, 1.8f}, ""}; + ConfigurableAxis binsMassTr{"binsMassTr", {500, -2.5, 2.5f}, ""}; + ConfigurableAxis binsMassHe{"binsMassHe", {1000, -5., 5.f}, ""}; + ConfigurableAxis avClsBins{"avClsBins", {90, 0, 18}, "Binning in average cluster size"}; // Enable custom cuts/debug functions struct : ConfigurableGroup { @@ -185,15 +191,15 @@ struct LFNucleiBATask { Configurable enableEvTimeSplitting{"enableEvTimeSplitting", false, "Flag to enable histograms splitting depending on the Event Time used"}; } filterOptions; - Configurable enableCustomDCACut{"enableCustomDCACut", false, "Flag to enable DCA custom cuts - unflag to use standard isGlobalCut DCA cut"}; + Configurable enableCustomDCACut{"enableCustomDCACut", true, "Flag to enable DCA custom cuts - unflag to use standard isGlobalCut DCA cut"}; struct : ConfigurableGroup { Configurable cfgCustomDCA{"cfgCustomDCA", 0, "Select to use: pT independent DCAxy and DCAz CustomCut (0), pT dependent DCAxy and DCAz cut (1), pt dependent DCAxy, DCAz CustomCut (2) DCAxy CustomCut, pT dependent DCAz (3) or a circular DCAxy,z cut (4) for tracks. Need 'enableCustomDCACut' to be enabled."}; - Configurable cfgCustomDCAxy{"cfgCustomDCAxy", 0.05f, "Value of the DCAxy selection for spectra (default 0.05 cm)"}; + Configurable cfgCustomDCAxy{"cfgCustomDCAxy", 0.12f, "Value of the DCAxy selection for spectra (default 0.12 cm)"}; Configurable cfgCustomDCAz{"cfgCustomDCAz", 0.5f, "Value of the DCAz selection for spectra (default 0.5 cm)"}; } dcaConfOptions; - Configurable> parDCAxycuts{"parDCAxycuts", {0.004f, 0.013f, 1, 1}, "Parameters for Pt dependent DCAxy cut (if enabled): |DCAxy| < [3] * ([O] + [1]/Pt^[2])."}; - Configurable> parDCAzcuts{"parDCAzcuts", {0.004f, 0.013f, 1, 1}, "Parameters for Pt dependent DCAz cut (if enabled): |DCAz| < [3] * ([O] + [1]/Pt^[2])."}; + Configurable> parDCAxycuts{"parDCAxycuts", {0.004f, 0.013f, 1, 3}, "Parameters for Pt dependent DCAxy cut (if enabled): |DCAxy| < [3] * ([O] + [1]/Pt^[2])."}; + Configurable> parDCAzcuts{"parDCAzcuts", {0.004f, 0.013f, 1, 3}, "Parameters for Pt dependent DCAz cut (if enabled): |DCAz| < [3] * ([O] + [1]/Pt^[2])."}; // Enable output histograms struct : ConfigurableGroup { @@ -237,15 +243,16 @@ struct LFNucleiBATask { Configurable> parShiftPtAntiHe{"parShiftPtAntiHe", {0.0f, 0.1f, 0.1f, 0.1f, 0.1f}, "Parameters for anti-helium3-Pt shift (if enabled)."}; Configurable enablePtShiftPID{"enablePtShiftPID", true, "Flag to enable wrong PID in tracking pT correction shift"}; - Configurable> parShiftPtPID{"parShiftPtPID", {0.0f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f}, "Parameters for helium3-Pt wrong pid shift (if enabled)."}; + Configurable> parShiftPtPID{"parShiftPtPID", {0.000048972, 12.2439, -0.493803, -9.06264, 3.4022, -0.17518, -0.087316}, "Parameters for helium3-Pt wrong pid shift (if enabled)."}; // o2-linter: disable=pdg/explicit-mass (there are not masses but calibration values) + Configurable cfgPtShiftPID{"cfgPtShiftPID", 1.25f, "Default upper limit for PID pt-shift correction"}; Configurable enableCentrality{"enableCentrality", true, "Flag to enable centrality 3D histos)"}; // ITS to TPC - Fake hit loop - static constexpr int kFakeLoop = 10; // Fixed O2Linter error + static constexpr int IntFakeLoop = 10; // Fixed O2Linter error // TPC low/high momentum range - static constexpr float kCfgTpcClasses[] = {0.5f, 0.1f}; - static constexpr float kCfgKaonCut = 5.f; + static constexpr std::array CfgTpcClasses = {0.5f, 1.5f}; + static constexpr float CfgKaonCut = 5.f; // PDG codes and masses used in this analysis static constexpr int PDGPion = PDG_t::kPiPlus; @@ -255,7 +262,7 @@ struct LFNucleiBATask { static constexpr int PDGTriton = o2::constants::physics::Pdg::kTriton; static constexpr int PDGHelium = o2::constants::physics::Pdg::kHelium3; static constexpr int PDGAlpha = o2::constants::physics::Pdg::kAlpha; - static constexpr int PDGHyperTriton = o2::constants::physics::Pdg::kHyperTriton; + // static constexpr int PDGHyperTriton = o2::constants::physics::Pdg::kHyperTriton; // not used static constexpr float MassProtonVal = o2::constants::physics::MassProton; static constexpr float MassDeuteronVal = o2::constants::physics::MassDeuteron; static constexpr float MassTritonVal = o2::constants::physics::MassTriton; @@ -263,35 +270,13 @@ struct LFNucleiBATask { static constexpr float MassAlphaVal = o2::constants::physics::MassAlpha; // PDG of Mothers - static constexpr int kPdgMotherList[] = { - PDG_t::kPiPlus, - PDG_t::kKPlus, - PDG_t::kK0Short, - PDG_t::kNeutron, - PDG_t::kProton, - PDG_t::kLambda0, - o2::constants::physics::Pdg::kDeuteron, - o2::constants::physics::Pdg::kHelium3, - o2::constants::physics::Pdg::kTriton, - o2::constants::physics::Pdg::kHyperTriton, - o2::constants::physics::Pdg::kAlpha}; - - static constexpr int kNumMotherList = sizeof(kPdgMotherList) / sizeof(kPdgMotherList[0]); - - static constexpr const char* kMotherNames[kNumMotherList] = { - "#pi^{+}", - "K^{+}", - "K^{0}_{S}", - "n", - "p", - "#Lambda", - "d", - "He3", - "t", - "^{3}_{#Lambda}H", - "He4"}; - - static constexpr int kMaxNumMom = 2; // X: 0..4, overflow=5 + static constexpr std::array PdgMotherList = {PDG_t::kPiPlus, PDG_t::kKPlus, PDG_t::kK0Short, PDG_t::kNeutron, PDG_t::kProton, PDG_t::kLambda0, o2::constants::physics::Pdg::kDeuteron, o2::constants::physics::Pdg::kHelium3, o2::constants::physics::Pdg::kTriton, o2::constants::physics::Pdg::kHyperTriton, o2::constants::physics::Pdg::kAlpha}; + + static constexpr int NumMotherList = PdgMotherList.size(); + + static constexpr std::array MotherNames = {"#pi^{+}", "K^{+}", "K^{0}_{S}", "n", "p", "#Lambda", "d", "He3", "t", "^{3}_{#Lambda}H", "He4"}; + + static constexpr int MaxNumMom = 2; // X: 0..4, overflow=5 template float averageClusterSizeTrk(const TrackType& track) @@ -315,6 +300,62 @@ struct LFNucleiBATask { } } + template + float getPFromPt(const TrackType& track, const float pt) + { + return pt * std::cosh(track.eta()); + } + + template + float getPzFromPt(const TrackType& track, const float pt) + { + return pt * std::sinh(track.eta()); + } + + template + float getRapidityFromPtMass(const TrackType& track, const float pt, const float mass) + { + const float pz = getPzFromPt(track, pt); + const float p = getPFromPt(track, pt); + const float e = std::sqrt(p * p + mass * mass); + if (e <= std::abs(pz)) { + return -999.f; + } + return 0.5f * std::log((e + pz) / (e - pz)); + } + + // Added TVX check on MC + bool hasGenTVX(const aod::McParticles& particles, int mcCollisionIndex) + { + bool hasHitFT0A = false; + bool hasHitFT0C = false; + + for (const auto& particle : particles) { + if (particle.mcCollisionId() != mcCollisionIndex) + continue; + + if (!particle.isPhysicalPrimary()) + continue; + + auto* pdgInfo = pdgDB->GetParticle(particle.pdgCode()); + if (!pdgInfo || pdgInfo->Charge() == 0.) + continue; + + const float eta = particle.eta(); + + if (eta > 3.5f && eta < 4.9f) + hasHitFT0A = true; + + if (eta > -3.3f && eta < -2.1f) + hasHitFT0C = true; + + if (hasHitFT0A && hasHitFT0C) + return true; + } + + return false; + } + void init(o2::framework::InitContext& context) { if (initITSPID) { @@ -325,13 +366,14 @@ struct LFNucleiBATask { } effEvtSet.clear(); - effEvtSetReady = false; + // effEvtSetReady = false; const AxisSpec pAxis{binsPt, "#it{p} (GeV/#it{c})"}; const AxisSpec ptAxis{binsPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec ptHeAxis{binsPtHe, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec pZAxis{binsPt, "#it{p}/z (GeV/#it{c})"}; const AxisSpec ptZHeAxis{binsPtZHe, "#it{p}_{T}/z (GeV/#it{c})"}; + const AxisSpec ptHeMigrAxis{binsPtHeMigration, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec dedxAxis{binsdEdx, "d#it{E}/d#it{x} A.U."}; const AxisSpec betaAxis{binsBeta, "TOF #beta"}; const AxisSpec dcaxyAxis{binsDCA, "DCAxy (cm)"}; @@ -346,9 +388,13 @@ struct LFNucleiBATask { const AxisSpec avClsAxis{avClsBins, ""}; const AxisSpec avClsEffAxis{avClsBins, " / cosh(#eta)"}; - if (doprocessData == true && doprocessMCReco == true) { + if (((doprocessData) || (doprocessDataLfPid)) && ((doprocessMCReco) || (doprocessMCRecoLfPid) || (doprocessMCGen))) { LOG(fatal) << "Can't enable processData and processMCReco in the same time, pick one!"; } + if (enablePtShiftHe && enablePtShiftPID) { + LOG(fatal) << "Invalid configuration: enablePtShiftHe and enablePtShiftPID cannot be enabled at the same time." + << "They define alternative pT-shift corrections for (anti)helium. Pick one!"; + } if (doprocessEvSgLossMC) { evLossHistos.add("evLoss/hEvent", "Event loss histograms; ; counts", HistType::kTH1F, {{4, 0., 4.}}); evLossHistos.get(HIST("evLoss/hEvent"))->GetXaxis()->SetBinLabel(1, "All Gen."); @@ -356,24 +402,29 @@ struct LFNucleiBATask { evLossHistos.get(HIST("evLoss/hEvent"))->GetXaxis()->SetBinLabel(3, "MC Sel8 (TVX + NoTFB) (reco.)"); evLossHistos.get(HIST("evLoss/hEvent"))->GetXaxis()->SetBinLabel(4, "Sel8 (reco.)"); - evLossHistos.add("evLoss/pt/hDeuteronTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hDeuteronTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hDeuteronTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hDeuteronGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiDeuteronTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiDeuteronTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiDeuteronTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiDeuteronGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - - evLossHistos.add("evLoss/pt/hHeliumTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hHeliumTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hHeliumTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hHeliumGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiHeliumTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiHeliumTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiHeliumTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); - evLossHistos.add("evLoss/pt/hAntiHeliumGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + if (enableDe) { + evLossHistos.add("evLoss/pt/hDeuteronTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hDeuteronTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hDeuteronTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hDeuteronGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiDeuteronTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiDeuteronTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiDeuteronTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiDeuteronGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + } + + if (enableHe) { + evLossHistos.add("evLoss/pt/hHeliumTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hHeliumTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hHeliumTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hHeliumGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiHeliumTriggeredTVX", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiHeliumTriggeredSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiHeliumTriggeredMCSel8", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + evLossHistos.add("evLoss/pt/hAntiHeliumGen", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{100, 0., 5.}}); + } } + if (doprocessMCRecoLfPidEv) { spectraGen.add("LfEv/pT_nocut", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{ptHeAxis}}); spectraGen.add("LfEv/pT_TVXtrigger", "Track #it{p}_{T}; #it{p}_{T} (GeV/#it{c}); counts", HistType::kTH1F, {{ptHeAxis}}); @@ -426,6 +477,35 @@ struct LFNucleiBATask { histoGen.get(HIST("events/hMCReco"))->GetXaxis()->SetBinLabel(2, "Ev sel passed"); histoGen.get(HIST("events/hMCReco"))->GetXaxis()->SetBinLabel(3, "INELgt0"); + histoGen.add("events/hNRecoCollPerMCColl_INEL", "Reco collisions per MC collision;N_{reco coll};Counts", HistType::kTH1D, {{6, -0.5, 5.5}}); + histoGen.add("events/hNRecoCollPerMCColl_INELgt0", "Reco INEL>0 collisions per MC collision;N_{reco coll};Counts", HistType::kTH1D, {{6, -0.5, 5.5}}); + + // Protons + histoGen.add("proton/MCGen/h2ProtonYvsPt", "#it{y} vs #it{p}_{T} (p)", HistType::kTH2F, {{96, -1.2, 1.2}, {800, 0, 8}}); + histoGen.add("proton/MCGen/h2antiProtonYvsPt", "#it{y} vs #it{p}_{T} (p)", HistType::kTH2F, {{96, -1.2, 1.2}, {800, 0, 8}}); + + histoGen.add("proton/MCGen/ptGen_INEL_Prim_P", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + histoGen.add("proton/MCGen/ptGen_INEL_Prim_antiP", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + + histoGen.add("proton/MCGen/ptGen_INELgt0_Prim_P", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + histoGen.add("proton/MCGen/ptGen_INELgt0_Prim_antiP", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + + histoGen.add("proton/MCGenReco/h2ProtonYvsPt", "#it{y} vs #it{p}_{T} (p)", HistType::kTH2F, {{96, -1.2, 1.2}, {800, 0, 8}}); + histoGen.add("proton/MCGenReco/h2antiProtonYvsPt", "#it{y} vs #it{p}_{T} (p)", HistType::kTH2F, {{96, -1.2, 1.2}, {800, 0, 8}}); + + histoGen.add("proton/MCGenReco/ptGen_INEL_Prim_P", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + histoGen.add("proton/MCGenReco/ptGen_INEL_Prim_antiP", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + + histoGen.add("proton/MCGenReco/ptGen_INELgt0_Prim_P", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + histoGen.add("proton/MCGenReco/ptGen_INELgt0_Prim_antiP", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + + histoGen.add("proton/MCReco/ptGen_INEL_Prim_P", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + histoGen.add("proton/MCReco/ptGen_INEL_Prim_antiP", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + + histoGen.add("proton/MCReco/ptGen_INELgt0_Prim_P", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + histoGen.add("proton/MCReco/ptGen_INELgt0_Prim_antiP", "generated particles", HistType::kTH1F, {{800, 0, 8}}); + + // Helium histoGen.add("helium/MCGen/h2HeliumYvsPt", "#it{y} vs #it{p}_{T} (He)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); histoGen.add("helium/MCGen/h2antiHeliumYvsPt", "#it{y} vs #it{p}_{T} (He)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); @@ -460,6 +540,31 @@ struct LFNucleiBATask { histoGen.get(HIST("events/hMCGenRecoVsMult"))->GetXaxis()->SetBinLabel(1, "INEL"); histoGen.get(HIST("events/hMCGenRecoVsMult"))->GetXaxis()->SetBinLabel(2, "INELgt0"); + histoGen.add("events/hMCRecoVsMult", "hMCRecoVsMult", HistType::kTH2D, {{3, 0.f, 3.f}, {binsPercentile}}); + histoGen.get(HIST("events/hMCRecoVsMult"))->GetXaxis()->SetBinLabel(1, "All"); + histoGen.get(HIST("events/hMCRecoVsMult"))->GetXaxis()->SetBinLabel(2, "Ev sel passed"); + histoGen.get(HIST("events/hMCRecoVsMult"))->GetXaxis()->SetBinLabel(3, "INELgt0"); + + histoGen.add("events/hNRecoCollPerMCCollVsMult_INEL", "Reco collisions per MC collision vs FT0M;N_{reco coll};FT0M percentile", HistType::kTH2D, {{6, -0.5, 5.5}, {binsPercentile}}); + histoGen.add("events/hNRecoCollPerMCCollVsMult_INELgt0", "Reco INEL>0 collisions per MC collision vs FT0M;N_{reco coll};FT0M percentile", HistType::kTH2D, {{6, -0.5, 5.5}, {binsPercentile}}); + + // Protons + histoGen.add("proton/MCGen/ptGenVsMult_INEL_Prim_P", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCGen/ptGenVsMult_INEL_Prim_antiP", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCGen/ptGenVsMult_INELgt0_Prim_P", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCGen/ptGenVsMult_INELgt0_Prim_antiP", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + + histoGen.add("proton/MCGenReco/ptGenVsMult_INEL_Prim_P", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCGenReco/ptGenVsMult_INEL_Prim_antiP", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCGenReco/ptGenVsMult_INELgt0_Prim_P", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCGenReco/ptGenVsMult_INELgt0_Prim_antiP", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + + histoGen.add("proton/MCReco/ptGenVsMult_INEL_Prim_P", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCReco/ptGenVsMult_INEL_Prim_antiP", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCReco/ptGenVsMult_INELgt0_Prim_P", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + histoGen.add("proton/MCReco/ptGenVsMult_INELgt0_Prim_antiP", "generated particles", HistType::kTH2F, {{800, 0, 8}, {binsPercentile}}); + + // Helium histoGen.add("helium/MCGen/ptGenVsMult_INEL_Prim_He", "generated particles", HistType::kTH2F, {{ptHeAxis}, {binsPercentile}}); histoGen.add("helium/MCGen/ptGenVsMult_INEL_Prim_antiHe", "generated particles", HistType::kTH2F, {{ptHeAxis}, {binsPercentile}}); histoGen.add("helium/MCGen/ptGenVsMult_INELgt0_Prim_He", "generated particles", HistType::kTH2F, {{ptHeAxis}, {binsPercentile}}); @@ -486,7 +591,9 @@ struct LFNucleiBATask { debugHistos.add("qa/h1VtxZ_Centrality", "V_{z};V_{z} (in cm); counts", HistType::kTH1F, {{1500, -15, 15}}); if (enableCentrality) { - debugHistos.add("event/hFT0M", "hFT0M", HistType::kTH1F, {{binsPercentile, "Centrality FT0M"}}); + debugHistos.add("event/hFT0M", "hFT0M (INEL)", HistType::kTH1F, {{binsPercentile, "Centrality FT0M"}}); + debugHistos.add("event/hFT0M_INELgt0", "hFT0M (INELgt0)", HistType::kTH1F, {{binsPercentile, "Centrality FT0M"}}); + debugHistos.add("event/hFT0M_INELgt1", "hFT0M (INELgt1)", HistType::kTH1F, {{binsPercentile, "Centrality FT0M"}}); debugHistos.add("event/hFV0M", "hFV0M", HistType::kTH1F, {{binsPercentile, "Centrality FV0M"}}); } } @@ -497,12 +604,13 @@ struct LFNucleiBATask { hSkim->GetXaxis()->SetBinLabel(2, "Skimmed events"); if (enableCentrality) { - histos.add("event/eventSelection", "eventSelection", HistType::kTH2D, {{8, -0.5, 7.5}, {binsPercentile, "Centrality FT0M"}}); + histos.add("event/eventSelection", "eventSelection", HistType::kTH2D, {{11, -0.5, 10.5}, {binsPercentile, "Centrality FT0M"}}); auto h2d = histos.get(HIST("event/eventSelection")); - if (skimmingOptions.applySkimming) + if (skimmingOptions.applySkimming) { h2d->GetXaxis()->SetBinLabel(1, "Skimmed events"); - else + } else { h2d->GetXaxis()->SetBinLabel(1, "Total"); + } h2d->GetXaxis()->SetBinLabel(2, "TVX trigger cut"); h2d->GetXaxis()->SetBinLabel(3, "TF border cut"); @@ -511,13 +619,17 @@ struct LFNucleiBATask { h2d->GetXaxis()->SetBinLabel(6, "Sel8 cut"); h2d->GetXaxis()->SetBinLabel(7, "Z-vert Cut"); h2d->GetXaxis()->SetBinLabel(8, "Multiplicity cut"); + h2d->GetXaxis()->SetBinLabel(9, "MB"); + h2d->GetXaxis()->SetBinLabel(10, "INEL > 0"); + h2d->GetXaxis()->SetBinLabel(11, "INEL > 1"); } else { - histos.add("event/eventSelection", "eventSelection", HistType::kTH1D, {{8, -0.5, 7.5}}); + histos.add("event/eventSelection", "eventSelection", HistType::kTH1D, {{11, -0.5, 10.5}}); auto h1d = histos.get(HIST("event/eventSelection")); - if (skimmingOptions.applySkimming) + if (skimmingOptions.applySkimming) { h1d->GetXaxis()->SetBinLabel(1, "Skimmed events"); - else + } else { h1d->GetXaxis()->SetBinLabel(1, "Total"); + } h1d->GetXaxis()->SetBinLabel(2, "TVX trigger cut"); h1d->GetXaxis()->SetBinLabel(3, "TF border cut"); @@ -526,6 +638,9 @@ struct LFNucleiBATask { h1d->GetXaxis()->SetBinLabel(6, "Sel8 cut"); h1d->GetXaxis()->SetBinLabel(7, "Z-vert Cut"); h1d->GetXaxis()->SetBinLabel(8, "Multiplicity cut"); + h1d->GetXaxis()->SetBinLabel(9, "MB"); + h1d->GetXaxis()->SetBinLabel(10, "INEL > 0"); + h1d->GetXaxis()->SetBinLabel(11, "INEL > 1"); } if (enableCentrality) @@ -540,9 +655,9 @@ struct LFNucleiBATask { if (enableDebug) { histos.add("qa/h1ITSncr", "number of crossed rows in ITS; ITSncr; counts", HistType::kTH1F, {{12, 0, 12}}); - histos.add("qa/h1TPCncr", "number of crossed rows in TPC; TPCncr; counts", HistType::kTH1F, {{150, 60, 170}}); - histos.add("qa/h1rTPC", "ratio of ncr over findable in TPC; rTPC; counts", HistType::kTH1F, {{200, 0.9, 1.8}}); - histos.add("qa/h1TPCnfound", "ratio of found cluster in TPC; TPCnfound; counts", HistType::kTH1F, {{150, 60, 170}}); + histos.add("qa/h1TPCncr", "number of crossed rows in TPC; TPCncr; counts", HistType::kTH1F, {{110, 60, 170}}); + histos.add("qa/h1rTPC", "ratio of ncr over findable in TPC; rTPC; counts", HistType::kTH1F, {{300, 0.45, 1.8}}); + histos.add("qa/h1TPCnfound", "ratio of found cluster in TPC; TPCnfound; counts", HistType::kTH1F, {{110, 60, 170}}); histos.add("qa/h1chi2ITS", "#chi^{2}_{ITS}/n_{ITS}; #chi^{2}_{ITS}/n_{ITS};counts", HistType::kTH1F, {{51, -0.5, 50.5}}); histos.add("qa/h1chi2TPC", "#chi^{2}_{TPC}/n_{TPC}; #chi^{2}_{TPC}/n_{TPC}; counts", HistType::kTH1F, {{11, -0.5, 10.5}}); } @@ -776,6 +891,10 @@ struct LFNucleiBATask { histos.add("tracks/deuteron/h2DeuteronYvsPt", "#it{y} vs #it{p}_{T} (d)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptAxis}}); histos.add("tracks/deuteron/h2antiDeuteronYvsPt", "#it{y} vs #it{p}_{T} (#bar{d})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptAxis}}); + + histos.add("tracks/deuteron/h2DeuteronShiftYvsPt", "#it{y} vs #it{p}_{T} (d)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptAxis}}); + histos.add("tracks/deuteron/h2antiDeuteronShiftYvsPt", "#it{y} vs #it{p}_{T} (#bar{d})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptAxis}}); + histos.add("tracks/deuteron/h2DeuteronEtavsPt", "#it{#eta} vs #it{p}_{T} (d)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptAxis}}); histos.add("tracks/deuteron/h2antiDeuteronEtavsPt", "#it{#eta} vs #it{p}_{T} (#bar{d})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptAxis}}); } @@ -785,23 +904,52 @@ struct LFNucleiBATask { } if (enableHe) { histos.add("tracks/helium/h2HeliumYvsPt_Z2", "#it{y} vs #it{p}_{T} (He)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); + histos.add("tracks/helium/h2HeliumShiftYvsPt_Z2", "#it{y} vs #it{p}_{T} (He)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); histos.add("tracks/helium/h2HeliumEtavsPt_Z2", "#it{#eta} vs #it{p}_{T} (He)", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); + histos.add("tracks/helium/h2antiHeliumYvsPt_Z2", "#it{y} vs #it{p}_{T} (#bar{He})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); + histos.add("tracks/helium/h2antiHeliumShiftYvsPt_Z2", "#it{y} vs #it{p}_{T} (#bar{He})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); + histos.add("tracks/helium/h2antiHeliumEtavsPt_Z2", "#it{#eta} vs #it{p}_{T} (#bar{He})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); + histos.add("tracks/helium/h1HeliumSpectra_Z2", "#it{p}_{T} (He)", HistType::kTH1F, {ptHeAxis}); histos.add("tracks/helium/h1antiHeliumSpectra_Z2", "#it{p}_{T} (#bar{He})", HistType::kTH1F, {ptHeAxis}); if (enableDebug) { debugHistos.add("tracks/helium/h2HeliumPidTrackingVsPt", "#it{p}_{T} (He) vs PIDforTracking", HistType::kTH2F, {{80, 0, 8}, {12, -0.5, 11.5}}); debugHistos.add("tracks/helium/h2antiHeliumPidTrackingVsPt", "#it{p}_{T} (#bar{He}) vs PIDforTracking", HistType::kTH2F, {{80, 0, 8}, {12, -0.5, 11.5}}); + + debugHistos.add("tracks/helium/h2HeCutFlowVsPt", "He cut-flow checker;#it{p}_{T} (GeV/#it{c});selection step", HistType::kTH2F, {{ptHeAxis}, {10, -0.5, 9.5}}); + debugHistos.add("tracks/helium/h2antiHeCutFlowVsPt", "#bar{He} cut-flow checker;#it{p}_{T} (GeV/#it{c});selection step", HistType::kTH2F, {{ptHeAxis}, {10, -0.5, 9.5}}); + + auto hHeCutFlow = debugHistos.get(HIST("tracks/helium/h2HeCutFlowVsPt")); + hHeCutFlow->GetYaxis()->SetBinLabel(1, "all tracks"); + hHeCutFlow->GetYaxis()->SetBinLabel(2, "quality cuts"); + hHeCutFlow->GetYaxis()->SetBinLabel(3, "global track"); + hHeCutFlow->GetYaxis()->SetBinLabel(4, "p eta cuts"); + hHeCutFlow->GetYaxis()->SetBinLabel(5, "pt shift"); + hHeCutFlow->GetYaxis()->SetBinLabel(6, "rapidity"); + hHeCutFlow->GetYaxis()->SetBinLabel(7, "ITS PID"); + hHeCutFlow->GetYaxis()->SetBinLabel(8, "DCA"); + hHeCutFlow->GetYaxis()->SetBinLabel(9, "TPC PID"); + hHeCutFlow->GetYaxis()->SetBinLabel(10, "TOF hit"); + + auto hAntiHeCutFlow = debugHistos.get(HIST("tracks/helium/h2antiHeCutFlowVsPt")); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(1, "all tracks"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(2, "quality cuts"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(3, "global track"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(4, "p eta cuts"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(5, "pt shift"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(6, "rapidity"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(7, "ITS PID"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(8, "DCA"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(9, "TPC PID"); + hAntiHeCutFlow->GetYaxis()->SetBinLabel(10, "TOF hit"); } if (outFlagOptions.doTOFplots && enableCentrality) { histos.add("tracks/helium/TOF/h2HeliumSpectraVsMult_Z2", "#it{p}_{T} (He)", HistType::kTH2F, {{ptHeAxis}, {binsPercentile}}); histos.add("tracks/helium/TOF/h2antiHeliumSpectraVsMult_Z2", "#it{p}_{T} (#bar{He})", HistType::kTH2F, {{ptHeAxis}, {binsPercentile}}); } - - histos.add("tracks/helium/h2antiHeliumYvsPt_Z2", "#it{y} vs #it{p}_{T} (#bar{He})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); - histos.add("tracks/helium/h2antiHeliumEtavsPt_Z2", "#it{#eta} vs #it{p}_{T} (#bar{He})", HistType::kTH2F, {{96, -1.2, 1.2}, {ptHeAxis}}); } if (enableAl) { histos.add("tracks/alpha/h1AlphaSpectra", "#it{p}_{T} (#alpha)", HistType::kTH1F, {ptAxis}); @@ -815,6 +963,16 @@ struct LFNucleiBATask { histos.get(HIST("tracks/hItsDeHeChecker"))->GetXaxis()->SetBinLabel(3, "keptDe"); histos.get(HIST("tracks/hItsDeHeChecker"))->GetXaxis()->SetBinLabel(4, "keptHe"); + if (enableDebug) { + debugHistos.add("tracks/helium/migration/h2PtGenVsPtRecoPrimHe", "Primary ^{3}He;#it{p}_{T}^{gen} (GeV/#it{c});#it{p}_{T}^{reco} (GeV/#it{c})", HistType::kTH2F, {{ptHeMigrAxis}, {ptHeMigrAxis}}); + + debugHistos.add("tracks/helium/migration/h2PtGenVsPtRecoPrimAntiHe", "Primary ^{3}#bar{He};#it{p}_{T}^{gen} (GeV/#it{c});#it{p}_{T}^{reco} (GeV/#it{c})", HistType::kTH2F, {{ptHeMigrAxis}, {ptHeMigrAxis}}); + + debugHistos.add("tracks/helium/migration/TOF/h2PtGenVsPtRecoPrimHe", "Primary ^{3}He, TOF;#it{p}_{T}^{gen} (GeV/#it{c});#it{p}_{T}^{reco} (GeV/#it{c})", HistType::kTH2F, {{ptHeMigrAxis}, {ptHeMigrAxis}}); + + debugHistos.add("tracks/helium/migration/TOF/h2PtGenVsPtRecoPrimAntiHe", "Primary ^{3}#bar{He}, TOF;#it{p}_{T}^{gen} (GeV/#it{c});#it{p}_{T}^{reco} (GeV/#it{c})", HistType::kTH2F, {{ptHeMigrAxis}, {ptHeMigrAxis}}); + } + // inclusive production if (enableTrackingEff) { debugHistos.add("tracks/trackingEff/h1_its", "#it{p}_{T} (p)", HistType::kTH1F, {ptAxis}); @@ -1078,7 +1236,7 @@ struct LFNucleiBATask { histos.add("tracks/proton/dca/before/hDCAxyVsPtProtonTrueMaterial", "DCAxy vs Pt (p); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptAxis}, {dcaxyAxis}}); histos.add("tracks/proton/dca/before/hNumMothers", "N mothers per particle; N mothers;counts", HistType::kTH1I, {{7, 1.0, 8.0}}); - histos.add("tracks/proton/dca/before/hMomTrueMaterial", "MC mothers;mother index;mother type; mother #it{p}_{T}", HistType::kTH3F, {{2, -2.0, 2.0}, {kNumMotherList + 2, -1.5, static_cast(kNumMotherList) + 0.5}, {150, 0.0, 15.0}}); + histos.add("tracks/proton/dca/before/hMomTrueMaterial", "MC mothers;mother index;mother type; mother #it{p}_{T}", HistType::kTH3F, {{2, -2.0, 2.0}, {NumMotherList + 2, -1.5, static_cast(NumMotherList) + 0.5}, {150, 0.0, 15.0}}); std::shared_ptr hTempPr = histos.get(HIST("tracks/proton/dca/before/hMomTrueMaterial")); TH3* hPdgPr = hTempPr.get(); @@ -1090,8 +1248,8 @@ struct LFNucleiBATask { TAxis* ayPdgPr = hPdgPr->GetYaxis(); ayPdgPr->SetBinLabel(1, "undef."); ayPdgPr->SetBinLabel(2, "other"); - for (int i = 0; i < kNumMotherList; i++) { - ayPdgPr->SetBinLabel(i + 3, kMotherNames[i]); + for (int i = 0; i < NumMotherList; i++) { + ayPdgPr->SetBinLabel(i + 3, MotherNames[i]); } histos.add("tracks/proton/dca/before/hDCAxyVsPtantiProtonTrue", "DCAxy vs Pt (#bar{p}); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptAxis}, {dcaxyAxis}}); @@ -1150,7 +1308,7 @@ struct LFNucleiBATask { histos.add("tracks/deuteron/dca/before/hDCAxyVsPtDeuteronTrueMaterial", "DCAxy vs Pt (d); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptAxis}, {dcaxyAxis}}); histos.add("tracks/deuteron/dca/before/hNumMothers", "N mothers per particle; N mothers;counts", HistType::kTH1I, {{7, 1.0, 8.0}}); - histos.add("tracks/deuteron/dca/before/hMomTrueMaterial", "MC mothers;mother index;mother type; mother #it{p}_{T}", HistType::kTH3F, {{2, -2.0, 2.0}, {kNumMotherList + 2, -1.5, static_cast(kNumMotherList) + 0.5}, {150, 0.0, 15.0}}); + histos.add("tracks/deuteron/dca/before/hMomTrueMaterial", "MC mothers;mother index;mother type; mother #it{p}_{T}", HistType::kTH3F, {{2, -2.0, 2.0}, {NumMotherList + 2, -1.5, static_cast(NumMotherList) + 0.5}, {150, 0.0, 15.0}}); std::shared_ptr hTempDe = histos.get(HIST("tracks/deuteron/dca/before/hMomTrueMaterial")); TH3* hPdgDe = hTempDe.get(); @@ -1162,8 +1320,8 @@ struct LFNucleiBATask { TAxis* ayPdgDe = hPdgDe->GetYaxis(); ayPdgDe->SetBinLabel(1, "undef."); ayPdgDe->SetBinLabel(2, "other"); - for (int i = 0; i < kNumMotherList; i++) { - ayPdgDe->SetBinLabel(i + 3, kMotherNames[i]); + for (int i = 0; i < NumMotherList; i++) { + ayPdgDe->SetBinLabel(i + 3, MotherNames[i]); } histos.add("tracks/deuteron/dca/before/hDCAxyVsPtantiDeuteronTrue", "DCAxy vs Pt (#bar{d}); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptAxis}, {dcaxyAxis}}); @@ -1282,21 +1440,6 @@ struct LFNucleiBATask { histos.add("tracks/triton/dca/before/TOF/hDCAxyVsPtantiTritonTruePrim", "DCAxy vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptAxis}, {dcaxyAxis}}); histos.add("tracks/triton/dca/before/TOF/hDCAxyVsPtantiTritonTrueSec", "DCAxy vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptAxis}, {dcaxyAxis}}); histos.add("tracks/triton/dca/before/TOF/hDCAxyVsPtantiTritonTrueMaterial", "DCAxy vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptAxis}, {dcaxyAxis}}); - - // Unused histograms - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtTritonTrue", "DCAz vs Pt (t); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtTritonTruePrim", "DCAz vs Pt (t); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtTritonTrueSec", "DCAz vs Pt (t); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtTritonTrueMaterial", "DCAz vs Pt (t); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtTritonTrueTransport", "DCAz vs Pt (t); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtantiTritonTrue", "DCAz vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtantiTritonTruePrim", "DCAz vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtantiTritonTrueSec", "DCAz vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtantiTritonTrueMaterial", "DCAz vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); - // histos.add("tracks/triton/dca/before/TOF/hDCAzVsPtantiTritonTrueTransport", "DCAz vs Pt (#bar{t}); #it{p}_{T} (GeV/#it{c}); DCAz (cm)", HistType::kTH2F, {{ptAxis}, {dcazAxis}}); } } @@ -1324,7 +1467,7 @@ struct LFNucleiBATask { histos.add("tracks/helium/dca/before/hDCAxyVsPtHeliumTrueMaterial", "DCAxy vs Pt (He); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptZHeAxis}, {dcaxyAxis}}); histos.add("tracks/helium/dca/before/hNumMothers", "N mothers per particle; N mothers;counts", HistType::kTH1I, {{7, 1.0, 8.0}}); - histos.add("tracks/helium/dca/before/hMomTrueMaterial", "MC mothers;mother index;mother type; mother #it{p}_{T}", HistType::kTH3F, {{2, -2.0, 2.0}, {kNumMotherList + 2, -1.5, static_cast(kNumMotherList) + 0.5}, {150, 0.0, 15.0}}); + histos.add("tracks/helium/dca/before/hMomTrueMaterial", "MC mothers;mother index;mother type; mother #it{p}_{T}", HistType::kTH3F, {{2, -2.0, 2.0}, {NumMotherList + 2, -1.5, static_cast(NumMotherList) + 0.5}, {150, 0.0, 15.0}}); // Fix for getting TH3 pointer std::shared_ptr hTempHe = histos.get(HIST("tracks/helium/dca/before/hMomTrueMaterial")); @@ -1337,8 +1480,8 @@ struct LFNucleiBATask { TAxis* ayPdgHe = hPdgHe->GetYaxis(); ayPdgHe->SetBinLabel(1, "undef."); ayPdgHe->SetBinLabel(2, "other"); - for (int i = 0; i < kNumMotherList; i++) { - ayPdgHe->SetBinLabel(i + 3, kMotherNames[i]); + for (int i = 0; i < NumMotherList; i++) { + ayPdgHe->SetBinLabel(i + 3, MotherNames[i]); } histos.add("tracks/helium/dca/before/hDCAxyVsPtantiHeliumTrue", "DCAxy vs Pt (#bar{He}); #it{p}_{T} (GeV/#it{c}); DCAxy (cm)", HistType::kTH2F, {{ptZHeAxis}, {dcaxyAxis}}); @@ -1796,9 +1939,10 @@ struct LFNucleiBATask { } else { histos.add("tracks/deuteron/h2DeuteronVspTNSigmaTPC", "NSigmaTPC(d) vs pT; #it{p}_{T} (GeV/#it{c}); NSigmaTPC", HistType::kTH2F, {{ptAxis}, {sigmaTPCAxis}}); histos.add("tracks/deuteron/h2antiDeuteronVspTNSigmaTPC", "NSigmaTPC(#bar{d}) vs pT; #it{p}_{T} (GeV/#it{c}); NSigmaTPC", HistType::kTH2F, {{ptAxis}, {sigmaTPCAxis}}); - histos.add("tracks/deuteron/h2DeuteronVspTNSigmaTPCTruePrim", "NSigmaTPC(d) vs pT; #it{p}_{T} (GeV/#it{c}); NSigmaTPC", HistType::kTH2F, {{ptAxis}, {sigmaTPCAxis}}); - histos.add("tracks/deuteron/h2antiDeuteronVspTNSigmaTPCTruePrim", "NSigmaTPC(#bar{d}) vs pT; #it{p}_{T} (GeV/#it{c}); NSigmaTPC", HistType::kTH2F, {{ptAxis}, {sigmaTPCAxis}}); } + + histos.add("tracks/deuteron/h2DeuteronVspTNSigmaTPCTruePrim", "NSigmaTPC(d) vs pT; #it{p}_{T} (GeV/#it{c}); NSigmaTPC", HistType::kTH2F, {{ptAxis}, {sigmaTPCAxis}}); + histos.add("tracks/deuteron/h2antiDeuteronVspTNSigmaTPCTruePrim", "NSigmaTPC(#bar{d}) vs pT; #it{p}_{T} (GeV/#it{c}); NSigmaTPC", HistType::kTH2F, {{ptAxis}, {sigmaTPCAxis}}); } if (enableTr) { histos.add("tracks/triton/h2TritonVspTNSigmaTPC", "NSigmaTPC(t) vs pT; #it{p}_{T} (GeV/#it{c}); NSigmaTPC", HistType::kTH2F, {{ptAxis}, {sigmaTPCAxis}}); @@ -2160,12 +2304,29 @@ struct LFNucleiBATask { } } // To be optimised - if (!doprocessMCGen && !doprocessMCReco && !doprocessMCRecoLfPid && !doprocessMCRecoFiltered && !doprocessMCRecoFilteredLight) { - LOG(info) << "Histograms of LFNucleiBATask:"; + if (!doprocessMCGen && !doprocessMCReco && !doprocessMCRecoLfPid && !doprocessMCRecoFiltered && !doprocessMCRecoFilteredLight && !doprocessEvSgLossMC && !doprocessMCGenLosses) { + LOG(info) << "DATA mode "; + LOG(info) << "Histograms of lfNucleiBATask:"; histos.print(); return; } + LOG(info) << " MC mode "; // MC histograms - all, primary, sec. from weak decay, sec. from material + + if (enableDebug) { + spectraGen.add("hEffEvtSetVsSG", "effEvtSet vs SmallGroups;in effEvtSet;has accepted reco collision", HistType::kTH2D, {{2, -0.5, 1.5}, {2, -0.5, 1.5}}); + auto hEffCheck = spectraGen.get(HIST("hEffEvtSetVsSG")); + hEffCheck->GetXaxis()->SetBinLabel(1, "Not in effEvtSet"); + hEffCheck->GetXaxis()->SetBinLabel(2, "In effEvtSet"); + hEffCheck->GetYaxis()->SetBinLabel(1, "No accepted reco"); + hEffCheck->GetYaxis()->SetBinLabel(2, "Accepted reco"); + + spectraGen.add("hEffEvtSetFalsePositive", "False positives in effEvtSet", HistType::kTH1D, {{2, 0., 2.}}); + auto hFalsePos = spectraGen.get(HIST("hEffEvtSetFalsePositive")); + hFalsePos->GetXaxis()->SetBinLabel(1, "No associated reco"); + hFalsePos->GetXaxis()->SetBinLabel(2, "Reco exists, fails cuts"); + } + if (enableCentrality) spectraGen.add("histGenVetxZ", "PosZ generated events", HistType::kTH2F, {{1500, -15.f, 15.f, "Vertex Z (cm)"}, {binsPercentile, "Centrality FT0M"}}); else @@ -2300,10 +2461,13 @@ struct LFNucleiBATask { spectraGen.add("alpha/histGenPtantiAlSec", "generated particles", HistType::kTH1F, {ptAxis}); spectraGen.add("alpha/histSecTransportPtantiAl", "generated particles", HistType::kTH1F, {ptAxis}); } - LOG(info) << "Histograms of LFNucleiBATask:"; + + LOG(info) << "MC Histograms defined"; + LOG(info) << "Histograms of lfNucleiBATask:"; histos.print(); - if (doprocessMCGen) - spectraGen.print(); + spectraGen.print(); + if (doprocessMCGenLosses) + histoGen.print(); } template @@ -2405,26 +2569,57 @@ struct LFNucleiBATask { if (enableCentrality && (centFT0M <= cfgMultCutLow || centFT0M > cfgMultCutHigh)) { return; } - if (enableCentrality) + if (enableCentrality) { histos.fill(HIST("event/eventSelection"), 7, centFT0M); - else + histos.fill(HIST("event/eventSelection"), 8, centFT0M); + } else { histos.fill(HIST("event/eventSelection"), 7); + histos.fill(HIST("event/eventSelection"), 8); + } + + if (event.isInelGt0()) { + if (enableCentrality) + histos.fill(HIST("event/eventSelection"), 9, centFT0M); + else + histos.fill(HIST("event/eventSelection"), 9); + } + if (event.isInelGt1()) { + if (enableCentrality) + histos.fill(HIST("event/eventSelection"), 10, centFT0M); + else + histos.fill(HIST("event/eventSelection"), 10); + } - float gamma = 0., massTOF = 0., massTOFhe = 0., massTOFantihe = 0., heTPCmomentum = 0.f, antiheTPCmomentum = 0.f, heP = 0.f, antiheP = 0.f, hePt = 0.f, antihePt = 0.f, antiDPt = 0.f, DPt = 0.f; - bool isTritonTPCpid = false; + if (evselOptions.useINELgt0cut && !event.isInelGt0()) + return; + if (evselOptions.useINELgt1cut && !event.isInelGt1()) + return; + + bool passPrTPCpid = false; + bool passDeTPCpid = false; + bool passTrTPCpid = false; + bool passHeTPCpid = false; + bool passAlTPCpid = false; bool prRapCut = false; bool deRapCut = false; bool trRapCut = false; bool heRapCut = false; bool alRapCut = false; + bool hasTOFplots = false; + // Event histos fill if (enableCentrality) histos.fill(HIST("event/h1VtxZ"), event.posZ(), centFT0M); else histos.fill(HIST("event/h1VtxZ"), event.posZ()); - if (enableDebug && enableCentrality) + if (enableDebug && enableCentrality) { debugHistos.fill(HIST("event/hFT0M"), centFT0M); + if (event.isInelGt0()) + debugHistos.fill(HIST("event/hFT0M_INELgt0"), centFT0M); + if (event.isInelGt1()) + debugHistos.fill(HIST("event/hFT0M_INELgt1"), centFT0M); + } if constexpr (IsFilteredData) { if (enableCentrality) @@ -2440,53 +2635,116 @@ struct LFNucleiBATask { } tracks.copyIndexBindings(tracksWithITS); + auto tpcChi2NclRange = (std::vector)trkqcOptions.tpcChi2NclCuts; + auto itsChi2NclRange = (std::vector)trkqcOptions.itsChi2NclCuts; for (auto const& track : tracksWithITS) { - if constexpr (!IsFilteredData) { - if (!track.isGlobalTrackWoDCA() && filterOptions.enableIsGlobalTrack) { - continue; - } - } - std::bitset<8> itsClusterMap = track.itsClusterMap(); + // Init all temp variables inside the track loop + float gamma = 0.; - if constexpr (!IsFilteredData) { - if (nsigmaITSvar.showAverageClusterSize && outFlagOptions.enablePIDplot) - histos.fill(HIST("tracks/avgClusterSizePerCoslInvVsITSlayers"), track.p(), averageClusterSizePerCoslInv(track), track.itsNCls()); + // Deuteron and helium pt init, before applying shift + const float trackPt = track.pt(); + const float trackP = track.p(); + const float trackTPCmomentum = track.tpcInnerParam(); + + float DPt = trackPt; + float antiDPt = trackPt; + + float hePt = trackPt; + float antihePt = trackPt; + + float heP = trackP; + float antiheP = trackP; + + float heTPCmomentum = trackTPCmomentum; + float antiheTPCmomentum = trackTPCmomentum; + + float massTOF = -99.f, massTOFhe = -99.f, massTOFantihe = -99.f; + + // all tracks checker + if (enableDebug && enableHe) { + if (track.sign() > 0) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * track.pt(), 0); + else + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * track.pt(), 0); } - if (track.itsNCls() < trkqcOptions.cfgCutITSClusters || + std::bitset<8> itsClusterMap = track.itsClusterMap(); + + // Quality cuts + if ((trkqcOptions.cfgCutITSClusters >= 0 && track.itsNCls() < trkqcOptions.cfgCutITSClusters) || track.tpcNClsCrossedRows() < trkqcOptions.cfgCutTPCXRows || track.tpcNClsFound() < trkqcOptions.cfgCutTPCClusters || track.tpcCrossedRowsOverFindableCls() < trkqcOptions.cfgCutTPCCROFnd) { continue; } - auto tpcChi2NclRange = (std::vector)trkqcOptions.tpcChi2NclCuts; + // TCP/ITS Chi2/ncl cuts if ((track.tpcChi2NCl() < tpcChi2NclRange[0]) || (track.tpcChi2NCl() > tpcChi2NclRange[1])) continue; - auto itsChi2NclRange = (std::vector)trkqcOptions.itsChi2NclCuts; if ((track.itsChi2NCl() < itsChi2NclRange[0]) || (track.itsChi2NCl() > itsChi2NclRange[1])) continue; + // qchecks checker + if (enableDebug && enableHe) { + if (track.sign() > 0) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * track.pt(), 1); + else + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * track.pt(), 1); + } + + // QA histos fill + if (enableDebug) { + histos.fill(HIST("qa/h1ITSncr"), track.itsNCls()); + histos.fill(HIST("qa/h1TPCncr"), track.tpcNClsCrossedRows()); + histos.fill(HIST("qa/h1TPCnfound"), track.tpcNClsFound()); + histos.fill(HIST("qa/h1rTPC"), track.tpcCrossedRowsOverFindableCls()); + histos.fill(HIST("qa/h1chi2ITS"), track.itsChi2NCl()); + histos.fill(HIST("qa/h1chi2TPC"), track.tpcChi2NCl()); + debugHistos.fill(HIST("debug/h2TPCsignVsTPCmomentum_AllTracks"), track.tpcInnerParam() / (1.f * track.sign()), track.tpcSignal()); + } + + // isGlobalTrack + if constexpr (!IsFilteredData) { + if (!track.isGlobalTrackWoDCA() && filterOptions.enableIsGlobalTrack) { + continue; + } + } + if (enableDebug && enableHe) { + if (track.sign() > 0) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * track.pt(), 2); + else + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * track.pt(), 2); + } + // p & eta cut if (std::abs(track.tpcInnerParam()) < kinemOptions.cfgMomentumCut || std::abs(track.eta()) > kinemOptions.cfgEtaCut) continue; + if (enableDebug && enableHe) { + if (track.sign() > 0) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * track.pt(), 3); + else + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * track.pt(), 3); + } if (outFlagOptions.enablePIDplot) { histos.fill(HIST("tracks/h1pT"), track.pt()); histos.fill(HIST("tracks/h1p"), track.p()); } - isTritonTPCpid = std::abs(track.tpcNSigmaTr()) < nsigmaTPCvar.nsigmaTPCTr; + passPrTPCpid = std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr; + passDeTPCpid = std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe; + passTrTPCpid = std::abs(track.tpcNSigmaTr()) < nsigmaTPCvar.nsigmaTPCTr; + passHeTPCpid = std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe; + passAlTPCpid = std::abs(track.tpcNSigmaAl()) < nsigmaTPCvar.nsigmaTPCAl; + + hasTOFplots = track.hasTOF() && outFlagOptions.doTOFplots; + bool heliumPID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; bool tritonPID = track.pidForTracking() == o2::track::PID::Triton; bool deuteronPID = track.pidForTracking() == o2::track::PID::Deuteron; - float shiftPtPos = 0.f; - float shiftPtNeg = 0.f; - float shiftPtPID = 0.f; - if (enablePtShiftHe && !fShiftPtHe) { fShiftPtHe = new TF1("fShiftPtHe", "[0] * exp([1] + [2] * x) + [3] + [4] * x", 0.f, 8.f); auto parHe = (std::vector)parShiftPtHe; // NOLINT @@ -2505,22 +2763,16 @@ struct LFNucleiBATask { fShiftAntiD->SetParameters(parAntiD[0], parAntiD[1], parAntiD[2], parAntiD[3], parAntiD[4]); } + // Deuteron pT-shift if (enablePtShiftPID && !fShiftPtPID) { fShiftPtPID = new TF1("fShiftPtPID", "[0] * exp([1] + [2] * x) + [3] + [4] * x + [5] * x * x + [6] * x * x * x", 0.f, 8.f); auto parPID = (std::vector)parShiftPtPID; // NOLINT fShiftPtPID->SetParameters(parPID[0], parPID[1], parPID[2], parPID[3], parPID[4], parPID[5], parPID[6]); } - switch (unableAntiDPtShift) { - case 0: - if (enablePtShiftAntiD && fShiftAntiD) { - auto shiftAntiD = fShiftAntiD->Eval(track.pt()); - antiDPt = track.pt() - shiftAntiD; - } - break; - case 1: - antiDPt = track.pt(); - break; + if (unableAntiDPtShift == 0 && enablePtShiftAntiD && fShiftAntiD) { + const auto shiftAntiD = fShiftAntiD->Eval(track.pt()); + antiDPt = track.pt() - shiftAntiD; } if (enablePtShiftD && !fShiftD) { @@ -2529,44 +2781,36 @@ struct LFNucleiBATask { fShiftD->SetParameters(parD[0], parD[1], parD[2], parD[3], parD[4]); } - switch (unableDPtShift) { - case 0: - if (enablePtShiftD && fShiftD) { - auto shiftD = fShiftD->Eval(track.pt()); - DPt = track.pt() - shiftD; - } - break; - case 1: - DPt = track.pt(); - break; + if (unableDPtShift == 0 && enablePtShiftD && fShiftD) { + const auto shiftD = fShiftD->Eval(track.pt()); + DPt = track.pt() - shiftD; } - switch (helium3Pt) { - case 0: - hePt = track.pt(); - if (enablePtShiftHe && fShiftPtHe) { - shiftPtPos = fShiftPtHe->Eval(2 * track.pt()); - hePt = track.pt() - shiftPtPos / 2.f; - } - antihePt = track.pt(); - if (enablePtShiftHe && fShiftPtantiHe) { - shiftPtNeg = fShiftPtantiHe->Eval(2 * track.pt()); - antihePt = track.pt() - shiftPtNeg / 2.f; - } - if (enablePtShiftPID && fShiftPtPID) { - shiftPtPID = fShiftPtPID->Eval(2 * track.pt()); - if (tritonPID && (track.pt() <= 1.25f)) { - hePt = track.pt() - shiftPtPID / 2.f; - antihePt = track.pt() - shiftPtPID / 2.f; - } - } - break; - case 1: - hePt = 2.f * track.pt(); - antihePt = 2.f * track.pt(); - break; + // Helium pT-shift and WrongPid pT-shift + if (enablePtShiftHe && fShiftPtHe) { + hePt -= fShiftPtHe->Eval(2.f * track.pt()) / 2.f; + } + + if (enablePtShiftHe && fShiftPtantiHe) { + antihePt -= fShiftPtantiHe->Eval(2.f * track.pt()) / 2.f; } + if (enablePtShiftPID && fShiftPtPID && tritonPID && track.pt() <= cfgPtShiftPID) { + const auto shiftPtPID = fShiftPtPID->Eval(2.f * track.pt()); + hePt = track.pt() - shiftPtPID / 2.f; + antihePt = track.pt() - shiftPtPID / 2.f; + } + + if (enableDebug && enableHe) { + if (track.sign() > 0) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * hePt, 4); + else + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * antihePt, 4); + } + + heP = getPFromPt(track, hePt); + antiheP = getPFromPt(track, antihePt); + float nITSDe = 99.f; float nITSHe = 99.f; @@ -2574,13 +2818,7 @@ struct LFNucleiBATask { nITSDe = track.itsNSigmaDe(); nITSHe = track.itsNSigmaHe(); } - heP = track.p(); - antiheP = track.p(); - heTPCmomentum = track.tpcInnerParam(); - antiheTPCmomentum = track.tpcInnerParam(); - // auto parDCAxy = (std::vector)parDCAxycuts; - // auto parDCAz = (std::vector)parDCAzcuts; const auto& parDCAxy = parDCAxycuts.value; const auto& parDCAz = parDCAzcuts.value; @@ -2693,6 +2931,8 @@ struct LFNucleiBATask { passDCAxyCutAntiHe = dcaXY2 / std::pow(parDCAxy[3] * (parDCAxy[0] + parDCAxy[1] / std::pow(antihePt, parDCAxy[2])), 2) + dcaZ2 / std::pow(parDCAz[3] * (parDCAz[0] + parDCAz[1] / std::pow(antihePt, parDCAz[2])), 2) <= 1; passDCAzCutAntiHe = dcaXY2 / std::pow(parDCAxy[3] * (parDCAxy[0] + parDCAxy[1] / std::pow(antihePt, parDCAxy[2])), 2) + dcaZ2 / std::pow(parDCAz[3] * (parDCAz[0] + parDCAz[1] / std::pow(antihePt, parDCAz[2])), 2) <= 1; break; + default: + break; } // Rapidity cuts @@ -2700,34 +2940,50 @@ struct LFNucleiBATask { const float rap = track.rapidity(m2z); return (rap > kinemOptions.cfgRapidityCutLow) && (rap < kinemOptions.cfgRapidityCutHigh); }; + auto rapCheckFromPt = [&](const float pt, const float m2z) { + const float yShifted = getRapidityFromPtMass(track, pt, m2z); + return (yShifted > kinemOptions.cfgRapidityCutLow) && (yShifted < kinemOptions.cfgRapidityCutHigh); + }; prRapCut = rapCheck(MassProtonVal); - deRapCut = rapCheck(MassDeuteronVal); trRapCut = rapCheck(MassTritonVal); - heRapCut = rapCheck(MassHeliumVal / 2.0); alRapCut = rapCheck(MassAlphaVal / 2.0); + if (track.sign() > 0) { + deRapCut = rapCheckFromPt(DPt, MassDeuteronVal); + heRapCut = rapCheckFromPt(hePt, MassHeliumVal / 2.0); + } else { + deRapCut = rapCheckFromPt(antiDPt, MassDeuteronVal); + heRapCut = rapCheckFromPt(antihePt, MassHeliumVal / 2.0); + } + isDeuteron = enableDe && deRapCut; isHelium = enableHe && heRapCut; + if (enableDebug && isHelium) { + if (track.sign() > 0) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * hePt, 5); + else + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * antihePt, 5); + } + // ITS PID cut bool passITSDeCut = !nsigmaITSvar.useITSDeCut || (nITSDe > nsigmaITSvar.nsigmaITSDe); bool passITSHeCut = !nsigmaITSvar.useITSHeCut || (nITSHe > nsigmaITSvar.nsigmaITSHe); if constexpr (IsMC && !IsFilteredData) { - int pdgCheck = track.mcParticle().pdgCode(); - if (std::abs(pdgCheck) == PDGDeuteron) + const int pdgCheck = std::abs(track.mcParticle().pdgCode()); + if (pdgCheck == PDGDeuteron) { histos.fill(HIST("tracks/hItsDeHeChecker"), 0); - if (std::abs(pdgCheck) == PDGHelium) + if (passITSDeCut) { + histos.fill(HIST("tracks/hItsDeHeChecker"), 2); + } + } else if (pdgCheck == PDGHelium) { histos.fill(HIST("tracks/hItsDeHeChecker"), 1); - } - - if constexpr (IsMC && !IsFilteredData) { - int pdgCheck = track.mcParticle().pdgCode(); - if ((std::abs(pdgCheck) == PDGDeuteron) && passITSDeCut) - histos.fill(HIST("tracks/hItsDeHeChecker"), 2); - if ((std::abs(pdgCheck) == PDGHelium) && passITSHeCut) - histos.fill(HIST("tracks/hItsDeHeChecker"), 3); + if (passITSHeCut) { + histos.fill(HIST("tracks/hItsDeHeChecker"), 3); + } + } } isDe = isDeuteron && passITSDeCut && track.sign() > 0; @@ -2736,6 +2992,13 @@ struct LFNucleiBATask { isHe = isHelium && passITSHeCut && track.sign() > 0; isAntiHe = isHelium && passITSHeCut && track.sign() < 0; + if (enableDebug && enableHe) { + if (isHe) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * hePt, 6); + if (isAntiHe) + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * antihePt, 6); + } + isDeWoDCAxy = isDe && passDCAzCutDe; isAntiDeWoDCAxy = isAntiDe && passDCAzCutAntiDe; isHeWoDCAxy = isHe && passDCAzCutHe; @@ -2746,25 +3009,25 @@ struct LFNucleiBATask { isHeWoDCAz = isHe && passDCAxyCutHe; isAntiHeWoDCAz = isAntiHe && passDCAxyCutAntiHe; - isDeWoDCAxyWTPCpid = isDeWoDCAxy && std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe; - isAntiDeWoDCAxyWTPCpid = isAntiDeWoDCAxy && std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe; - isHeWoDCAxyWTPCpid = isHeWoDCAxy && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe; - isAntiHeWoDCAxyWTPCpid = isAntiHeWoDCAxy && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe; + isDeWoDCAxyWTPCpid = isDeWoDCAxy && passDeTPCpid; + isAntiDeWoDCAxyWTPCpid = isAntiDeWoDCAxy && passDeTPCpid; + isHeWoDCAxyWTPCpid = isHeWoDCAxy && passHeTPCpid; + isAntiHeWoDCAxyWTPCpid = isAntiHeWoDCAxy && passHeTPCpid; - isDeWoDCAzWTPCpid = isDeWoDCAz && std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe; - isAntiDeWoDCAzWTPCpid = isAntiDeWoDCAz && std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe; - isHeWoDCAzWTPCpid = isHeWoDCAz && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe; - isAntiHeWoDCAzWTPCpid = isAntiHeWoDCAz && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe; + isDeWoDCAzWTPCpid = isDeWoDCAz && passDeTPCpid; + isAntiDeWoDCAzWTPCpid = isAntiDeWoDCAz && passDeTPCpid; + isHeWoDCAzWTPCpid = isHeWoDCAz && passHeTPCpid; + isAntiHeWoDCAzWTPCpid = isAntiHeWoDCAz && passHeTPCpid; isDeWoTPCpid = isDe && passDCAzCutDe && passDCAxyCutDe; isAntiDeWoTPCpid = isAntiDe && passDCAzCutAntiDe && passDCAxyCutAntiDe; isHeWoTPCpid = isHe && passDCAzCutHe && passDCAxyCutHe; isAntiHeWoTPCpid = isAntiHe && passDCAzCutAntiHe && passDCAxyCutAntiHe; - isDeWTPCpid = isDeWoTPCpid && std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe; - isAntiDeWTPCpid = isAntiDeWoTPCpid && std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe; - isHeWTPCpid = isHeWoTPCpid && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe; - isAntiHeWTPCpid = isAntiHeWoTPCpid && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe; + isDeWTPCpid = isDeWoTPCpid && passDeTPCpid; + isAntiDeWTPCpid = isAntiDeWoTPCpid && passDeTPCpid; + isHeWTPCpid = isHeWoTPCpid && passHeTPCpid; + isAntiHeWTPCpid = isAntiHeWoTPCpid && passHeTPCpid; passDCAxyzCut = passDCAxyCut && passDCAzCut; @@ -2773,36 +3036,34 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/dca/before/hDCAxyVsDCAzVsPt"), track.dcaXY(), track.dcaZ(), track.pt()); histos.fill(HIST("tracks/dca/before/hDCAxyVsDCAz"), track.dcaZ(), track.dcaXY()); - if (isHe && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe) { + if (isHe && passHeTPCpid) { histos.fill(HIST("tracks/helium/dca/before/h3DCAvsPtHelium"), track.dcaXY(), track.dcaZ(), hePt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsDCAzVsPtHelium"), track.dcaXY(), track.dcaZ(), hePt); - } } - if (isAntiHe && std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe) { + if (isAntiHe && passHeTPCpid) { histos.fill(HIST("tracks/helium/dca/before/h3DCAvsPtantiHelium"), track.dcaXY(), track.dcaZ(), antihePt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsDCAzVsPtantiHelium"), track.dcaXY(), track.dcaZ(), antihePt); - } } if (passDCAxyCut) { histos.fill(HIST("tracks/dca/before/hDCAzVsPt"), track.pt(), track.dcaZ()); - if (enablePr && prRapCut && (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr)) { + if (enablePr && prRapCut && passPrTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtProton"), track.pt(), track.dcaZ()); } else { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtantiProton"), track.pt(), track.dcaZ()); } } - if (enableTr && trRapCut && isTritonTPCpid) { + if (enableTr && trRapCut && passTrTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/triton/dca/before/hDCAzVsPtTriton"), track.pt(), track.dcaZ()); } else { histos.fill(HIST("tracks/triton/dca/before/hDCAzVsPtantiTriton"), track.pt(), track.dcaZ()); } } - if (enableAl && alRapCut && (std::abs(track.tpcNSigmaAl()) < nsigmaTPCvar.nsigmaTPCAl)) { + if (enableAl && alRapCut && passAlTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/alpha/dca/before/hDCAzVsPtAlpha"), track.pt(), track.dcaZ()); } else { @@ -2827,18 +3088,16 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtHelium"), hePt, track.dcaZ()); if (!track.hasTOF() && (outFlagOptions.enableNoTOFPlots)) histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtHeliumNoTOF"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtHelium"), hePt, track.dcaZ()); - } } if (isAntiHeWoDCAzWTPCpid) { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtantiHelium"), antihePt, track.dcaZ()); if (!track.hasTOF() && (outFlagOptions.enableNoTOFPlots)) - histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtantiHeliumNoTOF"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtantiHeliumNoTOF"), antihePt, track.dcaZ()); + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtantiHelium"), antihePt, track.dcaZ()); - } } } @@ -2869,14 +3128,12 @@ struct LFNucleiBATask { case PDGProton: if (enablePr && prRapCut && passDCAxyCut) { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtProtonTrue"), track.pt(), track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtProtonTrue"), track.pt(), track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtProtonTruePrim"), track.pt(), track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtProtonTruePrim"), track.pt(), track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -2884,7 +3141,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtProtonTrueMaterial"), track.pt(), track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtProtonTrueSec"), track.pt(), track.dcaZ()); } else { @@ -2897,14 +3154,13 @@ struct LFNucleiBATask { case -PDGProton: if (enablePr && prRapCut && passDCAxyCut) { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtantiProtonTrue"), track.pt(), track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtantiProtonTrue"), track.pt(), track.dcaZ()); } if (isPhysPrim) { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtantiProtonTruePrim"), track.pt(), track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtantiProtonTruePrim"), track.pt(), track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -2912,11 +3168,11 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/proton/dca/before/hDCAzVsPtantiProtonTrueMaterial"), track.pt(), track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { - histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtantiProtonTrueSec"), hePt, track.dcaZ()); + histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtantiProtonTrueSec"), track.pt(), track.dcaZ()); } else { - histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtantiProtonTrueMaterial"), hePt, track.dcaZ()); + histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAzVsPtantiProtonTrueMaterial"), track.pt(), track.dcaZ()); } } } @@ -2925,14 +3181,13 @@ struct LFNucleiBATask { case PDGDeuteron: if (isDeWoDCAz) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAzVsPtDeuteronTrue"), DPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAzVsPtDeuteronTrue"), DPt, track.dcaZ()); } if (isPhysPrim) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAzVsPtDeuteronTruePrim"), DPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAzVsPtDeuteronTruePrim"), DPt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -2940,7 +3195,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/deuteron/dca/before/hDCAzVsPtDeuteronTrueMaterial"), DPt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAzVsPtDeuteronTrueSec"), DPt, track.dcaZ()); } else { @@ -2953,14 +3208,13 @@ struct LFNucleiBATask { case -PDGDeuteron: if (isAntiDeWoDCAz) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAzVsPtantiDeuteronTrue"), antiDPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAzVsPtantiDeuteronTrue"), antiDPt, track.dcaZ()); } if (isPhysPrim) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAzVsPtantiDeuteronTruePrim"), antiDPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAzVsPtantiDeuteronTruePrim"), antiDPt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -2968,7 +3222,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/deuteron/dca/before/hDCAzVsPtantiDeuteronTrueMaterial"), antiDPt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAzVsPtantiDeuteronTrueSec"), antiDPt, track.dcaZ()); } else { @@ -3011,14 +3265,13 @@ struct LFNucleiBATask { case PDGHelium: if (isHeWoDCAz) { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -3026,7 +3279,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtHeliumTrueMaterial"), hePt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtHeliumTrueSec"), hePt, track.dcaZ()); } else { @@ -3039,21 +3292,19 @@ struct LFNucleiBATask { if ((event.has_mcCollision() && (track.mcParticle().mcCollisionId() != event.mcCollisionId())) || !event.has_mcCollision()) { if (isHeWoDCAz && outFlagOptions.makeWrongEventPlots) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtHeliumTrueTransport"), hePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtHeliumTrueSec"), hePt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtHeliumTrueTransport"), hePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtHeliumTrueSec"), hePt, track.dcaZ()); @@ -3067,12 +3318,11 @@ struct LFNucleiBATask { case -PDGHelium: if (isAntiHeWoDCAz) { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); } } @@ -3082,7 +3332,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/helium/dca/before/hDCAzVsPtantiHeliumTrueMaterial"), antihePt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAzVsPtantiHeliumTrueSec"), antihePt, track.dcaZ()); } else { @@ -3095,21 +3345,19 @@ struct LFNucleiBATask { if ((event.has_mcCollision() && (track.mcParticle().mcCollisionId() != event.mcCollisionId())) || !event.has_mcCollision()) { if (isAntiHeWoDCAz && outFlagOptions.makeWrongEventPlots) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtantiHeliumTrueTransport"), antihePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAzVsPtantiHeliumTrueSec"), antihePt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtantiHeliumTrueTransport"), antihePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAzVsPtantiHeliumTrueSec"), antihePt, track.dcaZ()); @@ -3160,21 +3408,19 @@ struct LFNucleiBATask { default: if (isDeWoDCAzWTPCpid && outFlagOptions.makeFakeTracksPlots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtDeuteronTrue"), DPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtDeuteronTrue"), DPt, track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtDeuteronTruePrim"), DPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtDeuteronTruePrim"), DPt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtDeuteronTrueTransport"), DPt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtDeuteronTrueSec"), DPt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtDeuteronTrueTransport"), DPt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtDeuteronTrueSec"), DPt, track.dcaZ()); @@ -3183,21 +3429,19 @@ struct LFNucleiBATask { } } else if (isAntiDeWoDCAzWTPCpid && outFlagOptions.makeFakeTracksPlots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtantiDeuteronTrue"), antiDPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtantiDeuteronTrue"), antiDPt, track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtantiDeuteronTruePrim"), antiDPt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtantiDeuteronTruePrim"), antiDPt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtantiDeuteronTrueTransport"), antiDPt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAzVsPtantiDeuteronTrueSec"), antiDPt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtantiDeuteronTrueTransport"), antiDPt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAzVsPtantiDeuteronTrueSec"), antiDPt, track.dcaZ()); @@ -3215,21 +3459,19 @@ struct LFNucleiBATask { default: if (isHeWoDCAzWTPCpid && outFlagOptions.makeFakeTracksPlots) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtHeliumTrueTransport"), hePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtHeliumTrueSec"), hePt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtHeliumTrueTransport"), hePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtHeliumTrueSec"), hePt, track.dcaZ()); @@ -3239,21 +3481,19 @@ struct LFNucleiBATask { } if (isAntiHeWoDCAzWTPCpid && outFlagOptions.makeFakeTracksPlots) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtantiHeliumTrueTransport"), antihePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAzVsPtantiHeliumTrueSec"), antihePt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtantiHeliumTrueTransport"), antihePt, track.dcaZ()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAzVsPtantiHeliumTrueSec"), antihePt, track.dcaZ()); @@ -3273,7 +3513,7 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/dca/before/hDCAxyVsPt"), track.pt(), track.dcaXY()); if (enablePr && prRapCut) { - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr) { + if (passPrTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/proton/dca/before/hDCAxyVsPtProton"), track.pt(), track.dcaXY()); } else { @@ -3283,7 +3523,7 @@ struct LFNucleiBATask { } if (enableTr && trRapCut) { - if (isTritonTPCpid) { + if (passTrTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtTriton"), track.pt(), track.dcaXY()); } else { @@ -3292,7 +3532,7 @@ struct LFNucleiBATask { } } if (enableAl && alRapCut) { - if (std::abs(track.tpcNSigmaAl()) < nsigmaTPCvar.nsigmaTPCAl) { + if (passAlTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/alpha/dca/before/hDCAxyVsPtAlpha"), track.pt(), track.dcaXY()); } else { @@ -3302,24 +3542,23 @@ struct LFNucleiBATask { } } - if (isDeWoDCAxyWTPCpid) { - if (usenITSLayer && !itsClusterMap.test(trkqcOptions.nITSLayer)) - continue; + if (isDeWoDCAxyWTPCpid && (!usenITSLayer || itsClusterMap.test(trkqcOptions.nITSLayer))) { if (enableCentrality) histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtDeuteronVsMult"), DPt, track.dcaXY(), centFT0M); else histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtDeuteron"), DPt, track.dcaXY()); - if (!track.hasTOF() && (outFlagOptions.enableNoTOFPlots)) + + if (!track.hasTOF() && outFlagOptions.enableNoTOFPlots) histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtDeuteronNoTOF"), DPt, track.dcaXY()); } - if (isAntiDeWoDCAxyWTPCpid) { - if (usenITSLayer && !itsClusterMap.test(trkqcOptions.nITSLayer)) - continue; + + if (isAntiDeWoDCAxyWTPCpid && (!usenITSLayer || itsClusterMap.test(trkqcOptions.nITSLayer))) { if (enableCentrality) histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtantiDeuteronVsMult"), antiDPt, track.dcaXY(), centFT0M); else histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtantiDeuteron"), antiDPt, track.dcaXY()); - if (!track.hasTOF() && (outFlagOptions.enableNoTOFPlots)) + + if (!track.hasTOF() && outFlagOptions.enableNoTOFPlots) histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtantiDeuteronNoTOF"), antiDPt, track.dcaXY()); } @@ -3327,17 +3566,15 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtHelium"), hePt, track.dcaXY()); if (!track.hasTOF() && (outFlagOptions.enableNoTOFPlots)) histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtHeliumNoTOF"), hePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtHelium"), hePt, track.dcaXY()); - } } if (isAntiHeWoDCAxyWTPCpid) { histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtantiHelium"), antihePt, track.dcaXY()); if (!track.hasTOF() && (outFlagOptions.enableNoTOFPlots)) histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtantiHeliumNoTOF"), antihePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtantiHelium"), antihePt, track.dcaXY()); - } } } @@ -3358,8 +3595,8 @@ struct LFNucleiBATask { [[maybe_unused]] int firstMotherId = -1; [[maybe_unused]] int firstMotherPdg = -1; [[maybe_unused]] float firstMotherPt = -1.f; - [[maybe_unused]] int pdgMomList[kMaxNumMom]; - [[maybe_unused]] float ptMomList[kMaxNumMom]; + [[maybe_unused]] std::array pdgMomList{}; + [[maybe_unused]] std::array ptMomList{}; [[maybe_unused]] int nSaved = 0; if constexpr (IsFilteredData) { @@ -3401,7 +3638,7 @@ struct LFNucleiBATask { firstMotherPdg = pdgMom; firstMotherPt = ptMom; } - if (nSaved < kMaxNumMom) { + if (nSaved < MaxNumMom) { pdgMomList[nSaved] = pdgMom; ptMomList[nSaved] = ptMom; nSaved++; @@ -3409,7 +3646,7 @@ struct LFNucleiBATask { } genPt = track.mcParticle().pt(); - for (int i = 0; i < kFakeLoop; i++) { // From ITS to TPC + for (int i = 0; i < IntFakeLoop; i++) { // From ITS to TPC if (track.mcMask() & 1 << i) { hasFakeHit = true; break; @@ -3423,14 +3660,12 @@ struct LFNucleiBATask { case PDGProton: if (enablePr && prRapCut && outFlagOptions.makeDCABeforeCutPlots && passDCAzCut) { histos.fill(HIST("tracks/proton/dca/before/hDCAxyVsPtProtonTrue"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAxyVsPtProtonTrue"), track.pt(), track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/proton/dca/before/hDCAxyVsPtProtonTruePrim"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAxyVsPtProtonTruePrim"), track.pt(), track.dcaXY()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -3441,25 +3676,25 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/dca/before/hNumMothers"), nSaved); if (nSaved > 0) { for (int iMom = 0; iMom < nSaved; iMom++) { - int pdgMom = pdgMomList[iMom]; - float pdgSign = (pdgMom > 0) ? 1.0 : -1.0; - float ptMom = ptMomList[iMom]; + const int pdgMomCurrent = pdgMomList[iMom]; + const float pdgSign = (pdgMomCurrent > 0) ? 1.0f : -1.0f; + const float ptMomCurrent = ptMomList[iMom]; int motherSpeciesBin = -1; - if (pdgMom != -1) { + if (pdgMomCurrent != -1) { motherSpeciesBin = 0; - for (int j = 0; j < kNumMotherList; j++) { - if (std::abs(kPdgMotherList[j]) == std::abs(pdgMom)) { + for (int j = 0; j < NumMotherList; j++) { + if (std::abs(PdgMotherList[j]) == std::abs(pdgMomCurrent)) { motherSpeciesBin = j + 1; break; } } } - histos.fill(HIST("tracks/proton/dca/before/hMomTrueMaterial"), pdgSign, motherSpeciesBin, ptMom); + histos.fill(HIST("tracks/proton/dca/before/hMomTrueMaterial"), pdgSign, motherSpeciesBin, ptMomCurrent); } } } } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAxyVsPtProtonTrueSec"), track.pt(), track.dcaXY()); } else { @@ -3472,14 +3707,12 @@ struct LFNucleiBATask { case -PDGProton: if (enablePr && prRapCut && outFlagOptions.makeDCABeforeCutPlots && passDCAzCut) { histos.fill(HIST("tracks/proton/dca/before/hDCAxyVsPtantiProtonTrue"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAxyVsPtantiProtonTrue"), track.pt(), track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/proton/dca/before/hDCAxyVsPtantiProtonTruePrim"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAxyVsPtantiProtonTruePrim"), track.pt(), track.dcaXY()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -3487,7 +3720,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/proton/dca/before/hDCAxyVsPtantiProtonTrueMaterial"), track.pt(), track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/proton/dca/before/TOF/hDCAxyVsPtantiProtonTrueSec"), track.pt(), track.dcaXY()); } else { @@ -3501,16 +3734,14 @@ struct LFNucleiBATask { if (isDeWoDCAxy) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtDeuteronTrue"), DPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAxyVsPtDeuteronTrue"), DPt, track.dcaXY()); - } } if (isPhysPrim) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtDeuteronTruePrim"), DPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAxyVsPtDeuteronTruePrim"), DPt, track.dcaXY()); - } } if constexpr (IsFilteredData) { spectraGen.fill(HIST("deuteron/histDeuteronPtShift"), track.pt(), track.pt() - genPt); @@ -3538,25 +3769,25 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/deuteron/dca/before/hNumMothers"), nSaved); if (nSaved > 0) { for (int iMom = 0; iMom < nSaved; iMom++) { - int pdgMom = pdgMomList[iMom]; - float pdgSign = (pdgMom > 0) ? 1.0 : -1.0; - float ptMom = ptMomList[iMom]; + const int pdgMomCurrent = pdgMomList[iMom]; + const float pdgSign = (pdgMomCurrent > 0) ? 1.0f : -1.0f; + const float ptMomCurrent = ptMomList[iMom]; int motherSpeciesBin = -1; - if (pdgMom != -1) { + if (pdgMomCurrent != -1) { motherSpeciesBin = 0; - for (int j = 0; j < kNumMotherList; j++) { - if (std::abs(kPdgMotherList[j]) == std::abs(pdgMom)) { + for (int j = 0; j < NumMotherList; j++) { + if (std::abs(PdgMotherList[j]) == std::abs(pdgMomCurrent)) { motherSpeciesBin = j + 1; break; } } } - histos.fill(HIST("tracks/deuteron/dca/before/hMomTrueMaterial"), pdgSign, motherSpeciesBin, ptMom); + histos.fill(HIST("tracks/deuteron/dca/before/hMomTrueMaterial"), pdgSign, motherSpeciesBin, ptMomCurrent); } } } } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAxyVsPtDeuteronTrueSec"), DPt, track.dcaXY()); } else { @@ -3571,16 +3802,14 @@ struct LFNucleiBATask { if (isAntiDeWoDCAxy) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtantiDeuteronTrue"), antiDPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAxyVsPtantiDeuteronTrue"), antiDPt, track.dcaXY()); - } } if (isPhysPrim) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtantiDeuteronTruePrim"), antiDPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAxyVsPtantiDeuteronTruePrim"), antiDPt, track.dcaXY()); - } } if constexpr (IsFilteredData) { spectraGen.fill(HIST("deuteron/histAntiDeuteronPtShift"), track.pt(), track.pt() - genPt); @@ -3605,7 +3834,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/deuteron/dca/before/hDCAxyVsPtantiDeuteronTrueMaterial"), antiDPt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/TOF/hDCAxyVsPtantiDeuteronTrueSec"), antiDPt, track.dcaXY()); } else { @@ -3619,26 +3848,22 @@ struct LFNucleiBATask { case PDGTriton: if (enableTr && trRapCut && outFlagOptions.makeDCABeforeCutPlots && passDCAzCut) { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtTritonTrue"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtTritonTrue"), track.pt(), track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtTritonTruePrim"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtTritonTruePrim"), track.pt(), track.dcaXY()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtTritonTrueSec"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtTritonTrueSec"), track.pt(), track.dcaXY()); - } } else { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtTritonTrueMaterial"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtTritonTrueMaterial"), track.pt(), track.dcaXY()); - } } } } @@ -3646,15 +3871,12 @@ struct LFNucleiBATask { case -PDGTriton: if (enableTr && trRapCut && outFlagOptions.makeDCABeforeCutPlots && passDCAzCut) { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtantiTritonTrue"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtantiTritonTrue"), track.pt(), track.dcaXY()); - } - if (isPhysPrim) { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtantiTritonTruePrim"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtantiTritonTruePrim"), track.pt(), track.dcaXY()); - } } if (!isPhysPrim && isProdByGen) { // @@ -3662,14 +3884,12 @@ struct LFNucleiBATask { if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtantiTritonTrueSec"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtantiTritonTrueSec"), track.pt(), track.dcaXY()); - } } else { histos.fill(HIST("tracks/triton/dca/before/hDCAxyVsPtantiTritonTrueMaterial"), track.pt(), track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/triton/dca/before/TOF/hDCAxyVsPtantiTritonTrueMaterial"), track.pt(), track.dcaXY()); - } } } } @@ -3678,14 +3898,12 @@ struct LFNucleiBATask { if (isHeWoDCAxy) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); - } } } if (!isPhysPrim && !isProdByGen && outFlagOptions.makeDCABeforeCutPlots) { @@ -3697,25 +3915,25 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/dca/before/hNumMothers"), nSaved); if (nSaved > 0) { for (int iMom = 0; iMom < nSaved; iMom++) { - int pdgMom = pdgMomList[iMom]; - float pdgSign = (pdgMom > 0) ? 1.0 : -1.0; - float ptMom = ptMomList[iMom]; + const int pdgMomCurrent = pdgMomList[iMom]; + const float pdgSign = (pdgMomCurrent > 0) ? 1.0f : -1.0f; + const float ptMomCurrent = ptMomList[iMom]; int motherSpeciesBin = -1; - if (pdgMom != -1) { + if (pdgMomCurrent != -1) { motherSpeciesBin = 0; - for (int j = 0; j < kNumMotherList; j++) { - if (std::abs(kPdgMotherList[j]) == std::abs(pdgMom)) { + for (int j = 0; j < NumMotherList; j++) { + if (std::abs(PdgMotherList[j]) == std::abs(pdgMomCurrent)) { motherSpeciesBin = j + 1; break; } } } - histos.fill(HIST("tracks/helium/dca/before/hMomTrueMaterial"), pdgSign, motherSpeciesBin, ptMom); + histos.fill(HIST("tracks/helium/dca/before/hMomTrueMaterial"), pdgSign, motherSpeciesBin, ptMomCurrent); } } } } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtHeliumTrueSec"), hePt, track.dcaXY()); } else { @@ -3728,21 +3946,19 @@ struct LFNucleiBATask { if ((event.has_mcCollision() && (track.mcParticle().mcCollisionId() != event.mcCollisionId())) || !event.has_mcCollision()) { if (isHeWoDCAxy && outFlagOptions.makeDCABeforeCutPlots && outFlagOptions.makeWrongEventPlots) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtHeliumTrueTransport"), hePt, track.dcaXY()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtHeliumTrueSec"), hePt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtHeliumTrueTransport"), hePt, track.dcaXY()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtHeliumTrueSec"), hePt, track.dcaXY()); @@ -3766,7 +3982,7 @@ struct LFNucleiBATask { spectraGen.fill(HIST("helium/histPtShiftHe_WrongPidDe"), 2.f * hePt, 2.f * hePt - track.mcParticle().pt()); spectraGen.fill(HIST("helium/histPtShiftVsEtaHe"), track.eta(), 2.f * hePt - track.mcParticle().pt()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { spectraGen.fill(HIST("helium/TOF/histPtShiftHe"), 2.f * hePt, 2.f * hePt - track.mcParticle().pt()); spectraGen.fill(HIST("helium/TOF/histPtShiftHeVsGen"), track.mcParticle().pt(), 2.f * hePt - track.mcParticle().pt()); } @@ -3783,14 +3999,12 @@ struct LFNucleiBATask { if (isAntiHeWoDCAxy) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); - } } } if (!isPhysPrim && !isProdByGen && outFlagOptions.makeDCABeforeCutPlots) { @@ -3799,7 +4013,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/helium/dca/before/hDCAxyVsPtantiHeliumTrueMaterial"), antihePt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/TOF/hDCAxyVsPtantiHeliumTrueSec"), antihePt, track.dcaXY()); } else { @@ -3812,21 +4026,19 @@ struct LFNucleiBATask { if ((event.has_mcCollision() && (track.mcParticle().mcCollisionId() != event.mcCollisionId())) || !event.has_mcCollision()) { if (isAntiHeWoDCAxy && outFlagOptions.makeDCABeforeCutPlots && outFlagOptions.makeWrongEventPlots) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); - } } if (!isPhysPrim && !isProdByGen) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtantiHeliumTrueTransport"), antihePt, track.dcaXY()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/hDCAxyVsPtantiHeliumTrueSec"), antihePt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtantiHeliumTrueTransport"), antihePt, track.dcaXY()); if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/wrong/TOF/hDCAxyVsPtantiHeliumTrueSec"), antihePt, track.dcaXY()); @@ -3850,7 +4062,7 @@ struct LFNucleiBATask { spectraGen.fill(HIST("helium/histPtShiftantiHe_WrongPidDe"), 2.f * antihePt, 2.f * antihePt - track.mcParticle().pt()); spectraGen.fill(HIST("helium/histPtShiftVsEtaantiHe"), track.eta(), 2.f * antihePt - track.mcParticle().pt()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { spectraGen.fill(HIST("helium/TOF/histPtShiftantiHe"), 2.f * antihePt, 2.f * antihePt - track.mcParticle().pt()); spectraGen.fill(HIST("helium/TOF/histPtShiftantiHeVsGen"), track.mcParticle().pt(), 2.f * antihePt - track.mcParticle().pt()); } @@ -3904,16 +4116,14 @@ struct LFNucleiBATask { if (isDeWoDCAxyWTPCpid && outFlagOptions.makeFakeTracksPlots) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAxyVsPtDeuteronTrue"), DPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAxyVsPtDeuteronTrue"), DPt, track.dcaXY()); - } } if (isPhysPrim) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAxyVsPtDeuteronTruePrim"), DPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAxyVsPtDeuteronTruePrim"), DPt, track.dcaXY()); - } } } if (!isPhysPrim && !isProdByGen) { @@ -3923,7 +4133,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAxyVsPtDeuteronTrueMaterial"), DPt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAxyVsPtDeuteronTrueSec"), DPt, track.dcaXY()); } else { @@ -3936,16 +4146,14 @@ struct LFNucleiBATask { if (isAntiDeWoDCAxyWTPCpid && outFlagOptions.makeFakeTracksPlots) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAxyVsPtantiDeuteronTrue"), antiDPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAxyVsPtantiDeuteronTrue"), antiDPt, track.dcaXY()); - } } if (isPhysPrim) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAxyVsPtantiDeuteronTruePrim"), antiDPt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAxyVsPtantiDeuteronTruePrim"), antiDPt, track.dcaXY()); - } } } if (!isPhysPrim && !isProdByGen) { @@ -3955,7 +4163,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/deuteron/dca/before/fake/hDCAxyVsPtantiDeuteronTrueMaterial"), antiDPt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/deuteron/dca/before/fake/TOF/hDCAxyVsPtantiDeuteronTrueSec"), antiDPt, track.dcaXY()); } else { @@ -3976,14 +4184,12 @@ struct LFNucleiBATask { if (isHeWoDCAxyWTPCpid && outFlagOptions.makeFakeTracksPlots) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -3991,7 +4197,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAxyVsPtHeliumTrueMaterial"), hePt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAxyVsPtHeliumTrueSec"), hePt, track.dcaXY()); } else { @@ -4004,14 +4210,12 @@ struct LFNucleiBATask { if (isAntiHeWoDCAxyWTPCpid && outFlagOptions.makeFakeTracksPlots) { if (outFlagOptions.makeDCABeforeCutPlots) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); - } if (isPhysPrim) { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); - } } if (!isPhysPrim && !isProdByGen) { if (isWeakDecay) { @@ -4019,7 +4223,7 @@ struct LFNucleiBATask { } else { histos.fill(HIST("tracks/helium/dca/before/fake/hDCAxyVsPtantiHeliumTrueMaterial"), antihePt, track.dcaXY()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isWeakDecay) { histos.fill(HIST("tracks/helium/dca/before/fake/TOF/hDCAxyVsPtantiHeliumTrueSec"), antihePt, track.dcaXY()); } else { @@ -4056,16 +4260,14 @@ struct LFNucleiBATask { if (isHeWTPCpid) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsDCAzVsPtHelium"), track.dcaXY(), track.dcaZ(), hePt); histos.fill(HIST("tracks/helium/dca/after/h3DCAvsPtHelium"), track.dcaXY(), track.dcaZ(), hePt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsDCAzVsPtHelium"), track.dcaXY(), track.dcaZ(), hePt); - } } if (isAntiHeWTPCpid) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsDCAzVsPtantiHelium"), track.dcaXY(), track.dcaZ(), antihePt); histos.fill(HIST("tracks/helium/dca/after/h3DCAvsPtantiHelium"), track.dcaXY(), track.dcaZ(), antihePt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsDCAzVsPtantiHelium"), track.dcaXY(), track.dcaZ(), antihePt); - } } if (passDCAxyzCut) { @@ -4074,7 +4276,7 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/dca/after/hDCAxyVsPt"), track.pt(), track.dcaXY()); histos.fill(HIST("tracks/dca/after/hDCAzVsPt"), track.pt(), track.dcaZ()); - if (enablePr && prRapCut && (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr)) { + if (enablePr && prRapCut && passPrTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/proton/dca/after/hDCAxyVsPtProton"), track.pt(), track.dcaXY()); histos.fill(HIST("tracks/proton/dca/after/hDCAzVsPtProton"), track.pt(), track.dcaZ()); @@ -4083,7 +4285,7 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/dca/after/hDCAzVsPtantiProton"), track.pt(), track.dcaZ()); } } - if (enableTr && trRapCut && isTritonTPCpid) { + if (enableTr && trRapCut && passTrTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/triton/dca/after/hDCAxyVsPtTriton"), track.pt(), track.dcaXY()); histos.fill(HIST("tracks/triton/dca/after/hDCAzVsPtTriton"), track.pt(), track.dcaZ()); @@ -4092,7 +4294,7 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/triton/dca/after/hDCAzVsPtantiTriton"), track.pt(), track.dcaZ()); } } - if (enableAl && alRapCut && (std::abs(track.tpcNSigmaAl()) < nsigmaTPCvar.nsigmaTPCAl)) { + if (enableAl && alRapCut && passAlTPCpid) { if (track.sign() > 0) { histos.fill(HIST("tracks/alpha/dca/after/hDCAxyVsPtAlpha"), track.pt(), track.dcaXY()); histos.fill(HIST("tracks/alpha/dca/after/hDCAzVsPtAlpha"), track.pt(), track.dcaZ()); @@ -4102,22 +4304,19 @@ struct LFNucleiBATask { } } } - if (isDeWTPCpid) { - if (usenITSLayer && !itsClusterMap.test(trkqcOptions.nITSLayer)) - continue; + if (isDeWTPCpid && (!usenITSLayer || itsClusterMap.test(trkqcOptions.nITSLayer))) { histos.fill(HIST("tracks/deuteron/dca/after/hDCAxyVsPtDeuteron"), DPt, track.dcaXY()); histos.fill(HIST("tracks/deuteron/dca/after/hDCAzVsPtDeuteron"), DPt, track.dcaZ()); } - if (isAntiDeWTPCpid) { - if (usenITSLayer && !itsClusterMap.test(trkqcOptions.nITSLayer)) - continue; + if (isAntiDeWTPCpid && (!usenITSLayer || itsClusterMap.test(trkqcOptions.nITSLayer))) { histos.fill(HIST("tracks/deuteron/dca/after/hDCAxyVsPtantiDeuteron"), antiDPt, track.dcaXY()); histos.fill(HIST("tracks/deuteron/dca/after/hDCAzVsPtantiDeuteron"), antiDPt, track.dcaZ()); } + if (isHeWTPCpid) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtHelium"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtHelium"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtHelium"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtHelium"), hePt, track.dcaZ()); } @@ -4125,7 +4324,7 @@ struct LFNucleiBATask { if (isAntiHeWTPCpid) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtantiHelium"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtantiHelium"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtantiHelium"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtantiHelium"), antihePt, track.dcaZ()); } @@ -4133,17 +4332,6 @@ struct LFNucleiBATask { } if (passDCAxyzCut) { - // QA histos fill - if (enableDebug) { - histos.fill(HIST("qa/h1ITSncr"), track.itsNCls()); - histos.fill(HIST("qa/h1TPCnfound"), track.tpcNClsFound()); - histos.fill(HIST("qa/h1TPCncr"), track.tpcNClsCrossedRows()); - histos.fill(HIST("qa/h1rTPC"), track.tpcCrossedRowsOverFindableCls()); - histos.fill(HIST("qa/h1chi2ITS"), track.tpcChi2NCl()); - histos.fill(HIST("qa/h1chi2TPC"), track.itsChi2NCl()); - debugHistos.fill(HIST("debug/h2TPCsignVsTPCmomentum_AllTracks"), track.tpcInnerParam() / (1.f * track.sign()), track.tpcSignal()); - } - if (enableDebug) { debugHistos.fill(HIST("debug/tracks/h1Eta"), track.eta()); debugHistos.fill(HIST("debug/tracks/h1VarPhi"), track.phi()); @@ -4154,26 +4342,26 @@ struct LFNucleiBATask { debugHistos.fill(HIST("debug/qa/h2TPCncrVsPtPos"), track.tpcInnerParam(), track.tpcNClsCrossedRows()); debugHistos.fill(HIST("debug/qa/h2TPCncrVsTPCsignalPos"), track.tpcSignal(), track.tpcNClsCrossedRows()); - if (track.tpcInnerParam() < kCfgTpcClasses[0]) { + if (track.tpcInnerParam() < CfgTpcClasses[0]) { debugHistos.fill(HIST("debug/qa/h1TPCncrLowPPos"), track.tpcNClsCrossedRows()); } - if ((track.tpcInnerParam() >= kCfgTpcClasses[0]) && (track.tpcInnerParam() < kCfgTpcClasses[1])) { + if ((track.tpcInnerParam() >= CfgTpcClasses[0]) && (track.tpcInnerParam() < CfgTpcClasses[1])) { debugHistos.fill(HIST("debug/qa/h1TPCncrMidPPos"), track.tpcNClsCrossedRows()); } - if (track.tpcInnerParam() >= kCfgTpcClasses[1]) { + if (track.tpcInnerParam() >= CfgTpcClasses[1]) { debugHistos.fill(HIST("debug/qa/h1TPCncrHighPPos"), track.tpcNClsCrossedRows()); } } else { debugHistos.fill(HIST("debug/qa/h2TPCncrVsPtNeg"), track.tpcInnerParam(), track.tpcNClsCrossedRows()); debugHistos.fill(HIST("debug/qa/h2TPCncrVsTPCsignalNeg"), track.tpcSignal(), track.tpcNClsCrossedRows()); - if (track.tpcInnerParam() < kCfgTpcClasses[0]) { + if (track.tpcInnerParam() < CfgTpcClasses[0]) { debugHistos.fill(HIST("debug/qa/h1TPCncrLowPNeg"), track.tpcNClsCrossedRows()); } - if ((track.tpcInnerParam() >= kCfgTpcClasses[0]) && (track.tpcInnerParam() < kCfgTpcClasses[1])) { + if ((track.tpcInnerParam() >= CfgTpcClasses[0]) && (track.tpcInnerParam() < CfgTpcClasses[1])) { debugHistos.fill(HIST("debug/qa/h1TPCncrMidPNeg"), track.tpcNClsCrossedRows()); } - if (track.tpcInnerParam() >= kCfgTpcClasses[1]) { + if (track.tpcInnerParam() >= CfgTpcClasses[1]) { debugHistos.fill(HIST("debug/qa/h1TPCncrHighPNeg"), track.tpcNClsCrossedRows()); } } @@ -4182,11 +4370,16 @@ struct LFNucleiBATask { debugHistos.fill(HIST("debug/tracks/kaon/h2KaonVspTNSigmaTPC"), track.pt(), track.tpcNSigmaKa()); } + if constexpr (!IsFilteredData) { + if (nsigmaITSvar.showAverageClusterSize && outFlagOptions.enablePIDplot) + histos.fill(HIST("tracks/avgClusterSizePerCoslInvVsITSlayers"), track.p(), averageClusterSizePerCoslInv(track), track.itsNCls()); + } + if (outFlagOptions.enableEffPlots) histos.fill(HIST("tracks/eff/h2pVsTPCmomentum"), track.tpcInnerParam(), track.p()); if (filterOptions.enableFiltering) { - if (track.tpcNSigmaKa() < kCfgKaonCut) + if (track.tpcNSigmaKa() < CfgKaonCut) continue; } @@ -4219,6 +4412,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/h2ProtonVspTNSigmaTPC"), track.pt(), track.tpcNSigmaPr()); } break; + default: + break; } } if (enableTr && trRapCut) { @@ -4245,6 +4440,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/h2antiProtonVspTNSigmaTPC"), track.pt(), track.tpcNSigmaPr()); } break; + default: + break; } } if (enableTr && trRapCut) { @@ -4256,7 +4453,7 @@ struct LFNucleiBATask { } // TOF - if (outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (enableDebug) { histos.fill(HIST("tracks/pion/h2PionVspTNSigmaTOF"), track.pt(), track.tofNSigmaPi()); histos.fill(HIST("tracks/kaon/h2KaonVspTNSigmaTOF"), track.pt(), track.tofNSigmaKa()); @@ -4278,6 +4475,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/h2ProtonVspTNSigmaTOF"), track.pt(), track.tofNSigmaPr()); } break; + default: + break; } if (outFlagOptions.enableExpSignalTOF) histos.fill(HIST("tracks/proton/h2ProtonTOFExpSignalDiffVsPt"), track.pt(), track.tofExpSignalDiffPr()); @@ -4430,6 +4629,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/h2antiProtonVspTNSigmaTOF"), track.pt(), track.tofNSigmaPr()); } break; + default: + break; } if (outFlagOptions.enableExpSignalTOF) histos.fill(HIST("tracks/proton/h2antiProtonTOFExpSignalDiffVsPt"), track.pt(), track.tofExpSignalDiffPr()); @@ -4573,15 +4774,17 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/deuteron/h2DeuteronVspTNSigmaTPC"), DPt, track.tpcNSigmaDe()); break; case 1: - if (track.hasTRD()) { + if (track.hasTRD() && !enableCentrality) { histos.fill(HIST("tracks/deuteron/h2DeuteronVspTNSigmaTPC"), DPt, track.tpcNSigmaDe()); } break; case 2: - if (!track.hasTRD()) { + if (!track.hasTRD() && !enableCentrality) { histos.fill(HIST("tracks/deuteron/h2DeuteronVspTNSigmaTPC"), DPt, track.tpcNSigmaDe()); } break; + default: + break; } } if (isAntiDeWoTPCpid) { @@ -4598,51 +4801,57 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/deuteron/h2antiDeuteronVspTNSigmaTPC"), antiDPt, track.tpcNSigmaDe()); break; case 1: - if (track.hasTRD()) { + if (track.hasTRD() && !enableCentrality) { histos.fill(HIST("tracks/deuteron/h2antiDeuteronVspTNSigmaTPC"), antiDPt, track.tpcNSigmaDe()); } break; case 2: - if (!track.hasTRD()) { + if (!track.hasTRD() && !enableCentrality) { histos.fill(HIST("tracks/deuteron/h2antiDeuteronVspTNSigmaTPC"), antiDPt, track.tpcNSigmaDe()); } break; + default: + break; } } if (isHeWoTPCpid) { if (outFlagOptions.enableExpSignalTPC) histos.fill(HIST("tracks/helium/h2HeliumTPCExpSignalDiffVsPt"), hePt, track.tpcExpSignalDiffHe()); - histos.fill(HIST("tracks/helium/h2HeliumVspTNSigmaITSHe"), track.p(), nITSHe); + histos.fill(HIST("tracks/helium/h2HeliumVspTNSigmaITSHe"), heP, nITSHe); histos.fill(HIST("tracks/helium/h2HeliumVspTNSigmaTPC"), hePt, track.tpcNSigmaHe()); + if (enableDebug && enableHe) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * hePt, 7); if (enableCentrality) histos.fill(HIST("tracks/helium/h3HeliumVspTNSigmaTPCVsMult"), hePt, track.tpcNSigmaHe(), centFT0M); } if (isAntiHeWoTPCpid) { if (outFlagOptions.enableExpSignalTPC) histos.fill(HIST("tracks/helium/h2antiHeliumTPCExpSignalDiffVsPt"), antihePt, track.tpcExpSignalDiffHe()); - histos.fill(HIST("tracks/helium/h2antiHeliumVspTNSigmaITSHe"), track.p(), nITSHe); + histos.fill(HIST("tracks/helium/h2antiHeliumVspTNSigmaITSHe"), antiheP, nITSHe); histos.fill(HIST("tracks/helium/h2antiHeliumVspTNSigmaTPC"), antihePt, track.tpcNSigmaHe()); + if (enableDebug) + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * antihePt, 7); if (enableCentrality) histos.fill(HIST("tracks/helium/h3antiHeliumVspTNSigmaTPCVsMult"), antihePt, track.tpcNSigmaHe(), centFT0M); } if (isHeWTPCpid) { - histos.fill(HIST("tracks/helium/h2HeliumVspTNSigmaITSHe_wTPCpid"), track.p(), nITSHe); + histos.fill(HIST("tracks/helium/h2HeliumVspTNSigmaITSHe_wTPCpid"), heP, nITSHe); } if (isAntiHeWTPCpid) { - histos.fill(HIST("tracks/helium/h2antiHeliumVspTNSigmaITSHe_wTPCpid"), track.p(), nITSHe); + histos.fill(HIST("tracks/helium/h2antiHeliumVspTNSigmaITSHe_wTPCpid"), antiheP, nITSHe); } if constexpr (!IsFilteredData) { if (isHeWTPCpid || isAntiHeWTPCpid) { if (nsigmaITSvar.showAverageClusterSize) { - histos.fill(HIST("tracks/helium/averageClusterSize"), track.p(), averageClusterSizeTrk(track)); - histos.fill(HIST("tracks/helium/averageClusterSizePerCoslInv"), track.p(), averageClusterSizePerCoslInv(track)); + histos.fill(HIST("tracks/helium/averageClusterSize"), heP, averageClusterSizeTrk(track)); + histos.fill(HIST("tracks/helium/averageClusterSizePerCoslInv"), antiheP, averageClusterSizePerCoslInv(track)); } } } // TOF - if (outFlagOptions.doTOFplots) { + if (hasTOFplots) { if (isDeWTPCpid) { switch (useHasTRDConfig) { case 0: @@ -4658,6 +4867,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/deuteron/h2DeuteronVspTNSigmaTOF"), DPt, track.tofNSigmaDe()); } break; + default: + break; } if (outFlagOptions.enableExpSignalTOF) histos.fill(HIST("tracks/deuteron/h2DeuteronTOFExpSignalDiffVsPt"), DPt, track.tofExpSignalDiffDe()); @@ -4678,6 +4889,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/deuteron/h2antiDeuteronVspTNSigmaTOF"), antiDPt, track.tofNSigmaDe()); } break; + default: + break; } if (outFlagOptions.enableExpSignalTOF) histos.fill(HIST("tracks/deuteron/h2antiDeuteronTOFExpSignalDiffVsPt"), antiDPt, track.tofExpSignalDiffDe()); @@ -4706,7 +4919,7 @@ struct LFNucleiBATask { } if (enablePr) { - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr && prRapCut) { + if (passPrTPCpid && prRapCut) { if (track.sign() > 0) { if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/proton/hPtPr"), track.pt()); @@ -4733,7 +4946,7 @@ struct LFNucleiBATask { } } if (enableTr) { - if ((isTritonTPCpid) && trRapCut) { + if ((passTrTPCpid) && trRapCut) { if (track.sign() > 0) { if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/triton/hPtTr"), track.pt()); @@ -4754,7 +4967,7 @@ struct LFNucleiBATask { } } if (enableAl) { - if ((std::abs(track.tpcNSigmaAl()) < nsigmaTPCvar.nsigmaTPCAl) && alRapCut) { + if (std::abs(track.tpcNSigmaAl()) < nsigmaTPCvar.nsigmaTPCAl && alRapCut) { if (track.sign() > 0) { histos.fill(HIST("tracks/alpha/h1AlphaSpectra"), track.pt()); if (outFlagOptions.enablePIDplot) @@ -4767,7 +4980,7 @@ struct LFNucleiBATask { } } - if (outFlagOptions.doTOFplots && track.hasTOF()) { + if (hasTOFplots) { if (outFlagOptions.enableEffPlots && enableDebug) { if (track.sign() > 0) debugHistos.fill(HIST("tracks/eff/hPtPTOF"), track.pt()); @@ -4792,6 +5005,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/h2TOFbetaVsP"), track.p() / (1.f * track.sign()), track.beta()); } break; + default: + break; } } @@ -4799,14 +5014,14 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/eff/h2TPCmomentumVsTOFExpMomentum"), track.tofExpMom(), track.tpcInnerParam()); if (enablePr && prRapCut) { - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr && track.sign() > 0) { + if (passPrTPCpid && track.sign() > 0) { histos.fill(HIST("tracks/proton/h2ProtonTOFbetaVsP"), track.p(), track.beta()); if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/proton/h2pVsTOFExpMomentumPr"), track.tofExpMom(), track.p()); histos.fill(HIST("tracks/eff/proton/h2TPCmomentumVsTOFExpMomentumPr"), track.tofExpMom(), track.tpcInnerParam()); } } - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr && track.sign() < 0) { + if (passPrTPCpid && track.sign() < 0) { histos.fill(HIST("tracks/proton/h2antiProtonTOFbetaVsP"), track.p(), track.beta()); if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/proton/h2pVsTOFExpMomentumantiPr"), track.tofExpMom(), track.p()); @@ -4815,14 +5030,14 @@ struct LFNucleiBATask { } } if (enableTr && trRapCut) { - if (isTritonTPCpid && track.sign() > 0) { + if (passTrTPCpid && track.sign() > 0) { histos.fill(HIST("tracks/triton/h2TritonTOFbetaVsP"), track.p(), track.beta()); if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/triton/h2pVsTOFExpMomentumTr"), track.tofExpMom(), track.p()); histos.fill(HIST("tracks/eff/triton/h2TPCmomentumVsTOFExpMomentumTr"), track.tofExpMom(), track.tpcInnerParam()); } } - if (isTritonTPCpid && track.sign() < 0) { + if (passTrTPCpid && track.sign() < 0) { histos.fill(HIST("tracks/triton/h2antiTritonTOFbetaVsP"), track.p(), track.beta()); if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/triton/h2pVsTOFExpMomentumantiTr"), track.tofExpMom(), track.p()); @@ -4855,6 +5070,7 @@ struct LFNucleiBATask { } histos.fill(HIST("tracks/deuteron/h1DeuteronSpectra"), DPt); histos.fill(HIST("tracks/deuteron/h2DeuteronYvsPt"), track.rapidity(o2::track::PID::getMass2Z(o2::track::PID::Deuteron)), DPt); + histos.fill(HIST("tracks/deuteron/h2DeuteronShiftYvsPt"), getRapidityFromPtMass(track, DPt, o2::track::PID::getMass2Z(o2::track::PID::Deuteron)), DPt); histos.fill(HIST("tracks/deuteron/h2DeuteronEtavsPt"), track.eta(), DPt); histos.fill(HIST("tracks/deuteron/h2DeuteronVspNSigmaITSDe_wTPCpid"), track.p(), nITSDe); @@ -4868,6 +5084,7 @@ struct LFNucleiBATask { } histos.fill(HIST("tracks/deuteron/h1antiDeuteronSpectra"), antiDPt); histos.fill(HIST("tracks/deuteron/h2antiDeuteronYvsPt"), track.rapidity(o2::track::PID::getMass2Z(o2::track::PID::Deuteron)), antiDPt); + histos.fill(HIST("tracks/deuteron/h2antiDeuteronShiftYvsPt"), getRapidityFromPtMass(track, antiDPt, o2::track::PID::getMass2Z(o2::track::PID::Deuteron)), antiDPt); histos.fill(HIST("tracks/deuteron/h2antiDeuteronEtavsPt"), track.eta(), antiDPt); histos.fill(HIST("tracks/deuteron/h2antiDeuteronVspNSigmaITSDe_wTPCpid"), track.p(), nITSDe); @@ -4880,7 +5097,10 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/eff/helium/h2pVsTPCmomentumHe"), heTPCmomentum, heP); } histos.fill(HIST("tracks/helium/h1HeliumSpectra_Z2"), 2 * hePt); + if (enableDebug && enableHe) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * hePt, 8); histos.fill(HIST("tracks/helium/h2HeliumYvsPt_Z2"), track.rapidity(o2::track::PID::getMass2Z(o2::track::PID::Helium3)), 2 * hePt); + histos.fill(HIST("tracks/helium/h2HeliumShiftYvsPt_Z2"), getRapidityFromPtMass(track, hePt, o2::track::PID::getMass2Z(o2::track::PID::Helium3)), 2 * hePt); histos.fill(HIST("tracks/helium/h2HeliumEtavsPt_Z2"), track.eta(), 2 * hePt); if (outFlagOptions.enablePIDplot) histos.fill(HIST("tracks/helium/h2TPCsignVsTPCmomentumHelium"), heTPCmomentum, track.tpcSignal()); @@ -4893,7 +5113,10 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/eff/helium/h2pVsTPCmomentumantiHe"), antiheTPCmomentum, antiheP); } histos.fill(HIST("tracks/helium/h1antiHeliumSpectra_Z2"), 2 * antihePt); + if (enableDebug) + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * antihePt, 8); histos.fill(HIST("tracks/helium/h2antiHeliumYvsPt_Z2"), track.rapidity(o2::track::PID::getMass2Z(o2::track::PID::Helium3)), 2 * antihePt); + histos.fill(HIST("tracks/helium/h2antiHeliumShiftYvsPt_Z2"), getRapidityFromPtMass(track, antihePt, o2::track::PID::getMass2Z(o2::track::PID::Helium3)), 2 * antihePt); histos.fill(HIST("tracks/helium/h2antiHeliumEtavsPt_Z2"), track.eta(), 2 * antihePt); if (outFlagOptions.enablePIDplot) histos.fill(HIST("tracks/helium/h2TPCsignVsTPCmomentumantiHelium"), antiheTPCmomentum, track.tpcSignal()); @@ -4901,7 +5124,7 @@ struct LFNucleiBATask { debugHistos.fill(HIST("tracks/helium/h2antiHeliumPidTrackingVsPt"), 2 * antihePt, track.pidForTracking()); } - if (outFlagOptions.doTOFplots && track.hasTOF()) { + if (hasTOFplots) { if (isDeWTPCpid) { histos.fill(HIST("tracks/deuteron/h2DeuteronTOFbetaVsP"), track.p(), track.beta()); if (outFlagOptions.enableEffPlots) { @@ -4954,6 +5177,11 @@ struct LFNucleiBATask { massTOFhe = heP * std::sqrt(1.f / (track.beta() * track.beta()) - 1.f); massTOFantihe = antiheP * std::sqrt(1.f / (track.beta() * track.beta()) - 1.f); break; + default: + massTOF = -99.f; + massTOFhe = -99.f; + massTOFantihe = -99.f; + break; } if (passDCAxyzCut && outFlagOptions.doTOFplots && outFlagOptions.enablePIDplot) histos.fill(HIST("tracks/h2TPCsignVsBetaGamma"), (track.beta() * gamma) / (1.f * track.sign()), track.tpcSignal()); @@ -4979,7 +5207,7 @@ struct LFNucleiBATask { } if (enablePr) { - if ((std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr) && prRapCut) { + if (passPrTPCpid && prRapCut) { if (track.sign() > 0) { if (outFlagOptions.enableEffPlots) histos.fill(HIST("tracks/eff/proton/hPtPrTOF"), track.pt()); @@ -5028,7 +5256,7 @@ struct LFNucleiBATask { } } - if (enableTr && isTritonTPCpid && trRapCut) { + if (enableTr && passTrTPCpid && trRapCut) { const float m2diff = massTOF * massTOF - MassTritonVal * MassTritonVal; if (track.sign() > 0) { if (outFlagOptions.enableEffPlots) @@ -5111,8 +5339,10 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/h2TOFmassHeliumVsPt"), 2.f * massTOFhe, hePt); histos.fill(HIST("tracks/helium/h2TOFmassDeltaHeliumVsPt"), 2.f * massTOFhe - MassHeliumVal, hePt); histos.fill(HIST("tracks/helium/h2TOFmass2HeliumVsPt"), 2.f * massTOFhe * 2.f * massTOFhe - MassHeliumVal * MassHeliumVal, hePt); + if (enableDebug && enableHe) + debugHistos.fill(HIST("tracks/helium/h2HeCutFlowVsPt"), 2.f * hePt, 9); if (enableCentrality) - histos.fill(HIST("tracks/helium/h3TOFmass2HeliumVsPtVsMult"), 2.f * massTOFantihe * 2.f * massTOFantihe - MassHeliumVal * MassHeliumVal, hePt, centFT0M); + histos.fill(HIST("tracks/helium/h3TOFmass2HeliumVsPtVsMult"), 2.f * massTOFhe * 2.f * massTOFhe - MassHeliumVal * MassHeliumVal, hePt, centFT0M); if (outFlagOptions.enableBetaCut && (track.beta() > cfgBetaCut)) { histos.fill(HIST("tracks/helium/h2TOFmassHeliumVsPt_BetaCut"), 2.f * massTOFhe, hePt); histos.fill(HIST("tracks/helium/h2TOFmass2HeliumVsPt_BetaCut"), 2.f * massTOFhe * 2.f * massTOFhe - MassHeliumVal * MassHeliumVal, hePt); @@ -5126,6 +5356,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/h2TOFmassantiHeliumVsPt"), 2.f * massTOFantihe, antihePt); histos.fill(HIST("tracks/helium/h2TOFmassDeltaantiHeliumVsPt"), 2.f * massTOFantihe - MassHeliumVal, antihePt); histos.fill(HIST("tracks/helium/h2TOFmass2antiHeliumVsPt"), 2.f * massTOFantihe * 2.f * massTOFantihe - MassHeliumVal * MassHeliumVal, antihePt); + if (enableDebug) + debugHistos.fill(HIST("tracks/helium/h2antiHeCutFlowVsPt"), 2.f * antihePt, 9); if (enableCentrality) histos.fill(HIST("tracks/helium/h3TOFmass2antiHeliumVsPtVsMult"), 2.f * massTOFantihe * 2.f * massTOFantihe - MassHeliumVal * MassHeliumVal, antihePt, centFT0M); if (outFlagOptions.enableBetaCut && (track.beta() > cfgBetaCut)) { @@ -5178,7 +5410,7 @@ struct LFNucleiBATask { track.passedTPCRefit() && track.hasTPC(); - for (int i = 0; i < kFakeLoop; i++) { // From ITS to TPC + for (int i = 0; i < IntFakeLoop; i++) { // From ITS to TPC if (track.mcMask() & 1 << i) { hasFakeHit = true; break; @@ -5282,9 +5514,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/dca/after/hDCAxyVsPtProtonTrue"), track.pt(), track.dcaXY()); histos.fill(HIST("tracks/proton/dca/after/hDCAzVsPtProtonTrue"), track.pt(), track.dcaZ()); } - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr) { + if (passPrTPCpid) histos.fill(HIST("tracks/proton/h1ProtonSpectraTrueWPID"), track.pt()); - } if (isPhysPrim) { histos.fill(HIST("tracks/proton/h1ProtonSpectraTruePrim"), track.pt()); if (outFlagOptions.makeDCAAfterCutPlots) { @@ -5336,7 +5567,7 @@ struct LFNucleiBATask { } } - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr) { + if (passPrTPCpid) { if (enableTrackingEff) { if (isItsPassed) { debugHistos.fill(HIST("tracks/proton/trackingEffPID/h1ProtonSpectraPIDTruePrim_its"), track.pt()); @@ -5405,9 +5636,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/proton/dca/after/hDCAxyVsPtantiProtonTrue"), track.pt(), track.dcaXY()); histos.fill(HIST("tracks/proton/dca/after/hDCAzVsPtantiProtonTrue"), track.pt(), track.dcaZ()); } - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr) { + if (passPrTPCpid) histos.fill(HIST("tracks/proton/h1antiProtonSpectraTrueWPID"), track.pt()); - } if (isPhysPrim) { histos.fill(HIST("tracks/proton/h1antiProtonSpectraTruePrim"), track.pt()); if (outFlagOptions.makeDCAAfterCutPlots) { @@ -5459,7 +5689,7 @@ struct LFNucleiBATask { } } - if (std::abs(track.tpcNSigmaPr()) < nsigmaTPCvar.nsigmaTPCPr) { + if (passPrTPCpid) { if (enableTrackingEff) { if (isItsPassed) { debugHistos.fill(HIST("tracks/proton/trackingEffPID/h1antiProtonSpectraPIDTruePrim_its"), track.pt()); @@ -5529,11 +5759,10 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/deuteron/dca/after/hDCAxyVsPtDeuteronTrue"), DPt, track.dcaXY()); histos.fill(HIST("tracks/deuteron/dca/after/hDCAzVsPtDeuteronTrue"), DPt, track.dcaZ()); } - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe) { + if (passDeTPCpid) { histos.fill(HIST("tracks/deuteron/h1DeuteronSpectraTrueWPID"), DPt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/hPtDeuteronTOFTrue"), DPt); - } } if (isPhysPrim) { histos.fill(HIST("tracks/deuteron/h2DeuteronVspTNSigmaTPCTruePrim"), DPt, track.tpcNSigmaDe()); @@ -5587,15 +5816,14 @@ struct LFNucleiBATask { } } - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe) { + if (passDeTPCpid) { histos.fill(HIST("tracks/deuteron/h1DeuteronSpectraTrueWPIDPrim"), DPt); if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/deuteron/dca/after/hDCAxyVsPtDeuteronTrueWPIDPrim"), DPt, track.dcaXY()); histos.fill(HIST("tracks/deuteron/dca/after/hDCAzVsPtDeuteronTrueWPIDPrim"), DPt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/hPtDeuteronTOFTrueWPIDPrim"), DPt); - } if (enableTrackingEff) { if (isItsPassed) { @@ -5665,11 +5893,10 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/deuteron/dca/after/hDCAxyVsPtantiDeuteronTrue"), antiDPt, track.dcaXY()); histos.fill(HIST("tracks/deuteron/dca/after/hDCAzVsPtantiDeuteronTrue"), antiDPt, track.dcaZ()); } - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe) { + if (passDeTPCpid) { histos.fill(HIST("tracks/deuteron/h1antiDeuteronSpectraTrueWPID"), antiDPt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/hPtantiDeuteronTOFTrue"), antiDPt); - } } if (isPhysPrim) { histos.fill(HIST("tracks/deuteron/h2antiDeuteronVspTNSigmaTPCTruePrim"), antiDPt, track.tpcNSigmaDe()); @@ -5723,15 +5950,14 @@ struct LFNucleiBATask { } } - if (std::abs(track.tpcNSigmaDe()) < nsigmaTPCvar.nsigmaTPCDe) { + if (passDeTPCpid) { histos.fill(HIST("tracks/deuteron/h1antiDeuteronSpectraTrueWPIDPrim"), antiDPt); if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/deuteron/dca/after/hDCAxyVsPtantiDeuteronTrueWPIDPrim"), antiDPt, track.dcaXY()); histos.fill(HIST("tracks/deuteron/dca/after/hDCAzVsPtantiDeuteronTrueWPIDPrim"), antiDPt, track.dcaZ()); } - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/deuteron/hPtantiDeuteronTOFTrueWPIDPrim"), antiDPt); - } if (enableTrackingEff) { if (isItsPassed) { @@ -5864,35 +6090,41 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtHeliumTrue"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtHeliumTrue"), hePt, track.dcaZ()); } } - if (std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe) { + if (passHeTPCpid) { histos.fill(HIST("tracks/helium/h1HeliumSpectraTrueWPID_Z2"), 2 * hePt); if (enableCentrality) { histos.fill(HIST("tracks/helium/h2HeliumSpectraTrueWPIDVsMult_Z2"), 2 * hePt, centFT0M); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/TOF/h2HeliumSpectraTrueWPIDVsMult_Z2"), 2 * hePt, centFT0M); - } } if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/helium/hPtHeTrue_Z2"), 2 * hePt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/eff/helium/hPtHeTOFTrue_Z2"), 2 * hePt); - } } } if (isPhysPrim) { + if constexpr (!IsFilteredData) { + if (enableDebug) { + debugHistos.fill(HIST("tracks/helium/migration/h2PtGenVsPtRecoPrimHe"), track.mcParticle().pt(), 2.f * hePt); + if (passHeTPCpid && hasTOFplots) { + debugHistos.fill(HIST("tracks/helium/migration/TOF/h2PtGenVsPtRecoPrimHe"), track.mcParticle().pt(), 2.f * hePt); + } + } + } histos.fill(HIST("tracks/helium/h1HeliumSpectraTruePrim_Z2"), 2 * hePt); if (enableCentrality) { histos.fill(HIST("tracks/helium/h2HeliumSpectraTruePrimVsMult_Z2"), 2 * hePt, centFT0M); if constexpr (!IsFilteredData) histos.fill(HIST("tracks/helium/h2HeliumSpectraTruePrimGenVsMult_Z2"), track.mcParticle().pt(), centFT0M); } - if (std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe) { - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (passHeTPCpid) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/TOF/h1HeliumSpectraTruePrim_Z2"), 2 * hePt); if (enableCentrality) { histos.fill(HIST("tracks/helium/TOF/h2HeliumSpectraTruePrimVsMult_Z2"), 2 * hePt, centFT0M); @@ -5905,7 +6137,7 @@ struct LFNucleiBATask { if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtHeliumTruePrim"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtHeliumTruePrim"), hePt, track.dcaZ()); } @@ -5916,7 +6148,7 @@ struct LFNucleiBATask { if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtHeliumTrueTransport"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtHeliumTrueTransport"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtHeliumTrueTransport"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtHeliumTrueTransport"), hePt, track.dcaZ()); } @@ -5928,7 +6160,7 @@ struct LFNucleiBATask { if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtHeliumTrueSec"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtHeliumTrueSec"), hePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtHeliumTrueSec"), hePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtHeliumTrueSec"), hePt, track.dcaZ()); } @@ -5942,30 +6174,36 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/h1antiHeliumSpectraTrue_Z2"), 2 * antihePt); if (enableCentrality) { histos.fill(HIST("tracks/helium/h2antiHeliumSpectraTrueVsMult_Z2"), 2 * antihePt, centFT0M); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/helium/TOF/h2antiHeliumSpectraTrueWPIDVsMult_Z2"), 2 * antihePt, centFT0M); - } } if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtantiHeliumTrue"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtantiHeliumTrue"), antihePt, track.dcaZ()); } } - if (std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe) { + if (passHeTPCpid) { histos.fill(HIST("tracks/helium/h1antiHeliumSpectraTrueWPID_Z2"), 2 * antihePt); if (enableCentrality) histos.fill(HIST("tracks/helium/h2antiHeliumSpectraTrueWPIDVsMult_Z2"), 2 * antihePt, centFT0M); if (outFlagOptions.enableEffPlots) { histos.fill(HIST("tracks/eff/helium/hPtantiHeTrue_Z2"), 2 * antihePt); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) histos.fill(HIST("tracks/eff/helium/hPtantiHeTOFTrue_Z2"), 2 * antihePt); - } } } if (isPhysPrim) { + if constexpr (!IsFilteredData) { + if (enableDebug) { + debugHistos.fill(HIST("tracks/helium/migration/h2PtGenVsPtRecoPrimAntiHe"), track.mcParticle().pt(), 2.f * antihePt); + if (passHeTPCpid && hasTOFplots) { + debugHistos.fill(HIST("tracks/helium/migration/TOF/h2PtGenVsPtRecoPrimAntiHe"), track.mcParticle().pt(), 2.f * antihePt); + } + } + } histos.fill(HIST("tracks/helium/h1antiHeliumSpectraTruePrim_Z2"), 2 * antihePt); if (enableCentrality) { histos.fill(HIST("tracks/helium/h2antiHeliumSpectraTruePrimVsMult_Z2"), 2 * antihePt, centFT0M); @@ -5973,8 +6211,8 @@ struct LFNucleiBATask { histos.fill(HIST("tracks/helium/h2antiHeliumSpectraTruePrimGenVsMult_Z2"), track.mcParticle().pt(), centFT0M); } - if (std::abs(track.tpcNSigmaHe()) < nsigmaTPCvar.nsigmaTPCHe) { - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (passHeTPCpid) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/TOF/h1antiHeliumSpectraTruePrim_Z2"), 2 * antihePt); if (enableCentrality) { histos.fill(HIST("tracks/helium/TOF/h2antiHeliumSpectraTruePrimVsMult_Z2"), 2 * antihePt, centFT0M); @@ -5987,7 +6225,7 @@ struct LFNucleiBATask { if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtantiHeliumTruePrim"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtantiHeliumTruePrim"), antihePt, track.dcaZ()); } @@ -5998,7 +6236,7 @@ struct LFNucleiBATask { if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtantiHeliumTrueTransport"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtantiHeliumTrueTransport"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtantiHeliumTrueTransport"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtantiHeliumTrueTransport"), antihePt, track.dcaZ()); } @@ -6011,7 +6249,7 @@ struct LFNucleiBATask { if (outFlagOptions.makeDCAAfterCutPlots) { histos.fill(HIST("tracks/helium/dca/after/hDCAxyVsPtantiHeliumTrueSec"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/hDCAzVsPtantiHeliumTrueSec"), antihePt, track.dcaZ()); - if (track.hasTOF() && outFlagOptions.doTOFplots) { + if (hasTOFplots) { histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAxyVsPtantiHeliumTrueSec"), antihePt, track.dcaXY()); histos.fill(HIST("tracks/helium/dca/after/TOF/hDCAzVsPtantiHeliumTrueSec"), antihePt, track.dcaZ()); } @@ -6090,7 +6328,7 @@ struct LFNucleiBATask { } } - using EventCandidates = soa::Join; + using EventCandidates = soa::Join; using EventCandidatesMC = soa::Join; using TrackCandidates0 = soa::Join(event, tracks, true /*dummy*/, event.centFT0M()); } - PROCESS_SWITCH(LFNucleiBATask, processData, "process data", true); + PROCESS_SWITCH(lfNucleiBATask, processData, "process data", true); // Process function that runs on the original AO2D void processDataLfPid(EventCandidates::iterator const& event, @@ -6136,7 +6374,7 @@ struct LFNucleiBATask { { fillHistograms(event, tracks, true /*dummy*/, event.centFT0M()); } - PROCESS_SWITCH(LFNucleiBATask, processDataLfPid, "process data with LF PID", false); + PROCESS_SWITCH(lfNucleiBATask, processDataLfPid, "process data with LF PID", false); // Process function that runs on the filtered data void processDataFiltered(o2::aod::LfNuclEvents::iterator const& event, @@ -6147,7 +6385,7 @@ struct LFNucleiBATask { // Takes as input full AO2Ds fillHistograms(event, tracks, true /*dummy*/, event.centFT0M()); } - PROCESS_SWITCH(LFNucleiBATask, processDataFiltered, "process data on the filtered data", false); + PROCESS_SWITCH(lfNucleiBATask, processDataFiltered, "process data on the filtered data", false); void processDataLight(o2::aod::LfNuclEvents::iterator const& event, o2::aod::LfCandNucleusDummy const& tracks, @@ -6157,7 +6395,7 @@ struct LFNucleiBATask { // Takes as input derived trees fillHistograms(event, tracks, true /*dummy*/, event.centFT0M()); } - PROCESS_SWITCH(LFNucleiBATask, processDataLight, "process data on the derived trees", false); + PROCESS_SWITCH(lfNucleiBATask, processDataLight, "process data on the derived trees", false); ///////////// // MC Reco // @@ -6191,12 +6429,13 @@ struct LFNucleiBATask { } float mcCentFT0M = -1.f; + const bool passesRecoVertex = event.posZ() >= cfgVzCutLow && event.posZ() <= cfgVzCutHigh; - if (doRecoSep && event.has_mcCollision()) { + if (doRecoSep && passesRecoVertex && event.has_mcCollision()) { const int mcIdx = event.mcCollisionId(); if (mcIdx >= 0) { effEvtSet.insert(mcIdx); - effEvtSetReady = true; + // effEvtSetReady = true; auto mcColIter = mcCollisions.iteratorAt(mcIdx); mcCentFT0M = mcColIter.centFT0M(); @@ -6205,7 +6444,7 @@ struct LFNucleiBATask { fillHistograms(event, tracks, mcParticles, mcCentFT0M); } // CLOSING PROCESS MC RECO - PROCESS_SWITCH(LFNucleiBATask, processMCReco, "process mc reco", false); + PROCESS_SWITCH(lfNucleiBATask, processMCReco, "process mc reco", false); // Process function that runs on the original AO2D (for the MC) with the LfPIDcalibration void processMCRecoLfPid(EventCandidatesMC::iterator const& event, @@ -6232,12 +6471,13 @@ struct LFNucleiBATask { } float mcCentFT0M = -1.f; + const bool passesRecoVertex = event.posZ() >= cfgVzCutLow && event.posZ() <= cfgVzCutHigh; - if (doRecoSep && event.has_mcCollision()) { + if (doRecoSep && passesRecoVertex && event.has_mcCollision()) { const int mcIdx = event.mcCollisionId(); if (mcIdx >= 0) { effEvtSet.insert(mcIdx); - effEvtSetReady = true; + // effEvtSetReady = true; auto mcColIter = mcCollisions.iteratorAt(mcIdx); mcCentFT0M = mcColIter.centFT0M(); @@ -6246,9 +6486,8 @@ struct LFNucleiBATask { fillHistograms(event, tracks, mcParticles, mcCentFT0M); } // CLOSING PROCESS MC RECO - PROCESS_SWITCH(LFNucleiBATask, processMCRecoLfPid, "process mc reco with LfPid", false); + PROCESS_SWITCH(lfNucleiBATask, processMCRecoLfPid, "process mc reco with LfPid", false); - // Process function that runs on the original AO2D (for the MC) with the LfPIDcalibration void processMCRecoLfPidEv(EventCandidatesMC const& collisions, soa::Join const&, aod::McParticles const& mcParticles, @@ -6274,91 +6513,101 @@ struct LFNucleiBATask { const float pt = mcParticle.pt(); bool isPhysPrim = mcParticle.isPhysicalPrimary(); + const bool isHe = (pdg == PDGHelium); + const bool isAntiHe = (pdg == -PDGHelium); + // No cut spectraGen.fill(HIST("LfEv/pT_nocut"), pt); - if (pdg == PDGHelium) { + if (isHe) { spectraGen.fill(HIST("LfEv/helium/pT_nocut_He"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_nocut_He"), pt); } - if (pdg == -PDGHelium) { + if (isAntiHe) { spectraGen.fill(HIST("LfEv/helium/pT_nocut_antiHe"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_nocut_antiHe"), pt); } + // Trigger TVX if (hasTVX) { spectraGen.fill(HIST("LfEv/pT_TVXtrigger"), pt); - if (pdg == PDGHelium) { + if (isHe) { spectraGen.fill(HIST("LfEv/helium/pT_TVXtrigger_He"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_TVXtrigger_He"), pt); } - if (pdg == -PDGHelium) { + if (isAntiHe) { spectraGen.fill(HIST("LfEv/helium/pT_TVXtrigger_antiHe"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_TVXtrigger_antiHe"), pt); } } + // No Time Frame Border if (hasNoTFB) { spectraGen.fill(HIST("LfEv/pT_TFrameBorder"), pt); - if (pdg == PDGHelium) { + if (isHe) { spectraGen.fill(HIST("LfEv/helium/pT_TFrameBorder_He"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_TFrameBorder_He"), pt); } - if (pdg == -PDGHelium) { + if (isAntiHe) { spectraGen.fill(HIST("LfEv/helium/pT_TFrameBorder_antiHe"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_TFrameBorder_antiHe"), pt); } } + // No ITS ROF Frame Border if (hasNoItsRofFB) { spectraGen.fill(HIST("LfEv/pT_ITSROFBorder"), pt); - if (pdg == PDGHelium) { + if (isHe) { spectraGen.fill(HIST("LfEv/helium/pT_ITSROFBorder_He"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_ITSROFBorder_He"), pt); } - if (pdg == -PDGHelium) { + if (isAntiHe) { spectraGen.fill(HIST("LfEv/helium/pT_ITSROFBorder_antiHe"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_ITSROFBorder_antiHe"), pt); } } + // Sel8 MC if (hasTVX && hasNoTFB) { spectraGen.fill(HIST("LfEv/pT_MCsel8"), pt); - if (pdg == PDGHelium) { + if (isHe) { spectraGen.fill(HIST("LfEv/helium/pT_MCsel8_He"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_MCsel8_He"), pt); } - if (pdg == -PDGHelium) { + if (isAntiHe) { spectraGen.fill(HIST("LfEv/helium/pT_MCsel8_antiHe"), pt); if (isPhysPrim) spectraGen.fill(HIST("LfEv/helium/prim/pT_MCsel8_antiHe"), pt); } } + // Sel8 tag if (hasSel8) { spectraGen.fill(HIST("LfEv/pT_sel8"), pt); - if (pdg == PDGHelium) + if (isHe) { spectraGen.fill(HIST("LfEv/helium/pT_sel8_He"), pt); - if (isPhysPrim) - spectraGen.fill(HIST("LfEv/helium/prim/pT_sel8_He"), pt); - if (pdg == -PDGHelium) + if (isPhysPrim) + spectraGen.fill(HIST("LfEv/helium/prim/pT_sel8_He"), pt); + } + if (isAntiHe) { spectraGen.fill(HIST("LfEv/helium/pT_sel8_antiHe"), pt); - if (isPhysPrim) - spectraGen.fill(HIST("LfEv/helium/prim/pT_sel8_antiHe"), pt); + if (isPhysPrim) + spectraGen.fill(HIST("LfEv/helium/prim/pT_sel8_antiHe"), pt); + } } } } } // CLOSING PROCESS MC RECO - PROCESS_SWITCH(LFNucleiBATask, processMCRecoLfPidEv, "process mc reco with LfPid w/ Event", false); + PROCESS_SWITCH(lfNucleiBATask, processMCRecoLfPidEv, "process mc reco with LfPid w/ Event", false); // Process function that runs on the filtered AO2D (for the MC) void processMCRecoFiltered(o2::aod::LfNuclEvents::iterator const& event, @@ -6367,7 +6616,7 @@ struct LFNucleiBATask { { fillHistograms(event, tracks, true /*dummy*/, event.centFT0M()); } // CLOSING PROCESS MC RECO ON FILTERED DATA - PROCESS_SWITCH(LFNucleiBATask, processMCRecoFiltered, "process mc reco on the filtered data", false); + PROCESS_SWITCH(lfNucleiBATask, processMCRecoFiltered, "process mc reco on the filtered data", false); void processMCRecoFilteredLight(o2::aod::LfNuclEvents::iterator const& event, soa::Join const& tracks, @@ -6375,28 +6624,60 @@ struct LFNucleiBATask { { fillHistograms(event, tracks, true /*dummy*/, event.centFT0M()); } // CLOSING PROCESS MC RECO ON FILTERED DATA - PROCESS_SWITCH(LFNucleiBATask, processMCRecoFilteredLight, "process mc reco on the derived trees", false); + PROCESS_SWITCH(lfNucleiBATask, processMCRecoFilteredLight, "process mc reco on the derived trees", false); //////////// // MC Gen // //////////// // LOOP OVER GENERATED MC PARTICLES - void processMCGen(soa::Join::iterator const& mcCollision, - aod::McParticles const& mcParticles) + // void processMCGen(soa::Join::iterator const& mcCollision, + // aod::McParticles const& mcParticles) + void processMCGen( + soa::Join::iterator const& mcCollision, + const soa::SmallGroups>& collisions, + aod::McParticles const& mcParticles) { - // Only events that are reconstructed const int mcIdx = mcCollision.globalIndex(); - if (enableEffEvtSet) { - if (!effEvtSetReady) + + // Add GenTVX condition + if (evselOptions.askGenFT0Coincidence) { + if (!hasGenTVX(mcParticles, mcIdx)) return; - if (!effEvtSet.count(mcIdx)) + } + + // Require Reco conditions (as in signal loss) + if (requireRecoEventForGen) { + bool hasAcceptedRecoCollision = false; + for (const auto& collision : collisions) { + const bool hasTVX = collision.selection_bit(aod::evsel::kIsTriggerTVX); + const bool hasNoTFB = collision.selection_bit(aod::evsel::kNoTimeFrameBorder); + const bool hasNoItsRofFB = collision.selection_bit(aod::evsel::kNoITSROFrameBorder); + if (evselOptions.useSel8 && !collision.sel8()) + continue; + if (evselOptions.useTVXtrigger && !hasTVX) + continue; + if (evselOptions.removeTFBorder && !hasNoTFB) + continue; + if (evselOptions.removeITSROFBorder && !hasNoItsRofFB) + continue; + if (collision.posZ() < cfgVzCutLow || collision.posZ() > cfgVzCutHigh) + continue; + if (evselOptions.useINELgt0cut && !collision.isInelGt0()) + continue; + if (evselOptions.useINELgt1cut && !collision.isInelGt1()) + continue; + hasAcceptedRecoCollision = true; + break; + } + if (!hasAcceptedRecoCollision) return; } if (enableCentrality && (mcCollision.centFT0M() < cfgMultCutLow || mcCollision.centFT0M() > cfgMultCutHigh)) return; + // Vz cut at Gen level if (evselOptions.enableGenVzCut) { if (mcCollision.posZ() < cfgVzCutLow || mcCollision.posZ() > cfgVzCutHigh) return; @@ -6407,9 +6688,6 @@ struct LFNucleiBATask { else spectraGen.fill(HIST("histGenVetxZ"), mcCollision.posZ()); - // const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcIdx, cache); - // const auto particlesInCollision = mcParticles.sliceBy(perMCCol, mcIdx); - for (const auto& mcParticleGen : mcParticles) { if (mcParticleGen.mcCollisionId() != mcIdx) continue; @@ -6645,7 +6923,7 @@ struct LFNucleiBATask { } } } // Close processMCGen - PROCESS_SWITCH(LFNucleiBATask, processMCGen, "process MC Generated", true); + PROCESS_SWITCH(lfNucleiBATask, processMCGen, "process MC Generated", false); void processEvSgLossMC(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles, @@ -6678,63 +6956,79 @@ struct LFNucleiBATask { if (isTvxEvent) evLossHistos.fill(HIST("evLoss/hEvent"), 1.5); - if (isSel8Event) - evLossHistos.fill(HIST("evLoss/hEvent"), 2.5); if (isMCSel8Event) + evLossHistos.fill(HIST("evLoss/hEvent"), 2.5); + if (isSel8Event) evLossHistos.fill(HIST("evLoss/hEvent"), 3.5); // Loop over all the Generated level particles for (const auto& mcPart : mcParticles) { if (!mcPart.isPhysicalPrimary()) continue; - if (std::abs(mcPart.y()) >= kCfgTpcClasses[0]) + if (mcPart.y() > kinemOptions.cfgRapidityCutHigh || mcPart.y() < kinemOptions.cfgRapidityCutLow) continue; - const float pt = mcPart.pt(); const int pdg = mcPart.pdgCode(); + const bool isDe = enableDe && (std::abs(pdg) == PDGDeuteron); + const bool isHe = enableHe && (std::abs(pdg) == PDGHelium); - if (pdg == PDGDeuteron) { - evLossHistos.fill(HIST("evLoss/pt/hDeuteronGen"), pt); - if (isTvxEvent) - evLossHistos.fill(HIST("evLoss/pt/hDeuteronTriggeredTVX"), pt); - if (isSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hDeuteronTriggeredSel8"), pt); - if (isMCSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hDeuteronTriggeredMCSel8"), pt); - } - if (pdg == -PDGDeuteron) { - evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronGen"), pt); - if (isTvxEvent) - evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronTriggeredTVX"), pt); - if (isSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronTriggeredSel8"), pt); - if (isMCSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronTriggeredMCSel8"), pt); - } - if (pdg == PDGHelium) { - evLossHistos.fill(HIST("evLoss/pt/hHeliumGen"), pt); - if (isTvxEvent) - evLossHistos.fill(HIST("evLoss/pt/hHeliumTriggeredTVX"), pt); - if (isSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hHeliumTriggeredSel8"), pt); - if (isMCSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hHeliumTriggeredMCSel8"), pt); - } - if (pdg == -PDGHelium) { - evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumGen"), pt); - if (isTvxEvent) - evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumTriggeredTVX"), pt); - if (isSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumTriggeredSel8"), pt); - if (isMCSel8Event) - evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumTriggeredMCSel8"), pt); + // Exclude other species + if (!isDe && !isHe) + continue; + + const float pt = mcPart.pt(); + + switch (pdg) { + case PDGDeuteron: // Deuteron + if (!enableDe) + break; + evLossHistos.fill(HIST("evLoss/pt/hDeuteronGen"), pt); + if (isTvxEvent) + evLossHistos.fill(HIST("evLoss/pt/hDeuteronTriggeredTVX"), pt); + if (isSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hDeuteronTriggeredSel8"), pt); + if (isMCSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hDeuteronTriggeredMCSel8"), pt); + break; + case -PDGDeuteron: // Antideuteron + if (!enableDe) + break; + evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronGen"), pt); + if (isTvxEvent) + evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronTriggeredTVX"), pt); + if (isSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronTriggeredSel8"), pt); + if (isMCSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hAntiDeuteronTriggeredMCSel8"), pt); + break; + case PDGHelium: // Helium-3 + if (!enableHe) + break; + evLossHistos.fill(HIST("evLoss/pt/hHeliumGen"), pt); + if (isTvxEvent) + evLossHistos.fill(HIST("evLoss/pt/hHeliumTriggeredTVX"), pt); + if (isSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hHeliumTriggeredSel8"), pt); + if (isMCSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hHeliumTriggeredMCSel8"), pt); + break; + case -PDGHelium: // Antihelium-3 + if (!enableHe) + break; + evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumGen"), pt); + if (isTvxEvent) + evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumTriggeredTVX"), pt); + if (isSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumTriggeredSel8"), pt); + if (isMCSel8Event) + evLossHistos.fill(HIST("evLoss/pt/hAntiHeliumTriggeredMCSel8"), pt); + break; + default: + break; } } } - PROCESS_SWITCH(LFNucleiBATask, processEvSgLossMC, "process MC SignLoss", false); - - // void processMCGen(soa::Join::iterator const& mcCollision, - // aod::McParticles const& mcParticles) + PROCESS_SWITCH(lfNucleiBATask, processEvSgLossMC, "process MC SignLoss", false); // EVENT LOSS, SIGNAL LOSS and EFFICIENCY CHECKER process function void processMCGenLosses( @@ -6742,8 +7036,14 @@ struct LFNucleiBATask { const soa::SmallGroups>& collisions, aod::McParticles const& mcParticles) { + const int mcIdx = mcCollision.globalIndex(); + const bool genFT0Coincidence = hasGenTVX(mcParticles, mcIdx); bool isINELgt0true = pwglf::isINELgtNmc(mcParticles, 0, pdgDB); + // GenFT0 coincidence + if (evselOptions.askGenFT0Coincidence && !genFT0Coincidence) + return; + // EVENT LOSS DENOMINATOR // No cuts histoGen.fill(HIST("events/hMCGen"), 0.5); @@ -6751,8 +7051,11 @@ struct LFNucleiBATask { histoGen.fill(HIST("events/hMCGenVsMult"), 0.5, mcCollision.centFT0M()); // Vtz cut - if (mcCollision.posZ() < cfgVzCutLow || mcCollision.posZ() > cfgVzCutHigh) - return; + if (evselOptions.enableGenVzCut) { + if (mcCollision.posZ() < cfgVzCutLow || mcCollision.posZ() > cfgVzCutHigh) + return; + } + histoGen.fill(HIST("events/hMCGen"), 1.5); if (enableCentrality) histoGen.fill(HIST("events/hMCGenVsMult"), 1.5, mcCollision.centFT0M()); @@ -6773,6 +7076,7 @@ struct LFNucleiBATask { float pt = mcParticle.pt(); bool isPhysPrim = mcParticle.isPhysicalPrimary(); + // Helium if (enableHe && isPhysPrim && (std::abs(pdg) == PDGHelium)) { if (pdg > 0) { histoGen.fill(HIST("helium/MCGen/h2HeliumYvsPt"), mcParticle.y(), pt); @@ -6785,6 +7089,7 @@ struct LFNucleiBATask { if (enableCentrality) histoGen.fill(HIST("helium/MCGen/ptGenVsMult_INEL_Prim_antiHe"), pt, mcCollision.centFT0M()); } + if (isINELgt0true) { if (pdg > 0) { histoGen.fill(HIST("helium/MCGen/ptGen_INELgt0_Prim_He"), pt); @@ -6797,31 +7102,83 @@ struct LFNucleiBATask { } } } + + // Proton + if (enablePr && isPhysPrim && (std::abs(pdg) == PDGProton)) { + if (pdg > 0) { + histoGen.fill(HIST("proton/MCGen/h2ProtonYvsPt"), mcParticle.y(), pt); + histoGen.fill(HIST("proton/MCGen/ptGen_INEL_Prim_P"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGen/ptGenVsMult_INEL_Prim_P"), pt, mcCollision.centFT0M()); + } else { + histoGen.fill(HIST("proton/MCGen/h2antiProtonYvsPt"), mcParticle.y(), pt); + histoGen.fill(HIST("proton/MCGen/ptGen_INEL_Prim_antiP"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGen/ptGenVsMult_INEL_Prim_antiP"), pt, mcCollision.centFT0M()); + } + if (isINELgt0true) { + if (pdg > 0) { + histoGen.fill(HIST("proton/MCGen/ptGen_INELgt0_Prim_P"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGen/ptGenVsMult_INELgt0_Prim_P"), pt, mcCollision.centFT0M()); + } else { + histoGen.fill(HIST("proton/MCGen/ptGen_INELgt0_Prim_antiP"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGen/ptGenVsMult_INELgt0_Prim_antiP"), pt, mcCollision.centFT0M()); + } + } + } } int recoIdxINEL = 0; int recoIdxINELgt0 = 0; - // for (const auto& collision : collisions) { + bool atLeastOneINEL = false; + bool atLeastOneINELgt0 = false; + for (const auto& collision : collisions) { + bool isSel8Event = collision.sel8(); bool hasTVX = collision.selection_bit(aod::evsel::kIsTriggerTVX); bool hasNoTFB = collision.selection_bit(aod::evsel::kNoTimeFrameBorder); bool hasNoItsRofFB = collision.selection_bit(aod::evsel::kNoITSROFrameBorder); - // Check event selection + bool thisCollisionINELgt0 = false; + histoGen.fill(HIST("events/hMCReco"), 0.5); + if (enableCentrality) + histoGen.fill(HIST("events/hMCRecoVsMult"), 0.5, mcCollision.centFT0M()); + + // Event selection cuts + if (evselOptions.useSel8 && !isSel8Event) + continue; if (evselOptions.useTVXtrigger && !hasTVX) continue; if (evselOptions.removeTFBorder && !hasNoTFB) continue; if (evselOptions.removeITSROFBorder && !hasNoItsRofFB) continue; + + // Vtz cut + if (collision.posZ() < cfgVzCutLow || collision.posZ() > cfgVzCutHigh) + continue; + + // EVENT SPLITTING DENOMINATOR histoGen.fill(HIST("events/hMCReco"), 1.5); + if (enableCentrality) + histoGen.fill(HIST("events/hMCRecoVsMult"), 1.5, mcCollision.centFT0M()); recoIdxINEL++; + atLeastOneINEL = true; if (collision.isInelGt0() && isINELgt0true) { + thisCollisionINELgt0 = true; + + // EVENT SPLITTING DENOMINATOR histoGen.fill(HIST("events/hMCReco"), 2.5); + if (enableCentrality) + histoGen.fill(HIST("events/hMCRecoVsMult"), 2.5, mcCollision.centFT0M()); + recoIdxINELgt0++; + atLeastOneINELgt0 = true; } for (const auto& mcParticle : mcParticles) { @@ -6842,7 +7199,8 @@ struct LFNucleiBATask { if (enableCentrality) histoGen.fill(HIST("helium/MCReco/ptGenVsMult_INEL_Prim_antiHe"), pt, mcCollision.centFT0M()); } - if (recoIdxINELgt0 > 0) { + + if (thisCollisionINELgt0) { if (pdg > 0) { histoGen.fill(HIST("helium/MCReco/ptGen_INELgt0_Prim_He"), pt); if (enableCentrality) @@ -6854,19 +7212,73 @@ struct LFNucleiBATask { } } } + + if (enablePr && isPhysPrim && (std::abs(pdg) == PDGProton)) { + if (pdg > 0) { + histoGen.fill(HIST("proton/MCReco/ptGen_INEL_Prim_P"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCReco/ptGenVsMult_INEL_Prim_P"), pt, mcCollision.centFT0M()); + } else { + histoGen.fill(HIST("proton/MCReco/ptGen_INEL_Prim_antiP"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCReco/ptGenVsMult_INEL_Prim_antiP"), pt, mcCollision.centFT0M()); + } + + if (thisCollisionINELgt0) { + if (pdg > 0) { + histoGen.fill(HIST("proton/MCReco/ptGen_INELgt0_Prim_P"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCReco/ptGenVsMult_INELgt0_Prim_P"), pt, mcCollision.centFT0M()); + } else { + histoGen.fill(HIST("proton/MCReco/ptGen_INELgt0_Prim_antiP"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCReco/ptGenVsMult_INELgt0_Prim_antiP"), pt, mcCollision.centFT0M()); + } + } + } } } - if (recoIdxINEL < 1) { - return; + histoGen.fill(HIST("events/hNRecoCollPerMCColl_INEL"), recoIdxINEL); + histoGen.fill(HIST("events/hNRecoCollPerMCColl_INELgt0"), recoIdxINELgt0); + if (enableCentrality) { + histoGen.fill(HIST("events/hNRecoCollPerMCCollVsMult_INEL"), recoIdxINEL, mcCollision.centFT0M()); + histoGen.fill(HIST("events/hNRecoCollPerMCCollVsMult_INELgt0"), recoIdxINELgt0, mcCollision.centFT0M()); + } + // Add checker for EffEvtSet + const int mcIdxCheck = mcCollision.globalIndex(); + const bool inEffEvtSet = effEvtSet.contains(mcIdxCheck); + + if (enableDebug) { + double effEvtSetValue = 0.0; + double inelValue = 0.0; + + if (inEffEvtSet) { + effEvtSetValue = 1.0; + } + if (atLeastOneINEL) { + inelValue = 1.0; + } + + spectraGen.fill(HIST("hEffEvtSetVsSG"), effEvtSetValue, inelValue); + + if (inEffEvtSet && !atLeastOneINEL) { + if (collisions.size() == 0) + spectraGen.fill(HIST("hEffEvtSetFalsePositive"), 0.5); + else + spectraGen.fill(HIST("hEffEvtSetFalsePositive"), 1.5); + } } - // EVENT LOSS NUMERATOR + if (!atLeastOneINEL) + return; + + // EVENT LOSS NUMERATOR & EVENT SPLITTING NUMERATOR histoGen.fill(HIST("events/hMCGenReco"), 0.5); if (enableCentrality) histoGen.fill(HIST("events/hMCGenRecoVsMult"), 0.5, mcCollision.centFT0M()); - if (recoIdxINELgt0 > 0) { + if (atLeastOneINELgt0) { histoGen.fill(HIST("events/hMCGenReco"), 1.5); if (enableCentrality) histoGen.fill(HIST("events/hMCGenRecoVsMult"), 1.5, mcCollision.centFT0M()); @@ -6893,7 +7305,8 @@ struct LFNucleiBATask { if (enableCentrality) histoGen.fill(HIST("helium/MCGenReco/ptGenVsMult_INEL_Prim_antiHe"), pt, mcCollision.centFT0M()); } - if (recoIdxINELgt0 > 0) { + + if (atLeastOneINELgt0) { if (pdg > 0) { histoGen.fill(HIST("helium/MCGenReco/ptGen_INELgt0_Prim_He"), pt); if (enableCentrality) @@ -6905,12 +7318,38 @@ struct LFNucleiBATask { } } } + + if (enablePr && isPhysPrim && (std::abs(pdg) == PDGProton)) { + if (pdg > 0) { + histoGen.fill(HIST("proton/MCGenReco/h2ProtonYvsPt"), mcParticle.y(), pt); + histoGen.fill(HIST("proton/MCGenReco/ptGen_INEL_Prim_P"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGenReco/ptGenVsMult_INEL_Prim_P"), pt, mcCollision.centFT0M()); + } else { + histoGen.fill(HIST("proton/MCGenReco/h2antiProtonYvsPt"), mcParticle.y(), pt); + histoGen.fill(HIST("proton/MCGenReco/ptGen_INEL_Prim_antiP"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGenReco/ptGenVsMult_INEL_Prim_antiP"), pt, mcCollision.centFT0M()); + } + + if (atLeastOneINELgt0) { + if (pdg > 0) { + histoGen.fill(HIST("proton/MCGenReco/ptGen_INELgt0_Prim_P"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGenReco/ptGenVsMult_INELgt0_Prim_P"), pt, mcCollision.centFT0M()); + } else { + histoGen.fill(HIST("proton/MCGenReco/ptGen_INELgt0_Prim_antiP"), pt); + if (enableCentrality) + histoGen.fill(HIST("proton/MCGenReco/ptGenVsMult_INELgt0_Prim_antiP"), pt, mcCollision.centFT0M()); + } + } + } } } - PROCESS_SWITCH(LFNucleiBATask, processMCGenLosses, "process MCGen losses", false); + PROCESS_SWITCH(lfNucleiBATask, processMCGenLosses, "process MCGen losses", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Nuspex/multiplicityPt.cxx b/PWGLF/Tasks/Nuspex/multiplicityPt.cxx index 6aaebebc9dd..a78aca0e336 100644 --- a/PWGLF/Tasks/Nuspex/multiplicityPt.cxx +++ b/PWGLF/Tasks/Nuspex/multiplicityPt.cxx @@ -16,9 +16,12 @@ #include "PWGLF/DataModel/mcCentrality.h" #include "PWGLF/Utils/inelGt.h" +#include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/TrackSelection.h" #include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -29,112 +32,751 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include +#include +#include +#include #include #include -#include +#include #include -#include #include -#include -#include +#include #include -#include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -using namespace o2::constants::math; -using namespace constants::physics; using BCsRun3 = soa::Join; +// Reconstructed collisions joined with MC label + centrality +using ColEvSelsMC = soa::Join; + +using McCollisionsCent = soa::Join; + +// Data collision table (for processData) +using CollisionTableData = soa::Join; + +// Track tables - with TPC PID only +using TrackTableData = soa::Join; + +using TracksMC = soa::Join; + struct MultiplicityPt { - Service pdg; - Service ccdb; + // ── Services ────────────────────────────────────────────── + Service pdg{}; + Service ccdb{}; + + static constexpr int NCentHists{10}; + static constexpr int NPartHists{5}; + std::array, NCentHists> hDedxVsMomentumVsCentPos{}; + std::array, NCentHists> hDedxVsMomentumVsCentNeg{}; + std::array, NCentHists + 1> hDedxVspTMomentumVsCent{}; + std::array, NCentHists + 1> hMomentumVsEtaPos{}; + std::array, NCentHists + 1> hMomentumVsEtaNeg{}; + std::array, NCentHists + 1> hpTVsEtaPos{}; + std::array, NCentHists + 1> hpTVsEtaNeg{}; + // Total counts + std::array, NCentHists + 1> hTotalMomPosCent{}; + std::array, NCentHists + 1> hTotalMomNegCent{}; + std::array, NCentHists + 1> hTotalPtPosCent{}; + std::array, NCentHists + 1> hTotalPtNegCent{}; + // Counts for particles + std::array, NCentHists + 1>, NPartHists> hFracMomPosCent{}; + std::array, NCentHists + 1>, NPartHists> hFracMomNegCent{}; + std::array, NCentHists + 1>, NPartHists> hFracPtPosCent{}; + std::array, NCentHists + 1>, NPartHists> hFracPtNegCent{}; + + // ── Constant values ────────────────────────────────── static constexpr int CentBinMax = 100; - static constexpr int MultBinMax = 200; - static constexpr int RecMultBinMax = 100; - - enum INELCutSelection : int { - INEL = 0, - INELgt0 = 1, - INELgt1 = 2 - }; + static constexpr int NEvLabel = 15; + static constexpr int ResponseMatrixTypes = 7; + // ── Configurables: event ────────────────────────────────── Configurable isRun3{"isRun3", true, "is Run3 dataset"}; Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; Configurable cfgINELCut{"cfgINELCut", 0, "INEL event selection: 0 no sel, 1 INEL>0, 2 INEL>1"}; + Configurable askForCustomTVX{"askForCustomTVX", false, "Ask for custom TVX rather than sel8"}; + Configurable removeITSROFrameBorder{"removeITSROFrameBorder", false, "Remove ITS Read-Out Frame border"}; + Configurable removeNoSameBunchPileup{"removeNoSameBunchPileup", false, "Remove no same bunch pileup"}; + Configurable requireIsGoodZvtxFT0vsPV{"requireIsGoodZvtxFT0vsPV", false, "Require good Z vertex FT0 vs PV"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "Require vertex ITSTPC"}; + Configurable removeNoTimeFrameBorder{"removeNoTimeFrameBorder", false, "Remove no time frame border"}; + Configurable nGoodITS{"nGoodITS", false, "Numbers of inactive chips on all ITS layers are below maximum allowed values"}; + + // Gen-level event selection + Configurable selTVXMC{"selTVXMC", true, "Require TVX-equivalent at gen level"}; + Configurable isZvtxPosSelMC{"isZvtxPosSelMC", true, "Require |Zvtx| cfgCutEtaMax{"cfgCutEtaMax", 0.8f, "Max eta range for tracks"}; Configurable cfgCutEtaMin{"cfgCutEtaMin", -0.8f, "Min eta range for tracks"}; + Configurable cfgCutY{"cfgCutY", 0.5f, "Y range for tracks"}; Configurable cfgCutNsigma{"cfgCutNsigma", 3.0f, "nsigma cut range for tracks"}; - + Configurable lastRequiredTrdCluster{"lastRequiredTrdCluster", -1, "Last cluster to require in TRD"}; + Configurable requireTrdOnly{"requireTrdOnly", false, "Require only tracks from TRD"}; + Configurable requireNoTrd{"requireNoTrd", false, "Require tracks without TRD"}; + Configurable multiplicityEstimator{"multiplicityEstimator", 6, + "Multiplicity estimator: 0=NoMult, 1=MultFV0M, 2=MultFT0M, 3=MultFDDM, 4=MultTracklets, 5=MultTPC, 6=MultNTracksPV, 7=MultNTracksPVeta1, 8=CentFT0C, 9=CentFT0M, 10=CentFV0A"}; + + // Track cuts + Configurable enableDCAHistograms{"enableDCAHistograms", false, "Enable DCA histograms"}; Configurable enablePIDHistograms{"enablePIDHistograms", true, "Enable PID histograms"}; Configurable useCustomTrackCuts{"useCustomTrackCuts", true, "Flag to use custom track cuts"}; Configurable itsPattern{"itsPattern", 0, "0 = Run3ITSibAny, 1 = Run3ITSallAny, 2 = Run3ITSall7Layers, 3 = Run3ITSibTwo"}; - Configurable requireITS{"requireITS", true, "Additional cut on the ITS requirement"}; - Configurable requireTPC{"requireTPC", true, "Additional cut on the TPC requirement"}; + Configurable requireITS{"requireITS", false, "Additional cut on the ITS requirement"}; + Configurable requireTPC{"requireTPC", false, "Additional cut on the TPC requirement"}; Configurable requireGoldenChi2{"requireGoldenChi2", true, "Additional cut on the GoldenChi2"}; Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.f, "Additional cut on the minimum number of crossed rows in the TPC"}; + Configurable minNCrossedRowsOverFindableClustersTPCBool{"minNCrossedRowsOverFindableClustersTPCBool", false, "Enable/disable cut on findable clusters in the TPC"}; Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; Configurable maxChi2PerClusterTPC{"maxChi2PerClusterTPC", 4.f, "Additional cut on the maximum value of the chi2 per cluster in the TPC"}; Configurable minChi2PerClusterTPC{"minChi2PerClusterTPC", 0.5f, "Additional cut on the minimum value of the chi2 per cluster in the TPC"}; Configurable maxChi2PerClusterITS{"maxChi2PerClusterITS", 36.f, "Additional cut on the maximum value of the chi2 per cluster in the ITS"}; - Configurable maxDcaXYFactor{"maxDcaXYFactor", 1.f, "Additional cut on the maximum value of the DCA xy (multiplicative factor)"}; - Configurable maxDcaZ{"maxDcaZ", 0.1f, "Additional cut on the maximum value of the DCA z"}; + Configurable nSigmaDCAxy{"nSigmaDCAxy", 1.f, "Additional cut on the maximum value of the DCA xy (multiplicative factor)"}; + Configurable dcaXYp0{"dcaXYp0", 0.0105f, "DCAxy formula: p0 + p1/pt^p2"}; + Configurable dcaXYp1{"dcaXYp1", 0.0350f, "DCAxy p1 parameter"}; + Configurable dcaXYp2{"dcaXYp2", 1.1f, "DCAxy p2 parameter"}; + Configurable nSigmaDCAz{"nSigmaDCAz", 1.f, "Additional cut on the maximum value of the DCA z (multiplicative factor)"}; + Configurable dcaZp0{"dcaZp0", 0.0105f, "DCAz formula: p0 + p1/pt^p2"}; + Configurable dcaZp1{"dcaZp1", 0.0350f, "DCAz p1 parameter"}; + Configurable dcaZp2{"dcaZp2", 1.1f, "DCAz p2 parameter"}; Configurable minTPCNClsFound{"minTPCNClsFound", 70.0f, "min number of found TPC clusters"}; Configurable minTPCNClsPID{"minTPCNClsPID", 130.0f, "min number of PID TPC clusters"}; Configurable nClTPCFoundCut{"nClTPCFoundCut", false, "Apply TPC found clusters cut"}; Configurable nClTPCPIDCut{"nClTPCPIDCut", true, "Apply TPC clusters for PID cut"}; + Configurable minITSnClusters{"minITSnClusters", 5, "minimum number of found ITS clusters"}; + Configurable cfgTrkLowPtCut{"cfgTrkLowPtCut", 0.15f, "Minimum constituent pT"}; + + // PID selection + Configurable cfgCutNsigmaPi{"cfgCutNsigmaPi", 3.0f, "nsigma cut for pions"}; + Configurable cfgCutNsigmaKa{"cfgCutNsigmaKa", 2.5f, "nsigma cut for kaons"}; + Configurable cfgCutNsigmaPr{"cfgCutNsigmaPr", 2.5f, "nsigma cut for protons"}; - Configurable applyPhiCut{"applyPhiCut", false, "Apply phi sector cut to remove problematic TPC regions"}; + // Phi cut parameters + Configurable applyPhiCut{"applyPhiCut", true, "Apply phi sector cut"}; Configurable pTthresholdPhiCut{"pTthresholdPhiCut", 2.0f, "pT threshold above which to apply phi cut"}; Configurable phiCutLowParam1{"phiCutLowParam1", 0.119297, "First parameter for low phi cut"}; Configurable phiCutLowParam2{"phiCutLowParam2", 0.000379693, "Second parameter for low phi cut"}; Configurable phiCutHighParam1{"phiCutHighParam1", 0.16685, "First parameter for high phi cut"}; Configurable phiCutHighParam2{"phiCutHighParam2", 0.00981942, "Second parameter for high phi cut"}; - Configurable cfgTrkLowPtCut{"cfgTrkLowPtCut", 0.15f, "Minimum constituent pT"}; + // ── Nch window for multiplicity axis ────────────────────── + Configurable tpcNchAcceptance{"tpcNchAcceptance", 0.8f, + "|eta| window for counting gen. Nch (multiplicity axis)"}; - Configurable cfgCutNsigmaPi{"cfgCutNsigmaPi", 3.0f, "nsigma cut for pions"}; - Configurable cfgCutNsigmaKa{"cfgCutNsigmaKa", 2.5f, "nsigma cut for kaons"}; - Configurable cfgCutNsigmaPr{"cfgCutNsigmaPr", 2.5f, "nsigma cut for protons"}; + // ── Histogram binning ──────────────────────────────────── + Configurable nBinsNch{"nBinsNch", 200, "Bins on gen-Nch axis"}; + Configurable maxNch{"maxNch", 200.0f, "Max gen Nch on histogram axis"}; + Configurable nBinsNPV{"nBinsNPV", 600, "N bins ITS tracks"}; + Configurable minNpv{"minNpv", 0, "Min NPV"}; + Configurable maxNpv{"maxNpv", 600, "Max NPV"}; + ConfigurableAxis ptBinning{"ptBinning", {VARIABLE_WIDTH, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0}, "pT bin limits"}; + ConfigurableAxis pFineBins{"pFineBins", {1995, 0.1, 40}, "Binning for momentum"}; + ConfigurableAxis dedxBins{"dedxBins", {100, 0, 100}, "Binning for dedx"}; + std::vector centBinningStd = {0., 1., 5., 10., 15., 20., 30., 40., 50., 70., 100.}; + ConfigurableAxis dcaBins{"dcaBins", {200, -0.5, 0.5}, "Binning for DCA plots"}; + // ── Custom track-selection object ──────────────────────── TrackSelection customTrackCuts; + + // ── TF1 pointers for phi cuts ──────────────────────────── TF1* fphiCutLow = nullptr; TF1* fphiCutHigh = nullptr; - HistogramRegistry ue; - - using CollisionTableData = soa::Join; - using TrackTableData = soa::Join; - using TrackTableMC = soa::Join; - using ParticlesMC = aod::McParticles; - using McCollisions = aod::McCollisions; - using RecoCollisions = aod::Collisions; - - Preslice perMCCol = aod::mcparticle::mcCollisionId; + // ── Histogram registry ─────────────────────────────────── + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryFrac{"registryFrac", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // ── Preslice: group tracks by reco collision ────────────── + Preslice perCollision = aod::track::collisionId; + + // FT0 acceptance for TVX-equivalent gen-level selection + static constexpr float MinFT0A = 3.5f; + static constexpr float MaxFT0A = 4.9f; + static constexpr float MinFT0C = -3.3f; + static constexpr float MaxFT0C = -2.1f; + + static constexpr float MinCharge = 3.0f; + static constexpr int CentralityClasses = 10; + + // dE/dx histogram names + static constexpr int ParticleTypes = 4; + static constexpr int ParticleMinusOne = 3; + + // Response Matrix histogram names + static constexpr std::string_view EtavspvspTPosPart[ResponseMatrixTypes] = {"heta_vs_pt_vs_p_all_Pos", "heta_vs_pt_vs_p_all_Pos_Pri", "heta_vs_pt_vs_p_all_Pos_Pri_MC", "heta_vs_pt_vs_p_all_Pos_Pri_MC_Part", "heta_vs_pt_vs_p_Pi_Pos", "heta_vs_pt_vs_p_K_Pos", "heta_vs_pt_vs_p_Pr_Pos"}; + static constexpr std::string_view EtavspvspTNegPart[ResponseMatrixTypes] = {"heta_vs_pt_vs_p_all_Neg", "heta_vs_pt_vs_p_all_Neg_Pri", "heta_vs_pt_vs_p_all_Neg_Pri_MC", "heta_vs_pt_vs_p_all_Neg_Pri_MC_Part", "heta_vs_pt_vs_p_Pi_Neg", "heta_vs_pt_vs_p_K_Neg", "heta_vs_pt_vs_p_Pr_Neg"}; + + // Event counter bins + enum EvCutLabel { + kAllGen = 1, + kTVXequiv, + kVtxZ, + kINELgt0, + kRecoColl, + kGoodITS, + kRecoSelected + }; + // Particle species enum enum ParticleSpecies : int { - PartPion = 0, - PartKaon = 1, - PartProton = 2, + kPion = 0, + kKaon = 1, + kProton = 2, + kAllCharged = 3, + kNSpecies = 4 }; + enum INELCutSelection : int { + INEL = 0, + INELgt0 = 1, + INELgt1 = 2 + }; + + void init(InitContext const&) + { + // Setup custom track cuts + if (useCustomTrackCuts.value) { + customTrackCuts = getGlobalTrackSelectionRun3ITSMatch(itsPattern.value); + customTrackCuts.SetRequireITSRefit(requireITS.value); + customTrackCuts.SetRequireTPCRefit(requireTPC.value); + customTrackCuts.SetMinNClustersITS(minITSnClusters.value); + customTrackCuts.SetRequireGoldenChi2(requireGoldenChi2.value); + customTrackCuts.SetMaxChi2PerClusterTPC(maxChi2PerClusterTPC.value); + customTrackCuts.SetMaxChi2PerClusterITS(maxChi2PerClusterITS.value); + customTrackCuts.SetMinNCrossedRowsTPC(minNCrossedRowsTPC.value); + if (minNCrossedRowsOverFindableClustersTPCBool.value) { + customTrackCuts.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); + } + + // customTrackCuts.SetMaxDcaXYPtDep([](float /*pt*/) { return 10000.f; }); + // customTrackCuts.SetMaxDcaZ(maxDcaZ.value); + } + + // Initialize phi cut functions if enabled + if (applyPhiCut.value) { + fphiCutLow = new TF1("StandardPhiCutLow", + Form("%f/x/x+pi/18.0-%f", + phiCutLowParam1.value, phiCutLowParam2.value), + 0, 50); + fphiCutHigh = new TF1("StandardPhiCutHigh", + Form("%f/x+pi/18.0+%f", + phiCutHighParam1.value, phiCutHighParam2.value), + 0, 50); + LOGF(info, "Phi cut ENABLED for pT > %.1f GeV/c", pTthresholdPhiCut.value); + } + + // Define axes + const AxisSpec ptAxis{ptBinning, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec dedxAxis = {dedxBins, "dE/dx (a. u.)"}; + const AxisSpec pAxis{ptBinning, "#it{p} (GeV/#it{c})"}; + const AxisSpec etaAxis{8, -0.8, 0.8, "#eta"}; + const AxisSpec pFineAxis{pFineBins, "#it{p} (GeV/c)"}; + const AxisSpec pTFineAxis{pFineBins, "#it{p}_{T} (GeV/c)"}; + const AxisSpec centAxis{centBinningStd, "FT0M Centrality (%)"}; + + // Fine centrality binning (100 bins) + std::vector centBinningFine; + for (int i = 0; i <= CentBinMax; i++) { + centBinningFine.push_back(static_cast(i)); + } + const AxisSpec centFineAxis{centBinningFine, "FT0M Centrality (%)"}; + + const AxisSpec nchAxis{nBinsNch.value, 0.f, maxNch.value, + Form("Gen. N_{ch} (|#eta|<%.1f)", tpcNchAcceptance.value)}; + + const AxisSpec npvAxis{nBinsNPV.value, minNpv.value, maxNpv.value, "NPV"}; + const AxisSpec dcaXYAxis{105, -1.05f, 1.05f, "DCA_{xy} (cm)"}; + const AxisSpec zvtxAxis{60, -30.0, 30.0, "Vtx_{z} (cm)"}; + const AxisSpec nclAxis{161, -0.5, 160.5, "N_{cl} TPC"}; + AxisSpec dcaAxis{dcaBins, ""}; + + // ======================================================================== + // EVENT COUNTER AND BASIC HISTOGRAMS + // ======================================================================== + registry.add("EventCounter", ";;Events", kTH1F, {{9, 0.5, 9.5}}); + { + auto h = registry.get(HIST("EventCounter")); + h->GetXaxis()->SetBinLabel(kAllGen, "All gen."); + h->GetXaxis()->SetBinLabel(kTVXequiv, "TVX-equiv."); + h->GetXaxis()->SetBinLabel(kVtxZ, "|Zvtx|GetXaxis()->SetBinLabel(kINELgt0, "INEL>0"); + h->GetXaxis()->SetBinLabel(kRecoColl, ">=1 reco coll."); + h->GetXaxis()->SetBinLabel(kGoodITS, "GoodITSLayersAll"); + h->GetXaxis()->SetBinLabel(kRecoSelected, ">=1 reco+sel."); + } + + registry.add("QA/genCentFT0M_raw", "Sanity check: calibrated gen-level FT0M percentile (unconditional);FT0M class (%);Entries", + kTH1F, {centFineAxis}); + + registry.add("NumberOfRecoCollisions", "Reco collisions per gen. collision;N_{reco};Entries", + kTH1F, {{10, -0.5, 9.5}}); + registry.add("zPosMC", "Gen. Vtx_{z} (evts w/ >=1 reco+sel. coll.);Vtx_{z} (cm);Entries", + kTH1F, {zvtxAxis}); + registry.add("zPosReco", "Reco. Vtx_{z} (selected);Vtx_{z} (cm);Entries", + kTH1F, {zvtxAxis}); + registry.add("T0Ccent", "FT0M centrality (selected);Centrality (%);Entries", + kTH1F, {centAxis}); + registry.add("T0CcentVsFoundFT0", "Found(1.5) NOT Found(0.5);;Status;", kTH2F, {{centAxis, {2, 0, 2}}}); + registry.add("T0CcentVsFoundFT0AndTVX", "Found(1.5) NOT Found(0.5);;Status;", kTH2F, {{centAxis, {2, 0, 2}}}); + registry.add("hvtxZ", "Vertex Z (data);Vertex Z (cm);Events", kTH1F, {{40, -20.0, 20.0}}); + registry.add("hvtxZmc", "MC vertex Z;Vertex Z (cm);Events", kTH1F, {{40, -20.0, 20.0}}); + + // ======================================================================== + // CENTRALITY DIAGNOSTIC HISTOGRAMS + // ======================================================================== + registry.add("Centrality/hCentRaw", "Raw FT0M Centrality (no cuts);Centrality (%);Counts", + kTH1D, {centFineAxis}); + registry.add("Centrality/hCentAfterVtx", "Centrality after vertex cut;Centrality (%);Counts", + kTH1D, {centFineAxis}); + registry.add("Centrality/hCentAfterINEL", "Centrality after INEL cut;Centrality (%);Counts", + kTH1D, {centFineAxis}); + registry.add("Centrality/hCentAfterAll", "Centrality after all cuts;Centrality (%);Counts", + kTH1D, {centFineAxis}); + registry.add("Centrality/hCentVsMult", "Centrality vs Generated Multiplicity;Centrality (%);N_{ch}^{gen}", + kTH2D, {centFineAxis, nchAxis}); + registry.add("Centrality/hMultVsCent", "Generated Multiplicity vs Centrality;N_{ch}^{gen};Centrality (%)", + kTH2D, {nchAxis, centFineAxis}); + registry.add("Centrality/hCentVsVz", "Centrality vs Vertex Z;Centrality (%);V_{z} (cm)", + kTH2D, {centFineAxis, {40, -20, 20}}); + registry.add("Centrality/hRecoMultVsCent", "Reconstructed Track Multiplicity vs Centrality;Centrality (%);N_{tracks}^{reco}", + kTH2D, {centFineAxis, {100, 0, 100}}); + registry.add("Centrality/hVertexResVsCent", "Vertex Resolution vs Centrality;Centrality (%);V_{z} resolution (cm)", + kTH2D, {centFineAxis, {100, -1, 1}}); + registry.add("NchVsNPV", ";Nch; NPV;", kTH2D, {{npvAxis, nchAxis}}); + registry.add("ExcludedEvtVsNch", ";Nch;Entries;", kTH1F, {nchAxis}); + registry.add("ExcludedEvtVsNPV", ";NPV;Entries;", kTH1F, {npvAxis}); + + // ======================================================================== + // INEL CLASS HISTOGRAMS + // ======================================================================== + registry.add("INEL/hINELClass", "INEL Class for MC Collisions;INEL Class;Counts", + kTH1D, {{3, 0.5, 3.5}}); + auto hINEL = registry.get(HIST("INEL/hINELClass")); + hINEL->GetXaxis()->SetBinLabel(1, "INEL0"); + hINEL->GetXaxis()->SetBinLabel(2, "INEL>0"); + hINEL->GetXaxis()->SetBinLabel(3, "INEL>1"); + registry.add("INEL/hINELVsCent", "INEL Class vs Centrality;Centrality (%);INEL Class", + kTH2D, {centFineAxis, {3, 0.5, 3.5}}); + + // ======================================================================== + // CUT FLOW HISTOGRAMS + // ======================================================================== + registry.add("CutFlow/hCutStats", "Cut Statistics;Cut Stage;Counts", + kTH1D, {{6, 0.5, 6.5}}); + auto hCut = registry.get(HIST("CutFlow/hCutStats")); + hCut->GetXaxis()->SetBinLabel(1, "All reco events"); + hCut->GetXaxis()->SetBinLabel(2, "Has MC match"); + hCut->GetXaxis()->SetBinLabel(3, "Has centrality"); + hCut->GetXaxis()->SetBinLabel(4, "Pass vertex"); + hCut->GetXaxis()->SetBinLabel(5, "Pass INEL"); + hCut->GetXaxis()->SetBinLabel(6, "Selected"); + registry.add("CutFlow/hCentPerCut", "Centrality Distribution at Each Cut;Cut Stage;Centrality (%)", + kTH2D, {{6, 0.5, 6.5}, centFineAxis}); + registry.add("evsel", "Event selection", HistType::kTH1D, {{20, 0.5, 20.5}}); + auto hEvSel = registry.get(HIST("evsel")); + for (int i = 1; i <= NEvLabel; i++) { + hEvSel->GetXaxis()->SetBinLabel(i, Form("Step %d", i)); + } + + // ======================================================================== + // MC COLLISION HISTOGRAMS + // ======================================================================== + registry.add("MC/GenRecoCollisions", "Generated and Reconstructed MC Collisions", + kTH1D, {{10, 0.5, 10.5}}); + auto hColl = registry.get(HIST("MC/GenRecoCollisions")); + hColl->GetXaxis()->SetBinLabel(1, "Collisions generated"); + hColl->GetXaxis()->SetBinLabel(2, "Collisions reconstructed"); + hColl->GetXaxis()->SetBinLabel(3, "INEL>0"); + hColl->GetXaxis()->SetBinLabel(4, "INEL>1"); + registry.add("NchMCcentVsTVX", ";Passed(1.5) NOT Passed(0.5);", kTH2F, {{nchAxis, {2, 0, 2}}}); + registry.add("Centrality_AllRecoEvt", "Generated Events Irrespective of number of reconstructions;Centrality;Entries", kTH1F, {centAxis}); + registry.add("Centrality_WRecoEvt", "Generated Events With at least One Rec. Collision;Centrality;Entries", kTH1F, {centAxis}); + registry.add("Centrality_WRecoEvtWSelCri", "Generated Events With at least One Rec. Collision + Sel. criteria;Centrality;Entries", kTH1F, {centAxis}); + + // ======================================================================== + // 2D and 3D CORRELATION HISTOGRAMS + // ======================================================================== + registry.add("NchMCVsCent", "Gen. N_{ch} vs FT0M centrality (reco+sel. evts);FT0M Centrality (%);Gen. N_{ch}", + kTH2F, {{centAxis, nchAxis}}); + registry.add("NchMCVsCentVsPt", "pT vs Gen. N_{ch} vs FT0M centrality;FT0M Centrality (%);Gen. N_{ch};#it{p}_{T}", + kTH3F, {{centAxis, nchAxis, ptAxis}}); + registry.add("hEta", "Track eta;#eta;Counts", kTH1D, {{20, -0.8, 0.8}}); + registry.add("hPhi", "Track phi;#varphi (rad);Counts", kTH1D, {{64, 0, o2::constants::math::TwoPI}}); + registry.add("EtaVsPhi", ";#eta;#varphi;", kTH2F, {{50, -1.0, 1.0}, {100, 0, o2::constants::math::TwoPI}}); + + // ======================================================================== + // EVENT LOSS HISTOGRAMS + // ======================================================================== + registry.add("NchMC_AllGen", "EVENT LOSS denom.;Gen. N_{ch};Entries", kTH1F, {nchAxis}); + registry.add("NchMC_WithRecoEvt", "EVENT LOSS numer.;Gen. N_{ch};Entries", kTH1F, {nchAxis}); + registry.add("EventLoss/NgenFT0M", "EVENT LOSS denom. vs calibrated FT0M class;FT0M class (%);Entries", + kTH1F, {centFineAxis}); + registry.add("EventLoss/NgenWithRecoFT0M", "EVENT LOSS numer. vs calibrated FT0M class;FT0M class (%);Entries", + kTH1F, {centFineAxis}); + + registry.add("EventLoss/NgenInclusive", "EVENT LOSS denom., inclusive 0-100%;;Entries", kTH1F, {{1, 0, 1}}); + registry.add("EventLoss/NgenWithRecoInclusive", "EVENT LOSS numer., inclusive 0-100%;;Entries", kTH1F, {{1, 0, 1}}); + + registry.add("MC/EventLoss/NchGenerated", "Generated charged multiplicity;N_{ch}^{gen};Counts", kTH1D, {nchAxis}); + registry.add("MC/EventLoss/NchGenerated_PhysicsSelected", "Generated charged multiplicity (physics selected);N_{ch}^{gen};Counts", kTH1D, {nchAxis}); + registry.add("MC/EventLoss/NchGenerated_Reconstructed", "Generated charged multiplicity (reconstructed);N_{ch}^{gen};Counts", kTH1D, {nchAxis}); + registry.add("MC/EventLoss/GenMultVsCent", "Generated charged particles vs FT0M centrality;FT0M Centrality (%);N_{ch}^{gen}", kTH2D, {centAxis, nchAxis}); + registry.add("MC/EventLoss/GenMultVsCent_Selected", "Generated vs FT0M centrality (selected);FT0M Centrality (%);N_{ch}^{gen}", kTH2D, {centAxis, nchAxis}); + registry.add("MC/EventLoss/GenMultVsCent_Rejected", "Generated vs FT0M centrality (rejected);FT0M Centrality (%);N_{ch}^{gen}", kTH2D, {centAxis, nchAxis}); + registry.add("hEventLossBreakdown", "Event loss breakdown", kTH1D, {{4, 0.5, 4.5}}); + auto hLoss = registry.get(HIST("hEventLossBreakdown")); + hLoss->GetXaxis()->SetBinLabel(1, "Physics selected"); + hLoss->GetXaxis()->SetBinLabel(2, "Reconstructed"); + hLoss->GetXaxis()->SetBinLabel(3, "Selected"); + hLoss->GetXaxis()->SetBinLabel(4, "Final efficiency"); + + // ======================================================================== + // SIGNAL LOSS HISTOGRAMS + // ======================================================================== + const std::vector speciesNames = {"Pi", "Ka", "Pr", "All"}; + for (int i = 0; i < ParticleTypes; ++i) { + const std::string& name = speciesNames[i]; + registry.add(Form("Pt%sVsNchMC_AllGen", name.c_str()), + Form("SIGNAL LOSS denom. (%s): all gen. evts.;#it{p}_{T};Gen. N_{ch}", name.c_str()), + kTH2F, {{ptAxis, nchAxis}}); + registry.add(Form("Pt%sVsNchMC_WithRecoEvt", name.c_str()), + Form("SIGNAL LOSS numer. (%s): gen. evts. w/ >=1 reco+sel.;#it{p}_{T};Gen. N_{ch}", name.c_str()), + kTH2F, {{ptAxis, nchAxis}}); + } + + for (int i = 0; i < ParticleTypes; ++i) { + const std::string& name = speciesNames[i]; + registry.add(Form("SignalLoss/Pt%sVsFT0MClass_AllGen", name.c_str()), + Form("SIGNAL LOSS denom. (%s) vs calibrated FT0M class;#it{p}_{T};FT0M class (%%)", name.c_str()), + kTH2F, {{ptAxis, centFineAxis}}); + registry.add(Form("SignalLoss/Pt%sVsFT0MClass_WithRecoEvt", name.c_str()), + Form("SIGNAL LOSS numer. (%s) vs calibrated FT0M class;#it{p}_{T};FT0M class (%%)", name.c_str()), + kTH2F, {{ptAxis, centFineAxis}}); + } + + for (int i = 0; i < ParticleTypes; ++i) { + const std::string& name = speciesNames[i]; + registry.add(Form("SignalLoss/Pt%sInclusive_AllGen", name.c_str()), + Form("SIGNAL LOSS denom. (%s), inclusive 0-100%%;#it{p}_{T};Entries", name.c_str()), + kTH1F, {ptAxis}); + registry.add(Form("SignalLoss/Pt%sInclusive_WithRecoEvt", name.c_str()), + Form("SIGNAL LOSS numer. (%s), inclusive 0-100%%;#it{p}_{T};Entries", name.c_str()), + kTH1F, {ptAxis}); + } + + // ======================================================================== + // TRACKING EFFICIENCY HISTOGRAMS + // ======================================================================== + for (int i = 0; i < ParticleTypes; ++i) { + const std::string& name = speciesNames[i]; + registry.add(Form("Pt%sVsCentMC_WithRecoEvt", name.c_str()), + Form("EFF denom. (%s): gen pT in reco+sel. evts.;#it{p}_{T}^{gen};FT0M Cent. (%s)", name.c_str(), ")"), + kTH2F, {{ptAxis, centAxis}}); + registry.add(Form("Pt%sVsCent_WithRecoEvt", name.c_str()), + Form("EFF numer. (%s): reco primaries (gen pT);#it{p}_{T}^{gen};FT0M Cent. (%s)", name.c_str(), ")"), + kTH2F, {{ptAxis, centAxis}}); + registry.add(Form("PtGen%sVsNchMC_WithRecoEvt", name.c_str()), + Form("EFF denom. (%s) vs gen Nch;#it{p}_{T}^{gen};Gen. N_{ch}", name.c_str()), + kTH2F, {{ptAxis, nchAxis}}); + registry.add(Form("PtGen%sVsNchMC_RecoTrk", name.c_str()), + Form("EFF numer. (%s) vs gen Nch;#it{p}_{T}^{gen};Gen. N_{ch}", name.c_str()), + kTH2F, {{ptAxis, nchAxis}}); + } + + // ======================================================================== + // MC CLOSURE HISTOGRAMS + // ======================================================================== + registry.add("MCclosure_PtMCPiVsNchMC", "MC closure Pi (gen);#it{p}_{T}^{gen};Gen. N_{ch}", kTH2F, {{ptAxis, nchAxis}}); + registry.add("MCclosure_PtMCKaVsNchMC", "MC closure Ka (gen);#it{p}_{T}^{gen};Gen. N_{ch}", kTH2F, {{ptAxis, nchAxis}}); + registry.add("MCclosure_PtMCPrVsNchMC", "MC closure Pr (gen);#it{p}_{T}^{gen};Gen. N_{ch}", kTH2F, {{ptAxis, nchAxis}}); + registry.add("MCclosure_PtPiVsNchMC", "MC closure Pi (reco);#it{p}_{T}^{reco};Gen. N_{ch}", kTH2F, {{ptAxis, nchAxis}}); + registry.add("MCclosure_PtKaVsNchMC", "MC closure Ka (reco);#it{p}_{T}^{reco};Gen. N_{ch}", kTH2F, {{ptAxis, nchAxis}}); + registry.add("MCclosure_PtPrVsNchMC", "MC closure Pr (reco);#it{p}_{T}^{reco};Gen. N_{ch}", kTH2F, {{ptAxis, nchAxis}}); + registry.add("MC/GenPtVsNch", "Generated pT vs Multiplicity;#it{p}_{T};N_{ch}^{gen}", kTH2D, {ptAxis, nchAxis}); + registry.add("MC/GenPtVsNch_PhysicsSelected", "Generated pT vs Multiplicity (physics selected);#it{p}_{T};N_{ch}^{gen}", kTH2D, {ptAxis, nchAxis}); + + // ======================================================================== + // DCA HISTOGRAMS FOR PRIMARY FRACTION + // ======================================================================== + registry.add("dcaVsPtPi", "PRIMARY FRAC. (Pi) - primaries;#it{p}_{T};DCA_{xy};FT0M Cent.", + kTH3F, {{ptAxis, dcaXYAxis, centAxis}}); + registry.add("dcaVsPtPr", "PRIMARY FRAC. (Pr) - primaries;#it{p}_{T};DCA_{xy};FT0M Cent.", + kTH3F, {{ptAxis, dcaXYAxis, centAxis}}); + registry.add("dcaVsPtPiDec", "PRIMARY FRAC. (Pi) - sec. from decays;#it{p}_{T};DCA_{xy};FT0M Cent.", + kTH3F, {{ptAxis, dcaXYAxis, centAxis}}); + registry.add("dcaVsPtPrDec", "PRIMARY FRAC. (Pr) - sec. from decays;#it{p}_{T};DCA_{xy};FT0M Cent.", + kTH3F, {{ptAxis, dcaXYAxis, centAxis}}); + registry.add("dcaVsPtPiMat", "PRIMARY FRAC. (Pi) - sec. from material;#it{p}_{T};DCA_{xy};FT0M Cent.", + kTH3F, {{ptAxis, dcaXYAxis, centAxis}}); + registry.add("dcaVsPtPrMat", "PRIMARY FRAC. (Pr) - sec. from material;#it{p}_{T};DCA_{xy};FT0M Cent.", + kTH3F, {{ptAxis, dcaXYAxis, centAxis}}); + + // ======================================================================== + // MEASURED SPECTRA + // ======================================================================== + for (int i = 0; i < ParticleTypes; ++i) { + const std::string& name = speciesNames[i]; + registry.add(Form("Pt%sMeasuredVsCent", name.c_str()), + Form("Measured %s (PID);#it{p}_{T};FT0M Centrality (%s)", name.c_str(), ")"), + kTH2F, {{ptAxis, centAxis}}); + registry.add(Form("Pt%sMeasuredVsNch", name.c_str()), + Form("Measured %s (PID) vs gen Nch;#it{p}_{T};Gen. N_{ch}", name.c_str()), + kTH2F, {{ptAxis, nchAxis}}); + } + + // ======================================================================== + // TPC CLUSTER HISTOGRAMS + // ======================================================================== + registry.add("hNclFoundTPC", "Number of TPC found clusters;N_{cl, found};Counts", kTH1D, {nclAxis}); + registry.add("hNclPIDTPC", "Number of TPC PID clusters;N_{cl, PID};Counts", kTH1D, {nclAxis}); + registry.add("hNclFoundTPCvsPt", "TPC found clusters vs pT;#it{p}_{T};N_{cl,found}", kTH2D, {ptAxis, nclAxis}); + registry.add("hNclPIDTPCvsPt", "TPC PID clusters vs pT;#it{p}_{T};N_{cl,PID}", kTH2D, {ptAxis, nclAxis}); + + // ======================================================================== + // INCLUSIVE HISTOGRAMS + // ======================================================================== + registry.add("Inclusive/hPtPrimGenAll", "All generated primaries (no cuts);#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtPrimBadVertex", "Generated primaries (bad vertex);#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtPrimGen", "Generated primaries (after physics selection);#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtPrimRecoEv", "Generated primaries (reco events);#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtPrimGoodEv", "Generated primaries (good events);#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtNumEff", "Tracking efficiency numerator;#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtDenEff", "Tracking efficiency denominator;#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtAllReco", "All reconstructed tracks;#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtPrimReco", "Reconstructed primaries;#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtSecReco", "Reconstructed secondaries;#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtMeasured", "All measured tracks;#it{p}_{T};Counts", kTH1D, {ptAxis}); + registry.add("Inclusive/hPtMeasuredVsCent", "All measured tracks (PID) vs centrality;#it{p}_{T};FT0M Centrality (%s)", kTH2D, {ptAxis, centAxis}); + registry.add("Inclusive/hPtMeasuredVsMult", "All measured tracks vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + + // Inclusive vs Multiplicity + registry.add("Inclusive/hPtPrimGenAllVsMult", "All generated primaries vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtPrimBadVertexVsMult", "Generated primaries (bad vertex) vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtPrimGenVsMult", "Generated primaries (after phys sel) vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtPrimRecoEvVsMult", "Generated primaries (reco events) vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtPrimGoodEvVsMult", "Generated primaries (good events) vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtNumEffVsMult", "Tracking efficiency numerator vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtDenEffVsMult", "Tracking efficiency denominator vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtAllRecoVsMult", "All reconstructed tracks vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtPrimRecoVsMult", "Reconstructed primaries vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + registry.add("Inclusive/hPtSecRecoVsMult", "Reconstructed secondaries vs mult;#it{p}_{T};Mult Class (%)", kTH2D, {ptAxis, nchAxis}); + + // ======================================================================== + // PER-SPECIES INCLUSIVE HISTOGRAMS + // ======================================================================== + const std::array particleNames = {"Pion", "Kaon", "Proton"}; + const std::array particleSymbols = {"#pi^{#pm}", "K^{#pm}", "p+#bar{p}"}; + + for (int iSpecies = 0; iSpecies < ParticleMinusOne; ++iSpecies) { + const auto& name = particleNames[iSpecies]; + const auto& symbol = particleSymbols[iSpecies]; + + registry.add(Form("%s/hPtPrimGenAll", name.c_str()), + Form("All generated %s (no cuts);#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtPrimBadVertex", name.c_str()), + Form("Generated %s (bad vertex);#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtPrimGen", name.c_str()), + Form("Generated %s (after physics selection);#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtPrimRecoEv", name.c_str()), + Form("Generated %s (reco events);#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtPrimGoodEv", name.c_str()), + Form("Generated %s (good events);#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtNumEff", name.c_str()), + Form("%s tracking efficiency numerator;#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtDenEff", name.c_str()), + Form("%s tracking efficiency denominator;#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtAllReco", name.c_str()), + Form("All reconstructed %s;#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtPrimReco", name.c_str()), + Form("Reconstructed primary %s;#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtSecReco", name.c_str()), + Form("Reconstructed secondary %s;#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + registry.add(Form("%s/hPtMeasured", name.c_str()), + Form("Measured %s;#it{p}_{T};Counts", symbol.c_str()), kTH1D, {ptAxis}); + + // Per-species vs multiplicity + registry.add(Form("%s/hPtPrimGenAllVsMult", name.c_str()), + Form("All generated %s vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtPrimBadVertexVsMult", name.c_str()), + Form("Generated %s (bad vertex) vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtPrimGenVsMult", name.c_str()), + Form("Generated %s (after phys sel) vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtPrimRecoEvVsMult", name.c_str()), + Form("Generated %s (reco events) vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtPrimGoodEvVsMult", name.c_str()), + Form("Generated %s (good events) vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtNumEffVsMult", name.c_str()), + Form("%s tracking eff numerator vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtDenEffVsMult", name.c_str()), + Form("%s tracking eff denominator vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtAllRecoVsMult", name.c_str()), + Form("All reconstructed %s vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtPrimRecoVsMult", name.c_str()), + Form("Reconstructed primary %s vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtSecRecoVsMult", name.c_str()), + Form("Reconstructed secondary %s vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + registry.add(Form("%s/hPtMeasuredVsMult", name.c_str()), + Form("Measured %s vs mult;#it{p}_{T};Mult Class (%s)", symbol.c_str(), ")"), kTH2D, {ptAxis, nchAxis}); + } + + // ======================================================================== + // PID HISTOGRAMS + // ======================================================================== + registry.add("PtResolution", "pT resolution;#it{p}_{T}^{gen};(p_{T}^{reco}-p_{T}^{gen})/p_{T}^{gen}", + kTH2F, {{ptAxis, {100, -1.0, 1.0}}}); + + if (enablePIDHistograms) { + registry.add("Pion/hNsigmaTPC", "Pion TPC n#sigma;#it{p}_{T};n#sigma_{TPC}", + kTH2D, {{ptAxis, {200, -10, 10}}}); + registry.add("Kaon/hNsigmaTPC", "Kaon TPC n#sigma;#it{p}_{T};n#sigma_{TPC}", + kTH2D, {{ptAxis, {200, -10, 10}}}); + registry.add("Proton/hNsigmaTPC", "Proton TPC n#sigma;#it{p}_{T};n#sigma_{TPC}", + kTH2D, {{ptAxis, {200, -10, 10}}}); + } + + // ======================================================================== + // PHI CUT MONITORING + // ======================================================================== + if (applyPhiCut.value) { + registry.add("PhiCut/hPtVsPhiPrimeBefore", "pT vs #phi' before cut;p_{T};#phi'", + kTH2F, {{100, 0, 10}, {100, 0, 0.4}}); + registry.add("PhiCut/hPtVsPhiPrimeAfter", "pT vs #phi' after cut;p_{T};#phi'", + kTH2F, {{100, 0, 10}, {100, 0, 0.4}}); + } + // ======================================================================== + // DCA CUT MONITORING + // ======================================================================== + + registry.add("hDCAxyVsPt_before", "DCAxy vs pT before cut;#it{p}_{T} (GeV/c);DCA_{xy} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registry.add("hDCAzVsPt_before", "DCAz vs pT before cut;#it{p}_{T} (GeV/c);DCA_{z} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registry.add("hDCAxyVsPt_after", "DCAxy vs pT after cut;#it{p}_{T} (GeV/c);DCA_{xy} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + registry.add("hDCAzVsPt_after", "DCAz vs pT after cut;#it{p}_{T} (GeV/c);DCA_{z} (cm)", + HistType::kTH2F, {{ptAxis}, {dcaAxis}}); + // ======================================================================== + // CALIBRATION HISTOGRAMS + // ======================================================================== + registry.add("Calibration/hRawMultiplicity", "Raw multiplicity distribution;N_{ch};Events", + kTH1D, {{150, 0, 150}}); + + // ======================================================================== + // DEDX VS MOMENTUM HISTOGRAMS + // ======================================================================== + const std::array centNames = { + "Cent0_1", "Cent1_5", "Cent5_10", "Cent10_15", "Cent15_20", + "Cent20_30", "Cent30_40", "Cent40_50", "Cent50_70", "Cent70_100", "MB"}; + const std::array v0Names = { + "all", "Pi_v0", "Pr_v0", "El_v0"}; + for (int i = 0; i < ParticleTypes; ++i) { + const auto& part = v0Names[i]; + registry.add(Form("DedxVsMomentum/dEdx_vs_Momentum_%s_Pos", part.c_str()), + "dE/dx vs Momentum Positive", kTH3F, {{pAxis}, {dedxAxis}, {etaAxis}}); + registry.add(Form("DedxVsMomentum/dEdx_vs_Momentum_%s_Neg", part.c_str()), + "dE/dx vs Momentum Negative", kTH3F, {{pAxis}, {dedxAxis}, {etaAxis}}); + } + for (int i = 0; i < CentralityClasses; ++i) { + const auto& cent = centNames[i]; + hDedxVsMomentumVsCentPos[i] = registry.add(Form("DedxVsMomentum/dEdx_vs_Momentum_%s_Pos", cent.c_str()), "dE/dx vs Momentum Positive", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + hDedxVsMomentumVsCentNeg[i] = registry.add(Form("DedxVsMomentum/dEdx_vs_Momentum_%s_Neg", cent.c_str()), "dE/dx vs Momentum Negative", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + } + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hDedxVspTMomentumVsCent[i] = registry.add(Form("DedxVsMomentum/dEdx_vs_pT_%s", cent.c_str()), "dE/dx vs pT", HistType::kTH3F, {{ptAxis}, {dedxAxis}, {etaAxis}}); + } + // ======================================================================== + // RESPONSE MATRIX HISTOGRAMS + // ======================================================================== + for (int i = 0; i < ResponseMatrixTypes; ++i) { + registry.add(("ResponseMatrix/" + std::string(EtavspvspTPosPart[i])).c_str(), + "eta vs pT vs p Positive", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxis}}); + registry.add(("ResponseMatrix/" + std::string(EtavspvspTNegPart[i])).c_str(), + "eta vs pT vs p Negative", HistType::kTH3F, + {{etaAxis}, {ptAxis}, {pAxis}}); + } + // ======================================================================== + // FINNER BINNING HISTOGRAMS + // ======================================================================== + for (int i = 0; i < CentralityClasses + 1; ++i) { + const auto& cent = centNames[i]; + hMomentumVsEtaPos[i] = registry.add(Form("Binning/p_vs_eta_%s_Pos", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hMomentumVsEtaNeg[i] = registry.add(Form("Binning/p_vs_eta_%s_Neg", cent.c_str()), "p vs eta", HistType::kTH2F, {{etaAxis}, {pFineAxis}}); + hpTVsEtaPos[i] = registry.add(Form("Binning/pT_vs_eta_%s_Pos", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + hpTVsEtaNeg[i] = registry.add(Form("Binning/pT_vs_eta_%s_Neg", cent.c_str()), "pT vs eta", HistType::kTH2F, {{etaAxis}, {pTFineAxis}}); + } + + // ======================================================================== + // PARTICLE FRACTIONS HISTOGRAMS + // ======================================================================== + const std::array partName = {"Pion", "Kaon", "Proton", "Electron", "Muon"}; + for (int ic = 0; ic < NCentHists + 1; ++ic) { + const auto& cent = centNames[ic]; + hTotalMomPosCent[ic] = registryFrac.add( + Form("ParticleFractions/hTotalCountsVsMomentumPos_%s", cent.c_str()), + "Total counts vs momentum", HistType::kTH2D, {{etaAxis}, {pAxis}}); + hTotalMomNegCent[ic] = registryFrac.add( + Form("ParticleFractions/hTotalCountsVsMomentumNeg_%s", cent.c_str()), + "Total counts vs momentum", HistType::kTH2D, {{etaAxis}, {pAxis}}); + hTotalPtPosCent[ic] = registryFrac.add( + Form("ParticleFractions/hTotalCountsVsPtPos_%s", cent.c_str()), + "Total counts vs pT", HistType::kTH2D, {{etaAxis}, {ptAxis}}); + hTotalPtNegCent[ic] = registryFrac.add( + Form("ParticleFractions/hTotalCountsVsPtNeg_%s", cent.c_str()), + "Total counts vs pT", HistType::kTH2D, {{etaAxis}, {ptAxis}}); + + for (int ip = 0; ip < NPartHists; ++ip) { + const auto& part = partName[ip]; + hFracMomPosCent[ip][ic] = registryFrac.add( + Form("ParticleFractions/hFractionVsMomentum_%s_Pos_%s", part.c_str(), cent.c_str()), + "Fraction vs momentum", HistType::kTH2D, {{etaAxis}, {pAxis}}); + hFracMomNegCent[ip][ic] = registryFrac.add( + Form("ParticleFractions/hFractionVsMomentum_%s_Neg_%s", part.c_str(), cent.c_str()), + "Fraction vs momentum", HistType::kTH2D, {{etaAxis}, {pAxis}}); + hFracPtPosCent[ip][ic] = registryFrac.add( + Form("ParticleFractions/hFractionVsPt_%s_Pos_%s", part.c_str(), cent.c_str()), + "Fraction vs pT", HistType::kTH2D, {{etaAxis}, {ptAxis}}); + hFracPtNegCent[ip][ic] = registryFrac.add( + Form("ParticleFractions/hFractionVsPt_%s_Neg_%s", part.c_str(), cent.c_str()), + "Fraction vs pT", HistType::kTH2D, {{etaAxis}, {ptAxis}}); + } + } + + LOG(info) << "=== MultiplicityPt initialized with ALL histograms (including dE/dx) ==="; + LOG(info) << "tpcNchAcceptance = " << tpcNchAcceptance.value; + LOG(info) << "cfgINELCut = " << cfgINELCut.value; + LOG(info) << "selTVXMC = " << selTVXMC.value; + LOG(info) << "applyPhiCut = " << applyPhiCut.value; + LOG(info) << "applyGoodITS = " << nGoodITS.value; + } + + // Get magnetic field from CCDB int getMagneticField(uint64_t timestamp) { static o2::parameters::GRPMagField* grpo = nullptr; @@ -144,690 +786,908 @@ struct MultiplicityPt { LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); return 0; } - LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); } return grpo->getNominalL3Field(); } + // Transform phi for phi cut float getTransformedPhi(const float phi, const int charge, const float magField) const { float transformedPhi = phi; - if (magField < 0) + if (magField < 0) { transformedPhi = o2::constants::math::TwoPI - transformedPhi; - if (charge < 0) + } + if (charge < 0) { transformedPhi = o2::constants::math::TwoPI - transformedPhi; + } transformedPhi += o2::constants::math::PI / 18.0f; transformedPhi = std::fmod(transformedPhi, o2::constants::math::PI / 9.0f); return transformedPhi; } - template - bool passedPhiCut(const TrackType& track, float magField) const + // Check phi cut + template + bool passedPhiCut(const T& track, float magField) const { if (!applyPhiCut.value) return true; if (track.pt() < pTthresholdPhiCut.value) return true; - float phi = track.phi(); - int charge = track.sign(); - - if (magField < 0) - phi = o2::constants::math::TwoPI - phi; - if (charge < 0) - phi = o2::constants::math::TwoPI - phi; - phi += o2::constants::math::PI / 18.0f; - phi = std::fmod(phi, o2::constants::math::PI / 9.0f); + float phiPrime = getTransformedPhi(track.phi(), track.sign(), magField); - if (phi < fphiCutHigh->Eval(track.pt()) && phi > fphiCutLow->Eval(track.pt())) + if (phiPrime < fphiCutHigh->Eval(track.pt()) && phiPrime > fphiCutLow->Eval(track.pt())) { return false; - return true; - } - - template - int countGeneratedChargedPrimaries(const ParticleContainer& particles, float etaMax, float ptMin) const - { - int count = 0; - for (const auto& particle : particles) { - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (!pdgParticle || pdgParticle->Charge() == 0.) - continue; - if (!particle.isPhysicalPrimary()) - continue; - if (std::abs(particle.eta()) > etaMax) - continue; - if (particle.pt() < ptMin) - continue; - count++; } - return count; - } - - template - bool passedNClTPCFoundCut(const T& trk) const - { - return !nClTPCFoundCut.value || trk.tpcNClsFound() >= minTPCNClsFound.value; + return true; } template - bool passedNClTPCPIDCut(const T& trk) const + bool passesTrackSelectionNoDCA(const T& track) const { - return !nClTPCPIDCut.value || trk.tpcNClsPID() >= minTPCNClsPID.value; - } + if (track.eta() < cfgCutEtaMin.value || track.eta() > cfgCutEtaMax.value) + return false; + if (track.pt() < cfgTrkLowPtCut.value) + return false; + if (track.tpcChi2NCl() < minChi2PerClusterTPC.value || track.tpcChi2NCl() > maxChi2PerClusterTPC.value) + return false; - template - bool passesCutWoDCA(TrackType const& track) const - { if (useCustomTrackCuts.value) { for (int i = 0; i < static_cast(TrackSelection::TrackCuts::kNCuts); i++) { - if (i == static_cast(TrackSelection::TrackCuts::kDCAxy) || - i == static_cast(TrackSelection::TrackCuts::kDCAz)) + if (i == static_cast(TrackSelection::TrackCuts::kDCAxy)) continue; if (!customTrackCuts.IsSelected(track, static_cast(i))) return false; } - return true; + } else { + if (!track.isGlobalTrackWoDCA()) + return false; } - return track.isGlobalTrackWoDCA(); + + if (nClTPCFoundCut.value && track.tpcNClsFound() < minTPCNClsFound.value) + return false; + if (nClTPCPIDCut.value && track.tpcNClsPID() < minTPCNClsPID.value) + return false; + + return true; } - template - bool passesDCAxyCut(TrackType const& track) const + // DCA xy cut + template + bool passesDCAxyCut(const T1& track) const { - if (useCustomTrackCuts.value) { - if (!passesCutWoDCA(track)) - return false; - constexpr float DcaXYConst = 0.0105f; - constexpr float DcaXYPtScale = 0.0350f; - constexpr float DcaXYPtPower = 1.1f; - const float maxDcaXY = maxDcaXYFactor.value * (DcaXYConst + DcaXYPtScale / std::pow(track.pt(), DcaXYPtPower)); - return std::abs(track.dcaXY()) <= maxDcaXY; - } - return track.isGlobalTrack(); + const float maxDcaXY = nSigmaDCAxy.value * (dcaXYp0.value + dcaXYp1.value / std::pow(track.pt(), dcaXYp2.value)) / 3.0; + return std::abs(track.dcaXY()) < maxDcaXY; + } + // DCA z cut + template + bool passesDCAzCut(const T1& track) const + { + const float maxiDcaZ = nSigmaDCAz.value * (dcaZp0.value + dcaZp1.value / std::pow(track.pt(), dcaZp2.value)) / 3.0; + return std::abs(track.dcaZ()) < maxiDcaZ; } - template - bool passesTrackSelection(TrackType const& track, float magField = 0) const + // Full track selection + template + bool passesTrackSelection(const T& track) const { - if (track.eta() < cfgCutEtaMin.value || track.eta() > cfgCutEtaMax.value) - return false; - if (track.tpcChi2NCl() < minChi2PerClusterTPC.value || track.tpcChi2NCl() > maxChi2PerClusterTPC.value) + return passesTrackSelectionNoDCA(track) && passesDCAxyCut(track) && passesDCAzCut(track); + } + + template + bool isEventSelected(const C& col) const + { + if (askForCustomTVX.value) { + if (!col.selection_bit(aod::evsel::kIsTriggerTVX)) + return false; + } else { + if (!col.sel8()) + return false; + } + if (removeITSROFrameBorder.value && !col.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; - if (!passesCutWoDCA(track)) + if (removeNoSameBunchPileup.value && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) return false; - if (!passesDCAxyCut(track)) + if (requireIsGoodZvtxFT0vsPV.value && !col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) return false; - if (!passedNClTPCFoundCut(track)) + if (requireIsVertexITSTPC.value && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) return false; - if (!passedNClTPCPIDCut(track)) + if (removeNoTimeFrameBorder.value && !col.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; - if (!passedPhiCut(track, magField)) + if (std::abs(col.posZ()) > cfgCutVertex.value) return false; return true; } - template - bool passesPIDSelection(const TrackType& track, int species) const + template + bool isEventSelectedMC(const C& col) const { - float nsigmaTPC = 0.f; - float cutValue = 0.f; + if (std::abs(col.posZ()) > cfgCutVertex.value) + return false; + return true; + } - if (species == PartPion) { - nsigmaTPC = track.tpcNSigmaPi(); - cutValue = cfgCutNsigmaPi.value; - } else if (species == PartKaon) { - nsigmaTPC = track.tpcNSigmaKa(); - cutValue = cfgCutNsigmaKa.value; - } else if (species == PartProton) { - nsigmaTPC = track.tpcNSigmaPr(); - cutValue = cfgCutNsigmaPr.value; + template + int bestPIDHypothesis(const T& track) const + { + const float nsPi = std::abs(track.tpcNSigmaPi()); + const float nsKa = std::abs(track.tpcNSigmaKa()); + const float nsPr = std::abs(track.tpcNSigmaPr()); + float best = 999.f; + int id = -1; + if (nsPi < cfgCutNsigmaPi.value && nsPi < best) { + best = nsPi; + id = kPion; } - - return std::abs(nsigmaTPC) < cutValue; + if (nsKa < cfgCutNsigmaKa.value && nsKa < best) { + best = nsKa; + id = kKaon; + } + if (nsPr < cfgCutNsigmaPr.value && nsPr < best) { + best = nsPr; + id = kProton; + } + return id; } - template - bool isGoodPrimary(ParticleType const& particle) const + int countGeneratedChargedPrimaries(const aod::McParticles& particles, float etaMax) const { - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (!pdgParticle || pdgParticle->Charge() == 0.) - return false; - if (!particle.isPhysicalPrimary()) - return false; - if (std::abs(particle.eta()) >= cfgCutEtaMax.value) - return false; - if (particle.pt() < cfgTrkLowPtCut.value) - return false; - return true; + int count = 0; + for (const auto& particle : particles) { + auto* pdgPart = pdg->GetParticle(particle.pdgCode()); + if (!pdgPart || std::abs(pdgPart->Charge()) < MinCharge) + continue; + if (!particle.isPhysicalPrimary()) + continue; + if (std::abs(particle.eta()) > etaMax) + continue; + if (particle.pt() < cfgTrkLowPtCut.value) + continue; + count++; + } + return count; } - void processData(CollisionTableData::iterator const& collision, TrackTableData const& tracks, BCsRun3 const& bcs); - PROCESS_SWITCH(MultiplicityPt, processData, "process data", false); + void processSim(McCollisionsCent::iterator const& mcCollision, + soa::SmallGroups const& collisions, + aod::McParticles const& mcParticles, + TracksMC const& tracksMC, + BCsRun3 const& /*bcs*/) + { + registry.fill(HIST("EventCounter"), kAllGen); - void processMC(TrackTableMC const& tracks, aod::McParticles const& particles, aod::McCollisions const& mcCollisions, - RecoCollisions const& collisions, aod::McCollisionLabels const& labels, aod::McCentFT0Ms const& centTable, BCsRun3 const& bcs); - PROCESS_SWITCH(MultiplicityPt, processMC, "process MC", true); + const float genCentFT0M = mcCollision.centFT0M(); - void init(InitContext const&); - void endOfStream(EndOfStreamContext& /*eos*/) {} -}; + int nChFT0A = 0, nChFT0C = 0; + int nChMCEta = 0; + std::vector particlePtBySpecies[4]; // Pi, Ka, Pr, All + std::vector particlePtAll; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -} + // Store particle pT for MC closure + std::vector mcPiPt, mcKaPt, mcPrPt; -void MultiplicityPt::init(InitContext const&) -{ - LOG(info) << "=================================================="; - LOG(info) << "Initializing MultiplicityPt task - FULL CORRECTION FACTORS"; - LOG(info) << "=================================================="; + for (const auto& particle : mcParticles) { + auto* pdgPart = pdg->GetParticle(particle.pdgCode()); + if (!pdgPart || std::abs(pdgPart->Charge()) < MinCharge) + continue; + if (!particle.isPhysicalPrimary()) + continue; - if (applyPhiCut.value) { - fphiCutLow = new TF1("StandardPhiCutLow", Form("%f/x/x+pi/18.0-%f", phiCutLowParam1.value, phiCutLowParam2.value), 0, 50); - fphiCutHigh = new TF1("StandardPhiCutHigh", Form("%f/x+pi/18.0+%f", phiCutHighParam1.value, phiCutHighParam2.value), 0, 50); - } + const float eta = particle.eta(); + const float pt = particle.pt(); + + if (eta > MinFT0A && eta < MaxFT0A) + nChFT0A++; + if (eta > MinFT0C && eta < MaxFT0C) + nChFT0C++; + + if (std::abs(eta) < tpcNchAcceptance.value) { + nChMCEta++; + + const int absPDG = std::abs(particle.pdgCode()); + if (absPDG == PDG_t::kPiPlus) { + particlePtBySpecies[kPion].push_back(pt); + mcPiPt.push_back(pt); + } else if (absPDG == PDG_t::kKPlus) { + particlePtBySpecies[kKaon].push_back(pt); + mcKaPt.push_back(pt); + } else if (absPDG == PDG_t::kProton) { + particlePtBySpecies[kProton].push_back(pt); + mcPrPt.push_back(pt); + } + particlePtAll.push_back(pt); + } + } - if (useCustomTrackCuts.value) { - customTrackCuts = getGlobalTrackSelectionRun3ITSMatch(itsPattern.value); - customTrackCuts.SetRequireITSRefit(requireITS.value); - customTrackCuts.SetRequireTPCRefit(requireTPC.value); - customTrackCuts.SetRequireGoldenChi2(requireGoldenChi2.value); - customTrackCuts.SetMaxChi2PerClusterTPC(maxChi2PerClusterTPC.value); - customTrackCuts.SetMaxChi2PerClusterITS(maxChi2PerClusterITS.value); - customTrackCuts.SetMinNCrossedRowsTPC(minNCrossedRowsTPC.value); - customTrackCuts.SetMinNClustersTPC(minTPCNClsFound.value); - customTrackCuts.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); - customTrackCuts.SetMaxDcaXYPtDep([](float /*pt*/) { return 10000.f; }); - customTrackCuts.SetMaxDcaZ(maxDcaZ.value); - customTrackCuts.print(); - } + // Fill NchMCcentVsTVX before TVX selection + registry.fill(HIST("QA/genCentFT0M_raw"), genCentFT0M); + registry.fill(HIST("NchMCcentVsTVX"), nChMCEta, 0.5); - // Axis definitions - ConfigurableAxis ptBinning{"ptBinning", {VARIABLE_WIDTH, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 18.0, 20.0}, "pT bin limits"}; - AxisSpec ptAxis = {ptBinning, "#it{p}_{T} (GeV/#it{c})"}; - - std::vector centBinningStd = {0., 1., 5., 10., 15., 20., 30., 40., 50., 60., 70., 80., 90., 100.}; - AxisSpec centAxis = {centBinningStd, "FT0M Centrality (%)"}; - - std::vector centBinningFine; - for (int i = 0; i <= CentBinMax; i++) - centBinningFine.push_back(static_cast(i)); - AxisSpec centFineAxis = {centBinningFine, "FT0M Centrality (%)"}; - - std::vector multBins; - for (int i = 0; i <= MultBinMax; i++) - multBins.push_back(static_cast(i)); - AxisSpec multAxis = {multBins, "N_{ch}^{gen} (|#eta|<0.8)"}; - - std::vector recoMultBins; - for (int i = 0; i <= RecMultBinMax; i++) - recoMultBins.push_back(static_cast(i)); - AxisSpec recoMultAxis = {recoMultBins, "N_{ch}^{reco}"}; - - ue.add("Centrality/hCentRaw", "Raw FT0M Centrality;Centrality (%);Counts", HistType::kTH1D, {centFineAxis}); - ue.add("Centrality/hCentAfterVtx", "Centrality after vertex cut;Centrality (%);Counts", HistType::kTH1D, {centFineAxis}); - ue.add("Centrality/hCentAfterAll", "Centrality after all cuts;Centrality (%);Counts", HistType::kTH1D, {centFineAxis}); - ue.add("Centrality/hCentVsMult", "Centrality vs Generated Multiplicity;Centrality (%);N_{ch}^{gen}", HistType::kTH2D, {centFineAxis, multAxis}); - ue.add("Centrality/hMultVsCent", "Generated Multiplicity vs Centrality;N_{ch}^{gen};Centrality (%)", HistType::kTH2D, {multAxis, centFineAxis}); - ue.add("Centrality/hRecoMultVsCent", "Reconstructed Track Multiplicity vs Centrality;Centrality (%);N_{tracks}^{reco}", HistType::kTH2D, {centFineAxis, recoMultAxis}); - - ue.add("CutFlow/hCutStats", "Cut Statistics;Cut Stage;Counts", HistType::kTH1D, {{5, 0.5, 5.5}}); - auto hCut = ue.get(HIST("CutFlow/hCutStats")); - hCut->GetXaxis()->SetBinLabel(1, "All collisions"); - hCut->GetXaxis()->SetBinLabel(2, "Has MC match"); - hCut->GetXaxis()->SetBinLabel(3, "Has centrality"); - hCut->GetXaxis()->SetBinLabel(4, "Pass vertex"); - hCut->GetXaxis()->SetBinLabel(5, "Selected"); - - ue.add("hEventLossBreakdown", "Event loss breakdown", HistType::kTH1D, {{3, 0.5, 3.5}}); - auto hLoss = ue.get(HIST("hEventLossBreakdown")); - hLoss->GetXaxis()->SetBinLabel(1, "Physics selected"); - hLoss->GetXaxis()->SetBinLabel(2, "Reconstructed"); - hLoss->GetXaxis()->SetBinLabel(3, "Selected"); - - ue.add("MC/GenRecoCollisions", "Generated and Reconstructed MC Collisions", HistType::kTH1D, {{5, 0.5, 5.5}}); - auto hColl = ue.get(HIST("MC/GenRecoCollisions")); - hColl->GetXaxis()->SetBinLabel(1, "Collisions generated"); - hColl->GetXaxis()->SetBinLabel(2, "INEL>0"); - hColl->GetXaxis()->SetBinLabel(3, "INEL>1"); - hColl->GetXaxis()->SetBinLabel(4, "Reconstructed"); - hColl->GetXaxis()->SetBinLabel(5, "Selected"); - - ue.add("MC/EventLoss/GenMultVsCent", "Generated charged particles vs FT0M centrality;FT0M Centrality (%);N_{ch}^{gen}", HistType::kTH2D, {centAxis, multAxis}); - ue.add("MC/EventLoss/GenMultVsCent_Selected", "Generated vs FT0M centrality (selected events);FT0M Centrality (%);N_{ch}^{gen}", HistType::kTH2D, {centAxis, multAxis}); - - ue.add("Inclusive/hPtGen", "Generated primaries (physics selected);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Inclusive/hPtReco", "All reconstructed tracks (track selection only);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Inclusive/hPtPrimReco", "Reconstructed primaries (MC matched);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Inclusive/hPtSecReco", "Reconstructed secondaries (MC matched);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - - ue.add("Pion/hPtPrimGenAll", "All generated #pi^{#pm} (no cuts);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Pion/hPtGenINEL", "Generated #pi^{#pm} in INEL>0 events (|#eta|<0.8, p_{T}>0.15);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Pion/hPtGenRecoEvent", "Generated #pi^{#pm} in reconstructed events (|#eta|<0.8, p_{T}>0.15);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Pion/hPtGen", "Generated #pi^{#pm} (physics selected);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Pion/hPtReco", "Reconstructed #pi^{#pm} (MC matched, any status);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Pion/hPtPrimReco", "Reconstructed primary #pi^{#pm} (MC matched);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Pion/hPtMeasured", "Measured #pi^{#pm} (PID selected);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - - ue.add("Pion/hPtGenRecoEvent_Mult", "Generated #pi^{#pm} in reconstructed events vs multiplicity;#it{p}_{T} (GeV/#it{c});N_{ch}^{gen}", HistType::kTH2D, {ptAxis, multAxis}); - ue.add("Pion/hPtGenINEL_Mult", "Generated #pi^{#pm} in INEL>0 events vs multiplicity;#it{p}_{T} (GeV/#it{c});N_{ch}^{gen}", HistType::kTH2D, {ptAxis, multAxis}); - - if (enablePIDHistograms) { - ue.add("Pion/hNsigmaTPC", "TPC n#sigma #pi^{#pm};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2D, {ptAxis, {200, -10, 10}}); - } + const bool passGenTVX = !selTVXMC.value || (nChFT0A > 0 || nChFT0C > 0); + if (passGenTVX) { + registry.fill(HIST("NchMCcentVsTVX"), nChMCEta, 1.5); + registry.fill(HIST("EventCounter"), kTVXequiv); + } - ue.add("Kaon/hPtPrimGenAll", "All generated K^{#pm} (no cuts);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Kaon/hPtGenINEL", "Generated K^{#pm} in INEL>0 events (|#eta|<0.8, p_{T}>0.15);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Kaon/hPtGenRecoEvent", "Generated K^{#pm} in reconstructed events (|#eta|<0.8, p_{T}>0.15);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Kaon/hPtGen", "Generated K^{#pm} (physics selected);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Kaon/hPtReco", "Reconstructed K^{#pm} (MC matched, any status);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Kaon/hPtPrimReco", "Reconstructed primary K^{#pm} (MC matched);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Kaon/hPtMeasured", "Measured K^{#pm} (PID selected);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); + if (isZvtxPosSelMC.value && std::abs(mcCollision.posZ()) > cfgCutVertex.value) + return; + registry.fill(HIST("EventCounter"), kVtxZ); - ue.add("Kaon/hPtGenRecoEvent_Mult", "Generated K^{#pm} in reconstructed events vs multiplicity;#it{p}_{T} (GeV/#it{c});N_{ch}^{gen}", HistType::kTH2D, {ptAxis, multAxis}); - ue.add("Kaon/hPtGenINEL_Mult", "Generated K^{#pm} in INEL>0 events vs multiplicity;#it{p}_{T} (GeV/#it{c});N_{ch}^{gen}", HistType::kTH2D, {ptAxis, multAxis}); + if (cfgINELCut.value == INELgt0 && !o2::pwglf::isINELgt0mc(mcParticles, pdg)) + return; + if (cfgINELCut.value == INELgt1 && !o2::pwglf::isINELgt1mc(mcParticles, pdg)) + return; + registry.fill(HIST("EventCounter"), kINELgt0); - if (enablePIDHistograms) { - ue.add("Kaon/hNsigmaTPC", "TPC n#sigma K^{#pm};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2D, {ptAxis, {200, -10, 10}}); - } + registry.fill(HIST("EventLoss/NgenInclusive"), 0.5); - ue.add("Proton/hPtPrimGenAll", "All generated p+#bar{p} (no cuts);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Proton/hPtGenINEL", "Generated p+#bar{p} in INEL>0 events (|#eta|<0.8, p_{T}>0.15);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Proton/hPtGenRecoEvent", "Generated p+#bar{p} in reconstructed events (|#eta|<0.8, p_{T}>0.15);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Proton/hPtGen", "Generated p+#bar{p} (physics selected);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Proton/hPtReco", "Reconstructed p+#bar{p} (MC matched, any status);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Proton/hPtPrimReco", "Reconstructed primary p+#bar{p} (MC matched);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); - ue.add("Proton/hPtMeasured", "Measured p+#bar{p} (PID selected);#it{p}_{T} (GeV/#it{c});Counts", HistType::kTH1D, {ptAxis}); + for (const float& pt : particlePtBySpecies[kPion]) { + registry.fill(HIST("SignalLoss/PtPiInclusive_AllGen"), pt); + } + for (const float& pt : particlePtBySpecies[kKaon]) { + registry.fill(HIST("SignalLoss/PtKaInclusive_AllGen"), pt); + } + for (const float& pt : particlePtBySpecies[kProton]) { + registry.fill(HIST("SignalLoss/PtPrInclusive_AllGen"), pt); + } + for (const float& pt : particlePtAll) { + registry.fill(HIST("SignalLoss/PtAllInclusive_AllGen"), pt); + } - ue.add("Proton/hPtGenRecoEvent_Mult", "Generated p+#bar{p} in reconstructed events vs multiplicity;#it{p}_{T} (GeV/#it{c});N_{ch}^{gen}", HistType::kTH2D, {ptAxis, multAxis}); - ue.add("Proton/hPtGenINEL_Mult", "Generated p+#bar{p} in INEL>0 events vs multiplicity;#it{p}_{T} (GeV/#it{c});N_{ch}^{gen}", HistType::kTH2D, {ptAxis, multAxis}); + const float nchF = static_cast(nChMCEta); - if (enablePIDHistograms) { - ue.add("Proton/hNsigmaTPC", "TPC n#sigma p+#bar{p};#it{p}_{T} (GeV/#it{c});n#sigma_{TPC}", HistType::kTH2D, {ptAxis, {200, -10, 10}}); - } + // Fill event loss denominator and MC closure + registry.fill(HIST("NchMC_AllGen"), nchF); + registry.fill(HIST("MC/EventLoss/NchGenerated"), nchF); - ue.add("hEventsReco_Cent", "Reconstructed events vs centrality;FT0M Centrality (%);Counts", HistType::kTH1D, {centAxis}); - ue.add("hEventsINEL_Cent", "INEL>0 events vs centrality;FT0M Centrality (%);Counts", HistType::kTH1D, {centAxis}); + if (passGenTVX) { + registry.fill(HIST("EventLoss/NgenFT0M"), genCentFT0M); + } - ue.add("hNclFoundTPC", "Number of TPC found clusters", HistType::kTH1D, {{200, 0, 200}}); - ue.add("hNclPIDTPC", "Number of TPC PID clusters", HistType::kTH1D, {{200, 0, 200}}); - ue.add("hEta", "Track eta;#eta;Counts", HistType::kTH1D, {{20, -0.8, 0.8}}); - ue.add("hPhi", "Track phi;#varphi (rad);Counts", HistType::kTH1D, {{64, 0, TwoPI}}); - ue.add("hvtxZ", "Vertex Z (data);Vertex Z (cm);Events", HistType::kTH1F, {{40, -20.0, 20.0}}); + for (const float& pt : mcPiPt) { + registry.fill(HIST("MCclosure_PtMCPiVsNchMC"), pt, nchF); + registry.fill(HIST("MC/GenPtVsNch"), pt, nchF); + } + for (const float& pt : mcKaPt) { + registry.fill(HIST("MCclosure_PtMCKaVsNchMC"), pt, nchF); + registry.fill(HIST("MC/GenPtVsNch"), pt, nchF); + } + for (const float& pt : mcPrPt) { + registry.fill(HIST("MCclosure_PtMCPrVsNchMC"), pt, nchF); + registry.fill(HIST("MC/GenPtVsNch"), pt, nchF); + } - LOG(info) << "=== Initialization complete ==="; -} + // Fill physics-selected histograms (after vertex and INEL cuts) + registry.fill(HIST("MC/EventLoss/NchGenerated_PhysicsSelected"), nchF); + for (const float& pt : mcPiPt) { + registry.fill(HIST("MC/GenPtVsNch_PhysicsSelected"), pt, nchF); + } + for (const float& pt : mcKaPt) { + registry.fill(HIST("MC/GenPtVsNch_PhysicsSelected"), pt, nchF); + } + for (const float& pt : mcPrPt) { + registry.fill(HIST("MC/GenPtVsNch_PhysicsSelected"), pt, nchF); + } -void MultiplicityPt::processMC(TrackTableMC const& tracks, - aod::McParticles const& particles, - aod::McCollisions const& mcCollisions, - RecoCollisions const& collisions, - aod::McCollisionLabels const& labels, - aod::McCentFT0Ms const& centTable, - BCsRun3 const& /*bcs*/) -{ + // Fill signal loss denominators + for (const float& pt : particlePtBySpecies[kPion]) { + registry.fill(HIST("PtPiVsNchMC_AllGen"), pt, nchF); + } + for (const float& pt : particlePtBySpecies[kKaon]) { + registry.fill(HIST("PtKaVsNchMC_AllGen"), pt, nchF); + } + for (const float& pt : particlePtBySpecies[kProton]) { + registry.fill(HIST("PtPrVsNchMC_AllGen"), pt, nchF); + } + for (const float& pt : particlePtAll) { + registry.fill(HIST("PtAllVsNchMC_AllGen"), pt, nchF); + } - std::map mcCollisionToNch; - std::set physicsSelectedMCCollisions; - std::map mcCollisionToINELClass; - std::set inel0MCCollisions; - std::map mcCollToCentFromReco; // MC collision ID -> centrality from reco + if (passGenTVX) { + for (const float& pt : particlePtBySpecies[kPion]) { + registry.fill(HIST("SignalLoss/PtPiVsFT0MClass_AllGen"), pt, genCentFT0M); + } + for (const float& pt : particlePtBySpecies[kKaon]) { + registry.fill(HIST("SignalLoss/PtKaVsFT0MClass_AllGen"), pt, genCentFT0M); + } + for (const float& pt : particlePtBySpecies[kProton]) { + registry.fill(HIST("SignalLoss/PtPrVsFT0MClass_AllGen"), pt, genCentFT0M); + } + for (const float& pt : particlePtAll) { + registry.fill(HIST("SignalLoss/PtAllVsFT0MClass_AllGen"), pt, genCentFT0M); + } + } - ue.fill(HIST("MC/GenRecoCollisions"), 1.f, mcCollisions.size()); + // Fill inclusive histograms - all generated + for (const float& pt : particlePtAll) { + registry.fill(HIST("Inclusive/hPtPrimGenAll"), pt); + registry.fill(HIST("Inclusive/hPtPrimGenAllVsMult"), pt, nchF); + } + for (const float& pt : mcPiPt) { + registry.fill(HIST("Pion/hPtPrimGenAll"), pt); + registry.fill(HIST("Pion/hPtPrimGenAllVsMult"), pt, nchF); + } + for (const float& pt : mcKaPt) { + registry.fill(HIST("Kaon/hPtPrimGenAll"), pt); + registry.fill(HIST("Kaon/hPtPrimGenAllVsMult"), pt, nchF); + } + for (const float& pt : mcPrPt) { + registry.fill(HIST("Proton/hPtPrimGenAll"), pt); + registry.fill(HIST("Proton/hPtPrimGenAllVsMult"), pt, nchF); + } - for (const auto& mcCollision : mcCollisions) { - int64_t mcCollId = mcCollision.globalIndex(); - auto particlesInCollision = particles.sliceBy(perMCCol, mcCollId); + for (const float& pt : particlePtAll) { + registry.fill(HIST("Inclusive/hPtPrimGen"), pt); + registry.fill(HIST("Inclusive/hPtPrimGenVsMult"), pt, nchF); + } - // Check if event has at least 1 particle in acceptance (INEL>0) - bool hasParticleInAcceptance = false; - for (const auto& particle : particlesInCollision) { - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (!pdgParticle || pdgParticle->Charge() == 0.) - continue; - if (!particle.isPhysicalPrimary()) + const int nRecColls = collisions.size(); + registry.fill(HIST("NumberOfRecoCollisions"), nRecColls); + + if (nRecColls == 0) + return; + registry.fill(HIST("EventCounter"), kRecoColl); + + int biggestNContribs = -1; + int bestCollisionIndex = -1; + for (const auto& col : collisions) { + if (col.numContrib() > biggestNContribs) { + biggestNContribs = col.numContrib(); + bestCollisionIndex = col.globalIndex(); + } + } + + for (const auto& collision : collisions) { + if (collision.globalIndex() != bestCollisionIndex) continue; - if (std::abs(particle.eta()) >= cfgCutEtaMax.value) + // if (!isEventSelectedMC(collision)) continue; + if (!isEventSelected(collision)) continue; - if (particle.pt() < cfgTrkLowPtCut.value) + + if (nGoodITS.value && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) continue; - hasParticleInAcceptance = true; - break; - } + registry.fill(HIST("EventCounter"), kGoodITS); - if (hasParticleInAcceptance) { - inel0MCCollisions.insert(mcCollId); - } + registry.fill(HIST("EventCounter"), kRecoSelected); - int nGenCharged = countGeneratedChargedPrimaries(particlesInCollision, cfgCutEtaMax.value, cfgTrkLowPtCut.value); - mcCollisionToNch[mcCollId] = nGenCharged; + const float centrality = collision.centFT0M(); - bool inel0 = o2::pwglf::isINELgt0mc(particlesInCollision, pdg); - bool inel1 = o2::pwglf::isINELgt1mc(particlesInCollision, pdg); - int inelClass = inel1 ? 2 : (inel0 ? 1 : 0); - mcCollisionToINELClass[mcCollId] = inelClass; + float magField = 0.f; + if (applyPhiCut.value) { + const auto& bc = collision.foundBC_as(); + magField = static_cast(getMagneticField(bc.timestamp())); + } - bool physicsSelected = (std::abs(mcCollision.posZ()) <= cfgCutVertex.value); - if (cfgINELCut.value == INELgt0 && !inel0) - physicsSelected = false; - if (cfgINELCut.value == INELgt1 && !inel1) - physicsSelected = false; + // Fill centrality and correlation histograms + registry.fill(HIST("Centrality/hCentRaw"), centrality); + registry.fill(HIST("NchMCVsCent"), centrality, nchF); + registry.fill(HIST("Centrality/hCentVsMult"), centrality, nchF); + registry.fill(HIST("Centrality/hMultVsCent"), nchF, centrality); + registry.fill(HIST("Centrality/hCentVsVz"), centrality, collision.posZ()); + registry.fill(HIST("Centrality_WRecoEvt"), centrality); + registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); + + for (const float& pt : particlePtAll) { + registry.fill(HIST("NchMCVsCentVsPt"), centrality, nchF, pt); + } - if (physicsSelected) { - physicsSelectedMCCollisions.insert(mcCollId); - if (inel0) - ue.fill(HIST("MC/GenRecoCollisions"), 2.f); - if (inel1) - ue.fill(HIST("MC/GenRecoCollisions"), 3.f); - } + registry.fill(HIST("NchMC_WithRecoEvt"), nchF); + registry.fill(HIST("MC/EventLoss/NchGenerated_Reconstructed"), nchF); + registry.fill(HIST("EventLoss/NgenWithRecoInclusive"), 0.5); - // Fill generated particle spectra - for (const auto& particle : particlesInCollision) { - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (!pdgParticle || pdgParticle->Charge() == 0.) - continue; - if (!particle.isPhysicalPrimary()) - continue; + for (const float& pt : particlePtBySpecies[kPion]) { + registry.fill(HIST("SignalLoss/PtPiInclusive_WithRecoEvt"), pt); + } + for (const float& pt : particlePtBySpecies[kKaon]) { + registry.fill(HIST("SignalLoss/PtKaInclusive_WithRecoEvt"), pt); + } + for (const float& pt : particlePtBySpecies[kProton]) { + registry.fill(HIST("SignalLoss/PtPrInclusive_WithRecoEvt"), pt); + } + for (const float& pt : particlePtAll) { + registry.fill(HIST("SignalLoss/PtAllInclusive_WithRecoEvt"), pt); + } - int pdgCode = std::abs(particle.pdgCode()); - float pt = particle.pt(); + if (passGenTVX) { + registry.fill(HIST("EventLoss/NgenWithRecoFT0M"), genCentFT0M); + } + registry.fill(HIST("MC/EventLoss/GenMultVsCent"), centrality, nchF); + registry.fill(HIST("zPosMC"), mcCollision.posZ()); + registry.fill(HIST("zPosReco"), collision.posZ()); + registry.fill(HIST("T0Ccent"), centrality); + + if (collision.has_foundFT0()) { + registry.fill(HIST("T0CcentVsFoundFT0"), centrality, 1.5); + } else { + registry.fill(HIST("T0CcentVsFoundFT0"), centrality, 0.5); + } + if (collision.has_foundFT0() && collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + registry.fill(HIST("T0CcentVsFoundFT0AndTVX"), centrality, 1.5); + } else { + registry.fill(HIST("T0CcentVsFoundFT0AndTVX"), centrality, 0.5); + } - // Fill hPtPrimGenAll for ALL generated particles (NO CUTS) - if (pdgCode == PDG_t::kPiPlus) { - ue.fill(HIST("Pion/hPtPrimGenAll"), pt); - } else if (pdgCode == PDG_t::kKPlus) { - ue.fill(HIST("Kaon/hPtPrimGenAll"), pt); - } else if (pdgCode == PDG_t::kProton) { - ue.fill(HIST("Proton/hPtPrimGenAll"), pt); + // Fill inclusive histograms for reco events + for (const float& pt : particlePtAll) { + registry.fill(HIST("Inclusive/hPtPrimRecoEv"), pt); + registry.fill(HIST("Inclusive/hPtPrimRecoEvVsMult"), pt, nchF); + registry.fill(HIST("Inclusive/hPtPrimGoodEv"), pt); + registry.fill(HIST("Inclusive/hPtPrimGoodEvVsMult"), pt, nchF); } - // Fill hPtGenINEL for particles in INEL>0 events (with acceptance cuts) - if (hasParticleInAcceptance) { - if (std::abs(particle.eta()) >= cfgCutEtaMax.value) - continue; - if (particle.pt() < cfgTrkLowPtCut.value) - continue; + // Fill signal loss numerators and efficiency denominators + for (const float& pt : particlePtBySpecies[kPion]) { + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), pt, nchF); + registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), pt, centrality); + registry.fill(HIST("PtGenPiVsNchMC_WithRecoEvt"), pt, nchF); + } + for (const float& pt : particlePtBySpecies[kKaon]) { + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), pt, nchF); + registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), pt, centrality); + registry.fill(HIST("PtGenKaVsNchMC_WithRecoEvt"), pt, nchF); + } + for (const float& pt : particlePtBySpecies[kProton]) { + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), pt, nchF); + registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), pt, centrality); + registry.fill(HIST("PtGenPrVsNchMC_WithRecoEvt"), pt, nchF); + } + for (const float& pt : particlePtAll) { + registry.fill(HIST("PtAllVsNchMC_WithRecoEvt"), pt, nchF); + registry.fill(HIST("PtAllVsCentMC_WithRecoEvt"), pt, centrality); + registry.fill(HIST("PtGenAllVsNchMC_WithRecoEvt"), pt, nchF); + } - if (pdgCode == PDG_t::kPiPlus) { - ue.fill(HIST("Pion/hPtGenINEL"), pt); - ue.fill(HIST("Pion/hPtGenINEL_Mult"), pt, nGenCharged); - } else if (pdgCode == PDG_t::kKPlus) { - ue.fill(HIST("Kaon/hPtGenINEL"), pt); - ue.fill(HIST("Kaon/hPtGenINEL_Mult"), pt, nGenCharged); - } else if (pdgCode == PDG_t::kProton) { - ue.fill(HIST("Proton/hPtGenINEL"), pt); - ue.fill(HIST("Proton/hPtGenINEL_Mult"), pt, nGenCharged); + if (passGenTVX) { + for (const float& pt : particlePtBySpecies[kPion]) { + registry.fill(HIST("SignalLoss/PtPiVsFT0MClass_WithRecoEvt"), pt, genCentFT0M); + } + for (const float& pt : particlePtBySpecies[kKaon]) { + registry.fill(HIST("SignalLoss/PtKaVsFT0MClass_WithRecoEvt"), pt, genCentFT0M); + } + for (const float& pt : particlePtBySpecies[kProton]) { + registry.fill(HIST("SignalLoss/PtPrVsFT0MClass_WithRecoEvt"), pt, genCentFT0M); + } + for (const float& pt : particlePtAll) { + registry.fill(HIST("SignalLoss/PtAllVsFT0MClass_WithRecoEvt"), pt, genCentFT0M); } } - // Apply acceptance cuts for physics-selected spectra - if (std::abs(particle.eta()) >= cfgCutEtaMax.value) - continue; - if (particle.pt() < cfgTrkLowPtCut.value) - continue; + // Fill efficiency denominator histograms + for (const float& pt : mcPiPt) { + registry.fill(HIST("Pion/hPtDenEff"), pt); + registry.fill(HIST("Pion/hPtDenEffVsMult"), pt, nchF); + } + for (const float& pt : mcKaPt) { + registry.fill(HIST("Kaon/hPtDenEff"), pt); + registry.fill(HIST("Kaon/hPtDenEffVsMult"), pt, nchF); + } + for (const float& pt : mcPrPt) { + registry.fill(HIST("Proton/hPtDenEff"), pt); + registry.fill(HIST("Proton/hPtDenEffVsMult"), pt, nchF); + } + for (const float& pt : particlePtAll) { + registry.fill(HIST("Inclusive/hPtDenEff"), pt); + registry.fill(HIST("Inclusive/hPtDenEffVsMult"), pt, nchF); + } - // Fill generated spectra (physics selected only) - if (physicsSelected) { - ue.fill(HIST("Inclusive/hPtGen"), pt); + const auto& groupedTracks = tracksMC.sliceBy(perCollision, collision.globalIndex()); - if (pdgCode == PDG_t::kPiPlus) { - ue.fill(HIST("Pion/hPtGen"), pt); - } else if (pdgCode == PDG_t::kKPlus) { - ue.fill(HIST("Kaon/hPtGen"), pt); - } else if (pdgCode == PDG_t::kProton) { - ue.fill(HIST("Proton/hPtGen"), pt); + for (const auto& track : groupedTracks) { + if (track.eta() < cfgCutEtaMin.value || track.eta() > cfgCutEtaMax.value) + continue; + if (track.pt() < cfgTrkLowPtCut.value) + continue; + if (!track.has_mcParticle()) + continue; + + // Before DCA cuts + registry.fill(HIST("hDCAxyVsPt_before"), track.pt(), track.dcaXY()); + registry.fill(HIST("hDCAzVsPt_before"), track.pt(), track.dcaZ()); + + if (applyPhiCut.value && track.pt() >= pTthresholdPhiCut.value) { + float phiPrime = getTransformedPhi(track.phi(), track.sign(), magField); + registry.fill(HIST("PhiCut/hPtVsPhiPrimeBefore"), track.pt(), phiPrime); } - } - } - } - std::map recoToMcMap; - std::map recoToCentMap; + const auto& particle = track.mcParticle(); + auto* pdgPart = pdg->GetParticle(particle.pdgCode()); + if (!pdgPart || std::abs(pdgPart->Charge()) < MinCharge) + continue; + + const bool isPrimary = particle.isPhysicalPrimary(); + const bool isDecay = (!isPrimary) && (particle.getProcess() == TMCProcess::kPDecay); + const bool isMaterial = (!isPrimary) && (!isDecay); + (void)isMaterial; + const int absPDG = std::abs(particle.pdgCode()); + const bool isPi = (absPDG == PDG_t::kPiPlus); + const bool isKa = (absPDG == PDG_t::kKPlus); + const bool isPr = (absPDG == PDG_t::kProton); + + float momentum = track.p(); + float tpcSignal = track.tpcSignal(); + float eta = track.eta(); + int charge = track.sign(); + + int centIndex = -1; + for (int j = 0; j < CentralityClasses; ++j) { + if (centrality >= centBinningStd[j] && centrality < centBinningStd[j + 1]) { + centIndex = j; + break; + } + } + if (centIndex == -1) + continue; - size_t nPairs = std::min(labels.size(), collisions.size()); - for (size_t i = 0; i < nPairs; ++i) { - const auto& collision = collisions.iteratorAt(i); - const auto& label = labels.iteratorAt(i); - recoToMcMap[collision.globalIndex()] = label.mcCollisionId(); - } + registry.fill(HIST("hEta"), track.eta()); + registry.fill(HIST("hPhi"), track.phi()); + registry.fill(HIST("EtaVsPhi"), track.eta(), track.phi()); + + // Fill TPC cluster histograms + registry.fill(HIST("hNclFoundTPC"), track.tpcNClsFound()); + registry.fill(HIST("hNclPIDTPC"), track.tpcNClsPID()); + registry.fill(HIST("hNclFoundTPCvsPt"), track.pt(), track.tpcNClsFound()); + registry.fill(HIST("hNclPIDTPCvsPt"), track.pt(), track.tpcNClsPID()); + + // Fill inclusive reconstructed histograms + registry.fill(HIST("Inclusive/hPtAllReco"), track.pt()); + registry.fill(HIST("Inclusive/hPtAllRecoVsMult"), track.pt(), nchF); + + if (isPi) { + registry.fill(HIST("Pion/hPtAllReco"), track.pt()); + registry.fill(HIST("Pion/hPtAllRecoVsMult"), track.pt(), nchF); + } else if (isKa) { + registry.fill(HIST("Kaon/hPtAllReco"), track.pt()); + registry.fill(HIST("Kaon/hPtAllRecoVsMult"), track.pt(), nchF); + } else if (isPr) { + registry.fill(HIST("Proton/hPtAllReco"), track.pt()); + registry.fill(HIST("Proton/hPtAllRecoVsMult"), track.pt(), nchF); + } - size_t nCentPairs = std::min(centTable.size(), collisions.size()); - for (size_t i = 0; i < nCentPairs; ++i) { - const auto& collision = collisions.iteratorAt(i); - const auto& cent = centTable.iteratorAt(i); - float centValue = cent.centFT0M(); - recoToCentMap[collision.globalIndex()] = centValue; - ue.fill(HIST("Centrality/hCentRaw"), centValue); - } + if (passesTrackSelectionNoDCA(track)) { + if (isPrimary) { + if (isPi) + registry.fill(HIST("dcaVsPtPi"), track.pt(), track.dcaXY(), centrality); + if (isPr) + registry.fill(HIST("dcaVsPtPr"), track.pt(), track.dcaXY(), centrality); + } else if (isDecay) { + if (isPi) + registry.fill(HIST("dcaVsPtPiDec"), track.pt(), track.dcaXY(), centrality); + if (isPr) + registry.fill(HIST("dcaVsPtPrDec"), track.pt(), track.dcaXY(), centrality); + } else { + if (isPi) + registry.fill(HIST("dcaVsPtPiMat"), track.pt(), track.dcaXY(), centrality); + if (isPr) + registry.fill(HIST("dcaVsPtPrMat"), track.pt(), track.dcaXY(), centrality); + } + } - std::set reconstructedMCCollisions; - std::set selectedMCCollisions; - - for (const auto& collision : collisions) { - ue.fill(HIST("CutFlow/hCutStats"), 1); - - int64_t collId = collision.globalIndex(); - - // MC matching - auto mcIt = recoToMcMap.find(collId); - if (mcIt == recoToMcMap.end()) - continue; - ue.fill(HIST("CutFlow/hCutStats"), 2); - - int64_t mcCollId = mcIt->second; - auto nchIt = mcCollisionToNch.find(mcCollId); - if (nchIt == mcCollisionToNch.end()) - continue; - int nGenCharged = nchIt->second; - - auto inelIt = mcCollisionToINELClass.find(mcCollId); - int inelClass = (inelIt != mcCollisionToINELClass.end()) ? inelIt->second : 0; - - // Centrality - auto centIt = recoToCentMap.find(collId); - if (centIt == recoToCentMap.end()) - continue; - ue.fill(HIST("CutFlow/hCutStats"), 3); - - float cent = centIt->second; - if (cent < 0 || cent > CentBinMax) - continue; - - // Store centrality for this MC collision (used later for MC truth plots) - mcCollToCentFromReco[mcCollId] = cent; - - // Vertex cut - bool passVertex = std::abs(collision.posZ()) <= cfgCutVertex.value; - if (!passVertex) - continue; - ue.fill(HIST("CutFlow/hCutStats"), 4); - ue.fill(HIST("Centrality/hCentAfterVtx"), cent); - - // INEL cut - bool passINEL = true; - if (cfgINELCut.value == INELgt0 && inelClass < INELgt0) - passINEL = false; - if (cfgINELCut.value == INELgt1 && inelClass < INELgt1) - passINEL = false; - if (!passINEL) - continue; - - // Event passed all cuts - ue.fill(HIST("CutFlow/hCutStats"), 5); - ue.fill(HIST("Centrality/hCentAfterAll"), cent); - - ue.fill(HIST("MC/EventLoss/GenMultVsCent_Selected"), cent, nGenCharged); - ue.fill(HIST("Centrality/hCentVsMult"), cent, nGenCharged); - ue.fill(HIST("Centrality/hMultVsCent"), nGenCharged, cent); - ue.fill(HIST("hvtxZ"), collision.posZ()); - - selectedMCCollisions.insert(mcCollId); - reconstructedMCCollisions.insert(mcCollId); - - // Get magnetic field for phi cut - float magField = 0; - if (applyPhiCut.value) { - const auto& bc = collision.bc_as(); - magField = getMagneticField(bc.timestamp()); - } + if (!passesTrackSelection(track)) + continue; - int nTracksInEvent = 0; + // After Trk cuts + registry.fill(HIST("hDCAxyVsPt_after"), track.pt(), track.dcaXY()); + registry.fill(HIST("hDCAzVsPt_after"), track.pt(), track.dcaZ()); - for (const auto& track : tracks) { - if (!track.has_collision()) - continue; - if (track.collisionId() != collId) - continue; + if (applyPhiCut.value && !passedPhiCut(track, magField)) + continue; - if (!passesTrackSelection(track, magField)) - continue; + if (applyPhiCut.value && track.pt() >= pTthresholdPhiCut.value) { + float phiPrime = getTransformedPhi(track.phi(), track.sign(), magField); + registry.fill(HIST("PhiCut/hPtVsPhiPrimeAfter"), track.pt(), phiPrime); + } - nTracksInEvent++; + // Fill efficiency numerators (reconstructed primaries) + if (isPrimary) { + registry.fill(HIST("PtResolution"), particle.pt(), + (track.pt() - particle.pt()) / particle.pt()); + + registry.fill(HIST("Inclusive/hPtNumEff"), particle.pt()); + registry.fill(HIST("Inclusive/hPtNumEffVsMult"), particle.pt(), nchF); + registry.fill(HIST("Inclusive/hPtPrimReco"), track.pt()); + registry.fill(HIST("Inclusive/hPtPrimRecoVsMult"), track.pt(), nchF); + + if (isPi) { + registry.fill(HIST("Pion/hPtNumEff"), particle.pt()); + registry.fill(HIST("Pion/hPtNumEffVsMult"), particle.pt(), nchF); + registry.fill(HIST("Pion/hPtPrimReco"), track.pt()); + registry.fill(HIST("Pion/hPtPrimRecoVsMult"), track.pt(), nchF); + } else if (isKa) { + registry.fill(HIST("Kaon/hPtNumEff"), particle.pt()); + registry.fill(HIST("Kaon/hPtNumEffVsMult"), particle.pt(), nchF); + registry.fill(HIST("Kaon/hPtPrimReco"), track.pt()); + registry.fill(HIST("Kaon/hPtPrimRecoVsMult"), track.pt(), nchF); + } else if (isPr) { + registry.fill(HIST("Proton/hPtNumEff"), particle.pt()); + registry.fill(HIST("Proton/hPtNumEffVsMult"), particle.pt(), nchF); + registry.fill(HIST("Proton/hPtPrimReco"), track.pt()); + registry.fill(HIST("Proton/hPtPrimRecoVsMult"), track.pt(), nchF); + } - ue.fill(HIST("hNclFoundTPC"), track.tpcNClsFound()); - ue.fill(HIST("hNclPIDTPC"), track.tpcNClsPID()); - ue.fill(HIST("hEta"), track.eta()); - ue.fill(HIST("hPhi"), track.phi()); - ue.fill(HIST("Inclusive/hPtReco"), track.pt()); + // Fill efficiency numerator histograms + if (isPi) { + registry.fill(HIST("PtPiVsCent_WithRecoEvt"), track.pt(), centrality); + registry.fill(HIST("PtGenPiVsNchMC_RecoTrk"), particle.pt(), nchF); + registry.fill(HIST("MCclosure_PtPiVsNchMC"), track.pt(), nchF); + } else if (isKa) { + registry.fill(HIST("PtKaVsCent_WithRecoEvt"), track.pt(), centrality); + registry.fill(HIST("PtGenKaVsNchMC_RecoTrk"), particle.pt(), nchF); + registry.fill(HIST("MCclosure_PtKaVsNchMC"), track.pt(), nchF); + } else if (isPr) { + registry.fill(HIST("PtPrVsCent_WithRecoEvt"), track.pt(), centrality); + registry.fill(HIST("PtGenPrVsNchMC_RecoTrk"), particle.pt(), nchF); + registry.fill(HIST("MCclosure_PtPrVsNchMC"), track.pt(), nchF); + } + registry.fill(HIST("PtAllVsCent_WithRecoEvt"), track.pt(), centrality); + registry.fill(HIST("PtGenAllVsNchMC_RecoTrk"), particle.pt(), nchF); + } else { + // Fill secondary particle histograms + registry.fill(HIST("Inclusive/hPtSecReco"), track.pt()); + registry.fill(HIST("Inclusive/hPtSecRecoVsMult"), track.pt(), nchF); + if (isPi) { + registry.fill(HIST("Pion/hPtSecReco"), track.pt()); + registry.fill(HIST("Pion/hPtSecRecoVsMult"), track.pt(), nchF); + } else if (isKa) { + registry.fill(HIST("Kaon/hPtSecReco"), track.pt()); + registry.fill(HIST("Kaon/hPtSecRecoVsMult"), track.pt(), nchF); + } else if (isPr) { + registry.fill(HIST("Proton/hPtSecReco"), track.pt()); + registry.fill(HIST("Proton/hPtSecRecoVsMult"), track.pt(), nchF); + } + } - if (track.has_mcParticle()) { - const auto& particle = track.mcParticle(); - int pdgCode = std::abs(particle.pdgCode()); + // ==================================================================== + // DEDX VS MOMENTUM HISTOGRAMS FILLING - ALL TRACKS + // ==================================================================== + hDedxVspTMomentumVsCent[10]->Fill(track.pt(), tpcSignal, eta); + if (charge > 0) { + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_all_Pos"), momentum, tpcSignal, eta); + hDedxVsMomentumVsCentPos[centIndex]->Fill(momentum, tpcSignal, eta); + hDedxVspTMomentumVsCent[centIndex]->Fill(track.pt(), tpcSignal, eta); + hMomentumVsEtaPos[centIndex]->Fill(eta, momentum); + hMomentumVsEtaPos[10]->Fill(eta, momentum); + hpTVsEtaPos[centIndex]->Fill(eta, track.pt()); + hpTVsEtaPos[10]->Fill(eta, track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Pos"), eta, track.pt(), momentum); + } else { + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_all_Neg"), momentum, tpcSignal, eta); + hDedxVsMomentumVsCentNeg[centIndex]->Fill(momentum, tpcSignal, eta); + hDedxVspTMomentumVsCent[centIndex]->Fill(track.pt(), tpcSignal, eta); + hMomentumVsEtaNeg[centIndex]->Fill(eta, momentum); + hMomentumVsEtaNeg[10]->Fill(eta, momentum); + hpTVsEtaNeg[centIndex]->Fill(eta, track.pt()); + hpTVsEtaNeg[10]->Fill(eta, track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Neg"), eta, track.pt(), momentum); + } - if (pdgCode == PDG_t::kPiPlus) { - ue.fill(HIST("Pion/hPtReco"), track.pt()); - if (particle.isPhysicalPrimary()) { - ue.fill(HIST("Pion/hPtPrimReco"), track.pt()); - ue.fill(HIST("Inclusive/hPtPrimReco"), track.pt()); + if (isPrimary) { + if (charge > 0) { + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Pos_Pri"), eta, track.pt(), momentum); } else { - ue.fill(HIST("Inclusive/hPtSecReco"), track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Neg_Pri"), eta, track.pt(), momentum); } - } else if (pdgCode == PDG_t::kKPlus) { - ue.fill(HIST("Kaon/hPtReco"), track.pt()); - if (particle.isPhysicalPrimary()) { - ue.fill(HIST("Kaon/hPtPrimReco"), track.pt()); - ue.fill(HIST("Inclusive/hPtPrimReco"), track.pt()); + } + + // ==================================================================== + // DEDX VS MOMENTUM HISTOGRAMS FILLING - PARTICLE SPECIFIC + // ==================================================================== + if (track.has_mcParticle() && isPrimary) { + int pdgCode = std::abs(particle.pdgCode()); + + if (charge > 0) { + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Pos_Pri_MC"), eta, track.pt(), momentum); } else { - ue.fill(HIST("Inclusive/hPtSecReco"), track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Neg_Pri_MC"), eta, track.pt(), momentum); } - } else if (pdgCode == PDG_t::kProton) { - ue.fill(HIST("Proton/hPtReco"), track.pt()); - if (particle.isPhysicalPrimary()) { - ue.fill(HIST("Proton/hPtPrimReco"), track.pt()); - ue.fill(HIST("Inclusive/hPtPrimReco"), track.pt()); - } else { - ue.fill(HIST("Inclusive/hPtSecReco"), track.pt()); + + if (pdgCode == PDG_t::kPiPlus || pdgCode == PDG_t::kKPlus || pdgCode == PDG_t::kProton || + pdgCode == PDG_t::kElectron || pdgCode == PDG_t::kMuonPlus) { + if (charge > 0) { + hTotalMomPosCent[centIndex]->Fill(eta, momentum); + hTotalMomPosCent[10]->Fill(eta, momentum); + hTotalPtPosCent[centIndex]->Fill(eta, track.pt()); + hTotalPtPosCent[10]->Fill(eta, track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Pos_Pri_MC_Part"), eta, track.pt(), momentum); + } else { + hTotalMomNegCent[centIndex]->Fill(eta, momentum); + hTotalMomNegCent[10]->Fill(eta, momentum); + hTotalPtNegCent[centIndex]->Fill(eta, track.pt()); + hTotalPtNegCent[10]->Fill(eta, track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_all_Neg_Pri_MC_Part"), eta, track.pt(), momentum); + } } - } - } - if (passesPIDSelection(track, PartPion)) { - ue.fill(HIST("Pion/hPtMeasured"), track.pt()); - if (enablePIDHistograms) { - ue.fill(HIST("Pion/hNsigmaTPC"), track.pt(), track.tpcNSigmaPi()); + if (pdgCode == PDG_t::kPiPlus) { + if (charge > 0) { + hFracMomPosCent[0][centIndex]->Fill(eta, momentum); + hFracMomPosCent[0][10]->Fill(eta, momentum); + hFracPtPosCent[0][centIndex]->Fill(eta, track.pt()); + hFracPtPosCent[0][10]->Fill(eta, track.pt()); + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_Pi_v0_Pos"), momentum, tpcSignal, eta); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_Pi_Pos"), eta, track.pt(), momentum); + } else { + hFracMomNegCent[0][centIndex]->Fill(eta, momentum); + hFracMomNegCent[0][10]->Fill(eta, momentum); + hFracPtNegCent[0][centIndex]->Fill(eta, track.pt()); + hFracPtNegCent[0][10]->Fill(eta, track.pt()); + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_Pi_v0_Neg"), momentum, tpcSignal, eta); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_Pi_Neg"), eta, track.pt(), momentum); + } + } else if (pdgCode == PDG_t::kKPlus) { + if (charge > 0) { + hFracMomPosCent[1][centIndex]->Fill(eta, momentum); + hFracMomPosCent[1][10]->Fill(eta, momentum); + hFracPtPosCent[1][centIndex]->Fill(eta, track.pt()); + hFracPtPosCent[1][10]->Fill(eta, track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_K_Pos"), eta, track.pt(), momentum); + } else { + hFracMomNegCent[1][centIndex]->Fill(eta, momentum); + hFracMomNegCent[1][10]->Fill(eta, momentum); + hFracPtNegCent[1][centIndex]->Fill(eta, track.pt()); + hFracPtNegCent[1][10]->Fill(eta, track.pt()); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_K_Neg"), eta, track.pt(), momentum); + } + } else if (pdgCode == PDG_t::kProton) { + if (charge > 0) { + hFracMomPosCent[2][centIndex]->Fill(eta, momentum); + hFracMomPosCent[2][10]->Fill(eta, momentum); + hFracPtPosCent[2][centIndex]->Fill(eta, track.pt()); + hFracPtPosCent[2][10]->Fill(eta, track.pt()); + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_Pr_v0_Pos"), momentum, tpcSignal, eta); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_Pr_Pos"), eta, track.pt(), momentum); + } else { + hFracMomNegCent[2][centIndex]->Fill(eta, momentum); + hFracMomNegCent[2][10]->Fill(eta, momentum); + hFracPtNegCent[2][centIndex]->Fill(eta, track.pt()); + hFracPtNegCent[2][10]->Fill(eta, track.pt()); + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_Pr_v0_Neg"), momentum, tpcSignal, eta); + registry.fill(HIST("ResponseMatrix/heta_vs_pt_vs_p_Pr_Neg"), eta, track.pt(), momentum); + } + } else if (pdgCode == PDG_t::kElectron) { + if (charge > 0) { + hFracMomPosCent[3][centIndex]->Fill(eta, momentum); + hFracMomPosCent[3][10]->Fill(eta, momentum); + hFracPtPosCent[3][centIndex]->Fill(eta, track.pt()); + hFracPtPosCent[3][10]->Fill(eta, track.pt()); + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_El_v0_Pos"), momentum, tpcSignal, eta); + } else { + hFracMomNegCent[3][centIndex]->Fill(eta, momentum); + hFracMomNegCent[3][10]->Fill(eta, momentum); + hFracPtNegCent[3][centIndex]->Fill(eta, track.pt()); + hFracPtNegCent[3][10]->Fill(eta, track.pt()); + registry.fill(HIST("DedxVsMomentum/dEdx_vs_Momentum_El_v0_Neg"), momentum, tpcSignal, eta); + } + } else if (pdgCode == PDG_t::kMuonPlus) { + if (charge > 0) { + hFracMomPosCent[4][centIndex]->Fill(eta, momentum); + hFracMomPosCent[4][10]->Fill(eta, momentum); + hFracPtPosCent[4][centIndex]->Fill(eta, track.pt()); + hFracPtPosCent[4][10]->Fill(eta, track.pt()); + } else { + hFracMomNegCent[4][centIndex]->Fill(eta, momentum); + hFracMomNegCent[4][10]->Fill(eta, momentum); + hFracPtNegCent[4][centIndex]->Fill(eta, track.pt()); + hFracPtNegCent[4][10]->Fill(eta, track.pt()); + } + } } - } - if (passesPIDSelection(track, PartKaon)) { - ue.fill(HIST("Kaon/hPtMeasured"), track.pt()); - if (enablePIDHistograms) { - ue.fill(HIST("Kaon/hNsigmaTPC"), track.pt(), track.tpcNSigmaKa()); - } - } + // PID and measured spectra + const int species = bestPIDHypothesis(track); - if (passesPIDSelection(track, PartProton)) { - ue.fill(HIST("Proton/hPtMeasured"), track.pt()); - if (enablePIDHistograms) { - ue.fill(HIST("Proton/hNsigmaTPC"), track.pt(), track.tpcNSigmaPr()); + registry.fill(HIST("Inclusive/hPtMeasured"), track.pt()); + registry.fill(HIST("Inclusive/hPtMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("Inclusive/hPtMeasuredVsMult"), track.pt(), nchF); + + if (species == kPion) { + registry.fill(HIST("PtPiMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("PtPiMeasuredVsNch"), track.pt(), nchF); + registry.fill(HIST("Pion/hPtMeasured"), track.pt()); + registry.fill(HIST("Pion/hPtMeasuredVsMult"), track.pt(), nchF); + if (enablePIDHistograms) { + registry.fill(HIST("Pion/hNsigmaTPC"), track.pt(), track.tpcNSigmaPi()); + } + } else if (species == kKaon) { + registry.fill(HIST("PtKaMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("PtKaMeasuredVsNch"), track.pt(), nchF); + registry.fill(HIST("Kaon/hPtMeasured"), track.pt()); + registry.fill(HIST("Kaon/hPtMeasuredVsMult"), track.pt(), nchF); + if (enablePIDHistograms) { + registry.fill(HIST("Kaon/hNsigmaTPC"), track.pt(), track.tpcNSigmaKa()); + } + } else if (species == kProton) { + registry.fill(HIST("PtPrMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("PtPrMeasuredVsNch"), track.pt(), nchF); + registry.fill(HIST("Proton/hPtMeasured"), track.pt()); + registry.fill(HIST("Proton/hPtMeasuredVsMult"), track.pt(), nchF); + if (enablePIDHistograms) { + registry.fill(HIST("Proton/hNsigmaTPC"), track.pt(), track.tpcNSigmaPr()); + } } + + registry.fill(HIST("PtAllMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("PtAllMeasuredVsNch"), track.pt(), nchF); } + break; } - - ue.fill(HIST("Centrality/hRecoMultVsCent"), cent, nTracksInEvent); } + PROCESS_SWITCH(MultiplicityPt, processSim, "Process MC simulation", true); - for (const auto& mcCollision : mcCollisions) { - int64_t mcCollId = mcCollision.globalIndex(); - - if (reconstructedMCCollisions.find(mcCollId) == reconstructedMCCollisions.end()) - continue; + void processData(CollisionTableData::iterator const& collision, + TrackTableData const& tracks, + BCsRun3 const& bcs) + { + (void)bcs; - auto centIt = mcCollToCentFromReco.find(mcCollId); - if (centIt == mcCollToCentFromReco.end()) - continue; + if (!isEventSelected(collision)) + return; - auto nchIt = mcCollisionToNch.find(mcCollId); - int nGenCharged = (nchIt != mcCollisionToNch.end()) ? nchIt->second : 0; + const float centrality = collision.centFT0M(); - auto particlesInCollision = particles.sliceBy(perMCCol, mcCollId); + float magField = 0; + if (applyPhiCut.value) { + const auto& bc = collision.bc_as(); + magField = getMagneticField(bc.timestamp()); + } - for (const auto& particle : particlesInCollision) { - auto pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (!pdgParticle || pdgParticle->Charge() == 0.) - continue; - if (!particle.isPhysicalPrimary()) - continue; + registry.fill(HIST("hvtxZ"), collision.posZ()); - if (std::abs(particle.eta()) >= cfgCutEtaMax.value) + for (const auto& track : tracks) { + if (track.eta() < cfgCutEtaMin.value || track.eta() > cfgCutEtaMax.value) continue; - if (particle.pt() < cfgTrkLowPtCut.value) + if (track.pt() < cfgTrkLowPtCut.value) continue; - int pdgCode = std::abs(particle.pdgCode()); - float pt = particle.pt(); - - if (pdgCode == PDG_t::kPiPlus) { - ue.fill(HIST("Pion/hPtGenRecoEvent"), pt); - ue.fill(HIST("Pion/hPtGenRecoEvent_Mult"), pt, nGenCharged); - } else if (pdgCode == PDG_t::kKPlus) { - ue.fill(HIST("Kaon/hPtGenRecoEvent"), pt); - ue.fill(HIST("Kaon/hPtGenRecoEvent_Mult"), pt, nGenCharged); - } else if (pdgCode == PDG_t::kProton) { - ue.fill(HIST("Proton/hPtGenRecoEvent"), pt); - ue.fill(HIST("Proton/hPtGenRecoEvent_Mult"), pt, nGenCharged); + if (applyPhiCut.value && track.pt() >= pTthresholdPhiCut.value) { + float phiPrime = getTransformedPhi(track.phi(), track.sign(), magField); + registry.fill(HIST("PhiCut/hPtVsPhiPrimeBefore"), track.pt(), phiPrime); } - } - } - for (const auto& mcCollId : inel0MCCollisions) { - auto centIt = mcCollToCentFromReco.find(mcCollId); - if (centIt != mcCollToCentFromReco.end()) { - float cent = centIt->second; - int nGenCharged = mcCollisionToNch[mcCollId]; - ue.fill(HIST("MC/EventLoss/GenMultVsCent"), cent, nGenCharged); - ue.fill(HIST("hEventsINEL_Cent"), cent); - } - } + if (!passesTrackSelection(track)) + continue; - for (const auto& mcCollId : reconstructedMCCollisions) { - auto centIt = mcCollToCentFromReco.find(mcCollId); - if (centIt != mcCollToCentFromReco.end()) { - ue.fill(HIST("hEventsReco_Cent"), centIt->second); - } - } + if (applyPhiCut.value && !passedPhiCut(track, magField)) + continue; - ue.fill(HIST("hEventLossBreakdown"), 1.f, physicsSelectedMCCollisions.size()); - ue.fill(HIST("hEventLossBreakdown"), 2.f, reconstructedMCCollisions.size()); - ue.fill(HIST("hEventLossBreakdown"), 3.f, selectedMCCollisions.size()); + if (applyPhiCut.value && track.pt() >= pTthresholdPhiCut.value) { + float phiPrime = getTransformedPhi(track.phi(), track.sign(), magField); + registry.fill(HIST("PhiCut/hPtVsPhiPrimeAfter"), track.pt(), phiPrime); + } - ue.fill(HIST("MC/GenRecoCollisions"), 4.f, reconstructedMCCollisions.size()); - ue.fill(HIST("MC/GenRecoCollisions"), 5.f, selectedMCCollisions.size()); + registry.fill(HIST("hEta"), track.eta()); + registry.fill(HIST("hPhi"), track.phi()); + registry.fill(HIST("Inclusive/hPtMeasured"), track.pt()); + registry.fill(HIST("Inclusive/hPtMeasuredVsCent"), track.pt(), centrality); - LOG(info) << "\n=== EVENT STATISTICS ==="; - LOG(info) << "INEL>0 MC collisions: " << inel0MCCollisions.size(); - LOG(info) << "Physics selected MC collisions: " << physicsSelectedMCCollisions.size(); - LOG(info) << "Reconstructed collisions: " << reconstructedMCCollisions.size(); - LOG(info) << "Selected collisions: " << selectedMCCollisions.size(); + const int species = bestPIDHypothesis(track); + if (species == kPion) { + registry.fill(HIST("PtPiMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("Pion/hPtMeasured"), track.pt()); + if (enablePIDHistograms) { + registry.fill(HIST("Pion/hNsigmaTPC"), track.pt(), track.tpcNSigmaPi()); + } + } else if (species == kKaon) { + registry.fill(HIST("PtKaMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("Kaon/hPtMeasured"), track.pt()); + if (enablePIDHistograms) { + registry.fill(HIST("Kaon/hNsigmaTPC"), track.pt(), track.tpcNSigmaKa()); + } + } else if (species == kProton) { + registry.fill(HIST("PtPrMeasuredVsCent"), track.pt(), centrality); + registry.fill(HIST("Proton/hPtMeasured"), track.pt()); + if (enablePIDHistograms) { + registry.fill(HIST("Proton/hNsigmaTPC"), track.pt(), track.tpcNSigmaPr()); + } + } - if (physicsSelectedMCCollisions.size() > 0) { - float efficiency = 100.f * selectedMCCollisions.size() / physicsSelectedMCCollisions.size(); - LOG(info) << "Final efficiency: " << efficiency << "%"; + registry.fill(HIST("PtAllMeasuredVsCent"), track.pt(), centrality); + } } -} + PROCESS_SWITCH(MultiplicityPt, processData, "Process data", false); +}; -void MultiplicityPt::processData(CollisionTableData::iterator const& /*collision*/, - TrackTableData const& /*tracks*/, - BCsRun3 const& /*bcs*/) +// ============================================================ +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Nuspex/nucleiEbye.cxx b/PWGLF/Tasks/Nuspex/nucleiEbye.cxx index f970a270906..df645514aba 100644 --- a/PWGLF/Tasks/Nuspex/nucleiEbye.cxx +++ b/PWGLF/Tasks/Nuspex/nucleiEbye.cxx @@ -189,7 +189,7 @@ struct nucleiEbye { return true; } - void fillHistoN(std::shared_ptr hFull, std::shared_ptr const& hTmp, int const subsample, int const centrality) + void fillHistoN(const std::shared_ptr& hFull, std::shared_ptr const& hTmp, int const subsample, int const centrality) { for (int iEta{1}; iEta < hTmp->GetNbinsX() + 1; ++iEta) { for (int iPt{1}; iPt < hTmp->GetNbinsY() + 1; ++iPt) { @@ -202,7 +202,7 @@ struct nucleiEbye { } } - void fillHistoN(std::shared_ptr hFull, std::shared_ptr const& hTmpA, std::shared_ptr const& hTmpB, int const subsample, int const centrality) + void fillHistoN(const std::shared_ptr& hFull, std::shared_ptr const& hTmpA, std::shared_ptr const& hTmpB, int const subsample, int const centrality) { for (int iEta{1}; iEta < hTmpA->GetNbinsX() + 1; ++iEta) { auto eta = hTmpA->GetXaxis()->GetBinCenter(iEta); diff --git a/PWGLF/Tasks/Nuspex/nucleiFromHypertritonMap.cxx b/PWGLF/Tasks/Nuspex/nucleiFromHypertritonMap.cxx index 13fadb46a50..5d96a361585 100644 --- a/PWGLF/Tasks/Nuspex/nucleiFromHypertritonMap.cxx +++ b/PWGLF/Tasks/Nuspex/nucleiFromHypertritonMap.cxx @@ -30,12 +30,11 @@ #include #include #include +#include #include #include #include -#include - #include #include #include diff --git a/PWGLF/Tasks/Nuspex/nuclei_in_toward_transv_regions.cxx b/PWGLF/Tasks/Nuspex/nuclei_in_toward_transv_regions.cxx index d00f320dbdc..8756a1b46e4 100644 --- a/PWGLF/Tasks/Nuspex/nuclei_in_toward_transv_regions.cxx +++ b/PWGLF/Tasks/Nuspex/nuclei_in_toward_transv_regions.cxx @@ -236,7 +236,7 @@ struct nuclei_in_toward_transv_regions { int i = -1; // Loop over Reconstructed Tracks - for (auto track : tracks) { + for (const auto& track : tracks) { i++; if (!passedTrackSelectionForJetReconstruction(track)) @@ -256,7 +256,7 @@ struct nuclei_in_toward_transv_regions { auto const& leading_track = tracks.iteratorAt(leading_ID); // Loop over Reconstructed Tracks - for (auto track : tracks) { + for (const auto& track : tracks) { // Track Selection if (!passedTrackSelection(track)) @@ -319,7 +319,7 @@ struct nuclei_in_toward_transv_regions { int i = -1; // Loop over Reconstructed Tracks - for (auto track : tracks_per_coll) { + for (const auto& track : tracks_per_coll) { i++; if (!passedTrackSelectionForJetReconstruction(track)) @@ -338,7 +338,7 @@ struct nuclei_in_toward_transv_regions { auto const& leading_track = tracks_per_coll.iteratorAt(leading_ID); // Loop over Reconstructed Tracks - for (auto track : tracks_per_coll) { + for (const auto& track : tracks_per_coll) { if (!passedTrackSelection(track)) continue; diff --git a/PWGLF/Tasks/Nuspex/nucleitpcPbPb.cxx b/PWGLF/Tasks/Nuspex/nucleitpcPbPb.cxx new file mode 100644 index 00000000000..1658ffb009e --- /dev/null +++ b/PWGLF/Tasks/Nuspex/nucleitpcPbPb.cxx @@ -0,0 +1,1481 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file nucleitpcPbPb.cxx +/// \brief Nuclei (pion, proton, deuteron, triton, He3, He4) TPC(+TOF) PID +/// analysis for Pb-Pb, including MC efficiency/acceptance, signal-loss, +/// event-loss and DCA-template corrections. +/// \note under work +/// +/// \author Jaideep Tanwar , Panjab University + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/PID/PIDTOF.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using CollisionsFull = soa::Join; +using TracksFull = soa::Join; +using CollisionsFullMC = soa::Join; +using McCollisionMults = soa::Join; +//=================================================================================== +// Static configuration tables (species definitions, PID/quality defaults, and +// track/DCA correction parameters). None of the values below are changed by +// this cleanup -- only the surrounding comments/formatting are. +//=================================================================================== +namespace +{ + +// ---- Particle species definitions ------------------------------------------- +constexpr int NParticles = 6; // pion, proton, deuteron, triton, helion(He3), alpha(He4) +const std::vector particleNames{"pion", "proton", "deuteron", "triton", "helion", "alpha"}; +const std::vector correctedparticleNames{"helion", "antihelion", "alpha", "antialpha"}; +const std::vector particlePdgCodes{211, 2212, o2::constants::physics::kDeuteron, o2::constants::physics::kTriton, o2::constants::physics::kHelium3, o2::constants::physics::kAlpha}; +const std::vector particleMasses{o2::constants::physics::MassPionCharged, o2::constants::physics::MassProton, o2::constants::physics::MassDeuteron, o2::constants::physics::MassTriton, o2::constants::physics::MassHelium3, o2::constants::physics::MassAlpha}; +const std::vector particleCharge{1, 1, 1, 1, 2, 2}; + +// ---- TPC Bethe-Bloch parameterisation defaults (per species) --------------- +constexpr int NBetheParams = 6; +const std::vector hfMothCodes = {511, 521, 531, 541, 5122}; // b-mesons + Lambda_b +const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; +const std::array, NParticles> kBetheBlochDefault{{{13.611469, 3.598765, -0.021138, 2.039562, 0.651040, 0.09}, // pion + {5.393020, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, // proton + {5.393020, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, // deuteron + {5.393020, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, // triton + {-126.557359, -0.858569, 1.111643, 1.210323, 2.656374, 0.09}, // helion + {-126.557359, -0.858569, 1.111643, 1.210323, 2.656374, 0.09}}}; // alpha + +// ---- Track PID/quality selection defaults (per species) -------------------- +constexpr int NTrkSettings = 13; +const std::vector trackPIDsettingsNames{"useBBparams", "minITSnCls", "minITSnClscos", "minTPCnCls", "maxTPCchi2", "minTPCchi2", "maxITSchi2", "maxTPCnSigma", "maxDcaXY", "maxDcaZ", "minITSclsSize", "minTPCnClsCrossedRows", "minReqClusterITSib"}; +const std::array, NParticles> kTrackPIDSettings{{{0, 0, 4, 60, 4.0, 0.5, 100, 2.5, 2., 2., 0., 70, 1}, + {1, 0, 4, 70, 4.0, 0.5, 100, 3.0, 2., 2., 0., 70, 1}, + {1, 0, 4, 70, 4.0, 0.5, 100, 3.0, 2., 2., 0., 70, 1}, + {1, 0, 4, 70, 4.0, 0.5, 100, 3.0, 2., 2., 0., 70, 1}, + {1, 0, 4, 75, 4.0, 0.5, 100, 5.0, 2., 2., 0., 70, 1}, + {1, 0, 4, 70, 4.0, 0.5, 100, 5.0, 2., 2., 0., 70, 1}}}; + +constexpr int NTrkSettings2 = 7; +const std::vector trackPIDsettingsNames2{"useITSnsigma", "minITSnsigma", "maxITSnsigma", "fillsparsh", "useTPCnsigmaTOF", "maxTPCnsigmaTOF", "maxTOFnsigma"}; +const std::array, NParticles> kTrackPIDSettings2{{{1, -5, 4, 0, 1, 2, 2}, + {1, -5, 4, 0, 1, 2, 2}, + {1, -5, 4, 0, 1, 2, 2}, + {1, -5, 4, 1, 1, 2, 2}, + {1, -5, 4, 1, 1, 2, 2}, + {1, -5, 4, 1, 1, 2, 2}}}; + +// ---- pT-dependent reconstruction correction parameters for He3/He4 --------- +constexpr int NFittingParticle = 4; +constexpr int NFittingParameters = 4; +const std::vector trackcorrectionNames{"a", "b", "c", "d"}; +const std::array, NFittingParticle> ktrackcorrection{{{0.464215, 0.195771, 0.0183111, 0.0}, // He3 + {0.464215, 0.195771, 0.0183111, 0.0}, // anti-He3 + {0.00765, 0.503791, -1.10517, 0.0}, // He4 + {0.00765, 0.503791, -1.10517, 0.0}}}; // anti-He4 + +// ---- pT-dependent DCA cut parameters ---------------------------------------- +// DCAxy(pt) = p0 * exp( p1 * pt) + p2 +// DCAz(pt) = p0 * exp(-p1 * pt) + p2 +constexpr int NDCATypes = 2; +constexpr int NDCAParameters = 3; +const std::vector dcaNames{"p0", "p1", "p2"}; +const std::vector dcaTypeNames{"DCAxy", "DCAz"}; +const std::array, NDCATypes> kDCAcorrection{{{0.0118, -0.6889, 0.0017}, + {0.1014, 1.7512, 0.0024}}}; + +// ---- MC decay-origin classification ----------------------------------------- +constexpr int DecayTypePrimary = 0; +constexpr int DecayTypeWeak = 1; +constexpr int DecayTypeTransport = 2; + +/// One row of the Bethe-Bloch/PID configuration table, resolved for a given species. +struct PrimParticles { + TString name; + int pdgCode; + int charge; + double mass; + double resolution; + std::vector betheParams; + bool active; + + PrimParticles(const std::string& name_, int pdgCode_, double mass_, int charge_, const LabeledArray& bethe) + : name(name_), pdgCode(pdgCode_), charge(charge_), mass(mass_), active(false) + { + resolution = bethe.get(name, "resolution"); + betheParams.clear(); + constexpr unsigned int NSpecies = 5; + for (unsigned int i = 0; i < NSpecies; ++i) { + betheParams.push_back(bethe.get(name, i)); + } + } +}; // struct PrimParticles + +std::vector> hmass; +std::vector> hmassnsigma; + +} // namespace + +//=================================================================================== +struct NucleitpcPbPb { + + Preslice tracksPerCollision = aod::track::collisionId; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histomc{"histomc", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // ---- Event selection configurables ------------------------------------ + Configurable removeNoSameBunchPileup{"removeNoSameBunchPileup", false, "Remove no same bunch pileup"}; + Configurable requireIsGoodZvtxFT0vsPV{"requireIsGoodZvtxFT0vsPV", false, "Require is good Zvtx FT0 vs PV"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "Require is vertex ITS TPC"}; + Configurable removeNoTimeFrameBorder{"removeNoTimeFrameBorder", false, "Remove no time frame border"}; + Configurable cfgsel8Require{"cfgsel8Require", true, "sel8 cut require"}; + Configurable cfgZvertex{"cfgZvertex", 10, "Min Z Vertex"}; + + // ---- Track selection configurables ------------------------------------ + Configurable cfgUsePVcontributors{"cfgUsePVcontributors", true, "use tracks that are PV contibutors"}; + Configurable cfgPassedITSRefit{"cfgPassedITSRefit", true, "Require ITS refit"}; + Configurable occupancycutrequire{"occupancycutrequire", true, "Require occupancy cut"}; + Configurable cfgPassedTPCRefit{"cfgPassedTPCRefit", true, "Require TPC refit"}; + Configurable cfgetaRequire{"cfgetaRequire", true, "eta cut require"}; + Configurable cfgetaRequireMC{"cfgetaRequireMC", true, "eta cut require for generated particles"}; + Configurable cfgRapidityRequire{"cfgRapidityRequire", true, "Require Rapidity cut"}; + Configurable cfgRapidityRequireMC{"cfgRapidityRequireMC", true, "rapidity cut require for generated particles"}; + Configurable cfgCutEta{"cfgCutEta", 0.9f, "Eta range for tracks"}; + Configurable cfgCutRapiditymin{"cfgCutRapiditymin", -0.5f, "min Rapidity range"}; + Configurable cfgCutRapiditymax{"cfgCutRapiditymax", 0.5f, "max Rapidity range"}; + Configurable cfgtpcNClsFindable{"cfgtpcNClsFindable", 0.8f, "tpcNClsFindable over crossedRows"}; + Configurable centcut{"centcut", 80.0f, "centrality cut"}; + Configurable occupancycut{"occupancycut", 1000.0f, "occupancy cut"}; + Configurable cfgZvertexRequireMC{"cfgZvertexRequireMC", true, "Pos Z cut in MC"}; + + // ---- Track quality-cut configurables ----------------------------------- + Configurable cfgTPCNClsCrossedRowsRequire{"cfgTPCNClsCrossedRowsRequire", true, "Require TPCNClsCrossedRows Cut"}; + Configurable cfgmaxTPCchi2Require{"cfgmaxTPCchi2Require", true, "Require maxTPCchi2 Cut"}; + Configurable cfgminTPCchi2Require{"cfgminTPCchi2Require", true, "Require minTPCchi2 Cut"}; + Configurable cfgminITSnClsRequire{"cfgminITSnClsRequire", false, "Require minITSnCls Cut"}; + Configurable cfgminITSnClscosRequire{"cfgminITSnClscosRequire", true, "Require minITSnCls / cosh(eta) Cut"}; + Configurable cfgminReqClusterITSibRequire{"cfgminReqClusterITSibRequire", true, " Require min number of clusters required in ITS inner barrel"}; + Configurable cfgmaxITSchi2Require{"cfgmaxITSchi2Require", true, "Require maxITSchi2 Cut"}; + Configurable cfgmaxTPCnSigmaRequire{"cfgmaxTPCnSigmaRequire", true, "Require maxTPCnSigma Cut"}; + Configurable cfgminGetMeanItsClsSizeRequire{"cfgminGetMeanItsClsSizeRequire", true, "Require minGetMeanItsClsSize Cut"}; + Configurable cfgmaxGetMeanItsClsSizeRequire{"cfgmaxGetMeanItsClsSizeRequire", true, "Require maxGetMeanItsClsSize Cut"}; + + // ---- He4-specific configurables ----------------------------------------- + Configurable cfghe3massrejreq{"cfghe3massrejreq", true, "Require mass cut on He4 particles"}; + Configurable cfgminmassrejection{"cfgminmassrejection", 6.5, "Min side of He3 particle rejection"}; + Configurable cfgmaxmassrejection{"cfgmaxmassrejection", 9.138, "Max side of He3 particle rejection"}; + Configurable deuteronsigmarejection{"deuteronsigmarejection", 2.0f, "Deuteron TPC nsigma rejection for He4"}; + + // ---- MC-only configurables ----------------------------------------------- + Configurable cfgmccorrectionhe4Require{"cfgmccorrectionhe4Require", true, "MC correction for pp he4 particle"}; + Configurable cfgUseEvtSelInNumer{"cfgUseEvtSelInNumer", true, "Event-/signal-loss numerator: require the reco vertex to pass the event selection (true) or merely to exist (false)"}; + + // ---- DCA cut configurables ------------------------------------------------- + Configurable cfgUseDCAxyCorrection{"cfgUseDCAxyCorrection", true, "Use pT-dependent DCAxy cut"}; + Configurable cfgUseDCAzCorrection{"cfgUseDCAzCorrection", true, "Use pT-dependent DCAz cut"}; + Configurable cfgMultiplyTransportPt{"cfgMultiplyTransportPt", true, "In the DCA templates (processDCA), double the reconstructed pT for transport (material) secondaries; if false, use pT as reconstructed"}; + + // ---- Misc configurables ----------------------------------------------------- + Configurable cfgDebug{"cfgDebug", 1, "debug level"}; + Configurable cfgRigidityCorrection{"cfgRigidityCorrection", false, "apply rigidity correction"}; + Configurable cfgRequirebetaplot{"cfgRequirebetaplot", true, "Require beta plot"}; + Configurable cfgmass2{"cfgmass2", true, "Fill mass square difference"}; + Configurable cfgFillmass{"cfgFillmass", false, "Fill mass histograms"}; + Configurable cfgFillmassnsigma{"cfgFillmassnsigma", true, "Fill mass vs nsigma histograms"}; + + // ---- Table-valued configurables (species x parameter) ------------------ + Configurable> cfgBetheBlochParams{"cfgBetheBlochParams", {kBetheBlochDefault[0].data(), NParticles, NBetheParams, particleNames, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for light nuclei"}; + Configurable> cfgTrackPIDsettings{"cfgTrackPIDsettings", {kTrackPIDSettings[0].data(), NParticles, NTrkSettings, particleNames, trackPIDsettingsNames}, "track selection and PID criteria"}; + Configurable> cfgTrackPIDsettings2{"cfgTrackPIDsettings2", {kTrackPIDSettings2[0].data(), NParticles, NTrkSettings2, particleNames, trackPIDsettingsNames2}, "track selection and PID criteria"}; + Configurable> cfgktrackcorrection{"cfgktrackcorrection", {ktrackcorrection[0].data(), NFittingParticle, NFittingParameters, correctedparticleNames, trackcorrectionNames}, "fitting parameters"}; + Configurable> cfgDCAcorrection{"cfgDCAcorrection", {kDCAcorrection[0].data(), NDCATypes, NDCAParameters, dcaTypeNames, dcaNames}, "DCA parameters: DCAxy: p0*exp(p1*pt)+p2, DCAz: p0*exp(-p1*pt)+p2"}; + + o2::track::TrackParametrizationWithError mTrackParCov; + + // ---- Binning configuration ----------------------------------------------- + ConfigurableAxis axisMagField{"axisMagField", {10, -10., 10.}, "magnetic field"}; + ConfigurableAxis axisNev{"axisNev", {10, 0., 10.}, "Number of events"}; + ConfigurableAxis axisRigidity{"axisRigidity", {4000, -10., 10.}, "#it{p}^{TPC}/#it{z}"}; + ConfigurableAxis axisdEdx{"axisdEdx", {4000, 0, 4000}, "d#it{E}/d#it{x}"}; + ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality"}; + ConfigurableAxis axisOccupancy{"axisOccupancy", {5000, 0, 50000}, "axis for Occupancy of event"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {120, -20, 20}, "z"}; + ConfigurableAxis ptAxis{"ptAxis", {200, 0, 10}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axiseta{"axiseta", {100, -1, 1}, "eta"}; + ConfigurableAxis axisrapidity{"axisrapidity", {100, -2, 2}, "rapidity"}; + ConfigurableAxis axismass{"axismass", {1200, 0, 12}, "mass"}; + ConfigurableAxis axismassnsigma{"axismassnsigma", {1200, 0, 12}, "nsigma mass"}; + ConfigurableAxis nsigmaAxis{"nsigmaAxis", {160, -10, 10}, "n#sigma_{#pi^{+}}"}; + ConfigurableAxis axisDCA{"axisDCA", {400, -10., 10.}, "DCA axis"}; + ConfigurableAxis particleAntiAxis{"particleAntiAxis", {2, -0.5, 1.5}, "Particle/Anti-particle"}; // 0 = particle, 1 = anti-particle + ConfigurableAxis decayTypeAxis{"decayTypeAxis", {3, -0.5, 2.5}, "Decay type"}; // 0 = primary, 1 = from decay, 2 = material + ConfigurableAxis axisMultMC{"axisMultMC", {500, 0., 5000.}, "N_{ch}^{gen} in |#eta| < 0.5"}; + ConfigurableAxis speciesAxis{"speciesAxis", {NParticles, -0.5, NParticles - 0.5}, "species index"}; + ConfigurableAxis axisImpactPar{"axisImpactPar", {200, 0., 20.}, "impact parameter #it{b} (fm)"}; + + // ---- CCDB ------------------------------------------------------------------- + Service ccdb; + Configurable bField{"bField", -999, "bz field, -999 is automatic"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable pidPath{"pidPath", "", "Path to the PID response object"}; + + std::vector primaryParticles; + std::vector primVtx, cents; + bool collHasCandidate = false; + bool collPassedEvSel = false; + int mRunNumber = 0; + int occupancy = 0; + float dBz = 0.0f; + TRandom3 rand; + + // Species indices into primaryParticles / particlePdgCodes (order fixed by particleNames above). + const float proton = 1; + const float he3 = 4; + const float he4 = 5; + + //---------------------------------------------------------------------------------------------------------------- + void init(InitContext const&) + { + mRunNumber = 0; + dBz = 0; + rand.SetSeed(0); + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + for (int i = 0; i < NParticles; i++) { + primaryParticles.push_back(PrimParticles(particleNames.at(i), particlePdgCodes.at(i), particleMasses.at(i), particleCharge.at(i), cfgBetheBlochParams)); + } + + if (doprocessData) { + histos.add("histNev", "histNev", kTH1F, {axisNev}); + histos.add("histVtxZ", "histVtxZ", kTH1F, {axisVtxZ}); + histos.add("histCentFT0C", "histCentFT0C", kTH1F, {axisCent}); + histos.add("histCentFT0M", "histCentFT0M", kTH1F, {axisCent}); + histos.add("histCentFTOC_cut", "histCentFTOC_cut", kTH1F, {axisCent}); + histos.add("hSpectra", " ", HistType::kTHnSparseF, {ptAxis, nsigmaAxis, {5, -2.5, 2.5}, axisCent}); + histos.add("hSpectratof", " ", HistType::kTHnSparseF, {ptAxis, nsigmaAxis, {5, -2.5, 2.5}, axisCent}); + histos.add("DCAxy_vs_pT_data", "DCA_{xy} vs p_{T} for He3 (Data);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + histos.add("DCAxy_vs_pT_anti_data", "DCA_{xy} vs p_{T} for anti-He3 (Data);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + + hmass.resize(2 * NParticles + 2); + hmassnsigma.resize(2 * NParticles + 2); + + for (int i = 0; i < NParticles; i++) { + TString histName = primaryParticles[i].name; + if (cfgFillmass) { + hmass[2 * i] = histos.add(Form("histmass_pt/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}; centrality(%)", HistType::kTH3F, {ptAxis, axismass, axisCent}); + hmass[2 * i + 1] = histos.add(Form("histmass_ptanti/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}; centrality(%)", HistType::kTH3F, {ptAxis, axismass, axisCent}); + } + } + for (int i = 0; i < NParticles; i++) { + TString histName = primaryParticles[i].name; + if (cfgFillmassnsigma) { + hmassnsigma[2 * i] = histos.add(Form("histmass_nsigma/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}/z^{2}; Centrality(%)", HistType::kTH3F, {ptAxis, axismassnsigma, axisCent}); + hmassnsigma[2 * i + 1] = histos.add(Form("histmass_nsigmaanti/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}/z^{2}; centrality(%)", HistType::kTH3F, {ptAxis, axismassnsigma, axisCent}); + } + } + + histos.add("histeta", "histeta", kTH1F, {axiseta}); + histos.add("histEvents", "histEvents", kTH2F, {axisCent, axisOccupancy}); + } + + histos.add("dcaZ", "dcaZ", kTH2F, {ptAxis, axisDCA}); + histos.add("dcaXY", "dcaXY", kTH2F, {ptAxis, axisDCA}); + histos.add("Tofsignal", "Tofsignal", kTH2F, {axisRigidity, {4000, 0.2, 1.2, "#beta"}}); + histos.add("Tpcsignal", "Tpcsignal", kTH2F, {axisRigidity, axisdEdx}); + + if (doprocessMC) { + histomc.add("hSpectramc", " ", HistType::kTHnSparseF, {particleAntiAxis, speciesAxis, axisCent, ptAxis, ptAxis}); + + // Efficiency x Acceptance + histomc.add("hDenomEffAcc", "Denominator for Efficiency x Acceptance", + {HistType::kTHnSparseF, {ptAxis, axisCent, speciesAxis, particleAntiAxis, decayTypeAxis}}); + histomc.add("hNumerEffAcc", "Numerator for Efficiency x Acceptance", + {HistType::kTHnSparseF, {ptAxis, axisCent, speciesAxis, particleAntiAxis, decayTypeAxis}}); + + // ---- Event loss, multiplicity method ------------------------------------- + histomc.add("hEventLossDenomVsMult", + "All generated events;N_{ch}^{gen} (|#eta|<0.5)", + kTH1F, + {axisMultMC}); + + histomc.add("hEventLossNumerVsMult", + "Generated events with a selected reco vertex;N_{ch}^{gen} (|#eta|<0.5)", + kTH1F, + {axisMultMC}); + + // ---- Event loss, impact-parameter method --------------------------------- + histomc.add("hEventLossDenomVsImpactPar", + "All generated events;#it{b} (fm)", + kTH1F, + {axisImpactPar}); + + histomc.add("hEventLossNumerVsImpactPar", + "Generated events with a selected reco vertex;#it{b} (fm)", + kTH1F, + {axisImpactPar}); + + // ---- Event splitting, Eq. (14) of the analysis note ----------------------- + // splitting = hEventSplittingNgenWithRecoVsCent / hEventSplittingNrecoPassedVsCent + histomc.add("hEventSplittingNgenWithRecoVsCent", + "N_{gen. evt}^{#geq 1 reco evt passed evtsel};centrality (%)", + kTH1F, + {axisCent}); + + histomc.add("hEventSplittingNrecoPassedVsCent", + "N_{reco evt passed evtsel};centrality (%)", + kTH1F, + {axisCent}); + + // ---- Signal loss, multiplicity method: mult vs pT vs species -------------- + histomc.add("hSignalLossDenomVsMult", + ";N_{ch}^{gen};#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisMultMC, ptAxis, speciesAxis}); + + histomc.add("hSignalLossNumerVsMult", + ";N_{ch}^{gen};#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisMultMC, ptAxis, speciesAxis}); + + histomc.add("haSignalLossDenomVsMult", + ";N_{ch}^{gen};#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisMultMC, ptAxis, speciesAxis}); + + histomc.add("haSignalLossNumerVsMult", + ";N_{ch}^{gen};#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisMultMC, ptAxis, speciesAxis}); + + // ---- Signal loss, impact-parameter method: b vs pT vs species ------------- + histomc.add("hSignalLossDenomVsImpactPar", + ";#it{b} (fm);#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisImpactPar, ptAxis, speciesAxis}); + + histomc.add("hSignalLossNumerVsImpactPar", + ";#it{b} (fm);#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisImpactPar, ptAxis, speciesAxis}); + + histomc.add("haSignalLossDenomVsImpactPar", + ";#it{b} (fm);#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisImpactPar, ptAxis, speciesAxis}); + + histomc.add("haSignalLossNumerVsImpactPar", + ";#it{b} (fm);#it{p}_{T} (GeV/#it{c});species", + kTH3F, + {axisImpactPar, ptAxis, speciesAxis}); + + // ---- Maps from the generated quantities to centrality --------------------- + histomc.add("hCentralityVsGenMultEta05", + "FT0C centrality vs generated multiplicity;centrality (%);N_{ch}^{gen} (|#eta|<0.5)", + kTH2F, + {axisCent, axisMultMC}); + + histomc.add("hCentralityVsImpactParameter", + "FT0C centrality vs impact parameter;centrality (%);#it{b} (fm)", + kTH2F, + {axisCent, axisImpactPar}); + + histomc.add("hImpactParameter", + "Impact parameter of generated MC events;#it{b} (fm)", + kTH1F, + {axisImpactPar}); + + histomc.add("histVtxZgen", "histVtxZgen", kTH1F, {axisVtxZ}); + histomc.add("histVtxZReco", "histVtxZReco", kTH1F, {axisVtxZ}); + + histomc.add("histDeltaPtVsPtGenanti", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10}, {1000, -0.5, 0.5, "p_{T}(reco) - p_{T}(gen);p_{T}(reco)"}}); + histomc.add("histDeltaPtVsPtGen", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10}, {1000, -0.5, 0.5, "p_{T}(reco) - p_{T}(gen);p_{T}(reco)"}}); + histomc.add("histPIDtrack", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10, "p_{T}(reco)"}, {9, -0.5, 8.5, "p_{T}(reco) - p_{T}(gen)"}}); + histomc.add("histPIDtrackanti", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10, "p_{T}(reco)"}, {9, -0.5, 8.5, "p_{T}(reco) - p_{T}(gen)"}}); + + // Mass^2/z^2 3D plots for MC (pT, mass^2/z^2, centrality) - separate for particles and anti-particles + histomc.add("hMassVsPtMC", "mass^{2}/z^{2} vs p_{T} (MC Particles);p_{T} (GeV/c);mass^{2}/z^{2};Centrality", + {HistType::kTH3F, {ptAxis, axismassnsigma, axisCent}}); + histomc.add("hMassVsPtAntiMC", "mass^{2}/z^{2} vs p_{T} (MC Anti-particles);p_{T} (GeV/c);mass^{2}/z^{2};Centrality", + {HistType::kTH3F, {ptAxis, axismassnsigma, axisCent}}); + } + + if (doprocessDCA) { + histomc.add("DCAxy_vs_pT_primary", "DCA_{xy} vs p_{T} (primary);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + histomc.add("DCAxy_vs_pT_weakdecay", "DCA_{xy} vs p_{T} (Weak Decay);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + histomc.add("DCAxy_vs_pT_transport", "DCA_{xy} vs p_{T} (Transport);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + histomc.add("DCAxy_vs_pT_anti_primary", "DCA_{xy} vs p_{T} for anti (primary);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + histomc.add("DCAxy_vs_pT_anti_weakdecay", "DCA_{xy} vs p_{T} for anti (Weak Decay);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + histomc.add("DCAxy_vs_pT_anti_transport", "DCA_{xy} vs p_{T} for anti (Transport);p_{T} (GeV/c);DCA_{xy} (cm)", + {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); + } + } + + //==================================================================================== + // processData: reconstructed data, fills TPC(+TOF) PID spectra and DCA/mass QA + //==================================================================================== + void processData(CollisionsFull const& collisions, + TracksFull const& tracks, + aod::BCsWithTimestamps const&) + { + for (const auto& collision : collisions) { + auto bc = collision.bc_as(); + initCCDB(bc); + initCollision(collision); + + if (!collPassedEvSel) + continue; + if (removeNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) + continue; + histos.fill(HIST("histNev"), 2.5); + + if (requireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) + continue; + histos.fill(HIST("histNev"), 3.5); + if (requireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) + continue; + + histos.fill(HIST("histNev"), 4.5); + if (removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + histos.fill(HIST("histNev"), 5.5); + histos.fill(HIST("histEvents"), collision.centFT0C(), occupancy); + histos.fill(HIST("histVtxZ"), collision.posZ()); + histos.fill(HIST("histCentFT0C"), collision.centFT0C()); + histos.fill(HIST("histCentFT0M"), collision.centFT0M()); + if (collision.centFT0C() > centcut) + continue; + histos.fill(HIST("histNev"), 6.5); + if (occupancycutrequire && (occupancy > occupancycut)) + continue; + histos.fill(HIST("histNev"), 7.5); + histos.fill(HIST("histCentFTOC_cut"), collision.centFT0C()); + + auto tracksInColl = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + for (const auto& track : tracksInColl) { + if (!passesBasicRecoTrackCuts(track)) + continue; + + for (size_t i = 0; i < primaryParticles.size(); i++) { + if (cfgTrackPIDsettings2->get(i, "fillsparsh") != 1) + continue; + + float ptMomn = computeSpeciesPt(track, i); + float tpcNsigma = getTPCnSigma(track, primaryParticles.at(i)); + if ((std::abs(tpcNsigma) > cfgTrackPIDsettings->get(i, "maxTPCnSigma")) && cfgmaxTPCnSigmaRequire) + continue; + + int sign = (track.sign() > 0) ? 1 : ((track.sign() < 0) ? -1 : 0); + float rapidity = getRapidity(track, i); + if ((rapidity < cfgCutRapiditymin || rapidity > cfgCutRapiditymax) && cfgRapidityRequire) + continue; + + if (!passesTrackQualityCuts(track, i)) + continue; + if (!passesDCACuts(track, i, ptMomn)) + continue; + + float itsSigma = getITSnSigma(track, primaryParticles.at(i)); + if (itsSigma < cfgTrackPIDsettings2->get(i, "minITSnsigma") && cfgTrackPIDsettings2->get(i, "useITSnsigma") < 1) + continue; + if (itsSigma > cfgTrackPIDsettings2->get(i, "maxITSnsigma") && cfgTrackPIDsettings2->get(i, "useITSnsigma") < 1) + continue; + + histos.fill(HIST("Tpcsignal"), getRigidity(track) * track.sign(), track.tpcSignal()); + histos.fill(HIST("dcaXY"), ptMomn, track.dcaXY()); + histos.fill(HIST("dcaZ"), ptMomn, track.dcaZ()); + + float tofnsigma = getTOFnSigma(track, primaryParticles.at(i)); + if (track.sign() > 0) { + histos.fill(HIST("DCAxy_vs_pT_data"), ptMomn, track.dcaXY(), collision.centFT0C()); + } else if (track.sign() < 0) { + histos.fill(HIST("DCAxy_vs_pT_anti_data"), ptMomn, track.dcaXY(), collision.centFT0C()); + } + + // He4 selection uses the deuteron TPC nsigma to reject deuterons. + float tpcNsigmaDe = track.tpcNSigmaDe(); + + if (i != he4) { + histos.fill(HIST("hSpectra"), ptMomn, tpcNsigma, sign, collision.centFT0C()); + if (track.hasTOF()) { + histos.fill(HIST("hSpectratof"), ptMomn, tofnsigma, sign, collision.centFT0C()); + } + } else if (!track.hasTOF()) { + if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { + histos.fill(HIST("hSpectra"), ptMomn, tpcNsigma, sign, collision.centFT0C()); + } + } else { + // Has TOF - apply He4 mass rejection. + float beta = o2::pid::tof::Beta::GetBeta(track); + const float eps = 1e-6f; + if (beta < eps || beta > 1.0f - eps) + continue; + + float charge = 2.f; // he4 has charge 2 + float p = getRigidity(track); + float massTOF = p * charge * std::sqrt(1.f / (beta * beta) - 1.f); + if (cfghe3massrejreq && (massTOF * massTOF > cfgminmassrejection && massTOF * massTOF < cfgmaxmassrejection)) { + continue; // reject He4 mass window + } + if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { + histos.fill(HIST("hSpectra"), ptMomn, tpcNsigma, sign, collision.centFT0C()); + } + } + + if (std::abs(tpcNsigma) > cfgTrackPIDsettings2->get(i, "maxTPCnsigmaTOF")) { + if (i == he4) { + if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { + fillhmassnsigma(track, i, collision.centFT0C()); + } + } else { + fillhmassnsigma(track, i, collision.centFT0C()); + } + } + + if ((std::abs(tpcNsigma) > cfgTrackPIDsettings2->get(i, "maxTPCnsigmaTOF")) && cfgTrackPIDsettings2->get(i, "useTPCnsigmaTOF") < 1) + continue; + + if (i == he4) { + if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { + fillhmass(track, i, collision.centFT0C()); + } + } else if (i == proton && (std::abs(tofnsigma) < cfgTrackPIDsettings2->get(i, "maxTOFnsigma"))) { + fillhmassnsigma(track, i, collision.centFT0C()); + } else { + fillhmass(track, i, collision.centFT0C()); + } + + if (cfgRequirebetaplot) { + histos.fill(HIST("Tofsignal"), getRigidity(track) * track.sign(), o2::pid::tof::Beta::GetBeta(track)); + } + } // species loop + + histos.fill(HIST("histeta"), track.eta()); + } // track loop + } // collision loop + } + PROCESS_SWITCH(NucleitpcPbPb, processData, "data analysis", false); + + //==================================================================================== + // processMC: MC reco+gen, for efficiency x acceptance, signal-loss, event-loss and + // event-splitting corrections. + // + // Two event classes, used consistently everywhere: + // truth class = generated collisions passing the generated |z| cut + // selected class = truth class + >= 1 reco vertex passing evtsel and reco |z| cut + // + // event loss = numer / denom, binned in multMCNParticlesEta05 (multiplicity + // method) and in the impact parameter (impact-parameter method) + // event splitting = N_gen(>=1 selected reco) / N_reco(passed evtsel), Eq. (14) + // signal loss = generated primaries of the selected class over the truth class + // + // Everything per-species sits inside a fillsparsh loop, as in processData; event + // loss and event splitting are species independent. + //==================================================================================== + struct McCollInfo { + bool hasRecoCollision = false; // >= 1 reco vertex, before any selection + bool passedEvSel = false; // >= 1 reco vertex passing event selection + bool passedEvSelVtZ = false; // ... and passing the reco |z| cut + bool genVtxZok = false; // generated |z| inside the cut + float centrality = -1.0f; // FT0C of the highest-numContrib selected vertex + float mult = -1.0f; // multMCNParticlesEta05 + float impactPar = -1.0f; // generated impact parameter (fm) + int bestCollisionId = -1; // globalIndex of that vertex (split-vertex safe) + int biggestNContribs = -1; + }; + std::vector mcCollInfos; + + void processMC(CollisionsFullMC const& collisions, + McCollisionMults const& mcCollisions, + soa::Join const& tracks, + aod::McParticles const& particlesMC, + aod::BCsWithTimestamps const&) + { + mcCollInfos.clear(); + mcCollInfos.resize(mcCollisions.size()); + + //-------------------------------------------------------------------------------- + // PASS 1: generated collisions. Generated multiplicity, impact parameter and the + // generated |z| cut. Runs first so genVtxZok is available to every later pass, + // including the splitting numerator which is filled per reconstructed vertex. + //-------------------------------------------------------------------------------- + for (auto const& mcCollision : mcCollisions) { + size_t idx = mcCollision.globalIndex(); + if (idx >= mcCollInfos.size()) + continue; + + auto& info = mcCollInfos[idx]; + info.mult = static_cast(mcCollision.multMCNParticlesEta05()); + info.impactPar = mcCollision.impactParameter(); + + histomc.fill(HIST("histVtxZgen"), mcCollision.posZ()); + + if (cfgZvertexRequireMC && std::abs(mcCollision.posZ()) > cfgZvertex) + continue; + info.genVtxZok = true; + } + + //-------------------------------------------------------------------------------- + // PASS 2: reco -> MC association. Flags are only ever raised, so several reco + // vertices pointing at the same MC collision collapse correctly instead of the + // last one overwriting the earlier ones. The reco |z| cut is applied + // unconditionally, as in processData. + //-------------------------------------------------------------------------------- + for (auto const& collision : collisions) { + int mcCollIdx = collision.mcCollisionId(); + if (mcCollIdx < 0 || mcCollIdx >= static_cast(mcCollInfos.size())) + continue; + + auto& info = mcCollInfos[mcCollIdx]; + info.hasRecoCollision = true; + + if (!collision.sel8() && cfgsel8Require) + continue; + if (removeNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) + continue; + if (requireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) + continue; + if (requireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) + continue; + if (removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + info.passedEvSel = true; + + if (std::abs(collision.posZ()) > cfgZvertex) + continue; + info.passedEvSelVtZ = true; + + // Splitting numerator: every selected reco vertex, inside the truth class only. + if (info.genVtxZok) { + histomc.fill(HIST("hEventSplittingNrecoPassedVsCent"), collision.centFT0C()); + } + + if (collision.numContrib() > info.biggestNContribs) { + info.biggestNContribs = collision.numContrib(); + info.centrality = collision.centFT0C(); + info.bestCollisionId = collision.globalIndex(); + } + } + + //-------------------------------------------------------------------------------- + // PASS 3: event level. Species independent -- no fillsparsh loop here. + // Event loss in both methods, splitting denominator, and the two maps used to + // express the generated-quantity-binned corrections as a function of centrality. + //-------------------------------------------------------------------------------- + for (auto const& mcCollision : mcCollisions) { + size_t idx = mcCollision.globalIndex(); + if (idx >= mcCollInfos.size()) + continue; + + auto const& info = mcCollInfos[idx]; + if (!info.genVtxZok) + continue; + + bool numerOk = cfgUseEvtSelInNumer ? info.passedEvSelVtZ : info.hasRecoCollision; + + histomc.fill(HIST("hImpactParameter"), info.impactPar); + + // Event loss: the denominator is every generated event of the truth class, + // including those with no reconstructed vertex at all -- that population is + // exactly what is being corrected for. + histomc.fill(HIST("hEventLossDenomVsMult"), info.mult); + histomc.fill(HIST("hEventLossDenomVsImpactPar"), info.impactPar); + if (numerOk) { + histomc.fill(HIST("hEventLossNumerVsMult"), info.mult); + histomc.fill(HIST("hEventLossNumerVsImpactPar"), info.impactPar); + } + + if (info.passedEvSelVtZ) { + // Splitting denominator: generated events with >= 1 selected reco vertex. + histomc.fill(HIST("hEventSplittingNgenWithRecoVsCent"), info.centrality); + histomc.fill(HIST("hCentralityVsGenMultEta05"), info.centrality, info.mult); + histomc.fill(HIST("hCentralityVsImpactParameter"), info.centrality, info.impactPar); + } + } + + //-------------------------------------------------------------------------------- + // PASS 4: generated particles. Per-species, gated on fillsparsh exactly like + // processData. Signal loss (rapidity only, following the analysis note) and the + // efficiency x acceptance denominator. + //-------------------------------------------------------------------------------- + for (auto const& mcParticle : particlesMC) { + int mcCollIdx = mcParticle.mcCollisionId(); + if (mcCollIdx < 0 || mcCollIdx >= static_cast(mcCollInfos.size())) + continue; + + auto const& info = mcCollInfos[mcCollIdx]; + if (!info.genVtxZok) + continue; + + bool numerOk = cfgUseEvtSelInNumer ? info.passedEvSelVtZ : info.hasRecoCollision; + + int pdgCode = mcParticle.pdgCode(); + int particleAnti = (pdgCode > 0) ? 0 : 1; + + for (size_t i = 0; i < primaryParticles.size(); i++) { + if (cfgTrackPIDsettings2->get(i, "fillsparsh") != 1) + continue; + if (std::abs(pdgCode) != std::abs(particlePdgCodes.at(i))) + continue; + + float rapidity = mcParticle.y(); + if ((rapidity < cfgCutRapiditymin || rapidity > cfgCutRapiditymax) && cfgRapidityRequireMC) + continue; + + float pt = mcParticle.pt(); + float fSpecies = static_cast(i); + + // ---- Signal loss: physical primaries only. Denominator = truth class, + // numerator = selected class. Same particle, same cuts, same iteration. + if (mcParticle.isPhysicalPrimary()) { + if (pdgCode > 0) { + histomc.fill(HIST("hSignalLossDenomVsMult"), info.mult, pt, fSpecies); + histomc.fill(HIST("hSignalLossDenomVsImpactPar"), info.impactPar, pt, fSpecies); + if (numerOk) { + histomc.fill(HIST("hSignalLossNumerVsMult"), info.mult, pt, fSpecies); + histomc.fill(HIST("hSignalLossNumerVsImpactPar"), info.impactPar, pt, fSpecies); + } + } else { + histomc.fill(HIST("haSignalLossDenomVsMult"), info.mult, pt, fSpecies); + histomc.fill(HIST("haSignalLossDenomVsImpactPar"), info.impactPar, pt, fSpecies); + if (numerOk) { + histomc.fill(HIST("haSignalLossNumerVsMult"), info.mult, pt, fSpecies); + histomc.fill(HIST("haSignalLossNumerVsImpactPar"), info.impactPar, pt, fSpecies); + } + } + } + + if (!info.passedEvSelVtZ) + continue; + if (std::abs(mcParticle.eta()) > cfgCutEta && cfgetaRequireMC) + continue; + int decayType = classifyDecayType(mcParticle); + if (decayType == DecayTypeTransport) + continue; + histomc.fill(HIST("hDenomEffAcc"), pt, info.centrality, fSpecies, particleAnti, decayType); + } // species loop + } // generated particle loop + + //-------------------------------------------------------------------------------- + // PASS 5: reconstructed tracks. Only the highest-numContrib selected vertex of + // each generated collision is used, so split vertices are not counted twice. + //-------------------------------------------------------------------------------- + for (auto const& collision : collisions) { + int mcCollIdx = collision.mcCollisionId(); + if (mcCollIdx < 0 || mcCollIdx >= static_cast(mcCollInfos.size())) + continue; + + auto const& info = mcCollInfos[mcCollIdx]; + if (!info.genVtxZok) + continue; + if (collision.globalIndex() != info.bestCollisionId) + continue; + + auto bc = collision.bc_as(); + initCCDB(bc); + histomc.fill(HIST("histVtxZReco"), collision.posZ()); + + auto tracksInColl = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + for (auto const& track : tracksInColl) { + if (!track.has_mcParticle()) + continue; // skip un-matched reco tracks + + auto const& matchedMCParticle = track.mcParticle_as(); + if (matchedMCParticle.mcCollisionId() != mcCollIdx) + continue; + + int pdg = matchedMCParticle.pdgCode(); + int decayType = classifyDecayType(matchedMCParticle); + + if (!passesBasicRecoTrackCuts(track)) + continue; + + for (size_t i = 0; i < primaryParticles.size(); i++) { + if (cfgTrackPIDsettings2->get(i, "fillsparsh") != 1) + continue; + if (std::abs(pdg) != std::abs(particlePdgCodes.at(i))) + continue; + + float ptReco = computeSpeciesPt(track, i); + ptReco = applyHe34PtCorrection(track, pdg, i, ptReco); + + int particleAnti = (pdg > 0) ? 0 : 1; + float fSpecies = static_cast(i); + + float rapidity = getRapidity(track, i); + if ((rapidity < cfgCutRapiditymin || rapidity > cfgCutRapiditymax) && cfgRapidityRequire) + continue; + + if (!passesTrackQualityCuts(track, i)) + continue; + if (!passesDCACuts(track, i, ptReco)) + continue; + + float tpcNsigma = getTPCnSigma(track, primaryParticles.at(i)); + bool passesNsigma = !((std::abs(tpcNsigma) > cfgTrackPIDsettings->get(i, "maxTPCnSigma")) && cfgmaxTPCnSigmaRequire); + + // Single fill per track: axis pTReco always gets the reco pT, with all + // cuts applied except TPC nsigma and hasTOF -- this is the denominator + // population for the TOF matching efficiency. Axis pTOF gets the same pT + // value only for tracks that both pass the TPC nsigma cut and have TOF -- + // the numerator population -- otherwise it gets a sentinel (-1, landing + // in the pTOF axis underflow bin since ptAxis starts at 0) so it's + // excluded from any real-pT projection of that axis. Filling once (not + // twice) avoids double-counting TOF-matched tracks in the pTReco axis + // relative to non-matched tracks. + bool isTOFmatched = passesNsigma && track.hasTOF(); + float ptTOF = isTOFmatched ? ptReco : -1.0f; + histomc.fill(HIST("hSpectramc"), particleAnti, fSpecies, collision.centFT0C(), ptReco, ptTOF); + + if (!passesNsigma) + continue; + + // Efficiency x Acceptance numerator. Same transport veto as the + // denominator, otherwise the ratio mixes two different populations. + if (decayType == DecayTypeTransport) + continue; + histomc.fill(HIST("hNumerEffAcc"), ptReco, collision.centFT0C(), fSpecies, particleAnti, decayType); + + if (decayType == DecayTypePrimary) { + histos.fill(HIST("dcaXY"), ptReco, track.dcaXY()); + histos.fill(HIST("dcaZ"), ptReco, track.dcaZ()); + histos.fill(HIST("Tpcsignal"), getRigidity(track) * track.sign(), track.tpcSignal()); + } + + float ptGen = matchedMCParticle.pt(); + float deltaPt = ptReco - ptGen; + if (pdg == -particlePdgCodes.at(i) && decayType == DecayTypePrimary) { // anti-particle + histomc.fill(HIST("histDeltaPtVsPtGenanti"), ptReco, deltaPt); + histomc.fill(HIST("histPIDtrackanti"), ptReco, track.pidForTracking()); + } + if (pdg == particlePdgCodes.at(i) && decayType == DecayTypePrimary) { // particle + histomc.fill(HIST("histDeltaPtVsPtGen"), ptReco, deltaPt); + histomc.fill(HIST("histPIDtrack"), ptReco, track.pidForTracking()); + } + + // Mass^2/z^2 for MC, split by particle/anti-particle. + if (track.hasTOF()) { + float beta = o2::pid::tof::Beta::GetBeta(track); + const float eps = 1e-6f; + if (beta >= eps && beta <= 1.0f - eps) { + float charge = (i == he3 || i == he4) ? 2.f : 1.f; + float p = getRigidity(track); + float massTOF = p * charge * std::sqrt(1.f / (beta * beta) - 1.f); + float massSquareOverChargeSquare = (massTOF * massTOF) / (charge * charge); + + bool skipHe4 = (std::abs(pdg) == particlePdgCodes.at(5)) && cfghe3massrejreq && + (massTOF * massTOF > cfgminmassrejection && massTOF * massTOF < cfgmaxmassrejection); + + if (!skipHe4 && decayType == DecayTypePrimary) { + if (pdg > 0) { + histomc.fill(HIST("hMassVsPtMC"), ptReco, massSquareOverChargeSquare, collision.centFT0C()); + } else { + histomc.fill(HIST("hMassVsPtAntiMC"), ptReco, massSquareOverChargeSquare, collision.centFT0C()); + } + } + } + } + } // species loop + } // track loop + } // collision loop + } + PROCESS_SWITCH(NucleitpcPbPb, processMC, "MC reco+gen analysis with efficiency corrections", false); + + //==================================================================================== + // processDCA: MC reco, builds DCAxy templates (primary / weak-decay / transport) + // used for the secondary-contamination correction. Note: no DCA cut is applied + // here, since the DCA distribution itself is what is being measured. + //==================================================================================== + void processDCA(CollisionsFullMC const& collisions, + aod::McCollisions const& mcCollisions, + aod::McParticles const& particlesMC, + soa::Join const& tracks, + aod::BCsWithTimestamps const&) + { + (void)particlesMC; + mcCollInfos.clear(); + mcCollInfos.resize(mcCollisions.size()); + + for (auto const& collision : collisions) { + int mcCollIdx = collision.mcCollisionId(); + if (mcCollIdx < 0 || mcCollIdx >= static_cast(mcCollisions.size())) { + continue; + } + + mcCollInfos[mcCollIdx].centrality = collision.centFT0C(); + + if (!collision.sel8() && cfgsel8Require) + continue; + if (collision.centFT0C() > centcut) + continue; + if (removeNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) + continue; + if (requireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) + continue; + if (requireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) + continue; + if (removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + mcCollInfos[mcCollIdx].passedEvSel = true; + + if (std::abs(collision.posZ()) > cfgZvertex && cfgZvertexRequireMC) + continue; + mcCollInfos[mcCollIdx].passedEvSelVtZ = true; + } + + for (auto const& mcCollision : mcCollisions) { + size_t idx = mcCollision.globalIndex(); + if (idx >= mcCollInfos.size()) + continue; + if (!mcCollInfos[idx].passedEvSelVtZ) + continue; + + for (auto const& collision : collisions) { + if (collision.mcCollisionId() != static_cast(idx)) + continue; + + auto bc = collision.bc_as(); + initCCDB(bc); + auto tracksInColl = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); + + for (auto const& track : tracksInColl) { + if (!track.has_mcParticle()) + continue; + + auto const& matchedMCParticle = track.mcParticle_as(); + if (matchedMCParticle.mcCollisionId() != mcCollision.globalIndex()) + continue; + + int pdg = matchedMCParticle.pdgCode(); + if (!passesBasicRecoTrackCuts(track)) + continue; + + for (size_t i = 0; i < primaryParticles.size(); i++) { + if (std::abs(pdg) != std::abs(particlePdgCodes.at(i))) + continue; + if (cfgTrackPIDsettings2->get(i, "fillsparsh") != 1) + continue; + + float ptReco = computeSpeciesPt(track, i); + ptReco = applyHe34PtCorrection(track, pdg, i, ptReco); + + float rapidity = getRapidity(track, i); + if ((rapidity < cfgCutRapiditymin || rapidity > cfgCutRapiditymax) && cfgRapidityRequire) + continue; + + if (!passesTrackQualityCuts(track, i)) + continue; + + float tpcNsigma = getTPCnSigma(track, primaryParticles.at(i)); + if ((std::abs(tpcNsigma) > cfgTrackPIDsettings->get(i, "maxTPCnSigma")) && cfgmaxTPCnSigmaRequire) + continue; + + // Decay-origin classification specific to the DCA-template building: + // physical primaries and weak decays (via getProcess()==kPDecay) are + // distinguished from transport/material secondaries. + int decayType = -999; + if (matchedMCParticle.isPhysicalPrimary()) { + decayType = DecayTypePrimary; + if (matchedMCParticle.has_mothers()) { + for (const auto& motherparticle : matchedMCParticle.mothers_as()) { + if (std::find(hfMothCodes.begin(), hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != hfMothCodes.end()) { + decayType = DecayTypeWeak; + break; + } + } + } + } else if (matchedMCParticle.getProcess() == TMCProcess::kPDecay) { + if (!matchedMCParticle.has_mothers()) { + continue; // secondaries from weak decay without mothers are skipped + } + decayType = DecayTypeWeak; + } else { + decayType = DecayTypeTransport; + } + + // Transport (material) secondaries optionally get their reconstructed + // pT doubled for the DCA template, per cfgMultiplyTransportPt; primary + // and weak-decay secondaries always use pT as reconstructed. + float ptTransport = cfgMultiplyTransportPt ? 2.f * ptReco : ptReco; + + if (pdg == particlePdgCodes.at(i)) { // particle + if (decayType == DecayTypePrimary) { + histomc.fill(HIST("DCAxy_vs_pT_primary"), ptReco, track.dcaXY(), collision.centFT0C()); + } else if (decayType == DecayTypeWeak) { + histomc.fill(HIST("DCAxy_vs_pT_weakdecay"), ptReco, track.dcaXY(), collision.centFT0C()); + } else if (decayType == DecayTypeTransport) { + histomc.fill(HIST("DCAxy_vs_pT_transport"), ptTransport, track.dcaXY(), collision.centFT0C()); + } + } else if (pdg == -particlePdgCodes.at(i)) { // anti-particle + if (decayType == DecayTypePrimary) { + histomc.fill(HIST("DCAxy_vs_pT_anti_primary"), ptReco, track.dcaXY(), collision.centFT0C()); + } else if (decayType == DecayTypeWeak) { + histomc.fill(HIST("DCAxy_vs_pT_anti_weakdecay"), ptReco, track.dcaXY(), collision.centFT0C()); + } else if (decayType == DecayTypeTransport) { + histomc.fill(HIST("DCAxy_vs_pT_anti_transport"), ptTransport, track.dcaXY(), collision.centFT0C()); + } + } + } // species loop + } // track loop + break; // matching reconstructed collision found and processed + } // collision loop + } // mcCollision loop + } + PROCESS_SWITCH(NucleitpcPbPb, processDCA, "MC DCA analysis For secondary correction", false); + + //==================================================================================== + // Shared per-track helper cuts (extracted from the near-identical blocks that used + // to be duplicated across processData / processMC / processDCA). + //==================================================================================== + + /// Basic reconstructed-track quality gate: PV contributor, has TPC, ITS/TPC refit, + /// eta acceptance. Identical requirement in all three process functions. + template + bool passesBasicRecoTrackCuts(T const& track) + { + if (!track.isPVContributor() && cfgUsePVcontributors) + return false; + if (!track.hasTPC()) + return false; + if (!track.passedITSRefit() && cfgPassedITSRefit) + return false; + if (!track.passedTPCRefit() && cfgPassedTPCRefit) + return false; + if (std::abs(track.eta()) > cfgCutEta && cfgetaRequire) + return false; + return true; + } + + /// Species-dependent TPC/ITS cluster-quality cuts (no DCA cut here -- processDCA + /// deliberately does not cut on DCA since it is measuring the DCA distribution). + template + bool passesTrackQualityCuts(T const& track, size_t i) + { + if (track.tpcNClsFound() < cfgTrackPIDsettings->get(i, "minTPCnCls")) + return false; + if (((track.tpcNClsCrossedRows() < cfgTrackPIDsettings->get(i, "minTPCnClsCrossedRows")) || + track.tpcNClsCrossedRows() < cfgtpcNClsFindable * track.tpcNClsFindable()) && + cfgTPCNClsCrossedRowsRequire) + return false; + if (track.tpcChi2NCl() > cfgTrackPIDsettings->get(i, "maxTPCchi2") && cfgmaxTPCchi2Require) + return false; + if (track.tpcChi2NCl() < cfgTrackPIDsettings->get(i, "minTPCchi2") && cfgminTPCchi2Require) + return false; + if (track.itsNCls() < cfgTrackPIDsettings->get(i, "minITSnCls") && cfgminITSnClsRequire) + return false; + double cosheta = std::cosh(track.eta()); + if ((track.itsNCls() / cosheta) < cfgTrackPIDsettings->get(i, "minITSnClscos") && cfgminITSnClscosRequire) + return false; + if ((track.itsNClsInnerBarrel() < cfgTrackPIDsettings->get(i, "minReqClusterITSib")) && cfgminReqClusterITSibRequire) + return false; + if (track.itsChi2NCl() > cfgTrackPIDsettings->get(i, "maxITSchi2") && cfgmaxITSchi2Require) + return false; + if (getMeanItsClsSize(track) < cfgTrackPIDsettings->get(i, "minITSclsSize") && cfgminGetMeanItsClsSizeRequire) + return false; + return true; + } + + /// pT-dependent DCAxy/DCAz cuts: sigma(pt) = p0*exp(+/-p1*pt) + p2, scaled by the + /// per-species sigma factor. Used by processData and processMC (not processDCA). + template + bool passesDCACuts(T const& track, size_t i, float pt) + { + double dcaXYp0 = cfgDCAcorrection->get(0U, "p0"); + double dcaXYp1 = cfgDCAcorrection->get(0U, "p1"); + double dcaXYp2 = cfgDCAcorrection->get(0U, "p2"); + double dcaZp0 = cfgDCAcorrection->get(1U, "p0"); + double dcaZp1 = cfgDCAcorrection->get(1U, "p1"); + double dcaZp2 = cfgDCAcorrection->get(1U, "p2"); + + bool insideDCAxy; + if (cfgUseDCAxyCorrection) { + double sigma = cfgTrackPIDsettings->get(i, "maxDcaXY") * (dcaXYp0 * std::exp(dcaXYp1 * pt) + dcaXYp2); + insideDCAxy = std::abs(track.dcaXY()) <= sigma; + } else { + insideDCAxy = std::abs(track.dcaXY()) <= cfgTrackPIDsettings->get(i, "maxDcaXY"); + } + + bool insideDCAz; + if (cfgUseDCAzCorrection) { + double sigma = cfgTrackPIDsettings->get(i, "maxDcaZ") * (dcaZp0 * std::exp(-dcaZp1 * pt) + dcaZp2); + insideDCAz = std::abs(track.dcaZ()) <= sigma; + } else { + insideDCAz = std::abs(track.dcaZ()) <= cfgTrackPIDsettings->get(i, "maxDcaZ"); + } + + return insideDCAxy && insideDCAz; + } + + /// Base candidate pT for species i: He3/He4 have charge 2, so the tracking pT + /// (computed for charge 1) is doubled. + template + float computeSpeciesPt(T const& track, size_t i) + { + setTrackParCov(track, mTrackParCov); + mTrackParCov.setPID(track.pidForTracking()); + return (i == he3 || i == he4) ? 2.f * mTrackParCov.getPt() : mTrackParCov.getPt(); + } + + /// MC-only pT correction for He3/He4 reconstructed tracks (empirical a/b/c + /// parameterisation per species/charge, plus an antitriton-mistag correction + /// for He3). No-op when cfgmccorrectionhe4Require is false or species is + /// neither He3 nor He4. + template + float applyHe34PtCorrection(T const& track, int pdg, size_t i, float ptIn) + { + if (!cfgmccorrectionhe4Require) + return ptIn; + + double a = 0, b = 0, c = 0; + int param = -1; + if (i == he3) { + param = (pdg > 0) ? 0 : 1; + } else if (i == he4) { + param = (pdg > 0) ? 2 : 3; + } + if (param >= 0) { + a = cfgktrackcorrection->get(param, "a"); + b = cfgktrackcorrection->get(param, "b"); + c = cfgktrackcorrection->get(param, "c"); + } + + float ptOut = ptIn; + if (std::abs(pdg) == particlePdgCodes.at(5)) { // He4/alpha + ptOut = ptOut + a + b * std::exp(c * ptOut); + } + if (std::abs(pdg) == particlePdgCodes.at(4)) { // He3/helion, antitriton-mistag correction + constexpr int AntiTritonPid = 6; + if (track.pidForTracking() == AntiTritonPid) { + ptOut = ptOut - a + b * ptOut - c * ptOut * ptOut; + } + } + return ptOut; + } + + /// Look up the primaryParticles/particlePdgCodes index matching an (MC) pdg code, + /// or -1 if none of the configured species match. + int findParticleTypeIndex(int pdgCode) + { + for (size_t j = 0; j < primaryParticles.size(); j++) { + if (std::abs(pdgCode) == std::abs(particlePdgCodes.at(j))) + return static_cast(j); + } + return -1; + } + + /// True if particleType is a valid species index with fillsparsh enabled. + bool speciesEnabled(int particleType) + { + return particleType >= 0 && cfgTrackPIDsettings2->get(particleType, "fillsparsh") == 1; + } + + /// Decay-origin classification shared by processMC's generated-particle loop and + /// reconstructed-track loop (identical logic in both places). + template + int classifyDecayType(McPart const& mcPart) + { + int decayType; + if (mcPart.isPhysicalPrimary()) { + decayType = DecayTypePrimary; + if (mcPart.has_mothers()) { + for (const auto& motherparticle : mcPart.template mothers_as()) { + if (std::find(hfMothCodes.begin(), hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != hfMothCodes.end()) { + decayType = DecayTypeWeak; + break; + } + } + } + } else if (mcPart.has_mothers()) { + decayType = DecayTypeWeak; + } else { + decayType = DecayTypeTransport; + } + return decayType; + } + + //==================================================================================== + // CCDB / event / mass-histogram helper functions + //==================================================================================== + + void initCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + constexpr float InvalidBField = -990.f; + auto run3grpTimestamp = bc.timestamp(); + dBz = 0; + auto* grpo = ccdb->getForTimeStamp(grpPath, run3grpTimestamp); + if (grpo) { + o2::base::Propagator::initFieldFromGRP(grpo); + if (bField < InvalidBField) { + dBz = grpo->getNominalL3Field(); + LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; + } else { + dBz = bField; + } + } else { + auto* grpmag = ccdb->getForTimeStamp(grpmagPath, run3grpTimestamp); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grpTimestamp; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + if (bField < InvalidBField) { + dBz = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; + } else { + dBz = bField; + } + } + mRunNumber = bc.runNumber(); + } + + template + void initCollision(const T& collision) + { + collHasCandidate = false; + histos.fill(HIST("histNev"), 0.5); + collPassedEvSel = collision.sel8() && std::abs(collision.posZ()) < cfgZvertex; + occupancy = collision.trackOccupancyInTimeRange(); + if (collPassedEvSel) { + histos.fill(HIST("histNev"), 1.5); + } + primVtx.assign({collision.posX(), collision.posY(), collision.posZ()}); + cents.assign({collision.centFT0A(), collision.centFT0C(), collision.centFT0M()}); + } + + template + void fillhmass(T const& track, int species, float cent) + { + if (!track.hasTOF() || !cfgFillmass) + return; + + float beta{o2::pid::tof::Beta::GetBeta(track)}; + const float eps = 1e-6f; + if (beta < eps || beta > 1.0f - eps) + return; + + float charge = (species == he3 || species == he4) ? 2.f : 1.f; + float p = getRigidity(track); + float massTOF = p * charge * std::sqrt(1.f / (beta * beta) - 1.f); + + if (species == he4 && cfghe3massrejreq && (massTOF * massTOF > cfgminmassrejection && massTOF * massTOF < cfgmaxmassrejection)) + return; + + float massDiff = cfgmass2 ? (massTOF * massTOF) / (charge * charge) : massTOF / charge; + + float ptMomn = computeSpeciesPt(track, species); + if (track.sign() > 0) { + hmass[2 * species]->Fill(ptMomn, massDiff, cent); + } else if (track.sign() < 0) { + hmass[2 * species + 1]->Fill(ptMomn, massDiff, cent); + } + } + + template + void fillhmassnsigma(T const& track, int species, float cent) + { + if (!track.hasTOF() || !cfgFillmassnsigma) + return; + + float beta{o2::pid::tof::Beta::GetBeta(track)}; + const float eps = 1e-6f; + if (beta < eps || beta > 1.0f - eps) + return; + + float charge = (species == he3 || species == he4) ? 2.f : 1.f; + float p = getRigidity(track); + float massTOF = p * charge * std::sqrt(1.f / (beta * beta) - 1.f); + float massSquareOverChargeSquare = (massTOF * massTOF) / (charge * charge); + + if (species == he4 && cfghe3massrejreq && (massTOF * massTOF > cfgminmassrejection && massTOF * massTOF < cfgmaxmassrejection)) + return; + + float ptMomn = computeSpeciesPt(track, species); + if (track.sign() > 0) { + hmassnsigma[2 * species]->Fill(ptMomn, massSquareOverChargeSquare, cent); + } else if (track.sign() < 0) { + hmassnsigma[2 * species + 1]->Fill(ptMomn, massSquareOverChargeSquare, cent); + } + } + + //==================================================================================== + // PID / kinematics helper functions + //==================================================================================== + + template + float getTPCnSigma(T const& track, PrimParticles& particle) + { + const float rigidity = getRigidity(track); + if (!track.hasTPC()) + return -999; + if (particle.name == "pion" && cfgTrackPIDsettings->get("pion", "useBBparams") < 1) + return cfgTrackPIDsettings->get("pion", "useBBparams") == 0 ? track.tpcNSigmaPi() : 0; + if (particle.name == "proton" && cfgTrackPIDsettings->get("proton", "useBBparams") < 1) + return cfgTrackPIDsettings->get("proton", "useBBparams") == 0 ? track.tpcNSigmaPr() : 0; + if (particle.name == "deuteron" && cfgTrackPIDsettings->get("deuteron", "useBBparams") < 1) + return cfgTrackPIDsettings->get("deuteron", "useBBparams") == 0 ? track.tpcNSigmaDe() : 0; + if (particle.name == "triton" && cfgTrackPIDsettings->get("triton", "useBBparams") < 1) + return cfgTrackPIDsettings->get("triton", "useBBparams") == 0 ? track.tpcNSigmaTr() : 0; + if (particle.name == "helion" && cfgTrackPIDsettings->get("helion", "useBBparams") < 1) + return cfgTrackPIDsettings->get("helion", "useBBparams") == 0 ? track.tpcNSigmaHe() : 0; + if (particle.name == "alpha" && cfgTrackPIDsettings->get("alpha", "useBBparams") < 1) + return cfgTrackPIDsettings->get("alpha", "useBBparams") == 0 ? track.tpcNSigmaAl() : 0; + + double expBethe{common::BetheBlochAleph(static_cast(particle.charge * rigidity / particle.mass), particle.betheParams[0], particle.betheParams[1], particle.betheParams[2], particle.betheParams[3], particle.betheParams[4])}; + double expSigma{expBethe * particle.resolution}; + return static_cast((track.tpcSignal() - expBethe) / expSigma); + } + + /// TOF n-sigma for the given species, mirroring getTPCnSigma's species + /// dispatch. Returns -999 if the track has no TOF signal. + template + float getTOFnSigma(T const& track, PrimParticles& particle) + { + if (!track.hasTOF()) + return -999; + if (particle.name == "pion") + return track.tofNSigmaPi(); + if (particle.name == "proton") + return track.tofNSigmaPr(); + if (particle.name == "deuteron") + return track.tofNSigmaDe(); + if (particle.name == "triton") + return track.tofNSigmaTr(); + if (particle.name == "helion") + return track.tofNSigmaHe(); + if (particle.name == "alpha") + return track.tofNSigmaAl(); + return -999; // fallback if no match + } + + template + float getITSnSigma(T const& track, PrimParticles& particle) + { + if (!track.hasITS()) + return -999; + o2::aod::ITSResponse itsResponse; + if (particle.name == "pion") + return itsResponse.nSigmaITS(track); + if (particle.name == "proton") + return itsResponse.nSigmaITS(track); + if (particle.name == "deuteron") + return itsResponse.nSigmaITS(track); + if (particle.name == "triton") + return itsResponse.nSigmaITS(track); + if (particle.name == "helion") + return itsResponse.nSigmaITS(track); + if (particle.name == "alpha") + return itsResponse.nSigmaITS(track); + return -999; // fallback if no match + } + + template + float getMeanItsClsSize(T const& track) + { + int sum = 0, n = 0; + constexpr int NITSLayers = 8; + for (int i = 0; i < NITSLayers; i++) { + sum += (track.itsClusterSizes() >> (4 * i) & 15); + if (track.itsClusterSizes() >> (4 * i) & 15) + n++; + } + return n > 0 ? static_cast(sum) / n : 0.f; + } + + template + float getRigidity(T const& track) + { + if (!cfgRigidityCorrection) + return track.tpcInnerParam(); + bool hePID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; + return hePID ? track.tpcInnerParam() / 2 : track.tpcInnerParam(); + } + + template + float getRapidity(T const& track, int species) + { + using PtEtaPhiMVector = ROOT::Math::LorentzVector>; + int speciesHe3 = 4; + int speciesHe4 = 5; + double momn = (species == speciesHe3 || species == speciesHe4) ? 2 * track.pt() : track.pt(); + PtEtaPhiMVector lorentzVectorParticle(momn, track.eta(), track.phi(), particleMasses[species]); + return lorentzVectorParticle.Rapidity(); + } +}; // end of the task here + +//---------------------------------------------------------------------------------------------------------------- +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Nuspex/nucleitpcpbpb.cxx b/PWGLF/Tasks/Nuspex/nucleitpcpbpb.cxx deleted file mode 100644 index b5c0d0fc948..00000000000 --- a/PWGLF/Tasks/Nuspex/nucleitpcpbpb.cxx +++ /dev/null @@ -1,1368 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file nucleitpcpbpb.cxx -/// \brief nuclei analysis -/// \note under work -/// -/// \author Jaideep Tanwar , Panjab University - -#include "Common/CCDB/EventSelectionParams.h" -#include "Common/Core/PID/PIDTOF.h" -#include "Common/Core/trackUtilities.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseITS.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using CollisionsFull = soa::Join; -using TracksFull = soa::Join; -using CollisionsFullMC = soa::Join; -//--------------------------------------------------------------------------------------------------------------------------------- -namespace -{ -static const int nParticles = 6; -static const std::vector particleNames{"pion", "proton", "deuteron", "triton", "helion", "alpha"}; -static const std::vector correctedparticleNames{"helion", "antihelion", "alpha", "antialpha"}; -static const std::vector particlePdgCodes{211, 2212, o2::constants::physics::kDeuteron, o2::constants::physics::kTriton, o2::constants::physics::kHelium3, o2::constants::physics::kAlpha}; -static const std::vector particleMasses{o2::constants::physics::MassPionCharged, o2::constants::physics::MassProton, o2::constants::physics::MassDeuteron, o2::constants::physics::MassTriton, o2::constants::physics::MassHelium3, o2::constants::physics::MassAlpha}; -static const std::vector particleCharge{1, 1, 1, 1, 2, 2}; -const int nBetheParams = 6; -std::vector hfMothCodes = {511, 521, 531, 541, 5122}; // b-mesons + Lambda_b -static const std::vector betheBlochParNames{"p0", "p1", "p2", "p3", "p4", "resolution"}; -constexpr double kBetheBlochDefault[nParticles][nBetheParams]{ - {13.611469, 3.598765, -0.021138, 2.039562, 0.651040, 0.09}, // pion - {5.393020, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, // proton - {5.393020, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, // deuteron - {5.393020, 7.859534, 0.004048, 2.323197, 1.609307, 0.09}, // triton - {-126.557359, -0.858569, 1.111643, 1.210323, 2.656374, 0.09}, // helion - {-126.557359, -0.858569, 1.111643, 1.210323, 2.656374, 0.09}}; // alpha -const int nTrkSettings = 13; -static const std::vector trackPIDsettingsNames{"useBBparams", "minITSnCls", "minITSnClscos", "minTPCnCls", "maxTPCchi2", "minTPCchi2", "maxITSchi2", "maxTPCnSigma", "maxDcaXY", "maxDcaZ", "minITSclsSize", "minTPCnClsCrossedRows", "minReqClusterITSib"}; -constexpr double kTrackPIDSettings[nParticles][nTrkSettings]{ - {0, 0, 4, 60, 4.0, 0.5, 100, 2.5, 2., 2., 0., 70, 1}, - {1, 0, 4, 70, 4.0, 0.5, 100, 3.0, 2., 2., 0., 70, 1}, - {1, 0, 4, 70, 4.0, 0.5, 100, 3.0, 2., 2., 0., 70, 1}, - {1, 0, 4, 70, 4.0, 0.5, 100, 3.0, 2., 2., 0., 70, 1}, - {1, 0, 4, 75, 4.0, 0.5, 100, 5.0, 2., 2., 0., 70, 1}, - {1, 0, 4, 70, 4.0, 0.5, 100, 5.0, 2., 2., 0., 70, 1}}; - -const int nTrkSettings2 = 6; -static const std::vector trackPIDsettingsNames2{"useITSnsigma", "minITSnsigma", "maxITSnsigma", "fillsparsh", "useTPCnsigmaTOF", "maxTPCnsigmaTOF"}; -constexpr double kTrackPIDSettings2[nParticles][nTrkSettings2]{ - {1, -5, 4, 0, 1, 2}, - {1, -5, 4, 0, 1, 2}, - {1, -5, 4, 0, 1, 2}, - {1, -5, 4, 1, 1, 2}, - {1, -5, 4, 1, 1, 2}, - {1, -5, 4, 1, 1, 2}}; - -static const int nfittingparticle = 4; -const int nfittingparameters = 4; -static const std::vector trackcorrectionNames{"correctionneed", "a", "b", "c"}; -constexpr double ktrackcorrection[nfittingparticle][nfittingparameters]{ - {1, 0.464215, 0.195771, 0.0183111}, // He3 - {1, 0.464215, 0.195771, 0.0183111}, // anti-He3 - {1, 0.00765, 0.503791, -1.10517}, // He4 - {1, 0.00765, 0.503791, -1.10517}}; // anti-He4 - -struct PrimParticles { - TString name; - int pdgCode, charge; - double mass, resolution; - std::vector betheParams; - bool active; - PrimParticles(std::string name_, int pdgCode_, double mass_, int charge_, LabeledArray bethe) : name(name_), pdgCode(pdgCode_), charge(charge_), mass(mass_), active(false) - { - resolution = bethe.get(name, "resolution"); - betheParams.clear(); - constexpr unsigned int kNSpecies = 5; - for (unsigned int i = 0; i < kNSpecies; i++) - betheParams.push_back(bethe.get(name, i)); - } -}; // struct PrimParticles - -//---------------------------------------------------------------------------------------------------------------- -std::vector> hmass; -std::vector> hmassnsigma; -} // namespace -//---------------------------------------------------------------------------------------------------------------- -struct NucleitpcPbPb { - - Preslice tracksPerCollision = aod::track::collisionId; - - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry histomc{"histomc", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - - // Event Selection Configurables - Configurable removeNoSameBunchPileup{"removeNoSameBunchPileup", false, "Remove no same bunch pileup"}; - Configurable requireIsGoodZvtxFT0vsPV{"requireIsGoodZvtxFT0vsPV", false, "Require is good Zvtx FT0 vs PV"}; - Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "Require is vertex ITS TPC"}; - Configurable removeNoTimeFrameBorder{"removeNoTimeFrameBorder", false, "Remove no time frame border"}; - Configurable cfgsel8Require{"cfgsel8Require", true, "sel8 cut require"}; - Configurable cfgZvertex{"cfgZvertex", 10, "Min Z Vertex"}; - - // Track Selection Configurables - Configurable cfgUsePVcontributors{"cfgUsePVcontributors", true, "use tracks that are PV contibutors"}; - Configurable cfgPassedITSRefit{"cfgPassedITSRefit", true, "Require ITS refit"}; - Configurable cfgPassedTPCRefit{"cfgPassedTPCRefit", true, "Require TPC refit"}; - Configurable cfgetaRequire{"cfgetaRequire", true, "eta cut require"}; - Configurable cfgetaRequireMC{"cfgetaRequireMC", true, "eta cut require for generated particles"}; - Configurable cfgRapidityRequire{"cfgRapidityRequire", true, "Require Rapidity cut"}; - Configurable cfgRapidityRequireMC{"cfgRapidityRequireMC", true, "rapidity cut require for generated particles"}; - Configurable cfgCutEta{"cfgCutEta", 0.9f, "Eta range for tracks"}; - Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "Rapidity range"}; - Configurable cfgtpcNClsFindable{"cfgtpcNClsFindable", 0.8f, "tpcNClsFindable over crossedRows"}; - Configurable centcut{"centcut", 80.0f, "centrality cut"}; - Configurable cfgZvertexRequireMC{"cfgZvertexRequireMC", true, "Pos Z cut in MC"}; - Configurable cfgdcaxynopt{"cfgdcaxynopt", true, "DCA xy cut without pT dependent"}; - - // Track Cut Configurables - Configurable cfgTPCNClsCrossedRowsRequire{"cfgTPCNClsCrossedRowsRequire", true, "Require TPCNClsCrossedRows Cut"}; - Configurable cfgmaxTPCchi2Require{"cfgmaxTPCchi2Require", true, "Require maxTPCchi2 Cut"}; - Configurable cfgminTPCchi2Require{"cfgminTPCchi2Require", true, "Require minTPCchi2 Cut"}; - Configurable cfgminITSnClsRequire{"cfgminITSnClsRequire", false, "Require minITSnCls Cut"}; - Configurable cfgminITSnClscosRequire{"cfgminITSnClscosRequire", true, "Require minITSnCls / cosh(eta) Cut"}; - Configurable cfgminReqClusterITSibRequire{"cfgminReqClusterITSibRequire", true, " Require min number of clusters required in ITS inner barrel"}; - Configurable cfgmaxITSchi2Require{"cfgmaxITSchi2Require", true, "Require maxITSchi2 Cut"}; - Configurable cfgmaxTPCnSigmaRequire{"cfgmaxTPCnSigmaRequire", true, "Require maxTPCnSigma Cut"}; - Configurable cfgminGetMeanItsClsSizeRequire{"cfgminGetMeanItsClsSizeRequire", true, "Require minGetMeanItsClsSize Cut"}; - Configurable cfgmaxGetMeanItsClsSizeRequire{"cfgmaxGetMeanItsClsSizeRequire", true, "Require maxGetMeanItsClsSize Cut"}; - - // He4 Configurables - Configurable cfghe3massrejreq{"cfghe3massrejreq", true, "Require mass cut on He4 particles"}; - Configurable cfgminmassrejection{"cfgminmassrejection", 6.5, "Min side of He3 particle rejection"}; - Configurable cfgmaxmassrejection{"cfgmaxmassrejection", 9.138, "Max side of He3 particle rejection"}; - Configurable deuteronsigmarejection{"deuteronsigmarejection", 2.0f, "Deuteron TPC nsigma rejection for He4"}; - - // MC Configurables - Configurable cfgmccorrectionhe4Require{"cfgmccorrectionhe4Require", true, "MC correction for pp he4 particle"}; - Configurable correctionsigma{"correctionsigma", 2, "Max sigma value outside which correction is require"}; - Configurable pTcorrectHe4{"pTcorrectHe4", 2, "Max pT below which correction is require in He4"}; - Configurable pTcorrectHe3{"pTcorrectHe3", 2, "Max pT value below which correction is require in He3"}; - - // DCA Configurables - Configurable cfgUseNewDCAxyCut{"cfgUseNewDCAxyCut", false, "Use new pT-dependent DCAxy sigma cut for He3"}; - Configurable cfgUseNewDCAzCut{"cfgUseNewDCAzCut", false, "Use new pT-dependent DCAz sigma cut for He3"}; - - Configurable cfgDebug{"cfgDebug", 1, "debug level"}; - Configurable cfgRigidityCorrection{"cfgRigidityCorrection", false, "apply rigidity correction"}; - Configurable cfgRequirebetaplot{"cfgRequirebetaplot", true, "Require beta plot"}; - Configurable cfgmass2{"cfgmass2", true, "Fill mass square difference"}; - Configurable cfgFillmass{"cfgFillmass", false, "Fill mass histograms"}; - Configurable cfgFillmassnsigma{"cfgFillmassnsigma", true, "Fill mass vs nsigma histograms"}; - - Configurable> cfgBetheBlochParams{"cfgBetheBlochParams", {kBetheBlochDefault[0], nParticles, nBetheParams, particleNames, betheBlochParNames}, "TPC Bethe-Bloch parameterisation for light nuclei"}; - Configurable> cfgTrackPIDsettings{"cfgTrackPIDsettings", {kTrackPIDSettings[0], nParticles, nTrkSettings, particleNames, trackPIDsettingsNames}, "track selection and PID criteria"}; - Configurable> cfgTrackPIDsettings2{"cfgTrackPIDsettings2", {kTrackPIDSettings2[0], nParticles, nTrkSettings2, particleNames, trackPIDsettingsNames2}, "track selection and PID criteria"}; - Configurable> cfgktrackcorrection{"cfgktrackcorrection", {ktrackcorrection[0], nfittingparticle, nfittingparameters, correctedparticleNames, trackcorrectionNames}, "fitting paramters"}; - - o2::track::TrackParametrizationWithError mTrackParCov; - // Binning configuration - ConfigurableAxis axisMagField{"axisMagField", {10, -10., 10.}, "magnetic field"}; - ConfigurableAxis axisNev{"axisNev", {10, 0., 10.}, "Number of events"}; - ConfigurableAxis axisRigidity{"axisRigidity", {4000, -10., 10.}, "#it{p}^{TPC}/#it{z}"}; - ConfigurableAxis axisdEdx{"axisdEdx", {4000, 0, 4000}, "d#it{E}/d#it{x}"}; - ConfigurableAxis axisCent{"axisCent", {100, 0, 100}, "centrality"}; - ConfigurableAxis axisOccupancy{"axisOccupancy", {5000, 0, 50000}, "axis for Occupancy of event"}; - ConfigurableAxis axisVtxZ{"axisVtxZ", {120, -20, 20}, "z"}; - ConfigurableAxis ptAxis{"ptAxis", {200, 0, 10}, "#it{p}_{T} (GeV/#it{c})"}; - ConfigurableAxis axiseta{"axiseta", {100, -1, 1}, "eta"}; - ConfigurableAxis axisrapidity{"axisrapidity", {100, -2, 2}, "rapidity"}; - ConfigurableAxis axismass{"axismass", {1200, 0, 12}, "mass"}; - ConfigurableAxis axismassnsigma{"axismassnsigma", {1200, 0, 12}, "nsigma mass"}; - ConfigurableAxis nsigmaAxis{"nsigmaAxis", {160, -10, 10}, "n#sigma_{#pi^{+}}"}; - ConfigurableAxis axisDCA{"axisDCA", {400, -10., 10.}, "DCA axis"}; - ConfigurableAxis particleAntiAxis{"particleAntiAxis", {2, -0.5, 1.5}, "Particle/Anti-particle"}; // 0 = particle, 1 = anti-particle - ConfigurableAxis decayTypeAxis{"decayTypeAxis", {3, -0.5, 2.5}, "Decay type"}; // 0 = primary, 1 = from decay, 2 = material - - // CCDB - Service ccdb; - Configurable bField{"bField", -999, "bz field, -999 is automatic"}; - Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; - Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; - Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; - Configurable pidPath{"pidPath", "", "Path to the PID response object"}; - - std::vector primaryParticles; - std::vector primVtx, cents; - bool collHasCandidate, collPassedEvSel; - int mRunNumber, occupancy; - float dBz; - TRandom3 rand; - - float de = 2; - float triton = 3; - float he3 = 4; - float he4 = 5; - //---------------------------------------------------------------------------------------------------------------- - void init(InitContext const&) - { - mRunNumber = 0; - dBz = 0; - rand.SetSeed(0); - ccdb->setURL(ccdbUrl); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setFatalWhenNull(false); - for (int i = 0; i < nParticles; i++) { // create primaryParticles - primaryParticles.push_back(PrimParticles(particleNames.at(i), particlePdgCodes.at(i), particleMasses.at(i), particleCharge.at(i), cfgBetheBlochParams)); - } - // create histograms - if (doprocessData) { - histos.add("histMagField", "histMagField", kTH1F, {axisMagField}); - histos.add("histNev", "histNev", kTH1F, {axisNev}); - histos.add("histVtxZ", "histVtxZ", kTH1F, {axisVtxZ}); - histos.add("histCentFT0C", "histCentFT0C", kTH1F, {axisCent}); - histos.add("histCentFT0M", "histCentFT0M", kTH1F, {axisCent}); - histos.add("histCentFTOC_cut", "histCentFTOC_cut", kTH1F, {axisCent}); - histos.add("hSpectra", " ", HistType::kTHnSparseF, {ptAxis, nsigmaAxis, {5, -2.5, 2.5}, axisCent}); - histos.add("DCAxy_vs_pT_data", "DCA_{xy} vs p_{T} for He3 (Data);p_{T} (GeV/c);DCA_{xy} (cm)", - {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); - histos.add("DCAxy_vs_pT_anti_data", "DCA_{xy} vs p_{T} for anti-He3 (Data);p_{T} (GeV/c);DCA_{xy} (cm)", - {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); - - hmass.resize(2 * nParticles + 2); - hmassnsigma.resize(2 * nParticles + 2); - - for (int i = 0; i < nParticles; i++) { - TString histName = primaryParticles[i].name; - if (cfgFillmass) { - hmass[2 * i] = histos.add(Form("histmass_pt/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}; centrality(%)", HistType::kTH3F, {ptAxis, axismass, axisCent}); - hmass[2 * i + 1] = histos.add(Form("histmass_ptanti/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}; centrality(%)", HistType::kTH3F, {ptAxis, axismass, axisCent}); - } - } - for (int i = 0; i < nParticles; i++) { - TString histName = primaryParticles[i].name; - if (cfgFillmassnsigma) { - hmassnsigma[2 * i] = histos.add(Form("histmass_nsigma/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}/z^{2}; Centrality(%)", HistType::kTH3F, {ptAxis, axismassnsigma, axisCent}); - hmassnsigma[2 * i + 1] = histos.add(Form("histmass_nsigmaanti/histmass_%s", histName.Data()), ";p_T{TPC} (GeV/#it{c}); mass^{2}/z^{2}; centrality(%)", HistType::kTH3F, {ptAxis, axismassnsigma, axisCent}); - } - } - - histos.add("histeta", "histeta", kTH1F, {axiseta}); - - histos.add("histEvents", "histEvents", kTH2F, {axisCent, axisOccupancy}); - } - - histos.add("dcaZ", "dcaZ", kTH2F, {ptAxis, axisDCA}); - histos.add("dcaXY", "dcaXY", kTH2F, {ptAxis, axisDCA}); - histos.add("Tofsignal", "Tofsignal", kTH2F, {axisRigidity, {4000, 0.2, 1.2, "#beta"}}); - histos.add("Tpcsignal", "Tpcsignal", kTH2F, {axisRigidity, axisdEdx}); - - if (doprocessMC) { - histomc.add("hSpectramc", " ", HistType::kTHnSparseF, {{5, -2.5, 2.5}, axisCent, ptAxis, ptAxis}); - - // Efficiency x Acceptance - histomc.add("hDenomEffAcc", "Denominator for Efficiency x Acceptance", - {HistType::kTHnSparseF, {ptAxis, axisCent, particleAntiAxis, decayTypeAxis}}); - histomc.add("hNumerEffAcc", "Numerator for Efficiency x Acceptance", - {HistType::kTHnSparseF, {ptAxis, axisCent, particleAntiAxis, decayTypeAxis}}); - - // The Signal loss correction - histomc.add("hSignalLossDenom", "Signal Loss Denominator", kTH2F, {axisCent, ptAxis}); - histomc.add("hSignalLossNumer", "Signal Loss Numerator", kTH2F, {axisCent, ptAxis}); - - histomc.add("haSignalLossDenom", "anti particle Signal Loss Denominator", kTH2F, {axisCent, ptAxis}); - histomc.add("haSignalLossNumer", "antiparticle Signal Loss Numerator", kTH2F, {axisCent, ptAxis}); - - // The event loss correction - histomc.add("hEventLossDenom", "Event loss denominator", kTH1F, {axisCent}); - histomc.add("hEventLossNumer", "Event loss numerator", kTH1F, {axisCent}); - - histomc.add("histVtxZgen", "histVtxZgen", kTH1F, {axisVtxZ}); - histomc.add("histVtxZReco", "histVtxZReco", kTH1F, {axisVtxZ}); - - histomc.add("histDeltaPtVsPtGenanti", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10}, {1000, -0.5, 0.5, "p_{T}(reco) - p_{T}(gen);p_{T}(reco)"}}); - histomc.add("histDeltaPtVsPtGen", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10}, {1000, -0.5, 0.5, "p_{T}(reco) - p_{T}(gen);p_{T}(reco)"}}); - histomc.add("histPIDtrack", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10, "p_{T}(reco)"}, {9, -0.5, 8.5, "p_{T}(reco) - p_{T}(gen)"}}); - histomc.add("histPIDtrackanti", " delta pt vs pt rec", HistType::kTH2F, {{1000, 0, 10, "p_{T}(reco)"}, {9, -0.5, 8.5, "p_{T}(reco) - p_{T}(gen)"}}); - } - - if (doprocessDCA) { - - // NEW: Add DCAxy vs pT histograms for MC - histomc.add("DCAxy_vs_pT_transport", "DCA_{xy} vs p_{T} (Transport);p_{T} (GeV/c);DCA_{xy} (cm)", - {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); - histomc.add("DCAxy_vs_pT_primary", "DCA_{xy} vs p_{T} (primary);p_{T} (GeV/c);DCA_{xy} (cm)", - {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); - histomc.add("DCAxy_vs_pT_weakdecay", "DCA_{xy} vs p_{T} (Weak Decay);p_{T} (GeV/c);DCA_{xy} (cm)", - {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); - histomc.add("DCAxy_vs_pT_total", "DCA_{xy} vs p_{T} (Total);p_{T} (GeV/c);DCA_{xy} (cm)", - {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); - histomc.add("DCAxy_vs_pT_anti_total", "DCA_{xy} vs p_{T} for anti (Total);p_{T} (GeV/c);DCA_{xy} (cm)", - {HistType::kTH3F, {ptAxis, axisDCA, axisCent}}); - } - } - //---------------------------------------------------------------------------------------------------------------- - //---------------------------------------------------------------------------------------------------------------- - void processData(CollisionsFull const& collisions, - TracksFull const& tracks, - aod::BCsWithTimestamps const&) - { - for (const auto& collision : collisions) { - auto bc = collision.bc_as(); - initCCDB(bc); - initCollision(collision); - - if (!collPassedEvSel) - continue; - if (removeNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) - continue; - histos.fill(HIST("histNev"), 2.5); - - if (requireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) - continue; - histos.fill(HIST("histNev"), 3.5); - if (requireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) - continue; - - histos.fill(HIST("histNev"), 4.5); - if (removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) - continue; - histos.fill(HIST("histNev"), 5.5); - histos.fill(HIST("histEvents"), collision.centFT0C(), occupancy); - histos.fill(HIST("histVtxZ"), collision.posZ()); - histos.fill(HIST("histCentFT0C"), collision.centFT0C()); - histos.fill(HIST("histCentFT0M"), collision.centFT0M()); - if (collision.centFT0C() > centcut) - continue; - histos.fill(HIST("histNev"), 6.5); - histos.fill(HIST("histCentFTOC_cut"), collision.centFT0C()); - // new slicing - auto tracksInColl = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); - // loop over sliced tracks - for (const auto& track : tracksInColl) { - if (!track.isPVContributor() && cfgUsePVcontributors) - continue; - if (!track.hasTPC()) - continue; - if (!track.passedITSRefit() && cfgPassedITSRefit) - continue; - if (!track.passedTPCRefit() && cfgPassedTPCRefit) - continue; - if (std::abs(track.eta()) > cfgCutEta && cfgetaRequire) - continue; - for (size_t i = 0; i < primaryParticles.size(); i++) { - if (cfgTrackPIDsettings2->get(i, "fillsparsh") != 1) - continue; - float ptMomn; - setTrackParCov(track, mTrackParCov); - mTrackParCov.setPID(track.pidForTracking()); - ptMomn = (i == he3 || i == he4) ? 2 * mTrackParCov.getPt() : mTrackParCov.getPt(); - float tpcNsigma = getTPCnSigma(track, primaryParticles.at(i)); - if ((std::abs(tpcNsigma) > cfgTrackPIDsettings->get(i, "maxTPCnSigma")) && cfgmaxTPCnSigmaRequire) - continue; - if (tpcNsigma < correctionsigma && cfgmccorrectionhe4Require) { - double a = 0, b = 0, c = 0; - - int param = -1; - if (i == he3) { - param = (track.sign() > 0) ? 0 : 1; - } else if (i == he4) { - param = (track.sign() > 0) ? 2 : 3; - } - if (param >= 0) { - a = cfgktrackcorrection->get(param, "a"); - b = cfgktrackcorrection->get(param, "b"); - c = cfgktrackcorrection->get(param, "c"); - } - if (i == he4 && ptMomn < pTcorrectHe4) { - ptMomn = ptMomn + a + b * std::exp(c * ptMomn); - } - if (i == he3 && ptMomn < pTcorrectHe3) { - ptMomn = ptMomn - a + b * ptMomn - c * ptMomn * ptMomn; - } - } - int sign = (track.sign() > 0) ? 1 : ((track.sign() < 0) ? -1 : 0); - if (std::abs(getRapidity(track, i)) > cfgCutRapidity && cfgRapidityRequire) - continue; - if (track.tpcNClsFound() < cfgTrackPIDsettings->get(i, "minTPCnCls")) - continue; - if (((track.tpcNClsCrossedRows() < cfgTrackPIDsettings->get(i, "minTPCnClsCrossedRows")) || - track.tpcNClsCrossedRows() < cfgtpcNClsFindable * track.tpcNClsFindable()) && - cfgTPCNClsCrossedRowsRequire) - continue; - if (track.tpcChi2NCl() > cfgTrackPIDsettings->get(i, "maxTPCchi2") && cfgmaxTPCchi2Require) - continue; - if (track.tpcChi2NCl() < cfgTrackPIDsettings->get(i, "minTPCchi2") && cfgminTPCchi2Require) - continue; - if (track.itsNCls() < cfgTrackPIDsettings->get(i, "minITSnCls") && cfgminITSnClsRequire) - continue; - double cosheta = std::cosh(track.eta()); - if ((track.itsNCls() / cosheta) < cfgTrackPIDsettings->get(i, "minITSnClscos") && cfgminITSnClscosRequire) - continue; - if ((track.itsNClsInnerBarrel() < cfgTrackPIDsettings->get(i, "minReqClusterITSib")) && cfgminReqClusterITSibRequire) - continue; - if (track.itsChi2NCl() > cfgTrackPIDsettings->get(i, "maxITSchi2") && cfgmaxITSchi2Require) - continue; - if (getMeanItsClsSize(track) < cfgTrackPIDsettings->get(i, "minITSclsSize") && cfgminGetMeanItsClsSizeRequire) - continue; - bool insideDCAxy = false; - if (cfgUseNewDCAxyCut) { - double sigmaFactor = cfgTrackPIDsettings->get(i, "maxDcaXY"); - double sigma_base = (0.0118 * std::exp(-0.6889 * ptMomn) + 0.0017); - double sigma_new = sigmaFactor * sigma_base; - - insideDCAxy = (std::abs(track.dcaXY()) <= sigma_new); - } else { - insideDCAxy = - cfgdcaxynopt - ? (std::abs(track.dcaXY()) <= cfgTrackPIDsettings->get(i, "maxDcaXY")) - : (std::abs(track.dcaXY()) <= - (cfgTrackPIDsettings->get(i, "maxDcaXY") * - (0.0105f + 0.0350f / std::pow(ptMomn, 1.1f)))); - } - - bool insideDCAz = false; - - if (cfgUseNewDCAzCut) { - double sigmaFactorZ = cfgTrackPIDsettings->get(i, "maxDcaZ"); - double p0 = 0.1014; - double p1 = 1.7512; - double p2 = 0.0024; - double sigma_base_z = p0 * std::exp(-p1 * ptMomn) + p2; - double sigma_new_z = sigmaFactorZ * sigma_base_z; - insideDCAz = (std::abs(track.dcaZ()) <= sigma_new_z); - } else { - insideDCAz = (std::abs(track.dcaZ()) <= cfgTrackPIDsettings->get(i, "maxDcaZ")); - } - - if ((!insideDCAxy || !insideDCAz)) { - continue; - } - - float itsSigma = getITSnSigma(track, primaryParticles.at(i)); - if (itsSigma < cfgTrackPIDsettings2->get(i, "minITSnsigma") && cfgTrackPIDsettings2->get(i, "useITSnsigma") < 1) - continue; - if (itsSigma > cfgTrackPIDsettings2->get(i, "maxITSnsigma") && cfgTrackPIDsettings2->get(i, "useITSnsigma") < 1) - continue; - - histos.fill(HIST("Tpcsignal"), getRigidity(track) * track.sign(), track.tpcSignal()); - histos.fill(HIST("dcaXY"), ptMomn, track.dcaXY()); - histos.fill(HIST("dcaZ"), ptMomn, track.dcaZ()); - - // Get deuteron TPC nsigma for He4 selection - float tpcNsigmaDe = -999; - if (i == he4) { - tpcNsigmaDe = track.tpcNSigmaDe(); - } - - if (track.sign() > 0) { - histos.fill(HIST("DCAxy_vs_pT_data"), ptMomn, track.dcaXY(), collision.centFT0C()); - } else if (track.sign() < 0) { - histos.fill(HIST("DCAxy_vs_pT_anti_data"), ptMomn, track.dcaXY(), collision.centFT0C()); - } - - if (i != he4) { - histos.fill(HIST("hSpectra"), ptMomn, tpcNsigma, sign, collision.centFT0C()); - } else { - if (!track.hasTOF()) { - if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { - histos.fill(HIST("hSpectra"), ptMomn, tpcNsigma, sign, collision.centFT0C()); - } - } else { - // Has TOF - apply mass cut - float beta = o2::pid::tof::Beta::GetBeta(track); - const float eps = 1e-6f; - if (beta < eps || beta > 1.0f - eps) - continue; - - float charge = 2.f; // he4 has charge 2 - float p = getRigidity(track); - float massTOF = p * charge * std::sqrt(1.f / (beta * beta) - 1.f); - - // Apply mass cut for he4 (mass^2 around 3.73^2 = 13.9) - if (cfghe3massrejreq && (massTOF * massTOF > cfgminmassrejection && massTOF * massTOF < cfgmaxmassrejection)) { - continue; // Skip if mass cut fails - } - if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { - histos.fill(HIST("hSpectra"), ptMomn, tpcNsigma, sign, collision.centFT0C()); - } - } - } - - if ((std::abs(tpcNsigma) > cfgTrackPIDsettings2->get(i, "maxTPCnsigmaTOF"))) { - if (i == he4) { - if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { - fillhmassnsigma(track, i, collision.centFT0C()); - } - } else { - fillhmassnsigma(track, i, collision.centFT0C()); - } - } - - if ((std::abs(tpcNsigma) > cfgTrackPIDsettings2->get(i, "maxTPCnsigmaTOF")) && cfgTrackPIDsettings2->get(i, "useTPCnsigmaTOF") < 1) - continue; - - if (i == he4) { - if (std::abs(tpcNsigmaDe) > deuteronsigmarejection) { - fillhmass(track, i, collision.centFT0C()); - } - } else { - fillhmass(track, i, collision.centFT0C()); - } - - if (cfgRequirebetaplot) { - histos.fill(HIST("Tofsignal"), getRigidity(track) * track.sign(), o2::pid::tof::Beta::GetBeta(track)); - } - } // loop primaryParticles - - histos.fill(HIST("histeta"), track.eta()); - } // loop sliced tracks - } // collision loop - } - PROCESS_SWITCH(NucleitpcPbPb, processData, "data analysis", false); - - //---------------------------------------------------------------------------------------------------------------- - // MC particles - Efficiency x Acceptance and Signal Loss calculations - //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - struct McCollInfo { - bool passedEvSel = false; - float centrality = -1.0f; - bool passedEvSelVtZ = false; - }; - std::vector mcCollInfos; - - void processMC(CollisionsFullMC const& collisions, - aod::McCollisions const& mcCollisions, - soa::Join const& tracks, - aod::McParticles const& particlesMC, - aod::BCsWithTimestamps const&) - - { - - mcCollInfos.clear(); - mcCollInfos.resize(mcCollisions.size()); - - // First pass: Store centrality and apply event selection - for (auto const& collision : collisions) { - int mcCollIdx = collision.mcCollisionId(); - if (mcCollIdx < 0 || mcCollIdx >= static_cast(mcCollisions.size())) { - continue; - } - - // STORE CENTRALITY WITHOUt CUTS - mcCollInfos[mcCollIdx].centrality = collision.centFT0C(); - - if (!collision.sel8() && cfgsel8Require) - continue; - if (collision.centFT0C() > centcut) - continue; - - if (removeNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) - continue; - if (requireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) - continue; - if (requireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) - continue; - if (removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) - continue; - - // Mark this MC collision as passing event selection - mcCollInfos[mcCollIdx].passedEvSel = true; - - // Apply event selection cuts - if (std::abs(collision.posZ()) > cfgZvertex && cfgZvertexRequireMC) - continue; - - mcCollInfos[mcCollIdx].passedEvSelVtZ = true; - } - - // FILL EVENT LOSS AND SIGNAL LOSS: Combined loop per MC collision - for (size_t i = 0; i < mcCollInfos.size(); i++) { - if (mcCollInfos[i].centrality >= 0) { // Only if we found a matching collision - // Event loss denominator (always filled regardless of fillsparsh) - histomc.fill(HIST("hEventLossDenom"), mcCollInfos[i].centrality); - - // Event loss numerator (if passed selection) - if (mcCollInfos[i].passedEvSel) { - histomc.fill(HIST("hEventLossNumer"), mcCollInfos[i].centrality); - } - - // Fill signal loss for all primary particles in this MC collision - for (auto const& mcParticle : particlesMC) { - if (mcParticle.mcCollisionId() != static_cast(i)) { - continue; - } - if (!mcParticle.isPhysicalPrimary()) { - continue; - } - - // Check if this particle type has fillsparsh enabled - int particleType = -1; - for (size_t j = 0; j < primaryParticles.size(); j++) { - if (std::abs(mcParticle.pdgCode()) == std::abs(particlePdgCodes.at(j))) { - particleType = j; - break; - } - } - - // Skip if fillsparsh is not enabled for this particle - if (particleType < 0 || cfgTrackPIDsettings2->get(particleType, "fillsparsh") != 1) - continue; - - // Signal loss denominator - if (mcParticle.pdgCode() == particlePdgCodes.at(particleType)) { // particle - histomc.fill(HIST("hSignalLossDenom"), mcCollInfos[i].centrality, mcParticle.pt()); - } else if (mcParticle.pdgCode() == -particlePdgCodes.at(particleType)) { // anti-particle - histomc.fill(HIST("haSignalLossDenom"), mcCollInfos[i].centrality, mcParticle.pt()); - } - // Signal loss numerator (if event passed selection) - if (mcCollInfos[i].passedEvSel) { - if (mcParticle.pdgCode() == particlePdgCodes.at(particleType)) { // particle - histomc.fill(HIST("hSignalLossNumer"), mcCollInfos[i].centrality, mcParticle.pt()); - } else if (mcParticle.pdgCode() == -particlePdgCodes.at(particleType)) { // anti-particle - histomc.fill(HIST("haSignalLossNumer"), mcCollInfos[i].centrality, mcParticle.pt()); - } - } - } - } - } - - // Process MC collisions for efficiency and reconstructed collisions - for (auto const& mcCollision : mcCollisions) { - size_t idx = mcCollision.globalIndex(); - if (idx >= mcCollInfos.size()) - continue; - - float centrality = mcCollInfos[idx].centrality; - bool passedEvSelVtZ = mcCollInfos[idx].passedEvSelVtZ; - - // Process generated particles for efficiency denominators - histomc.fill(HIST("histVtxZgen"), mcCollision.posZ()); - - for (auto const& mcParticle : particlesMC) { - if (mcParticle.mcCollisionId() != mcCollision.globalIndex()) - continue; - - int pdgCode = mcParticle.pdgCode(); - - // Check which particle type this is - int particleType = -1; - for (size_t j = 0; j < primaryParticles.size(); j++) { - if (std::abs(pdgCode) == std::abs(particlePdgCodes.at(j))) { - particleType = j; - break; - } - } - - // Only process if this particle type has fillsparsh enabled - if (particleType < 0 || cfgTrackPIDsettings2->get(particleType, "fillsparsh") != 1) - continue; - - if (std::abs(mcParticle.eta()) > cfgCutEta && cfgetaRequireMC) - continue; - if (std::abs(mcParticle.y()) > cfgCutRapidity && cfgRapidityRequireMC) - continue; - - int decayType = 0; - int particleAnti = (pdgCode > 0) ? 0 : 1; - - if (mcParticle.isPhysicalPrimary()) { - decayType = 0; - if (mcParticle.has_mothers()) { - for (const auto& motherparticle : mcParticle.mothers_as()) { - if (std::find(hfMothCodes.begin(), hfMothCodes.end(), - std::abs(motherparticle.pdgCode())) != hfMothCodes.end()) { - decayType = 1; - break; - } - } - } - } else if (mcParticle.has_mothers()) { - decayType = 1; - } else { - decayType = 2; - continue; - } - - // Efficiency x Acceptance histograms - Denominator - if (passedEvSelVtZ) { - histomc.fill(HIST("hDenomEffAcc"), mcParticle.pt(), centrality, particleAnti, decayType); - } - } - - // Process reconstructed collisions for this MC collision - if (passedEvSelVtZ) { - // Find the corresponding reconstructed collision - for (auto const& collision : collisions) { - if (collision.mcCollisionId() != static_cast(idx)) - continue; - - auto bc = collision.bc_as(); - initCCDB(bc); - - histomc.fill(HIST("histVtxZReco"), collision.posZ()); - - auto tracksInColl = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); - - for (auto const& track : tracksInColl) { - if (!track.has_mcParticle()) - continue; // skip un-matched reco tracks - - auto const& matchedMCParticle = track.mcParticle_as(); - - // Only process particles from this MC collision - if (matchedMCParticle.mcCollisionId() != mcCollision.globalIndex()) - continue; - - int pdg = matchedMCParticle.pdgCode(); - - int decayType = 0; - - if (matchedMCParticle.isPhysicalPrimary()) { - decayType = 0; - if (matchedMCParticle.has_mothers()) { - for (const auto& motherparticle : matchedMCParticle.mothers_as()) { - if (std::find(hfMothCodes.begin(), hfMothCodes.end(), - std::abs(motherparticle.pdgCode())) != hfMothCodes.end()) { - decayType = 1; - break; - } - } - } - } else if (matchedMCParticle.has_mothers()) { - decayType = 1; - } else { - decayType = 2; - } - - if (!track.isPVContributor() && cfgUsePVcontributors) - continue; - - if (!track.hasTPC()) - continue; - if (!track.passedITSRefit() && cfgPassedITSRefit) - continue; - if (!track.passedTPCRefit() && cfgPassedTPCRefit) - continue; - if (std::abs(track.eta()) > cfgCutEta && cfgetaRequire) - continue; - - for (size_t i = 0; i < primaryParticles.size(); i++) { - if (std::abs(pdg) != std::abs(particlePdgCodes.at(i))) - continue; - - // CRITICAL: Only process if fillsparsh is enabled for this particle - if (cfgTrackPIDsettings2->get(i, "fillsparsh") != 1) - continue; - - float ptReco; - setTrackParCov(track, mTrackParCov); - mTrackParCov.setPID(track.pidForTracking()); - - ptReco = (std::abs(pdg) == particlePdgCodes.at(4) || std::abs(pdg) == particlePdgCodes.at(5)) ? 2 * mTrackParCov.getPt() : mTrackParCov.getPt(); - - int particleAnti = (pdg > 0) ? 0 : 1; - - double a = 0, b = 0, c = 0; - - int param = -1; - if (i == he3) { - param = (pdg > 0) ? 0 : 1; - } else if (i == he4) { - param = (pdg > 0) ? 2 : 3; - } - - if (param >= 0) { - a = cfgktrackcorrection->get(param, "a"); - b = cfgktrackcorrection->get(param, "b"); - c = cfgktrackcorrection->get(param, "c"); - } - - if (std::abs(pdg) == particlePdgCodes.at(5) && cfgmccorrectionhe4Require) { - ptReco = ptReco + a + b * std::exp(c * ptReco); - } - - if (std::abs(pdg) == particlePdgCodes.at(4) && cfgmccorrectionhe4Require) { - int pidGuess = track.pidForTracking(); - int antitriton = 6; - if (pidGuess == antitriton) { - ptReco = ptReco - a + b * ptReco - c * ptReco * ptReco; - } - } - - if (std::abs(getRapidity(track, i)) > cfgCutRapidity && cfgRapidityRequire) - continue; - - if (track.tpcNClsFound() < cfgTrackPIDsettings->get(i, "minTPCnCls")) - continue; - if (((track.tpcNClsCrossedRows() < cfgTrackPIDsettings->get(i, "minTPCnClsCrossedRows")) || track.tpcNClsCrossedRows() < cfgtpcNClsFindable * track.tpcNClsFindable()) && cfgTPCNClsCrossedRowsRequire) - continue; - if (track.tpcChi2NCl() > cfgTrackPIDsettings->get(i, "maxTPCchi2") && cfgmaxTPCchi2Require) - continue; - if (track.tpcChi2NCl() < cfgTrackPIDsettings->get(i, "minTPCchi2") && cfgminTPCchi2Require) - continue; - if (track.itsNCls() < cfgTrackPIDsettings->get(i, "minITSnCls") && cfgminITSnClsRequire) - continue; - double cosheta = std::cosh(track.eta()); - if ((track.itsNCls() / cosheta) < cfgTrackPIDsettings->get(i, "minITSnClscos") && cfgminITSnClscosRequire) - continue; - if ((track.itsNClsInnerBarrel() < cfgTrackPIDsettings->get(i, "minReqClusterITSib")) && cfgminReqClusterITSibRequire) - continue; - if (track.itsChi2NCl() > cfgTrackPIDsettings->get(i, "maxITSchi2") && cfgmaxITSchi2Require) - continue; - if (getMeanItsClsSize(track) < cfgTrackPIDsettings->get(i, "minITSclsSize") && cfgminGetMeanItsClsSizeRequire) - continue; - - bool insideDCAxy = false; - - if (cfgUseNewDCAxyCut) { - double sigmaFactor = cfgTrackPIDsettings->get(i, "maxDcaXY"); - double sigma_base = (0.0118 * std::exp(-0.6889 * ptReco) + 0.0017); - double sigma_new = sigmaFactor * sigma_base; - insideDCAxy = (std::abs(track.dcaXY()) <= sigma_new); - } else { - insideDCAxy = cfgdcaxynopt - ? (std::abs(track.dcaXY()) <= cfgTrackPIDsettings->get(i, "maxDcaXY")) - : (std::abs(track.dcaXY()) <= (cfgTrackPIDsettings->get(i, "maxDcaXY") * (0.0105f + 0.0350f / std::pow(ptReco, 1.1f)))); - } - - bool insideDCAz = false; - - if (cfgUseNewDCAzCut) { - double sigmaFactorZ = cfgTrackPIDsettings->get(i, "maxDcaZ"); - double p0 = 0.1014; - double p1 = 1.7512; - double p2 = 0.0024; - double sigma_base_z = p0 * std::exp(-p1 * ptReco) + p2; - double sigma_new_z = sigmaFactorZ * sigma_base_z; - insideDCAz = (std::abs(track.dcaZ()) <= sigma_new_z); - } else { - insideDCAz = (std::abs(track.dcaZ()) <= cfgTrackPIDsettings->get(i, "maxDcaZ")); - } - - if ((!insideDCAxy || !insideDCAz)) { - continue; - } - - float tpcNsigma = getTPCnSigma(track, primaryParticles.at(i)); - if ((std::abs(tpcNsigma) > cfgTrackPIDsettings->get(i, "maxTPCnSigma")) && cfgmaxTPCnSigmaRequire) - continue; - - // Efficiency x Acceptance - Numerator - histomc.fill(HIST("hNumerEffAcc"), ptReco, collision.centFT0C(), particleAnti, decayType); - - // Sparse histogram - float ptTOF = -1.0; - if (track.hasTOF()) { - ptTOF = ptReco; - } - histomc.fill(HIST("hSpectramc"), particleAnti, collision.centFT0C(), ptReco, ptTOF); - - // Basic track histograms - histos.fill(HIST("dcaXY"), ptReco, track.dcaXY()); - histos.fill(HIST("dcaZ"), ptReco, track.dcaZ()); - histos.fill(HIST("Tpcsignal"), getRigidity(track) * track.sign(), track.tpcSignal()); - - // Delta Pt histograms - float ptGen = matchedMCParticle.pt(); - float deltaPt = ptReco - ptGen; - - if (pdg == -particlePdgCodes.at(i)) { // Anti-particle - histomc.fill(HIST("histDeltaPtVsPtGenanti"), ptReco, deltaPt); - histomc.fill(HIST("histPIDtrackanti"), ptReco, track.pidForTracking()); - } - if (pdg == particlePdgCodes.at(i)) { // Particle - histomc.fill(HIST("histDeltaPtVsPtGen"), ptReco, deltaPt); - histomc.fill(HIST("histPIDtrack"), ptReco, track.pidForTracking()); - } - } - } - break; // Found the matching collision, break out of collision loop - } - } - } - } - PROCESS_SWITCH(NucleitpcPbPb, processMC, "MC reco+gen analysis with efficiency corrections", false); - //=-=-=-==-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - void processDCA(CollisionsFullMC const& collisions, - aod::McCollisions const& mcCollisions, - aod::McParticles const& particlesMC, - soa::Join const& tracks, - aod::BCsWithTimestamps const&) - - { - (void)particlesMC; - mcCollInfos.clear(); - mcCollInfos.resize(mcCollisions.size()); - - // First pass: Store centrality and apply event selection - for (auto const& collision : collisions) { - int mcCollIdx = collision.mcCollisionId(); - if (mcCollIdx < 0 || mcCollIdx >= static_cast(mcCollisions.size())) { - continue; - } - - // STORE CENTRALITY WITHOUT CUTS - mcCollInfos[mcCollIdx].centrality = collision.centFT0C(); - - if (!collision.sel8() && cfgsel8Require) - continue; - if (collision.centFT0C() > centcut) - continue; - - if (removeNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) - continue; - if (requireIsGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) - continue; - if (requireIsVertexITSTPC && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) - continue; - if (removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) - continue; - - // Mark this MC collision as passing event selection - mcCollInfos[mcCollIdx].passedEvSel = true; - - // Apply event selection cuts - if (std::abs(collision.posZ()) > cfgZvertex && cfgZvertexRequireMC) - continue; - - mcCollInfos[mcCollIdx].passedEvSelVtZ = true; - } - - // Process MC collisions for efficiency and reconstructed collisions - for (auto const& mcCollision : mcCollisions) { - size_t idx = mcCollision.globalIndex(); - if (idx >= mcCollInfos.size()) - continue; - - bool passedEvSelVtZ = mcCollInfos[idx].passedEvSelVtZ; - - // Process reconstructed collisions for this MC collision - if (passedEvSelVtZ) { - // Find the corresponding reconstructed collision - for (auto const& collision : collisions) { - if (collision.mcCollisionId() != static_cast(idx)) - continue; - - auto bc = collision.bc_as(); - initCCDB(bc); - auto tracksInColl = tracks.sliceBy(tracksPerCollision, collision.globalIndex()); - - for (auto const& track : tracksInColl) { - if (!track.has_mcParticle()) - continue; // skip un-matched reco tracks - - auto const& matchedMCParticle = track.mcParticle_as(); - - // Only process particles from this MC collision - if (matchedMCParticle.mcCollisionId() != mcCollision.globalIndex()) - continue; - - int pdg = matchedMCParticle.pdgCode(); - - if (!track.isPVContributor() && cfgUsePVcontributors) - continue; - - if (!track.hasTPC()) - continue; - if (!track.passedITSRefit() && cfgPassedITSRefit) - continue; - if (!track.passedTPCRefit() && cfgPassedTPCRefit) - continue; - if (std::abs(track.eta()) > cfgCutEta && cfgetaRequire) - continue; - - for (size_t i = 0; i < primaryParticles.size(); i++) { - if (std::abs(pdg) != std::abs(particlePdgCodes.at(i))) - continue; - - if (cfgTrackPIDsettings2->get(i, "fillsparsh") != 1) - continue; - - float ptReco; - setTrackParCov(track, mTrackParCov); - mTrackParCov.setPID(track.pidForTracking()); - - ptReco = (std::abs(pdg) == particlePdgCodes.at(4) || std::abs(pdg) == particlePdgCodes.at(5)) ? 2 * mTrackParCov.getPt() : mTrackParCov.getPt(); - - double a = 0, b = 0, c = 0; - - int param = -1; - if (i == he3) { - param = (-particlePdgCodes.at(4) > 0) ? 0 : 1; - } else if (i == he4) { - param = (-particlePdgCodes.at(4) > 0) ? 2 : 3; - } - - if (param >= 0) { - a = cfgktrackcorrection->get(param, "a"); - b = cfgktrackcorrection->get(param, "b"); - c = cfgktrackcorrection->get(param, "c"); - } - - if (std::abs(pdg) == particlePdgCodes.at(5) && cfgmccorrectionhe4Require) { - ptReco = ptReco + a + b * std::exp(c * ptReco); - } - - if (std::abs(pdg) == particlePdgCodes.at(4) && cfgmccorrectionhe4Require) { - int pidGuess = track.pidForTracking(); - int antitriton = 6; - if (pidGuess == antitriton) { - ptReco = ptReco - a + b * ptReco - c * ptReco * ptReco; - } - } - - if (std::abs(getRapidity(track, i)) > cfgCutRapidity && cfgRapidityRequire) - continue; - - if (track.tpcNClsFound() < cfgTrackPIDsettings->get(i, "minTPCnCls")) - continue; - if (((track.tpcNClsCrossedRows() < cfgTrackPIDsettings->get(i, "minTPCnClsCrossedRows")) || track.tpcNClsCrossedRows() < cfgtpcNClsFindable * track.tpcNClsFindable()) && cfgTPCNClsCrossedRowsRequire) - continue; - if (track.tpcChi2NCl() > cfgTrackPIDsettings->get(i, "maxTPCchi2") && cfgmaxTPCchi2Require) - continue; - if (track.tpcChi2NCl() < cfgTrackPIDsettings->get(i, "minTPCchi2") && cfgminTPCchi2Require) - continue; - if (track.itsNCls() < cfgTrackPIDsettings->get(i, "minITSnCls") && cfgminITSnClsRequire) - continue; - double cosheta = std::cosh(track.eta()); - if ((track.itsNCls() / cosheta) < cfgTrackPIDsettings->get(i, "minITSnClscos") && cfgminITSnClscosRequire) - continue; - if ((track.itsNClsInnerBarrel() < cfgTrackPIDsettings->get(i, "minReqClusterITSib")) && cfgminReqClusterITSibRequire) - continue; - if (track.itsChi2NCl() > cfgTrackPIDsettings->get(i, "maxITSchi2") && cfgmaxITSchi2Require) - continue; - if (getMeanItsClsSize(track) < cfgTrackPIDsettings->get(i, "minITSclsSize") && cfgminGetMeanItsClsSizeRequire) - continue; - float tpcNsigma = getTPCnSigma(track, primaryParticles.at(i)); - if ((std::abs(tpcNsigma) > cfgTrackPIDsettings->get(i, "maxTPCnSigma")) && cfgmaxTPCnSigmaRequire) - continue; - - int decayType = -999; // 0 = primary, 1 = weak decay, 2 = material - if (matchedMCParticle.isPhysicalPrimary()) { - // ---- Primary particles ---- - decayType = 0; - if (matchedMCParticle.has_mothers()) { - for (auto& motherparticle : matchedMCParticle.mothers_as()) { - if (std::find(hfMothCodes.begin(), hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != hfMothCodes.end()) { - decayType = 1; - break; - } - } - } - } else if (matchedMCParticle.getProcess() == TMCProcess::kPDecay) { - // ---- Secondary from weak decay ---- - if (!matchedMCParticle.has_mothers()) { - continue; // skip secondaries from weak decay without mothers - } - decayType = 1; - - // Check if it's from HF decay - for (auto& motherparticle : matchedMCParticle.mothers_as()) { - if (std::find(hfMothCodes.begin(), hfMothCodes.end(), std::abs(motherparticle.pdgCode())) != hfMothCodes.end()) { - break; - } - } - } else { - // ---- Secondary from material interaction ---- - decayType = 2; - } - - float ptDCA; - - if (i == he3 || i == he4) { - ptDCA = ptReco; - } else { - ptDCA = 2 * ptReco; - } - - if (pdg == particlePdgCodes.at(i)) { // He3 - histomc.fill(HIST("DCAxy_vs_pT_total"), ptDCA, track.dcaXY(), collision.centFT0C()); - if (matchedMCParticle.isPhysicalPrimary()) { - histomc.fill(HIST("DCAxy_vs_pT_primary"), ptDCA, track.dcaXY(), collision.centFT0C()); - } - if (decayType == 2) { // Transport/Material - histomc.fill(HIST("DCAxy_vs_pT_transport"), ptDCA, track.dcaXY(), collision.centFT0C()); - } else if (decayType == 1) { // Weak decay (including HF) - histomc.fill(HIST("DCAxy_vs_pT_weakdecay"), ptDCA, track.dcaXY(), collision.centFT0C()); - } - } else if (pdg == -particlePdgCodes.at(i)) { // anti-He3 - histomc.fill(HIST("DCAxy_vs_pT_anti_total"), ptDCA, track.dcaXY(), collision.centFT0C()); - } - } - } - break; // Found the matching collision, break out of collision loop - } - } - } - } - PROCESS_SWITCH(NucleitpcPbPb, processDCA, "MC DCA analysis For secondary correction", false); - //=-=-=-==-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - - void initCCDB(aod::BCsWithTimestamps::iterator const& bc) - { - if (mRunNumber == bc.runNumber()) { - return; - } - constexpr float kInvalidBField = -990.f; - auto run3grpTimestamp = bc.timestamp(); - dBz = 0; - o2::parameters::GRPObject* grpo = ccdb->getForTimeStamp(grpPath, run3grpTimestamp); - o2::parameters::GRPMagField* grpmag = 0x0; - if (grpo) { - o2::base::Propagator::initFieldFromGRP(grpo); - if (bField < kInvalidBField) { - // Fetch magnetic field from ccdb for current collision - dBz = grpo->getNominalL3Field(); - LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; - } else { - dBz = bField; - } - } else { - grpmag = ccdb->getForTimeStamp(grpmagPath, run3grpTimestamp); - if (!grpmag) { - LOG(fatal) << "Got nullptr from CCDB for path " << grpmagPath << " of object GRPMagField and " << grpPath << " of object GRPObject for timestamp " << run3grpTimestamp; - } - o2::base::Propagator::initFieldFromGRP(grpmag); - if (bField < kInvalidBField) { - // Fetch magnetic field from ccdb for current collision - dBz = std::lround(5.f * grpmag->getL3Current() / 30000.f); - LOG(info) << "Retrieved GRP for timestamp " << run3grpTimestamp << " with magnetic field of " << dBz << " kZG"; - } else { - dBz = bField; - } - } - mRunNumber = bc.runNumber(); - } - //---------------------------------------------------------------------------------------------------------------- - template - void initCollision(const T& collision) - { - collHasCandidate = false; - histos.fill(HIST("histMagField"), dBz); - histos.fill(HIST("histNev"), 0.5); - collPassedEvSel = collision.sel8() && std::abs(collision.posZ()) < cfgZvertex; - occupancy = collision.trackOccupancyInTimeRange(); - if (collPassedEvSel) { - histos.fill(HIST("histNev"), 1.5); - } - primVtx.assign({collision.posX(), collision.posY(), collision.posZ()}); - cents.assign({collision.centFT0A(), collision.centFT0C(), collision.centFT0M()}); - } - //---------------------------------------------------------------------------------------------------------------- - template - void fillhmass(T const& track, int species, float cent) - { - if (!track.hasTOF() || !cfgFillmass) - return; - - float beta{o2::pid::tof::Beta::GetBeta(track)}; - const float eps = 1e-6f; - if (beta < eps || beta > 1.0f - eps) - return; - float charge = (species == he3 || species == he4) ? 2.f : 1.f; - float p = getRigidity(track); // assuming this is the momentum from inner TPC - float massTOF = p * charge * std::sqrt(1.f / (beta * beta) - 1.f); - float massDiff = 0.0; - if (species != he4) { - if (cfgmass2) { - // Compare squared masses - massDiff = (massTOF * massTOF) / (charge * charge); - } else { - // Compare linear masses - massDiff = massTOF / charge; - } - } - if (species == he4) { - if (cfghe3massrejreq && (massTOF * massTOF > cfgminmassrejection && massTOF * massTOF < cfgmaxmassrejection)) - return; - if (cfgmass2) { - // Compare squared masses - massDiff = (massTOF * massTOF) / (charge * charge); - } else { - // Compare linear masses - massDiff = massTOF / charge; - } - } - - float ptMomn; - setTrackParCov(track, mTrackParCov); - mTrackParCov.setPID(track.pidForTracking()); - ptMomn = (species == he3 || species == he4) ? 2 * mTrackParCov.getPt() : mTrackParCov.getPt(); - if (track.sign() > 0) { - hmass[2 * species]->Fill(ptMomn, massDiff, cent); - } else if (track.sign() < 0) { - hmass[2 * species + 1]->Fill(ptMomn, massDiff, cent); - } - } - //---------------------------------------------------------------------------------------------------------------- - template - void fillhmassnsigma(T const& track, int species, float cent) - { - if (!track.hasTOF() || !cfgFillmassnsigma) - return; - - float beta{o2::pid::tof::Beta::GetBeta(track)}; - const float eps = 1e-6f; - if (beta < eps || beta > 1.0f - eps) - return; - - float charge = (species == he3 || species == he4) ? 2.f : 1.f; - float p = getRigidity(track); - float massTOF = p * charge * std::sqrt(1.f / (beta * beta) - 1.f); - - // Calculate mass²/z² - float massSquareOverChargeSquare = (massTOF * massTOF) / (charge * charge); - - // Apply helium-4 rejection if needed - if (species == he4) { - if (cfghe3massrejreq && (massTOF * massTOF > cfgminmassrejection && massTOF * massTOF < cfgmaxmassrejection)) - return; - } - - float ptMomn; - setTrackParCov(track, mTrackParCov); - mTrackParCov.setPID(track.pidForTracking()); - ptMomn = (species == he3 || species == he4) ? 2 * mTrackParCov.getPt() : mTrackParCov.getPt(); - - // Fill histogram: mass²/z² vs pT vs centrality - if (track.sign() > 0) { - hmassnsigma[2 * species]->Fill(ptMomn, massSquareOverChargeSquare, cent); - } else if (track.sign() < 0) { - hmassnsigma[2 * species + 1]->Fill(ptMomn, massSquareOverChargeSquare, cent); - } - } - //---------------------------------------------------------------------------------------------------------------- - template - float getTPCnSigma(T const& track, PrimParticles& particle) - { - const float rigidity = getRigidity(track); - if (!track.hasTPC()) - return -999; - if (particle.name == "pion" && cfgTrackPIDsettings->get("pion", "useBBparams") < 1) - return cfgTrackPIDsettings->get("pion", "useBBparams") == 0 ? track.tpcNSigmaPi() : 0; - if (particle.name == "proton" && cfgTrackPIDsettings->get("proton", "useBBparams") < 1) - return cfgTrackPIDsettings->get("proton", "useBBparams") == 0 ? track.tpcNSigmaPr() : 0; - if (particle.name == "deuteron" && cfgTrackPIDsettings->get("deuteron", "useBBparams") < 1) - return cfgTrackPIDsettings->get("deuteron", "useBBparams") == 0 ? track.tpcNSigmaDe() : 0; - if (particle.name == "triton" && cfgTrackPIDsettings->get("triton", "useBBparams") < 1) - return cfgTrackPIDsettings->get("triton", "useBBparams") == 0 ? track.tpcNSigmaTr() : 0; - if (particle.name == "helion" && cfgTrackPIDsettings->get("helion", "useBBparams") < 1) - return cfgTrackPIDsettings->get("helion", "useBBparams") == 0 ? track.tpcNSigmaHe() : 0; - if (particle.name == "alpha" && cfgTrackPIDsettings->get("alpha", "useBBparams") < 1) - return cfgTrackPIDsettings->get("alpha", "useBBparams") == 0 ? track.tpcNSigmaAl() : 0; - - double expBethe{common::BetheBlochAleph(static_cast(particle.charge * rigidity / particle.mass), particle.betheParams[0], particle.betheParams[1], particle.betheParams[2], particle.betheParams[3], particle.betheParams[4])}; - double expSigma{expBethe * particle.resolution}; - float sigmaTPC = static_cast((track.tpcSignal() - expBethe) / expSigma); - return sigmaTPC; - } - //---------------------------------------------------------------------------------------------------------------- - template - float getITSnSigma(T const& track, PrimParticles& particle) - { - if (!track.hasITS()) - return -999; - o2::aod::ITSResponse itsResponse; - if (particle.name == "pion") - return itsResponse.nSigmaITS(track); - if (particle.name == "proton") - return itsResponse.nSigmaITS(track); - if (particle.name == "deuteron") - return itsResponse.nSigmaITS(track); - if (particle.name == "triton") - return itsResponse.nSigmaITS(track); - if (particle.name == "helion") - return itsResponse.nSigmaITS(track); - if (particle.name == "alpha") - return itsResponse.nSigmaITS(track); - return -999; // fallback if no match - } - //---------------------------------------------------------------------------------------------------------------- - template - float getMeanItsClsSize(T const& track) - { - int sum = 0, n = 0; - constexpr int kNITSLayers = 8; - for (int i = 0; i < kNITSLayers; i++) { - sum += (track.itsClusterSizes() >> (4 * i) & 15); - if (track.itsClusterSizes() >> (4 * i) & 15) - n++; - } - return n > 0 ? static_cast(sum) / n : 0.f; - } - //---------------------------------------------------------------------------------------------------------------- - template - float getRigidity(T const& track) - { - if (!cfgRigidityCorrection) - return track.tpcInnerParam(); - bool hePID = track.pidForTracking() == o2::track::PID::Helium3 || track.pidForTracking() == o2::track::PID::Alpha; - return hePID ? track.tpcInnerParam() / 2 : track.tpcInnerParam(); - } - - //---------------------------------------------------------------------------------------------------------------- - template - float getRapidity(T const& track, int species) - { - using PtEtaPhiMVector = ROOT::Math::LorentzVector>; - double momn; - int speciesHe3 = 4; - int speciesHe4 = 5; - if (species == speciesHe3 || species == speciesHe4) { - momn = 2 * track.pt(); - } else { - momn = track.pt(); - } - PtEtaPhiMVector lorentzVectorParticle(momn, track.eta(), track.phi(), particleMasses[species]); - return lorentzVectorParticle.Rapidity(); - } -}; // end of the task here -//---------------------------------------------------------------------------------------------------------------- -//---------------------------------------------------------------------------------------------------------------- -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; -} diff --git a/PWGLF/Tasks/Nuspex/pccQa.cxx b/PWGLF/Tasks/Nuspex/pccQa.cxx index bb481652550..fe04b0ffc4e 100644 --- a/PWGLF/Tasks/Nuspex/pccQa.cxx +++ b/PWGLF/Tasks/Nuspex/pccQa.cxx @@ -31,6 +31,7 @@ #include +#include #include using namespace o2; @@ -43,6 +44,8 @@ struct PccQa { static constexpr float MaxVtxZ = 10.f; + Configurable maxMult{"maxMult", 100, "max multiplicity"}; + void init(InitContext const&); template @@ -69,23 +72,45 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) void PccQa::init(InitContext const&) { + histos.add("eventCounter", "", kTH1D, {{1, 0.5, 1.5}}); const AxisSpec dcaAxis = {1000, -1., 1., "#it{DCA}_{xy}", "dca"}; std::vector ptBinEdges = {0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0}; const AxisSpec ptAxis{ptBinEdges, "#it{p}_{T} (GeV/#it{c})", "pt"}; + const int nBinsMult = 30; + const AxisSpec nchAxis = {nBinsMult, 0., static_cast(maxMult), "#it{N}_{ch}", "nch"}; + histos.add("DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); if (doprocessMC) { histos.add("DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + histos.add("prim/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("prim/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + + histos.add("prim/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); histos.add("prim/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("sec/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("sec/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + histos.add("sec/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("sec/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + histos.add("sec/dec/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("sec/dec/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + histos.add("sec/dec/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("sec/dec/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + histos.add("sec/mat/DCAxyVsPt", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("sec/mat/DCAxyVsPtVsMult", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); + histos.add("sec/mat/DCAxyVsPt_weighted", "", kTH2D, {ptAxis, dcaAxis}); + histos.add("sec/mat/DCAxyVsPtVsMult_weighted", "", kTH3D, {nchAxis, ptAxis, dcaAxis}); } } @@ -95,6 +120,7 @@ void PccQa::processData(CollisionTableData::iterator const& collision, TrackTabl } void PccQa::processMC(CollisionTableMCTrue::iterator const&, TrackTableMC const& tracks, CollisionTableMC const& collisions, ParticleTableMC const&) { + for (const auto& collision : collisions) { auto curTracks = tracks.sliceBy(perCollision, collision.globalIndex()); processMeas(collision, curTracks); @@ -105,6 +131,7 @@ void PccQa::processMC(CollisionTableMCTrue::iterator const&, TrackTableMC const& template void PccQa::processMeas(const C& collision, const T& tracks) { + if ((std::abs(collision.posZ()) > MaxVtxZ) || !collision.sel8()) { return; } @@ -115,6 +142,7 @@ void PccQa::processMeas(const C& collision, const T& tracks) continue; } histos.fill(HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + histos.fill(HIST("DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); if constexpr (IS_MC) { if (!track.has_mcParticle()) { @@ -123,19 +151,28 @@ void PccQa::processMeas(const C& collision, const T& tracks) const auto& particle = track.template mcParticle_as(); histos.fill(HIST("DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); + histos.fill(HIST("DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); if (particle.isPhysicalPrimary()) { histos.fill(HIST("prim/DCAxyVsPt"), track.pt(), track.dcaXY()); histos.fill(HIST("prim/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); + histos.fill(HIST("prim/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); + histos.fill(HIST("prim/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); } else { histos.fill(HIST("sec/DCAxyVsPt"), track.pt(), track.dcaXY()); histos.fill(HIST("sec/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); + histos.fill(HIST("sec/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); + histos.fill(HIST("sec/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); if (particle.getProcess() == TMCProcess::kPDecay) { histos.fill(HIST("sec/dec/DCAxyVsPt"), track.pt(), track.dcaXY()); histos.fill(HIST("sec/dec/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); + histos.fill(HIST("sec/dec/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); + histos.fill(HIST("sec/dec/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); } else if (particle.getProcess() == TMCProcess::kPHInhelastic || particle.getProcess() == TMCProcess::kPHadronic || particle.getProcess() == TMCProcess::kPHElastic) { histos.fill(HIST("sec/mat/DCAxyVsPt"), track.pt(), track.dcaXY()); histos.fill(HIST("sec/mat/DCAxyVsPt_weighted"), track.pt(), track.dcaXY(), particle.pccWeight()); + histos.fill(HIST("sec/mat/DCAxyVsPtVsMult"), tracks.size(), track.pt(), track.dcaXY()); + histos.fill(HIST("sec/mat/DCAxyVsPtVsMult_weighted"), tracks.size(), track.pt(), track.dcaXY(), particle.pccWeight()); } } } diff --git a/PWGLF/Tasks/Nuspex/piKpRAA.cxx b/PWGLF/Tasks/Nuspex/piKpRAA.cxx index 89d75b08963..3288501f999 100644 --- a/PWGLF/Tasks/Nuspex/piKpRAA.cxx +++ b/PWGLF/Tasks/Nuspex/piKpRAA.cxx @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -70,82 +71,91 @@ using namespace o2::constants::math; using namespace o2::framework::expressions; using namespace o2::aod::rctsel; -using ColEvSels = soa::Join; +using ColEvSels = soa::Join; using BCsRun3 = soa::Join; -using ColEvSelsMC = soa::Join; +using ColEvSelsMC = soa::Join; -using TracksFull = soa::Join; +using TracksFull = soa::Join; using TracksMC = soa::Join; -static constexpr int kNEtaHists{8}; - -std::array, kNEtaHists> dEdxPiV0{}; -std::array, kNEtaHists> dEdxPrV0{}; -std::array, kNEtaHists> dEdxElV0{}; -std::array, kNEtaHists> dEdxPiMC{}; -std::array, kNEtaHists> dEdxMuMC{}; -std::array, kNEtaHists> dEdx{}; -std::array, kNEtaHists> pTVsP{}; -std::array, kNEtaHists> nClVsP{}; -std::array, kNEtaHists> nCrRoVsNcl{}; -std::array, kNEtaHists> nClVsPElV0{}; -std::array, kNEtaHists> nClVsPPiV0{}; -std::array, kNEtaHists> nClVsPPrV0{}; -std::array, kNEtaHists> nClPIDVsnCl{}; -std::array, kNEtaHists> nClVsPp{}; -std::array, kNEtaHists> nCrRoVsNclp{}; -std::array, kNEtaHists> nClVsPpElV0{}; -std::array, kNEtaHists> nClVsPpPiV0{}; -std::array, kNEtaHists> nClVsPpPrV0{}; -std::array, kNEtaHists> nClPIDVsnClp{}; +static constexpr int KnEtaHists{8}; + +std::array, KnEtaHists> dEdxPiTOF{}; +std::array, KnEtaHists> dEdxPiTOF2{}; +std::array, KnEtaHists> dEdxPiV0{}; +std::array, KnEtaHists> dEdxPrV0{}; +std::array, KnEtaHists> dEdxElV0{}; +std::array, KnEtaHists> dEdx{}; +std::array, KnEtaHists> pTVsP{}; +std::array, KnEtaHists> nClVsP{}; +std::array, KnEtaHists> nClVsPElV0{}; +std::array, KnEtaHists> nClVsPPiV0{}; +std::array, KnEtaHists> nClVsPPrV0{}; +std::array, KnEtaHists> nClVsPp{}; +std::array, KnEtaHists> nClVsPpElV0{}; +std::array, KnEtaHists> nClVsPpPiV0{}; +std::array, KnEtaHists> nClVsPpPrV0{}; +std::array, KnEtaHists> etaTest{}; + +template +auto printArray(std::vector const& vec) +{ + std::stringstream ss; + ss << "[" << vec[0]; + for (auto i = 1u; i < vec.size(); ++i) { + ss << ", " << vec[i]; + } + ss << "]"; + return ss.str(); +} struct PiKpRAA { - static constexpr int kZeroInt{0}; - static constexpr int kSevenInt{7}; - - static constexpr float kZero{0.0f}; - static constexpr float kOne{1.0f}; - static constexpr float kTwoPtGeVSel{2.0f}; - static constexpr float kThree{3.0f}; - static constexpr float kTenToMinusNine{1e-9}; - static constexpr float kMinPtNchSel{0.1f}; - static constexpr float kMaxPtNchSel{3.0f}; - static constexpr float kMinCharge{3.f}; - static constexpr float kMinPMIP{0.4f}; - static constexpr float kMaxPMIP{0.6f}; - static constexpr float kMindEdxMIP{40.0f}; - static constexpr float kMaxdEdxMIP{60.0f}; - static constexpr float kMindEdxMIPPlateau{70.0f}; - static constexpr float kMaxdEdxMIPPlateau{90.0f}; - static constexpr float kMinFT0A{3.5f}; - static constexpr float kMaxFT0A{4.9f}; - static constexpr float kMinFT0C{-3.3f}; - static constexpr float kMaxFT0C{-2.1f}; - - static constexpr float kLowEta[kNEtaHists] = {-0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6}; - static constexpr float kHighEta[kNEtaHists] = {-0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}; + static constexpr int KzeroInt{0}; + static constexpr int KsevenInt{7}; + + static constexpr float Kzero{0.0f}; + static constexpr float Kone{1.0f}; + static constexpr float KtwoPtGeVSel{2.0f}; + static constexpr float Kthree{3.0f}; + static constexpr float KtEnToMinusNine{1e-9}; + static constexpr float KminCharge{3.f}; + static constexpr float KminPMIP{0.4f}; + static constexpr float KmaxPMIP{0.6f}; + static constexpr float KmindEdxMIP{40.0f}; + static constexpr float KmaxdEdxMIP{60.0f}; + static constexpr float KmindEdxMIPPlateau{70.0f}; + static constexpr float KmaxdEdxMIPPlateau{90.0f}; + static constexpr float KminFV0A{2.2f}; + static constexpr float KmaxFV0A{5.1f}; + static constexpr float KminFT0A{3.5f}; + static constexpr float KmaxFT0A{4.9f}; + static constexpr float KminFT0C{-3.3f}; + static constexpr float KmaxFT0C{-2.1f}; static constexpr float DefaultLifetimeCuts[1][2] = {{30., 20.}}; Configurable> lifetimecut{"lifetimecut", {DefaultLifetimeCuts[0], 2, {"lifetimecutLambda", "lifetimecutK0S"}}, "lifetimecut"}; struct : ConfigurableGroup { - Configurable minEta{"minEta", -0.8, "Daughter minimum-eta selection"}; - Configurable maxEta{"maxEta", +0.8, "Daughter maximum-eta selection"}; + Configurable minEta{"minEta", -0.8, "minimum eta selection"}; + Configurable maxEta{"maxEta", +0.8, "maximum eta selection"}; Configurable minPt{"minPt", 0.1, "minimum pt of the tracks"}; Configurable maxPt{"maxPt", 10000000000.0, "maximum pt of the tracks"}; Configurable nSigmaDCAxy{"nSigmaDCAxy", 1.0, "nSigma DCAxy selection"}; Configurable nSigmaDCAz{"nSigmaDCAz", 1.0, "nSigma DCAz selection"}; + Configurable particleVtxSel{"particleVtxSel", 5.0, "selection on the distance between particle creation vtx"}; Configurable minNCrossedRows{"minNCrossedRows", 70, "minimum number of crossed rows"}; - Configurable minNcls{"minNcls", 135, "minimum number of clusters for primary-particle selection"}; + Configurable minNcl{"minNcl", 135, "minimum number of clusters for primary-particle selection"}; Configurable minNClusITS{"minNClusITS", 7, "minimum number of ITS clusters"}; Configurable minChi2ClsTPC{"minChi2ClsTPC", 0.5, "Max chi2 per Cls TPC"}; Configurable maxChi2ClsTPC{"maxChi2ClsTPC", 4.0, "Max chi2 per Cls TPC"}; Configurable chi2ClsITS{"chi2ClsITS", 36.0, "chi2 per Cls ITS selection"}; Configurable maxElTOFBeta{"maxElTOFBeta", 0.1, "Maximum beta TOF selection"}; + Configurable maxPiTOFBeta{"maxPiTOFBeta", 0.005, "Maximum beta TOF selection for Pions"}; + Configurable nSigmaPIDselPrim{"nSigmaPIDselPrim", 1.0, "N sigma selection for primary pions with TOF"}; // Phi cut Configurable applyPhiCut{"applyPhiCut", false, "Apply geometrical cut?"}; @@ -154,6 +164,7 @@ struct PiKpRAA { Configurable usePinPhiSelection{"usePinPhiSelection", true, "Uses Phi selection as a function of p or p at inner wall of TPC?"}; Configurable applyNclSel{"applyNclSel", false, "Apply Min. N clusters selection?"}; Configurable useNclsPID{"useNclsPID", true, "Use Ncl for PID?"}; + Configurable signCharge{"signCharge", "Positive", "Perform analysis as a function of the particle charge"}; } trackSelections; struct : ConfigurableGroup { @@ -162,8 +173,9 @@ struct PiKpRAA { Configurable useTPCNsigma{"useTPCNsigma", false, "Include TPC Nsigma selection on decay products"}; // Selection criteria: acceptance - Configurable rapidityCut{"rapidityCut", 0.5, "rapidity"}; - Configurable minNcl{"minNcl", 135, "minimum number of clusters for V0 daughters"}; + Configurable minY{"minY", -0.1, "minimum rapidity selection"}; + Configurable maxY{"maxY", 0.8, "maximum rapidity selection"}; + Configurable minNclV0Daugther{"minNclV0Daugther", 135, "minimum number of clusters for V0 daughters"}; Configurable nSharedClusTpc{"nSharedClusTpc", 5, "maximum number of shared clusters"}; // Standard 5 topological criteria @@ -188,13 +200,12 @@ struct PiKpRAA { // Selection Configurable applyInvMassSel{"applyInvMassSel", true, "Select V0s close to the Inv. mass value"}; - Configurable selElecFromGammas{"selElecFromGammas", true, "track selection for electrons"}; Configurable dMassSel{"dMassSel", 0.01f, "Invariant mass selection"}; Configurable dMassSelG{"dMassSelG", 0.1f, "Inv mass selection gammas"}; // PID (TPC/TOF) Configurable dEdxPlateauSel{"dEdxPlateauSel", 50, "dEdx selection for electrons"}; - Configurable pidNsigmaCut{"pidNsigmaCut", 3.0, "pidNsigmaCut"}; + Configurable pidNsigmaCut{"pidNsigmaCut", 3.0, "N sigma selection for V0 daughters"}; } v0Selections; // Configurables Event Selection @@ -210,7 +221,7 @@ struct PiKpRAA { Configurable isCentSel{"isCentSel", true, "Centrality selection?"}; Configurable selHasBC{"selHasBC", true, "Has BC?"}; Configurable selHasFT0{"selHasFT0", true, "Has FT0?"}; - Configurable isT0Ccent{"isT0Ccent", true, "Use T0C-based centrality?"}; + Configurable centralitySelector{"centralitySelector", "FT0C", "which centrality selector?"}; Configurable useSel8{"useSel8", false, "Use sel8?"}; Configurable selTriggerTVX{"selTriggerTVX", true, "selTriggerTVX?"}; @@ -225,18 +236,15 @@ struct PiKpRAA { // Event selection Configurable posZcut{"posZcut", +10.0, "z-vertex position cut"}; - Configurable minT0CcentCut{"minT0CcentCut", 0.0, "Min T0C Cent. cut"}; - Configurable maxT0CcentCut{"maxT0CcentCut", 100.0, "Max T0C Cent. cut"}; + Configurable minCentCut{"minCentCut", 0.0, "Min Cent. cut"}; + Configurable maxCentCut{"maxCentCut", 100.0, "Max Cent. cut"}; Configurable minOccCut{"minOccCut", 0., "min Occu cut"}; Configurable maxOccCut{"maxOccCut", 500., "max Occu cut"}; - Configurable nSigmaNchCut{"nSigmaNchCut", 3., "nSigma Nch selection"}; Configurable tpcNchAcceptance{"tpcNchAcceptance", 0.5, "Eta window to measure Nch MC for Nch vs Cent distribution"}; - ConfigurableAxis binsPtPhiCut{"binsPtPhiCut", {VARIABLE_WIDTH, 0.0, 0.6, 0.8, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0, 3.5, 4.0, 5.0, 7.0, 10.0, 15.0, 20.0, 25.0, 30.0, 40.0, 45.0, 50.0}, "pT"}; ConfigurableAxis binsPtV0s{"binsPtV0s", {VARIABLE_WIDTH, 0, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.5, 3.0, 3.5, 4, 5, 7, 9, 12, 15, 20}, "pT"}; - ConfigurableAxis binsPtNcl{"binsPtNcl", {VARIABLE_WIDTH, 0.0, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.5, 3.0, 3.5, 4.0, 5.0, 7.0, 9.0, 12.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0}, "pT"}; ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0, 0.1, 0.12}, "pT binning"}; - ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "T0C binning"}; + ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "Centrality bins"}; ConfigurableAxis binsZpos{"binsZpos", {60, -30.0, 30.0}, "Z pos axis"}; ConfigurableAxis axisEta{"axisEta", {50, -1.0, 1.0}, "Eta axis"}; ConfigurableAxis axisY{"axisY", {50, -1.0, 1.0}, "rapidity axis"}; @@ -249,12 +257,14 @@ struct PiKpRAA { ConfigurableAxis axisdEdx{"axisdEdx", {140, 20.0, 160.0}, "dEdx binning"}; ConfigurableAxis axisDCAxy{"axisDCAxy", {105, -1.05f, 1.05f}, "DCAxy axis"}; ConfigurableAxis axisPtFineFixedWidth{"axisPtFineFixedWidth", {250, 0.1f, 20.1f}, "DCAxy axis"}; - Configurable nBinsNch{"nBinsNch", 400, "N bins Nch (|eta|<0.8)"}; - Configurable nBinsNPV{"nBinsNPV", 600, "N bins ITS tracks"}; - Configurable minNch{"minNch", 0, "Min Nch (|eta|<0.8)"}; - Configurable maxNch{"maxNch", 400, "Max Nch (|eta|<0.8)"}; - Configurable minNpv{"minNpv", 0, "Min NPV"}; - Configurable maxNpv{"maxNpv", 600, "Max NPV"}; + ConfigurableAxis axisNPV{"axisNPV", {600, 0.0f, 600.0f}, "Number of Primary Vertex Contributors"}; + ConfigurableAxis axisNch{"axisNch", {400, 0.0f, 400.0f}, "N_{ch} Multiplicity"}; + ConfigurableAxis axisFineCent{"axisFineCent", {100, 0.0, 100.0}, "Fine centrality bins"}; + + Configurable> vecLowEta{"vecLowEta", {-0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6}, "lower eta cuts"}; + Configurable> vecUpEta{"vecUpEta", {-0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8}, "upper eta cuts"}; + Configurable> vecEndingEta{"vecEndingEta", {"86", "64", "42", "20", "02", "24", "46", "68"}, "string representing names for histogram distiction"}; + Configurable> vecLatexEta{"vecLatexEta", {"-0.8<#eta<-0.6", "-0.6<#eta<-0.4", "-0.4<#eta<-0.2", "-0.2<#eta< 0", "0<#eta<0.2", "0.2<#eta<0.4", "0.4<#eta< 0.6", "0.6<#eta< 0.8"}, "string representing an eta interval"}; // CCDB paths Configurable pathDCAxy{"pathDCAxy", "Users/o/omvazque/DCAxy/Test", "base path to the ccdb object"}; @@ -319,7 +329,7 @@ struct PiKpRAA { }; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - Service ccdb; + Service ccdb{}; struct ConfigDCA { TH1F* hDCAxy = nullptr; @@ -340,42 +350,7 @@ struct PiKpRAA { bool isCalPlateauLoaded = false; } etaCal; - // TrackSelection trkSelGlobalOpenDCAxy; - // TrackSelection trkSelGlobal; - - // TrackSelection trkSelOpenDCAxy() { - // TrackSelection selectedTracks; - // selectedTracks.SetTrackType(o2::aod::track::TrackTypeEnum::Track); // Run 3 track asked by default - // // selectedTracks.SetPtRange(trackSelections.minPt, trackSelections.maxPt); - // // selectedTracks.SetEtaRange(trackSelections.minEta, trackSelections.maxEta); - // // selectedTracks.SetRequireITSRefit(trackSelections.itsRefit); - // // selectedTracks.SetRequireTPCRefit(trackSelections.tpcRefit); - // // selectedTracks.SetRequireGoldenChi2(trackSelections.chi2Golden); - // // selectedTracks.SetMinNCrossedRowsTPC(trackSelections.minNCrossedRows); - // // selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(trackSelections.minNCrossedRowsOverFindableCls); - // // selectedTracks.SetRequireHitsInITSLayers(1, {0, 1, 2}); // one hit in any layer of the inner barrel - // // selectedTracks.SetMaxDcaZ(trackSelections.maxDCAZ); - // return selectedTracks; - // } - - // TrackSelection trkSelGlb() { - // TrackSelection selectedTracks; - // selectedTracks.SetTrackType(o2::aod::track::TrackTypeEnum::Track); // Run 3 track asked by default - // // selectedTracks.SetPtRange(trackSelections.minPt, trackSelections.maxPt); - // // selectedTracks.SetEtaRange(trackSelections.minEta, trackSelections.maxEta); - // // selectedTracks.SetRequireITSRefit(trackSelections.itsRefit); - // // selectedTracks.SetRequireTPCRefit(trackSelections.tpcRefit); - // // selectedTracks.SetRequireGoldenChi2(trackSelections.chi2Golden); - // // selectedTracks.SetMinNCrossedRowsTPC(trackSelections.minNCrossedRows); - // // selectedTracks.SetMinNCrossedRowsOverFindableClustersTPC(trackSelections.minNCrossedRowsOverFindableCls); - // // selectedTracks.SetRequireHitsInITSLayers(1, {0, 1, 2}); // one hit in any layer of the inner barrel - // // selectedTracks.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / pow(pt, 1.1f); }); - // // selectedTracks.SetMaxDcaZ(trackSelections.maxDCAZ); - // return selectedTracks; - // } - - int currentRunNumberNchSel; - int currentRunNumberPhiSel; + int currentRunNumberPhiSel = -1; void init(InitContext const&) { @@ -384,10 +359,7 @@ struct PiKpRAA { rctChecker.init(rctLabel.value, rctCheckZDC.value, rctTreatLimitedAcceptanceAsBad.value); } - currentRunNumberNchSel = -1; currentRunNumberPhiSel = -1; - // trkSelGlobalOpenDCAxy = trkSelOpenDCAxy(); - // trkSelGlobal = trkSelGlb(); // define axes you want to use const std::string titlePorPTPC{trackSelections.usePinPhiSelection ? "#it{p} (GeV/#it{c})" : "#it{p} inner TPC wall (GeV/#it{c})"}; @@ -396,25 +368,25 @@ struct PiKpRAA { const AxisSpec axisNcl{161, -0.5, 160.5, "#it{N}_{cl}"}; const AxisSpec axisPt{binsPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec axisPtV0s{binsPtV0s, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisPtNcl{binsPtNcl, titlePorPTPC.data()}; - const AxisSpec axisXPhiCut{binsPtPhiCut, titlePorPTPC.data()}; const AxisSpec axisCent{binsCent, "Centrality Perc."}; - const char* endingEta[kNEtaHists] = {"86", "64", "42", "20", "02", "24", "46", "68"}; - const char* latexEta[kNEtaHists] = {"-0.8<#eta<-0.6", "-0.6<#eta<-0.4", "-0.4<#eta<-0.2", "-0.2<#eta<0", "0<#eta<0.2", "0.2<#eta<0.4", "0.4<#eta<0.6", "0.6<#eta<0.8"}; + const auto latexEta = (std::vector)vecLatexEta; + const auto endingEta = (std::vector)vecEndingEta; registry.add("EventCounter", ";;Events", kTH1F, {axisEvent}); registry.add("HasBCVsFT0VsTVXVsEvSel", "Alls=1 | BC=2 | FT0=3 | TVX=4 | EvSel=5;;", kTH1F, {{5, 0.5, 5.5}}); registry.add("zPos", "With Event Selection;;Entries;", kTH1F, {axisZpos}); - registry.add("T0Ccent", ";;Entries", kTH1F, {axisCent}); + registry.add("Centrality", ";;Entries", kTH1F, {axisCent}); registry.add("RCTSel", "Event accepted if flag=false: All=1 | RTC sel=2;;;", kTH1F, {{2, 0.5, 2.5}}); - registry.add("T0CcentVsRCTSel", "Event accepted if flag=false;;RCT Status;", kTH2F, {{{axisCent}, {9, 0.5, 9.5}}}); - registry.add("T0CcentVsFoundFT0", "Found(=1.5) NOT Found(=0.5);;Status;", kTH2F, {{{axisCent}, {2, 0, 2}}}); - registry.add("T0CcentVsBCVsFT0VsTVXVsEvSel", "All=1 | BC=2 | FT0=3 | TVX=4 | EvSel=5;;Status;", kTH2F, {{axisCent}, {5, 0.5, 5.5}}); - registry.add("NchVsCent", "Measured Nch v.s. Centrality (At least Once Rec. Coll. + Sel. criteria);;Nch", kTH2F, {{axisCent, {nBinsNch, minNch, maxNch}}}); - registry.add("NclVsEtaPID", ";#eta;Ncl used for PID", kTH2F, {axisEta, axisNcl}); - registry.add("NclVsEtaPIDp", ";#eta;#LTNcl#GT used for PID", kTProfile, {axisEta}); - registry.add("NclVsEta", ";#eta;Found Ncl TPC", kTH2F, {axisEta, axisNcl}); - registry.add("NclVsEtap", ";#eta;Found #LTNcl#GT TPC", kTProfile, {axisEta}); + registry.add("CentralityVsRCTSel", "Event accepted if flag=false;;RCT Status;", kTH2F, {{{axisCent}, {9, 0.5, 9.5}}}); + registry.add("CentralityVsFoundFT0", "Found(=1.5) NOT Found(=0.5);;Status;", kTH2F, {{{axisCent}, {2, 0, 2}}}); + registry.add("NchVsCent", "Measured Nch v.s. Centrality (At least Once Rec. Coll. + Sel. criteria);;Nch", kTH2F, {axisCent, axisNch}); + registry.add("NclVsEtaPID", "", kTH2F, {axisEta, axisNcl}); + registry.add("NclVsEtaPIDp", "", kTProfile, {axisEta}); + registry.add("NclVsEta", "", kTH2F, {axisEta, axisNcl}); + registry.add("NclVsEtap", "", kTProfile, {axisEta}); + registry.add("NclVsPhiHist", "", kTH2F, {{100, 0, o2::constants::math::TwoPI}, {axisNcl}}); + registry.add("NclVsPhiProfile", "", kTProfile, {{100, 0, o2::constants::math::TwoPI}}); + registry.add("EtaVsPhi", ";#eta;#varphi;", kTH2F, {{axisEta}, {100, 0, o2::constants::math::TwoPI}}); registry.add("MomentumTPCVsP", ";Global track momentum (GeV/#it{c});Momentum at inner wall of the TPC (GeV/#it{c});", kTH2F, {axisPtV0s, axisPtV0s}); registry.add("DCAxyVsPt", ";#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); registry.add("DCAzVsPt", ";#it{p}_{T} (GeV/#it{c});DCA_{z} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); @@ -448,7 +420,7 @@ struct PiKpRAA { x->SetBinLabel(19, "Nch Sel."); x->SetBinLabel(20, "INEL > 0"); - auto hrct = registry.get(HIST("T0CcentVsRCTSel")); + auto hrct = registry.get(HIST("CentralityVsRCTSel")); auto* y = hrct->GetYaxis(); y->SetBinLabel(1, "All"); y->SetBinLabel(2, "kFT0Bad"); @@ -461,7 +433,7 @@ struct PiKpRAA { y->SetBinLabel(9, "kTPCLimAccMCRepr"); if (doprocessCalibrationAndV0s) { - registry.add("NchVsNPV", ";Nch; NPV;", kTH2F, {{{nBinsNPV, minNpv, maxNpv}, {nBinsNch, minNch, maxNch}}}); + registry.add("NchVsNPV", ";Nch; NPV;", kTH2F, {axisNPV, axisNch}); registry.add("V0sCounter", ";V0 type; Entries;", kTH1F, {{4, 0.5, 4.5}}); registry.add("dcaDauVsPt", ";V0 #it{p}_{T} (GeV/#it{c});DCA_{xy} (cm) daughters;", kTH2F, {axisPt, axisDCAxy}); registry.add("nSigPiFromK0s", ";#it{n#sigma};;", kTH2F, {axisPtV0s, axisNsigmaTPC}); @@ -481,8 +453,8 @@ struct PiKpRAA { registry.add("MassLVsPt", ";;Inv. Mass (GeV/#it{c}^{2});", kTH2F, {axisPtV0s, axisLambdaMass}); registry.add("MassALVsPt", ";;Inv. Mass (GeV/#it{c}^{2});", kTH2F, {axisPtV0s, axisLambdaMass}); registry.add("MassGVsPt", ";;Inv. Mass (GeV/#it{c}^{2});", kTH2F, {axisPtV0s, axisGammaMass}); - registry.add("NclVsPhip", Form("Found #LTNcl#GT TPC;%s (GeV/#it{c});#varphi", titlePorPTPC.data()), kTProfile2D, {{{axisXPhiCut}, {350, 0.0, 0.35}}}); - registry.add("NclPIDVsPhip", Form("#LTNcl#GT used for PID;%s (GeV/#it{c});#varphi", titlePorPTPC.data()), kTProfile2D, {{{axisXPhiCut}, {350, 0.0, 0.35}}}); + registry.add("NclVsPhip", Form("Found #LTNcl#GT TPC;%s (GeV/#it{c});#varphi", titlePorPTPC.data()), kTProfile2D, {{{axisPt}, {350, 0.0, 0.35}}}); + registry.add("NclPIDVsPhip", Form("#LTNcl#GT used for PID;%s (GeV/#it{c});#varphi", titlePorPTPC.data()), kTProfile2D, {{{axisPt}, {350, 0.0, 0.35}}}); registry.add("NclVsEtaPiMIP", "MIP #pi^{+} + #pi^{-}: 0.4 < #it{p} < 0.6 [GeV/#it{c}], 40 < dE/dx < 60;#eta;Ncl TPC", kTH2F, {{{axisEta}, {161, -0.5, 160.5}}}); registry.add("NclVsEtaPiMIPp", "MIP #pi^{+} + #pi^{-}: 0.4 < #it{p} < 0.6 [GeV/#it{c}], 40 < dE/dx < 60;#eta;#LTNcl#GT TPC", kTProfile, {axisEta}); registry.add("NclVsEtaPiV0", ";#eta;Ncl TPC", kTH2F, {axisEta, axisNcl}); @@ -491,7 +463,6 @@ struct PiKpRAA { registry.add("NclVsEtaPrV0p", ";#eta;#LTNcl#GT TPC", kTProfile, {axisEta}); registry.add("NclVsEtaElV0", ";#eta;Ncl TPC", kTH2F, {axisEta, axisNcl}); registry.add("NclVsEtaElV0p", ";#eta;#LTNcl#GT TPC", kTProfile, {axisEta}); - registry.add("EtaVsPhi", ";#eta;#varphi;", kTH2F, {{axisEta}, {100, 0, o2::constants::math::TwoPI}}); registry.add("EtaVsYK0s", ";#eta;#it{y};", kTH2F, {axisEta, axisY}); registry.add("EtaVsYPiL", ";#eta;#it{y};", kTH2F, {axisEta, axisY}); registry.add("EtaVsYPrL", ";#eta;#it{y};", kTH2F, {axisEta, axisY}); @@ -511,101 +482,166 @@ struct PiKpRAA { registry.add("dEdxVsEtaElMIPV0p", "e^{+} + e^{-}: 0.4 <#it{p}_{T} < 0.6 [GeV/#it{c}];#eta; #LTdE/dx#GT", kTProfile, {axisEta}); registry.add("pTVsCent", "", kTH2F, {axisPt, axisCent}); - for (int i = 0; i < kNEtaHists; ++i) { - dEdx[i] = registry.add(Form("dEdx_%s", endingEta[i]), Form("%s;Momentum (GeV/#it{c});dE/dx;", latexEta[i]), kTH3F, {axisPt, axisdEdx, axisCent}); - pTVsP[i] = registry.add(Form("pTVsP_%s", endingEta[i]), Form("%s;Momentum (GeV/#it{c});#it{p}_{T} (GeV/#it{c});", latexEta[i]), kTH2F, {axisPt, axisPt}); - dEdxPiV0[i] = registry.add(Form("dEdxPiV0_%s", endingEta[i]), Form("#pi^{+} + #pi^{-}, %s;Momentum (GeV/#it{c});dE/dx;", latexEta[i]), kTH2F, {axisPtV0s, axisdEdx}); - dEdxPrV0[i] = registry.add(Form("dEdxPrV0_%s", endingEta[i]), Form("p + #bar{p}, %s;Momentum (GeV/#it{c});dE/dx;", latexEta[i]), kTH2F, {axisPtV0s, axisdEdx}); - dEdxElV0[i] = registry.add(Form("dEdxElV0_%s", endingEta[i]), Form("e^{+} + e^{-}, %s;Momentum (GeV/#it{c});dE/dx;", latexEta[i]), kTH2F, {axisPtV0s, axisdEdx}); - nClVsP[i] = registry.add(Form("NclVsPPrimaries_%s", endingEta[i]), Form("%s;;Ncl TPC", latexEta[i]), kTH2F, {axisPtNcl, axisNcl}); - nClPIDVsnCl[i] = registry.add(Form("NclPIDVsNcl_%s", endingEta[i]), Form("Primaries, %s;;", latexEta[i]), kTH2F, {axisNcl, axisNcl}); - nCrRoVsNcl[i] = registry.add(Form("NCrRoVsNcl_%s", endingEta[i]), Form("%s;;N Crossed Rows", latexEta[i]), kTH2F, {axisNcl, axisNcl}); - nClVsPElV0[i] = registry.add(Form("NclVsPElV0_%s", endingEta[i]), Form("%s;;Ncl TPC", latexEta[i]), kTH2F, {axisPtNcl, axisNcl}); - nClVsPPiV0[i] = registry.add(Form("NclVsPPiV0_%s", endingEta[i]), Form("%s;;Ncl TPC", latexEta[i]), kTH2F, {axisPtNcl, axisNcl}); - nClVsPPrV0[i] = registry.add(Form("NclVsPPrV0_%s", endingEta[i]), Form("%s;;Ncl TPC", latexEta[i]), kTH2F, {axisPtNcl, axisNcl}); - nClVsPp[i] = registry.add(Form("NclVsPrimariesp_%s", endingEta[i]), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i]), kTProfile, {axisPtNcl}); - nCrRoVsNclp[i] = registry.add(Form("NCrRoVsNclp_%s", endingEta[i]), Form("%s;;#LT#it{N}_{cro rows}#GT TPC", latexEta[i]), kTProfile, {axisNcl}); - nClVsPpElV0[i] = registry.add(Form("NclVsPElV0p_%s", endingEta[i]), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i]), kTProfile, {axisPtNcl}); - nClVsPpPiV0[i] = registry.add(Form("NclVsPPiV0p_%s", endingEta[i]), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i]), kTProfile, {axisPtNcl}); - nClVsPpPrV0[i] = registry.add(Form("NclVsPPrV0p_%s", endingEta[i]), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i]), kTProfile, {axisPtNcl}); - nClPIDVsnClp[i] = registry.add(Form("NclPIDVsNclp_%s", endingEta[i]), Form("%s;;#LT#it{N}_{cl} PID#GT TPC", latexEta[i]), kTProfile, {axisNcl}); - } - } - - if (doprocessSim) { - registry.add("zPosMC", "Generated Events With at least One Rec. Collision + Sel. criteria;;Entries;", kTH1F, {axisZpos}); + for (int i = 0; i < KnEtaHists; ++i) { + dEdx[i] = registry.add(Form("dEdx_%s", endingEta[i].c_str()), Form("%s;Momentum;dE/dx;", latexEta[i].c_str()), kTH3F, {axisPt, axisdEdx, axisCent}); + pTVsP[i] = registry.add(Form("pTVsP_%s", endingEta[i].c_str()), Form("%s;Momentum;#it{p}_{T} (GeV/#it{c});", latexEta[i].c_str()), kTH2F, {axisPt, axisPt}); + dEdxPiTOF[i] = registry.add(Form("dEdxPiTOF_%s", endingEta[i].c_str()), Form("#pi^{+} + #pi^{-}, %s;Momentum;dE/dx;", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisdEdx}); + dEdxPiTOF2[i] = registry.add(Form("dEdxPiTOF2_%s", endingEta[i].c_str()), Form("#pi^{+} + #pi^{-}, %s;Momentum;dE/dx;", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisdEdx}); + dEdxPiV0[i] = registry.add(Form("dEdxPiV0_%s", endingEta[i].c_str()), Form("#pi^{+} + #pi^{-}, %s;Momentum;dE/dx;", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisdEdx}); + dEdxPrV0[i] = registry.add(Form("dEdxPrV0_%s", endingEta[i].c_str()), Form("p + #bar{p}, %s;Momentum;dE/dx;", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisdEdx}); + dEdxElV0[i] = registry.add(Form("dEdxElV0_%s", endingEta[i].c_str()), Form("e^{+} + e^{-}, %s;Momentum;dE/dx;", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisdEdx}); + nClVsP[i] = registry.add(Form("NclVsPPrimaries_%s", endingEta[i].c_str()), Form("%s;;Ncl TPC", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisNcl}); + nClVsPElV0[i] = registry.add(Form("NclVsPElV0_%s", endingEta[i].c_str()), Form("%s;;Ncl TPC", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisNcl}); + nClVsPPiV0[i] = registry.add(Form("NclVsPPiV0_%s", endingEta[i].c_str()), Form("%s;;Ncl TPC", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisNcl}); + nClVsPPrV0[i] = registry.add(Form("NclVsPPrV0_%s", endingEta[i].c_str()), Form("%s;;Ncl TPC", latexEta[i].c_str()), kTH2F, {axisPtV0s, axisNcl}); + nClVsPp[i] = registry.add(Form("NclVsPrimariesp_%s", endingEta[i].c_str()), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i].c_str()), kTProfile, {axisPtV0s}); + nClVsPpElV0[i] = registry.add(Form("NclVsPElV0p_%s", endingEta[i].c_str()), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i].c_str()), kTProfile, {axisPtV0s}); + nClVsPpPiV0[i] = registry.add(Form("NclVsPPiV0p_%s", endingEta[i].c_str()), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i].c_str()), kTProfile, {axisPtV0s}); + nClVsPpPrV0[i] = registry.add(Form("NclVsPPrV0p_%s", endingEta[i].c_str()), Form("%s;;#LT#it{N}_{cl}#GT TPC", latexEta[i].c_str()), kTProfile, {axisPtV0s}); + etaTest[i] = registry.add(Form("etaTest_%s", endingEta[i].c_str()), ";Eta;Entries;", kTH1F, {axisEta}); + } + } + + if (doprocessSignalLoss) { + registry.add("EventCounterMC", "From 2nd bin, corresponds to number of MC Coll with associated Rec Coll.;;Events", kTH1F, {{7, 0, 7}}); + registry.add("zPosMC", "From the association between the MC event & the Rec. Coll with largest number of PV contributors & NO Evt. Sel.;;Entries;", kTH1F, {axisZpos}); + registry.add("zPosMCAll", "All MC Events;;Entries;", kTH1F, {axisZpos}); + registry.add("EtaMCParAllMCColl", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); + registry.add("EtaMCParAllMCColl_Pion", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); + registry.add("EtaMCParAllMCColl_Kaon", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); + registry.add("EtaMCParAllMCColl_Proton", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); + registry.add("EtaMCParAllMCColl_Electron", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); + registry.add("EtaMCParAllMCColl_Rest", "#eta from all MC particles in all MC collisions;#eta;Entries;", kTH1F, {{50, -10.0, 10.0}}); registry.add("dcaVsPtPiDec", "Secondary pions from decays;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); registry.add("dcaVsPtPrDec", "Secondary protons from decays;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); registry.add("dcaVsPtPiMat", "Secondary pions from material interactions;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); registry.add("dcaVsPtPrMat", "Secondary protons from material interactions;#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);Centrality Perc.;", kTH3F, {axisPt, axisDCAxy, axisCent}); - } + registry.add("DCAxyVsPtWithSelection", ";#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); + registry.add("DCAzVsPtWithSelection", ";#it{p}_{T} (GeV/#it{c});DCA_{z} (cm);", kTH2F, {{axisPtFineFixedWidth}, {axisDCAxy}}); - if (doprocessSim) { + registry.add("NumOfRecColl", "Num. of times a MC evt. is reconstructed;N;Entries", kTH1F, {{5, -0.5, 4.5}}); + registry.add("NumOfRecCollVsNContri", "Num. of times a MC evt. is reconstructed VS Num. of PV contributors;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {{8, -0.5, 7.5}, axisNch}); + registry.add("NumOfRecCollVsCentrality", "Num. of times a MC evt. is reconstructed VS Centrality WITH EVT SEL;Number of times a MC event is reconstructed;Number of tracks used for the PV", kTH2F, {axisCent, {8, -0.5, 7.5}}); - // MC events passing the TVX requirement - registry.add("NchMCcentVsTVX", ";Passed(=1.5) NOT Passed(=0.5);", kTH2F, {{{nBinsNch, minNch, maxNch}, {2, 0, 2}}}); - - registry.add("NumberOfRecoCollisions", "Number of times Gen. Coll.are reconstructed;N;Entries", kTH1F, {{10, -0.5, 9.5}}); - - // Pt resolution + // PT RESOLUTION registry.add("PtResolution", "p_{T} resolution;;(pt_{rec} - pt_{gen})/pt_{gen};", kTH2F, {axisPt, {100, -1.0, 1.0}}); - // Needed to calculate the numerator of the Acceptance X Efficiency + // NUMERATOR OF THE ACCEPTANCE X EFFICIENCY registry.add("PtPiVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); registry.add("PtKaVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); registry.add("PtPrVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); + // DENOMINATOR OF THE ACCEPTANCE X EFFICIENCY (TRUE PT) registry.add("PtGenPiVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); registry.add("PtGenKaVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); registry.add("PtGenPrVsCent_WithRecoEvt", "Generated Events With at least One Rec. Collision + Sel. criteria;;;", kTH2F, {axisPt, axisCent}); - // Needed to calculate the denominator of the Acceptance X Efficiency + // DENOMINATOR OF THE ACCEPTANCE X EFFICIENCY registry.add("PtPiVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); registry.add("PtKaVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); registry.add("PtPrVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {axisPt, axisCent}); - // Needed for the Gen. Nch to Centrality conversion - registry.add("NchMCVsCent", "Generated Nch v.s. Centrality (At least Once Rec. Coll. + Sel. criteria);;Gen. Nch MC (|#eta|<0.8)", kTH2F, {{axisCent, {nBinsNch, minNch, maxNch}}}); - - // Needed to measure Event Loss - registry.add("NchMC_WithRecoEvt", "Generated Nch of Evts With at least one Rec. Coll. + Sel. criteria;Gen. Nch MC (|#eta|<0.8);Entries", kTH1F, {{nBinsNch, minNch, maxNch}}); - registry.add("NchMC_AllGen", "Generated Nch of All Gen. Evts.;Gen. Nch;Entries", kTH1F, {{nBinsNch, minNch, maxNch}}); - - // Needed to measure Event Splitting - registry.add("Centrality_WRecoEvt", "Generated Events With at least One Rec. Collision And NO Sel. criteria;;Entries", kTH1F, {axisCent}); - registry.add("Centrality_WRecoEvtWSelCri", "Generated Events With at least One Rec. Collision + Sel. criteria;;Entries", kTH1F, {axisCent}); - registry.add("Centrality_AllRecoEvt", "Generated Events Irrespective of the number of times it was reconstructed + Evt. Selections;;Entries", kTH1F, {axisCent}); - - // Needed to calculate the numerator of the Signal Loss correction - registry.add("PtPiVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("PtKaVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("PtPrVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - - // Needed to calculate the denominator of the Signal Loss correction - registry.add("PtPiVsNchMC_AllGen", "All Generated Events;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("PtKaVsNchMC_AllGen", "All Generated Events;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("PtPrVsNchMC_AllGen", "All Generated Events;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - - registry.add("MCclosure_PtMCPiVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("MCclosure_PtMCKaVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("MCclosure_PtMCPrVsNchMC", "All Generated Events 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - - registry.add("MCclosure_PtPiVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("MCclosure_PtKaVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - registry.add("MCclosure_PtPrVsNchMC", "Gen Evts With at least one Rec. Coll. + Sel. criteria 4 MC closure;;Gen. Nch (|#eta|<0.8);", kTH2F, {{axisPt, {nBinsNch, minNch, maxNch}}}); - - for (int i = 0; i < kNEtaHists; ++i) { - dEdxPiMC[i] = registry.add(Form("dEdxPi_%s", endingEta[i]), Form("%s;Momentum (GeV/#it{c});dE/dx;", latexEta[i]), kTH2F, {axisPtV0s, axisdEdx}); - dEdxMuMC[i] = registry.add(Form("dEdxMu_%s", endingEta[i]), Form("%s;Momentum (GeV/#it{c});dE/dx;", latexEta[i]), kTH2F, {axisPtV0s, axisdEdx}); - } + // FOR MAPPING BETWEN MC NCH AND CENTRALITY. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("NchMCVsCent_WithRecoEvt_BestCollIdx", "Gen Nch vs Cent (Evt. Sel. + W/Largest N PV Contributors);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); + // FOR MAPPING BETWEN MC NCH AND CENTRALITY. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES + registry.add("NchMCVsCent_WithRecoEvt", "Gen Nch vs Cent (All Rec. Coll. + Evt. Sel.);;Gen. Nch MC", kTH2F, {axisCent, axisNch}); + + // DENOMINATOR OF THE EVENT LOSS. ALL GEN EVENTS + registry.add("NchMC_AllGen", "MC Nch of ALL events;Gen. Nch;Entries", kTH1F, {axisNch}); + // NUMERATOR OF THE EVENT LOOS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("NchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;Gen Nch;Entries", kTH1F, {axisNch}); + // NUMERATOR OF THE EVENT LOOS. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES + registry.add("NchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch;Entries", kTH1F, {axisNch}); + // DENOMINATOR OF THE EVENT LOOS. ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES WITH FT0 INFO + TVX-TRIGGERED + registry.add("NchMC_WithRecoEvt_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll With FT0 Info + TVX;Gen. Nch;Entries", kTH1F, {axisNch}); + // NUMERATOR OF THE EVENT LOOS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx With FT0 Info + TVX;Gen Nch;Entries", kTH1F, {axisNch}); + + // NUMERATOR OF THE EVENT SPLITTING. + registry.add("Centrality_WRecoEvtWSelCri", "From the association between the MC event & the Rec. Coll with largest number of PV contributors + Evt. Sel.;;Entries", kTH1F, {axisCent}); + // DENOMINATOR OF THE EVENT SPLITTING. + registry.add("Centrality_AllRecoEvt", "All Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;;Entries", kTH1F, {axisCent}); + + // ======================================= + + // USED FOR CALCULATING EVENT LOSS AND SIGNAL LOSS AS IN THE DN/DETA ANALYSIS + registry.add("Centrality_FineBins_WithRecoEvt", "Rec. Coll. Irrespective of the times it was reconstructed + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); + registry.add("Centrality_FineBins_WithRecoEvt_BestCollIdx", "Rec. Coll. W/best Coll Indx + Evt. Sel.;Centrality;Entries", kTH1F, {axisFineCent}); + registry.add("NchMC_Fwd_AllGen", "MC Nch of ALL events;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt", "True Nch from MC events W/at leas One Rec. Coll + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_BestCollIdx", "True Nch from MC events W/best Coll Idx + Evt. Sel.;Gen. Nch (Forward);Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll With FT0 Info + TVX;Gen. Nch;Entries", kTH1F, {axisNch}); + registry.add("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx With FT0 Info + TVX;Gen Nch;Entries", kTH1F, {axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("PtPiVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVEN IF RECONSTRUCTED MULTIPLE TIMES + registry.add("PtPiVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // DENOMINATOR OF THE SIGNAL LOSS + registry.add("PtPiVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_AllGen", "All Generated Events;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // DENOMITOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMCFwd_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch Forward;", kTH2F, {axisPt, axisNch}); + + // ======================================= + + // NUMERATOR OF THE SIGNAL LOSS. BASED ONLY ON THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS TO PV + registry.add("PtPiVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_WithRecoEvt_BestCollIdx", "Gen Evts W/best Coll Idx + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVEN IF RECONSTRUCTED MULTIPLE TIMES + registry.add("PtPiVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_WithRecoEvt", "Gen Evts W/at leas One Rec. Coll + Evt. Sel.;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + + // DENOMINATOR OF THE SIGNAL LOSS + registry.add("PtPiVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_AllGen", "All Generated Events;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + + // NUMERATOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMC_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_HasFT0AndTVX", "Gen Evts W/at leas One Rec. Coll + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + + // DENOMITOR OF THE SIGNAL LOSS. USES ALL THE RECONSTRUCTED COLLISIONS EVENT IF RECONSTRUCTED MULTIPLE TIMES (A LA PI0) + registry.add("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + registry.add("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx", "Gen Evts W/best Coll Idx + FT0 Info & TVX triggered;;Gen. Nch;", kTH2F, {axisPt, axisNch}); + + auto hMcCouter = registry.get(HIST("EventCounterMC")); + auto* xMcCounter = hMcCouter->GetXaxis(); + xMcCounter->SetBinLabel(1, "All MC Coll"); + xMcCounter->SetBinLabel(2, "nContributors > 0"); + xMcCounter->SetBinLabel(3, "nContributors > 0 + HasFT0"); + xMcCounter->SetBinLabel(4, "nContributors > 0 + HasFT0 & HasTVX"); + xMcCounter->SetBinLabel(5, "nContributors > 0 + HasFT0 & HasTVX & Only largest Contributors"); + xMcCounter->SetBinLabel(6, "W/Asso Rec. Coll + Evt. Sel + bestCollIdx"); } LOG(info) << "\trequireGoodRct=" << requireGoodRct.value; LOG(info) << "\tccdbNoLaterThan=" << ccdbNoLaterThan.value; LOG(info) << "\tselINELgt0=" << selINELgt0.value; + LOG(info) << "\tcentralitySelector=" << centralitySelector.value; LOG(info) << "\tv0TypeSelection=" << static_cast(v0Selections.v0TypeSelection); - LOG(info) << "\tselElecFromGammas=" << v0Selections.selElecFromGammas; LOG(info) << "\tapplyInvMassSel=" << v0Selections.applyInvMassSel; + LOG(info) << "\tsignCharge =" << trackSelections.signCharge.value; LOG(info) << "\tminPt=" << trackSelections.minPt; LOG(info) << "\tmaxPt=" << trackSelections.maxPt; LOG(info) << "\tuseNclsPID=" << trackSelections.useNclsPID; @@ -614,7 +650,6 @@ struct PiKpRAA { LOG(info) << "\tapplyNclSel=" << trackSelections.applyNclSel; LOG(info) << "\tapplyPhiCut=" << trackSelections.applyPhiCut; LOG(info) << "\tusePinPhiSelection=" << trackSelections.usePinPhiSelection; - LOG(info) << "\tcurrentRunNumberNchSel=" << currentRunNumberNchSel; LOG(info) << "\tcurrentRunNumberPhiSel=" << currentRunNumberPhiSel; LOG(info) << "\tpathDCAxy=" << pathDCAxy.value; LOG(info) << "\tpathDCAz=" << pathDCAz.value; @@ -646,14 +681,24 @@ struct PiKpRAA { } if (trackSelections.applyNclSel) { - LOG(info) << "\ttrackSelections.minNcls=" << trackSelections.minNcls; - LOG(info) << "\tv0Selections.minNcl=" << v0Selections.minNcl; + LOG(info) << "\ttrackSelections.minNcl=" << trackSelections.minNcl; + LOG(info) << "\tv0Selections.minNclV0Daugther=" << v0Selections.minNclV0Daugther; } if (loadHisWithDCASel) { + LOG(info) << "\tUsing DCAxy and DCAz selections"; LOG(info) << "\tpathDCAxy.value=" << pathDCAxy.value; LOG(info) << "\tpathDCAz.value=" << pathDCAz.value; + loadDCAselections(); + LOG(info) << "\tdcaSelectionsLoaded=" << cfgDCA.dcaSelectionsLoaded; } + + auto vecLowEtaSel = (std::vector)vecLowEta; + auto vecUpEtaSel = (std::vector)vecUpEta; + LOGF(info, "vecLowEta: %s", printArray(vecLowEtaSel).c_str()); + LOGF(info, "vecUpEta: %s", printArray(vecUpEtaSel).c_str()); + LOGF(info, "vecEndingEta: %s", printArray(endingEta).c_str()); + LOGF(info, "vecLatexEta: %s", printArray(latexEta).c_str()); } void processCalibrationAndV0s(ColEvSels::iterator const& collision, BCsRun3 const& /**/, aod::V0Datas const& v0s, aod::FV0As const& /**/, aod::FT0s const& /**/, TracksFull const& tracks) @@ -662,11 +707,23 @@ struct PiKpRAA { // Table's size: " << collisions.tableSize() << "\n"; // LOG(info) << "Run number: " << foundBC.runNumber() << "\n"; + const auto& kLowEta = (std::vector)vecLowEta; + const auto& kHighEta = (std::vector)vecUpEta; + const auto& foundBC = collision.foundBC_as(); const uint64_t timeStamp{foundBC.timestamp()}; const int magField{getMagneticField(timeStamp)}; const double nPV{collision.multNTracksPVeta1() / 1.}; - const float centrality{isT0Ccent ? collision.centFT0C() : collision.centFT0M()}; + float centrality{-999.0}; + if (centralitySelector.value == "FT0C") { + centrality = collision.centFT0C(); + } else if (centralitySelector.value == "FT0M") { + centrality = collision.centFT0M(); + } else if (centralitySelector.value == "FV0A") { + centrality = collision.centFV0A(); + } else { + centrality = -999.0; + } // Apply RCT selection? if (requireGoodRct) { @@ -680,58 +737,56 @@ struct PiKpRAA { const bool isTPCPIDBad{requireBCRct ? foundBC.rct_bit(kTPCBadPID) : collision.rct_bit(kTPCBadPID)}; const bool isTPCLimAcc{requireBCRct ? foundBC.rct_bit(kTPCLimAccMCRepr) : collision.rct_bit(kTPCLimAccMCRepr)}; - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 1.0); - if (!isFT0Bad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 2.0); - if (!isITSBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 3.0); - if (!isITSLimAcc) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 4.0); - if (!isTOFBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 5.0); - if (!isTOFLimAcc) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 6.0); - if (!isTPCTrackingBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 7.0); - if (!isTPCPIDBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 8.0); - if (!isTPCLimAcc) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 9.0); + registry.fill(HIST("CentralityVsRCTSel"), centrality, 1.0); + if (!isFT0Bad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 2.0); + } + if (!isITSBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 3.0); + } + if (!isITSLimAcc) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 4.0); + } + if (!isTOFBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 5.0); + } + if (!isTOFLimAcc) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 6.0); + } + if (!isTPCTrackingBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 7.0); + } + if (!isTPCPIDBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 8.0); + } + if (!isTPCLimAcc) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 9.0); + } registry.fill(HIST("RCTSel"), 1.0); - if (!rctChecker(collision)) + if (!rctChecker(collision)) { return; + } registry.fill(HIST("RCTSel"), 2.0); } - if (!isEventSelected(collision)) + if (!isEventSelected(collision)) { return; + } //--------------------------- // Control histogram //--------------------------- - if (selHasFT0 && !collision.has_foundFT0()) - registry.fill(HIST("T0CcentVsFoundFT0"), centrality, 0.5); - - registry.fill(HIST("T0CcentVsFoundFT0"), centrality, 1.5); - - if (loadHisWithDCASel) { - const int nextRunNumber{foundBC.runNumber()}; - if (currentRunNumberNchSel != nextRunNumber) { - loadDCAselections(timeStamp); - currentRunNumberNchSel = nextRunNumber; - LOG(info) << "\tcurrentRunNumberNchSel= " << currentRunNumberNchSel << " timeStamp = " << timeStamp; - } - - // return if DCA selection objects are nullptr - if (!(cfgDCA.hDCAxy && cfgDCA.hDCAz)) - return; - - if (!cfgDCA.dcaSelectionsLoaded) - return; + if (selHasFT0 && !collision.has_foundFT0()) { + registry.fill(HIST("CentralityVsFoundFT0"), centrality, 0.5); } + registry.fill(HIST("CentralityVsFoundFT0"), centrality, 1.5); + + //--------------------------- + // Load Phi selection + //--------------------------- if (trackSelections.applyPhiCut) { const int nextRunNumber{foundBC.runNumber()}; if (currentRunNumberPhiSel != nextRunNumber) { @@ -741,12 +796,13 @@ struct PiKpRAA { } // return if phi cut objects are nullptr - if (!(phiCut.hPhiCutHigh && phiCut.hPhiCutLow)) + if (!(phiCut.hPhiCutHigh && phiCut.hPhiCutLow)) { return; + } } registry.fill(HIST("zPos"), collision.posZ()); - registry.fill(HIST("T0Ccent"), centrality); + registry.fill(HIST("Centrality"), centrality); // ================ // Track selection WITHOUT DCAxy and DCAz selections @@ -755,9 +811,11 @@ struct PiKpRAA { for (const auto& track : tracks) { const bool applyDca{false}; - if (!selectPrimary(track, applyDca)) + if (!selectPrimary(track, applyDca)) { continue; + } + const int charge{track.sign()}; const float momentum{track.p()}; const float pTPC{track.tpcInnerParam()}; const float pt{track.pt()}; @@ -765,13 +823,20 @@ struct PiKpRAA { const float dcaXy{track.dcaXY()}; const float pOrpTPC{trackSelections.usePinPhiSelection ? momentum : pTPC}; - float phiPrime{phi}; - const int charge{track.sign()}; - phiPrimeFunc(phiPrime, magField, charge); + // Reject track on the basis of its charge + if (trackSelections.signCharge.value == "Positive" && charge < KzeroInt) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > KzeroInt) { + continue; + } + float phiPrime{phi}; if (trackSelections.applyPhiCut) { - if (!passesPhiSelection(pOrpTPC, phiPrime)) + phiPrimeFunc(phiPrime, magField, charge); + if (!passesPhiSelection(pOrpTPC, phiPrime)) { continue; + } } const float piTPCNsigma{std::fabs(track.tpcNSigmaPi())}; @@ -781,10 +846,12 @@ struct PiKpRAA { const double piRadiusNsigma{std::sqrt(std::pow(piTPCNsigma, 2.) + std::pow(piTOFNsigma, 2.))}; const double prRadiusNsigma{std::sqrt(std::pow(prTPCNsigma, 2.) + std::pow(prTOFNsigma, 2.))}; - if (piRadiusNsigma < kThree) + if (piRadiusNsigma < Kthree) { registry.fill(HIST("dcaVsPtPi"), pt, dcaXy, centrality); - if (prRadiusNsigma < kThree) + } + if (prRadiusNsigma < Kthree) { registry.fill(HIST("dcaVsPtPr"), pt, dcaXy, centrality); + } } // ================ @@ -792,9 +859,6 @@ struct PiKpRAA { // ================ for (const auto& track : tracks) { - // if (!trkSelGlobal.IsSelected(track)) - // continue; - const float momentum{track.p()}; const float pTPC{track.tpcInnerParam()}; const float pt{track.pt()}; @@ -806,75 +870,102 @@ struct PiKpRAA { const int16_t nclFound{track.tpcNClsFound()}; const int16_t nclPID{track.tpcNClsPID()}; const int16_t ncl{trackSelections.useNclsPID ? nclPID : nclFound}; - const int16_t nCrossedRows{track.tpcNClsCrossedRows()}; + + // Reject track on the basis of its charge + if (trackSelections.signCharge.value == "Positive" && charge < KzeroInt) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > KzeroInt) { + continue; + } // ================ // DCAxy & DCAz Selections // ================ const bool applyDca{true}; - if (!selectPrimary(track, applyDca)) + if (!selectPrimary(track, applyDca)) { continue; + } float phiPrime{phi}; - phiPrimeFunc(phiPrime, magField, charge); - if (trackSelections.applyPhiCut) { - if (!passesPhiSelection(pOrpTPC, phiPrime)) + phiPrimeFunc(phiPrime, magField, charge); + if (!passesPhiSelection(pOrpTPC, phiPrime)) { continue; + } } if (trackSelections.applyEtaCal && etaCal.isMIPCalLoaded) { const double dedxCal{etaCal.pEtaCal->GetBinContent(etaCal.pEtaCal->FindBin(eta))}; - if (dedxCal > kMindEdxMIP && dedxCal < kMaxdEdxMIP) + if (dedxCal > KmindEdxMIP && dedxCal < KmaxdEdxMIP) { dedx *= (50.0 / dedxCal); - else + } else { continue; + } } int indexEta{-999}; - for (int i = 0; i < kNEtaHists; ++i) { + for (int i = 0; i < KnEtaHists; ++i) { if (eta >= kLowEta[i] && eta < kHighEta[i]) { indexEta = i; break; } } - if (indexEta < kZeroInt || indexEta > kSevenInt) + if (indexEta < KzeroInt || indexEta > KsevenInt) { continue; + } - if (momentum > kMinPMIP && momentum < kMaxPMIP && dedx > kMindEdxMIP && dedx < kMaxdEdxMIP) { + if (momentum > KminPMIP && momentum < KmaxPMIP && dedx > KmindEdxMIP && dedx < KmaxdEdxMIP) { registry.fill(HIST("dEdxVsEtaPiMIP"), eta, dedx); registry.fill(HIST("dEdxVsEtaPiMIPp"), eta, dedx); registry.fill(HIST("NclVsEtaPiMIP"), eta, ncl); registry.fill(HIST("NclVsEtaPiMIPp"), eta, ncl); } - if (momentum > kMinPMIP && momentum < kMaxPMIP && dedx > kMindEdxMIPPlateau && dedx < kMaxdEdxMIPPlateau) { + if (momentum > KminPMIP && momentum < KmaxPMIP && dedx > KmindEdxMIPPlateau && dedx < KmaxdEdxMIPPlateau) { if (track.hasTOF() && track.goodTOFMatch()) { const float tTOF{track.tofSignal()}; const float trkLength{track.length()}; const float tExpElTOF{track.tofExpSignalEl(tTOF)}; - if ((std::abs((tExpElTOF / tTOF) - kOne) < trackSelections.maxElTOFBeta) && tTOF > kZero && trkLength > kZero) { + if ((std::abs((tExpElTOF / tTOF) - Kone) < trackSelections.maxElTOFBeta) && tTOF > Kzero && trkLength > Kzero) { registry.fill(HIST("dEdxVsEtaElMIP"), eta, dedx); registry.fill(HIST("dEdxVsEtaElMIPp"), eta, dedx); } } } + if (track.hasTOF() && track.goodTOFMatch()) { + const float tTOF{track.tofSignal()}; + const float trkLength{track.length()}; + const float tExpPiTOF{track.tofExpSignalPi(tTOF)}; + const float tOFNsigmaPi{std::fabs(track.tofNSigmaPi())}; + const float tPCNsigmaPi{std::fabs(track.tpcNSigmaPi())}; + + if (tTOF > Kzero && trkLength > Kzero) { + if (std::abs((tExpPiTOF / tTOF) - Kone) < trackSelections.maxPiTOFBeta) { + dEdxPiTOF2[indexEta]->Fill(momentum, dedx); + } + + if (std::sqrt(std::pow(tOFNsigmaPi, 2.0) + std::pow(tPCNsigmaPi, 2.0)) < trackSelections.nSigmaPIDselPrim) { + dEdxPiTOF[indexEta]->Fill(momentum, dedx); + } + } + } + + etaTest[indexEta]->Fill(eta); dEdx[indexEta]->Fill(momentum, dedx, centrality); pTVsP[indexEta]->Fill(momentum, pt); nClVsP[indexEta]->Fill(pOrpTPC, ncl); nClVsPp[indexEta]->Fill(pOrpTPC, ncl); - nCrRoVsNcl[indexEta]->Fill(nCrossedRows, ncl); - nCrRoVsNclp[indexEta]->Fill(nCrossedRows, ncl); - nClPIDVsnCl[indexEta]->Fill(nclFound, nclPID); - nClPIDVsnClp[indexEta]->Fill(nclFound, nclPID); registry.fill(HIST("DCAxyVsPt"), pt, track.dcaXY()); registry.fill(HIST("DCAzVsPt"), pt, track.dcaZ()); registry.fill(HIST("MomentumTPCVsP"), momentum, pTPC); registry.fill(HIST("pTVsCent"), pt, centrality); registry.fill(HIST("EtaVsPhi"), eta, track.phi()); + registry.fill(HIST("NclVsPhiHist"), track.phi(), nclFound); + registry.fill(HIST("NclVsPhiProfile"), track.phi(), nclFound); registry.fill(HIST("NclVsEta"), eta, nclFound); registry.fill(HIST("NclVsEtap"), eta, nclFound); registry.fill(HIST("NclVsEtaPID"), eta, nclPID); @@ -890,8 +981,9 @@ struct PiKpRAA { for (const auto& v0 : v0s) { // Select V0 type - if (v0.v0Type() != v0Selections.v0TypeSelection) + if (v0.v0Type() != v0Selections.v0TypeSelection) { continue; + } // Positive-(negative-)charged tracks (daughters) const auto& posTrack = v0.posTrack_as(); @@ -916,80 +1008,89 @@ struct PiKpRAA { const int16_t negNclPID{negTrack.tpcNClsPID()}; const int16_t posNcl{trackSelections.useNclsPID ? posNclPID : posNclFound}; const int16_t negNcl{trackSelections.useNclsPID ? negNclPID : negNclFound}; - - phiPrimeFunc(posTrackPhiPrime, magField, posTrackCharge); - phiPrimeFunc(negTrackPhiPrime, magField, negTrackCharge); const float posPorPTPC{trackSelections.usePinPhiSelection ? posTrkP : posTrkPTPC}; const float negPorPTPC{trackSelections.usePinPhiSelection ? negTrkP : negTrkPTPC}; // Skip v0s with like-sig daughters - if (posTrack.sign() == negTrack.sign()) + if (posTrack.sign() == negTrack.sign()) { continue; + } // Passes Geometrical (Phi) cut? if (trackSelections.applyPhiCut) { - if (!(passesPhiSelection(posPorPTPC, posTrackPhiPrime) && passesPhiSelection(negPorPTPC, negTrackPhiPrime))) + phiPrimeFunc(posTrackPhiPrime, magField, posTrackCharge); + phiPrimeFunc(negTrackPhiPrime, magField, negTrackCharge); + if (!(passesPhiSelection(posPorPTPC, posTrackPhiPrime) && passesPhiSelection(negPorPTPC, negTrackPhiPrime))) { continue; + } } // Passes daughters track-selection? - if (!(selectV0Daughter(posTrack) && selectV0Daughter(negTrack))) + if (!(selectV0Daughter(posTrack) && selectV0Daughter(negTrack))) { continue; + } // Eta calibration positive-charge track if (trackSelections.applyEtaCal && etaCal.isMIPCalLoaded) { const double dedxCal{etaCal.pEtaCal->GetBinContent(etaCal.pEtaCal->FindBin(posTrkEta))}; - if (dedxCal > kMindEdxMIP && dedxCal < kMaxdEdxMIP) + if (dedxCal > KmindEdxMIP && dedxCal < KmaxdEdxMIP) { posTrkdEdx *= (50.0 / dedxCal); - else + } else { continue; + } } // Eta calibration negative-charge track if (trackSelections.applyEtaCal && etaCal.isMIPCalLoaded) { const double dedxCal{etaCal.pEtaCal->GetBinContent(etaCal.pEtaCal->FindBin(negTrkEta))}; - if (dedxCal > kMindEdxMIP && dedxCal < kMaxdEdxMIP) + if (dedxCal > KmindEdxMIP && dedxCal < KmaxdEdxMIP) { negTrkdEdx *= (50.0 / dedxCal); - else + } else { continue; + } } const TVector3 ppos(posTrack.px(), posTrack.py(), posTrack.pz()); const TVector3 pneg(negTrack.px(), negTrack.py(), negTrack.pz()); - double alpha, qT; + double alpha{-999.0}; + double qT{-999.0}; getArmeterosVariables(ppos, pneg, alpha, qT); registry.fill(HIST("ArmAll"), alpha, qT); bool passesTopoSel{false}; // Passes V0 topological cuts? - if (passesV0TopologicalSelection(v0)) + if (passesV0TopologicalSelection(v0)) { passesTopoSel = true; + } - if (!passesTopoSel) + if (!passesTopoSel) { continue; + } int posIndexEta{-999}; int negIndexEta{-999}; - for (int i = 0; i < kNEtaHists; ++i) { + for (int i = 0; i < KnEtaHists; ++i) { if (posTrkEta >= kLowEta[i] && posTrkEta < kHighEta[i]) { posIndexEta = i; break; } } - for (int i = 0; i < kNEtaHists; ++i) { + for (int i = 0; i < KnEtaHists; ++i) { if (negTrkEta >= kLowEta[i] && negTrkEta < kHighEta[i]) { negIndexEta = i; break; } } - if (posIndexEta < kZeroInt || posIndexEta > kSevenInt) + if (posIndexEta < KzeroInt || posIndexEta > KsevenInt) { continue; + } - if (negIndexEta < kZeroInt || negIndexEta > kSevenInt) + if (negIndexEta < KzeroInt || negIndexEta > KsevenInt) { continue; + } registry.fill(HIST("ArmAfterTopoSel"), alpha, qT); registry.fill(HIST("dcaDauVsPt"), v0.pt(), v0.dcapostopv()); @@ -1001,26 +1102,50 @@ struct PiKpRAA { registry.fill(HIST("V0sCounter"), V0sCounter::K0s); registry.fill(HIST("ArmK0"), alpha, qT); registry.fill(HIST("MassK0sVsPt"), v0.pt(), v0.mK0Short()); - registry.fill(HIST("nSigPiFromK0s"), posTrkPt, posTrack.tpcNSigmaPi()); - registry.fill(HIST("nSigPiFromK0s"), negTrkPt, negTrack.tpcNSigmaPi()); - registry.fill(HIST("NclVsEtaPiV0"), posTrkEta, posNcl); - registry.fill(HIST("NclVsEtaPiV0p"), posTrkEta, posNcl); - registry.fill(HIST("NclVsEtaPiV0"), negTrkEta, negNcl); - registry.fill(HIST("NclVsEtaPiV0p"), negTrkEta, negNcl); - nClVsPPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); - nClVsPpPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); - nClVsPPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); - nClVsPpPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); - dEdxPiV0[posIndexEta]->Fill(posTrkP, posTrkdEdx); - dEdxPiV0[negIndexEta]->Fill(negTrkP, negTrkdEdx); - if (posTrkP > kMinPMIP && posTrkP < kMaxPMIP && posTrkdEdx > kMindEdxMIP && posTrkdEdx < kMaxdEdxMIP) { - registry.fill(HIST("dEdxVsEtaPiMIPV0"), posTrkEta, posTrkdEdx); - registry.fill(HIST("dEdxVsEtaPiMIPV0p"), posTrkEta, posTrkdEdx); - } - if (negTrkP > kMinPMIP && negTrkP < kMaxPMIP && negTrkdEdx > kMindEdxMIP && negTrkdEdx < kMaxdEdxMIP) { - registry.fill(HIST("dEdxVsEtaPiMIPV0"), negTrkEta, negTrkdEdx); - registry.fill(HIST("dEdxVsEtaPiMIPV0p"), negTrkEta, negTrkdEdx); + if (trackSelections.signCharge.value == "Positive" && posTrackCharge > KzeroInt) { + registry.fill(HIST("nSigPiFromK0s"), posTrkPt, posTrack.tpcNSigmaPi()); + registry.fill(HIST("NclVsEtaPiV0"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaPiV0p"), posTrkEta, posNcl); + nClVsPPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); + nClVsPpPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); + dEdxPiV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + if (posPorPTPC > KminPMIP && posPorPTPC < KmaxPMIP && posTrkdEdx > KmindEdxMIP && posTrkdEdx < KmaxdEdxMIP) { + registry.fill(HIST("dEdxVsEtaPiMIPV0"), posTrkEta, posTrkdEdx); + registry.fill(HIST("dEdxVsEtaPiMIPV0p"), posTrkEta, posTrkdEdx); + } + } else if (trackSelections.signCharge.value == "Negative" && negTrackCharge < KzeroInt) { + registry.fill(HIST("nSigPiFromK0s"), negTrkPt, negTrack.tpcNSigmaPi()); + registry.fill(HIST("NclVsEtaPiV0"), negTrkEta, negNcl); + registry.fill(HIST("NclVsEtaPiV0p"), negTrkEta, negNcl); + nClVsPPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); + nClVsPpPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); + dEdxPiV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + if (negPorPTPC > KminPMIP && negPorPTPC < KmaxPMIP && negTrkdEdx > KmindEdxMIP && negTrkdEdx < KmaxdEdxMIP) { + registry.fill(HIST("dEdxVsEtaPiMIPV0"), negTrkEta, negTrkdEdx); + registry.fill(HIST("dEdxVsEtaPiMIPV0p"), negTrkEta, negTrkdEdx); + } + } else { + registry.fill(HIST("nSigPiFromK0s"), posTrkPt, posTrack.tpcNSigmaPi()); + registry.fill(HIST("nSigPiFromK0s"), negTrkPt, negTrack.tpcNSigmaPi()); + registry.fill(HIST("NclVsEtaPiV0"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaPiV0p"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaPiV0"), negTrkEta, negNcl); + registry.fill(HIST("NclVsEtaPiV0p"), negTrkEta, negNcl); + nClVsPPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); + nClVsPpPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); + nClVsPPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); + nClVsPpPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); + dEdxPiV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + dEdxPiV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + if (posPorPTPC > KminPMIP && posPorPTPC < KmaxPMIP && posTrkdEdx > KmindEdxMIP && posTrkdEdx < KmaxdEdxMIP) { + registry.fill(HIST("dEdxVsEtaPiMIPV0"), posTrkEta, posTrkdEdx); + registry.fill(HIST("dEdxVsEtaPiMIPV0p"), posTrkEta, posTrkdEdx); + } + if (negPorPTPC > KminPMIP && negPorPTPC < KmaxPMIP && negTrkdEdx > KmindEdxMIP && negTrkdEdx < KmaxdEdxMIP) { + registry.fill(HIST("dEdxVsEtaPiMIPV0"), negTrkEta, negTrkdEdx); + registry.fill(HIST("dEdxVsEtaPiMIPV0p"), negTrkEta, negTrkdEdx); + } } } } @@ -1029,21 +1154,38 @@ struct PiKpRAA { registry.fill(HIST("V0sCounter"), V0sCounter::Lambda); registry.fill(HIST("ArmL"), alpha, qT); registry.fill(HIST("MassLVsPt"), v0.pt(), v0.mLambda()); - if (posTrackCharge > kZero) { + // Distributions of positive-charge particles + if (trackSelections.signCharge.value == "Positive" && posTrackCharge > KzeroInt) { registry.fill(HIST("nSigPrFromL"), posTrkPt, posTrack.tpcNSigmaPr()); registry.fill(HIST("NclVsEtaPrV0"), posTrkEta, posNcl); registry.fill(HIST("NclVsEtaPrV0p"), posTrkEta, posNcl); nClVsPPrV0[posIndexEta]->Fill(posPorPTPC, posNcl); nClVsPpPrV0[posIndexEta]->Fill(posPorPTPC, posNcl); - dEdxPrV0[posIndexEta]->Fill(posTrkP, posTrkdEdx); - } - if (negTrackCharge < kZero) { + dEdxPrV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + // Distributions of negative-charge particles + } else if (trackSelections.signCharge.value == "Negative" && negTrackCharge < KzeroInt) { registry.fill(HIST("nSigPiFromL"), negTrkPt, negTrack.tpcNSigmaPi()); - // registry.fill(HIST("NclVsEtaPiV0"), negTrkEta, negNcl); - // registry.fill(HIST("NclVsEtaPiV0p"), negTrkEta, negNcl); - // nClVsPPiV0[negIndexEta]->Fill(negPorPt, negNcl); - // nClVsPpPiV0[negIndexEta]->Fill(negPorPt, negNcl); - // dEdxPiV0[negIndexEta]->Fill(negTrkP, negTrkdEdx, centrality); + registry.fill(HIST("NclVsEtaPiV0"), negTrkEta, negNcl); + registry.fill(HIST("NclVsEtaPiV0p"), negTrkEta, negNcl); + nClVsPpPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); + dEdxPiV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + // Distributions of positive- and negative-charge particles + } else { + if (posTrackCharge > KzeroInt) { + registry.fill(HIST("nSigPrFromL"), posTrkPt, posTrack.tpcNSigmaPr()); + registry.fill(HIST("NclVsEtaPrV0"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaPrV0p"), posTrkEta, posNcl); + nClVsPPrV0[posIndexEta]->Fill(posPorPTPC, posNcl); + nClVsPpPrV0[posIndexEta]->Fill(posPorPTPC, posNcl); + dEdxPrV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + } + if (negTrackCharge < KzeroInt) { + registry.fill(HIST("nSigPiFromL"), negTrkPt, negTrack.tpcNSigmaPi()); + registry.fill(HIST("NclVsEtaPiV0"), negTrkEta, negNcl); + registry.fill(HIST("NclVsEtaPiV0p"), negTrkEta, negNcl); + nClVsPpPiV0[negIndexEta]->Fill(negPorPTPC, negNcl); + dEdxPiV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + } } } @@ -1051,21 +1193,38 @@ struct PiKpRAA { registry.fill(HIST("V0sCounter"), V0sCounter::AntiLambda); registry.fill(HIST("ArmAL"), alpha, qT); registry.fill(HIST("MassALVsPt"), v0.pt(), v0.mAntiLambda()); - if (posTrackCharge > kZero) { + // Distributions of positive-charge particles + if (trackSelections.signCharge.value == "Positive" && posTrackCharge > KzeroInt) { registry.fill(HIST("nSigPiFromAL"), posTrkPt, posTrack.tpcNSigmaPi()); - // registry.fill(HIST("NclVsEtaPiV0"), posTrkEta, posNcl); - // registry.fill(HIST("NclVsEtaPiV0p"), posTrkEta, posNcl); - // nClVsPPiV0[posIndexEta]->Fill(posPorPt, posNcl); - // nClVsPpPiV0[posIndexEta]->Fill(posPorPt, posNcl); - // dEdxPiV0[posIndexEta]->Fill(posTrkP, posTrkdEdx, centrality); - } - if (negTrackCharge < kZero) { + registry.fill(HIST("NclVsEtaPiV0"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaPiV0p"), posTrkEta, posNcl); + nClVsPpPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); + dEdxPiV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + // Distributions of positive-charge particles + } else if (trackSelections.signCharge.value == "Negative" && negTrackCharge < KzeroInt) { registry.fill(HIST("nSigPrFromAL"), negTrkPt, negTrack.tpcNSigmaPr()); registry.fill(HIST("NclVsEtaPrV0"), negTrkEta, negNcl); registry.fill(HIST("NclVsEtaPrV0p"), negTrkEta, negNcl); nClVsPPrV0[negIndexEta]->Fill(negPorPTPC, negNcl); nClVsPpPrV0[negIndexEta]->Fill(negPorPTPC, negNcl); - dEdxPrV0[negIndexEta]->Fill(negTrkP, negTrkdEdx); + dEdxPrV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + // Distributions of positive- and negative-charge particles + } else { + if (posTrackCharge > KzeroInt) { + registry.fill(HIST("nSigPiFromAL"), posTrkPt, posTrack.tpcNSigmaPi()); + registry.fill(HIST("NclVsEtaPiV0"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaPiV0p"), posTrkEta, posNcl); + nClVsPpPiV0[posIndexEta]->Fill(posPorPTPC, posNcl); + dEdxPiV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + } + if (negTrackCharge < KzeroInt) { + registry.fill(HIST("nSigPrFromAL"), negTrkPt, negTrack.tpcNSigmaPr()); + registry.fill(HIST("NclVsEtaPrV0"), negTrkEta, negNcl); + registry.fill(HIST("NclVsEtaPrV0p"), negTrkEta, negNcl); + nClVsPPrV0[negIndexEta]->Fill(negPorPTPC, negNcl); + nClVsPpPrV0[negIndexEta]->Fill(negPorPTPC, negNcl); + dEdxPrV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + } } } @@ -1075,33 +1234,59 @@ struct PiKpRAA { if (trackSelections.applyPlateauSel && etaCal.isCalPlateauLoaded) { const double posDedxCal{etaCal.pEtaCalPlateau->GetBinContent(etaCal.pEtaCalPlateau->FindBin(posTrkEta))}; const double negDedxCal{etaCal.pEtaCalPlateau->GetBinContent(etaCal.pEtaCalPlateau->FindBin(negTrkEta))}; - if (!(std::abs(posTrkdEdx - posDedxCal) < v0Selections.dEdxPlateauSel && std::abs(negTrkdEdx - negDedxCal) < v0Selections.dEdxPlateauSel)) + if (!(std::abs(posTrkdEdx - posDedxCal) < v0Selections.dEdxPlateauSel && std::abs(negTrkdEdx - negDedxCal) < v0Selections.dEdxPlateauSel)) { continue; + } } registry.fill(HIST("V0sCounter"), V0sCounter::Gamma); registry.fill(HIST("ArmG"), alpha, qT); registry.fill(HIST("MassGVsPt"), v0.pt(), v0.mGamma()); - registry.fill(HIST("nSigElFromG"), negTrkPt, negTrack.tpcNSigmaEl()); - registry.fill(HIST("nSigElFromG"), posTrkPt, posTrack.tpcNSigmaEl()); - registry.fill(HIST("NclVsEtaElV0"), posTrkEta, posNcl); - registry.fill(HIST("NclVsEtaElV0p"), posTrkEta, posNcl); - registry.fill(HIST("NclVsEtaElV0"), negTrkEta, negNcl); - registry.fill(HIST("NclVsEtaElV0p"), negTrkEta, negNcl); - nClVsPElV0[negIndexEta]->Fill(negPorPTPC, negNcl); - nClVsPpElV0[negIndexEta]->Fill(negPorPTPC, negNcl); - nClVsPElV0[posIndexEta]->Fill(posPorPTPC, posNcl); - nClVsPpElV0[posIndexEta]->Fill(posPorPTPC, posNcl); - if (posPorPTPC > kMinPMIP && posPorPTPC < kMaxPMIP && posTrkdEdx > kMindEdxMIPPlateau && posTrkdEdx < kMaxdEdxMIPPlateau) { - registry.fill(HIST("dEdxVsEtaElMIPV0"), posTrkEta, posTrkdEdx); - registry.fill(HIST("dEdxVsEtaElMIPV0p"), posTrkEta, posTrkdEdx); - } - if (negPorPTPC > kMinPMIP && negPorPTPC < kMaxPMIP && negTrkdEdx > kMindEdxMIPPlateau && negTrkdEdx < kMaxdEdxMIPPlateau) { - registry.fill(HIST("dEdxVsEtaElMIPV0"), negTrkEta, negTrkdEdx); - registry.fill(HIST("dEdxVsEtaElMIPV0p"), negTrkEta, negTrkdEdx); - } - dEdxElV0[posIndexEta]->Fill(posTrkP, posTrkdEdx); - dEdxElV0[negIndexEta]->Fill(negTrkP, negTrkdEdx); + + if (trackSelections.signCharge.value == "Positive" && posTrackCharge > KzeroInt) { + registry.fill(HIST("nSigElFromG"), posTrkPt, posTrack.tpcNSigmaEl()); + registry.fill(HIST("NclVsEtaElV0"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaElV0p"), posTrkEta, posNcl); + nClVsPElV0[posIndexEta]->Fill(posPorPTPC, posNcl); + nClVsPpElV0[posIndexEta]->Fill(posPorPTPC, posNcl); + if (posPorPTPC > KminPMIP && posPorPTPC < KmaxPMIP && posTrkdEdx > KmindEdxMIPPlateau && posTrkdEdx < KmaxdEdxMIPPlateau) { + registry.fill(HIST("dEdxVsEtaElMIPV0"), posTrkEta, posTrkdEdx); + registry.fill(HIST("dEdxVsEtaElMIPV0p"), posTrkEta, posTrkdEdx); + } + dEdxElV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + } else if (trackSelections.signCharge.value == "Negative" && negTrackCharge < KzeroInt) { + registry.fill(HIST("nSigElFromG"), negTrkPt, negTrack.tpcNSigmaEl()); + registry.fill(HIST("NclVsEtaElV0"), negTrkEta, negNcl); + registry.fill(HIST("NclVsEtaElV0p"), negTrkEta, negNcl); + nClVsPElV0[negIndexEta]->Fill(negPorPTPC, negNcl); + nClVsPpElV0[negIndexEta]->Fill(negPorPTPC, negNcl); + if (negPorPTPC > KminPMIP && negPorPTPC < KmaxPMIP && negTrkdEdx > KmindEdxMIPPlateau && negTrkdEdx < KmaxdEdxMIPPlateau) { + registry.fill(HIST("dEdxVsEtaElMIPV0"), negTrkEta, negTrkdEdx); + registry.fill(HIST("dEdxVsEtaElMIPV0p"), negTrkEta, negTrkdEdx); + } + dEdxElV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + } else { + registry.fill(HIST("nSigElFromG"), negTrkPt, negTrack.tpcNSigmaEl()); + registry.fill(HIST("nSigElFromG"), posTrkPt, posTrack.tpcNSigmaEl()); + registry.fill(HIST("NclVsEtaElV0"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaElV0p"), posTrkEta, posNcl); + registry.fill(HIST("NclVsEtaElV0"), negTrkEta, negNcl); + registry.fill(HIST("NclVsEtaElV0p"), negTrkEta, negNcl); + nClVsPElV0[negIndexEta]->Fill(negPorPTPC, negNcl); + nClVsPpElV0[negIndexEta]->Fill(negPorPTPC, negNcl); + nClVsPElV0[posIndexEta]->Fill(posPorPTPC, posNcl); + nClVsPpElV0[posIndexEta]->Fill(posPorPTPC, posNcl); + if (posPorPTPC > KminPMIP && posPorPTPC < KmaxPMIP && posTrkdEdx > KmindEdxMIPPlateau && posTrkdEdx < KmaxdEdxMIPPlateau) { + registry.fill(HIST("dEdxVsEtaElMIPV0"), posTrkEta, posTrkdEdx); + registry.fill(HIST("dEdxVsEtaElMIPV0p"), posTrkEta, posTrkdEdx); + } + if (negPorPTPC > KminPMIP && negPorPTPC < KmaxPMIP && negTrkdEdx > KmindEdxMIPPlateau && negTrkdEdx < KmaxdEdxMIPPlateau) { + registry.fill(HIST("dEdxVsEtaElMIPV0"), negTrkEta, negTrkdEdx); + registry.fill(HIST("dEdxVsEtaElMIPV0p"), negTrkEta, negTrkdEdx); + } + dEdxElV0[posIndexEta]->Fill(posPorPTPC, posTrkdEdx); + dEdxElV0[negIndexEta]->Fill(negPorPTPC, negTrkdEdx); + } } } } // v0s @@ -1111,16 +1296,22 @@ struct PiKpRAA { Preslice perCollision = aod::track::collisionId; Service pdg; - void processSim(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) + void processSignalLoss(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, BCsRun3 const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::McParticles const& mcParticles, TracksMC const& tracksMC) { + + const auto& kLowEta = (std::vector)vecLowEta; + const auto& kHighEta = (std::vector)vecUpEta; + registry.fill(HIST("EventCounterMC"), 0.5); + //--------------------------- - // Only INEL > 0 generated collisions - // By counting number of primary charged particles in |eta| < 1 + // Nch COUNTER IN THE TPC ACCEPTANCE + // ONLY ETA SELECTION IS IMPOSED //--------------------------- - int nChMC{0}; - int nChMCEta08{0}; - int nChFT0A{0}; - int nChFT0C{0}; + int nChMCTPCAcc{0}; + int nChMCFV0A{0}; + int nChMCFT0C{0}; + int nChMCFT0M{0}; + int nChMCForward{0}; for (const auto& particle : mcParticles) { auto charge{0.}; @@ -1133,75 +1324,85 @@ struct PiKpRAA { } // Is it a charged particle? - if (std::abs(charge) < kMinCharge) + if (std::abs(charge) < KminCharge) { continue; + } - // Is it a primary particle? - if (!particle.isPhysicalPrimary()) + // Select particle based on its charge + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { continue; + } - const float eta{particle.eta()}; + // Is it a primary particle? + if (!particle.isPhysicalPrimary()) { + continue; + } - // TVX requirement - if (eta > kMinFT0A && eta < kMaxFT0A) { - nChFT0A++; + // Selects particles based on their vertex creation + // This is added to reject loopers + const float vX{particle.vx()}; + const float vY{particle.vy()}; + if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { + continue; } - if (eta > kMinFT0C && eta < kMaxFT0C) { - nChFT0C++; + const float eta{particle.eta()}; + registry.fill(HIST("EtaMCParAllMCColl"), eta); + + if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { + registry.fill(HIST("EtaMCParAllMCColl_Pion"), eta); + } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { + registry.fill(HIST("EtaMCParAllMCColl_Kaon"), eta); + } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { + registry.fill(HIST("EtaMCParAllMCColl_Proton"), eta); + } else if (particle.pdgCode() == PDG_t::kElectron || particle.pdgCode() == PDG_t::kPositron) { + registry.fill(HIST("EtaMCParAllMCColl_Electron"), eta); + } else { + registry.fill(HIST("EtaMCParAllMCColl_Rest"), eta); } if (std::abs(eta) < tpcNchAcceptance) { - nChMCEta08++; + nChMCTPCAcc++; + } + if (eta > KminFV0A && eta < KmaxFV0A) { + nChMCFV0A++; + } + if (eta > KminFT0C && eta < KmaxFT0C) { + nChMCFT0C++; + } + if ((eta > KminFT0A && eta < KmaxFT0A) || (eta > KminFT0C && eta < KmaxFT0C)) { + nChMCFT0M++; } - - // INEL > 0 - if (std::abs(eta) > kOne) - continue; - - nChMC++; } - //--------------------------- - // Only events with at least one charged particle in the FT0A and FT0C acceptances - //--------------------------- - if (selTVXMC) { - if (!(nChFT0A > kZeroInt && nChFT0C > kZeroInt)) { - registry.fill(HIST("NchMCcentVsTVX"), nChMC, 0.5); - return; - } - registry.fill(HIST("NchMCcentVsTVX"), nChMC, 1.5); + // DEFINE THE MC NCH MULTIPLICITY BASED ON THE CENTRALITY ESTIMATOR + if (centralitySelector.value == "FT0C") { + nChMCForward = nChMCFT0C; + } else if (centralitySelector.value == "FT0M") { + nChMCForward = nChMCFT0M; + } else if (centralitySelector.value == "FV0A") { + nChMCForward = nChMCFV0A; + } else { + nChMCForward = -999; } //--------------------------- - // Only MC events with |Vtx Z| < 10 cm + // SELECTS MC EVENTS WITH |ZVTX| < ZVTX_CUT //--------------------------- if (isZvtxPosSelMC && (std::fabs(mccollision.posZ()) > posZcut)) { return; } //--------------------------- - // Only INEL > 0 generated events - //--------------------------- - if (selINELgt0) { - if (!(nChMC > kZeroInt)) { - return; - } - } - - //--------------------------- - // All Generated events irrespective of whether there is an associated reconstructed collision - // Consequently, the centrality being a reconstructed quantity, might not always be available - // Therefore it is expressed as a function of the generated pT and the generated Nch in ∣eta∣ < 0.8 - // This is used for the denominator of the signal loss correction - // Also for MC closure: True Pt vs Generated Nch + // DENOMINATOR OF THE SIGNAL LOSS: ALL MC EVENTS //--------------------------- for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) - continue; - - if (particle.pt() < trackSelections.minPt || particle.pt() > trackSelections.maxPt) + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { continue; + } auto charge{0.}; // Get the MC particle @@ -1213,155 +1414,421 @@ struct PiKpRAA { } // Is it a charged particle? - if (std::abs(charge) < kMinCharge) + if (std::abs(charge) < KminCharge) { continue; + } - // Is it a primary particle? - bool isPrimary{true}; - if (!particle.isPhysicalPrimary()) - isPrimary = false; - - if (isPrimary) { - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCEta08); - registry.fill(HIST("MCclosure_PtMCPiVsNchMC"), particle.pt(), nChMCEta08); - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCEta08); - registry.fill(HIST("MCclosure_PtMCKaVsNchMC"), particle.pt(), nChMCEta08); - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCEta08); - registry.fill(HIST("MCclosure_PtMCPrVsNchMC"), particle.pt(), nChMCEta08); - } else { - continue; - } + // Selects particle based on its charge + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } + + // Selects only primary particles + if (!particle.isPhysicalPrimary()) { + continue; + } + + // Selects particles based on their vertex creation + // This is added to reject loopers + const float vX{particle.vx()}; + const float vY{particle.vy()}; + if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { + continue; + } + + if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { + registry.fill(HIST("PtPiVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS + } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { + registry.fill(HIST("PtKaVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS + } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { + registry.fill(HIST("PtPrVsNchMC_AllGen"), particle.pt(), nChMCTPCAcc); // DENOMINATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMCFwd_AllGen"), particle.pt(), nChMCForward); // DENOMINATOR OF SIGNAL LOSS + } else { + continue; } } // Loop over Generated Particles //--------------------------- - // This is used for the denominator of the event loss correction + // nChMCTPCAc = CHARGEd PARTICLES WITHIN THE TPC ACCEPTANCE AND WITHOUT PT SELECTION //--------------------------- - registry.fill(HIST("NchMC_AllGen"), nChMCEta08); + + // DENOMINATOR OF THE EVENT LOSS: ALL THE GENERATED EVENTS + registry.fill(HIST("NchMC_Fwd_AllGen"), nChMCForward); + registry.fill(HIST("NchMC_AllGen"), nChMCTPCAcc); + registry.fill(HIST("zPosMCAll"), mccollision.posZ()); //--------------------------- - // Only Generated evets with at least one reconstrued collision + // HOW MANY TIMES WAS THE MC EVENT RECONSTRUED? //--------------------------- const auto& nRecColls{collisions.size()}; - registry.fill(HIST("NumberOfRecoCollisions"), nRecColls); + registry.fill(HIST("NumOfRecColl"), nRecColls); - if (nRecColls > kZeroInt) { + //--------------------------- + // MC EVENTS WITH AT LEAST ONE ASSOCIATED RECONSTRUCTED COLLISION + //--------------------------- + if (nRecColls > KzeroInt) { - // Finds the collisions with the largest number of contributors - // in case nRecColls is larger than One + //--------------------------- + // THIS COLLISIONS-LOOP GETS THE INDEX (bestCollisionIndex) OF THE RECONSTRUCTED COLLISION WITH THE LARGEST NUMBER OF PV CONTRIBUTORS + // NO EVENT SELECTIONS APPLIED + //--------------------------- int biggestNContribs{-1}; int bestCollisionIndex{-1}; for (const auto& collision : collisions) { - - const float centrality{isT0Ccent ? collision.centFT0C() : collision.centFT0M()}; - if (selHasFT0 && !collision.has_foundFT0()) { - continue; + float centrality{-999.0}; + if (centralitySelector.value == "FT0C") { + centrality = collision.centFT0C(); + } else if (centralitySelector.value == "FT0M") { + centrality = collision.centFT0M(); + } else if (centralitySelector.value == "FV0A") { + centrality = collision.centFV0A(); + } else { + centrality = -999.0; } + registry.fill(HIST("NumOfRecCollVsNContri"), nRecColls, collision.numContrib()); + // CENTRALITY FROM THE MULTIPLE TIMES THE MC EVENT WAS RECONSTRUCTED + registry.fill(HIST("NumOfRecCollVsCentrality"), centrality, nRecColls); + + //--------------------------- + // GETS THE BESTCOLLISION INDEX + //--------------------------- if (biggestNContribs < collision.numContrib()) { biggestNContribs = collision.numContrib(); bestCollisionIndex = collision.globalIndex(); } - if (selHasBC && !collision.has_foundBC()) - continue; + const bool hasft0{collision.has_foundFT0()}; + const bool hastvx{collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)}; - if (selHasFT0 && !collision.has_foundFT0()) - continue; + //--------------------------- + // ALL COLLISIONS WITH N CONTRIBUTORS > 0 + //--------------------------- + registry.fill(HIST("EventCounterMC"), 1.5); - if (useSel8 && !collision.sel8()) - continue; + //--------------------------- + // COLLISIONS WITH FT0 INFO + //--------------------------- + if (hasft0) { + registry.fill(HIST("EventCounterMC"), 2.5); + } - // kIsTriggerTVX - if (selTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) - continue; + //--------------------------- + // TVX-triggered collision + //--------------------------- + if (hasft0 && hastvx) { + registry.fill(HIST("EventCounterMC"), 3.5); + } + } - // kNoITSROFrameBorder - if (selNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) - continue; + //--------------------------- + // EVENT SELECTIONS APPLIED: ONLY FT0-INFO AND TVX-TRIGGERED REQUIRED + //--------------------------- + for (const auto& collision : collisions) { - // kNoTimeFrameBorder - if (selNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) - continue; + bool isBestCollIndex{false}; + if (bestCollisionIndex == collision.globalIndex()) { + isBestCollIndex = true; + } - // Zvtx - if (isZvtxPosSel && std::fabs(collision.posZ()) > posZcut) - continue; + bool hasbc{false}; + bool hasft0{false}; + bool hastvx{false}; + bool hasBcFT0TVX{false}; + + hasbc = collision.has_foundBC(); + hasft0 = collision.has_foundFT0(); + hastvx = collision.selection_bit(o2::aod::evsel::kIsTriggerTVX); + hasBcFT0TVX = (hasbc && hasft0 && hastvx); + + if (hasBcFT0TVX) { + // NUMERATOR/DENOMINATOR OF THE EVENT LOSS + registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX"), nChMCTPCAcc); + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX"), nChMCForward); + + // TVX-triggered + bestCollisionIndex + if (isBestCollIndex) { + registry.fill(HIST("EventCounterMC"), 4.5); + // NUMERATOR/DENOMINATOR OF THE EVENT LOSS + registry.fill(HIST("NchMC_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCTPCAcc); + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_HasFT0AndTVX_BestCollIdx"), nChMCForward); + } + } - if (selIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - continue; + //--------------------------- + // LOOPING OVER THE MC EVENT + //--------------------------- + for (const auto& particle : mcParticles) { + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { + continue; + } - if (selNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - continue; + auto charge{0.}; + // GET THE MC PARTICLE + auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle != nullptr) { + charge = pdgParticle->Charge(); + } else { + continue; + } - // Needed to calculate denominator of the Event Splitting correction - registry.fill(HIST("Centrality_AllRecoEvt"), centrality); + // IS IT A CHARGED PARTICLE? + if (std::abs(charge) < KminCharge) { + continue; + } + + // SELECTS PARTICLE BASED ON ITS CHARGE + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } + + // SELECTS ONLY PRIMARY PARTICLES + if (!particle.isPhysicalPrimary()) { + continue; + } + + // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION + // THIS IS ADDED TO REJECT LOOPERS + const float vX{particle.vx()}; + const float vY{particle.vy()}; + if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { + continue; + } + + // TO COMPUTE SIGNAL LOSS RELATIVE TO THE MC SIGNAL OF EVENTS WITH FT0 INFO + TVX-TRIGGERED + if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); + if (isBestCollIndex) { + registry.fill(HIST("PtPiVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPiVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); + } + } + } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); + if (isBestCollIndex) { + registry.fill(HIST("PtKaVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); + } + } + } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { + if (hasBcFT0TVX) { + registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX"), particle.pt(), nChMCForward); + if (isBestCollIndex) { + registry.fill(HIST("PtPrVsNchMC_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_HasFT0AndTVX_BestCollIdx"), particle.pt(), nChMCForward); + } + } + } else { + continue; + } + } // LOOP OVER TRUE MC EVENTS } //--------------------------- - // Loop over the reconstructed collisions - // Only that one with the largest number of contributors is considered + // MC EVENTS RECONSTRUCTED MULTIPLE TIMES + // EVENT SELECTION APPLIED MANUALLY TO AVOID DOUBLE FILLING IN THE EVENT COUNTER + // *) DENOMINATOR OF THE EVENT SPLITTING + // *) NUMERATOR OF THE EVENT LOSS + // *) NUMERATOR OF SIGNAL LOSS //--------------------------- for (const auto& collision : collisions) { - const float centrality{isT0Ccent ? collision.centFT0C() : collision.centFT0M()}; + float centrality{-999.0}; + if (centralitySelector.value == "FT0C") { + centrality = collision.centFT0C(); + } else if (centralitySelector.value == "FT0M") { + centrality = collision.centFT0M(); + } else if (centralitySelector.value == "FV0A") { + centrality = collision.centFV0A(); + } else { + centrality = -999.0; + } - //--------------------------- - // Pick the collisions with the largest number of contributors - //--------------------------- - if (bestCollisionIndex != collision.globalIndex()) - continue; + // HAS BC? + if (selHasBC) { + if (!collision.has_foundBC()) { + continue; + } + } - // Needed to load the Phi selection from the CCDB - const auto& foundBC = collision.foundBC_as(); - uint64_t timeStamp{foundBC.timestamp()}; - const int magField{getMagneticField(timeStamp)}; + // HAS FT0 INFORMATION? + if (selHasFT0) { + if (!collision.has_foundFT0()) { + continue; + } + } - if (trackSelections.applyPhiCut) { - const int nextRunNumber{foundBC.runNumber()}; - if (currentRunNumberPhiSel != nextRunNumber) { - loadPhiCutSelections(timeStamp); - currentRunNumberPhiSel = nextRunNumber; - LOG(info) << "\tcurrentRunNumberPhiSel= " << currentRunNumberPhiSel << " timeStamp = " << timeStamp; + // USE SEL8? + if (useSel8) { + if (!collision.sel8()) { + continue; } + } - // return if phi cut objects are nullptr - if (!(phiCut.hPhiCutHigh && phiCut.hPhiCutLow)) - return; + // KISTRIGGERTVX + if (selTriggerTVX) { + if (!collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { + continue; + } } - //--------------------------- - // Needed to construct the correlation between MC Nch v.s. centrality - //--------------------------- - registry.fill(HIST("Centrality_WRecoEvt"), centrality); - registry.fill(HIST("zPosMC"), mccollision.posZ()); + // kNoITSROFrameBorder + if (selNoITSROFrameBorder) { + if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + continue; + } + } - registry.fill(HIST("T0CcentVsBCVsFT0VsTVXVsEvSel"), centrality, 1.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 1.0); + // kNoTimeFrameBorder + if (selNoTimeFrameBorder) { + if (!collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + continue; + } + } - if (collision.has_foundBC()) { - registry.fill(HIST("T0CcentVsBCVsFT0VsTVXVsEvSel"), centrality, 2.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 2.0); + // Zvtx + if (isZvtxPosSel) { + if (std::fabs(collision.posZ()) > posZcut) { + continue; + } } - if (collision.has_foundBC() && collision.has_foundFT0()) { - registry.fill(HIST("T0CcentVsBCVsFT0VsTVXVsEvSel"), centrality, 3.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 3.0); + // Short distance between FT0 & PV vertices + if (selIsGoodZvtxFT0vsPV) { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + continue; + } } - if (collision.has_foundBC() && collision.has_foundFT0() && collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("T0CcentVsBCVsFT0VsTVXVsEvSel"), centrality, 4.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 4.0); + if (selNoSameBunchPileup) { + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + continue; + } } + if (isCentSel) { + if (centrality < minCentCut || centrality > maxCentCut) { + continue; + } + } + + if (selINELgt0) { + if (!collision.isInelGt0()) { + continue; + } + } + + registry.fill(HIST("NchMCVsCent_WithRecoEvt"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY + registry.fill(HIST("Centrality_AllRecoEvt"), centrality); // DENOMINATOR OF EVENT SPLITTING + registry.fill(HIST("NchMC_WithRecoEvt"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION + registry.fill(HIST("Centrality_FineBins_WithRecoEvt"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + BESTCOLLISIONINDEX + registry.fill(HIST("NchMC_Fwd_WithRecoEvt"), nChMCForward); // Nch MC Forward + + for (const auto& particle : mcParticles) { + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { + continue; + } + + auto charge{0.}; + // Get the MC particle + auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); + if (pdgParticle != nullptr) { + charge = pdgParticle->Charge(); + } else { + continue; + } + + // Is it a charged particle? + if (std::abs(charge) < KminCharge) { + continue; + } + + // Select particle based on its charge + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } + + // IS IT A PRIMARY PARTICLE? + if (!particle.isPhysicalPrimary()) { + continue; + } + + // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION + // THIS IS ADDED TO REJECT LOOPERS + const float vX{particle.vx()}; + const float vY{particle.vy()}; + if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { + continue; + } + + if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS + } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtKaVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS + } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPrVsNchMCFwd_WithRecoEvt"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS + } else { + continue; + } + } // Loop over generated particles per generated collision + } + + //--------------------------- + // ANALYZE ONLY THE COLLISION WITH THE LARGEST NUMBER OF CONTRIBUTORS + // EVENT SELECTIONS APPLIED + // *) NUMERATOR OF THE EVENT SPLITTING + // *) NUMERATOR OF THE EVENT LOSS + // *) NUMERATOR OF SIGNAL LOSS + // *) DENOMINATOR OF TRACKING EFFICIENCY + // *) NUMERATOR OF TRACKING EFFICIENCY + //--------------------------- + for (const auto& collision : collisions) { + //--------------------------- - // RCT Selection + // SELECTS THE COLLISIONS WITH THE LARGEST NUMBER OF CONTRIBUTORS + //--------------------------- + if (bestCollisionIndex != collision.globalIndex()) { + continue; + } + + float centrality{-999.0}; + if (centralitySelector.value == "FT0C") { + centrality = collision.centFT0C(); + } else if (centralitySelector.value == "FT0M") { + centrality = collision.centFT0M(); + } else if (centralitySelector.value == "FV0A") { + centrality = collision.centFV0A(); + } else { + centrality = -999.0; + } + + const auto& foundBC = collision.foundBC_as(); + registry.fill(HIST("zPosMC"), mccollision.posZ()); + + //--------------------------- + // RCT SELECTION? //--------------------------- if (requireGoodRct) { - // Checks if collisions passes RCT selection + // CHECKS IF COLLISIONS PASSES RCT SELECTION const bool isFT0Bad{requireBCRct ? foundBC.rct_bit(kFT0Bad) : collision.rct_bit(kFT0Bad)}; const bool isITSBad{requireBCRct ? foundBC.rct_bit(kITSBad) : collision.rct_bit(kITSBad)}; const bool isITSLimAcc{requireBCRct ? foundBC.rct_bit(kITSLimAccMCRepr) : collision.rct_bit(kITSLimAccMCRepr)}; @@ -1371,69 +1838,68 @@ struct PiKpRAA { const bool isTPCPIDBad{requireBCRct ? foundBC.rct_bit(kTPCBadPID) : collision.rct_bit(kTPCBadPID)}; const bool isTPCLimAcc{requireBCRct ? foundBC.rct_bit(kTPCLimAccMCRepr) : collision.rct_bit(kTPCLimAccMCRepr)}; - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 1.0); - if (!isFT0Bad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 2.0); - if (!isITSBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 3.0); - if (!isITSLimAcc) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 4.0); - if (!isTOFBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 5.0); - if (!isTOFLimAcc) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 6.0); - if (!isTPCTrackingBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 7.0); - if (!isTPCPIDBad) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 8.0); - if (!isTPCLimAcc) - registry.fill(HIST("T0CcentVsRCTSel"), centrality, 9.0); + registry.fill(HIST("CentralityVsRCTSel"), centrality, 1.0); + if (!isFT0Bad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 2.0); + } + if (!isITSBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 3.0); + } + if (!isITSLimAcc) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 4.0); + } + if (!isTOFBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 5.0); + } + if (!isTOFLimAcc) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 6.0); + } + if (!isTPCTrackingBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 7.0); + } + if (!isTPCPIDBad) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 8.0); + } + if (!isTPCLimAcc) { + registry.fill(HIST("CentralityVsRCTSel"), centrality, 9.0); + } registry.fill(HIST("RCTSel"), 1.0); - if (!rctChecker(collision)) + if (!rctChecker(collision)) { return; + } registry.fill(HIST("RCTSel"), 2.0); } //--------------------------- - // Event Selection + // **** EVENT SELECTION **** //--------------------------- - if (!isEventSelected(collision)) + if (!isEventSelected(collision)) { return; - - registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); - registry.fill(HIST("NchMCVsCent"), centrality, nChMCEta08); - registry.fill(HIST("NchMC_WithRecoEvt"), nChMCEta08); // Numerator of event loss correction - registry.fill(HIST("zPos"), collision.posZ()); - registry.fill(HIST("T0Ccent"), centrality); + } //--------------------------- - // has_foundFT0() ? + // NEEDED TO CONSTRUCT THE CORRELATION BETWEEN MC NCH V.S. CENTRALITY //--------------------------- - - if (collision.has_foundBC() && collision.has_foundFT0() && collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - registry.fill(HIST("T0CcentVsBCVsFT0VsTVXVsEvSel"), centrality, 5.0); - registry.fill(HIST("HasBCVsFT0VsTVXVsEvSel"), 5.0); - } - - if (collision.has_foundFT0()) - registry.fill(HIST("T0CcentVsFoundFT0"), centrality, 1.5); - else - registry.fill(HIST("T0CcentVsFoundFT0"), centrality, 0.5); + registry.fill(HIST("Centrality_WRecoEvtWSelCri"), centrality); // NUMERATOR OF EVENT SPLITTING + registry.fill(HIST("NchMC_WithRecoEvt_BestCollIdx"), nChMCTPCAcc); // NUMERATOR OF EVENT LOSS CORRECTION + registry.fill(HIST("NchMCVsCent_WithRecoEvt_BestCollIdx"), centrality, nChMCTPCAcc); // FOR MAPPING BETWEEN MC NCH AND CENTRALITY + registry.fill(HIST("zPos"), collision.posZ()); // VTX-Z OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + registry.fill(HIST("Centrality_FineBins_WithRecoEvt_BestCollIdx"), centrality); // CENTRALITY OF RECONSTRUED COLLISIONS + EVENT SELECTION + BESTCOLLISIONINDEX + registry.fill(HIST("EventCounterMC"), 5.5); // COUNTER: ASSOC. REC. COLL + EVT SEL + BESTCOLLISIONINDEX + registry.fill(HIST("NchMC_Fwd_WithRecoEvt_BestCollIdx"), nChMCForward); // Nch MC Forward //--------------------------- - // All Generated events with at least one associated reconstructed collision - // The Generated events are not subjected to any selection criteria - // However, the associated reconstructed collisions pass the selection criteria - // This histograms are used for the denominator of the tracking efficiency + // THE GENERATED EVENTS ARE NOT SUBJECTED TO ANY SELECTION CRITERIA + // HOWEVER, THE ASSOCIATED RECONSTRUCTED COLLISIONS HAD TO PASS THE SELECTION CRITERIA + // *) DENOMINATOR OF THE TRACKING EFFICIENCY //--------------------------- for (const auto& particle : mcParticles) { - if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) - continue; - - if (particle.pt() < trackSelections.minPt || particle.pt() > trackSelections.maxPt) + if (particle.eta() < trackSelections.minEta || particle.eta() > trackSelections.maxEta) { continue; + } auto charge{0.}; // Get the MC particle @@ -1445,44 +1911,64 @@ struct PiKpRAA { } // Is it a charged particle? - if (std::abs(charge) < kMinCharge) + if (std::abs(charge) < KminCharge) { continue; + } - // Is it a primary particle? - bool isPrimary{true}; - if (!particle.isPhysicalPrimary()) - isPrimary = false; - - if (isPrimary) { - if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { - registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // Denominator of tracking efficiency - registry.fill(HIST("PtPiVsNchMC_WithRecoEvt"), particle.pt(), nChMCEta08); // Numerator of signal loss - } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { - registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtKaVsNchMC_WithRecoEvt"), particle.pt(), nChMCEta08); - } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { - registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("PtPrVsNchMC_WithRecoEvt"), particle.pt(), nChMCEta08); - } else { - continue; - } + // Select particle based on its charge + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } + + // IS IT A PRIMARY PARTICLE? + if (!particle.isPhysicalPrimary()) { + continue; + } + + // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION + // THIS IS ADDED TO REJECT LOOPERS + const float vX{particle.vx()}; + const float vY{particle.vy()}; + if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { + continue; + } + + if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { + registry.fill(HIST("PtPiVsCentMC_WithRecoEvt"), particle.pt(), centrality); // DENOMINATOR OF TRACKING EFFICIENCY + registry.fill(HIST("PtPiVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); // NUMERATOR OF SIGNAL LOSS + registry.fill(HIST("PtPiVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS + } else if (particle.pdgCode() == PDG_t::kKPlus || particle.pdgCode() == PDG_t::kKMinus) { + registry.fill(HIST("PtKaVsCentMC_WithRecoEvt"), particle.pt(), centrality); + registry.fill(HIST("PtKaVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtKaVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS + } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { + registry.fill(HIST("PtPrVsCentMC_WithRecoEvt"), particle.pt(), centrality); + registry.fill(HIST("PtPrVsNchMC_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCTPCAcc); + registry.fill(HIST("PtPrVsNchMCFwd_WithRecoEvt_BestCollIdx"), particle.pt(), nChMCForward); // NUMERATOR OF SIGNAL LOSS + } else { + continue; } } // Loop over generated particles per generated collision const auto& groupedTracks{tracksMC.sliceBy(perCollision, collision.globalIndex())}; // ================ - // Track selection WITHOUT DCAxy and DCAz selections - // Needed for the Secondary Particle Correction + // TRACK SELECTION *** WITHOUT DCAXY AND DCAZ SELECTIONS *** + // *) SECONDARY PARTICLE CORRECTION // ================ for (const auto& track : groupedTracks) { const bool applyDca{false}; - if (!selectPrimary(track, applyDca)) + if (!selectPrimary(track, applyDca)) { continue; + } - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } // Get the MC particle const auto& particle{track.mcParticle()}; @@ -1495,11 +1981,20 @@ struct PiKpRAA { } // Is it a charged particle? - if (std::abs(charge) < kMinCharge) + if (std::abs(charge) < KminCharge) { continue; + } - float phiPrime{track.phi()}; - phiPrimeFunc(phiPrime, magField, charge); + // Select particle based on its charge + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } + + registry.fill(HIST("DCAxyVsPt"), track.pt(), track.dcaXY()); + registry.fill(HIST("DCAzVsPt"), track.pt(), track.dcaZ()); bool isPrimary{false}; bool isDecay{false}; @@ -1523,41 +2018,57 @@ struct PiKpRAA { } if (isPrimary && !isDecay && !isMaterial) { - if (isPi && !isPr) + + // SELECTS PARTICLES BASED ON THEIR VERTEX CREATION; ADDED TO REJECT LOOPERS + const float vX{particle.vx()}; + const float vY{particle.vy()}; + if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { + continue; + } + + if (isPi && !isPr) { registry.fill(HIST("dcaVsPtPi"), track.pt(), track.dcaXY(), centrality); - if (isPr && !isPi) + } + if (isPr && !isPi) { registry.fill(HIST("dcaVsPtPr"), track.pt(), track.dcaXY(), centrality); + } } if (isDecay && !isPrimary && !isMaterial) { - if (isPi && !isPr) + if (isPi && !isPr) { registry.fill(HIST("dcaVsPtPiDec"), track.pt(), track.dcaXY(), centrality); - if (isPr && !isPi) + } + if (isPr && !isPi) { registry.fill(HIST("dcaVsPtPrDec"), track.pt(), track.dcaXY(), centrality); + } } if (isMaterial && !isPrimary && !isDecay) { - if (isPi && !isPr) + if (isPi && !isPr) { registry.fill(HIST("dcaVsPtPiMat"), track.pt(), track.dcaXY(), centrality); - if (isPr && !isPi) + } + if (isPr && !isPi) { registry.fill(HIST("dcaVsPtPrMat"), track.pt(), track.dcaXY(), centrality); + } } } // ================ - // Track selection WITH DCAxy and DCAz selections - // Needed for the numerator in the tracking efficiency + // TRACK SELECTION WITH *** DCAXY AND DCAZ SELECTIONS *** + // *) NUMERATOR OF THE TRACKING EFFICIENCY // ================ int nCh{0}; for (const auto& track : groupedTracks) { const bool applyDca{true}; - if (!selectPrimary(track, applyDca)) + if (!selectPrimary(track, applyDca)) { continue; + } // Has MC particle? - if (!track.has_mcParticle()) + if (!track.has_mcParticle()) { continue; + } // Get the MC particle const auto& particle{track.mcParticle()}; @@ -1570,39 +2081,60 @@ struct PiKpRAA { } // Is it a charged particle? - if (std::abs(charge) < kMinCharge) + if (std::abs(charge) < KminCharge) { continue; + } - float phiPrime{track.phi()}; - phiPrimeFunc(phiPrime, magField, charge); - - const float pOrpTPC{trackSelections.usePinPhiSelection ? track.p() : track.tpcInnerParam()}; + // Select particle based on its charge + if (trackSelections.signCharge.value == "Positive" && charge < Kzero) { + continue; + } + if (trackSelections.signCharge.value == "Negative" && charge > Kzero) { + continue; + } int indexEta{-999}; const float eta{track.eta()}; - for (int i = 0; i < kNEtaHists; ++i) { + for (int i = 0; i < KnEtaHists; ++i) { if (eta >= kLowEta[i] && eta < kHighEta[i]) { indexEta = i; break; } } - if (indexEta < kZeroInt || indexEta > kSevenInt) + if (indexEta < KzeroInt || indexEta > KsevenInt) { continue; + } nCh++; - bool isPrimary{false}; - if (particle.isPhysicalPrimary()) - isPrimary = true; + registry.fill(HIST("MomentumTPCVsP"), track.p(), track.tpcInnerParam()); + registry.fill(HIST("DCAxyVsPtWithSelection"), track.pt(), track.dcaXY()); + registry.fill(HIST("DCAzVsPtWithSelection"), track.pt(), track.dcaZ()); + registry.fill(HIST("EtaVsPhi"), track.eta(), track.phi()); + registry.fill(HIST("NclVsPhiHist"), track.phi(), track.tpcNClsFound()); + registry.fill(HIST("NclVsPhiProfile"), track.phi(), track.tpcNClsFound()); + registry.fill(HIST("NclVsEta"), track.eta(), track.tpcNClsFound()); + registry.fill(HIST("NclVsEtap"), track.eta(), track.tpcNClsFound()); + registry.fill(HIST("NclVsEtaPID"), track.eta(), track.tpcNClsPID()); + registry.fill(HIST("NclVsEtaPIDp"), track.eta(), track.tpcNClsPID()); + + // Rejects non-physical primary particle + if (!particle.isPhysicalPrimary()) { + continue; + } - if (!isPrimary) + // Selects particles based on their vertex creation + // This is added to reject loopers + const float vX{particle.vx()}; + const float vY{particle.vy()}; + if (std::hypot(vX, vY) > trackSelections.particleVtxSel) { continue; + } bool isPi{false}; bool isKa{false}; bool isPr{false}; - bool isMu{false}; if (particle.pdgCode() == PDG_t::kPiPlus || particle.pdgCode() == PDG_t::kPiMinus) { isPi = true; @@ -1610,30 +2142,21 @@ struct PiKpRAA { isKa = true; } else if (particle.pdgCode() == PDG_t::kProton || particle.pdgCode() == PDG_t::kProtonBar) { isPr = true; - } else if (particle.pdgCode() == PDG_t::kMuonPlus || particle.pdgCode() == PDG_t::kMuonMinus) { - isMu = true; } else { continue; } - if (isMu && !isPi && !isKa && !isPr) - dEdxMuMC[indexEta]->Fill(pOrpTPC, track.tpcSignal()); - if (isPi && !isKa && !isPr) { - registry.fill(HIST("PtPiVsCent_WithRecoEvt"), track.pt(), centrality); // Numerator of tracking efficiency - registry.fill(HIST("PtGenPiVsCent_WithRecoEvt"), particle.pt(), centrality); // Numerator of tracking efficiency - registry.fill(HIST("MCclosure_PtPiVsNchMC"), track.pt(), nChMCEta08); - dEdxPiMC[indexEta]->Fill(pOrpTPC, track.tpcSignal()); + registry.fill(HIST("PtPiVsCent_WithRecoEvt"), track.pt(), centrality); // NUMERATOR OF TRACKING EFFICIENCY + registry.fill(HIST("PtGenPiVsCent_WithRecoEvt"), particle.pt(), centrality); // NUMERATOR OF TRACKING EFFICIENCY } if (isKa && !isPi && !isPr) { registry.fill(HIST("PtKaVsCent_WithRecoEvt"), track.pt(), centrality); registry.fill(HIST("PtGenKaVsCent_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("MCclosure_PtKaVsNchMC"), track.pt(), nChMCEta08); } if (isPr && !isPi && !isKa) { registry.fill(HIST("PtPrVsCent_WithRecoEvt"), track.pt(), centrality); registry.fill(HIST("PtGenPrVsCent_WithRecoEvt"), particle.pt(), centrality); - registry.fill(HIST("MCclosure_PtPrVsNchMC"), track.pt(), nChMCEta08); } registry.fill(HIST("PtResolution"), particle.pt(), (track.pt() - particle.pt()) / particle.pt()); } // Loop over reconstructed tracks @@ -1641,7 +2164,7 @@ struct PiKpRAA { } // Loop over Reco. Collisions: Only the collisions with the largest number of contributors } // If condition: Only simulated evets with at least one reconstrued collision } - PROCESS_SWITCH(PiKpRAA, processSim, "Process Sim", false); + PROCESS_SWITCH(PiKpRAA, processSignalLoss, "Process Signal loss", false); template void getArmeterosVariables(const T& ppos, const T& pneg, U& alpha, U& qT) @@ -1649,7 +2172,7 @@ struct PiKpRAA { alpha = 0., qT = 0.; TVector3 pV0 = ppos + pneg; double pV0mag = pV0.Mag(); - if (pV0mag < kTenToMinusNine) + if (pV0mag < KtEnToMinusNine) return; // protect against zero momentum const TVector3 u = pV0 * (1.0 / pV0mag); @@ -1663,8 +2186,9 @@ struct PiKpRAA { // α: longitudinal asymmetry (uses + and − labels by charge) double denom = pLpos + pLneg; - if (std::abs(denom) < kTenToMinusNine) - return; // avoid 0 division (unphysical for V0s) + if (std::abs(denom) < KtEnToMinusNine) { + return; + } // avoid 0 division (unphysical for V0s) alpha = (pLpos - pLneg) / denom; // equivalently / pV0mag } @@ -1688,7 +2212,7 @@ struct PiKpRAA { dcaZcut *= trackSelections.nSigmaDCAz; } - if (std::abs(track.eta()) > trackSelections.maxEta) { + if (track.eta() < trackSelections.minEta || track.eta() > trackSelections.maxEta) { return false; } @@ -1702,9 +2226,6 @@ struct PiKpRAA { return false; } - // std::cout << "track.itsClusterMap() = " << static_cast(track.itsClusterMap()) << '\n'; - // std::cout << "track.itsNCls() = " << static_cast(track.itsNCls()) << '\n'; - if (track.itsNCls() < trackSelections.minNClusITS || track.tpcNClsCrossedRows() < trackSelections.minNCrossedRows || track.tpcChi2NCl() > trackSelections.maxChi2ClsTPC || @@ -1714,13 +2235,22 @@ struct PiKpRAA { } // ==== Ncl selection ==== // - if (trackSelections.applyNclSel && ncl < trackSelections.minNcls) + if (trackSelections.applyNclSel && ncl < trackSelections.minNcl) { return false; + } // ==== DCAxy & DCAz selections ==== // if (applyDca && cfgDCA.dcaSelectionsLoaded) { - if (std::abs(dcaZ) > dcaZcut || std::abs(dcaXY) > dcaXYcut) + if (std::abs(dcaZ) > dcaZcut || std::abs(dcaXY) > dcaXYcut) { return false; + } + } + + // Flag to check that the DCA selections are loaded + // when asking to apply DCA + // ==== DCAxy & DCAz selections ==== // + if (loadHisWithDCASel && applyDca && !cfgDCA.dcaSelectionsLoaded) { + return false; } return true; @@ -1730,14 +2260,14 @@ struct PiKpRAA { bool selectV0Daughter(const T& track) { - const float eta{track.eta()}; + // const float eta{track.eta()}; const uint8_t nclShared{track.tpcNClsShared()}; const int16_t nCrossedRows{track.tpcNClsCrossedRows()}; const int16_t nclFound{track.tpcNClsFound()}; const int16_t nclPID{track.tpcNClsPID()}; const int16_t ncl{trackSelections.useNclsPID ? nclPID : nclFound}; - if (std::abs(eta) > trackSelections.maxEta) { + if (track.eta() < trackSelections.minEta || track.eta() > trackSelections.maxEta) { return false; } @@ -1751,12 +2281,14 @@ struct PiKpRAA { } // ==== Ncl selection ==== // - if (trackSelections.applyNclSel && ncl < v0Selections.minNcl) + if (trackSelections.applyNclSel && ncl < v0Selections.minNclV0Daugther) { return false; + } // ==== Ncl shared ==== // - if (nclShared > v0Selections.nSharedClusTpc) + if (nclShared > v0Selections.nSharedClusTpc) { return false; + } return true; } @@ -1795,7 +2327,7 @@ struct PiKpRAA { const double dMassL{std::abs(v0.mLambda() - o2::constants::physics::MassLambda0)}; const double dMassAL{std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0)}; const double dMassG{std::abs(v0.mGamma() - o2::constants::physics::MassGamma)}; - const double rapidity{std::abs(v0.yK0Short())}; + const double rapidity{v0.yK0Short()}; const double lifeTime{v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short}; // Checks if DCA of daughters to PV passes selection @@ -1803,19 +2335,22 @@ struct PiKpRAA { // Rejects V0 if its invariant mass is not compatible with the K0s proper mass if (v0Selections.applyInvMassSel) { - if (!(dMassK0s < v0Selections.dMassSel && dMassL > v0Selections.dMassSel && dMassAL > v0Selections.dMassSel && dMassG > v0Selections.dMassSelG)) + if (!(dMassK0s < v0Selections.dMassSel && dMassL > v0Selections.dMassSel && dMassAL > v0Selections.dMassSel && dMassG > v0Selections.dMassSelG)) { return false; + } } bool isSelected{false}; if (v0Selections.useOfficialV0sSelOfDaughters) { - isSelected = lifeTime < v0Selections.lifeTimeCutK0s && rapidity < v0Selections.rapidityCut && dcaDaugToPV ? true : false; + isSelected = lifeTime < v0Selections.lifeTimeCutK0s && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && dcaDaugToPV ? true : false; } if (!v0Selections.useOfficialV0sSelOfDaughters) { - if (v0Selections.useTPCNsigma) - isSelected = lifeTime < v0Selections.lifeTimeCutK0s && rapidity < v0Selections.rapidityCut && posTPCNsigma < v0Selections.pidNsigmaCut && negTPCNsigma < v0Selections.pidNsigmaCut && dcaDaugToPV ? true : false; - if (!v0Selections.useTPCNsigma) - isSelected = lifeTime < v0Selections.lifeTimeCutK0s && rapidity < v0Selections.rapidityCut && posTOFNsigma < v0Selections.pidNsigmaCut && negTOFNsigma < v0Selections.pidNsigmaCut && hasToF && goodToFmatch && dcaDaugToPV ? true : false; + if (v0Selections.useTPCNsigma) { + isSelected = lifeTime < v0Selections.lifeTimeCutK0s && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && posTPCNsigma < v0Selections.pidNsigmaCut && negTPCNsigma < v0Selections.pidNsigmaCut && dcaDaugToPV ? true : false; + } + if (!v0Selections.useTPCNsigma) { + isSelected = lifeTime < v0Selections.lifeTimeCutK0s && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && posTOFNsigma < v0Selections.pidNsigmaCut && negTOFNsigma < v0Selections.pidNsigmaCut && hasToF && goodToFmatch && dcaDaugToPV ? true : false; + } } if (isSelected) { @@ -1848,7 +2383,7 @@ struct PiKpRAA { const double dMassK0s{std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short)}; const double dMassL{std::abs(v0.mLambda() - o2::constants::physics::MassLambda0)}; const double dMassG{std::abs(v0.mGamma() - o2::constants::physics::MassGamma)}; - const double rapidity{std::abs(v0.yLambda())}; + const double rapidity{v0.yLambda()}; const double lifeTime{v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0}; // Checks if DCA of daughters to PV passes selection @@ -1856,19 +2391,22 @@ struct PiKpRAA { // Rejects V0 if the invariant mass is not compatible with the Lambda proper mass if (v0Selections.applyInvMassSel) { - if (!(dMassL < v0Selections.dMassSel && dMassK0s > v0Selections.dMassSel && dMassG > v0Selections.dMassSelG)) + if (!(dMassL < v0Selections.dMassSel && dMassK0s > v0Selections.dMassSel && dMassG > v0Selections.dMassSelG)) { return false; + } } bool isSelected{false}; if (v0Selections.useOfficialV0sSelOfDaughters) { - isSelected = lifeTime < v0Selections.lifeTimeCutLambda && rapidity < v0Selections.rapidityCut && dcaDaugToPV ? true : false; + isSelected = lifeTime < v0Selections.lifeTimeCutLambda && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && dcaDaugToPV ? true : false; } if (!v0Selections.useOfficialV0sSelOfDaughters) { - if (v0Selections.useTPCNsigma) - isSelected = lifeTime < v0Selections.lifeTimeCutLambda && rapidity < v0Selections.rapidityCut && posTPCNsigma < v0Selections.pidNsigmaCut && negTPCNsigma < v0Selections.pidNsigmaCut && dcaDaugToPV ? true : false; - if (!v0Selections.useTPCNsigma) - isSelected = lifeTime < v0Selections.lifeTimeCutLambda && rapidity < v0Selections.rapidityCut && posTOFNsigma < v0Selections.pidNsigmaCut && negTOFNsigma < v0Selections.pidNsigmaCut && hasToF && goodToFmatch && dcaDaugToPV ? true : false; + if (v0Selections.useTPCNsigma) { + isSelected = lifeTime < v0Selections.lifeTimeCutLambda && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && posTPCNsigma < v0Selections.pidNsigmaCut && negTPCNsigma < v0Selections.pidNsigmaCut && dcaDaugToPV ? true : false; + } + if (!v0Selections.useTPCNsigma) { + isSelected = lifeTime < v0Selections.lifeTimeCutLambda && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && posTOFNsigma < v0Selections.pidNsigmaCut && negTOFNsigma < v0Selections.pidNsigmaCut && hasToF && goodToFmatch && dcaDaugToPV ? true : false; + } } if (isSelected) { @@ -1901,7 +2439,7 @@ struct PiKpRAA { const double dMassK0s{std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short)}; const double dMassAL{std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0)}; const double dMassG{std::abs(v0.mGamma() - o2::constants::physics::MassGamma)}; - const double rapidity{std::abs(v0.yLambda())}; + const double rapidity{v0.yLambda()}; const double lifeTime{v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0}; // Checks if DCA of daughters to PV passes selection @@ -1909,19 +2447,22 @@ struct PiKpRAA { // Rejects V0 if the invariant mass is not compatible with the Lambda proper mass if (v0Selections.applyInvMassSel) { - if (!(dMassAL < v0Selections.dMassSel && dMassK0s > v0Selections.dMassSel && dMassG > v0Selections.dMassSelG)) + if (!(dMassAL < v0Selections.dMassSel && dMassK0s > v0Selections.dMassSel && dMassG > v0Selections.dMassSelG)) { return false; + } } bool isSelected{false}; if (v0Selections.useOfficialV0sSelOfDaughters) { - isSelected = lifeTime < v0Selections.lifeTimeCutLambda && rapidity < v0Selections.rapidityCut && dcaDaugToPV ? true : false; + isSelected = lifeTime < v0Selections.lifeTimeCutLambda && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && dcaDaugToPV ? true : false; } if (!v0Selections.useOfficialV0sSelOfDaughters) { - if (v0Selections.useTPCNsigma) - isSelected = lifeTime < v0Selections.lifeTimeCutLambda && rapidity < v0Selections.rapidityCut && posTPCNsigma < v0Selections.pidNsigmaCut && negTPCNsigma < v0Selections.pidNsigmaCut && dcaDaugToPV ? true : false; - if (!v0Selections.useTPCNsigma) - isSelected = lifeTime < v0Selections.lifeTimeCutLambda && rapidity < v0Selections.rapidityCut && posTOFNsigma < v0Selections.pidNsigmaCut && negTOFNsigma < v0Selections.pidNsigmaCut && hasToF && goodToFmatch && dcaDaugToPV ? true : false; + if (v0Selections.useTPCNsigma) { + isSelected = lifeTime < v0Selections.lifeTimeCutLambda && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && posTPCNsigma < v0Selections.pidNsigmaCut && negTPCNsigma < v0Selections.pidNsigmaCut && dcaDaugToPV ? true : false; + } + if (!v0Selections.useTPCNsigma) { + isSelected = lifeTime < v0Selections.lifeTimeCutLambda && (rapidity > v0Selections.minY && rapidity < v0Selections.maxY) && posTOFNsigma < v0Selections.pidNsigmaCut && negTOFNsigma < v0Selections.pidNsigmaCut && hasToF && goodToFmatch && dcaDaugToPV ? true : false; + } } if (isSelected) { @@ -1947,28 +2488,32 @@ struct PiKpRAA { const double dMassL{std::abs(v0.mLambda() - o2::constants::physics::MassLambda0)}; const double dMassAL{std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0)}; const double dMassG{std::abs(v0.mGamma() - o2::constants::physics::MassGamma)}; - const float yGamma = RecoDecay::y(std::array{v0.px(), v0.py(), v0.pz()}, o2::constants::physics::MassGamma); + const float rapidity = RecoDecay::y(std::array{v0.px(), v0.py(), v0.pz()}, o2::constants::physics::MassGamma); // Checks if DCA of daughters to PV passes selection const bool dcaDaugToPV{dcaPos > v0Selections.dcaElectronFromGamma && dcaNeg > v0Selections.dcaElectronFromGamma ? true : false}; if (v0Selections.applyInvMassSel) { - if (!(dMassK0s > v0Selections.dMassSel && dMassL > v0Selections.dMassSel && dMassAL > v0Selections.dMassSel && dMassG < v0Selections.dMassSel)) + if (!(dMassK0s > v0Selections.dMassSel && dMassL > v0Selections.dMassSel && dMassAL > v0Selections.dMassSel && dMassG < v0Selections.dMassSel)) { return false; + } } - if (!(std::abs(yGamma) < v0Selections.rapidityCut)) + if (!(rapidity > v0Selections.minY && rapidity < v0Selections.maxY)) { return false; + } bool isSelected{false}; - if (v0Selections.useOfficialV0sSelOfDaughters) + if (v0Selections.useOfficialV0sSelOfDaughters) { isSelected = dcaDaugToPV ? true : false; - if (!v0Selections.useOfficialV0sSelOfDaughters) + } + if (!v0Selections.useOfficialV0sSelOfDaughters) { isSelected = dcaDaugToPV && posTPCNsigma < v0Selections.pidNsigmaCut && negTPCNsigma < v0Selections.pidNsigmaCut ? true : false; + } if (isSelected) { - registry.fill(HIST("EtaVsYG"), negTrack.eta(), yGamma); - registry.fill(HIST("EtaVsYG"), posTrack.eta(), yGamma); + registry.fill(HIST("EtaVsYG"), negTrack.eta(), rapidity); + registry.fill(HIST("EtaVsYG"), posTrack.eta(), rapidity); } return isSelected; @@ -1991,10 +2536,12 @@ struct PiKpRAA { void phiPrimeFunc(float& phi, const int& magField, const int& charge) { - if (magField < 0) + if (magField < 0) { phi = o2::constants::math::TwoPI - phi; - if (charge < 0) + } + if (charge < 0) { phi = o2::constants::math::TwoPI - phi; + } phi += o2::constants::math::PI / 18.0f; phi = std::fmod(phi, o2::constants::math::PI / 9.0f); @@ -2003,8 +2550,9 @@ struct PiKpRAA { bool passesPhiSelection(const float& pt, const float& phi) { // Do not apply Phi Sel if pt < 2 GeV/c - if (pt < kTwoPtGeVSel) + if (pt < KtwoPtGeVSel) { return true; + } bool isSelected{true}; if (phiCut.isPhiCutLoaded) { @@ -2012,8 +2560,9 @@ struct PiKpRAA { const int binHigh{phiCut.hPhiCutHigh->FindBin(pt)}; const double phiCutLow{phiCut.hPhiCutLow->GetBinContent(binLow)}; const double phiCutHigh{phiCut.hPhiCutHigh->GetBinContent(binHigh)}; - if (phi >= phiCutLow && phi <= phiCutHigh) + if (phi >= phiCutLow && phi <= phiCutHigh) { isSelected = false; + } } return isSelected; } @@ -2145,7 +2694,19 @@ struct PiKpRAA { } if (isCentSel) { - if (col.centFT0C() < minT0CcentCut || col.centFT0C() > maxT0CcentCut) { + + float cenT{-999.0}; + if (centralitySelector.value == "FT0C") { + cenT = col.centFT0C(); + } else if (centralitySelector.value == "FT0M") { + cenT = col.centFT0M(); + } else if (centralitySelector.value == "FV0A") { + cenT = col.centFV0A(); + } else { + cenT = -999.0; + } + + if (cenT < minCentCut || cenT > maxCentCut) { return false; } registry.fill(HIST("EventCounter"), EvCutLabel::Centrality); @@ -2183,23 +2744,24 @@ struct PiKpRAA { } } - void loadDCAselections(uint64_t timeStamp) + void loadDCAselections() { if (pathDCAxy.value.empty() == false) { - cfgDCA.hDCAxy = ccdb->getForTimeStamp(pathDCAxy, timeStamp); + cfgDCA.hDCAxy = ccdb->getForTimeStamp(pathDCAxy, ccdbNoLaterThan.value); if (cfgDCA.hDCAxy == nullptr) { LOGF(fatal, "Could not load hDCAxy histogram from %s", pathDCAxy.value.c_str()); } } if (pathDCAz.value.empty() == false) { - cfgDCA.hDCAz = ccdb->getForTimeStamp(pathDCAz, timeStamp); + cfgDCA.hDCAz = ccdb->getForTimeStamp(pathDCAz, ccdbNoLaterThan.value); if (cfgDCA.hDCAz == nullptr) { LOGF(fatal, "Could not load hDCAz histogram from %s", pathDCAz.value.c_str()); } } - if (cfgDCA.hDCAxy && cfgDCA.hDCAz) + if (cfgDCA.hDCAxy && cfgDCA.hDCAz) { cfgDCA.dcaSelectionsLoaded = true; + } } void loadPhiCutSelections(const uint64_t& timeStamp) @@ -2218,20 +2780,23 @@ struct PiKpRAA { } } - if (phiCut.hPhiCutHigh && phiCut.hPhiCutLow) + if (phiCut.hPhiCutHigh && phiCut.hPhiCutLow) { phiCut.isPhiCutLoaded = true; + } } void loadEtaCalibration() { if (pathEtaCal.value.empty() == false) { etaCal.pEtaCal = ccdb->getForTimeStamp(pathEtaCal, ccdbNoLaterThan.value); - if (etaCal.pEtaCal == nullptr) + if (etaCal.pEtaCal == nullptr) { LOGF(fatal, "Could not load pEtaCal from %s", pathEtaCal.value.c_str()); + } } - if (etaCal.pEtaCal) + if (etaCal.pEtaCal) { etaCal.isMIPCalLoaded = true; + } } void loadEtaPlateauCalibration() @@ -2239,12 +2804,14 @@ struct PiKpRAA { if (pathEtaCalPlateau.value.empty() == false) { etaCal.pEtaCalPlateau = ccdb->getForTimeStamp(pathEtaCalPlateau, ccdbNoLaterThan.value); - if (etaCal.pEtaCalPlateau == nullptr) + if (etaCal.pEtaCalPlateau == nullptr) { LOGF(fatal, "Could not load pEtaCalPlateau from %s", pathEtaCalPlateau.value.c_str()); + } } - if (etaCal.pEtaCalPlateau) + if (etaCal.pEtaCalPlateau) { etaCal.isCalPlateauLoaded = true; + } } }; diff --git a/PWGLF/Tasks/Nuspex/spectraKinkPiKa.cxx b/PWGLF/Tasks/Nuspex/spectraKinkPiKa.cxx index 3adeefd79ee..7d2bb8bcd0a 100644 --- a/PWGLF/Tasks/Nuspex/spectraKinkPiKa.cxx +++ b/PWGLF/Tasks/Nuspex/spectraKinkPiKa.cxx @@ -558,7 +558,7 @@ struct SpectraKinkPiKa { } } - double computeMotherMass(ROOT::Math::PxPyPzMVector pmoth, ROOT::Math::PxPyPzMVector pdaug) + double computeMotherMass(const ROOT::Math::PxPyPzMVector& pmoth, const ROOT::Math::PxPyPzMVector& pdaug) { // Infer neutrino momentum from conservation ROOT::Math::XYZVector pnuvec = pmoth.Vect() - pdaug.Vect(); @@ -573,7 +573,7 @@ struct SpectraKinkPiKa { double m2 = etotal * etotal - ptotalsq; return (m2 > 0) ? std::sqrt(m2) : -1.0; } - double computeQt(ROOT::Math::PxPyPzMVector pmoth, ROOT::Math::PxPyPzMVector pdaug) + double computeQt(const ROOT::Math::PxPyPzMVector& pmoth, const ROOT::Math::PxPyPzMVector& pdaug) { TVector3 pdlab(pdaug.Px(), pdaug.Py(), pdaug.Pz()); // Compute transverse component diff --git a/PWGLF/Tasks/Nuspex/spectraTOF.cxx b/PWGLF/Tasks/Nuspex/spectraTOF.cxx index 458ed357b7e..6ab1dd8a82d 100644 --- a/PWGLF/Tasks/Nuspex/spectraTOF.cxx +++ b/PWGLF/Tasks/Nuspex/spectraTOF.cxx @@ -127,9 +127,12 @@ struct tofSpectra { struct : ConfigurableGroup { Configurable cfgCutEtaMax{"cfgCutEtaMax", 0.8f, "Max eta range for tracks"}; + Configurable cfgCutdcaZMax{"cfgCutdcaZMax", 0.02f, "Max dcaZ range for tracks"}; Configurable cfgCutNsigma{"cfgCutNsigma", 100.0f, "nsigma cut range for tracks"}; Configurable cfgCutEtaMin{"cfgCutEtaMin", -0.8f, "Min eta range for tracks"}; - Configurable cfgCutY{"cfgCutY", 0.5f, "Y range for tracks"}; + Configurable cfgCutdcaZMin{"cfgCutdcaZMin", -0.02f, "Min dcaZ range for tracks"}; + Configurable cfgCutYMax{"cfgCutYMax", 0.5f, "Max Y range for tracks"}; + Configurable cfgCutYMin{"cfgCutYMin", -0.5f, "Min Y range for tracks"}; Configurable lastRequiredTrdCluster{"lastRequiredTrdCluster", 5, "Last cluster to require in TRD for track selection. -1 does not require any TRD cluster"}; Configurable requireTrdOnly{"requireTrdOnly", false, "Require only tracks from TRD"}; Configurable requireNoTrd{"requireNoTrd", false, "Require tracks without TRD"}; @@ -175,7 +178,7 @@ struct tofSpectra { Configurable minChi2PerClusterTPC{"minChi2PerClusterTPC", 0.5f, "Additional cut on the minimum value of the chi2 per cluster in the TPC"}; Configurable maxChi2PerClusterITS{"maxChi2PerClusterITS", 36.f, "Additional cut on the maximum value of the chi2 per cluster in the ITS"}; Configurable maxDcaXYFactor{"maxDcaXYFactor", 1.f, "Additional cut on the maximum value of the DCA xy (multiplicative factor)"}; - Configurable maxDcaZ{"maxDcaZ", 2.f, "Additional cut on the maximum value of the DCA z"}; + Configurable maxDcaZ{"maxDcaZ", 0.02f, "Additional cut on the maximum value of the DCA z"}; Configurable minTPCNClsFound{"minTPCNClsFound", 100.f, "Additional cut on the minimum value of the number of found clusters in the TPC"}; Configurable makeTHnSparseChoice{"makeTHnSparseChoice", false, "choose if produce thnsparse"}; // RD Configurable enableTPCTOFvsEtaHistograms{"enableTPCTOFvsEtaHistograms", false, "choose if produce TPC tof vs Eta"}; @@ -757,7 +760,7 @@ struct tofSpectra { if (enableDCAxyzHistograms) { hDcaXYZ[i] = histos.add(Form("dca/%s/%s", (i < Np) ? "pos" : "neg", pN[i % Np]), pTCharge[i], kTH3D, {ptAxis, dcaXyAxis, dcaZAxis}); } else { - histos.add(hdcaxy[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hdcaxy[i].data(), pTCharge[i], kTH3D, {ptAxis, dcaXyAxis, multAxis}); histos.add(hdcaz[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaZAxis}); } @@ -898,7 +901,8 @@ struct tofSpectra { template void fillParticleHistos(const T& track, const C& collision) { - if (std::abs(track.rapidity(PID::getMass(id))) > trkselOptions.cfgCutY) { + const auto rapidity = track.rapidity(PID::getMass(id)); + if (rapidity < trkselOptions.cfgCutYMin || rapidity > trkselOptions.cfgCutYMax) { return; } if constexpr (id == PID::Kaon) { @@ -1127,7 +1131,7 @@ struct tofSpectra { } } else { if (track.sign() > 0) { - histos.fill(HIST(hdcaxy[id]), track.pt(), track.dcaXY()); + histos.fill(HIST(hdcaxy[id]), track.pt(), track.dcaXY(), multiplicity); histos.fill(HIST(hdcaz[id]), track.pt(), track.dcaZ()); if (isInPtRangeForPhi) { if (enableDCAxyphiHistograms) { @@ -1135,7 +1139,7 @@ struct tofSpectra { } } } else { - histos.fill(HIST(hdcaxy[id + Np]), track.pt(), track.dcaXY()); + histos.fill(HIST(hdcaxy[id + Np]), track.pt(), track.dcaXY(), multiplicity); histos.fill(HIST(hdcaz[id + Np]), track.pt(), track.dcaZ()); if (isInPtRangeForPhi) { if (enableDCAxyphiHistograms) { @@ -1305,6 +1309,9 @@ struct tofSpectra { if (track.eta() < trkselOptions.cfgCutEtaMin || track.eta() > trkselOptions.cfgCutEtaMax) { return false; } + if (track.dcaZ() < trkselOptions.cfgCutdcaZMin || track.dcaZ() > trkselOptions.cfgCutdcaZMax) { + return false; + } if constexpr (fillHistograms) { histos.fill(HIST("tracksel"), 2); if (enableTrackCutHistograms) { @@ -1551,14 +1558,14 @@ struct tofSpectra { bool isTOFKaon = track.hasTOF() && std::abs(nsigmaTOFKa) < trkselOptions.cfgCutNsigma; bool isTOFProton = track.hasTOF() && std::abs(nsigmaTOFPr) < trkselOptions.cfgCutNsigma; // Precompute rapidity values to avoid redundant calculations - double rapidityPi = std::abs(track.rapidity(PID::getMass(2))); - double rapidityKa = std::abs(track.rapidity(PID::getMass(3))); - double rapidityPr = std::abs(track.rapidity(PID::getMass(4))); + double rapidityPi = track.rapidity(PID::getMass(2)); + double rapidityKa = track.rapidity(PID::getMass(3)); + double rapidityPr = track.rapidity(PID::getMass(4)); if (track.eta() < trkselOptions.cfgCutEtaMin || track.eta() > trkselOptions.cfgCutEtaMax) { return; } if (mcParticle.isPhysicalPrimary()) { - if (isTPCPion && rapidityPi <= trkselOptions.cfgCutY) { + if (isTPCPion && rapidityPi >= trkselOptions.cfgCutYMin && rapidityPi <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kPiPlus) { histos.fill(HIST("nsigmatpc/mc_closure/pos/pi"), track.pt(), nsigmaTPCPi, multiplicity); @@ -1570,7 +1577,7 @@ struct tofSpectra { histos.fill(HIST("nsigmatpc/mc_closure/neg/pi"), track.pt(), nsigmaTPCPi, multiplicity); } } - if (isTPCKaon && rapidityKa <= trkselOptions.cfgCutY) { + if (isTPCKaon && rapidityKa >= trkselOptions.cfgCutYMin && rapidityKa <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kKPlus) { histos.fill(HIST("nsigmatpc/mc_closure/pos/ka"), track.pt(), nsigmaTPCKa, multiplicity); @@ -1582,7 +1589,7 @@ struct tofSpectra { histos.fill(HIST("nsigmatpc/mc_closure/neg/ka"), track.pt(), nsigmaTPCKa, multiplicity); } } - if (isTPCProton && rapidityPr <= trkselOptions.cfgCutY) { + if (isTPCProton && rapidityPr >= trkselOptions.cfgCutYMin && rapidityPr <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kProton) { histos.fill(HIST("nsigmatpc/mc_closure/pos/pr"), track.pt(), nsigmaTPCPr, multiplicity); @@ -1596,7 +1603,7 @@ struct tofSpectra { } // TOF Selection and Histogram Filling - if (isTOFPion && rapidityPi <= trkselOptions.cfgCutY) { + if (isTOFPion && rapidityPi >= trkselOptions.cfgCutYMin && rapidityPi <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kPiPlus) { histos.fill(HIST("nsigmatof/mc_closure/pos/pi"), track.pt(), nsigmaTOFPi, multiplicity); @@ -1608,7 +1615,7 @@ struct tofSpectra { histos.fill(HIST("nsigmatof/mc_closure/neg/pi"), track.pt(), nsigmaTOFPi, multiplicity); } } - if (isTOFKaon && rapidityKa <= trkselOptions.cfgCutY) { + if (isTOFKaon && rapidityKa >= trkselOptions.cfgCutYMin && rapidityKa <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kKPlus) { histos.fill(HIST("nsigmatof/mc_closure/pos/ka"), track.pt(), nsigmaTOFKa, multiplicity); @@ -1620,7 +1627,7 @@ struct tofSpectra { histos.fill(HIST("nsigmatof/mc_closure/neg/ka"), track.pt(), nsigmaTOFKa, multiplicity); } } - if (isTOFProton && rapidityPr <= trkselOptions.cfgCutY) { + if (isTOFProton && rapidityPr >= trkselOptions.cfgCutYMin && rapidityPr <= trkselOptions.cfgCutYMax) { if (usePDGcode) { if (pdgCode == kProton) { histos.fill(HIST("nsigmatof/mc_closure/pos/pr"), track.pt(), nsigmaTOFPr, multiplicity); @@ -1688,21 +1695,21 @@ struct tofSpectra { const bool isTOFProton = track.hasTOF() && std::abs(nsigmaTOFPr) < trkselOptions.cfgCutNsigma; // Apply rapidity cut for identified particles - if (isTPCPion && std::abs(track.rapidity(PID::getMass(2))) < trkselOptions.cfgCutY) { + if (isTPCPion && track.rapidity(PID::getMass(2)) >= trkselOptions.cfgCutYMin && track.rapidity(PID::getMass(2)) <= trkselOptions.cfgCutYMax) { tpcCount++; if (track.sign() > 0) { histos.fill(HIST("nsigmatpc/test_occupancy/pos/pi"), track.pt(), nsigmaTPCPi, multiplicity, occupancy); } else { histos.fill(HIST("nsigmatpc/test_occupancy/neg/pi"), track.pt(), nsigmaTPCPi, multiplicity, occupancy); } - } else if (isTPCKaon && std::abs(track.rapidity(PID::getMass(3))) < trkselOptions.cfgCutY) { + } else if (isTPCKaon && track.rapidity(PID::getMass(3)) >= trkselOptions.cfgCutYMin && track.rapidity(PID::getMass(3)) <= trkselOptions.cfgCutYMax) { tpcCount++; if (track.sign() > 0) { histos.fill(HIST("nsigmatpc/test_occupancy/pos/ka"), track.pt(), nsigmaTPCKa, multiplicity, occupancy); } else { histos.fill(HIST("nsigmatpc/test_occupancy/neg/ka"), track.pt(), nsigmaTPCKa, multiplicity, occupancy); } - } else if (isTPCProton && std::abs(track.rapidity(PID::getMass(4))) < trkselOptions.cfgCutY) { + } else if (isTPCProton && track.rapidity(PID::getMass(4)) >= trkselOptions.cfgCutYMin && track.rapidity(PID::getMass(4)) <= trkselOptions.cfgCutYMax) { tpcCount++; if (track.sign() > 0) { histos.fill(HIST("nsigmatpc/test_occupancy/pos/pr"), track.pt(), nsigmaTPCPr, multiplicity, occupancy); @@ -1712,21 +1719,21 @@ struct tofSpectra { } // TOF PID histograms - if (isTOFPion && std::abs(track.rapidity(PID::getMass(2))) < trkselOptions.cfgCutY) { + if (isTOFPion && track.rapidity(PID::getMass(2)) >= trkselOptions.cfgCutYMin && track.rapidity(PID::getMass(2)) <= trkselOptions.cfgCutYMax) { tofCount++; if (track.sign() > 0) { histos.fill(HIST("nsigmatof/test_occupancy/pos/pi"), track.pt(), nsigmaTOFPi, multiplicity, occupancy); } else { histos.fill(HIST("nsigmatof/test_occupancy/neg/pi"), track.pt(), nsigmaTOFPi, multiplicity, occupancy); } - } else if (isTOFKaon && std::abs(track.rapidity(PID::getMass(3))) < trkselOptions.cfgCutY) { + } else if (isTOFKaon && track.rapidity(PID::getMass(3)) >= trkselOptions.cfgCutYMin && track.rapidity(PID::getMass(3)) <= trkselOptions.cfgCutYMax) { tofCount++; if (track.sign() > 0) { histos.fill(HIST("nsigmatof/test_occupancy/pos/ka"), track.pt(), nsigmaTOFKa, multiplicity, occupancy); } else { histos.fill(HIST("nsigmatof/test_occupancy/neg/ka"), track.pt(), nsigmaTOFKa, multiplicity, occupancy); } - } else if (isTOFProton && std::abs(track.rapidity(PID::getMass(4))) < trkselOptions.cfgCutY) { + } else if (isTOFProton && track.rapidity(PID::getMass(4)) >= trkselOptions.cfgCutYMin && track.rapidity(PID::getMass(4)) <= trkselOptions.cfgCutYMax) { tofCount++; if (track.sign() > 0) { histos.fill(HIST("nsigmatof/test_occupancy/pos/pr"), track.pt(), nsigmaTOFPr, multiplicity, occupancy); @@ -1883,11 +1890,7 @@ struct tofSpectra { } break; case MultCodes::kCentralityFT0C: // Centrality FT0C - if constexpr (!isMC) { - return collision.centFT0C(); - } else { - return 50.f; // Not implemented yet - } + return collision.centFT0C(); break; case MultCodes::kCentralityFT0M: // Centrality FT0M return collision.centFT0M(); @@ -1898,7 +1901,7 @@ struct tofSpectra { } } - using GenMCCollisions = soa::Join; + using GenMCCollisions = soa::Join; float getMultiplicityMC(const GenMCCollisions::iterator& collision) { return getMultiplicity(collision); } template @@ -1974,7 +1977,7 @@ struct tofSpectra { return; } - if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + if (mcParticle.y() < trkselOptions.cfgCutYMin || mcParticle.y() > trkselOptions.cfgCutYMax) { return; } if (enablePureDCAHistogram) { @@ -2623,7 +2626,7 @@ struct tofSpectra { const float multiplicity = getMultiplicity(collision); for (const auto& mcParticle : particlesInCollision) { - if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + if (mcParticle.y() < trkselOptions.cfgCutYMin || mcParticle.y() > trkselOptions.cfgCutYMax) { continue; } static_for<0, 17>([&](auto i) { @@ -2633,7 +2636,7 @@ struct tofSpectra { } } else { for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + if (mcParticle.y() < trkselOptions.cfgCutYMin || mcParticle.y() > trkselOptions.cfgCutYMax) { continue; } @@ -2667,7 +2670,7 @@ struct tofSpectra { histos.fill(HIST("MC/MultiplicityRecoEv"), getMultiplicityMC(mcCollision)); } for (const auto& mcParticle : particlesInCollision) { - if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + if (mcParticle.y() < trkselOptions.cfgCutYMin || mcParticle.y() > trkselOptions.cfgCutYMax) { continue; } static_for<0, 17>([&](auto i) { @@ -2698,7 +2701,7 @@ struct tofSpectra { } histos.fill(HIST("MC/MultiplicityMCINELgt1"), getMultiplicityMC(mcCollision)); for (const auto& mcParticle : particlesInCollision) { - if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + if (mcParticle.y() < trkselOptions.cfgCutYMin || mcParticle.y() > trkselOptions.cfgCutYMax) { continue; } static_for<0, 17>([&](auto i) { @@ -2728,8 +2731,8 @@ struct tofSpectra { continue; int pdgCode = mcParticleGen.pdgCode(); float pt = mcParticleGen.pt(); - float absY = std::abs(mcParticleGen.y()); - if (absY > trkselOptions.cfgCutY) { + float rapidity = mcParticleGen.y(); + if (rapidity < trkselOptions.cfgCutYMin || rapidity > trkselOptions.cfgCutYMax) { continue; } @@ -2792,8 +2795,8 @@ struct tofSpectra { int pdgCode = mcParticle.pdgCode(); float pt = mcParticle.pt(); - float absY = std::abs(mcParticle.y()); - if (absY > trkselOptions.cfgCutY) { + float rapidity = mcParticle.y(); + if (rapidity < trkselOptions.cfgCutYMin || rapidity > trkselOptions.cfgCutYMax) { continue; } @@ -2876,7 +2879,7 @@ struct tofSpectra { // Precompute rapidity values to avoid redundant calculations double rapiditypar = std::abs(track.rapidity(PID::getMass(par))); // TPC Selection and histogram filling - if (isTPCpar && rapiditypar <= trkselOptions.cfgCutY) { + if (isTPCpar && rapiditypar >= trkselOptions.cfgCutYMin && rapiditypar <= trkselOptions.cfgCutYMax) { static_for<0, NpCharge - 1>([&](auto i) { if (pdgCode == PDGs[i]) { hMCPdgNsigmaTPC[par - 2][i]->Fill(track.pt(), nsigmaTPCpar, multiplicity); diff --git a/PWGLF/Tasks/Nuspex/spectraTOFLight.cxx b/PWGLF/Tasks/Nuspex/spectraTOFLight.cxx new file mode 100644 index 00000000000..3d6dacff05b --- /dev/null +++ b/PWGLF/Tasks/Nuspex/spectraTOFLight.cxx @@ -0,0 +1,1193 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file spectraTOFLight.cxx +/// \author Nicolò Jacazio nicolo.jacazio@cern.ch, Flavio Pio Volpe +/// +/// \brief Task for the analysis of the spectra with the TPC and TOF detector. +/// + +#include "PWGLF/DataModel/LFParticleIdentification.h" // IWYU pragma: keep +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/DataModel/spectraTOF.h" +#include "PWGLF/Utils/inelGt.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::track; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// Histograms +std::array, NpCharge> hDcaXYZ; +std::array, NpCharge> hDcaXYZPrm; +std::array, NpCharge> hDcaXYZStr; +std::array, NpCharge> hDcaXYZMat; + +// inel cut constants +static constexpr int EvSelInelGt0Cut = 1; + +// mc dca filling constants +static constexpr float DcaMaxCombinedSigma = 2.f; +static constexpr float DcaMaxTPCSigma = 1.f; +static constexpr float DcaTrkPtCut = 0.4f; + +// Spectra task +struct SpectraTOFLight { + struct : ConfigurableGroup { + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; + Configurable cfgINELCut{"cfgINELCut", 0, "INEL event selection: 0 sel, 1 INEL>0"}; + Configurable requireSel8{"requireSel8", false, "Ask for sel8"}; + Configurable removeITSROFrameBorder{"removeITSROFrameBorder", false, "Remove TF border"}; + Configurable removeNoTimeFrameBorder{"removeNoTimeFrameBorder", false, "Remove TF border"}; + } evselOptions; + + struct : ConfigurableGroup { + Configurable cfgCutEtaMax{"cfgCutEtaMax", 0.8f, "Max eta range for tracks"}; + Configurable cfgCutEtaMin{"cfgCutEtaMin", -0.8f, "Min eta range for tracks"}; + Configurable cfgCutNsigma{"cfgCutNsigma", 100.0f, "nsigma cut range for tracks"}; + Configurable cfgCutY{"cfgCutY", 0.5f, "Y range for tracks"}; + } trkselOptions; + + TrackSelection customTrackCuts; + Configurable kaonIsPvContrib{"kaonIsPvContrib", false, "Flag to check if kaon tracks are from pv"}; + Configurable useCustomTrackCuts{"useCustomTrackCuts", false, "Flag to use custom track cuts"}; + Configurable itsPattern{"itsPattern", 0, "0 = Run3ITSibAny, 1 = Run3ITSallAny, 2 = Run3ITSall7Layers, 3 = Run3ITSibTwo"}; + Configurable requireITS{"requireITS", true, "Additional cut on the ITS requirement"}; + Configurable requireTPC{"requireTPC", true, "Additional cut on the TPC requirement"}; + Configurable requireGoldenChi2{"requireGoldenChi2", true, "Additional cut on the GoldenChi2"}; + Configurable minNCrossedRowsTPC{"minNCrossedRowsTPC", 70.f, "Additional cut on the minimum number of crossed rows in the TPC"}; + Configurable minNCrossedRowsOverFindableClustersTPC{"minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; + Configurable maxChi2PerClusterTPC{"maxChi2PerClusterTPC", 4.f, "Additional cut on the maximum value of the chi2 per cluster in the TPC"}; + Configurable minChi2PerClusterTPC{"minChi2PerClusterTPC", 0.5f, "Additional cut on the minimum value of the chi2 per cluster in the TPC"}; + Configurable maxChi2PerClusterITS{"maxChi2PerClusterITS", 36.f, "Additional cut on the maximum value of the chi2 per cluster in the ITS"}; + Configurable maxDcaXYFactor{"maxDcaXYFactor", 1.f, "Additional cut on the maximum value of the DCA xy (multiplicative factor)"}; + Configurable maxDcaZ{"maxDcaZ", 2.f, "Additional cut on the maximum value of the DCA z"}; + Configurable minTPCNClsFound{"minTPCNClsFound", 100.f, "Additional cut on the minimum value of the number of found clusters in the TPC"}; + Configurable makeTHnSparseChoice{"makeTHnSparseChoice", false, "choose if produce thnsparse"}; // RD + Configurable enableTPCTOFvsEtaHistograms{"enableTPCTOFvsEtaHistograms", false, "choose if produce TPC tof vs Eta"}; + Configurable includeCentralityMC{"includeCentralityMC", false, "choose if include Centrality to MC"}; + Configurable enableTPCTOFVsMult{"enableTPCTOFVsMult", false, "Produce TPC-TOF plots vs multiplicity"}; + Configurable minITSnClusters{"minITSnClusters", 5, "minimum number of found ITS clusters"}; + + struct : ConfigurableGroup { + ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0, 0.1, 0.12, 0.14, 0.16, 0.18, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0, 4.2, 4.4, 4.6, 4.8, 5.0}, "Binning of the pT axis"}; + ConfigurableAxis binsEta{"binsEta", {100, -1, 1}, "Binning of the eta axis"}; + ConfigurableAxis binsY{"binsY", {100, -1, 1}, "Binning of the y axis"}; + ConfigurableAxis binsnsigmaTPC{"binsnsigmaTPC", {200, -10, 10}, "Binning of the nsigmaTPC axis"}; + ConfigurableAxis binsnsigmaTOF{"binsnsigmaTOF", {200, -10, 10}, "Binning of the nsigmaTOF axis"}; + ConfigurableAxis binsDca{"binsDca", {VARIABLE_WIDTH, -3.0, -2.95, -2.9, -2.85, -2.8, -2.75, -2.7, -2.65, -2.6, -2.55, -2.5, -2.45, -2.4, -2.35, -2.3, -2.25, -2.2, -2.15, -2.1, -2.05, -2.0, -1.975, -1.95, -1.925, -1.9, -1.875, -1.85, -1.825, -1.8, -1.775, -1.75, -1.725, -1.7, -1.675, -1.65, -1.625, -1.6, -1.575, -1.55, -1.525, -1.5, -1.475, -1.45, -1.425, -1.4, -1.375, -1.35, -1.325, -1.3, -1.275, -1.25, -1.225, -1.2, -1.175, -1.15, -1.125, -1.1, -1.075, -1.05, -1.025, -1.0, -0.99, -0.98, -0.97, -0.96, -0.95, -0.94, -0.93, -0.92, -0.91, -0.9, -0.89, -0.88, -0.87, -0.86, -0.85, -0.84, -0.83, -0.82, -0.81, -0.8, -0.79, -0.78, -0.77, -0.76, -0.75, -0.74, -0.73, -0.72, -0.71, -0.7, -0.69, -0.68, -0.67, -0.66, -0.65, -0.64, -0.63, -0.62, -0.61, -0.6, -0.59, -0.58, -0.57, -0.56, -0.55, -0.54, -0.53, -0.52, -0.51, -0.5, -0.49, -0.48, -0.47, -0.46, -0.45, -0.44, -0.43, -0.42, -0.41, -0.4, -0.396, -0.392, -0.388, -0.384, -0.38, -0.376, -0.372, -0.368, -0.364, -0.36, -0.356, -0.352, -0.348, -0.344, -0.34, -0.336, -0.332, -0.328, -0.324, -0.32, -0.316, -0.312, -0.308, -0.304, -0.3, -0.296, -0.292, -0.288, -0.284, -0.28, -0.276, -0.272, -0.268, -0.264, -0.26, -0.256, -0.252, -0.248, -0.244, -0.24, -0.236, -0.232, -0.228, -0.224, -0.22, -0.216, -0.212, -0.208, -0.204, -0.2, -0.198, -0.196, -0.194, -0.192, -0.19, -0.188, -0.186, -0.184, -0.182, -0.18, -0.178, -0.176, -0.174, -0.172, -0.17, -0.168, -0.166, -0.164, -0.162, -0.16, -0.158, -0.156, -0.154, -0.152, -0.15, -0.148, -0.146, -0.144, -0.142, -0.14, -0.138, -0.136, -0.134, -0.132, -0.13, -0.128, -0.126, -0.124, -0.122, -0.12, -0.118, -0.116, -0.114, -0.112, -0.11, -0.108, -0.106, -0.104, -0.102, -0.1, -0.099, -0.098, -0.097, -0.096, -0.095, -0.094, -0.093, -0.092, -0.091, -0.09, -0.089, -0.088, -0.087, -0.086, -0.085, -0.084, -0.083, -0.082, -0.081, -0.08, -0.079, -0.078, -0.077, -0.076, -0.075, -0.074, -0.073, -0.072, -0.071, -0.07, -0.069, -0.068, -0.067, -0.066, -0.065, -0.064, -0.063, -0.062, -0.061, -0.06, -0.059, -0.058, -0.057, -0.056, -0.055, -0.054, -0.053, -0.052, -0.051, -0.05, -0.049, -0.048, -0.047, -0.046, -0.045, -0.044, -0.043, -0.042, -0.041, -0.04, -0.039, -0.038, -0.037, -0.036, -0.035, -0.034, -0.033, -0.032, -0.031, -0.03, -0.029, -0.028, -0.027, -0.026, -0.025, -0.024, -0.023, -0.022, -0.021, -0.02, -0.019, -0.018, -0.017, -0.016, -0.015, -0.014, -0.013, -0.012, -0.011, -0.01, -0.009, -0.008, -0.007, -0.006, -0.005, -0.004, -0.003, -0.002, -0.001, -0.0, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01, 0.011, 0.012, 0.013, 0.014, 0.015, 0.016, 0.017, 0.018, 0.019, 0.02, 0.021, 0.022, 0.023, 0.024, 0.025, 0.026, 0.027, 0.028, 0.029, 0.03, 0.031, 0.032, 0.033, 0.034, 0.035, 0.036, 0.037, 0.038, 0.039, 0.04, 0.041, 0.042, 0.043, 0.044, 0.045, 0.046, 0.047, 0.048, 0.049, 0.05, 0.051, 0.052, 0.053, 0.054, 0.055, 0.056, 0.057, 0.058, 0.059, 0.06, 0.061, 0.062, 0.063, 0.064, 0.065, 0.066, 0.067, 0.068, 0.069, 0.07, 0.071, 0.072, 0.073, 0.074, 0.075, 0.076, 0.077, 0.078, 0.079, 0.08, 0.081, 0.082, 0.083, 0.084, 0.085, 0.086, 0.087, 0.088, 0.089, 0.09, 0.091, 0.092, 0.093, 0.094, 0.095, 0.096, 0.097, 0.098, 0.099, 0.1, 0.102, 0.104, 0.106, 0.108, 0.11, 0.112, 0.114, 0.116, 0.118, 0.12, 0.122, 0.124, 0.126, 0.128, 0.13, 0.132, 0.134, 0.136, 0.138, 0.14, 0.142, 0.144, 0.146, 0.148, 0.15, 0.152, 0.154, 0.156, 0.158, 0.16, 0.162, 0.164, 0.166, 0.168, 0.17, 0.172, 0.174, 0.176, 0.178, 0.18, 0.182, 0.184, 0.186, 0.188, 0.19, 0.192, 0.194, 0.196, 0.198, 0.2, 0.204, 0.208, 0.212, 0.216, 0.22, 0.224, 0.228, 0.232, 0.236, 0.24, 0.244, 0.248, 0.252, 0.256, 0.26, 0.264, 0.268, 0.272, 0.276, 0.28, 0.284, 0.288, 0.292, 0.296, 0.3, 0.304, 0.308, 0.312, 0.316, 0.32, 0.324, 0.328, 0.332, 0.336, 0.34, 0.344, 0.348, 0.352, 0.356, 0.36, 0.364, 0.368, 0.372, 0.376, 0.38, 0.384, 0.388, 0.392, 0.396, 0.4, 0.41, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.57, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.69, 0.7, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.0, 1.025, 1.05, 1.075, 1.1, 1.125, 1.15, 1.175, 1.2, 1.225, 1.25, 1.275, 1.3, 1.325, 1.35, 1.375, 1.4, 1.425, 1.45, 1.475, 1.5, 1.525, 1.55, 1.575, 1.6, 1.625, 1.65, 1.675, 1.7, 1.725, 1.75, 1.775, 1.8, 1.825, 1.85, 1.875, 1.9, 1.925, 1.95, 1.975, 2.0, 2.05, 2.1, 2.15, 2.2, 2.25, 2.3, 2.35, 2.4, 2.45, 2.5, 2.55, 2.6, 2.65, 2.7, 2.75, 2.8, 2.85, 2.9, 2.95, 3.0}, "Binning of DCA xy and z axis"}; + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {100, 0, 100}, "Binning for multiplicity"}; + ConfigurableAxis binsPercentile{"binsPercentile", {100, 0, 100}, "Binning for percentiles"}; + } binsOptions; + + Configurable enablePureDCAHistogram{"enablePureDCAHistogram", false, "Enables the pure DCA histograms"}; + Configurable enableTPCTOFHistograms{"enableTPCTOFHistograms", true, "Enables TPC TOF histograms"}; + Configurable enableDCAxyzHistograms{"enableDCAxyzHistograms", false, "Enables DCAxyz correlation histograms"}; + Configurable multiplicityEstimator{"multiplicityEstimator", 9, "Flag to use a multiplicity estimator: 0 no multiplicity, 1 MultFV0M, 2 MultFT0M, 3 MultFDDM, 4 MultTracklets, 5 MultTPC, 6 MultNTracksPV, 7 MultNTracksPVeta1, 8 CentralityFT0C, 9 CentralityFT0M, 10 CentralityFV0A"}; + Configurable enableTrackCutHistograms{"enableTrackCutHistograms", true, "Enables track cut histograms, before and after the cut"}; + Configurable doDCAxyCut{"doDCAxyCut", false, "do DCAxy Cut"}; + Configurable doDCAzCut{"doDCAzCut", false, "do DCAz Cut"}; + + // Histograms + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + void init(o2::framework::InitContext&) + { + LOG(info) << "doprocessMC = " << doprocessMC; + LOG(info) << "doprocessStandard = " << doprocessStandard; + // Custom track cuts + if (useCustomTrackCuts.value) { + LOG(info) << "Using custom track cuts from values:"; + LOG(info) << "\trequireITS=" << requireITS.value; + LOG(info) << "\trequireTPC=" << requireTPC.value; + LOG(info) << "\trequireGoldenChi2=" << requireGoldenChi2.value; + LOG(info) << "\tmaxChi2PerClusterTPC=" << maxChi2PerClusterTPC.value; + LOG(info) << "\tminChi2PerClusterTPC=" << minChi2PerClusterTPC.value; + LOG(info) << "\tminNCrossedRowsTPC=" << minNCrossedRowsTPC.value; + LOG(info) << "\tminITSnClusters=" << minITSnClusters.value; + LOG(info) << "\tminTPCNClsFound=" << minTPCNClsFound.value; + LOG(info) << "\tmaxChi2PerClusterITS=" << maxChi2PerClusterITS.value; + LOG(info) << "\tmaxDcaZ=" << maxDcaZ.value; + LOG(info) << "\tmakeTHnSparseChoice=" << makeTHnSparseChoice.value; + + customTrackCuts = getGlobalTrackSelectionRun3ITSMatch(itsPattern.value); + LOG(info) << "Customizing track cuts:"; + customTrackCuts.SetRequireITSRefit(requireITS.value); + customTrackCuts.SetRequireTPCRefit(requireTPC.value); + customTrackCuts.SetMinNClustersITS(minITSnClusters.value); + customTrackCuts.SetRequireGoldenChi2(requireGoldenChi2.value); + customTrackCuts.SetMaxChi2PerClusterTPC(maxChi2PerClusterTPC.value); + customTrackCuts.SetMaxChi2PerClusterITS(maxChi2PerClusterITS.value); + customTrackCuts.SetMinNCrossedRowsTPC(minNCrossedRowsTPC.value); + customTrackCuts.SetMinNClustersTPC(minTPCNClsFound.value); + customTrackCuts.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); + customTrackCuts.SetMaxDcaXYPtDep([](float /*pt*/) { return 10000.f; }); // No DCAxy cut will be used, this is done via the member function of the task + customTrackCuts.SetMaxDcaZ(maxDcaZ.value); + customTrackCuts.print(); + } + // Histograms + const AxisSpec vtxZAxis{100, -20, 20, "Vtx_{z} (cm)"}; + const AxisSpec pAxis{binsOptions.binsPt, "#it{p} (GeV/#it{c})"}; + const AxisSpec ptAxis{binsOptions.binsPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec etaAxis{binsOptions.binsEta, "#eta"}; + const AxisSpec yAxis{binsOptions.binsY, "y"}; + AxisSpec multAxis{binsOptions.binsMultiplicity, "Undefined multiplicity estimator"}; + switch (multiplicityEstimator) { + case MultCodes::kNoMultiplicity: // No multiplicity + break; + case MultCodes::kMultFV0M: // MultFV0M + multAxis.name = "MultFV0M"; + break; + case MultCodes::kMultFT0M: // MultFT0M + multAxis.name = "MultFT0M"; + break; + case MultCodes::kMultFDDM: // MultFDDM + multAxis.name = "MultFDDM"; + break; + case MultCodes::kMultTracklets: // MultTracklets + multAxis.name = "MultTracklets"; + break; + case MultCodes::kMultTPC: // MultTPC + multAxis.name = "MultTPC"; + break; + case MultCodes::kMultNTracksPV: // MultNTracksPV + multAxis.name = "MultNTracksPV"; + break; + case MultCodes::kMultNTracksPVeta1: // MultNTracksPVeta1 + multAxis.name = "MultNTracksPVeta1"; + break; + case MultCodes::kCentralityFT0C: // Centrality FT0C + multAxis = {binsOptions.binsPercentile, "Centrality FT0C"}; + break; + case MultCodes::kCentralityFT0M: // Centrality FT0M + multAxis = {binsOptions.binsPercentile, "Centrality FT0M"}; + break; + case MultCodes::kCentralityFV0A: // Centrality FV0A + multAxis = {binsOptions.binsPercentile, "Centrality FV0A"}; + break; + default: + LOG(fatal) << "Unrecognized option for multiplicity " << multiplicityEstimator; + } + histos.add("event/vertexz", "", HistType::kTH1D, {vtxZAxis}); + + enum EvSelBin { + kAllCollisions = 1, + kTriggerTVXCut, + kNoTFBorderCut, + kNoITSROFBorderCut, + kPosZCut, + kINELGt0Cut, + kXimXimb + }; + auto h = histos.add("Evsel", "Evsel", HistType::kTH1D, {{7, 0.5, 7.5}}); + h->GetXaxis()->SetBinLabel(kAllCollisions, "All collisions"); + h->GetXaxis()->SetBinLabel(kTriggerTVXCut, "Trigger TVX cut"); + h->GetXaxis()->SetBinLabel(kNoTFBorderCut, "kNoTFBorder cut"); + h->GetXaxis()->SetBinLabel(kNoITSROFBorderCut, "kNoITSROFBorder cut"); + h->GetXaxis()->SetBinLabel(kPosZCut, "posZ cut"); + h->GetXaxis()->SetBinLabel(kINELGt0Cut, "INEL>0 cut"); + h->GetXaxis()->SetBinLabel(kXimXimb, "With at least a #Xi^{-}-#bar{#Xi^{-}} pair"); + + h = histos.add("tracksel", "tracksel", HistType::kTH1D, {{10, 0.5, 10.5}}); + h->GetXaxis()->SetBinLabel(1, "Tracks read"); + h->GetXaxis()->SetBinLabel(2, Form(" %.2f < #eta < %.2f ", trkselOptions.cfgCutEtaMin.value, trkselOptions.cfgCutEtaMax.value)); + h->GetXaxis()->SetBinLabel(3, "Quality passed"); + h->GetXaxis()->SetBinLabel(4, "TOF passed (partial)"); + + histos.add("Centrality/FT0M", "FT0M", HistType::kTH1D, {{binsOptions.binsPercentile, "Centrality FT0M"}}); + histos.add("Mult/FT0M", "MultFT0M", HistType::kTH1D, {{binsOptions.binsMultiplicity, "MultFT0M"}}); + + histos.add("Mult/TPC", "MultTPC", HistType::kTH1D, {{binsOptions.binsMultiplicity, "MultTPC"}}); + histos.add("Mult/NTracksPV", "MultNTracksPV", HistType::kTH1D, {{binsOptions.binsMultiplicity, "MultNTracksPV"}}); + histos.add("Mult/NTracksPVeta1", "MultNTracksPVeta1", HistType::kTH1D, {{binsOptions.binsMultiplicity, "MultNTracksPVeta1"}}); + + const AxisSpec dcaXyAxis{binsOptions.binsDca, "DCA_{xy} (cm)"}; + const AxisSpec dcaZAxis{binsOptions.binsDca, "DCA_{z} (cm)"}; + + if (enableTrackCutHistograms) { + const AxisSpec chargeAxis{2, -2.f, 2.f, "Charge"}; + histos.add("track/pos/Eta", "Eta Positive tracks", HistType::kTH1D, {{binsOptions.binsEta, "#eta tracks"}}); + histos.add("track/neg/Eta", "Eta Negative tracks", HistType::kTH1D, {{binsOptions.binsEta, "#eta tracks"}}); + // its histograms + histos.add("track/ITS/itsNCls", "number of found ITS clusters;# clusters ITS", kTH2D, {{8, -0.5, 7.5}, chargeAxis}); + histos.add("track/ITS/itsChi2NCl", "chi2 per ITS cluster;chi2 / cluster ITS", kTH2D, {{100, 0, 40}, chargeAxis}); + // tpc histograms + histos.add("track/TPC/tpcNClsFindable", "number of findable TPC clusters;# findable clusters TPC", kTH2D, {{165, -0.5, 164.5}, chargeAxis}); + histos.add("track/TPC/tpcNClsFound", "number of found TPC clusters;# clusters TPC", kTH2D, {{165, -0.5, 164.5}, chargeAxis}); + histos.add("track/TPC/tpcNClsShared", "number of shared TPC clusters;# shared clusters TPC", kTH2D, {{165, -0.5, 164.5}, chargeAxis}); + histos.add("track/TPC/tpcCrossedRows", "number of crossed TPC rows;# crossed rows TPC", kTH2D, {{165, -0.5, 164.5}, chargeAxis}); + histos.add("track/TPC/tpcFractionSharedCls", "fraction of shared TPC clusters;fraction shared clusters TPC", kTH2D, {{100, 0., 1.}, chargeAxis}); + histos.add("track/TPC/tpcCrossedRowsOverFindableCls", "crossed TPC rows over findable clusters;crossed rows / findable clusters TPC", kTH2D, {{60, 0.7, 1.3}, chargeAxis}); + histos.add("track/TPC/tpcChi2NCl", "chi2 per cluster in TPC;chi2 / cluster TPC", kTH2D, {{100, 0, 10}, chargeAxis}); + histos.add("Vertex/histGenVtxMC", "MC generated vertex z position", HistType::kTH1F, {{400, -40., +40., "z position (cm)"}}); + + histos.addClone("track/ITS/itsNCls", "track/selected/ITS/itsNCls"); + histos.addClone("track/ITS/itsChi2NCl", "track/selected/ITS/itsChi2NCl"); + histos.addClone("track/TPC/tpcNClsFindable", "track/selected/TPC/tpcNClsFindable"); + histos.addClone("track/TPC/tpcNClsFound", "track/selected/TPC/tpcNClsFound"); + histos.addClone("track/TPC/tpcNClsShared", "track/selected/TPC/tpcNClsShared"); + histos.addClone("track/TPC/tpcCrossedRows", "track/selected/TPC/tpcCrossedRows"); + histos.addClone("track/TPC/tpcFractionSharedCls", "track/selected/TPC/tpcFractionSharedCls"); + histos.addClone("track/TPC/tpcCrossedRowsOverFindableCls", "track/selected/TPC/tpcCrossedRowsOverFindableCls"); + histos.addClone("track/TPC/tpcChi2NCl", "track/selected/TPC/tpcChi2NCl"); + } + + // 2 detectors + histos.add("Data/pos/pt/its_tpc", "pos ITS-TPC", kTH1D, {ptAxis}); + histos.add("Data/neg/pt/its_tpc", "neg ITS-TPC", kTH1D, {ptAxis}); + + histos.add("Data/pos/pt/tpc_tof", "pos TPC-TOF", kTH1D, {ptAxis}); + histos.add("Data/neg/pt/tpc_tof", "neg TPC-TOF", kTH1D, {ptAxis}); + + histos.add("Data/pos/pt/its_tof", "pos ITS-TOF", kTH1D, {ptAxis}); + histos.add("Data/neg/pt/its_tof", "neg ITS-TOF", kTH1D, {ptAxis}); + + // 1 detectors + histos.add("Data/pos/pt/its", "pos ITS", kTH1D, {ptAxis}); + histos.add("Data/neg/pt/its", "neg ITS", kTH1D, {ptAxis}); + histos.add("Data/pos/pt/tpc", "pos TPC", kTH1D, {ptAxis}); + histos.add("Data/neg/pt/tpc", "neg TPC", kTH1D, {ptAxis}); + if (doprocessMC) { + histos.add("MC/fake/pos", "Fake positive tracks", kTH1D, {ptAxis}); + histos.add("MC/fake/neg", "Fake negative tracks", kTH1D, {ptAxis}); + histos.add("MC/no_collision/pos", "No collision pos track", kTH1D, {ptAxis}); + histos.add("MC/no_collision/neg", "No collision neg track", kTH1D, {ptAxis}); + + auto hh = histos.add("MC/GenRecoCollisions", "Generated and Reconstructed MC Collisions", kTH1D, {{10, 0.5, 10.5}}); + hh->GetXaxis()->SetBinLabel(1, "Collisions generated"); + hh->GetXaxis()->SetBinLabel(2, "Collisions reconstructed"); + hh->GetXaxis()->SetBinLabel(3, "INEL>0"); + hh->GetXaxis()->SetBinLabel(4, "INEL>1"); + hh->GetXaxis()->SetBinLabel(5, "hasParticleInFT0C && hasParticleInFT0A"); + histos.add("MC/MultiplicityRecoEv", "MC multiplicity", kTH1D, {multAxis}); + histos.add("MC/Multiplicity", "MC multiplicity", kTH1D, {multAxis}); + histos.add("MC/MultiplicityMCINELgt0", "MC multiplicity", kTH1D, {multAxis}); + } + for (int i = 0; i < NpCharge; i++) { + const AxisSpec nsigmaTPCAxis{binsOptions.binsnsigmaTPC, Form("N_{#sigma}^{TPC}(%s)", pTCharge[i])}; + const AxisSpec nsigmaTOFAxis{binsOptions.binsnsigmaTOF, Form("N_{#sigma}^{TOF}(%s)", pTCharge[i])}; + + if (multiplicityEstimator == MultCodes::kNoMultiplicity) { + histos.add(hnsigmatof[i].data(), pTCharge[i], kTH2D, {ptAxis, nsigmaTOFAxis}); + histos.add(hnsigmatpc[i].data(), pTCharge[i], kTH2D, {ptAxis, nsigmaTPCAxis}); + if (enableTPCTOFHistograms) { + if (enableTPCTOFvsEtaHistograms) { + histos.add(hnsigmatpctof[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, etaAxis, nsigmaTPCAxis, nsigmaTOFAxis}); + } else { + histos.add(hnsigmatpctof[i].data(), pTCharge[i], kTH3D, {ptAxis, nsigmaTPCAxis, nsigmaTOFAxis}); + } + } + } else { + if (makeTHnSparseChoice) { // RD + histos.add(hnsigmatof[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, nsigmaTOFAxis, multAxis, dcaXyAxis}); // RD + histos.add(hnsigmatpc[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, nsigmaTPCAxis, multAxis, dcaXyAxis}); // RD + } else { + histos.add(hnsigmatof[i].data(), pTCharge[i], kTH3D, {ptAxis, nsigmaTOFAxis, multAxis}); + histos.add(hnsigmatpc[i].data(), pTCharge[i], kTH3D, {ptAxis, nsigmaTPCAxis, multAxis}); + } + if (enableTPCTOFHistograms) { + if (enableTPCTOFVsMult) { + if (enableTPCTOFvsEtaHistograms) { + histos.add(hnsigmatpctof[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, etaAxis, nsigmaTPCAxis, nsigmaTOFAxis, multAxis}); + } else { + histos.add(hnsigmatpctof[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, nsigmaTPCAxis, nsigmaTOFAxis, multAxis}); + } + } else { + if (enableTPCTOFvsEtaHistograms) { + histos.add(hnsigmatpctof[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, etaAxis, nsigmaTPCAxis, nsigmaTOFAxis}); + } else { + histos.add(hnsigmatpctof[i].data(), pTCharge[i], kTH3D, {ptAxis, nsigmaTPCAxis, nsigmaTOFAxis}); + } + } + } + } + + if (enableDCAxyzHistograms) { + hDcaXYZ[i] = histos.add(Form("dca/%s/%s", (i < Np) ? "pos" : "neg", pN[i % Np]), pTCharge[i], kTH3D, {ptAxis, dcaXyAxis, dcaZAxis}); + } else { + histos.add(hdcaxy[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hdcaz[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaZAxis}); + } + // --- MC --- + if (doprocessMC) { + const std::string cpName = Form("/%s/%s", (i < Np) ? "pos" : "neg", pN[i % Np]); + if (includeCentralityMC) { + histos.add(hpt_num_prm[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis, dcaXyAxis}); + histos.add(hpt_num_str[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis, dcaXyAxis}); + histos.add(hpt_num_mat[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis, dcaXyAxis}); + + histos.add(hpt_den_prm[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis}); + histos.add(hpt_den_str[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis}); + histos.add(hpt_den_mat[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis}); + + histos.add(hpt_numtof_prm[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis, dcaXyAxis}); + histos.add(hpt_numtof_str[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis, dcaXyAxis}); + histos.add(hpt_numtof_mat[i].data(), pTCharge[i], kTHnSparseD, {ptAxis, multAxis, dcaXyAxis}); + + histos.add(hpt_den_prm_goodev[i].data(), pTCharge[i], kTH2D, {ptAxis, multAxis}); + + } else { + histos.add(hpt_num_prm[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hpt_num_str[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hpt_num_mat[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + + histos.add(hpt_numtof_prm[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hpt_numtof_str[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hpt_numtof_mat[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + + histos.add(hpt_den_prm[i].data(), pTCharge[i], kTH1D, {ptAxis}); + histos.add(hpt_den_str[i].data(), pTCharge[i], kTH1D, {ptAxis}); + histos.add(hpt_den_mat[i].data(), pTCharge[i], kTH1D, {ptAxis}); + + histos.add(hpt_den_prm_goodev[i].data(), pTCharge[i], kTH1D, {ptAxis}); + } + histos.add(hpt_den_prm_mcgoodev[i].data(), pTCharge[i], kTH2D, {ptAxis, multAxis}); + if (enableDCAxyzHistograms) { + hDcaXYZPrm[i] = histos.add("dcaprm" + cpName, pTCharge[i], kTH3D, {ptAxis, dcaXyAxis, dcaZAxis}); + hDcaXYZStr[i] = histos.add("dcastr" + cpName, pTCharge[i], kTH3D, {ptAxis, dcaXyAxis, dcaZAxis}); + hDcaXYZMat[i] = histos.add("dcamat" + cpName, pTCharge[i], kTH3D, {ptAxis, dcaXyAxis, dcaZAxis}); + } else { + histos.add(hdcaxyprm[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hdcazprm[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaZAxis}); + histos.add(hdcaxystr[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hdcazstr[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaZAxis}); + histos.add(hdcaxymat[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaXyAxis}); + histos.add(hdcazmat[i].data(), pTCharge[i], kTH2D, {ptAxis, dcaZAxis}); + } + } + } + // Print output histograms statistics + LOG(info) << "Size of the histograms in spectraTOFLight"; + histos.print(); + } + + template + void fillParticleHistos(const T& track, const C& collision) + { + if (std::abs(track.rapidity(PID::getMass(id))) > trkselOptions.cfgCutY) { + return; + } + if constexpr (id == PID::Kaon) { + if (kaonIsPvContrib && !track.isPVContributor()) { + return; + } + } + + const auto& nsigmaTOF = o2::aod::pidutils::tofNSigma(track); + const auto& nsigmaTPC = o2::aod::pidutils::tpcNSigma(track); + + // Filling DCA info with the TPC+TOF PID + bool isDCAPureSample = (std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < DcaMaxCombinedSigma); + if (track.pt() <= DcaTrkPtCut) { + isDCAPureSample = (std::fabs(nsigmaTPC) < DcaMaxTPCSigma); + } + if (isDCAPureSample) { + if (enableDCAxyzHistograms) { + if (track.sign() > 0) { + hDcaXYZ[id]->Fill(track.pt(), track.dcaXY(), track.dcaZ()); + } else { + hDcaXYZ[id + Np]->Fill(track.pt(), track.dcaXY(), track.dcaZ()); + } + } else { + if (track.sign() > 0) { + histos.fill(HIST(hdcaxy[id]), track.pt(), track.dcaXY()); + histos.fill(HIST(hdcaz[id]), track.pt(), track.dcaZ()); + } else { + histos.fill(HIST(hdcaxy[id + Np]), track.pt(), track.dcaXY()); + histos.fill(HIST(hdcaz[id + Np]), track.pt(), track.dcaZ()); + } + } + } + + // -- nsigma histograms -- + if (doDCAxyCut && !passesDCAxyCut(track)) { + return; + } + if (doDCAzCut && !passesDCAzCut(track)) { + return; + } + const float multiplicity = getMultiplicity(collision); + if (makeTHnSparseChoice) { // RD + if (track.sign() > 0) { // RD + histos.fill(HIST(hnsigmatpc[id]), track.pt(), nsigmaTPC, multiplicity, track.dcaXY()); // RD + } else { // RD + histos.fill(HIST(hnsigmatpc[id + Np]), track.pt(), nsigmaTPC, multiplicity, track.dcaXY()); // RD + } + } else { + if (track.sign() > 0) { + histos.fill(HIST(hnsigmatpc[id]), track.pt(), nsigmaTPC, multiplicity); + } else { + histos.fill(HIST(hnsigmatpc[id + Np]), track.pt(), nsigmaTPC, multiplicity); + } + } + // TOF part + if (!track.hasTOF()) { + return; + } + + if (makeTHnSparseChoice) { // RD + if (track.sign() > 0) { // RD + histos.fill(HIST(hnsigmatof[id]), track.pt(), nsigmaTOF, multiplicity, track.dcaXY()); // RD + } else { // RD + histos.fill(HIST(hnsigmatof[id + Np]), track.pt(), nsigmaTOF, multiplicity, track.dcaXY()); // RD + } + } else { + if (track.sign() > 0) { + histos.fill(HIST(hnsigmatof[id]), track.pt(), nsigmaTOF, multiplicity); + } else { + histos.fill(HIST(hnsigmatof[id + Np]), track.pt(), nsigmaTOF, multiplicity); + } + } + + if (enableTPCTOFHistograms) { + if (enableTPCTOFVsMult) { + if (enableTPCTOFvsEtaHistograms) { + if (track.sign() > 0) { + histos.fill(HIST(hnsigmatpctof[id]), track.pt(), track.eta(), nsigmaTPC, nsigmaTOF, multiplicity); + } else { + histos.fill(HIST(hnsigmatpctof[id + Np]), track.pt(), track.eta(), nsigmaTPC, nsigmaTOF, multiplicity); + } + } else { + if (track.sign() > 0) { + histos.fill(HIST(hnsigmatpctof[id]), track.pt(), nsigmaTPC, nsigmaTOF, multiplicity); + } else { + histos.fill(HIST(hnsigmatpctof[id + Np]), track.pt(), nsigmaTPC, nsigmaTOF, multiplicity); + } + } + } else { + if (enableTPCTOFvsEtaHistograms) { + if (track.sign() > 0) { + histos.fill(HIST(hnsigmatpctof[id]), track.pt(), track.eta(), nsigmaTPC, nsigmaTOF); + } else { + histos.fill(HIST(hnsigmatpctof[id + Np]), track.pt(), track.eta(), nsigmaTPC, nsigmaTOF); + } + } else { + if (track.sign() > 0) { + histos.fill(HIST(hnsigmatpctof[id]), track.pt(), nsigmaTPC, nsigmaTOF); + } else { + histos.fill(HIST(hnsigmatpctof[id + Np]), track.pt(), nsigmaTPC, nsigmaTOF); + } + } + } + } + + } // RD + + template + bool isEventSelected(CollisionType const& collision) + { + if constexpr (fillHistograms) { + histos.fill(HIST("Evsel"), 1.f); + } + + if (evselOptions.requireSel8) { + if (!collision.sel8()) { + return false; + } + histos.fill(HIST("Evsel"), 2.f); + histos.fill(HIST("Evsel"), 3.f); + histos.fill(HIST("Evsel"), 4.f); + } else { + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + histos.fill(HIST("Evsel"), 2.f); + if (evselOptions.removeNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + histos.fill(HIST("Evsel"), 3.f); + if (evselOptions.removeITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + histos.fill(HIST("Evsel"), 4.f); + } + if (std::abs(collision.posZ()) > evselOptions.cfgCutVertex) { + return false; + } + if constexpr (fillHistograms) { + histos.fill(HIST("Evsel"), 5.f); + } + if (evselOptions.cfgINELCut == EvSelInelGt0Cut && !collision.isInelGt0()) { + return false; + } + if constexpr (fillHistograms) { + histos.fill(HIST("Evsel"), 6.f); + histos.fill(HIST("event/vertexz"), collision.posZ()); + } + + if constexpr (fillMultiplicity) { + histos.fill(HIST("Centrality/FT0M"), collision.centFT0M()); + histos.fill(HIST("Mult/FT0M"), collision.multZeqFT0A() + collision.multZeqFT0C()); + histos.fill(HIST("Mult/TPC"), collision.multTPC()); + histos.fill(HIST("Mult/NTracksPV"), collision.multZeqNTracksPV()); + histos.fill(HIST("Mult/NTracksPVeta1"), collision.multNTracksPVeta1()); + } + + return true; + } + template + bool passesDCAxyCut(TrackType const& track) const + { + if (useCustomTrackCuts.value) { + for (int i = 0; i < static_cast(TrackSelection::TrackCuts::kNCuts); i++) { + if (i == static_cast(TrackSelection::TrackCuts::kDCAxy)) { + continue; + } + if (!customTrackCuts.IsSelected(track, static_cast(i))) { + return false; + } + } + return (std::abs(track.dcaXY()) <= (maxDcaXYFactor.value * (0.004f + 0.013f / track.pt()))); + } + + return track.isGlobalTrack(); + } + + template + bool passesDCAzCut(TrackType const& track) const + { + if (useCustomTrackCuts.value) { + for (int i = 0; i < static_cast(TrackSelection::TrackCuts::kNCuts); i++) { + if (i == static_cast(TrackSelection::TrackCuts::kDCAz)) { + continue; + } + if (!customTrackCuts.IsSelected(track, static_cast(i))) { + return false; + } + } + // return (std::abs(track.dcaZ()) <= maxDcaZ.value); + return true; + } + + return track.isGlobalTrack(); + } + + template + bool passesCutWoDCA(TrackType const& track) const + { + if (useCustomTrackCuts.value) { + for (int i = 0; i < static_cast(TrackSelection::TrackCuts::kNCuts); i++) { + if (i == static_cast(TrackSelection::TrackCuts::kDCAxy)) { + continue; + } + if (i == static_cast(TrackSelection::TrackCuts::kDCAz)) { + continue; + } + if (!customTrackCuts.IsSelected(track, static_cast(i))) { + return false; + } + } + return true; + } + return track.isGlobalTrackWoDCA(); + } + template + bool isTrackSelected(TrackType const& track, CollisionType const& /*collision*/) + { + if constexpr (fillHistograms) { + histos.fill(HIST("tracksel"), 1); + } + if (track.eta() < trkselOptions.cfgCutEtaMin || track.eta() > trkselOptions.cfgCutEtaMax) { + return false; + } + + if constexpr (fillHistograms) { + histos.fill(HIST("tracksel"), 2); + if (enableTrackCutHistograms) { + if (track.sign() > 0) { + histos.fill(HIST("track/pos/Eta"), track.eta()); + } else { + histos.fill(HIST("track/neg/Eta"), track.eta()); + } + + if (track.hasITS() && track.hasTPC()) { + histos.fill(HIST("track/ITS/itsNCls"), track.itsNCls(), track.sign()); + histos.fill(HIST("track/ITS/itsChi2NCl"), track.itsChi2NCl(), track.sign()); + + histos.fill(HIST("track/TPC/tpcNClsFindable"), track.tpcNClsFindable(), track.sign()); + histos.fill(HIST("track/TPC/tpcNClsFound"), track.tpcNClsFound(), track.sign()); + histos.fill(HIST("track/TPC/tpcNClsShared"), track.tpcNClsShared(), track.sign()); + histos.fill(HIST("track/TPC/tpcCrossedRows"), track.tpcNClsCrossedRows(), track.sign()); + histos.fill(HIST("track/TPC/tpcCrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls(), track.sign()); + histos.fill(HIST("track/TPC/tpcFractionSharedCls"), track.tpcFractionSharedCls(), track.sign()); + histos.fill(HIST("track/TPC/tpcChi2NCl"), track.tpcChi2NCl(), track.sign()); + } + } + + if (track.hasITS() && track.isQualityTrackITS() && track.isInAcceptanceTrack()) { + if (track.sign() > 0) { + histos.fill(HIST("Data/pos/pt/its"), track.pt()); + } else { + histos.fill(HIST("Data/neg/pt/its"), track.pt()); + } + } + if (track.hasTPC() && track.isQualityTrackTPC() && track.isInAcceptanceTrack()) { + if (track.sign() > 0) { + histos.fill(HIST("Data/pos/pt/tpc"), track.pt()); + } else { + histos.fill(HIST("Data/neg/pt/tpc"), track.pt()); + } + } + } + + if (track.tpcChi2NCl() < minChi2PerClusterTPC || track.tpcChi2NCl() > maxChi2PerClusterTPC) { + return false; + } + + if (!passesCutWoDCA(track)) { + return false; + } + + if constexpr (fillHistograms) { + histos.fill(HIST("tracksel"), 3); + if (track.hasTOF()) { + histos.fill(HIST("tracksel"), 4); + } + if (enableTrackCutHistograms) { + histos.fill(HIST("track/selected/ITS/itsNCls"), track.itsNCls(), track.sign()); + histos.fill(HIST("track/selected/ITS/itsChi2NCl"), track.itsChi2NCl(), track.sign()); + + histos.fill(HIST("track/selected/TPC/tpcNClsFindable"), track.tpcNClsFindable(), track.sign()); + histos.fill(HIST("track/selected/TPC/tpcNClsFound"), track.tpcNClsFound(), track.sign()); + histos.fill(HIST("track/selected/TPC/tpcNClsShared"), track.tpcNClsShared(), track.sign()); + histos.fill(HIST("track/selected/TPC/tpcCrossedRows"), track.tpcNClsCrossedRows(), track.sign()); + histos.fill(HIST("track/selected/TPC/tpcCrossedRowsOverFindableCls"), track.tpcCrossedRowsOverFindableCls(), track.sign()); + histos.fill(HIST("track/selected/TPC/tpcFractionSharedCls"), track.tpcFractionSharedCls(), track.sign()); + histos.fill(HIST("track/selected/TPC/tpcChi2NCl"), track.tpcChi2NCl(), track.sign()); + } + } + + if constexpr (fillHistograms) { + + if (track.hasITS() && track.hasTPC()) { + if (track.sign() > 0) { + histos.fill(HIST("Data/pos/pt/its_tpc"), track.pt()); + } else { + histos.fill(HIST("Data/neg/pt/its_tpc"), track.pt()); + } + } + if (track.hasTPC() && track.hasTOF()) { + if (track.sign() > 0) { + histos.fill(HIST("Data/pos/pt/tpc_tof"), track.pt()); + } else { + histos.fill(HIST("Data/neg/pt/tpc_tof"), track.pt()); + } + } + if (track.hasITS() && track.hasTOF()) { + if (track.sign() > 0) { + histos.fill(HIST("Data/pos/pt/its_tof"), track.pt()); + } else { + histos.fill(HIST("Data/neg/pt/its_tof"), track.pt()); + } + } + } + + return true; + } + + using CollisionCandidate = soa::Join; + using TrackCandidates = soa::Join; + + void processStandard(CollisionCandidate::iterator const& collision, + soa::Join const& tracks, + aod::BCs const&) + { + if (!isEventSelected(collision)) { + return; + } + + for (const auto& track : tracks) { + if (!isTrackSelected(track, collision)) { + continue; + } + fillParticleHistos(track, collision); + fillParticleHistos(track, collision); + // fillParticleHistos(track, collision); + } + + } // end of the process function + PROCESS_SWITCH(SpectraTOFLight, processStandard, "Standard data processor", true); + + template + float getMultiplicity(const CollisionType& collision) + { + switch (multiplicityEstimator) { + case MultCodes::kNoMultiplicity: // No multiplicity + return 50.f; // to check if its filled + break; + case MultCodes::kMultFV0M: // MultFV0M + if constexpr (!isMC) { + return collision.multZeqFV0A(); + } else { + return 50.f; // Not implemented yet + } + break; + case MultCodes::kMultFT0M: + if constexpr (!isMC) { + return collision.multZeqFT0A() + collision.multZeqFT0C(); + } else { + return 50.f; // Not implemented yet + } + break; + case MultCodes::kMultFDDM: // MultFDDM + if constexpr (!isMC) { + return collision.multZeqFDDA() + collision.multZeqFDDC(); + } else { + return 50.f; // Not implemented yet + } + break; + case MultCodes::kMultTracklets: // MultTracklets + if constexpr (!isMC) { + // return collision.multTracklets(); + } else { + return 50.f; // Not implemented yet + } + return 0.f; // Undefined in Run3 + break; + case MultCodes::kMultTPC: // MultTPC + if constexpr (!isMC) { + return collision.multTPC(); + } else { + return 50.f; // Not implemented yet + } + break; + case MultCodes::kMultNTracksPV: // MultNTracksPV + if constexpr (!isMC) { + // return collision.multNTracksPV(); + return collision.multZeqNTracksPV(); + } else { + return 50.f; // Not implemented yet + } + break; + case MultCodes::kMultNTracksPVeta1: // MultNTracksPVeta1 + if constexpr (!isMC) { + return collision.multNTracksPVeta1(); + } else { + return 50.f; // Not implemented yet + } + break; + case MultCodes::kCentralityFT0C: // Centrality FT0C + if constexpr (!isMC) { + return collision.centFT0C(); + } else { + return 50.f; // Not implemented yet + } + break; + case MultCodes::kCentralityFT0M: // Centrality FT0M + return collision.centFT0M(); + break; + default: + LOG(fatal) << "Unknown multiplicity estimator: " << multiplicityEstimator; + return 0.f; + } + } + using GenMCCollisions = soa::Join; + float getMultiplicityMC(const GenMCCollisions::iterator& collision) { return getMultiplicity(collision); } + + template + bool isParticleEnabled() + { + if constexpr (id == PID::Pion || id == Np + PID::Pion) { + return true; + } else if constexpr (id == PID::Kaon || id == Np + PID::Kaon) { + return true; + } + return false; + } + + using RecoMCCollisions = soa::Join; // RD + template + void fillTrackHistogramsMC(TrackType const& track, + ParticleType::iterator const& mcParticle, + RecoMCCollisions::iterator const& collision, + ParticleType const& /*mcParticles*/) + { + if (!isParticleEnabled()) { // Check if the particle is enabled + return; + } + if (!collision.has_mcCollision()) { + return; // Skips processing if no corresponding MC collision is found (rare case!) + } + + const auto& mcCollision = collision.mcCollision_as(); + const float multiplicity = getMultiplicityMC(mcCollision); + + if (mcParticle.pdgCode() != PDGs[i]) { + return; + } + + if (track.eta() < trkselOptions.cfgCutEtaMin || track.eta() > trkselOptions.cfgCutEtaMax) { + return; + } + + if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + return; + } + if (enablePureDCAHistogram) { + float nsigmaTPC = 999.f; + float nsigmaTOF = 999.f; + + if constexpr (i == PID::Pion || i == Np + PID::Pion) { + nsigmaTPC = o2::aod::pidutils::tpcNSigma<2>(track); + nsigmaTOF = o2::aod::pidutils::tofNSigma<2>(track); + } else if constexpr (i == PID::Kaon || i == Np + PID::Kaon) { + nsigmaTPC = o2::aod::pidutils::tpcNSigma<3>(track); + nsigmaTOF = o2::aod::pidutils::tofNSigma<3>(track); + } + + // Filling DCA info with the TPC+TOF PID + // -- FEED DOWN : DCAxy distribution cor prm, str e mat per DCA Pure Sample. NO MULT per ora -- + bool isDCAPureSample = (std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < DcaMaxCombinedSigma); + if (track.pt() <= DcaTrkPtCut) { + isDCAPureSample = (nsigmaTPC < DcaMaxTPCSigma); + } + + if (isDCAPureSample) { + if (!mcParticle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + if (mcParticle.getProcess() == kPDecay) { // Particles from decay + if (enableDCAxyzHistograms) { + hDcaXYZStr[i]->Fill(track.pt(), track.dcaXY(), track.dcaZ()); + } else { + histos.fill(HIST(hdcaxystr[i]), track.pt(), track.dcaXY()); + histos.fill(HIST(hdcazstr[i]), track.pt(), track.dcaZ()); + } + } else { // Particles from the material + if (enableDCAxyzHistograms) { + hDcaXYZMat[i]->Fill(track.pt(), track.dcaXY(), track.dcaZ()); + } else { + histos.fill(HIST(hdcaxymat[i]), track.pt(), track.dcaXY()); + histos.fill(HIST(hdcazmat[i]), track.pt(), track.dcaZ()); + } + } + } else { // Primaries + if (enableDCAxyzHistograms) { + hDcaXYZPrm[i]->Fill(track.pt(), track.dcaXY(), track.dcaZ()); + } else { + histos.fill(HIST(hdcaxyprm[i]), track.pt(), track.dcaXY()); + histos.fill(HIST(hdcazprm[i]), track.pt(), track.dcaZ()); + } + } + } + } else { + if (!mcParticle.isPhysicalPrimary()) { + if (mcParticle.getProcess() == kPDecay) { // particles from decay + histos.fill(HIST(hdcaxystr[i]), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST(hdcaxymat[i]), track.pt(), track.dcaXY()); + } + } else { + histos.fill(HIST(hdcaxyprm[i]), track.pt(), track.dcaXY()); + } + } + + if (!passesDCAxyCut(track)) { // Skipping tracks that don't pass the standard cuts + return; + } + if (!passesDCAzCut(track)) { + return; + } + + // -- Number of MC recostructed tracks after DCAxy cut -- + if (!mcParticle.isPhysicalPrimary()) { // Is not physical primary + if (mcParticle.getProcess() == kPDecay) { // Is from decay + if (includeCentralityMC) { + histos.fill(HIST(hpt_num_str[i]), track.pt(), multiplicity, track.dcaXY()); + } else { + histos.fill(HIST(hpt_num_str[i]), track.pt(), track.dcaXY()); + } + if (track.hasTOF()) { + if (includeCentralityMC) { + histos.fill(HIST(hpt_numtof_str[i]), track.pt(), multiplicity, track.dcaXY()); + } else { + histos.fill(HIST(hpt_numtof_str[i]), track.pt(), track.dcaXY()); + } + } + } else { + if (includeCentralityMC) { + histos.fill(HIST(hpt_num_mat[i]), track.pt(), multiplicity, track.dcaXY()); + if (track.hasTOF()) { + histos.fill(HIST(hpt_numtof_mat[i]), track.pt(), multiplicity, track.dcaXY()); + } + } else { + histos.fill(HIST(hpt_num_mat[i]), track.pt(), track.dcaXY()); + if (track.hasTOF()) { + histos.fill(HIST(hpt_numtof_mat[i]), track.pt(), track.dcaXY()); + } + } + } + } else { // Is physical primary + if (includeCentralityMC) { + histos.fill(HIST(hpt_num_prm[i]), track.pt(), multiplicity, track.dcaXY()); + if (track.hasTOF()) { + histos.fill(HIST(hpt_numtof_prm[i]), track.pt(), multiplicity, track.dcaXY()); + } + } else { + histos.fill(HIST(hpt_num_prm[i]), track.pt(), track.dcaXY()); + if (track.hasTOF()) { + histos.fill(HIST(hpt_numtof_prm[i]), track.pt(), track.dcaXY()); + } + } + } + } + + template + void fillParticleHistogramsMC(const float multiplicity, ParticleType const& mcParticle) + { + if (!isParticleEnabled()) { // Check if the particle is enabled + return; + } + + if (mcParticle.pdgCode() != PDGs[i]) { + return; + } + + if (!mcParticle.isPhysicalPrimary()) { + if (mcParticle.getProcess() == kPDecay) { // Particles from decay + if (includeCentralityMC) { + histos.fill(HIST(hpt_den_str[i]), mcParticle.pt(), multiplicity); + } else { + histos.fill(HIST(hpt_den_str[i]), mcParticle.pt()); + } + } else { + if (includeCentralityMC) { + histos.fill(HIST(hpt_den_mat[i]), mcParticle.pt(), multiplicity); + } else { + histos.fill(HIST(hpt_den_mat[i]), mcParticle.pt()); + } + } + } else { + if (includeCentralityMC) { + histos.fill(HIST(hpt_den_prm[i]), mcParticle.pt(), multiplicity); + } else { + histos.fill(HIST(hpt_den_prm[i]), mcParticle.pt()); + } + } + } + + template + void fillParticleHistogramsMCRecoEvs(ParticleType const& mcParticle, RecoMCCollisions::iterator const& collision) + { + if (!isParticleEnabled()) { // Check if the particle is enabled + return; + } + + if (mcParticle.pdgCode() != PDGs[i]) { + return; + } + + const auto& mcCollision = collision.mcCollision_as(); + const float multiplicity = getMultiplicityMC(mcCollision); + + // Number of particles that pass the final selection + if (mcParticle.isPhysicalPrimary()) { + if (isEventSelected(collision)) { + if (includeCentralityMC) { + histos.fill(HIST(hpt_den_prm_goodev[i]), mcParticle.pt(), multiplicity); + } else { + histos.fill(HIST(hpt_den_prm_goodev[i]), mcParticle.pt()); + } + } + } + } + + template + void fillParticleHistogramsMCGenEvs(ParticleType const& mcParticle, GenMCCollisions::iterator const& mcCollision) + { + + if (!isParticleEnabled()) { // Check if the particle is enabled + return; + } + + if (mcParticle.pdgCode() != PDGs[i]) { + return; + } + + const float multiplicity = getMultiplicityMC(mcCollision); + if (mcParticle.isPhysicalPrimary()) { + // Number of primary particles generated from good events (z<10cm) + histos.fill(HIST(hpt_den_prm_mcgoodev[i]), mcParticle.pt(), multiplicity); + } + } + + //-- Process MC .--- + + Service pdgDB; + + Preslice perMCCol = aod::mcparticle::mcCollisionId; + SliceCache cache; + void processMC(soa::Join const& tracks, + aod::McParticles const& mcParticles, + GenMCCollisions const& mcCollisions, + RecoMCCollisions const& collisions) + { + // Fill number of generated and reconstructed collisions for normalization + histos.fill(HIST("MC/GenRecoCollisions"), 1.f, mcCollisions.size()); + histos.fill(HIST("MC/GenRecoCollisions"), 2.f, collisions.size()); + + for (const auto& track : tracks) { + if (!track.has_collision()) { + if (track.sign() > 0) { + histos.fill(HIST("MC/no_collision/pos"), track.pt()); + } else { + histos.fill(HIST("MC/no_collision/neg"), track.pt()); + } + continue; + } + if (!isEventSelected(track.collision_as())) { + continue; + } + if (!passesCutWoDCA(track)) { + continue; + } + if (!track.has_mcParticle()) { + if (track.sign() > 0) { + histos.fill(HIST("MC/fake/pos"), track.pt()); + } else { + histos.fill(HIST("MC/fake/neg"), track.pt()); + } + continue; + } + const auto& mcParticle = track.mcParticle(); + + static_for<0, 17>([&](auto i) { + fillTrackHistogramsMC(track, mcParticle, track.collision_as(), mcParticles); + }); + } + if (includeCentralityMC) { + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + const auto& mcCollision = collision.mcCollision_as(); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + const float multiplicity = getMultiplicity(collision); + for (const auto& mcParticle : particlesInCollision) { + + if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + continue; + } + static_for<0, 17>([&](auto i) { + fillParticleHistogramsMC(multiplicity, mcParticle); + }); + } + } + } else { + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + continue; + } + + const auto& mcCollision = mcParticle.mcCollision_as(); + const float multiplicity = getMultiplicityMC(mcCollision); + + static_for<0, 17>([&](auto i) { + fillParticleHistogramsMC(multiplicity, mcParticle); + }); + } + } + // Loop on reconstructed collisions + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + const auto& mcCollision = collision.mcCollision_as(); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + if (evselOptions.cfgINELCut.value == EvSelInelGt0Cut) { + if (!o2::pwglf::isINELgt0mc(particlesInCollision, pdgDB)) { + continue; + } + } + + if (isEventSelected(collision)) { + histos.fill(HIST("MC/MultiplicityRecoEv"), getMultiplicityMC(mcCollision)); + } + for (const auto& mcParticle : particlesInCollision) { + if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + continue; + } + static_for<0, 17>([&](auto i) { + fillParticleHistogramsMCRecoEvs(mcParticle, collision); + }); + } + } + + // Loop on generated collisions + for (const auto& mcCollision : mcCollisions) { + if (std::abs(mcCollision.posZ()) > evselOptions.cfgCutVertex) { + continue; + } + histos.fill(HIST("MC/Multiplicity"), getMultiplicityMC(mcCollision)); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + if (evselOptions.cfgINELCut.value == EvSelInelGt0Cut) { + if (!o2::pwglf::isINELgt0mc(particlesInCollision, pdgDB)) { + continue; + } + } + histos.fill(HIST("MC/MultiplicityMCINELgt0"), getMultiplicityMC(mcCollision)); + + for (const auto& mcParticle : particlesInCollision) { + if (std::abs(mcParticle.y()) > trkselOptions.cfgCutY) { + continue; + } + static_for<0, 17>([&](auto i) { + fillParticleHistogramsMCGenEvs(mcParticle, mcCollision); + }); + } + } + } + PROCESS_SWITCH(SpectraTOFLight, processMC, "Process MC", false); + +}; // end of spectra task + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/QC/CMakeLists.txt b/PWGLF/Tasks/QC/CMakeLists.txt index e1618a43d06..8185c1c91e4 100644 --- a/PWGLF/Tasks/QC/CMakeLists.txt +++ b/PWGLF/Tasks/QC/CMakeLists.txt @@ -11,6 +11,7 @@ o2physics_add_dpl_workflow(v0cascades-qa SOURCES v0cascadesqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) @@ -31,11 +32,13 @@ o2physics_add_dpl_workflow(pid-qa o2physics_add_dpl_workflow(strarecostudy SOURCES straRecoStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(kfperformancestudy SOURCES kfPerformanceStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -46,6 +49,7 @@ o2physics_add_dpl_workflow(its-tpc-matching-qa o2physics_add_dpl_workflow(strangeness-tracking-qc SOURCES strangenessTrackingQC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -62,11 +66,13 @@ o2physics_add_dpl_workflow(lfpropstudy o2physics_add_dpl_workflow(tpc-dedx-postcalibration SOURCES tpc_dEdx_postcalibration.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tpc-dedx-qa SOURCES tpc_dedx_qa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -87,21 +93,25 @@ o2physics_add_dpl_workflow(strangenessqcpp o2physics_add_dpl_workflow(strangepidqa SOURCES strangepidqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(stqa SOURCES stqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(kfstrangenessstudy SOURCES kfStrangenessStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(findable-study SOURCES findableStudy.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::DetectorsBase O2Physics::AnalysisCore O2Physics::v0SelectionGroup COMPONENT_NAME Analysis) @@ -112,6 +122,7 @@ o2physics_add_dpl_workflow(mcinelgt0 o2physics_add_dpl_workflow(tracked-cascade-properties SOURCES trackedCascadeProperties.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -122,26 +133,31 @@ o2physics_add_dpl_workflow(mc-particle-predictions o2physics_add_dpl_workflow(strange-derived-qa SOURCES strangederivedqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(str-derived-genqa SOURCES strderivedGenQA.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(its-tpc-matching-vzeros SOURCES lfITSTPCMatchingSecondaryTracksQA.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0assoqa SOURCES v0assoqa.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2::DCAFitter KFParticle::KFParticle O2Physics::TPCDriftManager COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(systematics-mapping SOURCES systematicsMapping.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/QC/efficiencyQA.cxx b/PWGLF/Tasks/QC/efficiencyQA.cxx index c02606e3de1..822b6bb24b6 100644 --- a/PWGLF/Tasks/QC/efficiencyQA.cxx +++ b/PWGLF/Tasks/QC/efficiencyQA.cxx @@ -322,7 +322,7 @@ struct efficiencyQA { } template - void fillHistTrack(T const& track, std::shared_ptr hist, float const& y, float const& z = 1) + void fillHistTrack(T const& track, const std::shared_ptr& hist, float const& y, float const& z = 1) { bool itsAccept = !(track.itsChi2NCl() > 36. || track.itsNCls() < 4); bool tpcAccept = !(track.tpcCrossedRowsOverFindableCls() < 0.8 || track.tpcNClsCrossedRows() < 70 || track.tpcChi2NCl() > 4. || track.tpcNClsFound() < 90); @@ -347,7 +347,7 @@ struct efficiencyQA { } template - void fillHistTrack(T const& track, std::shared_ptr hist, float const& y, float const& z = 1, float const& t = 1) + void fillHistTrack(T const& track, const std::shared_ptr& hist, float const& y, float const& z = 1, float const& t = 1) { bool itsAccept = !(track.itsChi2NCl() > 36. || track.itsNCls() < 4); bool tpcAccept = !(track.tpcCrossedRowsOverFindableCls() < 0.8 || track.tpcNClsCrossedRows() < 70 || track.tpcChi2NCl() > 4. || track.tpcNClsFound() < 90); diff --git a/PWGLF/Tasks/QC/hypertriton3bodyQa.cxx b/PWGLF/Tasks/QC/hypertriton3bodyQa.cxx index e628386ebcb..65ee66c4563 100644 --- a/PWGLF/Tasks/QC/hypertriton3bodyQa.cxx +++ b/PWGLF/Tasks/QC/hypertriton3bodyQa.cxx @@ -18,6 +18,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/MetadataHelper.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTOF.h" @@ -34,7 +35,6 @@ #include #include #include -#include #include #include diff --git a/PWGLF/Tasks/QC/mcParticlePrediction.cxx b/PWGLF/Tasks/QC/mcParticlePrediction.cxx index 9688e0e5906..d13b874da14 100644 --- a/PWGLF/Tasks/QC/mcParticlePrediction.cxx +++ b/PWGLF/Tasks/QC/mcParticlePrediction.cxx @@ -55,11 +55,70 @@ using namespace o2::framework; using namespace o2::pwglf; // Particles -static const std::vector parameterNames{"Enable"}; -static constexpr int nParameters = 1; -static const int defaultParticles[PIDExtended::NIDsTot][nParameters]{{0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {1}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}}; -bool enabledParticlesArray[PIDExtended::NIDsTot]; - +static constexpr std::array, PIDExtended::NIDsTot> + defaultParticles{{{0, 1}, // Electron + {0, 1}, // Muon + {0, 1}, // Pion + {0, 1}, // Kaon + {0, 1}, // Proton + {0, 1}, // Deuteron + {0, 1}, // Triton + {0, 1}, // Helium3 + {0, 1}, // Alpha + {0, 1}, // PI0 + {0, 1}, // Photon + {1, 1}, // K0 + {0, 1}, // Lambda + {0, 1}, // HyperTriton + {0, 1}, // Hyperhydrog4 + {0, 1}, // XiMinus + {0, 1}, // OmegaMinus + {0, 1}, // HyperHelium4 + {0, 1}, // HyperHelium5 + {0, 1}, // Positron + {0, 1}, // MuonPlus + {0, 1}, // PionMinus + {0, 1}, // KaonMinus + {0, 1}, // AntiProton + {0, 1}, // AntiDeuteron + {0, 1}, // AntiTriton + {0, 1}, // AntiHelium3 + {0, 1}, // AntiAlpha + {0, 1}, // AntiLambda + {0, 1}, // AntiHyperTriton + {0, 1}, // AntiHyperhydrog4 + {0, 1}, // XiPlus + {0, 1}, // OmegaPlus + {0, 1}, // AntiHyperHelium4 + {0, 1}, // AntiHyperHelium5 + {0, 1}, // Neutron + {0, 1}, // AntiNeutron + {0, 0}, // Phi + {0, 1}, // BZero + {0, 1}, // BPlus + {0, 1}, // BS + {0, 1}, // D0 + {0, 1}, // DPlus + {0, 1}, // DS + {0, 1}, // DStar + {0, 1}, // ChiC1 + {0, 1}, // JPsi + {0, 1}, // LambdaB0 + {0, 1}, // LambdaCPlus + {0, 1}, // OmegaC0 + {0, 1}, // SigmaC0 + {0, 1}, // SigmaCPlusPlus + {0, 1}, // X3872 + {0, 1}, // Xi0 + {0, 1}, // XiB0 + {0, 1}, // XiCCPlusPlus + {0, 1}, // XiCPlus + {0, 1}, // XiC0 + {0, 0}, // Kstar + {0, 0}, // KstarPM + {0, 1}, // Kshort + {0, 0}, // Xi1530 + {0, 0}}}; // Lambda1520 // Estimators struct Estimators { typedef int estID; @@ -85,26 +144,26 @@ struct Estimators { static constexpr estID ImpactParameter = 19; // (Run2 V0M) static constexpr estID nEstimators = 20; - static constexpr const char* estimatorNames[nEstimators] = {"FT0A", - "FT0C", - "FT0AC", - "FV0A", - "FDDA", - "FDDC", - "FDDAC", - "ZNA", - "ZNC", - "ZEM1", - "ZEM2", - "ZPA", - "ZPC", - "ITSIB", - "ETA05", - "ETA08", - "V0A", - "V0C", - "V0AC", - "ImpactParameter"}; + static constexpr std::array estimatorNames{"FT0A", + "FT0C", + "FT0AC", + "FV0A", + "FDDA", + "FDDC", + "FDDAC", + "ZNA", + "ZNC", + "ZEM1", + "ZEM2", + "ZPA", + "ZPC", + "ITSIB", + "ETA05", + "ETA08", + "V0A", + "V0C", + "V0AC", + "ImpactParameter"}; static std::vector arrayNames() { static std::vector names; @@ -117,69 +176,90 @@ struct Estimators { return names; } }; -bool enabledEstimatorsArray[Estimators::nEstimators]; -static const int defaultEstimators[Estimators::nEstimators][nParameters]{{0}, // FT0A - {0}, // FT0C - {1}, // FT0AC - {0}, // FV0A - {0}, // FDDA - {0}, // FDDC - {0}, // FDDAC - {0}, // ZNA - {0}, // ZNC - {0}, // ZEM1 - {0}, // ZEM2 - {0}, // ZPA - {0}, // ZPC - {0}, // ITSIB - {0}, // ETA05 - {0}, // ETA08 - {0}, // V0A (Run2) - {0}, // V0C (Run2) - {0}, // V0AC (Run2 V0M) - {0}}; // ImpactParamter - -// Histograms -std::array, Estimators::nEstimators> hestimators; -std::array, Estimators::nEstimators> hestimatorsVsITS; -std::array, Estimators::nEstimators> hestimatorsVsETA05; -std::array, Estimators::nEstimators> hestimatorsVsETA08; -std::array, Estimators::nEstimators> hestimatorsVsImpactParameter; -std::array, Estimators::nEstimators> hestimatorsRecoEvGenVsReco; -std::array, Estimators::nEstimators> hestimatorsRecoEvGenVsReco_BCMC; -std::array, Estimators::nEstimators> hestimatorsRecoEvGenVsRecoITS; -std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsITS; -std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsRecoITS; -std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsRecoITS_BCMC; -std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsFT0A; -std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsBCId; -std::array, Estimators::nEstimators> hestimatorsRecoEvVsBCId; -std::array, Estimators::nEstimators> hvertexPosZ; -std::array, PIDExtended::NIDsTot>, Estimators::nEstimators> hpt; -std::array, PIDExtended::NIDsTot>, Estimators::nEstimators> hyield; - -struct mcParticlePrediction { +static constexpr std::array, Estimators::nEstimators> + defaultEstimators{{{0}, // FT0A + {0}, // FT0C + {1}, // FT0AC + {0}, // FV0A + {0}, // FDDA + {0}, // FDDC + {0}, // FDDAC + {0}, // ZNA + {0}, // ZNC + {0}, // ZEM1 + {0}, // ZEM2 + {0}, // ZPA + {0}, // ZPC + {0}, // ITSIB + {0}, // ETA05 + {0}, // ETA08 + {0}, // V0A (Run2) + {0}, // V0C (Run2) + {0}, // V0AC (Run2 V0M) + {0}}}; // ImpactParamter + +struct McParticlePrediction { // Histograms HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry histosRecoEvs{"HistosRecoEvs", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry histosYield{"HistosYield", {}, OutputObjHandlingPolicy::AnalysisObject}; HistogramRegistry histosPt{"HistosPt", {}, OutputObjHandlingPolicy::AnalysisObject}; - ConfigurableAxis binsEta{"binsEta", {100, -20, 20}, "Binning of the Eta axis"}; - ConfigurableAxis binsVxy{"binsVxy", {100, -10, 10}, "Binning of the production vertex (x and y) axis"}; - ConfigurableAxis binsVz{"binsVz", {100, -10, 10}, "Binning of the production vertex (z) axis"}; - ConfigurableAxis binsPt{"binsPt", {100, 0, 10}, "Binning of the Pt axis"}; - ConfigurableAxis binsImpactParameter{"binsImpactParameter", {400, 0.0, 20.0}, "Binning of the impact parameter axis"}; - ConfigurableAxis binsMultiplicity{"binsMultiplicity", {300, -0.5, 299.5}, "Binning of the Multiplicity axis"}; - ConfigurableAxis binsMultiplicityReco{"binsMultiplicityReco", {1000, -0.5, -0.5 + 10000}, "Binning of the Multiplicity axis"}; + HistogramRegistry histosEta{"HistosEta", {}, OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry histosY{"HistosY", {}, OutputObjHandlingPolicy::AnalysisObject}; + + std::array enabledParticlesArray{false}; + std::array selectPrimariesArray{false}; + std::array enabledEstimatorsArray{false}; + std::array, Estimators::nEstimators> hestimators; + std::array, Estimators::nEstimators> hestimatorsVsITS; + std::array, Estimators::nEstimators> hestimatorsVsETA05; + std::array, Estimators::nEstimators> hestimatorsVsETA08; + std::array, Estimators::nEstimators> hestimatorsVsImpactParameter; + std::array, Estimators::nEstimators> hestimatorsRecoEvGenVsReco; + std::array, Estimators::nEstimators> hestimatorsRecoEvGenVsReco_BCMC; + std::array, Estimators::nEstimators> hestimatorsRecoEvGenVsRecoITS; + std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsITS; + std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsRecoITS; + std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsRecoITS_BCMC; + std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsFT0A; + std::array, Estimators::nEstimators> hestimatorsRecoEvRecoVsBCId; + std::array, Estimators::nEstimators> hestimatorsRecoEvVsBCId; + std::array, Estimators::nEstimators> hvertexPosZ; + std::array, PIDExtended::NIDsTot>, Estimators::nEstimators> hpt; + std::array, PIDExtended::NIDsTot>, Estimators::nEstimators> hy; + std::array, PIDExtended::NIDsTot>, Estimators::nEstimators> heta; + std::array, PIDExtended::NIDsTot>, Estimators::nEstimators> hyield; + + struct : ConfigurableGroup { + std::string prefix = "Binning"; // JSON group name + ConfigurableAxis binsEta{"binsEta", {100, -20, 20}, "Binning of the Eta axis"}; + ConfigurableAxis binsY{"binsY", {100, -20, 20}, "Binning of the Y axis"}; + ConfigurableAxis binsVxy{"binsVxy", {100, -10, 10}, "Binning of the production vertex (x and y) axis"}; + ConfigurableAxis binsVz{"binsVz", {100, -10, 10}, "Binning of the production vertex (z) axis"}; + ConfigurableAxis binsPt{"binsPt", {200, 0, 20}, "Binning of the Pt axis"}; + ConfigurableAxis binsImpactParameter{"binsImpactParameter", {400, 0.0, 20.0}, "Binning of the impact parameter axis"}; + ConfigurableAxis binsMultiplicity{"binsMultiplicity", {300, -0.5, 299.5}, "Binning of the Multiplicity axis"}; + ConfigurableAxis binsMultiplicityReco{"binsMultiplicityReco", {1000, -0.5, -0.5 + 10000}, "Binning of the Multiplicity axis"}; + } cfgBinning; + + struct : ConfigurableGroup { + std::string prefix = "Predictions type"; // JSON group name + Configurable enablePt{"enablePt", true, "Produce prediction for pT"}; + Configurable enableY{"enableY", true, "Produce prediction for rapidity"}; + Configurable enableEta{"enableEta", true, "Produce prediction for eta"}; + } cfgPrediction; + Configurable> enabledSpecies{"enabledSpecies", - {defaultParticles[0], PIDExtended::NIDsTot, nParameters, PIDExtended::arrayNames(), parameterNames}, + {defaultParticles.front().data(), PIDExtended::NIDsTot, defaultParticles[0].size(), PIDExtended::arrayNames(), {"Enable", "SelectPrimaries"}}, "Particles enabled"}; Configurable> enabledEstimators{"enabledEstimators", - {defaultEstimators[0], Estimators::nEstimators, nParameters, Estimators::arrayNames(), parameterNames}, + {defaultEstimators.front().data(), Estimators::nEstimators, defaultEstimators[0].size(), Estimators::arrayNames(), {"Enable"}}, "Estimators enabled"}; Configurable selectInelGt0{"selectInelGt0", true, "Select only inelastic events"}; - Configurable selectPrimaries{"selectPrimaries", true, "Select only primary particles"}; + Configurable selectPrimariesForMultiplicity{"selectPrimariesForMultiplicity", true, "Select only primary particles for multiplicity computation"}; + Configurable rapidityCut{"rapidityCut", 0.5, "Select only particles within |y| < cut for the pT and yield plots"}; + Configurable ptCut{"ptCut", 0.01, "Select only particles within |pt| > cut for the eta and y plots"}; Configurable requireCoincidenceEstimators{"requireCoincidenceEstimators", false, "Asks for a coincidence when two estimators are used"}; Configurable discardkIsGoodZvtxFT0vsPV{"discardkIsGoodZvtxFT0vsPV", false, "Select only collisions with matching BC and MC BC"}; Configurable discardMismatchedBCs{"discardMismatchedBCs", false, "Select only collisions with matching BC and MC BC"}; @@ -194,23 +274,25 @@ struct mcParticlePrediction { Configurable enableVsEta05Histograms{"enableVsEta05Histograms", true, "Enables the correlation between ETA05 and other estimators"}; Configurable enableVsEta08Histograms{"enableVsEta08Histograms", true, "Enables the correlation between ETA08 and other estimators"}; Configurable enableVsImpactParameterHistograms{"enableVsImpactParameterHistograms", true, "Enables the correlation between impact parameter and other estimators"}; + Configurable chargetolerance{"chargetolerance", 1e-3, "Tolerance to consider a particle as charged based on its charge"}; - Service pdgDB; - o2::pwglf::ParticleCounter mCounter; + Service pdgDB{}; + o2::pwglf::ParticleCounter mCounter{}; void init(o2::framework::InitContext&) { mCounter.mPdgDatabase = pdgDB.service; - mCounter.mSelectPrimaries = selectPrimaries.value; - const AxisSpec axisEta{binsEta, "#eta"}; - const AxisSpec axisVx{binsVxy, "Vx"}; - const AxisSpec axisVy{binsVxy, "Vy"}; - const AxisSpec axisVz{binsVz, "Vz"}; - const AxisSpec axisPt{binsPt, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec axisImpactParameter{binsImpactParameter, "Impact parameter (fm)"}; - const AxisSpec axisMultiplicity{binsMultiplicity, "Multiplicity (undefined)"}; - const AxisSpec axisMultiplicityReco{binsMultiplicityReco, "Multiplicity Reco. (undefined)"}; - const AxisSpec axisMultiplicityRecoITS{binsMultiplicityReco, "Multiplicity Reco. ITSIB"}; + mCounter.mSelectPrimaries = selectPrimariesForMultiplicity.value; + const AxisSpec axisEta{cfgBinning.binsEta, "#eta"}; + const AxisSpec axisRapidity{cfgBinning.binsY, "#it{y}"}; + const AxisSpec axisVx{cfgBinning.binsVxy, "Vx"}; + const AxisSpec axisVy{cfgBinning.binsVxy, "Vy"}; + const AxisSpec axisVz{cfgBinning.binsVz, "Vz"}; + const AxisSpec axisPt{cfgBinning.binsPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec axisImpactParameter{cfgBinning.binsImpactParameter, "Impact parameter (fm)"}; + const AxisSpec axisMultiplicity{cfgBinning.binsMultiplicity, "Multiplicity (undefined)"}; + const AxisSpec axisMultiplicityReco{cfgBinning.binsMultiplicityReco, "Multiplicity Reco. (undefined)"}; + const AxisSpec axisMultiplicityRecoITS{cfgBinning.binsMultiplicityReco, "Multiplicity Reco. ITSIB"}; const AxisSpec axisMultiplicityGenV0s{100, 0, 100, "K0s gen"}; const AxisSpec axisMultiplicityRecoV0s{20, 0, 20, "K0s reco"}; const AxisSpec axisBCID{o2::constants::lhc::LHCMaxBunches, -0.5, -0.5 + o2::constants::lhc::LHCMaxBunches, "BC ID in orbit"}; @@ -221,50 +303,65 @@ struct mcParticlePrediction { h->GetXaxis()->SetBinLabel(1, "Read"); h->GetXaxis()->SetBinLabel(2, "INELgt0"); h->GetXaxis()->SetBinLabel(3, "|Z|<10"); - h = histos.add("collisions/reconstructed", "collisions", kTH1D, {{20, -0.5, 19.5}}); - h->GetXaxis()->SetBinLabel(1, "Read"); - h->GetXaxis()->SetBinLabel(2, "has_mcCollision"); - h->GetXaxis()->SetBinLabel(3, "sel8"); - h->GetXaxis()->SetBinLabel(4, "kIsBBT0A"); - h->GetXaxis()->SetBinLabel(5, "kIsBBT0C"); - h->GetXaxis()->SetBinLabel(6, "collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))"); - h->GetXaxis()->SetBinLabel(7, "globalBC == MC globalBC"); - h->GetXaxis()->SetBinLabel(8, "found globalBC == MC globalBC"); - h->GetXaxis()->SetBinLabel(9, "isINELgt0mc"); - h->GetXaxis()->SetBinLabel(10, "VTXz"); - h->GetXaxis()->SetBinLabel(11, "collisionTimeRes"); - h->GetXaxis()->SetBinLabel(11, "collisionTimeRes"); - h->GetXaxis()->SetBinLabel(12, "kIsGoodZvtxFT0vsPV"); - h->GetXaxis()->SetBinLabel(13, "kIsVertexITSTPC"); - h->GetXaxis()->SetBinLabel(14, "kIsVertexTOFmatched"); - h->GetXaxis()->SetBinLabel(15, "kIsVertexTRDmatched"); - - histos.add("collisions/Reco/BCvsMCBC", "BC vs MC BC", kTH2D, {axisBCID, axisBCIDMC}); - histos.add("collisions/Reco/FoundBCvsMCBC", "Found BC vs MC BC", kTH2D, {axisBCID, axisBCIDMC})->GetXaxis()->SetTitle("Found BC ID in orbit"); - histos.add("collisions/Reco/FoundBCvsBC", "Found BC vs MC BC", kTH2D, {axisBCID, axisBCID})->GetXaxis()->SetTitle("Found BC ID in orbit"); - histos.add("collisions/Reco/collisionTime", "Collision Time", kTH1D, {{1000, -20, 20, "collisionTime"}}); - histos.add("collisions/Reco/collisionTimeRes", "Collision Time Res", kTH1D, {{1600, 0, 1600, "collisionTimeRes (ns)"}}); - histos.add("collisions/Reco/bcMinusfoundBc", "bcMinusfoundBc", kTH1D, {{1600, -1000, 1000, "bc - foundBc (ns)"}}); - histos.add("collisions/Reco/bcMinusfoundBcRatio", "bcMinusfoundBcRatio", kTH1D, {{1600, -40, 40, "(bc - foundBc)/collisionTimeRes"}}); - histos.add("collisions/Reco/bcMinusMcBcRatio", "bcMinusMcBcRatio", kTH1D, {{1600, -40, 40, "(bc - mcBc)/collisionTimeRes"}}); - histos.add("collisions/Reco/foundbcMinusMcBcRatio", "foundbcMinusMcBcRatio", kTH1D, {{1600, -40, 40, "(foundBc-mcBc)/collisionTimeRes"}}); - histos.add("collisions/Reco/FT0A", "FT0A", kTH1D, {axisFT0})->GetXaxis()->SetTitle("Coll time FT0A (ps)"); - histos.add("collisions/Reco/FT0C", "FT0C", kTH1D, {axisFT0})->GetXaxis()->SetTitle("Coll time FT0C (ps)"); - histos.add("collisions/Reco/FT0AC", "FT0AC", kTH1D, {axisFT0})->GetXaxis()->SetTitle("Coll time FT0AC (ps)"); + if (doprocessReco) { + h = histos.add("collisions/reconstructed", "collisions", kTH1D, {{20, -0.5, 19.5}}); + h->GetXaxis()->SetBinLabel(1, "Read"); + h->GetXaxis()->SetBinLabel(2, "has_mcCollision"); + h->GetXaxis()->SetBinLabel(3, "sel8"); + h->GetXaxis()->SetBinLabel(4, "kIsBBT0A"); + h->GetXaxis()->SetBinLabel(5, "kIsBBT0C"); + h->GetXaxis()->SetBinLabel(6, "collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))"); + h->GetXaxis()->SetBinLabel(7, "globalBC == MC globalBC"); + h->GetXaxis()->SetBinLabel(8, "found globalBC == MC globalBC"); + h->GetXaxis()->SetBinLabel(9, "isINELgt0mc"); + h->GetXaxis()->SetBinLabel(10, "VTXz"); + h->GetXaxis()->SetBinLabel(11, "collisionTimeRes"); + h->GetXaxis()->SetBinLabel(11, "collisionTimeRes"); + h->GetXaxis()->SetBinLabel(12, "kIsGoodZvtxFT0vsPV"); + h->GetXaxis()->SetBinLabel(13, "kIsVertexITSTPC"); + h->GetXaxis()->SetBinLabel(14, "kIsVertexTOFmatched"); + h->GetXaxis()->SetBinLabel(15, "kIsVertexTRDmatched"); + + histos.add("collisions/Reco/BCvsMCBC", "BC vs MC BC", kTH2D, {axisBCID, axisBCIDMC}); + histos.add("collisions/Reco/FoundBCvsMCBC", "Found BC vs MC BC", kTH2D, {axisBCID, axisBCIDMC})->GetXaxis()->SetTitle("Found BC ID in orbit"); + histos.add("collisions/Reco/FoundBCvsBC", "Found BC vs MC BC", kTH2D, {axisBCID, axisBCID})->GetXaxis()->SetTitle("Found BC ID in orbit"); + histos.add("collisions/Reco/collisionTime", "Collision Time", kTH1D, {{1000, -20, 20, "collisionTime"}}); + histos.add("collisions/Reco/collisionTimeRes", "Collision Time Res", kTH1D, {{1600, 0, 1600, "collisionTimeRes (ns)"}}); + histos.add("collisions/Reco/bcMinusfoundBc", "bcMinusfoundBc", kTH1D, {{1600, -1000, 1000, "bc - foundBc (ns)"}}); + histos.add("collisions/Reco/bcMinusfoundBcRatio", "bcMinusfoundBcRatio", kTH1D, {{1600, -40, 40, "(bc - foundBc)/collisionTimeRes"}}); + histos.add("collisions/Reco/bcMinusMcBcRatio", "bcMinusMcBcRatio", kTH1D, {{1600, -40, 40, "(bc - mcBc)/collisionTimeRes"}}); + histos.add("collisions/Reco/foundbcMinusMcBcRatio", "foundbcMinusMcBcRatio", kTH1D, {{1600, -40, 40, "(foundBc-mcBc)/collisionTimeRes"}}); + histos.add("collisions/Reco/FT0A", "FT0A", kTH1D, {axisFT0})->GetXaxis()->SetTitle("Coll time FT0A (ps)"); + histos.add("collisions/Reco/FT0C", "FT0C", kTH1D, {axisFT0})->GetXaxis()->SetTitle("Coll time FT0C (ps)"); + histos.add("collisions/Reco/FT0AC", "FT0AC", kTH1D, {axisFT0})->GetXaxis()->SetTitle("Coll time FT0AC (ps)"); + } + histos.add("particles/eta/charged", "eta", kTH1D, {axisEta}); histos.add("particles/eta/neutral", "eta", kTH1D, {axisEta}); - histos.add("particles/vtx/x", "Vx", kTH1D, {axisVx}); - histos.add("particles/vtx/y", "Vy", kTH1D, {axisVy}); - histos.add("particles/vtx/z", "Vz", kTH1D, {axisVz}); - histos.add("particles/FromCollVsFromMCColl", "FromCollVsFromMCColl", kTH2D, {{binsMultiplicity, "PV contributor particles (good bc)"}, {binsMultiplicityReco, "Particles in MC collision"}}); - histos.add("particles/FromCollVsFromMCCollBad", "FromCollVsFromMCCollBad", kTH2D, {{binsMultiplicity, "PV contributor particles (bad bc)"}, {binsMultiplicityReco, "Particles in MC collision"}}); - histos.add("particles/FromCollVsFromCollBad", "FromCollVsFromCollBad", kTH2D, {{binsMultiplicity, "PV contributor particles (good bc)"}, {binsMultiplicity, "PV contributor particles (bad bc)"}}); - histos.add("particles/FromCollBadOverFromCollVsVsFromMCColl", "FromCollBadOverFromCollVsVsFromMCColl", kTH2D, {{100, 0, 2, "bad/good"}, {binsMultiplicityReco, "Particles in MC collision"}}); - histos.add("V0s/V0RecovsPV", "V0s Reco + Ass vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); - histos.add("V0s/V0RecoAssvsPV", "V0s Reco + Ass vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); - histos.add("V0s/V0AssvsPV", "V0s Ass vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityGenV0s}); - histos.add("V0s/V0RecoAssvsPV_TOFOneLeg", "V0s Reco + Ass + TOF 1 Leg vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); - histos.add("V0s/V0RecoAssvsPV_TOFTwoLegs", "V0s Reco + Ass + TOF 2 Legs vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); + histos.add("particles/eta/undefined", "eta", kTH1D, {axisEta}); + + histos.add("particles/y/charged", "y", kTH1D, {axisRapidity}); + histos.add("particles/y/neutral", "y", kTH1D, {axisRapidity}); + histos.add("particles/y/undefined", "y", kTH1D, {axisRapidity}); + + histos.add("particles/vtx/x", "V_{x}", kTH1D, {axisVx}); + histos.add("particles/vtx/y", "V_{y}", kTH1D, {axisVy}); + histos.add("particles/vtx/z", "V_{z}", kTH1D, {axisVz}); + histos.add("particles/vtx/deltax", "V_{x} - VTX_{x}", kTH1D, {axisVx}); + histos.add("particles/vtx/deltay", "V_{y} - VTX_{y}", kTH1D, {axisVy}); + histos.add("particles/vtx/deltaz", "V_{z} - VTX_{z}", kTH1D, {axisVz}); + histos.add("particles/FromCollVsFromMCColl", "FromCollVsFromMCColl", kTH2D, {{cfgBinning.binsMultiplicity, "PV contributor particles (good bc)"}, {cfgBinning.binsMultiplicityReco, "Particles in MC collision"}}); + histos.add("particles/FromCollVsFromMCCollBad", "FromCollVsFromMCCollBad", kTH2D, {{cfgBinning.binsMultiplicity, "PV contributor particles (bad bc)"}, {cfgBinning.binsMultiplicityReco, "Particles in MC collision"}}); + histos.add("particles/FromCollVsFromCollBad", "FromCollVsFromCollBad", kTH2D, {{cfgBinning.binsMultiplicity, "PV contributor particles (good bc)"}, {cfgBinning.binsMultiplicity, "PV contributor particles (bad bc)"}}); + histos.add("particles/FromCollBadOverFromCollVsVsFromMCColl", "FromCollBadOverFromCollVsVsFromMCColl", kTH2D, {{100, 0, 2, "bad/good"}, {cfgBinning.binsMultiplicityReco, "Particles in MC collision"}}); + + if (doprocessReco) { + histos.add("V0s/V0RecovsPV", "V0s Reco + Ass vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); + histos.add("V0s/V0RecoAssvsPV", "V0s Reco + Ass vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); + histos.add("V0s/V0AssvsPV", "V0s Ass vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityGenV0s}); + histos.add("V0s/V0RecoAssvsPV_TOFOneLeg", "V0s Reco + Ass + TOF 1 Leg vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); + histos.add("V0s/V0RecoAssvsPV_TOFTwoLegs", "V0s Reco + Ass + TOF 2 Legs vs PV", kTH2D, {axisMultiplicityRecoITS, axisMultiplicityRecoV0s}); + } for (int i = 0; i < Estimators::nEstimators; i++) { if (enabledEstimators->get(Estimators::estimatorNames[i], "Enable") != 1) { @@ -279,6 +376,8 @@ struct mcParticlePrediction { for (int i = 0; i < PIDExtended::NIDsTot; i++) { h->GetXaxis()->SetBinLabel(i + 1, PIDExtended::getName(i)); } + histos.addClone("particles/yields", "particles/idBeforePrimarySelection"); + histos.addClone("particles/yields", "particles/idAfterPrimarySelection"); for (int i = 0; i < Estimators::nEstimators; i++) { if (!enabledEstimatorsArray[i]) { @@ -359,6 +458,7 @@ struct mcParticlePrediction { enabledParticlesArray[i] = false; continue; } + selectPrimariesArray[i] = (enabledSpecies->get(PIDExtended::getName(i), "SelectPrimaries") == 1); LOG(info) << "Enabling particle " << i << " " << PIDExtended::getName(i); enabledParticlesArray[i] = true; for (int j = 0; j < Estimators::nEstimators; j++) { @@ -370,22 +470,34 @@ struct mcParticlePrediction { axisThisEstimator = axisImpactParameter; } const char* name = Estimators::estimatorNames[j]; - hpt[j][i] = histosPt.add(Form("prediction/pt/%s/%s", name, PIDExtended::getName(i)), PIDExtended::getName(i), kTH2D, {axisPt, axisThisEstimator}); - hpt[j][i]->GetYaxis()->SetTitle(Form("Multiplicity %s", name)); - + if (cfgPrediction.enablePt) { + hpt[j][i] = histosPt.add(Form("prediction/pt/%s/%s", name, PIDExtended::getName(i)), PIDExtended::getName(i), kTH2D, {axisPt, axisThisEstimator}); + hpt[j][i]->GetYaxis()->SetTitle(Form("Multiplicity %s", name)); + } + if (cfgPrediction.enableEta) { + heta[j][i] = histosEta.add(Form("prediction/eta/%s/%s", name, PIDExtended::getName(i)), PIDExtended::getName(i), kTH2D, {axisEta, axisThisEstimator}); + heta[j][i]->GetYaxis()->SetTitle(Form("Multiplicity %s", name)); + } + if (cfgPrediction.enableY) { + hy[j][i] = histosY.add(Form("prediction/y/%s/%s", name, PIDExtended::getName(i)), PIDExtended::getName(i), kTH2D, {axisRapidity, axisThisEstimator}); + hy[j][i]->GetYaxis()->SetTitle(Form("Multiplicity %s", name)); + } hyield[j][i] = histosYield.add(Form("prediction/yield/%s/%s", name, PIDExtended::getName(i)), PIDExtended::getName(i), kTH1D, {axisThisEstimator}); hyield[j][i]->GetYaxis()->SetTitle(Form("Multiplicity %s", name)); + hyield[j][i]->GetYaxis()->SetTitle(Form("Multiplicity %s in |y| < %.2f", PIDExtended::getName(i), rapidityCut.value)); } } histos.print(); histosRecoEvs.print(); histosPt.print(); + histosEta.print(); + histosY.print(); histosYield.print(); } std::array genMult(const auto& mcCollision, const auto& mcParticles) { - std::array nMult; + std::array nMult = {0}; if (enabledEstimatorsArray[Estimators::FT0A] || enabledEstimatorsArray[Estimators::FT0AC]) { nMult[Estimators::FT0A] = mCounter.countFT0A(mcParticles); } @@ -419,6 +531,22 @@ struct mcParticlePrediction { if (enabledEstimatorsArray[Estimators::ZNC]) { nMult[Estimators::ZNC] = mCounter.countZNC(mcParticles); } + if (enabledEstimatorsArray[Estimators::ZEM1]) { + LOG(warn) << "Estimator ZEM1 is not implemented yet"; + nMult[Estimators::ZEM1] = 0; + } + if (enabledEstimatorsArray[Estimators::ZEM2]) { + LOG(warn) << "Estimator ZEM2 is not implemented yet"; + nMult[Estimators::ZEM2] = 0; + } + if (enabledEstimatorsArray[Estimators::ZPA]) { + LOG(warn) << "Estimator ZPA is not implemented yet"; + nMult[Estimators::ZPA] = 0; + } + if (enabledEstimatorsArray[Estimators::ZPC]) { + LOG(warn) << "Estimator ZPC is not implemented yet"; + nMult[Estimators::ZPC] = 0; + } if (enabledEstimatorsArray[Estimators::ITSIB] || enableVsITSHistograms) { nMult[Estimators::ITSIB] = mCounter.countITSIB(mcParticles); } @@ -455,7 +583,7 @@ struct mcParticlePrediction { } histos.fill(HIST("collisions/generated"), 1); - if (std::abs(mcCollision.posZ()) > 10.f) { + if (std::abs(mcCollision.posZ()) > posZCut.value) { return; } histos.fill(HIST("collisions/generated"), 2); @@ -493,33 +621,60 @@ struct mcParticlePrediction { continue; } - if (!particle.isPhysicalPrimary()) { + // Count ids before applying the primary selection + histos.fill(HIST("particles/idBeforePrimarySelection"), id); + if (selectPrimariesArray[id] && !particle.isPhysicalPrimary()) { continue; } + // Count ids surviving the primary selection + histos.fill(HIST("particles/idAfterPrimarySelection"), id); - TParticlePDG* p = pdgDB->GetParticle(particle.pdgCode()); + const TParticlePDG* p = pdgDB->GetParticle(particle.pdgCode()); if (p) { - if (std::abs(p->Charge()) > 1e-3) { + if (std::abs(p->Charge()) > chargetolerance) { histos.fill(HIST("particles/eta/charged"), particle.eta()); + histos.fill(HIST("particles/y/charged"), particle.y()); } else { histos.fill(HIST("particles/eta/neutral"), particle.eta()); + histos.fill(HIST("particles/y/neutral"), particle.y()); } + } else { + histos.fill(HIST("particles/eta/undefined"), particle.eta()); + histos.fill(HIST("particles/y/undefined"), particle.y()); } - if (std::abs(particle.y()) > 0.5) { + if (std::abs(particle.pt()) > ptCut.value) { // Fill the eta and rapidity histograms only for particles above the pt cut + for (int i = 0; i < Estimators::nEstimators; i++) { + if (!enabledEstimatorsArray[i]) { + continue; + } + if (heta[i][id]) { + heta[i][id]->Fill(particle.eta(), nMult[i]); + } + if (hy[i][id]) { + hy[i][id]->Fill(particle.y(), nMult[i]); + } + } + } + if (std::abs(particle.y()) >= rapidityCut) { continue; } histos.fill(HIST("particles/vtx/x"), particle.vx()); histos.fill(HIST("particles/vtx/y"), particle.vy()); - histos.fill(HIST("particles/vtx/z"), particle.vz() - mcCollision.posZ()); + histos.fill(HIST("particles/vtx/z"), particle.vz()); + histos.fill(HIST("particles/vtx/deltax"), particle.vx() - mcCollision.posX()); + histos.fill(HIST("particles/vtx/deltay"), particle.vy() - mcCollision.posY()); + histos.fill(HIST("particles/vtx/deltaz"), particle.vz() - mcCollision.posZ()); histos.fill(HIST("particles/yields"), id); for (int i = 0; i < Estimators::nEstimators; i++) { if (!enabledEstimatorsArray[i]) { continue; } - hpt[i][id]->Fill(particle.pt(), nMult[i]); + if (hpt[i][id]) { + hpt[i][id]->Fill(particle.pt(), nMult[i]); + } hyield[i][id]->Fill(nMult[i]); } } @@ -657,7 +812,7 @@ struct mcParticlePrediction { const std::array& nMult = genMult(mcCollision, particlesInCollision); - float nMultReco[Estimators::nEstimators]; + std::array nMultReco = {0}; nMultReco[Estimators::FT0A] = collision.multFT0A(); nMultReco[Estimators::FT0C] = collision.multFT0C(); nMultReco[Estimators::FT0AC] = collision.multFT0M(); @@ -669,13 +824,13 @@ struct mcParticlePrediction { nMultReco[Estimators::ZNC] = collision.multZNC(); nMultReco[Estimators::ITSIB] = collision.multNTracksPV(); - float nMultRecoMCBC[Estimators::nEstimators] = {0}; + std::array nMultRecoMCBC = {0}; if (mcBC.has_ft0()) { const auto& ft0 = mcBC.ft0(); - for (auto amplitude : ft0.amplitudeA()) { + for (const auto& amplitude : ft0.amplitudeA()) { nMultRecoMCBC[Estimators::FT0A] += amplitude; } - for (auto amplitude : ft0.amplitudeC()) { + for (const auto& amplitude : ft0.amplitudeC()) { nMultRecoMCBC[Estimators::FT0C] += amplitude; } nMultRecoMCBC[Estimators::FT0AC] = nMultRecoMCBC[Estimators::FT0A] + nMultRecoMCBC[Estimators::FT0C]; @@ -699,7 +854,7 @@ struct mcParticlePrediction { hestimatorsRecoEvVsBCId[i]->Fill(foundBCid, nMultReco[i]); } } - PROCESS_SWITCH(mcParticlePrediction, processReco, "Process the reco info", true); + PROCESS_SWITCH(McParticlePrediction, processReco, "Process the reco info", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/QC/resonanceqa.cxx b/PWGLF/Tasks/QC/resonanceqa.cxx index c4d6b1aac7c..aae477086e9 100644 --- a/PWGLF/Tasks/QC/resonanceqa.cxx +++ b/PWGLF/Tasks/QC/resonanceqa.cxx @@ -264,7 +264,7 @@ struct resonanceqa { ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for bin"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0.0, 2.750, 5.250, 7.750, 12.750, 17.750, 22.750, 27.750, 32.750, 37.750, 42.750, 47.750, 52.750, 57.750, 62.750, 67.750, 72.750, 77.750, 82.750, 87.750, 92.750, 97.750, 250.1}, "multiplicity axis for histograms"}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, &cache}; void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { @@ -275,7 +275,7 @@ struct resonanceqa { histos.fill(HIST("hCentrality"), multiplicity); histos.fill(HIST("hNcontributor"), collision.numContrib()); histos.fill(HIST("hVtxZ"), collision.posZ()); - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -289,7 +289,7 @@ struct resonanceqa { histos.fill(HIST("hNsigmaProtonTPC"), track1.tpcNSigmaPr()); histos.fill(HIST("hNsigmaProtonTOF"), track1.tofNSigmaPr()); auto track1ID = track1.globalIndex(); - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!selectionTrack(track2)) { continue; } @@ -388,7 +388,7 @@ struct resonanceqa { } auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } @@ -425,7 +425,7 @@ struct resonanceqa { return; } histos.fill(HIST("hMC"), 1.5); - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -433,7 +433,7 @@ struct resonanceqa { continue; } auto track1ID = track1.globalIndex(); - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!track2.has_mcParticle()) { continue; } diff --git a/PWGLF/Tasks/QC/straRecoStudy.cxx b/PWGLF/Tasks/QC/straRecoStudy.cxx index 8482f7eafc1..4eddea21f14 100644 --- a/PWGLF/Tasks/QC/straRecoStudy.cxx +++ b/PWGLF/Tasks/QC/straRecoStudy.cxx @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -328,7 +329,7 @@ struct straRecoStudy { kEvSelVtxZ, kEvSelAllSteps }; - std::array evselstats; + std::array evselstats; void resetCounters() { @@ -592,7 +593,7 @@ struct straRecoStudy { PROCESS_SWITCH(straRecoStudy, processV0RealData, "Regular V0 analysis in real data", false); template - void processCascadeCandidate(TCascCandidate casc) + void processCascadeCandidate(const TCascCandidate& casc) { // MC association if (!casc.has_collision()) { diff --git a/PWGLF/Tasks/QC/strderivedGenQA.cxx b/PWGLF/Tasks/QC/strderivedGenQA.cxx index 7fa8d57ed56..b9223226614 100644 --- a/PWGLF/Tasks/QC/strderivedGenQA.cxx +++ b/PWGLF/Tasks/QC/strderivedGenQA.cxx @@ -53,7 +53,7 @@ using namespace o2::framework::expressions; using namespace std; using std::array; using dauTracks = soa::Join; -using StrCollisionsDatas = soa::Join; +using StrCollisionsDatas = soa::Join; using V0DerivedDatas = soa::Join; using V0DerivedMCDatas = soa::Join; using CascDerivedMCDatas = soa::Join; @@ -172,7 +172,7 @@ struct strderivedGenQA { ConfigurableAxis axisTrackProperties{"axisTrackProperties", {32, -0.5, 31.5f}, "Track Properties"}; } AxisConfig; - PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; void init(InitContext const&) { @@ -518,7 +518,7 @@ struct strderivedGenQA { } template - bool IsEventAccepted(TCollision collision) + bool IsEventAccepted(const TCollision& collision) // check whether the collision passes our collision selections { if (eventSelections.requireSel8 && !collision.sel8()) { @@ -589,7 +589,7 @@ struct strderivedGenQA { // ______________________________________________________ // Simulated processing // Return the list of indices to the recoed collision associated to a given MC collision. - std::vector getListOfRecoCollIndices(soa::Join const& mcCollisions, soa::Join const& collisions) + std::vector getListOfRecoCollIndices(soa::Join const& mcCollisions, soa::Join const& collisions) { std::vector listBestCollisionIdx(mcCollisions.size()); for (auto const& mcCollision : mcCollisions) { @@ -612,7 +612,7 @@ struct strderivedGenQA { // ______________________________________________________ // Simulated processing // Fill generated event information (for event loss/splitting estimation) - void fillGeneratedEventProperties(soa::Join const& mcCollisions, soa::Join const& collisions) + void fillGeneratedEventProperties(soa::Join const& mcCollisions, soa::Join const& collisions) { std::vector listBestCollisionIdx(mcCollisions.size()); for (auto const& mcCollision : mcCollisions) { @@ -1076,7 +1076,7 @@ struct strderivedGenQA { // ______________________________________________________ // Simulated processing (subscribes to MC information too) - void processGenerated(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& CascMCCores, soa::Join const& collisions) + void processGenerated(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& CascMCCores, soa::Join const& collisions) { fillGeneratedEventProperties(mcCollisions, collisions); std::vector listBestCollisionIdx = getListOfRecoCollIndices(mcCollisions, collisions); diff --git a/PWGLF/Tasks/QC/v0cascadesqa.cxx b/PWGLF/Tasks/QC/v0cascadesqa.cxx index 1c98ab982b8..4beab4e9572 100644 --- a/PWGLF/Tasks/QC/v0cascadesqa.cxx +++ b/PWGLF/Tasks/QC/v0cascadesqa.cxx @@ -47,6 +47,7 @@ #include #include +#include #include #include @@ -125,6 +126,8 @@ struct v0cascadesQA { RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; static constexpr float DefaultLifetimeCuts[1][2] = {{30., 20.}}; + const float ctauxiPDG = 4.91; // from PDG + const float ctauomegaPDG = 2.461; // from PDG struct : ConfigurableGroup { std::string prefix = "v0Selections"; // JSON group name @@ -204,7 +207,7 @@ struct v0cascadesQA { Configurable dcabachtopv{"dcabachtopv", .05, "min DCA Bachelor To PV (cm)"}; Configurable cascradius{"cascradius", 0.5, "minimum Cascade radius (cm)"}; Configurable cascradiusMax{"cascradiusMax", 1E5, "maximum Cascade radius (cm)"}; - Configurable cascProperLifeTime{"cascProperLifeTime", 3, "maximum lifetime (ctau)"}; + Configurable cascProperLifeTime{"cascProperLifeTime", 3, "maximum lifetime (meanctau)"}; // invariant mass selection Configurable v0MassWindow{"v0MassWindow", 0.008, "#Lambda mass (GeV/#it{c}^{2})"}; @@ -453,7 +456,7 @@ struct v0cascadesQA { } template - bool isEventAccepted(TCollision collision, bool fillHists) + bool isEventAccepted(const TCollision& collision, bool fillHists) // check whether the collision passes our collision selections { if (fillHists) { @@ -622,7 +625,7 @@ struct v0cascadesQA { } template - bool isV0Accepted(TV0 v0, TCollision collision, float rapidity, int v0Type) + bool isV0Accepted(const TV0& v0, const TCollision& collision, float rapidity, int v0Type) // precalculate this information so that a check is one mask operation, not many { // Base topological variables @@ -813,7 +816,7 @@ struct v0cascadesQA { } template - bool isCascadeSelected(TCascade casc, TCollision collision, float rapidity, int cascType) + bool isCascadeSelected(const TCascade& casc, const TCollision& collision, float rapidity, int cascType) // precalculate this information so that a check is one mask operation, not many { // @@ -876,9 +879,9 @@ struct v0cascadesQA { // // proper lifetime float distOverTotMom = std::sqrt(std::pow(casc.x() - collision.posX(), 2) + std::pow(casc.y() - collision.posY(), 2) + std::pow(casc.z() - collision.posZ(), 2)) / (casc.p() + 1E-10); - if ((cascType == kXiM || cascType == kXiP) && distOverTotMom * o2::constants::physics::MassXiMinus > cascSelections.cascProperLifeTime) + if ((cascType == kXiM || cascType == kXiP) && distOverTotMom * o2::constants::physics::MassXiMinus > (cascSelections.cascProperLifeTime * ctauxiPDG)) return false; - if ((cascType == kOmegaM || cascType == kOmegaP) && distOverTotMom * o2::constants::physics::MassOmegaMinus > cascSelections.cascProperLifeTime) + if ((cascType == kOmegaM || cascType == kOmegaP) && distOverTotMom * o2::constants::physics::MassOmegaMinus > (cascSelections.cascProperLifeTime * ctauomegaPDG)) return false; // rapidity @@ -1027,7 +1030,7 @@ struct v0cascadesQA { } template - bool checkV0MCAssociation(TV0 v0, int v0Type) + bool checkV0MCAssociation(const TV0& v0, int v0Type) // precalculate this information so that a check is one mask operation, not many { if (!v0.isPhysicalPrimary()) @@ -1051,7 +1054,7 @@ struct v0cascadesQA { } template - bool checkCascadeMCAssociation(TCascade casc, int cascType) + bool checkCascadeMCAssociation(const TCascade& casc, int cascType) // precalculate this information so that a check is one mask operation, not many { if (!casc.isPhysicalPrimary()) diff --git a/PWGLF/Tasks/QC/vertexQA.cxx b/PWGLF/Tasks/QC/vertexQA.cxx index 9dab5ce7f10..45aef295193 100644 --- a/PWGLF/Tasks/QC/vertexQA.cxx +++ b/PWGLF/Tasks/QC/vertexQA.cxx @@ -45,7 +45,7 @@ namespace { constexpr double LHCRFFreq = 400.789e6; constexpr double LHCBunchSpacingNS = 10 * 1.e9 / LHCRFFreq; -double deltaTimeColl(BCcoll const bccoll1, BCcoll const bccoll2) +double deltaTimeColl(BCcoll const& bccoll1, BCcoll const& bccoll2) { auto coll1 = std::get(bccoll1); auto coll2 = std::get(bccoll2); @@ -271,7 +271,7 @@ struct vertexQA { std::vector jumps{0ll}; int64_t lastBC = bcs.rawIteratorAt(0).globalBC(); - for (auto bc : bcs) { + for (const auto& bc : bcs) { if (bc.globalBC() - lastBC > 3564 * 32) { // 32 orbits jumps.push_back(bc.globalIndex()); lastBC = bc.globalBC(); @@ -279,7 +279,7 @@ struct vertexQA { } uint64_t jumpsSentinel{1}; std::vector collisionsIndices{0ll}; - for (auto col : collisions) { + for (const auto& col : collisions) { if (jumpsSentinel == jumps.size()) { break; } diff --git a/PWGLF/Tasks/Resonances/CMakeLists.txt b/PWGLF/Tasks/Resonances/CMakeLists.txt index e129ce85dcf..3105aa36c43 100644 --- a/PWGLF/Tasks/Resonances/CMakeLists.txt +++ b/PWGLF/Tasks/Resonances/CMakeLists.txt @@ -24,6 +24,11 @@ o2physics_add_dpl_workflow(cksspinalignder PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(phiflowder + SOURCES phiflowder.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(k892analysis SOURCES k892analysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -74,6 +79,11 @@ o2physics_add_dpl_workflow(k1analysismicro PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(k1-training-table + SOURCES k1TrainingTable.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(phianalysisrun3 SOURCES phianalysisrun3.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -89,13 +99,18 @@ o2physics_add_dpl_workflow(f0980pbpbanalysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(f0980-flow-sp + SOURCES f0980FlowSP.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(lambda1520spherocityanalysis SOURCES lambda1520SpherocityAnalysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(delta-analysis - SOURCES deltaanalysis.cxx +o2physics_add_dpl_workflow(deltaanalysis + SOURCES deltaAnalysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -119,8 +134,8 @@ o2physics_add_dpl_workflow(chargedkstaranalysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambda1520-pbpb - SOURCES lambda1520_PbPb.cxx +o2physics_add_dpl_workflow(lambda1520pbpb + SOURCES lambda1520pbpb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -129,13 +144,18 @@ o2physics_add_dpl_workflow(kshortkshort PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(k0s-k0s-flow + SOURCES k0sK0sFlow.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(phipbpb SOURCES phipbpb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(phianalysisrun3pbpb - SOURCES phianalysisrun3_PbPb.cxx + SOURCES phianalysisrun3pbpb.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -179,6 +199,11 @@ o2physics_add_dpl_workflow(omega2012analysis PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(omega2012-training-table + SOURCES omega2012TrainingTable.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(xi1820analysis SOURCES xi1820Analysis.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -249,8 +274,8 @@ o2physics_add_dpl_workflow(double-resonance-scan PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(kstarinoo - SOURCES kstarInOO.cxx +o2physics_add_dpl_workflow(kstarinjets + SOURCES kstarInJets.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -308,3 +333,53 @@ o2physics_add_dpl_workflow(k892hadronphoton SOURCES k892hadronphoton.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(k892hadronphotonbkg + SOURCES k892hadronphotonBkg.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::MLCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(chk892li + SOURCES chk892LI.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(lambda1520-analysis-po + SOURCES lambda1520analysispo.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(cha01710analysis + SOURCES cha01710analysis.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(xi1530kaoncorrelation + SOURCES xi1530kaoncorrelation.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(f0phiproxy + SOURCES f0phiproxy.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(lambdastarproxy + SOURCES lambdastarproxy.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(hresonancecorrelation + SOURCES hResonanceCorrelation.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(phiphi-mc-injected + SOURCES phiphimcinjected.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(phiphi-mc-trigger + SOURCES phiphimctrigger.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Resonances/cha01710analysis.cxx b/PWGLF/Tasks/Resonances/cha01710analysis.cxx new file mode 100644 index 00000000000..6a23c0dec06 --- /dev/null +++ b/PWGLF/Tasks/Resonances/cha01710analysis.cxx @@ -0,0 +1,480 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file cha01710analysis.cxx +/// \brief charged a01710 resonance analysis +/// \author Junlee Kim (jikim1290@gmail.com) + +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseITS.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct Cha01710analysis { + enum CentSel { + kFT0C = 0, + kFT0M + }; + + enum V0MassRegion { + kReject = 0, + kSignal, + kSideband + }; + + TRandom* rn = new TRandom(); + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Service ccdb{}; + + o2::aod::ITSResponse itsResponse; + + Configurable cfgUrl{"cfgUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + + struct : ConfigurableGroup { + Configurable cfgCentEst{"cfgCentEst", 1, "0: FT0C, 1: FT0M"}; + Configurable cfgZVertexMax{"cfgZVertexMax", 10.f, "maximum |zPV| (cm)"}; + Configurable cfgRequireSel8{"cfgRequireSel8", true, "require sel8"}; + Configurable cfgRequireGoodZvtx{"cfgRequireGoodZvtx", true, "require kIsGoodZvtxFT0vsPV"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", true, "require kNoSameBunchPileup"}; + } eventCuts; + + struct : ConfigurableGroup { + Configurable cfgV0PtMin{"cfgV0PtMin", 0.f, "minimum K0S pT"}; + Configurable cfgV0PtMax{"cfgV0PtMax", 100.f, "maximum K0S pT"}; + Configurable cfgV0RapidityMax{"cfgV0RapidityMax", 0.5f, "maximum |y(K0S)|"}; + Configurable cfgV0DcaDaughtersMax{"cfgV0DcaDaughtersMax", 1.f, "maximum daughter DCA"}; + Configurable cfgV0CosPAMin{"cfgV0CosPAMin", 0.97f, "minimum V0 cosine pointing angle"}; + Configurable cfgV0RadiusMin{"cfgV0RadiusMin", 0.5f, "minimum V0 radius (cm)"}; + Configurable cfgV0CtauMax{"cfgV0CtauMax", 15.f, "maximum K0S c tau (cm)"}; + Configurable cfgV0DaughterDcaPVMin{"cfgV0DaughterDcaPVMin", 0.06f, "minimum daughter |DCA to PV|"}; + Configurable cfgV0DaughterEtaMax{"cfgV0DaughterEtaMax", 0.8f, "maximum daughter |eta|"}; + Configurable cfgV0DaughterPtMin{"cfgV0DaughterPtMin", 0.1f, "minimum daughter pT"}; + Configurable cfgV0DaughterTPCNClsMin{"cfgV0DaughterTPCNClsMin", 70, "minimum daughter TPC clusters"}; + Configurable cfgV0DaughterTPCNSigmaPiMax{"cfgV0DaughterTPCNSigmaPiMax", 5.f, "maximum daughter |TPC nSigma(pi)|"}; + Configurable cfgApplyV0DaughterTOFPID{"cfgApplyV0DaughterTOFPID", false, "Apply pion TOF PID to V0 daughters with TOF"}; + Configurable cfgV0DaughterTOFNSigmaPiMax{"cfgV0DaughterTOFNSigmaPiMax", 5.f, "maximum daughter |TOF nSigma(pi)|"}; + Configurable cfgKsMassWindow{"cfgKsMassWindow", 0.01, "K short mass window"}; + Configurable cfgRejectLambda{"cfgRejectLambda", true, "reject Lambda/anti-Lambda competitors"}; + Configurable cfgLambdaMassWindow{"cfgLambdaMassWindow", 0.005f, "Lambda rejection window (GeV/c2)"}; + Configurable cfgApplyArmenterosCut{"cfgApplyArmenterosCut", false, "Apply Armenteros-Podolanski cut"}; + Configurable cfgArmenterosCut{"cfgArmenterosCut", 0.2f, "Minimum qT / |alpha|"}; + Configurable cfgApplyV0DaughterDeltaR{"cfgApplyV0DaughterDeltaR", false, "Apply minimum deltaR between V0 daughters"}; + Configurable cfgV0DaughterDeltaRMin{"cfgV0DaughterDeltaRMin", 0.001f, "Minimum deltaR between pion daughters"}; + } v0Cuts; + + struct : ConfigurableGroup { + Configurable cfgPtMin{"cfgPtMin", 0.2f, "minimum charged-kaon pT"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8f, "maximum charged-kaon |eta|"}; + Configurable cfgTPCCrossedRowsMin{"cfgTPCCrossedRowsMin", 70, "minimum crossed TPC rows"}; + Configurable cfgCrossedRowsRatioMin{"cfgCrossedRowsRatioMin", 0.8f, "minimum crossed/findable ratio"}; + Configurable cfgTPCChi2Max{"cfgTPCChi2Max", 4.f, "maximum TPC chi2/cluster"}; + Configurable cfgITSChi2Max{"cfgITSChi2Max", 36.f, "maximum ITS chi2/cluster"}; + Configurable cfgDcaXYMax{"cfgDcaXYMax", 0.5f, "maximum |DCAxy| (cm)"}; + Configurable cfgDcaZMax{"cfgDcaZMax", 2.f, "maximum |DCAz| (cm)"}; + Configurable cfgTPCNSigmaMax{"cfgTPCNSigmaMax", 3.f, "maximum |TPC nSigma(K)| without TOF"}; + Configurable cfgCombinedNSigmaMax{"cfgCombinedNSigmaMax", 3.f, "maximum combined TPC-TOF nSigma(K)"}; + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaKaon{"cfgPIDMaxITSnSigmaKaon", 3.0, "ITS nSigma selection"}; + } kaonCuts; + + struct : ConfigurableGroup { + Configurable cfgRequireQVector{"cfgRequireQVector", true, "reject events with an invalid Q-vector"}; + Configurable cfgQvecHarmonic{"cfgQvecHarmonic", 2, "event-plane harmonic"}; + Configurable cfgQvecNumDetectors{"cfgQvecNumDetectors", 7, "number of detectors in the Q-vector table"}; + Configurable cfgQvecDetName{"cfgQvecDetName", "FT0M", "detector used for the event plane"}; + Configurable cfgQvecRefAName{"cfgQvecRefAName", "TPCpos", "event-plane reference A"}; + Configurable cfgQvecRefBName{"cfgQvecRefBName", "TPCneg", "event-plane reference B"}; + Configurable cfgQvecAmplitudeMin{"cfgQvecAmplitudeMin", 1.e-4f, "minimum accepted Q-vector amplitude"}; + } epConfig; + + Configurable cfgMotherRapidityMax{"cfgMotherRapidityMax", 0.5f, "maximum |y(K0S K)|"}; + Configurable cfgNRotations{"cfgNRotations", 3, "number of deterministic rotations per candidate"}; + + ConfigurableAxis cfgAxisMass{"cfgAxisMass", {300, 1.0, 2.5}, "M(K^{0}_{S}K^{#pm}) (GeV/c^{2})"}; + ConfigurableAxis cfgAxisPt{"cfgAxisPt", {200, 0., 20.}, "pT(K^{0}_{S}K^{#pm}) (GeV/c)"}; + ConfigurableAxis cfgAxisCent{"cfgAxisCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 100.}, "FT0M centrality (%)"}; + ConfigurableAxis cfgAxisEP{"cfgAxisEP", {6, 0., constants::math::TwoPI}, "n#Delta#varphi = n(#varphi-#Psi_{n})"}; + + Filter collisionFilter = nabs(aod::collision::posZ) < eventCuts.cfgZVertexMax; + Filter trackFilter = nabs(aod::track::eta) < kaonCuts.cfgEtaMax && aod::track::pt > kaonCuts.cfgPtMin; + + using Collisions = soa::Filtered>; + using Tracks = soa::Filtered>; + using V0s = soa::Join; + + int detId = 0; + int refAId = 4; + int refBId = 5; + int minHarm = 2; + + float centrality = -1.; + float rotmin = 5.0 / 6.0; + float rotmax = 7.0 / 6.0; + + template + int getDetId(T const& name) + { + if (name.value == "FT0C") { + return 0; + } + if (name.value == "FT0A") { + return 1; + } + if (name.value == "FT0M") { + return 2; + } + if (name.value == "FV0A") { + return 3; + } + if (name.value == "TPCpos") { + return 4; + } + if (name.value == "TPCneg") { + return 5; + } + LOGF(warning, "Unknown Q-vector detector %s; using FT0C", name.value.c_str()); + return 0; + } + + void init(InitContext const&) + { + histos.add("Event/hCutFlow", "event cut flow", HistType::kTH1F, {{6, -0.5, 5.5}}); + histos.add("Event/hCentDist", "", HistType::kTH1F, {{120, 0, 120}}); + histos.add("Event/hPVzDist", "", HistType::kTH1F, {{200, -12, 12}}); + + histos.add("EventPlane/hPsiDet", "event plane, detector", HistType::kTH2F, {cfgAxisCent, {180, -constants::math::PI, constants::math::PI}}); + histos.add("EventPlane/hPsiRefA", "event plane, reference A", HistType::kTH2F, {cfgAxisCent, {180, -constants::math::PI, constants::math::PI}}); + histos.add("EventPlane/hPsiRefB", "event plane, reference B", HistType::kTH2F, {cfgAxisCent, {180, -constants::math::PI, constants::math::PI}}); + histos.add("EventPlane/hResolutionDetRefA", "cos[n(PsiDet-PsiRefA)]", HistType::kTH2F, {cfgAxisCent, {102, -1.02, 1.02}}); + histos.add("EventPlane/hResolutionDetRefB", "cos[n(PsiDet-PsiRefB)]", HistType::kTH2F, {cfgAxisCent, {102, -1.02, 1.02}}); + histos.add("EventPlane/hResolutionRefARefB", "cos[n(PsiRefA-PsiRefB)]", HistType::kTH2F, {cfgAxisCent, {102, -1.02, 1.02}}); + + histos.add("V0/hMassBefore", "K0S mass before selection", HistType::kTH2F, {{200, 0.45, 0.55}, cfgAxisPt}); + histos.add("V0/hMassSelected", "selected K0S signal", HistType::kTH2F, {{200, 0.45, 0.55}, cfgAxisPt}); + + histos.add("Kaon/hTPCNSigma", "charged kaon TPC PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hTOFNSigma", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hTPCNSigmaSelected", "charged kaon TPC PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hTOFNSigmaSelected", "charged kaon TOF PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hITSNSigma", "charged kaon ITS PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + histos.add("Kaon/hITSNSigmaSelected", "charged kaon ITS PID", HistType::kTH2F, {cfgAxisPt, {120, -6., 6.}}); + + histos.add("Pair/hSignalPlus", "K0S K+ same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); + histos.add("Pair/hSignalMinus", "K0S K- same-event versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); + histos.add("Pair/hSidebandPlus", "K0S-sideband K+ versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); + histos.add("Pair/hSidebandMinus", "K0S-sideband K- versus EP", HistType::kTHnSparseF, {cfgAxisMass, cfgAxisPt, cfgAxisCent, cfgAxisEP}); + histos.add("Pair/hRotatedPlus", "rotated K+ background versus EP", HistType::kTH3F, {cfgAxisMass, cfgAxisPt, cfgAxisCent}); + histos.add("Pair/hRotatedMinus", "rotated K- background versus EP", HistType::kTH3F, {cfgAxisMass, cfgAxisPt, cfgAxisCent}); + histos.add("Pair/hMassVsK0SMass", "pair mass versus reconstructed K0S mass", HistType::kTH2F, {cfgAxisMass, {200, 0.43, 0.57}}); + + detId = getDetId(epConfig.cfgQvecDetName); + refAId = getDetId(epConfig.cfgQvecRefAName); + refBId = getDetId(epConfig.cfgQvecRefBName); + if (detId == refAId || detId == refBId || refAId == refBId) { + LOGF(warning, "Q-vector detector names are not distinct; falling back to FT0M, TPCpos, TPCneg"); + detId = 0; + refAId = 4; + refBId = 5; + } + if (epConfig.cfgQvecHarmonic < minHarm) { + LOGF(fatal, "cfgQvecHarmonic must be >= 2"); + } + + ccdb->setURL(cfgUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + } + + template + bool selectEvent(C const& collision) + { + histos.fill(HIST("Event/hCutFlow"), 0.); + if (eventCuts.cfgRequireSel8 && !collision.sel8()) { + return false; + } + histos.fill(HIST("Event/hCutFlow"), 1.); + if (eventCuts.cfgRequireGoodZvtx && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + histos.fill(HIST("Event/hCutFlow"), 2.); + if (eventCuts.cfgRequireNoSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + histos.fill(HIST("Event/hCutFlow"), 3.); + + if (epConfig.cfgRequireQVector && + (collision.qvecAmp()[detId] < epConfig.cfgQvecAmplitudeMin || + collision.qvecAmp()[refAId] < epConfig.cfgQvecAmplitudeMin || + collision.qvecAmp()[refBId] < epConfig.cfgQvecAmplitudeMin)) { + return false; + } + histos.fill(HIST("Event/hCutFlow"), 4.); + return true; + } + + template + bool selectKaon(T const& track) + { + if (!track.isPVContributor() || !track.isGlobalTrackWoDCA() || !track.isPrimaryTrack()) { + return false; + } + if (track.pt() < kaonCuts.cfgPtMin || std::abs(track.eta()) > kaonCuts.cfgEtaMax) { + return false; + } + if (track.tpcNClsCrossedRows() < kaonCuts.cfgTPCCrossedRowsMin || + track.tpcCrossedRowsOverFindableCls() < kaonCuts.cfgCrossedRowsRatioMin || + track.tpcChi2NCl() > kaonCuts.cfgTPCChi2Max || track.itsChi2NCl() > kaonCuts.cfgITSChi2Max || + std::abs(track.dcaXY()) > kaonCuts.cfgDcaXYMax || std::abs(track.dcaZ()) > kaonCuts.cfgDcaZMax) { + return false; + } + histos.fill(HIST("Kaon/hTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (track.hasTOF()) { + histos.fill(HIST("Kaon/hTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } + histos.fill(HIST("Kaon/hITSNSigma"), track.pt(), itsResponse.nSigmaITS(track)); + + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaKaon{"cfgPIDMaxITSnSigmaKaon", 3.0, "ITS nSigma selection"}; + + if (track.hasTOF()) { + if (std::hypot(track.tpcNSigmaKa(), track.tofNSigmaKa()) > kaonCuts.cfgCombinedNSigmaMax) { + return false; + } + if (kaonCuts.cfgPIDITSFlag && std::abs(itsResponse.nSigmaITS(track)) > kaonCuts.cfgPIDMaxITSnSigmaKaon) { + return false; + } + return true; + } + if (std::abs(track.tpcNSigmaKa()) > kaonCuts.cfgTPCNSigmaMax) { + return false; + } + if (kaonCuts.cfgPIDITSFlag && std::abs(itsResponse.nSigmaITS(track)) > kaonCuts.cfgPIDMaxITSnSigmaKaon) { + return false; + } + return true; + } + + template + bool selectPionDaughter(T const& track) + { + if (track.tpcNClsFound() < v0Cuts.cfgV0DaughterTPCNClsMin) { + return false; + } + if (track.pt() < v0Cuts.cfgV0DaughterPtMin) { + return false; + } + if (std::abs(track.eta()) > v0Cuts.cfgV0DaughterEtaMax) { + return false; + } + if (std::abs(track.tpcNSigmaPi()) > v0Cuts.cfgV0DaughterTPCNSigmaPiMax) { + return false; + } + return true; + } + + template + V0MassRegion selectV0(C const& collision, V const& v0) + { + histos.fill(HIST("V0/hMassBefore"), v0.mK0Short(), v0.pt()); + float ctau = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * constants::physics::MassK0Short; + if (v0.pt() < v0Cuts.cfgV0PtMin || v0.pt() > v0Cuts.cfgV0PtMax || + v0.dcaV0daughters() > v0Cuts.cfgV0DcaDaughtersMax || v0.v0cosPA() < v0Cuts.cfgV0CosPAMin || + v0.v0radius() < v0Cuts.cfgV0RadiusMin || std::abs(ctau) > v0Cuts.cfgV0CtauMax || + std::abs(v0.dcapostopv()) < v0Cuts.cfgV0DaughterDcaPVMin || std::abs(v0.dcanegtopv()) < v0Cuts.cfgV0DaughterDcaPVMin) { + return V0MassRegion::kReject; + } + if (v0Cuts.cfgRejectLambda && + (std::abs(v0.mLambda() - constants::physics::MassLambda0) < v0Cuts.cfgLambdaMassWindow || + std::abs(v0.mAntiLambda() - constants::physics::MassLambda0) < v0Cuts.cfgLambdaMassWindow)) { + return V0MassRegion::kReject; + } + if (v0Cuts.cfgApplyArmenterosCut && v0.qtarm() < v0Cuts.cfgArmenterosCut) { + return V0MassRegion::kReject; + } + + auto pos = v0.template posTrack_as(); + auto neg = v0.template negTrack_as(); + if (!selectPionDaughter(pos) || !selectPionDaughter(neg)) { + return V0MassRegion::kReject; + } + if (v0Cuts.cfgApplyV0DaughterTOFPID) { + if (!v0.positiveHasTOF()) { + return V0MassRegion::kReject; + } + if (!v0.negativeHasTOF()) { + return V0MassRegion::kReject; + } + if (std::abs(v0.tofNSigmaK0PiPlus()) > v0Cuts.cfgV0DaughterTOFNSigmaPiMax) { + return V0MassRegion::kReject; + } + if (std::abs(v0.tofNSigmaK0PiMinus()) > v0Cuts.cfgV0DaughterTOFNSigmaPiMax) { + return V0MassRegion::kReject; + } + } + + ROOT::Math::PxPyPzMVector k0(v0.px(), v0.py(), v0.pz(), constants::physics::MassK0Short); + if (std::abs(k0.Rapidity()) > v0Cuts.cfgV0RapidityMax) { + return V0MassRegion::kReject; + } + + if (v0Cuts.cfgApplyV0DaughterDeltaR) { + float dEta = pos.eta() - neg.eta(); + float dPhi = TVector2::Phi_mpi_pi(pos.phi() - neg.phi()); + float deltaR = std::hypot(dEta, dPhi); + if (deltaR < v0Cuts.cfgV0DaughterDeltaRMin) { + return V0MassRegion::kReject; + } + } + + float dm = std::abs(v0.mK0Short() - constants::physics::MassK0Short); + histos.fill(HIST("V0/hMassSelected"), v0.mK0Short(), v0.pt()); + + if (dm < v0Cuts.cfgKsMassWindow) { + return V0MassRegion::kSignal; + } + return V0MassRegion::kSideband; + } + + template + void fillHistograms(CollisionType const& collision, TracksType const& dTracks, V0Type const& v0s) + { + int harmonic = epConfig.cfgQvecHarmonic; + int qVecDetInd = detId * 4 + 3 + (harmonic - 2) * epConfig.cfgQvecNumDetectors * 4; + int qVecRefAInd = refAId * 4 + 3 + (harmonic - 2) * epConfig.cfgQvecNumDetectors * 4; + int qVecRefBInd = refBId * 4 + 3 + (harmonic - 2) * epConfig.cfgQvecNumDetectors * 4; + + float eventPlaneDet = std::atan2(collision.qvecIm()[qVecDetInd], collision.qvecRe()[qVecDetInd]) / static_cast(harmonic); + float eventPlaneRefA = std::atan2(collision.qvecIm()[qVecRefAInd], collision.qvecRe()[qVecRefAInd]) / static_cast(harmonic); + float eventPlaneRefB = std::atan2(collision.qvecIm()[qVecRefBInd], collision.qvecRe()[qVecRefBInd]) / static_cast(harmonic); + + histos.fill(HIST("EventPlane/hPsiDet"), centrality, eventPlaneDet); + histos.fill(HIST("EventPlane/hPsiRefA"), centrality, eventPlaneRefA); + histos.fill(HIST("EventPlane/hPsiRefB"), centrality, eventPlaneRefB); + histos.fill(HIST("EventPlane/hResolutionDetRefA"), centrality, std::cos(harmonic * (eventPlaneDet - eventPlaneRefA))); + histos.fill(HIST("EventPlane/hResolutionDetRefB"), centrality, std::cos(harmonic * (eventPlaneDet - eventPlaneRefB))); + histos.fill(HIST("EventPlane/hResolutionRefARefB"), centrality, std::cos(harmonic * (eventPlaneRefA - eventPlaneRefB))); + + for (const auto& v0 : v0s) { + auto region = selectV0(collision, v0); + if (region == kReject) { + continue; + } + auto pos = v0.template posTrack_as(); + auto neg = v0.template negTrack_as(); + + ROOT::Math::PxPyPzMVector k0(v0.px(), v0.py(), v0.pz(), constants::physics::MassK0Short); + + for (const auto& track : dTracks) { + if (track.globalIndex() == pos.globalIndex() || track.globalIndex() == neg.globalIndex() || !selectKaon(track)) { + continue; + } + histos.fill(HIST("Kaon/hTPCNSigmaSelected"), track.pt(), track.tpcNSigmaKa()); + histos.fill(HIST("Kaon/hTOFNSigmaSelected"), track.pt(), track.tofNSigmaKa()); + + ROOT::Math::PxPyPzMVector kaon(track.px(), track.py(), track.pz(), constants::physics::MassKaonCharged); + auto mother = k0 + kaon; + if (std::abs(mother.Rapidity()) > cfgMotherRapidityMax) { + continue; + } + + float relPhi = TVector2::Phi_0_2pi((mother.Phi() - eventPlaneDet) * harmonic); + histos.fill(HIST("Pair/hMassVsK0SMass"), mother.M(), v0.mK0Short()); + if (region != kSignal) { + continue; + } + if (track.sign() > 0) { + histos.fill(HIST("Pair/hSignalPlus"), mother.M(), mother.Pt(), centrality, relPhi); + } else if (track.sign() < 0) { + histos.fill(HIST("Pair/hSignalMinus"), mother.M(), mother.Pt(), centrality, relPhi); + } + for (int i = 0; i < cfgNRotations; ++i) { + auto randomPhi = rn->Uniform(o2::constants::math::PI * rotmin, o2::constants::math::PI * rotmax); + randomPhi += kaon.Phi(); + auto kaonRot = ROOT::Math::PxPyPzMVector(kaon.Pt() * std::cos(randomPhi), kaon.Pt() * std::sin(randomPhi), track.pz(), o2::constants::physics::MassKaonCharged); + auto motherRot = k0 + kaonRot; + if (std::abs(motherRot.Rapidity()) > cfgMotherRapidityMax) { + continue; + } + if (track.sign() > 0) { + histos.fill(HIST("Pair/hRotatedPlus"), motherRot.M(), motherRot.Pt(), centrality); + } else if (track.sign() < 0) { + histos.fill(HIST("Pair/hRotatedMinus"), motherRot.M(), motherRot.Pt(), centrality); + } + } + } + } + } + + void processDataSame(Collisions::iterator const& collision, Tracks const& tracks, V0s const& v0s) + { + if (!selectEvent(collision)) { + return; + } + centrality = collision.centFT0M(); + if (eventCuts.cfgCentEst == kFT0M) { + centrality = collision.centFT0M(); + } else if (eventCuts.cfgCentEst == kFT0C) { + centrality = collision.centFT0C(); + } + + histos.fill(HIST("Event/hCentDist"), centrality); + histos.fill(HIST("Event/hPVzDist"), collision.posZ()); + + fillHistograms(collision, tracks, v0s); + }; + PROCESS_SWITCH(Cha01710analysis, processDataSame, "Process Event for data", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx b/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx index fa36132eb6d..364b677d2a0 100644 --- a/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx +++ b/PWGLF/Tasks/Resonances/chargedkstaranalysis.cxx @@ -19,6 +19,7 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/mcCentrality.h" #include "PWGLF/Utils/collisionCuts.h" +#include "PWGLF/Utils/inelGt.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" @@ -79,9 +80,26 @@ struct Chargedkstaranalysis { FT0C = 1, FT0M = 2 }; + enum EvtStep { + kAll = 0, + kZvtx, + kINELgt0, + kAssocReco, + kNSteps + }; + enum CollisionSystem : int { + OO = 1, + pp = 2 + }; + + const int nSteps = static_cast(EvtStep::kNSteps); + SliceCache cache; Preslice perCollision = aod::track::collisionId; + Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; bool currentIsGen = false; + bool mcClosure = false; + bool sigLossDen = false; struct : ConfigurableGroup { ConfigurableAxis cfgvtxbins{"cfgvtxbins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgmultbins{"cfgmultbins", {VARIABLE_WIDTH, 0., 1., 5., 10., 30., 50., 70., 100., 110.}, "Mixing bins - multiplicity"}; @@ -104,18 +122,24 @@ struct Chargedkstaranalysis { Configurable activateProductionFrame{"activateProductionFrame", false, "Activate the THnSparse with cosThStar w.r.t. production axis"}; Configurable activateBeamAxisFrame{"activateBeamAxisFrame", false, "Activate the THnSparse with cosThStar w.r.t. beam axis (Gottified jackson frame)"}; Configurable activateRandomFrame{"activateRandomFrame", false, "Activate the THnSparse with cosThStar w.r.t. random axis"}; - Configurable cRotations{"cRotations", 5, "Number of random rotations in the rotational background"}; - Configurable cBoostKShot{"cBoostKShot", true, "Boost the Kshot in Charged Kstar frame of reference"}; + Configurable cRotations{"cRotations", 4, "Number of random rotations in the rotational background"}; + Configurable cCosWithKShot{"cCosWithKShot", true, "Measure the angle with Kshort in Charged Kstar frame of reference"}; // Other cuts on Ks - Configurable rotationalCut{"rotationalCut", 10, "Cut value (Rotation angle pi - pi/cut and pi + pi/cut)"}; + Configurable rotationalCut{"rotationalCut", 6, "Cut value (Rotation angle pi - pi/cut and pi + pi/cut)"}; + + // for Kinematics checks + Configurable genKinematicsChecks{"genKinematicsChecks", false, "Activate the Kinematics cuts on the gen MC"}; // fixed variables float rapidityMotherData = 0.5; float beamEnergy = 13600.0; - double beamMomentum = std::sqrt(beamEnergy * beamEnergy / 4 - o2::constants::physics::MassProton * o2::constants::physics::MassProton); // GeV + float beamEnergyOo = 5360.0; int noOfDaughters = 2; } helicityCfgs; + Configurable cfgTruthUseInelGt0{"cfgTruthUseInelGt0", true, "Truth denominator: require INEL>0"}; + Configurable cfgTruthIncludeZvtx{"cfgTruthIncludeZvtx", true, "Truth denominator: also require |vtxz|; // using EventCandidates = soa::Join; // using TrackCandidates = soa::Join; @@ -140,8 +164,8 @@ struct Chargedkstaranalysis { // Event Mixing Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; - Service ccdb; - Service pdg; + Service ccdb{}; + Service pdg{}; o2::ccdb::CcdbApi ccdbApi; Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; @@ -206,11 +230,14 @@ struct Chargedkstaranalysis { Configurable cfgEventCentralityMin{"cfgEventCentralityMin", 0.0f, "Event sel: minimum centrality"}; Configurable cfgEventCentralityMax{"cfgEventCentralityMax", 100.0f, "Event sel: maximum centrality"}; Configurable cfgCentEst{"cfgCentEst", static_cast(CentralityEstimator::FT0C), "Centrality estimator: 1=FT0C, 2=FT0M"}; + Configurable cfgColSyst{"cfgColSyst", static_cast(CollisionSystem::pp), "Collision system: 1=OO, 2=pp"}; + } eventCutCfgs; // MC configurables struct : ConfigurableGroup { Configurable doBkgMc{"doBkgMc", false, "Apply rotation in MC"}; + Configurable doMcClosure{"doMcClosure", true, "Do MC Closure Study in MC"}; } mcCfgs; /// Track selections @@ -285,7 +312,7 @@ struct Chargedkstaranalysis { } kstarCutCfgs; Configurable isQaRequired{"isQaRequired", false, "Fill QA plots"}; - float centrality; + float centrality = 0.f; // PDG code int kPDGK0s = kK0Short; @@ -293,9 +320,10 @@ struct Chargedkstaranalysis { // int kPiPlus = kPiPlus; int kPDGK0 = kK0; // Variable declaration - ROOT::Math::PxPyPzEVector beam1{0., 0., -helicityCfgs.beamMomentum, 13600. / 2.}; - ROOT::Math::PxPyPzEVector beam2{0., 0., helicityCfgs.beamMomentum, 13600. / 2.}; + ROOT::Math::PxPyPzEVector beam1{0.0, 0.0, 0.0, 0.0}; + ROOT::Math::PxPyPzEVector beam2{0.0, 0.0, 0.0, 0.0}; + double fMaxPosPV = 1e-2; void init(o2::framework::InitContext&) { centrality = -999; @@ -344,7 +372,6 @@ struct Chargedkstaranalysis { AxisSpec thnAxisPhi = {axisCfgs.configThnAxisPhi, "Configurabel phi axis"}; // 0 to 2pi // THnSparse AxisSpec mcLabelAxis = {5, -0.5, 4.5, "MC Label"}; - if (!doprocessMC) { histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{5, 0, 5.0}}); auto hCutFlow = histos.get(HIST("hEvtSelInfo")); @@ -403,7 +430,6 @@ struct Chargedkstaranalysis { histos.add("QA/before/CentDist", "Centrality distribution", {HistType::kTH1D, {centAxis}}); histos.add("QA/before/CentDist1", "Centrality distribution", HistType::kTH1F, {{110, 0, 110}}); - histos.add("QA/trkbpionTPCPIDME", "TPC PID of bachelor pion candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); // Bachelor pion @@ -494,6 +520,15 @@ struct Chargedkstaranalysis { } // MC if (doprocessMC) { + histos.add("hEvtSelInfoMc", "hEvtSelInfoMc", kTH1F, {{5, 0, 5.0}}); + auto hCutFlowMc = histos.get(HIST("hEvtSelInfoMc")); + if (hCutFlowMc) { + hCutFlowMc->GetXaxis()->SetBinLabel(1, "All Events"); + hCutFlowMc->GetXaxis()->SetBinLabel(2, "coll cuts"); + hCutFlowMc->GetXaxis()->SetBinLabel(3, "rctChecker"); + hCutFlowMc->GetXaxis()->SetBinLabel(4, "Multiplicity"); + hCutFlowMc->GetXaxis()->SetBinLabel(5, "IsINELgt0"); + } if (isQaRequired) { histos.add("QA/MC/QACent_woCut", "Centrality without cut", HistType::kTH1F, {centAxis}); histos.add("QA/MC/QACent_woCentCut", "Centrality without cent cut", HistType::kTH1F, {centAxis}); @@ -549,9 +584,12 @@ struct Chargedkstaranalysis { if (!helicityCfgs.qAOptimisation) { histosMc.add("h3ChaKstarInvMassDSMcGen", "h3ChaKstarInvMassDSMcGen", kTHnSparseF, {centAxis, ptAxis, invMassAxisReso, thnAxisPOL}, true); histosMc.add("h3ChaKstarInvMassDSMcRec", "h3ChaKstarInvMassDSMcRec", kTHnSparseF, {centAxis, ptAxis, invMassAxisReso, thnAxisPOL}, true); + histosMc.add("h3ChaKstarInvMassDSMcRecClosure", "h3ChaKstarInvMassDSMcRecClosure", kTHnSparseF, {centAxis, ptAxis, invMassAxisReso, thnAxisPOL}, true); + histosMc.add("sigLoss_den_pri_threeD", "sigLoss_den_pri_threeD", kTHnSparseF, {centAxis, ptAxis, thnAxisPOL}, true); + if (mcCfgs.doBkgMc) { - histosMc.add("h3ChaKstarInvMassRotMcGen", "h3ChaKstarInvMassRotMcGen", kTHnSparseF, {centAxis, ptAxis, invMassAxisReso, thnAxisPOL}, true); histosMc.add("h3ChaKstarInvMassRotMcRec", "h3ChaKstarInvMassRotMcRec", kTHnSparseF, {centAxis, ptAxis, invMassAxisReso, thnAxisPOL}, true); + histosMc.add("h3ChaKstarInvMassRotMcRecClosure", "h3ChaKstarInvMassRotMcRecClosure", kTHnSparseF, {centAxis, ptAxis, invMassAxisReso, thnAxisPOL}, true); } } @@ -560,8 +598,28 @@ struct Chargedkstaranalysis { histos.add("EffKstar/recoKstar", "Kstar Reco matched (final all)", HistType::kTH2F, {ptAxis, centAxis}); histos.add("MCReco/hInvmass_Kstar_true", "MC-reco truth-tagged chK(892)", HistType::kTHnSparseD, {centAxis, ptAxis, invMassAxisReso}); + histos.add("Correction/sigLoss_den", "Gen Kstar (|y|<0.5) in truth class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_den_pri", "Gen primary Kstar (|y|<0.5) in truth class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_num", "Gen Kstar (|y|<0.5, selected events) in reco class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_num_pri", "Gen primary Kstar (|y|<0.5, selected events) in reco class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/EF_den", "Gen events (truth class)", HistType::kTH1F, {centAxis}); + histos.add("Correction/EF_num", "Reco events (selected events)", HistType::kTH1F, {centAxis}); + histos.add("Correction/hNEventsMCTruth", "hNEventsMCTruth", HistType::kTH1F, {AxisSpec{nSteps, 0.5, nSteps + 0.5, ""}}); + auto hstep = histos.get(HIST("Correction/hNEventsMCTruth")); + hstep->GetXaxis()->SetBinLabel(1, "All"); + hstep->GetXaxis()->SetBinLabel(2, "zvtx"); + hstep->GetXaxis()->SetBinLabel(3, "INEL>0"); + hstep->GetXaxis()->SetBinLabel(4, "Assoc with reco coll"); + } + if (eventCutCfgs.cfgColSyst == static_cast(CollisionSystem::OO)) { + double selectedBeamMomentum = std::sqrt((helicityCfgs.beamEnergyOo * helicityCfgs.beamEnergyOo / 4.0) - (o2::constants::physics::MassProton * o2::constants::physics::MassProton)); + beam1.SetPxPyPzE(0., 0., -selectedBeamMomentum, helicityCfgs.beamEnergyOo / 2.); + beam2.SetPxPyPzE(0., 0., selectedBeamMomentum, helicityCfgs.beamEnergyOo / 2.); + } else { + double selectedBeamMomentum = std::sqrt((helicityCfgs.beamEnergy * helicityCfgs.beamEnergy / 4.0) - (o2::constants::physics::MassProton * o2::constants::physics::MassProton)); + beam1.SetPxPyPzE(0., 0., -selectedBeamMomentum, helicityCfgs.beamEnergy / 2.); + beam2.SetPxPyPzE(0., 0., selectedBeamMomentum, helicityCfgs.beamEnergy / 2.); } - ccdb->setURL(cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -575,8 +633,9 @@ struct Chargedkstaranalysis { std::unordered_set allowedMcIds; std::unordered_map centTruthByAllowed; - - float lMultiplicity; + std::unordered_set refClassIds; + std::unordered_map refCentByMcId; + float lMultiplicity = 0.f; template float getCentrality(CollisionType const& collision) { @@ -598,76 +657,100 @@ struct Chargedkstaranalysis { int ibin = 1; auto applyCut = [&](bool condition) -> bool { - if (!condition) + if (!condition) { return false; - if (!doprocessMC && isQaRequired) + } + if (!doprocessMC && isQaRequired) { histos.fill(HIST("QA/hTrackCutFlow"), ibin); + } ibin++; return true; }; // First bin (before any cuts) - if (!doprocessMC && isQaRequired) + if (!doprocessMC && isQaRequired) { histos.fill(HIST("QA/hTrackCutFlow"), ibin++); + } // Cuts - if (!applyCut(std::abs(track.pt()) >= trackCutCfgs.cMinPtcut)) + if (!applyCut(std::abs(track.pt()) >= trackCutCfgs.cMinPtcut)) { return false; - if (!applyCut(std::abs(track.eta()) <= trackCutCfgs.cMaxEtacut)) + } + if (!applyCut(std::abs(track.eta()) <= trackCutCfgs.cMaxEtacut)) { return false; - if (!applyCut(track.itsNCls() >= trackCutCfgs.cfgITScluster)) + } + if (!applyCut(track.itsNCls() >= trackCutCfgs.cfgITScluster)) { return false; - if (!applyCut(track.tpcNClsFound() >= trackCutCfgs.cfgTPCcluster)) + } + if (!applyCut(track.tpcNClsFound() >= trackCutCfgs.cfgTPCcluster)) { return false; - if (!applyCut(track.tpcCrossedRowsOverFindableCls() >= trackCutCfgs.cfgRatioTPCRowsOverFindableCls)) + } + if (!applyCut(track.tpcCrossedRowsOverFindableCls() >= trackCutCfgs.cfgRatioTPCRowsOverFindableCls)) { return false; - if (!applyCut(track.itsChi2NCl() < trackCutCfgs.cfgITSChi2NCl)) + } + if (!applyCut(track.itsChi2NCl() < trackCutCfgs.cfgITSChi2NCl)) { return false; - if (!applyCut(track.tpcChi2NCl() < trackCutCfgs.cfgTPCChi2NCl)) + } + if (!applyCut(track.tpcChi2NCl() < trackCutCfgs.cfgTPCChi2NCl)) { return false; - - if (!applyCut(!trackCutCfgs.cfgHasITS || track.hasITS())) + } + if (!applyCut(!trackCutCfgs.cfgHasITS || track.hasITS())) { return false; - if (!applyCut(!trackCutCfgs.cfgHasTPC || track.hasTPC())) + } + if (!applyCut(!trackCutCfgs.cfgHasTPC || track.hasTPC())) { return false; - if (!applyCut(!trackCutCfgs.cfgHasTOF || track.hasTOF())) + } + if (!applyCut(!trackCutCfgs.cfgHasTOF || track.hasTOF())) { return false; + } - if (!applyCut(!trackCutCfgs.cfgUseITSRefit || track.passedITSRefit())) + if (!applyCut(!trackCutCfgs.cfgUseITSRefit || track.passedITSRefit())) { return false; - if (!applyCut(!trackCutCfgs.cfgUseTPCRefit || track.passedTPCRefit())) + } + if (!applyCut(!trackCutCfgs.cfgUseTPCRefit || track.passedTPCRefit())) { return false; + } - if (!applyCut(!trackCutCfgs.cfgPVContributor || track.isPVContributor())) + if (!applyCut(!trackCutCfgs.cfgPVContributor || track.isPVContributor())) { return false; - if (!applyCut(!trackCutCfgs.cfgGlobalWoDCATrack || track.isGlobalTrackWoDCA())) + } + if (!applyCut(!trackCutCfgs.cfgGlobalWoDCATrack || track.isGlobalTrackWoDCA())) { return false; - if (!applyCut(!trackCutCfgs.cfgGlobalTrack || track.isGlobalTrack())) + } + if (!applyCut(!trackCutCfgs.cfgGlobalTrack || track.isGlobalTrack())) { return false; - if (!applyCut(!trackCutCfgs.cfgPrimaryTrack || track.isPrimaryTrack())) + } + if (!applyCut(!trackCutCfgs.cfgPrimaryTrack || track.isPrimaryTrack())) { return false; + } - if (!applyCut(std::abs(track.dcaXY()) <= trackCutCfgs.cMaxbDCArToPVcut)) + if (!applyCut(std::abs(track.dcaXY()) <= trackCutCfgs.cMaxbDCArToPVcut)) { return false; - if (!applyCut(std::abs(track.dcaZ()) <= trackCutCfgs.cMaxbDCAzToPVcut)) + } + if (!applyCut(std::abs(track.dcaZ()) <= trackCutCfgs.cMaxbDCAzToPVcut)) { return false; + } // pT dependent DCA XY if (trackCutCfgs.cfgpTdepDCAxyCut) { - if (!applyCut(std::abs(track.dcaXY()) <= (0.004 + (0.013 / track.pt())))) + if (!applyCut(std::abs(track.dcaXY()) <= (0.004 + (0.013 / track.pt())))) { return false; + } } else { - if (!applyCut(std::abs(track.dcaXY()) <= trackCutCfgs.cfgMaxbDCArToPVcut)) + if (!applyCut(std::abs(track.dcaXY()) <= trackCutCfgs.cfgMaxbDCArToPVcut)) { return false; + } } // pT dependent DCA Z if (trackCutCfgs.cfgpTdepDCAzCut) { - if (!applyCut(std::abs(track.dcaZ()) <= (0.004 + (0.013 / track.pt())))) + if (!applyCut(std::abs(track.dcaZ()) <= (0.004 + (0.013 / track.pt())))) { return false; + } } else { - if (!applyCut(std::abs(track.dcaZ()) <= trackCutCfgs.cfgMaxbDCAzToPVcut)) + if (!applyCut(std::abs(track.dcaZ()) <= trackCutCfgs.cfgMaxbDCAzToPVcut)) { return false; + } } return true; @@ -676,13 +759,15 @@ struct Chargedkstaranalysis { template bool selectionPIDPion(TrackType const& candidate) { - if (std::abs(candidate.tpcNSigmaPi()) >= pidCutCfgs.cfgMaxTPCnSigmaPion) + if (std::abs(candidate.tpcNSigmaPi()) >= pidCutCfgs.cfgMaxTPCnSigmaPion) { return false; - if (pidCutCfgs.cfgTPConly) + } + if (pidCutCfgs.cfgTPConly) { return true; - if (candidate.pt() <= pidCutCfgs.cfgTOFMinPt) + } + if (candidate.pt() <= pidCutCfgs.cfgTOFMinPt) { return true; - + } if (candidate.hasTOF()) { const bool tofPIDPassed = std::abs(candidate.tofNSigmaPi()) < pidCutCfgs.cfgMaxTOFnSigmaPion; const bool combo = (pidCutCfgs.cfgNsigmaCutCombinedPion > 0) && @@ -690,9 +775,8 @@ struct Chargedkstaranalysis { candidate.tofNSigmaPi() * candidate.tofNSigmaPi() < pidCutCfgs.cfgNsigmaCutCombinedPion * pidCutCfgs.cfgNsigmaCutCombinedPion); return tofPIDPassed || combo; - } else { - return pidCutCfgs.cfgTOFVeto; } + return pidCutCfgs.cfgTOFVeto; } template @@ -725,8 +809,9 @@ struct Chargedkstaranalysis { auto mLambda = candidate.mLambda(); auto mALambda = candidate.mAntiLambda(); - if (!doprocessMC && isQaRequired) + if (!doprocessMC && isQaRequired) { histos.fill(HIST("QA/K0sCutCheck"), ibin); + } applyCut(dauDCA <= secondaryCutsCfgs.cSecondaryDauDCAMax); applyCut(dauPosDCAtoPV >= secondaryCutsCfgs.cSecondaryDauPosDCAtoPVMin); @@ -744,126 +829,58 @@ struct Chargedkstaranalysis { return returnFlag; } - template - bool isTrueKstar(const TrackTemplate& bTrack, const V0Template& K0scand) - { - if (std::abs(bTrack.PDGCode()) != kPiPlus) // Are you pion? - return false; - if (std::abs(K0scand.PDGCode()) != kPDGK0s) // Are you K0s? - return false; - - auto motherbTrack = bTrack.template mothers_as(); - auto motherkV0 = K0scand.template mothers_as(); - - // Check bTrack first - if (std::abs(motherbTrack.pdgCode()) != kKstarPlus) // Are you charged Kstar's daughter? - return false; // Apply first since it's more restrictive - - if (std::abs(motherkV0.pdgCode()) != kPDGK0) // Is it K0s? - return false; - // Check if K0s's mother is K0 (311) - auto motherK0 = motherkV0.template mothers_as(); - if (std::abs(motherK0.pdgCode()) != kPDGK0) - return false; - - // Check if K0's mother is Kstar (323) - auto motherKstar = motherK0.template mothers_as(); - if (std::abs(motherKstar.pdgCode()) != kKstarPlus) - return false; - - // Check if bTrack and K0 have the same mother (global index) - if (motherbTrack.globalIndex() != motherK0.globalIndex()) - return false; - - return true; - } - int count = 0; - template - bool matchRecoToTruthKstar(V0T const& v0, TrkT const& trk) - { - if (!v0.has_mcParticle() || !trk.has_mcParticle()) - return false; - - auto mcK0s = v0.template mcParticle_as(); - auto mcPi = trk.template mcParticle_as(); - - if (std::abs(mcK0s.pdgCode()) != kPDGK0s) - return false; - if (std::abs(mcPi.pdgCode()) != kPiPlus) - return false; - - MCTrueTrackCandidates::iterator kstarFromPi; - bool havePiKstar = false; - for (const auto& m1 : mcPi.template mothers_as()) { - if (std::abs(m1.pdgCode()) == kKstarPlus) { - kstarFromPi = m1; - havePiKstar = true; - break; - } - } - if (!havePiKstar) { - return false; - } - - bool shareSameKstar = false; - for (const auto& m1 : mcK0s.template mothers_as()) { - if (std::abs(m1.pdgCode()) == kPDGK0) { - for (const auto& m2 : m1.template mothers_as()) { - if (m2.globalIndex() == kstarFromPi.globalIndex()) { - shareSameKstar = true; - break; - } - } - if (shareSameKstar) - break; - } - } - if (!shareSameKstar) { - return false; - } - - return true; - } // matchRecoToTruthKstar - template void fillKstarHist(bool isRot, float multiplicity, const T& mother, double cosTheta) { + // 1. Data Path if (!doprocessMC) { - if (isRot) { hChaKstar.fill(HIST("h3ChaKstarInvMassRot"), multiplicity, mother.Pt(), mother.M(), cosTheta); } else { hChaKstar.fill(HIST("h3ChaKstarInvMassDS"), multiplicity, mother.Pt(), mother.M(), cosTheta); } + return; + } + + // 2. MC Generated Path + if (currentIsGen) { + if (sigLossDen) { + // Fill ONLY the Signal Loss Denominator 3D Histograms + histosMc.fill(HIST("sigLoss_den_pri_threeD"), multiplicity, mother.Pt(), cosTheta); + } else { + // Fill standard 4D MC Gen Histograms + histosMc.fill(HIST("h3ChaKstarInvMassDSMcGen"), multiplicity, mother.Pt(), mother.M(), cosTheta); + } + return; + } + + // 3. MC Reconstructed Path + if (isRot) { + if (!mcClosure) { + histosMc.fill(HIST("h3ChaKstarInvMassRotMcRec"), multiplicity, mother.Pt(), mother.M(), cosTheta); + } else { + histosMc.fill(HIST("h3ChaKstarInvMassRotMcRecClosure"), multiplicity, mother.Pt(), mother.M(), cosTheta); + } } else { - if (currentIsGen) { - if (isRot) { - histosMc.fill(HIST("h3ChaKstarInvMassRotMcGen"), multiplicity, mother.Pt(), mother.M(), cosTheta); - } else { - histosMc.fill(HIST("h3ChaKstarInvMassDSMcGen"), multiplicity, mother.Pt(), mother.M(), cosTheta); - } + if (!mcClosure) { + histosMc.fill(HIST("h3ChaKstarInvMassDSMcRec"), multiplicity, mother.Pt(), mother.M(), cosTheta); } else { - if (isRot) { - histosMc.fill(HIST("h3ChaKstarInvMassRotMcRec"), multiplicity, mother.Pt(), mother.M(), cosTheta); - } else { - histosMc.fill(HIST("h3ChaKstarInvMassDSMcRec"), multiplicity, mother.Pt(), mother.M(), cosTheta); - } + histosMc.fill(HIST("h3ChaKstarInvMassDSMcRecClosure"), multiplicity, mother.Pt(), mother.M(), cosTheta); } } } - template void fillInvMass(const T& mother, float multiplicity, const T& daughter1, const T& daughter2, bool isMix) { - TRandom* rn = new TRandom(); + auto* rn = new TRandom(); rn->SetSeed(0); // Variable declarations ROOT::Math::PxPyPzMVector fourVecDauCM, daughterRot, motherRot, daughterRotCM; ROOT::Math::XYZVectorF beam1CM, beam2CM, zAxisCS, yAxisCS, xAxisCS; ROOT::Math::XYZVectorF v1CM, zaxisHE, yaxisHE, xaxisHE; - ROOT::Math::XYZVector randomVec, beamVec, normalVec; - float theta2; + ROOT::Math::XYZVector randomVec, beamVec, normalVec, normalVecRot, randomVecRot; + float theta2 = 0.0f; // //polarization calculations // zBeam = ROOT::Math::XYZVector(0.f, 0.f, 1.f); // ẑ: beam direction in lab frame @@ -899,7 +916,7 @@ struct Chargedkstaranalysis { auto phiCS = std::atan2(yAxisCS.Dot(v1CM), xAxisCS.Dot(v1CM)); phiCS = RecoDecay::constrainAngle(phiCS, 0.0); - bool doRotation = (!doprocessMC) || (doprocessMC && mcCfgs.doBkgMc); + bool doRotation = !doprocessMC || (!currentIsGen && mcCfgs.doBkgMc); // if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { if (helicityCfgs.activateHelicityFrame) { // helicityVec = mother.Vect(); // 3 vector of mother in COM frame @@ -912,13 +929,21 @@ struct Chargedkstaranalysis { if (doRotation) { for (int i = 0; i < helicityCfgs.cRotations; i++) { theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / helicityCfgs.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / helicityCfgs.rotationalCut); + if (helicityCfgs.cCosWithKShot) { + daughterRot = ROOT::Math::PxPyPzMVector(daughter2.Px() * std::cos(theta2) - daughter2.Py() * std::sin(theta2), daughter2.Px() * std::sin(theta2) + daughter2.Py() * std::cos(theta2), daughter2.Pz(), daughter2.M()); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); - - motherRot = daughterRot + daughter2; + motherRot = daughterRot + daughter1; + } else { + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + motherRot = daughterRot + daughter2; + } ROOT::Math::Boost boost2{motherRot.BoostToCM()}; - daughterRotCM = boost2(daughterRot); + if (helicityCfgs.cCosWithKShot) { + daughterRotCM = boost2(daughter1); + } else { + daughterRotCM = boost2(daughterRot); + } auto cosThetaStarHelicityRot = motherRot.Vect().Dot(daughterRotCM.Vect()) / (std::sqrt(daughterRotCM.Vect().Mag2()) * std::sqrt(motherRot.Vect().Mag2())); auto phiHelicityRot = std::atan2(yaxisHE.Dot(daughterRotCM.Vect().Unit()), xaxisHE.Dot(daughterRotCM.Vect().Unit())); @@ -938,9 +963,15 @@ struct Chargedkstaranalysis { for (int i = 0; i < helicityCfgs.cRotations; i++) { theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / helicityCfgs.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / helicityCfgs.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + if (helicityCfgs.cCosWithKShot) { + daughterRot = ROOT::Math::PxPyPzMVector(daughter2.Px() * std::cos(theta2) - daughter2.Py() * std::sin(theta2), daughter2.Px() * std::sin(theta2) + daughter2.Py() * std::cos(theta2), daughter2.Pz(), daughter2.M()); - motherRot = daughterRot + daughter2; + motherRot = daughterRot + daughter1; + } else { + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + + motherRot = daughterRot + daughter2; + } ROOT::Math::Boost boost2{motherRot.BoostToCM()}; daughterRotCM = boost2(daughterRot); @@ -965,11 +996,27 @@ struct Chargedkstaranalysis { if (doRotation) { for (int i = 0; i < helicityCfgs.cRotations; i++) { theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / helicityCfgs.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / helicityCfgs.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); - motherRot = daughterRot + daughter2; + if (helicityCfgs.cCosWithKShot) { + daughterRot = ROOT::Math::PxPyPzMVector(daughter2.Px() * std::cos(theta2) - daughter2.Py() * std::sin(theta2), daughter2.Px() * std::sin(theta2) + daughter2.Py() * std::cos(theta2), daughter2.Pz(), daughter2.M()); + + motherRot = daughterRot + daughter1; + } else { + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + + motherRot = daughterRot + daughter2; + } + ROOT::Math::Boost boost2{motherRot.BoostToCM()}; + if (helicityCfgs.cCosWithKShot) { + daughterRotCM = boost2(daughter1); + } else { + daughterRotCM = boost2(daughterRot); + } + + normalVecRot = ROOT::Math::XYZVector(motherRot.Py(), -motherRot.Px(), 0.f); + auto cosThetaProductionRot = normalVecRot.Dot(daughterRotCM.Vect()) / (std::sqrt(daughterRotCM.Vect().Mag2()) * std::sqrt(normalVecRot.Mag2())); if (std::abs(motherRot.Rapidity()) < helicityCfgs.rapidityMotherData) { - fillKstarHist(true, multiplicity, motherRot, cosThetaProduction); + fillKstarHist(true, multiplicity, motherRot, cosThetaProductionRot); } } } @@ -984,9 +1031,17 @@ struct Chargedkstaranalysis { if (doRotation) { for (int i = 0; i < helicityCfgs.cRotations; i++) { theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / helicityCfgs.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / helicityCfgs.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); - motherRot = daughterRot + daughter2; + if (helicityCfgs.cCosWithKShot) { + daughterRot = ROOT::Math::PxPyPzMVector(daughter2.Px() * std::cos(theta2) - daughter2.Py() * std::sin(theta2), daughter2.Px() * std::sin(theta2) + daughter2.Py() * std::cos(theta2), daughter2.Pz(), daughter2.M()); + + motherRot = daughterRot + daughter1; + } else { + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + + motherRot = daughterRot + daughter2; + } + if (std::abs(motherRot.Rapidity()) < helicityCfgs.rapidityMotherData) { fillKstarHist(true, multiplicity, motherRot, cosThetaStarBeam); } @@ -1005,12 +1060,31 @@ struct Chargedkstaranalysis { } if (doRotation) { for (int i = 0; i < helicityCfgs.cRotations; i++) { + auto phiRandomRot = gRandom->Uniform(0.f, constants::math::TwoPI); + auto thetaRandomRot = gRandom->Uniform(0.f, constants::math::PI); + randomVecRot = ROOT::Math::XYZVector(std::sin(thetaRandomRot) * std::cos(phiRandomRot), std::sin(thetaRandomRot) * std::sin(phiRandomRot), std::cos(thetaRandomRot)); theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / helicityCfgs.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / helicityCfgs.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); - motherRot = daughterRot + daughter2; + if (helicityCfgs.cCosWithKShot) { + daughterRot = ROOT::Math::PxPyPzMVector(daughter2.Px() * std::cos(theta2) - daughter2.Py() * std::sin(theta2), daughter2.Px() * std::sin(theta2) + daughter2.Py() * std::cos(theta2), daughter2.Pz(), daughter2.M()); + + motherRot = daughterRot + daughter1; + } else { + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + + motherRot = daughterRot + daughter2; + } + + ROOT::Math::Boost boost2{motherRot.BoostToCM()}; + if (helicityCfgs.cCosWithKShot) { + daughterRotCM = boost2(daughter1); + } else { + daughterRotCM = boost2(daughterRot); + } + auto cosThetaStarRandomRot = randomVecRot.Dot(daughterRotCM.Vect()) / std::sqrt(daughterRotCM.Vect().Mag2()); + if (std::abs(motherRot.Rapidity()) < helicityCfgs.rapidityMotherData) { - fillKstarHist(true, multiplicity, motherRot, cosThetaStarRandom); + fillKstarHist(true, multiplicity, motherRot, cosThetaStarRandomRot); } } } @@ -1022,8 +1096,8 @@ struct Chargedkstaranalysis { void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksTypeK0s& dTracks2) { if (!doprocessMC && isQaRequired) { - histos.fill(HIST("QA/before/CentDist"), collision.centFT0M()); - histos.fill(HIST("QA/before/CentDist1"), collision.centFT0M()); + histos.fill(HIST("QA/before/CentDist"), getCentrality(collision)); + histos.fill(HIST("QA/before/CentDist1"), getCentrality(collision)); } ROOT::Math::PxPyPzMVector lResoSecondary, lDecayDaughter_bach, lResoKstar, chargeKstarrot; @@ -1058,8 +1132,9 @@ struct Chargedkstaranalysis { } } - if (!trackCut(bTrack) || !selectionPIDPion(bTrack)) + if (!trackCut(bTrack) || !selectionPIDPion(bTrack)) { continue; + } if constexpr (!IsMix) { if (!doprocessMC && isQaRequired) { @@ -1141,8 +1216,9 @@ struct Chargedkstaranalysis { if ((!secondaryCutsCfgs.cfgByPassDauPIDSelection && (!selectionPIDPion(pos) || !selectionPIDPion(neg))) || - !selectionK0s(collision, K0scand)) + !selectionK0s(collision, K0scand)) { continue; + } if constexpr (!IsMix) { if (!doprocessMC && isQaRequired) { @@ -1176,7 +1252,6 @@ struct Chargedkstaranalysis { k0sIndicies.push_back(K0scand.index()); } - // ========================= // Pairing // ========================= @@ -1198,8 +1273,9 @@ struct Chargedkstaranalysis { } if (lResoKstar.Rapidity() > kstarCutCfgs.cKstarMaxRap || - lResoKstar.Rapidity() < kstarCutCfgs.cKstarMinRap) + lResoKstar.Rapidity() < kstarCutCfgs.cKstarMinRap) { continue; + } if constexpr (!IsMix) { if (!doprocessMC && isQaRequired) { @@ -1207,7 +1283,7 @@ struct Chargedkstaranalysis { histos.fill(HIST("QA/after/kstarinvmass"), lResoKstar.M()); } - if (helicityCfgs.cBoostKShot) { + if (helicityCfgs.cCosWithKShot) { fillInvMass(lResoKstar, collision.centFT0M(), lResoSecondary, lDecayDaughter_bach, IsMix); } else { fillInvMass(lResoKstar, collision.centFT0M(), lDecayDaughter_bach, lResoSecondary, IsMix); @@ -1228,8 +1304,9 @@ struct Chargedkstaranalysis { aod::BCsWithTimestamps const&) { histos.fill(HIST("hEvtSelInfo"), 0.5); - if (!colCuts.isSelected(collision)) // Default event selection + if (!colCuts.isSelected(collision)) { // Default event selection return; + } histos.fill(HIST("hEvtSelInfo"), 1.5); if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(collision)) { return; @@ -1237,11 +1314,13 @@ struct Chargedkstaranalysis { histos.fill(HIST("hEvtSelInfo"), 2.5); lMultiplicity = getCentrality(collision); - if (lMultiplicity < eventCutCfgs.cfgEventCentralityMin || lMultiplicity > eventCutCfgs.cfgEventCentralityMax) + if (lMultiplicity < eventCutCfgs.cfgEventCentralityMin || lMultiplicity > eventCutCfgs.cfgEventCentralityMax) { return; + } histos.fill(HIST("hEvtSelInfo"), 3.5); - if (!collision.isInelGt0()) + if (!collision.isInelGt0()) { return; + } histos.fill(HIST("hEvtSelInfo"), 4.5); colCuts.fillQA(collision); fillHistograms(collision, tracks, v0s); @@ -1249,7 +1328,7 @@ struct Chargedkstaranalysis { PROCESS_SWITCH(Chargedkstaranalysis, processDataSE, "Process Event for data without Partitioning", true); using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor binningOnPositions{{axisCfgs.cfgvtxbins, axisCfgs.cfgmultbins}, true}; + BinningTypeVertexContributor binningOnPositions{{axisCfgs.cfgvtxbins, axisCfgs.cfgmultbins}}; Pair pair{binningOnPositions, nEvtMixing, -1, &cache}; void processDataME(EventCandidates const& /*collisions*/, TrackCandidates const& /*tracks*/, V0Candidates const& /*V0s*/) @@ -1266,60 +1345,77 @@ struct Chargedkstaranalysis { for (const auto& [t1, t2] : o2::soa::combinations( o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { // Here t1 corressponds to bachelor track and t2 corressponds to v0s. - if (!trackCut(t1)) + if (!trackCut(t1)) { continue; - if (!selectionPIDPion(t1)) + } + if (!selectionPIDPion(t1)) { continue; + } auto posDauTrack = t2.template posTrack_as(); auto negDauTrack = t2.template negTrack_as(); - if (!secondaryCutsCfgs.cfgByPassDauPIDSelection && !selectionPIDPion(posDauTrack)) // Perhaps it's already applied in trackCut (need to check QA plots) + if (!secondaryCutsCfgs.cfgByPassDauPIDSelection && !selectionPIDPion(posDauTrack)) { // Perhaps it's already applied in trackCut (need to check QA plots) continue; - if (!secondaryCutsCfgs.cfgByPassDauPIDSelection && !selectionPIDPion(negDauTrack)) + } + if (!secondaryCutsCfgs.cfgByPassDauPIDSelection && !selectionPIDPion(negDauTrack)) { continue; - if (!selectionK0s(c2, t2)) + } + if (!selectionK0s(c2, t2)) { continue; + } ROOT::Math::PxPyPzMVector lResoSecondary, lDecayDaughter_bach, lResoKstar; lDecayDaughter_bach = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), MassPionCharged); lResoSecondary = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), MassK0Short); lResoKstar = lResoSecondary + lDecayDaughter_bach; - if (lResoKstar.Rapidity() > kstarCutCfgs.cKstarMaxRap || lResoKstar.Rapidity() < kstarCutCfgs.cKstarMinRap) + if (lResoKstar.Rapidity() > kstarCutCfgs.cKstarMaxRap || lResoKstar.Rapidity() < kstarCutCfgs.cKstarMinRap) { continue; + } histos.fill(HIST("hInvmass_KstarME"), c1.centFT0M(), lResoKstar.Pt(), lResoKstar.M()); } } } PROCESS_SWITCH(Chargedkstaranalysis, processDataME, "Process Event for data without Partitioning", true); - void processMC(soa::Join const&, aod::McParticles const& mcParticles, soa::Join const& events, MCV0Candidates const& v0s, MCTrackCandidates const& tracks) + void processMC(soa::Join const& mccolls, aod::McParticles const& mcParticles, soa::Join const& events, MCV0Candidates const& v0s, MCTrackCandidates const& tracks) { allowedMcIds.clear(); centTruthByAllowed.clear(); - - // To apply event selection and store the collision IDs of reconstructed tracks that pass the selection criteria + refClassIds.clear(); + refCentByMcId.clear(); + // To apply event selection and store the collision IDs of reconstructed events that pass the selection criteria for (const auto& coll : events) { - if (!coll.has_mcCollision()) + if (!coll.has_mcCollision()) { continue; - + } + histos.fill(HIST("hEvtSelInfoMc"), 0.5); const auto mcid = coll.mcCollisionId(); - const auto mccoll = coll.template mcCollision_as>(); - const float lCentrality = mccoll.centFT0M(); + const auto mccoll = coll.template mcCollision_as>(); + const float lCentrality = getCentrality(mccoll); if (doprocessMC && isQaRequired) { histos.fill(HIST("QA/MC/QACent_woCut"), lCentrality); histos.fill(HIST("QA/MC/QAvtxz_woCut"), coll.posZ()); } - - if (!colCuts.isSelected(coll)) + if (!colCuts.isSelected(coll)) { continue; - if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(coll)) + } + histos.fill(HIST("hEvtSelInfoMc"), 1.5); + if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(coll)) { continue; - if (!coll.isInelGt0()) + } + histos.fill(HIST("hEvtSelInfoMc"), 2.5); + if (lCentrality < eventCutCfgs.cfgEventCentralityMin || lCentrality > eventCutCfgs.cfgEventCentralityMax) { continue; + } + histos.fill(HIST("hEvtSelInfoMc"), 3.5); + if (!coll.isInelGt0()) { + continue; + } + histos.fill(HIST("hEvtSelInfoMc"), 4.5); colCuts.fillQA(coll); if (doprocessMC && isQaRequired) { @@ -1327,24 +1423,51 @@ struct Chargedkstaranalysis { histos.fill(HIST("QA/MC/QAvtxz_wVtxzCut"), coll.posZ()); } - if (lCentrality < eventCutCfgs.cfgEventCentralityMin || lCentrality > eventCutCfgs.cfgEventCentralityMax) - continue; - if (doprocessMC && isQaRequired) { histos.fill(HIST("QA/MC/QACent_wCentCut"), lCentrality); } allowedMcIds.insert(mcid); centTruthByAllowed.emplace(mcid, lCentrality); } + + // To builds a list of accepted MC collisions ID's for the truth candidates: i.e. the total number of generated events + for (const auto& coll : mccolls) { + bool pass = true; + + if (cfgTruthIncludeZvtx && std::abs(coll.posZ()) >= eventCutCfgs.confEvtZvtx) { + pass = false; + } + + if (pass && cfgTruthUseInelGt0) { + auto partsThisMc = mcParticles.sliceBy(perMCCollision, coll.globalIndex()); // This is to slice all MC particles belonging to the current MC collision. + // To check the INEL > 0 + if (!pwglf::isINELgtNmc(partsThisMc, 0, pdg)) { + pass = false; + } + } + + if (!pass) { + continue; + } + + const auto mcid = coll.globalIndex(); + refClassIds.insert(mcid); + const float lCentrality = getCentrality(coll); + refCentByMcId.emplace(mcid, lCentrality); + } + currentIsGen = true; + sigLossDen = false; // Calculating the generated Kstar for (const auto& part : mcParticles) { - currentIsGen = true; - if (!part.has_mcCollision()) + if (!part.has_mcCollision()) { continue; - if (std::abs(part.pdgCode()) != kKstarPlus) + } + if (std::abs(part.pdgCode()) != kKstarPlus) { continue; - if (std::abs(part.y()) > kstarCutCfgs.cKstarMaxRap) + } + if (std::abs(part.y()) > kstarCutCfgs.cKstarMaxRap) { continue; + } LorentzVectorSetXYZM lResoSecondary, lDecayDaughter_bach, lResoKstar, lDaughterRot; lResoKstar = LorentzVectorSetXYZM(part.px(), part.py(), part.pz(), MassKPlusStar892); @@ -1355,16 +1478,37 @@ struct Chargedkstaranalysis { const int pdg1 = d1.pdgCode(); if (pdg1 == pionWanted) { lDecayDaughter_bach = LorentzVectorSetXYZM(d1.px(), d1.py(), d1.pz(), MassPionCharged); + if (helicityCfgs.genKinematicsChecks) { + if (lDecayDaughter_bach.pt() <= trackCutCfgs.cMinPtcut || std::abs(lDecayDaughter_bach.eta()) >= trackCutCfgs.cMaxEtacut) { + continue; + } + } hasRightPion = true; } else if (std::abs(pdg1) == kPDGK0) { for (const auto& d2 : d1.template daughters_as()) { if (std::abs(d2.pdgCode()) == kPDGK0s) { + if (helicityCfgs.genKinematicsChecks) { + if (d2.pt() <= secondaryCutsCfgs.cSecondaryPtMin || std::abs(d2.eta()) >= secondaryCutsCfgs.cSecondaryRapidityMax) { + continue; + } + } bool seenPip = false, seenPim = false; for (const auto& d3 : d2.template daughters_as()) { - if (d3.pdgCode() == +kPiPlus) + if (d3.pdgCode() == +kPiPlus) { + if (helicityCfgs.genKinematicsChecks) { + if (d3.pt() <= trackCutCfgs.cMinPtcut || std::abs(d3.eta()) >= trackCutCfgs.cMaxEtacut) { + continue; + } + } seenPip = true; - else if (d3.pdgCode() == -kPiPlus) + } else if (d3.pdgCode() == -kPiPlus) { + if (helicityCfgs.genKinematicsChecks) { + if (d3.pt() <= trackCutCfgs.cMinPtcut || std::abs(d3.eta()) >= trackCutCfgs.cMaxEtacut) { + continue; + } + } seenPim = true; + } } if (seenPip && seenPim) { lResoSecondary = LorentzVectorSetXYZM(d2.px(), d2.py(), d2.pz(), MassK0Short); @@ -1374,69 +1518,170 @@ struct Chargedkstaranalysis { } } } - if (hasRightPion && hasK0sToPipi) + if (hasRightPion && hasK0sToPipi) { break; + } } - if (!(hasRightPion && hasK0sToPipi)) + if (!(hasRightPion && hasK0sToPipi)) { continue; + } const auto mcid = part.mcCollisionId(); - if (allowedMcIds.count(mcid) == 0) + if (!allowedMcIds.contains(mcid)) { continue; + } auto iter = centTruthByAllowed.find(mcid); - if (iter == centTruthByAllowed.end()) + if (iter == centTruthByAllowed.end()) { continue; + } const float lCentrality = iter->second; histos.fill(HIST("EffKstar/genKstar"), part.pt(), lCentrality); - if (helicityCfgs.cBoostKShot) { + if (part.vt() == 0) { + histos.fill(HIST("EffKstar/genKstar_pri"), part.pt(), lCentrality); // To check the primary particle + if (helicityCfgs.cCosWithKShot) { + fillInvMass(lResoKstar, lCentrality, lResoSecondary, lDecayDaughter_bach, eventCutCfgs.confIsMix); + } else { + fillInvMass(lResoKstar, lCentrality, lDecayDaughter_bach, lResoSecondary, eventCutCfgs.confIsMix); + } + } + } + sigLossDen = true; + // To calculate the denominator -> To check the all the events have chk892 + for (auto const& part : mcParticles) { + if (!part.has_mcCollision()) { + continue; + } + if (std::abs(part.pdgCode()) != kKstarPlus) { + continue; + } + if (std::abs(part.y()) > kstarCutCfgs.cKstarMaxRap) { + continue; + } + + const auto mcid = part.mcCollisionId(); + if (!refClassIds.contains(mcid)) { + continue; + } + + auto iter = refCentByMcId.find(mcid); + if (iter == refCentByMcId.end()) { + continue; + } + + const float lCentrality = iter->second; + + histos.fill(HIST("Correction/sigLoss_den"), part.pt(), lCentrality); + if (part.vt() == 0) { + histos.fill(HIST("Correction/sigLoss_den_pri"), part.pt(), lCentrality); + } + LorentzVectorSetXYZM lResoSecondary, lDecayDaughter_bach, lResoKstar, lDaughterRot; + lResoKstar = LorentzVectorSetXYZM(part.px(), part.py(), part.pz(), MassKPlusStar892); + const int pionWanted = (part.pdgCode() > 0) ? +kPiPlus : -kPiPlus; + bool hasRightPion = false; + bool hasK0sToPipi = false; + for (const auto& d1 : part.template daughters_as()) { + const int pdg1 = d1.pdgCode(); + if (pdg1 == pionWanted) { + lDecayDaughter_bach = LorentzVectorSetXYZM(d1.px(), d1.py(), d1.pz(), MassPionCharged); + if (helicityCfgs.genKinematicsChecks) { + if (lDecayDaughter_bach.pt() <= trackCutCfgs.cMinPtcut || std::abs(lDecayDaughter_bach.eta()) >= trackCutCfgs.cMaxEtacut) { + continue; + } + } + hasRightPion = true; + } else if (std::abs(pdg1) == kPDGK0) { + for (const auto& d2 : d1.template daughters_as()) { + if (std::abs(d2.pdgCode()) == kPDGK0s) { + if (helicityCfgs.genKinematicsChecks) { + if (d2.pt() <= secondaryCutsCfgs.cSecondaryPtMin || std::abs(d2.eta()) >= secondaryCutsCfgs.cSecondaryRapidityMax) { + continue; + } + } + bool seenPip = false, seenPim = false; + for (const auto& d3 : d2.template daughters_as()) { + if (d3.pdgCode() == +kPiPlus) { + if (helicityCfgs.genKinematicsChecks) { + if (d3.pt() <= trackCutCfgs.cMinPtcut || std::abs(d3.eta()) >= trackCutCfgs.cMaxEtacut) { + continue; + } + } + seenPip = true; + } else if (d3.pdgCode() == -kPiPlus) { + if (helicityCfgs.genKinematicsChecks) { + if (d3.pt() <= trackCutCfgs.cMinPtcut || std::abs(d3.eta()) >= trackCutCfgs.cMaxEtacut) { + continue; + } + } + seenPim = true; + } + } + if (seenPip && seenPim) { + lResoSecondary = LorentzVectorSetXYZM(d2.px(), d2.py(), d2.pz(), MassK0Short); + hasK0sToPipi = true; + break; + } + } + } + } + if (hasRightPion && hasK0sToPipi) { + break; + } + } + + if (!(hasRightPion && hasK0sToPipi)) { + continue; + } + if (helicityCfgs.cCosWithKShot) { fillInvMass(lResoKstar, lCentrality, lResoSecondary, lDecayDaughter_bach, eventCutCfgs.confIsMix); } else { fillInvMass(lResoKstar, lCentrality, lDecayDaughter_bach, lResoSecondary, eventCutCfgs.confIsMix); } - if (part.vt() == 0) { - histos.fill(HIST("EffKstar/genKstar_pri"), part.pt(), lCentrality); // To check the primary particle - } } + + currentIsGen = false; // To store the recoKstar for (const auto& v0 : v0s) { - currentIsGen = false; auto coll = v0.template collision_as(); - if (!coll.has_mcCollision()) + if (!coll.has_mcCollision()) { continue; - + } const auto mcid = coll.mcCollisionId(); - - if (allowedMcIds.count(mcid) == 0) + if (!allowedMcIds.contains(mcid)) { continue; // To check the event is allowed or not + } - const auto mccoll = coll.template mcCollision_as>(); - const float lCentrality = mccoll.centFT0M(); + const auto mccoll = coll.template mcCollision_as>(); + const float lCentrality = getCentrality(mccoll); if (!secondaryCutsCfgs.cfgByPassDauPIDSelection) { auto posDauTrack = v0.template posTrack_as(); auto negDauTrack = v0.template negTrack_as(); - if (!selectionPIDPion(posDauTrack)) + if (!selectionPIDPion(posDauTrack)) { continue; - if (!selectionPIDPion(negDauTrack)) + } + if (!selectionPIDPion(negDauTrack)) { continue; + } } - if (!selectionK0s(coll, v0)) + if (!selectionK0s(coll, v0)) { continue; - + } auto trks = tracks.sliceBy(perCollision, v0.collisionId()); // Grouping the tracks with the v0s, means only those tracks that belong to the same collision as v0 for (const auto& bTrack : trks) { - if (bTrack.collisionId() != v0.collisionId()) + if (bTrack.collisionId() != v0.collisionId()) { continue; - if (!trackCut(bTrack)) + } + if (!trackCut(bTrack)) { continue; - if (!selectionPIDPion(bTrack)) + } + if (!selectionPIDPion(bTrack)) { continue; - + } LorentzVectorSetXYZM lResoSecondary, lDecayDaughter_bach, lResoKstar, lDaughterRot; lResoSecondary = LorentzVectorSetXYZM(v0.px(), v0.py(), v0.pz(), MassK0Short); @@ -1445,18 +1690,32 @@ struct Chargedkstaranalysis { const double ptreco = lResoKstar.Pt(); const double yreco = lResoKstar.Rapidity(); - if (std::abs(yreco) > kstarCutCfgs.cKstarMaxRap) + if (std::abs(yreco) > kstarCutCfgs.cKstarMaxRap) { continue; + } + if (mcCfgs.doMcClosure) { + // For MC clousre test + mcClosure = true; + if (helicityCfgs.cCosWithKShot) { + fillInvMass(lResoKstar, lCentrality, lResoSecondary, lDecayDaughter_bach, eventCutCfgs.confIsMix); + } else { + fillInvMass(lResoKstar, lCentrality, lDecayDaughter_bach, lResoSecondary, eventCutCfgs.confIsMix); + } + } + mcClosure = false; // Since we are doing the MC study and we know about the PDG code of each particle let's try to check the things which we have - if (!v0.has_mcParticle() || !bTrack.has_mcParticle()) + if (!v0.has_mcParticle() || !bTrack.has_mcParticle()) { continue; + } auto mcK0s = v0.template mcParticle_as(); // To get the MC truth particle corressponds to the V0 candidate auto mcPi = bTrack.template mcParticle_as(); - if (std::abs(mcK0s.pdgCode()) != kPDGK0s) + if (std::abs(mcK0s.pdgCode()) != kPDGK0s) { continue; - if (std::abs(mcPi.pdgCode()) != kPiPlus) + } + if (std::abs(mcPi.pdgCode()) != kPiPlus) { continue; + } MCTrueTrackCandidates::iterator kstarFromPi; bool havePiKstar = false; // Loops over all the mother's of pions and check if this pion comming from a kstar @@ -1470,8 +1729,27 @@ struct Chargedkstaranalysis { if (!havePiKstar) { continue; } + // Loops over all the mother's of K0s and check if this K0s comming from a kstar and also share the smae mother as of the pion + bool shareSameKstar = false; + for (const auto& m1 : mcK0s.template mothers_as()) { + if (std::abs(m1.pdgCode()) == kPDGK0) { + for (const auto& m2 : m1.template mothers_as()) { + if (m2.globalIndex() == kstarFromPi.globalIndex()) { + shareSameKstar = true; + break; + } + } + if (shareSameKstar) { + break; + } + } + } + if (!shareSameKstar) { + continue; + } + histos.fill(HIST("EffKstar/recoKstar"), ptreco, lCentrality); - if (helicityCfgs.cBoostKShot) { + if (helicityCfgs.cCosWithKShot) { fillInvMass(lResoKstar, lCentrality, lResoSecondary, lDecayDaughter_bach, eventCutCfgs.confIsMix); } else { fillInvMass(lResoKstar, lCentrality, lDecayDaughter_bach, lResoSecondary, eventCutCfgs.confIsMix); @@ -1479,9 +1757,74 @@ struct Chargedkstaranalysis { histos.fill(HIST("MCReco/hInvmass_Kstar_true"), lCentrality, ptreco, lResoKstar.M()); } } + // To calculate the event losses to store the generated KstartPlus --> To check the number of events remains after passing all the event selection criteria for chk892 + for (auto const& part : mcParticles) { + if (!part.has_mcCollision()) { + continue; + } + if (std::abs(part.pdgCode()) != kKstarPlus) { + continue; + } + if (std::abs(part.y()) > kstarCutCfgs.cKstarMaxRap) { + continue; + } + + const auto mcid = part.mcCollisionId(); + if (!allowedMcIds.contains(mcid)) { + continue; + } + + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) { + continue; + } + + const float lCentrality = iter->second; + + histos.fill(HIST("Correction/sigLoss_num"), part.pt(), lCentrality); + if (part.vt() == 0) { + histos.fill(HIST("Correction/sigLoss_num_pri"), part.pt(), lCentrality); + } + } + // To calculate the event fraction correction + for (const auto& mcid : refClassIds) { + histos.fill(HIST("Correction/EF_den"), refCentByMcId[mcid]); + } + for (const auto& mcid : allowedMcIds) { + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) { + continue; + } + + const float lCentrality = iter->second; + histos.fill(HIST("Correction/EF_num"), lCentrality); + } + + for (auto const& mcc : mccolls) { + const auto mcid = mcc.globalIndex(); + + histos.fill(HIST("Correction/hNEventsMCTruth"), 1.0); + + bool passZvtx = true; + if (cfgTruthIncludeZvtx && std::abs(mcc.posZ()) > eventCutCfgs.confEvtZvtx) { + passZvtx = false; + } + if (passZvtx) { + histos.fill(HIST("Correction/hNEventsMCTruth"), 2.0); + + auto partsThisMc = mcParticles.sliceBy(perMCCollision, mcid); + if (pwglf::isINELgtNmc(partsThisMc, 0, pdg)) { + histos.fill(HIST("Correction/hNEventsMCTruth"), 3.0); + } + } + if (allowedMcIds.contains(mcid)) { + histos.fill(HIST("Correction/hNEventsMCTruth"), 4.0); + } + } } PROCESS_SWITCH(Chargedkstaranalysis, processMC, "Process Event for MC", false); }; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; diff --git a/PWGLF/Tasks/Resonances/chk892LI.cxx b/PWGLF/Tasks/Resonances/chk892LI.cxx new file mode 100644 index 00000000000..db27c412708 --- /dev/null +++ b/PWGLF/Tasks/Resonances/chk892LI.cxx @@ -0,0 +1,1662 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file chk892LI.cxx +/// \brief Reconstruction of track-track decay resonance candidates +/// +/// +/// \author Su-Jeong Ji , Bong-Hwi Lim + +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/collisionCuts.h" +#include "PWGLF/Utils/inelGt.h" + +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include // FIXME + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; +using namespace o2::constants::physics; +using namespace o2::aod::rctsel; + +namespace +{ +template +inline bool getTruthK0sAndGenKinematics(V0T const& v0, double& ptgen, double& ygen) +{ + if (!v0.has_mcParticle()) + return false; + auto mcPart = v0.template mcParticle_as(); + if (mcPart.pdgCode() != kK0Short) + return false; + ptgen = mcPart.pt(); + ygen = mcPart.y(); + return true; +} +} // namespace + +struct Chk892LI { + enum BinType : unsigned int { + kKstarP = 0, + kKstarN, + kKstarP_Mix, + kKstarN_Mix, + kKstarP_Rot, + kKstarN_Rot, + kTYEnd + }; + + enum EvtStep { + kAll = 0, + kZvtx, + kINELgt0, + kAssocReco, + kNSteps + }; + + const int nSteps = static_cast(EvtStep::kNSteps); + + SliceCache cache; + Preslice perMCCollision = o2::aod::mcparticle::mcCollisionId; + + using EventCandidates = soa::Join; + using TrackCandidates = soa::Join; + using V0Candidates = aod::V0Datas; + + // for MC reco + using MCEventCandidates = soa::Join; + using MCTrackCandidates = soa::Join; //, aod::McParticles>; + using MCV0Candidates = soa::Join; + + Preslice perCollision = aod::track::collisionId; + Preslice perCollisionV0 = aod::v0data::collisionId; + Preslice perCollisionMC = aod::track::collisionId; + PresliceUnsorted perMcRecoCollision = aod::mccollisionlabel::mcCollisionId; + + // for MC truth + using MCTrueEventCandidates = aod::McCollisions; + using MCTrueTrackCandidates = aod::McParticles; + + using LorentzVectorSetXYZM = ROOT::Math::LorentzVector>; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Service ccdb; + Service pdg; + o2::ccdb::CcdbApi ccdbApi; + + struct : ConfigurableGroup { + Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; + } CCDBConfig; + + // Configurables + struct : ConfigurableGroup { + ConfigurableAxis cfgBinsPt{"cfgBinsPt", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0}, "Binning of the pT axis"}; + ConfigurableAxis cfgBinsPtQA{"cfgBinsPtQA", {VARIABLE_WIDTH, 0.0, 0.3, 0.6, 1.2, 1.8, 2.4, 3.0, 3.6, 4.2, 4.8, 5.4, 6.0, 7.0, 10.0}, "Binning of the pT axis"}; + ConfigurableAxis cfgBinsCent{"cfgBinsCent", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0, 110.0}, "Binning of the centrality axis"}; + ConfigurableAxis cfgBinsVtxZ{"cfgBinsVtxZ", {VARIABLE_WIDTH, -10.0, -9.0, -8.0, -7.0, -6.0, -5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0}, "Binning of the z-vertex axis"}; + Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; + Configurable cNbinsDivQA{"cNbinsDivQA", 1, "Integer to divide the number of bins for QA"}; + + Configurable> cfgGenMultCuts{"cfgGenMultCuts", std::vector{500, 300, 200, 120, 80, 50, 30, 10, 0}, "Generated multiplicity lower cuts corresponding to reco centrality bins"}; + Configurable> cfgCentBinCentres{"cfgCentBinCentres", std::vector{2.5, 7.5, 15.0, 25.0, 35.0, 45.0, 55.0, 75.0, 95.0}, "Reco centrality bin centres"}; + } AxisConfig; + + /// Event cuts + o2::analysis::CollisonCuts colCuts; + struct : ConfigurableGroup { + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtOccupancyInTimeRangeMax{"cfgEvtOccupancyInTimeRangeMax", -1, "Evt sel: maximum track occupancy"}; + Configurable cfgEvtOccupancyInTimeRangeMin{"cfgEvtOccupancyInTimeRangeMin", -1, "Evt sel: minimum track occupancy"}; + Configurable cfgEvtTriggerCheck{"cfgEvtTriggerCheck", false, "Evt sel: check for trigger"}; + Configurable cfgEvtOfflineCheck{"cfgEvtOfflineCheck", false, "Evt sel: check for offline selection"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtTFBorderCut{"cfgEvtTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtUseITSTPCvertex{"cfgEvtUseITSTPCvertex", false, "Evt sel: use at lease on ITS-TPC track for vertexing"}; + Configurable cfgEvtZvertexTimedifference{"cfgEvtZvertexTimedifference", false, "Evt sel: apply Z-vertex time difference"}; + Configurable cfgEvtPileupRejection{"cfgEvtPileupRejection", false, "Evt sel: apply pileup rejection"}; + Configurable cfgEvtNoITSROBorderCut{"cfgEvtNoITSROBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgincludeCentralityMC{"cfgincludeCentralityMC", false, "Include centrality in MC"}; + Configurable cfgEvtCollInTimeRangeStandard{"cfgEvtCollInTimeRangeStandard", false, "Evt sel: apply NoCollInTimeRangeStandard"}; + Configurable cfgEventCentralityMin{"cfgEventCentralityMin", 0.0f, "Event sel: minimum centrality"}; + Configurable cfgEventCentralityMax{"cfgEventCentralityMax", 100.0f, "Event sel: maximum centrality"}; + Configurable cfgEvtUseRCTFlagChecker{"cfgEvtUseRCTFlagChecker", false, "Evt sel: use RCT flag checker"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", false, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + } EventCuts; + RCTFlagsChecker rctChecker; + + Configurable cfgFillQAPlots{"cfgFillQAPlots", false, "Fill QA plots"}; + Configurable cfgCentEst{"cfgCentEst", 2, "Centrality estimator, 1: FT0C, 2: FT0M, 3: FV0A"}; + + /// PID Selections, pion + struct : ConfigurableGroup { + Configurable cfgTPConly{"cfgTPConly", true, "Use only TPC for PID"}; // bool + Configurable cfgMaxTPCnSigmaPion{"cfgMaxTPCnSigmaPion", 5.0, "TPC nSigma cut for Pion"}; // TPC + Configurable cfgMaxTOFnSigmaPion{"cfgMaxTOFnSigmaPion", 5.0, "TOF nSigma cut for Pion"}; // TOF + Configurable cfgNsigmaCutCombinedPion{"cfgNsigmaCutCombinedPion", -999, "Combined nSigma cut for Pion"}; // Combined + Configurable cfgTOFVeto{"cfgTOFVeto", false, "TOF Veto, if false, TOF is nessessary for PID selection"}; // TOF Veto + Configurable cfgTOFMinPt{"cfgTOFMinPt", 0.6, "Minimum TOF pT cut for Pion"}; // TOF pT cut + } PIDCuts; + + // Track selections + struct : ConfigurableGroup { + Configurable cfgMinPtcut{"cfgMinPtcut", 0.15, "Track minium pt cut"}; + Configurable cfgMaxEtacut{"cfgMaxEtacut", 0.8, "Track maximum eta cut"}; + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + + Configurable cfgpTdepDCAxyCut{"cfgpTdepDCAxyCut", true, "pT-dependent DCAxy cut"}; + Configurable cfgpTdepDCAzCut{"cfgpTdepDCAzCut", true, "pT-dependent DCAz cut"}; + Configurable cfgITScluster{"cfgITScluster", 0, "Number of ITS cluster"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 0, "Number of TPC cluster"}; + Configurable cfgRatioTPCRowsOverFindableCls{"cfgRatioTPCRowsOverFindableCls", 0.0f, "TPC Crossed Rows to Findable Clusters"}; + Configurable cfgITSChi2NCl{"cfgITSChi2NCl", 999.0, "ITS Chi2/NCl"}; + Configurable cfgTPCChi2NCl{"cfgTPCChi2NCl", 999.0, "TPC Chi2/NCl"}; + Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; + Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; + Configurable cfgHasITS{"cfgHasITS", false, "Require ITS"}; + Configurable cfgHasTPC{"cfgHasTPC", false, "Require TPC"}; + Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; + // DCAr to PV + Configurable cfgMaxbDCArToPVcut{"cfgMaxbDCArToPVcut", 0.1, "Track DCAr cut to PV Maximum"}; + // DCAz to PV + Configurable cfgMaxbDCAzToPVcut{"cfgMaxbDCAzToPVcut", 0.1, "Track DCAz cut to PV Maximum"}; + } TrackCuts; + + // Secondary Selection + struct : ConfigurableGroup { + Configurable cfgReturnFlag{"cfgReturnFlag", false, "Return Flag for debugging"}; + Configurable cfgSecondaryRequire{"cfgSecondaryRequire", true, "Secondary cuts on/off"}; + Configurable cfgSecondaryArmenterosCut{"cfgSecondaryArmenterosCut", true, "cut on Armenteros-Podolanski graph"}; + Configurable cfgSecondaryCrossMassHypothesisCut{"cfgSecondaryCrossMassHypothesisCut", false, "Apply cut based on the lambda mass hypothesis"}; + + Configurable cfgByPassDauPIDSelection{"cfgByPassDauPIDSelection", true, "Bypass Daughters PID selection"}; + Configurable cfgSecondaryDauDCAMax{"cfgSecondaryDauDCAMax", 1., "Maximum DCA Secondary daughters to PV"}; + Configurable cfgSecondaryDauPosDCAtoPVMin{"cfgSecondaryDauPosDCAtoPVMin", 0.1, "Minimum DCA Secondary positive daughters to PV"}; + Configurable cfgSecondaryDauNegDCAtoPVMin{"cfgSecondaryDauNegDCAtoPVMin", 0.1, "Minimum DCA Secondary negative daughters to PV"}; + + Configurable cfgSecondaryPtMin{"cfgSecondaryPtMin", 0.f, "Minimum transverse momentum of Secondary"}; + Configurable cfgSecondaryRapidityMax{"cfgSecondaryRapidityMax", 0.8, "Maximum rapidity of Secondary"}; + Configurable cfgSecondaryRadiusMin{"cfgSecondaryRadiusMin", 0, "Minimum transverse radius of Secondary"}; + Configurable cfgSecondaryRadiusMax{"cfgSecondaryRadiusMax", 999.9, "Maximum transverse radius of Secondary"}; + Configurable cfgSecondaryCosPAMin{"cfgSecondaryCosPAMin", 0.995, "Mininum cosine pointing angle of Secondary"}; + Configurable cfgSecondaryDCAtoPVMax{"cfgSecondaryDCAtoPVMax", 0.4, "Maximum DCA Secondary to PV"}; + Configurable cfgSecondaryProperLifetimeMax{"cfgSecondaryProperLifetimeMax", 20, "Maximum Secondary Lifetime"}; + Configurable cfgSecondaryparamArmenterosCut{"cfgSecondaryparamArmenterosCut", 0.2, "parameter for Armenteros Cut"}; + Configurable cfgSecondaryMassWindow{"cfgSecondaryMassWindow", 0.03, "Secondary inv mass selciton window"}; + Configurable cfgSecondaryCrossMassCutWindow{"cfgSecondaryCrossMassCutWindow", 0.05, "Secondary inv mass selection window with (anti)lambda hypothesis"}; + } SecondaryCuts; + + // K* selection + struct : ConfigurableGroup { + Configurable cfgKstarMaxRap{"cfgKstarMaxRap", 0.5, "Kstar maximum rapidity"}; + Configurable cfgKstarMinRap{"cfgKstarMinRap", -0.5, "Kstar minimum rapidity"}; + } KstarCuts; + + // Bkg estimation + struct : ConfigurableGroup { + Configurable cfgFillRotBkg{"cfgFillRotBkg", true, "Fill rotated background"}; + Configurable cfgMinRot{"cfgMinRot", 5.0 * constants::math::PI / 6.0, "Minimum of rotation"}; + Configurable cfgMaxRot{"cfgMaxRot", 7.0 * constants::math::PI / 6.0, "Maximum of rotation"}; + Configurable cfgRotPion{"cfgRotPion", true, "Rotate pion"}; + Configurable cfgNrotBkg{"cfgNrotBkg", 4, "Number of rotated copies (background) per each original candidate"}; + } BkgEstimationConfig; + + Configurable cfgRecoUseInelGt0{"cfgRecoUseInelGt0", false, "Data/Reco require INEL>0"}; + Configurable cfgTruthUseInelGt0{"cfgTruthUseInelGt0", false, "Truth denominator: require INEL>0"}; + Configurable cfgTruthIncludeZvtx{"cfgTruthIncludeZvtx", false, "Truth denominator: also require |vtxz| cfgGenMultEtaMax{"cfgGenMultEtaMax", 0.5, "Max Eta for generated mid-rapidity multiplicity"}; + Configurable cfgGenMultEtaMin{"cfgGenMultEtaMin", -0.5, "Min Eta for generated mid-rapidity multiplicity"}; + + float lCentrality; + + // PDG code + int kPDGK0s = kK0Short; + int kPDGK0 = kK0; + int kKstarPlus = o2::constants::physics::Pdg::kKPlusStar892; + // int kPiPlus = 211; + double fMaxPosPV = 1e-2; + + void init(o2::framework::InitContext&) + { + lCentrality = -999; + + // setCuts(float zvtxMax, bool checkTrigger, bool checkOffline, bool checkRun3, bool triggerTVXsel = false, int trackOccupancyInTimeRangeMax = -1, int trackOccupancyInTimeRangeMin = -1) + colCuts.setCuts(EventCuts.cfgEvtZvtx, EventCuts.cfgEvtTriggerCheck, EventCuts.cfgEvtOfflineCheck, /*checkRun3*/ true, EventCuts.cfgEvtTriggerTVXSel, EventCuts.cfgEvtOccupancyInTimeRangeMax, EventCuts.cfgEvtOccupancyInTimeRangeMin); + colCuts.init(&histos); + colCuts.setTriggerTVX(EventCuts.cfgEvtTriggerTVXSel); + colCuts.setApplyTFBorderCut(EventCuts.cfgEvtTFBorderCut); + colCuts.setApplyNoITSROBorderCut(EventCuts.cfgEvtNoITSROBorderCut); + colCuts.setApplyITSTPCvertex(EventCuts.cfgEvtUseITSTPCvertex); + colCuts.setApplyZvertexTimedifference(EventCuts.cfgEvtZvertexTimedifference); + colCuts.setApplyPileupRejection(EventCuts.cfgEvtPileupRejection); + colCuts.setApplyCollInTimeRangeStandard(EventCuts.cfgEvtCollInTimeRangeStandard); + colCuts.printCuts(); + + rctChecker.init(EventCuts.cfgEvtRCTFlagCheckerLabel, EventCuts.cfgEvtRCTFlagCheckerZDCCheck, EventCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + AxisSpec centAxis = {AxisConfig.cfgBinsCent, "T0M (%)"}; + AxisSpec vtxzAxis = {AxisConfig.cfgBinsVtxZ, "Z Vertex (cm)"}; + AxisSpec epAxis = {100, -1.0 * constants::math::PI, constants::math::PI}; + AxisSpec epresAxis = {100, -1.02, 1.02}; + AxisSpec ptAxis = {AxisConfig.cfgBinsPt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec ptAxisQA = {AxisConfig.cfgBinsPtQA, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec radiusAxis = {50, 0, 5, "Radius (cm)"}; + AxisSpec cpaAxis = {50, 0.95, 1.0, "CPA"}; + AxisSpec tauAxis = {250, 0, 25, "Lifetime (cm)"}; + AxisSpec dcaAxis = {200, 0, 2, "DCA (cm)"}; + AxisSpec dcaxyAxis = {200, 0, 2, "DCA_{#it{xy}} (cm)"}; + AxisSpec dcazAxis = {200, 0, 2, "DCA_{#it{z}} (cm)"}; + AxisSpec yAxis = {100, -1, 1, "Rapidity"}; + AxisSpec invMassAxisK0s = {800 / AxisConfig.cNbinsDiv, 0.46, 0.54, "Invariant Mass (GeV/#it{c}^2)"}; // K0s ~497.611 + AxisSpec invMassAxisReso = {900 / AxisConfig.cNbinsDiv, 0.5f, 1.4f, "Invariant Mass (GeV/#it{c}^2)"}; // chK(892) ~892 + AxisSpec pidQAAxis = {130 / AxisConfig.cNbinsDivQA, -6.5, 6.5}; + AxisSpec dataTypeAxis = {9, 0, 9, "Histogram types"}; + AxisSpec mcTypeAxis = {4, 0, 4, "Histogram types"}; + AxisSpec cutAxis{14, 0.5, 14.5, "Check"}; // 1..14 + + // THnSparse + AxisSpec axisType = {BinType::kTYEnd, 0, BinType::kTYEnd, "Type of bin with charge and mix"}; + AxisSpec mcLabelAxis = {5, -0.5, 4.5, "MC Label"}; + + histos.add("QA/K0sCutCheck", "Check K0s cut", HistType::kTH1D, {AxisSpec{13, -0.5, 12.5, "Check"}}); + histos.add("QA/K0sCutFlow", "Check K0s cut (first-fail or pass)", HistType::kTH1F, {cutAxis}); + auto hcut = histos.get(HIST("QA/K0sCutFlow")); + hcut->GetXaxis()->SetBinLabel(1, "TOTAL"); + hcut->GetXaxis()->SetBinLabel(2, "PASS"); + hcut->GetXaxis()->SetBinLabel(3, "DauDCA>max"); + hcut->GetXaxis()->SetBinLabel(4, "PosDCAtoPVGetXaxis()->SetBinLabel(5, "NegDCAtoPVGetXaxis()->SetBinLabel(6, "pTGetXaxis()->SetBinLabel(7, "|y|>max"); + hcut->GetXaxis()->SetBinLabel(8, "Rmax"); + hcut->GetXaxis()->SetBinLabel(9, "DCAtoPV>max"); + hcut->GetXaxis()->SetBinLabel(10, "cosPAGetXaxis()->SetBinLabel(11, "ctau>max"); + hcut->GetXaxis()->SetBinLabel(12, "qtarmGetXaxis()->SetBinLabel(13, "|M(K0s)-m0|>win"); + hcut->GetXaxis()->SetBinLabel(14, "cross-mass veto"); + + histos.add("QA/before/CentDist", "Centrality distribution", {HistType::kTH1D, {centAxis}}); + histos.add("QA/before/VtxZ", "Centrality distribution", {HistType::kTH1D, {vtxzAxis}}); + histos.add("QA/before/hEvent", "Number of Events", HistType::kTH1F, {{1, 0.5, 1.5}}); + + if (BkgEstimationConfig.cfgFillRotBkg) { + histos.add("QA/RotBkg/hRotBkg", "Rotated angle of rotated background", HistType::kTH1F, {{360, 0.0, o2::constants::math::TwoPI}}); + } + + // Bachelor pion + histos.add("QA/before/trkbpionDCAxy", "DCAxy distribution of bachelor pion candidates", HistType::kTH1D, {dcaxyAxis}); + histos.add("QA/before/trkbpionDCAz", "DCAz distribution of bachelor pion candidates", HistType::kTH1D, {dcazAxis}); + histos.add("QA/before/trkbpionpT", "pT distribution of bachelor pion candidates", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/before/trkbpionTPCPID", "TPC PID of bachelor pion candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/before/trkbpionTOFPID", "TOF PID of bachelor pion candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/before/trkbpionTPCTOFPID", "TPC-TOF PID map of bachelor pion candidates", HistType::kTH2D, {pidQAAxis, pidQAAxis}); + + histos.add("QA/after/trkbpionDCAxy", "DCAxy distribution of bachelor pion candidates", HistType::kTH1D, {dcaxyAxis}); + histos.add("QA/after/trkbpionDCAz", "DCAz distribution of bachelor pion candidates", HistType::kTH1D, {dcazAxis}); + histos.add("QA/after/trkbpionpT", "pT distribution of bachelor pion candidates", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/after/trkbpionTPCPID", "TPC PID of bachelor pion candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/after/trkbpionTOFPID", "TOF PID of bachelor pion candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/after/trkbpionTPCTOFPID", "TPC-TOF PID map of bachelor pion candidates", HistType::kTH2D, {pidQAAxis, pidQAAxis}); + + // Secondary pion 1 + histos.add("QA/before/trkppionTPCPID", "TPC PID of secondary pion 1 (positive) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/before/trkppionTOFPID", "TOF PID of secondary pion 1 (positive) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/before/trkppionTPCTOFPID", "TPC-TOF PID map of secondary pion 1 (positive) candidates", HistType::kTH2D, {pidQAAxis, pidQAAxis}); + histos.add("QA/before/trkppionpT", "pT distribution of secondary pion 1 (positive) candidates", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/before/trkppionDCAxy", "DCAxy distribution of secondary pion 1 (positive) candidates", HistType::kTH1D, {dcaxyAxis}); + histos.add("QA/before/trkppionDCAz", "DCAz distribution of secondary pion 1 (positive) candidates", HistType::kTH1D, {dcazAxis}); + + histos.add("QA/after/trkppionTPCPID", "TPC PID of secondary pion 1 (positive) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/after/trkppionTOFPID", "TOF PID of secondary pion 1 (positive) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/after/trkppionTPCTOFPID", "TPC-TOF PID map of secondary pion 1 (positive) candidates", HistType::kTH2D, {pidQAAxis, pidQAAxis}); + histos.add("QA/after/trkppionpT", "pT distribution of secondary pion 1 (positive) candidates", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/after/trkppionDCAxy", "DCAxy distribution of secondary pion 1 (positive) candidates", HistType::kTH1D, {dcaxyAxis}); + histos.add("QA/after/trkppionDCAz", "DCAz distribution of secondary pion 1 (positive) candidates", HistType::kTH1D, {dcazAxis}); + + // Secondary pion 2 + histos.add("QA/before/trknpionTPCPID", "TPC PID of secondary pion 2 (negative) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/before/trknpionTOFPID", "TOF PID of secondary pion 2 (negative) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/before/trknpionTPCTOFPID", "TPC-TOF PID map of secondary pion 2 (negative) candidates", HistType::kTH2D, {pidQAAxis, pidQAAxis}); + histos.add("QA/before/trknpionpT", "pT distribution of secondary pion 2 (negative) candidates", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/before/trknpionDCAxy", "DCAxy distribution of secondary pion 2 (negative) candidates", HistType::kTH1D, {dcaxyAxis}); + histos.add("QA/before/trknpionDCAz", "DCAz distribution of secondary pion 2 (negative) candidates", HistType::kTH1D, {dcazAxis}); + + histos.add("QA/after/trknpionTPCPID", "TPC PID of secondary pion 2 (negative) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/after/trknpionTOFPID", "TOF PID of secondary pion 2 (negative) candidates", HistType::kTH2D, {ptAxisQA, pidQAAxis}); + histos.add("QA/after/trknpionTPCTOFPID", "TPC-TOF PID map of secondary pion 2 (negative) candidates", HistType::kTH2D, {pidQAAxis, pidQAAxis}); + histos.add("QA/after/trknpionpT", "pT distribution of secondary pion 2 (negative) candidates", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/after/trknpionDCAxy", "DCAxy distribution of secondary pion 2 (negative) candidates", HistType::kTH1D, {dcaxyAxis}); + histos.add("QA/after/trknpionDCAz", "DCAz distribution of secondary pion 2 (negative) candidates", HistType::kTH1D, {dcazAxis}); + + // K0s + histos.add("QA/before/hDauDCASecondary", "DCA of daughters of secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/before/hDauPosDCAtoPVSecondary", "Pos DCA to PV of daughters secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/before/hDauNegDCAtoPVSecondary", "Neg DCA to PV of daughters secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/before/hpT_Secondary", "pT distribution of secondary resonance", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/before/hy_Secondary", "Rapidity distribution of secondary resonance", HistType::kTH1D, {yAxis}); + histos.add("QA/before/hRadiusSecondary", "Radius distribution of secondary resonance", HistType::kTH1D, {radiusAxis}); + histos.add("QA/before/hCPASecondary", "Cosine pointing angle distribution of secondary resonance", HistType::kTH1D, {cpaAxis}); + histos.add("QA/before/hDCAtoPVSecondary", "DCA to PV distribution of secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/before/hPropTauSecondary", "Proper Lifetime distribution of secondary resonance", HistType::kTH1D, {tauAxis}); + histos.add("QA/before/hPtAsymSecondary", "pT asymmetry distribution of secondary resonance", HistType::kTH1D, {AxisSpec{100, -1, 1, "Pair asymmetry"}}); + histos.add("QA/before/hArmSecondary", "Armenteros distribution of secondary resonance", HistType::kTH2D, {AxisSpec{100, -1, 1, "alpha"}, {200, 0, 0.5, "qtArm"}}); + histos.add("QA/before/hInvmassSecondary", "Invariant mass of unlike-sign secondary resonance", HistType::kTH1D, {invMassAxisK0s}); + + histos.add("QA/after/hDauDCASecondary", "DCA of daughters of secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/after/hDauPosDCAtoPVSecondary", "Pos DCA to PV of daughters secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/after/hDauNegDCAtoPVSecondary", "Neg DCA to PV of daughters secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/after/hpT_Secondary", "pT distribution of secondary resonance", HistType::kTH1D, {ptAxisQA}); + histos.add("QA/after/hy_Secondary", "Rapidity distribution of secondary resonance", HistType::kTH1D, {yAxis}); + histos.add("QA/after/hRadiusSecondary", "Radius distribution of secondary resonance", HistType::kTH1D, {radiusAxis}); + histos.add("QA/after/hCPASecondary", "Cosine pointing angle distribution of secondary resonance", HistType::kTH1D, {cpaAxis}); + histos.add("QA/after/hDCAtoPVSecondary", "DCA to PV distribution of secondary resonance", HistType::kTH1D, {dcaAxis}); + histos.add("QA/after/hPropTauSecondary", "Proper Lifetime distribution of secondary resonance", HistType::kTH1D, {tauAxis}); + histos.add("QA/after/hPtAsymSecondary", "pT asymmetry distribution of secondary resonance", HistType::kTH1D, {AxisSpec{100, -1, 1, "Pair asymmetry"}}); + histos.add("QA/after/hArmSecondary", "Armenteros distribution of secondary resonance", HistType::kTH2D, {AxisSpec{100, -1, 1, "alpha"}, {200, 0, 0.5, "qtArm"}}); + histos.add("QA/after/hInvmassSecondary", "Invariant mass of unlike-sign secondary resonance", HistType::kTH1D, {invMassAxisK0s}); + + // Kstar + // Invariant mass nSparse + histos.add("QA/before/KstarRapidity", "Rapidity distribution of chK(892)", HistType::kTH1D, {yAxis}); + histos.add("hInvmass_Kstar", "Invariant mass of unlike-sign chK(892)", HistType::kTHnSparseD, {axisType, centAxis, ptAxis, invMassAxisReso}); + histos.add("hInvmass_Kstar_Mix", "Invariant mass of unlike-sign chK(892) from mixed event", HistType::kTHnSparseD, {axisType, centAxis, ptAxis, invMassAxisReso}); + histos.add("hInvmass_K0s", "Invariant mass of unlike-sign K0s", HistType::kTHnSparseD, {centAxis, ptAxis, invMassAxisK0s}); + + // Mass QA (quick check) + histos.add("QA/before/kstarinvmass", "Invariant mass of unlike-sign chK(892)", HistType::kTH1D, {invMassAxisReso}); + histos.add("QA/before/kstarinvmass_Mix", "Invariant mass of unlike-sign chK(892) from mixed event", HistType::kTH1D, {invMassAxisReso}); + + histos.add("QA/after/KstarRapidity", "Rapidity distribution of chK(892)", HistType::kTH1D, {yAxis}); + histos.add("QA/after/kstarinvmass", "Invariant mass of unlike-sign chK(892)", HistType::kTH1D, {invMassAxisReso}); + histos.add("QA/after/kstarinvmass_Mix", "Invariant mass of unlike-sign chK(892) from mixed event", HistType::kTH1D, {invMassAxisReso}); + + // MC + if (doprocessMC) { + + AxisSpec genMultAxis{500, -0.5, 499.5, "N_{ch}^{gen} (|#eta|<0.5)"}; + + histos.add("QACent_woCut", "Centrality without cut", HistType::kTH1F, {centAxis}); + histos.add("QACent_woCentCut", "Centrality without cent cut", HistType::kTH1F, {centAxis}); + histos.add("QACent_wCentCut", "Centrality with cent cut", HistType::kTH1F, {centAxis}); + histos.add("QAMCCent_allowed", "Centrality which allowed", HistType::kTH1F, {centAxis}); + histos.add("QAvtxz_woCut", "z-vertex without cut", HistType::kTH1F, {vtxzAxis}); + histos.add("QAvtxz_wVtxzCut", "z-vertex with vtxz cut", HistType::kTH1F, {vtxzAxis}); + + histos.add("EffK0s/genK0s", "Gen K0s (|y<0.8|)", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("EffK0s/recoK0s", "Reco K0s (|y<0.8|)", HistType::kTH2F, {ptAxis, centAxis}); + + histos.add("EffKstar/genKstar", "Gen Kstar (|y|<0.5)", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("EffKstar/genKstar_pri", "Gen primary Kstar (|y|<0.5)", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("EffKstar/genKstar_pri_pos", "Gen primary Kstar selected by vertex position (|y|<0.5)", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("EffKstar/recoKstar", "Kstar Reco matched (final all)", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("EffKstar/recoKstar_vsGenMult", "Reco K* vs gen mid-rapidity multiplicity;#it{p}_{T};N_{ch}^{gen}", HistType::kTH2D, {ptAxis, genMultAxis}); + histos.add("EffKstar/genKstar_vsGenMult", "Gen K* vs gen mid-rapidity multiplicity;#it{p}_{T};N_{ch}^{gen}", HistType::kTH2D, {ptAxis, genMultAxis}); + histos.add("EffKstar/genKstar_pri_vsGenMult", "Gen primary Kstar (|y|<0.5) vs gen mid-rapidity multiplicity", HistType::kTH2D, {ptAxis, genMultAxis}); + histos.add("EffKstar/genKstar_pri_pos_vsGenMult", "Gen primary Kstar selected by vertex position (|y|<0.5) vs gen mid-rapidity multiplicity", HistType::kTH2D, {ptAxis, genMultAxis}); + + histos.add("Correction/sigLoss_den", "Gen Kstar (|y|<0.5) in truth class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_den_pri", "Gen primary Kstar (|y|<0.5) in truth class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_den_pri_pos", "Gen primary Kstar selected by vertex position (|y|<0.5) in truth class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_num", "Gen Kstar (|y|<0.5, selected events) in reco class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_num_pri", "Gen primary Kstar (|y|<0.5, selected events) in reco class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/sigLoss_num_pri_pos", "Gen primary Kstar selected by vertex position (|y|<0.5, selected events) in reco class", HistType::kTH2F, {ptAxis, centAxis}); + histos.add("Correction/EF_den", "Gen events (truth class)", HistType::kTH1F, {centAxis}); + histos.add("Correction/EF_num", "Reco events (selected events)", HistType::kTH1F, {centAxis}); + histos.add("Correction/RecoCentVsGenMult", "Reco centrality vs generated mid-rapidity multiplicity", HistType::kTH2F, {centAxis, genMultAxis}); + histos.add("Correction/sigLoss_num_vsGenMult", "Generated Kstar in selected events vs gen mult", HistType::kTH2F, {ptAxis, genMultAxis}); + histos.add("Correction/sigLoss_num_vsGenMult_pri", "Generated Kstar in selected events vs gen mult", HistType::kTH2F, {ptAxis, genMultAxis}); + histos.add("Correction/sigLoss_num_vsGenMult_pri_pos", "Generated Kstar in selected events vs gen mult", HistType::kTH2F, {ptAxis, genMultAxis}); + histos.add("Correction/sigLoss_den_vsGenMult", "Generated Kstar vs generated mid-rapidity multiplicity", HistType::kTH2F, {ptAxis, genMultAxis}); + histos.add("Correction/sigLoss_den_vsGenMult_pri", "Generated primary Kstar vs generated mid-rapidity multiplicity", HistType::kTH2F, {ptAxis, genMultAxis}); + histos.add("Correction/sigLoss_den_vsGenMult_pri_pos", "Generated primary Kstar selected by vertex position vs generated mid-rapidity multiplicity", HistType::kTH2F, {ptAxis, genMultAxis}); + histos.add("Correction/EF_num_vsGenMult", "Selected reco-associated events vs gen mult", HistType::kTH1F, {genMultAxis}); + histos.add("Correction/EF_den_vsGenMult", "Truth selected generated events vs generated multiplicity", HistType::kTH1F, {genMultAxis}); + + histos.add("Correction/MCTruthCent_all", "MC truth FT0M centrality (all mcCollisions)", HistType::kTH1F, {centAxis}); + histos.add("Correction/MCTruthCent_allowed", "MC truth FT0M centrality (allowed mcCollisions)", HistType::kTH1F, {centAxis}); + histos.add("Correction/MCTruthCent_cut", "MC truth FT0M centrality (truth selection applied)", HistType::kTH1F, {centAxis}); + + histos.add("Correction/setSizes", "Sizes of sets", HistType::kTH1F, {{4, -0.5, 3.5}}); + auto hset = histos.get(HIST("Correction/setSizes")); + hset->GetXaxis()->SetBinLabel(1, "refClassIds"); + hset->GetXaxis()->SetBinLabel(2, "allowedMcIds"); + hset->GetXaxis()->SetBinLabel(3, "intersection"); + hset->GetXaxis()->SetBinLabel(4, "allowed-only"); + + histos.add("Correction/hNEventsMCTruth", "hNEventsMCTruth", HistType::kTH1F, {AxisSpec{nSteps, 0.5, nSteps + 0.5, ""}}); + auto hstep = histos.get(HIST("Correction/hNEventsMCTruth")); + hstep->GetXaxis()->SetBinLabel(1, "All"); + hstep->GetXaxis()->SetBinLabel(2, "zvtx"); + hstep->GetXaxis()->SetBinLabel(3, "INEL>0"); + hstep->GetXaxis()->SetBinLabel(4, "Assoc with reco coll"); + + histos.add("MCReco/hInvmass_Kstar_true", "MC-reco truth-tagged chK(892)", HistType::kTHnSparseD, {centAxis, ptAxis, invMassAxisReso}); + histos.add("MCReco/hInvmass_Kstar_bkg", "MC-reco residual background chK(892)", HistType::kTHnSparseD, {centAxis, ptAxis, invMassAxisReso}); + } + + ccdb->setURL(CCDBConfig.cfgURL); + ccdbApi.init("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + // Print output histograms statistics + LOG(info) << "Size of the histograms in chK(892) Analysis Task"; + histos.print(); + } + + const int kCentFT0C = 1; + const int kCentFT0M = 2; + const int kCentFV0A = 3; + const float kInvalidCentrality = -999.f; + + template + float getCentrality(CollisionType const& collision) + { + if (cfgCentEst == kCentFT0C) { + return collision.centFT0C(); + } else if (cfgCentEst == kCentFT0M) { + return collision.centFT0M(); + } else if (cfgCentEst == kCentFV0A) { + return collision.centFV0A(); + } else { + return kInvalidCentrality; + } + } + + template + int getGenMidRapMultiplicity(McPartsT const& partsThisMc) + { + int nCh = 0; + + for (auto const& part : partsThisMc) { + if (!part.isPhysicalPrimary()) { + continue; + } + if (part.eta() > cfgGenMultEtaMax || part.eta() < cfgGenMultEtaMin) { + continue; + } + + auto pdgParticle = pdg->GetParticle(part.pdgCode()); + if (!pdgParticle) { + continue; + } + if (pdgParticle->Charge() == 0) { + continue; + } + + nCh++; + } + + return nCh; + } + + float getCentClassFromGenMult(int nCh) + { + const auto& cuts = AxisConfig.cfgGenMultCuts.value; + const auto& centres = AxisConfig.cfgCentBinCentres.value; + + if (cuts.size() != centres.size()) { + LOGF(fatal, + "cfgGenMultCuts size (%zu) and cfgCentBinCentres size (%zu) must be same", + cuts.size(), centres.size()); + } + + for (size_t i = 0; i < cuts.size(); ++i) { + if (nCh >= cuts[i]) { + return centres[i]; + } + } + + return kInvalidCentrality; + } + + // Track selection + template + bool trackCut(TrackType const& track) + { + // basic track cuts + if (std::abs(track.pt()) < TrackCuts.cfgMinPtcut) + return false; + if (std::abs(track.eta()) > TrackCuts.cfgMaxEtacut) + return false; + if (track.itsNCls() < TrackCuts.cfgITScluster) + return false; + if (track.tpcNClsFound() < TrackCuts.cfgTPCcluster) + return false; + if (track.tpcCrossedRowsOverFindableCls() < TrackCuts.cfgRatioTPCRowsOverFindableCls) + return false; + if (track.itsChi2NCl() >= TrackCuts.cfgITSChi2NCl) + return false; + if (track.tpcChi2NCl() >= TrackCuts.cfgTPCChi2NCl) + return false; + if (TrackCuts.cfgHasITS && !track.hasITS()) + return false; + if (TrackCuts.cfgHasTPC && !track.hasTPC()) + return false; + if (TrackCuts.cfgHasTOF && !track.hasTOF()) + return false; + if (TrackCuts.cfgUseITSRefit && !track.passedITSRefit()) + return false; + if (TrackCuts.cfgUseTPCRefit && !track.passedTPCRefit()) + return false; + if (TrackCuts.cfgPVContributor && !track.isPVContributor()) + return false; + if (TrackCuts.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) + return false; + if (TrackCuts.cfgGlobalTrack && !track.isGlobalTrack()) + return false; + if (TrackCuts.cfgPrimaryTrack && !track.isPrimaryTrack()) + return false; + if (TrackCuts.cfgpTdepDCAxyCut) { + // Tuned on the LHC22f anchored MC LHC23d1d on primary pions. 7 Sigmas of the resolution + if (std::abs(track.dcaXY()) > (0.004 + (0.013 / track.pt()))) + return false; + } else { + if (std::abs(track.dcaXY()) > TrackCuts.cfgMaxbDCArToPVcut) + return false; + } + if (TrackCuts.cfgpTdepDCAzCut) { + // Tuned on the LHC22f anchored MC LHC23d1d on primary pions. 7 Sigmas of the resolution + if (std::abs(track.dcaZ()) > (0.004 + (0.013 / track.pt()))) + return false; + } else { + if (std::abs(track.dcaZ()) > TrackCuts.cfgMaxbDCAzToPVcut) + return false; + } + return true; + } + + // PID selection tools + template + bool selectionPIDPion(TrackType const& candidate) + { + if (std::abs(candidate.tpcNSigmaPi()) >= PIDCuts.cfgMaxTPCnSigmaPion) + return false; + if (PIDCuts.cfgTPConly) + return true; + + if (candidate.hasTOF()) { + const bool tofPIDPassed = std::abs(candidate.tofNSigmaPi()) < PIDCuts.cfgMaxTOFnSigmaPion; + const bool combo = (PIDCuts.cfgNsigmaCutCombinedPion > 0) && + (candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi() + + candidate.tofNSigmaPi() * candidate.tofNSigmaPi() < + PIDCuts.cfgNsigmaCutCombinedPion * PIDCuts.cfgNsigmaCutCombinedPion); + return tofPIDPassed || combo; + } else { + return PIDCuts.cfgTOFVeto; + } + } + + template + bool selectionK0s(CollisionType const& collision, K0sType const& candidate) + { + auto lDauDCA = std::fabs(candidate.dcaV0daughters()); + auto lDauPosDCAtoPV = std::fabs(candidate.dcapostopv()); + auto lDauNegDCAtoPV = std::fabs(candidate.dcanegtopv()); + auto lPt = candidate.pt(); + auto lRapidity = candidate.yK0Short(); + auto lRadius = candidate.v0radius(); + auto lDCAtoPV = std::fabs(candidate.dcav0topv()); + auto lCPA = candidate.v0cosPA(); + auto lPropTauK0s = candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassK0Short; + auto lMk0s = candidate.mK0Short(); + auto lMLambda = candidate.mLambda(); + auto lMALambda = candidate.mAntiLambda(); + + auto checkCommonCuts = [&]() { + if (std::fabs(lDauDCA) > SecondaryCuts.cfgSecondaryDauDCAMax) + return false; + if (std::fabs(lDauPosDCAtoPV) < SecondaryCuts.cfgSecondaryDauPosDCAtoPVMin) + return false; + if (std::fabs(lDauNegDCAtoPV) < SecondaryCuts.cfgSecondaryDauNegDCAtoPVMin) + return false; + if (lPt < SecondaryCuts.cfgSecondaryPtMin) + return false; + if (std::fabs(lRapidity) > SecondaryCuts.cfgSecondaryRapidityMax) + return false; + if (lRadius < SecondaryCuts.cfgSecondaryRadiusMin || lRadius > SecondaryCuts.cfgSecondaryRadiusMax) + return false; + if (std::fabs(lDCAtoPV) > SecondaryCuts.cfgSecondaryDCAtoPVMax) + return false; + if (lCPA < SecondaryCuts.cfgSecondaryCosPAMin) + return false; + if (lPropTauK0s > SecondaryCuts.cfgSecondaryProperLifetimeMax) + return false; + if (candidate.qtarm() < SecondaryCuts.cfgSecondaryparamArmenterosCut * std::fabs(candidate.alpha())) + return false; + if (std::fabs(lMk0s - MassK0Short) > SecondaryCuts.cfgSecondaryMassWindow) + return false; + if (SecondaryCuts.cfgSecondaryCrossMassHypothesisCut && + ((std::fabs(lMLambda - MassLambda0) < SecondaryCuts.cfgSecondaryCrossMassCutWindow) || (std::fabs(lMALambda - MassLambda0Bar) < SecondaryCuts.cfgSecondaryCrossMassCutWindow))) + return false; + return true; + }; + + if (SecondaryCuts.cfgReturnFlag) { // For cut study + bool returnFlag = true; + histos.fill(HIST("QA/K0sCutCheck"), 0); + if (lDauDCA > SecondaryCuts.cfgSecondaryDauDCAMax) { + histos.fill(HIST("QA/K0sCutCheck"), 1); + returnFlag = false; + } + if (lDauPosDCAtoPV < SecondaryCuts.cfgSecondaryDauPosDCAtoPVMin) { + histos.fill(HIST("QA/K0sCutCheck"), 2); + returnFlag = false; + } + if (lDauNegDCAtoPV < SecondaryCuts.cfgSecondaryDauNegDCAtoPVMin) { + histos.fill(HIST("QA/K0sCutCheck"), 3); + returnFlag = false; + } + if (lPt < SecondaryCuts.cfgSecondaryPtMin) { + histos.fill(HIST("QA/K0sCutCheck"), 4); + returnFlag = false; + } + if (std::fabs(lRapidity) > SecondaryCuts.cfgSecondaryRapidityMax) { + histos.fill(HIST("QA/K0sCutCheck"), 5); + returnFlag = false; + } + if (lRadius < SecondaryCuts.cfgSecondaryRadiusMin || lRadius > SecondaryCuts.cfgSecondaryRadiusMax) { + histos.fill(HIST("QA/K0sCutCheck"), 6); + returnFlag = false; + } + if (lDCAtoPV > SecondaryCuts.cfgSecondaryDCAtoPVMax) { + histos.fill(HIST("QA/K0sCutCheck"), 7); + returnFlag = false; + } + if (lCPA < SecondaryCuts.cfgSecondaryCosPAMin) { + histos.fill(HIST("QA/K0sCutCheck"), 8); + returnFlag = false; + } + if (lPropTauK0s > SecondaryCuts.cfgSecondaryProperLifetimeMax) { + histos.fill(HIST("QA/K0sCutCheck"), 9); + returnFlag = false; + } + if (candidate.qtarm() < SecondaryCuts.cfgSecondaryparamArmenterosCut * std::fabs(candidate.alpha())) { + histos.fill(HIST("QA/K0sCutCheck"), 10); + returnFlag = false; + } + if (std::fabs(lMk0s - MassK0Short) > SecondaryCuts.cfgSecondaryMassWindow) { + histos.fill(HIST("QA/K0sCutCheck"), 11); + returnFlag = false; + } + if (SecondaryCuts.cfgSecondaryCrossMassHypothesisCut && + ((std::fabs(lMLambda - MassLambda0) < SecondaryCuts.cfgSecondaryCrossMassCutWindow) || (std::fabs(lMALambda - MassLambda0Bar) < SecondaryCuts.cfgSecondaryCrossMassCutWindow))) { + histos.fill(HIST("QA/K0sCutCheck"), 12); + returnFlag = false; + } + return returnFlag; + } else { // normal usage + if (SecondaryCuts.cfgSecondaryRequire) { + return checkCommonCuts(); + } else { + return std::fabs(lMk0s - MassK0Short) <= SecondaryCuts.cfgSecondaryMassWindow; // always apply mass window cut + } + } + } // selectionK0s + + template + bool isTrueKstar(const TrackTemplate& bTrack, const V0Template& K0scand) + { + if (std::abs(bTrack.PDGCode()) != kPiPlus) // Are you pion? + return false; + if (std::abs(K0scand.PDGCode()) != kPDGK0s) // Are you K0s? + return false; + + auto motherbTrack = bTrack.template mothers_as(); + auto motherkV0 = K0scand.template mothers_as(); + + // Check bTrack first + if (std::abs(motherbTrack.pdgCode()) != kKstarPlus) // Are you charged Kstar's daughter? + return false; // Apply first since it's more restrictive + + if (std::abs(motherkV0.pdgCode()) != kPDGK0s) // Is it K0s? + return false; + // Check if K0s's mother is K0 (311) + auto motherK0 = motherkV0.template mothers_as(); + if (std::abs(motherK0.pdgCode()) != kPDGK0) + return false; + + // Check if K0's mother is Kstar (323) + auto motherKstar = motherK0.template mothers_as(); + if (std::abs(motherKstar.pdgCode()) != kKstarPlus) + return false; + + // Check if bTrack and K0 have the same mother (global index) + if (motherbTrack.globalIndex() != motherK0.globalIndex()) + return false; + + return true; + } + + std::unordered_set allowedMcIds; + std::unordered_map centTruthByAllowed; + std::unordered_set refClassIds; + std::unordered_map refCentByMcId; + std::unordered_map genMultByMcId; + + template + void buildAllowedMcIds(McCollsT const& mcCollisions, MCEventCandidates const& events, McPartsT const& mcparts) + { + allowedMcIds.clear(); + centTruthByAllowed.clear(); + + for (const auto& mcColl : mcCollisions) { + const auto mcid = mcColl.globalIndex(); + + if (doprocessMC) { + histos.fill(HIST("QAvtxz_woCut"), mcColl.posZ()); + } + if (cfgTruthIncludeZvtx && std::abs(mcColl.posZ()) >= EventCuts.cfgEvtZvtx) { + continue; + } + if (doprocessMC) { + histos.fill(HIST("QAvtxz_wVtxzCut"), mcColl.posZ()); + } + + auto groupedCollisions = events.sliceBy(perMcRecoCollision, mcid); + + bool atLeastOneMatch = false; + int biggestNContribs = -1; + float bestCent = kInvalidCentrality; + + for (auto const& coll : groupedCollisions) { + const float lCentrality = getCentrality(coll); + if (lCentrality == kInvalidCentrality) + continue; + + if (doprocessMC) { + histos.fill(HIST("QACent_woCut"), lCentrality); + } + + if (!colCuts.isSelected(coll)) { + continue; + } + if (EventCuts.cfgEvtUseRCTFlagChecker && !rctChecker(coll)) { + continue; + } + if (cfgRecoUseInelGt0 && !coll.isInelGt0()) { + continue; + } + + if (doprocessMC) { + histos.fill(HIST("QACent_woCentCut"), lCentrality); + } + + atLeastOneMatch = true; + + // choose best collision by largest number of PV contributors + const int nContrib = coll.multPVTotalContributors(); + if (nContrib > biggestNContribs) { + biggestNContribs = nContrib; + bestCent = lCentrality; + } + } + + if (!atLeastOneMatch || bestCent == kInvalidCentrality) { + continue; + } + + auto partsThisMc = mcparts.sliceBy(perMCCollision, mcid); + const int genMult = getGenMidRapMultiplicity(partsThisMc); + const float genCentClass = getCentClassFromGenMult(genMult); + + if (genCentClass == kInvalidCentrality) + continue; + + if (genCentClass < EventCuts.cfgEventCentralityMin || genCentClass > EventCuts.cfgEventCentralityMax) { + continue; + } + + if (doprocessMC) { + histos.fill(HIST("QACent_wCentCut"), bestCent); + } + + allowedMcIds.insert(mcid); + centTruthByAllowed.emplace(mcid, genCentClass); + genMultByMcId[mcid] = genMult; + + if (doprocessMC) { + histos.fill(HIST("Correction/EF_num_vsGenMult"), genMult); + histos.fill(HIST("Correction/RecoCentVsGenMult"), bestCent, genMult); + histos.fill(HIST("QAMCCent_allowed"), genCentClass); + } + } + } // buildAllowedMcIds + + template + void buildReferenceMcIds(McCollsT const& mccolls, McPartsT const& mcparts) + { + refClassIds.clear(); + refCentByMcId.clear(); + + for (const auto& coll : mccolls) { + const auto mcid = coll.globalIndex(); + + if (cfgTruthIncludeZvtx && std::abs(coll.posZ()) >= EventCuts.cfgEvtZvtx) { + continue; + } + + auto partsThisMc = mcparts.sliceBy(perMCCollision, mcid); + + if (cfgTruthUseInelGt0 && !pwglf::isINELgtNmc(partsThisMc, 0, pdg)) { + continue; + } + + const int genMult = getGenMidRapMultiplicity(partsThisMc); + const float genCentClass = getCentClassFromGenMult(genMult); + + if (genCentClass == kInvalidCentrality) { + continue; + } + if (genCentClass < EventCuts.cfgEventCentralityMin || genCentClass > EventCuts.cfgEventCentralityMax) { + continue; + } + + refClassIds.insert(mcid); + refCentByMcId.emplace(mcid, genCentClass); + genMultByMcId[mcid] = genMult; + + histos.fill(HIST("Correction/EF_den_vsGenMult"), genMult); + + } // for + } // buildReferenceMcIds + + void effK0sProcessGen(MCTrueTrackCandidates const& mcparts) + { + for (const auto& part : mcparts) { + if (!part.has_mcCollision()) + continue; + if (part.pdgCode() != kPDGK0s) + continue; + if (!part.isPhysicalPrimary()) + continue; + if (std::abs(part.y()) > SecondaryCuts.cfgSecondaryRapidityMax) + continue; + + const auto mcid = part.mcCollisionId(); + if (allowedMcIds.count(mcid) == 0) + continue; + + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) + continue; + + const float lCentrality = iter->second; + + histos.fill(HIST("EffK0s/genK0s"), part.pt(), lCentrality); + } + } + + void effK0sProcessReco(MCV0Candidates const& v0s) + { + for (const auto& v0 : v0s) { + auto coll = v0.template collision_as(); + + if (!coll.has_mcCollision()) + continue; + + const auto mcid = coll.mcCollisionId(); + + if (allowedMcIds.count(mcid) == 0) + continue; + + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) { + continue; + } + const float lCentrality = iter->second; + + const double ptreco = v0.pt(); + const double yreco = v0.yK0Short(); + + double ptgen = -1, ygen = 0; + if (!getTruthK0sAndGenKinematics(v0, ptgen, ygen)) + continue; + if (std::abs(yreco) > SecondaryCuts.cfgSecondaryRapidityMax) + continue; + + if (!SecondaryCuts.cfgByPassDauPIDSelection) { + auto posDauTrack = v0.template posTrack_as(); + auto negDauTrack = v0.template negTrack_as(); + if (!selectionPIDPion(posDauTrack)) + continue; + if (!selectionPIDPion(negDauTrack)) + continue; + } + if (!selectionK0s(coll, v0)) + continue; + + histos.fill(HIST("EffK0s/recoK0s"), ptreco, lCentrality); + } + } // effK0sProcessReco + + template + bool matchRecoToTruthKstar(V0T const& v0, TrkT const& trk, double& ptgen, double& ygen) + { + if (!v0.has_mcParticle() || !trk.has_mcParticle()) + return false; + + auto mcK0s = v0.template mcParticle_as(); + auto mcPi = trk.template mcParticle_as(); + + if (std::abs(mcK0s.pdgCode()) != kPDGK0s) + return false; + if (std::abs(mcPi.pdgCode()) != kPiPlus) + return false; + + MCTrueTrackCandidates::iterator kstarFromPi; + bool havePiKstar = false; + for (const auto& m1 : mcPi.template mothers_as()) { + if (std::abs(m1.pdgCode()) == kKstarPlus) { + kstarFromPi = m1; + havePiKstar = true; + break; + } + } + if (!havePiKstar) { + return false; + } + + bool shareSameKstar = false; + for (const auto& m1 : mcK0s.template mothers_as()) { + if (std::abs(m1.pdgCode()) == kPDGK0) { + for (const auto& m2 : m1.template mothers_as()) { + if (m2.globalIndex() == kstarFromPi.globalIndex()) { + shareSameKstar = true; + break; + } + } + if (shareSameKstar) + break; + } + } + if (!shareSameKstar) { + return false; + } + + ptgen = kstarFromPi.pt(); + ygen = kstarFromPi.y(); + + return true; + } // matchRecoToTruthKstar + + void effKstarProcessGen(MCTrueEventCandidates const&, MCTrueTrackCandidates const& mcparts) + { + for (const auto& part : mcparts) { + if (!part.has_mcCollision()) + continue; + if (std::abs(part.pdgCode()) != kKstarPlus) + continue; + if (part.y() < KstarCuts.cfgKstarMinRap || part.y() > KstarCuts.cfgKstarMaxRap) + continue; + + const int pionWanted = (part.pdgCode() > 0) ? +kPiPlus : -kPiPlus; + bool hasRightPion = false; + bool hasK0sToPipi = false; + + for (const auto& d1 : part.template daughters_as()) { + const int pdg1 = d1.pdgCode(); + if (pdg1 == pionWanted) { + hasRightPion = true; + } else if (std::abs(pdg1) == kPDGK0) { + for (const auto& d2 : d1.template daughters_as()) { + if (std::abs(d2.pdgCode()) == kPDGK0s) { + bool seenPip = false, seenPim = false; + for (const auto& d3 : d2.template daughters_as()) { + if (d3.pdgCode() == +kPiPlus) + seenPip = true; + else if (d3.pdgCode() == -kPiPlus) + seenPim = true; + } + if (seenPip && seenPim) { + hasK0sToPipi = true; + break; + } + } + } + } + if (hasRightPion && hasK0sToPipi) + break; + } + + if (!(hasRightPion && hasK0sToPipi)) + continue; + + const auto mcid = part.mcCollisionId(); + if (allowedMcIds.count(mcid) == 0) + continue; + + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) + continue; + + const float lCentrality = iter->second; + + auto itGenMult = genMultByMcId.find(mcid); + if (itGenMult == genMultByMcId.end()) { + continue; + } + const int genMult = itGenMult->second; + + histos.fill(HIST("EffKstar/genKstar"), part.pt(), lCentrality); + histos.fill(HIST("EffKstar/genKstar_vsGenMult"), part.pt(), genMult); + + if (part.vt() == 0) { + histos.fill(HIST("EffKstar/genKstar_pri"), part.pt(), lCentrality); + histos.fill(HIST("EffKstar/genKstar_pri_vsGenMult"), part.pt(), genMult); + } + + const auto mcc = part.mcCollision_as(); + + const float dx = part.vx() - mcc.posX(); + const float dy = part.vy() - mcc.posY(); + const float dz = part.vz() - mcc.posZ(); + + const float distanceFromPV = std::sqrt(dx * dx + dy * dy + dz * dz); + + if (distanceFromPV < fMaxPosPV) { + histos.fill(HIST("EffKstar/genKstar_pri_pos"), part.pt(), lCentrality); + histos.fill(HIST("EffKstar/genKstar_pri_pos_vsGenMult"), part.pt(), genMult); + } + } + } // effKstarProcessGen + + template + void effKstarProcessReco(V0RangeT const& v0s, TrkRangeT const& tracks) + { + + for (const auto& v0 : v0s) { + auto coll = v0.template collision_as(); + + if (!coll.has_mcCollision()) + continue; + + const auto mcid = coll.mcCollisionId(); + + if (allowedMcIds.count(mcid) == 0) + continue; + + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) { + continue; + } + const float lCentrality = iter->second; + + auto itGenMult = genMultByMcId.find(mcid); + if (itGenMult == genMultByMcId.end()) { + continue; + } + const int genMult = itGenMult->second; + + if (!SecondaryCuts.cfgByPassDauPIDSelection) { + auto posDauTrack = v0.template posTrack_as(); + auto negDauTrack = v0.template negTrack_as(); + if (!selectionPIDPion(posDauTrack)) + continue; + if (!selectionPIDPion(negDauTrack)) + continue; + } + + if (!selectionK0s(coll, v0)) + continue; + + auto trks = tracks.sliceBy(perCollisionMC, v0.collisionId()); + for (const auto& bTrack : trks) { + + if (bTrack.collisionId() != v0.collisionId()) + continue; + if (!trackCut(bTrack)) + continue; + if (!selectionPIDPion(bTrack)) + continue; + LorentzVectorSetXYZM lResoSecondary, lDecayDaughter_bach, lResoKstar; + + lResoSecondary = LorentzVectorSetXYZM(v0.px(), v0.py(), v0.pz(), MassK0Short); + lDecayDaughter_bach = LorentzVectorSetXYZM(bTrack.px(), bTrack.py(), bTrack.pz(), MassPionCharged); + lResoKstar = lResoSecondary + lDecayDaughter_bach; + + const double ptreco = lResoKstar.Pt(); + const double yreco = lResoKstar.Rapidity(); + + if (yreco < KstarCuts.cfgKstarMinRap || yreco > KstarCuts.cfgKstarMaxRap) + continue; + + double ptgen = 0, ygen = 0; + const bool isTrue = matchRecoToTruthKstar(v0, bTrack, ptgen, ygen); + + if (isTrue) { + histos.fill(HIST("EffKstar/recoKstar"), ptreco, lCentrality); + histos.fill(HIST("EffKstar/recoKstar_vsGenMult"), ptreco, genMult); + histos.fill(HIST("MCReco/hInvmass_Kstar_true"), lCentrality, ptreco, lResoKstar.M()); + } else { + histos.fill(HIST("MCReco/hInvmass_Kstar_bkg"), lCentrality, ptreco, lResoKstar.M()); + } + } + } + } // effKstarProcessReco + + void fillSigLossNum(MCTrueEventCandidates const&, MCTrueTrackCandidates const& mcparts) + { + for (auto const& part : mcparts) { + if (!part.has_mcCollision()) + continue; + if (std::abs(part.pdgCode()) != kKstarPlus) + continue; + if (part.y() < KstarCuts.cfgKstarMinRap || part.y() > KstarCuts.cfgKstarMaxRap) + continue; + + const auto mcid = part.mcCollisionId(); + if (allowedMcIds.count(mcid) == 0) + continue; + + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) + continue; + + const float lCentrality = iter->second; + + auto itMult = genMultByMcId.find(mcid); + if (itMult != genMultByMcId.end()) { + histos.fill(HIST("Correction/sigLoss_num_vsGenMult"), part.pt(), itMult->second); + } + histos.fill(HIST("Correction/sigLoss_num"), part.pt(), lCentrality); + + if (part.vt() == 0) { + if (itMult != genMultByMcId.end()) { + histos.fill(HIST("Correction/sigLoss_num_vsGenMult_pri"), part.pt(), itMult->second); + } + histos.fill(HIST("Correction/sigLoss_num_pri"), part.pt(), lCentrality); + } + + const auto mcc = part.mcCollision_as(); + + const float dx = part.vx() - mcc.posX(); + const float dy = part.vy() - mcc.posY(); + const float dz = part.vz() - mcc.posZ(); + + const float distanceFromPV = std::sqrt(dx * dx + dy * dy + dz * dz); + + if (distanceFromPV < fMaxPosPV) { + if (itMult != genMultByMcId.end()) { + histos.fill(HIST("Correction/sigLoss_num_vsGenMult_pri_pos"), part.pt(), itMult->second); + } + histos.fill(HIST("Correction/sigLoss_num_pri_pos"), part.pt(), lCentrality); + } + } + } // fillSigLossNum + + void fillSigLossDen(MCTrueEventCandidates const&, MCTrueTrackCandidates const& mcparts) + { + for (auto const& part : mcparts) { + if (!part.has_mcCollision()) + continue; + if (std::abs(part.pdgCode()) != kKstarPlus) + continue; + if (part.y() < KstarCuts.cfgKstarMinRap || part.y() > KstarCuts.cfgKstarMaxRap) + continue; + + const auto mcid = part.mcCollisionId(); + if (refClassIds.count(mcid) == 0) + continue; + + auto iter = refCentByMcId.find(mcid); + if (iter == refCentByMcId.end()) + continue; + + const float lCentrality = iter->second; + + histos.fill(HIST("Correction/sigLoss_den"), part.pt(), lCentrality); + + auto itMult = genMultByMcId.find(mcid); + if (itMult != genMultByMcId.end()) { + histos.fill(HIST("Correction/sigLoss_den_vsGenMult"), part.pt(), itMult->second); + } + if (part.vt() == 0) { + histos.fill(HIST("Correction/sigLoss_den_pri"), part.pt(), lCentrality); + if (itMult != genMultByMcId.end()) { + histos.fill(HIST("Correction/sigLoss_den_vsGenMult_pri"), part.pt(), itMult->second); + } + } + + const auto mcc = part.mcCollision_as(); + + const float dx = part.vx() - mcc.posX(); + const float dy = part.vy() - mcc.posY(); + const float dz = part.vz() - mcc.posZ(); + + const float distanceFromPV = std::sqrt(dx * dx + dy * dy + dz * dz); + + if (distanceFromPV < fMaxPosPV) { + histos.fill(HIST("Correction/sigLoss_den_pri_pos"), part.pt(), lCentrality); + if (itMult != genMultByMcId.end()) { + histos.fill(HIST("Correction/sigLoss_den_vsGenMult_pri_pos"), part.pt(), itMult->second); + } + } + } + } // fillSigLossDen + + int count = 0; + + template + void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksTypeK0s& dTracks2) + { + using TrackTarget = std::decay_t; + + histos.fill(HIST("QA/before/CentDist"), lCentrality); + + LorentzVectorSetXYZM lDecayDaughter1, lDecayDaughter2, lResoSecondary, lDecayDaughter_bach, lResoKstar, lDaughterRot, lResonanceRot; + std::vector trackIndicies = {}; + std::vector k0sIndicies = {}; + + for (const auto& bTrack : dTracks1) { + auto trkbpt = bTrack.pt(); + auto istrkbhasTOF = bTrack.hasTOF(); + auto trkbNSigmaPiTPC = bTrack.tpcNSigmaPi(); + auto trkbNSigmaPiTOF = (istrkbhasTOF) ? bTrack.tofNSigmaPi() : -999.; + + if constexpr (!IsMix) { + if (cfgFillQAPlots) { + // Bachelor pion QA plots + histos.fill(HIST("QA/before/trkbpionTPCPID"), trkbpt, trkbNSigmaPiTPC); + if (istrkbhasTOF) { + histos.fill(HIST("QA/before/trkbpionTOFPID"), trkbpt, trkbNSigmaPiTOF); + histos.fill(HIST("QA/before/trkbpionTPCTOFPID"), trkbNSigmaPiTPC, trkbNSigmaPiTOF); + } + histos.fill(HIST("QA/before/trkbpionpT"), trkbpt); + histos.fill(HIST("QA/before/trkbpionDCAxy"), bTrack.dcaXY()); + histos.fill(HIST("QA/before/trkbpionDCAz"), bTrack.dcaZ()); + } + } + + if (!trackCut(bTrack)) + continue; + if (!selectionPIDPion(bTrack)) + continue; + + if constexpr (!IsMix) { + if (cfgFillQAPlots) { + // Bachelor pion QA plots after applying cuts + histos.fill(HIST("QA/after/trkbpionTPCPID"), trkbpt, trkbNSigmaPiTPC); + if (istrkbhasTOF) { + histos.fill(HIST("QA/after/trkbpionTOFPID"), trkbpt, trkbNSigmaPiTOF); + histos.fill(HIST("QA/after/trkbpionTPCTOFPID"), trkbNSigmaPiTPC, trkbNSigmaPiTOF); + } + histos.fill(HIST("QA/after/trkbpionpT"), trkbpt); + histos.fill(HIST("QA/after/trkbpionDCAxy"), bTrack.dcaXY()); + histos.fill(HIST("QA/after/trkbpionDCAz"), bTrack.dcaZ()); + } + } + trackIndicies.push_back(bTrack.index()); + } + + for (const auto& k0sCand : dTracks2) { + + auto posDauTrack = k0sCand.template posTrack_as(); + auto negDauTrack = k0sCand.template negTrack_as(); + + /// Daughters + // Positve pion + auto trkppt = posDauTrack.pt(); + auto istrkphasTOF = posDauTrack.hasTOF(); + auto trkpNSigmaPiTPC = posDauTrack.tpcNSigmaPi(); + auto trkpNSigmaPiTOF = (istrkphasTOF) ? posDauTrack.tofNSigmaPi() : -999.; + // Negative pion + auto trknpt = negDauTrack.pt(); + auto istrknhasTOF = negDauTrack.hasTOF(); + auto trknNSigmaPiTPC = negDauTrack.tpcNSigmaPi(); + auto trknNSigmaPiTOF = (istrknhasTOF) ? negDauTrack.tofNSigmaPi() : -999.; + + /// K0s + auto trkkDauDCA = k0sCand.dcaV0daughters(); + auto trkky = k0sCand.yK0Short(); + auto trkkDCAtoPV = k0sCand.dcav0topv(); + auto trkkCPA = k0sCand.v0cosPA(); + auto trkkPropTau = k0sCand.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * MassK0Short; + auto trkkMass = k0sCand.mK0Short(); + auto trkkDauDCAPostoPV = k0sCand.dcapostopv(); + auto trkkDauDCANegtoPV = k0sCand.dcanegtopv(); + auto trkkpt = k0sCand.pt(); + auto trkkRadius = k0sCand.v0radius(); + + if constexpr (!IsMix) { + if (cfgFillQAPlots) { + // Seconddary QA plots + histos.fill(HIST("QA/before/trkppionTPCPID"), trkppt, trkpNSigmaPiTPC); + if (istrkphasTOF) { + histos.fill(HIST("QA/before/trkppionTOFPID"), trkppt, trkpNSigmaPiTOF); + histos.fill(HIST("QA/before/trkppionTPCTOFPID"), trkpNSigmaPiTPC, trkpNSigmaPiTOF); + } + histos.fill(HIST("QA/before/trkppionpT"), trkppt); + histos.fill(HIST("QA/before/trkppionDCAxy"), posDauTrack.dcaXY()); + histos.fill(HIST("QA/before/trkppionDCAz"), posDauTrack.dcaZ()); + + histos.fill(HIST("QA/before/trknpionTPCPID"), trknpt, trknNSigmaPiTPC); + if (istrknhasTOF) { + histos.fill(HIST("QA/before/trknpionTOFPID"), trknpt, trknNSigmaPiTOF); + histos.fill(HIST("QA/before/trknpionTPCTOFPID"), trknNSigmaPiTPC, trknNSigmaPiTOF); + } + histos.fill(HIST("QA/before/trknpionpT"), trknpt); + histos.fill(HIST("QA/before/trknpionDCAxy"), negDauTrack.dcaXY()); + histos.fill(HIST("QA/before/trknpionDCAz"), negDauTrack.dcaZ()); + + histos.fill(HIST("QA/before/hDauDCASecondary"), trkkDauDCA); + histos.fill(HIST("QA/before/hDauPosDCAtoPVSecondary"), trkkDauDCAPostoPV); + histos.fill(HIST("QA/before/hDauNegDCAtoPVSecondary"), trkkDauDCANegtoPV); + + histos.fill(HIST("QA/before/hpT_Secondary"), trkkpt); + histos.fill(HIST("QA/before/hy_Secondary"), trkky); + histos.fill(HIST("QA/before/hRadiusSecondary"), trkkRadius); + histos.fill(HIST("QA/before/hDCAtoPVSecondary"), trkkDCAtoPV); + histos.fill(HIST("QA/before/hCPASecondary"), trkkCPA); + histos.fill(HIST("QA/before/hPropTauSecondary"), trkkPropTau); + histos.fill(HIST("QA/before/hArmSecondary"), k0sCand.alpha(), k0sCand.qtarm()); + histos.fill(HIST("QA/before/hInvmassSecondary"), trkkMass); + } + } + + if (!SecondaryCuts.cfgByPassDauPIDSelection && !selectionPIDPion(posDauTrack)) + continue; + if (!SecondaryCuts.cfgByPassDauPIDSelection && !selectionPIDPion(negDauTrack)) + continue; + if (!selectionK0s(collision, k0sCand)) + continue; + + if constexpr (!IsMix) { + if (cfgFillQAPlots) { + // Seconddary QA plots after applying cuts + + histos.fill(HIST("QA/after/trkppionTPCPID"), trkppt, trkpNSigmaPiTPC); + if (istrkphasTOF) { + histos.fill(HIST("QA/after/trkppionTOFPID"), trkppt, trkpNSigmaPiTOF); + histos.fill(HIST("QA/after/trkppionTPCTOFPID"), trkpNSigmaPiTPC, trkpNSigmaPiTOF); + } + histos.fill(HIST("QA/after/trkppionpT"), trkppt); + histos.fill(HIST("QA/after/trkppionDCAxy"), posDauTrack.dcaXY()); + histos.fill(HIST("QA/after/trkppionDCAz"), posDauTrack.dcaZ()); + + histos.fill(HIST("QA/after/trknpionTPCPID"), trknpt, trknNSigmaPiTPC); + if (istrknhasTOF) { + histos.fill(HIST("QA/after/trknpionTOFPID"), trknpt, trknNSigmaPiTOF); + histos.fill(HIST("QA/after/trknpionTPCTOFPID"), trknNSigmaPiTPC, trknNSigmaPiTOF); + } + histos.fill(HIST("QA/after/trknpionpT"), trknpt); + histos.fill(HIST("QA/after/trknpionDCAxy"), negDauTrack.dcaXY()); + histos.fill(HIST("QA/after/trknpionDCAz"), negDauTrack.dcaZ()); + + histos.fill(HIST("QA/after/hDauDCASecondary"), trkkDauDCA); + histos.fill(HIST("QA/after/hDauPosDCAtoPVSecondary"), trkkDauDCAPostoPV); + histos.fill(HIST("QA/after/hDauNegDCAtoPVSecondary"), trkkDauDCANegtoPV); + + histos.fill(HIST("QA/after/hpT_Secondary"), trkkpt); + histos.fill(HIST("QA/after/hy_Secondary"), trkky); + histos.fill(HIST("QA/after/hRadiusSecondary"), trkkRadius); + histos.fill(HIST("QA/after/hDCAtoPVSecondary"), trkkDCAtoPV); + histos.fill(HIST("QA/after/hCPASecondary"), trkkCPA); + histos.fill(HIST("QA/after/hPropTauSecondary"), trkkPropTau); + histos.fill(HIST("QA/after/hArmSecondary"), k0sCand.alpha(), k0sCand.qtarm()); + histos.fill(HIST("QA/after/hInvmassSecondary"), trkkMass); + } + histos.fill(HIST("hInvmass_K0s"), lCentrality, trkkpt, trkkMass); + } + k0sIndicies.push_back(k0sCand.index()); + } + + for (const auto& trackIndex : trackIndicies) { + auto bTrack = dTracks1.rawIteratorAt(trackIndex); + for (const auto& k0sIndex : k0sIndicies) { + auto k0sCand = dTracks2.rawIteratorAt(k0sIndex); + + lDecayDaughter_bach = LorentzVectorSetXYZM(bTrack.px(), bTrack.py(), bTrack.pz(), MassPionCharged); + lResoSecondary = LorentzVectorSetXYZM(k0sCand.px(), k0sCand.py(), k0sCand.pz(), MassK0Short); + lResoKstar = lResoSecondary + lDecayDaughter_bach; + + // QA plots + if constexpr (!IsMix) { + if (cfgFillQAPlots) { + histos.fill(HIST("QA/before/KstarRapidity"), lResoKstar.Rapidity()); + histos.fill(HIST("QA/before/kstarinvmass"), lResoKstar.M()); + } + } + + if (lResoKstar.Rapidity() > KstarCuts.cfgKstarMaxRap || lResoKstar.Rapidity() < KstarCuts.cfgKstarMinRap) + continue; + + if constexpr (!IsMix) { + unsigned int typeKstar = bTrack.sign() > 0 ? BinType::kKstarP : BinType::kKstarN; + if (cfgFillQAPlots) { + + histos.fill(HIST("QA/after/KstarRapidity"), lResoKstar.Rapidity()); + histos.fill(HIST("QA/after/kstarinvmass"), lResoKstar.M()); + } + histos.fill(HIST("hInvmass_Kstar"), typeKstar, lCentrality, lResoKstar.Pt(), lResoKstar.M()); + + if (BkgEstimationConfig.cfgFillRotBkg) { + for (int i = 0; i < BkgEstimationConfig.cfgNrotBkg; i++) { + auto lRotAngle = BkgEstimationConfig.cfgMinRot + i * ((BkgEstimationConfig.cfgMaxRot - BkgEstimationConfig.cfgMinRot) / (BkgEstimationConfig.cfgNrotBkg - 1)); + if (cfgFillQAPlots) { + histos.fill(HIST("QA/RotBkg/hRotBkg"), lRotAngle); + } + if (BkgEstimationConfig.cfgRotPion) { + lDaughterRot = lDecayDaughter_bach; + // lDaughterRot.RotateZ(lRotAngle); + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = lDaughterRot + lResoSecondary; + } else { + lDaughterRot = lResoSecondary; + // lDaughterRot.RotateZ(lRotAngle); + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = lDecayDaughter_bach + lDaughterRot; + } + typeKstar = bTrack.sign() > 0 ? BinType::kKstarP_Rot : BinType::kKstarN_Rot; + histos.fill(HIST("hInvmass_Kstar"), typeKstar, lCentrality, lResonanceRot.Pt(), lResonanceRot.M()); + } + } + } // IsMix + } // K0scand + } // bTrack + + count++; + + } // fillHistograms + + void processData(EventCandidates::iterator const& collision, + TrackCandidates const& tracks, + V0Candidates const& v0s, + aod::BCsWithTimestamps const&) + { + if (!colCuts.isSelected(collision)) // Default event selection + return; + if (EventCuts.cfgEvtUseRCTFlagChecker && !rctChecker(collision)) { + return; + } + lCentrality = getCentrality(collision); + if (lCentrality < EventCuts.cfgEventCentralityMin || lCentrality > EventCuts.cfgEventCentralityMax) + return; + if (cfgRecoUseInelGt0 && !collision.isInelGt0()) + return; + colCuts.fillQA(collision); + + fillHistograms(collision, tracks, v0s); + } + PROCESS_SWITCH(Chk892LI, processData, "Process Event for data without Partitioning", false); + + void processMC(MCTrueTrackCandidates const& mcpart, + MCTrackCandidates const& tracks, + MCV0Candidates const& v0s, + MCEventCandidates const& events, + MCTrueEventCandidates const& mccolls) + { + genMultByMcId.clear(); + buildAllowedMcIds(mccolls, events, mcpart); + buildReferenceMcIds(mccolls, mcpart); + effK0sProcessGen(mcpart); + effK0sProcessReco(v0s); + effKstarProcessGen(mccolls, mcpart); + effKstarProcessReco(v0s, tracks); + fillSigLossNum(mccolls, mcpart); + fillSigLossDen(mccolls, mcpart); + + for (const auto& mcid : refClassIds) { + auto iter = refCentByMcId.find(mcid); + if (iter == refCentByMcId.end()) + continue; + + const float lCentrality = iter->second; + histos.fill(HIST("Correction/EF_den"), lCentrality); + } + + for (const auto& mcid : allowedMcIds) { + auto iter = centTruthByAllowed.find(mcid); + if (iter == centTruthByAllowed.end()) + continue; + + const float lCentrality = iter->second; + histos.fill(HIST("Correction/EF_num"), lCentrality); + } + + size_t nIntersect = 0; + for (const auto& mcid : allowedMcIds) + if (refClassIds.count(mcid)) + nIntersect++; + histos.fill(HIST("Correction/setSizes"), 0.0, refClassIds.size()); + histos.fill(HIST("Correction/setSizes"), 1.0, allowedMcIds.size()); + histos.fill(HIST("Correction/setSizes"), 2.0, nIntersect); + histos.fill(HIST("Correction/setSizes"), 3.0, allowedMcIds.size() - nIntersect); + + for (auto const& mcc : mccolls) { + auto partsThisMc = mcpart.sliceBy(perMCCollision, mcc.globalIndex()); + const int genMult = getGenMidRapMultiplicity(partsThisMc); + const float genCentClass = getCentClassFromGenMult(genMult); + if (genCentClass == kInvalidCentrality) { + continue; + } + histos.fill(HIST("Correction/MCTruthCent_all"), genCentClass); + } + + for (const auto& mcid : refClassIds) { + auto iter = refCentByMcId.find(mcid); + if (iter == refCentByMcId.end()) + continue; + lCentrality = iter->second; + histos.fill(HIST("Correction/MCTruthCent_cut"), lCentrality); + } + + for (auto const& mcc : mccolls) { + const auto mcid = mcc.globalIndex(); + + histos.fill(HIST("Correction/hNEventsMCTruth"), 1.0); + + bool passZvtx = true; + if (cfgTruthIncludeZvtx && std::abs(mcc.posZ()) > EventCuts.cfgEvtZvtx) { + passZvtx = false; + } + if (passZvtx) { + histos.fill(HIST("Correction/hNEventsMCTruth"), 2.0); + + auto partsThisMc = mcpart.sliceBy(perMCCollision, mcid); + if (!cfgTruthUseInelGt0 || pwglf::isINELgtNmc(partsThisMc, 0, pdg)) { + histos.fill(HIST("Correction/hNEventsMCTruth"), 3.0); + } + } + if (allowedMcIds.count(mcid)) { + histos.fill(HIST("Correction/hNEventsMCTruth"), 4.0); + } + } + } + PROCESS_SWITCH(Chk892LI, processMC, "Process Event for MC", true); + + void processMCQA(MCEventCandidates::iterator const& collision, + MCTrackCandidates const& tracks, + MCV0Candidates const& v0s, + MCTrueEventCandidates const& /*mccolls*/, + aod::BCsWithTimestamps const&) + { + if (!colCuts.isSelected(collision)) + return; + if (EventCuts.cfgEvtUseRCTFlagChecker && !rctChecker(collision)) + return; + if (cfgRecoUseInelGt0 && !collision.isInelGt0()) + return; + if (!collision.has_mcCollision()) + return; + + const auto mcid = collision.mcCollisionId(); + + auto it = centTruthByAllowed.find(mcid); + if (it == centTruthByAllowed.end()) { + return; + } + lCentrality = it->second; + + if (lCentrality < EventCuts.cfgEventCentralityMin || lCentrality > EventCuts.cfgEventCentralityMax) + return; + colCuts.fillQA(collision); + + fillHistograms(collision, tracks, v0s); + } + PROCESS_SWITCH(Chk892LI, processMCQA, "Process Event for MC and fill QA plots", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/cksspinalignder.cxx b/PWGLF/Tasks/Resonances/cksspinalignder.cxx index 830d4764f71..55456c551a1 100644 --- a/PWGLF/Tasks/Resonances/cksspinalignder.cxx +++ b/PWGLF/Tasks/Resonances/cksspinalignder.cxx @@ -10,20 +10,15 @@ // or submit itself to any jurisdiction. /// \file cksspinalignder.cxx -/// \brief Analysis task for ChargedKStar spin alignment +/// \brief Derived task for charged KStar spin alignment using reduced K0s and pion tables /// /// \author prottay.das@cern.ch #include "PWGLF/DataModel/LFCKSSpinalignmentTables.h" -#include "Common/Core/RecoDecay.h" - -#include -#include #include #include #include -#include #include #include #include @@ -35,22 +30,15 @@ #include #include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include #include -#include -#include // for std::fabs -#include +#include #include -#include #include -#include -#include -#include -#include using namespace o2; using namespace o2::framework; @@ -59,108 +47,65 @@ using namespace o2::soa; struct cksspinalignder { - struct : ConfigurableGroup { - Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; - Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; - } cfgCcdbParam; - - // event sel///////// - Configurable centMin{"centMin", 0, "Minimum Centrality"}; - Configurable centMax{"centMax", 80, "Maximum Centrality"}; - // V0 selection //////////// - Configurable cosPA{"cosPA", 0.995, "Cosine Pointing Angle"}; - Configurable radiusMin{"radiusMin", 1.2, "Minimum V0 radius"}; - Configurable radiusMax{"radiusMax", 100, "Maximum V0 radius"}; - Configurable dcaPion{"dcaPion", 0.1, "DCA Pion"}; - Configurable dcaDaughters{"dcaDaughters", 1.0, "DCA between daughters"}; - Configurable lifetimeMax{"lifetimeMax", 20, "Maximum V0 lifetime"}; - Configurable ptMin{"ptMin", 0.5, "V0 Pt minimum"}; - Configurable ptMax{"ptMax", 10.0, "V0 Pt maximum"}; - Configurable v0eta{"v0eta", 0.8, "Eta cut on lambda"}; - // pion sel///////// - Configurable usePID{"usePID", false, "Enable PID selection using stored bitmask"}; - // Choose which PID bit to require (1..4). If pidExclusive=true -> require ONLY that PID. - Configurable pidChoice{"pidChoice", 2, "PID choice: 1,2,3,4"}; - Configurable pidExclusive{"pidExclusive", false, "If true require ONLY the chosen PID (mask == bit). If false require (mask & bit)!=0"}; - - // Event Mixing - Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; - ConfigurableAxis cfgVtxBins{"cfgVtxBins", {10, -10, 10}, "Mixing bins - z-vertex"}; - ConfigurableAxis cfgMultBins{"cfgMultBins", {8, 0.0, 80}, "Mixing bins - centrality"}; - Configurable ptMix{"ptMix", 0.2f, "ME: pT bin width"}; - Configurable etaMix{"etaMix", 0.2f, "ME: eta bin width"}; - Configurable phiMix{"phiMix", 0.3f, "ME: phi bin width"}; - - // THnsparse bining - ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {50, 1.09, 1.14}, "#it{M} (GeV/#it{c}^{2})"}; - ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0, 10.0}, "#it{p}_{T}"}; - ConfigurableAxis configThnAxisSA{"configThnAxisSA", {20, -1.0, 1.0}, "cos#it{#theta *}"}; - ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {8, 0.0, 80.0}, "Centrality"}; - - // Enable access to the CCDB for the offset and correction constants and save them in dedicated variables. - Service ccdb; - o2::ccdb::CcdbApi ccdbApi; + // Event selection + Configurable centMin{"centMin", 0.0f, "Minimum centrality"}; + Configurable centMax{"centMax", 80.0f, "Maximum centrality"}; + + // K0s selection from stored reduced table + Configurable applyK0sSelection{"applyK0sSelection", true, "Apply additional K0s selection in derived task"}; + Configurable cosPA{"cosPA", 0.995f, "Minimum V0 cosine pointing angle"}; + Configurable radiusMin{"radiusMin", 1.2f, "Minimum V0 radius"}; + Configurable radiusMax{"radiusMax", 100.0f, "Maximum V0 radius"}; + Configurable dcaPion{"dcaPion", 0.1f, "Minimum V0 daughter DCA to PV"}; + // Configurable dcaDaughters{"dcaDaughters", 1.0f, "Maximum DCA between V0 daughters"}; + Configurable k0sPtMin{"k0sPtMin", 0.0f, "Minimum K0s pT"}; + Configurable k0sPtMax{"k0sPtMax", 10.0f, "Maximum K0s pT"}; + Configurable k0sEtaMax{"k0sEtaMax", 0.8f, "Maximum K0s eta"}; + Configurable k0sMassMin{"k0sMassMin", 0.45f, "Minimum K0s mass"}; + Configurable k0sMassMax{"k0sMassMax", 0.55f, "Maximum K0s mass"}; + + // Bachelor pion PID + Configurable usePID{"usePID", true, "Apply bachelor pion PID using stored bitmask"}; + Configurable pidChoice{"pidChoice", 4, "PID choice: 1, 2, 3, or 4"}; + Configurable pidExclusive{"pidExclusive", false, "If true require only selected PID bit. If false require selected bit among possible bits"}; + + // Charge selection + Configurable chargeSelection{"chargeSelection", 0, "Bachelor pion charge selection: 0 = both, +1 = K*+, -1 = K*-"}; + + // Event-plane choice + Configurable epChoice{"epChoice", 0, "Event plane: 0 = FT0C, 1 = FT0A, 2 = TPC"}; + + // Event mixing + Configurable nEvtMixing{"nEvtMixing", 5, "Number of mixed events"}; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {10, -10.0f, 10.0f}, "Mixing bins in z vertex"}; + ConfigurableAxis cfgCentBins{"cfgCentBins", {8, 0.0f, 80.0f}, "Mixing bins in centrality"}; + + // Sparse axes + ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {150, 0.75f, 1.05f}, "#it{M}_{K^{0}_{S}#pi} (GeV/#it{c}^{2})"}; + ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0f, 10.0f}, "#it{p}_{T}^{K^{0}_{S}#pi} (GeV/#it{c})"}; + ConfigurableAxis configThnAxisSA{"configThnAxisSA", {20, -1.0f, 1.0f}, "cos#it{#theta}^{*}"}; + ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {8, 0.0f, 80.0f}, "Centrality (%)"}; + ConfigurableAxis configThnAxisCharge{"configThnAxisCharge", {2, -1.5f, 1.5f}, "Bachelor pion charge"}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; void init(o2::framework::InitContext&) { - - histos.add("hCentrality", "Centrality distribution", kTH1F, {{80, 0, 80}}); - histos.add("hKShortMass", "hKShortMass", kTH1F, {{100, 0.45, 0.55}}); - - histos.add("hSparsesame", "hSparsesame", kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisSA, configThnAxisCentrality}); - histos.add("hSparsemix", "hSparsemix", kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisSA, configThnAxisCentrality}); - - ccdb->setURL(cfgCcdbParam.cfgURL); - ccdbApi.init("http://alice-ccdb.cern.ch"); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + histos.add("hCentrality", "Centrality distribution;Centrality (%);Events", kTH1F, {{80, 0.0f, 80.0f}}); + histos.add("hKShortMass", "K^{0}_{S} mass;M_{#pi^{+}#pi^{-}} (GeV/#it{c}^{2});Counts", kTH1F, {{200, 0.4f, 0.6f}}); + + histos.add("hSparsesame", + "Same-event K^{0}_{S}#pi;M_{K^{0}_{S}#pi};p_{T};cos#theta^{*};centrality;charge", + kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, configThnAxisSA, configThnAxisCentrality, configThnAxisCharge}); + + histos.add("hSparsemix", + "Mixed-event K^{0}_{S}#pi;M_{K^{0}_{S}#pi};p_{T};cos#theta^{*};centrality;charge", + kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, configThnAxisSA, configThnAxisCentrality, configThnAxisCharge}); } - template - bool selectionV0(T const& candidate) - { - auto kshortPt = std::sqrt(candidate.kShortPx() * candidate.kShortPx() + candidate.kShortPy() * candidate.kShortPy()); - auto px = candidate.kShortPx(); - auto py = candidate.kShortPy(); - auto pz = candidate.kShortPz(); - auto p = std::sqrt(px * px + py * py + pz * pz); - auto kshortEta = 0.5 * std::log((p + pz) / (p - pz)); - - if (std::abs(kshortEta) > v0eta) { - return false; - } - if (candidate.v0Cospa() < cosPA) { - return false; - } - if (candidate.v0Radius() > radiusMax) { - return false; - } - if (candidate.v0Radius() < radiusMin) { - return false; - } - if (candidate.dcaBetweenDaughter() > dcaDaughters) { - return false; - } - if (std::abs(candidate.dcaPositive()) < dcaPion && std::abs(candidate.dcaNegative()) < dcaPion) { - return false; - } - if (candidate.v0Lifetime() > lifetimeMax) { - return false; - } - if (kshortPt < ptMin) { - return false; - } - if (kshortPt > ptMax) { - return false; - } - return true; - } - - // ----- PID via stored bitmask (no selectionPID function needed) ----- - // Assumes your PionTracks table has a uint8_t column named pionPidMask(). - // Bit mapping: PID1=1, PID2=2, PID3=4, PID4=8. + // PID bit mapping from producer static constexpr uint8_t kPID1 = 1u << 0; static constexpr uint8_t kPID2 = 1u << 1; static constexpr uint8_t kPID3 = 1u << 2; @@ -168,8 +113,7 @@ struct cksspinalignder { uint8_t requiredPidBit() const { - const int choice = pidChoice.value; - switch (choice) { + switch (pidChoice.value) { case 1: return kPID1; case 2: @@ -179,8 +123,8 @@ struct cksspinalignder { case 4: return kPID4; default: - LOGF(warn, "pidChoice=%d is invalid. Using PID2.", choice); - return kPID2; + LOGF(warn, "pidChoice=%d is invalid. Using PID4.", pidChoice.value); + return kPID4; } } @@ -190,640 +134,300 @@ struct cksspinalignder { if (!usePID.value) { return true; } - const uint8_t bit = requiredPidBit(); + const uint8_t mask = pion.pionPidMask(); + const uint8_t bit = requiredPidBit(); if (pidExclusive.value) { - return mask == bit; // ONLY chosen PID + return mask == bit; } - return (mask & bit) != 0; // chosen PID (maybe others too) - } - // --------------------------------------------------------------- - std::tuple computePtEtaPhi(float px, float py, float pz) - { - float pt = std::sqrt(px * px + py * py); - float p = std::sqrt(px * px + py * py + pz * pz); - float eta = (p != std::abs(pz)) ? 0.5 * std::log((p + pz) / (p - pz)) : 0.0f; // avoid division by zero - float phi = RecoDecay::constrainAngle(std::atan2(py, px)); - return {pt, eta, phi}; + return (mask & bit) != 0; } - ROOT::Math::PtEtaPhiMVector kshort, pion, chkstar; - ROOT::Math::PtEtaPhiMVector kshortmix, pionmix, chkstarmix; - ROOT::Math::PxPyPzMVector fourVecDauCM, fourVecDauCMmix; - ROOT::Math::XYZVector threeVecDauCM, eventplaneVecNorm, threeVecDauCMmix, eventplaneVecNormmix; - - Filter centralityFilter = (nabs(aod::kshortpionevent::cent) < centMax && nabs(aod::kshortpionevent::cent) > centMin); - - using EventCandidates = soa::Filtered; - - void processSameData(EventCandidates::iterator const& collision, aod::KShortTracks const& V0s, aod::PionTracks const& piontracks) + template + bool passChargeSelection(const TPion& pion) const { - auto centrality = collision.cent(); - histos.fill(HIST("hCentrality"), centrality); - - auto psiFT0C = collision.psiFT0C(); + const int charge = static_cast(pion.charge()); - for (const auto& v0 : V0s) { - if (!selectionV0(v0)) { - continue; - } - auto [kshortPt, kshortEta, kshortPhi] = computePtEtaPhi(v0.kShortPx(), v0.kShortPy(), v0.kShortPz()); - kshort = ROOT::Math::PtEtaPhiMVector(kshortPt, kshortEta, kshortPhi, v0.kShortMass()); - histos.fill(HIST("hKShortMass"), kshort.M()); - - for (const auto& piontrack : piontracks) { - - // PID selection via stored mask - if (!passSelectedPID(piontrack)) { - continue; - } - auto [pionPt, pionEta, pionPhi] = computePtEtaPhi(piontrack.pionBachPx(), piontrack.pionBachPy(), piontrack.pionBachPz()); - pion = ROOT::Math::PtEtaPhiMVector(pionPt, pionEta, pionPhi, o2::constants::physics::MassPionCharged); - - if (piontrack.pionBachIndex() == v0.pionIndex1() || piontrack.pionBachIndex() == v0.pionIndex2()) - continue; // checking if bachelor pion is khort daughter or not -> skip further processing if such is the case - - chkstar = kshort + pion; - - ROOT::Math::Boost boost{chkstar.BoostToCM()}; - fourVecDauCM = boost(kshort); - threeVecDauCM = fourVecDauCM.Vect(); - eventplaneVecNorm = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0C), -std::cos(2.0 * psiFT0C), 0); - auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + if (charge == 0) { + return false; + } - histos.fill(HIST("hSparsesame"), chkstar.M(), chkstar.Pt(), cosThetaStar, centrality); - } + if (chargeSelection.value == 0) { + return true; } - } - PROCESS_SWITCH(cksspinalignder, processSameData, "Process same data", true); - // Processing Event Mixing - SliceCache cache; - using BinningType = ColumnBinningPolicy; - BinningType colBinning{{cfgVtxBins, cfgMultBins}, true}; - Preslice tracksPerCollisionV0 = aod::kshortpionpair::kshortpioneventId; - Preslice tracksPerCollisionBach = aod::kshortpionpair::kshortpioneventId; + return charge == chargeSelection.value; + } - void processMixedData(EventCandidates const& collisions, aod::KShortTracks const& V0s, aod::PionTracks const& piontracks) + template + bool selectionV0(const TK0s& candidate) const { - for (const auto& [collision1, collision2] : selfCombinations(colBinning, nEvtMixing, -1, collisions, collisions)) { - if (collision1.index() == collision2.index()) { - continue; - } - auto centrality = collision1.cent(); - auto psiFT0Cmix = collision1.psiFT0C(); + if (!applyK0sSelection.value) { + return true; + } - auto groupV0 = V0s.sliceBy(tracksPerCollisionV0, collision1.index()); - auto groupPion = piontracks.sliceBy(tracksPerCollisionBach, collision2.index()); - for (const auto& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(groupV0, groupPion))) { - if (!selectionV0(t1)) - continue; - auto [kshortPtmix, kshortEtamix, kshortPhimix] = computePtEtaPhi(t1.kShortPx(), t1.kShortPy(), t1.kShortPz()); - kshortmix = ROOT::Math::PtEtaPhiMVector(kshortPtmix, kshortEtamix, kshortPhimix, t1.kShortMass()); + ROOT::Math::PxPyPzMVector k0s(candidate.kShortPx(), + candidate.kShortPy(), + candidate.kShortPz(), + candidate.kShortMass()); - // PID selection via stored mask (on the bachelor pion from the mixed event) - if (!passSelectedPID(t2)) { - continue; - } - auto [pionPtmix, pionEtamix, pionPhimix] = computePtEtaPhi(t2.pionBachPx(), t2.pionBachPy(), t2.pionBachPz()); - pionmix = ROOT::Math::PtEtaPhiMVector(pionPtmix, pionEtamix, pionPhimix, o2::constants::physics::MassPionCharged); + const float k0sPt = k0s.Pt(); + const float k0sEta = k0s.Eta(); - chkstarmix = kshortmix + pionmix; + if (std::abs(k0sEta) > k0sEtaMax.value) { + return false; + } - ROOT::Math::Boost boost{chkstarmix.BoostToCM()}; - fourVecDauCMmix = boost(kshortmix); - threeVecDauCMmix = fourVecDauCMmix.Vect(); - eventplaneVecNormmix = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0Cmix), -std::cos(2.0 * psiFT0Cmix), 0); - auto cosThetaStarmix = eventplaneVecNormmix.Dot(threeVecDauCMmix) / std::sqrt(threeVecDauCMmix.Mag2()) / std::sqrt(eventplaneVecNormmix.Mag2()); + if (candidate.kShortMass() < k0sMassMin.value || + candidate.kShortMass() > k0sMassMax.value) { + return false; + } - histos.fill(HIST("hSparsemix"), chkstarmix.M(), chkstarmix.Pt(), cosThetaStarmix, centrality); - } + if (candidate.v0Cospa() < cosPA.value) { + return false; } - } - PROCESS_SWITCH(cksspinalignder, processMixedData, "Process mixed data", true); - - // ===================================================================================== - // MEV2-style mixing for Charged K* : KEEP K0s (from event E) FIXED, MIX bachelor pion - // pion is taken from other events E' but matched in (event class + pion pt/eta/phi) - // ===================================================================================== - - // One status bin (unused dimension, kept for compatibility with linearKey) - static constexpr int N_STATUS = 1; - - // ---------- Binner for bachelor-pion kinematics ---------- - struct MixBinnerPi { - float ptMin, ptMax, ptStep; - float etaMin, etaMax, etaStep; - float phiMin, phiMax, phiStep; - - // Dummy "mass" axis (we keep 1 bin to keep the same 6D infrastructure) - static constexpr float mMin = 0.f; - static constexpr float mMax = 1.f; - static constexpr int nM_ = 1; - static constexpr float mStep = (mMax - mMin) / nM_; - - int nPt_{1}, nEta_{1}, nPhi_{1}; - - MixBinnerPi(float ptMin_, float ptMax_, float ptStep_, - float etaAbsMax_, float etaStep_, - float phiStep_) - : ptMin(ptMin_), ptMax(ptMax_), ptStep(ptStep_), etaMin(-etaAbsMax_), etaMax(+etaAbsMax_), etaStep(etaStep_), phiMin(0.f), phiMax(static_cast(2.0 * TMath::Pi())), phiStep(phiStep_) - { - ptStep = (ptStep > 0.f ? ptStep : 0.2f); - etaStep = (etaStep > 0.f ? etaStep : 0.2f); - phiStep = (phiStep > 0.f ? phiStep : 0.2f); - - nPt_ = std::max(1, static_cast(std::floor((ptMax - ptMin) / ptStep + 0.5f))); - nEta_ = std::max(1, static_cast(std::floor((etaMax - etaMin) / etaStep + 0.5f))); - nPhi_ = std::max(1, static_cast(std::ceil((phiMax - phiMin) / phiStep))); + + if (candidate.v0Radius() < radiusMin.value || + candidate.v0Radius() > radiusMax.value) { + return false; + } + /* + if (candidate.dcaBetweenDaughter() > dcaDaughters.value) { + return false; + } + */ + if (candidate.dcaPositive() < dcaPion.value || + candidate.dcaNegative() < dcaPion.value) { + return false; } - inline int nPt() const { return nPt_; } - inline int nEta() const { return nEta_; } - inline int nPhi() const { return nPhi_; } - inline int nM() const { return nM_; } - - inline int binFromValue(float v, float vmin, float step, int nBins) const - { - if (!std::isfinite(v)) - return -1; - const float x = (v - vmin) / step; - int b = static_cast(std::floor(x + 1e-6f)); - if (b < 0) - return -1; - if (b >= nBins) - b = nBins - 1; - return b; + if (k0sPt < k0sPtMin.value || + k0sPt > k0sPtMax.value) { + return false; } - inline int ptBin(float pt) const { return binFromValue(pt, ptMin, ptStep, nPt_); } - inline int etaBin(float eta) const { return binFromValue(eta, etaMin, etaStep, nEta_); } - inline int phiBin(float phi) const { return binFromValue(phi, phiMin, phiStep, nPhi_); } - inline int massBin(float m) const { return binFromValue(m, mMin, mStep, nM_); } // always 0 if finite - }; - - // Buffer candidate: one pion stored in a bin - struct BufferCandPi { - int64_t collisionIdx; // to enforce different event - int64_t rowIndex; // row id in PionTracks (for iteratorAt) - uint16_t ptBin, etaBin, phiBin, mBin; - }; - - struct MatchRef { - int64_t collisionIdx; - int64_t rowIndex; - }; - - // Key: (colBin, stat, pt, eta, phi, m) - static inline size_t linearKey(int colBin, int statBin, - int ptBin, int etaBin, int phiBin, int mBin, - int nStat, int nPt, int nEta, int nPhi, int nM) - { - return ((((((static_cast(colBin) * nStat + statBin) * nPt + ptBin) * nEta + etaBin) * nPhi + phiBin) * nM + mBin)); + return true; } - // ===================================================================================== - // Mixed-event method: - // PASS 1: build buffer of pions by (event-class bin) + (pi pt/eta/phi bins) - // PASS 2: loop same-event (K0, piSame) pairs; replace piSame by piX from buffer - // keep K0 from current event fixed; use psiFT0C of current event. - // ===================================================================================== - void processMixedDataMEV2_Pion(EventCandidates const& collisions, - aod::KShortTracks const& V0s, - aod::PionTracks const& piontracks) + template + bool k0sBelongsToEvent(const TK0s& k0s, const TCollision& collision) const { - static thread_local std::mt19937 rng(0xBADC0FFE); - - // Build pion binner from your configurables - // Use your own pt range config for bachelor pions if you have it; otherwise reuse ptMin/ptMax - MixBinnerPi mb{ - ptMin, ptMax, /*ptStep*/ ptMix.value, - /*|eta|max*/ v0eta.value, /*etaStep*/ etaMix.value, - /*phiStep*/ phiMix.value}; - - const int nCol = colBinning.getAllBinsCount(); // event-class bins (vz, cent) - const int nStat = N_STATUS; // 1 - const int nPt = mb.nPt(); - const int nEta = mb.nEta(); - const int nPhi = mb.nPhi(); - const int nM = mb.nM(); - - const size_t nKeys = static_cast(nCol) * nStat * nPt * nEta * nPhi * nM; - std::vector> buffer(nKeys); - - // ---------------- PASS 1: fill 6D buffer with bachelor pions ---------------- - for (auto const& col : collisions) { - const int colBin = colBinning.getBin(std::make_tuple(col.posz(), col.cent())); - if (colBin < 0) - continue; - - auto slicePi = piontracks.sliceBy(tracksPerCollisionBach, col.index()); - - for (auto const& pi : slicePi) { + return k0s.kshortpioneventId() == collision.globalIndex(); + } - // apply configurable PID selection stored in bitmask (your passSelectedPID) - if (!passSelectedPID(pi)) - continue; + template + bool pionBelongsToEvent(const TPion& pion, const TCollision& collision) const + { + return pion.kshortpioneventId() == collision.globalIndex(); + } - auto [pt, eta, phi] = computePtEtaPhi(pi.pionBachPx(), pi.pionBachPy(), pi.pionBachPz()); - phi = RecoDecay::constrainAngle(phi, 0.0F); + template + float getEventPlane(const TCollision& collision) const + { + switch (epChoice.value) { + case 0: + return collision.psiFT0C(); + case 1: + return collision.psiFT0C(); + case 2: + return collision.psiFT0C(); + default: + LOGF(warn, "epChoice=%d is invalid. Using FT0C.", epChoice.value); + return collision.psiFT0C(); + } + } - const int ptB = mb.ptBin(pt); - const int etaB = mb.etaBin(eta); - const int phiB = mb.phiBin(phi); - const int mB = 0; // dummy axis has 1 bin - if (ptB < 0 || etaB < 0 || phiB < 0) - continue; + bool computeCosThetaStar(const ROOT::Math::PxPyPzMVector& k0s, + const ROOT::Math::PxPyPzMVector& kstar, + float psi, + float& cosThetaStar) const + { + ROOT::Math::Boost boost{kstar.BoostToCM()}; + ROOT::Math::PxPyPzMVector k0sCM = boost(k0s); + ROOT::Math::XYZVector k0sCMVec = k0sCM.Vect(); - const int stat = 0; - const size_t key = linearKey(colBin, stat, ptB, etaB, phiB, mB, nStat, nPt, nEta, nPhi, nM); + ROOT::Math::XYZVector eventPlaneVector(std::sin(2.0f * psi), + -std::cos(2.0f * psi), + 0.0f); - const int64_t row = static_cast(pi.globalIndex()); + const double denom = + std::sqrt(k0sCMVec.Mag2()) * + std::sqrt(eventPlaneVector.Mag2()); - buffer[key].push_back(BufferCandPi{ - .collisionIdx = static_cast(col.index()), - .rowIndex = row, - .ptBin = static_cast(ptB), - .etaBin = static_cast(etaB), - .phiBin = static_cast(phiB), - .mBin = static_cast(mB)}); - } + if (denom <= 0.0 || !std::isfinite(denom)) { + return false; } - // ---------------- PASS 2: same-event (K0,piSame), replace ONLY pion by piX ---------------- - for (auto const& col1 : collisions) { - const int colBin = colBinning.getBin(std::make_tuple(col1.posz(), col1.cent())); - if (colBin < 0) - continue; + cosThetaStar = + static_cast(eventPlaneVector.Dot(k0sCMVec) / denom); - auto poolK0 = V0s.sliceBy(tracksPerCollisionV0, col1.index()); - auto poolPi = piontracks.sliceBy(tracksPerCollisionBach, col1.index()); - if (poolK0.size() == 0 || poolPi.size() == 0) - continue; + cosThetaStar = std::clamp(cosThetaStar, -1.0f, 1.0f); - const float centrality = col1.cent(); - const float psiFT0Cmix = col1.psiFT0C(); + return true; + } - // Loop over K0s from the *current* event (fixed object) - for (auto const& k0 : poolK0) { - if (!selectionV0(k0)) - continue; + template + bool fillKstarPair(const TK0s& k0sCand, + const TPion& pionCand, + const TCollision& collision, + bool isMixed) + { + if (!selectionV0(k0sCand)) { + return false; + } - // Build K0 4-vector once - auto [kpt, keta, kphi] = computePtEtaPhi(k0.kShortPx(), k0.kShortPy(), k0.kShortPz()); - kphi = RecoDecay::constrainAngle(kphi, 0.0F); - kshortmix = ROOT::Math::PtEtaPhiMVector(kpt, keta, kphi, k0.kShortMass()); + if (!passSelectedPID(pionCand)) { + return false; + } - // Loop over same-event pions to define the "same-event pair" base - for (auto const& piSame : poolPi) { - if (!passSelectedPID(piSame)) - continue; + if (!passChargeSelection(pionCand)) { + return false; + } - // avoid self-combination: bachelor pion can't be a K0 daughter - if (piSame.pionBachIndex() == k0.pionIndex1() || piSame.pionBachIndex() == k0.pionIndex2()) { - continue; - } + // Same-event self-combination rejection: + // bachelor pion must not be one of the K0s daughters. + if (!isMixed) { + if (pionCand.pionBachIndex() == k0sCand.pionIndex1() || + pionCand.pionBachIndex() == k0sCand.pionIndex2()) { + return false; + } + } - // Compute the kinematic bin of THIS pion (this defines where to pick mixed pions) - auto [ppt, peta, pphi] = computePtEtaPhi(piSame.pionBachPx(), piSame.pionBachPy(), piSame.pionBachPz()); - pphi = RecoDecay::constrainAngle(pphi, 0.0F); + ROOT::Math::PxPyPzMVector k0s(k0sCand.kShortPx(), + k0sCand.kShortPy(), + k0sCand.kShortPz(), + k0sCand.kShortMass()); - const int ptB = mb.ptBin(ppt); - const int etaB = mb.etaBin(peta); - const int phiB = mb.phiBin(pphi); - const int mB = 0; - if (ptB < 0 || etaB < 0 || phiB < 0) - continue; + ROOT::Math::PxPyPzMVector pion(pionCand.pionBachPx(), + pionCand.pionBachPy(), + pionCand.pionBachPz(), + o2::constants::physics::MassPionCharged); - const int stat = 0; - const size_t key = linearKey(colBin, stat, ptB, etaB, phiB, mB, nStat, nPt, nEta, nPhi, nM); - auto const& binVec = buffer[key]; - if (binVec.empty()) - continue; + const auto kstar = k0s + pion; - // collect partners from same bin but different collision - std::vector matches; - matches.reserve(binVec.size()); - const int64_t curColIdx = static_cast(col1.index()); + const float mass = kstar.M(); + const float pt = kstar.Pt(); + const float centrality = collision.cent(); + const float charge = static_cast(pionCand.charge()); + const float psi = getEventPlane(collision); - for (auto const& bc : binVec) { - if (bc.collisionIdx == curColIdx) - continue; - matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); - } - if (matches.empty()) - continue; + float cosThetaStar = 0.0f; - // random unique sampling (cap = nEvtMixing) - const int cap = nEvtMixing.value; - if (cap > 0 && cap < static_cast(matches.size())) { - std::uniform_int_distribution dist(0, static_cast(matches.size()) - 1); - std::unordered_set chosen; - chosen.reserve(static_cast(cap) * 2); - while (static_cast(chosen.size()) < cap) - chosen.insert(dist(rng)); - - std::vector subset; - subset.reserve(cap); - for (const int& idx : chosen) - subset.push_back(matches[idx]); - matches.swap(subset); - } else { - std::shuffle(matches.begin(), matches.end(), rng); - } + if (!computeCosThetaStar(k0s, kstar, psi, cosThetaStar)) { + return false; + } - // const float wBase = 1.0f / static_cast(matches.size()); - const float wBase = 1.0; + if (isMixed) { + histos.fill(HIST("hSparsemix"), mass, pt, cosThetaStar, centrality, charge); + } else { + histos.fill(HIST("hSparsesame"), mass, pt, cosThetaStar, centrality, charge); + } - // Replace pion by mixed pion piX, keep k0 fixed - for (auto const& m : matches) { - auto piX = piontracks.iteratorAt(m.rowIndex); // must match PASS 1 rowIndex convention + return true; + } - // safety: PID selection again - if (!passSelectedPID(piX)) - continue; + Filter centralityFilter = + (aod::kshortpionevent::cent >= centMin) && + (aod::kshortpionevent::cent < centMax); - // OPTIONAL keep: also avoid piX being a K0 daughter (same k0 from current event) - if (piX.pionBachIndex() == k0.pionIndex1() || piX.pionBachIndex() == k0.pionIndex2()) { - continue; - } + using EventCandidates = soa::Filtered; - auto [xpt, xeta, xphi] = computePtEtaPhi(piX.pionBachPx(), piX.pionBachPy(), piX.pionBachPz()); - xphi = RecoDecay::constrainAngle(xphi, 0.0F); - pionmix = ROOT::Math::PtEtaPhiMVector(xpt, xeta, xphi, o2::constants::physics::MassPionCharged); + void processSameData(EventCandidates::iterator const& collision, + aod::KShortTracks const& k0sTracks, + aod::PionTracks const& pionTracks) + { + const float centrality = collision.cent(); - chkstarmix = kshortmix + pionmix; + histos.fill(HIST("hCentrality"), centrality); - ROOT::Math::Boost boost{chkstarmix.BoostToCM()}; - fourVecDauCMmix = boost(kshortmix); - threeVecDauCMmix = fourVecDauCMmix.Vect(); + for (const auto& k0sCand : k0sTracks) { + if (!k0sBelongsToEvent(k0sCand, collision)) { + continue; + } - eventplaneVecNormmix = ROOT::Math::XYZVector(std::sin(2.f * psiFT0Cmix), - -std::cos(2.f * psiFT0Cmix), 0.f); + if (!selectionV0(k0sCand)) { + continue; + } - const float cosThetaStarmix = - eventplaneVecNormmix.Dot(threeVecDauCMmix) / - std::sqrt(threeVecDauCMmix.Mag2()) / - std::sqrt(eventplaneVecNormmix.Mag2()); + histos.fill(HIST("hKShortMass"), k0sCand.kShortMass()); - histos.fill(HIST("hSparsemix"), - chkstarmix.M(), chkstarmix.Pt(), cosThetaStarmix, centrality, wBase); - } + for (const auto& pionCand : pionTracks) { + if (!pionBelongsToEvent(pionCand, collision)) { + continue; } + + fillKstarPair(k0sCand, pionCand, collision, false); } } } - PROCESS_SWITCH(cksspinalignder, processMixedDataMEV2_Pion, "Process mixed data MEV4 (keep K0 fixed, replace pion with mixed pion)", true); - - /* -// ---- Configuration knobs (already in your task or add similarly) ---- -// ptMix: step for K0 pT binning, etaMix step for eta, phiMix step for phi -// configThnAxisInvMass etc are your histogram axes (not used for binning here) - -// We can keep one status bin for now -static constexpr int N_STATUS = 1; - -// Binner for K0 kinematics -struct MixBinnerK0 { - float ptMin, ptMax, ptStep; - float etaMin, etaMax, etaStep; - float phiMin, phiMax, phiStep; - - // K0 mass binning (you can widen if you want) - static constexpr float mMin = 0.45f; - static constexpr float mMax = 0.55f; // exclusive-ish - static constexpr int nM_ = 1; - static constexpr float mStep = (mMax - mMin) / nM_; - - int nPt_, nEta_, nPhi_; + PROCESS_SWITCH(cksspinalignder, processSameData, "Process same-event charged KStar", true); - MixBinnerK0(float ptMin_, float ptMax_, float ptStep_, - float etaAbsMax_, float etaStep_, - float phiStep_) - : ptMin(ptMin_), ptMax(ptMax_), ptStep(ptStep_), - etaMin(-etaAbsMax_), etaMax(+etaAbsMax_), etaStep(etaStep_), - phiMin(0.f), phiMax(static_cast(2.0 * TMath::Pi())), phiStep(phiStep_) - { - ptStep = (ptStep > 0.f ? ptStep : 0.1f); - etaStep = (etaStep > 0.f ? etaStep : 0.1f); - phiStep = (phiStep > 0.f ? phiStep : 0.1f); - - nPt_ = std::max(1, static_cast(std::floor((ptMax - ptMin) / ptStep + 0.5f))); - nEta_ = std::max(1, static_cast(std::floor((etaMax - etaMin) / etaStep + 0.5f))); - nPhi_ = std::max(1, static_cast(std::ceil((phiMax - phiMin) / phiStep))); - } - - inline int nPt() const { return nPt_; } - inline int nEta() const { return nEta_; } - inline int nPhi() const { return nPhi_; } - inline int nM() const { return nM_; } - - inline int binFromValue(float v, float vmin, float step, int nBins) const - { - if (!std::isfinite(v)) return -1; - const float x = (v - vmin) / step; - int b = static_cast(std::floor(x + 1e-6f)); - if (b < 0) return -1; - if (b >= nBins) b = nBins - 1; - return b; - } - - inline int ptBin(float pt) const { return binFromValue(pt, ptMin, ptStep, nPt_); } - inline int etaBin(float eta) const { return binFromValue(eta, etaMin, etaStep, nEta_); } - inline int phiBin(float phi) const { return binFromValue(phi, phiMin, phiStep, nPhi_); } - inline int massBin(float m) const { return binFromValue(m, mMin, mStep, nM_); } -}; - - struct BufferCandK0 { - int64_t collisionIdx; // to enforce different event - int64_t rowIndex; // row id in KShortTracks (for iteratorAt) - uint16_t ptBin, etaBin, phiBin, mBin; - }; - - struct MatchRef { - int64_t collisionIdx; - int64_t rowIndex; - }; - - // Key: (colBin, stat, pt, eta, phi, m) - static inline size_t linearKey(int colBin, int statBin, - int ptBin, int etaBin, int phiBin, int mBin, - int nStat, int nPt, int nEta, int nPhi, int nM) - { - return ((((((static_cast(colBin) * nStat + statBin) * nPt + ptBin) * nEta + etaBin) * nPhi + phiBin) * nM + mBin)); - } + SliceCache cache; + using BinningType = ColumnBinningPolicy; - void processMixedDataMEV4(EventCandidates const& collisions, - aod::KShortTracks const& V0s, - aod::PionTracks const& piontracks) + template + void fillMixedPairs(const TK0sGroup& k0sGroup, + const TPionGroup& pionGroup, + const TCollision& referenceCollision) { - static thread_local std::mt19937 rng(0xBADC0FFE); - - // Build K0 binner from your configurables - MixBinnerK0 mb{ - ptMin.value, ptMax.value, ptMix.value, - v0eta.value, etaMix.value, - phiMix.value - }; - - const int nCol = colBinning.getAllBinsCount(); - const int nStat = N_STATUS; - const int nPt = mb.nPt(); - const int nEta = mb.nEta(); - const int nPhi = mb.nPhi(); - const int nM = mb.nM(); - - const size_t nKeys = static_cast(nCol) * nStat * nPt * nEta * nPhi * nM; - std::vector> buffer(nKeys); - - // ---------------- PASS 1: fill 6D buffer with K0s ---------------- - for (auto const& col : collisions) { - const int colBin = colBinning.getBin(std::make_tuple(col.posz(), col.cent())); - if (colBin < 0) continue; - - auto sliceK0 = V0s.sliceBy(tracksPerCollisionV0, col.index()); - - for (auto const& k0 : sliceK0) { - if (!selectionV0(k0)) continue; - - auto [kpt, keta, kphi] = computePtEtaPhi(k0.kShortPx(), k0.kShortPy(), k0.kShortPz()); - kphi = RecoDecay::constrainAngle(kphi, 0.0F); - - const int ptB = mb.ptBin(kpt); - const int etaB = mb.etaBin(keta); - const int phiB = mb.phiBin(kphi); - const int mB = mb.massBin(k0.kShortMass()); - if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0) continue; - - const int stat = 0; - const size_t key = linearKey(colBin, stat, ptB, etaB, phiB, mB, nStat, nPt, nEta, nPhi, nM); - - // IMPORTANT: rowIndex must match iteratorAt accessor below. - const int64_t row = static_cast(k0.globalIndex()); // if this fails -> use k0.index() consistently - - buffer[key].push_back(BufferCandK0{ - .collisionIdx = static_cast(col.index()), - .rowIndex = row, - .ptBin = static_cast(ptB), - .etaBin = static_cast(etaB), - .phiBin = static_cast(phiB), - .mBin = static_cast(mB) - }); + for (const auto& k0sCand : k0sGroup) { + for (const auto& pionCand : pionGroup) { + fillKstarPair(k0sCand, pionCand, referenceCollision, true); } } - - // ---------------- PASS 2: build same-event (K0,π) pairs, replace only K0 by K0X ---------------- - for (auto const& col1 : collisions) { - const int colBin = colBinning.getBin(std::make_tuple(col1.posz(), col1.cent())); - if (colBin < 0) continue; - - auto poolK0 = V0s.sliceBy(tracksPerCollisionV0, col1.index()); - auto poolPi = piontracks.sliceBy(tracksPerCollisionBach, col1.index()); - - if (poolK0.size() == 0 || poolPi.size() == 0) continue; // no .empty() in ASoA slice - - const float centrality = col1.cent(); - const float psiFT0Cmix = col1.psiFT0C(); - - // same-event pair loop - for (auto const& k0 : poolK0) { - if (!selectionV0(k0)) continue; - - auto [kpt, keta, kphi] = computePtEtaPhi(k0.kShortPx(), k0.kShortPy(), k0.kShortPz()); - kphi = RecoDecay::constrainAngle(kphi, 0.0F); - - const int ptB = mb.ptBin(kpt); - const int etaB = mb.etaBin(keta); - const int phiB = mb.phiBin(kphi); - const int mB = mb.massBin(k0.kShortMass()); - if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0) continue; - - const int stat = 0; - const size_t key = linearKey(colBin, stat, ptB, etaB, phiB, mB, nStat, nPt, nEta, nPhi, nM); - auto const& binVec = buffer[key]; - if (binVec.empty()) continue; - - // pre-collect eligible K0 partners from other events (same bin, different collision) - std::vector matches; - matches.reserve(binVec.size()); - const int64_t curColIdx = static_cast(col1.index()); - - for (auto const& bc : binVec) { - if (bc.collisionIdx == curColIdx) continue; - matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); - } - if (matches.empty()) continue; - - // choose random unique subset of K0 partners - const int cap = nEvtMixing.value; - if (cap > 0 && cap < static_cast(matches.size())) { - std::uniform_int_distribution dist(0, static_cast(matches.size()) - 1); - std::unordered_set chosen; - chosen.reserve(static_cast(cap) * 2); - while (static_cast(chosen.size()) < cap) chosen.insert(dist(rng)); - - std::vector subset; - subset.reserve(cap); - for (const int& idx : chosen) subset.push_back(matches[idx]); - matches.swap(subset); - } else { - std::shuffle(matches.begin(), matches.end(), rng); } - const float wBase = 1.0f / static_cast(matches.size()); - - // Now keep π from SAME event fixed (this is your requirement) - for (auto const& piSame : poolPi) { - if (!passSelectedPID(piSame)) continue; - - // avoid self-combination if you stored K0 daughter indices - if (piSame.pionBachIndex() == k0.pionIndex1() || piSame.pionBachIndex() == k0.pionIndex2()) { - continue; - } - - auto [ppt, peta, pphi] = computePtEtaPhi(piSame.pionBachPx(), piSame.pionBachPy(), piSame.pionBachPz()); - pphi = RecoDecay::constrainAngle(pphi, 0.0F); - pionmix = ROOT::Math::PtEtaPhiMVector(ppt, peta, pphi, o2::constants::physics::MassPionCharged); + void processMixedData(EventCandidates const& collisions, + aod::KShortTracks const& k0sTracks, + aod::PionTracks const& pionTracks) + { + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; - // replace K0 by mixed K0X from other event, but keep piSame - for (auto const& m : matches) { - auto k0X = V0s.iteratorAt(m.rowIndex); // if globalIndex fails -> use index consistently in PASS1 + for (const auto& [collision1, collision2] : + selfCombinations(colBinning, nEvtMixing.value, -1, collisions, collisions)) { - if (!selectionV0(k0X)) continue; + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } - auto [xpt, xeta, xphi] = computePtEtaPhi(k0X.kShortPx(), k0X.kShortPy(), k0X.kShortPz()); - xphi = RecoDecay::constrainAngle(xphi, 0.0F); - kshortmix = ROOT::Math::PtEtaPhiMVector(xpt, xeta, xphi, k0X.kShortMass()); + // Direction 1: K0s from event 1 + pion from event 2. + for (const auto& k0sCand : k0sTracks) { + if (!k0sBelongsToEvent(k0sCand, collision1)) { + continue; + } - chkstarmix = kshortmix + pionmix; + for (const auto& pionCand : pionTracks) { + if (!pionBelongsToEvent(pionCand, collision2)) { + continue; + } - ROOT::Math::Boost boost{chkstarmix.BoostToCM()}; - fourVecDauCMmix = boost(kshortmix); - threeVecDauCMmix = fourVecDauCMmix.Vect(); + fillKstarPair(k0sCand, pionCand, collision1, true); + } + } - eventplaneVecNormmix = ROOT::Math::XYZVector(std::sin(2.f * psiFT0Cmix), - -std::cos(2.f * psiFT0Cmix), 0.f); + // Direction 2: K0s from event 2 + pion from event 1. + for (const auto& k0sCand : k0sTracks) { + if (!k0sBelongsToEvent(k0sCand, collision2)) { + continue; + } - const float cosThetaStarmix = - eventplaneVecNormmix.Dot(threeVecDauCMmix) / - std::sqrt(threeVecDauCMmix.Mag2()) / - std::sqrt(eventplaneVecNormmix.Mag2()); + for (const auto& pionCand : pionTracks) { + if (!pionBelongsToEvent(pionCand, collision1)) { + continue; + } - histos.fill(HIST("hSparsemix"), - chkstarmix.M(), chkstarmix.Pt(), cosThetaStarmix, centrality, wBase); - } - } + fillKstarPair(k0sCand, pionCand, collision2, true); + } } } } - PROCESS_SWITCH(cksspinalignder, processMixedDataMEV4, - "Process mixed data MEV4 (replace K0, keep same-event pion)", true); - */ + PROCESS_SWITCH(cksspinalignder, processMixedData, "Process mixed-event charged KStar", true); }; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; diff --git a/PWGLF/Tasks/Resonances/deltaAnalysis.cxx b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx new file mode 100644 index 00000000000..81f28e2b513 --- /dev/null +++ b/PWGLF/Tasks/Resonances/deltaAnalysis.cxx @@ -0,0 +1,2112 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file deltaAnalysis.cxx +/// \brief Delta(1232) resonance analysis via proton-pion invariant mass reconstruction with advance PID and background rejection cuts also add efficiency correction and acceptance correction +/// \author Durgesh Bhatt + +#include "PWGLF/DataModel/mcCentrality.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +namespace +{ +constexpr float MassProton = o2::constants::physics::MassProton; +constexpr float MassPion = o2::constants::physics::MassPionCharged; +constexpr float MinAbsCharge = 1e-3f; +const std::array kProtonPionMasses{MassProton, MassPion}; +} // namespace +namespace delta_analysis +{ +static constexpr int PdgProton{2212}; +static constexpr int PdgPion{211}; +static constexpr int PdgDeltaPlusPlus{2224}; +static constexpr int PdgDeltaZero{2114}; + +enum CentEstimator : int { + kFT0M = 0, + kFT0A = 1, + kFT0C = 2, + kFV0A = 3, + kNTPV = 4 +}; +} // namespace delta_analysis + +struct DeltaAnalysis { + + SliceCache cache; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // PDG service used only for truth-level charged-particle identification + // (see isChargedPrimaryMC() below). + Service pdgDB{}; + + struct : ConfigurableGroup { + Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted |z-vertex| range [cm]"}; + Configurable cfgRequireRecoINELgt0{"cfgRequireRecoINELgt0", true, "Require reconstructed INEL>0 event class (at least one PV track in |eta|<1)"}; + Configurable cfgUseNoSameBunchPileupCut{"cfgUseNoSameBunchPileupCut", false, "Apply kNoSameBunchPileup event selection"}; + Configurable cfgUseGoodZvtxFT0vsPVCut{"cfgUseGoodZvtxFT0vsPVCut", false, "Apply kIsGoodZvtxFT0vsPV event selection"}; + Configurable cfgUseGoodITSLayersAll{"cfgUseGoodITSLayersAll", false, "Apply kIsGoodITSLayersAll event selection"}; + Configurable applyOccupancyInTimeRangeCut{"applyOccupancyInTimeRangeCut", false, "Apply occupancy-in-time-range cut"}; + Configurable cfgOccupancyMin{"cfgOccupancyMin", 0, "Minimum track occupancy in time range"}; + Configurable cfgOccupancyMax{"cfgOccupancyMax", 9999, "Maximum track occupancy in time range"}; + Configurable cfgCentralityEstimator{"cfgCentralityEstimator", 0, "Centrality estimator: 0=FT0M 1=FT0A 2=FT0C 3=FV0A 4=NTPV"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum centrality percentile"}; + Configurable cfgCentMax{"cfgCentMax", 100.f, "Maximum centrality percentile"}; + Configurable cfgUseMCTruthCentrality{"cfgUseMCTruthCentrality", false, "Use MC truth centrality for generated Delta histograms"}; + Configurable cfgApplyMCTruthINELgt0{"cfgApplyMCTruthINELgt0", true, "Apply truth-level INEL>0 selection to MC generated events"}; + } evSel; + + struct : ConfigurableGroup { + Configurable cfgCutPt{"cfgCutPt", 0.2f, "Minimum pT of daughter track [GeV/c]"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Maximum |eta| of daughter track"}; + Configurable cfgMinY{"cfgMinY", -0.5f, "Minimum rapidity of reconstructed Delta"}; + Configurable cfgMaxY{"cfgMaxY", 0.5f, "Maximum rapidity of reconstructed Delta"}; + Configurable cfgMinITSClusters{"cfgMinITSClusters", 5, "Minimum ITS clusters"}; + Configurable cfgMinTPCClusters{"cfgMinTPCClusters", 70, "Minimum TPC clusters found"}; + Configurable numberOfInvMassBins{"numberOfInvMassBins", 120, "Number of bins along the invariant mass axis"}; + Configurable cfgMinTPCCrossedRows{"cfgMinTPCCrossedRows", 70, "Minimum TPC crossed pad rows"}; + Configurable cfgMinCrossedRowsOverFindable{"cfgMinCrossedRowsOverFindable", 0.8f, "Minimum ratio crossedRows/findableClusters"}; + Configurable cfgMaxTPCSharedClusters{"cfgMaxTPCSharedClusters", 0, "Maximum TPC shared clusters"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.0f, "Maximum TPC chi2/NCl"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.0f, "Maximum ITS chi2/NCl"}; + // ── Per-cut ON/OFF switches for the first 7 basic track-quality cuts (all default true) ── + Configurable applyITSClusterCut{"applyITSClusterCut", true, "Enable/disable minimum ITS clusters cut"}; + Configurable applyTPCSharedClusterCut{"applyTPCSharedClusterCut", true, "Enable/disable maximum TPC shared clusters cut"}; + Configurable applyTPCFoundClusterCut{"applyTPCFoundClusterCut", true, "Enable/disable minimum TPC clusters found cut"}; + Configurable applyTPCCrossedRowsCut{"applyTPCCrossedRowsCut", true, "Enable/disable minimum TPC crossed rows cut"}; + Configurable applyTPCCrossedRowsOverFindableCut{"applyTPCCrossedRowsOverFindableCut", true, "Enable/disable minimum TPC crossed rows / findable clusters cut"}; + Configurable applyTPCChi2NClCut{"applyTPCChi2NClCut", true, "Enable/disable maximum TPC chi2/NCl cut"}; + Configurable applyITSChi2NClCut{"applyITSChi2NClCut", true, "Enable/disable maximum ITS chi2/NCl cut"}; + Configurable requirePrimaryTrack{"requirePrimaryTrack", true, "Require isPrimaryTrack flag"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require global track"}; + Configurable requirePVContributor{"requirePVContributor", true, "Require PV-contributor flag"}; + } trackCuts; + + struct : ConfigurableGroup { + Configurable cfgCutDCAz{"cfgCutDCAz", 0.1f, "Maximum |DCAz| for all tracks [cm]"}; + Configurable> protonDCAPtBinEdges{"protonDCAPtBinEdges", {0.0f, 0.5f, 1.0f, 2.0f, 1000.f}, "Proton pT bin edges for DCAxy cut [GeV/c]"}; + Configurable> protonMaxDCAxyPerPtBin{"protonMaxDCAxyPerPtBin", {0.10f, 0.08f, 0.05f, 0.05f}, "Max |DCAxy| for proton per pT bin [cm]"}; + Configurable> pionDCAPtBinEdges{"pionDCAPtBinEdges", {0.0f, 0.5f, 1.0f, 2.0f, 1000.f}, "Pion pT bin edges for DCAxy cut [GeV/c]"}; + Configurable> pionMaxDCAxyPerPtBin{"pionMaxDCAxyPerPtBin", {0.20f, 0.15f, 0.10f, 0.08f}, "Max |DCAxy| for pion per pT bin [cm]"}; + Configurable isApplyPtDepDCAxyCut{"isApplyPtDepDCAxyCut", false, "Use continuous pT-dependent DCAxy cut"}; + Configurable dcaXYConst{"dcaXYConst", 0.0105f, "Constant term of pT-dependent DCAxy cut [cm]"}; + Configurable dcaXYPtCoeff{"dcaXYPtCoeff", 0.035f, "pT-dependent coefficient of DCAxy cut [cm]"}; + Configurable dcaXYPtExponent{"dcaXYPtExponent", 1.1f, "pT exponent of DCAxy cut"}; + } dcaCuts; + + struct : ConfigurableGroup { + Configurable useTPCOnlyPID{"useTPCOnlyPID", false, "Use TPC-only PID (ignore TOF even if present)"}; + Configurable tpcNSigmaVetoThreshold{"tpcNSigmaVetoThreshold", 3.0f, "Reject track if TPC nSigma of a competing species is below this value"}; + Configurable tofNSigmaVetoThreshold{"tofNSigmaVetoThreshold", 3.0f, "Reject track if TOF nSigma of a competing species is below this value"}; + } pidShared; + + struct : ConfigurableGroup { + Configurable requireTOFForProton{"requireTOFForProton", false, "Require TOF signal for proton candidates"}; + Configurable minProtonMomentum{"minProtonMomentum", 0.f, "Minimum proton momentum for TOF PID [GeV/c]"}; + Configurable minTPCNSigmaProton{"minTPCNSigmaProton", -6.0f, "Minimum (lower bound) TPC nSigma for proton"}; + Configurable minTOFNSigmaProton{"minTOFNSigmaProton", -6.0f, "Minimum (lower bound) TOF nSigma for proton"}; + Configurable minCombinedNSigmaProton{"minCombinedNSigmaProton", -6.0f, "Minimum combined nSigma for proton (asymmetric mode)"}; + Configurable maxTPCNSigmaProton{"maxTPCNSigmaProton", 3.0, "Maximum |TPC nSigma| for proton"}; + Configurable combinedNSigmaCutProton{"combinedNSigmaCutProton", 3.0, "Circular TPC+TOF combined nSigma cut for proton. Negative = asymmetric mode."}; + Configurable> protonTPCPIDMomentumBins{"protonTPCPIDMomentumBins", {0.f, 0.5f, 0.7f, 0.8f}, "Proton TPC PID momentum bin edges [GeV/c]"}; + Configurable> protonTPCNSigmaCutPerBin{"protonTPCNSigmaCutPerBin", {5.f, 3.5f, 2.5f}, "Maximum TPC nSigma for proton per momentum bin"}; + Configurable> protonTOFPIDMomentumBins{"protonTOFPIDMomentumBins", {0.f, 999.f}, "Proton TOF PID momentum bin edges [GeV/c]"}; + Configurable> protonTOFNSigmaCutPerBin{"protonTOFNSigmaCutPerBin", {3.0f}, "Maximum TOF nSigma for proton per momentum bin"}; + Configurable shiftInNsigmaTOFProton{"shiftInNsigmaTOFProton", 0.0f, "Shift applied to proton TOF nSigma"}; + } protonPID; + + struct : ConfigurableGroup { + Configurable requireTOFForPion{"requireTOFForPion", false, "Require TOF signal for pion candidates"}; + Configurable minPionMomentum{"minPionMomentum", 0.f, "Minimum pion momentum for TOF PID [GeV/c]"}; + Configurable minTPCNSigmaPion{"minTPCNSigmaPion", -6.0f, "Minimum (lower bound) TPC nSigma for pion"}; + Configurable minTOFNSigmaPion{"minTOFNSigmaPion", -6.0f, "Minimum (lower bound) TOF nSigma for pion"}; + Configurable minCombinedNSigmaPion{"minCombinedNSigmaPion", -6.0f, "Minimum combined nSigma for pion (asymmetric mode)"}; + Configurable maxTPCNSigmaPion{"maxTPCNSigmaPion", 3.0, "Maximum |TPC nSigma| for pion"}; + Configurable combinedNSigmaCutPion{"combinedNSigmaCutPion", 3.0, "Circular TPC+TOF combined nSigma cut for pion. Negative = asymmetric mode."}; + Configurable> pionTPCPIDMomentumBins{"pionTPCPIDMomentumBins", {0.f, 0.25f, 0.4f, 0.5f}, "Pion TPC PID momentum bin edges [GeV/c]"}; + Configurable> pionTPCNSigmaCutPerBin{"pionTPCNSigmaCutPerBin", {5.f, 3.5f, 2.5f}, "Maximum TPC nSigma for pion per momentum bin"}; + Configurable> pionTOFPIDMomentumBins{"pionTOFPIDMomentumBins", {0.f, 999.f}, "Pion TOF PID momentum bin edges [GeV/c]"}; + Configurable> pionTOFNSigmaCutPerBin{"pionTOFNSigmaCutPerBin", {3.0f}, "Maximum TOF nSigma for pion per momentum bin"}; + Configurable shiftInNsigmaTOFPion{"shiftInNsigmaTOFPion", 0.0f, "Shift applied to pion TOF nSigma"}; + } pionPID; + + struct : ConfigurableGroup { + Configurable applyDeepAngleCut{"applyDeepAngleCut", false, "Apply minimum opening-angle cut (removes split-track background)"}; + Configurable deepAngleCutValue{"deepAngleCutValue", 0.04, "Minimum opening angle between proton and pion [rad]"}; + } pairCuts; + + struct : ConfigurableGroup { + Configurable cfgNoMixedEvents{"cfgNoMixedEvents", 5, "Number of mixed events per signal event"}; + Configurable enableMCEventMixing{"enableMCEventMixing", false, "Enable event mixing for reconstructed MC (independent of the DATA event-mixing switch)"}; + } mixingCfg; + + struct : ConfigurableGroup { + Configurable enableRotationalBackground{"enableRotationalBackground", false, "Compute rotational background by rotating pion phi near PI"}; + Configurable numberOfRotations{"numberOfRotations", 10, "Number of pion-phi rotations for background"}; + Configurable rotationAngleWindow{"rotationAngleWindow", 6.f, "Pion rotated by angles within PI +/- PI/rotationAngleWindow"}; + } rotBkg; + + struct : ConfigurableGroup { + ConfigurableAxis cfgPtAxis{"cfgPtAxis", {VARIABLE_WIDTH, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.8, 3.2, 3.6, 4.0, 5.0, 7.0, 10.0}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis cfgCentAxis{"cfgCentAxis", {VARIABLE_WIDTH, 0.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f}, "Centrality (%)"}; + ConfigurableAxis cfgVtxAxis{"cfgVtxAxis", {VARIABLE_WIDTH, -12.f, -10.f, -9.f, -8.f, -7.f, -6.f, -5.f, -4.f, -3.f, -2.f, -1.f, 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 12.f}, "Vertex z [cm]"}; + ConfigurableAxis cfgRapAxis{"cfgRapAxis", {20, -1.0, 1.0}, "Rapidity y"}; + } axes; + + std::vector mProtonTPCMomBins; + std::vector mProtonTPCNSigCuts; + std::vector mProtonTOFMomBins; + std::vector mProtonTOFNSigCuts; + std::vector mPionTPCMomBins; + std::vector mPionTPCNSigCuts; + std::vector mPionTOFMomBins; + std::vector mPionTOFNSigCuts; + std::vector mProtonDCAPtEdges; + std::vector mProtonMaxDCAxy; + std::vector mPionDCAPtEdges; + std::vector mPionMaxDCAxy; + + void init(InitContext const&) + { + auto chkSz = [](auto const& cuts, auto const& bins, const char* label) { + const auto& cutsVec = cuts.value; + const auto& binsVec = bins.value; + if (cutsVec.size() != binsVec.size() - 1) { + LOG(fatal) << "PID/DCA bin-size mismatch for: " << label + << " (cuts=" << cutsVec.size() + << ", bins=" << binsVec.size() << ")"; + } + }; + chkSz(protonPID.protonTPCNSigmaCutPerBin, protonPID.protonTPCPIDMomentumBins, "proton TPC PID"); + chkSz(protonPID.protonTOFNSigmaCutPerBin, protonPID.protonTOFPIDMomentumBins, "proton TOF PID"); + chkSz(pionPID.pionTPCNSigmaCutPerBin, pionPID.pionTPCPIDMomentumBins, "pion TPC PID"); + chkSz(pionPID.pionTOFNSigmaCutPerBin, pionPID.pionTOFPIDMomentumBins, "pion TOF PID"); + chkSz(dcaCuts.protonMaxDCAxyPerPtBin, dcaCuts.protonDCAPtBinEdges, "proton DCA"); + chkSz(dcaCuts.pionMaxDCAxyPerPtBin, dcaCuts.pionDCAPtBinEdges, "pion DCA"); + + mProtonTPCMomBins = protonPID.protonTPCPIDMomentumBins.value; + mProtonTPCNSigCuts = protonPID.protonTPCNSigmaCutPerBin.value; + mProtonTOFMomBins = protonPID.protonTOFPIDMomentumBins.value; + mProtonTOFNSigCuts = protonPID.protonTOFNSigmaCutPerBin.value; + mPionTPCMomBins = pionPID.pionTPCPIDMomentumBins.value; + mPionTPCNSigCuts = pionPID.pionTPCNSigmaCutPerBin.value; + mPionTOFMomBins = pionPID.pionTOFPIDMomentumBins.value; + mPionTOFNSigCuts = pionPID.pionTOFNSigmaCutPerBin.value; + mProtonDCAPtEdges = dcaCuts.protonDCAPtBinEdges.value; + mProtonMaxDCAxy = dcaCuts.protonMaxDCAxyPerPtBin.value; + mPionDCAPtEdges = dcaCuts.pionDCAPtBinEdges.value; + mPionMaxDCAxy = dcaCuts.pionMaxDCAxyPerPtBin.value; + + const AxisSpec ptAxis{50, 0., 20., "p_{T} (GeV/c)"}; + const AxisSpec massAxis{trackCuts.numberOfInvMassBins, 1.0, 6.0, "M_{inv} (GeV/#it{c}^{2})"}; + const AxisSpec centAxis{axes.cfgCentAxis, "Centrality (%)"}; + const AxisSpec vtxAxis{axes.cfgVtxAxis, "Vertex z [cm]"}; + const AxisSpec rapAxis{axes.cfgRapAxis, "Rapidity y"}; + const AxisSpec nSigmaTPCaxis{100, -5., 5., "n#sigma^{TPC}"}; + const AxisSpec nSigmaTOFaxis{100, -5., 5., "n#sigma^{TOF}"}; + const AxisSpec momentumAxis{50, 0., 20., "p (GeV/#it{c})"}; + const AxisSpec ptForPIDAxis{50, 0., 20., "p_{T} (GeV/#it{c})"}; + const AxisSpec dcaXYaxis{200, -0.2f, 0.2f, "DCA_{xy} (cm)"}; + const AxisSpec dcaZaxis{200, -1.2f, 1.2f, "DCA_{z} (cm)"}; + const AxisSpec tpcClusAxis{200, 0, 200, "N_{clusters}^{TPC}"}; + const AxisSpec tpcRowsAxis{200, 0, 200, "Crossed rows^{TPC}"}; + const AxisSpec occupancyAxis{200, 0, 10000, "Track occupancy [-40, 100] ns"}; + const AxisSpec etaAxis{100, -1.0, 1.0, "#eta"}; + const AxisSpec openAngleAxis{100, 0., o2::constants::math::PI, "Opening angle (rad)"}; + + histos.add("Event/hVtxZ", "Vertex z; z (cm)", kTH1F, {{400, -20., 20.}}); + histos.add("Event/hNcontributor", "PV contributors; N", kTH1F, {{2001, -0.5f, 2000.5f}}); + histos.add("Event/hCentrality", "Centrality", kTH1F, {centAxis}); + histos.add("Event/hOccupancy", "Occupancy in time range", kTH1F, {occupancyAxis}); + histos.add("Event/centralitydistribution", "Centrality distribution (Data);vCentFT0M;Entries", kTH1F, {centAxis}); + + histos.add("CentQA/hCentralityVsVtxZ", "Centrality vs vertex z", kTH2F, {vtxAxis, centAxis}); + histos.add("CentQA/hCentralityVsOccupancy", "Centrality vs occupancy", kTH2F, {occupancyAxis, centAxis}); + histos.add("CentQA/hEventCountVsCentrality", "Event count vs centrality", kTH1F, {centAxis}); + + histos.add("OccupancyQA/hOccupancyVsCentralityBefore", "Occupancy vs centrality (before cut)", kTH2F, {centAxis, occupancyAxis}); + histos.add("OccupancyQA/hOccupancyVsVtxZBefore", "Occupancy vs vertex z (before cut)", kTH2F, {vtxAxis, occupancyAxis}); + histos.add("OccupancyQA/hOccupancyVsCentralityAfter", "Occupancy vs centrality (after cut)", kTH2F, {centAxis, occupancyAxis}); + histos.add("OccupancyQA/hOccupancyVsVtxZAfter", "Occupancy vs vertex z (after cut)", kTH2F, {vtxAxis, occupancyAxis}); + + histos.add("QAbefore/Proton/tpcNSigmaVsMomentum", "TPC n#sigma proton vs p (before cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("QAbefore/Proton/tofNSigmaVsMomentum", "TOF n#sigma proton vs p (before cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAbefore/Proton/tofNSigmaVsTPCNSigma", "TOF vs TPC n#sigma proton (before cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("QAbefore/Pion/tpcNSigmaVsMomentum", "TPC n#sigma pion vs p (before cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("QAbefore/Pion/tofNSigmaVsMomentum", "TOF n#sigma pion vs p (before cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAbefore/Pion/tofNSigmaVsTPCNSigma", "TOF vs TPC n#sigma pion (before cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + + // ── Reconstructed-track kinematic QA (Data, before track-quality/PID/DCA cuts) ────────── + histos.add("QAbefore/hEta_rec", "Reco dN/d#eta; #eta; dN/d#eta", kTH1F, {{50, -1.0, 1.0}}); + histos.add("QAbefore/hPt_rec", "Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("QAbefore/hPhi_rec", "Reco #varphi; #varphi (rad); Tracks", kTH1F, {{72, 0, 6.2832}}); + histos.add("QAbefore/hEtaPhi_rec", "Reco #eta vs #varphi; #eta; #varphi", kTH2F, {etaAxis, {72, 0, 6.2832}}); + + histos.add("QAafter/Proton/dcaXYvsPt", "Proton DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("QAafter/Proton/dcaZvsPt", "Proton DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("QAafter/Proton/tpcNSigmaVsMomentum", "Proton TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("QAafter/Proton/tpcNSigmaVsPt", "Proton TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAafter/Proton/tpcNSigmaVsCentrality", "Proton TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("QAafter/Proton/tofNSigmaVsMomentum", "Proton TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAafter/Proton/tofNSigmaVsPt", "Proton TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("QAafter/Proton/tofNSigmaVsCentrality", "Proton TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("QAafter/Proton/tofNSigmaVsTPCNSigma", "Proton TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("QAafter/Proton/tpcNSigmaPionContamVsPt", "Proton track: TPC n#sigma pion contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAafter/Proton/tpcNSigmaKaonContamVsPt", "Proton track: TPC n#sigma kaon contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAafter/Proton/tofNSigmaPionContamVsMomentum", "Proton track: TOF n#sigma pion contamination", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAafter/Proton/tpcCrossedRowsVsPt", "Proton TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("QAafter/Proton/tpcClustersFoundVsPt", "Proton TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("QAafter/Proton/dcaXYdist", "Proton DCA_{xy} distribution (fine bins)", kTH1F, {dcaXYaxis}); + histos.add("QAafter/Proton/dcaZdist", "Proton DCA_{z} distribution (fine bins)", kTH1F, {dcaZaxis}); + + histos.add("QAafter/Pion/dcaXYvsPt", "Pion DCA_{xy} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("QAafter/Pion/dcaZvsPt", "Pion DCA_{z} vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("QAafter/Pion/tpcNSigmaVsMomentum", "Pion TPC n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("QAafter/Pion/tpcNSigmaVsPt", "Pion TPC n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAafter/Pion/tpcNSigmaVsCentrality", "Pion TPC n#sigma vs centrality", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("QAafter/Pion/tofNSigmaVsMomentum", "Pion TOF n#sigma vs p (after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAafter/Pion/tofNSigmaVsPt", "Pion TOF n#sigma vs p_{T} (after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("QAafter/Pion/tofNSigmaVsCentrality", "Pion TOF n#sigma vs centrality", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("QAafter/Pion/tofNSigmaVsTPCNSigma", "Pion TOF vs TPC n#sigma (after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("QAafter/Pion/tpcNSigmaProtonContamVsPt", "Pion track: TPC n#sigma proton contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAafter/Pion/tpcNSigmaKaonContamVsPt", "Pion track: TPC n#sigma kaon contamination", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAafter/Pion/tofNSigmaProtonContamVsMomentum", "Pion track: TOF n#sigma proton contamination", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAafter/Pion/tpcCrossedRowsVsPt", "Pion TPC crossed rows vs p_{T}", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("QAafter/Pion/tpcClustersFoundVsPt", "Pion TPC clusters found vs p_{T}", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("QAafter/Pion/dcaXYdist", "Pion DCA_{xy} distribution (fine bins)", kTH1F, {dcaXYaxis}); + histos.add("QAafter/Pion/dcaZdist", "Pion DCA_{z} distribution (fine bins)", kTH1F, {dcaZaxis}); + + histos.add("QAChecks/Pair/hOpeningAngleBefore", "Opening angle (before cuts)", kTH1F, {openAngleAxis}); + histos.add("QAChecks/Pair/hOpeningAngleAfter", "Opening angle (after cuts)", kTH1F, {openAngleAxis}); + + histos.add("Analysis/hDeltaPlusPlusInvMass", "#Delta^{++} (#rightarrow p + #pi^{+}) invariant mass", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaPlusPlusInvMass", "#bar{#Delta}^{++} (#rightarrow #bar{p} + #pi^{-}) invariant mass", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hDeltaZeroInvMass", "#Delta^{0} (#rightarrow p + #pi^{-}) invariant mass", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaZeroInvMass", "#bar{#Delta}^{0} (#rightarrow #bar{p} + #pi^{+}) invariant mass", kTH2F, {ptAxis, massAxis}); + + if (rotBkg.enableRotationalBackground) { + histos.add("Analysis/hDeltaPlusPlusInvMassRot", "#Delta^{++} invariant mass - rotational background", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaPlusPlusInvMassRot", "#bar{#Delta}^{++} invariant mass - rotational background", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hDeltaZeroInvMassRot", "#Delta^{0} invariant mass - rotational background", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaZeroInvMassRot", "#bar{#Delta}^{0} invariant mass - rotational background", kTH2F, {ptAxis, massAxis}); + } + + if (doprocessMixedEvent) { + histos.add("Analysis/hDeltaPlusPlusInvMassEM", "#Delta^{++} invariant mass - event mixing", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaPlusPlusInvMassEM", "#bar{#Delta}^{++} invariant mass - event mixing", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hDeltaZeroInvMassEM", "#Delta^{0} invariant mass - event mixing", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaZeroInvMassEM", "#bar{#Delta}^{0} invariant mass - event mixing", kTH2F, {ptAxis, massAxis}); + } + + histos.add("THnSparse/hDeltaPlusPlus", "THnSparse #Delta^{++} same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaPlusPlus", "THnSparse #bar{#Delta}^{++} same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hDeltaZero", "THnSparse #Delta^{0} same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaZero", "THnSparse #bar{#Delta}^{0} same-event", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + if (rotBkg.enableRotationalBackground) { + histos.add("THnSparse/hDeltaPlusPlusRot", "THnSparse #Delta^{++} rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaPlusPlusRot", "THnSparse #bar{#Delta}^{++} rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hDeltaZeroRot", "THnSparse #Delta^{0} rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaZeroRot", "THnSparse #bar{#Delta}^{0} rotational background", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + } + + if (doprocessMixedEvent) { + histos.add("THnSparse/hDeltaPlusPlusEM", "THnSparse #Delta^{++} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaPlusPlusEM", "THnSparse #bar{#Delta}^{++} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hDeltaZeroEM", "THnSparse #Delta^{0} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaZeroEM", "THnSparse #bar{#Delta}^{0} event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + } + + if (doprocessMC) { + histos.add("THnSparse/hDeltaPlusPlusMC", "THnSparse #Delta^{++} MC reconstructed", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaPlusPlusMC", "THnSparse #bar{#Delta}^{++} MC reconstructed", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hDeltaZeroMC", "THnSparse #Delta^{0} MC reconstructed", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparse/hAntiDeltaZeroMC", "THnSparse #bar{#Delta}^{0} MC reconstructed", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("Analysis/hDeltaPlusPlusInvMassMC", "#Delta^{++} invariant mass (MC truth-matched)", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaPlusPlusInvMassMC", "#bar{#Delta}^{++} invariant mass (MC truth-matched)", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hDeltaZeroInvMassMC", "#Delta^{0} invariant mass (MC truth-matched)", kTH2F, {ptAxis, massAxis}); + histos.add("Analysis/hAntiDeltaZeroInvMassMC", "#bar{#Delta}^{0} invariant mass (MC truth-matched)", kTH2F, {ptAxis, massAxis}); + + histos.add("QAChecks/hRecProtonDeltaPlusPlus", "Rec proton from #Delta^{++}", kTH1F, {ptAxis}); + histos.add("QAChecks/hRecProtonAntiDeltaPlusPlus", "Rec proton from #bar{#Delta}^{++}", kTH1F, {ptAxis}); + histos.add("QAChecks/hRecProtonDeltaZero", "Rec proton from #Delta^{0}", kTH1F, {ptAxis}); + histos.add("QAChecks/hRecProtonAntiDeltaZero", "Rec proton from #bar{#Delta}^{0}", kTH1F, {ptAxis}); + histos.add("QAChecks/hRecPionDeltaPlusPlus", "Rec pion from #Delta^{++}", kTH1F, {ptAxis}); + histos.add("QAChecks/hRecPionAntiDeltaPlusPlus", "Rec pion from #bar{#Delta}^{++}", kTH1F, {ptAxis}); + histos.add("QAChecks/hRecPionDeltaZero", "Rec pion from #Delta^{0}", kTH1F, {ptAxis}); + histos.add("QAChecks/hRecPionAntiDeltaZero", "Rec pion from #bar{#Delta}^{0}", kTH1F, {ptAxis}); + + histos.add("AnalysisMCReco/hDeltaPlusPlusInvMassReco", "#Delta^{++} invariant mass - MC reconstructed (pre-truth-matching)", kTH2F, {ptAxis, massAxis}); + histos.add("AnalysisMCReco/hAntiDeltaPlusPlusInvMassReco", "#bar{#Delta}^{++} invariant mass - MC reconstructed (pre-truth-matching)", kTH2F, {ptAxis, massAxis}); + histos.add("AnalysisMCReco/hDeltaZeroInvMassReco", "#Delta^{0} invariant mass - MC reconstructed (pre-truth-matching)", kTH2F, {ptAxis, massAxis}); + histos.add("AnalysisMCReco/hAntiDeltaZeroInvMassReco", "#bar{#Delta}^{0} invariant mass - MC reconstructed (pre-truth-matching)", kTH2F, {ptAxis, massAxis}); + + histos.add("THnSparseMCReco/hDeltaPlusPlusReco", "THnSparse #Delta^{++} MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparseMCReco/hAntiDeltaPlusPlusReco", "THnSparse #bar{#Delta}^{++} MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparseMCReco/hDeltaZeroReco", "THnSparse #Delta^{0} MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparseMCReco/hAntiDeltaZeroReco", "THnSparse #bar{#Delta}^{0} MC reconstructed (pre-truth-matching)", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("QAMC/Proton/dcaXYvsPt", "Proton DCA_{xy} vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("QAMC/Proton/dcaZvsPt", "Proton DCA_{z} vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("QAMC/Proton/tpcNSigmaVsMomentum", "Proton TPC n#sigma vs p (MC reco, after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("QAMC/Proton/tpcNSigmaVsPt", "Proton TPC n#sigma vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAMC/Proton/tpcNSigmaVsCentrality", "Proton TPC n#sigma vs centrality (MC reco)", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("QAMC/Proton/tofNSigmaVsMomentum", "Proton TOF n#sigma vs p (MC reco, after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAMC/Proton/tofNSigmaVsPt", "Proton TOF n#sigma vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("QAMC/Proton/tofNSigmaVsCentrality", "Proton TOF n#sigma vs centrality (MC reco)", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("QAMC/Proton/tofNSigmaVsTPCNSigma", "Proton TOF vs TPC n#sigma (MC reco, after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("QAMC/Proton/tpcNSigmaPionContamVsPt", "Proton track: TPC n#sigma pion contamination (MC reco)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAMC/Proton/tpcNSigmaKaonContamVsPt", "Proton track: TPC n#sigma kaon contamination (MC reco)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAMC/Proton/tofNSigmaPionContamVsMomentum", "Proton track: TOF n#sigma pion contamination (MC reco)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAMC/Proton/tpcCrossedRowsVsPt", "Proton TPC crossed rows vs p_{T} (MC reco)", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("QAMC/Proton/tpcClustersFoundVsPt", "Proton TPC clusters found vs p_{T} (MC reco)", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("QAMC/Proton/dcaXYdist", "Proton DCA_{xy} distribution (MC reco, fine bins)", kTH1F, {dcaXYaxis}); + histos.add("QAMC/Proton/dcaZdist", "Proton DCA_{z} distribution (MC reco, fine bins)", kTH1F, {dcaZaxis}); + + histos.add("QAMC/Pion/dcaXYvsPt", "Pion DCA_{xy} vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, dcaXYaxis}); + histos.add("QAMC/Pion/dcaZvsPt", "Pion DCA_{z} vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, dcaZaxis}); + histos.add("QAMC/Pion/tpcNSigmaVsMomentum", "Pion TPC n#sigma vs p (MC reco, after cuts)", kTH2F, {momentumAxis, nSigmaTPCaxis}); + histos.add("QAMC/Pion/tpcNSigmaVsPt", "Pion TPC n#sigma vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAMC/Pion/tpcNSigmaVsCentrality", "Pion TPC n#sigma vs centrality (MC reco)", kTH2F, {centAxis, nSigmaTPCaxis}); + histos.add("QAMC/Pion/tofNSigmaVsMomentum", "Pion TOF n#sigma vs p (MC reco, after cuts)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAMC/Pion/tofNSigmaVsPt", "Pion TOF n#sigma vs p_{T} (MC reco, after cuts)", kTH2F, {ptForPIDAxis, nSigmaTOFaxis}); + histos.add("QAMC/Pion/tofNSigmaVsCentrality", "Pion TOF n#sigma vs centrality (MC reco)", kTH2F, {centAxis, nSigmaTOFaxis}); + histos.add("QAMC/Pion/tofNSigmaVsTPCNSigma", "Pion TOF vs TPC n#sigma (MC reco, after cuts)", kTH2F, {nSigmaTPCaxis, nSigmaTOFaxis}); + histos.add("QAMC/Pion/tpcNSigmaProtonContamVsPt", "Pion track: TPC n#sigma proton contamination (MC reco)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAMC/Pion/tpcNSigmaKaonContamVsPt", "Pion track: TPC n#sigma kaon contamination (MC reco)", kTH2F, {ptForPIDAxis, nSigmaTPCaxis}); + histos.add("QAMC/Pion/tofNSigmaProtonContamVsMomentum", "Pion track: TOF n#sigma proton contamination (MC reco)", kTH2F, {momentumAxis, nSigmaTOFaxis}); + histos.add("QAMC/Pion/tpcCrossedRowsVsPt", "Pion TPC crossed rows vs p_{T} (MC reco)", kTH2F, {ptForPIDAxis, tpcRowsAxis}); + histos.add("QAMC/Pion/tpcClustersFoundVsPt", "Pion TPC clusters found vs p_{T} (MC reco)", kTH2F, {ptForPIDAxis, tpcClusAxis}); + histos.add("QAMC/Pion/dcaXYdist", "Pion DCA_{xy} distribution (MC reco, fine bins)", kTH1F, {dcaXYaxis}); + histos.add("QAMC/Pion/dcaZdist", "Pion DCA_{z} distribution (MC reco, fine bins)", kTH1F, {dcaZaxis}); + + // ── Reconstructed-track kinematic QA (MC reco, before track-quality/PID/DCA cuts) ──── + histos.add("QAMC/hEta_rec", "MC Reco dN/d#eta; #eta; dN/d#eta", kTH1F, {{50, -1.0, 1.0}}); + histos.add("QAMC/hPt_rec", "MC Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {ptForPIDAxis}); + histos.add("QAMC/hPhi_rec", "MC Reco #varphi; #varphi (rad); Tracks", kTH1F, {{72, 0, 6.2832}}); + histos.add("QAMC/hEtaPhi_rec", "MC Reco #eta vs #varphi; #eta; #varphi", kTH2F, {etaAxis, {72, 0, 6.2832}}); + + histos.add("MCRecoEvent/hRecoEvents", "Reconstructed INEL>0 events (Nrec, MC reco)", kTH1F, {centAxis}); + histos.add("MCRecoEvent/centralitydistribution", "Centrality distribution (MC);vCentFT0M;Entries", kTH1F, {centAxis}); + } + + // ── MC reconstructed event mixing: histograms (gated by the dedicated MC mixing switch) + if (mixingCfg.enableMCEventMixing) { + histos.add("AnalysisMCReco/hDeltaPlusPlusInvMassEM", "#Delta^{++} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); + histos.add("AnalysisMCReco/hAntiDeltaPlusPlusInvMassEM", "#bar{#Delta}^{++} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); + histos.add("AnalysisMCReco/hDeltaZeroInvMassEM", "#Delta^{0} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); + histos.add("AnalysisMCReco/hAntiDeltaZeroInvMassEM", "#bar{#Delta}^{0} invariant mass - MC reconstructed event mixing", kTH2F, {ptAxis, massAxis}); + + histos.add("THnSparseMCReco/hDeltaPlusPlusEM", "THnSparse #Delta^{++} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparseMCReco/hAntiDeltaPlusPlusEM", "THnSparse #bar{#Delta}^{++} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparseMCReco/hDeltaZeroEM", "THnSparse #Delta^{0} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("THnSparseMCReco/hAntiDeltaZeroEM", "THnSparse #bar{#Delta}^{0} MC reconstructed event mixing", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("QAMC/EventMixing/hMixedEventPairs", "Number of MC mixed-event collision pairs processed", kTH1F, {{1, 0.5f, 1.5f}}); + histos.add("QAMC/EventMixing/hMixedEventPairsByCentrality", "MC mixed-event collision pairs vs centrality", kTH1F, {centAxis}); + } + + // ── processMCGen(): generated-level Delta spectra + QA ───────────────────────────────── + if (doprocessMCGen) { + histos.add("MCGen/hDeltaPlusPlusGenSparse", "THnSparse #Delta^{++} generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MCGen/hAntiDeltaPlusPlusGenSparse", "THnSparse #bar{#Delta}^{++} generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MCGen/hDeltaZeroGenSparse", "THnSparse #Delta^{0} generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MCGen/hAntiDeltaZeroGenSparse", "THnSparse #bar{#Delta}^{0} generated", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + + histos.add("MCGen/hDeltaPlusPlusInvMassGen", "#Delta^{++} invariant mass - generated", kTH2F, {ptAxis, massAxis}); + histos.add("MCGen/hAntiDeltaPlusPlusInvMassGen", "#bar{#Delta}^{++} invariant mass - generated", kTH2F, {ptAxis, massAxis}); + histos.add("MCGen/hDeltaZeroInvMassGen", "#Delta^{0} invariant mass - generated", kTH2F, {ptAxis, massAxis}); + histos.add("MCGen/hAntiDeltaZeroInvMassGen", "#bar{#Delta}^{0} invariant mass - generated", kTH2F, {ptAxis, massAxis}); + + histos.add("MCGenQA/hGenProtonDeltaPlusPlus", "Gen proton from #Delta^{++}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenProtonAntiDeltaPlusPlus", "Gen proton from #bar{#Delta}^{++}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenProtonDeltaZero", "Gen proton from #Delta^{0}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenProtonAntiDeltaZero", "Gen proton from #bar{#Delta}^{0}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenPionDeltaPlusPlus", "Gen pion from #Delta^{++}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenPionAntiDeltaPlusPlus", "Gen pion from #bar{#Delta}^{++}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenPionDeltaZero", "Gen pion from #Delta^{0}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenPionAntiDeltaZero", "Gen pion from #bar{#Delta}^{0}", kTH1F, {ptAxis}); + histos.add("MCGenQA/hGenDeltaCutFlow", "Generated #Delta cut flow", kTH1F, {{8, -0.5f, 7.5f}}); + + histos.add("MCGen/GeneratedDelta_EventAccepted/hDeltaPlusPlusInvMass", "#Delta^{++} generated, event-accepted", kTH2F, {ptAxis, massAxis}); + histos.add("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaPlusPlusInvMass", "#bar{#Delta}^{++} generated, event-accepted", kTH2F, {ptAxis, massAxis}); + histos.add("MCGen/GeneratedDelta_EventAccepted/hDeltaZeroInvMass", "#Delta^{0} generated, event-accepted", kTH2F, {ptAxis, massAxis}); + histos.add("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaZeroInvMass", "#bar{#Delta}^{0} generated, event-accepted", kTH2F, {ptAxis, massAxis}); + histos.add("MCGen/GeneratedDelta_EventAccepted/hDeltaPlusPlusSparse", "THnSparse #Delta^{++} generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaPlusPlusSparse", "THnSparse #bar{#Delta}^{++} generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MCGen/GeneratedDelta_EventAccepted/hDeltaZeroSparse", "THnSparse #Delta^{0} generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + histos.add("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaZeroSparse", "THnSparse #bar{#Delta}^{0} generated, event-accepted", kTHnSparseF, {massAxis, ptAxis, centAxis, rapAxis}); + } + + // ── processEventFactor(): event-level corrections only (Event Loss / Event Splitting) ── + if (doprocessEventFactor) { + histos.add("EventFactor/hRecoEvents", "Reconstructed INEL>0 events (Nrec)", kTH1F, {centAxis}); + histos.add("EventFactor/hEventsGenAll", "Generated events passing truth |Zvtx| + truth INEL>0 cuts", kTH1F, {centAxis}); + histos.add("EventFactor/hEventsGenAccepted", "Generated events with >=1 accepted reconstructed collision (truth centrality)", kTH1F, {centAxis}); + histos.add("EventFactor/hEventsGenAcceptedReco", "Generated events with >=1 accepted reconstructed collision (reconstructed centrality)", kTH1F, {centAxis}); + histos.add("EventFactor/hNRecoCollisionsPerMcCollision", "Number of reconstructed collisions per generated collision", kTH1F, {{21, -0.5f, 20.5f}}); + } + + // ── Event-level cut-flow (QA-only) histograms ─────────────────────────────────────────── + { + histos.add("CutFlow/Data/hEventCutFlow", "Data event cut flow", kTH1F, {{9, -0.5f, 8.5f}}); + auto hDataCutFlow = histos.get(HIST("CutFlow/Data/hEventCutFlow")); + hDataCutFlow->GetXaxis()->SetBinLabel(1, "All"); + hDataCutFlow->GetXaxis()->SetBinLabel(2, "sel8"); + hDataCutFlow->GetXaxis()->SetBinLabel(3, "INEL>0"); + hDataCutFlow->GetXaxis()->SetBinLabel(4, "Vz"); + hDataCutFlow->GetXaxis()->SetBinLabel(5, "Occupancy"); + hDataCutFlow->GetXaxis()->SetBinLabel(6, "Centrality"); + hDataCutFlow->GetXaxis()->SetBinLabel(7, "Pileup"); + hDataCutFlow->GetXaxis()->SetBinLabel(8, "GoodZvtx"); + hDataCutFlow->GetXaxis()->SetBinLabel(9, "Accepted"); + } + + if (doprocessMC) { + histos.add("CutFlow/MCReco/hEventCutFlow", "MC reconstructed event cut flow", kTH1F, {{9, -0.5f, 8.5f}}); + auto hMcRecoCutFlow = histos.get(HIST("CutFlow/MCReco/hEventCutFlow")); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(1, "All"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(2, "sel8"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(3, "INEL>0"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(4, "Vz"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(5, "Occupancy"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(6, "Centrality"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(7, "Pileup"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(8, "GoodZvtx"); + hMcRecoCutFlow->GetXaxis()->SetBinLabel(9, "Accepted"); + } + + if (doprocessMCGen) { + histos.add("CutFlow/MCGen/hEventCutFlow", "MC generated event cut flow", kTH1F, {{5, -0.5f, 4.5f}}); + auto hMcGenCutFlow = histos.get(HIST("CutFlow/MCGen/hEventCutFlow")); + hMcGenCutFlow->GetXaxis()->SetBinLabel(1, "All"); + hMcGenCutFlow->GetXaxis()->SetBinLabel(2, "Vz"); + hMcGenCutFlow->GetXaxis()->SetBinLabel(3, "INEL>0"); + hMcGenCutFlow->GetXaxis()->SetBinLabel(4, "Centrality"); + hMcGenCutFlow->GetXaxis()->SetBinLabel(5, "Accepted"); + } + + if (doprocessEventFactor) { + histos.add("CutFlow/EventFactor/hEventAcceptedCutFlow", "Event Loss / Event Splitting: generated-event cut flow", kTH1F, {{6, -0.5f, 5.5f}}); + auto hEventFactorCutFlow = histos.get(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow")); + hEventFactorCutFlow->GetXaxis()->SetBinLabel(1, "All"); + hEventFactorCutFlow->GetXaxis()->SetBinLabel(2, "Vz"); + hEventFactorCutFlow->GetXaxis()->SetBinLabel(3, "INEL>0"); + hEventFactorCutFlow->GetXaxis()->SetBinLabel(4, "HasRecoColl"); + hEventFactorCutFlow->GetXaxis()->SetBinLabel(5, "RecoCollAccepted"); + hEventFactorCutFlow->GetXaxis()->SetBinLabel(6, "EventAccepted"); + } + + if (doprocessMCGen) { + histos.add("EfficiencyQA/hGeneratedEventCutFlow", "Generated-event cut flow (efficiency QA)", kTH1F, {{5, -0.5f, 4.5f}}); + auto hGenEventCutFlow = histos.get(HIST("EfficiencyQA/hGeneratedEventCutFlow")); + hGenEventCutFlow->GetXaxis()->SetBinLabel(1, "All generated events"); + hGenEventCutFlow->GetXaxis()->SetBinLabel(2, "|Vz| accepted"); + hGenEventCutFlow->GetXaxis()->SetBinLabel(3, "Truth INEL>0"); + hGenEventCutFlow->GetXaxis()->SetBinLabel(4, "Final generated event"); + hGenEventCutFlow->GetXaxis()->SetBinLabel(5, "Associated reconstructed event accepted"); + } + + if (doprocessMC) { + histos.add("EfficiencyQA/hDeltaRecoCutFlow", "Generated-to-reconstructed #Delta cut flow (efficiency QA)", kTH1F, {{21, -0.5f, 20.5f}}); + auto hDeltaRecoCutFlow = histos.get(HIST("EfficiencyQA/hDeltaRecoCutFlow")); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(1, "Generated Delta (event accepted)"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(2, "Delta -> p + pi"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(3, "Delta rapidity accepted"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(4, "Proton acceptance"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(5, "Pion acceptance"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(6, "Both daughters acceptance"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(7, "Proton reconstructed"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(8, "Pion reconstructed"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(9, "Both daughters reconstructed"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(10, "Proton track quality"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(11, "Pion track quality"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(12, "Both track quality"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(13, "Proton PID"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(14, "Pion PID"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(15, "Both PID"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(16, "Proton DCA"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(17, "Pion DCA"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(18, "Both DCA"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(19, "Common MC mother"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(20, "Common Delta mother"); + hDeltaRecoCutFlow->GetXaxis()->SetBinLabel(21, "Final reconstructed Delta"); + + histos.add("EfficiencyQA/hTruthMatchCutFlow", "MC truth-matching cut flow (efficiency QA)", kTH1F, {{7, -0.5f, 6.5f}}); + auto hTruthMatchCutFlow = histos.get(HIST("EfficiencyQA/hTruthMatchCutFlow")); + hTruthMatchCutFlow->GetXaxis()->SetBinLabel(1, "Reco pair"); + hTruthMatchCutFlow->GetXaxis()->SetBinLabel(2, "Proton has MC label"); + hTruthMatchCutFlow->GetXaxis()->SetBinLabel(3, "Pion has MC label"); + hTruthMatchCutFlow->GetXaxis()->SetBinLabel(4, "Proton is proton"); + hTruthMatchCutFlow->GetXaxis()->SetBinLabel(5, "Pion is pion"); + hTruthMatchCutFlow->GetXaxis()->SetBinLabel(6, "Common MC mother"); + hTruthMatchCutFlow->GetXaxis()->SetBinLabel(7, "Mother is Delta"); + } + } // end init() + + template + float getCentrality(CollisionType const& collision) + { + switch (evSel.cfgCentralityEstimator) { + case delta_analysis::kFT0M: + return collision.centFT0M(); + case delta_analysis::kFT0A: + return collision.centFT0A(); + case delta_analysis::kFT0C: + return collision.centFT0C(); + case delta_analysis::kFV0A: + return collision.centFV0A(); + case delta_analysis::kNTPV: + return collision.centNTPV(); + default: + return collision.centFT0M(); + } + } + + // INEL>0 (reconstructed): at least one PV-associated track within |eta| < 1. + template + bool isRecoInelGt0(CollisionType const& collision) + { + return collision.multNTracksPVeta1() > 0; + } + + template + bool isChargedPrimaryMC(McParticleType const& mcPart) + { + if (!mcPart.isPhysicalPrimary()) { + return false; + } + auto* pdgParticle = pdgDB->GetParticle(mcPart.pdgCode()); + if (!pdgParticle) { + return false; + } + return std::abs(pdgParticle->Charge()) > MinAbsCharge; + } + + template + bool isTruthInelGt0(McParticleTableType const& mcParticlesInCollision) + { + for (auto const& mcPart : mcParticlesInCollision) { + if (std::abs(mcPart.eta()) < 1.f && isChargedPrimaryMC(mcPart)) { + return true; + } + } + return false; + } + + struct NoCutFlowTag { + }; + struct DataCutFlowTag { + }; + struct McRecoCutFlowTag { + }; + + template + void fillEventCutFlowBin(float bin) + { + if constexpr (std::is_same_v) { + histos.fill(HIST("CutFlow/Data/hEventCutFlow"), bin); + } else if constexpr (std::is_same_v) { + histos.fill(HIST("CutFlow/MCReco/hEventCutFlow"), bin); + } + // NoCutFlowTag: intentionally does nothing. + } + + template + bool passesEventSelectionImpl(CollisionType const& collision) + { + fillEventCutFlowBin(0.f); // All collisions + + if (!collision.sel8()) { + return false; + } + fillEventCutFlowBin(1.f); // sel8 + + if (evSel.cfgRequireRecoINELgt0 && !isRecoInelGt0(collision)) { + return false; + } + fillEventCutFlowBin(2.f); // Reconstructed INEL>0 + + if (std::abs(collision.posZ()) > evSel.cfgCutVertex) { + return false; + } + fillEventCutFlowBin(3.f); // |Vz| < cfgCutVertex + + if (evSel.applyOccupancyInTimeRangeCut) { + const int occ = collision.trackOccupancyInTimeRange(); + if (occ < evSel.cfgOccupancyMin || occ > evSel.cfgOccupancyMax) { + return false; + } + } + fillEventCutFlowBin(4.f); // Occupancy cut + + fillEventCutFlowBin(5.f); // Centrality range (bin retained for cut-flow numbering; no longer a cut) + + if (evSel.cfgUseNoSameBunchPileupCut && + !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + fillEventCutFlowBin(6.f); // NoSameBunchPileup + + if (evSel.cfgUseGoodZvtxFT0vsPVCut && + !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + fillEventCutFlowBin(7.f); // GoodZvtxFT0vsPV + + if (evSel.cfgUseGoodITSLayersAll && + !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + + fillEventCutFlowBin(8.f); // Final accepted event + return true; + } + + // passesEventSelection with optional EventSelection cuts + template + bool passesEventSelection(CollisionType const& collision) + { + return passesEventSelectionImpl(collision); + } + + template + bool passesBasicTrackSelection(TrackType const& track) + { + if (trackCuts.applyITSClusterCut && + track.itsNCls() < trackCuts.cfgMinITSClusters) { + return false; + } + if (trackCuts.applyTPCSharedClusterCut && + track.tpcNClsShared() > trackCuts.cfgMaxTPCSharedClusters) { + return false; + } + if (trackCuts.applyTPCFoundClusterCut && + track.tpcNClsFound() < trackCuts.cfgMinTPCClusters) { + return false; + } + if (trackCuts.applyTPCCrossedRowsCut && + track.tpcNClsCrossedRows() < trackCuts.cfgMinTPCCrossedRows) { + return false; + } + if (trackCuts.applyTPCCrossedRowsOverFindableCut && + track.tpcNClsCrossedRows() < trackCuts.cfgMinCrossedRowsOverFindable * track.tpcNClsFindable()) { + return false; + } + if (trackCuts.applyTPCChi2NClCut && + track.tpcChi2NCl() > trackCuts.cfgMaxTPCChi2NCl) { + return false; + } + if (trackCuts.applyITSChi2NClCut && + track.itsChi2NCl() > trackCuts.cfgMaxITSChi2NCl) { + return false; + } + if (trackCuts.requirePrimaryTrack && !track.isPrimaryTrack()) { + return false; + } + if (trackCuts.requireGlobalTrack && !track.isGlobalTrack()) { + return false; + } + if (trackCuts.requirePVContributor && !track.isPVContributor()) { + return false; + } + return true; + } + + template + bool passesProtonDCASelection(TrackType const& track) + { + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxDCAxy = dcaConst + dcaCoeff / std::pow(track.pt(), dcaExp); + return (std::abs(track.dcaXY()) < maxDCAxy) && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + + const int nBins = static_cast(mProtonDCAPtEdges.size()) - 1; + const float pt = track.pt(); + bool passed = false; + for (int i = 0; i < nBins; ++i) { + if (pt >= mProtonDCAPtEdges[i] && pt < mProtonDCAPtEdges[i + 1] && + std::abs(track.dcaXY()) < mProtonMaxDCAxy[i]) { + passed = true; + break; + } + } + return passed && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + + template + bool passesPionDCASelection(TrackType const& track) + { + if (dcaCuts.isApplyPtDepDCAxyCut) { + const float dcaConst = dcaCuts.dcaXYConst; + const float dcaCoeff = dcaCuts.dcaXYPtCoeff; + const float dcaExp = dcaCuts.dcaXYPtExponent; + const float maxDCAxy = dcaConst + dcaCoeff / std::pow(track.pt(), dcaExp); + return (std::abs(track.dcaXY()) < maxDCAxy) && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + + const int nBins = static_cast(mPionDCAPtEdges.size()) - 1; + const float pt = track.pt(); + bool passed = false; + for (int i = 0; i < nBins; ++i) { + if (pt >= mPionDCAPtEdges[i] && pt < mPionDCAPtEdges[i + 1] && + std::abs(track.dcaXY()) < mPionMaxDCAxy[i]) { + passed = true; + break; + } + } + return passed && (std::abs(track.dcaZ()) < dcaCuts.cfgCutDCAz); + } + + // ── PID decision flow ──────────────────────────────────────────────────────────────────────────── + template + bool passesProtonPID(TrackType const& track, float totalMomentum) + { + const float tpcNSigPi = std::abs(track.tpcNSigmaPi()); + const float tpcNSigPr = std::abs(track.tpcNSigmaPr()); + const float tofNSigPi = std::abs(track.tofNSigmaPi()); + const float tofNSigPr = std::abs(track.tofNSigmaPr()); + + const bool useTOF = !pidShared.useTPCOnlyPID && track.hasTOF(); + + if (useTOF) { + // Combined TPC+TOF PID - used whenever TOF is available, regardless of momentum. + if (protonPID.combinedNSigmaCutProton < 0) { + // Asymmetric mode: independent TPC and TOF nSigma windows. + if (track.tofNSigmaPr() < protonPID.minTOFNSigmaProton) { + return false; + } + bool tofPassed = false; + const int nTOFBins = static_cast(mProtonTOFMomBins.size()); + for (int i = 0; i < nTOFBins - 1; ++i) { + if (totalMomentum >= mProtonTOFMomBins[i] && totalMomentum < mProtonTOFMomBins[i + 1] && + tofNSigPr < mProtonTOFNSigCuts[i] && tofNSigPi > pidShared.tofNSigmaVetoThreshold) { + tofPassed = true; + break; + } + } + if (track.tpcNSigmaPr() < protonPID.minCombinedNSigmaProton) { + return false; + } + const bool tpcPassed = tpcNSigPr < static_cast(protonPID.maxTPCNSigmaProton) && tpcNSigPi > pidShared.tpcNSigmaVetoThreshold; + return tofPassed && tpcPassed; + } + + // Circular mode: joint TPC+TOF nSigma radius. + const float shiftedTofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); + const float combinedNSigPr = tpcNSigPr * tpcNSigPr + shiftedTofNSigPr * shiftedTofNSigPr; + const float combinedNSigPi = tpcNSigPi * tpcNSigPi + tofNSigPi * tofNSigPi; + const auto circularCutSq = static_cast(protonPID.combinedNSigmaCutProton * protonPID.combinedNSigmaCutProton); + const float circularVetoCutSq = pidShared.tpcNSigmaVetoThreshold * pidShared.tpcNSigmaVetoThreshold + + pidShared.tofNSigmaVetoThreshold * pidShared.tofNSigmaVetoThreshold; + return combinedNSigPr < circularCutSq && combinedNSigPi > circularVetoCutSq; + } + + // No usable TOF: above the momentum threshold, TOF is mandatory -> reject. + if (totalMomentum >= protonPID.minProtonMomentum) { + return false; + } + + // TPC-only PID below the momentum threshold, using the momentum-binned TPC cuts. + const int nTPCBins = static_cast(mProtonTPCMomBins.size()); + for (int i = 0; i < nTPCBins - 1; ++i) { + if (totalMomentum >= mProtonTPCMomBins[i] && totalMomentum < mProtonTPCMomBins[i + 1]) { + return tpcNSigPr < mProtonTPCNSigCuts[i] && tpcNSigPi > pidShared.tpcNSigmaVetoThreshold; + } + } + return false; + } + + template + bool passesPionPID(TrackType const& track, float totalMomentum) + { + const float tpcNSigPi = std::abs(track.tpcNSigmaPi()); + const float tpcNSigPr = std::abs(track.tpcNSigmaPr()); + const float tofNSigPi = std::abs(track.tofNSigmaPi()); + const float tofNSigPr = std::abs(track.tofNSigmaPr()); + + const bool useTOF = !pidShared.useTPCOnlyPID && track.hasTOF(); + + if (useTOF) { + // Combined TPC+TOF PID - used whenever TOF is available, regardless of momentum. + if (pionPID.combinedNSigmaCutPion < 0) { + // Asymmetric mode: independent TPC and TOF nSigma windows. + if (track.tofNSigmaPi() < pionPID.minTOFNSigmaPion) { + return false; + } + bool tofPassed = false; + const int nTOFBins = static_cast(mPionTOFMomBins.size()); + for (int i = 0; i < nTOFBins - 1; ++i) { + if (totalMomentum >= mPionTOFMomBins[i] && totalMomentum < mPionTOFMomBins[i + 1] && + tofNSigPi < mPionTOFNSigCuts[i] && tofNSigPr > pidShared.tofNSigmaVetoThreshold) { + tofPassed = true; + break; + } + } + if (track.tpcNSigmaPi() < pionPID.minCombinedNSigmaPion) { + return false; + } + const bool tpcPassed = tpcNSigPi < static_cast(pionPID.maxTPCNSigmaPion) && tpcNSigPr > pidShared.tpcNSigmaVetoThreshold; + return tofPassed && tpcPassed; + } + + // Circular mode: joint TPC+TOF nSigma radius. + const float shiftedTofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); + const float combinedNSigPi = tpcNSigPi * tpcNSigPi + shiftedTofNSigPi * shiftedTofNSigPi; + const float combinedNSigPr = tpcNSigPr * tpcNSigPr + tofNSigPr * tofNSigPr; + const auto circularCutSq = static_cast(pionPID.combinedNSigmaCutPion * pionPID.combinedNSigmaCutPion); + const float circularVetoCutSq = pidShared.tpcNSigmaVetoThreshold * pidShared.tpcNSigmaVetoThreshold + + pidShared.tofNSigmaVetoThreshold * pidShared.tofNSigmaVetoThreshold; + return combinedNSigPi < circularCutSq && combinedNSigPr > circularVetoCutSq; + } + + // No usable TOF: above the momentum threshold, TOF is mandatory -> reject. + if (totalMomentum >= pionPID.minPionMomentum) { + return false; + } + + // TPC-only PID below the momentum threshold, using the momentum-binned TPC cuts. + const int nTPCBins = static_cast(mPionTPCMomBins.size()); + for (int i = 0; i < nTPCBins - 1; ++i) { + if (totalMomentum >= mPionTPCMomBins[i] && totalMomentum < mPionTPCMomBins[i + 1]) { + return tpcNSigPi < mPionTPCNSigCuts[i] && tpcNSigPr > pidShared.tpcNSigmaVetoThreshold; + } + } + return false; + } + + template + void fillQAProton(TrackType const& track, float totalMomentum, float centralityPercent) + { + const float pt = track.pt(); + const float tpcNSigPr = track.tpcNSigmaPr(); + histos.fill(HIST("QAafter/Proton/dcaXYvsPt"), pt, track.dcaXY()); + histos.fill(HIST("QAafter/Proton/dcaZvsPt"), pt, track.dcaZ()); + histos.fill(HIST("QAafter/Proton/dcaXYdist"), track.dcaXY()); + histos.fill(HIST("QAafter/Proton/dcaZdist"), track.dcaZ()); + histos.fill(HIST("QAafter/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigPr); + histos.fill(HIST("QAafter/Proton/tpcNSigmaVsPt"), pt, tpcNSigPr); + histos.fill(HIST("QAafter/Proton/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigPr); + histos.fill(HIST("QAafter/Proton/tpcNSigmaPionContamVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("QAafter/Proton/tpcNSigmaKaonContamVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); + if (!pidShared.useTPCOnlyPID && track.hasTOF()) { + const float tofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); + histos.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigPr); + histos.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), pt, tofNSigPr); + histos.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigPr); + histos.fill(HIST("QAafter/Proton/tofNSigmaVsTPCNSigma"), tpcNSigPr, tofNSigPr); + histos.fill(HIST("QAafter/Proton/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + } + } + + template + void fillQAPion(TrackType const& track, float totalMomentum, float centralityPercent) + { + const float pt = track.pt(); + const float tpcNSigPi = track.tpcNSigmaPi(); + histos.fill(HIST("QAafter/Pion/dcaXYvsPt"), pt, track.dcaXY()); + histos.fill(HIST("QAafter/Pion/dcaZvsPt"), pt, track.dcaZ()); + histos.fill(HIST("QAafter/Pion/dcaXYdist"), track.dcaXY()); + histos.fill(HIST("QAafter/Pion/dcaZdist"), track.dcaZ()); + histos.fill(HIST("QAafter/Pion/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigPi); + histos.fill(HIST("QAafter/Pion/tpcNSigmaVsPt"), pt, tpcNSigPi); + histos.fill(HIST("QAafter/Pion/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigPi); + histos.fill(HIST("QAafter/Pion/tpcNSigmaProtonContamVsPt"), pt, track.tpcNSigmaPr()); + histos.fill(HIST("QAafter/Pion/tpcNSigmaKaonContamVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("QAafter/Pion/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("QAafter/Pion/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); + if (!pidShared.useTPCOnlyPID && track.hasTOF()) { + const float tofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); + histos.fill(HIST("QAafter/Pion/tofNSigmaVsMomentum"), totalMomentum, tofNSigPi); + histos.fill(HIST("QAafter/Pion/tofNSigmaVsPt"), pt, tofNSigPi); + histos.fill(HIST("QAafter/Pion/tofNSigmaVsCentrality"), centralityPercent, tofNSigPi); + histos.fill(HIST("QAafter/Pion/tofNSigmaVsTPCNSigma"), tpcNSigPi, tofNSigPi); + histos.fill(HIST("QAafter/Pion/tofNSigmaProtonContamVsMomentum"), totalMomentum, track.tofNSigmaPr()); + } + } + + template + void fillQAMCProton(TrackType const& track, float totalMomentum, float centralityPercent) + { + const float pt = track.pt(); + const float tpcNSigPr = track.tpcNSigmaPr(); + histos.fill(HIST("QAMC/Proton/dcaXYvsPt"), pt, track.dcaXY()); + histos.fill(HIST("QAMC/Proton/dcaZvsPt"), pt, track.dcaZ()); + histos.fill(HIST("QAMC/Proton/dcaXYdist"), track.dcaXY()); + histos.fill(HIST("QAMC/Proton/dcaZdist"), track.dcaZ()); + histos.fill(HIST("QAMC/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigPr); + histos.fill(HIST("QAMC/Proton/tpcNSigmaVsPt"), pt, tpcNSigPr); + histos.fill(HIST("QAMC/Proton/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigPr); + histos.fill(HIST("QAMC/Proton/tpcNSigmaPionContamVsPt"), pt, track.tpcNSigmaPi()); + histos.fill(HIST("QAMC/Proton/tpcNSigmaKaonContamVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("QAMC/Proton/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("QAMC/Proton/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); + if (!pidShared.useTPCOnlyPID && track.hasTOF()) { + const float tofNSigPr = track.tofNSigmaPr() - static_cast(protonPID.shiftInNsigmaTOFProton); + histos.fill(HIST("QAMC/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigPr); + histos.fill(HIST("QAMC/Proton/tofNSigmaVsPt"), pt, tofNSigPr); + histos.fill(HIST("QAMC/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigPr); + histos.fill(HIST("QAMC/Proton/tofNSigmaVsTPCNSigma"), tpcNSigPr, tofNSigPr); + histos.fill(HIST("QAMC/Proton/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + } + } + + template + void fillQAMCPion(TrackType const& track, float totalMomentum, float centralityPercent) + { + const float pt = track.pt(); + const float tpcNSigPi = track.tpcNSigmaPi(); + histos.fill(HIST("QAMC/Pion/dcaXYvsPt"), pt, track.dcaXY()); + histos.fill(HIST("QAMC/Pion/dcaZvsPt"), pt, track.dcaZ()); + histos.fill(HIST("QAMC/Pion/dcaXYdist"), track.dcaXY()); + histos.fill(HIST("QAMC/Pion/dcaZdist"), track.dcaZ()); + histos.fill(HIST("QAMC/Pion/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigPi); + histos.fill(HIST("QAMC/Pion/tpcNSigmaVsPt"), pt, tpcNSigPi); + histos.fill(HIST("QAMC/Pion/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigPi); + histos.fill(HIST("QAMC/Pion/tpcNSigmaProtonContamVsPt"), pt, track.tpcNSigmaPr()); + histos.fill(HIST("QAMC/Pion/tpcNSigmaKaonContamVsPt"), pt, track.tpcNSigmaKa()); + histos.fill(HIST("QAMC/Pion/tpcCrossedRowsVsPt"), pt, track.tpcNClsCrossedRows()); + histos.fill(HIST("QAMC/Pion/tpcClustersFoundVsPt"), pt, track.tpcNClsFound()); + if (!pidShared.useTPCOnlyPID && track.hasTOF()) { + const float tofNSigPi = track.tofNSigmaPi() - static_cast(pionPID.shiftInNsigmaTOFPion); + histos.fill(HIST("QAMC/Pion/tofNSigmaVsMomentum"), totalMomentum, tofNSigPi); + histos.fill(HIST("QAMC/Pion/tofNSigmaVsPt"), pt, tofNSigPi); + histos.fill(HIST("QAMC/Pion/tofNSigmaVsCentrality"), centralityPercent, tofNSigPi); + histos.fill(HIST("QAMC/Pion/tofNSigmaVsTPCNSigma"), tpcNSigPi, tofNSigPi); + histos.fill(HIST("QAMC/Pion/tofNSigmaProtonContamVsMomentum"), totalMomentum, track.tofNSigmaPr()); + } + } + + void fillDeltaHistogramSameEvent(int protonSign, int pionSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign > 0) { + if (pionSign > 0) { + histos.fill(HIST("Analysis/hDeltaPlusPlusInvMass"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hDeltaPlusPlus"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Analysis/hDeltaZeroInvMass"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hDeltaZero"), pairMass, pairPt, centrality, rapidity); + } + } else { + if (pionSign < 0) { + histos.fill(HIST("Analysis/hAntiDeltaPlusPlusInvMass"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hAntiDeltaPlusPlus"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Analysis/hAntiDeltaZeroInvMass"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hAntiDeltaZero"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillDeltaHistogramMixedEvent(int protonSign, int pionSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign > 0) { + if (pionSign > 0) { + histos.fill(HIST("Analysis/hDeltaPlusPlusInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hDeltaPlusPlusEM"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Analysis/hDeltaZeroInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hDeltaZeroEM"), pairMass, pairPt, centrality, rapidity); + } + } else { + if (pionSign < 0) { + histos.fill(HIST("Analysis/hAntiDeltaPlusPlusInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hAntiDeltaPlusPlusEM"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Analysis/hAntiDeltaZeroInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hAntiDeltaZeroEM"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillDeltaHistogramMC(int protonSign, int pionSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign > 0) { + if (pionSign > 0) { + histos.fill(HIST("Analysis/hDeltaPlusPlusInvMassMC"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hDeltaPlusPlusMC"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Analysis/hDeltaZeroInvMassMC"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hDeltaZeroMC"), pairMass, pairPt, centrality, rapidity); + } + } else { + if (pionSign < 0) { + histos.fill(HIST("Analysis/hAntiDeltaPlusPlusInvMassMC"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hAntiDeltaPlusPlusMC"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("Analysis/hAntiDeltaZeroInvMassMC"), pairPt, pairMass); + histos.fill(HIST("THnSparse/hAntiDeltaZeroMC"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillDeltaHistogramMCReco(int protonSign, int pionSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign > 0) { + if (pionSign > 0) { + histos.fill(HIST("AnalysisMCReco/hDeltaPlusPlusInvMassReco"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hDeltaPlusPlusReco"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("AnalysisMCReco/hDeltaZeroInvMassReco"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hDeltaZeroReco"), pairMass, pairPt, centrality, rapidity); + } + } else { + if (pionSign < 0) { + histos.fill(HIST("AnalysisMCReco/hAntiDeltaPlusPlusInvMassReco"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hAntiDeltaPlusPlusReco"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("AnalysisMCReco/hAntiDeltaZeroInvMassReco"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hAntiDeltaZeroReco"), pairMass, pairPt, centrality, rapidity); + } + } + } + + // MCReco EVENT MIXING ONLY - fills the dedicated MCReco-EM histograms. Never called with + // truth-matching information: mixed-event daughters come from two different MC collisions, + // so a common Delta mother is not physically meaningful (see fillInvariantMassHistogramsMCMixedFromPools()). + void fillDeltaHistogramMCRecoMixedEvent(int protonSign, int pionSign, float pairPt, float pairMass, float centrality, float rapidity) + { + if (protonSign > 0) { + if (pionSign > 0) { + histos.fill(HIST("AnalysisMCReco/hDeltaPlusPlusInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hDeltaPlusPlusEM"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("AnalysisMCReco/hDeltaZeroInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hDeltaZeroEM"), pairMass, pairPt, centrality, rapidity); + } + } else { + if (pionSign < 0) { + histos.fill(HIST("AnalysisMCReco/hAntiDeltaPlusPlusInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hAntiDeltaPlusPlusEM"), pairMass, pairPt, centrality, rapidity); + } else { + histos.fill(HIST("AnalysisMCReco/hAntiDeltaZeroInvMassEM"), pairPt, pairMass); + histos.fill(HIST("THnSparseMCReco/hAntiDeltaZeroEM"), pairMass, pairPt, centrality, rapidity); + } + } + } + + void fillRotationalBackground(int protonSign, int pionSign, float pxProton, float pyProton, float pzProton, float pionPhi, float pionPt, float pzPion, float centrality) + { + if (rotBkg.numberOfRotations <= 0) { + return; + } + const float weight = 1.f / static_cast(rotBkg.numberOfRotations); + const float rotWindowHalf = o2::constants::math::PI / static_cast(rotBkg.rotationAngleWindow); + + for (int iRot = 0; iRot < rotBkg.numberOfRotations; ++iRot) { + float rotAngle = 0.f; + if (rotBkg.numberOfRotations == 1) { + rotAngle = o2::constants::math::PI; + } else { + rotAngle = (o2::constants::math::PI - rotWindowHalf) + + static_cast(iRot) * (2.f * rotWindowHalf / static_cast(rotBkg.numberOfRotations - 1)); + } + const float newPhi = RecoDecay::constrainAngle(pionPhi + rotAngle, 0.f); + const float pxPionRot = pionPt * std::cos(newPhi); + const float pyPionRot = pionPt * std::sin(newPhi); + const std::array, 2> rotMomenta = { + std::array{pxProton, pyProton, pzProton}, + std::array{pxPionRot, pyPionRot, pzPion}}; + const float rotMass = RecoDecay::m(rotMomenta, kProtonPionMasses); + const float rotPt = RecoDecay::pt(std::array{pxProton + pxPionRot, pyProton + pyPionRot}); + const float rotY = RecoDecay::y( + std::array{pxProton + pxPionRot, pyProton + pyPionRot, pzProton + pzPion}, rotMass); + if (rotY < trackCuts.cfgMinY || rotY > trackCuts.cfgMaxY) { + continue; + } + + if (protonSign > 0) { + if (pionSign > 0) { + histos.fill(HIST("Analysis/hDeltaPlusPlusInvMassRot"), rotPt, rotMass, weight); + histos.fill(HIST("THnSparse/hDeltaPlusPlusRot"), rotMass, rotPt, centrality, rotY, weight); + } else { + histos.fill(HIST("Analysis/hDeltaZeroInvMassRot"), rotPt, rotMass, weight); + histos.fill(HIST("THnSparse/hDeltaZeroRot"), rotMass, rotPt, centrality, rotY, weight); + } + } else { + if (pionSign < 0) { + histos.fill(HIST("Analysis/hAntiDeltaPlusPlusInvMassRot"), rotPt, rotMass, weight); + histos.fill(HIST("THnSparse/hAntiDeltaPlusPlusRot"), rotMass, rotPt, centrality, rotY, weight); + } else { + histos.fill(HIST("Analysis/hAntiDeltaZeroInvMassRot"), rotPt, rotMass, weight); + histos.fill(HIST("THnSparse/hAntiDeltaZeroRot"), rotMass, rotPt, centrality, rotY, weight); + } + } + } + } + + void fillPairQABefore(float pxPr, float pyPr, float pzPr, float pxPi, float pyPi, float pzPi, float momPr, float momPi) + { + const float cosAngle = std::clamp((pxPr * pxPi + pyPr * pyPi + pzPr * pzPi) / (momPr * momPi), -1.f, 1.f); + histos.fill(HIST("QAChecks/Pair/hOpeningAngleBefore"), std::acos(cosAngle)); + } + + void fillPairQAAfter(float pxPr, float pyPr, float pzPr, float pxPi, float pyPi, float pzPi, float momPr, float momPi) + { + const float cosAngle = std::clamp((pxPr * pxPi + pyPr * pyPi + pzPr * pzPi) / (momPr * momPi), -1.f, 1.f); + histos.fill(HIST("QAChecks/Pair/hOpeningAngleAfter"), std::acos(cosAngle)); + } + + // ── EfficiencyQA-only helpers (used exclusively to fill EfficiencyQA/hDeltaRecoCutFlow) ──── + // These do not feed into, and are not called by, any existing selection or histogram fill. + // They exist solely so the diagnostic cut flow can reuse the existing selection functions + // verbatim instead of re-implementing any cut. + struct EfficiencyQADaughterMatch { + bool found = false; // a reconstructed track carrying this MC daughter's label was found + bool trackQuality = false; // that track passes the existing passesBasicTrackSelection() + bool pid = false; // that track passes the existing passesProtonPID()/passesPionPID() + bool dca = false; // that track passes the existing passesProtonDCASelection()/passesPionDCASelection() + }; + + template + EfficiencyQADaughterMatch matchDaughterForEfficiencyQA(TracksSlice const& tracksSlice, McPart const& mcDaughter, bool isProton) + { + EfficiencyQADaughterMatch result; + for (auto const& trk : tracksSlice) { + if (!trk.has_mcParticle() || trk.mcParticle().globalIndex() != mcDaughter.globalIndex()) { + continue; + } + result.found = true; + result.trackQuality = passesBasicTrackSelection(trk); + const float mom = RecoDecay::p(trk.px(), trk.py(), trk.pz()); + if (isProton) { + result.pid = passesProtonPID(trk, mom); + result.dca = passesProtonDCASelection(trk); + } else { + result.pid = passesPionPID(trk, mom); + result.dca = passesPionDCASelection(trk); + } + break; + } + return result; + } + + template + std::pair checkCommonMotherForEfficiencyQA(McPart const& mcA, McPart const& mcB) + { + bool anyCommon = false; + bool motherIsDelta = false; + for (const auto& motherA : mcA.template mothers_as()) { + for (const auto& motherB : mcB.template mothers_as()) { + if (motherA != motherB) { + continue; + } + anyCommon = true; + if (std::abs(motherA.pdgCode()) == delta_analysis::PdgDeltaPlusPlus || + std::abs(motherA.pdgCode()) == delta_analysis::PdgDeltaZero) { + motherIsDelta = true; + } + break; + } + if (motherIsDelta) { + break; + } + } + return {anyCommon, motherIsDelta}; + } + + struct TrackCandidate { + float px, py, pz, pt, eta, phi, dcaXY, dcaZ; + float mom; + int sign; + bool hasTOF; + float tpcNSigmaPr, tpcNSigmaPi; + uint64_t globalIndex; + }; + + template + void buildCandidatePools(TrackCollection const& tracks, std::vector& protons, std::vector& pions) + { + protons.clear(); + pions.clear(); + for (auto const& track : tracks) { + if (!passesBasicTrackSelection(track)) { + continue; + } + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + const bool isProton = passesProtonPID(track, mom) && passesProtonDCASelection(track) && + !(protonPID.requireTOFForProton && !track.hasTOF()); + const bool isPion = passesPionPID(track, mom) && passesPionDCASelection(track) && + !(pionPID.requireTOFForPion && !track.hasTOF()); + if (!isProton && !isPion) { + continue; + } + TrackCandidate cand{}; + cand.px = track.px(); + cand.py = track.py(); + cand.pz = track.pz(); + cand.pt = track.pt(); + cand.eta = track.eta(); + cand.phi = track.phi(); + cand.dcaXY = track.dcaXY(); + cand.dcaZ = track.dcaZ(); + cand.mom = mom; + cand.sign = track.sign(); + cand.hasTOF = track.hasTOF(); + cand.tpcNSigmaPr = track.tpcNSigmaPr(); + cand.tpcNSigmaPi = track.tpcNSigmaPi(); + cand.globalIndex = track.globalIndex(); + if (isProton) { + protons.push_back(cand); + } + if (isPion) { + pions.push_back(cand); + } + } + } + + template + void fillInvariantMassHistogramsFromPools( + std::vector const& protonPool, + std::vector const& pionPool, + float centrality, + bool fillPairQA = false) + { + for (auto const& protonCand : protonPool) { + for (auto const& pionCand : pionPool) { + + if constexpr (!isMixed) { + if (protonCand.globalIndex == pionCand.globalIndex) { + continue; + } + } + + const float pxPr = protonCand.px, pyPr = protonCand.py, pzPr = protonCand.pz; + const float pxPi = pionCand.px, pyPi = pionCand.py, pzPi = pionCand.pz; + + if constexpr (!isMixed) { + if (fillPairQA) { + fillPairQABefore(pxPr, pyPr, pzPr, pxPi, pyPi, pzPi, protonCand.mom, pionCand.mom); + } + } + + if (pairCuts.applyDeepAngleCut) { + const float cosAngle = std::clamp( + (pxPr * pxPi + pyPr * pyPi + pzPr * pzPi) / (protonCand.mom * pionCand.mom), -1.f, 1.f); + if (std::acos(cosAngle) < static_cast(pairCuts.deepAngleCutValue)) { + continue; + } + } + + const std::array, 2> bothMomenta = { + std::array{pxPr, pyPr, pzPr}, + std::array{pxPi, pyPi, pzPi}}; + const float pairMass = RecoDecay::m(bothMomenta, kProtonPionMasses); + const float pairPt = RecoDecay::pt(std::array{pxPr + pxPi, pyPr + pyPi}); + const float pairY = RecoDecay::y(std::array{pxPr + pxPi, pyPr + pyPi, pzPr + pzPi}, pairMass); + + if (pairY < trackCuts.cfgMinY || pairY > trackCuts.cfgMaxY) { + continue; + } + + if constexpr (!isMixed) { + if (fillPairQA) { + fillPairQAAfter(pxPr, pyPr, pzPr, pxPi, pyPi, pzPi, protonCand.mom, pionCand.mom); + } + } + + if constexpr (isMixed) { + fillDeltaHistogramMixedEvent(protonCand.sign, pionCand.sign, pairPt, pairMass, centrality, pairY); + } else { + fillDeltaHistogramSameEvent(protonCand.sign, pionCand.sign, pairPt, pairMass, centrality, pairY); + if (rotBkg.enableRotationalBackground) { + fillRotationalBackground(protonCand.sign, pionCand.sign, + pxPr, pyPr, pzPr, pionCand.phi, pionCand.pt, pzPi, centrality); + } + } + } + } + } + + // MCReco EVENT-MIXING pool-pairing. + void fillInvariantMassHistogramsMCMixedFromPools( + std::vector const& protonPool, + std::vector const& pionPool, + float centrality) + { + for (auto const& protonCand : protonPool) { + for (auto const& pionCand : pionPool) { + const float pxPr = protonCand.px, pyPr = protonCand.py, pzPr = protonCand.pz; + const float pxPi = pionCand.px, pyPi = pionCand.py, pzPi = pionCand.pz; + + if (pairCuts.applyDeepAngleCut) { + const float cosAngle = std::clamp( + (pxPr * pxPi + pyPr * pyPi + pzPr * pzPi) / (protonCand.mom * pionCand.mom), -1.f, 1.f); + if (std::acos(cosAngle) < static_cast(pairCuts.deepAngleCutValue)) { + continue; + } + } + + const std::array, 2> bothMomenta = { + std::array{pxPr, pyPr, pzPr}, + std::array{pxPi, pyPi, pzPi}}; + const float pairMass = RecoDecay::m(bothMomenta, kProtonPionMasses); + const float pairPt = RecoDecay::pt(std::array{pxPr + pxPi, pyPr + pyPi}); + const float pairY = RecoDecay::y(std::array{pxPr + pxPi, pyPr + pyPi, pzPr + pzPi}, pairMass); + + if (pairY < trackCuts.cfgMinY || pairY > trackCuts.cfgMaxY) { + continue; + } + + // NOTE: intentionally no truth-matching call here - see function docstring above. + fillDeltaHistogramMCRecoMixedEvent(protonCand.sign, pionCand.sign, pairPt, pairMass, centrality, pairY); + } + } + } + + Filter collisionFilter = nabs(aod::collision::posZ) < evSel.cfgCutVertex; + Filter acceptanceFilter = (nabs(aod::track::eta) < trackCuts.cfgCutEta && nabs(aod::track::pt) > trackCuts.cfgCutPt); + + using EventCandidates = soa::Filtered>; + using TrackCandidates = soa::Filtered>; + using TrackCandidatesMC = soa::Filtered>; + using EventCandidatesMC = soa::Join; + using McCollisionsCent = soa::Join; + + Preslice perCol = aod::track::collisionId; + Preslice perColMC = aod::track::collisionId; + Preslice perMcCollisionDelta = aod::mcparticle::mcCollisionId; + + // ── Event-mixing binning policies (one per centrality estimator) ──────────── + using BinningTypeFT0M = ColumnBinningPolicy; + using BinningTypeFT0A = ColumnBinningPolicy; + using BinningTypeFT0C = ColumnBinningPolicy; + using BinningTypeFV0A = ColumnBinningPolicy; + using BinningTypeNTPV = ColumnBinningPolicy; + + BinningTypeFT0M binningFT0M{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0A binningFT0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFT0C binningFT0C{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeFV0A binningFV0A{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + BinningTypeNTPV binningNTPV{{axes.cfgVtxAxis, axes.cfgCentAxis}}; + + SameKindPair pairFT0M{binningFT0M, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairFT0A{binningFT0A, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairFT0C{binningFT0C, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairFV0A{binningFV0A, mixingCfg.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairNTPV{binningNTPV, mixingCfg.cfgNoMixedEvents, -1, &cache}; + + std::vector mProtonPool; + std::vector mPionPool; + + void processSameEvent(EventCandidates const& collisions, + TrackCandidates const& tracks, + aod::BCs const&) + { + for (auto const& collision : collisions) { + if (!passesEventSelectionImpl(collision)) { + continue; + } + const float centrality = getCentrality(collision); + const int occupancy = collision.trackOccupancyInTimeRange(); + + histos.fill(HIST("OccupancyQA/hOccupancyVsCentralityBefore"), centrality, occupancy); + histos.fill(HIST("OccupancyQA/hOccupancyVsVtxZBefore"), collision.posZ(), occupancy); + histos.fill(HIST("Event/hNcontributor"), collision.numContrib()); + histos.fill(HIST("Event/hVtxZ"), collision.posZ()); + histos.fill(HIST("Event/hCentrality"), centrality); + histos.fill(HIST("Event/hOccupancy"), occupancy); + histos.fill(HIST("Event/centralitydistribution"), collision.centFT0M()); + histos.fill(HIST("CentQA/hCentralityVsVtxZ"), collision.posZ(), centrality); + histos.fill(HIST("CentQA/hCentralityVsOccupancy"), occupancy, centrality); + histos.fill(HIST("CentQA/hEventCountVsCentrality"), centrality); + histos.fill(HIST("OccupancyQA/hOccupancyVsCentralityAfter"), centrality, occupancy); + histos.fill(HIST("OccupancyQA/hOccupancyVsVtxZAfter"), collision.posZ(), occupancy); + + const uint64_t collIdx = collision.globalIndex(); + auto perColTracks = tracks.sliceBy(perCol, collIdx); + perColTracks.bindExternalIndices(&tracks); + + for (auto const& track : perColTracks) { + const float mom = RecoDecay::p(track.px(), track.py(), track.pz()); + histos.fill(HIST("QAbefore/Proton/tpcNSigmaVsMomentum"), mom, track.tpcNSigmaPr()); + if (track.hasTOF()) { + histos.fill(HIST("QAbefore/Proton/tofNSigmaVsMomentum"), mom, track.tofNSigmaPr()); + histos.fill(HIST("QAbefore/Proton/tofNSigmaVsTPCNSigma"), track.tpcNSigmaPr(), track.tofNSigmaPr()); + } + histos.fill(HIST("QAbefore/Pion/tpcNSigmaVsMomentum"), mom, track.tpcNSigmaPi()); + if (track.hasTOF()) { + histos.fill(HIST("QAbefore/Pion/tofNSigmaVsMomentum"), mom, track.tofNSigmaPi()); + histos.fill(HIST("QAbefore/Pion/tofNSigmaVsTPCNSigma"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + + // ── Reconstructed-track kinematic QA (Data), filled for all reconstructed tracks + // before any track-quality/PID/DCA selection is applied. ───────────────────────── + histos.fill(HIST("QAbefore/hEta_rec"), track.eta()); + histos.fill(HIST("QAbefore/hPt_rec"), track.pt()); + histos.fill(HIST("QAbefore/hPhi_rec"), track.phi()); + histos.fill(HIST("QAbefore/hEtaPhi_rec"), track.eta(), track.phi()); + + if (!passesBasicTrackSelection(track)) { + continue; + } + if (passesProtonPID(track, mom) && passesProtonDCASelection(track)) { + fillQAProton(track, mom, centrality); + } + if (passesPionPID(track, mom) && passesPionDCASelection(track)) { + fillQAPion(track, mom, centrality); + } + } + buildCandidatePools(perColTracks, mProtonPool, mPionPool); + fillInvariantMassHistogramsFromPools(mProtonPool, mPionPool, centrality, true); + } + } + PROCESS_SWITCH(DeltaAnalysis, processSameEvent, "Process same event", true); + + template + void runMixedEvent(PairType& mixingPair) + { + for (auto const& [c1, tracks1, c2, tracks2] : mixingPair) { + if (!passesEventSelection(c1) || !passesEventSelection(c2)) { + continue; + } + const float centrality = getCentrality(c1); + std::vector protonPool1, pionPool1, protonPool2, pionPool2; + buildCandidatePools(tracks1, protonPool1, pionPool1); + buildCandidatePools(tracks2, protonPool2, pionPool2); + fillInvariantMassHistogramsFromPools(protonPool1, pionPool2, centrality); + fillInvariantMassHistogramsFromPools(protonPool2, pionPool1, centrality); + } + } + + void processMixedEvent(EventCandidates const&, TrackCandidates const&) + { + switch (evSel.cfgCentralityEstimator) { + case delta_analysis::kFT0M: + runMixedEvent(pairFT0M); + break; + case delta_analysis::kFT0A: + runMixedEvent(pairFT0A); + break; + case delta_analysis::kFT0C: + runMixedEvent(pairFT0C); + break; + case delta_analysis::kFV0A: + runMixedEvent(pairFV0A); + break; + case delta_analysis::kNTPV: + runMixedEvent(pairNTPV); + break; + default: + runMixedEvent(pairFT0M); + break; + } + } + PROCESS_SWITCH(DeltaAnalysis, processMixedEvent, "Process mixed event", true); + + // ── MC reconstructed event mixing ──────────────────────────────────────────────────────── + // Mirrors runMixedEvent()/processMixedEvent() above exactly (same event-selection call, same + // "protons1+pions2" / "protons2+pions1" combinatorics, same centrality-estimator dispatch), + // but: + // * fills the dedicated MCReco-EM histograms instead of the DATA EM histograms + template + void runMixedEventMC(PairType& mixingPair) + { + for (auto const& [c1, tracks1, c2, tracks2] : mixingPair) { + if (!passesEventSelection(c1) || !passesEventSelection(c2)) { + continue; + } + const float centrality = getCentrality(c1); + + histos.fill(HIST("QAMC/EventMixing/hMixedEventPairs"), 1.f); + histos.fill(HIST("QAMC/EventMixing/hMixedEventPairsByCentrality"), centrality); + + std::vector protonPool1, pionPool1, protonPool2, pionPool2; + buildCandidatePools(tracks1, protonPool1, pionPool1); + buildCandidatePools(tracks2, protonPool2, pionPool2); + fillInvariantMassHistogramsMCMixedFromPools(protonPool1, pionPool2, centrality); + fillInvariantMassHistogramsMCMixedFromPools(protonPool2, pionPool1, centrality); + } + } + + void processMCEventMixing(EventCandidates const&, TrackCandidates const&) + { + if (!mixingCfg.enableMCEventMixing) { + return; + } + switch (evSel.cfgCentralityEstimator) { + case delta_analysis::kFT0M: + runMixedEventMC(pairFT0M); + break; + case delta_analysis::kFT0A: + runMixedEventMC(pairFT0A); + break; + case delta_analysis::kFT0C: + runMixedEventMC(pairFT0C); + break; + case delta_analysis::kFV0A: + runMixedEventMC(pairFV0A); + break; + case delta_analysis::kNTPV: + runMixedEventMC(pairNTPV); + break; + default: + runMixedEventMC(pairFT0M); + break; + } + } + PROCESS_SWITCH(DeltaAnalysis, processMCEventMixing, "Process MC reconstructed event mixing (reuses DATA mixing pairs; no truth matching)", false); + + // ===================================================================================== + // processMC(): RECONSTRUCTED-ONLY. Reconstructs Delta candidates, performs truth matching, + // fills reconstructed QA, fills reconstructed Delta histograms (RecoDelta - the A x eps numerator). + // ===================================================================================== + void processMC(EventCandidatesMC const& collisions, aod::BCs const&, TrackCandidatesMC const& tracks, aod::McParticles const& mcParticles) + { + for (auto const& collision : collisions) { + if (!passesEventSelectionImpl(collision)) { + continue; + } + + const float centrality = getCentrality(collision); + + // Nrec - "the total number of selected reconstructed INEL>0 events". + histos.fill(HIST("MCRecoEvent/hRecoEvents"), centrality); + histos.fill(HIST("MCRecoEvent/centralitydistribution"), collision.centFT0M()); + + histos.fill(HIST("Event/hNcontributor"), collision.numContrib()); + histos.fill(HIST("Event/hVtxZ"), collision.posZ()); + + const uint64_t collIdx = collision.globalIndex(); + auto perColTracks = tracks.sliceBy(perColMC, collIdx); + perColTracks.bindExternalIndices(&tracks); + perColTracks.bindExternalIndices(&mcParticles); + + if (collision.has_mcCollision()) { + auto qaGenDeltas = mcParticles.sliceBy(perMcCollisionDelta, collision.mcCollisionId()); + for (auto const& qaGenDelta : qaGenDeltas) { + if (!qaGenDelta.producedByGenerator()) { + continue; + } + const int qaGenPdg = qaGenDelta.pdgCode(); + if (std::abs(qaGenPdg) != delta_analysis::PdgDeltaPlusPlus && + std::abs(qaGenPdg) != delta_analysis::PdgDeltaZero) { + continue; + } + + const auto qaDaughters = qaGenDelta.daughters_as(); + bool qaHasPr = false, qaHasPi = false; + auto qaMcProton = qaGenDelta; // placeholder, overwritten below when qaHasPr is true + auto qaMcPion = qaGenDelta; // placeholder, overwritten below when qaHasPi is true + for (const auto& d : qaDaughters) { + if (std::abs(d.pdgCode()) == delta_analysis::PdgProton) { + qaHasPr = true; + qaMcProton = d; + } else if (std::abs(d.pdgCode()) == delta_analysis::PdgPion) { + qaHasPi = true; + qaMcPion = d; + } + } + if (!qaHasPr || !qaHasPi) { + continue; + } + + if (qaGenDelta.y() < trackCuts.cfgMinY || qaGenDelta.y() > trackCuts.cfgMaxY) { + continue; + } + + const bool qaProtonAcc = std::abs(qaMcProton.eta()) < trackCuts.cfgCutEta && qaMcProton.pt() > trackCuts.cfgCutPt; + const bool qaPionAcc = std::abs(qaMcPion.eta()) < trackCuts.cfgCutEta && qaMcPion.pt() > trackCuts.cfgCutPt; + if (qaProtonAcc) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 3.f); // Proton acceptance + } + if (qaPionAcc) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 4.f); // Pion acceptance + } + if (!qaProtonAcc || !qaPionAcc) { + continue; + } + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 5.f); // Both daughters acceptance + + const auto qaProtonMatch = matchDaughterForEfficiencyQA(perColTracks, qaMcProton, true); + const auto qaPionMatch = matchDaughterForEfficiencyQA(perColTracks, qaMcPion, false); + if (qaProtonMatch.found) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 6.f); // Proton reconstructed + } + if (qaPionMatch.found) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 7.f); // Pion reconstructed + } + if (!qaProtonMatch.found || !qaPionMatch.found) { + continue; + } + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 8.f); // Both daughters reconstructed + + if (qaProtonMatch.trackQuality) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 9.f); // Proton track quality + } + if (qaPionMatch.trackQuality) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 10.f); // Pion track quality + } + if (!qaProtonMatch.trackQuality || !qaPionMatch.trackQuality) { + continue; + } + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 11.f); // Both track quality + + if (qaProtonMatch.pid) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 12.f); // Proton PID + } + if (qaPionMatch.pid) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 13.f); // Pion PID + } + if (!qaProtonMatch.pid || !qaPionMatch.pid) { + continue; + } + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 14.f); // Both PID + + if (qaProtonMatch.dca) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 15.f); // Proton DCA + } + if (qaPionMatch.dca) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 16.f); // Pion DCA + } + if (!qaProtonMatch.dca || !qaPionMatch.dca) { + continue; + } + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 17.f); // Both DCA + + const auto [qaAnyCommonMother, qaMotherIsDelta] = checkCommonMotherForEfficiencyQA(qaMcProton, qaMcPion); + if (qaAnyCommonMother) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 18.f); // Common MC mother + } + if (qaMotherIsDelta) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 19.f); // Common Delta mother + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 20.f); // Final reconstructed Delta + } + } + } + + for (auto const& t0 : perColTracks) { + if (!passesBasicTrackSelection(t0) || !t0.has_mcParticle()) { + continue; + } + const auto mcTrack = t0.mcParticle(); + const float mom = RecoDecay::p(t0.px(), t0.py(), t0.pz()); + if (std::abs(mcTrack.pdgCode()) == delta_analysis::PdgProton && passesProtonPID(t0, mom)) { + for (const auto& mother : mcTrack.mothers_as()) { + if (std::abs(mother.pdgCode()) == delta_analysis::PdgDeltaPlusPlus) { + if (t0.sign() < 0) { + histos.fill(HIST("QAChecks/hRecProtonAntiDeltaPlusPlus"), t0.pt()); + } else { + histos.fill(HIST("QAChecks/hRecProtonDeltaPlusPlus"), t0.pt()); + } + } else if (std::abs(mother.pdgCode()) == delta_analysis::PdgDeltaZero) { + if (t0.sign() < 0) { + histos.fill(HIST("QAChecks/hRecProtonAntiDeltaZero"), t0.pt()); + } else { + histos.fill(HIST("QAChecks/hRecProtonDeltaZero"), t0.pt()); + } + } + } + } + if (std::abs(mcTrack.pdgCode()) == delta_analysis::PdgPion && passesPionPID(t0, mom)) { + for (const auto& mother : mcTrack.mothers_as()) { + if (std::abs(mother.pdgCode()) == delta_analysis::PdgDeltaPlusPlus) { + if (t0.sign() < 0) { + histos.fill(HIST("QAChecks/hRecPionAntiDeltaPlusPlus"), t0.pt()); + } else { + histos.fill(HIST("QAChecks/hRecPionDeltaPlusPlus"), t0.pt()); + } + } else if (std::abs(mother.pdgCode()) == delta_analysis::PdgDeltaZero) { + if (t0.sign() < 0) { + histos.fill(HIST("QAChecks/hRecPionAntiDeltaZero"), t0.pt()); + } else { + histos.fill(HIST("QAChecks/hRecPionDeltaZero"), t0.pt()); + } + } + } + } + } + + for (auto const& trackForQAMC : perColTracks) { + const float momQAMC = RecoDecay::p(trackForQAMC.px(), trackForQAMC.py(), trackForQAMC.pz()); + + // ── Reconstructed-track kinematic QA (MC reco), filled for all reconstructed tracks + // before any track-quality/PID/DCA selection is applied. ───────────────────────── + histos.fill(HIST("QAMC/hEta_rec"), trackForQAMC.eta()); + histos.fill(HIST("QAMC/hPt_rec"), trackForQAMC.pt()); + histos.fill(HIST("QAMC/hPhi_rec"), trackForQAMC.phi()); + histos.fill(HIST("QAMC/hEtaPhi_rec"), trackForQAMC.eta(), trackForQAMC.phi()); + + if (!passesBasicTrackSelection(trackForQAMC)) { + continue; + } + if (passesProtonPID(trackForQAMC, momQAMC) && passesProtonDCASelection(trackForQAMC)) { + fillQAMCProton(trackForQAMC, momQAMC, centrality); + } + if (passesPionPID(trackForQAMC, momQAMC) && passesPionDCASelection(trackForQAMC)) { + fillQAMCPion(trackForQAMC, momQAMC, centrality); + } + } + + for (auto const& [t0, t1] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(perColTracks, perColTracks))) { + if (t0.globalIndex() == t1.globalIndex()) { + continue; + } + if (!passesBasicTrackSelection(t0) || !passesBasicTrackSelection(t1)) { + continue; + } + const float momT0 = RecoDecay::p(t0.px(), t0.py(), t0.pz()); + const float momT1 = RecoDecay::p(t1.px(), t1.py(), t1.pz()); + if (!passesProtonPID(t0, momT0) || !passesPionPID(t1, momT1)) { + continue; + } + if (!passesProtonDCASelection(t0) || !passesPionDCASelection(t1)) { + continue; + } + + { + const std::array, 2> momentaReco = { + std::array{t0.px(), t0.py(), t0.pz()}, + std::array{t1.px(), t1.py(), t1.pz()}}; + const float pairMassReco = RecoDecay::m(momentaReco, kProtonPionMasses); + const float pairPtReco = RecoDecay::pt(std::array{t0.px() + t1.px(), t0.py() + t1.py()}); + const float pairYReco = RecoDecay::y(std::array{t0.px() + t1.px(), t0.py() + t1.py(), t0.pz() + t1.pz()}, pairMassReco); + if (pairYReco >= trackCuts.cfgMinY && pairYReco <= trackCuts.cfgMaxY) { + fillDeltaHistogramMCReco(t0.sign(), t1.sign(), pairPtReco, pairMassReco, centrality, pairYReco); + } + } + + histos.fill(HIST("EfficiencyQA/hTruthMatchCutFlow"), 0.f); // Reco pair + const bool qaProtonHasLabel = t0.has_mcParticle(); + if (qaProtonHasLabel) { + histos.fill(HIST("EfficiencyQA/hTruthMatchCutFlow"), 1.f); // Proton has MC label + } + const bool qaPionHasLabel = t1.has_mcParticle(); + if (qaProtonHasLabel && qaPionHasLabel) { + histos.fill(HIST("EfficiencyQA/hTruthMatchCutFlow"), 2.f); // Pion has MC label + } + if (!qaProtonHasLabel || !qaPionHasLabel) { + continue; + } + const auto mcProton = t0.mcParticle(); + const auto mcPion = t1.mcParticle(); + const bool qaProtonIsProton = std::abs(mcProton.pdgCode()) == delta_analysis::PdgProton; + if (qaProtonIsProton) { + histos.fill(HIST("EfficiencyQA/hTruthMatchCutFlow"), 3.f); // Proton is proton + } + if (!qaProtonIsProton) { + continue; + } + const bool qaPionIsPion = std::abs(mcPion.pdgCode()) == delta_analysis::PdgPion; + if (qaPionIsPion) { + histos.fill(HIST("EfficiencyQA/hTruthMatchCutFlow"), 4.f); // Pion is pion + } + if (!qaPionIsPion) { + continue; + } + bool foundMother = false; + bool qaAnyCommonMother = false; // EfficiencyQA-only: any shared mother, regardless of PDG + for (const auto& motherPr : mcProton.mothers_as()) { + for (const auto& motherPi : mcPion.mothers_as()) { + if (motherPr != motherPi) { + continue; + } + qaAnyCommonMother = true; + if (std::abs(motherPr.pdgCode()) != delta_analysis::PdgDeltaPlusPlus && + std::abs(motherPr.pdgCode()) != delta_analysis::PdgDeltaZero) { + continue; + } + foundMother = true; + break; + } + if (foundMother) { + break; + } + } + if (qaAnyCommonMother) { + histos.fill(HIST("EfficiencyQA/hTruthMatchCutFlow"), 5.f); // Common MC mother + } + if (foundMother) { + histos.fill(HIST("EfficiencyQA/hTruthMatchCutFlow"), 6.f); // Mother is Delta + } + if (!foundMother) { + continue; + } + const std::array, 2> momenta = { + std::array{t0.px(), t0.py(), t0.pz()}, + std::array{t1.px(), t1.py(), t1.pz()}}; + const float pairMass = RecoDecay::m(momenta, kProtonPionMasses); + const float pairPt = RecoDecay::pt(std::array{t0.px() + t1.px(), t0.py() + t1.py()}); + const float pairY = RecoDecay::y(std::array{t0.px() + t1.px(), t0.py() + t1.py(), t0.pz() + t1.pz()}, pairMass); + if (pairY < trackCuts.cfgMinY || pairY > trackCuts.cfgMaxY) { + continue; + } + fillDeltaHistogramMC(t0.sign(), t1.sign(), pairPt, pairMass, centrality, pairY); + } + } + } + PROCESS_SWITCH(DeltaAnalysis, processMC, "Process MC", false); + + void processMCGen(McCollisionsCent::iterator const& mcCollision, + aod::McParticles const& mcParticles, + soa::SmallGroups const& collisions) + { + histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 0.f); // All MC collisions + histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 0.f); // All generated events + + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + return; + } + histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 1.f); // |Vz| < cfgCutVertex + histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 1.f); // |Vz| accepted + + const bool truthInelGt0 = isTruthInelGt0(mcParticles); + if (evSel.cfgApplyMCTruthINELgt0 && !truthInelGt0) { + return; + } + histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 2.f); // Truth INEL>0 + histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 2.f); // Truth INEL>0 + histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 3.f); // Centrality (no cut currently applied) + histos.fill(HIST("CutFlow/MCGen/hEventCutFlow"), 4.f); // Final generated event + histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 3.f); // Final generated event + + bool hasAcceptedReco = false; + float genCentrality = mcCollision.centFT0M(); + + for (auto const& collision : collisions) { + if (passesEventSelection(collision)) { + hasAcceptedReco = true; + + if (!evSel.cfgUseMCTruthCentrality) { + genCentrality = getCentrality(collision); + } + + break; + } + } + + if (hasAcceptedReco) { + histos.fill(HIST("EfficiencyQA/hGeneratedEventCutFlow"), 4.f); // Associated reconstructed event accepted + } + + for (auto const& mcParticle : mcParticles) { + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 0.f); // bin0: all generated particles + + if (!mcParticle.producedByGenerator()) { + continue; + } + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 1.f); // bin1: producedByGenerator + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 2.f); // bin2: truth vertex-z (event-level, already enforced above) + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 3.f); // bin3: truth INEL>0 (event-level, already enforced above) + + const int pdg = mcParticle.pdgCode(); + if (std::abs(pdg) != delta_analysis::PdgDeltaPlusPlus && + std::abs(pdg) != delta_analysis::PdgDeltaZero) { + continue; + } + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 4.f); // bin4: PDG + + if (hasAcceptedReco) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 0.f); // Generated Delta (event accepted) + + const auto qaDaughtersGD = mcParticle.daughters_as(); + bool qaHasPrGD = false, qaHasPiGD = false; + for (const auto& d : qaDaughtersGD) { + if (std::abs(d.pdgCode()) == delta_analysis::PdgProton) { + qaHasPrGD = true; + } else if (std::abs(d.pdgCode()) == delta_analysis::PdgPion) { + qaHasPiGD = true; + } + } + if (qaHasPrGD && qaHasPiGD) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 1.f); // Delta -> p + pi + if (mcParticle.y() >= trackCuts.cfgMinY && mcParticle.y() <= trackCuts.cfgMaxY) { + histos.fill(HIST("EfficiencyQA/hDeltaRecoCutFlow"), 2.f); // Delta rapidity accepted + } + } + } + + if (mcParticle.y() < trackCuts.cfgMinY || mcParticle.y() > trackCuts.cfgMaxY) { + continue; + } + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 5.f); // bin5: rapidity + + const auto daughters = mcParticle.daughters_as(); + bool hasPr = false, hasPi = false; + float ptPr = -999.f, ptPi = -999.f; + for (const auto& d : daughters) { + if (std::abs(d.pdgCode()) == delta_analysis::PdgProton) { + hasPr = true; + ptPr = d.pt(); + } else if (std::abs(d.pdgCode()) == delta_analysis::PdgPion) { + hasPi = true; + ptPi = d.pt(); + } + } + if (!hasPr || !hasPi) { + continue; + } + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 6.f); // bin6: decay channel + histos.fill(HIST("MCGenQA/hGenDeltaCutFlow"), 7.f); // bin7: final generated histogram + + const float eMother = mcParticle.e(); + const float pxMother = mcParticle.px(); + const float pyMother = mcParticle.py(); + const float pzMother = mcParticle.pz(); + const float mass2 = eMother * eMother - pxMother * pxMother - pyMother * pyMother - pzMother * pzMother; + const float genMass = std::sqrt(std::max(0.f, mass2)); + const float genPt = mcParticle.pt(); + const float genY = mcParticle.y(); + + // GeneratedDelta_All + if (pdg == delta_analysis::PdgDeltaPlusPlus) { + histos.fill(HIST("MCGen/hDeltaPlusPlusInvMassGen"), genPt, genMass); + histos.fill(HIST("MCGen/hDeltaPlusPlusGenSparse"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("MCGenQA/hGenProtonDeltaPlusPlus"), ptPr); + histos.fill(HIST("MCGenQA/hGenPionDeltaPlusPlus"), ptPi); + } else if (pdg == -delta_analysis::PdgDeltaPlusPlus) { + histos.fill(HIST("MCGen/hAntiDeltaPlusPlusInvMassGen"), genPt, genMass); + histos.fill(HIST("MCGen/hAntiDeltaPlusPlusGenSparse"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("MCGenQA/hGenProtonAntiDeltaPlusPlus"), ptPr); + histos.fill(HIST("MCGenQA/hGenPionAntiDeltaPlusPlus"), ptPi); + } else if (pdg == delta_analysis::PdgDeltaZero) { + histos.fill(HIST("MCGen/hDeltaZeroInvMassGen"), genPt, genMass); + histos.fill(HIST("MCGen/hDeltaZeroGenSparse"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("MCGenQA/hGenProtonDeltaZero"), ptPr); + histos.fill(HIST("MCGenQA/hGenPionDeltaZero"), ptPi); + } else if (pdg == -delta_analysis::PdgDeltaZero) { + histos.fill(HIST("MCGen/hAntiDeltaZeroInvMassGen"), genPt, genMass); + histos.fill(HIST("MCGen/hAntiDeltaZeroGenSparse"), genMass, genPt, genCentrality, genY); + histos.fill(HIST("MCGenQA/hGenProtonAntiDeltaZero"), ptPr); + histos.fill(HIST("MCGenQA/hGenPionAntiDeltaZero"), ptPi); + } + + // GeneratedDelta_EventAccepted (A x eps denominator) + if (hasAcceptedReco) { + if (pdg == delta_analysis::PdgDeltaPlusPlus) { + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hDeltaPlusPlusInvMass"), genPt, genMass); + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hDeltaPlusPlusSparse"), genMass, genPt, genCentrality, genY); + } else if (pdg == -delta_analysis::PdgDeltaPlusPlus) { + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaPlusPlusInvMass"), genPt, genMass); + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaPlusPlusSparse"), genMass, genPt, genCentrality, genY); + } else if (pdg == delta_analysis::PdgDeltaZero) { + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hDeltaZeroInvMass"), genPt, genMass); + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hDeltaZeroSparse"), genMass, genPt, genCentrality, genY); + } else if (pdg == -delta_analysis::PdgDeltaZero) { + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaZeroInvMass"), genPt, genMass); + histos.fill(HIST("MCGen/GeneratedDelta_EventAccepted/hAntiDeltaZeroSparse"), genMass, genPt, genCentrality, genY); + } + } + } + } + PROCESS_SWITCH(DeltaAnalysis, processMCGen, "Process MC truth: generated-level Delta spectra and QA", false); + + void processEventFactor(EventCandidatesMC const& collisions, + McCollisionsCent const& mcCollisions, + aod::McParticles const& mcParticles) + { + // ── Loop A: reconstructed collisions ────────────────────────────────────────────────── + std::unordered_map nRecoCollisionsPerMc; + std::unordered_map acceptedMcCollisionRecoCent; + for (auto const& collision : collisions) { + if (collision.has_mcCollision()) { + ++nRecoCollisionsPerMc[collision.mcCollisionId()]; + } + if (!passesEventSelection(collision)) { + continue; + } + const float centrality = getCentrality(collision); + histos.fill(HIST("EventFactor/hRecoEvents"), centrality); + if (collision.has_mcCollision()) { + acceptedMcCollisionRecoCent.emplace(collision.mcCollisionId(), centrality); + } + } + + // ── Loop B: generated MC collisions ─────────────────────────────────────────────────── + for (auto const& mcCollision : mcCollisions) { + histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 0.f); // All generated MC collisions + + if (std::abs(mcCollision.posZ()) > evSel.cfgCutVertex) { + continue; + } + histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 1.f); // |Vz| < cfgCutVertex + + auto mcPartsThisColl = mcParticles.sliceBy(perMcCollisionDelta, mcCollision.globalIndex()); + if (evSel.cfgApplyMCTruthINELgt0 && !isTruthInelGt0(mcPartsThisColl)) { + continue; + } + histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 2.f); // Truth INEL>0 + + const float truthCentrality = mcCollision.centFT0M(); // MC-truth centrality proxy (see note above) + histos.fill(HIST("EventFactor/hEventsGenAll"), truthCentrality); + + const auto itCount = nRecoCollisionsPerMc.find(mcCollision.globalIndex()); + const int nRecoTotal = (itCount != nRecoCollisionsPerMc.end()) ? itCount->second : 0; + histos.fill(HIST("EventFactor/hNRecoCollisionsPerMcCollision"), nRecoTotal); + + if (nRecoTotal > 0) { + histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 3.f); // Has associated reconstructed collision + } + + const auto itCent = acceptedMcCollisionRecoCent.find(mcCollision.globalIndex()); + const bool hasAcceptedReco = (itCent != acceptedMcCollisionRecoCent.end()); + if (hasAcceptedReco) { + histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 4.f); // Associated reco collision passes event selection + histos.fill(HIST("CutFlow/EventFactor/hEventAcceptedCutFlow"), 5.f); // Final EventAccepted + histos.fill(HIST("EventFactor/hEventsGenAccepted"), truthCentrality); + histos.fill(HIST("EventFactor/hEventsGenAcceptedReco"), itCent->second); + } + } + } + PROCESS_SWITCH(DeltaAnalysis, processEventFactor, "Process Event Loss / Event Splitting factors", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGLF/Tasks/Resonances/deltaanalysis.cxx b/PWGLF/Tasks/Resonances/deltaanalysis.cxx deleted file mode 100644 index a121980f67e..00000000000 --- a/PWGLF/Tasks/Resonances/deltaanalysis.cxx +++ /dev/null @@ -1,613 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// Analysis task for delta analysis - -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -namespace -{ -constexpr float pionMass = o2::constants::physics::MassPionCharged; -constexpr float protonMass = o2::constants::physics::MassProton; -constexpr int deltaPlusPlusPDG = 2224; -constexpr int deltaZeroPDG = 2114; -constexpr int protonPDG = 2212; -constexpr int pionPDG = 211; -} // namespace - -struct deltaAnalysis { - - SliceCache cache; - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // events - Configurable cfgCutVertex{"cfgCutVertex", 10.0f, "Accepted z-vertex range"}; - // track - Configurable cfgCutPt{"cfgCutPt", 0.2, "Pt cut on daughter track"}; - Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut on daughter track"}; - Configurable cfgCutY{"cfgCutY", 0.5, "Y cut on reconstructed delta"}; - Configurable cfgCutDCAxy{"cfgCutDCAxy", 2.0f, "DCAxy range for tracks"}; - Configurable cfgCutDCAz{"cfgCutDCAz", 2.0f, "DCAz range for tracks"}; - Configurable nsigmaCutTPC{"nsigmacutTPC", 3.0, "Value of the TPC Nsigma cut"}; - Configurable nsigmaCutTOF{"nsigmaCutTOF", 3.0, "Value of the TOF Nsigma cut"}; - Configurable cfgNoMixedEvents{"cfgNoMixedEvents", 5, "Number of mixed events per event"}; - Configurable cfgCutPtProtonTPC{"cfgCutPtProtonTPC", 0.8, "Pt cut of proton in TPC"}; - Configurable cfgCutPtPionTPC{"cfgCutPtPionTPC", 0.8, "Pt cut of pion in TPC"}; - - // Histogram axes - AxisSpec nSigmaTPCaxis = {100, -5., 5., "n#sigma_{TPC}"}; - AxisSpec nSigmaTOFaxis = {100, -5., 5., "n#sigma_{TOF}"}; - ConfigurableAxis cfgPtAxis{"cfgPtAxis", {VARIABLE_WIDTH, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.8, 3.2, 3.6, 4.}, "#it{p}_{T} (GeV/#it{c})"}; - ConfigurableAxis cfgMassAxis{"cfgDeltaPlusPlusAxis", {75, 1.05, 1.8}, ""}; - - void init(o2::framework::InitContext&) - { - // Define axes - AxisSpec deltaPlusPlusAxis{cfgMassAxis, "m(p + #pi^{+}) (GeV/#it{c}^{2})"}; - AxisSpec antiDeltaPlusPlusAxis{cfgMassAxis, "m(#bar{p} + #pi^{-}) (GeV/#it{c}^{2})"}; - AxisSpec deltaZeroAxis{cfgMassAxis, "m(p + #pi^{-}) (GeV/#it{c}^{2})"}; - AxisSpec antiDeltaZeroAxis{cfgMassAxis, "m(#bar{p} + #pi^{+}) (GeV/#it{c}^{2})"}; - AxisSpec ptAxis{cfgPtAxis, "#it{p}_{T} (GeV/#it{c})"}; - - // Collision - histos.add("hCentrality", "Centrality distribution", kTH1F, {{2001, -0.5, 2000.5}}); - histos.add("hVtxZ", "Vertex distribution in Z;Z (cm)", kTH1F, {{400, -20.0, 20.0}}); - histos.add("hNcontributor", "Number of primary vertex contributors; n", kTH1F, {{2001, -0.5f, 2000.5f}}); - - // Single track - histos.add("hPiPlusDCAxy", "DCA_{xy} distribution for #pi^{+}; DCA_{xy} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPiPlusDCAz", "DCA_{z} distribution for #pi^{+}; DCA_{z} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPiPlusNsigmaTPCvsPt", "n#sigma_{TPC} distribution vs #it{p}_{T} for #pi^{+}", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPiPlusNsigmaTPCvsPt_TPC_only", "n#sigma_{TPC} distribution vs #it{p}_{T} for #pi^{+}", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPiPlusNsigmaTOFvsPt", "n#sigma_{TOF} distribution vs #it{p}_{T} for #pi^{+}", kTH2F, {ptAxis, nSigmaTOFaxis}); - - histos.add("hPiMinusDCAxy", "DCA_{xy} distribution for #pi^{-}; DCA_{xy} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPiMinusDCAz", "DCA_{z} distribution for #pi^{-}; DCA_{z} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPiMinusNsigmaTPCvsPt", "n#sigma_{TPC} distribution vs #it{p}_{T} for #pi^{-}", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPiMinusNsigmaTPCvsPt_TPC_only", "n#sigma_{TPC} distribution vs #it{p}_{T} for #pi^{-}", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPiMinusNsigmaTOFvsPt", "n#sigma_{TOF} distribution vs #it{p}_{T} for #pi^{-}", kTH2F, {ptAxis, nSigmaTOFaxis}); - - histos.add("hPrPlusDCAxy", "DCA_{xy} distribution for p; DCA_{xy} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPrPlusDCAz", "DCA_{z} distribution for p; DCA_{z} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPrPlusNsigmaTPCvsPt", "n#sigma_{TPC} distribution vs #it{p}_{T} for p", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPrPlusNsigmaTPCvsPt_TPC_only", "n#sigma_{TPC} distribution vs #it{p}_{T} for p", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPrPlusNsigmaTOFvsPt", "n#sigma_{TOF} distribution vs #it{p}_{T} for p", kTH2F, {ptAxis, nSigmaTOFaxis}); - - histos.add("hPrMinusDCAxy", "DCA_{xy} distribution for #bar{p}; DCA_{xy} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPrMinusDCAz", "DCA_{z} distribution for #bar{p}; DCA_{z} (cm)", kTH1F, {{200, -1.0f, 1.0f}}); - histos.add("hPrMinusNsigmaTPCvsPt", "n#sigma_{TPC} distribution vs #it{p}_{T} for #bar{p}", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPrMinusNsigmaTPCvsPt_TPC_only", "n#sigma_{TPC} distribution vs #it{p}_{T} for #bar{p}", kTH2F, {ptAxis, nSigmaTPCaxis}); - histos.add("hPrMinusNsigmaTOFvsPt", "n#sigma_{TOF} distribution vs #it{p}_{T} for #bar{p}", kTH2F, {ptAxis, nSigmaTOFaxis}); - - // Deltas - histos.add("hDeltaPlusPlusInvMass", "Invariant mass distribution for #Delta^{++}", kTH2F, {ptAxis, deltaPlusPlusAxis}); - histos.add("hAntiDeltaPlusPlusInvMass", "Invariant mass distribution for #bar{#Delta^{++}}", kTH2F, {ptAxis, antiDeltaPlusPlusAxis}); - - histos.add("hDeltaZeroInvMass", "Invariant mass distribution for #Delta^{0}", kTH2F, {ptAxis, deltaZeroAxis}); - histos.add("hAntiDeltaZeroInvMass", "Invariant mass distribution for #bar{#Delta^{0}}", kTH2F, {ptAxis, antiDeltaZeroAxis}); - - if (doprocessMixedEvent) { - // Deltas - Event mixing - histos.add("hDeltaPlusPlusInvMassEM", "Invariant mass distribution for #Delta^{++} - event mixing", kTH2F, {ptAxis, deltaPlusPlusAxis}); - histos.add("hAntiDeltaPlusPlusInvMassEM", "Invariant mass distribution for #bar{#Delta^{++}} - event mixing", kTH2F, {ptAxis, antiDeltaPlusPlusAxis}); - - histos.add("hDeltaZeroInvMassEM", "Invariant mass distribution for #Delta^{0} - event mixing", kTH2F, {ptAxis, deltaZeroAxis}); - histos.add("hAntiDeltaZeroInvMassEM", "Invariant mass distribution for #bar{#Delta^{0}} - event mixing", kTH2F, {ptAxis, antiDeltaZeroAxis}); - } - - if (doprocessMC) { - // generated quantities - histos.add("hDeltaPlusPlusInvMassGen", "Invariant mass distribution for #Delta^{++} - generated", kTH2F, {ptAxis, deltaPlusPlusAxis}); - histos.add("hAntiDeltaPlusPlusInvMassGen", "Invariant mass distribution for #bar{#Delta^{++}} - generated", kTH2F, {ptAxis, antiDeltaPlusPlusAxis}); - - histos.add("hDeltaZeroInvMassGen", "Invariant mass distribution for #Delta^{0} - generated", kTH2F, {ptAxis, deltaZeroAxis}); - histos.add("hAntiDeltaZeroInvMassGen", "Invariant mass distribution for #bar{#Delta^{0}} - generated", kTH2F, {ptAxis, antiDeltaZeroAxis}); - - histos.add("hRecProtonAntiDeltaPlusPlus", "Proton from #bar{#Delta^{++}}", kTH1F, {ptAxis}); - histos.add("hRecProtonDeltaPlusPlus", "Proton from #Delta^{++}", kTH1F, {ptAxis}); - histos.add("hRecProtonAntiDeltaZero", "Proton from #bar{#Delta^{0}}", kTH1F, {ptAxis}); - histos.add("hRecProtonDeltaZero", "Proton from #Delta^{0}", kTH1F, {ptAxis}); - histos.add("hRecPionAntiDeltaPlusPlus", "Pion from #bar{#Delta^{++}}", kTH1F, {ptAxis}); - histos.add("hRecPionDeltaPlusPlus", "Pion from #Delta^{++}", kTH1F, {ptAxis}); - histos.add("hRecPionAntiDeltaZero", "Pion from #bar{#Delta^{0}}", kTH1F, {ptAxis}); - histos.add("hRecPionDeltaZero", "Pion from #Delta^{0}", kTH1F, {ptAxis}); - - histos.add("hGenProtonAntiDeltaPlusPlus", "Proton from #bar{#Delta^{++}}", kTH1F, {ptAxis}); - histos.add("hGenProtonDeltaPlusPlus", "Proton from #Delta^{++}", kTH1F, {ptAxis}); - histos.add("hGenProtonAntiDeltaZero", "Proton from #bar{#Delta^{0}}", kTH1F, {ptAxis}); - histos.add("hGenProtonDeltaZero", "Proton from #Delta^{0}", kTH1F, {ptAxis}); - histos.add("hGenPionAntiDeltaPlusPlus", "Pion from #bar{#Delta^{++}}", kTH1F, {ptAxis}); - histos.add("hGenPionDeltaPlusPlus", "Pion from #Delta^{++}", kTH1F, {ptAxis}); - histos.add("hGenPionAntiDeltaZero", "Pion from #bar{#Delta^{0}}", kTH1F, {ptAxis}); - histos.add("hGenPionDeltaZero", "Pion from #Delta^{0}", kTH1F, {ptAxis}); - } - } - - template - bool selectionTrack(const T& track) - { - // if (!track.isGlobalTrack()) { - // return false; - // } - if (track.itsNCls() < 5) { - return false; - } - if (track.tpcNClsShared() > 0) { - return false; - } - if (track.tpcNClsFound() < 70) { - return false; - } - if (track.tpcNClsCrossedRows() < 70) { - return false; - } - if (track.tpcNClsCrossedRows() < 0.8 * track.tpcNClsFindable()) { - return false; - } - if (track.tpcChi2NCl() > 4.f) { - return false; - } - if (track.itsChi2NCl() > 36.f) { - return false; - } - return true; - } - - template - bool selectionPIDPion(const T& track) - { - if (track.hasTOF()) { - if (std::abs(track.tofNSigmaPi()) < nsigmaCutTOF && std::abs(track.tpcNSigmaPi()) < nsigmaCutTPC) { - if (track.sign() > 0) { - histos.fill(HIST("hPiPlusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hPiPlusNsigmaTOFvsPt"), track.pt(), track.tofNSigmaPi()); - histos.fill(HIST("hPiPlusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPiPlusDCAz"), track.dcaZ()); - } else { - histos.fill(HIST("hPiMinusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hPiMinusNsigmaTOFvsPt"), track.pt(), track.tofNSigmaPi()); - histos.fill(HIST("hPiMinusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPiMinusDCAz"), track.dcaZ()); - } - return true; - } - } else if (std::abs(track.tpcNSigmaPi()) < nsigmaCutTPC) { - if (track.sign() > 0) { - histos.fill(HIST("hPiPlusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hPiPlusNsigmaTPCvsPt_TPC_only"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hPiPlusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPiPlusDCAz"), track.dcaZ()); - } else { - histos.fill(HIST("hPiMinusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hPiMinusNsigmaTPCvsPt_TPC_only"), track.pt(), track.tpcNSigmaPi()); - histos.fill(HIST("hPiMinusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPiMinusDCAz"), track.dcaZ()); - } - if (track.pt() < cfgCutPtPionTPC) { - return true; - } - } - return false; - } - - template - bool selectionPIDProton(const T& track) - { - if (track.hasTOF()) { - if (std::abs(track.tofNSigmaPr()) < nsigmaCutTOF && std::abs(track.tpcNSigmaPr()) < nsigmaCutTPC) { - if (track.sign() > 0) { - histos.fill(HIST("hPrPlusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPr()); - histos.fill(HIST("hPrPlusNsigmaTOFvsPt"), track.pt(), track.tofNSigmaPr()); - histos.fill(HIST("hPrPlusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPrPlusDCAz"), track.dcaZ()); - } else { - histos.fill(HIST("hPrMinusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPr()); - histos.fill(HIST("hPrMinusNsigmaTOFvsPt"), track.pt(), track.tofNSigmaPr()); - histos.fill(HIST("hPrMinusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPrMinusDCAz"), track.dcaZ()); - } - return true; - } - } else if (std::abs(track.tpcNSigmaPr()) < nsigmaCutTPC) { - if (track.sign() > 0) { - histos.fill(HIST("hPrPlusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPr()); - histos.fill(HIST("hPrPlusNsigmaTPCvsPt_TPC_only"), track.pt(), track.tpcNSigmaPr()); - histos.fill(HIST("hPrPlusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPrPlusDCAz"), track.dcaZ()); - } else { - histos.fill(HIST("hPrMinusNsigmaTPCvsPt"), track.pt(), track.tpcNSigmaPr()); - histos.fill(HIST("hPrMinusNsigmaTPCvsPt_TPC_only"), track.pt(), track.tpcNSigmaPr()); - histos.fill(HIST("hPrMinusDCAxy"), track.dcaXY()); - histos.fill(HIST("hPrMinusDCAz"), track.dcaZ()); - } - if (track.pt() < cfgCutPtProtonTPC) { - return true; - } - } - return false; - } - - Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter acceptanceFilter = (nabs(aod::track::eta) < cfgCutEta && nabs(aod::track::pt) > cfgCutPt); - Filter DCAcutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); - - using EventCandidates = soa::Filtered>; - - using TrackCandidates = soa::Filtered>; - - using TrackCandidatesMC = soa::Filtered>; - - Preslice perCol = aod::track::collisionId; - Preslice perColMC = aod::track::collisionId; - - // Binning for event mixing - ConfigurableAxis cfgCentAxis{"cfgCentAxis", {VARIABLE_WIDTH, 0., 0.01, 0.1, 1.0, 5.0, 10., 15., 20., 30., 40., 50., 70., 100.0, 105.}, "Binning of the centrality axis"}; - ConfigurableAxis cfgVtxAxis{"cfgVtxAxis", {VARIABLE_WIDTH, -20, -15, -10, -7, -5, -3, -2, -1, 0, 1, 2, 3, 5, 7, 10, 15, 20}, "Mixing bins - z-vertex"}; - - using BinningType = ColumnBinningPolicy; - - BinningType binningOnPositions{{cfgVtxAxis}, true}; - SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, &cache}; - - void processSameEvent(EventCandidates const& collisions, TrackCandidates const& tracks, aod::BCs const&) - { - for (auto& collision : collisions) { - if (!collision.sel8()) { - continue; - } - histos.fill(HIST("hNcontributor"), collision.numContrib()); - histos.fill(HIST("hVtxZ"), collision.posZ()); - - const uint64_t collIdx = collision.globalIndex(); - auto perColTracks = tracks.sliceBy(perCol, collIdx); - perColTracks.bindExternalIndices(&tracks); - - for (auto& [t0, t1] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(perColTracks, perColTracks))) { - - if (t0.globalIndex() == t1.globalIndex()) { - continue; - } - if (!selectionTrack(t0) || !selectionTrack(t1)) { - continue; - } - if (selectionPIDProton(t0)) { - - TLorentzVector proton; - proton.SetPtEtaPhiM(t0.pt(), t0.eta(), t0.phi(), protonMass); - - if (selectionPIDPion(t1)) { - - TLorentzVector pion; - pion.SetPtEtaPhiM(t1.pt(), t1.eta(), t1.phi(), pionMass); - - TLorentzVector delta = proton + pion; - - if (abs(delta.Rapidity()) > cfgCutY) { - continue; - } - - if (t0.sign() < 0) { - if (t1.sign() < 0) { - histos.fill(HIST("hAntiDeltaPlusPlusInvMass"), delta.Pt(), delta.M()); - } else { - histos.fill(HIST("hAntiDeltaZeroInvMass"), delta.Pt(), delta.M()); - } - } else { - if (t1.sign() < 0) { - histos.fill(HIST("hDeltaZeroInvMass"), delta.Pt(), delta.M()); - } else { - histos.fill(HIST("hDeltaPlusPlusInvMass"), delta.Pt(), delta.M()); - } - } - } - } - } - } - } - PROCESS_SWITCH(deltaAnalysis, processSameEvent, "Process same event", false); - - void processMixedEvent(EventCandidates const& /*collisions*/, TrackCandidates const& /*tracks*/) - { - for (auto& [c1, tracks1, c2, tracks2] : pair) { - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; - } - - for (auto& [t0, t1] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - - if (!selectionTrack(t0) || !selectionTrack(t1)) { - continue; - } - - if (selectionPIDProton(t0)) { - - TLorentzVector proton; - proton.SetPtEtaPhiM(t0.pt(), t0.eta(), t0.phi(), protonMass); - - if (selectionPIDPion(t1)) { - - TLorentzVector pion; - pion.SetPtEtaPhiM(t1.pt(), t1.eta(), t1.phi(), pionMass); - - TLorentzVector delta = proton + pion; - - if (abs(delta.Rapidity()) > cfgCutY) { - continue; - } - - if (t0.sign() < 0) { - if (t1.sign() < 0) { - histos.fill(HIST("hAntiDeltaPlusPlusInvMassEM"), delta.Pt(), delta.M()); - } else { - histos.fill(HIST("hAntiDeltaZeroInvMassEM"), delta.Pt(), delta.M()); - } - } else { - if (t1.sign() < 0) { - histos.fill(HIST("hDeltaZeroInvMassEM"), delta.Pt(), delta.M()); - } else { - histos.fill(HIST("hDeltaPlusPlusInvMassEM"), delta.Pt(), delta.M()); - } - } - } - } - } - } - } - PROCESS_SWITCH(deltaAnalysis, processMixedEvent, "Process Mixed event", false); - - void processMC(soa::Join const& collisions, aod::BCs const&, TrackCandidatesMC const& tracks, aod::McParticles const& mcParticles) - { - - for (auto& collision : collisions) { - - if (!collision.sel8() || std::abs(collision.posZ()) > cfgCutVertex) { - continue; - } - - histos.fill(HIST("hCentrality"), 1); - histos.fill(HIST("hNcontributor"), collision.numContrib()); - histos.fill(HIST("hVtxZ"), collision.posZ()); - - const uint64_t collIdx = collision.globalIndex(); - auto perColTracks = tracks.sliceBy(perColMC, collIdx); - perColTracks.bindExternalIndices(&tracks); - - for (auto& t0 : perColTracks) { - - if (!selectionTrack(t0)) { - continue; - } - if (!t0.has_mcParticle()) { - continue; - } - - const auto mcTrack = t0.mcParticle(); - - if (std::abs(mcTrack.pdgCode()) == protonPDG) { - if (selectionPIDProton(t0)) { - for (auto& motherTrackProton : mcTrack.mothers_as()) { - if (std::abs(motherTrackProton.pdgCode()) == deltaPlusPlusPDG) { - if (t0.sign() < 0) { - histos.fill(HIST("hRecProtonAntiDeltaPlusPlus"), t0.pt()); - } else { - histos.fill(HIST("hRecProtonDeltaPlusPlus"), t0.pt()); - } - } else if (std::abs(motherTrackProton.pdgCode()) == deltaZeroPDG) { - if (t0.sign() < 0) { - histos.fill(HIST("hRecProtonAntiDeltaZero"), t0.pt()); - } else { - histos.fill(HIST("hRecProtonDeltaZero"), t0.pt()); - } - } - } - } - } else if (std::abs(mcTrack.pdgCode()) == pionPDG) { - if (selectionPIDPion(t0)) { - for (auto& motherTrackPion : mcTrack.mothers_as()) { - if (std::abs(motherTrackPion.pdgCode()) == deltaPlusPlusPDG) { - if (t0.sign() < 0) { - histos.fill(HIST("hRecPionAntiDeltaPlusPlus"), t0.pt()); - } else { - histos.fill(HIST("hRecPionDeltaPlusPlus"), t0.pt()); - } - } else if (std::abs(motherTrackPion.pdgCode()) == deltaZeroPDG) { - if (t0.sign() < 0) { - histos.fill(HIST("hRecPionDeltaZero"), t0.pt()); - } else { - histos.fill(HIST("hRecPionAntiDeltaZero"), t0.pt()); - } - } - } - } - } - } - - for (auto& [t0, t1] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(perColTracks, perColTracks))) { - - if (t0.globalIndex() == t1.globalIndex()) { - continue; - } - if (!selectionTrack(t0) || !selectionTrack(t1)) { - continue; - } - if (selectionPIDProton(t0)) { - - if (!t0.has_mcParticle()) { - continue; - } - TLorentzVector proton; - proton.SetPtEtaPhiM(t0.pt(), t0.eta(), t0.phi(), protonMass); - - if (selectionPIDPion(t1)) { - - if (!t1.has_mcParticle()) { - continue; - } - TLorentzVector pion; - pion.SetPtEtaPhiM(t1.pt(), t1.eta(), t1.phi(), pionMass); - - // select real protons and pion - const auto mcTrackProton = t0.mcParticle(); - const auto mcTrackPion = t1.mcParticle(); - - if (std::abs(mcTrackProton.pdgCode()) != protonPDG) { - continue; - } - - if (std::abs(mcTrackPion.pdgCode()) != pionPDG) { - continue; - } - - bool found_mother = false; - - for (auto& motherTrackProton : mcTrackProton.mothers_as()) { - for (auto& motherTrackPion : mcTrackPion.mothers_as()) { - if (motherTrackProton != motherTrackPion) { - continue; - } - if (std::abs(motherTrackProton.pdgCode()) != deltaPlusPlusPDG && std::abs(motherTrackProton.pdgCode()) != deltaZeroPDG) { - continue; - } - found_mother = true; - break; - } - if (found_mother) { - break; - } - } - - if (!found_mother) { - continue; - } - - TLorentzVector delta = proton + pion; - - if (abs(delta.Rapidity()) > cfgCutY) { - continue; - } - - if (t0.sign() < 0) { - if (t1.sign() < 0) { - histos.fill(HIST("hAntiDeltaPlusPlusInvMass"), delta.Pt(), delta.M()); - } else { - histos.fill(HIST("hAntiDeltaZeroInvMass"), delta.Pt(), delta.M()); - } - } else { - if (t1.sign() < 0) { - histos.fill(HIST("hDeltaZeroInvMass"), delta.Pt(), delta.M()); - } else { - histos.fill(HIST("hDeltaPlusPlusInvMass"), delta.Pt(), delta.M()); - } - } - } - } - } - } - - for (auto& mcParticle : mcParticles) { - - int pdg = mcParticle.pdgCode(); - if (std::abs(pdg) != deltaPlusPlusPDG && std::abs(pdg) != deltaZeroPDG) { - continue; - } - - if (std::abs(mcParticle.y()) > cfgCutY) { - continue; - } - - // if (!mcParticle.isPhysicalPrimary()) { - // continue; - // } - - auto daughters = mcParticle.daughters_as(); - bool daughtPr = false; - bool daughtPi = false; - float ptPr = -999.; - float ptPi = -999.; - double eDelta = 0.; - for (auto daught : daughters) { - if (std::abs(daught.pdgCode()) == protonPDG) { - daughtPr = true; - ptPr = daught.pt(); - eDelta += daught.e(); - } else if (std::abs(daught.pdgCode()) == pionPDG) { - daughtPi = true; - ptPi = daught.pt(); - eDelta += daught.e(); - } - } - - if (!daughtPr || !daughtPi) { - continue; - } - - float gen_mass = TMath::Sqrt(eDelta * eDelta - mcParticle.p() * mcParticle.p()); - - if (pdg > 0) { - if (std::abs(pdg) == deltaPlusPlusPDG) { - histos.fill(HIST("hDeltaPlusPlusInvMassGen"), mcParticle.pt(), gen_mass); - histos.fill(HIST("hGenProtonDeltaPlusPlus"), ptPr); - histos.fill(HIST("hGenPionDeltaPlusPlus"), ptPi); - } else if (std::abs(pdg) == deltaZeroPDG) { - histos.fill(HIST("hDeltaZeroInvMassGen"), mcParticle.pt(), gen_mass); - histos.fill(HIST("hGenProtonDeltaZero"), ptPr); - histos.fill(HIST("hGenPionDeltaZero"), ptPi); - } - } else { - if (std::abs(pdg) == deltaPlusPlusPDG) { - histos.fill(HIST("hAntiDeltaPlusPlusInvMassGen"), mcParticle.pt(), gen_mass); - histos.fill(HIST("hGenProtonAntiDeltaPlusPlus"), ptPr); - histos.fill(HIST("hGenPionAntiDeltaPlusPlus"), ptPi); - } else if (std::abs(pdg) == deltaZeroPDG) { - histos.fill(HIST("hAntiDeltaZeroInvMassGen"), mcParticle.pt(), gen_mass); - histos.fill(HIST("hGenProtonAntiDeltaZero"), ptPr); - histos.fill(HIST("hGenPionAntiDeltaZero"), ptPi); - } - } - } - } - PROCESS_SWITCH(deltaAnalysis, processMC, "Process MC", true); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"deltaAnalysis"})}; -} diff --git a/PWGLF/Tasks/Resonances/doubleResonanceScan.cxx b/PWGLF/Tasks/Resonances/doubleResonanceScan.cxx index bc81e47c208..44e196fb8e3 100644 --- a/PWGLF/Tasks/Resonances/doubleResonanceScan.cxx +++ b/PWGLF/Tasks/Resonances/doubleResonanceScan.cxx @@ -213,7 +213,7 @@ struct DoubleResonanceScan { } template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { if constexpr (!IsResoMicrotrack) { if (std::abs(track.pt()) < cMinPtcut) diff --git a/PWGLF/Tasks/Resonances/doublephimeson.cxx b/PWGLF/Tasks/Resonances/doublephimeson.cxx index bab19207680..585d6a92173 100644 --- a/PWGLF/Tasks/Resonances/doublephimeson.cxx +++ b/PWGLF/Tasks/Resonances/doublephimeson.cxx @@ -15,6 +15,7 @@ #include "PWGLF/DataModel/ReducedDoublePhiTables.h" +#include #include #include #include @@ -60,15 +61,141 @@ struct doublephimeson { Configurable maxPhiPt{"maxPhiPt", 100, "Maximum phi Pt"}; Configurable minPhiMass2{"minPhiMass2", 1.01, "Minimum phi mass2"}; Configurable maxPhiMass2{"maxPhiMass2", 1.03, "Maximum phi mass2"}; + Configurable minExoticPt{"minExoticPt", 6.0, "Minimum Exotic Pt"}; Configurable minExoticMass{"minExoticMass", 2.0, "Minimum Exotic mass"}; Configurable maxExoticMass{"maxExoticMass", 3.6, "Maximum Exotic mass"}; - Configurable additionalEvsel{"additionalEvsel", false, "Additional event selection"}; + Configurable additionalEvsel{"additionalEvsel", true, "Additional event selection"}; Configurable isDeep{"isDeep", true, "Store deep angle"}; Configurable cutMinNsigmaTPC{"cutMinNsigmaTPC", -2.5, "nsigma cut TPC"}; - Configurable cutNsigmaTPC{"cutNsigmaTPC", 3.0, "nsigma cut TPC"}; - Configurable cutNsigmaTOF{"cutNsigmaTOF", 3.0, "nsigma cut TOF"}; + Configurable cutNsigmaTPC{"cutNsigmaTPC", 2.5, "nsigma cut TPC"}; + Configurable cutNsigmaTOF{"cutNsigmaTOF", 2.5, "nsigma cut TOF"}; Configurable momTOFCut{"momTOFCut", 1.8, "minimum pT cut for madnatory TOF"}; Configurable maxKaonPt{"maxKaonPt", 100.0, "maximum kaon pt cut"}; + Configurable cfgCrossPhiLow{"cfgCrossPhiLow", 1.01, "Lower edge of phi mass window for cross-pairing (ghost) veto"}; + Configurable cfgCrossPhiHigh{"cfgCrossPhiHigh", 1.03, "Upper edge of phi mass window for cross-pairing (ghost) veto"}; + Configurable useParametrized{"useParametrized", false, "Use pT dependent mass peak and width"}; + Configurable useCrossPairRejection{"useCrossPairRejection", true, "Use cross pair phi signal compatibilaty"}; + Configurable cfgFillDataDrivenPhiResolution{ + "cfgFillDataDrivenPhiResolution", true, + "Fill the single-phi daughter-kinematics sparse used for data-driven X resolution and inter-dataset momentum-scale calibration"}; + + Configurable cfgFillSelectedXResolutionSparse{ + "cfgFillSelectedXResolutionSparse", true, + "Fill compact selected-X candidate sparse for data-driven X mass-resolution study"}; + Configurable cfgSelectedXResolutionMassLow{ + "cfgSelectedXResolutionMassLow", 2.63f, + "Lower M(phi-phi) edge for selected-X resolution sparse (GeV/c^2)"}; + Configurable cfgSelectedXResolutionMassHigh{ + "cfgSelectedXResolutionMassHigh", 2.75f, + "Upper M(phi-phi) edge for selected-X resolution sparse (GeV/c^2)"}; + Configurable cfgSelectedXResolutionPtMin{ + "cfgSelectedXResolutionPtMin", 9.0f, + "Minimum pT(phi-phi) for selected-X resolution sparse (GeV/c)"}; + + // Optional 2025 -> 2026 kaon momentum-scale correction used only in + // processopti5. The selected calibration model is a sigmoid plus a quadratic + // tail: + // + // epsilon_corr(%) = p0 + p1/[1 + exp(-(pT_phi-p2)/p3)] + // + p4*pT_phi + p5*pT_phi^2, + // + // with pT_phi, p2 and p3 in GeV/c. Both kaons belonging to a phi candidate + // receive the same scale factor: + // p_corr = (1 + epsilon_corr/100) * p_measured. + // The numerical parameters should be supplied from the JSON file generated + // by CalibratePhiMomentumScaleVsPt.C after the final calibration fit. + Configurable cfgApplyKaonMomentumCorrection{ + "cfgApplyKaonMomentumCorrection", false, + "Apply the pT-dependent kaon momentum-scale correction in processopti5"}; + Configurable cfgMomCorrP0Percent{ + "cfgMomCorrP0Percent", 0.749950, + "p0 of epsilon_corr(pT_phi) in percent"}; + Configurable cfgMomCorrP1Percent{ + "cfgMomCorrP1Percent", 0.421824, + "p1 sigmoid-step amplitude of epsilon_corr in percent"}; + Configurable cfgMomCorrP2GeV{ + "cfgMomCorrP2GeV", 1.614360, + "p2 sigmoid turn-on position in GeV/c"}; + Configurable cfgMomCorrP3GeV{ + "cfgMomCorrP3GeV", 0.290147, + "p3 sigmoid width in GeV/c; must be positive"}; + Configurable cfgMomCorrP4PercentPerGeV{ + "cfgMomCorrP4PercentPerGeV", 0.0279158, + "p4 linear-tail slope in percent per GeV/c"}; + Configurable cfgMomCorrP5PercentPerGeV2{ + "cfgMomCorrP5PercentPerGeV2", 0.0, + "p5 quadratic-tail coefficient in percent per (GeV/c)^2"}; + Configurable cfgMomCorrPtMin{ + "cfgMomCorrPtMin", 0.8, + "Minimum uncorrected phi pT for applying the momentum correction (GeV/c)"}; + Configurable cfgMomCorrPtMax{ + "cfgMomCorrPtMax", 20.0, + "Maximum uncorrected phi pT for applying the momentum correction (GeV/c)"}; + // ------------------------------------------------------------ + // pT-dependent phi mass peak and width from single-phi BW fits + // + // DeltaM = sqrt( ((m1 - mean(pt1))/width(pt1))^2 + // + ((m2 - mean(pt2))/width(pt2))^2 ) + // + // Units: + // pT : GeV/c + // mean : GeV/c^2 + // width : GeV/c^2 + // + // 35 pT-bin edges -> 34 mean/width values. + // ------------------------------------------------------------ + + Configurable> cfgPhiPtBins{ + "cfgPhiPtBins", + std::vector{ + 0.4, 0.5, 0.6, 0.8, 0.9, + 1.0, 1.1, 1.2, 1.4, 1.6, + 1.8, 2.0, 2.2, 2.4, 2.6, + 2.8, 3.0, 3.5, 4.0, 4.5, + 5.0, 6.0, 7.0, 8.0, 9.0, + 10.0, 12.0, 14.0, 16.0, 18.0, + 20.0, 25.0, 30.0, 40.0, 100.0}, + "pT bin edges for phi mass peak and width calibration"}; + + Configurable> cfgPhiMeanVsPt{ + "cfgPhiMeanVsPt", + std::vector{ + 1.01779, 1.01826, 1.01897, 1.01924, 1.01934, + 1.01938, 1.01942, 1.01945, 1.01946, 1.01947, + 1.01953, 1.01960, 1.01965, 1.01967, 1.01971, + 1.01972, 1.01974, 1.01977, 1.01980, 1.01983, + 1.01987, 1.01988, 1.01989, 1.01990, 1.01992, + 1.01992, 1.01990, 1.01990, 1.01990, 1.01990, + 1.01990, 1.01990, 1.01990, 1.01990}, + "phi mass peak vs pT from single-phi Breit-Wigner fit"}; + + Configurable> cfgPhiSigmaVsPt{ + "cfgPhiSigmaVsPt", + std::vector{ + 0.00507421, 0.00554344, 0.00554447, 0.00562718, 0.00548766, + 0.00541166, 0.00539718, 0.00539168, 0.00532416, 0.00534485, + 0.00547586, 0.00567904, 0.00579755, 0.00583511, 0.00587245, + 0.00600147, 0.00600020, 0.00610160, 0.00628558, 0.00646913, + 0.00678283, 0.00720555, 0.00753636, 0.00784207, 0.00814550, + 0.00854121, 0.00890474, 0.00981538, 0.01011060, 0.01077820, + 0.01112900, 0.01203120, 0.01372720, 0.02443550}, + "phi Breit-Wigner width vs pT from single-phi fit"}; + + Configurable cfgDefaultPhiMean{ + "cfgDefaultPhiMean", + 1.019461f, + "default phi mass peak if pT is outside calibration range"}; + + Configurable cfgDefaultPhiSigma{ + "cfgDefaultPhiSigma", + 0.0055f, + "default phi width if pT is outside calibration range"}; + + Configurable cfgMinAllowedPhiSigma{ + "cfgMinAllowedPhiSigma", + 1.0e-6f, + "protection against zero or negative phi width"}; + // Event Mixing Configurable nEvtMixing{"nEvtMixing", 1, "Number of events to mix"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {10, -10, 10}, "Mixing bins - z-vertex"}; @@ -76,21 +203,81 @@ struct doublephimeson { // THnsparse bining ConfigurableAxis configThnAxisPtCorr{"configThnAxisPtCorr", {1000, 0.0, 100}, "#it{M} (GeV/#it{c}^{2})"}; - ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {1500, 2.0, 3.5}, "#it{M} (GeV/#it{c}^{2})"}; + ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {400, 2.5, 2.9}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisInvMassPhi{"configThnAxisInvMassPhi", {20, 1.01, 1.03}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisInvMassDeltaPhi{"configThnAxisInvMassDeltaPhi", {80, 0.0, 0.08}, "#it{M} (GeV/#it{c}^{2})"}; + ConfigurableAxis configThnAxisInvMassDeltaPhiSigma{"configThnAxisInvMassDeltaPhiSigma", {100, 0.0, 10}, "#it{M} (GeV/#it{c}^{2}) sigma"}; ConfigurableAxis configThnAxisDaugherPt{"configThnAxisDaugherPt", {25, 0.0, 50.}, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis configThnAxisPt{"configThnAxisPt", {40, 0.0, 20.}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis configThnAxisDaughterPt{"configThnAxisDaughterPt", {100, 0.0, 100.}, "daughter #it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis configThnAxisKstar{"configThnAxisKstar", {200, 0.0, 2.0}, "#it{k}^{*} (GeV/#it{c})"}; - ConfigurableAxis configThnAxisDeltaR{"configThnAxisDeltaR", {200, 0.0, 2.0}, "#it{k}^{*} (GeV/#it{c})"}; - ConfigurableAxis configThnAxisCosTheta{"configThnAxisCosTheta", {160, 0.0, 3.2}, "cos #theta{*}"}; - ConfigurableAxis configThnAxisNumPhi{"configThnAxisNumPhi", {101, -0.5, 100.5}, "cos #theta{*}"}; + ConfigurableAxis configThnAxisDeltaR{"configThnAxisDeltaR", {VARIABLE_WIDTH, 0.0, 0.0001, 0.0003, 0.0005, 0.0007, 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.01, 0.02, 0.03, 0.04, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 2.0, 10.0}, "#it{k}^{*} (GeV/#it{c})"}; + ConfigurableAxis configThnAxisCosTheta{"configThnAxisCosTheta", {10, 0.0, 1.0}, "cos #theta{*}"}; + ConfigurableAxis configThnAxisRapidity{"configThnAxisRapidity", {10, 0.0, 1.0}, "Rapidity"}; + ConfigurableAxis configThnAxisNumPhi{"configThnAxisNumPhi", {6, -0.5, 5.5}, "Number of Kaon daughter have TOF hit"}; ConfigurableAxis configThnAxisDeltaPt{"configThnAxisDeltaPt", {100, 0.0, 1.0}, "delta pt"}; Configurable maxDeltaMPhi{"maxDeltaMPhi", 0.01f, "Delta-m cut on the two phi masses: sqrt((m1-mPDG)^2 + (m2-mPDG)^2) < maxDeltaMPhi (GeV/c^2)"}; // --- NEW: steerable axes from JSON --- ConfigurableAxis configThnAxisDeltaRPhi{"configThnAxisDeltaRPhi", {120, 0.0, 6.0}, "ΔR(φ,φ)"}; ConfigurableAxis configThnAxisZ{"configThnAxisZ", {100, 0.0, 1.0}, "z"}; - ConfigurableAxis configThnAxisA{"configThnAxisA", {100, 0.0, 1.0}, "A"}; + ConfigurableAxis configThnAxisA{"configThnAxisA", {10, 0.0, 1.0}, "A"}; + ConfigurableAxis configThnAxisPhiPtVertex{"configThnAxisPhiPtVertex", {100, 0.0, 100.0}, "phi pT (GeV/c)"}; + ConfigurableAxis configThnAxisDecayLength{"configThnAxisDecayLength", {200, 0.0, 1.0}, "3D decay length (cm)"}; + ConfigurableAxis configThnAxisFitChi2Ndf{"configThnAxisFitChi2Ndf", {200, 0.0, 100.0}, "four-kaon fit chi2/NDF"}; + ConfigurableAxis configThnAxisRmsDcaSig{"configThnAxisRmsDcaSig", {300, 0.0, 15.0}, "RMS DCA significance"}; + + // Data-driven mass-resolution inputs. + // + // 1) PhiMassResolutionDataDriven: + // inclusive single-phi calibration, filled after daughter/PID selections + // but before the hard phi-mass window. + // + // 2) SelectedXResolutionDataDriven: + // compact candidate-level sparse filled only for the selected X window. + // It keeps the measured joint pT configuration of the two phis and all + // four kaons, so no pT(X) reweighting or regenerated daughter-pT topology + // is needed later. + ConfigurableAxis cfgDDPhiMassAxis{ + "cfgDDPhiMassAxis", + {100, 0.995, 1.045}, + "m(KK) for data-driven resolution (GeV/c^2)"}; + // The pT edges are deliberately fine around 1--3 GeV/c, where the momentum + // response changes fastest, and coarser at high pT. eta and DeltaPhi are + // retained only as validation axes; the primary extraction integrates them. + ConfigurableAxis cfgDDCalibKaonPtAxis{ + "cfgDDCalibKaonPtAxis", + {VARIABLE_WIDTH, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.1, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.5, 3.0, + 3.5, 4.0, 5.0, 6.0, 8.0, 10.0, 15.0, 20.0, 30.0, 50.0}, + "kaon pT in single-phi calibration (GeV/c)"}; + ConfigurableAxis cfgDDCalibKaonEtaAxis{ + "cfgDDCalibKaonEtaAxis", + {10, -1.0, 1.0}, + "kaon eta in single-phi calibration"}; + ConfigurableAxis cfgDDCalibDeltaPhiAxis{ + "cfgDDCalibDeltaPhiAxis", + {36, -3.141592653589793, 3.141592653589793}, + "Delta phi(K+,K-) in single-phi calibration"}; + ConfigurableAxis cfgDDPhiPtAxis{ + "cfgDDPhiPtAxis", + {VARIABLE_WIDTH, 0.0, 0.4, 0.5, 0.6, 0.8, 0.9, 1.0, 1.1, 1.2, + 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, + 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, + 12.0, 14.0, 16.0, 18.0, 20.0, 25.0, 30.0, 40.0, 50.0, 100.0}, + "phi pT for data-driven resolution (GeV/c)"}; + + // Compact pT(X) axis. The sparse itself is only filled for pT(X)>9 GeV/c, + // so there is no need to allocate fine bins below the analysis threshold. + ConfigurableAxis cfgDDSelectedXPtAxis{ + "cfgDDSelectedXPtAxis", + {VARIABLE_WIDTH, 9.0, 10.0, 12.0, 14.0, 16.0, 18.0, 20.0, + 25.0, 30.0, 40.0, 50.0, 70.0, 100.0}, + "pT(phi-phi) for selected-X data-driven resolution (GeV/c)"}; + + // The selected-X candidate sparse is intentionally restricted at filling + // time to the narrow M(phi-phi) window and pT(X) threshold. M(phi-phi) is + // therefore NOT stored as an axis, keeping the 9D sparse compact. + // Initialize the ananlysis task void init(o2::framework::InitContext&) { @@ -101,37 +288,125 @@ struct doublephimeson { histos.add("hnsigmaTPCKaonPlus", "hnsigmaTPCKaonPlus", kTH2F, {{1000, -3.0, 3.0f}, {100, 0.0f, 10.0f}}); histos.add("hnsigmaTPCKaonMinus", "hnsigmaTPCKaonMinus", kTH2F, {{1000, -3.0, 3.0f}, {100, 0.0f, 10.0f}}); histos.add("hnsigmaTPCTOFKaon", "hnsigmaTPCTOFKaon", kTH3F, {{500, -3.0, 3.0f}, {500, -3.0, 3.0f}, {100, 0.0f, 10.0f}}); - histos.add("hPhiMass", "hPhiMass", kTH3F, {{40, 1.0, 1.04f}, {40, 1.0, 1.04f}, {100, 0.0f, 10.0f}}); + histos.add("hPhiMassVsPt", "hPhiMassVsPt", kTH2F, {{40, 1.0, 1.04f}, {1000, 0.0f, 100.0f}}); + histos.add("hPhiMass", "hPhiMass", kTH3F, {{40, 1.0, 1.04f}, {40, 1.0, 1.04f}, {250, 0.0f, 100.0f}}); + histos.add("hPhiMassNormalized", "hPhiMassNormalized", kTH3F, {{100, -10.0, 10.0f}, {100, -10.0, 10.0f}, {250, 0.0f, 100.0f}}); histos.add("hPhiMass2", "hPhiMass2", kTH2F, {{40, 1.0, 1.04f}, {40, 1.0f, 1.04f}}); histos.add("hkPlusDeltaetaDeltaPhi", "hkPlusDeltaetaDeltaPhi", kTH2F, {{400, -2.0, 2.0}, {640, -2.0 * TMath::Pi(), 2.0 * TMath::Pi()}}); histos.add("hkMinusDeltaetaDeltaPhi", "hkMinusDeltaetaDeltaPhi", kTH2F, {{400, -2.0, 2.0}, {640, -2.0 * TMath::Pi(), 2.0 * TMath::Pi()}}); histos.add("hDeltaRkaonplus", "hDeltaRkaonplus", kTH1F, {{800, 0.0, 8.0}}); histos.add("hDeltaRkaonminus", "hDeltaRkaonminus", kTH1F, {{800, 0.0, 8.0}}); histos.add("hPtCorrelation", "hPtCorrelation", kTH2F, {{400, 0.0, 40.0}, {5000, 0.0, 100.0}}); + histos.add("hMassCent", "hMassCent", kTH3F, {{40, 1.0, 1.04f}, {40, 1.0, 1.04f}, {100, 0.0, 100.0}}); const AxisSpec thnAxisdeltapt{configThnAxisDeltaPt, "Delta pt"}; + const AxisSpec thnAxisdaughterpt{configThnAxisDaughterPt, "Daughter pt"}; const AxisSpec thnAxisInvMass{configThnAxisInvMass, "#it{M} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisPt{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec thnAxisInvMassPhi{configThnAxisInvMassPhi, "#it{M} (GeV/#it{c}^{2})"}; const AxisSpec thnAxisInvMassDeltaPhi{configThnAxisInvMassDeltaPhi, "#it{M} (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisInvMassDeltaPhiSigma{configThnAxisInvMassDeltaPhiSigma, "#it{M} (GeV/#it{c}^{2}) sigma"}; const AxisSpec thnAxisDeltaR{configThnAxisDeltaR, "#Delta R)"}; const AxisSpec thnAxisCosTheta{configThnAxisCosTheta, "cos #theta"}; + const AxisSpec thnAxisRapidity{configThnAxisRapidity, "Rapidity"}; const AxisSpec thnAxisNumPhi{configThnAxisNumPhi, "Number of phi meson"}; const AxisSpec thnAxisPtCorr{configThnAxisPtCorr, "Pt Corr var"}; const AxisSpec thnAxisDeltaRPhi{configThnAxisDeltaRPhi, "#Delta R(#phi,#phi)"}; const AxisSpec thnAxisZ{configThnAxisZ, "z = p_{T1}/(p_{T1}+p_{T2})"}; const AxisSpec thnAxisA{configThnAxisA, "A = |p_{T1}-p_{T2}|/(p_{T1}+p_{T2})"}; + AxisSpec axisDoublePhiPID{50, 0.0, 5.0, "max daughter n_{#sigma}^{comb}"}; + const AxisSpec thnAxisDecayLength{configThnAxisDecayLength, "#it{L}_{3D} (cm)"}; + const AxisSpec thnAxisFitChi2Ndf{configThnAxisFitChi2Ndf, "#chi^{2}/NDF"}; + const AxisSpec thnAxisRmsDcaSig{configThnAxisRmsDcaSig, "RMS DCA significance"}; + + const AxisSpec ddPhiMassAxis{cfgDDPhiMassAxis, "m_{K^{+}K^{-}} (GeV/c^{2})"}; + const AxisSpec ddCalibKaonPtAxis{cfgDDCalibKaonPtAxis, "p_{T,K} (GeV/c)"}; + const AxisSpec ddCalibKaonEtaAxis{cfgDDCalibKaonEtaAxis, "#eta_{K}"}; + const AxisSpec ddCalibDeltaPhiAxis{cfgDDCalibDeltaPhiAxis, "#Delta#varphi_{K^{+}K^{-}}"}; + const AxisSpec ddPhiPtAxis{cfgDDPhiPtAxis, "p_{T,#phi} (GeV/c)"}; + + const AxisSpec ddSelectedXPtAxis{cfgDDSelectedXPtAxis, "p_{T,X} (GeV/c)"}; + histos.add("SEMassUnlike", "SEMassUnlike", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisDeltaR, thnAxisPt, thnAxisDeltaR, thnAxisInvMassDeltaPhi, thnAxisPtCorr}); // histos.add("SEMassLike", "SEMassLike", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisPt, thnAxisDeltaR, thnAxisInvMassPhi, thnAxisInvMassPhi, thnAxisNumPhi}); histos.add("MEMassUnlike", "MEMassUnlike", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisDeltaR, thnAxisPt, thnAxisDeltaR, thnAxisInvMassDeltaPhi, thnAxisPtCorr}); // --- NEW THnSparse storing (ΔRphi, z, A) WITHOUT applying cuts --- histos.add("SEMassUnlike_DeltaRZA", "SEMassUnlike_DeltaRZA", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisPt, thnAxisDeltaRPhi, thnAxisZ, thnAxisA, thnAxisInvMassDeltaPhi}); histos.add("MEMassUnlike_DeltaRZA", "MEMassUnlike_DeltaRZA", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisPt, thnAxisDeltaRPhi, thnAxisZ, thnAxisA, thnAxisInvMassDeltaPhi}); + histos.add("SEMassUnlike_AllVars", "SEMassUnlike_AllVars", HistType::kTHnSparseF, + {thnAxisInvMass, // M(phi-phi) + thnAxisPt, // pT(phi-phi) + thnAxisDeltaRPhi, // DeltaR(phi,phi) + thnAxisDeltaR, // min DeltaR(K,K) + thnAxisInvMassPhi, + thnAxisInvMassPhi, + thnAxisInvMassDeltaPhi, + thnAxisInvMassDeltaPhiSigma, + thnAxisPtCorr, + thnAxisNumPhi}); // pT correlation variable + + histos.add("MEMassUnlike_AllVars", "MEMassUnlike_AllVars", HistType::kTHnSparseF, + {thnAxisInvMass, // M(phi-phi) + thnAxisPt, // pT(phi-phi) + thnAxisDeltaRPhi, // DeltaR(phi,phi) + thnAxisDeltaR, // min DeltaR(K,K) + thnAxisInvMassPhi, // m(phi1) + thnAxisInvMassPhi, // m(phi2) + thnAxisInvMassDeltaPhi, // DeltaM_phi + thnAxisPtCorr, + thnAxisNumPhi}); // pT correlation variable + + histos.add("SEMassDoublePhi", "SEMassDoublePhi", HistType::kTHnSparseF, + {thnAxisInvMass, // M(phi-phi) + thnAxisPt, // pT(phi-phi) + thnAxisA, + thnAxisRapidity, + thnAxisInvMassPhi, // m(phi1) + thnAxisInvMassPhi, // m(phi2) + thnAxisInvMassDeltaPhi, // DeltaM_phi + thnAxisNumPhi, + axisDoublePhiPID}); + + histos.add("SEMassUnlike_VertexVars", "SEMassUnlike_VertexVars", HistType::kTHnSparseF, {thnAxisInvMass, thnAxisPt, thnAxisInvMassDeltaPhi, thnAxisInvMassPhi, thnAxisInvMassPhi, thnAxisDecayLength, thnAxisFitChi2Ndf, thnAxisRmsDcaSig}); + + // Single-phi calibration input. Axis order: + // 0 m(KK), 1 pT(K+), 2 pT(K-), 3 eta(K+), 4 eta(K-), + // 5 DeltaPhi(K+,K-), 6 pT(phi). + // It is intentionally filled BEFORE minPhiMass/maxPhiMass are applied. + histos.add("PhiMassResolutionDataDriven", "PhiMassResolutionDataDriven", + HistType::kTHnSparseF, + {ddPhiMassAxis, ddCalibKaonPtAxis, ddCalibKaonPtAxis, + ddCalibKaonEtaAxis, ddCalibKaonEtaAxis, + ddCalibDeltaPhiAxis, ddPhiPtAxis}); + + // Selected-X candidate-level resolution sparse. Filled only for + // cfgSelectedXResolutionMassLow < M(phi-phi) < cfgSelectedXResolutionMassHigh + // pT(phi-phi) > cfgSelectedXResolutionPtMin. + // + // Axis order: + // 0 pT(X) + // 1 m(phi1) + // 2 m(phi2) + // 3 pT(phi1) + // 4 pT(phi2) + // 5 pT(K+ from phi1) + // 6 pT(K- from phi1) + // 7 pT(K+ from phi2) + // 8 pT(K- from phi2) + // + // No eta axes and no M(phi-phi) axis are stored. + histos.add("SelectedXResolutionDataDriven", "SelectedXResolutionDataDriven", + HistType::kTHnSparseF, + {ddSelectedXPtAxis, + ddPhiMassAxis, ddPhiMassAxis, + ddPhiPtAxis, ddPhiPtAxis, + ddCalibKaonPtAxis, ddCalibKaonPtAxis, + ddCalibKaonPtAxis, ddCalibKaonPtAxis}); } // get kstar TLorentzVector trackSum, PartOneCMS, PartTwoCMS, trackRelK; - float getkstar(const TLorentzVector part1, - const TLorentzVector part2) + float getkstar(const TLorentzVector& part1, + const TLorentzVector& part2) { // const TLorentzVector trackSum = part1 + part2; trackSum = part1 + part2; @@ -151,39 +426,37 @@ struct doublephimeson { return 0.5 * trackRelK.P(); } - float deepangle2(const ROOT::Math::PtEtaPhiMVector candidate1, - const ROOT::Math::PtEtaPhiMVector candidate2) + float deepangle2(const ROOT::Math::PtEtaPhiMVector& candidate1, + const ROOT::Math::PtEtaPhiMVector& candidate2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; - pt1 = candidate1.Pt(); - pt2 = candidate2.Pt(); - pz1 = candidate1.Pz(); - pz2 = candidate2.Pz(); - p1 = candidate1.P(); - p2 = candidate2.P(); - angle = TMath::ACos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); + const double pt1 = candidate1.Pt(); + const double pt2 = candidate2.Pt(); + const double pz1 = candidate1.Pz(); + const double pz2 = candidate2.Pz(); + const double p1 = candidate1.P(); + const double p2 = candidate2.P(); + const double angle = TMath::ACos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); return angle; } - float deepangle(const TLorentzVector candidate1, - const TLorentzVector candidate2) + float deepangle(const TLorentzVector& candidate1, + const TLorentzVector& candidate2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; - pt1 = candidate1.Pt(); - pt2 = candidate2.Pt(); - pz1 = candidate1.Pz(); - pz2 = candidate2.Pz(); - p1 = candidate1.P(); - p2 = candidate2.P(); - angle = TMath::ACos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); + const double pt1 = candidate1.Pt(); + const double pt2 = candidate2.Pt(); + const double pz1 = candidate1.Pz(); + const double pz2 = candidate2.Pz(); + const double p1 = candidate1.P(); + const double p2 = candidate2.P(); + const double angle = TMath::ACos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); return angle; } // get cosTheta TLorentzVector daughterCMS; ROOT::Math::XYZVector threeVecDauCM, threeVecMother; - float getCosTheta(const TLorentzVector mother, - const TLorentzVector daughter) + float getCosTheta(const TLorentzVector& mother, + const TLorentzVector& daughter) { threeVecMother = mother.Vect(); const float beta = mother.Beta(); @@ -200,7 +473,205 @@ struct doublephimeson { bool selectionPID(float nsigmaTPC, float nsigmaTOF, int TOFHit, int PIDStrategy, float ptcand) { - if (PIDStrategy == 100) { + if (PIDStrategy == 2003) { + constexpr float radius2Max = 2.5f * 2.5f; + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + // TPC-only branch + if (ptcand < 0.5f) { + return std::abs(nsigmaTPC) < cutNsigmaTPC; + } else { + return nsigmaTPC > -2.0f && + nsigmaTPC < cutNsigmaTPC; + } + + } else { + // TPC+TOF branch + const float radius2 = + nsigmaTPC * nsigmaTPC + + nsigmaTOF * nsigmaTOF; + + if (radius2 >= radius2Max) { + return false; + } + + // Circle only below 2 GeV/c + if (ptcand < 2.0f) { + return true; + } + + float slope = 0.0f; + float intercept = 0.0f; + + if (ptcand < 2.5f) { + slope = 1.29f; + intercept = 3.34f; + } else if (ptcand < 3.0f) { + slope = 0.94f; + intercept = 2.45f; + } else if (ptcand < 4.0f) { + slope = 0.62f; + intercept = 1.55f; + } else if (ptcand < 5.0f) { + slope = 0.32f; + intercept = 1.45f; + } else if (ptcand < 7.0f) { + slope = 0.22f; + intercept = 1.35f; + } else { + slope = 0.10f; + intercept = 1.25f; + } + + return nsigmaTPC < + slope * nsigmaTOF + intercept; + } + } + + if (PIDStrategy == 2004) { + + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + // No TOF hit + return std::abs(nsigmaTPC) < 2.0f; + + } else { + // TOF hit present + + if (ptcand < 2.0f) { + // No TPC or TOF PID cut + return true; + } + + if (ptcand < 2.5f) { + return !(nsigmaTPC > 1.5f && + nsigmaTOF < -1.6f); + } + + if (ptcand < 3.0f) { + return !(nsigmaTPC > 1.0f && + nsigmaTOF < -1.0f); + } + + if (ptcand < 3.5f) { + return !(nsigmaTPC > 1.0f && + nsigmaTOF < -0.8f); + } + + if (ptcand < 4.5f) { + return !(nsigmaTPC > 1.0f && + nsigmaTOF < -0.5f); + } + + // pT >= 4.5 GeV/c + return !(nsigmaTPC > 1.0f && + nsigmaTOF < 1.0f); + } + } + + if (PIDStrategy == 1000) { + if ((TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) || + (TOFHit != 1 && std::abs(nsigmaTPC) < 2.0)) { + return true; + } + } + + if (PIDStrategy == 1001) { + if ((TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.0) || + (TOFHit != 1 && ptcand < 2.5 && std::abs(nsigmaTPC) < 2.0) || + (TOFHit != 1 && ptcand >= 2.5 && nsigmaTPC > -2.0 && nsigmaTPC < 1.0)) { + return true; + } + } + + if (PIDStrategy == 1002) { + if ((TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.0) || + (TOFHit != 1 && std::abs(nsigmaTPC) < 2.0)) { + return true; + } + } + + if (PIDStrategy == 1003) { + if (ptcand < 0.5 && TOFHit != 1 && std::abs(nsigmaTPC) < cutNsigmaTPC) { + return true; + } + if (ptcand < 0.5 && TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < cutNsigmaTPC) { + return true; + } + if (ptcand >= 0.5) { + if (TOFHit != 1 && nsigmaTPC > -2.0 && nsigmaTPC < cutNsigmaTPC) { + return true; + } + if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < cutNsigmaTOF) { + return true; + } + } + } + + if (PIDStrategy == 1004) { + if (ptcand < 0.5 && TOFHit != 1 && nsigmaTPC > -3.0 && nsigmaTPC < 3.0) { + return true; + } + if (ptcand < 0.5 && TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { + return true; + } + if (ptcand >= 0.5) { + if (TOFHit != 1 && nsigmaTPC > -2.0 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { + return true; + } + } + } + + if (PIDStrategy == 1008) { + if (ptcand < 0.5 && TOFHit != 1 && nsigmaTPC > -2.0 && nsigmaTPC < 2.0) { + return true; + } + if (ptcand < 0.5 && TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { + return true; + } + if (ptcand >= 0.5) { + if (TOFHit != 1 && ptcand >= 0.5 && ptcand < 0.6 && nsigmaTPC > -1.5 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit != 1 && ptcand >= 0.6 && ptcand < 0.7 && nsigmaTPC > -1.0 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit != 1 && ptcand >= 0.7 && ptcand < 1.0 && nsigmaTPC > 0.0 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit != 1 && ptcand >= 1.0 && nsigmaTPC > -2.0 && nsigmaTPC < 2.0) { + return true; + } + if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { + return true; + } + } + } + + if (PIDStrategy == 1005) { + // low pT: TPC-only + if (ptcand < 0.5 && nsigmaTPC > -2.0 && nsigmaTPC < 3.0) { + return true; + } + + // intermediate pT: require TOF + beta cut + combined TPC-TOF nsigma + if (ptcand >= 0.5 && ptcand < 5.0) { + if (TOFHit == 1 && std::sqrt(nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF) < 2.5) { + return true; + } + } + // high pT: TPC-only + if (ptcand >= 5.0 && nsigmaTPC > -2.0 && nsigmaTPC < 2.0) { + return true; + } + } + + if (PIDStrategy == 1004) { if (ptcand < 1.2) { if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { return true; @@ -225,9 +696,70 @@ struct doublephimeson { } } } else { - if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.0) { + if ((TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) || + (TOFHit != 1 && ptcand < 2.0 && nsigmaTPC > -2.5 && nsigmaTPC < 2.0) || + (TOFHit != 1 && ptcand > 2.0 && nsigmaTPC > -2.5 && nsigmaTPC < 1.0)) { + return true; + } + } + } + + if (PIDStrategy == 1007) { + + const bool hasTOF = (TOFHit == 1); + const bool passTPC_low = (nsigmaTPC > -2.0 && nsigmaTPC < 3.0); + const bool passTPC_midNoTOF = (nsigmaTPC > -1.5 && nsigmaTPC < 3.0); + const bool passTPC_highNoTOF = (nsigmaTPC > -2.0 && nsigmaTPC < 2.0); + + const bool passTPC_TOF = + hasTOF && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5; + + if (ptcand < 0.5) { + if (passTPC_low) + return true; + } else if (ptcand < 0.7) { + if (hasTOF && passTPC_TOF) + return true; + if (!hasTOF && passTPC_midNoTOF) + return true; + } else if (ptcand < 1.1) { + if (passTPC_TOF) + return true; + } else { + if (hasTOF && passTPC_TOF) + return true; + if (!hasTOF && passTPC_highNoTOF) return true; - } else if (TOFHit != 1 && nsigmaTPC > -2.5 && nsigmaTPC < 1.0) { + } + } + + if (PIDStrategy == 100) { + if (ptcand < 1.2) { + if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { + return true; + } else if (TOFHit != 1) { + if (ptcand < 0.5 && nsigmaTPC > -2.0 && nsigmaTPC < 2.5) { + return true; + } + if (ptcand >= 0.5 && ptcand < 0.6 && nsigmaTPC > -1.5 && nsigmaTPC < 2.5) { + return true; + } + if (ptcand >= 0.6 && ptcand < 0.7 && nsigmaTPC > -1.0 && nsigmaTPC < 2.0) { + return true; + } + if (ptcand >= 0.7 && ptcand < 0.8 && nsigmaTPC > -0.4 && nsigmaTPC < 2.0) { + return true; + } + if (ptcand >= 0.8 && ptcand < 1.0 && nsigmaTPC > 0.0 && nsigmaTPC < 2.0) { + return true; + } + if (ptcand >= 1.0 && ptcand < 1.2 && nsigmaTPC > -2.5 && nsigmaTPC < 0.5) { + return true; + } + } + } else { + if ((TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.0) || + (TOFHit != 1 && nsigmaTPC > -2.5 && nsigmaTPC < 1.0)) { return true; } } @@ -257,9 +789,8 @@ struct doublephimeson { } else if (ptcand >= 1.0 && ptcand < 2.0 && TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.5) { return true; } else if (ptcand > 2.0) { - if (TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.0) { - return true; - } else if (TOFHit != 1 && nsigmaTPC > -2.5 && nsigmaTPC < 1.0) { + if ((TOFHit == 1 && std::sqrt(nsigmaTOF * nsigmaTOF + nsigmaTPC * nsigmaTPC) < 2.0) || + (TOFHit != 1 && nsigmaTPC > -2.5 && nsigmaTPC < 1.0)) { return true; } } @@ -406,6 +937,60 @@ struct doublephimeson { } } } + if (PIDStrategy == 4) { + // pT <= 0.5 GeV/c (TPC-only) + if (ptcand <= 0.5f) { + if (ptcand < 0.4f) { + if (nsigmaTPC > -2.7f && nsigmaTPC < 2.3f) + return true; + } else { + if (nsigmaTPC > -2.5f && nsigmaTPC < 2.5f) + return true; + } + } else if (ptcand > 0.5f && ptcand < 1.0f) { + // 0.5 < pT < 1.0 GeV/c + if (TOFHit == 1) { + if (TMath::Sqrt(nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF) < 2.5f) + return true; + } else { + if (ptcand >= 0.5f && ptcand < 0.6f) { + if (nsigmaTPC > -1.5f && nsigmaTPC < 2.5f) + return true; + } else if (ptcand >= 0.6f && ptcand < 0.7f) { + if (nsigmaTPC > -0.75f && nsigmaTPC < 2.5f) + return true; + } else if (ptcand >= 0.7f && ptcand < 0.8f) { + if (nsigmaTPC > -0.3f && nsigmaTPC < 2.5f) + return true; + } else { // 0.8 - 1.0 + if (nsigmaTPC > 0.0f && nsigmaTPC < 2.5f) + return true; + } + } + } else if (ptcand >= 1.0f && ptcand < 1.8f) { + // 1.0 <= pT < 1.8 GeV/c + if (TOFHit == 1) { + if (TMath::Sqrt(nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF) < 2.5f) + return true; + } else { + if (nsigmaTPC > -2.0f && nsigmaTPC < 0.0f) + return true; + } + } else if (ptcand >= 1.8f && ptcand < 5.0f) { + // 1.8 < pT < 5.0 GeV/c + if (TOFHit == 1) { + if (TMath::Sqrt(nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF) < 2.0f) + return true; + } else { + if (nsigmaTPC > -2.5f && nsigmaTPC < 1.0f) + return true; + } + } else { + // pT >= 5.0 GeV/c (TPC-only, low stats) + if (nsigmaTPC > -2.5f && nsigmaTPC < 1.5f) + return true; + } + } return false; } @@ -413,13 +998,98 @@ struct doublephimeson { TLorentzVector Phi1kaonplus, Phi1kaonminus, Phi2kaonplus, Phi2kaonminus; // TLorentzVector exoticRot, Phid1Rot; + int getPhiPtCalibBin(float pt) + { + const auto& bins = cfgPhiPtBins.value; + + if (bins.size() < 2) { + return -1; + } + + if (pt < bins.front()) { + return -1; + } + + for (size_t i = 0; i + 1 < bins.size(); ++i) { + if (pt >= bins[i] && pt < bins[i + 1]) { + return static_cast(i); + } + } + + if (pt >= bins.back()) { + return static_cast(bins.size()) - 2; + } + + return -1; + } + + float getPhiMassPeakVsPt(float pt) + { + const int ibin = getPhiPtCalibBin(pt); + const auto& means = cfgPhiMeanVsPt.value; + + if (ibin >= 0 && ibin < static_cast(means.size())) { + return means[ibin]; + } + + return cfgDefaultPhiMean.value; + } + + float getPhiWidthVsPt(float pt) + { + const int ibin = getPhiPtCalibBin(pt); + const auto& widths = cfgPhiSigmaVsPt.value; + + float width = cfgDefaultPhiSigma.value; + + if (ibin >= 0 && ibin < static_cast(widths.size())) { + width = widths[ibin]; + } + + if (width < cfgMinAllowedPhiSigma.value) { + width = cfgDefaultPhiSigma.value; + } + + return width; + } + + float getNormalizedDeltaMPhi(float m1, float pt1, float m2, float pt2) + { + const float mean1 = getPhiMassPeakVsPt(pt1); + const float mean2 = getPhiMassPeakVsPt(pt2); + + const float width1 = getPhiWidthVsPt(pt1); + const float width2 = getPhiWidthVsPt(pt2); + + return TMath::Sqrt( + TMath::Power((m1 - mean1) / width1, 2.0) + + TMath::Power((m2 - mean2) / width2, 2.0)); + } + + float getDeltaMPhi(float m1, float pt1, float m2, float pt2) + { + const float mean1 = getPhiMassPeakVsPt(pt1); + const float mean2 = getPhiMassPeakVsPt(pt2); + + return TMath::Sqrt( + TMath::Power((m1 - mean1), 2.0) + + TMath::Power((m2 - mean2), 2.0)); + } + + float getNormalizedMPhi(float m1, float pt1) + { + const float mean1 = getPhiMassPeakVsPt(pt1); + const float width1 = getPhiWidthVsPt(pt1); + return ((m1 - mean1) / width1); + } + void processSE(aod::RedPhiEvents::iterator const& collision, aod::PhiTracks const& phitracks) { if (additionalEvsel && (collision.numPos() < 2 || collision.numNeg() < 2)) { return; } int phimult = 0; - for (auto phitrackd1 : phitracks) { + for (auto const& phitrackd1 : phitracks) { if (phitrackd1.phiMass() < minPhiMass1 || phitrackd1.phiMass() > maxPhiMass1) { continue; } @@ -439,7 +1109,7 @@ struct doublephimeson { } phimult = phimult + 1; } - for (auto phitrackd1 : phitracks) { + for (auto const& phitrackd1 : phitracks) { auto kaonplusd1pt = TMath::Sqrt(phitrackd1.phid1Px() * phitrackd1.phid1Px() + phitrackd1.phid1Py() * phitrackd1.phid1Py()); auto kaonminusd1pt = TMath::Sqrt(phitrackd1.phid2Px() * phitrackd1.phid2Px() + phitrackd1.phid2Py() * phitrackd1.phid2Py()); if (kaonplusd1pt > maxKaonPt) { @@ -451,7 +1121,7 @@ struct doublephimeson { if (!selectionPID(phitrackd1.phid1TPC(), phitrackd1.phid1TOF(), phitrackd1.phid1TOFHit(), strategyPID1, kaonplusd1pt)) { continue; } - if (!selectionPID(phitrackd1.phid2TPC(), phitrackd1.phid2TOF(), phitrackd1.phid2TOFHit(), strategyPID1, kaonminusd1pt)) { + if (!selectionPID(phitrackd1.phid2TPC(), phitrackd1.phid2TOF(), phitrackd1.phid2TOFHit(), strategyPID2, kaonminusd1pt)) { continue; } histos.fill(HIST("hnsigmaTPCTOFKaon"), phitrackd1.phid1TPC(), phitrackd1.phid1TOF(), kaonplusd1pt); @@ -462,7 +1132,7 @@ struct doublephimeson { Phid1.SetXYZM(phitrackd1.phiPx(), phitrackd1.phiPy(), phitrackd1.phiPz(), phitrackd1.phiMass()); Phi1kaonplus.SetXYZM(phitrackd1.phid1Px(), phitrackd1.phid1Py(), phitrackd1.phid1Pz(), 0.493); Phi1kaonminus.SetXYZM(phitrackd1.phid2Px(), phitrackd1.phid2Py(), phitrackd1.phid2Pz(), 0.493); - for (auto phitrackd2 : phitracks) { + for (auto const& phitrackd2 : phitracks) { auto phid2id = phitrackd2.index(); if (phid2id <= phid1id) { continue; @@ -555,7 +1225,7 @@ struct doublephimeson { } int phimult = 0; - for (auto phitrackd1 : phitracks) { + for (auto const& phitrackd1 : phitracks) { if (phitrackd1.phiMass() < minPhiMass1 || phitrackd1.phiMass() > maxPhiMass1) { continue; } @@ -570,12 +1240,12 @@ struct doublephimeson { if (!selectionPID(phitrackd1.phid1TPC(), phitrackd1.phid1TOF(), phitrackd1.phid1TOFHit(), strategyPID1, kaonplusd1pt)) { continue; } - if (!selectionPID(phitrackd1.phid2TPC(), phitrackd1.phid2TOF(), phitrackd1.phid2TOFHit(), strategyPID1, kaonminusd1pt)) { + if (!selectionPID(phitrackd1.phid2TPC(), phitrackd1.phid2TOF(), phitrackd1.phid2TOFHit(), strategyPID2, kaonminusd1pt)) { continue; } phimult = phimult + 1; } - for (auto phitrackd1 : phitracks) { + for (auto const& phitrackd1 : phitracks) { auto kaonplusd1pt = TMath::Sqrt(phitrackd1.phid1Px() * phitrackd1.phid1Px() + phitrackd1.phid1Py() * phitrackd1.phid1Py()); auto kaonminusd1pt = TMath::Sqrt(phitrackd1.phid2Px() * phitrackd1.phid2Px() + phitrackd1.phid2Py() * phitrackd1.phid2Py()); @@ -592,7 +1262,7 @@ struct doublephimeson { if (!selectionPID(phitrackd1.phid1TPC(), phitrackd1.phid1TOF(), phitrackd1.phid1TOFHit(), strategyPID1, kaonplusd1pt)) { continue; } - if (!selectionPID(phitrackd1.phid2TPC(), phitrackd1.phid2TOF(), phitrackd1.phid2TOFHit(), strategyPID1, kaonminusd1pt)) { + if (!selectionPID(phitrackd1.phid2TPC(), phitrackd1.phid2TOF(), phitrackd1.phid2TOFHit(), strategyPID2, kaonminusd1pt)) { continue; } histos.fill(HIST("hnsigmaTPCTOFKaon"), phitrackd1.phid1TPC(), phitrackd1.phid1TOF(), kaonplusd1pt); @@ -603,7 +1273,7 @@ struct doublephimeson { Phid1.SetXYZM(phitrackd1.phiPx(), phitrackd1.phiPy(), phitrackd1.phiPz(), phitrackd1.phiMass()); Phi1kaonplus.SetXYZM(phitrackd1.phid1Px(), phitrackd1.phid1Py(), phitrackd1.phid1Pz(), 0.493); Phi1kaonminus.SetXYZM(phitrackd1.phid2Px(), phitrackd1.phid2Py(), phitrackd1.phid2Pz(), 0.493); - for (auto phitrackd2 : phitracks) { + for (auto const& phitrackd2 : phitracks) { auto phid2id = phitrackd2.index(); if (phid2id <= phid1id) { continue; @@ -670,14 +1340,14 @@ struct doublephimeson { for (auto if1 = exoticresonance.begin(); if1 != exoticresonance.end(); ++if1) { auto i5 = std::distance(exoticresonance.begin(), if1); - auto exotic1phi1 = phiresonanced1.at(i5); - auto exotic1phi2 = phiresonanced2.at(i5); - auto exotic1 = exoticresonance.at(i5); + const auto& exotic1phi1 = phiresonanced1.at(i5); + const auto& exotic1phi2 = phiresonanced2.at(i5); + const auto& exotic1 = exoticresonance.at(i5); - auto exotic1kaonplus1 = kaonplus1.at(i5); - auto exotic1kaonminus1 = kaonminus1.at(i5); - auto exotic1kaonplus2 = kaonplus2.at(i5); - auto exotic1kaonminus2 = kaonminus2.at(i5); + const auto& exotic1kaonplus1 = kaonplus1.at(i5); + const auto& exotic1kaonminus1 = kaonminus1.at(i5); + const auto& exotic1kaonplus2 = kaonplus2.at(i5); + const auto& exotic1kaonminus2 = kaonminus2.at(i5); auto deltaRkaonplus1 = TMath::Sqrt(TMath::Power(exotic1kaonplus1.Phi() - exotic1kaonplus2.Phi(), 2.0) + TMath::Power(exotic1kaonplus1.Eta() - exotic1kaonplus2.Eta(), 2.0)); auto deltaRkaonminus1 = TMath::Sqrt(TMath::Power(exotic1kaonminus1.Phi() - exotic1kaonminus2.Phi(), 2.0) + TMath::Power(exotic1kaonminus1.Eta() - exotic1kaonminus2.Eta(), 2.0)); histos.fill(HIST("hDeltaRkaonplus"), deltaRkaonplus1); @@ -695,14 +1365,14 @@ struct doublephimeson { for (auto if2 = if1 + 1; if2 != exoticresonance.end(); ++if2) { auto i6 = std::distance(exoticresonance.begin(), if2); - auto exotic2phi1 = phiresonanced1.at(i6); - auto exotic2phi2 = phiresonanced2.at(i6); - auto exotic2 = exoticresonance.at(i6); - - auto exotic2kaonplus1 = kaonplus1.at(i6); - auto exotic2kaonminus1 = kaonminus1.at(i6); - auto exotic2kaonplus2 = kaonplus2.at(i6); - auto exotic2kaonminus2 = kaonminus2.at(i6); + const auto& exotic2phi1 = phiresonanced1.at(i6); + const auto& exotic2phi2 = phiresonanced2.at(i6); + const auto& exotic2 = exoticresonance.at(i6); + + const auto& exotic2kaonplus1 = kaonplus1.at(i6); + const auto& exotic2kaonminus1 = kaonminus1.at(i6); + const auto& exotic2kaonplus2 = kaonplus2.at(i6); + const auto& exotic2kaonminus2 = kaonminus2.at(i6); auto deltaRkaonplus2 = TMath::Sqrt(TMath::Power(exotic2kaonplus1.Phi() - exotic2kaonplus2.Phi(), 2.0) + TMath::Power(exotic2kaonplus1.Eta() - exotic2kaonplus2.Eta(), 2.0)); auto deltaRkaonminus2 = TMath::Sqrt(TMath::Power(exotic2kaonminus1.Phi() - exotic2kaonminus2.Phi(), 2.0) + TMath::Power(exotic2kaonminus1.Eta() - exotic2kaonminus2.Eta(), 2.0)); @@ -728,14 +1398,14 @@ struct doublephimeson { } else { for (auto if1 = exoticresonance.begin(); if1 != exoticresonance.end(); ++if1) { auto i5 = std::distance(exoticresonance.begin(), if1); - auto exotic1phi1 = phiresonanced1.at(i5); - auto exotic1phi2 = phiresonanced2.at(i5); - auto exotic1 = exoticresonance.at(i5); - - auto exotic1kaonplus1 = kaonplus1.at(i5); - auto exotic1kaonminus1 = kaonminus1.at(i5); - auto exotic1kaonplus2 = kaonplus2.at(i5); - auto exotic1kaonminus2 = kaonminus2.at(i5); + const auto& exotic1phi1 = phiresonanced1.at(i5); + const auto& exotic1phi2 = phiresonanced2.at(i5); + const auto& exotic1 = exoticresonance.at(i5); + + const auto& exotic1kaonplus1 = kaonplus1.at(i5); + const auto& exotic1kaonminus1 = kaonminus1.at(i5); + const auto& exotic1kaonplus2 = kaonplus2.at(i5); + const auto& exotic1kaonminus2 = kaonminus2.at(i5); auto deltaRkaonplus1 = TMath::Sqrt(TMath::Power(exotic1kaonplus1.Phi() - exotic1kaonplus2.Phi(), 2.0) + TMath::Power(exotic1kaonplus1.Eta() - exotic1kaonplus2.Eta(), 2.0)); auto deltaRkaonminus1 = TMath::Sqrt(TMath::Power(exotic1kaonminus1.Phi() - exotic1kaonminus2.Phi(), 2.0) + TMath::Power(exotic1kaonminus1.Eta() - exotic1kaonminus2.Eta(), 2.0)); auto deltam1 = TMath::Sqrt(TMath::Power(exotic1phi1.M() - 1.0192, 2.0) + TMath::Power(exotic1phi2.M() - 1.0192, 2.0)); @@ -974,8 +1644,8 @@ struct doublephimeson { return; // --- helpers --- - constexpr double mPhiPDG = 1.019461; // GeV/c^2 - constexpr double mKPDG = 0.493677; // GeV/c^2 + constexpr double mPhiPDG = o2::constants::physics::MassPhi; // GeV/c^2 + constexpr double mKPDG = o2::constants::physics::MassKPlus; // GeV/c^2 const auto deltaMPhi = [=](double m1, double m2) { const double d1 = m1 - mPhiPDG, d2 = m2 - mPhiPDG; @@ -1127,9 +1797,8 @@ struct doublephimeson { const double minDR = minDRV[i]; // (optional but recommended) protect ptcorr from blow-ups - const double denom = (pair.Pt() - p1.Pt()); - if (std::abs(denom) < 1e-9) - continue; + const double denom = std::abs(pair.Pt() - p1.Pt()); + const double ptcorr = p1.Pt() / denom; histos.fill(HIST("hPtCorrelation"), pair.Pt(), ptcorr); @@ -1156,14 +1825,769 @@ struct doublephimeson { } } PROCESS_SWITCH(doublephimeson, processopti4, "Process Optimized same event", true); + double dMNominal = 100.0; - SliceCache cache; - using BinningTypeVertexContributor = ColumnBinningPolicy; + void processopti5(aod::RedPhiEvents::iterator const& collision, aod::PhiTracks const& phitracks) + { + if (additionalEvsel && (collision.numPos() < 2 || collision.numNeg() < 2)) { + return; + } + + constexpr double mPhiPDG = o2::constants::physics::MassPhi; // GeV/c^2 + constexpr double mKPDG = o2::constants::physics::MassKPlus; // GeV/c^2 + + // Build the phi and its daughter-kaon four-vectors. With the correction + // disabled this reproduces the original opti5 construction exactly. With + // it enabled, the scale is evaluated using the uncorrected phi pT, both + // kaon three-momenta are scaled coherently, their energies are recomputed + // using mKPDG, and the corrected phi is rebuilt from the two kaons. + const auto buildPhiAndKaons = [&](const auto& t, + TLorentzVector& phi, + TLorentzVector& kplus, + TLorentzVector& kminus) { + kplus.SetXYZM(t.phid1Px(), t.phid1Py(), t.phid1Pz(), mKPDG); + kminus.SetXYZM(t.phid2Px(), t.phid2Py(), t.phid2Pz(), mKPDG); + phi.SetXYZM(t.phiPx(), t.phiPy(), t.phiPz(), t.phiMass()); + + if (!cfgApplyKaonMomentumCorrection) { + return; + } + + const double uncorrectedPhiPt = std::hypot(t.phiPx(), t.phiPy()); + if (!std::isfinite(uncorrectedPhiPt) || uncorrectedPhiPt <= 0. || + uncorrectedPhiPt < cfgMomCorrPtMin || + uncorrectedPhiPt > cfgMomCorrPtMax) { + return; + } + + const double sigmoidWidth = cfgMomCorrP3GeV; + if (!std::isfinite(sigmoidWidth) || sigmoidWidth <= 0.) { + return; + } + + const double sigmoid = + 1. / (1. + std::exp(-(uncorrectedPhiPt - cfgMomCorrP2GeV) / + sigmoidWidth)); + const double epsilonPercent = + cfgMomCorrP0Percent + + cfgMomCorrP1Percent * sigmoid + + cfgMomCorrP4PercentPerGeV * uncorrectedPhiPt + + cfgMomCorrP5PercentPerGeV2 * uncorrectedPhiPt * uncorrectedPhiPt; + const double scale = 1. + 0.01 * epsilonPercent; + + if (!std::isfinite(scale) || scale <= 0.) { + return; + } + + kplus.SetXYZM(scale * t.phid1Px(), + scale * t.phid1Py(), + scale * t.phid1Pz(), + mKPDG); + kminus.SetXYZM(scale * t.phid2Px(), + scale * t.phid2Py(), + scale * t.phid2Pz(), + mKPDG); + phi = kplus + kminus; + }; + + // --- phi multiplicity with PID --- + int phimult = 0; + // int nBestPairingRejected = 0; + for (auto const& t : phitracks) { + const double kpluspt = std::hypot(t.phid1Px(), t.phid1Py()); + const double kminuspt = std::hypot(t.phid2Px(), t.phid2Py()); + + histos.fill(HIST("hnsigmaTPCTOFKaonBefore"), t.phid1TPC(), t.phid1TOF(), kpluspt); + histos.fill(HIST("hnsigmaTPCKaonPlusBefore"), t.phid1TPC(), kpluspt); + histos.fill(HIST("hnsigmaTPCKaonMinusBefore"), t.phid2TPC(), kminuspt); + + TLorentzVector phi1; + TLorentzVector kplus; + TLorentzVector kminus; + buildPhiAndKaons(t, phi1, kplus, kminus); + const double phi1Mass = cfgApplyKaonMomentumCorrection ? phi1.M() : t.phiMass(); + + // Apply the same daughter/PID requirements before filling the calibration + // sparse. The phi-pT requirement for that sparse is evaluated below from + // the RAW daughters, so it remains independent of any optional correction. + if (kpluspt > maxKaonPt || kminuspt > maxKaonPt) { + continue; + } + if (!selectionPID(t.phid1TPC(), t.phid1TOF(), t.phid1TOFHit(), strategyPID1, kpluspt)) { + continue; + } + if (!selectionPID(t.phid2TPC(), t.phid2TOF(), t.phid2TOFHit(), strategyPID2, kminuspt)) { + continue; + } + + if (cfgFillDataDrivenPhiResolution) { + // IMPORTANT: always store the RAW reconstructed daughter momenta here, + // independent of cfgApplyKaonMomentumCorrection. This keeps the + // resolution calibration data-driven and also allows two data sets to + // be compared later to infer their relative momentum-scale shift + // without circularly applying a pre-existing correction first. + TLorentzVector kplusRaw; + TLorentzVector kminusRaw; + kplusRaw.SetXYZM(t.phid1Px(), t.phid1Py(), t.phid1Pz(), mKPDG); + kminusRaw.SetXYZM(t.phid2Px(), t.phid2Py(), t.phid2Pz(), mKPDG); + const TLorentzVector phiForResolution = kplusRaw + kminusRaw; + const double dPhiKK = TVector2::Phi_mpi_pi(kplusRaw.Phi() - kminusRaw.Phi()); + if (phiForResolution.Pt() >= minPhiPt && phiForResolution.Pt() <= maxPhiPt) { + histos.fill(HIST("PhiMassResolutionDataDriven"), + phiForResolution.M(), + kplusRaw.Pt(), kminusRaw.Pt(), + kplusRaw.Eta(), kminusRaw.Eta(), + dPhiKK, phiForResolution.Pt()); + } + } + + // From here onward keep the original signal-phi definition unchanged. + if (phi1.Pt() < minPhiPt || phi1.Pt() > maxPhiPt) { + continue; + } + if (phi1Mass < minPhiMass1 || phi1Mass > maxPhiMass1) { + continue; + } + + histos.fill(HIST("hnsigmaTPCTOFKaon"), t.phid1TPC(), t.phid1TOF(), kpluspt); + histos.fill(HIST("hnsigmaTPCKaonPlus"), t.phid1TPC(), kpluspt); + histos.fill(HIST("hnsigmaTPCKaonMinus"), t.phid2TPC(), kminuspt); + histos.fill(HIST("hPhiMassVsPt"), phi1Mass, phi1.Pt()); + + ++phimult; + } + + if (phimult < 2) { + return; + } + + const auto deltaMPhiNominal = [=](double m1, double m2) { + const double d1 = m1 - mPhiPDG; + const double d2 = m2 - mPhiPDG; + return std::sqrt(d1 * d1 + d2 * d2); + }; + + const auto deltaR = [](double phi1, double eta1, double phi2, double eta2) { + const double dphi = std::abs(TVector2::Phi_mpi_pi(phi1 - phi2)); + const double deta = eta1 - eta2; + return std::sqrt(dphi * dphi + deta * deta); + }; + + const auto minKaonDeltaR = + [&](const ROOT::Math::PtEtaPhiMVector& kplus1, + const ROOT::Math::PtEtaPhiMVector& kminus1, + const ROOT::Math::PtEtaPhiMVector& kplus2, + const ROOT::Math::PtEtaPhiMVector& kminus2) { + const double dRpp = + deltaR(kplus1.Phi(), kplus1.Eta(), + kplus2.Phi(), kplus2.Eta()); + + const double dRmm = + deltaR(kminus1.Phi(), kminus1.Eta(), + kminus2.Phi(), kminus2.Eta()); + + const double dRpm = + deltaR(kplus1.Phi(), kplus1.Eta(), + kminus2.Phi(), kminus2.Eta()); + + const double dRmp = + deltaR(kminus1.Phi(), kminus1.Eta(), + kplus2.Phi(), kplus2.Eta()); + + histos.fill(HIST("hDeltaRkaonplus"), dRpp); + histos.fill(HIST("hDeltaRkaonminus"), dRmm); + return std::min({dRpp, dRmm, dRpm, dRmp}); + }; + + const auto phiPtAsymmetry = + [](const auto& phi1, + const auto& phi2) -> double { + const double sumPt = phi1.Pt() + phi2.Pt(); + + if (sumPt <= 0.) { + return -1.; + } + + return std::abs(phi1.Pt() - phi2.Pt()) / sumPt; + }; + + const auto nKaonTOFHits = + [](const auto& t1, const auto& t2) -> int { + return static_cast(t1.phid1TOFHit() == 1) + + static_cast(t1.phid2TOFHit() == 1) + + static_cast(t2.phid1TOFHit() == 1) + + static_cast(t2.phid2TOFHit() == 1); + }; + + const auto doublePhiPIDScore = + [](const auto& t1, const auto& t2) -> double { + const auto kaonPIDScore = + [](double nSigmaTPC, + double nSigmaTOF, + int tofHit) -> double { + // TOFHit is 1 when TOF is available and -1 otherwise. + if (tofHit == 1) { + return std::sqrt( + (nSigmaTPC * nSigmaTPC + + nSigmaTOF * nSigmaTOF)); + } + + // TPC-only track + return std::abs(nSigmaTPC); + }; + + const double pid1 = + kaonPIDScore(t1.phid1TPC(), + t1.phid1TOF(), + t1.phid1TOFHit()); + + const double pid2 = + kaonPIDScore(t1.phid2TPC(), + t1.phid2TOF(), + t1.phid2TOFHit()); + + const double pid3 = + kaonPIDScore(t2.phid1TPC(), + t2.phid1TOF(), + t2.phid1TOFHit()); + + const double pid4 = + kaonPIDScore(t2.phid2TPC(), + t2.phid2TOF(), + t2.phid2TOFHit()); + + return std::max({pid1, pid2, pid3, pid4}); + }; + + std::vector pairV; + std::vector phi1V; + std::vector phi2V; + std::vector minDRV; + std::vector nTOFV; + std::vector pid4KV; + + for (auto const& t1 : phitracks) { + const double kplus1pt = std::hypot(t1.phid1Px(), t1.phid1Py()); + const double kminus1pt = std::hypot(t1.phid2Px(), t1.phid2Py()); + + if (kplus1pt > maxKaonPt || kminus1pt > maxKaonPt) { + continue; + } + if (!selectionPID(t1.phid1TPC(), t1.phid1TOF(), t1.phid1TOFHit(), strategyPID1, kplus1pt)) { + continue; + } + if (!selectionPID(t1.phid2TPC(), t1.phid2TOF(), t1.phid2TOFHit(), strategyPID2, kminus1pt)) { + continue; + } + + TLorentzVector phi1; + TLorentzVector k1p; + TLorentzVector k1m; + + buildPhiAndKaons(t1, phi1, k1p, k1m); + const double phi1Mass = cfgApplyKaonMomentumCorrection ? phi1.M() : t1.phiMass(); + + if (phi1Mass < minPhiMass1 || phi1Mass > maxPhiMass1) { + continue; + } + if (phi1.Pt() < minPhiPt || phi1.Pt() > maxPhiPt) { + continue; + } + + const auto id1 = t1.index(); + + for (auto const& t2 : phitracks) { + const auto id2 = t2.index(); + if (id2 <= id1) { + // LOGF(info, "track reject %d %d %f %f", id1, id2, t1.phiMass(), t2.phiMass()); + continue; + } + const double kplus2pt = std::hypot(t2.phid1Px(), t2.phid1Py()); + const double kminus2pt = std::hypot(t2.phid2Px(), t2.phid2Py()); + + if (kplus2pt > maxKaonPt || kminus2pt > maxKaonPt) { + continue; + } + if (!selectionPID(t2.phid1TPC(), t2.phid1TOF(), t2.phid1TOFHit(), strategyPID1, kplus2pt)) { + continue; + } + if (!selectionPID(t2.phid2TPC(), t2.phid2TOF(), t2.phid2TOFHit(), strategyPID2, kminus2pt)) { + continue; + } + + TLorentzVector phi2; + TLorentzVector k2p; + TLorentzVector k2m; + + buildPhiAndKaons(t2, phi2, k2p, k2m); + const double phi2Mass = cfgApplyKaonMomentumCorrection ? phi2.M() : t2.phiMass(); + + if (phi2Mass < minPhiMass1 || phi2Mass > maxPhiMass1) { + continue; + } + if (phi2.Pt() < minPhiPt || phi2.Pt() > maxPhiPt) { + continue; + } + + TLorentzVector pair = phi1 + phi2; + if (pair.Pt() < minExoticPt) { + continue; + } + if (pair.M() < minExoticMass || pair.M() > maxExoticMass) { + continue; + } + // reject any shared daughter between the two phi candidates + if (t1.phid1Index() == t2.phid1Index() || + t1.phid1Index() == t2.phid2Index() || + t1.phid2Index() == t2.phid1Index() || + t1.phid2Index() == t2.phid2Index()) { + LOGF(info, "track share %d %d %d %d", t1.phid1Index(), t1.phid2Index(), t2.phid1Index(), t2.phid2Index()); + continue; + } + + auto cross12 = k1p + k2m; + auto cross21 = k2p + k1m; + bool alternativePairValid = cross12.M() > cfgCrossPhiLow && cross12.M() < cfgCrossPhiHigh && cross21.M() > cfgCrossPhiLow && cross21.M() < cfgCrossPhiHigh; + if (alternativePairValid) { + float scoreOriginal = deltaMPhiNominal(phi1.M(), phi2.M()); + float scoreCross = deltaMPhiNominal(cross12.M(), cross21.M()); + if (useCrossPairRejection && (scoreCross < scoreOriginal)) { + LOGF(info, "Best-pairing rejected: original score = %3.4f, cross scoremPhi2 = %3.4f", scoreOriginal, scoreCross); + continue; // another pairing of these four tracks is better + } + } + + // Compact candidate-level input for the data-driven X resolution. + // Fill only in the requested X mass window and above the X pT threshold. + // At this point the candidate has already passed: + // - daughter pT/PID selections, + // - phi mass and phi pT selections, + // - pair pT / broad pair-mass selections, + // - shared-track rejection, + // - cross-pairing rejection. + // + // Use the same four-vectors that define the selected candidate. Thus, + // if cfgApplyKaonMomentumCorrection is enabled, the sparse consistently + // stores the corrected candidate kinematics; otherwise it stores raw + // reconstructed kinematics. + if (cfgFillSelectedXResolutionSparse && + pair.Pt() > cfgSelectedXResolutionPtMin && + pair.M() > cfgSelectedXResolutionMassLow && + pair.M() < cfgSelectedXResolutionMassHigh) { + histos.fill(HIST("SelectedXResolutionDataDriven"), + pair.Pt(), + phi1.M(), phi2.M(), + phi1.Pt(), phi2.Pt(), + k1p.Pt(), k1m.Pt(), + k2p.Pt(), k2m.Pt()); + } + + histos.fill(HIST("hPhiMass"), phi1.M(), phi2.M(), pair.Pt()); + histos.fill(HIST("hPhiMassNormalized"), getNormalizedMPhi(phi1.M(), phi1.Pt()), getNormalizedMPhi(phi2.M(), phi2.Pt()), pair.Pt()); + + ROOT::Math::PtEtaPhiMVector k1pV(k1p.Pt(), k1p.Eta(), k1p.Phi(), mKPDG); + ROOT::Math::PtEtaPhiMVector k1mV(k1m.Pt(), k1m.Eta(), k1m.Phi(), mKPDG); + ROOT::Math::PtEtaPhiMVector k2pV(k2p.Pt(), k2p.Eta(), k2p.Phi(), mKPDG); + ROOT::Math::PtEtaPhiMVector k2mV(k2m.Pt(), k2m.Eta(), k2m.Phi(), mKPDG); + + const double minDR = minKaonDeltaR(k1pV, k1mV, k2pV, k2mV); + const int nTOF = nKaonTOFHits(t1, t2); + const double pid4K = doublePhiPIDScore(t1, t2); + pairV.emplace_back(pair.Pt(), pair.Eta(), pair.Phi(), pair.M()); + phi1V.emplace_back(phi1.Pt(), phi1.Eta(), phi1.Phi(), phi1.M()); + phi2V.emplace_back(phi2.Pt(), phi2.Eta(), phi2.Phi(), phi2.M()); + minDRV.emplace_back(minDR); + nTOFV.emplace_back(nTOF); + pid4KV.emplace_back(pid4K); + } + } + + if (pairV.empty()) { + return; + } + + for (size_t i = 0; i < pairV.size(); ++i) { + TLorentzVector p1; + TLorentzVector p2; + TLorentzVector pair; + + p1.SetPtEtaPhiM(phi1V[i].Pt(), phi1V[i].Eta(), phi1V[i].Phi(), phi1V[i].M()); + p2.SetPtEtaPhiM(phi2V[i].Pt(), phi2V[i].Eta(), phi2V[i].Phi(), phi2V[i].M()); + pair.SetPtEtaPhiM(pairV[i].Pt(), pairV[i].Eta(), pairV[i].Phi(), pairV[i].M()); + + const double M = pair.M(); + const double pairPt = pair.Pt(); + const double dRphi = deltaR(p1.Phi(), p1.Eta(), p2.Phi(), p2.Eta()); + const double minDR = minDRV[i]; + const double combine4kpid = pid4KV[i]; + const double nkaonTOF = nTOFV[i]; + if (!useParametrized) { + dMNominal = deltaMPhiNominal(p1.M(), p2.M()); + } else { + dMNominal = getDeltaMPhi(p1.M(), p1.Pt(), p2.M(), p2.Pt()); + } + const double dMNominalNsigma = getNormalizedDeltaMPhi(p1.M(), p1.Pt(), p2.M(), p2.Pt()); + const double denom = std::abs(pairPt - p1.Pt()); + + const double ptcorr = p1.Pt() / denom; + + const double pt1 = p1.Pt(); + const double pt2 = p2.Pt(); + const double ptsum = pt1 + pt2; + if (ptsum <= 0.0) { + continue; + } + const double apt = phiPtAsymmetry(p1, p2); + // const double absCosTheta = absCosThetaStar(p1, p2); + + if (pairPt > minExoticPt) { + histos.fill(HIST("hPtCorrelation"), pairPt, ptcorr); + // histos.fill(HIST("hMassCent"), p1.M(), p2.M(), collision.centrality()); + histos.fill(HIST("SEMassUnlike_AllVars"), + M, + pairPt, + dRphi, + minDR, + p1.M(), + p2.M(), + dMNominal, + dMNominalNsigma, + ptcorr, + pairV.size()); + + histos.fill(HIST("SEMassDoublePhi"), + M, + pairPt, + apt, + std::abs(pair.Rapidity()), + p1.M(), + p2.M(), + dMNominal, + nkaonTOF, + combine4kpid); + } + } + } + PROCESS_SWITCH(doublephimeson, processopti5, "Process Optimized same event with all variables", true); + + void processPairOpti6(aod::PhiPhiPairs const& pairs) + { + constexpr double mPhiPDG = o2::constants::physics::MassPhi; + constexpr double mKPDG = o2::constants::physics::MassKPlus; + + for (auto const& pair : pairs) { + if (pair.k1Index() == pair.k2Index() || pair.k1Index() == pair.k3Index() || pair.k1Index() == pair.k4Index() || pair.k2Index() == pair.k3Index() || pair.k2Index() == pair.k4Index() || pair.k3Index() == pair.k4Index()) { + continue; + } + if (std::abs(pair.k1DcaXY()) >= 0.02f || std::abs(pair.k1DcaZ()) >= 0.02f || std::abs(pair.k2DcaXY()) >= 0.02f || std::abs(pair.k2DcaZ()) >= 0.02f || std::abs(pair.k3DcaXY()) >= 0.02f || std::abs(pair.k3DcaZ()) >= 0.02f || std::abs(pair.k4DcaXY()) >= 0.02f || std::abs(pair.k4DcaZ()) >= 0.02f) { + continue; + } + const double pairPt = std::hypot(pair.pairPx(), pair.pairPy()); + if (pairPt <= minExoticPt || pair.pairMass() < minExoticMass || pair.pairMass() > maxExoticMass) { + continue; + } + + const double pt1 = std::hypot(pair.phi1Px(), pair.phi1Py()); + const double pt2 = std::hypot(pair.phi2Px(), pair.phi2Py()); + if (pt1 < minPhiPt || pt1 > maxPhiPt || pt2 < minPhiPt || pt2 > maxPhiPt) { + continue; + } + + const ROOT::Math::PxPyPzMVector k1(pair.k1Px(), pair.k1Py(), pair.k1Pz(), mKPDG); + const ROOT::Math::PxPyPzMVector k2(pair.k2Px(), pair.k2Py(), pair.k2Pz(), mKPDG); + const ROOT::Math::PxPyPzMVector k3(pair.k3Px(), pair.k3Py(), pair.k3Pz(), mKPDG); + const ROOT::Math::PxPyPzMVector k4(pair.k4Px(), pair.k4Py(), pair.k4Pz(), mKPDG); + + const double mCross14 = (k1 + k4).M(); + const double mCross32 = (k3 + k2).M(); + if ((mCross14 > cfgCrossPhiLow && mCross14 < cfgCrossPhiHigh) || (mCross32 > cfgCrossPhiLow && mCross32 < cfgCrossPhiHigh)) { + continue; + } + + double deltaM = std::hypot(pair.phi1Mass() - mPhiPDG, pair.phi2Mass() - mPhiPDG); + if (useParametrized) { + deltaM = getDeltaMPhi(pair.phi1Mass(), pt1, pair.phi2Mass(), pt2); + } + + const double decayLength = pair.vertexL3DSig(); + const double fitChi2Ndf = pair.fitChi2Ndf(); + const double rmsDcaSig = pair.rmsDcaSig(); + + if (!std::isfinite(pair.pairMass()) || !std::isfinite(deltaM) || !std::isfinite(pt1) || !std::isfinite(pt2) || !std::isfinite(decayLength) || !std::isfinite(fitChi2Ndf) || !std::isfinite(rmsDcaSig)) { + continue; + } + + histos.fill(HIST("SEMassUnlike_VertexVars"), pair.pairMass(), pairPt, deltaM, pair.phi1Mass(), pair.phi2Mass(), decayLength, fitChi2Ndf, rmsDcaSig); + } + } + PROCESS_SWITCH(doublephimeson, processPairOpti6, "Process fitted phi-phi pairs with vertex variables", false); + + SliceCache cache; + using BinningTypeVertexContributor = ColumnBinningPolicy; + + void processMixedEventopti5(aod::RedPhiEvents& collisions, aod::PhiTracks& phitracks) + { + auto tracksTuple = std::make_tuple(phitracks); + + BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}}; + + SameKindPair pair{ + binningOnPositions, nEvtMixing, -1, collisions, tracksTuple, &cache}; + + constexpr double mKPDG = o2::constants::physics::MassKPlus; // GeV/c^2 + + const auto deltaR = [](double phi1, double eta1, double phi2, double eta2) { + const double dphi = std::abs(TVector2::Phi_mpi_pi(phi1 - phi2)); + const double deta = eta1 - eta2; + return std::sqrt(dphi * dphi + deta * deta); + }; + + const auto minKaonDeltaR = + [&](const ROOT::Math::PtEtaPhiMVector& kplus1, + const ROOT::Math::PtEtaPhiMVector& kminus1, + const ROOT::Math::PtEtaPhiMVector& kplus2, + const ROOT::Math::PtEtaPhiMVector& kminus2) { + const double dRkplus = + deltaR(kplus1.Phi(), kplus1.Eta(), kplus2.Phi(), kplus2.Eta()); + + const double dRkminus = + deltaR(kminus1.Phi(), kminus1.Eta(), kminus2.Phi(), kminus2.Eta()); + + histos.fill(HIST("hDeltaRkaonplus"), dRkplus); + histos.fill(HIST("hDeltaRkaonminus"), dRkminus); + + double minDR = dRkplus; + minDR = std::min(minDR, dRkminus); + + minDR = std::min(minDR, + deltaR(kplus1.Phi(), kplus1.Eta(), + kminus1.Phi(), kminus1.Eta())); + + minDR = std::min(minDR, + deltaR(kplus1.Phi(), kplus1.Eta(), + kminus2.Phi(), kminus2.Eta())); + + minDR = std::min(minDR, + deltaR(kplus2.Phi(), kplus2.Eta(), + kminus1.Phi(), kminus1.Eta())); + + minDR = std::min(minDR, + deltaR(kplus2.Phi(), kplus2.Eta(), + kminus2.Phi(), kminus2.Eta())); + + return minDR; + }; + + struct PhiCand { + ROOT::Math::PtEtaPhiMVector phi; + ROOT::Math::PtEtaPhiMVector kplus; + ROOT::Math::PtEtaPhiMVector kminus; + }; + + for (auto& [collision1, tracks1, collision2, tracks2] : pair) { + + if (collision1.index() == collision2.index()) { + continue; + } + + if (additionalEvsel) { + if (collision1.numPos() < 2 || collision1.numNeg() < 2) { + continue; + } + if (collision2.numPos() < 2 || collision2.numNeg() < 2) { + continue; + } + } + + std::vector cands1; + std::vector cands2; + + // ====================================================== + // Build phi candidates from event 1 + // ====================================================== + for (auto const& t1 : tracks1) { + const double kplus1pt = std::hypot(t1.phid1Px(), t1.phid1Py()); + const double kminus1pt = std::hypot(t1.phid2Px(), t1.phid2Py()); + + if (kplus1pt > maxKaonPt || kminus1pt > maxKaonPt) { + continue; + } + + if (!selectionPID(t1.phid1TPC(), t1.phid1TOF(), t1.phid1TOFHit(), + strategyPID1, kplus1pt)) { + continue; + } + + if (!selectionPID(t1.phid2TPC(), t1.phid2TOF(), t1.phid2TOFHit(), + strategyPID2, kminus1pt)) { + continue; + } + + if (t1.phiMass() < minPhiMass1 || t1.phiMass() > maxPhiMass1) { + continue; + } + + TLorentzVector phi1; + TLorentzVector k1p; + TLorentzVector k1m; + + phi1.SetXYZM(t1.phiPx(), t1.phiPy(), t1.phiPz(), t1.phiMass()); + k1p.SetXYZM(t1.phid1Px(), t1.phid1Py(), t1.phid1Pz(), mKPDG); + k1m.SetXYZM(t1.phid2Px(), t1.phid2Py(), t1.phid2Pz(), mKPDG); + + if (phi1.Pt() < minPhiPt || phi1.Pt() > maxPhiPt) { + continue; + } + + PhiCand cand; + cand.phi = ROOT::Math::PtEtaPhiMVector(phi1.Pt(), phi1.Eta(), phi1.Phi(), phi1.M()); + cand.kplus = ROOT::Math::PtEtaPhiMVector(k1p.Pt(), k1p.Eta(), k1p.Phi(), mKPDG); + cand.kminus = ROOT::Math::PtEtaPhiMVector(k1m.Pt(), k1m.Eta(), k1m.Phi(), mKPDG); + + cands1.emplace_back(std::move(cand)); + } + + // ====================================================== + // Build phi candidates from event 2 + // ====================================================== + for (auto const& t2 : tracks2) { + const double kplus2pt = std::hypot(t2.phid1Px(), t2.phid1Py()); + const double kminus2pt = std::hypot(t2.phid2Px(), t2.phid2Py()); + + if (kplus2pt > maxKaonPt || kminus2pt > maxKaonPt) { + continue; + } + + if (!selectionPID(t2.phid1TPC(), t2.phid1TOF(), t2.phid1TOFHit(), + strategyPID1, kplus2pt)) { + continue; + } + + if (!selectionPID(t2.phid2TPC(), t2.phid2TOF(), t2.phid2TOFHit(), + strategyPID2, kminus2pt)) { + continue; + } + + if (t2.phiMass() < minPhiMass2 || t2.phiMass() > maxPhiMass2) { + continue; + } + + TLorentzVector phi2; + TLorentzVector k2p; + TLorentzVector k2m; + + phi2.SetXYZM(t2.phiPx(), t2.phiPy(), t2.phiPz(), t2.phiMass()); + k2p.SetXYZM(t2.phid1Px(), t2.phid1Py(), t2.phid1Pz(), mKPDG); + k2m.SetXYZM(t2.phid2Px(), t2.phid2Py(), t2.phid2Pz(), mKPDG); + + if (phi2.Pt() < minPhiPt || phi2.Pt() > maxPhiPt) { + continue; + } + + PhiCand cand; + cand.phi = ROOT::Math::PtEtaPhiMVector(phi2.Pt(), phi2.Eta(), phi2.Phi(), phi2.M()); + cand.kplus = ROOT::Math::PtEtaPhiMVector(k2p.Pt(), k2p.Eta(), k2p.Phi(), mKPDG); + cand.kminus = ROOT::Math::PtEtaPhiMVector(k2m.Pt(), k2m.Eta(), k2m.Phi(), mKPDG); + + cands2.emplace_back(std::move(cand)); + } + + if (cands1.empty() || cands2.empty()) { + continue; + } + + // ====================================================== + // Build mixed-event phi-phi pairs + // ====================================================== + for (auto const& c1 : cands1) { + + TLorentzVector phi1; + TLorentzVector k1p; + TLorentzVector k1m; + + phi1.SetPtEtaPhiM(c1.phi.Pt(), c1.phi.Eta(), c1.phi.Phi(), c1.phi.M()); + k1p.SetPtEtaPhiM(c1.kplus.Pt(), c1.kplus.Eta(), c1.kplus.Phi(), mKPDG); + k1m.SetPtEtaPhiM(c1.kminus.Pt(), c1.kminus.Eta(), c1.kminus.Phi(), mKPDG); + + for (auto const& c2 : cands2) { + + TLorentzVector phi2; + TLorentzVector k2p; + TLorentzVector k2m; + + phi2.SetPtEtaPhiM(c2.phi.Pt(), c2.phi.Eta(), c2.phi.Phi(), c2.phi.M()); + k2p.SetPtEtaPhiM(c2.kplus.Pt(), c2.kplus.Eta(), c2.kplus.Phi(), mKPDG); + k2m.SetPtEtaPhiM(c2.kminus.Pt(), c2.kminus.Eta(), c2.kminus.Phi(), mKPDG); + + const double dM = getDeltaMPhi(phi1.M(), phi1.Pt(), + phi2.M(), phi2.Pt()); + + TLorentzVector pairPhiPhi = phi1 + phi2; + + if (pairPhiPhi.Pt() < minExoticPt) { + continue; + } + + const double minDR = + minKaonDeltaR(c1.kplus, c1.kminus, c2.kplus, c2.kminus); + + const double dRphi = + deltaR(phi1.Phi(), phi1.Eta(), phi2.Phi(), phi2.Eta()); + + const double denom = std::abs(pairPhiPhi.Pt() - phi1.Pt()); + if (denom < 1e-9) { + continue; + } + + const double ptcorr = phi1.Pt() / denom; + + const double pt1 = phi1.Pt(); + const double pt2 = phi2.Pt(); + const double ptsum = pt1 + pt2; + + if (ptsum <= 0.0) { + continue; + } + + const double z = pt1 / ptsum; + const double A = std::abs(pt1 - pt2) / ptsum; + + histos.fill(HIST("MEMassUnlike"), + pairPhiPhi.M(), + minDR, + pairPhiPhi.Pt(), + dRphi, + dM, + ptcorr); + + histos.fill(HIST("MEMassUnlike_DeltaRZA"), + pairPhiPhi.M(), + pairPhiPhi.Pt(), + pairPhiPhi.Pt() * dRphi, + z, + A, + dM); + + histos.fill(HIST("MEMassUnlike_AllVars"), + pairPhiPhi.M(), + pairPhiPhi.Pt(), + dRphi, + minDR, + phi1.M(), + phi2.M(), + dM, + ptcorr); + } + } + } + } + PROCESS_SWITCH(doublephimeson, processMixedEventopti5, + "Process EventMixing for combinatorial background", false); void processMixedEvent(aod::RedPhiEvents& collisions, aod::PhiTracks& phitracks) { auto tracksTuple = std::make_tuple(phitracks); - BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVertexContributor binningOnPositions{{CfgVtxBins, CfgMultBins}}; SameKindPair pair{ binningOnPositions, nEvtMixing, -1, collisions, tracksTuple, &cache}; @@ -1315,25 +2739,25 @@ struct doublephimeson { if (dM > maxDeltaMPhi) continue; - TLorentzVector pair = phi1 + phi2; - if (pair.M() < minExoticMass || pair.M() > maxExoticMass) + TLorentzVector pairPhiPhi = phi1 + phi2; + if (pairPhiPhi.M() < minExoticMass || pairPhiPhi.M() > maxExoticMass) continue; const double minDR = minKaonDeltaR(c1.kplus, c2.kplus, c1.kminus, c2.kminus); const double dR = deltaR(phi1.Phi(), phi1.Eta(), phi2.Phi(), phi2.Eta()); // same definition as SE - const double ptcorr = (pair.Pt() - phi1.Pt() != 0.) - ? phi1.Pt() / (pair.Pt() - phi1.Pt()) + const double ptcorr = (pairPhiPhi.Pt() - phi1.Pt() != 0.) + ? phi1.Pt() / (pairPhiPhi.Pt() - phi1.Pt()) : 0.; histos.fill(HIST("MEMassUnlike"), - pair.M(), // M(phi-phi) - minDR, // min ΔR among all kaon pairs - pair.Pt(), // pT(phi-phi) - dR, // ΔR(phi1, phi2) - dM, // Δm(phi) - ptcorr); // pT correlation + pairPhiPhi.M(), // M(phi-phi) + minDR, // min ΔR among all kaon pairs + pairPhiPhi.Pt(), // pT(phi-phi) + dR, // ΔR(phi1, phi2) + dM, // Δm(phi) + ptcorr); // pT correlation // --- NEW: compute z and A from phi candidates (no cuts) --- const double pt1 = phi1.Pt(); @@ -1344,7 +2768,7 @@ struct doublephimeson { const double z = pt1 / ptsum; const double A = std::abs(pt1 - pt2) / ptsum; // --- Fill NEW THnSparse (no cuts) --- - histos.fill(HIST("MEMassUnlike_DeltaRZA"), pair.M(), pair.Pt(), pair.Pt() * dR, z, A, dM); + histos.fill(HIST("MEMassUnlike_DeltaRZA"), pairPhiPhi.M(), pairPhiPhi.Pt(), pairPhiPhi.Pt() * dR, z, A, dM); } } } diff --git a/PWGLF/Tasks/Resonances/f0980FlowSP.cxx b/PWGLF/Tasks/Resonances/f0980FlowSP.cxx new file mode 100644 index 00000000000..b868aca99f1 --- /dev/null +++ b/PWGLF/Tasks/Resonances/f0980FlowSP.cxx @@ -0,0 +1,934 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file f0980FlowSP.cxx +/// \brief pi+pi- invariant-mass analysis and f0(980) v2 with scalar-product and event-plane methods + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +namespace +{ +using P4 = ROOT::Math::PxPyPzMVector; +constexpr float InvalidQ = -900.f; +constexpr int ProtonMassNumber = 1; +constexpr int OxygenMassNumber = 16; +} // namespace + +struct F0980FlowSP { + SliceCache cache; + HistogramRegistry registry{"registry"}; + Service ccdb{}; + + enum class CollisionSystem : int { + PP = 0, + OO = 1, + PO = 2, + Unsupported = 3 + }; + + enum class PionPidMode : int { + TPCWithTOFWhenAvailable = 0, + TPCOnly = 1, + TPCTOFRequired = 2, + MomentumDependentTOF = 4 + }; + + enum class EventSelectionStep : int { + All, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoITSROFrameBorder, + ITSTPCVertex, + Centrality, + QVectorCalibrated, + QVectorValid, + Count + }; + + enum class PPEventSelectionStep : int { + All, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoITSROFrameBorder, + ITSTPCVertex, + Inelastic, + QVectorValid, + Count + }; + + enum class QVectorStatus { + IndexUnavailable, + Uncalibrated, + Invalid, + Valid + }; + + struct QVectorData { + double qAx = 0.; + double qAy = 0.; + double qBx = 0.; + double qBy = 0.; + double qCx = 0.; + double qCy = 0.; + double qAMagnitude = 0.; + double qBMagnitude = 0.; + double qCMagnitude = 0.; + + bool hasValidEventPlanes() const + { + return qAMagnitude > 0. && qBMagnitude > 0. && qCMagnitude > 0.; + } + }; + + int cachedRunNumber = -1; + CollisionSystem cachedCollisionSystem = CollisionSystem::Unsupported; + + // Pair and daughter selection. Defaults follow rho770analysis.cxx. + Configurable cfgMinPt{"cfgMinPt", 0.15f, "Minimum daughter transverse momentum"}; + Configurable cfgMaxEta{"cfgMaxEta", 0.8f, "Maximum daughter pseudorapidity"}; + Configurable cfgMinRap{"cfgMinRap", -0.5f, "Minimum pair rapidity"}; + Configurable cfgMaxRap{"cfgMaxRap", 0.5f, "Maximum pair rapidity"}; + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Require primary track"}; + Configurable cfgGlobalWoDCA{"cfgGlobalWoDCA", true, "Require global track without DCA cuts"}; + Configurable cfgPVContributor{"cfgPVContributor", true, "Require PV contributor"}; + Configurable cfgMaxDCAxy{"cfgMaxDCAxy", 0.1f, "Maximum |DCAxy| (cm)"}; + Configurable cfgMaxDCAz{"cfgMaxDCAz", 0.1f, "Maximum |DCAz| (cm)"}; + Configurable cfgMaxTPCChi2NCl{"cfgMaxTPCChi2NCl", 4.f, "Maximum TPC chi2 per cluster"}; + Configurable cfgMaxITSChi2NCl{"cfgMaxITSChi2NCl", 36.f, "Maximum ITS chi2 per cluster"}; + Configurable cfgMinTPCRows{"cfgMinTPCRows", 70, "Minimum TPC crossed rows"}; + Configurable cfgMinITSClusters{"cfgMinITSClusters", 5, "Minimum ITS clusters"}; + + Configurable cfgRequireSel8{"cfgRequireSel8", true, "Require sel8"}; + Configurable cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", true, "Require kNoSameBunchPileup"}; + Configurable cfgRequireNoTimeFrameBorder{"cfgRequireNoTimeFrameBorder", true, "Require kNoTimeFrameBorder"}; + Configurable cfgRequireNoITSROFrameBorder{"cfgRequireNoITSROFrameBorder", true, "Require kNoITSROFrameBorder"}; + Configurable cfgRequireITSTPCVertex{"cfgRequireITSTPCVertex", true, "Require kIsVertexITSTPC"}; + Configurable cfgCollisionSystem{"cfgCollisionSystem", -1, "Collision system: -1 auto from GRPLHCIF, 0 pp, 1 OO, 2 pO"}; + + Configurable cfgPIDMode{"cfgPIDMode", 0, "Pion PID: 0 TPC plus TOF when available, 1 TPC only, 2 require both TPC and TOF, 4 TPC-only below momentum threshold and require TOF above it"}; + Configurable cfgTPCNSigmaPi{"cfgTPCNSigmaPi", 3.f, "TPC pion n-sigma with TOF"}; + Configurable cfgTPCNSigmaPiNoTOF{"cfgTPCNSigmaPiNoTOF", 3.f, "TPC pion n-sigma without TOF"}; + Configurable cfgTOFNSigmaPi{"cfgTOFNSigmaPi", 3.f, "TOF pion n-sigma"}; + Configurable cfgTOFRequiredAboveP{"cfgTOFRequiredAboveP", 0.6f, "For PID mode 4, require TOF at and above this total momentum (GeV/c)"}; + // The vector tables contain one entry per harmonic requested from qVectorsTable. + Configurable cfgHarmonicIndex{"cfgHarmonicIndex", 0, "Index of n=2 in Q-vector vector columns"}; + Configurable cfgCentMin{"cfgCentMin", 0.f, "Minimum FT0C centrality for OO"}; + Configurable cfgCentMax{"cfgCentMax", 100.f, "Maximum FT0C centrality for OO"}; + + ConfigurableAxis axisMass{"axisMass", {400, 0.2, 2.2}, "m_{pi pi} (GeV/c^{2})"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "pair pT (GeV/c)"}; + ConfigurableAxis axisCent{"axisCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 100.}, "FT0C centrality (%)"}; + ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0., 1., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., 105.}, "N_{PV contributors}"}; + ConfigurableAxis axisRun{"axisRun", {2001, 563999.5, 566000.5}, "run number"}; + + using PPCollisions = soa::Join; + using OOCollisions = soa::Join; + using POCollisions = OOCollisions; + using TracksTPC = soa::Join; + using TracksTPCTOF = soa::Join; + + template + CollisionSystem collisionSystem(Collision const& collision) + { + if (cfgCollisionSystem.value >= static_cast(CollisionSystem::PP) && cfgCollisionSystem.value <= static_cast(CollisionSystem::PO)) { + return static_cast(cfgCollisionSystem.value); + } + + const auto bc = collision.template bc_as(); + if (bc.runNumber() == cachedRunNumber) { + return cachedCollisionSystem; + } + + auto* grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", bc.timestamp()); + if (grplhcif == nullptr) { + LOGF(fatal, "Cannot load GLO/Config/GRPLHCIF for run %d and timestamp %" PRIu64, bc.runNumber(), bc.timestamp()); + return CollisionSystem::Unsupported; + } + + const int beamAA = grplhcif->getBeamA(o2::constants::lhc::BeamA); + const int beamCA = grplhcif->getBeamA(o2::constants::lhc::BeamC); + if (beamAA == ProtonMassNumber && beamCA == ProtonMassNumber) { + cachedCollisionSystem = CollisionSystem::PP; + } else if (beamAA == OxygenMassNumber && beamCA == OxygenMassNumber) { + cachedCollisionSystem = CollisionSystem::OO; + } else if ((beamAA == ProtonMassNumber && beamCA == OxygenMassNumber) || + (beamAA == OxygenMassNumber && beamCA == ProtonMassNumber)) { + cachedCollisionSystem = CollisionSystem::PO; + } else { + cachedCollisionSystem = CollisionSystem::Unsupported; + } + cachedRunNumber = bc.runNumber(); + LOGF(info, "Automatically identified collision system for run %d: beam A values %d and %d, mode %d", cachedRunNumber, beamAA, beamCA, static_cast(cachedCollisionSystem)); + return cachedCollisionSystem; + } + + template + QVectorStatus getQVectorData(Collision const& collision, QVectorData& qVector) const + { + const size_t harmonicIndex = cfgHarmonicIndex.value; + if (harmonicIndex >= collision.qvecFT0CReVec().size() || + harmonicIndex >= collision.qvecFT0CImVec().size() || + harmonicIndex >= collision.qvecFT0AReVec().size() || + harmonicIndex >= collision.qvecFT0AImVec().size() || + harmonicIndex >= collision.qvecTPCposReVec().size() || + harmonicIndex >= collision.qvecTPCposImVec().size()) { + return QVectorStatus::IndexUnavailable; + } + if (!collision.isCalibrated()) { + return QVectorStatus::Uncalibrated; + } + + qVector.qAx = collision.qvecFT0CReVec()[harmonicIndex]; + qVector.qAy = collision.qvecFT0CImVec()[harmonicIndex]; + qVector.qBx = collision.qvecFT0AReVec()[harmonicIndex]; + qVector.qBy = collision.qvecFT0AImVec()[harmonicIndex]; + qVector.qCx = collision.qvecTPCposReVec()[harmonicIndex]; + qVector.qCy = collision.qvecTPCposImVec()[harmonicIndex]; + if (qVector.qAx < InvalidQ || qVector.qAy < InvalidQ || + qVector.qBx < InvalidQ || qVector.qBy < InvalidQ || + qVector.qCx < InvalidQ || qVector.qCy < InvalidQ) { + return QVectorStatus::Invalid; + } + + qVector.qAMagnitude = std::hypot(qVector.qAx, qVector.qAy); + qVector.qBMagnitude = std::hypot(qVector.qBx, qVector.qBy); + qVector.qCMagnitude = std::hypot(qVector.qCx, qVector.qCy); + return QVectorStatus::Valid; + } + + void init(InitContext const&) + { + // Counts and weighted SP numerators have identical axes. Their ratio is + // in each (mass, pT, centrality) bin. + registry.add("PP/hMassUS", "pp unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hMassLSpp", "pp ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hMassLSmm", "pp --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;multiplicity", HistType::kTHnSparseF, {axisMass, axisPt, axisMult}); + registry.add("PP/pQAB", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQAC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQBC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQABIm", "Im(qFT0C qFT0A*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQACIm", "Im(qFT0C qTPCpos*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pQBCIm", "Im(qFT0A qTPCpos*);multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiAB", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiAC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/pCosPsiBC", ";multiplicity", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Event/hSelection", "pp event-selection cut flow;selection step;events", HistType::kTH1F, {{static_cast(PPEventSelectionStep::Count), -0.5, static_cast(PPEventSelectionStep::Count) - 0.5}}); + registry.add("PP/QA/Event/hMultiplicityBefore", "pp events before selection;N_{PV contributors};events", HistType::kTH1F, {{106, -0.5, 105.5}}); + registry.add("PP/QA/Event/hMultiplicityAfter", "pp events after selection;N_{PV contributors};events", HistType::kTH1F, {{106, -0.5, 105.5}}); + registry.add("PP/QA/Event/hVertexZBefore", "pp events before selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("PP/QA/Event/hVertexZAfter", "pp events after selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("PP/QA/Qvector/hFT0C", "corrected q_{2,FT0C};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hFT0A", "corrected q_{2,FT0A};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hTPCpos", "corrected q_{2,TPCpos};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("PP/QA/Qvector/hMagnitudeFT0C", "FT0C q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hMagnitudeFT0A", "FT0A q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hMagnitudeTPCpos", "TPCpos q_{2} magnitude;multiplicity;|q_{2}|", HistType::kTH2F, {axisMult, {200, 0., 1.5}}); + registry.add("PP/QA/Qvector/hPsi2FT0C", "FT0C event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hPsi2FT0A", "FT0A event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hPsi2TPCpos", "TPCpos event-plane angle;multiplicity;#Psi_{2}", HistType::kTH2F, {axisMult, {180, -1.5708, 1.5708}}); + registry.add("PP/QA/Qvector/hDotAB", "Re(q_{FT0C}q_{FT0A}^{*});multiplicity;q_{A}#upointq_{B}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/hDotAC", "Re(q_{FT0C}q_{TPCpos}^{*});multiplicity;q_{A}#upointq_{C}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/hDotBC", "Re(q_{FT0A}q_{TPCpos}^{*});multiplicity;q_{B}#upointq_{C}", HistType::kTH2F, {axisMult, {240, -1.2, 1.2}}); + registry.add("PP/QA/Qvector/pQxFT0CVsMult", "mean corrected q_{x,FT0C};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyFT0CVsMult", "mean corrected q_{y,FT0C};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxFT0AVsMult", "mean corrected q_{x,FT0A};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyFT0AVsMult", "mean corrected q_{y,FT0A};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxTPCposVsMult", "mean corrected q_{x,TPCpos};multiplicity;<#it{q}_{x}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQyTPCposVsMult", "mean corrected q_{y,TPCpos};multiplicity;<#it{q}_{y}>", HistType::kTProfile, {axisMult}); + registry.add("PP/QA/Qvector/pQxFT0CVsRun", "mean corrected q_{x,FT0C};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyFT0CVsRun", "mean corrected q_{y,FT0C};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQxFT0AVsRun", "mean corrected q_{x,FT0A};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyFT0AVsRun", "mean corrected q_{y,FT0A};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQxTPCposVsRun", "mean corrected q_{x,TPCpos};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("PP/QA/Qvector/pQyTPCposVsRun", "mean corrected q_{y,TPCpos};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + + registry.add("OO/hMassUS", "OO unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hMassLSpp", "OO ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hMassLSmm", "OO --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("OO/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + + registry.add("OO/pQAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQBC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQABIm", "Im(qFT0C qFT0A*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQACIm", "Im(qFT0C qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pQBCIm", "Im(qFT0A qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("OO/pCosPsiBC", ";centrality", HistType::kTProfile, {axisCent}); + + registry.add("PO/hMassUS", "pO unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hMassLSpp", "pO ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hMassLSmm", "pO --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumUS", "sum Re(u2 qFT0C*), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumLSpp", "sum Re(u2 qFT0C*), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hSpNumLSmm", "sum Re(u2 qFT0C*), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumUS", "sum cos(2(phi-Psi2FT0C)), unlike-sign;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumLSpp", "sum cos(2(phi-Psi2FT0C)), ++;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/hEpNumLSmm", "sum cos(2(phi-Psi2FT0C)), --;mass;pT;centrality", HistType::kTHnSparseF, {axisMass, axisPt, axisCent}); + registry.add("PO/pQAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQBC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQABIm", "Im(qFT0C qFT0A*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQACIm", "Im(qFT0C qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pQBCIm", "Im(qFT0A qTPCpos*);centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiAB", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiAC", ";centrality", HistType::kTProfile, {axisCent}); + registry.add("PO/pCosPsiBC", ";centrality", HistType::kTProfile, {axisCent}); + + registry.add("QA/Event/hSelection", "ion-collision event-selection cut flow;selection step;events", HistType::kTH1F, {{static_cast(EventSelectionStep::Count), -0.5, static_cast(EventSelectionStep::Count) - 0.5}}); + registry.add("QA/Event/hCentrality", "selected ion collisions;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hCentralityBefore", "ion-collision events before selection;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hCentralityAfter", "ion-collision events after selection;FT0C centrality (%);events", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Event/hVertexZBefore", "ion-collision events before selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + registry.add("QA/Event/hVertexZAfter", "ion-collision events after selection;z_{vtx} (cm);events", HistType::kTH1F, {{240, -30., 30.}}); + + registry.add("QA/Qvector/hFT0C", "corrected q_{2,FT0C};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hFT0A", "corrected q_{2,FT0A};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hTPCpos", "corrected q_{2,TPCpos};q_{x};q_{y}", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Qvector/hMagnitudeFT0C", "FT0C q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hMagnitudeFT0A", "FT0A q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hMagnitudeTPCpos", "TPCpos q_{2} magnitude;FT0C centrality (%);|q_{2}|", HistType::kTH2F, {axisCent, {200, 0., 1.5}}); + registry.add("QA/Qvector/hPsi2FT0C", "FT0C event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hPsi2FT0A", "FT0A event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hPsi2TPCpos", "TPCpos event-plane angle;FT0C centrality (%);#Psi_{2}", HistType::kTH2F, {axisCent, {180, -1.5708, 1.5708}}); + registry.add("QA/Qvector/hDotAB", "Re(q_{FT0C}q_{FT0A}^{*});FT0C centrality (%);q_{A}#upointq_{B}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/hDotAC", "Re(q_{FT0C}q_{TPCpos}^{*});FT0C centrality (%);q_{A}#upointq_{C}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/hDotBC", "Re(q_{FT0A}q_{TPCpos}^{*});FT0C centrality (%);q_{B}#upointq_{C}", HistType::kTH2F, {axisCent, {240, -1.2, 1.2}}); + registry.add("QA/Qvector/pQxFT0CVsCent", "mean corrected q_{x,FT0C};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyFT0CVsCent", "mean corrected q_{y,FT0C};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxFT0AVsCent", "mean corrected q_{x,FT0A};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyFT0AVsCent", "mean corrected q_{y,FT0A};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxTPCposVsCent", "mean corrected q_{x,TPCpos};FT0C centrality (%);<#it{q}_{x}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQyTPCposVsCent", "mean corrected q_{y,TPCpos};FT0C centrality (%);<#it{q}_{y}>", HistType::kTProfile, {axisCent}); + registry.add("QA/Qvector/pQxFT0CVsRun", "mean corrected q_{x,FT0C};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyFT0CVsRun", "mean corrected q_{y,FT0C};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQxFT0AVsRun", "mean corrected q_{x,FT0A};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyFT0AVsRun", "mean corrected q_{y,FT0A};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQxTPCposVsRun", "mean corrected q_{x,TPCpos};run number;<#it{q}_{x}>", HistType::kTProfile, {axisRun}); + registry.add("QA/Qvector/pQyTPCposVsRun", "mean corrected q_{y,TPCpos};run number;<#it{q}_{y}>", HistType::kTProfile, {axisRun}); + + auto eventSelection = registry.get(HIST("QA/Event/hSelection")); + constexpr std::array eventSelectionLabels = { + "all", "sel8", "no same-bunch pileup", "no TF border", + "no ITS ROF border", "ITS-TPC vertex", "centrality", "Q calibrated", "Q valid"}; + for (size_t i = 0; i < eventSelectionLabels.size(); ++i) { + eventSelection->GetXaxis()->SetBinLabel(i + 1, eventSelectionLabels[i]); + } + auto ppEventSelection = registry.get(HIST("PP/QA/Event/hSelection")); + constexpr std::array ppEventSelectionLabels = { + "all", "sel8", "no same-bunch pileup", "no TF border", + "no ITS ROF border", "ITS-TPC vertex", "INEL > 0", "Q valid"}; + for (size_t i = 0; i < ppEventSelectionLabels.size(); ++i) { + ppEventSelection->GetXaxis()->SetBinLabel(i + 1, ppEventSelectionLabels[i]); + } + registry.add("QA/TrackSelected/hPt", "track-selected candidates;p_{T} (GeV/c);counts", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/TrackSelected/hEta", "track-selected candidates;#eta;counts", HistType::kTH1F, {{160, -0.8, 0.8}}); + registry.add("QA/TrackSelected/hDCAxy", "track-selected candidates;DCA_{xy} (cm);counts", HistType::kTH1F, {{200, -0.1, 0.1}}); + registry.add("QA/TrackSelected/hDCAz", "track-selected candidates;DCA_{z} (cm);counts", HistType::kTH1F, {{200, -0.1, 0.1}}); + registry.add("QA/TrackSelected/hTPCCrossedRows", "track-selected candidates;TPC crossed rows;counts", HistType::kTH1F, {{165, -0.5, 164.5}}); + registry.add("QA/TrackSelected/hTPCNSigmaPiVsP", "track-selected candidates before pion PID;p (GeV/c);n#sigma_{TPC}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/TrackSelected/hTPCSignalVsP", "track-selected candidates before pion PID;p (GeV/c);TPC dE/dx", HistType::kTH2F, {{200, 0., 20.}, {500, 0., 500.}}); + registry.add("QA/TrackSelected/hTOFNSigmaPiVsP", "track-selected candidates with TOF before pion PID;p (GeV/c);n#sigma_{TOF}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/TrackSelected/hTOFBetaVsP", "track-selected candidates with TOF before pion PID;p (GeV/c);#beta_{TOF}", HistType::kTH2F, {{200, 0., 20.}, {300, 0., 1.5}}); + registry.add("QA/SelectedPions/hTPCNSigmaPiVsP", "selected pion candidates;p (GeV/c);n#sigma_{TPC}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/SelectedPions/hTPCSignalVsP", "selected pion candidates after pion PID;p (GeV/c);TPC dE/dx", HistType::kTH2F, {{200, 0., 20.}, {500, 0., 500.}}); + registry.add("QA/SelectedPions/hTOFNSigmaPiVsP", "selected pion candidates with TOF after pion PID;p (GeV/c);n#sigma_{TOF}^{#pi}", HistType::kTH2F, {{200, 0., 20.}, {120, -6., 6.}}); + registry.add("QA/SelectedPions/hTOFBetaVsP", "selected pion candidates with TOF after pion PID;p (GeV/c);#beta_{TOF}", HistType::kTH2F, {{200, 0., 20.}, {300, 0., 1.5}}); + } + + template + bool selectTrack(T const& track) const + { + if (track.pt() < cfgMinPt.value || std::abs(track.eta()) > cfgMaxEta.value) { + return false; + } + if (std::abs(track.dcaXY()) >= cfgMaxDCAxy.value || std::abs(track.dcaZ()) >= cfgMaxDCAz.value) { + return false; + } + if (cfgPrimaryTrack.value && !track.isPrimaryTrack()) { + return false; + } + if (cfgGlobalWoDCA.value && !track.isGlobalTrackWoDCA()) { + return false; + } + if (cfgPVContributor.value && !track.isPVContributor()) { + return false; + } + return track.tpcChi2NCl() < cfgMaxTPCChi2NCl.value && + track.itsChi2NCl() < cfgMaxITSChi2NCl.value && + track.tpcNClsCrossedRows() > cfgMinTPCRows.value && + track.itsNCls() > cfgMinITSClusters.value; + } + + template + bool selectEvent(C const& collision) const + { + return (!cfgRequireSel8.value || collision.sel8()) && + (!cfgRequireNoSameBunchPileup.value || collision.selection_bit(aod::evsel::kNoSameBunchPileup)) && + (!cfgRequireNoTimeFrameBorder.value || collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) && + (!cfgRequireNoITSROFrameBorder.value || collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) && + (!cfgRequireITSTPCVertex.value || collision.selection_bit(aod::evsel::kIsVertexITSTPC)); + } + + template + bool selectOOEventWithQA(C const& collision, float centrality) + { + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::All)); + registry.fill(HIST("QA/Event/hCentralityBefore"), centrality); + registry.fill(HIST("QA/Event/hVertexZBefore"), collision.posZ()); + if (cfgRequireSel8.value && !collision.sel8()) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::Sel8)); + if (cfgRequireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoSameBunchPileup)); + if (cfgRequireNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoTimeFrameBorder)); + if (cfgRequireNoITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::NoITSROFrameBorder)); + if (cfgRequireITSTPCVertex.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::ITSTPCVertex)); + if (centrality < cfgCentMin.value || centrality >= cfgCentMax.value) { + return false; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::Centrality)); + registry.fill(HIST("QA/Event/hCentrality"), centrality); + registry.fill(HIST("QA/Event/hCentralityAfter"), centrality); + registry.fill(HIST("QA/Event/hVertexZAfter"), collision.posZ()); + return true; + } + + void fillQVectorQA(float centrality, int runNumber, + double qAx, double qAy, + double qBx, double qBy, + double qCx, double qCy) + { + registry.fill(HIST("QA/Qvector/hFT0C"), qAx, qAy); + registry.fill(HIST("QA/Qvector/hFT0A"), qBx, qBy); + registry.fill(HIST("QA/Qvector/hTPCpos"), qCx, qCy); + registry.fill(HIST("QA/Qvector/hMagnitudeFT0C"), centrality, std::hypot(qAx, qAy)); + registry.fill(HIST("QA/Qvector/hMagnitudeFT0A"), centrality, std::hypot(qBx, qBy)); + registry.fill(HIST("QA/Qvector/hMagnitudeTPCpos"), centrality, std::hypot(qCx, qCy)); + registry.fill(HIST("QA/Qvector/hPsi2FT0C"), centrality, 0.5 * std::atan2(qAy, qAx)); + registry.fill(HIST("QA/Qvector/hPsi2FT0A"), centrality, 0.5 * std::atan2(qBy, qBx)); + registry.fill(HIST("QA/Qvector/hPsi2TPCpos"), centrality, 0.5 * std::atan2(qCy, qCx)); + registry.fill(HIST("QA/Qvector/hDotAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("QA/Qvector/hDotAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("QA/Qvector/hDotBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("QA/Qvector/pQxFT0CVsCent"), centrality, qAx); + registry.fill(HIST("QA/Qvector/pQyFT0CVsCent"), centrality, qAy); + registry.fill(HIST("QA/Qvector/pQxFT0AVsCent"), centrality, qBx); + registry.fill(HIST("QA/Qvector/pQyFT0AVsCent"), centrality, qBy); + registry.fill(HIST("QA/Qvector/pQxTPCposVsCent"), centrality, qCx); + registry.fill(HIST("QA/Qvector/pQyTPCposVsCent"), centrality, qCy); + registry.fill(HIST("QA/Qvector/pQxFT0CVsRun"), runNumber, qAx); + registry.fill(HIST("QA/Qvector/pQyFT0CVsRun"), runNumber, qAy); + registry.fill(HIST("QA/Qvector/pQxFT0AVsRun"), runNumber, qBx); + registry.fill(HIST("QA/Qvector/pQyFT0AVsRun"), runNumber, qBy); + registry.fill(HIST("QA/Qvector/pQxTPCposVsRun"), runNumber, qCx); + registry.fill(HIST("QA/Qvector/pQyTPCposVsRun"), runNumber, qCy); + } + + void fillPPQVectorQA(float multiplicity, int runNumber, + double qAx, double qAy, + double qBx, double qBy, + double qCx, double qCy) + { + registry.fill(HIST("PP/QA/Qvector/hFT0C"), qAx, qAy); + registry.fill(HIST("PP/QA/Qvector/hFT0A"), qBx, qBy); + registry.fill(HIST("PP/QA/Qvector/hTPCpos"), qCx, qCy); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeFT0C"), multiplicity, std::hypot(qAx, qAy)); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeFT0A"), multiplicity, std::hypot(qBx, qBy)); + registry.fill(HIST("PP/QA/Qvector/hMagnitudeTPCpos"), multiplicity, std::hypot(qCx, qCy)); + registry.fill(HIST("PP/QA/Qvector/hPsi2FT0C"), multiplicity, 0.5 * std::atan2(qAy, qAx)); + registry.fill(HIST("PP/QA/Qvector/hPsi2FT0A"), multiplicity, 0.5 * std::atan2(qBy, qBx)); + registry.fill(HIST("PP/QA/Qvector/hPsi2TPCpos"), multiplicity, 0.5 * std::atan2(qCy, qCx)); + registry.fill(HIST("PP/QA/Qvector/hDotAB"), multiplicity, qAx * qBx + qAy * qBy); + registry.fill(HIST("PP/QA/Qvector/hDotAC"), multiplicity, qAx * qCx + qAy * qCy); + registry.fill(HIST("PP/QA/Qvector/hDotBC"), multiplicity, qBx * qCx + qBy * qCy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0CVsMult"), multiplicity, qAx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0CVsMult"), multiplicity, qAy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0AVsMult"), multiplicity, qBx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0AVsMult"), multiplicity, qBy); + registry.fill(HIST("PP/QA/Qvector/pQxTPCposVsMult"), multiplicity, qCx); + registry.fill(HIST("PP/QA/Qvector/pQyTPCposVsMult"), multiplicity, qCy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0CVsRun"), runNumber, qAx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0CVsRun"), runNumber, qAy); + registry.fill(HIST("PP/QA/Qvector/pQxFT0AVsRun"), runNumber, qBx); + registry.fill(HIST("PP/QA/Qvector/pQyFT0AVsRun"), runNumber, qBy); + registry.fill(HIST("PP/QA/Qvector/pQxTPCposVsRun"), runNumber, qCx); + registry.fill(HIST("PP/QA/Qvector/pQyTPCposVsRun"), runNumber, qCy); + } + + template + bool selectPion(T const& track) const + { + const float tpc = std::abs(track.tpcNSigmaPi()); + const float tof = std::abs(track.tofNSigmaPi()); + switch (static_cast(cfgPIDMode.value)) { + case PionPidMode::TPCWithTOFWhenAvailable: + if (track.hasTOF()) { + return tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + } + return tpc < cfgTPCNSigmaPiNoTOF.value; + case PionPidMode::TPCOnly: + return tpc < cfgTPCNSigmaPiNoTOF.value; + case PionPidMode::TPCTOFRequired: + return track.hasTOF() && + tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + case PionPidMode::MomentumDependentTOF: + if (track.p() < cfgTOFRequiredAboveP.value) { + return tpc < cfgTPCNSigmaPiNoTOF.value; + } + return track.hasTOF() && + tpc < cfgTPCNSigmaPi.value && + tof < cfgTOFNSigmaPi.value; + default: + return false; + } + } + + template + bool selectPionTPCOnly(T const& track) const + { + return std::abs(track.tpcNSigmaPi()) < cfgTPCNSigmaPiNoTOF.value; + } + + template + void loopPairsSelected(Tracks const& tracks, TrackSelect trackSelect, PionSelect pionSelect, FillPair fillPair) + { + struct SelectedPion { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + int8_t sign = 0; + }; + + // Apply daughter-quality and PID selections once per track before building + // pairs. In OO events this avoids constructing all unfiltered O(N^2) + // combinations and repeatedly evaluating the same track cuts. + std::vector pions; + pions.reserve(tracks.size()); + for (auto const& track : tracks) { + if (!trackSelect(track)) { + continue; + } + registry.fill(HIST("QA/TrackSelected/hPt"), track.pt()); + registry.fill(HIST("QA/TrackSelected/hEta"), track.eta()); + registry.fill(HIST("QA/TrackSelected/hDCAxy"), track.dcaXY()); + registry.fill(HIST("QA/TrackSelected/hDCAz"), track.dcaZ()); + registry.fill(HIST("QA/TrackSelected/hTPCCrossedRows"), track.tpcNClsCrossedRows()); + registry.fill(HIST("QA/TrackSelected/hTPCNSigmaPiVsP"), track.p(), track.tpcNSigmaPi()); + registry.fill(HIST("QA/TrackSelected/hTPCSignalVsP"), track.p(), track.tpcSignal()); + if constexpr (requires { track.tofNSigmaPi(); }) { + if (track.hasTOF()) { + registry.fill(HIST("QA/TrackSelected/hTOFNSigmaPiVsP"), track.p(), track.tofNSigmaPi()); + registry.fill(HIST("QA/TrackSelected/hTOFBetaVsP"), track.p(), track.beta()); + } + } + if (!pionSelect(track)) { + continue; + } + registry.fill(HIST("QA/SelectedPions/hTPCNSigmaPiVsP"), track.p(), track.tpcNSigmaPi()); + registry.fill(HIST("QA/SelectedPions/hTPCSignalVsP"), track.p(), track.tpcSignal()); + if constexpr (requires { track.tofNSigmaPi(); }) { + if (track.hasTOF()) { + registry.fill(HIST("QA/SelectedPions/hTOFNSigmaPiVsP"), track.p(), track.tofNSigmaPi()); + registry.fill(HIST("QA/SelectedPions/hTOFBetaVsP"), track.p(), track.beta()); + } + } + pions.push_back({track.px(), track.py(), track.pz(), static_cast(track.sign())}); + } + + const double mpi = o2::constants::physics::MassPionCharged; + for (size_t i = 0; i < pions.size(); ++i) { + for (size_t j = i + 1; j < pions.size(); ++j) { + auto const& track1 = pions[i]; + auto const& track2 = pions[j]; + P4 pion1(track1.px, track1.py, track1.pz, mpi); + P4 pion2(track2.px, track2.py, track2.pz, mpi); + auto pair = pion1 + pion2; + if (pair.Rapidity() < cfgMinRap.value || pair.Rapidity() > cfgMaxRap.value) { + continue; + } + fillPair(track1.sign, track2.sign, pair); + } + } + } + + template + void loopPairs(Tracks const& tracks, FillPair&& fillPair) + { + loopPairsSelected(tracks, [&](auto const& track) { return selectTrack(track); }, [&](auto const& track) { return selectPion(track); }, std::forward(fillPair)); + } + + void processPP(PPCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::PP) { + return; + } + const float multiplicity = collision.numContrib(); + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::All)); + registry.fill(HIST("PP/QA/Event/hMultiplicityBefore"), multiplicity); + registry.fill(HIST("PP/QA/Event/hVertexZBefore"), collision.posZ()); + if (cfgRequireSel8.value && !collision.sel8()) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::Sel8)); + if (cfgRequireNoSameBunchPileup.value && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoSameBunchPileup)); + if (cfgRequireNoTimeFrameBorder.value && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoTimeFrameBorder)); + if (cfgRequireNoITSROFrameBorder.value && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::NoITSROFrameBorder)); + if (cfgRequireITSTPCVertex.value && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::ITSTPCVertex)); + if (collision.numContrib() < 1) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::Inelastic)); + registry.fill(HIST("PP/QA/Event/hMultiplicityAfter"), multiplicity); + registry.fill(HIST("PP/QA/Event/hVertexZAfter"), collision.posZ()); + + QVectorData qVector; + if (getQVectorData(collision, qVector) != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("PP/QA/Event/hSelection"), static_cast(PPEventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillPPQVectorQA(multiplicity, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + + registry.fill(HIST("PP/pQAB"), multiplicity, qAx * qBx + qAy * qBy); + registry.fill(HIST("PP/pQAC"), multiplicity, qAx * qCx + qAy * qCy); + registry.fill(HIST("PP/pQBC"), multiplicity, qBx * qCx + qBy * qCy); + registry.fill(HIST("PP/pQABIm"), multiplicity, qAy * qBx - qAx * qBy); + registry.fill(HIST("PP/pQACIm"), multiplicity, qAy * qCx - qAx * qCy); + registry.fill(HIST("PP/pQBCIm"), multiplicity, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("PP/pCosPsiAB"), multiplicity, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("PP/pCosPsiAC"), multiplicity, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("PP/pCosPsiBC"), multiplicity, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? sp / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("PP/hMassUS"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumUS"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumUS"), pair.M(), pair.Pt(), multiplicity, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("PP/hMassLSpp"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumLSpp"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumLSpp"), pair.M(), pair.Pt(), multiplicity, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("PP/hMassLSmm"), pair.M(), pair.Pt(), multiplicity); + registry.fill(HIST("PP/hSpNumLSmm"), pair.M(), pair.Pt(), multiplicity, sp); + if (validEventPlanes) { + registry.fill(HIST("PP/hEpNumLSmm"), pair.M(), pair.Pt(), multiplicity, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processPP, "Process pp invariant-mass scalar-product and event-plane analyses", true); + + void processOO(OOCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::OO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("OO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("OO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("OO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("OO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("OO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("OO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("OO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("OO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("OO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? (ux * qAx + uy * qAy) / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("OO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("OO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("OO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processOO, "Process OO invariant-mass scalar-product and event-plane analyses", true); + + void processPO(POCollisions::iterator const& collision, TracksTPCTOF const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::PO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("PO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("PO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("PO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("PO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("PO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("PO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("PO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("PO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("PO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairs(tracks, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? sp / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("PO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("PO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("PO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("PO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("PO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } + }); + } + PROCESS_SWITCH(F0980FlowSP, processPO, "Process pO invariant-mass scalar-product and event-plane analyses", true); + + void processOOTPCOnly(OOCollisions::iterator const& collision, TracksTPC const& tracks, + aod::BCsWithTimestamps const&) + { + if (collisionSystem(collision) != CollisionSystem::OO) { + return; + } + const float centrality = collision.centFT0C(); + if (!selectOOEventWithQA(collision, centrality)) { + return; + } + + QVectorData qVector; + const auto qVectorStatus = getQVectorData(collision, qVector); + if (qVectorStatus == QVectorStatus::IndexUnavailable || qVectorStatus == QVectorStatus::Uncalibrated) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorCalibrated)); + if (qVectorStatus != QVectorStatus::Valid) { + return; + } + registry.fill(HIST("QA/Event/hSelection"), static_cast(EventSelectionStep::QVectorValid)); + + const auto [qAx, qAy, qBx, qBy, qCx, qCy, qAMagnitude, qBMagnitude, qCMagnitude] = qVector; + + const int runNumber = collision.bc_as().runNumber(); + fillQVectorQA(centrality, runNumber, qAx, qAy, qBx, qBy, qCx, qCy); + registry.fill(HIST("OO/pQAB"), centrality, qAx * qBx + qAy * qBy); + registry.fill(HIST("OO/pQAC"), centrality, qAx * qCx + qAy * qCy); + registry.fill(HIST("OO/pQBC"), centrality, qBx * qCx + qBy * qCy); + registry.fill(HIST("OO/pQABIm"), centrality, qAy * qBx - qAx * qBy); + registry.fill(HIST("OO/pQACIm"), centrality, qAy * qCx - qAx * qCy); + registry.fill(HIST("OO/pQBCIm"), centrality, qBy * qCx - qBx * qCy); + const bool validEventPlanes = qVector.hasValidEventPlanes(); + if (validEventPlanes) { + registry.fill(HIST("OO/pCosPsiAB"), centrality, (qAx * qBx + qAy * qBy) / (qAMagnitude * qBMagnitude)); + registry.fill(HIST("OO/pCosPsiAC"), centrality, (qAx * qCx + qAy * qCy) / (qAMagnitude * qCMagnitude)); + registry.fill(HIST("OO/pCosPsiBC"), centrality, (qBx * qCx + qBy * qCy) / (qBMagnitude * qCMagnitude)); + } + + loopPairsSelected(tracks, [&](auto const& track) { return selectTrack(track); }, [&](auto const& track) { return selectPionTPCOnly(track); }, [&](int8_t sign1, int8_t sign2, P4 const& pair) { + const double ux = std::cos(2. * pair.Phi()); + const double uy = std::sin(2. * pair.Phi()); + const double sp = ux * qAx + uy * qAy; + const double ep = validEventPlanes ? (ux * qAx + uy * qAy) / qAMagnitude : 0.; + if (sign1 * sign2 < 0) { + registry.fill(HIST("OO/hMassUS"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumUS"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumUS"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 > 0 && sign2 > 0) { + registry.fill(HIST("OO/hMassLSpp"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSpp"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSpp"), pair.M(), pair.Pt(), centrality, ep); + } + } else if (sign1 < 0 && sign2 < 0) { + registry.fill(HIST("OO/hMassLSmm"), pair.M(), pair.Pt(), centrality); + registry.fill(HIST("OO/hSpNumLSmm"), pair.M(), pair.Pt(), centrality, sp); + if (validEventPlanes) { + registry.fill(HIST("OO/hEpNumLSmm"), pair.M(), pair.Pt(), centrality, ep); + } + } }); + } + PROCESS_SWITCH(F0980FlowSP, processOOTPCOnly, "Process OO with TPC-only pion PID", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/f0980analysis.cxx b/PWGLF/Tasks/Resonances/f0980analysis.cxx index 257e8a464fc..7efd84760e3 100644 --- a/PWGLF/Tasks/Resonances/f0980analysis.cxx +++ b/PWGLF/Tasks/Resonances/f0980analysis.cxx @@ -284,7 +284,7 @@ struct f0980analysis { } template - bool selPion(const TrackType track) + bool selPion(const TrackType& track) { switch (selectType) { case 0: diff --git a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx index bb40ed8b46e..6166c39a314 100644 --- a/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx +++ b/PWGLF/Tasks/Resonances/f0980pbpbanalysis.cxx @@ -17,6 +17,7 @@ #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseITS.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/Qvectors.h" @@ -38,6 +39,7 @@ #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include @@ -68,8 +70,9 @@ struct F0980pbpbanalysis { {}, OutputObjHandlingPolicy::AnalysisObject}; - Service ccdb; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; + o2::aod::ITSResponse itsResponse; Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; @@ -130,6 +133,8 @@ struct F0980pbpbanalysis { Configurable cfgPIDMaxTPCnSigmaPion{"cfgPIDMaxTPCnSigmaPion", 5.0, "TPC nSigma cut for Pion"}; // TPC Configurable cfgPIDMaxTPCnSigmaPionS{"cfgPIDMaxTPCnSigmaPionS", 3.0, "TPC nSigma cut for Pion as a standalone"}; Configurable cfgPIDMaxTiednSigmaPion{"cfgPIDMaxTiednSigmaPion", 3.0, "Combined nSigma cut for Pion"}; + Configurable cfgPIDITSFlag{"cfgPIDITSFlag", true, "PID ITS flag"}; + Configurable cfgPIDMaxITSnSigmaPion{"cfgPIDMaxITSnSigmaPion", 3.0, "ITS nSigma selection"}; } PIDConfig; // Flow Configurables @@ -198,27 +203,27 @@ struct F0980pbpbanalysis { int nmode = FlowConfig.cfgQvecNMods; static constexpr double QvecAmpMin = 1e-4; - int detId; - int refAId; - int refBId; + int detId = 0; + int refAId = 4; + int refBId = 5; - int qVecDetInd; - int qVecRefAInd; - int qVecRefBInd; + int qVecDetInd = 0; + int qVecRefAInd = 0; + int qVecRefBInd = 0; - double eventPlaneDet; - double eventPlaneRefA; - double eventPlaneRefB; + double eventPlaneDet = 0.; + double eventPlaneRefA = 0.; + double eventPlaneRefB = 0.; - float centrality; + float centrality = 1.; - double angle; - double relPhi; - double relPhiRot; - double relPhiMix; + double angle = -1.; + double relPhi = -1.; + double relPhiRot = -1.; + double relPhiMix = -1.; // double massPi = o2::constants::physics::MassPionCharged; - double massPtl; + double massPtl = 0.; int nTotalEvents = 0; @@ -231,6 +236,7 @@ struct F0980pbpbanalysis { PIDRun3 = 0, PIDRun2 = 1, PIDTest = 2, + PIDAddITS = 3, }; enum PtlList { @@ -262,19 +268,23 @@ struct F0980pbpbanalysis { { if (name.value == "FT0C") { return 0; - } else if (name.value == "FT0A") { + } + if (name.value == "FT0A") { return 1; - } else if (name.value == "FT0M") { + } + if (name.value == "FT0M") { return 2; - } else if (name.value == "FV0A") { + } + if (name.value == "FV0A") { return 3; - } else if (name.value == "TPCpos") { + } + if (name.value == "TPCpos") { return 4; - } else if (name.value == "TPCneg") { + } + if (name.value == "TPCneg") { return 5; - } else { - return 0; } + return 0; } template @@ -361,7 +371,7 @@ struct F0980pbpbanalysis { // Event selection template - bool eventSelected(TCollision collision, const bool QA) + bool eventSelected(const TCollision& collision, const bool QA) { if (QAConfig.cfgQAEventCut && QA) fillQA(false, collision, 1); @@ -372,45 +382,45 @@ struct F0980pbpbanalysis { if (std::abs(collision.posZ()) > EventConfig.cfgEventCutVertex) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 1); // Vertex cut - + } if (!collision.sel8()) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 2); // sel8 cut - + } if (EventConfig.cfgEventGoodZvtxSel && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 3); // Good Zvtx cut - + } if (EventConfig.cfgEventNSamePileupSel && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 4); // Same bunch pileup cut - + } if (EventConfig.cfgEventNCollinTRSel && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 5); // Collision time range standard cut - + } if (EventConfig.cfgEventQvecSel && (collision.qvecAmp()[detId] < QvecAmpMin || collision.qvecAmp()[refAId] < QvecAmpMin || collision.qvecAmp()[refBId] < QvecAmpMin)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 6); // Qvector cut - + } if (EventConfig.cfgEventOccupancySel && (collision.trackOccupancyInTimeRange() > EventConfig.cfgEventOccupancyMax || collision.trackOccupancyInTimeRange() < EventConfig.cfgEventOccupancyMin)) { return 0; } - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 7); // Occupancy cut - + } if (EventConfig.cfgEventCentMax < centrality) { return 0; } @@ -423,9 +433,9 @@ struct F0980pbpbanalysis { return 0; } */ - if (QAConfig.cfgQAEventFlowCut && QA) + if (QAConfig.cfgQAEventFlowCut && QA) { histos.fill(HIST("EventQA/hnEvents"), 8); // Centrality cut - + } if (EventConfig.cfgEventPVSel && std::abs(collision.posZ()) > EventConfig.cfgEventPV) { return 0; } @@ -438,29 +448,33 @@ struct F0980pbpbanalysis { // Track selection template - bool trackSelected(const TrackType track, const bool QA) + bool trackSelected(const TrackType& track, const bool QA) { - if (QAConfig.cfgQATrackCut && QA) + if (QAConfig.cfgQATrackCut && QA) { fillQA(false, track, 3); - if (QAConfig.cfgQATrackFlowCut && QA) + } + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 0); // All passed // // Kinematic + } if (std::abs(track.pt()) < TrackConfig.cfgTrackPtMin) { return 0; } if (std::abs(track.eta()) > TrackConfig.cfgTrackEtaMax) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 1); // Kinematic // Primary Vertex(PV) Contributing to the PV fit + } if (TrackConfig.cfgTrackIsPVContributor && !track.isPVContributor()) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 2); // PV Contributor // Global Track without DCA + } if (TrackConfig.cfgTrackIsGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return 0; } @@ -476,9 +490,10 @@ struct F0980pbpbanalysis { if (TrackConfig.cfgTrackITSChi2 > 0 && track.itsChi2NCl() > TrackConfig.cfgTrackITSChi2) { return 0; } - if (QAConfig.cfgQATrackFlowCut && QA) + if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 4); // GlobalWoDCATrack // DCA cuts + } if (TrackConfig.cfgTrackDCArDepPTSel) { if (std::abs(track.dcaXY()) > (TrackConfig.cfgTrackDCArDepPTP0 + (TrackConfig.cfgTrackDCArDepPTExp / track.pt()))) { return 0; @@ -519,7 +534,7 @@ struct F0980pbpbanalysis { // PID selection template - bool selectionPID(const TrackType track, const bool QA) + bool selectionPID(const TrackType& track, const bool QA) { if (QA) fillQA(false, track, 4); @@ -571,6 +586,16 @@ struct F0980pbpbanalysis { return 0; } } + } else if (cfgListPID == PIDList::PIDAddITS) { + if (!track.hasTOF()) { + return 0; + } + if ((getTpcNSigma(track) * getTpcNSigma(track) + getTofNSigma(track) * getTofNSigma(track)) > (PIDConfig.cfgPIDMaxTiednSigmaPion * PIDConfig.cfgPIDMaxTiednSigmaPion)) { + return 0; + } + if (!PIDConfig.cfgPIDITSFlag || std::abs(itsResponse.nSigmaITS(track)) > PIDConfig.cfgPIDMaxITSnSigmaPion) { + return 0; + } } if (QAConfig.cfgQATrackFlowCut && QA) { histos.fill(HIST("TrackQA/hnTracks"), 8); // PID @@ -580,40 +605,45 @@ struct F0980pbpbanalysis { } // PID selection template - bool pairAngleSelection(const TrackType1 track1, const TrackType2 track2) + bool pairAngleSelection(const TrackType1& track1, const TrackType2& track2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; + double pt1 = 0.; + double pt2 = 0.; + double pz1 = 0.; + double pz2 = 0.; + double p1 = 0.; + double p2 = 0.; + double angle12 = 0.; + pt1 = track1.pt(); pt2 = track2.pt(); pz1 = track1.pz(); pz2 = track2.pz(); p1 = track1.p(); p2 = track2.p(); - angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - if (PhiTestConfig.cfgPhiDeepAngleSel && angle < PhiTestConfig.cfgPhiDeepAngle) { + angle12 = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); + if (PhiTestConfig.cfgPhiDeepAngleSel && angle12 < PhiTestConfig.cfgPhiDeepAngle) { return 0; } return 1; } template - float getTpcNSigma(const TrackType track) + float getTpcNSigma(const TrackType& track) { if (cfgListPtl == PtlList::PtlPion) { return track.tpcNSigmaPi(); - } else { - return track.tpcNSigmaKa(); } + return track.tpcNSigmaKa(); } template - float getTofNSigma(const TrackType track) + float getTofNSigma(const TrackType& track) { if (cfgListPtl == PtlList::PtlPion) { return track.tofNSigmaPi(); - } else { - return track.tofNSigmaKa(); } + return track.tofNSigmaKa(); } template @@ -634,9 +664,9 @@ struct F0980pbpbanalysis { if (!trackSelected(trk1, true)) { continue; } - if (QAConfig.cfgQATrackCut) + if (QAConfig.cfgQATrackCut) { fillQA(true, trk1, 3); - + } if (!selectionPID(trk1, true)) { continue; } @@ -706,7 +736,7 @@ struct F0980pbpbanalysis { qVecDetInd = detId * 4 + 3 + (nmode - 2) * FlowConfig.cfgQvecNum * 4; auto trackTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{mixAxisVertex, mixAxisCent}, true}; + BinningTypeVertexContributor binningOnPositions{{mixAxisVertex, mixAxisCent}}; SameKindPair pair{binningOnPositions, BkgMethodConfig.cfgBkgMixedNum, -1, collisions, trackTuple, &cache}; ROOT::Math::PxPyPzMVector ptl1, ptl2, recoPtl; for (const auto& [c1, t1, c2, t2] : pair) { @@ -721,7 +751,7 @@ struct F0980pbpbanalysis { if (c1.bcId() == c2.bcId()) { continue; } - double eventPlaneDet = std::atan2(c1.qvecIm()[qVecDetInd], c1.qvecRe()[qVecDetInd]) / static_cast(nmode); + eventPlaneDet = std::atan2(c1.qvecIm()[qVecDetInd], c1.qvecRe()[qVecDetInd]) / static_cast(nmode); for (const auto& trk1 : t1) { if (!trackSelected(trk1, false)) { diff --git a/PWGLF/Tasks/Resonances/f0phiproxy.cxx b/PWGLF/Tasks/Resonances/f0phiproxy.cxx new file mode 100644 index 00000000000..b83cdee0f7c --- /dev/null +++ b/PWGLF/Tasks/Resonances/f0phiproxy.cxx @@ -0,0 +1,690 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file f0phiproxy.cxx +/// \brief Dipion-based half-momentum kaon proxies with kaon/phi and pion/K*0 bachelors, in data and MC. +/// \author Sushanta Tripathy + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct F0phiproxy { + using Collisions = soa::Join; + using Tracks = soa::Join; + using CollisionsMC = soa::Join; + using TracksMC = soa::Join; + using FourVector = ROOT::Math::PxPyPzMVector; + Preslice perCollision = aod::track::collisionId; + Preslice perCollisionMC = aod::track::collisionId; + Service pdgDB{}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable vertexMax{"vertexMax", 10.f, "Maximum absolute vertex z (cm)"}; + Configurable requireSel8{"requireSel8", true, "Require sel8"}; + Configurable requireINELgt0{"requireINELgt0", true, "Require at least one PV track in |eta|<1; MC gen uses charged physical primaries"}; + Configurable trackPtMin{"trackPtMin", 0.15f, "Minimum track pT (GeV/c)"}; + Configurable trackEtaMax{"trackEtaMax", 0.8f, "Maximum track |eta|"}; + Configurable requireGlobalTrack{"requireGlobalTrack", true, "Require global track selection"}; + Configurable requirePVContributor{"requirePVContributor", true, "Require PV contributor"}; + Configurable pionTPC{"pionTPC", 3.f, "Pion TPC nSigma cut"}; + Configurable pionTOF{"pionTOF", 3.f, "Pion TOF veto when matched"}; + Configurable kaonTPC{"kaonTPC", 3.f, "Kaon TPC nSigma cut"}; + Configurable kaonTOF{"kaonTOF", 3.f, "Kaon TOF veto when matched"}; + Configurable requireTOF{"requireTOF", false, "Require TOF for both species instead of veto-only"}; + Configurable dipionYMax{"dipionYMax", 0.5f, "Maximum dipion |y|"}; + Configurable proxyYMax{"proxyYMax", 0.5f, "Maximum proxy+bachelor and standard-control |y|"}; + Configurable dipionMassMin{"dipionMassMin", 0.6f, "Lower dipion mass for proxy construction"}; + Configurable dipionMassMax{"dipionMassMax", 1.4f, "Upper dipion mass for proxy construction"}; + Configurable mixDepth{"mixDepth", 5, "Previous events per mixing bin; zero disables ME booking and mixing"}; + Configurable mixZWidth{"mixZWidth", 2.f, "Vertex bin width (cm)"}; + Configurable mixMultWidth{"mixMultWidth", 20.f, "PV multiplicity bin width"}; + + Configurable enablePhiProxy{"enablePhiProxy", true, "Dipion/2 as on-shell K plus bachelor K"}; + Configurable enableKstarProxy{"enableKstarProxy", false, "Dipion/2 as on-shell K plus bachelor pion; use the dedicated Kstar configuration"}; + Configurable enablePhiControls{"enablePhiControls", true, "Standard KK SE/ME OS/LS spectra"}; + Configurable enableKstarControls{"enableKstarControls", false, "Standard K pi SE/ME OS/LS spectra"}; + Configurable enableCompactProxy{"enableCompactProxy", true, "Proxy maps retaining mass, pT, dipion mass, grouped multiplicity, charges and TOF"}; + Configurable enableFullProxySparse{"enableFullProxySparse", false, "Diagnostic 7D maps including multiplicity, bachelor charge and TOF; expensive to merge"}; + Configurable enablePidQA{"enablePidQA", false, "Book/fill the four PID QA histograms"}; + Configurable multiplicityBins{"multiplicityBins", 100, "Multiplicity bins in dipion/control and full diagnostic maps; identical settings required for merging"}; + Configurable proxyDipionMassBins{"proxyDipionMassBins", 300, "Dipion mass bins in compact proxy maps; default preserves original 5 MeV resolution"}; + ConfigurableAxis proxyMultiplicityAxis{"proxyMultiplicityAxis", {0., 0., 20., 50., 200., 1000000.}, "Compact proxy NPV intervals; first zero selects variable-width bins"}; + ConfigurableAxis proxyPtAxis{"proxyPtAxis", {0., 0., 0.3, 1., 2., 3., 4., 6., 10.}, "Compact proxy pT intervals covering both the data and Kstar-study selections"}; + Configurable kstarMassBins{"kstarMassBins", 200, "K pi and Kstar-proxy mass bins over 0.6-1.4 GeV; 200 gives the study's 4 MeV bins"}; + Configurable mcPrimaryOnly{"mcPrimaryOnly", true, "Generated daughters must be physical primaries; strong-decay daughters are included"}; + Configurable mcF0PdgCode{"mcF0PdgCode", 0, "0 resolves f0_980 from the O2 PDG service; override only for a verified generator convention"}; + + static constexpr size_t Phi = 0; + static constexpr size_t Kstar = 1; + static constexpr size_t NChannels = 2; + static constexpr size_t NCharges = 3; + static constexpr size_t NMixClasses = 2; + static constexpr size_t NTruthClasses = 4; + static constexpr double ProxyMomentumScale = 0.5; + static constexpr int PdgPion = static_cast(PDG_t::kPiPlus); + static constexpr int PdgKaon = static_cast(PDG_t::kKPlus); + static constexpr int PdgPhi = static_cast(constants::physics::Pdg::kPhi); + static constexpr int PdgKstar = static_cast(constants::physics::Pdg::kK0Star892); + static constexpr size_t TwoBodyDaughters = 2; + static constexpr int IonCodeThreshold = 1000000000; + static constexpr int IonZDivisor = 10000; + static constexpr int IonZModulo = 1000; + int resolvedF0Code = 0; + + struct TruthInfo { + int64_t id = -1; + int collisionId = -1; + int pdg = 0; + double px = 0., py = 0., pz = 0.; + bool primary = false; + std::vector> parents{}; + }; + struct TrackCandidate { + int64_t id = -1; + int sign = 0; + double px = 0., py = 0., pz = 0.; + bool hasTOF = false; + TruthInfo truth{}; + [[nodiscard]] FourVector vector(double mass) const { return {px, py, pz, mass}; } + }; + struct Event { + std::vector pions{}, kaons{}; + }; + + std::array, NMixClasses> dipionSpectra{}; + std::array, NMixClasses>, NChannels> compactProxy{}, fullProxy{}; + std::array, NCharges>, NMixClasses>, NChannels> controls{}; + std::array, NTruthClasses>, NChannels> recoComponents{}; + std::array, NChannels> recoTrueParent{}, recoPtResponse{}, proxyPtResponse{}; + std::shared_ptr recoTrueF0{}; + enum GenSpectrum : size_t { GenDipion, + GenF0, + GenPhi, + GenPhiTrue, + GenKstar, + GenKstarTrue, + GenProxyPhi, + GenProxyPhiF0, + GenProxyKstar, + GenProxyKstarF0, + NGenSpectra }; + std::array, NGenSpectra> generated{}; + + bool mixingEnabled() const { return mixDepth.value > 0; } + bool channelEnabled(size_t channel) const { return channel == Phi ? enablePhiProxy.value : enableKstarProxy.value; } + bool controlEnabled(size_t channel) const { return channel == Phi ? enablePhiControls.value : enableKstarControls.value; } + static double bachelorMass(size_t channel) { return channel == Phi ? constants::physics::MassKaonCharged : constants::physics::MassPionCharged; } + static int bachelorPdg(size_t channel) { return channel == Phi ? PdgKaon : PdgPion; } + + void init(InitContext const&) + { + if (doprocess && doprocessMCReco) { + throw std::runtime_error("Enable either process (data) or processMCReco, never both"); + } + if (mixDepth.value < 0 || mixZWidth.value <= 0.f || mixMultWidth.value <= 0.f || + vertexMax.value <= 0.f || trackPtMin.value < 0.f || trackEtaMax.value <= 0.f || + pionTPC.value <= 0.f || pionTOF.value <= 0.f || kaonTPC.value <= 0.f || kaonTOF.value <= 0.f || + dipionMassMin.value >= dipionMassMax.value || dipionYMax.value <= 0.f || proxyYMax.value <= 0.f || + multiplicityBins.value <= 0 || proxyDipionMassBins.value <= 0 || kstarMassBins.value <= 0 || mcF0PdgCode.value < 0) { + throw std::runtime_error("Invalid F0PhiProxy configuration"); + } + if (doprocess || doprocessMCReco) { + if ((enablePhiProxy.value || enableKstarProxy.value) && !enableCompactProxy.value && !enableFullProxySparse.value) { + throw std::runtime_error("Enable compact or full proxy output when a proxy channel is enabled"); + } + bookReco(); + } + if (doprocessMCReco || doprocessMCGen) { + resolvedF0Code = mcF0PdgCode.value; + if (resolvedF0Code == 0) { + auto const* particle = pdgDB->GetParticle("f0_980"); + if (!particle) { + throw std::runtime_error("f0_980 not found in O2 PDG service; configure mcF0PdgCode explicitly"); + } + resolvedF0Code = particle->PdgCode(); + } + bookMC(); + } + } + + void bookReco() + { + const AxisSpec massPi{300, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; + const AxisSpec compactMassPi{proxyDipionMassBins.value, 0.2, 1.7, "m_{#pi#pi} (GeV/c^{2})"}; + const AxisSpec pt{100, 0., 10., "p_{T} (GeV/c)"}; + const AxisSpec mult{multiplicityBins.value, 0., 200., "N_{PV}(|#eta|<1)"}; + const AxisSpec compactMult{proxyMultiplicityAxis, "N_{PV}(|#eta|<1), grouped"}; + const AxisSpec compactPt{proxyPtAxis, "proxy pair p_{T} (GeV/c), grouped"}; + const AxisSpec charge{3, -0.5, 2.5, "dipion: 0=unlike, 1=++, 2=--"}; + const AxisSpec bachelorCharge{2, -1.5, 1.5, "bachelor charge"}; + const AxisSpec tof{2, -0.5, 1.5, "bachelor has TOF"}; + histos.add("hEventSelection", "Received; selected", HistType::kTH1D, {{2, 0.5, 2.5}}); + if (mixingEnabled()) { + histos.add("hMixPartners", "Mixing partners;N", HistType::kTH1D, {{101, -0.5, 100.5}}); + } + if (enablePidQA.value) { + histos.add("hPionTPC", "Quality-selected tracks;pT;nSigmaTPC pion", HistType::kTH2F, {pt, {100, -10., 10.}}); + histos.add("hPionTOF", "TOF-matched quality tracks;pT;nSigmaTOF pion", HistType::kTH2F, {pt, {100, -10., 10.}}); + histos.add("hKaonTPC", "Quality-selected tracks;pT;nSigmaTPC kaon", HistType::kTH2F, {pt, {100, -10., 10.}}); + histos.add("hKaonTOF", "TOF-matched quality tracks;pT;nSigmaTOF kaon", HistType::kTH2F, {pt, {100, -10., 10.}}); + } + const std::array dipionNames{"hDipionSE", "hDipionME"}; + for (size_t mix = 0; mix < dipionNames.size(); ++mix) { + if (mix != 0 && !mixingEnabled()) { + continue; + } + dipionSpectra[mix] = histos.add(dipionNames[mix], "Dipion SE or raw ME", HistType::kTHnSparseF, {massPi, pt, mult, charge}); + } + const std::array, NChannels> compactNames{{{"hProxyCompactSE", "hProxyCompactME"}, {"hKstarProxyCompactSE", "hKstarProxyCompactME"}}}; + const std::array, NChannels> fullNames{{{"hProxySE", "hProxyME"}, {"hKstarProxySE", "hKstarProxyME"}}}; + const std::array, NMixClasses>, NChannels> controlNames{{{{{"hPhiKK", "hPhiKKLikePP", "hPhiKKLikeMM"}, {"hPhiKKMixed", "hPhiKKMixedLikePP", "hPhiKKMixedLikeMM"}}}, + {{{"hKstarKPi", "hKstarKPiLikePP", "hKstarKPiLikeMM"}, {"hKstarKPiMixed", "hKstarKPiMixedLikePP", "hKstarKPiMixedLikeMM"}}}}}; + for (size_t channel = 0; channel < NChannels; ++channel) { + const AxisSpec mass = channel == Phi ? AxisSpec{600, 0.9, 1.5, "m_{K,proxy K} (GeV/c^{2})"} : AxisSpec{kstarMassBins.value, 0.6, 1.4, "m_{K,proxy #pi} (GeV/c^{2})"}; + for (size_t mix = 0; mix < NMixClasses; ++mix) { + if (mix != 0 && !mixingEnabled()) { + continue; + } + if (channelEnabled(channel)) { + // The measured dipion mass is retained; a mass window is not an f0 tag. + if (enableCompactProxy.value) { + compactProxy[channel][mix] = histos.add(compactNames[channel][mix], "Grouped-pT/multiplicity proxy; no constituent charge is measured", HistType::kTHnSparseF, {mass, compactPt, compactMassPi, compactMult, charge, bachelorCharge, tof}); + } + if (enableFullProxySparse.value) { + fullProxy[channel][mix] = histos.add(fullNames[channel][mix], "Diagnostic full proxy", HistType::kTHnSparseF, {mass, pt, massPi, mult, charge, bachelorCharge, tof}); + } + } + if (controlEnabled(channel)) { + for (size_t sign = 0; sign < NCharges; ++sign) { + controls[channel][mix][sign] = histos.add(controlNames[channel][mix][sign], "Standard control SE or raw ME", HistType::kTHnSparseF, {mass, pt, mult}); + } + } + } + } + } + + void bookMC() + { + const AxisSpec pt{100, 0., 10., "pair pT (GeV/c)"}; + const AxisSpec pionMass{300, 0.2, 1.7, "dipion mass (GeV/c2)"}; + const std::array mass{AxisSpec{600, 0.9, 1.5, "KK mass (GeV/c2)"}, AxisSpec{kstarMassBins.value, 0.6, 1.4, "K pi mass (GeV/c2)"}}; + if (doprocessMCGen) { + histos.add("hMCGenEventSelection", "Generated events;1=all,2=vertex,3=truth INEL>0", HistType::kTH1D, {{3, 0.5, 3.5}}); + const std::array names{"hMCGenDipion", "hMCGenTrueF0", "hMCGenPhiKK", "hMCGenTruePhi", "hMCGenKstarKPi", "hMCGenTrueKstar", "hMCGenProxyPhi", "hMCGenProxyPhiTrueF0", "hMCGenProxyKstar", "hMCGenProxyKstarTrueF0"}; + for (size_t i = 0; i < names.size(); ++i) { + const AxisSpec axis = i <= GenF0 ? pionMass : (i == GenKstar || i == GenKstarTrue || i >= GenProxyKstar ? mass[Kstar] : mass[Phi]); + generated[i] = histos.add(names[i], "Generated unweighted fiducial spectrum", HistType::kTH2D, {axis, pt}, true); + } + } + if (doprocessMCReco) { + recoTrueF0 = histos.add("hMCRecoTrueF0", "Selected common-parent f0 -> pi pi", HistType::kTH2D, {pionMass, pt}, true); + const std::array channelNames{"Phi", "Kstar"}; + const std::array classNames{"TrueF0", "Combinatorial", "MisID", "Unmatched"}; + for (size_t channel = 0; channel < NChannels; ++channel) { + if (channelEnabled(channel)) { + for (size_t category = 0; category < classNames.size(); ++category) { + const std::string name = std::string("hMCRecoProxy") + channelNames[channel] + classNames[category]; + recoComponents[channel][category] = histos.add(name.c_str(), "Exclusive proxy truth component; not a coalescence ancestry tag", HistType::kTH2D, {mass[channel], pt}, true); + } + const std::string name = std::string("hMCRecoProxy") + channelNames[channel] + "PtResponse"; + proxyPtResponse[channel] = histos.add(name.c_str(), "Primary true f0 daughters and true bachelor;generated proxy pair pT;reconstructed proxy pair pT", HistType::kTH2D, {pt, pt}, true); + } + if (controlEnabled(channel)) { + const std::string truthName = std::string("hMCRecoTrue") + channelNames[channel]; + const std::string responseName = std::string("hMCReco") + channelNames[channel] + "PtResponse"; + recoTrueParent[channel] = histos.add(truthName.c_str(), "Selected exact common-parent standard control", HistType::kTH2D, {mass[channel], pt}, true); + recoPtResponse[channel] = histos.add(responseName.c_str(), "Selected standard control;generated pair pT;reconstructed pair pT", HistType::kTH2D, {pt, pt}, true); + } + } + } + } + + static void fillSparse(std::shared_ptr const& histogram, std::initializer_list coordinates) + { + if (histogram) { + histogram->Fill(coordinates.begin()); + } + } + + bool passPID(float tpc, float tof, bool matched, float tpcCut, float tofCut) const + { + return std::isfinite(tpc) && std::abs(tpc) < tpcCut && (!requireTOF.value || matched) && + (!matched || (std::isfinite(tof) && std::abs(tof) < tofCut)); + } + static int chargeCategory(int a, int b) { return a != b ? 0 : (a > 0 ? 1 : 2); } + static FourVector constituent(FourVector const& dipion) + { + return {ProxyMomentumScale * dipion.Px(), ProxyMomentumScale * dipion.Py(), ProxyMomentumScale * dipion.Pz(), constants::physics::MassKaonCharged}; + } + int chargeFromPdg(int code) const + { + const int absolute = std::abs(code); + if (absolute >= IonCodeThreshold) { + const int z = (absolute / IonZDivisor) % IonZModulo; + return code > 0 ? z : -z; + } + if (auto const* particle = pdgDB->GetParticle(code)) { + return static_cast(std::lround(particle->Charge() / 3.)); + } + return 0; + } + template + TruthInfo particleTruth(TParticle const& particle) const + { + TruthInfo truth{.id = particle.globalIndex(), .collisionId = particle.mcCollisionId(), .pdg = particle.pdgCode(), .px = particle.px(), .py = particle.py(), .pz = particle.pz(), .primary = particle.isPhysicalPrimary(), .parents = {}}; + for (auto const& mother : particle.template mothers_as()) { + const int code = std::abs(mother.pdgCode()); + if (mother.mcCollisionId() == truth.collisionId && (code == resolvedF0Code || code == PdgPhi || code == PdgKstar)) { + // The complementary-channel study uses exact two-body parent tags. + if (mother.template daughters_as().size() == TwoBodyDaughters) { + truth.parents.emplace_back(mother.globalIndex(), code); + } + } + } + return truth; + } + template + TruthInfo trackTruth(TTrack const& track, int collisionId) const + { + if (collisionId < 0 || !track.has_mcParticle()) { + return {}; + } + auto particle = track.template mcParticle_as(); + return particle.mcCollisionId() == collisionId ? particleTruth(particle) : TruthInfo{}; + } + static bool commonParent(TruthInfo const& first, TruthInfo const& second, int parentCode) + { + if (first.id < 0 || second.id < 0 || first.id == second.id || first.collisionId != second.collisionId) { + return false; + } + return std::any_of(first.parents.begin(), first.parents.end(), [&second, parentCode](auto const& parent) { + return parent.second == parentCode && std::find(second.parents.begin(), second.parents.end(), parent) != second.parents.end(); + }); + } + static bool identified(TrackCandidate const& candidate, int positiveCode) + { + return candidate.truth.id >= 0 && candidate.truth.pdg == candidate.sign * positiveCode; + } + bool trueF0(TrackCandidate const& first, TrackCandidate const& second) const + { + return first.sign != second.sign && identified(first, PdgPion) && identified(second, PdgPion) && commonParent(first.truth, second.truth, resolvedF0Code); + } + static double truthPairPt(TruthInfo const& first, TruthInfo const& second) + { + return std::hypot(first.px + second.px, first.py + second.py); + } + size_t proxyTruthClass(TrackCandidate const& first, TrackCandidate const& second, TrackCandidate const& bachelor, size_t channel) const + { + if (first.truth.id < 0 || second.truth.id < 0 || bachelor.truth.id < 0 || + first.truth.collisionId != second.truth.collisionId || first.truth.collisionId != bachelor.truth.collisionId) { + return NTruthClasses - 1; + } + if (!identified(first, PdgPion) || !identified(second, PdgPion) || !identified(bachelor, bachelorPdg(channel))) { + return 2; + } + const bool distinctBachelor = bachelor.truth.id != first.truth.id && bachelor.truth.id != second.truth.id; + return distinctBachelor && trueF0(first, second) ? 0 : 1; + } + + void fillProxy(FourVector const& dipion, TrackCandidate const& first, TrackCandidate const& second, + std::vector const& bachelors, float mult, bool mixed, size_t channel, bool mc = false) + { + if (!channelEnabled(channel)) { + return; + } + const FourVector proxy = constituent(dipion); + const int category = chargeCategory(first.sign, second.sign); + for (auto const& bachelor : bachelors) { + if (!mixed && (bachelor.id == first.id || bachelor.id == second.id)) { + continue; + } + const auto pair = proxy + bachelor.vector(bachelorMass(channel)); + if (std::abs(pair.Rapidity()) >= proxyYMax.value) { + continue; + } + // Neutral dipion: one entry per bachelor. There is no measured proxy-kaon charge. + fillSparse(compactProxy[channel][mixed], {pair.M(), pair.Pt(), dipion.M(), mult, static_cast(category), static_cast(bachelor.sign), static_cast(bachelor.hasTOF)}); + fillSparse(fullProxy[channel][mixed], {pair.M(), pair.Pt(), dipion.M(), mult, static_cast(category), static_cast(bachelor.sign), static_cast(bachelor.hasTOF)}); + if (mc && !mixed) { + const size_t truthClass = proxyTruthClass(first, second, bachelor, channel); + recoComponents[channel][truthClass]->Fill(pair.M(), pair.Pt()); + if (truthClass == 0 && first.truth.primary && second.truth.primary && bachelor.truth.primary && + bachelor.truth.id != first.truth.id && bachelor.truth.id != second.truth.id) { + const double px = ProxyMomentumScale * (first.truth.px + second.truth.px) + bachelor.truth.px; + const double py = ProxyMomentumScale * (first.truth.py + second.truth.py) + bachelor.truth.py; + proxyPtResponse[channel]->Fill(std::hypot(px, py), pair.Pt()); + } + } + } + } + + void fillControlPair(TrackCandidate const& first, TrackCandidate const& second, float mult, bool mixed, size_t channel, bool mc = false) + { + if ((!mixed && first.id == second.id) || !controlEnabled(channel)) { + return; + } + const auto pair = first.vector(constants::physics::MassKaonCharged) + second.vector(bachelorMass(channel)); + if (std::abs(pair.Rapidity()) >= proxyYMax.value) { + return; + } + fillSparse(controls[channel][mixed][chargeCategory(first.sign, second.sign)], {pair.M(), pair.Pt(), mult}); + const int parent = channel == Phi ? PdgPhi : PdgKstar; + if (mc && !mixed && first.sign != second.sign && identified(first, PdgKaon) && identified(second, bachelorPdg(channel)) && commonParent(first.truth, second.truth, parent)) { + recoTrueParent[channel]->Fill(pair.M(), pair.Pt()); + recoPtResponse[channel]->Fill(truthPairPt(first.truth, second.truth), pair.Pt()); + } + } + void fillControls(Event const& event, std::deque const& pool, float mult, bool mc) + { + for (size_t i = 0; i < event.kaons.size(); ++i) { + auto const& kaon = event.kaons[i]; + if (enablePhiControls.value) { + for (size_t j = i + 1; j < event.kaons.size(); ++j) { + fillControlPair(kaon, event.kaons[j], mult, false, Phi, mc); + } + } + if (enableKstarControls.value) { + for (auto const& pion : event.pions) { + fillControlPair(kaon, pion, mult, false, Kstar, mc); + } + } + for (auto const& previous : pool) { + if (enablePhiControls.value) { + for (auto const& other : previous.kaons) { + fillControlPair(kaon, other, mult, true, Phi); + } + } + if (enableKstarControls.value) { + for (auto const& pion : previous.pions) { + fillControlPair(kaon, pion, mult, true, Kstar); + } + } + } + } + if (enableKstarControls.value) { + // Both K(current)-pi(previous) and K(previous)-pi(current) are included. + for (auto const& previous : pool) { + for (auto const& kaon : previous.kaons) { + for (auto const& pion : event.pions) { + fillControlPair(kaon, pion, mult, true, Kstar); + } + } + } + } + } + + template + void processReco(TCollisions const& collisions, TTracks const& tracks, TSlice const& slice) + { + // Pools reset at each dataframe; the key also separates runs, z and multiplicity. + std::map>, std::deque> pools; + const std::deque emptyPool; + for (auto const& collision : collisions) { + histos.fill(HIST("hEventSelection"), 1.); + if (std::abs(collision.posZ()) >= vertexMax.value || (requireSel8.value && !collision.sel8()) || + (requireINELgt0.value && collision.multNTracksPVeta1() < 1)) { + continue; + } + histos.fill(HIST("hEventSelection"), 2.); + const float mult = collision.multNTracksPVeta1(); + std::deque* activePool = nullptr; + if (mixingEnabled()) { + const int run = collision.template bc_as().runNumber(); + activePool = &pools[{run, {static_cast(std::floor(collision.posZ() / mixZWidth.value)), static_cast(std::floor(mult / mixMultWidth.value))}}]; + histos.fill(HIST("hMixPartners"), activePool->size()); + } + auto const& pool = activePool ? *activePool : emptyPool; + Event event; + auto selected = tracks.sliceBy(slice, collision.globalIndex()); + for (auto const& track : selected) { + if (track.sign() == 0 || track.pt() < trackPtMin.value || std::abs(track.eta()) >= trackEtaMax.value || + (requireGlobalTrack.value && !track.isGlobalTrack()) || (requirePVContributor.value && !track.isPVContributor())) { + continue; + } + const bool tof = track.hasTOF(); + if (enablePidQA.value) { + histos.fill(HIST("hPionTPC"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("hKaonTPC"), track.pt(), track.tpcNSigmaKa()); + if (tof) { + histos.fill(HIST("hPionTOF"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("hKaonTOF"), track.pt(), track.tofNSigmaKa()); + } + } + TrackCandidate candidate{.id = track.globalIndex(), .sign = track.sign(), .px = track.px(), .py = track.py(), .pz = track.pz(), .hasTOF = tof, .truth = {}}; + if constexpr (IsMC) { + candidate.truth = trackTruth(track, collision.mcCollisionId()); + } + if (passPID(track.tpcNSigmaPi(), track.tofNSigmaPi(), tof, pionTPC.value, pionTOF.value)) { + event.pions.push_back(candidate); + } + if (passPID(track.tpcNSigmaKa(), track.tofNSigmaKa(), tof, kaonTPC.value, kaonTOF.value)) { + event.kaons.push_back(candidate); + } + } + fillDipions(event, pool, mult, IsMC); + fillControls(event, pool, mult, IsMC); + if (activePool) { + // Mixed events never use ancestry; release that storage before caching. + if constexpr (IsMC) { + for (auto const& collection : {&event.pions, &event.kaons}) { + for (size_t i = 0; i < collection->size(); ++i) { + (*collection)[i].truth = {}; + } + } + } + activePool->push_back(std::move(event)); + if (activePool->size() > static_cast(mixDepth.value)) { + activePool->pop_front(); + } + } + } + } + + void fillDipions(Event const& event, std::deque const& pool, float mult, bool mc) + { + for (size_t i = 0; i < event.pions.size(); ++i) { + auto const& first = event.pions[i]; + for (size_t j = i + 1; j < event.pions.size(); ++j) { + auto const& second = event.pions[j]; + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) >= dipionYMax.value) { + continue; + } + fillSparse(dipionSpectra[0], {pair.M(), pair.Pt(), mult, static_cast(chargeCategory(first.sign, second.sign))}); + if (mc && trueF0(first, second)) { + recoTrueF0->Fill(pair.M(), pair.Pt()); + } + if (pair.M() < dipionMassMin.value || pair.M() >= dipionMassMax.value) { + continue; + } + fillProxy(pair, first, second, event.kaons, mult, false, Phi, mc); + fillProxy(pair, first, second, event.pions, mult, false, Kstar, mc); + for (auto const& previous : pool) { + fillProxy(pair, first, second, previous.kaons, mult, true, Phi); + fillProxy(pair, first, second, previous.pions, mult, true, Kstar); + } + } + for (auto const& previous : pool) { + for (auto const& second : previous.pions) { + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) < dipionYMax.value) { + fillSparse(dipionSpectra[1], {pair.M(), pair.Pt(), mult, static_cast(chargeCategory(first.sign, second.sign))}); + } + } + } + } + } + + void fillGenerated(Event const& event) + { + for (size_t i = 0; i < event.pions.size(); ++i) { + auto const& first = event.pions[i]; + for (size_t j = i + 1; j < event.pions.size(); ++j) { + auto const& second = event.pions[j]; + if (first.sign == second.sign) { + continue; + } + const auto pair = first.vector(constants::physics::MassPionCharged) + second.vector(constants::physics::MassPionCharged); + if (std::abs(pair.Rapidity()) >= dipionYMax.value) { + continue; + } + const bool tagged = trueF0(first, second); + generated[GenDipion]->Fill(pair.M(), pair.Pt()); + if (tagged) { + generated[GenF0]->Fill(pair.M(), pair.Pt()); + } + if (pair.M() < dipionMassMin.value || pair.M() >= dipionMassMax.value) { + continue; + } + const FourVector proxy = constituent(pair); + for (size_t channel = 0; channel < NChannels; ++channel) { + if (!channelEnabled(channel)) { + continue; + } + auto const& bachelors = channel == Phi ? event.kaons : event.pions; + for (auto const& bachelor : bachelors) { + if (bachelor.id == first.id || bachelor.id == second.id) { + continue; + } + const auto candidate = proxy + bachelor.vector(bachelorMass(channel)); + if (std::abs(candidate.Rapidity()) >= proxyYMax.value) { + continue; + } + generated[channel == Phi ? GenProxyPhi : GenProxyKstar]->Fill(candidate.M(), candidate.Pt()); + if (tagged) { + generated[channel == Phi ? GenProxyPhiF0 : GenProxyKstarF0]->Fill(candidate.M(), candidate.Pt()); + } + } + } + } + } + for (size_t channel = 0; channel < NChannels; ++channel) { + if (!controlEnabled(channel)) { + continue; + } + for (size_t i = 0; i < event.kaons.size(); ++i) { + auto const& first = event.kaons[i]; + auto const& others = channel == Phi ? event.kaons : event.pions; + const size_t start = channel == Phi ? i + 1 : 0; + for (size_t j = start; j < others.size(); ++j) { + auto const& second = others[j]; + if (first.id == second.id || first.sign == second.sign) { + continue; + } + const auto pair = first.vector(constants::physics::MassKaonCharged) + second.vector(bachelorMass(channel)); + if (std::abs(pair.Rapidity()) >= proxyYMax.value) { + continue; + } + generated[channel == Phi ? GenPhi : GenKstar]->Fill(pair.M(), pair.Pt()); + if (commonParent(first.truth, second.truth, channel == Phi ? PdgPhi : PdgKstar)) { + generated[channel == Phi ? GenPhiTrue : GenKstarTrue]->Fill(pair.M(), pair.Pt()); + } + } + } + } + } + + void processData(Collisions const& collisions, Tracks const& tracks, aod::BCsWithTimestamps const&) + { + processReco(collisions, tracks, perCollision); + } + // Keep the original JSON option "process"; no unconditional process() method. + PROCESS_SWITCH_FULL(F0phiproxy, processData, process, "Dipion phi/Kstar proxies in data", true); + + void processMCReco(CollisionsMC const& collisions, TracksMC const& tracks, aod::McParticles const&, aod::BCsWithTimestamps const&) + { + processReco(collisions, tracks, perCollisionMC); + } + PROCESS_SWITCH(F0phiproxy, processMCReco, "Data-identical reconstruction with truth components and responses", false); + + void processMCGen(aod::McCollision const& collision, aod::McParticles const& particles) + { + histos.fill(HIST("hMCGenEventSelection"), 1.); + if (std::abs(collision.posZ()) >= vertexMax.value) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 2.); + bool inel = false; + for (auto const& particle : particles) { + // O2 ASoA row iterators and their end sentinel are not STL input iterators. + // cppcheck-suppress useStlAlgorithm + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) < 1.f && chargeFromPdg(particle.pdgCode()) != 0) { + inel = true; + break; + } + } + if (requireINELgt0.value && !inel) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 3.); + Event event; + for (auto const& particle : particles) { + const int absolute = std::abs(particle.pdgCode()); + if ((absolute != PdgPion && absolute != PdgKaon) || particle.pt() < trackPtMin.value || + std::abs(particle.eta()) >= trackEtaMax.value || (mcPrimaryOnly.value && !particle.isPhysicalPrimary())) { + continue; + } + TrackCandidate candidate{.id = particle.globalIndex(), .sign = particle.pdgCode() > 0 ? 1 : -1, .px = particle.px(), .py = particle.py(), .pz = particle.pz(), .hasTOF = false, .truth = particleTruth(particle)}; + (absolute == PdgPion ? event.pions : event.kaons).push_back(std::move(candidate)); + } + // Unweighted primary-daughter fiducial spectra, with no detector selection. + // Native f0 truth is not a resonance-fed coalescence ancestry tag. + fillGenerated(event); + } + PROCESS_SWITCH(F0phiproxy, processMCGen, "Independent generated fiducial spectra and exact common-parent tags", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx b/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx index f466066c980..ead7c9b294f 100644 --- a/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx +++ b/PWGLF/Tasks/Resonances/f1protoncorrelation.cxx @@ -464,7 +464,7 @@ struct f1protoncorrelation { /// Magnetic field to be provided in Tesla static constexpr float tmpRadiiTPC[9] = {85., 105., 125., 145., 165., 185., 205., 225., 245.}; - float PhiAtSpecificRadiiTPC(const TLorentzVector part1, const TLorentzVector part2, float charge1 = 0, int charge2 = 0, float magfield1 = 0.0, float magfield2 = 0.0) + float PhiAtSpecificRadiiTPC(const TLorentzVector& part1, const TLorentzVector& part2, float charge1 = 0, int charge2 = 0, float magfield1 = 0.0, float magfield2 = 0.0) { float pt1 = part1.Pt(); float phi1 = part1.Phi(); @@ -496,8 +496,8 @@ struct f1protoncorrelation { // get kstar TLorentzVector trackSum, PartOneCMS, PartTwoCMS, trackRelK; - float getkstar(const TLorentzVector part1, - const TLorentzVector part2) + float getkstar(const TLorentzVector& part1, + const TLorentzVector& part2) { // const TLorentzVector trackSum = part1 + part2; trackSum = part1 + part2; @@ -517,7 +517,7 @@ struct f1protoncorrelation { return 0.5 * trackRelK.P(); } - float getmT(const TLorentzVector part1, const TLorentzVector part2) + float getmT(const TLorentzVector& part1, const TLorentzVector& part2) { trackSum = part1 + part2; float kT = 0.5 * trackSum.Pt(); @@ -543,7 +543,7 @@ struct f1protoncorrelation { } lastRunNumber = currentRunNumber; - for (auto f1track : f1tracks) { + for (const auto& f1track : f1tracks) { if (f1track.f1MassKaonKshort() > maxKKS0Mass) { continue; } @@ -593,7 +593,7 @@ struct f1protoncorrelation { if (typeofCombined == 1) { combinedTPC = (f1track.f1d1TPC() - f1track.f1d2TPC()) / (f1track.f1d1TPC() + f1track.f1d2TPC()); } - for (auto protontrack : protontracks) { + for (const auto& protontrack : protontracks) { Proton.SetXYZM(protontrack.protonPx(), protontrack.protonPy(), protontrack.protonPz(), 0.938); if (Proton.Pt() > momentumProtonMax || Proton.Pt() < momentumProtonMin) { continue; @@ -668,7 +668,7 @@ struct f1protoncorrelation { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningType colBinning{{CfgVtxBins, CfgMultBins}}; Preslice tracksPerCollisionPresliceF1 = aod::f1protondaughter::redF1PEventId; Preslice tracksPerCollisionPresliceP = aod::f1protondaughter::redF1PEventId; void processME(aod::RedF1PEvents& collisions, @@ -975,7 +975,7 @@ struct f1protoncorrelation { lastRunNumber = currentRunNumber; auto countf1 = 0; - for (auto f1track : f1tracks) { + for (const auto& f1track : f1tracks) { if (f1track.f1MassKaonKshort() > maxKKS0Mass) continue; @@ -1021,7 +1021,7 @@ struct f1protoncorrelation { continue; // Proton loop - for (auto protontrack : protontracks) { + for (const auto& protontrack : protontracks) { Proton.SetXYZM(protontrack.protonPx(), protontrack.protonPy(), protontrack.protonPz(), 0.938); if ((f1track.f1PionIndex() == protontrack.f1ProtonIndex()) || diff --git a/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx new file mode 100644 index 00000000000..5d8989567fe --- /dev/null +++ b/PWGLF/Tasks/Resonances/hResonanceCorrelation.cxx @@ -0,0 +1,3448 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file hResonanceCorrelation.cxx +/// \brief This task serves to do hadron-resonance correlation studies. +/// The yield will be calculated using the two-particle correlation method. +/// Trigger particle : Hadrons +/// Associated Particles : Phi, K*0 +/// this task requires the hResonanceCorrelationFilter to have been run before. +/// +/// \author Hirak Kumar Koley (hirak.koley@cern.ch) + +#include "PWGLF/DataModel/LFHResonanceCorrelationTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/Utils/inelGt.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::constants::math; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +using TracksComplete = soa::Join; +using AssocPhis = soa::Join; +using AssocKstars = soa::Join; + +struct HResonanceCorrelation { + // for efficiency corrections if requested + Service ccdb; + + Service pdgDB; + o2::pwglf::ParticleCounter mCounter; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + struct : ConfigurableGroup { + std::string prefix = "masterConfigurations"; + Configurable collisionHasTriggOrAssoc{"collisionHasTriggOrAssoc", 0, "require the collisions containing (0:no requirement 1:trig 2:assoc 3:trig or assoc 4:trig and assoc"}; + Configurable doFullCorrelationStudy{"doFullCorrelationStudy", true, "if true, do full correlation study by creating all THnSparse histograms for the correlation function"}; + Configurable doCorrelationHadron{"doCorrelationHadron", false, "do Hadron correlation"}; + Configurable doCorrelationPhi{"doCorrelationPhi", false, "do Phi correlation"}; + Configurable doCorrelationKstar{"doCorrelationKstar", false, "do K*0 correlation"}; + Configurable doCorrelationPion{"doCorrelationPion", false, "do Pion correlation"}; + Configurable doCorrelationKaon{"doCorrelationKaon", false, "do Kaon correlation"}; + Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; + // Used by the generator-level (MC-truth) selection in processPrediction, + // not by isSelectedEvents() below -- that's a different, reconstructed- + // level selection now driven entirely by configEvents.* to match the + // filter task (hResonanceCorrelationFilter.cxx) exactly. + Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events (generator level, see processPrediction)"}; + // Still used directly by the closure-test/prediction blocks (reconstructed + // vs. generated z-vertex checks), independent of isSelectedEvents(). + Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position (closure test / prediction only)"}; + Configurable skipUnderOverflowInTHn{"skipUnderOverflowInTHn", false, "skip under/overflow in THns"}; + Configurable mixingParameter{"mixingParameter", 10, "how many events are mixed"}; + Configurable doMCassociation{"doMCassociation", false, "fill everything only for MC associated"}; + Configurable doTriggPhysicalPrimary{"doTriggPhysicalPrimary", false, "require physical primary for trigger particles"}; + Configurable applyNewMCSelection{"applyNewMCSelection", false, "apply new MC Generated selection"}; + Configurable doSeparateFT0Prediction{"doSeparateFT0Prediction", false, "separate FT0M to FT0A and FT0C in prediction process"}; + Configurable useCentralityinPrediction{"useCentralityinPrediction", false, "if true, use centrality instead of multiplisity"}; + Configurable doMirroringInDelataEta{"doMirroringInDelataEta", false, "if true, fill only positive delta eta and mirror the negative side in post processing, Adjust the delta axis!"}; + Configurable fillCorrelationHistWithMass{"fillCorrelationHistWithMass", false, "if true, fill correlation histograms with particle mass"}; + } masterConfigurations; + + // Trigger-selection-mode flags: which trigger-pT bin to require (if + // processSelectEventWithTrigger is enabled), a charge cut on the trigger + // particle, and whether to use the event's leading particle as the sole + // trigger instead of every trigger candidate. + struct : ConfigurableGroup { + std::string prefix = "triggerSelection"; + Configurable triggerBinToSelect{"triggerBinToSelect", 0, "trigger bin to select on if processSelectEventWithTrigger enabled"}; + Configurable triggerParticleCharge{"triggerParticleCharge", 0, "For checks, if 0 all charged tracks, if -1 only neg., if 1 only positive"}; + Configurable useTheLeadingParticleAsTrigger{"useTheLeadingParticleAsTrigger", false, "if true, use the leading particle in the event as trigger particle"}; + } triggerSelection; + + // MC-truth physical-primary requirements, reconstructed-level and + // generator-level respectively. + struct : ConfigurableGroup { + std::string prefix = "mcSelections"; + Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; + Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; + } mcSelections; + + // Histogram-booking/filling toggles, not physics cuts. + struct : ConfigurableGroup { + std::string prefix = "qaSwitches"; + Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; + Configurable doDeltaPhiStarCheck{"doDeltaPhiStarCheck", false, "if true, create and fill delta phi star histograms"}; + } qaSwitches; + + // Generic phase-space cuts used across trigger, associated AND gen-level + // (MC-truth) particles alike -- not species- or reco/gen-specific, so not + // folded into configTriggerTracks/configAssocTracks. + struct : ConfigurableGroup { + std::string prefix = "phaseSpaceSelections"; + Configurable etaSel{"etaSel", 0.8, "Selection in eta for trigger and associated particles"}; + Configurable ySel{"ySel", 0.5, "Selection in rapidity for consistency checks"}; + } phaseSpaceSelections; + + // Reconstructed-event selection cuts -- deliberately the same cut set and + // names as configEvents in hResonanceCorrelationFilter.cxx, so a collision + // that was selected to produce the trigger/associated/candidate tables is + // selected identically here (previously this task re-derived its own, + // different cut list via isisCollisionSelect()/isisCollisionSelectPbPb(), + // which could disagree with the filter task's selection). + struct : ConfigurableGroup { + std::string prefix = "configEvents"; + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", true, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtSel8{"cfgEvtSel8", true, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtNoSameBunchPileupCut{"cfgEvtNoSameBunchPileupCut", true, "Evt sel: reject collisions associated with the same found-by-T0 bunch crossing"}; + Configurable cfgEvtGoodZvtxFT0vsPVCut{"cfgEvtGoodZvtxFT0vsPVCut", true, "Evt sel: require small difference between z-vertex from PV and from FT0"}; + } configEvents; + + // Axes - configurable for smaller sizes + struct : ConfigurableGroup { + std::string prefix = "axesConfigurations"; + ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0.0f, 0.01f, 1.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Mixing bins - multiplicity"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "#phi"}; + ConfigurableAxis axisEta{"axisEta", {80, -0.8, +0.8}, "#eta"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {72, -PIHalf, PIHalf * 3}, "delta #varphi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {50, -1.6, 1.6}, "delta eta axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt associated axis for histograms"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.0, 1.0, 2.0, 3.0, 100}, "pt associated axis for histograms"}; + ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; + ConfigurableAxis axisMassNSigma{"axisMassNSigma", {40, -2, 2}, "Axis for mass Nsigma"}; + // Bounds must stay >= the filter task's own acceptance pre-cut + // (hResonanceCorrelationFilter.cxx: peakMass +/- maxMassNSigma*sigma, + // ~[0.998, 1.041] GeV for Phi and ~[0.654, 1.137] GeV for K*0 by + // default) -- anything narrower here silently underflows/overflows + // candidates the filter already let through, before fillCorrelationHistWithMass + // ever gets a chance to keep them. Bin count trimmed from the previous + // 300 (which spanned mostly-empty ranges, e.g. Phi out to 1.31 GeV) down + // to what the narrower, physically-relevant range actually needs while + // keeping comparable or finer MeV/bin resolution for the post-processing fit. + ConfigurableAxis axisPhiMass{"axisPhiMass", {100, 0.99f, 1.05f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis axisKstarMass{"axisKstarMass", {150, 0.65f, 1.15f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Multiplicity axis in model prediction process"}; + ConfigurableAxis axisMidrapidityMultiplicity{"axisMidrapidityMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Midrapidity Multiplicity axis in model prediction process"}; + } axesConfigurations; + + // for topo var QA + // Per-species, peak-relative mass windows: a single shared + // `massWindowConfigurations` comparing raw assocCandidate.mass() directly + // against these bounds would break isLeftBg structurally (it requires a + // NEGATIVE mass, which raw mass never is), and sharing one window between + // Phi and K*0 leaves one species' tuned range (Phi's, ~1.0-1.04 GeV) + // entirely outside the other's candidate mass range (K*0's filter-level + // cut is 0.796-0.996 GeV), making K*0's RightBg structurally unreachable. + // + // Instead, classify using delta = mass - peakMass (a real signed quantity, + // so isLeftBg is reachable), with each species' own peak mass and its own + // effective sigma, in its own struct. maxPeakNSigma/minBgNSigma/maxBgNSigma + // are genuinely "number of sigma" (2, 3, 6 by default), multiplied by + // `sigma` (in GeV) to get the actual mass-offset bounds. + // + // `sigma` defaults here are PDG Gamma/2.355 (treating the natural + // Breit-Wigner width as if it were a Gaussian FWHM) -- a reasonable + // placeholder for K*0, where the ~47 MeV natural width likely dominates + // over detector resolution, but LIKELY AN UNDERESTIMATE for Phi, whose + // ~4.2 MeV natural width is probably smaller than your actual track + // momentum resolution. Refit both from your own reconstructed invariant + // mass peak (Voigt / BW (x) Gaussian) once you have statistics, and update + // `sigma` here -- everything else (the Nsigma multipliers) stays valid. + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsPhi"; + Configurable peakMass{"peakMass", 1.019455f, "Phi(1020) PDG mass (GeV), used as delta = mass - peakMass"}; + Configurable sigma{"sigma", 0.0018f, "effective width (GeV) for the Nsigma windows below -- PDG Gamma/2.355 placeholder, refit from your own peak"}; + Configurable maxPeakNSigma{"maxPeakNSigma", 2, "Signal region half-width, in units of sigma"}; + Configurable minBgNSigma{"minBgNSigma", 3, "Bg region edge closest to peak, in units of sigma"}; + Configurable maxBgNSigma{"maxBgNSigma", 6, "Bg region edge furthest from peak, in units of sigma"}; + } massWindowConfigurationsPhi; // allows for gap between peak and bg in case someone wants to + + struct : ConfigurableGroup { + std::string prefix = "massWindowConfigurationsKstar"; + Configurable peakMass{"peakMass", 0.89555f, "K*0(892) PDG mass (GeV), used as delta = mass - peakMass"}; + Configurable sigma{"sigma", 0.0201f, "effective width (GeV) for the Nsigma windows below -- PDG Gamma/2.355 placeholder, refit from your own peak"}; + Configurable maxPeakNSigma{"maxPeakNSigma", 2, "Signal region half-width, in units of sigma"}; + Configurable minBgNSigma{"minBgNSigma", 3, "Bg region edge closest to peak, in units of sigma"}; + Configurable maxBgNSigma{"maxBgNSigma", 6, "Bg region edge furthest from peak, in units of sigma"}; + } massWindowConfigurationsKstar; // allows for gap between peak and bg in case someone wants to + + // Implementation of on-the-spot efficiency correction + struct : ConfigurableGroup { + std::string prefix = "efficiencyFlags"; + Configurable applyEfficiencyCorrection{"applyEfficiencyCorrection", false, "apply efficiency correction"}; + Configurable applyEfficiencyForTrigger{"applyEfficiencyForTrigger", false, "apply efficiency correction for the trigger particle"}; + Configurable applyEfficiencyPropagation{"applyEfficiencyPropagation", false, "propagate also the efficiency uncertainty"}; + Configurable applyPurityHadron{"applyPurityHadron", false, "apply the purity correction for associated hadrons"}; + Configurable applyPurityTrigger{"applyPurityTrigger", false, "apply the purity correction for trigger particle"}; + Configurable applyEffAsFunctionOfMult{"applyEffAsFunctionOfMult", false, "apply efficiency as a function of multiplicity as well"}; + Configurable applyEffAsFunctionOfMultAndPhi{"applyEffAsFunctionOfMultAndPhi", false, "apply efficiency as a function of multiplicity and phi"}; + } efficiencyFlags; + struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; + Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository to use"}; + Configurable efficiencyCCDBPath{"efficiencyCCDBPath", "GLO/Config/GeometryAligned", "Path of the efficiency corrections"}; + } ccdbConfigurations; + + // Configurables for doing subwagon systematics. + // Split by who they apply to (trigger hadron vs. associated particle). + // requireDCAzCut is the one genuinely shared switch (it toggles the DCAz + // cut for BOTH isValidTrigger() and the assoc selection); rather than its + // own single-entry group, it lives in `checks` below alongside the other + // small behavioral toggles. + struct : ConfigurableGroup { + std::string prefix = "configTriggerTracks"; + Configurable minTPCNCrossedRowsTrigger{"minTPCNCrossedRowsTrigger", 70, "Minimum TPC crossed rows (trigger)"}; + Configurable triggerRequireITS{"triggerRequireITS", true, "require ITS signal in trigger tracks"}; + Configurable triggerMaxTPCSharedClusters{"triggerMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive)"}; + Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; + // DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaZconstant{"dcaZconstant", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZpTdep{"dcaZpTdep", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; + } configTriggerTracks; + + struct : ConfigurableGroup { + std::string prefix = "configAssocTracks"; + Configurable minTPCNCrossedRowsAssociated{"minTPCNCrossedRowsAssociated", 70, "Minimum TPC crossed rows (associated)"}; + Configurable assocRequireITS{"assocRequireITS", true, "require ITS signal in associated primary tracks"}; + Configurable assocMaxTPCSharedClusters{"assocMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive) for assoc primary tracks"}; + Configurable assocRequireL0{"assocRequireL0", true, "require ITS L0 cluster for assoc primary track"}; + // DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) + Configurable dcaXYconstantAssoc{"dcaXYconstantAssoc", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaXYpTdepAssoc{"dcaXYpTdepAssoc", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + Configurable dcaZconstantAssoc{"dcaZconstantAssoc", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZpTdepAssoc{"dcaZpTdepAssoc", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; + } configAssocTracks; + + struct : ConfigurableGroup { + std::string prefix = "checks"; + + // on the fly correction instead of mixingParameter + Configurable doOnTheFlyFlattening{"doOnTheFlyFlattening", 0, "enable an on-the-fly correction instead of using mixing"}; + Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; + Configurable requireDCAzCut{"requireDCAzCut", false, "require DCAz cut for trigger and associated primary tracks"}; + } checks; + + struct ValidCollision { + struct ValidParticle { + float eta = 0.f; + float phi = 0.f; + float pt = 0.f; + int region = -1; + float efficiency = 1.f; + float efficiencyError = 0.f; + float mass = 0.f; // only meaningful for Phi/K*0 buffered candidates when + // masterConfigurations.fillCorrelationHistWithMass is + // set; unused (stays 0) for trigger particles and for + // Pion/Kaon/Hadron, which never carry a mass axis. + // no species tag stored here: the `type` argument to addValidParticle + // below (-1 = trigger, else Phi/K*0) only decides which vector this + // particle goes into; once stored, nothing reads it back. + }; + float pvz = 0.f; + float mult = 0.f; + std::vector trigParticles; + std::vector assocParticles; + void addValidParticle(float eta, float phi, float pt, int region, float efficiency, float efficiencyError, int type, float mass = 0.f) + { + ValidParticle particle{eta, phi, pt, region, efficiency, efficiencyError, mass}; + + if (type == -1) { + trigParticles.push_back(particle); + } else { + assocParticles.push_back(particle); + } + } + }; + + using ValidCollisions = std::vector>; + ValidCollisions validCollisions; + + // objects to use for efficiency corrections + TH2F* hEfficiencyTrigger = nullptr; + TH3F* hEfficiencyTriggerMult = nullptr; + THnF* hEfficiencyTriggerMultVsPhi = nullptr; + TH2F* hEfficiencyPion = nullptr; + TH2F* hEfficiencyKaon = nullptr; + TH2F* hEfficiencyPhi = nullptr; + THnF* hEfficiencyPhiMultVsPhi = nullptr; + TH2F* hEfficiencyKstar = nullptr; + THnF* hEfficiencyKstarMultVsPhi = nullptr; + TH2F* hEfficiencyHadron = nullptr; + TH3F* hEfficiencyHadronMult = nullptr; + TH1F* hPurityHadron = nullptr; + TH2F* hPurityHadronMult = nullptr; + + // objects to propagate the efficiency uncertainty + TH2F* hEfficiencyUncertaintyTrigger = nullptr; + TH3F* hEfficiencyUncertaintyTriggerMult = nullptr; + TH2F* hEfficiencyUncertaintyPion = nullptr; + TH2F* hEfficiencyUncertaintyKaon = nullptr; + TH2F* hEfficiencyUncertaintyPhi = nullptr; + TH2F* hEfficiencyUncertaintyKstar = nullptr; + TH2F* hEfficiencyUncertaintyHadron = nullptr; + TH3F* hEfficiencyUncertaintyHadronMult = nullptr; + TH1F* hPurityUncertaintyHadron = nullptr; + TH2F* hPurityUncertaintyHadronMult = nullptr; + + using BinningTypePP = ColumnBinningPolicy; + using BinningTypePbPb = ColumnBinningPolicy; + + // collision slicing for mixed events + Preslice collisionSliceTracks = aod::triggerTracks::collisionId; + // Preslice collisionSlicePions = aod::assocHadrons::collisionId; + Preslice collisionSliceHadrons = aod::assocHadrons::collisionId; + Preslice perCollision = aod::mcparticle::mcCollisionId; + Preslice collisionSlicePhis = aod::assocPhis::collisionId; + Preslice collisionSliceKstars = aod::assocKstars::collisionId; + + // Kaon sits after Pion, taking the slot Hadron used to occupy (Hadron + // moves to IndexHadron below) -- same reason Hadron itself sits outside + // AssocParticleTypesNoHadron: the generator-level MC-truth matching used by + // the closure test/prediction blocks further down is "intentionally + // restricted to Pion only" (see the NOTE comments at their static_for<...> + // calls) and was never extended to Kstar0 either, so Kaon joins Hadron + // outside that group rather than shifting Pion's/Kstar0's/Phi's indices or + // AssocParticleTypesNoHadron's boundary. + static constexpr std::string_view Particlenames[] = {"Phi", "Kstar0", "Pion", "Kaon", "Hadron"}; + static constexpr int PdgCodes[] = {333, 313, 211, 321, 0}; // Hadron has no single PDG code; 0 is a harmless placeholder + + static constexpr int IndexPhi = 0; + static constexpr int IndexKstar = 1; + static constexpr int IndexPion = 2; + static constexpr int IndexKaon = 3; + static constexpr int IndexHadron = 4; + + uint16_t doCorrelation = 0; + // Which species' processSameEventH() is responsible for filling the + // shared CollCutCounts/EventQA histograms for a given collision, computed + // once in init() -- see the comment there. -1 means none of the same-event + // process functions are actually enabled (bit + switch), so nothing fills them. + int eventQAOwnerIndex = -1; + int mRunNumber = 0; + + std::vector> axisRanges; + + static constexpr float MinRadiusTPC = 0.8; + static constexpr float MaxRadiusTPC = 2.5; + + static constexpr float Neutral = 0.0; + + static constexpr int AssocParticleTypes = 5; // Phi, Kstar0, Pion, Kaon, Hadron + static constexpr int AssocParticleTypesNoHadron = 3; // Phi, Kstar0, Pion (Kaon excluded too -- see the comment above IndexKaon) + + /// Function to aid in calculating delta-phi + /// \param phi1 first phi value + /// \param phi2 second phi value + double computeDeltaPhi(double phi1, double phi2) + { + double deltaPhi = phi1 - phi2; + double shiftedDeltaPhi = RecoDecay::constrainAngle(deltaPhi, -PIHalf); + return shiftedDeltaPhi; + } + + /// Function to load efficiencies to memory from CCDB + /// \param bc provided such that the run number can be used + void initEfficiencyFromCCDB(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + LOG(info) << "Loading efficiencies from CCDB for run " << mRunNumber << " now..."; + auto timeStamp = bc.timestamp(); + + TList* listEfficiencies = ccdb->getForTimeStamp(ccdbConfigurations.efficiencyCCDBPath, timeStamp); + + if (!listEfficiencies) { + LOG(fatal) << "Problem getting TList object with efficiencies!"; + } + + hEfficiencyTrigger = static_cast(listEfficiencies->FindObject("hEfficiencyTrigger")); + hEfficiencyTriggerMult = static_cast(listEfficiencies->FindObject("hEfficiencyTriggerMult")); + hEfficiencyTriggerMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyTriggerMultVsPhi")); + hEfficiencyPhi = static_cast(listEfficiencies->FindObject("hEfficiencyPhi")); + hEfficiencyPhiMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyPhiMultVsPhi")); + hEfficiencyKstar = static_cast(listEfficiencies->FindObject("hEfficiencyKstar")); + hEfficiencyKstarMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyKstarMultVsPhi")); + hEfficiencyHadron = static_cast(listEfficiencies->FindObject("hEfficiencyHadron")); + hEfficiencyHadronMult = static_cast(listEfficiencies->FindObject("hEfficiencyHadronMult")); + hEfficiencyPion = static_cast(listEfficiencies->FindObject("hEfficiencyPion")); + hEfficiencyKaon = static_cast(listEfficiencies->FindObject("hEfficiencyKaon")); + hPurityHadron = static_cast(listEfficiencies->FindObject("hPurityHadron")); + hPurityHadronMult = static_cast(listEfficiencies->FindObject("hPurityHadronMult")); + hEfficiencyUncertaintyTrigger = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTrigger")); + hEfficiencyUncertaintyTriggerMult = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTriggerMult")); + hEfficiencyUncertaintyPhi = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyPhi")); + hEfficiencyUncertaintyKstar = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyKstar")); + hEfficiencyUncertaintyPion = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyPion")); + hEfficiencyUncertaintyKaon = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyKaon")); + hEfficiencyUncertaintyHadron = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadron")); + hEfficiencyUncertaintyHadronMult = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadronMult")); + hPurityUncertaintyHadron = static_cast(listEfficiencies->FindObject("hPurityUncertaintyHadron")); + hPurityUncertaintyHadronMult = static_cast(listEfficiencies->FindObject("hPurityUncertaintyHadronMult")); + if (efficiencyFlags.applyEfficiencyPropagation && !efficiencyFlags.applyEffAsFunctionOfMultAndPhi && !hEfficiencyUncertaintyTrigger) + LOG(fatal) << "Problem getting hEfficiencyUncertaintyTrigger!"; + if (efficiencyFlags.applyEffAsFunctionOfMult && !hEfficiencyTriggerMult) + LOG(fatal) << "Problem getting hEfficiencyTriggerMult!"; + LOG(info) << "Efficiencies now loaded for " << mRunNumber; + } + + void init(InitContext const&) + { + mRunNumber = 0; + hEfficiencyPion = 0x0; + hEfficiencyKaon = 0x0; + hEfficiencyPhi = 0x0; + hEfficiencyKstar = 0x0; + hEfficiencyUncertaintyTrigger = 0x0; + hEfficiencyUncertaintyPion = 0x0; + hEfficiencyUncertaintyKaon = 0x0; + hEfficiencyUncertaintyPhi = 0x0; + hEfficiencyUncertaintyKstar = 0x0; + + hEfficiencyHadron = 0x0; + hPurityHadron = 0x0; + hPurityUncertaintyHadron = 0x0; + hEfficiencyUncertaintyHadron = 0x0; + + // set bitmap for convenience + doCorrelation = 0; + if (masterConfigurations.doCorrelationPhi) + SETBIT(doCorrelation, IndexPhi); + if (masterConfigurations.doCorrelationKstar) + SETBIT(doCorrelation, IndexKstar); + if (masterConfigurations.doCorrelationPion) + SETBIT(doCorrelation, IndexPion); + if (masterConfigurations.doCorrelationKaon) + SETBIT(doCorrelation, IndexKaon); + if (masterConfigurations.doCorrelationHadron) + SETBIT(doCorrelation, IndexHadron); + + // Pick exactly one "owner" species for the shared CollCutCounts/EventQA + // fills: the highest-priority species (Phi > Kstar > Pion > Kaon > + // Hadron) that will actually run its processSameEventH() (both its + // doCorrelation bit AND its PROCESS_SWITCH on). Every processSameEventH + // calls isSelectedEvents(collision, eventQAOwnerIndex == IndexX) -- only + // the owner's call passes fillHist=true, so a collision examined by + // several same-event process functions in the same run (e.g. Phi and + // K*0 both enabled) is still only counted once, instead of once per + // enabled species. + if (TESTBIT(doCorrelation, IndexPhi) && doprocessSameEventHPhis) { + eventQAOwnerIndex = IndexPhi; + } else if (TESTBIT(doCorrelation, IndexKstar) && doprocessSameEventHKstars) { + eventQAOwnerIndex = IndexKstar; + } else if (TESTBIT(doCorrelation, IndexPion) && doprocessSameEventHPions) { + eventQAOwnerIndex = IndexPion; + } else if (TESTBIT(doCorrelation, IndexKaon) && doprocessSameEventHKaons) { + eventQAOwnerIndex = IndexKaon; + } else if (TESTBIT(doCorrelation, IndexHadron) && doprocessSameEventHHadrons) { + eventQAOwnerIndex = IndexHadron; + } + + // Store axis ranges to prevent spurious filling + // axis status: + // --- Delta-phi is safe -> math forbids insanity + // --- Delta-eta depends on pre-filter -> check + // --- pT assoc depends on binning -> check + // --- vertex Z is safe -> skipped at evsel level + // --- multiplicity -> check + + // grab axis edge from ConfigurableAxes + const AxisSpec preAxisDeltaPhi{axesConfigurations.axisDeltaPhi, "#Delta#varphi"}; + const AxisSpec preAxisDeltaEta{axesConfigurations.axisDeltaEta, "#Delta#eta"}; + const AxisSpec preAxisPtAssoc{axesConfigurations.axisPtAssoc, "#it{p}_{T}^{assoc} (GeV/c)"}; + const AxisSpec preAxisPtTrigger{axesConfigurations.axisPtTrigger, "#it{p}_{T}^{trigger} (GeV/c)"}; + const AxisSpec preAxisVtxZ{axesConfigurations.axisVtxZ, "vertex Z (cm)"}; + const AxisSpec preAxisMult{axesConfigurations.axisMult, "mult percentile"}; + const AxisSpec axisPtPhi{axesConfigurations.axisPtAssoc, "#it{p}_{T}^{#phi} (GeV/c)"}; + const AxisSpec preAxisMultiplicity{axesConfigurations.axisMultiplicity, "multiplicity"}; + + // store the original axes in specific TH1Cs for completeness + histos.add("axes/hDeltaPhiAxis", "", kTH1C, {preAxisDeltaPhi}); + histos.add("axes/hDeltaEtaAxis", "", kTH1C, {preAxisDeltaEta}); + histos.add("axes/hPtAssocAxis", "", kTH1C, {preAxisPtAssoc}); + histos.add("axes/hPtTriggerAxis", "", kTH1C, {preAxisPtTrigger}); + histos.add("axes/hVertexZAxis", "", kTH1C, {preAxisVtxZ}); + histos.add("axes/hMultAxis", "", kTH1C, {preAxisMult}); + histos.add("axes/hMultiplicityAxis", "", kTH1C, {preAxisMultiplicity}); + + std::vector edgesDeltaPhiOrig = preAxisDeltaPhi.binEdges; + std::vector edgesDeltaEtaOrig = preAxisDeltaEta.binEdges; + std::vector edgesPtAssocOrig = preAxisPtAssoc.binEdges; + std::vector edgesPtTriggerOrig = preAxisPtTrigger.binEdges; + std::vector edgesVtxZOrig = preAxisVtxZ.binEdges; + std::vector edgesMultOrig = preAxisMult.binEdges; + std::vector edgesMultiplicityOrig = preAxisMultiplicity.binEdges; + + std::vector rangesDeltaPhi = {static_cast(edgesDeltaPhiOrig[0]), static_cast(edgesDeltaPhiOrig[edgesDeltaPhiOrig.size() - 1])}; + std::vector rangesDeltaEta = {static_cast(edgesDeltaEtaOrig[0]), static_cast(edgesDeltaEtaOrig[edgesDeltaEtaOrig.size() - 1])}; + std::vector rangesPtAssoc = {static_cast(edgesPtAssocOrig[0]), static_cast(edgesPtAssocOrig[edgesPtAssocOrig.size() - 1])}; + std::vector rangesPtTrigger = {static_cast(edgesPtTriggerOrig[0]), static_cast(edgesPtTriggerOrig[edgesPtTriggerOrig.size() - 1])}; + std::vector rangesVtxZ = {static_cast(edgesVtxZOrig[0]), static_cast(edgesVtxZOrig[edgesVtxZOrig.size() - 1])}; + std::vector rangesMult = {static_cast(edgesMultOrig[0]), static_cast(edgesMultOrig[edgesMultOrig.size() - 1])}; + std::vector rangesMultiplicity = {static_cast(edgesMultiplicityOrig[0]), static_cast(edgesMultiplicityOrig[edgesMultiplicityOrig.size() - 1])}; + + axisRanges.emplace_back(rangesDeltaPhi); + axisRanges.emplace_back(rangesDeltaEta); + axisRanges.emplace_back(rangesPtAssoc); + axisRanges.emplace_back(rangesPtTrigger); + axisRanges.emplace_back(rangesVtxZ); + axisRanges.emplace_back(rangesMult); + axisRanges.emplace_back(rangesMultiplicity); + + std::vector edgesDeltaPhi; + std::vector edgesDeltaEta; + std::vector edgesPtAssoc; + std::vector edgesPtTrigger; + std::vector edgesVtxZ; + std::vector edgesMult; + std::vector edgesMultiplicity; + + // v--- skipUnderOverflowInTHn ---v + // + // if enabled, this will change the axes such that they will solely cover the interval from + // edge[1] to edge[n-1]; this will mean that the bin 1 and bin N will be stored in + // under / overflow bins and will have to be manually unpacked. Do not forget to do the manual + // unpacking a posteriori! + // + // this feature is meant to save memory conveniently. + // it should actually be implemented centrally in ROOT but ok, this will do it for now. + + int offset = masterConfigurations.skipUnderOverflowInTHn ? 1 : 0; + // ===] delta-phi [=== + if (!preAxisDeltaPhi.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesDeltaPhiOrig.size()) - offset; i++) + edgesDeltaPhi.emplace_back(edgesDeltaPhiOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesDeltaPhiOrig[0]; + double delta = (edgesDeltaPhiOrig[1] - edgesDeltaPhiOrig[0]) / preAxisDeltaPhi.nBins.value(); + for (int i = offset; i < preAxisDeltaPhi.nBins.value() + 1 - offset; i++) + edgesDeltaPhi.emplace_back(min + static_cast(i) * delta); + } + // ===] delta-eta [=== + if (!preAxisDeltaEta.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesDeltaEtaOrig.size()) - offset; i++) + edgesDeltaEta.emplace_back(edgesDeltaEtaOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesDeltaEtaOrig[0]; + double delta = (edgesDeltaEtaOrig[1] - edgesDeltaEtaOrig[0]) / preAxisDeltaEta.nBins.value(); + for (int i = offset; i < preAxisDeltaEta.nBins.value() + 1 - offset; i++) + edgesDeltaEta.emplace_back(min + static_cast(i) * delta); + } + // ===] pt assoc [=== + if (!preAxisPtAssoc.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesPtAssocOrig.size()) - offset; i++) + edgesPtAssoc.emplace_back(edgesPtAssocOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesPtAssocOrig[0]; + double delta = (edgesPtAssocOrig[1] - edgesPtAssocOrig[0]) / preAxisPtAssoc.nBins.value(); + for (int i = offset; i < preAxisPtAssoc.nBins.value() + 1 - offset; i++) + edgesPtAssoc.emplace_back(min + static_cast(i) * delta); + } + // ===] pt trigger [=== + if (!preAxisPtTrigger.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesPtTriggerOrig.size()) - offset; i++) + edgesPtTrigger.emplace_back(edgesPtTriggerOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesPtTriggerOrig[0]; + double delta = (edgesPtTriggerOrig[1] - edgesPtTriggerOrig[0]) / preAxisPtTrigger.nBins.value(); + for (int i = offset; i < preAxisPtTrigger.nBins.value() + 1 - offset; i++) + edgesPtTrigger.emplace_back(min + static_cast(i) * delta); + } + // ===] vtx Z [=== + if (!preAxisVtxZ.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesVtxZOrig.size()) - offset; i++) + edgesVtxZ.emplace_back(edgesVtxZOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesVtxZOrig[0]; + double delta = (edgesVtxZOrig[1] - edgesVtxZOrig[0]) / preAxisVtxZ.nBins.value(); + for (int i = offset; i < preAxisVtxZ.nBins.value() + 1 - offset; i++) + edgesVtxZ.emplace_back(min + static_cast(i) * delta); + } + // ===] mult percentile [=== + if (!preAxisMult.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesMultOrig.size()) - offset; i++) + edgesMult.emplace_back(edgesMultOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesMultOrig[0]; + double delta = (edgesMultOrig[1] - edgesMultOrig[0]) / preAxisMult.nBins.value(); + for (int i = offset; i < preAxisMult.nBins.value() + 1 - offset; i++) + edgesMult.emplace_back(min + static_cast(i) * delta); + } + // ===] multiplicity count [=== + if (!preAxisMultiplicity.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesMultiplicityOrig.size()) - offset; i++) + edgesMultiplicity.emplace_back(edgesMultiplicityOrig[i]); + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesMultiplicityOrig[0]; + double delta = (edgesMultiplicityOrig[1] - edgesMultiplicityOrig[0]) / preAxisMultiplicity.nBins.value(); + for (int i = offset; i < preAxisMultiplicity.nBins.value() + 1 - offset; i++) + edgesMultiplicity.emplace_back(min + static_cast(i) * delta); + } + + LOGF(info, "Initialized THnF axis delta-phi with %i bins.", edgesDeltaPhi.size() - 1); + LOGF(info, "Initialized THnF axis delta-eta with %i bins.", edgesDeltaEta.size() - 1); + LOGF(info, "Initialized THnF axis pTassoc with %i bins.", edgesPtAssoc.size() - 1); + LOGF(info, "Initialized THnF axis pTtrigger with %i bins.", edgesPtTrigger.size() - 1); + LOGF(info, "Initialized THnF axis vertex-Z with %i bins.", edgesVtxZ.size() - 1); + LOGF(info, "Initialized THnF axis mult cent with %i bins.", edgesMult.size() - 1); + LOGF(info, "Initialized THnF axis multiplicity with %i bins.", edgesMultiplicity.size() - 1); + + const AxisSpec axisDeltaPhiNDim{edgesDeltaPhi, "#Delta#varphi"}; + const AxisSpec axisDeltaEtaNDim{edgesDeltaEta, "#Delta#eta"}; + const AxisSpec axisPtAssocNDim{edgesPtAssoc, "#it{p}_{T}^{assoc} (GeV/c)"}; + const AxisSpec axisPtTriggerNDim{edgesPtTrigger, "#it{p}_{T}^{trigger} (GeV/c)"}; + const AxisSpec axisVtxZNDim{edgesVtxZ, "vertex Z (cm)"}; + const AxisSpec axisMultNDim{edgesMult, "mult percentile"}; + const AxisSpec axisMultiplicityNDim{edgesMultiplicity, "Multiplicity"}; + + if (doprocessMixedEventHPhisInBuffer || doprocessMixedEventHKstarsInBuffer) { + validCollisions.resize(histos.get(HIST("axes/hMultAxis"))->GetNbinsX() * histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX()); + for (size_t i = 0; i < validCollisions.size(); ++i) { + validCollisions[i].reserve(masterConfigurations.mixingParameter); + } + } + // Event-selection cut-flow, same cut set/order/labels as CollCutCounts in + // hResonanceCorrelationFilter.cxx, filled by isSelectedEvents() -- see + // eventQAOwnerIndex above for why only one process function fills it. + if (eventQAOwnerIndex != -1) { + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "kNoSameBunchPileup"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); + } + + // ======================================================================== + // SHARED QA HISTOGRAMS + // ======================================================================== + bool anySameEvent = doprocessSameEventHPhis || doprocessSameEventHKstars || doprocessSameEventHPions || doprocessSameEventHKaons || doprocessSameEventHHadrons; + bool anyMixedEvent = doprocessMixedEventHPhis || doprocessMixedEventHPhisInBuffer || doprocessMixedEventHKstars || doprocessMixedEventHKstarsInBuffer || doprocessMixedEventHPions || doprocessMixedEventHKaons || doprocessMixedEventHHadrons; + + if (qaSwitches.doMixingQAandEventQA && (anySameEvent || anyMixedEvent)) { + if (anySameEvent) + histos.add("MixingQA/hSECollisionBins", ";bin;Entries", kTH1F, {{140, -0.5, 139.5}}); + if (anyMixedEvent) { + histos.add("MixingQA/hMECollisionBins", ";bin;Entries", kTH1F, {{140, -0.5, 139.5}}); + histos.add("MixingQA/hMEpvz1", ";pvz;Entries", kTH1F, {{30, -15, 15}}); + histos.add("MixingQA/hMEpvz2", ";pvz;Entries", kTH1F, {{30, -15, 15}}); + histos.add("MixingQA/hMixingQA", "mixing QA", kTH1F, {{2, -0.5, 1.5}}); + } + histos.add("EventQA/hMult", "Multiplicity", kTH1F, {axesConfigurations.axisMult}); + histos.add("EventQA/hPvz", ";pvz;Entries", kTH1F, {{30, -15, 15}}); + } + + if (anySameEvent) { + histos.add("hTriggerAllSelectedEtaVsPt", "hTriggerAllSelectedEtaVsPt", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hTriggerPtResolution", ";p_{T}^{reconstructed} (GeV/c); p_{T}^{generated} (GeV/c)", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisPtQA}); + histos.add("hTrackEtaVsPtVsPhi", "hTrackEtaVsPtVsPhi", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add("hTriggerPrimaryEtaVsPt", "hTriggerPrimaryEtaVsPt", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + histos.add("hTriggerPrimaryEtaVsPt", "hTriggerPrimaryEtaVsPt", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + } + + // ======================================================================== + // MAIN CORRELATION LOOP (SE & ME) + // ======================================================================== + bool needsSignalBgCloneSE = false; + bool needsSignalBgCloneME = false; + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { + + bool doSE = false; + bool doME = false; + if (i == IndexPhi) { // Phi + doSE = doprocessSameEventHPhis; + doME = doprocessMixedEventHPhis || doprocessMixedEventHPhisInBuffer; + } else if (i == IndexKstar) { // K*0 + doSE = doprocessSameEventHKstars; + doME = doprocessMixedEventHKstars || doprocessMixedEventHKstarsInBuffer; + } else if (i == IndexPion) { // Pion + doSE = doprocessSameEventHPions; + doME = doprocessMixedEventHPions; + } else if (i == IndexKaon) { // Kaon + doSE = doprocessSameEventHKaons; + doME = doprocessMixedEventHKaons; + } else { // Hadron + doSE = doprocessSameEventHHadrons; + doME = doprocessMixedEventHHadrons; + } + + // Same-/mixed-event invariant-mass cross-check (Phi/K*0 only). Booked + // independent of doFullCorrelationStudy/fillCorrelationHistWithMass + // since these are standalone QA histograms, not part of the + // Signal/LeftBg/RightBg THnF family -- just need doSE/doME (the + // process switch(es) for this species) to actually get filled. + if (i == IndexPhi && doSE) { + histos.add("hPhiInvMass", "Phi candidate invariant mass, same event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doSE) { + histos.add("hKstarInvMass", "K*0 candidate invariant mass, same event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexPhi && doME) { + histos.add("hPhiMixedEventInvMass", "Phi candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisPhiMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + if (i == IndexKstar && doME) { + histos.add("hKstarMixedEventInvMass", "K*0 candidate invariant mass, mixed event", kTH3F, {axesConfigurations.axisKstarMass, axesConfigurations.axisPtAssoc, axesConfigurations.axisMult}); + } + + // Kinematic QA (Only for Phi) + if (i < IndexPion && (doSE || doME || doprocessPrediction || doprocessClosureTest)) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add(fmt::format("h{}EtaVsPtVsPhi", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add(fmt::format("h{}EtaVsPtVsPhiBg", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + } else { + histos.add(fmt::format("h{}EtaVsPtVsPhiVsCent", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + histos.add(fmt::format("h{}EtaVsPtVsPhiVsCentBg", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + histos.add(fmt::format("h3d{}Spectrum", Particlenames[i]).c_str(), fmt::format("h3d{}Spectrum", Particlenames[i]).c_str(), kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult, axesConfigurations.axisMassNSigma}); + histos.add(fmt::format("h3d{}SpectrumY", Particlenames[i]).c_str(), fmt::format("h3d{}SpectrumY", Particlenames[i]).c_str(), kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult, axesConfigurations.axisMassNSigma}); + + // REMOVED (2026-09-24): hITSClusters{Phi,Kstar0}{Positive,Negative}Daughter{Toward,Transverse}. + // These were registered but never filled anywhere in this file (confirmed by + // exhaustive grep for any fill using these names). The V0/cascade analog in + // hStrangeCorrelation.cxx fills the equivalent histograms with the DAUGHTER's + // ITS cluster count vs the V0's DECAY RADIUS (assoc.v0radius()), split into + // "Toward"/"Transverse" via a delta-phi cut using checks.towardDeltaEtaRange / + // checks.transwerseDeltaEtaRangeMin/Max -- but neither of those two things has + // an equivalent here: Phi/K*0 candidates are reconstructed at the primary + // vertex from track pairs, not displaced V0 decays, so there is no "decay + // radius" analog on AssocPhis/AssocKstars, and the towardDeltaEtaRange / + // transwerseDeltaEtaRangeMin/Max configurables that drive the region split + // don't exist in this task's `checks` struct at all (only doOnTheFlyFlattening + // does). Implementing this would mean inventing both a physical observable for + // the third axis and the region-cut configurables, so it's removed rather than + // guessed. If you want it back, tell me what the third axis should represent + // for a non-displaced daughter pair (e.g. a DCA quantity, or drop to a TH2), + // and I'll add the config fields and fill it properly. + } + + // Core Correlation THnFs + if (masterConfigurations.doFullCorrelationStudy) { + if (doSE) { + if (masterConfigurations.fillCorrelationHistWithMass && (i == IndexPhi || i == IndexKstar)) { + // Mass is ADDED as its own axis here, not swapped in for + // Delta-eta: the mixed-event acceptance correction needs + // Delta-eta (pair acceptance isn't flat in it), so it has to + // stay present even when mass is also kept for post-processing + // signal/sideband extraction. 7 axes total; see + // fillCorrelationsPhi/fillCorrelationsKstar's binFillThnMass + // for the matching fill order. + histos.add(fmt::format("sameEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, (i == IndexPhi ? axesConfigurations.axisPhiMass : axesConfigurations.axisKstarMass), axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } else { + histos.add(fmt::format("sameEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (qaSwitches.doDeltaPhiStarCheck) + histos.add(fmt::format("sameEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); + if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { + needsSignalBgCloneSE = true; + } + } + + if (doME) { + if (masterConfigurations.fillCorrelationHistWithMass && (i == IndexPhi || i == IndexKstar)) { + // Same 7-axis layout as the sameEvent/Signal booking above -- + // must match, since CorrelationBuilder-style same/mixed + // acceptance correction requires identical axis definitions on + // both sides. + histos.add(fmt::format("mixedEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, (i == IndexPhi ? axesConfigurations.axisPhiMass : axesConfigurations.axisKstarMass), axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } else { + histos.add(fmt::format("mixedEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (qaSwitches.doDeltaPhiStarCheck) + histos.add(fmt::format("mixedEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); + if ((i == IndexPhi || i == IndexKstar) && !masterConfigurations.fillCorrelationHistWithMass) { + needsSignalBgCloneME = true; + } + } + } + } + } + // Clone Signal/ into LeftBg/ and RightBg/ once, after all species for this + // loop have booked their Signal/ histograms (Phi and/or K*0 use sideband + // subtraction; avoids cloning the same prefix twice, which the histogram + // registry does not allow). + if (needsSignalBgCloneSE) { + histos.addClone("sameEvent/Signal/", "sameEvent/LeftBg/"); + histos.addClone("sameEvent/Signal/", "sameEvent/RightBg/"); + } + if (needsSignalBgCloneME) { + histos.addClone("mixedEvent/Signal/", "mixedEvent/LeftBg/"); + histos.addClone("mixedEvent/Signal/", "mixedEvent/RightBg/"); + } + + // ======================================================================== + // TRIGGER AND ASSOC SPECIFIC QA + // ======================================================================== + if (TESTBIT(doCorrelation, IndexPhi) && doprocessSameEventHPhis && masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesPhi", "TriggersPhi", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + histos.add("hNumberOfRejectedPairsPhi", "hNumberOfRejectedPairsPhi", kTH1F, {{1, 0, 1}}); + } + + if (TESTBIT(doCorrelation, IndexKstar) && doprocessSameEventHKstars && masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesKstar0", "TriggersKstar0", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + histos.add("hNumberOfRejectedPairsKstar0", "hNumberOfRejectedPairsKstar0", kTH1F, {{1, 0, 1}}); + } + + if (TESTBIT(doCorrelation, IndexPion) && doprocessSameEventHPions) { + if (masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesPion", "TriggersPion", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + histos.add("hNumberOfRejectedPairsPion", "hNumberOfRejectedPairsPion", kTH1F, {{1, 0, 1}}); + histos.add("hPionEtaVsPtAllSelected", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hPionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hPositivePionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hNegativePionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + + if (TESTBIT(doCorrelation, IndexKaon) && doprocessSameEventHKaons) { + if (masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesKaon", "TriggersKaon", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + histos.add("hNumberOfRejectedPairsKaon", "hNumberOfRejectedPairsKaon", kTH1F, {{1, 0, 1}}); + histos.add("hKaonEtaVsPtAllSelected", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hKaonEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hPositiveKaonEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hNegativeKaonEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + + if (TESTBIT(doCorrelation, IndexHadron) && doprocessSameEventHHadrons) { + if (masterConfigurations.doFullCorrelationStudy) { + histos.add("sameEvent/TriggerParticlesHadron", "TriggersHadron", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + histos.add("hNumberOfRejectedPairsHadron", "hNumberOfRejectedPairsHadron", kTH1F, {{1, 0, 1}}); + histos.add("hDCAzTriggerHadron", "hDCAzTriggerHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAxyTriggerHadron", "hDCAxyTriggerHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAzAssociatedHadron", "hDCAzAssociatedHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAxyAssociatedHadron", "hDCAxyAssociatedHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hAsssocTrackEtaVsPtVsPhi", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("hAssocPrimaryEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hAssocHadronsAllSelectedEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("hAssocPtResolution", ";p_{T}^{reconstructed} (GeV/c); p_{T}^{generated} (GeV/c)", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisPtQA}); + } + + LOGF(info, "Init THnFs done"); + + // ======================================================================== + // MC GENERATED, CLOSURE TEST, AND PREDICTION + // ======================================================================== + if (doprocessMCGenerated) { + histos.add("hGeneratedQAPtTrigger", "hGeneratedQAPtTrigger", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + histos.add("hGeneratedQAPtAssociatedPhi", "hGeneratedQAPtAssociatedPhi", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + histos.add("hClosureTestEventCounter", "hClosureTestEventCounter", kTH1F, {{10, 0, 10}}); + + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add("Generated/hTrigger", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + histos.add("Generated/hTrigger", "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + histos.add("Generated/hPositiveTrigger", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("Generated/hNegativeTrigger", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { // Fixed missing check here + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.add(fmt::format("Generated/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + histos.add(fmt::format("Generated/h{}", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); + } + if (i == IndexPion) { + histos.add(fmt::format("Generated/hPositive{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add(fmt::format("Generated/hNegative{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + } + } + histos.addClone("Generated/", "GeneratedWithPV/"); + + for (int i = 0; i < AssocParticleTypesNoHadron; i++) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("GeneratedWithPV/h{}_MidYVsMult", Particlenames[i]).c_str(), "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + histos.add(fmt::format("GeneratedWithPV/h{}_MidYVsMult_TwoPVsOrMore", Particlenames[i]).c_str(), "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } + } + } + + if (doprocessClosureTest) { + if (!doprocessMCGenerated) + histos.add("hClosureTestEventCounter", "hClosureTestEventCounter", kTH1F, {{10, 0, 10}}); + histos.add("hClosureQAPtTrigger", "hClosureQAPtTrigger", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + histos.add("hClosureQAPtAssociatedPhi", "hClosureQAPtAssociatedPhi", kTH2F, {axesConfigurations.axisPtQA, {5, -0.5f, 4.5f}}); + + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("ClosureTest/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add(fmt::format("ClosureTest/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + } + } + histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + + if (doprocessPrediction) { + mCounter.mPdgDatabase = pdgDB.service; + mCounter.mSelectPrimaries = mcSelections.doAssocPhysicalPrimary.value; + histos.add("Prediction/hEventSelection", "hEventSelection", kTH1F, {{3, 0, 3}}); + TString eventSelLabel[] = {"Read", "INELgt0", "|Z|<10"}; + for (int i = 1; i <= histos.get(HIST("Prediction/hEventSelection"))->GetNbinsX(); i++) { + histos.get(HIST("Prediction/hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); + } + if (masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.add("Prediction/hTriggerFT0A", "Trigger Tracks FT0A", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("Prediction/hTriggerFT0C", "Trigger Tracks FT0C", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + histos.add("Prediction/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.add("Prediction/hTriggerFT0A", "Trigger Tracks FT0A", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0AvsNchEta08", "Nch in 0.8 vs FT0A multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0AvsNchEta05", "Nch in 0.5 vs FT0A multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hTriggerFT0C", "Trigger Tracks FT0C", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0CvsNchEta08", "Nch in 0.8 vs FT0C multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0CvsNchEta05", "Nch in 0.5 vs FT0C multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + } + histos.add("Prediction/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0MvsNchEta08", "Nch in 0.8 vs FT0M multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0MvsNchEta05", "Nch in 0.5 vs FT0M multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + } + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("Prediction/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + if (masterConfigurations.useCentralityinPrediction) { + histos.add(fmt::format("Prediction/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } else { + histos.add(fmt::format("Prediction/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultiplicityNDim}); + } + } + } + if (masterConfigurations.doSeparateFT0Prediction) { + histos.addClone("Prediction/sameEvent/", "Prediction/sameEventFT0A/"); + histos.addClone("Prediction/sameEvent/", "Prediction/sameEventFT0C/"); + } + } + + // visual inspection of sizes + histos.print(); + + // initialize CCDB *only* if efficiency correction requested + // skip if not requested, saves a bit of time + if (efficiencyFlags.applyEfficiencyCorrection) { + ccdb->setURL(ccdbConfigurations.ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + } + } + + // Single event-selection predicate, cut-for-cut and bin-for-bin the same as + // isSelectedEvents() in hResonanceCorrelationFilter.cxx, so a collision is + // selected identically whether the filter task is producing its tables or + // this task is correlating them. Replaces the former isisCollisionSelect() + // (pp) / isisCollisionSelectPbPb() (Pb-Pb) pair, which carried their own, + // different cut lists (centrality-range, INEL>0, ITS-good-layers, TPC + // occupancy) that could disagree with the filter task's selection. + // + // fillHist controls the CollCutCounts cut-flow and EventQA/hMult+hPvz + // fills: pass true only from the one process function that "owns" this + // collision for QA purposes (see eventQAOwnerIndex in init()) so a + // collision examined by several enabled same-event process functions is + // still only counted once. Mixed-event callers always pass false, since ME + // revisits the same collisions many times per mixing pool and has its own + // separate MixingQA histograms. + template + bool isSelectedEvents(TCollision const& collision, bool fillHist = true) + { + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) { + return true; + } + if (!condition) { + return false; + } + if (fillHist) { + histos.fill(HIST("CollCutCounts"), bin); + } + return true; + }; + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 0); + } + + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 5)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoSameBunchPileupCut, + collision.selection_bit(aod::evsel::kNoSameBunchPileup), 6)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtGoodZvtxFT0vsPVCut, + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), 7)) { + return false; + } + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 8); + histos.fill(HIST("EventQA/hMult"), collision.centFT0M()); + histos.fill(HIST("EventQA/hPvz"), collision.posZ()); + } + + return true; + } + + template + bool isValidTrigger(TTrack const& track, bool isLeading) + { + if (track.tpcNClsCrossedRows() < configTriggerTracks.minTPCNCrossedRowsTrigger) { + return false; // crossed rows + } + if (!track.hasITS() && configTriggerTracks.triggerRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + if (track.tpcNClsShared() > configTriggerTracks.triggerMaxTPCSharedClusters) { + return false; // skip, has shared clusters + } + if (!(TESTBIT(track.itsClusterMap(), 0)) && configTriggerTracks.triggerRequireL0) { + return false; // skip, doesn't have cluster in ITS L0 + } + // systematic variations: trigger DCAxy + if (std::abs(track.dcaXY()) > configTriggerTracks.dcaXYconstant + configTriggerTracks.dcaXYpTdep * std::abs(track.signed1Pt())) { + return false; + } + // systematic variations: trigger DCAz + if (checks.requireDCAzCut && std::abs(track.dcaZ()) > configTriggerTracks.dcaZconstant + configTriggerTracks.dcaZpTdep * std::abs(track.signed1Pt())) { + return false; + } + if (track.pt() > axisRanges[3][1] || track.pt() < axisRanges[3][0]) { + return false; + } + if (triggerSelection.triggerParticleCharge > 0 && track.sign() < 0) { + return false; + } + if (triggerSelection.triggerParticleCharge < 0 && track.sign() > 0) { + return false; + } + if (triggerSelection.useTheLeadingParticleAsTrigger && !isLeading) { + return false; + } + return true; + } + template + bool isValidAssocHadron(TTrack const& track) + { + if (track.tpcNClsCrossedRows() < configAssocTracks.minTPCNCrossedRowsAssociated) { + return false; // crossed rows + } + if (!track.hasITS() && configAssocTracks.assocRequireITS) { + return false; // skip, doesn't have ITS signal (skips lots of TPC-only!) + } + if (track.tpcNClsShared() > configAssocTracks.assocMaxTPCSharedClusters) { + return false; // skip, has shared clusters + } + if (!(TESTBIT(track.itsClusterMap(), 0)) && configAssocTracks.assocRequireL0) { + return false; // skip, doesn't have cluster in ITS L0 + } + // systematic variations: trigger DCAxy + if (std::abs(track.dcaXY()) > configAssocTracks.dcaXYconstantAssoc + configAssocTracks.dcaXYpTdepAssoc * std::abs(track.signed1Pt())) { + return false; + } + // systematic variations: trigger DCAz + if (checks.requireDCAzCut && std::abs(track.dcaZ()) > configAssocTracks.dcaZconstantAssoc + configAssocTracks.dcaZpTdepAssoc * std::abs(track.signed1Pt())) { + return false; + } + if (track.pt() > axisRanges[2][1] || track.pt() < axisRanges[2][0]) { + return false; + } + return true; + } + + double calculateAverageDeltaPhiStar(const double* trigg, const double* assoc, double B) + { + double dPhiStar = 0; + double dPhiStarMean = 0; + + double dPhi = assoc[0] - trigg[0]; + double phaseProton = (-0.3 * B * assoc[2]) / (2 * assoc[1]); + double phaseTrack = (-0.3 * B * trigg[2]) / (2 * trigg[1]); + + constexpr double kRadiusStep = 0.05; + int nSteps = static_cast((MaxRadiusTPC - MinRadiusTPC) / kRadiusStep); + for (int iR = 0; iR <= nSteps; ++iR) { + double r = MinRadiusTPC + iR * kRadiusStep; + dPhiStar = dPhi + std::asin(phaseProton * r) - std::asin(phaseTrack * r); + dPhiStarMean += (dPhiStar / 34); + } + + return dPhiStarMean; + } + void fillTriggerHistogram(std::shared_ptr const& hist, double pt, double mult, float eff, float effUncert, float purity, float purityErr) + { + int binx = hist->GetXaxis()->FindBin(pt); + int biny = hist->GetYaxis()->FindBin(mult); + float previousContent = hist->GetBinContent(binx, biny); + float previousUncert = hist->GetBinError(binx, biny); + float newContent = previousContent + purity / eff; + float newUncert = std::sqrt(previousUncert * previousUncert + std::pow(purity / eff, 2) + std::pow(purityErr / eff, 2) + std::pow(effUncert, 2) / std::pow(eff, 4)); + hist->SetBinContent(binx, biny, newContent); + hist->SetBinError(binx, biny, newUncert); + } + void fillCorrelationHistogram(std::shared_ptr const& hist, double binFillThn[], float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) + { + float previousContent, previousError2, currentContent, currentError2; + int bin = hist->GetBin(binFillThn); + previousContent = hist->GetBinContent(bin); + previousError2 = hist->GetBinError2(bin); + currentContent = previousContent + etaWeight * purity / (efficiency); + currentError2 = previousError2 + std::pow(etaWeight * purity / (efficiency), 2) + std::pow(etaWeight * totalPurityUncert / (efficiency), 2) + std::pow(totalEffUncert * purity * etaWeight, 2) / std::pow(efficiency, 4); + hist->SetBinContent(bin, currentContent); + hist->SetBinError2(bin, currentError2); + } + + // Species is an explicit compile-time tag (IndexPion, IndexKaon or + // IndexHadron) rather than inferred from column presence on THadrons: + // Pion's and Kaon's assoc tables are both Join + // (AssocPID carries all four species' nSigmas generically), so a + // `requires { assocTrack.nSigmaTPCPi(); }` check can no longer tell them + // apart the way it could tell Pion apart from the plain (no-PID) Hadron + // table. Only Hadron's THadrons type lacks PID columns at all. + template + void fillCorrelationsHadron(TTriggers const& triggers, THadrons const& assocs, bool mixing, float pvz, float mult, double bField) + { + static_assert(Species == IndexPion || Species == IndexKaon || Species == IndexHadron, + "fillCorrelationsHadron: Species must be IndexPion, IndexKaon or IndexHadron"); + + for (auto const& triggerTrack : triggers) { + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + auto trigg = triggerTrack.template track_as(); + if (!isValidTrigger(trigg, triggerTrack.isLeading())) + continue; + + float efficiencyTrigger = 1.0f; + float efficiencyTriggerError = 0.0f; + float purityTrigger = 1.0f; + float purityTriggerError = 0.0f; + if (efficiencyFlags.applyEfficiencyForTrigger) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiencyTrigger = hEfficiencyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); + else + efficiencyTrigger = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purityTrigger = hPurityHadronMult->Interpolate(trigg.pt(), mult); + else + purityTrigger = hPurityHadron->Interpolate(trigg.pt()); + } + if (efficiencyFlags.applyEfficiencyPropagation) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiencyTriggerError = hEfficiencyUncertaintyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); + else + efficiencyTriggerError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purityTriggerError = hPurityUncertaintyHadronMult->Interpolate(trigg.pt(), mult); + else + purityTriggerError = hPurityUncertaintyHadron->Interpolate(trigg.pt()); + } + } + if (efficiencyTrigger == 0) { // check for zero efficiency, do not apply if the case + efficiencyTrigger = 1; + efficiencyTriggerError = 0; + } + } + if (!mixing) { + if constexpr (Species == IndexPion) + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesPion")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); + else if constexpr (Species == IndexKaon) + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesKaon")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); + else + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesHadron")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); + } + double triggSign = trigg.sign(); + const double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + for (auto const& assocTrack : assocs) { + auto assoc = assocTrack.template track_as(); + + //---] removing autocorrelations [--- + if (checks.doAutocorrelationRejection) { + if (trigg.globalIndex() == assoc.globalIndex()) { + if constexpr (Species == IndexPion) + histos.fill(HIST("hNumberOfRejectedPairsPion"), 0.5); + else if constexpr (Species == IndexKaon) + histos.fill(HIST("hNumberOfRejectedPairsKaon"), 0.5); + else + histos.fill(HIST("hNumberOfRejectedPairsHadron"), 0.5); + continue; + } + } + //---] track quality check [--- + if (!isValidAssocHadron(assoc)) + continue; + if (mcSelections.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + continue; + } + float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); + float deltaeta = trigg.eta() - assoc.eta(); + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + float ptassoc = assoc.pt(); + float pttrigger = trigg.pt(); + + double assocSign = assoc.sign(); + const double assocForDeltaPhiStar[] = {assoc.phi(), assoc.pt(), assocSign}; + + float etaWeight = 1.; + if (checks.doOnTheFlyFlattening) { + float preWeight = 1 - std::abs(deltaeta) / 1.6; + etaWeight = preWeight != 0 ? 1.0f / preWeight : 1.0f; + } + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + + float efficiency = 1; + float purity = 1.0f; + float purityUncertainty = 0.0f; + float totalEffUncert = 0.0; + float efficiencyUncertainty = 0.0f; + float totalPurityUncert = 0.0; + if (efficiencyFlags.applyEfficiencyCorrection) { + if constexpr (Species == IndexPion) { + efficiency = hEfficiencyPion->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyEfficiencyPropagation) + efficiencyUncertainty = hEfficiencyUncertaintyPion->Interpolate(ptassoc, assoc.eta()); + } else if constexpr (Species == IndexKaon) { + efficiency = hEfficiencyKaon->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyEfficiencyPropagation) + efficiencyUncertainty = hEfficiencyUncertaintyKaon->Interpolate(ptassoc, assoc.eta()); + } else { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiency = hEfficiencyHadronMult->Interpolate(ptassoc, assoc.eta(), mult); + else + efficiency = hEfficiencyHadron->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyPurityHadron) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purity = hPurityHadronMult->Interpolate(ptassoc, mult); + else + purity = hPurityHadron->Interpolate(ptassoc); + } + if (efficiencyFlags.applyEfficiencyPropagation) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + efficiencyUncertainty = hEfficiencyUncertaintyHadronMult->Interpolate(ptassoc, assoc.eta(), mult); + else + efficiencyUncertainty = hEfficiencyUncertaintyHadron->Interpolate(ptassoc, assoc.eta()); + if (efficiencyFlags.applyPurityHadron) { + if (efficiencyFlags.applyEffAsFunctionOfMult) + purityUncertainty = hPurityUncertaintyHadronMult->Interpolate(ptassoc, mult); + else + purityUncertainty = hPurityUncertaintyHadron->Interpolate(ptassoc); + } + } + } + } + if (efficiency == 0) { // check for zero efficiency, do not apply if the case + efficiency = 1; + efficiencyUncertainty = 0.0; + } + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigger * efficiencyUncertainty, 2) + std::pow(efficiencyTriggerError * efficiency, 2)); + totalPurityUncert = std::sqrt(std::pow(purityTrigger * purityUncertainty, 2) + std::pow(purity * purityTriggerError, 2)); + } + + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); + if (!mixing) { + if constexpr (Species == IndexPion) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Pion")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); + } else if (qaSwitches.doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Pion") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); + } + } else if constexpr (Species == IndexKaon) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kaon")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); + } else if (qaSwitches.doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Kaon") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); + } + } else { + if (triggSign == assocSign && qaSwitches.doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), 0.5); + } else if (qaSwitches.doDeltaPhiStarCheck) { + histos.fill(HIST("sameEvent/Signal/Hadron") + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - assoc.eta(), -0.5); + } + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Hadron")), binFillThn, etaWeight, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } + } else { + if constexpr (Species == IndexPion) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Pion")), binFillThn, 1, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } else if constexpr (Species == IndexKaon) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Kaon")), binFillThn, 1, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Hadron")), binFillThn, 1, efficiency * efficiencyTrigger, totalEffUncert, purity * purityTrigger, totalPurityUncert); + } + } + } + } + } + + // ---- OPTIMIZED (2026-09-24): daughter-track resolution, efficiency lookup and + // mass-window classification hoisted out of the trigger loop; see comment block + // originally delivered in fillCorrelationsPhi_Kstar_optimized.cxx for rationale ---- + void fillCorrelationsPhi(aod::TriggerTracks const& triggers, aod::AssocPhis const& assocs, bool mixing, bool mixingInBf, float pvz, float mult, double bField) + { + ValidCollision currentCollision; + int binMult = 0; + int nBinsMult = 0; + int nBinsVtxZ = 0; + int binVtxZ = 0; + currentCollision.pvz = pvz; + currentCollision.mult = mult; + if (mixingInBf) { + nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); + binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; + nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); + binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; + } + + // --------------------------------------------------------------------- + // Pre-resolve everything that does NOT depend on the trigger, once per + // assoc candidate instead of once per (trigger, assoc) pair. + // --------------------------------------------------------------------- + struct CachedPhi { + float phi, eta, pt, mass; + float phiKplus, etaKplus, ptKplus, signKplus; + float phiKminus, etaKminus, ptKminus, signKminus; + int32_t globalIndexPos, globalIndexNeg; + float efficiency, efficiencyError; + bool isLeftBg, isSignal, isRightBg; + bool passesMcSelection; // (!doMCassociation || mcTruePhi()) && (!mcSelections.doAssocPhysicalPrimary || mcPhysicalPrimary()) + }; + std::vector cache; + cache.reserve(assocs.size()); + for (auto const& assocCandidate : assocs) { + auto postrack = assocCandidate.posTrackDaughter_as(); + auto negtrack = assocCandidate.negTrackDaughter_as(); + + CachedPhi c; + c.phi = assocCandidate.phi(); + c.eta = assocCandidate.eta(); + c.pt = assocCandidate.pt(); + c.mass = assocCandidate.mass(); + + c.phiKplus = postrack.phi(); + c.etaKplus = postrack.eta(); + c.ptKplus = postrack.pt(); + c.signKplus = postrack.sign(); + c.phiKminus = negtrack.phi(); + c.etaKminus = negtrack.eta(); + c.ptKminus = negtrack.pt(); + c.signKminus = negtrack.sign(); + c.globalIndexPos = postrack.globalIndex(); + c.globalIndexNeg = negtrack.globalIndex(); + + c.efficiency = 1.0f; + c.efficiencyError = 0.0f; + if (efficiencyFlags.applyEfficiencyCorrection) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + c.efficiency = hEfficiencyPhi->Interpolate(c.pt, c.eta); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyUncertaintyPhi->Interpolate(c.pt, c.eta); + } else { + double bin[4] = {c.pt, c.eta, c.phi, mult}; + c.efficiency = hEfficiencyPhiMultVsPhi->GetBinContent(hEfficiencyPhiMultVsPhi->GetBin(bin)); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyPhiMultVsPhi->GetBinError(hEfficiencyPhiMultVsPhi->GetBin(bin)); + } + } + + // Peak-relative classification using Phi's own mass window. + float delta = c.mass - massWindowConfigurationsPhi.peakMass; + float sig = massWindowConfigurationsPhi.sigma; + c.isLeftBg = (-massWindowConfigurationsPhi.maxBgNSigma * sig < delta && delta < -massWindowConfigurationsPhi.minBgNSigma * sig); + c.isSignal = (-massWindowConfigurationsPhi.maxPeakNSigma * sig < delta && delta < massWindowConfigurationsPhi.maxPeakNSigma * sig); + c.isRightBg = (massWindowConfigurationsPhi.minBgNSigma * sig < delta && delta < massWindowConfigurationsPhi.maxBgNSigma * sig); + + c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTruePhi()) && + (!mcSelections.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + + // Candidate-level kinematic/spectrum QA, ported from the equivalent + // block in hStrangeCorrelation.cxx (h3dSpectrum[Y], + // hEtaVsPtVsPhi[Bg]). These are registered (histos.add(...) + // above) but only filled here. Filled once per candidate per + // SAME EVENT only (never during mixing -- these are candidate QA, not + // correlation-pair QA, so filling them again per mixed pair would + // overcount each candidate by the mixing-pool depth). + // "SpectrumY" mirrors the V0 analog's rapidity cut (std::abs(rapidity) < + // phaseSpaceSelections.ySel); AssocPhis has no exposed rapidity() accessor in this file, so + // pseudorapidity (c.eta) is used as a stand-in -- swap in a real + // rapidity() call here if/when the table exposes one. + if (!mixing) { + float nSigmaVal = delta / sig; + histos.fill(HIST("h3dPhiSpectrum"), c.pt, mult, nSigmaVal); + if (std::abs(c.eta) < phaseSpaceSelections.ySel) { + histos.fill(HIST("h3dPhiSpectrumY"), c.pt, mult, nSigmaVal); + } + // matches the two registration branches above (TH3F vs THnF-with-mult, + // selected by efficiencyFlags.applyEffAsFunctionOfMultAndPhi) + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + if (c.isSignal) { + histos.fill(HIST("hPhiEtaVsPtVsPhi"), c.pt, c.eta, c.phi); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hPhiEtaVsPtVsPhiBg"), c.pt, c.eta, c.phi); + } + } else { + if (c.isSignal) { + histos.fill(HIST("hPhiEtaVsPtVsPhiVsCent"), c.pt, c.eta, c.phi, mult); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hPhiEtaVsPtVsPhiVsCentBg"), c.pt, c.eta, c.phi, mult); + } + } + } + + cache.push_back(c); + } + // --------------------------------------------------------------------- + + bool firstLoop = false; + for (auto const& triggerTrack : triggers) { + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + auto trigg = triggerTrack.track_as(); + if (!isValidTrigger(trigg, triggerTrack.isLeading())) + continue; + + float efficiencyTrigg = 1.0f; + float efficiencyTriggError = 0.0f; + float purityTrigg = 1.0f; + float purityTriggErr = 0.0; + double bintrig[4] = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; + + if (efficiencyFlags.applyEfficiencyForTrigger) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + efficiencyTrigg = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTrigg = hPurityHadron->Interpolate(trigg.pt()); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTriggErr = hPurityHadron->Interpolate(trigg.pt()); + } + } else { + efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + } + } + if (efficiencyTrigg == 0) { + efficiencyTrigg = 1; + efficiencyTriggError = 0; + } + } + + if (!mixing) { + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesPhi")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + } + + double triggSign = trigg.sign(); + const double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + + if (mixingInBf) { + currentCollision.addValidParticle(trigg.eta(), trigg.phi(), trigg.pt(), -1, efficiencyTrigg, efficiencyTriggError, -1); + if (firstLoop) + continue; + } + + for (auto const& c : cache) { + firstLoop = true; + + //---] removing autocorrelations [--- + if (checks.doAutocorrelationRejection) { + if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { + histos.fill(HIST("hNumberOfRejectedPairsPhi"), 0.5); + continue; + } + } + + float deltaphi = computeDeltaPhi(trigg.phi(), c.phi); + float deltaeta = trigg.eta() - c.eta; + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + float ptassoc = c.pt; + float pttrigger = trigg.pt(); + float massassoc = c.mass; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + + float etaWeight = 1; + if (checks.doOnTheFlyFlattening) { + float preWeight = 1 - std::abs(deltaeta) / 1.6; + etaWeight = preWeight != 0 ? 1.0f / preWeight : 1.0f; + } + + float totalEffUncert = 0.0f; + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * c.efficiencyError, 2) + std::pow(efficiencyTriggError * c.efficiency, 2)); + } + + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + // 7-slot layout matching the mass-mode Signal booking above: mass + // ADDED after Delta-eta, everything else shifted one slot right. + double binFillThnMass[7] = {deltaphi, deltaeta, massassoc, ptassoc, pttrigger, pvz, mult}; + + const double assocForDeltaPhiStarPlus[] = {c.phiKplus, c.ptKplus, c.signKplus}; + const double assocForDeltaPhiStarMinus[] = {c.phiKminus, c.ptKminus, c.signKminus}; + + if (!c.passesMcSelection) + continue; + + if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (qaSwitches.doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, +0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, -0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, +0.5); + } + } + } + if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (qaSwitches.doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, +0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, -0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, +0.5); + } + } + } + if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Phi")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (qaSwitches.doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, +0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaKplus, -0.5); + histos.fill(HIST("sameEvent/Signal/PhiDeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaKminus, +0.5); + } + } + } + + // Mixed Event Logic buffered in ValidCollision. LeftBg/RightBg are + // only booked when !fillCorrelationHistWithMass (see booking above), + // so both are skipped entirely in mass mode -- filling/replaying into + // an unbooked histogram would abort. Signal carries a real mass + // value through the buffer (ValidParticle::mass) so the replay loop + // below can build the correct 7-slot fill array in mass mode too. + if (mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 0, c.efficiency, c.efficiencyError, 0 /* 0 = Phi type */); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Phi")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 1, c.efficiency, c.efficiencyError, 0, c.mass); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 2, c.efficiency, c.efficiencyError, 0); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Phi")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + } + } + + // Process Buffer for Mixed Events -- unchanged from original. + if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + return; + + int binnumb = binMult * nBinsVtxZ + binVtxZ; + int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; + if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || + (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 3 && currentCollision.trigParticles.empty() && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) + return; + + for (const auto& collision : validCollisions[binnumb]) { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + + for (const auto& trigger : collision.trigParticles) { + for (const auto& assoc : currentCollision.assocParticles) { + float deltaeta = trigger.eta - assoc.eta; + float deltaphi = computeDeltaPhi(trigger.phi, assoc.phi); + float efficiencyTrigg = trigger.efficiency; + float efficiencyAssoc = assoc.efficiency; + float efficiencyTriggError = trigger.efficiencyError; + float efficiencyAssocError = assoc.efficiencyError; + float totalEffUncert = 0.0; + float ptassoc = assoc.pt; + float pttrigger = trigger.pt; + + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyAssocError, 2) + std::pow(efficiencyTriggError * efficiencyAssoc, 2)); + } + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + double binFillThnMass[7] = {deltaphi, deltaeta, assoc.mass, ptassoc, pttrigger, pvz, mult}; + + // region 0/2 (LeftBg/RightBg) are never buffered in mass mode (see + // the buffering guard above), so no separate guard is needed here + // beyond region itself never being 0/2 in that case; region 1 + // (Signal) picks the 7-slot mass array when mass mode is on, to + // match the booking above. + if (assoc.region == 0) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Phi")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 1) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Phi")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 2) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Phi")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + } + } + } + + if (validCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter)) { + validCollisions[binnumb].erase(validCollisions[binnumb].begin()); + } + if (!currentCollision.trigParticles.empty()) + validCollisions[binnumb].push_back(currentCollision); + } + + // ============================================================================ + // fillCorrelationsKstar -- identical restructuring, mirrors fillCorrelationsPhi + // exactly (K*0 has no separate LeftBg/Signal/RightBg physics difference from + // Phi in this file -- same three-region sideband pattern, same daughter-pair + // caching opportunity). mcTruePhi() -> mcTrueKstar() is the only selection + // difference, and hEfficiencyPhi* -> hEfficiencyKstar* for the efficiency + // histograms. + // ============================================================================ + + void fillCorrelationsKstar(aod::TriggerTracks const& triggers, aod::AssocKstars const& assocs, bool mixing, bool mixingInBf, float pvz, float mult, double bField) + { + ValidCollision currentCollision; + int binMult = 0; + int nBinsMult = 0; + int nBinsVtxZ = 0; + int binVtxZ = 0; + currentCollision.pvz = pvz; + currentCollision.mult = mult; + if (mixingInBf) { + nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); + binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; + nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); + binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; + } + + // --------------------------------------------------------------------- + // Same pre-pass as fillCorrelationsPhi -- this matters most for K*0, + // since assocKstars typically has far more candidates per event than + // assocPhis, so the N_trig-fold redundant daughter-track resolution is + // multiplied by a much bigger candidate count here than for Phi. + // --------------------------------------------------------------------- + struct CachedKstar { + float phi, eta, pt, mass; + float phiPos, etaPos, ptPos, signPos; + float phiNeg, etaNeg, ptNeg, signNeg; + int32_t globalIndexPos, globalIndexNeg; + float efficiency, efficiencyError; + bool isLeftBg, isSignal, isRightBg; + bool passesMcSelection; // (!doMCassociation || mcTrueKstar()) && (!mcSelections.doAssocPhysicalPrimary || mcPhysicalPrimary()) + }; + std::vector cache; + cache.reserve(assocs.size()); + for (auto const& assocCandidate : assocs) { + auto postrack = assocCandidate.posTrackDaughter_as(); + auto negtrack = assocCandidate.negTrackDaughter_as(); + + CachedKstar c; + c.phi = assocCandidate.phi(); + c.eta = assocCandidate.eta(); + c.pt = assocCandidate.pt(); + c.mass = assocCandidate.mass(); + + c.phiPos = postrack.phi(); + c.etaPos = postrack.eta(); + c.ptPos = postrack.pt(); + c.signPos = postrack.sign(); + c.phiNeg = negtrack.phi(); + c.etaNeg = negtrack.eta(); + c.ptNeg = negtrack.pt(); + c.signNeg = negtrack.sign(); + c.globalIndexPos = postrack.globalIndex(); + c.globalIndexNeg = negtrack.globalIndex(); + + c.efficiency = 1.0f; + c.efficiencyError = 0.0f; + if (efficiencyFlags.applyEfficiencyCorrection) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + c.efficiency = hEfficiencyKstar->Interpolate(c.pt, c.eta); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyUncertaintyKstar->Interpolate(c.pt, c.eta); + } else { + double bin[4] = {c.pt, c.eta, c.phi, mult}; + c.efficiency = hEfficiencyKstarMultVsPhi->GetBinContent(hEfficiencyKstarMultVsPhi->GetBin(bin)); + if (efficiencyFlags.applyEfficiencyPropagation) + c.efficiencyError = hEfficiencyKstarMultVsPhi->GetBinError(hEfficiencyKstarMultVsPhi->GetBin(bin)); + } + } + + // Peak-relative classification using K*0's own mass window. + float delta = c.mass - massWindowConfigurationsKstar.peakMass; + float sig = massWindowConfigurationsKstar.sigma; + c.isLeftBg = (-massWindowConfigurationsKstar.maxBgNSigma * sig < delta && delta < -massWindowConfigurationsKstar.minBgNSigma * sig); + c.isSignal = (-massWindowConfigurationsKstar.maxPeakNSigma * sig < delta && delta < massWindowConfigurationsKstar.maxPeakNSigma * sig); + c.isRightBg = (massWindowConfigurationsKstar.minBgNSigma * sig < delta && delta < massWindowConfigurationsKstar.maxBgNSigma * sig); + + c.passesMcSelection = (!masterConfigurations.doMCassociation || assocCandidate.mcTrueKstar()) && + (!mcSelections.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()); + + // Same candidate-level Spectrum/EtaVsPtVsPhi QA as fillCorrelationsPhi + // above -- see the comment there for rationale. + if (!mixing) { + float nSigmaVal = delta / sig; + histos.fill(HIST("h3dKstar0Spectrum"), c.pt, mult, nSigmaVal); + if (std::abs(c.eta) < phaseSpaceSelections.ySel) { + histos.fill(HIST("h3dKstar0SpectrumY"), c.pt, mult, nSigmaVal); + } + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + if (c.isSignal) { + histos.fill(HIST("hKstar0EtaVsPtVsPhi"), c.pt, c.eta, c.phi); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hKstar0EtaVsPtVsPhiBg"), c.pt, c.eta, c.phi); + } + } else { + if (c.isSignal) { + histos.fill(HIST("hKstar0EtaVsPtVsPhiVsCent"), c.pt, c.eta, c.phi, mult); + } + if (c.isLeftBg || c.isRightBg) { + histos.fill(HIST("hKstar0EtaVsPtVsPhiVsCentBg"), c.pt, c.eta, c.phi, mult); + } + } + } + + cache.push_back(c); + } + // --------------------------------------------------------------------- + + bool firstLoop = false; + for (auto const& triggerTrack : triggers) { + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + auto trigg = triggerTrack.track_as(); + if (!isValidTrigger(trigg, triggerTrack.isLeading())) + continue; + + float efficiencyTrigg = 1.0f; + float efficiencyTriggError = 0.0f; + float purityTrigg = 1.0f; + float purityTriggErr = 0.0; + double bintrig[4] = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; + + if (efficiencyFlags.applyEfficiencyForTrigger) { + if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + efficiencyTrigg = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTrigg = hPurityHadron->Interpolate(trigg.pt()); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + if (efficiencyFlags.applyPurityTrigger) + purityTriggErr = hPurityHadron->Interpolate(trigg.pt()); + } + } else { + efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + } + } + if (efficiencyTrigg == 0) { + efficiencyTrigg = 1; + efficiencyTriggError = 0; + } + } + + if (!mixing) { + fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesKstar0")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + } + + double triggSign = trigg.sign(); + const double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + + if (mixingInBf) { + currentCollision.addValidParticle(trigg.eta(), trigg.phi(), trigg.pt(), -1, efficiencyTrigg, efficiencyTriggError, -1); + if (firstLoop) + continue; + } + + for (auto const& c : cache) { + firstLoop = true; + + //---] removing autocorrelations [--- + if (checks.doAutocorrelationRejection) { + if (trigg.globalIndex() == c.globalIndexPos || trigg.globalIndex() == c.globalIndexNeg) { + histos.fill(HIST("hNumberOfRejectedPairsKstar0"), 0.5); + continue; + } + } + + float deltaphi = computeDeltaPhi(trigg.phi(), c.phi); + float deltaeta = trigg.eta() - c.eta; + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + float ptassoc = c.pt; + float pttrigger = trigg.pt(); + float massassoc = c.mass; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + + float etaWeight = 1; + if (checks.doOnTheFlyFlattening) { + float preWeight = 1 - std::abs(deltaeta) / 1.6; + etaWeight = preWeight != 0 ? 1.0f / preWeight : 1.0f; + } + + float totalEffUncert = 0.0f; + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * c.efficiencyError, 2) + std::pow(efficiencyTriggError * c.efficiency, 2)); + } + + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + // 7-slot layout matching the mass-mode Signal booking above: mass + // ADDED after Delta-eta, everything else shifted one slot right. + double binFillThnMass[7] = {deltaphi, deltaeta, massassoc, ptassoc, pttrigger, pvz, mult}; + + const double assocForDeltaPhiStarPlus[] = {c.phiPos, c.ptPos, c.signPos}; + const double assocForDeltaPhiStarMinus[] = {c.phiNeg, c.ptNeg, c.signNeg}; + + if (!c.passesMcSelection) + continue; + + if (!mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (qaSwitches.doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, +0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, -0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, +0.5); + } + } + } + if (!mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (qaSwitches.doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, +0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, -0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, +0.5); + } + } + } + if (!mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/Kstar0")), binFillThn, etaWeight, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (qaSwitches.doDeltaPhiStarCheck) { + double deltaPhiStarPlus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPlus, bField); + double deltaPhiStarMinus = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarMinus, bField); + if (triggSign > 0) { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, +0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, -0.5); + } else { + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarPlus, trigg.eta() - c.etaPos, -0.5); + histos.fill(HIST("sameEvent/Signal/Kstar0DeltaPhiStar"), deltaPhiStarMinus, trigg.eta() - c.etaNeg, +0.5); + } + } + } + + // Mixed-event logic: buffered into ValidCollision when mixing via the + // collision pool (processMixedEventHKstarsInBuffer), filled directly + // otherwise (processMixedEventHKstars). LeftBg/RightBg are only + // booked when !fillCorrelationHistWithMass (see booking above), so + // both are skipped entirely in mass mode -- filling/replaying into an + // unbooked histogram would abort. Signal carries a real mass value + // through the buffer (ValidParticle::mass) so the replay loop below + // can build the correct 7-slot fill array in mass mode too. + if (mixing && c.isLeftBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 0, c.efficiency, c.efficiencyError, 1 /* 1 = Kstar0 type */); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Kstar0")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && (masterConfigurations.fillCorrelationHistWithMass || c.isSignal)) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 1, c.efficiency, c.efficiencyError, 1, c.mass); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + if (mixing && c.isRightBg && !masterConfigurations.fillCorrelationHistWithMass) { + if (mixingInBf) { + currentCollision.addValidParticle(c.eta, c.phi, c.pt, 2, c.efficiency, c.efficiencyError, 1); + } else { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Kstar0")), binFillThn, 1, c.efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } + } + } + + // Process buffer for mixed events (mirrors fillCorrelationsPhi; reached + // when called from processMixedEventHKstarsInBuffer with mixingInBf=true) + if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + return; + + int binnumb = binMult * nBinsVtxZ + binVtxZ; + int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; + if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || + (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 3 && currentCollision.trigParticles.empty() && currentCollision.assocParticles.empty()) || + (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) + return; + + for (const auto& collision : validCollisions[binnumb]) { + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + + for (const auto& trigger : collision.trigParticles) { + for (const auto& assoc : currentCollision.assocParticles) { + float deltaeta = trigger.eta - assoc.eta; + float deltaphi = computeDeltaPhi(trigger.phi, assoc.phi); + float efficiencyTrigg = trigger.efficiency; + float efficiencyAssoc = assoc.efficiency; + float efficiencyTriggError = trigger.efficiencyError; + float efficiencyAssocError = assoc.efficiencyError; + float totalEffUncert = 0.0; + float ptassoc = assoc.pt; + float pttrigger = trigger.pt; + + if (efficiencyFlags.applyEfficiencyPropagation) { + totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyAssocError, 2) + std::pow(efficiencyTriggError * efficiencyAssoc, 2)); + } + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + double binFillThnMass[7] = {deltaphi, deltaeta, assoc.mass, ptassoc, pttrigger, pvz, mult}; + + // region 0/2 (LeftBg/RightBg) are never buffered in mass mode (see + // the buffering guard above); region 1 (Signal) picks the 7-slot + // mass array when mass mode is on, to match the booking above. + if (assoc.region == 0) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/Kstar0")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 1) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/Kstar0")), masterConfigurations.fillCorrelationHistWithMass ? binFillThnMass : binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + if (assoc.region == 2) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/Kstar0")), binFillThn, 1, efficiencyTrigg * efficiencyAssoc, totalEffUncert, 1., 0.); + } + } + } + } + + if (validCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter)) { + validCollisions[binnumb].erase(validCollisions[binnumb].begin()); + } + if (!currentCollision.trigParticles.empty()) + validCollisions[binnumb].push_back(currentCollision); + } + + double getMagneticField(uint64_t timestamp) + { + static parameters::GRPMagField* grpo = nullptr; + if (grpo == nullptr) { + grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for timestamp %llu", timestamp); + return 0; + } + LOGF(info, "Retrieved GRP for timestamp %llu with magnetic field of %d kG", timestamp, grpo->getNominalL3Field()); + } + + return 0.1 * (grpo->getNominalL3Field()); // 1 T = 10 kG + } + // if this process function is enabled, it will be such that only events with trigger particles within a given + // trigger pt bin are taken for the entire processing. This allows for the calculation of e.g. efficiencies + // within an event class that has a trigger (which may differ with respect to other cases, to be checked) + + // for map determining which trigger bins are present and which aren't + std::vector triggerPresenceMap; + + void processSelectEventWithTrigger(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // setup + triggerPresenceMap.clear(); + triggerPresenceMap.resize(collisions.size(), 0); + + for (auto const& collision : collisions) { + // ________________________________________________ + // Perform basic event selection + if (!isSelectedEvents(collision, false)) { + continue; + } + + // do not forget to re-group ... + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); + + for (auto const& triggerTrack : slicedTriggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + auto binNumber = histos.get(HIST("axes/hPtTriggerAxis"))->FindFixBin(track.pt()) - 1; + SETBIT(triggerPresenceMap[collision.globalIndex()], binNumber); + } + } + } + + void processSameEventHHadrons(soa::Join::iterator const& collision, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // Guard against the fatal "histogram not found" crash: the per-species + // histograms this function fills are only booked in init() when + // TESTBIT(doCorrelation, IndexHadron) is also set (see init(), ~line 831) + // -- doCorrelation defaults to 0 (masterConfigurations.doCorrelationHadron + // defaults false) while this PROCESS_SWITCH defaults true, so without this + // guard the default config fills unbooked histograms and crashes. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } + // LOGF(info, "SameEventHadron: collisions=%d triggers=%zu assocHadrons=%zu", collision.globalIndex(), triggerTracks.size(), assocHadrons.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { + return; + } + + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // ________________________________________________ + // Perform basic event selection. fillHist=true only from the owning + // species (see eventQAOwnerIndex in init()) so CollCutCounts/EventQA + // aren't multi-counted when several same-event species run together. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexHadron)) { + return; + } + // ________________________________________________ + if (eventQAOwnerIndex == IndexHadron && qaSwitches.doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + } + + // Do basic QA + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions && !doprocessSameEventHKaons) { + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) + continue; + histos.fill(HIST("hDCAzTriggerHadron"), track.dcaZ(), track.pt()); + histos.fill(HIST("hDCAxyTriggerHadron"), track.dcaXY(), track.pt()); + float efficiency = 1.0f; + if (efficiencyFlags.applyEfficiencyCorrection) { + efficiency = hEfficiencyTrigger->Interpolate(track.pt(), track.eta()); + } + if (efficiency == 0) { // check for zero efficiency, do not apply if the case + efficiency = 1; + } + float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi(), weight); + } + } + for (auto const& assocTrack : assocHadrons) { + auto assoc = assocTrack.track_as(); + if (!isValidAssocHadron(assoc)) + continue; + float efficiency = 1.0f; + float purity = 1.0f; + histos.fill(HIST("hDCAzAssociatedHadron"), assoc.dcaZ(), assoc.pt()); + histos.fill(HIST("hDCAxyAssociatedHadron"), assoc.dcaXY(), assoc.pt()); + if (efficiencyFlags.applyEfficiencyCorrection) { + efficiency = hEfficiencyHadron->Interpolate(assoc.pt(), assoc.eta()); + if (efficiencyFlags.applyPurityHadron) + purity = hPurityHadron->Interpolate(assoc.pt()); + } + if (efficiency == 0) { // check for zero efficiency, do not apply if the case + efficiency = 1; + } + float weight = efficiencyFlags.applyEfficiencyCorrection ? purity / efficiency : 1.0f; + histos.fill(HIST("hAssocHadronsAllSelectedEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M(), weight); + histos.fill(HIST("hAssocPtResolution"), assoc.pt(), assocTrack.mcOriginalPt()); + if (mcSelections.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hAssocPrimaryEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M()); + histos.fill(HIST("hAsssocTrackEtaVsPtVsPhi"), assoc.pt(), assoc.eta(), assoc.phi(), weight); + } + + // ________________________________________________ + // Do hadron - hadron correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(triggerTracks, assocHadrons, false, collision.posZ(), collision.centFT0M(), bField); + } + + void processSameEventHPions(soa::Join::iterator const& collision, soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPion=false, this switch=true) fills + // hPionEtaVsPtAllSelected etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } + // LOGF(info, "SameEventPion: collisions=%d triggers=%zu assocPions=%zu", collision.globalIndex(), triggerTracks.size(), associatedPions.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { + return; + } + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // ________________________________________________ + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPion)) { + return; + } + // ________________________________________________ + if (eventQAOwnerIndex == IndexPion && qaSwitches.doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + } + // Do basic QA + for (auto const& pion : associatedPions) { + auto pionTrack = pion.track_as(); + if (!isValidAssocHadron(pionTrack)) + continue; + + histos.fill(HIST("hPionEtaVsPtAllSelected"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + if (mcSelections.doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) + continue; + if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) + continue; + histos.fill(HIST("hPionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + if (pionTrack.sign() > 0) + histos.fill(HIST("hPositivePionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + else + histos.fill(HIST("hNegativePionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + } + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars) { + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) + continue; + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + } + + // ________________________________________________ + // Do hadron - Pion correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(triggerTracks, associatedPions, false, collision.posZ(), collision.centFT0M(), bField); + } + + void processSameEventHKaons(soa::Join::iterator const& collision, soa::Join const& associatedKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKaon=false, this switch=true) fills + // hKaonEtaVsPtAllSelected etc. before they're booked -> fatal (this is + // the exact FATAL reported: "hKaonEtaVsPtAllSelected" not found). + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } + // LOGF(info, "SameEventKaon: collisions=%d triggers=%zu assocKaons=%zu", collision.globalIndex(), triggerTracks.size(), associatedKaons.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { + return; + } + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // ________________________________________________ + // Perform basic event selection. See the matching comment in + // processSameEventHHadrons for fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKaon)) { + return; + } + // ________________________________________________ + if (eventQAOwnerIndex == IndexKaon && qaSwitches.doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + } + // Do basic QA + for (auto const& kaon : associatedKaons) { + auto kaonTrack = kaon.track_as(); + if (!isValidAssocHadron(kaonTrack)) + continue; + + histos.fill(HIST("hKaonEtaVsPtAllSelected"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + if (mcSelections.doAssocPhysicalPrimary && !kaon.mcPhysicalPrimary()) + continue; + if (masterConfigurations.doMCassociation && std::abs(kaon.pdgCode()) != PdgCodes[IndexKaon]) + continue; + histos.fill(HIST("hKaonEtaVsPt"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + if (kaonTrack.sign() > 0) + histos.fill(HIST("hPositiveKaonEtaVsPt"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + else + histos.fill(HIST("hNegativeKaonEtaVsPt"), kaonTrack.pt(), kaonTrack.eta(), collision.centFT0M()); + } + if (!doprocessSameEventHPhis && !doprocessSameEventHKstars && !doprocessSameEventHPions) { + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + if (!isValidTrigger(track, triggerTrack.isLeading())) + continue; + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + continue; + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + } + + // ________________________________________________ + // Do hadron - Kaon correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(triggerTracks, associatedKaons, false, collision.posZ(), collision.centFT0M(), bField); + } + + void processSameEventHPhis(soa::Join::iterator const& collision, aod::AssocPhis const& associatedPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationPhi=false, this switch=true) fills + // sameEvent/Signal/Phi etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } + // LOGF(info, "SameEventPhi: collisions=%d triggers=%zu assocPhi=%zu", collision.globalIndex(), triggerTracks.size(), associatedPhis.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + + // Skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { + return; + } + + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexPhi)) { + return; + } + + if (eventQAOwnerIndex == IndexPhi && qaSwitches.doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + } + + // ----------------------------- + // Trigger QA + // ----------------------------- + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { + continue; + } + + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + + // Same-event invariant-mass cross-check: raw per-candidate mass, read + // straight off the already-computed Mass column -- no kinematics + // recomputed here. See the matching comment at hPhiMixedEventInvMass's + // booking in init() for why this is the mixed-event counterpart. + for (auto const& cand : associatedPhis) { + histos.fill(HIST("hPhiInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + + // ----------------------------- + // h-phi correlation + // ----------------------------- + // Guarded by doFullCorrelationStudy to match how sameEvent/Signal/Phi is + // booked in init() (~line 753) -- this call used to be unconditional, + // which would fill that THnF even when doFullCorrelationStudy=false (and + // it was therefore never booked). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(triggerTracks, associatedPhis, false, false, collision.posZ(), collision.centFT0M(), bField); + } + } + + void processSameEventHKstars(soa::Join::iterator const& collision, aod::AssocKstars const& associatedKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons: without this, + // the default config (doCorrelationKstar=false, this switch=true) fills + // sameEvent/Signal/Kstar0 etc. before they're booked -> fatal. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } + // LOGF(info, "SameEventKstar: collisions=%d triggers=%zu assocKstar=%zu", collision.globalIndex(), triggerTracks.size(), associatedKstars.size()); + + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + + // Skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerSelection.triggerBinToSelect)) { + return; + } + + auto bc = collision.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + // See the matching comment in processSameEventHHadrons for + // fillHist/eventQAOwnerIndex. + if (!isSelectedEvents(collision, eventQAOwnerIndex == IndexKstar)) { + return; + } + + if (eventQAOwnerIndex == IndexKstar && qaSwitches.doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hSECollisionBins"), colBinning.getBin({collision.posZ(), collision.centFT0M()})); + } + + // ----------------------------- + // Trigger QA + // ----------------------------- + for (auto const& triggerTrack : triggerTracks) { + auto track = triggerTrack.track_as(); + + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + + histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); + + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { + continue; + } + + histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); + + histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + } + + // Same-event invariant-mass cross-check -- see the matching comment in + // processSameEventHPhis. + for (auto const& cand : associatedKstars) { + histos.fill(HIST("hKstarInvMass"), cand.mass(), cand.pt(), collision.centFT0M()); + } + + // ----------------------------- + // h-K*0 correlation + // ----------------------------- + // Guarded by doFullCorrelationStudy for the same reason as the Phi call + // above -- matches how sameEvent/Signal/Kstar0 is booked in init(). + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(triggerTracks, associatedKstars, false, false, collision.posZ(), collision.centFT0M(), bField); + } + } + + void + processMixedEventHHadrons(soa::Join const& collisions, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Hadron + // is booked only when TESTBIT(doCorrelation, IndexHadron) is set. + if (!TESTBIT(doCorrelation, IndexHadron)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + // ________________________________________________ + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { + return; + } + + // ________________________________________________ + // Perform basic event selection on both collisions + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { + continue; + } + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + continue; + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + continue; + if (qaSwitches.doMixingQAandEventQA) { + if (collision1.globalIndex() == collision2.globalIndex()) { + histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting + } + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + // ________________________________________________ + // Do slicing + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + auto slicedAssocHadrons = assocHadrons.sliceBy(collisionSliceHadrons, collision2.globalIndex()); + // ________________________________________________ + // Do hadron - hadron correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(slicedTriggerTracks, slicedAssocHadrons, true, collision1.posZ(), collision1.centFT0M(), bField); + } + } + + void processMixedEventHPions(soa::Join const& collisions, soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Pion + // is booked only when TESTBIT(doCorrelation, IndexPion) is set. + if (!TESTBIT(doCorrelation, IndexPion)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + // ________________________________________________ + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { + continue; + } + + // ________________________________________________ + // Perform basic event selection on both collisions + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { + continue; + } + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + continue; + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + continue; + if (qaSwitches.doMixingQAandEventQA) { + if (collision1.globalIndex() == collision2.globalIndex()) { + histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting + } + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + // ________________________________________________ + // Do slicing + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + auto slicedAssocPions = assocPions.sliceBy(collisionSliceHadrons, collision2.globalIndex()); + // ________________________________________________ + // Do hadron - phi correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(slicedTriggerTracks, slicedAssocPions, true, collision1.posZ(), collision1.centFT0M(), bField); + } + } + + void processMixedEventHKaons(soa::Join const& collisions, soa::Join const& assocKaons, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kaon + // is booked only when TESTBIT(doCorrelation, IndexKaon) is set. + if (!TESTBIT(doCorrelation, IndexKaon)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + // ________________________________________________ + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { + continue; + } + + // ________________________________________________ + // Perform basic event selection on both collisions + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { + continue; + } + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + continue; + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + continue; + if (qaSwitches.doMixingQAandEventQA) { + if (collision1.globalIndex() == collision2.globalIndex()) { + histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting + } + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + // ________________________________________________ + // Do slicing + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + auto slicedAssocKaons = assocKaons.sliceBy(collisionSliceHadrons, collision2.globalIndex()); + // ________________________________________________ + // Do hadron - Kaon correlations + if (masterConfigurations.doFullCorrelationStudy) + fillCorrelationsHadron(slicedTriggerTracks, slicedAssocKaons, true, collision1.posZ(), collision1.centFT0M(), bField); + } + } + + void processMixedEventHPhis(soa::Join const& collisions, aod::AssocPhis const& assocPhis, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { + continue; + } + + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { + continue; + } + + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (qaSwitches.doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + + auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision2.globalIndex()); + + // Mixed-event invariant-mass cross-check: reuses the Mass/Rapidity + // columns already stored by the filter task (no kinematics + // recomputed here) and this same mixing pool, so it's directly + // comparable to the same-event mass QA and to the real ME background. + for (auto const& cand : slicedAssocPhis) { + histos.fill(HIST("hPhiMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + + if (masterConfigurations.doFullCorrelationStudy) { + // Use the per-collision slices computed above, not the full unsliced + // triggerTracks/assocPhis tables -- passing the whole dataframe here + // would mix every trigger with every candidate in the TF regardless + // of which two collisions are actually being paired. + fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, false, collision1.posZ(), collision1.centFT0M(), bField); + } + } + } + + void processMixedEventHKstars(soa::Join const& collisions, aod::AssocKstars const& assocKstars, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + + for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { + + auto bc = collision1.bc_as(); + auto bField = getMagneticField(bc.timestamp()); + + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + + if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerSelection.triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerSelection.triggerBinToSelect))) { + continue; + } + + if (!isSelectedEvents(collision1, false) || !isSelectedEvents(collision2, false)) { + continue; + } + + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) { + continue; + } + + if (qaSwitches.doMixingQAandEventQA) { + histos.fill(HIST("MixingQA/hMEpvz1"), collision1.posZ()); + histos.fill(HIST("MixingQA/hMEpvz2"), collision2.posZ()); + histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision1.posZ(), collision1.centFT0M()})); + } + + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision1.globalIndex()); + + auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision2.globalIndex()); + + // Mixed-event invariant-mass cross-check -- see the matching comment + // in processMixedEventHPhis. + for (auto const& cand : slicedAssocKstars) { + histos.fill(HIST("hKstarMixedEventInvMass"), cand.mass(), cand.pt(), collision1.centFT0M()); + } + + if (masterConfigurations.doFullCorrelationStudy) { + // Same reasoning as processMixedEventHPhis above -- use this collision + // pair's own slices, not the full unsliced triggerTracks/assocKstars. + fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, false, collision1.posZ(), collision1.centFT0M(), bField); + } + } + } + + void processMCGenerated(aod::McCollision const& /*mcCollision*/, soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) + { + histos.fill(HIST("hClosureTestEventCounter"), 2.5f); + + //--------------------------------------------- + // Generated particles before event selection + //--------------------------------------------- + + for (auto const& mcParticle : mcParticles) { + + if (std::abs(mcParticle.eta()) > phaseSpaceSelections.etaSel) + continue; + + float pt = mcParticle.pt(); + + // trigger particles + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hGeneratedQAPtTrigger"), pt, 0.0f); + } + } + + // generated phi + + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + + // if (!mcSelections.doAssocPhysicalPrimaryInGen || + // mcParticle.isPhysicalPrimary()) { + + histos.fill(HIST("hGeneratedQAPtAssociatedPhi"), pt, 0.0f); + + //} + } + } + + for (auto const& mcParticle : mcParticles) { + + // phi + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("Generated/hPhi"), mcParticle.pt(), mcParticle.eta(), mcParticle.phi(), 1.0); + } else { + histos.fill(HIST("Generated/hPhi"), mcParticle.pt(), mcParticle.eta(), 1.0); + } + } + + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + continue; + // NOTE: intentionally restricted to Pion only (IndexPion..IndexPion); Kstar0 is + // not extended to processMCGenerated/ClosureTest/Prediction in this version. + static_for([&](auto i) { + constexpr int Index = i.value; + if (i == IndexPion && mcParticle.pdgCode() > Neutral) { + histos.fill(HIST("Generated/hPositive") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } else if (i == IndexPion && mcParticle.pdgCode() < Neutral) { + histos.fill(HIST("Generated/hNegative") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } else if (mcParticle.pdgCode() == PdgCodes[i]) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("Generated/h") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), mcParticle.phi(), 1); + } else { + histos.fill(HIST("Generated/h") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } + } + }); + } + + //--------------------------------------------- + // Require reconstructed collision + //--------------------------------------------- + + if (collisions.size() < 1) + return; + + int biggestNContribs = -1; + float bestCollisionVtxZ = 0.f; + float bestCollisionFT0Mpercentile = -1.f; + float bestCollisionFT0Cpercentile = -1.f; + bool bestCollisionSel8 = false; + bool bestCollisionINELgtZERO = false; + bool isCollisionSelect = false; + uint32_t bestCollisionTriggerPresenceMap = 0; + + for (auto const& collision : collisions) { + + if (collision.numContrib() <= biggestNContribs) + continue; + + biggestNContribs = collision.numContrib(); + + bestCollisionFT0Mpercentile = collision.centFT0M(); + bestCollisionFT0Cpercentile = collision.centFT0C(); + + if (masterConfigurations.applyNewMCSelection) { + // isSelectedEvents() is the one selection used everywhere in this + // task now (pp and Pb-Pb alike), matching the filter task; see its + // own comment for why -- no more doPPAnalysis branch to a different + // cut set here. + isCollisionSelect = isSelectedEvents(collision, false); + } else { + bestCollisionSel8 = collision.sel8(); + bestCollisionVtxZ = collision.posZ(); + bestCollisionINELgtZERO = collision.isInelGt0(); + } + + if (triggerPresenceMap.size() > 0) + bestCollisionTriggerPresenceMap = triggerPresenceMap[collision.globalIndex()]; + } + + if (collisions.size() > 1) { + for (auto const& mcParticle : mcParticles) { + + if (std::abs(mcParticle.y()) > phaseSpaceSelections.ySel) + continue; + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { + histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult_TwoPVsOrMore"), mcParticle.pt(), bestCollisionFT0Mpercentile); + } + + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + continue; + + // NOTE: Pion only, see note above; Kstar0 not extended here. + static_for([&](auto i) { + constexpr int Index = i.value; + + if (std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i])) + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult_TwoPVsOrMore"), mcParticle.pt(), bestCollisionFT0Mpercentile); + }); + } + } + + //--------------------------------------------- + // Collision selection + //--------------------------------------------- + + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerSelection.triggerBinToSelect)) + return; + + if (masterConfigurations.applyNewMCSelection) { + + if (!isCollisionSelect) + return; + + } else { + + if (!bestCollisionSel8) + return; + + if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) + return; + + if (!bestCollisionINELgtZERO) + return; + } + + histos.fill(HIST("hClosureTestEventCounter"), 3.5f); + + //--------------------------------------------- + // Generated particles after event selection + //--------------------------------------------- + + for (auto const& mcParticle : mcParticles) { + + if (std::abs(mcParticle.eta()) > phaseSpaceSelections.etaSel) + continue; + + float pt = mcParticle.pt(); + + // trigger QA + + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hGeneratedQAPtTrigger"), pt, 1.0f); + } + } + + // generated phi + + if (std::abs(mcParticle.pdgCode()) != Pdg::kPhi) + continue; + + // if (mcSelections.doAssocPhysicalPrimaryInGen && + // !mcParticle.isPhysicalPrimary()) + // continue; + + histos.fill(HIST("hGeneratedQAPtAssociatedPhi"), pt, 1.0f); + } + + for (auto const& mcParticle : mcParticles) { + + double eta = mcParticle.eta(); + double pt = mcParticle.pt(); + + // generated phi + + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi) { + + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + + histos.fill(HIST("GeneratedWithPV/hPhi"), pt, mcParticle.eta(), mcParticle.phi(), bestCollisionFT0Cpercentile); + + } else { + histos.fill(HIST("GeneratedWithPV/hPhi"), pt, mcParticle.eta(), bestCollisionFT0Mpercentile); + } + if (std::abs(mcParticle.y()) < phaseSpaceSelections.ySel) + histos.fill(HIST("GeneratedWithPV/hPhi") + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); + } + + if (mcSelections.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("GeneratedWithPV/hTrigger"), pt, eta, mcParticle.phi(), bestCollisionFT0Cpercentile); + } else { + histos.fill(HIST("GeneratedWithPV/hTrigger"), pt, eta, bestCollisionFT0Mpercentile); + } + if (mcParticle.pdgCode() > 0) + histos.fill(HIST("GeneratedWithPV/hPositiveTrigger"), pt, eta, bestCollisionFT0Mpercentile); + else + histos.fill(HIST("GeneratedWithPV/hNegativeTrigger"), pt, eta, bestCollisionFT0Mpercentile); + } + + // NOTE: Pion only, see note above; Kstar0 not extended here. + static_for([&](auto i) { + constexpr int Index = i.value; + if (i == IndexPion && std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i]) && mcParticle.pdgCode() > Neutral) { + histos.fill(HIST("GeneratedWithPV/hPositive") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), bestCollisionFT0Mpercentile); + } else if (i == IndexPion && std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i]) && mcParticle.pdgCode() < Neutral) { + histos.fill(HIST("GeneratedWithPV/hNegative") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), bestCollisionFT0Mpercentile); + } + + if (std::abs(mcParticle.pdgCode()) == std::abs(PdgCodes[i])) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]), pt, eta, mcParticle.phi(), bestCollisionFT0Cpercentile); + } else { + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]), pt, eta, bestCollisionFT0Mpercentile); + } + if (std::abs(mcParticle.y()) < phaseSpaceSelections.ySel) + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult"), pt, bestCollisionFT0Mpercentile); + } + }); + } + } + + void processClosureTest(aod::McCollision const& /*mcCollision*/, soa::SmallGroups> const& recCollisions, aod::McParticles const& mcParticles) + { + + std::vector triggerIndices; + std::vector> associatedIndices; + std::vector assocHadronIndices; + std::vector piIndices; + std::vector phiIndices; + std::vector kstarIndices; // intentionally left unpopulated: Kstar0 is not + // extended to this process function in this version + + for (auto const& mcParticle : mcParticles) { + double geta = mcParticle.eta(); + if (std::abs(geta) > phaseSpaceSelections.etaSel) { + continue; + } + double gpt = mcParticle.pt(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hClosureQAPtTrigger"), gpt, 0.0f); // step 1: no event selection whatsoever + } + } + + // if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 0.0f); // step 1: no event selection whatsoever + } + // } + } + + histos.fill(HIST("hClosureTestEventCounter"), 0.5f); + + int bestCollisionFT0Mpercentile = -1; + float bestCollisionVtxZ = 0.0f; + bool bestCollisionSel8 = false; + bool bestCollisionINELgtZERO = false; + int biggestNContribs = -1; + uint32_t bestCollisionTriggerPresenceMap = 0; + + for (auto const& recCollision : recCollisions) { + if (biggestNContribs < recCollision.numContrib()) { + biggestNContribs = recCollision.numContrib(); + bestCollisionFT0Mpercentile = recCollision.centFT0M(); + bestCollisionSel8 = recCollision.sel8(); + bestCollisionVtxZ = recCollision.posZ(); + bestCollisionINELgtZERO = recCollision.isInelGt0(); + if (triggerPresenceMap.size() > 0) + bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; + } + } + // ________________________________________________ + // skip if desired trigger not found + if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerSelection.triggerBinToSelect)) { + return; + } + + if (masterConfigurations.doGenEventSelection) { + if (!bestCollisionSel8) + return; + if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) + return; + if (!bestCollisionINELgtZERO) + return; + if (bestCollisionFT0Mpercentile > axisRanges[5][1] || bestCollisionFT0Mpercentile < axisRanges[5][0]) { + return; + } + } + + histos.fill(HIST("hClosureTestEventCounter"), 1.5f); + + for (auto const& mcParticle : mcParticles) { + double geta = mcParticle.eta(); + if (std::abs(geta) > phaseSpaceSelections.etaSel) { + continue; + } + double gpt = mcParticle.pt(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hClosureQAPtTrigger"), gpt, 1.0f); // step 2: after event selection + } + } + + // if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + histos.fill(HIST("hClosureQAPtAssociatedPhi"), gpt, 1.0f); // step 2: after event selection + } + // } + } + + int iteratorNum = -1; + for (auto const& mcParticle : mcParticles) { + iteratorNum = iteratorNum + 1; + double geta = mcParticle.eta(); + double gpt = mcParticle.pt(); + double gphi = mcParticle.phi(); + if (std::abs(geta) > phaseSpaceSelections.etaSel) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + triggerIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionFT0Mpercentile); + } + if (masterConfigurations.doCorrelationHadron) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + assocHadronIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); + } + } + } + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { + piIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); + } + } + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + phiIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hPhi"), gpt, geta, gphi); + } + } + + associatedIndices.emplace_back(phiIndices); // IndexPhi = 0 + associatedIndices.emplace_back(kstarIndices); // IndexKstar = 1 (always empty here) + associatedIndices.emplace_back(piIndices); // IndexPion = 2 + associatedIndices.emplace_back(assocHadronIndices); // Hadron = 3 + + for (std::size_t iTrigger = 0; iTrigger < triggerIndices.size(); iTrigger++) { + auto triggerParticle = mcParticles.iteratorAt(triggerIndices[iTrigger]); + // check range of trigger particle + if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { + continue; + } + double getatrigger = triggerParticle.eta(); + double gphitrigger = triggerParticle.phi(); + double pttrigger = triggerParticle.pt(); + + auto mothers = triggerParticle.mothers_as(); + auto globalIndex = (mothers.size() > 0) ? triggerParticle.mothers_first_as().globalIndex() : -1; + + static_for<0, 3>([&](auto i) { // associated loop (Kstar0 slot is always empty, see above) + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + if (triggerIndices[iTrigger] != associatedIndices[Index][iassoc] && globalIndex != assocParticle.globalIndex()) { // avoid self + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaphi = computeDeltaPhi(gphitrigger, gphiassoc); + double deltaeta = getatrigger - getaassoc; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + if (TESTBIT(doCorrelation, i)) + histos.fill(HIST("ClosureTest/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionFT0Mpercentile); + } + } + }); + } + } + + void processMixedEventHPhisInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocPhis const& assocPhis, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Phi + // is booked only when TESTBIT(doCorrelation, IndexPhi) is set. + if (!TESTBIT(doCorrelation, IndexPhi)) { + return; + } + for (auto const& collision : collisions) { + + auto bc = collision.bc_as(); + auto dBz = getMagneticField(bc.timestamp()); + + // Slice down to this collision's own triggers/candidates before + // buffering into ValidCollision -- without this, every collision would + // buffer the entire dataframe's triggers/candidates instead of just + // its own, defeating the point of per-collision buffering. + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); + auto slicedAssocPhis = assocPhis.sliceBy(collisionSlicePhis, collision.globalIndex()); + + // Guarded by doFullCorrelationStudy -- see the matching comment in + // processSameEventHPhis; this call used to be unconditional. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsPhi(slicedTriggerTracks, slicedAssocPhis, true, true, collision.posZ(), collision.centFT0M(), dBz); + } + } + } + + void processMixedEventHKstarsInBuffer(soa::Join const& collisions, aod::TriggerTracks const& triggerTracks, aod::AssocKstars const& assocKstars, TracksComplete const&, aod::BCsWithTimestamps const&) + { + // See the matching comment in processSameEventHHadrons -- mixedEvent/Signal/Kstar0 + // is booked only when TESTBIT(doCorrelation, IndexKstar) is set. + if (!TESTBIT(doCorrelation, IndexKstar)) { + return; + } + for (auto const& collision : collisions) { + + auto bc = collision.bc_as(); + auto dBz = getMagneticField(bc.timestamp()); + + auto slicedTriggerTracks = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); + auto slicedAssocKstars = assocKstars.sliceBy(collisionSliceKstars, collision.globalIndex()); + + // Guarded by doFullCorrelationStudy -- see the matching comment above. + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsKstar(slicedTriggerTracks, slicedAssocKstars, true, true, collision.posZ(), collision.centFT0M(), dBz); + } + } + } + + void processPrediction(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles) + { + std::vector triggerIndices; + std::vector> associatedIndices; + std::vector assocHadronIndices; + std::vector piIndices; + std::vector phiIndices; + std::vector kstarIndices; // intentionally left unpopulated: Kstar0 is not + // extended to this process function in this version + float centMultFT0M = -1; + float centMultFT0A = -1; + float centMultFT0C = -1; + float multFT0M = -1; + float multFT0A = -1; + float multFT0C = -1; + float multEta08 = -1; + float multEta05 = -1; + histos.fill(HIST("Prediction/hEventSelection"), 0.5); + if (masterConfigurations.selectINELgtZERO && !o2::pwglf::isINELgt0mc(mcParticles, pdgDB)) { + return; + } + histos.fill(HIST("Prediction/hEventSelection"), 1.5); + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + histos.fill(HIST("Prediction/hEventSelection"), 2.5); + if (masterConfigurations.useCentralityinPrediction) { + centMultFT0M = mcCollision.centFT0M(); + centMultFT0A = mcCollision.centFT0A(); + centMultFT0C = mcCollision.centFT0C(); + } else { + multFT0M = mCounter.countFT0A(mcParticles) + mCounter.countFT0C(mcParticles); + multFT0A = mCounter.countFT0A(mcParticles); + multFT0C = mCounter.countFT0C(mcParticles); + multEta08 = mCounter.countEta08(mcParticles); + multEta05 = mCounter.countEta05(mcParticles); + } + if (!masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hFT0AvsNchEta08"), multFT0A, multEta08); + histos.fill(HIST("Prediction/hFT0AvsNchEta05"), multFT0A, multEta05); + histos.fill(HIST("Prediction/hFT0CvsNchEta08"), multFT0C, multEta08); + histos.fill(HIST("Prediction/hFT0CvsNchEta05"), multFT0C, multEta05); + } + histos.fill(HIST("Prediction/hFT0MvsNchEta08"), multFT0M, multEta08); + histos.fill(HIST("Prediction/hFT0MvsNchEta05"), multFT0M, multEta05); + } + int iteratorNum = -1; + for (auto const& mcParticle : mcParticles) { + iteratorNum = iteratorNum + 1; + double geta = mcParticle.eta(); + double gpt = mcParticle.pt(); + double gphi = mcParticle.phi(); + if (std::abs(geta) > phaseSpaceSelections.etaSel) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + triggerIndices.emplace_back(iteratorNum); + if (masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hTriggerFT0A"), gpt, geta, centMultFT0A); + histos.fill(HIST("Prediction/hTriggerFT0C"), gpt, geta, centMultFT0C); + } + histos.fill(HIST("Prediction/hTrigger"), gpt, geta, centMultFT0M); + } else { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hTriggerFT0A"), gpt, geta, multFT0A); + histos.fill(HIST("Prediction/hTriggerFT0C"), gpt, geta, multFT0C); + } + histos.fill(HIST("Prediction/hTrigger"), gpt, geta, multFT0M); + } + } + if (masterConfigurations.doCorrelationHadron) { + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + assocHadronIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hHadron"), gpt, geta, gphi); + } + } + } + if (!mcSelections.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { + piIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hPion"), gpt, geta, gphi); + } + } + if (std::abs(mcParticle.pdgCode()) == Pdg::kPhi && masterConfigurations.doCorrelationPhi) { + phiIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hPhi"), gpt, geta, gphi); + } + } + + associatedIndices.emplace_back(phiIndices); // IndexPhi = 0 + associatedIndices.emplace_back(kstarIndices); // IndexKstar = 1 (always empty here) + associatedIndices.emplace_back(piIndices); // IndexPion = 2 + associatedIndices.emplace_back(assocHadronIndices); // Hadron = 3 + for (std::size_t iTrigger = 0; iTrigger < triggerIndices.size(); iTrigger++) { + auto triggerParticle = mcParticles.iteratorAt(triggerIndices[iTrigger]); + // check range of trigger particle + if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { + continue; + } + double getatrigger = triggerParticle.eta(); + double gphitrigger = triggerParticle.phi(); + double pttrigger = triggerParticle.pt(); + auto mothers = triggerParticle.mothers_as(); + auto globalIndex = (mothers.size() > 0) ? triggerParticle.mothers_first_as().globalIndex() : -1; + static_for<0, 3>([&](auto i) { // associated loop (Kstar0 slot is always empty, see above) + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + if (triggerIndices[iTrigger] != associatedIndices[Index][iassoc] && globalIndex != assocParticle.globalIndex()) { // avoid self + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaphi = computeDeltaPhi(gphitrigger, gphiassoc); + double deltaeta = getatrigger - getaassoc; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + continue; + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + continue; + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + continue; + if (TESTBIT(doCorrelation, i)) { + if (masterConfigurations.useCentralityinPrediction) { + histos.fill(HIST("Prediction/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0M); + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/sameEventFT0A/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0A); + histos.fill(HIST("Prediction/sameEventFT0C/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0C); + } + } else { + histos.fill(HIST("Prediction/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0M); + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/sameEventFT0A/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0A); + histos.fill(HIST("Prediction/sameEventFT0C/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0C); + } + } + } + } + } + }); + } + } + + PROCESS_SWITCH(HResonanceCorrelation, processSelectEventWithTrigger, "Select events with trigger only", true); + + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHPions, "Process same events, h-Pion", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHKaons, "Process same events, h-Kaon", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHHadrons, "Process same events, h-h", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHPhis, "Process same events, h-phi", true); + PROCESS_SWITCH(HResonanceCorrelation, processSameEventHKstars, "Process same events, h-K*0", true); + + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHPions, "Process mixed events, h-Pion", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHKaons, "Process mixed events, h-Kaon", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHHadrons, "Process mixed events, h-h", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHPhis, "Process mixed events, h-phi", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHPhisInBuffer, "Process mixed-event, h-phi using collision buffer", false); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHKstars, "Process mixed events, h-K*0", true); + PROCESS_SWITCH(HResonanceCorrelation, processMixedEventHKstarsInBuffer, "Process mixed-event, h-K*0 using collision buffer", false); + + PROCESS_SWITCH(HResonanceCorrelation, processMCGenerated, "Process MC generated", false); + PROCESS_SWITCH(HResonanceCorrelation, processClosureTest, "Process Closure Test", false); + PROCESS_SWITCH(HResonanceCorrelation, processPrediction, "process model prediction", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/heptaquark.cxx b/PWGLF/Tasks/Resonances/heptaquark.cxx index 351ff08ab0a..f5da2552359 100644 --- a/PWGLF/Tasks/Resonances/heptaquark.cxx +++ b/PWGLF/Tasks/Resonances/heptaquark.cxx @@ -261,7 +261,7 @@ struct heptaquark { if (collision.numLambda() < 1 || collision.numPhi() < 2) return; - for (auto hqtrackd1 : hqtracks) { + for (const auto& hqtrackd1 : hqtracks) { if (hqtrackd1.hqId() != 333) continue; @@ -289,7 +289,7 @@ struct heptaquark { auto hqd1id = hqtrackd1.index(); histos.fill(HIST("hPhid1Mass"), HQ1.M(), HQ1.Pt()); - for (auto hqtrackd2 : hqtracks) { + for (const auto& hqtrackd2 : hqtracks) { auto hqd2id = hqtrackd2.index(); if (hqd2id <= hqd1id) continue; @@ -325,7 +325,7 @@ struct heptaquark { histos.fill(HIST("hnsigmaTOFKa"), hqtrackd2.hqd2TOF(), DauVec2.Pt()); histos.fill(HIST("hPhid2Mass"), HQ2.M(), HQ2.Pt()); - for (auto hqtrackd3 : hqtracks) { + for (const auto& hqtrackd3 : hqtracks) { if (std::abs(hqtrackd3.hqId()) != 3122) continue; diff --git a/PWGLF/Tasks/Resonances/higherMassResonances.cxx b/PWGLF/Tasks/Resonances/higherMassResonances.cxx index cdbdb01934d..30c22f10d14 100644 --- a/PWGLF/Tasks/Resonances/higherMassResonances.cxx +++ b/PWGLF/Tasks/Resonances/higherMassResonances.cxx @@ -20,12 +20,14 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/EventPlaneHelper.h" #include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" // #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" // #include "Common/DataModel/PIDResponseTPC.h" // +#include "Common/DataModel/Qvectors.h" #include "Common/DataModel/TrackSelectionTables.h" #include @@ -62,6 +64,7 @@ #include #include #include +#include #include using namespace o2; @@ -69,7 +72,8 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::aod::rctsel; -// using namespace o2::constants::physics; +using namespace o2::constants::physics; + using std::array; // FixME: Add INEL>0 selection for the derived data @@ -110,20 +114,12 @@ struct HigherMassResonances { Configurable isselectTWOKsOnly{"isselectTWOKsOnly", true, "Select only events with two K0s"}; Configurable isapplyPairRapidityMC{"isapplyPairRapidityMC", false, "Apply pair rapidity cut on reconstructed mother (after already applying rapidity cut on generated mother)"}; Configurable cSelectMultEstimator{"cSelectMultEstimator", 0, "Select multiplicity estimator: 0 - FT0M, 1 - FT0A, 2 - FT0C"}; - // Configurable configOccCut{"configOccCut", 1000, "Occupancy cut"}; - // Configurable isVertexTOFMatched{"isVertexTOFMatched", false, "Vertex TOF Matched"}; - // Configurable isNoCollInTimeRangeStandard{"isNoCollInTimeRangeStandard", false, "No collision in time range standard"}; - // Configurable isSel8{"isSel8", false, "Event Selection 8"}; // Configurables for event selection Configurable isINELgt0{"isINELgt0", true, "INEL>0 selection"}; Configurable isTriggerTVX{"isTriggerTVX", false, "TriggerTVX"}; - // Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", false, "IsGoodZvtxFT0vsPV"}; - // Configurable isApplyOccCut{"isApplyOccCut", true, "Apply occupancy cut"}; Configurable cutzvertex{"cutzvertex", 10.0f, "Accepted z-vertex range (cm)"}; Configurable timFrameEvsel{"timFrameEvsel", true, "TPC Time frame boundary cut"}; - // Configurable isNoSameBunchPileup{"isNoSameBunchPileup", true, "kNoSameBunchPileup"}; - Configurable isAllLayersGoodITS{"isAllLayersGoodITS", true, "Require all ITS layers to be good"}; Configurable isNoTimeFrameBorder{"isNoTimeFrameBorder", true, "kNoTimeFrameBorder"}; Configurable isNoITSROFrameBorder{"isNoITSROFrameBorder", true, "kNoITSROFrameBorder"}; @@ -139,7 +135,6 @@ struct HigherMassResonances { Configurable cMaxV0DCA{"cMaxV0DCA", 0.5, "DCA V0 to PV"}; Configurable confV0CPAMin{"confV0CPAMin", 0.97f, "Minimum CPA of V0"}; Configurable confV0TranRadV0Min{"confV0TranRadV0Min", 0.5f, "Minimum transverse radius"}; - // Configurable confV0TranRadV0Max{"confV0TranRadV0Max", 200.f, "Maximum transverse radius"}; Configurable cMaxV0LifeTime{"cMaxV0LifeTime", 15, "Maximum V0 life time"}; Configurable cSigmaMassKs0{"cSigmaMassKs0", 4, "n Sigma cut on Ks0 mass (Mass (Ks) - cSigmaMassKs0*cWidthKs0)"}; Configurable cWidthKs0{"cWidthKs0", 0.005, "Width of KS0"}; @@ -148,18 +143,17 @@ struct HigherMassResonances { Configurable confDaughPIDCutTPC{"confDaughPIDCutTPC", 5, "PID selections for KS0 daughters"}; Configurable confDaughPIDCutTOF{"confDaughPIDCutTOF", 5, "PID selections for KS0 daughters in TOF"}; Configurable confKsrapidity{"confKsrapidity", 0.5f, "Rapidity cut on K0s"}; - // Configurable isStandardV0{"isStandardV0", false, "Standard V0 selection"}; Configurable isApplyEtaCutK0s{"isApplyEtaCutK0s", false, "Apply eta cut on K0s daughters"}; Configurable cfgETAcut{"cfgETAcut", 0.8f, "Track ETA cut"}; Configurable deltaRDaugherCut{"deltaRDaugherCut", 0.001f, "DeltaR cut on V0 daughters"}; Configurable deltaRK0sCut{"deltaRK0sCut", 0.1f, "Apply deltaR cut between two K0s"}; + Configurable cfgArmenterosCut{"cfgArmenterosCut", 0.2f, "Armenteros-Podolanski cut"}; // Configurable for track selection and multiplicity Configurable cfgPTcut{"cfgPTcut", 0.2f, "Track PT cut"}; Configurable cfgNmixedEvents{"cfgNmixedEvents", 5, "Number of mixed events"}; // Configurable for MC - // Configurable isMC{"isMC", false, "Is MC"}; Configurable isallGenCollisions{"isallGenCollisions", true, "To fill all generated collisions for the signal loss calculations"}; Configurable isavoidsplitrackMC{"isavoidsplitrackMC", false, "avoid split track in MC"}; Configurable isapplyRapidityMC{"isapplyRapidityMC", true, "Apply rapidity cut on generated and reconstructed particles"}; @@ -170,46 +164,54 @@ struct HigherMassResonances { Configurable activateHelicityFrame{"activateHelicityFrame", false, "Activate the THnSparse with cosThStar w.r.t. helicity axis"}; Configurable activateCollinsSoperFrame{"activateCollinsSoperFrame", false, "Activate the THnSparse with cosThStar w.r.t. Collins soper axis"}; Configurable activateProductionFrame{"activateProductionFrame", false, "Activate the THnSparse with cosThStar w.r.t. production axis"}; - Configurable activateBeamAxisFrame{"activateBeamAxisFrame", true, "Activate the THnSparse with cosThStar w.r.t. beam axis (Gottified jackson frame)"}; + Configurable activateGJFrame{"activateGJFrame", true, "Activate the THnSparse with cosThStar w.r.t. beam axis (Gottified jackson frame)"}; Configurable activateRandomFrame{"activateRandomFrame", false, "Activate the THnSparse with cosThStar w.r.t. random axis"}; Configurable cRotations{"cRotations", 3, "Number of random rotations in the rotational background"}; // Other cuts on Ks and glueball Configurable isapplyCompetingcut{"isapplyCompetingcut", false, "Competing cascade rejection cut"}; Configurable competingcascrejlambda{"competingcascrejlambda", 0.005, "rejecting competing cascade lambda"}; - Configurable tpcCrossedrows{"tpcCrossedrows", 70, "TPC crossed rows"}; - Configurable tpcCrossedrowsOverfcls{"tpcCrossedrowsOverfcls", 0.8, "TPC crossed rows over findable clusters"}; Configurable rotationalCut{"rotationalCut", 10, "Cut value (Rotation angle pi - pi/cut and pi + pi/cut)"}; + // event plane configurables + Configurable cfgnTotalSystem{"cfgnTotalSystem", 7, "Total number of detectors in qVectorsTable"}; + Configurable cfgDetName{"cfgDetName", "FT0C", "The name of detector to be analyzed"}; + Configurable cfgRefAName{"cfgRefAName", "TPCPos", "The name of detector for reference A"}; + Configurable cfgRefBName{"cfgRefBName", "TPCNeg", "The name of detector for reference B"}; + // // Mass and pT axis as configurables ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 5., 10., 30., 50., 70., 100., 110., 150.}, "Binning of the centrality axis"}; ConfigurableAxis configThnAxisPOL{"configThnAxisPOL", {20, -1.0, 1.0}, "Costheta axis"}; - ConfigurableAxis configThnAxisPhi{"configThnAxisPhi", {70, 0.0f, 7.0f}, "Phi axis"}; // 0 to 2pi + ConfigurableAxis configThnAxisPhi{"configThnAxisPhi", {12, 0.0f, o2::constants::math::TwoPI}, "Phi axis"}; // 0 to 2pi ConfigurableAxis ksMassBins{"ksMassBins", {200, 0.45f, 0.55f}, "K0s invariant mass axis"}; ConfigurableAxis cGlueMassBins{"cGlueMassBins", {200, 0.9f, 3.0f}, "Glueball invariant mass axis"}; ConfigurableAxis cPtBins{"cPtBins", {200, 0.0f, 20.0f}, "Glueball pT axis"}; ConfigurableAxis configAxisDeltaM{"configAxisDeltaM", {80, 0.0, 0.08}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configAxisAngleSep{"configAxisAngleSep", {200, 0.0, 2.0}, "Angular separation between V0s"}; - ConfigurableAxis configAxisPtCorr{"configAxisPtCorr", {1000, 0.0, 100.0}, "Pt correlation between two K0s"}; // fixed variables float rapidityMotherData = 0.5; float beamEnergy = 13600.0; double beamMomentum = std::sqrt(beamEnergy * beamEnergy / 4 - o2::constants::physics::MassProton * o2::constants::physics::MassProton); // GeV int noOfDaughters = 2; + float theta2 = 0.0f; + int detId = 0; + int refAId = 0; + int refBId = 0; + float minQvecAmp = 1e-5; + double tolerance = 1e-12; } config; // Service PDGdatabase; + Service pdgDB{}; TRandom* rn = new TRandom(); // variables declaration float multiplicity = 0.0f; - float theta2; - ROOT::Math::PxPyPzMVector daughter1, daughter2, daughterRot, daughterRotCM, mother, motherRot, fourVecDauCM, fourVecDauCM1; + ROOT::Math::PxPyPzMVector daughter1, daughter2, daughterRot, daughterRotCM, mother, motherRot, fourVecDauCM, fourVecDauCM1, mother2, mother3; ROOT::Math::PxPyPzEVector mother1; ROOT::Math::XYZVector randomVec, beamVec, normalVec; ROOT::Math::XYZVectorF v1CM, zaxisHE, yaxisHE, xaxisHE; - // ROOT::Math::XYZVector threeVecDauCM, helicityVec, randomVec, beamVec, normalVec; ROOT::Math::XYZVector zBeam; // ẑ: beam direction in lab frame ROOT::Math::PxPyPzEVector beam1{0., 0., -config.beamMomentum, 13600. / 2.}; ROOT::Math::PxPyPzEVector beam2{0., 0., config.beamMomentum, 13600. / 2.}; @@ -218,6 +220,8 @@ struct HigherMassResonances { // const double massK0s = o2::constants::physics::MassK0Short; bool isMix = false; + EventPlaneHelper helperEP; + void init(InitContext const&) { rctChecker.init(rctCut.cfgEvtRCTFlagCheckerLabel, rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); @@ -232,10 +236,12 @@ struct HigherMassResonances { AxisSpec thnAxisPhi = {config.configThnAxisPhi, "Configurabel phi axis"}; // 0 to 2pi AxisSpec deltaMAxis = {config.configAxisDeltaM, "#Delta M (GeV/#it{c}^{2})"}; AxisSpec angleSepAxis = {config.configAxisAngleSep, "Angular separation between V0s"}; - AxisSpec ptCorrAxis = {config.configAxisPtCorr, "Pt correlation between two K0s"}; + AxisSpec axisCentQA = {100, 0, 100, ""}; + AxisSpec axisEvtPlQA = {100, -o2::constants::math::PI, o2::constants::math::PI, ""}; + AxisSpec axisEvtResPlQA = {102, -1.02, 1.02, ""}; // THnSparses - std::array sparses = {config.activateHelicityFrame, config.activateCollinsSoperFrame, config.activateProductionFrame, config.activateBeamAxisFrame, config.activateRandomFrame}; + std::array sparses = {config.activateHelicityFrame, config.activateCollinsSoperFrame, config.activateProductionFrame, config.activateGJFrame, config.activateRandomFrame}; if (std::accumulate(sparses.begin(), sparses.end(), 0) == 0) { LOGP(fatal, "No output THnSparses enabled"); @@ -249,7 +255,7 @@ struct HigherMassResonances { if (config.activateProductionFrame) { LOGP(info, "THnSparse with cosThStar w.r.t. production axis active."); } - if (config.activateBeamAxisFrame) { + if (config.activateGJFrame) { LOGP(info, "THnSparse with cosThStar w.r.t. beam axis active. (Gottified jackson frame)"); } if (config.activateRandomFrame) { @@ -270,18 +276,11 @@ struct HigherMassResonances { std::shared_ptr hCutFlow = rEventSelection.get(HIST("hEventCut")); hCutFlow->GetXaxis()->SetBinLabel(1, "All Events"); hCutFlow->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); - hCutFlow->GetXaxis()->SetBinLabel(3, "sel8"); - hCutFlow->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); - hCutFlow->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); - hCutFlow->GetXaxis()->SetBinLabel(6, "kNoSameBunchPileup"); - hCutFlow->GetXaxis()->SetBinLabel(7, "kIsGoodITSLayersAll"); - hCutFlow->GetXaxis()->SetBinLabel(8, "Occupancy Cut"); - hCutFlow->GetXaxis()->SetBinLabel(9, "rctChecker"); - hCutFlow->GetXaxis()->SetBinLabel(10, "kIsTriggerTVX"); - hCutFlow->GetXaxis()->SetBinLabel(11, "No selection"); - hCutFlow->GetXaxis()->SetBinLabel(12, "IsINELgt0"); - // hCutFlow->GetXaxis()->SetBinLabel(13, "isVertexITSTPC"); - // hCutFlow->GetXaxis()->SetBinLabel(14, "isVertexTOFMatched"); + hCutFlow->GetXaxis()->SetBinLabel(3, "kNoTimeFrameBorder"); + hCutFlow->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + hCutFlow->GetXaxis()->SetBinLabel(5, "kIsTriggerTVX"); + hCutFlow->GetXaxis()->SetBinLabel(6, "rctChecker"); + hCutFlow->GetXaxis()->SetBinLabel(7, "IsINELgt0"); std::shared_ptr hv0label = rEventSelection.get(HIST("htrackscheck_v0")); hv0label->GetXaxis()->SetBinLabel(1, "All Tracks"); @@ -291,23 +290,22 @@ struct HigherMassResonances { hv0label->GetXaxis()->SetBinLabel(5, "Daughter DCA"); hv0label->GetXaxis()->SetBinLabel(6, "CosPA"); hv0label->GetXaxis()->SetBinLabel(7, "Decay Radius"); - hv0label->GetXaxis()->SetBinLabel(8, "Lifetime"); - hv0label->GetXaxis()->SetBinLabel(9, "CompetingCascade"); - hv0label->GetXaxis()->SetBinLabel(10, "Standard V0"); - hv0label->GetXaxis()->SetBinLabel(11, "Mass Tolerance"); + hv0label->GetXaxis()->SetBinLabel(8, "Armenteros-Podolanski"); + hv0label->GetXaxis()->SetBinLabel(9, "Lifetime"); + hv0label->GetXaxis()->SetBinLabel(10, "CompetingCascade"); + hv0label->GetXaxis()->SetBinLabel(11, "Standard V0"); + hv0label->GetXaxis()->SetBinLabel(12, "Mass Tolerance"); std::shared_ptr hv0DauLabel = rEventSelection.get(HIST("htrackscheck_v0_daughters")); hv0DauLabel->GetXaxis()->SetBinLabel(1, "AllDau Tracks"); hv0DauLabel->GetXaxis()->SetBinLabel(2, "has TPC"); - hv0DauLabel->GetXaxis()->SetBinLabel(3, "TPC CrossedRows"); - hv0DauLabel->GetXaxis()->SetBinLabel(4, "TPC CRFC"); - hv0DauLabel->GetXaxis()->SetBinLabel(5, "TPC Chi2NCL"); - hv0DauLabel->GetXaxis()->SetBinLabel(6, "Charge"); - hv0DauLabel->GetXaxis()->SetBinLabel(7, "Charge"); - hv0DauLabel->GetXaxis()->SetBinLabel(8, "Eta"); - hv0DauLabel->GetXaxis()->SetBinLabel(9, "PID TPC"); - hv0DauLabel->GetXaxis()->SetBinLabel(10, "PID TOF"); - hv0DauLabel->GetXaxis()->SetBinLabel(11, "Pt cut"); + hv0DauLabel->GetXaxis()->SetBinLabel(3, "TPC Chi2NCL"); + hv0DauLabel->GetXaxis()->SetBinLabel(4, "Charge"); + hv0DauLabel->GetXaxis()->SetBinLabel(5, "Charge"); + hv0DauLabel->GetXaxis()->SetBinLabel(6, "Eta"); + hv0DauLabel->GetXaxis()->SetBinLabel(7, "PID TPC"); + hv0DauLabel->GetXaxis()->SetBinLabel(8, "PID TOF"); + hv0DauLabel->GetXaxis()->SetBinLabel(9, "Pt cut"); std::shared_ptr hv0labelmcrec = hMChists.get(HIST("events_checkrec")); hv0labelmcrec->GetXaxis()->SetBinLabel(1, "All Tracks"); @@ -338,9 +336,13 @@ struct HigherMassResonances { hglue.add("h3glueInvMassME", "h3glueInvMassME", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPOL, thnAxisPhi}, true); hglue.add("h3glueInvMassRot", "h3glueInvMassRot", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPOL, thnAxisPhi}, true); } else { - hglue.add("h3glueInvMassDS", "h3glueInvMassDS", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, deltaMAxis, angleSepAxis, ptCorrAxis}, true); - hglue.add("h3glueInvMassME", "h3glueInvMassME", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, deltaMAxis, angleSepAxis, ptCorrAxis}, true); - hglue.add("h3glueInvMassRot", "h3glueInvMassRot", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, deltaMAxis, angleSepAxis, ptCorrAxis}, true); + hglue.add("h3glueInvMassDS", "h3glueInvMassDS", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, deltaMAxis, angleSepAxis, k0ShortMassAxis, k0ShortMassAxis}, true); + hglue.add("h3glueInvMassME", "h3glueInvMassME", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, deltaMAxis, angleSepAxis, k0ShortMassAxis, k0ShortMassAxis}, true); + hglue.add("h3glueInvMassRot", "h3glueInvMassRot", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, deltaMAxis, angleSepAxis, k0ShortMassAxis, k0ShortMassAxis}, true); + if (doprocessSEEP) { + hglue.add("h3glueInvMassEPDS", "h3glueInvMassEPDS", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPhi}, true); + hglue.add("h3glueInvMassEPRot", "h3glueInvMassEPRot", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPhi}, true); + } } // K0s topological/PID cuts @@ -352,8 +354,7 @@ struct HigherMassResonances { // Invariant Mass rKzeroShort.add("hMassK0Shortbefore", "hMassK0Shortbefore", kTHnSparseF, {k0ShortMassAxis, ptAxis}); rKzeroShort.add("hK0ShortMassCorr", "hK0ShortMassCorr", kTHnSparseF, {k0ShortMassAxis, k0ShortMassAxis, deltaMAxis}); - // rKzeroShort.add("hK0ShortMassCorrAfterCut", "hK0ShortMassCorrAfterCut", kTH2F, {k0ShortMassAxis, k0ShortMassAxis}); - rKzeroShort.add("hK0sPtCorrelation", "hK0sPtCorrelation", kTH1F, {{1000, 0.0f, 100.0f}}); + rKzeroShort.add("hMassK0ShortSelected", "hMassK0ShortSelected", kTHnSparseF, {k0ShortMassAxis, ptAxis}); // Topological histograms (after the selection) rKzeroShort.add("hDCAV0Daughters", "DCA between v0 daughters", {HistType::kTH1F, {{60, -3.0f, 3.0f}}}); @@ -361,6 +362,7 @@ struct HigherMassResonances { rKzeroShort.add("hLT", "hLT", {HistType::kTH1F, {{100, 0.0f, 50.0f}}}); rKzeroShort.add("angularSeparation", "Angular distribution between two K0s vs pT", {HistType::kTH1F, {{200, 0.0f, 4.0f}}}); rKzeroShort.add("hDauDeltaR", "Delta R of positive and negative daughers", {HistType::kTHnSparseF, {angleSepAxis, angleSepAxis}}); + rKzeroShort.add("hArmenterosPodolanski", "Armenteros-Podolanski plot", HistType::kTH2D, {AxisSpec{100, -1, 1, "#alpha"}, {200, 0, 0.5, "qtArm"}}); } rKzeroShort.add("NksProduced", "Number of K0s produced", kTH1I, {{15, -0.5, 14.5}}); @@ -369,20 +371,29 @@ struct HigherMassResonances { rKzeroShort.add("hNSigmaPosPionK0s_after", "hNSigmaPosPionK0s_after", {HistType::kTH2F, {{ptAxis}, {100, -5.f, 5.f}}}); rKzeroShort.add("hNSigmaNegPionK0s_before", "hNSigmaNegPionK0s_before", {HistType::kTH2F, {{ptAxis}, {100, -5.f, 5.f}}}); rKzeroShort.add("hNSigmaNegPionK0s_after", "hNSigmaNegPionK0s_after", {HistType::kTH2F, {{ptAxis}, {100, -5.f, 5.f}}}); - // rKzeroShort.add("dE_by_dx_TPC", "dE/dx signal in the TPC as a function of pT", kTH2F, {config.axisPtfordEbydx, config.axisdEdx}); } - // For MC + // For Monte Carlo if (doprocessGen || doprocessRec) { hMChists.add("MCcorrections/hGenNo", "Generated collisions before and after event selection", kTH1F, {{5, 0.0f, 5.0f}}); hMChists.add("Genf1710", "Gen f_{0}(1710)", kTHnSparseF, {multiplicityAxis, ptAxis, thnAxisPOL}); hMChists.add("Genf1710Calib", "Calibrated Gen f_{0}(1710)", kTHnSparseF, {multiplicityAxis, ptAxis, thnAxisPOL}); + hMChists.add("Gen1710PWA", "Gen f_{0}(1710) PWA", kTHnSparseF, {glueballMassAxis, thnAxisPOL, thnAxisPhi}); hMChists.add("Genf17102", "Gen f_{0}(1710)", kTHnSparseF, {multiplicityAxis, ptAxis, thnAxisPOL}); hMChists.add("Genf1710Calib2", "Calibrated Gen f_{0}(1710)", kTHnSparseF, {multiplicityAxis, ptAxis, thnAxisPOL}); + hMChists.add("Gen1710PWA2", "Gen f_{0}(1710) PWA", kTHnSparseF, {glueballMassAxis, thnAxisPOL, thnAxisPhi}); + hMChists.add("GenThetavsPhi1", "GenThetavsPhi1", kTH2F, {{thnAxisPOL}, {thnAxisPhi}}); + hMChists.add("GenThetavsPhi2", "GenThetavsPhi2", kTH2F, {{thnAxisPOL}, {thnAxisPhi}}); + hMChists.add("Recf1710_pt1", "Rec f_{0}(1710) p_{T}", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPOL}); hMChists.add("Recf1710Calib_pt1", "Calibrated Rec f_{0}(1710) p_{T}", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPOL}); + hMChists.add("Recf1710PWA_pt1", "Rec f_{0}(1710) PWA", kTHnSparseF, {glueballMassAxis, thnAxisPOL, thnAxisPhi}); hMChists.add("Recf1710_pt2", "Rec f_{0}(1710) p_{T}", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPOL}); hMChists.add("Recf1710Calib_pt2", "Calibrated Rec f_{0}(1710) p_{T}", kTHnSparseF, {multiplicityAxis, ptAxis, glueballMassAxis, thnAxisPOL}); + hMChists.add("Recf1710PWA_pt2", "Rec f_{0}(1710) PWA", kTHnSparseF, {glueballMassAxis, thnAxisPOL, thnAxisPhi}); + hMChists.add("RecThetavsPhi1", "RecTheta vs Phi1", kTH2F, {{thnAxisPOL}, {thnAxisPhi}}); + hMChists.add("RecThetavsPhi2", "RecTheta vs Phi2", kTH2F, {{thnAxisPOL}, {thnAxisPhi}}); + hMChists.add("h1Recsplit", "Rec p_{T}2", kTH1F, {ptAxis}); hMChists.add("Genf1710_mass", "Gen f_{0}(1710) mass", kTH1F, {glueballMassAxis}); hMChists.add("Genf1710_mass2", "Gen f_{0}(1710) mass", kTH1F, {glueballMassAxis}); @@ -417,6 +428,47 @@ struct HigherMassResonances { hMChists.add("MCcorrections/hSignalLossNumerator3", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hMChists.add("MCcorrections/hMultvsCent", "Kstar generated after event selection", kTH2F, {{multiplicityAxis}, {multiplicityAxis}}); } + + if (doprocessSEEP) { + config.detId = getdetId(config.cfgDetName); + config.refAId = getdetId(config.cfgRefAName); + config.refBId = getdetId(config.cfgRefBName); + + hglue.add("EpDet", "", {HistType::kTH2F, {axisCentQA, axisEvtPlQA}}); + hglue.add("EpRefA", "", {HistType::kTH2F, {axisCentQA, axisEvtPlQA}}); + hglue.add("EpRefB", "", {HistType::kTH2F, {axisCentQA, axisEvtPlQA}}); + + hglue.add("EpResDetRefA", "", {HistType::kTH2F, {axisCentQA, axisEvtResPlQA}}); + hglue.add("EpResDetRefB", "", {HistType::kTH2F, {axisCentQA, axisEvtResPlQA}}); + hglue.add("EpResRefARefB", "", {HistType::kTH2F, {axisCentQA, axisEvtResPlQA}}); + } + } + + template + int getdetId(const T& name) + { + if (name.value == "FT0C") { + return 0; + } + if (name.value == "FT0A") { + return 1; + } + if (name.value == "FT0M") { + return 2; + } + if (name.value == "FV0A") { + return 3; + } + if (name.value == "TPCPos") { + return 4; + } + if (name.value == "TPCNeg") { + return 5; + } + if (name.value == "TPCTot") { + return 6; + } + return 0; } template @@ -430,71 +482,31 @@ struct HigherMassResonances { if (fillHist) rEventSelection.fill(HIST("hEventCut"), 1); - // if (config.isSel8 && !collision.sel8()) - // return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 2); - if (config.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 3); + rEventSelection.fill(HIST("hEventCut"), 2); if (config.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 4); - - // if (config.isNoSameBunchPileup && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup))) - // return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 5); + rEventSelection.fill(HIST("hEventCut"), 3); - if (config.isAllLayersGoodITS && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + if (config.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 6); - - // if (config.isNoCollInTimeRangeStandard && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) - // return false; - - // if (config.isApplyOccCut && (std::abs(collision.trackOccupancyInTimeRange()) > config.configOccCut)) - // return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 7); + rEventSelection.fill(HIST("hEventCut"), 4); if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 8); - - if (config.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) - return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 9); - - // if (config.isGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) - // return false; - // if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 10); + rEventSelection.fill(HIST("hEventCut"), 5); if (config.isINELgt0 && !collision.isInelGt0()) { return false; } if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 11); - - // if (config.isVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { - // return false; - // } - // if (fillHist) - // rEventSelection.fill(HIST("hEventCut"), 12); - - // if (config.isVertexTOFMatched && !collision.selection_bit(aod::evsel::kIsVertexTOFmatched)) { - // return false; - // } - // if (fillHist) - // rEventSelection.fill(HIST("hEventCut"), 13); + rEventSelection.fill(HIST("hEventCut"), 6); return true; } @@ -510,48 +522,25 @@ struct HigherMassResonances { if (fillHist) rEventSelection.fill(HIST("hEventCut"), 1); - // if (config.isSel8 && !collision.sel8()) - // return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 2); - if (config.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 3); + rEventSelection.fill(HIST("hEventCut"), 2); if (config.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 4); - - // if (config.isNoSameBunchPileup && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup))) - // return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 5); + rEventSelection.fill(HIST("hEventCut"), 3); - if (config.isAllLayersGoodITS && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + if (config.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 6); - - // if (config.isNoCollInTimeRangeStandard && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) - // return false; - - // if (config.isApplyOccCut && (std::abs(collision.trackOccupancyInTimeRange()) > config.configOccCut)) - // return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 7); + rEventSelection.fill(HIST("hEventCut"), 4); if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) return false; if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 8); - - if (config.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) - return false; - if (fillHist) - rEventSelection.fill(HIST("hEventCut"), 9); + rEventSelection.fill(HIST("hEventCut"), 5); return true; } @@ -559,9 +548,6 @@ struct HigherMassResonances { template bool selectionV0(Collision const& collision, V0 const& candidate, float /*multiplicity*/) { - // const float qtarm = candidate.qtarm(); - // const float alph = candidate.alpha(); - // float arm = qtarm / alph; const float pT = candidate.pt(); const float tranRad = candidate.v0radius(); const float dcaDaughv0 = candidate.dcaV0daughters(); @@ -576,6 +562,7 @@ struct HigherMassResonances { rKzeroShort.fill(HIST("hLT"), ctauK0s); rKzeroShort.fill(HIST("hDCAV0Daughters"), candidate.dcaV0daughters()); rKzeroShort.fill(HIST("hV0CosPA"), candidate.v0cosPA()); + rKzeroShort.fill(HIST("hArmenterosPodolanski"), candidate.alpha(), candidate.qtarm()); } if (config.qAcorrelation2Dhist) rKzeroShort.fill(HIST("mass_lambda_kshort_before"), candidate.mK0Short(), candidate.mLambda()); @@ -612,9 +599,9 @@ struct HigherMassResonances { } rEventSelection.fill(HIST("htrackscheck_v0"), 6.5); - // if (tranRad > config.confV0TranRadV0Max) { - // return false; - // } + if (candidate.qtarm() < config.cfgArmenterosCut * std::fabs(candidate.alpha())) { + return false; + } rEventSelection.fill(HIST("htrackscheck_v0"), 7.5); if (std::fabs(ctauK0s) > config.cMaxV0LifeTime) { @@ -664,38 +651,30 @@ struct HigherMassResonances { return false; rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 1.5); - if (track.tpcNClsCrossedRows() < config.tpcCrossedrows) - return false; - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 2.5); - - if (track.tpcCrossedRowsOverFindableCls() < config.tpcCrossedrowsOverfcls) - return false; - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 3.5); - if (tpcNClsF < config.confDaughTPCnclsMin) { return false; } - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 4.5); + rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 2.5); if (charge < 0 && sign > 0) { return false; } - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 5.5); + rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 3.5); if (charge > 0 && sign < 0) { return false; } - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 6.5); + rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 4.5); if (std::abs(eta) > config.confDaughEta) { return false; } - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 7.5); + rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 5.5); if (std::abs(nsigmaV0DaughterTPC) > config.confDaughPIDCutTPC) { return false; } - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 8.5); + rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 6.5); if (std::abs(v0candidate.tofNSigmaK0PiPlus()) > config.confDaughPIDCutTOF && v0candidate.positiveHasTOF()) { return false; @@ -704,12 +683,12 @@ struct HigherMassResonances { if (std::abs(v0candidate.tofNSigmaK0PiMinus()) > config.confDaughPIDCutTOF && v0candidate.negativeHasTOF()) { return false; } - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 9.5); + rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 7.5); if (track.pt() < config.confPiPtMin || track.pt() > config.confPiPtMax) { return false; } - rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 10.5); + rEventSelection.fill(HIST("htrackscheck_v0_daughters"), 8.5); if (config.qAPID) { (charge == 1) ? rKzeroShort.fill(HIST("hNSigmaPosPionK0s_after"), track.tpcInnerParam(), track.tpcNSigmaPi()) : rKzeroShort.fill(HIST("hNSigmaNegPionK0s_after"), track.tpcInnerParam(), track.tpcNSigmaPi()); @@ -725,7 +704,125 @@ struct HigherMassResonances { return std::sqrt(d1 * d1 + d2 * d2); } - using EventCandidatesDerivedData = soa::Join; + enum FrameType { kHelicity = 0, + kCollinsSoper = 1, + kProduction = 2, + kGottfriedJackson = 3, + kRandom = 4 }; + + std::pair getCosThetaPhi(const ROOT::Math::PxPyPzMVector& motherVec, const ROOT::Math::PxPyPzMVector& daughterVec, int frame) + { + //---------------------------------------------------------------- + // Boost daughter into mother rest frame + //---------------------------------------------------------------- + ROOT::Math::Boost boost{motherVec.BoostToCM()}; + + auto dauCM = boost(daughterVec); + + // Unit vector of daughter momentum in mother rest frame + auto v1cm = ROOT::Math::XYZVectorF(dauCM.Vect()).Unit(); + + //---------------------------------------------------------------- + // Beam directions boosted into mother rest frame + //---------------------------------------------------------------- + auto beam1cm = ROOT::Math::XYZVectorF((boost(beam1).Vect()).Unit()); + + auto beam2cm = ROOT::Math::XYZVectorF((boost(beam2).Vect()).Unit()); + + double cosTheta = 0.; + double phi = 0.; + + if (frame == kHelicity) { + //============================================================== + // HELICITY FRAME + // z-axis : Mother momentum direction in laboratory frame + // y-axis : Normal to beam plane + // x-axis : Completes right-handed coordinate system + //============================================================== + + auto zaxisHELocal = ROOT::Math::XYZVectorF(motherVec.Vect()).Unit(); + auto yaxisHELocal = ROOT::Math::XYZVectorF(beam1cm.Cross(beam2cm)).Unit(); + auto xaxisHELocal = ROOT::Math::XYZVectorF(yaxisHELocal.Cross(zaxisHELocal)).Unit(); + cosTheta = motherVec.Vect().Dot(dauCM.Vect()) / (std::sqrt(dauCM.Vect().Mag2()) * std::sqrt(motherVec.Vect().Mag2())); + phi = std::atan2(yaxisHELocal.Dot(v1cm), xaxisHELocal.Dot(v1cm)); + phi = RecoDecay::constrainAngle(phi, 0.0); + } else if (frame == kCollinsSoper) { + //============================================================== + // COLLINS-SOPER FRAME + // z-axis : Bisector of the two beam directions + // y-axis : Normal to beam plane + // x-axis : Completes right-handed system + //============================================================== + + auto zAxisCSLocal = ROOT::Math::XYZVectorF((beam1cm.Unit() - beam2cm.Unit())).Unit(); + auto yAxisCSLocal = ROOT::Math::XYZVectorF(beam1cm.Cross(beam2cm)).Unit(); + auto xAxisCSLocal = ROOT::Math::XYZVectorF(yAxisCSLocal.Cross(zAxisCSLocal)).Unit(); + cosTheta = zAxisCSLocal.Dot(v1cm); + phi = std::atan2(yAxisCSLocal.Dot(v1cm), xAxisCSLocal.Dot(v1cm)); + phi = RecoDecay::constrainAngle(phi, 0.0); + } else if (frame == kProduction) { + //============================================================== + // PRODUCTION FRAME + // z-axis : Normal to production plane + // + // Production plane is defined by: + // beam direction + // mother momentum + // Used occasionally in spin-alignment analyses. + //============================================================== + + ROOT::Math::XYZVector normalVecLocal = ROOT::Math::XYZVector(motherVec.Py(), -motherVec.Px(), 0.f); + cosTheta = normalVecLocal.Dot(dauCM.Vect()) / (std::sqrt(dauCM.Vect().Mag2()) * std::sqrt(normalVecLocal.Mag2())); + // for production frame reuse helicity-style azimuthal angle + auto zaxisHELocal = ROOT::Math::XYZVectorF(motherVec.Vect()).Unit(); + auto yaxisHELocal = ROOT::Math::XYZVectorF(beam1cm.Cross(beam2cm)).Unit(); + auto xaxisHELocal = ROOT::Math::XYZVectorF(yaxisHELocal.Cross(zaxisHELocal)).Unit(); + phi = std::atan2(yaxisHELocal.Dot(v1cm), xaxisHELocal.Dot(v1cm)); + phi = RecoDecay::constrainAngle(phi, 0.0); + } else if (frame == kGottfriedJackson) { + // ------------------------------------------------------------------ + // Gottfried-Jackson (GJ') frame + // + // z-axis : beam direction in the mother rest frame + // y-axis : normal to the production plane + // x-axis : right-handed coordinate system + // ------------------------------------------------------------------ + + // z-axis: beam direction boosted into mother rest frame + auto zAxisGJ = beam1cm.Unit(); + + // Mother momentum in LAB (defines production plane) + auto motherLab = ROOT::Math::XYZVectorF(motherVec.Vect()).Unit(); + + // Normal to production plane + auto yAxisGJ = ROOT::Math::XYZVectorF(zAxisGJ.Cross(motherLab)); + + if (yAxisGJ.Mag2() > config.tolerance) + yAxisGJ = yAxisGJ.Unit(); + else + yAxisGJ = ROOT::Math::XYZVectorF(0.f, 1.f, 0.f); + + // Complete right-handed system + ROOT::Math::XYZVectorF xAxisGJ = ROOT::Math::XYZVectorF(yAxisGJ.Cross(zAxisGJ)).Unit(); + + // Daughter direction in mother rest frame + cosTheta = zAxisGJ.Dot(v1cm); + + phi = std::atan2(yAxisGJ.Dot(v1cm), xAxisGJ.Dot(v1cm)); + + phi = RecoDecay::constrainAngle(phi, 0.0); + } else { // kRandom or fallback + auto phiRandom = gRandom->Uniform(0.f, constants::math::TwoPI); + auto thetaRandom = gRandom->Uniform(0.f, constants::math::PI); + ROOT::Math::XYZVector randomVecLocal = ROOT::Math::XYZVector(std::sin(thetaRandom) * std::cos(phiRandom), std::sin(thetaRandom) * std::sin(phiRandom), std::cos(thetaRandom)); + cosTheta = randomVecLocal.Dot(dauCM.Vect()) / std::sqrt(dauCM.Vect().Mag2()); + phi = phiRandom; + } + + return {cosTheta, phi}; + } + + using EventCandidatesDerivedData = soa::Join; using V0CandidatesDerivedData = soa::Join; // using DauTracks = soa::Join; using DauTracks = soa::Join; @@ -743,16 +840,16 @@ struct HigherMassResonances { return false; } - if (posTrackExtra.tpcNClsCrossedRows() < config.tpcCrossedrows || negTrackExtra.tpcNClsCrossedRows() < config.tpcCrossedrows) { - return false; - } + // if (posTrackExtra.tpcNClsCrossedRows() < config.tpcCrossedrows || negTrackExtra.tpcNClsCrossedRows() < config.tpcCrossedrows) { + // return false; + // } if (posTrackExtra.tpcNClsFound() < config.confDaughTPCnclsMin || negTrackExtra.tpcNClsFound() < config.confDaughTPCnclsMin) { return false; } - if (posTrackExtra.tpcCrossedRowsOverFindableCls() < config.tpcCrossedrowsOverfcls || negTrackExtra.tpcCrossedRowsOverFindableCls() < config.tpcCrossedrowsOverfcls) { - return false; - } + // if (posTrackExtra.tpcCrossedRowsOverFindableCls() < config.tpcCrossedrowsOverfcls || negTrackExtra.tpcCrossedRowsOverFindableCls() < config.tpcCrossedrowsOverfcls) { + // return false; + // } // check TPC PID if (((std::abs(posTrackExtra.tpcNSigmaPi()) > config.confDaughPIDCutTPC) || (std::abs(negTrackExtra.tpcNSigmaPi()) > config.confDaughPIDCutTPC))) { @@ -799,6 +896,7 @@ struct HigherMassResonances { using EventCandidates = soa::Filtered>; using TrackCandidates = soa::Filtered>; using V0TrackCandidate = soa::Join; + using EventCandidateEPs = soa::Filtered>; // For Monte Carlo using EventCandidatesMC = soa::Join; using TrackCandidatesMC = soa::Filtered>; @@ -807,185 +905,114 @@ struct HigherMassResonances { // zBeam direction in lab frame template - void fillInvMass(const T& mother, float multiplicity, const T& daughter1, const T& daughter2, bool isMix) + void fillInvMass(const T& motherVec, float eventMultiplicity, const T& daughterVec1, const T& daughterVec2, bool isMixed) { - // //polarization calculations - // zBeam = ROOT::Math::XYZVector(0.f, 0.f, 1.f); // ẑ: beam direction in lab frame - - ROOT::Math::Boost boost{mother.BoostToCM()}; // define the boost to the center of mass frame - fourVecDauCM = boost(daughter1); // boost the frame of daughter to the center of mass frame - // threeVecDauCM = fourVecDauCM.Vect(); // get the 3 vector of daughter in the frame of mother - - beam1CM = ROOT::Math::XYZVectorF((boost(beam1).Vect()).Unit()); - beam2CM = ROOT::Math::XYZVectorF((boost(beam2).Vect()).Unit()); - - //========================Helicity and Production frame calculation========================== - // define y = zBeam x z: Normal to the production plane - // ẑ: mother direction in lab, boosted into mother's rest frame - - // auto motherLabDirection = ROOT::Math::XYZVector(0, 0, mother.Vect().Z()); // ẑ axis in lab frame - - // // ŷ = zBeam × ẑ - // auto y_axis = zBeam.Cross(motherLabDirection).Unit(); - - // // x̂ = ŷ × ẑ - // auto x_axis = y_axis.Cross(motherLabDirection).Unit(); - - // // Project daughter momentum onto x–y plane - // auto p_proj_x = threeVecDauCM.Dot(x_axis); - // auto p_proj_y = threeVecDauCM.Dot(y_axis); - - // // Calculate φ in [-π, π] - // auto anglePhi = std::atan2(p_proj_y, p_proj_x); // φ in radians - //============================================================================================= - - v1CM = ROOT::Math::XYZVectorF(boost(daughter1).Vect()).Unit(); - // ROOT::Math::XYZVectorF v2_CM{(boost(daughter1).Vect()).Unit()}; - // using positive sign convention for the first track - // ROOT::Math::XYZVectorF v_CM = (t1.sign() > 0 ? v1CM : v2_CM); // here selected decay daughter momentum is intested. here you can choose one decay daughter no need to check both case as it is neutral particle for our case - // Helicity Frame - zaxisHE = ROOT::Math::XYZVectorF(mother.Vect()).Unit(); - yaxisHE = ROOT::Math::XYZVectorF(beam1CM.Cross(beam2CM)).Unit(); - xaxisHE = ROOT::Math::XYZVectorF(yaxisHE.Cross(zaxisHE)).Unit(); - - // CosThetaHE = zaxisHE.Dot(v_CM); - - auto anglePhi = std::atan2(yaxisHE.Dot(v1CM), xaxisHE.Dot(v1CM)); - anglePhi = RecoDecay::constrainAngle(anglePhi, 0.0); - // if (anglePhi < 0) { - // anglePhi += o2::constants::math::TwoPI; // ensure phi is in [0, 2pi] - // } - - // CS Frame - zAxisCS = ROOT::Math::XYZVectorF((beam1CM.Unit() - beam2CM.Unit())).Unit(); - yAxisCS = ROOT::Math::XYZVectorF(beam1CM.Cross(beam2CM)).Unit(); - xAxisCS = ROOT::Math::XYZVectorF(yAxisCS.Cross(zAxisCS)).Unit(); - double cosThetaStarCS = zAxisCS.Dot(v1CM); - auto phiCS = std::atan2(yAxisCS.Dot(v1CM), xAxisCS.Dot(v1CM)); - phiCS = RecoDecay::constrainAngle(phiCS, 0.0); - - // if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { if (config.activateHelicityFrame) { - // helicityVec = mother.Vect(); // 3 vector of mother in COM frame - // auto cosThetaStarHelicity = helicityVec.Dot(threeVecDauCM) / (std::sqrt(threeVecDauCM.Mag2()) * std::sqrt(helicityVec.Mag2())); - auto cosThetaStarHelicity = mother.Vect().Dot(fourVecDauCM.Vect()) / (std::sqrt(fourVecDauCM.Vect().Mag2()) * std::sqrt(mother.Vect().Mag2())); - if (!isMix) { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), cosThetaStarHelicity, anglePhi); + auto pr = getCosThetaPhi(motherVec, daughterVec1, kHelicity); + if (!isMixed) { + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassDS"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } for (int i = 0; i < config.cRotations; i++) { - theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - motherRot = daughterRot + daughter2; + daughterRot = ROOT::Math::PxPyPzMVector(daughterVec1.Px() * std::cos(config.theta2) - daughterVec1.Py() * std::sin(config.theta2), daughterVec1.Px() * std::sin(config.theta2) + daughterVec1.Py() * std::cos(config.theta2), daughterVec1.Pz(), daughterVec1.M()); - ROOT::Math::Boost boost2{motherRot.BoostToCM()}; - daughterRotCM = boost2(daughterRot); + motherRot = daughterRot + daughterVec2; - auto cosThetaStarHelicityRot = motherRot.Vect().Dot(daughterRotCM.Vect()) / (std::sqrt(daughterRotCM.Vect().Mag2()) * std::sqrt(motherRot.Vect().Mag2())); - auto phiHelicityRot = std::atan2(yaxisHE.Dot(daughterRotCM.Vect().Unit()), xaxisHE.Dot(daughterRotCM.Vect().Unit())); - phiHelicityRot = RecoDecay::constrainAngle(phiHelicityRot, 0.0); - if (motherRot.Rapidity() < config.rapidityMotherData) - hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarHelicityRot, phiHelicityRot); + auto prrot = getCosThetaPhi(motherRot, daughterRot, kHelicity); + if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) + hglue.fill(HIST("h3glueInvMassRot"), eventMultiplicity, motherRot.Pt(), motherRot.M(), prrot.first, prrot.second); } } else { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), cosThetaStarHelicity, anglePhi); + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassME"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } } } else if (config.activateCollinsSoperFrame) { - if (!isMix) { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), cosThetaStarCS, phiCS); + auto pr = getCosThetaPhi(motherVec, daughterVec1, kCollinsSoper); + if (!isMixed) { + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassDS"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } for (int i = 0; i < config.cRotations; i++) { - theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); - - motherRot = daughterRot + daughter2; + daughterRot = ROOT::Math::PxPyPzMVector(daughterVec1.Px() * std::cos(config.theta2) - daughterVec1.Py() * std::sin(config.theta2), daughterVec1.Px() * std::sin(config.theta2) + daughterVec1.Py() * std::cos(config.theta2), daughterVec1.Pz(), daughterVec1.M()); - ROOT::Math::Boost boost2{motherRot.BoostToCM()}; - daughterRotCM = boost2(daughterRot); + motherRot = daughterRot + daughterVec2; - auto cosThetaStarCSrot = zAxisCS.Dot(daughterRotCM.Vect()) / std::sqrt(daughterRotCM.Vect().Mag2()); - auto phiCSrot = std::atan2(yAxisCS.Dot(daughterRotCM.Vect().Unit()), xAxisCS.Dot(daughterRotCM.Vect().Unit())); - phiCSrot = RecoDecay::constrainAngle(phiCSrot, 0.0); + auto prrot = getCosThetaPhi(motherRot, daughterRot, kCollinsSoper); - if (motherRot.Rapidity() < config.rapidityMotherData) - hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarCSrot, phiCSrot); + if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) + hglue.fill(HIST("h3glueInvMassRot"), eventMultiplicity, motherRot.Pt(), motherRot.M(), prrot.first, prrot.second); } } else { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), cosThetaStarCS, phiCS); + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassME"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } } } else if (config.activateProductionFrame) { - normalVec = ROOT::Math::XYZVector(mother.Py(), -mother.Px(), 0.f); - auto cosThetaProduction = normalVec.Dot(fourVecDauCM.Vect()) / (std::sqrt(fourVecDauCM.Vect().Mag2()) * std::sqrt(normalVec.Mag2())); - if (!isMix) { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), cosThetaProduction, anglePhi); + auto pr = getCosThetaPhi(motherVec, daughterVec1, kProduction); + if (!isMixed) { + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassDS"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } for (int i = 0; i < config.cRotations; i++) { - theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - motherRot = ROOT::Math::PxPyPzMVector(mother.Px() * std::cos(theta2) - mother.Py() * std::sin(theta2), mother.Px() * std::sin(theta2) + mother.Py() * std::cos(theta2), mother.Pz(), mother.M()); + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); + motherRot = ROOT::Math::PxPyPzMVector(motherVec.Px() * std::cos(config.theta2) - motherVec.Py() * std::sin(config.theta2), motherVec.Px() * std::sin(config.theta2) + motherVec.Py() * std::cos(config.theta2), motherVec.Pz(), motherVec.M()); + auto prrot = getCosThetaPhi(motherRot, daughterVec1, kProduction); if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaProduction, anglePhi); + hglue.fill(HIST("h3glueInvMassRot"), eventMultiplicity, motherRot.Pt(), motherRot.M(), prrot.first, prrot.second); } } } else { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), cosThetaProduction, anglePhi); + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassME"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } } - } else if (config.activateBeamAxisFrame) { - beamVec = ROOT::Math::XYZVector(0.f, 0.f, 1.f); - auto cosThetaStarBeam = beamVec.Dot(fourVecDauCM.Vect()) / std::sqrt(fourVecDauCM.Vect().Mag2()); - if (!isMix) { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), cosThetaStarBeam, anglePhi); + } else if (config.activateGJFrame) { + auto pr = getCosThetaPhi(motherVec, daughterVec1, kGottfriedJackson); + if (!isMixed) { + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassDS"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } for (int i = 0; i < config.cRotations; i++) { - theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - motherRot = ROOT::Math::PxPyPzMVector(mother.Px() * std::cos(theta2) - mother.Py() * std::sin(theta2), mother.Px() * std::sin(theta2) + mother.Py() * std::cos(theta2), mother.Pz(), mother.M()); + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); + motherRot = ROOT::Math::PxPyPzMVector(motherVec.Px() * std::cos(config.theta2) - motherVec.Py() * std::sin(config.theta2), motherVec.Px() * std::sin(config.theta2) + motherVec.Py() * std::cos(config.theta2), motherVec.Pz(), motherVec.M()); + auto prrot = getCosThetaPhi(motherRot, daughterVec1, kGottfriedJackson); if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarBeam, anglePhi); + hglue.fill(HIST("h3glueInvMassRot"), eventMultiplicity, motherRot.Pt(), motherRot.M(), prrot.first, prrot.second); } } } else { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), cosThetaStarBeam, anglePhi); + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassME"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } } } else if (config.activateRandomFrame) { - auto phiRandom = gRandom->Uniform(0.f, constants::math::TwoPI); - auto thetaRandom = gRandom->Uniform(0.f, constants::math::PI); - - randomVec = ROOT::Math::XYZVector(std::sin(thetaRandom) * std::cos(phiRandom), std::sin(thetaRandom) * std::sin(phiRandom), std::cos(thetaRandom)); - auto cosThetaStarRandom = randomVec.Dot(fourVecDauCM.Vect()) / std::sqrt(fourVecDauCM.Vect().Mag2()); - if (!isMix) { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), cosThetaStarRandom, phiRandom); + auto pr = getCosThetaPhi(motherVec, daughterVec1, kRandom); + if (!isMixed) { + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassDS"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } for (int i = 0; i < config.cRotations; i++) { - theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - motherRot = ROOT::Math::PxPyPzMVector(mother.Px() * std::cos(theta2) - mother.Py() * std::sin(theta2), mother.Px() * std::sin(theta2) + mother.Py() * std::cos(theta2), mother.Pz(), mother.M()); + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); + motherRot = ROOT::Math::PxPyPzMVector(motherVec.Px() * std::cos(config.theta2) - motherVec.Py() * std::sin(config.theta2), motherVec.Px() * std::sin(config.theta2) + motherVec.Py() * std::cos(config.theta2), motherVec.Pz(), motherVec.M()); + auto prrot = getCosThetaPhi(motherRot, daughterVec1, kRandom); if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarRandom, phiRandom); + hglue.fill(HIST("h3glueInvMassRot"), eventMultiplicity, motherRot.Pt(), motherRot.M(), prrot.first, prrot.second); } } } else { - if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), cosThetaStarRandom, phiRandom); + if (std::abs(motherVec.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassME"), eventMultiplicity, motherVec.Pt(), motherVec.M(), pr.first, pr.second); } } } - // } } void processSE(EventCandidates::iterator const& collision, TrackCandidates const& /*tracks*/, V0TrackCandidate const& V0s) @@ -1000,8 +1027,6 @@ struct HigherMassResonances { multiplicity = collision.centFT0C(); } else if (config.cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } if (!selectionEvent(collision, true)) { @@ -1026,7 +1051,7 @@ struct HigherMassResonances { } std::vector v0indexes; - bool allConditionsMet = 0; + bool allConditionsMet = false; for (const auto& [v1, v2] : combinations(CombinationsFullIndexPolicy(V0s, V0s))) { @@ -1088,7 +1113,7 @@ struct HigherMassResonances { continue; } - allConditionsMet = 1; + allConditionsMet = true; daughter1 = ROOT::Math::PxPyPzMVector(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short); // Kshort daughter2 = ROOT::Math::PxPyPzMVector(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short); // Kshort @@ -1106,15 +1131,6 @@ struct HigherMassResonances { } } - // if (config.qAv0) { - // rKzeroShort.fill(HIST("hK0ShortMassCorrAfterCut"), v1.mK0Short(), v2.mK0Short()); - // } - - const double ptCorr = (mother.Pt() - daughter1.Pt() != 0.) ? daughter1.Pt() / (mother.Pt() - daughter1.Pt()) : 0.; - if (config.qAv0) { - rKzeroShort.fill(HIST("hK0sPtCorrelation"), ptCorr); - } - double deltaRvalue = std::sqrt(TVector2::Phi_mpi_pi(v1.phi() - v2.phi()) * TVector2::Phi_mpi_pi(v1.phi() - v2.phi()) + (v1.eta() - v2.eta()) * (v1.eta() - v2.eta())); if (!config.qAOptimisation) { @@ -1130,21 +1146,18 @@ struct HigherMassResonances { if (!config.isselectTWOKsOnly && config.qAOptimisation) { if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), deltaMass, deltaRvalue, ptCorr); + hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), deltaMass, deltaRvalue, v1.mK0Short(), v2.mK0Short()); } for (int i = 0; i < config.cRotations; i++) { - double theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(config.theta2) - daughter1.Py() * std::sin(config.theta2), daughter1.Px() * std::sin(config.theta2) + daughter1.Py() * std::cos(config.theta2), daughter1.Pz(), daughter1.M()); motherRot = daughterRot + daughter2; - // double pTcorrRot = std::abs(daughterRot.Pt() + daughter2.Pt()) / motherRot.Pt(); - double pTcorrRot = (motherRot.Pt() - daughterRot.Pt() != 0.) ? daughterRot.Pt() / (motherRot.Pt() - daughterRot.Pt()) : 0.; - - if (motherRot.Rapidity() < config.rapidityMotherData) - hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), deltaMass, deltaRvalue, pTcorrRot); + if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) + hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), deltaMass, deltaRvalue, daughterRot.M(), v2.mK0Short()); } } } @@ -1157,6 +1170,161 @@ struct HigherMassResonances { } PROCESS_SWITCH(HigherMassResonances, processSE, "same event process", true); + void processSEEP(EventCandidateEPs::iterator const& collision, TrackCandidates const& /*tracks*/, V0TrackCandidate const& V0s) + { + multiplicity = 0.0; + + if (config.cSelectMultEstimator == kFT0M) { + multiplicity = collision.centFT0M(); + } else if (config.cSelectMultEstimator == kFT0A) { + multiplicity = collision.centFT0A(); + } else if (config.cSelectMultEstimator == kFT0C) { + multiplicity = collision.centFT0C(); + } else if (config.cSelectMultEstimator == kFV0A) { + multiplicity = collision.centFV0A(); + } + + if (!selectionEvent(collision, true)) { + return; + } + + if (collision.qvecAmp()[config.detId] < config.minQvecAmp || collision.qvecAmp()[config.refAId] < config.minQvecAmp || collision.qvecAmp()[config.refBId] < config.minQvecAmp) { + return; + } + + if (config.qAevents) { + rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); + rEventSelection.fill(HIST("hmultiplicity"), multiplicity); + } + + std::array eps{}; + + eps[0] = helperEP.GetEventPlane(collision.qvecRe()[4 * config.detId + 3], collision.qvecIm()[4 * config.detId + 3], 2); + eps[1] = helperEP.GetEventPlane(collision.qvecRe()[4 * config.refAId + 3], collision.qvecIm()[4 * config.refAId + 3], 2); + eps[2] = helperEP.GetEventPlane(collision.qvecRe()[4 * config.refBId + 3], collision.qvecIm()[4 * config.refBId + 3], 2); + + float resNumA = helperEP.GetResolution(eps[0], eps[1], 2); + float resNumB = helperEP.GetResolution(eps[0], eps[2], 2); + float resDenom = helperEP.GetResolution(eps[1], eps[2], 2); + + hglue.fill(HIST("EpDet"), multiplicity, eps[0]); + hglue.fill(HIST("EpRefA"), multiplicity, eps[1]); + hglue.fill(HIST("EpRefB"), multiplicity, eps[2]); + + hglue.fill(HIST("EpResDetRefA"), multiplicity, resNumA); + hglue.fill(HIST("EpResDetRefB"), multiplicity, resNumB); + hglue.fill(HIST("EpResRefARefB"), multiplicity, resDenom); + + std::vector v0indexes; + + for (const auto& [v1, v2] : combinations(CombinationsFullIndexPolicy(V0s, V0s))) { + + if (v1.size() == 0 || v2.size() == 0) { + continue; + } + + if (!selectionV0(collision, v1, multiplicity)) { + continue; + } + if (!selectionV0(collision, v2, multiplicity)) { + continue; + } + + auto postrack1 = v1.template posTrack_as(); + auto negtrack1 = v1.template negTrack_as(); + auto postrack2 = v2.template posTrack_as(); + auto negtrack2 = v2.template negTrack_as(); + + double nTPCSigmaPos1{postrack1.tpcNSigmaPi()}; + double nTPCSigmaNeg1{negtrack1.tpcNSigmaPi()}; + double nTPCSigmaPos2{postrack2.tpcNSigmaPi()}; + double nTPCSigmaNeg2{negtrack2.tpcNSigmaPi()}; + + if (!(isSelectedV0Daughter(negtrack1, -1, nTPCSigmaNeg1, v1) && isSelectedV0Daughter(postrack1, 1, nTPCSigmaPos1, v1))) { + continue; + } + if (!(isSelectedV0Daughter(postrack2, 1, nTPCSigmaPos2, v2) && isSelectedV0Daughter(negtrack2, -1, nTPCSigmaNeg2, v2))) { + continue; + } + + if (std::find(v0indexes.begin(), v0indexes.end(), v1.globalIndex()) == v0indexes.end()) { + v0indexes.push_back(v1.globalIndex()); + } + + if (v2.globalIndex() <= v1.globalIndex()) { + continue; + } + + if (postrack1.globalIndex() == postrack2.globalIndex()) { + continue; + } + if (negtrack1.globalIndex() == negtrack2.globalIndex()) { + continue; + } + + double deltaRDaugherPos = std::sqrt(TVector2::Phi_mpi_pi(postrack1.phi() - negtrack1.phi()) * TVector2::Phi_mpi_pi(postrack1.phi() - negtrack1.phi()) + (postrack1.eta() - negtrack1.eta()) * (postrack1.eta() - negtrack1.eta())); + double deltaRDaugherNeg = std::sqrt(TVector2::Phi_mpi_pi(postrack2.phi() - negtrack2.phi()) * TVector2::Phi_mpi_pi(postrack2.phi() - negtrack2.phi()) + (postrack2.eta() - negtrack2.eta()) * (postrack2.eta() - negtrack2.eta())); + + if (config.qAv0) { + rKzeroShort.fill(HIST("hDauDeltaR"), deltaRDaugherPos, deltaRDaugherNeg); + } + + if (deltaRDaugherPos < config.deltaRDaugherCut || deltaRDaugherNeg < config.deltaRDaugherCut) { + continue; + } + + if (config.isApplyEtaCutK0s && (v1.eta() < config.confDaughEta || v2.eta() < config.confDaughEta)) { + continue; + } + + daughter1 = ROOT::Math::PxPyPzMVector(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short); // Kshort + daughter2 = ROOT::Math::PxPyPzMVector(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short); // Kshort + + mother = daughter1 + daughter2; // invariant mass of Kshort pair + isMix = false; + + const double deltaMass = deltaM(v1.mK0Short(), v2.mK0Short()); + if (config.qAv0) { + rKzeroShort.fill(HIST("hK0ShortMassCorr"), v1.mK0Short(), v2.mK0Short(), deltaMass); + } + + if (!config.qAOptimisation) { + if (deltaMass > config.cMaxDeltaM) { + continue; + } + } + + double deltaRvalue = std::sqrt(TVector2::Phi_mpi_pi(v1.phi() - v2.phi()) * TVector2::Phi_mpi_pi(v1.phi() - v2.phi()) + (v1.eta() - v2.eta()) * (v1.eta() - v2.eta())); + + if (!config.qAOptimisation) { + if (deltaRvalue < config.deltaRK0sCut) { + continue; + } + } + + if (!config.isselectTWOKsOnly && config.qAOptimisation) { + + if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassEPDS"), multiplicity, mother.Pt(), mother.M(), RecoDecay::constrainAngle(2.0 * mother.Phi() - 2.0 * eps[0])); + } + + for (int i = 0; i < config.cRotations; i++) { + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); + + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(config.theta2) - daughter1.Py() * std::sin(config.theta2), daughter1.Px() * std::sin(config.theta2) + daughter1.Py() * std::cos(config.theta2), daughter1.Pz(), daughter1.M()); + + motherRot = daughterRot + daughter2; + + if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) { + hglue.fill(HIST("h3glueInvMassEPRot"), multiplicity, motherRot.Pt(), motherRot.M(), RecoDecay::constrainAngle(2.0 * motherRot.Phi() - 2.0 * eps[0])); + } + } + } + } + v0indexes.clear(); + } + PROCESS_SWITCH(HigherMassResonances, processSEEP, "same event process with EP table", true); + ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for ME mixing"}; // ConfigurableAxis axisMultiplicityClass{"axisMultiplicityClass", {10, 0, 100}, "multiplicity percentile for ME mixing"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {2000, 0, 10000}, "TPC multiplicity axis for ME mixing"}; @@ -1167,13 +1335,13 @@ struct HigherMassResonances { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}}; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVertex, axisMultiplicity}, true}; // for derived data only + BinningType colBinning{{axisVertex, axisMultiplicity}}; // for derived data only Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only void processME(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, V0TrackCandidate const& v0s) @@ -1184,7 +1352,7 @@ struct HigherMassResonances { SameKindPair pair3{binningOnFT0C, config.cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; SameKindPair pair4{binningOnFV0A, config.cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; - auto runMixing = [&](auto& pair, auto multiplicityGetter) { + auto runMixing = [&](const auto& pair, auto multiplicityGetter) { for (const auto& [c1, tracks1, c2, tracks2] : pair) { multiplicity = multiplicityGetter(c1); @@ -1261,11 +1429,10 @@ struct HigherMassResonances { if (config.qAOptimisation) { double deltaRvalue = std::sqrt(TVector2::Phi_mpi_pi(daughter1.phi() - daughter2.phi()) * TVector2::Phi_mpi_pi(daughter1.phi() - daughter2.phi()) + (daughter1.eta() - daughter2.eta()) * (daughter1.eta() - daughter2.eta())); - const double deltaMass = deltaM(t1.mK0Short(), t2.mK0Short()); - // const double ptCorr = std::abs(daughter1.Pt() + daughter2.Pt()) / mother.Pt(); - const double ptCorr = (mother.Pt() - daughter1.Pt() != 0.) ? daughter1.Pt() / (mother.Pt() - daughter1.Pt()) : 0.; + const double deltaMassOpt = deltaM(t1.mK0Short(), t2.mK0Short()); + if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), deltaMass, deltaRvalue, ptCorr); + hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), deltaMassOpt, deltaRvalue, t1.mK0Short(), t2.mK0Short()); } } } @@ -1289,7 +1456,6 @@ struct HigherMassResonances { std::vector passKs; ROOT::Math::PxPyPzMVector lResonanceGen1; ROOT::Math::PxPyPzEVector lResonanceGen; - Service pdgDB; // void processGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions) void processGen(EventMCGenerated::iterator const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions) @@ -1324,8 +1490,6 @@ struct HigherMassResonances { multiplicityGen = collision.centFT0C(); } else if (config.cSelectMultEstimator == kFV0A) { multiplicityGen = collision.centFV0A(); - } else { - multiplicityGen = collision.centFT0M(); // default } if (!selectionEvent(collision, true)) { @@ -1383,35 +1547,51 @@ struct HigherMassResonances { } } if (static_cast(passKs.size()) == config.noOfDaughters) { - lResonanceGen = ROOT::Math::PxPyPzEVector(mcParticle.pt(), mcParticle.eta(), mcParticle.phi(), mcParticle.e()); + lResonanceGen = ROOT::Math::PxPyPzEVector(mcParticle.px(), mcParticle.py(), mcParticle.pz(), mcParticle.e()); lResonanceGen1 = daughter1 + daughter2; + mother2 = ROOT::Math::PxPyPzMVector(lResonanceGen1.Px(), lResonanceGen1.Py(), lResonanceGen1.Pz(), lResonanceGen1.M()); + mother3 = ROOT::Math::PxPyPzMVector(lResonanceGen.Px(), lResonanceGen.Py(), lResonanceGen.Pz(), lResonanceGen.M()); + + int frame = -1; + + if (config.activateHelicityFrame) { + frame = kHelicity; + } else if (config.activateCollinsSoperFrame) { + frame = kCollinsSoper; + } else if (config.activateProductionFrame) { + frame = kProduction; + } else if (config.activateGJFrame) { + frame = kGottfriedJackson; + } else if (config.activateRandomFrame) { + frame = kRandom; + } else { + LOGP(fatal, "No reference frame selected"); + } - ROOT::Math::Boost boost{lResonanceGen.BoostToCM()}; - ROOT::Math::Boost boost1{lResonanceGen1.BoostToCM()}; - - fourVecDauCM = boost(daughter1); - fourVecDauCM1 = boost1(daughter1); - - auto helicityGen = lResonanceGen.Vect().Dot(fourVecDauCM.Vect()) / (std::sqrt(fourVecDauCM.Vect().Mag2()) * std::sqrt(lResonanceGen.Vect().Mag2())); - auto helicityGen1 = lResonanceGen1.Vect().Dot(fourVecDauCM1.Vect()) / (std::sqrt(fourVecDauCM1.Vect().Mag2()) * std::sqrt(lResonanceGen1.Vect().Mag2())); + auto angularVar = getCosThetaPhi(mother3, daughter1, frame); + auto angularVar1 = getCosThetaPhi(mother2, daughter1, frame); - hMChists.fill(HIST("Genf1710"), multiplicityGen, lResonanceGen.pt(), helicityGen); - hMChists.fill(HIST("Genf1710Calib"), genMultiplicity, lResonanceGen.pt(), helicityGen); + hMChists.fill(HIST("Genf1710"), multiplicityGen, lResonanceGen.pt(), angularVar.first); + hMChists.fill(HIST("Genf1710Calib"), genMultiplicity, lResonanceGen.pt(), angularVar.first); hMChists.fill(HIST("Genf1710_mass"), lResonanceGen.M()); hMChists.fill(HIST("GenRapidity"), mcParticle.y()); hMChists.fill(HIST("GenEta"), mcParticle.eta()); hMChists.fill(HIST("GenPhi"), mcParticle.phi()); + hMChists.fill(HIST("Gen1710PWA"), lResonanceGen.M(), angularVar.first, angularVar.second); + hMChists.fill(HIST("GenThetavsPhi1"), angularVar.first, angularVar.second); if (config.isapplyPairRapidityMC && std::abs(lResonanceGen1.Rapidity()) >= config.rapidityMotherData) { continue; } - hMChists.fill(HIST("Genf17102"), multiplicityGen, lResonanceGen1.pt(), helicityGen1); - hMChists.fill(HIST("Genf1710Calib2"), genMultiplicity, lResonanceGen1.pt(), helicityGen1); + hMChists.fill(HIST("Genf17102"), multiplicityGen, lResonanceGen1.pt(), angularVar1.first); + hMChists.fill(HIST("Genf1710Calib2"), genMultiplicity, lResonanceGen1.pt(), angularVar1.first); hMChists.fill(HIST("Genf1710_mass2"), lResonanceGen1.M()); hMChists.fill(HIST("GenRapidity2"), lResonanceGen1.Rapidity()); hMChists.fill(HIST("GenEta2"), lResonanceGen1.Eta()); hMChists.fill(HIST("GenPhi2"), lResonanceGen1.Phi()); + hMChists.fill(HIST("Gen1710PWA2"), lResonanceGen1.M(), angularVar1.first, angularVar1.second); + hMChists.fill(HIST("GenThetavsPhi2"), angularVar1.first, angularVar1.second); } passKs.clear(); // clear the vector for the next iteration } @@ -1442,8 +1622,6 @@ struct HigherMassResonances { multiplicity1 = RecCollision.centFT0C(); } else if (config.cSelectMultEstimator == kFV0A) { multiplicity1 = RecCollision.centFV0A(); - } else { - multiplicity1 = RecCollision.centFT0M(); // default } isSelectedEvent = true; @@ -1462,15 +1640,15 @@ struct HigherMassResonances { return; } - auto multiplicityGen = -1; - multiplicityGen = mcCollision.centFT0M(); - hMChists.fill(HIST("MCcorrections/MultiplicityGen"), multiplicityGen); + auto multiplicityGenEvt = -1.f; + multiplicityGenEvt = mcCollision.centFT0M(); + hMChists.fill(HIST("MCcorrections/MultiplicityGen"), multiplicityGenEvt); hMChists.fill(HIST("MCcorrections/MultiplicityGen2"), multiplicity1); hMChists.fill(HIST("MCcorrections/hMultvsCent"), multiplicity1, multMC); // Event loss if (isSelectedEvent) { - hMChists.fill(HIST("MCcorrections/MultiplicityRec"), multiplicityGen); + hMChists.fill(HIST("MCcorrections/MultiplicityRec"), multiplicityGenEvt); hMChists.fill(HIST("MCcorrections/MultiplicityRec2"), multiplicity1); } @@ -1479,11 +1657,11 @@ struct HigherMassResonances { if (std::abs(mcPart.y()) >= config.rapidityMotherData || std::abs(mcPart.pdgCode()) != config.pdgCodes[config.selectMCparticles]) continue; - hMChists.fill(HIST("MCcorrections/hSignalLossDenominator"), mcPart.pt(), multiplicityGen); + hMChists.fill(HIST("MCcorrections/hSignalLossDenominator"), mcPart.pt(), multiplicityGenEvt); hMChists.fill(HIST("MCcorrections/hSignalLossDenominator2"), mcPart.pt(), multiplicity1); hMChists.fill(HIST("MCcorrections/hSignalLossDenominator3"), mcPart.pt(), multMC); if (isSelectedEvent) { - hMChists.fill(HIST("MCcorrections/hSignalLossNumerator"), mcPart.pt(), multiplicityGen); + hMChists.fill(HIST("MCcorrections/hSignalLossNumerator"), mcPart.pt(), multiplicityGenEvt); hMChists.fill(HIST("MCcorrections/hSignalLossNumerator2"), mcPart.pt(), multiplicity1); hMChists.fill(HIST("MCcorrections/hSignalLossNumerator3"), mcPart.pt(), multMC); } @@ -1533,26 +1711,24 @@ struct HigherMassResonances { const auto& mcCollisionRec = collision.mcCollision_as(); multiplicityRec = mcCollisionRec.centFT0M(); - auto multiplicity = -999.0; + auto eventMultiplicity = -999.0; if (config.cSelectMultEstimator == kFT0M) { - multiplicity = collision.centFT0M(); + eventMultiplicity = collision.centFT0M(); } else if (config.cSelectMultEstimator == kFT0A) { - multiplicity = collision.centFT0A(); + eventMultiplicity = collision.centFT0A(); } else if (config.cSelectMultEstimator == kFT0C) { - multiplicity = collision.centFT0C(); + eventMultiplicity = collision.centFT0C(); } else if (config.cSelectMultEstimator == kFV0A) { - multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default + eventMultiplicity = collision.centFV0A(); } if (!selectionEvent(collision, false)) { return; } - hMChists.fill(HIST("Rec_Multiplicity"), multiplicity); + hMChists.fill(HIST("Rec_Multiplicity"), eventMultiplicity); rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); - hMChists.fill(HIST("MC_mult_after_event_sel"), multiplicity); + hMChists.fill(HIST("MC_mult_after_event_sel"), eventMultiplicity); eventCounter++; for (const auto& v01 : V0s) { @@ -1580,21 +1756,23 @@ struct HigherMassResonances { if (!negtrack1.has_mcParticle() || !negtrack2.has_mcParticle()) continue; - double nTPCSigmaPos1[1]{postrack1.tpcNSigmaPi()}; - double nTPCSigmaNeg1[1]{negtrack1.tpcNSigmaPi()}; - double nTPCSigmaPos2[1]{postrack2.tpcNSigmaPi()}; - double nTPCSigmaNeg2[1]{negtrack2.tpcNSigmaPi()}; + double nTPCSigmaPos1 = postrack1.tpcNSigmaPi(); + double nTPCSigmaNeg1 = negtrack1.tpcNSigmaPi(); + double nTPCSigmaPos2 = postrack2.tpcNSigmaPi(); + double nTPCSigmaNeg2 = negtrack2.tpcNSigmaPi(); - if (!isSelectedV0Daughter(postrack1, 1, nTPCSigmaPos1[0], v01) || !isSelectedV0Daughter(postrack2, 1, nTPCSigmaPos2[0], v02)) { + if (!isSelectedV0Daughter(postrack1, 1, nTPCSigmaPos1, v01) || + !isSelectedV0Daughter(postrack2, 1, nTPCSigmaPos2, v02)) { continue; } - if (!isSelectedV0Daughter(negtrack1, -1, nTPCSigmaNeg1[0], v01) || !isSelectedV0Daughter(negtrack2, -1, nTPCSigmaNeg2[0], v02)) { + if (!isSelectedV0Daughter(negtrack1, -1, nTPCSigmaNeg1, v01) || + !isSelectedV0Daughter(negtrack2, -1, nTPCSigmaNeg2, v02)) { continue; } hMChists.fill(HIST("events_checkrec"), 1.5); - if (!selectionV0(collision, v01, multiplicity) || !selectionV0(collision, v02, multiplicity)) { + if (!selectionV0(collision, v01, eventMultiplicity) || !selectionV0(collision, v02, eventMultiplicity)) { continue; } hMChists.fill(HIST("events_checkrec"), 2.5); @@ -1667,37 +1845,42 @@ struct HigherMassResonances { daughter2 = ROOT::Math::PxPyPzMVector(v02.px(), v02.py(), v02.pz(), o2::constants::physics::MassK0Short); mother = daughter1 + daughter2; mother1 = ROOT::Math::PxPyPzEVector(mothertrack1.px(), mothertrack1.py(), mothertrack1.pz(), mothertrack1.e()); + mother2 = ROOT::Math::PxPyPzMVector(mother1.Px(), mother1.Py(), mother1.Pz(), mother1.M()); + + int frame = kHelicity; + + if (config.activateCollinsSoperFrame) { + frame = kCollinsSoper; + } else if (config.activateProductionFrame) { + frame = kProduction; + } else if (config.activateGJFrame) { + frame = kGottfriedJackson; + } else if (config.activateRandomFrame) { + frame = kRandom; + } - ROOT::Math::Boost boost{mother.BoostToCM()}; - ROOT::Math::Boost boost1{mother1.BoostToCM()}; - - fourVecDauCM = boost(daughter1); - fourVecDauCM1 = boost1(daughter1); - - auto helicityRec = mother.Vect().Dot(fourVecDauCM.Vect()) / (std::sqrt(fourVecDauCM.Vect().Mag2()) * std::sqrt(mother.Vect().Mag2())); - - auto helicityRec2 = mother1.Vect().Dot(fourVecDauCM1.Vect()) / (std::sqrt(fourVecDauCM1.Vect().Mag2()) * std::sqrt(mother1.Vect().Mag2())); + auto angularVar = getCosThetaPhi(mother, daughter1, frame); + auto angularVar1 = getCosThetaPhi(mother2, daughter1, frame); - // const double deltaMassRec = deltaM(mctrackv01.mK0Short(), mctrackv02.mK0Short()); - // if (deltaMassRec > config.cMaxDeltaM) { - // continue; - // } - - hMChists.fill(HIST("Recf1710_pt1"), multiplicity, mothertrack1.pt(), mother1.M(), helicityRec2); - hMChists.fill(HIST("Recf1710Calib_pt1"), multiplicityRec, mothertrack1.pt(), mother1.M(), helicityRec2); + hMChists.fill(HIST("Recf1710_pt1"), eventMultiplicity, mothertrack1.pt(), mother1.M(), angularVar1.first); + hMChists.fill(HIST("Recf1710Calib_pt1"), multiplicityRec, mothertrack1.pt(), mother1.M(), angularVar1.first); hMChists.fill(HIST("RecRapidity"), mothertrack1.y()); hMChists.fill(HIST("RecPhi"), mothertrack1.phi()); hMChists.fill(HIST("RecEta"), mothertrack1.eta()); + hMChists.fill(HIST("Recf1710PWA_pt1"), mother1.M(), angularVar1.first, angularVar1.second); + hMChists.fill(HIST("RecThetavsPhi1"), angularVar1.first, angularVar1.second); if (config.isapplyPairRapidityMC && std::abs(mother.Rapidity()) >= config.rapidityMotherData) { continue; } - hMChists.fill(HIST("Recf1710_pt2"), multiplicity, mother.Pt(), mother.M(), helicityRec); - hMChists.fill(HIST("Recf1710Calib_pt2"), multiplicityRec, mother.Pt(), mother.M(), helicityRec); + hMChists.fill(HIST("Recf1710_pt2"), eventMultiplicity, mother.Pt(), mother.M(), angularVar.first); + hMChists.fill(HIST("Recf1710Calib_pt2"), multiplicityRec, mother.Pt(), mother.M(), angularVar.first); hMChists.fill(HIST("RecRapidity2"), mother.Rapidity()); hMChists.fill(HIST("RecPhi2"), mother.Phi()); hMChists.fill(HIST("RecEta2"), mother.Eta()); + hMChists.fill(HIST("Recf1710PWA_pt2"), mother.M(), angularVar.first, angularVar.second); + hMChists.fill(HIST("RecThetavsPhi2"), angularVar.first, angularVar.second); } gindex2.clear(); } @@ -1718,8 +1901,6 @@ struct HigherMassResonances { multiplicity = collision.centFT0C(); } else if (config.cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } if (!selectionEventDerived(collision, true)) { @@ -1732,7 +1913,7 @@ struct HigherMassResonances { } std::vector v0indexes; - bool allConditionsMet = 0; + bool allConditionsMet = false; for (const auto& [v1, v2] : combinations(CombinationsFullIndexPolicy(V0s, V0s))) { @@ -1759,7 +1940,7 @@ struct HigherMassResonances { continue; } - allConditionsMet = 1; + allConditionsMet = true; daughter1 = ROOT::Math::PxPyPzMVector(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short); // Kshort daughter2 = ROOT::Math::PxPyPzMVector(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short); // Kshort @@ -1775,15 +1956,6 @@ struct HigherMassResonances { continue; } - // if (config.qAv0) { - // rKzeroShort.fill(HIST("hK0ShortMassCorrAfterCut"), v1.mK0Short(), v2.mK0Short()); - // } - - const double ptCorr = (mother.Pt() - daughter1.Pt() != 0.) ? daughter1.Pt() / (mother.Pt() - daughter1.Pt()) : 0.; - if (config.qAv0) { - rKzeroShort.fill(HIST("hK0sPtCorrelation"), ptCorr); - } - double deltaRvalue = std::sqrt(TVector2::Phi_mpi_pi(v1.phi() - v2.phi()) * TVector2::Phi_mpi_pi(v1.phi() - v2.phi()) + (v1.eta() - v2.eta()) * (v1.eta() - v2.eta())); if (!config.qAOptimisation) { @@ -1798,19 +1970,18 @@ struct HigherMassResonances { if (!config.isselectTWOKsOnly && config.qAOptimisation) { if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), deltaMass, deltaRvalue, ptCorr); + hglue.fill(HIST("h3glueInvMassDS"), multiplicity, mother.Pt(), mother.M(), deltaMass, deltaRvalue, v1.mK0Short(), v2.mK0Short()); } for (int i = 0; i < config.cRotations; i++) { - double theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); + config.theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / config.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / config.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(config.theta2) - daughter1.Py() * std::sin(config.theta2), daughter1.Px() * std::sin(config.theta2) + daughter1.Py() * std::cos(config.theta2), daughter1.Pz(), daughter1.M()); motherRot = daughterRot + daughter2; - // double pTcorrRot = std::abs(daughterRot.Pt() + daughter2.Pt()) / motherRot.Pt(); - double pTcorrRot = (motherRot.Pt() - daughterRot.Pt() != 0.) ? daughterRot.Pt() / (motherRot.Pt() - daughterRot.Pt()) : 0.; - if (motherRot.Rapidity() < config.rapidityMotherData) - hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), deltaMass, deltaRvalue, pTcorrRot); + + if (std::abs(motherRot.Rapidity()) < config.rapidityMotherData) + hglue.fill(HIST("h3glueInvMassRot"), multiplicity, motherRot.Pt(), motherRot.M(), deltaMass, deltaRvalue, daughterRot.M(), v2.mK0Short()); } } } @@ -1875,11 +2046,10 @@ struct HigherMassResonances { if (config.qAOptimisation) { double deltaRvalue = std::sqrt(TVector2::Phi_mpi_pi(daughter1.phi() - daughter2.phi()) * TVector2::Phi_mpi_pi(daughter1.phi() - daughter2.phi()) + (daughter1.eta() - daughter2.eta()) * (daughter1.eta() - daughter2.eta())); - const double deltaMass = deltaM(t1.mK0Short(), t2.mK0Short()); - // const double ptCorr = std::abs(daughter1.Pt() + daughter2.Pt()) / mother.Pt(); - const double ptCorr = (mother.Pt() - daughter1.Pt() != 0.) ? daughter1.Pt() / (mother.Pt() - daughter1.Pt()) : 0.; + const double deltaMassOpt = deltaM(t1.mK0Short(), t2.mK0Short()); + if (std::abs(mother.Rapidity()) < config.rapidityMotherData) { - hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), deltaMass, deltaRvalue, ptCorr); + hglue.fill(HIST("h3glueInvMassME"), multiplicity, mother.Pt(), mother.M(), deltaMassOpt, deltaRvalue, t1.mK0Short(), t2.mK0Short()); } } } diff --git a/PWGLF/Tasks/Resonances/highmasslambda.cxx b/PWGLF/Tasks/Resonances/highmasslambda.cxx index c0827bfe7d2..dae9c6c0d82 100644 --- a/PWGLF/Tasks/Resonances/highmasslambda.cxx +++ b/PWGLF/Tasks/Resonances/highmasslambda.cxx @@ -591,7 +591,7 @@ struct highmasslambda { histos.fill(HIST("hVtxZ"), collision.posZ()); histos.fill(HIST("hOccupancy"), occupancy); auto firstprimarytrack = 0; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -626,7 +626,7 @@ struct highmasslambda { histos.fill(HIST("hNsigmaProtonTOF"), track1.tofNSigmaPr(), track1.pt()); } auto track1ID = track1.globalIndex(); - for (auto v0 : V0s) { + for (const auto& v0 : V0s) { if (firstprimarytrack == 0) { histos.fill(HIST("hInvMassKs0before"), v0.mK0Short()); } @@ -714,7 +714,7 @@ struct highmasslambda { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks, AllTrackCandidates const&, ResoV0s const& V0s) { auto tracksV0sTuple = std::make_tuple(tracks, V0s); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; Pair pairs{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (!collision1.sel8() || !collision1.triggereventep() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder)) { @@ -872,7 +872,7 @@ struct highmasslambda { histos.fill(HIST("hVtxZ"), collision.posZ()); histos.fill(HIST("hOccupancy"), occupancy); auto firstprimarytrack = 0; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -906,7 +906,7 @@ struct highmasslambda { auto trackParCovBach = getTrackParCov(track1); // auto trackParCovBach = getTrackParCov(bach); - for (auto v0 : V0s) { + for (const auto& v0 : V0s) { if (!SelectionV0(collision, v0)) { continue; } diff --git a/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx b/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx index cc971733c05..33d5b697660 100644 --- a/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx +++ b/PWGLF/Tasks/Resonances/highmasslambdasvx.cxx @@ -573,7 +573,7 @@ struct highmasslambdasvx { histos.fill(HIST("hVtxZ"), collision.posZ()); histos.fill(HIST("hOccupancy"), occupancy); auto firstprimarytrack = 0; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -611,7 +611,7 @@ struct highmasslambdasvx { histos.fill(HIST("hNsigmaProtonTOF"), track1.tofNSigmaPr(), track1.pt()); auto track1ID = track1.globalIndex(); auto trackParCovBach = getTrackParCov(track1); - for (auto v0 : V0s) { + for (const auto& v0 : V0s) { if (!SelectionV0(collision, v0)) { continue; } @@ -808,7 +808,7 @@ struct highmasslambdasvx { /* void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks, AllTrackCandidates const&, ResoV0s const& V0s) { auto tracksV0sTuple = std::make_tuple(tracks, V0s); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; Pair pairs{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { if (!collision1.sel8() || !collision2.sel8()) { diff --git a/PWGLF/Tasks/Resonances/initializereventqa.cxx b/PWGLF/Tasks/Resonances/initializereventqa.cxx index 81b12b19534..ad8e4e89ea5 100644 --- a/PWGLF/Tasks/Resonances/initializereventqa.cxx +++ b/PWGLF/Tasks/Resonances/initializereventqa.cxx @@ -169,7 +169,7 @@ struct Initializereventqa { Partition globalTracksIUEta05 = (nabs(aod::track::eta) < globalEta05) && (requireGlobalTrackInFilter()); template - uint16_t getGenNchInFT0Mregion(TMcParticles particles) + uint16_t getGenNchInFT0Mregion(const TMcParticles& particles) { float region1FT0 = -3.3f; float region2FT0 = -2.1f; diff --git a/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx b/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx new file mode 100644 index 00000000000..583ef56a6d4 --- /dev/null +++ b/PWGLF/Tasks/Resonances/k0sK0sFlow.cxx @@ -0,0 +1,926 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file k0sK0sFlow.cxx +/// \brief K0S and K0S-K0S spectra, angular distributions, SP and EP observables +/// \author Satoshi Yano + +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector3Dfwd.h) +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +struct K0sK0sFlow { + enum class PionPidMode : int { + TpcWithTofIfAvailable = 0, + TpcOnly = 1, + TpcAndTof = 2, + TofRequiredAboveMomentum = 4 + }; + + enum class EventSelectionStep : int { + All = 1, + Vertex, + Sel8, + NoSameBunchPileup, + NoTimeFrameBorder, + NoItsRofBorder, + Centrality, + NSteps + }; + + enum class TrackSelectionStep : int { + All = 1, + Kinematics, + TpcClusters, + TrackQuality, + TpcPid, + TofPid, + NSteps + }; + + enum class K0sSelectionStep : int { + All = 1, + Daughters, + Kinematics, + Topology, + DaughterDca, + DecayGeometry, + Armenteros, + LambdaRejection, + NSteps + }; + + enum class PairSelectionStep : int { + All = 1, + NoSharedDaughters, + SelectedK0s, + ClosePair, + Kinematics, + SignalMass, + NSteps + }; + + enum class LikeSignCategory : int { + PositivePositive, + NegativeNegative, + PositiveNegative + }; + + enum class SidebandRegion : int { + Lower = -1, + Outside = 0, + Upper = 1 + }; + + static constexpr double VectorTolerance = 1.e-12; + static constexpr int QVectorComponents = 4; + + template + static constexpr auto enumValue(E value) + { + return static_cast>(value); + } + + HistogramRegistry registry{"registry"}; + SliceCache cache; + o2::vertexing::DCAFitterN<2> lsFitter; + + struct : ConfigurableGroup { + Configurable maxAbsZvtx{"maxAbsZvtx", 10.f, "maximum |z vertex| (cm)"}; + Configurable requireSel8{"requireSel8", true, "require sel8"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", true, "reject same-bunch pileup"}; + Configurable requireNoTimeFrameBorder{"requireNoTimeFrameBorder", true, "reject TF borders"}; + Configurable requireNoITSROFrameBorder{"requireNoITSROFrameBorder", true, "reject ITS ROF borders"}; + Configurable minCentrality{"minCentrality", 0.f, "minimum FT0C centrality"}; + Configurable maxCentrality{"maxCentrality", 100.f, "maximum FT0C centrality"}; + } eventCuts; + + struct : ConfigurableGroup { + Configurable daughterMinPt{"daughterMinPt", 0.1f, "daughter minimum pT"}; + Configurable maxAbsEta{"maxAbsEta", 0.8f, "daughter maximum |eta|"}; + Configurable minTPCRows{"minTPCRows", 70, "minimum TPC crossed rows"}; + Configurable minTPCRowsOverFindable{"minTPCRowsOverFindable", 0.8f, "minimum crossed/findable ratio"}; + Configurable maxTPCChi2NCl{"maxTPCChi2NCl", 4.f, "maximum TPC chi2/cluster"}; + Configurable maxITSChi2NCl{"maxITSChi2NCl", 36.f, "maximum ITS chi2/cluster"}; + Configurable minITSClusters{"minITSClusters", 0, "minimum ITS clusters; zero disables"}; + Configurable pidMode{"pidMode", static_cast(PionPidMode::TpcWithTofIfAvailable), "0 TPC and TOF if present; 1 TPC only; 2 require TOF; 4 require TOF above momentum threshold"}; + Configurable maxTPCNSigmaPi{"maxTPCNSigmaPi", 5.f, "maximum pion TPC |n sigma|"}; + Configurable maxTOFNSigmaPi{"maxTOFNSigmaPi", 3.f, "maximum pion TOF |n sigma|"}; + Configurable tofRequiredAboveP{"tofRequiredAboveP", 0.6f, "momentum threshold for PID mode 4"}; + } daughterCuts; + + struct : ConfigurableGroup { + Configurable k0sMinPt{"k0sMinPt", 0.f, "K0S minimum pT"}; + Configurable k0sMaxPt{"k0sMaxPt", 20.f, "K0S maximum pT"}; + Configurable k0sMaxAbsY{"k0sMaxAbsY", 0.5f, "K0S maximum |y|"}; + Configurable minCosPA{"minCosPA", 0.98f, "minimum cos(PA)"}; + Configurable maxDCADaughters{"maxDCADaughters", 0.3f, "maximum daughter DCA (cm)"}; + Configurable minDCAPosToPV{"minDCAPosToPV", 0.05f, "minimum positive daughter |DCA to PV| (cm)"}; + Configurable minDCANegToPV{"minDCANegToPV", 0.05f, "minimum negative daughter |DCA to PV| (cm)"}; + Configurable minRadius{"minRadius", 0.5f, "minimum decay radius (cm)"}; + Configurable maxRadius{"maxRadius", 200.f, "maximum decay radius (cm)"}; + Configurable maxCtau{"maxCtau", 20.f, "maximum c tau (cm)"}; + Configurable useArmenterosCut{"useArmenterosCut", true, "apply K0S Armenteros cut"}; + Configurable armenterosSlope{"armenterosSlope", 0.2f, "require qT > slope |alpha|"}; + Configurable rejectLambda{"rejectLambda", true, "reject Lambda and anti-Lambda"}; + Configurable lambdaRejectionWindow{"lambdaRejectionWindow", 0.01f, "Lambda rejection half-width"}; + Configurable k0sMassMin{"k0sMassMin", 0.47f, "K0S pair-building mass minimum"}; + Configurable k0sMassMax{"k0sMassMax", 0.525f, "K0S pair-building mass maximum"}; + Configurable k0sLowerSidebandMin{"k0sLowerSidebandMin", 0.42f, "K0S lower sideband minimum"}; + Configurable k0sLowerSidebandMax{"k0sLowerSidebandMax", 0.46f, "K0S lower sideband maximum"}; + Configurable k0sUpperSidebandMin{"k0sUpperSidebandMin", 0.535f, "K0S upper sideband minimum"}; + Configurable k0sUpperSidebandMax{"k0sUpperSidebandMax", 0.575f, "K0S upper sideband maximum"}; + } k0Cuts; + + struct : ConfigurableGroup { + Configurable pairMinPt{"pairMinPt", 0.f, "pair minimum pT"}; + Configurable pairMaxPt{"pairMaxPt", 20.f, "pair maximum pT"}; + Configurable pairMaxAbsY{"pairMaxAbsY", 0.5f, "pair maximum |y|"}; + Configurable pairMassMin{"pairMassMin", 0.99f, "pair mass minimum"}; + Configurable pairMassMax{"pairMassMax", 3.f, "pair mass maximum"}; + Configurable enableClosePairRejection{"enableClosePairRejection", false, "enable minimum DeltaR cut"}; + Configurable minDeltaR{"minDeltaR", 0.0f, "minimum K0S-K0S DeltaR"}; + Configurable mixingDepth{"mixingDepth", 5, "number of mixed events"}; + } pairCuts; + + struct : ConfigurableGroup { + Configurable enableLikeSign{"enableLikeSign", true, "build like-sign V0 and V0-V0 background candidates"}; + Configurable likeSignBz{"likeSignBz", 5.f, "magnetic field Bz used by the like-sign DCA fitter (kG)"}; + Configurable likeSignFitterMaxR{"likeSignFitterMaxR", 200.f, "maximum radius used by the like-sign DCA fitter (cm)"}; + Configurable likeSignFitterMaxChi2{"likeSignFitterMaxChi2", 1.e9f, "maximum fitter chi2"}; + } likeSign; + + struct : ConfigurableGroup { + Configurable harmonic{"harmonic", 2, "flow harmonic"}; + Configurable correctionStep{"correctionStep", 3, "Q-vector correction step (0-3)"}; + Configurable numberOfDetectors{"numberOfDetectors", 7, "detectors stored in Qvectors"}; + Configurable ft0cId{"ft0cId", 0, "FT0C detector index"}; + Configurable ft0aId{"ft0aId", 1, "FT0A detector index"}; + Configurable tpcPosId{"tpcPosId", 4, "TPC positive-eta detector index"}; + Configurable minQAmplitude{"minQAmplitude", 1.e-4f, "minimum Q-vector amplitude"}; + } flow; + + ConfigurableAxis axisCent{"axisCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "centrality"}; + ConfigurableAxis axisK0Pt{"axisK0Pt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "K0S pT"}; + ConfigurableAxis axisPairPt{"axisPairPt", {VARIABLE_WIDTH, 0., 0.5, 1., 1.5, 2., 2.5, 3., 4., 6., 8., 10., 13., 20.}, "pair pT"}; + ConfigurableAxis axisK0Mass{"axisK0Mass", {200, 0.4, 0.6}, "pi-pi mass"}; + ConfigurableAxis axisPairMass{"axisPairMass", {402, 0.99, 3.0}, "K0S-K0S mass"}; + ConfigurableAxis axisAbsCosTheta{"axisAbsCosTheta", {10, 0., 1.}, "|cos(theta*)|"}; + ConfigurableAxis axisPhiStar{"axisPhiStar", {12, -3.141592653589793, 3.141592653589793}, "phi*"}; + ConfigurableAxis axisMixZvtx{"axisMixZvtx", {VARIABLE_WIDTH, -10., -5., 0., 5., 10.}, "event-mixing vertex-z bins"}; + ConfigurableAxis axisMixCent{"axisMixCent", {VARIABLE_WIDTH, 0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100.}, "event-mixing centrality bins"}; + + using Collisions = soa::Join; + using DaughterTracks = soa::Join; + Preslice perCollisionV0 = aod::v0data::collisionId; + Preslice perCollisionTrack = aod::track::collisionId; + using MixingBinning = ColumnBinningPolicy; + + void init(InitContext const&) + { + lsFitter.setPropagateToPCA(true); + lsFitter.setMaxR(likeSign.likeSignFitterMaxR); + lsFitter.setMinParamChange(1.e-3f); + lsFitter.setMinRelChi2Change(0.9f); + lsFitter.setMaxDZIni(1.e9f); + lsFitter.setMaxChi2(likeSign.likeSignFitterMaxChi2); + lsFitter.setUseAbsDCA(true); + lsFitter.setBz(likeSign.likeSignBz); + AxisSpec cent{axisCent, "FT0C centrality (%)"}, k0pt{axisK0Pt, "p_{T}(K^{0}_{S})"}, pairpt{axisPairPt, "p_{T}(K^{0}_{S}K^{0}_{S})"}; + AxisSpec k0mass{axisK0Mass, "m_{#pi#pi}"}, pairmass{axisPairMass, "m_{K^{0}_{S}K^{0}_{S}}"}; + AxisSpec absct{axisAbsCosTheta, "|cos#theta*|"}, phistar{axisPhiStar, "#phi*"}; + constexpr int NEventSelectionSteps = enumValue(EventSelectionStep::NSteps) - 1; + constexpr int NTrackSelectionSteps = enumValue(TrackSelectionStep::NSteps) - 1; + constexpr int NK0sSelectionSteps = enumValue(K0sSelectionStep::NSteps) - 1; + constexpr int NPairSelectionSteps = enumValue(PairSelectionStep::NSteps) - 1; + registry.add("Event/hEventCutFlow", "event cut flow", HistType::kTH1F, {{NEventSelectionSteps, 0.5, NEventSelectionSteps + 0.5}}); + registry.add("Event/hVertexZBefore", "vertex z before", HistType::kTH1F, {{120, -30., 30.}}); + registry.add("Event/hVertexZAfter", "vertex z after", HistType::kTH1F, {{120, -30., 30.}}); + registry.add("Event/hCentralityBefore", "centrality before", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("Event/hCentralityAfter", "centrality after", HistType::kTH1F, {{100, 0., 100.}}); + registry.add("QA/Tracks/hCutFlow", "daughter cut flow", HistType::kTH1F, {{NTrackSelectionSteps, 0.5, NTrackSelectionSteps + 0.5}}); + registry.add("QA/Tracks/BeforeSelection/hPt", "daughter pT before", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/Tracks/BeforeSelection/hEta", "daughter eta before", HistType::kTH1F, {{100, -1., 1.}}); + registry.add("QA/Tracks/BeforeSelection/hDCAxyVsPt", "DCAxy vs pT before", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/BeforeSelection/hDCAzVsPt", "DCAz vs pT before", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCCrossedRowsVsPt", "TPC rows vs pT before", HistType::kTH2F, {{200, 0., 20.}, {170, 0., 170.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCSignalVsP", "TPC signal before", HistType::kTH2F, {{200, 0., 10.}, {300, 0., 300.}}); + registry.add("QA/Tracks/BeforeSelection/hTPCNSigmaPiVsP", "TPC pion PID before", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/BeforeSelection/hTOFBetaVsP", "TOF beta before", HistType::kTH2F, {{200, 0., 10.}, {140, 0., 1.4}}); + registry.add("QA/Tracks/BeforeSelection/hTOFNSigmaPiVsP", "TOF pion PID before", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/AfterSelection/hPt", "daughter pT", HistType::kTH1F, {{200, 0., 20.}}); + registry.add("QA/Tracks/AfterSelection/hEta", "daughter eta", HistType::kTH1F, {{100, -1., 1.}}); + registry.add("QA/Tracks/AfterSelection/hDCAxyVsPt", "DCAxy vs pT", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/AfterSelection/hDCAzVsPt", "DCAz vs pT", HistType::kTH2F, {{200, 0., 20.}, {200, -5., 5.}}); + registry.add("QA/Tracks/AfterSelection/hTPCCrossedRowsVsPt", "TPC rows vs pT", HistType::kTH2F, {{200, 0., 20.}, {170, 0., 170.}}); + registry.add("QA/Tracks/AfterSelection/hTPCSignalVsP", "TPC signal", HistType::kTH2F, {{200, 0., 10.}, {300, 0., 300.}}); + registry.add("QA/Tracks/AfterSelection/hTPCNSigmaPiVsP", "TPC pion PID", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/Tracks/AfterSelection/hTOFBetaVsP", "TOF beta", HistType::kTH2F, {{200, 0., 10.}, {140, 0., 1.4}}); + registry.add("QA/Tracks/AfterSelection/hTOFNSigmaPiVsP", "TOF pion PID", HistType::kTH2F, {{200, 0., 10.}, {120, -6., 6.}}); + registry.add("QA/K0s/hCutFlow", "K0S cut flow", HistType::kTH1F, {{NK0sSelectionSteps, 0.5, NK0sSelectionSteps + 0.5}}); + registry.add("QA/K0s/BeforeSelection/hMassVsPt", "K0S before", HistType::kTH2F, {k0mass, k0pt}); + registry.add("QA/K0s/BeforeSelection/hArmenteros", "Armenteros before", HistType::kTH2F, {{100, -1., 1.}, {100, 0., 0.3}}); + registry.add("QA/K0s/BeforeSelection/hCosPA", "cosPA before", HistType::kTH1F, {{200, 0.9, 1.}}); + registry.add("QA/K0s/BeforeSelection/hDCADaughters", "DCA daughters before", HistType::kTH1F, {{200, 0., 2.}}); + registry.add("QA/K0s/BeforeSelection/hCtau", "c tau before", HistType::kTH1F, {{200, 0., 50.}}); + registry.add("QA/K0s/AfterSelection/hMassVsPt", "K0S after (no mass window)", HistType::kTH2F, {k0mass, k0pt}); + registry.add("QA/K0s/AfterSelection/hArmenteros", "Armenteros", HistType::kTH2F, {{100, -1., 1.}, {100, 0., 0.3}}); + registry.add("QA/K0s/AfterSelection/hCosPA", "cosPA", HistType::kTH1F, {{200, 0.9, 1.}}); + registry.add("QA/K0s/AfterSelection/hDCADaughters", "DCA daughters", HistType::kTH1F, {{200, 0., 2.}}); + registry.add("QA/K0s/AfterSelection/hCtau", "c tau", HistType::kTH1F, {{200, 0., 50.}}); + registry.add("QA/K0sK0s/hPairCutFlow", "pair cut flow", HistType::kTH1F, {{NPairSelectionSteps, 0.5, NPairSelectionSteps + 0.5}}); + registry.add("QA/K0sK0s/hPairCutFlowVsCent", "pair cut flow vs centrality", HistType::kTH2F, {{NPairSelectionSteps, 0.5, NPairSelectionSteps + 0.5}, cent}); + registry.add("QA/K0sK0s/SameEvent/hMass", "same-event pair mass", HistType::kTH1F, {pairmass}); + registry.add("QA/K0sK0s/SameEvent/hDeltaR", "same-event DeltaR", HistType::kTH1F, {{200, 0., 5.}}); + registry.add("QA/K0sK0s/SameEvent/hPt1VsPt2", "K0S pT correlation", HistType::kTH2F, {k0pt, k0pt}); + registry.add("QA/K0sK0s/SameEvent/hK0Mass1VsMass2", "K0S mass correlation", HistType::kTH2F, {k0mass, k0mass}); + registry.add("QA/K0sK0s/MixedEvent/hMass", "mixed-event pair mass", HistType::kTH1F, {pairmass}); + registry.add("QA/Flow/QVectors/hQxQyFT0C", "corrected FT0C q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/QVectors/hQxQyFT0A", "corrected FT0A q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/QVectors/hQxQyTPCpos", "corrected TPCpos q2", HistType::kTH2F, {{200, -1., 1.}, {200, -1., 1.}}); + registry.add("QA/Flow/EventPlane/hPsi2VsCentFT0C", "FT0C event plane", HistType::kTH2F, {cent, {120, -1.5708, 1.5708}}); + registry.add("QA/Flow/Calibration/hCalibrationFlag", "Q-vector calibration flag", HistType::kTH1F, {{5, -0.5, 4.5}}); + registry.add("K0s/hCounts", "K0S counts", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + registry.add("K0s/hCos2Weighted", "K0S EP numerator", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/hSPWeightedFT0C", "K0S SP numerator FT0C", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/hSPWeightedFT0A", "K0S SP numerator FT0A", HistType::kTHnSparseF, {k0mass, k0pt, cent}, true); + registry.add("K0s/LikeSign/PP/hCounts", "like-sign pi+pi+ candidates", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + registry.add("K0s/LikeSign/MM/hCounts", "like-sign pi-pi- candidates", HistType::kTHnSparseF, {k0mass, k0pt, cent}); + for (auto const& base : {std::string("K0sK0s/SameEvent/"), std::string("K0sK0s/MixedEvent/"), std::string("K0sK0s/Sideband/Combined/"), std::string("K0sK0s/Sideband/LowerLower/"), std::string("K0sK0s/Sideband/LowerUpper/"), std::string("K0sK0s/Sideband/UpperUpper/")}) { + registry.add((base + "hCounts").c_str(), "K0S-K0S counts", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}); + registry.add((base + "hP2Weighted").c_str(), "pair angular moment P2(cos theta*)", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add((base + "hP4Weighted").c_str(), "pair angular moment P4(cos theta*)", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + } + registry.add("K0sK0s/SameEvent/hCos2Weighted", "pair EP numerator", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add("K0sK0s/SameEvent/hSPWeightedFT0C", "pair SP numerator FT0C", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add("K0sK0s/SameEvent/hSPWeightedFT0A", "pair SP numerator FT0A", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + for (auto const& base : {std::string("K0sK0s/LikeSign/PPPP/"), std::string("K0sK0s/LikeSign/MMMM/"), std::string("K0sK0s/LikeSign/PPMM/"), std::string("K0sK0s/LikeSign/Combined/")}) { + registry.add((base + "hCounts").c_str(), "like-sign K0S-K0S mimic counts", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}); + registry.add((base + "hP2Weighted").c_str(), "like-sign pair angular moment P2", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + registry.add((base + "hP4Weighted").c_str(), "like-sign pair angular moment P4", HistType::kTHnSparseF, {pairmass, pairpt, cent, absct, phistar}, true); + } + registry.add("Resolution/SP/pFT0CFT0AVsCent", "FT0C-FT0A SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/SP/pFT0CTPCposVsCent", "FT0C-TPCpos SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/SP/pFT0ATPCposVsCent", "FT0A-TPCpos SP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0CFT0AVsCent", "FT0C-FT0A EP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0CTPCposVsCent", "FT0C-TPCpos EP correlation", HistType::kTProfile, {cent}); + registry.add("Resolution/EP/pCos2PsiFT0ATPCposVsCent", "FT0A-TPCpos EP correlation", HistType::kTProfile, {cent}); + + constexpr std::array EventSelectionLabels{"all", "vertex", "sel8", "no same-bunch pileup", "no TF border", "no ITS ROF border", "centrality"}; + constexpr std::array TrackSelectionLabels{"all", "kinematics", "TPC clusters", "track quality", "TPC PID", "TOF PID"}; + constexpr std::array K0sSelectionLabels{"all", "daughters", "kinematics", "topology", "daughter DCA", "decay geometry", "Armenteros", "Lambda rejection"}; + constexpr std::array PairSelectionLabels{"all", "no shared daughters", "selected K0S", "close pair", "kinematics", "signal mass"}; + auto eventCutFlow = registry.get(HIST("Event/hEventCutFlow")); + auto trackCutFlow = registry.get(HIST("QA/Tracks/hCutFlow")); + auto k0sCutFlow = registry.get(HIST("QA/K0s/hCutFlow")); + auto pairCutFlow = registry.get(HIST("QA/K0sK0s/hPairCutFlow")); + auto pairCutFlowVsCent = registry.get(HIST("QA/K0sK0s/hPairCutFlowVsCent")); + for (size_t index = 0; index < EventSelectionLabels.size(); ++index) { + eventCutFlow->GetXaxis()->SetBinLabel(index + 1, EventSelectionLabels[index]); + } + for (size_t index = 0; index < TrackSelectionLabels.size(); ++index) { + trackCutFlow->GetXaxis()->SetBinLabel(index + 1, TrackSelectionLabels[index]); + } + for (size_t index = 0; index < K0sSelectionLabels.size(); ++index) { + k0sCutFlow->GetXaxis()->SetBinLabel(index + 1, K0sSelectionLabels[index]); + } + for (size_t index = 0; index < PairSelectionLabels.size(); ++index) { + pairCutFlow->GetXaxis()->SetBinLabel(index + 1, PairSelectionLabels[index]); + pairCutFlowVsCent->GetXaxis()->SetBinLabel(index + 1, PairSelectionLabels[index]); + } + } + + SidebandRegion sidebandRegion(float mass) const + { + if (mass >= k0Cuts.k0sLowerSidebandMin.value && mass < k0Cuts.k0sLowerSidebandMax.value) { + return SidebandRegion::Lower; + } + if (mass >= k0Cuts.k0sUpperSidebandMin.value && mass < k0Cuts.k0sUpperSidebandMax.value) { + return SidebandRegion::Upper; + } + return SidebandRegion::Outside; + } + + void fillSidebandPair(ROOT::Math::PxPyPzMVector const& d1, ROOT::Math::PxPyPzMVector const& m, float cent, SidebandRegion side1, SidebandRegion side2) + { + auto ang = decayAngles(d1, m); + const double cosTheta2 = ang[0] * ang[0]; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/Sideband/Combined/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/Combined/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/Combined/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + if (side1 == SidebandRegion::Lower && side2 == SidebandRegion::Lower) { + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/LowerLower/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } else if (side1 == SidebandRegion::Upper && side2 == SidebandRegion::Upper) { + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/UpperUpper/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } else { + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/Sideband/LowerUpper/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + } + } + + template + bool selectEvent(C const& c, bool fill) + { + float cent = c.centFT0C(); + if (fill) { + registry.fill(HIST("Event/hVertexZBefore"), c.posZ()); + registry.fill(HIST("Event/hCentralityBefore"), cent); + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::All)); + } + if (std::abs(c.posZ()) > eventCuts.maxAbsZvtx) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Vertex)); + } + if (eventCuts.requireSel8 && !c.sel8()) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Sel8)); + } + if (eventCuts.requireNoSameBunchPileup && !c.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoSameBunchPileup)); + } + if (eventCuts.requireNoTimeFrameBorder && !c.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoTimeFrameBorder)); + } + if (eventCuts.requireNoITSROFrameBorder && !c.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::NoItsRofBorder)); + } + if (cent < eventCuts.minCentrality || cent >= eventCuts.maxCentrality) { + return false; + } + if (fill) { + registry.fill(HIST("Event/hEventCutFlow"), enumValue(EventSelectionStep::Centrality)); + registry.fill(HIST("Event/hVertexZAfter"), c.posZ()); + registry.fill(HIST("Event/hCentralityAfter"), cent); + } + return true; + } + + template + bool selectDaughter(T const& t, bool qa) + { + bool hasTOF = t.hasTOF(); + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::All)); + registry.fill(HIST("QA/Tracks/BeforeSelection/hPt"), t.pt()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hEta"), t.eta()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hDCAxyVsPt"), t.pt(), t.dcaXY()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hDCAzVsPt"), t.pt(), t.dcaZ()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCCrossedRowsVsPt"), t.pt(), t.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCSignalVsP"), t.p(), t.tpcSignal()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTPCNSigmaPiVsP"), t.p(), t.tpcNSigmaPi()); + if (hasTOF) { + registry.fill(HIST("QA/Tracks/BeforeSelection/hTOFBetaVsP"), t.p(), t.beta()); + registry.fill(HIST("QA/Tracks/BeforeSelection/hTOFNSigmaPiVsP"), t.p(), t.tofNSigmaPi()); + } + } + if (t.pt() < daughterCuts.daughterMinPt || std::abs(t.eta()) > daughterCuts.maxAbsEta) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::Kinematics)); + } + if (t.tpcNClsCrossedRows() < daughterCuts.minTPCRows || t.tpcCrossedRowsOverFindableCls() < daughterCuts.minTPCRowsOverFindable) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TpcClusters)); + } + if (t.tpcChi2NCl() > daughterCuts.maxTPCChi2NCl || t.itsChi2NCl() > daughterCuts.maxITSChi2NCl) { + return false; + } + if (daughterCuts.minITSClusters > 0 && t.itsNCls() < daughterCuts.minITSClusters) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TrackQuality)); + } + if (std::abs(t.tpcNSigmaPi()) > daughterCuts.maxTPCNSigmaPi) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TpcPid)); + } + const auto pidMode = static_cast(daughterCuts.pidMode.value); + bool requireTOF = pidMode == PionPidMode::TpcAndTof || (pidMode == PionPidMode::TofRequiredAboveMomentum && t.p() >= daughterCuts.tofRequiredAboveP); + bool useTOF = requireTOF || (pidMode == PionPidMode::TpcWithTofIfAvailable && hasTOF); + if (requireTOF && !hasTOF) { + return false; + } + if (useTOF && std::abs(t.tofNSigmaPi()) > daughterCuts.maxTOFNSigmaPi) { + return false; + } + if (qa) { + registry.fill(HIST("QA/Tracks/hCutFlow"), enumValue(TrackSelectionStep::TofPid)); + registry.fill(HIST("QA/Tracks/AfterSelection/hPt"), t.pt()); + registry.fill(HIST("QA/Tracks/AfterSelection/hEta"), t.eta()); + registry.fill(HIST("QA/Tracks/AfterSelection/hDCAxyVsPt"), t.pt(), t.dcaXY()); + registry.fill(HIST("QA/Tracks/AfterSelection/hDCAzVsPt"), t.pt(), t.dcaZ()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCCrossedRowsVsPt"), t.pt(), t.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCSignalVsP"), t.p(), t.tpcSignal()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTPCNSigmaPiVsP"), t.p(), t.tpcNSigmaPi()); + if (hasTOF) { + registry.fill(HIST("QA/Tracks/AfterSelection/hTOFBetaVsP"), t.p(), t.beta()); + registry.fill(HIST("QA/Tracks/AfterSelection/hTOFNSigmaPiVsP"), t.p(), t.tofNSigmaPi()); + } + } + return true; + } + + template + bool selectK0(C const& c, V const& v, DaughterTracks const&, bool qa) + { + float ctau = v.distovertotmom(c.posX(), c.posY(), c.posZ()) * o2::constants::physics::MassK0Short; + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::All)); + registry.fill(HIST("QA/K0s/BeforeSelection/hMassVsPt"), v.mK0Short(), v.pt()); + registry.fill(HIST("QA/K0s/BeforeSelection/hArmenteros"), v.alpha(), v.qtarm()); + registry.fill(HIST("QA/K0s/BeforeSelection/hCosPA"), v.v0cosPA()); + registry.fill(HIST("QA/K0s/BeforeSelection/hDCADaughters"), v.dcaV0daughters()); + registry.fill(HIST("QA/K0s/BeforeSelection/hCtau"), ctau); + } + auto pos = v.template posTrack_as(); + auto neg = v.template negTrack_as(); + if (!selectDaughter(pos, qa)) { + return false; + } + if (!selectDaughter(neg, qa)) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Daughters)); + } + if (v.pt() < k0Cuts.k0sMinPt || v.pt() >= k0Cuts.k0sMaxPt || std::abs(v.rapidity(0)) >= k0Cuts.k0sMaxAbsY) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Kinematics)); + } + if (v.v0cosPA() < k0Cuts.minCosPA || v.dcaV0daughters() > k0Cuts.maxDCADaughters) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Topology)); + } + if (std::abs(v.dcapostopv()) < k0Cuts.minDCAPosToPV || std::abs(v.dcanegtopv()) < k0Cuts.minDCANegToPV) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::DaughterDca)); + } + if (v.v0radius() < k0Cuts.minRadius || v.v0radius() >= k0Cuts.maxRadius) { + return false; + } + if (std::abs(ctau) > k0Cuts.maxCtau) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::DecayGeometry)); + } + if (k0Cuts.useArmenterosCut && v.qtarm() <= k0Cuts.armenterosSlope * std::abs(v.alpha())) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::Armenteros)); + } + if (k0Cuts.rejectLambda && (std::abs(v.mLambda() - o2::constants::physics::MassLambda0) < k0Cuts.lambdaRejectionWindow || std::abs(v.mAntiLambda() - o2::constants::physics::MassLambda0) < k0Cuts.lambdaRejectionWindow)) { + return false; + } + if (qa) { + registry.fill(HIST("QA/K0s/hCutFlow"), enumValue(K0sSelectionStep::LambdaRejection)); + registry.fill(HIST("QA/K0s/AfterSelection/hMassVsPt"), v.mK0Short(), v.pt()); + registry.fill(HIST("QA/K0s/AfterSelection/hArmenteros"), v.alpha(), v.qtarm()); + registry.fill(HIST("QA/K0s/AfterSelection/hCosPA"), v.v0cosPA()); + registry.fill(HIST("QA/K0s/AfterSelection/hDCADaughters"), v.dcaV0daughters()); + registry.fill(HIST("QA/K0s/AfterSelection/hCtau"), ctau); + } + return true; + } + + template + bool getQ(C const& c, int det, float& qx, float& qy) const + { + const int rawIndex = det * QVectorComponents + flow.correctionStep.value + (flow.harmonic.value - 2) * flow.numberOfDetectors.value * QVectorComponents; + if (rawIndex < 0 || det < 0) { + return false; + } + const auto index = static_cast(rawIndex); + const auto detectorIndex = static_cast(det); + if (index >= c.qvecRe().size() || index >= c.qvecIm().size() || detectorIndex >= c.qvecAmp().size() || c.qvecAmp()[detectorIndex] < flow.minQAmplitude.value) { + return false; + } + qx = c.qvecRe()[index]; + qy = c.qvecIm()[index]; + return std::isfinite(qx) && std::isfinite(qy); + } + + std::array decayAngles(ROOT::Math::PxPyPzMVector const& d, ROOT::Math::PxPyPzMVector const& mother) const + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto rest = boost(d); + ROOT::Math::XYZVector z(mother.Px(), mother.Py(), mother.Pz()); + if (z.R() == 0.) { + z.SetZ(1.); + } + z = z.Unit(); + ROOT::Math::XYZVector beam(0., 0., 1.); + ROOT::Math::XYZVector y = beam.Cross(z); + if (y.R() < VectorTolerance) { + y.SetY(1.); + } + y = y.Unit(); + ROOT::Math::XYZVector x = y.Cross(z).Unit(); + ROOT::Math::XYZVector k(rest.Px(), rest.Py(), rest.Pz()); + k = k.Unit(); + return {std::abs(k.Dot(z)), std::atan2(k.Dot(y), k.Dot(x))}; + } + + template + void fillResolution(C const& c, float cent, float cX, float cY, float aX, float aY, float tX, float tY) + { + auto dot = [](float x1, float y1, float x2, float y2) { return x1 * x2 + y1 * y2; }; + double psiC = std::atan2(cY, cX) / flow.harmonic, psiA = std::atan2(aY, aX) / flow.harmonic, psiT = std::atan2(tY, tX) / flow.harmonic; + registry.fill(HIST("Resolution/SP/pFT0CFT0AVsCent"), cent, dot(cX, cY, aX, aY)); + registry.fill(HIST("Resolution/SP/pFT0CTPCposVsCent"), cent, dot(cX, cY, tX, tY)); + registry.fill(HIST("Resolution/SP/pFT0ATPCposVsCent"), cent, dot(aX, aY, tX, tY)); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0CFT0AVsCent"), cent, std::cos(flow.harmonic * (psiC - psiA))); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0CTPCposVsCent"), cent, std::cos(flow.harmonic * (psiC - psiT))); + registry.fill(HIST("Resolution/EP/pCos2PsiFT0ATPCposVsCent"), cent, std::cos(flow.harmonic * (psiA - psiT))); + registry.fill(HIST("QA/Flow/QVectors/hQxQyFT0C"), cX, cY); + registry.fill(HIST("QA/Flow/QVectors/hQxQyFT0A"), aX, aY); + registry.fill(HIST("QA/Flow/QVectors/hQxQyTPCpos"), tX, tY); + registry.fill(HIST("QA/Flow/EventPlane/hPsi2VsCentFT0C"), cent, psiC); + registry.fill(HIST("QA/Flow/Calibration/hCalibrationFlag"), c.isCalibrated()); + } + + struct LikeSignCandidate { + ROOT::Math::PxPyPzMVector p4; + uint64_t firstTrackId{}; + uint64_t secondTrackId{}; + }; + + template + bool buildLikeSignCandidate(C const& c, T const& t1, T const& t2, LikeSignCandidate& candidate) + { + auto track1 = getTrackParCov(t1); + auto track2 = getTrackParCov(t2); + int nCandidates = 0; + try { + nCandidates = lsFitter.process(track1, track2); + } catch (std::runtime_error const&) { + return false; + } + if (nCandidates == 0) { + return false; + } + auto const& vertex = lsFitter.getPCACandidate(); + std::array p1{}; + std::array p2{}; + lsFitter.getTrack(0).getPxPyPzGlo(p1); + lsFitter.getTrack(1).getPxPyPzGlo(p2); + ROOT::Math::PxPyPzMVector pion1(p1[0], p1[1], p1[2], o2::constants::physics::MassPionCharged); + ROOT::Math::PxPyPzMVector pion2(p2[0], p2[1], p2[2], o2::constants::physics::MassPionCharged); + auto pair = pion1 + pion2; + if (pair.Pt() < k0Cuts.k0sMinPt || pair.Pt() >= k0Cuts.k0sMaxPt || std::abs(pair.Rapidity()) >= k0Cuts.k0sMaxAbsY) { + return false; + } + const std::array pv{c.posX(), c.posY(), c.posZ()}; + const std::array sv{static_cast(vertex[0]), static_cast(vertex[1]), static_cast(vertex[2])}; + const std::array momentum{static_cast(pair.Px()), static_cast(pair.Py()), static_cast(pair.Pz())}; + const float cosPA = RecoDecay::cpa(pv, sv, momentum); + const float dcaDaughters = lsFitter.getChi2AtPCACandidate(); + if (cosPA < k0Cuts.minCosPA || dcaDaughters > k0Cuts.maxDCADaughters) { + return false; + } + if (std::abs(t1.dcaXY()) < k0Cuts.minDCAPosToPV || std::abs(t2.dcaXY()) < k0Cuts.minDCANegToPV) { + return false; + } + const float radius = std::hypot(vertex[0], vertex[1]); + if (radius < k0Cuts.minRadius || radius >= k0Cuts.maxRadius) { + return false; + } + const float decayLength = std::sqrt((vertex[0] - c.posX()) * (vertex[0] - c.posX()) + (vertex[1] - c.posY()) * (vertex[1] - c.posY()) + (vertex[2] - c.posZ()) * (vertex[2] - c.posZ())); + const float ctau = decayLength * o2::constants::physics::MassK0Short / pair.P(); + if (std::abs(ctau) > k0Cuts.maxCtau) { + return false; + } + ROOT::Math::XYZVector total(pair.Px(), pair.Py(), pair.Pz()); + if (total.R() <= 0.) { + return false; + } + total = total.Unit(); + ROOT::Math::XYZVector daughter1(p1[0], p1[1], p1[2]); + ROOT::Math::XYZVector daughter2(p2[0], p2[1], p2[2]); + const double longitudinal1 = daughter1.Dot(total); + const double longitudinal2 = daughter2.Dot(total); + const double denominator = longitudinal1 + longitudinal2; + if (std::abs(denominator) < VectorTolerance) { + return false; + } + const double alpha = (longitudinal1 - longitudinal2) / denominator; + const double qt = (daughter1 - longitudinal1 * total).R(); + if (k0Cuts.useArmenterosCut && qt <= k0Cuts.armenterosSlope * std::abs(alpha)) { + return false; + } + candidate = {.p4 = pair, .firstTrackId = static_cast(t1.globalIndex()), .secondTrackId = static_cast(t2.globalIndex())}; + return true; + } + + void fillLikeSignPair(LikeSignCandidate const& first, LikeSignCandidate const& second, float cent, LikeSignCategory category) + { + if (first.firstTrackId == second.firstTrackId || first.firstTrackId == second.secondTrackId || first.secondTrackId == second.firstTrackId || first.secondTrackId == second.secondTrackId) { + return; + } + ROOT::Math::PxPyPzMVector d1(first.p4.Px(), first.p4.Py(), first.p4.Pz(), o2::constants::physics::MassK0Short); + ROOT::Math::PxPyPzMVector d2(second.p4.Px(), second.p4.Py(), second.p4.Pz(), o2::constants::physics::MassK0Short); + auto pair = d1 + d2; + if (std::abs(pair.Rapidity()) >= pairCuts.pairMaxAbsY || pair.Pt() < pairCuts.pairMinPt || pair.Pt() >= pairCuts.pairMaxPt || pair.M() < pairCuts.pairMassMin || pair.M() >= pairCuts.pairMassMax) { + return; + } + const auto angles = decayAngles(d1, pair); + const double cosTheta2 = angles[0] * angles[0]; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/Combined/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + if (category == LikeSignCategory::PositivePositive) { + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/PPPP/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } else if (category == LikeSignCategory::NegativeNegative) { + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/MMMM/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } else { + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hCounts"), pair.M(), pair.Pt(), cent, angles[0], angles[1]); + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hP2Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p2Weight); + registry.fill(HIST("K0sK0s/LikeSign/PPMM/hP4Weighted"), pair.M(), pair.Pt(), cent, angles[0], angles[1], p4Weight); + } + } + + template + void fillLikeSign(C const& c, DaughterTracks const& tracks) + { + auto tracksInCollision = tracks.sliceBy(perCollisionTrack, c.globalIndex()); + std::vector positiveTrackRows; + std::vector negativeTrackRows; + positiveTrackRows.reserve(tracksInCollision.size()); + negativeTrackRows.reserve(tracksInCollision.size()); + + // Apply all inexpensive daughter-quality and PID selections before forming + // pairs. Running the DCA fitter over every track pair scales as N^2 and + // dominated the processing time in high-multiplicity events. + for (auto const& track : tracksInCollision) { + if (!selectDaughter(track, false)) { + continue; + } + const auto localRow = static_cast(track.globalIndex()) - tracks.offset(); + (track.signed1Pt() > 0.f ? positiveTrackRows : negativeTrackRows).push_back(localRow); + } + + std::vector positiveCandidates; + std::vector negativeCandidates; + auto buildCandidates = [&](std::vector const& selectedRows, std::vector& candidates, bool positive) { + for (size_t i = 0; i < selectedRows.size(); ++i) { + auto track1 = tracks.iteratorAt(selectedRows[i]); + for (size_t j = i + 1; j < selectedRows.size(); ++j) { + auto track2 = tracks.iteratorAt(selectedRows[j]); + LikeSignCandidate candidate; + if (!buildLikeSignCandidate(c, track1, track2, candidate)) { + continue; + } + if (positive) { + registry.fill(HIST("K0s/LikeSign/PP/hCounts"), candidate.p4.M(), candidate.p4.Pt(), c.centFT0C()); + } else { + registry.fill(HIST("K0s/LikeSign/MM/hCounts"), candidate.p4.M(), candidate.p4.Pt(), c.centFT0C()); + } + if (candidate.p4.M() >= k0Cuts.k0sMassMin && candidate.p4.M() < k0Cuts.k0sMassMax) { + candidates.push_back(candidate); + } + } + } + }; + buildCandidates(positiveTrackRows, positiveCandidates, true); + buildCandidates(negativeTrackRows, negativeCandidates, false); + for (size_t i = 0; i < positiveCandidates.size(); ++i) { + for (size_t j = i + 1; j < positiveCandidates.size(); ++j) { + fillLikeSignPair(positiveCandidates[i], positiveCandidates[j], c.centFT0C(), LikeSignCategory::PositivePositive); + } + } + for (size_t i = 0; i < negativeCandidates.size(); ++i) { + for (size_t j = i + 1; j < negativeCandidates.size(); ++j) { + fillLikeSignPair(negativeCandidates[i], negativeCandidates[j], c.centFT0C(), LikeSignCategory::NegativeNegative); + } + } + for (auto const& positive : positiveCandidates) { + for (auto const& negative : negativeCandidates) { + fillLikeSignPair(positive, negative, c.centFT0C(), LikeSignCategory::PositiveNegative); + } + } + } + + template + void fillSame(C const& c, V const& v0s, DaughterTracks const& tracks) + { + float cent = c.centFT0C(), cX = 0.f, cY = 0.f, aX = 0.f, aY = 0.f, tX = 0.f, tY = 0.f; + bool qok = getQ(c, flow.ft0cId, cX, cY) && getQ(c, flow.ft0aId, aX, aY) && getQ(c, flow.tpcPosId, tX, tY); + if (qok) { + fillResolution(c, cent, cX, cY, aX, aY, tX, tY); + } + for (auto const& v : v0s) { + if (!selectK0(c, v, tracks, true)) { + continue; + } + double phi = v.phi(), psi = qok ? std::atan2(cY, cX) / flow.harmonic : 0.; + double ux = std::cos(flow.harmonic * phi), uy = std::sin(flow.harmonic * phi); + registry.fill(HIST("K0s/hCounts"), v.mK0Short(), v.pt(), cent); + if (qok) { + registry.fill(HIST("K0s/hCos2Weighted"), v.mK0Short(), v.pt(), cent, std::cos(flow.harmonic * (phi - psi))); + registry.fill(HIST("K0s/hSPWeightedFT0C"), v.mK0Short(), v.pt(), cent, ux * cX + uy * cY); + registry.fill(HIST("K0s/hSPWeightedFT0A"), v.mK0Short(), v.pt(), cent, ux * aX + uy * aY); + } + } + for (auto const& [v1, v2] : combinations(CombinationsStrictlyUpperIndexPolicy(v0s, v0s))) { + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::All)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::All), cent); + if (v1.posTrackId() == v2.posTrackId() || v1.posTrackId() == v2.negTrackId() || v1.negTrackId() == v2.posTrackId() || v1.negTrackId() == v2.negTrackId()) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::NoSharedDaughters)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::NoSharedDaughters), cent); + if (!selectK0(c, v1, tracks, false) || !selectK0(c, v2, tracks, false)) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::SelectedK0s)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::SelectedK0s), cent); + ROOT::Math::PxPyPzMVector d1(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short), d2(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short), m = d1 + d2; + double dr = std::hypot(v1.eta() - v2.eta(), TVector2::Phi_mpi_pi(v1.phi() - v2.phi())); + if (pairCuts.enableClosePairRejection && dr < pairCuts.minDeltaR) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::ClosePair)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::ClosePair), cent); + if (std::abs(m.Rapidity()) >= pairCuts.pairMaxAbsY || m.Pt() < pairCuts.pairMinPt || m.Pt() >= pairCuts.pairMaxPt || m.M() < pairCuts.pairMassMin || m.M() >= pairCuts.pairMassMax) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::Kinematics)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::Kinematics), cent); + const bool signalPair = v1.mK0Short() >= k0Cuts.k0sMassMin && v1.mK0Short() < k0Cuts.k0sMassMax && v2.mK0Short() >= k0Cuts.k0sMassMin && v2.mK0Short() < k0Cuts.k0sMassMax; + const auto side1 = sidebandRegion(v1.mK0Short()); + const auto side2 = sidebandRegion(v2.mK0Short()); + if (side1 != SidebandRegion::Outside && side2 != SidebandRegion::Outside) { + fillSidebandPair(d1, m, cent, side1, side2); + } + if (!signalPair) { + continue; + } + registry.fill(HIST("QA/K0sK0s/hPairCutFlow"), enumValue(PairSelectionStep::SignalMass)); + registry.fill(HIST("QA/K0sK0s/hPairCutFlowVsCent"), enumValue(PairSelectionStep::SignalMass), cent); + registry.fill(HIST("QA/K0sK0s/SameEvent/hMass"), m.M()); + registry.fill(HIST("QA/K0sK0s/SameEvent/hDeltaR"), dr); + registry.fill(HIST("QA/K0sK0s/SameEvent/hPt1VsPt2"), v1.pt(), v2.pt()); + registry.fill(HIST("QA/K0sK0s/SameEvent/hK0Mass1VsMass2"), v1.mK0Short(), v2.mK0Short()); + auto ang = decayAngles(d1, m); + registry.fill(HIST("K0sK0s/SameEvent/hCounts"), m.M(), m.Pt(), cent, ang[0], ang[1]); + const double cosTheta = ang[0]; + const double cosTheta2 = cosTheta * cosTheta; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/SameEvent/hP2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/SameEvent/hP4Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], p4Weight); + if (qok) { + double ux = std::cos(flow.harmonic * m.Phi()), uy = std::sin(flow.harmonic * m.Phi()), psi = std::atan2(cY, cX) / flow.harmonic; + registry.fill(HIST("K0sK0s/SameEvent/hCos2Weighted"), m.M(), m.Pt(), cent, ang[0], ang[1], std::cos(flow.harmonic * (m.Phi() - psi))); + registry.fill(HIST("K0sK0s/SameEvent/hSPWeightedFT0C"), m.M(), m.Pt(), cent, ang[0], ang[1], ux * cX + uy * cY); + registry.fill(HIST("K0sK0s/SameEvent/hSPWeightedFT0A"), m.M(), m.Pt(), cent, ang[0], ang[1], ux * aX + uy * aY); + } + } + if (likeSign.enableLikeSign) { + fillLikeSign(c, tracks); + } + } + + void processSame(Collisions::iterator const& collision, aod::V0Datas const& v0s, DaughterTracks const& tracks) + { + if (!selectEvent(collision, true)) { + return; + } + fillSame(collision, v0s, tracks); + } + PROCESS_SWITCH(K0sK0sFlow, processSame, "same-event K0S-K0S and flow", true); + + void processMixed(Collisions const& collisions, aod::V0Datas const& v0s, DaughterTracks const& tracks) + { + MixingBinning binning{{axisMixZvtx, axisMixCent}, true}; + for (auto const& [c1, c2] : selfCombinations(binning, pairCuts.mixingDepth, -1, collisions, collisions)) { + if (c1.index() == c2.index()) { + continue; + } + if (!selectEvent(c1, false) || !selectEvent(c2, false)) { + continue; + } + auto s1 = v0s.sliceBy(perCollisionV0, c1.globalIndex()); + auto s2 = v0s.sliceBy(perCollisionV0, c2.globalIndex()); + if (s1.size() == 0 || s2.size() == 0) { + continue; + } + for (auto const& [v1, v2] : combinations(CombinationsFullIndexPolicy(s1, s2))) { + if (!selectK0(c1, v1, tracks, false) || !selectK0(c2, v2, tracks, false) || v1.mK0Short() < k0Cuts.k0sMassMin || v1.mK0Short() >= k0Cuts.k0sMassMax || v2.mK0Short() < k0Cuts.k0sMassMin || v2.mK0Short() >= k0Cuts.k0sMassMax) { + continue; + } + ROOT::Math::PxPyPzMVector d1(v1.px(), v1.py(), v1.pz(), o2::constants::physics::MassK0Short), d2(v2.px(), v2.py(), v2.pz(), o2::constants::physics::MassK0Short), m = d1 + d2; + if (std::abs(m.Rapidity()) >= pairCuts.pairMaxAbsY || m.Pt() < pairCuts.pairMinPt || m.Pt() >= pairCuts.pairMaxPt || m.M() < pairCuts.pairMassMin || m.M() >= pairCuts.pairMassMax) { + continue; + } + auto ang = decayAngles(d1, m); + registry.fill(HIST("K0sK0s/MixedEvent/hCounts"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1]); + const double cosTheta = ang[0]; + const double cosTheta2 = cosTheta * cosTheta; + const double p2Weight = 0.5 * (3. * cosTheta2 - 1.); + const double p4Weight = 0.125 * (35. * cosTheta2 * cosTheta2 - 30. * cosTheta2 + 3.); + registry.fill(HIST("K0sK0s/MixedEvent/hP2Weighted"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1], p2Weight); + registry.fill(HIST("K0sK0s/MixedEvent/hP4Weighted"), m.M(), m.Pt(), c1.centFT0C(), ang[0], ang[1], p4Weight); + registry.fill(HIST("QA/K0sK0s/MixedEvent/hMass"), m.M()); + } + } + } + PROCESS_SWITCH(K0sK0sFlow, processMixed, "mixed-event K0S-K0S (optional cross-check)", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx index e636df0f774..efd75953055 100644 --- a/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx +++ b/PWGLF/Tasks/Resonances/k1AnalysisMicro.cxx @@ -14,12 +14,11 @@ /// \author Su-Jeong Ji , Bong-Hwi Lim /// +#include "PWGLF/Core/K1AnalysisMicroCore.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" #include "PWGLF/DataModel/LFResonanceTables.h" -#include -#include #include -#include #include #include #include @@ -28,129 +27,135 @@ #include #include #include +#include #include #include #include -#include -#include // FIXME +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; -using namespace o2::constants::physics; -using namespace o2::constants::math; -; +using namespace o2::analysis::resonance; +using namespace o2::analysis::k1micro; + +/// Histogram binning, QA and debug output +struct HistogramOptions : ConfigurableGroup { + Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; + Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; + Configurable cfgTruthDebug{"cfgTruthDebug", 0, "Maximum logged matched candidates per truth channel"}; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require generated K1 rapidity in the configured window for MC efficiency numerators"}; +}; + +enum BinAnti : unsigned int { + kNormal = 0, + kAnti, + kNAEnd +}; + +enum BinType : unsigned int { + kK1P = 0, + kK1N, + kK1P_Mix, + kK1N_Mix, + kK1P_GenINEL10, + kK1N_GenINEL10, + kK1P_GenINELgt10, + kK1N_GenINELgt10, + kK1P_GenTrig10, + kK1N_GenTrig10, + kK1P_GenEvtSel, + kK1N_GenEvtSel, + kK1P_Rec, + kK1N_Rec, + kTYEnd +}; + +enum class QAFolder { + Before, // QA/*: before the candidate cuts + After, // QAcut/*: after the candidate cuts + MC // QAMC/*: matched K1 truth candidates +}; struct K1AnalysisMicro { - enum BinAnti : unsigned int { - kNormal = 0, - kAnti, - kNAEnd - }; - enum BinType : unsigned int { - kK1P = 0, - kK1N, - kK1P_Mix, - kK1N_Mix, - kK1P_GenINEL10, - kK1N_GenINEL10, - kK1P_GenINELgt10, - kK1N_GenINELgt10, - kK1P_GenTrig10, - kK1N_GenTrig10, - kK1P_GenEvtSel, - kK1N_GenEvtSel, - kK1P_Rec, - kK1N_Rec, - kTYEnd - }; + // Module-initializer v001 tables; full tracks keep their unversioned schema as a fallback. + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoTracks = aod::ResoTracks; // no v001 exists; K1 does not need ResoTrackTracks (trackId unused) + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCTracks = soa::Join; + using ResoMCMicroTracks = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + SliceCache cache; - Preslice perRCol = aod::resodaughter::resoCollisionId; - Preslice perCollision = aod::track::collisionId; + // Registered only to enable the slice cache that SameKindPair (event mixing) needs, as in Xi1820Analysis + Preslice perResoCollisionTrack = aod::resodaughter::resoCollisionId; + Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - using ResoMCCols = soa::Join; - //// Configurables - Configurable cNbinsDiv{"cNbinsDiv", 1, "Integer to divide the number of bins"}; + // Selection shared with the K1 training-table task (plain JSON keys, no group prefix) + EventCuts eventCuts; + TrackCuts trackCuts; + PionPidCuts pionPID; + KaonPidCuts kaonPID; + SecondaryCuts secondaryCuts; + CandidateCuts candidateCuts; + HistogramOptions histogramOptions; + /// Event Mixing Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgMultBins{"cfgMultBins", {VARIABLE_WIDTH, 0.0f, 20.0f, 40.0f, 60.0f, 80.0f, 100.0f, 200.0f, 99999.f}, "Mixing bins - multiplicity"}; - /// Pre-selection cuts - Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; - - /// DCA Selections - // DCAr to PV - Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1, "Track DCAr cut to PV Maximum"}; - // DCAz to PV - Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1, "Track DCAz cut to PV Maximum"}; - Configurable cMinDCAzToPVcut{"cMinDCAzToPVcut", 0.0, "Track DCAz cut to PV Minimum"}; - - /// PID Selections - Configurable cMaxTPCnSigmaPion{"cMaxTPCnSigmaPion", 3.0, "TPC nSigma cut for Pion"}; // TPC - Configurable cMaxTOFnSigmaPion{"cMaxTOFnSigmaPion", 3.0, "TOF nSigma cut for Pion"}; // TOF - Configurable nsigmaCutCombinedPion{"nsigmaCutCombinedPion", -999, "Combined nSigma cut for Pion"}; // Combined - Configurable cTOFVeto{"cTOFVeto", true, "TOF Veto, if false, TOF is nessessary for PID selection"}; // TOF Veto - Configurable cUseOnlyTOFTrackPi{"cUseOnlyTOFTrackPi", false, "Use only TOF track for PID selection"}; // Use only TOF track for Pion PID selection - // Kaon - Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon"}; // TPC - Configurable cMaxTOFnSigmaKaon{"cMaxTOFnSigmaKaon", 3.0, "TOF nSigma cut for Kaon"}; // TOF - Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", -999, "Combined nSigma cut for Kaon"}; // Combined - Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; // Use only TOF track for Kaon PID selection - // Track selections - Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor - Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; - Configurable additionalEvsel{"additionalEvsel", true, "Additional event selcection"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 0, "Number of TPC cluster"}; - Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; - Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; - Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; - - // Secondary selection - Configurable cMinSecondaryPtCut{"cMinSecondaryPtCut", 0.5, "Min pT cut for secondary selection"}; - /* - Configurable cfgModeK892orRho{"cfgModeK892orRho", false, "Secondary scenario for K892 (true) or Rho (false)"}; - Configurable cSecondaryMasswindow{"cSecondaryMasswindow", 0.1, "Secondary inv mass selection window"}; - Configurable cMinAnotherSecondaryMassCut{"cMinAnotherSecondaryMassCut", 0, "Min inv. mass selection of another secondary scenario"}; - Configurable cMaxAnotherSecondaryMassCut{"cMaxAnotherSecondaryMassCut", 999, "MAx inv. mass selection of another secondary scenario"}; - Configurable cMinPiKaMassCut{"cMinPiKaMassCut", 0, "bPion-Kaon pair inv mass selection minimum"}; - Configurable cMaxPiKaMassCut{"cMaxPiKaMassCut", 999, "bPion-Kaon pair inv mass selection maximum"}; - Configurable cMinAngle{"cMinAngle", 0, "Minimum angle between K(892)0 and bachelor pion"}; - Configurable cMaxAngle{"cMaxAngle", 4, "Maximum angle between K(892)0 and bachelor pion"}; - Configurable cMinPairAsym{"cMinPairAsym", -1, "Minimum pair asymmetry"}; - Configurable cMaxPairAsym{"cMaxPairAsym", 1, "Maximum pair asymmetry"}; -*/ - - // K1 selection - Configurable cK1MaxRap{"cK1MaxRap", 0.5, "K1 maximum rapidity"}; - Configurable cK1MinRap{"cK1MinRap", -0.5, "K1 minimum rapidity"}; - - void init(o2::framework::InitContext&) + + K1AnalysisMicroCore core; + std::array truthDebugCounts{}; + + void init(InitContext&) + { + const int sameEventModes = static_cast(doprocessResoTracks) + static_cast(doprocessResoMicroTracks) + + static_cast(doprocessMC) + static_cast(doprocessMCMicro); + const int mixedEventModes = static_cast(doprocessME) + static_cast(doprocessMEMicro); + if (sameEventModes > 1 || mixedEventModes > 1) { + LOG(fatal) << "Enable at most one same-event mode and one mixing mode"; + } + + ProcessModes modes; + modes.microTracks = doprocessResoMicroTracks || doprocessMCMicro || doprocessMEMicro; + modes.mcReco = doprocessMC || doprocessMCMicro; + modes.mcRecoMicro = doprocessMCMicro; + modes.mcGen = doprocessMCTrue; + core.init(histos, eventCuts, trackCuts, pionPID, kaonPID, secondaryCuts, candidateCuts, modes); + registerHistograms(modes); + + // Print output histograms statistics + LOG(info) << "Size of the histograms in K1 Analysis Task"; + histos.print(); + } + + void registerHistograms(ProcessModes const& modes) { + const int nBinsDiv = histogramOptions.cNbinsDiv; std::vector centBinning = {0., 1., 5., 10., 15., 20., 25., 30., 35., 40., 45., 50., 55., 60., 65., 70., 80., 90., 100., 200.}; AxisSpec centAxis = {centBinning, "T0M (%)"}; AxisSpec ptAxis = {150, 0, 15, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec dcaxyAxis = {300, 0, 3, "DCA_{#it{xy}} (cm)"}; AxisSpec dcazAxis = {500, 0, 5, "DCA_{#it{z}} (cm)"}; - AxisSpec invMassAxisK892 = {1400 / cNbinsDiv, 0.6, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // K(892)0 - AxisSpec invMassAxisRho = {2000 / cNbinsDiv, 0.0, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // rho - AxisSpec invMassAxisReso = {1600 / cNbinsDiv, 0.9f, 2.5f, "Invariant Mass (GeV/#it{c}^2)"}; // K1 - AxisSpec invMassAxisScan = {250, 0, 2.5, "Invariant Mass (GeV/#it{c}^2)"}; // For selection + AxisSpec invMassAxisK892 = {1400 / nBinsDiv, 0.6, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // K(892)0 + AxisSpec invMassAxisRho = {2000 / nBinsDiv, 0.0, 2.0, "Invariant Mass (GeV/#it{c}^2)"}; // rho + AxisSpec invMassAxisReso = {1600 / nBinsDiv, 0.9f, 2.5f, "Invariant Mass (GeV/#it{c}^2)"}; // K1 AxisSpec pidQAAxis = {130, -6.5, 6.5}; - AxisSpec dataTypeAxis = {9, 0, 9, "Histogram types"}; - AxisSpec mcTypeAxis = {4, 0, 4, "Histogram types"}; // THnSparse AxisSpec axisAnti = {BinAnti::kNAEnd, 0, BinAnti::kNAEnd, "Type of bin: Normal or Anti"}; AxisSpec axisType = {BinType::kTYEnd, 0, BinType::kTYEnd, "Type of bin with charge and mix"}; - AxisSpec mcLabelAxis = {5, -0.5, 4.5, "MC Label"}; // DCA QA // Primary pion @@ -223,7 +228,19 @@ struct K1AnalysisMicro { histos.add("k1invmass_Mix", "Invariant mass of K1(1270) (ME)", HistType::kTH1F, {invMassAxisReso}); // MC - if (doprocessMC) { + if (modes.mcReco) { + AxisSpec channelAxis = {3, -0.5, 2.5, "0: non-K1, 1: rho K, 2: K* pi"}; + histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); + histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); + // These channel and pT histograms are pre-truth-fiducial QA; MCReco/response is the validated numerator. + histos.add("MCReco/channel", "Pre-truth-fiducial QA: selected pi-pi-K combinations by truth channel", HistType::kTH1D, {channelAxis}); + histos.add("MCReco/mass", "Reconstructed mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisReso}); + histos.add("MCReco/pt", "Pre-truth-fiducial QA: reconstructed pT by truth channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCReco/piPiMass", "pi-pi mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisRho}); + histos.add("MCReco/pi1KMass", "First pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); + histos.add("MCReco/pi2KMass", "Second pion-kaon mass by truth channel", HistType::kTH2D, {channelAxis, invMassAxisK892}); + histos.add("MCReco/response", "Selected MC candidates;reconstructed pT;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); histos.add("k1invmass_MC", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); histos.add("k1invmass_MC_noK1", "Invariant mass of K1(1270)", HistType::kTH1F, {invMassAxisReso}); @@ -254,494 +271,340 @@ struct K1AnalysisMicro { histos.add("QAMC/hInvmassSecon_PiKa", "Invariant mass of secondary resonance vs pion-kaon", HistType::kTH2F, {invMassAxisRho, invMassAxisK892}); histos.add("QAMC/hInvmassSecon", "Invariant mass of secondary resonance", HistType::kTH1F, {invMassAxisRho}); histos.add("QAMC/hpT_Secondary", "pT distribution of secondary resonance", HistType::kTH1F, {ptAxis}); - } // doprocessMC - // Print output histograms statistics - LOG(info) << "Size of the histograms in K1 Analysis Task"; - histos.print(); - } // init - - // PDG code - int kPDGRho770 = 113; - int kK1Plus = 10323; - - template - bool trackCut(const TrackType track) - { - if constexpr (!IsResoMicrotrack) { - // basic track cuts - if (std::abs(track.pt()) < cMinPtcut) - return false; - if (std::abs(track.dcaXY()) > cMaxDCArToPVcut) - return false; - if (std::abs(track.dcaZ()) > cMaxDCAzToPVcut) - return false; - if (track.tpcNClsFound() < cfgTPCcluster) - return false; - if (cfgHasTOF && !track.hasTOF()) - return false; - if (cfgUseITSRefit && !track.passedITSRefit()) - return false; - if (cfgUseTPCRefit && !track.passedTPCRefit()) - return false; - if (cfgPVContributor && !track.isPVContributor()) - return false; - if (cfgPrimaryTrack && !track.isPrimaryTrack()) - return false; - if (cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - if (cfgGlobalTrack && !track.isGlobalTrack()) - return false; - } else { - if (std::abs(track.pt()) < cMinPtcut) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags()) > cMaxDCArToPVcut - Epsilon) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags()) > cMaxDCAzToPVcut - Epsilon) - return false; - if (cfgPrimaryTrack && !track.isPrimaryTrack()) - return false; - if (cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - if (cfgPVContributor && !track.isPVContributor()) - return false; + } // mcReco + if (modes.mcGen) { + AxisSpec channelAxis = {3, -0.5, 2.5, "0: other/unresolved, 1: rho K, 2: K* pi"}; + histos.add("MCGen/chargeChannel", "K1 parents in selected reconstructed events, inside the K1 rapidity window", HistType::kTH2D, {{2, -1.5, 1.5, "K1 charge"}, channelAxis}); + histos.add("MCGen/ptChannel", "Generated K1 pT by immediate decay channel", HistType::kTH2D, {channelAxis, ptAxis}); + histos.add("MCGen/response", "Generated K1 parents;generated pT;channel;charge;centrality", HistType::kTHnSparseD, + {ptAxis, channelAxis, AxisSpec{2, -1.5, 1.5, "K1 charge"}, centAxis}); } - return true; } - // Pion PID selection tools - template - bool selectionPIDpion(const T& candidate) + // Track QA of a pion; isPrimary selects the trkppion (first) or trkspion (second) histograms + template + void fillPionQA(const TrackType& track, bool isPrimary) { - if constexpr (!IsResoMicrotrack) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - if (std::abs(candidate.tpcNSigmaPi()) < cMaxTPCnSigmaPion) { - tpcPIDPassed = true; - } else { - return false; - } - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaPi()) < cMaxTOFnSigmaPion) { - tofPIDPassed = true; + const bool hasTOF = track.hasTOF(); + if (isPrimary) { + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QA/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QA/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } - if ((nsigmaCutCombinedPion > 0) && (candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi() + candidate.tofNSigmaPi() * candidate.tofNSigmaPi() < nsigmaCutCombinedPion * nsigmaCutCombinedPion)) { - tofPIDPassed = true; + histos.fill(HIST("QA/trkppionpT"), track.pt()); + histos.fill(HIST("QA/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkppionDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAcut/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } + histos.fill(HIST("QAcut/trkppionpT"), track.pt()); + histos.fill(HIST("QAcut/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkppionDCAz"), track.dcaZ()); } else { - if (!cTOFVeto) { - return false; + histos.fill(HIST("QAMC/trkppionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkppionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAMC/trkppionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); } - tofPIDPassed = true; - } - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QAMC/trkppionpT"), track.pt()); + histos.fill(HIST("QAMC/trkppionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkppionDCAz"), track.dcaZ()); } } else { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - tpcPIDPassed = std::abs(o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(candidate.pidNSigmaPiFlag())) < cMaxTPCnSigmaPion + Epsilon; - tofPIDPassed = candidate.hasTOF() ? std::abs(o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(candidate.pidNSigmaPiFlag())) < cMaxTOFnSigmaPion + Epsilon : true; - if (tpcPIDPassed && tofPIDPassed) { - return true; + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QA/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QA/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QA/trkspionpT"), track.pt()); + histos.fill(HIST("QA/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkspionDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAcut/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QAcut/trkspionpT"), track.pt()); + histos.fill(HIST("QAcut/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkspionDCAz"), track.dcaZ()); + } else { + histos.fill(HIST("QAMC/trkspionTPCPID"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkspionTOFPID"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("QAMC/trkspionTPCTOFPID"), track.tpcNSigmaPi(), track.tofNSigmaPi()); + } + histos.fill(HIST("QAMC/trkspionpT"), track.pt()); + histos.fill(HIST("QAMC/trkspionDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkspionDCAz"), track.dcaZ()); } } - return false; } - // Kaon PID selection tools - template - bool selectionPIDkaon(const T& candidate) + // Track QA of the bachelor kaon + template + void fillKaonQA(const TrackType& track) { - if constexpr (!IsResoMicrotrack) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - if (std::abs(candidate.tpcNSigmaKa()) < cMaxTPCnSigmaKaon) { - tpcPIDPassed = true; - } else { - return false; + const bool hasTOF = track.hasTOF(); + if constexpr (Folder == QAFolder::Before) { + histos.fill(HIST("QA/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QA/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QA/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaKa()) < cMaxTOFnSigmaKaon) { - tofPIDPassed = true; - } - if ((nsigmaCutCombinedKaon > 0) && (candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa() + candidate.tofNSigmaKa() * candidate.tofNSigmaKa() < nsigmaCutCombinedKaon * nsigmaCutCombinedKaon)) { - tofPIDPassed = true; - } - } else { - if (!cTOFVeto) { - return false; - } - tofPIDPassed = true; - } - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QA/trkkaonpT"), track.pt()); + histos.fill(HIST("QA/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QA/trkkaonDCAz"), track.dcaZ()); + } else if constexpr (Folder == QAFolder::After) { + histos.fill(HIST("QAcut/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QAcut/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QAcut/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } + histos.fill(HIST("QAcut/trkkaonpT"), track.pt()); + histos.fill(HIST("QAcut/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QAcut/trkkaonDCAz"), track.dcaZ()); } else { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - tpcPIDPassed = std::abs(o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(candidate.pidNSigmaKaFlag())) < cMaxTPCnSigmaKaon + Epsilon; - tofPIDPassed = candidate.hasTOF() ? std::abs(o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(candidate.pidNSigmaKaFlag())) < cMaxTOFnSigmaKaon + Epsilon : true; - if (tpcPIDPassed && tofPIDPassed) { - return true; + histos.fill(HIST("QAMC/trkkaonTPCPID"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("QAMC/trkkaonTOFPID"), track.pt(), track.tofNSigmaKa()); + histos.fill(HIST("QAMC/trkkaonTPCTOFPID"), track.tpcNSigmaKa(), track.tofNSigmaKa()); } + histos.fill(HIST("QAMC/trkkaonpT"), track.pt()); + histos.fill(HIST("QAMC/trkkaonDCAxy"), track.dcaXY()); + histos.fill(HIST("QAMC/trkkaonDCAz"), track.dcaZ()); } - return false; } - template - bool isTrueK1(const T& trk1, const T& trk2, const T2& bTrack) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kPiPlus) - return false; - if (std::abs(bTrack.pdgCode()) != kKPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (((std::abs(trk1.motherPDG()) && std::abs(trk2.motherPDG()) != kPDGRho770) && (std::abs(bTrack.motherPDG()) != kK1Plus)) || (std::abs(trk1.motherPDG()) && std::abs(bTrack.motherPDG()) != kK0Star892 && (std::abs(trk2.motherPDG()) != kK1Plus)) || (std::abs(trk2.motherPDG()) && std::abs(bTrack.motherPDG()) != kK0Star892 && (std::abs(trk1.motherPDG()) != kK1Plus))) - return false; - auto siblings = bTrack.siblingIds(); - if (siblings[0] != mother1 && siblings[1] != mother2) - return false; - return true; - } // isTrueK1 - - template - bool isTrueK892(const T& trk1, const T& trk2) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kKPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (std::abs(trk1.motherPDG()) != kK0Star892) - return false; - return true; - } - - template - bool isTrueRho(const T& trk1, const T& trk2) - { - if (std::abs(trk1.pdgCode()) != kPiPlus || std::abs(trk2.pdgCode()) != kPiPlus) - return false; - auto mother1 = trk1.motherId(); - auto mother2 = trk2.motherId(); - if (mother1 != mother2) - return false; - if (std::abs(trk1.motherPDG()) != kPDGRho770) - return false; - return true; - } + // Histograms of the selected pion pairs and (pion, pion, kaon) candidates of one collision + // (or one mixed pair of collisions). The selection itself is the shared K1 core. + // dTracks1: bachelor kaons, dTracks2: pions. template - void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) + void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2, + const K1ParentTruthMap* parentTruth = nullptr) { - auto multiplicity = collision.cent(); - TLorentzVector lDecayDaughter1, lDecayDaughter2, lResonanceSecondary, lDecayDaughter_bach, lResonanceK1; - for (const auto& [trk1, trk2] : combinations(CombinationsFullIndexPolicy(dTracks2, dTracks2))) { - // Full index policy is needed to consider all possible combinations - if (trk1.index() == trk2.index()) - continue; // We need to run (0,1), (1,0) pairs too. But the same id pairs are not needed. - // trk1: pion, trk2: pion, bTrack: kaon - if (!trackCut(trk1) || !trackCut(trk2)) - continue; + const bool fillQA = !IsMix && histogramOptions.additionalQAplots; + const auto multiplicity = collision.cent(); + + // Pion pair passing the pion selection; trk1 is the pion with the lower index + auto onPair = [&](auto const& trk1, auto const& trk2, ROOT::Math::PxPyPzMVector const& secondary, bool passesPairPt) { + if (fillQA) { + fillPionQA(trk1, true); + fillPionQA(trk2, false); + } + if (!passesPairPt) { + return; + } + if (fillQA) { + histos.fill(HIST("QA/hInvmassSecon"), secondary.M()); + } + if constexpr (IsMC) { + histos.fill(HIST("QAMC/hpT_Secondary"), secondary.Pt()); + } + }; - auto trk1pt = trk1.pt(); - auto trk2pt = trk2.pt(); - auto isTrk1hasTOF = trk1.hasTOF(); - auto isTrk2hasTOF = trk2.hasTOF(); - - if constexpr (!IsResoMicrotrack) { - auto trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); - auto trk1NSigmaPiTOF = (isTrk1hasTOF) ? trk1.tofNSigmaPi() : -999.; - auto trk2NSigmaPiTPC = trk2.tpcNSigmaPi(); - auto trk2NSigmaPiTOF = (isTrk2hasTOF) ? trk2.tofNSigmaPi() : -999.; - - if (cUseOnlyTOFTrackPi && !isTrk1hasTOF) - continue; - if (!selectionPIDpion(trk1) || !selectionPIDpion(trk2)) - continue; - - if constexpr (!IsMix) { - - histos.fill(HIST("QA/trkppionTPCPID"), trk1pt, trk1NSigmaPiTPC); - if (isTrk1hasTOF) { - histos.fill(HIST("QA/trkppionTOFPID"), trk1pt, trk1NSigmaPiTOF); - histos.fill(HIST("QA/trkppionTPCTOFPID"), trk1NSigmaPiTPC, trk1NSigmaPiTOF); - } - histos.fill(HIST("QA/trkppionpT"), trk1pt); - histos.fill(HIST("QA/trkppionDCAxy"), trk1.dcaXY()); - histos.fill(HIST("QA/trkppionDCAz"), trk1.dcaZ()); - - histos.fill(HIST("QA/trkspionTPCPID"), trk2pt, trk2NSigmaPiTPC); - if (isTrk2hasTOF) { - histos.fill(HIST("QA/trkspionTOFPID"), trk2pt, trk2NSigmaPiTOF); - histos.fill(HIST("QA/trkspionTPCTOFPID"), trk2NSigmaPiTPC, trk2NSigmaPiTOF); - } - histos.fill(HIST("QA/trkspionpT"), trk2pt); - histos.fill(HIST("QA/trkspionDCAxy"), trk2.dcaXY()); - histos.fill(HIST("QA/trkspionDCAz"), trk2.dcaZ()); - } - } else { - histos.fill(HIST("QA/trkppionTPCPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaPiFlag())); - if (isTrk1hasTOF) { - histos.fill(HIST("QA/trkppionTOFPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaPiFlag())); - histos.fill(HIST("QA/trkppionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaPiFlag()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaPiFlag())); - } - histos.fill(HIST("QA/trkppionpT"), trk1pt); - histos.fill(HIST("QA/trkppionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk1.trackSelectionFlags())); - histos.fill(HIST("QA/trkppionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk1.trackSelectionFlags())); - - histos.fill(HIST("QA/trkspionTPCPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaPiFlag())); - if (isTrk2hasTOF) { - histos.fill(HIST("QA/trkspionTOFPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaPiFlag())); - histos.fill(HIST("QA/trkspionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaPiFlag()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaPiFlag())); - } - histos.fill(HIST("QA/trkspionpT"), trk2pt); - histos.fill(HIST("QA/trkspionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk2.trackSelectionFlags())); - histos.fill(HIST("QA/trkspionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk2.trackSelectionFlags())); + // Candidate passing the pion, pair and kaon selection; pion1 is the K*0 partner in the unlike-sign case + auto onCandidate = [&](auto const& kaon, auto const& pion1, auto const& pion2, K1CandidateValues const& c) { + if (fillQA) { + fillKaonQA(kaon); + } + if (!c.inRapidity) { + return; } - // Resonance reconstruction - lDecayDaughter1.SetXYZM(trk1.px(), trk1.py(), trk1.pz(), MassPionCharged); - lDecayDaughter2.SetXYZM(trk2.px(), trk2.py(), trk2.pz(), MassPionCharged); - lResonanceSecondary = lDecayDaughter1 + lDecayDaughter2; + // QA histogram before the candidate cuts + if (fillQA) { + histos.fill(HIST("QA/K1OA"), c.angle); + histos.fill(HIST("QA/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QA/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QA/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QA/hpT_Secondary"), c.secondary.Pt()); + } + if (!c.passesCandidateCuts) { + return; + } - if (lResonanceSecondary.Pt() < cMinSecondaryPtCut) - continue; + // QA histograms after the candidate cuts + if (fillQA) { + fillPionQA(pion1, true); + fillPionQA(pion2, false); + fillKaonQA(kaon); + histos.fill(HIST("QAcut/K1OA"), c.angle); + histos.fill(HIST("QAcut/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QAcut/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QAcut/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QAcut/hInvmassSecon"), c.secondary.M()); + histos.fill(HIST("QAcut/hpT_Secondary"), c.secondary.Pt()); + } - if constexpr (!IsMix) { - histos.fill(HIST("QA/hInvmassSecon"), lResonanceSecondary.M()); + const unsigned int typeNormal = BinAnti::kNormal; + if constexpr (IsMix) { + const unsigned int typeK1 = kaon.sign() > 0 ? BinType::kK1P_Mix : BinType::kK1N_Mix; + histos.fill(HIST("hInvmass_K1_Mix"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_Mix"), c.k1.M()); + return; } - if constexpr (IsMC) { - /* - if (isTrueK892(trk1, trk2)) - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); - } else { - if (isTrueRho(trk1, trk2)) - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); - } - */ - histos.fill(HIST("QAMC/hpT_Secondary"), lResonanceSecondary.Pt()); + unsigned int typeK1 = kaon.sign() > 0 ? BinType::kK1P : BinType::kK1N; + if (c.isUnlikeSign) { + histos.fill(HIST("k1invmass"), c.k1.M()); + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + } else { + histos.fill(HIST("k1invmass_LS"), c.k1.M()); + histos.fill(HIST("hInvmass_K1_LS"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); } - // Mass Window cut is removed - - for (const auto& bTrack : dTracks1) { - if (bTrack.index() == trk1.index() || bTrack.index() == trk2.index()) - continue; - if (!trackCut(bTrack)) - continue; - if (!selectionPIDkaon(bTrack)) - continue; - - // K1 reconstruction - lDecayDaughter_bach.SetXYZM(bTrack.px(), bTrack.py(), bTrack.pz(), MassKaonCharged); - lResonanceK1 = lResonanceSecondary + lDecayDaughter_bach; - - // Cuts - if (lResonanceK1.Rapidity() > cK1MaxRap || lResonanceK1.Rapidity() < cK1MinRap) - continue; - - auto lK1Angle = lResonanceSecondary.Angle(lDecayDaughter_bach.Vect()); - auto lPairAsym = (lResonanceSecondary.E() - lDecayDaughter_bach.E()) / (lResonanceSecondary.E() + lDecayDaughter_bach.E()); - - TLorentzVector temp13 = lDecayDaughter1 + lDecayDaughter_bach; - TLorentzVector temp23 = lDecayDaughter2 + lDecayDaughter_bach; - - // QA histogram - if constexpr (!IsMix) { - histos.fill(HIST("QA/K1OA"), lK1Angle); - histos.fill(HIST("QA/K1PairAsym"), lPairAsym); - histos.fill(HIST("QA/hInvmassK892_Rho"), temp13.M(), lResonanceSecondary.M()); - histos.fill(HIST("QA/hInvmassSecon_PiKa"), lResonanceSecondary.M(), temp23.M()); - histos.fill(HIST("QA/hpT_Secondary"), lResonanceSecondary.Pt()); + + if constexpr (IsMC) { + const auto channel = classifyK1Truth(pion1, pion2, kaon); + const int channelBin = static_cast(channel); + histos.fill(HIST("MCReco/channel"), channelBin); + histos.fill(HIST("MCReco/mass"), channelBin, c.k1.M()); + histos.fill(HIST("MCReco/pt"), channelBin, c.k1.Pt()); + histos.fill(HIST("MCReco/piPiMass"), channelBin, c.secondary.M()); + histos.fill(HIST("MCReco/pi1KMass"), channelBin, c.mass13); + histos.fill(HIST("MCReco/pi2KMass"), channelBin, c.mass23); + if (channel == K1TruthChannel::None) { + histos.fill(HIST("k1invmass_MC_noK1"), c.k1.M()); + return; } - // Selection cuts are removed - // QA histograms after the cuts are removed as no cuts are applied - - if constexpr (!IsMix) { - unsigned int typeK1 = bTrack.sign() > 0 ? BinType::kK1P : BinType::kK1N; - unsigned int typeNormal = BinAnti::kNormal; - if (trk1.sign() * trk2.sign() < 0) { - histos.fill(HIST("k1invmass"), lResonanceK1.M()); - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - } else { - histos.fill(HIST("k1invmass_LS"), lResonanceK1.M()); - histos.fill(HIST("hInvmass_K1_LS"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - } - - if constexpr (IsMC) { - if (isTrueK1(trk1, trk2, bTrack)) { - typeK1 = bTrack.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - histos.fill(HIST("k1invmass_MC"), lResonanceK1.M()); - histos.fill(HIST("QAMC/K1OA"), lK1Angle); - histos.fill(HIST("QAMC/K1PairAsym"), lPairAsym); - histos.fill(HIST("QAMC/hInvmassK892_Rho"), temp13.M(), lResonanceSecondary.M()); - histos.fill(HIST("QAMC/hInvmassSecon_PiKa"), lResonanceSecondary.M(), temp23.M()); - histos.fill(HIST("QAMC/hInvmassSecon"), lResonanceSecondary.M()); - histos.fill(HIST("QAMC/hpT_Seocondary"), lResonanceSecondary.Pt()); - - if constexpr (!IsResoMicrotrack) { - - auto trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); - auto trk1NSigmaPiTOF = (isTrk1hasTOF) ? trk1.tofNSigmaPi() : -999.; - auto trk2NSigmaPiTPC = trk2.tpcNSigmaPi(); - auto trk2NSigmaPiTOF = (isTrk2hasTOF) ? trk2.tofNSigmaPi() : -999.; - - // PID QA primary pion - histos.fill(HIST("QAMC/trkppionTPCPID"), trk1pt, trk1NSigmaPiTPC); - if (isTrk1hasTOF) { - histos.fill(HIST("QAMC/trkppionTOFPID"), trk1pt, trk1NSigmaPiTOF); - histos.fill(HIST("QAMC/trkppionTPCTOFPID"), trk1NSigmaPiTPC, trk1NSigmaPiTOF); - } - histos.fill(HIST("QAMC/trkppionpT"), trk1pt); - histos.fill(HIST("QAMC/trkppionDCAxy"), trk1.dcaXY()); - histos.fill(HIST("QAMC/trkppionDCAz"), trk1.dcaZ()); - - // PID QA secondary pion - histos.fill(HIST("QAMC/trkspionTPCPID"), trk2pt, trk2NSigmaPiTPC); - if (isTrk2hasTOF) { - histos.fill(HIST("QAMC/trkspionTOFPID"), trk2pt, trk2NSigmaPiTOF); - histos.fill(HIST("QAMC/trkspionTPCTOFPID"), trk2NSigmaPiTPC, trk2NSigmaPiTOF); - } - histos.fill(HIST("QAMC/trkspionpT"), trk2pt); - histos.fill(HIST("QAMC/trkspionDCAxy"), trk2.dcaXY()); - histos.fill(HIST("QAMC/trkspionDCAz"), trk2.dcaZ()); - - } else { - - histos.fill(HIST("QAMC/trkppionTPCPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaSelectionFlags())); - if (isTrk1hasTOF) { - histos.fill(HIST("QAMC/trkppionTOFPID"), trk1pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaSelectionFlags())); - histos.fill(HIST("QAMC/trkppionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaSelectionFlags()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaSelectionFlags())); - } - histos.fill(HIST("QAMC/trkppionpT"), trk1pt); - histos.fill(HIST("QAMC/trkppionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk1.trackSelectionFlags())); - histos.fill(HIST("QAMC/trkppionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk1.trackSelectionFlags())); - - // PID QA secondary pion - histos.fill(HIST("QAMC/trkspionTPCPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaSelectionFlags())); - if (isTrk2hasTOF) { - histos.fill(HIST("QAMC/trkspionTOFPID"), trk2pt, o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaSelectionFlags())); - histos.fill(HIST("QAMC/trkspionTPCTOFPID"), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk2.pidNSigmaSelectionFlags()), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk2.pidNSigmaSelectionFlags())); - } - histos.fill(HIST("QAMC/trkspionpT"), trk2pt); - histos.fill(HIST("QAMC/trkspionDCAxy"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk2.trackSelectionFlags())); - histos.fill(HIST("QAMC/trkspionDCAz"), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk2.trackSelectionFlags())); - } - } else { - histos.fill(HIST("k1invmass_MC_noK1"), lResonanceK1.M()); - } - } // IsMC - } else { - unsigned int typeK1 = bTrack.sign() > 0 ? BinType::kK1P_Mix : BinType::kK1N_Mix; - unsigned int typeNormal = BinAnti::kNormal; - histos.fill(HIST("hInvmass_K1_Mix"), typeNormal, typeK1, multiplicity, lResonanceK1.Pt(), lResonanceK1.M()); - histos.fill(HIST("k1invmass_Mix"), lResonanceK1.M()); + if (truthDebugCounts[channelBin] < histogramOptions.cfgTruthDebug) { + ++truthDebugCounts[channelBin]; + LOGP(info, "K1Truth channel={} collision={} tracks=({},{},{}) pdg=({},{},{}) mothers=({},{},{}) motherPDG=({},{},{}) siblings=(({},{}),({},{}),({},{}))", + channelBin, collision.globalIndex(), + pion1.globalIndex(), pion2.globalIndex(), kaon.globalIndex(), + pion1.pdgCode(), pion2.pdgCode(), kaon.pdgCode(), pion1.motherId(), pion2.motherId(), kaon.motherId(), + pion1.motherPDG(), pion2.motherPDG(), kaon.motherPDG(), + pion1.siblingIds()[0], pion1.siblingIds()[1], pion2.siblingIds()[0], pion2.siblingIds()[1], kaon.siblingIds()[0], kaon.siblingIds()[1]); } - } // bTrack - } - } // fillHistograms + const int motherId = k1MotherId(channel, kaon, pion1, pion2); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + const auto* parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; + const bool truthRapidity = c.isUnlikeSign && parent && (!histogramOptions.cUseTruthRapidity || (parent->y >= candidateCuts.cK1MinRap && parent->y <= candidateCuts.cK1MaxRap)); + if (truthRapidity) { + typeK1 = kaon.sign() > 0 ? BinType::kK1P_Rec : BinType::kK1N_Rec; + histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, c.k1.Pt(), c.k1.M()); + histos.fill(HIST("k1invmass_MC"), c.k1.M()); + const int parentCharge = parent->pdgCode > 0 ? 1 : -1; + histos.fill(HIST("MCReco/response"), c.k1.Pt(), parent->pt, channelBin, parentCharge, multiplicity); + } + histos.fill(HIST("QAMC/K1OA"), c.angle); + histos.fill(HIST("QAMC/K1PairAsym"), c.pairAsym); + histos.fill(HIST("QAMC/hInvmassK892_Rho"), c.mass13, c.secondary.M()); + histos.fill(HIST("QAMC/hInvmassSecon_PiKa"), c.secondary.M(), c.mass23); + histos.fill(HIST("QAMC/hInvmassSecon"), c.secondary.M()); + histos.fill(HIST("QAMC/hpT_Secondary"), c.secondary.Pt()); + + // PID QA primary and secondary pion + fillPionQA(pion1, true); + fillPionQA(pion2, false); + fillKaonQA(kaon); + } + }; - void processResoTracks(aod::ResoCollision const& collision, - aod::ResoTracks const& resotracks) + core.forEachCandidate(histos, collision, dTracks1, dTracks2, IsMC || fillQA, onPair, onCandidate); + } + + void processResoTracks(ResoCollisions::iterator const& collision, + ResoTracks const& resotracks) { + if (!core.passesEventCuts(collision)) { + return; + } fillHistograms(collision, resotracks, resotracks); } PROCESS_SWITCH(K1AnalysisMicro, processResoTracks, "Process ResoTracks", false); - void processResoMicroTracks(aod::ResoCollision const& collision, - aod::ResoMicroTracks const& resomicrotracks) + void processResoMicroTracks(ResoCollisions::iterator const& collision, + ResoMicroTracks const& resomicrotracks) { + if (!core.passesEventCuts(collision)) { + return; + } fillHistograms(collision, resomicrotracks, resomicrotracks); } PROCESS_SWITCH(K1AnalysisMicro, processResoMicroTracks, "Process ResoMicroTracks", true); - void processMC(aod::ResoCollision const& collision, - soa::Join const& resotracks) + void processMC(ResoMCCols::iterator const& collision, + ResoMCTracks const& resotracks, ResoMCParents const& resoParents) { - fillHistograms(collision, resotracks, resotracks); + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + histos.fill(HIST("MCReco/collisions"), 0.5); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, resotracks, resotracks, &parentTruth); } PROCESS_SWITCH(K1AnalysisMicro, processMC, "Process Event for MC", false); - void processMCTrue(ResoMCCols::iterator const& collision, aod::ResoMCParents const& resoParents) + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) { - auto multiplicity = collision.cent(); - for (const auto& part : resoParents) { - if (std::abs(part.pdgCode()) != kK1Plus) - continue; - if (std::abs(part.y()) > 0.5) { - continue; - } - bool pass1 = false; - bool pass2 = false; - bool pass3 = false; - bool pass4 = false; - if (std::abs(part.daughterPDG1()) == 313 || std::abs(part.daughterPDG2()) == 313) { // At least one decay into K892 - pass2 = true; - } - if (std::abs(part.daughterPDG1()) == kPiPlus || std::abs(part.daughterPDG2()) == kPiPlus) { // At lest one decay into pion - pass1 = true; - } - if (std::abs(part.daughterPDG1()) == kPDGRho770 || std::abs(part.daughterPDG2()) == kPDGRho770) { - pass4 = true; - } - if (std::abs(part.daughterPDG1()) == kKPlus || std::abs(part.daughterPDG2()) == kKPlus) { - pass3 = true; - } - if (!pass1 || !pass2 || !pass3 || !pass4) // If we have both decay products - continue; - auto typeNormal = part.pdgCode() > 0 ? BinAnti::kNormal : BinAnti::kAnti; - if (collision.isVtxIn10()) // INEL>10 - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenINEL10 : BinType::kK1N_GenINEL10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isVtxIn10() && collision.isInSel8()) // INEL>10, vtx10 - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenINELgt10 : BinType::kK1N_GenINELgt10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isVtxIn10() && collision.isTriggerTVX()) // vtx10, TriggerTVX - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenTrig10 : BinType::kK1N_GenTrig10; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } - if (collision.isInAfterAllCuts()) // after all event selection - { - auto typeK1 = part.pdgCode() > 0 ? BinType::kK1P_GenEvtSel : BinType::kK1N_GenEvtSel; - histos.fill(HIST("hInvmass_K1"), typeNormal, typeK1, multiplicity, part.pt(), 1); - } + // The modular producer already selected these reconstructed collisions. + // Apply precisely the same reconstruction loop as the data baseline. + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; } + histos.fill(HIST("MCReco/collisions"), 0.5); + histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + fillHistograms(collision, tracks, tracks, &parentTruth); } - PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process Event for MC", false); + PROCESS_SWITCH(K1AnalysisMicro, processMCMicro, "Process reconstructed MC with micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + // Keep other/unresolved immediate decays too; never require both pairs. + core.forEachGeneratedK1(histos, resoParents, [&](auto const& part, K1TruthChannel channel) { + const int charge = part.pdgCode() > 0 ? 1 : -1; + histos.fill(HIST("MCGen/chargeChannel"), charge, static_cast(channel)); + histos.fill(HIST("MCGen/ptChannel"), static_cast(channel), part.pt()); + if (channel != K1TruthChannel::None) { + histos.fill(HIST("MCGen/response"), part.pt(), static_cast(channel), charge, collision.cent()); + } + }); + } + PROCESS_SWITCH(K1AnalysisMicro, processMCTrue, "Process generated K1 in selected events with v001 parents", false); // Processing Event Mixing using BinningTypeVtxZT0M = ColumnBinningPolicy; - void processME(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) + void processME(ResoCollisions const& collisions, ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; - SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; + SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } fillHistograms(collision1, tracks1, tracks2); } }; PROCESS_SWITCH(K1AnalysisMicro, processME, "Process EventMixing light without partition", false); // Processing Event Mixing -- Micro - // using BinningTypeVtxZT0M = ColumnBinningPolicy; - void processMEMicro(o2::aod::ResoCollisions const& collisions, aod::ResoMicroTracks const& resomicrotracks) + void processMEMicro(ResoCollisions const& collisions, ResoMicroTracks const& resomicrotracks) { auto tracksTuple = std::make_tuple(resomicrotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; - SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; + SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } fillHistograms(collision1, tracks1, tracks2); } }; - PROCESS_SWITCH(K1AnalysisMicro, processMEMicro, "Process EventMixing light without partition", true); -}; // struct + PROCESS_SWITCH(K1AnalysisMicro, processMEMicro, "Process EventMixing light without partition", false); +}; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/Tasks/Resonances/k1TrainingTable.cxx b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx new file mode 100644 index 00000000000..008984a0112 --- /dev/null +++ b/PWGLF/Tasks/Resonances/k1TrainingTable.cxx @@ -0,0 +1,285 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file k1TrainingTable.cxx +/// \brief Derived K1 training table workflow +/// \author Bong-Hwi Lim +/// + +#include "PWGLF/Core/K1AnalysisMicroCore.h" +#include "PWGLF/Core/K1MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" +#include "PWGLF/DataModel/LFK1MlTables.h" +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; +using namespace o2::analysis::resonance; +using namespace o2::analysis::k1micro; + +static_assert(std::extent_v == o2::analysis::k1ml::NMasterFeatures, + "K1MasterFeatures column size must match the K1 ML feature contract"); + +/// Writes the unlike-sign K1 micro candidates of the shared K1 selection, with the +/// canonical (kaon, same-sign pion, opposite-sign pion) tracks and the master features. +struct K1TrainingTable { + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCMicroTracks = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + + // FNV-1a over the bit patterns of the master features, for parity logs + static constexpr uint64_t FnvOffsetBasis = 14695981039346656037ULL; + static constexpr uint64_t FnvPrime = 1099511628211ULL; + static constexpr unsigned int BitsPerByte = 8; + static constexpr unsigned int BitsPerFloat = 32; + static constexpr uint32_t ByteMask = 0xffU; + + Produces k1MlEvents; + Produces k1MlTracks; + Produces k1MlCandidates; + Produces k1MlInputs; + Produces k1MlTruth; + Produces k1MlGenAudit; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Selection shared with the K1 histogram task (plain JSON keys, no group prefix) + EventCuts eventCuts; + TrackCuts trackCuts; + PionPidCuts pionPID; + KaonPidCuts kaonPID; + SecondaryCuts secondaryCuts; + CandidateCuts candidateCuts; + + Configurable k1MlExportStage{"k1MlExportStage", "loose", "Candidate export stage: loose (pass bits >= 1) or selected (pass bits = 31)"}; + Configurable k1MlLooseAudit{"k1MlLooseAudit", true, "Record loose US cutflow and mass/pT/activity spectrum"}; + Configurable k1MlParityRows{"k1MlParityRows", 0, "Log feature hashes of the first N exported candidates"}; + + K1AnalysisMicroCore core; + int64_t k1MlEventRow = -1; + std::unordered_map k1MlTrackRows; + int k1MlParityLogged = 0; + + void init(InitContext&) + { + if (static_cast(doprocessResoMicroTracks) + static_cast(doprocessMCMicro) != 1 || + (doprocessMCTrue && !doprocessMCMicro)) { + LOG(fatal) << "K1 training table requires exactly one of processResoMicroTracks and processMCMicro; processMCTrue requires processMCMicro"; + } + if (k1MlParityRows < 0) { + LOG(fatal) << "k1MlParityRows must not be negative"; + } + if (k1MlExportStage.value != "loose" && k1MlExportStage.value != "selected") { + LOG(fatal) << "k1MlExportStage must be loose or selected"; + } + + ProcessModes modes; + modes.microTracks = true; + modes.mcReco = doprocessMCMicro; + modes.mcRecoMicro = doprocessMCMicro; + modes.mcGen = doprocessMCTrue; + LooseStageOptions looseOptions; + looseOptions.audit = k1MlLooseAudit; + looseOptions.exportSelected = k1MlExportStage.value == "selected"; + core.init(histos, eventCuts, trackCuts, pionPID, kaonPID, secondaryCuts, candidateCuts, modes, looseOptions); + if (doprocessMCMicro) { + histos.add("MCReco/collisions", "Selected reconstructed MC collisions", HistType::kTH1D, {{1, 0, 1}}); + histos.add("MCReco/microTracks", "Input micro tracks in selected MC collisions", HistType::kTH1D, {{1, 0, 1}}); + } + + // Candidates that violate the canonical or feature contract are skipped, never written. + auto skipped = histos.add("ML/exportSkipped", "Skipped candidates;K1 ML build status;candidates", HistType::kTH1D, {{6, -0.5, 5.5}}); + const std::array statusLabels{"Ok", "InvalidChargePattern", "ReusedTrack", "InvalidMomentum", "InvalidKinematics", "InvalidContract"}; + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + + LOG(info) << "Size of the histograms in K1 training table task"; + histos.print(); + } + + template + int64_t writeK1MlTrack(Track const& track) + { + const auto id = static_cast(track.globalIndex()); + if (auto it = k1MlTrackRows.find(id); it != k1MlTrackRows.end()) { + return it->second; + } + k1MlTracks(k1MlEventRow, static_cast(track.trackId()), track.px(), track.py(), track.pz(), + track.pidNSigmaPiFlag(), track.pidNSigmaKaFlag(), track.pidNSigmaPrFlag(), + track.trackSelectionFlags(), track.trackFlags(), track.tpcNClsCrossedRows(), track.itsClusterMap()); + const int64_t row = k1MlTracks.lastIndex(); + k1MlTrackRows.emplace(id, row); + return row; + } + + template + void writeK1MlEvent(Collision const& collision) + { + k1MlTrackRows.clear(); + // ResoCollisions_001 carries no run number or BC; the reduced collision row identifies the event within its DF. + k1MlEvents(collision.globalIndex(), + collision.posZ(), collision.bMagField(), collision.cent(), collision.multiplicity(), collision.isRecINELgt0()); + k1MlEventRow = k1MlEvents.lastIndex(); + } + + template + void logParity(Collision const& collision, o2::analysis::k1ml::CandidateSnapshot const& candidate, o2::analysis::k1ml::FeaturePack const& pack) + { + if (k1MlParityLogged >= k1MlParityRows) { + return; + } + uint64_t hash = FnvOffsetBasis; + for (const auto& value : pack.master) { + const auto bits = std::bit_cast(value); + for (unsigned int shift = 0; shift < BitsPerFloat; shift += BitsPerByte) { + hash = (hash ^ ((bits >> shift) & ByteMask)) * FnvPrime; + } + } + LOGP(info, "K1MLPARITY collision={} tracks={},{},{} featureHash={}", + collision.globalIndex(), candidate.tracks[0].sourceTrackId, candidate.tracks[1].sourceTrackId, + candidate.tracks[2].sourceTrackId, hash); + ++k1MlParityLogged; + } + + template + void writeK1MlCandidate(Collision const& collision, Kaon const& kaon, Pion const& samePion, Pion const& oppPion, + K1TruthChannel channel, uint16_t passBits, const K1ParentTruthMap* parentTruth = nullptr) + { + using o2::analysis::k1ml::BuildStatus; + const auto canonical = o2::analysis::k1ml::canonicalizeUS(o2::analysis::k1ml::makeTrackSnapshot(kaon), o2::analysis::k1ml::makeTrackSnapshot(samePion), o2::analysis::k1ml::makeTrackSnapshot(oppPion)); + if (canonical.status != BuildStatus::Ok) { + histos.fill(HIST("ML/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = o2::analysis::k1ml::buildMasterFeatures(canonical.candidate); + if (pack.status != BuildStatus::Ok) { + histos.fill(HIST("ML/exportSkipped"), static_cast(pack.status)); + return; + } + logParity(collision, canonical.candidate, pack); + const auto kaonRow = writeK1MlTrack(kaon); + const auto sameRow = writeK1MlTrack(samePion); + const auto oppRow = writeK1MlTrack(oppPion); + ROOT::Math::PxPyPzMVector k{kaon.px(), kaon.py(), kaon.pz(), MassKaonCharged}; + ROOT::Math::PxPyPzMVector s{samePion.px(), samePion.py(), samePion.pz(), MassPionCharged}; + ROOT::Math::PxPyPzMVector o{oppPion.px(), oppPion.py(), oppPion.pz(), MassPionCharged}; + const auto mother = k + s + o; + k1MlCandidates(k1MlEventRow, kaonRow, sameRow, oppRow, + static_cast(mother.M()), static_cast((s + o).M()), + static_cast((k + s).M()), static_cast((k + o).M()), + pack.kinematics.scalarSumPt, static_cast((s + o).Pt()), + static_cast(mother.Pt()), static_cast(mother.Rapidity()), + static_cast(mother.Eta()), static_cast(mother.Phi()), + static_cast(kaon.sign()), passBits); + const int64_t row = k1MlCandidates.lastIndex(); + k1MlInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = channel != K1TruthChannel::None; + int motherId = -1; + const K1ParentTruth* parent = nullptr; + if (matched) { + motherId = k1MotherId(channel, kaon, samePion, oppPion); + const int charge = kaon.pdgCode() > 0 ? 1 : -1; + parent = parentTruth ? findK1ParentTruth(*parentTruth, motherId, channel, charge) : nullptr; + } + // Immediate-mother information alone does not prove a physical UID. + k1MlTruth(row, matched ? uint8_t{1} : uint8_t{2}, static_cast(channel), + parent ? parent->pdgCode : (matched ? kaon.sign() * Pdg::kK1_1270Plus : 0), static_cast(motherId), + parent ? static_cast(parent->pt) : std::numeric_limits::quiet_NaN(), + parent ? static_cast(parent->y) : std::numeric_limits::quiet_NaN()); + } else { + k1MlTruth(row, uint8_t{0}, uint8_t{0}, 0, int64_t{-1}, + std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()); + } + } + + void processResoMicroTracks(ResoCollisions::iterator const& collision, ResoMicroTracks const& tracks) + { + if (!core.passesEventCuts(collision)) { + return; + } + writeK1MlEvent(collision); + // Selection only: the K1 analysis histograms belong to the K1 analysis task + core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, + [this](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits); + }); + } + PROCESS_SWITCH(K1TrainingTable, processResoMicroTracks, "Write K1 candidates from data micro v001 tables", true); + + void processMCMicro(ResoMCCols::iterator const& collision, ResoMCMicroTracks const& tracks, ResoMCParents const& resoParents) + { + // The modular producer already selected these reconstructed collisions. + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + histos.fill(HIST("MCReco/collisions"), 0.5); + histos.fill(HIST("MCReco/microTracks"), 0.5, tracks.size()); + writeK1MlEvent(collision); + const auto parentTruth = makeK1ParentTruthMap(resoParents); + core.forEachCandidate(histos, collision, tracks, tracks, false, nullptr, nullptr, + [this, &parentTruth](auto const& coll, auto const& kaon, auto const& samePion, auto const& oppPion, + K1TruthChannel channel, uint16_t passBits) { + writeK1MlCandidate(coll, kaon, samePion, oppPion, channel, passBits, &parentTruth); + }); + } + PROCESS_SWITCH(K1TrainingTable, processMCMicro, "Write K1 candidates with truth from reconstructed MC micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + core.forEachGeneratedK1(histos, resoParents, [&](auto const& part, K1TruthChannel channel) { + k1MlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), + part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), + part.pt(), part.y(), true); + }); + } + PROCESS_SWITCH(K1TrainingTable, processMCTrue, "Write generated K1 parents of selected reconstructed MC events", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx index 5508bb7cf9a..5af74594dc6 100644 --- a/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/k892SpherocityAnalysis.cxx @@ -511,8 +511,8 @@ struct k892Analysis { { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}, true}; - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (cMixSph) { SameKindPair pairs{binningPositions1, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGLF/Tasks/Resonances/k892analysis.cxx b/PWGLF/Tasks/Resonances/k892analysis.cxx index 8dea9031fc7..f80fbb3b5d7 100644 --- a/PWGLF/Tasks/Resonances/k892analysis.cxx +++ b/PWGLF/Tasks/Resonances/k892analysis.cxx @@ -320,7 +320,7 @@ struct K892analysis { double massPi = MassPionCharged; template - bool eventSelected(TCollision collision, const float& centrality) + bool eventSelected(const TCollision& collision, const float& centrality) { // if (collision.alias_bit(kTVXinTRD)) { // // TRD triggered @@ -342,7 +342,7 @@ struct K892analysis { } template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -862,7 +862,7 @@ struct K892analysis { void processMELight(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/k892analysispbpb.cxx b/PWGLF/Tasks/Resonances/k892analysispbpb.cxx index 05d1d4e0c30..a49fd403148 100644 --- a/PWGLF/Tasks/Resonances/k892analysispbpb.cxx +++ b/PWGLF/Tasks/Resonances/k892analysispbpb.cxx @@ -982,7 +982,7 @@ struct K892analysispbpb { void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1009,7 +1009,7 @@ struct K892analysispbpb { void processMixedEventRun2(Run2Events const& collisions, TrackCandidates const& tracks, BCsWithRun2Info const& bcs) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1034,7 +1034,7 @@ struct K892analysispbpb { void processMixedEventMC(EventCandidatesMCrec const& recCollisions, TrackCandidatesMCrec const& RecTracks, aod::McParticles const&) { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCent colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1059,7 +1059,7 @@ struct K892analysispbpb { void processMixedEventMCRun2(EventCandidatesMCrecRun2 const& recCollisions, TrackCandidatesMCrec const& RecTracks, BCsWithRun2Info const& bcs, aod::McParticles const&) { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxCentRun2 colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/k892hadronphoton.cxx b/PWGLF/Tasks/Resonances/k892hadronphoton.cxx index 9be3bb33b1a..62a32bcfc01 100644 --- a/PWGLF/Tasks/Resonances/k892hadronphoton.cxx +++ b/PWGLF/Tasks/Resonances/k892hadronphoton.cxx @@ -9,16 +9,11 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -// This is a task that reads kstar tables (from sigma0builder) to perform analysis. -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* -// k892hadronphoton analysis task -// *+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+* -// -// Comments, questions, complaints, suggestions? -// Please write to: -// oussama.benchikhi@cern.ch -// gianni.shigeru.setoue.liveraro@cern.ch -// + +/// \file k892hadronphoton.cxx +/// \brief This is a task that reads kstar tables (from sigma0builder) to perform analysis. +/// \author Gianni Shigeru Setoue Liveraro +/// \author Oussama Benchikhi #include "PWGLF/DataModel/LFSigmaTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" @@ -42,12 +37,12 @@ #include #include -#include #include #include #include #include +#include #include #include #include @@ -60,25 +55,25 @@ using namespace o2::framework::expressions; using KStars = soa::Join; using MCKStars = soa::Join; -static const std::vector PhotonSels = {"NoSel", "V0Type", "DCADauToPV", +static const std::vector photonSels = {"NoSel", "V0Type", "DCADauToPV", "DCADau", "DauTPCCR", "TPCNSigmaEl", "V0pT", "Y", "V0Radius", "RZCut", "Armenteros", "CosPA", "PsiPair", "Phi", "Mass"}; -static const std::vector KShortSels = {"NoSel", "V0Radius", "DCADau", "Armenteros", +static const std::vector kshortSels = {"NoSel", "V0Radius", "DCADau", "Armenteros", "CosPA", "Y", "TPCCR", "DauITSCls", "Lifetime", "TPCTOFPID", "DCADauToPV", "Mass"}; static const std::vector DirList = {"BeforeSel", "AfterSel"}; struct k892hadronphoton { - Service ccdb; + Service ccdb{}; ctpRateFetcher rateFetcher; //__________________________________________________ // For manual sliceBy // SliceCache cache; - PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -121,7 +116,7 @@ struct k892hadronphoton { } eventSelections; // generated - Configurable mc_keepOnlyFromGenerator{"mc_keepOnlyFromGenerator", true, "if true, consider only particles from generator to calculate efficiency."}; + Configurable mcKeepOnlyFromGenerator{"mcKeepOnlyFromGenerator", true, "if true, consider only particles from generator to calculate efficiency."}; // QA Configurable fillBkgQAhistos{"fillBkgQAhistos", false, "if true, fill MC QA histograms for Bkg study. Only works with MC."}; @@ -134,65 +129,65 @@ struct k892hadronphoton { //// K0Short criteria: struct : ConfigurableGroup { - Configurable KShort_MLThreshold{"KShort_MLThreshold", 0.1, "Decision Threshold value to select kshorts"}; - Configurable KShortMinDCANegToPv{"KShortMinDCANegToPv", .05, "min DCA Neg To PV (cm)"}; - Configurable KShortMinDCAPosToPv{"KShortMinDCAPosToPv", .05, "min DCA Pos To PV (cm)"}; - Configurable KShortMaxDCAV0Dau{"KShortMaxDCAV0Dau", 2.5, "Max DCA V0 Daughters (cm)"}; - Configurable KShortMinv0radius{"KShortMinv0radius", 0.0, "Min V0 radius (cm)"}; - Configurable KShortMaxv0radius{"KShortMaxv0radius", 40, "Max V0 radius (cm)"}; - Configurable KShortMinQt{"KShortMinQt", 0.1, "Min kshort qt value (AP plot) (GeV/c)"}; - Configurable KShortMaxQt{"KShortMaxQt", 2.5, "Max kshort qt value (AP plot) (GeV/c)"}; - Configurable KShortMinAlpha{"KShortMinAlpha", 0.0, "Min kshort alpha absolute value (AP plot)"}; - Configurable KShortMaxAlpha{"KShortMaxAlpha", 1.0, "Max kshort alpha absolute value (AP plot)"}; - Configurable KShortMinv0cospa{"KShortMinv0cospa", 0.95, "Min V0 CosPA"}; - Configurable KShortMaxLifeTime{"KShortMaxLifeTime", 30, "Max lifetime"}; - Configurable KShortWindow{"KShortWindow", 0.015, "Mass window around expected (in GeV/c2)"}; - Configurable KShortMaxRap{"KShortMaxRap", 0.8, "Max kshort rapidity"}; - Configurable KShortMaxDauEta{"KShortMaxDauEta", 0.8, "Max pseudorapidity of daughter tracks"}; + Configurable kshortMLThreshold{"kshortMLThreshold", 0.1, "Decision Threshold value to select kshorts"}; + Configurable kshortMinDCANegToPv{"kshortMinDCANegToPv", .05, "min DCA Neg To PV (cm)"}; + Configurable kshortMinDCAPosToPv{"kshortMinDCAPosToPv", .05, "min DCA Pos To PV (cm)"}; + Configurable kshortMaxDCAV0Dau{"kshortMaxDCAV0Dau", 2.5, "Max DCA V0 Daughters (cm)"}; + Configurable kshortMinv0radius{"kshortMinv0radius", 0.0, "Min V0 radius (cm)"}; + Configurable kshortMaxv0radius{"kshortMaxv0radius", 40, "Max V0 radius (cm)"}; + Configurable kshortMinQt{"kshortMinQt", 0.1, "Min kshort qt value (AP plot) (GeV/c)"}; + Configurable kshortMaxQt{"kshortMaxQt", 2.5, "Max kshort qt value (AP plot) (GeV/c)"}; + Configurable kshortMinAlpha{"kshortMinAlpha", 0.0, "Min kshort alpha absolute value (AP plot)"}; + Configurable kshortMaxAlpha{"kshortMaxAlpha", 1.0, "Max kshort alpha absolute value (AP plot)"}; + Configurable kshortMinv0cospa{"kshortMinv0cospa", 0.95, "Min V0 CosPA"}; + Configurable kshortMaxLifeTime{"kshortMaxLifeTime", 30, "Max lifetime"}; + Configurable kshortWindow{"kshortWindow", 0.015, "Mass window around expected (in GeV/c2)"}; + Configurable kshortMaxRap{"kshortMaxRap", 0.8, "Max kshort rapidity"}; + Configurable kshortMaxDauEta{"kshortMaxDauEta", 0.8, "Max pseudorapidity of daughter tracks"}; Configurable fselKShortTPCPID{"fselKShortTPCPID", true, "Flag to select kshort-like candidates using TPC NSigma."}; Configurable fselKShortTOFPID{"fselKShortTOFPID", false, "Flag to select kshort-like candidates using TOF NSigma."}; - Configurable KShortMaxTPCNSigmas{"KShortMaxTPCNSigmas", 1e+9, "Max TPC NSigmas for daughters"}; - // Configurable KShortPrMaxTOFNSigmas{"KShortPrMaxTOFNSigmas", 1e+9, "Max TOF NSigmas for daughters"}; - Configurable KShortPiMaxTOFNSigmas{"KShortPiMaxTOFNSigmas", 1e+9, "Max TOF NSigmas for daughters"}; - Configurable KShortMinTPCCrossedRows{"KShortMinTPCCrossedRows", 50, "Min daughter TPC Crossed Rows"}; - Configurable KShortMinITSclusters{"KShortMinITSclusters", 1, "minimum ITS clusters"}; - Configurable KShortRejectPosITSafterburner{"KShortRejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; - Configurable KShortRejectNegITSafterburner{"KShortRejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; + Configurable kshortMaxTPCNSigmas{"kshortMaxTPCNSigmas", 1e+9, "Max TPC NSigmas for daughters"}; + // Configurable kshortMaxTOFNSigmas{"kshortMaxTOFNSigmas", 1e+9, "Max TOF NSigmas for daughters"}; + Configurable kshortPiMaxTOFNSigmas{"kshortPiMaxTOFNSigmas", 1e+9, "Max TOF NSigmas for daughters"}; + Configurable kshortMinTPCCrossedRows{"kshortMinTPCCrossedRows", 50, "Min daughter TPC Crossed Rows"}; + Configurable kshortMinITSclusters{"kshortMinITSclusters", 1, "minimum ITS clusters"}; + Configurable kshortRejectPosITSafterburner{"kshortRejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; + Configurable kshortRejectNegITSafterburner{"kshortRejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; } kshortSelections; //// Photon criteria: struct : ConfigurableGroup { - Configurable Gamma_MLThreshold{"Gamma_MLThreshold", 0.1, "Decision Threshold value to select gammas"}; - Configurable Photonv0TypeSel{"Photonv0TypeSel", 7, "select on a certain V0 type (leave negative if no selection desired)"}; - Configurable PhotonMinDCADauToPv{"PhotonMinDCADauToPv", 0.0, "Min DCA daughter To PV (cm)"}; - Configurable PhotonMaxDCAV0Dau{"PhotonMaxDCAV0Dau", 3.5, "Max DCA V0 Daughters (cm)"}; - Configurable PhotonMinTPCCrossedRows{"PhotonMinTPCCrossedRows", 30, "Min daughter TPC Crossed Rows"}; - Configurable PhotonMinTPCNSigmas{"PhotonMinTPCNSigmas", -7, "Min TPC NSigmas for daughters"}; - Configurable PhotonMaxTPCNSigmas{"PhotonMaxTPCNSigmas", 7, "Max TPC NSigmas for daughters"}; - Configurable PhotonMinPt{"PhotonMinPt", 0.0, "Min photon pT (GeV/c)"}; - Configurable PhotonMaxPt{"PhotonMaxPt", 50.0, "Max photon pT (GeV/c)"}; - Configurable PhotonMaxRap{"PhotonMaxRap", 0.5, "Max photon rapidity"}; - Configurable PhotonMinRadius{"PhotonMinRadius", 3.0, "Min photon conversion radius (cm)"}; - Configurable PhotonMaxRadius{"PhotonMaxRadius", 115, "Max photon conversion radius (cm)"}; - Configurable PhotonMaxZ{"PhotonMaxZ", 240, "Max photon conversion point z value (cm)"}; - Configurable PhotonMaxQt{"PhotonMaxQt", 0.05, "Max photon qt value (AP plot) (GeV/c)"}; - Configurable PhotonMaxAlpha{"PhotonMaxAlpha", 0.95, "Max photon alpha absolute value (AP plot)"}; - Configurable PhotonMinV0cospa{"PhotonMinV0cospa", 0.80, "Min V0 CosPA"}; - Configurable PhotonMaxMass{"PhotonMaxMass", 0.10, "Max photon mass (GeV/c^{2})"}; - Configurable PhotonPsiPairMax{"PhotonPsiPairMax", 1e+9, "maximum psi angle of the track pair"}; - Configurable PhotonMaxDauEta{"PhotonMaxDauEta", 0.8, "Max pseudorapidity of daughter tracks"}; - Configurable PhotonLineCutZ0{"PhotonLineCutZ0", 7.0, "The offset for the linecute used in the Z vs R plot"}; - Configurable PhotonPhiMin1{"PhotonPhiMin1", -1, "Phi min value to reject photons, region 1 (leave negative if no selection desired)"}; - Configurable PhotonPhiMax1{"PhotonPhiMax1", -1, "Phi max value to reject photons, region 1 (leave negative if no selection desired)"}; - Configurable PhotonPhiMin2{"PhotonPhiMin2", -1, "Phi max value to reject photons, region 2 (leave negative if no selection desired)"}; - Configurable PhotonPhiMax2{"PhotonPhiMax2", -1, "Phi min value to reject photons, region 2 (leave negative if no selection desired)"}; + Configurable gammaMLThreshold{"gammaMLThreshold", 0.1, "Decision Threshold value to select gammas"}; + Configurable photonv0TypeSel{"photonv0TypeSel", 7, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable photonMinDCADauToPv{"photonMinDCADauToPv", 0.0, "Min DCA daughter To PV (cm)"}; + Configurable photonMaxDCAV0Dau{"photonMaxDCAV0Dau", 3.5, "Max DCA V0 Daughters (cm)"}; + Configurable photonMinTPCCrossedRows{"photonMinTPCCrossedRows", 30, "Min daughter TPC Crossed Rows"}; + Configurable photonMinTPCNSigmas{"photonMinTPCNSigmas", -7, "Min TPC NSigmas for daughters"}; + Configurable photonMaxTPCNSigmas{"photonMaxTPCNSigmas", 7, "Max TPC NSigmas for daughters"}; + Configurable photonMinPt{"photonMinPt", 0.0, "Min photon pT (GeV/c)"}; + Configurable photonMaxPt{"photonMaxPt", 50.0, "Max photon pT (GeV/c)"}; + Configurable photonMaxRap{"photonMaxRap", 0.5, "Max photon rapidity"}; + Configurable photonMinRadius{"photonMinRadius", 3.0, "Min photon conversion radius (cm)"}; + Configurable photonMaxRadius{"photonMaxRadius", 115, "Max photon conversion radius (cm)"}; + Configurable photonMaxZ{"photonMaxZ", 240, "Max photon conversion point z value (cm)"}; + Configurable photonMaxQt{"photonMaxQt", 0.05, "Max photon qt value (AP plot) (GeV/c)"}; + Configurable photonMaxAlpha{"photonMaxAlpha", 0.95, "Max photon alpha absolute value (AP plot)"}; + Configurable photonMinV0cospa{"photonMinV0cospa", 0.80, "Min V0 CosPA"}; + Configurable photonMaxMass{"photonMaxMass", 0.10, "Max photon mass (GeV/c^{2})"}; + Configurable photonPsiPairMax{"photonPsiPairMax", 1e+9, "maximum psi angle of the track pair"}; + Configurable photonMaxDauEta{"photonMaxDauEta", 0.8, "Max pseudorapidity of daughter tracks"}; + Configurable photonLineCutZ0{"photonLineCutZ0", 7.0, "The offset for the linecute used in the Z vs R plot"}; + Configurable photonPhiMin1{"photonPhiMin1", -1, "Phi min value to reject photons, region 1 (leave negative if no selection desired)"}; + Configurable photonPhiMax1{"photonPhiMax1", -1, "Phi max value to reject photons, region 1 (leave negative if no selection desired)"}; + Configurable photonPhiMin2{"photonPhiMin2", -1, "Phi max value to reject photons, region 2 (leave negative if no selection desired)"}; + Configurable photonPhiMax2{"photonPhiMax2", -1, "Phi min value to reject photons, region 2 (leave negative if no selection desired)"}; } photonSelections; struct : ConfigurableGroup { - Configurable KStarMaxRap{"KStarMaxRap", 0.5, "Max kstar rapidity"}; - Configurable KStarMaxRadius{"KStarMaxRadius", 200, "Max kstar decay radius"}; - Configurable KStarMaxDCADau{"KStarMaxDCADau", 50, "Max kstar DCA between daughters"}; - Configurable KStarMaxOPAngle{"KStarMaxOPAngle", 7, "Max kstar OP Angle between daughters"}; + Configurable kstarMaxRap{"kstarMaxRap", 0.5, "Max kstar rapidity"}; + Configurable kstarMaxRadius{"kstarMaxRadius", 200, "Max kstar decay radius"}; + Configurable kstarMaxDCADau{"kstarMaxDCADau", 50, "Max kstar DCA between daughters"}; + Configurable kstarMaxOPAngle{"kstarMaxOPAngle", 7, "Max kstar OP Angle between daughters"}; } kstarSelections; // Axis @@ -231,18 +226,18 @@ struct k892hadronphoton { ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; ConfigurableAxis axisPA{"axisPA", {100, 0.0f, 1}, "Pointing angle"}; ConfigurableAxis axisPsiPair{"axisPsiPair", {250, -5.0f, 5.0f}, "Psipair for photons"}; - ConfigurableAxis axisPhi{"axisPhi", {200, 0, 2 * o2::constants::math::PI}, "Phi for photons"}; + ConfigurableAxis axisPhi{"axisPhi", {200, 0, o2::constants::math::TwoPI}, "Phi for photons"}; ConfigurableAxis axisZ{"axisZ", {120, -120.0f, 120.0f}, "V0 Z position (cm)"}; ConfigurableAxis axisCandSel{"axisCandSel", {20, 0.5f, +20.5f}, "Candidate Selection"}; // ML - ConfigurableAxis MLProb{"MLOutput", {100, 0.0f, 1.0f}, ""}; + ConfigurableAxis mlProb{"mlProb", {100, 0.0f, 1.0f}, ""}; void init(InitContext const&) { LOGF(info, "Initializing now: cross-checking correctness..."); - if (doprocessRealData + doprocessMonteCarlo > 1) { + if (doprocessRealData && doprocessMonteCarlo) { LOGF(fatal, "You have enabled more than one process function. Please check your configuration! Aborting now."); } @@ -348,6 +343,8 @@ struct k892hadronphoton { histos.add(histodir + "/KStar/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisKStarMass}); histos.add(histodir + "/KStar/h3dOPAngleVsMass", "h3dOPAngleVsMass", kTH3D, {{140, 0.0f, +7.0f}, axisPt, axisKStarMass}); histos.add(histodir + "/KStar/h2dOPAngleVsPt", "h2dOPAngleVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); + histos.add(histodir + "/KStar/h2dOPAnglePhotonVsPt", "h2dOPAnglePhotonVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); + histos.add(histodir + "/KStar/h2dOPAngleKShortVsPt", "h2dOPAngleKShortVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); if (doprocessMonteCarlo) { @@ -379,15 +376,75 @@ struct k892hadronphoton { histos.add(histodir + "/MC/KStar/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisKStarMass}); histos.add(histodir + "/MC/KStar/h3dMCProcess", "h3dMCProcess", kTH3D, {{50, -0.5f, 49.5f}, axisPt, axisKStarMass}); histos.add(histodir + "/MC/KStar/h2dOPAngleVsPt", "h2dOPAngleVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); + histos.add(histodir + "/MC/KStar/h2dOPAngleKShortVsPt", "h2dOPAngleKShortVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); + histos.add(histodir + "/MC/KStar/h2dOPAnglePhotonVsPt", "h2dOPAnglePhotonVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); histos.add(histodir + "/MC/KStar/h2dRadiusVspT", "h2dRadiusVspT", kTH2D, {axisV0PairRadius, axisPt}); histos.add(histodir + "/MC/KStar/hDCAPairDauVsPt", "hDCAPairDauVsPt", kTH2D, {axisDCAdau, axisPt}); + // K*(892)0 -> pi K candidates + histos.add(histodir + "/PionKaon/Photon/hTrackCode", "hTrackCode", kTH1D, {{11, 0.5f, 11.5f}}); + histos.add(histodir + "/PionKaon/Photon/hV0Type", "hV0Type", kTH1D, {{8, 0.5f, 8.5f}}); + histos.add(histodir + "/PionKaon/Photon/hDCANegToPV", "hDCANegToPV", kTH1D, {axisDCAtoPV}); + histos.add(histodir + "/PionKaon/Photon/hDCAPosToPV", "hDCAPosToPV", kTH1D, {axisDCAtoPV}); + histos.add(histodir + "/PionKaon/Photon/hDCADau", "hDCADau", kTH1D, {axisDCAdau}); + histos.add(histodir + "/PionKaon/Photon/hPosTPCCR", "hPosTPCCR", kTH1D, {axisTPCrows}); + histos.add(histodir + "/PionKaon/Photon/hNegTPCCR", "hNegTPCCR", kTH1D, {axisTPCrows}); + histos.add(histodir + "/PionKaon/Photon/hPosTPCNSigmaEl", "hPosTPCNSigmaEl", kTH1D, {axisTPCNSigma}); + histos.add(histodir + "/PionKaon/Photon/hNegTPCNSigmaEl", "hNegTPCNSigmaEl", kTH1D, {axisTPCNSigma}); + histos.add(histodir + "/PionKaon/Photon/hpT", "hpT", kTH1D, {axisPt}); + histos.add(histodir + "/PionKaon/Photon/hY", "hY", kTH1D, {axisRapidity}); + histos.add(histodir + "/PionKaon/Photon/hPosEta", "hPosEta", kTH1D, {axisRapidity}); + histos.add(histodir + "/PionKaon/Photon/hNegEta", "hNegEta", kTH1D, {axisRapidity}); + histos.add(histodir + "/PionKaon/Photon/hRadius", "hRadius", kTH1D, {axisV0Radius}); + histos.add(histodir + "/PionKaon/Photon/hZ", "hZ", kTH1D, {axisZ}); + histos.add(histodir + "/PionKaon/Photon/h2dRZCut", "h2dRZCut", kTH2D, {axisZ, axisV0Radius}); + histos.add(histodir + "/PionKaon/Photon/h2dRZPlane", "h2dRZPlane", kTH2D, {axisZ, axisV0Radius}); + histos.add(histodir + "/PionKaon/Photon/hCosPA", "hCosPA", kTH1D, {axisCosPA}); + histos.add(histodir + "/PionKaon/Photon/hPsiPair", "hPsiPair", kTH1D, {axisPsiPair}); + histos.add(histodir + "/PionKaon/Photon/hPhi", "hPhi", kTH1D, {axisPhi}); + histos.add(histodir + "/PionKaon/Photon/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisPhotonMass}); + histos.add(histodir + "/PionKaon/Photon/hMass", "hMass", kTH1D, {axisPhotonMass}); + + histos.add(histodir + "/PionKaon/h2dArmenteros", "h2dArmenteros", kTH2D, {axisAPAlpha, axisAPQt}); + + histos.add(histodir + "/PionKaon/KShort/hTrackCode", "hTrackCode", kTH1D, {{11, 0.5f, 11.5f}}); + histos.add(histodir + "/PionKaon/KShort/hRadius", "hRadius", kTH1D, {axisV0Radius}); + histos.add(histodir + "/PionKaon/KShort/hDCADau", "hDCADau", kTH1D, {axisDCAdau}); + histos.add(histodir + "/PionKaon/KShort/hCosPA", "hCosPA", kTH1D, {axisCosPA}); + histos.add(histodir + "/PionKaon/KShort/hY", "hY", kTH1D, {axisRapidity}); + histos.add(histodir + "/PionKaon/KShort/hPosEta", "hPosEta", kTH1D, {axisRapidity}); + histos.add(histodir + "/PionKaon/KShort/hNegEta", "hNegEta", kTH1D, {axisRapidity}); + histos.add(histodir + "/PionKaon/KShort/hPosTPCCR", "hPosTPCCR", kTH1D, {axisTPCrows}); + histos.add(histodir + "/PionKaon/KShort/hNegTPCCR", "hNegTPCCR", kTH1D, {axisTPCrows}); + histos.add(histodir + "/PionKaon/KShort/hPosITSCls", "hPosITSCls", kTH1D, {axisNCls}); + histos.add(histodir + "/PionKaon/KShort/hNegITSCls", "hNegITSCls", kTH1D, {axisNCls}); + histos.add(histodir + "/PionKaon/KShort/hPosChi2PerNc", "hPosChi2PerNc", kTH1D, {axisChi2PerNcl}); + histos.add(histodir + "/PionKaon/KShort/hNegChi2PerNc", "hNegChi2PerNc", kTH1D, {axisChi2PerNcl}); + histos.add(histodir + "/PionKaon/KShort/hLifeTime", "hLifeTime", kTH1D, {axisLifetime}); + histos.add(histodir + "/PionKaon/KShort/h2dTPCvsTOFNSigma_KShortPi", "h2dTPCvsTOFNSigma_KShortPi", kTH2D, {axisTPCNSigma, axisTOFNSigma}); + histos.add(histodir + "/PionKaon/KShort/hKShortDCANegToPV", "hKShortDCANegToPV", kTH1D, {axisDCAtoPV}); + histos.add(histodir + "/PionKaon/KShort/hKShortDCAPosToPV", "hKShortDCAPosToPV", kTH1D, {axisDCAtoPV}); + histos.add(histodir + "/PionKaon/KShort/hKShortpT", "hKShortpT", kTH1D, {axisPt}); + histos.add(histodir + "/PionKaon/KShort/hKShortMass", "hKShortMass", kTH1D, {axisKShortMass}); + histos.add(histodir + "/PionKaon/KShort/h3dKShortMass", "h3dKShortMass", kTH3D, {axisCentrality, axisPt, axisKShortMass}); + + histos.add(histodir + "/PionKaon/KStar/hMass", "hMass", kTH1D, {axisKStarMass}); + histos.add(histodir + "/PionKaon/KStar/hPt", "hPt", kTH1D, {axisPt}); + histos.add(histodir + "/PionKaon/KStar/hY", "hY", kTH1D, {axisRapidity}); + histos.add(histodir + "/PionKaon/KStar/hRadius", "hRadius", kTH1D, {axisV0PairRadius}); + histos.add(histodir + "/PionKaon/KStar/h2dRadiusVspT", "h2dRadiusVspT", kTH2D, {axisV0PairRadius, axisPt}); + histos.add(histodir + "/PionKaon/KStar/hDCAPairDau", "hDCAPairDau", kTH1D, {axisDCAdau}); + histos.add(histodir + "/PionKaon/KStar/hDCAPairDauVsPt", "hDCAPairDauVsPt", kTH2D, {axisDCAdau, axisPt}); + histos.add(histodir + "/PionKaon/KStar/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisKStarMass}); + histos.add(histodir + "/PionKaon/KStar/h3dOPAngleVsMass", "h3dOPAngleVsMass", kTH3D, {{140, 0.0f, +7.0f}, axisPt, axisKStarMass}); + histos.add(histodir + "/PionKaon/KStar/h2dOPAngleVsPt", "h2dOPAngleVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); + histos.add(histodir + "/PionKaon/KStar/h2dOPAngleKShortVsPt", "h2dOPAngleKShortVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); + histos.add(histodir + "/PionKaon/KStar/h2dOPAnglePhotonVsPt", "h2dOPAnglePhotonVsPt", kTH2D, {{140, 0.0f, +7.0f}, axisPt}); // 1/pT Resolution: - if (fillResoQAhistos && histodir == "BeforeSel") { + if (fillResoQAhistos) { histos.add(histodir + "/MC/Reso/h2dKShortPtResolution", "h2dKShortPtResolution", kTH2D, {axisInvPt, axisDeltaPt}); - histos.add(histodir + "/MC/Reso/h3dKShortPtResoVsTPCCR", "h3dKShortPtResoVsTPCCR", kTH3D, {axisInvPt, axisDeltaPt, axisTPCrows}); - histos.add(histodir + "/MC/Reso/h3dKShortPtResoVsTPCCR", "h3dKShortPtResoVsTPCCR", kTH3D, {axisInvPt, axisDeltaPt, axisTPCrows}); + histos.add(histodir + "/MC/Reso/h2dGammaPtResolution", "h2dGammaPtResolution", kTH2D, {axisInvPt, axisDeltaPt}); histos.add(histodir + "/MC/Reso/h2dKStarPtResolution", "h2dKStarPtResolution", kTH2D, {axisInvPt, axisDeltaPt}); histos.add(histodir + "/MC/Reso/h2dKStarRadiusResolution", "h2dKStarRadiusResolution", kTH2D, {axisPt, axisDeltaPt}); } @@ -396,10 +453,18 @@ struct k892hadronphoton { if (fillBkgQAhistos) { histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_All", "h2dPtVsMassKStar_All", kTH2D, {axisPt, axisKStarMass}); histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_TrueDaughters", "h2dPtVsMassKStar_TrueDaughters", kTH2D, {axisPt, axisKStarMass}); - histos.add(histodir + "/MC/BkgStudy/h2dTrueDaughtersMatrix", "h2dTrueDaughtersMatrix", kTHnSparseD, {{10001, -5000.5f, +5000.5f}, {10001, -5000.5f, +5000.5f}}); + histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_PhotonOmega", "h2dPtVsMassKStar_PhotonOmega", kTH2D, {axisPt, axisKStarMass}); + histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_PhotonRho", "h2dPtVsMassKStar_PhotonRho", kTH2D, {axisPt, axisKStarMass}); + histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_PhotonEta", "h2dPtVsMassKStar_PhotonEta", kTH2D, {axisPt, axisKStarMass}); + histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_KShortKCharged", "h2dPtVsMassKStar_KShortKCharged", kTH2D, {axisPt, axisKStarMass}); + histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_KStarPionKaon", "h2dPtVsMassKStar_KStarPionKaon", kTH2D, {axisPt, axisKStarMass}); + histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_PionKaon", "h2dPtVsMassKStar_PionKaon", kTH2D, {axisPt, axisKStarMass}); histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_TrueGammaFakeKShort", "h2dPtVsMassKStar_TrueGammaFakeKShort", kTH2D, {axisPt, axisKStarMass}); histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_FakeGammaTrueKShort", "h2dPtVsMassKStar_FakeGammaTrueKShort", kTH2D, {axisPt, axisKStarMass}); histos.add(histodir + "/MC/BkgStudy/h2dPtVsMassKStar_FakeDaughters", "h2dPtVsMassKStar_FakeDaughters", kTH2D, {axisPt, axisKStarMass}); + histos.add(histodir + "/MC/BkgStudy/h2dTrueDaughtersMatrix", "h2dTrueDaughtersMatrix", kTHnSparseD, {{10001, -5000.5f, +5000.5f}, {10001, -5000.5f, +5000.5f}}); + histos.add(histodir + "/MC/BkgStudy/h2dPionKaonMother", "h2dPionKaonMother", kTHnSparseD, {{10001, -5000.5f, +5000.5f}, {10001, -5000.5f, +5000.5f}}); + histos.add(histodir + "/MC/BkgStudy/h2dTrueDaughtersMatrixGrandMother", "h2dTrueDaughtersMatrixGrandMother", kTHnSparseD, {{10001, -5000.5f, +5000.5f}, {10001, -5000.5f, +5000.5f}}); } } } @@ -408,16 +473,16 @@ struct k892hadronphoton { histos.add("Selection/Photon/hCandidateSel", "hCandidateSel", kTH1D, {axisCandSel}); histos.add("Selection/KShort/hCandidateSel", "hCandidateSel", kTH1D, {axisCandSel}); - for (size_t i = 0; i < PhotonSels.size(); ++i) { - const auto& sel = PhotonSels[i]; + for (size_t i = 0; i < photonSels.size(); ++i) { + const auto& sel = photonSels[i]; histos.add(Form("Selection/Photon/h2d%s", sel.c_str()), ("h2d" + sel).c_str(), kTH2D, {axisPt, axisPhotonMass}); histos.get(HIST("Selection/Photon/hCandidateSel"))->GetXaxis()->SetBinLabel(i + 1, sel.c_str()); histos.add(Form("Selection/KStar/h2dPhoton%s", sel.c_str()), ("h2dPhoton" + sel).c_str(), kTH2D, {axisPt, axisKStarMass}); } - for (size_t i = 0; i < KShortSels.size(); ++i) { - const auto& sel = KShortSels[i]; + for (size_t i = 0; i < kshortSels.size(); ++i) { + const auto& sel = kshortSels[i]; histos.add(Form("Selection/KShort/h2d%s", sel.c_str()), ("h2d" + sel).c_str(), kTH2D, {axisPt, axisKShortMass}); histos.get(HIST("Selection/KShort/hCandidateSel"))->GetXaxis()->SetBinLabel(i + 1, sel.c_str()); @@ -450,7 +515,7 @@ struct k892hadronphoton { // Check whether the collision passes our collision selections // Should work with collisions, mccollisions, stracollisions and stramccollisions tables! template - bool IsEventAccepted(TCollision const& collision, bool fillHists) + bool isEventAccepted(TCollision const& collision, bool fillHists) { if (fillHists) histos.fill(HIST("hEventSelection"), 0. /* all collisions */); @@ -575,12 +640,12 @@ struct k892hadronphoton { if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); - // Fill centrality histogram after event selection - if (fillHists) + if (fillHists) { + histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); + // Fill centrality histogram after event selection histos.fill(HIST("hEventCentrality"), centrality); + } return true; } @@ -599,7 +664,7 @@ struct k892hadronphoton { for (auto const& collision : groupedCollisions) { // consider event selections in the recoed <-> gen collision association, for the denominator (or numerator) of the efficiency (or signal loss)? if (eventSelections.useEvtSelInDenomEff) { - if (!IsEventAccepted(collision, false)) { + if (!isEventAccepted(collision, false)) { continue; } } @@ -648,7 +713,7 @@ struct k892hadronphoton { int nCollisions = 0; for (auto const& collision : groupedCollisions) { - if (!IsEventAccepted(collision, false)) { + if (!isEventAccepted(collision, false)) { continue; } @@ -672,7 +737,6 @@ struct k892hadronphoton { histos.fill(HIST("Gen/hGenEventCentrality"), centrality); } } - return; } // ______________________________________________________ @@ -683,7 +747,7 @@ struct k892hadronphoton { fillGeneratedEventProperties(mcCollisions, collisions); std::vector listBestCollisionIdx = getListOfRecoCollIndices(mcCollisions, collisions); - for (auto& genParticle : genParticles) { + for (const auto& genParticle : genParticles) { float centrality = 100.5f; // Has MC collision @@ -691,7 +755,7 @@ struct k892hadronphoton { continue; // Selection on the source (generator/transport) - if (!genParticle.producedByGenerator() && mc_keepOnlyFromGenerator) + if (!genParticle.producedByGenerator() && mcKeepOnlyFromGenerator) continue; // Select corresponding mc collision && Basic event selection @@ -731,66 +795,76 @@ struct k892hadronphoton { } } - //__________________________________________ + // per-daughter detector code (bit 0: TPC, bit 1: ITS tracker) + enum DauTrackCode : uint8_t { + DauTPCOnly = 1, // hasTPC only + DauITSTrackerOnly = 2, // hasITSTracker only, no TPC + DauITSTPC = 3 // hasTPC | hasITSTracker + }; + template int retrieveV0TrackCode(TKStarObject const& kstar) { - int TrkCode = 10; // 1: TPC-only, 2: TPC+Something, 3: ITS-Only, 4: ITS+TPC + Something, 10: anything else + int trkCode = 10; // 1: TPC-only, 2: TPC+Something, 3: ITS-Only, 4: ITS+TPC + Something, 10: anything else + auto photonPosTrackCode = kstar.photonPosTrackCode(); + auto photonNegTrackCode = kstar.photonNegTrackCode(); + auto kshortPosTrackCode = kstar.kshortPosTrackCode(); + auto kshortNegTrackCode = kstar.kshortNegTrackCode(); if (isGamma) { - if (kstar.photonPosTrackCode() == 1 && kstar.photonNegTrackCode() == 1) - TrkCode = 1; - if ((kstar.photonPosTrackCode() != 1 && kstar.photonNegTrackCode() == 1) || (kstar.photonPosTrackCode() == 1 && kstar.photonNegTrackCode() != 1)) - TrkCode = 2; - if (kstar.photonPosTrackCode() == 3 && kstar.photonNegTrackCode() == 3) - TrkCode = 3; - if (kstar.photonPosTrackCode() == 2 || kstar.photonNegTrackCode() == 2) - TrkCode = 4; + if (photonPosTrackCode == DauTPCOnly && photonNegTrackCode == DauTPCOnly) + trkCode = 1; + if ((photonPosTrackCode != DauTPCOnly && photonNegTrackCode == DauTPCOnly) || (photonPosTrackCode == DauTPCOnly && photonNegTrackCode != DauTPCOnly)) + trkCode = 2; + if (photonPosTrackCode == DauITSTPC && photonNegTrackCode == DauITSTPC) + trkCode = 3; + if (photonPosTrackCode == DauITSTrackerOnly || photonNegTrackCode == DauITSTrackerOnly) + trkCode = 4; } else { - if (kstar.kshortPosTrackCode() == 1 && kstar.kshortNegTrackCode() == 1) - TrkCode = 1; - if ((kstar.kshortPosTrackCode() != 1 && kstar.kshortNegTrackCode() == 1) || (kstar.kshortPosTrackCode() == 1 && kstar.kshortNegTrackCode() != 1)) - TrkCode = 2; - if (kstar.kshortPosTrackCode() == 3 && kstar.kshortNegTrackCode() == 3) - TrkCode = 3; - if (kstar.kshortPosTrackCode() == 2 || kstar.kshortNegTrackCode() == 2) - TrkCode = 4; + if (kshortPosTrackCode == DauTPCOnly && kshortNegTrackCode == DauTPCOnly) + trkCode = 1; + if ((kshortPosTrackCode != DauTPCOnly && kshortNegTrackCode == DauTPCOnly) || (kshortPosTrackCode == DauTPCOnly && kshortNegTrackCode != DauTPCOnly)) + trkCode = 2; + if (kshortPosTrackCode == DauITSTPC && kshortNegTrackCode == DauITSTPC) + trkCode = 3; + if (kshortPosTrackCode == DauITSTrackerOnly || kshortNegTrackCode == DauITSTrackerOnly) + trkCode = 4; } - return TrkCode; + return trkCode; } - template + template void getResolution(TKStarObject const& kstar) { + // Check whether it is before or after selections + static constexpr std::array MainDir = {"BeforeSel", "AfterSel"}; //_______________________________________ // Gamma MC association - if (kstar.photonPDGCode() == PDG_t::kGamma) { + if (std::abs(kstar.photonPDGCode()) == PDG_t::kGamma) { if (kstar.photonmcpt() > 0) { - histos.fill(HIST("BeforeSel/MC/Reso/h3dGammaPtResoVsTPCCR"), 1.f / kstar.kshortmcpt(), 1.f / kstar.kshortPt() - 1.f / kstar.kshortmcpt(), -1 * kstar.photonNegTPCCrossedRows()); // 1/pT resolution - histos.fill(HIST("BeforeSel/MC/Reso/h3dGammaPtResoVsTPCCR"), 1.f / kstar.kshortmcpt(), 1.f / kstar.kshortPt() - 1.f / kstar.kshortmcpt(), kstar.photonPosTPCCrossedRows()); // 1/pT resolution - histos.fill(HIST("BeforeSel/MC/Reso/h2dGammaPtResolution"), 1.f / kstar.photonmcpt(), 1.f / kstar.photonPt() - 1.f / kstar.photonmcpt()); // pT resolution + // histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dGammaPtResolution"), 1.f / kstar.photonmcpt(), kstar.photonPt() - kstar.photonmcpt()); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dGammaPtResolution"), 1.f / kstar.photonmcpt(), (kstar.photonPt() - kstar.photonmcpt()) / kstar.photonmcpt()); } } //_______________________________________ // KShort MC association - if (kstar.kshortPDGCode() == PDG_t::kK0Short) { + if (std::abs(kstar.kshortPDGCode()) == PDG_t::kK0Short) { if (kstar.kshortmcpt() > 0) { - histos.fill(HIST("BeforeSel/MC/Reso/h2dKShortPtResolution"), 1.f / kstar.kshortmcpt(), 1.f / kstar.kshortPt() - 1.f / kstar.kshortmcpt()); // 1/pT resolution - histos.fill(HIST("BeforeSel/MC/Reso/h3dKShortPtResoVsTPCCR"), 1.f / kstar.kshortmcpt(), 1.f / kstar.kshortPt() - 1.f / kstar.kshortmcpt(), -1 * kstar.kshortNegTPCCrossedRows()); // 1/pT resolution - histos.fill(HIST("BeforeSel/MC/Reso/h3dKShortPtResoVsTPCCR"), 1.f / kstar.kshortmcpt(), 1.f / kstar.kshortPt() - 1.f / kstar.kshortmcpt(), kstar.kshortPosTPCCrossedRows()); // 1/pT resolution + // histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dKShortPtResolution"), 1.f / kstar.kshortmcpt(), kstar.kshortPt() - kstar.kshortmcpt()); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dKShortPtResolution"), 1.f / kstar.kshortmcpt(), (kstar.kshortPt() - kstar.kshortmcpt()) / kstar.kshortmcpt()); } } //_______________________________________ // KStar MC association if (kstar.isKStar()) { - histos.fill(HIST("BeforeSel/MC/Reso/h2dKStarRadiusResolution"), kstar.mcpt(), kstar.radius() - kstar.mcradius()); // pT resolution + histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dKStarRadiusResolution"), kstar.mcpt(), kstar.radius() - kstar.mcradius()); // pT resolution if (kstar.mcpt() > 0) - histos.fill(HIST("BeforeSel/MC/Reso/h2dKStarPtResolution"), 1.f / kstar.mcpt(), 1.f / kstar.pt() - 1.f / kstar.mcpt()); // pT resolution + histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dKStarPtResolution"), 1.f / kstar.mcpt(), (1.f / kstar.pt() - 1.f / kstar.mcpt()) / (1.f / kstar.mcpt())); // pT resolution } } @@ -799,38 +873,66 @@ struct k892hadronphoton { void runBkgAnalysis(TKStarObject const& kstar) { // Check whether it is before or after selections - static constexpr std::string_view MainDir[] = {"BeforeSel", "AfterSel"}; + static constexpr std::array MainDir = {"BeforeSel", "AfterSel"}; bool fIsKStar = kstar.isKStar(); - int PhotonPDGCode = kstar.photonPDGCode(); - int PhotonPDGCodeMother = kstar.photonPDGCodeMother(); - int KShortPDGCode = kstar.kshortPDGCode(); - int KShortPDGCodeMother = kstar.kshortPDGCodeMother(); + int photonPDGCode = kstar.photonPDGCode(); + int photonPDGCodeMother = kstar.photonPDGCodeMother(); + int photonPDGCodeGrandMother = kstar.photonPDGCodeGrandMother(); + int photonGlobalIndexGrandMother = kstar.photonGlobalIndexGrandMother(); + int kshortPDGCode = kstar.kshortPDGCode(); + int kshortPDGCodeMother = kstar.kshortPDGCodeMother(); + int kshortPDGCodeGrandMother = kstar.kshortPDGCodeGrandMother(); + int kshortGlobalIndexGrandMother = kstar.kshortGlobalIndexGrandMother(); float kstarpT = kstar.pt(); float kstarMass = kstar.kstarMass(); histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_All"), kstarpT, kstarMass); + // coming from the same kstar (coupled), but via a different decay channel (K* -> K0 pi0 -> K0s gamma) //_______________________________________ - // Real Gamma x Real KShort - but not from the same kstar! - if ((PhotonPDGCode == PDG_t::kGamma) && (KShortPDGCode == PDG_t::kK0Short) && (!fIsKStar)) { + // Real Gamma x Real KShort - but not direct decays from the same kstar! + if ((!fIsKStar) && (std::abs(photonPDGCode) == PDG_t::kGamma) && (std::abs(kshortPDGCode) == PDG_t::kK0Short)) { histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_TrueDaughters"), kstarpT, kstarMass); - histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dTrueDaughtersMatrix"), KShortPDGCodeMother, PhotonPDGCodeMother); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dTrueDaughtersMatrix"), kshortPDGCodeMother, photonPDGCodeMother); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dTrueDaughtersMatrixGrandMother"), kshortPDGCodeGrandMother, photonPDGCodeGrandMother); + + // coming from the same kstar (coupled), but via a different decay channel (K* -> K0 pi0 -> K0s gamma) + if (std::abs(kshortPDGCodeGrandMother) == o2::constants::physics::Pdg::kK0Star892 && std::abs(photonPDGCodeGrandMother) == o2::constants::physics::Pdg::kK0Star892 && (photonGlobalIndexGrandMother == kshortGlobalIndexGrandMother)) // K*(892)0 + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_KStarPionKaon"), kstarpT, kstarMass); + + if ((photonGlobalIndexGrandMother == kshortGlobalIndexGrandMother) && (photonPDGCodeGrandMother != o2::constants::physics::Pdg::kK0Star892) && (photonPDGCodeGrandMother != PDG_t::kProton)) { + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_PionKaon"), kstarpT, kstarMass); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPionKaonMother"), kshortPDGCodeGrandMother, photonPDGCodeGrandMother); + } + + // Break down of different photon / kshort sources + if (std::abs(photonPDGCodeGrandMother) == o2::constants::physics::Pdg::kOmega) // omega(782) + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_PhotonOmega"), kstarpT, kstarMass); + + if (std::abs(photonPDGCodeGrandMother) == PDG_t::kRho770Plus) // rho+-(770) + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_PhotonRho"), kstarpT, kstarMass); + + if (std::abs(photonPDGCodeMother) == o2::constants::physics::Pdg::kEta) // eta + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_PhotonEta"), kstarpT, kstarMass); + + if (std::abs(kshortPDGCodeGrandMother) == o2::constants::physics::Pdg::kKPlusStar892) // K*(892)+- + histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_KShortKCharged"), kstarpT, kstarMass); } //_______________________________________ // Real Gamma x fake KShort - if ((PhotonPDGCode == PDG_t::kGamma) && (KShortPDGCode != PDG_t::kK0Short)) + if ((std::abs(photonPDGCode) == PDG_t::kGamma) && (std::abs(kshortPDGCode) != PDG_t::kK0Short)) histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_TrueGammaFakeKShort"), kstarpT, kstarMass); //_______________________________________ // Fake Gamma x Real KShort - if ((PhotonPDGCode != PDG_t::kGamma) && ((KShortPDGCode == PDG_t::kK0Short))) + if ((std::abs(photonPDGCode) != PDG_t::kGamma) && ((std::abs(kshortPDGCode) == PDG_t::kK0Short))) histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_FakeGammaTrueKShort"), kstarpT, kstarMass); //_______________________________________ // Fake Gamma x Fake KShort - if ((PhotonPDGCode != PDG_t::kGamma) && (KShortPDGCode != PDG_t::kK0Short)) + if ((std::abs(photonPDGCode) != PDG_t::kGamma) && (std::abs(kshortPDGCode) != PDG_t::kK0Short)) histos.fill(HIST(MainDir[mode]) + HIST("/MC/BkgStudy/h2dPtVsMassKStar_FakeDaughters"), kstarpT, kstarMass); } @@ -839,17 +941,17 @@ struct k892hadronphoton { { // Check whether it is before or after selections - static constexpr std::string_view MainDir[] = {"BeforeSel", "AfterSel"}; + static constexpr std::array MainDir = {"BeforeSel", "AfterSel"}; // Get V0trackCode - int GammaTrkCode = retrieveV0TrackCode(kstar); - int KShortTrkCode = retrieveV0TrackCode(kstar); + int gammaTrkCode = retrieveV0TrackCode(kstar); + int kshortTrkCode = retrieveV0TrackCode(kstar); - float photonRZLineCut = TMath::Abs(kstar.photonZconv()) * TMath::Tan(2 * TMath::ATan(TMath::Exp(-photonSelections.PhotonMaxDauEta))) - photonSelections.PhotonLineCutZ0; + float photonRZLineCut = std::abs(kstar.photonZconv()) * std::tan(2 * std::atan(std::exp(-photonSelections.photonMaxDauEta))) - photonSelections.photonLineCutZ0; float centrality = doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); //_______________________________________ // Photon - histos.fill(HIST(MainDir[mode]) + HIST("/Photon/hTrackCode"), GammaTrkCode); + histos.fill(HIST(MainDir[mode]) + HIST("/Photon/hTrackCode"), gammaTrkCode); histos.fill(HIST(MainDir[mode]) + HIST("/Photon/hV0Type"), kstar.photonV0Type()); histos.fill(HIST(MainDir[mode]) + HIST("/Photon/hDCANegToPV"), kstar.photonDCANegPV()); @@ -875,7 +977,7 @@ struct k892hadronphoton { //_______________________________________ // KShorts - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hTrackCode"), KShortTrkCode); + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hTrackCode"), kshortTrkCode); histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hRadius"), kstar.kshortRadius()); histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hDCADau"), kstar.kshortDCADau()); histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hCosPA"), kstar.kshortCosPA()); @@ -895,26 +997,27 @@ struct k892hadronphoton { histos.fill(HIST(MainDir[mode]) + HIST("/h2dArmenteros"), kstar.photonAlpha(), kstar.photonQt()); histos.fill(HIST(MainDir[mode]) + HIST("/h2dArmenteros"), kstar.kshortAlpha(), kstar.kshortQt()); - if (kstar.kshortAlpha() < 1) { - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/h2dTPCvsTOFNSigma_KShortPi"), kstar.kshortPosPiTPCNSigma(), kstar.kshortPiTOFNSigma()); - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/h2dTPCvsTOFNSigma_KShortPi"), kstar.kshortNegPiTPCNSigma(), kstar.kshortPiTOFNSigma()); - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortDCANegToPV"), kstar.kshortDCANegPV()); - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortDCAPosToPV"), kstar.kshortDCAPosPV()); - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortpT"), kstar.kshortPt()); - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortMass"), kstar.kshortMass()); - histos.fill(HIST(MainDir[mode]) + HIST("/KShort/h3dKShortMass"), centrality, kstar.kshortPt(), kstar.kshortMass()); - - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hMass"), kstar.kstarMass()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hPt"), kstar.pt()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hY"), kstar.kstarY()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hRadius"), kstar.radius()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h2dRadiusVspT"), kstar.radius(), kstar.pt()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hDCAPairDau"), kstar.dcadaughters()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hDCAPairDauVsPt"), kstar.dcadaughters(), kstar.pt()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h3dMass"), centrality, kstar.pt(), kstar.kstarMass()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h3dOPAngleVsMass"), kstar.opAngle(), kstar.pt(), kstar.kstarMass()); - histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h2dOPAngleVsPt"), kstar.opAngle(), kstar.pt()); - } + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/h2dTPCvsTOFNSigma_KShortPi"), kstar.kshortPosPiTPCNSigma(), kstar.kshortPiTOFNSigma()); + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/h2dTPCvsTOFNSigma_KShortPi"), kstar.kshortNegPiTPCNSigma(), kstar.kshortPiTOFNSigma()); + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortDCANegToPV"), kstar.kshortDCANegPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortDCAPosToPV"), kstar.kshortDCAPosPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortpT"), kstar.kshortPt()); + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/hKShortMass"), kstar.kshortMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/KShort/h3dKShortMass"), centrality, kstar.kshortPt(), kstar.kshortMass()); + + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hMass"), kstar.kstarMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hPt"), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hY"), kstar.kstarY()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hRadius"), kstar.radius()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h2dRadiusVspT"), kstar.radius(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hDCAPairDau"), kstar.dcadaughters()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/hDCAPairDauVsPt"), kstar.dcadaughters(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h3dMass"), centrality, kstar.pt(), kstar.kstarMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h3dOPAngleVsMass"), kstar.opAngle(), kstar.pt(), kstar.kstarMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h2dOPAngleVsPt"), kstar.opAngle(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h2dOPAngleKShortVsPt"), kstar.opAngleKShortKStar(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/KStar/h2dOPAnglePhotonVsPt"), kstar.opAnglePhotonKStar(), kstar.pt()); + //_______________________________________ // MC specific if (doprocessMonteCarlo) { @@ -929,11 +1032,11 @@ struct k892hadronphoton { histos.fill(HIST(MainDir[mode]) + HIST("/MC/Photon/hPosTPCNSigmaEl"), kstar.photonPosTPCNSigmaEl()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/Photon/hNegTPCNSigmaEl"), kstar.photonNegTPCNSigmaEl()); - histos.fill(HIST(MainDir[mode]) + HIST("/MC/Photon/h2dPAVsPt"), TMath::ACos(kstar.photonCosPA()), kstar.photonmcpt()); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/Photon/h2dPAVsPt"), std::acos(kstar.photonCosPA()), kstar.photonmcpt()); if (!kstar.photonIsCorrectlyAssoc()) { histos.fill(HIST(MainDir[mode]) + HIST("/MC/Photon/hPt_BadCollAssig"), kstar.photonmcpt()); - histos.fill(HIST(MainDir[mode]) + HIST("/MC/Photon/h2dPAVsPt_BadCollAssig"), TMath::ACos(kstar.photonCosPA()), kstar.photonmcpt()); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/Photon/h2dPAVsPt_BadCollAssig"), std::acos(kstar.photonCosPA()), kstar.photonmcpt()); } } @@ -971,6 +1074,74 @@ struct k892hadronphoton { histos.fill(HIST(MainDir[mode]) + HIST("/MC/KStar/h2dOPAngleVsPt"), kstar.opAngle(), kstar.pt()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/KStar/h2dRadiusVspT"), kstar.radius(), kstar.pt()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/KStar/hDCAPairDauVsPt"), kstar.dcadaughters(), kstar.pt()); + + histos.fill(HIST(MainDir[mode]) + HIST("/MC/KStar/h2dOPAngleKShortVsPt"), kstar.opAngleKShortKStar(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/MC/KStar/h2dOPAnglePhotonVsPt"), kstar.opAnglePhotonKStar(), kstar.pt()); + } + + if ((kstar.kshortPDGCode() == PDG_t::kK0Short) && (kstar.photonPDGCode() == PDG_t::kGamma) && (std::abs(kstar.kshortPDGCodeGrandMother()) == o2::constants::physics::Pdg::kK0Star892 && std::abs(kstar.photonPDGCodeGrandMother()) == o2::constants::physics::Pdg::kK0Star892 && (kstar.photonGlobalIndexGrandMother() == kstar.kshortGlobalIndexGrandMother()))) { // K*(892)0 + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hTrackCode"), gammaTrkCode); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hV0Type"), kstar.photonV0Type()); + + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hDCANegToPV"), kstar.photonDCANegPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hDCAPosToPV"), kstar.photonDCAPosPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hDCADau"), kstar.photonDCADau()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hPosTPCCR"), kstar.photonPosTPCCrossedRows()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hNegTPCCR"), kstar.photonNegTPCCrossedRows()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hPosTPCNSigmaEl"), kstar.photonPosTPCNSigmaEl()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hNegTPCNSigmaEl"), kstar.photonNegTPCNSigmaEl()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hpT"), kstar.photonPt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hY"), kstar.photonY()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hPosEta"), kstar.photonPosEta()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hNegEta"), kstar.photonNegEta()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hRadius"), kstar.photonRadius()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hZ"), kstar.photonZconv()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/h2dRZCut"), kstar.photonRadius(), photonRZLineCut); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/h2dRZPlane"), kstar.photonZconv(), kstar.photonRadius()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hCosPA"), kstar.photonCosPA()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hPsiPair"), kstar.photonPsiPair()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hPhi"), kstar.photonPhi()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/h3dMass"), centrality, kstar.photonPt(), kstar.photonMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/Photon/hMass"), kstar.photonMass()); + + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hTrackCode"), kshortTrkCode); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hRadius"), kstar.kshortRadius()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hDCADau"), kstar.kshortDCADau()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hCosPA"), kstar.kshortCosPA()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hY"), kstar.kshortY()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hPosEta"), kstar.kshortPosEta()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hNegEta"), kstar.kshortNegEta()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hPosTPCCR"), kstar.kshortPosTPCCrossedRows()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hNegTPCCR"), kstar.kshortNegTPCCrossedRows()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hPosITSCls"), kstar.kshortPosITSCls()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hNegITSCls"), kstar.kshortNegITSCls()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hPosChi2PerNc"), kstar.kshortPosChi2PerNcl()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hNegChi2PerNc"), kstar.kshortNegChi2PerNcl()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hLifeTime"), kstar.kshortLifeTime()); + + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/h2dArmenteros"), kstar.photonAlpha(), kstar.photonQt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/h2dArmenteros"), kstar.kshortAlpha(), kstar.kshortQt()); + + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/h2dTPCvsTOFNSigma_KShortPi"), kstar.kshortPosPiTPCNSigma(), kstar.kshortPiTOFNSigma()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/h2dTPCvsTOFNSigma_KShortPi"), kstar.kshortNegPiTPCNSigma(), kstar.kshortPiTOFNSigma()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hKShortDCANegToPV"), kstar.kshortDCANegPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hKShortDCAPosToPV"), kstar.kshortDCAPosPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hKShortpT"), kstar.kshortPt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/hKShortMass"), kstar.kshortMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KShort/h3dKShortMass"), centrality, kstar.kshortPt(), kstar.kshortMass()); + + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/hMass"), kstar.kstarMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/hPt"), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/hY"), kstar.kstarY()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/hRadius"), kstar.radius()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/h2dRadiusVspT"), kstar.radius(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/hDCAPairDau"), kstar.dcadaughters()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/hDCAPairDauVsPt"), kstar.dcadaughters(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/h3dMass"), centrality, kstar.pt(), kstar.kstarMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/h3dOPAngleVsMass"), kstar.opAngle(), kstar.pt(), kstar.kstarMass()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/h2dOPAngleVsPt"), kstar.opAngle(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/h2dOPAngleKShortVsPt"), kstar.opAngleKShortKStar(), kstar.pt()); + histos.fill(HIST(MainDir[mode]) + HIST("/PionKaon/KStar/h2dOPAnglePhotonVsPt"), kstar.opAnglePhotonKStar(), kstar.pt()); } // For background studies: @@ -979,8 +1150,8 @@ struct k892hadronphoton { //_______________________________________ // pT resolution histos - if ((mode == 0) && fillResoQAhistos) - getResolution(kstar); + if (fillResoQAhistos) + getResolution(kstar); } } } @@ -989,28 +1160,28 @@ struct k892hadronphoton { void fillSelHistos(TKStarObject const& kstar, int PDGRequired) { - static constexpr std::string_view PhotonSelsLocal[] = {"NoSel", "V0Type", "DCADauToPV", - "DCADau", "DauTPCCR", "TPCNSigmaEl", "V0pT", - "Y", "V0Radius", "RZCut", "Armenteros", "CosPA", - "PsiPair", "Phi", "Mass"}; + static constexpr std::array photonSelsLocal = {"NoSel", "V0Type", "DCADauToPV", + "DCADau", "DauTPCCR", "TPCNSigmaEl", "V0pT", + "Y", "V0Radius", "RZCut", "Armenteros", "CosPA", + "PsiPair", "Phi", "Mass"}; - static constexpr std::string_view KShortSelsLocal[] = {"NoSel", "V0Radius", "DCADau", "Armenteros", - "CosPA", "Y", "TPCCR", "DauITSCls", "Lifetime", - "TPCTOFPID", "DCADauToPV", "Mass"}; + static constexpr std::array kshortSelsLocal = {"NoSel", "V0Radius", "DCADau", "Armenteros", + "CosPA", "Y", "TPCCR", "DauITSCls", "Lifetime", + "TPCTOFPID", "DCADauToPV", "Mass"}; - if (PDGRequired == PDG_t::kGamma) { - if constexpr (selection_index >= 0 && selection_index < (int)std::size(PhotonSelsLocal)) { + if (std::abs(PDGRequired) == PDG_t::kGamma) { + if constexpr (selection_index >= 0 && selection_index < static_cast(std::size(photonSelsLocal))) { histos.fill(HIST("Selection/Photon/hCandidateSel"), selection_index); - histos.fill(HIST("Selection/Photon/h2d") + HIST(PhotonSelsLocal[selection_index]), kstar.photonPt(), kstar.photonMass()); - histos.fill(HIST("Selection/KStar/h2dPhoton") + HIST(PhotonSelsLocal[selection_index]), kstar.pt(), kstar.kstarMass()); + histos.fill(HIST("Selection/Photon/h2d") + HIST(photonSelsLocal[selection_index]), kstar.photonPt(), kstar.photonMass()); + histos.fill(HIST("Selection/KStar/h2dPhoton") + HIST(photonSelsLocal[selection_index]), kstar.pt(), kstar.kstarMass()); } } - if (PDGRequired == PDG_t::kK0Short) { - if constexpr (selection_index >= 0 && selection_index < (int)std::size(KShortSelsLocal)) { + if (std::abs(PDGRequired) == PDG_t::kK0Short) { + if constexpr (selection_index >= 0 && selection_index < static_cast(std::size(kshortSelsLocal))) { histos.fill(HIST("Selection/KShort/hCandidateSel"), selection_index); - histos.fill(HIST("Selection/KShort/h2d") + HIST(KShortSelsLocal[selection_index]), kstar.kshortPt(), kstar.kshortMass()); - histos.fill(HIST("Selection/KStar/h2dKShort") + HIST(KShortSelsLocal[selection_index]), kstar.pt(), kstar.kstarMass()); + histos.fill(HIST("Selection/KShort/h2d") + HIST(kshortSelsLocal[selection_index]), kstar.kshortPt(), kstar.kshortMass()); + histos.fill(HIST("Selection/KStar/h2dKShort") + HIST(kshortSelsLocal[selection_index]), kstar.pt(), kstar.kstarMass()); } } } @@ -1020,66 +1191,66 @@ struct k892hadronphoton { bool selectPhoton(TV0Object const& cand) { fillSelHistos<0>(cand, PDG_t::kGamma); - if (cand.photonV0Type() != photonSelections.Photonv0TypeSel && photonSelections.Photonv0TypeSel > -1) + if (cand.photonV0Type() != photonSelections.photonv0TypeSel && photonSelections.photonv0TypeSel > -1) return false; fillSelHistos<1>(cand, PDG_t::kGamma); - if ((TMath::Abs(cand.photonDCAPosPV()) < photonSelections.PhotonMinDCADauToPv) || (TMath::Abs(cand.photonDCANegPV()) < photonSelections.PhotonMinDCADauToPv)) + if ((std::abs(cand.photonDCAPosPV()) < photonSelections.photonMinDCADauToPv) || (std::abs(cand.photonDCANegPV()) < photonSelections.photonMinDCADauToPv)) return false; fillSelHistos<2>(cand, PDG_t::kGamma); - if (TMath::Abs(cand.photonDCADau()) > photonSelections.PhotonMaxDCAV0Dau) + if (std::abs(cand.photonDCADau()) > photonSelections.photonMaxDCAV0Dau) return false; fillSelHistos<3>(cand, PDG_t::kGamma); - if ((cand.photonPosTPCCrossedRows() < photonSelections.PhotonMinTPCCrossedRows) || (cand.photonNegTPCCrossedRows() < photonSelections.PhotonMinTPCCrossedRows)) + if ((cand.photonPosTPCCrossedRows() < photonSelections.photonMinTPCCrossedRows) || (cand.photonNegTPCCrossedRows() < photonSelections.photonMinTPCCrossedRows)) return false; fillSelHistos<4>(cand, PDG_t::kGamma); - if (((cand.photonPosTPCNSigmaEl() < photonSelections.PhotonMinTPCNSigmas) || (cand.photonPosTPCNSigmaEl() > photonSelections.PhotonMaxTPCNSigmas))) + if (((cand.photonPosTPCNSigmaEl() < photonSelections.photonMinTPCNSigmas) || (cand.photonPosTPCNSigmaEl() > photonSelections.photonMaxTPCNSigmas))) return false; - if (((cand.photonNegTPCNSigmaEl() < photonSelections.PhotonMinTPCNSigmas) || (cand.photonNegTPCNSigmaEl() > photonSelections.PhotonMaxTPCNSigmas))) + if (((cand.photonNegTPCNSigmaEl() < photonSelections.photonMinTPCNSigmas) || (cand.photonNegTPCNSigmaEl() > photonSelections.photonMaxTPCNSigmas))) return false; fillSelHistos<5>(cand, PDG_t::kGamma); - if ((cand.photonPt() < photonSelections.PhotonMinPt) || (cand.photonPt() > photonSelections.PhotonMaxPt)) + if ((cand.photonPt() < photonSelections.photonMinPt) || (cand.photonPt() > photonSelections.photonMaxPt)) return false; fillSelHistos<6>(cand, PDG_t::kGamma); - if ((TMath::Abs(cand.photonY()) > photonSelections.PhotonMaxRap) || (TMath::Abs(cand.photonPosEta()) > photonSelections.PhotonMaxDauEta) || (TMath::Abs(cand.photonNegEta()) > photonSelections.PhotonMaxDauEta)) + if ((std::abs(cand.photonY()) > photonSelections.photonMaxRap) || (std::abs(cand.photonPosEta()) > photonSelections.photonMaxDauEta) || (std::abs(cand.photonNegEta()) > photonSelections.photonMaxDauEta)) return false; fillSelHistos<7>(cand, PDG_t::kGamma); - if ((cand.photonRadius() < photonSelections.PhotonMinRadius) || (cand.photonRadius() > photonSelections.PhotonMaxRadius)) + if ((cand.photonRadius() < photonSelections.photonMinRadius) || (cand.photonRadius() > photonSelections.photonMaxRadius)) return false; fillSelHistos<8>(cand, PDG_t::kGamma); - float photonRZLineCut = TMath::Abs(cand.photonZconv()) * TMath::Tan(2 * TMath::ATan(TMath::Exp(-photonSelections.PhotonMaxDauEta))) - photonSelections.PhotonLineCutZ0; - if ((TMath::Abs(cand.photonRadius()) < photonRZLineCut) || (TMath::Abs(cand.photonZconv()) > photonSelections.PhotonMaxZ)) + float photonRZLineCut = std::abs(cand.photonZconv()) * std::tan(2 * std::atan(std::exp(-photonSelections.photonMaxDauEta))) - photonSelections.photonLineCutZ0; + if ((std::abs(cand.photonRadius()) < photonRZLineCut) || (std::abs(cand.photonZconv()) > photonSelections.photonMaxZ)) return false; fillSelHistos<9>(cand, PDG_t::kGamma); - if (cand.photonQt() > photonSelections.PhotonMaxQt) + if (cand.photonQt() > photonSelections.photonMaxQt) return false; - if (TMath::Abs(cand.photonAlpha()) > photonSelections.PhotonMaxAlpha) + if (std::abs(cand.photonAlpha()) > photonSelections.photonMaxAlpha) return false; fillSelHistos<10>(cand, PDG_t::kGamma); - if (cand.photonCosPA() < photonSelections.PhotonMinV0cospa) + if (cand.photonCosPA() < photonSelections.photonMinV0cospa) return false; fillSelHistos<11>(cand, PDG_t::kGamma); - if (TMath::Abs(cand.photonPsiPair()) > photonSelections.PhotonPsiPairMax) + if (std::abs(cand.photonPsiPair()) > photonSelections.photonPsiPairMax) return false; fillSelHistos<12>(cand, PDG_t::kGamma); - if ((((cand.photonPhi() > photonSelections.PhotonPhiMin1) && (cand.photonPhi() < photonSelections.PhotonPhiMax1)) || ((cand.photonPhi() > photonSelections.PhotonPhiMin2) && (cand.photonPhi() < photonSelections.PhotonPhiMax2))) && ((photonSelections.PhotonPhiMin1 != -1) && (photonSelections.PhotonPhiMax1 != -1) && (photonSelections.PhotonPhiMin2 != -1) && (photonSelections.PhotonPhiMax2 != -1))) + if ((((cand.photonPhi() > photonSelections.photonPhiMin1) && (cand.photonPhi() < photonSelections.photonPhiMax1)) || ((cand.photonPhi() > photonSelections.photonPhiMin2) && (cand.photonPhi() < photonSelections.photonPhiMax2))) && ((photonSelections.photonPhiMin1 != -1) && (photonSelections.photonPhiMax1 != -1) && (photonSelections.photonPhiMin2 != -1) && (photonSelections.photonPhiMax2 != -1))) return false; fillSelHistos<13>(cand, PDG_t::kGamma); - if (TMath::Abs(cand.photonMass()) > photonSelections.PhotonMaxMass) + if (std::abs(cand.photonMass()) > photonSelections.photonMaxMass) return false; fillSelHistos<14>(cand, PDG_t::kGamma); @@ -1091,30 +1262,30 @@ struct k892hadronphoton { bool selectKShort(TV0Object const& cand) { fillSelHistos<0>(cand, PDG_t::kK0Short); - if ((cand.kshortRadius() < kshortSelections.KShortMinv0radius) || (cand.kshortRadius() > kshortSelections.KShortMaxv0radius)) + if ((cand.kshortRadius() < kshortSelections.kshortMinv0radius) || (cand.kshortRadius() > kshortSelections.kshortMaxv0radius)) return false; fillSelHistos<1>(cand, PDG_t::kK0Short); - if (TMath::Abs(cand.kshortDCADau()) > kshortSelections.KShortMaxDCAV0Dau) + if (std::abs(cand.kshortDCADau()) > kshortSelections.kshortMaxDCAV0Dau) return false; fillSelHistos<2>(cand, PDG_t::kK0Short); - if ((cand.kshortQt() < kshortSelections.KShortMinQt) || (cand.kshortQt() > kshortSelections.KShortMaxQt)) + if ((cand.kshortQt() < kshortSelections.kshortMinQt) || (cand.kshortQt() > kshortSelections.kshortMaxQt)) return false; - if ((TMath::Abs(cand.kshortAlpha()) < kshortSelections.KShortMinAlpha) || (TMath::Abs(cand.kshortAlpha()) > kshortSelections.KShortMaxAlpha)) + if ((std::abs(cand.kshortAlpha()) < kshortSelections.kshortMinAlpha) || (std::abs(cand.kshortAlpha()) > kshortSelections.kshortMaxAlpha)) return false; fillSelHistos<3>(cand, PDG_t::kK0Short); - if (cand.kshortCosPA() < kshortSelections.KShortMinv0cospa) + if (cand.kshortCosPA() < kshortSelections.kshortMinv0cospa) return false; fillSelHistos<4>(cand, PDG_t::kK0Short); - if ((TMath::Abs(cand.kshortY()) > kshortSelections.KShortMaxRap) || (TMath::Abs(cand.kshortPosEta()) > kshortSelections.KShortMaxDauEta) || (TMath::Abs(cand.kshortNegEta()) > kshortSelections.KShortMaxDauEta)) + if ((std::abs(cand.kshortY()) > kshortSelections.kshortMaxRap) || (std::abs(cand.kshortPosEta()) > kshortSelections.kshortMaxDauEta) || (std::abs(cand.kshortNegEta()) > kshortSelections.kshortMaxDauEta)) return false; fillSelHistos<5>(cand, PDG_t::kK0Short); - if ((cand.kshortPosTPCCrossedRows() < kshortSelections.KShortMinTPCCrossedRows) || (cand.kshortNegTPCCrossedRows() < kshortSelections.KShortMinTPCCrossedRows)) + if ((cand.kshortPosTPCCrossedRows() < kshortSelections.kshortMinTPCCrossedRows) || (cand.kshortNegTPCCrossedRows() < kshortSelections.kshortMinTPCCrossedRows)) return false; fillSelHistos<6>(cand, PDG_t::kK0Short); @@ -1122,42 +1293,41 @@ struct k892hadronphoton { // check minimum number of ITS clusters + reject ITS afterburner tracks if requested bool posIsFromAfterburner = cand.kshortPosChi2PerNcl() < 0; bool negIsFromAfterburner = cand.kshortNegChi2PerNcl() < 0; - if (cand.kshortPosITSCls() < kshortSelections.KShortMinITSclusters && (!kshortSelections.KShortRejectPosITSafterburner || posIsFromAfterburner)) + if (cand.kshortPosITSCls() < kshortSelections.kshortMinITSclusters && (!kshortSelections.kshortRejectPosITSafterburner || posIsFromAfterburner)) return false; - if (cand.kshortNegITSCls() < kshortSelections.KShortMinITSclusters && (!kshortSelections.KShortRejectNegITSafterburner || negIsFromAfterburner)) + if (cand.kshortNegITSCls() < kshortSelections.kshortMinITSclusters && (!kshortSelections.kshortRejectNegITSafterburner || negIsFromAfterburner)) return false; fillSelHistos<7>(cand, PDG_t::kK0Short); - if (cand.kshortLifeTime() > kshortSelections.KShortMaxLifeTime) + if (cand.kshortLifeTime() > kshortSelections.kshortMaxLifeTime) return false; // Separating kshort selections: fillSelHistos<8>(cand, PDG_t::kK0Short); - if (cand.kshortAlpha() < 1) { // KShort selection - // TPC Selection - if (kshortSelections.fselKShortTPCPID && (TMath::Abs(cand.kshortPosPiTPCNSigma()) > kshortSelections.KShortMaxTPCNSigmas)) - return false; - if (kshortSelections.fselKShortTPCPID && (TMath::Abs(cand.kshortNegPiTPCNSigma()) > kshortSelections.KShortMaxTPCNSigmas)) - return false; - // // TOF Selection - // if (kshortSelections.fselKShortTOFPID && (TMath::Abs(cand.kshortPiTOFNSigma()) > kshortSelections.KShortPiMaxTOFNSigmas)) - // return false; - // if (kshortSelections.fselKShortTOFPID && (TMath::Abs(cand.lambdaPiTOFNSigma()) > kshortSelections.KShortPiMaxTOFNSigmas)) - // return false; + // TPC Selection + if (kshortSelections.fselKShortTPCPID && (std::abs(cand.kshortPosPiTPCNSigma()) > kshortSelections.kshortMaxTPCNSigmas)) + return false; + if (kshortSelections.fselKShortTPCPID && (std::abs(cand.kshortNegPiTPCNSigma()) > kshortSelections.kshortMaxTPCNSigmas)) + return false; - // DCA Selection - fillSelHistos<9>(cand, PDG_t::kK0Short); - if ((TMath::Abs(cand.kshortDCAPosPV()) < kshortSelections.KShortMinDCAPosToPv) || (TMath::Abs(cand.kshortDCANegPV()) < kshortSelections.KShortMinDCANegToPv)) - return false; + // // TOF Selection + // if (kshortSelections.fselKShortTOFPID && (std::abs(cand.kshortPiTOFNSigma()) > kshortSelections.kshortPiMaxTOFNSigmas)) + // return false; + // if (kshortSelections.fselKShortTOFPID && (std::abs(cand.lambdaPiTOFNSigma()) > kshortSelections.kshortPiMaxTOFNSigmas)) + // return false; + // DCA Selection + fillSelHistos<9>(cand, PDG_t::kK0Short); + if ((std::abs(cand.kshortDCAPosPV()) < kshortSelections.kshortMinDCAPosToPv) || (std::abs(cand.kshortDCANegPV()) < kshortSelections.kshortMinDCANegToPv)) + return false; - // Mass Selection - fillSelHistos<10>(cand, PDG_t::kK0Short); - if (TMath::Abs(cand.kshortMass() - o2::constants::physics::MassK0Short) > kshortSelections.KShortWindow) - return false; + // Mass Selection + fillSelHistos<10>(cand, PDG_t::kK0Short); + if (std::abs(cand.kshortMass() - o2::constants::physics::MassK0Short) > kshortSelections.kshortWindow) + return false; + + fillSelHistos<11>(cand, PDG_t::kK0Short); - fillSelHistos<11>(cand, PDG_t::kK0Short); - } return true; } @@ -1176,23 +1346,23 @@ struct k892hadronphoton { // KStar specific selections // Rapidity if constexpr (requires { cand.kstarMCY(); }) { // MC - if (TMath::Abs(cand.kstarMCY()) > kstarSelections.KStarMaxRap) + if (std::abs(cand.kstarMCY()) > kstarSelections.kstarMaxRap) return false; } else { // Real data - if (TMath::Abs(cand.kstarY()) > kstarSelections.KStarMaxRap) + if (std::abs(cand.kstarY()) > kstarSelections.kstarMaxRap) return false; } // V0Pair Radius - if (cand.radius() > kstarSelections.KStarMaxRadius) + if (cand.radius() > kstarSelections.kstarMaxRadius) return false; // DCA V0Pair Daughters - if (cand.dcadaughters() > kstarSelections.KStarMaxDCADau) + if (cand.dcadaughters() > kstarSelections.kstarMaxDCADau) return false; // Opening Angle - if (cand.opAngle() > kstarSelections.KStarMaxOPAngle) + if (cand.opAngle() > kstarSelections.kstarMaxOPAngle) return false; return true; @@ -1213,7 +1383,7 @@ struct k892hadronphoton { for (const auto& coll : collisions) { // Event selection - if (!IsEventAccepted(coll, true)) + if (!isEventAccepted(coll, true)) continue; // KStars loop @@ -1242,18 +1412,18 @@ struct k892hadronphoton { } } - void processRealData(soa::Join const& collisions, KStars const& fullKStars) + void processRealData(soa::Join const& collisions, KStars const& fullKStars) { analyzeRecoeKStars(collisions, fullKStars); } - void processMonteCarlo(soa::Join const& collisions, MCKStars const& fullKStars) + void processMonteCarlo(soa::Join const& collisions, MCKStars const& fullKStars) { analyzeRecoeKStars(collisions, fullKStars); } // Simulated processing in Run 3 - void processGeneratedRun3(soa::Join const& mcCollisions, soa::Join const& collisions, soa::Join const& KStarGens) + void processGeneratedRun3(soa::Join const& mcCollisions, soa::Join const& collisions, soa::Join const& KStarGens) { analyzeGenerated(mcCollisions, collisions, KStarGens); } diff --git a/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx new file mode 100644 index 00000000000..206f8861590 --- /dev/null +++ b/PWGLF/Tasks/Resonances/k892hadronphotonBkg.cxx @@ -0,0 +1,1266 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file k892hadronphotonBkg.cxx +/// \brief This is a ask that computes the same-event rotational and the mixed-event combinatorial backgrounds for the K*(892) -> K0S + gamma analysis. +/// \author Oussama Benchikhi + +#include "PWGLF/DataModel/LFStrangenessMLTables.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/ResonanceMlResponse.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::ml; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; +using dauTracks = soa::Join; +using V0StandardDerivedDatas = soa::Join; + +static const std::vector photonSels = {"No Sel", "Mass", "Y", "Neg Eta", "Pos Eta", + "DCAToPV", "DCADau", "Radius", "Z", "CosPA", + "Phi", "Qt", "Alpha", "TPCCR", "TPC NSigma"}; +static const std::vector kshortSels = {"No Sel", "Mass", "Y", "Neg Eta", "Pos Eta", + "DCAToPV", "Radius", "Z", "DCADau", "Armenteros", + "CosPA", "TPCCR", "ITSNCls", "Lifetime", "TPC NSigma"}; +static const std::vector lambdaSels = {"No Sel", "Radius", "Z", "DCADau", "Armenteros", + "CosPA", "Y + Dau Eta", "TPCCR", "ITSNCls", "Lifetime", + "PID", "DCAToPV", "Mass"}; + +enum BkgResonance { + kResoKStar = 0, // K*(892)0 -> K0S + gamma + kResoLambdaStar = 1 // Lambda(1520) -> Lambda + gamma +}; + +struct k892hadronphotonBkg { + Service ccdb{}; + o2::ccdb::CcdbApi ccdbApi; + ctpRateFetcher rateFetcher; + o2::analysis::ResonanceMlResponse mlResponse; + + TRandom3 rotRng{12345}; // struct member; fixed seed for reproducibility across grid jobs + + // Histogram registry + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; + + Configurable doArm{"doArm", true, "Fill the 3D Armenteros histograms"}; + + // For ML Selection + Configurable useMLScores{"useMLScores", false, "use ML scores to select candidates"}; + + // Interaction-rate retrieval (used by the event selection) + Configurable fGetIR{"fGetIR", false, "Flag to retrieve the IR info."}; + Configurable fIRCrashOnNull{"fIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; + Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + + // KStar(892) -> K0S + gamma background + struct : ConfigurableGroup { + std::string prefix = "kstarBkgConfig"; + Configurable doSameEvtRotation{"doSameEvtRotation", false, "Same-event rotational background"}; + Configurable doEvtMixing{"doEvtMixing", false, "Mixed-event background"}; + Configurable nMix{"nMix", 5, "Number of mixed events"}; + Configurable deltaCollision{"deltaCollision", 25, "Min |Δ globalIndex| for mixing"}; + Configurable kstarMaxOPAngle{"kstarMaxOPAngle", 7.f, "Max opening angle (rad)"}; + Configurable kstarMaxRap{"kstarMaxRap", 0.5f, "Max |y(K*)|"}; + Configurable nBkgRot{"nBkgRot", 3, "Rotations per pair (rotational bkg)"}; + Configurable rotationalCut{"rotationalCut", 10, "theta band: [pi - pi/cut, pi + pi/cut]"}; + Configurable rotationalFactor{"rotationalFactor", 1.f, "Factor to scale the angle of rotation (rotationalFactor * PI)"}; + Configurable rotGamma{"rotGamma", false, "Flag to rotate the photon direction"}; + } kstarBkgConfig; + + // Lambda(1520) -> Lambda + gamma background + struct : ConfigurableGroup { + std::string prefix = "lambdaStarBkgConfig"; + Configurable doSameEvtRotation{"doSameEvtRotation", false, "Same-event rotational background"}; + Configurable doEvtMixing{"doEvtMixing", false, "Mixed-event background"}; + Configurable lstarMaxOPAngle{"lstarMaxOPAngle", 7.f, "Max opening angle (rad)"}; + Configurable lstarMaxRap{"lstarMaxRap", 0.5f, "Max |y(#Lambda(1520))|"}; + Configurable nBkgRot{"nBkgRot", 3, "Rotations per pair (rotational bkg)"}; + Configurable rotationalCut{"rotationalCut", 10, "theta band: [pi - pi/cut, pi + pi/cut]"}; + Configurable rotationalFactor{"rotationalFactor", 1.f, "Factor to scale the angle of rotation (rotationalFactor * PI)"}; + Configurable rotGamma{"rotGamma", false, "Flag to rotate the photon direction"}; + } lstarBkgConfig; + + struct : ConfigurableGroup { + std::string prefix = "bdt"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"BDTModel.onnx"}, "Local .onnx file names, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/o/obenchik/MLModels/BDT"}, "Model paths on CCDB, one per pT bin (each model needs its own folder)"}; + Configurable timestampCCDB{"timestampCCDB", 1695750420200, "timestamp of the ONNX file for ML model used to query in CCDB. Please use 1695750420200"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + Configurable numThreads{"numThreads", 1, "ONNX intra-op threads. 0 lets ONNX Runtime default to one thread per physical core"}; + Configurable enableML{"enableML", false, "Enables bdt model"}; + Configurable> ptBinEdges{"ptBinEdges", {0., 30.}, "Pair-pT bin edges of the BDT models, one model per bin (pairs outside are rejected)"}; + Configurable> scoreCuts{"scoreCuts", {std::array{0., 0.}.data(), 1, 2, {"pT bin 0"}, {"Background score", "Signal score"}}, "BDT score cuts, one row per pT bin"}; + Configurable> cutDir{"cutDir", std::vector{o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, "Cut direction per class: 0 = keep score < cut, 1 = keep score >= cut, 2 = no cut"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"lambdaDCADau", "lambdaAlpha", "lambdaDCANegPV", "lambdaDCAPosPV", "lambdaQt", "photonAlpha", "photonCosPA", "photonDCADau", "photonDCANegPV", "photonDCAPosPV", "photonQt", "photonRadius", "opAngle"}, "Names and order of the BDT input features (see ResonanceMlResponse.h): must match FeaturesToTrain"}; + } bdt; + + ConfigurableAxis axisVertexMixBkg{"axisVertexMixBkg", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "z-vertex bins for mixing"}; + ConfigurableAxis axisCentralityMixBkg{"axisCentralityMixBkg", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "centrality bins for mixing"}; + + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable fUseEventSelection{"fUseEventSelection", false, "Apply event selection cuts"}; + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", true, "require events with at least one ITS-TPC track"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", false, "reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds"}; + Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold"}; + Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; + // fast check on occupancy + Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; + Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; + // fast check on interaction rate + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + } eventSelections; + + //// Photon criteria: + struct : ConfigurableGroup { + std::string prefix = "photonSelections"; // JSON group name + Configurable gammaMLThreshold{"gammaMLThreshold", 0.1, "Decision Threshold value to select gammas"}; + Configurable photonv0TypeSel{"photonv0TypeSel", 7, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable photonMinDCADauToPv{"photonMinDCADauToPv", 0.0, "Min DCA daughter To PV (cm)"}; + Configurable photonMaxDCAV0Dau{"photonMaxDCAV0Dau", 3.5, "Max DCA V0 Daughters (cm)"}; + Configurable photonMinTPCCrossedRows{"photonMinTPCCrossedRows", 30, "Min daughter TPC Crossed Rows"}; + Configurable photonMinTPCNSigmas{"photonMinTPCNSigmas", -7, "Min TPC NSigmas for daughters"}; + Configurable photonMaxTPCNSigmas{"photonMaxTPCNSigmas", 7, "Max TPC NSigmas for daughters"}; + Configurable photonMinRapidity{"photonMinRapidity", -0.5, "v0 min rapidity"}; + Configurable photonMaxRapidity{"photonMaxRapidity", 0.5, "v0 max rapidity"}; + Configurable photonDauEtaMin{"photonDauEtaMin", -0.8, "Min pseudorapidity of daughter tracks"}; + Configurable photonDauEtaMax{"photonDauEtaMax", 0.8, "Max pseudorapidity of daughter tracks"}; + Configurable photonMinRadius{"photonMinRadius", 3.0, "Min photon conversion radius (cm)"}; + Configurable photonMaxRadius{"photonMaxRadius", 115, "Max photon conversion radius (cm)"}; + Configurable photonMinZ{"photonMinZ", -240, "Min photon conversion point z value (cm)"}; + Configurable photonMaxZ{"photonMaxZ", 240, "Max photon conversion point z value (cm)"}; + Configurable photonMaxQt{"photonMaxQt", 0.08, "Max photon qt value (AP plot) (GeV/c)"}; + Configurable photonMaxAlpha{"photonMaxAlpha", 1.0, "Max photon alpha absolute value (AP plot)"}; + Configurable photonMinV0cospa{"photonMinV0cospa", 0.80, "Min V0 CosPA"}; + Configurable photonMaxMass{"photonMaxMass", 0.10, "Max photon mass (GeV/c^{2})"}; + Configurable photonPhiMin1{"photonPhiMin1", -1, "Phi min value to reject photons, region 1 (leave negative if no selection desired)"}; + Configurable photonPhiMax1{"photonPhiMax1", -1, "Phi max value to reject photons, region 1 (leave negative if no selection desired)"}; + Configurable photonPhiMin2{"photonPhiMin2", -1, "Phi max value to reject photons, region 2 (leave negative if no selection desired)"}; + Configurable photonPhiMax2{"photonPhiMax2", -1, "Phi min value to reject photons, region 2 (leave negative if no selection desired)"}; + } photonSelections; + + // KShort criteria: + struct : ConfigurableGroup { + std::string prefix = "kshortSelections"; // JSON group name + Configurable kshortMLThreshold{"kshortMLThreshold", 0.1, "Decision Threshold value to select kshorts"}; + Configurable kshortMinDCANegToPv{"kshortMinDCANegToPv", .05, "min DCA Neg To PV (cm)"}; + Configurable kshortMinDCAPosToPv{"kshortMinDCAPosToPv", .05, "min DCA Pos To PV (cm)"}; + Configurable kshortMaxDCAV0Dau{"kshortMaxDCAV0Dau", 2.5, "Max DCA V0 Daughters (cm)"}; + Configurable kshortMinv0radius{"kshortMinv0radius", 0.0, "Min V0 radius (cm)"}; + Configurable kshortMaxv0radius{"kshortMaxv0radius", 40, "Max V0 radius (cm)"}; + Configurable kshortMinv0cospa{"kshortMinv0cospa", 0.95, "Min V0 CosPA"}; + Configurable kshortMaxLifeTime{"kshortMaxLifeTime", 20, "Max lifetime"}; + Configurable kshortWindow{"kshortWindow", 0.015, "Mass window around expected (in GeV/c2). Leave negative to disable"}; + Configurable kshortMinRapidity{"kshortMinRapidity", -0.5, "v0 min rapidity"}; + Configurable kshortMaxRapidity{"kshortMaxRapidity", 0.5, "v0 max rapidity"}; + Configurable kshortDauEtaMin{"kshortDauEtaMin", -0.8, "Min pseudorapidity of daughter tracks"}; + Configurable kshortDauEtaMax{"kshortDauEtaMax", 0.8, "Max pseudorapidity of daughter tracks"}; + Configurable kshortMinZ{"kshortMinZ", -240, "Min kshort decay point z value (cm)"}; + Configurable kshortMaxZ{"kshortMaxZ", 240, "Max kshort decay point z value (cm)"}; + Configurable kshortMinTPCCrossedRows{"kshortMinTPCCrossedRows", 50, "Min daughter TPC Crossed Rows"}; + Configurable kshortMinITSclusters{"kshortMinITSclusters", 1, "minimum ITS clusters"}; + Configurable kshortRejectPosITSafterburner{"kshortRejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; + Configurable kshortRejectNegITSafterburner{"kshortRejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; + Configurable kshortArmenterosCoefficient{"kshortArmenterosCoefficient", 0.2, "Armenteros-Podolanski coefficient to reject lambdas"}; + Configurable kshortMaxTPCNSigmas{"kshortMaxTPCNSigmas", 1e+9, "Max |TPC NSigma| (pion hypothesis) for K0S daughters"}; + } kshortSelections; + + //// Lambda criteria:: + struct : ConfigurableGroup { + std::string prefix = "lambdaSelections"; // JSON group name + Configurable Lambda_MLThreshold{"Lambda_MLThreshold", 0.1, "Decision Threshold value to select lambdas"}; + Configurable AntiLambda_MLThreshold{"AntiLambda_MLThreshold", 0.1, "Decision Threshold value to select antilambdas"}; + Configurable LambdaMinDCANegToPv{"LambdaMinDCANegToPv", .05, "min DCA Neg To PV (cm)"}; + Configurable LambdaMinDCAPosToPv{"LambdaMinDCAPosToPv", .05, "min DCA Pos To PV (cm)"}; + Configurable ALambdaMinDCANegToPv{"ALambdaMinDCANegToPv", .05, "min DCA Neg To PV (cm)"}; + Configurable ALambdaMinDCAPosToPv{"ALambdaMinDCAPosToPv", .05, "min DCA Pos To PV (cm)"}; + Configurable LambdaMaxDCAV0Dau{"LambdaMaxDCAV0Dau", 2.5, "Max DCA V0 Daughters (cm)"}; + Configurable LambdaMinv0radius{"LambdaMinv0radius", 0.0, "Min V0 radius (cm)"}; + Configurable LambdaMaxv0radius{"LambdaMaxv0radius", 40, "Max V0 radius (cm)"}; + Configurable LambdaMinQt{"LambdaMinQt", 0.01, "Min lambda qt value (AP plot) (GeV/c)"}; + Configurable LambdaMaxQt{"LambdaMaxQt", 0.17, "Max lambda qt value (AP plot) (GeV/c)"}; + Configurable LambdaMinAlpha{"LambdaMinAlpha", 0.25, "Min lambda alpha absolute value (AP plot)"}; + Configurable LambdaMaxAlpha{"LambdaMaxAlpha", 1.0, "Max lambda alpha absolute value (AP plot)"}; + Configurable LambdaMinv0cospa{"LambdaMinv0cospa", 0.95, "Min V0 CosPA"}; + Configurable LambdaMaxLifeTime{"LambdaMaxLifeTime", 30, "Max lifetime"}; + Configurable LambdaWindow{"LambdaWindow", 0.015, "Mass window around expected (in GeV/c2)"}; + Configurable LambdaMinRapidity{"LambdaMinRapidity", -0.5, "v0 min rapidity"}; + Configurable LambdaMaxRapidity{"LambdaMaxRapidity", 0.5, "v0 max rapidity"}; + Configurable LambdaMinDauEta{"LambdaMinDauEta", -0.8, "Min pseudorapidity of daughter tracks"}; + Configurable LambdaMaxDauEta{"LambdaMaxDauEta", 0.8, "Max pseudorapidity of daughter tracks"}; + Configurable LambdaMinZ{"LambdaMinZ", -240, "Min lambda decay point z value (cm)"}; + Configurable LambdaMaxZ{"LambdaMaxZ", 240, "Max lambda decay point z value (cm)"}; + Configurable fselLambdaTPCPID{"fselLambdaTPCPID", true, "Flag to select lambda-like candidates using TPC NSigma."}; + Configurable LambdaMaxTPCNSigmas{"LambdaMaxTPCNSigmas", 1e+9, "Max TPC NSigmas for daughters"}; + Configurable LambdaMinTPCCrossedRows{"LambdaMinTPCCrossedRows", 50, "Min daughter TPC Crossed Rows"}; + Configurable LambdaMinITSclusters{"LambdaMinITSclusters", 1, "minimum ITS clusters"}; + Configurable LambdaRejectPosITSafterburner{"LambdaRejectPosITSafterburner", false, "reject positive track formed out of afterburner ITS tracks"}; + Configurable LambdaRejectNegITSafterburner{"LambdaRejectNegITSafterburner", false, "reject negative track formed out of afterburner ITS tracks"}; + } lambdaSelections; + + struct : ConfigurableGroup { + // base properties + std::string prefix = "axisConfig"; // JSON group name + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for analysis"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Centrality"}; + ConfigurableAxis axisKStarMass{"axisKStarMass", {500, 0.6f, 1.6f}, "M_{K^{*}} (GeV/c^{2})"}; + ConfigurableAxis axisLambdaStarMass{"axisLambdaStarMass", {500, 1.1f, 2.1f}, "M_{#Lambda(1520)} (GeV/c^{2})"}; + ConfigurableAxis axisIRBinning{"axisIRBinning", {151, -10, 1500}, "Binning for the interaction rate (kHz)"}; + ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "Resonance AP alpha (#gamma = positive leg)"}; + ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 1.1f}, "Resonance AP q_{T} (GeV/c)"}; + ConfigurableAxis axisCandSel{"axisCandSel", {15, 0.5f, +15.5f}, "Candidate Selection"}; + ConfigurableAxis axisOPAngle{"axisOPAngle", {140, 0.0f, 7.0f}, "Opening angle (rad)"}; + // BDT QA axes + ConfigurableAxis mlProb{"mlProb", {100, 0.0f, 1.0f}, "BDT signal score"}; + ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPV{"axisSignedDCAtoPV", {1000, -50.0f, 50.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPVLambda{"axisSignedDCAtoPVLambda", {500, -10.0f, 10.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisV0APQt{"axisV0APQt", {220, 0.0f, 0.5f}, "V0 AP q_{T} (GeV/c)"}; + ConfigurableAxis axisV0Radius{"axisV0Radius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; + } axisConfig; + + void init(InitContext const&) + { + // setting CCDB service + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + + if (bdt.enableML) { + ccdb->setURL(bdt.ccdbUrl.value); + + // One model per pair-pT bin. MlResponse checks the model files and cutDir, not the rows of scoreCuts + constexpr uint8_t NClassesML = 2; // background, signal + if (bdt.scoreCuts.value.rows() != bdt.ptBinEdges.value.size() - 1 || bdt.scoreCuts.value.cols() != NClassesML) { + LOG(fatal) << "bdt.scoreCuts needs one row per pT bin and " << static_cast(NClassesML) << " columns"; + } + mlResponse.configure(bdt.ptBinEdges.value, bdt.scoreCuts.value, bdt.cutDir.value, NClassesML); + mlResponse.cacheInputFeaturesIndices(bdt.namesInputFeatures); + + if (bdt.loadModelsFromCCDB) { + ccdbApi.init(bdt.ccdbUrl); + LOG(info) << "Fetching models for timestamp: " << bdt.timestampCCDB.value; + mlResponse.setModelPathsCCDB(bdt.onnxFileNames.value, ccdbApi, bdt.modelPathsCCDB.value, bdt.timestampCCDB.value); + } else { + mlResponse.setModelPathsLocal(bdt.onnxFileNames.value); + } + mlResponse.init(bdt.enableOptimizations.value, bdt.numThreads.value); + + // It is applied to the Lambda(1520) MIXED background only (for now!!) + if (!lstarBkgConfig.doSameEvtRotation && !lstarBkgConfig.doEvtMixing) { + LOG(warning) << "bdt.enableML is set but no Lambda(1520) background is requested: the BDT will not be applied."; + } + if (kstarBkgConfig.doSameEvtRotation || kstarBkgConfig.doEvtMixing) { + LOG(info) << "The BDT (gamma + Lambda features) is not applied to the K*(892) background."; + } + + histos.add("BDT/hScoreSignal", "hScoreSignal", kTH1D, {axisConfig.mlProb}); + histos.add("BDT/hScoreBackground", "hScoreBackground", kTH1D, {axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsMassSignal", "h2dScoreVsMassSignal", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsPtSignal", "h2dScoreVsPtSignal", kTH2D, {axisConfig.axisPt, axisConfig.mlProb}); + histos.add("BDT/h3dScoreSignal", "h3dScoreSignal", kTH3D, {axisConfig.axisPt, axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsMassBackground", "h2dScoreVsMassBackground", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dScoreVsPtBackground", "h2dScoreVsPtBackground", kTH2D, {axisConfig.axisPt, axisConfig.mlProb}); + histos.add("BDT/h3dScoreBackground", "h3dScoreBackground", kTH3D, {axisConfig.axisPt, axisConfig.axisLambdaStarMass, axisConfig.mlProb}); + histos.add("BDT/h2dLambdaDCADaughters", "h2dLambdaDCADaughters", kTH2D, {axisConfig.mlProb, axisConfig.axisDCAdau}); + + histos.add("BDT/h2dLambdaAlpha", "h2dLambdaAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dLambdaDCANegPV", "h2dLambdaDCANegPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaDCAPosPV", "h2dLambdaDCAPosPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaQt", "h2dLambdaQt", kTH2D, {axisConfig.mlProb, axisConfig.axisV0APQt}); + histos.add("BDT/h2dPhotonAlpha", "h2dPhotonAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dPhotonCosPA", "h2dPhotonCosPA", kTH2D, {axisConfig.mlProb, axisConfig.axisCosPA}); + histos.add("BDT/h2dPhotonDCADau", "h2dPhotonDCADau", kTH2D, {axisConfig.mlProb, axisConfig.axisDCAdau}); + histos.add("BDT/h2dPhotonDCANegPV", "h2dPhotonDCANegPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonDCAPosPV", "h2dPhotonDCAPosPV", kTH2D, {axisConfig.mlProb, axisConfig.axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {axisConfig.mlProb, axisConfig.axisV0APQt}); + histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {axisConfig.mlProb, axisConfig.axisV0Radius}); + histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {axisConfig.mlProb, axisConfig.axisOPAngle}); + histos.add("BDT/h2dAPAlpha", "h2dAPAlpha", kTH2D, {axisConfig.mlProb, axisConfig.axisAPAlpha}); + histos.add("BDT/h2dAPQt", "h2dAPQt", kTH2D, {axisConfig.mlProb, axisConfig.axisAPQt}); + } + + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisConfig.axisCentrality}); + + if (eventSelections.fUseEventSelection) { + histos.add("hEventSelection", "hEventSelection", kTH1D, {{21, -0.5f, +20.5f}}); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(6, "posZ cut"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + if (doPPAnalysis) { + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + } else { + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "Below min occup."); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "Above max occup."); + } + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Below min IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); + + if (fGetIR) { + histos.add("GeneralQA/hRunNumberNegativeIR", "", kTH1D, {{1, 0., 1.}}); + histos.add("GeneralQA/hInteractionRate", "hInteractionRate", kTH1D, {axisConfig.axisIRBinning}); + histos.add("GeneralQA/hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {axisConfig.axisCentrality, axisConfig.axisIRBinning}); + } + } + + // Single-particle selection + histos.add("PhotonSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); + for (size_t i = 0; i < photonSels.size(); ++i) + histos.get(HIST("PhotonSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(i + 1, photonSels[i].c_str()); + + histos.add("KShortSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); + for (size_t i = 0; i < kshortSels.size(); ++i) + histos.get(HIST("KShortSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(i + 1, kshortSels[i].c_str()); + + histos.add("LambdaSel/hSelectionStatistics", "hSelectionStatistics", kTH1D, {axisConfig.axisCandSel}); + for (size_t i = 0; i < lambdaSels.size(); ++i) + histos.get(HIST("LambdaSel/hSelectionStatistics"))->GetXaxis()->SetBinLabel(i + 1, lambdaSels[i].c_str()); + + if (kstarBkgConfig.doSameEvtRotation || kstarBkgConfig.doEvtMixing) { + histos.add("KStarBkg/hDeltaCollision", "hDeltaCollision", kTH1D, {{2000, -1000.f, 1000.f}}); + histos.add("KStarBkg/h2dCentralityCollPair", "h2dCentralityCollPair", kTH2D, {axisConfig.axisCentrality, axisConfig.axisCentrality}); + } + if (kstarBkgConfig.doSameEvtRotation) { + histos.add("KStarBkg/h2dRotKStarMassVsPt", "h2dRotKStarMassVsPt", kTH2D, {axisConfig.axisKStarMass, axisConfig.axisPt}); + histos.add("KStarBkg/h3dRotKStarMassVsPt", "h3dRotKStarMassVsPt", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisKStarMass}); + histos.add("KStarBkg/h3dRotKStarPtVsOPAngle", "h3dRotKStarPtVsOPAngle", kTH3D, {axisConfig.axisOPAngle, axisConfig.axisPt, axisConfig.axisKStarMass}); + if (doArm) { + histos.add("KStarBkg/h4dRotKStarPtVsAPAlphaVsAPQt", "h4dRotKStarPtVsAPAlphaVsAPQt", kTHnD, {axisConfig.axisAPAlpha, axisConfig.axisAPQt, axisConfig.axisPt, axisConfig.axisKStarMass}); + } + } + if (kstarBkgConfig.doEvtMixing) { + histos.add("KStarBkg/h2dMixedKStarMassVsPt", "h2dMixedKStarMassVsPt", kTH2D, {axisConfig.axisKStarMass, axisConfig.axisPt}); + histos.add("KStarBkg/h3dMixedKStarMassVsPt", "h3dMixedKStarMassVsPt", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisKStarMass}); + histos.add("KStarBkg/h3dMixedKStarPtVsOPAngle", "h3dMixedKStarPtVsOPAngle", kTH3D, {axisConfig.axisOPAngle, axisConfig.axisPt, axisConfig.axisKStarMass}); + if (doArm) { + histos.add("KStarBkg/h4dMixedKStarPtVsAPAlphaVsAPQt", "h4dMixedKStarPtVsAPAlphaVsAPQt", kTHnD, {axisConfig.axisAPAlpha, axisConfig.axisAPQt, axisConfig.axisPt, axisConfig.axisKStarMass}); + } + } + + // Lambda(1520) -> Lambda + gamma + if (lstarBkgConfig.doSameEvtRotation || lstarBkgConfig.doEvtMixing) { + histos.add("LambdaStarBkg/hDeltaCollision", "hDeltaCollision", kTH1D, {{2000, -1000.f, 1000.f}}); + histos.add("LambdaStarBkg/h2dCentralityCollPair", "h2dCentralityCollPair", kTH2D, {axisConfig.axisCentrality, axisConfig.axisCentrality}); + } + if (lstarBkgConfig.doSameEvtRotation) { + histos.add("LambdaStarBkg/h2dRotLambdaStarMassVsPt", "h2dRotLambdaStarMassVsPt", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.axisPt}); + histos.add("LambdaStarBkg/h3dRotLambdaStarMassVsPt", "h3dRotLambdaStarMassVsPt", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); + histos.add("LambdaStarBkg/h3dRotLambdaStarPtVsOPAngle", "h3dRotLambdaStarPtVsOPAngle", kTH3D, {axisConfig.axisOPAngle, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); + if (doArm) { + histos.add("LambdaStarBkg/h4dRotLambdaStarPtVsAPAlphaVsAPQt", "h4dRotLambdaStarPtVsAPAlphaVsAPQt", kTHnD, {axisConfig.axisAPAlpha, axisConfig.axisAPQt, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); + } + } + if (lstarBkgConfig.doEvtMixing) { + histos.add("LambdaStarBkg/h2dMixedLambdaStarMassVsPt", "h2dMixedLambdaStarMassVsPt", kTH2D, {axisConfig.axisLambdaStarMass, axisConfig.axisPt}); + histos.add("LambdaStarBkg/h3dMixedLambdaStarMassVsPt", "h3dMixedLambdaStarMassVsPt", kTH3D, {axisConfig.axisCentrality, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); + histos.add("LambdaStarBkg/h3dMixedLambdaStarPtVsOPAngle", "h3dMixedLambdaStarPtVsOPAngle", kTH3D, {axisConfig.axisOPAngle, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); + if (doArm) { + histos.add("LambdaStarBkg/h4dMixedLambdaStarPtVsAPAlphaVsAPQt", "h4dMixedLambdaStarPtVsAPAlphaVsAPQt", kTHnD, {axisConfig.axisAPAlpha, axisConfig.axisAPQt, axisConfig.axisPt, axisConfig.axisLambdaStarMass}); + } + } + + histos.print(); + } + + //_______________________________________________ + // Event selection (identical to the builder) + template + bool isEventAccepted(TCollision const& collision, bool fillHists) + { + if (fillHists) + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 3 /* Not at ITS ROF border */); + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 4 /* Not at TF border */); + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 5 /* vertex-Z selected */); + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 6 /* Contains at least one ITS-TPC track */); + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 7 /* PV position consistency check */); + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 8 /* PV with at least one contributor matched with TOF */); + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 9 /* PV with at least one contributor matched with TRD */); + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 10 /* Not at same bunch pile-up */); + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds*/); + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 12 /* No other collision within +/- 10 microseconds */); + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 13 /* No other collision within +/- 2 microseconds */); + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */); + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 15 /* No other collision within the same ITS ROF */); + if (doPPAnalysis) { // we are in pp + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + } else { // we are in Pb-Pb + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 16 /* Below min occupancy */); + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 17 /* Above max occupancy */); + } + + // Fetch interaction rate only if required (in order to limit ccdb calls) + float interactionRate = (fGetIR) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource, fIRCrashOnNull) * 1.e-3 : -1; + float centrality = doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); + + if (fGetIR) { + if (interactionRate < 0) + histos.get(HIST("GeneralQA/hRunNumberNegativeIR"))->Fill(Form("%d", collision.runNumber()), 1); // This lists all run numbers without IR info! + + histos.fill(HIST("GeneralQA/hInteractionRate"), interactionRate); + histos.fill(HIST("GeneralQA/hCentralityVsInteractionRate"), centrality, interactionRate); + } + + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 18 /* Below min IR */); + + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); + // Fill centrality histogram after event selection + histos.fill(HIST("hEventCentrality"), centrality); + } + return true; + } + + //_______________________________________________ + // Process v0 photon candidate + template + bool processPhotonCandidate(TV0Object const& gamma) + { + // V0 type selection + if (gamma.v0Type() != photonSelections.photonv0TypeSel && photonSelections.photonv0TypeSel > -1) + return false; + + float photonY = RecoDecay::y(std::array{gamma.px(), gamma.py(), gamma.pz()}, o2::constants::physics::MassGamma); + + if (useMLScores) { + if (gamma.gammaBDTScore() <= photonSelections.gammaMLThreshold) + return false; + + } else { + // Standard selection + // Gamma basic selection criteria: + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 1.); + if ((gamma.mGamma() < 0) || (gamma.mGamma() > photonSelections.photonMaxMass)) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 2.); + if ((photonY < photonSelections.photonMinRapidity) || (photonY > photonSelections.photonMaxRapidity)) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 3.); + if (gamma.negativeeta() < photonSelections.photonDauEtaMin || gamma.negativeeta() > photonSelections.photonDauEtaMax) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 4.); + if (gamma.positiveeta() < photonSelections.photonDauEtaMin || gamma.positiveeta() > photonSelections.photonDauEtaMax) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 5.); + if ((std::abs(gamma.dcapostopv()) < photonSelections.photonMinDCADauToPv) || (std::abs(gamma.dcanegtopv()) < photonSelections.photonMinDCADauToPv)) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 6.); + if (std::abs(gamma.dcaV0daughters()) > photonSelections.photonMaxDCAV0Dau) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 7.); + if ((gamma.v0radius() < photonSelections.photonMinRadius) || (gamma.v0radius() > photonSelections.photonMaxRadius)) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 8.); + if ((gamma.z() < photonSelections.photonMinZ) || (gamma.z() > photonSelections.photonMaxZ)) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 9.); + if (gamma.v0cosPA() < photonSelections.photonMinV0cospa) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 10.); + float photonPhi = RecoDecay::phi(gamma.px(), gamma.py()); + if ((((photonPhi > photonSelections.photonPhiMin1) && (photonPhi < photonSelections.photonPhiMax1)) || ((photonPhi > photonSelections.photonPhiMin2) && (photonPhi < photonSelections.photonPhiMax2))) && ((photonSelections.photonPhiMin1 != -1) && (photonSelections.photonPhiMax1 != -1) && (photonSelections.photonPhiMin2 != -1) && (photonSelections.photonPhiMax2 != -1))) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 11.); + if (gamma.qtarm() > photonSelections.photonMaxQt) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 12.); + if (std::abs(gamma.alpha()) > photonSelections.photonMaxAlpha) + return false; + + auto posTrackGamma = gamma.template posTrackExtra_as(); + auto negTrackGamma = gamma.template negTrackExtra_as(); + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 13.); + if ((posTrackGamma.tpcCrossedRows() < photonSelections.photonMinTPCCrossedRows) || (negTrackGamma.tpcCrossedRows() < photonSelections.photonMinTPCCrossedRows)) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 14.); + if (((posTrackGamma.tpcNSigmaEl() < photonSelections.photonMinTPCNSigmas) || (posTrackGamma.tpcNSigmaEl() > photonSelections.photonMaxTPCNSigmas))) + return false; + + if (((negTrackGamma.tpcNSigmaEl() < photonSelections.photonMinTPCNSigmas) || (negTrackGamma.tpcNSigmaEl() > photonSelections.photonMaxTPCNSigmas))) + return false; + + histos.fill(HIST("PhotonSel/hSelectionStatistics"), 15.); + } + + return true; + } + + //_______________________________________________ + // Process K0Short candidate + template + bool processKShortCandidate(TV0Object const& kshort, TCollision const& collision) + { + // V0 type selection + if (kshort.v0Type() != 1) + return false; + + if (useMLScores) { + // if (kshort.k0ShortBDTScore() <= kshortSelections.kshortMLThreshold) + return false; + } + + // KShort basic selection criteria: + histos.fill(HIST("KShortSel/hSelectionStatistics"), 1.); + if ((std::abs(kshort.mK0Short() - o2::constants::physics::MassK0Short) > kshortSelections.kshortWindow) && kshortSelections.kshortWindow > 0) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 2.); + if ((kshort.yK0Short() < kshortSelections.kshortMinRapidity) || (kshort.yK0Short() > kshortSelections.kshortMaxRapidity)) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 3.); + if ((kshort.negativeeta() < kshortSelections.kshortDauEtaMin) || (kshort.negativeeta() > kshortSelections.kshortDauEtaMax)) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 4.); + if ((kshort.positiveeta() < kshortSelections.kshortDauEtaMin) || (kshort.positiveeta() > kshortSelections.kshortDauEtaMax)) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 5.); + if ((std::abs(kshort.dcapostopv()) < kshortSelections.kshortMinDCAPosToPv) || (std::abs(kshort.dcanegtopv()) < kshortSelections.kshortMinDCANegToPv)) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 6.); + if ((kshort.v0radius() < kshortSelections.kshortMinv0radius) || (kshort.v0radius() > kshortSelections.kshortMaxv0radius)) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 7.); + if ((kshort.z() < kshortSelections.kshortMinZ) || (kshort.z() > kshortSelections.kshortMaxZ)) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 8.); + if (std::abs(kshort.dcaV0daughters()) > kshortSelections.kshortMaxDCAV0Dau) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 9.); + if (kshort.qtarm() < kshortSelections.kshortArmenterosCoefficient * std::abs(kshort.alpha())) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 10.); + if (kshort.v0cosPA() < kshortSelections.kshortMinv0cospa) + return false; + + auto posTrackKShort = kshort.template posTrackExtra_as(); + auto negTrackKShort = kshort.template negTrackExtra_as(); + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 11.); + if ((posTrackKShort.tpcCrossedRows() < kshortSelections.kshortMinTPCCrossedRows) || (negTrackKShort.tpcCrossedRows() < kshortSelections.kshortMinTPCCrossedRows)) + return false; + + // MinITSCls + bool posIsFromAfterburner = posTrackKShort.itsChi2PerNcl() < 0; + bool negIsFromAfterburner = negTrackKShort.itsChi2PerNcl() < 0; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 12.); + if (posTrackKShort.itsNCls() < kshortSelections.kshortMinITSclusters && (!kshortSelections.kshortRejectPosITSafterburner || posIsFromAfterburner)) + return false; + if (negTrackKShort.itsNCls() < kshortSelections.kshortMinITSclusters && (!kshortSelections.kshortRejectNegITSafterburner || negIsFromAfterburner)) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 13.); + float fKShortLifeTime = kshort.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassK0Short; + if (fKShortLifeTime > kshortSelections.kshortMaxLifeTime) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 14.); + // TPC PID selection on the K0S pion daughters (same convention as posTrackKShort.tpcNSigmaPi()) + if (((std::abs(posTrackKShort.tpcNSigmaPi()) > kshortSelections.kshortMaxTPCNSigmas) || + (std::abs(negTrackKShort.tpcNSigmaPi()) > kshortSelections.kshortMaxTPCNSigmas))) + return false; + + histos.fill(HIST("KShortSel/hSelectionStatistics"), 15.); + return true; + } + + //_______________________________________________ + // Process Lambda candidate + template + bool processLambdaCandidate(TV0Object const& lambda, TCollision const& collision) + { + // V0 type selection (matching builder-level) + if (lambda.v0Type() != 1) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 1.); + if ((lambda.v0radius() < lambdaSelections.LambdaMinv0radius) || (lambda.v0radius() > lambdaSelections.LambdaMaxv0radius)) + return false; + + // Decay-point z window (builder-level) + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 2.); + if ((lambda.z() < lambdaSelections.LambdaMinZ) || (lambda.z() > lambdaSelections.LambdaMaxZ)) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 3.); + if (std::abs(lambda.dcaV0daughters()) > lambdaSelections.LambdaMaxDCAV0Dau) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 4.); + if ((lambda.qtarm() < lambdaSelections.LambdaMinQt) || (lambda.qtarm() > lambdaSelections.LambdaMaxQt)) + return false; + + if ((std::abs(lambda.alpha()) < lambdaSelections.LambdaMinAlpha) || (std::abs(lambda.alpha()) > lambdaSelections.LambdaMaxAlpha)) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 5.); + if (lambda.v0cosPA() < lambdaSelections.LambdaMinv0cospa) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 6.); + if ((lambda.yLambda() < lambdaSelections.LambdaMinRapidity) || (lambda.yLambda() > lambdaSelections.LambdaMaxRapidity)) + return false; + if ((lambda.positiveeta() < lambdaSelections.LambdaMinDauEta) || (lambda.positiveeta() > lambdaSelections.LambdaMaxDauEta)) + return false; + if ((lambda.negativeeta() < lambdaSelections.LambdaMinDauEta) || (lambda.negativeeta() > lambdaSelections.LambdaMaxDauEta)) + return false; + + auto posTrackLambda = lambda.template posTrackExtra_as(); + auto negTrackLambda = lambda.template negTrackExtra_as(); + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 7.); + if ((posTrackLambda.tpcCrossedRows() < lambdaSelections.LambdaMinTPCCrossedRows) || (negTrackLambda.tpcCrossedRows() < lambdaSelections.LambdaMinTPCCrossedRows)) + return false; + + // MinITSCls + reject ITS afterburner tracks if requested + bool posIsFromAfterburner = posTrackLambda.itsChi2PerNcl() < 0; + bool negIsFromAfterburner = negTrackLambda.itsChi2PerNcl() < 0; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 8.); + if (posTrackLambda.itsNCls() < lambdaSelections.LambdaMinITSclusters && (!lambdaSelections.LambdaRejectPosITSafterburner || posIsFromAfterburner)) + return false; + if (negTrackLambda.itsNCls() < lambdaSelections.LambdaMinITSclusters && (!lambdaSelections.LambdaRejectNegITSafterburner || negIsFromAfterburner)) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 9.); + float fLambdaLifeTime = lambda.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + if (fLambdaLifeTime > lambdaSelections.LambdaMaxLifeTime) + return false; + + // Separating lambda and antilambda selections: + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 10.); + if (lambda.alpha() > 0) { // Lambda selection + + // TPC Selection + if (lambdaSelections.fselLambdaTPCPID && (std::abs(posTrackLambda.tpcNSigmaPr()) > lambdaSelections.LambdaMaxTPCNSigmas)) + return false; + if (lambdaSelections.fselLambdaTPCPID && (std::abs(negTrackLambda.tpcNSigmaPi()) > lambdaSelections.LambdaMaxTPCNSigmas)) + return false; + + // DCA Selection + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 11.); + if ((std::abs(lambda.dcapostopv()) < lambdaSelections.LambdaMinDCAPosToPv) || (std::abs(lambda.dcanegtopv()) < lambdaSelections.LambdaMinDCANegToPv)) + return false; + + // Mass Selection + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 12.); + if (std::abs(lambda.mLambda() - o2::constants::physics::MassLambda0) > lambdaSelections.LambdaWindow) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 13.); + + } else { // AntiLambda selection + + // TPC Selection + if (lambdaSelections.fselLambdaTPCPID && (std::abs(posTrackLambda.tpcNSigmaPi()) > lambdaSelections.LambdaMaxTPCNSigmas)) + return false; + if (lambdaSelections.fselLambdaTPCPID && (std::abs(negTrackLambda.tpcNSigmaPr()) > lambdaSelections.LambdaMaxTPCNSigmas)) + return false; + + // DCA Selection + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 11.); + if ((std::abs(lambda.dcapostopv()) < lambdaSelections.ALambdaMinDCAPosToPv) || (std::abs(lambda.dcanegtopv()) < lambdaSelections.ALambdaMinDCANegToPv)) + return false; + + // Mass Selection + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 12.); + if (std::abs(lambda.mAntiLambda() - o2::constants::physics::MassLambda0) > lambdaSelections.LambdaWindow) + return false; + + histos.fill(HIST("LambdaSel/hSelectionStatistics"), 13.); + } + + return true; + } + + //_______________________________________________ + // Armenteros-Podolanski variables of the (photon + hadron) pair. + static float armenterosAlpha(std::array const& photonP, + std::array const& hadronP) + { + const std::array momRes{photonP[0] + hadronP[0], photonP[1] + hadronP[1], photonP[2] + hadronP[2]}; + const double momTot = RecoDecay::p(momRes); + const double lQlNeg = RecoDecay::dotProd(hadronP, momRes) / momTot; + const double lQlPos = RecoDecay::dotProd(photonP, momRes) / momTot; + return (lQlPos - lQlNeg) / (lQlPos + lQlNeg); + } + + static float armenterosQt(std::array const& photonP, + std::array const& hadronP) + { + const std::array momRes{photonP[0] + hadronP[0], photonP[1] + hadronP[1], photonP[2] + hadronP[2]}; + const double momTot2 = RecoDecay::p2(momRes); + const double dp = RecoDecay::dotProd(hadronP, momRes); + return std::sqrt(RecoDecay::p2(hadronP) - dp * dp / momTot2); + } + + //_______________________________________________ + // The two V0s share a daughter track (or are the same V0): rejected by the builder + template + static bool shareDaughters(TV0Object const& photon, TV0Object const& hadron) + { + return photon.globalIndex() == hadron.globalIndex() || + photon.posTrackExtraId() == hadron.posTrackExtraId() || + photon.negTrackExtraId() == hadron.negTrackExtraId() || + photon.posTrackExtraId() == hadron.negTrackExtraId() || + photon.negTrackExtraId() == hadron.posTrackExtraId(); + } + + //_______________________________________________ + // Fill BDT performance QA + template + void fillBDTPerformance(TV0Object const& lambda, TV0Object const& photon, float openAngle, float apAlpha, float apQt, float score, float pt, float mass) + { + float bkgScore = 1.0f - score; + + // Signal-probability output + histos.fill(HIST("BDT/hScoreSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsMassSignal"), mass, score); + histos.fill(HIST("BDT/h2dScoreVsPtSignal"), pt, score); + histos.fill(HIST("BDT/h3dScoreSignal"), pt, mass, score); + + // Background-probability output + histos.fill(HIST("BDT/hScoreBackground"), bkgScore); + histos.fill(HIST("BDT/h2dScoreVsMassBackground"), mass, bkgScore); + histos.fill(HIST("BDT/h2dScoreVsPtBackground"), pt, bkgScore); + histos.fill(HIST("BDT/h3dScoreBackground"), pt, mass, bkgScore); + + // Signal score vs the main topological variables + histos.fill(HIST("BDT/h2dLambdaDCADaughters"), score, lambda.dcaV0daughters()); + histos.fill(HIST("BDT/h2dLambdaAlpha"), score, lambda.alpha()); + histos.fill(HIST("BDT/h2dLambdaDCANegPV"), score, lambda.dcanegtopv()); + histos.fill(HIST("BDT/h2dLambdaDCAPosPV"), score, lambda.dcapostopv()); + histos.fill(HIST("BDT/h2dLambdaQt"), score, lambda.qtarm()); + histos.fill(HIST("BDT/h2dPhotonAlpha"), score, photon.alpha()); + histos.fill(HIST("BDT/h2dPhotonCosPA"), score, photon.v0cosPA()); + histos.fill(HIST("BDT/h2dPhotonDCADau"), score, photon.dcaV0daughters()); + histos.fill(HIST("BDT/h2dPhotonDCANegPV"), score, photon.dcanegtopv()); + histos.fill(HIST("BDT/h2dPhotonDCAPosPV"), score, photon.dcapostopv()); + histos.fill(HIST("BDT/h2dPhotonQt"), score, photon.qtarm()); + histos.fill(HIST("BDT/h2dPhotonRadius"), score, photon.v0radius()); + histos.fill(HIST("BDT/h2dOPAngle"), score, openAngle); + histos.fill(HIST("BDT/h2dAPAlpha"), score, apAlpha); + histos.fill(HIST("BDT/h2dAPQt"), score, apQt); + } + + //_______________________________________________ + // BDT selection of a Lambda + photon pair + template + bool selectML(TV0Object const& lambda, TV0Object const& photon, + float openAngle, float apAlpha, float apQt, float pt, float mass) + { + // No model outside the bdt.ptBinEdges range + if (pt < bdt.ptBinEdges.value.front() || pt >= bdt.ptBinEdges.value.back()) + return false; + + // Features in the order of bdt.namesInputFeatures + auto inputFeatures = mlResponse.getInputFeatures(lambda, photon, openAngle, apAlpha, apQt); + std::vector outputMl; + const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin + + fillBDTPerformance(lambda, photon, openAngle, apAlpha, apQt, outputMl[1], pt, mass); + + return isSelected; + } + + //_______________________________________________ + // Compute same-event rotational background within a single collision. + template + void calculateRotBackground(TCollision const& coll, + std::vector const& photonIndices, + std::vector const& hadronIndices, + TV0s const& fullV0s) + { + if (photonIndices.empty() || hadronIndices.empty()) + return; + + constexpr float HadronMass = (resonance == kResoKStar) ? o2::constants::physics::MassK0Short : o2::constants::physics::MassLambda0; + constexpr float ResonanceMass = (resonance == kResoKStar) ? o2::constants::physics::MassK0Star892 : o2::constants::physics::MassLambda1520; + + const int nBkgRot = (resonance == kResoKStar) ? kstarBkgConfig.nBkgRot.value : lstarBkgConfig.nBkgRot.value; + const int rotationalCut = (resonance == kResoKStar) ? kstarBkgConfig.rotationalCut.value : lstarBkgConfig.rotationalCut.value; + const float rotationalFactor = (resonance == kResoKStar) ? kstarBkgConfig.rotationalFactor.value : lstarBkgConfig.rotationalFactor.value; + const float maxRap = (resonance == kResoKStar) ? kstarBkgConfig.kstarMaxRap.value : lstarBkgConfig.lstarMaxRap.value; + const bool rotGamma = (resonance == kResoKStar) ? kstarBkgConfig.rotGamma.value : lstarBkgConfig.rotGamma.value; + + const float centrality = getCentralityRun3Bkg(coll); + for (const int& hIdx : hadronIndices) { + const auto& hadron = fullV0s.rawIteratorAt(hIdx); + + for (const int& pIdx : photonIndices) { + const auto& photon = fullV0s.rawIteratorAt(pIdx); + + // Same pair rejection + if (shareDaughters(photon, hadron)) + continue; + + // photon as a massless 4-vector + ROOT::Math::PtEtaPhiMVector pGamma(photon.pt(), + photon.eta(), + photon.phi(), + o2::constants::physics::MassGamma); + + ROOT::Math::PtEtaPhiMVector pHadron(hadron.pt(), + hadron.eta(), + hadron.phi(), + HadronMass); + + for (int irot = 0; irot < nBkgRot; ++irot) { + float theta = rotRng.Uniform(rotationalFactor * o2::constants::math::PI - o2::constants::math::PI / rotationalCut, + rotationalFactor * o2::constants::math::PI + o2::constants::math::PI / rotationalCut); + + ROOT::Math::PtEtaPhiMVector hRot(hadron.pt(), hadron.eta(), hadron.phi() + theta, HadronMass); + ROOT::Math::PtEtaPhiMVector gRot(photon.pt(), photon.eta(), photon.phi() + theta, o2::constants::physics::MassGamma); + + const auto& gammaLeg = rotGamma ? gRot : pGamma; + const auto& hadronLeg = rotGamma ? pHadron : hRot; + + auto reso = gammaLeg + hadronLeg; + + float rapidity = RecoDecay::y(std::array{static_cast(reso.Px()), + static_cast(reso.Py()), + static_cast(reso.Pz())}, + ResonanceMass); + if (std::abs(rapidity) > maxRap) + continue; + + // Opening angle between photon and hadron + double cosOA = gammaLeg.Vect().Dot(hadronLeg.Vect()) / (gammaLeg.P() * hadronLeg.P()); + double openAngle = std::acos(cosOA); + // double pt = reso.Pt(); + // double mass = reso.M(); + + // // To:Do BDT selection (Lambda(1520)) + // if constexpr (resonance == kResoLambdaStar) { + // if (bdt.enableML) { + // if (!selectML(hadron, photon, openAngle, pt, mass)) + // continue; + // } + // } + + // Armenteros-Podolanski of the rotated pair + const std::array gammaMom{static_cast(gammaLeg.Px()), static_cast(gammaLeg.Py()), static_cast(gammaLeg.Pz())}; + const std::array hadronMom{static_cast(hadronLeg.Px()), static_cast(hadronLeg.Py()), static_cast(hadronLeg.Pz())}; + const float apAlpha = armenterosAlpha(gammaMom, hadronMom); + const float apQt = armenterosQt(gammaMom, hadronMom); + + if constexpr (resonance == kResoKStar) { + histos.fill(HIST("KStarBkg/h2dRotKStarMassVsPt"), reso.M(), reso.Pt()); + histos.fill(HIST("KStarBkg/h3dRotKStarMassVsPt"), centrality, reso.Pt(), reso.M()); + histos.fill(HIST("KStarBkg/h3dRotKStarPtVsOPAngle"), openAngle, reso.Pt(), reso.M()); + if (doArm) { + histos.fill(HIST("KStarBkg/h4dRotKStarPtVsAPAlphaVsAPQt"), apAlpha, apQt, reso.Pt(), reso.M()); + } + } else { + histos.fill(HIST("LambdaStarBkg/h2dRotLambdaStarMassVsPt"), reso.M(), reso.Pt()); + histos.fill(HIST("LambdaStarBkg/h3dRotLambdaStarMassVsPt"), centrality, reso.Pt(), reso.M()); + histos.fill(HIST("LambdaStarBkg/h3dRotLambdaStarPtVsOPAngle"), openAngle, reso.Pt(), reso.M()); + if (doArm) { + histos.fill(HIST("LambdaStarBkg/h4dRotLambdaStarPtVsAPAlphaVsAPQt"), apAlpha, apQt, reso.Pt(), reso.M()); + } + } + } + } + } + } + + //_______________________________________________ + // Mixed-event pairing: hadrons and photons come from two different collisions. + // Centrality is taken from the reference collision (the first of the pair). + template + void calculateMixedBackground(TRefColl const& refColl, + std::vector const& hadronIndices, + std::vector const& photonIndices, + TV0s const& fullV0s) + { + if (hadronIndices.empty() || photonIndices.empty()) + return; + + constexpr float HadronMass = (resonance == kResoKStar) ? o2::constants::physics::MassK0Short : o2::constants::physics::MassLambda0; + constexpr float ResonanceMass = (resonance == kResoKStar) ? o2::constants::physics::MassK0Star892 : o2::constants::physics::MassLambda1520; + + const float maxOPAngle = (resonance == kResoKStar) ? kstarBkgConfig.kstarMaxOPAngle.value : lstarBkgConfig.lstarMaxOPAngle.value; + const float maxRap = (resonance == kResoKStar) ? kstarBkgConfig.kstarMaxRap.value : lstarBkgConfig.lstarMaxRap.value; + + const float centrality = getCentralityRun3Bkg(refColl); + + for (const int& hIdx : hadronIndices) { + const auto& hadron = fullV0s.rawIteratorAt(hIdx); + float hP = std::hypot(hadron.px(), hadron.py(), hadron.pz()); + ROOT::Math::PxPyPzEVector fourMomHadron( + hadron.px(), hadron.py(), hadron.pz(), + std::sqrt(hP * hP + HadronMass * HadronMass)); + + for (const int& pIdx : photonIndices) { + const auto& photon = fullV0s.rawIteratorAt(pIdx); + + // Same pair rejection as the builder + if (shareDaughters(photon, hadron)) + continue; + + float pP = std::hypot(photon.px(), photon.py(), photon.pz()); + ROOT::Math::PxPyPzEVector fourMomPhoton( + photon.px(), photon.py(), photon.pz(), pP); + + auto fourMomReso = fourMomPhoton + fourMomHadron; + + double cosOA = fourMomPhoton.Vect().Dot(fourMomHadron.Vect()) / + (fourMomPhoton.P() * fourMomHadron.P()); + double openAngle = std::acos(cosOA); + float mass = fourMomReso.M(); + float pt = fourMomReso.Pt(); + + float rapidity = RecoDecay::y(std::array{static_cast(fourMomReso.Px()), + static_cast(fourMomReso.Py()), + static_cast(fourMomReso.Pz())}, + ResonanceMass); + + if (openAngle > maxOPAngle) + continue; + if (std::abs(rapidity) > maxRap) + continue; + + // Armenteros-Podolanski of the mixed pair + const std::array gammaMom{photon.px(), photon.py(), photon.pz()}; + const std::array hadronMom{hadron.px(), hadron.py(), hadron.pz()}; + const float apAlpha = armenterosAlpha(gammaMom, hadronMom); + const float apQt = armenterosQt(gammaMom, hadronMom); + + // BDT selection (Lambda(1520) only) + if constexpr (resonance == kResoLambdaStar) { + if (bdt.enableML) { + if (!selectML(hadron, photon, openAngle, apAlpha, apQt, pt, mass)) + continue; + } + } + + if constexpr (resonance == kResoKStar) { + histos.fill(HIST("KStarBkg/h2dMixedKStarMassVsPt"), mass, pt); + histos.fill(HIST("KStarBkg/h3dMixedKStarMassVsPt"), centrality, pt, mass); + histos.fill(HIST("KStarBkg/h3dMixedKStarPtVsOPAngle"), openAngle, pt, mass); + if (doArm) { + histos.fill(HIST("KStarBkg/h4dMixedKStarPtVsAPAlphaVsAPQt"), apAlpha, apQt, pt, mass); + } + } else { + histos.fill(HIST("LambdaStarBkg/h2dMixedLambdaStarMassVsPt"), mass, pt); + histos.fill(HIST("LambdaStarBkg/h3dMixedLambdaStarMassVsPt"), centrality, pt, mass); + histos.fill(HIST("LambdaStarBkg/h3dMixedLambdaStarPtVsOPAngle"), openAngle, pt, mass); + if (doArm) { + histos.fill(HIST("LambdaStarBkg/h4dMixedLambdaStarPtVsAPAlphaVsAPQt"), apAlpha, apQt, pt, mass); + } + } + } + } + } + + //_______________________________________________ + // Centrality helper for the background (keeps the builder's semantics) + template + float getCentralityRun3Bkg(TCollision const& collision) + { + return doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); + } + + //_______________________________________________ + // Main: same-event rotation + event mixing for the K* and Lambda(1520) backgrounds + using BkgBinningType = ColumnBinningPolicy; + template + void calculateResonanceBkg(TCollisions const& collisions, TV0s const& fullV0s) + { + // Lambdas are only selected if a Lambda(1520) background was requested + const bool doLambdaStar = lstarBkgConfig.doSameEvtRotation || lstarBkgConfig.doEvtMixing; + + // Per-collision pools of selected photon, K0s and Lambda V0 indices + std::vector> photonPool(collisions.size()); + std::vector> kshortPool(collisions.size()); + std::vector> lambdaPool(collisions.size()); + + // V0 grouping by straCollisionId + std::vector> v0grouped(collisions.size()); + for (const auto& v0 : fullV0s) { + v0grouped[v0.straCollisionId()].push_back(v0.globalIndex()); + } + + // ── Pass 1: populate pools using single-particle selections ── + for (const auto& coll : collisions) { + + if (eventSelections.fUseEventSelection && !isEventAccepted(coll, true)) + continue; + + for (size_t i = 0; i < v0grouped[coll.globalIndex()].size(); i++) { + auto v0 = fullV0s.rawIteratorAt(v0grouped[coll.globalIndex()][i]); + + if (processPhotonCandidate(v0)) + photonPool[coll.globalIndex()].push_back(v0.globalIndex()); + + if (processKShortCandidate(v0, coll)) + kshortPool[coll.globalIndex()].push_back(v0.globalIndex()); + + if (doLambdaStar && processLambdaCandidate(v0, coll)) + lambdaPool[coll.globalIndex()].push_back(v0.globalIndex()); + } + + // Same-event rotational background + if (kstarBkgConfig.doSameEvtRotation) { + calculateRotBackground(coll, + photonPool[coll.globalIndex()], + kshortPool[coll.globalIndex()], + fullV0s); + } + if (lstarBkgConfig.doSameEvtRotation) { + calculateRotBackground(coll, + photonPool[coll.globalIndex()], + lambdaPool[coll.globalIndex()], + fullV0s); + } + } + + // Event Mixing + if (!kstarBkgConfig.doEvtMixing && !lstarBkgConfig.doEvtMixing) + return; + + // Build the mixing binning locally: a struct member initialized from a + // ConfigurableAxis captures the default bins at task construction time + BkgBinningType bkgColBinning{{axisVertexMixBkg, axisCentralityMixBkg}}; + + for (const auto& [coll1, coll2] : selfCombinations(bkgColBinning, kstarBkgConfig.nMix, -1, + collisions, collisions)) { + if (coll1.globalIndex() == coll2.globalIndex()) + continue; + + if (kstarBkgConfig.doEvtMixing) { + histos.fill(HIST("KStarBkg/hDeltaCollision"), + coll1.globalIndex() - coll2.globalIndex()); + histos.fill(HIST("KStarBkg/h2dCentralityCollPair"), + getCentralityRun3Bkg(coll1), getCentralityRun3Bkg(coll2)); + } + if (lstarBkgConfig.doEvtMixing) { + histos.fill(HIST("LambdaStarBkg/hDeltaCollision"), + coll1.globalIndex() - coll2.globalIndex()); + histos.fill(HIST("LambdaStarBkg/h2dCentralityCollPair"), + getCentralityRun3Bkg(coll1), getCentralityRun3Bkg(coll2)); + } + + if (std::abs(static_cast(coll1.globalIndex()) - static_cast(coll2.globalIndex())) < kstarBkgConfig.deltaCollision) + continue; + + auto const& photons1 = photonPool[coll1.globalIndex()]; + auto const& photons2 = photonPool[coll2.globalIndex()]; + + if (kstarBkgConfig.doEvtMixing) { + // K0s(coll1) × γ(coll2) and γ(coll1) × K0s(coll2) + calculateMixedBackground(coll1, kshortPool[coll1.globalIndex()], photons2, fullV0s); + calculateMixedBackground(coll1, kshortPool[coll2.globalIndex()], photons1, fullV0s); + } + + if (lstarBkgConfig.doEvtMixing) { + // Λ(coll1) × γ(coll2) and γ(coll1) × Λ(coll2) + calculateMixedBackground(coll1, lambdaPool[coll1.globalIndex()], photons2, fullV0s); + calculateMixedBackground(coll1, lambdaPool[coll2.globalIndex()], photons1, fullV0s); + } + } + } + + //_______________________________________________ + // Data process: same-event rotational + mixed-event K* and Lambda(1520) background + void processKStarBkg(soa::Join const& collisions, + V0StandardDerivedDatas const& fullV0s, + dauTracks const&) + { + calculateResonanceBkg(collisions, fullV0s); + } + + PROCESS_SWITCH(k892hadronphotonBkg, processKStarBkg, "Compute K* and Lambda(1520) same-event rotational and mixed-event background (data)", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/k892pmanalysis.cxx b/PWGLF/Tasks/Resonances/k892pmanalysis.cxx index 0abd5992a29..abe0ae5f7f3 100644 --- a/PWGLF/Tasks/Resonances/k892pmanalysis.cxx +++ b/PWGLF/Tasks/Resonances/k892pmanalysis.cxx @@ -197,7 +197,7 @@ struct k892pmanalysis { double massAntiLambda0 = MassLambda0Bar; template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -219,7 +219,7 @@ struct k892pmanalysis { } template - bool V0Cut(const V0Type v0) + bool V0Cut(const V0Type& v0) { // V0 track cuts if (std::abs(v0.eta()) > cMaxV0Etacut) @@ -492,7 +492,7 @@ struct k892pmanalysis { { auto tracksV0sTuple = std::make_tuple(resotracks, resov0s); // auto V0sTuple = std::make_tuple(resov0s); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; Pair pairs{colBinning, nEvtMixing, -1, collisions, tracksV0sTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, resotracks1, collision2, resov0s2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx b/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx index 55d41750b35..c746e7c082f 100644 --- a/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx +++ b/PWGLF/Tasks/Resonances/kaonkaonanalysis.cxx @@ -454,7 +454,7 @@ struct kaonkaonAnalysisRun3 { histos.fill(HIST("multdist_FT0A"), collision.multFT0A()); histos.fill(HIST("multdist_FT0C"), collision.multFT0C()); } - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -467,7 +467,7 @@ struct kaonkaonAnalysisRun3 { histos.fill(HIST("hNsigmaKaonTOF_TPC_before"), track1.tofNSigmaKa(), track1.tpcNSigmaKa()); } auto track1ID = track1.index(); - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!selectionTrack(track2)) { continue; } @@ -504,8 +504,8 @@ struct kaonkaonAnalysisRun3 { { auto tracksTuple = std::make_tuple(tracks); //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor1 binningOnPositions1{{axisVertex, axisMultiplicity}, true}; // for pp - BinningTypeVertexContributor2 binningOnPositions2{{axisVertex, axisMultiplicity}, true}; // for PbPb + BinningTypeVertexContributor1 binningOnPositions1{{axisVertex, axisMultiplicity}}; // for pp + BinningTypeVertexContributor2 binningOnPositions2{{axisVertex, axisMultiplicity}}; // for PbPb SameKindPair pair1{binningOnPositions1, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; SameKindPair pair2{binningOnPositions2, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; @@ -605,7 +605,7 @@ struct kaonkaonAnalysisRun3 { } auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } @@ -634,7 +634,7 @@ struct kaonkaonAnalysisRun3 { histos.fill(HIST("Recmutiplicity"), multiplicity); histos.fill(HIST("hMC"), 5.5); auto oldindex = -999; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -642,7 +642,7 @@ struct kaonkaonAnalysisRun3 { continue; } auto track1ID = track1.index(); - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!track2.has_mcParticle()) { continue; } diff --git a/PWGLF/Tasks/Resonances/kshortlambda.cxx b/PWGLF/Tasks/Resonances/kshortlambda.cxx index 0ddf5ade8bf..64b0278d835 100644 --- a/PWGLF/Tasks/Resonances/kshortlambda.cxx +++ b/PWGLF/Tasks/Resonances/kshortlambda.cxx @@ -714,8 +714,8 @@ struct Kshortlambda { void processME(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, V0TrackCandidate const& v0s) { auto tracksTuple = std::make_tuple(v0s); - BinningTypeVertexContributor binningOnPositions1{{mevz, memult}, true}; - BinningTypeCentralityM binningOnPositions2{{mevz, memult}, true}; + BinningTypeVertexContributor binningOnPositions1{{mevz, memult}}; + BinningTypeCentralityM binningOnPositions2{{mevz, memult}}; SameKindPair pair1{binningOnPositions1, cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; // for PbPb SameKindPair pair2{binningOnPositions2, cfgNmixedEvents, -1, collisions, tracksTuple, &cache}; // for pp diff --git a/PWGLF/Tasks/Resonances/kstar0analysis.cxx b/PWGLF/Tasks/Resonances/kstar0analysis.cxx index 4c3a32afcae..ad544f85608 100644 --- a/PWGLF/Tasks/Resonances/kstar0analysis.cxx +++ b/PWGLF/Tasks/Resonances/kstar0analysis.cxx @@ -8,11 +8,16 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. + /// \file kstar0analysis.cxx /// \brief the reconstruction of k*0(892) resonance analysis using K\pi decay channel /// \author Hirak Kumar Koley +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/Utils/inelGt.h" + #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -20,6 +25,7 @@ #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include #include @@ -28,918 +34,1336 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include -#include +#include +#include #include #include -#include -#include -#include #include -#include #include -#include - using namespace o2; +using namespace o2::soa; +using namespace o2::aod; +using namespace o2::aod::rctsel; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +using LorentzVectorPtEtaPhiMass = ROOT::Math::PtEtaPhiMVector; + +enum TrackSelectionType { + AllTracks = 0, + GlobalTracks, + GlobalTracksWoPtEta, + GlobalTracksWoDCA, + QualityTracks, + InAcceptanceTracks, +}; + +enum PIDCutType { + SquareType = 1, + CircularType, +}; -struct kstar0analysis { +struct Kstar0analysis { + // Define slice per collision + Preslice perCollision = o2::aod::track::collisionId; SliceCache cache; - Preslice perCollision = aod::track::collisionId; + Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; + SliceCache cacheMC; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - //================================== - //|| - //|| Selection - //|| - //================================== - - // Event Selection - Configurable cfgEventSelections{"cfgEventSelections", "sel8", "Set event selection"}; - Configurable cfgEventVtxCut{"cfgEventVtxCut", 10.0, "V_z cut selection"}; - ConfigurableAxis cfgCentAxis{"cfgCentAxis", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}, "Binning of the centrality axis"}; - Configurable cfgOccupancySel{"cfgOccupancySel", false, "Occupancy selection"}; - Configurable cfgOccupancyMax{"cfgOccupancyMax", 999999., "maximum occupancy of tracks in neighbouring collisions in a given time range"}; - Configurable cfgOccupancyMin{"cfgOccupancyMin", -100., "minimum occupancy of tracks in neighbouring collisions in a given time range"}; - - // Track Selection - // General - Configurable cfgTrackMinPt{"cfgTrackMinPt", 0.15, "set track min pT"}; - Configurable cfgTrackMaxEta{"cfgTrackMaxEta", 0.8, "set track max Eta"}; - Configurable cfgTrackMaxDCArToPVcut{"cfgTrackMaxDCArToPVcut", 0.5, "Track DCAr cut to PV Maximum"}; - Configurable cfgTrackMaxDCAzToPVcut{"cfgTrackMaxDCAzToPVcut", 2.0, "Track DCAz cut to PV Maximum"}; - Configurable cfgTrackGlobalSel{"cfgTrackGlobalSel", true, "Global track selection"}; - Configurable cfgTrackPrimaryTrack{"cfgTrackPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgTrackConnectedToPV{"cfgTrackConnectedToPV", true, "PV contributor track selection"}; // PV Contriuibutor - Configurable cfgTrackGlobalWoDCATrack{"cfgTrackGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - - // TPC - Configurable cfgTrackFindableTPCClusters{"cfgTrackFindableTPCClusters", 50, "nFindable TPC Clusters"}; - Configurable cfgTrackTPCCrossedRows{"cfgTrackTPCCrossedRows", 70, "nCrossed TPC Rows"}; - Configurable cfgTrackRowsOverFindable{"cfgTrackRowsOverFindable", 1.2, "nRowsOverFindable TPC CLusters"}; - Configurable cfgTrackTPCChi2{"cfgTrackTPCChi2", 4.0, "nTPC Chi2 per Cluster"}; - - // ITS - Configurable cfgTrackITSChi2{"cfgTrackITSChi2", 36.0, "nITS Chi2 per Cluster"}; - - // PID - Configurable cfgTrackTPCPID{"cfgTrackTPCPID", true, "Enables TPC PID"}; - Configurable cfgTrackTOFPID{"cfgTrackTOFPID", true, "Enables TOF PID"}; - Configurable cfgTrackSquarePIDCut{"cfgTrackSqurePIDCut", true, "Enables PID cut shape square switch"}; - Configurable cfgTrackCirclePIDCut{"cfgTrackCirclePIDCut", true, "Enables PID cut shape circle switch"}; - Configurable cfgTrackCircleValue{"cfgTrackCircleValue", 2, "Enables TOF TPC PID circle cut value"}; - Configurable cfgTrackTOFHard{"cfgTrackTOFHard", false, "Enables TOF Hard"}; - - Configurable cfgTrackTPCPIDnSig{"cfgTrackTPCPIDnSig", 4.0, "nTPC PID sigma"}; - Configurable cfgTrackTOFPIDnSig{"cfgTrackTOFPIDnSig", 4.0, "nTOF PID sigma"}; - Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; - - // Mixing - ConfigurableAxis cfgBinsMixMult{"cfgBinsCent", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0}, "Binning of the centrality axis"}; - ConfigurableAxis cfgBinsMixVtx{"cfgBinsMixVtx", {VARIABLE_WIDTH, -10.0f, -5.f, 0.f, 5.f, 10.f}, "Mixing bins - z-vertex"}; - Configurable cfgMixNMixedEvents{"cfgMixNMixedEvents", 10, "Number of mixed events per event"}; - Configurable cfgVtxMixCut{"cfgVtxMixCut", 10, "Vertex Mix Cut"}; - - // MCGen - Configurable cfgForceGenReco{"cfgForceGenReco", false, "Only consider events which are reconstructed (neglect event-loss)"}; - Configurable cfgReqMcEffPID{"cfgReqMcEffPID", false, "Request McEfficiency PID"}; - - // Pair - Configurable cfgMinvNBins{"cfgMinvNBins", 300, "Number of bins for Minv axis"}; - Configurable cfgMinvMin{"cfgMinvMin", 0.60, "Minimum Minv value"}; - Configurable cfgMinvMax{"cfgMinvMax", 1.20, "Maximum Minv value"}; - - // Histogram - ConfigurableAxis binsDCAz{"binsDCAz", {40, -0.2, 0.2}, ""}; - ConfigurableAxis binsDCAxy{"binsDCAxy", {40, -0.2, 0.2}, ""}; - Configurable cfgEventCutQA{"cfgEventCutsQA", false, "Enable Event QA Hists"}; - Configurable cfgTrackCutQA{"cfgTrackCutQA", false, "Enable Track QA Hists"}; - - Configurable cfgMCHistos{"cfgMCHistos", false, "Enable MC Hists"}; - Configurable cfgMixedHistos{"cfgMixedHistos", false, "Enable Mixed Histos"}; - Configurable cfgManualEvSel{"cfgManualEvSel", false, "Enable Manual EvSel"}; - - // Main + + Service pdg; + RCTFlagsChecker rctChecker; + + struct : ConfigurableGroup { + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", false, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtIsRCTFlagpassed{"cfgEvtIsRCTFlagpassed", false, "Evt sel: apply RCT flag selection"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + Configurable cfgEvtSel8{"cfgEvtSel8", false, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtIsINELgt0{"cfgEvtIsINELgt0", false, "Evt sel: apply INEL>0 selection"}; + } configEvents; + + struct : ConfigurableGroup { + // Pre-selection Track cuts + Configurable trackSelection{"trackSelection", 0, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; + Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; + Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for particles"}; + Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; + + // DCA Selections + // DCAr to PV + Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1f, "Track DCAr cut to PV Maximum"}; + // DCAz to PV + Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1f, "Track DCAz cut to PV Maximum"}; + + // Track selections + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; + Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; + Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; + Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; + Configurable cDCAr7SigCut{"cDCAr7SigCut", false, "Track DCAr 7 Sigma cut to PV Maximum"}; + } configTracks; + + struct : ConfigurableGroup { + /// PID Selections + Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 4.0f, "pidnSigma Cut for pre-selection of tracks"}; + Configurable cByPassTOF{"cByPassTOF", false, "By pass TOF PID selection"}; // By pass TOF PID selection + Configurable cPIDcutType{"cPIDcutType", 2, "cPIDcutType = 1 for square cut, 2 for circular cut"}; // By pass TOF PID selection + Configurable ispTdepPID{"ispTdepPID", false, "enable pT dependent PID"}; + + // Kaon + Configurable> kaonTPCPIDpTintv{"kaonTPCPIDpTintv", {0.5f}, "pT intervals for Kaon TPC PID cuts"}; + Configurable> kaonTPCPIDcuts{"kaonTPCPIDcuts", {2}, "nSigma list for Kaon TPC PID cuts"}; + Configurable> kaonTOFPIDpTintv{"kaonTOFPIDpTintv", {999.0f}, "pT intervals for Kaon TOF PID cuts"}; + Configurable> kaonTOFPIDcuts{"kaonTOFPIDcuts", {2}, "nSigma list for Kaon TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedpTintv{"kaonTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Kaon TPC-TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedPIDcuts{"kaonTPCTOFCombinedPIDcuts", {2}, "nSigma list for Kaon TPC-TOF PID cuts"}; + + // Pion + Configurable> pionTPCPIDpTintv{"pionTPCPIDpTintv", {0.9f}, "pT intervals for Pion TPC PID cuts"}; + Configurable> pionTPCPIDcuts{"pionTPCPIDcuts", {2}, "nSigma list for Pion TPC PID cuts"}; + Configurable> pionTOFPIDpTintv{"pionTOFPIDpTintv", {999.0f}, "pT intervals for Pion TOF PID cuts"}; + Configurable> pionTOFPIDcuts{"pionTOFPIDcuts", {2}, "nSigma list for Pion TOF PID cuts"}; + Configurable> pionTPCTOFCombinedpTintv{"pionTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Pion TPC-TOF PID cuts"}; + Configurable> pionTPCTOFCombinedPIDcuts{"pionTPCTOFCombinedPIDcuts", {2}, "nSigma list for Pion TPC-TOF PID cuts"}; + } configPID; + + struct : ConfigurableGroup { + /// Event Mixing + Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.0f, -6.0f, -4.0f, -2.0f, 0.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f}, "Mixing bins - z-vertex"}; + ConfigurableAxis cfgMultPercentileBins{"cfgMultPercentileBins", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Mixing bins - multiplicity"}; + + // Rotational background + Configurable rotationalcut{"rotationalcut", 10, "Cut value (Rotation angle pi - pi/cut and pi + pi/cut)"}; + Configurable cNofRotations{"cNofRotations", 3, "Number of random rotations in the rotational background"}; + Configurable cfgRotPi{"cfgRotPi", true, "rotate Pion"}; + } configBkg; + + // Additional purity check + Configurable crejectProton{"crejectProton", false, "Switch to turn on/off proton contamination"}; + Configurable cUseOpeningAngleCut{"cUseOpeningAngleCut", false, "Kinematic Cuts for p-K pair opening angle"}; + Configurable cMinOpeningAngle{"cMinOpeningAngle", 1.4f, "Minimum opening angle between daughters"}; + Configurable cMaxOpeningAngle{"cMaxOpeningAngle", 2.4f, "Maximum opening angle between daughters"}; + Configurable cfgUseDeltaEtaPhiCuts{"cfgUseDeltaEtaPhiCuts", false, "Switch to turn on/off delta eta and delta phi cuts"}; + Configurable cfgUseDaughterEtaCutMC{"cfgUseDaughterEtaCutMC", false, "Switch to turn on/off eta cuts for daughters in MC"}; + + // MC selection cut + Configurable cUseRapcutMC{"cUseRapcutMC", true, "Use rapidity cut for MC"}; + + // cuts on mother + Configurable cfgUseCutsOnMother{"cfgUseCutsOnMother", false, "Enable additional cuts on mother"}; + Configurable cMaxPtMotherCut{"cMaxPtMotherCut", 10.0f, "Maximum pt of mother cut"}; + Configurable cMaxMinvMotherCut{"cMaxMinvMotherCut", 3.0f, "Maximum Minv of mother cut"}; + Configurable cMaxDeltaEtaCut{"cMaxDeltaEtaCut", 0.7f, "Maximum deltaEta between daughters"}; + Configurable cMaxDeltaPhiCut{"cMaxDeltaPhiCut", 1.5f, "Maximum deltaPhi between daughters"}; + + // switches + Configurable cFillMultQA{"cFillMultQA", false, "Turn on/off additional QA plots"}; + Configurable cFillTrackQA{"cFillTrackQA", false, "Turn on/off additional QA plots"}; + Configurable cFilladditionalQAeventPlots{"cFilladditionalQAeventPlots", false, "Additional QA event plots"}; + Configurable cFilladditionalMEPlots{"cFilladditionalMEPlots", false, "Additional Mixed event plots"}; + Configurable cFilldeltaEtaPhiPlots{"cFilldeltaEtaPhiPlots", false, "Enable additional cuts on daughters"}; + Configurable cFill1DQAs{"cFill1DQAs", false, "Invariant mass 1D"}; + Configurable centEstimator{"centEstimator", 0, "Select centrality estimator: 0 - FT0M, 1 - FT0A, 2 - FT0C"}; + + TRandom* rn = new TRandom(); + + // Pre-filters for efficient process + Filter zVtxFilter = (nabs(o2::aod::collision::posZ) <= configEvents.cfgEvtZvtx); + Filter collisionFilterMC = nabs(aod::mccollision::posZ) <= configEvents.cfgEvtZvtx; + + Filter acceptanceFilter = (nabs(aod::track::eta) < configTracks.cfgCutEta && nabs(aod::track::pt) > configTracks.cMinPtcut) && + (nabs(aod::track::dcaXY) < configTracks.cMaxDCArToPVcut) && (nabs(aod::track::dcaZ) < configTracks.cMaxDCAzToPVcut); + + using EventCandidates = soa::Join; + using TrackCandidates = soa::Filtered>; + + // for MC reco + using MCEventCandidates = soa::Join; + using MCTrackCandidates = soa::Filtered>; + + /// Figures + ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.12f, 0.14f, 0.16f, 0.18f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.5f, 0.55f, 0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1.0f, 1.1f, 1.2f, 1.25f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.75f, 1.8f, 1.9f, 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, 3.6f, 3.7f, 3.8f, 3.9f, 4.0f, 4.1f, 4.2f, 4.5f, 4.6f, 4.8f, 4.9f, 5.0f, 5.5f, 5.6f, 6.0f, 6.4f, 6.5f, 7.0f, 7.2f, 8.0f, 9.0f, 9.5f, 9.6f, 10.0f, 11.0f, 11.5f, 12.0f, 13.0f, 14.0f, 14.4f, 15.0f, 16.0f, 18.0f, 19.2f, 20.0f}, "Binning of the pT axis"}; + ConfigurableAxis binsPtQA{"binsPtQA", {VARIABLE_WIDTH, 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f, 1.2f, 1.4f, 1.6f, 1.8f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.2f, 4.4f, 4.6f, 4.8f, 5.0f, 5.2f, 5.4f, 5.6f, 5.8f, 6.0f, 6.2f, 6.4f, 6.6f, 6.8f, 7.0f, 7.2f, 7.4f, 7.6f, 7.8f, 8.0f, 8.2f, 8.4f, 8.6f, 8.8f, 9.0f, 9.2f, 9.4f, 9.6f, 9.8f, 10.0f, 10.2f, 10.4f, 10.6f, 10.8f, 11.0f, 11.2f, 11.4f, 11.6f, 11.8f, 12.0f, 12.2f, 12.4f, 12.6f, 12.8f, 13.0f, 13.2f, 13.4f, 13.6f, 13.8f, 14.0f, 14.2f, 14.4f, 14.6f, 14.8f, 15.0f, 15.2f, 15.4f, 15.6f, 15.8f, 16.0f, 16.2f, 16.4f, 16.6f, 16.8f, 17.0f, 17.2f, 17.4f, 17.6f, 17.8f, 18.0f, 18.2f, 18.4f, 18.6f, 18.8f, 19.0f, 19.2f, 19.4f, 19.6f, 19.8f, 20.0f}, "Binning of the pT axis"}; + ConfigurableAxis binsEta{"binsEta", {150, -1.5f, 1.5f}, ""}; + ConfigurableAxis binsMass{"binsMass", {300, 0.6f, 1.2f}, "Invariant Mass (GeV/#it{c}^2)"}; + ConfigurableAxis binsMult{"binsMult", {105, 0.0f, 105.0f}, "mult_{FT0M}"}; + ConfigurableAxis binsDCAz{"binsDCAz", {40, -0.2f, 0.2f}, ""}; + ConfigurableAxis binsDCAxy{"binsDCAxy", {40, -0.2f, 0.2f}, ""}; + ConfigurableAxis binsTPCXrows{"binsTPCXrows", {100, 60, 160}, ""}; + ConfigurableAxis binsnSigma{"binsnSigma", {130, -6.5f, 6.5f}, ""}; + ConfigurableAxis binsnTPCSignal{"binsnTPCSignal", {1000, 0, 1000}, ""}; + ConfigurableAxis binsEtaPhi{"binsEtaPhi", {350, -3.5f, 3.5f}, ""}; + void init(o2::framework::InitContext&) { - // HISTOGRAMS - const AxisSpec axisEta{30, -1.5, +1.5, "#eta"}; - const AxisSpec axisPhi{200, -1, +7, "#phi"}; - const AxisSpec ptAxis = {200, 0, 20.0}; - const AxisSpec pidAxis = {120, -6, 6}; - const AxisSpec minvAxis = {cfgMinvNBins, cfgMinvMin, cfgMinvMax}; - const AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; - const AxisSpec axisDCAxy{binsDCAxy, "DCA_{XY}"}; - - if (cfgEventCutQA) { - histos.add("hEvent_Cut", "Number of event after cuts", kTH1D, {{13, -0.5, 12.5}}); - histos.add("hPosZ_BC", "hPosZ_BC", kTH1F, {{300, -15.0, 15.0}}); - histos.add("hPosZ_AC", "hPosZ_AC", kTH1F, {{300, -15.0, 15.0}}); - histos.add("hcentFT0C_BC", "centFT0C_BC", kTH1F, {{110, 0.0, 110.0}}); - histos.add("hcentFT0C_AC", "centFT0C_AC", kTH1F, {{110, 0.0, 110.0}}); - } - if (cfgTrackCutQA) { - histos.add("hDCArToPv_BC", "DCArToPv_BC", kTH1F, {axisDCAxy}); - histos.add("hDCAzToPv_BC", "DCAzToPv_BC", kTH1F, {axisDCAz}); - histos.add("hIsPrim_BC", "hIsPrim_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsGood_BC", "hIsGood_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsPrimCont_BC", "hIsPrimCont_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hFindableTPCClusters_BC", "hFindableTPCClusters_BC", kTH1F, {{200, 0, 200}}); - histos.add("hFindableTPCRows_BC", "hFindableTPCRows_BC", kTH1F, {{200, 0, 200}}); - histos.add("hClustersVsRows_BC", "hClustersVsRows_BC", kTH1F, {{200, 0, 2}}); - histos.add("hTPCChi2_BC", "hTPCChi2_BC", kTH1F, {{200, 0, 100}}); - histos.add("hITSChi2_BC", "hITSChi2_BC", kTH1F, {{200, 0, 100}}); - histos.add("QA_nSigma_pion_TPC_BC", "QA_nSigma_pion_TPC_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_pion_TOF_BC", "QA_nSigma_pion_TOF_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_pion_TPC_TOF_BC", "QA_pion_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TPC_BC", "QA_nSigma_kaon_TPC_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TOF_BC", "QA_nSigma_kaon_TOF_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_kaon_TPC_TOF_BC", "QA_kaon_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_track_pT_BC", "QA_track_pT_BC", kTH1F, {{13, 0.0, 13.0}}); - - histos.add("hDCArToPv_AC", "DCArToPv_AC", kTH1F, {axisDCAxy}); - histos.add("hDCAzToPv_AC", "DCAzToPv_AC", kTH1F, {axisDCAz}); - histos.add("hIsPrim_AC", "hIsPrim_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsGood_AC", "hIsGood_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsPrimCont_AC", "hIsPrimCont_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hFindableTPCClusters_AC", "hFindableTPCClusters_AC", kTH1F, {{200, 0, 200}}); - histos.add("hFindableTPCRows_AC", "hFindableTPCRows_AC", kTH1F, {{200, 0, 200}}); - histos.add("hClustersVsRows_AC", "hClustersVsRows_AC", kTH1F, {{200, 0, 2}}); - histos.add("hTPCChi2_AC", "hTPCChi2_AC", kTH1F, {{200, 0, 100}}); - histos.add("hITSChi2_AC", "hITSChi2_AC", kTH1F, {{200, 0, 100}}); - histos.add("QA_nSigma_pion_TPC_AC", "QA_nSigma_pion_TPC_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_pion_TOF_AC", "QA_nSigma_pion_TOF_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_pion_TPC_TOF_AC", "QA_pion_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TPC_AC", "QA_nSigma_kaon_TPC_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TOF_AC", "QA_nSigma_kaon_TOF_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_kaon_TPC_TOF_AC", "QA_kaon_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_track_pT_AC", "QA_track_pT_AC", kTH1F, {{13, 0.0, 13.0}}); - } - - //////////////////////////////////// - histos.add("nEvents", "nEvents", kTH1F, {{7, 0.0, 7.0}}); - histos.add("hUSS_KPi", "hUSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hUSS_PiK", "hUSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hLSS_KPi", "hLSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hLSS_PiK", "hLSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - - if (cfgMixedHistos) { - histos.add("hUSS_KPi_Mix", "hUSS_KPi_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hUSS_PiK_Mix", "hUSS_PiK_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - } - if (cfgMCHistos) { - histos.add("nEvents_Gen", "nEvents_Gen", kTH1F, {{4, 0.0, 4.0}}); - histos.add("hUSS_TrueRec", "hUSS_TrueRec", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hGen_pT_Raw", "Gen_pT_Raw (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hGen_pT_GoodEv", "hGen_pT_GoodEv", kTHnSparseF, {cfgCentAxis, ptAxis}); - } - - if (cfgEventCutQA) { - std::shared_ptr hCutFlow = histos.get(HIST("hEvent_Cut")); - std::vector eventCutLabels = { - "All Events", - "sel8", - Form("|Vz| < %.1f", cfgEventVtxCut.value), - "kIsGoodZvtxFT0vsPV", - "kNoSameBunchPileup", - "kNoTimeFrameBorder", - "kNoITSROFrameBorder", - "kNoCollInTimeRangeStandard", - "kIsGoodITSLayersAll", - Form("Occupancy < %.0f", cfgOccupancyMax.value), - "All passed events"}; - - for (size_t i = 0; i < eventCutLabels.size(); ++i) { - hCutFlow->GetXaxis()->SetBinLabel(i + 1, eventCutLabels[i].c_str()); + rctChecker.init(configEvents.cfgEvtRCTFlagCheckerLabel, configEvents.cfgEvtRCTFlagCheckerZDCCheck, configEvents.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + // axes + AxisSpec axisPt{binsPt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisPtQA{binsPtQA, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisEta{binsEta, "#eta"}; + AxisSpec axisRap{binsEta, "#it{y}"}; + AxisSpec axisMassk892{binsMass, "Invariant Mass (GeV/#it{c}^2)"}; + AxisSpec axisMult{binsMult, "mult_{FT0M}"}; + AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; + AxisSpec axisDCAxy{binsDCAxy, "DCA_{XY}"}; + AxisSpec axisTPCXrow{binsTPCXrows, "#Xrows_{TPC}"}; + AxisSpec axisPIDQA{binsnSigma, "#sigma"}; + AxisSpec axisTPCSignal{binsnTPCSignal, ""}; + AxisSpec axisEtaPhi{binsEtaPhi, ""}; + AxisSpec axisPhi{350, 0, 7, "#Phi"}; + AxisSpec axisMultMix{configBkg.cfgMultPercentileBins, "Multiplicity Percentile"}; + AxisSpec axisVtxMix{configBkg.cfgVtxBins, "Vertex Z (cm)"}; + + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "rctChecker"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "IsINELgt0"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); + + histos.add("Event/posZ", "; vtx_{z} (cm); Entries", HistType::kTH1F, {{250, -12.5, 12.5}}); + histos.add("Event/centFT0M", "; FT0M Percentile; Entries", HistType::kTH1F, {{110, 0, 110}}); + + if (cFilladditionalQAeventPlots) { + // event histograms + if (doprocessData) { + histos.add("QAevent/hPairsCounterSameE", "total valid no. of pairs sameE", HistType::kTH1F, {{1, 0.5f, 1.5f}}); + histos.add("QAevent/hnTrksSameE", "n tracks per event SameE", HistType::kTH1F, {{1000, 0.0, 1000.0}}); + } + // Gen on Mixed event + if (doprocessME) { + + // Histograms for Mixed Event Pool characteristics + histos.add("QAevent/hMixPool_VtxZ", "Mixed Event Pool: Vertex Z;Vtx Z (cm);Counts", HistType::kTH1F, {axisVtxMix}); + histos.add("QAevent/hMixPool_Multiplicity", "Mixed Event Pool: Multiplicity;Multiplicity;Counts", HistType::kTH1F, {axisMultMix}); + histos.add("QAevent/hMixPool_VtxZ_vs_Multiplicity", "Mixed Event Pool: Vertex Z vs Multiplicity;Counts", HistType::kTH2F, {axisVtxMix, axisMultMix}); + + histos.add("QAevent/hPairsCounterMixedE", "total valid no. of pairs mixedE", HistType::kTH1F, {{1, 0.5f, 1.5f}}); + histos.add("QAevent/hVertexZMixedE", "Collision Vertex Z position", HistType::kTH1F, {{100, -15.0f, 15.0f}}); + histos.add("QAevent/hMultiplicityPercentMixedE", "Multiplicity percentile of collision", HistType::kTH1F, {{120, 0.0f, 120.0f}}); + histos.add("QAevent/hnTrksMixedE", "n tracks per event MixedE", HistType::kTH1F, {{1000, 0.0f, 1000.0f}}); } } - } // end of init - //====================== - //|| For LF Analysis - //====================== - using EventCandidates = soa::Join; //, aod::CentFT0Ms, aod::CentFT0As - using EventCandidatesTrue = aod::McCollisions; - using TrackCandidates = soa::Join; - using TrackCandidatesMC = soa::Join; - - // For Mixed Event - using BinningType = ColumnBinningPolicy; - - Partition kaon = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pion = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - Partition kaonMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pionMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - - double massKa = o2::constants::physics::MassKPlus; - double massPi = o2::constants::physics::MassPiMinus; - - //================================== - //|| - //|| Helper Templates - //|| - //================================== - template - void fillQA(const bool pass, const objType& obj, const int objecttype = 0) - { - if (objecttype == 1) { - if constexpr (requires { obj.posZ(); }) { - if (!pass) { - histos.fill(HIST("hPosZ_BC"), obj.posZ()); - histos.fill(HIST("hcentFT0C_BC"), obj.centFT0C()); - } else { - histos.fill(HIST("hPosZ_AC"), obj.posZ()); - histos.fill(HIST("hcentFT0C_AC"), obj.centFT0C()); - } + if (doprocessData) { + // Track QA before cuts + // --- Track + if (cFillTrackQA) { + histos.add("QA/QAbefore/Track/TOF_TPC_Map_ka_all", "TOF + TPC Combined PID for all Kaon;{#sigma_{TOF}^{Kaon}};{#sigma_{TPC}^{Kaon}}", {HistType::kTH2F, {axisPIDQA, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TOF_Nsigma_ka_all", "TOF NSigma for all Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TPC_Nsigma_ka_all", "TPC NSigma for all Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TPConly_Nsigma_ka_all", "TPC NSigma for all Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTH2F, {axisPt, axisPIDQA}}); + + histos.add("QA/QAbefore/Track/TOF_TPC_Map_pi_all", "TOF + TPC Combined PID for all Pion;{#sigma_{TOF}^{Pion}};{#sigma_{TPC}^{Pion}}", {HistType::kTH2F, {axisPIDQA, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TOF_Nsigma_pi_all", "TOF NSigma for all Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Pion}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TPC_Nsigma_pi_all", "TPC NSigma for all Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Pion}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TPConly_Nsigma_pi_all", "TPC NSigma for all Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Pion}};", {HistType::kTH2F, {axisPt, axisPIDQA}}); + + histos.add("QA/QAbefore/Track/dcaZ_all", "DCA_{Z} distribution of all Tracks; #it{p}_{T} (GeV/#it{c}); DCA_{Z} (cm); ", HistType::kTH2F, {axisPt, axisDCAz}); + histos.add("QA/QAbefore/Track/dcaXY_all", "DCA_{XY} momentum distribution of all Tracks; #it{p}_{T} (GeV/#it{c}); DCA_{XY} (cm);", HistType::kTH2F, {axisPt, axisDCAxy}); + histos.add("QA/QAbefore/Track/TPC_CR_all", "# TPC Xrows distribution of all Tracks; #it{p}_{T} (GeV/#it{c}); TPC X rows", HistType::kTH2F, {axisPt, axisTPCXrow}); + histos.add("QA/QAbefore/Track/pT_all", "pT distribution of all Tracks; #it{p}_{T} (GeV/#it{c}); Counts;", {HistType::kTH1F, {axisPt}}); + histos.add("QA/QAbefore/Track/eta_all", "#eta distribution of all Tracks; #eta; Counts;", {HistType::kTH1F, {axisEta}}); } - } // eventSelection histogram - - if constexpr (requires { obj.tpcCrossedRowsOverFindableCls(); }) { - if (objecttype == 3) { - if (!pass) { - histos.fill(HIST("hDCArToPv_BC"), obj.dcaXY()); - histos.fill(HIST("hDCAzToPv_BC"), obj.dcaZ()); - histos.fill(HIST("hIsPrim_BC"), obj.isPrimaryTrack()); - histos.fill(HIST("hIsGood_BC"), obj.isGlobalTrackWoDCA()); - histos.fill(HIST("hIsPrimCont_BC"), obj.isPVContributor()); - histos.fill(HIST("hFindableTPCClusters_BC"), obj.tpcNClsFindable()); - histos.fill(HIST("hFindableTPCRows_BC"), obj.tpcNClsCrossedRows()); - histos.fill(HIST("hClustersVsRows_BC"), obj.tpcCrossedRowsOverFindableCls()); - histos.fill(HIST("hTPCChi2_BC"), obj.tpcChi2NCl()); - histos.fill(HIST("hITSChi2_BC"), obj.itsChi2NCl()); - histos.fill(HIST("QA_track_pT_BC"), obj.pt()); - } else { - histos.fill(HIST("hDCArToPv_AC"), obj.dcaXY()); - histos.fill(HIST("hDCAzToPv_AC"), obj.dcaZ()); - histos.fill(HIST("hIsPrim_AC"), obj.isPrimaryTrack()); - histos.fill(HIST("hIsGood_AC"), obj.isGlobalTrackWoDCA()); - histos.fill(HIST("hIsPrimCont_AC"), obj.isPVContributor()); - histos.fill(HIST("hFindableTPCClusters_AC"), obj.tpcNClsFindable()); - histos.fill(HIST("hFindableTPCRows_AC"), obj.tpcNClsCrossedRows()); - histos.fill(HIST("hClustersVsRows_AC"), obj.tpcCrossedRowsOverFindableCls()); - histos.fill(HIST("hTPCChi2_AC"), obj.tpcChi2NCl()); - histos.fill(HIST("hITSChi2_AC"), obj.itsChi2NCl()); - histos.fill(HIST("QA_track_pT_AC"), obj.pt()); - } + if (cFillMultQA) { + // Multiplicity correlation calibrations + histos.add("MultCalib/centGloPVpi", "Centrality vs Global-Tracks", kTHnSparseF, {{110, 0, 110, "Centrality"}, {500, 0, 5000, "Global Tracks"}, {500, 0, 5000, "PV tracks"}}); + histos.add("MultCalib/centGloPVka", "Centrality vs Global-Tracks", kTHnSparseF, {{110, 0, 110, "Centrality"}, {500, 0, 5000, "Global Tracks"}, {500, 0, 5000, "PV tracks"}}); } - } // trackSelection - if (objecttype == 4) { - if constexpr (requires { obj.pt(); }) { - if (!pass) { - histos.fill(HIST("QA_nSigma_kaon_TPC_BC"), obj.pt(), obj.tpcNSigmaKa()); - histos.fill(HIST("QA_nSigma_kaon_TOF_BC"), obj.pt(), obj.tofNSigmaKa()); - histos.fill(HIST("QA_kaon_TPC_TOF_BC"), obj.tpcNSigmaKa(), obj.tofNSigmaKa()); - } else { - histos.fill(HIST("QA_nSigma_kaon_TPC_AC"), obj.pt(), obj.tpcNSigmaKa()); - histos.fill(HIST("QA_nSigma_kaon_TOF_AC"), obj.pt(), obj.tofNSigmaKa()); - histos.fill(HIST("QA_kaon_TPC_TOF_AC"), obj.tpcNSigmaKa(), obj.tofNSigmaKa()); - } + + // PID QA after cuts + // --- Kaon + histos.add("QA/QAafter/Kaon/TOF_TPC_Map_ka_selected", "TOF + TPC Combined PID for selected Kaon;{#sigma_{TOF}^{Kaon}};{#sigma_{TPC}^{Kaon}}", {HistType::kTH2F, {axisPIDQA, axisPIDQA}}); + histos.add("QA/QAafter/Kaon/TOF_Nsigma_ka_selected", "TOF NSigma for selected Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Kaon/TPC_Nsigma_ka_selected", "TPC NSigma for selected Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Kaon/TPC_Nsigma_ka_TPConly_selected", "TPC NSigma for selected Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTH2F, {axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Kaon/dcaZ_selected", "DCA_{Z} distribution of selected Kaons; #it{p}_{T} (GeV/#it{c}); DCA_{Z} (cm); ", HistType::kTH2F, {axisPt, axisDCAz}); + histos.add("QA/QAafter/Kaon/dcaXY_selected", "DCA_{XY} momentum distribution of selected Kaons; #it{p}_{T} (GeV/#it{c}); DCA_{XY} (cm);", HistType::kTH2F, {axisPt, axisDCAxy}); + histos.add("QA/QAafter/Kaon/TPC_CR_selected", "# TPC Xrows distribution of selected Kaons; #it{p}_{T} (GeV/#it{c}); TPC X rows", HistType::kTH2F, {axisPt, axisTPCXrow}); + histos.add("QA/QAafter/Kaon/pT_selected", "pT distribution of selected Kaons; #it{p}_{T} (GeV/#it{c}); Counts;", {HistType::kTH1F, {axisPt}}); + histos.add("QA/QAafter/Kaon/eta_selected", "#eta distribution of selected Kaons; #eta; Counts;", {HistType::kTH1F, {axisEta}}); + histos.add("QA/QAafter/Kaon/TPC_Signal_ka_selected", "TPC Signal for selected Kaon;#it{p} (GeV/#it{c});TPC Signal (A.U.)", {HistType::kTH2F, {axisPt, axisTPCSignal}}); + histos.add("QA/QAafter/Kaon/TPCnclusterPhika_selected", "TPC ncluster vs phi for selected Kaon", kTHnSparseF, {{160, 0, 160, "TPC nCluster"}, {63, 0.0f, 6.28f, "#phi"}}); + + // --- Pion + histos.add("QA/QAafter/Pion/TOF_TPC_Map_pi_selected", "TOF + TPC Combined PID for selected Pion;{#sigma_{TOF}^{Pion}};{#sigma_{TPC}^{Pion}}", {HistType::kTH2F, {axisPIDQA, axisPIDQA}}); + histos.add("QA/QAafter/Pion/TOF_Nsigma_pi_selected", "TOF NSigma for selected Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Pion}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Pion/TPC_Nsigma_pi_selected", "TPC NSigma for selected Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Pion}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Pion/TPC_Nsigma_pi_TPConly_selected", "TPC NSigma for selected Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Pion}};", {HistType::kTH2F, {axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Pion/dcaZ_selected", "DCA_{Z} distribution of selected Pions; #it{p}_{T} (GeV/#it{c}); DCA_{Z} (cm);", HistType::kTH2F, {axisPt, axisDCAz}); + histos.add("QA/QAafter/Pion/dcaXY_selected", "DCA_{XY} momentum distribution of selected Pions; #it{p}_{T} (GeV/#it{c}); DCA_{XY} (cm);", HistType::kTH2F, {axisPt, axisDCAxy}); + histos.add("QA/QAafter/Pion/TPC_CR_selected", "# TPC Xrows distribution of selected Pions; #it{p}_{T} (GeV/#it{c}); TPC X rows", HistType::kTH2F, {axisPt, axisTPCXrow}); + histos.add("QA/QAafter/Pion/pT_selected", "pT distribution of selected Pions; #it{p}_{T} (GeV/#it{c}); Counts;", {HistType::kTH1F, {axisPt}}); + histos.add("QA/QAafter/Pion/eta_selected", "#eta distribution of selected Pions; #eta; Counts;", {HistType::kTH1F, {axisEta}}); + histos.add("QA/QAafter/Pion/TPC_Signal_pi_selected", "TPC Signal for selected Pion;#it{p} (GeV/#it{c});TPC Signal (A.U.)", {HistType::kTH2F, {axisPt, axisTPCSignal}}); + histos.add("QA/QAafter/Pion/TPCnclusterPhipi_selected", "TPC ncluster vs phi for selected Pion", kTHnSparseF, {{160, 0, 160, "TPC nCluster"}, {63, 0.0f, 6.28f, "#phi"}}); + + // Mass QA 1D for quick check + if (cFill1DQAs) { + histos.add("Result/Data/k892invmass", "Invariant mass of K(892) K^{#pm}#pi^{#mp}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); + histos.add("Result/Data/antik892invmass", "Invariant mass of K(892) K^{#mp}#pi^{#pm}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); + histos.add("Result/Data/k892invmassLSPP", "Invariant mass of K(892) Like Sign Method K^{#plus}#pi^{#plus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); // K+ + Pi + histos.add("Result/Data/k892invmassLSMM", "Invariant mass of K(892) Like Sign Method K^{#minus}#pi^{#minus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); // K- + anti-Pi } - } // kaon pid Selection - if (objecttype == 5) { - if constexpr (requires { obj.pt(); }) { - if (!pass) { - histos.fill(HIST("QA_nSigma_pion_TPC_BC"), obj.pt(), obj.tpcNSigmaPi()); - histos.fill(HIST("QA_nSigma_pion_TOF_BC"), obj.pt(), obj.tofNSigmaPi()); - histos.fill(HIST("QA_pion_TPC_TOF_BC"), obj.tpcNSigmaPi(), obj.tofNSigmaPi()); - } else { - histos.fill(HIST("QA_nSigma_pion_TPC_AC"), obj.pt(), obj.tpcNSigmaPi()); - histos.fill(HIST("QA_nSigma_pion_TOF_AC"), obj.pt(), obj.tofNSigmaPi()); - histos.fill(HIST("QA_pion_TPC_TOF_AC"), obj.tpcNSigmaPi(), obj.tofNSigmaPi()); + // eta phi QA + if (cFilldeltaEtaPhiPlots) { + histos.add("QAbefore/deltaEta", "deltaEta of kaon and Pion candidates", HistType::kTH1F, {axisEtaPhi}); + histos.add("QAbefore/deltaPhi", "deltaPhi of kaon and Pion candidates", HistType::kTH1F, {axisEtaPhi}); + + histos.add("QAafter/deltaEta", "deltaEta of kaon and Pion candidates", HistType::kTH1F, {axisEtaPhi}); + histos.add("QAafter/deltaPhi", "deltaPhi of kaon and Pion candidates", HistType::kTH1F, {axisEtaPhi}); + + histos.add("QAafter/PhiPiafter", "Phi of Pion candidates", HistType::kTH1F, {axisPhi}); + histos.add("QAafter/PhiKaafter", "Phi of kaon candidates", HistType::kTH1F, {axisPhi}); + } + + // 3d histogram + histos.add("Result/Data/h3k892invmass", "Invariant mass of K(892) K^{#pm}#pi^{#mp}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); + histos.add("Result/Data/h3antik892invmass", "Invariant mass of K(892) K^{#mp}#pi^{#pm}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); + histos.add("Result/Data/h3k892invmassLSPP", "Invariant mass of K(892) Like Sign Method K^{#plus}#pi^{#plus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); // K+ + Pi + histos.add("Result/Data/h3k892invmassLSMM", "Invariant mass of K(892) Like Sign Method K^{#minus}#pi^{#minus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); // K- + anti-Pi + } + + if (doprocessRotational) { + if (cFill1DQAs) { + histos.add("Result/Data/k892InvMassRotation", "Invariant mass of K(892) Like Sign Method K^{#plus}#pi^{#plus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); // K+ + Pi + histos.add("Result/Data/antik892InvMassRotation", "Invariant mass of K(892) Like Sign Method K^{#minus}#pi^{#minus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); // K- + anti-Pi + } + histos.add("Result/Data/h3k892InvMassRotation", "Invariant mass of K(892) rotation", kTHnSparseF, {axisMult, axisPt, axisMassk892}); + histos.add("Result/Data/h3antik892InvMassRotation", "Invariant mass of K(892) rotation", kTHnSparseF, {axisMult, axisPt, axisMassk892}); + } + // Mixed event histograms + if (doprocessME) { + if (cFill1DQAs) { + histos.add("Result/Data/k892invmassME_UnlikeSign", "Invariant mass of K(892) mixed event K^{#pm}#pi^{#mp}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); + histos.add("Result/Data/antik892invmassME_UnlikeSign", "Invariant mass of K(892) mixed event K^{#pm}#pi^{#mp}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); + } + histos.add("Result/Data/h3k892invmassME_UnlikeSign", "Invariant mass of K(892) mixed event K^{#pm}#pi^{#mp}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); + histos.add("Result/Data/h3antik892invmassME_UnlikeSign", "Invariant mass of K(892) mixed event K^{#pm}#pi^{#mp}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); + + if (cFilladditionalMEPlots) { + if (cFill1DQAs) { + histos.add("Result/Data/k892invmassME_LSPP", "Invariant mass of K(892) Like Sign Method K^{#plus}#pi^{#plus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); // K+ + Pi + histos.add("Result/Data/k892invmassME_LSMM", "Invariant mass of K(892) Like Sign Method K^{#minus}#pi^{#minus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassk892}}); // K- + anti-Pi } + histos.add("Result/Data/h3k892invmassME_LSPP", "Invariant mass of K(892) mixed event Like Sign Method K^{#plus}#pi^{#plus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); // K+ + Pi + histos.add("Result/Data/h3k892invmassME_LSMM", "Invariant mass of K(892) mixed event Like Sign Method K^{#minus}#pi^{#minus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassk892}); // K- + anti-Pi } - } // pion pid Selection - } // fill QA + } - enum class objectType { MB, - MBRecParticle }; + // MC QA + if (doprocessEventFactor) { + histos.add("Event/MultiplicityGenEv", "hMCEventIndices", kTH1D, {axisMult}); + histos.add("Event/MultiplicityRecoEv", "Multiplicity of Reconstructed Events", kTH1D, {axisMult}); + } - template - void fillMinv(objectType type, const TrackType& trk1, const TrackType& trk2, const ROOT::Math::PxPyPzMVector& lReso, double centrality, bool IsMix, bool flip) + if (doprocessMCGen) { + histos.add("QA/MC/h2GenEtaPt_beforeanycut", " #eta-#it{p}_{T} distribution of Generated K(892); #eta; #it{p}_{T}; Counts;", HistType::kTHnSparseF, {axisEta, axisPtQA}); + histos.add("QA/MC/h2GenPhiRapidity_beforeanycut", " #phi-y distribution of Generated K(892); #phi; y; Counts;", HistType::kTHnSparseF, {axisPhi, axisRap}); + histos.add("QA/MC/h2GenEtaPt_afterRapcut", " #phi-#it{p}_{T} distribution of Generated K(892); #eta; #it{p}_{T}; Counts;", HistType::kTHnSparseF, {axisEta, axisPtQA}); + histos.add("QA/MC/h2GenPhiRapidity_afterRapcut", " #phi-y distribution of Generated K(892); #phi; y; Counts;", HistType::kTHnSparseF, {axisPhi, axisRap}); + + histos.add("Result/MC/Genk892pt", "pT distribution of True MC K(892)0", kTH2F, {axisPt, axisMult}); + histos.add("Result/MC/Genantik892pt", "pT distribution of True MC Anti-K(892)0", kTH2F, {axisPt, axisMult}); + } + + if (doprocessMCRec) { + histos.add("QA/MC/h2RecoEtaPt_after", " #eta-#it{p}_{T} distribution of Reconstructed K(892); #eta; #it{p}_{T}; Counts;", HistType::kTHnSparseF, {axisEta, axisPt}); + histos.add("QA/MC/h2RecoPhiRapidity_after", " #phi-y distribution of Reconstructed K(892); #phi; y; Counts;", HistType::kTHnSparseF, {axisPhi, axisRap}); + + histos.add("QA/MC/trkDCAxy_pi", "DCAxy distribution of Pion track candidates", HistType::kTHnSparseF, {axisPt, axisDCAxy}); + histos.add("QA/MC/trkDCAxy_ka", "DCAxy distribution of kaon track candidates", HistType::kTHnSparseF, {axisPt, axisDCAxy}); + histos.add("QA/MC/trkDCAz_pi", "DCAz distribution of Pion track candidates", HistType::kTHnSparseF, {axisPt, axisDCAz}); + histos.add("QA/MC/trkDCAz_ka", "DCAz distribution of kaon track candidates", HistType::kTHnSparseF, {axisPt, axisDCAz}); + histos.add("QA/MC/TOF_Nsigma_pi_all", "TOF NSigma for Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Pion}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/MC/TPC_Nsigma_pi_all", "TPC NSigma for Pion;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Pion}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/MC/TOF_Nsigma_ka_all", "TOF NSigma for Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/MC/TPC_Nsigma_ka_all", "TPC NSigma for Kaon;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + + histos.add("Result/MC/h3k892Recoinvmass", "Invariant mass of Reconstructed MC K(892)0", kTHnSparseF, {axisMult, axisPt, axisMassk892}); + histos.add("Result/MC/h3antik892Recoinvmass", "Invariant mass of Reconstructed MC Anti-K(892)0", kTHnSparseF, {axisMult, axisPt, axisMassk892}); + } + + if (doprocessSignalLoss) { + histos.add("Result/SignalLoss/GenTruek892pt_den", "True k892 (den)", kTH2F, {axisPt, axisMult}); + histos.add("Result/SignalLoss/GenTrueantik892pt_den", "True anti-k892 (den)", kTH2F, {axisPt, axisMult}); + + histos.add("Result/SignalLoss/GenTruek892pt_num", "True k892 (num)", kTH2F, {axisPt, axisMult}); + histos.add("Result/SignalLoss/GenTrueantik892pt_num", "True anti-k892 (num)", kTH2F, {axisPt, axisMult}); + } + + // Print output histograms statistics + LOG(info) << "Size of the histograms in Kstar0analysis:"; + histos.print(); + } // end of init + + float massKa = MassKaonCharged; + float massPi = MassPionCharged; + + // Centralicity estimator selection + template + float centEst(const Coll& collisions) { - double conjugate = trk1.sign() * trk2.sign(); - switch (type) { - case objectType::MB: - if (IsMix && cfgMixedHistos) { - if (conjugate < 0) { - if (!flip) - histos.fill(HIST("hUSS_KPi_Mix"), centrality, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hUSS_PiK_Mix"), centrality, lReso.Pt(), lReso.M()); - } - } else { - if (conjugate < 0) { - if (!flip) - histos.fill(HIST("hUSS_KPi"), centrality, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hUSS_PiK"), centrality, lReso.Pt(), lReso.M()); - } else if (conjugate > 0) { - if (!flip) - histos.fill(HIST("hLSS_KPi"), centrality, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hLSS_PiK"), centrality, lReso.Pt(), lReso.M()); - } - } + float returnValue = -999.0f; + switch (centEstimator) { + case 0: + returnValue = collisions.centFT0M(); break; - - case objectType::MBRecParticle: - if (cfgMCHistos) { - if (conjugate < 0) { - histos.fill(HIST("hUSS_TrueRec"), centrality, lReso.Pt(), lReso.M()); - } - } + case 1: + returnValue = collisions.centFT0A(); + break; + case 2: + returnValue = collisions.centFT0C(); break; - } // switch - } // fillMinv - //====================================================================== + default: + returnValue = collisions.centFT0M(); + break; + } + return returnValue; + } - template - std::pair eventSelection(const EventType event, const bool QA) - { - if (cfgEventCutQA && QA) { - fillQA(false, event, 1); - histos.fill(HIST("hEvent_Cut"), 0); - } - - if (!event.sel8()) - return {false, 1}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 1); - } - if (std::abs(event.posZ()) > cfgEventVtxCut) - return {false, 2}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 2); - } - if (!event.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) - return {false, 3}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 3); - } - if (!event.selection_bit(aod::evsel::kNoSameBunchPileup)) - return {false, 4}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 4); - } - if (!event.selection_bit(aod::evsel::kNoTimeFrameBorder)) - return {false, 5}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 5); - } - if (!event.selection_bit(aod::evsel::kNoITSROFrameBorder)) - return {false, 6}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 6); - } - if (!event.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) - return {false, 7}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 7); - } - if (!event.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) - return {false, 8}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 8); - } - if (cfgOccupancySel && (event.trackOccupancyInTimeRange() > cfgOccupancyMax || event.trackOccupancyInTimeRange() < cfgOccupancyMin)) - return {false, 9}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 9); - } - - if (cfgEventCutQA && QA) { - fillQA(true, event, 1); - histos.fill(HIST("hEvent_Cut"), 10); - } - return {true, 11}; - }; - - template - bool trackSelection(const TracksType track, const bool QA) + template + bool isSelected(const Coll& collision, bool fillHist = true) { - if (cfgTrackCutQA && QA) { - fillQA(false, track, 3); - } + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) + return true; + if (!condition) + return false; + if (fillHist) + histos.fill(HIST("CollCutCounts"), bin); + return true; + }; - if (cfgTrackGlobalSel && !track.isGlobalTrack()) + if (fillHist) + histos.fill(HIST("CollCutCounts"), 0); + + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) return false; - if (track.pt() < cfgTrackMinPt) + + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) return false; - if (std::abs(track.eta()) > cfgTrackMaxEta) + + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) return false; - if (std::abs(track.dcaXY()) > cfgTrackMaxDCArToPVcut) + + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) return false; - if (std::abs(track.dcaZ()) > cfgTrackMaxDCAzToPVcut) + + if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) + return false; + + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) + return false; + + if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) return false; - if (cfgTrackPrimaryTrack && !track.isPrimaryTrack()) + + if (fillHist) + histos.fill(HIST("CollCutCounts"), 8); + + return true; + } + + template + bool trackCut(const TrackType& track) + { + // basic track cuts + if (configTracks.cDCAr7SigCut && std::abs(track.dcaXY()) > (0.004f + 0.013f / (track.pt()))) // 7 - Sigma cut return false; - if (cfgTrackGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) + if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) return false; - if (cfgTrackFindableTPCClusters > 0 && track.tpcNClsFindable() < cfgTrackFindableTPCClusters) + if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) return false; - if (track.tpcNClsCrossedRows() < cfgTrackTPCCrossedRows) + if (configTracks.cfgHasTOF && !track.hasTOF()) return false; - if (cfgTrackRowsOverFindable > 0 && track.tpcCrossedRowsOverFindableCls() > cfgTrackRowsOverFindable) + if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) return false; - if (track.tpcChi2NCl() > cfgTrackTPCChi2) + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) return false; - if (track.itsChi2NCl() > cfgTrackITSChi2) + if (configTracks.cfgPVContributor && !track.isPVContributor()) return false; - if (cfgTrackConnectedToPV && !track.isPVContributor()) + if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) return false; - if (cfgTrackCutQA && QA) { - fillQA(true, track, 3); - } return true; - }; + } - template - bool trackPIDKaon(const TrackPID& candidate, const bool QA) + // LOGF(info, "AFTER: pt: %f, hasTOF: %d, TPCSigma: %f, TOFSigma: %f, boolTPC: %d, boolTOF: %d, bool: %d", pt, candidate.hasTOF(), + // candidate.tpcNSigmaPr(), candidate.tofNSigmaPr(), tpcPIDPassed, tofPIDPassed, tpcPIDPassed || tofPIDPassed); + + template + bool ptDependentPidPion(const T& candidate) { - double tpcpid = 0; - double tofpid = 0; - bool tpcPIDPassed{false}, tofPIDPassed{false}; + auto vPionTPCPIDpTintv = configPID.pionTPCPIDpTintv.value; + vPionTPCPIDpTintv.insert(vPionTPCPIDpTintv.begin(), configTracks.cMinPtcut); + auto vPionTPCPIDcuts = configPID.pionTPCPIDcuts.value; + auto vPionTOFPIDpTintv = configPID.pionTOFPIDpTintv.value; + auto vPionTPCTOFCombinedpTintv = configPID.pionTPCTOFCombinedpTintv.value; + auto vPionTPCTOFCombinedPIDcuts = configPID.pionTPCTOFCombinedPIDcuts.value; + auto vPionTOFPIDcuts = configPID.pionTOFPIDcuts.value; + + float pt = candidate.pt(); + float ptSwitchToTOF = vPionTPCPIDpTintv.back(); + float tpcNsigmaPi = candidate.tpcNSigmaPi(); + float tofNsigmaPi = candidate.tofNSigmaPi(); + + bool tpcPIDPassed = false; + + // TPC PID (interval check) + for (size_t i = 0; i < vPionTPCPIDpTintv.size() - 1; ++i) { + if (pt > vPionTPCPIDpTintv[i] && pt < vPionTPCPIDpTintv[i + 1]) { + if (std::abs(tpcNsigmaPi) < vPionTPCPIDcuts[i]) + tpcPIDPassed = true; + } + } - if (cfgTrackCutQA && QA) { - fillQA(false, candidate, 4); + // TOF bypass option (for QA or MC) + if (configPID.cByPassTOF) { + return std::abs(tpcNsigmaPi) < vPionTPCPIDcuts.back(); } - // TPC - if (cfgTrackSquarePIDCut) { - if (std::abs(candidate.tpcNSigmaKa()) < cfgTrackTPCPIDnSig) - tpcPIDPassed = true; - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaKa()) < cfgTrackTOFPIDnSig) { - tofPIDPassed = true; - } - } else { - if (!cfgTrackTOFHard) { - tofPIDPassed = true; - } else { - tofPIDPassed = false; + // Case 1: No TOF and pt ≤ threshold → accept only via TPC PID + if (!candidate.hasTOF() && pt <= ptSwitchToTOF) { + return tpcPIDPassed; + } + + // Case 2: No TOF but pt > threshold → reject + if (!candidate.hasTOF() && pt > ptSwitchToTOF) { + return false; + } + + // Case 3: Has TOF → use TPC + TOF (square or circular) + if (candidate.hasTOF()) { + if (configPID.cPIDcutType == SquareType) { + // Rectangular cut + for (size_t i = 0; i < vPionTOFPIDpTintv.size(); ++i) { + if (pt < vPionTOFPIDpTintv[i]) { + if (std::abs(tofNsigmaPi) < vPionTOFPIDcuts[i] && + std::abs(tpcNsigmaPi) < vPionTPCPIDcuts.back()) + return true; + } } - } - } // end of square cut - if (cfgTrackCirclePIDCut) { - if (std::abs(candidate.tpcNSigmaKa()) < cfgTrackTPCPIDnSig) - tpcpid = std::abs(candidate.tpcNSigmaKa()); - tofpid = 0; - - if (candidate.hasTOF()) { - tofpid = std::abs(candidate.tofNSigmaKa()); - } else { - if (cfgTrackTOFHard) { - tofpid = 999; + } else if (configPID.cPIDcutType == CircularType) { + // Circular cut + for (size_t i = 0; i < vPionTPCTOFCombinedpTintv.size(); ++i) { + if (pt < vPionTPCTOFCombinedpTintv[i]) { + float combinedSigma2 = + tpcNsigmaPi * tpcNsigmaPi + + tofNsigmaPi * tofNsigmaPi; + if (combinedSigma2 < vPionTPCTOFCombinedPIDcuts[i] * vPionTPCTOFCombinedPIDcuts[i]) + return true; + } } } - if (tpcpid * tpcpid + tofpid * tofpid < cfgTrackCircleValue) { - tpcPIDPassed = true; - tofPIDPassed = true; - } - } // circular cut + } - // TPC & TOF - if (tpcPIDPassed && tofPIDPassed) { - if (cfgTrackCutQA && QA) { - fillQA(true, candidate, 4); - } + return false; + } + + template + bool selectionPIDPion(const T& candidate) + { + auto vPionTPCPIDcuts = configPID.pionTPCPIDcuts.value; + auto vPionTPCTOFCombinedPIDcuts = configPID.pionTPCTOFCombinedPIDcuts.value; + + if (!configPID.cByPassTOF && candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (vPionTPCTOFCombinedPIDcuts[0] * vPionTPCTOFCombinedPIDcuts[0])) { + return true; + } + if (!configPID.cByPassTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < vPionTPCPIDcuts[0]) { + return true; + } + if (configPID.cByPassTOF && std::abs(candidate.tpcNSigmaPi()) < vPionTPCPIDcuts[0]) { return true; } return false; } - template - bool trackPIDPion(const TrackPID& candidate, const bool QA) + template + bool ptDependentPidKaon(const T& candidate) { - double tpcpid = 0; - double tofpid = 0; - bool tpcPIDPassed{false}, tofPIDPassed{false}; + auto vKaonTPCPIDpTintv = configPID.kaonTPCPIDpTintv.value; + vKaonTPCPIDpTintv.insert(vKaonTPCPIDpTintv.begin(), configTracks.cMinPtcut); + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTOFPIDpTintv = configPID.kaonTOFPIDpTintv.value; + auto vKaonTPCTOFCombinedpTintv = configPID.kaonTPCTOFCombinedpTintv.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; + auto vKaonTOFPIDcuts = configPID.kaonTOFPIDcuts.value; + + float pt = candidate.pt(); + float ptSwitchToTOF = vKaonTPCPIDpTintv.back(); + float tpcNsigmaKa = candidate.tpcNSigmaKa(); + float tofNsigmaKa = candidate.tofNSigmaKa(); + + bool tpcPIDPassed = false; + + // TPC PID interval-based check + for (size_t i = 0; i < vKaonTPCPIDpTintv.size() - 1; ++i) { + if (pt > vKaonTPCPIDpTintv[i] && pt < vKaonTPCPIDpTintv[i + 1]) { + if (std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts[i]) { + tpcPIDPassed = true; + break; + } + } + } - if (cfgTrackCutQA && QA) { - fillQA(false, candidate, 5); + // TOF bypass option + if (configPID.cByPassTOF) { + return std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back(); } - // TPC - if (cfgTrackSquarePIDCut) { - if (std::abs(candidate.tpcNSigmaPi()) < cfgTrackTPCPIDnSig) - tpcPIDPassed = true; - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaPi()) < cfgTrackTOFPIDnSig) { - tofPIDPassed = true; - } - } else { - if (!cfgTrackTOFHard) { - tofPIDPassed = true; - } else { - tofPIDPassed = false; + // Case 1: No TOF and pt ≤ ptSwitch → use TPC-only + if (!candidate.hasTOF() && pt <= ptSwitchToTOF) { + return tpcPIDPassed; + } + + // Case 2: No TOF but pt > ptSwitch → reject + if (!candidate.hasTOF() && pt > ptSwitchToTOF) { + return false; + } + + // Case 3: TOF is available → apply TPC+TOF PID logic + if (candidate.hasTOF()) { + if (configPID.cPIDcutType == SquareType) { + // Rectangular cut + for (size_t i = 0; i < vKaonTOFPIDpTintv.size(); ++i) { + if (pt < vKaonTOFPIDpTintv[i]) { + if (std::abs(tofNsigmaKa) < vKaonTOFPIDcuts[i] && + std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back()) { + return true; + } + } } - } - } // end of square cut - if (cfgTrackCirclePIDCut) { - if (std::abs(candidate.tpcNSigmaPi()) < cfgTrackTPCPIDnSig) - tpcpid = std::abs(candidate.tpcNSigmaPi()); - tofpid = 0; - - if (candidate.hasTOF()) { - tofpid = std::abs(candidate.tofNSigmaPi()); - } else { - if (cfgTrackTOFHard) { - tofpid = 999; + } else if (configPID.cPIDcutType == CircularType) { + // Circular cut + for (size_t i = 0; i < vKaonTPCTOFCombinedpTintv.size(); ++i) { + if (pt < vKaonTPCTOFCombinedpTintv[i]) { + float combinedSigma2 = tpcNsigmaKa * tpcNsigmaKa + + tofNsigmaKa * tofNsigmaKa; + if (combinedSigma2 < vKaonTPCTOFCombinedPIDcuts[i] * vKaonTPCTOFCombinedPIDcuts[i]) { + return true; + } + } } } - if (tpcpid * tpcpid + tofpid * tofpid < cfgTrackCircleValue) { - tpcPIDPassed = true; - tofPIDPassed = true; - } - } // circular cut - - // TPC & TOF - if (tpcPIDPassed && tofPIDPassed) { - if (cfgTrackCutQA && QA) { - fillQA(true, candidate, 5); - } - return true; } + return false; } - template - ROOT::Math::PxPyPzMVector minvReconstruction(const TracksType& trk1, const TracksType& trk2, const bool QA, const bool flip) + template + bool selectionPIDKaon(const T& candidate) { - if (!trackSelection(trk1, false) || !trackSelection(trk2, false)) - return {}; + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; - if (!trackPIDKaon(trk1, QA) || !trackPIDPion(trk2, QA)) - return {}; + if (!configPID.cByPassTOF && candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (vKaonTPCTOFCombinedPIDcuts[0] * vKaonTPCTOFCombinedPIDcuts[0])) { + return true; + } + if (!configPID.cByPassTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { + return true; + } + if (configPID.cByPassTOF && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { + return true; + } + return false; + } - if (trk1.globalIndex() >= trk2.globalIndex()) - return {}; + auto static constexpr MinPtforProtonRejection = 1.0f; + auto static constexpr MaxPtforProtonRejection = 2.0f; + auto static constexpr MaxnSigmaforProtonRejection = 2.0f; - ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; - if (!flip) { - lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); - lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); - } else { - lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massPi); - lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massKa); + template + bool rejectProton(const T& candidate) + { + if (candidate.pt() > MinPtforProtonRejection && candidate.pt() < MaxPtforProtonRejection && !candidate.hasTOF() && candidate.tpcNSigmaPr() < MaxnSigmaforProtonRejection) { + return false; } - lResonance = lDecayDaughter1 + lDecayDaughter2; - - if (std::abs(lResonance.Eta()) > cfgTrackMaxEta) - return {}; - return {lResonance}; + return true; } - template - ROOT::Math::PxPyPzMVector TrueReconstruction(const TracksType& trk1, const TracksType& trk2) - { - double conjugate = trk1.sign() * trk2.sign(); - if (conjugate > 0) - return {}; + auto static constexpr MaxNok892Daughters = 2; - auto particle1 = trk1.mcParticle(); - auto particle2 = trk2.mcParticle(); + template + void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) + { + auto centrality = centEst(collision); - if (!particle1.has_mothers() || !particle2.has_mothers()) { - return {}; + // Multiplicity correlation calibration plots + if (cFillMultQA) { + if constexpr (IsData) { + histos.fill(HIST("MultCalib/centGloPVpr"), centrality, dTracks1.size(), collision.multNTracksPV()); + histos.fill(HIST("MultCalib/centGloPVka"), centrality, dTracks2.size(), collision.multNTracksPV()); + } } - std::vector mothers1{}; - std::vector mothers1PDG{}; - for (auto& particle1_mom : particle1.template mothers_as()) { - mothers1.push_back(particle1_mom.globalIndex()); - mothers1PDG.push_back(particle1_mom.pdgCode()); + if (cFilladditionalQAeventPlots) { + if constexpr (IsData) { + histos.fill(HIST("QAevent/hnTrksSameE"), dTracks1.size()); + } else if constexpr (IsMix) { + histos.fill(HIST("QAevent/hVertexZMixedE"), collision.posZ()); + histos.fill(HIST("QAevent/hMultiplicityPercentMixedE"), centrality); + histos.fill(HIST("QAevent/hnTrksMixedE"), dTracks1.size()); + } } + // LOG(info) << "After pass, Collision index:" << collision.index() << "multiplicity: " << collision.centFT0M() << endl; - std::vector mothers2{}; - std::vector mothers2PDG{}; - for (auto& particle2_mom : particle2.template mothers_as()) { - mothers2.push_back(particle2_mom.globalIndex()); - mothers2PDG.push_back(particle2_mom.pdgCode()); - } + LorentzVectorPtEtaPhiMass lDecayDaughter1, lDecayDaughter2, lResonance, ldaughterRot, lResonanceRot; - if (mothers1PDG[0] != 313) - return {}; // mother not K*0 - if (mothers2PDG[0] != 313) - return {}; // mothers not K*0 + for (const auto& [trk1, trk2] : combinations(CombinationsFullIndexPolicy(dTracks1, dTracks2))) { + // Full index policy is needed to consider all possible combinations + if (trk1.index() == trk2.index()) + continue; // We need to run (0,1), (1,0) pairs as well. but same id pairs are not needed. - if (mothers1[0] != mothers2[0]) - return {}; // Kaon and pion not from the same K*0 + // apply the track cut + if (!trackCut(trk1) || !trackCut(trk2)) + continue; - if (std::fabs(particle1.pdgCode()) != 211 && std::fabs(particle1.pdgCode()) != 321) - return {}; - if (std::fabs(particle2.pdgCode()) != 211 && std::fabs(particle2.pdgCode()) != 321) - return {}; + //// Initialize variables + // Trk1: Pion + auto isTrk1hasTOF = trk1.hasTOF(); + auto trk1ptPi = trk1.pt(); + auto trk1etaPi = trk1.eta(); + auto trk1phiPi = trk1.phi(); + auto trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); + auto trk1NSigmaPiTOF = (isTrk1hasTOF) ? trk1.tofNSigmaPi() : -999.0f; + + // Trk2: Kaon + auto isTrk2hasTOF = trk2.hasTOF(); + auto trk2ptKa = trk2.pt(); + auto trk2etaKa = trk2.eta(); + auto trk2phiKa = trk2.phi(); + auto trk2NSigmaKaTPC = trk2.tpcNSigmaKa(); + auto trk2NSigmaKaTOF = (isTrk2hasTOF) ? trk2.tofNSigmaKa() : -999.0f; + + auto deltaEta = 0.0f; + auto deltaPhi = 0.0f; + + if (cfgUseDeltaEtaPhiCuts) { + deltaEta = std::abs(trk1etaPi - trk2etaKa); + deltaPhi = std::abs(trk1phiPi - trk2phiKa); + deltaPhi = (deltaPhi > o2::constants::math::PI) ? (o2::constants::math::TwoPI - deltaPhi) : deltaPhi; + if (deltaEta >= cMaxDeltaEtaCut) + continue; + if (deltaPhi >= cMaxDeltaPhiCut) + continue; + } - double track1_mass, track2_mass; - if (std::fabs(particle1.pdgCode()) == 211) { - track1_mass = massPi; - } else { - track1_mass = massKa; - } + //// QA plots before the selection + // --- Track QA all + if constexpr (IsData) { + if (cFillTrackQA) { + histos.fill(HIST("QA/QAbefore/Track/TPC_Nsigma_pi_all"), centrality, trk1ptPi, trk1NSigmaPiTPC); + if (isTrk1hasTOF) { + histos.fill(HIST("QA/QAbefore/Track/TOF_Nsigma_pi_all"), centrality, trk1ptPi, trk1NSigmaPiTOF); + histos.fill(HIST("QA/QAbefore/Track/TOF_TPC_Map_pi_all"), trk1NSigmaPiTOF, trk1NSigmaPiTPC); + } + if (!isTrk1hasTOF) { + histos.fill(HIST("QA/QAbefore/Track/TPConly_Nsigma_pi_all"), trk1ptPi, trk1NSigmaPiTPC); + } + histos.fill(HIST("QA/QAbefore/Track/TPC_Nsigma_ka_all"), centrality, trk2ptKa, trk2NSigmaKaTPC); + if (isTrk2hasTOF) { + histos.fill(HIST("QA/QAbefore/Track/TOF_Nsigma_ka_all"), centrality, trk2ptKa, trk2NSigmaKaTOF); + histos.fill(HIST("QA/QAbefore/Track/TOF_TPC_Map_ka_all"), trk2NSigmaKaTOF, trk2NSigmaKaTPC); + } + if (!isTrk2hasTOF) { + histos.fill(HIST("QA/QAbefore/Track/TPConly_Nsigma_ka_all"), trk2ptKa, trk2NSigmaKaTPC); + } - if (std::fabs(particle2.pdgCode()) == 211) { - track2_mass = massPi; - } else { - track2_mass = massKa; - } + histos.fill(HIST("QA/QAbefore/Track/dcaZ_all"), trk1ptPi, trk1.dcaZ()); + histos.fill(HIST("QA/QAbefore/Track/dcaXY_all"), trk1ptPi, trk1.dcaXY()); + histos.fill(HIST("QA/QAbefore/Track/TPC_CR_all"), trk1ptPi, trk1.tpcNClsCrossedRows()); + histos.fill(HIST("QA/QAbefore/Track/pT_all"), trk1ptPi); + histos.fill(HIST("QA/QAbefore/Track/eta_all"), trk1etaPi); + } + if (cFilldeltaEtaPhiPlots) { + histos.fill(HIST("QAbefore/deltaEta"), deltaEta); + histos.fill(HIST("QAbefore/deltaPhi"), deltaPhi); + } + } - if (track1_mass == track2_mass) { - return {}; - } + //// Apply the pid selection + if (crejectProton && rejectProton(trk2)) // to remove proton contamination from the kaon track + continue; - ROOT::Math::PxPyPzMVector lTrueDaughter1, lTrueDaughter2, lTrueReso; - lTrueDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), track1_mass); - lTrueDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), track2_mass); - lTrueReso = lTrueDaughter1 + lTrueDaughter2; + if (configPID.ispTdepPID && (!ptDependentPidPion(trk1) || !ptDependentPidKaon(trk2))) + continue; - if (lTrueReso.M() < 0) - return {}; + if (!configPID.ispTdepPID && (!selectionPIDPion(trk1) || !selectionPIDKaon(trk2))) + continue; - return {lTrueReso}; - } + //// QA plots after the selection + if constexpr (IsData) { // --- PID QA Pion + histos.fill(HIST("QA/QAafter/Pion/TPC_Nsigma_pi_selected"), centrality, trk1ptPi, trk1NSigmaPiTPC); + histos.fill(HIST("QA/QAafter/Pion/TPC_Signal_pi_selected"), trk1.tpcInnerParam(), trk1.tpcSignal()); + if (isTrk1hasTOF) { + histos.fill(HIST("QA/QAafter/Pion/TOF_Nsigma_pi_selected"), centrality, trk1ptPi, trk1NSigmaPiTOF); + histos.fill(HIST("QA/QAafter/Pion/TOF_TPC_Map_pi_selected"), trk1NSigmaPiTOF, trk1NSigmaPiTPC); + } + if (!isTrk1hasTOF) { + histos.fill(HIST("QA/QAafter/Pion/TPC_Nsigma_pi_TPConly_selected"), trk1ptPi, trk1NSigmaPiTPC); + } + histos.fill(HIST("QA/QAafter/Pion/dcaZ_selected"), trk1ptPi, trk1.dcaZ()); + histos.fill(HIST("QA/QAafter/Pion/dcaXY_selected"), trk1ptPi, trk1.dcaXY()); + histos.fill(HIST("QA/QAafter/Pion/TPC_CR_selected"), trk1ptPi, trk1.tpcNClsCrossedRows()); + histos.fill(HIST("QA/QAafter/Pion/pT_selected"), trk1ptPi); + histos.fill(HIST("QA/QAafter/Pion/eta_selected"), trk1etaPi); + histos.fill(HIST("QA/QAafter/Pion/TPCnclusterPhipi_selected"), trk1.tpcNClsFound(), trk1phiPi); + + // --- PID QA Kaon + histos.fill(HIST("QA/QAafter/Kaon/TPC_Nsigma_ka_selected"), centrality, trk2ptKa, trk2NSigmaKaTPC); + histos.fill(HIST("QA/QAafter/Kaon/TPC_Signal_ka_selected"), trk2.tpcInnerParam(), trk2.tpcSignal()); + if (isTrk2hasTOF) { + histos.fill(HIST("QA/QAafter/Kaon/TOF_Nsigma_ka_selected"), centrality, trk2ptKa, trk2NSigmaKaTOF); + histos.fill(HIST("QA/QAafter/Kaon/TOF_TPC_Map_ka_selected"), trk2NSigmaKaTOF, trk2NSigmaKaTPC); + } + if (!isTrk2hasTOF) { + histos.fill(HIST("QA/QAafter/Kaon/TPC_Nsigma_ka_TPConly_selected"), trk2ptKa, trk2NSigmaKaTPC); + } + histos.fill(HIST("QA/QAafter/Kaon/dcaZ_selected"), trk2ptKa, trk2.dcaZ()); + histos.fill(HIST("QA/QAafter/Kaon/dcaXY_selected"), trk2ptKa, trk2.dcaXY()); + histos.fill(HIST("QA/QAafter/Kaon/TPC_CR_selected"), trk2ptKa, trk2.tpcNClsCrossedRows()); + histos.fill(HIST("QA/QAafter/Kaon/pT_selected"), trk2ptKa); + histos.fill(HIST("QA/QAafter/Kaon/eta_selected"), trk2etaKa); + histos.fill(HIST("QA/QAafter/Kaon/TPCnclusterPhika_selected"), trk2.tpcNClsFound(), trk2phiKa); + + if (cFilldeltaEtaPhiPlots) { + histos.fill(HIST("QAafter/PhiPrafter"), trk1phiPi); + histos.fill(HIST("QAafter/PhiKaafter"), trk2phiKa); + histos.fill(HIST("QAafter/deltaEta"), deltaEta); + histos.fill(HIST("QAafter/deltaPhi"), deltaPhi); + } + } - template - void TrackSlicing(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool IsMix, const bool QA) - { - auto tracks1 = kaon->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pion->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); - - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - for (bool flip : {false, true}) { - auto lReso = minvReconstruction(trk1, trk2, QA, flip); - if (lReso.M() < 0) + //// Resonance reconstruction + lDecayDaughter1 = LorentzVectorPtEtaPhiMass(trk1ptPi, trk1etaPi, trk1phiPi, massPi); + lDecayDaughter2 = LorentzVectorPtEtaPhiMass(trk2ptKa, trk2etaKa, trk2phiKa, massKa); + + // Apply kinematic opening angle cut + if (cUseOpeningAngleCut) { + auto v1 = lDecayDaughter1.Vect(); + auto v2 = lDecayDaughter2.Vect(); + float alpha = std::acos(v1.Dot(v2) / (v1.R() * v2.R())); + if (alpha > cMinOpeningAngle && alpha < cMaxOpeningAngle) continue; + } - fillMinv(objectType::MB, trk1, trk2, lReso, centrality, IsMix, flip); - } // flip - } // for - } // TrackSlicing + lResonance = lDecayDaughter1 + lDecayDaughter2; - template - void TrackSlicingMC(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool IsMix, const bool QA) - { - auto tracks1 = kaonMC->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pionMC->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); + auto resonanceMass = lResonance.M(); + auto resonancePt = lResonance.Pt(); + auto resonanceRapidity = lResonance.Rapidity(); - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) - continue; + if constexpr (IsData || IsMix) { + // Rapidity cut + if (std::abs(resonanceRapidity) > configTracks.cfgCutRapidity) + continue; + } - for (bool flip : {false, true}) { - auto lReso = minvReconstruction(trk1, trk2, QA, flip); - if (lReso.M() < 0) + if (cfgUseCutsOnMother) { + if (resonancePt >= cMaxPtMotherCut) // excluding candidates in overflow + continue; + if (resonanceMass >= cMaxMinvMotherCut) // excluding candidates in overflow continue; + } - fillMinv(objectType::MB, trk1, trk2, lReso, centrality, IsMix, flip); - } // flip - - //============================ - //| True Reconstruction - //============================ - auto lTrueReso = TrueReconstruction(trk1, trk2); - fillMinv(objectType::MBRecParticle, trk1, trk2, lTrueReso, centrality, IsMix, false); - } // tracks - } // TrackSlicingMC - - //======================================================= - //| - //| DATA STUFF (SE) - //| - //======================================================= - int nEvents = 0; - void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) - { - if (cDebugLevel > 0) { - nEvents++; - if ((nEvents + 1) % 10000 == 0) { - std::cout << "Processed Data Events: " << nEvents << std::endl; + if (cFilladditionalQAeventPlots) { + if constexpr (IsData) { + histos.fill(HIST("QAevent/hPairsCounterSameE"), 1.0f); + } else if (IsMix) { + histos.fill(HIST("QAevent/hPairsCounterMixedE"), 1.0f); + } } - } - auto [goodEv, code] = eventSelection(collision, true); - histos.fill(HIST("nEvents"), 0.5); + //// Un-like sign pair only + if (trk1.sign() * trk2.sign() < 0) { + if constexpr (IsRot) { + for (int i = 0; i < configBkg.cNofRotations; i++) { + float theta = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / configBkg.rotationalcut, o2::constants::math::PI + o2::constants::math::PI / configBkg.rotationalcut); + if (configBkg.cfgRotPi) { + ldaughterRot = LorentzVectorPtEtaPhiMass(trk1ptPi, trk1etaPi, trk1phiPi + theta, massPi); + lResonanceRot = ldaughterRot + lDecayDaughter2; + } else { + ldaughterRot = LorentzVectorPtEtaPhiMass(trk2ptKa, trk2etaKa, trk2phiKa + theta, massKa); + lResonanceRot = lDecayDaughter1 + ldaughterRot; + } + auto resonanceRotMass = lResonanceRot.M(); + auto resonanceRotPt = lResonanceRot.Pt(); + + // Rapidity cut + if (std::abs(lResonanceRot.Rapidity()) >= configTracks.cfgCutRapidity) + continue; + + if (cfgUseCutsOnMother) { + if (resonanceRotPt >= cMaxPtMotherCut) // excluding candidates in overflow + continue; + if (resonanceRotMass >= cMaxMinvMotherCut) // excluding candidates in overflow + continue; + } + if (trk1.sign() < 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/k892InvMassRotation"), resonanceRotMass); + } + histos.fill(HIST("Result/Data/h3k892InvMassRotation"), centrality, resonanceRotPt, resonanceRotMass); + } else if (trk1.sign() > 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/antik892InvMassRotation"), resonanceRotMass); + } + histos.fill(HIST("Result/Data/h3antik892InvMassRotation"), centrality, resonanceRotPt, resonanceRotMass); + } + } + } + if constexpr (IsData) { + if (trk1.sign() < 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/k892invmass"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3k892invmass"), centrality, resonancePt, resonanceMass); + } else if (trk1.sign() > 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/antik892invmass"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3antik892invmass"), centrality, resonancePt, resonanceMass); + } + } else if (IsMix) { + if (trk1.sign() < 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/k892invmassME_UnlikeSign"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3k892invmassME_UnlikeSign"), centrality, resonancePt, resonanceMass); + } else if (trk1.sign() > 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/antik892invmassME_UnlikeSign"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3antik892invmassME_UnlikeSign"), centrality, resonancePt, resonanceMass); + } + } + + // MC + if constexpr (IsMC) { + // now we do mc true + // ------ Temporal lambda function to prevent error in build + auto getMothersIndeces = [&](auto const& theMcParticle) { + std::vector lMothersIndeces{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother index lMother: %d", lMother.globalIndex()); + lMothersIndeces.push_back(lMother.globalIndex()); + } + return lMothersIndeces; + }; + auto getMothersPDGCodes = [&](auto const& theMcParticle) { + std::vector lMothersPDGs{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother pdgcode lMother: %d", lMother.pdgCode()); + lMothersPDGs.push_back(lMother.pdgCode()); + } + return lMothersPDGs; + }; + // ------ + std::vector motherstrk1 = {-1, -1}; + std::vector mothersPDGtrk1 = {-1, -1}; + + std::vector motherstrk2 = {-1, -1}; + std::vector mothersPDGtrk2 = {-1, -1}; + + // + // Get the MC particle + const auto& mctrk1 = trk1.mcParticle(); + if (mctrk1.has_mothers()) { + motherstrk1 = getMothersIndeces(mctrk1); + mothersPDGtrk1 = getMothersPDGCodes(mctrk1); + } + while (motherstrk1.size() > MaxNok892Daughters) { + motherstrk1.pop_back(); + mothersPDGtrk1.pop_back(); + } - if (!goodEv) - return; + const auto& mctrk2 = trk2.mcParticle(); + if (mctrk2.has_mothers()) { + motherstrk2 = getMothersIndeces(mctrk2); + mothersPDGtrk2 = getMothersPDGCodes(mctrk2); + } + while (motherstrk2.size() > MaxNok892Daughters) { + motherstrk2.pop_back(); + mothersPDGtrk2.pop_back(); + } - bool INELgt0 = false; - for (const auto& track : tracks) { - if (!trackSelection(track, true)) - continue; - if (std::fabs(track.eta()) < cfgTrackMaxEta) { - INELgt0 = true; + if (std::abs(mctrk1.pdgCode()) != kPiPlus || std::abs(mctrk2.pdgCode()) != kKPlus) + continue; + + if (motherstrk1[0] != motherstrk2[0]) // Same mother + continue; + + if (std::abs(mothersPDGtrk1[0]) != Pdg::kK0Star892) + continue; + + // LOGF(info, "mother trk1 id: %d, mother trk1: %d, trk1 id: %d, trk1 pdgcode: %d, mother trk2 id: %d, mother trk2: %d, trk2 id: %d, trk2 pdgcode: %d", motherstrk1[0], mothersPDGtrk1[0], trk1.globalIndex(), mctrk1.pdgCode(), motherstrk2[0], mothersPDGtrk2[0], trk2.globalIndex(), mctrk2.pdgCode()); + + if (cUseRapcutMC && std::abs(resonanceRapidity) > configTracks.cfgCutRapidity) // rapidity cut + continue; + + histos.fill(HIST("QA/MC/h2RecoEtaPt_after"), lResonance.Eta(), resonancePt); + histos.fill(HIST("QA/MC/h2RecoPhiRapidity_after"), lResonance.Phi(), resonanceRapidity); + + // Track selection check. + histos.fill(HIST("QA/MC/trkDCAxy_pi"), trk1ptPi, trk1.dcaXY()); + histos.fill(HIST("QA/MC/trkDCAxy_ka"), trk2ptKa, trk2.dcaXY()); + histos.fill(HIST("QA/MC/trkDCAz_pi"), trk1ptPi, trk1.dcaZ()); + histos.fill(HIST("QA/MC/trkDCAz_ka"), trk2ptKa, trk2.dcaZ()); + + histos.fill(HIST("QA/MC/TPC_Nsigma_pi_all"), centrality, trk1ptPi, trk1NSigmaPiTPC); + if (isTrk1hasTOF) { + histos.fill(HIST("QA/MC/TOF_Nsigma_pi_all"), centrality, trk1ptPi, trk1NSigmaPiTOF); + } + histos.fill(HIST("QA/MC/TPC_Nsigma_ka_all"), centrality, trk2ptKa, trk2NSigmaKaTPC); + if (isTrk2hasTOF) { + histos.fill(HIST("QA/MC/TOF_Nsigma_ka_all"), centrality, trk2ptKa, trk2NSigmaKaTOF); + } + + // MC histograms + if (mothersPDGtrk1[0] > 0) { + histos.fill(HIST("Result/MC/h3k892Recoinvmass"), centrality, resonancePt, resonanceMass); + } else { + histos.fill(HIST("Result/MC/h3antik892Recoinvmass"), centrality, resonancePt, resonanceMass); + } + } + } else { + if constexpr (IsData) { + // Like sign pair ++ + if (trk1.sign() > 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/k892invmassLSPP"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3k892invmassLSPP"), centrality, resonancePt, resonanceMass); + } else { // Like sign pair -- + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/k892invmassLSMM"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3k892invmassLSMM"), centrality, resonancePt, resonanceMass); + } + } else if (IsMix) { + if (cFilladditionalMEPlots) { + // Like sign pair ++ + if (trk1.sign() > 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/k892invmassME_LSPP"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3k892invmassME_LSPP"), centrality, resonancePt, resonanceMass); + } else { // Like sign pair -- + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/k892invmassME_LSMM"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3k892invmassME_LSMM"), centrality, resonancePt, resonanceMass); + } + } + } } } - if (!INELgt0) + } + + void processData(EventCandidates::iterator const& collision, + TrackCandidates const& tracks) + { + if (!isSelected(collision)) // Default event selection + return; + + auto centrality = centEst(collision); + + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centFT0M"), centrality); + + fillHistograms(collision, tracks, tracks); + } + PROCESS_SWITCH(Kstar0analysis, processData, "Process Event for data without partition", false); + + void processRotational(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + if (!isSelected(collision, false)) // Default event selection + return; + + if (!collision.isInelGt0()) // <-- return; - histos.fill(HIST("nEvents"), 1.5); - TrackSlicing(collision, tracks, collision, tracks, false, true); + fillHistograms(collision, tracks, tracks); + } + PROCESS_SWITCH(Kstar0analysis, processRotational, "Process Rotational Background", false); - } // processSameEvents - PROCESS_SWITCH(kstar0analysis, processDataSameEvent, "process Data Same Event", false); + // Processing Event Mixing + using BinningTypeVtxZT0M = ColumnBinningPolicy; - //======================================================= - //| - //| DATA STUFF (ME) - //| - //======================================================= - int nEventsMix = 0; - void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) + void processME(EventCandidates const& collision, + TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; + + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; + SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMix++; - if ((nEventsMix + 1) % 10000 == 0) { - std::cout << "Processed DATA Mixed Events : " << nEventsMix << std::endl; - } - } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - bool VtxMixFlag = false; - bool CentMixFlag = false; - // bool OccupanacyMixFlag = false; - if (std::fabs(collision1.posZ() - collision2.posZ()) <= cfgVtxMixCut) // set default to maybe 10 - VtxMixFlag = true; - if (std::fabs(collision1.centFT0C() - collision2.centFT0C()) <= cfgVtxMixCut) // set default to maybe 10 - CentMixFlag = true; - - if (!goodEv1 || !goodEv2) + // LOGF(info, "Mixed event collisions: (%d, %d)", collision1.globalIndex(), collision2.globalIndex()); + + // for (auto& [t1, t2] : combinations(CombinationsFullIndexPolicy(tracks1, tracks2))) { + // LOGF(info, "Mixed event tracks pair: (%d, %d) from events (%d, %d)", t1.index(), t2.index(), collision1.index(), collision2.index()); + // } + + if (!isSelected(collision1, false)) // Default event selection + continue; + + if (!isSelected(collision2, false)) // Default event selection continue; - if (!CentMixFlag) + + if (!collision1.isInelGt0()) // <-- continue; - if (!VtxMixFlag) + + if (!collision2.isInelGt0()) // <-- continue; - TrackSlicing(collision1, tracks1, collision2, tracks2, true, false); - } - } - PROCESS_SWITCH(kstar0analysis, processDataMixedEvent, "process DATA Mixed Event", false); - - //======================================================= - //| - //| MC STUFF (SE) - //| - //========================================================= - int nEventsMC = 0; - void processSameEventMC(EventCandidates::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) - { - if (cDebugLevel > 0) { - nEventsMC++; - if ((nEventsMC + 1) % 10000 == 0) { - double histmem = histos.getSize(); - std::cout << histmem << std::endl; - std::cout << "process_SameEvent_MC: " << nEventsMC << std::endl; + if (cFilladditionalQAeventPlots) { + // Fill histograms for the characteristics of the *mixed* events (collision1 and collision2) + // This will show the distribution of events that are actually being mixed. + if (cFill1DQAs) { + histos.fill(HIST("QAevent/hMixPool_VtxZ"), collision1.posZ()); + histos.fill(HIST("QAevent/hMixPool_Multiplicity"), collision1.centFT0M()); + } + histos.fill(HIST("QAevent/hMixPool_VtxZ_vs_Multiplicity"), collision1.posZ(), collision1.centFT0M()); + + // You might also want to fill for collision2 if you want to see both partners' distributions + // histos.fill(HIST("QAevent/hMixPool_VtxZ"), collision2.posZ()); + // histos.fill(HIST("QAevent/hMixPool_Multiplicity"), collision2.centFT0M()); + // histos.fill(HIST("QAevent/hMixPool_VtxZ_vs_Multiplicity"), collision2.posZ(), collision2.centFT0M()); } + fillHistograms(collision1, tracks1, tracks2); } - auto [goodEv, code] = eventSelection(collision, true); - - histos.fill(HIST("nEvents"), 0.5); + } + PROCESS_SWITCH(Kstar0analysis, processME, "Process EventMixing light without partition", false); - if (!goodEv) + void processMCRec(MCEventCandidates::iterator const& collision, + aod::McCollisions const&, + MCTrackCandidates const& tracks, aod::McParticles const&) + { + if (!collision.has_mcCollision()) return; - bool INELgt0 = false; - for (const auto& track : tracks) { - if (!trackSelection(track, true)) - continue; - if (std::fabs(track.eta()) < cfgTrackMaxEta) { - INELgt0 = true; - } - } - if (!INELgt0) + if (!isSelected(collision)) return; - histos.fill(HIST("nEvents"), 1.5); + auto centrality = centEst(collision); - TrackSlicingMC(collision, tracks, collision, tracks, false, true); - } // processSameEvents_MC - PROCESS_SWITCH(kstar0analysis, processSameEventMC, "process Same Event MC", false); + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centFT0M"), centrality); - //======================================================= - //| - //| MC STUFF (ME) - //| - //======================================================= - int nEventsMCMix = 0; - void processMixedEventMC(EventCandidates const& collisions, TrackCandidatesMC const& tracks, aod::McParticles const&) + fillHistograms(collision, tracks, tracks); + } + PROCESS_SWITCH(Kstar0analysis, processMCRec, "Process Event for MC Rec without partition", false); + + Partition selectedMCParticles = (nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kK0Star892)); // K(892) + + void processMCGen(MCEventCandidates::iterator const& collision, aod::McCollisions const&, aod::McParticles const& mcParticles) { - auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMCMix++; - if ((nEventsMCMix + 1) % 10000 == 0) { - std::cout << "Processed Mixed Events: " << nEventsMCMix << std::endl; - } + if (!collision.has_mcCollision()) + return; + + bool isInAfterAllCuts = isSelected(collision, false); + + auto mcPartsAll = mcParticles.sliceBy(perMcCollision, collision.mcCollision().globalIndex()); + // bool isTrueINELgt0 = pwglf::isINELgt0mc(mcPartsAll, pdg); + bool isTrueINELgt0 = collision.isInelGt0(); // <-- + + auto centrality = centEst(collision); + + auto mcParts = selectedMCParticles->sliceBy(perMcCollision, collision.mcCollision().globalIndex()); + + // Not related to the real collisions + for (const auto& part : mcParts) { // loop over all K(892) particles + + std::vector daughterPDGs; + if (part.has_daughters()) { + auto daughter01 = mcParticles.rawIteratorAt(part.daughtersIds()[0] - mcParticles.offset()); + auto daughter02 = mcParticles.rawIteratorAt(part.daughtersIds()[1] - mcParticles.offset()); + daughterPDGs = {daughter01.pdgCode(), daughter02.pdgCode()}; + } else { + daughterPDGs = {-1, -1}; } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - if (!goodEv1 || !goodEv2) { + + bool pass1 = std::abs(daughterPDGs[0]) == kKPlus || std::abs(daughterPDGs[1]) == kKPlus; // At least one decay to Kaon + bool pass2 = std::abs(daughterPDGs[0]) == kPiPlus || std::abs(daughterPDGs[1]) == kPiPlus; // At least one decay to Pion + + // Checking if we have both decay products + if (!pass1 || !pass2) continue; + + // LOGF(info, "Part PDG: %d", part.pdgCode(), "DAU_ID1: %d", pass1, "DAU_ID2: %d", pass2); + + histos.fill(HIST("QA/MC/h2GenEtaPt_beforeanycut"), part.eta(), part.pt()); + histos.fill(HIST("QA/MC/h2GenPhiRapidity_beforeanycut"), part.phi(), part.y()); + + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) // rapidity cut + continue; + + if (cfgUseDaughterEtaCutMC) { + for (auto const& daughters : part.daughters_as()) { + if (std::fabs(daughters.eta()) > configTracks.cfgCutEta) + continue; // eta cut for daughters + } // loop over daughters } - TrackSlicingMC(collision1, tracks1, collision2, tracks2, true, false); - } // mixing - } // processMixedEvent_MC - PROCESS_SWITCH(kstar0analysis, processMixedEventMC, "process Mixed Event MC", false); - - //====================================== - //| - //| GENERATED STUFF - //| - //====================================== - int nEventsGen = 0; - void processGen(EventCandidatesTrue::iterator const& collision, soa::SmallGroups> const& recocolls, aod::McParticles const& mcParticles) + histos.fill(HIST("QA/MC/h2GenEtaPt_afterRapcut"), part.eta(), part.pt()); + histos.fill(HIST("QA/MC/h2GenPhiRapidity_afterRapcut"), part.phi(), part.y()); + + if (isInAfterAllCuts && isTrueINELgt0) // after all event selection && INEL>0 + { + if (part.pdgCode() > 0) + histos.fill(HIST("Result/MC/Genk892pt"), part.pt(), centrality); + else + histos.fill(HIST("Result/MC/Genantik892pt"), part.pt(), centrality); + } + } + } + PROCESS_SWITCH(Kstar0analysis, processMCGen, "Process Event for MC only", false); + + void processEventFactor(MCEventCandidates const& collisions, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) { - if (cDebugLevel > 0) { - ++nEventsGen; - if (nEventsGen % 10000 == 0) { - std::cout << "Processed MC (GEN) Events: " << nEventsGen << std::endl; + // Loop on reconstructed collisions + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; } + const auto& mcCollision = collision.mcCollision_as>(); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cacheMC); + + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); + bool isInAfterAllCuts = isSelected(collision, false); + + float centrality = mcCollision.centFT0M(); + + if (isTrueINELgt0 && isInAfterAllCuts) + histos.fill(HIST("Event/MultiplicityRecoEv"), centrality); } - //======================= - //|| Event & Signal loss - //======================= - if (cfgMCHistos) { - histos.fill(HIST("nEvents_Gen"), 0.5); + + // Loop on generated collisions to fill the event factor for the INEL>0 correction + for (const auto& mccolls : mcCollisions) { + float centrality = mccolls.centFT0M(); + bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; + + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolls.globalIndex(), cacheMC); + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); // QA for Trigger efficiency + + if (inVtx10 && isTrueINELgt0) + histos.fill(HIST("Event/MultiplicityGenEv"), centrality); } + } + PROCESS_SWITCH(Kstar0analysis, processEventFactor, "Process Event factor", false); - for (auto& particle : mcParticles) { - if (particle.pdgCode() != 313) + void processSignalLoss(MCEventCandidates const& collisions, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) + { + // Loop on reconstructed collisions + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { continue; - if (std::fabs(particle.eta()) > cfgTrackMaxEta) + } + const auto& mcCollision = collision.mcCollision_as>(); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cacheMC); + + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); + bool isInAfterAllCuts = isSelected(collision, false); + bool inVtx10 = std::abs(mcCollision.posZ()) <= configEvents.cfgEvtZvtx; + + float centrality = mcCollision.centFT0M(); + + // ===== NUM ===== + if (!(inVtx10 && isTrueINELgt0)) continue; - if (fabs(collision.posZ()) > cfgEventVtxCut) - break; - if (cfgMCHistos) { - histos.fill(HIST("hGen_pT_Raw"), particle.pt()); - } - } // Unreconstructed collisions(=Raw coll) for correction + if (!isInAfterAllCuts) + continue; + + for (const auto& part : particlesInCollision) { + + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) + continue; + + float pt = part.pt(); - if (recocolls.size() <= 0) { // not reconstructed - if (cfgForceGenReco) { - return; + // kaon + if (std::abs(part.pdgCode()) == Pdg::kK0Star892) { + if (part.pdgCode() > 0) + histos.fill(HIST("Result/SignalLoss/GenTruek892pt_num"), pt, centrality); + else + histos.fill(HIST("Result/SignalLoss/GenTrueantik892pt_num"), pt, centrality); + } } } - double centrality = -1; - for (auto& recocoll : recocolls) { - centrality = recocoll.centFT0C(); - auto [goodEv, code] = eventSelection(recocoll, true); - if (cfgMCHistos) { - histos.fill(HIST("nEvents_Gen"), 1.5); - } - if (!goodEv) - continue; - } // recocolls (=reconstructed collisions) - - //================= - //|| Efficiency - //================= - for (auto& particle : mcParticles) { - if (particle.pdgCode() != 313) - continue; // Not K*0 - if (std::fabs(particle.eta()) > cfgTrackMaxEta) + // Loop on generated collisions to fill the event factor for the INEL>0 correction + for (const auto& mccolls : mcCollisions) { + float centrality = mccolls.centFT0M(); + + bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; + + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolls.globalIndex(), cacheMC); + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); + + if (!(inVtx10 && isTrueINELgt0)) continue; - if (cfgMCHistos) { - histos.fill(HIST("nEvents_Gen"), 2.5); - histos.fill(HIST("hGen_pT_GoodEv"), centrality, particle.pt()); - } // cfgMCHistos - } // loop over particles - } // processMCTrue - PROCESS_SWITCH(kstar0analysis, processGen, "process Generated Particles", false); + for (const auto& part : particlesInCollision) { - void processEventsDummy(EventCandidates::iterator const&, TrackCandidates const&) - { - return; + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) + continue; + + // ========================= + // ===== K(892) ====== + // ========================= + if (std::abs(part.pdgCode()) == Pdg::kK0Star892) { + + if (part.pdgCode() > 0) + histos.fill(HIST("Result/SignalLoss/GenTruek892pt_den"), part.pt(), centrality); + else + histos.fill(HIST("Result/SignalLoss/GenTrueantik892pt_den"), part.pt(), centrality); + } + } + } } - PROCESS_SWITCH(kstar0analysis, processEventsDummy, "dummy", false); -}; // kstar0analysis + PROCESS_SWITCH(Kstar0analysis, processSignalLoss, "Process SignalLoss", false); +}; // Kstar0analysis WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; }; diff --git a/PWGLF/Tasks/Resonances/kstar892LightIon.cxx b/PWGLF/Tasks/Resonances/kstar892LightIon.cxx index 951bfdd1440..24ad4ba2a94 100644 --- a/PWGLF/Tasks/Resonances/kstar892LightIon.cxx +++ b/PWGLF/Tasks/Resonances/kstar892LightIon.cxx @@ -16,6 +16,7 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -27,16 +28,13 @@ #include #include #include -#include #include #include #include -#include #include #include #include #include -#include #include #include @@ -48,10 +46,13 @@ #include #include +// #include #include #include #include +#include #include +#include #include using namespace o2; @@ -97,6 +98,7 @@ struct Kstar892LightIon { Configurable isVertexTOFMatched{"isVertexTOFMatched", false, "Vertex TOF Matched"}; Configurable isApplyhasFT0{"isApplyhasFT0", false, "Apply has_foundFT0 event selection"}; + Configurable isApplyhasBC{"isApplyhasBC", false, "Apply has_foundBC event selection"}; // check Configurable isApplyMCGenInelgt0{"isApplyMCGenInelgt0", true, "Apply INEL>0 cut in MC Gen Collisions"}; @@ -115,16 +117,15 @@ struct Kstar892LightIon { Configurable cfgNoMixedEvents{"cfgNoMixedEvents", 15, "Number of mixed events per event"}; Configurable cfgITScluster{"cfgITScluster", 0, "Number of ITS cluster"}; Configurable cfgTPCcluster{"cfgTPCcluster", 70, "Number of TPC cluster"}; - Configurable cfgRCRFC{"cfgRCRFC", 0.8f, "Crossed Rows to Findable Clusters"}; Configurable cfgITSChi2NCl{"cfgITSChi2NCl", 36.0, "ITS Chi2/NCl"}; Configurable cfgTPCChi2NClMax{"cfgTPCChi2NClMax", 4.0, "TPC Chi2/NCl"}; Configurable cfgTPCChi2NClMin{"cfgTPCChi2NClMin", 0.0, "TPC Chi2/NCl"}; - Configurable isUseITSTPCRefit{"isUseITSTPCRefit", false, "Require ITS Refit"}; + // Configurable isUseITSTPCRefit{"isUseITSTPCRefit", false, "Require ITS Refit"}; Configurable isApplyPtDepDCAxyCut{"isApplyPtDepDCAxyCut", false, "Apply pT dependent DCAxy cut"}; Configurable isGoldenChi2{"isGoldenChi2", false, "Apply golden chi2 cut"}; Configurable cfgDeepAngle{"cfgDeepAngle", 0.04, "Deep Angle cut value"}; // Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", false, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - Configurable cfgBetaCutTOF{"cfgBetaCutTOF", 0.0, "cut TOF beta"}; + Configurable cfgTOFBetaCut{"cfgTOFBetaCut", 0.0, "cut TOF beta"}; Configurable isAvoidsplitrackMC{"isAvoidsplitrackMC", true, "avoid split track in MC"}; @@ -136,13 +137,12 @@ struct Kstar892LightIon { Configurable motherRapidityMin{"motherRapidityMin", -0.5, "Minimum rapidity of mother"}; // PID selections - Configurable onlyTOF{"onlyTOF", false, "only TOF tracks"}; - Configurable onlyTOFHIT{"onlyTOFHIT", false, "accept only TOF hit tracks at high pt"}; - Configurable onlyTPC{"onlyTPC", false, "only TPC tracks"}; - Configurable isApplypTdepPID{"isApplypTdepPID", false, "Apply pT dependent PID"}; - Configurable isApplypTdepPIDwTOF{"isApplypTdepPIDwTOF", false, "Apply pT dependent PID with compulsory TOF condition in a pT range"}; - Configurable isApplyMID{"isApplyMID", false, "Apply particle MID"}; - Configurable isApplypTdepMID{"isApplypTdepMID", false, "Apply pT dependent particle MID"}; + Configurable pidStrategy{"pidStrategy", 0, "0=Standard, 1=pTDependent, 2=pTDependentTOF, 3=ThreePtDependent, 4=PIDCompare"}; + Configurable pidMode{"pidMode", 0, "0=Combined,1=TPC,2=TOF,3=TOFHIT,4=TOFVeto"}; + Configurable misIdStrategy{"misIdStrategy", -1, "-1=Disabled, 0=Standard, 1=PtDependent, 2=PtDependentCompare"}; + + Configurable shiftTofNsigmaKa{"shiftTofNsigmaKa", 0.0, "Shift in Nsigma for kaons in TOF"}; + Configurable shiftTofNsigmaPi{"shiftTofNsigmaPi", 0.0, "Shift in Nsigma for pions in TOF"}; Configurable nsigmaCutTPCPi{"nsigmaCutTPCPi", 3.0, "TPC Nsigma cut for pions"}; Configurable nsigmaCutTPCKa{"nsigmaCutTPCKa", 3.0, "TPC Nsigma cut for kaons"}; @@ -151,11 +151,23 @@ struct Kstar892LightIon { Configurable nsigmaCutCombinedKa{"nsigmaCutCombinedKa", 3.0, "Combined Nsigma cut for kaon"}; Configurable nsigmaCutCombinedPi{"nsigmaCutCombinedPi", 3.0, "Combined Nsigma cut for pion"}; - Configurable nsigmaCutCombinedMID{"nsigmaCutCombinedMID", 3.0, "Combined Nsigma cut for pion in MID"}; - Configurable nsigmaCutTPCMID{"nsigmaCutTPCMID", 1.0, "MID Nsigma cut for pion in TPC"}; + Configurable nsigmaCutTpcMisId{"nsigmaCutTpcMisId", 1.0, "MID Nsigma cut for pion and kaon in TPC"}; + + Configurable isApplyFakeTrack{"isApplyFakeTrack", false, "Fake track selection"}; + Configurable cfgFakeTrackCutKa{"cfgFakeTrackCutKa", 0.3, "Cut based on momentum difference in global and TPC tracks for kaons"}; + Configurable cfgFakeTrackCutPi{"cfgFakeTrackCutPi", 0.3, "Cut based on momentum difference in global and TPC tracks for pions"}; + + Configurable pionMisIdPtLow{"pionMisIdPtLow", 1.0, "Low pT cut for pion in MID"}; + Configurable pionMisIdPtHigh{"pionMisIdPtHigh", 2.5, "High pT cut for pion in MID"}; + Configurable kaonMisIdPtLow{"kaonMisIdPtLow", 0.7, "Low pT cut for kaon in MID"}; + Configurable kaonMisIdPtHigh{"kaonMisIdPtHigh", 2.5, "High pT cut for kaon in MID"}; + + Configurable lowPtCutPid{"lowPtCutPid", 0.5, "Low pT cut for PID"}; + Configurable highPtCutPid{"highPtCutPid", 6.0, "High pT cut for PID"}; - // Fixed variables - float lowPtCutPID = 0.5; + Configurable lowPtPid{"lowPtPid", 2.5, "Low pT cut for PID"}; + Configurable midPtPid{"midPtPid", 1.5, "Mid pT cut for PID"}; + Configurable highPtPid{"highPtPid", 2.5, "High pT cut for PID"}; Configurable selHasFT0MC{"selHasFT0MC", true, "Has FT0?"}; Configurable isZvtxPosSelMC{"isZvtxPosSelMC", true, "Zvtx position selection for MC events?"}; @@ -170,16 +182,11 @@ struct Kstar892LightIon { // Confugrable for QA histograms Configurable cQAplots{"cQAplots", true, "cQAplots"}; - Configurable cQAevents{"cQAevents", true, "centrality dist, DCAxy, DCAz"}; + Configurable additionalKin{"additionalKin", false, "Additional kinematics histograms for processMisIdKinematics"}; + Configurable cQAevents{"cQAevents", true, "centrality dist, Vz, Event Cut hists"}; Configurable selectCentEstimator{"selectCentEstimator", 0, "Select centrality estimator: 0 - FT0M, 1 - FT0A, 2 - FT0C, 3 - FV0A"}; - Configurable nchAcceptance{"nchAcceptance", 0.5, "Eta window to measure Nch MC for Nch vs Cent distribution"}; - - Configurable nBinsNch{"nBinsNch", 400, "N bins Nch (|eta|<0.8)"}; - Configurable minNch{"minNch", 0, "Min Nch (|eta|<0.8)"}; - Configurable maxNch{"maxNch", 400, "Max Nch (|eta|<0.8)"}; - // Configurable for histograms ConfigurableAxis binsCentPlot{"binsCentPlot", {110, 0.0, 110}, "Centrality axis"}; ConfigurableAxis axisdEdx{"axisdEdx", {1, 0.0f, 200.0f}, "dE/dx (a.u.)"}; @@ -199,15 +206,53 @@ struct Kstar892LightIon { kNEstimators // useful if you want to iterate or size things }; - enum PIDParticle { + enum class PIDParticle { kPion, - kKaon, - kProton + kKaon + }; + + enum class PIDStrategy { + Standard, + PtDependent, + PtDependentTOF, + ThreePtDependent, + PIDCompare + }; + + enum class PIDMode { + Combined, + TPC, + TOF, + TOFHIT, + TOFVeto + }; + + enum class MIDStrategy { + Disabled = -1, + Standard = 0, + PtDependent = 1, + PtDependentCompare = 2 }; int noOfDaughters = 2; + int initialValue = -9999999; + double massPi = o2::constants::physics::MassPiPlus; + double massKa = o2::constants::physics::MassKPlus; + + double pionPidPtLow = 1.0, pionPidPtHigh = 2.5, kaonPidPtLow = 0.7, kaonPidPtHigh = 2.5; + + int kKstar14300 = 10311, kK11400Plus = 20323, kKStar1680Plus = 30323, kK21770Plus = 10325, kK21820Plus = 20325, kKstar14100 = 100313, kKstar16800 = 30313, kK218200 = 20315, kK217700 = 10315, kKstar214300 = 315, kKstar1430Plus = 10321; - double pionPIDpTlow = 1, pionPIDpThigh = 2.5, kaonPIDpTlow = 0.7, kaonPIDpThigh = 2.5; + // ThreePtDependent PID cuts + // Regions: + // 0 : pT < lowPtCutPid + // 1 : lowPtCutPid <= pT < highPtCutPID + // 2 : pT >= highPtCutPID + // static constexpr std::array TPCPiCuts3Pt{3.0f, 1.5f, 2.0f}; + // static constexpr std::array TOFPiCuts3Pt{3.0f, 2.0f, 3.0f}; + + // static constexpr std::array TPCKaCuts3Pt{2.0f, 1.5f, 2.0f}; + // static constexpr std::array TOFKaCuts3Pt{3.0f, 2.0f, 3.0f}; TRandom* rn = new TRandom(); @@ -245,7 +290,8 @@ struct Kstar892LightIon { std::string("isVertexITSTPC") + check(selectionConfig.isVertexITSTPC.value), std::string("isVertexTOFMatched") + check(selectionConfig.isVertexTOFMatched.value), std::string("INEL > 0") + check(selectionConfig.isApplyINELgt0.value), - std::string("hasFT0") + check(selectionConfig.isApplyhasFT0.value)}; + std::string("hasFT0") + check(selectionConfig.isApplyhasFT0.value), + std::string("hasBC") + check(selectionConfig.isApplyhasBC.value)}; // assign labels for (size_t i = 0; i < eveCutLabels.size(); ++i) { @@ -256,8 +302,10 @@ struct Kstar892LightIon { if (cQAplots) { hOthers.add("dE_by_dx_TPC", "dE/dx signal in the TPC as a function of pT", kTH2F, {axisPtfordEbydx, axisdEdx}); hOthers.add("hEta_after", "Eta distribution", kTH1F, {{200, -1.0f, 1.0f}}); - hOthers.add("hCRFC_after", "CRFC after distribution", kTH1F, {{100, 0.0f, 10.0f}}); - hOthers.add("hCRFC_before", "CRFC before distribution", kTH1F, {{100, 0.0f, 10.0f}}); + hOthers.add("hTOFBetaKa", "Beta distribution from TOF", kTH1F, {{50, 0.0f, 1.0f}}); + hOthers.add("hTOFBetaPi", "Beta distribution from TOF", kTH1F, {{50, 0.0f, 1.0f}}); + hOthers.add("hTPCpDiffKa", "Momentum difference between global and TPC tracks for Kaons", kTH2F, {{100, -1.0f, 1.0f}, ptAxis}); + hOthers.add("hTPCpDiffPi", "Momentum difference between global and TPC tracks for Kaons", kTH2F, {{100, -1.0f, 1.0f}, ptAxis}); hOthers.add("hKstar_rap_pt", "Pair rapidity distribution; y; p_{T}; Counts", kTH2F, {{400, -2.0f, 2.0f}, ptAxis}); hOthers.add("hKstar_eta_pt", "Pair eta distribution; #eta; p_{T}; Counts", kTH2F, {{400, -2.0f, 2.0f}, ptAxis}); @@ -278,11 +326,21 @@ struct Kstar892LightIon { hPID.add("Before/hTOFnsigKa_Neg_mult_pt", "TOF nsigma of K^{-} before PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("Before/hTOFnsigPi_Neg_mult_pt", "TOF nsigma of #pi^{-} before PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTPCnsigKa_Neg_mult_p", "TPC nsigma of K^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTPCnsigPi_Neg_mult_p", "TPC nsigma of #pi^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTOFnsigKa_Neg_mult_p", "TOF nsigma of K^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTOFnsigPi_Neg_mult_p", "TOF nsigma of #pi^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTPCnsigKa_Pos_mult_pt", "TPC nsigma of K^{+} before PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("Before/hTPCnsigPi_Pos_mult_pt", "TPC nsigma of #pi^{+} before PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("Before/hTOFnsigKa_Pos_mult_pt", "TOF nsigma of K^{+} before PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("Before/hTOFnsigPi_Pos_mult_pt", "TOF nsigma of #pi^{+} before PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTPCnsigKa_Pos_mult_p", "TPC nsigma of K^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTPCnsigPi_Pos_mult_p", "TPC nsigma of #pi^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTOFnsigKa_Pos_mult_p", "TOF nsigma of K^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("Before/hTOFnsigPi_Pos_mult_p", "TOF nsigma of #pi^{-} before PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hNsigma_TPC_TOF_Ka_pt", "N #sigma Kaon TPC TOF after", kTH3F, {{50, -5.0f, 5.0f}, {50, -5.0f, 5.0f}, ptAxis}); hPID.add("After/hNsigma_TPC_TOF_Pi_pt", "N #sigma Pion TPC TOF after", kTH3F, {{50, -5.0f, 5.0f}, {50, -5.0f, 5.0f}, ptAxis}); @@ -291,10 +349,20 @@ struct Kstar892LightIon { hPID.add("After/hTOFnsigKa_Neg_mult_pt", "TOF nsigma of K^{-} after PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("After/hTOFnsigPi_Neg_mult_pt", "TOF nsigma of #pi^{-} after PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTPCnsigKa_Neg_mult_p", "TPC nsigma of K^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTPCnsigPi_Neg_mult_p", "TPC nsigma of #pi^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTOFnsigKa_Neg_mult_p", "TOF nsigma of K^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTOFnsigPi_Neg_mult_p", "TOF nsigma of #pi^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTPCnsigKa_Pos_mult_pt", "TPC nsigma of K^{+} after PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("After/hTPCnsigPi_Pos_mult_pt", "TPC nsigma of #pi^{+} after PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("After/hTOFnsigKa_Pos_mult_pt", "TOF nsigma of K^{+} after PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); hPID.add("After/hTOFnsigPi_Pos_mult_pt", "TOF nsigma of #pi^{+} after PID with pt and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + + hPID.add("After/hTPCnsigKa_Pos_mult_p", "TPC nsigma of K^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTPCnsigPi_Pos_mult_p", "TPC nsigma of #pi^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTOFnsigKa_Pos_mult_p", "TOF nsigma of K^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); + hPID.add("After/hTOFnsigPi_Pos_mult_p", "TOF nsigma of #pi^{-} After PID with p and centrality", kTH3F, {{100, -10.0f, 10.0f}, centralityAxis, ptAxis}); } // KStar histograms @@ -304,15 +372,15 @@ struct Kstar892LightIon { hInvMass.add("h3KstarInvMasslikeSignPP", "kstar like Sign", kTH3F, {centralityAxis, ptAxis, invmassAxis}); hInvMass.add("h3KstarInvMasslikeSignMM", "kstar like Sign", kTH3F, {centralityAxis, ptAxis, invmassAxis}); } - if (calcRotational) + if (calcRotational) { hInvMass.add("h3KstarInvMassRotated", "kstar rotated", kTH3F, {centralityAxis, ptAxis, invmassAxis}); + } // MC histograms if (doprocessGen) { hMC.add("Gen/hGenNo", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); - hMC.add("Gen/hk892GenpT", "pT distribution of True MC K(892)0", kTH2F, {ptAxis, centralityAxis}); - hMC.add("Gen/hk892GenpT2", "pT distribution of True MC K(892)0", kTH2F, {ptAxis, centralityAxis}); - hMC.add("Gen/h1genmass", "Invariant mass of generated kstar meson", kTH1F, {invmassAxis}); + hMC.add("Gen/h3KstarPtCentMassDirect", "pT distribution of generated K*0 with centrality and mass (direct from generator)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Gen/h3KstarPtCentMassRec", "pT distribution of generated K*0 with centrality and mass (reconstructed from daughter tracks)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); hMC.add("Gen/h1GenCent", "centrality generated", kTH1F, {centralityAxis}); hMC.add("Gen/hAllGenCollisions", "All generated events", kTH1F, {centralityAxis}); hMC.add("Gen/hAllGenCollisions1Rec", "All gen events with at least one rec event", kTH1F, {centralityAxis}); @@ -322,18 +390,19 @@ struct Kstar892LightIon { if (doprocessRec) { hMC.add("Rec/hAllRecCollisions", "All reconstructed events", kTH1F, {centralityAxis}); - hMC.add("Rec/h1KstarRecMass", "Invariant mass of kstar meson", kTH1F, {invmassAxis}); - hMC.add("Rec/h2KstarRecpt1", "pT of kstar meson", kTH3F, {ptAxis, centralityAxis, invmassAxis}); - hMC.add("Rec/h2KstarRecpt2", "pT of kstar meson", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Rec/h3KstarPtCentMassrec", "pT of reconstructed K*0 with centrality and mass (reconstructed from rec daughter tracks)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Rec/h3KstarPtCentMassgen", "pT of reconstructed K*0 with centrality and mass (reconstructed from gen daughter tracks)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); hMC.add("Rec/h1RecCent", "centrality reconstructed", kTH1F, {centralityAxis}); - hMC.add("Rec/h1KSRecsplit", "KS meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); + hMC.add("Rec/h1KSRecsplit", "K*0 Rec split", kTH1F, {{100, 0.0f, 10.0f}}); + + hMC.add("Rec/hMassShift", "#Delta M = m_{rec} - m_{gen}; #it{p}_{T}_{gen}; #it{p}_{T}_{rec}; #Delta M", kTH3F, {ptAxis, ptAxis, {2000, -0.1, 0.1}}); } // Signal Loss & Event Loss if (doprocessEvtLossSigLossMC) { hMC.add("ImpactCorr/hImpactParameterGen", "Impact parameter of generated MC events", kTH1F, {impactParAxis}); hMC.add("ImpactCorr/hImpactParameterRec", "Impact parameter of selected MC events", kTH1F, {impactParAxis}); - hMC.add("ImpactCorr/hImpactParvsCentrRec", "Impact parameter of selected MC events vs centrality", kTH2F, {{centralityAxis}, impactParAxis}); + hMC.add("ImpactCorr/hImpactParvsCentrRec", "Impact parameter of selected MC events vs centrality", kTH2F, {centralityAxis, impactParAxis}); hMC.add("ImpactCorr/hKstarGenBeforeEvtSel", "K*0 before event selections", kTH2F, {ptAxis, impactParAxis}); hMC.add("ImpactCorr/hKstarGenAfterEvtSel", "K*0 after event selections", kTH2F, {ptAxis, impactParAxis}); } @@ -370,7 +439,8 @@ struct Kstar892LightIon { } if (doprocessRecMisID) { - hMC.add("RecMisID/hMassMisID", "Reconstruction misidentification", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("RecMisID/hMassMisIDPiPi", "Reconstruction misidentification", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("RecMisID/hMassMisIDKK", "Reconstruction misidentification", kTH3F, {ptAxis, centralityAxis, invmassAxis}); } if (doprocessRecReflection) { @@ -378,129 +448,286 @@ struct Kstar892LightIon { hMC.add("Reflections/hOmegaToKpi", "Refelction template of Omega", kTH3F, {ptAxis, centralityAxis, invmassAxis}); hMC.add("Reflections/hPhiToKpi", "Refelction template of Phi", kTH3F, {ptAxis, centralityAxis, invmassAxis}); hMC.add("Reflections/hKstarSelf", "Refelction template of Kstar", kTH3F, {ptAxis, centralityAxis, invmassAxis}); - } - - if (doprocessMCCheck) { - hMC.add("MCCheck/CentVsFoundFT0", "Found(=1.5) NOT Found(=0.5);;Status;", kTH2F, {{{centralityAxis}, {2, 0, 2}}}); - hMC.add("MCCheck/zPosMC", "Generated Events With at least One Rec. Collision + Sel. criteria;;Entries;", kTH1F, {vertexZAxis}); - hMC.add("MCCheck/zPos", "With Event Selection;;Entries;", kTH1F, {vertexZAxis}); - hMC.add("MCCheck/Cent", ";;Entries", kTH1F, {centralityAxis}); - hMC.add("MCCheck/NchVsCent", "Measured Nch v.s. Centrality (At least Once Rec. Coll. + Sel. criteria);;Nch", kTH2F, {{centralityAxis, {nBinsNch, minNch, maxNch}}}); - - // MC events passing the TVX requirement - hMC.add("MCCheck/NchMCcentVsTVX", ";Passed(=1.5) NOT Passed(=0.5);", kTH2F, {{{nBinsNch, minNch, maxNch}, {2, 0, 2}}}); - - hMC.add("MCCheck/NumberOfRecoCollisions", "Number of times Gen. Coll.are reconstructed;N;Entries", kTH1F, {{10, -0.5, 9.5}}); - - // Needed for the Gen. Nch to Centrality conversion - hMC.add("MCCheck/NchMCVsCent", "Generated Nch v.s. Centrality (At least Once Rec. Coll. + Sel. criteria);;Gen. Nch MC (|#eta|<0.8)", kTH2F, {{centralityAxis, {nBinsNch, minNch, maxNch}}}); - - // Needed to measure Event Loss - hMC.add("MCCheck/NchMC_WithRecoEvt", "Generated Nch of Evts With at least one Rec. Coll. + Sel. criteria;Gen. Nch MC (|#eta|<0.8);Entries", kTH1F, {{nBinsNch, minNch, maxNch}}); - hMC.add("MCCheck/NchMC_AllGen", "Generated Nch of All Gen. Evts.;Gen. Nch;Entries", kTH1F, {{nBinsNch, minNch, maxNch}}); - - // Needed to measure Event Splitting - hMC.add("MCCheck/Centrality_WRecoEvt", "Generated Events With at least One Rec. Collision And NO Sel. criteria;;Entries", kTH1F, {centralityAxis}); - hMC.add("MCCheck/Centrality_WRecoEvtWSelCri", "Generated Events With at least One Rec. Collision + Sel. criteria;;Entries", kTH1F, {centralityAxis}); - hMC.add("MCCheck/Centrality_AllRecoEvt", "Generated Events Irrespective of the number of times it was reconstructed + Evt. Selections;;Entries", kTH1F, {centralityAxis}); - - hMC.add("MCCheck/PtKstarVsCentMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;;", kTH2F, {ptAxis, centralityAxis}); - - // Needed to calculate the numerator of the Signal Loss correction - hMC.add("MCCheck/PtKstarVsNchMC_WithRecoEvt", "Generated Events With at least One Rec. Collision;;Gen. Nch (|#eta|<0.8);", kTH2F, {{ptAxis, {nBinsNch, minNch, maxNch}}}); - - // Needed to calculate the denominator of the Signal Loss correction - hMC.add("MCCheck/PtKstarVsNchMC_AllGen", "All Generated Events;;Gen. Nch (|#eta|<0.8);", kTH2F, {{ptAxis, {nBinsNch, minNch, maxNch}}}); + hMC.add("Reflections/hEtaToKpi", "Refelction template of Eta", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Reflections/hEtaPrimeToKpi", "Refelction template of Eta'", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("FeedDown/hK1_1270", "Distribution for K1(1270)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hK1_1400", "Distribution for K1(1270)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hKstar1410", "Distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hKstar0_1430_0", "Distribution for K*0(1430)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hKstar0_1430_ch", "Distribution for K*+-(1430)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hKstar2_1430", "Distribution for K*2(1430)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hK2_1770", "Distribution for K2(1770)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hK2_1820", "Distribution for K2(1820)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("FeedDown/hKstar1680", "Distribution for K2(1680)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + } + + if (doprocessRecCorrelatedBackground) { + hMC.add("CorrelatedBG/hKstar1410", "Wrong pair distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("CorrelatedBG/hKstar0_1430", "Wrong pair distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("CorrelatedBG/hK1_1270", "Wrong pair distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("CorrelatedBG/hK1_1400", "Wrong pair distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("CorrelatedBG/hKstar1680", "Wrong pair distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("CorrelatedBG/hK2_1770", "Wrong pair distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("CorrelatedBG/hK2_1820", "Wrong pair distribution for K*(1410)", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + } + if (doprocessTemplateMC) { + hMC.add("Template/hSignal", "True K*0 signal", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Template/hKstarReflection", "K*0 signal due to mis-identification", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Template/hSameMotherOther", "Kpi pair from same mother other than K*0", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Template/hDifferentMother", "Kpi pair from different mothers", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Template/hPrimaryResonance", "Kpi pair one primary another from decay", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Template/hPrimaryPrimary", "Primary Kpi pair", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + } + + if (doprocessRecKinematics) { + hMC.add("Kinematics/h1RecCent", "centrality reconstructed", kTH1F, {centralityAxis}); + + hMC.add("Kinematics/hPDGMotherKstar", "Mother PDG of Ka and Pi from K*0", kTH2F, {{100000, -50000.0, 50000.0}, invmassAxis}); + hMC.add("Kinematics/hOpenAngleKstar", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH2F, {{180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("Kinematics/hDeltaPhiKstar", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH2F, {{180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("Kinematics/hDeltaEtaKstar", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH2F, {{200, -2.0, 2.0}, invmassAxis}); + hMC.add("Kinematics/hDeltaRKstar", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH2F, {{200, 0.0, 5.0}, invmassAxis}); + hMC.add("Kinematics/hKaonPtKstar", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH2F, {ptAxis, invmassAxis}); + hMC.add("Kinematics/hPionPtKstar", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH2F, {ptAxis, invmassAxis}); + + hMC.add("Kinematics/hPtCentMassKstar", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("Kinematics/hOpenAngleOther", "Opening angle vs M(K#pi) (non-K*);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH2F, {{180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("Kinematics/hDeltaPhiOther", "#Delta#phi vs M(K#pi) (non-K*);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH2F, {{180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("Kinematics/hDeltaEtaOther", "#Delta#eta vs M(K#pi) (non-K*);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH2F, {{200, -2.0, 2.0}, invmassAxis}); + hMC.add("Kinematics/hDeltaROther", "#DeltaR vs M(K#pi) (non-K*);#DeltaR;M(K#pi) (GeV/c^{2})", kTH2F, {{200, 0.0, 5.0}, invmassAxis}); + hMC.add("Kinematics/hKaonPtOther", "Kaon p_{T} vs M(K#pi) (non-K*);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH2F, {ptAxis, invmassAxis}); + hMC.add("Kinematics/hPionPtOther", "Pion p_{T} vs M(K#pi) (non-K*);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH2F, {ptAxis, invmassAxis}); + hMC.add("Kinematics/hPtCentMassOther", "p_{T} vs Centrality vs M(K#pi) (non-K*);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + hMC.add("Kinematics/hOtherMotherPDG", "Common mother PDG of non-K* pairs;PDG Code;M(K#pi) (GeV/c^{2})", kTH2F, {{100000, -50000.0, 50000.0}, invmassAxis}); + } + + if (doprocessMisIdKinematics) { + hMC.add("KinematicsMisId/hRecCent", "centrality reconstructed", kTH1F, {centralityAxis}); + + hMC.add("KinematicsMisId/hTrueKstar_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hTrueKstar_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hOmega_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hOmega_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hRho_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hRho_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hEta_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hEta_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hEtaP_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hEtaP_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hPhi_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hPhi_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hKstarMisId_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hKstarMisId_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hOtherMisId_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMisId_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hK0s_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hK0s_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hLambda_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hLambda_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hUnknownMisIdMotherPDG", "PDG Code of unknown mothers for fake Kpi pairs;PDG Code", kTH1F, {{100000, -50000.0, 50000.0}}); + hMC.add("KinematicsMisId/hOtherMother_PtDeltaRMass", "#DeltaR vs M(K#pi);#DeltaR;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, 0.0, 5.0}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMother_PtCentMass", "p_{T} vs Centrality vs M(K#pi);p_{T} (GeV/c);Centrality (%);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, centralityAxis, invmassAxis}); + + if (additionalKin) { + hMC.add("KinematicsMisId/hTrueKstar_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hTrueKstar_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hTrueKstar_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hTrueKstar_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hTrueKstar_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hOmega_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hOmega_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hOmega_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hOmega_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hOmega_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hRho_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hRho_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hRho_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hRho_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hRho_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hEta_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hEta_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hEta_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hEta_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hEta_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hEtaP_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hEtaP_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hEtaP_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hEtaP_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hEtaP_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hPhi_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hPhi_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hPhi_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hPhi_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hPhi_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hKstarMisId_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hKstarMisId_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hKstarMisId_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hKstarMisId_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hKstarMisId_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hOtherMisId_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMisId_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMisId_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMisId_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMisId_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hK0s_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hK0s_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hK0s_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hK0s_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hK0s_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hLambda_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hLambda_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hLambda_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hLambda_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hLambda_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + + hMC.add("KinematicsMisId/hOtherMother_PtOpenAngleMass", "Opening angle vs M(K#pi);Opening angle (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, 0., o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMother_PtDeltaPhiMass", "#Delta#phi vs M(K#pi);#Delta#phi (rad);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {180, -o2::constants::math::PI, o2::constants::math::PI}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMother_PtDeltaEtaMass", "#Delta#eta vs M(K#pi);#Delta#eta;M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, {200, -2.0, 2.0}, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMother_PtKaonPtMass", "Kaon p_{T} vs M(K#pi);p_{T}^{K} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + hMC.add("KinematicsMisId/hOtherMother_PtPionPtMass", "Pion p_{T} vs M(K#pi);p_{T}^{#pi} (GeV/c);M(K#pi) (GeV/c^{2})", kTH3F, {ptAxis, ptAxis, invmassAxis}); + } } } - double massPi = o2::constants::physics::MassPiPlus; - double massKa = o2::constants::physics::MassKPlus; - template bool selectionEvent(const Coll& collision, bool fillHist = false) // default to false { - if (fillHist) + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 0); + } - if (std::abs(collision.posZ()) > selectionConfig.cfgVrtxZCut) + if (std::abs(collision.posZ()) > selectionConfig.cfgVrtxZCut) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 1); + } - if (selectionConfig.isApplysel8 && !collision.sel8()) + if (selectionConfig.isApplysel8 && !collision.sel8()) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 2); + } - if (selectionConfig.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + if (selectionConfig.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 3); + } - if (selectionConfig.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + if (selectionConfig.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 4); + } - if (selectionConfig.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) + if (selectionConfig.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 5); + } - if (selectionConfig.isNoSameBunchPileup && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup))) + if (selectionConfig.isNoSameBunchPileup && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup))) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 6); + } - if (selectionConfig.isGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + if (selectionConfig.isGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 7); + } - if (selectionConfig.isNoCollInTimeRangeStandard && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) + if (selectionConfig.isNoCollInTimeRangeStandard && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 8); + } - if (selectionConfig.isApplyOccCut && (std::abs(collision.trackOccupancyInTimeRange()) > selectionConfig.cfgOccCut)) + if (selectionConfig.isApplyOccCut && (std::abs(collision.trackOccupancyInTimeRange()) > selectionConfig.cfgOccCut)) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 9); + } - if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) + if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 10); + } - if (selectionConfig.isGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) + if (selectionConfig.isGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; - if (fillHist) + } + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 11); + } if (selectionConfig.isVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } - if (fillHist) + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 12); + } if (selectionConfig.isVertexTOFMatched && !collision.selection_bit(aod::evsel::kIsVertexTOFmatched)) { return false; } - if (fillHist) + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 13); + } if (selectionConfig.isApplyINELgt0 && !collision.isInelGt0()) { return false; } - if (fillHist) + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 14); + } if (selectionConfig.isApplyhasFT0 && !collision.has_foundFT0()) { return false; } - if (fillHist) + if (fillHist) { hEventSelection.fill(HIST("hEventCut"), 15); + } + + if (selectionConfig.isApplyhasBC && !collision.has_foundBC()) { + return false; + } + if (fillHist) { + hEventSelection.fill(HIST("hEventCut"), 16); + } return true; } @@ -509,63 +736,81 @@ struct Kstar892LightIon { bool selectionTrack(const T& candidate) { if (selectionConfig.isGlobalTracks) { - if (!candidate.isGlobalTrackWoDCA()) + if (!candidate.isGlobalTrackWoDCA()) { return false; - if (std::abs(candidate.pt()) < selectionConfig.cfgCutPT) + } + if (std::abs(candidate.pt()) < selectionConfig.cfgCutPT) { return false; + } - if (std::abs(candidate.eta()) > selectionConfig.cfgCutEta) + if (std::abs(candidate.eta()) > selectionConfig.cfgCutEta) { return false; + } if (!selectionConfig.isApplyPtDepDCAxyCut) { - if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxy) + if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxy) { return false; + } } else { - if (std::abs(candidate.dcaXY()) > (0.0105 + 0.035 / std::pow(candidate.pt(), 1.1))) + if (std::abs(candidate.dcaXY()) > (0.0105 + 0.035 / std::pow(candidate.pt(), 1.1))) { return false; + } } - if (selectionConfig.isGoldenChi2 && !candidate.passedGoldenChi2()) - return false; - if (std::abs(candidate.dcaZ()) > selectionConfig.cfgCutDCAz) + if (selectionConfig.isGoldenChi2 && !candidate.passedGoldenChi2()) { return false; - if (candidate.tpcCrossedRowsOverFindableCls() < selectionConfig.cfgRCRFC) - return false; - if (candidate.itsNCls() < selectionConfig.cfgITScluster) + } + if (std::abs(candidate.dcaZ()) > selectionConfig.cfgCutDCAz) { return false; - if (candidate.tpcNClsFound() < selectionConfig.cfgTPCcluster) + } + if (candidate.itsNCls() < selectionConfig.cfgITScluster) { return false; - if (candidate.itsChi2NCl() >= selectionConfig.cfgITSChi2NCl) + } + if (candidate.tpcNClsFound() < selectionConfig.cfgTPCcluster) { return false; - if (candidate.tpcChi2NCl() >= selectionConfig.cfgTPCChi2NClMax || candidate.tpcChi2NCl() < selectionConfig.cfgTPCChi2NClMin) + } + if (candidate.itsChi2NCl() >= selectionConfig.cfgITSChi2NCl) { return false; - if (selectionConfig.isPVContributor && !candidate.isPVContributor()) + } + if (candidate.tpcChi2NCl() >= selectionConfig.cfgTPCChi2NClMax || candidate.tpcChi2NCl() < selectionConfig.cfgTPCChi2NClMin) { return false; - if (selectionConfig.isUseITSTPCRefit && (!(o2::aod::track::ITSrefit) || !(o2::aod::track::TPCrefit))) + } + if (selectionConfig.isPVContributor && !candidate.isPVContributor()) { return false; - } else if (!selectionConfig.isGlobalTracks) { - if (std::abs(candidate.pt()) < selectionConfig.cfgCutPT) + } + // if (selectionConfig.isUseITSTPCRefit && (!(o2::aod::track::ITSrefit) || !(o2::aod::track::TPCrefit))) + // return false; + } else { + if (std::abs(candidate.pt()) < selectionConfig.cfgCutPT) { return false; + } // if (std::abs(candidate.eta()) > selectionConfig.cfgCutEta || std::abs(candidate.eta()) < selectionConfig.cfgCutEtaMin) - if (std::abs(candidate.eta()) > selectionConfig.cfgCutEta) + if (std::abs(candidate.eta()) > selectionConfig.cfgCutEta) { return false; + } // if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxy || std::abs(candidate.dcaXY()) < selectionConfig.cfgCutDCAxyMin) - if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxy) - return false; - if (std::abs(candidate.dcaZ()) > selectionConfig.cfgCutDCAz) + if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxy) { return false; - if (candidate.tpcCrossedRowsOverFindableCls() < selectionConfig.cfgRCRFC) + } + if (std::abs(candidate.dcaZ()) > selectionConfig.cfgCutDCAz) { return false; - if (candidate.itsNCls() < selectionConfig.cfgITScluster) + } + if (candidate.itsNCls() < selectionConfig.cfgITScluster) { return false; - if (candidate.tpcNClsFound() < selectionConfig.cfgTPCcluster) + } + if (candidate.tpcNClsFound() < selectionConfig.cfgTPCcluster) { return false; - if (candidate.itsChi2NCl() >= selectionConfig.cfgITSChi2NCl) + } + if (candidate.itsChi2NCl() >= selectionConfig.cfgITSChi2NCl) { return false; - if (candidate.tpcChi2NCl() >= selectionConfig.cfgTPCChi2NClMax || candidate.tpcChi2NCl() < selectionConfig.cfgTPCChi2NClMin) + } + if (candidate.tpcChi2NCl() >= selectionConfig.cfgTPCChi2NClMax || candidate.tpcChi2NCl() < selectionConfig.cfgTPCChi2NClMin) { return false; - if (selectionConfig.isPVContributor && !candidate.isPVContributor()) + } + if (selectionConfig.isPVContributor && !candidate.isPVContributor()) { return false; - if (selectionConfig.isPrimaryTrack && !candidate.isPrimaryTrack()) + } + if (selectionConfig.isPrimaryTrack && !candidate.isPrimaryTrack()) { return false; + } } return true; @@ -575,229 +820,347 @@ struct Kstar892LightIon { template bool selectionPair(const T1& candidate1, const T2& candidate2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; - pt1 = candidate1.pt(); - pt2 = candidate2.pt(); - pz1 = candidate1.pz(); - pz2 = candidate2.pz(); - p1 = candidate1.p(); - p2 = candidate2.p(); - angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - if (selectionConfig.isApplyDeepAngle && angle < selectionConfig.cfgDeepAngle) { - return false; + if (!selectionConfig.isApplyDeepAngle) { + return true; } - return true; + + const double cosAngle = std::clamp(static_cast((candidate1.pt() * candidate2.pt() + candidate1.pz() * candidate2.pz())) / (candidate1.p() * candidate2.p()), -1.0, 1.0); + const double cosDeepAngle = std::cos(selectionConfig.cfgDeepAngle); + return cosAngle <= cosDeepAngle; } template - bool selectionPID(const T& candidate, int PID) + bool isFakeTrack(const T& track, PIDParticle PID) { + const auto pglobal = track.p(); + const auto ptpc = track.tpcInnerParam(); if (PID == PIDParticle::kPion) { - if (selectionConfig.onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTOFPi && candidate.beta() > selectionConfig.cfgBetaCutTOF) { - return true; - } - } else if (selectionConfig.onlyTOFHIT) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTOFPi && candidate.beta() > selectionConfig.cfgBetaCutTOF) { - return true; - } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { - return true; - } - } else if (selectionConfig.onlyTPC) { - if (std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { - return true; - } - } else { - if (candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (selectionConfig.nsigmaCutCombinedPi * selectionConfig.nsigmaCutCombinedPi) && candidate.beta() > selectionConfig.cfgBetaCutTOF) { - return true; - } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { - return true; - } + if (std::abs(pglobal - ptpc) > selectionConfig.cfgFakeTrackCutPi) { + return true; } } else if (PID == PIDParticle::kKaon) { - if (selectionConfig.onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTOFKa && candidate.beta() > selectionConfig.cfgBetaCutTOF) { - return true; - } - } else if (selectionConfig.onlyTOFHIT) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTOFKa && candidate.beta() > selectionConfig.cfgBetaCutTOF) { - return true; - } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { - return true; - } - } else if (selectionConfig.onlyTPC) { - if (std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { - return true; - } - } else { - if (candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (selectionConfig.nsigmaCutCombinedKa * selectionConfig.nsigmaCutCombinedKa) && candidate.beta() > selectionConfig.cfgBetaCutTOF) { - return true; - } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { - return true; - } + if (std::abs(pglobal - ptpc) > selectionConfig.cfgFakeTrackCutKa) { + return true; } } return false; } template - bool selectionPIDpTdep(const T& candidate, int PID) + float tpcSigma(const T& c, PIDParticle pid) { - if (PID == PIDParticle::kPion) { - if (candidate.pt() < selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { - return true; - } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi && candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTOFPi) { - return true; - } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi && !candidate.hasTOF()) { - return true; - } - } else if (PID == PIDParticle::kKaon) { - if (candidate.pt() < selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { - return true; - } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTOFKa) { - return true; - } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && !candidate.hasTOF()) { - return true; - } + return pid == PIDParticle::kPion ? c.tpcNSigmaPi() : c.tpcNSigmaKa(); + } + + /* template + float tofSigma(const T& c, PIDParticle pid) + { + return pid == PIDParticle::kPion ? c.tofNSigmaPi() : c.tofNSigmaKa(); + } */ + + template + float tofSigma(const T& c, PIDParticle pid) + { + return pid == PIDParticle::kPion ? (c.tofNSigmaPi() - selectionConfig.shiftTofNsigmaPi) : (c.tofNSigmaKa() - selectionConfig.shiftTofNsigmaKa); + } + + template + float combinedNSigma2(const T& c, PIDParticle pid) + { + const float tpc = tpcSigma(c, pid); + const float tof = tofSigma(c, pid); + + return tpc * tpc + tof * tof; + } + + float tpcCut(PIDParticle pid) + { + return pid == PIDParticle::kPion ? selectionConfig.nsigmaCutTPCPi : selectionConfig.nsigmaCutTPCKa; + } + + float tofCut(PIDParticle pid) + { + return pid == PIDParticle::kPion ? selectionConfig.nsigmaCutTOFPi : selectionConfig.nsigmaCutTOFKa; + } + + float combinedCut(PIDParticle pid) + { + return pid == PIDParticle::kPion ? selectionConfig.nsigmaCutCombinedPi : selectionConfig.nsigmaCutCombinedKa; + } + + template + bool passTPC(const T& c, PIDParticle pid, float cut) + { + return std::abs(tpcSigma(c, pid)) < cut; + } + + template + bool passTOF(const T& c, PIDParticle pid, float cut) + { + return c.hasTOF() && c.beta() > selectionConfig.cfgTOFBetaCut && std::abs(tofSigma(c, pid)) < cut; + } + + template + bool passCombined(const T& c, PIDParticle pid, float cut) + { + if (!c.hasTOF() || c.beta() <= selectionConfig.cfgTOFBetaCut) { + return false; } - return false; + + return combinedNSigma2(c, pid) < cut * cut; } template - bool selectionPIDpTdepTOF(const T& candidate, int PID) + bool selectionPID(const T& candidate, PIDParticle pid) { - if (PID == PIDParticle::kPion) { - if (candidate.pt() < pionPIDpTlow || candidate.pt() > pionPIDpThigh) { - if (candidate.pt() < selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { - return true; - } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi && candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTOFPi) { - return true; - } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi && !candidate.hasTOF()) { - return true; - } - } else { - if (candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (selectionConfig.nsigmaCutCombinedPi * selectionConfig.nsigmaCutCombinedPi)) { - return true; + const auto strategy = static_cast(selectionConfig.pidStrategy.value); + const auto mode = static_cast(selectionConfig.pidMode.value); + + switch (strategy) { + case PIDStrategy::Standard: { + switch (mode) { + case PIDMode::TPC: // Apply only TPC cut + return passTPC(candidate, pid, tpcCut(pid)); + + case PIDMode::TOF: // Apply only TOF cut + return passTOF(candidate, pid, tofCut(pid)); + + case PIDMode::TOFVeto: // Require TOF hit; apply both TPC and TOF cuts separately. Tracks without TOF are rejected. + return passTPC(candidate, pid, tpcCut(pid)) && passTOF(candidate, pid, tofCut(pid)); + + case PIDMode::TOFHIT: // Apply only TOF cut that has tof hits, apply TPC cut for the rest + if (candidate.hasTOF()) { + return passTOF(candidate, pid, tofCut(pid)); + } + + return passTPC(candidate, pid, tpcCut(pid)); + + case PIDMode::Combined: // Apply combined cut if TOF is available, apply TPC cut for the rest + if (candidate.hasTOF()) { + return passCombined(candidate, pid, combinedCut(pid)); + } + + return passTPC(candidate, pid, tpcCut(pid)); + + default: + LOGF(fatal, "Unknown PID mode!"); + return false; } } - } else if (PID == PIDParticle::kKaon) { - if (candidate.pt() < kaonPIDpTlow || candidate.pt() > kaonPIDpThigh) { - if (candidate.pt() < selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { - return true; - } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTOFKa) { - return true; + + case PIDStrategy::PtDependent: // Apply TPC and TOF cut above lowPtCut, apply TPC cut for the rest + { + if (candidate.hasTOF() && candidate.pt() >= selectionConfig.lowPtCutPid) { + return passTPC(candidate, pid, tpcCut(pid)) && passTOF(candidate, pid, tofCut(pid)); } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && !candidate.hasTOF()) { - return true; + + return passTPC(candidate, pid, tpcCut(pid)); + } + + case PIDStrategy::PtDependentTOF: // Apply combined cut (requires TOF) in specific pT window, apply TPC+TOF cut for tracks with TOF hit above lowPtCut, apply TPC cut for the rest. + { + const bool inPtWindow = (pid == PIDParticle::kPion) ? (candidate.pt() >= pionPidPtLow && candidate.pt() <= pionPidPtHigh) : (candidate.pt() >= kaonPidPtLow && candidate.pt() <= kaonPidPtHigh); + + if (inPtWindow) { + return passCombined(candidate, pid, combinedCut(pid)); } - } else { - if (candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (selectionConfig.nsigmaCutCombinedKa * selectionConfig.nsigmaCutCombinedKa)) { - return true; + + if (candidate.hasTOF() && candidate.pt() >= selectionConfig.lowPtCutPid) { + return passTPC(candidate, pid, tpcCut(pid)) && passTOF(candidate, pid, tofCut(pid)); } + + return passTPC(candidate, pid, tpcCut(pid)); } - } - return false; - } - template - bool selectionMID(const T& candidate, int PID) - { - if (PID == PIDParticle::kPion) { - if (selectionConfig.onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { - return true; - } - } else if (selectionConfig.onlyTPC) { - if (std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { - return true; + /* case PIDStrategy::ThreePtDependent: // Apply pT-dependent TPC and TOF cuts using three pT regions + { + const int region = (candidate.pt() < selectionConfig.lowPtCutPid) ? 0 : (candidate.pt() < selectionConfig.highPtCutPid) ? 1 + : 2; + + const float regionTPCCut = (pid == PIDParticle::kPion) ? TPCPiCuts3Pt[region] : TPCKaCuts3Pt[region]; + + const float regionTOFCut = (pid == PIDParticle::kPion) ? TOFPiCuts3Pt[region] : TOFKaCuts3Pt[region]; + + if (candidate.hasTOF()) { + return (std::abs(tpcSigma(candidate, pid)) < regionTPCCut) && (std::abs(tofSigma(candidate, pid)) < regionTOFCut) && (candidate.beta() > selectionConfig.cfgTOFBetaCut); + } + + return (std::abs(tpcSigma(candidate, pid)) < regionTPCCut); + } */ + + case PIDStrategy::ThreePtDependent: // Apply region-dependent PID cuts using one PID cut per pT region. The middle pT region supports two TOF modes. + { + const float pidCut = (candidate.pt() < selectionConfig.lowPtCutPid) ? selectionConfig.lowPtPid : (candidate.pt() < selectionConfig.highPtCutPid) ? selectionConfig.midPtPid + : selectionConfig.highPtPid; + + // Low-pT region + if (candidate.pt() < selectionConfig.lowPtCutPid) { + + if (!candidate.hasTOF()) { + return passTPC(candidate, pid, pidCut); + } + + return passCombined(candidate, pid, pidCut); } - } else { - if (candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (selectionConfig.nsigmaCutCombinedMID * selectionConfig.nsigmaCutCombinedMID)) { - return true; + + // Mid-pT region + if (candidate.pt() < selectionConfig.highPtCutPid) { + switch (mode) { + case PIDMode::TOFVeto: // Mid-pT region (TOF mandatory with combined cut) + if (!candidate.hasTOF() || candidate.beta() <= selectionConfig.cfgTOFBetaCut) { + return false; + } + return passCombined(candidate, pid, pidCut); + + case PIDMode::Combined: // Mid-pT region (If TOF available use combined cut, otherwise use TPC cut) + if (!candidate.hasTOF()) { + return passTPC(candidate, pid, pidCut); + } + + return passCombined(candidate, pid, pidCut); + + default: + LOGF(fatal, "ThreePtDependent supports only Combined and TOFVeto modes."); + return false; + } } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { - return true; + + // High-pT region + if (!candidate.hasTOF()) { + return passTPC(candidate, pid, pidCut); } + + return passCombined(candidate, pid, pidCut); } - } else if (PID == PIDParticle::kKaon) { - if (selectionConfig.onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { - return true; - } - } else if (selectionConfig.onlyTPC) { - if (std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { - return true; + + case PIDStrategy::PIDCompare: // Apply independent TPC/TOF cuts below lowPtCutPid, within lowPtCutPid and highPtCutPid, require TOF and accept the track only if its combined nsigma is smaller than that of the alternate PID hypothesis and above highPtCutPid if TOF is available, accept if the track passes combined cut and if TOF is not available, check if it passes TPC cut + { + if (candidate.pt() < selectionConfig.lowPtCutPid) { + if (candidate.hasTOF()) { + return passTPC(candidate, pid, tpcCut(pid)) && passTOF(candidate, pid, tofCut(pid)); + } + return passTPC(candidate, pid, tpcCut(pid)); } - } else { - if (candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (selectionConfig.nsigmaCutCombinedMID * selectionConfig.nsigmaCutCombinedMID)) { - return true; + + if (candidate.pt() < selectionConfig.highPtCutPid) { + if (!candidate.hasTOF() || candidate.beta() <= selectionConfig.cfgTOFBetaCut) { + return false; + } + + const float sigmaComb2 = combinedNSigma2(candidate, pid); + + const float cut = combinedCut(pid); + if (sigmaComb2 >= cut * cut) { + return false; + } + + const PIDParticle otherPID = (pid == PIDParticle::kPion) ? PIDParticle::kKaon : PIDParticle::kPion; + + return sigmaComb2 < combinedNSigma2(candidate, otherPID); } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { - return true; + + if (candidate.hasTOF()) { + return passCombined(candidate, pid, combinedCut(pid)); } + + return passTPC(candidate, pid, tpcCut(pid)); } + + default: + LOGF(fatal, "Unknown PID strategy!"); } return false; } template - bool selectionMIDpTdep(const T& candidate, int PID) + bool isMisidentified(const T& candidate, PIDParticle pid) { - if (PID == PIDParticle::kPion) { - if (candidate.pt() >= pionPIDpTlow && candidate.pt() < pionPIDpThigh && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { - return true; + const auto strategy = static_cast(selectionConfig.misIdStrategy.value); + const PIDParticle misPID = (pid == PIDParticle::kPion) ? PIDParticle::kKaon : PIDParticle::kPion; + + switch (strategy) { + case MIDStrategy::Disabled: + return false; + + case MIDStrategy::Standard: // Reject if track also satisfies TPC cut for the opposite hypothesis + return std::abs(tpcSigma(candidate, misPID)) < selectionConfig.nsigmaCutTpcMisId; + + case MIDStrategy::PtDependent: // Reject if track is in MID pT window, has no TOF, and passes opposite TPC cut + { + const float lowPt = (pid == PIDParticle::kPion) ? selectionConfig.pionMisIdPtLow : selectionConfig.kaonMisIdPtLow; + const float highPt = (pid == PIDParticle::kPion) ? selectionConfig.pionMisIdPtHigh : selectionConfig.kaonMisIdPtHigh; + + return candidate.pt() >= lowPt && candidate.pt() < highPt && !candidate.hasTOF() && std::abs(tpcSigma(candidate, misPID)) < selectionConfig.nsigmaCutTpcMisId; } - } else if (PID == PIDParticle::kKaon) { - if (candidate.pt() >= kaonPIDpTlow && candidate.pt() < kaonPIDpThigh && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { - return true; + + case MIDStrategy::PtDependentCompare: // Reject if track is in MID pT window, has no TOF, and looks more like the opposite hypothesis than the signal hypothesis + { + const float lowPt = (pid == PIDParticle::kPion) ? selectionConfig.pionMisIdPtLow : selectionConfig.kaonMisIdPtLow; + const float highPt = (pid == PIDParticle::kPion) ? selectionConfig.pionMisIdPtHigh : selectionConfig.kaonMisIdPtHigh; + + return candidate.pt() >= lowPt && candidate.pt() < highPt && !candidate.hasTOF() && std::abs(tpcSigma(candidate, misPID)) < std::abs(tpcSigma(candidate, pid)); } + + default: + LOGF(fatal, "Unknown MID strategy!"); } return false; } + template + std::vector> getAncestors(const MCParticle& particle) + { + std::vector> ancestors; + + std::function collect = [&](const auto& p) { + for (const auto& mother : p.template mothers_as()) { + + ancestors.emplace_back(mother.globalIndex(), std::abs(mother.pdgCode())); + collect(mother); + } + }; + + collect(particle); + + return ancestors; + } + //*********Varibles declaration*************** - float centrality{-1.0}, theta2; - ROOT::Math::PxPyPzMVector daughter1, daughter2, daughterRot, mother, motherRot; + ROOT::Math::PxPyPzMVector daughter1, daughter2, daughterRot, genDaughter1, genDaughter2, mother, motherRot, genMother; + + float centrality = -1.0f, theta2 = 0.0f; + double genMass = 0.0, recMass = 0.0, recPt = 0.0, genPt = 0.0; + bool isMix = false; template - void fillInvMass(const T1& daughter1, const T1& daughter2, const T1& mother, float centrality, bool isMix, const T2& track1, const T2& track2) + void fillInvMass(const T1& argDaughter1, const T1& argDaughter2, const T1& argMother, float argCentrality, bool argIsMix, const T2& track1, const T2& track2) { if (track1.sign() * track2.sign() < 0) { - if (!isMix) { - if (mother.Rapidity() > selectionConfig.motherRapidityMin && mother.Rapidity() < selectionConfig.motherRapidityMax) { - hInvMass.fill(HIST("h3KstarInvMassUnlikeSign"), centrality, mother.Pt(), mother.M()); + if (!argIsMix) { + if (argMother.Rapidity() > selectionConfig.motherRapidityMin && argMother.Rapidity() < selectionConfig.motherRapidityMax) { + hInvMass.fill(HIST("h3KstarInvMassUnlikeSign"), argCentrality, argMother.Pt(), argMother.M()); } for (int i = 0; i < cRotations; i++) { theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / rotationalCut, o2::constants::math::PI + o2::constants::math::PI / rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); - motherRot = daughterRot + daughter2; + daughterRot = ROOT::Math::PxPyPzMVector(argDaughter1.Px() * std::cos(theta2) - argDaughter1.Py() * std::sin(theta2), argDaughter1.Px() * std::sin(theta2) + argDaughter1.Py() * std::cos(theta2), argDaughter1.Pz(), argDaughter1.M()); + motherRot = daughterRot + argDaughter2; - if (calcRotational && (motherRot.Rapidity() > selectionConfig.motherRapidityMin && motherRot.Rapidity() < selectionConfig.motherRapidityMax)) - hInvMass.fill(HIST("h3KstarInvMassRotated"), centrality, motherRot.Pt(), motherRot.M()); + if (calcRotational && (motherRot.Rapidity() > selectionConfig.motherRapidityMin && motherRot.Rapidity() < selectionConfig.motherRapidityMax)) { + hInvMass.fill(HIST("h3KstarInvMassRotated"), argCentrality, motherRot.Pt(), motherRot.M()); + } } - } else if (isMix && (mother.Rapidity() > selectionConfig.motherRapidityMin && mother.Rapidity() < selectionConfig.motherRapidityMax)) { - hInvMass.fill(HIST("h3KstarInvMassMixed"), centrality, mother.Pt(), mother.M()); + } else if (argMother.Rapidity() > selectionConfig.motherRapidityMin && argMother.Rapidity() < selectionConfig.motherRapidityMax) { + hInvMass.fill(HIST("h3KstarInvMassMixed"), argCentrality, argMother.Pt(), argMother.M()); } } else { - if (!isMix) { - if (calcLikeSign && (mother.Rapidity() > selectionConfig.motherRapidityMin && mother.Rapidity() < selectionConfig.motherRapidityMax)) { + if (!argIsMix) { + if (calcLikeSign && (argMother.Rapidity() > selectionConfig.motherRapidityMin && argMother.Rapidity() < selectionConfig.motherRapidityMax)) { if (track1.sign() > 0 && track2.sign() > 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignPP"), centrality, mother.Pt(), mother.M()); + hInvMass.fill(HIST("h3KstarInvMasslikeSignPP"), argCentrality, argMother.Pt(), argMother.M()); } else if (track1.sign() < 0 && track2.sign() < 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignMM"), centrality, mother.Pt(), mother.M()); + hInvMass.fill(HIST("h3KstarInvMasslikeSignMM"), argCentrality, argMother.Pt(), argMother.M()); } } } @@ -836,16 +1199,14 @@ struct Kstar892LightIon { centrality = -1; - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); // default + centrality = collision.centFT0M(); // default includes kFT0M } /* else if (selectCentEstimator == 4) { @@ -869,63 +1230,68 @@ struct Kstar892LightIon { continue; } - if (track1.globalIndex() == track2.globalIndex()) + if (track1.globalIndex() == track2.globalIndex()) { continue; + } if (!selectionPair(track1, track2)) { continue; } if (cQAplots) { - hOthers.fill(HIST("hCRFC_before"), track1.tpcCrossedRowsOverFindableCls()); hOthers.fill(HIST("dE_by_dx_TPC"), track1.p(), track1.tpcSignal()); if (track1.sign() < 0) { hPID.fill(HIST("Before/hTPCnsigKa_Neg_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); hPID.fill(HIST("Before/hTPCnsigPi_Neg_mult_pt"), track1.tpcNSigmaPi(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_pt"), track1.tofNSigmaPi(), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_pt"), tofSigma(track1, PIDParticle::kPion), centrality, track1.pt()); + + hPID.fill(HIST("Before/hTPCnsigKa_Neg_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("Before/hTPCnsigPi_Neg_mult_p"), track1.tpcNSigmaPi(), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_p"), tofSigma(track1, PIDParticle::kPion), centrality, track1.p()); } else if (track1.sign() > 0) { hPID.fill(HIST("Before/hTPCnsigKa_Pos_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); hPID.fill(HIST("Before/hTPCnsigPi_Pos_mult_pt"), track1.tpcNSigmaPi(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_pt"), track1.tofNSigmaPi(), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_pt"), tofSigma(track1, PIDParticle::kPion), centrality, track1.pt()); + + hPID.fill(HIST("Before/hTPCnsigKa_Pos_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("Before/hTPCnsigPi_Pos_mult_p"), track1.tpcNSigmaPi(), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_p"), tofSigma(track1, PIDParticle::kPion), centrality, track1.p()); } - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_pt"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - } + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), tofSigma(track1, PIDParticle::kKaon), track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_pt"), track1.tpcNSigmaPi(), tofSigma(track1, PIDParticle::kPion), track1.pt()); - if (cQAevents) { hOthers.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), centrality, track1.pt()); hOthers.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), centrality, track1.pt()); } // since we are using combinations full index policy, so repeated pairs are allowed, so we can check one with Kaon and other with pion - if ((!selectionConfig.isApplypTdepPID && !selectionConfig.isApplypTdepPIDwTOF) && !selectionPID(track1, 1)) // Track 1 is checked with Kaon - continue; - if ((!selectionConfig.isApplypTdepPID && !selectionConfig.isApplypTdepPIDwTOF) && !selectionPID(track2, 0)) // Track 2 is checked with Pion + if (!selectionPID(track1, PIDParticle::kKaon)) { // Track 1 is checked with Kaon continue; + } - if (selectionConfig.isApplypTdepPID && !selectionPIDpTdep(track1, 1)) // Track 1 is checked with Kaon - continue; - if (selectionConfig.isApplypTdepPID && !selectionPIDpTdep(track2, 0)) // Track 2 is checked with Pion + if (!selectionPID(track2, PIDParticle::kPion)) { // Track 2 is checked with Pion continue; + } - if (selectionConfig.isApplypTdepPIDwTOF && !selectionPIDpTdepTOF(track1, 1)) // Track 1 is checked with Kaon + if (isMisidentified(track1, PIDParticle::kKaon)) { continue; - if (selectionConfig.isApplypTdepPIDwTOF && !selectionPIDpTdepTOF(track2, 0)) // Track 2 is checked with Pion - continue; - - if (selectionConfig.isApplyMID && (selectionMID(track1, 0) || selectionMID(track2, 1))) + } + if (isMisidentified(track2, PIDParticle::kPion)) { continue; + } - if (selectionConfig.isApplypTdepMID && (selectionMIDpTdep(track1, 0) || selectionMIDpTdep(track2, 1))) + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kKaon) || isFakeTrack(track2, PIDParticle::kPion))) { continue; + } if (cQAplots) { hOthers.fill(HIST("hEta_after"), track1.eta()); - hOthers.fill(HIST("hCRFC_after"), track1.tpcCrossedRowsOverFindableCls()); hOthers.fill(HIST("hDcaxyPi"), track2.dcaXY()); hOthers.fill(HIST("hDcaxyKa"), track1.dcaXY()); hOthers.fill(HIST("hDcazPi"), track2.dcaZ()); @@ -933,22 +1299,43 @@ struct Kstar892LightIon { if (track1.sign() < 0) { hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + + hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); } else if (track1.sign() > 0) { hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + + hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); } if (track2.sign() < 0) { hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } else if (track2.sign() > 0) { hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } - hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_pt"), track2.tpcNSigmaPi(), track2.tofNSigmaPi(), track2.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), tofSigma(track1, PIDParticle::kKaon), track1.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_pt"), track2.tpcNSigmaPi(), tofSigma(track2, PIDParticle::kPion), track2.pt()); + + if (track1.hasTOF()) { + hOthers.fill(HIST("hTOFBetaKa"), track1.beta()); + } + if (track2.hasTOF()) { + hOthers.fill(HIST("hTOFBetaPi"), track2.beta()); + } + hOthers.fill(HIST("hTPCpDiffKa"), track1.p() - track1.tpcInnerParam(), track1.pt()); + hOthers.fill(HIST("hTPCpDiffPi"), track2.p() - track2.tpcInnerParam(), track2.pt()); } daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); @@ -961,9 +1348,10 @@ struct Kstar892LightIon { if (mother.M() >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow continue; } */ - - hOthers.fill(HIST("hKstar_rap_pt"), mother.Rapidity(), mother.Pt()); - hOthers.fill(HIST("hKstar_eta_pt"), mother.Eta(), mother.Pt()); + if (cQAplots) { + hOthers.fill(HIST("hKstar_rap_pt"), mother.Rapidity(), mother.Pt()); + hOthers.fill(HIST("hKstar_eta_pt"), mother.Eta(), mother.Pt()); + } isMix = false; fillInvMass(daughter1, daughter2, mother, centrality, isMix, track1, track2); @@ -986,16 +1374,14 @@ struct Kstar892LightIon { centrality = -1; - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); // default + centrality = collision.centFT0M(); // default includes kFT0M } // Fill the event counter @@ -1005,65 +1391,79 @@ struct Kstar892LightIon { } for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { - if (!selectionTrack(track1) || !selectionTrack(track2)) + if (!selectionTrack(track1) || !selectionTrack(track2)) { continue; + } const auto mctrack1 = track1.mcParticle(); const auto mctrack2 = track2.mcParticle(); - if (!track1.has_mcParticle() || !track2.has_mcParticle()) + if (!track1.has_mcParticle() || !track2.has_mcParticle()) { continue; // skip if no MC particle associated + } - if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) + if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) { continue; + } - if (track1.globalIndex() == track2.globalIndex()) + if (track1.globalIndex() == track2.globalIndex()) { continue; + } if (cQAplots) { - hOthers.fill(HIST("hCRFC_before"), track1.tpcCrossedRowsOverFindableCls()); hOthers.fill(HIST("dE_by_dx_TPC"), track1.p(), track1.tpcSignal()); if (track1.sign() < 0) { hPID.fill(HIST("Before/hTPCnsigKa_Neg_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); hPID.fill(HIST("Before/hTPCnsigPi_Neg_mult_pt"), track1.tpcNSigmaPi(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_pt"), track1.tofNSigmaPi(), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_pt"), tofSigma(track1, PIDParticle::kPion), centrality, track1.pt()); + + hPID.fill(HIST("Before/hTPCnsigKa_Neg_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("Before/hTPCnsigPi_Neg_mult_p"), track1.tpcNSigmaPi(), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_p"), tofSigma(track1, PIDParticle::kPion), centrality, track1.p()); } else { hPID.fill(HIST("Before/hTPCnsigKa_Pos_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); hPID.fill(HIST("Before/hTPCnsigPi_Pos_mult_pt"), track1.tpcNSigmaPi(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_pt"), track1.tofNSigmaPi(), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_pt"), tofSigma(track1, PIDParticle::kPion), centrality, track1.pt()); + + hPID.fill(HIST("Before/hTPCnsigKa_Pos_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("Before/hTPCnsigPi_Pos_mult_p"), track1.tpcNSigmaPi(), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); + hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_p"), tofSigma(track1, PIDParticle::kPion), centrality, track1.p()); } - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_pt"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - } + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), tofSigma(track1, PIDParticle::kKaon), track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_pt"), track1.tpcNSigmaPi(), tofSigma(track1, PIDParticle::kPion), track1.pt()); - if (cQAevents) { hOthers.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), centrality, track1.pt()); hOthers.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), centrality, track1.pt()); } // since we are using combinations full index policy, so repeated pairs are allowed, so we can check one with Kaon and other with pion - if ((!selectionConfig.isApplypTdepPID && !selectionConfig.isApplypTdepPIDwTOF) && (!selectionPID(track1, 1) || !selectionPID(track2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion + if (!selectionPID(track1, PIDParticle::kKaon)) { // Track 1 is checked with Kaon continue; + } - if (selectionConfig.isApplypTdepPID && (!selectionPIDpTdep(track1, 1) || !selectionPIDpTdep(track2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion + if (!selectionPID(track2, PIDParticle::kPion)) { // Track 2 is checked with Pion continue; + } - if (selectionConfig.isApplypTdepPIDwTOF && (!selectionPIDpTdepTOF(track1, 1) || !selectionPIDpTdepTOF(track2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion + if (isMisidentified(track1, PIDParticle::kKaon)) { continue; - - if (selectionConfig.isApplyMID && (selectionMID(track1, 0) || selectionMID(track2, 1))) + } + if (isMisidentified(track2, PIDParticle::kPion)) { continue; + } - if (selectionConfig.isApplypTdepMID && (selectionMIDpTdep(track1, 0) || selectionMIDpTdep(track2, 1))) + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kKaon) || isFakeTrack(track2, PIDParticle::kPion))) { continue; + } if (cQAplots) { hOthers.fill(HIST("hEta_after"), track1.eta()); - hOthers.fill(HIST("hCRFC_after"), track1.tpcCrossedRowsOverFindableCls()); hOthers.fill(HIST("hDcaxyPi"), track2.dcaXY()); hOthers.fill(HIST("hDcaxyKa"), track1.dcaXY()); hOthers.fill(HIST("hDcazPi"), track2.dcaZ()); @@ -1071,22 +1471,43 @@ struct Kstar892LightIon { if (track1.sign() < 0) { hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + + hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); } else if (track1.sign() > 0) { hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); + + hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); } if (track2.sign() < 0) { hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } else if (track2.sign() > 0) { hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } - hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_pt"), track2.tpcNSigmaPi(), track2.tofNSigmaPi(), track2.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), tofSigma(track1, PIDParticle::kKaon), track1.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_pt"), track2.tpcNSigmaPi(), tofSigma(track2, PIDParticle::kPion), track2.pt()); + + if (track1.hasTOF()) { + hOthers.fill(HIST("hTOFBetaKa"), track1.beta()); + } + if (track2.hasTOF()) { + hOthers.fill(HIST("hTOFBetaPi"), track2.beta()); + } + hOthers.fill(HIST("hTPCpDiffKa"), track1.p() - track1.tpcInnerParam(), track1.pt()); + hOthers.fill(HIST("hTPCpDiffPi"), track2.p() - track2.tpcInnerParam(), track2.pt()); } daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); @@ -1098,10 +1519,10 @@ struct Kstar892LightIon { if (mother.M() >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow continue; } */ - - hOthers.fill(HIST("hKstar_rap_pt"), mother.Rapidity(), mother.Pt()); - hOthers.fill(HIST("hKstar_eta_pt"), mother.Eta(), mother.Pt()); - + if (cQAplots) { + hOthers.fill(HIST("hKstar_rap_pt"), mother.Rapidity(), mother.Pt()); + hOthers.fill(HIST("hKstar_eta_pt"), mother.Eta(), mother.Pt()); + } isMix = false; fillInvMass(daughter1, daughter2, mother, centrality, isMix, track1, track2); } @@ -1118,10 +1539,10 @@ struct Kstar892LightIon { using BinningTypeFT0C = ColumnBinningPolicy; using BinningTypeFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisCentrality}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisCentrality}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisCentrality}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisCentrality}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisCentrality}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisCentrality}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisCentrality}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisCentrality}}; SameKindPair pair1{binningOnFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; SameKindPair pair2{binningOnFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; @@ -1131,7 +1552,7 @@ struct Kstar892LightIon { void processME(EventCandidatesMix const&, TrackCandidates const&) { // Map estimator to pair and centrality accessor - auto runMixing = [&](auto& pair, auto centralityGetter) { + auto runMixing = [&](const auto& pair, auto centralityGetter) { for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (!selectionEvent(c1, false) || !selectionEvent(c2, false)) { // don't fill event cut histogram @@ -1141,27 +1562,26 @@ struct Kstar892LightIon { centrality = centralityGetter(c1); for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!selectionTrack(t1) || !selectionTrack(t2)) + if (!selectionTrack(t1) || !selectionTrack(t2)) { continue; + } if (!selectionPair(t1, t2)) { continue; } - if ((!selectionConfig.isApplypTdepPID && !selectionConfig.isApplypTdepPIDwTOF) && (!selectionPID(t1, 1) || !selectionPID(t2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion - continue; - - if (selectionConfig.isApplypTdepPID && (!selectionPIDpTdep(t1, 1) || !selectionPIDpTdep(t2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion - continue; - - if (selectionConfig.isApplypTdepPIDwTOF && (!selectionPIDpTdepTOF(t1, 1) || !selectionPIDpTdepTOF(t2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion + // t1 checked as kaon and t2 checked as pion + if (!selectionPID(t1, PIDParticle::kKaon) || !selectionPID(t2, PIDParticle::kPion)) { continue; + } - if (selectionConfig.isApplyMID && (selectionMID(t1, 0) || selectionMID(t2, 1))) + if (isMisidentified(t1, PIDParticle::kKaon) || isMisidentified(t2, PIDParticle::kPion)) { continue; + } - if (selectionConfig.isApplypTdepMID && (selectionMIDpTdep(t1, 0) || selectionMIDpTdep(t2, 1))) + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(t1, PIDParticle::kKaon) || isFakeTrack(t2, PIDParticle::kPion))) { continue; + } daughter1 = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), massPi); @@ -1192,10 +1612,10 @@ struct Kstar892LightIon { using BinningTypeMCFT0C = ColumnBinningPolicy; using BinningTypeMCFV0A = ColumnBinningPolicy; - BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisCentrality}, true}; - BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisCentrality}, true}; - BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisCentrality}, true}; - BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisCentrality}, true}; + BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisCentrality}}; + BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisCentrality}}; + BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisCentrality}}; + BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisCentrality}}; SameKindPair pairmc1{binningOnMCFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; SameKindPair pairmc2{binningOnMCFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; @@ -1204,7 +1624,7 @@ struct Kstar892LightIon { void processMEMC(EventCandidatesMC const&, TrackCandidatesMC const&, aod::McParticles const&, aod::McCollisions const&) { - auto runMixing = [&](auto& pair, auto centralityGetter) { + auto runMixing = [&](const auto& pair, auto centralityGetter) { for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (!selectionEvent(c1, false) || !selectionEvent(c2, false)) { // don't fill event cut histogram @@ -1218,27 +1638,26 @@ struct Kstar892LightIon { centrality = centralityGetter(c1); for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!selectionTrack(t1) || !selectionTrack(t2)) + if (!selectionTrack(t1) || !selectionTrack(t2)) { continue; + } if (!selectionPair(t1, t2)) { continue; } - if ((!selectionConfig.isApplypTdepPID && !selectionConfig.isApplypTdepPIDwTOF) && (!selectionPID(t1, 1) || !selectionPID(t2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion - continue; - - if (selectionConfig.isApplypTdepPID && (!selectionPIDpTdep(t1, 1) || !selectionPIDpTdep(t2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion - continue; - - if (selectionConfig.isApplypTdepPIDwTOF && (!selectionPIDpTdepTOF(t1, 1) || !selectionPIDpTdepTOF(t2, 0))) // Track 1 is checked with Kaon, track 2 is checked with Pion + // t1 checked as kaon and t2 checked as pion + if (!selectionPID(t1, PIDParticle::kKaon) || !selectionPID(t2, PIDParticle::kPion)) { continue; + } - if (selectionConfig.isApplyMID && (selectionMID(t1, 0) || selectionMID(t2, 1))) + if (isMisidentified(t1, PIDParticle::kKaon) || isMisidentified(t2, PIDParticle::kPion)) { continue; + } - if (selectionConfig.isApplypTdepMID && (selectionMIDpTdep(t1, 0) || selectionMIDpTdep(t2, 1))) + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(t1, PIDParticle::kKaon) || isFakeTrack(t2, PIDParticle::kPion))) { continue; + } if (!t1.has_mcParticle() || !t2.has_mcParticle()) { continue; // skip if no MC particle associated @@ -1281,7 +1700,7 @@ struct Kstar892LightIon { return; } - if (selectionConfig.isApplyMCGenTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + if (selectionConfig.isApplyMCGenTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { return; } @@ -1302,17 +1721,16 @@ struct Kstar892LightIon { } centrality = collision.centFT0M(); - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); // default + centrality = collision.centFT0M(); // default includes kFT0M } + hMC.fill(HIST("Gen/h1GenCent"), centrality); int occupancy = collision.trackOccupancyInTimeRange(); @@ -1372,22 +1790,28 @@ struct Kstar892LightIon { } int pdgDau = kCurrentDaughter.pdgCode(); - int charge = (pdgDau > 0) - (pdgDau < 0); + int charge = static_cast(pdgDau > 0) - static_cast(pdgDau < 0); - if (charge > 0) + if (charge > 0) { hasPos = true; - if (charge < 0) + } + if (charge < 0) { hasNeg = true; + } if (std::abs(pdgDau) == PDG_t::kKPlus) { passkaon = true; + daughter1 = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); } else if (std::abs(pdgDau) == PDG_t::kPiPlus) { + daughter2 = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massPi); passpion = true; } } if ((passkaon && passpion) && (hasPos && hasNeg)) { - hMC.fill(HIST("Gen/hk892GenpT"), mcParticle.pt(), centrality); + mother = daughter1 + daughter2; + hMC.fill(HIST("Gen/h3KstarPtCentMassDirect"), mcParticle.pt(), centrality, std::sqrt(mcParticle.e() * mcParticle.e() - mcParticle.p() * mcParticle.p())); + hMC.fill(HIST("Gen/h3KstarPtCentMassRec"), mother.Pt(), centrality, mother.M()); } } } @@ -1401,16 +1825,14 @@ struct Kstar892LightIon { centrality = collision.centFT0M(); - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); // default + centrality = collision.centFT0M(); // default includes kFT0M } hMC.fill(HIST("Rec/hAllRecCollisions"), centrality); @@ -1436,14 +1858,15 @@ struct Kstar892LightIon { continue; } - if (track1.index() <= track2.index()) + if (track1.index() <= track2.index()) { continue; + } if (!selectionPair(track1, track2)) { continue; } - if (cQAevents) { + if (cQAplots) { hOthers.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), centrality, track1.pt()); hOthers.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), centrality, track1.pt()); } @@ -1464,27 +1887,39 @@ struct Kstar892LightIon { if (cQAplots) { if (mctrack2.pdgCode() == PDG_t::kPiPlus) { // pion hPID.fill(HIST("Before/hTPCnsigPi_Pos_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("Before/hTPCnsigPi_Pos_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("Before/hTOFnsigPi_Pos_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } if (mctrack2.pdgCode() == PDG_t::kKPlus) { // kaon hPID.fill(HIST("Before/hTPCnsigKa_Pos_mult_pt"), track2.tpcNSigmaKa(), centrality, track2.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_pt"), track2.tofNSigmaKa(), centrality, track2.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_pt"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.pt()); + + hPID.fill(HIST("Before/hTPCnsigKa_Pos_mult_p"), track2.tpcNSigmaKa(), centrality, track2.p()); + hPID.fill(HIST("Before/hTOFnsigKa_Pos_mult_p"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.p()); } if (mctrack2.pdgCode() == PDG_t::kPiMinus) { // negative track pion hPID.fill(HIST("Before/hTPCnsigPi_Neg_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("Before/hTPCnsigPi_Neg_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("Before/hTOFnsigPi_Neg_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } if (mctrack2.pdgCode() == PDG_t::kKMinus) { // negative track kaon hPID.fill(HIST("Before/hTPCnsigKa_Neg_mult_pt"), track2.tpcNSigmaKa(), centrality, track2.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_pt"), track2.tofNSigmaKa(), centrality, track2.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_pt"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.pt()); + + hPID.fill(HIST("Before/hTPCnsigKa_Neg_mult_p"), track2.tpcNSigmaKa(), centrality, track2.p()); + hPID.fill(HIST("Before/hTOFnsigKa_Neg_mult_p"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.p()); } if (std::abs(mctrack1.pdgCode()) == PDG_t::kKPlus && std::abs(mctrack2.pdgCode()) == PDG_t::kPiPlus) { - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_pt"), track2.tpcNSigmaPi(), track2.tofNSigmaPi(), track2.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_pt"), track1.tpcNSigmaKa(), tofSigma(track1, PIDParticle::kKaon), track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_pt"), track2.tpcNSigmaPi(), tofSigma(track2, PIDParticle::kPion), track2.pt()); } } - if (!(track1PDG == PDG_t::kPiPlus && track2PDG == PDG_t::kKPlus) && !(track1PDG == PDG_t::kKPlus && track2PDG == PDG_t::kPiPlus)) { + if ((track1PDG != PDG_t::kPiPlus || track2PDG != PDG_t::kKPlus) && (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kPiPlus)) { continue; } @@ -1511,61 +1946,95 @@ struct Kstar892LightIon { } if (track1PDG == PDG_t::kPiPlus) { - if ((!selectionConfig.isApplypTdepPID && !selectionConfig.isApplypTdepPIDwTOF) && !(selectionPID(track1, 0) && selectionPID(track2, 1))) { // pion and kaon - continue; - } else if (selectionConfig.isApplypTdepPID && !(selectionPIDpTdep(track1, 0) && selectionPIDpTdep(track2, 1))) { // pion and kaon - continue; - } else if (selectionConfig.isApplypTdepPIDwTOF && !(selectionPIDpTdepTOF(track1, 0) && selectionPIDpTdepTOF(track2, 1))) { + if (!selectionPID(track1, PIDParticle::kPion) || !selectionPID(track2, PIDParticle::kKaon)) { // Treat track1 as the pion candidate and track2 as the kaon candidate continue; } - if (selectionConfig.isApplyMID && (selectionMID(track1, 1) || selectionMID(track2, 0))) + if (isMisidentified(track1, PIDParticle::kPion) || isMisidentified(track2, PIDParticle::kKaon)) { continue; + } - if (selectionConfig.isApplypTdepMID && (selectionMIDpTdep(track1, 1) || selectionMIDpTdep(track2, 0))) + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kPion) || isFakeTrack(track2, PIDParticle::kKaon))) { continue; + } if (cQAplots) { if (track1.sign() < 0 && track2.sign() > 0) { hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_pt"), track1.tpcNSigmaPi(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), track1.tofNSigmaPi(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), tofSigma(track1, PIDParticle::kPion), centrality, track1.pt()); hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_pt"), track2.tpcNSigmaKa(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), track2.tofNSigmaKa(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_p"), track1.tpcNSigmaPi(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_p"), tofSigma(track1, PIDParticle::kPion), centrality, track1.p()); + hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_p"), track2.tpcNSigmaKa(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_p"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.p()); } else { hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_pt"), track1.tpcNSigmaPi(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), track1.tofNSigmaPi(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), tofSigma(track1, PIDParticle::kPion), centrality, track1.pt()); hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_pt"), track2.tpcNSigmaKa(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), track2.tofNSigmaKa(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_p"), track1.tpcNSigmaPi(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_p"), tofSigma(track1, PIDParticle::kPion), centrality, track1.p()); + hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_p"), track2.tpcNSigmaKa(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_p"), tofSigma(track2, PIDParticle::kKaon), centrality, track2.p()); + } + + if (track1.hasTOF()) { + hOthers.fill(HIST("hTOFBetaPi"), track1.beta()); + } + if (track2.hasTOF()) { + hOthers.fill(HIST("hTOFBetaKa"), track2.beta()); } + hOthers.fill(HIST("hTPCpDiffPi"), track1.p() - track1.tpcInnerParam(), track1.pt()); + hOthers.fill(HIST("hTPCpDiffKa"), track2.p() - track2.tpcInnerParam(), track2.pt()); } } else if (track1PDG == PDG_t::kKPlus) { - if ((!selectionConfig.isApplypTdepPID && !selectionConfig.isApplypTdepPIDwTOF) && !(selectionPID(track1, 1) && selectionPID(track2, 0))) { // kaon and pion - continue; - } else if (selectionConfig.isApplypTdepPID && !(selectionPIDpTdep(track1, 1) && selectionPIDpTdep(track2, 0))) { // kaon and pion - continue; - } else if (selectionConfig.isApplypTdepPIDwTOF && !(selectionPIDpTdepTOF(track1, 1) && selectionPIDpTdepTOF(track2, 0))) { + if (!selectionPID(track1, PIDParticle::kKaon) || !selectionPID(track2, PIDParticle::kPion)) { // Treat track1 as the kaon candidate and track2 as the pion candidate continue; } - if (selectionConfig.isApplyMID && (selectionMID(track1, 0) || selectionMID(track2, 1))) + if (isMisidentified(track1, PIDParticle::kKaon) || isMisidentified(track2, PIDParticle::kPion)) { continue; + } - if (selectionConfig.isApplypTdepMID && (selectionMIDpTdep(track1, 0) || selectionMIDpTdep(track2, 1))) + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kKaon) || isFakeTrack(track2, PIDParticle::kPion))) { continue; + } if (cQAplots) { if (track1.sign() < 0 && track2.sign() > 0) { hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigKa_Neg_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigKa_Neg_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); + hPID.fill(HIST("After/hTPCnsigPi_Pos_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigPi_Pos_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } else { hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_pt"), track1.tpcNSigmaKa(), centrality, track1.pt()); - hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), track1.tofNSigmaKa(), centrality, track1.pt()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_pt"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.pt()); hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_pt"), track2.tpcNSigmaPi(), centrality, track2.pt()); - hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), track2.tofNSigmaPi(), centrality, track2.pt()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_pt"), tofSigma(track2, PIDParticle::kPion), centrality, track2.pt()); + + hPID.fill(HIST("After/hTPCnsigKa_Pos_mult_p"), track1.tpcNSigmaKa(), centrality, track1.p()); + hPID.fill(HIST("After/hTOFnsigKa_Pos_mult_p"), tofSigma(track1, PIDParticle::kKaon), centrality, track1.p()); + hPID.fill(HIST("After/hTPCnsigPi_Neg_mult_p"), track2.tpcNSigmaPi(), centrality, track2.p()); + hPID.fill(HIST("After/hTOFnsigPi_Neg_mult_p"), tofSigma(track2, PIDParticle::kPion), centrality, track2.p()); } + + if (track1.hasTOF()) { + hOthers.fill(HIST("hTOFBetaKa"), track1.beta()); + } + if (track2.hasTOF()) { + hOthers.fill(HIST("hTOFBetaPi"), track2.beta()); + } + hOthers.fill(HIST("hTPCpDiffKa"), track1.p() - track1.tpcInnerParam(), track1.pt()); + hOthers.fill(HIST("hTPCpDiffPi"), track2.p() - track2.tpcInnerParam(), track2.pt()); } } @@ -1586,21 +2055,34 @@ struct Kstar892LightIon { if (track1PDG == PDG_t::kPiPlus) { daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massPi); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); + + genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massPi); + genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massKa); + } else if (track1PDG == PDG_t::kKPlus) { daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - } - mother = daughter1 + daughter2; // Kstar meson + genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massKa); + genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massPi); + } - hMC.fill(HIST("Rec/h2KstarRecpt2"), mothertrack1.pt(), centrality, std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())); + mother = daughter1 + daughter2; // Rec Kstar meson + genMother = genDaughter1 + genDaughter2; // Gen Kstar from MC daughters if (mother.Rapidity() < selectionConfig.motherRapidityMin || mother.Rapidity() > selectionConfig.motherRapidityMax) { continue; } - hMC.fill(HIST("Rec/h1KstarRecMass"), mother.M()); - hMC.fill(HIST("Rec/h2KstarRecpt1"), mother.Pt(), centrality, mother.M()); + recMass = mother.M(); + recPt = mother.Pt(); + + genMass = genMother.M(); + genPt = mothertrack1.pt(); + + hMC.fill(HIST("Rec/hMassShift"), genPt, recPt, recMass - genMass); + hMC.fill(HIST("Rec/h3KstarPtCentMassgen"), genPt, centrality, genMass); + hMC.fill(HIST("Rec/h3KstarPtCentMassrec"), recPt, centrality, recMass); } } } @@ -1613,7 +2095,7 @@ struct Kstar892LightIon { return; } - if (selectionConfig.isApplyMCGenTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + if (selectionConfig.isApplyMCGenTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { return; } @@ -1628,21 +2110,21 @@ struct Kstar892LightIon { centrality = -1.f; for (const auto& RecCollision : recCollisions) { - if (!RecCollision.has_mcCollision()) + if (!RecCollision.has_mcCollision()) { continue; - if (!selectionEvent(RecCollision, false)) // don't fill event cut histogram + } + if (!selectionEvent(RecCollision, false)) { // don't fill event cut histogram continue; + } - if (selectCentEstimator == kFT0M) { - centrality = RecCollision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = RecCollision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = RecCollision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = RecCollision.centFV0A(); } else { - centrality = RecCollision.centFT0M(); // default + centrality = RecCollision.centFT0M(); // default includes kFT0M } isSelectedEvent = true; @@ -1655,8 +2137,9 @@ struct Kstar892LightIon { // Generated MC for (const auto& mcPart : mcParticles) { - if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) + if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) { continue; + } // signal loss estimation hMC.fill(HIST("ImpactCorr/hKstarGenBeforeEvtSel"), mcPart.pt(), impactPar); @@ -1675,7 +2158,7 @@ struct Kstar892LightIon { return; } - if (selectionConfig.isApplyMCGenTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + if (selectionConfig.isApplyMCGenTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { return; } @@ -1691,19 +2174,18 @@ struct Kstar892LightIon { for (auto const& collision : recCollisions) { - if (!selectionEvent(collision, false)) + if (!selectionEvent(collision, false)) { continue; + } - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); // default + centrality = collision.centFT0M(); // default includes kFT0M } isSelectedEvent = true; @@ -1722,8 +2204,9 @@ struct Kstar892LightIon { // Signal loss histograms for (auto const& mcPart : mcParticles) { - if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) + if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) { continue; + } const float pt = mcPart.pt(); @@ -1742,7 +2225,7 @@ struct Kstar892LightIon { return; } - if (selectionConfig.isApplyMCGenTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + if (selectionConfig.isApplyMCGenTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { return; } @@ -1758,23 +2241,22 @@ struct Kstar892LightIon { hMC.fill(HIST("AllLoss/hMultEta05Gen"), mult05); hMC.fill(HIST("AllLoss/hMultEta08Gen"), mult08); bool isSelectedEvent = false; - float centrality = -1.f; + centrality = -1.f; for (auto const& collision : recCollisions) { - if (!selectionEvent(collision, false)) + if (!selectionEvent(collision, false)) { continue; + } - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); // default + centrality = collision.centFT0M(); // default includes kFT0M } isSelectedEvent = true; @@ -1796,8 +2278,9 @@ struct Kstar892LightIon { // Generated MC for (const auto& mcPart : mcParticles) { - if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) + if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) { continue; + } const float pt = mcPart.pt(); @@ -1821,16 +2304,14 @@ struct Kstar892LightIon { return; } - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); // default + centrality = collision.centFT0M(); // default includes kFT0M } if (!selectionEvent(collision, false)) { @@ -1839,31 +2320,37 @@ struct Kstar892LightIon { for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { - if (!selectionTrack(track1) || !selectionTrack(track2)) + if (!selectionTrack(track1) || !selectionTrack(track2)) { continue; + } - if (track1.index() >= track2.index()) + if (track1.index() >= track2.index()) { continue; + } - if (track1.sign() * track2.sign() >= 0) + if (track1.sign() * track2.sign() >= 0) { continue; + } - if (!track1.has_mcParticle() || !track2.has_mcParticle()) + if (!track1.has_mcParticle() || !track2.has_mcParticle()) { continue; + } const auto mc1 = track1.mcParticle(); const auto mc2 = track2.mcParticle(); - if (!mc1.isPhysicalPrimary() || !mc2.isPhysicalPrimary()) + if (!mc1.isPhysicalPrimary() || !mc2.isPhysicalPrimary()) { continue; + } int pdg1 = std::abs(mc1.pdgCode()); int pdg2 = std::abs(mc2.pdgCode()); bool ok1 = (pdg1 == PDG_t::kPiPlus || pdg1 == PDG_t::kKPlus); bool ok2 = (pdg2 == PDG_t::kPiPlus || pdg2 == PDG_t::kKPlus); - if (!ok1 || !ok2) + if (!ok1 || !ok2) { continue; + } // pi-pi misidentification if (pdg1 == PDG_t::kPiPlus && pdg2 == PDG_t::kPiPlus) { @@ -1872,7 +2359,15 @@ struct Kstar892LightIon { auto misIDMother = p1Fake + p2True; if (misIDMother.Rapidity() > selectionConfig.motherRapidityMin && misIDMother.Rapidity() < selectionConfig.motherRapidityMax) { - hMC.fill(HIST("RecMisID/hMassMisID"), misIDMother.Pt(), centrality, misIDMother.M()); + hMC.fill(HIST("RecMisID/hMassMisIDPiPi"), misIDMother.Pt(), centrality, misIDMother.M()); + } + + ROOT::Math::PxPyPzMVector p1True(track1.px(), track1.py(), track1.pz(), massPi); + ROOT::Math::PxPyPzMVector p2Fake(track2.px(), track2.py(), track2.pz(), massKa); + + misIDMother = p1True + p2Fake; + if (misIDMother.Rapidity() > selectionConfig.motherRapidityMin && misIDMother.Rapidity() < selectionConfig.motherRapidityMax) { + hMC.fill(HIST("RecMisID/hMassMisIDPiPi"), misIDMother.Pt(), centrality, misIDMother.M()); } } @@ -1883,7 +2378,15 @@ struct Kstar892LightIon { auto misIDMother = p1Fake + p2True; if (misIDMother.Rapidity() > selectionConfig.motherRapidityMin && misIDMother.Rapidity() < selectionConfig.motherRapidityMax) { - hMC.fill(HIST("RecMisID/hMassMisID"), misIDMother.Pt(), centrality, misIDMother.M()); + hMC.fill(HIST("RecMisID/hMassMisIDKK"), misIDMother.Pt(), centrality, misIDMother.M()); + } + + ROOT::Math::PxPyPzMVector p1True(track1.px(), track1.py(), track1.pz(), massKa); + ROOT::Math::PxPyPzMVector p2Fake(track2.px(), track2.py(), track2.pz(), massPi); + + misIDMother = p1True + p2Fake; + if (misIDMother.Rapidity() > selectionConfig.motherRapidityMin && misIDMother.Rapidity() < selectionConfig.motherRapidityMax) { + hMC.fill(HIST("RecMisID/hMassMisIDKK"), misIDMother.Pt(), centrality, misIDMother.M()); } } } @@ -1892,46 +2395,50 @@ struct Kstar892LightIon { void processRecReflection(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&, EventMCGenerated const&) { - if (!collision.has_mcCollision()) + if (!collision.has_mcCollision()) { return; + } - if (!selectionEvent(collision, false)) + if (!selectionEvent(collision, false)) { return; + } centrality = -1.f; - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { + if (selectCentEstimator == kFT0A) { centrality = collision.centFT0A(); } else if (selectCentEstimator == kFT0C) { centrality = collision.centFT0C(); } else if (selectCentEstimator == kFV0A) { centrality = collision.centFV0A(); } else { - centrality = collision.centFT0M(); + centrality = collision.centFT0M(); // default includes kFT0M } - for (const auto& [track1, track2] : - combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { - if (!selectionTrack(track1) || !selectionTrack(track2)) + if (!selectionTrack(track1) || !selectionTrack(track2)) { continue; + } - if (track1.index() >= track2.index()) + if (track1.index() >= track2.index()) { continue; + } - if (track1.sign() * track2.sign() >= 0) + if (track1.sign() * track2.sign() >= 0) { continue; + } - if (!track1.has_mcParticle() || !track2.has_mcParticle()) + if (!track1.has_mcParticle() || !track2.has_mcParticle()) { continue; + } const auto mc1 = track1.mcParticle(); const auto mc2 = track2.mcParticle(); - if (!mc1.isPhysicalPrimary() || !mc2.isPhysicalPrimary()) + if (!mc1.isPhysicalPrimary() || !mc2.isPhysicalPrimary()) { continue; + } bool sameMother = false; int motherPDG = 0; @@ -1944,12 +2451,14 @@ struct Kstar892LightIon { break; } } - if (sameMother) + if (sameMother) { break; + } } - if (!sameMother) + if (!sameMother) { continue; + } int pdg1 = std::abs(mc1.pdgCode()); int pdg2 = std::abs(mc2.pdgCode()); @@ -1964,16 +2473,18 @@ struct Kstar892LightIon { ROOT::Math::PxPyPzMVector p2Pi(track2.px(), track2.py(), track2.pz(), massPi); auto fake1 = p1K + p2Pi; - if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) + if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) { hMC.fill(HIST("Reflections/hRhoToKpi"), fake1.Pt(), centrality, fake1.M()); + } // track 2 -> K ROOT::Math::PxPyPzMVector p1Pi(track1.px(), track1.py(), track1.pz(), massPi); ROOT::Math::PxPyPzMVector p2K(track2.px(), track2.py(), track2.pz(), massKa); auto fake2 = p1Pi + p2K; - if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) + if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) { hMC.fill(HIST("Reflections/hRhoToKpi"), fake2.Pt(), centrality, fake2.M()); + } } // ===================================================== @@ -1986,16 +2497,66 @@ struct Kstar892LightIon { ROOT::Math::PxPyPzMVector p2Pi(track2.px(), track2.py(), track2.pz(), massPi); auto fake1 = p1K + p2Pi; - if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) + if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) { hMC.fill(HIST("Reflections/hOmegaToKpi"), fake1.Pt(), centrality, fake1.M()); + } // track 2 -> K ROOT::Math::PxPyPzMVector p1Pi(track1.px(), track1.py(), track1.pz(), massPi); ROOT::Math::PxPyPzMVector p2K(track2.px(), track2.py(), track2.pz(), massKa); auto fake2 = p1Pi + p2K; - if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) + if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) { hMC.fill(HIST("Reflections/hOmegaToKpi"), fake2.Pt(), centrality, fake2.M()); + } + } + + // ===================================================== + // Eta -> pi pi(pi0) -> K pi + // ===================================================== + if (motherPDG == o2::constants::physics::kEta && pdg1 == PDG_t::kPiPlus && pdg2 == PDG_t::kPiPlus) { + + // track 1 -> K + ROOT::Math::PxPyPzMVector p1K(track1.px(), track1.py(), track1.pz(), massKa); + ROOT::Math::PxPyPzMVector p2Pi(track2.px(), track2.py(), track2.pz(), massPi); + auto fake1 = p1K + p2Pi; + + if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) { + hMC.fill(HIST("Reflections/hEtaToKpi"), fake1.Pt(), centrality, fake1.M()); + } + + // track 2 -> K + ROOT::Math::PxPyPzMVector p1Pi(track1.px(), track1.py(), track1.pz(), massPi); + ROOT::Math::PxPyPzMVector p2K(track2.px(), track2.py(), track2.pz(), massKa); + auto fake2 = p1Pi + p2K; + + if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) { + hMC.fill(HIST("Reflections/hEtaToKpi"), fake2.Pt(), centrality, fake2.M()); + } + } + + // ===================================================== + // Eta' (958) -> pi pi(eta) -> K pi + // ===================================================== + if (motherPDG == o2::constants::physics::kEtaPrime && pdg1 == PDG_t::kPiPlus && pdg2 == PDG_t::kPiPlus) { + + // track 1 -> K + ROOT::Math::PxPyPzMVector p1K(track1.px(), track1.py(), track1.pz(), massKa); + ROOT::Math::PxPyPzMVector p2Pi(track2.px(), track2.py(), track2.pz(), massPi); + auto fake1 = p1K + p2Pi; + + if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) { + hMC.fill(HIST("Reflections/hEtaPrimeToKpi"), fake1.Pt(), centrality, fake1.M()); + } + + // track 2 -> K + ROOT::Math::PxPyPzMVector p1Pi(track1.px(), track1.py(), track1.pz(), massPi); + ROOT::Math::PxPyPzMVector p2K(track2.px(), track2.py(), track2.pz(), massKa); + auto fake2 = p1Pi + p2K; + + if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) { + hMC.fill(HIST("Reflections/hEtaPrimeToKpi"), fake2.Pt(), centrality, fake2.M()); + } } // ===================================================== @@ -2008,16 +2569,18 @@ struct Kstar892LightIon { ROOT::Math::PxPyPzMVector p2K(track2.px(), track2.py(), track2.pz(), massKa); auto fake1 = p1Pi + p2K; - if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) + if (fake1.Rapidity() > selectionConfig.motherRapidityMin && fake1.Rapidity() < selectionConfig.motherRapidityMax) { hMC.fill(HIST("Reflections/hPhiToKpi"), fake1.Pt(), centrality, fake1.M()); + } // track 2 -> pi ROOT::Math::PxPyPzMVector p1K(track1.px(), track1.py(), track1.pz(), massKa); ROOT::Math::PxPyPzMVector p2Pi(track2.px(), track2.py(), track2.pz(), massPi); auto fake2 = p1K + p2Pi; - if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) + if (fake2.Rapidity() > selectionConfig.motherRapidityMin && fake2.Rapidity() < selectionConfig.motherRapidityMax) { hMC.fill(HIST("Reflections/hPhiToKpi"), fake2.Pt(), centrality, fake2.M()); + } } // ===================================================== @@ -2031,236 +2594,816 @@ struct Kstar892LightIon { auto fake = p1Swap + p2Swap; - if (fake.Rapidity() > selectionConfig.motherRapidityMin && fake.Rapidity() < selectionConfig.motherRapidityMax) + if (fake.Rapidity() > selectionConfig.motherRapidityMin && fake.Rapidity() < selectionConfig.motherRapidityMax) { hMC.fill(HIST("Reflections/hKstarSelf"), fake.Pt(), centrality, fake.M()); + } } - } - } - PROCESS_SWITCH(Kstar892LightIon, processRecReflection, "Process reconstructed reflections", false); - Service pdg; + // ========================================= + // Higher resonance feed-down + // ========================================= - void processMCCheck(aod::McCollisions::iterator const& mccollision, soa::SmallGroups const& collisions, aod::McParticles const& mcParticles, TrackCandidatesMC const&) - { + if ((pdg1 == PDG_t::kKPlus && pdg2 == PDG_t::kPiPlus) || (pdg1 == PDG_t::kPiPlus && pdg2 == PDG_t::kKPlus)) { - //--------------------------- - // Only INEL > 0 generated collisions - // By counting number of primary charged particles in |eta| < 1 - //--------------------------- - int nChMC{0}; - int nChMCEta08{0}; - int nChFT0A{0}; - int nChFT0C{0}; - static constexpr float MinCharge{3.f}; - static constexpr float MinFT0A{3.5f}; - static constexpr float MaxFT0A{4.9f}; - static constexpr float MinFT0C{-3.3f}; - static constexpr float MaxFT0C{-2.1f}; - static constexpr float One{1.0f}; - static constexpr int ZeroInt{0}; - - for (const auto& particle : mcParticles) { - - auto charge{0.}; - // Get the MC particle - const auto* pdgParticle = pdg->GetParticle(particle.pdgCode()); - if (pdgParticle != nullptr) { - charge = pdgParticle->Charge(); - } else { - continue; - } + ROOT::Math::PxPyPzMVector p1(track1.px(), track1.py(), track1.pz(), pdg1 == PDG_t::kKPlus ? massKa : massPi); - // Is it a charged particle? - if (std::abs(charge) < MinCharge) - continue; + ROOT::Math::PxPyPzMVector p2(track2.px(), track2.py(), track2.pz(), pdg2 == PDG_t::kKPlus ? massKa : massPi); - // Is it a primary particle? - if (!particle.isPhysicalPrimary()) - continue; + auto pair = p1 + p2; - const float eta{particle.eta()}; + if (pair.Rapidity() < selectionConfig.motherRapidityMin || pair.Rapidity() > selectionConfig.motherRapidityMax) { + continue; + } - // TVX requirement - if (eta > MinFT0A && eta < MaxFT0A) { - nChFT0A++; - } + // ========================================== + // K1(1270) + // ========================================== + if (motherPDG == o2::constants::physics::kK1_1270Plus) { + hMC.fill(HIST("FeedDown/hK1_1270"), pair.Pt(), centrality, pair.M()); + } - if (eta > MinFT0C && eta < MaxFT0C) { - nChFT0C++; - } + // ========================================== + // K1(1400) + // ========================================== + if (motherPDG == kK11400Plus) { + hMC.fill(HIST("FeedDown/hK1_1400"), pair.Pt(), centrality, pair.M()); + } - if (std::abs(eta) < nchAcceptance) { - nChMCEta08++; - } + // ========================================== + // K*(1410) + // ========================================== + if (motherPDG == kKstar14100) { + hMC.fill(HIST("FeedDown/hKstar1410"), pair.Pt(), centrality, pair.M()); + } - // INEL > 0 - if (std::abs(eta) > One) - continue; + // ========================================== + // K*0(1430) + // ========================================== + if (motherPDG == kKstar14300) { + hMC.fill(HIST("FeedDown/hKstar0_1430_0"), pair.Pt(), centrality, pair.M()); + } - nChMC++; - } + // ========================================== + // K*±(1430) + // ========================================== + if (motherPDG == kKstar1430Plus) { + hMC.fill(HIST("FeedDown/hKstar0_1430_ch"), pair.Pt(), centrality, pair.M()); + } - //--------------------------- - // Only events with at least one charged particle in the FT0A and FT0C acceptances - //--------------------------- - if (selectionConfig.selTVXMC) { - if (!(nChFT0A > ZeroInt && nChFT0C > ZeroInt)) { - hMC.fill(HIST("MCCheck/NchMCcentVsTVX"), nChMC, 0.5); - return; + // ========================================== + // K*2(1430) + // ========================================== + if (motherPDG == kKstar214300) { + hMC.fill(HIST("FeedDown/hKstar2_1430"), pair.Pt(), centrality, pair.M()); + } + + // ========================================== + // K2(1770) + // ========================================== + if (motherPDG == kK217700) { + hMC.fill(HIST("FeedDown/hK2_1770"), pair.Pt(), centrality, pair.M()); + } + + // ========================================== + // K2(1820) + // ========================================== + if (motherPDG == kK218200) { + hMC.fill(HIST("FeedDown/hK2_1820"), pair.Pt(), centrality, pair.M()); + } + + // ========================================== + // K*(1680) + // ========================================== + if (motherPDG == kKstar16800) { + hMC.fill(HIST("FeedDown/hKstar1680"), pair.Pt(), centrality, pair.M()); + } } - hMC.fill(HIST("MCCheck/NchMCcentVsTVX"), nChMC, 1.5); } + } + PROCESS_SWITCH(Kstar892LightIon, processRecReflection, "Process reconstructed reflections", false); - //--------------------------- - // Only MC events with |Vtx Z| < 10 cm - //--------------------------- - if (selectionConfig.isZvtxPosSelMC && (std::fabs(mccollision.posZ()) > selectionConfig.cfgVrtxZCut)) { + void processRecCorrelatedBackground(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&, EventMCGenerated const&) + { + if (!collision.has_mcCollision()) { return; } - //--------------------------- - // Only INEL > 0 generated events - //--------------------------- - if (selectionConfig.selINELgt0MC) { - if (!(nChMC > ZeroInt)) { - return; - } + if (!selectionEvent(collision, false)) { + return; } - const auto& nRecColls{collisions.size()}; - hMC.fill(HIST("MCCheck/NumberOfRecoCollisions"), nRecColls); + centrality = -1.f; - //--------------------------- - // Only Generated evets with at least one reconstrued collision - //--------------------------- - if (nRecColls > ZeroInt) { + if (selectCentEstimator == kFT0A) { + centrality = collision.centFT0A(); + } else if (selectCentEstimator == kFT0C) { + centrality = collision.centFT0C(); + } else if (selectCentEstimator == kFV0A) { + centrality = collision.centFV0A(); + } else { + centrality = collision.centFT0M(); // default includes kFT0M + } - // Finds the collisions with the largest number of contributors - // in case nRecColls is larger than One - int biggestNContribs{-1}; - int bestCollisionIndex{-1}; - centrality = -1.f; - for (const auto& collision : collisions) { + for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { - centrality = collision.centFT0A(); - } else if (selectCentEstimator == kFT0C) { - centrality = collision.centFT0C(); - } else if (selectCentEstimator == kFV0A) { - centrality = collision.centFV0A(); - } else { - centrality = collision.centFT0M(); // default - } + if (!selectionTrack(track1)) { + continue; + } - if (selectionConfig.selHasFT0MC && !collision.has_foundFT0()) { - continue; - } + if (!selectionTrack(track2)) { + continue; + } - if (biggestNContribs < collision.numContrib()) { - biggestNContribs = collision.numContrib(); - bestCollisionIndex = collision.globalIndex(); - } + if (track1.index() >= track2.index()) { + continue; + } - // Needed to calculate denominator of the Event Splitting correction - if (selectionEvent(collision, false)) { - hMC.fill(HIST("MCCheck/Centrality_AllRecoEvt"), centrality); - } + if (track1.sign() * track2.sign() >= 0) { + continue; } - //--------------------------- - // Loop over the reconstructed collisions - // Only that one with the largest number of contributors is considered - //--------------------------- - centrality = -1.f; - for (const auto& collision : collisions) { + if (!track1.has_mcParticle()) { + continue; + } - if (selectCentEstimator == kFT0M) { - centrality = collision.centFT0M(); - } else if (selectCentEstimator == kFT0A) { - centrality = collision.centFT0A(); - } else if (selectCentEstimator == kFT0C) { - centrality = collision.centFT0C(); - } else if (selectCentEstimator == kFV0A) { - centrality = collision.centFV0A(); - } else { - centrality = collision.centFT0M(); // default + if (!track2.has_mcParticle()) { + continue; + } + + auto mc1 = track1.mcParticle(); + auto mc2 = track2.mcParticle(); + + // if (!mc1.isPhysicalPrimary() || !mc2.isPhysicalPrimary()) + // continue; + + int pdg1 = std::abs(mc1.pdgCode()); + int pdg2 = std::abs(mc2.pdgCode()); + + if ((pdg1 != PDG_t::kKPlus || pdg2 != PDG_t::kPiPlus) && (pdg1 != PDG_t::kPiPlus || pdg2 != PDG_t::kKPlus)) { + continue; + } + + ROOT::Math::PxPyPzMVector p1(track1.px(), track1.py(), track1.pz(), pdg1 == PDG_t::kKPlus ? massKa : massPi); + ROOT::Math::PxPyPzMVector p2(track2.px(), track2.py(), track2.pz(), pdg2 == PDG_t::kKPlus ? massKa : massPi); + auto pair = p1 + p2; + + if (pair.Rapidity() < selectionConfig.motherRapidityMin || pair.Rapidity() > selectionConfig.motherRapidityMax) { + continue; + } + + bool trueKstar = false; + + for (const auto& m1 : mc1.mothers_as()) { + for (const auto& m2 : mc2.mothers_as()) { + + if (m1.globalIndex() == m2.globalIndex() && std::abs(m1.pdgCode()) == o2::constants::physics::kK0Star892) { + trueKstar = true; + break; + } + } + if (trueKstar) { + break; + } + } + if (trueKstar) { + continue; + } + + auto ancestors1 = getAncestors(mc1); + auto ancestors2 = getAncestors(mc2); + + bool fromK11270 = false; + bool fromK11400 = false; + bool fromKstar1680 = false; + bool fromK21770 = false; + bool fromK21820 = false; + bool fromKstar1410 = false; + bool fromKstar01430 = false; + + for (auto const& a1 : ancestors1) { + + for (auto const& a2 : ancestors2) { + + if (a1.first != a2.first) { + continue; + } + + int ancestorPdg = a1.second; + + if (ancestorPdg == o2::constants::physics::kK1_1270Plus) { + fromK11270 = true; + } + + if (ancestorPdg == kK11400Plus) { + fromK11400 = true; + } + + if (ancestorPdg == kKStar1680Plus) { + fromKstar1680 = true; + } + + if (ancestorPdg == kK21770Plus) { + fromK21770 = true; + } + + if (ancestorPdg == kK21820Plus) { + fromK21820 = true; + } + + if (ancestorPdg == kKstar14100) { + fromKstar1410 = true; + } + + if (ancestorPdg == kKstar14300) { + fromKstar01430 = true; + } + } + } + + if (fromKstar1410) { + hMC.fill(HIST("CorrelatedBG/hKstar1410"), pair.Pt(), centrality, pair.M()); + } + + if (fromKstar01430) { + hMC.fill(HIST("CorrelatedBG/hKstar0_1430"), pair.Pt(), centrality, pair.M()); + } + + if (fromK11270) { + hMC.fill(HIST("CorrelatedBG/hK1_1270"), pair.Pt(), centrality, pair.M()); + } + + if (fromK11400) { + hMC.fill(HIST("CorrelatedBG/hK1_1400"), pair.Pt(), centrality, pair.M()); + } + + if (fromKstar1680) { + hMC.fill(HIST("CorrelatedBG/hKstar1680"), pair.Pt(), centrality, pair.M()); + } + + if (fromK21770) { + hMC.fill(HIST("CorrelatedBG/hK2_1770"), pair.Pt(), centrality, pair.M()); + } + + if (fromK21820) { + hMC.fill(HIST("CorrelatedBG/hK2_1820"), pair.Pt(), centrality, pair.M()); + } + } + } + + PROCESS_SWITCH(Kstar892LightIon, processRecCorrelatedBackground, "Process correlated background", false); + + void processTemplateMC(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&, EventMCGenerated const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!selectionEvent(collision, false)) { + return; + } + + centrality = -1.f; + if (selectCentEstimator == kFT0A) { + centrality = collision.centFT0A(); + } else if (selectCentEstimator == kFT0C) { + centrality = collision.centFT0C(); + } else if (selectCentEstimator == kFV0A) { + centrality = collision.centFV0A(); + } else { + centrality = collision.centFT0M(); // default includes kFT0M + } + + auto classifyOrigin = [](const aod::McParticle& part, int64_t& motherIdx) -> int { + motherIdx = -1; + if (!part.has_mothers()) { + return 0; + } + for (const auto& mom : part.mothers_as()) { + if (!mom.producedByGenerator()) { + continue; + } + motherIdx = mom.globalIndex(); + if (std::abs(mom.pdgCode()) == o2::constants::physics::kK0Star892) { + return 1; + } + return 2; + } + return 0; + }; + + for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + + if (track1.globalIndex() == track2.globalIndex()) { + continue; + } + + if (!selectionPair(track1, track2)) { + continue; + } + + if (!selectionPID(track1, PIDParticle::kKaon)) { + continue; + } + if (!selectionPID(track2, PIDParticle::kPion)) { + continue; + } + + if (isMisidentified(track1, PIDParticle::kKaon)) { + continue; + } + if (isMisidentified(track2, PIDParticle::kPion)) { + continue; + } + + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kKaon) || isFakeTrack(track2, PIDParticle::kPion))) { + continue; + } + + if (!track1.has_mcParticle() || !track2.has_mcParticle()) { + continue; + } + + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); + daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + mother = daughter1 + daughter2; + + if (mother.Rapidity() < selectionConfig.motherRapidityMin || mother.Rapidity() > selectionConfig.motherRapidityMax) { + continue; + } + + recMass = mother.M(); + recPt = mother.Pt(); + + const auto mcKa = track1.mcParticle(); + const auto mcPi = track2.mcParticle(); + + if (selectionConfig.isPrimaryTrack && (!mcKa.isPhysicalPrimary() || !mcPi.isPhysicalPrimary())) { + continue; + } + + int64_t kaMotherIdx = -1; + int64_t piMotherIdx = -1; + const int kaOrigin = classifyOrigin(mcKa, kaMotherIdx); + const int piOrigin = classifyOrigin(mcPi, piMotherIdx); + + const bool kaIsKstar = (kaOrigin == 1); + const bool piIsKstar = (piOrigin == 1); + const bool kaHasParent = (kaOrigin != 0); + const bool piHasParent = (piOrigin != 0); + const bool sameMother = (kaMotherIdx >= 0) && (piMotherIdx >= 0) && (kaMotherIdx == piMotherIdx); + + if (kaIsKstar && piIsKstar && sameMother) { + const bool correctAssignment = (std::abs(mcKa.pdgCode()) == PDG_t::kKPlus) && (std::abs(mcPi.pdgCode()) == PDG_t::kPiPlus); + if (correctAssignment) { + hMC.fill(HIST("Template/hSignal"), recPt, centrality, recMass); + } else { + hMC.fill(HIST("Template/hKstarReflection"), recPt, centrality, recMass); + } + + } else if (sameMother && kaHasParent && piHasParent) { + hMC.fill(HIST("Template/hSameMotherOther"), recPt, centrality, recMass); + + } else if (!sameMother && kaHasParent && piHasParent) { + hMC.fill(HIST("Template/hDifferentMother"), recPt, centrality, recMass); + + } else if (kaHasParent != piHasParent) { + hMC.fill(HIST("Template/hPrimaryResonance"), recPt, centrality, recMass); + + } else { + hMC.fill(HIST("Template/hPrimaryPrimary"), recPt, centrality, recMass); + } + } + } + PROCESS_SWITCH(Kstar892LightIon, processTemplateMC, "Process MC Template Background", false); + + void processRecKinematics(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&, EventMCGenerated const&) + { + if (!collision.has_mcCollision()) { + return; + } + + centrality = collision.centFT0M(); + + if (selectCentEstimator == kFT0A) { + centrality = collision.centFT0A(); + } else if (selectCentEstimator == kFT0C) { + centrality = collision.centFT0C(); + } else if (selectCentEstimator == kFV0A) { + centrality = collision.centFV0A(); + } else { + centrality = collision.centFT0M(); // default includes kFT0M + } + + if (!selectionEvent(collision, true)) { // fill MC event cut histogram + return; + } + + hMC.fill(HIST("Kinematics/h1RecCent"), centrality); + + // auto oldindex = -999; + + for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + + if (!track1.has_mcParticle() || !track2.has_mcParticle()) { + continue; + } + + if (track1.index() <= track2.index()) { + continue; + } + + if (!selectionPair(track1, track2)) { + continue; + } + + if (track1.sign() * track2.sign() >= 0) { + continue; + } + + const auto mctrack1 = track1.mcParticle(); + const auto mctrack2 = track2.mcParticle(); + if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) { + continue; + } + + int track1PDG = std::abs(mctrack1.pdgCode()); + int track2PDG = std::abs(mctrack2.pdgCode()); + + if ((track1PDG != PDG_t::kPiPlus || track2PDG != PDG_t::kKPlus) && (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kPiPlus)) { + continue; + } + + ROOT::Math::PxPyPzMVector kVec; + ROOT::Math::PxPyPzMVector piVec; + + double phiK = 0.0, phiPi = 0.0; + double etaK = 0.0, etaPi = 0.0; + double ptK = 0.0, ptPi = 0.0; + + if (track1PDG == PDG_t::kKPlus) { + + if (!selectionPID(track1, PIDParticle::kKaon) || !selectionPID(track2, PIDParticle::kPion)) { // Treat track1 as the kaon candidate and track2 as the pion candidate + continue; } - //--------------------------- - // Reject collisions if has_foundFT0() returns false - //--------------------------- - if (selectionConfig.selHasFT0MC && !collision.has_foundFT0()) { - hMC.fill(HIST("MCCheck/CentVsFoundFT0"), centrality, 0.5); + if (isMisidentified(track1, PIDParticle::kKaon) || isMisidentified(track2, PIDParticle::kPion)) { continue; } - hMC.fill(HIST("MCCheck/CentVsFoundFT0"), centrality, 1.5); - //--------------------------- - // Pick the collisions with the largest number of contributors - //--------------------------- - if (bestCollisionIndex != collision.globalIndex()) { + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kKaon) || isFakeTrack(track2, PIDParticle::kPion))) { continue; } - //--------------------------- - // Needed to construct the correlation between MC Nch v.s. centrality - //--------------------------- + kVec = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); + piVec = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + + phiK = track1.phi(); + etaK = track1.eta(); + ptK = track1.pt(); - hMC.fill(HIST("MCCheck/Centrality_WRecoEvt"), centrality); - hMC.fill(HIST("MCCheck/zPosMC"), mccollision.posZ()); + phiPi = track2.phi(); + etaPi = track2.eta(); + ptPi = track2.pt(); - //--------------------------- - // Event selection - // for reconstructed collisions - //--------------------------- - if (!selectionEvent(collision, false)) { + } else { + + if (!selectionPID(track1, PIDParticle::kPion) || !selectionPID(track2, PIDParticle::kKaon)) { // Treat track1 as the pion candidate and track2 as the kaon candidate + continue; + } + + if (isMisidentified(track1, PIDParticle::kPion) || isMisidentified(track2, PIDParticle::kKaon)) { continue; } - hMC.fill(HIST("MCCheck/Centrality_WRecoEvtWSelCri"), centrality); - hMC.fill(HIST("MCCheck/NchMCVsCent"), centrality, nChMCEta08); - hMC.fill(HIST("MCCheck/NchMC_WithRecoEvt"), nChMCEta08); // Numerator of event loss correction - hMC.fill(HIST("MCCheck/zPos"), collision.posZ()); - hMC.fill(HIST("MCCheck/Cent"), centrality); - - //--------------------------- - // All Generated events with at least one associated reconstructed collision - // The Generated events are not subjected to any selection criteria - // However, the associated reconstructed collisions pass the selection criteria - // This histograms are used for the denominator of the tracking efficiency - //--------------------------- - for (const auto& mcPart : mcParticles) { - if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kPion) || isFakeTrack(track2, PIDParticle::kKaon))) { + continue; + } + + kVec = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); + piVec = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massPi); + + phiK = track2.phi(); + etaK = track2.eta(); + ptK = track2.pt(); + + phiPi = track1.phi(); + etaPi = track1.eta(); + ptPi = track1.pt(); + } + + auto pair = kVec + piVec; + + double mass = pair.M(); + + double cosAngle = (kVec.Px() * piVec.Px() + kVec.Py() * piVec.Py() + kVec.Pz() * piVec.Pz()) / (kVec.P() * piVec.P()); + + cosAngle = std::clamp(cosAngle, -1.0, 1.0); + + double openAngle = std::acos(cosAngle); + + double dPhi = RecoDecay::constrainAngle(phiK - phiPi, -o2::constants::math::PI); + + double dEta = etaK - etaPi; + + double dR = std::sqrt(dPhi * dPhi + dEta * dEta); + + bool isTrueKstar = false; + bool hasCommonMother = false; + + int commonMotherPDG = initialValue; + + for (const auto& mothertrack1 : mctrack1.mothers_as()) { + for (const auto& mothertrack2 : mctrack2.mothers_as()) { + + if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { continue; + } - hMC.fill(HIST("MCCheck/PtKstarVsCentMC_WithRecoEvt"), mcPart.pt(), centrality); - hMC.fill(HIST("MCCheck/PtKstarVsNchMC_WithRecoEvt"), mcPart.pt(), nChMCEta08); // Numerator of signal loss - } // Loop over generated particles per generated collision - // hMC.fill(HIST("MCCheck/NchVsCent"), centrality, nCh); - } // Loop over Reco. Collisions: Only the collisions with the largest number of contributors - } // If condition: Only simulated evets with at least one reconstrued collision - - //--------------------------- - // All Generated events irrespective of whether there is an associated reconstructed collision - // Consequently, the centrality being a reconstructed quantity, might not always be available - // Therefore it is expressed as a function of the generated pT and the generated Nch in ∣eta∣ < 0.8 - // This is used for the denominator of the signal loss correction - //--------------------------- - for (const auto& mcPart : mcParticles) { - if ((mcPart.y() < selectionConfig.motherRapidityMin || mcPart.y() > selectionConfig.motherRapidityMax) || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) + if (mothertrack1.globalIndex() != mothertrack2.globalIndex()) { + continue; + } + + hasCommonMother = true; + commonMotherPDG = mothertrack1.pdgCode(); + + if (std::abs(commonMotherPDG) == o2::constants::physics::kK0Star892) { + isTrueKstar = true; + } + } + } + + if (pair.Rapidity() < selectionConfig.motherRapidityMin || pair.Rapidity() > selectionConfig.motherRapidityMax) { + continue; + } + + if (isTrueKstar) { + + hMC.fill(HIST("Kinematics/hPDGMotherKstar"), commonMotherPDG, mass); + + hMC.fill(HIST("Kinematics/hOpenAngleKstar"), openAngle, mass); + + hMC.fill(HIST("Kinematics/hDeltaPhiKstar"), dPhi, mass); + + hMC.fill(HIST("Kinematics/hDeltaEtaKstar"), dEta, mass); + + hMC.fill(HIST("Kinematics/hDeltaRKstar"), dR, mass); + + hMC.fill(HIST("Kinematics/hKaonPtKstar"), ptK, mass); + + hMC.fill(HIST("Kinematics/hPionPtKstar"), ptPi, mass); + + hMC.fill(HIST("Kinematics/hPtCentMassKstar"), pair.Pt(), centrality, mass); + + } else { + + hMC.fill(HIST("Kinematics/hOpenAngleOther"), openAngle, mass); + + hMC.fill(HIST("Kinematics/hDeltaPhiOther"), dPhi, mass); + + hMC.fill(HIST("Kinematics/hDeltaEtaOther"), dEta, mass); + + hMC.fill(HIST("Kinematics/hDeltaROther"), dR, mass); + + hMC.fill(HIST("Kinematics/hKaonPtOther"), ptK, mass); + + hMC.fill(HIST("Kinematics/hPionPtOther"), ptPi, mass); + + hMC.fill(HIST("Kinematics/hPtCentMassOther"), pair.Pt(), centrality, mass); + + if (hasCommonMother) { + hMC.fill(HIST("Kinematics/hOtherMotherPDG"), commonMotherPDG, mass); + } + } + } + } + PROCESS_SWITCH(Kstar892LightIon, processRecKinematics, "Process Reconstructed Kinematics", false); + + void processMisIdKinematics(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&, EventMCGenerated const&) + { + if (!collision.has_mcCollision()) { + return; + } + + if (!selectionEvent(collision, true)) { + return; + } + + centrality = -1; + if (selectCentEstimator == kFT0A) { + centrality = collision.centFT0A(); + } else if (selectCentEstimator == kFT0C) { + centrality = collision.centFT0C(); + } else if (selectCentEstimator == kFV0A) { + centrality = collision.centFV0A(); + } else { + centrality = collision.centFT0M(); + } + + hMC.fill(HIST("KinematicsMisId/hRecCent"), centrality); + + for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { + + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + if (track1.globalIndex() == track2.globalIndex()) { continue; + } + if (!selectionPair(track1, track2)) { + continue; + } - hMC.fill(HIST("MCCheck/PtKstarVsNchMC_AllGen"), mcPart.pt(), nChMCEta08); - } // Loop over Generated Particles + if (!selectionPID(track1, PIDParticle::kKaon)) { + continue; + } + if (!selectionPID(track2, PIDParticle::kPion)) { + continue; + } - //--------------------------- - // This is used for the denominator of the event loss correction - //--------------------------- - hMC.fill(HIST("MCCheck/NchMC_AllGen"), nChMCEta08); + if (isMisidentified(track1, PIDParticle::kKaon)) { + continue; + } + if (isMisidentified(track2, PIDParticle::kPion)) { + continue; + } + + if (selectionConfig.isApplyFakeTrack && (isFakeTrack(track1, PIDParticle::kKaon) || isFakeTrack(track2, PIDParticle::kPion))) { + continue; + } + + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); + daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + mother = daughter1 + daughter2; + + double ptK = daughter1.Pt(); + double ptPi = daughter2.Pt(); + double ptPair = mother.Pt(); + double mass = mother.M(); + + double cosAngle = (daughter1.Px() * daughter2.Px() + daughter1.Py() * daughter2.Py() + daughter1.Pz() * daughter2.Pz()) / (daughter1.P() * daughter2.P()); + cosAngle = std::clamp(cosAngle, -1.0, 1.0); + double openAngle = std::acos(cosAngle); + + double dPhi = RecoDecay::constrainAngle(track1.phi() - track2.phi(), -o2::constants::math::PI); + double dEta = track1.eta() - track2.eta(); + double dR = std::sqrt(dPhi * dPhi + dEta * dEta); + + const auto mctrack1 = track1.mcParticle(); + const auto mctrack2 = track2.mcParticle(); + + if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) { + continue; + } + + int truePdg1 = std::abs(mctrack1.pdgCode()); + int truePdg2 = std::abs(mctrack2.pdgCode()); + + bool isFakeKaon = (truePdg1 != PDG_t::kKPlus); + bool isFakePion = (truePdg2 != PDG_t::kPiPlus); + + bool hasCommonMother = false; + int commonMotherPDG = initialValue; + + for (const auto& mother1 : mctrack1.mothers_as()) { + for (const auto& mother2 : mctrack2.mothers_as()) { + if (mother1.pdgCode() == mother2.pdgCode() && mother1.globalIndex() == mother2.globalIndex()) { + hasCommonMother = true; + commonMotherPDG = std::abs(mother1.pdgCode()); + break; + } + } + if (hasCommonMother) { + break; + } + } + + if (!isFakeKaon && !isFakePion && hasCommonMother && commonMotherPDG == o2::constants::physics::kK0Star892) { // True Signal (Correct identification and from a K*0) + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hTrueKstar_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hTrueKstar_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hTrueKstar_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hTrueKstar_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hTrueKstar_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hTrueKstar_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hTrueKstar_PtCentMass"), ptPair, centrality, mass); + } else if (isFakeKaon || isFakePion) { // Incorrect identification + if (hasCommonMother) { // From same mother + if (commonMotherPDG == o2::constants::physics::kOmega) { // Omega reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hOmega_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hOmega_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hOmega_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hOmega_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hOmega_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hOmega_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hOmega_PtCentMass"), ptPair, centrality, mass); + } else if (commonMotherPDG == PDG_t::kRho770_0) { // Rho reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hRho_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hRho_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hRho_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hRho_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hRho_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hRho_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hRho_PtCentMass"), ptPair, centrality, mass); + } else if (commonMotherPDG == o2::constants::physics::kEta) { // Eta reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hEta_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hEta_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hEta_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hEta_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hEta_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hEta_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hEta_PtCentMass"), ptPair, centrality, mass); + } else if (commonMotherPDG == o2::constants::physics::kEtaPrime) { // Eta' reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hEtaP_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hEtaP_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hEtaP_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hEtaP_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hEtaP_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hEtaP_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hEtaP_PtCentMass"), ptPair, centrality, mass); + } else if (commonMotherPDG == o2::constants::physics::kPhi) { // Phi reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hPhi_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hPhi_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hPhi_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hPhi_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hPhi_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hPhi_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hPhi_PtCentMass"), ptPair, centrality, mass); + } else if (commonMotherPDG == o2::constants::physics::kK0Star892) { // K*0 self reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hKstarMisId_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hKstarMisId_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hKstarMisId_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hKstarMisId_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hKstarMisId_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hKstarMisId_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hKstarMisId_PtCentMass"), ptPair, centrality, mass); + } else if (commonMotherPDG == PDG_t::kK0Short) { // K0s reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hK0s_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hK0s_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hK0s_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hK0s_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hK0s_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hK0s_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hK0s_PtCentMass"), ptPair, centrality, mass); + } else if (commonMotherPDG == PDG_t::kLambda0) { // Lambda reflection + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hLambda_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hLambda_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hLambda_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hLambda_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hLambda_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hLambda_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hLambda_PtCentMass"), ptPair, centrality, mass); + } else { // All other reflections + hMC.fill(HIST("KinematicsMisId/hUnknownMisIdMotherPDG"), commonMotherPDG); + + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hOtherMother_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMother_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMother_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMother_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMother_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hOtherMother_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMother_PtCentMass"), ptPair, centrality, mass); + } + } else { // Incorrect identification but no common mother + if (additionalKin) { + hMC.fill(HIST("KinematicsMisId/hOtherMisId_PtOpenAngleMass"), ptPair, openAngle, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMisId_PtDeltaPhiMass"), ptPair, dPhi, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMisId_PtDeltaEtaMass"), ptPair, dEta, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMisId_PtKaonPtMass"), ptPair, ptK, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMisId_PtPionPtMass"), ptPair, ptPi, mass); + } + hMC.fill(HIST("KinematicsMisId/hOtherMisId_PtDeltaRMass"), ptPair, dR, mass); + hMC.fill(HIST("KinematicsMisId/hOtherMisId_PtCentMass"), ptPair, centrality, mass); + } + } + } } - PROCESS_SWITCH(Kstar892LightIon, processMCCheck, "Cross-check MC analysis", false); + PROCESS_SWITCH(Kstar892LightIon, processMisIdKinematics, "Process Signal vs Misid Background Kinematics", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/Resonances/kstar892analysis.cxx b/PWGLF/Tasks/Resonances/kstar892analysis.cxx index ad8b6d9e3d8..88fd873b977 100644 --- a/PWGLF/Tasks/Resonances/kstar892analysis.cxx +++ b/PWGLF/Tasks/Resonances/kstar892analysis.cxx @@ -331,7 +331,7 @@ struct kstar892analysis { float massPi = MassPionCharged; template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // TPC if (track.tpcNClsFound() < cfgTPCcluster) @@ -886,7 +886,7 @@ struct kstar892analysis { void processMELight(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -904,7 +904,7 @@ struct kstar892analysis { void processMELightWithTof(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -922,7 +922,7 @@ struct kstar892analysis { void processMELightTPCLowPt(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -940,7 +940,7 @@ struct kstar892analysis { void processMELightTOFHighPt(o2::aod::ResoCollisions& collisions, soa::Filtered const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair, BinningTypeVtxZT0M> pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/kstarInJets.cxx b/PWGLF/Tasks/Resonances/kstarInJets.cxx new file mode 100644 index 00000000000..d62d47cd5dc --- /dev/null +++ b/PWGLF/Tasks/Resonances/kstarInJets.cxx @@ -0,0 +1,2115 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// \file kstarInJets.cxx +/// \brief the pT spectra of k*0(892) resonance analysis in Jets collisions +/// \author Jimun Lee + +#include "PWGJE/Core/JetDerivedDataUtilities.h" +#include "PWGJE/DataModel/Jet.h" +#include "PWGJE/DataModel/JetReducedData.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace ROOT::Math; + +struct kstarInJets { + SliceCache cache; + Preslice perCollision = aod::track::collisionId; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + //================================== + //| + //| Selection + //| + //================================== + Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; + + // Event Selection + Configurable cfgEventSelections{"cfgEventSelections", "sel8", "Set event selection"}; + Configurable cfgEventVtxCut{"cfgEventVtxCut", 10.0, "V_z cut selection"}; + Configurable cfgEventMaxEta{"cfgEventMaxEta", 1.0, "set INEL event eta cut"}; + ConfigurableAxis cfgCentAxis{"cfgCentAxis", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}, "Binning of the centrality axis"}; + + // Track Selection + // General + Configurable cfgTrackMinPt{"cfgTrackMinPt", 0.15, "set track min pT"}; + Configurable cfgTrackMaxEta{"cfgTrackMaxEta", 0.8, "set track max Eta"}; + Configurable cfgTrackMaxDCArToPVcut{"cfgTrackMaxDCArToPVcut", 0.5, "Track DCAr cut to PV Maximum"}; + Configurable cfgTrackMaxDCAzToPVcut{"cfgTrackMaxDCAzToPVcut", 2.0, "Track DCAz cut to PV Maximum"}; + Configurable cfgTrackGlobalSel{"cfgTrackGlobalSel", true, "Global track selection"}; + Configurable cfgTrackPrimaryTrack{"cfgTrackPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgTrackConnectedToPV{"cfgTrackConnectedToPV", true, "PV contributor track selection"}; // PV Contriuibutor + Configurable cfgTrackGlobalWoDCATrack{"cfgTrackGlobalWoDCATrack", true, "Global track selection without DCA"}; + // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNTCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + // TPC + Configurable cfgTrackFindableTPCClusters{"cfgTrackFindableTPCClusters", 0, "nFindable TPC Clusters"}; // origin = 50 + Configurable cfgTrackTPCCrossedRows{"cfgTrackTPCCrossedRows", 70, "nCrossed TPC Rows"}; + Configurable cfgTrackRowsOverFindable{"cfgTrackRowsOverFindable", 0, "nRowsOverFindable TPC CLusters"}; // origin = 1.2 + Configurable cfgTrackTPCChi2{"cfgTrackTPCChi2", 4.0, "nTPC Chi2 per Cluster"}; + + // ITS + Configurable cfgTrackITSChi2{"cfgTrackITSChi2", 36.0, "nITS Chi2 per Cluster"}; + + // PID + Configurable cfgTrackTPCPID{"cfgTrackTPCPID", true, "Enables TPC PID"}; + Configurable cfgTrackTOFPID{"cfgTrackTOFPID", true, "Enables TOF PID"}; + Configurable cfgTrackSquarePIDCut{"cfgTrackSqurePIDCut", true, "Enables PID cut shape square switch"}; + Configurable cfgTrackCirclePIDCut{"cfgTrackCirclePIDCut", true, "Enables PID cut shape circle switch"}; + Configurable cfgTrackCircleValue{"cfgTrackCircleValue", 2, "Enables TOF TPC PID circle cut value"}; + Configurable cfgTrackTOFHard{"cfgTrackTOFHard", false, "Enables TOF Hard"}; + Configurable cfgTrackTPCPIDnSig{"cfgTrackTPCPIDnSig", 4.0, "nTPC PID sigma"}; + Configurable cfgTrackTOFPIDnSig{"cfgTrackTOFPIDnSig", 4.0, "nTOF PID sigma"}; + + // MCGen + Configurable cfgForceGenReco{"cfgForceGenReco", false, "Only consider events which are reconstructed (neglect event-loss)"}; + Configurable cfgReqMcEffPID{"cfgReqMcEffPID", false, "Request McEfficiency PID"}; + + // Pair + Configurable cfgMinvNBins{"cfgMinvNBins", 300, "Number of bins for Minv axis"}; + Configurable cfgMinvMin{"cfgMinvMin", 0.60, "Minimum Minv value"}; + Configurable cfgMinvMax{"cfgMinvMax", 1.20, "Maximum Minv value"}; + + // Histogram + ConfigurableAxis binsDCAz{"binsDCAz", {40, -0.2, 0.2}, ""}; + ConfigurableAxis binsDCAxy{"binsDCAxy", {40, -0.2, 0.2}, ""}; + Configurable cfgEventCutQA{"cfgEventCutsQA", false, "Enable Event QA Hists"}; + Configurable cfgTrackCutQA{"cfgTrackCutQA", false, "Enable Track QA Hists"}; + Configurable cfgJetQAHistos{"cfgJetQAHistos", false, "Enable Jet QA Histos"}; + + Configurable cfgMCHistos{"cfgMCHistos", false, "Enable MC Histos"}; + Configurable cfgJetHistos{"cfgJetHistos", false, "Enable Jet Histos"}; + Configurable cfgJetMCHistos{"cfgJetMCHistos", false, "Enable Jet MC Histos"}; + Configurable cfgJetdRHistos{"cfgJetdRHistos", false, "Enable Jet dR QA Histos"}; + Configurable cfgMatchedGenHistos{"cfgMatchedGenHistos", false, "Enable Matched Gen Histos"}; + + Configurable cfgCutonTrig{"cfgCutonTrig", false, "Enable Jet Cut on Trig"}; + + //====================== + //| + //| JET + //| + //====================== + Configurable cfgJetpT{"cfgJetpT", 8.0, "Set Jet pT minimum"}; + Configurable cfgJetR{"cfgJetR", 0.4, "Anti-kT Radius"}; + Configurable cfgJetdR{"cfgJetdR", 0.4, "Set Jet radius parameter"}; + Configurable cfgJetMaxEta{"cfgJetMaxEta", 0.9, "Set Jet Max Eta"}; + + Configurable cfgSingleJet{"cfgSingleJet", false, "Enforces strict phi-jet correspondance"}; + Configurable cfgReqJets{"cfgReqJets", false, "False: MB, True: Inside Jets"}; + Configurable cfgRealTriggerMasks{"cfgRealTriggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; + Configurable cfgTriggerMasksTest1{"cfgTriggerMasksTest1", "", "possible JE Trigger masks Test1"}; + Configurable cfgTriggerMasksTest2{"cfgTriggerMasksTest2", "", "possible JE Trigger masks Test2"}; + Configurable cfgTriggerMasksTest3{"cfgTriggerMasksTest3", "", "possible JE Trigger masks Test3"}; + Configurable cfgForceTrueINELgt{"cfgForceTrueINELgt", true, "Check generated event with True INELgt0"}; + + std::vector eventSelectionBits; + std::vector RealTriggerMaskBits; + std::vector triggerMaskBitsTest1; + std::vector triggerMaskBitsTest2; + std::vector triggerMaskBitsTest3; + + void init(o2::framework::InitContext&) + { + // HISTOGRAMS + const AxisSpec axisEta{30, -1.5, 1.5, "#eta"}; + const AxisSpec axisPhi{200, -1, 7, "#phi"}; + const AxisSpec axisPt{100, 0, 100, "#it{p}_{T}"}; // Jet pT + const AxisSpec ptAxis = {100, 0, 100.0}; // Particle pT + const AxisSpec pidAxis = {120, -6, 6}; + const AxisSpec minvAxis = {cfgMinvNBins, cfgMinvMin, cfgMinvMax}; + const AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; + const AxisSpec axisDCAxy{binsDCAxy, "DCA_{XY}"}; + const AxisSpec dRAxis = {100, 0, 100}; + + if (cfgEventCutQA) { + histos.add("hEvent_Cut", "Number of event after cuts", kTH1D, {{13, -0.5, 12.5}}); + histos.add("hPosZ_BC", "hPosZ_BC", kTH1F, {{300, -15.0, 15.0}}); + histos.add("hPosZ_AC", "hPosZ_AC", kTH1F, {{300, -15.0, 15.0}}); + histos.add("hcentFT0C_BC", "centFT0C_BC", kTH1F, {{110, 0.0, 110.0}}); + histos.add("hcentFT0C_AC", "centFT0C_AC", kTH1F, {{110, 0.0, 110.0}}); + + std::shared_ptr hCutFlow = histos.get(HIST("hEvent_Cut")); + std::vector eventCutLabels = { + "All Events", + "sel8", + Form("|Vz| < %.1f", cfgEventVtxCut.value), + "kIsGoodZvtxFT0vsPV", + "kNoSameBunchPileup", + "kNoTimeFrameBorder", + "kNoITSROFrameBorder", + "kNoCollInTimeRangeStandard", + "kIsGoodITSLayersAll", + "All passed events"}; + for (size_t i = 0; i < eventCutLabels.size(); ++i) { + hCutFlow->GetXaxis()->SetBinLabel(i + 1, eventCutLabels[i].c_str()); + } + } + if (cfgTrackCutQA) { + histos.add("hDCArToPv_BC", "DCArToPv_BC", kTH1F, {axisDCAxy}); + histos.add("hDCAzToPv_BC", "DCAzToPv_BC", kTH1F, {axisDCAz}); + histos.add("hIsPrim_BC", "hIsPrim_BC", kTH1F, {{2, -0.5, 1.5}}); + histos.add("hIsGood_BC", "hIsGood_BC", kTH1F, {{2, -0.5, 1.5}}); + histos.add("hIsPrimCont_BC", "hIsPrimCont_BC", kTH1F, {{2, -0.5, 1.5}}); + histos.add("hFindableTPCClusters_BC", "hFindableTPCClusters_BC", kTH1F, {{200, 0, 200}}); + histos.add("hFindableTPCRows_BC", "hFindableTPCRows_BC", kTH1F, {{200, 0, 200}}); + histos.add("hClustersVsRows_BC", "hClustersVsRows_BC", kTH1F, {{200, 0, 2}}); + histos.add("hTPCChi2_BC", "hTPCChi2_BC", kTH1F, {{200, 0, 100}}); + histos.add("hITSChi2_BC", "hITSChi2_BC", kTH1F, {{200, 0, 100}}); + histos.add("QA_nSigma_pion_TPC_BC", "QA_nSigma_pion_TPC_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_nSigma_pion_TOF_BC", "QA_nSigma_pion_TOF_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_pion_TPC_TOF_BC", "QA_pion_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); + histos.add("QA_nSigma_kaon_TPC_BC", "QA_nSigma_kaon_TPC_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_nSigma_kaon_TOF_BC", "QA_nSigma_kaon_TOF_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_kaon_TPC_TOF_BC", "QA_kaon_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); + histos.add("QA_track_pT_BC", "QA_track_pT_BC", kTH1F, {{13, 0.0, 13.0}}); + + histos.add("hDCArToPv_AC", "DCArToPv_AC", kTH1F, {axisDCAxy}); + histos.add("hDCAzToPv_AC", "DCAzToPv_AC", kTH1F, {axisDCAz}); + histos.add("hIsPrim_AC", "hIsPrim_AC", kTH1F, {{2, -0.5, 1.5}}); + histos.add("hIsGood_AC", "hIsGood_AC", kTH1F, {{2, -0.5, 1.5}}); + histos.add("hIsPrimCont_AC", "hIsPrimCont_AC", kTH1F, {{2, -0.5, 1.5}}); + histos.add("hFindableTPCClusters_AC", "hFindableTPCClusters_AC", kTH1F, {{200, 0, 200}}); + histos.add("hFindableTPCRows_AC", "hFindableTPCRows_AC", kTH1F, {{200, 0, 200}}); + histos.add("hClustersVsRows_AC", "hClustersVsRows_AC", kTH1F, {{200, 0, 2}}); + histos.add("hTPCChi2_AC", "hTPCChi2_AC", kTH1F, {{200, 0, 100}}); + histos.add("hITSChi2_AC", "hITSChi2_AC", kTH1F, {{200, 0, 100}}); + histos.add("QA_nSigma_pion_TPC_AC", "QA_nSigma_pion_TPC_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_nSigma_pion_TOF_AC", "QA_nSigma_pion_TOF_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_pion_TPC_TOF_AC", "QA_pion_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); + histos.add("QA_nSigma_kaon_TPC_AC", "QA_nSigma_kaon_TPC_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_nSigma_kaon_TOF_AC", "QA_nSigma_kaon_TOF_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); + histos.add("QA_kaon_TPC_TOF_AC", "QA_kaon_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); + histos.add("QA_track_pT_AC", "QA_track_pT_AC", kTH1F, {{13, 0.0, 13.0}}); + } + + if (cfgJetQAHistos) { + histos.add("nTriggerQA", "nTriggerQA", kTH1F, {{8, 0.0, 8.0}}); + + histos.add("JetpT", "Jet pT (GeV/c)", kTH1F, {{4000, 0., 200.}}); + histos.add("JetEta", "Jet Eta", kTH1F, {{100, -1.0, 1.0}}); + histos.add("JetPhi", "Jet Phi", kTH1F, {{80, -1.0, 7.0}}); + + histos.add("nGoodJets", "The number of good jets", kTH1F, {{6, -0.5, 5.5}}); + histos.add("nJetsPerEvent", "The number of jet per event", kTH1F, {{6, -0.5, 5.5}}); + + histos.add("jetTrackEta", "Jet Track Eta", kTH1F, {{100, -1.0, 1.0}}); + histos.add("jetTrackPhi", "Jet Track Phi", kTH1F, {{80, -1.0, 7.0}}); + } + if (cfgJetdRHistos) { + histos.add("mcpjet_pt", "MC particle-level Jet pT (GeV/c)", kTH1F, {{2000, 0., 100.}}); + histos.add("mcpjet_eta", "MC particle-level Jet Eta", kTH1F, {{100, -1.0, 1.0}}); + histos.add("mcpjet_phi", "MC particle-level Jet Phi", kTH1F, {{80, -1.0, 7.0}}); + + histos.add("Gen_KstarPt_All_BR", "Generated K*0 All pT (GeV/c)", kTH1F, {{2000, 0., 100.}}); + histos.add("Gen_Kstar_BR", "Generated K*0 BR", kTH1F, {minvAxis}); + histos.add("Gen_KstarPt_BR", "Generated K*0 pT (GeV/c)", kTH1F, {{2000, 0., 100.}}); + + histos.add("dR_taggedjet_kaon", "dR between tagged jet and kaon wo pT cut", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + histos.add("dR_taggedjet_pion", "dR between tagged jet and pion wo pT cut", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + histos.add("dR_taggedjet_all", "dR between tagged jet and kpi", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + histos.add("dR_taggedjet_all_6_8", "dR between tagged jet and kpi 6 < jet pT < 8", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + histos.add("dR_taggedjet_all_8_12", "dR between tagged jet and kpi 8 < jet pT < 12", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + + histos.add("dR_taggedjet_0_8", "dR between tagged jet and kpi 0 < jet pT < 8", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + + histos.add("dR_kstar_pion", "dR between kstar and pion", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + histos.add("dR_kstar_kaon", "dR between kstar and kaon", {HistType::kTH2F, {{100, 0., 3.0}, {150, 0., 30.}}}); + + histos.add("Kstar_pT_INJet_6_8", "K*0 pT Inside 6-8 Jet", kTH1F, {{1000, 0., 50.}}); + histos.add("Kstar_pT_INJet_8_10", "K*0 pT Inside 8-10 Jet", kTH1F, {{1000, 0., 50.}}); + histos.add("Kstar_pT_INJet_10_12", "K*0 pT Inside 10-12 Jet", kTH1F, {{1000, 0., 50.}}); + histos.add("Kstar_pT_INJet_8_12", "K*0 pT Inside 8-12 Jet", kTH1F, {{1000, 0., 50.}}); + + histos.add("JetpT_8to", "Jet pT 8 to", kTH1F, {{2000, 0., 100.}}); + histos.add("recoveredJetpT_6_8to8", "Recovered Jet pT 6-8 --> 8", kTH1F, {{2000, 0., 100.}}); + histos.add("Kstar_pT_6_8to8_IN_recoveredJet", "K*0 pT In recovered Jet pT 6-8 --> 8", kTH1F, {{1000, 0., 50.}}); + histos.add("Kstar_pT_6_8to8_10_IN_recoveredJet", "K*0 pT In recovered Jet pT 6-8 --> 8-10", kTH1F, {{1000, 0., 50.}}); + + histos.add("missed_kpi_INJets_6_8", "missed kpi In Jets with 6 < jetPt < 8", {HistType::kTH2F, {{120, 0.0, 1.2}, {100, 0., 20.}}}); + histos.add("missed_kpi_INJets_8_10", "missed kpi In Jets with 8 < jetPt < 10", {HistType::kTH2F, {{120, 0.0, 1.2}, {100, 0., 20.}}}); + histos.add("missed_kpi_INJets_10_12", "missed kpi In Jets with 10 < jetPt < 12", {HistType::kTH2F, {{120, 0.0, 1.2}, {100, 0., 20.}}}); + histos.add("missed_kpi_INJets_12_15", "missed kpi In Jets with 12 < jetPt < 15", {HistType::kTH2F, {{120, 0.0, 1.2}, {100, 0., 20.}}}); + histos.add("missed_kpi_INJets_15_25", "missed kpi In Jets with 15 < jetPt < 25", {HistType::kTH2F, {{120, 0.0, 1.2}, {100, 0., 20.}}}); + histos.add("missed_kpi_INJets_25_infinite", "missed kpi In Jets with 25 < jetPt < infinite", {HistType::kTH2F, {{120, 0.0, 1.2}, {100, 0., 20.}}}); + histos.add("missed_kpi_INJets_8_infinite", "missed kpi In Jets with 8 < jetPt < infinite", {HistType::kTH2F, {{120, 0.0, 1.2}, {100, 0., 20.}}}); + + histos.add("JetMigration", "bin to bin migration", {HistType::kTH2F, {{150, 0.0, 15.0, "True jet pT (GeV/c)"}, {150, 0., 15., "Recovered jet pT (GeV/c)"}}}); + } + + /////////////////////////////////// + histos.add("nEvents", "nEvents", kTH1F, {{7, 0.0, 7.0}}); + histos.add("hUSS_KPi", "hUSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); + histos.add("hLSS_KPi", "hLSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); + histos.add("hUSS_PiK", "hUSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); + histos.add("hLSS_PiK", "hLSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); + + if (cfgMCHistos) { + histos.add("nEvents_Gen", "nEvents_Gen", kTH1F, {{7, 0.0, 7.0}}); + histos.add("hUSS_TrueRec", "hUSS_TrueRec", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); + histos.add("hGen_pT_Kstar", "Gen_pT_Kstar (GeV/c)", kTH1F, {{800, 0., 40.}}); + histos.add("hRec_pT_Kstar", "Rec_pT_Kstar", kTHnSparseF, {cfgCentAxis, ptAxis}); + } + if (cfgJetHistos) { + histos.add("hUSS_KPi_INSIDE", "hUSS_KPi_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); + histos.add("hUSS_PiK_INSIDE", "hUSS_PiK_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); + histos.add("hLSS_KPi_INSIDE", "hLSS_KPi_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); + histos.add("hLSS_PiK_INSIDE", "hLSS_PiK_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); + } + if (cfgJetMCHistos) { + histos.add("nEvents_Gen", "nEvents_Gen", kTH1F, {{7, 0.0, 7.0}}); + histos.add("hUSS_TrueRec", "hUSS_TrueRec", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); + histos.add("hUSS_TrueRec_INSIDE", "hUSS_TrueRec_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); + + histos.add("hGen_pT_Kstar", "Gen_pT_Kstar (GeV/c)", kTH1F, {{800, 0., 40.}}); + histos.add("hRec_pT_Kstar", "Rec_pT_Kstar (GeV/c)", kTH1F, {{800, 0., 40.}}); + histos.add("hEffRec_pT", "EffRec_pT (GeV/c)", kTH1F, {{1600, 0., 80.}}); + } + + if (cfgMatchedGenHistos) { + histos.add("nEvents_MCGen_MATCHED", "nEvents_MCGen_MATCHED", kTH1F, {{7, 0.0, 7.0}}); + histos.add("matchedJets_pT", "#it{p}_{T,jet part}; #it{p}_{T,jet det}", {HistType::kTH2F, {{100, 0., 20.}, {400, -20., 20.0}}}); + histos.add("matchedJets_eta", "#eta_{jet part}; #eta_{jet det}", {HistType::kTH2F, {{100, -1.0, 1.0}, {400, -20.0, 20.0}}}); + histos.add("matchedJets_phi", "#phi_{jet part}; #phi_{jet det}", {HistType::kTH2F, {{80, -1.0, 7.}, {400, -20.0, 20.}}}); + + histos.add("NoMatchedJets_pT", "Gen. No Matched detector-level Jet", kTHnSparseD, {axisPt}); + histos.add("RespGen_Matched_rand0", "Gen. Response Matrix Matched 0", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); + histos.add("RespGen_Matched_rand1", "Gen. Response Matrix Matched 1", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); + + // ALL Jets + histos.add("nEvents_MCGen_MATCHED_allJet", "nEvents_MCGen_MATCHED_allJet", kTH1F, {{7, 0.0, 7.0}}); + histos.add("NoMatchedJets_pT_allJet", "Gen. No Matched detector-level ALL Jet", kTHnSparseD, {axisPt}); + histos.add("RespGen_MatchedJet_TEST_allJet", "Gen. Response Matrix Matched ALL Jet Test", kTHnSparseD, {axisPt, axisPt}); + histos.add("RespGen_Matrix_Matched_allJet", "Gen. Response Matrix Matched ALL Jet", kTHnSparseD, {ptAxis, axisPt, ptAxis, axisPt}); + + histos.add("RespGen_MatchedJet_TEST_rand0_allJet", "Gen. Response Matrix Matched ALL Jet TEST", kTHnSparseD, {axisPt, axisPt}); + } + + // Jet + eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(cfgEventSelections)); + RealTriggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(cfgRealTriggerMasks); + triggerMaskBitsTest1 = jetderiveddatautilities::initialiseTriggerMaskBits(cfgTriggerMasksTest1); + triggerMaskBitsTest2 = jetderiveddatautilities::initialiseTriggerMaskBits(cfgTriggerMasksTest2); + triggerMaskBitsTest3 = jetderiveddatautilities::initialiseTriggerMaskBits(cfgTriggerMasksTest3); + } // end of init + + //=============== + //| LF Analysis + //=============== + using EventCandidates = soa::Join; //, aod::CentFT0Ms, aod::CentFT0As + using EventCandidatesTrue = aod::McCollisions; + using TrackCandidates = soa::Join; + using TrackCandidatesMC = soa::Join; + + //================= + //|| Jet Analysis + //================= + Filter JEPosZFilter = nabs(aod::jcollision::posZ) < cfgEventVtxCut; + Filter JEMCPosZFilter = nabs(aod::jmccollision::posZ) < cfgEventVtxCut; + Filter JetCuts = aod::jet::pt > cfgJetpT&& aod::jet::r == nround(cfgJetR.node() * 100.0f); + + using JetTrackCandidatesMC = soa::Join; + + double massKa = o2::constants::physics::MassKPlus; + double massPi = o2::constants::physics::MassPiMinus; + + static constexpr int Kstar0PDG = 313; + static constexpr int KaonPDG = 321; + static constexpr int PionPDG = 211; + + //================================== + //|| + //|| Helper Templates + //|| + //================================== + template + void fillQA(const bool pass, const objType& obj, const int objecttype = 0) + { + if (objecttype == 1) { + if constexpr (requires { obj.posZ(); }) { + if (!pass) { + histos.fill(HIST("hPosZ_BC"), obj.posZ()); + histos.fill(HIST("hcentFT0C_BC"), obj.centFT0C()); + } else { + histos.fill(HIST("hPosZ_AC"), obj.posZ()); + histos.fill(HIST("hcentFT0C_AC"), obj.centFT0C()); + } + } + } // eventSelection histogram + if constexpr (requires { obj.tpcCrossedRowsOverFindableCls(); }) { + if (objecttype == 2) { + if (!pass) { + histos.fill(HIST("hDCArToPv_BC"), obj.dcaXY()); + histos.fill(HIST("hDCAzToPv_BC"), obj.dcaZ()); + histos.fill(HIST("hIsPrim_BC"), obj.isPrimaryTrack()); + histos.fill(HIST("hIsGood_BC"), obj.isGlobalTrackWoDCA()); + histos.fill(HIST("hIsPrimCont_BC"), obj.isPVContributor()); + histos.fill(HIST("hFindableTPCClusters_BC"), obj.tpcNClsFindable()); + histos.fill(HIST("hFindableTPCRows_BC"), obj.tpcNClsCrossedRows()); + histos.fill(HIST("hClustersVsRows_BC"), obj.tpcCrossedRowsOverFindableCls()); + histos.fill(HIST("hTPCChi2_BC"), obj.tpcChi2NCl()); + histos.fill(HIST("hITSChi2_BC"), obj.itsChi2NCl()); + histos.fill(HIST("QA_track_pT_BC"), obj.pt()); + } else { + histos.fill(HIST("hDCArToPv_AC"), obj.dcaXY()); + histos.fill(HIST("hDCAzToPv_AC"), obj.dcaZ()); + histos.fill(HIST("hIsPrim_AC"), obj.isPrimaryTrack()); + histos.fill(HIST("hIsGood_AC"), obj.isGlobalTrackWoDCA()); + histos.fill(HIST("hIsPrimCont_AC"), obj.isPVContributor()); + histos.fill(HIST("hFindableTPCClusters_AC"), obj.tpcNClsFindable()); + histos.fill(HIST("hFindableTPCRows_AC"), obj.tpcNClsCrossedRows()); + histos.fill(HIST("hClustersVsRows_AC"), obj.tpcCrossedRowsOverFindableCls()); + histos.fill(HIST("hTPCChi2_AC"), obj.tpcChi2NCl()); + histos.fill(HIST("hITSChi2_AC"), obj.itsChi2NCl()); + histos.fill(HIST("QA_track_pT_AC"), obj.pt()); + } + } + } // trackSelection + if (objecttype == 3) { + if constexpr (requires { obj.pt(); }) { + if (!pass) { + histos.fill(HIST("QA_nSigma_kaon_TPC_BC"), obj.pt(), obj.tpcNSigmaKa()); + histos.fill(HIST("QA_nSigma_kaon_TOF_BC"), obj.pt(), obj.tofNSigmaKa()); + histos.fill(HIST("QA_kaon_TPC_TOF_BC"), obj.tpcNSigmaKa(), obj.tofNSigmaKa()); + } else { + histos.fill(HIST("QA_nSigma_kaon_TPC_AC"), obj.pt(), obj.tpcNSigmaKa()); + histos.fill(HIST("QA_nSigma_kaon_TOF_AC"), obj.pt(), obj.tofNSigmaKa()); + histos.fill(HIST("QA_kaon_TPC_TOF_AC"), obj.tpcNSigmaKa(), obj.tofNSigmaKa()); + } + } + } // kaon pid Selection + if (objecttype == 4) { + if constexpr (requires { obj.pt(); }) { + if (!pass) { + histos.fill(HIST("QA_nSigma_pion_TPC_BC"), obj.pt(), obj.tpcNSigmaPi()); + histos.fill(HIST("QA_nSigma_pion_TOF_BC"), obj.pt(), obj.tofNSigmaPi()); + histos.fill(HIST("QA_pion_TPC_TOF_BC"), obj.tpcNSigmaPi(), obj.tofNSigmaPi()); + } else { + histos.fill(HIST("QA_nSigma_pion_TPC_AC"), obj.pt(), obj.tpcNSigmaPi()); + histos.fill(HIST("QA_nSigma_pion_TOF_AC"), obj.pt(), obj.tofNSigmaPi()); + histos.fill(HIST("QA_pion_TPC_TOF_AC"), obj.tpcNSigmaPi(), obj.tofNSigmaPi()); + } + } + } // pion pid Selection + } // fill QA + + // enum class objectType { Inclusive, + // Jets, + // RecoParticle, + // JetsRecoParticle }; + + enum class objectType { Inclusive, + Jets }; + template + void fillMinv(objectType type, const TrackType& trk1, const TrackType& trk2, const ROOT::Math::PxPyPzMVector& lReso, double centrality, double jetpt, bool flip) + { + double conjugate = trk1.sign() * trk2.sign(); + switch (type) { + case objectType::Inclusive: + if (conjugate < 0) { + if (!flip) { + histos.fill(HIST("hUSS_KPi"), centrality, lReso.Pt(), lReso.M()); + } else { + histos.fill(HIST("hUSS_PiK"), centrality, lReso.Pt(), lReso.M()); + } + } else if (conjugate > 0) { + if (!flip) { + histos.fill(HIST("hLSS_KPi"), centrality, lReso.Pt(), lReso.M()); + } else { + histos.fill(HIST("hLSS_PiK"), centrality, lReso.Pt(), lReso.M()); + } + } + break; + + case objectType::Jets: + if (cfgJetHistos) { + if (conjugate < 0) { + if (!flip) { + histos.fill(HIST("hUSS_KPi_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); + } else { + histos.fill(HIST("hUSS_PiK_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); + } + } else if (conjugate > 0) { + if (!flip) { + histos.fill(HIST("hLSS_KPi_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); + } else { + histos.fill(HIST("hLSS_PiK_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); + } + } + } + break; + + } // switch + } // fillMinv + //====================================================================== + + template + std::pair eventSelection(const EventType& event, const bool QA) + { + if (cfgEventCutQA && QA) { + fillQA(false, event, 1); + histos.fill(HIST("hEvent_Cut"), 0); + } + + if (!event.sel8()) { + return {false, 1}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 1); + } + if (std::abs(event.posZ()) > cfgEventVtxCut) { + return {false, 2}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 2); + } + if (!event.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return {false, 3}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 3); + } + if (!event.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return {false, 4}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 4); + } + if (!event.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return {false, 5}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 5); + } + if (!event.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return {false, 6}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 6); + } + if (!event.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) { + return {false, 7}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 7); + } + if (!event.selection_bit(aod::evsel::kIsGoodITSLayersAll)) { + return {false, 8}; + } + if (cfgEventCutQA && QA) { + histos.fill(HIST("hEvent_Cut"), 8); + } + + if (cfgEventCutQA && QA) { + fillQA(true, event, 1); + histos.fill(HIST("hEvent_Cut"), 9); + } + return {true, 10}; + }; + + template + std::pair JetEventSelection(const EventType& event, const bool QA) + { + if (cfgEventCutQA && QA) { + fillQA(false, event, 1); + } + + if (!jetderiveddatautilities::selectCollision(event, eventSelectionBits)) { // sel8 + return {false, 1}; + } + if (std::abs(event.posZ()) > cfgEventVtxCut) { + return {false, 2}; + } + + if (cfgEventCutQA && QA) { + fillQA(true, event, 1); + } + return {true, 8}; + }; + + template + bool trackSelection(const TracksType& track, const bool QA) + { + if (cfgTrackCutQA && QA) { + fillQA(false, track, 2); + } + + if (cfgTrackGlobalSel && !track.isGlobalTrack()) { + return false; + } + if (track.pt() < cfgTrackMinPt) { + return false; + } + if (std::abs(track.eta()) > cfgTrackMaxEta) { + return false; + } + if (std::abs(track.dcaXY()) > cfgTrackMaxDCArToPVcut) { + return false; + } + if (std::abs(track.dcaZ()) > cfgTrackMaxDCAzToPVcut) { + return false; + } + if (cfgTrackPrimaryTrack && !track.isPrimaryTrack()) { + return false; + } + if (cfgTrackGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { + return false; + } + if (cfgTrackFindableTPCClusters > 0 && track.tpcNClsFindable() < cfgTrackFindableTPCClusters) { + return false; + } + if (track.tpcNClsCrossedRows() < cfgTrackTPCCrossedRows) { + return false; + } + if (cfgTrackRowsOverFindable > 0 && track.tpcCrossedRowsOverFindableCls() > cfgTrackRowsOverFindable) { + return false; + } + if (track.tpcChi2NCl() > cfgTrackTPCChi2) { + return false; + } + if (track.itsChi2NCl() > cfgTrackITSChi2) { + return false; + } + if (cfgTrackConnectedToPV && !track.isPVContributor()) { + return false; + } + + if (cfgTrackCutQA && QA) { + fillQA(true, track, 2); + } + return true; + }; + + template + bool trackPIDKaon(const TrackPID& candidate, const bool QA) + { + double tpcpid = 0; + double tofpid = 0; + bool tpcPIDPassed{false}, tofPIDPassed{false}; + + if (cfgTrackCutQA && QA) { + fillQA(false, candidate, 3); + } + + // TPC + if (cfgTrackSquarePIDCut) { + if (std::abs(candidate.tpcNSigmaKa()) < cfgTrackTPCPIDnSig) { + tpcPIDPassed = true; + + if (candidate.hasTOF()) { + if (std::abs(candidate.tofNSigmaKa()) < cfgTrackTOFPIDnSig) { + tofPIDPassed = true; + } + } else { + if (!cfgTrackTOFHard) { + tofPIDPassed = true; + } else { + tofPIDPassed = false; + } + } + } // TPCPID cut + } // end of square cut + + if (cfgTrackCirclePIDCut) { + if (std::abs(candidate.tpcNSigmaKa()) < cfgTrackTPCPIDnSig) { + tpcpid = std::abs(candidate.tpcNSigmaKa()); + } + tofpid = 0; + + if (candidate.hasTOF()) { + tofpid = std::abs(candidate.tofNSigmaKa()); + } else { + if (cfgTrackTOFHard) { + tofpid = 999; + } + } + if (tpcpid * tpcpid + tofpid * tofpid < cfgTrackCircleValue) { + tpcPIDPassed = true; + tofPIDPassed = true; + } + } // circular cut + + // TPC & TOF + if (tpcPIDPassed && tofPIDPassed) { + if (cfgTrackCutQA && QA) + fillQA(true, candidate, 3); + return true; + } + return false; + } + + template + bool trackPIDPion(const TrackPID& candidate, const bool QA) + { + double tpcpid = 0; + double tofpid = 0; + bool tpcPIDPassed{false}, tofPIDPassed{false}; + + if (cfgTrackCutQA && QA) { + fillQA(false, candidate, 4); + } + + // TPC + if (cfgTrackSquarePIDCut) { + if (std::abs(candidate.tpcNSigmaPi()) < cfgTrackTPCPIDnSig) { + tpcPIDPassed = true; + + if (candidate.hasTOF()) { + if (std::abs(candidate.tofNSigmaPi()) < cfgTrackTOFPIDnSig) { + tofPIDPassed = true; + } + } else { + if (!cfgTrackTOFHard) { + tofPIDPassed = true; + } else { + tofPIDPassed = false; + } + } + } // TPCPID cut + + } // end of square cut + + if (cfgTrackCirclePIDCut) { + if (std::abs(candidate.tpcNSigmaPi()) < cfgTrackTPCPIDnSig) { + tpcpid = std::abs(candidate.tpcNSigmaPi()); + } + tofpid = 0; + + if (candidate.hasTOF()) { + tofpid = std::abs(candidate.tofNSigmaPi()); + } else { + if (cfgTrackTOFHard) { + tofpid = 999; + } + } + if (tpcpid * tpcpid + tofpid * tofpid < cfgTrackCircleValue) { + tpcPIDPassed = true; + tofPIDPassed = true; + } + } // circular cut + + // TPC & TOF + if (tpcPIDPassed && tofPIDPassed) { + if (cfgTrackCutQA && QA) + fillQA(true, candidate, 4); + return true; + } + return false; + } + + template + ROOT::Math::PxPyPzMVector minvReconstruction(const TracksType& trk1, const TracksType& trk2, const bool QA, const bool flip) + { + if (!trackSelection(trk1, false) || !trackSelection(trk2, false)) { + return {}; + } + if (!trackPIDKaon(trk1, QA) || !trackPIDPion(trk2, QA)) { + return {}; + } + if (trk1.globalIndex() >= trk2.globalIndex()) { + return {}; + } + + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + if (!flip) { + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); + } else { + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massPi); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massKa); + } + lResonance = lDecayDaughter1 + lDecayDaughter2; + + if (std::abs(lResonance.Eta()) > cfgTrackMaxEta) { + return {}; + } + + return {lResonance}; + } // minvReconstruction + + template + ROOT::Math::PxPyPzMVector TrueReconstruction(const TracksType& trk1, const TracksType& trk2) + { + double conjugate = trk1.sign() * trk2.sign(); + if (conjugate > 0) { + return {}; + } + + auto particle1 = trk1.mcParticle(); + auto particle2 = trk2.mcParticle(); + + if (!particle1.has_mothers() || !particle2.has_mothers()) { + return {}; + } + if (std::abs(particle1.eta()) > cfgTrackMaxEta || std::abs(particle2.eta()) > cfgTrackMaxEta) { + return {}; + } + + std::vector mothers1{}; + std::vector mothers1PDG{}; + for (auto& particle1_mom : particle1.template mothers_as()) { + mothers1.push_back(particle1_mom.globalIndex()); + mothers1PDG.push_back(particle1_mom.pdgCode()); + } + + std::vector mothers2{}; + std::vector mothers2PDG{}; + for (auto& particle2_mom : particle2.template mothers_as()) { + mothers2.push_back(particle2_mom.globalIndex()); + mothers2PDG.push_back(particle2_mom.pdgCode()); + } + + if (std::abs(mothers1PDG[0]) != Kstar0PDG) { + return {}; // mother not K*0 + } + if (std::abs(mothers2PDG[0]) != Kstar0PDG) { + return {}; // mothers not K*0 + } + if (mothers1[0] != mothers2[0]) { + return {}; // Kaon and pion not from the same K*0 + } + if (std::abs(particle1.pdgCode()) != PionPDG && std::abs(particle1.pdgCode()) != KaonPDG) { + return {}; + } + if (std::abs(particle2.pdgCode()) != PionPDG && std::abs(particle2.pdgCode()) != KaonPDG) { + return {}; + } + + double track1_mass = 0; + double track2_mass = 0; + if (std::abs(particle1.pdgCode()) == PionPDG) { + track1_mass = massPi; + } else { + track1_mass = massKa; + } + + if (std::abs(particle2.pdgCode()) == PionPDG) { + track2_mass = massPi; + } else { + track2_mass = massKa; + } + + if (track1_mass == track2_mass) { + return {}; + } + + ROOT::Math::PxPyPzMVector lTrueDaughter1, lTrueDaughter2, lTrueReso; + lTrueDaughter1 = ROOT::Math::PxPyPzMVector(particle1.px(), particle1.py(), particle1.pz(), track1_mass); + lTrueDaughter2 = ROOT::Math::PxPyPzMVector(particle2.px(), particle2.py(), particle2.pz(), track2_mass); + lTrueReso = lTrueDaughter1 + lTrueDaughter2; + + if (lTrueReso.M() < 0) { + return {}; + } + if (std::abs(lTrueReso.Eta()) > cfgTrackMaxEta) { + return {}; + } + + return {lTrueReso}; + } // TrueReconstruction + + template + void TrackSlicing(const CollisionType& collision, const TracksType& tracks, const bool QA) + { + auto centrality = collision.centFT0C(); + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsUpperIndexPolicy(tracks, tracks))) { + for (bool flip : {false, true}) { + auto lReso = minvReconstruction(trk1, trk2, QA, flip); + if (lReso.M() < 0) { + continue; + } + + fillMinv(objectType::Inclusive, trk1, trk2, lReso, centrality, -1.0, flip); + } // flip + } // for + } // TrackSlicing + + template + void TrackSlicingMC(const CollisionType& collision, const TracksType& tracks, const bool QA) + { + auto centrality = collision.centFT0C(); + for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsUpperIndexPolicy(tracks, tracks))) { + if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) { + continue; + } + + for (bool flip : {false, true}) { + auto lReso = minvReconstruction(trk1, trk2, QA, flip); + if (lReso.M() < 0) { + continue; + } + fillMinv(objectType::Inclusive, trk1, trk2, lReso, centrality, -1.0, flip); + } // flip + + //============================ + //| True Reconstruction + //============================ + auto lTrueReso = TrueReconstruction(trk1, trk2); + if (cfgMCHistos) { + histos.fill(HIST("hUSS_TrueRec"), centrality, lTrueReso.Pt(), lTrueReso.M()); + } + + } // tracks + } // TrackSlicingMC + + template + double DistinguishJets(const JetType& jets, ROOT::Math::PxPyPzMVector& lResonance) + { + if (cDebugLevel > 0) { + LOG(info) << "Found multiple jets to the same phi."; + } + + double bestR = 0; + double bestJetpT = 0; + for (auto const& jet : jets) { + double phidiff = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); + double etadiff = jet.eta() - lResonance.Eta(); + double R = TMath::Sqrt((phidiff * phidiff) + (etadiff * etadiff)); + if (R < cfgJetR && (bestR == 0 || R < bestR)) { + bestR = R; + bestJetpT = jet.pt(); + } + } // jet + return bestJetpT; + } + + template + double DistinguishJetsMC(const Jet_pt& jet_pt, const Jet_phi& jet_phi, const Jet_eta& jet_eta, const Resonance& lResonance) + { + if (cDebugLevel > 0) { + LOG(info) << "Multiple jets fit to the same K*0. Time to find the best K*0-jet"; + } + + double best_R = 0; + double best_jetpt = 0; + for (std::vector::size_type i = 0; i < jet_pt.size(); i++) { + double phidiff = TVector2::Phi_mpi_pi(jet_phi[i] - lResonance.Phi()); + double etadiff = jet_eta[i] - lResonance.Eta(); + double R = TMath::Sqrt((phidiff * phidiff) + (etadiff * etadiff)); + + if (R < cfgJetR && (best_R == 0 || R < best_R)) { + best_R = R; + best_jetpt = jet_pt[i]; + } + } // jet loop + return best_jetpt; + } + + template + void JetTrackSlicing(aod::JetCollision const& collision, TracksType const& jetTracks, const JetType& chargedjets, const bool QA) + { + //============= + //| Inclusive + //============= + auto centrality = collision.centFT0C(); + for (const auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { + auto trk1 = track1.template track_as(); + auto trk2 = track2.template track_as(); + + for (bool flip : {false, true}) { + auto lResonance = minvReconstruction(trk1, trk2, QA, flip); + if (lResonance.M() < 0) { + continue; + } + + fillMinv(objectType::Inclusive, trk1, trk2, lResonance, centrality, -1.0, flip); + + //======== + //| Jets + //======== + bool jetFlag = false; + int goodjets = 0; + double jetpt = 0; + double dR = 0; + for (auto const& jet : chargedjets) { + double dphi = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); + double deta = jet.eta() - lResonance.Eta(); + dR = TMath::Sqrt((deta * deta) + (dphi * dphi)); + + if (dR < cfgJetR) { + jetFlag = true; + jetpt = jet.pt(); + goodjets++; + } + } // The loop of chargedjets + if (cfgJetQAHistos) { + histos.fill(HIST("nGoodJets"), goodjets); + } + + if (!cfgSingleJet) { + if (goodjets > 1) { + jetpt = DistinguishJets(chargedjets, lResonance); + } + } + + if (jetFlag) { + fillMinv(objectType::Jets, trk1, trk2, lResonance, centrality, jetpt, flip); + } // jetFlag + } // flip + } // jetTracks + } // JetTrackSlicing + + template + void JetTrackSlicingMC(aod::JetCollision const& collision, TracksType const& jetTracks, const JetType& mcdjets, const bool QA) + { + //============================ + //| MB: Track Reconstruction + //============================ + auto centrality = collision.centFT0C(); + for (const auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { + auto trk1 = track1.template track_as(); + auto trk2 = track2.template track_as(); + + if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) { + continue; + } + + for (bool flip : {false, true}) { + auto lResonance = minvReconstruction(trk1, trk2, QA, flip); + if (lResonance.M() < 0) { + continue; + } + fillMinv(objectType::Inclusive, trk1, trk2, lResonance, centrality, -1.0, flip); + + //============================== + //| Jets: Track Reconstruction + //============================== + bool jetFlag = false; + int goodjets = 0; + double jetpt = 0; + for (auto const& jet : mcdjets) { + double phidiff = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); + double etadiff = jet.eta() - lResonance.Eta(); + double R = TMath::Sqrt((etadiff * etadiff) + (phidiff * phidiff)); + if (R < cfgJetR) { + jetFlag = true; + jetpt = jet.pt(); + goodjets++; + } + } + if (cfgJetQAHistos) { + histos.fill(HIST("nGoodJets"), goodjets); + } + if (!cfgSingleJet) { + if (goodjets > 1) { + jetpt = DistinguishJets(mcdjets, lResonance); + } + } + + if (jetFlag) { + fillMinv(objectType::Jets, trk1, trk2, lResonance, centrality, jetpt, flip); + } // jetFlag + } // filp + + } // Tracks loop + } // JetTrackSlicingMC + + //======================================================= + //| + //| JET DATA STUFF + //| + //======================================================= + using JetTrackCandidates = soa::Join; + using JetFilteredJets = soa::Filtered>; + + void processDataJets(o2::aod::JetCollision const& collision, JetFilteredJets const& chargedjets, JetTrackCandidates const& jetTracks, TrackCandidates const&) + { + static int nJetEvents = 0; + if (cDebugLevel > 0) { + nJetEvents++; + if ((nJetEvents + 1) % 10000 == 0) { + LOG(info) << "Processed Jet Data Events: " << nJetEvents; + } + } + + histos.fill(HIST("nEvents"), 0.5); // Raw event + + bool INELgt0 = false; + for (auto& jetTrack : jetTracks) { + if (std::abs(jetTrack.eta()) < cfgEventMaxEta) { + INELgt0 = true; + break; + } + } + if (!INELgt0) { + return; + } + histos.fill(HIST("nEvents"), 1.5); // INEL>0 event + + auto [goodEv, code] = JetEventSelection(collision, true); + // auto [goodEv, code] = eventSelection(collision, true); + if (!goodEv) { + return; + } + histos.fill(HIST("nEvents"), 2.5); // After selection + + // Trigger before we start jet finding + //===================== + //| Trigger + //===================== + if (cfgCutonTrig) { + bool RT = false; + bool VTtest1 = false; + bool VTtest2 = false; + bool VTtest3 = false; + if (jetderiveddatautilities::selectTrigger(collision, RealTriggerMaskBits)) { + RT = true; + if (cfgJetQAHistos) { + histos.fill(HIST("nTriggerQA"), 0.5); + } + } + if (jetderiveddatautilities::selectTrigger(collision, triggerMaskBitsTest1)) { + VTtest1 = true; + if (cfgJetQAHistos) { + histos.fill(HIST("nTriggerQA"), 1.5); + } + } + if (jetderiveddatautilities::selectTrigger(collision, triggerMaskBitsTest2)) { + VTtest2 = true; + if (cfgJetQAHistos) { + histos.fill(HIST("nTriggerQA"), 2.5); + } + } + if (jetderiveddatautilities::selectTrigger(collision, triggerMaskBitsTest3)) { + VTtest3 = true; + if (cfgJetQAHistos) { + histos.fill(HIST("nTriggerQA"), 3.5); + } + } + if (!RT) { + return; + } + if (VTtest1 || VTtest2 || VTtest3) { + return; + } + } // Trigger cut + histos.fill(HIST("nEvents"), 3.5); // After trigger cut + + bool HasJets = false; + int nJets = 0; + for (auto& chargedjet : chargedjets) { + if (std::abs(chargedjet.eta()) > (cfgJetMaxEta - cfgJetdR)) { + continue; + } + + nJets++; + if (cfgJetQAHistos) { + histos.fill(HIST("JetpT"), chargedjet.pt()); + histos.fill(HIST("JetEta"), chargedjet.eta()); + histos.fill(HIST("JetPhi"), chargedjet.phi()); + } + if (chargedjet.pt() > cfgJetpT) { + HasJets = true; + } + } + if (cfgJetQAHistos) { + histos.fill(HIST("nJetsPerEvent"), nJets); + } + + //==================== + //| Has Jets + //==================== + if (cfgReqJets) { + if (!HasJets) { + return; + } + } + histos.fill(HIST("nEvents"), 4.5); // Has jets + + // Just for checking track QA plot + for (auto& jetTrack : jetTracks) { + auto originTrack = jetTrack.track_as(); + if (!trackSelection(originTrack, true)) { + continue; + } + + if (cfgJetQAHistos) { + histos.fill(HIST("jetTrackEta"), jetTrack.eta()); + histos.fill(HIST("jetTrackPhi"), jetTrack.phi()); + } + } // jetTrack loop + + JetTrackSlicing(collision, jetTracks, chargedjets, true); + } // ProcessDataJets + PROCESS_SWITCH(kstarInJets, processDataJets, "process Data Jets", false); + + //======================================================= + //| + //| JET MC STUFF + //| + //======================================================= + void processMCJets(o2::aod::JetCollision const& collision, JetTrackCandidatesMC const& jetTracks, soa::Filtered const& mcdjets, TrackCandidatesMC const&, aod::McParticles const&, aod::JetParticles const&) + { + static int nJetMCEvents = 0; + if (cDebugLevel > 0) { + nJetMCEvents++; + if ((nJetMCEvents + 1) % 10000 == 0) { + LOG(info) << "Processed Jet MC Events: " << nJetMCEvents; + } + } + histos.fill(HIST("nEvents"), 0.5); // Gen event + + bool INELgt0 = false; + for (auto& jetTrack : jetTracks) { + if (std::abs(jetTrack.eta()) < cfgEventMaxEta) { + INELgt0 = true; + break; + } + } // jetTrack loop + if (!INELgt0) { + return; + } + histos.fill(HIST("nEvents"), 1.5); // INEL>0 event + + auto [goodEv, code] = JetEventSelection(collision, true); + if (!goodEv) { + return; + } + histos.fill(HIST("nEvents"), 2.5); // Inclusive event + + // The trigger option doesn't work + // if (!jetderiveddatautilities::selectTrigger(collision, RealTriggerMaskBits)) + // return; + // histos.fill(HIST("nEvents"), 3.5); // Events for Inclusive + + bool HasJets = false; + int nJets = 0; + for (auto& mcdjet : mcdjets) { + if (std::abs(mcdjet.eta()) > cfgJetMaxEta - cfgJetdR) { + continue; + } + + nJets++; + if (cfgJetQAHistos) { + histos.fill(HIST("JetpT"), mcdjet.pt()); + histos.fill(HIST("JetEta"), mcdjet.eta()); + histos.fill(HIST("JetPhi"), mcdjet.phi()); + } + if (mcdjet.pt() > cfgJetpT) { + HasJets = true; + } + } // mcdjets + if (cfgJetQAHistos) { + histos.fill(HIST("nJetsPerEvent"), nJets); + } + //==================== + //| Has Jets + //==================== + if (cfgReqJets) { + if (!HasJets) { + return; + } + } + histos.fill(HIST("nEvents"), 3.5); // Jet event + + JetTrackSlicingMC(collision, jetTracks, mcdjets, true); + + // Instead of using "JetTrackSlicingMC", Once we have to test showing the distribution each condition + + for (auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { + auto trk1 = track1.template track_as(); + auto trk2 = track2.template track_as(); + + // Each section, test pT. which section we lost the entries + ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; + lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); + lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); + lResonance = lDecayDaughter1 + lDecayDaughter2; + + if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) { + continue; + } + if (!trackSelection(trk1, true) || !trackSelection(trk2, false)) { + continue; + } + if (cfgReqMcEffPID) { // false, true + if (!trackPIDKaon(trk1, true) || !trackPIDPion(trk2, true)) { + continue; + } + } + + auto particle1 = trk1.mcParticle(); + auto particle2 = trk2.mcParticle(); + if (!particle1.has_mothers() || !particle2.has_mothers()) { + continue; + } + + std::vector mothers1{}; + std::vector mothers1PDG{}; + for (auto& particle1_mom : particle1.template mothers_as()) { + mothers1.push_back(particle1_mom.globalIndex()); + mothers1PDG.push_back(particle1_mom.pdgCode()); + } + + std::vector mothers2{}; + std::vector mothers2PDG{}; + for (auto& particle2_mom : particle2.template mothers_as()) { + mothers2.push_back(particle2_mom.globalIndex()); + mothers2PDG.push_back(particle2_mom.pdgCode()); + } + + if (mothers1[0] != mothers2[0]) { + continue; // Kaon and pion not from the same K*0 + } + if (std::abs(particle1.pdgCode()) != KaonPDG) { + continue; + } + if (std::abs(particle2.pdgCode()) != PionPDG) { + continue; + } + + if (std::abs(mothers1PDG[0]) != Kstar0PDG) { + continue; // mother not K*0 + } + if (std::abs(mothers2PDG[0]) != Kstar0PDG) { + continue; // mothers not K*0 + } + if (cfgJetMCHistos) { + histos.fill(HIST("hEffRec_pT"), lResonance.Pt()); + } + + } // track loop + + } // process loop + PROCESS_SWITCH(kstarInJets, processMCJets, "process MC Jets", false); + + //======================================================= + //| + //| DATA STUFF (SE) + //| + //======================================================= + void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + static int nEvents = 0; + if (cDebugLevel > 0) { + nEvents++; + if ((nEvents + 1) % 10000 == 0) { + LOG(info) << "Processed Data Events: " << nEvents; + } + } + histos.fill(HIST("nEvents"), 0.5); + + bool INELgt0 = false; + for (const auto& track : tracks) { + if (std::abs(track.eta()) < cfgEventMaxEta) { + INELgt0 = true; + break; + } + } + if (!INELgt0) { + return; + } + histos.fill(HIST("nEvents"), 1.5); + + auto [goodEv, code] = eventSelection(collision, true); + if (!goodEv) { + return; + } + histos.fill(HIST("nEvents"), 2.5); + + // for trackQA plot + for (const auto& track : tracks) { + if (!trackSelection(track, true)) { + continue; + } + } + + TrackSlicing(collision, tracks, true); + } // processSameEvents + PROCESS_SWITCH(kstarInJets, processDataSameEvent, "process Data Same Event", false); + + //======================================================= + //| + //| MC STUFF (SE) + //| + //========================================================= + void processSameEventMC(EventCandidates::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) + { + static int nEventsMC = 0; + if (cDebugLevel > 0) { + nEventsMC++; + if ((nEventsMC + 1) % 10000 == 0) { + double histmem = histos.getSize(); + LOG(info) << histmem; + LOG(info) << "process_SameEvent_MC: " << nEventsMC; + } + } + histos.fill(HIST("nEvents"), 0.5); + + bool INELgt0 = false; + for (const auto& track : tracks) { + if (std::abs(track.eta()) < cfgEventMaxEta) { + INELgt0 = true; + break; + } + } + if (!INELgt0) { + return; + } + histos.fill(HIST("nEvents"), 1.5); + + auto [goodEv, code] = eventSelection(collision, true); // sel8 & vtx + if (!goodEv) { + return; + } + histos.fill(HIST("nEvents"), 2.5); + + // for trackQA plot + for (const auto& track : tracks) { + if (!trackSelection(track, true)) { + continue; + } + } + + TrackSlicingMC(collision, tracks, true); + } // processSameEvents_MC + PROCESS_SWITCH(kstarInJets, processSameEventMC, "process Same Event MC", false); + + //====================================== + //| + //| GENERATED STUFF + //| + //====================================== + void processGen(EventCandidatesTrue::iterator const& collision, soa::SmallGroups> const& recocolls, aod::McParticles const& mcParticles) + { + static int nEventsGen = 0; + if (cDebugLevel > 0) { + ++nEventsGen; + if (nEventsGen % 10000 == 0) { + LOG(info) << "Processed MC (GEN) Events: " << nEventsGen; + } + } + if (cfgMCHistos) { + histos.fill(HIST("nEvents_Gen"), 0.5); // Gen events + } + //======================= + //| Event & Signal loss + //======================= + bool INELgt0 = false; + for (auto& particle : mcParticles) { + if (std::abs(particle.eta()) > cfgEventMaxEta) { + continue; + } + if (particle.pt() <= 0.0) { + continue; + } + INELgt0 = true; + break; + } + if (cfgForceTrueINELgt) { + if (!INELgt0) { + return; + } + } + if (cfgMCHistos) { + histos.fill(HIST("nEvents_Gen"), 1.5); // INEL>0 Gen events & EL: denominator + } + if (std::abs(collision.posZ()) > cfgEventVtxCut) { + return; + } + + for (auto& particle : mcParticles) { + if (std::abs(particle.pdgCode()) != Kstar0PDG) { + continue; + } + if (std::abs(particle.eta()) > cfgTrackMaxEta) { + continue; + } + + if (cfgMCHistos) { + histos.fill(HIST("hGen_pT_Kstar"), particle.pt()); // SL: denominator + } + } // Gen colls + + if (recocolls.size() <= 0) { // not reconstructed + if (cfgForceGenReco) { + return; + } + } + + //================= + //| Efficiency + //================= + double centrality = -1; + bool hasGoodEv = false; + for (auto& recocoll : recocolls) { // poorly reconstructed + auto [goodEv, code] = eventSelection(recocoll, true); + if (recocoll.posZ() > cfgEventVtxCut) { + goodEv = false; + } + if (!goodEv) { + continue; + } + + hasGoodEv = true; + centrality = recocoll.centFT0C(); + break; + } // recocolls + if (!hasGoodEv) { + return; + } + if (cfgMCHistos) { + histos.fill(HIST("nEvents_Gen"), 2.5); // EL: numerator + } + for (auto& particle : mcParticles) { + if (std::abs(particle.pdgCode()) != Kstar0PDG) { + continue; // Not K*0 + } + if (std::abs(particle.eta()) > cfgTrackMaxEta) { + continue; + } + if (particle.pt() < cfgTrackMinPt) { + continue; + } + + if (cfgMCHistos) { + histos.fill(HIST("hRec_pT_Kstar"), centrality, particle.pt()); // SL: numerator // eff: denominator + } + } // loop over particles + } // processGen + PROCESS_SWITCH(kstarInJets, processGen, "process Generated Particles", false); + + //============================================== + //| + //| GENERATED STUFF (INCLUSIVE & JETS) + //| + //============================================== + void processGenJets(o2::aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, aod::JetParticles const& mcParticles) + { + static int nprocessGenEvents = 0; + if (cDebugLevel > 0) { + ++nprocessGenEvents; + if (nprocessGenEvents % 10000 == 0) { + LOG(info) << "Processed MC (GEN) Events: " << nprocessGenEvents; + } + } + if (cfgJetMCHistos) { + histos.fill(HIST("nEvents_Gen"), 0.5); + } + //======================= + //| Event & Signal loss + //======================= + bool INELgt0 = false; + for (auto& particle : mcParticles) { + if (std::abs(particle.eta()) > cfgEventMaxEta) { + continue; + } + if (particle.pt() <= 0.0) { + continue; + } + INELgt0 = true; + break; + } + if (cfgForceTrueINELgt) { + if (!INELgt0) { + return; + } + } + if (cfgJetMCHistos) { + histos.fill(HIST("nEvents_Gen"), 1.5); // EL: denominator + } + if (std::abs(collision.posZ()) > cfgEventVtxCut) { + return; + } + + for (auto& particle : mcParticles) { + if (std::abs(particle.pdgCode()) != Kstar0PDG) { + continue; + } + if (std::abs(particle.eta()) > cfgTrackMaxEta) { + continue; + } + + if (cfgJetMCHistos) { + histos.fill(HIST("hGen_pT_Kstar"), particle.pt()); // SL: denominator + } + } // Unreco. + + if (recocolls.size() <= 0) { // not reconstructed + return; + } + //================= + //| Efficiency + //================= + for (auto& recocoll : recocolls) { // poorly reconstructed + auto goodEv = jetderiveddatautilities::selectCollision(recocoll, eventSelectionBits); + if (goodEv) { + goodEv = jetderiveddatautilities::selectTrigger(recocoll, RealTriggerMaskBits); + } + if (std::abs(recocoll.posZ()) > cfgEventVtxCut) { + goodEv = false; + } + + if (!goodEv) { + return; + } + } // reco.coll + if (cfgJetMCHistos) { + histos.fill(HIST("nEvents_Gen"), 2.5); // EL: numerator + } + + for (auto& particle : mcParticles) { + if (std::abs(particle.pdgCode()) != Kstar0PDG) { + continue; + } + if (std::abs(particle.eta()) > cfgTrackMaxEta) { + continue; + } + if (particle.pt() < cfgTrackMinPt) { + continue; + } + + /* // Not Yet + if (cfg_Force_BR) { + bool baddecay = false; + for (auto& phidaughter : particle.daughters_as()) { + if (std::abs(phidaughter.pdgCode()) != 321) { + baddecay = true; + break; + } + if (cfg_Force_Kaon_Acceptence) { + if (std::abs(phidaughter.eta()) > cfg_Track_MaxEta) { + baddecay = true; + break; + } + } + } // loop over daughters + + if (baddecay) + continue; + } // enforce BR restriction + */ + + if (cfgJetMCHistos) { + histos.fill(HIST("hRec_pT_Kstar"), particle.pt()); // SL: numerator and eff: denominator + } + } // loop over particles + } // end of process + PROCESS_SWITCH(kstarInJets, processGenJets, "Process Generated Particles Inclusive&Jets", false); + + void processJetQA(o2::aod::JetMcCollision const& collision, soa::Filtered const& mcpjets, aod::JetParticles const& mcParticles, aod::McParticles const&) + { + static int ndRtest = 0; + if (cDebugLevel > 0) { + ++ndRtest; + if (ndRtest % 10000 == 0) { + LOG(info) << "Processed dR test: " << ndRtest; + } + } + + bool INELgt0 = false; + for (auto& mcpjet : mcpjets) { + if (std::abs(mcpjet.eta()) > cfgEventMaxEta) { + continue; + } + if (mcpjet.pt() <= 0.0) { + continue; + } + INELgt0 = true; + break; + } // Selection event through mcpjet loop + if (!INELgt0) { + return; + } + if (std::abs(collision.posZ()) > cfgEventVtxCut) { + return; + } + histos.fill(HIST("nEvents"), 0.5); + + for (auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.eta()) > cfgTrackMaxEta) { + continue; + } + if (!mcParticle.has_daughters()) { + continue; + } + if (std::abs(mcParticle.pdgCode()) != Kstar0PDG) { + continue; + } + + if (cfgJetdRHistos) { + histos.fill(HIST("Gen_KstarPt_All_BR"), mcParticle.pt()); + } + // 4vector: K*0 + ROOT::Math::PxPyPzEVector lResonance = ROOT::Math::PxPyPzEVector(mcParticle.px(), mcParticle.py(), mcParticle.pz(), mcParticle.e()); + bool skip = false; + int daughter_kaon = 0; + int daughter_pion = 0; + + for (auto& daughter : mcParticle.daughters_as()) { + if (std::abs(daughter.pdgCode()) == KaonPDG) { + ++daughter_kaon; + } else if (std::abs(daughter.pdgCode()) == PionPDG) { + ++daughter_pion; + } + } + if (daughter_kaon != 1 || daughter_pion != 1) { + skip = true; + } + if (skip) { + continue; + } + + if (cfgJetdRHistos) { + histos.fill(HIST("Gen_Kstar_BR"), lResonance.M()); + histos.fill(HIST("Gen_KstarPt_BR"), mcParticle.pt()); + } + + //================== + // Distinguish Jets + double bestR = 999; + ROOT::Math::PxPyPzEVector bestJet; + + for (auto& mcpjet : mcpjets) { + if (mcpjet.pt() < cfgJetpT) { + continue; + } + + if (cfgJetdRHistos) { + histos.fill(HIST("mcpjet_eta"), mcpjet.eta()); + histos.fill(HIST("mcpjet_phi"), mcpjet.phi()); + histos.fill(HIST("mcpjet_pt"), mcpjet.pt()); + } + + double dphi = TVector2::Phi_mpi_pi(mcpjet.phi() - lResonance.Phi()); + double deta = mcpjet.eta() - lResonance.Eta(); + double dR = TMath::Sqrt((dphi * dphi) + (deta * deta)); + if (dR < bestR) { + bestR = dR; + bestJet = ROOT::Math::PxPyPzEVector(mcpjet.px(), mcpjet.py(), mcpjet.pz(), mcpjet.energy()); + } // bestR + } // mcpJets + if (bestR > cfgJetdR) { + continue; + } + + //================== + // daughters + double dR_kaon = 0; + double dR_pion = 0; + double dR_k_kstar = 0; + double dR_pi_kstar = 0; + bool isKaonInJet = false; + bool isPionInJet = false; + ROOT::Math::PxPyPzEVector Daughter, missing_daughter; + + for (auto& daughter : mcParticle.daughters_as()) { + Daughter = ROOT::Math::PxPyPzEVector(daughter.px(), daughter.py(), daughter.pz(), daughter.energy()); + + if (std::abs(daughter.pdgCode()) == KaonPDG) { + //=============== + // K*0 and kaon + double dphi_k_kstar = TVector2::Phi_mpi_pi(lResonance.Phi() - Daughter.Phi()); + double deta_k_kstar = lResonance.Eta() - Daughter.Eta(); + dR_k_kstar = TMath::Sqrt((dphi_k_kstar * dphi_k_kstar) + (deta_k_kstar * deta_k_kstar)); + + // Jet and kaon + double dphi_kaon = TVector2::Phi_mpi_pi(bestJet.Phi() - Daughter.Phi()); + double deta_kaon = bestJet.Eta() - Daughter.Eta(); + dR_kaon = TMath::Sqrt((dphi_kaon * dphi_kaon) + (deta_kaon * deta_kaon)); + + if (dR_kaon <= cfgJetdR) { + isKaonInJet = true; + } else { + missing_daughter += Daughter; + } + } else if (std::abs(daughter.pdgCode()) == PionPDG) { + //=============== + // K*0 and pion + double dphi_pi_kstar = TVector2::Phi_mpi_pi(lResonance.Phi() - Daughter.Phi()); + double deta_pi_kstar = lResonance.Eta() - Daughter.Eta(); + dR_pi_kstar = TMath::Sqrt((dphi_pi_kstar * dphi_pi_kstar) + (deta_pi_kstar * deta_pi_kstar)); + + // Jet and pion + double dphi_pion = TVector2::Phi_mpi_pi(bestJet.Phi() - Daughter.Phi()); + double deta_pion = bestJet.Eta() - Daughter.Eta(); + dR_pion = TMath::Sqrt((dphi_pion * dphi_pion) + (deta_pion * deta_pion)); + + if (dR_pion <= cfgJetdR) { + isPionInJet = true; + } else { + missing_daughter += Daughter; + } + } + } // daughter + + if (cfgJetdRHistos) { + histos.fill(HIST("dR_kstar_kaon"), dR_k_kstar, lResonance.Pt()); + histos.fill(HIST("dR_kstar_pion"), dR_pi_kstar, lResonance.Pt()); + + if (bestR < cfgJetdR) { + histos.fill(HIST("dR_taggedjet_all"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_all"), dR_pion, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_kaon"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_pion"), dR_pion, lResonance.Pt()); + + if (bestJet.Pt() < 8.0) { + histos.fill(HIST("dR_taggedjet_0_8"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_0_8"), dR_pion, lResonance.Pt()); + } + + if (bestJet.Pt() > 6.0 && bestJet.Pt() < 8.0) { + histos.fill(HIST("dR_taggedjet_all_6_8"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_all_6_8"), dR_pion, lResonance.Pt()); + histos.fill(HIST("Kstar_pT_INJet_6_8"), lResonance.Pt()); + } + if (bestJet.Pt() > 8.0) { + histos.fill(HIST("JetpT_8to"), bestJet.Pt()); + } + if (bestJet.Pt() > 8.0 && bestJet.Pt() < 10.0) { + histos.fill(HIST("Kstar_pT_INJet_8_10"), lResonance.Pt()); + } + if (bestJet.Pt() > 10.0 && bestJet.Pt() < 12.0) { + histos.fill(HIST("Kstar_pT_INJet_10_12"), lResonance.Pt()); + } + if (bestJet.Pt() > 8.0 && bestJet.Pt() < 12.0) { + histos.fill(HIST("dR_taggedjet_all_8_12"), dR_kaon, lResonance.Pt()); + histos.fill(HIST("dR_taggedjet_all_8_12"), dR_pion, lResonance.Pt()); + histos.fill(HIST("Kstar_pT_INJet_8_12"), lResonance.Pt()); + } + + // Daughters out of jets + if (!isKaonInJet || !isPionInJet) { + ROOT::Math::PxPyPzEVector recoveredJet = bestJet + missing_daughter; + if (bestJet.Pt() > 6.0 && bestJet.Pt() < 8.0) { + histos.fill(HIST("missed_kpi_INJets_6_8"), (bestJet.Pt() - missing_daughter.Pt()) / bestJet.Pt(), lResonance.Pt()); + + if (recoveredJet.Pt() > 8.0) { + histos.fill(HIST("recoveredJetpT_6_8to8"), recoveredJet.Pt()); + histos.fill(HIST("Kstar_pT_6_8to8_IN_recoveredJet"), lResonance.Pt()); + } + if (recoveredJet.Pt() > 8.0 && recoveredJet.Pt() < 10.0) { + histos.fill(HIST("Kstar_pT_6_8to8_10_IN_recoveredJet"), lResonance.Pt()); + } + } // 6 < bestJetpT < 8 GeV/c + if (bestJet.Pt() > 8.0 && bestJet.Pt() < 10.0) { + histos.fill(HIST("missed_kpi_INJets_8_10"), (bestJet.Pt() - missing_daughter.Pt()) / bestJet.Pt(), lResonance.Pt()); + } + if (bestJet.Pt() > 10.0 && bestJet.Pt() < 12.0) { + histos.fill(HIST("missed_kpi_INJets_10_12"), (bestJet.Pt() - missing_daughter.Pt()) / bestJet.Pt(), lResonance.Pt()); + } + if (bestJet.Pt() > 12.0 && bestJet.Pt() < 15.0) { + histos.fill(HIST("missed_kpi_INJets_12_15"), (bestJet.Pt() - missing_daughter.Pt()) / bestJet.Pt(), lResonance.Pt()); + } + if (bestJet.Pt() > 15.0 && bestJet.Pt() < 25.0) { + histos.fill(HIST("missed_kpi_INJets_15_25"), (bestJet.Pt() - missing_daughter.Pt()) / bestJet.Pt(), lResonance.Pt()); + } + if (bestJet.Pt() > 25.0) { + histos.fill(HIST("missed_kpi_INJets_25_infinite"), (bestJet.Pt() - missing_daughter.Pt()) / bestJet.Pt(), lResonance.Pt()); + } + if (bestJet.Pt() > 8.0) { + histos.fill(HIST("missed_kpi_INJets_8_infinite"), (bestJet.Pt() - missing_daughter.Pt()) / bestJet.Pt(), lResonance.Pt()); + } + histos.fill(HIST("JetMigration"), bestJet.Pt(), recoveredJet.Pt()); + } // Daughters out of jet + } // K*0 InJet + } // cfgJetdRHistos + } // mcParticles + }; + PROCESS_SWITCH(kstarInJets, processJetQA, "Process dR of K*0 Inclusive and Inside jet", false); + + //======================== + // + // Response matrix + // + //======================== + + using JetMCDTable = soa::Filtered>; + using JetMCPTable = soa::Filtered>; + + void processMatchedJet(aod::JetMcCollision const& collision, + soa::SmallGroups> const& recocolls, + JetMCPTable const& mcpjets, JetMCDTable const&, + JetTrackCandidatesMC const& tracks, TrackCandidatesMC const&, + aod::JetParticles const& mcParticles, aod::McParticles const&) + { + static int nMatched = 0; + if (cDebugLevel > 0) { + ++nMatched; + if (nMatched % 10000 == 0) { + LOG(info) << "Processed Matched test: " << nMatched; + } + } + + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 0.5); + } + if (std::fabs(collision.posZ()) > cfgEventVtxCut) { + return; + } + + if (recocolls.size() <= 0) { + return; + } + for (auto& recocoll : recocolls) { + if (!jetderiveddatautilities::selectCollision(recocoll, eventSelectionBits)) { + return; + } + } + + bool INELgt0 = false; + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.eta()) < cfgEventMaxEta) { + INELgt0 = true; + break; + } + } // mcParticles + if (!INELgt0) { + return; + } + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 1.5); + } + + // 1. Gen jet --> Reco jet + + std::vector mcd_pt{}; + std::vector mcd_phi{}; + std::vector mcd_eta{}; + + std::vector mcp_pt{}; + std::vector mcp_phi{}; + std::vector mcp_eta{}; + + bool hasJets = false; + for (const auto& mcpjet : mcpjets) { + if (!mcpjet.has_matchedJetGeo()) { + + if (cfgMatchedGenHistos) { + histos.fill(HIST("NoMatchedJets_pT"), mcpjet.pt()); + } + continue; + } + + for (const auto& mcdjet : mcpjet.template matchedJetGeo_as()) { + if (!mcdjet.has_matchedJetGeo()) { + continue; + } + hasJets = true; + + // QA + // double dPhi = TVector2::Phi_mpi_pi(mcdjet.phi() - mcpjet.phi()); + // double dEta = mcdjet.eta() - mcpjet.eta(); + // double dR = std::sqrt((dPhi * dPhi) + (dEta * dEta)); + + if (cfgMatchedGenHistos) { + histos.fill(HIST("matchedJets_pT"), mcpjet.pt(), mcdjet.pt() - mcpjet.pt()); + histos.fill(HIST("matchedJets_phi"), mcpjet.phi(), mcdjet.phi() - mcpjet.phi()); + histos.fill(HIST("matchedJets_eta"), mcpjet.eta(), mcdjet.eta() - mcpjet.eta()); + } + + mcd_pt.push_back(mcdjet.pt()); + mcd_phi.push_back(mcdjet.phi()); + mcd_eta.push_back(mcdjet.eta()); + + mcp_pt.push_back(mcpjet.pt()); + mcp_phi.push_back(mcpjet.phi()); + mcp_eta.push_back(mcpjet.eta()); + + } // mcdjet + } // mcpjet + + if (hasJets) { + if (cfgMatchedGenHistos) { + histos.fill(HIST("nEvents_MCGen_MATCHED"), 2.5); + } + } + + // 2. Find Gen K*0 + + // True K*0 + for (const auto& mcParticle : mcParticles) { + if (std::fabs(mcParticle.eta()) > cfgTrackMaxEta) { + continue; + } + if (std::fabs(mcParticle.pdgCode()) != Kstar0PDG) { + continue; + } + if (!mcParticle.has_daughters()) { + continue; + } + + // K*0 --> Kpi + int nKaon = 0; + int nPion = 0; + + int kaonIndex = -1; + int pionIndex = -1; + + double kaonPx = 0; + double kaonPy = 0; + double kaonPz = 0; + + double pionPx = 0; + double pionPy = 0; + double pionPz = 0; + + // Truth K*0 --> Keep the KPi Index + for (const auto& daughter : mcParticle.daughters_as()) { + + if (std::abs(daughter.pdgCode()) == KaonPDG) { + ++nKaon; + kaonIndex = daughter.globalIndex(); + } else if (std::abs(daughter.pdgCode()) == PionPDG) { + ++nPion; + pionIndex = daughter.globalIndex(); + } + } + if (nKaon != 1 || nPion != 1) { + continue; + } + + //======================= + // Track KPi --> Reco K*0 + + bool kaon_daughter = false; + bool pion_daughter = false; + + for (const auto& track : tracks) { + auto trk = track.track_as(); + + if (!trk.has_mcParticle()) { + continue; + } + if (!trackSelection(trk, true)) { + continue; + } + if (std::fabs(trk.eta()) > cfgTrackMaxEta) { + continue; + } + if (!trackPIDKaon(trk, false) || !trackPIDPion(trk, false)) { + continue; + } + + auto particle1 = trk.mcParticle(); + auto particle2 = trk.mcParticle(); + + if (particle1.globalIndex() == kaonIndex) { + kaonPx = trk.px(); + kaonPy = trk.py(); + kaonPz = trk.pz(); + kaon_daughter = true; + } + if (particle2.globalIndex() == pionIndex) { + pionPx = trk.px(); + pionPy = trk.py(); + pionPz = trk.pz(); + pion_daughter = true; + } + } // track + + if (!kaon_daughter || !pion_daughter) { + continue; + } + + // 3. Prepare Gen and Rec K*0 + + ROOT::Math::PxPyPzEVector lResonance = ROOT::Math::PxPyPzEVector(mcParticle.px(), mcParticle.py(), mcParticle.pz(), mcParticle.e()); + ROOT::Math::PxPyPzMVector lDecayKaon_Rec = ROOT::Math::PxPyPzMVector(kaonPx, kaonPy, kaonPz, massKa); + ROOT::Math::PxPyPzMVector lDecayPion_Rec = ROOT::Math::PxPyPzMVector(pionPx, pionPy, pionPz, massPi); + ROOT::Math::PxPyPzMVector lResonance_Rec = lDecayKaon_Rec + lDecayPion_Rec; + + // 4. Calculate dR between jet and K*0 + + int goodjets = 0; + bool jetFlag = false; + double jetpt_mcp = 0; + double jetpt_mcd = 0; + + for (std::vector::size_type i = 0; i < mcp_pt.size(); i++) { + double dphi = TVector2::Phi_mpi_pi(mcp_phi[i] - lResonance.Phi()); + double deta = mcp_eta[i] - lResonance.Eta(); + double dR = TMath::Sqrt((deta * deta) + (dphi * dphi)); + + if (dR < cfgJetdR) { + jetFlag = true; + jetpt_mcp = mcp_pt[i]; + jetpt_mcd = mcd_pt[i]; + + goodjets++; + } + } // mcp jet + + if (cfgSingleJet) { + if (goodjets > 1) { + jetpt_mcp = DistinguishJetsMC(mcp_pt, mcp_phi, mcp_eta, lResonance); + jetpt_mcd = DistinguishJetsMC(mcd_pt, mcd_phi, mcd_eta, lResonance_Rec); + } + } + + if (!jetFlag) { + continue; + } + + if (cfgMatchedGenHistos) { + unsigned int seed = static_cast(std::chrono::system_clock::now().time_since_epoch().count()); + int dice = rand_r(&seed) % 2; + + // for checking + if (dice > 0) { + // Train + histos.fill(HIST("RespGen_Matched_rand0"), lResonance_Rec.Pt(), jetpt_mcd, lResonance.Pt(), jetpt_mcp); + } else { + // Test + histos.fill(HIST("RespGen_Matched_rand1"), lResonance_Rec.Pt(), jetpt_mcd, lResonance.Pt(), jetpt_mcp); + } + + } // cfgMatchedGenHistos + + } // mcParticles + }; // processJetUnfolding + PROCESS_SWITCH(kstarInJets, processMatchedJet, "Process Unfolding of K*0 jets", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/kstarInOO.cxx b/PWGLF/Tasks/Resonances/kstarInOO.cxx deleted file mode 100644 index 2b0f71c60e6..00000000000 --- a/PWGLF/Tasks/Resonances/kstarInOO.cxx +++ /dev/null @@ -1,1659 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// \file kstarInOO.cxx -/// \brief the pT spectra of k*0(892) resonance analysis in OO collisions -/// \author Jimun Lee - -#include "PWGJE/Core/JetDerivedDataUtilities.h" -#include "PWGJE/DataModel/Jet.h" -#include "PWGJE/DataModel/JetReducedData.h" - -#include "Common/CCDB/EventSelectionParams.h" -#include "Common/DataModel/Centrality.h" -#include "Common/DataModel/EventSelection.h" -#include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTOF.h" -#include "Common/DataModel/PIDResponseTPC.h" -#include "Common/DataModel/TrackSelectionTables.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; - -struct kstarInOO { - SliceCache cache; - Preslice perCollision = aod::track::collisionId; - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - //================================== - //|| - //|| Selection - //|| - //================================== - - // Event Selection - Configurable cfgEventSelections{"cfgEventSelections", "sel8", "Set event selection"}; - Configurable cfgEventVtxCut{"cfgEventVtxCut", 10.0, "V_z cut selection"}; - ConfigurableAxis cfgCentAxis{"cfgCentAxis", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}, "Binning of the centrality axis"}; - Configurable cfgOccupancySel{"cfgOccupancySel", false, "Occupancy selection"}; - Configurable cfgOccupancyMax{"cfgOccupancyMax", 999999., "maximum occupancy of tracks in neighbouring collisions in a given time range"}; - Configurable cfgOccupancyMin{"cfgOccupancyMin", -100., "minimum occupancy of tracks in neighbouring collisions in a given time range"}; - - // Track Selection - // General - Configurable cfgTrackMinPt{"cfgTrackMinPt", 0.15, "set track min pT"}; - Configurable cfgTrackMaxEta{"cfgTrackMaxEta", 0.8, "set track max Eta"}; - Configurable cfgTrackMaxDCArToPVcut{"cfgTrackMaxDCArToPVcut", 0.5, "Track DCAr cut to PV Maximum"}; - Configurable cfgTrackMaxDCAzToPVcut{"cfgTrackMaxDCAzToPVcut", 2.0, "Track DCAz cut to PV Maximum"}; - Configurable cfgTrackGlobalSel{"cfgTrackGlobalSel", true, "Global track selection"}; - Configurable cfgTrackPrimaryTrack{"cfgTrackPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfgTrackConnectedToPV{"cfgTrackConnectedToPV", true, "PV contributor track selection"}; // PV Contriuibutor - Configurable cfgTrackGlobalWoDCATrack{"cfgTrackGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - // TPC - Configurable cfgTrackFindableTPCClusters{"cfgTrackFindableTPCClusters", 50, "nFindable TPC Clusters"}; - Configurable cfgTrackTPCCrossedRows{"cfgTrackTPCCrossedRows", 70, "nCrossed TPC Rows"}; - Configurable cfgTrackRowsOverFindable{"cfgTrackRowsOverFindable", 1.2, "nRowsOverFindable TPC CLusters"}; - Configurable cfgTrackTPCChi2{"cfgTrackTPCChi2", 4.0, "nTPC Chi2 per Cluster"}; - - // ITS - Configurable cfgTrackITSChi2{"cfgTrackITSChi2", 36.0, "nITS Chi2 per Cluster"}; - - // PID - Configurable cfgTrackTPCPID{"cfgTrackTPCPID", true, "Enables TPC PID"}; - Configurable cfgTrackTOFPID{"cfgTrackTOFPID", true, "Enables TOF PID"}; - Configurable cfgTrackSquarePIDCut{"cfgTrackSqurePIDCut", true, "Enables PID cut shape square switch"}; - Configurable cfgTrackCirclePIDCut{"cfgTrackCirclePIDCut", true, "Enables PID cut shape circle switch"}; - Configurable cfgTrackCircleValue{"cfgTrackCircleValue", 2, "Enables TOF TPC PID circle cut value"}; - Configurable cfgTrackTOFHard{"cfgTrackTOFHard", false, "Enables TOF Hard"}; - - Configurable cfgTrackTPCPIDnSig{"cfgTrackTPCPIDnSig", 4.0, "nTPC PID sigma"}; - Configurable cfgTrackTOFPIDnSig{"cfgTrackTOFPIDnSig", 4.0, "nTOF PID sigma"}; - Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; - - // Mixing - ConfigurableAxis cfgBinsMixMult{"cfgBinsCent", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0}, "Binning of the centrality axis"}; - ConfigurableAxis cfgBinsMixVtx{"cfgBinsMixVtx", {VARIABLE_WIDTH, -10.0f, -5.f, 0.f, 5.f, 10.f}, "Mixing bins - z-vertex"}; - Configurable cfgMixNMixedEvents{"cfgMixNMixedEvents", 10, "Number of mixed events per event"}; - Configurable cfgVtxMixCut{"cfgVtxMixCut", 10, "Vertex Mix Cut"}; - - // MCGen - Configurable cfgForceGenReco{"cfgForceGenReco", false, "Only consider events which are reconstructed (neglect event-loss)"}; - Configurable cfgReqMcEffPID{"cfgReqMcEffPID", false, "Request McEfficiency PID"}; - - // Pair - Configurable cfgMinvNBins{"cfgMinvNBins", 300, "Number of bins for Minv axis"}; - Configurable cfgMinvMin{"cfgMinvMin", 0.60, "Minimum Minv value"}; - Configurable cfgMinvMax{"cfgMinvMax", 1.20, "Maximum Minv value"}; - - // Histogram - ConfigurableAxis binsDCAz{"binsDCAz", {40, -0.2, 0.2}, ""}; - ConfigurableAxis binsDCAxy{"binsDCAxy", {40, -0.2, 0.2}, ""}; - Configurable cfgEventCutQA{"cfgEventCutsQA", false, "Enable Event QA Hists"}; - Configurable cfgTrackCutQA{"cfgTrackCutQA", false, "Enable Track QA Hists"}; - Configurable cfgJetQAHistos{"cfgJetQAHistos", false, "Enable Jet QA Histos"}; - - Configurable cfgMCHistos{"cfgMCHistos", false, "Enable MC Hists"}; - Configurable cfgMixedHistos{"cfgMixedHistos", false, "Enable Mixed Histos"}; - Configurable cfgJetHistos{"cfgJetHistos", false, "Enable Jet Histos"}; - Configurable cfgJetMCHistos{"cfgJetMCHistos", false, "Enable Jet MC Histos"}; - Configurable cfgCutonTrig{"cfgCutonTrig", false, "Enable Jet Cut on Trig"}; - - Configurable cfgManualEvSel{"cfgManualEvSel", false, "Enable Manual EvSel"}; - Configurable cfgJetEvSel{"cfgJetEvSel", false, "Enable Manual JetEvSel"}; - - //====================== - //|| - //|| JET - //|| - //====================== - Configurable cfgJetpT{"cfgJetpT", 8.0, "Set Jet pT minimum"}; - Configurable cfgJetR{"cfgJetR", 0.4, "Set Jet radius parameter"}; - Configurable cfgSingleJet{"cfgSingleJet", false, "Enforces strict phi-jet correspondance"}; - Configurable cfgReqJets{"cfgReqJets", false, "False: MB, True: Inside Jets"}; - Configurable cfgRealTriggerMasks{"cfgRealTriggerMasks", "", "possible JE Trigger masks: fJetChLowPt,fJetChHighPt,fTrackLowPt,fTrackHighPt,fJetD0ChLowPt,fJetD0ChHighPt,fJetLcChLowPt,fJetLcChHighPt,fEMCALReadout,fJetFullHighPt,fJetFullLowPt,fJetNeutralHighPt,fJetNeutralLowPt,fGammaVeryHighPtEMCAL,fGammaVeryHighPtDCAL,fGammaHighPtEMCAL,fGammaHighPtDCAL,fGammaLowPtEMCAL,fGammaLowPtDCAL,fGammaVeryLowPtEMCAL,fGammaVeryLowPtDCAL"}; - Configurable cfgTriggerMasksTest1{"cfgTriggerMasksTest1", "", "possible JE Trigger masks Test1"}; - Configurable cfgTriggerMasksTest2{"cfgTriggerMasksTest2", "", "possible JE Trigger masks Test2"}; - Configurable cfgTriggerMasksTest3{"cfgTriggerMasksTest3", "", "possible JE Trigger masks Test3"}; - - std::vector eventSelectionBits; - std::vector RealTriggerMaskBits; - std::vector triggerMaskBitsTest1; - std::vector triggerMaskBitsTest2; - std::vector triggerMaskBitsTest3; - // Main - void init(o2::framework::InitContext&) - { - // HISTOGRAMS - const AxisSpec axisEta{30, -1.5, +1.5, "#eta"}; - const AxisSpec axisPhi{200, -1, +7, "#phi"}; - const AxisSpec ptAxis = {200, 0, 20.0}; - const AxisSpec pidAxis = {120, -6, 6}; - const AxisSpec minvAxis = {cfgMinvNBins, cfgMinvMin, cfgMinvMax}; - const AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; - const AxisSpec axisDCAxy{binsDCAxy, "DCA_{XY}"}; - const AxisSpec dRAxis = {100, 0, 100}; - - if (cfgEventCutQA) { - histos.add("hEvent_Cut", "Number of event after cuts", kTH1D, {{13, -0.5, 12.5}}); - histos.add("hPosZ_BC", "hPosZ_BC", kTH1F, {{300, -15.0, 15.0}}); - histos.add("hPosZ_AC", "hPosZ_AC", kTH1F, {{300, -15.0, 15.0}}); - histos.add("hcentFT0C_BC", "centFT0C_BC", kTH1F, {{110, 0.0, 110.0}}); - histos.add("hcentFT0C_AC", "centFT0C_AC", kTH1F, {{110, 0.0, 110.0}}); - } - if (cfgTrackCutQA) { - histos.add("hDCArToPv_BC", "DCArToPv_BC", kTH1F, {axisDCAxy}); - histos.add("hDCAzToPv_BC", "DCAzToPv_BC", kTH1F, {axisDCAz}); - histos.add("hIsPrim_BC", "hIsPrim_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsGood_BC", "hIsGood_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsPrimCont_BC", "hIsPrimCont_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hFindableTPCClusters_BC", "hFindableTPCClusters_BC", kTH1F, {{200, 0, 200}}); - histos.add("hFindableTPCRows_BC", "hFindableTPCRows_BC", kTH1F, {{200, 0, 200}}); - histos.add("hClustersVsRows_BC", "hClustersVsRows_BC", kTH1F, {{200, 0, 2}}); - histos.add("hTPCChi2_BC", "hTPCChi2_BC", kTH1F, {{200, 0, 100}}); - histos.add("hITSChi2_BC", "hITSChi2_BC", kTH1F, {{200, 0, 100}}); - histos.add("QA_nSigma_pion_TPC_BC", "QA_nSigma_pion_TPC_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_pion_TOF_BC", "QA_nSigma_pion_TOF_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_pion_TPC_TOF_BC", "QA_pion_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TPC_BC", "QA_nSigma_kaon_TPC_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TOF_BC", "QA_nSigma_kaon_TOF_BC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_kaon_TPC_TOF_BC", "QA_kaon_TPC_TOF_BC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_track_pT_BC", "QA_track_pT_BC", kTH1F, {{13, 0.0, 13.0}}); - - histos.add("hDCArToPv_AC", "DCArToPv_AC", kTH1F, {axisDCAxy}); - histos.add("hDCAzToPv_AC", "DCAzToPv_AC", kTH1F, {axisDCAz}); - histos.add("hIsPrim_AC", "hIsPrim_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsGood_AC", "hIsGood_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsPrimCont_AC", "hIsPrimCont_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hFindableTPCClusters_AC", "hFindableTPCClusters_AC", kTH1F, {{200, 0, 200}}); - histos.add("hFindableTPCRows_AC", "hFindableTPCRows_AC", kTH1F, {{200, 0, 200}}); - histos.add("hClustersVsRows_AC", "hClustersVsRows_AC", kTH1F, {{200, 0, 2}}); - histos.add("hTPCChi2_AC", "hTPCChi2_AC", kTH1F, {{200, 0, 100}}); - histos.add("hITSChi2_AC", "hITSChi2_AC", kTH1F, {{200, 0, 100}}); - histos.add("QA_nSigma_pion_TPC_AC", "QA_nSigma_pion_TPC_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_pion_TOF_AC", "QA_nSigma_pion_TOF_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_pion_TPC_TOF_AC", "QA_pion_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TPC_AC", "QA_nSigma_kaon_TPC_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_nSigma_kaon_TOF_AC", "QA_nSigma_kaon_TOF_AC", {HistType::kTH2F, {ptAxis, pidAxis}}); - histos.add("QA_kaon_TPC_TOF_AC", "QA_kaon_TPC_TOF_AC", {HistType::kTH2F, {pidAxis, pidAxis}}); - histos.add("QA_track_pT_AC", "QA_track_pT_AC", kTH1F, {{13, 0.0, 13.0}}); - } - if (cfgJetQAHistos) { - histos.add("nTriggerQA", "nTriggerQA", kTH1F, {{8, 0.0, 8.0}}); - histos.add("nTriggerQA_GoodEv", "nTriggerQA_GoodEv", kTH1F, {{8, 0.0, 8.0}}); - histos.add("nTriggerQA_GoodTrig", "nTriggerQA_GoodTrig", kTH1F, {{8, 0.0, 8.0}}); - histos.add("nTriggerQA_GoodEvTrig", "nTriggerQA_GoodEvTrig", kTH1F, {{8, 0.0, 8.0}}); - - histos.add("JetpT", "Jet pT (GeV/c)", kTH1F, {{4000, 0., 200.}}); - histos.add("JetEta", "Jet Eta", kTH1F, {{100, -1.0, 1.0}}); - histos.add("JetPhi", "Jet Phi", kTH1F, {{80, -1.0, 7.0}}); - - histos.add("rawDimpT", "rawDimpT", kTH2F, {{1000, 0.0, 10.0}, {100, -0.5, 0.5}}); - histos.add("jetTrackEta", "Jet Track Eta", kTH1F, {{100, -1.0, 1.0}}); - histos.add("jetTrackPhi", "Jet Track Phi", kTH1F, {{80, -1.0, 7.0}}); - - histos.add("nJetsPerEvent", "nJetsPerEvent", kTH1I, {{4, -0.5, 3.5}}); - histos.add("nGoodJets", "nGoodJets", kTH1I, {{4, -0.5, 3.5}}); - } - - //////////////////////////////////// - histos.add("nEvents", "nEvents", kTH1F, {{7, 0.0, 7.0}}); - histos.add("hUSS_KPi", "hUSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hUSS_PiK", "hUSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hLSS_KPi", "hLSS_KPi", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hLSS_PiK", "hLSS_PiK", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - - if (cfgMixedHistos) { - histos.add("hUSS_KPi_Mix", "hUSS_KPi_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hUSS_PiK_Mix", "hUSS_PiK_Mix", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - } - if (cfgMCHistos) { - histos.add("nEvents_Gen", "nEvents_Gen", kTH1F, {{4, 0.0, 4.0}}); - histos.add("hUSS_TrueRec", "hUSS_TrueRec", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hGen_pT_Raw", "Gen_pT_Raw (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hGen_pT_GoodEv", "hGen_pT_GoodEv", kTHnSparseF, {cfgCentAxis, ptAxis}); - } - if (cfgJetHistos) { - histos.add("hUSS_KPi_INSIDE", "hUSS_KPi_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); - histos.add("hUSS_PiK_INSIDE", "hUSS_PiK_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); - histos.add("hLSS_KPi_INSIDE", "hLSS_KPi_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); - histos.add("hLSS_PiK_INSIDE", "hLSS_PiK_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); - } - if (cfgJetMCHistos) { - histos.add("nEvents_Gen", "nEvents_Gen", kTH1F, {{7, -.0, 7.0}}); - histos.add("hUSS_TrueRec", "hUSS_TrueRec", kTHnSparseF, {cfgCentAxis, ptAxis, minvAxis}); - histos.add("hUSS_TrueRec_INSIDE", "hUSS_TrueRec_INSIDE", kTHnSparseF, {cfgCentAxis, dRAxis, ptAxis, minvAxis}); - - histos.add("hGen_pT_Raw", "Gen_pT_Raw (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hGen_pT_GoodEv", "Gen_pT_GoodTrig (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hGen_pT_GoodTrig", "Gen_pT_GoodTrig (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hGen_pT_GoodEvTrig", "Gen_pT_GoodEvTrig (GeV/c)", kTH1F, {{800, 0., 40.}}); - - histos.add("hEffRec_pT", "EffRec_pT (GeV/c)", kTH1F, {{1600, 0., 80.}}); - histos.add("hEffRecTest1_pT", "EffRecTest1_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest2_pT", "EffRecTest2_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest3_pT", "EffRecTest3_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest4_pT", "EffRecTest4_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest5_pT", "EffRecTest5_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest6_pT", "EffRecTest6_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest7_pT", "EffRecTest7_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffRecTest8_pT", "EffRecTest8_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - histos.add("hEffGen_pT", "EffGen_pT (GeV/c)", kTH1F, {{800, 0., 40.}}); - - histos.add("hMotherPdg1", "hMotherPdg1", kTH1F, {{5000, 0., 5000.}}); - histos.add("hMotherPdg2", "hMotherPdg2", kTH1F, {{5000, 0., 5000.}}); - } - - std::shared_ptr hCutFlow = histos.get(HIST("hEvent_Cut")); - std::vector eventCutLabels = { - "All Events", - "sel8", - Form("|Vz| < %.1f", cfgEventVtxCut.value), - "kIsGoodZvtxFT0vsPV", - "kNoSameBunchPileup", - "kNoTimeFrameBorder", - "kNoITSROFrameBorder", - "kNoCollInTimeRangeStandard", - "kIsGoodITSLayersAll", - Form("Occupancy < %.0f", cfgOccupancyMax.value), - "All passed events"}; - for (size_t i = 0; i < eventCutLabels.size(); ++i) { - hCutFlow->GetXaxis()->SetBinLabel(i + 1, eventCutLabels[i].c_str()); - } - - // Jet - eventSelectionBits = jetderiveddatautilities::initialiseEventSelectionBits(static_cast(cfgEventSelections)); - RealTriggerMaskBits = jetderiveddatautilities::initialiseTriggerMaskBits(cfgRealTriggerMasks); - triggerMaskBitsTest1 = jetderiveddatautilities::initialiseTriggerMaskBits(cfgTriggerMasksTest1); - triggerMaskBitsTest2 = jetderiveddatautilities::initialiseTriggerMaskBits(cfgTriggerMasksTest2); - triggerMaskBitsTest3 = jetderiveddatautilities::initialiseTriggerMaskBits(cfgTriggerMasksTest3); - } // end of init - - //====================== - //|| For LF Analysis - //====================== - using EventCandidates = soa::Join; //, aod::CentFT0Ms, aod::CentFT0As - using EventCandidatesTrue = aod::McCollisions; - using TrackCandidates = soa::Join; - using TrackCandidatesMC = soa::Join; - - //============== - //|| For jets - //============== - Filter JEPosZFilter = nabs(aod::jcollision::posZ) < cfgEventVtxCut; - Filter JEMCPosZFilter = nabs(aod::jmccollision::posZ) < cfgEventVtxCut; - Filter jetCuts = aod::jet::pt > cfgJetpT&& aod::jet::r == nround(cfgJetR.node() * 100.0f); - - using JetTrackCandidates = soa::Join; - using JetTrackCandidatesMC = soa::Join; - - using JetFilteredJets = soa::Filtered>; - - // For Mixed Event - using BinningType = ColumnBinningPolicy; - - Partition kaon = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pion = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - Partition kaonMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfgTrackTPCPIDnSig); - Partition pionMC = !cfgTrackTPCPID || (nabs(aod::pidtpc::tpcNSigmaPi) <= cfgTrackTPCPIDnSig); - - double massKa = o2::constants::physics::MassKPlus; - double massPi = o2::constants::physics::MassPiMinus; - - //================================== - //|| - //|| Helper Templates - //|| - //================================== - template - void fillQA(const bool pass, const objType& obj, const int objecttype = 0) - { - if (objecttype == 1) { - if constexpr (requires { obj.posZ(); }) { - if (!pass) { - histos.fill(HIST("hPosZ_BC"), obj.posZ()); - histos.fill(HIST("hcentFT0C_BC"), obj.centFT0C()); - } else { - histos.fill(HIST("hPosZ_AC"), obj.posZ()); - histos.fill(HIST("hcentFT0C_AC"), obj.centFT0C()); - } - } - } // eventSelection histogram - if (objecttype == 2) { - if constexpr (requires { obj.posZ(); }) { - if (!pass) { - histos.fill(HIST("hPosZ_BC"), obj.posZ()); - histos.fill(HIST("hcentFT0C_BC"), obj.centFT0C()); - } else { - histos.fill(HIST("hPosZ_AC"), obj.posZ()); - histos.fill(HIST("hcentFT0C_AC"), obj.centFT0C()); - } - } - } // Jet eventSelection histogram - if constexpr (requires { obj.tpcCrossedRowsOverFindableCls(); }) { - if (objecttype == 3) { - if (!pass) { - histos.fill(HIST("hDCArToPv_BC"), obj.dcaXY()); - histos.fill(HIST("hDCAzToPv_BC"), obj.dcaZ()); - histos.fill(HIST("hIsPrim_BC"), obj.isPrimaryTrack()); - histos.fill(HIST("hIsGood_BC"), obj.isGlobalTrackWoDCA()); - histos.fill(HIST("hIsPrimCont_BC"), obj.isPVContributor()); - histos.fill(HIST("hFindableTPCClusters_BC"), obj.tpcNClsFindable()); - histos.fill(HIST("hFindableTPCRows_BC"), obj.tpcNClsCrossedRows()); - histos.fill(HIST("hClustersVsRows_BC"), obj.tpcCrossedRowsOverFindableCls()); - histos.fill(HIST("hTPCChi2_BC"), obj.tpcChi2NCl()); - histos.fill(HIST("hITSChi2_BC"), obj.itsChi2NCl()); - histos.fill(HIST("QA_track_pT_BC"), obj.pt()); - } else { - histos.fill(HIST("hDCArToPv_AC"), obj.dcaXY()); - histos.fill(HIST("hDCAzToPv_AC"), obj.dcaZ()); - histos.fill(HIST("hIsPrim_AC"), obj.isPrimaryTrack()); - histos.fill(HIST("hIsGood_AC"), obj.isGlobalTrackWoDCA()); - histos.fill(HIST("hIsPrimCont_AC"), obj.isPVContributor()); - histos.fill(HIST("hFindableTPCClusters_AC"), obj.tpcNClsFindable()); - histos.fill(HIST("hFindableTPCRows_AC"), obj.tpcNClsCrossedRows()); - histos.fill(HIST("hClustersVsRows_AC"), obj.tpcCrossedRowsOverFindableCls()); - histos.fill(HIST("hTPCChi2_AC"), obj.tpcChi2NCl()); - histos.fill(HIST("hITSChi2_AC"), obj.itsChi2NCl()); - histos.fill(HIST("QA_track_pT_AC"), obj.pt()); - } - } - } // trackSelection - if (objecttype == 4) { - if constexpr (requires { obj.pt(); }) { - if (!pass) { - histos.fill(HIST("QA_nSigma_kaon_TPC_BC"), obj.pt(), obj.tpcNSigmaKa()); - histos.fill(HIST("QA_nSigma_kaon_TOF_BC"), obj.pt(), obj.tofNSigmaKa()); - histos.fill(HIST("QA_kaon_TPC_TOF_BC"), obj.tpcNSigmaKa(), obj.tofNSigmaKa()); - } else { - histos.fill(HIST("QA_nSigma_kaon_TPC_AC"), obj.pt(), obj.tpcNSigmaKa()); - histos.fill(HIST("QA_nSigma_kaon_TOF_AC"), obj.pt(), obj.tofNSigmaKa()); - histos.fill(HIST("QA_kaon_TPC_TOF_AC"), obj.tpcNSigmaKa(), obj.tofNSigmaKa()); - } - } - } // kaon pid Selection - if (objecttype == 5) { - if constexpr (requires { obj.pt(); }) { - if (!pass) { - histos.fill(HIST("QA_nSigma_pion_TPC_BC"), obj.pt(), obj.tpcNSigmaPi()); - histos.fill(HIST("QA_nSigma_pion_TOF_BC"), obj.pt(), obj.tofNSigmaPi()); - histos.fill(HIST("QA_pion_TPC_TOF_BC"), obj.tpcNSigmaPi(), obj.tofNSigmaPi()); - } else { - histos.fill(HIST("QA_nSigma_pion_TPC_AC"), obj.pt(), obj.tpcNSigmaPi()); - histos.fill(HIST("QA_nSigma_pion_TOF_AC"), obj.pt(), obj.tofNSigmaPi()); - histos.fill(HIST("QA_pion_TPC_TOF_AC"), obj.tpcNSigmaPi(), obj.tofNSigmaPi()); - } - } - } // pion pid Selection - } // fill QA - - enum class objectType { MB, - Jets, - MBRecParticle, - JetsRecParticle }; - template - void fillMinv(objectType type, const TrackType& trk1, const TrackType& trk2, const ROOT::Math::PxPyPzMVector& lReso, double centrality, double jetpt, bool IsMix, bool flip) - { - double conjugate = trk1.sign() * trk2.sign(); - switch (type) { - case objectType::MB: - if (IsMix && cfgMixedHistos) { - if (conjugate < 0) { - if (!flip) - histos.fill(HIST("hUSS_KPi_Mix"), centrality, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hUSS_PiK_Mix"), centrality, lReso.Pt(), lReso.M()); - } - } else { - if (conjugate < 0) { - if (!flip) - histos.fill(HIST("hUSS_KPi"), centrality, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hUSS_PiK"), centrality, lReso.Pt(), lReso.M()); - } else if (conjugate > 0) { - if (!flip) - histos.fill(HIST("hLSS_KPi"), centrality, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hLSS_PiK"), centrality, lReso.Pt(), lReso.M()); - } - } - break; - - case objectType::Jets: - if (!IsMix && cfgJetHistos) { - if (conjugate < 0) { - if (!flip) - histos.fill(HIST("hUSS_KPi_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hUSS_PiK_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); - } else { - if (conjugate > 0) { - if (!flip) - histos.fill(HIST("hLSS_KPi_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); - else - histos.fill(HIST("hLSS_PiK_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); - } - } - } - break; - - case objectType::MBRecParticle: - if (cfgMCHistos) { - if (conjugate < 0) { - histos.fill(HIST("hUSS_TrueRec"), centrality, lReso.Pt(), lReso.M()); - } - } - break; - - case objectType::JetsRecParticle: - if (cfgJetMCHistos) { - if (conjugate < 0) { - histos.fill(HIST("hUSS_TrueRec_INSIDE"), centrality, jetpt, lReso.Pt(), lReso.M()); - } - } - break; - } // switch - } // fillMinv - //====================================================================== - - template - std::pair eventSelection(const EventType event, const bool QA) - { - if (cfgEventCutQA && QA) { - fillQA(false, event, 1); - histos.fill(HIST("hEvent_Cut"), 0); - } - - if (!event.sel8()) - return {false, 1}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 1); - } - if (std::abs(event.posZ()) > cfgEventVtxCut) - return {false, 2}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 2); - } - if (!event.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) - return {false, 3}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 3); - } - if (!event.selection_bit(aod::evsel::kNoSameBunchPileup)) - return {false, 4}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 4); - } - if (!event.selection_bit(aod::evsel::kNoTimeFrameBorder)) - return {false, 5}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 5); - } - if (!event.selection_bit(aod::evsel::kNoITSROFrameBorder)) - return {false, 6}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 6); - } - if (!event.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) - return {false, 7}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 7); - } - if (!event.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) - return {false, 8}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 8); - } - if (cfgOccupancySel && (event.trackOccupancyInTimeRange() > cfgOccupancyMax || event.trackOccupancyInTimeRange() < cfgOccupancyMin)) - return {false, 9}; - if (cfgEventCutQA && QA) { - histos.fill(HIST("hEvent_Cut"), 9); - } - - if (cfgEventCutQA && QA) { - fillQA(true, event, 1); - histos.fill(HIST("hEvent_Cut"), 10); - } - return {true, 11}; - }; - - template - std::pair JeteventSelection(const EventType event, const bool QA) - { - if (cfgEventCutQA && QA) { - fillQA(false, event, 2); - } - - if (!jetderiveddatautilities::selectCollision(event, eventSelectionBits)) { // sel8 - return {false, 1}; - } - if (std::abs(event.posZ()) > cfgEventVtxCut) - return {false, 2}; - - if (cfgEventCutQA && QA) { - fillQA(true, event, 2); - } - return {true, 8}; - }; - - template - bool trackSelection(const TracksType track, const bool QA) - { - if (cfgTrackCutQA && QA) { - fillQA(false, track, 3); - } - - if (cfgTrackGlobalSel && !track.isGlobalTrack()) - return false; - if (track.pt() < cfgTrackMinPt) - return false; - if (std::abs(track.eta()) > cfgTrackMaxEta) - return false; - if (std::abs(track.dcaXY()) > cfgTrackMaxDCArToPVcut) - return false; - if (std::abs(track.dcaZ()) > cfgTrackMaxDCAzToPVcut) - return false; - if (cfgTrackPrimaryTrack && !track.isPrimaryTrack()) - return false; - if (cfgTrackGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - if (cfgTrackFindableTPCClusters > 0 && track.tpcNClsFindable() < cfgTrackFindableTPCClusters) - return false; - if (track.tpcNClsCrossedRows() < cfgTrackTPCCrossedRows) - return false; - if (cfgTrackRowsOverFindable > 0 && track.tpcCrossedRowsOverFindableCls() > cfgTrackRowsOverFindable) - return false; - if (track.tpcChi2NCl() > cfgTrackTPCChi2) - return false; - if (track.itsChi2NCl() > cfgTrackITSChi2) - return false; - if (cfgTrackConnectedToPV && !track.isPVContributor()) - return false; - - if (cfgTrackCutQA && QA) { - fillQA(true, track, 3); - } - return true; - }; - - template - bool trackPIDKaon(const TrackPID& candidate, const bool QA) - { - double tpcpid = 0; - double tofpid = 0; - bool tpcPIDPassed{false}, tofPIDPassed{false}; - - if (cfgTrackCutQA && QA) { - fillQA(false, candidate, 4); - } - - // TPC - if (cfgTrackSquarePIDCut) { - if (std::abs(candidate.tpcNSigmaKa()) < cfgTrackTPCPIDnSig) - tpcPIDPassed = true; - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaKa()) < cfgTrackTOFPIDnSig) { - tofPIDPassed = true; - } - } else { - if (!cfgTrackTOFHard) { - tofPIDPassed = true; - } else { - tofPIDPassed = false; - } - } - } // end of square cut - if (cfgTrackCirclePIDCut) { - if (std::abs(candidate.tpcNSigmaKa()) < cfgTrackTPCPIDnSig) - tpcpid = std::abs(candidate.tpcNSigmaKa()); - tofpid = 0; - - if (candidate.hasTOF()) { - tofpid = std::abs(candidate.tofNSigmaKa()); - } else { - if (cfgTrackTOFHard) { - tofpid = 999; - } - } - if (tpcpid * tpcpid + tofpid * tofpid < cfgTrackCircleValue) { - tpcPIDPassed = true; - tofPIDPassed = true; - } - } // circular cut - - // TPC & TOF - if (tpcPIDPassed && tofPIDPassed) { - if (cfgTrackCutQA && QA) { - fillQA(true, candidate, 4); - } - return true; - } - return false; - } - - template - bool trackPIDPion(const TrackPID& candidate, const bool QA) - { - double tpcpid = 0; - double tofpid = 0; - bool tpcPIDPassed{false}, tofPIDPassed{false}; - - if (cfgTrackCutQA && QA) { - fillQA(false, candidate, 5); - } - - // TPC - if (cfgTrackSquarePIDCut) { - if (std::abs(candidate.tpcNSigmaPi()) < cfgTrackTPCPIDnSig) - tpcPIDPassed = true; - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaPi()) < cfgTrackTOFPIDnSig) { - tofPIDPassed = true; - } - } else { - if (!cfgTrackTOFHard) { - tofPIDPassed = true; - } else { - tofPIDPassed = false; - } - } - } // end of square cut - if (cfgTrackCirclePIDCut) { - if (std::abs(candidate.tpcNSigmaPi()) < cfgTrackTPCPIDnSig) - tpcpid = std::abs(candidate.tpcNSigmaPi()); - tofpid = 0; - - if (candidate.hasTOF()) { - tofpid = std::abs(candidate.tofNSigmaPi()); - } else { - if (cfgTrackTOFHard) { - tofpid = 999; - } - } - if (tpcpid * tpcpid + tofpid * tofpid < cfgTrackCircleValue) { - tpcPIDPassed = true; - tofPIDPassed = true; - } - } // circular cut - - // TPC & TOF - if (tpcPIDPassed && tofPIDPassed) { - if (cfgTrackCutQA && QA) { - fillQA(true, candidate, 5); - } - return true; - } - return false; - } - - template - ROOT::Math::PxPyPzMVector minvReconstruction(const TracksType& trk1, const TracksType& trk2, const bool QA, const bool flip) - { - if (!trackSelection(trk1, false) || !trackSelection(trk2, false)) - return {}; - - if (!trackPIDKaon(trk1, QA) || !trackPIDPion(trk2, QA)) - return {}; - - if (trk1.globalIndex() >= trk2.globalIndex()) - return {}; - - ROOT::Math::PxPyPzMVector lDecayDaughter1, lDecayDaughter2, lResonance; - if (!flip) { - lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); - lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); - } else { - lDecayDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massPi); - lDecayDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massKa); - } - lResonance = lDecayDaughter1 + lDecayDaughter2; - - if (std::abs(lResonance.Eta()) > cfgTrackMaxEta) - return {}; - return {lResonance}; - } - - template - ROOT::Math::PxPyPzMVector TrueReconstruction(const TracksType& trk1, const TracksType& trk2) - { - double conjugate = trk1.sign() * trk2.sign(); - if (conjugate > 0) - return {}; - - auto particle1 = trk1.mcParticle(); - auto particle2 = trk2.mcParticle(); - - if (!particle1.has_mothers() || !particle2.has_mothers()) { - return {}; - } - - std::vector mothers1{}; - std::vector mothers1PDG{}; - for (auto& particle1_mom : particle1.template mothers_as()) { - mothers1.push_back(particle1_mom.globalIndex()); - mothers1PDG.push_back(particle1_mom.pdgCode()); - } - - std::vector mothers2{}; - std::vector mothers2PDG{}; - for (auto& particle2_mom : particle2.template mothers_as()) { - mothers2.push_back(particle2_mom.globalIndex()); - mothers2PDG.push_back(particle2_mom.pdgCode()); - } - - if (mothers1PDG[0] != 313) - return {}; // mother not K*0 - if (mothers2PDG[0] != 313) - return {}; // mothers not K*0 - - if (mothers1[0] != mothers2[0]) - return {}; // Kaon and pion not from the same K*0 - - if (std::fabs(particle1.pdgCode()) != 211 && std::fabs(particle1.pdgCode()) != 321) - return {}; - if (std::fabs(particle2.pdgCode()) != 211 && std::fabs(particle2.pdgCode()) != 321) - return {}; - - double track1_mass, track2_mass; - if (std::fabs(particle1.pdgCode()) == 211) { - track1_mass = massPi; - } else { - track1_mass = massKa; - } - - if (std::fabs(particle2.pdgCode()) == 211) { - track2_mass = massPi; - } else { - track2_mass = massKa; - } - - if (track1_mass == track2_mass) { - return {}; - } - - ROOT::Math::PxPyPzMVector lTrueDaughter1, lTrueDaughter2, lTrueReso; - lTrueDaughter1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), track1_mass); - lTrueDaughter2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), track2_mass); - lTrueReso = lTrueDaughter1 + lTrueDaughter2; - - if (lTrueReso.M() < 0) - return {}; - - return {lTrueReso}; - } - - template - void TrackSlicing(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool IsMix, const bool QA) - { - auto tracks1 = kaon->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pion->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); - - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - for (bool flip : {false, true}) { - auto lReso = minvReconstruction(trk1, trk2, QA, flip); - if (lReso.M() < 0) - continue; - - fillMinv(objectType::MB, trk1, trk2, lReso, centrality, -1.0, IsMix, flip); - } // flip - } // for - } // TrackSlicing - - template - void TrackSlicingMC(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool IsMix, const bool QA) - { - auto tracks1 = kaonMC->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto tracks2 = pionMC->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = collision1.centFT0C(); - - for (const auto& [trk1, trk2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) - continue; - - for (bool flip : {false, true}) { - auto lReso = minvReconstruction(trk1, trk2, QA, flip); - if (lReso.M() < 0) - continue; - - fillMinv(objectType::MB, trk1, trk2, lReso, centrality, -1.0, IsMix, flip); - } // flip - - //============================ - //| True Reconstruction - //============================ - auto lTrueReso = TrueReconstruction(trk1, trk2); - fillMinv(objectType::MBRecParticle, trk1, trk2, lTrueReso, centrality, -1.0, IsMix, false); - } // tracks - } // TrackSlicingMC - - template - double DistinguishJets(const JetType& jets, ROOT::Math::PxPyPzMVector lResonance) - { - if (cDebugLevel > 0) - std::cout << "Finded multiple jets to the same phi." << std::endl; - - double bestR = 0; - double bestJetpT = 0; - for (auto const& jet : jets) { - double phidiff = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); - double etadiff = jet.eta() - lResonance.Eta(); - double R = TMath::Sqrt((phidiff * phidiff) + (etadiff * etadiff)); - if (R < cfgJetR && bestR == 0) { - bestR = R; - bestJetpT = jet.pt(); - } else if (R < bestR) { - bestR = R; - bestJetpT = jet.pt(); - } - } // jet - return bestJetpT; - } - - template - void JetTrackSlicing(aod::JetCollision const& collision, TracksType const& jetTracks, const JetType& chargedjets, const bool IsMix, const bool QA) - { - //============================ - //| MB: Track Reconstruction - //============================ - auto centrality = collision.centFT0C(); - for (const auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { - auto trk1 = track1.template track_as(); - auto trk2 = track2.template track_as(); - - for (bool flip : {false, true}) { - auto lResonance = minvReconstruction(trk1, trk2, QA, flip); - if (lResonance.M() < 0) - continue; - - fillMinv(objectType::MB, trk1, trk2, lResonance, centrality, -1.0, IsMix, flip); - - //============================== - //| Jets: Track Reconstruction - //============================== - bool jetFlag = false; - int goodjets = 0; - double jetpt = 0; - for (auto const& jet : chargedjets) { - double phidiff = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); - double etadiff = jet.eta() - lResonance.Eta(); - double R = TMath::Sqrt((etadiff * etadiff) + (phidiff * phidiff)); - if (R < cfgJetR) { - jetFlag = true; - jetpt = jet.pt(); - goodjets++; - } - } - if (cfgJetQAHistos) { - histos.fill(HIST("nGoodJets"), goodjets); - } - if (!cfgSingleJet) { - if (goodjets > 1) { - jetpt = DistinguishJets(chargedjets, lResonance); - } - } - - if (jetFlag) { - fillMinv(objectType::Jets, trk1, trk2, lResonance, centrality, jetpt, IsMix, flip); - } // jetFlag - } // flip - } // jetTracks - } // JetTrackSlicing - - template - void JetTrackSlicingMC(aod::JetCollision const& collision, TracksType const& jetTracks, const JetType& chargedjets, const bool IsMix, const bool QA) - { - //============================ - //| MB: Track Reconstruction - //============================ - auto centrality = collision.centFT0C(); - for (const auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { - auto trk1 = track1.template track_as(); - auto trk2 = track2.template track_as(); - - if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) - continue; - - for (bool flip : {false, true}) { - auto lResonance = minvReconstruction(trk1, trk2, QA, flip); - if (lResonance.M() < 0) - continue; - - fillMinv(objectType::MB, trk1, trk2, lResonance, centrality, -1.0, IsMix, flip); - - //============================== - //| Jets: Track Reconstruction - //============================== - bool jetFlag = false; - int goodjets = 0; - double jetpt = 0; - for (auto const& jet : chargedjets) { - double phidiff = TVector2::Phi_mpi_pi(jet.phi() - lResonance.Phi()); - double etadiff = jet.eta() - lResonance.Eta(); - double R = TMath::Sqrt((etadiff * etadiff) + (phidiff * phidiff)); - if (R < cfgJetR) { - jetFlag = true; - jetpt = jet.pt(); - goodjets++; - } - } - if (cfgJetQAHistos) { - histos.fill(HIST("nGoodJets"), goodjets); - } - if (!cfgSingleJet) { - if (goodjets > 1) { - jetpt = DistinguishJets(chargedjets, lResonance); - } - } - - if (jetFlag) { - fillMinv(objectType::Jets, trk1, trk2, lResonance, centrality, jetpt, IsMix, flip); - } // jetFlag - - //=============================== - //| MB: Particle Reconstruction - //=============================== - auto lTrueReso = TrueReconstruction(trk1, trk2); - fillMinv(objectType::MBRecParticle, trk1, trk2, lTrueReso, centrality, -1.0, IsMix, false); - - //================================== - //| Jets: Particle Reconstruction - //================================== - if (jetFlag) { - fillMinv(objectType::JetsRecParticle, trk1, trk2, lTrueReso, centrality, jetpt, IsMix, false); - } // jetFlag - } // filp - } // Tracks loop - } // JetTrackSlicingMC - - //======================================================= - //| - //| JET DATA STUFF - //| - //======================================================= - int nJetEvents = 0; - void processDataJets(o2::aod::JetCollision const& collision, JetFilteredJets const& chargedjets, JetTrackCandidates const& jetTracks, TrackCandidates const&) - { - if (cDebugLevel > 0) { - nJetEvents++; - if ((nJetEvents + 1) % 10000 == 0) { - std::cout << "Processed Jet Data Events: " << nJetEvents << std::endl; - } - } - - histos.fill(HIST("nEvents"), 0.5); // Raw event - - // Trigger before we start jet finding - if (cfgCutonTrig) { - bool RT = false; - bool VTtest1 = false; - bool VTtest2 = false; - bool VTtest3 = false; - if (jetderiveddatautilities::selectTrigger(collision, RealTriggerMaskBits)) { - RT = true; - if (cfgJetQAHistos) { - histos.fill(HIST("nTriggerQA"), 0.5); - } - } - if (jetderiveddatautilities::selectTrigger(collision, triggerMaskBitsTest1)) { - VTtest1 = true; - if (cfgJetQAHistos) { - histos.fill(HIST("nTriggerQA"), 1.5); - } - } - if (jetderiveddatautilities::selectTrigger(collision, triggerMaskBitsTest2)) { - VTtest2 = true; - if (cfgJetQAHistos) { - histos.fill(HIST("nTriggerQA"), 2.5); - } - } - if (jetderiveddatautilities::selectTrigger(collision, triggerMaskBitsTest3)) { - VTtest3 = true; - if (cfgJetQAHistos) { - histos.fill(HIST("nTriggerQA"), 3.5); - } - } - if (!RT) { - return; - } else if (RT && (VTtest1 || VTtest2 || VTtest3)) { - return; - } - } // Trigger cut - - histos.fill(HIST("nEvents"), 1.5); // Before passing the condition - - if (cfgManualEvSel) { - auto [goodEv, code] = JeteventSelection(collision, true); - if (!goodEv) - return; - } - - if (cfgJetEvSel) { - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { - return; - } - } - histos.fill(HIST("nEvents"), 2.5); // Events after event quality selection for Inclusive - - std::vector jetpT{}; - std::vector jetEta{}; - std::vector jetPhi{}; - bool HasJets = false; - int nJets = 0; - for (auto chargedjet : chargedjets) { - jetpT.push_back(chargedjet.pt()); - jetEta.push_back(chargedjet.eta()); - jetPhi.push_back(chargedjet.phi()); - nJets++; - if (cfgJetQAHistos) { - histos.fill(HIST("JetpT"), chargedjet.pt()); - histos.fill(HIST("JetEta"), chargedjet.eta()); - histos.fill(HIST("JetPhi"), chargedjet.phi()); - } - if (chargedjet.pt() > cfgJetpT) - HasJets = true; - } - if (cfgJetQAHistos) { - histos.fill(HIST("nJetsPerEvent"), nJets); - } - - //==================== - //|| Has Jets - //==================== - if (cfgReqJets) { - if (!HasJets) - return; - } - histos.fill(HIST("nEvents"), 3.5); // Has jets - - bool INELgt0 = false; - for (auto& jetTrack : jetTracks) { - auto originTrack = jetTrack.track_as(); - if (!trackSelection(originTrack, true)) - continue; - INELgt0 = true; - - if (cfgJetQAHistos) { - histos.fill(HIST("rawDimpT"), jetTrack.pt(), jetTrack.pt() - originTrack.pt()); - histos.fill(HIST("jetTrackEta"), jetTrack.eta()); - histos.fill(HIST("jetTrackPhi"), jetTrack.phi()); - } - } // jetTrack loop - if (!INELgt0) - return; - - JetTrackSlicing(collision, jetTracks, chargedjets, false, true); - - } // ProcessDataJets - PROCESS_SWITCH(kstarInOO, processDataJets, "process Data Jets", false); - - //======================================================= - //| - //| JET MC STUFF - //| - //======================================================= - int nJetMCEvents = 0; - void processMCJets(o2::aod::JetCollision const& collision, soa::Filtered const& mcdjets, JetTrackCandidatesMC const& jetTracks, TrackCandidatesMC const&, aod::McParticles const&) - { - if (cDebugLevel > 0) { - nJetMCEvents++; - if ((nJetMCEvents + 1) % 10000 == 0) { - std::cout << "Processed Jet MC Events: " << nJetMCEvents << std::endl; - } - } - histos.fill(HIST("nEvents"), 0.5); // Raw event - - if (std::abs(collision.posZ()) > cfgEventVtxCut) - return; - - if (!jetderiveddatautilities::selectTrigger(collision, RealTriggerMaskBits)) - return; - if (!jetderiveddatautilities::selectCollision(collision, eventSelectionBits)) { - return; - } - histos.fill(HIST("nEvents"), 1.5); // Before passing the condition - - bool INELgt0 = false; - for (auto& jetTrack : jetTracks) { - if (std::fabs(jetTrack.eta()) < cfgTrackMaxEta) { - INELgt0 = true; - break; - } - } // jetTrack loop - if (!INELgt0) - return; - - histos.fill(HIST("nEvents"), 2.5); // Events after event quality selection for Inclusive - - std::vector mcdjetpT{}; - std::vector mcdjetEta{}; - std::vector mcdjetPhi{}; - - bool HasJets = false; - int nJets = 0; - for (auto mcdjet : mcdjets) { - mcdjetpT.push_back(mcdjet.pt()); - mcdjetEta.push_back(mcdjet.eta()); - mcdjetPhi.push_back(mcdjet.phi()); - nJets++; - if (cfgJetQAHistos) { - histos.fill(HIST("JetpT"), mcdjet.pt()); - histos.fill(HIST("JetEta"), mcdjet.eta()); - histos.fill(HIST("JetPhi"), mcdjet.phi()); - } - if (mcdjet.pt() > cfgJetpT) { - HasJets = true; - } - } - if (cfgJetQAHistos) { - histos.fill(HIST("nJetsPerEvent"), nJets); - } - - //==================== - //|| Has Jets - //==================== - if (cfgReqJets) { - if (!HasJets) { - return; - } - } - histos.fill(HIST("nEvents"), 3.5); // Has jets - - // JetTrackSlicingMC(collision, jetTracks, chargedjets, false, true); - - for (auto& [track1, track2] : combinations(o2::soa::CombinationsUpperIndexPolicy(jetTracks, jetTracks))) { - auto trk1 = track1.track_as(); - auto trk2 = track2.track_as(); - - // Each section, test pT. which section we lost the entries - ROOT::Math::PxPyPzMVector lDecayDaughterTest1, lDecayDaughterTest2, lResonanceTest1; - lDecayDaughterTest1 = ROOT::Math::PxPyPzMVector(trk1.px(), trk1.py(), trk1.pz(), massKa); - lDecayDaughterTest2 = ROOT::Math::PxPyPzMVector(trk2.px(), trk2.py(), trk2.pz(), massPi); - lResonanceTest1 = lDecayDaughterTest1 + lDecayDaughterTest2; - - if (!trk1.has_mcParticle() || !trk2.has_mcParticle()) - continue; - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest1_pT"), lResonanceTest1.Pt()); - } - - if (!trackSelection(trk1, true) || !trackSelection(trk2, false)) - continue; - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest2_pT"), lResonanceTest1.Pt()); - } - - if (cfgReqMcEffPID) { - if (!trackPIDKaon(trk1, true) || !trackPIDPion(trk2, true)) - continue; - } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest3_pT"), lResonanceTest1.Pt()); - } - - auto particle1 = trk1.mcParticle(); - auto particle2 = trk2.mcParticle(); - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - if (!particle1.has_mothers() || !particle2.has_mothers()) { - continue; - } - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest4_pT"), lResonanceTest1.Pt()); - } - //////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////// - - std::vector mothers1{}; - std::vector mothers1PDG{}; - for (auto& particle1_mom : particle1.template mothers_as()) { - mothers1.push_back(particle1_mom.globalIndex()); - mothers1PDG.push_back(particle1_mom.pdgCode()); - } - - if (cfgJetMCHistos) { - histos.fill(HIST("hMotherPdg1"), std::fabs(mothers1PDG[0])); - } - std::vector mothers2{}; - std::vector mothers2PDG{}; - for (auto& particle2_mom : particle2.template mothers_as()) { - mothers2.push_back(particle2_mom.globalIndex()); - mothers2PDG.push_back(particle2_mom.pdgCode()); - } - if (cfgJetMCHistos) { - histos.fill(HIST("hMotherPdg2"), std::fabs(mothers2PDG[0])); - } - - if (mothers1[0] != mothers2[0]) - continue; // Kaon and pion not from the same K*0 - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest5_pT"), lResonanceTest1.Pt()); - } - - if (std::fabs(particle1.pdgCode()) != 321) // kaon - continue; - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest6_pT"), lResonanceTest1.Pt()); - } - - if (std::fabs(particle2.pdgCode()) != 211) // pion - continue; - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest7_pT"), lResonanceTest1.Pt()); - } - - if (std::fabs(mothers1PDG[0]) != 313) - continue; // mother not K*0 - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRecTest8_pT"), lResonanceTest1.Pt()); - } - - if (std::fabs(mothers2PDG[0]) != 313) - continue; // mothers not K*0 - - if (cfgJetMCHistos) { - histos.fill(HIST("hEffRec_pT"), lResonanceTest1.Pt()); - } - } // track loop - } // process loop - PROCESS_SWITCH(kstarInOO, processMCJets, "process MC Jets", false); - - //======================================================= - //| - //| DATA STUFF (SE) - //| - //======================================================= - int nEvents = 0; - void processDataSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) - { - if (cDebugLevel > 0) { - nEvents++; - if ((nEvents + 1) % 10000 == 0) { - std::cout << "Processed Data Events: " << nEvents << std::endl; - } - } - - auto [goodEv, code] = eventSelection(collision, true); - histos.fill(HIST("nEvents"), 0.5); - - if (!goodEv) - return; - - bool INELgt0 = false; - for (const auto& track : tracks) { - if (!trackSelection(track, true)) - continue; - if (std::fabs(track.eta()) < cfgTrackMaxEta) { - INELgt0 = true; - } - } - if (!INELgt0) - return; - histos.fill(HIST("nEvents"), 1.5); - - TrackSlicing(collision, tracks, collision, tracks, false, true); - - } // processSameEvents - PROCESS_SWITCH(kstarInOO, processDataSameEvent, "process Data Same Event", false); - - //======================================================= - //| - //| DATA STUFF (ME) - //| - //======================================================= - int nEventsMix = 0; - void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMix++; - if ((nEventsMix + 1) % 10000 == 0) { - std::cout << "Processed DATA Mixed Events : " << nEventsMix << std::endl; - } - } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - bool VtxMixFlag = false; - bool CentMixFlag = false; - // bool OccupanacyMixFlag = false; - if (std::fabs(collision1.posZ() - collision2.posZ()) <= cfgVtxMixCut) // set default to maybe 10 - VtxMixFlag = true; - if (std::fabs(collision1.centFT0C() - collision2.centFT0C()) <= cfgVtxMixCut) // set default to maybe 10 - CentMixFlag = true; - - if (!goodEv1 || !goodEv2) - continue; - if (!CentMixFlag) - continue; - if (!VtxMixFlag) - continue; - - TrackSlicing(collision1, tracks1, collision2, tracks2, true, false); - } - } - PROCESS_SWITCH(kstarInOO, processDataMixedEvent, "process DATA Mixed Event", false); - - //======================================================= - //| - //| MC STUFF (SE) - //| - //========================================================= - int nEventsMC = 0; - void processSameEventMC(EventCandidates::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) - { - if (cDebugLevel > 0) { - nEventsMC++; - if ((nEventsMC + 1) % 10000 == 0) { - double histmem = histos.getSize(); - std::cout << histmem << std::endl; - std::cout << "process_SameEvent_MC: " << nEventsMC << std::endl; - } - } - auto [goodEv, code] = eventSelection(collision, true); - - histos.fill(HIST("nEvents"), 0.5); - - if (!goodEv) - return; - - bool INELgt0 = false; - for (const auto& track : tracks) { - if (!trackSelection(track, true)) - continue; - if (std::fabs(track.eta()) < cfgTrackMaxEta) { - INELgt0 = true; - } - } - if (!INELgt0) - return; - - histos.fill(HIST("nEvents"), 1.5); - - TrackSlicingMC(collision, tracks, collision, tracks, false, true); - } // processSameEvents_MC - PROCESS_SWITCH(kstarInOO, processSameEventMC, "process Same Event MC", false); - - //======================================================= - //| - //| MC STUFF (ME) - //| - //======================================================= - int nEventsMCMix = 0; - void processMixedEventMC(EventCandidates const& collisions, TrackCandidatesMC const& tracks, aod::McParticles const&) - { - auto tracksTuple = std::make_tuple(tracks); - BinningType colBinning{{cfgBinsMixVtx, cfgBinsMixMult}, true}; // true is for 'ignore overflows' (true by default) - SameKindPair pairs{colBinning, cfgMixNMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - nEventsMCMix++; - if ((nEventsMCMix + 1) % 10000 == 0) { - std::cout << "Processed Mixed Events: " << nEventsMCMix << std::endl; - } - } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - if (!goodEv1 || !goodEv2) { - continue; - } - - TrackSlicingMC(collision1, tracks1, collision2, tracks2, true, false); - } // mixing - } // processMixedEvent_MC - PROCESS_SWITCH(kstarInOO, processMixedEventMC, "process Mixed Event MC", false); - - //====================================== - //| - //| GENERATED STUFF - //| - //====================================== - int nEventsGen = 0; - void processGen(EventCandidatesTrue::iterator const& collision, soa::SmallGroups> const& recocolls, aod::McParticles const& mcParticles) - { - if (cDebugLevel > 0) { - ++nEventsGen; - if (nEventsGen % 10000 == 0) { - std::cout << "Processed MC (GEN) Events: " << nEventsGen << std::endl; - } - } - //======================= - //|| Event & Signal loss - //======================= - if (cfgMCHistos) { - histos.fill(HIST("nEvents_Gen"), 0.5); - } - - for (auto& particle : mcParticles) { - if (particle.pdgCode() != 313) - continue; - if (std::fabs(particle.eta()) > cfgTrackMaxEta) - continue; - if (fabs(collision.posZ()) > cfgEventVtxCut) - break; - - if (cfgMCHistos) { - histos.fill(HIST("hGen_pT_Raw"), particle.pt()); - } - } // Unreconstructed collisions(=Raw coll) for correction - - if (recocolls.size() <= 0) { // not reconstructed - if (cfgForceGenReco) { - return; - } - } - double centrality = -1; - for (auto& recocoll : recocolls) { - centrality = recocoll.centFT0C(); - auto [goodEv, code] = eventSelection(recocoll, true); - - if (cfgMCHistos) { - histos.fill(HIST("nEvents_Gen"), 1.5); - } - if (!goodEv) - continue; - } // recocolls (=reconstructed collisions) - - //================= - //|| Efficiency - //================= - for (auto& particle : mcParticles) { - if (particle.pdgCode() != 313) - continue; // Not K*0 - if (std::fabs(particle.eta()) > cfgTrackMaxEta) - continue; - - if (cfgMCHistos) { - histos.fill(HIST("nEvents_Gen"), 2.5); - histos.fill(HIST("hGen_pT_GoodEv"), centrality, particle.pt()); - } // cfgMCHistos - } // loop over particles - } // processMCTrue - PROCESS_SWITCH(kstarInOO, processGen, "process Generated Particles", false); - - //============================================== - //| - //| GENERATED STUFF (INCLUSIVE & JETS) - //| - //============================================== - int nprocessGenEvents = 0; - void processJetsGen(o2::aod::JetMcCollision const& collision, soa::SmallGroups> const& recocolls, aod::JetParticles const& mcParticles) - { - if (cDebugLevel > 0) { - ++nprocessGenEvents; - if (nprocessGenEvents % 10000 == 0) { - std::cout << "Processed MC (GEN) Events: " << nprocessGenEvents << std::endl; - } - } - //======================= - //|| Event & Signal loss - //======================= - if (cfgJetMCHistos) { - histos.fill(HIST("nEvents_Gen"), 0.5); - } - - for (auto& particle : mcParticles) { - if (particle.pdgCode() != 313) - continue; - if (std::fabs(particle.eta()) > cfgTrackMaxEta) - continue; - if (fabs(collision.posZ()) > cfgEventVtxCut) - break; - - if (cfgJetMCHistos) { - histos.fill(HIST("hGen_pT_Raw"), particle.pt()); - } - } // Unrecon. collision(=Raw coll) for correction - - if (recocolls.size() <= 0) { // not reconstructed - return; - } - - for (auto& recocoll : recocolls) { // poorly reconstructed - if (recocoll.posZ() > cfgEventVtxCut) - continue; - auto goodEv = jetderiveddatautilities::selectCollision(recocoll, eventSelectionBits); - auto goodTrig = jetderiveddatautilities::selectTrigger(recocoll, RealTriggerMaskBits); - for (auto& particle : mcParticles) { - if (particle.pdgCode() != 313) - continue; - if (std::fabs(particle.eta()) > cfgTrackMaxEta) - continue; - if (cfgJetMCHistos) { - // check K* PID - if (goodEv) { - histos.fill(HIST("hGen_pT_GoodEv"), particle.pt()); - - } // goodEv - - if (goodTrig) { - histos.fill(HIST("hGen_pT_GoodTrig"), particle.pt()); - - } // goodTrig - - if (goodEv && goodTrig) { - histos.fill(HIST("hGen_pT_GoodEvTrig"), particle.pt()); - - if (cfgJetQAHistos) { - histos.fill(HIST("nTriggerQA"), 7.5); - } - - } // goodEvTrig - } // cfgJetMCHistos - } // mcParticles - } // recocolls (=reconstructed collisions) - - //================= - //|| Efficiency - //================= - // if (fabs(collision.posZ()) > cfgEventVtxCut) - // return; - - for (auto& recocoll : recocolls) { // poorly reconstructed - auto goodEv = jetderiveddatautilities::selectCollision(recocoll, eventSelectionBits); - if (goodEv) { - goodEv = jetderiveddatautilities::selectTrigger(recocoll, RealTriggerMaskBits); - } - if (cfgJetMCHistos) { - histos.fill(HIST("nEvents_Gen"), 1.5); - } - if (!goodEv) - return; - } - if (cfgJetMCHistos) { - histos.fill(HIST("nEvents_Gen"), 2.5); - } - - for (auto& particle : mcParticles) { - if (particle.pdgCode() != 313) - continue; - if (std::fabs(particle.eta()) > cfgTrackMaxEta) - continue; - if (particle.pt() < cfgTrackMinPt) - continue; - - /* // Not Yet - if (cfg_Force_BR) { - bool baddecay = false; - for (auto& phidaughter : particle.daughters_as()) { - if (std::fabs(phidaughter.pdgCode()) != 321) { - baddecay = true; - break; - } - if (cfg_Force_Kaon_Acceptence) { - if (std::fabs(phidaughter.eta()) > cfg_Track_MaxEta) { - baddecay = true; - break; - } - } - } // loop over daughters - - if (baddecay) - continue; - } // enforce BR restriction - */ - - if (cfgJetMCHistos) { - histos.fill(HIST("nEvents_Gen"), 3.5); - histos.fill(HIST("hEffGen_pT"), particle.pt()); - } // cfgJetMCHistos - } // loop over particles - } // end of process - PROCESS_SWITCH(kstarInOO, processJetsGen, "Process Generated Particles Inclusive&Jets", false); - - void processEventsDummy(EventCandidates::iterator const&, TrackCandidates const&) - { - return; - } - PROCESS_SWITCH(kstarInOO, processEventsDummy, "dummy", false); -}; // kstarInOO -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -}; diff --git a/PWGLF/Tasks/Resonances/kstarpbpb.cxx b/PWGLF/Tasks/Resonances/kstarpbpb.cxx index 851ee73a728..46c1a5ba640 100644 --- a/PWGLF/Tasks/Resonances/kstarpbpb.cxx +++ b/PWGLF/Tasks/Resonances/kstarpbpb.cxx @@ -8,13 +8,16 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -// sourav.kundu@cern.ch , sarjeeta.gami@cern.ch + +/// \file kstarpbpb.cxx +/// \brief Code for K*(892)^0 resonance flow and spin alignment analysis +/// \author sourav.kundu@cern.ch , sarjeeta.gami@cern.ch +/// #include "PWGLF/DataModel/EPCalibrationTables.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" -#include "Common/CCDB/TriggerAliases.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -24,6 +27,7 @@ #include #include +#include #include #include #include @@ -35,6 +39,7 @@ #include #include #include +#include #include #include @@ -42,10 +47,12 @@ #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include +#include #include -#include +#include +#include +#include #include #include #include @@ -59,15 +66,15 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::aod::rctsel; -struct kstarpbpb { +struct Kstarpbpb { struct : ConfigurableGroup { Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; } cfgCcdbParam; // Enable access to the CCDB for the offset and correction constants and save them in dedicated variables. - Service ccdb; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; // Service pdg; struct RCTCut : ConfigurableGroup { @@ -93,16 +100,14 @@ struct kstarpbpb { Configurable cfgCutCentrality{"cfgCutCentrality", 80.0f, "Accepted maximum Centrality"}; // track Configurable cfgCutCharge{"cfgCutCharge", 0.0, "cut on Charge"}; - Configurable additionalEvSel2{"additionalEvSel2", true, "Additional evsel2"}; - Configurable additionalEvSel3{"additionalEvSel3", true, "Additional evsel3"}; Configurable cfgCutPT{"cfgCutPT", 0.2, "PT cut on daughter track"}; Configurable cfgCutEta{"cfgCutEta", 0.8, "Eta cut on daughter track"}; Configurable cfgCutDCAxy{"cfgCutDCAxy", 2.0f, "DCAxy range for tracks"}; Configurable cfgCutDCAz{"cfgCutDCAz", 2.0f, "DCAz range for tracks"}; Configurable useGlobalTrack{"useGlobalTrack", true, "use Global track"}; Configurable usepolar{"usepolar", true, "flag to fill type of SA"}; - Configurable nsigmaCutTOF{"nsigmacutTOF", 3.0, "Value of the TOF Nsigma cut"}; - Configurable nsigmaCutTPC{"nsigmacutTPC", 3.0, "Value of the TPC Nsigma cut"}; + Configurable nsigmaCutTOF{"nsigmaCutTOF", 3.0, "Value of the TOF Nsigma cut"}; + Configurable nsigmaCutTPC{"nsigmaCutTPC", 3.0, "Value of the TPC Nsigma cut"}; Configurable isTOFOnly{"isTOFOnly", false, "use TOF only PID"}; Configurable nsigmaCutCombined{"nsigmaCutCombined", 3.0, "Value of the TOF Nsigma cut"}; Configurable cfgNoMixedEvents{"cfgNoMixedEvents", 1, "Number of mixed events per event"}; @@ -113,50 +118,144 @@ struct kstarpbpb { ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0, 10.}, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {8, 0., 80}, "Centrality"}; ConfigurableAxis configrapAxis{"configrapAxis", {VARIABLE_WIDTH, -0.8, -0.4, 0.4, 0.8}, "Rapidity"}; - Configurable removefaketrak{"removefaketrack", true, "Remove fake track from momentum difference"}; - Configurable ConfFakeKaonCut{"ConfFakeKaonCut", 0.1, "Cut based on track from momentum difference"}; + Configurable confFakeKaonCut{"confFakeKaonCut", 0.1, "Cut based on track from momentum difference"}; ConfigurableAxis configThnAxisV2{"configThnAxisV2", {400, -16, 16}, "V2"}; - Configurable additionalEvsel{"additionalEvsel", false, "Additional event selcection"}; - Configurable timFrameEvsel{"timFrameEvsel", false, "TPC Time frame boundary cut"}; - Configurable additionalEvselITS{"additionalEvselITS", true, "Additional event selcection for ITS"}; - Configurable ispTdepPID{"ispTdepPID", true, "pT dependent PID"}; Configurable isNoTOF{"isNoTOF", true, "isNoTOF"}; - Configurable PDGcheck{"PDGcheck", true, "PDGcheck"}; + Configurable pdgcheck{"pdgcheck", true, "pdgcheck"}; Configurable strategyPID{"strategyPID", 2, "PID strategy"}; Configurable cfgCutTOFBeta{"cfgCutTOFBeta", 0.0, "cut TOF beta"}; Configurable additionalQAplots{"additionalQAplots", true, "Additional QA plots"}; Configurable additionalQAplots1{"additionalQAplots1", true, "Additional QA plots"}; - Configurable confMinRot{"confMinRot", 5.0 * TMath::Pi() / 6.0, "Minimum of rotation"}; - Configurable confMaxRot{"confMaxRot", 7.0 * TMath::Pi() / 6.0, "Maximum of rotation"}; + Configurable confMinRot{"confMinRot", 5.0 * o2::constants::math::PI / 6.0, "Minimum of rotation"}; + Configurable confMaxRot{"confMaxRot", 7.0 * o2::constants::math::PI / 6.0, "Maximum of rotation"}; Configurable nBkgRotations{"nBkgRotations", 9, "Number of rotated copies (background) per each original candidate"}; Configurable fillRotation{"fillRotation", true, "fill rotation"}; - Configurable same{"same", true, "same event"}; - Configurable like{"like", false, "like-sign"}; Configurable fillSA{"fillSA", true, "same event SA"}; - Configurable fillOccupancy{"fillOccupancy", false, "fill Occupancy"}; - Configurable cfgOccupancyCut{"cfgOccupancyCut", 500, "Occupancy cut"}; + Configurable doClosureTest{"doClosureTest", false, "Fill dedicated MC closure histograms for spin alignment"}; + Configurable fillLikeSignSA{"fillLikeSignSA", false, "fill the same-event like-sign SA sparse (needs fillSA)"}; Configurable useWeight{"useWeight", false, "use EP dep effi weight"}; Configurable useSP{"useSP", false, "use SP"}; + // spin-alignment quantization axis + Configurable cfgSAFrame{"cfgSAFrame", 0, "SA quantization axis: 0 = event plane (FT0C), 1 = production plane, 2 = random event plane"}; + Configurable cfgRndSeed{"cfgRndSeed", 0, "Seed for random event plane (0 = unique seed per job)"}; + Configurable cfgEPNormalHarmonic{"cfgEPNormalHarmonic", 1.0f, "EP normal = (sin(n*Psi), -cos(n*Psi), 0); n = 1 is the normal to the Psi2 plane (default), n = 2 is the old behaviour (axis at 2*Psi2 - pi/2, not the normal; the R2 resolution correction does not apply)"}; + // phi(1020) -> K+K- reflection veto for K* (off by default: the task then behaves exactly as before) + Configurable cfgPhiVeto{"cfgPhiVeto", false, "Reject K pi pairs whose KK mass (pion candidate given the kaon mass) is near the phi(1020) mass"}; + Configurable cfgPhiVetoWindow{"cfgPhiVetoWindow", 0.010f, "Half-width of the phi(1020) veto window (GeV/c^2)"}; + + // event loss and signal loss for K* and phi(1020) (processEvtLossSigLossMC), adapted from processEvtLossSigLossMC1 in phianalysisrun3pbpb.cxx + struct : ConfigurableGroup { + std::string prefix = "evtSigLoss"; + Configurable cutVzGen{"cutVzGen", true, "Cut |vz| < cfgCutVertex on the generated collision"}; + Configurable isApplyInelgt0{"isApplyInelgt0", false, "Require INEL > 0 for the generated collision"}; + Configurable isApplyTVX{"isApplyTVX", false, "Require TVX (FT0A and FT0C MC multiplicity > 0) for the generated collision"}; + Configurable requireSingleReco{"requireSingleReco", true, "Accepted MC event: exactly one reconstructed collision passing the selection, as in processMC / processMCPhi (false: at least one, as in phianalysisrun3pbpb.cxx)"}; + ConfigurableAxis axisImpactPar{"axisImpactPar", {200, 0.0, 20.0}, "Impact parameter (fm)"}; + ConfigurableAxis axisMultMC{"axisMultMC", {500, 0.0, 2000.0}, "Generated charged particles in |eta| < 0.5"}; + } evtSigLoss; + + // phi(1020) spin alignment: event selection is the K* one; track and PID selections identical to PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx + // (JSON keys follow the O2 linter naming rules) + struct : ConfigurableGroup { + std::string prefix = "phiSA"; + Configurable removeFakeTrack{"removeFakeTrack", true, "Remove fake track from momentum difference"}; + Configurable confFakeKaonCut{"confFakeKaonCut", 0.1, "Cut based on track from momentum difference"}; + // track selection (phianalysisrun3pbpb.cxx) + Configurable iscustomDCAcut{"iscustomDCAcut", false, "Global track (with DCA) + PV contributor + ITS clusters"}; + Configurable ismanualDCAcut{"ismanualDCAcut", true, "Global track w/o DCA + PV contributor + manual DCAxy/DCAz + ITS clusters"}; + Configurable cfgCutDCAxy{"cfgCutDCAxy", 2.0f, "DCAxy range for kaon tracks (manual DCA cut)"}; + Configurable cfgCutDCAz{"cfgCutDCAz", 2.0f, "DCAz range for kaon tracks (manual DCA cut)"}; + Configurable cfgITScluster{"cfgITScluster", 0, "Number of ITS cluster"}; + // PID (phianalysisrun3pbpb.cxx) + Configurable nsigmaCutTPC{"nsigmaCutTPC", 3.0, "Value of the TPC Nsigma cut"}; + Configurable nsigmaCutCombined{"nsigmaCutCombined", 3.0, "Value of the combined TPC-TOF Nsigma cut"}; + Configurable isNoTOF{"isNoTOF", false, "TPC-only PID for all tracks"}; + Configurable isDeepAngle{"isDeepAngle", false, "Deep Angle cut"}; + Configurable cfgDeepAngle{"cfgDeepAngle", 0.04, "Deep Angle cut value"}; + Configurable confRapidity{"confRapidity", 0.5, "Rapidity cut"}; + Configurable genacceptancecut{"genacceptancecut", true, "use acceptance cut for generated"}; + Configurable avoidsplitrackMC{"avoidsplitrackMC", false, "avoid split track in MC"}; + // same-event backgrounds, as for K* (off by default: processSEPhi then behaves exactly as before) + Configurable fillRotation{"fillRotation", false, "Fill the same-event rotated K+K- background (K+ rotated, with confMinRot, confMaxRot and nBkgRotations as for K*)"}; + Configurable fillLikeSign{"fillLikeSign", false, "Fill the same-event like-sign (K+K+ and K-K-) background"}; + ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {120, 0.98, 1.1}, "#it{M} (GeV/#it{c}^{2})"}; + ConfigurableAxis configThnAxisPt{"configThnAxisPt", {100, 0.0, 10.}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis configThnAxisCosThetaStar{"configThnAxisCosThetaStar", {10, -1.0, 1.}, "cos(#vartheta)"}; + ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {8, 0., 80}, "Centrality"}; + ConfigurableAxis configThnAxisRapidity{"configThnAxisRapidity", {8, 0, 0.8}, "Rapidity"}; + ConfigurableAxis configThnAxisSA{"configThnAxisSA", {200, -1, 1}, "SA"}; + } phiSA; + + // MC closure test of the cos(theta*) chain (processMCClosure, processMCClosureME, processMCClosurePhi, processMCClosureMEPhi) + // Step 1: SE unlike-sign minus normalised ME (same cuts as data) -> raw yield vs cos(theta*) + // Step 2: raw yield compared with the true reconstructed yield (hSparseRecTrue) + // Step 3: raw yield / (Acc x Eff)(cos theta*), with Acc x Eff = hSparseRecTrueEff / hSparseGenEff + // Step 4: corrected yield and rho00 compared with the generated input (hSparseGen) + struct : ConfigurableGroup { + std::string prefix = "closure"; + Configurable injectPolarization{"injectPolarization", false, "Reweight the true K*/phi with W(cos theta*) so that the sample has rho00 = rho00Inj"}; + Configurable rho00Inj{"rho00Inj", 1.0f / 3.0f, "Injected rho00, in [0,1] (1/3 = unpolarized)"}; + Configurable useTrueEP{"useTrueEP", true, "Use the MC true event plane angle as Psi (false: Psi = 0, fixed axis as in processMC with mcEffEP.psiSource = 0)"}; + Configurable splitSample{"splitSample", true, "Even MC events build Acc x Eff, odd MC events are the test sample (false: same events for both)"}; + Configurable fillRotBkg{"fillRotBkg", false, "Fill the rotated-pair background (weight 1), cross-check of the ME background"}; + Configurable genAcceptanceCut{"genAcceptanceCut", false, "Daughter pT/eta cut on the generated K*/phi (false: denominator is the full |y| < cut sample, so the correction is the full Acc x Eff in cos theta*)"}; + Configurable requirePrimaryTracks{"requirePrimaryTracks", false, "SE, ME and rotated pairs only from tracks matched to physical primaries (as in phianalysisrun3pbpb / kstar892LightIon); false: all tracks, as in data"}; + Configurable meRequireSingleReco{"meRequireSingleReco", true, "ME: both events must come from an MC collision with exactly one reco collision, as required in the SE (processMCClosure / processMCClosurePhi)"}; + ConfigurableAxis axisCosThetaStar{"axisCosThetaStar", {10, -1.0, 1.0}, "cos(#vartheta*)"}; + } closure; + + // Psi of the EP-frame MC efficiency (processMC, processMCPhi). In data the axis follows Psi_FT0C, which is flat in the lab; + // with a fixed Psi = 0 the axis is fixed in the lab, so azimuthal holes of the detector enter Acc x Eff(cos theta*). + // psiSource = 1 follows the thesis (S. Kundu, App. B.10.4): Psi2 from the generated tracks in the forward EP-detector acceptance + // (V0C there, FT0C here), one Psi per MC event, for both the generated and the reconstructed K*/phi. + struct : ConfigurableGroup { + std::string prefix = "mcEffEP"; + Configurable psiSource{"psiSource", 1, "Psi of the EP-frame MC efficiency: 0 = fixed Psi = 0 (old: axis -y for K*, in-plane x for phi), 1 = Psi2 from generated charged primaries in [etaMin, etaMax] (thesis), 2 = MC true event plane (mcCollision.eventPlaneAngle())"}; + Configurable etaMin{"etaMin", -3.3f, "eta min of the generated-track Q2 vector (FT0C acceptance)"}; + Configurable etaMax{"etaMax", -2.1f, "eta max of the generated-track Q2 vector (FT0C acceptance)"}; + Configurable fillPhiVMinusPsi{"fillPhiVMinusPsi", false, "Fill gen/rec Acc x Eff maps vs pT, cos theta* and phi_V - Psi (thesis Fig. B.69 / Table B.1 check)"}; + ConfigurableAxis axisPhiVMinusPsi{"axisPhiVMinusPsi", {6, 0.0, o2::constants::math::PI}, "#varphi_{V} - #Psi"}; + } mcEffEP; + Configurable cfgMinTrackPt{"cfgMinTrackPt", 0.15f, + "Minimum track pT"}; + + Configurable cfgMaxTrackPt{"cfgMaxTrackPt", 10.0f, + "Maximum track pT"}; Configurable genacceptancecut{"genacceptancecut", true, "use acceptance cut for generated"}; Configurable avoidsplitrackMC{"avoidsplitrackMC", false, "avoid split track in MC"}; - Configurable ConfWeightPath{"ConfWeightPath", "Users/s/skundu/My/Object/fitweight", "Path to gain calibration"}; + Configurable additionalEvSel1{"additionalEvSel1", true, "Additional evsel1"}; + Configurable additionalEvSel2{"additionalEvSel2", true, "Additional evsel2"}; + Configurable additionalEvSel3{"additionalEvSel3", true, "Additional evsel3"}; + Configurable additionalEvSel4{"additionalEvSel4", true, "Additional evsel4"}; + Configurable confWeightPath{"confWeightPath", "Users/s/skundu/My/Object/fitweight", "Path to gain calibration"}; ConfigurableAxis axisPtKaonWeight{"axisPtKaonWeight", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f}, "pt axis"}; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter centralityFilter = nabs(aod::cent::centFT0C) < cfgCutCentrality; Filter acceptanceFilter = (nabs(aod::track::eta) < cfgCutEta && nabs(aod::track::pt) > cfgCutPT); - Filter DCAcutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); + Filter dcacutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); using EventCandidates = soa::Filtered>; using TrackCandidates = soa::Filtered>; using CollisionMCTrueTable = aod::McCollisions; + using McCollisionMults = soa::Join; using TrackMCTrueTable = aod::McParticles; using CollisionMCRecTableCentFT0C = soa::SmallGroups>; using TrackMCRecTable = soa::Join; using FilTrackMCRecTable = soa::Filtered; Preslice perCollision = aod::track::collisionId; + // reconstructed MC collisions for the closure: adds EPCalibrationTables (psiFT0C etc.) on top of CollisionMCRecTableCentFT0C, + // so the closure can optionally use the reconstructed (finite-resolution) Psi_FT0C instead of the MC true event plane + using CollisionMCRecTableClosure = soa::SmallGroups>; + // reconstructed MC collisions for the closure mixed event (posZ and centrality filters as in data) + using CollisionMCRecMixTable = soa::Filtered>; + // same as CollisionMCRecMixTable, plus EPCalibrationTables (psiFT0C etc.) for the reconstructed-event-plane closure + using CollisionMCRecMixTableRecoEP = soa::Filtered>; + using BinningTypeMCClosure = ColumnBinningPolicy; + // same as BinningTypeMCClosure, plus Psi_FT0C (as in data's BinningTypeVertexContributor), for the reconstructed-EP closure ME + using BinningTypeMCClosureRecoEP = ColumnBinningPolicy; SliceCache cache; // Partition posTracks = aod::track::signed1Pt > cfgCutCharge; @@ -164,12 +263,12 @@ struct kstarpbpb { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Event selection cuts - Alex - TF1* fMultPVCutLow = nullptr; - TF1* fMultPVCutHigh = nullptr; - TF1* fMultCutLow = nullptr; - TF1* fMultCutHigh = nullptr; - TF1* fMultMultPVCut = nullptr; + enum SAFrame : int { + kEventPlane = 0, + kProductionPlane = 1, + kRandomEventPlane = 2 + }; + TRandom3 rndGen; void init(o2::framework::InitContext&) { @@ -178,59 +277,113 @@ struct kstarpbpb { rctCut.cfgEvtRCTFlagCheckerZDCCheck, rctCut.cfgEvtRCTFlagCheckerLimitAcceptAsBad); - std::vector occupancyBinning = {0.0, 500.0, 1000.0, 1500.0, 2000.0, 3000.0, 4000.0, 5000.0, 50000.0}; + if (cfgSAFrame.value < kEventPlane || cfgSAFrame.value > kRandomEventPlane) { + LOGF(fatal, "cfgSAFrame = %d not supported (0 = event plane, 1 = production plane, 2 = random event plane)", cfgSAFrame.value); + } + if (cfgSAFrame.value != kProductionPlane && cfgEPNormalHarmonic.value != 1.0f) { + LOGF(warning, "cfgEPNormalHarmonic = %.1f: the SA axis is not the normal to the Psi2 plane, so the R2 = resolution correction does not close", cfgEPNormalHarmonic.value); + } + rndGen.SetSeed(cfgRndSeed); + AxisSpec phiAxis = {500, -6.28, 6.28, "phi"}; AxisSpec resAxis = {6000, -30, 30, "Res"}; AxisSpec centAxis = {8, 0, 80, "V0M (%)"}; - AxisSpec occupancyAxis = {occupancyBinning, "Occupancy"}; - histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0, 10.0}}); - if (!fillSA) { - if (same) { + AxisSpec vzAxis = {400, -20.0, 20.0, "Z_{vtx} (cm)"}; + AxisSpec qvecAxis = {200, 0, 20, "q"}; + if (doprocessSE) { + histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{10, 0, 10.0}}); + if (!fillSA) { histos.add("hSparseV2SASameEvent_V2", "hSparseV2SASameEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); } - if (like) { - histos.add("hSparseV2SAlikeEventNN_V2", "hSparseV2SAlikeEventNN_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); - histos.add("hSparseV2SAlikeEventPP_V2", "hSparseV2SAlikeEventPP_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + if (fillRotation && !fillSA) { + histos.add("hRotation", "hRotation", kTH1F, {{360, 0.0, o2::constants::math::TwoPI}}); + histos.add("hSparseV2SASameEventRotational_V2", "hSparseV2SASameEventRotational_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + } + if (fillSA) { + histos.add("hSparseSAvsrapsameunlike", "hSparseSAvsrapsameunlike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + if (fillLikeSignSA) { + histos.add("hSparseSAvsrapsamelike", "hSparseSAvsrapsamelike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + } + if (fillRotation) { + histos.add("hSparseSAvsraprot", "hSparseSAvsraprot", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + } + if (cfgSAFrame.value == kRandomEventPlane) { + histos.add("hPsiRandom", "Random event plane angle", kTH2F, {centAxis, phiAxis}); + } } } - if (fillRotation) { + + if (doprocessMixedEvent) { if (!fillSA) { - histos.add("hRotation", "hRotation", kTH1F, {{360, 0.0, 2.0 * TMath::Pi()}}); - histos.add("hSparseV2SASameEventRotational_V2", "hSparseV2SASameEventRotational_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + histos.add("hSparseV2SAMixedEvent_V2", "hSparseV2SAMixedEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); + } + if (fillSA) { + histos.add("hSparseSAvsrapmix", "hSparseSAvsrapmix", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); } } - if (fillSA) { - histos.add("hSparseSAvsrapsameunlike", "hSparseSAvsrapsameunlike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - histos.add("hSparseSAvsrapsamelike", "hSparseSAvsrapsamelike", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - histos.add("hSparseSAvsraprot", "hSparseSAvsraprot", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - histos.add("hSparseSAvsrapmix", "hSparseSAvsrapmix", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}, true); - } - if (!fillSA) { + if (doprocessMC) { histos.add("hSparseV2SAGen_V2", "hSparseV2SAGen_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); histos.add("hSparseV2SARec_V2", "hSparseV2SARec_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); histos.add("hpt", "hpt", kTH1F, {configThnAxisPt}); histos.add("hMC", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); - histos.add("h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); histos.add("CentPercentileMCRecHist", "MC Centrality", kTH1F, {{100, 0.0f, 100.0f}}); - histos.add("hSparseV2SAMixedEvent_V2", "hSparseV2SAMixedEvent_V2", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configThnAxisCentrality}); histos.add("h2PhiGen2", "Phi meson gen", kTH2F, {configThnAxisPt, configThnAxisCentrality}); histos.add("h2PhiRec2", "Phi meson Rec", kTH2F, {configThnAxisPt, configThnAxisCentrality}); + histos.add("hSparseKstarMCGenSA", "hSparseKstarMCGenSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCGenCosThetaStar_effy", "hSparseKstarMCGenCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCRecSA", "hSparseKstarMCRecSA", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCRecCosThetaStar_effy", "hSparseKstarMCRecCosThetaStar_effy", HistType::kTHnSparseD, {configThnAxisInvMass, configThnAxisPt, configThnAxisV2, configrapAxis, configThnAxisCentrality}); + histos.add("hMCEffPsi", "Psi of the MC efficiency;centrality (%);#Psi", kTH2F, {configThnAxisCentrality, {36, -o2::constants::math::PI, o2::constants::math::PI}}); + if (mcEffEP.fillPhiVMinusPsi) { + const AxisSpec phiVMinusPsiAxis{mcEffEP.axisPhiVMinusPsi, "#varphi_{V} - #Psi"}; + histos.add("hSparseKstarMCGenCosThetaStarPhiVPsi", "Generated K*: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseD, {configThnAxisPt, configThnAxisV2, phiVMinusPsiAxis, configThnAxisCentrality}); + histos.add("hSparseKstarMCRecCosThetaStarPhiVPsi", "Reconstructed K*: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseD, {configThnAxisPt, configThnAxisV2, phiVMinusPsiAxis, configThnAxisCentrality}); + } + } + + if (doClosureTest) { + histos.add("closure/KstarSameEventCosThetaStar", "K*0 closure: same-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMixedEventCosThetaStar", "K*0 closure: mixed-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMCRecTrueCosThetaStar", "K*0 closure: true matched reconstructed;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + histos.add("closure/KstarMCGenCosThetaStar", "K*0 closure: generated;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {configThnAxisInvMass, configThnAxisPt, {20, -1.0, 1.0, "cos(#vartheta)"}, configrapAxis, configThnAxisCentrality}); + + const AxisSpec closurePhiMass{phiSA.configThnAxisInvMass, "#it{M} (GeV/#it{c}^{2})"}; + const AxisSpec closurePhiPt{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec closurePhiCos{20, -1.0, 1.0, "cos(#vartheta)"}; + const AxisSpec closurePhiRap{phiSA.configThnAxisRapidity, "|y|"}; + const AxisSpec closurePhiCent{phiSA.configThnAxisCentrality, "Centrality (%)"}; + histos.add("closure/PhiSameEventCosThetaStar", "phi closure: same-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMixedEventCosThetaStar", "phi closure: mixed-event unlike-sign;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMCRecTrueCosThetaStar", "phi closure: true matched reconstructed;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + histos.add("closure/PhiMCGenCosThetaStar", "phi closure: generated;M (GeV/c^{2});p_{T} (GeV/c);cos(#vartheta);|y|;centrality", HistType::kTHnSparseF, + {closurePhiMass, closurePhiPt, closurePhiCos, closurePhiRap, closurePhiCent}); + } + + if (doprocessMCkstarWeight) { + histos.add("h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); histos.add("hImpactParameter", "Impact parameter", kTH1F, {{200, 0.0f, 20.0f}}); - histos.add("hEventPlaneAngle", "hEventPlaneAngle", kTH1F, {{200, -2.0f * TMath::Pi(), 2.0f * TMath::Pi()}}); - histos.add("hSparseKstarMCGenWeight", "hSparseKstarMCGenWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecWeight", "hSparseKstarMCRecWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCGenKaonWeight", "hSparseKstarMCGenKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecKaonWeight", "hSparseKstarMCRecKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecKaonMissMatchWeight", "hSparseKstarMCRecKaonMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCGenPionWeight", "hSparseKstarMCGenPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecPionWeight", "hSparseKstarMCRecPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); - histos.add("hSparseKstarMCRecPionMissMatchWeight", "hSparseKstarMCRecPionMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, TMath::Pi()}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hEventPlaneAngle", "hEventPlaneAngle", kTH1F, {{200, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}}); + histos.add("hSparseKstarMCGenWeight", "hSparseKstarMCGenWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecWeight", "hSparseKstarMCRecWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, configThnAxisPt, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCGenKaonWeight", "hSparseKstarMCGenKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecKaonWeight", "hSparseKstarMCRecKaonWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecKaonMissMatchWeight", "hSparseKstarMCRecKaonMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCGenPionWeight", "hSparseKstarMCGenPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecPionWeight", "hSparseKstarMCRecPionWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); + histos.add("hSparseKstarMCRecPionMissMatchWeight", "hSparseKstarMCRecPionMissMatchWeight", HistType::kTHnSparseD, {configThnAxisCentrality, {36, 0.0f, o2::constants::math::PI}, {400, 0.0f, 1}, axisPtKaonWeight, {8, -0.8, 0.8}}); } - if (additionalQAplots1) { + + if (doprocessSE && additionalQAplots1) { histos.add("hFTOCvsTPCSelected", "Mult correlation FT0C vs. TPC after selection", kTH2F, {{80, 0.0f, 80.0f}, {100, -0.5f, 5999.5f}}); histos.add("hCentrality", "Centrality distribution", kTH1F, {{200, 0.0, 200.0}}); - histos.add("hOccupancy", "Occupancy distribution", kTH1F, {occupancyAxis}); histos.add("hVtxZ", "Vertex distribution in Z;Z (cm)", kTH1F, {{400, -20.0, 20.0}}); histos.add("hPsiFT0C", "PsiFT0C", kTH2F, {centAxis, phiAxis}); histos.add("hPsiFT0A", "PsiFT0A", kTH2F, {centAxis, phiAxis}); @@ -241,11 +394,17 @@ struct kstarpbpb { histos.add("ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); histos.add("ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); histos.add("ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); - histos.add("ResTrackSPFT0CTPC", "ResTrackSPFT0CTPC", kTH3F, {centAxis, occupancyAxis, resAxis}); - histos.add("ResTrackSPFT0CFT0A", "ResTrackSPFT0CFT0A", kTH3F, {centAxis, occupancyAxis, resAxis}); - histos.add("ResTrackSPFT0ATPC", "ResTrackSPFT0ATPC", kTH3F, {centAxis, occupancyAxis, resAxis}); + histos.add("ResTrackSPFT0CTPC", "ResTrackSPFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("ResTrackSPFT0CFT0A", "ResTrackSPFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("ResTrackSPFT0ATPC", "ResTrackSPFT0ATPC", kTH2F, {centAxis, resAxis}); + histos.add("hQFT0CvsCent", "q_{FT0C} vs centrality", kTH2F, {centAxis, qvecAxis}); + histos.add("hQFT0CvsVz", "q_{FT0C} vs Z_{vtx}", kTH2F, {vzAxis, qvecAxis}); + histos.add("hQFT0AvsCent", "q_{FT0A} vs centrality", kTH2F, {centAxis, qvecAxis}); + histos.add("hQFT0AvsVz", "q_{FT0A} vs Z_{vtx}", kTH2F, {vzAxis, qvecAxis}); + histos.add("hQTPCvsCent", "q_{TPC} vs centrality", kTH2F, {centAxis, qvecAxis}); + histos.add("hQTPCvsVz", "q_{TPC} vs Z_{vtx}", kTH2F, {vzAxis, qvecAxis}); } - if (additionalQAplots) { + if (doprocessSE && additionalQAplots) { // DCA QA histos.add("QAbefore/trkDCAxyka", "DCAxy distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); histos.add("QAbefore/trkDCAzka", "DCAz distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); @@ -276,19 +435,219 @@ struct kstarpbpb { histos.add("QAafter/TPC_Nsigma_allpi", "TPC NSigma for pion;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{pion};", {HistType::kTH3D, {{200, 0.0, 20.0}, {100, -6, 6}, {100, 0.0, 100.0}}}); } - // Event selection cut additional - Alex - if (additionalEvsel) { - fMultPVCutLow = new TF1("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x - 2.5*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); - fMultPVCutLow->SetParameters(2834.66, -87.0127, 0.915126, -0.00330136, 332.513, -12.3476, 0.251663, -0.00272819, 1.12242e-05); - fMultPVCutHigh = new TF1("fMultPVCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x + 2.5*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); - fMultPVCutHigh->SetParameters(2834.66, -87.0127, 0.915126, -0.00330136, 332.513, -12.3476, 0.251663, -0.00272819, 1.12242e-05); - fMultCutLow = new TF1("fMultCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x - 2.5*([4]+[5]*x)", 0, 100); - fMultCutLow->SetParameters(1893.94, -53.86, 0.502913, -0.0015122, 109.625, -1.19253); - fMultCutHigh = new TF1("fMultCutHigh", "[0]+[1]*x+[2]*x*x+[3]*x*x*x + 3.*([4]+[5]*x)", 0, 100); - fMultCutHigh->SetParameters(1893.94, -53.86, 0.502913, -0.0015122, 109.625, -1.19253); - fMultMultPVCut = new TF1("fMultMultPVCut", "[0]+[1]*x+[2]*x*x", 0, 5000); - fMultMultPVCut->SetParameters(-0.1, 0.785, -4.7e-05); + if (doprocessSE) { + histos.add("hMassSameEventLikeNN", "Same-event like-sign (--) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + histos.add("hMassSameEventLikePP", "Same-event like-sign (++) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + } + if (doprocessMixedEvent) { + histos.add("hMassMixedEventLikeNN", "Mixed-event like-sign (--) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + histos.add("hMassMixedEventLikePP", "Mixed-event like-sign (++) mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + histos.add("hMassMixedEventUnlike", "Mixed-event unlike-sign mass", kTH2F, {configThnAxisInvMass, configThnAxisCentrality}); + } + + // phi(1020) spin alignment, same names and binning as phipbpb.cxx + if (doprocessSEPhi || doprocessMEPhi || doprocessMCPhi) { + const AxisSpec thnAxisInvMassPhi{phiSA.configThnAxisInvMass, "#it{M} (GeV/#it{c}^{2})"}; + const AxisSpec thnAxisPtPhi{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec thnAxisCosThetaStarPhi{phiSA.configThnAxisCosThetaStar, "cos(#vartheta_{OP})"}; + const AxisSpec thnAxisCentralityPhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + const AxisSpec thnAxisRapidityPhi{phiSA.configThnAxisRapidity, "Rapidity"}; + const AxisSpec thnAxisSAPhi{phiSA.configThnAxisSA, "SA"}; + if (cfgSAFrame.value == kRandomEventPlane) { + histos.add("phi/hPsiRandom", "Random event plane angle", kTH2F, {centAxis, phiAxis}); + } + if (doprocessSEPhi) { + histos.add("phi/hSparseV2SameEventSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2SameEventCosThetaStar", "hSparseV2SameEventCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + if (phiSA.fillRotation) { + histos.add("phi/hSparseV2RotationSA", "hSparseV2RotationSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2RotationCosThetaStar", "hSparseV2RotationCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + } + if (phiSA.fillLikeSign) { + histos.add("phi/hSparseV2SameEventLikeSA", "hSparseV2SameEventLikeSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2SameEventLikeCosThetaStar", "hSparseV2SameEventLikeCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hMassSameEventLikePP", "Same-event like-sign (++) mass", kTH2F, {thnAxisInvMassPhi, thnAxisCentralityPhi}); + histos.add("phi/hMassSameEventLikeNN", "Same-event like-sign (--) mass", kTH2F, {thnAxisInvMassPhi, thnAxisCentralityPhi}); + } + } + if (doprocessMEPhi) { + histos.add("phi/hSparseV2MixedEventSA", "hSparseV2MixedEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MixedEventCosThetaStar", "hSparseV2MixedEventCosThetaStar", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + } + if (doprocessMCPhi) { + histos.add("phi/hMC", "MC Event statistics", kTH1F, {{10, 0.0f, 10.0f}}); + histos.add("phi/h1PhiRecsplit", "Phi meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); + histos.add("phi/CentPercentileMCRecHist", "MC Centrality", kTH1F, {{100, 0.0f, 100.0f}}); + histos.add("phi/hSparseV2MCGenSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCGenCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecSA", "hSparseV2SameEventSA", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisSAPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecCosThetaStar_effy", "hSparseV2SameEventCosThetaStar_effy", HistType::kTHnSparseF, {thnAxisInvMassPhi, thnAxisPtPhi, thnAxisCosThetaStarPhi, thnAxisRapidityPhi, thnAxisCentralityPhi}); + histos.add("phi/hMCEffPsi", "Psi of the MC efficiency;centrality (%);#Psi", kTH2F, {thnAxisCentralityPhi, {36, -o2::constants::math::PI, o2::constants::math::PI}}); + if (mcEffEP.fillPhiVMinusPsi) { + const AxisSpec phiVMinusPsiAxis{mcEffEP.axisPhiVMinusPsi, "#varphi_{V} - #Psi"}; + histos.add("phi/hSparseV2MCGenCosThetaStarPhiVPsi", "Generated #phi: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseF, {thnAxisPtPhi, thnAxisCosThetaStarPhi, phiVMinusPsiAxis, thnAxisCentralityPhi}); + histos.add("phi/hSparseV2MCRecCosThetaStarPhiVPsi", "Reconstructed #phi: p_{T}, cos#theta*, #varphi_{V} - #Psi, centrality", HistType::kTHnSparseF, {thnAxisPtPhi, thnAxisCosThetaStarPhi, phiVMinusPsiAxis, thnAxisCentralityPhi}); + } + } + } + + if (doprocessEvtLossSigLossMC) { + const AxisSpec impactParAxis{evtSigLoss.axisImpactPar, "Impact parameter (fm)"}; + const AxisSpec multMCAxis{evtSigLoss.axisMultMC, "N_{ch}^{gen} (|#eta| < 0.5)"}; + const AxisSpec centLossAxis{100, 0.0, 100.0, "Centrality FT0C (%)"}; + const AxisSpec ptKstarAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec ptPhiAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + histos.add("evtSigLoss/MCEventHist", "MC event statistics", kTH1F, {{4, 0.5, 4.5}}); + auto hstat = histos.get(HIST("evtSigLoss/MCEventHist")); + hstat->GetXaxis()->SetBinLabel(1, "All MC events"); + hstat->GetXaxis()->SetBinLabel(2, "MC events with selected reco event"); + hstat->GetXaxis()->SetBinLabel(3, "MC events with no reco event"); + hstat->GetXaxis()->SetBinLabel(4, "MC events with >1 reco event"); + // event loss + histos.add("evtSigLoss/hImpactParameterGen", "Impact parameter, all generated events", kTH1F, {impactParAxis}); + histos.add("evtSigLoss/hImpactParameterRec", "Impact parameter, generated events with selected reco event", kTH1F, {impactParAxis}); + histos.add("evtSigLoss/hImpactParameterGenNoReco", "Impact parameter, generated events with no reco event", kTH1F, {impactParAxis}); + histos.add("evtSigLoss/hImpactParVsCentRec", "Impact parameter vs centrality of the selected reco event", kTH2F, {centLossAxis, impactParAxis}); + histos.add("evtSigLoss/hMultEta05Gen", "N_{ch}^{gen}, all generated events", kTH1F, {multMCAxis}); + histos.add("evtSigLoss/hMultEta05Rec", "N_{ch}^{gen}, generated events with selected reco event", kTH1F, {multMCAxis}); + histos.add("evtSigLoss/hMultEta05GenNoReco", "N_{ch}^{gen}, generated events with no reco event", kTH1F, {multMCAxis}); + histos.add("evtSigLoss/hMultEta05VsCentRec", "N_{ch}^{gen} vs centrality of the selected reco event", kTH2F, {centLossAxis, multMCAxis}); + // signal loss: K*0 + anti-K*0 + histos.add("evtSigLoss/hKstarGenBeforeEvtSel", "Generated K*0, all events", kTH2F, {ptKstarAxis, impactParAxis}); + histos.add("evtSigLoss/hKstarGenAfterEvtSel", "Generated K*0, events with selected reco event", kTH2F, {ptKstarAxis, impactParAxis}); + histos.add("evtSigLoss/hKstarGenVsMultBeforeEvtSel", "Generated K*0, all events", kTH2F, {ptKstarAxis, multMCAxis}); + histos.add("evtSigLoss/hKstarGenVsMultAfterEvtSel", "Generated K*0, events with selected reco event", kTH2F, {ptKstarAxis, multMCAxis}); + histos.add("evtSigLoss/hKstarGenAfterEvtSelVsCent", "Generated K*0, events with selected reco event", kTH2F, {ptKstarAxis, centLossAxis}); + // signal loss: phi(1020) + histos.add("evtSigLoss/hPhiGenBeforeEvtSel", "Generated phi, all events", kTH2F, {ptPhiAxis, impactParAxis}); + histos.add("evtSigLoss/hPhiGenAfterEvtSel", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, impactParAxis}); + histos.add("evtSigLoss/hPhiGenVsMultBeforeEvtSel", "Generated phi, all events", kTH2F, {ptPhiAxis, multMCAxis}); + histos.add("evtSigLoss/hPhiGenVsMultAfterEvtSel", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, multMCAxis}); + histos.add("evtSigLoss/hPhiGenAfterEvtSelVsCent", "Generated phi, events with selected reco event", kTH2F, {ptPhiAxis, centLossAxis}); + } + + // MC closure test of the cos(theta*) chain + if (doprocessMCClosure || doprocessMCClosureME || doprocessMCClosurePhi || doprocessMCClosureMEPhi) { + if (closure.rho00Inj.value < 0.f || closure.rho00Inj.value > 1.f) { + LOGF(fatal, "closure.rho00Inj = %f is outside [0,1]", closure.rho00Inj.value); + } + const AxisSpec cosAxisClosure{closure.axisCosThetaStar, "cos(#vartheta*)"}; + const AxisSpec cosFineAxis{100, -1.0, 1.0, "cos(#vartheta*)"}; + if (doprocessMCClosure || doprocessMCClosureME) { + const AxisSpec massAxis{configThnAxisInvMass, "#it{M}_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosureKstar{configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosure) { + // bins: 0 all MC events, 1 no reco event, 2 more than one reco event, 3 selected (eff sample), 4 selected (test sample) + histos.add("closureKstar/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + // Acc x Eff sample + histos.add("closureKstar/hSparseGenEff", "Generated K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseRecTrueEff", "Reconstructed true K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + // test sample + histos.add("closureKstar/hSparseGen", "Generated K* (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseRecTrue", "Reconstructed true K*, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstar/hSparseSE", "Same-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + if (closure.fillRotBkg) { + histos.add("closureKstar/hSparseRot", "Rotated unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + histos.add("closureKstar/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true K*", kTH2F, {cosFineAxis, cosFineAxis}); + } + if (doprocessMCClosureME) { + histos.add("closureKstar/hSparseME", "Mixed-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + } + if (doprocessMCClosurePhi || doprocessMCClosureMEPhi) { + const AxisSpec massAxis{phiSA.configThnAxisInvMass, "#it{M}_{KK} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosurePhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosurePhi) { + histos.add("closurePhi/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("closurePhi/hSparseGenEff", "Generated phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseRecTrueEff", "Reconstructed true phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseGen", "Generated phi (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseRecTrue", "Reconstructed true phi, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhi/hSparseSE", "Same-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + if (closure.fillRotBkg) { + histos.add("closurePhi/hSparseRot", "Rotated K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + histos.add("closurePhi/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true phi", kTH2F, {cosFineAxis, cosFineAxis}); + } + if (doprocessMCClosureMEPhi) { + histos.add("closurePhi/hSparseME", "Mixed-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + } + } + + // MC closure test of the cos(theta*) chain, using the reconstructed (finite-resolution) Psi_FT0C instead of the MC true event plane. + // Needs EPCalibrationTables on the MC sample (i.e. the epvector producer run on it), unlike the true-EP closure above. + if (doprocessMCClosureRecoEP || doprocessMCClosureMERecoEP || doprocessMCClosureRecoEPPhi || doprocessMCClosureMERecoEPPhi) { + if (closure.rho00Inj.value < 0.f || closure.rho00Inj.value > 1.f) { + LOGF(fatal, "closure.rho00Inj = %f is outside [0,1]", closure.rho00Inj.value); + } + const AxisSpec cosAxisClosure{closure.axisCosThetaStar, "cos(#vartheta*)"}; + const AxisSpec cosFineAxis{100, -1.0, 1.0, "cos(#vartheta*)"}; + if (doprocessMCClosureRecoEP || doprocessMCClosureMERecoEP) { + const AxisSpec massAxis{configThnAxisInvMass, "#it{M}_{K#pi} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosureKstar{configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosureRecoEP) { + // bins: 0 all MC events, 1 no reco event, 2 more than one reco event, 3 selected (eff sample), 4 selected (test sample) + histos.add("closureKstarRecoEP/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + // Acc x Eff sample + histos.add("closureKstarRecoEP/hSparseGenEff", "Generated K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseRecTrueEff", "Reconstructed true K*, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + // test sample + histos.add("closureKstarRecoEP/hSparseGen", "Generated K* (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseRecTrue", "Reconstructed true K*, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + histos.add("closureKstarRecoEP/hSparseSE", "Same-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + if (closure.fillRotBkg) { + histos.add("closureKstarRecoEP/hSparseRot", "Rotated unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + histos.add("closureKstarRecoEP/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true K*", kTH2F, {cosFineAxis, cosFineAxis}); + // sub-event resolution of the closure sample, needed to derive R2 and un-smear rho00 downstream; same definition as the data ResFT0C*SP + histos.add("closureKstarRecoEP/ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.add("closureKstarRecoEP/ResFT0CTPC", "ResFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0CFT0A", "ResFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("closureKstarRecoEP/ResFT0ATPC", "ResFT0ATPC", kTH2F, {centAxis, resAxis}); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.add("closureKstarRecoEP/ResTrueFT0C", "ResTrueFT0C", kTH2F, {centAxis, resAxis}); + } + if (doprocessMCClosureMERecoEP) { + histos.add("closureKstarRecoEP/hSparseME", "Mixed-event unlike-sign K#pi pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosureKstar}, true); + } + } + if (doprocessMCClosureRecoEPPhi || doprocessMCClosureMERecoEPPhi) { + const AxisSpec massAxis{phiSA.configThnAxisInvMass, "#it{M}_{KK} (GeV/#it{c}^{2})"}; + const AxisSpec ptAxis{phiSA.configThnAxisPt, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec centAxisClosurePhi{phiSA.configThnAxisCentrality, "Centrality (%)"}; + if (doprocessMCClosureRecoEPPhi) { + histos.add("closurePhiRecoEP/hMC", "MC event statistics", kTH1F, {{5, 0.0f, 5.0f}}); + histos.add("closurePhiRecoEP/hSparseGenEff", "Generated phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseRecTrueEff", "Reconstructed true phi, efficiency sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseGen", "Generated phi (input), test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseRecTrue", "Reconstructed true phi, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + histos.add("closurePhiRecoEP/hSparseSE", "Same-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + if (closure.fillRotBkg) { + histos.add("closurePhiRecoEP/hSparseRot", "Rotated K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + histos.add("closurePhiRecoEP/hCosThetaStarGenVsRec", "cos(#vartheta*) gen vs rec, true phi", kTH2F, {cosFineAxis, cosFineAxis}); + histos.add("closurePhiRecoEP/ResFT0CTPCSP", "ResFT0CTPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CFT0ASP", "ResFT0CFT0ASP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0ATPCSP", "ResFT0ATPCSP", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CTPC", "ResFT0CTPC", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0CFT0A", "ResFT0CFT0A", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResFT0ATPC", "ResFT0ATPC", kTH2F, {centAxis, resAxis}); + histos.add("closurePhiRecoEP/ResTrueFT0C", "ResTrueFT0C", kTH2F, {centAxis, resAxis}); + } + if (doprocessMCClosureMERecoEPPhi) { + histos.add("closurePhiRecoEP/hSparseME", "Mixed-event K^{+}K^{-} pairs, test sample", HistType::kTHnSparseD, {massAxis, ptAxis, cosAxisClosure, centAxisClosurePhi}, true); + } + } } + ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -296,30 +655,9 @@ struct kstarpbpb { ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); } - double massKa = o2::constants::physics::MassKPlus; - double massPi = o2::constants::physics::MassPiMinus; + static constexpr double MassKa = o2::constants::physics::MassKPlus; + static constexpr double MassPi = o2::constants::physics::MassPiMinus; - template - bool eventSelected(TCollision collision, const float& centrality) - { - if (collision.alias_bit(kTVXinTRD)) { - // TRD triggered - // return 0; - } - auto multNTracksPV = collision.multNTracksPV(); - if (multNTracksPV < fMultPVCutLow->Eval(centrality)) - return 0; - if (multNTracksPV > fMultPVCutHigh->Eval(centrality)) - return 0; - // if (multTrk < fMultCutLow->Eval(centrality)) - // return 0; - // if (multTrk > fMultCutHigh->Eval(centrality)) - // return 0; - // if (multTrk > fMultMultPVCut->Eval(multNTracksPV)) - // return 0; - - return 1; - } template bool selectionTrack(const T& candidate) { @@ -331,140 +669,88 @@ struct kstarpbpb { } return true; } - - template - bool selectionPIDNew(const T& candidate, int PID) - { - if (PID == 0) { - if (candidate.pt() < 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { - return true; - } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && candidate.hasTOF() && TMath::Abs(candidate.tofNSigmaKa()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { - return true; - } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && !candidate.hasTOF()) { - return true; - } - } else if (PID == 1) { - if (candidate.pt() < 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { - return true; - } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && candidate.hasTOF() && TMath::Abs(candidate.tofNSigmaPi()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { - return true; - } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && !candidate.hasTOF()) { - return true; - } - } - return false; - } + static constexpr float TPCOnlyPt = 0.5f; + static constexpr int Strategy = 2; template bool selectionPID2(const T& candidate, int PID) { if (PID == 0) { - if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && TMath::Abs(candidate.tofNSigmaKa()) < nsigmaCutTOF) { + if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && std::abs(candidate.tofNSigmaKa()) < nsigmaCutTOF) { return true; } } if (PID == 1) { - if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && TMath::Abs(candidate.tofNSigmaPi()) < nsigmaCutTOF) { - return true; - } - } - return false; - } - template - bool selectionPID(const T& candidate, int PID) - { - if (PID == 0) { - if (!isNoTOF && !candidate.hasTOF() && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { - return true; - } - if (!isNoTOF && candidate.hasTOF() && ((candidate.tofNSigmaKa() * candidate.tofNSigmaKa()) + (candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa())) < (nsigmaCutCombined * nsigmaCutCombined)) { - return true; - } - if (isNoTOF && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { - return true; - } - } else if (PID == 1) { - if (!isNoTOF && !candidate.hasTOF() && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { - return true; - } - if (!isNoTOF && candidate.hasTOF() && ((candidate.tofNSigmaPi() * candidate.tofNSigmaPi()) + (candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi())) < (nsigmaCutCombined * nsigmaCutCombined)) { - return true; - } - if (isNoTOF && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { + if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && std::abs(candidate.tofNSigmaPi()) < nsigmaCutTOF) { return true; } } return false; } - template bool strategySelectionPID(const T& candidate, int PID, int strategy) { if (PID == 0) { if (strategy == 0) { - if (!isNoTOF && !candidate.hasTOF() && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { + if (!isNoTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { return true; } - if (!isNoTOF && candidate.hasTOF() && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && TMath::Abs(candidate.tofNSigmaKa()) < nsigmaCutTOF) { + if (!isNoTOF && candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && std::abs(candidate.tofNSigmaKa()) < nsigmaCutTOF) { return true; } if (isNoTOF && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { return true; } } else if (strategy == 1) { - if (candidate.pt() < 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { + if (!isNoTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { return true; } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && candidate.hasTOF() && TMath::Abs(candidate.tofNSigmaKa()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { + if (!isNoTOF && candidate.hasTOF() && ((candidate.tofNSigmaKa() * candidate.tofNSigmaKa()) + (candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa())) < (nsigmaCutCombined * nsigmaCutCombined)) { return true; } - if (!useGlobalTrack && !candidate.hasTPC()) { + if (isNoTOF && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { return true; } - } else if (strategy == 2) { - if (candidate.pt() < 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { + } else if (strategy == Strategy) { + if (candidate.pt() < TPCOnlyPt && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC) { return true; } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && candidate.hasTOF() && TMath::Abs(candidate.tofNSigmaKa()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { + if (candidate.pt() >= TPCOnlyPt && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { return true; } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && !candidate.hasTOF()) { + if (candidate.pt() >= TPCOnlyPt && std::abs(candidate.tpcNSigmaKa()) < nsigmaCutTPC && !candidate.hasTOF()) { return true; } } } if (PID == 1) { if (strategy == 0) { - if (!isNoTOF && !candidate.hasTOF() && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { + if (!isNoTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { return true; } - if (!isNoTOF && candidate.hasTOF() && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && TMath::Abs(candidate.tofNSigmaPi()) < nsigmaCutTOF) { + if (!isNoTOF && candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && std::abs(candidate.tofNSigmaPi()) < nsigmaCutTOF) { return true; } if (isNoTOF && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { return true; } } else if (strategy == 1) { - if (candidate.pt() < 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { + if (!isNoTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { return true; } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && candidate.hasTOF() && TMath::Abs(candidate.tofNSigmaPi()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { + if (!isNoTOF && candidate.hasTOF() && ((candidate.tofNSigmaPi() * candidate.tofNSigmaPi()) + (candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi())) < (nsigmaCutCombined * nsigmaCutCombined)) { return true; } - if (!useGlobalTrack && !candidate.hasTPC()) { + if (isNoTOF && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { return true; } - } else if (strategy == 2) { - if (candidate.pt() < 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { + } else if (strategy == Strategy) { + if (candidate.pt() < TPCOnlyPt && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC) { return true; } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && candidate.hasTOF() && TMath::Abs(candidate.tofNSigmaPi()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { + if (candidate.pt() >= TPCOnlyPt && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < nsigmaCutTOF && candidate.beta() > cfgCutTOFBeta) { return true; } - if (candidate.pt() >= 0.5 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && !candidate.hasTOF()) { + if (candidate.pt() >= TPCOnlyPt && std::abs(candidate.tpcNSigmaPi()) < nsigmaCutTPC && !candidate.hasTOF()) { return true; } } @@ -472,102 +758,281 @@ struct kstarpbpb { return false; } - double GetPhiInRange(double phi) + double getPhiInRange(double phi) { double result = phi; while (result < 0) { - result = result + 2. * TMath::Pi() / 2; + result += o2::constants::math::PI; } - while (result > 2. * TMath::Pi() / 2) { - result = result - 2. * TMath::Pi() / 2; + while (result >= o2::constants::math::PI) { // >= not > + result -= o2::constants::math::PI; } return result; } + + // event-level plane angle for SA: reconstructed Psi2 (event plane) or a random angle (random event plane) + double getSAEventAngle(double psiEP) + { + if (cfgSAFrame.value == kRandomEventPlane) { + return rndGen.Uniform(-o2::constants::math::PIHalf, o2::constants::math::PIHalf); + } + return psiEP; + } + + // in-plane angle of the SA plane, used for the cos(2(phi* - Psi)) variable + double getSAPlaneAngle(const ROOT::Math::PxPyPzMVector& mother, double psiSA) + { + if (cfgSAFrame.value == kProductionPlane) { + return mother.Phi(); // production plane is spanned by the beam axis and the mother momentum + } + return psiSA; + } + + // phi(1020) -> K+K- with one kaon taken as the pion: true if the KK mass is inside the veto window (always false if cfgPhiVeto is off) + bool isPhiReflection(const ROOT::Math::PxPyPzMVector& kaon, const ROOT::Math::PxPyPzMVector& pion) + { + if (!cfgPhiVeto) { + return false; + } + ROOT::Math::PxPyPzMVector kaonAsKaon(kaon.Px(), kaon.Py(), kaon.Pz(), MassKa); + ROOT::Math::PxPyPzMVector pionAsKaon(pion.Px(), pion.Py(), pion.Pz(), MassKa); + return std::abs((kaonAsKaon + pionAsKaon).M() - o2::constants::physics::MassPhi) < cfgPhiVetoWindow; + } + + // quantization axis = normal to the SA plane + ROOT::Math::XYZVector getSAAxis(const ROOT::Math::PxPyPzMVector& mother, double psiSA) + { + if (cfgSAFrame.value == kProductionPlane) { + return ROOT::Math::XYZVector(0., 0., 1.).Cross(mother.Vect()).Unit(); // z x p + } + return {std::sin(cfgEPNormalHarmonic.value * psiSA), -std::cos(cfgEPNormalHarmonic.value * psiSA), 0.}; + } + + // ---------------- event selection, common to all K* and phi(1020) process functions ---------------- + template + bool selectionEventRCT(const TCollision& collision) + { + return !rctCut.requireRCTFlagChecker || rctCut.rctChecker(collision); + } + + // additionalEvSel1-4 (data and reconstructed MC) + template + bool selectionEventBits(const TCollision& collision) + { + if (additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (additionalEvSel2 && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (additionalEvSel3 && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + return true; + } + + // sel8, EP trigger and additionalEvSel1-4 + template + bool selectionEventCuts(const TCollision& collision) + { + return collision.sel8() && collision.triggereventep() && selectionEventBits(collision); + } + + // full data event selection + template + bool selectionEvent(const TCollision& collision) + { + return selectionEventRCT(collision) && selectionEventCuts(collision); + } + + // mixed-event pair: both events selected and not from the same bunch crossing + template + bool selectionEventPairME(const TCollision& collision1, const TCollision& collision2) + { + if (!selectionEvent(collision1) || !selectionEvent(collision2)) { + return false; + } + return collision1.bcId() != collision2.bcId(); + } + + // ---------------- phi(1020) track and PID selections, identical to phianalysisrun3pbpb.cxx ---------------- + template - bool isFakeKaon(T const& track, int /*PID*/) + bool selectionTrackPhi(const T& candidate) { - const auto pglobal = track.p(); - const auto ptpc = track.tpcInnerParam(); - if (TMath::Abs(pglobal - ptpc) > ConfFakeKaonCut) { + if (phiSA.iscustomDCAcut && !(candidate.isGlobalTrack() && candidate.isPVContributor() && candidate.itsNCls() > phiSA.cfgITScluster)) { + return false; + } + if (phiSA.ismanualDCAcut && !(candidate.isGlobalTrackWoDCA() && candidate.isPVContributor() && std::abs(candidate.dcaXY()) < phiSA.cfgCutDCAxy && std::abs(candidate.dcaZ()) < phiSA.cfgCutDCAz && candidate.itsNCls() > phiSA.cfgITScluster)) { + return false; + } + return true; + } + + // kaon PID: circular TPC-TOF cut if TOF is present, TPC-only otherwise (or always, if isNoTOF) + template + bool selectionPIDPhi(const T& candidate) + { + if (!phiSA.isNoTOF && candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (phiSA.nsigmaCutCombined * phiSA.nsigmaCutCombined)) { + return true; + } + if (!phiSA.isNoTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { + return true; + } + if (phiSA.isNoTOF && std::abs(candidate.tpcNSigmaKa()) < phiSA.nsigmaCutTPC) { return true; } return false; } + + // deep angle cut on pair to remove photon conversion + template + bool selectionPairPhi(const T1& candidate1, const T2& candidate2) + { + double pt1 = candidate1.pt(), pt2 = candidate2.pt(); + double pz1 = candidate1.pz(), pz2 = candidate2.pz(); + double p1 = candidate1.p(), p2 = candidate2.p(); + double angle = std::acos(std::clamp((pt1 * pt2 + pz1 * pz2) / (p1 * p2), -1.0, 1.0)); // clamp = TMath::ACos behaviour + return !phiSA.isDeepAngle || angle >= phiSA.cfgDeepAngle; + } + + template + bool isFakeKaonPhi(T const& track) + { + return std::abs(track.p() - track.tpcInnerParam()) > phiSA.confFakeKaonCut; + } + + // phipbpb.cxx builds the EP-frame MC efficiency with the in-plane vector at Psi = 0 (x axis); kept for identical results + // with mcEffEP.psiSource = 0 only. With an event-by-event Psi the axis must be the plane normal, as in data (getSAAxis). + ROOT::Math::XYZVector getSAAxisPhiMC(const ROOT::Math::PxPyPzMVector& mother, double psiSA) + { + if (cfgSAFrame.value == kEventPlane && mcEffEP.psiSource == 0) { + return {std::cos(2.0 * psiSA), std::sin(2.0 * psiSA), 0.}; + } + return getSAAxis(mother, psiSA); + } + + // Psi2 = atan2(Q2y, Q2x) / 2 from the generated charged physical primaries in [etaMin, etaMax] (thesis Eq. B.16, unit weights) + template + double getGenPsi2(TParticles const& particles) + { + double qx = 0., qy = 0.; + for (const auto& particle : particles) { + if (!particle.isPhysicalPrimary() || particle.eta() < mcEffEP.etaMin || particle.eta() > mcEffEP.etaMax) { + continue; + } + const int absPdg = std::abs(particle.pdgCode()); + if (absPdg != PDG_t::kPiPlus && absPdg != PDG_t::kKPlus && absPdg != PDG_t::kProton && absPdg != PDG_t::kElectron && absPdg != PDG_t::kMuonMinus) { + continue; + } + qx += std::cos(2.0 * particle.phi()); + qy += std::sin(2.0 * particle.phi()); + } + return 0.5 * std::atan2(qy, qx); + } + + // Psi of the EP-frame MC efficiency, one per MC event (see mcEffEP) + template + double getMCEffPsi(TMcCollision const& mcCollision, TParticles const& particles) + { + if (mcEffEP.psiSource == 1) { + return getGenPsi2(particles); + } + if (mcEffEP.psiSource == 2) { + return mcCollision.eventPlaneAngle(); + } + return 0.0; + } + + template + bool isFakeKaon(T const& track, int /*PID*/) + { + const auto pglobal = track.p(); + const auto ptpc = track.tpcInnerParam(); + return std::abs(pglobal - ptpc) > confFakeKaonCut; + } + + static constexpr float HalfPI = o2::constants::math::PI * 0.5f; ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for bin"}; ConfigurableAxis axisMultiplicityClass{"axisMultiplicityClass", {20, 0, 100}, "multiplicity percentile for bin"}; - ConfigurableAxis axisEPAngle{"axisEPAngle", {6, -TMath::Pi() / 2, TMath::Pi() / 2}, "event plane angle"}; - ConfigurableAxis axisOccup{"axisOccup", {20, -0.5, 40000.0}, "occupancy axis"}; - double v2, v2Rot; + ConfigurableAxis axisEPAngle{"axisEPAngle", + {6, -HalfPI, HalfPI}, + "event plane angle"}; using BinningTypeVertexContributor = ColumnBinningPolicy; - ROOT::Math::PxPyPzMVector KstarMother, fourVecDauCM, daughter1, daughter2, kaonrot, kstarrot, KaonPlus, PionMinus; - ROOT::Math::XYZVector threeVecDauCM, threeVecDauCMXY, eventplaneVec, eventplaneVecNorm; - ROOT::Math::PxPyPzMVector daughter2rot, fourVecDauCMrot; - ROOT::Math::XYZVector threeVecDauCMrot, threeVecDauCMXYrot; int currentRunNumber = -999; int lastRunNumber = -999; - TH2D* hweight; + TH2D* hweight = nullptr; void processSE(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCsWithTimestamps const&) { + // scratch vectors: local to this call, never carry state across events/tracks + ROOT::Math::PxPyPzMVector kstarMother, fourVecDauCM, daughter1, daughter2, kaonrot, kstarrot; + ROOT::Math::XYZVector threeVecDauCM, threeVecDauCMXY, eventplaneVecNorm; + ROOT::Math::PxPyPzMVector fourVecDauCMrot; + ROOT::Math::XYZVector threeVecDauCMrot, threeVecDauCMXYrot; + double v2 = 0.; + double v2Rot = 0.; + histos.fill(HIST("hEvtSelInfo"), 0.5); - if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(collision)) { + if (!selectionEventRCT(collision)) { return; } histos.fill(HIST("hEvtSelInfo"), 1.5); - if (!collision.sel8() || !collision.triggereventep() || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + if (!selectionEventCuts(collision)) { return; } histos.fill(HIST("hEvtSelInfo"), 2.5); auto centrality = collision.centFT0C(); auto multTPC = collision.multNTracksPV(); - int occupancy = collision.trackOccupancyInTimeRange(); auto psiFT0C = collision.psiFT0C(); auto psiFT0A = collision.psiFT0A(); auto psiTPC = collision.psiTPC(); - auto QFT0C = collision.qFT0C(); - auto QFT0A = collision.qFT0A(); - auto QTPC = collision.qTPC(); - if (fillOccupancy && occupancy > cfgOccupancyCut) { - return; - } + auto qFT0C = collision.qFT0C(); + auto qFT0A = collision.qFT0A(); + auto qTPC = collision.qTPC(); histos.fill(HIST("hEvtSelInfo"), 3.5); - if (additionalEvsel && !eventSelected(collision, centrality)) { - return; - } - histos.fill(HIST("hEvtSelInfo"), 4.5); - if (additionalEvselITS && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - return; - } - histos.fill(HIST("hEvtSelInfo"), 5.5); if (additionalQAplots1) { histos.fill(HIST("hFTOCvsTPCSelected"), centrality, multTPC); histos.fill(HIST("hPsiFT0C"), centrality, psiFT0C); histos.fill(HIST("hPsiFT0A"), centrality, psiFT0A); histos.fill(HIST("hPsiTPC"), centrality, psiTPC); - histos.fill(HIST("ResFT0CTPC"), centrality, TMath::Cos(2.0 * (psiFT0C - psiTPC))); - histos.fill(HIST("ResFT0CFT0A"), centrality, TMath::Cos(2.0 * (psiFT0C - psiFT0A))); - histos.fill(HIST("ResFT0ATPC"), centrality, TMath::Cos(2.0 * (psiTPC - psiFT0A))); - histos.fill(HIST("ResFT0CTPCSP"), centrality, QFT0C * QTPC * TMath::Cos(2.0 * (psiFT0C - psiTPC))); - histos.fill(HIST("ResFT0CFT0ASP"), centrality, QFT0C * QFT0A * TMath::Cos(2.0 * (psiFT0C - psiFT0A))); - histos.fill(HIST("ResFT0ATPCSP"), centrality, QTPC * QFT0A * TMath::Cos(2.0 * (psiTPC - psiFT0A))); + histos.fill(HIST("ResFT0CTPC"), centrality, std::cos(2.0 * (psiFT0C - psiTPC))); + histos.fill(HIST("ResFT0CFT0A"), centrality, std::cos(2.0 * (psiFT0C - psiFT0A))); + histos.fill(HIST("ResFT0ATPC"), centrality, std::cos(2.0 * (psiTPC - psiFT0A))); + histos.fill(HIST("ResFT0CTPCSP"), centrality, qFT0C * qTPC * std::cos(2.0 * (psiFT0C - psiTPC))); + histos.fill(HIST("ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (psiFT0C - psiFT0A))); + histos.fill(HIST("ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); histos.fill(HIST("hCentrality"), centrality); - histos.fill(HIST("hOccupancy"), occupancy); histos.fill(HIST("hVtxZ"), collision.posZ()); + histos.fill(HIST("hQFT0CvsCent"), centrality, qFT0C); + histos.fill(HIST("hQFT0CvsVz"), collision.posZ(), qFT0C); + histos.fill(HIST("hQFT0AvsCent"), centrality, qFT0A); + histos.fill(HIST("hQFT0AvsVz"), collision.posZ(), qFT0A); + histos.fill(HIST("hQTPCvsCent"), centrality, qTPC); + histos.fill(HIST("hQTPCvsVz"), collision.posZ(), qTPC); + } + // one angle per event: Psi2 from FT0C, or a random angle for the random-EP null test + auto psiSA = getSAEventAngle(psiFT0C); + if (fillSA && cfgSAFrame.value == kRandomEventPlane) { + histos.fill(HIST("hPsiRandom"), centrality, psiSA); } auto bc = collision.bc_as(); currentRunNumber = collision.bc_as().runNumber(); if (useWeight && (currentRunNumber != lastRunNumber)) { - hweight = ccdb->getForTimeStamp(ConfWeightPath.value, bc.timestamp()); + hweight = ccdb->getForTimeStamp(confWeightPath.value, bc.timestamp()); } lastRunNumber = currentRunNumber; float weight1 = 1.0; float weight2 = 1.0; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } - bool track1kaon = false; + auto track1ID = track1.globalIndex(); if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { continue; @@ -575,20 +1040,20 @@ struct kstarpbpb { if (isTOFOnly && !selectionPID2(track1, 0)) { continue; } - track1kaon = true; if (useWeight) { - if (track1.pt() < 10.0 && track1.pt() > 0.15) { - weight1 = 1 + hweight->GetBinContent(hweight->FindBin(centrality, track1.pt() + 0.000005)) * TMath::Cos(2.0 * GetPhiInRange(track1.phi() - psiFT0C)); + if (track1.pt() < cfgMaxTrackPt && + track1.pt() > cfgMinTrackPt) { + weight1 = 1 + hweight->GetBinContent(hweight->FindBin(centrality, track1.pt() + 0.000005)) * std::cos(2.0 * getPhiInRange(track1.phi() - psiFT0C)); } else { weight1 = 1; } } - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!selectionTrack(track2)) { continue; } - bool track2pion = false; + auto track2ID = track2.globalIndex(); if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { continue; @@ -596,13 +1061,11 @@ struct kstarpbpb { if (isTOFOnly && !selectionPID2(track2, 1)) { continue; } - track2pion = true; + if (track2ID == track1ID) { continue; } - if (!track1kaon || !track2pion) { - continue; - } + if (additionalQAplots) { histos.fill(HIST("QAafter/TPC_Nsigma_allka"), track1.pt(), track1.tpcNSigmaKa(), centrality); histos.fill(HIST("QAafter/TOF_Nsigma_allka"), track1.pt(), track1.tofNSigmaKa(), centrality); @@ -616,69 +1079,85 @@ struct kstarpbpb { histos.fill(HIST("QAafter/trkDCAzpi"), track2.dcaZ()); } if (useWeight) { - if (track2.pt() < 10.0 && track2.pt() > 0.15) { - weight2 = 1 + hweight->GetBinContent(hweight->FindBin(centrality, track2.pt() + 0.000005)) * TMath::Cos(2.0 * GetPhiInRange(track2.phi() - psiFT0C)); + if (track2.pt() < cfgMaxTrackPt && + track2.pt() > cfgMinTrackPt) { + weight2 = 1 + hweight->GetBinContent(hweight->FindBin(centrality, track2.pt() + 0.000005)) * std::cos(2.0 * getPhiInRange(track2.phi() - psiFT0C)); } else { weight2 = 1; } } - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - KstarMother = daughter1 + daughter2; - if (TMath::Abs(KstarMother.Rapidity()) > confRapidity) { + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstarMother = daughter1 + daughter2; + if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; } - auto phiminuspsi = GetPhiInRange(KstarMother.Phi() - psiFT0C); + if (isPhiReflection(daughter1, daughter2)) { // phi(1020) veto (cfgPhiVeto); also skips the rotations of this pair + continue; + } + auto phiMinusPsi = getPhiInRange(kstarMother.Phi() - psiFT0C); if (useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi) * QFT0C; + v2 = std::cos(2.0 * phiMinusPsi) * qFT0C; } if (!useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi); + v2 = std::cos(2.0 * phiMinusPsi); } auto totalweight = weight1 * weight2; - if (totalweight <= 0.0000005) { + static constexpr float MinTotalWeight = 5e-7f; + if (totalweight <= MinTotalWeight) { totalweight = 1.0; } if (additionalQAplots1) { - histos.fill(HIST("ResTrackSPFT0CTPC"), centrality, occupancy, QFT0C * QTPC * TMath::Cos(2.0 * (psiFT0C - psiTPC))); - histos.fill(HIST("ResTrackSPFT0CFT0A"), centrality, occupancy, QFT0C * QFT0A * TMath::Cos(2.0 * (psiFT0C - psiFT0A))); - histos.fill(HIST("ResTrackSPFT0ATPC"), centrality, occupancy, QTPC * QFT0A * TMath::Cos(2.0 * (psiTPC - psiFT0A))); + histos.fill(HIST("ResTrackSPFT0CTPC"), centrality, qFT0C * qTPC * std::cos(2.0 * (psiFT0C - psiTPC))); + histos.fill(HIST("ResTrackSPFT0CFT0A"), centrality, qFT0C * qFT0A * std::cos(2.0 * (psiFT0C - psiFT0A))); + histos.fill(HIST("ResTrackSPFT0ATPC"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); } if (!fillSA) { - if (same) { - if (useWeight) { - histos.fill(HIST("hSparseV2SASameEvent_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality, 1 / totalweight); - } else { - histos.fill(HIST("hSparseV2SASameEvent_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality); - } + + if (useWeight) { + histos.fill(HIST("hSparseV2SASameEvent_V2"), kstarMother.M(), kstarMother.Pt(), v2, centrality, 1 / totalweight); + } else { + histos.fill(HIST("hSparseV2SASameEvent_V2"), kstarMother.M(), kstarMother.Pt(), v2, centrality); } } int track1Sign = track1.sign(); int track2Sign = track2.sign(); + if (track1Sign * track2Sign > 0) { + if (track1Sign > 0) { + histos.fill(HIST("hMassSameEventLikePP"), kstarMother.M(), centrality); + } else { + histos.fill(HIST("hMassSameEventLikeNN"), kstarMother.M(), centrality); + } + } else { + } + if (fillSA) { - ROOT::Math::Boost boost{KstarMother.BoostToCM()}; + ROOT::Math::Boost boost{kstarMother.BoostToCM()}; fourVecDauCM = boost(daughter1); threeVecDauCM = fourVecDauCM.Vect(); threeVecDauCMXY = ROOT::Math::XYZVector(threeVecDauCM.X(), threeVecDauCM.Y(), 0.); - eventplaneVec = ROOT::Math::XYZVector(std::cos(2.0 * psiFT0C), std::sin(2.0 * psiFT0C), 0); - eventplaneVecNorm = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0C), -std::cos(2.0 * psiFT0C), 0); - auto cosPhistarminuspsi = GetPhiInRange(fourVecDauCM.Phi() - psiFT0C); - auto SA = TMath::Cos(2.0 * cosPhistarminuspsi); + eventplaneVecNorm = getSAAxis(kstarMother, psiSA); + auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - getSAPlaneAngle(kstarMother, psiSA)); + auto sa = std::cos(2.0 * cosPhistarminuspsi); auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + if (doClosureTest && track1Sign * track2Sign < 0) { + histos.fill(HIST("closure/KstarSameEventCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, std::abs(kstarMother.Rapidity()), centrality); + } + if (track1Sign * track2Sign < 0) { if (usepolar) { - histos.fill(HIST("hSparseSAvsrapsameunlike"), KstarMother.M(), KstarMother.Pt(), cosThetaStar, KstarMother.Rapidity(), centrality); + histos.fill(HIST("hSparseSAvsrapsameunlike"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); } else { - histos.fill(HIST("hSparseSAvsrapsameunlike"), KstarMother.M(), KstarMother.Pt(), SA, KstarMother.Rapidity(), centrality); + histos.fill(HIST("hSparseSAvsrapsameunlike"), kstarMother.M(), kstarMother.Pt(), sa, kstarMother.Rapidity(), centrality); } - } else if (track1Sign * track2Sign > 0) { + } else if (fillLikeSignSA && track1Sign * track2Sign > 0) { if (usepolar) { - histos.fill(HIST("hSparseSAvsrapsamelike"), KstarMother.M(), KstarMother.Pt(), cosThetaStar, KstarMother.Rapidity(), centrality); + histos.fill(HIST("hSparseSAvsrapsamelike"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); } else { - histos.fill(HIST("hSparseSAvsrapsamelike"), KstarMother.M(), KstarMother.Pt(), SA, KstarMother.Rapidity(), centrality); + histos.fill(HIST("hSparseSAvsrapsamelike"), kstarMother.M(), kstarMother.Pt(), sa, kstarMother.Rapidity(), centrality); } } } @@ -693,18 +1172,21 @@ struct kstarpbpb { } auto rotkaonPx = track1.px() * std::cos(rotangle) - track1.py() * std::sin(rotangle); auto rotkaonPy = track1.px() * std::sin(rotangle) + track1.py() * std::cos(rotangle); - kaonrot = ROOT::Math::PxPyPzMVector(rotkaonPx, rotkaonPy, track1.pz(), massKa); + kaonrot = ROOT::Math::PxPyPzMVector(rotkaonPx, rotkaonPy, track1.pz(), MassKa); kstarrot = kaonrot + daughter2; - if (TMath::Abs(kstarrot.Rapidity()) > confRapidity) { + if (std::abs(kstarrot.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaonrot, daughter2)) { // phi(1020) veto (cfgPhiVeto) on the rotated pair continue; } - auto phiminuspsiRot = GetPhiInRange(kstarrot.Phi() - psiFT0C); + auto phiMinusPsiRot = getPhiInRange(kstarrot.Phi() - psiFT0C); if (useSP) { - v2Rot = TMath::Cos(2.0 * phiminuspsiRot) * QFT0C; + v2Rot = std::cos(2.0 * phiMinusPsiRot) * qFT0C; } if (!useSP) { - v2Rot = TMath::Cos(2.0 * phiminuspsiRot); + v2Rot = std::cos(2.0 * phiMinusPsiRot); } if (!fillSA) { histos.fill(HIST("hSparseV2SASameEventRotational_V2"), kstarrot.M(), kstarrot.Pt(), v2Rot, centrality); @@ -715,13 +1197,15 @@ struct kstarpbpb { fourVecDauCMrot = boost(kaonrot); threeVecDauCMrot = fourVecDauCMrot.Vect(); threeVecDauCMXYrot = ROOT::Math::XYZVector(threeVecDauCMrot.X(), threeVecDauCMrot.Y(), 0.); - auto cosPhistarminuspsirot = GetPhiInRange(fourVecDauCMrot.Phi() - psiFT0C); - auto SArot = TMath::Cos(2.0 * cosPhistarminuspsirot); - auto cosThetaStarrot = eventplaneVecNorm.Dot(threeVecDauCMrot) / std::sqrt(threeVecDauCMrot.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + // production-plane axis must follow the rotated candidate + auto saAxisRot = getSAAxis(kstarrot, psiSA); + auto cosPhistarminuspsirot = getPhiInRange(fourVecDauCMrot.Phi() - getSAPlaneAngle(kstarrot, psiSA)); + auto sarot = std::cos(2.0 * cosPhistarminuspsirot); + auto cosThetaStarrot = saAxisRot.Dot(threeVecDauCMrot) / std::sqrt(threeVecDauCMrot.Mag2()) / std::sqrt(saAxisRot.Mag2()); if (usepolar) { histos.fill(HIST("hSparseSAvsraprot"), kstarrot.M(), kstarrot.Pt(), cosThetaStarrot, kstarrot.Rapidity(), centrality); } else { - histos.fill(HIST("hSparseSAvsraprot"), kstarrot.M(), kstarrot.Pt(), SArot, kstarrot.Rapidity(), centrality); + histos.fill(HIST("hSparseSAvsraprot"), kstarrot.M(), kstarrot.Pt(), sarot, kstarrot.Rapidity(), centrality); } } } @@ -730,509 +1214,114 @@ struct kstarpbpb { } } } - PROCESS_SWITCH(kstarpbpb, processSE, "Process Same event latest", true); - /* - void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& , aod::BCsWithTimestamps const&) + PROCESS_SWITCH(Kstarpbpb, processSE, "Process Same event latest", true); + + void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { - if (!collision.sel8()) { - return; - } - auto centrality = collision.centFT0C(); - auto multTPC = collision.multNTracksPV(); - auto QFT0C = collision.qFT0C(); - if (!collision.triggereventep()) { - return; - } - if (timFrameEvsel && (!collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - return; - } - if (additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - return; - } - if (additionalEvSel3 && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) { - return; - } - if (additionalEvselITS && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - return; - } - int occupancy = collision.trackOccupancyInTimeRange(); - auto posThisColl = posTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - auto negThisColl = negTracks->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - auto psiFT0C = collision.psiFT0C(); - auto psiFT0A = collision.psiFT0A(); - auto psiTPC = collision.psiTPC(); - if (fillOccupancy && occupancy >= cfgOccupancyCut) // occupancy info is available for this collision (*) - { - return; - } - if (additionalEvsel && !eventSelected(collision, centrality)) { - return; - } - if (additionalQAplots1) { - histos.fill(HIST("hFTOCvsTPCSelected"), centrality, multTPC); - histos.fill(HIST("hPsiFT0C"), centrality, psiFT0C); - histos.fill(HIST("hPsiFT0A"), centrality, psiFT0A); - histos.fill(HIST("hPsiTPC"), centrality, psiTPC); - histos.fill(HIST("ResFT0CTPC"), centrality, TMath::Cos(2.0 * (psiFT0C - psiTPC))); - histos.fill(HIST("ResFT0CFT0A"), centrality, TMath::Cos(2.0 * (psiFT0C - psiFT0A))); - histos.fill(HIST("ResFT0ATPC"), centrality, TMath::Cos(2.0 * (psiTPC - psiFT0A))); - histos.fill(HIST("hCentrality"), centrality); - histos.fill(HIST("hOccupancy"), occupancy); - histos.fill(HIST("hVtxZ"), collision.posZ()); - } - for (auto track1 : posThisColl) { - if (!selectionTrack(track1)) { - continue; - } - if (additionalQAplots) { - histos.fill(HIST("QAbefore/TPC_Nsigma_allka"), track1.pt(), track1.tpcNSigmaKa(), centrality); - histos.fill(HIST("QAbefore/TOF_Nsigma_allka"), track1.pt(), track1.tofNSigmaKa(), centrality); - histos.fill(HIST("QAbefore/trkDCAxyka"), track1.dcaXY()); - histos.fill(HIST("QAbefore/trkDCAzka"), track1.dcaZ()); - histos.fill(HIST("QAbefore/TOF_TPC_Mapka_allka"), track1.tofNSigmaKa(), track1.tpcNSigmaKa()); - } + // scratch vectors: local to this call, never carry state across pairs + ROOT::Math::PxPyPzMVector kstarMother, fourVecDauCM, daughter1, daughter2; + ROOT::Math::XYZVector threeVecDauCM, threeVecDauCMXY, eventplaneVecNorm; + double v2 = 0.; - bool track1pion = false; - bool track1kaon = false; - if (ispTdepPID && !isTOFOnly && !(selectionPIDNew(track1, 0) || selectionPIDNew(track1, 1))) { - continue; - } - if (!ispTdepPID && !isTOFOnly && !(selectionPID(track1, 0) || selectionPID(track1, 1))) { - continue; - } - if (isTOFOnly && !(selectionPID2(track1, 0) || selectionPID2(track1, 1))) { + auto tracksTuple = std::make_tuple(tracks); + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionEventPairME(collision1, collision2)) { continue; } - auto track1ID = track1.globalIndex(); - for (auto track2 : negThisColl) { - bool track2pion = false; - bool track2kaon = false; - if (!selectionTrack(track2)) { - continue; - } - if (additionalQAplots) { - histos.fill(HIST("QAbefore/TOF_TPC_Mapka_allpi"), track2.tofNSigmaPi(), track2.tpcNSigmaPi()); - histos.fill(HIST("QAbefore/TPC_Nsigma_allpi"), track2.pt(), track2.tpcNSigmaPi(), centrality); - histos.fill(HIST("QAbefore/TOF_Nsigma_allpi"), track2.pt(), track2.tofNSigmaPi(), centrality); - histos.fill(HIST("QAbefore/trkDCAxypi"), track2.dcaXY()); - histos.fill(HIST("QAbefore/trkDCAzpi"), track2.dcaZ()); - } - if (ispTdepPID && !isTOFOnly && !(selectionPIDNew(track2, 0) || selectionPIDNew(track2, 1))) { + auto centrality = collision1.centFT0C(); + auto psiFT0C1 = collision1.psiFT0C(); + auto qFT0C1 = collision1.qFT0C(); + auto psiFT0C2 = collision2.psiFT0C(); + auto qFT0C2 = collision2.qFT0C(); + auto psiSA1 = getSAEventAngle(psiFT0C1); + + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + + if (!selectionTrack(track1) || !selectionTrack(track2)) { continue; } - if (!ispTdepPID && !isTOFOnly && !(selectionPID(track2, 0) || selectionPID(track2, 1))) { + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { continue; } - if (isTOFOnly && !(selectionPID2(track2, 0) || selectionPID2(track2, 1))) { + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { continue; } - auto track2ID = track2.globalIndex(); - if (track2ID == track1ID) { + if (isTOFOnly && !selectionPID2(track1, 0)) { continue; } - if (track1.sign() * track2.sign() > 0) { + if (isTOFOnly && !selectionPID2(track2, 1)) { continue; } - if (ispTdepPID && !isTOFOnly) { - if (selectionPIDNew(track1, 1) && selectionPIDNew(track2, 0)) { - track1pion = true; - track2kaon = true; - if (removefaketrak && isFakeKaon(track2, 0)) { - continue; - } - } - if (selectionPIDNew(track2, 1) && selectionPIDNew(track1, 0)) { - track2pion = true; - track1kaon = true; - if (removefaketrak && isFakeKaon(track1, 0)) { - continue; - } - } - } - if (!ispTdepPID && !isTOFOnly) { - if (selectionPID(track1, 1) && selectionPID(track2, 0)) { - track1pion = true; - track2kaon = true; - if (removefaketrak && isFakeKaon(track2, 0)) { - continue; - } - } - if (selectionPID(track2, 1) && selectionPID(track1, 0)) { - track2pion = true; - track1kaon = true; - if (removefaketrak && isFakeKaon(track1, 0)) { - continue; - } - } - } - if (isTOFOnly) { - if (selectionPID2(track1, 1) && selectionPID2(track2, 0)) { - track1pion = true; - track2kaon = true; - if (removefaketrak && isFakeKaon(track2, 0)) { - continue; - } - } - if (selectionPID2(track2, 1) && selectionPID2(track1, 0)) { - track2pion = true; - track1kaon = true; - if (removefaketrak && isFakeKaon(track1, 0)) { - continue; - } - } - } - if (same) { - if (track1kaon && track2pion) { - if (additionalQAplots) { - histos.fill(HIST("QAafter/TPC_Nsigma_allka"), track1.pt(), track1.tpcNSigmaKa(), centrality); - histos.fill(HIST("QAafter/TOF_Nsigma_allka"), track1.pt(), track1.tofNSigmaKa(), centrality); - histos.fill(HIST("QAafter/trkDCAxyka"), track1.dcaXY()); - histos.fill(HIST("QAafter/trkDCAzka"), track1.dcaZ()); - histos.fill(HIST("QAafter/TOF_TPC_Mapka_allka"), track1.tofNSigmaKa(), track1.tpcNSigmaKa()); - histos.fill(HIST("QAafter/TOF_TPC_Mapka_allpi"), track2.tofNSigmaPi(), track2.tpcNSigmaPi()); - histos.fill(HIST("QAafter/TPC_Nsigma_allpi"), track2.pt(), track2.tpcNSigmaPi(), centrality); - histos.fill(HIST("QAafter/TOF_Nsigma_allpi"), track2.pt(), track2.tofNSigmaPi(), centrality); - histos.fill(HIST("QAafter/trkDCAxypi"), track2.dcaXY()); - histos.fill(HIST("QAafter/trkDCAzpi"), track2.dcaZ()); - } - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - } else if (track1pion && track2kaon) { - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massPi); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - } else { - continue; - } - - KstarMother = daughter1 + daughter2; - if (TMath::Abs(KstarMother.Rapidity()) > confRapidity) { - continue; - } - auto phiminuspsi = GetPhiInRange(KstarMother.Phi() - psiFT0C); - - if (useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi) * QFT0C; - } - if (!useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi); - } - - histos.fill(HIST("hSparseV2SASameEvent_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality); - } - if (fillRotation) { - for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { - auto anglestart = confMinRot; - auto angleend = confMaxRot; - auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); - auto rotangle = anglestart + nrotbkg * anglestep; - histos.fill(HIST("hRotation"), rotangle); - if (track1kaon && track2pion) { - auto rotkaonPx = track1.px() * std::cos(rotangle) - track1.py() * std::sin(rotangle); - auto rotkaonPy = track1.px() * std::sin(rotangle) + track1.py() * std::cos(rotangle); - kaonrot = ROOT::Math::PxPyPzMVector(rotkaonPx, rotkaonPy, track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - } else if (track1pion && track2kaon) { - auto rotkaonPx = track2.px() * std::cos(rotangle) - track2.py() * std::sin(rotangle); - auto rotkaonPy = track2.px() * std::sin(rotangle) + track2.py() * std::cos(rotangle); - kaonrot = ROOT::Math::PxPyPzMVector(rotkaonPx, rotkaonPy, track2.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massPi); - } else { - continue; - } - kstarrot = kaonrot + daughter2; - if (TMath::Abs(kstarrot.Rapidity()) > confRapidity) { - continue; - } - auto phiminuspsiRot = GetPhiInRange(kstarrot.Phi() - psiFT0C); - - if (useSP) { - v2Rot = TMath::Cos(2.0 * phiminuspsiRot) * QFT0C; - } - if (!useSP) { - v2Rot = TMath::Cos(2.0 * phiminuspsiRot); - } - - histos.fill(HIST("hSparseV2SASameEventRotational_V2"), kstarrot.M(), kstarrot.Pt(), v2Rot, centrality); - } - } - } - } - } - PROCESS_SWITCH(kstarpbpb, processSameEvent, "Process Same event", false); - - void processlikeEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) - { - if (!collision.sel8()) { - return; - } - auto centrality = collision.centFT0C(); - auto QFT0C = collision.qFT0C(); - if (!collision.triggereventep()) { - return; - } - if (timFrameEvsel && (!collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - return; - } - if (additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - return; - } - if (additionalEvSel3 && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) { - return; - } - if (additionalEvselITS && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - return; - } - int occupancy = collision.trackOccupancyInTimeRange(); - auto psiFT0C = collision.psiFT0C(); - if (fillOccupancy && occupancy >= cfgOccupancyCut) // occupancy info is available for this collision (*) - { - return; - } + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); - if (additionalEvsel && !eventSelected(collision, centrality)) { - return; - } - for (auto track1 : tracks) { - if (!selectionTrack(track1)) { - continue; - } - bool track1pion = false; - bool track1kaon = false; - if (ispTdepPID && !isTOFOnly && !(selectionPIDNew(track1, 0) || selectionPIDNew(track1, 1))) { - continue; - } - if (!ispTdepPID && !isTOFOnly && !(selectionPID(track1, 0) || selectionPID(track1, 1))) { - continue; - } - if (isTOFOnly && !(selectionPID2(track1, 0) || selectionPID2(track1, 1))) { - continue; - } - for (auto track2 : tracks) { - bool track2pion = false; - bool track2kaon = false; - if (!selectionTrack(track2)) { - continue; - } - if (ispTdepPID && !isTOFOnly && !(selectionPIDNew(track2, 0) || selectionPIDNew(track2, 1))) { - continue; - } - if (!ispTdepPID && !isTOFOnly && !(selectionPID(track2, 0) || selectionPID(track2, 1))) { - continue; - } - if (isTOFOnly && !(selectionPID2(track2, 0) || selectionPID2(track2, 1))) { + kstarMother = daughter1 + daughter2; + if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; } - if (track1.sign() * track2.sign() < 0) { + if (isPhiReflection(daughter1, daughter2)) { // phi(1020) veto (cfgPhiVeto), same as in the same event continue; } - if (ispTdepPID && !isTOFOnly) { - if (selectionPIDNew(track1, 1) && selectionPIDNew(track2, 0)) { - track1pion = true; - track2kaon = true; - if (removefaketrak && isFakeKaon(track2, 0)) { - continue; - } - } - if (selectionPIDNew(track2, 1) && selectionPIDNew(track1, 0)) { - track2pion = true; - track1kaon = true; - if (removefaketrak && isFakeKaon(track1, 0)) { - continue; - } - } - } - if (!ispTdepPID && !isTOFOnly) { - if (selectionPID(track1, 1) && selectionPID(track2, 0)) { - track1pion = true; - track2kaon = true; - if (removefaketrak && isFakeKaon(track2, 0)) { - continue; - } - } - if (selectionPID(track2, 1) && selectionPID(track1, 0)) { - track2pion = true; - track1kaon = true; - if (removefaketrak && isFakeKaon(track1, 0)) { - continue; - } - } - } - if (isTOFOnly) { - if (selectionPID2(track1, 1) && selectionPID2(track2, 0)) { - track1pion = true; - track2kaon = true; - if (removefaketrak && isFakeKaon(track2, 0)) { - continue; - } - } - if (selectionPID2(track2, 1) && selectionPID2(track1, 0)) { - track2pion = true; - track1kaon = true; - if (removefaketrak && isFakeKaon(track1, 0)) { - continue; - } - } - } - if (track1kaon && track2pion) { - if (track1.sign() < 0 && track2.sign() < 0) { + int s1 = track1.sign(); + int s2 = track2.sign(); - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + if (s1 * s2 < 0) { - KstarMother = daughter1 + daughter2; - if (TMath::Abs(KstarMother.Rapidity()) > confRapidity) { - continue; - } - auto phiminuspsi = GetPhiInRange(KstarMother.Phi() - psiFT0C); + auto phi1 = track1.phi(); + auto phi2 = track2.phi(); + auto phiKstar = kstarMother.Phi(); - if (useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi) * QFT0C; - } - if (!useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi); - } - histos.fill(HIST("hSparseV2SAlikeEventNN_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality); - } - } else if (track1pion && track2kaon) { - if (track1.sign() > 0 && track2.sign() > 0) { - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massPi); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - KstarMother = daughter1 + daughter2; - if (TMath::Abs(KstarMother.Rapidity()) > confRapidity) { - continue; - } - auto phiminuspsi = GetPhiInRange(KstarMother.Phi() - psiFT0C); + double term1 = qFT0C1 * std::cos(2.0 * getPhiInRange(phi1 - psiFT0C1)) * std::cos(2.0 * getPhiInRange(phi1 - phiKstar)); + double term2 = qFT0C2 * std::cos(2.0 * getPhiInRange(phi2 - psiFT0C2)) * std::cos(2.0 * getPhiInRange(phi2 - phiKstar)); - if (useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi) * QFT0C; + v2 = term1 + term2; + + if (!fillSA) { + histos.fill(HIST("hSparseV2SAMixedEvent_V2"), kstarMother.M(), kstarMother.Pt(), v2, centrality); + } + histos.fill(HIST("hMassMixedEventUnlike"), kstarMother.M(), centrality); + + if (fillSA) { + ROOT::Math::Boost boost{kstarMother.BoostToCM()}; + fourVecDauCM = boost(daughter1); + threeVecDauCM = fourVecDauCM.Vect(); + threeVecDauCMXY = ROOT::Math::XYZVector(threeVecDauCM.X(), threeVecDauCM.Y(), 0.); + eventplaneVecNorm = getSAAxis(kstarMother, psiSA1); + auto cosPhistarminuspsi = getPhiInRange(fourVecDauCM.Phi() - getSAPlaneAngle(kstarMother, psiSA1)); + auto sa = std::cos(2.0 * cosPhistarminuspsi); + auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); + if (doClosureTest) { + histos.fill(HIST("closure/KstarMixedEventCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, std::abs(kstarMother.Rapidity()), centrality); } - if (!useSP) { - v2 = TMath::Cos(2.0 * phiminuspsi); + if (usepolar) { + histos.fill(HIST("hSparseSAvsrapmix"), kstarMother.M(), kstarMother.Pt(), cosThetaStar, kstarMother.Rapidity(), centrality); + } else { + histos.fill(HIST("hSparseSAvsrapmix"), kstarMother.M(), kstarMother.Pt(), sa, kstarMother.Rapidity(), centrality); } - - histos.fill(HIST("hSparseV2SAlikeEventPP_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality); } - } - } - } - } - - PROCESS_SWITCH(kstarpbpb, processlikeEvent, "Process like event", false); - */ - - void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) - { - - auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisOccup}, true}; - SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (auto& [collision1, tracks1, collision2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(collision1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctCut.rctChecker(collision2)) { - continue; - } - if (!collision1.sel8() || !collision1.triggereventep() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision1.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - continue; - } - if (!collision2.sel8() || !collision2.triggereventep() || !collision2.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision2.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision2.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision2.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { - continue; - } - if (collision1.bcId() == collision2.bcId()) { - continue; - } - int occupancy1 = collision1.trackOccupancyInTimeRange(); - int occupancy2 = collision2.trackOccupancyInTimeRange(); - if (fillOccupancy && occupancy1 >= cfgOccupancyCut && occupancy2 >= cfgOccupancyCut) // occupancy info is available for this collision (*) - { - continue; - } - auto centrality = collision1.centFT0C(); - auto centrality2 = collision2.centFT0C(); - auto psiFT0C = collision1.psiFT0C(); - auto QFT0C = collision1.qFT0C(); - if (additionalEvsel && !eventSelected(collision1, centrality)) { - continue; - } - if (additionalEvsel && !eventSelected(collision2, centrality2)) { - continue; - } - if (additionalEvselITS && !collision1.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - continue; - } - if (additionalEvselITS && !collision2.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - continue; - } - - for (auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - if (track1.sign() * track2.sign() > 0) { - continue; - } - if (!selectionTrack(track1) || !selectionTrack(track2)) { - - continue; - } - if (ispTdepPID && !isTOFOnly && !(selectionPIDNew(track1, 0))) { - continue; - } - if (ispTdepPID && !isTOFOnly && !(selectionPIDNew(track2, 1))) { - continue; - } - if (!ispTdepPID && !isTOFOnly && !(selectionPID(track1, 0))) { - continue; - } - if (!ispTdepPID && !isTOFOnly && !(selectionPID(track2, 1))) { - continue; - } - if (isTOFOnly && !selectionPID2(track1, 0)) { - continue; - } - if (isTOFOnly && !selectionPID2(track2, 1)) { - continue; - } - // if (track1.sign() > 0 && track2.sign() < 0) { - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - /*} else if (track1.sign() < 0 && track2.sign() > 0) { - daughter2 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter1 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - }*/ - KstarMother = daughter1 + daughter2; - if (TMath::Abs(KstarMother.Rapidity()) > confRapidity) { - continue; - } - auto phiminuspsi = GetPhiInRange(KstarMother.Phi() - psiFT0C); + } else { - v2 = TMath::Cos(2.0 * phiminuspsi) * QFT0C; - if (!fillSA) { - if (track1.sign() * track2.sign() < 0) - histos.fill(HIST("hSparseV2SAMixedEvent_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality); - } - if (fillSA) { - ROOT::Math::Boost boost{KstarMother.BoostToCM()}; - fourVecDauCM = boost(daughter1); - threeVecDauCM = fourVecDauCM.Vect(); - threeVecDauCMXY = ROOT::Math::XYZVector(threeVecDauCM.X(), threeVecDauCM.Y(), 0.); - eventplaneVec = ROOT::Math::XYZVector(std::cos(2.0 * psiFT0C), std::sin(2.0 * psiFT0C), 0); - eventplaneVecNorm = ROOT::Math::XYZVector(std::sin(2.0 * psiFT0C), -std::cos(2.0 * psiFT0C), 0); - auto cosPhistarminuspsi = GetPhiInRange(fourVecDauCM.Phi() - psiFT0C); - auto SA = TMath::Cos(2.0 * cosPhistarminuspsi); - auto cosThetaStar = eventplaneVecNorm.Dot(threeVecDauCM) / std::sqrt(threeVecDauCM.Mag2()) / std::sqrt(eventplaneVecNorm.Mag2()); - if (usepolar) { - histos.fill(HIST("hSparseSAvsrapmix"), KstarMother.M(), KstarMother.Pt(), cosThetaStar, KstarMother.Rapidity(), centrality); + if (s1 > 0) { + histos.fill(HIST("hMassMixedEventLikePP"), kstarMother.M(), centrality); } else { - histos.fill(HIST("hSparseSAvsrapmix"), KstarMother.M(), KstarMother.Pt(), SA, KstarMother.Rapidity(), centrality); + histos.fill(HIST("hMassMixedEventLikeNN"), kstarMother.M(), centrality); } } } } } - PROCESS_SWITCH(kstarpbpb, processMixedEvent, "Process Mixed event", true); - void processMC(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + PROCESS_SWITCH(Kstarpbpb, processMixedEvent, "Process Mixed event", true); + + void processMC(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { + // scratch vectors: local to this call, never carry state across particles + ROOT::Math::PxPyPzMVector kstarMother, kaonPlus, pionMinus; + double v2 = 0.; + histos.fill(HIST("hMC"), 0); if (RecCollisions.size() == 0) { histos.fill(HIST("hMC"), 1); @@ -1242,54 +1331,57 @@ struct kstarpbpb { histos.fill(HIST("hMC"), 2); return; } - for (auto& RecCollision : RecCollisions) { - auto psiFT0C = 0.0; + for (const auto& RecCollision : RecCollisions) { + auto psiFT0C = getMCEffPsi(TrueCollision, GenParticles); // one Psi per MC event (mcEffEP.psiSource; 0 = old fixed Psi = 0) histos.fill(HIST("hMC"), 3); if (!RecCollision.sel8()) { histos.fill(HIST("hMC"), 4); continue; } - - if (!RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { - histos.fill(HIST("hMC"), 5); + if (!selectionEventBits(RecCollision)) { continue; } - if (TMath::Abs(RecCollision.posZ()) > cfgCutVertex) { + histos.fill(HIST("hMC"), 5); + if (std::abs(RecCollision.posZ()) > cfgCutVertex) { histos.fill(HIST("hMC"), 6); continue; } histos.fill(HIST("hMC"), 7); auto centrality = RecCollision.centFT0C(); histos.fill(HIST("CentPercentileMCRecHist"), centrality); + auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event + histos.fill(HIST("hMCEffPsi"), centrality, psiSA); auto oldindex = -999; - auto Rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); + auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle - for (auto track1 : Rectrackspart) { + for (const auto& track1 : rectrackspart) { if (!selectionTrack(track1)) { continue; } - if (ispTdepPID && !(selectionPIDNew(track1, 0))) { + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { continue; } - if (!ispTdepPID && !(selectionPID(track1, 0))) { + if (isTOFOnly && !selectionPID2(track1, 0)) { continue; } if (!track1.has_mcParticle()) { continue; } auto track1ID = track1.index(); - for (auto track2 : Rectrackspart) { + for (const auto& track2 : rectrackspart) { auto track2ID = track2.index(); - if (track2ID <= track1ID) { + // roles are fixed (track1 = kaon candidate, track2 = pion candidate, checked against the MC PDG below), + // so every true K pi pair is found exactly once whatever the track order; only skip the same track + if (track2ID == track1ID) { continue; } if (!selectionTrack(track2)) { continue; } - if (ispTdepPID && !(selectionPIDNew(track2, 1))) { + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { continue; } - if (!ispTdepPID && !(selectionPID(track2, 1))) { + if (isTOFOnly && !selectionPID2(track2, 1)) { continue; } if (!track2.has_mcParticle()) { @@ -1300,156 +1392,192 @@ struct kstarpbpb { } const auto mctrack1 = track1.mcParticle(); const auto mctrack2 = track2.mcParticle(); - int track1PDG = TMath::Abs(mctrack1.pdgCode()); - int track2PDG = TMath::Abs(mctrack2.pdgCode()); + int track1PDG = std::abs(mctrack1.pdgCode()); + int track2PDG = std::abs(mctrack2.pdgCode()); if (!mctrack1.isPhysicalPrimary()) { continue; } if (!mctrack2.isPhysicalPrimary()) { continue; } - if (!(track1PDG == 321 && track2PDG == 211)) { + if (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kPiPlus) { continue; } - for (auto& mothertrack1 : mctrack1.mothers_as()) { - for (auto& mothertrack2 : mctrack2.mothers_as()) { + for (const auto& mothertrack1 : mctrack1.mothers_as()) { + for (const auto& mothertrack2 : mctrack2.mothers_as()) { if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { continue; } if (mothertrack1 != mothertrack2) { continue; } - if (TMath::Abs(mothertrack1.y()) > confRapidity) { + if (std::abs(mothertrack1.y()) > confRapidity) { continue; } - if (PDGcheck && TMath::Abs(mothertrack1.pdgCode()) != 313) { + // K*0 and anti-K*0, as in data (all unlike-sign K pi pairs) + if (pdgcheck && std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } - if (ispTdepPID && !(selectionPIDNew(track1, 0) || selectionPIDNew(track2, 1))) { + if (!isTOFOnly && !(strategySelectionPID(track1, 0, strategyPID) || strategySelectionPID(track2, 1, strategyPID))) { continue; } - if (!ispTdepPID && !(selectionPID(track1, 0) || selectionPID(track2, 1))) { + if (isTOFOnly && !(selectionPID2(track1, 0) || selectionPID2(track2, 1))) { continue; } if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { continue; } oldindex = mothertrack1.globalIndex(); - if (track1.sign() > 0 && track2.sign() < 0) { - KaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - PionMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); - } - if (track1.sign() < 0 && track2.sign() > 0) { - PionMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - KaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + // track1 is always the kaon-PID candidate and track2 always the pion-PID candidate (enforced above); + // assign unconditionally so the boost below always uses the true kaon, regardless of its charge sign + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pionMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstarMother = kaonPlus + pionMinus; + if (std::abs(kstarMother.Rapidity()) > confRapidity) { + continue; } - KstarMother = KaonPlus + PionMinus; - if (TMath::Abs(KstarMother.Rapidity()) > confRapidity) { + if (isPhiReflection(kaonPlus, pionMinus)) { // phi(1020) veto (cfgPhiVeto), so the efficiency includes the true K* it removes continue; } - auto phiminuspsi = GetPhiInRange(KstarMother.Phi() - psiFT0C); - - v2 = TMath::Cos(2.0 * phiminuspsi); - - histos.fill(HIST("hSparseV2SARec_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality); - histos.fill(HIST("h2PhiRec2"), KstarMother.pt(), centrality); - histos.fill(HIST("hpt"), KstarMother.Pt()); + auto phiMinusPsi = getPhiInRange(kstarMother.Phi() - psiFT0C); + + v2 = std::cos(2.0 * phiMinusPsi); + + histos.fill(HIST("hSparseV2SARec_V2"), kstarMother.M(), kstarMother.Pt(), v2, centrality); + histos.fill(HIST("h2PhiRec2"), kstarMother.pt(), centrality); + histos.fill(HIST("hpt"), kstarMother.Pt()); + + { + ROOT::Math::Boost boost{kstarMother.BoostToCM()}; + auto fourVecDauCMRec = boost(kaonPlus); + auto threeVecDauCMRec = fourVecDauCMRec.Vect(); + auto eventplaneVecNormRec = getSAAxis(kstarMother, psiSA); + auto cosPhistarminuspsiRec = getPhiInRange(fourVecDauCMRec.Phi() - getSAPlaneAngle(kstarMother, psiSA)); + auto saRec = std::cos(2.0 * cosPhistarminuspsiRec); + auto cosThetaStarRec = eventplaneVecNormRec.Dot(threeVecDauCMRec) / std::sqrt(threeVecDauCMRec.Mag2()) / std::sqrt(eventplaneVecNormRec.Mag2()); + + histos.fill(HIST("hSparseKstarMCRecSA"), kstarMother.M(), kstarMother.Pt(), saRec, std::abs(kstarMother.Rapidity()), centrality); + histos.fill(HIST("hSparseKstarMCRecCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("hSparseKstarMCRecCosThetaStarPhiVPsi"), kstarMother.Pt(), cosThetaStarRec, getPhiInRange(kstarMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/KstarMCRecTrueCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarRec, std::abs(kstarMother.Rapidity()), centrality); + } + } } } } } // loop over generated particle - for (auto& mcParticle : GenParticles) { - if (TMath::Abs(mcParticle.y()) > confRapidity) { + for (const auto& mcParticle : GenParticles) { + if (std::abs(mcParticle.y()) > confRapidity) { continue; } - if (PDGcheck && mcParticle.pdgCode() != 313) { + // K*0 and anti-K*0, as in data and in the reconstructed loop above + if (pdgcheck && std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } auto kDaughters = mcParticle.daughters_as(); - if (kDaughters.size() != 2) { + static constexpr std::size_t NumberOfDaughters = 2; + + if (kDaughters.size() != NumberOfDaughters) { continue; } + // daughtp = kaon daughter found, daughtm = pion daughter found (either charge: K+ pi- or K- pi+); + // kaonPlus / pionMinus hold the kaon / pion daughter, so the boost uses the kaon as in the reconstructed loop auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } - if (kCurrentDaughter.pdgCode() == +321) { - if (genacceptancecut && kCurrentDaughter.pt() > cfgCutPT && TMath::Abs(kCurrentDaughter.eta()) < cfgCutEta) { + if (std::abs(kCurrentDaughter.pdgCode()) == PDG_t::kKPlus) { + if (genacceptancecut && kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtp = true; } if (!genacceptancecut) { daughtp = true; } - KaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); - } else if (kCurrentDaughter.pdgCode() == -211) { - if (genacceptancecut && kCurrentDaughter.pt() > cfgCutPT && TMath::Abs(kCurrentDaughter.eta()) < cfgCutEta) { + kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); + } else if (std::abs(kCurrentDaughter.pdgCode()) == PDG_t::kPiPlus) { + if (genacceptancecut && kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtm = true; } if (!genacceptancecut) { daughtm = true; } - PionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massPi); + pionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassPi); } } if (daughtp && daughtm) { - KstarMother = KaonPlus + PionMinus; - if (TMath::Abs(KstarMother.Rapidity()) > confRapidity) { + kstarMother = kaonPlus + pionMinus; + if (std::abs(kstarMother.Rapidity()) > confRapidity) { continue; } - auto phiminuspsi = GetPhiInRange(KstarMother.Phi() - psiFT0C); - - v2 = TMath::Cos(2.0 * phiminuspsi); - - histos.fill(HIST("hSparseV2SAGen_V2"), KstarMother.M(), KstarMother.Pt(), v2, centrality); - histos.fill(HIST("h2PhiGen2"), KstarMother.pt(), centrality); + auto phiMinusPsi = getPhiInRange(kstarMother.Phi() - psiFT0C); + + v2 = std::cos(2.0 * phiMinusPsi); + + histos.fill(HIST("hSparseV2SAGen_V2"), kstarMother.M(), kstarMother.Pt(), v2, centrality); + histos.fill(HIST("h2PhiGen2"), kstarMother.pt(), centrality); + + { + ROOT::Math::Boost boost{kstarMother.BoostToCM()}; + auto fourVecDauCMGen = boost(kaonPlus); + auto threeVecDauCMGen = fourVecDauCMGen.Vect(); + auto eventplaneVecNormGen = getSAAxis(kstarMother, psiSA); + auto cosPhistarminuspsiGen = getPhiInRange(fourVecDauCMGen.Phi() - getSAPlaneAngle(kstarMother, psiSA)); + auto saGen = std::cos(2.0 * cosPhistarminuspsiGen); + auto cosThetaStarGen = eventplaneVecNormGen.Dot(threeVecDauCMGen) / std::sqrt(threeVecDauCMGen.Mag2()) / std::sqrt(eventplaneVecNormGen.Mag2()); + + histos.fill(HIST("hSparseKstarMCGenSA"), kstarMother.M(), kstarMother.Pt(), saGen, std::abs(kstarMother.Rapidity()), centrality); + histos.fill(HIST("hSparseKstarMCGenCosThetaStar_effy"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("hSparseKstarMCGenCosThetaStarPhiVPsi"), kstarMother.Pt(), cosThetaStarGen, getPhiInRange(kstarMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/KstarMCGenCosThetaStar"), kstarMother.M(), kstarMother.Pt(), cosThetaStarGen, std::abs(kstarMother.Rapidity()), centrality); + } + } } } } // rec collision loop } // process MC - PROCESS_SWITCH(kstarpbpb, processMC, "Process MC", false); + PROCESS_SWITCH(Kstarpbpb, processMC, "Process MC", false); void processMCkstarWeight(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { + // scratch vectors: local to this call, never carry state across particles + ROOT::Math::PxPyPzMVector kaonPlus, pionMinus; + float imp = TrueCollision.impactParameter(); float evPhi = TrueCollision.eventPlaneAngle() / 2.0; - float centclass = -999; - if (imp >= 0 && imp < 3.49) { - centclass = 2.5; - } - if (imp >= 3.49 && imp < 4.93) { - centclass = 7.5; - } - if (imp >= 4.93 && imp < 6.98) { - centclass = 15.0; - } - if (imp >= 6.98 && imp < 8.55) { - centclass = 25.0; + static constexpr std::array CentEdges = { + 0.0f, 3.49f, 4.93f, 6.98f, 8.55f, + 9.87f, 11.0f, 12.1f, 13.1f, 14.0f}; + + static constexpr std::array CentValues = { + 2.5f, 7.5f, 15.0f, 25.0f, 35.0f, + 45.0f, 55.0f, 65.0f, 75.0f}; + float centclass = -999.f; + + for (size_t i = 0; i < CentValues.size(); ++i) { + if (imp >= CentEdges[i] && imp < CentEdges[i + 1]) { + centclass = CentValues[i]; + break; + } } - if (imp >= 8.55 && imp < 9.87) { - centclass = 35.0; + histos.fill(HIST("hImpactParameter"), imp); + histos.fill(HIST("hEventPlaneAngle"), evPhi); + static constexpr float MinCentrality = 0.0f; + static constexpr float MaxCentrality = 80.0f; + + if (centclass < MinCentrality || centclass > MaxCentrality) { + return; } - if (imp >= 9.87 && imp < 11) { - centclass = 45.0; - } - if (imp >= 11 && imp < 12.1) { - centclass = 55.0; - } - if (imp >= 12.1 && imp < 13.1) { - centclass = 65.0; - } - if (imp >= 13.1 && imp < 14) { - centclass = 75.0; - } - histos.fill(HIST("hImpactParameter"), imp); - histos.fill(HIST("hEventPlaneAngle"), evPhi); - if (centclass < 0.0 || centclass > 80.0) { - return; - } - for (auto& RecCollision : RecCollisions) { + for (const auto& RecCollision : RecCollisions) { auto psiFT0C = TrueCollision.eventPlaneAngle(); /* if (!RecCollision.sel8()) { @@ -1459,33 +1587,33 @@ struct kstarpbpb { continue; } */ - if (TMath::Abs(RecCollision.posZ()) > cfgCutVertex) { + if (std::abs(RecCollision.posZ()) > cfgCutVertex) { continue; } auto oldindex = -999; - auto Rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); + auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle - for (auto track1 : Rectrackspart) { + for (const auto& track1 : rectrackspart) { if (!track1.has_mcParticle()) { continue; } const auto mctrack1 = track1.mcParticle(); - if (selectionTrack(track1) && strategySelectionPID(track1, 0, strategyPID) && TMath::Abs(mctrack1.pdgCode()) == 321 && mctrack1.isPhysicalPrimary()) { - histos.fill(HIST("hSparseKstarMCRecKaonWeight"), centclass, GetPhiInRange(mctrack1.phi() - psiFT0C), TMath::Power(TMath::Cos(2.0 * GetPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); + if (selectionTrack(track1) && strategySelectionPID(track1, 0, strategyPID) && std::abs(mctrack1.pdgCode()) == PDG_t::kKPlus && mctrack1.isPhysicalPrimary()) { + histos.fill(HIST("hSparseKstarMCRecKaonWeight"), centclass, getPhiInRange(mctrack1.phi() - psiFT0C), std::pow(std::cos(2.0 * getPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); } - if (selectionTrack(track1) && track1.pt() > 0.5 && track1.hasTOF() && TMath::Abs(track1.tofNSigmaKa()) > nsigmaCutTOF && TMath::Abs(track1.tpcNSigmaKa()) < nsigmaCutTPC && TMath::Abs(mctrack1.pdgCode()) == 321 && mctrack1.isPhysicalPrimary()) { - histos.fill(HIST("hSparseKstarMCRecKaonMissMatchWeight"), centclass, GetPhiInRange(mctrack1.phi() - psiFT0C), TMath::Power(TMath::Cos(2.0 * GetPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); + if (selectionTrack(track1) && track1.pt() > TPCOnlyPt && track1.hasTOF() && std::abs(track1.tofNSigmaKa()) > nsigmaCutTOF && std::abs(track1.tpcNSigmaKa()) < nsigmaCutTPC && std::abs(mctrack1.pdgCode()) == PDG_t::kKPlus && mctrack1.isPhysicalPrimary()) { + histos.fill(HIST("hSparseKstarMCRecKaonMissMatchWeight"), centclass, getPhiInRange(mctrack1.phi() - psiFT0C), std::pow(std::cos(2.0 * getPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); } - if (selectionTrack(track1) && strategySelectionPID(track1, 1, strategyPID) && TMath::Abs(mctrack1.pdgCode()) == 211 && mctrack1.isPhysicalPrimary()) { - histos.fill(HIST("hSparseKstarMCRecPionWeight"), centclass, GetPhiInRange(mctrack1.phi() - psiFT0C), TMath::Power(TMath::Cos(2.0 * GetPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); + if (selectionTrack(track1) && strategySelectionPID(track1, 1, strategyPID) && std::abs(mctrack1.pdgCode()) == PDG_t::kPiPlus && mctrack1.isPhysicalPrimary()) { + histos.fill(HIST("hSparseKstarMCRecPionWeight"), centclass, getPhiInRange(mctrack1.phi() - psiFT0C), std::pow(std::cos(2.0 * getPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); } - if (selectionTrack(track1) && track1.pt() > 0.5 && track1.hasTOF() && TMath::Abs(track1.tofNSigmaPi()) > nsigmaCutTOF && TMath::Abs(track1.tpcNSigmaPi()) < nsigmaCutTPC && TMath::Abs(mctrack1.pdgCode()) == 211 && mctrack1.isPhysicalPrimary()) { - histos.fill(HIST("hSparseKstarMCRecPionMissMatchWeight"), centclass, GetPhiInRange(mctrack1.phi() - psiFT0C), TMath::Power(TMath::Cos(2.0 * GetPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); + if (selectionTrack(track1) && track1.pt() > TPCOnlyPt && track1.hasTOF() && std::abs(track1.tofNSigmaPi()) > nsigmaCutTOF && std::abs(track1.tpcNSigmaPi()) < nsigmaCutTPC && std::abs(mctrack1.pdgCode()) == PDG_t::kPiPlus && mctrack1.isPhysicalPrimary()) { + histos.fill(HIST("hSparseKstarMCRecPionMissMatchWeight"), centclass, getPhiInRange(mctrack1.phi() - psiFT0C), std::pow(std::cos(2.0 * getPhiInRange(mctrack1.phi() - psiFT0C)), 2.0), mctrack1.pt(), mctrack1.eta()); } auto track1ID = track1.index(); - for (auto track2 : Rectrackspart) { + for (const auto& track2 : rectrackspart) { if (!track2.has_mcParticle()) { continue; } @@ -1494,42 +1622,29 @@ struct kstarpbpb { continue; } const auto mctrack2 = track2.mcParticle(); - int track1PDG = TMath::Abs(mctrack1.pdgCode()); - int track2PDG = TMath::Abs(mctrack2.pdgCode()); + int track1PDG = std::abs(mctrack1.pdgCode()); + int track2PDG = std::abs(mctrack2.pdgCode()); if (!mctrack1.isPhysicalPrimary()) { continue; } if (!mctrack2.isPhysicalPrimary()) { continue; } - if (!(track1PDG == 321 && track2PDG == 211)) { - continue; - } - if (!selectionTrack(track1) || !selectionTrack(track2) || track1.sign() * track2.sign() > 0) { - continue; - } - // PID check - if (ispTdepPID && !isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { - continue; - } - if (!ispTdepPID && !isTOFOnly && (!selectionPID(track1, 0) || !selectionPID(track2, 1))) { - continue; - } - if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { + if (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kPiPlus) { continue; } - for (auto& mothertrack1 : mctrack1.mothers_as()) { - for (auto& mothertrack2 : mctrack2.mothers_as()) { + for (const auto& mothertrack1 : mctrack1.mothers_as()) { + for (const auto& mothertrack2 : mctrack2.mothers_as()) { if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { continue; } if (mothertrack1 != mothertrack2) { continue; } - if (TMath::Abs(mothertrack1.y()) > confRapidity) { + if (std::abs(mothertrack1.y()) > confRapidity) { continue; } - if (TMath::Abs(mothertrack1.pdgCode()) != 313) { + if (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } // if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { @@ -1539,62 +1654,1559 @@ struct kstarpbpb { } // oldindex = mothertrack1.globalIndex(); oldindex = mothertrack1.index(); - auto PhiMinusPsi = GetPhiInRange(mothertrack1.phi() - psiFT0C); - histos.fill(HIST("hSparseKstarMCRecWeight"), centclass, PhiMinusPsi, TMath::Power(TMath::Cos(2.0 * PhiMinusPsi), 2.0), mothertrack1.pt(), mothertrack1.eta()); + auto phiMinusPsi = getPhiInRange(mothertrack1.phi() - psiFT0C); + histos.fill(HIST("hSparseKstarMCRecWeight"), centclass, phiMinusPsi, std::pow(std::cos(2.0 * phiMinusPsi), 2.0), mothertrack1.pt(), mothertrack1.eta()); } } } } // loop over generated particle - for (auto& mcParticle : GenParticles) { - if (TMath::Abs(mcParticle.eta()) > 0.8) // main acceptance + for (const auto& mcParticle : GenParticles) { + static constexpr float MaxEtaAcceptance = 0.8f; + if (std::abs(mcParticle.eta()) > MaxEtaAcceptance) { continue; - if (TMath::Abs(mcParticle.pdgCode()) == 321 && mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("hSparseKstarMCGenKaonWeight"), centclass, GetPhiInRange(mcParticle.phi() - psiFT0C), TMath::Power(TMath::Cos(2.0 * GetPhiInRange(mcParticle.phi() - psiFT0C)), 2.0), mcParticle.pt(), mcParticle.eta()); } - if (TMath::Abs(mcParticle.pdgCode()) == 211 && mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("hSparseKstarMCGenPionWeight"), centclass, GetPhiInRange(mcParticle.phi() - psiFT0C), TMath::Power(TMath::Cos(2.0 * GetPhiInRange(mcParticle.phi() - psiFT0C)), 2.0), mcParticle.pt(), mcParticle.eta()); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus && mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hSparseKstarMCGenKaonWeight"), centclass, getPhiInRange(mcParticle.phi() - psiFT0C), std::pow(std::cos(2.0 * getPhiInRange(mcParticle.phi() - psiFT0C)), 2.0), mcParticle.pt(), mcParticle.eta()); } - if (TMath::Abs(mcParticle.y()) > confRapidity) { + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hSparseKstarMCGenPionWeight"), centclass, getPhiInRange(mcParticle.phi() - psiFT0C), std::pow(std::cos(2.0 * getPhiInRange(mcParticle.phi() - psiFT0C)), 2.0), mcParticle.pt(), mcParticle.eta()); + } + if (std::abs(mcParticle.y()) > confRapidity) { continue; } - if (mcParticle.pdgCode() != 313) { + if (mcParticle.pdgCode() != o2::constants::physics::kK0Star892) { continue; } auto kDaughters = mcParticle.daughters_as(); - if (kDaughters.size() != 2) { + static constexpr std::size_t NumberOfDaughters = 2; + + if (kDaughters.size() != NumberOfDaughters) { continue; } auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } - if (kCurrentDaughter.pdgCode() == +321) { - if (kCurrentDaughter.pt() > cfgCutPT && TMath::Abs(kCurrentDaughter.eta()) < cfgCutEta) { + if (kCurrentDaughter.pdgCode() == +PDG_t::kKPlus) { + if (kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtp = true; } - KaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); - } else if (kCurrentDaughter.pdgCode() == -211) { - if (kCurrentDaughter.pt() > cfgCutPT && TMath::Abs(kCurrentDaughter.eta()) < cfgCutEta) { + kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); + } else if (kCurrentDaughter.pdgCode() == -PDG_t::kPiPlus) { + if (kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta) { daughtm = true; } - PionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massPi); + pionMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassPi); } } if (daughtp && daughtm) { - auto PhiMinusPsiGen = GetPhiInRange(mcParticle.phi() - psiFT0C); - histos.fill(HIST("hSparseKstarMCGenWeight"), centclass, PhiMinusPsiGen, TMath::Power(TMath::Cos(2.0 * PhiMinusPsiGen), 2.0), mcParticle.pt(), mcParticle.eta()); + auto phiMinusPsiGen = getPhiInRange(mcParticle.phi() - psiFT0C); + histos.fill(HIST("hSparseKstarMCGenWeight"), centclass, phiMinusPsiGen, std::pow(std::cos(2.0 * phiMinusPsiGen), 2.0), mcParticle.pt(), mcParticle.eta()); } } } // rec collision loop } // process MC - PROCESS_SWITCH(kstarpbpb, processMCkstarWeight, "Process MC kstar Weight", false); + PROCESS_SWITCH(Kstarpbpb, processMCkstarWeight, "Process MC kstar Weight", false); + + // ================= phi(1020) spin alignment: port of the SA part of phipbpb.cxx ================= + struct SAValues { + double cosThetaStar; + double sa; + }; + + // K- in the phi rest frame (as in phipbpb.cxx) w.r.t. the chosen quantization axis + SAValues getSAValuesPhi(const ROOT::Math::PxPyPzMVector& mother, const ROOT::Math::PxPyPzMVector& kaonMinus, const ROOT::Math::XYZVector& axis, double psiSA) + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto threeVecDau = boost(kaonMinus).Vect(); + auto cosThetaStar = axis.Dot(threeVecDau) / std::sqrt(threeVecDau.Mag2()) / std::sqrt(axis.Mag2()); + auto sa = std::cos(2.0 * getPhiInRange(threeVecDau.Phi() - getSAPlaneAngle(mother, psiSA))); + return {.cosThetaStar = cosThetaStar, .sa = sa}; + } + + void processSEPhi(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + if (!selectionEvent(collision)) { + return; + } + auto centrality = collision.centFT0C(); + auto psiSA = getSAEventAngle(collision.psiFT0C()); + if (cfgSAFrame.value == kRandomEventPlane) { + histos.fill(HIST("phi/hPsiRandom"), centrality, psiSA); + } + + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother, kaonPlusRot, phiRot; + for (const auto& track1 : tracks) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // positive kaon + continue; + } + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1)) { + continue; + } + for (const auto& track2 : tracks) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // negative kaon + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phiMother = kaonPlusPhi + kaonMinusPhi; + auto absRapidity = std::abs(phiMother.Rapidity()); + if (absRapidity > phiSA.confRapidity) { + continue; + } + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + if (doClosureTest) { + histos.fill(HIST("closure/PhiSameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } + histos.fill(HIST("phi/hSparseV2SameEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); + histos.fill(HIST("phi/hSparseV2SameEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + + // rotated background, same rotation as for K* in processSE: rotate the K+, cos(theta*) of the K- in the rotated pair + if (phiSA.fillRotation && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinusPhi; + auto absRapidityRot = std::abs(phiRot.Rapidity()); + if (absRapidityRot > phiSA.confRapidity) { + continue; + } + // production-plane axis must follow the rotated candidate + auto [cosThetaStarRot, saRot] = getSAValuesPhi(phiRot, kaonMinusPhi, getSAAxis(phiRot, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2RotationSA"), phiRot.M(), phiRot.Pt(), saRot, absRapidityRot, centrality); + histos.fill(HIST("phi/hSparseV2RotationCosThetaStar"), phiRot.M(), phiRot.Pt(), cosThetaStarRot, absRapidityRot, centrality); + } + } + } + } + + // like-sign background (K+K+ and K-K-): same track, PID and pair cuts as the unlike-sign pairs, each pair taken once; + // cos(theta*) of the first kaon of the pair (the two kaons are back to back in the pair rest frame, so the choice only flips the sign) + if (phiSA.fillLikeSign) { + ROOT::Math::PxPyPzMVector kaonLike1, kaonLike2, pairLike; + for (const auto& track1 : tracks) { + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1)) { + continue; + } + for (const auto& track2 : tracks) { + if (track2.globalIndex() <= track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() <= 0) { + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonLike1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonLike2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + pairLike = kaonLike1 + kaonLike2; + auto absRapidityLike = std::abs(pairLike.Rapidity()); + if (absRapidityLike > phiSA.confRapidity) { + continue; + } + auto [cosThetaStarLike, saLike] = getSAValuesPhi(pairLike, kaonLike1, getSAAxis(pairLike, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2SameEventLikeSA"), pairLike.M(), pairLike.Pt(), saLike, absRapidityLike, centrality); + histos.fill(HIST("phi/hSparseV2SameEventLikeCosThetaStar"), pairLike.M(), pairLike.Pt(), cosThetaStarLike, absRapidityLike, centrality); + if (track1.sign() > 0) { + histos.fill(HIST("phi/hMassSameEventLikePP"), pairLike.M(), centrality); + } else { + histos.fill(HIST("phi/hMassSameEventLikeNN"), pairLike.M(), centrality); + } + } + } + } + } + PROCESS_SWITCH(Kstarpbpb, processSEPhi, "Process same event phi(1020) spin alignment", false); + + void processMEPhi(EventCandidates const& collisions, TrackCandidates const& tracks) + { + auto tracksTuple = std::make_tuple(tracks); + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionEventPairME(collision1, collision2)) { + continue; + } + auto centrality = collision1.centFT0C(); + auto psiSA = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phiMother = kaonPlusPhi + kaonMinusPhi; + auto absRapidity = std::abs(phiMother.Rapidity()); + if (absRapidity > phiSA.confRapidity) { + continue; + } + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxis(phiMother, psiSA), psiSA); + if (doClosureTest) { + histos.fill(HIST("closure/PhiMixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } + histos.fill(HIST("phi/hSparseV2MixedEventSA"), phiMother.M(), phiMother.Pt(), sa, absRapidity, centrality); + histos.fill(HIST("phi/hSparseV2MixedEventCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, absRapidity, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMEPhi, "Process mixed event phi(1020) spin alignment", false); + + void processMCPhi(CollisionMCTrueTable::iterator const& TrueCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) + { + histos.fill(HIST("phi/hMC"), 0); + if (RecCollisions.size() == 0) { + histos.fill(HIST("phi/hMC"), 1); + return; + } + if (RecCollisions.size() > 1) { + histos.fill(HIST("phi/hMC"), 2); + return; + } + ROOT::Math::PxPyPzMVector kaonPlusPhi, kaonMinusPhi, phiMother; + for (const auto& RecCollision : RecCollisions) { + auto psiFT0C = getMCEffPsi(TrueCollision, GenParticles); // one Psi per MC event (mcEffEP.psiSource; 0 = old fixed Psi = 0) + histos.fill(HIST("phi/hMC"), 3); + if (!RecCollision.sel8()) { + histos.fill(HIST("phi/hMC"), 4); + continue; + } + if (!selectionEventBits(RecCollision)) { + continue; + } + histos.fill(HIST("phi/hMC"), 5); // same bin meaning as the K* hMC + if (std::abs(RecCollision.posZ()) > cfgCutVertex) { + histos.fill(HIST("phi/hMC"), 6); + continue; + } + histos.fill(HIST("phi/hMC"), 7); + auto centrality = RecCollision.centFT0C(); + histos.fill(HIST("phi/CentPercentileMCRecHist"), centrality); + auto psiSA = getSAEventAngle(psiFT0C); // same angle for rec and gen of this event + histos.fill(HIST("phi/hMCEffPsi"), centrality, psiSA); + auto oldindex = -999; + auto rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); + // loop over reconstructed particle + for (const auto& track1 : rectrackspart) { + if (!selectionTrackPhi(track1)) { + continue; + } + if (!selectionPIDPhi(track1)) { + continue; + } + if (!track1.has_mcParticle()) { + continue; + } + auto track1ID = track1.index(); + for (const auto& track2 : rectrackspart) { + auto track2ID = track2.index(); + if (track2ID <= track1ID) { + continue; + } + if (!selectionTrackPhi(track2)) { + continue; + } + if (!selectionPIDPhi(track2)) { + continue; + } + if (!track2.has_mcParticle()) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + const auto mctrack1 = track1.mcParticle(); + const auto mctrack2 = track2.mcParticle(); + if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) { + continue; + } + if (std::abs(mctrack1.pdgCode()) != PDG_t::kKPlus || std::abs(mctrack2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + for (const auto& mothertrack1 : mctrack1.mothers_as()) { + for (const auto& mothertrack2 : mctrack2.mothers_as()) { + if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { + continue; + } + if (mothertrack1 != mothertrack2) { + continue; + } + if (std::abs(mothertrack1.y()) > phiSA.confRapidity) { + continue; + } + if (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kPhi) { + continue; + } + if (phiSA.avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { + histos.fill(HIST("phi/h1PhiRecsplit"), mothertrack1.pt()); + continue; + } + oldindex = mothertrack1.globalIndex(); + if (track1.sign() > 0) { + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinusPhi = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlusPhi = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phiMother = kaonPlusPhi + kaonMinusPhi; + // reconstructed-pair rapidity, same cut as in processSEPhi / processMEPhi (true-mother y is cut above) + if (std::abs(phiMother.Rapidity()) > phiSA.confRapidity) { + continue; + } + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2MCRecCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("phi/hSparseV2MCRecCosThetaStarPhiVPsi"), phiMother.Pt(), cosThetaStar, getPhiInRange(phiMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/PhiMCRecTrueCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + } + histos.fill(HIST("phi/hSparseV2MCRecSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); + } + } + } + } + // loop over generated particle + for (const auto& mcParticle : GenParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto kDaughters = mcParticle.daughters_as(); + static constexpr std::size_t NumberOfDaughters = 2; + if (kDaughters.size() != NumberOfDaughters) { + continue; + } + auto daughtp = false; + auto daughtm = false; + for (const auto& kCurrentDaughter : kDaughters) { + if (!kCurrentDaughter.isPhysicalPrimary()) { + continue; + } + bool inAcceptance = !phiSA.genacceptancecut || (kCurrentDaughter.pt() > cfgCutPT && std::abs(kCurrentDaughter.eta()) < cfgCutEta); + if (kCurrentDaughter.pdgCode() == +PDG_t::kKPlus) { + daughtp = daughtp || inAcceptance; + kaonPlusPhi = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); + } else if (kCurrentDaughter.pdgCode() == -PDG_t::kKPlus) { + daughtm = daughtm || inAcceptance; + kaonMinusPhi = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), MassKa); + } + } + if (daughtp && daughtm) { + phiMother = kaonPlusPhi + kaonMinusPhi; + auto [cosThetaStar, sa] = getSAValuesPhi(phiMother, kaonMinusPhi, getSAAxisPhiMC(phiMother, psiSA), psiSA); + histos.fill(HIST("phi/hSparseV2MCGenCosThetaStar_effy"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + if (mcEffEP.fillPhiVMinusPsi) { + histos.fill(HIST("phi/hSparseV2MCGenCosThetaStarPhiVPsi"), phiMother.Pt(), cosThetaStar, getPhiInRange(phiMother.Phi() - psiSA), centrality); + } + if (doClosureTest) { + histos.fill(HIST("closure/PhiMCGenCosThetaStar"), phiMother.M(), phiMother.Pt(), cosThetaStar, std::abs(phiMother.Rapidity()), centrality); + } + histos.fill(HIST("phi/hSparseV2MCGenSA"), phiMother.M(), phiMother.Pt(), sa, std::abs(phiMother.Rapidity()), centrality); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCPhi, "Process MC phi(1020) spin alignment", false); + + // ================= event loss and signal loss for K* and phi(1020) ================= + // adapted from processEvtLossSigLossMC1 in phianalysisrun3pbpb.cxx. The accepted MC event uses the same reconstructed-event + // selection as processMC / processMCPhi (sel8, additionalEvSel1-4, |vz|) and, by default, the same single-reco-collision + // requirement; the generated K* and phi use the same rapidity and daughter conditions as the generated loops there. + void processEvtLossSigLossMC(McCollisionMults::iterator const& mcCollision, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles) + { + if (evtSigLoss.cutVzGen && std::abs(mcCollision.posZ()) > cfgCutVertex) { + return; + } + if (evtSigLoss.isApplyInelgt0 && !mcCollision.isInelGt0()) { + return; + } + if (evtSigLoss.isApplyTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { + return; + } + const float impactPar = mcCollision.impactParameter(); + const float multMC = mcCollision.multMCNParticlesEta05(); + + // all generated events + histos.fill(HIST("evtSigLoss/MCEventHist"), 1); + histos.fill(HIST("evtSigLoss/hImpactParameterGen"), impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05Gen"), multMC); + if (RecCollisions.size() == 0) { + histos.fill(HIST("evtSigLoss/MCEventHist"), 3); + histos.fill(HIST("evtSigLoss/hImpactParameterGenNoReco"), impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05GenNoReco"), multMC); + } + if (RecCollisions.size() > 1) { + histos.fill(HIST("evtSigLoss/MCEventHist"), 4); + } + + // generated events with a selected reconstructed collision (event loss) + bool selected = false; + float centrality = -999.f; + if (!evtSigLoss.requireSingleReco || RecCollisions.size() == 1) { + for (const auto& RecCollision : RecCollisions) { + if (!RecCollision.sel8() || !selectionEventBits(RecCollision) || std::abs(RecCollision.posZ()) > cfgCutVertex) { + continue; + } + selected = true; + centrality = RecCollision.centFT0C(); + } + } + if (selected) { + histos.fill(HIST("evtSigLoss/MCEventHist"), 2); + histos.fill(HIST("evtSigLoss/hImpactParameterRec"), impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05Rec"), multMC); + histos.fill(HIST("evtSigLoss/hImpactParVsCentRec"), centrality, impactPar); + histos.fill(HIST("evtSigLoss/hMultEta05VsCentRec"), centrality, multMC); + } + + // generated K*0 / anti-K*0 and phi(1020) (signal loss) + static constexpr std::size_t NumberOfDaughters = 2; + for (const auto& mcParticle : GenParticles) { + const int pdgMother = std::abs(mcParticle.pdgCode()); + if (pdgMother != o2::constants::physics::kK0Star892 && mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + const bool isKstar = (pdgMother == o2::constants::physics::kK0Star892); + if (std::abs(mcParticle.y()) > (isKstar ? confRapidity.value : phiSA.confRapidity.value)) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false, hasPion = false, hasKPlus = false, hasKMinus = false; + ROOT::Math::PxPyPzMVector dau1, dau2; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + if (isKstar) { + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = true; + dau1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = true; + dau2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } else { + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = true; + dau1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = true; + dau2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + } + const double pt = (dau1 + dau2).Pt(); + if (isKstar && hasKaon && hasPion) { + histos.fill(HIST("evtSigLoss/hKstarGenBeforeEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hKstarGenVsMultBeforeEvtSel"), pt, multMC); + if (selected) { + histos.fill(HIST("evtSigLoss/hKstarGenAfterEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hKstarGenVsMultAfterEvtSel"), pt, multMC); + histos.fill(HIST("evtSigLoss/hKstarGenAfterEvtSelVsCent"), pt, centrality); + } + } else if (!isKstar && hasKPlus && hasKMinus) { + histos.fill(HIST("evtSigLoss/hPhiGenBeforeEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hPhiGenVsMultBeforeEvtSel"), pt, multMC); + if (selected) { + histos.fill(HIST("evtSigLoss/hPhiGenAfterEvtSel"), pt, impactPar); + histos.fill(HIST("evtSigLoss/hPhiGenVsMultAfterEvtSel"), pt, multMC); + histos.fill(HIST("evtSigLoss/hPhiGenAfterEvtSelVsCent"), pt, centrality); + } + } + } + } + PROCESS_SWITCH(Kstarpbpb, processEvtLossSigLossMC, "Process event loss and signal loss for K* and phi(1020)", false); + + // ================= MC closure test of the cos(theta*) chain ================= + // Test sample (reconstructed MC analysed like data): + // hSparseSE all same-event unlike-sign pairs (signal + background), true pairs weighted by W + // hSparseME mixed-event unlike-sign pairs (weight 1) -> step 1: SE - norm * ME + // hSparseRecTrue reconstructed true K* (phi), weighted by W -> step 2: truth for the raw yield + // hSparseGen generated K* (phi), daughters in acceptance, weighted -> step 4: input yield and rho00 + // Efficiency sample (independent events if closure.splitSample): + // hSparseRecTrueEff / hSparseGenEff = Acc x Eff(cos theta*) -> step 3 + // W(cos) = 3/2 [(1 - rho00) + (3 rho00 - 1) cos^2] with the GENERATED cos(theta*) (W = 1 if injectPolarization is off) + + // W(cos) = 3/2 [(1 - rho) + (3 rho - 1) cos^2]; = 1 over cos in [-1,1]; W = 1 for rho = 1/3 + double getInjectionWeight(double cosThetaStar) + { + if (!closure.injectPolarization) { + return 1.0; + } + const double rho = closure.rho00Inj.value; + return 1.5 * ((1.0 - rho) + (3.0 * rho - 1.0) * cosThetaStar * cosThetaStar); + } + + // cos(theta*) of 'dau' in the rest frame of 'mother' with respect to 'axis' + double getCosThetaStar(const ROOT::Math::PxPyPzMVector& mother, const ROOT::Math::PxPyPzMVector& dau, const ROOT::Math::XYZVector& axis) + { + ROOT::Math::Boost boost{mother.BoostToCM()}; + auto threeVecDau = boost(dau).Vect(); + return axis.Dot(threeVecDau) / std::sqrt(threeVecDau.Mag2()) / std::sqrt(axis.Mag2()); + } + + // one Psi per MC event (call once per event: the random-EP option draws a new angle at each call) + double getClosurePsi(double trueEventPlaneAngle) + { + return getSAEventAngle(closure.useTrueEP ? trueEventPlaneAngle : 0.0); + } + + // even MC events -> Acc x Eff sample, odd MC events -> test sample (both if splitSample is off) + static constexpr int64_t SplitSampleModulus = 2; + bool isClosureEffSample(int64_t mcCollisionIndex) + { + return !closure.splitSample || (mcCollisionIndex % SplitSampleModulus == 0); + } + bool isClosureTestSample(int64_t mcCollisionIndex) + { + return !closure.splitSample || (mcCollisionIndex % SplitSampleModulus == 1); + } + + // track enters the SE / ME / rotated pairs: always if requirePrimaryTracks is off, else only if matched to a physical primary + template + bool selectionClosurePrimary(const TTrack& track) + { + return !closure.requirePrimaryTracks || (track.has_mcParticle() && track.mcParticle().isPhysicalPrimary()); + } + + // number of reco collisions per MC collision (for the single-reco requirement of the ME, same as the SE) + std::vector countRecoPerMcCollision(aod::McCollisionLabels const& mcLabels, int64_t nMcCollisions) + { + std::vector nRecPerMc(nMcCollisions, 0); + for (const auto& label : mcLabels) { + if (label.mcCollisionId() >= 0 && label.mcCollisionId() < nMcCollisions) { + nRecPerMc[label.mcCollisionId()]++; + } + } + return nRecPerMc; + } + + // mixed-event pair of the closure test sample: same event class as the SE + template + bool selectionClosurePairME(const TCollision& collision1, const TCollision& collision2, const std::vector& nRecPerMc) + { + if (!selectionEventMC(collision1) || !selectionEventMC(collision2) || collision1.bcId() == collision2.bcId()) { + return false; + } + if (!collision1.has_mcCollision() || !collision2.has_mcCollision()) { + return false; + } + if (!isClosureTestSample(collision1.mcCollisionId()) || !isClosureTestSample(collision2.mcCollisionId())) { + return false; + } + if (closure.meRequireSingleReco && (nRecPerMc[collision1.mcCollisionId()] != 1 || nRecPerMc[collision2.mcCollisionId()] != 1)) { + return false; + } + return true; + } + + // reco-event selection of processMC / processMCPhi, plus the data centrality cut (identical for SE and ME) + template + bool selectionEventMC(const TCollision& collision) + { + return collision.sel8() && selectionEventBits(collision) && std::abs(collision.posZ()) < cfgCutVertex && std::abs(collision.centFT0C()) < cfgCutCentrality; + } + + // reco-event selection of the reconstructed-Psi_FT0C closure: selectionEventMC plus the EP-calibration trigger required + // on data (processSE / processSEPhi via selectionEventCuts), only available once EPCalibrationTables is joined + template + bool selectionEventMCRecoEP(const TCollision& collision) + { + return selectionEventMC(collision) && collision.triggereventep(); + } + + // mixed-event pair of the reconstructed-Psi_FT0C closure test sample: same as selectionClosurePairME, with the EP trigger + template + bool selectionClosurePairMERecoEP(const TCollision& collision1, const TCollision& collision2, const std::vector& nRecPerMc) + { + if (!selectionEventMCRecoEP(collision1) || !selectionEventMCRecoEP(collision2) || collision1.bcId() == collision2.bcId()) { + return false; + } + if (!collision1.has_mcCollision() || !collision2.has_mcCollision()) { + return false; + } + if (!isClosureTestSample(collision1.mcCollisionId()) || !isClosureTestSample(collision2.mcCollisionId())) { + return false; + } + if (closure.meRequireSingleReco && (nRecPerMc[collision1.mcCollisionId()] != 1 || nRecPerMc[collision2.mcCollisionId()] != 1)) { + return false; + } + return true; + } + + // K pi pair from the decay of one primary K*0 / anti-K*0 (|y| < confRapidity) + template + bool isTrueKstarPair(const TTrack& trackKaon, const TTrack& trackPion) + { + if (!trackKaon.has_mcParticle() || !trackPion.has_mcParticle()) { + return false; + } + const auto mcKaon = trackKaon.mcParticle(); + const auto mcPion = trackPion.mcParticle(); + if (!mcKaon.isPhysicalPrimary() || !mcPion.isPhysicalPrimary()) { + return false; + } + if (std::abs(mcKaon.pdgCode()) != PDG_t::kKPlus || std::abs(mcPion.pdgCode()) != PDG_t::kPiPlus) { + return false; + } + for (const auto& motherKaon : mcKaon.template mothers_as()) { + for (const auto& motherPion : mcPion.template mothers_as()) { + if (motherKaon != motherPion) { + continue; + } + if (std::abs(motherKaon.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + if (std::abs(motherKaon.y()) > confRapidity) { + continue; + } + return true; + } + } + return false; + } + + // K+ K- pair from the decay of one primary phi(1020) (|y| < phiSA.confRapidity) + template + bool isTruePhiPair(const TTrack& trackPlus, const TTrack& trackMinus) + { + if (!trackPlus.has_mcParticle() || !trackMinus.has_mcParticle()) { + return false; + } + const auto mcPlus = trackPlus.mcParticle(); + const auto mcMinus = trackMinus.mcParticle(); + if (!mcPlus.isPhysicalPrimary() || !mcMinus.isPhysicalPrimary()) { + return false; + } + if (mcPlus.pdgCode() != PDG_t::kKPlus || mcMinus.pdgCode() != PDG_t::kKMinus) { + return false; + } + for (const auto& motherPlus : mcPlus.template mothers_as()) { + for (const auto& motherMinus : mcMinus.template mothers_as()) { + if (motherPlus != motherMinus) { + continue; + } + if (motherPlus.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + if (std::abs(motherPlus.y()) > phiSA.confRapidity) { + continue; + } + return true; + } + } + return false; + } + + // ---------------- K*0: same event, true reco, generated ---------------- + // track1 = kaon candidate, track2 = pion candidate, unlike-sign, same cuts as processSE. + // The axis is built with getSAAxis, as in processMC (efficiency) and processSE (data). + void processMCClosure(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableCentFT0C const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar, kaonRot, kstarRot, kaonGen, pionGen, kstarGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closureKstar/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closureKstar/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closureKstar/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + const double psi = getClosurePsi(trueCollision.eventPlaneAngle()); // same angle for the rec and the gen part of this event + for (const auto& recCollision : recCollisions) { + if (!selectionEventMC(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closureKstar/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closureKstar/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!selectionTrack(track1) || !selectionClosurePrimary(track1)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track1, 0)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (track2.globalIndex() == track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track2, 1)) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + + double weight = 1.0; + if (isTrueKstarPair(track1, track2)) { + const auto mcKaon = track1.mcParticle(); + const auto mcPion = track2.mcParticle(); + kaonGen = ROOT::Math::PxPyPzMVector(mcKaon.px(), mcKaon.py(), mcKaon.pz(), MassKa); + pionGen = ROOT::Math::PxPyPzMVector(mcPion.px(), mcPion.py(), mcPion.pz(), MassPi); + kstarGen = kaonGen + pionGen; + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psi)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstar/hSparseRecTrueEff"), kstar.M(), kstar.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closureKstar/hSparseRecTrue"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closureKstar/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closureKstar/hSparseSE"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + + // rotated background, same rotation as in processSE (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + kstarRot = kaonRot + pion; + if (std::abs(kstarRot.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaonRot, pion)) { + continue; + } + const double cosRot = getCosThetaStar(kstarRot, kaonRot, getSAAxis(kstarRot, psi)); + histos.fill(HIST("closureKstar/hSparseRot"), kstarRot.M(), kstarRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated K*0 / anti-K*0: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > confRapidity) { + continue; + } + if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false; + bool hasPion = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = hasKaon || inAcceptance; + kaonGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = hasPion || inAcceptance; + pionGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } + if (!hasKaon || !hasPion) { + continue; + } + kstarGen = kaonGen + pionGen; + if (std::abs(kstarGen.Rapidity()) > confRapidity) { + continue; + } + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psi)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstar/hSparseGenEff"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closureKstar/hSparseGen"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosure, "Process MC closure of cos(theta*) for K*: SE, true rec, gen", false); + + // ---------------- K*0: mixed event of the test sample ---------------- + // Same pairing as processMixedEvent (kaon from event 1, pion from event 2, unlike-sign), axis from event 1. + void processMCClosureME(CollisionMCRecMixTable const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getClosurePsi(collision1.mcCollision().eventPlaneAngle()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { + continue; + } + if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + histos.fill(HIST("closureKstar/hSparseME"), kstar.M(), kstar.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureME, "Process MC closure of cos(theta*) for K*: mixed event", false); + + // ---------------- phi(1020): same event, true reco, generated ---------------- + // track1 = K+, track2 = K-, as in processSEPhi; cos(theta*) of the K-. + // The axis is built with getSAAxis, as in processSEPhi/processMEPhi (the real data analysis). + void processMCClosurePhi(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableCentFT0C const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi, kaonPlusRot, phiRot, kaonPlusGen, kaonMinusGen, phiGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closurePhi/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closurePhi/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closurePhi/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + const double psi = getClosurePsi(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMC(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closurePhi/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closurePhi/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // K+ + continue; + } + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1) || !selectionClosurePrimary(track1)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // K- + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + + double weight = 1.0; + if (isTruePhiPair(track1, track2)) { + const auto mcPlus = track1.mcParticle(); + const auto mcMinus = track2.mcParticle(); + kaonPlusGen = ROOT::Math::PxPyPzMVector(mcPlus.px(), mcPlus.py(), mcPlus.pz(), MassKa); + kaonMinusGen = ROOT::Math::PxPyPzMVector(mcMinus.px(), mcMinus.py(), mcMinus.pz(), MassKa); + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psi)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhi/hSparseRecTrueEff"), phi.M(), phi.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closurePhi/hSparseRecTrue"), phi.M(), phi.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closurePhi/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closurePhi/hSparseSE"), phi.M(), phi.Pt(), cosRec, centrality, weight); + + // rotated background: rotate the K+ (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinus; + if (std::abs(phiRot.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRot = getCosThetaStar(phiRot, kaonMinus, getSAAxis(phiRot, psi)); + histos.fill(HIST("closurePhi/hSparseRot"), phiRot.M(), phiRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated phi: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKPlus = false; + bool hasKMinus = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = hasKPlus || inAcceptance; + kaonPlusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = hasKMinus || inAcceptance; + kaonMinusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + if (!hasKPlus || !hasKMinus) { + continue; + } + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psi)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhi/hSparseGenEff"), phiGen.M(), phiGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closurePhi/hSparseGen"), phiGen.M(), phiGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosurePhi, "Process MC closure of cos(theta*) for phi(1020): SE, true rec, gen", false); + + // ---------------- phi(1020): mixed event of the test sample ---------------- + // Same pairing as processMEPhi (K+K- from two events), cos(theta*) of the K-, axis from event 1. + void processMCClosureMEPhi(CollisionMCRecMixTable const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosure binningOnPositions{{axisVertex, axisMultiplicityClass}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairME(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getClosurePsi(collision1.mcCollision().eventPlaneAngle()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + histos.fill(HIST("closurePhi/hSparseME"), phi.M(), phi.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMEPhi, "Process MC closure of cos(theta*) for phi(1020): mixed event", false); + + // ================= MC closure test of the cos(theta*) chain, reconstructed (finite-resolution) Psi_FT0C ================= + // Same as processMCClosure(Phi)/processMCClosureME(Phi) above, but Psi is the reconstructed Psi_FT0C (as on data) instead of + // the MC true event plane, and the sub-event resolution of the sample is filled too (ResFT0C*SP, needed to derive R2 and + // un-smear the fitted raw rho00 downstream). Kept as separate process functions (own histogram directories, own collision + // tables joined with EPCalibrationTables) so the true-EP closure above keeps zero dependency on the EP-calibration producer. + + // ---------------- K*0: same event, true reco, generated (reconstructed Psi_FT0C) ---------------- + void processMCClosureRecoEP(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableClosure const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar, kaonRot, kstarRot, kaonGen, pionGen, kstarGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closureKstarRecoEP/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + // true axis: defines the injected weight and the Gen/GenEff truth, so the test is not tautological against the smeared axis + const double psiTrue = getSAEventAngle(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMCRecoEP(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closureKstarRecoEP/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + const double recoPsiFT0C = recCollision.psiFT0C(); + const double psi = getSAEventAngle(recoPsiFT0C); // measured axis: defines cosRec (SE/ME/RecTrue/RecTrueEff/Rot) only + { + const double psiTPC = recCollision.psiTPC(); + const double psiFT0A = recCollision.psiFT0A(); + const double qFT0C = recCollision.qFT0C(); + const double qTPC = recCollision.qTPC(); + const double qFT0A = recCollision.qFT0A(); + histos.fill(HIST("closureKstarRecoEP/ResFT0CTPCSP"), centrality, qFT0C * qTPC * std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closureKstarRecoEP/ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closureKstarRecoEP/ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.fill(HIST("closureKstarRecoEP/ResFT0CTPC"), centrality, std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closureKstarRecoEP/ResFT0CFT0A"), centrality, std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closureKstarRecoEP/ResFT0ATPC"), centrality, std::cos(2.0 * (psiTPC - psiFT0A))); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.fill(HIST("closureKstarRecoEP/ResTrueFT0C"), centrality, std::cos(2.0 * (recoPsiFT0C - trueCollision.eventPlaneAngle()))); + } + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!selectionTrack(track1) || !selectionClosurePrimary(track1)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track1, 0, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track1, 0)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (track2.globalIndex() == track1.globalIndex()) { + continue; + } + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && !strategySelectionPID(track2, 1, strategyPID)) { + continue; + } + if (isTOFOnly && !selectionPID2(track2, 1)) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + + double weight = 1.0; + if (isTrueKstarPair(track1, track2)) { + const auto mcKaon = track1.mcParticle(); + const auto mcPion = track2.mcParticle(); + kaonGen = ROOT::Math::PxPyPzMVector(mcKaon.px(), mcKaon.py(), mcKaon.pz(), MassKa); + pionGen = ROOT::Math::PxPyPzMVector(mcPion.px(), mcPion.py(), mcPion.pz(), MassPi); + kstarGen = kaonGen + pionGen; + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psiTrue)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseRecTrueEff"), kstar.M(), kstar.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseRecTrue"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closureKstarRecoEP/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closureKstarRecoEP/hSparseSE"), kstar.M(), kstar.Pt(), cosRec, centrality, weight); + + // rotated background, same rotation as in processSE (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + kstarRot = kaonRot + pion; + if (std::abs(kstarRot.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaonRot, pion)) { + continue; + } + const double cosRot = getCosThetaStar(kstarRot, kaonRot, getSAAxis(kstarRot, psi)); + histos.fill(HIST("closureKstarRecoEP/hSparseRot"), kstarRot.M(), kstarRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated K*0 / anti-K*0: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > confRapidity) { + continue; + } + if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKaon = false; + bool hasPion = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + hasKaon = hasKaon || inAcceptance; + kaonGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus) { + hasPion = hasPion || inAcceptance; + pionGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassPi); + } + } + if (!hasKaon || !hasPion) { + continue; + } + kstarGen = kaonGen + pionGen; + if (std::abs(kstarGen.Rapidity()) > confRapidity) { + continue; + } + const double cosGen = getCosThetaStar(kstarGen, kaonGen, getSAAxis(kstarGen, psiTrue)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closureKstarRecoEP/hSparseGenEff"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closureKstarRecoEP/hSparseGen"), kstarGen.M(), kstarGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureRecoEP, "Process MC closure of cos(theta*) for K*, reconstructed Psi_FT0C: SE, true rec, gen", false); + + // ---------------- K*0: mixed event of the test sample (reconstructed Psi_FT0C) ---------------- + void processMCClosureMERecoEP(CollisionMCRecMixTableRecoEP const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaon, pion, kstar; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrack(track1) || !selectionTrack(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!isTOFOnly && (!strategySelectionPID(track1, 0, strategyPID) || !strategySelectionPID(track2, 1, strategyPID))) { + continue; + } + if (isTOFOnly && (!selectionPID2(track1, 0) || !selectionPID2(track2, 1))) { + continue; + } + kaon = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + pion = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassPi); + kstar = kaon + pion; + if (std::abs(kstar.Rapidity()) > confRapidity) { + continue; + } + if (isPhiReflection(kaon, pion)) { + continue; + } + const double cosRec = getCosThetaStar(kstar, kaon, getSAAxis(kstar, psi)); + histos.fill(HIST("closureKstarRecoEP/hSparseME"), kstar.M(), kstar.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMERecoEP, "Process MC closure of cos(theta*) for K*, reconstructed Psi_FT0C: mixed event", false); + + // ---------------- phi(1020): same event, true reco, generated (reconstructed Psi_FT0C) ---------------- + void processMCClosureRecoEPPhi(CollisionMCTrueTable::iterator const& trueCollision, CollisionMCRecTableClosure const& recCollisions, TrackMCTrueTable const& genParticles, FilTrackMCRecTable const& recTracks) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi, kaonPlusRot, phiRot, kaonPlusGen, kaonMinusGen, phiGen; + static constexpr std::size_t NumberOfDaughters = 2; + + histos.fill(HIST("closurePhiRecoEP/hMC"), 0.5); + if (recCollisions.size() == 0) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 1.5); + return; + } + if (recCollisions.size() > 1) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 2.5); + return; + } + const bool isEffSample = isClosureEffSample(trueCollision.globalIndex()); + const bool isTestSample = isClosureTestSample(trueCollision.globalIndex()); + // true axis: defines the injected weight and the Gen/GenEff truth, so the test is not tautological against the smeared axis + const double psiTrue = getSAEventAngle(trueCollision.eventPlaneAngle()); + for (const auto& recCollision : recCollisions) { + if (!selectionEventMCRecoEP(recCollision)) { + continue; + } + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 3.5); + } + if (isTestSample) { + histos.fill(HIST("closurePhiRecoEP/hMC"), 4.5); + } + const auto centrality = recCollision.centFT0C(); + const double recoPsiFT0C = recCollision.psiFT0C(); + const double psi = getSAEventAngle(recoPsiFT0C); // measured axis: defines cosRec (SE/ME/RecTrue/RecTrueEff/Rot) only + { + const double psiTPC = recCollision.psiTPC(); + const double psiFT0A = recCollision.psiFT0A(); + const double qFT0C = recCollision.qFT0C(); + const double qTPC = recCollision.qTPC(); + const double qFT0A = recCollision.qFT0A(); + histos.fill(HIST("closurePhiRecoEP/ResFT0CTPCSP"), centrality, qFT0C * qTPC * std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closurePhiRecoEP/ResFT0CFT0ASP"), centrality, qFT0C * qFT0A * std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closurePhiRecoEP/ResFT0ATPCSP"), centrality, qTPC * qFT0A * std::cos(2.0 * (psiTPC - psiFT0A))); + // plain (unweighted) sub-event resolution, as needed by the EP-method correction formula; same definition as the data ResFT0CTPC etc. + histos.fill(HIST("closurePhiRecoEP/ResFT0CTPC"), centrality, std::cos(2.0 * (recoPsiFT0C - psiTPC))); + histos.fill(HIST("closurePhiRecoEP/ResFT0CFT0A"), centrality, std::cos(2.0 * (recoPsiFT0C - psiFT0A))); + histos.fill(HIST("closurePhiRecoEP/ResFT0ATPC"), centrality, std::cos(2.0 * (psiTPC - psiFT0A))); + // closure-only: Psi_FT0C vs the MC true event plane, to cross-check R from sub-events against the true R + histos.fill(HIST("closurePhiRecoEP/ResTrueFT0C"), centrality, std::cos(2.0 * (recoPsiFT0C - trueCollision.eventPlaneAngle()))); + } + auto recTracksThisColl = recTracks.sliceBy(perCollision, recCollision.globalIndex()); + + // ---- reconstructed pairs ---- + for (const auto& track1 : recTracksThisColl) { + if (!(track1.signed1Pt() > cfgCutCharge.value)) { // K+ + continue; + } + if (!selectionTrackPhi(track1) || !selectionPIDPhi(track1) || !selectionClosurePrimary(track1)) { + continue; + } + for (const auto& track2 : recTracksThisColl) { + if (!(track2.signed1Pt() < cfgCutCharge.value)) { // K- + continue; + } + if (!selectionTrackPhi(track2) || !selectionPIDPhi(track2) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + + double weight = 1.0; + if (isTruePhiPair(track1, track2)) { + const auto mcPlus = track1.mcParticle(); + const auto mcMinus = track2.mcParticle(); + kaonPlusGen = ROOT::Math::PxPyPzMVector(mcPlus.px(), mcPlus.py(), mcPlus.pz(), MassKa); + kaonMinusGen = ROOT::Math::PxPyPzMVector(mcMinus.px(), mcMinus.py(), mcMinus.pz(), MassKa); + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psiTrue)); + weight = getInjectionWeight(cosGen); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseRecTrueEff"), phi.M(), phi.Pt(), cosRec, centrality, 1.0); + } + if (isTestSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseRecTrue"), phi.M(), phi.Pt(), cosRec, centrality, weight); + histos.fill(HIST("closurePhiRecoEP/hCosThetaStarGenVsRec"), cosGen, cosRec); + } + } + if (!isTestSample) { + continue; + } + histos.fill(HIST("closurePhiRecoEP/hSparseSE"), phi.M(), phi.Pt(), cosRec, centrality, weight); + + // rotated background: rotate the K+ (weight 1) + if (closure.fillRotBkg && nBkgRotations.value > 1) { + const double angleStart = confMinRot.value; + const double angleStep = (confMaxRot.value - angleStart) / (nBkgRotations.value - 1.0); + for (int irot = 0; irot < nBkgRotations.value; irot++) { + const double rotAngle = angleStart + irot * angleStep; + kaonPlusRot = ROOT::Math::PxPyPzMVector(track1.px() * std::cos(rotAngle) - track1.py() * std::sin(rotAngle), track1.px() * std::sin(rotAngle) + track1.py() * std::cos(rotAngle), track1.pz(), MassKa); + phiRot = kaonPlusRot + kaonMinus; + if (std::abs(phiRot.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRot = getCosThetaStar(phiRot, kaonMinus, getSAAxis(phiRot, psi)); + histos.fill(HIST("closurePhiRecoEP/hSparseRot"), phiRot.M(), phiRot.Pt(), cosRot, centrality); + } + } + } + } + + // ---- generated phi: Acc x Eff denominator (eff sample) and input truth (test sample) ---- + for (const auto& mcParticle : genParticles) { + if (std::abs(mcParticle.y()) > phiSA.confRapidity) { + continue; + } + if (mcParticle.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != NumberOfDaughters) { + continue; + } + bool hasKPlus = false; + bool hasKMinus = false; + for (const auto& dau : daughters) { + if (!dau.isPhysicalPrimary()) { + continue; + } + const bool inAcceptance = !closure.genAcceptanceCut || (dau.pt() > cfgCutPT && std::abs(dau.eta()) < cfgCutEta); + if (dau.pdgCode() == PDG_t::kKPlus) { + hasKPlus = hasKPlus || inAcceptance; + kaonPlusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } else if (dau.pdgCode() == PDG_t::kKMinus) { + hasKMinus = hasKMinus || inAcceptance; + kaonMinusGen = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), MassKa); + } + } + if (!hasKPlus || !hasKMinus) { + continue; + } + phiGen = kaonPlusGen + kaonMinusGen; + const double cosGen = getCosThetaStar(phiGen, kaonMinusGen, getSAAxis(phiGen, psiTrue)); + // Acc x Eff must come from an unweighted (unpolarized) sample, independent of the injected rho00 under test + if (isEffSample) { + histos.fill(HIST("closurePhiRecoEP/hSparseGenEff"), phiGen.M(), phiGen.Pt(), cosGen, centrality, 1.0); + } + if (isTestSample) { + const double weight = getInjectionWeight(cosGen); + histos.fill(HIST("closurePhiRecoEP/hSparseGen"), phiGen.M(), phiGen.Pt(), cosGen, centrality, weight); + } + } + } // rec collision loop + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureRecoEPPhi, "Process MC closure of cos(theta*) for phi(1020), reconstructed Psi_FT0C: SE, true rec, gen", false); + + // ---------------- phi(1020): mixed event of the test sample (reconstructed Psi_FT0C) ---------------- + void processMCClosureMERecoEPPhi(CollisionMCRecMixTableRecoEP const& collisions, FilTrackMCRecTable const& tracks, aod::McCollisions const& mcCollisions, aod::McCollisionLabels const& mcLabels, aod::McParticles const&) + { + ROOT::Math::PxPyPzMVector kaonPlus, kaonMinus, phi; + const auto nRecPerMc = countRecoPerMcCollision(mcLabels, mcCollisions.size()); + auto tracksTuple = std::make_tuple(tracks); + BinningTypeMCClosureRecoEP binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [collision1, tracks1, collision2, tracks2] : pair) { + if (!selectionClosurePairMERecoEP(collision1, collision2, nRecPerMc)) { + continue; + } + const auto centrality = collision1.centFT0C(); + const double psi = getSAEventAngle(collision1.psiFT0C()); + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + if (track1.sign() * track2.sign() > 0) { + continue; + } + if (!selectionTrackPhi(track1) || !selectionTrackPhi(track2)) { + continue; + } + if (!selectionClosurePrimary(track1) || !selectionClosurePrimary(track2)) { + continue; + } + if (!selectionPIDPhi(track1) || !selectionPIDPhi(track2)) { + continue; + } + if (!selectionPairPhi(track1, track2)) { + continue; + } + if (phiSA.removeFakeTrack && (isFakeKaonPhi(track1) || isFakeKaonPhi(track2))) { + continue; + } + if (track1.sign() > 0) { + kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } else { + kaonMinus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), MassKa); + kaonPlus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), MassKa); + } + phi = kaonPlus + kaonMinus; + if (std::abs(phi.Rapidity()) > phiSA.confRapidity) { + continue; + } + const double cosRec = getCosThetaStar(phi, kaonMinus, getSAAxis(phi, psi)); + histos.fill(HIST("closurePhiRecoEP/hSparseME"), phi.M(), phi.Pt(), cosRec, centrality); + } + } + } + PROCESS_SWITCH(Kstarpbpb, processMCClosureMERecoEPPhi, "Process MC closure of cos(theta*) for phi(1020), reconstructed Psi_FT0C: mixed event", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"kstarpbpb"})}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Resonances/kstarqa.cxx b/PWGLF/Tasks/Resonances/kstarqa.cxx index 8ebdff47675..95ca4f38765 100644 --- a/PWGLF/Tasks/Resonances/kstarqa.cxx +++ b/PWGLF/Tasks/Resonances/kstarqa.cxx @@ -51,7 +51,6 @@ #include #include #include -#include #include #include @@ -83,93 +82,75 @@ struct Kstarqa { struct : ConfigurableGroup { // Configurables for event selections + Configurable isCheckMotherClosure{"isCheckMotherClosure", false, "Check mother globalid in MC for closure"}; + Configurable isSquarePIDcut{"isSquarePIDcut", false, "Apply square PID cut"}; Configurable isINELgt0{"isINELgt0", true, "INEL>0 selection"}; + Configurable isINELgt0Gen{"isINELgt0Gen", false, "Apply INEL>0 in Gen direclty from collisions in addition to already applied from pwglf::inelGt"}; Configurable isSel8{"isSel8", false, "Event selection sel8"}; Configurable isTriggerTVX{"isTriggerTVX", false, "TriggerTVX"}; - // Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", false, "IsGoodZvtxFT0vsPV"}; - // Configurable isApplyOccCut{"isApplyOccCut", true, "Apply occupancy cut"}; - // Configurable isNoSameBunchPileup{"isNoSameBunchPileup", true, "kNoSameBunchPileup"}; Configurable isGoodITSLayersAll{"isGoodITSLayersAll", true, "Require all ITS layers to be good"}; Configurable isVertexITSTPC{"isVertexITSTPC", false, "Vertex ITS TPC"}; - // Configurable isVertexTOFMatched{"isVertexTOFMatched", false, "Vertex TOF Matched"}; - // Configurable isNoCollInTimeRangeStandard{"isNoCollInTimeRangeStandard", false, "No collision in time range standard"}; Configurable isNoTimeFrameBorder{"isNoTimeFrameBorder", true, "kNoTimeFrameBorder"}; Configurable isNoITSROFrameBorder{"isNoITSROFrameBorder", true, "kNoITSROFrameBorder"}; Configurable isapplypTdepPID{"isapplypTdepPID", false, "Apply pT dependent PID"}; Configurable isapplypTdepPIDTOF{"isapplypTdepPIDTOF", false, "Apply pT dependent PID for TOF"}; Configurable isApplyParticleMID{"isApplyParticleMID", false, "Apply particle misidentification"}; Configurable isApplyParticleMIDPtDep{"isApplyParticleMIDPtDep", false, "Apply pT dependent MID selection"}; + Configurable isapplyPID2{"isapplyPID2", false, "Apply second PID selection"}; + Configurable isapplyPID1{"isapplyPID1", true, "Apply default PID selection"}; + Configurable isApplyParticleMIDPtDep2{"isApplyParticleMIDPtDep2", false, "Apply pT dependent MID selection (nSigma less of contamination lt nSigma of signal)"}; Configurable allGenEvents{"allGenEvents", false, "Fill all generated events in MC for signal loss calculations"}; - Configurable checkVzEvSigLoss{"checkVzEvSigLoss", false, "Check Vz event signal loss"}; Configurable isApplyDeepAngle{"isApplyDeepAngle", false, "Deep Angle cut"}; - Configurable isApplyMCchecksClosure{"isApplyMCchecksClosure", true, "Apply MC checks for closure test"}; - // Configurable deltaRCut{"deltaRCut", 0.0f, "Apply deltaR cut between two daughters"}; + Configurable isApplyMCGenVz{"isApplyMCGenVz", true, "Apply Vz cut on generated MC events"}; Configurable cutzvertex{"cutzvertex", 10.0f, "Accepted z-vertex range (cm)"}; - // Configurable configOccCut{"configOccCut", 1000., "Occupancy cut"}; // Configurables for track selections Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor - // Configurable cfgPrimaryTrack{"cfgPrimaryTrack", false, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable isGlobalTracks{"isGlobalTracks", true, "isGlobalTracks"}; + Configurable isGlobalTracks{"isGlobalTracks", true, "1:Global tracks, 0: Global tracks without DCA"}; Configurable rotationalCut{"rotationalCut", 10, "Cut value (Rotation angle pi - pi/cut and pi + pi/cut)"}; - // Configurable cfgCutPT{"cfgCutPT", 0.2f, "PT cut on daughter track"}; - // Configurable cfgCutEtaMax{"cfgCutEtaMax", 0.8f, "Eta cut on daughter track"}; - // Configurable cfgCutEtaMin{"cfgCutEtaMin", 0.0f, "Eta cut on daughter track"}; - // Configurable cfgCutDCAxyMin{"cfgCutDCAxyMin", 0.0f, "DCAxy range for tracks"}; Configurable cfgCutDCAxyMax{"cfgCutDCAxyMax", 2.0f, "DCAxy range for tracks"}; Configurable cfgCutDCAz{"cfgCutDCAz", 2.0f, "DCAz range for tracks"}; // Configurable ctrackRapidity{"ctrackRapidity", 0.3f, "Cut on track rapidity"}; Configurable cfgNoMixedEvents{"cfgNoMixedEvents", 5, "Number of mixed events per event"}; - // Configurable cfgITScluster{"cfgITScluster", 0, "Number of ITS cluster"}; - Configurable cfgTPCcluster{"cfgTPCcluster", 70, "TPC NCL Found"}; - // Configurable cfgRCRFC{"cfgRCRFC", 0.8f, "Crossed Rows to Findable Clusters"}; - // Configurable cfgITSChi2NCl{"cfgITSChi2NCl", 36.0, "ITS Chi2/NCl"}; - // Configurable cfgTPCChi2NClMax{"cfgTPCChi2NClMax", 4.0, "TPC Chi2/NCl"}; - // Configurable cfgTPCChi2NClMin{"cfgTPCChi2NClMin", 0.0, "TPC Chi2/NCl"}; + Configurable cfgITScluster{"cfgITScluster", 5, "Number of ITS cluster"}; + Configurable cfgTPCcluster{"cfgTPCcluster", 80, "TPC NCL Found"}; Configurable hasITS{"hasITS", true, "Required ITS"}; Configurable isITSTPCRefit{"isITSTPCRefit", false, "Require ITS Refit"}; - Configurable isApplyPtDepDCAxyCut{"isApplyPtDepDCAxyCut", false, "Apply pT dependent DCAxy cut"}; - // Configurable isGoldenChi2{"isGoldenChi2", false, "Apply golden chi2 cut"}; - // Configurable cfgDeepAngle{"cfgDeepAngle", 0.04, "Deep Angle cut value"}; - - // cuts on mother - // Configurable isApplyCutsOnMother{"isApplyCutsOnMother", false, "Enable additional cuts on Kstar mother"}; - // Configurable cMaxPtMotherCut{"cMaxPtMotherCut", 15.0, "Maximum pt of mother cut"}; - // Configurable cMaxMinvMotherCut{"cMaxMinvMotherCut", 1.5, "Maximum mass of mother cut"}; - // Configurable rapidityMotherData{"rapidityMotherData", 0.5, "Maximum rapidity of mother"}; - Configurable isPDGCheckMC{"isPDGCheckMC", true, "Check PDG code in MC (false for MC closure test)"}; + Configurable isApplyPtDepDCACut{"isApplyPtDepDCACut", false, "Apply pT dependent DCAxy cut"}; + Configurable cfgVariableDCAParamOne{"cfgVariableDCAParamOne", 0.004f, "pT dependent DCA cut parameter one"}; + Configurable cfgVariableDCAParamTwo{"cfgVariableDCAParamTwo", 0.008f, "pT dependent DCA cut parameter two"}; + Configurable cfgVariableDCApTPower{"cfgVariableDCApTPower", 1.1f, "pT dependent DCA cut power"}; + Configurable cFakeTrackCutKa{"cFakeTrackCutKa", 0.1, "Cut based on momentum difference in global and TPC tracks for Kaons"}; + Configurable cBetaCutTOFKaon{"cBetaCutTOFKaon", 0.0, "cut TOF beta"}; + Configurable cFakeTrack{"cFakeTrack", true, "Fake track selection"}; // PID selections Configurable nsigmaCutTPCPi{"nsigmaCutTPCPi", 3.0, "TPC Nsigma cut for pions"}; Configurable nsigmaCutTPCKa{"nsigmaCutTPCKa", 3.0, "TPC Nsigma cut for kaons"}; Configurable nsigmaCutTOFPi{"nsigmaCutTOFPi", 3.0, "TOF Nsigma cut for pions"}; Configurable nsigmaCutTOFKa{"nsigmaCutTOFKa", 3.0, "TOF Nsigma cut for kaons"}; + Configurable shiftInNsigmaTOFKa{"shiftInNsigmaTOFKa", 0.0, "Shift in Nsigma for kaons in TOF"}; + Configurable shiftInNsigmaTOFPi{"shiftInNsigmaTOFPi", 0.0, "Shift in Nsigma for pions in TOF"}; Configurable nsigmaCutCombinedKa{"nsigmaCutCombinedKa", 3.0, "Combined Nsigma cut for kaon"}; Configurable nsigmaCutCombinedPi{"nsigmaCutCombinedPi", 3.0, "Combined Nsigma cut for pion"}; - // Configurable nsigmaCutCombinedMIDKa{"nsigmaCutCombinedMIDKa", 3.0, "Combined Nsigma cut for kaon in MID"}; Configurable nsigmaCutCombinedMID{"nsigmaCutCombinedMID", 3.0, "Combined Nsigma cut for pion in MID"}; Configurable nsigmaCutTPCMID{"nsigmaCutTPCMID", 1.0, "MID Nsigma cut for pion in TPC"}; - // Configurable nsigmaCutTPCMIDKa{"nsigmaCutTPCMIDKa", 1.0, "MID Nsigma cut for kaon in TPC"}; - // Configurable nsigmaCutTOFMIDPi{"nsigmaCutTOFMIDPi", 1.0, "MID Nsigma cut for pion in TOF"}; - // Configurable nsigmaCutTOFMIDKa{"nsigmaCutTOFMIDKa", 1.0, "MID Nsigma cut for kaon in TOF"}; - // Configurable nsigmaCutTPCMIDPr{"nsigmaCutTPCMIDPr", 3.0, "TPC Nsigma cut for protons (for MID)"}; - // Configurable nsigmaCutTOFMIDPr{"nsigmaCutTOFMIDPr", 3.0, "TOF Nsigma cut for protons (for MID)"}; // Other fixed variables float lowPtCutPID = 0.5; int noOfDaughters = 2; - // float rapidityMotherData = 0.5; - // float cutzvertex = 10.0f; float cfgCutEtaMax = 0.8f; float cfgCutPT = 0.2f; float cfgDeepAngle = 0.04; float rapidityMotherData = 0.5; float pidCutTemp = 2.0f; - } selectionConfig; + } configGp; + + Service pdgDB{}; enum MultEstimator { kFT0M, @@ -192,6 +173,7 @@ struct Kstarqa { HistogramRegistry hInvMass{"hInvMass", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry hPID{"hPID", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry hOthers{"hOthers", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry hShift{"hShift", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Confugrable for QA histograms Configurable calcLikeSign{"calcLikeSign", true, "Calculate Like Sign"}; @@ -206,40 +188,22 @@ struct Kstarqa { Configurable cSelectMultEstimator{"cSelectMultEstimator", 0, "Select multiplicity estimator: 0 - FT0M, 1 - FT0A, 2 - FT0C, 3 - FV0A"}; // Configurable applyRecMotherRapidity{"applyRecMotherRapidity", true, "Apply rapidity cut on reconstructed mother track"}; - // Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", false, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - // Configurable cBetaCutTOF{"cBetaCutTOF", 0.0, "cut TOF beta"}; - // Configurable cFakeTrack{"cFakeTrack", true, "Fake track selection"}; - // Configurable cFakeTrackCutKa{"cFakeTrackCutKa", 0.5, "Cut based on momentum difference in global and TPC tracks for Kaons"}; - // Configurable cFakeTrackCutPi{"cFakeTrackCutPi", 0.5, "Cut based on momentum difference in global and TPC tracks for Pions"}; - // Configurable for histograms Configurable avoidsplitrackMC{"avoidsplitrackMC", true, "avoid split track in MC"}; - // Configurable cAllGenCollisions{"cAllGenCollisions", false, "To fill all generated collisions for the signal loss calculations"}; ConfigurableAxis binsMultPlot{"binsMultPlot", {110, 0.0, 110}, "THnSpare multiplicity axis"}; - // ConfigurableAxis axisdEdx{"axisdEdx", {1, 0.0f, 200.0f}, "dE/dx (a.u.)"}; - // ConfigurableAxis axisPtfordEbydx{"axisPtfordEbydx", {1, 0, 20}, "pT (GeV/c)"}; - // ConfigurableAxis axisMultdist{"axisMultdist", {1, 0, 70000}, "Multiplicity distribution"}; ConfigurableAxis configThnAxisPOL{"configThnAxisPOL", {20, -1.0, 1.0}, "Costheta axis"}; ConfigurableAxis invMassKstarAxis{"invMassKstarAxis", {300, 0.7f, 1.3f}, "Kstar invariant mass axis"}; ConfigurableAxis ptAxisKstar{"ptAxisKstar", {200, 0.0f, 20.0f}, "Kstar pT axis"}; ConfigurableAxis binsImpactPar{"binsImpactPar", {100, 0, 25}, "Binning of the impact parameter axis"}; ConfigurableAxis axisNch{"axisNch", {100, 0.0f, 100.0f}, "Number of charged particles in |y| < 0.5"}; - - // // Event plane configurables - // Configurable boostDaugter1{"boostDaugter1", false, "Boost daughter Kaon in the COM frame"}; - // Configurable boostDaugter2{"boostDaugter2", true, "Boost daughter Pion in the COM frame"}; - // Configurable activateTHnSparseCosThStarHelicity{"activateTHnSparseCosThStarHelicity", true, "Activate the THnSparse with cosThStar w.r.t. helicity axis"}; - // Configurable activateTHnSparseCosThStarProduction{"activateTHnSparseCosThStarProduction", false, "Activate the THnSparse with cosThStar w.r.t. production axis"}; - // Configurable activateTHnSparseCosThStarBeam{"activateTHnSparseCosThStarBeam", false, "Activate the THnSparse with cosThStar w.r.t. beam axis (Gottified jackson frame)"}; - // Configurable activateTHnSparseCosThStarRandom{"activateTHnSparseCosThStarRandom", false, "Activate the THnSparse with cosThStar w.r.t. random axis"}; + ConfigurableAxis etaAxis = {"etaAxis", {100, -1.0f, 1.0f}, "Eta axis"}; + ConfigurableAxis phiAxis = {"phiAxis", {100, -3.15f, 3.15f}, "Phi axis"}; + ConfigurableAxis subAxis = {"subAxis", {100, -1.0f, 1.0f}, "Difference axis"}; // Temporarily using fixed configurables for angular study bool boostDaugter1 = false; bool boostDaugter2 = true; bool activateTHnSparseCosThStarHelicity = true; - bool activateTHnSparseCosThStarProduction = false; - bool activateTHnSparseCosThStarBeam = false; - bool activateTHnSparseCosThStarRandom = false; TRandom* rn = new TRandom(); @@ -260,39 +224,72 @@ struct Kstarqa { rEventSelection.add("hMultiplicity", "Multiplicity percentile", kTH1F, {{110, 0, 110}}); rEventSelection.add("hEventCut", "No. of event after cuts", kTH1D, {{20, 0, 20}}); - std::shared_ptr hCutFlow = rEventSelection.get(HIST("hEventCut")); + std::shared_ptr hEventSelection = rEventSelection.get(HIST("hEventCut")); + rEventSelection.add("hEventCutMC", "No. of event after cuts in MC", kTH1D, {{10, 0, 10}}); + std::shared_ptr hEventSelectionMC = rEventSelection.get(HIST("hEventCutMC")); + rEventSelection.add("hTrackCut", "No. of tracks after cuts", kTH1D, {{20, 0, 20}}); + std::shared_ptr hTrackSelection = rEventSelection.get(HIST("hTrackCut")); auto check = [](bool enabled) { return enabled ? "" : " #otimes"; }; // check if a cut is enabled and put #otimes beside that label if not enabled std::vector eveCutLabels = { "All Events", + std::string("kIsTriggerTVX") + check(configGp.isTriggerTVX.value), "|Vz| < 10", - "sel8", - std::string("kNoTimeFrameBorder") + check(selectionConfig.isNoTimeFrameBorder.value), - std::string("kNoITSROFrameBorder") + check(selectionConfig.isNoITSROFrameBorder.value), - // std::string("kNoSameBunchPileup") + check(selectionConfig.isNoSameBunchPileup.value), - std::string("kIsGoodITSLayersAll") + check(selectionConfig.isGoodITSLayersAll.value), - // std::string("kNoCollInTimeRangeStandard") + check(selectionConfig.isNoCollInTimeRangeStandard.value), - // Form("Occupancy < %.0f%s", selectionConfig.configOccCut.value, check(selectionConfig.isApplyOccCut.value)), + std::string("NoTimeFrameBorder") + check(configGp.isNoTimeFrameBorder.value), + std::string("NoITSROFrameBorder") + check(configGp.isNoITSROFrameBorder.value), + std::string("Sel8") + check(configGp.isSel8.value), std::string("rctChecker") + check(rctCut.requireRCTFlagChecker.value), - std::string("kIsTriggerTVX") + check(selectionConfig.isTriggerTVX.value), - // std::string("kIsGoodZvtxFT0vsPV") + check(selectionConfig.isGoodZvtxFT0vsPV.value), - std::string("IsINELgt0") + check(selectionConfig.isINELgt0.value), - std::string("isVertexITSTPC") + check(selectionConfig.isVertexITSTPC.value), - // std::string("isVertexTOFMatched") + check(selectionConfig.isVertexTOFMatched.value) + std::string("IsINELgt0") + check(configGp.isINELgt0.value), + std::string("IsGoodITSLayersAll") + check(configGp.isGoodITSLayersAll.value), + std::string("isVertexITSTPC") + check(configGp.isVertexITSTPC.value), }; + + std::vector eveCutLabelsMC = { + "All Events", + "RecCollision", + "TimeFrameBorder", + "ITSROFrameBorder", + "Vz", + "TriggerTVX"}; + // assign labels for (size_t i = 0; i < eveCutLabels.size(); ++i) { - hCutFlow->GetXaxis()->SetBinLabel(i + 1, eveCutLabels[i].c_str()); + hEventSelection->GetXaxis()->SetBinLabel(i + 1, eveCutLabels[i].c_str()); + } + + for (size_t i = 0; i < eveCutLabelsMC.size(); ++i) { + hEventSelectionMC->GetXaxis()->SetBinLabel(i + 1, eveCutLabelsMC[i].c_str()); + } + + std::vector trackCutLabels = { + "All Tracks", + std::string("GlobalTracks") + check(configGp.isGlobalTracks.value), + std::string("pT > ") + std::to_string(configGp.cfgCutPT), + std::string("|#eta| < ") + std::to_string(configGp.cfgCutEtaMax), + std::string("DCAxy < ") + std::to_string(configGp.cfgCutDCAxyMax.value) + check(!configGp.isApplyPtDepDCACut.value), + std::string("DCAz < ") + std::to_string(configGp.cfgCutDCAz.value), + std::string("ITS clusters > ") + std::to_string(configGp.cfgITScluster.value), + std::string("TPC clusters > ") + std::to_string(configGp.cfgTPCcluster.value), + std::string("has ITS") + check(configGp.hasITS.value), + std::string("ITS Refit") + check(configGp.isITSTPCRefit.value), + std::string("PVContributor") + check(configGp.cfgPVContributor.value), + }; + + for (size_t i = 0; i < trackCutLabels.size(); ++i) { + hTrackSelection->GetXaxis()->SetBinLabel(i + 1, trackCutLabels[i].c_str()); } // for primary tracksbinsMultPlot if (cQAplots) { - // hOthers.add("dE_by_dx_TPC", "dE/dx signal in the TPC as a function of pT", kTH2F, {axisPtfordEbydx, axisdEdx}); hOthers.add("hphi", "Phi distribution", kTH1F, {{65, 0, 6.5}}); hOthers.add("hEta_after", "Eta distribution", kTH1F, {{200, -1.0f, 1.0f}}); hOthers.add("hCRFC_after", "CRFC after distribution", kTH1F, {{100, 0.0f, 10.0f}}); hOthers.add("hCRFC_before", "CRFC before distribution", kTH1F, {{100, 0.0f, 10.0f}}); + hOthers.add("hTOFBetaKa", "Beta distribution from TOF", kTH1F, {{50, 0.0f, 1.0f}}); + hOthers.add("hTOFBetaPi", "Beta distribution from TOF", kTH1F, {{50, 0.0f, 1.0f}}); + hOthers.add("hTPCMomDiffKa", "Momentum difference between global and TPC tracks for Kaons", kTH1F, {{100, -1.0f, 1.0f}}); + hOthers.add("hTPCMomDiffPi", "Momentum difference between global and TPC tracks for Pions", kTH1F, {{100, -1.0f, 1.0f}}); hOthers.add("hKstar_rap_pt", "Pair rapidity distribution; y; p_{T}; Counts", kTH2F, {{400, -2.0f, 2.0f}, ptAxis}); hOthers.add("hKstar_eta_pt", "Pair eta distribution; #eta; p_{T}; Counts", kTH2F, {{400, -2.0f, 2.0f}, ptAxis}); @@ -353,28 +350,55 @@ struct Kstarqa { hInvMass.add("h1RecMult", "Multiplicity reconstructed", kTH1F, {multiplicityAxis}); hInvMass.add("h1RecMult2", "Multiplicity reconstructed", kTH1F, {multiplicityAxis}); hInvMass.add("h1KSRecsplit", "KS meson Rec split", kTH1F, {{100, 0.0f, 10.0f}}); - // hInvMass.add("hAllGenCollisionsImpact", "All generated collisions vs impact parameter", kTH1F, {multiplicityAxis}); + + hInvMass.add("h3KstarMassGen", "Gen pt, multiplicity, mass", kTHnSparseF, {ptAxis, multiplicityAxis, invmassAxis}); + hInvMass.add("h3KstarMassGenCalib", "Gen pt, multiplicity, mass", kTHnSparseF, {ptAxis, multiplicityAxis, invmassAxis}); + hInvMass.add("h3KstarMassRec", "Rec pt, multiplicity, mass", kTHnSparseF, {ptAxis, multiplicityAxis, invmassAxis}); + hInvMass.add("h3KstarMassRecCalib", "Rec pt, multiplicity, mass", kTHnSparseF, {ptAxis, multiplicityAxis, invmassAxis}); + hInvMass.add("hAllGenCollisions", "All generated events", kTH1F, {multiplicityAxis}); hInvMass.add("hAllGenCollisions1Rec", "All gen events with at least one rec event", kTH1F, {multiplicityAxis}); hInvMass.add("hAllKstarGenCollisisons", "All generated Kstar in events with rapidity in 0.5", kTH2F, {{multiplicityAxis}, {ptAxis}}); hInvMass.add("hAllKstarGenCollisisons1Rec", "All generated Kstar in events with at least one rec event in rapidity in 0.5", kTH2F, {{multiplicityAxis}, {ptAxis}}); hInvMass.add("hAllRecCollisions", "All reconstructed events", kTH1F, {multiplicityAxis}); hInvMass.add("hAllRecCollisionsCalib", "All reconstructed events", kTH1F, {multiplicityAxis}); - hInvMass.add("sigEvLossFromGenRec/MultiplicityGen", "Multiplicity in generated MC", kTH1F, {multiplicityAxis}); - hInvMass.add("sigEvLossFromGenRec/MultiplicityRec", "Multiplicity in generated MC with at least 1 reconstruction", kTH1F, {multiplicityAxis}); - hInvMass.add("sigEvLossFromGenRec/hSignalLossDenominator", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); - hInvMass.add("sigEvLossFromGenRec/hSignalLossNumerator", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); if (doprocessEvtLossSigLossMC || doprocessEvtLossSigLossMCPhi) { hInvMass.add("MCcorrections/hSignalLossDenominator", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); + hInvMass.add("MCcorrections/hSignalLossDenominatorRec", "Kstar generated before event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hSignalLossNumerator", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); + hInvMass.add("MCcorrections/hSignalLossNumeratorINEL", "Kstar generated after event selection", kTH2F, {{ptAxis}, {multiplicityAxis}}); hInvMass.add("MCcorrections/hImpactParameterRec", "Impact parameter in reconstructed MC", kTH1F, {impactParAxis}); hInvMass.add("MCcorrections/MultiplicityRec", "Multiplicity in generated MC with at least 1 reconstruction", kTH1F, {multiplicityAxis}); hInvMass.add("MCcorrections/hImpactParameterGen", "Impact parameter in generated MC", kTH1F, {impactParAxis}); hInvMass.add("MCcorrections/MultiplicityGen", "Multiplicity in generated MC", kTH1F, {multiplicityAxis}); + hInvMass.add("MCcorrections/MultiplicityRecINEL", "Multiplicity in generated MC", kTH1F, {multiplicityAxis}); hInvMass.add("MCcorrections/hImpactParametervsMultiplicity", "Impact parameter vs multiplicity in reconstructed MC", kTH2F, {{impactParAxis}, {multiplicityAxis}}); } + if (doprocessRecPhi) { + hInvMass.add("PhiMCChecks", "PhiMCChecks", kTHnSparseF, {ptAxis, etaAxis, phiAxis, subAxis, subAxis, subAxis}); // pT, eta, phi, deltaEta, deltaPhi, deltaR + + hShift.add("h2D_dpx_vs_px_KaonFromPhi", "Kaon from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_KaonFromPhi", "Kaon from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_KaonFromPhi", "Kaon from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_KaonFromPhi", "Kaon from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h2D_dpx_vs_px_AllKaons", "Kaon from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_AllKaons", "Kaon from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_AllKaons", "Kaon from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_AllKaons", "Kaon from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h2D_dpx_vs_px_Phi", "Pion from Phi: dpx vs px", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpy_vs_py_Phi", "Pion from Phi: dpy vs py", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpz_vs_pz_Phi", "Pion from Phi: dpz vs pz", kTH2F, {{subAxis}, {ptAxis}}); + hShift.add("h2D_dpt_vs_pt_Phi", "Pion from Phi: dpt vs pt", kTH2F, {{subAxis}, {ptAxis}}); + + hShift.add("h1D_MassPhiGen", "Invariant mass of generated Phi meson", kTH1F, {{300, 1.0, 1.06}}); + hShift.add("h1D_MassPhiRec", "Invariant mass of reconstructed Phi meson", kTH1F, {{300, 1.0, 1.06}}); + hShift.add("h2D_pt_massShiftPhi", "pT vs mass shift of Phi meson", kTH2F, {{ptAxis}, {300, -0.1, 0.1}}); + } + // Signal Loss & Event Loss in Light Ion Collisions if (doprocessEvtLossSigLossLightIonMC) { hInvMass.add("MCcorrections/hImpactParameterGen", "Impact parameter of generated MC events", kTH1F, {impactParAxis}); @@ -405,6 +429,7 @@ struct Kstarqa { rEventSelection.add("tracksCheckData", "No. of events in the data", kTH1I, {{10, 0, 10}}); rEventSelection.add("eventsCheckGen", "No. of events in the generated MC", kTH1I, {{10, 0, 10}}); + rEventSelection.add("eventsCheckRec", "No. of events in the generated MC", kTH1I, {{5, 0, 5}}); rEventSelection.add("recMCparticles", "No. of events in the reconstructed MC", kTH1I, {{20, 0, 20}}); rEventSelection.add("hOccupancy", "Occupancy distribution", kTH1F, {{1000, 0, 15000}}); @@ -438,20 +463,21 @@ struct Kstarqa { std::shared_ptr hGenTracks = rEventSelection.get(HIST("eventsCheckGen")); hGenTracks->GetXaxis()->SetBinLabel(1, "All events"); - hGenTracks->GetXaxis()->SetBinLabel(2, "INELgt0+vtz"); + hGenTracks->GetXaxis()->SetBinLabel(2, "Vz cut"); hGenTracks->GetXaxis()->SetBinLabel(3, "INELgt0"); - hGenTracks->GetXaxis()->SetBinLabel(4, "All Collisions"); - hGenTracks->GetXaxis()->SetBinLabel(5, "Gen events with at least 1 rec event"); - hGenTracks->GetXaxis()->SetBinLabel(6, "Rec events"); + hGenTracks->GetXaxis()->SetBinLabel(4, "PWGlf:INELgt0"); + hGenTracks->GetXaxis()->SetBinLabel(5, "Atleat 1rec"); + + std::shared_ptr hRecTracks = rEventSelection.get(HIST("eventsCheckRec")); + hRecTracks->GetXaxis()->SetBinLabel(1, "All events"); + hRecTracks->GetXaxis()->SetBinLabel(2, "has_MCcollision"); + hRecTracks->GetXaxis()->SetBinLabel(3, "INELgt0"); + hRecTracks->GetXaxis()->SetBinLabel(4, "Event_Selection"); // Multplicity distribution if (cQAevents) { - // rEventSelection.add("multdist_FT0M", "FT0M Multiplicity distribution", kTH1F, {axisMultdist}); - // hInvMass.add("multdist_FT0A", "FT0A Multiplicity distribution", kTH1F, {axisMultdist}); - // hInvMass.add("multdist_FT0C", "FT0C Multiplicity distribution", kTH1F, {axisMultdist}); - // hInvMass.add("hNcontributor", "Number of primary vertex contributor", kTH1F, {{2000, 0.0f, 10000.0f}}); - rEventSelection.add("hDcaxy_cent_pt", "Dcaxy distribution", kTH3F, {{200, -1.0f, 1.0f}, multiplicityAxis, ptAxis}); - rEventSelection.add("hDcaz_cent_pt", "Dcaz distribution", kTH3F, {{200, -1.0f, 1.0f}, multiplicityAxis, ptAxis}); + rEventSelection.add("hDcaxy_cent_pt", "Dcaxy distribution", kTH3F, {{200, -0.2f, 0.2f}, multiplicityAxis, ptAxis}); + rEventSelection.add("hDcaz_cent_pt", "Dcaz distribution", kTH3F, {{200, -0.2f, 0.2f}, multiplicityAxis, ptAxis}); } } @@ -465,183 +491,248 @@ struct Kstarqa { if (fillHist) rEventSelection.fill(HIST("hEventCut"), 0); - if (std::abs(collision.posZ()) > selectionConfig.cutzvertex) + if (configGp.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 1); - if (selectionConfig.isSel8 && !collision.sel8()) + if (std::abs(collision.posZ()) > configGp.cutzvertex) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 2); - if (selectionConfig.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + if (configGp.isNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 3); - if (selectionConfig.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + if (configGp.isNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 4); - // if (selectionConfig.isNoSameBunchPileup && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup))) - // return false; - // if (fillHist) - // rEventSelection.fill(HIST("hEventCut"), 5); - - if (selectionConfig.isGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + if (configGp.isSel8 && !collision.sel8()) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 5); - // if (selectionConfig.isNoCollInTimeRangeStandard && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) - // return false; - // if (fillHist) - // rEventSelection.fill(HIST("hEventCut"), 7); - - // if (selectionConfig.isApplyOccCut && (std::abs(collision.trackOccupancyInTimeRange()) > selectionConfig.configOccCut)) - // return false; - // if (fillHist) - // rEventSelection.fill(HIST("hEventCut"), 8); - if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) return false; if (fillHist) rEventSelection.fill(HIST("hEventCut"), 6); - if (selectionConfig.isTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) + if (configGp.isINELgt0 && !collision.isInelGt0()) { return false; + } if (fillHist) rEventSelection.fill(HIST("hEventCut"), 7); - // if (selectionConfig.isGoodZvtxFT0vsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) - // return false; - // if (fillHist) - // rEventSelection.fill(HIST("hEventCut"), 11); - - if (selectionConfig.isINELgt0 && !collision.isInelGt0()) { + if (configGp.isGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) return false; - } if (fillHist) rEventSelection.fill(HIST("hEventCut"), 8); - if (selectionConfig.isVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + if (configGp.isVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } if (fillHist) rEventSelection.fill(HIST("hEventCut"), 9); - // if (selectionConfig.isVertexTOFMatched && !collision.selection_bit(aod::evsel::kIsVertexTOFmatched)) { - // return false; - // } - // if (fillHist) - // rEventSelection.fill(HIST("hEventCut"), 14); - return true; } template bool selectionTrack(const T& candidate) { - if (selectionConfig.isGlobalTracks) { - if (!candidate.isGlobalTrackWoDCA()) + + rEventSelection.fill(HIST("hTrackCut"), 0); + + if (configGp.isGlobalTracks) { + if (!candidate.isGlobalTrack()) return false; - if (std::abs(candidate.pt()) < selectionConfig.cfgCutPT) + + rEventSelection.fill(HIST("hTrackCut"), 1); + + if (std::abs(candidate.pt()) < configGp.cfgCutPT) return false; - // if (std::abs(candidate.eta()) > selectionConfig.cfgCutEtaMax || std::abs(candidate.eta()) < selectionConfig.cfgCutEtaMin) - if (std::abs(candidate.eta()) > selectionConfig.cfgCutEtaMax) + + rEventSelection.fill(HIST("hTrackCut"), 2); + + if (std::abs(candidate.eta()) > configGp.cfgCutEtaMax) return false; - if (!selectionConfig.isApplyPtDepDCAxyCut) { - // if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxyMax || std::abs(candidate.dcaXY()) < selectionConfig.cfgCutDCAxyMin) - if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxyMax) + + rEventSelection.fill(HIST("hTrackCut"), 3); + + if (!configGp.isApplyPtDepDCACut) { + if (std::abs(candidate.dcaXY()) > configGp.cfgCutDCAxyMax) return false; + } else { - if (std::abs(candidate.dcaXY()) > (0.0105 + 0.035 / std::pow(candidate.pt(), 1.1))) + if (std::abs(candidate.dcaXY()) > (configGp.cfgVariableDCAParamOne + configGp.cfgVariableDCAParamTwo / std::pow(candidate.pt(), configGp.cfgVariableDCApTPower))) return false; } - if (std::abs(candidate.dcaZ()) > selectionConfig.cfgCutDCAz) - return false; - // if (candidate.tpcCrossedRowsOverFindableCls() < selectionConfig.cfgRCRFC) - // return false; - // if (candidate.itsNCls() < selectionConfig.cfgITScluster) - // return false; - if (candidate.tpcNClsFound() < selectionConfig.cfgTPCcluster) + + rEventSelection.fill(HIST("hTrackCut"), 4); + + if (!configGp.isApplyPtDepDCACut) { + if (std::abs(candidate.dcaZ()) > configGp.cfgCutDCAz) + return false; + } else { + if (std::abs(candidate.dcaZ()) > (configGp.cfgVariableDCAParamOne + configGp.cfgVariableDCAParamTwo / std::pow(candidate.pt(), configGp.cfgVariableDCApTPower))) + return false; + } + + rEventSelection.fill(HIST("hTrackCut"), 5); + + if (candidate.itsNCls() < configGp.cfgITScluster) return false; - // if (candidate.itsChi2NCl() >= selectionConfig.cfgITSChi2NCl) - // return false; - // if (candidate.tpcChi2NCl() >= selectionConfig.cfgTPCChi2NClMax || candidate.tpcChi2NCl() < selectionConfig.cfgTPCChi2NClMin) - // return false; - if (selectionConfig.hasITS && !candidate.hasITS()) + + rEventSelection.fill(HIST("hTrackCut"), 6); + + if (candidate.tpcNClsFound() < configGp.cfgTPCcluster) return false; - if (selectionConfig.isITSTPCRefit && (!(o2::aod::track::ITSrefit) || !(o2::aod::track::TPCrefit))) + + rEventSelection.fill(HIST("hTrackCut"), 7); + + if (configGp.hasITS && !candidate.hasITS()) return false; - if (selectionConfig.cfgPVContributor && !candidate.isPVContributor()) + + rEventSelection.fill(HIST("hTrackCut"), 8); + + if (configGp.isITSTPCRefit) { + if (!(candidate.flags() & o2::aod::track::ITSrefit) || + !(candidate.flags() & o2::aod::track::TPCrefit)) { + return false; + } + } + + rEventSelection.fill(HIST("hTrackCut"), 9); + + if (configGp.cfgPVContributor && !candidate.isPVContributor()) return false; - } else if (!selectionConfig.isGlobalTracks) { - if (std::abs(candidate.pt()) < selectionConfig.cfgCutPT) + + rEventSelection.fill(HIST("hTrackCut"), 10); + + } else if (!configGp.isGlobalTracks) { + + rEventSelection.fill(HIST("hTrackCut"), 0); + + if (!candidate.isGlobalTrackWoDCA()) return false; - // if (std::abs(candidate.eta()) > selectionConfig.cfgCutEtaMax || std::abs(candidate.eta()) < selectionConfig.cfgCutEtaMin) - if (std::abs(candidate.eta()) > selectionConfig.cfgCutEtaMax) + + rEventSelection.fill(HIST("hTrackCut"), 1); + + if (std::abs(candidate.pt()) < configGp.cfgCutPT) return false; - // if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxyMax || std::abs(candidate.dcaXY()) < selectionConfig.cfgCutDCAxyMin) - if (std::abs(candidate.dcaXY()) > selectionConfig.cfgCutDCAxyMax) + + rEventSelection.fill(HIST("hTrackCut"), 2); + + if (std::abs(candidate.eta()) > configGp.cfgCutEtaMax) return false; - if (std::abs(candidate.dcaZ()) > selectionConfig.cfgCutDCAz) + + rEventSelection.fill(HIST("hTrackCut"), 3); + + if (!configGp.isApplyPtDepDCACut) { + if (std::abs(candidate.dcaXY()) > configGp.cfgCutDCAxyMax) + return false; + + } else { + if (std::abs(candidate.dcaXY()) > (configGp.cfgVariableDCAParamOne + configGp.cfgVariableDCAParamTwo / std::pow(candidate.pt(), configGp.cfgVariableDCApTPower))) + return false; + } + + rEventSelection.fill(HIST("hTrackCut"), 4); + + if (!configGp.isApplyPtDepDCACut) { + if (std::abs(candidate.dcaZ()) > configGp.cfgCutDCAz) + return false; + } else { + if (std::abs(candidate.dcaZ()) > (configGp.cfgVariableDCAParamOne + configGp.cfgVariableDCAParamTwo / std::pow(candidate.pt(), configGp.cfgVariableDCApTPower))) + return false; + } + + rEventSelection.fill(HIST("hTrackCut"), 5); + + if (candidate.itsNCls() < configGp.cfgITScluster) return false; - // if (candidate.tpcCrossedRowsOverFindableCls() < selectionConfig.cfgRCRFC) - return false; - // if (candidate.itsNCls() < selectionConfig.cfgITScluster) - // return false; - if (candidate.tpcNClsFound() < selectionConfig.cfgTPCcluster) + + rEventSelection.fill(HIST("hTrackCut"), 6); + + if (candidate.tpcNClsFound() < configGp.cfgTPCcluster) return false; - // if (candidate.itsChi2NCl() >= selectionConfig.cfgITSChi2NCl) - // return false; - // if (candidate.tpcChi2NCl() >= selectionConfig.cfgTPCChi2NClMax || candidate.tpcChi2NCl() < selectionConfig.cfgTPCChi2NClMin) - // return false; - if (selectionConfig.cfgPVContributor && !candidate.isPVContributor()) + + if (configGp.hasITS && !candidate.hasITS()) return false; - if (!candidate.isPrimaryTrack()) + + rEventSelection.fill(HIST("hTrackCut"), 7); + + if (configGp.isITSTPCRefit) { + if (!(candidate.flags() & o2::aod::track::ITSrefit) || + !(candidate.flags() & o2::aod::track::TPCrefit)) { + return false; + } + } + + rEventSelection.fill(HIST("hTrackCut"), 8); + + if (configGp.cfgPVContributor && !candidate.isPVContributor()) return false; + + rEventSelection.fill(HIST("hTrackCut"), 9); } return true; } - // template - // bool isFakeTrack(const T& track, int PID) - // { - // const auto pglobal = track.p(); - // const auto ptpc = track.tpcInnerParam(); - // if (PID == 0 && std::abs(pglobal - ptpc) > cFakeTrackCutPi) { - // return true; - // } - // if (PID == 1 && std::abs(pglobal - ptpc) > cFakeTrackCutKa) { - // return true; - // } - // return false; - // } + template + bool isFakeTrack(const T& track) + { + const auto pglobal = track.p(); + const auto ptpc = track.tpcInnerParam(); + return std::abs(pglobal - ptpc) > configGp.cFakeTrackCutKa; + } // deep angle cut on pair to remove photon conversion template bool selectionPair(const T1& candidate1, const T2& candidate2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; - pt1 = candidate1.pt(); - pt2 = candidate2.pt(); - pz1 = candidate1.pz(); - pz2 = candidate2.pz(); - p1 = candidate1.p(); - p2 = candidate2.p(); - angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - if (selectionConfig.isApplyDeepAngle && angle < selectionConfig.cfgDeepAngle) { - return false; + const double pt1 = candidate1.pt(); + const double pt2 = candidate2.pt(); + const double pz1 = candidate1.pz(); + const double pz2 = candidate2.pz(); + const double p1 = candidate1.p(); + const double p2 = candidate2.p(); + const double angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); + + return !configGp.isApplyDeepAngle || angle >= configGp.cfgDeepAngle; + } + + template + bool selectionPID2(const T& candidate, int PID) + { + float ptThreshold = 0.5; // GeV/c + if (PID == PIDParticle::kPion) { + if (candidate.pt() < ptThreshold && TMath::Abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { + return true; + } + if (candidate.pt() >= ptThreshold && TMath::Abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi && candidate.hasTOF() && (std::pow(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, 2) + std::pow(candidate.tpcNSigmaPi(), 2)) < (std::pow(configGp.nsigmaCutCombinedPi, 2))) { + return true; + } + if (candidate.pt() >= ptThreshold && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { + return true; + } + } else if (PID == PIDParticle::kKaon) { + if (candidate.pt() < ptThreshold && TMath::Abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa) { + return true; + } + if (candidate.pt() >= ptThreshold && candidate.hasTOF() && (std::pow(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, 2) + std::pow(candidate.tpcNSigmaKa(), 2)) < (std::pow(configGp.nsigmaCutCombinedKa, 2)) && candidate.beta() > configGp.cBetaCutTOFKaon) { + return true; + } + if (candidate.pt() >= ptThreshold && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa) { + return true; + } } - // double deltaRvalue = std::sqrt(TVector2::Phi_mpi_pi(candidate1.phi() - candidate2.phi()) * TVector2::Phi_mpi_pi(candidate1.phi() - candidate2.phi()) + (candidate1.eta() - candidate2.eta()) * (candidate1.eta() - candidate2.eta())); - // if (deltaRvalue < selectionConfig.deltaRCut) { - // return false; - // } - return true; + return false; } template @@ -649,60 +740,72 @@ struct Kstarqa { { if (PID == PIDParticle::kPion) { if (onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTOFPi) { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi) < configGp.nsigmaCutTOFPi) { return true; } } else if (onlyTPC) { - if (std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { + if (std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { return true; } } else { - if (candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (selectionConfig.nsigmaCutCombinedPi * selectionConfig.nsigmaCutCombinedPi)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.hasTOF() && (std::pow(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, 2) + std::pow(candidate.tpcNSigmaPi(), 2)) < (std::pow(configGp.nsigmaCutCombinedPi, 2))) { + return true; + } + } else { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi) < configGp.nsigmaCutTOFPi && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { + return true; + } } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { + if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { return true; } } } else if (PID == PIDParticle::kKaon) { if (onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTOFKa) { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < configGp.nsigmaCutTOFKa) { return true; } } else if (onlyTPC) { - if (std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { + if (std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa) { return true; } } else { - if (candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (selectionConfig.nsigmaCutCombinedKa * selectionConfig.nsigmaCutCombinedKa)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.hasTOF() && (std::pow(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, 2) + std::pow(candidate.tpcNSigmaKa(), 2)) < (std::pow(configGp.nsigmaCutCombinedKa, 2))) { + return true; + } + } else { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < configGp.nsigmaCutTOFKa && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa) { + return true; + } } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { + if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa) { return true; } } } /* else if (PID == PIDParticle::kProton) { // for proton if (onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < selectionConfig.nsigmaCutTOFPr ) { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < configGp.nsigmaCutTOFPr ) { return true; } } else if (onlyTOFHIT) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < selectionConfig.nsigmaCutTOFPr ) { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < configGp.nsigmaCutTOFPr ) { return true; } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < selectionConfig.nsigmaCutTPCPr) { + if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < configGp.nsigmaCutTPCPr) { return true; } } else if (onlyTPC) { - if (std::abs(candidate.tpcNSigmaPr()) < selectionConfig.nsigmaCutTPCPr) { + if (std::abs(candidate.tpcNSigmaPr()) < configGp.nsigmaCutTPCPr) { return true; } } else { - if (candidate.hasTOF() && (candidate.tofNSigmaPr() * candidate.tofNSigmaPr() + candidate.tpcNSigmaPr() * candidate.tpcNSigmaPr()) < (selectionConfig.nsigmaCutTOFPr * selectionConfig.nsigmaCutTOFPr) ) { + if (candidate.hasTOF() && (candidate.tofNSigmaPr() * candidate.tofNSigmaPr() + candidate.tpcNSigmaPr() * candidate.tpcNSigmaPr()) < (configGp.nsigmaCutTOFPr * configGp.nsigmaCutTOFPr) ) { return true; } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < selectionConfig.nsigmaCutTPCPr) { + if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < configGp.nsigmaCutTPCPr) { return true; } } @@ -715,60 +818,72 @@ struct Kstarqa { { if (PID == PIDParticle::kPion) { if (onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi) < configGp.nsigmaCutTPCMID) { return true; } } else if (onlyTPC) { - if (std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { + if (std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCMID) { return true; } } else { - if (candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (selectionConfig.nsigmaCutCombinedMID * selectionConfig.nsigmaCutCombinedMID)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.hasTOF() && (std::pow(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, 2) + std::pow(candidate.tpcNSigmaPi(), 2)) < (std::pow(configGp.nsigmaCutCombinedMID, 2))) { + return true; + } + } else { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi) < configGp.nsigmaCutCombinedMID && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutCombinedMID) { + return true; + } } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { + if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCMID) { return true; } } } else if (PID == PIDParticle::kKaon) { if (onlyTOF) { - if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < configGp.nsigmaCutTPCMID) { return true; } } else if (onlyTPC) { - if (std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { + if (std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCMID) { return true; } } else { - if (candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (selectionConfig.nsigmaCutCombinedMID * selectionConfig.nsigmaCutCombinedMID)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.hasTOF() && (std::pow(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, 2) + std::pow(candidate.tpcNSigmaKa(), 2)) < (std::pow(configGp.nsigmaCutCombinedMID, 2))) { + return true; + } + } else { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < configGp.nsigmaCutCombinedMID && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutCombinedMID) { + return true; + } } - if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { + if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCMID) { return true; } } } // else if (PID == PIDParticle::kProton) { // for proton // if (onlyTOF) { - // if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < selectionConfig.nsigmaCutTOFPr && ) { + // if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < configGp.nsigmaCutTOFPr && ) { // return true; // } // } else if (onlyTOFHIT) { - // if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < selectionConfig.nsigmaCutTOFPr && ) { + // if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPr()) < configGp.nsigmaCutTOFPr && ) { // return true; // } - // if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < selectionConfig.nsigmaCutTPCPr) { + // if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < configGp.nsigmaCutTPCPr) { // return true; // } // } else if (onlyTPC) { - // if (std::abs(candidate.tpcNSigmaPr()) < selectionConfig.nsigmaCutTPCPr) { + // if (std::abs(candidate.tpcNSigmaPr()) < configGp.nsigmaCutTPCPr) { // return true; // } // } else { - // if (candidate.hasTOF() && (candidate.tofNSigmaPr() * candidate.tofNSigmaPr() + candidate.tpcNSigmaPr() * candidate.tpcNSigmaPr()) < (selectionConfig.nsigmaCutTOFPr * selectionConfig.nsigmaCutTOFPr) && ) { + // if (candidate.hasTOF() && (candidate.tofNSigmaPr() * candidate.tofNSigmaPr() + candidate.tpcNSigmaPr() * candidate.tpcNSigmaPr()) < (configGp.nsigmaCutTOFPr * configGp.nsigmaCutTOFPr) && ) { // return true; // } - // if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < selectionConfig.nsigmaCutTPCPr) { + // if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaPr()) < configGp.nsigmaCutTPCPr) { // return true; // } // } @@ -784,11 +899,30 @@ struct Kstarqa { const float ptCut07 = 0.7f; if (PID == PIDParticle::kPion) { - if (candidate.pt() >= ptCut1 && candidate.pt() < ptCut2p5 && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCMID) { + if (candidate.pt() >= ptCut1 && candidate.pt() < ptCut2p5 && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCMID) { + return true; + } + } else if (PID == PIDParticle::kKaon) { + if (candidate.pt() >= ptCut07 && candidate.pt() < ptCut2p5 && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCMID) { + return true; + } + } + return false; + } + + template + bool selectionMIDPtDep2(const T& candidate, int PID) + { + const float ptCut1 = 1.0f; + const float ptCut2p5 = 2.5f; + const float ptCut07 = 0.7f; + + if (PID == PIDParticle::kPion) { + if (candidate.pt() >= ptCut1 && candidate.pt() < ptCut2p5 && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaPi()) < std::abs(candidate.tpcNSigmaKa())) { return true; } } else if (PID == PIDParticle::kKaon) { - if (candidate.pt() >= ptCut07 && candidate.pt() < ptCut2p5 && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCMID) { + if (candidate.pt() >= ptCut07 && candidate.pt() < ptCut2p5 && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < std::abs(candidate.tpcNSigmaPi())) { return true; } } @@ -816,59 +950,76 @@ struct Kstarqa { const float sigmaThreshold1 = 1.0f; if (PID == PIDParticle::kPion) { - if (candidate.pt() < selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { + if (candidate.pt() < configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { return true; } - if (candidate.pt() >= selectionConfig.lowPtCutPID && !candidate.hasTOF()) { - if (candidate.pt() < ptCut1 && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { + if (candidate.pt() >= configGp.lowPtCutPID && !candidate.hasTOF()) { + if (candidate.pt() < ptCut1 && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { return true; } - if (candidate.pt() >= ptCut1 && candidate.pt() < ptCut2 && candidate.tpcNSigmaPi() > sigmaNeg15 && candidate.tpcNSigmaPi() < selectionConfig.nsigmaCutTPCPi) { + if (candidate.pt() >= ptCut1 && candidate.pt() < ptCut2 && candidate.tpcNSigmaPi() > sigmaNeg15 && candidate.tpcNSigmaPi() < configGp.nsigmaCutTPCPi) { return true; } - if (candidate.pt() >= ptCut2 && candidate.pt() < ptCut5 && candidate.tpcNSigmaPi() > sigmaNeg1 && candidate.tpcNSigmaPi() < selectionConfig.nsigmaCutTPCPi) { + if (candidate.pt() >= ptCut2 && candidate.pt() < ptCut5 && candidate.tpcNSigmaPi() > sigmaNeg1 && candidate.tpcNSigmaPi() < configGp.nsigmaCutTPCPi) { return true; } - if (candidate.pt() >= ptCut5 && candidate.tpcNSigmaPi() > sigmaNeg05 && candidate.tpcNSigmaPi() < selectionConfig.nsigmaCutTPCPi) { + if (candidate.pt() >= ptCut5 && candidate.tpcNSigmaPi() > sigmaNeg05 && candidate.tpcNSigmaPi() < configGp.nsigmaCutTPCPi) { return true; } } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi && candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (selectionConfig.nsigmaCutCombinedPi * selectionConfig.nsigmaCutCombinedPi)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.pt() >= configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi && candidate.hasTOF() && (std::pow(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, 2) + std::pow(candidate.tpcNSigmaPi(), 2)) < (std::pow(configGp.nsigmaCutCombinedPi, 2))) { + return true; + } + } else { + if (candidate.pt() >= configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi && candidate.hasTOF() && std::abs(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi) < configGp.nsigmaCutCombinedPi) { + return true; + } } } else if (PID == PIDParticle::kKaon) { - if (candidate.pt() < selectionConfig.lowPtCutPID && candidate.tpcNSigmaKa() > sigmaNeg2 && candidate.tpcNSigmaKa() < selectionConfig.nsigmaCutTPCKa) { + if (candidate.pt() < configGp.lowPtCutPID && candidate.tpcNSigmaKa() > sigmaNeg2 && candidate.tpcNSigmaKa() < configGp.nsigmaCutTPCKa) { return true; } - if (candidate.pt() >= selectionConfig.lowPtCutPID && !candidate.hasTOF()) { - if (candidate.pt() >= ptCut05 && candidate.pt() < ptCut06 && candidate.tpcNSigmaKa() > sigmaNeg15 && candidate.tpcNSigmaKa() < selectionConfig.nsigmaCutTPCKa) { + if (candidate.pt() >= configGp.lowPtCutPID && !candidate.hasTOF()) { + if (candidate.pt() >= ptCut05 && candidate.pt() < ptCut06 && candidate.tpcNSigmaKa() > sigmaNeg15 && candidate.tpcNSigmaKa() < configGp.nsigmaCutTPCKa) { return true; } - if (candidate.pt() >= ptCut06 && candidate.pt() < ptCut07 && candidate.tpcNSigmaKa() > sigmaNeg1 && candidate.tpcNSigmaKa() < selectionConfig.nsigmaCutTPCKa) { + if (candidate.pt() >= ptCut06 && candidate.pt() < ptCut07 && candidate.tpcNSigmaKa() > sigmaNeg1 && candidate.tpcNSigmaKa() < configGp.nsigmaCutTPCKa) { return true; } - if (candidate.pt() >= ptCut07 && candidate.pt() < ptCut08 && candidate.tpcNSigmaKa() > sigmaNeg05 && candidate.tpcNSigmaKa() < selectionConfig.nsigmaCutTPCKa) { + if (candidate.pt() >= ptCut07 && candidate.pt() < ptCut08 && candidate.tpcNSigmaKa() > sigmaNeg05 && candidate.tpcNSigmaKa() < configGp.nsigmaCutTPCKa) { return true; } - if (candidate.pt() >= ptCut08 && candidate.pt() < ptCut1 && candidate.tpcNSigmaKa() > sigma0 && candidate.tpcNSigmaKa() < selectionConfig.nsigmaCutTPCKa) { + if (candidate.pt() >= ptCut08 && candidate.pt() < ptCut1 && candidate.tpcNSigmaKa() > sigma0 && candidate.tpcNSigmaKa() < configGp.nsigmaCutTPCKa) { return true; } - if (candidate.pt() >= ptCut1 && candidate.pt() < ptCut2 && candidate.tpcNSigmaKa() > -selectionConfig.nsigmaCutTPCKa && candidate.tpcNSigmaKa() < sigma0) { + if (candidate.pt() >= ptCut1 && candidate.pt() < ptCut2 && candidate.tpcNSigmaKa() > -configGp.nsigmaCutTPCKa && candidate.tpcNSigmaKa() < sigma0) { return true; } - if (candidate.pt() >= ptCut2 && candidate.pt() < ptCut4 && candidate.tpcNSigmaKa() > -selectionConfig.nsigmaCutTPCKa && candidate.tpcNSigmaKa() < sigma1) { + if (candidate.pt() >= ptCut2 && candidate.pt() < ptCut4 && candidate.tpcNSigmaKa() > -configGp.nsigmaCutTPCKa && candidate.tpcNSigmaKa() < sigma1) { return true; } - if (candidate.pt() >= ptCut4 && candidate.tpcNSigmaKa() > -selectionConfig.nsigmaCutTPCKa && candidate.tpcNSigmaKa() < sigma15) { + if (candidate.pt() >= ptCut4 && candidate.tpcNSigmaKa() > -configGp.nsigmaCutTPCKa && candidate.tpcNSigmaKa() < sigma15) { return true; } } - if (candidate.pt() >= selectionConfig.lowPtCutPID && candidate.hasTOF()) { - if (candidate.pt() < ptCut2 && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (selectionConfig.nsigmaCutCombinedKa * selectionConfig.nsigmaCutCombinedKa)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.pt() >= configGp.lowPtCutPID && candidate.hasTOF()) { + if (candidate.pt() < ptCut2 && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa && (std::pow(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, 2) + std::pow(candidate.tpcNSigmaKa(), 2)) < (std::pow(configGp.nsigmaCutCombinedKa, 2))) { + return true; + } + if (candidate.pt() >= ptCut2 && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa && (std::pow(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, 2) + std::pow(candidate.tpcNSigmaKa(), 2)) < (std::pow(sigmaThreshold1, 2))) { + return true; + } } - if (candidate.pt() >= ptCut2 && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (sigmaThreshold1 * sigmaThreshold1)) { - return true; + } else { + if (candidate.pt() >= configGp.lowPtCutPID && candidate.hasTOF()) { + if (candidate.pt() < ptCut2 && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < configGp.nsigmaCutCombinedKa) { + return true; + } + if (candidate.pt() >= ptCut2 && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < sigmaThreshold1) { + return true; + } } } } @@ -884,216 +1035,155 @@ struct Kstarqa { if (PID == PIDParticle::kPion) { if (candidate.pt() < ptCut1 || candidate.pt() > ptCut2p5) { - if (candidate.pt() < selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi) { + if (candidate.pt() < configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi) { return true; } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi && candidate.hasTOF() && std::abs(candidate.tofNSigmaPi()) < selectionConfig.nsigmaCutTOFPi) { + if (candidate.pt() >= configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi && candidate.hasTOF() && std::abs(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi) < configGp.nsigmaCutTOFPi) { return true; } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < selectionConfig.nsigmaCutTPCPi && !candidate.hasTOF()) { + if (candidate.pt() >= configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutTPCPi && !candidate.hasTOF()) { return true; } } else { - if (candidate.hasTOF() && (candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi()) < (selectionConfig.nsigmaCutCombinedPi * selectionConfig.nsigmaCutCombinedPi)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.hasTOF() && (std::pow(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, 2) + std::pow(candidate.tpcNSigmaPi(), 2)) < (configGp.nsigmaCutCombinedPi * configGp.nsigmaCutCombinedPi)) { + return true; + } + } else { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi) < configGp.nsigmaCutCombinedPi && std::abs(candidate.tpcNSigmaPi()) < configGp.nsigmaCutCombinedPi) { + return true; + } } } } else if (PID == PIDParticle::kKaon) { if (candidate.pt() < ptCut07 || candidate.pt() > ptCut2p5) { - if (candidate.pt() < selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa) { + if (candidate.pt() < configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa) { return true; } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && candidate.hasTOF() && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmaCutTOFKa) { + if (candidate.pt() >= configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa && candidate.hasTOF() && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < configGp.nsigmaCutTOFKa) { return true; } - if (candidate.pt() >= selectionConfig.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmaCutTPCKa && !candidate.hasTOF()) { + if (candidate.pt() >= configGp.lowPtCutPID && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutTPCKa && !candidate.hasTOF()) { return true; } } else { - if (candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (selectionConfig.nsigmaCutCombinedKa * selectionConfig.nsigmaCutCombinedKa)) { - return true; + if (!configGp.isSquarePIDcut) { + if (candidate.hasTOF() && (std::pow(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, 2) + std::pow(candidate.tpcNSigmaKa(), 2)) < (std::pow(configGp.nsigmaCutCombinedKa, 2))) { + return true; + } + } else { + if (candidate.hasTOF() && std::abs(candidate.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa) < configGp.nsigmaCutCombinedKa && std::abs(candidate.tpcNSigmaKa()) < configGp.nsigmaCutCombinedKa) { + return true; + } } } } return false; } - std::array pvec0; - std::array pvec1; + std::array pvec0 = {0.0, 0.0, 0.0}; + std::array pvec1 = {0.0, 0.0, 0.0}; // Defining filters for events (event selection) // Processed events will be already fulfilling the event selection // requirements // Filter eventFilter = (o2::aod::evsel::sel8 == true); - Filter posZFilter = (nabs(o2::aod::collision::posZ) < selectionConfig.cutzvertex); + // Filter posZFilter = (nabs(o2::aod::collision::posZ) < configGp.cutzvertex); - // Filter acceptanceFilter = (nabs(aod::track::eta) < selectionConfig.cfgCutEtaMax && nabs(aod::track::pt) > selectionConfig.cfgCutPT) && (nabs(aod::track::eta) > selectionConfig.cfgCutEtaMin); - Filter acceptanceFilter = (nabs(aod::track::eta) < selectionConfig.cfgCutEtaMax && nabs(aod::track::pt) > selectionConfig.cfgCutPT); - // Filter fDCAcutFilter = (nabs(aod::track::dcaXY) < selectionConfig.cfgCutDCAxyMax) && (nabs(aod::track::dcaZ) < selectionConfig.cfgCutDCAz) && (nabs(aod::track::dcaXY) > selectionConfig.cfgCutDCAxyMin); - Filter fDCAcutFilter = (nabs(aod::track::dcaXY) < selectionConfig.cfgCutDCAxyMax) && (nabs(aod::track::dcaZ) < selectionConfig.cfgCutDCAz); + Filter acceptanceFilter = (nabs(aod::track::eta) < configGp.cfgCutEtaMax && nabs(aod::track::pt) > configGp.cfgCutPT); + Filter fDCAcutFilter = (nabs(aod::track::dcaXY) < configGp.cfgCutDCAxyMax) && (nabs(aod::track::dcaZ) < configGp.cfgCutDCAz); + + // using EventCandidates = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + // using EventCandidatesMix = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + + using EventCandidates = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs + using EventCandidatesMix = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs - using EventCandidates = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs - using EventCandidatesMix = soa::Filtered>; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs using TrackCandidates = soa::Filtered>; using EventCandidatesMC = soa::Join; // using EventCandidatesMC = soa::Filtered>; using TrackCandidatesMC = soa::Filtered>; - // using EventMCGenerated = soa::Join; // aod::CentNGlobals, aod::CentNTPVs, aod::CentMFTs using EventMCGenerated = soa::Join; using McCollisionMults = soa::Join; using LabeledTracks = soa::Join; //*********Varibles declaration*************** - float multiplicity{-1.0}, theta2; - ROOT::Math::PxPyPzMVector daughter1, daughter2, daughterRot, mother, motherRot, daughterSelected, fourVecDauCM, daughterRotCM; + float multiplicity{-1.0}, theta2{0.0}; + ROOT::Math::PxPyPzMVector daughter1, daughter2, daughterRot, mother, motherRot, daughterSelected, fourVecDauCM, daughterRotCM, genDaughter1, genDaughter2, genMother; ROOT::Math::XYZVector randomVec, beamVec, normalVec; bool isMix = false; template - void fillInvMass(const T1& daughter1, const T1& daughter2, const T1& mother, float multiplicity, bool isMix, const T2& track1, const T2& track2) + void fillInvMass(const T1& dau1, const T1& dau2, const T1& motherVec, float multiplicityFill, bool isMixInput, const T2& track1, const T2& track2) { - daughterSelected = (boostDaugter1) ? daughter1 : daughter2; // polarization calculations - ROOT::Math::Boost boost{mother.BoostToCM()}; // boost mother to center of mass frame - fourVecDauCM = boost(daughterSelected); // boost the frame of daughter same as mother + daughterSelected = (boostDaugter1) ? dau1 : dau2; // polarization calculations + + // Boost mother to the center-of-mass frame + ROOT::Math::Boost boost{motherVec.BoostToCM()}; + + // Boost selected daughter to the mother rest frame + fourVecDauCM = boost(daughterSelected); - // if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { if (activateTHnSparseCosThStarHelicity) { - auto cosThetaStarHelicity = mother.Vect().Dot(fourVecDauCM.Vect()) / (std::sqrt(fourVecDauCM.Vect().Mag2()) * std::sqrt(mother.Vect().Mag2())); + auto cosThetaStarHelicity = motherVec.Vect().Dot(fourVecDauCM.Vect()) / (std::sqrt(fourVecDauCM.Vect().Mag2()) * std::sqrt(motherVec.Vect().Mag2())); + + // Unlike-sign pairs if (track1.sign() * track2.sign() < 0) { - if (!isMix) { - if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassUnlikeSign"), multiplicity, mother.Pt(), mother.M(), cosThetaStarHelicity); + + // Same-event + if (!isMixInput) { + + if (std::abs(motherVec.Rapidity()) < configGp.rapidityMotherData) { + hInvMass.fill(HIST("h3KstarInvMassUnlikeSign"), multiplicityFill, motherVec.Pt(), motherVec.M(), cosThetaStarHelicity); } + // Rotational background for (int i = 0; i < cRotations; i++) { - theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / selectionConfig.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / selectionConfig.rotationalCut); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + theta2 = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / configGp.rotationalCut, o2::constants::math::PI + o2::constants::math::PI / configGp.rotationalCut); + + daughterRot = ROOT::Math::PxPyPzMVector(dau1.Px() * std::cos(theta2) - dau1.Py() * std::sin(theta2), dau1.Px() * std::sin(theta2) + dau1.Py() * std::cos(theta2), dau1.Pz(), dau1.M()); - motherRot = daughterRot + daughter2; + motherRot = daughterRot + dau2; ROOT::Math::Boost boost2{motherRot.BoostToCM()}; daughterRotCM = boost2(daughterRot); auto cosThetaStarHelicityRot = motherRot.Vect().Dot(daughterRotCM.Vect()) / (std::sqrt(daughterRotCM.Vect().Mag2()) * std::sqrt(motherRot.Vect().Mag2())); - if (calcRotational && std::abs(motherRot.Rapidity()) < selectionConfig.rapidityMotherData) - hInvMass.fill(HIST("h3KstarInvMassRotated"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarHelicityRot); - } - } else if (isMix && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassMixed"), multiplicity, mother.Pt(), mother.M(), cosThetaStarHelicity); - } - } else { - if (!isMix) { - if (calcLikeSign && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - if (track1.sign() > 0 && track2.sign() > 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignPP"), multiplicity, mother.Pt(), mother.M(), cosThetaStarHelicity); - } else if (track1.sign() < 0 && track2.sign() < 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignMM"), multiplicity, mother.Pt(), mother.M(), cosThetaStarHelicity); + if (calcRotational && std::abs(motherRot.Rapidity()) < configGp.rapidityMotherData) { + hInvMass.fill(HIST("h3KstarInvMassRotated"), multiplicityFill, motherRot.Pt(), motherRot.M(), cosThetaStarHelicityRot); } } - } - } - - } else if (activateTHnSparseCosThStarProduction) { - normalVec = ROOT::Math::XYZVector(mother.Py(), -mother.Px(), 0.f); - auto cosThetaStarProduction = normalVec.Dot(fourVecDauCM.Vect()) / (std::sqrt(fourVecDauCM.Vect().Mag2()) * std::sqrt(normalVec.Mag2())); - if (track1.sign() * track2.sign() < 0) { - if (!isMix) { - if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassUnlikeSign"), multiplicity, mother.Pt(), mother.M(), cosThetaStarProduction); - } - for (int i = 0; i < cRotations; i++) { - theta2 = rn->Uniform(0, o2::constants::math::PI); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + // Mixed-event + } else if (std::abs(motherVec.Rapidity()) < configGp.rapidityMotherData) { - motherRot = daughterRot + daughter2; - if (calcRotational && std::abs(motherRot.Rapidity()) < selectionConfig.rapidityMotherData) - hInvMass.fill(HIST("h3KstarInvMassRotated"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarProduction); - } - } else if (isMix && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassMixed"), multiplicity, mother.Pt(), mother.M(), cosThetaStarProduction); - } - } else { - if (!isMix) { - if (calcLikeSign && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - if (track1.sign() > 0 && track2.sign() > 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignPP"), multiplicity, mother.Pt(), mother.M(), cosThetaStarProduction); - } else if (track1.sign() < 0 && track2.sign() < 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignMM"), multiplicity, mother.Pt(), mother.M(), cosThetaStarProduction); - } - } + hInvMass.fill(HIST("h3KstarInvMassMixed"), multiplicityFill, motherVec.Pt(), motherVec.M(), cosThetaStarHelicity); } - } - } else if (activateTHnSparseCosThStarBeam) { - beamVec = ROOT::Math::XYZVector(0.f, 0.f, 1.f); - auto cosThetaStarBeam = beamVec.Dot(fourVecDauCM.Vect()) / std::sqrt(fourVecDauCM.Vect().Mag2()); - if (track1.sign() * track2.sign() < 0) { - if (!isMix) { - if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassUnlikeSign"), multiplicity, mother.Pt(), mother.M(), cosThetaStarBeam); - } - for (int i = 0; i < cRotations; i++) { - theta2 = rn->Uniform(0, o2::constants::math::PI); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); - - motherRot = daughterRot + daughter2; - if (calcRotational && std::abs(motherRot.Rapidity()) < selectionConfig.rapidityMotherData) - hInvMass.fill(HIST("h3KstarInvMassRotated"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarBeam); - } - } else if (isMix && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassMixed"), multiplicity, mother.Pt(), mother.M(), cosThetaStarBeam); - } + // Like-sign pairs } else { - if (calcLikeSign && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - if (track1.sign() > 0 && track2.sign() > 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignPP"), multiplicity, mother.Pt(), mother.M(), cosThetaStarBeam); - } else if (track1.sign() < 0 && track2.sign() < 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignMM"), multiplicity, mother.Pt(), mother.M(), cosThetaStarBeam); - } - } - } - } else if (activateTHnSparseCosThStarRandom) { - auto phiRandom = gRandom->Uniform(0.f, constants::math::TwoPI); - auto thetaRandom = gRandom->Uniform(0.f, constants::math::PI); - randomVec = ROOT::Math::XYZVector(std::sin(thetaRandom) * std::cos(phiRandom), std::sin(thetaRandom) * std::sin(phiRandom), std::cos(thetaRandom)); - auto cosThetaStarRandom = randomVec.Dot(fourVecDauCM.Vect()) / std::sqrt(fourVecDauCM.Vect().Mag2()); + if (!isMixInput) { - if (track1.sign() * track2.sign() < 0) { - if (!isMix) { - if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassUnlikeSign"), multiplicity, mother.Pt(), mother.M(), cosThetaStarRandom); - } - for (int i = 0; i < cRotations; i++) { - theta2 = rn->Uniform(0, o2::constants::math::PI); - daughterRot = ROOT::Math::PxPyPzMVector(daughter1.Px() * std::cos(theta2) - daughter1.Py() * std::sin(theta2), daughter1.Px() * std::sin(theta2) + daughter1.Py() * std::cos(theta2), daughter1.Pz(), daughter1.M()); + if (calcLikeSign && std::abs(motherVec.Rapidity()) < configGp.rapidityMotherData) { - motherRot = daughterRot + daughter2; - if (calcRotational && std::abs(motherRot.Rapidity()) < selectionConfig.rapidityMotherData) - hInvMass.fill(HIST("h3KstarInvMassRotated"), multiplicity, motherRot.Pt(), motherRot.M(), cosThetaStarRandom); - } - } else if (isMix && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { - hInvMass.fill(HIST("h3KstarInvMassMixed"), multiplicity, mother.Pt(), mother.M(), cosThetaStarRandom); - } - } else { - if (!isMix) { - if (calcLikeSign && std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { if (track1.sign() > 0 && track2.sign() > 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignPP"), multiplicity, mother.Pt(), mother.M(), cosThetaStarRandom); + + hInvMass.fill(HIST("h3KstarInvMasslikeSignPP"), multiplicityFill, motherVec.Pt(), motherVec.M(), cosThetaStarHelicity); + } else if (track1.sign() < 0 && track2.sign() < 0) { - hInvMass.fill(HIST("h3KstarInvMasslikeSignMM"), multiplicity, mother.Pt(), mother.M(), cosThetaStarRandom); + + hInvMass.fill(HIST("h3KstarInvMasslikeSignMM"), multiplicityFill, motherVec.Pt(), motherVec.M(), cosThetaStarHelicity); } } } } } - // } } // int counter = 0; @@ -1117,8 +1207,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } // Fill the event counter @@ -1129,6 +1217,12 @@ struct Kstarqa { for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { rEventSelection.fill(HIST("tracksCheckData"), 0.5); + + if (cQAevents) { + rEventSelection.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), multiplicity, track1.pt()); + rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); + } + if (!selectionTrack(track1)) { continue; } @@ -1141,13 +1235,13 @@ struct Kstarqa { continue; if (cQAplots) { - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, track1.pt()); hPID.fill(HIST("Before/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("Before/hTPCnsigPi_mult_pt"), track1.tpcNSigmaPi(), multiplicity, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi(), multiplicity, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, multiplicity, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, multiplicity, track1.pt()); hOthers.fill(HIST("hCRFC_before"), track1.tpcCrossedRowsOverFindableCls()); // hOthers.fill(HIST("dE_by_dx_TPC"), track1.p(), track1.tpcSignal()); @@ -1155,70 +1249,66 @@ struct Kstarqa { } if (cPIDAdditionalChecks) { - if (std::abs(track1.tpcNSigmaPi()) > selectionConfig.pidCutTemp) + if (std::abs(track1.tpcNSigmaPi()) > configGp.pidCutTemp) hPID.fill(HIST("AdditionalChecks/hTPCnsigKa_pt"), track1.tpcNSigmaKa(), track1.pt()); - if (std::abs(track2.tpcNSigmaKa()) > selectionConfig.pidCutTemp) + if (std::abs(track2.tpcNSigmaKa()) > configGp.pidCutTemp) hPID.fill(HIST("AdditionalChecks/hTPCnsigPi_pt"), track1.tpcNSigmaPi(), track1.pt()); - if (std::abs(track1.tpcNSigmaPr()) > selectionConfig.pidCutTemp) { + if (std::abs(track1.tpcNSigmaPr()) > configGp.pidCutTemp) { hPID.fill(HIST("AdditionalChecks/hTOFnsigKa_ptPrCut"), track1.tpcNSigmaKa(), track1.pt()); hPID.fill(HIST("AdditionalChecks/hTOFnsigPi_ptPrCut"), track1.tpcNSigmaPi(), track1.pt()); } } - if (cQAevents) { - rEventSelection.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), multiplicity, track1.pt()); - rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); - } - // since we are using combinations full index policy, so repeated pairs are allowed, so we can check one with Kaon and other with pion - if (!selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPID(track1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplyPID1 && !selectionPID(track1, 1)) // kaon continue; - if (!selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPID(track2, 0)) // Track 2 is checked with Pion + if (configGp.isapplyPID1 && !selectionPID(track2, 0)) // pion continue; - if (selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPIDPtDep(track1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplypTdepPID && !selectionPIDPtDep(track1, 1)) // kaon continue; - if (selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPIDPtDep(track2, 0)) // Track 2 is checked with Pion + if (configGp.isapplypTdepPID && !selectionPIDPtDep(track2, 0)) // pion continue; - if (!selectionConfig.isapplypTdepPID && selectionConfig.isapplypTdepPIDTOF && !selectionPIDptDepTOF(track1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplypTdepPIDTOF && !selectionPIDptDepTOF(track1, 1)) // kaon continue; - if (!selectionConfig.isapplypTdepPID && selectionConfig.isapplypTdepPIDTOF && !selectionPIDptDepTOF(track2, 0)) // Track 2 is checked with Pion + if (configGp.isapplypTdepPIDTOF && !selectionPIDptDepTOF(track2, 0)) // pion + continue; + + if (configGp.isapplyPID2 && !selectionPID2(track1, 1)) // kaon + continue; + if (configGp.isapplyPID2 && !selectionPID2(track2, 0)) // pion continue; rEventSelection.fill(HIST("tracksCheckData"), 2.5); - // if (cFakeTrack && isFakeTrack(track1, 1)) // Kaon - // continue; - // if (cFakeTrack && isFakeTrack(track2, 0)) // Pion - // continue; + if (configGp.cFakeTrack && isFakeTrack(track1)) // Kaon + continue; rEventSelection.fill(HIST("tracksCheckData"), 3.5); - // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; - - // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > selectionConfig.ctrackRapidity) - // continue; rEventSelection.fill(HIST("tracksCheckData"), 4.5); - if (selectionConfig.isApplyParticleMID) { + if (configGp.isApplyParticleMID) { if (selectionMID(track1, 0)) // Kaon misidentified as pion continue; - // if (selectionMID(track1, 2)) // Kaon misidentified as proton - // continue; if (selectionMID(track2, 1)) // Pion misidentified as kaon continue; - // if (selectionMID(track2, 2)) // Pion misidentified as proton - // continue; } - if (selectionConfig.isApplyParticleMIDPtDep) { + if (configGp.isApplyParticleMIDPtDep) { if (selectionMIDPtDep(track1, 0)) // Kaon misidentified as pion continue; if (selectionMIDPtDep(track2, 1)) // Pion misidentified as kaon continue; } + if (configGp.isApplyParticleMIDPtDep2) { + if (selectionMIDPtDep2(track1, 0)) // Kaon misidentified as pion + continue; + if (selectionMIDPtDep2(track2, 1)) // Pion misidentified as kaon + continue; + } + rEventSelection.fill(HIST("tracksCheckData"), 5.5); if (cQAplots) { @@ -1229,12 +1319,20 @@ struct Kstarqa { hPID.fill(HIST("After/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("After/hTPCnsigPi_mult_pt"), track2.tpcNSigmaPi(), multiplicity, track2.pt()); - hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi(), multiplicity, track2.pt()); + hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, multiplicity, track1.pt()); + hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, multiplicity, track2.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, track1.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_after"), track2.tpcNSigmaPi(), track2.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, track2.pt()); hOthers.fill(HIST("hEta_after"), track1.eta()); hOthers.fill(HIST("hCRFC_after"), track1.tpcCrossedRowsOverFindableCls()); - hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_after"), track2.tpcNSigmaPi(), track2.tofNSigmaPi(), track2.pt()); + if (track1.hasTOF()) { + hOthers.fill(HIST("hTOFBetaKa"), track1.beta()); + } + if (track2.hasTOF()) { + hOthers.fill(HIST("hTOFBetaPi"), track2.beta()); + } + hOthers.fill(HIST("hTPCMomDiffKa"), track1.p() - track1.tpcInnerParam()); + hOthers.fill(HIST("hTPCMomDiffPi"), track2.p() - track2.tpcInnerParam()); } if (!selectionPair(track1, track2)) { @@ -1246,10 +1344,10 @@ struct Kstarqa { daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); mother = daughter1 + daughter2; // Kstar meson - /* if (selectionConfig.isApplyCutsOnMother) { - if (mother.Pt() >= selectionConfig.cMaxPtMotherCut) // excluding candidates in overflow + /* if (configGp.isApplyCutsOnMother) { + if (mother.Pt() >= configGp.cMaxPtMotherCut) // excluding candidates in overflow continue; - if (mother.M() >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow + if (mother.M() >= configGp.cMaxMinvMotherCut) // excluding candidates in overflow continue; } */ @@ -1278,30 +1376,30 @@ struct Kstarqa { using BinningTypeMCFT0C = ColumnBinningPolicy; using BinningTypeMCFV0A = ColumnBinningPolicy; - BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeFT0M binningOnFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeFT0A binningOnFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeFT0C binningOnFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeFV0A binningOnFV0A{{axisVertex, axisMultiplicity}}; - BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisMultiplicity}, true}; - BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisMultiplicity}, true}; + BinningTypeMCFT0M binningOnMCFT0M{{axisVertex, axisMultiplicity}}; + BinningTypeMCFT0A binningOnMCFT0A{{axisVertex, axisMultiplicity}}; + BinningTypeMCFT0C binningOnMCFT0C{{axisVertex, axisMultiplicity}}; + BinningTypeMCFV0A binningOnMCFV0A{{axisVertex, axisMultiplicity}}; - SameKindPair pair1{binningOnFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; - SameKindPair pair2{binningOnFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; - SameKindPair pair3{binningOnFT0C, selectionConfig.cfgNoMixedEvents, -1, &cache}; - SameKindPair pair4{binningOnFV0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; + SameKindPair pair1{binningOnFT0M, configGp.cfgNoMixedEvents, -1, &cache}; + SameKindPair pair2{binningOnFT0A, configGp.cfgNoMixedEvents, -1, &cache}; + SameKindPair pair3{binningOnFT0C, configGp.cfgNoMixedEvents, -1, &cache}; + SameKindPair pair4{binningOnFV0A, configGp.cfgNoMixedEvents, -1, &cache}; - SameKindPair pairmc1{binningOnMCFT0M, selectionConfig.cfgNoMixedEvents, -1, &cache}; - SameKindPair pairmc2{binningOnMCFT0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; - SameKindPair pairmc3{binningOnMCFT0C, selectionConfig.cfgNoMixedEvents, -1, &cache}; - SameKindPair pairmc4{binningOnMCFV0A, selectionConfig.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairmc1{binningOnMCFT0M, configGp.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairmc2{binningOnMCFT0A, configGp.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairmc3{binningOnMCFT0C, configGp.cfgNoMixedEvents, -1, &cache}; + SameKindPair pairmc4{binningOnMCFV0A, configGp.cfgNoMixedEvents, -1, &cache}; void processME(EventCandidatesMix const&, TrackCandidates const&) { // Map estimator to pair and multiplicity accessor - auto runMixing = [&](auto& pair, auto multiplicityGetter) { + auto runMixing = [&](const auto& pair, auto multiplicityGetter) { for (const auto& [c1, tracks1, c2, tracks2] : pair) { // if (!c1.sel8() || !c2.sel8()) // continue; @@ -1316,35 +1414,37 @@ struct Kstarqa { o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { if (!selectionTrack(t1) || !selectionTrack(t2)) continue; - // if (!selectionPID(t1, 1) || !selectionPID(t2, 0)) - // continue; - if (!selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPID(t1, 1)) // Track 1 is checked with Kaon + + if (configGp.isapplyPID1 && !selectionPID(t1, 1)) // kaon continue; - if (!selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPID(t2, 0)) // Track 2 is checked with Pion + if (configGp.isapplyPID1 && !selectionPID(t2, 0)) // pion continue; - if (selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPIDPtDep(t1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplypTdepPID && !selectionPIDPtDep(t1, 1)) // kaon continue; - if (selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !selectionPIDPtDep(t2, 0)) // Track 2 is checked with Pion + if (configGp.isapplypTdepPID && !selectionPIDPtDep(t2, 0)) // pion continue; - if (!selectionConfig.isapplypTdepPID && selectionConfig.isapplypTdepPIDTOF && !selectionPIDptDepTOF(t1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplypTdepPIDTOF && !selectionPIDptDepTOF(t1, 1)) // kaon continue; - if (!selectionConfig.isapplypTdepPID && selectionConfig.isapplypTdepPIDTOF && !selectionPIDptDepTOF(t2, 0)) // Track 2 is checked with Pion + if (configGp.isapplypTdepPIDTOF && !selectionPIDptDepTOF(t2, 0)) // pion continue; - // if (std::abs(t1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) + if (configGp.isapplyPID2 && !selectionPID2(t1, 1)) // kaon + continue; + if (configGp.isapplyPID2 && !selectionPID2(t2, 0)) // pion + continue; + + // if (std::abs(t1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > configGp.ctrackRapidity) // continue; - // if (std::abs(t2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > selectionConfig.ctrackRapidity) + // if (std::abs(t2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > configGp.ctrackRapidity) // continue; - // if (cFakeTrack && isFakeTrack(t1, 1)) // Kaon - // continue; - // if (cFakeTrack && isFakeTrack(t2, 0)) // Pion - // continue; + if (configGp.cFakeTrack && isFakeTrack(t1)) // Kaon + continue; - if (selectionConfig.isApplyParticleMID) { + if (configGp.isApplyParticleMID) { if (selectionMID(t1, 0)) // Kaon misidentified as pion continue; // if (selectionMID(t1, 2)) // Kaon misidentified as proton @@ -1355,13 +1455,20 @@ struct Kstarqa { // continue; } - if (selectionConfig.isApplyParticleMIDPtDep) { + if (configGp.isApplyParticleMIDPtDep) { if (selectionMIDPtDep(t1, 0)) // Kaon misidentified as pion continue; if (selectionMIDPtDep(t2, 1)) // Pion misidentified as kaon continue; } + if (configGp.isApplyParticleMIDPtDep2) { + if (selectionMIDPtDep2(t1, 0)) // Kaon misidentified as pion + continue; + if (selectionMIDPtDep2(t2, 1)) // Pion misidentified as kaon + continue; + } + if (!selectionPair(t1, t2)) { continue; } @@ -1372,7 +1479,7 @@ struct Kstarqa { isMix = true; - if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { + if (std::abs(mother.Rapidity()) < configGp.rapidityMotherData) { fillInvMass(daughter1, daughter2, mother, multiplicity, isMix, t1, t2); } } @@ -1394,14 +1501,14 @@ struct Kstarqa { void processMEMC(EventCandidatesMC const&, TrackCandidatesMC const&, aod::McParticles const&, aod::McCollisions const&) { - auto runMixing = [&](auto& pair, auto multiplicityGetter) { + auto runMixing = [&](const auto& pair, auto multiplicityGetter) { for (const auto& [c1, tracks1, c2, tracks2] : pair) { if (!selectionEvent(c1, false) || !selectionEvent(c2, false)) { // don't fill event cut histogram continue; } - if (selectionConfig.isApplyMCchecksClosure && (!c1.has_mcCollision() || !c2.has_mcCollision())) { + if (!c1.has_mcCollision() || !c2.has_mcCollision()) { continue; // skip if no MC collision associated } @@ -1411,54 +1518,56 @@ struct Kstarqa { for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { if (!selectionTrack(t1) || !selectionTrack(t2)) continue; - // if (!selectionPID(t1, 1) || !selectionPID(t2, 0)) - // continue; - if (!selectionConfig.isapplypTdepPID && !selectionPID(t1, 1)) // Track 1 is checked with Kaon + + if (configGp.isapplyPID1 && !selectionPID(t1, 1)) // kaon continue; - if (!selectionConfig.isapplypTdepPID && !selectionPID(t2, 0)) // Track 2 is checked with Pion + if (configGp.isapplyPID1 && !selectionPID(t2, 0)) // pion continue; - if (selectionConfig.isapplypTdepPID && !selectionPIDPtDep(t1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplypTdepPID && !selectionPIDPtDep(t1, 1)) // kaon continue; - if (selectionConfig.isapplypTdepPID && !selectionPIDPtDep(t2, 0)) // Track 2 is checked with Pion + if (configGp.isapplypTdepPID && !selectionPIDPtDep(t2, 0)) // pion continue; - if (selectionConfig.isApplyParticleMID) { + if (configGp.isApplyParticleMID) { if (selectionMID(t1, 0)) // Kaon misidentified as pion continue; - // if (selectionMID(t1, 2)) // Kaon misidentified as proton - // continue; if (selectionMID(t2, 1)) // Pion misidentified as kaon continue; - // if (selectionMID(t2, 2)) // Pion misidentified as proton - // continue; } - if (selectionConfig.isApplyParticleMIDPtDep) { + if (configGp.isApplyParticleMIDPtDep) { if (selectionMIDPtDep(t1, 0)) // Kaon misidentified as pion continue; if (selectionMIDPtDep(t2, 1)) // Pion misidentified as kaon continue; } - if (selectionConfig.isApplyMCchecksClosure && (!t1.has_mcParticle() || !t2.has_mcParticle())) { + if (configGp.isApplyParticleMIDPtDep2) { + if (selectionMIDPtDep2(t1, 0)) // Kaon misidentified as pion + continue; + if (selectionMIDPtDep2(t2, 1)) // Pion misidentified as kaon + continue; + } + if (configGp.isapplyPID2 && !selectionPID2(t1, 1)) // kaon + continue; + if (configGp.isapplyPID2 && !selectionPID2(t2, 0)) // pion + continue; + + if (!t1.has_mcParticle() || !t2.has_mcParticle()) { continue; // skip if no MC particle associated } - // if (std::abs(t1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) + // if (std::abs(t1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > configGp.ctrackRapidity) // continue; - // if (std::abs(t2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > selectionConfig.ctrackRapidity) + // if (std::abs(t2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > configGp.ctrackRapidity) // continue; const auto mctrack1 = t1.mcParticle(); const auto mctrack2 = t2.mcParticle(); - if (selectionConfig.isApplyMCchecksClosure && !mctrack1.isPhysicalPrimary()) { - continue; - } - - if (selectionConfig.isApplyMCchecksClosure && !mctrack2.isPhysicalPrimary()) { + if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) { continue; } @@ -1468,7 +1577,7 @@ struct Kstarqa { isMix = true; - if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { + if (std::abs(mother.Rapidity()) < configGp.rapidityMotherData) { fillInvMass(daughter1, daughter2, mother, multiplicity, isMix, t1, t2); } } @@ -1489,7 +1598,9 @@ struct Kstarqa { void processSEMC(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&, aod::McCollisions const& /*mcCollisions*/) { + // if (configGp.isCheckMotherClosure) { auto oldindex = -999; + // } if (!collision.has_mcCollision()) { return; } @@ -1510,8 +1621,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } // Fill the event counter @@ -1522,94 +1631,95 @@ struct Kstarqa { for (const auto& [track1, track2] : combinations(CombinationsFullIndexPolicy(tracks, tracks))) { rEventSelection.fill(HIST("tracksCheckData"), 0.5); - if (!selectionTrack(track1)) { - continue; - } - if (!selectionTrack(track2)) { + + // if (cQAevents) { + // rEventSelection.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), multiplicity, track1.pt()); + // rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); + // } + + if (!selectionTrack(track1) || !selectionTrack(track2)) { continue; } const auto mctrack1 = track1.mcParticle(); const auto mctrack2 = track2.mcParticle(); - if (selectionConfig.isApplyMCchecksClosure && (!track1.has_mcParticle() || !track2.has_mcParticle())) { + if (!track1.has_mcParticle() || !track2.has_mcParticle()) { continue; // skip if no MC particle associated } - if (selectionConfig.isApplyMCchecksClosure && !mctrack1.isPhysicalPrimary()) { + if (!mctrack1.isPhysicalPrimary() || !mctrack2.isPhysicalPrimary()) { continue; } - if (selectionConfig.isApplyMCchecksClosure && !mctrack2.isPhysicalPrimary()) { - continue; - } rEventSelection.fill(HIST("tracksCheckData"), 1.5); + if (track1.globalIndex() == track2.globalIndex()) + continue; + + rEventSelection.fill(HIST("tracksCheckData"), 2.5); + if (cQAplots) { - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, track1.pt()); hPID.fill(HIST("Before/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("Before/hTPCnsigPi_mult_pt"), track1.tpcNSigmaPi(), multiplicity, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi(), multiplicity, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, multiplicity, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, multiplicity, track1.pt()); hOthers.fill(HIST("hCRFC_before"), track1.tpcCrossedRowsOverFindableCls()); hOthers.fill(HIST("hphi"), track1.phi()); } - if (cQAevents) { - rEventSelection.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), multiplicity, track1.pt()); - rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); - } - // since we are using combinations full index policy, so repeated pairs are allowed, so we can check one with Kaon and other with pion - if (!selectionConfig.isapplypTdepPID && !selectionPID(track1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplyPID1 && !selectionPID(track1, 1)) // kaon continue; - if (!selectionConfig.isapplypTdepPID && !selectionPID(track2, 0)) // Track 2 is checked with Pion + if (configGp.isapplyPID1 && !selectionPID(track2, 0)) // pion continue; - rEventSelection.fill(HIST("tracksCheckData"), 2.5); + rEventSelection.fill(HIST("tracksCheckData"), 3.5); - if (selectionConfig.isapplypTdepPID && !selectionPIDPtDep(track1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplypTdepPID && !selectionPIDPtDep(track1, 1)) // kaon continue; - if (selectionConfig.isapplypTdepPID && !selectionPIDPtDep(track2, 0)) // Track 2 is checked with Pion + if (configGp.isapplypTdepPID && !selectionPIDPtDep(track2, 0)) // pion continue; - rEventSelection.fill(HIST("tracksCheckData"), 3.5); + rEventSelection.fill(HIST("tracksCheckData"), 4.5); - if (selectionConfig.isApplyParticleMID) { + if (configGp.isapplyPID2 && !selectionPID2(track1, 1)) // kaon + continue; + if (configGp.isapplyPID2 && !selectionPID2(track2, 0)) // pion + continue; + + if (configGp.isApplyParticleMID) { if (selectionMID(track1, 0)) // Kaon misidentified as pion continue; - // if (selectionMID(track1, 2)) // Kaon misidentified as proton - // continue; if (selectionMID(track2, 1)) // Pion misidentified as kaon continue; - // if (selectionMID(track2, 2)) // Pion misidentified as proton - // continue; } - if (selectionConfig.isApplyParticleMIDPtDep) { + if (configGp.isApplyParticleMIDPtDep) { if (selectionMIDPtDep(track1, 0)) // Kaon misidentified as pion continue; if (selectionMIDPtDep(track2, 1)) // Pion misidentified as kaon continue; } - rEventSelection.fill(HIST("tracksCheckData"), 4.5); - // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; + if (configGp.isApplyParticleMIDPtDep2) { + if (selectionMIDPtDep2(track1, 0)) // Kaon misidentified as pion + continue; + if (selectionMIDPtDep2(track2, 1)) // Pion misidentified as kaon + continue; + } - // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > selectionConfig.ctrackRapidity) + rEventSelection.fill(HIST("tracksCheckData"), 5.5); + // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > configGp.ctrackRapidity) // continue; - // if (cFakeTrack && isFakeTrack(track1, 1)) // Kaon - // continue; - // if (cFakeTrack && isFakeTrack(track2, 0)) // Pion - // continue; - rEventSelection.fill(HIST("tracksCheckData"), 5.5); + // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > configGp.ctrackRapidity) + // continue; - if (track1.globalIndex() == track2.globalIndex()) + if (configGp.cFakeTrack && isFakeTrack(track1)) // Kaon continue; - rEventSelection.fill(HIST("tracksCheckData"), 6.5); if (cQAplots) { @@ -1620,19 +1730,25 @@ struct Kstarqa { hPID.fill(HIST("After/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("After/hTPCnsigPi_mult_pt"), track2.tpcNSigmaPi(), multiplicity, track2.pt()); - hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi(), multiplicity, track2.pt()); + hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, multiplicity, track1.pt()); + hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, multiplicity, track2.pt()); hOthers.fill(HIST("hEta_after"), track1.eta()); hOthers.fill(HIST("hCRFC_after"), track1.tpcCrossedRowsOverFindableCls()); // hPID.fill(HIST("After/hNsigmaKaonTPC_after"), track1.pt(), track1.tpcNSigmaKa()); // hPID.fill(HIST("After/hNsigmaKaonTOF_after"), track1.pt(), track1.tofNSigmaKa()); // hPID.fill(HIST("After/hNsigmaPionTPC_after"), track2.pt(), track2.tpcNSigmaPi()); // hPID.fill(HIST("After/hNsigmaPionTOF_after"), track2.pt(), track2.tofNSigmaPi()); - hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_after"), track2.tpcNSigmaPi(), track2.tofNSigmaPi(), track2.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, track1.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_after"), track2.tpcNSigmaPi(), track2.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, track2.pt()); + if (track1.hasTOF()) { + hOthers.fill(HIST("hTOFBetaKa"), track1.beta()); + } + if (track2.hasTOF()) { + hOthers.fill(HIST("hTOFBetaPi"), track2.beta()); + } } - if (selectionConfig.isApplyMCchecksClosure) { + if (configGp.isCheckMotherClosure) { for (const auto& mothertrack1 : mctrack1.mothers_as()) { for (const auto& mothertrack2 : mctrack2.mothers_as()) { @@ -1640,11 +1756,11 @@ struct Kstarqa { continue; } - if (selectionConfig.isApplyMCchecksClosure && !mothertrack1.producedByGenerator()) { + if (!mothertrack1.producedByGenerator()) { continue; } - if (selectionConfig.isApplyMCchecksClosure && avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { + if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { continue; } rEventSelection.fill(HIST("recMCparticles"), 11.5); @@ -1663,6 +1779,7 @@ struct Kstarqa { } } } else { + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); mother = daughter1 + daughter2; // Kstar meson @@ -1677,24 +1794,20 @@ struct Kstarqa { } PROCESS_SWITCH(Kstarqa, processSEMC, "Process same event in MC", false); - Service pdgDB; - void processGen(EventMCGenerated::iterator const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions) // void processGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions) { rEventSelection.fill(HIST("eventsCheckGen"), 0.5); - int nChInel = 0; - for (const auto& mcParticle : mcParticles) { - auto pdgcode = std::abs(mcParticle.pdgCode()); - if (mcParticle.isPhysicalPrimary() && (pdgcode == PDG_t::kPiPlus || pdgcode == PDG_t::kKPlus || pdgcode == PDG_t::kProton || pdgcode == std::abs(PDG_t::kElectron) || pdgcode == std::abs(PDG_t::kMuonMinus))) { - if (std::abs(mcParticle.eta()) < 1.0) { - nChInel = nChInel + 1; - } - } + if (configGp.isApplyMCGenVz && std::abs(mcCollision.posZ()) > configGp.cutzvertex) { + return; } - if (nChInel > 0 && std::abs(mcCollision.posZ()) < selectionConfig.cutzvertex) - rEventSelection.fill(HIST("eventsCheckGen"), 1.5); + rEventSelection.fill(HIST("eventsCheckGen"), 1.5); + + if (configGp.isINELgt0Gen && !mcCollision.isInelGt0()) { + return; + } + rEventSelection.fill(HIST("eventsCheckGen"), 2.5); std::vector selectedEvents(collisions.size()); int nevts = 0; @@ -1706,17 +1819,12 @@ struct Kstarqa { if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { isINELgt0true = true; } - if (selectionConfig.isINELgt0 && !isINELgt0true) { + if (configGp.isINELgt0 && !isINELgt0true) { return; } - - // if (selectionConfig.isINELgt0 && !mcCollision.isInelGt0()) { - // return; - // } - rEventSelection.fill(HIST("eventsCheckGen"), 2.5); + rEventSelection.fill(HIST("eventsCheckGen"), 3.5); for (const auto& collision : collisions) { - rEventSelection.fill(HIST("eventsCheckGen"), 3.5); if (!selectionEvent(collision, false)) { // don't fill event cut histogram continue; @@ -1731,8 +1839,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } hInvMass.fill(HIST("h1GenMult"), multiplicity); @@ -1744,50 +1850,46 @@ struct Kstarqa { selectedEvents.resize(nevts); for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) < selectionConfig.rapidityMotherData && std::abs(mcParticle.pdgCode()) == o2::constants::physics::kK0Star892) { + if (std::abs(mcParticle.y()) < configGp.rapidityMotherData && std::abs(mcParticle.pdgCode()) == o2::constants::physics::kK0Star892) { hInvMass.fill(HIST("hAllKstarGenCollisisons"), multiplicity, mcParticle.pt()); } } - if (std::abs(mcCollision.posZ()) > selectionConfig.cutzvertex) { - return; - } - const auto evtReconstructedAndSelected = std::find(selectedEvents.begin(), selectedEvents.end(), mcCollision.globalIndex()) != selectedEvents.end(); hInvMass.fill(HIST("hAllGenCollisions"), multiplicity); - // if (!evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection - // return; - // } - double genMultiplicity = mcCollision.centFT0M(); - hInvMass.fill(HIST("sigEvLossFromGenRec/MultiplicityGen"), genMultiplicity); - if (evtReconstructedAndSelected) { - hInvMass.fill(HIST("sigEvLossFromGenRec/MultiplicityRec"), genMultiplicity); - hInvMass.fill(HIST("h1GenMult2"), genMultiplicity); - hInvMass.fill(HIST("hAllGenCollisions1Rec"), multiplicity); - rEventSelection.fill(HIST("eventsCheckGen"), 4.5); + if (!evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection + return; } + double genMultiplicity = mcCollision.centFT0M(); + // hInvMass.fill(HIST("sigEvLossFromGenRec/MultiplicityGen"), genMultiplicity); + // if (evtReconstructedAndSelected) { + // hInvMass.fill(HIST("sigEvLossFromGenRec/MultiplicityRec"), genMultiplicity); + hInvMass.fill(HIST("h1GenMult2"), genMultiplicity); + hInvMass.fill(HIST("hAllGenCollisions1Rec"), multiplicity); + rEventSelection.fill(HIST("eventsCheckGen"), 4.5); + // } for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) >= selectionConfig.rapidityMotherData) { + if (std::abs(mcParticle.y()) >= configGp.rapidityMotherData) { continue; } - /* if (selectionConfig.isApplyCutsOnMother) { - if (mcParticle.pt() >= selectionConfig.cMaxPtMotherCut) // excluding candidates in overflow + /* if (configGp.isApplyCutsOnMother) { + if (mcParticle.pt() >= configGp.cMaxPtMotherCut) // excluding candidates in overflow continue; - if ((std::sqrt(mcParticle.e() * mcParticle.e() - mcParticle.p() * mcParticle.p())) >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow + if ((std::sqrt(mcParticle.e() * mcParticle.e() - mcParticle.p() * mcParticle.p())) >= configGp.cMaxMinvMotherCut) // excluding candidates in overflow continue; } */ if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } - if (evtReconstructedAndSelected) - hInvMass.fill(HIST("hAllKstarGenCollisisons1Rec"), multiplicity, mcParticle.pt()); + // if (evtReconstructedAndSelected) + hInvMass.fill(HIST("hAllKstarGenCollisisons1Rec"), multiplicity, mcParticle.pt()); auto kDaughters = mcParticle.daughters_as(); - if (kDaughters.size() != selectionConfig.noOfDaughters) { + if (kDaughters.size() != configGp.noOfDaughters) { continue; } @@ -1809,15 +1911,15 @@ struct Kstarqa { } if (passkaon && passpion) { mother = daughter1 + daughter2; // Kstar meson - hInvMass.fill(HIST("sigEvLossFromGenRec/hSignalLossDenominator"), mother.Pt(), genMultiplicity); - if (evtReconstructedAndSelected) { - hInvMass.fill(HIST("hk892GenpT"), mcParticle.pt(), multiplicity); - hInvMass.fill(HIST("hk892GenpT2"), mother.Pt(), multiplicity); - hInvMass.fill(HIST("hk892GenpTCalib1"), mcParticle.pt(), genMultiplicity); - hInvMass.fill(HIST("hk892GenpTCalib2"), mother.Pt(), genMultiplicity); - hInvMass.fill(HIST("h1genmass"), mother.M()); - hInvMass.fill(HIST("sigEvLossFromGenRec/hSignalLossNumerator"), mother.Pt(), genMultiplicity); - } + // hInvMass.fill(HIST("sigEvLossFromGenRec/hSignalLossDenominator"), mother.Pt(), genMultiplicity); + // if (evtReconstructedAndSelected) { + hInvMass.fill(HIST("hk892GenpT"), mcParticle.pt(), multiplicity); + hInvMass.fill(HIST("hk892GenpT2"), mother.Pt(), multiplicity); + hInvMass.fill(HIST("hk892GenpTCalib1"), mcParticle.pt(), genMultiplicity); + hInvMass.fill(HIST("hk892GenpTCalib2"), mother.Pt(), genMultiplicity); + hInvMass.fill(HIST("h1genmass"), mother.M()); + // hInvMass.fill(HIST("sigEvLossFromGenRec/hSignalLossNumerator"), mother.Pt(), genMultiplicity); + // } } } } @@ -1828,11 +1930,22 @@ struct Kstarqa { { // auto multiplicityRec = -1; bool isSelectedEvent = false; + bool isRecCollision = false; + bool isSelectedEventINEL = false; auto multiplicity1 = -999.; + if (configGp.isINELgt0Gen && !mcCollision.isInelGt0()) { + return; + } + if (configGp.isApplyMCGenVz && std::abs(mcCollision.posZ()) > configGp.cutzvertex) { + return; + } + for (const auto& RecCollision : recCollisions) { if (!RecCollision.has_mcCollision()) continue; + isRecCollision = true; + if (!selectionEvent(RecCollision, false)) // don't fill event cut histogram continue; @@ -1847,32 +1960,84 @@ struct Kstarqa { multiplicity1 = RecCollision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity1 = RecCollision.centFV0A(); - } else { - multiplicity1 = RecCollision.centFT0M(); // default } isSelectedEvent = true; } + for (const auto& RecCollision : recCollisions) { + if (!RecCollision.has_mcCollision()) + continue; + + // if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) + // continue; + + if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) + continue; + + if (configGp.isNoTimeFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + continue; + + // if (configGp.isSel8 && !RecCollision.sel8()) + // continue; + + if (rctCut.requireRCTFlagChecker && !rctChecker(RecCollision)) + continue; + + isSelectedEventINEL = true; + } + + for (const auto& RecCollision : recCollisions) { + rEventSelection.fill(HIST("hEventCutMC"), 0.5); + + if (!RecCollision.has_mcCollision()) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 1.5); + + if (configGp.isNoTimeFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 2.5); + + if (configGp.isNoITSROFrameBorder && !RecCollision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 3.5); + + if (std::abs(RecCollision.posZ()) > configGp.cutzvertex) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 4.5); + + if (configGp.isTriggerTVX && !RecCollision.selection_bit(aod::evsel::kIsTriggerTVX)) + continue; + + rEventSelection.fill(HIST("hEventCutMC"), 5.5); + } + bool isINELgt0true = false; if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { isINELgt0true = true; } - if (selectionConfig.isINELgt0 && !isINELgt0true) { - return; - } - - if (selectionConfig.checkVzEvSigLoss && (std::abs(mcCollision.posZ()) > selectionConfig.cutzvertex)) { + if (configGp.isINELgt0 && !isINELgt0true) { return; } auto impactPar = mcCollision.impactParameter(); - auto multiplicityGen = -1; + auto multiplicityGen = -1.0f; multiplicityGen = mcCollision.centFT0M(); hInvMass.fill(HIST("MCcorrections/hImpactParameterGen"), impactPar); hInvMass.fill(HIST("MCcorrections/MultiplicityGen"), multiplicityGen); + if (isSelectedEventINEL) { + hInvMass.fill(HIST("MCcorrections/MultiplicityRecINEL"), multiplicityGen); + } + // Event loss if (isSelectedEvent) { hInvMass.fill(HIST("MCcorrections/hImpactParameterRec"), impactPar); @@ -1882,13 +2047,20 @@ struct Kstarqa { // Generated MC for (const auto& mcPart : mcParticles) { - if (std::abs(mcPart.y()) >= selectionConfig.rapidityMotherData || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) + if (std::abs(mcPart.y()) >= configGp.rapidityMotherData || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) continue; hInvMass.fill(HIST("MCcorrections/hSignalLossDenominator"), mcPart.pt(), multiplicityGen); + if (isRecCollision) { + hInvMass.fill(HIST("MCcorrections/hSignalLossDenominatorRec"), mcPart.pt(), multiplicityGen); + } + if (isSelectedEvent) { hInvMass.fill(HIST("MCcorrections/hSignalLossNumerator"), mcPart.pt(), multiplicityGen); } + if (isSelectedEventINEL) { + hInvMass.fill(HIST("MCcorrections/hSignalLossNumeratorINEL"), mcPart.pt(), multiplicityGen); + } } // end loop on gen particles } PROCESS_SWITCH(Kstarqa, processEvtLossSigLossMC, "Process Signal Loss, Event Loss", false); @@ -1899,17 +2071,21 @@ struct Kstarqa { multiplicityNch = mcCollision.multMCNParticlesEta08(); hInvMass.fill(HIST("CorrFactors/hGenEvents"), multiplicityNch, 0.5); - if (selectionConfig.checkVzEvSigLoss && std::abs(mcCollision.posZ()) > selectionConfig.cutzvertex) + if (configGp.isINELgt0Gen && !mcCollision.isInelGt0()) { + return; + } + + if (configGp.isApplyMCGenVz && std::abs(mcCollision.posZ()) > configGp.cutzvertex) return; hInvMass.fill(HIST("CorrFactors/hGenEvents"), multiplicityNch, 1.5); - if (selectionConfig.isINELgt0 && !mcCollision.isInelGt0()) { + if (configGp.isINELgt0 && !mcCollision.isInelGt0()) { return; } hInvMass.fill(HIST("CorrFactors/hGenEvents"), multiplicityNch, 2.5); - float multiplicity = -1.0; + float multiplicitySigLoss = -1.0; bool isSelectedEvent = false; for (auto const& collision : collisions) { @@ -1919,15 +2095,13 @@ struct Kstarqa { continue; if (cSelectMultEstimator == kFT0M) { - multiplicity = collision.centFT0M(); + multiplicitySigLoss = collision.centFT0M(); } else if (cSelectMultEstimator == kFT0A) { - multiplicity = collision.centFT0A(); + multiplicitySigLoss = collision.centFT0A(); } else if (cSelectMultEstimator == kFT0C) { - multiplicity = collision.centFT0C(); + multiplicitySigLoss = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { - multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default + multiplicitySigLoss = collision.centFV0A(); } isSelectedEvent = true; } @@ -1935,7 +2109,7 @@ struct Kstarqa { // auto multiplicityGen = -1; // multiplicityGen = mcCollision.centFT0M(); - hInvMass.fill(HIST("CorrFactors/hMultiplicityVsMultMC"), multiplicity, multiplicityNch); + hInvMass.fill(HIST("CorrFactors/hMultiplicityVsMultMC"), multiplicitySigLoss, multiplicityNch); hInvMass.fill(HIST("CorrFactors/hNrecInGen"), collisions.size()); hInvMass.fill(HIST("CorrFactors/MultiplicityGen"), multiplicityNch); if (isSelectedEvent) { @@ -1944,13 +2118,13 @@ struct Kstarqa { for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) >= selectionConfig.rapidityMotherData) + if (std::abs(mcParticle.y()) >= configGp.rapidityMotherData) continue; if (std::abs(mcParticle.pdgCode()) == o2::constants::physics::kK0Star892) { auto kDaughters = mcParticle.daughters_as(); - if (kDaughters.size() != selectionConfig.noOfDaughters) { + if (kDaughters.size() != configGp.noOfDaughters) { continue; } @@ -1964,7 +2138,12 @@ struct Kstarqa { // continue; int pdgDau = kCurrentDaughter.pdgCode(); - int sign = (pdgDau > 0) - (pdgDau < 0); + int sign = 0; + if (pdgDau > 0) { + sign = 1; + } else if (pdgDau < 0) { + sign = -1; + } if (sign > 0) hasPos = true; @@ -1984,8 +2163,8 @@ struct Kstarqa { if ((passkaon && passpion) && (hasPos && hasNeg)) { mother = daughter1 + daughter2; // Kstar meson - hInvMass.fill(HIST("CorrFactors/h2dGenKstar"), multiplicity, mother.Pt()); - hInvMass.fill(HIST("CorrFactors/h3dGenKstarVsMultMCVsMultiplicity"), multiplicityNch, multiplicity, mother.Pt()); + hInvMass.fill(HIST("CorrFactors/h2dGenKstar"), multiplicitySigLoss, mother.Pt()); + hInvMass.fill(HIST("CorrFactors/h3dGenKstarVsMultMCVsMultiplicity"), multiplicityNch, multiplicitySigLoss, mother.Pt()); hInvMass.fill(HIST("CorrFactors/hSignalLoss1"), mother.pt(), multiplicityNch); if (isSelectedEvent) { hInvMass.fill(HIST("CorrFactors/hSignalLoss2"), mother.pt(), multiplicityNch); @@ -2001,21 +2180,26 @@ struct Kstarqa { } PROCESS_SWITCH(Kstarqa, processEvSigLossFactors, "Process Event and Signal loss", false); + double genMass = 0.0, recMass = 0.0, recPt = 0.0, genPt = 0.0; + void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&, EventMCGenerated const&) { + rEventSelection.fill(HIST("eventsCheckRec"), 0.5); if (!collision.has_mcCollision()) { return; } + rEventSelection.fill(HIST("eventsCheckRec"), 1.5); double multiplicityRec = -1.0; // multiplicityRec = collision.mcCollision_as().centFT0M(); const auto& mcCollisionRec = collision.mcCollision_as(); multiplicityRec = mcCollisionRec.centFT0M(); - if (selectionConfig.isINELgt0 && !collision.isInelGt0()) { + if (configGp.isINELgt0 && !collision.isInelGt0()) { return; } + rEventSelection.fill(HIST("eventsCheckRec"), 2.5); // multiplicity = collision.centFT0M(); if (cSelectMultEstimator == kFT0M) { @@ -2026,8 +2210,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } hInvMass.fill(HIST("hAllRecCollisions"), multiplicity); @@ -2036,8 +2218,8 @@ struct Kstarqa { if (!selectionEvent(collision, true)) { // fill MC event cut histogram return; } + rEventSelection.fill(HIST("eventsCheckRec"), 3.5); - rEventSelection.fill(HIST("eventsCheckGen"), 5.5); hInvMass.fill(HIST("h1RecMult"), multiplicity); hInvMass.fill(HIST("h1RecMult2"), multiplicityRec); @@ -2047,9 +2229,6 @@ struct Kstarqa { auto oldindex = -999; for (const auto& track1 : tracks) { - if (!selectionTrack(track1)) { - continue; - } if (!track1.has_mcParticle()) { continue; @@ -2060,6 +2239,10 @@ struct Kstarqa { rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); } + if (!selectionTrack(track1)) { + continue; + } + auto track1ID = track1.index(); for (const auto& track2 : tracks) { rEventSelection.fill(HIST("recMCparticles"), 0.5); @@ -2086,17 +2269,6 @@ struct Kstarqa { const auto mctrack1 = track1.mcParticle(); const auto mctrack2 = track2.mcParticle(); - int track1PDG = std::abs(mctrack1.pdgCode()); - int track2PDG = std::abs(mctrack2.pdgCode()); - - if (cQAplots && (std::abs(mctrack1.pdgCode()) == PDG_t::kKPlus && std::abs(mctrack2.pdgCode()) == PDG_t::kPiPlus)) { - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); - hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi(), track1.pt()); - hPID.fill(HIST("Before/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); - hPID.fill(HIST("Before/hTPCnsigPi_mult_pt"), track1.tpcNSigmaPi(), multiplicity, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi(), multiplicity, track1.pt()); - } if (!mctrack1.isPhysicalPrimary()) { continue; @@ -2106,16 +2278,29 @@ struct Kstarqa { continue; } rEventSelection.fill(HIST("recMCparticles"), 5.5); + + int track1PDG = std::abs(mctrack1.pdgCode()); + int track2PDG = std::abs(mctrack2.pdgCode()); + + if (cQAplots && (std::abs(mctrack1.pdgCode()) == PDG_t::kKPlus && std::abs(mctrack2.pdgCode()) == PDG_t::kPiPlus)) { + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, track1.pt()); + hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_before"), track1.tpcNSigmaPi(), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, track1.pt()); + hPID.fill(HIST("Before/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); + hPID.fill(HIST("Before/hTPCnsigPi_mult_pt"), track1.tpcNSigmaPi(), multiplicity, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, multiplicity, track1.pt()); + hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, multiplicity, track1.pt()); + } rEventSelection.fill(HIST("recMCparticles"), 6.5); - if (selectionConfig.isPDGCheckMC && !(track1PDG == PDG_t::kPiPlus && track2PDG == PDG_t::kKPlus) && !(track1PDG == PDG_t::kKPlus && track2PDG == PDG_t::kPiPlus)) { + if ((track1PDG != PDG_t::kPiPlus || track2PDG != PDG_t::kKPlus) && + (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kPiPlus)) { continue; } rEventSelection.fill(HIST("recMCparticles"), 7.5); for (const auto& mothertrack1 : mctrack1.mothers_as()) { for (const auto& mothertrack2 : mctrack2.mothers_as()) { - if (selectionConfig.isPDGCheckMC && (mothertrack1.pdgCode() != mothertrack2.pdgCode())) { + if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { continue; } @@ -2129,109 +2314,128 @@ struct Kstarqa { } rEventSelection.fill(HIST("recMCparticles"), 9.5); - if (std::abs(mothertrack1.y()) >= selectionConfig.rapidityMotherData) { + if (std::abs(mothertrack1.y()) >= configGp.rapidityMotherData) { continue; } rEventSelection.fill(HIST("recMCparticles"), 10.5); - if (selectionConfig.isPDGCheckMC && (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892)) { + if (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } rEventSelection.fill(HIST("recMCparticles"), 11.5); - if (selectionConfig.isPDGCheckMC && (track1PDG == PDG_t::kPiPlus)) { - if (!selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !(selectionPID(track1, 0) && selectionPID(track2, 1))) { // pion and kaon + if (track1PDG == PDG_t::kPiPlus) { + if (configGp.isapplyPID1 && !(selectionPID(track1, 0) && selectionPID(track2, 1))) { // pion and kaon continue; - } else if (selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !(selectionPIDPtDep(track1, 0) && selectionPIDPtDep(track2, 1))) { // pion and kaon + } + if (configGp.isapplypTdepPID && !(selectionPIDPtDep(track1, 0) && selectionPIDPtDep(track2, 1))) { // pion and kaon continue; - } else if (!selectionConfig.isapplypTdepPID && selectionConfig.isapplypTdepPIDTOF && !(selectionPIDptDepTOF(track1, 0) && selectionPIDptDepTOF(track2, 1))) { // pion and kaon + } + if (configGp.isapplypTdepPIDTOF && !(selectionPIDptDepTOF(track1, 0) && selectionPIDptDepTOF(track2, 1))) { // pion and kaon continue; } + if (configGp.isapplyPID2 && !selectionPID2(track2, 0)) // pion and kaon + continue; rEventSelection.fill(HIST("recMCparticles"), 12.5); - if (selectionConfig.isApplyParticleMID) { + if (configGp.isApplyParticleMID) { if (selectionMID(track2, 0)) // Kaon misidentified as pion continue; - // if (selectionMID(track2, 2)) // Kaon misidentified as proton - // continue; if (selectionMID(track1, 1)) // Pion misidentified as kaon continue; - // if (selectionMID(track1, 2)) // Pion misidentified as proton - // continue; } - if (selectionConfig.isApplyParticleMIDPtDep) { + if (configGp.isApplyParticleMIDPtDep) { if (selectionMIDPtDep(track1, 1)) // Kaon misidentified as pion continue; if (selectionMIDPtDep(track2, 0)) // Pion misidentified as kaon continue; } + + if (configGp.isApplyParticleMIDPtDep2) { + if (selectionMIDPtDep2(track1, 1)) // Kaon misidentified as pion + continue; + if (selectionMIDPtDep2(track2, 0)) // Pion misidentified as kaon + continue; + } rEventSelection.fill(HIST("recMCparticles"), 13.5); - // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > selectionConfig.ctrackRapidity) + // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > configGp.ctrackRapidity) // continue; - // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) + // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > configGp.ctrackRapidity) // continue; rEventSelection.fill(HIST("recMCparticles"), 14.5); if (cQAplots) { hPID.fill(HIST("After/hTPCnsigPi_mult_pt"), track1.tpcNSigmaPi(), multiplicity, track1.pt()); hPID.fill(HIST("After/hTPCnsigKa_mult_pt"), track2.tpcNSigmaKa(), multiplicity, track2.pt()); - hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi(), multiplicity, track1.pt()); - hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track2.tofNSigmaKa(), multiplicity, track2.pt()); + hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, multiplicity, track1.pt()); + hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track2.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, multiplicity, track2.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_after"), track2.tpcNSigmaKa(), track2.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, track2.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_after"), track1.tpcNSigmaPi(), track1.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, track1.pt()); + if (track1.hasTOF()) { + hOthers.fill(HIST("hTOFBetaKa"), track1.beta()); + } + if (track2.hasTOF()) { + hOthers.fill(HIST("hTOFBetaPi"), track2.beta()); + } } - } else if (selectionConfig.isPDGCheckMC && (track1PDG == PDG_t::kKPlus)) { - if (!selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !(selectionPID(track1, 1) && selectionPID(track2, 0))) { // pion and kaon + } else if (track1PDG == PDG_t::kKPlus) { + if (configGp.isapplyPID1 && !(selectionPID(track1, 1) && selectionPID(track2, 0))) { // pion and kaon continue; - } else if (selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !(selectionPIDPtDep(track1, 1) && selectionPIDPtDep(track2, 0))) { // pion and kaon + } + if (configGp.isapplypTdepPID && !(selectionPIDPtDep(track1, 1) && selectionPIDPtDep(track2, 0))) { // pion and kaon continue; - } else if (!selectionConfig.isapplypTdepPID && selectionConfig.isapplypTdepPIDTOF && !(selectionPIDptDepTOF(track1, 1) && selectionPIDptDepTOF(track2, 0))) { // pion and kaon + } + if (configGp.isapplypTdepPIDTOF && !(selectionPIDptDepTOF(track1, 1) && selectionPIDptDepTOF(track2, 0))) { // pion and kaon continue; } + if (configGp.isapplyPID2 && !selectionPID2(track1, 1)) // kaon + continue; + if (configGp.isapplyPID2 && !selectionPID2(track2, 0)) // pion + continue; rEventSelection.fill(HIST("recMCparticles"), 12.5); - if (selectionConfig.isApplyParticleMID) { + if (configGp.isApplyParticleMID) { if (selectionMID(track2, 1)) // Pion misidentified as kaon continue; - // if (selectionMID(track2, 2)) // Kaon misidentified as proton - // continue; if (selectionMID(track1, 0)) // Kaon misidentified as pion continue; - // if (selectionMID(track1, 2)) // Pion misidentified as proton - // continue; } - if (selectionConfig.isApplyParticleMIDPtDep) { + if (configGp.isApplyParticleMIDPtDep) { if (selectionMIDPtDep(track1, 0)) // Kaon misidentified as pion continue; if (selectionMIDPtDep(track2, 1)) // Pion misidentified as kaon continue; } - // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) + if (configGp.isApplyParticleMIDPtDep2) { + if (selectionMIDPtDep2(track1, 0)) // Kaon misidentified as pion + continue; + if (selectionMIDPtDep2(track2, 1)) // Pion misidentified as kaon + continue; + } + + // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > configGp.ctrackRapidity) // continue; - // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > selectionConfig.ctrackRapidity) + // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > configGp.ctrackRapidity) // continue; rEventSelection.fill(HIST("recMCparticles"), 14.5); if (cQAplots) { hPID.fill(HIST("After/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("After/hTPCnsigPi_mult_pt"), track2.tpcNSigmaPi(), multiplicity, track2.pt()); - hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi(), multiplicity, track2.pt()); + hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, multiplicity, track1.pt()); + hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, multiplicity, track2.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa() - configGp.shiftInNsigmaTOFKa, track1.pt()); + hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_after"), track2.tpcNSigmaPi(), track2.tofNSigmaPi() - configGp.shiftInNsigmaTOFPi, track2.pt()); } } - /* if (selectionConfig.isApplyCutsOnMother) { - if (mothertrack1.pt() >= selectionConfig.cMaxPtMotherCut) // excluding candidates in overflow - continue; - if ((std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())) >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow - continue; - } */ - if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { hInvMass.fill(HIST("h1KSRecsplit"), mothertrack1.pt()); continue; @@ -2245,24 +2449,47 @@ struct Kstarqa { oldindex = mothertrack1.globalIndex(); if (track1PDG == PDG_t::kPiPlus) { + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massPi); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); + + genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massPi); + genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massKa); + } else if (track1PDG == PDG_t::kKPlus) { + daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massPi); + + genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massKa); + genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massPi); } - mother = daughter1 + daughter2; // Kstar meson + mother = daughter1 + daughter2; // Kstar meson + genMother = genDaughter1 + genDaughter2; // Gen Kstar from MC daughters + + recMass = mother.M(); + recPt = mother.Pt(); + + genMass = genMother.M(); + genPt = mothertrack1.pt(); + + hInvMass.fill(HIST("h3KstarMassGen"), genPt, multiplicity, genMass); + hInvMass.fill(HIST("h3KstarMassGenCalib"), genPt, multiplicityRec, genMass); + + hInvMass.fill(HIST("h3KstarMassRec"), recPt, multiplicity, recMass); + hInvMass.fill(HIST("h3KstarMassRecCalib"), recPt, multiplicityRec, recMass); hInvMass.fill(HIST("h2KstarRecpt2"), mothertrack1.pt(), multiplicity, std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())); hInvMass.fill(HIST("h2KstarRecptCalib2"), mothertrack1.pt(), multiplicityRec, std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())); - if (mother.Rapidity() >= selectionConfig.rapidityMotherData) { + if (mother.Rapidity() >= configGp.rapidityMotherData) { continue; } hInvMass.fill(HIST("h1KstarRecMass"), mother.M()); hInvMass.fill(HIST("h2KstarRecpt1"), mother.Pt(), multiplicity, mother.M()); hInvMass.fill(HIST("h2KstarRecptCalib1"), mother.Pt(), multiplicityRec, mother.M()); + // h3KstarMassGen } } } @@ -2281,7 +2508,7 @@ struct Kstarqa { const auto& mcCollisionRec = collision.mcCollision_as(); multiplicityRec = mcCollisionRec.centFT0M(); - if (selectionConfig.isINELgt0 && !collision.isInelGt0()) { + if (configGp.isINELgt0 && !collision.isInelGt0()) { return; } // multiplicity = collision.centFT0M(); @@ -2294,8 +2521,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } hInvMass.fill(HIST("hAllRecCollisions"), multiplicity); @@ -2377,17 +2602,17 @@ struct Kstarqa { // if (!(track1PDG == PDG_t::kKPlus && track2PDG == PDG_t::kPiPlus)) { // continue; // } - if (selectionConfig.isPDGCheckMC && (track1PDG != PDG_t::kKPlus)) { + if (track1PDG != PDG_t::kKPlus) { continue; } - if (selectionConfig.isPDGCheckMC && (track2PDG != PDG_t::kPiPlus)) { + if (track2PDG != PDG_t::kPiPlus) { continue; } rEventSelection.fill(HIST("recMCparticles"), 6.5); for (const auto& mothertrack1 : mctrack1.mothers_as()) { for (const auto& mothertrack2 : mctrack2.mothers_as()) { - if (selectionConfig.isPDGCheckMC && (mothertrack1.pdgCode() != mothertrack2.pdgCode())) { + if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { continue; } @@ -2401,58 +2626,54 @@ struct Kstarqa { } rEventSelection.fill(HIST("recMCparticles"), 8.5); - if (std::abs(mothertrack1.y()) >= selectionConfig.rapidityMotherData) { + if (std::abs(mothertrack1.y()) >= configGp.rapidityMotherData) { continue; } rEventSelection.fill(HIST("recMCparticles"), 9.5); - if (selectionConfig.isPDGCheckMC && (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892)) { + if (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kK0Star892) { continue; } rEventSelection.fill(HIST("recMCparticles"), 10.5); - if (!selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !(selectionPID(track1, 0) && selectionPID(track2, 1))) { // pion and kaon + if (configGp.isapplyPID1 && !(selectionPID(track1, 0) && selectionPID(track2, 1))) { // pion and kaon continue; - } else if (selectionConfig.isapplypTdepPID && !selectionConfig.isapplypTdepPIDTOF && !(selectionPIDPtDep(track1, 0) && selectionPIDPtDep(track2, 1))) { // pion and kaon + } + if (configGp.isapplypTdepPID && !(selectionPIDPtDep(track1, 0) && selectionPIDPtDep(track2, 1))) { // pion and kaon continue; - } else if (!selectionConfig.isapplypTdepPID && selectionConfig.isapplypTdepPIDTOF && !(selectionPIDptDepTOF(track1, 0) && selectionPIDptDepTOF(track2, 1))) { // pion and kaon + } + if (configGp.isapplypTdepPIDTOF && !(selectionPIDptDepTOF(track1, 0) && selectionPIDptDepTOF(track2, 1))) { // pion and kaon continue; } + if (configGp.isapplyPID2 && !selectionPID2(track1, 0)) + continue; + if (configGp.isapplyPID2 && !selectionPID2(track2, 1)) + continue; rEventSelection.fill(HIST("recMCparticles"), 11.5); - if (selectionConfig.isApplyParticleMID) { + if (configGp.isApplyParticleMID) { if (selectionMID(track1, 1)) // Pion misidentified as kaon continue; - // if (selectionMID(track1, 2)) // Kaon misidentified as proton - // continue; if (selectionMID(track2, 0)) // Kaon misidentified as pion continue; - // if (selectionMID(track2, 2)) // Pion misidentified as proton - // continue; } rEventSelection.fill(HIST("recMCparticles"), 12.5); - if (selectionConfig.isApplyParticleMIDPtDep) { + if (configGp.isApplyParticleMIDPtDep) { if (selectionMIDPtDep(track1, 1)) // Pion misidentified as kaon continue; if (selectionMIDPtDep(track2, 0)) // Kaon misidentified as pion continue; } - // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; - - // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Pion))) > selectionConfig.ctrackRapidity) - // continue; - - rEventSelection.fill(HIST("recMCparticles"), 13.5); - - /* if (selectionConfig.isApplyCutsOnMother) { - if (mothertrack1.pt() >= selectionConfig.cMaxPtMotherCut) // excluding candidates in overflow + if (configGp.isApplyParticleMIDPtDep2) { + if (selectionMIDPtDep2(track1, 1)) // Pion misidentified as kaon continue; - if ((std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())) >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow + if (selectionMIDPtDep2(track2, 0)) // Kaon misidentified as pion continue; - } */ + } + + rEventSelection.fill(HIST("recMCparticles"), 13.5); if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { hInvMass.fill(HIST("h1KSRecsplit"), mothertrack1.pt()); @@ -2474,7 +2695,7 @@ struct Kstarqa { hInvMass.fill(HIST("h2KstarRecpt2"), mothertrack1.pt(), multiplicity, std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())); hInvMass.fill(HIST("h2KstarRecptCalib2"), mothertrack1.pt(), multiplicityRec, std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())); - if (mother.Rapidity() >= selectionConfig.rapidityMotherData) { + if (mother.Rapidity() >= configGp.rapidityMotherData) { continue; } @@ -2510,8 +2731,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } // Fill the event counter @@ -2535,95 +2754,50 @@ struct Kstarqa { continue; if (cQAplots) { - // hPID.fill(HIST("Before/hNsigmaTPC_Ka_before"), track1.pt(), track1.tpcNSigmaKa()); - // hPID.fill(HIST("Before/hNsigmaTOF_Ka_before"), track1.pt(), track1.tofNSigmaKa()); - // hPID.fill(HIST("Before/hNsigmaTPC_Pi_before"), track2.pt(), track2.tpcNSigmaPi()); - // hPID.fill(HIST("Before/hNsigmaTOF_Pi_before"), track2.pt(), track2.tofNSigmaPi()); hPID.fill(HIST("Before/hNsigma_TPC_TOF_Ka_before"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); hPID.fill(HIST("Before/hNsigma_TPC_TOF_Pi_before"), track2.tpcNSigmaPi(), track2.tofNSigmaPi(), track2.pt()); hPID.fill(HIST("Before/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); - // hPID.fill(HIST("Before/hTPCnsigPi_mult_pt"), track2.tpcNSigmaPi(), multiplicity, track2.pt()); hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi(), multiplicity, track2.pt()); hOthers.fill(HIST("hCRFC_before"), track1.tpcCrossedRowsOverFindableCls()); - // hOthers.fill(HIST("dE_by_dx_TPC"), track1.p(), track1.tpcSignal()); hOthers.fill(HIST("hphi"), track1.phi()); - - // if (track1.sign() < 0) { - // hPID.fill(HIST("Before/h1PID_TPC_neg_kaon"), track1.tpcNSigmaKa()); - // // hPID.fill(HIST("Before/h1PID_TPC_neg_pion"), track2.tpcNSigmaPi()); - // hPID.fill(HIST("Before/h1PID_TOF_neg_kaon"), track1.tofNSigmaKa()); - // // hPID.fill(HIST("Before/h1PID_TOF_neg_pion"), track2.tofNSigmaPi()); - // } else { - // hPID.fill(HIST("Before/h1PID_TPC_pos_kaon"), track1.tpcNSigmaKa()); - // // hPID.fill(HIST("Before/h1PID_TPC_pos_pion"), track2.tpcNSigmaPi()); - // hPID.fill(HIST("Before/h1PID_TOF_pos_kaon"), track1.tofNSigmaKa()); - // // hPID.fill(HIST("Before/h1PID_TOF_pos_pion"), track2.tofNSigmaPi()); - // } } - if (cQAevents) { - rEventSelection.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), multiplicity, track1.pt()); - rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); - } + // if (cQAevents) { + // rEventSelection.fill(HIST("hDcaxy_cent_pt"), track1.dcaXY(), multiplicity, track1.pt()); + // rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); + // } // since we are using combinations full index policy, so repeated pairs are allowed, so we can check one with Kaon and other with kaon - if (!selectionConfig.isapplypTdepPID && !selectionPID(track1, 1)) // Track 1 is checked with Kaon + if (!configGp.isapplypTdepPID && !selectionPID(track1, 1)) // kaon continue; - if (!selectionConfig.isapplypTdepPID && !selectionPID(track2, 1)) // Track 2 is checked with kaon + if (!configGp.isapplypTdepPID && !selectionPID(track2, 1)) // Track 2 is checked with kaon continue; - if (selectionConfig.isapplypTdepPID && !selectionPIDPtDep(track1, 1)) // Track 1 is checked with Kaon + if (configGp.isapplypTdepPID && !selectionPIDPtDep(track1, 1)) // kaon continue; - if (selectionConfig.isapplypTdepPID && !selectionPIDPtDep(track2, 1)) // Track 2 is checked with kaon + if (configGp.isapplypTdepPID && !selectionPIDPtDep(track2, 1)) // Track 2 is checked with kaon continue; rEventSelection.fill(HIST("tracksCheckData"), 2.5); - // if (cFakeTrack && isFakeTrack(track1, 1)) // Kaon - // continue; - // if (cFakeTrack && isFakeTrack(track2, 1)) // Pion - // continue; + if (configGp.cFakeTrack && isFakeTrack(track1)) // Kaon + continue; rEventSelection.fill(HIST("tracksCheckData"), 3.5); - - // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; - - // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; rEventSelection.fill(HIST("tracksCheckData"), 4.5); - - /* if (selectionConfig.isApplyParticleMID) { - if (selectionMID(track1, 0)) // Kaon misidentified as pion - continue; - if (selectionMID(track1, 2)) // Kaon misidentified as proton - continue; - if (selectionMID(track2, 0)) // Pion misidentified as pion - continue; - if (selectionMID(track2, 2)) // Pion misidentified as proton - continue; - } */ - rEventSelection.fill(HIST("tracksCheckData"), 5.5); if (cQAplots) { - // hPID.fill(HIST("After/hDcaxyPi"), track2.dcaXY()); hPID.fill(HIST("After/hDcaxyKa"), track1.dcaXY()); - // hPID.fill(HIST("After/hDcazPi"), track2.dcaZ()); hPID.fill(HIST("After/hDcazKa"), track1.dcaZ()); hPID.fill(HIST("After/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); - // hPID.fill(HIST("After/hTPCnsigPi_mult_pt"), track2.tpcNSigmaPi(), multiplicity, track2.pt()); hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi(), multiplicity, track2.pt()); hOthers.fill(HIST("hEta_after"), track1.eta()); hOthers.fill(HIST("hCRFC_after"), track1.tpcCrossedRowsOverFindableCls()); - // hPID.fill(HIST("After/hNsigmaKaonTPC_after"), track1.pt(), track1.tpcNSigmaKa()); - // hPID.fill(HIST("After/hNsigmaKaonTOF_after"), track1.pt(), track1.tofNSigmaKa()); - // hPID.fill(HIST("After/hNsigmaPionTPC_after"), track2.pt(), track2.tpcNSigmaPi()); - // hPID.fill(HIST("After/hNsigmaPionTOF_after"), track2.pt(), track2.tofNSigmaPi()); hPID.fill(HIST("After/hNsigma_TPC_TOF_Ka_after"), track1.tpcNSigmaKa(), track1.tofNSigmaKa(), track1.pt()); hPID.fill(HIST("After/hNsigma_TPC_TOF_Pi_after"), track2.tpcNSigmaPi(), track2.tofNSigmaPi(), track2.pt()); } @@ -2637,13 +2811,6 @@ struct Kstarqa { daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); mother = daughter1 + daughter2; // Phi meson - /* if (selectionConfig.isApplyCutsOnMother) { - if (mother.Pt() >= selectionConfig.cMaxPtMotherCut) // excluding candidates in overflow - continue; - if (mother.M() >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow - continue; - } */ - if (cQAplots) { hOthers.fill(HIST("hKstar_rap_pt"), mother.Rapidity(), mother.Pt()); hOthers.fill(HIST("hKstar_eta_pt"), mother.Eta(), mother.Pt()); @@ -2659,7 +2826,7 @@ struct Kstarqa { void processMEPhi(EventCandidatesMix const&, TrackCandidates const&) { // Map estimator to pair and multiplicity accessor - auto runMixing = [&](auto& pair, auto multiplicityGetter) { + auto runMixing = [&](const auto& pair, auto multiplicityGetter) { for (const auto& [c1, tracks1, c2, tracks2] : pair) { // if (!c1.sel8() || !c2.sel8()) // continue; @@ -2677,28 +2844,6 @@ struct Kstarqa { if (!selectionPID(t1, 1) || !selectionPID(t2, 1)) continue; - // if (std::abs(t1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; - - // if (std::abs(t2.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; - - // if (cFakeTrack && isFakeTrack(t1, 1)) // Kaon - // continue; - // if (cFakeTrack && isFakeTrack(t2, 1)) // Kaon - // continue; - - /* if (selectionConfig.isApplyParticleMID) { - if (selectionMID(t1, 0)) // Kaon misidentified as pion - continue; - if (selectionMID(t1, 2)) // Kaon misidentified as proton - continue; - if (selectionMID(t2, 0)) // Pion misidentified as pion - continue; - if (selectionMID(t2, 2)) // Pion misidentified as proton - continue; - } */ - if (!selectionPair(t1, t2)) { continue; } @@ -2709,7 +2854,7 @@ struct Kstarqa { isMix = true; - if (std::abs(mother.Rapidity()) < selectionConfig.rapidityMotherData) { + if (std::abs(mother.Rapidity()) < configGp.rapidityMotherData) { fillInvMass(daughter1, daughter2, mother, multiplicity, isMix, t1, t2); } } @@ -2743,7 +2888,7 @@ struct Kstarqa { } } } - if (nChInel > 0 && std::abs(mcCollision.posZ()) < selectionConfig.cutzvertex) + if (nChInel > 0 && std::abs(mcCollision.posZ()) < configGp.cutzvertex) rEventSelection.fill(HIST("eventsCheckGen"), 1.5); std::vector selectedEvents(collisions.size()); @@ -2756,11 +2901,11 @@ struct Kstarqa { if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { isINELgt0true = true; } - if (selectionConfig.isINELgt0 && !isINELgt0true) { + if (configGp.isINELgt0 && !isINELgt0true) { return; } - // if (selectionConfig.isINELgt0 && !mcCollision.isInelGt0()) { + // if (configGp.isINELgt0 && !mcCollision.isInelGt0()) { // return; // } rEventSelection.fill(HIST("eventsCheckGen"), 2.5); @@ -2781,8 +2926,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } hInvMass.fill(HIST("h1GenMult"), multiplicity); @@ -2794,14 +2937,14 @@ struct Kstarqa { selectedEvents.resize(nevts); for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) < selectionConfig.rapidityMotherData && std::abs(mcParticle.pdgCode()) == o2::constants::physics::kPhi) { + if (std::abs(mcParticle.y()) < configGp.rapidityMotherData && std::abs(mcParticle.pdgCode()) == o2::constants::physics::kPhi) { hInvMass.fill(HIST("hAllKstarGenCollisisons"), multiplicity, mcParticle.pt()); } } const auto evtReconstructedAndSelected = std::find(selectedEvents.begin(), selectedEvents.end(), mcCollision.globalIndex()) != selectedEvents.end(); hInvMass.fill(HIST("hAllGenCollisions"), multiplicity); - if (!selectionConfig.allGenEvents && !evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection + if (!configGp.allGenEvents && !evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection return; } double genMultiplicity = mcCollision.centFT0M(); @@ -2811,24 +2954,17 @@ struct Kstarqa { for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) >= selectionConfig.rapidityMotherData) { + if (std::abs(mcParticle.y()) >= configGp.rapidityMotherData) { continue; } - /* if (selectionConfig.isApplyCutsOnMother) { - if (mcParticle.pt() >= selectionConfig.cMaxPtMotherCut) // excluding candidates in overflow - continue; - if ((std::sqrt(mcParticle.e() * mcParticle.e() - mcParticle.p() * mcParticle.p())) >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow - continue; - } */ - if (std::abs(mcParticle.pdgCode()) != o2::constants::physics::kPhi) { continue; } hInvMass.fill(HIST("hAllKstarGenCollisisons1Rec"), multiplicity, mcParticle.pt()); auto kDaughters = mcParticle.daughters_as(); - if (kDaughters.size() != selectionConfig.noOfDaughters) { + if (kDaughters.size() != configGp.noOfDaughters) { continue; } @@ -2866,17 +3002,17 @@ struct Kstarqa { multiplicityNch = mcCollision.multMCNParticlesEta05(); hInvMass.fill(HIST("CorrFactors/hGenEvents"), multiplicityNch, 0.5); - if (selectionConfig.checkVzEvSigLoss && std::abs(mcCollision.posZ()) > selectionConfig.cutzvertex) + if (configGp.isApplyMCGenVz && std::abs(mcCollision.posZ()) > configGp.cutzvertex) return; hInvMass.fill(HIST("CorrFactors/hGenEvents"), multiplicityNch, 1.5); - if (selectionConfig.isINELgt0 && !mcCollision.isInelGt0()) { + if (configGp.isINELgt0 && !mcCollision.isInelGt0()) { return; } hInvMass.fill(HIST("CorrFactors/hGenEvents"), multiplicityNch, 2.5); - float multiplicity = -1.0; + float multiplicityPhi = -1.0; bool isSelectedEvent = false; for (auto const& collision : collisions) { @@ -2886,15 +3022,13 @@ struct Kstarqa { continue; if (cSelectMultEstimator == kFT0M) { - multiplicity = collision.centFT0M(); + multiplicityPhi = collision.centFT0M(); } else if (cSelectMultEstimator == kFT0A) { - multiplicity = collision.centFT0A(); + multiplicityPhi = collision.centFT0A(); } else if (cSelectMultEstimator == kFT0C) { - multiplicity = collision.centFT0C(); + multiplicityPhi = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { - multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default + multiplicityPhi = collision.centFV0A(); } isSelectedEvent = true; } @@ -2902,7 +3036,7 @@ struct Kstarqa { // auto multiplicityGen = -1; // multiplicityGen = mcCollision.centFT0M(); - hInvMass.fill(HIST("CorrFactors/hMultiplicityVsMultMC"), multiplicity, multiplicityNch); + hInvMass.fill(HIST("CorrFactors/hMultiplicityVsMultMC"), multiplicityPhi, multiplicityNch); hInvMass.fill(HIST("CorrFactors/hNrecInGen"), collisions.size()); hInvMass.fill(HIST("CorrFactors/MultiplicityGen"), multiplicityNch); if (isSelectedEvent) { @@ -2911,13 +3045,13 @@ struct Kstarqa { for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) >= selectionConfig.rapidityMotherData) + if (std::abs(mcParticle.y()) >= configGp.rapidityMotherData) continue; if (std::abs(mcParticle.pdgCode()) == o2::constants::physics::kPhi) { auto kDaughters = mcParticle.daughters_as(); - if (kDaughters.size() != selectionConfig.noOfDaughters) { + if (kDaughters.size() != configGp.noOfDaughters) { continue; } @@ -2931,7 +3065,12 @@ struct Kstarqa { // continue; int pdgDau = kCurrentDaughter.pdgCode(); - int sign = (pdgDau > 0) - (pdgDau < 0); + int sign = 0; + if (pdgDau > 0) { + sign = 1; + } else if (pdgDau < 0) { + sign = -1; + } if (sign > 0) hasPos = true; @@ -2951,8 +3090,8 @@ struct Kstarqa { if ((passkaon && passpion) && (hasPos && hasNeg)) { mother = daughter1 + daughter2; // Kstar meson - hInvMass.fill(HIST("CorrFactors/h2dGenKstar"), multiplicity, mother.Pt()); - hInvMass.fill(HIST("CorrFactors/h3dGenKstarVsMultMCVsMultiplicity"), multiplicityNch, multiplicity, mother.Pt()); + hInvMass.fill(HIST("CorrFactors/h2dGenKstar"), multiplicityPhi, mother.Pt()); + hInvMass.fill(HIST("CorrFactors/h3dGenKstarVsMultMCVsMultiplicity"), multiplicityNch, multiplicityPhi, mother.Pt()); hInvMass.fill(HIST("CorrFactors/hSignalLoss1"), mother.pt(), multiplicityNch); if (isSelectedEvent) { hInvMass.fill(HIST("CorrFactors/hSignalLoss2"), mother.pt(), multiplicityNch); @@ -2976,14 +3115,12 @@ struct Kstarqa { } double multiplicityRec = -1.0; - // multiplicityRec = collision.mcCollision_as().centFT0M(); const auto& mcCollisionRec = collision.mcCollision_as(); multiplicityRec = mcCollisionRec.centFT0M(); - if (selectionConfig.isINELgt0 && !collision.isInelGt0()) { + if (configGp.isINELgt0 && !collision.isInelGt0()) { return; } - // multiplicity = collision.centFT0M(); if (cSelectMultEstimator == kFT0M) { multiplicity = collision.centFT0M(); @@ -2993,8 +3130,6 @@ struct Kstarqa { multiplicity = collision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity = collision.centFV0A(); - } else { - multiplicity = collision.centFT0M(); // default } hInvMass.fill(HIST("hAllRecCollisions"), multiplicity); @@ -3014,9 +3149,6 @@ struct Kstarqa { auto oldindex = -999; for (const auto& track1 : tracks) { - if (!selectionTrack(track1)) { - continue; - } if (!track1.has_mcParticle()) { continue; @@ -3027,6 +3159,24 @@ struct Kstarqa { rEventSelection.fill(HIST("hDcaz_cent_pt"), track1.dcaZ(), multiplicity, track1.pt()); } + if (!selectionTrack(track1)) { + continue; + } + + // All Kaons (Filling single-track kaon resolution histograms) + const auto mctrack1_all = track1.mcParticle(); + if (std::abs(mctrack1_all.pdgCode()) == PDG_t::kKPlus) { + double dpx_all = mctrack1_all.px() - track1.px(); + double dpy_all = mctrack1_all.py() - track1.py(); + double dpz_all = mctrack1_all.pz() - track1.pz(); + double dpt_all = mctrack1_all.pt() - track1.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_AllKaons"), dpx_all, track1.px()); + hShift.fill(HIST("h2D_dpy_vs_py_AllKaons"), dpy_all, track1.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_AllKaons"), dpz_all, track1.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_AllKaons"), dpt_all, track1.pt()); + } + auto track1ID = track1.index(); for (const auto& track2 : tracks) { rEventSelection.fill(HIST("recMCparticles"), 0.5); @@ -3057,7 +3207,6 @@ struct Kstarqa { int track2PDG = std::abs(mctrack2.pdgCode()); if (cQAplots) { hPID.fill(HIST("Before/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); - // hPID.fill(HIST("Before/hTPCnsigPi_mult_pt"), track2.tpcNSigmaPi(), multiplicity, track2.pt()); hPID.fill(HIST("Before/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); hPID.fill(HIST("Before/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi(), multiplicity, track2.pt()); } @@ -3074,7 +3223,7 @@ struct Kstarqa { } rEventSelection.fill(HIST("recMCparticles"), 5.5); - if (selectionConfig.isPDGCheckMC && ((track1PDG != PDG_t::kKPlus) || (track2PDG != PDG_t::kKPlus))) { + if (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kKPlus) { continue; } rEventSelection.fill(HIST("recMCparticles"), 6.5); @@ -3082,7 +3231,7 @@ struct Kstarqa { for (const auto& mothertrack1 : mctrack1.mothers_as()) { for (const auto& mothertrack2 : mctrack2.mothers_as()) { - if (selectionConfig.isPDGCheckMC && (mothertrack1.pdgCode() != mothertrack2.pdgCode())) { + if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { continue; } @@ -3096,55 +3245,30 @@ struct Kstarqa { } rEventSelection.fill(HIST("recMCparticles"), 9.5); - if (std::abs(mothertrack1.y()) >= selectionConfig.rapidityMotherData) { + if (std::abs(mothertrack1.y()) >= configGp.rapidityMotherData) { continue; } rEventSelection.fill(HIST("recMCparticles"), 10.5); - if (selectionConfig.isPDGCheckMC && (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kPhi)) { + if (std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kPhi) { continue; } rEventSelection.fill(HIST("recMCparticles"), 11.5); - if (!selectionConfig.isapplypTdepPID && !(selectionPID(track1, 1) && selectionPID(track2, 1))) { // kaon and kaon + if (!configGp.isapplypTdepPID && !(selectionPID(track1, 1) && selectionPID(track2, 1))) { continue; - } else if (selectionConfig.isapplypTdepPID && !(selectionPIDPtDep(track1, 1) && selectionPIDPtDep(track2, 1))) { // kaon and kaon + } + if (configGp.isapplypTdepPID && !(selectionPIDPtDep(track1, 1) && selectionPIDPtDep(track2, 1))) { continue; } rEventSelection.fill(HIST("recMCparticles"), 12.5); - - /* if (selectionConfig.isApplyParticleMID) { - if (selectionMID(track1, 0)) // Kaon misidentified as pion - continue; - if (selectionMID(track1, 2)) // Kaon misidentified as proton - continue; - if (selectionMID(track2, 0)) // Kaon misidentified as pion - continue; - if (selectionMID(track2, 2)) // Kaon misidentified as proton - continue; - } */ rEventSelection.fill(HIST("recMCparticles"), 13.5); - - // if (std::abs(track1.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; - - // if (std::abs(track2.rapidity(o2::track::PID::getMass(o2::track::PID::Kaon))) > selectionConfig.ctrackRapidity) - // continue; rEventSelection.fill(HIST("recMCparticles"), 14.5); if (cQAplots) { hPID.fill(HIST("After/hTPCnsigKa_mult_pt"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); - // hPID.fill(HIST("After/hTPCnsigPi_mult_pt"), track2.tpcNSigmaPi(), multiplicity, track2.pt()); hPID.fill(HIST("After/hTOFnsigKa_mult_pt"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - // hPID.fill(HIST("After/hTOFnsigPi_mult_pt"), track2.tofNSigmaPi(), multiplicity, track2.pt()); } - /* if (selectionConfig.isApplyCutsOnMother) { - if (mothertrack1.pt() >= selectionConfig.cMaxPtMotherCut) // excluding candidates in overflow - continue; - if ((std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())) >= selectionConfig.cMaxMinvMotherCut) // excluding candidates in overflow - continue; - } */ - if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { hInvMass.fill(HIST("h1KSRecsplit"), mothertrack1.pt()); continue; @@ -3160,18 +3284,57 @@ struct Kstarqa { if (track1.sign() * track2.sign() < 0) { daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - mother = daughter1 + daughter2; // Phi meson - hInvMass.fill(HIST("h2KstarRecpt2"), mothertrack1.pt(), multiplicity, std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())); - hInvMass.fill(HIST("h2KstarRecptCalib2"), mothertrack1.pt(), multiplicityRec, std::sqrt(mothertrack1.e() * mothertrack1.e() - mothertrack1.p() * mothertrack1.p())); + genDaughter1 = ROOT::Math::PxPyPzMVector(mctrack1.px(), mctrack1.py(), mctrack1.pz(), massKa); + genDaughter2 = ROOT::Math::PxPyPzMVector(mctrack2.px(), mctrack2.py(), mctrack2.pz(), massKa); - if (mother.Rapidity() >= selectionConfig.rapidityMotherData) { - continue; - } + mother = daughter1 + daughter2; // Phi meson + genMother = genDaughter1 + genDaughter2; // Phi meson - hInvMass.fill(HIST("h1KstarRecMass"), mother.M()); - hInvMass.fill(HIST("h2KstarRecpt1"), mother.Pt(), multiplicity, mother.M()); - hInvMass.fill(HIST("h2KstarRecptCalib1"), mother.Pt(), multiplicityRec, mother.M()); + recMass = mother.M(); + genMass = genMother.M(); + + hShift.fill(HIST("h1D_MassPhiGen"), genMass); + hShift.fill(HIST("h1D_MassPhiRec"), recMass); + hShift.fill(HIST("h2D_pt_massShiftPhi"), mother.pt(), genMass - recMass); + + auto genpTPhi = mothertrack1.pt(); + auto genEtaPhi = mothertrack1.eta(); + auto genPhiPhi = mothertrack1.phi(); + + hInvMass.fill(HIST("PhiMCChecks"), mother.pt(), mother.eta(), mother.phi(), genpTPhi - mother.pt(), genEtaPhi - mother.eta(), genPhiPhi - mother.phi()); + + // Kaons from Phi daughter (Filling for daughter track1 & daughter track2) + double dpx_k1 = mctrack1.px() - track1.px(); + double dpy_k1 = mctrack1.py() - track1.py(); + double dpz_k1 = mctrack1.pz() - track1.pz(); + double dpt_k1 = mctrack1.pt() - track1.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_KaonFromPhi"), dpx_k1, track1.px()); + hShift.fill(HIST("h2D_dpy_vs_py_KaonFromPhi"), dpy_k1, track1.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_KaonFromPhi"), dpz_k1, track1.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_KaonFromPhi"), dpt_k1, track1.pt()); + + double dpx_k2 = mctrack2.px() - track2.px(); + double dpy_k2 = mctrack2.py() - track2.py(); + double dpz_k2 = mctrack2.pz() - track2.pz(); + double dpt_k2 = mctrack2.pt() - track2.pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_KaonFromPhi"), dpx_k2, track2.px()); + hShift.fill(HIST("h2D_dpy_vs_py_KaonFromPhi"), dpy_k2, track2.py()); + hShift.fill(HIST("h2D_dpz_vs_pz_KaonFromPhi"), dpz_k2, track2.pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_KaonFromPhi"), dpt_k2, track2.pt()); + + // Phi Meson + double dpx_phi = mothertrack1.px() - mother.Px(); + double dpy_phi = mothertrack1.py() - mother.Py(); + double dpz_phi = mothertrack1.pz() - mother.Pz(); + double dpt_phi = mothertrack1.pt() - mother.Pt(); + + hShift.fill(HIST("h2D_dpx_vs_px_Phi"), dpx_phi, mother.Px()); + hShift.fill(HIST("h2D_dpy_vs_py_Phi"), dpy_phi, mother.Py()); + hShift.fill(HIST("h2D_dpz_vs_pz_Phi"), dpz_phi, mother.Pz()); + hShift.fill(HIST("h2D_dpt_vs_pt_Phi"), dpt_phi, mother.Pt()); } } } @@ -3203,8 +3366,6 @@ struct Kstarqa { centrality = RecCollision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { centrality = RecCollision.centFV0A(); - } else { - centrality = RecCollision.centFT0M(); // default } isSelectedEvent = true; @@ -3218,7 +3379,7 @@ struct Kstarqa { // Generated MC for (const auto& mcPart : mcParticles) { - if (std::abs(mcPart.y()) >= selectionConfig.rapidityMotherData || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) + if (std::abs(mcPart.y()) >= configGp.rapidityMotherData || std::abs(mcPart.pdgCode()) != o2::constants::physics::kK0Star892) continue; // signal loss estimation @@ -3234,7 +3395,7 @@ struct Kstarqa { void processEvtLossSigLossMCPhi(EventMCGenerated::iterator const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& recCollisions) // void processEvtLossSigLossMCPhi(aod::McCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& recCollisions) { - // if (selectionConfig.isINELgt0 && !mcCollision.isInelGt0()) { + // if (configGp.isINELgt0 && !mcCollision.isInelGt0()) { // return; // } @@ -3243,17 +3404,17 @@ struct Kstarqa { if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { isINELgt0true = true; } - if (selectionConfig.isINELgt0 && !isINELgt0true) { + if (configGp.isINELgt0 && !isINELgt0true) { return; } - if (selectionConfig.checkVzEvSigLoss && (std::abs(mcCollision.posZ()) > selectionConfig.cutzvertex)) { + if (configGp.isApplyMCGenVz && (std::abs(mcCollision.posZ()) > configGp.cutzvertex)) { return; } auto impactPar = mcCollision.impactParameter(); // auto multiplicityRec = -1; - auto multiplicityGen = -1; + auto multiplicityGen = -1.0f; multiplicityGen = mcCollision.centFT0M(); hInvMass.fill(HIST("MCcorrections/hImpactParameterGen"), impactPar); hInvMass.fill(HIST("MCcorrections/MultiplicityGen"), multiplicityGen); @@ -3277,8 +3438,6 @@ struct Kstarqa { multiplicity1 = RecCollision.centFT0C(); } else if (cSelectMultEstimator == kFV0A) { multiplicity1 = RecCollision.centFV0A(); - } else { - multiplicity1 = RecCollision.centFT0M(); // default } isSelectedEvent = true; } @@ -3292,7 +3451,7 @@ struct Kstarqa { // Generated MC for (const auto& mcPart : mcParticles) { - if (std::abs(mcPart.y()) >= selectionConfig.rapidityMotherData || std::abs(mcPart.pdgCode()) != o2::constants::physics::kPhi) + if (std::abs(mcPart.y()) >= configGp.rapidityMotherData || std::abs(mcPart.pdgCode()) != o2::constants::physics::kPhi) continue; // signal loss estimation diff --git a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx index 7c12c2b9042..f186a29e35b 100644 --- a/PWGLF/Tasks/Resonances/lambda1405analysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1405analysis.cxx @@ -17,134 +17,530 @@ #include "PWGLF/DataModel/LFLambda1405Table.h" #include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/Qvectors.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include +#include #include +#include +#include #include #include #include #include +#include #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include + #include #include +#include #include +#include +#include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -using TracksFull = soa::Join; -using CollisionsFull = soa::Join; -using CollisionsFullMC = soa::Join; +using TracksFull = soa::Join; +using CollisionsFull = soa::Join; +using CollisionsFullWQVecs = soa::Join; +using CollisionsFullMc = soa::Join; +// using CollisionsFullMcWCent = soa::Join; + +using BinningCentPosZ = ColumnBinningPolicy; +using BinningMultPosZ = ColumnBinningPolicy, aod::collision::PosZ>; + +enum L1405DecayType { kSigmaMinusPiToPiPiNeutron = 0, + kSigmaPlusPiToPiPiNeutron, + kSigmaPlusPiToPiPiProton, + kNL1405Decays }; struct lambda1405candidate { // Columns for Lambda(1405) candidate - float mass = -1; // Invariant mass of the Lambda(1405) candidate + float massL1405 = -1; // Invariant mass of the Lambda(1405) candidate float massXi1530 = -1; // Invariant mass of the Xi(1530) candidate float px = -1; // Px of the Lambda(1405) candidate float py = -1; // Py of the Lambda(1405) candidate float pz = -1; // Pz of the Lambda(1405) candidate - float pt() const { return std::sqrt(px * px + py * py); } // pT of the Lambda(1405 candidate - - bool isSigmaPlus = false; // True if compatible with Sigma+ - bool isSigmaMinus = false; // True if compatible with Sigma- - float sigmaMinusMass = -1; // Invariant mass of the Sigma- candidate - float sigmaPlusMass = -1; // Invariant mass of the Sigma+ candidate - float xiMinusMass = -1; // Invariant mass of the Xi- candidate - int sigmaSign = 0; // Sign of the Sigma candidate: 1 for matter, -1 for antimatter - float sigmaPt = -1; // pT of the Sigma daughter - float sigmaAlphaAP = -1; // Alpha of the Sigma - float sigmaQtAP = -1; // qT of the Sigma - float kinkPt = -1; // pT of the kink daughter - float kinkTPCNSigmaPi = -1; // Number of sigmas for the pion candidate from Sigma kink in TPC - float kinkTOFNSigmaPi = -1; // Number of sigmas for the pion candidate from Sigma kink in TOF - float kinkTPCNSigmaPr = -1; // Number of sigmas for the proton candidate from Sigma kink in TPC - float kinkTOFNSigmaPr = -1; // Number of sigmas for the proton candidate from Sigma kink in TOF - float dcaKinkDauToPV = -1; // DCA of the kink daughter to the primary vertex - float sigmaRadius = -1; // Radius of the Sigma decay vertex - - float piPt = -1; // pT of the pion daughter - float nSigmaTPCPi = -1; // Number of sigmas for the pion candidate - float nSigmaTOFPi = -1; // Number of sigmas for the pion candidate using TOF - int kinkDauID = 0; // ID of the pion from Sigma decay in MC - int sigmaID = 0; // ID of the Sigma candidate in MC - int piID = 0; // ID of the pion candidate in MC + float pt() const { return std::sqrt(px * px + py * py); } // pT of the Lambda(1405) candidate + float phi = -1; // Phi of the Lambda(1405) candidate + float eta = -1; // Eta of the Lambda(1405) candidate + + bool isSigmaPlus = false; // True if compatible with Sigma+ + bool isSigmaMinus = false; // True if compatible with Sigma- + float sigmaMinusMass = -1; // Invariant mass of the Sigma- candidate + float sigmaPlusMass = -1; // Invariant mass of the Sigma+ candidate + float xiMinusMass = -1; // Invariant mass of the Xi- candidate + int sigmaSign = 0; // Sign of the Sigma candidate: 1 for matter, -1 for antimatter + float sigmaPt = -1; // pT of the Sigma daughter + float sigmaAlphaAP = -1; // Alpha of the Sigma + float sigmaQtAP = -1; // qT of the Sigma + float sigmaRadius = -1; // Radius of the Sigma decay vertex + float dcaSigmaToPv = -1; // DCA of the Sigma candidate to the primary vertex + float kinkPt = -1; // pT of the kink daughter + float kinkPiNSigTpc = -1; // Number of sigmas for the pion candidate from Sigma kink in Tpc + float kinkPiNSigTof = -1; // Number of sigmas for the pion candidate from Sigma kink in Tof + float kinkPrNSigTpc = -1; // Number of sigmas for the proton candidate from Sigma kink in Tpc + float kinkPrNSigTof = -1; // Number of sigmas for the proton candidate from Sigma kink in Tof + float kinkDcaDauToPv = -1; // DCA of the kink daughter to the primary vertex + float chi2KinkPCA = -1; // Chi2 of the Sigma candidate at the PCA + + float bachPiPt = -1; // pT of the pion daughter + float bachPiNSigTpc = -1; // Number of sigmas for the pion candidate + float bachPiNSigTof = -1; // Number of sigmas for the pion candidate using Tof + int kinkDauId = 0; // Id of the pion from Sigma decay in MC + int sigmaId = 0; // Id of the Sigma candidate in MC + int bachPiId = 0; // Id of the pion candidate in MC + + float centMult = -1; // Centrality of the collision + float pvContrib = -1; // Number of contributors to the primary vertex + float occupancy = -1; // Occupancy of the collision + + float scalarProd = -1; // Scalar product for flow analysis + + int poolBin = -1; // Pool bin for event mixing }; struct lambda1405analysis { - int lambda1405PdgCode = 102132; // PDG code for Lambda(1405) - lambda1405candidate lambda1405Cand; // Lambda(1405) candidate structure - Produces outputDataTable; // Output table for Lambda(1405) candidates - Produces outputDataTableMC; // Output table for Lambda(1405) candidates in MC + int lambda1405PdgCode = 102132; // o2-linter: disable=pdg/explicit-code + Produces outputDataTable; // Output table for Lambda(1405) candidates + Produces outputDataTableMC; // Output table for Lambda(1405) candidates in MC + Produces outputSigmaEffMC; // Output table for Lambda(1405) sigma efficiency in MC + + Service ccdb; + // Histograms are defined with HistogramRegistry HistogramRegistry rEventSelection{"eventSelection", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry rLambda1405{"lambda1405", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry rSigmaMinus{"sigmaMinus", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry rSigmaPlus{"sigmaPlus", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry rSelections{"selections", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Configurable for event selection - Configurable cutzvertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; - Configurable cutEtaDaught{"cutEtaDaughter", 0.8f, "Eta cut for daughter tracks"}; - Configurable cutDCAtoPVSigma{"cutDCAtoPVSigma", 0.1f, "Max DCA to primary vertex for Sigma candidates (cm)"}; - Configurable cutDCAtoPVPiFromSigma{"cutDCAtoPVPiFromSigma", 2., "Min DCA to primary vertex for pion from Sigma candidates (cm)"}; - - Configurable cutUpperMass{"cutUpperMass", 1.6f, "Upper mass cut for Lambda(1405) candidates (GeV/c^2)"}; - Configurable cutSigmaRadius{"cutSigmaRadius", 20.f, "Minimum radius for Sigma candidates (cm)"}; - Configurable cutSigmaMass{"cutSigmaMass", 0.1, "Sigma mass window (MeV/c^2)"}; - Configurable cutNITSClusPi{"cutNITSClusPi", 5, "Minimum number of ITS clusters for pion candidate"}; - Configurable cutNTPCClusPi{"cutNTPCClusPi", 90, "Minimum number of TPC clusters for pion candidate"}; - Configurable cutNSigmaTPC{"cutNSigmaTPC", 3, "NSigmaTPCPion"}; - Configurable cutNSigmaTOF{"cutNSigmaTOF", 3, "NSigmaTOFPion"}; + struct : o2::framework::ConfigurableGroup { + Configurable cutZVertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable centMultMin{"centMultMin", 0., "Minimum collision centrality/multiplicity accepted"}; + Configurable centMultMax{"centMultMax", 100., "Maximum collision centrality/multiplicity accepted"}; + Configurable occupancyMax{"occupancyMax", 3000000., "Maximum collision occupancy accepted"}; + } eventSelection; + + struct : o2::framework::ConfigurableGroup { + Configurable cutEtaDaughter{"cutEtaDaughter", 0.8f, "Eta cut for daughter tracks"}; + Configurable cutDcaToPvSigma{"cutDcaToPvSigma", 0.1f, "Max DCA to primary vertex for Sigma candidates (cm)"}; + Configurable cutDcaToPvPiFromSigma{"cutDcaToPvPiFromSigma", 2., "Min DCA to primary vertex for pion from Sigma candidates (cm)"}; + Configurable cutMinPtL1405{"cutMinPtL1405", 2.0f, "Minimum pT cut for Lambda(1405) candidates (GeV/c)"}; + Configurable cutUpperMass{"cutUpperMass", 1.6f, "Upper mass cut for Lambda(1405) candidates (GeV/c^2)"}; + Configurable cutSigmaRadius{"cutSigmaRadius", 20.f, "Minimum radius for Sigma candidates (cm)"}; + Configurable cutSigmaMass{"cutSigmaMass", 0.1, "Sigma mass window (MeV/c^2)"}; + Configurable cutSigmaChi2PCA{"cutSigmaChi2PCA", 100.f, "Maximum Chi2 PCA for Sigma candidates"}; + Configurable cutItsNClusKinkMin{"cutItsNClusKinkMin", 1, "Minimum number of ITS clusters for kink daughter"}; + Configurable cutItsNClusKinkMax{"cutItsNClusKinkMax", 3, "Maximum number of ITS clusters for kink daughter"}; + Configurable cutNTpcClus{"cutNTpcClus", 90, "Minimum number of Tpc clusters for pion candidate"}; + Configurable cutNSigTpc{"cutNSigTpc", 3, "NSigTpcPion"}; + Configurable cutNSigTof{"cutNSigTof", 3, "NSigTofPion"}; + Configurable cutMaxDcaZBach{"cutMaxDcaZBach", 0.1, "Maximum DcaZ for bachelor pion (cm)"}; + Configurable dcaXYBachNSigmaMax{"dcaXYBachNSigmaMax", 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"}; + Configurable dcaXYPtBachFunc{"dcaXYPtBachFunc", "(0.0026+0.005/(x^1.01))", "Functional form of pt-dependent DCAxy cut"}; + Configurable cutSigmaQtAPMin{"cutSigmaQtAPMin", "0.17", "Functional form of minimum qT(alpha) selection"}; + Configurable cutSigmaQtAPMax{"cutSigmaQtAPMax", "0.2", "Functional form of minimum qT(alpha) selection"}; + Configurable cutMinBachPiPtVsL1405Pt{"cutMinBachPiPtVsL1405Pt", "0", "Functional form of minimum qT(alpha) selection"}; + Configurable cutMaxBachPiPtVsL1405Pt{"cutMaxBachPiPtVsL1405Pt", "10", "Functional form of minimum qT(alpha) selection"}; + Configurable cutMinSigmaPtVsL1405Pt{"cutMinSigmaPtVsL1405Pt", "0", "Functional form of minimum qT(alpha) selection"}; + Configurable cutMaxSigmaPtVsL1405Pt{"cutMaxSigmaPtVsL1405Pt", "10", "Functional form of minimum qT(alpha) selection"}; + } selections; + // Configurables for flow analysis + Configurable minDEta{"minDEta", -1., "Minimum delta eta between L1405-track pairs"}; + Configurable assTrkMinPt{"assTrkMinPt", 0., "Minimum pT of associated track for 2PC"}; + Configurable assTrkMaxPt{"assTrkMaxPt", 10., "Maximum pT of associated track for 2PC"}; + Configurable saveDEtaDPhi{"saveDEtaDPhi", false, "If true, save the dEta and dPhi distributions for Lambda(1405) candidates"}; + Configurable fillFlowTree{"fillFlowTree", false, "If true, fill the output tree with Lambda(1405) candidates"}; + Configurable useCentrality{"useCentrality", true, "If true, use centrality for binning"}; + Configurable numberEventsMixed{"numberEventsMixed", 5, "Number of events mixed in ME process"}; + + // Downsample for table filling + Configurable downSampleFactor{"downSampleFactor", 1., "Fraction of candidates to keep in TTree"}; + Configurable ptDownSampleMax{"ptDownSampleMax", 10., "Maximum pt for the application of the downsampling factor"}; + + Configurable skipBkgSigmas{"skipBkgSigmas", true, "If true, skip un-matched sigmas in efficiency process function"}; Configurable fillOutputTree{"fillOutputTree", true, "If true, fill the output tree with Lambda(1405) candidates"}; - Configurable doLSBkg{"doLikeSignBkg", false, "Use like-sign background"}; - Configurable useTOF{"useTOF", false, "Use TOF for PID for pion candidates"}; + Configurable doLikeSignBkg{"doLikeSignBkg", false, "Use like-sign background"}; + Configurable useTof{"useTof", false, "Use Tof for PID for pion candidates"}; + Configurable recomputeSigmaMom{"recomputeSigmaMom", true, "Recalculate sigma momentum from daughter kinematics"}; + Configurable skipSigmasFailedRecompMom{"skipSigmasFailedRecompMom", false, "Skip sigmas for which momentum recalculation fails"}; + + // CCDB options + Configurable cfgMaterialCorrection{"cfgMaterialCorrection", static_cast(o2::base::Propagator::MatCorrType::USEMatCorrNONE), "Type of material correction"}; + Configurable ccdbPath{"ccdbPath", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + + TF1 funcMinQtAlphaAP, funcMaxQtAlphaAP, funcMinBachPiPtVsL1405Pt, funcMaxBachPiPtVsL1405Pt, funcMinSigmaPtVsL1405Pt, funcMaxSigmaPtVsL1405Pt, funcDcaXYPtCutBachPi; + + using CollisionsSel = soa::Filtered; + using CollisionsSelWQVecs = soa::Filtered; + using McRecoCollisionsSel = soa::Filtered; + + Filter filterOccupancy = aod::evsel::ft0cOccupancyInTimeRange <= eventSelection.occupancyMax; + + PresliceUnsorted colPerMcCollision = aod::mccollisionlabel::mcCollisionId; + + SliceCache cache; + + int poolBins{0}; + + // Needed for DCA propagation of bachelor tracks + o2::base::MatLayerCylSet* lut = nullptr; + + // Configurable axes + ConfigurableAxis zPoolBins{"zPoolBins", {VARIABLE_WIDTH, -10.0, -2.5, 2.5, 10.0}, "Z vertex position pools"}; + ConfigurableAxis centMultPoolBins{"centMultPoolBins", {VARIABLE_WIDTH, 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100}, "Event cent/mult pools"}; + ConfigurableAxis axisPvContrib{"axisPvContrib", {5000, 0., 5000.}, ""}; + ConfigurableAxis axisCent{"axisCent", {100, 0., 100.}, ""}; + ConfigurableAxis axisOccupancy{"axisOccupancy", {100, 0., 140000.}, ""}; + ConfigurableAxis axisPtL1405{"axisPtL1405", {100, 0., 10.}, ""}; + ConfigurableAxis axisPCompsL1405{"axisPCompsL1405", {100, -10., 10.}, ""}; + ConfigurableAxis axisPtKinkDaug{"axisPtKinkDaug", {100, 0., 10.}, ""}; + ConfigurableAxis axisPtResolution{"axisPtResolution", {100, -0.5, 0.5}, ""}; + ConfigurableAxis axisMassL1405{"axisMassL1405", {500, 1.3, 1.8}, ""}; + ConfigurableAxis axisXi1530Mass{"axisXi1530Mass", {500, 1.3, 1.8}, ""}; + ConfigurableAxis axisMassReso{"axisMassReso", {100, -0.1, 0.1}, ""}; + ConfigurableAxis axisNSig{"axisNSig", {100, -5., 5.}, ""}; + ConfigurableAxis axisSigmaMass{"axisSigmaMass", {300, 1.1, 1.4}, ""}; + ConfigurableAxis axisXiMass{"axisXiMass", {300, 1.2, 1.5}, ""}; + ConfigurableAxis axisVertexZ{"axisVertexZ", {100, -15., 15.}, ""}; + ConfigurableAxis axisQtAP{"axisQtAP", {100, 0., 0.3}, ""}; + ConfigurableAxis axisAlphaAP{"axisAlphaAP", {200, -1., 1.}, ""}; + ConfigurableAxis axisSigmaRadius{"axisSigmaRadius", {100, 0., 100.}, ""}; + ConfigurableAxis axisDcaSigmaToPv{"axisDcaSigmaToPv", {120, 0., 0.05}, ""}; + ConfigurableAxis axisDcaKinkToPv{"axisDcaKinkToPv", {200, 0., 20.}, ""}; + ConfigurableAxis axisDcaBachPi{"axisDcaBachPi", {4000, -1., 1.}, ""}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {100, -2., 2.}, ""}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {64, -o2::constants::math::PIHalf, 3. * o2::constants::math::PIHalf}, ""}; + ConfigurableAxis axisScalarProd{"axisScalarProd", {200, -4., 4.}, ""}; Preslice mKinkPerCol = aod::track::collisionId; Preslice mPerColTracks = aod::track::collisionId; void init(InitContext const&) { + // Define pool bins + poolBins = (centMultPoolBins->size() - 2) * (zPoolBins->size() - 2); + const AxisSpec axisPoolBin = {poolBins, -0.5, static_cast(poolBins) - 0.5, "PoolBin"}; + // Axes - const AxisSpec ptAxis{100, -10, 10, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec ptPiAxis{100, -5, 5, "#it{p}_{T}^{#pi} (GeV/#it{c})"}; - const AxisSpec ptResolutionAxis{100, -0.5, 0.5, "#it{p}_{T}^{rec} - #it{p}_{T}^{gen} (GeV/#it{c})"}; - const AxisSpec massAxis{100, 1.3, 1.6, "m (GeV/#it{c}^{2})"}; - const AxisSpec massResolutionAxis{100, -0.1, 0.1, "m_{rec} - m_{gen} (GeV/#it{c}^{2})"}; - const AxisSpec nSigmaPiAxis{100, -5, 5, "n#sigma_{#pi}"}; - const AxisSpec sigmaMassAxis{100, 1.1, 1.4, "m (GeV/#it{c}^{2})"}; - const AxisSpec vertexZAxis{100, -15., 15., "vrtx_{Z} [cm]"}; + const AxisSpec ptAxis{axisPtL1405, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec pCompAxisL1405{axisPCompsL1405, "#it{p}_{comp} (GeV/#it{c})"}; + const AxisSpec ptKinkDaugAxis{axisPtKinkDaug, "#it{p}_{T}^{#pi} (GeV/#it{c})"}; + const AxisSpec ptResolutionAxis{axisPtResolution, "#it{p}_{T}^{rec} - #it{p}_{T}^{gen} (GeV/#it{c})"}; + const AxisSpec lambda1405MassAxis{axisMassL1405, "m(#Sigma#pi) (GeV/#it{c}^{2})"}; + const AxisSpec xi1530MassAxis{axisXi1530Mass, "m(#Xi#pi) (GeV/#it{c}^{2})"}; + const AxisSpec massResoAxis{axisMassReso, "m_{rec} - m_{gen} (GeV/#it{c}^{2})"}; + const AxisSpec nSigmaAxis{axisNSig, "n#sigma_{#pi}"}; + const AxisSpec sigmaMassAxis{axisSigmaMass, "m(#Sigma) (GeV/#it{c}^{2})"}; + const AxisSpec xiMassAxis{axisXiMass, "m(#Xi) (GeV/#it{c}^{2})"}; + const AxisSpec vertexZAxis{axisVertexZ, "vtx_{Z} (cm)"}; + const AxisSpec qtAxis{axisQtAP, "q_{T, AP}"}; + const AxisSpec alphaAxis{axisAlphaAP, "#alpha_{AP}"}; + const AxisSpec sigmaRadiusAxis{axisSigmaRadius, "Dec. radius #Sigma (cm)"}; + const AxisSpec centMultAxis{axisCent, "Centrality (%)"}; + const AxisSpec occAxis{axisOccupancy, "Occupancy FT0C"}; + const AxisSpec pvContribAxis{axisPvContrib, "PV Contributors"}; + const AxisSpec dcaSigmaToPvAxis{axisDcaSigmaToPv, "#Sigma DCA to PV (cm)"}; + const AxisSpec dcaKinkToPvAxis{axisDcaKinkToPv, "Kink daug. DCA to PV (cm)"}; + const AxisSpec dcaBachPiAxis{axisDcaBachPi, "Bach. #pi DCA to PV (cm)"}; + const AxisSpec deltaEtaAxis{axisDeltaEta, "#Delta#it{#eta}"}; + const AxisSpec deltaPhiAxis{axisDeltaPhi, "#Delta#it{#varphi}"}; + const AxisSpec scalarProdAxis{axisScalarProd, "SP"}; + rEventSelection.add("hVertexZRec", "hVertexZRec", {HistType::kTH1F, {vertexZAxis}}); + rEventSelection.add("hCentMultVsPvContrib", "hCentMultVsPvContrib", {HistType::kTH2F, {centMultAxis, pvContribAxis}}); + rEventSelection.add("hOccVsPvContrib", "hOccVsPvContrib", {HistType::kTH2F, {occAxis, pvContribAxis}}); + rEventSelection.add("hCentVsOcc", "hCentVsOcc", {HistType::kTH2F, {centMultAxis, occAxis}}); + rEventSelection.add("hCentPoolBin", "Centrality pool bin", {HistType::kTH2F, {{axisCent}, {axisPoolBin}}}); + rEventSelection.add("hZVtxPoolBin", "Z vtx pool bin", {HistType::kTH2F, {{vertexZAxis}, {axisPoolBin}}}); + rEventSelection.add("hMultPoolBin", "Multiplicity pool bin", {HistType::kTH2F, {{axisCent}, {axisPoolBin}}}); + rEventSelection.add("hPoolBins", "Pool bins", {HistType::kTH1F, {{102, -1.5, 100.5}}}); + + // Sigma- candidate properties + rSigmaMinus.add("hMassXiMinusSigmaMinus", "hMassXiMinusSigmaMinus", {HistType::kTH2F, {xiMassAxis, sigmaMassAxis}}); + rSigmaMinus.add("h2PtMassSigmaMinusBeforeCuts", "h2PtMassSigmaMinusBeforeCuts", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); + rSigmaMinus.add("h2PtPiKinkNSigBeforeCutsSigmaMinus", "h2PtPiKinkNSigBeforeCutsSigmaMinus", {HistType::kTH2F, {ptAxis, nSigmaAxis}}); + rSigmaMinus.add("h2dPtMassSigmaMinus", "h2dPtMassSigmaMinus", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); + rSigmaMinus.add("h2dPvContribMassSigmaMinus", "h2dPvContribMassSigmaMinus", {HistType::kTH2F, {pvContribAxis, sigmaMassAxis}}); + rSigmaMinus.add("h2dOccMassSigmaMinus", "h2dOccMassSigmaMinus", {HistType::kTH2F, {occAxis, sigmaMassAxis}}); + rSigmaMinus.add("h2dPvContribPtSigmaMinus", "h2dPvContribPtSigmaMinus", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + rSigmaMinus.add("h2KinkPiPtNSigTofSigmaMinus", "h2KinkPiPtNSigTofSigmaMinus", {HistType::kTH2F, {ptKinkDaugAxis, nSigmaAxis}}); + rSigmaMinus.add("hSigmaMinusArmPod", "hSigmaMinusArmPod", {HistType::kTH2D, {alphaAxis, qtAxis}}); + rSigmaMinus.add("hSigmaMinusRadius", "hSigmaMinusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); + rSigmaMinus.add("hSigmaMinusDcaToPv", "hSigmaMinusDcaToPv", {HistType::kTH2F, {dcaSigmaToPvAxis, ptAxis}}); + rSigmaMinus.add("hSigmaMinusKinkTpcNSigPi", "hSigmaMinusKinkTpcNSigPi", {HistType::kTH2F, {nSigmaAxis, ptKinkDaugAxis}}); + rSigmaMinus.add("hSigmaMinusKinkTofNSigPi", "hSigmaMinusKinkTofNSigPi", {HistType::kTH2F, {nSigmaAxis, ptKinkDaugAxis}}); + rSigmaMinus.add("hSigmaMinusDcaKinkDauToPv", "hSigmaMinusDcaKinkDauToPv", {HistType::kTH2F, {dcaKinkToPvAxis, ptAxis}}); + if (recomputeSigmaMom) { + rSigmaMinus.add("hDeltaPxRecalcSigmaMinus", "hDeltaPxRecalcSigmaMinus;#Delta #it{p}_{x}/p_{x};#it{p}_{x}", {HistType::kTH2F, {{800, -4, 4}, ptAxis}}); + rSigmaMinus.add("hDeltaPyRecalcSigmaMinus", "hDeltaPyRecalcSigmaMinus;#Delta #it{p}_{y}/p_{y};#it{p}_{y}", {HistType::kTH2F, {{800, -4, 4}, ptAxis}}); + rSigmaMinus.add("hDeltaPzRecalcSigmaMinus", "hDeltaPzRecalcSigmaMinus;#Delta #it{p}_{z}/p_{z};#it{p}_{z}", {HistType::kTH2F, {{800, -4, 4}, ptAxis}}); + rSigmaMinus.add("hRecalcPtFactorSigmaMinus", "hRecalcPtFactorSigmaMinus;Rescale factor;|#it{p}|", {HistType::kTH2F, {{250, 0, 5}, ptAxis}}); + } + + // Sigma+ candidate properties + rSigmaPlus.add("h2PtMassSigmaPlusBeforeCuts", "h2PtMassSigmaPlusBeforeCuts", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); + rSigmaPlus.add("h2PtPrKinkNSigBeforeCuts", "h2PtPrKinkNSigBeforeCuts", {HistType::kTH2F, {ptAxis, nSigmaAxis}}); + rSigmaPlus.add("h2dPtMassSigmaPlus", "h2dPtMassSigmaPlus", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); + rSigmaPlus.add("h2dPvContribMassSigmaPlus", "h2dPvContribMassSigmaPlus", {HistType::kTH2F, {pvContribAxis, sigmaMassAxis}}); + rSigmaPlus.add("h2dOccMassSigmaPlus", "h2dOccMassSigmaPlus", {HistType::kTH2F, {occAxis, sigmaMassAxis}}); + rSigmaPlus.add("h2dPvContribPtSigmaPlus", "h2dPvContribPtSigmaPlus", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + rSigmaPlus.add("h2KinkPrPtNSigTofSigmaPlus", "h2KinkPrPtNSigTofSigmaPlus", {HistType::kTH2F, {ptKinkDaugAxis, nSigmaAxis}}); + rSigmaPlus.add("hSigmaPlusArmPod", "hSigmaPlusArmPod", {HistType::kTH2F, {alphaAxis, qtAxis}}); + rSigmaPlus.add("hSigmaPlusRadius", "hSigmaPlusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); + rSigmaPlus.add("hSigmaPlusDcaToPv", "hSigmaPlusDcaToPv", {HistType::kTH2F, {dcaSigmaToPvAxis, ptAxis}}); + rSigmaPlus.add("hSigmaPlusKinkTpcNSigPr", "hSigmaPlusKinkTpcNSigPr", {HistType::kTH2F, {nSigmaAxis, ptKinkDaugAxis}}); + rSigmaPlus.add("hSigmaPlusKinkTofNSigPr", "hSigmaPlusKinkTofNSigPr", {HistType::kTH2F, {nSigmaAxis, ptKinkDaugAxis}}); + rSigmaPlus.add("hSigmaPlusDcaKinkDauToPv", "hSigmaPlusDcaKinkDauToPv", {HistType::kTH2F, {dcaKinkToPvAxis, ptKinkDaugAxis}}); + rSigmaPlus.add("hMassXiMinusSigmaPlus", "hMassXiMinusSigmaPlus", {HistType::kTH2F, {xiMassAxis, sigmaMassAxis}}); + if (recomputeSigmaMom) { + rSigmaPlus.add("hDeltaPxRecalcSigmaPlus", "hDeltaPxRecalcSigmaPlus;#Delta #it{p}_{x}/p_{x};#it{p}_{x}", {HistType::kTH2F, {{800, -4, 4}, ptAxis}}); + rSigmaPlus.add("hDeltaPyRecalcSigmaPlus", "hDeltaPyRecalcSigmaPlus;#Delta #it{p}_{y}/p_{y};#it{p}_{y}", {HistType::kTH2F, {{800, -4, 4}, ptAxis}}); + rSigmaPlus.add("hDeltaPzRecalcSigmaPlus", "hDeltaPzRecalcSigmaPlus;#Delta #it{p}_{z}/p_{z};#it{p}_{z}", {HistType::kTH2F, {{800, -4, 4}, ptAxis}}); + rSigmaPlus.add("hRecalcPtFactorSigmaPlus", "hRecalcPtFactorSigmaPlus;Rescale factor;|#it{p}|", {HistType::kTH2F, {{250, 0, 5}, ptAxis}}); + } + + // Mass QA + rLambda1405.add("h2SigmaMinusMassVsLambdaMass", "h2SigmaMinusMassVsLambdaMass", {HistType::kTH2F, {lambda1405MassAxis, sigmaMassAxis}}); + rLambda1405.add("h2SigmaPlusMassVsLambdaMass", "h2SigmaPlusMassVsLambdaMass", {HistType::kTH2F, {lambda1405MassAxis, sigmaMassAxis}}); + rLambda1405.add("hMassL1405", "hMassL1405", {HistType::kTH2F, {lambda1405MassAxis, ptAxis}}); + rLambda1405.add("hMassXi1530", "hMassXi1530", {HistType::kTH2F, {xi1530MassAxis, ptAxis}}); + // Kinematic distributions + rLambda1405.add("hPx", "hPx;#it{p}_{x};Counts", {HistType::kTH1D, {pCompAxisL1405}}); + rLambda1405.add("hPy", "hPy;#it{p}_{y};Counts", {HistType::kTH1D, {pCompAxisL1405}}); + rLambda1405.add("hPz", "hPz;#it{p}_{z};Counts", {HistType::kTH1D, {pCompAxisL1405}}); + rLambda1405.add("hPt", "hPt", {HistType::kTH1D, {ptAxis}}); + rLambda1405.add("hPhi", "hPhi", {HistType::kTH1D, {{128, -o2::constants::math::PI, o2::constants::math::PI}}}); + // Correlation histograms + rLambda1405.add("hDeltaEta", "hDeltaEta", {HistType::kTH1D, {deltaEtaAxis}}); + rLambda1405.add("hDeltaPhi", "hDeltaPhi", {HistType::kTH1D, {deltaPhiAxis}}); + // Pion daughter properties + rLambda1405.add("hPairedBachPiVsCent", "hPairedBachPiVsCent", {HistType::kTH2F, {centMultAxis, {1000, -0.5, 999.5}}}); + rLambda1405.add("h2BachPiPtNSigTof", "h2BachPiPtNSigTof", {HistType::kTH2F, {ptKinkDaugAxis, nSigmaAxis}}); + rLambda1405.add("h2BachPiPtNSigTpc", "h2BachPiPtNSigTpc", {HistType::kTH2F, {ptKinkDaugAxis, nSigmaAxis}}); + rLambda1405.add("h3BachPiDcaXYVsPt", "h3BachPiDcaXYVsPt", {HistType::kTH3F, {ptKinkDaugAxis, dcaBachPiAxis, centMultAxis}}); + rLambda1405.add("h3BachPiDcaZVsPt", "h3BachPiDcaZVsPt", {HistType::kTH3F, {ptKinkDaugAxis, dcaBachPiAxis, centMultAxis}}); + // Sparse histograms + std::vector axesMass = {axisPoolBin, lambda1405MassAxis, ptAxis, sigmaMassAxis, dcaSigmaToPvAxis, dcaKinkToPvAxis}; + if (doprocessMc) { + axesMass.push_back(pvContribAxis); + } else { + axesMass.push_back(centMultAxis); + } + rLambda1405.add("hSparseL1405", "THn for mass peak", HistType::kTHnSparseF, axesMass); + std::vector axesScalarProd = {axisPoolBin, lambda1405MassAxis, ptAxis, sigmaMassAxis, dcaSigmaToPvAxis, dcaKinkToPvAxis, centMultAxis, scalarProdAxis}; + if (doprocessDataWQVecsSameEvent || doprocessDataWQVecsMixedEvent) { + rLambda1405.add("hSparseL1405ScalProd", "THn for SP", HistType::kTHnSparseF, axesScalarProd); + } + std::vector axesCorrel = {axisPoolBin, lambda1405MassAxis, ptAxis, deltaEtaAxis, deltaPhiAxis}; + if (saveDEtaDPhi) { + rLambda1405.add("hSparseL1405Correl", "THn for 2PC", HistType::kTHnSparseF, axesCorrel); + } + + // Selection QA + rSelections.add("hSelectionsL1405", "hSelectionsL1405", {HistType::kTH1D, {{6, -0.f, 5.5f}}}); + rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(2, "Sigma sel"); + rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(3, "Bach PID sel"); + rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(4, "Upper mass sel"); + rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(5, "#it{p}_{T} Correl"); + rSelections.get(HIST("hSelectionsL1405"))->GetXaxis()->SetBinLabel(6, "Accepted"); + rSelections.add("hSelectionsSigmaPlus", "hSelectionsSigmaPlus", {HistType::kTH1D, {{9, -0.f, 8.5f}}}); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(2, "Kink"); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(3, "Mom recomp."); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(4, "Mass"); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(5, "DCA PV #Sigma"); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(6, "DCA PV #pi #leftarrow #Sigma"); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(7, "#Sigma Radius"); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(8, "#Sigma Qt"); + rSelections.get(HIST("hSelectionsSigmaPlus"))->GetXaxis()->SetBinLabel(9, "Chi2 PCA"); + rSelections.add("hSelectionsSigmaMinus", "hSelectionsSigmaMinus", {HistType::kTH1D, {{9, -0.f, 8.5f}}}); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(2, "Kink"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(3, "Mom recomp."); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(4, "Mass"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(5, "DCA PV #Sigma"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(6, "DCA PV Pi #leftarrow #Sigma"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(7, "#Sigma Radius"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(8, "#Sigma Qt"); + rSelections.get(HIST("hSelectionsSigmaMinus"))->GetXaxis()->SetBinLabel(9, "Chi2 PCA"); + rSelections.add("hRecalcSigmaPlusMom", "hRecalcSigmaPlusMom", {HistType::kTH1D, {{5, -0.f, 4.5f}}}); + rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(2, "Non-null mom."); + rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(3, "Non-null A"); + rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(4, "Positive D"); + rSelections.get(HIST("hRecalcSigmaPlusMom"))->GetXaxis()->SetBinLabel(5, "Real sol."); + rSelections.add("hRecalcSigmaMinusMom", "hRecalcSigmaMinusMom", {HistType::kTH1D, {{5, -0.f, 4.5f}}}); + rSelections.get(HIST("hRecalcSigmaMinusMom"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hRecalcSigmaMinusMom"))->GetXaxis()->SetBinLabel(2, "Non-null mom."); + rSelections.get(HIST("hRecalcSigmaMinusMom"))->GetXaxis()->SetBinLabel(3, "Non-null A"); + rSelections.get(HIST("hRecalcSigmaMinusMom"))->GetXaxis()->SetBinLabel(4, "Positive D"); + rSelections.get(HIST("hRecalcSigmaMinusMom"))->GetXaxis()->SetBinLabel(5, "Real sol."); + rSelections.add("hSelectionsBachPi", "hSelectionsBachPi", {HistType::kTH1D, {{6, -0.f, 5.5f}}}); + rSelections.get(HIST("hSelectionsBachPi"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hSelectionsBachPi"))->GetXaxis()->SetBinLabel(2, "Sign"); + rSelections.get(HIST("hSelectionsBachPi"))->GetXaxis()->SetBinLabel(3, "Tpc N#sigma"); + rSelections.get(HIST("hSelectionsBachPi"))->GetXaxis()->SetBinLabel(4, "Tpc Ncls"); + rSelections.get(HIST("hSelectionsBachPi"))->GetXaxis()->SetBinLabel(5, "#eta"); + rSelections.get(HIST("hSelectionsBachPi"))->GetXaxis()->SetBinLabel(6, "DCA"); + rSelections.add("hSelectionsKinkPi", "hSelectionsKinkPi", {HistType::kTH1D, {{7, -0.f, 6.5f}}}); + rSelections.get(HIST("hSelectionsKinkPi"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hSelectionsKinkPi"))->GetXaxis()->SetBinLabel(2, "Tpc N#sigma"); + rSelections.get(HIST("hSelectionsKinkPi"))->GetXaxis()->SetBinLabel(3, "Tpc Ncls"); + rSelections.get(HIST("hSelectionsKinkPi"))->GetXaxis()->SetBinLabel(4, "#eta"); + rSelections.get(HIST("hSelectionsKinkPi"))->GetXaxis()->SetBinLabel(5, "ITS cls"); + rSelections.get(HIST("hSelectionsKinkPi"))->GetXaxis()->SetBinLabel(6, "has TOF"); + rSelections.get(HIST("hSelectionsKinkPi"))->GetXaxis()->SetBinLabel(7, "N#sigma TOF"); + rSelections.add("hSelectionsKinkPr", "hSelectionsKinkPr", {HistType::kTH1D, {{7, -0.f, 6.5f}}}); + rSelections.get(HIST("hSelectionsKinkPr"))->GetXaxis()->SetBinLabel(1, "All"); + rSelections.get(HIST("hSelectionsKinkPr"))->GetXaxis()->SetBinLabel(2, "Tpc N#sigma"); + rSelections.get(HIST("hSelectionsKinkPr"))->GetXaxis()->SetBinLabel(3, "Tpc Ncls"); + rSelections.get(HIST("hSelectionsKinkPr"))->GetXaxis()->SetBinLabel(4, "#eta"); + rSelections.get(HIST("hSelectionsKinkPr"))->GetXaxis()->SetBinLabel(5, "ITS cls"); + rSelections.get(HIST("hSelectionsKinkPr"))->GetXaxis()->SetBinLabel(6, "has TOF"); + rSelections.get(HIST("hSelectionsKinkPr"))->GetXaxis()->SetBinLabel(7, "N#sigma TOF"); + + if (doprocessDataWQVecsSameEvent || doprocessDataWQVecsMixedEvent) { + rLambda1405.add("hScalarProd", "hScalarProd", {HistType::kTH2F, {scalarProdAxis, ptAxis}}); + std::vector axesFlow = {lambda1405MassAxis, ptAxis, centMultAxis, scalarProdAxis}; + rLambda1405.add("hSparseFlowL1405", "THn for SP", HistType::kTHnSparseF, axesFlow); + } + + // Add MC histograms + if (doprocessMc) { + + rSelections.add("hRecoNotMatchedCounter", "hRecoNotMatchedCounter", {HistType::kTH1D, {{4, -0.5, 3.5f}}}); + rSelections.get(HIST("hRecoNotMatchedCounter"))->GetXaxis()->SetBinLabel(1, "#Lambda(1405)"); + rSelections.get(HIST("hRecoNotMatchedCounter"))->GetXaxis()->SetBinLabel(2, "#Sigma"); + rSelections.get(HIST("hRecoNotMatchedCounter"))->GetXaxis()->SetBinLabel(3, "Kink daug"); + rSelections.get(HIST("hRecoNotMatchedCounter"))->GetXaxis()->SetBinLabel(4, "Bach #pi"); + + rSigmaPlus.add("h2DeltaGenRecoPtSigmaPlus", "h2DeltaGenRecoPtSigmaPlus", {HistType::kTH2F, {{600, -3, 3}, ptAxis}}); + rSigmaPlus.add("h2GenSigmaPlusPvContribPt", "h2GenSigmaPlusPvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + rSigmaPlus.add("hGenSigmaPlusRadius", "hGenSigmaPlusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); + rSigmaMinus.add("hGenSigmaMinusRadius", "hGenSigmaMinusRadius", {HistType::kTH2F, {sigmaRadiusAxis, ptAxis}}); + rSigmaMinus.add("h2DeltaGenRecoPtSigmaMinus", "h2DeltaGenRecoPtSigmaMinus", {HistType::kTH2F, {{600, -3, 3}, ptAxis}}); + rSigmaMinus.add("h2GenSigmaMinusPvContribPt", "h2GenSigmaMinusPvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + + rLambda1405.add("hQARecoL1405", "hQARecoL1405;;Counts", {HistType::kTH2F, {{4, -0.5, 3.5}, ptAxis}}); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(1, "Reco #Lambda(1405)"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(2, "Has bach #pi"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(3, "Has mothers"); + rLambda1405.get(HIST("hQARecoL1405"))->GetXaxis()->SetBinLabel(4, "Matched"); + rLambda1405.add("hGenL1405", "hGenL1405;;Counts", {HistType::kTH2F, {{3, -0.5, 2.5}, ptAxis}}); + rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(1, "#Lambda(1405) #rightarrow #Sigma^{-} #pi^{+} #rightarrow n #pi^{-} #pi^{+}"); + rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(2, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow n #pi^{+} #pi^{-}"); + rLambda1405.get(HIST("hGenL1405"))->GetXaxis()->SetBinLabel(3, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow p #pi^{0} #pi^{-}"); + rLambda1405.add("hRecoL1405", "hRecoL1405;;Counts", {HistType::kTH2F, {{3, -0.5, 2.5}, ptAxis}}); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(1, "#Lambda(1405) #rightarrow #Sigma^{-} #pi^{+} #rightarrow n #pi^{-} #pi^{+}"); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(2, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow n #pi^{+} #pi^{-}"); + rLambda1405.get(HIST("hRecoL1405"))->GetXaxis()->SetBinLabel(3, "#Lambda(1405) #rightarrow #Sigma^{+} #pi^{-} #rightarrow p #pi^{0} #pi^{-}"); + rLambda1405.add("h2GenL1405PvContribPt", "h2GenL1405PvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + rLambda1405.add("h2RecL1405PvContribPt", "h2RecL1405PvContribPt", {HistType::kTH2F, {pvContribAxis, ptAxis}}); + rLambda1405.add("h2GenSigmaMinusArmPod", "h2GenSigmaMinusArmPod", {HistType::kTH2F, {alphaAxis, qtAxis}}); + rLambda1405.add("h2GenSigmaPlusArmPod", "h2GenSigmaPlusArmPod", {HistType::kTH2F, {alphaAxis, qtAxis}}); + rLambda1405.add("h2GenPtVsBachPtSigmaMinus", "h2GenPtVsBachPtSigmaMinus;#Lambda(1405) #it{p}_{T} (GeV/c); Bach #pi #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenPtVsBachPtSigmaPlusToPi", "h2GenPtVsBachPtSigmaPlusToPi;#Lambda(1405) #it{p}_{T} (GeV/c); Bach #pi #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenPtVsBachPtSigmaPlusToPr", "h2GenPtVsBachPtSigmaPlusToPr;#Lambda(1405) #it{p}_{T} (GeV/c); Bach #pi #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenPtVsSigmaMinusPt", "h2GenPtVsBachPtSigmaMinus;#Lambda(1405) #it{p}_{T} (GeV/c); #Sigma #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenPtVsSigmaPlusToPiPt", "h2GenPtVsBachPtSigmaPlusToPi;#Lambda(1405) #it{p}_{T} (GeV/c); #Sigma #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenPtVsSigmaPlusToPrPt", "h2GenPtVsBachPtSigmaPlusToPr;#Lambda(1405) #it{p}_{T} (GeV/c); #Sigma #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenSigmaPtVsKinkPtSigmaMinus", "h2GenSigmaPtVsKinkPtSigmaMinus;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenSigmaPtVsPiKinkPt", "h2GenSigmaPtVsPiKinkPt;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2GenSigmaPtVsPrKinkPt", "h2GenSigmaPtVsPrKinkPt;#Sigma #it{p}_{T} (GeV/c); Kink #it{p}_{T} (GeV/c)", {HistType::kTH2F, {ptAxis, ptAxis}}); + rLambda1405.add("h2MassResoFromSigmaMinus", "h2MassResoFromSigmaMinus", {HistType::kTH2F, {lambda1405MassAxis, massResoAxis}}); + rLambda1405.add("h2PtResoFromSigmaMinus", "h2PtResoFromSigmaMinus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); + rLambda1405.add("h2MassResoFromSigmaPlus", "h2MassResoFromSigmaPlus", {HistType::kTH2F, {lambda1405MassAxis, massResoAxis}}); + rLambda1405.add("h2PtResoFromSigmaPlus", "h2PtResoFromSigmaPlus", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); + rLambda1405.add("h2PtMassMC", "h2PtMassMC", {HistType::kTH2F, {ptAxis, lambda1405MassAxis}}); + } + + if (doprocessMcSigmasCentSel) { + rSigmaPlus.add("hSparseGenFromRecoSigmaPlus", "THn for generated Sigma plus", {HistType::kTHnSparseF, {sigmaMassAxis, ptAxis, alphaAxis, qtAxis, sigmaRadiusAxis, centMultAxis, occAxis, pvContribAxis}}); + rSigmaPlus.add("hSparseGenSigmaPlus", "THn for generated Sigma plus", {HistType::kTHnSparseF, {sigmaMassAxis, ptAxis, alphaAxis, qtAxis, sigmaRadiusAxis, centMultAxis, occAxis, pvContribAxis}}); + rSigmaMinus.add("hSparseGenFromRecoSigmaMinus", "THn for generated Sigma minus", {HistType::kTHnSparseF, {sigmaMassAxis, ptAxis, alphaAxis, qtAxis, sigmaRadiusAxis, centMultAxis, occAxis, pvContribAxis}}); + rSigmaMinus.add("hSparseGenSigmaMinus", "THn for generated Sigma minus", {HistType::kTHnSparseF, {sigmaMassAxis, ptAxis, alphaAxis, qtAxis, sigmaRadiusAxis, centMultAxis, occAxis, pvContribAxis}}); + } - // lambda1405 to sigmaminus - rLambda1405.add("h2PtMass_0", "h2PtMass_0", {HistType::kTH2F, {ptAxis, massAxis}}); - rLambda1405.add("h2PtMassSigmaBeforeCuts_0", "h2PtMassSigmaBeforeCuts_0", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); - rLambda1405.add("h2PtMassSigma_0", "h2PtMassSigma_0", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); - rLambda1405.add("h2SigmaMassVsMass_0", "h2SigmaMassVsMass_0", {HistType::kTH2F, {massAxis, sigmaMassAxis}}); - rLambda1405.add("h2PtPiNSigma_0", "h2PtPiNSigma_0", {HistType::kTH2F, {ptPiAxis, nSigmaPiAxis}}); - rLambda1405.add("h2PtPiNSigmaTOF_0", "h2PtPiNSigmaTOF_0", {HistType::kTH2F, {ptPiAxis, nSigmaPiAxis}}); - // lambda1405 to sigmaplus - rLambda1405.add("h2PtMass_1", "h2PtMass_1", {HistType::kTH2F, {ptAxis, massAxis}}); - rLambda1405.add("h2PtMassSigmaBeforeCuts_1", "h2PtMassSigmaBeforeCuts_1", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); - rLambda1405.add("h2PtMassSigma_1", "h2PtMassSigma_1", {HistType::kTH2F, {ptAxis, sigmaMassAxis}}); - rLambda1405.add("h2SigmaMassVsMass_1", "h2SigmaMassVsMass_1", {HistType::kTH2F, {massAxis, sigmaMassAxis}}); - rLambda1405.add("h2PtPiNSigma_1", "h2PtPiNSigma_1", {HistType::kTH2F, {ptPiAxis, nSigmaPiAxis}}); - rLambda1405.add("h2PtPiNSigmaTOF_1", "h2PtPiNSigmaTOF_1", {HistType::kTH2F, {ptPiAxis, nSigmaPiAxis}}); - - if (doprocessMC) { - // Add MC histograms if needed, to sigmaminus - rLambda1405.add("h2MassResolution_0", "h2MassResolution_0", {HistType::kTH2F, {massAxis, massResolutionAxis}}); - rLambda1405.add("h2PtResolution_0", "h2PtResolution_0", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); - rLambda1405.add("h2PtMassMC_0", "h2PtMassMC_0", {HistType::kTH2F, {ptAxis, massAxis}}); - // Add MC histograms if needed, to sigmaplus - rLambda1405.add("h2MassResolution_1", "h2MassResolution_1", {HistType::kTH2F, {massAxis, massResolutionAxis}}); - rLambda1405.add("h2PtResolution_1", "h2PtResolution_1", {HistType::kTH2F, {ptAxis, ptResolutionAxis}}); - rLambda1405.add("h2PtMassMC_1", "h2PtMassMC_1", {HistType::kTH2F, {ptAxis, massAxis}}); + // Functional selections + funcMinQtAlphaAP = TF1("funcMinQtAlphaAP", Form("%s", selections.cutSigmaQtAPMin.value.data()), -1, 1); + LOGF(info, "funcMinQtAlphaAP: %s", Form("%s", selections.cutSigmaQtAPMin.value.data())); + funcMaxQtAlphaAP = TF1("funcMaxQtAlphaAP", Form("%s", selections.cutSigmaQtAPMax.value.data()), -1, 1); + LOGF(info, "funcMaxQtAlphaAP: %s", Form("%s", selections.cutSigmaQtAPMax.value.data())); + funcMinBachPiPtVsL1405Pt = TF1("funcMinBachPiPtVsL1405Pt", Form("%s", selections.cutMinBachPiPtVsL1405Pt.value.data()), 0., 100); + LOGF(info, "funcMinBachPiPtVsL1405Pt: %s", Form("%s", selections.cutMinBachPiPtVsL1405Pt.value.data())); + funcMaxBachPiPtVsL1405Pt = TF1("funcMaxBachPiPtVsL1405Pt", Form("%s", selections.cutMaxBachPiPtVsL1405Pt.value.data()), 0., 100); + LOGF(info, "funcMaxBachPiPtVsL1405Pt: %s", Form("%s", selections.cutMaxBachPiPtVsL1405Pt.value.data())); + funcMinSigmaPtVsL1405Pt = TF1("funcMinSigmaPtVsL1405Pt", Form("%s", selections.cutMinSigmaPtVsL1405Pt.value.data()), 0., 100); + LOGF(info, "funcMinSigmaPtVsL1405Pt: %s", Form("%s", selections.cutMinSigmaPtVsL1405Pt.value.data())); + funcMaxSigmaPtVsL1405Pt = TF1("funcMaxSigmaPtVsL1405Pt", Form("%s", selections.cutMaxSigmaPtVsL1405Pt.value.data()), 0., 100); + LOGF(info, "funcMaxSigmaPtVsL1405Pt: %s", Form("%s", selections.cutMaxSigmaPtVsL1405Pt.value.data())); + funcDcaXYPtCutBachPi = TF1("funcDcaXYPtCutBachPi", Form("[0]*%s", selections.dcaXYPtBachFunc.value.data()), 0.001, 100); + funcDcaXYPtCutBachPi.SetParameter(0, selections.dcaXYBachNSigmaMax); + LOGF(info, "DCAxy pt-dependence function: %s", Form("[0]*%s", selections.dcaXYPtBachFunc.value.data())); + + rSelections.print(); + rSigmaMinus.print(); + rSigmaPlus.print(); + rLambda1405.print(); + + // Info for DCA propagation of bachelor tracks + ccdb->setURL(ccdbPath); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + } + + /// Get the binning pool associated to the collision + /// \param collision is the collision + /// \param binPolicy is the binning policy for the correlation + template + int getPoolBin(const TColl& collision, const TBinningType& binPolicy) + { + int poolBin{0}; + float centMult = getCentMult(collision); + poolBin = binPolicy.getBin(std::make_tuple(centMult, collision.posZ())); + if constexpr (std::is_same_v) { + rEventSelection.fill(HIST("hCentPoolBin"), centMult, poolBin); + } else if constexpr (std::is_same_v) { + rEventSelection.fill(HIST("hMultPoolBin"), centMult, poolBin); + } + rEventSelection.fill(HIST("hZVtxPoolBin"), collision.posZ(), poolBin); + rEventSelection.fill(HIST("hPoolBins"), poolBin); + return poolBin; + } + + template + float getCentMult(const TCollision& collision) + { + if (useCentrality) { + return collision.centFT0C(); + } else { + return collision.multFT0M(); } } @@ -164,150 +560,566 @@ struct lambda1405analysis { return std::sqrt(p2A - dp * dp / p2V0); } - template - bool selectPiTrack(const Ttrack& candidate, bool piFromSigma) + float recalcSigmaMom(bool& success, bool isSigmaMinus, float sigmaPx, float sigmaPy, float sigmaPz, float sigmaDauPx, float sigmaDauPy, float sigmaDauPz) { - if (std::abs(candidate.tpcNSigmaPi()) > cutNSigmaTPC || candidate.tpcNClsFound() < cutNTPCClusPi || std::abs(candidate.eta()) > cutEtaDaught) { - return false; + success = false; + if (isSigmaMinus) { + rSelections.fill(HIST("hRecalcSigmaMinusMom"), 0); // All + } else { + rSelections.fill(HIST("hRecalcSigmaPlusMom"), 0); // All + } + // Sigma- -> n + pi- (charged daughter = pion, neutral daughter = neutron) + // Sigma+ -> p + pi0 (charged daughter = proton, neutral daughter = pi0) + double massChargedDau = isSigmaMinus ? o2::constants::physics::MassPionCharged : o2::constants::physics::MassProton; + double massNeutralDau = isSigmaMinus ? o2::constants::physics::MassNeutron : o2::constants::physics::MassPionNeutral; + double massSigma = isSigmaMinus ? o2::constants::physics::MassSigmaMinus : o2::constants::physics::MassSigmaPlus; + + double pMother = std::sqrt(sigmaPx * sigmaPx + sigmaPy * sigmaPy + sigmaPz * sigmaPz); + float zeroValTolerance{1e-12f}; + if (pMother < zeroValTolerance) { + LOG(debug) << "Recalculation of Sigma momentum failed: mother momentum is zero " << sigmaPx << ", " << sigmaPy << ", " << sigmaPz; + return -999.f; + } + if (isSigmaMinus) { + rSelections.fill(HIST("hRecalcSigmaMinusMom"), 1); // Non-zero momentum + } else { + rSelections.fill(HIST("hRecalcSigmaPlusMom"), 1); // Non-zero momentum + } + + double versorX = sigmaPx / pMother; + double versorY = sigmaPy / pMother; + double versorZ = sigmaPz / pMother; + double eChDau = std::sqrt(massChargedDau * massChargedDau + sigmaDauPx * sigmaDauPx + sigmaDauPy * sigmaDauPy + sigmaDauPz * sigmaDauPz); + double a = versorX * sigmaDauPx + versorY * sigmaDauPy + versorZ * sigmaDauPz; + double K = massSigma * massSigma + massChargedDau * massChargedDau - massNeutralDau * massNeutralDau; + double A = 4.0 * (eChDau * eChDau - a * a); + double B = -4.0 * a * K; + double C = 4.0 * eChDau * eChDau * massSigma * massSigma - K * K; + + if (std::abs(A) < zeroValTolerance) { + LOG(debug) << "Recalculation of Sigma momentum failed: A is zero " << sigmaPx << ", " << sigmaPy << ", " << sigmaPz << ", A = " << A << ", B = " << B << ", C = " << C; + return -999.f; + } + if (isSigmaMinus) { + rSelections.fill(HIST("hRecalcSigmaMinusMom"), 2); // Non-zero A + } else { + rSelections.fill(HIST("hRecalcSigmaPlusMom"), 2); // Non-zero A + } + + double det = B * B - 4.0 * A * C; + if (det < 0.0) { + LOG(debug) << "Recalculation of Sigma momentum failed: determinant is negative " << sigmaPx << ", " << sigmaPy << ", " << sigmaPz << ", A = " << A << ", B = " << B << ", C = " << C << ", determinant = " << det; + return -999.f; + } + if (isSigmaMinus) { + rSelections.fill(HIST("hRecalcSigmaMinusMom"), 3); // Positive determinant + } else { + rSelections.fill(HIST("hRecalcSigmaPlusMom"), 3); // Positive determinant + } + + double sqrtDet = std::sqrt(det); + double p1 = (-B + sqrtDet) / (2.0 * A); + double p2 = (-B - sqrtDet) / (2.0 * A); + if (p2 < 0.0 && p1 < 0.0) { + LOG(debug) << "Recalculation of Sigma momentum failed: both solutions are negative " << sigmaPx << ", " << sigmaPy << ", " << sigmaPz << ", p1: " << p1 << ", p2: " << p2; + return -999.f; + } + if (isSigmaMinus) { + rSelections.fill(HIST("hRecalcSigmaMinusMom"), 4); // Real solutions + } else { + rSelections.fill(HIST("hRecalcSigmaPlusMom"), 4); // Real solutions + } + + success = true; + if (p2 < 0.0) { + return static_cast(p1); } - if (piFromSigma) { - return true; + if (p1 < 0.0) { + return static_cast(p2); } + double p1Diff = std::abs(p1 - pMother); + double p2Diff = std::abs(p2 - pMother); + return static_cast((p1Diff < p2Diff) ? p1 : p2); + } - if (candidate.itsNCls() < cutNITSClusPi) { + template + bool selectPiBach(const TTrack& candidate, float centMult) + { + if (std::abs(candidate.tpcNSigmaPi()) > selections.cutNSigTpc) { return false; } + rSelections.fill(HIST("hSelectionsBachPi"), 2); // Nsigma Tpc - if (useTOF && !candidate.hasTOF()) { + if (candidate.tpcNClsFound() < selections.cutNTpcClus) { return false; } + rSelections.fill(HIST("hSelectionsBachPi"), 3); // Tpc clusters - if (useTOF && std::abs(candidate.tofNSigmaPi()) > cutNSigmaTOF) { + if (std::abs(candidate.eta()) > selections.cutEtaDaughter) { return false; } + rSelections.fill(HIST("hSelectionsBachPi"), 4); // Eta selection - return true; // Track is selected + std::array dcaInfoBachPi{candidate.dcaXY(), candidate.dcaZ()}; + rLambda1405.fill(HIST("h3BachPiDcaXYVsPt"), candidate.pt(), dcaInfoBachPi[0], centMult); + rLambda1405.fill(HIST("h3BachPiDcaZVsPt"), candidate.pt(), dcaInfoBachPi[1], centMult); + if (std::abs(dcaInfoBachPi[0]) > funcDcaXYPtCutBachPi.Eval(candidate.pt()) || + std::abs(dcaInfoBachPi[1]) > selections.cutMaxDcaZBach) { + return false; + } + rSelections.fill(HIST("hSelectionsBachPi"), 5); // DCA selection + + return true; } - template - bool selectProTrack(const Ttrack& candidate, bool prFromSigma) + template + bool selectPiKink(const TTrack& candidate) { - if (std::abs(candidate.tpcNSigmaPr()) > cutNSigmaTPC || candidate.tpcNClsFound() < cutNTPCClusPi || std::abs(candidate.eta()) > cutEtaDaught) { + rSelections.fill(HIST("hSelectionsKinkPi"), 0); // All pion kink candidates + + if (std::abs(candidate.tpcNSigmaPi()) > selections.cutNSigTpc) { return false; } - if (prFromSigma) { - return true; + rSelections.fill(HIST("hSelectionsKinkPi"), 1); // Nsigma Tpc + + if (candidate.tpcNClsFound() < selections.cutNTpcClus) { + return false; + } + rSelections.fill(HIST("hSelectionsKinkPi"), 2); // Tpc clusters + + if (std::abs(candidate.eta()) > selections.cutEtaDaughter) { + return false; } - if (candidate.itsNCls() < cutNITSClusPi) { + rSelections.fill(HIST("hSelectionsKinkPi"), 3); // Eta selection + + if (candidate.itsNCls() < selections.cutItsNClusKinkMin || candidate.itsNCls() > selections.cutItsNClusKinkMax) { return false; } - if (useTOF && !candidate.hasTOF()) { + rSelections.fill(HIST("hSelectionsKinkPi"), 4); // ITS clusters + + if (useTof && !candidate.hasTOF()) { return false; } - if (useTOF && std::abs(candidate.tofNSigmaPr()) > cutNSigmaTOF) { + rSelections.fill(HIST("hSelectionsKinkPi"), 5); // has Tof + + if (useTof && std::abs(candidate.tofNSigmaPi()) > selections.cutNSigTof) { return false; } + rSelections.fill(HIST("hSelectionsKinkPi"), 6); // Nsigma Tof + return true; // Track is selected } - bool selectCandidate(aod::KinkCands::iterator const& sigmaCand, TracksFull const& tracks) + template + bool selectPrKink(const TTrack& candidate) { - auto kinkDauTrack = sigmaCand.trackDaug_as(); - bool isPiKink = selectPiTrack(kinkDauTrack, true); - bool isProKink = selectProTrack(kinkDauTrack, true); - if (!isPiKink && !isProKink) { + rSelections.fill(HIST("hSelectionsKinkPr"), 0); // All proton kink candidates + + if (std::abs(candidate.tpcNSigmaPr()) > selections.cutNSigTpc) { return false; } + rSelections.fill(HIST("hSelectionsKinkPr"), 1); // Nsigma Tpc - if (isPiKink) { - rLambda1405.fill(HIST("h2PtMassSigmaBeforeCuts_0"), sigmaCand.mothSign() * sigmaCand.ptMoth(), sigmaCand.mSigmaMinus()); - rLambda1405.fill(HIST("h2PtPiNSigma_0"), sigmaCand.mothSign() * kinkDauTrack.pt(), kinkDauTrack.tpcNSigmaPi()); + if (candidate.tpcNClsFound() < selections.cutNTpcClus) { + return false; } - if (isProKink) { - rLambda1405.fill(HIST("h2PtMassSigmaBeforeCuts_1"), sigmaCand.mothSign() * sigmaCand.ptMoth(), sigmaCand.mSigmaPlus()); - rLambda1405.fill(HIST("h2PtPiNSigma_1"), sigmaCand.mothSign() * kinkDauTrack.pt(), kinkDauTrack.tpcNSigmaPr()); + rSelections.fill(HIST("hSelectionsKinkPr"), 2); // Tpc clusters + + if (std::abs(candidate.eta()) > selections.cutEtaDaughter) { + return false; } + rSelections.fill(HIST("hSelectionsKinkPr"), 3); // eta selection - lambda1405Cand.isSigmaPlus = isProKink && (sigmaCand.mSigmaPlus() > o2::constants::physics::MassSigmaPlus - cutSigmaMass && sigmaCand.mSigmaPlus() < o2::constants::physics::MassSigmaPlus + cutSigmaMass); - lambda1405Cand.isSigmaMinus = isPiKink && (sigmaCand.mSigmaMinus() > o2::constants::physics::MassSigmaMinus - cutSigmaMass && sigmaCand.mSigmaMinus() < o2::constants::physics::MassSigmaMinus + cutSigmaMass); - if (!lambda1405Cand.isSigmaPlus && !lambda1405Cand.isSigmaMinus) { + if (candidate.itsNCls() < selections.cutItsNClusKinkMin || candidate.itsNCls() > selections.cutItsNClusKinkMax) { return false; } - float sigmaRad = std::hypot(sigmaCand.xDecVtx(), sigmaCand.yDecVtx()); - if (std::abs(sigmaCand.dcaMothPv()) > cutDCAtoPVSigma || std::abs(sigmaCand.dcaDaugPv()) < cutDCAtoPVPiFromSigma || sigmaRad < cutSigmaRadius) { + rSelections.fill(HIST("hSelectionsKinkPr"), 4); // ITS clusters + + if (useTof && !candidate.hasTOF()) { return false; } + rSelections.fill(HIST("hSelectionsKinkPr"), 5); // has Tof - for (const auto& piTrack : tracks) { - if (!doLSBkg) { - if (piTrack.sign() == sigmaCand.mothSign()) { - continue; + if (useTof && std::abs(candidate.tofNSigmaPr()) > selections.cutNSigTof) { + return false; + } + rSelections.fill(HIST("hSelectionsKinkPr"), 6); // Nsigma Tof + + return true; // Track is selected + } + + template + void fillHistosSigma(const lambda1405candidate& lambda1405Cand, const TCand& sigmaCand, const TTrack& kinkDauTrack) + { + + if (lambda1405Cand.isSigmaPlus) { + rSigmaPlus.fill(HIST("h2dPtMassSigmaPlus"), sigmaCand.ptMoth(), sigmaCand.mSigmaPlus()); + rSigmaPlus.fill(HIST("h2dPvContribMassSigmaPlus"), lambda1405Cand.pvContrib, sigmaCand.mSigmaPlus()); + rSigmaPlus.fill(HIST("h2dOccMassSigmaPlus"), lambda1405Cand.occupancy, sigmaCand.mSigmaPlus()); + rSigmaPlus.fill(HIST("h2dPvContribPtSigmaPlus"), lambda1405Cand.pvContrib, sigmaCand.ptMoth()); + rSigmaPlus.fill(HIST("hMassXiMinusSigmaPlus"), sigmaCand.mXiMinus(), sigmaCand.mSigmaPlus()); + rSigmaPlus.fill(HIST("hSigmaPlusArmPod"), lambda1405Cand.sigmaAlphaAP, lambda1405Cand.sigmaQtAP); + rSigmaPlus.fill(HIST("hSigmaPlusRadius"), lambda1405Cand.sigmaRadius, sigmaCand.ptMoth()); + rSigmaPlus.fill(HIST("hSigmaPlusDcaToPv"), sigmaCand.dcaMothPv(), sigmaCand.ptMoth()); + rSigmaPlus.fill(HIST("hSigmaPlusDcaKinkDauToPv"), sigmaCand.dcaDaugPv(), sigmaCand.ptMoth()); + rSigmaPlus.fill(HIST("h2KinkPrPtNSigTofSigmaPlus"), lambda1405Cand.kinkPt, lambda1405Cand.kinkPrNSigTof); + // Fill QA histos for kink daughter + rSigmaPlus.fill(HIST("hSigmaPlusKinkTpcNSigPr"), kinkDauTrack.tpcNSigmaPr(), kinkDauTrack.pt()); + rSigmaPlus.fill(HIST("hSigmaPlusKinkTofNSigPr"), kinkDauTrack.tofNSigmaPr(), kinkDauTrack.pt()); + } else { + rSigmaMinus.fill(HIST("h2dPtMassSigmaMinus"), sigmaCand.ptMoth(), sigmaCand.mSigmaMinus()); + rSigmaMinus.fill(HIST("h2dPvContribMassSigmaMinus"), lambda1405Cand.pvContrib, sigmaCand.mSigmaMinus()); + rSigmaMinus.fill(HIST("h2dOccMassSigmaMinus"), lambda1405Cand.occupancy, sigmaCand.mSigmaMinus()); + rSigmaMinus.fill(HIST("h2dPvContribPtSigmaMinus"), lambda1405Cand.pvContrib, sigmaCand.ptMoth()); + rSigmaMinus.fill(HIST("hMassXiMinusSigmaMinus"), sigmaCand.mXiMinus(), sigmaCand.mSigmaMinus()); + rSigmaMinus.fill(HIST("hSigmaMinusArmPod"), lambda1405Cand.sigmaAlphaAP, lambda1405Cand.sigmaQtAP); + rSigmaMinus.fill(HIST("hSigmaMinusRadius"), lambda1405Cand.sigmaRadius, sigmaCand.ptMoth()); + rSigmaMinus.fill(HIST("hSigmaMinusDcaToPv"), sigmaCand.dcaMothPv(), sigmaCand.ptMoth()); + rSigmaMinus.fill(HIST("hSigmaMinusDcaKinkDauToPv"), sigmaCand.dcaDaugPv(), sigmaCand.ptMoth()); + rSigmaMinus.fill(HIST("h2KinkPiPtNSigTofSigmaMinus"), lambda1405Cand.kinkPt, lambda1405Cand.kinkPiNSigTof); + // Fill QA histos for kink daughter + rSigmaMinus.fill(HIST("hSigmaMinusKinkTpcNSigPi"), kinkDauTrack.tpcNSigmaPi(), kinkDauTrack.pt()); + rSigmaMinus.fill(HIST("hSigmaMinusKinkTofNSigPi"), kinkDauTrack.tofNSigmaPi(), kinkDauTrack.pt()); + } + } + + template + void fillHistosLambda1405(const lambda1405candidate& cand, const TTrack& trks) + { + + // Fill QA histos for Lambda(1405) candidate + rLambda1405.fill(HIST("hMassL1405"), cand.massL1405, cand.pt()); + rLambda1405.fill(HIST("hMassXi1530"), cand.massXi1530, cand.pt()); + rLambda1405.fill(HIST("hPx"), cand.px); + rLambda1405.fill(HIST("hPy"), cand.py); + rLambda1405.fill(HIST("hPz"), cand.pz); + rLambda1405.fill(HIST("hPt"), cand.pt()); + rLambda1405.fill(HIST("hPhi"), cand.phi); + + // Bachelor Pi + rLambda1405.fill(HIST("h2BachPiPtNSigTpc"), cand.bachPiPt, cand.bachPiNSigTpc); + rLambda1405.fill(HIST("h2BachPiPtNSigTof"), cand.bachPiPt, cand.bachPiNSigTof); + + auto hSparseMass = rLambda1405.get(HIST("hSparseL1405")); + std::vector sparseMassEntry = {static_cast(cand.poolBin), cand.massL1405, cand.pt(), cand.sigmaMinusMass, cand.dcaSigmaToPv, cand.kinkDcaDauToPv}; + if constexpr (IsMC) { + sparseMassEntry.push_back(cand.pvContrib); + } else { + sparseMassEntry.push_back(cand.centMult); + } + hSparseMass->Fill(sparseMassEntry.data()); + if constexpr (FillQVectors) { + std::vector sparseScalProdEntry = {static_cast(cand.poolBin), cand.massL1405, cand.pt(), cand.sigmaMinusMass, cand.dcaSigmaToPv, cand.kinkDcaDauToPv}; + sparseScalProdEntry.push_back(cand.centMult); + sparseScalProdEntry.push_back(cand.scalarProd); + auto hSparseScalProd = rLambda1405.get(HIST("hSparseL1405ScalProd")); + hSparseScalProd->Fill(sparseScalProdEntry.data()); + } + if constexpr (FillCorrelations) { + std::vector sparseCorrelEntry = {static_cast(cand.poolBin), cand.massL1405, cand.pt()}; + auto hSparseCorrel = rLambda1405.get(HIST("hSparseL1405Correl")); + sparseCorrelEntry.push_back(0.0); // Δη + sparseCorrelEntry.push_back(0.0); // Δφ + const auto deltaEtaIdx = sparseCorrelEntry.size() - 2; + const auto deltaPhiIdx = sparseCorrelEntry.size() - 1; + + for (const auto& trk : trks) { + if (trk.globalIndex() == cand.bachPiId || trk.globalIndex() == cand.kinkDauId) { + continue; // Skip the bachelor pi and kink daughter } - } else { - if (piTrack.sign() != sigmaCand.mothSign()) { + if (trk.pt() < assTrkMinPt || trk.pt() > assTrkMaxPt) { + continue; // Skip tracks outside the pT range + } + double deltaPhi = RecoDecay::constrainAngle(trk.phi() - cand.phi, -o2::constants::math::PIHalf); + double deltaEta = trk.eta() - cand.eta; + if (std::abs(deltaEta) < minDEta) { continue; } + sparseCorrelEntry[deltaEtaIdx] = deltaEta; + sparseCorrelEntry[deltaPhiIdx] = deltaPhi; + rLambda1405.fill(HIST("hDeltaEta"), deltaEta); + rLambda1405.fill(HIST("hDeltaPhi"), deltaPhi); + hSparseCorrel->Fill(sparseCorrelEntry.data()); + } + } + } + + template + void constructCollCandidates(const TColl& collision, + aod::KinkCands::iterator const& sigmaCand, + TracksFull const& tracks, + std::vector& selectedCandidates, + const TBinningType& binPolicy) + { + + lambda1405candidate lambda1405Cand{}; + + if constexpr (!IsMc) { + // Set pool bin + int const poolBin = getPoolBin(collision, binPolicy); + lambda1405Cand.poolBin = poolBin; + } + + rSelections.fill(HIST("hSelectionsL1405"), 0); // All candidates + rSelections.fill(HIST("hSelectionsSigmaMinus"), 0); // All Sigma- candidates + rSelections.fill(HIST("hSelectionsSigmaPlus"), 0); // All Sigma+ candidates + + auto kinkDauTrack = sigmaCand.template trackDaug_as(); + bool isPiKink = selectPiKink(kinkDauTrack); + bool isPrKink = selectPrKink(kinkDauTrack); + if (!isPiKink && !isPrKink) { + return; + } + + if (isPiKink) { // Dominated by Sigma-, Sigma+ treated as contamination + lambda1405Cand.isSigmaMinus = true; + lambda1405Cand.isSigmaPlus = false; + rSelections.fill(HIST("hSelectionsSigmaMinus"), 1); // Passed kink sel + } else { // Only Sigma+ can have a proton as kink daughter + lambda1405Cand.isSigmaMinus = false; + lambda1405Cand.isSigmaPlus = true; + rSelections.fill(HIST("hSelectionsSigmaPlus"), 1); // Passed kink sel + } + + auto sigmaMom = std::array{sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth()}; + auto sigmaPt = std::sqrt(sigmaMom[0] * sigmaMom[0] + sigmaMom[1] * sigmaMom[1]); + if (recomputeSigmaMom) { + bool success{false}; + float sigmaPRecalc = recalcSigmaMom(success, lambda1405Cand.isSigmaMinus, + sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth(), + sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()); + if (!success && skipSigmasFailedRecompMom) { + return; } + if (success) { + float sigmaPOriginal = std::sqrt(sigmaCand.pxMoth() * sigmaCand.pxMoth() + + sigmaCand.pyMoth() * sigmaCand.pyMoth() + + sigmaCand.pzMoth() * sigmaCand.pzMoth()); + if (sigmaPRecalc > 0.f && sigmaPOriginal > 0.f) { + float scale = sigmaPRecalc / sigmaPOriginal; + sigmaMom[0] *= scale; + sigmaMom[1] *= scale; + sigmaMom[2] *= scale; + + sigmaPt = std::sqrt(sigmaMom[0] * sigmaMom[0] + sigmaMom[1] * sigmaMom[1]); + if (lambda1405Cand.isSigmaMinus) { + rSigmaMinus.fill(HIST("hDeltaPxRecalcSigmaMinus"), sigmaMom[0] - sigmaCand.pxMoth(), sigmaCand.pxMoth()); + rSigmaMinus.fill(HIST("hDeltaPyRecalcSigmaMinus"), sigmaMom[1] - sigmaCand.pyMoth(), sigmaCand.pyMoth()); + rSigmaMinus.fill(HIST("hDeltaPzRecalcSigmaMinus"), sigmaMom[2] - sigmaCand.pzMoth(), sigmaCand.pzMoth()); + rSigmaMinus.fill(HIST("hRecalcPtFactorSigmaMinus"), scale, sigmaPOriginal); + } else { + rSigmaPlus.fill(HIST("hDeltaPxRecalcSigmaPlus"), sigmaMom[0] - sigmaCand.pxMoth(), sigmaCand.pxMoth()); + rSigmaPlus.fill(HIST("hDeltaPyRecalcSigmaPlus"), sigmaMom[1] - sigmaCand.pyMoth(), sigmaCand.pyMoth()); + rSigmaPlus.fill(HIST("hDeltaPzRecalcSigmaPlus"), sigmaMom[2] - sigmaCand.pzMoth(), sigmaCand.pzMoth()); + rSigmaPlus.fill(HIST("hRecalcPtFactorSigmaPlus"), scale, sigmaPOriginal); + } + } + } + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 2); // Passed mass sel + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 2); // Passed mass sel + } + + // Sigma- or AntiSigma+ candidates + if (lambda1405Cand.isSigmaMinus) { + rSigmaMinus.fill(HIST("h2PtMassSigmaMinusBeforeCuts"), sigmaPt, sigmaCand.mSigmaMinus()); + rSigmaMinus.fill(HIST("h2PtPiKinkNSigBeforeCutsSigmaMinus"), kinkDauTrack.pt(), kinkDauTrack.tpcNSigmaPi()); + } + if (lambda1405Cand.isSigmaPlus) { + rSigmaPlus.fill(HIST("h2PtMassSigmaPlusBeforeCuts"), sigmaPt, sigmaCand.mSigmaPlus()); + rSigmaPlus.fill(HIST("h2PtPrKinkNSigBeforeCuts"), kinkDauTrack.pt(), kinkDauTrack.tpcNSigmaPr()); + } + + if (lambda1405Cand.isSigmaMinus && (sigmaCand.mSigmaMinus() < o2::constants::physics::MassSigmaMinus - selections.cutSigmaMass || + sigmaCand.mSigmaMinus() > o2::constants::physics::MassSigmaMinus + selections.cutSigmaMass)) { + return; + } + if (lambda1405Cand.isSigmaPlus && (sigmaCand.mSigmaPlus() < o2::constants::physics::MassSigmaPlus - selections.cutSigmaMass || + sigmaCand.mSigmaPlus() > o2::constants::physics::MassSigmaPlus + selections.cutSigmaMass)) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 3); // Passed mass sel + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 3); // Passed mass sel + } + + if (std::abs(sigmaCand.dcaMothPv()) > selections.cutDcaToPvSigma) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 4); // Passed cutDcaToPvSigma + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 4); // Passed cutDcaToPvSigma + } + + if (std::abs(sigmaCand.dcaDaugPv()) < selections.cutDcaToPvPiFromSigma) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 5); // cutDcaToPvPiFromSigma + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 5); // cutDcaToPvPiFromSigma + } + + float sigmaRad = std::hypot(sigmaCand.xDecVtx(), sigmaCand.yDecVtx()); + if (sigmaRad < selections.cutSigmaRadius) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 6); // Passed radius sel + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 6); // Passed radius sel + } + + if (lambda1405Cand.chi2KinkPCA > selections.cutSigmaChi2PCA) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 7); // Passed radius sel + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 7); // Passed radius sel + } + + auto kinkDauMom = std::array{sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()}; + // Sigma properties + lambda1405Cand.sigmaId = sigmaCand.globalIndex(); + lambda1405Cand.sigmaMinusMass = sigmaCand.mSigmaMinus(); + lambda1405Cand.sigmaPlusMass = sigmaCand.mSigmaPlus(); + lambda1405Cand.xiMinusMass = sigmaCand.mXiMinus(); + lambda1405Cand.sigmaSign = sigmaCand.mothSign(); + lambda1405Cand.sigmaAlphaAP = alphaAP(sigmaMom, kinkDauMom); + lambda1405Cand.sigmaQtAP = qtAP(sigmaMom, kinkDauMom); + lambda1405Cand.sigmaPt = sigmaPt; + lambda1405Cand.sigmaRadius = sigmaRad; + lambda1405Cand.dcaSigmaToPv = sigmaCand.dcaMothPv(); + + if (lambda1405Cand.sigmaQtAP < funcMinQtAlphaAP.Eval(lambda1405Cand.sigmaAlphaAP) || + lambda1405Cand.sigmaQtAP > funcMaxQtAlphaAP.Eval(lambda1405Cand.sigmaAlphaAP)) { + return; + } + if (lambda1405Cand.isSigmaMinus) { + rSelections.fill(HIST("hSelectionsSigmaMinus"), 7); // Passed AP sel + } else { + rSelections.fill(HIST("hSelectionsSigmaPlus"), 7); // Passed AP sel + } + + // Kink daughter properties + lambda1405Cand.kinkDauId = kinkDauTrack.globalIndex(); + lambda1405Cand.kinkPt = kinkDauTrack.pt(); + lambda1405Cand.kinkPiNSigTpc = kinkDauTrack.tpcNSigmaPi(); + lambda1405Cand.kinkPiNSigTof = kinkDauTrack.tofNSigmaPi(); + lambda1405Cand.kinkPrNSigTpc = kinkDauTrack.tpcNSigmaPr(); + lambda1405Cand.kinkPrNSigTof = kinkDauTrack.tofNSigmaPr(); + lambda1405Cand.kinkDcaDauToPv = sigmaCand.dcaDaugPv(); + lambda1405Cand.chi2KinkPCA = sigmaCand.dcaKinkTopo(); + + rSelections.fill(HIST("hSelectionsL1405"), 1); // Passed Sigma sel + + // Collision properties + lambda1405Cand.pvContrib = collision.numContrib(); + lambda1405Cand.centMult = getCentMult(collision); + lambda1405Cand.occupancy = collision.ft0cOccupancyInTimeRange(); - if (!selectPiTrack(piTrack, false)) { + fillHistosSigma(lambda1405Cand, sigmaCand, kinkDauTrack); + + int countPairedBachPi{0}; + for (const auto& piTrack : tracks) { + rSelections.fill(HIST("hSelectionsBachPi"), 0); // All bachelors + + // Needed to avoid spurious correlations in the Like-Sign case + if (piTrack.globalIndex() == kinkDauTrack.globalIndex()) { + continue; // Skip the kink daughter track + } + + bool acceptPair{false}; + if (doLikeSignBkg) { + acceptPair = (piTrack.sign() == sigmaCand.mothSign()); + } else { + acceptPair = (piTrack.sign() != sigmaCand.mothSign()); + } + if (!acceptPair) { continue; } + rSelections.fill(HIST("hSelectionsBachPi"), 1); + + if (!selectPiBach(piTrack, lambda1405Cand.centMult)) { + continue; + } + rSelections.fill(HIST("hSelectionsL1405"), 2); // Bach Pi selection - auto kinkDauMom = std::array{sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()}; - auto sigmaMom = std::array{sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth()}; auto piMom = std::array{piTrack.px(), piTrack.py(), piTrack.pz()}; - float invMass = RecoDecay::m(std::array{sigmaMom, piMom}, std::array{o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPiPlus}); - float invMassXiPi = RecoDecay::m(std::array{sigmaMom, kinkDauMom}, std::array{o2::constants::physics::MassXiMinus, o2::constants::physics::MassPiPlus}); - if (invMass > cutUpperMass) { + float invMass{-1.f}; + if (lambda1405Cand.isSigmaMinus) { + invMass = RecoDecay::m(std::array{sigmaMom, piMom}, std::array{o2::constants::physics::MassSigmaMinus, o2::constants::physics::MassPiPlus}); + } else if (lambda1405Cand.isSigmaPlus) { + invMass = RecoDecay::m(std::array{sigmaMom, piMom}, std::array{o2::constants::physics::MassSigmaPlus, o2::constants::physics::MassPiMinus}); + } + if (invMass > selections.cutUpperMass) { continue; } + rSelections.fill(HIST("hSelectionsL1405"), 3); // Upper mass selection - lambda1405Cand.kinkDauID = kinkDauTrack.globalIndex(); - lambda1405Cand.sigmaID = sigmaCand.globalIndex(); - lambda1405Cand.piID = piTrack.globalIndex(); + // Daughter Pi properties + lambda1405Cand.bachPiId = piTrack.globalIndex(); + lambda1405Cand.bachPiPt = piTrack.pt(); + lambda1405Cand.bachPiNSigTpc = piTrack.tpcNSigmaPi(); + if (useTof) { + lambda1405Cand.bachPiNSigTof = piTrack.tofNSigmaPi(); + } else { + lambda1405Cand.bachPiNSigTof = -999; // Not used if Tof is not enabled + } + // Lambda(1405) candidate properties + lambda1405Cand.massL1405 = invMass; + lambda1405Cand.massXi1530 = RecoDecay::m(std::array{sigmaMom, piMom}, std::array{o2::constants::physics::MassXiMinus, o2::constants::physics::MassPiPlus}); lambda1405Cand.px = sigmaMom[0] + piMom[0]; lambda1405Cand.py = sigmaMom[1] + piMom[1]; lambda1405Cand.pz = sigmaMom[2] + piMom[2]; - lambda1405Cand.mass = invMass; - lambda1405Cand.massXi1530 = invMassXiPi; - - lambda1405Cand.sigmaMinusMass = sigmaCand.mSigmaMinus(); - lambda1405Cand.sigmaPlusMass = sigmaCand.mSigmaPlus(); - lambda1405Cand.xiMinusMass = sigmaCand.mXiMinus(); - lambda1405Cand.sigmaSign = sigmaCand.mothSign(); - lambda1405Cand.sigmaAlphaAP = alphaAP(sigmaMom, kinkDauMom); - lambda1405Cand.sigmaQtAP = qtAP(sigmaMom, kinkDauMom); - lambda1405Cand.sigmaPt = sigmaCand.ptMoth(); - lambda1405Cand.sigmaRadius = sigmaRad; - lambda1405Cand.kinkPt = kinkDauTrack.pt(); - lambda1405Cand.kinkTPCNSigmaPi = kinkDauTrack.tpcNSigmaPi(); - lambda1405Cand.kinkTOFNSigmaPi = kinkDauTrack.tofNSigmaPi(); - lambda1405Cand.kinkTPCNSigmaPr = kinkDauTrack.tpcNSigmaPr(); - lambda1405Cand.kinkTOFNSigmaPr = kinkDauTrack.tofNSigmaPr(); - lambda1405Cand.dcaKinkDauToPV = sigmaCand.dcaDaugPv(); - - lambda1405Cand.piPt = piTrack.pt(); - lambda1405Cand.nSigmaTPCPi = piTrack.tpcNSigmaPi(); - if (useTOF) { - lambda1405Cand.nSigmaTOFPi = piTrack.tofNSigmaPi(); - } else { - lambda1405Cand.nSigmaTOFPi = -999; // Not used if TOF is not enabled + lambda1405Cand.phi = std::atan2(lambda1405Cand.py, lambda1405Cand.px); + lambda1405Cand.eta = -std::log(std::tan(0.5 * std::atan2(std::hypot(lambda1405Cand.px, lambda1405Cand.py), lambda1405Cand.pz))); + lambda1405Cand.scalarProd = -1; + if constexpr (requires { collision.qvecFT0CRe(); }) { + float const xQVec = collision.qvecFT0CRe(); + float const yQVec = collision.qvecFT0CIm(); + float const cos2Phi = std::cos(2 * lambda1405Cand.phi); + float const sin2Phi = std::sin(2 * lambda1405Cand.phi); + lambda1405Cand.scalarProd = cos2Phi * xQVec + sin2Phi * yQVec; + } + + // Check correlations between transverse momenta of L1405, sigma and bachelor pi + if (std::hypot(sigmaCand.pxMoth(), sigmaCand.pyMoth()) < funcMinSigmaPtVsL1405Pt.Eval(lambda1405Cand.pt()) || + std::hypot(sigmaCand.pxMoth(), sigmaCand.pyMoth()) > funcMaxSigmaPtVsL1405Pt.Eval(lambda1405Cand.pt())) { + continue; + } + if (piTrack.pt() < funcMinBachPiPtVsL1405Pt.Eval(lambda1405Cand.pt()) || + piTrack.pt() > funcMaxBachPiPtVsL1405Pt.Eval(lambda1405Cand.pt())) { + continue; + } + rSelections.fill(HIST("hSelectionsL1405"), 4); // Pt correlations + + if (lambda1405Cand.pt() < selections.cutMinPtL1405) { + continue; } - return true; // Candidate is selected + rSelections.fill(HIST("hSelectionsL1405"), 5); // Accepted + + selectedCandidates.push_back(lambda1405Cand); + countPairedBachPi++; } - return false; // No valid pion track found + rLambda1405.fill(HIST("hPairedBachPiVsCent"), lambda1405Cand.centMult, countPairedBachPi); } template bool checkSigmaKinkMC(const mcTrack& mcTrackSigma, const mcTrack& mcTrackKinkDau, float sigmaAbsPDG, float kinkAbsPDG, aod::McParticles const&) { - if (std::abs(mcTrackSigma.pdgCode()) != sigmaAbsPDG || std::abs(mcTrackKinkDau.pdgCode()) != kinkAbsPDG) { - return false; // Not a valid Sigma kink decay - } if (!mcTrackKinkDau.has_mothers()) { return false; // No mothers found } + if (std::abs(mcTrackSigma.pdgCode()) != sigmaAbsPDG || std::abs(mcTrackKinkDau.pdgCode()) != kinkAbsPDG) { + return false; // Not a valid Sigma kink decay + } // Check if the kink comes from the Sigma bool isKinkFromSigma = false; for (const auto& mcMother : mcTrackKinkDau.template mothers_as()) { @@ -319,84 +1131,318 @@ struct lambda1405analysis { return isKinkFromSigma; // Return true if the kink comes from the Sigma } - void processData(CollisionsFull::iterator const& collision, aod::KinkCands const& kinkCands, TracksFull const& tracks) + template + void fillOutputData(const TCollision& collision, const TCand& sigmaCands, const TTrack& tracks, const TBinningType& binPolicy) { - if (std::abs(collision.posZ()) > cutzvertex || !collision.sel8()) { + if (std::abs(collision.posZ()) > eventSelection.cutZVertex || !collision.sel8()) { return; } rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); - for (const auto& sigmaCand : kinkCands) { - if (selectCandidate(sigmaCand, tracks)) { + float centMult = getCentMult(collision); + if (centMult < eventSelection.centMultMin || centMult > eventSelection.centMultMax) { + return; + } + rEventSelection.fill(HIST("hCentMultVsPvContrib"), centMult, collision.numContrib()); + rEventSelection.fill(HIST("hOccVsPvContrib"), collision.ft0cOccupancyInTimeRange(), collision.numContrib()); + rEventSelection.fill(HIST("hCentVsOcc"), centMult, collision.ft0cOccupancyInTimeRange()); + + for (const auto& sigmaCand : sigmaCands) { + std::vector selectedCandidates; + constructCollCandidates(collision, sigmaCand, tracks, selectedCandidates, binPolicy); + for (const auto& lambda1405Cand : selectedCandidates) { if (lambda1405Cand.isSigmaMinus) { - rLambda1405.fill(HIST("h2PtMass_0"), lambda1405Cand.sigmaSign * lambda1405Cand.pt(), lambda1405Cand.mass); - rLambda1405.fill(HIST("h2PtMassSigma_0"), lambda1405Cand.sigmaSign * lambda1405Cand.sigmaPt, lambda1405Cand.sigmaMinusMass); - rLambda1405.fill(HIST("h2SigmaMassVsMass_0"), lambda1405Cand.mass, lambda1405Cand.sigmaMinusMass); - rLambda1405.fill(HIST("h2PtPiNSigmaTOF_0"), lambda1405Cand.sigmaSign * lambda1405Cand.piPt, lambda1405Cand.nSigmaTOFPi); + rLambda1405.fill(HIST("h2SigmaMinusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaMinusMass); + } else { + rLambda1405.fill(HIST("h2SigmaPlusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaPlusMass); } - if (lambda1405Cand.isSigmaPlus) { - rLambda1405.fill(HIST("h2PtMass_1"), lambda1405Cand.sigmaSign * lambda1405Cand.pt(), lambda1405Cand.mass); - rLambda1405.fill(HIST("h2PtMassSigma_1"), lambda1405Cand.sigmaSign * lambda1405Cand.sigmaPt, lambda1405Cand.sigmaPlusMass); - rLambda1405.fill(HIST("h2SigmaMassVsMass_1"), lambda1405Cand.mass, lambda1405Cand.sigmaPlusMass); - rLambda1405.fill(HIST("h2PtPiNSigmaTOF_1"), lambda1405Cand.sigmaSign * lambda1405Cand.piPt, lambda1405Cand.nSigmaTOFPi); + if (fillOutputTree || fillFlowTree) { + float const ptCand = lambda1405Cand.pt(); + if (downSampleFactor < 1.) { + float const pseudoRndm = ptCand * 1000. - static_cast(ptCand * 1000); + if (ptCand < ptDownSampleMax && pseudoRndm >= downSampleFactor) { + continue; + } + } + if (fillOutputTree) { + outputDataTable(lambda1405Cand.px, lambda1405Cand.py, lambda1405Cand.pz, + lambda1405Cand.massL1405, lambda1405Cand.massXi1530, + lambda1405Cand.sigmaMinusMass, lambda1405Cand.sigmaPlusMass, lambda1405Cand.xiMinusMass, + lambda1405Cand.sigmaPt, lambda1405Cand.sigmaAlphaAP, lambda1405Cand.sigmaQtAP, lambda1405Cand.sigmaRadius, + lambda1405Cand.kinkPt, + lambda1405Cand.kinkPiNSigTpc, lambda1405Cand.kinkPiNSigTof, + lambda1405Cand.kinkPrNSigTpc, lambda1405Cand.kinkPrNSigTof, + lambda1405Cand.kinkDcaDauToPv, lambda1405Cand.dcaSigmaToPv, lambda1405Cand.chi2KinkPCA, + lambda1405Cand.bachPiNSigTpc, lambda1405Cand.bachPiNSigTof, + lambda1405Cand.scalarProd, + lambda1405Cand.centMult, lambda1405Cand.occupancy, lambda1405Cand.poolBin); // 24 + } + } + + // Fill histograms + if constexpr (requires { collision.qvecFT0CRe(); }) { + if (saveDEtaDPhi) { + fillHistosLambda1405(lambda1405Cand, tracks); + } else { + fillHistosLambda1405(lambda1405Cand, tracks); + } + } else { + if (saveDEtaDPhi) { + fillHistosLambda1405(lambda1405Cand, tracks); + } else { + fillHistosLambda1405(lambda1405Cand, tracks); + } + } + } + } + } + + void processDataSameEvent(CollisionsSel::iterator const& collision, + aod::KinkCands const& kinkCands, + TracksFull const& tracks, + const aod::BCs&) + { + if (useCentrality) { + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; + fillOutputData(collision, kinkCands, tracks, binPolicy); + } else { + BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}}; + fillOutputData(collision, kinkCands, tracks, binPolicy); + } + } + PROCESS_SWITCH(lambda1405analysis, processDataSameEvent, "Data processing", true); + + void processDataWQVecsSameEvent(CollisionsSelWQVecs::iterator const& collision, + aod::KinkCands const& kinkCands, + TracksFull const& tracks, + const aod::BCs&) + { + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; + fillOutputData(collision, kinkCands, tracks, binPolicy); + } + PROCESS_SWITCH(lambda1405analysis, processDataWQVecsSameEvent, "Data processing with centrality and Q vectors info", false); + + void processDataMixedEvent(CollisionsFull const& collisions, + aod::KinkCands const& kinkCands, + TracksFull const& tracks, + const aod::BCs&) + { + auto pairsTuple = std::make_tuple(kinkCands, tracks); + if (useCentrality) { + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; + Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; + for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { + if (sigmas.size() == 0 || bachPis.size() == 0) { + continue; + } + int const poolBinSigma = getPoolBin(sigmasColl, binPolicy); + int const poolBinBachPi = getPoolBin(bachPisColl, binPolicy); + if (poolBinBachPi != poolBinSigma) { + LOG(debug) << "Different poolBins for sigma and bachelor pi: " << poolBinSigma << " vs " << poolBinBachPi; + continue; + } + fillOutputData(sigmasColl, sigmas, bachPis, binPolicy); + } + } else { + BinningMultPosZ binPolicy{{centMultPoolBins, zPoolBins}}; + Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; + for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { + if (sigmas.size() == 0 || bachPis.size() == 0) { + continue; + } + int const poolBinSigma = getPoolBin(sigmasColl, binPolicy); + int const poolBinBachPi = getPoolBin(bachPisColl, binPolicy); + if (poolBinBachPi != poolBinSigma) { + LOG(debug) << "Different poolBins for sigma and bachelor pi: " << poolBinSigma << " vs " << poolBinBachPi; + continue; + } + fillOutputData(sigmasColl, sigmas, bachPis, binPolicy); + } + } + } + PROCESS_SWITCH(lambda1405analysis, processDataMixedEvent, "Data event mixing processing", false); + + void processDataWQVecsMixedEvent(CollisionsSelWQVecs const& collisions, + aod::KinkCands const& kinkCands, + TracksFull const& tracks, + const aod::BCs&) + { + auto pairsTuple = std::make_tuple(kinkCands, tracks); + BinningCentPosZ binPolicy{{centMultPoolBins, zPoolBins}}; + Pair const pairs{binPolicy, numberEventsMixed, -1, collisions, pairsTuple, &cache}; + for (const auto& [sigmasColl, sigmas, bachPisColl, bachPis] : pairs) { + if (sigmas.size() == 0 || bachPis.size() == 0) { + continue; + } + int const poolBinSigma = getPoolBin(sigmasColl, binPolicy); + int const poolBinBachPi = getPoolBin(bachPisColl, binPolicy); + if (poolBinBachPi != poolBinSigma) { + LOG(debug) << "Different poolBins for sigma and bachelor pi: " << poolBinSigma << " vs " << poolBinBachPi; + continue; + } + fillOutputData(sigmasColl, sigmas, bachPis, binPolicy); + } + } + PROCESS_SWITCH(lambda1405analysis, processDataWQVecsMixedEvent, "Data event mixing with centrality and Q vectors info", false); + + template + int matchGenDecay(const TMother& motherPart, const aod::McParticles& mcParticles, std::array& daugsIdxs) + { + int pdgMother = motherPart.pdgCode(); + int8_t sign = 0; + + int MaxDepthFinState = 2; // Maximum depth to look for the decay chain + + std::vector arrL1405Daugs = {}; + std::array finalState; + int decayChannel = -1; + bool isMatched = false; + + // Match L(1405) --> n pi- pi+ final state + if (pdgMother > 0) { // Change sign of neutral decay products + finalState = {PDG_t::kNeutron, PDG_t::kPiMinus, PDG_t::kPiPlus}; + } else { + finalState = {-PDG_t::kNeutron, PDG_t::kPiMinus, PDG_t::kPiPlus}; + } + if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, lambda1405PdgCode, finalState, true, &sign, MaxDepthFinState)) { + isMatched = true; + // Match the intermediate Sigma + RecoDecay::getDaughters(motherPart, &arrL1405Daugs, std::array{0}, 1); + for (auto iProng = 0u; iProng < arrL1405Daugs.size(); ++iProng) { + auto daughI = mcParticles.rawIteratorAt(arrL1405Daugs[iProng]); + if (std::abs(daughI.pdgCode()) == PDG_t::kSigmaPlus) { + decayChannel = kSigmaPlusPiToPiPiNeutron; + daugsIdxs[1] = arrL1405Daugs[iProng]; + } + if (std::abs(daughI.pdgCode()) == PDG_t::kSigmaMinus) { + decayChannel = kSigmaMinusPiToPiPiNeutron; + daugsIdxs[1] = arrL1405Daugs[iProng]; + } + if (std::abs(daughI.pdgCode()) == PDG_t::kPiPlus) { + daugsIdxs[0] = arrL1405Daugs[iProng]; + } + } + } + + // Match L(1405) --> p pi0 pi+ final state, only possible for Sigma+ + if (pdgMother > 0) { // Change sign of neutral decay products + finalState = {PDG_t::kProton, PDG_t::kPi0, PDG_t::kPiMinus}; + } else { + finalState = {PDG_t::kProton, -PDG_t::kPi0, PDG_t::kPiMinus}; + } + + if (RecoDecay::isMatchedMCGen(mcParticles, motherPart, lambda1405PdgCode, finalState, true, &sign, MaxDepthFinState)) { + isMatched = true; + // Match the intermediate Sigma + RecoDecay::getDaughters(motherPart, &arrL1405Daugs, std::array{0}, 1); + for (auto iProng = 0u; iProng < arrL1405Daugs.size(); ++iProng) { + auto daughI = mcParticles.rawIteratorAt(arrL1405Daugs[iProng]); + if (std::abs(daughI.pdgCode()) == PDG_t::kSigmaPlus) { + decayChannel = kSigmaPlusPiToPiPiProton; + daugsIdxs[1] = arrL1405Daugs[iProng]; } - if (fillOutputTree) { - outputDataTable(lambda1405Cand.px, lambda1405Cand.py, lambda1405Cand.pz, - lambda1405Cand.mass, lambda1405Cand.massXi1530, - lambda1405Cand.sigmaMinusMass, lambda1405Cand.sigmaPlusMass, lambda1405Cand.xiMinusMass, - lambda1405Cand.sigmaPt, lambda1405Cand.sigmaAlphaAP, lambda1405Cand.sigmaQtAP, lambda1405Cand.sigmaRadius, - lambda1405Cand.kinkPt, - lambda1405Cand.kinkTPCNSigmaPi, lambda1405Cand.kinkTOFNSigmaPi, - lambda1405Cand.kinkTPCNSigmaPr, lambda1405Cand.kinkTOFNSigmaPr, - lambda1405Cand.dcaKinkDauToPV, - lambda1405Cand.nSigmaTPCPi, lambda1405Cand.nSigmaTOFPi); + if (std::abs(daughI.pdgCode()) == PDG_t::kPiPlus) { + daugsIdxs[0] = arrL1405Daugs[iProng]; } } } + + if (!isMatched) { + return decayChannel; // No valid decay channel found + } + + std::vector arrSigmaDaugs = {}; + auto sigmaDaug = mcParticles.rawIteratorAt(daugsIdxs[1]); + RecoDecay::getDaughters(sigmaDaug, &arrSigmaDaugs, std::array{0}, 1); + for (auto iProng = 0u; iProng < arrSigmaDaugs.size(); ++iProng) { + auto daughSigma = mcParticles.rawIteratorAt(arrSigmaDaugs[iProng]); + if (std::abs(daughSigma.pdgCode()) == PDG_t::kPiPlus || std::abs(daughSigma.pdgCode()) == PDG_t::kProton) { + daugsIdxs[2] = arrSigmaDaugs[iProng]; + } + } + + return decayChannel; } - PROCESS_SWITCH(lambda1405analysis, processData, "Data processing", true); - void processMC(CollisionsFullMC const& collisions, aod::KinkCands const& kinkCands, aod::McTrackLabels const& trackLabelsMC, aod::McParticles const& particlesMC, TracksFull const& tracks) + template + void fillOutputMc(const TCollision& recoCollisions, + const aod::KinkCands& sigmaCands, + const aod::McTrackLabels& trackLabelsMC, + const TTrack& tracks, + const aod::McParticles& particlesMC) { - for (const auto& collision : collisions) { - if (std::abs(collision.posZ()) > cutzvertex || !collision.sel8()) { + for (const auto& collision : recoCollisions) { + if (std::abs(collision.posZ()) > eventSelection.cutZVertex) { // || !collision.sel8()) { continue; } rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); - auto sigmaCandsPerCol = kinkCands.sliceBy(mKinkPerCol, collision.globalIndex()); + float centMult = getCentMult(collision); + if (centMult < eventSelection.centMultMin || centMult > eventSelection.centMultMax) { + continue; + } + rEventSelection.fill(HIST("hCentMultVsPvContrib"), centMult, collision.numContrib()); + rEventSelection.fill(HIST("hOccVsPvContrib"), collision.ft0cOccupancyInTimeRange(), collision.numContrib()); + rEventSelection.fill(HIST("hCentVsOcc"), centMult, collision.ft0cOccupancyInTimeRange()); + + auto sigmaCandsPerCol = sigmaCands.sliceBy(mKinkPerCol, collision.globalIndex()); auto tracksPerCol = tracks.sliceBy(mPerColTracks, collision.globalIndex()); for (const auto& sigmaCand : sigmaCandsPerCol) { - if (selectCandidate(sigmaCand, tracksPerCol)) { - // Do MC association - auto mcLabPiKink = trackLabelsMC.rawIteratorAt(lambda1405Cand.kinkDauID); - auto mcLabSigma = trackLabelsMC.rawIteratorAt(lambda1405Cand.sigmaID); - auto mcLabPi = trackLabelsMC.rawIteratorAt(lambda1405Cand.piID); - if (!mcLabSigma.has_mcParticle() || mcLabPiKink.has_mcParticle() || mcLabPi.has_mcParticle()) { - continue; // Skip if no valid MC association - } - auto mcTrackKink = mcLabPiKink.mcParticle_as(); - auto mcTrackSigma = mcLabSigma.mcParticle_as(); - auto mcTrackPi = mcLabPi.mcParticle_as(); - bool isSigmaMinusKink = checkSigmaKinkMC(mcTrackSigma, mcTrackKink, 3122, 211, particlesMC); - bool isSigmaPlusToPiKink = checkSigmaKinkMC(mcTrackSigma, mcTrackKink, 3222, 211, particlesMC); - bool isSigmaPlusToPrKink = checkSigmaKinkMC(mcTrackSigma, mcTrackKink, 3222, 2212, particlesMC); + // Perform the sigma matching here, so sigma histograms + // can be used for efficiency studies on the sigma + auto labelSigma = trackLabelsMC.rawIteratorAt(sigmaCand.trackMothId()); + auto labelKinkDaug = trackLabelsMC.rawIteratorAt(sigmaCand.trackDaugId()); + if (!labelSigma.has_mcParticle()) { + rSelections.fill(HIST("hRecoNotMatchedCounter"), 1); // Sigma not matched + continue; // Skip if no valid MC association + } + if (!labelKinkDaug.has_mcParticle()) { + rSelections.fill(HIST("hRecoNotMatchedCounter"), 2); // Kink daughter not matched + continue; // Skip if no valid MC association + } + auto genSigma = labelSigma.template mcParticle_as(); + auto genKinkDaug = labelKinkDaug.template mcParticle_as(); + + bool isSigmaMinusKink = checkSigmaKinkMC(genSigma, genKinkDaug, PDG_t::kSigmaMinus, PDG_t::kPiPlus, particlesMC); + bool isSigmaPlusToPiKink = checkSigmaKinkMC(genSigma, genKinkDaug, PDG_t::kSigmaPlus, PDG_t::kPiPlus, particlesMC); + bool isSigmaPlusToPrKink = checkSigmaKinkMC(genSigma, genKinkDaug, PDG_t::kSigmaPlus, PDG_t::kProton, particlesMC); + + if (!isSigmaMinusKink && !isSigmaPlusToPiKink && !isSigmaPlusToPrKink) { + continue; // Skip if not a valid Sigma kink decay + } + if (isSigmaMinusKink) { + rSigmaMinus.fill(HIST("h2DeltaGenRecoPtSigmaMinus"), sigmaCand.ptMoth() - genSigma.pt(), sigmaCand.ptMoth()); + } + if (isSigmaPlusToPiKink || isSigmaPlusToPrKink) { + rSigmaPlus.fill(HIST("h2DeltaGenRecoPtSigmaPlus"), sigmaCand.ptMoth() - genSigma.pt(), sigmaCand.ptMoth()); + } + std::vector selectedCandidates; + const int dummyPoolBin{-1}; // Not used in MC, but required by the function signature + constructCollCandidates(collision, sigmaCand, tracksPerCol, selectedCandidates, dummyPoolBin); + for (const auto& lambda1405Cand : selectedCandidates) { + rLambda1405.fill(HIST("hQARecoL1405"), 0., lambda1405Cand.pt()); // All reconstructed - if (!isSigmaMinusKink && !isSigmaPlusToPiKink && !isSigmaPlusToPrKink) { - continue; // Skip if not a valid Sigma kink decay + // Do MC association + auto labelBachPi = trackLabelsMC.rawIteratorAt(lambda1405Cand.bachPiId); + if (!labelBachPi.has_mcParticle()) { + rSelections.fill(HIST("hRecoNotMatchedCounter"), 3); // Bach pion not matched + continue; // Skip if no valid MC association } - if (std::abs(mcTrackPi.pdgCode()) != 211) { + auto genBachPi = labelBachPi.template mcParticle_as(); + if (std::abs(genBachPi.pdgCode()) != PDG_t::kPiPlus) { continue; // Skip if not a valid pion candidate } + rLambda1405.fill(HIST("hQARecoL1405"), 1., lambda1405Cand.pt()); // Has bach pi - if (!mcTrackSigma.has_mothers() || !mcTrackPi.has_mothers()) { + if (!genSigma.has_mothers() || !genBachPi.has_mothers()) { continue; // Skip if no mothers found } + rLambda1405.fill(HIST("hQARecoL1405"), 2., lambda1405Cand.pt()); // Has mothers for Sigma and Pi // check that labpi and labsigma have the same mother (a lambda1405 candidate) int lambda1405Id = -1; - for (const auto& piMother : mcTrackPi.mothers_as()) { - for (const auto& sigmaMother : mcTrackSigma.mothers_as()) { + for (const auto& piMother : genBachPi.template mothers_as()) { + for (const auto& sigmaMother : genSigma.template mothers_as()) { if (piMother.globalIndex() == sigmaMother.globalIndex() && std::abs(piMother.pdgCode()) == lambda1405PdgCode) { lambda1405Id = piMother.globalIndex(); break; // Found the mother, exit loop @@ -406,70 +1452,318 @@ struct lambda1405analysis { if (lambda1405Id == -1) { continue; // Skip if the Sigma and pion do not share the same lambda1405 candidate } + rLambda1405.fill(HIST("hQARecoL1405"), 3., lambda1405Cand.pt()); // Matched + + std::array idxsSigmaKinkBachPi{-1, -1, -1}; + auto genLambda1405 = particlesMC.rawIteratorAt(lambda1405Id); + int decayChannel = matchGenDecay(genLambda1405, particlesMC, idxsSigmaKinkBachPi); + if (decayChannel == -1) { + continue; // Skip if it doesn't match the decay channels of interest + } + rLambda1405.fill(HIST("hRecoL1405"), decayChannel, genLambda1405.pt()); + auto lambda1405Mother = particlesMC.rawIteratorAt(lambda1405Id); float lambda1405Mass = std::sqrt(lambda1405Mother.e() * lambda1405Mother.e() - lambda1405Mother.p() * lambda1405Mother.p()); + rLambda1405.fill(HIST("h2RecL1405PvContribPt"), collision.numContrib(), lambda1405Cand.pt()); if (lambda1405Cand.isSigmaMinus) { - rLambda1405.fill(HIST("h2PtMass_0"), lambda1405Cand.sigmaSign * lambda1405Cand.pt(), lambda1405Cand.mass); - rLambda1405.fill(HIST("h2PtMassSigma_0"), lambda1405Cand.sigmaSign * lambda1405Cand.sigmaPt, lambda1405Cand.sigmaMinusMass); - rLambda1405.fill(HIST("h2SigmaMassVsMass_0"), lambda1405Cand.mass, lambda1405Cand.sigmaMinusMass); - rLambda1405.fill(HIST("h2PtPiNSigma_0"), lambda1405Cand.piPt, lambda1405Cand.nSigmaTPCPi); - rLambda1405.fill(HIST("h2MassResolution_0"), lambda1405Mass, lambda1405Mass - lambda1405Cand.mass); - rLambda1405.fill(HIST("h2PtResolution_0"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); + rLambda1405.fill(HIST("h2SigmaMinusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaMinusMass); + rLambda1405.fill(HIST("h2MassResoFromSigmaMinus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); + rLambda1405.fill(HIST("h2PtResoFromSigmaMinus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); } if (lambda1405Cand.isSigmaPlus) { - rLambda1405.fill(HIST("h2PtMass_1"), lambda1405Cand.sigmaSign * lambda1405Cand.pt(), lambda1405Cand.mass); - rLambda1405.fill(HIST("h2PtMassSigma_1"), lambda1405Cand.sigmaSign * lambda1405Cand.sigmaPt, lambda1405Cand.sigmaPlusMass); - rLambda1405.fill(HIST("h2SigmaMassVsMass_1"), lambda1405Cand.mass, lambda1405Cand.sigmaPlusMass); - rLambda1405.fill(HIST("h2PtPiNSigma_1"), lambda1405Cand.piPt, lambda1405Cand.nSigmaTPCPi); - rLambda1405.fill(HIST("h2MassResolution_1"), lambda1405Mass, lambda1405Mass - lambda1405Cand.mass); - rLambda1405.fill(HIST("h2PtResolution_1"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); + rLambda1405.fill(HIST("h2SigmaPlusMassVsLambdaMass"), lambda1405Cand.massL1405, lambda1405Cand.sigmaPlusMass); + rLambda1405.fill(HIST("h2MassResoFromSigmaPlus"), lambda1405Mass, lambda1405Mass - lambda1405Cand.massL1405); + rLambda1405.fill(HIST("h2PtResoFromSigmaPlus"), lambda1405Cand.pt(), lambda1405Cand.pt() - lambda1405Mother.pt()); } if (fillOutputTree) { outputDataTableMC(lambda1405Cand.px, lambda1405Cand.py, lambda1405Cand.pz, - lambda1405Cand.mass, lambda1405Cand.massXi1530, + lambda1405Cand.massL1405, lambda1405Cand.massXi1530, lambda1405Cand.sigmaMinusMass, lambda1405Cand.sigmaPlusMass, lambda1405Cand.xiMinusMass, lambda1405Cand.sigmaPt, lambda1405Cand.sigmaAlphaAP, lambda1405Cand.sigmaQtAP, lambda1405Cand.sigmaRadius, lambda1405Cand.kinkPt, - lambda1405Cand.kinkTPCNSigmaPi, lambda1405Cand.kinkTOFNSigmaPi, - lambda1405Cand.kinkTPCNSigmaPr, lambda1405Cand.kinkTOFNSigmaPr, - lambda1405Cand.dcaKinkDauToPV, - lambda1405Cand.nSigmaTPCPi, lambda1405Cand.nSigmaTOFPi, - lambda1405Mother.pt(), lambda1405Mass, mcTrackSigma.pdgCode(), mcTrackKink.pdgCode()); + lambda1405Cand.kinkPiNSigTpc, lambda1405Cand.kinkPiNSigTof, + lambda1405Cand.kinkPrNSigTpc, lambda1405Cand.kinkPrNSigTof, + lambda1405Cand.kinkDcaDauToPv, lambda1405Cand.dcaSigmaToPv, lambda1405Cand.chi2KinkPCA, + lambda1405Cand.bachPiNSigTpc, lambda1405Cand.bachPiNSigTof, + lambda1405Mother.pt(), lambda1405Mass, genSigma.pdgCode(), genBachPi.pdgCode(), + lambda1405Cand.centMult, lambda1405Cand.occupancy); } + fillHistosLambda1405(lambda1405Cand, tracksPerCol); } } } // Loop over generated particles to fill MC histograms for (const auto& mcPart : particlesMC) { + if (std::abs(mcPart.pdgCode()) != lambda1405PdgCode && + std::abs(mcPart.pdgCode()) != PDG_t::kSigmaMinus && + std::abs(mcPart.pdgCode()) != PDG_t::kSigmaPlus) { + continue; // Only consider Lambda(1405) and Sigma candidates + } + + // Compute generated PV contributors + const auto& recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollision, mcPart.mcCollision().globalIndex()); + if (recoCollsPerMcColl.size() == 0) { + continue; // Skip if no reconstructed collisions associated with this MC collision + } + unsigned maxNumContrib = 0; + for (const auto& recCol : recoCollsPerMcColl) { + maxNumContrib = recCol.numContrib() > maxNumContrib ? recCol.numContrib() : maxNumContrib; + } + + // Needed for Sigma efficiency vs PV contributors + if (std::abs(mcPart.pdgCode()) == PDG_t::kSigmaMinus) { + rSigmaMinus.fill(HIST("h2GenSigmaMinusPvContribPt"), maxNumContrib, mcPart.pt()); + } + if (std::abs(mcPart.pdgCode()) == PDG_t::kSigmaPlus) { + rSigmaPlus.fill(HIST("h2GenSigmaPlusPvContribPt"), maxNumContrib, mcPart.pt()); + } if (std::abs(mcPart.pdgCode()) != lambda1405PdgCode) { continue; // Only consider Lambda(1405) candidates } + std::array idxsSigmaKinkBachPi{-1, -1, -1}; + int decayChannel = matchGenDecay(mcPart, particlesMC, idxsSigmaKinkBachPi); + if (decayChannel == -1) { + continue; // Skip if it doesn't match the decay channels of interest + } + rLambda1405.fill(HIST("hGenL1405"), decayChannel, mcPart.pt()); - if (!mcPart.has_daughters()) { - continue; // Skip if no daughters + auto bachPi = particlesMC.rawIteratorAt(idxsSigmaKinkBachPi[0]); + auto sigmaDaug = particlesMC.rawIteratorAt(idxsSigmaKinkBachPi[1]); + auto sigmaKinkDaug = particlesMC.rawIteratorAt(idxsSigmaKinkBachPi[2]); + // Generated Armenteros-Podolanski variables + float genSigmaAlphaAP = alphaAP({sigmaDaug.px(), sigmaDaug.py(), sigmaDaug.pz()}, {sigmaKinkDaug.px(), sigmaKinkDaug.py(), sigmaKinkDaug.pz()}); + float genSigmaQtAP = qtAP({sigmaDaug.px(), sigmaDaug.py(), sigmaDaug.pz()}, {sigmaKinkDaug.px(), sigmaKinkDaug.py(), sigmaKinkDaug.pz()}); + float mcMass = std::sqrt(mcPart.e() * mcPart.e() - mcPart.p() * mcPart.p()); + rLambda1405.fill(HIST("h2PtMassMC"), mcPart.pt(), mcMass); + rLambda1405.fill(HIST("h2GenL1405PvContribPt"), maxNumContrib, mcPart.pt()); + if (decayChannel == kSigmaMinusPiToPiPiNeutron) { + rLambda1405.fill(HIST("h2GenSigmaMinusArmPod"), genSigmaAlphaAP, genSigmaQtAP); + rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaMinus"), mcPart.pt(), bachPi.pt()); + rLambda1405.fill(HIST("h2GenPtVsSigmaMinusPt"), mcPart.pt(), sigmaDaug.pt()); + rLambda1405.fill(HIST("h2GenSigmaPtVsKinkPtSigmaMinus"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaMinus.fill(HIST("hGenSigmaMinusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); + } + if (decayChannel == kSigmaPlusPiToPiPiNeutron) { + rLambda1405.fill(HIST("h2GenSigmaPlusArmPod"), genSigmaAlphaAP, genSigmaQtAP); + rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaPlusToPi"), mcPart.pt(), bachPi.pt()); + rLambda1405.fill(HIST("h2GenPtVsSigmaPlusToPiPt"), mcPart.pt(), sigmaDaug.pt()); + rLambda1405.fill(HIST("h2GenSigmaPtVsPiKinkPt"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaPlus.fill(HIST("hGenSigmaPlusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); + } + if (decayChannel == kSigmaPlusPiToPiPiProton) { + rLambda1405.fill(HIST("h2GenSigmaPlusArmPod"), genSigmaAlphaAP, genSigmaQtAP); + rLambda1405.fill(HIST("h2GenPtVsBachPtSigmaPlusToPr"), mcPart.pt(), bachPi.pt()); + rLambda1405.fill(HIST("h2GenPtVsSigmaPlusToPrPt"), mcPart.pt(), sigmaDaug.pt()); + rLambda1405.fill(HIST("h2GenSigmaPtVsPrKinkPt"), sigmaDaug.pt(), sigmaKinkDaug.pt()); + rSigmaPlus.fill(HIST("hGenSigmaPlusRadius"), std::hypot(sigmaKinkDaug.vx(), sigmaKinkDaug.vy()), sigmaDaug.pt()); } + } + } + + void processMc(CollisionsFullMc const& recoCollisions, + aod::KinkCands const& kinkCands, + aod::McTrackLabels const& trackLabelsMC, + aod::McParticles const& particlesMC, + TracksFull const& tracks, + const aod::BCs&, + aod::McCollisions const&) + { + fillOutputMc(recoCollisions, kinkCands, trackLabelsMC, tracks, particlesMC); + } + PROCESS_SWITCH(lambda1405analysis, processMc, "MC processing", false); - // Check if the Lambda(1405) has a Sigma daughter - bool hasSigmaDaughter = false; - int dauPdgCode = 0; - for (const auto& daughter : mcPart.daughters_as()) { - if (std::abs(daughter.pdgCode()) == 3122 || std::abs(daughter.pdgCode()) == 3222) { // Sigma PDG code - hasSigmaDaughter = true; - dauPdgCode = daughter.pdgCode(); - break; // Found a Sigma daughter, exit loop + void processMcSigmasCentSel(McRecoCollisionsSel const& recoCollisions, + aod::KinkCands const& sigmaCands, + aod::McTrackLabels const& trackLabelsMC, + aod::McParticles const& particlesMC, + aod::McCollisions const&, + const aod::BCs&) + { + // Loop over kink candidates to fill Sigma efficiency histograms + for (const auto& collision : recoCollisions) { + if (std::abs(collision.posZ()) > eventSelection.cutZVertex) { // || !collision.sel8()) { + continue; + } + rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); + float centMult = getCentMult(collision); + if (centMult < eventSelection.centMultMin || centMult > eventSelection.centMultMax) { + continue; + } + + rEventSelection.fill(HIST("hCentMultVsPvContrib"), centMult, collision.numContrib()); + rEventSelection.fill(HIST("hOccVsPvContrib"), collision.ft0cOccupancyInTimeRange(), collision.numContrib()); + rEventSelection.fill(HIST("hCentVsOcc"), centMult, collision.ft0cOccupancyInTimeRange()); + + // Compute occupancy and pv contrib of the generated collision + const auto& recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollision, collision.mcCollisionId()); + if (recoCollsPerMcColl.size() == 0) { + continue; // Skip if no reconstructed collisions associated with this MC collision + } + unsigned genNumContrib = 0; + float genOcc{0.f}; + float genCentMult{0.f}; + for (const auto& recCol : recoCollsPerMcColl) { + genNumContrib = recCol.numContrib() > genNumContrib ? recCol.numContrib() : genNumContrib; + genOcc = recCol.ft0cOccupancyInTimeRange() > genOcc ? recCol.ft0cOccupancyInTimeRange() : genOcc; + genCentMult = getCentMult(collision) > genCentMult ? getCentMult(collision) : genCentMult; + } + + auto sigmaCandsPerCol = sigmaCands.sliceBy(mKinkPerCol, collision.globalIndex()); + for (const auto& sigmaCand : sigmaCandsPerCol) { + + auto labelSigma = trackLabelsMC.rawIteratorAt(sigmaCand.trackMothId()); + auto labelKinkDaug = trackLabelsMC.rawIteratorAt(sigmaCand.trackDaugId()); + if (!labelSigma.has_mcParticle() || !labelKinkDaug.has_mcParticle()) { + continue; // No generated particles + } + auto genSigma = labelSigma.template mcParticle_as(); + auto genKinkDaug = labelKinkDaug.template mcParticle_as(); + + bool isSigmaMinusKink = checkSigmaKinkMC(genSigma, genKinkDaug, PDG_t::kSigmaMinus, PDG_t::kPiPlus, particlesMC); + bool isSigmaPlusToPiKink = checkSigmaKinkMC(genSigma, genKinkDaug, PDG_t::kSigmaPlus, PDG_t::kPiPlus, particlesMC); + bool isSigmaPlusToPrKink = checkSigmaKinkMC(genSigma, genKinkDaug, PDG_t::kSigmaPlus, PDG_t::kProton, particlesMC); + + if (skipBkgSigmas && (!isSigmaMinusKink && !isSigmaPlusToPiKink && !isSigmaPlusToPrKink)) { + continue; // Skip if not a valid Sigma kink decay } + + float sigmaPt = sigmaCand.ptMoth(); + if (downSampleFactor < 1.) { + float const pseudoRndm = sigmaPt * 1000. - static_cast(sigmaPt * 1000); + if (sigmaPt < ptDownSampleMax && pseudoRndm >= downSampleFactor) { + continue; + } + } + + std::array sigmaMomReco{sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth()}; + std::array kinkMomReco{sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()}; + float recoSigmaAlphaAP = alphaAP(sigmaMomReco, kinkMomReco); + float recoSigmaQtAP = qtAP(sigmaMomReco, kinkMomReco); + + // Recompute the sigma momentum + bool success{false}; + float sigmaPRecalc = recalcSigmaMom(success, isSigmaMinusKink, + sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth(), + sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()); + if (!success && skipSigmasFailedRecompMom) { + return; + } + if (success) { + float sigmaPOriginal = std::sqrt(sigmaCand.pxMoth() * sigmaCand.pxMoth() + + sigmaCand.pyMoth() * sigmaCand.pyMoth() + + sigmaCand.pzMoth() * sigmaCand.pzMoth()); + if (sigmaPRecalc > 0.f && sigmaPOriginal > 0.f) { + float scale = sigmaPRecalc / sigmaPOriginal; + sigmaMomReco[0] *= scale; + sigmaMomReco[1] *= scale; + sigmaMomReco[2] *= scale; + + if (isSigmaMinusKink) { + rSigmaMinus.fill(HIST("hDeltaPxRecalcSigmaMinus"), sigmaMomReco[0] - sigmaCand.pxMoth(), sigmaCand.pxMoth()); + rSigmaMinus.fill(HIST("hDeltaPyRecalcSigmaMinus"), sigmaMomReco[1] - sigmaCand.pyMoth(), sigmaCand.pyMoth()); + rSigmaMinus.fill(HIST("hDeltaPzRecalcSigmaMinus"), sigmaMomReco[2] - sigmaCand.pzMoth(), sigmaCand.pzMoth()); + rSigmaMinus.fill(HIST("hRecalcPtFactorSigmaMinus"), scale, sigmaPOriginal); + } else { + rSigmaPlus.fill(HIST("hDeltaPxRecalcSigmaPlus"), sigmaMomReco[0] - sigmaCand.pxMoth(), sigmaCand.pxMoth()); + rSigmaPlus.fill(HIST("hDeltaPyRecalcSigmaPlus"), sigmaMomReco[1] - sigmaCand.pyMoth(), sigmaCand.pyMoth()); + rSigmaPlus.fill(HIST("hDeltaPzRecalcSigmaPlus"), sigmaMomReco[2] - sigmaCand.pzMoth(), sigmaCand.pzMoth()); + rSigmaPlus.fill(HIST("hRecalcPtFactorSigmaPlus"), scale, sigmaPOriginal); + } + } + } + + float recoRecalcPtSigmaAlphaAP = alphaAP(sigmaMomReco, kinkMomReco); + float recoRecalcPtSigmaQtAP = qtAP(sigmaMomReco, kinkMomReco); + float massSigma = isSigmaMinusKink ? sigmaCand.mSigmaMinus() : sigmaCand.mSigmaPlus(); + + // Generated properties + float genMassSigma{-1.f}; + if (isSigmaMinusKink || isSigmaPlusToPiKink || isSigmaPlusToPrKink) { + genMassSigma = std::sqrt(genSigma.e() * genSigma.e() - genSigma.p() * genSigma.p()); + std::array sigmaMomGen{sigmaCand.pxMoth(), sigmaCand.pyMoth(), sigmaCand.pzMoth()}; + std::array kinkMomGen{sigmaCand.pxDaug(), sigmaCand.pyDaug(), sigmaCand.pzDaug()}; + float genSigmaAlphaAP = alphaAP(sigmaMomGen, kinkMomGen); + float genSigmaQtAP = qtAP(sigmaMomGen, kinkMomGen); + float sigmaRadius = std::hypot(genKinkDaug.vx(), genKinkDaug.vy()); + if (isSigmaMinusKink) { + rSigmaMinus.fill(HIST("hSparseGenFromRecoSigmaMinus"), genMassSigma, genSigma.pt(), genSigmaAlphaAP, genSigmaQtAP, sigmaRadius, genCentMult, genNumContrib, genOcc); + } else if (isSigmaPlusToPiKink || isSigmaPlusToPrKink) { + rSigmaPlus.fill(HIST("hSparseGenFromRecoSigmaPlus"), genMassSigma, genSigma.pt(), genSigmaAlphaAP, genSigmaQtAP, sigmaRadius, genCentMult, genNumContrib, genOcc); + } + } + + // Fill table with sigma properties for efficiency studies + outputSigmaEffMC( + sigmaCand.pxMoth(), sigmaCand.pxMoth() - genSigma.px(), + sigmaCand.pyMoth(), sigmaCand.pyMoth() - genSigma.py(), + sigmaCand.pzMoth(), sigmaCand.pzMoth() - genSigma.pz(), + massSigma, massSigma - genMassSigma, + sigmaCand.pxMoth() - sigmaMomReco[0], + sigmaCand.pyMoth() - sigmaMomReco[1], + sigmaCand.pzMoth() - sigmaMomReco[2], + genSigma.phi(), + genSigma.eta(), + sigmaCand.pxDaug(), sigmaCand.pxDaug() - genKinkDaug.px(), + sigmaCand.pyDaug(), sigmaCand.pyDaug() - genKinkDaug.py(), + sigmaCand.pzDaug(), sigmaCand.pzDaug() - genKinkDaug.pz(), + genKinkDaug.phi(), + genKinkDaug.eta(), + sigmaCand.xDecVtx(), sigmaCand.xDecVtx() - genKinkDaug.vx(), + sigmaCand.yDecVtx(), sigmaCand.yDecVtx() - genKinkDaug.vy(), + sigmaCand.zDecVtx(), sigmaCand.zDecVtx() - genKinkDaug.vz(), + recoSigmaAlphaAP, recoSigmaQtAP, + recoRecalcPtSigmaAlphaAP, recoRecalcPtSigmaQtAP, + sigmaCand.dcaDaugPv(), sigmaCand.dcaMothPv(), sigmaCand.dcaKinkTopo(), + genSigma.pdgCode(), genKinkDaug.pdgCode(), + centMult, collision.ft0cOccupancyInTimeRange(), collision.numContrib()); } - if (!hasSigmaDaughter) { - continue; // Skip if no Sigma daughter found + } + + // generated sigmas + for (const auto& genSigma : particlesMC) { + if (std::abs(genSigma.pdgCode()) != PDG_t::kSigmaMinus && std::abs(genSigma.pdgCode()) != PDG_t::kSigmaPlus) { + continue; // Only consider Sigma candidates } - float mcMass = std::sqrt(mcPart.e() * mcPart.e() - mcPart.p() * mcPart.p()); - dauPdgCode ? rLambda1405.fill(HIST("h2PtMassMC_0"), mcPart.pt(), mcMass) : rLambda1405.fill(HIST("h2PtMassMC_1"), mcPart.pt(), mcMass); + const auto& recoCollsPerMcColl = recoCollisions.sliceBy(colPerMcCollision, genSigma.mcCollision().globalIndex()); + if (recoCollsPerMcColl.size() == 0) { + continue; // Skip if no reconstructed collisions associated with this MC collision + } + + unsigned genNumContrib = 0; + float genOcc{0.f}; + float genCentMult{0.f}; + for (const auto& recCol : recoCollsPerMcColl) { + genNumContrib = recCol.numContrib() > genNumContrib ? recCol.numContrib() : genNumContrib; + genOcc = recCol.ft0cOccupancyInTimeRange() > genOcc ? recCol.ft0cOccupancyInTimeRange() : genOcc; + genCentMult = getCentMult(recCol) > genCentMult ? getCentMult(recCol) : genCentMult; + } + + // Check the sigma daughter of the kink decay + std::vector arrSigmaDaugs = {}; + RecoDecay::getDaughters(genSigma, &arrSigmaDaugs, std::array{0}, 1); + for (auto iProng = 0u; iProng < arrSigmaDaugs.size(); ++iProng) { + auto daughSigma = particlesMC.rawIteratorAt(arrSigmaDaugs[iProng]); + float genMassSigma = std::sqrt(genSigma.e() * genSigma.e() - genSigma.p() * genSigma.p()); + std::array sigmaMomGen{genSigma.px(), genSigma.py(), genSigma.pz()}; + std::array kinkMomGen{daughSigma.px(), daughSigma.py(), daughSigma.pz()}; + float genSigmaAlphaAP = alphaAP(sigmaMomGen, kinkMomGen); + float genSigmaQtAP = qtAP(sigmaMomGen, kinkMomGen); + float sigmaRadius = std::hypot(daughSigma.vx(), daughSigma.vy()); + + if (std::abs(daughSigma.pdgCode()) == PDG_t::kPiPlus && std::abs(genSigma.pdgCode()) == PDG_t::kSigmaMinus) { + rSigmaMinus.fill(HIST("hSparseGenSigmaMinus"), genMassSigma, genSigma.pt(), genSigmaAlphaAP, genSigmaQtAP, sigmaRadius, genCentMult, genNumContrib, genOcc); + } else if (std::abs(daughSigma.pdgCode()) == PDG_t::kPiPlus && std::abs(genSigma.pdgCode()) == PDG_t::kSigmaPlus) { + rSigmaPlus.fill(HIST("hSparseGenSigmaPlus"), genMassSigma, genSigma.pt(), genSigmaAlphaAP, genSigmaQtAP, sigmaRadius, genCentMult, genNumContrib, genOcc); + } else if (std::abs(daughSigma.pdgCode()) == PDG_t::kProton && std::abs(genSigma.pdgCode()) == PDG_t::kSigmaPlus) { + rSigmaPlus.fill(HIST("hSparseGenSigmaPlus"), genMassSigma, genSigma.pt(), genSigmaAlphaAP, genSigmaQtAP, sigmaRadius, genCentMult, genNumContrib, genOcc); + } + } } } - PROCESS_SWITCH(lambda1405analysis, processMC, "MC processing", false); + PROCESS_SWITCH(lambda1405analysis, processMcSigmasCentSel, "MC processing for sigma efficiency studies", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx index 390bfd1768a..d84c295aa29 100644 --- a/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520SpherocityAnalysis.cxx @@ -345,7 +345,7 @@ struct lambdaAnalysis { // kinematic cuts method template - bool kinCuts(trackType trkPr, trackType trkKa, T p, float& alpha) + bool kinCuts(const trackType& trkPr, const trackType& trkKa, const T& p, float& alpha) { // initialize std::vector kinCutsPt = static_cast>(cKinCutsPt); @@ -653,8 +653,8 @@ struct lambdaAnalysis { { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}, true}; - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; + BinningType1 binningPositions1{{cMixVtxBins, cMixMultBins, cMixSphBins}}; + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (cMixSph) { SameKindPair pairs{binningPositions1, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip diff --git a/PWGLF/Tasks/Resonances/lambda1520_PbPb.cxx b/PWGLF/Tasks/Resonances/lambda1520_PbPb.cxx deleted file mode 100644 index 40589f98e58..00000000000 --- a/PWGLF/Tasks/Resonances/lambda1520_PbPb.cxx +++ /dev/null @@ -1,998 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file -/// /// Invariant Mass Reconstruction of Lambda(1520) Resonance. -/// -/// \author Yash Patley -/// \author Nasir Mehdi Malik - -#include "PWGLF/DataModel/LFResonanceTables.h" - -#include "Common/Core/RecoDecay.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -using namespace o2; -using namespace o2::framework; -using namespace o2::framework::expressions; -using namespace o2::constants::physics; - -struct lambdaAnalysis_pb { - SliceCache cache; - Preslice perRCol = aod::resodaughter::resoCollisionId; - Preslice perCollision = aod::track::collisionId; - // Configurables. - aod::ResoMCParents const* mResoParents = nullptr; - Configurable ConfEvtOccupancyInTimeRange{"ConfEvtOccupancyInTimeRange", false, "occupancy selection true or false"}; - Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histogram"}; - Configurable nBinsInvM{"nBinsInvM", 120, "N bins in InvMass histogram"}; - Configurable lambda1520id{"lambda1520id", 3124, "pdg"}; - Configurable doRotate{"doRotate", true, "rotated inv mass spectra"}; - // Tracks - Configurable cPtMin{"cPtMin", 0.15, "Minimum Track pT"}; - Configurable cPMin{"cPMin", 0., "Minimum Track p"}; - Configurable cEtaCut{"cEtaCut", 0.8, "Pseudorapidity cut"}; - Configurable cDcaz{"cDcazMin", 1., "Minimum DCAz"}; - Configurable cfgRapidityShift{"cfgRapidityShift", 0., " rapidity shift"}; - Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity window"}; - // TPC crossed rows (absolute) - Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min TPC crossed rows"}; - Configurable cfgUseCrossedRows{"cfgUseCrossedRows", false, "apply crossed rows cut"}; - - Configurable cfgMinTPCcls{"cfgMinTPCcls", 70, "min TPC clusters found"}; - Configurable cfgUseTPCcls{"cfgUseTPCcls", false, "apply TPC clusters cut"}; - - Configurable> cDcaPtBinsPr{"cDcaPtBinsPr", {0.0f, 0.5f, 1.0f, 2.0f, 3.0f, 5.0f, 1000.0f}, "Proton pT bin edges for DCAxy cut"}; - - Configurable> cDcaXYBinsPr{"cDcaXYBinsPr", {0.020f, 0.015f, 0.010f, 0.007f, 0.005f, 0.004f}, "Proton max |DCAxy| per pT bin (cm)"}; - - // Kaon DCAxy — pT binned - Configurable> cDcaPtBinsKa{"cDcaPtBinsKa", {0.0f, 0.3f, 0.6f, 1.0f, 2.0f, 1000.0f}, "Kaon pT bin edges for DCAxy cut"}; - - Configurable> cDcaXYBinsKa{"cDcaXYBinsKa", {0.025f, 0.018f, 0.012f, 0.008f, 0.004f}, "Kaon max |DCAxy| per pT bin (cm)"}; - Configurable isonlyQC{"isonlyQC", false, "only QC"}; - Configurable isDeepAngle{"isDeepAngle", false, "Deep Angle cut"}; - Configurable cfgDeepAngle{"cfgDeepAngle", 0.04, "Deep Angle cut value"}; - Configurable cKinCuts{"cKinCuts", false, "Kinematic Cuts for p-K pair opening angle"}; - Configurable cPrimaryTrack{"cPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cGlobalWoDCATrack{"cGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - Configurable cPVContributor{"cPVContributor", true, "PV contributor track selection"}; // PV Contriuibutor - - // PID Selections - Configurable cUseOnlyTOFTrackPr{"cUseOnlyTOFTrackPr", false, "Use only TOF track for PID selection"}; // Use only TOF track for Proton PID selection - Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; // Use only TOF track for Kaon PID selection - Configurable cUseTpcOnly{"cUseTpcOnly", false, "Use TPC Only selection"}; // TPC And TOF tracks - Configurable cRejNsigmaTpc{"cRejNsigmaTpc", 3.0, "Reject tracks to improve purity of TPC PID"}; // Reject missidentified particles when tpc bands merge - Configurable cRejNsigmaTpcPi{"cRejNsigmaTpcPi", 3.0, "Reject tracks to improve purity of TPC PID"}; // TPC And TOF tracks - // Configurable cRejNsigmaTpcPr{"cRejNsigmaTpcPr", 3.0, "Reject tracks to improve purity of TPC PID"}; - Configurable cRejNsigmaTpcKa{"cRejNsigmaTpcKa", 3.0, "Reject tracks to improve purity of TPC PID"}; - Configurable cRejNsigmakTpcPi{"cRejNsigmakTpcPi", 3.0, "Reject tracks to improve purity of TPC PID"}; - Configurable cRejNsigmakTpcPr{"cRejNsigmakTpcPr", 3.0, "Reject tracks to improve purity of TPC PID"}; - Configurable minnsigmatpcKa{"minnsigmatpcKa", -6.0, "Reject tracks to improve purity of TPC PID"}; - Configurable minnsigmatpcPr{"minnsigmatpcPr", -6.0, "Reject tracks to improve purity of TPC PID"}; - Configurable minnsigmatofKa{"minnsigmatofKa", -6.0, "Reject tracks to improve purity of TofPID"}; - Configurable minnsigmatofPr{"minnsigmatofPr", -6.0, "Reject tracks to improve purity of Tof PID"}; - Configurable minnsigmatpctofKa{"minnsigmatpctofKa", -6.0, "Reject tracks to improve purity of TPC PID"}; - Configurable minnsigmatpctofPr{"minnsigmatpctofPr", -6.0, "Reject tracks to improve purity of TPC PID"}; - // Configurable cRejNsigmaTpcPr{"cRejNsigmaTpcPr", 3.0, "Reject tracks to improve purity of TPC PID"}; - Configurable cRejNsigmaTpcVeto{"cRejNsigmaTpcVeto", 3.0, "Reject tracks to improve purity of TPC PID"}; // Reject missidentified particles when tpc bands merge - Configurable cRejNsigmaTof{"cRejNsigmaTof", 3.0, "Reject tracks to improve purity of TOF PID"}; // Reject missidentified particles when tpc bands merge - // Proton - Configurable cMaxTPCnSigmaProton{"cMaxTPCnSigmaProton", 3.0, "TPC nSigma cut for Proton"}; // TPC - // Configurable cMaxTOFnSigmaProton{"cMaxTOFnSigmaProton", 3.0, "TOF nSigma cut for Proton"}; // TOF - Configurable nsigmaCutCombinedProton{"nsigmaCutCombinedProton", 3.0, "Combined nSigma cut for Proton"}; // Combined - Configurable> protonTPCPIDp{"protonTPCPIDp", {0, 0.5, 0.7, 0.8}, "p dependent TPC cuts protons"}; - Configurable> protonTPCPIDcut{"protonTPCPIDcut", {5., 3.5, 2.5}, "TPC nsigma cuts protons"}; - Configurable> protonTOFPIDp{"protonTOFPIDp", {0., 999.}, "p dependent TOF cuts protons"}; - Configurable> protonTOFPIDcut{"protonTOFPIDcut", {3.0}, "TOF nsigma cuts protons"}; - // Kaon - Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon"}; // TPC - // Configurable cMaxTOFnSigmaKaon{"cMaxTOFnSigmaKaon", 3.0, "TOF nSigma cut for Kaon"}; // TOF - Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", 3.0, "Combined nSigma cut for Kaon"}; // Combined - Configurable> kaonTPCPIDp{"kaonTPCPIDp", {0., 0.25, 0.3, 0.45}, "pT dependent TPC cuts kaons"}; - Configurable> kaonTPCPIDcut{"kaonTPCPIDcut", {6, 3.5, 2.5}, "TPC nsigma cuts kaons"}; - Configurable> kaonTOFPIDp{"kaonTOFPIDp", {0., 999.}, "p dependent TOF cuts kaons"}; - Configurable> kaonTOFPIDcut{"kaonTOFPIDcut", {3.0}, "TOF nsigma cuts kaons"}; - // Event Mixing. - Configurable cNumMixEv{"cNumMixEv", 20, "Number of Events to be mixed"}; - ConfigurableAxis cDCAzBins{"dcazbins", {VARIABLE_WIDTH, -1.2f, -1.0f, -0.9f, -0.8f, -0.7f, -0.6f, -0.5f, -0.4f, -0.3f, -0.2f, -0.1f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.2f}, "DCA - z-vertex"}; - - ConfigurableAxis cMixVtxBins{"cMixVtxBins", {VARIABLE_WIDTH, -10.0f, -9.f, -8.f, -7.f, -6.f, -5.f, -4.f, -3.f, -2.f, -1.f, 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f}, "Mixing bins - z-vertex"}; - ConfigurableAxis cMixMultBins{"cMixMultBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 200.0f}, "Mixing bins - multiplicity"}; - ConfigurableAxis cMixEPAngle{"cMixEPAngle", {VARIABLE_WIDTH, -1.5708f, -1.25664f, -0.942478f, -0.628319f, 0.f, 0.628319f, 0.942478f, 1.25664f, 1.5708f}, "event plane"}; - ConfigurableAxis occupancy_bins{"occupancy_bins", {VARIABLE_WIDTH, 0.0, 100, 500, 600, 1000, 1100, 1500, 1600, 2000, 2100, 2500, 2600, 3000, 3100, 3500, 3600, 4000, 4100, 4500, 4600, 5000, 5100, 9999}, "Binning of the occupancy axis"}; - Configurable cNofRotations{"cNofRotations", 10, "Number of rotations for rotational background"}; - Configurable rotationalcut{"rotationalcut", 6.f, "Rotational background angle window: PI/rotationalcut"}; - - // ── MC Event Selection Configurables ───────────────────────────────────── - Configurable cEvtMCAfterAllCuts{"cEvtMCAfterAllCuts", false, "MC event sel: isInAfterAllCuts"}; - Configurable cEvtMCINELgt0{"cEvtMCINELgt0", false, "MC event sel: isINELgt0"}; - Configurable cEvtMCSel8{"cEvtMCSel8", false, "MC event sel: isInSel8"}; - Configurable cEvtMCVtxIn10{"cEvtMCVtxIn10", false, "MC event sel: isVtxIn10"}; - Configurable cEvtMCTriggerTVX{"cEvtMCTriggerTVX", false, "MC event sel: isTriggerTVX"}; - Configurable cEvtMCRecINELgt0{"cEvtMCRecINELgt0", false, "MC event sel: isRecINELgt0"}; - // Histogram Registry. - HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - - void init(InitContext const&) - { - const AxisSpec axisCent(110, 0, 110, "FT0 (%)"); - const AxisSpec axisP_pid(200, 0., 10., "p (GeV/c)"); - const AxisSpec axisPt_pid(200, 0., 10., "p_{T} (GeV/c)"); - const AxisSpec axisPt(nBinsPt, 0., 10., "p_{T} (GeV/c)"); - const AxisSpec axisEta(40, -1, 1, "#eta"); - // const AxisSpec axisDCAz(500, -0.5, 0.5, {"DCA_{z} (cm)"}); - const AxisSpec axisDCAxy(240, -0.12, 0.12, {"DCA_{xy} (cm)"}); - const AxisSpec axisTPCNCls(200, 0, 200, {"TPCNCls"}); - const AxisSpec axisTPCNsigma(401, -10.025, 10.025, {"n#sigma^{TPC}"}); - const AxisSpec axisTOFNsigma(401, -10.025, 10.025, {"n#sigma^{TOF}"}); - const AxisSpec axisdEdx(380, 10, 200, {"#frac{dE}{dx}"}); - const AxisSpec axisVz(120, -12, 12, {"vz"}); - const AxisSpec axisEP(120, -3.14, 3.14, {"#theta"}); - const AxisSpec axisInvM(nBinsInvM, 1.4, 2.0, {"M_{inv} (GeV/c^{2})"}); - AxisSpec axisOccupancy = {occupancy_bins, "Occupancy [-40,100]"}; - AxisSpec axisDCAz = {cDCAzBins, "DCA_{z} (cm)"}; - - histos.add("Event/h1d_ft0_mult_percentile", "FT0 (%)", kTH2F, {axisCent, axisOccupancy}); - histos.add("Event/h_ft0_vz", "Collision Vertex Z position", kTH1F, {{100, -15., 15.}}); - if (doprocessMix || doprocessMixDF || doprocessMixepDF) { - histos.add("Event/mixing_vzVsmultpercentile", "FT0(%)", kTH3F, {axisCent, axisVz, axisEP}); - } - // QA Beforei - histos.add("QAbefore/hEta_rec", "Reco dN/d#eta; #eta; dN/d#eta", kTH1F, {{50, -1.0, 1.0}}); - histos.add("QAbefore/hPt_rec", "Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {axisP_pid}); - histos.add("QAbefore/hPhi_rec", "Reco #varphi; #varphi (rad); Tracks", kTH1F, {{72, 0, 6.2832}}); - histos.add("QAbefore/hEtaPhi_rec", "Reco #eta vs #varphi; #eta; #varphi", kTH2F, {axisEta, {72, 0, 6.2832}}); - histos.add("QAbefore/Proton/h2d_pr_nsigma_tpc_p", "n#sigma^{TPC} Protons", kTH2F, {axisP_pid, axisTPCNsigma}); - histos.add("QAbefore/Proton/h2d_pr_nsigma_tof_p", "n#sigma^{TOF} Protons", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAbefore/Proton/h2d_pr_nsigma_tof_vs_tpc", "n#sigma^{TPC} vs n#sigma^{TOF} Protons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); - histos.add("QAbefore/Kaon/h2d_ka_nsigma_tpc_p", "n#sigma^{TPC} Kaons", kTH2F, {axisP_pid, axisTPCNsigma}); - histos.add("QAbefore/Kaon/h2d_ka_nsigma_tof_p", "n#sigma^{TOF} Kaons", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAbefore/Kaon/h2d_ka_nsigma_tof_vs_tpc", "n#sigma^{TPC} vs n#sigma^{TOF} Kaons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); - - // QA After - histos.add("QAafter/Proton/hd_pr_pt", "p_{T}-spectra Protons", kTH2F, {axisPt_pid, axisCent}); - histos.add("QAafter/Proton/h2d_pr_dca_z", "dca_{z} Protons", kTH2F, {axisPt_pid, axisDCAz}); - histos.add("QAafter/Proton/h2d_pr_dca_xy", "dca_{xy} Protons", kTH2F, {axisPt_pid, axisDCAxy}); - histos.add("QAafter/Proton/h2d_pr_dEdx_p", "TPC Signal Protons", kTH2F, {axisP_pid, axisdEdx}); - histos.add("QAafter/Proton/h2d_pr_nsigma_tpc_pt", " Protons", kTH2F, {axisPt_pid, axisTPCNsigma}); - histos.add("QAafter/Proton/h2d_Prpi_nsigma_tpc_p", " Protons pion", kTH2F, {axisPt_pid, axisTPCNsigma}); - histos.add("QAafter/Proton/h2d_Prka_nsigma_tpc_p", " Protons kaon", kTH2F, {axisPt_pid, axisTPCNsigma}); - histos.add("QAafter/Proton/h2d_pr_nsigma_tpc_p", " Protons", kTH2F, {axisP_pid, axisTPCNsigma}); - histos.add("QAafter/Proton/h2d_pr_nsigma_tof_pt", " Protons", kTH2F, {axisPt_pid, axisTOFNsigma}); - histos.add("QAafter/Proton/h2d_pr_nsigma_tof_p", " Protons", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAafter/Proton/h2d_Prpi_nsigma_tof_p", " Protons pion", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAafter/Proton/h2d_Prka_nsigma_tof_p", " Protons kaon", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAafter/Proton/h2d_pr_nsigma_tof_vs_tpc", "n#sigma(TOF) vs n#sigma(TPC) Protons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); - histos.add("QAafter/Proton/hTPCNClsCrossedRowsVsPt", "TPC Crossed Rows vs pT;p_{T} (GeV/c);N_{cls,crossed};Counts", kTH2F, {axisPt_pid, {200, 0, 200}}); - histos.add("QAafter/Proton/hTPCNClsFoundVsPt", "TPC Found Clusters vs pT;p_{T} (GeV/c);N_{cls,found};Counts", kTH2F, {axisPt_pid, {200, 0, 200}}); - histos.add("QAafter/Kaon/hd_ka_pt", "p_{T}-spectra Kaons", kTH2F, {axisPt_pid, axisCent}); - histos.add("QAafter/Kaon/h2d_ka_dca_z", "dca_{z} Kaons", kTH2F, {axisPt_pid, axisDCAz}); - histos.add("QAafter/Kaon/h2d_ka_dca_xy", "dca_{xy} Kaons", kTH2F, {axisPt_pid, axisDCAxy}); - histos.add("QAafter/Kaon/h2d_ka_dEdx_p", "TPC Signal Kaon", kTH2F, {axisP_pid, axisdEdx}); - histos.add("QAafter/Kaon/h2d_Kapi_nsigma_tpc_p", " Kaons pion", kTH2F, {axisPt_pid, axisTPCNsigma}); - histos.add("QAafter/Kaon/h2d_Kapr_nsigma_tpc_p", " Kaons proton", kTH2F, {axisP_pid, axisTPCNsigma}); - histos.add("QAafter/Kaon/h2d_ka_nsigma_tpc_pt", " Kaons", kTH2F, {axisPt_pid, axisTPCNsigma}); - histos.add("QAafter/Kaon/h2d_ka_nsigma_tpc_p", " Kaons", kTH2F, {axisP_pid, axisTPCNsigma}); - histos.add("QAafter/Kaon/h2d_ka_nsigma_tof_pt", " Kaons", kTH2F, {axisPt_pid, axisTOFNsigma}); - histos.add("QAafter/Kaon/h2d_ka_nsigma_tof_p", " Kaons", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAafter/Kaon/h2d_Kapi_nsigma_tof_p", " Kaons pion", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAafter/Kaon/h2d_Kapr_nsigma_tof_p", " Kaons proton", kTH2F, {axisP_pid, axisTOFNsigma}); - histos.add("QAafter/Kaon/h2d_ka_nsigma_tof_vs_tpc", "n#sigma(TOF) vs n#sigma(TPC) Kaons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); - histos.add("QAafter/Kaon/hTPCNClsCrossedRowsVsPt", "TPC Crossed Rows vs pT;p_{T} (GeV/c);N_{cls,crossed};Counts", kTH2F, {axisPt_pid, {200, 0, 200}}); - histos.add("QAafter/Kaon/hTPCNClsFoundVsPt", "TPC Found Clusters vs pT;p_{T} (GeV/c);N_{cls,found};Counts", kTH2F, {axisPt_pid, {200, 0, 200}}); - // Analysis - // Lambda Invariant Mass - if (!doprocessMC) { - histos.add("Analysis/h4d_lstar_invm_US_PM", "THn #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_US_MP", "THn #bar #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_PP", "THn Like Signs p K^{+}", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_MM", "THn Like Signs #bar{p} K^{-}", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_rot_PM", "THn Rotated", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_rot_MP", "THn Rotated", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_US_PM_mix", "THn Mixed Events", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_US_MP_mix", "THn anti Mixed Events", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_LS_PP_mix", "THn Mixed Events PP", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - histos.add("Analysis/h4d_lstar_invm_LS_MM_mix", "THn Mixed Events MM", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); - } - // MC - if (doprocessMC) { - histos.add("Event/hMCEventCutflow", "MC Event Cutflow", kTH1F, {{7, 0, 7}}); - histos.add("QAChecks/h1d_pr_rec_pt", "Reconstructed p_{T}-spectra Protons", kTH1F, {axisPt_pid}); - histos.add("QAChecks/h1d_ka_rec_pt", "Recondstucted p_{T}-spectra Kaons", kTH1F, {axisPt_pid}); - histos.add("QAChecks/h1d_pr_gen_pt", "Generated p_{T}-spectra Protons", kTH1F, {axisPt_pid}); - histos.add("QAChecks/h1d_ka_gen_pt", "Generated p_{T}-spectra Kaons", kTH1F, {axisPt_pid}); - - histos.add("Analysis/h3d_gen_lstar_PM", "Generated #Lambda(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); - histos.add("Analysis/h3d_gen_lstar_MP", "Generated #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); - histos.add("Analysis/h3d_rec_lstar_PM", "Reconstructed #Lambda(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); - histos.add("Analysis/h3d_rec_lstar_MP", "Reconstructed #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); - histos.add("Analysis/h3d_reso_lstar_PM", "Resolution #Lambda(1520) p_{T}", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCent}); - histos.add("Analysis/h3d_reso_lstar_MP", "Resolution #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCent}); - } - - if (doprocessMCGen) { - histos.add("SignalLoss/hMCEventCutflow", "MC Event Cutflow", kTH1F, {{7, 0, 7}}); - histos.add("SignalLoss/hGen_mT_scaled_Proton", "mT Scaled #Lambda(1520) from Proton", kTHnSparseF, {axisPt, axisCent}); - histos.add("SignalLoss/hGen_mT_scaled_AntiProton", "mT Scaled #bar{#Lambda}(1520) from AntiProton", kTHnSparseF, {axisPt, axisCent}); - - histos.add("SignalLoss/hGen_mT_scaled_Lambda0", "mT Scaled #Lambda(1520) from Lambda0", kTHnSparseF, {axisPt, axisCent}); - histos.add("SignalLoss/hGen_mT_scaled_AntiLambda0", "mT Scaled #bar{#Lambda}(1520) from AntiLambda0", kTHnSparseF, {axisPt, axisCent}); - - histos.add("SignalLoss/hGen_mT_scaled_XiMinus", "mT Scaled #Lambda(1520) from Xi-", kTHnSparseF, {axisPt, axisCent}); - histos.add("SignalLoss/hGen_mT_scaled_XiPlus", "mT Scaled #bar{#Lambda}(1520) from Xi+", kTHnSparseF, {axisPt, axisCent}); - - histos.add("SignalLoss/hGen_mT_scaled_Xi0", "mT Scaled #Lambda(1520) from Xi0", kTHnSparseF, {axisPt, axisCent}); - histos.add("SignalLoss/hGen_mT_scaled_AntiXi0", "mT Scaled #bar{#Lambda}(1520) from AntiXi0", kTHnSparseF, {axisPt, axisCent}); - - histos.add("SignalLoss/hGen_mT_scaled_OmegaMinus", "mT Scaled #Lambda(1520) from Omega-", kTHnSparseF, {axisPt, axisCent}); - histos.add("SignalLoss/hGen_mT_scaled_OmegaPlus", "mT Scaled #bar{#Lambda}(1520) from Omega+", kTHnSparseF, {axisPt, axisCent}); - } - } - - template - bool selTracks(T const& track) - { - if (track.pt() < cPtMin) - return false; - - if (std::abs(track.eta()) > cEtaCut) - return false; - - if (cPrimaryTrack && !track.isPrimaryTrack()) - return false; - - if (cGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) - return false; - - if (cPVContributor && !track.isPVContributor()) - return false; - - if (cfgUseCrossedRows && track.tpcNClsCrossedRows() < cfgMinCrossedRows) - return false; - - if (cfgUseTPCcls && track.tpcNClsFound() < cfgMinTPCcls) - return false; - - return true; - } - - template - bool dcaSelectionProton(T const& track, float p) - { - auto ptBinsPr = static_cast>(cDcaPtBinsPr); - auto dcaXYPr = static_cast>(cDcaXYBinsPr); - int nBinsPr = static_cast(ptBinsPr.size()) - 1; - - bool dcaXYPassed = false; - for (int i = 0; i < nBinsPr; i++) { - if (p >= ptBinsPr[i] && p < ptBinsPr[i + 1] && - std::abs(track.dcaXY()) < dcaXYPr[i]) - dcaXYPassed = true; - } - if (!dcaXYPassed) - return false; - - if (std::abs(track.dcaZ()) > cDcaz) - return false; - - return true; - } - - // ── Kaon DCA Selection ──────────────────────────────────────────────────── - template - bool dcaSelectionKaon(T const& track, float p) - { - auto ptBinsKa = static_cast>(cDcaPtBinsKa); - auto dcaXYKa = static_cast>(cDcaXYBinsKa); - int nBinsKa = static_cast(ptBinsKa.size()) - 1; - - bool dcaXYPassed = false; - for (int i = 0; i < nBinsKa; i++) { - if (p >= ptBinsKa[i] && p < ptBinsKa[i + 1] && - std::abs(track.dcaXY()) < dcaXYKa[i]) - dcaXYPassed = true; - } - if (!dcaXYPassed) - return false; - - if (std::abs(track.dcaZ()) > cDcaz) - return false; - - return true; - } - // PID selection tools - template - bool selectionPIDProton(const T& candidate, float p) - { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - auto tpcPIDp = static_cast>(protonTPCPIDp); - auto tpcPIDcut = static_cast>(protonTPCPIDcut); - auto tofPIDp = static_cast>(protonTOFPIDp); - auto tofPIDcut = static_cast>(protonTOFPIDcut); - int nitr = static_cast(tpcPIDp.size()); - int nitrtof = static_cast(tofPIDp.size()); - - float tpcNsigmaPi = std::abs(candidate.tpcNSigmaPi()); - float tpcNsigmaKa = std::abs(candidate.tpcNSigmaKa()); - float tpcNsigmaPr = std::abs(candidate.tpcNSigmaPr()); - float tofNsigmaPi = std::abs(candidate.tofNSigmaPi()); - float tofNsigmaKa = std::abs(candidate.tofNSigmaKa()); - float tofNsigmaPr = std::abs(candidate.tofNSigmaPr()); - - float tpcTofNsigmaPi = tpcNsigmaPi * tpcNsigmaPi + tofNsigmaPi * tofNsigmaPi; - float tpcTofNsigmaKa = tpcNsigmaKa * tpcNsigmaKa + tofNsigmaKa * tofNsigmaKa; - float tpcTofNsigmaPr = tpcNsigmaPr * tpcNsigmaPr + tofNsigmaPr * tofNsigmaPr; - float combinedCut = nsigmaCutCombinedProton * nsigmaCutCombinedProton; - float combinedRejCut = cRejNsigmaTof * cRejNsigmaTpc; - - if (!cUseTpcOnly && candidate.hasTOF()) { - if (candidate.tofNSigmaPr() < minnsigmatofPr) - return false; - if (nsigmaCutCombinedProton < 0 && p >= cPMin) { - - for (int i = 0; i < nitrtof - 1; ++i) { - if (p >= tofPIDp[i] && p < tofPIDp[i + 1] && (tofNsigmaPr < tofPIDcut[i] && tofNsigmaPi > cRejNsigmaTof && tofNsigmaKa > cRejNsigmaTof)) - tofPIDPassed = true; - } - if (candidate.tpcNSigmaPr() < minnsigmatpctofPr) - return false; - if (tpcNsigmaPr < cMaxTPCnSigmaProton && tpcNsigmaPi > cRejNsigmaTpcVeto && tpcNsigmaKa > cRejNsigmaTpcVeto) - tpcPIDPassed = true; - } - - // circular cut - if ((nsigmaCutCombinedProton > 0) && p >= cPMin && (tpcTofNsigmaPr < combinedCut && tpcTofNsigmaPi > combinedRejCut && tpcTofNsigmaKa > combinedRejCut)) { - tofPIDPassed = true; - tpcPIDPassed = true; - } - - if (p < cPMin && tpcNsigmaPr < cMaxTPCnSigmaProton) { - - tofPIDPassed = true; - tpcPIDPassed = true; - } - } else { - tofPIDPassed = true; - if (candidate.tpcNSigmaPr() < minnsigmatpcPr) - return false; - for (int i = 0; i < nitr - 1; ++i) { - if (p >= tpcPIDp[i] && p < tpcPIDp[i + 1] && (tpcNsigmaPr < tpcPIDcut[i] && tpcNsigmaPi > cRejNsigmaTpcPi && tpcNsigmaKa > cRejNsigmaTpcKa)) { - tpcPIDPassed = true; - } - } - } - if (tpcPIDPassed && tofPIDPassed) { - - return true; - } - return false; - } - template - bool selectionPIDKaon(const T& candidate, float p) - { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - auto tpcPIDp = static_cast>(kaonTPCPIDp); - auto tpcPIDcut = static_cast>(kaonTPCPIDcut); - auto tofPIDp = static_cast>(kaonTOFPIDp); - auto tofPIDcut = static_cast>(kaonTOFPIDcut); - int nitr = static_cast(tpcPIDp.size()); - int nitrtof = static_cast(tofPIDp.size()); - - float tpcNsigmaPi = std::abs(candidate.tpcNSigmaPi()); - float tpcNsigmaKa = std::abs(candidate.tpcNSigmaKa()); - float tpcNsigmaPr = std::abs(candidate.tpcNSigmaPr()); - float tofNsigmaPi = std::abs(candidate.tofNSigmaPi()); - float tofNsigmaKa = std::abs(candidate.tofNSigmaKa()); - float tofNsigmaPr = std::abs(candidate.tofNSigmaPr()); - - float tpcTofNsigmaPi = tpcNsigmaPi * tpcNsigmaPi + tofNsigmaPi * tofNsigmaPi; - float tpcTofNsigmaKa = tpcNsigmaKa * tpcNsigmaKa + tofNsigmaKa * tofNsigmaKa; - float tpcTofNsigmaPr = tpcNsigmaPr * tpcNsigmaPr + tofNsigmaPr * tofNsigmaPr; - float combinedCut = nsigmaCutCombinedKaon * nsigmaCutCombinedKaon; - float combinedRejCut = cRejNsigmaTpc * cRejNsigmaTof; - - if (!cUseTpcOnly && candidate.hasTOF()) { - if (candidate.tofNSigmaKa() < minnsigmatofKa) - return false; - if (nsigmaCutCombinedKaon < 0 && p >= cPMin) { - - for (int i = 0; i < nitrtof - 1; ++i) { - if (p >= tofPIDp[i] && p < tofPIDp[i + 1] && (tofNsigmaKa < tofPIDcut[i] && tofNsigmaPi > cRejNsigmaTof && tofNsigmaPr > cRejNsigmaTof)) - tofPIDPassed = true; - } - if (candidate.tpcNSigmaKa() < minnsigmatpctofKa) - return false; - if (tpcNsigmaKa < cMaxTPCnSigmaKaon && tpcNsigmaPi > cRejNsigmaTpcVeto && tpcNsigmaPr > cRejNsigmaTpcVeto) - tpcPIDPassed = true; - } - - // circular - if ((nsigmaCutCombinedKaon > 0) && p >= cPMin && (tpcTofNsigmaKa < combinedCut && tpcTofNsigmaPi > combinedRejCut && tpcTofNsigmaPr > combinedRejCut)) { - tofPIDPassed = true; - tpcPIDPassed = true; - } - - if (p < cPMin && tpcNsigmaKa < cMaxTPCnSigmaKaon) { - - tofPIDPassed = true; - tpcPIDPassed = true; - } - - } else { - tofPIDPassed = true; - if (candidate.tpcNSigmaKa() < minnsigmatpcKa) - return false; - for (int i = 0; i < nitr - 1; ++i) { - if (p >= tpcPIDp[i] && p < tpcPIDp[i + 1] && (tpcNsigmaKa < tpcPIDcut[i] && tpcNsigmaPi > cRejNsigmakTpcPi && tpcNsigmaPr > cRejNsigmakTpcPr)) { - tpcPIDPassed = true; - } - } - } - if (tpcPIDPassed && tofPIDPassed) { - - return true; - } - return false; - } - - template - void fillDataHistos(trackType const& trk1, trackType const& trk2, float mult, int occup = 100) - { - - float p_ptot = 0., k_ptot = 0.; - - for (auto const& [trkPr, trkKa] : soa::combinations(soa::CombinationsFullIndexPolicy(trk1, trk2))) { - // Do not analyse same index tracks. - if (trkPr.index() == trkKa.index()) // && !mix) - continue; - // pT, DCA, Global Tracks and PVcontrib selection. - if (!selTracks(trkPr) || !selTracks(trkKa)) - continue; - - // LOGF(info, "eork 4 %d, %d %d ",trkPr.index(),trk1.size(),trkPr.size()); - auto _pxPr = trkPr.px(); - auto _pyPr = trkPr.py(); - auto _pzPr = trkPr.pz(); - auto _pxKa = trkKa.px(); - auto _pyKa = trkKa.py(); - auto _pzKa = trkKa.pz(); - - p_ptot = TMath::Sqrt(_pxPr * _pxPr + _pyPr * _pyPr + _pzPr * _pzPr); - k_ptot = TMath::Sqrt(_pxKa * _pxKa + _pyKa * _pyKa + _pzKa * _pzKa); - - // Fill QA before track selection. - if (!mix) { - auto _tpcnsigmaPr = trkPr.tpcNSigmaPr(); - histos.fill(HIST("QAbefore/Proton/h2d_pr_nsigma_tpc_p"), p_ptot, _tpcnsigmaPr); - if (trkPr.hasTOF()) { - auto _tofnsigmaPr = trkPr.tofNSigmaPr(); - histos.fill(HIST("QAbefore/Proton/h2d_pr_nsigma_tof_p"), p_ptot, _tofnsigmaPr); - histos.fill(HIST("QAbefore/Proton/h2d_pr_nsigma_tof_vs_tpc"), _tpcnsigmaPr, _tofnsigmaPr); - } - auto _tpcnsigmaKa = trkKa.tpcNSigmaKa(); - histos.fill(HIST("QAbefore/Kaon/h2d_ka_nsigma_tpc_p"), k_ptot, _tpcnsigmaKa); - if (trkKa.hasTOF()) { - auto _tofnsigmaKa = trkKa.tofNSigmaKa(); - histos.fill(HIST("QAbefore/Kaon/h2d_ka_nsigma_tof_p"), k_ptot, _tofnsigmaKa); - histos.fill(HIST("QAbefore/Kaon/h2d_ka_nsigma_tof_vs_tpc"), _tpcnsigmaKa, _tofnsigmaKa); - } - } - - // Apply PID Selection - if (cUseOnlyTOFTrackPr && !trkPr.hasTOF()) - continue; - if (cUseOnlyTOFTrackKa && !trkKa.hasTOF()) - continue; - if (!selectionPIDProton(trkPr, p_ptot) || !selectionPIDKaon(trkKa, k_ptot)) - continue; - if (!dcaSelectionProton(trkPr, p_ptot) || !dcaSelectionKaon(trkKa, k_ptot)) - continue; - - if (isDeepAngle && TMath::ACos((trkPr.pt() * trkKa.pt() + _pzPr * _pzKa) / (p_ptot * k_ptot)) < cfgDeepAngle) - continue; - - // Fill QA after track selection. - if constexpr (!mix) { - auto _ptPr = trkPr.pt(); - auto _tpcnsigmaPr = trkPr.tpcNSigmaPr(); - - // Proton - histos.fill(HIST("QAafter/Proton/hd_pr_pt"), _ptPr, mult); - histos.fill(HIST("QAafter/Proton/h2d_pr_dca_z"), _ptPr, trkPr.dcaZ()); - histos.fill(HIST("QAafter/Proton/h2d_pr_dca_xy"), _ptPr, trkPr.dcaXY()); - histos.fill(HIST("QAafter/Proton/h2d_pr_dEdx_p"), p_ptot, trkPr.tpcSignal()); - histos.fill(HIST("QAafter/Proton/h2d_Prpi_nsigma_tpc_p"), p_ptot, trkPr.tpcNSigmaPi()); - histos.fill(HIST("QAafter/Proton/h2d_Prka_nsigma_tpc_p"), p_ptot, trkPr.tpcNSigmaKa()); - histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tpc_p"), p_ptot, _tpcnsigmaPr); - histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tpc_pt"), _ptPr, _tpcnsigmaPr); - histos.fill(HIST("QAafter/Proton/hTPCNClsCrossedRowsVsPt"), _ptPr, trkPr.tpcNClsCrossedRows()); - histos.fill(HIST("QAafter/Proton/hTPCNClsFoundVsPt"), _ptPr, trkPr.tpcNClsFound()); - - if (!cUseTpcOnly && trkPr.hasTOF()) { - auto _tofnsigmaPr = trkPr.tofNSigmaPr(); - histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tof_p"), p_ptot, _tofnsigmaPr); - histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tof_pt"), _ptPr, _tofnsigmaPr); - histos.fill(HIST("QAafter/Proton/h2d_Prpi_nsigma_tof_p"), p_ptot, trkPr.tofNSigmaPi()); - histos.fill(HIST("QAafter/Proton/h2d_Prka_nsigma_tof_p"), p_ptot, trkPr.tofNSigmaKa()); - histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tof_vs_tpc"), _tpcnsigmaPr, _tofnsigmaPr); - } - auto _ptKa = trkKa.pt(); - auto _tpcnsigmaKa = trkKa.tpcNSigmaKa(); - - // Kaon - histos.fill(HIST("QAafter/Kaon/hd_ka_pt"), _ptKa, mult); - histos.fill(HIST("QAafter/Kaon/h2d_ka_dca_z"), _ptKa, trkKa.dcaZ()); - histos.fill(HIST("QAafter/Kaon/h2d_ka_dca_xy"), _ptKa, trkKa.dcaXY()); - histos.fill(HIST("QAafter/Kaon/h2d_ka_dEdx_p"), k_ptot, trkKa.tpcSignal()); - histos.fill(HIST("QAafter/Kaon/h2d_Kapi_nsigma_tpc_p"), k_ptot, trkKa.tpcNSigmaPi()); - histos.fill(HIST("QAafter/Kaon/h2d_Kapr_nsigma_tpc_p"), k_ptot, trkKa.tpcNSigmaPr()); - histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tpc_p"), k_ptot, _tpcnsigmaKa); - histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tpc_pt"), _ptKa, _tpcnsigmaKa); - histos.fill(HIST("QAafter/Kaon/hTPCNClsCrossedRowsVsPt"), _ptKa, trkKa.tpcNClsCrossedRows()); - histos.fill(HIST("QAafter/Kaon/hTPCNClsFoundVsPt"), _ptKa, trkKa.tpcNClsFound()); - - if (!cUseTpcOnly && trkKa.hasTOF()) { - auto _tofnsigmaKa = trkKa.tofNSigmaKa(); - histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tof_p"), k_ptot, _tofnsigmaKa); - histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tof_pt"), _ptKa, _tofnsigmaKa); - histos.fill(HIST("QAafter/Kaon/h2d_Kapi_nsigma_tof_p"), k_ptot, trkKa.tofNSigmaPi()); - histos.fill(HIST("QAafter/Kaon/h2d_Kapr_nsigma_tof_p"), k_ptot, trkKa.tofNSigmaPr()); - histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tof_vs_tpc"), _tpcnsigmaKa, _tofnsigmaKa); - } - } - - if (isonlyQC) - continue; - - std::array pvec0 = {_pxPr, _pyPr, _pzPr}; - std::array pvec1 = {_pxKa, _pyKa, _pzKa}; - std::array, 2> arrMomrec = {pvec0, pvec1}; - - float _M = RecoDecay::m(arrMomrec, std::array{MassProton, MassKaonCharged}); - float _pt = RecoDecay::pt(std::array{_pxPr + _pxKa, _pyPr + _pyKa}); - - float _y = std::abs(RecoDecay::y(std::array{_pxPr + _pxKa, _pyPr + _pyKa, _pzPr + _pzKa}, _M)); - - float _yshift = _y - cfgRapidityShift; - - if (std::abs(_yshift) > cfgRapidityCut) - continue; - // Apply kinematic cuts. - - // Fill Invariant Mass Histograms. - if constexpr (!mix && !mc) { - if (trkPr.sign() * trkKa.sign() < 0) { - - if (trkPr.sign() > 0) - histos.fill(HIST("Analysis/h4d_lstar_invm_US_PM"), _M, _pt, mult, occup); - else - histos.fill(HIST("Analysis/h4d_lstar_invm_US_MP"), _M, _pt, mult, occup); - - if (doRotate) { - for (int i = 0; i < cNofRotations; i++) { - - float delta = o2::constants::math::PI / rotationalcut; - float theta2; - if (cNofRotations == 1) { - // Single rotation — just rotate by exactly PI - theta2 = o2::constants::math::PI; - } else { - theta2 = (o2::constants::math::PI - delta) + - i * (2.f * delta / (cNofRotations - 1)); - } - - float phiRot = trkKa.phi() + theta2; - if (phiRot > o2::constants::math::TwoPI) - phiRot -= o2::constants::math::TwoPI; - if (phiRot < 0.f) - phiRot += o2::constants::math::TwoPI; - - float _pxKaRot = trkKa.pt() * std::cos(phiRot); - float _pyKaRot = trkKa.pt() * std::sin(phiRot); - - std::array pvec0rot = {_pxPr, _pyPr, _pzPr}; - std::array pvec1rot = {_pxKaRot, _pyKaRot, _pzKa}; - std::array, 2> arrMomRot = {pvec0rot, pvec1rot}; - - float _Mrot = RecoDecay::m(arrMomRot, std::array{MassProton, MassKaonCharged}); - float _ptRot = RecoDecay::pt(std::array{_pxPr + _pxKaRot, _pyPr + _pyKaRot}); - - float _yrot = std::abs(RecoDecay::y(std::array{_pxPr + _pxKaRot, _pyPr + _pyKaRot, _pzPr + _pzKa}, MassLambda1520)); - - float _yshiftrot = _yrot - cfgRapidityShift; - - if (std::abs(_yshiftrot) > cfgRapidityCut) - continue; - - if (trkPr.sign() * trkKa.sign() < 0) { - if (trkPr.sign() > 0) - histos.fill(HIST("Analysis/h4d_lstar_invm_rot_PM"), _Mrot, _ptRot, mult, occup); - else - histos.fill(HIST("Analysis/h4d_lstar_invm_rot_MP"), _Mrot, _ptRot, mult, occup); - } - } - } - } else { - if (trkPr.sign() > 0) { - histos.fill(HIST("Analysis/h4d_lstar_invm_PP"), _M, _pt, mult, occup); - } else { - histos.fill(HIST("Analysis/h4d_lstar_invm_MM"), _M, _pt, mult, occup); - } - } - } - - if constexpr (mix) { - if (trkPr.sign() * trkKa.sign() < 0) { - if (trkPr.sign() > 0) - histos.fill(HIST("Analysis/h4d_lstar_invm_US_PM_mix"), _M, _pt, mult, occup); - else - histos.fill(HIST("Analysis/h4d_lstar_invm_US_MP_mix"), _M, _pt, mult, occup); - } else { - if (trkPr.sign() > 0) - histos.fill(HIST("Analysis/h4d_lstar_invm_LS_PP_mix"), _M, _pt, mult, occup); - else - histos.fill(HIST("Analysis/h4d_lstar_invm_LS_MM_mix"), _M, _pt, mult, occup); - } - } - if constexpr (mc) { - if (trkPr.sign() * trkKa.sign() < 0) { - if (std::abs(trkPr.pdgCode()) != 2212 || std::abs(trkKa.pdgCode()) != 321) - continue; - - if (trkPr.motherId() != trkKa.motherId()) - continue; - if (trkPr.motherPDG() != trkKa.motherPDG()) - continue; - - if (trkPr.pdgCode() == 0 || trkKa.pdgCode() == 0) - continue; - - if (trkPr.motherPDG() == -1 || trkKa.motherPDG() == -1) - continue; - - if (std::abs(trkPr.motherPDG()) != lambda1520id) // L* pdg_code = 3124 - continue; - - float massParent = 0.; - for (auto const& _part : *mResoParents) { - if (_part.mcParticleId() == trkPr.motherId()) { - std::array pvecParent = {_part.px(), _part.py(), _part.pz()}; - massParent = RecoDecay::m(pvecParent, _part.e()); - break; - } - } - - float _MRes = _M - massParent; - if (trkPr.motherPDG() > 0) { - histos.fill(HIST("Analysis/h3d_rec_lstar_PM"), _M, _pt, mult); - histos.fill(HIST("Analysis/h3d_reso_lstar_PM"), _MRes, _pt, mult); - } else { - histos.fill(HIST("Analysis/h3d_rec_lstar_MP"), _M, _pt, mult); - histos.fill(HIST("Analysis/h3d_reso_lstar_MP"), _MRes, _pt, mult); - } - } - } - } - } - - using resoCols = soa::Join; - using resoMCCols = soa::Join; - - using resoTracks = aod::ResoTracks; - - void processData(resoCols::iterator const& collision, resoTracks const& tracks) - { - // LOGF(info, " collisions: Index = %d %d", collision.globalIndex(),tracks.size()); - histos.fill(HIST("Event/h1d_ft0_mult_percentile"), collision.cent(), 100); - histos.fill(HIST("Event/h_ft0_vz"), collision.posZ()); - - fillDataHistos(tracks, tracks, collision.cent()); - } - - PROCESS_SWITCH(lambdaAnalysis_pb, processData, "Process for Same Event Data", true); - - void processMC(resoMCCols::iterator const& collision, - soa::Join const& tracks, aod::ResoMCParents const& resoParents) - { - histos.fill(HIST("Event/hMCEventCutflow"), 0); // All collisions - - if (cEvtMCTriggerTVX && !collision.isTriggerTVX()) - return; - histos.fill(HIST("Event/hMCEventCutflow"), 1); // After TriggerTVX - - if (cEvtMCVtxIn10 && !collision.isVtxIn10()) - return; - histos.fill(HIST("Event/hMCEventCutflow"), 2); // After VtxIn10 - - if (cEvtMCINELgt0 && !collision.isINELgt0()) - return; - histos.fill(HIST("Event/hMCEventCutflow"), 3); // After INELgt0 - - if (cEvtMCSel8 && !collision.isInSel8()) - return; - histos.fill(HIST("Event/hMCEventCutflow"), 4); // After Sel8 - - if (cEvtMCRecINELgt0 && !collision.isRecINELgt0()) - return; - histos.fill(HIST("Event/hMCEventCutflow"), 5); // After RecINELgt0 - - if (cEvtMCAfterAllCuts && !collision.isInAfterAllCuts()) - return; - histos.fill(HIST("Event/hMCEventCutflow"), 6); // After AfterAllCuts auto mult = collision.cent(); - - auto mult = collision.cent(); - histos.fill(HIST("Event/h1d_ft0_mult_percentile"), mult, 100); - histos.fill(HIST("Event/h_ft0_vz"), collision.posZ()); - mResoParents = &resoParents; - fillDataHistos(tracks, tracks, mult); - - // get MC pT-spectra - for (auto const& track : tracks) { - histos.fill(HIST("QAbefore/hEta_rec"), track.eta()); - histos.fill(HIST("QAbefore/hPt_rec"), track.pt()); - histos.fill(HIST("QAbefore/hPhi_rec"), track.phi()); - histos.fill(HIST("QAbefore/hEtaPhi_rec"), track.eta(), track.phi()); - // get the generated level pT spectra of protons and kaons - if (std::abs(track.pdgCode()) == 321) - histos.fill(HIST("QAChecks/h1d_ka_gen_pt"), track.pt()); - if (std::abs(track.pdgCode()) == 2212) - histos.fill(HIST("QAChecks/h1d_pr_gen_pt"), track.pt()); - // get the reconstructed level pT spectra of protons and kaons - if (!selTracks(track)) - continue; - - float p = TMath::Sqrt(track.px() * track.px() + track.py() * track.py() + track.pz() * track.pz()); - - if (selectionPIDKaon(track, p)) { - if (std::abs(track.pdgCode()) == 321) { - histos.fill(HIST("QAChecks/h1d_ka_rec_pt"), track.pt()); - } - } - - if (selectionPIDProton(track, p)) { - if (std::abs(track.pdgCode()) == 2212) { - histos.fill(HIST("QAChecks/h1d_pr_rec_pt"), track.pt()); - } - } - } - - for (auto const& part : resoParents) { - if (std::abs(part.pdgCode()) != lambda1520id) // // L* pdg_code = 3124 - continue; - - float yshift = std::abs(part.y()) - cfgRapidityShift; - - if (std::abs(yshift) > cfgRapidityCut) - continue; - bool pass1 = false; - bool pass2 = false; - - if (abs(part.daughterPDG1()) == 2212 || abs(part.daughterPDG2()) == 2212) { // At least one decay to Proton - pass1 = true; - } - if (abs(part.daughterPDG1()) == 321 || abs(part.daughterPDG2()) == 321) { // At least one decay to Kaon - pass2 = true; - } - - if (!pass1 || !pass2) // If we have both decay products - continue; - std::array pvec = {part.px(), part.py(), part.pz()}; - float mass = RecoDecay::m(pvec, part.e()); - if (part.pdgCode() > 0) - histos.fill(HIST("Analysis/h3d_gen_lstar_PM"), mass, part.pt(), mult); - else - histos.fill(HIST("Analysis/h3d_gen_lstar_MP"), mass, part.pt(), mult); - } - } - PROCESS_SWITCH(lambdaAnalysis_pb, processMC, "Process Event for MC", false); - - void processMCGen(resoMCCols::iterator const& collision, - aod::ResoMCParents const& resoParents) - { - - float centrality = collision.cent(); - - histos.fill(HIST("SignalLoss/hMCEventCutflow"), 0); // All collisions - - if (cEvtMCTriggerTVX && !collision.isTriggerTVX()) - return; - histos.fill(HIST("SignalLoss/hMCEventCutflow"), 1); // After TriggerTVX - - if (cEvtMCVtxIn10 && !collision.isVtxIn10()) - return; - histos.fill(HIST("SignalLoss/hMCEventCutflow"), 2); // After VtxIn10 - - if (cEvtMCINELgt0 && !collision.isINELgt0()) - return; - histos.fill(HIST("SignalLoss/hMCEventCutflow"), 3); // After INELgt0 - - if (cEvtMCSel8 && !collision.isInSel8()) - return; - histos.fill(HIST("SignalLoss/hMCEventCutflow"), 4); // After Sel8 - - if (cEvtMCRecINELgt0 && !collision.isRecINELgt0()) - return; - histos.fill(HIST("SignalLoss/hMCEventCutflow"), 5); // After RecINELgt0 - - if (cEvtMCAfterAllCuts && !collision.isInAfterAllCuts()) - return; - histos.fill(HIST("SignalLoss/hMCEventCutflow"), 6); // After AfterAllCuts - - for (auto const& part : resoParents) { - - float yshift = std::abs(part.y()) - cfgRapidityShift; - - if (std::abs(yshift) > cfgRapidityCut) - continue; - - int pdg = part.pdgCode(); - float ptRef = part.pt(); - double ptSq = -1.0; - - std::array pvec = {part.px(), part.py(), part.pz()}; - float mass = RecoDecay::m(pvec, part.e()); - - if (pdg == 2212) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_Proton"), std::sqrt(ptSq), centrality); - } else if (pdg == -2212) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_AntiProton"), std::sqrt(ptSq), centrality); - } else if (pdg == 3122) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_Lambda0"), std::sqrt(ptSq), centrality); - } else if (pdg == -3122) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_AntiLambda0"), std::sqrt(ptSq), centrality); - } else if (pdg == 3312) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_XiMinus"), std::sqrt(ptSq), centrality); - } else if (pdg == -3312) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_XiPlus"), std::sqrt(ptSq), centrality); - } else if (pdg == 3322) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_Xi0"), std::sqrt(ptSq), centrality); - } else if (pdg == -3322) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_AntiXi0"), std::sqrt(ptSq), centrality); - } else if (pdg == 3334) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_OmegaMinus"), std::sqrt(ptSq), centrality); - } else if (pdg == -3334) { - ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); - if (ptSq > 0) - histos.fill(HIST("SignalLoss/hGen_mT_scaled_OmegaPlus"), std::sqrt(ptSq), centrality); - } - } - } - - PROCESS_SWITCH(lambdaAnalysis_pb, processMCGen, "Process Event for MC", false); - - using BinningType2 = ColumnBinningPolicy; - - void processMix(resoCols& collisions, resoTracks const& tracks) - { - LOGF(debug, "Event Mixing Started"); - - BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}, true}; - auto tracksTuple = std::make_tuple(tracks); - - SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - for (auto& [c1, t1, c2, t2] : pairs) { - // LOGF(info, "processMCMixedDerived: Mixed collisions : %d (%.3f, %.3f,%d), %d (%.3f, %.3f,%d)",c1.globalIndex(), c1.posZ(), c1.cent(),c1.mult(), c2.globalIndex(), c2.posZ(), c2.cent(),c2.mult()); - histos.fill(HIST("Event/mixing_vzVsmultpercentile"), c1.cent(), c1.posZ(), c1.evtPl()); - fillDataHistos(t1, t2, c1.cent()); - } - } - - PROCESS_SWITCH(lambdaAnalysis_pb, processMix, "Process for Mixed Events", true); - - Preslice perRColdf = aod::resodaughter::resoCollisionDFId; - - using resoColDFs = aod::ResoCollisionDFs; - using resoTrackDFs = aod::ResoTrackDFs; - - void processDatadf(resoColDFs::iterator const& collision, resoTrackDFs const& tracks) - { - - if (doprocessData) - LOG(error) << "Disable processData() first!"; - auto _occup = 100; - if (ConfEvtOccupancyInTimeRange) - _occup = collision.trackOccupancyInTimeRange(); - - // LOGF(info, "inside df collisions: Index = %d %d", collision.globalIndex(),tracks.size()); - histos.fill(HIST("Event/h1d_ft0_mult_percentile"), collision.cent(), _occup); - fillDataHistos(tracks, tracks, collision.cent(), _occup); - } - - PROCESS_SWITCH(lambdaAnalysis_pb, processDatadf, "Process for data merged DF", false); - - using BinningTypeDF = ColumnBinningPolicy; - void processMixDF(resoColDFs& collisions, resoTrackDFs const& tracks) - { - if (doprocessMix) - LOG(fatal) << "Disable processMix() first!"; - LOGF(debug, "Event Mixing Started"); - - BinningTypeDF binningPositions2{{cMixVtxBins, cMixMultBins}, true}; - auto tracksTuple = std::make_tuple(tracks); - - SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - for (auto& [c1, t1, c2, t2] : pairs) { - auto _occup = 100; - if (ConfEvtOccupancyInTimeRange) - _occup = c1.trackOccupancyInTimeRange(); - - // LOGF(info, "processMCMixedDerived: Mixed collisions : %d (%.3f, %.3f,%d), %d (%.3f, %.3f,%d)",c1.globalIndex(), c1.posZ(), c1.cent(),c1.mult(), c2.globalIndex(), c2.posZ(), c2.cent(),c2.mult()); - histos.fill(HIST("Event/mixing_vzVsmultpercentile"), c1.cent(), c1.posZ(), c1.evtPl()); - fillDataHistos(t1, t2, c1.cent(), _occup); - } - } - - PROCESS_SWITCH(lambdaAnalysis_pb, processMixDF, "Process for merged DF Mixed Events", false); - - using BinningTypeEP = ColumnBinningPolicy; - void processMixepDF(resoColDFs& collisions, resoTrackDFs const& tracks) - { - if (doprocessMix || doprocessMixDF) - LOG(fatal) << "Disable processMix() or processMixDF() first!"; - LOGF(debug, "Event Mixing Started"); - BinningTypeEP binningPositions2{{cMixVtxBins, cMixMultBins, cMixEPAngle}, true}; - auto tracksTuple = std::make_tuple(tracks); - - SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip - for (auto& [c1, t1, c2, t2] : pairs) { - // LOGF(info, "processMCMixedDerived: Mixed collisions : %d (%.3f, %.3f,%.3f), %d (%.3f, %.3f, %.3f)",c1.globalIndex(), c1.posZ(), c1.cent(),c1.evtPl(), c2.globalIndex(), c2.posZ(), c2.cent(),c2.evtPl()); - histos.fill(HIST("Event/mixing_vzVsmultpercentile"), c1.cent(), c1.posZ(), c1.evtPl()); - fillDataHistos(t1, t2, c1.cent()); - } - } - - PROCESS_SWITCH(lambdaAnalysis_pb, processMixepDF, "Process for merged DF Mixed Events", false); -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{adaptAnalysisTask(cfgc)}; -} diff --git a/PWGLF/Tasks/Resonances/lambda1520analysis.cxx b/PWGLF/Tasks/Resonances/lambda1520analysis.cxx index d09b4f18dda..b59bbf3a1d2 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysis.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysis.cxx @@ -356,7 +356,7 @@ struct Lambda1520analysis { double massPr = MassProton; template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -1107,7 +1107,7 @@ struct Lambda1520analysis { void processME(o2::aod::ResoCollisions const& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx b/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx index 77a0d4403b1..413690fc58c 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysisinOO.cxx @@ -438,7 +438,7 @@ struct Lstaranalysis { // Centralicity estimator selection template - float centEst(ResoColl ResoEvents) + float centEst(const ResoColl& ResoEvents) { float returnValue = -999.0; switch (multEstimator) { @@ -477,7 +477,7 @@ struct Lstaranalysis { } template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -1154,7 +1154,7 @@ struct Lstaranalysis { // Processing Event Mixing using BinningTypeVtxZT0M = ColumnBinningPolicy; - BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{cfgVtxBins, cfgMultBins}}; void processME(EventCandidates const& collision, TrackCandidates const& tracks) diff --git a/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx b/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx index 9ecfda3ff5e..20f2d5dd2ca 100644 --- a/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx +++ b/PWGLF/Tasks/Resonances/lambda1520analysisinpp.cxx @@ -95,7 +95,7 @@ struct Lambda1520analysisinpp { HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Service pdg; + Service pdg{}; RCTFlagsChecker rctChecker; struct : ConfigurableGroup { @@ -203,6 +203,13 @@ struct Lambda1520analysisinpp { Configurable cFill1DQAs{"cFill1DQAs", false, "Invariant mass 1D"}; Configurable centEstimator{"centEstimator", 0, "Select centrality estimator: 0 - FT0M, 1 - FT0A, 2 - FT0C"}; + // calibration + Configurable> cfgGenMultCuts{"cfgGenMultCuts", std::vector{500, 300, 200, 120, 80, 50, 30, 10, 0}, "Generated multiplicity lower cuts"}; + Configurable> cfgCentBinCentres{"cfgCentBinCentres", std::vector{2.5f, 7.5f, 15.0f, 25.0f, 35.0f, 45.0f, 55.0f, 75.0f, 95.0f}, "Reco centrality bin centres"}; + Configurable cfgGenMultEtaMax{"cfgGenMultEtaMax", 0.5f, "Max Eta for generated mid-rapidity multiplicity"}; + Configurable cfgGenMultEtaMin{"cfgGenMultEtaMin", -0.5f, "Min Eta for generated mid-rapidity multiplicity"}; + Configurable useManualCalibration{"useManualCalibration", false, "Use Manual generated multiplicity for centrality estimation"}; + TRandom* rn = new TRandom(); // Pre-filters for efficient process @@ -487,12 +494,13 @@ struct Lambda1520analysisinpp { float massKa = MassKaonCharged; float massPr = MassProton; + static constexpr float kInvalidCentrality = -999.0f; // Centralicity estimator selection template - float centEst(Coll collisions) + float centEst(const Coll& collisions) { - float returnValue = -999.0f; + float returnValue = kInvalidCentrality; switch (centEstimator) { case 0: returnValue = collisions.centFT0M(); @@ -514,68 +522,89 @@ struct Lambda1520analysisinpp { bool isSelected(const Coll& collision, bool fillHist = true) { auto applyCut = [&](bool enabled, bool condition, int bin) { - if (!enabled) + if (!enabled) { return true; - if (!condition) + } + if (!condition) { return false; - if (fillHist) + } + if (fillHist) { histos.fill(HIST("CollCutCounts"), bin); + } return true; }; - if (fillHist) + if (fillHist) { histos.fill(HIST("CollCutCounts"), 0); + } - if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { return false; + } if (!applyCut(configEvents.cfgEvtTriggerTVXSel, - collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { return false; + } if (!applyCut(configEvents.cfgEvtNoTFBorderCut, - collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { return false; + } if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, - collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { return false; + } - if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) + if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) { return false; + } - if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) { return false; + } - if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) + if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) { return false; + } - if (fillHist) + if (fillHist) { histos.fill(HIST("CollCutCounts"), 8); + } return true; } template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (configTracks.cDCAr7SigCut && std::abs(track.dcaXY()) > (0.004f + 0.013f / (track.pt()))) // 7 - Sigma cut + { return false; - if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) + } + if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) { return false; - if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) + } + if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) { return false; - if (configTracks.cfgHasTOF && !track.hasTOF()) + } + if (configTracks.cfgHasTOF && !track.hasTOF()) { return false; - if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) + } + if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) { return false; - if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) + } + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return false; - if (configTracks.cfgPVContributor && !track.isPVContributor()) + } + if (configTracks.cfgPVContributor && !track.isPVContributor()) { return false; - if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) + } + if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) { return false; + } return true; } @@ -604,14 +633,17 @@ struct Lambda1520analysisinpp { // TPC PID (interval check) for (size_t i = 0; i < vProtonTPCPIDpTintv.size() - 1; ++i) { if (pt > vProtonTPCPIDpTintv[i] && pt < vProtonTPCPIDpTintv[i + 1]) { - if (std::abs(tpcNsigmaPr) < vProtonTPCPIDcuts[i]) + if (std::abs(tpcNsigmaPr) < vProtonTPCPIDcuts[i]) { tpcPIDPassed = true; + } } } // TOF bypass option (for QA or MC) if (configPID.cByPassTOF) { - return std::abs(tpcNsigmaPr) < vProtonTPCPIDcuts.back(); + { + return std::abs(tpcNsigmaPr) < vProtonTPCPIDcuts.back(); + } } // Case 1: No TOF and pt ≤ threshold → accept only via TPC PID @@ -631,8 +663,9 @@ struct Lambda1520analysisinpp { for (size_t i = 0; i < vProtonTOFPIDpTintv.size(); ++i) { if (pt < vProtonTOFPIDpTintv[i]) { if (std::abs(tofNsigmaPr) < vProtonTOFPIDcuts[i] && - std::abs(tpcNsigmaPr) < vProtonTPCPIDcuts.back()) + std::abs(tpcNsigmaPr) < vProtonTPCPIDcuts.back()) { return true; + } } } } else if (configPID.cPIDcutType == CircularType) { @@ -642,8 +675,9 @@ struct Lambda1520analysisinpp { float combinedSigma2 = tpcNsigmaPr * tpcNsigmaPr + tofNsigmaPr * tofNsigmaPr; - if (combinedSigma2 < vProtonTPCTOFCombinedPIDcuts[i] * vProtonTPCTOFCombinedPIDcuts[i]) + if (combinedSigma2 < vProtonTPCTOFCombinedPIDcuts[i] * vProtonTPCTOFCombinedPIDcuts[i]) { return true; + } } } } @@ -731,10 +765,7 @@ struct Lambda1520analysisinpp { template bool rejectPion(const T& candidate) { - if (candidate.pt() > MinPtforPionRejection && candidate.pt() < MaxPtforPionRejection && !candidate.hasTOF() && candidate.tpcNSigmaPi() < MaxnSigmaforPionRejection) { - return false; - } - return true; + return !(candidate.pt() > MinPtforPionRejection && candidate.pt() < MaxPtforPionRejection && !candidate.hasTOF() && candidate.tpcNSigmaPi() < MaxnSigmaforPionRejection); } auto static constexpr MaxNoLambda1520Daughters = 2; @@ -767,12 +798,14 @@ struct Lambda1520analysisinpp { for (const auto& [trk1, trk2] : combinations(CombinationsFullIndexPolicy(dTracks1, dTracks2))) { // Full index policy is needed to consider all possible combinations - if (trk1.index() == trk2.index()) - continue; // We need to run (0,1), (1,0) pairs as well. but same id pairs are not needed. + if (trk1.index() == trk2.index()) { + continue; + } // We need to run (0,1), (1,0) pairs as well. but same id pairs are not needed. // apply the track cut - if (!trackCut(trk1) || !trackCut(trk2)) + if (!trackCut(trk1) || !trackCut(trk2)) { continue; + } //// Initialize variables // Trk1: Proton @@ -798,10 +831,12 @@ struct Lambda1520analysisinpp { deltaEta = std::abs(trk1etaPr - trk2etaKa); deltaPhi = std::abs(trk1phiPr - trk2phiKa); deltaPhi = (deltaPhi > o2::constants::math::PI) ? (o2::constants::math::TwoPI - deltaPhi) : deltaPhi; - if (deltaEta >= cMaxDeltaEtaCut) + if (deltaEta >= cMaxDeltaEtaCut) { continue; - if (deltaPhi >= cMaxDeltaPhiCut) + } + if (deltaPhi >= cMaxDeltaPhiCut) { continue; + } } //// QA plots before the selection @@ -839,10 +874,13 @@ struct Lambda1520analysisinpp { //// Apply the pid selection if (crejectPion && rejectPion(trk2)) // to remove pion contamination from the kaon track + { continue; + } - if (!ptDependentPidProton(trk1) || !ptDependentPidKaon(trk2)) + if (!ptDependentPidProton(trk1) || !ptDependentPidKaon(trk2)) { continue; + } //// QA plots after the selection if constexpr (IsData) { // --- PID QA Proton @@ -896,8 +934,9 @@ struct Lambda1520analysisinpp { auto v1 = lDecayDaughter1.Vect(); auto v2 = lDecayDaughter2.Vect(); float alpha = std::acos(v1.Dot(v2) / (v1.R() * v2.R())); - if (alpha > cMinOpeningAngle && alpha < cMaxOpeningAngle) + if (alpha > cMinOpeningAngle && alpha < cMaxOpeningAngle) { continue; + } } lResonance = lDecayDaughter1 + lDecayDaughter2; @@ -908,15 +947,20 @@ struct Lambda1520analysisinpp { if constexpr (IsData || IsMix) { // Rapidity cut - if (std::abs(resonanceRapidity) > configTracks.cfgCutRapidity) + if (std::abs(resonanceRapidity) > configTracks.cfgCutRapidity) { continue; + } } if (cfgUseCutsOnMother) { if (resonancePt >= cMaxPtMotherCut) // excluding candidates in overflow + { continue; + } if (resonanceMass >= cMaxMinvMotherCut) // excluding candidates in overflow + { continue; + } } if (cFilladditionalQAeventPlots) { @@ -943,14 +987,19 @@ struct Lambda1520analysisinpp { auto resonanceRotPt = lResonanceRot.Pt(); // Rapidity cut - if (std::abs(lResonanceRot.Rapidity()) >= configTracks.cfgCutRapidity) + if (std::abs(lResonanceRot.Rapidity()) >= configTracks.cfgCutRapidity) { continue; + } if (cfgUseCutsOnMother) { if (resonanceRotPt >= cMaxPtMotherCut) // excluding candidates in overflow + { continue; + } if (resonanceRotMass >= cMaxMinvMotherCut) // excluding candidates in overflow + { continue; + } } if (trk1.sign() < 0) { if (cFill1DQAs) { @@ -1040,22 +1089,30 @@ struct Lambda1520analysisinpp { mothersPDGtrk2.pop_back(); } - if (std::abs(mctrk1.pdgCode()) != kProton || std::abs(mctrk2.pdgCode()) != kKPlus) + if (std::abs(mctrk1.pdgCode()) != kProton || std::abs(mctrk2.pdgCode()) != kKPlus) { continue; + } if (motherstrk1[0] != motherstrk2[0]) // Same mother + { continue; + } - if (std::abs(mothersPDGtrk1[0]) != Pdg::kLambda1520_Py) + if (std::abs(mothersPDGtrk1[0]) != Pdg::kLambda1520_Py) { continue; + } // LOGF(info, "mother trk1 id: %d, mother trk1: %d, trk1 id: %d, trk1 pdgcode: %d, mother trk2 id: %d, mother trk2: %d, trk2 id: %d, trk2 pdgcode: %d", motherstrk1[0], mothersPDGtrk1[0], trk1.globalIndex(), mctrk1.pdgCode(), motherstrk2[0], mothersPDGtrk2[0], trk2.globalIndex(), mctrk2.pdgCode()); if (cUseEtacutMC && std::abs(lResonance.Eta()) > cEtacutMC) // eta cut + { continue; + } if (cUseRapcutMC && std::abs(resonanceRapidity) > configTracks.cfgCutRapidity) // rapidity cut + { continue; + } histos.fill(HIST("QA/MC/h2RecoEtaPt_after"), lResonance.Eta(), resonancePt); histos.fill(HIST("QA/MC/h2RecoPhiRapidity_after"), lResonance.Phi(), resonanceRapidity); @@ -1116,11 +1173,47 @@ struct Lambda1520analysisinpp { } } + // Helper 1: Calculate generated mid-rapidity multiplicity + template + int getGenMidRapMultiplicity(McPartsT const& partsThisMc) + { + int nCh = 0; + for (auto const& part : partsThisMc) { + if (!part.isPhysicalPrimary()) { + continue; + } + if (part.eta() > cfgGenMultEtaMax || part.eta() < cfgGenMultEtaMin) { + continue; + } + auto pdgParticle = pdg->GetParticle(part.pdgCode()); + if (!pdgParticle || pdgParticle->Charge() == 0) { + continue; + } + nCh++; + } + return nCh; + } + + // Helper 2: Map multiplicity count to a specific centrality bin + float getCentClassFromGenMult(int nCh) + { + const auto& cuts = cfgGenMultCuts.value; + const auto& centres = cfgCentBinCentres.value; + for (size_t i = 0; i < cuts.size(); ++i) { + if (nCh >= cuts[i]) { + return centres[i]; + } + } + return kInvalidCentrality; // Invalid centrality + } + void processData(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { if (!isSelected(collision)) // Default event selection + { return; + } auto centrality = centEst(collision); @@ -1134,10 +1227,14 @@ struct Lambda1520analysisinpp { void processRotational(EventCandidates::iterator const& collision, TrackCandidates const& tracks) { if (!isSelected(collision, false)) // Default event selection + { return; + } if (!collision.isInelGt0()) // <-- + { return; + } fillHistograms(collision, tracks, tracks); } @@ -1151,7 +1248,7 @@ struct Lambda1520analysisinpp { { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}, true}; + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { @@ -1162,16 +1259,24 @@ struct Lambda1520analysisinpp { // } if (!isSelected(collision1, false)) // Default event selection + { continue; + } if (!isSelected(collision2, false)) // Default event selection + { continue; + } if (!collision1.isInelGt0()) // <-- + { continue; + } if (!collision2.isInelGt0()) // <-- + { continue; + } if (cFilladditionalQAeventPlots) { // Fill histograms for the characteristics of the *mixed* events (collision1 and collision2) @@ -1196,11 +1301,13 @@ struct Lambda1520analysisinpp { aod::McCollisions const&, MCTrackCandidates const& tracks, aod::McParticles const&) { - if (!collision.has_mcCollision()) + if (!collision.has_mcCollision()) { return; + } - if (!isSelected(collision)) + if (!isSelected(collision)) { return; + } auto centrality = centEst(collision); @@ -1215,11 +1322,12 @@ struct Lambda1520analysisinpp { void processMCGen(MCEventCandidates::iterator const& collision, aod::McCollisions const&, aod::McParticles const& mcParticles) { - if (!collision.has_mcCollision()) + if (!collision.has_mcCollision()) { return; + } bool isInAfterAllCuts = isSelected(collision, false); - bool inVtx10 = (std::abs(collision.mcCollision().posZ()) > configEvents.cfgEvtZvtx) ? false : true; + bool inVtx10 = std::abs(collision.mcCollision().posZ()) <= configEvents.cfgEvtZvtx; bool isTriggerTVX = collision.selection_bit(aod::evsel::kIsTriggerTVX); bool isSel8 = collision.sel8(); @@ -1247,8 +1355,9 @@ struct Lambda1520analysisinpp { bool pass2 = std::abs(daughterPDGs[0]) == kProton || std::abs(daughterPDGs[1]) == kProton; // At least one decay to Proton // Checking if we have both decay products - if (!pass1 || !pass2) + if (!pass1 || !pass2) { continue; + } // LOGF(info, "Part PDG: %d", part.pdgCode(), "DAU_ID1: %d", pass1, "DAU_ID2: %d", pass2); @@ -1256,12 +1365,15 @@ struct Lambda1520analysisinpp { histos.fill(HIST("QA/MC/h2GenPhiRapidity_beforeanycut"), part.phi(), part.y()); if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) // rapidity cut + { continue; + } if (cfgUseDaughterEtaCutMC) { for (auto const& daughters : part.daughters_as()) { - if (std::fabs(daughters.eta()) > configTracks.cfgCutEta) - continue; // eta cut for daughters + if (std::fabs(daughters.eta()) > configTracks.cfgCutEta) { + continue; + } // eta cut for daughters } // loop over daughters } @@ -1269,64 +1381,74 @@ struct Lambda1520analysisinpp { histos.fill(HIST("QA/MC/h2GenPhiRapidity_afterRapcut"), part.phi(), part.y()); if (cUseEtacutMC && std::abs(part.eta()) > cEtacutMC) // eta cut + { continue; + } histos.fill(HIST("QA/MC/h2GenEtaPt_afterEtaRapCut"), part.eta(), part.pt()); histos.fill(HIST("QA/MC/h2GenPhiRapidity_afterEtaRapCut"), part.phi(), part.y()); histos.fill(HIST("QA/Gen"), 1); if (part.pdgCode() > 0) // without any event selection + { histos.fill(HIST("Result/MC/Genlambda1520pt"), 0, part.pt(), centrality); - else + } else { histos.fill(HIST("Result/MC/Genantilambda1520pt"), 0, part.pt(), centrality); + } if (inVtx10) // vtx10 { histos.fill(HIST("QA/Gen"), 2); - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("Result/MC/Genlambda1520pt"), 1, part.pt(), centrality); - else + } else { histos.fill(HIST("Result/MC/Genantilambda1520pt"), 1, part.pt(), centrality); + } } if (inVtx10 && isSel8) // vtx10, sel8 { histos.fill(HIST("QA/Gen"), 3); - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("Result/MC/Genlambda1520pt"), 2, part.pt(), centrality); - else + } else { histos.fill(HIST("Result/MC/Genantilambda1520pt"), 2, part.pt(), centrality); + } } if (inVtx10 && isTriggerTVX) // vtx10, TriggerTVX { histos.fill(HIST("QA/Gen"), 4); - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("Result/MC/Genlambda1520pt"), 3, part.pt(), centrality); - else + } else { histos.fill(HIST("Result/MC/Genantilambda1520pt"), 3, part.pt(), centrality); + } } if (inVtx10 && isTrueINELgt0) // vtx10, INEL>0 { histos.fill(HIST("QA/Gen"), 5); - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("Result/MC/Genlambda1520pt"), 4, part.pt(), centrality); - else + } else { histos.fill(HIST("Result/MC/Genantilambda1520pt"), 4, part.pt(), centrality); + } } if (isInAfterAllCuts) // after all event selection { histos.fill(HIST("QA/Gen"), 6); - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("Result/MC/Genlambda1520pt"), 5, part.pt(), centrality); - else + } else { histos.fill(HIST("Result/MC/Genantilambda1520pt"), 5, part.pt(), centrality); + } } if (isInAfterAllCuts && isTrueINELgt0) // after all event selection && INEL>0 { histos.fill(HIST("QA/Gen"), 7); - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("Result/MC/Genlambda1520pt"), 6, part.pt(), centrality); - else + } else { histos.fill(HIST("Result/MC/Genantilambda1520pt"), 6, part.pt(), centrality); + } } } } @@ -1345,27 +1467,44 @@ struct Lambda1520analysisinpp { bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); bool isInAfterAllCuts = isSelected(collision, false); - float centrality = mcCollision.centFT0M(); + float centrality = useManualCalibration + ? getCentClassFromGenMult(getGenMidRapMultiplicity(particlesInCollision)) + : mcCollision.centFT0M(); - if (isTrueINELgt0 && isInAfterAllCuts) + if (centrality == kInvalidCentrality) { + continue; + } + + if (isTrueINELgt0 && isInAfterAllCuts) { histos.fill(HIST("Event/MultiplicityRecoEv"), centrality); + } } // Loop on generated collisions to fill the event factor for the INEL>0 correction for (const auto& mccolls : mcCollisions) { - float centrality = mccolls.centFT0M(); - bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; - const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolls.globalIndex(), cacheMC); + + float centrality = useManualCalibration + ? getCentClassFromGenMult(getGenMidRapMultiplicity(particlesInCollision)) + : mccolls.centFT0M(); + + if (centrality == kInvalidCentrality) { + continue; + } + + bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); // QA for Trigger efficiency histos.fill(HIST("Event/hMCEventIndices"), centrality, Inel); - if (inVtx10) + if (inVtx10) { histos.fill(HIST("Event/hMCEventIndices"), centrality, Inel10); - if (isTrueINELgt0) + } + if (isTrueINELgt0) { histos.fill(HIST("Event/hMCEventIndices"), centrality, Inelg0); - if (inVtx10 && isTrueINELgt0) + } + if (inVtx10 && isTrueINELgt0) { histos.fill(HIST("Event/hMCEventIndices"), centrality, Inelg010); + } } } PROCESS_SWITCH(Lambda1520analysisinpp, processEventFactor, "Process Event factor", false); @@ -1384,7 +1523,13 @@ struct Lambda1520analysisinpp { bool isInAfterAllCuts = isSelected(collision, false); bool inVtx10 = std::abs(mcCollision.posZ()) <= configEvents.cfgEvtZvtx; - float centrality = mcCollision.centFT0M(); + float centrality = useManualCalibration + ? getCentClassFromGenMult(getGenMidRapMultiplicity(particlesInCollision)) + : mcCollision.centFT0M(); + + if (centrality == kInvalidCentrality) { + continue; + } auto computePtL = [&](float pt, float m_ref) { float ptL2 = pt * pt + m_ref * m_ref - MassLambda1520 * MassLambda1520; @@ -1392,22 +1537,27 @@ struct Lambda1520analysisinpp { }; // ===== NUM ===== - if (!(inVtx10 && isTrueINELgt0)) + if (!(inVtx10 && isTrueINELgt0)) { continue; + } - if (!isInAfterAllCuts) + if (!isInAfterAllCuts) { continue; + } for (const auto& part : particlesInCollision) { - if (!part.isPhysicalPrimary()) + if (!part.isPhysicalPrimary()) { continue; + } - if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) { continue; + } - if (cUseEtacutMC && std::abs(part.eta()) > cEtacutMC) + if (cUseEtacutMC && std::abs(part.eta()) > cEtacutMC) { continue; + } float pt = part.pt(); @@ -1470,15 +1620,22 @@ struct Lambda1520analysisinpp { // Loop on generated collisions to fill the event factor for the INEL>0 correction for (const auto& mccolls : mcCollisions) { - float centrality = mccolls.centFT0M(); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolls.globalIndex(), cacheMC); - bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; + float centrality = useManualCalibration + ? getCentClassFromGenMult(getGenMidRapMultiplicity(particlesInCollision)) + : mccolls.centFT0M(); - const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolls.globalIndex(), cacheMC); + if (centrality == kInvalidCentrality) { + continue; + } + + bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); - if (!(inVtx10 && isTrueINELgt0)) + if (!(inVtx10 && isTrueINELgt0)) { continue; + } auto computePtL = [&](float pt, float m_ref) { float ptL2 = pt * pt + m_ref * m_ref - MassLambda1520 * MassLambda1520; @@ -1487,11 +1644,13 @@ struct Lambda1520analysisinpp { for (const auto& part : particlesInCollision) { - if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) { continue; + } - if (cUseEtacutMC && std::abs(part.eta()) > cEtacutMC) + if (cUseEtacutMC && std::abs(part.eta()) > cEtacutMC) { continue; + } // ========================= // ===== LAMBDA(1520) ====== @@ -1511,17 +1670,20 @@ struct Lambda1520analysisinpp { bool pass2 = std::abs(daughterPDGs[0]) == kProton || std::abs(daughterPDGs[1]) == kProton; // At least one decay to Proton // Checking if we have both decay products - if (!pass1 || !pass2) + if (!pass1 || !pass2) { continue; + } - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("Result/SignalLoss/GenTruelambda1520pt_den"), part.pt(), centrality); - else + } else { histos.fill(HIST("Result/SignalLoss/GenTrueantilambda1520pt_den"), part.pt(), centrality); + } } - if (!part.isPhysicalPrimary()) + if (!part.isPhysicalPrimary()) { continue; + } float pt = part.pt(); float weight = 1.f; @@ -1535,11 +1697,13 @@ struct Lambda1520analysisinpp { histos.fill(HIST("Result/SignalLoss/GenTruekaonpt_den"), pt, centrality); float ptL = computePtL(pt, massKa); - if (ptL < 0) + if (ptL < 0) { continue; + } - if (useWeight) + if (useWeight) { weight = ptL / pt; + } histos.fill(HIST("Result/SignalLoss/Genkaonpt_den"), ptL, centrality, weight); } @@ -1553,11 +1717,13 @@ struct Lambda1520analysisinpp { histos.fill(HIST("Result/SignalLoss/GenTrueprotonpt_den"), pt, centrality); float ptL = computePtL(pt, massPr); - if (ptL < 0) + if (ptL < 0) { continue; + } - if (useWeight) + if (useWeight) { weight = ptL / pt; + } histos.fill(HIST("Result/SignalLoss/Genprotonpt_den"), ptL, centrality, weight); } @@ -1570,11 +1736,13 @@ struct Lambda1520analysisinpp { histos.fill(HIST("Result/SignalLoss/GenTruelambdapt_den"), pt, centrality); float ptL = computePtL(pt, MassLambda0); - if (ptL < 0) + if (ptL < 0) { continue; + } - if (useWeight) + if (useWeight) { weight = ptL / pt; + } histos.fill(HIST("Result/SignalLoss/Genlambdapt_den"), ptL, centrality, weight); } @@ -1587,11 +1755,13 @@ struct Lambda1520analysisinpp { histos.fill(HIST("Result/SignalLoss/GenTruexipt_den"), pt, centrality); float ptL = computePtL(pt, MassXiMinus); - if (ptL < 0) + if (ptL < 0) { continue; + } - if (useWeight) + if (useWeight) { weight = ptL / pt; + } histos.fill(HIST("Result/SignalLoss/Genxipt_den"), ptL, centrality, weight); } @@ -1604,11 +1774,13 @@ struct Lambda1520analysisinpp { histos.fill(HIST("Result/SignalLoss/GenTrueomegapt_den"), pt, centrality); float ptL = computePtL(pt, MassOmegaMinus); - if (ptL < 0) + if (ptL < 0) { continue; + } - if (useWeight) + if (useWeight) { weight = ptL / pt; + } histos.fill(HIST("Result/SignalLoss/Genomegapt_den"), ptL, centrality, weight); } diff --git a/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx new file mode 100644 index 00000000000..b2e09900706 --- /dev/null +++ b/PWGLF/Tasks/Resonances/lambda1520analysispo.cxx @@ -0,0 +1,1206 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambda1520Analysispo.cxx +/// \brief Task for Lambda(1520) resonance reconstruction via proton-kaon invariant mass analysis for pO system +/// +/// \author Durgesh Bhatt + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +namespace lambda_analysis +{ +// Named PDG codes — fixes pdg/explicit-code and magic-number errors +static constexpr int PdgProton{2212}; +static constexpr int PdgKaon{321}; +static constexpr int PdgLambda0{3122}; +static constexpr int PdgXiMinus{3312}; +static constexpr int PdgXi0{3322}; +static constexpr int PdgOmegaMinus{3334}; +} // namespace lambda_analysis + +struct lambda1520analysispo { + SliceCache sliceCache; + + // Preslice helpers + Preslice tracksPerResonanceCollision = aod::resodaughter::resoCollisionId; + Preslice tracksPerStandardCollision = aod::track::collisionId; + + // Pointer to MC parent particle table (used only in MC processing) + aod::ResoMCParents const* mcResonanceParentTable = nullptr; + + // ── Event-level configurables ──────────────────────────────────────────── + // FIX name/configurable: lowerCamelCase, member name == JSON string, on one + // line + Configurable applyOccupancyInTimeRangeCut{"applyOccupancyInTimeRangeCut", false, "If true, apply a cut on the number of tracks in a time window around the collision (occupancy cut)"}; + + // ── Histogram binning configurables ───────────────────────────────────── + Configurable numberOfPtBins{"numberOfPtBins", 100, "Number of bins along the transverse momentum (pT) axis"}; + Configurable numberOfInvMassBins{"numberOfInvMassBins", 120, "Number of bins along the invariant mass axis"}; + + // ── Physics configurables ──────────────────────────────────────────────── + Configurable pdgCodeLambda1520{"pdgCodeLambda1520", 3124, "PDG code of the Lambda(1520) resonance"}; + Configurable enableRotationalBackground{"enableRotationalBackground", true, "If true, compute rotational background (kaon phi rotated by ~pi) to estimate combinatorial background"}; + + // ── Track quality cuts ─────────────────────────────────────────────────── + Configurable minTrackPt{"minTrackPt", 0.15f, "Minimum transverse momentum pT of a track [GeV/c]"}; + Configurable minProtonMomentum{"minProtonMomentum", 0.f, "Minimum total momentum p for proton PID [GeV/c]"}; + Configurable minKaonMomentum{"minKaonMomentum", 0.f, "Minimum total momentum p for kaon PID [GeV/c]"}; + Configurable minPseudorapidity{"minPseudorapidity", -0.8f, "Minimum pseudorapidity eta (detector acceptance)"}; + Configurable maxPseudorapidity{"maxPseudorapidity", 0.8f, "Maximum pseudorapidity eta (detector acceptance)"}; + Configurable maxDCAz{"maxDCAz", 1.0f, "Maximum allowed distance of closest approach along z-axis (DCAz) [cm]"}; + Configurable minPairRapidity{"minPairRapidity", -0.5f, "Minimum rapidity y of the reconstructed proton-kaon pair"}; + Configurable maxPairRapidity{"maxPairRapidity", 0.5f, "Maximum rapidity y of the reconstructed proton-kaon pair"}; + + // ── TPC cluster quality cuts ───────────────────────────────────────────── + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Minimum number of TPC crossed pad rows (track quality)"}; + Configurable applyCrossedRowsCut{"applyCrossedRowsCut", false, "If true, require at least minTPCCrossedRows crossed rows in the TPC"}; + Configurable minTPCClustersFound{"minTPCClustersFound", 70, "Minimum number of TPC clusters found on the track"}; + Configurable applyTPCClustersCut{"applyTPCClustersCut", false, "If true, require at least minTPCClustersFound TPC clusters"}; + + // ── pT-dependent DCAxy cuts for Protons ───────────────────────────────── + Configurable> protonDCAPtBinEdges{"protonDCAPtBinEdges", {0.0f, 0.5f, 1.0f, 2.0f, 3.0f, 5.0f, 1000.0f}, "Proton pT bin edges [GeV/c] for the pT-dependent DCAxy selection"}; + Configurable> protonMaxDCAxyPerPtBin{"protonMaxDCAxyPerPtBin", {0.020f, 0.015f, 0.010f, 0.007f, 0.005f, 0.004f}, "Maximum |DCAxy| [cm] for protons in each pT bin defined by protonDCAPtBinEdges"}; + + // ── pT-dependent DCAxy cuts for Kaons ─────────────────────────────────── + Configurable> kaonDCAPtBinEdges{"kaonDCAPtBinEdges", {0.0f, 0.3f, 0.6f, 1.0f, 2.0f, 1000.0f}, "Kaon pT bin edges [GeV/c] for the pT-dependent DCAxy selection"}; + Configurable> kaonMaxDCAxyPerPtBin{"kaonMaxDCAxyPerPtBin", {0.025f, 0.018f, 0.012f, 0.008f, 0.004f}, "Maximum |DCAxy| [cm] for kaons in each pT bin defined by kaonDCAPtBinEdges"}; + + // ── Analysis mode switches ─────────────────────────────────────────────── + Configurable runQualityChecksOnly{"runQualityChecksOnly", false, "If true, only fill QA histograms and skip invariant mass computation"}; + Configurable applyDeepAngleCut{"applyDeepAngleCut", false, "If true, reject proton-kaon pairs with very small opening angle (removes split-track background)"}; + Configurable deepAngleCutValue{"deepAngleCutValue", 0.04, "Minimum allowed opening angle [rad] between proton and kaon (used if applyDeepAngleCut = true)"}; + Configurable applyKinematicPairCuts{"applyKinematicPairCuts", false, "If true, apply additional kinematic cuts on the p-K opening angle"}; + + // ── Global track selection flags ───────────────────────────────────────── + Configurable requirePrimaryTrack{"requirePrimaryTrack", true, "Require track to pass the 'isPrimaryTrack' flag (kGoldenChi2 | kDCAxy | kDCAz)"}; + Configurable requireGlobalTrackNoDCA{"requireGlobalTrackNoDCA", true, "Require track to pass 'isGlobalTrackWoDCA' (quality cuts without DCA requirement)"}; + Configurable requirePVContributor{"requirePVContributor", true, "Require track to be a contributor to the primary vertex reconstruction"}; + + // ── PID configurables ──────────────────────────────────────────────────── + Configurable requireTOFForProton{"requireTOFForProton", false, "If true, only accept proton candidates that have a TOF measurement"}; + Configurable requireTOFForKaon{"requireTOFForKaon", false, "If true, only accept kaon candidates that have a TOF measurement"}; + Configurable useTPCOnlyPID{"useTPCOnlyPID", false, "If true, use only TPC nSigma for PID (ignore TOF even if available)"}; + + Configurable tpcNSigmaVetoOtherSpecies{"tpcNSigmaVetoOtherSpecies", 3.0f, "Reject track if its TPC nSigma for a different species is below this threshold (avoids misID)"}; + Configurable tpcNSigmaVetoPion{"tpcNSigmaVetoPion", 3.0f, "TPC nSigma threshold below which a track is vetoed as a pion"}; + Configurable tpcNSigmaVetoKaonForProton{"tpcNSigmaVetoKaonForProton", 3.0f, "TPC nSigma threshold: veto proton candidates that look like kaons"}; + Configurable tpcNSigmaVetoPionForKaon{"tpcNSigmaVetoPionForKaon", 3.0f, "TPC nSigma threshold: veto kaon candidates that look like pions"}; + Configurable tpcNSigmaVetoProtonForKaon{"tpcNSigmaVetoProtonForKaon", 3.0f, "TPC nSigma threshold: veto kaon candidates that look like protons"}; + + Configurable minTPCNSigmaKaon{"minTPCNSigmaKaon", -6.0f, "Minimum (most negative) TPC nSigma for kaon"}; + Configurable minTPCNSigmaProton{"minTPCNSigmaProton", -6.0f, "Minimum (most negative) TPC nSigma for proton"}; + Configurable minTOFNSigmaKaon{"minTOFNSigmaKaon", -6.0f, "Minimum (most negative) TOF nSigma for kaon"}; + Configurable minTOFNSigmaProton{"minTOFNSigmaProton", -6.0f, "Minimum (most negative) TOF nSigma for proton"}; + Configurable minCombinedTPCTOFNSigmaKaon{"minCombinedTPCTOFNSigmaKaon", -6.0f, "Minimum combined TPC+TOF nSigma for kaon (used in combined PID mode)"}; + Configurable minCombinedTPCTOFNSigmaProton{"minCombinedTPCTOFNSigmaProton", -6.0f, "Minimum combined TPC+TOF nSigma for proton (used in combined PID mode)"}; + + Configurable tpcNSigmaVetoThreshold{"tpcNSigmaVetoThreshold", 3.0f, "General TPC nSigma veto cut to reject misidentified particles when TPC bands overlap"}; + Configurable tofNSigmaVetoThreshold{"tofNSigmaVetoThreshold", 3.0f, "General TOF nSigma veto cut to reject misidentified particles"}; + + // ── Proton PID momentum-dependent TPC cuts ─────────────────────────────── + Configurable maxTPCNSigmaProton{"maxTPCNSigmaProton", 3.0, "Maximum |TPC nSigma| for proton identification (symmetric cut)"}; + Configurable combinedNSigmaCutProton{"combinedNSigmaCutProton", 3.0, "Cut on sqrt(nSigmaTPC^2 + nSigmaTOF^2) for proton. Negative value switches to asymmetric mode."}; + Configurable> protonTPCPIDMomentumBins{"protonTPCPIDMomentumBins", {0, 0.5, 0.7, 0.8}, "Momentum p bin edges [GeV/c] for momentum-dependent TPC PID cuts on protons"}; + Configurable> protonTPCNSigmaCutPerBin{"protonTPCNSigmaCutPerBin", {5., 3.5, 2.5}, "Maximum TPC nSigma for proton in each momentum bin (tighter at higher p)"}; + Configurable> protonTOFPIDMomentumBins{"protonTOFPIDMomentumBins", {0., 999.}, "Momentum p bin edges [GeV/c] for momentum-dependent TOF PID cuts on protons"}; + Configurable> protonTOFNSigmaCutPerBin{"protonTOFNSigmaCutPerBin", {3.0}, "Maximum TOF nSigma for proton in each momentum bin"}; + + // ── Kaon PID momentum-dependent TPC cuts ──────────────────────────────── + Configurable maxTPCNSigmaKaon{"maxTPCNSigmaKaon", 3.0, "Maximum |TPC nSigma| for kaon identification (symmetric cut)"}; + Configurable combinedNSigmaCutKaon{"combinedNSigmaCutKaon", 3.0, "Cut on sqrt(nSigmaTPC^2 + nSigmaTOF^2) for kaon. Negative value switches to asymmetric mode."}; + Configurable> kaonTPCPIDMomentumBins{"kaonTPCPIDMomentumBins", {0., 0.25, 0.3, 0.45}, "Momentum p bin edges [GeV/c] for momentum-dependent TPC PID cuts on kaons"}; + Configurable> kaonTPCNSigmaCutPerBin{"kaonTPCNSigmaCutPerBin", {6, 3.5, 2.5}, "Maximum TPC nSigma for kaon in each momentum bin"}; + Configurable> kaonTOFPIDMomentumBins{"kaonTOFPIDMomentumBins", {0., 999.}, "Momentum p bin edges [GeV/c] for momentum-dependent TOF PID cuts on kaons"}; + Configurable> kaonTOFNSigmaCutPerBin{"kaonTOFNSigmaCutPerBin", {3.0}, "Maximum TOF nSigma for kaon in each momentum bin"}; + + // ── Event mixing configurables ─────────────────────────────────────────── + Configurable numberOfEventsToMix{"numberOfEventsToMix", 20, "Number of events to mix with each signal event for background estimation"}; + ConfigurableAxis dcaZMixingBins{"dcaZMixingBins", {VARIABLE_WIDTH, -1.2f, -1.0f, -0.9f, -0.8f, -0.7f, -0.6f, -0.5f, -0.4f, -0.3f, -0.2f, -0.1f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.2f}, "DCAz vertex bins used for event mixing pairing"}; + ConfigurableAxis vertexZMixingBins{"vertexZMixingBins", {VARIABLE_WIDTH, -12.f, -10.f, -9.f, -8.f, -7.f, -6.f, -5.f, -4.f, -3.f, -2.f, -1.f, 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f, 12.f}, "Primary vertex z-position bins used for event mixing pairing"}; + ConfigurableAxis centralityMixingBins{"centralityMixingBins", {VARIABLE_WIDTH, 0.f, 10.f, 20.f, 30.f, 40.f, 50.f, 60.f, 70.f, 80.f, 90.f, 100.f, 200.f}, "Centrality (FT0 %) bins used for event mixing pairing"}; + ConfigurableAxis eventPlaneMixingBins{"eventPlaneMixingBins", {VARIABLE_WIDTH, -1.5708f, -1.25664f, -0.942478f, -0.628319f, 0.f, 0.628319f, 0.942478f, 1.25664f, 1.5708f}, "Event plane angle bins used for event mixing (EP-dependent analysis)"}; + ConfigurableAxis occupancyBins{"occupancyBins", {VARIABLE_WIDTH, 0.0, 100, 500, 600, 1000, 1100, 1500, 1600, 2000, 2100, 2500, 2600, 3000, 3100, 3500, 3600, 4000, 4100, 4500, 4600, 5000, 5100, 9999}, "Track occupancy bins in the time range around the collision"}; + + // ── Rotational background configurables ───────────────────────────────── + Configurable numberOfRotations{"numberOfRotations", 10, "How many times to rotate the kaon phi for the rotational background estimate"}; + Configurable rotationAngleWindow{"rotationAngleWindow", 6.f, "The kaon is rotated by angles near PI, within a window of PI/rotationAngleWindow"}; + + // ── MC event selection flags ───────────────────────────────────────────── + Configurable mcRequireAfterAllCuts{"mcRequireAfterAllCuts", false, "MC event selection: require isInAfterAllCuts flag"}; + Configurable mcRequireINELgt0{"mcRequireINELgt0", false, "MC event selection: require at least 1 charged particle in |eta|<1 (INEL>0)"}; + Configurable mcRequireSel8{"mcRequireSel8", false, "MC event selection: require the standard Sel8 event selection"}; + Configurable mcRequireVtxWithin10cm{"mcRequireVtxWithin10cm", false, "MC event selection: require primary vertex |z| < 10 cm"}; + Configurable mcRequireTriggerTVX{"mcRequireTriggerTVX", false, "MC event selection: require the TVX (T0 vertex) trigger"}; + Configurable cEvtRecINELgt0{"cEvtRecINELgt0", false, "MC event selection: require reconstructed INEL>0 condition"}; + + // ── Histogram registry ─────────────────────────────────────────────────── + HistogramRegistry allHistograms{"allHistograms", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // ============================================================ + // init() + // ============================================================ + void init(InitContext const&) + { + const AxisSpec axisCentralityPercent(110, 0, 110, "FT0 centrality (%)"); + const AxisSpec axisMomentumForPID(200, 0., 10., "p (GeV/c)"); + const AxisSpec axisPtForPID(200, 0., 10., "p_{T} (GeV/c)"); + const AxisSpec axisPt(numberOfPtBins, 0., 10., "p_{T} (GeV/c)"); + const AxisSpec axisPseudorapidity(40, -1, 1, "#eta"); + const AxisSpec axisDCAxy(240, -0.12, 0.12, "DCA_{xy} (cm)"); + const AxisSpec axisTPCNClusters(200, 0, 200, "TPC N_{clusters}"); + const AxisSpec axisTPCNSigma(401, -10.025, 10.025, "n#sigma^{TPC}"); + const AxisSpec axisTOFNSigma(401, -10.025, 10.025, "n#sigma^{TOF}"); + const AxisSpec axisTPCdEdx(380, 10, 200, "#frac{dE}{dx}"); + const AxisSpec axisVertexZ(120, -12, 12, "v_{z} (cm)"); + const AxisSpec axisEventPlaneAngle(120, -3.14, 3.14, "#theta (rad)"); + const AxisSpec axisInvariantMass(numberOfInvMassBins, 1.4, 3.0, "M_{inv} (GeV/c^{2})"); + + AxisSpec axisOccupancy = {occupancyBins, "Track occupancy [-40,100]"}; + AxisSpec axisDCAz = {dcaZMixingBins, "DCA_{z} (cm)"}; + + allHistograms.add("Event/centralityVsOccupancy", "Collision centrality vs track occupancy", kTH2F, {axisCentralityPercent, axisOccupancy}); + allHistograms.add("Event/primaryVertexZ", "Primary vertex z-position distribution", kTH1F, {{100, -15., 15.}}); + if (doprocessMix || doprocessMixDF || doprocessMixepDF) { + allHistograms.add("Event/mixingBins_centralityVsVtxZVsEventPlane", "Event mixing bin occupancy: centrality vs vtxZ vs event plane", kTH3F, {axisCentralityPercent, axisVertexZ, axisEventPlaneAngle}); + } + + allHistograms.add("QAbefore/trackEta", "Track pseudorapidity (before cuts)", kTH1F, {{50, -1.0, 1.0}}); + allHistograms.add("QAbefore/trackPt", "Track pT (before cuts)", kTH1F, {axisMomentumForPID}); + allHistograms.add("QAbefore/trackPhi", "Track azimuthal angle (before cuts)", kTH1F, {{72, 0, 6.2832}}); + allHistograms.add("QAbefore/trackEtaVsPhi", "Track eta vs phi (before cuts)", kTH2F, {axisPseudorapidity, {72, 0, 6.2832}}); + + allHistograms.add("QAbefore/Proton/tpcNSigmaVsMomentum", "TPC nSigma proton vs momentum (before PID cuts)", kTH2F, {axisMomentumForPID, axisTPCNSigma}); + allHistograms.add("QAbefore/Proton/tofNSigmaVsMomentum", "TOF nSigma proton vs momentum (before PID cuts)", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAbefore/Proton/tofNSigmaVsTPCNSigma", "TOF nSigma vs TPC nSigma proton (before PID cuts)", kTH2F, {axisTPCNSigma, axisTOFNSigma}); + + allHistograms.add("QAbefore/Kaon/tpcNSigmaVsMomentum", "TPC nSigma kaon vs momentum (before PID cuts)", kTH2F, {axisMomentumForPID, axisTPCNSigma}); + allHistograms.add("QAbefore/Kaon/tofNSigmaVsMomentum", "TOF nSigma kaon vs momentum (before PID cuts)", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAbefore/Kaon/tofNSigmaVsTPCNSigma", "TOF nSigma vs TPC nSigma kaon (before PID cuts)", kTH2F, {axisTPCNSigma, axisTOFNSigma}); + + allHistograms.add("QAafter/Proton/ptVsCentrality", "Proton pT vs centrality (after cuts)", kTH2F, {axisPtForPID, axisCentralityPercent}); + allHistograms.add("QAafter/Proton/dcaZVsPt", "Proton DCAz vs pT (after cuts)", kTH2F, {axisPtForPID, axisDCAz}); + allHistograms.add("QAafter/Proton/dcaXYVsPt", "Proton DCAxy vs pT (after cuts)", kTH2F, {axisPtForPID, axisDCAxy}); + allHistograms.add("QAafter/Proton/tpcDedxVsMomentum", "Proton TPC dE/dx signal vs momentum (after cuts)", kTH2F, {axisMomentumForPID, axisTPCdEdx}); + allHistograms.add("QAafter/Proton/tpcNSigmaVsPt", "Proton TPC nSigma vs pT (after cuts)", kTH2F, {axisPtForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Proton/tpcNSigmaPionContamVsPt", "Proton track: TPC nSigma pion contamination check", kTH2F, {axisPtForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Proton/tpcNSigmaKaonContamVsPt", "Proton track: TPC nSigma kaon contamination check", kTH2F, {axisPtForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Proton/tpcNSigmaVsMomentum", "Proton TPC nSigma vs total momentum (after cuts)", kTH2F, {axisMomentumForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Proton/tpcNSigmaVsCentrality", "Proton TPC nSigma vs centrality", kTH2F, {axisCentralityPercent, axisTPCNSigma}); + allHistograms.add("QAafter/Proton/tofNSigmaVsPt", "Proton TOF nSigma vs pT (after cuts)", kTH2F, {axisPtForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Proton/tofNSigmaVsMomentum", "Proton TOF nSigma vs total momentum (after cuts)", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Proton/tofNSigmaVsCentrality", "Proton TOF nSigma vs centrality", kTH2F, {axisCentralityPercent, axisTOFNSigma}); + allHistograms.add("QAafter/Proton/tofNSigmaPionContamVsMomentum", "Proton track: TOF nSigma pion contamination check", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Proton/tofNSigmaKaonContamVsMomentum", "Proton track: TOF nSigma kaon contamination check", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Proton/tofNSigmaVsTPCNSigma", "Proton TOF nSigma vs TPC nSigma (after cuts)", kTH2F, {axisTPCNSigma, axisTOFNSigma}); + allHistograms.add("QAafter/Proton/tpcCrossedRowsVsPt", "Proton TPC crossed rows vs pT", kTH2F, {axisPtForPID, {200, 0, 200}}); + allHistograms.add("QAafter/Proton/tpcClustersFoundVsPt", "Proton TPC clusters found vs pT", kTH2F, {axisPtForPID, {200, 0, 200}}); + + allHistograms.add("QAafter/Kaon/ptVsCentrality", "Kaon pT vs centrality (after cuts)", kTH2F, {axisPtForPID, axisCentralityPercent}); + allHistograms.add("QAafter/Kaon/dcaZVsPt", "Kaon DCAz vs pT (after cuts)", kTH2F, {axisPtForPID, axisDCAz}); + allHistograms.add("QAafter/Kaon/dcaXYVsPt", "Kaon DCAxy vs pT (after cuts)", kTH2F, {axisPtForPID, axisDCAxy}); + allHistograms.add("QAafter/Kaon/tpcDedxVsMomentum", "Kaon TPC dE/dx signal vs momentum (after cuts)", kTH2F, {axisMomentumForPID, axisTPCdEdx}); + allHistograms.add("QAafter/Kaon/tpcNSigmaPionContamVsPt", "Kaon track: TPC nSigma pion contamination check", kTH2F, {axisPtForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Kaon/tpcNSigmaProtonContamVsMomentum", "Kaon track: TPC nSigma proton contamination check", kTH2F, {axisMomentumForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Kaon/tpcNSigmaVsPt", "Kaon TPC nSigma vs pT (after cuts)", kTH2F, {axisPtForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Kaon/tpcNSigmaVsMomentum", "Kaon TPC nSigma vs total momentum (after cuts)", kTH2F, {axisMomentumForPID, axisTPCNSigma}); + allHistograms.add("QAafter/Kaon/tpcNSigmaVsCentrality", "Kaon TPC nSigma vs centrality", kTH2F, {axisCentralityPercent, axisTPCNSigma}); + allHistograms.add("QAafter/Kaon/tofNSigmaVsPt", "Kaon TOF nSigma vs pT (after cuts)", kTH2F, {axisPtForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Kaon/tofNSigmaVsMomentum", "Kaon TOF nSigma vs total momentum (after cuts)", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Kaon/tofNSigmaVsCentrality", "Kaon TOF nSigma vs centrality", kTH2F, {axisCentralityPercent, axisTOFNSigma}); + allHistograms.add("QAafter/Kaon/tofNSigmaPionContamVsMomentum", "Kaon track: TOF nSigma pion contamination check", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Kaon/tofNSigmaProtonContamVsMomentum", "Kaon track: TOF nSigma proton contamination check", kTH2F, {axisMomentumForPID, axisTOFNSigma}); + allHistograms.add("QAafter/Kaon/tofNSigmaVsTPCNSigma", "Kaon TOF nSigma vs TPC nSigma (after cuts)", kTH2F, {axisTPCNSigma, axisTOFNSigma}); + allHistograms.add("QAafter/Kaon/tpcCrossedRowsVsPt", "Kaon TPC crossed rows vs pT", kTH2F, {axisPtForPID, {200, 0, 200}}); + allHistograms.add("QAafter/Kaon/tpcClustersFoundVsPt", "Kaon TPC clusters found vs pT", kTH2F, {axisPtForPID, {200, 0, 200}}); + + if (!doprocessMC) { + allHistograms.add("Analysis/invMass_UnlikeSign_ProtonPlusKaonMinus", "Invariant mass: p^{+}K^{-} (Lambda(1520) signal)", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_UnlikeSign_ProtonMinusKaonPlus", "Invariant mass: p^{-}K^{+} (anti-Lambda(1520) signal)", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_LikeSign_ProtonPlusKaonPlus", "Invariant mass: p^{+}K^{+} (like-sign background)", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_LikeSign_ProtonMinusKaonMinus", "Invariant mass: p^{-}K^{-} (like-sign background)", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_Rotated_ProtonPlusKaonMinus", "Invariant mass: rotational background (Lambda(1520))", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_Rotated_ProtonMinusKaonPlus", "Invariant mass: rotational background (anti-Lambda(1520))", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_Mixed_ProtonPlusKaonMinus", "Invariant mass: mixed events p^{+}K^{-} (background)", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_Mixed_ProtonMinusKaonPlus", "Invariant mass: mixed events p^{-}K^{+} (background)", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_Mixed_LikeSign_PlusPlus", "Invariant mass: mixed events like-sign ++", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + allHistograms.add("Analysis/invMass_Mixed_LikeSign_MinusMinus", "Invariant mass: mixed events like-sign --", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent, axisOccupancy}); + } + + if (doprocessMC) { + allHistograms.add("Event/mcEventSelectionCutflow", "MC event selection cutflow (how many events pass each cut)", kTH1F, {{7, 0, 7}}); + allHistograms.add("QAChecks/protonRecoLevelPt", "Reconstructed proton pT (after PID cuts) - MC", kTH1F, {axisPtForPID}); + allHistograms.add("QAChecks/kaonRecoLevelPt", "Reconstructed kaon pT (after PID cuts) - MC", kTH1F, {axisPtForPID}); + allHistograms.add("QAChecks/protonGenLevelPt", "Generated proton pT (truth level) - MC", kTH1F, {axisPtForPID}); + allHistograms.add("QAChecks/kaonGenLevelPt", "Generated kaon pT (truth level) - MC", kTH1F, {axisPtForPID}); + allHistograms.add("Analysis/mcGenerated_Lambda1520", "Generated Lambda(1520) invariant mass vs pT vs centrality", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent}); + allHistograms.add("Analysis/mcGenerated_AntiLambda1520", "Generated anti-Lambda(1520) invariant mass vs pT vs centrality", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent}); + allHistograms.add("Analysis/mcReconstructed_Lambda1520", "Reconstructed Lambda(1520) invariant mass vs pT vs centrality - MC", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent}); + allHistograms.add("Analysis/mcReconstructed_AntiLambda1520", "Reconstructed anti-Lambda(1520) invariant mass vs pT vs centrality - MC", kTHnSparseF, {axisInvariantMass, axisPt, axisCentralityPercent}); + allHistograms.add("Analysis/mcMassResolution_Lambda1520", "Mass resolution (Mreco - Mgen) vs pT - Lambda(1520)", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCentralityPercent}); + allHistograms.add("Analysis/mcMassResolution_AntiLambda1520", "Mass resolution (Mreco - Mgen) vs pT - anti-Lambda(1520)", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCentralityPercent}); + } + + if (doprocessMCGen) { + allHistograms.add("SignalLoss/mcEventSelectionCutflow", "MC event selection cutflow (signal loss study)", kTH1F, {{7, 0, 7}}); + allHistograms.add("SignalLoss/mTScaled_fromProton", "mT-scaled Lambda(1520) pT from proton parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromAntiProton", "mT-scaled anti-Lambda(1520) pT from anti-proton parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromLambda0", "mT-scaled Lambda(1520) pT from Lambda0 parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromAntiLambda0", "mT-scaled anti-Lambda(1520) pT from anti-Lambda0 parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromXiMinus", "mT-scaled Lambda(1520) pT from Xi- parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromXiPlus", "mT-scaled anti-Lambda(1520) pT from Xi+ parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromXi0", "mT-scaled Lambda(1520) pT from Xi0 parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromAntiXi0", "mT-scaled anti-Lambda(1520) pT from anti-Xi0 parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromOmegaMinus", "mT-scaled Lambda(1520) pT from Omega- parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + allHistograms.add("SignalLoss/mTScaled_fromOmegaPlus", "mT-scaled anti-Lambda(1520) pT from Omega+ parent", kTHnSparseF, {axisPt, axisCentralityPercent}); + } + } + + // ============================================================ + // passesBasicTrackSelection() + // ============================================================ + template + bool passesBasicTrackSelection(TrackType const& track) + { + if (track.pt() < minTrackPt) + return false; + if (track.eta() < minPseudorapidity || track.eta() > maxPseudorapidity) + return false; + if (requirePrimaryTrack && !track.isPrimaryTrack()) + return false; + if (requireGlobalTrackNoDCA && !track.isGlobalTrackWoDCA()) + return false; + if (requirePVContributor && !track.isPVContributor()) + return false; + if (applyCrossedRowsCut && track.tpcNClsCrossedRows() < minTPCCrossedRows) + return false; + if (applyTPCClustersCut && track.tpcNClsFound() < minTPCClustersFound) + return false; + return true; + } + + // ============================================================ + // passesProtonDCASelection() + // ============================================================ + template + bool passesProtonDCASelection(TrackType const& track, float trackPt) + { + auto ptBinEdges = static_cast>(protonDCAPtBinEdges); + auto maxDCAxyValues = + static_cast>(protonMaxDCAxyPerPtBin); + int numberOfBins = static_cast(ptBinEdges.size()) - 1; + + bool dcaXYPassed = false; + for (int iBin = 0; iBin < numberOfBins; iBin++) { + if (trackPt >= ptBinEdges[iBin] && + trackPt < ptBinEdges[iBin + 1] && + std::abs(track.dcaXY()) < maxDCAxyValues[iBin]) + dcaXYPassed = true; + } + if (!dcaXYPassed) + return false; + if (std::abs(track.dcaZ()) > maxDCAz) + return false; + return true; + } + + // ============================================================ + // passesKaonDCASelection() + // ============================================================ + template + bool passesKaonDCASelection(TrackType const& track, float trackPt) + { + auto ptBinEdges = static_cast>(kaonDCAPtBinEdges); + auto maxDCAxyValues = static_cast>(kaonMaxDCAxyPerPtBin); + int numberOfBins = static_cast(ptBinEdges.size()) - 1; + + bool dcaXYPassed = false; + for (int iBin = 0; iBin < numberOfBins; iBin++) { + if (trackPt >= ptBinEdges[iBin] && + trackPt < ptBinEdges[iBin + 1] && + std::abs(track.dcaXY()) < maxDCAxyValues[iBin]) + dcaXYPassed = true; + } + if (!dcaXYPassed) + return false; + if (std::abs(track.dcaZ()) > maxDCAz) + return false; + return true; + } + + // ============================================================ + // passesProtonPID() + // ============================================================ + template + bool passesProtonPID(TrackType const& track, float totalMomentum) + { + bool tpcPIDPassed{false}, tofPIDPassed{false}; + + auto tpcMomBins = static_cast>(protonTPCPIDMomentumBins); + auto tpcNSigCuts = + static_cast>(protonTPCNSigmaCutPerBin); + auto tofMomBins = static_cast>(protonTOFPIDMomentumBins); + auto tofNSigCuts = + static_cast>(protonTOFNSigmaCutPerBin); + int nTPCBins = static_cast(tpcMomBins.size()); + int nTOFBins = static_cast(tofMomBins.size()); + + float tpcNSigPion = std::abs(track.tpcNSigmaPi()); + float tpcNSigKaon = std::abs(track.tpcNSigmaKa()); + float tpcNSigProton = std::abs(track.tpcNSigmaPr()); + float tofNSigPion = std::abs(track.tofNSigmaPi()); + float tofNSigKaon = std::abs(track.tofNSigmaKa()); + float tofNSigProton = std::abs(track.tofNSigmaPr()); + + float combinedNSigPion = + tpcNSigPion * tpcNSigPion + tofNSigPion * tofNSigPion; + float combinedNSigKaon = + tpcNSigKaon * tpcNSigKaon + tofNSigKaon * tofNSigKaon; + float combinedNSigProton = + tpcNSigProton * tpcNSigProton + tofNSigProton * tofNSigProton; + + float circularCutSquared = + combinedNSigmaCutProton * combinedNSigmaCutProton; + float circularRejCutSquared = + tofNSigmaVetoThreshold * tpcNSigmaVetoThreshold; + + if (!useTPCOnlyPID && track.hasTOF()) { + if (track.tofNSigmaPr() < minTOFNSigmaProton) + return false; + + if (combinedNSigmaCutProton < 0) { + for (int i = 0; i < nTOFBins - 1; ++i) { + if (totalMomentum >= tofMomBins[i] && + totalMomentum < tofMomBins[i + 1] && + (tofNSigProton < tofNSigCuts[i] && + tofNSigPion > tofNSigmaVetoThreshold && + tofNSigKaon > tofNSigmaVetoThreshold)) + tofPIDPassed = true; + } + if (track.tpcNSigmaPr() < minCombinedTPCTOFNSigmaProton) + return false; + if (tpcNSigProton < maxTPCNSigmaProton && + tpcNSigPion > tpcNSigmaVetoThreshold && + tpcNSigKaon > tpcNSigmaVetoThreshold) + tpcPIDPassed = true; + } + + if ((combinedNSigmaCutProton > 0) && + (combinedNSigProton < circularCutSquared && + combinedNSigPion > circularRejCutSquared && + combinedNSigKaon > circularRejCutSquared)) { + tofPIDPassed = true; + tpcPIDPassed = true; + } + + } else { + if (!track.hasTOF() && totalMomentum >= minProtonMomentum) + return false; + tofPIDPassed = true; + if (track.tpcNSigmaPr() < minTPCNSigmaProton) + return false; + for (int i = 0; i < nTPCBins - 1; ++i) { + if (totalMomentum >= tpcMomBins[i] && + totalMomentum < tpcMomBins[i + 1] && + (tpcNSigProton < tpcNSigCuts[i] && + tpcNSigPion > tpcNSigmaVetoPion && + tpcNSigKaon > tpcNSigmaVetoKaonForProton)) + tpcPIDPassed = true; + } + } + + return (tpcPIDPassed && tofPIDPassed); + } + + // ============================================================ + // passesKaonPID() + // ============================================================ + template + bool passesKaonPID(TrackType const& track, float totalMomentum) + { + bool tpcPIDPassed{false}, tofPIDPassed{false}; + + auto tpcMomBins = static_cast>(kaonTPCPIDMomentumBins); + auto tpcNSigCuts = static_cast>(kaonTPCNSigmaCutPerBin); + auto tofMomBins = static_cast>(kaonTOFPIDMomentumBins); + auto tofNSigCuts = static_cast>(kaonTOFNSigmaCutPerBin); + int nTPCBins = static_cast(tpcMomBins.size()); + int nTOFBins = static_cast(tofMomBins.size()); + + float tpcNSigPion = std::abs(track.tpcNSigmaPi()); + float tpcNSigKaon = std::abs(track.tpcNSigmaKa()); + float tpcNSigProton = std::abs(track.tpcNSigmaPr()); + float tofNSigPion = std::abs(track.tofNSigmaPi()); + float tofNSigKaon = std::abs(track.tofNSigmaKa()); + float tofNSigProton = std::abs(track.tofNSigmaPr()); + + float combinedNSigPion = + tpcNSigPion * tpcNSigPion + tofNSigPion * tofNSigPion; + float combinedNSigKaon = + tpcNSigKaon * tpcNSigKaon + tofNSigKaon * tofNSigKaon; + float combinedNSigProton = + tpcNSigProton * tpcNSigProton + tofNSigProton * tofNSigProton; + float circularCutSquared = combinedNSigmaCutKaon * combinedNSigmaCutKaon; + float circularRejCutSquared = + tpcNSigmaVetoOtherSpecies * tofNSigmaVetoThreshold; + + if (!useTPCOnlyPID && track.hasTOF()) { + if (track.tofNSigmaKa() < minTOFNSigmaKaon) + return false; + + if (combinedNSigmaCutKaon < 0) { + for (int i = 0; i < nTOFBins - 1; ++i) { + if (totalMomentum >= tofMomBins[i] && + totalMomentum < tofMomBins[i + 1] && + (tofNSigKaon < tofNSigCuts[i] && + tofNSigPion > tofNSigmaVetoThreshold && + tofNSigProton > tofNSigmaVetoThreshold)) + tofPIDPassed = true; + } + if (track.tpcNSigmaKa() < minCombinedTPCTOFNSigmaKaon) + return false; + if (tpcNSigKaon < maxTPCNSigmaKaon && + tpcNSigPion > tpcNSigmaVetoThreshold && + tpcNSigProton > tpcNSigmaVetoThreshold) + tpcPIDPassed = true; + } + + if ((combinedNSigmaCutKaon > 0) && + (combinedNSigKaon < circularCutSquared && + combinedNSigPion > circularRejCutSquared && + combinedNSigProton > circularRejCutSquared)) { + tofPIDPassed = true; + tpcPIDPassed = true; + } + + } else { + if (!track.hasTOF() && totalMomentum >= minKaonMomentum) + return false; + tofPIDPassed = true; + if (track.tpcNSigmaKa() < minTPCNSigmaKaon) + return false; + for (int i = 0; i < nTPCBins - 1; ++i) { + if (totalMomentum >= tpcMomBins[i] && + totalMomentum < tpcMomBins[i + 1] && + (tpcNSigKaon < tpcNSigCuts[i] && + tpcNSigPion > tpcNSigmaVetoPionForKaon && + tpcNSigProton > tpcNSigmaVetoProtonForKaon)) + tpcPIDPassed = true; + } + } + + return (tpcPIDPassed && tofPIDPassed); + } + + template + void fillTrackQAHistograms(TrackType const& track, float centralityPercent) + { + if (!passesBasicTrackSelection(track)) + return; + + const float trackPt = track.pt(); + const float totalMomentum = RecoDecay::p(track.px(), track.py(), track.pz()); + + const auto tpcNSigProtonRaw = track.tpcNSigmaPr(); + allHistograms.fill(HIST("QAbefore/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigProtonRaw); + const auto tpcNSigKaonRaw = track.tpcNSigmaKa(); + allHistograms.fill(HIST("QAbefore/Kaon/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigKaonRaw); + if (track.hasTOF()) { + const auto tofNSigProtonRaw = track.tofNSigmaPr(); + allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigProtonRaw); + allHistograms.fill(HIST("QAbefore/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProtonRaw, tofNSigProtonRaw); + const auto tofNSigKaonRaw = track.tofNSigmaKa(); + allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsMomentum"), totalMomentum, tofNSigKaonRaw); + allHistograms.fill(HIST("QAbefore/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaonRaw, tofNSigKaonRaw); + } + + const bool isProtonCandidate = + (!requireTOFForProton || track.hasTOF()) && + passesProtonPID(track, totalMomentum) && + passesProtonDCASelection(track, trackPt); + + if (isProtonCandidate) { + allHistograms.fill(HIST("QAafter/Proton/ptVsCentrality"), trackPt, centralityPercent); + allHistograms.fill(HIST("QAafter/Proton/dcaZVsPt"), trackPt, track.dcaZ()); + allHistograms.fill(HIST("QAafter/Proton/dcaXYVsPt"), trackPt, track.dcaXY()); + allHistograms.fill(HIST("QAafter/Proton/tpcDedxVsMomentum"), totalMomentum, track.tpcSignal()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaPionContamVsPt"), totalMomentum, track.tpcNSigmaPi()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaKaonContamVsPt"), totalMomentum, track.tpcNSigmaKa()); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcNSigmaVsPt"), trackPt, tpcNSigProtonRaw); + allHistograms.fill(HIST("QAafter/Proton/tpcCrossedRowsVsPt"), trackPt, track.tpcNClsCrossedRows()); + allHistograms.fill(HIST("QAafter/Proton/tpcClustersFoundVsPt"), trackPt, track.tpcNClsFound()); + + if (!useTPCOnlyPID && track.hasTOF()) { + const auto tofNSigProton = track.tofNSigmaPr(); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsMomentum"), totalMomentum, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsCentrality"), centralityPercent, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsPt"), trackPt, tofNSigProton); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaKaonContamVsMomentum"), totalMomentum, track.tofNSigmaKa()); + allHistograms.fill(HIST("QAafter/Proton/tofNSigmaVsTPCNSigma"), tpcNSigProtonRaw, tofNSigProton); + } + } + + const bool isKaonCandidate = + (!requireTOFForKaon || track.hasTOF()) && + passesKaonPID(track, totalMomentum) && + passesKaonDCASelection(track, trackPt); + + if (isKaonCandidate) { + allHistograms.fill(HIST("QAafter/Kaon/ptVsCentrality"), trackPt, centralityPercent); + allHistograms.fill(HIST("QAafter/Kaon/dcaZVsPt"), trackPt, track.dcaZ()); + allHistograms.fill(HIST("QAafter/Kaon/dcaXYVsPt"), trackPt, track.dcaXY()); + allHistograms.fill(HIST("QAafter/Kaon/tpcDedxVsMomentum"), totalMomentum, track.tpcSignal()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaPionContamVsPt"), totalMomentum, track.tpcNSigmaPi()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaProtonContamVsMomentum"), totalMomentum, track.tpcNSigmaPr()); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsMomentum"), totalMomentum, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsCentrality"), centralityPercent, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcNSigmaVsPt"), trackPt, tpcNSigKaonRaw); + allHistograms.fill(HIST("QAafter/Kaon/tpcCrossedRowsVsPt"), trackPt, track.tpcNClsCrossedRows()); + allHistograms.fill(HIST("QAafter/Kaon/tpcClustersFoundVsPt"), trackPt, track.tpcNClsFound()); + + if (!useTPCOnlyPID && track.hasTOF()) { + const auto tofNSigKaon = track.tofNSigmaKa(); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsMomentum"), totalMomentum, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsPt"), trackPt, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsCentrality"), centralityPercent, tofNSigKaon); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaPionContamVsMomentum"), totalMomentum, track.tofNSigmaPi()); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaProtonContamVsMomentum"), totalMomentum, track.tofNSigmaPr()); + allHistograms.fill(HIST("QAafter/Kaon/tofNSigmaVsTPCNSigma"), tpcNSigKaonRaw, tofNSigKaon); + } + } + } + + // ============================================================ + // fillInvariantMassHistograms() + // ============================================================ + template + void fillInvariantMassHistograms(TrackCollectionType const& protonCandidates, + TrackCollectionType const& kaonCandidates, + float centralityPercent, + int occupancyValue = 100) + { + float protonTotalMomentum = 0., kaonTotalMomentum = 0.; + + for (auto const& [protonTrack, kaonTrack] : + soa::combinations(soa::CombinationsFullIndexPolicy(protonCandidates, + kaonCandidates))) { + if (protonTrack.index() == kaonTrack.index()) + continue; + if (!passesBasicTrackSelection(protonTrack) || + !passesBasicTrackSelection(kaonTrack)) + continue; + auto pxProton = protonTrack.px(); + auto pyProton = protonTrack.py(); + auto pzProton = protonTrack.pz(); + auto pxKaon = kaonTrack.px(); + auto pyKaon = kaonTrack.py(); + auto pzKaon = kaonTrack.pz(); + // FIX root/entity: replaced TMath::Sqrt with RecoDecay::p + protonTotalMomentum = RecoDecay::p(pxProton, pyProton, pzProton); + kaonTotalMomentum = RecoDecay::p(pxKaon, pyKaon, pzKaon); + + if (requireTOFForProton && !protonTrack.hasTOF()) + continue; + if (requireTOFForKaon && !kaonTrack.hasTOF()) + continue; + + if (!passesProtonPID(protonTrack, protonTotalMomentum) || + !passesKaonPID(kaonTrack, kaonTotalMomentum)) + continue; + + if (!passesProtonDCASelection(protonTrack, protonTrack.pt()) || + !passesKaonDCASelection(kaonTrack, kaonTrack.pt())) + continue; + + // FIX root/entity: replaced TMath::ACos with std::acos + if (applyDeepAngleCut && + std::acos((protonTrack.pt() * kaonTrack.pt() + pzProton * pzKaon) / + (protonTotalMomentum * kaonTotalMomentum)) < + deepAngleCutValue) + continue; + + if (runQualityChecksOnly) + continue; + + std::array momProton = {pxProton, pyProton, pzProton}; + std::array momKaon = {pxKaon, pyKaon, pzKaon}; + std::array, 2> bothMomenta = {momProton, momKaon}; + + float pairInvMass = RecoDecay::m(bothMomenta, std::array{MassProton, MassKaonCharged}); + float pairPt = RecoDecay::pt(std::array{pxProton + pxKaon, pyProton + pyKaon}); + float pairRapidity = RecoDecay::y(std::array{pxProton + pxKaon, pyProton + pyKaon, pzProton + pzKaon}, pairInvMass); + + if (pairRapidity < minPairRapidity || pairRapidity > maxPairRapidity) + continue; + + if constexpr (!isMixedEvent && !isMCAnalysis) { + if (protonTrack.sign() * kaonTrack.sign() < 0) { + if (protonTrack.sign() > 0) + allHistograms.fill(HIST("Analysis/invMass_UnlikeSign_ProtonPlusKaonMinus"), pairInvMass, pairPt, centralityPercent, occupancyValue); + else + allHistograms.fill(HIST("Analysis/invMass_UnlikeSign_ProtonMinusKaonPlus"), pairInvMass, pairPt, centralityPercent, occupancyValue); + + if (enableRotationalBackground) { + for (int iRot = 0; iRot < numberOfRotations; iRot++) { + // FIX magic-number: named constant for the half-window + // calculation + float rotationWindowHalfWidth = + o2::constants::math::PI / rotationAngleWindow; + float rotatedKaonPhi; + if (numberOfRotations == 1) { + rotatedKaonPhi = o2::constants::math::PI; + } else { + rotatedKaonPhi = + (o2::constants::math::PI - rotationWindowHalfWidth) + + iRot * (2.f * rotationWindowHalfWidth / + (numberOfRotations - 1)); + } + + // FIX two-pi-add-subtract: use RecoDecay::constrainAngle instead + // of manual +/- TwoPI + float newKaonPhi = RecoDecay::constrainAngle( + kaonTrack.phi() + rotatedKaonPhi, 0.f); + + float pxKaonRotated = kaonTrack.pt() * std::cos(newKaonPhi); + float pyKaonRotated = kaonTrack.pt() * std::sin(newKaonPhi); + + std::array momProtonRot = {pxProton, pyProton, + pzProton}; + std::array momKaonRot = {pxKaonRotated, pyKaonRotated, + pzKaon}; + std::array, 2> rotatedMomenta = { + momProtonRot, momKaonRot}; + + float rotatedPairInvMass = RecoDecay::m( + rotatedMomenta, std::array{MassProton, MassKaonCharged}); + float rotatedPairPt = RecoDecay::pt(std::array{ + pxProton + pxKaonRotated, pyProton + pyKaonRotated}); + float rotatedPairRapidity = RecoDecay::y( + std::array{pxProton + pxKaonRotated, pyProton + pyKaonRotated, + pzProton + pzKaon}, + MassLambda1520); + + if (rotatedPairRapidity < minPairRapidity || + rotatedPairRapidity > maxPairRapidity) + continue; + + if (protonTrack.sign() * kaonTrack.sign() < 0) { + if (protonTrack.sign() > 0) + allHistograms.fill( + HIST("Analysis/invMass_Rotated_ProtonPlusKaonMinus"), + rotatedPairInvMass, rotatedPairPt, centralityPercent, + occupancyValue); + else + allHistograms.fill( + HIST("Analysis/invMass_Rotated_ProtonMinusKaonPlus"), + rotatedPairInvMass, rotatedPairPt, centralityPercent, + occupancyValue); + } + } + } + + } else { + if (protonTrack.sign() > 0) + allHistograms.fill( + HIST("Analysis/invMass_LikeSign_ProtonPlusKaonPlus"), + pairInvMass, pairPt, centralityPercent, occupancyValue); + else + allHistograms.fill( + HIST("Analysis/invMass_LikeSign_ProtonMinusKaonMinus"), + pairInvMass, pairPt, centralityPercent, occupancyValue); + } + } + + if constexpr (isMixedEvent) { + if (protonTrack.sign() * kaonTrack.sign() < 0) { + if (protonTrack.sign() > 0) + allHistograms.fill( + HIST("Analysis/invMass_Mixed_ProtonPlusKaonMinus"), pairInvMass, + pairPt, centralityPercent, occupancyValue); + else + allHistograms.fill( + HIST("Analysis/invMass_Mixed_ProtonMinusKaonPlus"), pairInvMass, + pairPt, centralityPercent, occupancyValue); + } else { + if (protonTrack.sign() > 0) + allHistograms.fill(HIST("Analysis/invMass_Mixed_LikeSign_PlusPlus"), + pairInvMass, pairPt, centralityPercent, + occupancyValue); + else + allHistograms.fill( + HIST("Analysis/invMass_Mixed_LikeSign_MinusMinus"), pairInvMass, + pairPt, centralityPercent, occupancyValue); + } + } + + if constexpr (isMCAnalysis) { + if (protonTrack.sign() * kaonTrack.sign() < 0) { + // FIX pdg/explicit-code: use named constants from lambda_analysis + // namespace + if (std::abs(protonTrack.pdgCode()) != lambda_analysis::PdgProton || + std::abs(kaonTrack.pdgCode()) != lambda_analysis::PdgKaon) + continue; + if (protonTrack.motherId() != kaonTrack.motherId()) + continue; + if (protonTrack.motherPDG() != kaonTrack.motherPDG()) + continue; + if (protonTrack.pdgCode() == 0 || kaonTrack.pdgCode() == 0) + continue; + if (protonTrack.motherPDG() == -1 || kaonTrack.motherPDG() == -1) + continue; + if (std::abs(protonTrack.motherPDG()) != pdgCodeLambda1520) + continue; + + float trueMassOfParent = 0.; + for (auto const& parentParticle : *mcResonanceParentTable) { + if (parentParticle.mcParticleId() == protonTrack.motherId()) { + std::array parentMom = {parentParticle.px(), + parentParticle.py(), + parentParticle.pz()}; + trueMassOfParent = RecoDecay::m(parentMom, parentParticle.e()); + break; + } + } + + float massResidual = pairInvMass - trueMassOfParent; + + if (protonTrack.motherPDG() > 0) { + allHistograms.fill(HIST("Analysis/mcReconstructed_Lambda1520"), + pairInvMass, pairPt, centralityPercent); + allHistograms.fill(HIST("Analysis/mcMassResolution_Lambda1520"), + massResidual, pairPt, centralityPercent); + } else { + allHistograms.fill(HIST("Analysis/mcReconstructed_AntiLambda1520"), + pairInvMass, pairPt, centralityPercent); + allHistograms.fill(HIST("Analysis/mcMassResolution_AntiLambda1520"), + massResidual, pairPt, centralityPercent); + } + } + } + } + } + + // ── Type aliases ───────────────────────────────────────────────────────── + // FIX name/type: use UpperCamelCase for type aliases + using ResonanceCollisionsWithEP = + soa::Join; + using ResonanceMCCollisions = + soa::Join; + using ResonanceTrackTable = aod::ResoTracks; + + // ============================================================ + // processData() + // ============================================================ + void processData(ResonanceCollisionsWithEP::iterator const& collision, + ResonanceTrackTable const& tracks) + { + if (cEvtRecINELgt0 && !collision.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision + return; + + allHistograms.fill(HIST("Event/centralityVsOccupancy"), collision.cent(), + 100); + allHistograms.fill(HIST("Event/primaryVertexZ"), collision.posZ()); + for (const auto& track : tracks) { + fillTrackQAHistograms(track, collision.cent()); + } + fillInvariantMassHistograms(tracks, tracks, collision.cent()); + } + PROCESS_SWITCH(lambda1520analysispo, processData, + "Process real collision data (same-event analysis)", true); + + // ============================================================ + // processMC() + // FIX const-ref-in-process: table subscriptions use const& + // ============================================================ + void processMC(ResonanceMCCollisions::iterator const& collision, + soa::Join const& tracks, + aod::ResoMCParents const& mcParents) + { + allHistograms.fill(HIST("Event/mcEventSelectionCutflow"), 0); + + if (mcRequireTriggerTVX && !collision.isTriggerTVX()) + return; + allHistograms.fill(HIST("Event/mcEventSelectionCutflow"), 1); + + if (mcRequireVtxWithin10cm && !collision.isVtxIn10()) + return; + allHistograms.fill(HIST("Event/mcEventSelectionCutflow"), 2); + + if (mcRequireINELgt0 && !collision.isINELgt0()) + return; + allHistograms.fill(HIST("Event/mcEventSelectionCutflow"), 3); + + if (mcRequireSel8 && !collision.isInSel8()) + return; + allHistograms.fill(HIST("Event/mcEventSelectionCutflow"), 4); + + if (cEvtRecINELgt0 && !collision.isRecINELgt0()) + return; + allHistograms.fill(HIST("Event/mcEventSelectionCutflow"), 5); + + if (mcRequireAfterAllCuts && !collision.isInAfterAllCuts()) + return; + allHistograms.fill(HIST("Event/mcEventSelectionCutflow"), 6); + + auto centralityPercent = collision.cent(); + allHistograms.fill(HIST("Event/centralityVsOccupancy"), centralityPercent, + 100); + allHistograms.fill(HIST("Event/primaryVertexZ"), collision.posZ()); + + mcResonanceParentTable = &mcParents; + fillInvariantMassHistograms(tracks, tracks, centralityPercent); + + // FIX const-ref-in-for-loop: use const auto& in all range-based for loops + for (const auto& track : tracks) { + allHistograms.fill(HIST("QAbefore/trackEta"), track.eta()); + allHistograms.fill(HIST("QAbefore/trackPt"), track.pt()); + allHistograms.fill(HIST("QAbefore/trackPhi"), track.phi()); + allHistograms.fill(HIST("QAbefore/trackEtaVsPhi"), track.eta(), + track.phi()); + + // FIX pdg/explicit-code: use named PDG constants + if (std::abs(track.pdgCode()) == lambda_analysis::PdgKaon) + allHistograms.fill(HIST("QAChecks/kaonGenLevelPt"), track.pt()); + if (std::abs(track.pdgCode()) == lambda_analysis::PdgProton) + allHistograms.fill(HIST("QAChecks/protonGenLevelPt"), track.pt()); + + if (!passesBasicTrackSelection(track)) + continue; + + fillTrackQAHistograms(track, centralityPercent); + + // FIX root/entity: replaced TMath::Sqrt with RecoDecay::p + float totalMom = RecoDecay::p(track.px(), track.py(), track.pz()); + + if (passesKaonPID(track, totalMom) && + std::abs(track.pdgCode()) == lambda_analysis::PdgKaon) + allHistograms.fill(HIST("QAChecks/kaonRecoLevelPt"), track.pt()); + if (passesProtonPID(track, totalMom) && + std::abs(track.pdgCode()) == lambda_analysis::PdgProton) + allHistograms.fill(HIST("QAChecks/protonRecoLevelPt"), track.pt()); + } + + // FIX const-ref-in-for-loop: use const auto& in all range-based for loops + for (const auto& parentParticle : mcParents) { + if (std::abs(parentParticle.pdgCode()) != pdgCodeLambda1520) + continue; + if (parentParticle.y() < minPairRapidity || + parentParticle.y() > maxPairRapidity) + continue; + + // FIX pdg/explicit-code: use named PDG constants + bool hasProtonDaughter = (std::abs(parentParticle.daughterPDG1()) == + lambda_analysis::PdgProton || + std::abs(parentParticle.daughterPDG2()) == + lambda_analysis::PdgProton); + bool hasKaonDaughter = + (std::abs(parentParticle.daughterPDG1()) == + lambda_analysis::PdgKaon || + std::abs(parentParticle.daughterPDG2()) == lambda_analysis::PdgKaon); + if (!hasProtonDaughter || !hasKaonDaughter) + continue; + + std::array parentMom = { + parentParticle.px(), parentParticle.py(), parentParticle.pz()}; + float parentMass = RecoDecay::m(parentMom, parentParticle.e()); + + if (parentParticle.pdgCode() > 0) + allHistograms.fill(HIST("Analysis/mcGenerated_Lambda1520"), parentMass, + parentParticle.pt(), centralityPercent); + else + allHistograms.fill(HIST("Analysis/mcGenerated_AntiLambda1520"), + parentMass, parentParticle.pt(), centralityPercent); + } + } + PROCESS_SWITCH(lambda1520analysispo, processMC, + "Process Monte Carlo simulated events", false); + + // ============================================================ + // processMCGen() + // ============================================================ + void processMCGen(ResonanceMCCollisions::iterator const& collision, + aod::ResoMCParents const& mcParents) + { + float centralityPercent = collision.cent(); + + allHistograms.fill(HIST("SignalLoss/mcEventSelectionCutflow"), 0); + if (mcRequireTriggerTVX && !collision.isTriggerTVX()) + return; + allHistograms.fill(HIST("SignalLoss/mcEventSelectionCutflow"), 1); + if (mcRequireVtxWithin10cm && !collision.isVtxIn10()) + return; + allHistograms.fill(HIST("SignalLoss/mcEventSelectionCutflow"), 2); + if (mcRequireINELgt0 && !collision.isINELgt0()) + return; + allHistograms.fill(HIST("SignalLoss/mcEventSelectionCutflow"), 3); + if (mcRequireSel8 && !collision.isInSel8()) + return; + allHistograms.fill(HIST("SignalLoss/mcEventSelectionCutflow"), 4); + if (cEvtRecINELgt0 && !collision.isRecINELgt0()) + return; + allHistograms.fill(HIST("SignalLoss/mcEventSelectionCutflow"), 5); + if (mcRequireAfterAllCuts && !collision.isInAfterAllCuts()) + return; + allHistograms.fill(HIST("SignalLoss/mcEventSelectionCutflow"), 6); + + // FIX const-ref-in-for-loop: use const auto& in all range-based for loops + for (const auto& parentParticle : mcParents) { + if (parentParticle.y() < minPairRapidity || + parentParticle.y() > maxPairRapidity) + continue; + + int pdgCode = parentParticle.pdgCode(); + float parentPt = parentParticle.pt(); + double mTScaledPtSquared = -1.0; + + std::array parentMom = { + parentParticle.px(), parentParticle.py(), parentParticle.pz()}; + float parentMass = RecoDecay::m(parentMom, parentParticle.e()); + + // FIX pdg/explicit-mass: use o2::constants::physics::MassLambda1520 + // (already done via using) + auto computeMtScaledPtSquared = [&]() -> double { + return (parentPt * parentPt) + (parentMass * parentMass) - + (MassLambda1520 * MassLambda1520); + }; + + // FIX pdg/explicit-code: use named PDG constants from lambda_analysis + // namespace + if (pdgCode == lambda_analysis::PdgProton) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromProton"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == -lambda_analysis::PdgProton) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromAntiProton"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == lambda_analysis::PdgLambda0) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromLambda0"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == -lambda_analysis::PdgLambda0) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromAntiLambda0"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == lambda_analysis::PdgXiMinus) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromXiMinus"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == -lambda_analysis::PdgXiMinus) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromXiPlus"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == lambda_analysis::PdgXi0) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromXi0"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == -lambda_analysis::PdgXi0) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromAntiXi0"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == lambda_analysis::PdgOmegaMinus) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromOmegaMinus"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } else if (pdgCode == -lambda_analysis::PdgOmegaMinus) { + mTScaledPtSquared = computeMtScaledPtSquared(); + if (mTScaledPtSquared > 0) + allHistograms.fill(HIST("SignalLoss/mTScaled_fromOmegaPlus"), + std::sqrt(mTScaledPtSquared), centralityPercent); + } + } + } + PROCESS_SWITCH(lambda1520analysispo, processMCGen, + "Generator-level MC signal loss study (mT scaling)", false); + + // ── Event-mixing binning types ─────────────────────────────────────────── + // FIX name/type: UpperCamelCase type aliases + using MixingBinningVtxZAndCentrality = + ColumnBinningPolicy; + + // ============================================================ + // processMix() + // FIX const-ref-in-process: collisions argument is const& + // ============================================================ + void processMix(ResonanceCollisionsWithEP const& collisions, + ResonanceTrackTable const& tracks) + { + LOGF(debug, "Event mixing started"); + MixingBinningVtxZAndCentrality mixingBins{ + {vertexZMixingBins, centralityMixingBins}}; + auto trackPool = std::make_tuple(tracks); + SameKindPair + eventPairs{mixingBins, numberOfEventsToMix, -1, + collisions, trackPool, &sliceCache}; + + // FIX const-ref-in-for-loop: const auto& structured binding + for (const auto& [col1, tracks1, col2, tracks2] : eventPairs) { + if (cEvtRecINELgt0 && !col1.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision + continue; + allHistograms.fill(HIST("Event/mixingBins_centralityVsVtxZVsEventPlane"), + col1.cent(), col1.posZ(), col1.evtPl()); + fillInvariantMassHistograms(tracks1, tracks2, col1.cent()); + } + } + PROCESS_SWITCH(lambda1520analysispo, processMix, + "Event mixing for background estimation (standard format)", + true); + + // ── Merged-DF type aliases ─────────────────────────────────────────────── + Preslice tracksPerMergedDFCollision = + aod::resodaughter::resoCollisionDFId; + using MergedDFCollisions = aod::ResoCollisionDFs; + using MergedDFTracks = aod::ResoTrackDFs; + + // ============================================================ + // processDatadf() + // ============================================================ + void processDatadf(MergedDFCollisions::iterator const& collision, + MergedDFTracks const& tracks) + { + if (doprocessData) + LOG(error) << "Disable processData() first when using processDatadf()!"; + + if (cEvtRecINELgt0 && !collision.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision + return; + auto occupancyValue = 100; + if (applyOccupancyInTimeRangeCut) + occupancyValue = collision.trackOccupancyInTimeRange(); + + allHistograms.fill(HIST("Event/centralityVsOccupancy"), collision.cent(), + occupancyValue); + for (const auto& track : tracks) { + fillTrackQAHistograms(track, collision.cent()); + } + fillInvariantMassHistograms(tracks, tracks, collision.cent(), + occupancyValue); + } + PROCESS_SWITCH(lambda1520analysispo, processDatadf, + "Process real data in merged derived-data (DF) format", false); + + // ============================================================ + // processMixDF() + // FIX const-ref-in-process: collisions argument is const& + // ============================================================ + // FIX name/type: UpperCamelCase type alias + using MixingBinningDF = + ColumnBinningPolicy; + + void processMixDF(MergedDFCollisions const& collisions, + MergedDFTracks const& tracks) + { + if (doprocessMix) + LOG(fatal) << "Disable processMix() first when using processMixDF()!"; + LOGF(debug, "Event mixing (DF format) started"); + + MixingBinningDF mixingBins{{vertexZMixingBins, centralityMixingBins}}; + auto trackPool = std::make_tuple(tracks); + SameKindPair + eventPairs{mixingBins, numberOfEventsToMix, -1, + collisions, trackPool, &sliceCache}; + + // FIX const-ref-in-for-loop: const auto& structured binding + for (const auto& [col1, tracks1, col2, tracks2] : eventPairs) { + if (cEvtRecINELgt0 && !col1.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision + continue; + auto occupancyValue = 100; + if (applyOccupancyInTimeRangeCut) + occupancyValue = col1.trackOccupancyInTimeRange(); + + allHistograms.fill(HIST("Event/mixingBins_centralityVsVtxZVsEventPlane"), + col1.cent(), col1.posZ(), col1.evtPl()); + fillInvariantMassHistograms(tracks1, tracks2, col1.cent(), + occupancyValue); + } + } + PROCESS_SWITCH(lambda1520analysispo, processMixDF, + "Event mixing for DF-format data", false); + + // ============================================================ + // processMixepDF() + // FIX const-ref-in-process: collisions argument is const& + // ============================================================ + // FIX name/type: UpperCamelCase type alias + using MixingBinningWithEventPlane = + ColumnBinningPolicy; + + void processMixepDF(MergedDFCollisions const& collisions, + MergedDFTracks const& tracks) + { + if (doprocessMix || doprocessMixDF) + LOG(fatal) << "Disable processMix() or processMixDF() first!"; + LOGF(debug, "Event-plane-dependent event mixing (DF format) started"); + + MixingBinningWithEventPlane mixingBins{ + {vertexZMixingBins, centralityMixingBins, eventPlaneMixingBins}}; + auto trackPool = std::make_tuple(tracks); + SameKindPair + eventPairs{mixingBins, numberOfEventsToMix, -1, + collisions, trackPool, &sliceCache}; + + // FIX const-ref-in-for-loop: const auto& structured binding + for (const auto& [col1, tracks1, col2, tracks2] : eventPairs) { + allHistograms.fill(HIST("Event/mixingBins_centralityVsVtxZVsEventPlane"), + col1.cent(), col1.posZ(), col1.evtPl()); + fillInvariantMassHistograms(tracks1, tracks2, col1.cent()); + } + } + PROCESS_SWITCH(lambda1520analysispo, processMixepDF, + "Event-plane dependent event mixing for DF-format data", + false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx new file mode 100644 index 00000000000..8e06ab022dd --- /dev/null +++ b/PWGLF/Tasks/Resonances/lambda1520pbpb.cxx @@ -0,0 +1,1339 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambda1520pbpb.cxx +/// \brief Invariant-mass reconstruction and cosThetaStar polarization/spin-alignment +/// analysis (Helicity / Collins-Soper / Production / Beam-axis / Random frames) +/// of the Lambda(1520) resonance in Pb-Pb collisions. +/// +/// \author Dukhishyam Mallick +/// \author Nasir Mehdi Malik + +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +struct Lambda1520pbpb { + SliceCache cache; + Preslice perRCol = aod::resodaughter::resoCollisionId; + Preslice perCollision = aod::track::collisionId; + // Configurables. + aod::ResoMCParents const* mResoParents = nullptr; + Configurable cfgEvtOccupancyInTimeRange{"cfgEvtOccupancyInTimeRange", false, "occupancy selection true or false"}; + Configurable nBinsPt{"nBinsPt", 100, "N bins in pT histogram"}; + Configurable nBinsInvM{"nBinsInvM", 120, "N bins in InvMass histogram"}; + Configurable lambda1520id{"lambda1520id", 3124, "pdg"}; + Configurable doRotate{"doRotate", true, "rotated inv mass spectra"}; + // Tracks + Configurable cPtMin{"cPtMin", 0.15, "Minimum Track pT"}; + Configurable cPMin{"cPMin", 0., "Minimum Track p"}; + Configurable cEtaCut{"cEtaCut", 0.8, "Pseudorapidity cut"}; + Configurable cDcazMin{"cDcazMin", 1., "Minimum DCAz"}; + Configurable cfgRapidityShift{"cfgRapidityShift", 0., " rapidity shift"}; + Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity window"}; + // TPC crossed rows (absolute) + Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min TPC crossed rows"}; + Configurable cfgUseCrossedRows{"cfgUseCrossedRows", false, "apply crossed rows cut"}; + + Configurable cfgMinTPCcls{"cfgMinTPCcls", 70, "min TPC clusters found"}; + Configurable cfgUseTPCcls{"cfgUseTPCcls", false, "apply TPC clusters cut"}; + + Configurable> cDcaPtBinsPr{"cDcaPtBinsPr", {0.0f, 0.5f, 1.0f, 2.0f, 3.0f, 5.0f, 1000.0f}, "Proton pT bin edges for DCAxy cut"}; + Configurable> cDcaXYBinsPr{"cDcaXYBinsPr", {0.020f, 0.015f, 0.010f, 0.007f, 0.005f, 0.004f}, "Proton max |DCAxy| per pT bin (cm)"}; + + // Kaon DCAxy — pT binned + Configurable> cDcaPtBinsKa{"cDcaPtBinsKa", {0.0f, 0.3f, 0.6f, 1.0f, 2.0f, 1000.0f}, "Kaon pT bin edges for DCAxy cut"}; + Configurable> cDcaXYBinsKa{"cDcaXYBinsKa", {0.025f, 0.018f, 0.012f, 0.008f, 0.004f}, "Kaon max |DCAxy| per pT bin (cm)"}; + + Configurable isonlyQC{"isonlyQC", false, "only QC"}; + Configurable isDeepAngle{"isDeepAngle", false, "Deep Angle cut"}; + Configurable cfgDeepAngle{"cfgDeepAngle", 0.04, "Deep Angle cut value"}; + Configurable cKinCuts{"cKinCuts", false, "Kinematic Cuts for p-K pair opening angle"}; + Configurable cPrimaryTrack{"cPrimaryTrack", true, "Primary track selection"}; + Configurable cGlobalWoDCATrack{"cGlobalWoDCATrack", true, "Global track selection without DCA"}; + Configurable cPVContributor{"cPVContributor", true, "PV contributor track selection"}; + + // PID Selections + Configurable cUseOnlyTOFTrackPr{"cUseOnlyTOFTrackPr", false, "Use only TOF track for PID selection"}; + Configurable cUseOnlyTOFTrackKa{"cUseOnlyTOFTrackKa", false, "Use only TOF track for PID selection"}; + Configurable cUseTpcOnly{"cUseTpcOnly", false, "Use TPC Only selection"}; + Configurable cRejNsigmaTpc{"cRejNsigmaTpc", 3.0, "Reject tracks to improve purity of TPC PID"}; + Configurable cRejNsigmaTpcPi{"cRejNsigmaTpcPi", 3.0, "Reject tracks to improve purity of TPC PID"}; + Configurable cRejNsigmaTpcKa{"cRejNsigmaTpcKa", 3.0, "Reject tracks to improve purity of TPC PID"}; + Configurable cRejNsigmakTpcPi{"cRejNsigmakTpcPi", 3.0, "Reject tracks to improve purity of TPC PID"}; + Configurable cRejNsigmakTpcPr{"cRejNsigmakTpcPr", 3.0, "Reject tracks to improve purity of TPC PID"}; + Configurable minnsigmatpcKa{"minnsigmatpcKa", -6.0, "Reject tracks to improve purity of TPC PID"}; + Configurable minnsigmatpcPr{"minnsigmatpcPr", -6.0, "Reject tracks to improve purity of TPC PID"}; + Configurable minnsigmatofKa{"minnsigmatofKa", -6.0, "Reject tracks to improve purity of TofPID"}; + Configurable minnsigmatofPr{"minnsigmatofPr", -6.0, "Reject tracks to improve purity of Tof PID"}; + Configurable minnsigmatpctofKa{"minnsigmatpctofKa", -6.0, "Reject tracks to improve purity of TPC PID"}; + Configurable minnsigmatpctofPr{"minnsigmatpctofPr", -6.0, "Reject tracks to improve purity of TPC PID"}; + Configurable cRejNsigmaTpcVeto{"cRejNsigmaTpcVeto", 3.0, "Reject tracks to improve purity of TPC PID"}; + Configurable cRejNsigmaTof{"cRejNsigmaTof", 3.0, "Reject tracks to improve purity of TOF PID"}; + + // Proton + Configurable cMaxTPCnSigmaProton{"cMaxTPCnSigmaProton", 3.0, "TPC nSigma cut for Proton"}; + Configurable nsigmaCutCombinedProton{"nsigmaCutCombinedProton", 3.0, "Combined nSigma cut for Proton"}; + Configurable> protonTPCPIDp{"protonTPCPIDp", {0, 0.5, 0.7, 0.8}, "p dependent TPC cuts protons"}; + Configurable> protonTPCPIDcut{"protonTPCPIDcut", {5., 3.5, 2.5}, "TPC nsigma cuts protons"}; + Configurable> protonTOFPIDp{"protonTOFPIDp", {0., 999.}, "p dependent TOF cuts protons"}; + Configurable> protonTOFPIDcut{"protonTOFPIDcut", {3.0}, "TOF nsigma cuts protons"}; + + // Kaon + Configurable cMaxTPCnSigmaKaon{"cMaxTPCnSigmaKaon", 3.0, "TPC nSigma cut for Kaon"}; + Configurable nsigmaCutCombinedKaon{"nsigmaCutCombinedKaon", 3.0, "Combined nSigma cut for Kaon"}; + Configurable> kaonTPCPIDp{"kaonTPCPIDp", {0., 0.25, 0.3, 0.45}, "pT dependent TPC cuts kaons"}; + Configurable> kaonTPCPIDcut{"kaonTPCPIDcut", {6, 3.5, 2.5}, "TPC nsigma cuts kaons"}; + Configurable> kaonTOFPIDp{"kaonTOFPIDp", {0., 999.}, "p dependent TOF cuts kaons"}; + Configurable> kaonTOFPIDcut{"kaonTOFPIDcut", {3.0}, "TOF nsigma cuts kaons"}; + + // Event Mixing. + Configurable cNumMixEv{"cNumMixEv", 20, "Number of Events to be mixed"}; + ConfigurableAxis cDcaZBins{"cDcaZBins", {VARIABLE_WIDTH, -1.2f, -1.0f, -0.9f, -0.8f, -0.7f, -0.6f, -0.5f, -0.4f, -0.3f, -0.2f, -0.1f, 0.f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.2f}, "DCA - z-vertex"}; + ConfigurableAxis cMixVtxBins{"cMixVtxBins", {VARIABLE_WIDTH, -10.0f, -9.f, -8.f, -7.f, -6.f, -5.f, -4.f, -3.f, -2.f, -1.f, 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f, 10.f}, "Mixing bins - z-vertex"}; + ConfigurableAxis cMixMultBins{"cMixMultBins", {VARIABLE_WIDTH, 0.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 200.0f}, "Mixing bins - multiplicity"}; + ConfigurableAxis cMixEPAngle{"cMixEPAngle", {VARIABLE_WIDTH, -1.5708f, -1.25664f, -0.942478f, -0.628319f, 0.f, 0.628319f, 0.942478f, 1.25664f, 1.5708f}, "event plane"}; + ConfigurableAxis occupancyBins{"occupancyBins", {VARIABLE_WIDTH, 0.0, 100, 500, 600, 1000, 1100, 1500, 1600, 2000, 2100, 2500, 2600, 3000, 3100, 3500, 3600, 4000, 4100, 4500, 4600, 5000, 5100, 9999}, "Binning of the occupancy axis"}; + Configurable cNofRotations{"cNofRotations", 10, "Number of rotations for rotational background"}; + Configurable rotationalcut{"rotationalcut", 6.f, "Rotational background angle window: PI/rotationalcut"}; + + // ── MC Event Selection Configurables ───────────────────────────────────── + Configurable cEvtMCAfterAllCuts{"cEvtMCAfterAllCuts", false, "MC event sel: isInAfterAllCuts"}; + Configurable cEvtMCINELgt0{"cEvtMCINELgt0", false, "MC event sel: isINELgt0"}; + Configurable cEvtMCSel8{"cEvtMCSel8", false, "MC event sel: isInSel8"}; + Configurable cEvtMCVtxIn10{"cEvtMCVtxIn10", false, "MC event sel: isVtxIn10"}; + Configurable cEvtMCTriggerTVX{"cEvtMCTriggerTVX", false, "MC event sel: isTriggerTVX"}; + Configurable cEvtMCRecINELgt0{"cEvtMCRecINELgt0", false, "MC event sel: isRecINELgt0"}; + + // ── Polarization / angular-distribution (cosThetaStar) Configurables ───── + Configurable cPolarizationRefDaughter{"cPolarizationRefDaughter", 0, "Reference daughter for cosThetaStar: 0 = proton, 1 = kaon"}; + Configurable cActivateHelicityFrame{"cActivateHelicityFrame", false, "Compute cosThetaStar in the Helicity frame"}; + Configurable cActivateCollinsSoperFrame{"cActivateCollinsSoperFrame", false, "Compute cosThetaStar in the Collins-Soper frame"}; + Configurable cActivateProductionFrame{"cActivateProductionFrame", false, "Compute cosThetaStar wrt the production-plane normal"}; + Configurable cActivateBeamAxisFrame{"cActivateBeamAxisFrame", false, "Compute cosThetaStar in the beam-axis (Gottfried-Jackson) frame"}; + Configurable cActivateRandomFrame{"cActivateRandomFrame", false, "Compute cosThetaStar wrt a random axis (null/control test, should be flat)"}; + Configurable doPolarizationRot{"doPolarizationRot", false, "Also fill rotated-background polarization histograms"}; + Configurable doPolarizationMix{"doPolarizationMix", false, "Also fill mixed-event-background polarization histograms"}; + Configurable cfgBeamEnergy{"cfgBeamEnergy", 13600.0, "sqrt(s) beam energy in GeV (used for Collins-Soper/beam-axis/production frames)"}; + ConfigurableAxis cCosThetaBins{"cCosThetaBins", {20, -1.0f, 1.0f}, "cosThetaStar binning"}; + + float beam1E = 0.f; + std::array beam1P{}; + float beam2E = 0.f; + std::array beam2P{}; + + std::mt19937 randGen{std::random_device{}()}; + std::uniform_real_distribution randUniform01{0., 1.}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + void init(InitContext const&) + { + const AxisSpec axisCent(110, 0, 110, "FT0 (%)"); + const AxisSpec axisPpid(200, 0., 10., "p (GeV/c)"); + const AxisSpec axisPtpid(200, 0., 10., "p_{T} (GeV/c)"); + const AxisSpec axisPt(nBinsPt, 0., 10., "p_{T} (GeV/c)"); + const AxisSpec axisEta(40, -1, 1, "#eta"); + const AxisSpec axisDCAxy(240, -0.12, 0.12, {"DCA_{xy} (cm)"}); + const AxisSpec axisTPCNsigma(401, -10.025, 10.025, {"n#sigma^{TPC}"}); + const AxisSpec axisTOFNsigma(401, -10.025, 10.025, {"n#sigma^{TOF}"}); + const AxisSpec axisdEdx(380, 10, 200, {"#frac{dE}{dx}"}); + const AxisSpec axisVz(120, -12, 12, {"vz"}); + const AxisSpec axisEP(120, -3.14, 3.14, {"#theta"}); + const AxisSpec axisInvM(nBinsInvM, 1.4, 2.0, {"M_{inv} (GeV/c^{2})"}); + AxisSpec axisOccupancy = {occupancyBins, "Occupancy [-40,100]"}; + AxisSpec axisDCAz = {cDcaZBins, "DCA_{z} (cm)"}; + + double beamMomentum = std::sqrt(cfgBeamEnergy * cfgBeamEnergy / 4. - MassProton * MassProton); + beam1E = static_cast(cfgBeamEnergy * 0.5); + beam1P = {0.f, 0.f, static_cast(-beamMomentum)}; + beam2E = static_cast(cfgBeamEnergy * 0.5); + beam2P = {0.f, 0.f, static_cast(beamMomentum)}; + + histos.add("Event/h1d_ft0_mult_percentile", "FT0 (%)", kTH2F, {axisCent, axisOccupancy}); + histos.add("Event/h_ft0_vz", "Collision Vertex Z position", kTH1F, {{100, -15., 15.}}); + if (doprocessMix || doprocessMixDF || doprocessMixepDF) { + histos.add("Event/mixing_vzVsmultpercentile", "FT0(%)", kTH3F, {axisCent, axisVz, axisEP}); + } + + // QA Before + histos.add("QAbefore/hEta_rec", "Reco dN/d#eta; #eta; dN/d#eta", kTH1F, {{50, -1.0, 1.0}}); + histos.add("QAbefore/hPt_rec", "Reco pT; p_{T} (GeV/c); Tracks", kTH1F, {axisPpid}); + histos.add("QAbefore/hPhi_rec", "Reco #varphi; #varphi (rad); Tracks", kTH1F, {{72, 0, 6.2832}}); + histos.add("QAbefore/hEtaPhi_rec", "Reco #eta vs #varphi; #eta; #varphi", kTH2F, {axisEta, {72, 0, 6.2832}}); + histos.add("QAbefore/Proton/h2d_pr_nsigma_tpc_p", "n#sigma^{TPC} Protons", kTH2F, {axisPpid, axisTPCNsigma}); + histos.add("QAbefore/Proton/h2d_pr_nsigma_tof_p", "n#sigma^{TOF} Protons", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAbefore/Proton/h2d_pr_nsigma_tof_vs_tpc", "n#sigma^{TPC} vs n#sigma^{TOF} Protons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); + histos.add("QAbefore/Kaon/h2d_ka_nsigma_tpc_p", "n#sigma^{TPC} Kaons", kTH2F, {axisPpid, axisTPCNsigma}); + histos.add("QAbefore/Kaon/h2d_ka_nsigma_tof_p", "n#sigma^{TOF} Kaons", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAbefore/Kaon/h2d_ka_nsigma_tof_vs_tpc", "n#sigma^{TPC} vs n#sigma^{TOF} Kaons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); + + // ======================================================================== + // QA After - Protons + // ======================================================================== + // 1. Base QA (Filled for all tracks) + histos.add("QAafter/Proton/h2d_pr_dca_z", "dca_{z} Protons", kTH2F, {axisPtpid, axisDCAz}); + histos.add("QAafter/Proton/h2d_pr_dca_xy", "dca_{xy} Protons", kTH2F, {axisPtpid, axisDCAxy}); + histos.add("QAafter/Proton/h2d_pr_dEdx_p", "TPC Signal Protons", kTH2F, {axisPpid, axisdEdx}); + histos.add("QAafter/Proton/hTPCNClsCrossedRowsVsPt", "TPC Crossed Rows vs pT;p_{T} (GeV/c);N_{cls,crossed};Counts", kTH2F, {axisPtpid, {200, 0, 200}}); + histos.add("QAafter/Proton/hTPCNClsFoundVsPt", "TPC Found Clusters vs pT;p_{T} (GeV/c);N_{cls,found};Counts", kTH2F, {axisPtpid, {200, 0, 200}}); + histos.add("QAafter/Proton/hFracTPConly_Proton", "Proton candidates using TPC-only PID;p (GeV/c);TPC-only (0=no,1=yes)", kTH2F, {axisPpid, {2, 0, 2}}); + + // 2. TPC-Only Split + histos.add("QAafter/Proton/hd_pr_pt_TPConly", "p_{T}-spectra Protons, TPC-only PID", kTH2F, {axisPtpid, axisCent}); + histos.add("QAafter/Proton/h2d_pr_nsigma_tpc_pt_TPConly", "Protons, TPC-only PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Proton/h2d_Prpi_nsigma_tpc_p_TPConly", "Protons pion, TPC-only PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Proton/h2d_Prka_nsigma_tpc_p_TPConly", "Protons kaon, TPC-only PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Proton/h2d_pr_nsigma_tpc_p_TPConly", "Protons, TPC-only PID", kTH2F, {axisPpid, axisTPCNsigma}); + + // 3. hasTOF Split + histos.add("QAafter/Proton/hd_pr_pt_hasTOF", "p_{T}-spectra Protons, hasTOF PID", kTH2F, {axisPtpid, axisCent}); + histos.add("QAafter/Proton/h2d_pr_nsigma_tpc_pt_hasTOF", "Protons, hasTOF PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Proton/h2d_Prpi_nsigma_tpc_p_hasTOF", "Protons pion, hasTOF PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Proton/h2d_Prka_nsigma_tpc_p_hasTOF", "Protons kaon, hasTOF PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Proton/h2d_pr_nsigma_tpc_p_hasTOF", "Protons, hasTOF PID", kTH2F, {axisPpid, axisTPCNsigma}); + + // 4. TOF-Specific (Only possible if hasTOF) + histos.add("QAafter/Proton/h2d_pr_nsigma_tof_pt", " Protons", kTH2F, {axisPtpid, axisTOFNsigma}); + histos.add("QAafter/Proton/h2d_pr_nsigma_tof_p", " Protons", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAafter/Proton/h2d_Prpi_nsigma_tof_p", " Protons pion", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAafter/Proton/h2d_Prka_nsigma_tof_p", " Protons kaon", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAafter/Proton/h2d_pr_nsigma_tof_vs_tpc", "n#sigma(TOF) vs n#sigma(TPC) Protons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); + + // ======================================================================== + // QA After - Kaons + // ======================================================================== + // 1. Base QA (Filled for all tracks) + histos.add("QAafter/Kaon/h2d_ka_dca_z", "dca_{z} Kaons", kTH2F, {axisPtpid, axisDCAz}); + histos.add("QAafter/Kaon/h2d_ka_dca_xy", "dca_{xy} Kaons", kTH2F, {axisPtpid, axisDCAxy}); + histos.add("QAafter/Kaon/h2d_ka_dEdx_p", "TPC Signal Kaon", kTH2F, {axisPpid, axisdEdx}); + histos.add("QAafter/Kaon/hTPCNClsCrossedRowsVsPt", "TPC Crossed Rows vs pT;p_{T} (GeV/c);N_{cls,crossed};Counts", kTH2F, {axisPtpid, {200, 0, 200}}); + histos.add("QAafter/Kaon/hTPCNClsFoundVsPt", "TPC Found Clusters vs pT;p_{T} (GeV/c);N_{cls,found};Counts", kTH2F, {axisPtpid, {200, 0, 200}}); + histos.add("QAafter/Kaon/hFracTPConly_Kaon", "Kaon candidates using TPC-only PID;p (GeV/c);TPC-only (0=no,1=yes)", kTH2F, {axisPpid, {2, 0, 2}}); + + // 2. TPC-Only Split + histos.add("QAafter/Kaon/hd_ka_pt_TPConly", "p_{T}-spectra Kaons, TPC-only PID", kTH2F, {axisPtpid, axisCent}); + histos.add("QAafter/Kaon/h2d_ka_nsigma_tpc_pt_TPConly", "Kaons, TPC-only PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Kaon/h2d_Kapi_nsigma_tpc_p_TPConly", "Kaons pion, TPC-only PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Kaon/h2d_Kapr_nsigma_tpc_p_TPConly", "Kaons proton, TPC-only PID", kTH2F, {axisPpid, axisTPCNsigma}); + histos.add("QAafter/Kaon/h2d_ka_nsigma_tpc_p_TPConly", "Kaons, TPC-only PID", kTH2F, {axisPpid, axisTPCNsigma}); + + // 3. hasTOF Split + histos.add("QAafter/Kaon/hd_ka_pt_hasTOF", "p_{T}-spectra Kaons, hasTOF PID", kTH2F, {axisPtpid, axisCent}); + histos.add("QAafter/Kaon/h2d_ka_nsigma_tpc_pt_hasTOF", "Kaons, hasTOF PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Kaon/h2d_Kapi_nsigma_tpc_p_hasTOF", "Kaons pion, hasTOF PID", kTH2F, {axisPtpid, axisTPCNsigma}); + histos.add("QAafter/Kaon/h2d_Kapr_nsigma_tpc_p_hasTOF", "Kaons proton, hasTOF PID", kTH2F, {axisPpid, axisTPCNsigma}); + histos.add("QAafter/Kaon/h2d_ka_nsigma_tpc_p_hasTOF", "Kaons, hasTOF PID", kTH2F, {axisPpid, axisTPCNsigma}); + + // 4. TOF-Specific (Only possible if hasTOF) + histos.add("QAafter/Kaon/h2d_ka_nsigma_tof_pt", " Kaons", kTH2F, {axisPtpid, axisTOFNsigma}); + histos.add("QAafter/Kaon/h2d_ka_nsigma_tof_p", " Kaons", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAafter/Kaon/h2d_Kapi_nsigma_tof_p", " Kaons pion", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAafter/Kaon/h2d_Kapr_nsigma_tof_p", " Kaons proton", kTH2F, {axisPpid, axisTOFNsigma}); + histos.add("QAafter/Kaon/h2d_ka_nsigma_tof_vs_tpc", "n#sigma(TOF) vs n#sigma(TPC) Kaons", kTH2F, {axisTPCNsigma, axisTOFNsigma}); + + // ── Polarization-frame activation (shared by data and MC booking) ─────── + int nActiveFrames = static_cast(cActivateHelicityFrame) + + static_cast(cActivateCollinsSoperFrame) + + static_cast(cActivateProductionFrame) + + static_cast(cActivateBeamAxisFrame) + + static_cast(cActivateRandomFrame); + + bool polActive = nActiveFrames > 0; + + if (nActiveFrames > 1) { + LOG(fatal) << "Multiple polarization frames activated, but axisPolFrame was removed! " + << "Please activate only ONE frame at a time to prevent data mixing in the 4D sparse."; + } + + const AxisSpec axisCosTheta{cCosThetaBins, "cos#theta*"}; + + // Analysis + if (!doprocessMC) { + if (!polActive) { + histos.add("Analysis/h4d_lstar_invm_US_PM", "THn #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + histos.add("Analysis/h4d_lstar_invm_US_MP", "THn #bar #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + histos.add("Analysis/h4d_lstar_invm_PP", "THn Like Signs p K^{+}", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + histos.add("Analysis/h4d_lstar_invm_MM", "THn Like Signs #bar{p} K^{-}", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + + if (doRotate) { + histos.add("Analysis/h4d_lstar_invm_rot_PM", "THn Rotated", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + histos.add("Analysis/h4d_lstar_invm_rot_MP", "THn Rotated", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + } + + if (doprocessMix || doprocessMixDF || doprocessMixepDF) { + histos.add("Analysis/h4d_lstar_invm_US_PM_mix", "THn Mixed Events", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + histos.add("Analysis/h4d_lstar_invm_US_MP_mix", "THn anti Mixed Events", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + histos.add("Analysis/h4d_lstar_invm_LS_PP_mix", "THn Mixed Events PP", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + histos.add("Analysis/h4d_lstar_invm_LS_MM_mix", "THn Mixed Events MM", kTHnSparseF, {axisInvM, axisPt, axisCent, axisOccupancy}); + } + } + + // ── 4D Polarization / cosThetaStar histograms ────────────────────────── + if (polActive) { + histos.add("Analysis/h4d_pol_US_PM", "cos#theta* #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_US_MP", "cos#theta* #bar{#Lambda}(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_LS_PP", "cos#theta* Like Signs p K^{+}", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_LS_MM", "cos#theta* Like Signs #bar{p} K^{-}", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + + if (doPolarizationRot) { + histos.add("Analysis/h4d_pol_rot_PM", "cos#theta* Rotated bkg", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_rot_MP", "cos#theta* Rotated bkg", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + } + + if (doPolarizationMix && (doprocessMix || doprocessMixDF || doprocessMixepDF)) { + histos.add("Analysis/h4d_pol_mix_PM", "cos#theta* Mixed-event bkg", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_mix_MP", "cos#theta* Mixed-event bkg", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_LS_PP_mix", "cos#theta* Mixed PP", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_LS_MM_mix", "cos#theta* Mixed MM", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + } + } + } + + // MC + if (doprocessMC) { + histos.add("Event/hMCEventCutflow", "MC Event Cutflow", kTH1F, {{7, 0, 7}}); + histos.add("QAChecks/h1d_pr_rec_pt", "Reconstructed p_{T}-spectra Protons", kTH1F, {axisPtpid}); + histos.add("QAChecks/h1d_ka_rec_pt", "Recondstucted p_{T}-spectra Kaons", kTH1F, {axisPtpid}); + histos.add("QAChecks/h1d_pr_gen_pt", "Generated p_{T}-spectra Protons", kTH1F, {axisPtpid}); + histos.add("QAChecks/h1d_ka_gen_pt", "Generated p_{T}-spectra Kaons", kTH1F, {axisPtpid}); + + histos.add("Analysis/h3d_gen_lstar_PM", "Generated #Lambda(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); + histos.add("Analysis/h3d_gen_lstar_MP", "Generated #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); + histos.add("Analysis/h3d_rec_lstar_PM", "Reconstructed #Lambda(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); + histos.add("Analysis/h3d_rec_lstar_MP", "Reconstructed #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {axisInvM, axisPt, axisCent}); + histos.add("Analysis/h3d_reso_lstar_PM", "Resolution #Lambda(1520) p_{T}", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCent}); + histos.add("Analysis/h3d_reso_lstar_MP", "Resolution #bar{#Lambda}(1520) p_{T}", kTHnSparseF, {{200, -0.05, 0.05}, axisPt, axisCent}); + + if (polActive) { + const AxisSpec axisMassRes{200, -0.05, 0.05, "M_{rec} - M_{gen} (GeV/#it{c}^{2})"}; + + histos.add("Analysis/h4d_pol_rec_PM", "cos#theta* Reconstructed (truth-matched) #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_rec_MP", "cos#theta* Reconstructed (truth-matched) #bar{#Lambda}(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_reso_PM", "cos#theta* vs mass resolution #Lambda(1520)", kTHnSparseF, {axisMassRes, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_reso_MP", "cos#theta* vs mass resolution #bar{#Lambda}(1520)", kTHnSparseF, {axisMassRes, axisPt, axisCent, axisCosTheta}); + } + } + + if (doprocessMCGenPol && polActive) { + histos.add("Analysis/h4d_pol_gen_PM", "cos#theta* Generated (truth) #Lambda(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + histos.add("Analysis/h4d_pol_gen_MP", "cos#theta* Generated (truth) #bar{#Lambda}(1520)", kTHnSparseF, {axisInvM, axisPt, axisCent, axisCosTheta}); + } + + if (doprocessMCGen) { + histos.add("SignalLoss/hMCEventCutflow", "MC Event Cutflow", kTH1F, {{7, 0, 7}}); + histos.add("SignalLoss/hGen_mT_scaled_Proton", "mT Scaled #Lambda(1520) from Proton", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_AntiProton", "mT Scaled #bar{#Lambda}(1520) from AntiProton", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_Lambda0", "mT Scaled #Lambda(1520) from Lambda0", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_AntiLambda0", "mT Scaled #bar{#Lambda}(1520) from AntiLambda0", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_XiMinus", "mT Scaled #Lambda(1520) from Xi-", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_XiPlus", "mT Scaled #bar{#Lambda}(1520) from Xi+", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_Xi0", "mT Scaled #Lambda(1520) from Xi0", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_AntiXi0", "mT Scaled #bar{#Lambda}(1520) from AntiXi0", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_OmegaMinus", "mT Scaled #Lambda(1520) from Omega-", kTHnSparseF, {axisPt, axisCent}); + histos.add("SignalLoss/hGen_mT_scaled_OmegaPlus", "mT Scaled #bar{#Lambda}(1520) from Omega+", kTHnSparseF, {axisPt, axisCent}); + } + } + + template + bool selTracks(T const& track) + { + if (track.pt() < cPtMin) + return false; + if (std::abs(track.eta()) > cEtaCut) + return false; + if (cPrimaryTrack && !track.isPrimaryTrack()) + return false; + if (cGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) + return false; + if (cPVContributor && !track.isPVContributor()) + return false; + if (cfgUseCrossedRows && track.tpcNClsCrossedRows() < cfgMinCrossedRows) + return false; + if (cfgUseTPCcls && track.tpcNClsFound() < cfgMinTPCcls) + return false; + return true; + } + + template + bool dcaSelectionProton(T const& track, float p) + { + auto ptBinsPr = static_cast>(cDcaPtBinsPr); + auto dcaXYPr = static_cast>(cDcaXYBinsPr); + int nBinsPr = static_cast(ptBinsPr.size()) - 1; + + bool dcaXYPassed = false; + for (int i = 0; i < nBinsPr; i++) { + if (p >= ptBinsPr[i] && p < ptBinsPr[i + 1] && + std::abs(track.dcaXY()) < dcaXYPr[i]) + dcaXYPassed = true; + } + if (!dcaXYPassed) + return false; + if (std::abs(track.dcaZ()) > cDcazMin) + return false; + return true; + } + + template + bool dcaSelectionKaon(T const& track, float p) + { + auto ptBinsKa = static_cast>(cDcaPtBinsKa); + auto dcaXYKa = static_cast>(cDcaXYBinsKa); + int nBinsKa = static_cast(ptBinsKa.size()) - 1; + + bool dcaXYPassed = false; + for (int i = 0; i < nBinsKa; i++) { + if (p >= ptBinsKa[i] && p < ptBinsKa[i + 1] && + std::abs(track.dcaXY()) < dcaXYKa[i]) + dcaXYPassed = true; + } + if (!dcaXYPassed) + return false; + if (std::abs(track.dcaZ()) > cDcazMin) + return false; + return true; + } + + template + bool selectionPIDProton(const T& candidate, float p) + { + bool tpcPIDPassed{false}, tofPIDPassed{false}; + auto tpcPIDp = static_cast>(protonTPCPIDp); + auto tpcPIDcut = static_cast>(protonTPCPIDcut); + auto tofPIDp = static_cast>(protonTOFPIDp); + auto tofPIDcut = static_cast>(protonTOFPIDcut); + int nitr = static_cast(tpcPIDp.size()); + int nitrtof = static_cast(tofPIDp.size()); + + float tpcNsigmaPi = std::abs(candidate.tpcNSigmaPi()); + float tpcNsigmaKa = std::abs(candidate.tpcNSigmaKa()); + float tpcNsigmaPr = std::abs(candidate.tpcNSigmaPr()); + float tofNsigmaPi = std::abs(candidate.tofNSigmaPi()); + float tofNsigmaKa = std::abs(candidate.tofNSigmaKa()); + float tofNsigmaPr = std::abs(candidate.tofNSigmaPr()); + + float tpcTofNsigmaPi = tpcNsigmaPi * tpcNsigmaPi + tofNsigmaPi * tofNsigmaPi; + float tpcTofNsigmaKa = tpcNsigmaKa * tpcNsigmaKa + tofNsigmaKa * tofNsigmaKa; + float tpcTofNsigmaPr = tpcNsigmaPr * tpcNsigmaPr + tofNsigmaPr * tofNsigmaPr; + float combinedCut = nsigmaCutCombinedProton * nsigmaCutCombinedProton; + float combinedRejCut = cRejNsigmaTof * cRejNsigmaTpc; + + if (!cUseTpcOnly && candidate.hasTOF()) { + if (candidate.tofNSigmaPr() < minnsigmatofPr) + return false; + if (nsigmaCutCombinedProton < 0 && p >= cPMin) { + for (int i = 0; i < nitrtof - 1; ++i) { + if (p >= tofPIDp[i] && p < tofPIDp[i + 1] && (tofNsigmaPr < tofPIDcut[i] && tofNsigmaPi > cRejNsigmaTof && tofNsigmaKa > cRejNsigmaTof)) + tofPIDPassed = true; + } + if (candidate.tpcNSigmaPr() < minnsigmatpctofPr) + return false; + if (tpcNsigmaPr < cMaxTPCnSigmaProton && tpcNsigmaPi > cRejNsigmaTpcVeto && tpcNsigmaKa > cRejNsigmaTpcVeto) + tpcPIDPassed = true; + } + + if ((nsigmaCutCombinedProton > 0) && p >= cPMin && (tpcTofNsigmaPr < combinedCut && tpcTofNsigmaPi > combinedRejCut && tpcTofNsigmaKa > combinedRejCut)) { + tofPIDPassed = true; + tpcPIDPassed = true; + } + + if (p < cPMin && tpcNsigmaPr < cMaxTPCnSigmaProton) { + tofPIDPassed = true; + tpcPIDPassed = true; + } + } else { + tofPIDPassed = true; + if (candidate.tpcNSigmaPr() < minnsigmatpcPr) + return false; + for (int i = 0; i < nitr - 1; ++i) { + if (p >= tpcPIDp[i] && p < tpcPIDp[i + 1] && (tpcNsigmaPr < tpcPIDcut[i] && tpcNsigmaPi > cRejNsigmaTpcPi && tpcNsigmaKa > cRejNsigmaTpcKa)) { + tpcPIDPassed = true; + } + } + } + if (tpcPIDPassed && tofPIDPassed) + return true; + return false; + } + + template + bool selectionPIDKaon(const T& candidate, float p) + { + bool tpcPIDPassed{false}, tofPIDPassed{false}; + auto tpcPIDp = static_cast>(kaonTPCPIDp); + auto tpcPIDcut = static_cast>(kaonTPCPIDcut); + auto tofPIDp = static_cast>(kaonTOFPIDp); + auto tofPIDcut = static_cast>(kaonTOFPIDcut); + int nitr = static_cast(tpcPIDp.size()); + int nitrtof = static_cast(tofPIDp.size()); + + float tpcNsigmaPi = std::abs(candidate.tpcNSigmaPi()); + float tpcNsigmaKa = std::abs(candidate.tpcNSigmaKa()); + float tpcNsigmaPr = std::abs(candidate.tpcNSigmaPr()); + float tofNsigmaPi = std::abs(candidate.tofNSigmaPi()); + float tofNsigmaKa = std::abs(candidate.tofNSigmaKa()); + float tofNsigmaPr = std::abs(candidate.tofNSigmaPr()); + + float tpcTofNsigmaPi = tpcNsigmaPi * tpcNsigmaPi + tofNsigmaPi * tofNsigmaPi; + float tpcTofNsigmaKa = tpcNsigmaKa * tpcNsigmaKa + tofNsigmaKa * tofNsigmaKa; + float tpcTofNsigmaPr = tpcNsigmaPr * tpcNsigmaPr + tofNsigmaPr * tofNsigmaPr; + float combinedCut = nsigmaCutCombinedKaon * nsigmaCutCombinedKaon; + float combinedRejCut = cRejNsigmaTpc * cRejNsigmaTof; + + if (!cUseTpcOnly && candidate.hasTOF()) { + if (candidate.tofNSigmaKa() < minnsigmatofKa) + return false; + if (nsigmaCutCombinedKaon < 0 && p >= cPMin) { + for (int i = 0; i < nitrtof - 1; ++i) { + if (p >= tofPIDp[i] && p < tofPIDp[i + 1] && (tofNsigmaKa < tofPIDcut[i] && tofNsigmaPi > cRejNsigmaTof && tofNsigmaPr > cRejNsigmaTof)) + tofPIDPassed = true; + } + if (candidate.tpcNSigmaKa() < minnsigmatpctofKa) + return false; + if (tpcNsigmaKa < cMaxTPCnSigmaKaon && tpcNsigmaPi > cRejNsigmaTpcVeto && tpcNsigmaPr > cRejNsigmaTpcVeto) + tpcPIDPassed = true; + } + + if ((nsigmaCutCombinedKaon > 0) && p >= cPMin && (tpcTofNsigmaKa < combinedCut && tpcTofNsigmaPi > combinedRejCut && tpcTofNsigmaPr > combinedRejCut)) { + tofPIDPassed = true; + tpcPIDPassed = true; + } + + if (p < cPMin && tpcNsigmaKa < cMaxTPCnSigmaKaon) { + tofPIDPassed = true; + tpcPIDPassed = true; + } + } else { + tofPIDPassed = true; + if (candidate.tpcNSigmaKa() < minnsigmatpcKa) + return false; + for (int i = 0; i < nitr - 1; ++i) { + if (p >= tpcPIDp[i] && p < tpcPIDp[i + 1] && (tpcNsigmaKa < tpcPIDcut[i] && tpcNsigmaPi > cRejNsigmakTpcPi && tpcNsigmaPr > cRejNsigmakTpcPr)) { + tpcPIDPassed = true; + } + } + } + if (tpcPIDPassed && tofPIDPassed) + return true; + return false; + } + + static constexpr float VecUnitEpsilon = 1e-9f; + static constexpr float MomentumEpsilon = 1e-6f; + + static float vecDot(const std::array& a, const std::array& b) + { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + + static std::array vecSub(const std::array& a, const std::array& b) + { + return {a[0] - b[0], a[1] - b[1], a[2] - b[2]}; + } + + static std::array vecCross(const std::array& a, const std::array& b) + { + return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]}; + } + + static std::array vecUnit(const std::array& a) + { + float mag = std::sqrt(vecDot(a, a)); + if (mag < VecUnitEpsilon) + return {0.f, 0.f, 0.f}; + return {a[0] / mag, a[1] / mag, a[2] / mag}; + } + + static std::array boostToRestFrame(float motherE, const std::array& motherP, + float dE, const std::array& dP) + { + float pMag2 = vecDot(motherP, motherP); + float pMag = std::sqrt(pMag2); + if (pMag < MomentumEpsilon) + return dP; + + std::array nHat = {motherP[0] / pMag, motherP[1] / pMag, motherP[2] / pMag}; + float beta = pMag / motherE; + float motherMass2 = motherE * motherE - pMag2; + float gamma = motherMass2 > 0.f ? motherE / std::sqrt(motherMass2) : 1.f; + + float pParallel = vecDot(dP, nHat); + std::array pPerp = {dP[0] - pParallel * nHat[0], dP[1] - pParallel * nHat[1], dP[2] - pParallel * nHat[2]}; + float pParallelPrime = gamma * (pParallel - beta * dE); + + return {pPerp[0] + pParallelPrime * nHat[0], pPerp[1] + pParallelPrime * nHat[1], pPerp[2] + pParallelPrime * nHat[2]}; + } + + enum class PolBkgMode { Signal, + Rotated, + Mixed, + LikeSign, + LikeSignMixed, + McTrue, + McTrueRes, + GenTrue }; + + template + void fillPolarization(float candMass, float candPt, + const std::array& motherP, float motherE, + const std::array& protonP, float protonE, + const std::array& kaonP, float kaonE, + float mult, bool protonIsPositive) + { + if (!cActivateHelicityFrame && !cActivateCollinsSoperFrame && !cActivateProductionFrame && + !cActivateBeamAxisFrame && !cActivateRandomFrame) + return; + + std::array protonCM = boostToRestFrame(motherE, motherP, protonE, protonP); + std::array kaonCM = boostToRestFrame(motherE, motherP, kaonE, kaonP); + std::array refCM = vecUnit(cPolarizationRefDaughter == 0 ? protonCM : kaonCM); + + auto fillFrame = [&](float cosTheta) { + if constexpr (Mode == PolBkgMode::Rotated) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_rot_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_rot_MP"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::Mixed) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_mix_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_mix_MP"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::LikeSign) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_LS_PP"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_LS_MM"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::LikeSignMixed) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_LS_PP_mix"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_LS_MM_mix"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::McTrue) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_rec_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_rec_MP"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::McTrueRes) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_reso_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_reso_MP"), candMass, candPt, mult, cosTheta); + } else if constexpr (Mode == PolBkgMode::GenTrue) { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_gen_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_gen_MP"), candMass, candPt, mult, cosTheta); + } else { + if (protonIsPositive) + histos.fill(HIST("Analysis/h4d_pol_US_PM"), candMass, candPt, mult, cosTheta); + else + histos.fill(HIST("Analysis/h4d_pol_US_MP"), candMass, candPt, mult, cosTheta); + } + }; + + if (cActivateHelicityFrame) { + std::array zAxisHE = vecUnit(motherP); + fillFrame(vecDot(refCM, zAxisHE)); + } + + if (cActivateCollinsSoperFrame || cActivateProductionFrame || cActivateBeamAxisFrame) { + std::array beam1CM = vecUnit(boostToRestFrame(motherE, motherP, beam1E, beam1P)); + std::array beam2CM = vecUnit(boostToRestFrame(motherE, motherP, beam2E, beam2P)); + + if (cActivateCollinsSoperFrame) { + std::array zAxisCS = vecUnit(vecSub(beam1CM, beam2CM)); + fillFrame(vecDot(refCM, zAxisCS)); + } + if (cActivateProductionFrame) { + std::array motherDir = vecUnit(motherP); + std::array yAxisPR = vecUnit(vecCross(beam1CM, motherDir)); + fillFrame(vecDot(refCM, yAxisPR)); + } + if (cActivateBeamAxisFrame) { + fillFrame(vecDot(refCM, beam1CM)); + } + } + + if (cActivateRandomFrame) { + double randCos = randUniform01(randGen) * 2. - 1.; + double randPhi = randUniform01(randGen) * o2::constants::math::TwoPI; + double randSin = std::sqrt(1. - randCos * randCos); + std::array zAxisRD = {static_cast(randSin * std::cos(randPhi)), + static_cast(randSin * std::sin(randPhi)), + static_cast(randCos)}; + fillFrame(vecDot(refCM, zAxisRD)); + } + } + + template + void fillDataHistos(trackType const& trk1, trackType const& trk2, float mult, int occup = 100) + { + float prptot = 0., kaptot = 0.; + bool polActive = static_cast(cActivateHelicityFrame) || + static_cast(cActivateCollinsSoperFrame) || + static_cast(cActivateProductionFrame) || + static_cast(cActivateBeamAxisFrame) || + static_cast(cActivateRandomFrame); + + for (auto const& [trkPr, trkKa] : soa::combinations(soa::CombinationsFullIndexPolicy(trk1, trk2))) { + if (trkPr.index() == trkKa.index()) + continue; + if (!selTracks(trkPr) || !selTracks(trkKa)) + continue; + + auto pxPr = trkPr.px(); + auto pyPr = trkPr.py(); + auto pzPr = trkPr.pz(); + auto pxKa = trkKa.px(); + auto pyKa = trkKa.py(); + auto pzKa = trkKa.pz(); + + prptot = std::sqrt(pxPr * pxPr + pyPr * pyPr + pzPr * pzPr); + kaptot = std::sqrt(pxKa * pxKa + pyKa * pyKa + pzKa * pzKa); + + if (!mix) { + auto tpcNsigmaPr = trkPr.tpcNSigmaPr(); + histos.fill(HIST("QAbefore/Proton/h2d_pr_nsigma_tpc_p"), prptot, tpcNsigmaPr); + if (trkPr.hasTOF()) { + auto tofNsigmaPr = trkPr.tofNSigmaPr(); + histos.fill(HIST("QAbefore/Proton/h2d_pr_nsigma_tof_p"), prptot, tofNsigmaPr); + histos.fill(HIST("QAbefore/Proton/h2d_pr_nsigma_tof_vs_tpc"), tpcNsigmaPr, tofNsigmaPr); + } + auto tpcNsigmaKa = trkKa.tpcNSigmaKa(); + histos.fill(HIST("QAbefore/Kaon/h2d_ka_nsigma_tpc_p"), kaptot, tpcNsigmaKa); + if (trkKa.hasTOF()) { + auto tofNsigmaKa = trkKa.tofNSigmaKa(); + histos.fill(HIST("QAbefore/Kaon/h2d_ka_nsigma_tof_p"), kaptot, tofNsigmaKa); + histos.fill(HIST("QAbefore/Kaon/h2d_ka_nsigma_tof_vs_tpc"), tpcNsigmaKa, tofNsigmaKa); + } + } + + if (cUseOnlyTOFTrackPr && !trkPr.hasTOF()) + continue; + if (cUseOnlyTOFTrackKa && !trkKa.hasTOF()) + continue; + if (!selectionPIDProton(trkPr, prptot) || !selectionPIDKaon(trkKa, kaptot)) + continue; + if (!dcaSelectionProton(trkPr, prptot) || !dcaSelectionKaon(trkKa, kaptot)) + continue; + + if (isDeepAngle && std::acos((trkPr.pt() * trkKa.pt() + pzPr * pzKa) / (prptot * kaptot)) < cfgDeepAngle) + continue; + + if constexpr (!mix) { + // ==================================================================== + // Proton QA Fills + // ==================================================================== + auto ptPr = trkPr.pt(); + auto tpcNsigmaPr = trkPr.tpcNSigmaPr(); + + // 1. Base QA (Filled for all) + histos.fill(HIST("QAafter/Proton/h2d_pr_dca_z"), ptPr, trkPr.dcaZ()); + histos.fill(HIST("QAafter/Proton/h2d_pr_dca_xy"), ptPr, trkPr.dcaXY()); + histos.fill(HIST("QAafter/Proton/h2d_pr_dEdx_p"), prptot, trkPr.tpcSignal()); + histos.fill(HIST("QAafter/Proton/hTPCNClsCrossedRowsVsPt"), ptPr, trkPr.tpcNClsCrossedRows()); + histos.fill(HIST("QAafter/Proton/hTPCNClsFoundVsPt"), ptPr, trkPr.tpcNClsFound()); + + bool prIsTpcOnly = cUseTpcOnly || !trkPr.hasTOF(); + histos.fill(HIST("QAafter/Proton/hFracTPConly_Proton"), prptot, prIsTpcOnly ? 1 : 0); + + // 2 & 3. Mutually Exclusive TPC vs hasTOF Split + if (prIsTpcOnly) { + histos.fill(HIST("QAafter/Proton/hd_pr_pt_TPConly"), ptPr, mult); + histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tpc_pt_TPConly"), ptPr, tpcNsigmaPr); + histos.fill(HIST("QAafter/Proton/h2d_Prpi_nsigma_tpc_p_TPConly"), ptPr, trkPr.tpcNSigmaPi()); + histos.fill(HIST("QAafter/Proton/h2d_Prka_nsigma_tpc_p_TPConly"), ptPr, trkPr.tpcNSigmaKa()); + histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tpc_p_TPConly"), prptot, tpcNsigmaPr); + } else { + histos.fill(HIST("QAafter/Proton/hd_pr_pt_hasTOF"), ptPr, mult); + histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tpc_pt_hasTOF"), ptPr, tpcNsigmaPr); + histos.fill(HIST("QAafter/Proton/h2d_Prpi_nsigma_tpc_p_hasTOF"), ptPr, trkPr.tpcNSigmaPi()); + histos.fill(HIST("QAafter/Proton/h2d_Prka_nsigma_tpc_p_hasTOF"), ptPr, trkPr.tpcNSigmaKa()); + histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tpc_p_hasTOF"), prptot, tpcNsigmaPr); + + // 4. TOF-Specific (Only possible if hasTOF) + auto tofNsigmaPr = trkPr.tofNSigmaPr(); + histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tof_pt"), ptPr, tofNsigmaPr); + histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tof_p"), prptot, tofNsigmaPr); + histos.fill(HIST("QAafter/Proton/h2d_Prpi_nsigma_tof_p"), prptot, trkPr.tofNSigmaPi()); + histos.fill(HIST("QAafter/Proton/h2d_Prka_nsigma_tof_p"), prptot, trkPr.tofNSigmaKa()); + histos.fill(HIST("QAafter/Proton/h2d_pr_nsigma_tof_vs_tpc"), tpcNsigmaPr, tofNsigmaPr); + } + + // ==================================================================== + // Kaon QA Fills + // ==================================================================== + auto ptKa = trkKa.pt(); + auto tpcNsigmaKa = trkKa.tpcNSigmaKa(); + + // 1. Base QA (Filled for all) + histos.fill(HIST("QAafter/Kaon/h2d_ka_dca_z"), ptKa, trkKa.dcaZ()); + histos.fill(HIST("QAafter/Kaon/h2d_ka_dca_xy"), ptKa, trkKa.dcaXY()); + histos.fill(HIST("QAafter/Kaon/h2d_ka_dEdx_p"), kaptot, trkKa.tpcSignal()); + histos.fill(HIST("QAafter/Kaon/hTPCNClsCrossedRowsVsPt"), ptKa, trkKa.tpcNClsCrossedRows()); + histos.fill(HIST("QAafter/Kaon/hTPCNClsFoundVsPt"), ptKa, trkKa.tpcNClsFound()); + + bool kaIsTpcOnly = cUseTpcOnly || !trkKa.hasTOF(); + histos.fill(HIST("QAafter/Kaon/hFracTPConly_Kaon"), kaptot, kaIsTpcOnly ? 1 : 0); + + // 2 & 3. Mutually Exclusive TPC vs hasTOF Split + if (kaIsTpcOnly) { + histos.fill(HIST("QAafter/Kaon/hd_ka_pt_TPConly"), ptKa, mult); + histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tpc_pt_TPConly"), ptKa, tpcNsigmaKa); + histos.fill(HIST("QAafter/Kaon/h2d_Kapi_nsigma_tpc_p_TPConly"), ptKa, trkKa.tpcNSigmaPi()); + histos.fill(HIST("QAafter/Kaon/h2d_Kapr_nsigma_tpc_p_TPConly"), kaptot, trkKa.tpcNSigmaPr()); + histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tpc_p_TPConly"), kaptot, tpcNsigmaKa); + } else { + histos.fill(HIST("QAafter/Kaon/hd_ka_pt_hasTOF"), ptKa, mult); + histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tpc_pt_hasTOF"), ptKa, tpcNsigmaKa); + histos.fill(HIST("QAafter/Kaon/h2d_Kapi_nsigma_tpc_p_hasTOF"), ptKa, trkKa.tpcNSigmaPi()); + histos.fill(HIST("QAafter/Kaon/h2d_Kapr_nsigma_tpc_p_hasTOF"), kaptot, trkKa.tpcNSigmaPr()); + histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tpc_p_hasTOF"), kaptot, tpcNsigmaKa); + + // 4. TOF-Specific (Only possible if hasTOF) + auto tofNsigmaKa = trkKa.tofNSigmaKa(); + histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tof_pt"), ptKa, tofNsigmaKa); + histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tof_p"), kaptot, tofNsigmaKa); + histos.fill(HIST("QAafter/Kaon/h2d_Kapi_nsigma_tof_p"), kaptot, trkKa.tofNSigmaPi()); + histos.fill(HIST("QAafter/Kaon/h2d_Kapr_nsigma_tof_p"), kaptot, trkKa.tofNSigmaPr()); + histos.fill(HIST("QAafter/Kaon/h2d_ka_nsigma_tof_vs_tpc"), tpcNsigmaKa, tofNsigmaKa); + } + } + + if (isonlyQC) + continue; + + std::array pvec0 = {pxPr, pyPr, pzPr}; + std::array pvec1 = {pxKa, pyKa, pzKa}; + std::array, 2> arrMomrec = {pvec0, pvec1}; + float candMass = RecoDecay::m(arrMomrec, std::array{MassProton, MassKaonCharged}); + float candPt = RecoDecay::pt(std::array{pxPr + pxKa, pyPr + pyKa}); + float candY = std::abs(RecoDecay::y(std::array{pxPr + pxKa, pyPr + pyKa, pzPr + pzKa}, candMass)); + float candYShift = candY - cfgRapidityShift; + + if (std::abs(candYShift) > cfgRapidityCut) + continue; + + if constexpr (!mix && !mc) { + if (trkPr.sign() * trkKa.sign() < 0) { + if (!polActive) { + if (trkPr.sign() > 0) + histos.fill(HIST("Analysis/h4d_lstar_invm_US_PM"), candMass, candPt, mult, occup); + else + histos.fill(HIST("Analysis/h4d_lstar_invm_US_MP"), candMass, candPt, mult, occup); + } + + if (polActive) { + float eProton = std::sqrt(pxPr * pxPr + pyPr * pyPr + pzPr * pzPr + MassProton * MassProton); + float eKaon = std::sqrt(pxKa * pxKa + pyKa * pyKa + pzKa * pzKa + MassKaonCharged * MassKaonCharged); + std::array protonP = {pxPr, pyPr, pzPr}; + std::array kaonP = {pxKa, pyKa, pzKa}; + std::array motherP = {pxPr + pxKa, pyPr + pyKa, pzPr + pzKa}; + float motherE = eProton + eKaon; + fillPolarization(candMass, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.sign() > 0); + + if (doRotate) { + for (int i = 0; i < cNofRotations; i++) { + float delta = o2::constants::math::PI / rotationalcut; + float theta2 = (cNofRotations == 1) ? o2::constants::math::PI : (o2::constants::math::PI - delta) + i * (2.f * delta / (cNofRotations - 1)); + float phiRot = RecoDecay::constrainAngle(trkKa.phi() + theta2, 0.f); + float pxKaRot = trkKa.pt() * std::cos(phiRot); + float pyKaRot = trkKa.pt() * std::sin(phiRot); + + std::array pvec0rot = {pxPr, pyPr, pzPr}; + std::array pvec1rot = {pxKaRot, pyKaRot, pzKa}; + std::array, 2> arrMomRot = {pvec0rot, pvec1rot}; + + float candMassRot = RecoDecay::m(arrMomRot, std::array{MassProton, MassKaonCharged}); + float candPtRot = RecoDecay::pt(std::array{pxPr + pxKaRot, pyPr + pyKaRot}); + float candYRot = std::abs(RecoDecay::y(std::array{pxPr + pxKaRot, pyPr + pyKaRot, pzPr + pzKa}, MassLambda1520)); + float candYShiftRot = candYRot - cfgRapidityShift; + + if (std::abs(candYShiftRot) > cfgRapidityCut) + continue; + + if (doPolarizationRot) { + float eKaonRot = std::sqrt(pxKaRot * pxKaRot + pyKaRot * pyKaRot + pzKa * pzKa + MassKaonCharged * MassKaonCharged); + std::array kaonRotP = {pxKaRot, pyKaRot, pzKa}; + std::array motherRotP = {pxPr + pxKaRot, pyPr + pyKaRot, pzPr + pzKa}; + float motherRotE = eProton + eKaonRot; + fillPolarization(candMassRot, candPtRot, motherRotP, motherRotE, protonP, eProton, kaonRotP, eKaonRot, mult, trkPr.sign() > 0); + } + } + } + } else if (doRotate) { + for (int i = 0; i < cNofRotations; i++) { + float delta = o2::constants::math::PI / rotationalcut; + float theta2 = (cNofRotations == 1) ? o2::constants::math::PI : (o2::constants::math::PI - delta) + i * (2.f * delta / (cNofRotations - 1)); + float phiRot = RecoDecay::constrainAngle(trkKa.phi() + theta2, 0.f); + float pxKaRot = trkKa.pt() * std::cos(phiRot); + float pyKaRot = trkKa.pt() * std::sin(phiRot); + std::array pvec0rot = {pxPr, pyPr, pzPr}; + std::array pvec1rot = {pxKaRot, pyKaRot, pzKa}; + std::array, 2> arrMomRot = {pvec0rot, pvec1rot}; + float candMassRot = RecoDecay::m(arrMomRot, std::array{MassProton, MassKaonCharged}); + float candPtRot = RecoDecay::pt(std::array{pxPr + pxKaRot, pyPr + pyKaRot}); + float candYRot = std::abs(RecoDecay::y(std::array{pxPr + pxKaRot, pyPr + pyKaRot, pzPr + pzKa}, MassLambda1520)); + + if (std::abs(candYRot - cfgRapidityShift) > cfgRapidityCut) + continue; + + if (trkPr.sign() > 0) + histos.fill(HIST("Analysis/h4d_lstar_invm_rot_PM"), candMassRot, candPtRot, mult, occup); + else + histos.fill(HIST("Analysis/h4d_lstar_invm_rot_MP"), candMassRot, candPtRot, mult, occup); + } + } + } else { + if (!polActive) { + if (trkPr.sign() > 0) + histos.fill(HIST("Analysis/h4d_lstar_invm_PP"), candMass, candPt, mult, occup); + else + histos.fill(HIST("Analysis/h4d_lstar_invm_MM"), candMass, candPt, mult, occup); + } else { + float eProton = std::sqrt(pxPr * pxPr + pyPr * pyPr + pzPr * pzPr + MassProton * MassProton); + float eKaon = std::sqrt(pxKa * pxKa + pyKa * pyKa + pzKa * pzKa + MassKaonCharged * MassKaonCharged); + std::array protonP = {pxPr, pyPr, pzPr}; + std::array kaonP = {pxKa, pyKa, pzKa}; + std::array motherP = {pxPr + pxKa, pyPr + pyKa, pzPr + pzKa}; + float motherE = eProton + eKaon; + fillPolarization(candMass, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.sign() > 0); + } + } + } + + if constexpr (mix) { + if (trkPr.sign() * trkKa.sign() < 0) { + if (!polActive) { + if (trkPr.sign() > 0) + histos.fill(HIST("Analysis/h4d_lstar_invm_US_PM_mix"), candMass, candPt, mult, occup); + else + histos.fill(HIST("Analysis/h4d_lstar_invm_US_MP_mix"), candMass, candPt, mult, occup); + } + if (doPolarizationMix && polActive) { + float eProton = std::sqrt(pxPr * pxPr + pyPr * pyPr + pzPr * pzPr + MassProton * MassProton); + float eKaon = std::sqrt(pxKa * pxKa + pyKa * pyKa + pzKa * pzKa + MassKaonCharged * MassKaonCharged); + std::array protonP = {pxPr, pyPr, pzPr}; + std::array kaonP = {pxKa, pyKa, pzKa}; + std::array motherP = {pxPr + pxKa, pyPr + pyKa, pzPr + pzKa}; + float motherE = eProton + eKaon; + fillPolarization(candMass, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.sign() > 0); + } + } else { + if (!polActive) { + if (trkPr.sign() > 0) + histos.fill(HIST("Analysis/h4d_lstar_invm_LS_PP_mix"), candMass, candPt, mult, occup); + else + histos.fill(HIST("Analysis/h4d_lstar_invm_LS_MM_mix"), candMass, candPt, mult, occup); + } + if (doPolarizationMix && polActive) { + float eProton = std::sqrt(pxPr * pxPr + pyPr * pyPr + pzPr * pzPr + MassProton * MassProton); + float eKaon = std::sqrt(pxKa * pxKa + pyKa * pyKa + pzKa * pzKa + MassKaonCharged * MassKaonCharged); + std::array protonP = {pxPr, pyPr, pzPr}; + std::array kaonP = {pxKa, pyKa, pzKa}; + std::array motherP = {pxPr + pxKa, pyPr + pyKa, pzPr + pzKa}; + float motherE = eProton + eKaon; + fillPolarization(candMass, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.sign() > 0); + } + } + } + if constexpr (mc) { + if (trkPr.sign() * trkKa.sign() < 0) { + if (std::abs(trkPr.pdgCode()) != kProton || std::abs(trkKa.pdgCode()) != kKPlus) + continue; + if (trkPr.motherId() != trkKa.motherId()) + continue; + if (trkPr.motherPDG() != trkKa.motherPDG()) + continue; + if (trkPr.pdgCode() == 0 || trkKa.pdgCode() == 0) + continue; + if (trkPr.motherPDG() == -1 || trkKa.motherPDG() == -1) + continue; + if (std::abs(trkPr.motherPDG()) != lambda1520id) + continue; + + float massParent = 0.; + for (auto const& resoParent : *mResoParents) { + if (resoParent.mcParticleId() == trkPr.motherId()) { + std::array pvecParent = {resoParent.px(), resoParent.py(), resoParent.pz()}; + massParent = RecoDecay::m(pvecParent, resoParent.e()); + break; + } + } + + float candMassRes = candMass - massParent; + if (trkPr.motherPDG() > 0) { + histos.fill(HIST("Analysis/h3d_rec_lstar_PM"), candMass, candPt, mult); + histos.fill(HIST("Analysis/h3d_reso_lstar_PM"), candMassRes, candPt, mult); + } else { + histos.fill(HIST("Analysis/h3d_rec_lstar_MP"), candMass, candPt, mult); + histos.fill(HIST("Analysis/h3d_reso_lstar_MP"), candMassRes, candPt, mult); + } + + if (polActive) { + float eProton = std::sqrt(pxPr * pxPr + pyPr * pyPr + pzPr * pzPr + MassProton * MassProton); + float eKaon = std::sqrt(pxKa * pxKa + pyKa * pyKa + pzKa * pzKa + MassKaonCharged * MassKaonCharged); + std::array protonP = {pxPr, pyPr, pzPr}; + std::array kaonP = {pxKa, pyKa, pzKa}; + std::array motherP = {pxPr + pxKa, pyPr + pyKa, pzPr + pzKa}; + float motherE = eProton + eKaon; + fillPolarization(candMass, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.motherPDG() > 0); + fillPolarization(candMassRes, candPt, motherP, motherE, protonP, eProton, kaonP, eKaon, mult, trkPr.motherPDG() > 0); + } + } + } + } + } + + using ResoCols = soa::Join; + using ResoMCCols = soa::Join; + using ResoTracks = aod::ResoTracks; + + void processData(ResoCols::iterator const& collision, ResoTracks const& tracks) + { + if (cEvtMCRecINELgt0 && !collision.isRecINELgt0()) + return; + histos.fill(HIST("Event/h1d_ft0_mult_percentile"), collision.cent(), 100); + histos.fill(HIST("Event/h_ft0_vz"), collision.posZ()); + fillDataHistos(tracks, tracks, collision.cent()); + } + + PROCESS_SWITCH(Lambda1520pbpb, processData, "Process for Same Event Data", true); + + void processMC(ResoMCCols::iterator const& collision, soa::Join const& tracks, aod::ResoMCParents const& resoParents) + { + histos.fill(HIST("Event/hMCEventCutflow"), 0); + if (cEvtMCTriggerTVX && !collision.isTriggerTVX()) + return; + histos.fill(HIST("Event/hMCEventCutflow"), 1); + if (cEvtMCVtxIn10 && !collision.isVtxIn10()) + return; + histos.fill(HIST("Event/hMCEventCutflow"), 2); + if (cEvtMCINELgt0 && !collision.isINELgt0()) + return; + histos.fill(HIST("Event/hMCEventCutflow"), 3); + if (cEvtMCSel8 && !collision.isInSel8()) + return; + histos.fill(HIST("Event/hMCEventCutflow"), 4); + if (cEvtMCRecINELgt0 && !collision.isRecINELgt0()) + return; + histos.fill(HIST("Event/hMCEventCutflow"), 5); + if (cEvtMCAfterAllCuts && !collision.isInAfterAllCuts()) + return; + histos.fill(HIST("Event/hMCEventCutflow"), 6); + + auto mult = collision.cent(); + histos.fill(HIST("Event/h1d_ft0_mult_percentile"), mult, 100); + histos.fill(HIST("Event/h_ft0_vz"), collision.posZ()); + mResoParents = &resoParents; + fillDataHistos(tracks, tracks, mult); + + for (auto const& track : tracks) { + histos.fill(HIST("QAbefore/hEta_rec"), track.eta()); + histos.fill(HIST("QAbefore/hPt_rec"), track.pt()); + histos.fill(HIST("QAbefore/hPhi_rec"), track.phi()); + histos.fill(HIST("QAbefore/hEtaPhi_rec"), track.eta(), track.phi()); + + if (std::abs(track.pdgCode()) == kKPlus) + histos.fill(HIST("QAChecks/h1d_ka_gen_pt"), track.pt()); + if (std::abs(track.pdgCode()) == kProton) + histos.fill(HIST("QAChecks/h1d_pr_gen_pt"), track.pt()); + + if (!selTracks(track)) + continue; + float p = std::sqrt(track.px() * track.px() + track.py() * track.py() + track.pz() * track.pz()); + + if (selectionPIDKaon(track, p)) { + if (std::abs(track.pdgCode()) == kKPlus) + histos.fill(HIST("QAChecks/h1d_ka_rec_pt"), track.pt()); + } + if (selectionPIDProton(track, p)) { + if (std::abs(track.pdgCode()) == kProton) + histos.fill(HIST("QAChecks/h1d_pr_rec_pt"), track.pt()); + } + } + + for (auto const& part : resoParents) { + if (std::abs(part.pdgCode()) != lambda1520id) + continue; + float yshift = std::abs(part.y()) - cfgRapidityShift; + if (std::abs(yshift) > cfgRapidityCut) + continue; + + bool pass1 = false, pass2 = false; + if (std::abs(part.daughterPDG1()) == kProton || std::abs(part.daughterPDG2()) == kProton) + pass1 = true; + if (std::abs(part.daughterPDG1()) == kKPlus || std::abs(part.daughterPDG2()) == kKPlus) + pass2 = true; + + if (!pass1 || !pass2) + continue; + std::array pvec = {part.px(), part.py(), part.pz()}; + float mass = RecoDecay::m(pvec, part.e()); + if (part.pdgCode() > 0) + histos.fill(HIST("Analysis/h3d_gen_lstar_PM"), mass, part.pt(), mult); + else + histos.fill(HIST("Analysis/h3d_gen_lstar_MP"), mass, part.pt(), mult); + } + } + PROCESS_SWITCH(Lambda1520pbpb, processMC, "Process Event for MC", false); + + void processMCGenPol(ResoMCCols::iterator const& collision, aod::ResoMCParents const& resoParents, aod::McParticles const& mcParticles) + { + auto mult = collision.cent(); + bool polActive = static_cast(cActivateHelicityFrame) || + static_cast(cActivateCollinsSoperFrame) || + static_cast(cActivateProductionFrame) || + static_cast(cActivateBeamAxisFrame) || + static_cast(cActivateRandomFrame); + if (!polActive) + return; + + for (auto const& part : resoParents) { + if (std::abs(part.pdgCode()) != lambda1520id) + continue; + float yshift = std::abs(part.y()) - cfgRapidityShift; + if (std::abs(yshift) > cfgRapidityCut) + continue; + + bool pass1 = std::abs(part.daughterPDG1()) == kProton || std::abs(part.daughterPDG2()) == kProton; + bool pass2 = std::abs(part.daughterPDG1()) == kKPlus || std::abs(part.daughterPDG2()) == kKPlus; + if (!pass1 || !pass2) + continue; + + std::array pvec = {part.px(), part.py(), part.pz()}; + float mass = RecoDecay::m(pvec, part.e()); + + auto motherMc = mcParticles.iteratorAt(part.mcParticleId()); + bool foundProton = false, foundKaon = false; + std::array protonP{}, kaonP{}; + float protonE = 0.f, kaonE = 0.f; + for (auto const& dau : motherMc.daughters_as()) { + if (!foundProton && std::abs(dau.pdgCode()) == kProton) { + protonP = {dau.px(), dau.py(), dau.pz()}; + protonE = dau.e(); + foundProton = true; + } else if (!foundKaon && std::abs(dau.pdgCode()) == kKPlus) { + kaonP = {dau.px(), dau.py(), dau.pz()}; + kaonE = dau.e(); + foundKaon = true; + } + } + if (!foundProton || !foundKaon) + continue; + + std::array motherP = {part.px(), part.py(), part.pz()}; + fillPolarization(mass, part.pt(), motherP, part.e(), protonP, protonE, kaonP, kaonE, mult, part.pdgCode() > 0); + } + } + PROCESS_SWITCH(Lambda1520pbpb, processMCGenPol, "Standalone generator-level polarization pass (run separately from processMC)", false); + + void processMCGen(ResoMCCols::iterator const& collision, aod::ResoMCParents const& resoParents) + { + float centrality = collision.cent(); + histos.fill(HIST("SignalLoss/hMCEventCutflow"), 0); + if (cEvtMCTriggerTVX && !collision.isTriggerTVX()) + return; + histos.fill(HIST("SignalLoss/hMCEventCutflow"), 1); + if (cEvtMCVtxIn10 && !collision.isVtxIn10()) + return; + histos.fill(HIST("SignalLoss/hMCEventCutflow"), 2); + if (cEvtMCINELgt0 && !collision.isINELgt0()) + return; + histos.fill(HIST("SignalLoss/hMCEventCutflow"), 3); + if (cEvtMCSel8 && !collision.isInSel8()) + return; + histos.fill(HIST("SignalLoss/hMCEventCutflow"), 4); + if (cEvtMCRecINELgt0 && !collision.isRecINELgt0()) + return; + histos.fill(HIST("SignalLoss/hMCEventCutflow"), 5); + if (cEvtMCAfterAllCuts && !collision.isInAfterAllCuts()) + return; + histos.fill(HIST("SignalLoss/hMCEventCutflow"), 6); + + for (auto const& part : resoParents) { + float yshift = std::abs(part.y()) - cfgRapidityShift; + if (std::abs(yshift) > cfgRapidityCut) + continue; + + int pdg = part.pdgCode(); + float ptRef = part.pt(); + double ptSq = -1.0; + + std::array pvec = {part.px(), part.py(), part.pz()}; + float mass = RecoDecay::m(pvec, part.e()); + + if (pdg == kProton) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_Proton"), std::sqrt(ptSq), centrality); + } else if (pdg == -kProton) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_AntiProton"), std::sqrt(ptSq), centrality); + } else if (pdg == kLambda0) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_Lambda0"), std::sqrt(ptSq), centrality); + } else if (pdg == -kLambda0) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_AntiLambda0"), std::sqrt(ptSq), centrality); + } else if (pdg == kXiMinus) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_XiMinus"), std::sqrt(ptSq), centrality); + } else if (pdg == -kXiMinus) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_XiPlus"), std::sqrt(ptSq), centrality); + } else if (pdg == kXi0) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_Xi0"), std::sqrt(ptSq), centrality); + } else if (pdg == -kXi0) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_AntiXi0"), std::sqrt(ptSq), centrality); + } else if (pdg == kOmegaMinus) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_OmegaMinus"), std::sqrt(ptSq), centrality); + } else if (pdg == -kOmegaMinus) { + ptSq = (ptRef * ptRef) + (mass * mass) - (o2::constants::physics::MassLambda1520 * o2::constants::physics::MassLambda1520); + if (ptSq > 0) + histos.fill(HIST("SignalLoss/hGen_mT_scaled_OmegaPlus"), std::sqrt(ptSq), centrality); + } + } + } + + PROCESS_SWITCH(Lambda1520pbpb, processMCGen, "Process Event for MC", false); + + using BinningType2 = ColumnBinningPolicy; + + void processMix(ResoCols const& collisions, ResoTracks const& tracks) + { + LOGF(debug, "Event Mixing Started"); + BinningType2 binningPositions2{{cMixVtxBins, cMixMultBins}}; + auto tracksTuple = std::make_tuple(tracks); + + SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; + for (auto const& [c1, t1, c2, t2] : pairs) { + if (cEvtMCRecINELgt0 && !c1.isRecINELgt0()) + return; + histos.fill(HIST("Event/mixing_vzVsmultpercentile"), c1.cent(), c1.posZ(), c1.evtPl()); + fillDataHistos(t1, t2, c1.cent()); + } + } + + PROCESS_SWITCH(Lambda1520pbpb, processMix, "Process for Mixed Events", true); + + Preslice perRColdf = aod::resodaughter::resoCollisionDFId; + using ResoColDFs = aod::ResoCollisionDFs; + using ResoTrackDFs = aod::ResoTrackDFs; + + void processDatadf(ResoColDFs::iterator const& collision, ResoTrackDFs const& tracks) + { + if (doprocessData) + LOG(error) << "Disable processData() first!"; + if (cEvtMCRecINELgt0 && !collision.isRecINELgt0()) + return; + + auto occup = 100; + if (cfgEvtOccupancyInTimeRange) + occup = collision.trackOccupancyInTimeRange(); + + histos.fill(HIST("Event/h1d_ft0_mult_percentile"), collision.cent(), occup); + fillDataHistos(tracks, tracks, collision.cent(), occup); + } + + PROCESS_SWITCH(Lambda1520pbpb, processDatadf, "Process for data merged DF", false); + + using BinningTypeDF = ColumnBinningPolicy; + void processMixDF(ResoColDFs const& collisions, ResoTrackDFs const& tracks) + { + if (doprocessMix) + LOG(fatal) << "Disable processMix() first!"; + LOGF(debug, "Event Mixing Started"); + + BinningTypeDF binningPositions2{{cMixVtxBins, cMixMultBins}}; + auto tracksTuple = std::make_tuple(tracks); + + SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; + for (auto const& [c1, t1, c2, t2] : pairs) { + if (cEvtMCRecINELgt0 && !c1.isRecINELgt0()) + return; + + auto occup = 100; + if (cfgEvtOccupancyInTimeRange) + occup = c1.trackOccupancyInTimeRange(); + + histos.fill(HIST("Event/mixing_vzVsmultpercentile"), c1.cent(), c1.posZ(), c1.evtPl()); + fillDataHistos(t1, t2, c1.cent(), occup); + } + } + + PROCESS_SWITCH(Lambda1520pbpb, processMixDF, "Process for merged DF Mixed Events", false); + + using BinningTypeEP = ColumnBinningPolicy; + void processMixepDF(ResoColDFs const& collisions, ResoTrackDFs const& tracks) + { + if (doprocessMix || doprocessMixDF) + LOG(fatal) << "Disable processMix() or processMixDF() first!"; + LOGF(debug, "Event Mixing Started"); + BinningTypeEP binningPositions2{{cMixVtxBins, cMixMultBins, cMixEPAngle}}; + auto tracksTuple = std::make_tuple(tracks); + + SameKindPair pairs{binningPositions2, cNumMixEv, -1, collisions, tracksTuple, &cache}; + for (auto const& [c1, t1, c2, t2] : pairs) { + histos.fill(HIST("Event/mixing_vzVsmultpercentile"), c1.cent(), c1.posZ(), c1.evtPl()); + fillDataHistos(t1, t2, c1.cent()); + } + } + + PROCESS_SWITCH(Lambda1520pbpb, processMixepDF, "Process for merged DF Mixed Events", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/lambdastarproxy.cxx b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx new file mode 100644 index 00000000000..2ff5ad926a9 --- /dev/null +++ b/PWGLF/Tasks/Resonances/lambdastarproxy.cxx @@ -0,0 +1,2772 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file lambdastarproxy.cxx +/// \brief Standalone Lambda* deuteron-proxy task extracted from nucleibalance.cxx. +/// \author Sushanta Tripathy + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace constants::math; + +struct Lambdastarproxy { + Service pdgDB{}; + + // ---- Ion/nucleus PDG encoding helpers (10LZZZAAAI) ---- + // Note: these are *format* constants (not particle PDG species codes) + static constexpr int IonCodeThreshold = 1000000000; // 10^9 + static constexpr int IonZDivisor = 10000; + static constexpr int IonZModulo = 1000; + // ---- Common PDG codes (use named values; avoid explicit literals) ---- + static constexpr int PdgElectron = static_cast(PDG_t::kElectron); + static constexpr int PdgMuon = static_cast(PDG_t::kMuonMinus); + static constexpr int PdgPion = static_cast(PDG_t::kPiPlus); + static constexpr int PdgKaon = static_cast(PDG_t::kKPlus); + static constexpr int PdgProton = static_cast(PDG_t::kProton); + // ---- Named defaults to avoid magic numbers (o2-linter) ---- + static constexpr float CutVertexDefault = 10.f; + static constexpr float CutPtMinDefault = 0.5f; + static constexpr float CutEtaMaxDefault = 0.8f; + + static constexpr float CutMCPtMinDefault = 0.1f; + static constexpr float CutMCEtaMaxDefault = 0.8f; + static constexpr int FillMCTruthDefault = 1; + + static constexpr float NsigmaTPCDefault = 3.f; + static constexpr float NsigmaTOFDefault = 3.f; + + static constexpr bool RequireGlobalTrackDefault = true; + static constexpr int TPCNClsMinDefault = 70; + static constexpr float DcaXYMaxDefault = 0.1f; + static constexpr float DcaZMaxDefault = 0.2f; + static constexpr float Chi2PerTPCClusterDefault = 2.5f; + static constexpr float Chi2PerITSClusterDefault = 36.f; + static constexpr int ITSNClustersDefault = 5; + + static constexpr int TriggerDefault = 4; + // Named trigger codes + static constexpr int TriggerNone = 0; + static constexpr int TriggerSel8 = 1; + static constexpr int TriggerSel8Quality = 2; + static constexpr int TriggerSel8OccQuality = 3; + static constexpr int TriggerSel8NoSbpZvtx = 4; + static constexpr int MinOccDefault = 0; + static constexpr int MaxOccDefault = 3000; + + static constexpr int NoMixedEventsDefault = 5; + static constexpr float MixZvtxMaxDefault = 2.0f; + static constexpr float MixMultMaxDefault = 50.0f; + + static constexpr float ProxyMomentumScale = 0.5f; + static constexpr float TofBetaMin = 0.01f; + static constexpr float TofBetaMax = 1.2f; + // PID strategy values + static constexpr int PidStrategyRectangular = 0; + static constexpr int PidStrategyCircularTPCAndTOF = 1; + static constexpr int PidStrategyNucleiDeuteronTPC = 2; + // Basic configuration for event and track selection + Configurable lstarCutVertex{"lstarCutVertex", float{CutVertexDefault}, "Accepted z-vertex range (cm)"}; + Configurable lstarCutPtMin{"lstarCutPtMin", float{CutPtMinDefault}, "Minimal pT for tracks (GeV/c)"}; + Configurable lstarCutEtaMax{"lstarCutEtaMax", float{CutEtaMaxDefault}, "Max |eta| for tracks"}; + + // MC truth + Configurable lstarCutMCPtMin{"lstarCutMCPtMin", float{CutMCPtMinDefault}, "Minimal pT for MC particles (GeV/c)"}; + Configurable lstarCutMCEtaMax{"lstarCutMCEtaMax", float{CutMCEtaMaxDefault}, "Max |eta| for MC particles"}; + Configurable lstarFillMCTruth{"lstarFillMCTruth", int{FillMCTruthDefault}, "Fill MC truth and reco-matching QA (AO2D-MC mode)"}; + + // PID cuts + Configurable lstarCutNsigmaTPCPi{"lstarCutNsigmaTPCPi", float{NsigmaTPCDefault}, "|nSigma^{TPC}_{#pi}| cut"}; + Configurable lstarCutNsigmaTOFPi{"lstarCutNsigmaTOFPi", float{NsigmaTOFDefault}, "|nSigma^{TOF}_{#pi}| cut"}; + Configurable lstarCutNsigmaTPCPr{"lstarCutNsigmaTPCPr", float{NsigmaTPCDefault}, "|nSigma^{TPC}_{p}| cut"}; + Configurable lstarCutNsigmaTOFPr{"lstarCutNsigmaTOFPr", float{NsigmaTOFDefault}, "|nSigma^{TOF}_{p}| cut"}; + Configurable lstarCutNsigmaTPCKaon{"lstarCutNsigmaTPCKaon", float{NsigmaTPCDefault}, "|nSigma^{TPC}_{K}| cut"}; + Configurable lstarCutNsigmaTOFKaon{"lstarCutNsigmaTOFKaon", float{NsigmaTOFDefault}, "|nSigma^{TOF}_{K}| cut"}; + Configurable lstarCutNsigmaTPCDe{"lstarCutNsigmaTPCDe", float{NsigmaTPCDefault}, "|nSigma^{TPC}_{d}| cut"}; + Configurable lstarCutNsigmaTOFDe{"lstarCutNsigmaTOFDe", float{NsigmaTOFDefault}, "|nSigma^{TOF}_{d}| cut"}; + Configurable lstarEnableTOFNsigmaCutDe{"lstarEnableTOFNsigmaCutDe", 0, "Enable deuteron-only TOF nSigma cut in PID strategy 2"}; + Configurable lstarProxyUseProtonPIDAsDeuteron{"lstarProxyUseProtonPIDAsDeuteron", 0, "Closure/control test: keep proxy candidates that pass the normal deuteron selection and are also proton-like, then build p_proxy = p_track/2"}; + // Optional deuteron-only TOF auxiliary selections. + // Defaults are OFF, so strategy 2 remains TPC nSigma only for deuterons. + Configurable lstarEnableBetaCutDe{"lstarEnableBetaCutDe", 0, "Enable deuteron-only TOF beta cut using beta() > lstarBetaCutDe"}; + Configurable lstarBetaCutDe{"lstarBetaCutDe", 0.4f, "Minimum TOF beta for deuteron-only beta cut"}; + Configurable lstarEnableExpSignalTOFDe{"lstarEnableExpSignalTOFDe", 0, "Enable deuteron-only TOF expected-signal-difference cut when lstarTOFExpSignalDiffDeMax > 0"}; + Configurable lstarTOFExpSignalDiffDeMax{"lstarTOFExpSignalDiffDeMax", -1.f, "Maximum |tofExpSignalDiffDe| for deuterons; <=0 disables the numeric cut"}; + + // PID strategy for final K/p/d candidate selection. + // 0 = pT-ref dependent rectangular cuts: + // pT < pTref: require |TPC| < TPC cut only + // pT >= pTref and TOF exists: require |TPC| < TPC cut and |TOF| < TOF cut + // pT >= pTref and TOF missing: reject + // 1 = pT-ref dependent circular cut: + // pT < pTref: require |TPC| < TPC cut only + // pT >= pTref and TOF exists: require sqrt(TPC^2 + TOF^2) < circular cut + // pT >= pTref and TOF missing: reject + // 2 = hybrid official-like PID: + // deuterons: require |TPC_d| < TPC cut only + // kaons/protons: use the Lambda(1520)-like pT-ref circular TPC+TOF logic + Configurable lstarPidStrategy{"lstarPidStrategy", int{PidStrategyRectangular}, "PID strategy: 0=pTref rectangular TPC/TOF, 1=pTref circular TPC+TOF, 2=hybrid: K/p circular, d TPC-only"}; + + Configurable lstarPidCircularCutKaon{"lstarPidCircularCutKaon", 2.0f, "Circular PID cut sqrt(nSigmaTPC_K^2+nSigmaTOF_K^2) for kaons"}; + Configurable lstarPidCircularCutPr{"lstarPidCircularCutPr", 2.0f, "Circular PID cut sqrt(nSigmaTPC_p^2+nSigmaTOF_p^2) for protons"}; + Configurable lstarPidCircularCutDe{"lstarPidCircularCutDe", 2.0f, "Circular PID cut sqrt(nSigmaTPC_d^2+nSigmaTOF_d^2) for deuterons"}; + + Configurable lstarPidPtRefKaon{"lstarPidPtRefKaon", 0.5f, "pT reference for kaon PID strategy"}; + Configurable lstarPidPtRefPr{"lstarPidPtRefPr", 0.8f, "pT reference for proton PID strategy"}; + Configurable lstarPidPtRefDe{"lstarPidPtRefDe", 0.8f, "pT reference for deuteron PID strategy"}; + + // Track quality + Configurable lstarRequireINELgt0{"lstarRequireINELgt0", 1, "Require INEL>0 event selection using isInelGt0() when available; fallback to multNTracksPVeta1()/numContrib()"}; + Configurable lstarRequireGlobalTrack{"lstarRequireGlobalTrack", bool{RequireGlobalTrackDefault}, "Require global tracks (default)"}; + Configurable lstarRequirePrimaryTrack{"lstarRequirePrimaryTrack", 1, "Require isPrimaryTrack() for Lambda* proxy candidates when the column is available"}; + Configurable lstarOnlyGlobalTrackCuts{"lstarOnlyGlobalTrackCuts", 0, "If enabled, apply only the global-track plus primary-track requirements and skip additional ITS/TPC/DCA/chi2 track-quality cuts"}; + Configurable lstarTPCNClsMin{"lstarTPCNClsMin", int{TPCNClsMinDefault}, "Minimum number of TPC crossed rows (tpcNClsCrossedRows)"}; + Configurable lstarTPCCrossedRowsOverFindableMin{"lstarTPCCrossedRowsOverFindableMin", 0.8f, "Minimum TPC crossed rows over findable clusters"}; + Configurable lstarDcaXYMax{"lstarDcaXYMax", float{DcaXYMaxDefault}, "Max |DCA_{xy}| to PV (cm)"}; + Configurable lstarDcaZMax{"lstarDcaZMax", float{DcaZMaxDefault}, "Max |DCA_{z}| to PV (cm)"}; + Configurable lstarChi2PerTPCCluster{"lstarChi2PerTPCCluster", float{Chi2PerTPCClusterDefault}, "Maximum Chi2/cluster for the TPC track segment"}; + Configurable lstarChi2PerITSCluster{"lstarChi2PerITSCluster", float{Chi2PerITSClusterDefault}, "Maximum Chi2/cluster for the ITS track segment"}; + Configurable lstarITSNClusters{"lstarITSNClusters", int{ITSNClustersDefault}, "Minimum number of ITS clusters"}; + + // Trigger + occupancy + Configurable lstarCfgTrigger{"lstarCfgTrigger", int{TriggerDefault}, "Event trigger selection: 0=none, 1=sel8, 2=sel8+(kNoSameBunchPileup+kIsGoodZvtxFT0vsPV+kIsGoodITSLayersAll), 3=sel8+occupancy+(kNoCollInTimeRangeStandard)+(kNoSameBunchPileup+kIsGoodZvtxFT0vsPV+kIsGoodITSLayersAll), 4=sel8+(kNoSameBunchPileup+kIsGoodZvtxFT0vsPV)"}; + Configurable lstarMinOcc{"lstarMinOcc", int{MinOccDefault}, "minimum occupancy selection (for cfgTrigger==3)"}; + Configurable lstarMaxOcc{"lstarMaxOcc", int{MaxOccDefault}, "maximum occupancy selection (for cfgTrigger==3)"}; + Configurable lstarUseFT0M{"lstarUseFT0M", 1, "Use FT0M centrality (0-100) as multiplicity axis (1=ON, 0=use Ntracks)"}; + + // --- Mixed-event configuration for pK / proxy invariant-mass background (AO2D only) --- + Configurable lstarNoMixedEvents{"lstarNoMixedEvents", int{NoMixedEventsDefault}, "Number of previous events kept for mixed-event background"}; + Configurable lstarMixZvtxMax{"lstarMixZvtxMax", float{MixZvtxMaxDefault}, "Max |Δzvtx| (cm) for event mixing"}; + Configurable lstarMixMultMax{"lstarMixMultMax", float{MixMultMaxDefault}, "Max |Δmult| for event mixing"}; + // Master switch for PID QA histogram filling. + // Inclusive PID-QA histograms are filled before final PID cuts, after event/track-quality cuts. + // Candidate-level nSigma/TOF/DCA histograms are filled after final PID cuts. + Configurable lstarEnablePidQA{"lstarEnablePidQA", 0, "Enable PID QA histograms (dE/dx, TOF #beta, proxy invariant-mass QA, etc.): 1 = ON, 0 = OFF"}; + Configurable lstarEnableProxyControls{"lstarEnableProxyControls", 1, "Enable TOF/PID-partitioned proxy spectra and inclusive full-pK maps (requires lstarEnableSparse)"}; + Configurable lstarRequireTOFMatchDe{"lstarRequireTOFMatchDe", 0, "Require a TOF match for every selected deuteron; 0 preserves hybrid unmatched candidates"}; + Configurable lstarEnableSparse{"lstarEnableSparse", 1, "Enable THnSparse invariant-mass histograms (#Lambda^{*} pK and proxy); 1 = ON, 0 = OFF"}; + Configurable lstarLambdaAbsYMax{"lstarLambdaAbsYMax", 0.5f, "Max |y_{pK}| (or y_{proxy K}) for #Lambda^{*} candidates"}; + + // Booking and filling use the same switches. Full mass maps grow with + // occupied mass x mass x pT x multiplicity cells and are diagnostic only. + Configurable lstarEnableFullPKMaps{"lstarEnableFullPKMaps", 0, "Book/fill 4D and 8D proxy/full-pK maps; enable only for diagnostic productions"}; + Configurable lstarEnableProtonLikeSparse{"lstarEnableProtonLikeSparse", 0, "Book/fill redundant proton-like proxy spectra (also obtainable from compact controls)"}; + Configurable lstarSparseMultBins{"lstarSparseMultBins", 100, "Multiplicity-axis bins in all invariant-mass sparse histograms; keep identical across merge inputs"}; + Configurable lstarMCPrimaryOnly{"lstarMCPrimaryOnly", 1, "Generated p/K/d fiducial spectra: require physical primaries (strong-decay daughters included)"}; + + // Optional diagnostics leave the original selections and histograms intact. + Configurable lstarEnableMCProxySpecies{"lstarEnableMCProxySpecies", 0, "Book selected MC proxy species x TOF/proton/multiplicity states and retained-parent controls"}; + Configurable lstarEnableProxyPIDProbes{"lstarEnableProxyPIDProbes", 0, "Book separate loose-TPC/TOF matched pair probes for PID-versus-proxy-mass fits (data or reconstructed MC)"}; + Configurable lstarProxyProbeTPCDeMax{"lstarProxyProbeTPCDeMax", 6.f, "Loose d-slot TPC envelope for PID probes; 0 < cut <= 6"}; + Configurable lstarProxyProbeTPCKaMax{"lstarProxyProbeTPCKaMax", 6.f, "Loose K-slot TPC envelope for PID probes; 0 < cut <= 6"}; + Configurable lstarProxyProbeTOFDeMax{"lstarProxyProbeTOFDeMax", 8.f, "Loose d-slot TOF envelope for PID probes; 0 < cut <= 8"}; + Configurable lstarProxyProbeTOFKaMax{"lstarProxyProbeTOFKaMax", 8.f, "Loose K-slot TOF envelope for PID probes; 0 < cut <= 8"}; + + struct RetainedMother { + int64_t id = -1; + int pdg = 0; + }; + + struct TruthInfo { + int64_t id = -1; + int collisionId = -1; + int pdg = 0; + float px = 0.f, py = 0.f, pz = 0.f; + bool primary = false; + std::vector lambdaMothers; + std::vector diagnosticMothers; + }; + + struct KaonCand { + float px = 0.f, py = 0.f, pz = 0.f; + bool hasTOF = false; + int charge = 0; + int tid = -1; + TruthInfo truth{}; + }; + // PID flags describe detector compatibility, not truth-level purity. + struct ProxyCand { + float px = 0.f, py = 0.f, pz = 0.f; + float pxFull = 0.f, pyFull = 0.f, pzFull = 0.f; + bool protonLike = false; + bool hasTOF = false; + bool protonTPCCompatible = false; + int charge = 0; + int tid = -1; + TruthInfo truth{}; + }; + struct ProtonCand { + float px = 0.f, py = 0.f, pz = 0.f; + int charge = 0; + int tid = -1; + TruthInfo truth{}; + }; + + // Helpers for invariant-mass kinematics + static float phiFromPxPy(float px, float py) + { + return RecoDecay::constrainAngle(std::atan2(py, px), -o2::constants::math::PI); + } + + static float ptFromPxPy(float px, float py) + { + return RecoDecay::pt(std::array{px, py}); + } + + static float rapidityFromMomentumAndMass(float px, float py, float pz, double mass) + + { + return RecoDecay::y(std::array{px, py, pz}, mass); + } + + // Mixed-event pool entry for pK / proxy background (AO2D only) + struct LStarMixEventEntry { + float mult = 0.f; + float zvtx = 0.f; + std::vector kaons; + std::vector protons; + }; + + // Keep last N events for event-mixing + std::deque mLStarMixEvents; + + template + bool keepCollisionAO2D(TCollision const& collision) const + { + // Prefer the official event-selection flag when available. + if (lstarRequireINELgt0.value != 0) { + bool isINELgt0 = true; + + if constexpr (requires { collision.isInelGt0(); }) { + isINELgt0 = collision.isInelGt0(); + } else if constexpr (requires { collision.multNTracksPVeta1(); }) { + isINELgt0 = collision.multNTracksPVeta1() > 0; + } else if constexpr (requires { collision.numContrib(); }) { + isINELgt0 = collision.numContrib() > 0; + } + + if (!isINELgt0) { + return false; + } + } + if (lstarCfgTrigger.value == TriggerNone) { + return true; + } + if (lstarCfgTrigger.value == TriggerSel8) { + return collision.sel8(); + } + if (lstarCfgTrigger.value == TriggerSel8Quality) { + return collision.sel8() && + collision.selection_bit(aod::evsel::kNoSameBunchPileup) && + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && + collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); + } + if (lstarCfgTrigger.value == TriggerSel8OccQuality) { + const int occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < lstarMinOcc.value || occupancy >= lstarMaxOcc.value) { + return false; + } + return collision.sel8() && + collision.selection_bit(aod::evsel::kNoSameBunchPileup) && + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) && + collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) && + collision.selection_bit(aod::evsel::kIsGoodITSLayersAll); + } + if (lstarCfgTrigger.value == TriggerSel8NoSbpZvtx) { + return collision.sel8() && + collision.selection_bit(aod::evsel::kNoSameBunchPileup) && + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV); + } + + LOGF(warn, "Invalid lstarCfgTrigger=%d. Accepting all collisions.", lstarCfgTrigger.value); + return true; + } + + template + float eventMultiplicityFT0MOrFallback(TCollision const& collision, float fallback) const + { + if (lstarUseFT0M.value == 0) { + return fallback; + } + + if constexpr (requires { collision.centFT0M(); }) { + const float v = collision.centFT0M(); + if (v >= 0.f) { + return v; + } + } + + if constexpr (requires { collision.centFT0MValid(); }) { + if (collision.centFT0MValid()) { + if constexpr (requires { collision.centFT0M(); }) { + return collision.centFT0M(); + } + } + } + + return fallback; + } + + int chargeFromPdg(int pdg) const + { + const int apdg = std::abs(pdg); + + // Ions/nuclei: PDG code format 10LZZZAAAI -> Z is encoded in digits [7..5] + if (apdg >= IonCodeThreshold) { + const int z = (apdg / IonZDivisor) % IonZModulo; + return (pdg >= 0) ? z : -z; + } + + switch (apdg) { + case PdgElectron: // e + case PdgMuon: // mu + return (pdg >= 0) ? -1 : 1; + case PdgPion: // pi + case PdgKaon: // K + case PdgProton: // p + return (pdg >= 0) ? 1 : -1; + default: + if (auto* particle = pdgDB->GetParticle(pdg)) { + return static_cast(std::lround(particle->Charge() / 3.)); + } + return 0; + } + } + + // Histogram registry for this task + HistogramRegistry histos{"lstarRegistry"}; + + // Filters + Filter collisionZVtxFilter = nabs(aod::collision::posZ) < lstarCutVertex; + Filter trackFilter = (nabs(aod::track::eta) < lstarCutEtaMax) && (aod::track::pt > lstarCutPtMin); + + // Tracks with PID information from TPC and TOF services for all relevant species + // NOTE: aod::TracksExtra is needed for TPC dE/dx (tpcSignal) + using TracksWithPID = soa::Join; + template + bool passTrackQuality(const TTrack& trk) const + { + if (lstarRequireGlobalTrack.value) { + if constexpr (requires { trk.isGlobalTrack(); }) { + if (!trk.isGlobalTrack()) { + return false; + } + } else if constexpr (requires { trk.isGlobalTrackSDD(); }) { + if (!trk.isGlobalTrackSDD()) { + return false; + } + } + } + + // Always require primary tracks + if (lstarRequirePrimaryTrack.value != 0) { + if constexpr (requires { trk.isPrimaryTrack(); }) { + if (!trk.isPrimaryTrack()) { + return false; + } + } + } + + // Optional official-baseline mode: use only the O2 global-track and primary-track definitions. + // This bypasses the extra explicit ITS/TPC/DCA/chi2 cuts below. + if (lstarOnlyGlobalTrackCuts.value != 0) { + return true; + } + + if constexpr (requires { trk.itsNCls(); }) { + if (lstarITSNClusters.value > 0 && trk.itsNCls() < lstarITSNClusters.value) { + return false; + } + } + + if constexpr (requires { trk.tpcNClsCrossedRows(); }) { + if (lstarTPCNClsMin.value > 0 && trk.tpcNClsCrossedRows() < lstarTPCNClsMin.value) { + return false; + } + } else if constexpr (requires { trk.tpcNClsFindable(); trk.tpcCrossedRowsOverFindableCls(); }) { + if (lstarTPCNClsMin.value > 0 && trk.tpcNClsFindable() * trk.tpcCrossedRowsOverFindableCls() < lstarTPCNClsMin.value) { + return false; + } + } + + if constexpr (requires { trk.tpcCrossedRowsOverFindableCls(); }) { + if (lstarTPCCrossedRowsOverFindableMin.value > 0.f && + trk.tpcCrossedRowsOverFindableCls() < lstarTPCCrossedRowsOverFindableMin.value) { + return false; + } + } + + if constexpr (requires { trk.tpcChi2NCl(); }) { + if (lstarChi2PerTPCCluster.value > 0.f && trk.tpcChi2NCl() > lstarChi2PerTPCCluster.value) { + return false; + } + } + + if constexpr (requires { trk.itsChi2NCl(); }) { + if (lstarChi2PerITSCluster.value > 0.f && trk.itsChi2NCl() > lstarChi2PerITSCluster.value) { + return false; + } + } + + if constexpr (requires { trk.dcaXY(); }) { + if (lstarDcaXYMax.value > 0.f && std::abs(trk.dcaXY()) > lstarDcaXYMax.value) { + return false; + } + } + + if constexpr (requires { trk.dcaZ(); }) { + if (lstarDcaZMax.value > 0.f && std::abs(trk.dcaZ()) > lstarDcaZMax.value) { + return false; + } + } + + return true; + } + using CollisionsWithEvSel = soa::Join; + using FilteredCollisions = soa::Filtered; + using FilteredTracks = soa::Filtered; + + // AO2D-MC variants (for truth QA and reco->MC matching) + using CollisionsWithEvSelMC = soa::Join; + using FilteredCollisionsMC = soa::Filtered; + + using TracksWithPIDMC = soa::Join; + using FilteredTracksMC = soa::Filtered; + + Preslice mcParticlesPerCollision = aod::mcparticle::mcCollisionId; + + void init(o2::framework::InitContext&) + { + if (doprocess && doprocessMCReco) { + LOGF(fatal, "Enable either process (data) or processMCReco, never both"); + } + if (lstarNoMixedEvents.value < 0 || lstarSparseMultBins.value <= 0) { + LOGF(fatal, "lstarNoMixedEvents must be nonnegative; lstarSparseMultBins must be positive"); + } + AxisSpec massAxis{200, 1.4, 1.9, "M_{pK} (GeV/c^{2})"}; + AxisSpec ptAxis{100, 0., 10., "p_{T} (GeV/c)"}; + AxisSpec nsAxis{500, -50., 50., "n#sigma"}; + AxisSpec tofMatchAxis{2, -0.5, 1.5, "has TOF match"}; + AxisSpec pAxis{100, 0., 10., "p (GeV/c)"}; + AxisSpec etaAxis{80, -2., 2., "#eta"}; + AxisSpec phiAxis{64, 0., o2::constants::math::TwoPI, "#varphi"}; + AxisSpec centAxis{lstarSparseMultBins.value, 0., 100., "multiplicity/centrality"}; + + AxisSpec pdgAxis{10001, -5000.5, 5000.5, "PDG code"}; + + AxisSpec dEdxAxis{400, 0., 200., "TPC dE/dx (arb. units)"}; + AxisSpec betaAxis{160, 0., 1.6, "#beta_{TOF}"}; + AxisSpec dcaXYAxis{200, -0.2, 0.2, "DCA_{xy} (cm)"}; + AxisSpec dcaZAxis{200, -0.2, 0.2, "DCA_{z} (cm)"}; + + // Count reconstructed collisions received by the data process, before any + // task event cuts and after all of them, independently of track candidates. + histos.add("hEventSelection", "Proxy event selection;Selection stage;Events", + HistType::kTH1D, {AxisSpec{2, 0.5, 2.5, "Selection stage"}}); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "Before event selection"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "After event selection"); + + // Invariant-mass spectra + histos.add("hInvMassPKUnlike", + "pK invariant mass (unlike-sign);M_{pK} (GeV/c^{2});Counts", + HistType::kTH1F, {massAxis}); + histos.add("hInvMassPKLike", + "pK invariant mass (like-sign);M_{pK} (GeV/c^{2});Counts", + HistType::kTH1F, {massAxis}); + + // THnSparse for invariant-mass analysis (mass, pT, multiplicity/centrality) + if (lstarEnableSparse.value != 0) { + + if (lstarEnableProxyControls.value != 0) { + // Axes 3/4/5 partition the SAME inclusive proxy sample in every pair class. + const std::vector controlAxes{ + AxisSpec{400, 1.4, 1.8, "M_{(d/2)K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{(d/2)K} (GeV/c)"}, centAxis, + AxisSpec{2, -0.5, 1.5, "deuteron hasTOF (0/1)"}, + AxisSpec{2, -0.5, 1.5, "passes full proton PID (0/1)"}, + AxisSpec{2, -0.5, 1.5, "passes proton TPC cut only (0/1)"}}; + histos.add("hLambdaStarProxySelectedTrackControlSparse", "Selected deuteron track controls", + HistType::kTHnSparseF, + {AxisSpec{100, 0., 10., "p_{T}^{d,full} (GeV/c)"}, controlAxes[3], controlAxes[4], controlAxes[5]}, true); + auto mapAxes = controlAxes; + // Full-pK rapidity is recorded, not imposed. Include mass flow bins + // when projecting these maps back onto the inclusive proxy sample. + mapAxes.emplace_back(1000, 1.4, 2.4, "M_{pK}^{full} (GeV/c^{2})"); + mapAxes.emplace_back(2, -0.5, 1.5, "passes full-pK rapidity (0/1)"); + for (auto const& pairClass : {"Unlike", "Like", "Mixed"}) { + if (std::strcmp(pairClass, "Mixed") == 0 && lstarNoMixedEvents.value == 0) { + continue; + } + const std::string spectrumName = std::string("hLambdaStarProxyControl") + pairClass + "Sparse"; + const std::string mapName = std::string("hLambdaStarProxyControlFullPK") + pairClass + "Sparse"; + histos.add(spectrumName.c_str(), "Inclusive proxy acceptance; PID compatibility controls", HistType::kTHnSparseF, controlAxes, true); + if (lstarEnableFullPKMaps.value != 0) { + histos.add(mapName.c_str(), "Inclusive proxy acceptance; full-pK control map", HistType::kTHnSparseF, mapAxes, true); + } + } + } + + if (lstarEnableFullPKMaps.value != 0) { + if (lstarNoMixedEvents.value > 0) { + histos.add( + "hLambdaStarProxyVsFullPKMixedSparse", + "d-selected mixed-event pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + } + } + + if (lstarEnableProtonLikeSparse.value != 0) { + histos.add( + "hLambdaStarProxyProtonLikeUnlikeSparse", + "d-selected and proton-like OS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + } + + if (lstarEnableProtonLikeSparse.value != 0) { + histos.add( + "hLambdaStarProxyProtonLikeLikeSparse", + "d-selected and proton-like LS proxy candidates;" + "M_{(d/2)K} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + } + + histos.add("hLambdaStarPKUnlikeSparse", + "#Lambda^{*}(1520) pK unlike-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); + + histos.add("hLambdaStarPKLikeSparse", + "#Lambda^{*}(1520) pK like-sign candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); + + if (lstarNoMixedEvents.value > 0) { + histos.add("hLambdaStarPKMixedSparse", + "#Lambda^{*}(1520) pK mixed-event candidates;M_{pK} (GeV/c^{2});p_{T}^{pK} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{pK} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{pK} (GeV/c)"}, centAxis}); + } + + // THnSparse for deuteron-proxy invariant-mass analysis (mass, pT, multiplicity/centrality) + histos.add("hLambdaStarProxySparse", + "#Lambda^{*}(1520) deuteron-proxy candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + + histos.add("hLambdaStarProxyLikeSparse", + "#Lambda^{*}(1520) deuteron-proxy like-sign candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + + if (lstarNoMixedEvents.value > 0) { + histos.add("hLambdaStarProxyMixedSparse", + "#Lambda^{*}(1520) deuteron-proxy mixed-event candidates;M_{p_{proxy}K} (GeV/c^{2});p_{T}^{p_{proxy}K} (GeV/c);multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, "M_{p_{proxy}K} (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "p_{T}^{p_{proxy}K} (GeV/c)"}, centAxis}); + } + + if (lstarEnableFullPKMaps.value != 0) { + histos.add( + "hLambdaStarProxyVsFullPKUnlikeSparse", + "d-selected OS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + } + + if (lstarEnableFullPKMaps.value != 0) { + histos.add( + "hLambdaStarProxyVsFullPKLikeSparse", + "d-selected LS pairs: full-p proton hypothesis vs (d/2)K proxy;" + "M_{(d/2)K} (GeV/c^{2});" + "M_{pK}^{full} (GeV/c^{2});" + "p_{T}^{(d/2)K} (GeV/c);" + "multiplicity/centrality", + HistType::kTHnSparseF, + {AxisSpec{400, 1.4, 1.8, + "M_{(d/2)K} (GeV/c^{2})"}, + + AxisSpec{400, 1.4, 1.8, + "M_{pK}^{full} (GeV/c^{2})"}, + + AxisSpec{100, 0., 10., + "p_{T}^{(d/2)K} (GeV/c)"}, + + centAxis}); + } + } + + // Deuteron-proxy invariant mass (p_{proxy} from d/2 combined with K) + histos.add("hDeuteronProxyMass", + "#Lambda^{*} proxy invariant mass from (d/2 + K);M_{p_{proxy}K} (GeV/c^{2});Counts", + HistType::kTH1F, {massAxis}); + + if (lstarEnablePidQA.value != 0) { + // Inclusive PID QA before final candidate PID cuts: after track-quality cuts only + // TPC dE/dx vs total momentum + histos.add("hTPCdEdxVsP", + "TPC dE/dx vs p;p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + + // TOF #beta vs total momentum + histos.add("hTOFBetaVsP", + "TOF #beta vs p;p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + + histos.add("hHasTOFVsP", + "TOF matching flag vs p;p (GeV/c);has TOF match;Counts", + HistType::kTH2F, {pAxis, tofMatchAxis}); + + histos.add("hHasTOFVsPt", + "TOF matching flag vs p_{T};p_{T} (GeV/c);has TOF match;Counts", + HistType::kTH2F, {ptAxis, tofMatchAxis}); + + // --- Per-species inclusive PID QA before final candidate PID cuts --- + // Species tagging here uses classifyPidSpecies() and is meant only for QA. + histos.add("hTPCdEdxVsP_Pi", + "TPC dE/dx vs p (tagged #pi);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_K", + "TPC dE/dx vs p (tagged K);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_P", + "TPC dE/dx vs p (tagged p);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + histos.add("hTPCdEdxVsP_D", + "TPC dE/dx vs p (tagged d);p (GeV/c);dE/dx (arb. units);Counts", + HistType::kTH2F, {pAxis, dEdxAxis}); + + histos.add("hTOFBetaVsP_Pi", + "TOF #beta vs p (tagged #pi);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_K", + "TOF #beta vs p (tagged K);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_P", + "TOF #beta vs p (tagged p);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + histos.add("hTOFBetaVsP_D", + "TOF #beta vs p (tagged d);p (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {pAxis, betaAxis}); + + // Inclusive tagged nSigma QA before final candidate PID cuts: after track-quality cuts only. + // These are separate from the final-candidate PID QA histograms. + histos.add("hNsigmaTPCPionTaggedVsP", + "TPC n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TPC}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFPionTaggedVsP", + "TOF n#sigma_{#pi} vs p for tagged #pi;p (GeV/c);n#sigma^{TOF}_{#pi};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCKaonTaggedVsP", + "TPC n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFKaonTaggedVsP", + "TOF n#sigma_{K} vs p for tagged K;p (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonTaggedVsP", + "TPC n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonTaggedVsP", + "TOF n#sigma_{p} vs p for tagged p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronTaggedVsP", + "TPC n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronTaggedVsP", + "TOF n#sigma_{d} vs p for tagged d;p (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + } + + if (doprocessMCQA && lstarFillMCTruth.value != 0) { + // --- MC QA (AO2D-MC mode) --- + histos.add("hMcPrimariesPtEta", + "MC charged physical primaries; p_{T} (GeV/c); #eta; Counts", + HistType::kTH2F, {ptAxis, etaAxis}); + + histos.add("hRecoMatchedPdg", + "Reco tracks matched to MC (PDG); PDG code; Counts", + HistType::kTH1F, {pdgAxis}); + + histos.add("hRecoFakePtEta", + "Reco tracks without MC label (fakes / unmatched); p_{T} (GeV/c); #eta; Counts", + HistType::kTH2F, {ptAxis, etaAxis}); + } + + if (lstarEnablePidQA.value != 0) { + // Deuteron-proxy kinematics and PID QA + histos.add("hDeuteronProxyPt", + "Deuteron proxy p_{T};p_{T} (GeV/c);Counts", + HistType::kTH1F, {ptAxis}); + histos.add("hDeuteronProxyEta", + "Deuteron proxy #eta;#eta;Counts", + HistType::kTH1F, {etaAxis}); + histos.add("hDeuteronProxyPhi", + "Deuteron proxy #varphi;#varphi;Counts", + HistType::kTH1F, {phiAxis}); + + histos.add("hNsigmaTPCDeuteron", + "TPC n#sigma_{d};n#sigma^{TPC}_{d};Counts", + HistType::kTH1F, {nsAxis}); + histos.add("hNsigmaTOFDeuteron", + "TOF n#sigma_{d};n#sigma^{TOF}_{d};Counts", + HistType::kTH1F, {nsAxis}); + + histos.add("hNsigmaTPCDeuteronVsP", + "TPC n#sigma_{d} vs p; p (GeV/c); n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronVsP", + "TOF n#sigma_{d} vs p; p (GeV/c); n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + // Kaon kinematics and PID QA + histos.add("hKaonPt", + "Kaon p_{T};p_{T} (GeV/c);Counts", + HistType::kTH1F, {ptAxis}); + histos.add("hKaonEta", + "Kaon #eta;#eta;Counts", + HistType::kTH1F, {etaAxis}); + histos.add("hKaonPhi", + "Kaon #varphi;#varphi;Counts", + HistType::kTH1F, {phiAxis}); + + histos.add("hNsigmaTPCKaon", + "TPC n#sigma_{K};n#sigma^{TPC}_{K};Counts", + HistType::kTH1F, {nsAxis}); + histos.add("hNsigmaTOFKaon", + "TOF n#sigma_{K};n#sigma^{TOF}_{K};Counts", + HistType::kTH1F, {nsAxis}); + + histos.add("hNsigmaTPCKaonVsP", + "TPC n#sigma_{K} vs p; p (GeV/c); n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFKaonVsP", + "TOF n#sigma_{K} vs p; p (GeV/c); n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + + // --- Candidate PID QA after final selected K/p/d PID cuts --- + // These histograms are needed for PID studies in the same pT intervals used by the analysis. + histos.add("hTOFBetaVsPt_K", + "TOF #beta vs p_{T} for selected K;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_P", + "TOF #beta vs p_{T} for selected p;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + histos.add("hTOFBetaVsPt_D", + "TOF #beta vs p_{T} for selected d;p_{T} (GeV/c);#beta_{TOF};Counts", + HistType::kTH2F, {ptAxis, betaAxis}); + + histos.add("hNsigmaTPCKaonVsPt", + "TPC n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFKaonVsPt", + "TOF n#sigma_{K} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCProtonVsP", + "TPC n#sigma_{p} vs p;p (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsP", + "TOF n#sigma_{p} vs p;p (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {pAxis, nsAxis}); + histos.add("hNsigmaTPCProtonVsPt", + "TPC n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFProtonVsPt", + "TOF n#sigma_{p} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + + histos.add("hNsigmaTPCDeuteronVsPt", + "TPC n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TPC}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add("hNsigmaTOFDeuteronVsPt", + "TOF n#sigma_{d} vs p_{T};p_{T} (GeV/c);n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {ptAxis, nsAxis}); + histos.add( + "hNsigmaTPCProtonForSelectedDeuteronVsPt", + "TPC n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hNsigmaTOFProtonForSelectedDeuteronVsPt", + "TOF n#sigma_{p} for selected d candidates vs p_{T};" + "p_{T} (GeV/c);" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {ptAxis, nsAxis}); + + histos.add( + "hTPCNsigmaDeVsPrForSelectedDeuteron", + "TPC PID overlap for selected d candidates;" + "n#sigma^{TPC}_{d};" + "n#sigma^{TPC}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add( + "hTOFNsigmaDeVsPrForSelectedDeuteron", + "TOF PID overlap for selected d candidates;" + "n#sigma^{TOF}_{d};" + "n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, + {nsAxis, nsAxis}); + + histos.add("hTPCvsTOFNsigma_K", + "TPC vs TOF n#sigma for selected K;n#sigma^{TPC}_{K};n#sigma^{TOF}_{K};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_P", + "TPC vs TOF n#sigma for selected p;n#sigma^{TPC}_{p};n#sigma^{TOF}_{p};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + histos.add("hTPCvsTOFNsigma_D", + "TPC vs TOF n#sigma for selected d;n#sigma^{TPC}_{d};n#sigma^{TOF}_{d};Counts", + HistType::kTH2F, {nsAxis, nsAxis}); + + // --- DCA QA for final selected K/p/d candidates --- + // Filled only when lstarEnablePidQA = 1. + histos.add("hDCAxyVsPt_K", + "DCA_{xy} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_K", + "DCA_{z} vs p_{T} distribution for selected K;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_P", + "DCA_{xy} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_P", + "DCA_{z} vs p_{T} distribution for selected p;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + + histos.add("hDCAxyVsPt_D", + "DCA_{xy} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{xy} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaXYAxis}); + histos.add("hDCAzVsPt_D", + "DCA_{z} vs p_{T} distribution for selected d;p_{T} (GeV/c);DCA_{z} (cm);Counts", + HistType::kTH2F, {ptAxis, dcaZAxis}); + } + bookMC(); + bookProxyDiagnostics(); + } + + static constexpr int PdgLambdaStar = static_cast(PDG_t::kLambda1520); + static constexpr int PdgDeuteron = static_cast(o2::constants::physics::Pdg::kDeuteron); + // MC outputs are 2D mass/pT histograms. They add no high-dimensional maps. + std::array, 5> mcGenSpectra{}; + std::array, 2>, 4> mcRecoProxy{}; + std::shared_ptr mcRecoPKTrue, mcPKResponse, mcProxyResponse; + + // Fixed bounded diagnostics: the four analysis pT bins plus ROOT flow bins. + // Existing native 1-MeV/0.1-GeV histograms are not changed or replaced. + static constexpr std::array DiagnosticPairPtEdges{0.6, 1.3, 1.4, 1.9, 3.6}; + static constexpr std::array ProbeMomentumEdges{0., 0.5, 0.8, 1.2, 1.8, 2.5, 3.5, 5., 10.}; + static constexpr std::array SpeciesNames{"e", "mu", "pi", "K", "p", "d", "remaining"}; + static constexpr int SpeciesCount = SpeciesNames.size(); + static constexpr int SpeciesPairs = SpeciesCount * SpeciesCount; + static constexpr int UnmatchedSpeciesTag = SpeciesPairs; + static constexpr int ChargeMismatchSpeciesTag = SpeciesPairs + 1; + static constexpr int SpeciesTagCount = SpeciesPairs + 2; + static constexpr int ProtonSpecies = 4; + static constexpr int DeuteronSpecies = 5; + static constexpr int KaonSpecies = 3; + static constexpr int SelectedPIDStates = 16; + static constexpr int ProbeRegions = 4; + static constexpr int ProbePairPtBins = DiagnosticPairPtEdges.size() - 1; + static constexpr int ProbeMomentumBins = ProbeMomentumEdges.size(); // includes p >= 10 + static constexpr int ProbeTagCount = ProbePairPtBins * ProbeMomentumBins * ProbeRegions; + static constexpr int ParentClasses = 10; + static constexpr int DiagnosticMassBins = 200; + static constexpr int ProbeMassBins = 100; + static constexpr float ProbeTPCMax = 6.f; + static constexpr float ProbeTOFMax = 8.f; + static constexpr double MultMin = 0.; + static constexpr double MultMax = 100.; + static constexpr std::array PairSigns{"Unlike", "Like"}; + static constexpr std::array ProbeRoles{"De", "Ka"}; + static constexpr std::array ParentNames{ + "no retained common mother", "K0S", "Lambda1115", "Kstar", "phi", "Lambda1520", + "other retained common mother", "multiple retained common mothers", "same MC particle", "invalid truth labels"}; + + std::array, 2> mcProxySpecies{}; + std::array, 2> mcProxyOtherParents{}; + std::array, 2>, 2> probeMass2{}, probeTPC{}, probeTOF{}; + std::array, 2> probePairRegions{}, mcProbeSpecies{}; + std::shared_ptr probeTrackStatus; + std::shared_ptr probeEventScope; + + struct PIDProbeCand { + float px = 0.f, py = 0.f, pz = 0.f; + double momentum = 0., mass2 = 0.; + float nsTPC = 0.f, nsTOF = 0.f; + bool nominal = false; + int charge = 0; + int64_t tid = -1; + TruthInfo truth{}; + }; + + static bool regularMultiplicity(float mult) + { + return std::isfinite(mult) && mult >= MultMin && mult < MultMax; + } + + static int speciesGroup(int pdg) + { + switch (std::abs(pdg)) { + case PdgElectron: + return 0; + case PdgMuon: + return 1; + case PdgPion: + return 2; + case PdgKaon: + return KaonSpecies; + case PdgProton: + return ProtonSpecies; + case PdgDeuteron: + return DeuteronSpecies; + default: + return SpeciesCount - 1; + } + } + + int speciesPairTag(TruthInfo const& a, TruthInfo const& b, int chargeA, int chargeB) + { + if (a.id < 0 || b.id < 0) { + return UnmatchedSpeciesTag; + } + if (chargeFromPdg(a.pdg) != chargeA || chargeFromPdg(b.pdg) != chargeB) { + return ChargeMismatchSpeciesTag; + } + return SpeciesCount * speciesGroup(a.pdg) + speciesGroup(b.pdg); + } + + static bool otherSpeciesTag(int tag) + { + return tag != UnmatchedSpeciesTag && tag != SpeciesCount * DeuteronSpecies + KaonSpecies && tag != SpeciesCount * ProtonSpecies + KaonSpecies; + } + + static int retainedParentClass(TruthInfo const& a, TruthInfo const& b) + { + if (a.id < 0 || b.id < 0) { + return ParentClasses - 1; + } + if (a.id == b.id) { + return ParentClasses - 2; + } + int64_t commonId = -1; + int commonPdg = 0; + for (auto const& ma : a.diagnosticMothers) { + for (auto const& mb : b.diagnosticMothers) { + if (ma.id != mb.id) { + continue; + } + if (commonId >= 0 && commonId != ma.id) { + return ParentClasses - 3; + } + commonId = ma.id; + commonPdg = std::abs(ma.pdg); + } + } + if (commonId < 0) { + return 0; // absence of a retained link does not establish absence of ancestry + } + if (commonPdg == static_cast(PDG_t::kK0Short)) { + return 1; + } + if (commonPdg == static_cast(PDG_t::kLambda0)) { + return 2; + } + if (commonPdg == static_cast(o2::constants::physics::Pdg::kK0Star892) || commonPdg == static_cast(o2::constants::physics::Pdg::kKPlusStar892)) { + return 3; + } + if (commonPdg == static_cast(o2::constants::physics::Pdg::kPhi)) { + return 4; + } + if (commonPdg == PdgLambdaStar) { + return 5; + } + return 6; + } + + static int probePairPtBin(float pt) + { + if (!std::isfinite(pt) || pt < DiagnosticPairPtEdges.front() || pt >= DiagnosticPairPtEdges.back()) { + return -1; + } + return static_cast(std::upper_bound(DiagnosticPairPtEdges.begin(), DiagnosticPairPtEdges.end(), pt) - DiagnosticPairPtEdges.begin()) - 1; + } + + static int probeMomentumBin(double momentum) + { + return std::clamp(static_cast(std::upper_bound(ProbeMomentumEdges.begin(), ProbeMomentumEdges.end(), momentum) - ProbeMomentumEdges.begin()) - 1, 0, ProbeMomentumBins - 1); + } + + static int probeTag(int pairBin, double momentum, int region) + { + return pairBin + ProbePairPtBins * (probeMomentumBin(momentum) + ProbeMomentumBins * region); + } + + static std::string speciesTagName(int tag) + { + if (tag == UnmatchedSpeciesTag) { + return "unmatched"; + } + if (tag == ChargeMismatchSpeciesTag) { + return "charge-inconsistent"; + } + return std::string(SpeciesNames[tag / SpeciesCount]) + ":" + SpeciesNames[tag % SpeciesCount]; + } + + void bookProxyDiagnostics() + { + if (lstarEnableMCProxySpecies.value != 0 && !doprocessMCReco) { + LOGF(fatal, "lstarEnableMCProxySpecies requires processMCReco"); + } + if (lstarEnableProxyPIDProbes.value != 0) { + if (!doprocess && !doprocessMCReco) { + LOGF(fatal, "PID probes require the data or reconstructed-MC process"); + } + for (auto const& cut : {lstarProxyProbeTPCDeMax.value, lstarProxyProbeTPCKaMax.value}) { + if (!std::isfinite(cut) || cut <= 0.f || cut > ProbeTPCMax) { + LOGF(fatal, "Probe TPC limits must lie in (0,6]"); + } + } + for (auto const& cut : {lstarProxyProbeTOFDeMax.value, lstarProxyProbeTOFKaMax.value}) { + if (!std::isfinite(cut) || cut <= 0.f || cut > ProbeTOFMax) { + LOGF(fatal, "Probe TOF limits must lie in (0,8]"); + } + } + } + const AxisSpec pairPt{std::vector(DiagnosticPairPtEdges.begin(), DiagnosticPairPtEdges.end()), "Proxy pair pT (GeV/c)"}; + const AxisSpec mcMass{DiagnosticMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; + const AxisSpec probeMass{ProbeMassBins, 1.4, 1.8, "M_(d/2)K (GeV/c2)"}; + uint64_t payload = 0; + auto add3 = [&](std::string const& name, std::string const& title, std::vector const& axes) { + auto h = histos.add(name.c_str(), title.c_str(), HistType::kTH3D, axes, true); + payload += static_cast(h->GetNcells()) * 2 * sizeof(double); + return h; + }; + for (size_t sign = 0; sign < PairSigns.size(); ++sign) { + const std::string suffix = PairSigns[sign]; + if (lstarEnableMCProxySpecies.value != 0) { + mcProxySpecies[sign] = add3("hMCProxySpecies" + suffix, "Selected proxy;species+51*(dTOF+2*kTOF+4*pTPC+8*regularMult)", + {mcMass, pairPt, AxisSpec{SpeciesTagCount * SelectedPIDStates, -0.5, SpeciesTagCount * SelectedPIDStates - 0.5, "Species and detector state"}}); + for (int state = 0; state < SelectedPIDStates; ++state) { + for (int tag = 0; tag < SpeciesTagCount; ++tag) { + const std::string label = speciesTagName(tag) + ";state=" + std::to_string(state); + mcProxySpecies[sign]->GetZaxis()->SetBinLabel(tag + SpeciesTagCount * state + 1, label.c_str()); + } + } + mcProxyOtherParents[sign] = add3("hMCProxyOtherParents" + suffix, "Selected Other: retained immediate common-mother class only", + {mcMass, pairPt, AxisSpec{ParentClasses, -0.5, ParentClasses - 0.5, "Retained parent class"}}); + for (int parent = 0; parent < ParentClasses; ++parent) { + mcProxyOtherParents[sign]->GetZaxis()->SetBinLabel(parent + 1, ParentNames[parent]); + } + } + if (lstarEnableProxyPIDProbes.value == 0) { + continue; + } + probePairRegions[sign] = add3("hProxyPIDProbeRegions" + suffix, "Loose matched PID probes;regular multiplicity only;region=dNominal+2*kNominal", + {probeMass, pairPt, AxisSpec{ProbeRegions, -0.5, ProbeRegions - 0.5, "Nominal-selection region"}}); + for (size_t role = 0; role < ProbeRoles.size(); ++role) { + const std::string stem = std::string("hProxyPIDProbe") + ProbeRoles[role]; + const AxisSpec m2 = role == 0 ? AxisSpec{120, -1., 7., "Full-track TOF mass2 (GeV2/c4)"} : AxisSpec{80, -1., 2., "Full-track TOF mass2 (GeV2/c4)"}; + const AxisSpec packed{ProbeTagCount, -0.5, ProbeTagCount - 0.5, "pairBin+4*(momentumBin+9*region)"}; + probeMass2[role][sign] = add3(std::string(stem).append("Mass2").append(suffix), "Loose both-TOF proxy probes;TPC/TOF envelopes;regular multiplicity", {probeMass, m2, packed}); + probeTPC[role][sign] = add3(std::string(stem).append("TPC").append(suffix), "Same loose both-TOF proxy probes;TPC nSigma;regular multiplicity", {probeMass, AxisSpec{60, -ProbeTPCMax, ProbeTPCMax, "TPC nSigma for slot hypothesis"}, packed}); + probeTOF[role][sign] = add3(std::string(stem).append("TOF").append(suffix), "Same loose both-TOF proxy probes;TOF nSigma;regular multiplicity", {probeMass, AxisSpec{80, -ProbeTOFMax, ProbeTOFMax, "TOF nSigma for slot hypothesis"}, packed}); + for (int region = 0; region < ProbeRegions; ++region) { + for (int momentum = 0; momentum < ProbeMomentumBins; ++momentum) { + for (int pair = 0; pair < ProbePairPtBins; ++pair) { + const int tag = pair + ProbePairPtBins * (momentum + ProbeMomentumBins * region); + const std::string label = "pair=" + std::to_string(pair) + ";p=" + std::to_string(momentum) + ";region=" + std::to_string(region); + probeMass2[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); + probeTPC[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); + probeTOF[role][sign]->GetZaxis()->SetBinLabel(tag + 1, label.c_str()); + } + } + } + } + if (doprocessMCReco) { + mcProbeSpecies[sign] = add3("hMCProxyPIDProbeSpecies" + suffix, "Same loose probe pairs;species+51*nominalRegion;regular multiplicity", + {probeMass, pairPt, AxisSpec{SpeciesTagCount * ProbeRegions, -0.5, SpeciesTagCount * ProbeRegions - 0.5, "Species plus nominal region"}}); + for (int region = 0; region < ProbeRegions; ++region) { + for (int tag = 0; tag < SpeciesTagCount; ++tag) { + const std::string label = speciesTagName(tag) + ";region=" + std::to_string(region); + mcProbeSpecies[sign]->GetZaxis()->SetBinLabel(tag + SpeciesTagCount * region + 1, label.c_str()); + } + } + } + } + if (lstarEnableProxyPIDProbes.value != 0) { + probeTrackStatus = histos.add("hProxyPIDProbeTrackStatus", "After quality and loose TPC;slot;stage", HistType::kTH2D, + {AxisSpec{2, -0.5, 1.5, "Slot: d=0,K=1"}, AxisSpec{5, -0.5, 4.5, "Stage"}}, true); + const std::array labels{"loose TPC", "no TOF", "invalid TOF response", "outside loose TOF", "accepted probe track"}; + for (size_t i = 0; i < labels.size(); ++i) { + probeTrackStatus->GetYaxis()->SetBinLabel(i + 1, labels[i]); + } + probeEventScope = histos.add("hProxyPIDProbeEventScope", "Selected events;regular multiplicity flag;events", HistType::kTH1D, {AxisSpec{2, -0.5, 1.5, "0=outside,1=regular [0,100)"}}, true); + } + if (payload > 0) { + LOGF(info, "Proxy diagnostic TH3 content+Sumw2 payload: %.2f MiB per wagon (fixed bins; object/axis overhead extra)", static_cast(payload) / (1024. * 1024.)); + } + } + + void fillMCProxySpecies(ProxyCand const& d, KaonCand const& k, double mass, float pt, float mult) + { + const int tag = speciesPairTag(d.truth, k.truth, d.charge, k.charge); + const int state = static_cast(d.hasTOF) + 2 * static_cast(k.hasTOF) + 4 * static_cast(d.protonTPCCompatible) + 8 * static_cast(regularMultiplicity(mult)); + const size_t sign = d.charge * k.charge < 0 ? 0 : 1; + mcProxySpecies[sign]->Fill(mass, pt, tag + SpeciesTagCount * state); + if (otherSpeciesTag(tag)) { + mcProxyOtherParents[sign]->Fill(mass, pt, retainedParentClass(d.truth, k.truth)); + } + } + + template + void fillProxyPIDProbes(TTracks const& tracks, float mult, int collisionId) + { + // This separate path must not feed nominal candidate vectors or mixing pools. + const bool regular = regularMultiplicity(mult); + probeEventScope->Fill(regular); + if (!regular) { + return; + } + std::array, 2> candidates; + for (auto const& track : tracks) { + if (track.pt() < lstarCutPtMin.value || std::abs(track.eta()) > lstarCutEtaMax.value || !passTrackQuality(track) || track.sign() == 0) { + continue; + } + const std::array tpc{track.tpcNSigmaDe(), track.tpcNSigmaKa()}; + const std::array tof{track.tofNSigmaDe(), track.tofNSigmaKa()}; + const std::array tpcMax{lstarProxyProbeTPCDeMax.value, lstarProxyProbeTPCKaMax.value}; + const std::array tofMax{lstarProxyProbeTOFDeMax.value, lstarProxyProbeTOFKaMax.value}; + for (size_t role = 0; role < ProbeRoles.size(); ++role) { + if (!std::isfinite(tpc[role]) || std::abs(tpc[role]) >= tpcMax[role]) { + continue; + } + probeTrackStatus->Fill(role, 0); + if (!track.hasTOF()) { + probeTrackStatus->Fill(role, 1); + continue; + } + const double beta = track.beta(); + const double momentum = static_cast(track.pt()) * std::cosh(static_cast(track.eta())); + if (!std::isfinite(beta) || beta <= TofBetaMin || beta >= TofBetaMax || !std::isfinite(momentum) || momentum <= 0. || !std::isfinite(tof[role])) { + probeTrackStatus->Fill(role, 2); + continue; + } + if (std::abs(tof[role]) >= tofMax[role]) { + probeTrackStatus->Fill(role, 3); + continue; + } + probeTrackStatus->Fill(role, 4); + PIDProbeCand probe; + probe.px = track.pt() * std::cos(track.phi()); + probe.py = track.pt() * std::sin(track.phi()); + probe.pz = track.pt() * std::sinh(track.eta()); + probe.momentum = momentum; + probe.mass2 = momentum * momentum * (1. / (beta * beta) - 1.); + probe.nsTPC = tpc[role]; + probe.nsTOF = tof[role]; + probe.charge = static_cast(track.sign()); + probe.tid = track.globalIndex(); + if (role == 0) { + const bool passProton = passFinalCandidatePID(track.pt(), track.tpcNSigmaPr(), track.tofNSigmaPr(), true, + lstarCutNsigmaTPCPr.value, lstarCutNsigmaTOFPr.value, lstarPidCircularCutPr.value, lstarPidPtRefPr.value); + probe.nominal = passFinalCandidatePID(track.pt(), tpc[role], tof[role], true, lstarCutNsigmaTPCDe.value, lstarCutNsigmaTOFDe.value, lstarPidCircularCutDe.value, lstarPidPtRefDe.value, true) && + passOptionalDeuteronTOFExtras(track) && (lstarProxyUseProtonPIDAsDeuteron.value == 0 || passProton); + } else { + probe.nominal = passFinalCandidatePID(track.pt(), tpc[role], tof[role], true, lstarCutNsigmaTPCKaon.value, lstarCutNsigmaTOFKaon.value, lstarPidCircularCutKaon.value, lstarPidPtRefKaon.value); + } + if constexpr (IsMC) { + probe.truth = trackTruth(track, collisionId); + } + candidates[role].push_back(std::move(probe)); + } + } + for (auto const& d : candidates[0]) { + for (auto const& k : candidates[1]) { + if (d.tid == k.tid) { + continue; + } + const double mass = invariantMass(ProxyMomentumScale * d.px, ProxyMomentumScale * d.py, ProxyMomentumScale * d.pz, o2::constants::physics::MassProton, + k.px, k.py, k.pz, o2::constants::physics::MassKaonCharged); + const float px = ProxyMomentumScale * d.px + k.px, py = ProxyMomentumScale * d.py + k.py, pz = ProxyMomentumScale * d.pz + k.pz; + const float pt = ptFromPxPy(px, py); + const int pairBin = probePairPtBin(pt); + const float y = rapidityFromMomentumAndMass(px, py, pz, mass); + if (pairBin < 0 || !std::isfinite(mass) || !std::isfinite(y) || std::abs(y) > lstarLambdaAbsYMax.value) { + continue; + } + const size_t sign = d.charge * k.charge < 0 ? 0 : 1; + const int region = static_cast(d.nominal) + 2 * static_cast(k.nominal); + probePairRegions[sign]->Fill(mass, pt, region); + probeMass2[0][sign]->Fill(mass, d.mass2, probeTag(pairBin, d.momentum, region)); + probeMass2[1][sign]->Fill(mass, k.mass2, probeTag(pairBin, k.momentum, region)); + probeTPC[0][sign]->Fill(mass, d.nsTPC, probeTag(pairBin, d.momentum, region)); + probeTPC[1][sign]->Fill(mass, k.nsTPC, probeTag(pairBin, k.momentum, region)); + probeTOF[0][sign]->Fill(mass, d.nsTOF, probeTag(pairBin, d.momentum, region)); + probeTOF[1][sign]->Fill(mass, k.nsTOF, probeTag(pairBin, k.momentum, region)); + if constexpr (IsMC) { + mcProbeSpecies[sign]->Fill(mass, pt, speciesPairTag(d.truth, k.truth, d.charge, k.charge) + SpeciesTagCount * region); + } + } + } + } + + bool mixingEnabled() const + { + return lstarEnableSparse.value != 0 && lstarNoMixedEvents.value > 0; + } + + void bookMC() + { + const std::vector massPt{ + AxisSpec{400, 1.4, 1.8, "Invariant mass (GeV/c^{2})"}, + AxisSpec{100, 0., 10., "Pair p_{T} (GeV/c)"}}; + const std::vector response{ + AxisSpec{100, 0., 10., "Generated pair p_{T} (GeV/c)"}, + AxisSpec{100, 0., 10., "Reconstructed pair p_{T} (GeV/c)"}}; + if (doprocessMCGen) { + histos.add("hMCGenEventSelection", "Generated events;Stage;Events", HistType::kTH1D, + {AxisSpec{3, 0.5, 3.5, "1=all, 2=vertex, 3=vertex+truth INEL>0"}}); + const std::array names{ + "hMCGenPKUnlike", "hMCGenPKLike", "hMCGenProxyUnlike", "hMCGenProxyLike", "hMCGenPKTrueLambdaStar"}; + for (size_t i = 0; i < names.size(); ++i) { + mcGenSpectra[i] = histos.add(names[i], "Generated fiducial pairs", HistType::kTH2D, massPt, true); + } + } + if (doprocessMCReco) { + const std::array categories{"TrueD", "ProtonMisID", "Other", "Unmatched"}; + for (size_t i = 0; i < categories.size(); ++i) { + for (size_t sign = 0; sign < mcRecoProxy[i].size(); ++sign) { + const std::string name = std::string("hMCRecoProxy") + categories[i] + (sign == 0 ? "Unlike" : "Like"); + mcRecoProxy[i][sign] = histos.add(name.c_str(), "Reconstructed proxy truth component", HistType::kTH2D, massPt, true); + } + } + mcRecoPKTrue = histos.add("hMCRecoPKTrueLambdaStar", "Selected true common-parent pK", HistType::kTH2D, massPt, true); + mcPKResponse = histos.add("hMCRecoPKPtResponse", "Selected true common-parent pK;Generated pair pT;Reconstructed pair pT", HistType::kTH2D, response, true); + mcProxyResponse = histos.add("hMCRecoProxyPtResponse", "Selected true primary dK pairs;Generated proxy pair pT;Reconstructed proxy pair pT", HistType::kTH2D, response, true); + } + } + + template + static TruthInfo particleTruth(TParticle const& particle, bool retainDiagnosticMothers = false) + { + TruthInfo info; + info.id = particle.globalIndex(); + info.collisionId = particle.mcCollisionId(); + info.pdg = particle.pdgCode(); + info.px = particle.px(); + info.py = particle.py(); + info.pz = particle.pz(); + info.primary = particle.isPhysicalPrimary(); + for (auto const& mother : particle.template mothers_as()) { + if (retainDiagnosticMothers && mother.mcCollisionId() == info.collisionId) { + info.diagnosticMothers.push_back({mother.globalIndex(), mother.pdgCode()}); + } + if (std::abs(mother.pdgCode()) == PdgLambdaStar && mother.mcCollisionId() == info.collisionId) { + info.lambdaMothers.push_back(mother.globalIndex()); + } + } + return info; + } + + template + static TruthInfo trackTruth(TTrack const& track, int collisionId, bool retainDiagnosticMothers = false) + { + if (collisionId < 0 || !track.has_mcParticle()) { + return {}; + } + auto particle = track.template mcParticle_as(); + if (particle.mcCollisionId() != collisionId) { + return {}; // pileup or wrong-collision labels do not enter truth components + } + return particleTruth(particle, retainDiagnosticMothers); + } + + static bool commonLambdaStar(TruthInfo const& proton, TruthInfo const& kaon) + { + if (std::abs(proton.pdg) != PdgProton || std::abs(kaon.pdg) != PdgKaon || + proton.pdg * kaon.pdg >= 0 || proton.id == kaon.id || + proton.collisionId != kaon.collisionId) { + return false; + } + for (auto const& id : proton.lambdaMothers) { + if (std::find(kaon.lambdaMothers.begin(), kaon.lambdaMothers.end(), id) != kaon.lambdaMothers.end()) { + return true; + } + } + return false; + } + + template + void fillRecoMC(TCandidate const& candidate, KaonCand const& kaon, double mass, float pt, bool proxy) + { + const auto& a = candidate.truth; + const auto& b = kaon.truth; + if (!proxy) { + if (commonLambdaStar(a, b) && chargeFromPdg(a.pdg) == candidate.charge && chargeFromPdg(b.pdg) == kaon.charge) { + mcRecoPKTrue->Fill(mass, pt); + mcPKResponse->Fill(ptFromPxPy(a.px + b.px, a.py + b.py), pt); + } + return; + } + size_t category = 2; // all other labelled species, secondaries included + if (a.id < 0 || b.id < 0) { + category = 3; + } else if (std::abs(b.pdg) == PdgKaon && chargeFromPdg(b.pdg) == kaon.charge && chargeFromPdg(a.pdg) == candidate.charge) { + if (std::abs(a.pdg) == PdgDeuteron) { + category = 0; + } else if (std::abs(a.pdg) == PdgProton) { + category = 1; + } + } + const size_t sign = candidate.charge * kaon.charge < 0 ? 0 : 1; + mcRecoProxy[category][sign]->Fill(mass, pt); + if (category == 0 && a.primary && b.primary && a.id != b.id) { + mcProxyResponse->Fill(ptFromPxPy(ProxyMomentumScale * a.px + b.px, ProxyMomentumScale * a.py + b.py), pt); + } + // TrueD means true d + true K, not a truth tag of resonance-fed coalescence. + } + + template + void fillGenerated(TCollision const& collision, TParticles const& particles) + { + histos.fill(HIST("hMCGenEventSelection"), 1.); + if (!(std::abs(collision.posZ()) < lstarCutVertex.value)) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 2.); + // Truth INEL>0 uses charged physical primaries in |eta|<1, with no pT cut. + // It is independent of detector trigger/vertex reconstruction efficiency. + bool inel = false; + for (auto const& particle : particles) { + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) < 1.f && chargeFromPdg(particle.pdgCode()) != 0) { + inel = true; + break; + } + } + if (lstarRequireINELgt0.value != 0 && !inel) { + return; + } + histos.fill(HIST("hMCGenEventSelection"), 3.); + std::vector protons, kaons, deuterons; + for (auto const& particle : particles) { + // Use the DATA daughter kinematic acceptance for generated pair spectra. + // lstarCutMC* remains specific to the optional legacy track-QA process. + if (!(particle.pt() > lstarCutPtMin.value) || !(std::abs(particle.eta()) < lstarCutEtaMax.value) || + (lstarMCPrimaryOnly.value != 0 && !particle.isPhysicalPrimary())) { + continue; + } + switch (std::abs(particle.pdgCode())) { + case PdgProton: + protons.push_back(particleTruth(particle)); + break; + case PdgKaon: + kaons.push_back(particleTruth(particle)); + break; + case PdgDeuteron: + deuterons.push_back(particleTruth(particle)); + break; + default: + break; + } + } + auto fillPairs = [&](auto const& candidates, bool proxy) { + const float scale = proxy ? ProxyMomentumScale : 1.f; + for (auto const& a : candidates) { + for (auto const& b : kaons) { + if (a.id == b.id) { + continue; + } + const double mass = invariantMass(scale * a.px, scale * a.py, scale * a.pz, o2::constants::physics::MassProton, + b.px, b.py, b.pz, o2::constants::physics::MassKaonCharged); + const float px = scale * a.px + b.px, py = scale * a.py + b.py, pz = scale * a.pz + b.pz; + if (std::abs(rapidityFromMomentumAndMass(px, py, pz, mass)) > lstarLambdaAbsYMax.value) { + continue; + } + const float pt = ptFromPxPy(px, py); + const size_t sign = chargeFromPdg(a.pdg) * chargeFromPdg(b.pdg) < 0 ? 0 : 1; + mcGenSpectra[(proxy ? 2 : 0) + sign]->Fill(mass, pt); + if (!proxy && commonLambdaStar(a, b)) { + mcGenSpectra[4]->Fill(mass, pt); + } + } + } + }; + fillPairs(protons, false); + fillPairs(deuterons, true); + } + + // AO2D-MC QA: truth primaries + reco-to-MC matching sanity plots + void processMCQA(FilteredCollisionsMC::iterator const& collision, + FilteredTracksMC const& tracks, + aod::McParticles const& mcParticles) + { + if (lstarFillMCTruth.value == 0) { + return; + } + + // Same basic event selection as AO2D reco + if (!keepCollisionAO2D(collision)) { + return; + } + if (std::abs(collision.posZ()) > lstarCutVertex.value) { + return; + } + + // Require a linked MC collision + if constexpr (requires { collision.has_mcCollision(); }) { + if (!collision.has_mcCollision()) { + return; + } + } + + int mcCollIdx = -1; + if constexpr (requires { collision.mcCollisionId(); }) { + mcCollIdx = collision.mcCollisionId(); + } else if constexpr (requires { collision.mcCollision().globalIndex(); }) { + mcCollIdx = collision.mcCollision().globalIndex(); + } + if (mcCollIdx < 0) { + return; + } + + // --- Truth QA: charged physical primaries --- + auto truth = mcParticles.sliceBy(mcParticlesPerCollision, mcCollIdx); + for (auto const& mcPart : truth) { + if (!mcPart.isPhysicalPrimary()) { + continue; + } + if (mcPart.pt() < lstarCutMCPtMin.value || std::abs(mcPart.eta()) > lstarCutMCEtaMax.value) { + continue; + } + if (chargeFromPdg(mcPart.pdgCode()) == 0) { + continue; + } + histos.fill(HIST("hMcPrimariesPtEta"), mcPart.pt(), mcPart.eta()); + } + + // --- Reco->MC matching QA --- + const auto collIdx = collision.globalIndex(); + for (auto const& trk : tracks) { + if (trk.collisionId() != collIdx) { + continue; + } + if (!passTrackQuality(trk)) { + continue; + } + + if constexpr (requires { trk.has_mcParticle(); trk.mcParticle(); }) { + if (trk.has_mcParticle()) { + const auto mcPart = trk.mcParticle(); + histos.fill(HIST("hRecoMatchedPdg"), mcPart.pdgCode()); + } else { + histos.fill(HIST("hRecoFakePtEta"), trk.pt(), trk.eta()); + } + } + } + } + + PROCESS_SWITCH(Lambdastarproxy, processMCQA, + "AO2D-MC: fill truth and reco-matching QA histograms", false); + + // Helper: fill TPC dE/dx vs total momentum if TPC signal is available + template + void fillTPCdEdxVsPIfAvailable(const TTrack& trk) + { + if (lstarEnablePidQA.value == 0) { + return; + } + // aod::TracksExtra provides tpcSignal(); keep the constexpr-guard for robustness + if constexpr (requires { trk.tpcSignal(); }) { + const float p = trk.p(); + histos.fill(HIST("hTPCdEdxVsP"), p, trk.tpcSignal()); + } + } + + // Helper: fill TOF beta vs total momentum if beta is available (and looks valid) + template + void fillTOFBetaVsPIfAvailable(const TTrack& trk) + { + if (lstarEnablePidQA.value == 0) { + return; + } + if constexpr (requires { trk.beta(); }) { + bool hasTof = true; + if constexpr (requires { trk.hasTOF(); }) { + hasTof = trk.hasTOF(); + } + const float beta = trk.beta(); + // Guard against default/invalid values for tracks without TOF match + if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsP"), trk.p(), beta); + } + } + } + + // --- Per-species PID QA helpers --- + template + static bool hasTOFMatch(const TTrack& trk) + { + if constexpr (requires { trk.hasTOF(); }) { + return trk.hasTOF(); + } + return true; // fallback: if column not present, assume available + } + + template + bool passOptionalDeuteronTOFExtras(const TTrack& trk) const + { + const bool needTOF = + (lstarRequireTOFMatchDe.value != 0) || + (lstarEnableBetaCutDe.value != 0) || + (lstarEnableExpSignalTOFDe.value != 0 && lstarTOFExpSignalDiffDeMax.value > 0.f); + + if (needTOF) { + if constexpr (requires { trk.hasTOF(); }) { + if (!trk.hasTOF()) { + return false; + } + } else { + return false; + } + } + + if (lstarEnableBetaCutDe.value != 0) { + if constexpr (requires { trk.beta(); }) { + if (trk.beta() <= lstarBetaCutDe.value) { + return false; + } + } else { + return false; + } + } + + if (lstarEnableExpSignalTOFDe.value != 0 && + lstarTOFExpSignalDiffDeMax.value > 0.f) { + if constexpr (requires { trk.tofExpSignalDiffDe(); }) { + if (std::abs(trk.tofExpSignalDiffDe()) > lstarTOFExpSignalDiffDeMax.value) { + return false; + } + } else { + return false; + } + } + + return true; + } + + bool passFinalCandidatePID(float pt, float nsTPC, float nsTOF, bool hasTof, + float tpcCut, float tofCut, float circularCut, + float ptRef, bool isDeuteron = false) const + { + // Strategy 1: analysis-note style circular TPC+TOF cut + if (lstarPidStrategy.value == PidStrategyCircularTPCAndTOF) { + if (pt < ptRef) { + return std::abs(nsTPC) < tpcCut; + } + + if (!hasTof) { + return false; + } + + return std::sqrt(nsTPC * nsTPC + nsTOF * nsTOF) < circularCut; + } + + if (lstarPidStrategy.value == PidStrategyNucleiDeuteronTPC) { + // For deuterons, use TPC nσ as the main hard PID selection. + // If the optional deuteron TOF nσ cut is enabled, apply it only when + // the track has a valid TOF match. Tracks without TOF are kept with + // the TPC-only decision, avoiding an additional TOF-matching efficiency loss. + if (isDeuteron) { + if (std::abs(nsTPC) >= tpcCut) { + return false; + } + if (lstarEnableTOFNsigmaCutDe.value != 0 && hasTof) { + return std::abs(nsTOF) < tofCut; + } + return true; + } + + // For kaons/protons, use the Lambda(1520)-like pT-ref circular TPC+TOF logic. + if (pt < ptRef) { + return std::abs(nsTPC) < tpcCut; + } + + if (!hasTof) { + return false; + } + + return std::sqrt(nsTPC * nsTPC + nsTOF * nsTOF) < circularCut; + } + + // Strategy 0: pT-ref dependent rectangular TPC+TOF cut. + // Below pTref use TPC only; above pTref require TOF and apply both TPC and TOF cuts. + if (pt < ptRef) { + return std::abs(nsTPC) < tpcCut; + } + if (!hasTof) { + return false; + } + return (std::abs(nsTPC) < tpcCut) && (std::abs(nsTOF) < tofCut); + } + + // Return: 0=#pi, 1=K, 2=p, 3=d, -1=unclassified + template + int classifyPidSpecies(const TTrack& trk) + { + const bool hasTof = hasTOFMatch(trk); + + auto score = [hasTof](float nsTPC, float nsTOF) { + return std::abs(nsTPC) + (hasTof ? std::abs(nsTOF) : 0.f); + }; + + float bestScore = 1e9f; + int best = -1; + + // pion + { + const float nsTPC = trk.tpcNSigmaPi(); + const float nsTOF = trk.tofNSigmaPi(); + const bool pass = (std::abs(nsTPC) < lstarCutNsigmaTPCPi.value) && (!hasTof || (std::abs(nsTOF) < lstarCutNsigmaTOFPi.value)); + if (pass) { + const float sc = score(nsTPC, nsTOF); + if (sc < bestScore) { + bestScore = sc; + best = 0; + } + } + } + + // kaon + { + const float nsTPC = trk.tpcNSigmaKa(); + const float nsTOF = trk.tofNSigmaKa(); + const bool pass = (std::abs(nsTPC) < lstarCutNsigmaTPCKaon.value) && (!hasTof || (std::abs(nsTOF) < lstarCutNsigmaTOFKaon.value)); + if (pass) { + const float sc = score(nsTPC, nsTOF); + if (sc < bestScore) { + bestScore = sc; + best = 1; + } + } + } + + // proton + { + const float nsTPC = trk.tpcNSigmaPr(); + const float nsTOF = trk.tofNSigmaPr(); + const bool pass = (std::abs(nsTPC) < lstarCutNsigmaTPCPr.value) && (!hasTof || (std::abs(nsTOF) < lstarCutNsigmaTOFPr.value)); + if (pass) { + const float sc = score(nsTPC, nsTOF); + if (sc < bestScore) { + bestScore = sc; + best = 2; + } + } + } + + // deuteron + { + const float nsTPC = trk.tpcNSigmaDe(); + const float nsTOF = trk.tofNSigmaDe(); + const bool pass = (std::abs(nsTPC) < lstarCutNsigmaTPCDe.value) && (!hasTof || (std::abs(nsTOF) < lstarCutNsigmaTOFDe.value)); + if (pass) { + const float sc = score(nsTPC, nsTOF); + if (sc < bestScore) { + bestScore = sc; + best = 3; + } + } + } + + return best; + } + + // Helper to compute invariant mass from two 3-momenta and masses + static double invariantMass(float px1, float py1, float pz1, double m1, + float px2, float py2, float pz2, double m2) + { + return RecoDecay::m(std::array{std::array{px1, py1, pz1}, + std::array{px2, py2, pz2}}, + std::array{m1, m2}); + } + + template + void processReco(TCollision const& collision, TTracks const& tracks) + { + histos.fill(HIST("hEventSelection"), 1.); + // Use unfiltered collisions here so the first bin includes vertex rejects. + // Preserve the strict vertex acceptance of collisionZVtxFilter; the MC QA + // process still uses that framework filter and does not fill this counter. + if (!(std::abs(collision.posZ()) < lstarCutVertex.value)) { + return; + } + // Event selection (cfgTrigger) -- AO2D only + if (!keepCollisionAO2D(collision)) { + return; + } + histos.fill(HIST("hEventSelection"), 2.); + // physics masses (GeV/c^2) + constexpr double MassProton = o2::constants::physics::MassProton; + constexpr double MassKaonCharged = o2::constants::physics::MassKaonCharged; + + std::vector kaonCands; + std::vector proxyCands; + std::vector protonCands; + kaonCands.reserve(128); + proxyCands.reserve(32); + protonCands.reserve(128); + + float eventMultFallback = 0.f; // fallback mixing variable: number of selected charged tracks (after quality cuts) + + // Inclusive track loop before final candidate PID cuts. + // It counts selected charged tracks for fallback multiplicity and when enabled, + // fills inclusive PID QA after track-quality cuts but before final K/p/d PID cuts. + for (auto const& trk : tracks) { + if (trk.pt() < lstarCutPtMin.value || std::abs(trk.eta()) > lstarCutEtaMax.value) { + continue; + } + if (!passTrackQuality(trk)) { + continue; + } + if (trk.sign() == 0) { + continue; + } + eventMultFallback += 1.f; + if (lstarEnablePidQA.value == 0) { + continue; + } + // Inclusive PID QA + fillTPCdEdxVsPIfAvailable(trk); + fillTOFBetaVsPIfAvailable(trk); + + const double pForPidQA = static_cast(trk.pt()) * std::cosh(static_cast(trk.eta())); + const bool hasTofForPidQA = hasTOFMatch(trk); + + histos.fill(HIST("hHasTOFVsP"), pForPidQA, hasTofForPidQA ? 1.0 : 0.0); + histos.fill(HIST("hHasTOFVsPt"), trk.pt(), hasTofForPidQA ? 1.0 : 0.0); + + // Per-species PID-QA (tagged) histograms + const int sp = classifyPidSpecies(trk); + switch (sp) { + case 0: { // pion + histos.fill(HIST("hNsigmaTPCPionTaggedVsP"), pForPidQA, trk.tpcNSigmaPi()); + histos.fill(HIST("hNsigmaTOFPionTaggedVsP"), pForPidQA, trk.tofNSigmaPi()); + if constexpr (requires { trk.tpcSignal(); }) { + histos.fill(HIST("hTPCdEdxVsP_Pi"), trk.p(), trk.tpcSignal()); + } + if constexpr (requires { trk.beta(); }) { + const bool hasTof = hasTOFMatch(trk); + const float beta = trk.beta(); + if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsP_Pi"), trk.p(), beta); + } + } + break; + } + case 1: { // kaon + histos.fill(HIST("hNsigmaTPCKaonTaggedVsP"), pForPidQA, trk.tpcNSigmaKa()); + histos.fill(HIST("hNsigmaTOFKaonTaggedVsP"), pForPidQA, trk.tofNSigmaKa()); + if constexpr (requires { trk.tpcSignal(); }) { + histos.fill(HIST("hTPCdEdxVsP_K"), trk.p(), trk.tpcSignal()); + } + if constexpr (requires { trk.beta(); }) { + const bool hasTof = hasTOFMatch(trk); + const float beta = trk.beta(); + if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsP_K"), trk.p(), beta); + } + } + break; + } + case 2: { // proton + histos.fill(HIST("hNsigmaTPCProtonTaggedVsP"), pForPidQA, trk.tpcNSigmaPr()); + histos.fill(HIST("hNsigmaTOFProtonTaggedVsP"), pForPidQA, trk.tofNSigmaPr()); + if constexpr (requires { trk.tpcSignal(); }) { + histos.fill(HIST("hTPCdEdxVsP_P"), trk.p(), trk.tpcSignal()); + } + if constexpr (requires { trk.beta(); }) { + const bool hasTof = hasTOFMatch(trk); + const float beta = trk.beta(); + if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsP_P"), trk.p(), beta); + } + } + break; + } + case 3: { // deuteron + histos.fill(HIST("hNsigmaTPCDeuteronTaggedVsP"), pForPidQA, trk.tpcNSigmaDe()); + histos.fill(HIST("hNsigmaTOFDeuteronTaggedVsP"), pForPidQA, trk.tofNSigmaDe()); + if constexpr (requires { trk.tpcSignal(); }) { + histos.fill(HIST("hTPCdEdxVsP_D"), trk.p(), trk.tpcSignal()); + } + if constexpr (requires { trk.beta(); }) { + const bool hasTof = hasTOFMatch(trk); + const float beta = trk.beta(); + if (hasTof && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsP_D"), trk.p(), beta); + } + } + break; + } + default: + break; + } + } + + // Compute event multiplicity (FT0M or fallback) + const float eventMult = eventMultiplicityFT0MOrFallback(collision, eventMultFallback); + if (lstarEnableProxyPIDProbes.value != 0) { + if constexpr (IsMC) { + fillProxyPIDProbes(tracks, eventMult, collision.mcCollisionId()); + } else { + fillProxyPIDProbes(tracks, eventMult, -1); + } + } + + // Deuteron candidates -> proton-proxy candidates + for (auto const& trkD : tracks) { + if (trkD.pt() < lstarCutPtMin.value || std::abs(trkD.eta()) > lstarCutEtaMax.value) { + continue; + } + if (!passTrackQuality(trkD)) { + continue; + } + if (trkD.sign() == 0) { + continue; + } + + // Deuteron kinematics needed before PID because the PID strategy can depend on pT + const float ptD = trkD.pt(); + const float etaD = trkD.eta(); + const float phiD = trkD.phi(); + + // PID for proxy candidates. + // Normal mode: use the standard deuteron PID and build p_proxy = p_d / 2. + // Closure/control mode: select the subset of standard deuteron-proxy candidates + // that are also proton-like. This tests proton contamination inside the deuteron + // selection, not all proton candidates. + const bool useProtonAsProxy = (lstarProxyUseProtonPIDAsDeuteron.value != 0); + + const float nsTPCDe = trkD.tpcNSigmaDe(); + const float nsTOFDe = trkD.tofNSigmaDe(); + // These controls require the actual match flag, never an inferred PID value. + const bool hasTofDe = trkD.hasTOF(); + + if (!passOptionalDeuteronTOFExtras(trkD)) { + continue; + } + + const bool passesDeuteronSelection = passFinalCandidatePID(ptD, nsTPCDe, nsTOFDe, hasTofDe, + lstarCutNsigmaTPCDe.value, lstarCutNsigmaTOFDe.value, + lstarPidCircularCutDe.value, lstarPidPtRefDe.value, + true); + if (!passesDeuteronSelection) { + continue; + } + + const float nsTPCPrAsProxy = + trkD.tpcNSigmaPr(); + + const float nsTOFPrAsProxy = + trkD.tofNSigmaPr(); + + const bool passesProtonSelection = + passFinalCandidatePID( + ptD, + nsTPCPrAsProxy, + nsTOFPrAsProxy, + hasTofDe, + lstarCutNsigmaTPCPr.value, + lstarCutNsigmaTOFPr.value, + lstarPidCircularCutPr.value, + lstarPidPtRefPr.value, + false); + + if (useProtonAsProxy && + !passesProtonSelection) { + continue; + } + + const double pD = static_cast(ptD) * std::cosh(static_cast(etaD)); + + // Candidate QA after final deuteron PID cut + if (lstarEnablePidQA.value != 0) { + histos.fill(HIST("hDeuteronProxyPt"), ptD); + histos.fill(HIST("hDeuteronProxyEta"), etaD); + histos.fill(HIST("hDeuteronProxyPhi"), phiD); + histos.fill(HIST("hNsigmaTPCDeuteron"), nsTPCDe); + histos.fill(HIST("hNsigmaTOFDeuteron"), nsTOFDe); + histos.fill(HIST("hNsigmaTPCDeuteronVsP"), pD, nsTPCDe); + histos.fill(HIST("hNsigmaTOFDeuteronVsP"), pD, nsTOFDe); + histos.fill(HIST("hNsigmaTPCDeuteronVsPt"), ptD, nsTPCDe); + histos.fill(HIST("hNsigmaTOFDeuteronVsPt"), ptD, nsTOFDe); + histos.fill( + HIST( + "hNsigmaTPCProtonForSelectedDeuteronVsPt"), + ptD, + nsTPCPrAsProxy); + + histos.fill( + HIST( + "hNsigmaTOFProtonForSelectedDeuteronVsPt"), + ptD, + nsTOFPrAsProxy); + + histos.fill( + HIST( + "hTPCNsigmaDeVsPrForSelectedDeuteron"), + nsTPCDe, + nsTPCPrAsProxy); + + if (hasTofDe) { + histos.fill( + HIST( + "hTOFNsigmaDeVsPrForSelectedDeuteron"), + nsTOFDe, + nsTOFPrAsProxy); + histos.fill(HIST("hTPCvsTOFNsigma_D"), nsTPCDe, nsTOFDe); + } + if constexpr (requires { trkD.beta(); }) { + const float beta = trkD.beta(); + if (hasTofDe && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsPt_D"), ptD, beta); + } + } + if constexpr (requires { trkD.dcaXY(); trkD.dcaZ(); }) { + histos.fill(HIST("hDCAxyVsPt_D"), ptD, trkD.dcaXY()); + histos.fill(HIST("hDCAzVsPt_D"), ptD, trkD.dcaZ()); + } + } + + // build proton-proxy momentum from deuteron: p_p ≈ p_d / 2 + const float pxProxy = ProxyMomentumScale * ptD * std::cos(phiD); + const float pyProxy = ProxyMomentumScale * ptD * std::sin(phiD); + const float pzProxy = ProxyMomentumScale * ptD * std::sinh(etaD); + + // Full measured momentum of the same d-selected track. + // This will later be interpreted under the proton mass hypothesis. + const float pxFull = + ptD * std::cos(phiD); + + const float pyFull = + ptD * std::sin(phiD); + + const float pzFull = + ptD * std::sinh(etaD); + + if (lstarEnableSparse.value != 0 && lstarEnableProxyControls.value != 0) { + histos.fill(HIST("hLambdaStarProxySelectedTrackControlSparse"), ptD, hasTofDe, + passesProtonSelection, std::abs(nsTPCPrAsProxy) < lstarCutNsigmaTPCPr.value); + } + + proxyCands.push_back( + ProxyCand{ + .px = pxProxy, + .py = pyProxy, + .pz = pzProxy, + + .pxFull = pxFull, + .pyFull = pyFull, + .pzFull = pzFull, + + .protonLike = + passesProtonSelection, + .hasTOF = hasTofDe, + .protonTPCCompatible = std::abs(nsTPCPrAsProxy) < lstarCutNsigmaTPCPr.value, + + .charge = + static_cast( + trkD.sign()), + + .tid = static_cast(trkD.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + proxyCands.back().truth = trackTruth(trkD, collision.mcCollisionId(), lstarEnableMCProxySpecies.value != 0); + } + } + + // Proton candidates (for genuine pK #Lambda^{*} reconstruction) + for (auto const& trkP : tracks) { + if (trkP.pt() < lstarCutPtMin.value || std::abs(trkP.eta()) > lstarCutEtaMax.value) { + continue; + } + if (!passTrackQuality(trkP)) { + continue; + } + if (trkP.sign() == 0) { + continue; + } + + const float ptP = trkP.pt(); + const float etaP = trkP.eta(); + const float phiP = trkP.phi(); + + const float nsTPCPr = trkP.tpcNSigmaPr(); + const float nsTOFPr = trkP.tofNSigmaPr(); + const bool hasTofPr = hasTOFMatch(trkP); + const bool isProton = passFinalCandidatePID(ptP, + nsTPCPr, + nsTOFPr, + hasTofPr, + lstarCutNsigmaTPCPr.value, + lstarCutNsigmaTOFPr.value, + lstarPidCircularCutPr.value, + lstarPidPtRefPr.value, false); + if (!isProton) { + continue; + } + + const double pP = static_cast(ptP) * std::cosh(static_cast(etaP)); + + if (lstarEnablePidQA.value != 0) { + histos.fill(HIST("hNsigmaTPCProtonVsP"), pP, nsTPCPr); + histos.fill(HIST("hNsigmaTOFProtonVsP"), pP, nsTOFPr); + histos.fill(HIST("hNsigmaTPCProtonVsPt"), ptP, nsTPCPr); + histos.fill(HIST("hNsigmaTOFProtonVsPt"), ptP, nsTOFPr); + if (hasTofPr) { + histos.fill(HIST("hTPCvsTOFNsigma_P"), nsTPCPr, nsTOFPr); + } + if constexpr (requires { trkP.beta(); }) { + const float beta = trkP.beta(); + if (hasTofPr && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsPt_P"), ptP, beta); + } + } + if constexpr (requires { trkP.dcaXY(); trkP.dcaZ(); }) { + histos.fill(HIST("hDCAxyVsPt_P"), ptP, trkP.dcaXY()); + histos.fill(HIST("hDCAzVsPt_P"), ptP, trkP.dcaZ()); + } + } + + const float pxP = ptP * std::cos(phiP); + const float pyP = ptP * std::sin(phiP); + const float pzP = ptP * std::sinh(etaP); + + protonCands.push_back(ProtonCand{ + .px = pxP, + .py = pyP, + .pz = pzP, + .charge = static_cast(trkP.sign()), + .tid = static_cast(trkP.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + protonCands.back().truth = trackTruth(trkP, collision.mcCollisionId()); + } + } + + // Kaon candidates + for (auto const& trkK : tracks) { + if (trkK.pt() < lstarCutPtMin.value || std::abs(trkK.eta()) > lstarCutEtaMax.value) { + continue; + } + if (!passTrackQuality(trkK)) { + continue; + } + if (trkK.sign() == 0) { + continue; + } + + // Kaon kinematics needed before PID because the PID strategy can depend on pT + const float ptK = trkK.pt(); + const float etaK = trkK.eta(); + const float phiK = trkK.phi(); + + // PID for kaon candidates + const float nsTPCK = trkK.tpcNSigmaKa(); + const float nsTOFK = trkK.tofNSigmaKa(); + const bool hasTofK = hasTOFMatch(trkK); + const bool isKaon = passFinalCandidatePID(ptK, + nsTPCK, + nsTOFK, + hasTofK, + lstarCutNsigmaTPCKaon.value, + lstarCutNsigmaTOFKaon.value, + lstarPidCircularCutKaon.value, + lstarPidPtRefKaon.value, false); + if (!isKaon) { + continue; + } + + const double pK = static_cast(ptK) * std::cosh(static_cast(etaK)); + + // Kaon QA + if (lstarEnablePidQA.value != 0) { + histos.fill(HIST("hKaonPt"), ptK); + histos.fill(HIST("hKaonEta"), etaK); + histos.fill(HIST("hKaonPhi"), phiK); + histos.fill(HIST("hNsigmaTPCKaon"), nsTPCK); + histos.fill(HIST("hNsigmaTOFKaon"), nsTOFK); + histos.fill(HIST("hNsigmaTPCKaonVsP"), pK, nsTPCK); + histos.fill(HIST("hNsigmaTOFKaonVsP"), pK, nsTOFK); + histos.fill(HIST("hNsigmaTPCKaonVsPt"), ptK, nsTPCK); + histos.fill(HIST("hNsigmaTOFKaonVsPt"), ptK, nsTOFK); + if (hasTofK) { + histos.fill(HIST("hTPCvsTOFNsigma_K"), nsTPCK, nsTOFK); + } + if constexpr (requires { trkK.beta(); }) { + const float beta = trkK.beta(); + if (hasTofK && beta > TofBetaMin && beta < TofBetaMax) { + histos.fill(HIST("hTOFBetaVsPt_K"), ptK, beta); + } + } + if constexpr (requires { trkK.dcaXY(); trkK.dcaZ(); }) { + histos.fill(HIST("hDCAxyVsPt_K"), ptK, trkK.dcaXY()); + histos.fill(HIST("hDCAzVsPt_K"), ptK, trkK.dcaZ()); + } + } + + const float pxK = ptK * std::cos(phiK); + const float pyK = ptK * std::sin(phiK); + const float pzK = ptK * std::sinh(etaK); + + kaonCands.push_back(KaonCand{ + .px = pxK, + .py = pyK, + .pz = pzK, + .hasTOF = hasTofK, + .charge = static_cast(trkK.sign()), + .tid = static_cast(trkK.globalIndex()), + .truth = {}}); + if constexpr (IsMC) { + kaonCands.back().truth = trackTruth(trkK, collision.mcCollisionId(), lstarEnableMCProxySpecies.value != 0); + } + } + + if (kaonCands.empty()) { + // Still update mixing buffer so that later events can mix with this one. + // The pK mixed-event background needs stored protons, while the proxy background + // needs stored proxy candidates. Therefore, do not require proxy candidates here. + if (mixingEnabled()) { + LStarMixEventEntry entry; + entry.mult = eventMult; + entry.zvtx = collision.posZ(); + entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); + + mLStarMixEvents.push_front(std::move(entry)); + if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { + mLStarMixEvents.pop_back(); + } + } + return; + } + + const bool hasProtonCandidates = !protonCands.empty(); + const bool hasProxyCandidates = !proxyCands.empty(); + + // --- SAME-EVENT: genuine pK #Lambda^{*} candidates --- + if (hasProtonCandidates) { + for (auto const& pr : protonCands) { + for (auto const& k : kaonCands) { + if (pr.tid == k.tid) { + continue; + } + const double mass = invariantMass(pr.px, pr.py, pr.pz, MassProton, + k.px, k.py, k.pz, MassKaonCharged); + + const float pxTot = pr.px + k.px; + const float pyTot = pr.py + k.py; + const float pzTot = pr.pz + k.pz; + const float ptPair = ptFromPxPy(pxTot, pyTot); + + const float yPair = rapidityFromMomentumAndMass(pxTot, pyTot, pzTot, mass); + + if (std::abs(yPair) > lstarLambdaAbsYMax.value) { + continue; + } + + if constexpr (IsMC) { + fillRecoMC(pr, k, mass, ptPair, false); + } + const bool unlikeSignPK = (pr.charge * k.charge) < 0; + if (unlikeSignPK) { + histos.fill(HIST("hInvMassPKUnlike"), mass); + if (lstarEnableSparse.value != 0) { + histos.fill(HIST("hLambdaStarPKUnlikeSparse"), mass, ptPair, eventMult); + } + } else { + histos.fill(HIST("hInvMassPKLike"), mass); + if (lstarEnableSparse.value != 0) { + histos.fill(HIST("hLambdaStarPKLikeSparse"), mass, ptPair, eventMult); + } + } + } + } + } + + // --- SAME-EVENT: proxy (d/2) + K --- + if (hasProxyCandidates) { + for (auto const& pr : proxyCands) { + for (auto const& k : kaonCands) { + if (pr.tid == k.tid) { + continue; // sanity check: should never match, but just in case of bug in candidate-building logic + } + const double mass = invariantMass(pr.px, pr.py, pr.pz, MassProton, k.px, k.py, k.pz, MassKaonCharged); + // Same d-selected track, but now use its FULL measured + // momentum and assign the proton mass. + // + // No proton PID requirement is involved. + const double massFullPK = + invariantMass( + pr.pxFull, + pr.pyFull, + pr.pzFull, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTotFullPK = + pr.pxFull + k.px; + + const float pyTotFullPK = + pr.pyFull + k.py; + + const float pzTotFullPK = + pr.pzFull + k.pz; + + const float yFullPK = + rapidityFromMomentumAndMass( + pxTotFullPK, + pyTotFullPK, + pzTotFullPK, + massFullPK); + + const float pxTot = pr.px + k.px; + const float pyTot = pr.py + k.py; + const float pzTot = pr.pz + k.pz; + const float ptPair = ptFromPxPy(pxTot, pyTot); + + const float yProxy = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); + + if (std::abs(yProxy) > + lstarLambdaAbsYMax.value) { + continue; + } + + if constexpr (IsMC) { + fillRecoMC(pr, k, mass, ptPair, true); + if (lstarEnableMCProxySpecies.value != 0) { + fillMCProxySpecies(pr, k, mass, ptPair, eventMult); + } + } + const bool unlikeSignProxy = + (pr.charge * k.charge) < 0; + + // Inclusive invariant-mass spectrum for the #Lambda^{*} proxy (d/2 + K) + histos.fill(HIST("hDeuteronProxyMass"), mass); + if (lstarEnableSparse.value != 0) { + if (lstarEnableProxyControls.value != 0) { + const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; + if (unlikeSignProxy) { + histos.fill(HIST("hLambdaStarProxyControlUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKUnlikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } + } else { + histos.fill(HIST("hLambdaStarProxyControlLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKLikeSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } + } + } + + if (unlikeSignProxy) { + + histos.fill( + HIST( + "hLambdaStarProxySparse"), + mass, + ptPair, + eventMult); + + } else { + + histos.fill( + HIST( + "hLambdaStarProxyLikeSparse"), + mass, + ptPair, + eventMult); + } + if (std::abs(yFullPK) <= + lstarLambdaAbsYMax.value) { + + if (unlikeSignProxy) { + + if (lstarEnableFullPKMaps.value != 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKUnlikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } + + } else { + + if (lstarEnableFullPKMaps.value != 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKLikeSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } + } + } + if (pr.protonLike) { + + if (unlikeSignProxy) { + + if (lstarEnableProtonLikeSparse.value != 0) { + histos.fill( + HIST("hLambdaStarProxyProtonLikeUnlikeSparse"), + mass, + ptPair, + eventMult); + } + + } else { + + if (lstarEnableProtonLikeSparse.value != 0) { + histos.fill( + HIST("hLambdaStarProxyProtonLikeLikeSparse"), + mass, + ptPair, + eventMult); + } + } + } + } + } + } + } + + if (mixingEnabled()) { + // --- MIXED-EVENT: current kaons + previous-event real protons --- + // This fills the standard pK mixed-event background. + for (auto const& prev : mLStarMixEvents) { + + if (std::abs(prev.zvtx - collision.posZ()) > + lstarMixZvtxMax.value) { + continue; + } + + if (std::abs(prev.mult - eventMult) > + lstarMixMultMax.value) { + continue; + } + + if (prev.protons.empty()) { + continue; + } + + for (auto const& pr : prev.protons) { + + for (auto const& k : kaonCands) { + + // Unlike-sign pK mixed-event background. + if ((pr.charge * k.charge) >= 0) { + continue; + } + + const double mass = + invariantMass( + pr.px, + pr.py, + pr.pz, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTot = + pr.px + k.px; + + const float pyTot = + pr.py + k.py; + + const float pzTot = + pr.pz + k.pz; + + const float ptPair = + ptFromPxPy( + pxTot, + pyTot); + + const float yPair = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); + + if (std::abs(yPair) > + lstarLambdaAbsYMax.value) { + continue; + } + + if (lstarEnableSparse.value != 0) { + + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarPKMixedSparse"), + mass, + ptPair, + eventMult); + } + } + } + } + } + + // --- MIXED-EVENT: current proxies + previous-event kaons --- + // This fills the deuteron-proxy mixed-event background. + // + // For every d-selected proxy candidate we calculate: + // + // 1) M_{(d/2)K} + // using p_proxy = p_track / 2 + // + // 2) M_{pK}^{full} + // using the FULL measured momentum of the same d-selected track, + // but assigning the proton mass. + // + // The standard proxy mixed spectrum requires only the proxy rapidity + // acceptance. The 2D proxy-vs-full-pK map additionally requires the + // full-pK hypothesis to satisfy the same rapidity acceptance. + if (hasProxyCandidates) { + + for (auto const& prev : mLStarMixEvents) { + + if (std::abs(prev.zvtx - collision.posZ()) > + lstarMixZvtxMax.value) { + continue; + } + + if (std::abs(prev.mult - eventMult) > + lstarMixMultMax.value) { + continue; + } + + if (prev.kaons.empty()) { + continue; + } + + for (auto const& pr : proxyCands) { + + for (auto const& k : prev.kaons) { + + // Unlike-sign proxy-K mixed-event background. + if ((pr.charge * k.charge) >= 0) { + continue; + } + + // ---------------------------------------------------------- + // Proxy hypothesis: p_proxy = p_d / 2 + // ---------------------------------------------------------- + + const double mass = + invariantMass( + pr.px, + pr.py, + pr.pz, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTot = + pr.px + k.px; + + const float pyTot = + pr.py + k.py; + + const float pzTot = + pr.pz + k.pz; + + const float ptPair = + ptFromPxPy( + pxTot, + pyTot); + + const float yProxy = + rapidityFromMomentumAndMass( + pxTot, + pyTot, + pzTot, + mass); + + // Standard proxy rapidity acceptance. + if (std::abs(yProxy) > + lstarLambdaAbsYMax.value) { + continue; + } + + // ---------------------------------------------------------- + // Full-momentum proton hypothesis for the SAME d-selected + // track. + // ---------------------------------------------------------- + + const double massFullPK = + invariantMass( + pr.pxFull, + pr.pyFull, + pr.pzFull, + MassProton, + k.px, + k.py, + k.pz, + MassKaonCharged); + + const float pxTotFullPK = + pr.pxFull + k.px; + + const float pyTotFullPK = + pr.pyFull + k.py; + + const float pzTotFullPK = + pr.pzFull + k.pz; + + const float yFullPK = + rapidityFromMomentumAndMass( + pxTotFullPK, + pyTotFullPK, + pzTotFullPK, + massFullPK); + + if (lstarEnableSparse.value != 0) { + + if (lstarEnableProxyControls.value != 0) { + const bool passFullY = std::abs(yFullPK) <= lstarLambdaAbsYMax.value; + histos.fill(HIST("hLambdaStarProxyControlMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible); + if (lstarEnableFullPKMaps.value != 0) { + histos.fill(HIST("hLambdaStarProxyControlFullPKMixedSparse"), mass, ptPair, eventMult, pr.hasTOF, pr.protonLike, pr.protonTPCCompatible, massFullPK, passFullY); + } + } + + // Standard mixed-event proxy spectrum. + // + // This requires only the proxy hypothesis to satisfy + // |y| < lstarLambdaAbsYMax. + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarProxyMixedSparse"), + mass, + ptPair, + eventMult); + } + + // Mixed-event 2D map. + // + // Since this histogram explicitly represents both + // hypotheses, require the full-pK hypothesis to satisfy + // the same configurable rapidity acceptance as well. + if (std::abs(yFullPK) <= + lstarLambdaAbsYMax.value) { + + if (lstarEnableFullPKMaps.value != 0) { + if (lstarNoMixedEvents.value > 0) { + histos.fill( + HIST( + "hLambdaStarProxyVsFullPKMixedSparse"), + mass, + massFullPK, + ptPair, + eventMult); + } + } + } + } + } + } + } + } + + // --- Update mixing buffer with current event --- + LStarMixEventEntry entry; + entry.mult = eventMult; + entry.zvtx = collision.posZ(); + entry.kaons = std::move(kaonCands); + entry.protons = std::move(protonCands); + + mLStarMixEvents.push_front(std::move(entry)); + if (mLStarMixEvents.size() > static_cast(lstarNoMixedEvents.value)) { + mLStarMixEvents.pop_back(); + } + } + } + + void processData(CollisionsWithEvSel::iterator const& collision, FilteredTracks const& tracks) + { + processReco(collision, tracks); + } + + // A literal process() is always invoked by O2 in addition to optional methods. + // Keep the established JSON key "process" while registering data only once. + PROCESS_SWITCH_FULL(Lambdastarproxy, processData, process, "Lambda* proxy via (d/2)+K", true); + + void processMCReco(CollisionsWithEvSelMC::iterator const& collision, + FilteredTracksMC const& tracks, aod::McParticles const&, + aod::McCollisions const&) + { + // Inclusive reconstructed spectra include unmatched tracks and collisions, + // just as in data. Truth components require a valid same-event MC label. + processReco(collision, tracks); + } + PROCESS_SWITCH(Lambdastarproxy, processMCReco, "MC: data-identical reconstruction and truth-component spectra", false); + + void processMCGen(aod::McCollision const& collision, aod::McParticles const& particles) + { + fillGenerated(collision, particles); + } + PROCESS_SWITCH(Lambdastarproxy, processMCGen, "MC: generated pK and (d/2)K spectra, without reconstructed-event requirements", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGLF/Tasks/Resonances/lambdav2.cxx b/PWGLF/Tasks/Resonances/lambdav2.cxx index ea87a33f1fb..cf1cdfb8ef3 100644 --- a/PWGLF/Tasks/Resonances/lambdav2.cxx +++ b/PWGLF/Tasks/Resonances/lambdav2.cxx @@ -209,8 +209,7 @@ struct lambdav2 { template bool SelectionPID(const T& candidate, int PID) { - if (PID == 0) // pion - { + if (PID == 0) { // pion auto combPIDPi = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaPi() * candidate.tofNSigmaPi() + candidate.tpcNSigmaPi() * candidate.tpcNSigmaPi())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.6 && TMath::Abs(candidate.tpcNSigmaPi()) < nsigmaCutTPCPi) { return true; @@ -218,8 +217,7 @@ struct lambdav2 { if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && combPIDPi < nsigmaCutTOFPi) { return true; } - } else if (PID == 1) // kaon - { + } else if (PID == 1) { // kaon auto combPIDKa = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.45 && TMath::Abs(candidate.tpcNSigmaKa()) < nsigmaCutTPCKa) { return true; @@ -227,8 +225,7 @@ struct lambdav2 { if (candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && combPIDKa < nsigmaCutTOFKa) { return true; } - } else // proton - { + } else { // proton auto combPIDPr = TMath::Sqrt(TMath::Abs(candidate.tofNSigmaPr() * candidate.tofNSigmaPr() + candidate.tpcNSigmaPr() * candidate.tpcNSigmaPr())); if (!candidate.hasTOF() && candidate.tpcInnerParam() < 0.6 && TMath::Abs(candidate.tpcNSigmaPr()) < nsigmaCutTPCPr) { return true; @@ -306,7 +303,7 @@ struct lambdav2 { histos.fill(HIST("hpQxpQytvscent"), centrality, QxpQyt); histos.fill(HIST("hpQxtQypvscent"), centrality, QxtQyp); - for (auto track : tracks) { + for (const auto& track : tracks) { if (!selectionTrack(track)) { continue; } diff --git a/PWGLF/Tasks/Resonances/lstarpbpbv2.cxx b/PWGLF/Tasks/Resonances/lstarpbpbv2.cxx index 216cc36f7b0..f62d6f87927 100644 --- a/PWGLF/Tasks/Resonances/lstarpbpbv2.cxx +++ b/PWGLF/Tasks/Resonances/lstarpbpbv2.cxx @@ -420,7 +420,7 @@ struct lstarpbpbv2 { histos.fill(HIST("ResFT0CFT0ASP"), centrality, QFT0C * QFT0A * TMath::Cos(2.0 * (psiFT0C - psiFT0A))); histos.fill(HIST("ResFT0ATPCSP"), centrality, QTPC * QFT0A * TMath::Cos(2.0 * (psiTPC - psiFT0A))); - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -431,7 +431,7 @@ struct lstarpbpbv2 { } track1kaon = true; - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!selectionTrack(track2)) { continue; } diff --git a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx index 9bfdc99d194..bc7b2dd433e 100644 --- a/PWGLF/Tasks/Resonances/omega2012Analysis.cxx +++ b/PWGLF/Tasks/Resonances/omega2012Analysis.cxx @@ -12,7 +12,27 @@ /// \file omega2012Analysis.cxx /// \brief Invariant Mass Reconstruction of Omega(2012) Resonance /// \author Bong-Hwi Lim - +/// +/// Two decay modes, analysed separately (never merged in any histogram or process function): +/// - mode A (XiK0s): Omega(2012)- -> Xi- K0S. processData, processMixedEvent, processMC, processMCGen, processMCGenerated. +/// Histograms: Event/, QAbefore/, QAafter/, omega2012/, MC/ (unchanged). +/// - mode B (Xi1530K): Omega(2012)- -> Xi(1530)0 K- -> Xi- pi+ K- with a charged kaon track. +/// processXi1530KMicro, processXi1530KTracks, processXi1530KMCMicro, processXi1530KMCGen, processXi1530KMixedMicro. +/// Histograms: xi1530K/ (signal charge pattern) and xi1530K_wrongSign/ (charge-pattern controls). +/// Selection, candidate enumeration and truth classification: PWGLF/Core/Omega2012AnalysisCore.h. +/// +/// Configuration keys: all mode-A keys are unchanged. The former three-body (Xi pi K0S) process functions +/// processThreeBodyWithTracks / processThreeBodyWithMicroTracks are removed (that final state cannot be an Omega(2012)-). +/// Mode-B pion/kaon track selection: the common resonance TrackCuts with the prefix "trk.": +/// cPionPtMin -> trk.cMinPtcut, cPionEtaMax -> trk.cMaxEtacut, cPionDCAxyMax -> trk.cMaxDCArToPVcut, +/// cPionDCAzMax -> trk.cMaxDCAzToPVcut (default 0.15 cm), cPionTPCNClusMin -> trk.cfgTPCcluster; +/// further trk.* keys as in PWGLF/Core/ResoAnalysisSelectionCore.h. +/// Pion PID keys cPion* are unchanged; the kaon PID keys cKaon* have the same shape. +/// New mode-B keys: cXi1530UseMassWindow, cXi1530KFillWrongSign, cByPassTOF. + +#include "PWGLF/Core/Omega2012AnalysisCore.h" +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" #include "PWGLF/DataModel/LFResonanceTables.h" #include @@ -25,36 +45,35 @@ #include #include #include +#include #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include #include -#include -#include -#include +#include +#include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::constants::physics; +using namespace o2::analysis::omega2012; struct Omega2012Analysis { // Constants - static constexpr float kSmallNumber = 1e-10f; // Small number to avoid division by zero - static constexpr float kMaxDCAV0ToPV = 1.0f; // Maximum DCA of V0 to PV - static constexpr int kNumExpectedDaughters = 2; // Expected number of daughters for 2-body decay - static constexpr int kPlaceholderPdgCode = 9999999; // o2-linter: disable=pdg/explicit-code (placeholder for generator-specific Omega(2012) PDG code) + static constexpr int NumExpectedDaughters = 2; // Expected number of daughters for 2-body decay SliceCache cache; Preslice perResoCollisionCasc = aod::resodaughter::resoCollisionId; Preslice perResoCollisionV0 = aod::resodaughter::resoCollisionId; Preslice perResoCollisionTrack = aod::resodaughter::resoCollisionId; - Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; + Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; // Axes @@ -62,92 +81,67 @@ struct Omega2012Analysis { ConfigurableAxis binsPtQA{"binsPtQA", {VARIABLE_WIDTH, 0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, "pT (QA)"}; ConfigurableAxis binsCent{"binsCent", {VARIABLE_WIDTH, 0., 1., 5., 10., 30., 50., 70., 100., 110.}, "Centrality"}; - // Invariant mass range for Omega(2012) → Xi + K0s + // Invariant mass range for Omega(2012) Configurable cInvMassStart{"cInvMassStart", 1.6, "Invariant mass start (GeV/c^2)"}; Configurable cInvMassEnd{"cInvMassEnd", 2.2, "Invariant mass end (GeV/c^2)"}; Configurable cInvMassBins{"cInvMassBins", 600, "Invariant mass bins"}; - // Basic pre-selections (mirroring refs) - Configurable cMinPtcut{"cMinPtcut", 0.15, "Minimum pT for candidates"}; - Configurable cMaxEtaCut{"cMaxEtaCut", 0.8, "Maximum |eta|"}; - // V0 selections (K0s) from k892pmanalysis - Configurable cV0MinCosPA{"cV0MinCosPA", 0.97, "V0 minimum pointing angle cosine"}; - Configurable cV0MaxDaughDCA{"cV0MaxDaughDCA", 1.0, "V0 daughter DCA Maximum"}; - Configurable cV0MassWindow{"cV0MassWindow", 0.0043, "Mass window for competing Lambda0 rejection"}; - Configurable cMaxV0Etacut{"cMaxV0Etacut", 0.8, "V0 maximum eta cut"}; - - // Xi (cascade) selections from xi1530Analysisqa.cxx - Configurable cDCAxyToPVByPtCascP0{"cDCAxyToPVByPtCascP0", 999., "Cascade DCAxy p0"}; - Configurable cDCAxyToPVByPtCascExp{"cDCAxyToPVByPtCascExp", 1., "Cascade DCAxy exp"}; - Configurable cDCAxyToPVAsPtForCasc{"cDCAxyToPVAsPtForCasc", true, "Use pt-dep DCAxy cut (casc)"}; - - Configurable cDCAzToPVAsPtForCasc{"cDCAzToPVAsPtForCasc", true, "Use pt-dep DCAz cut (casc)"}; - - // V0 topology inside cascade (Λ) - Configurable cDCALambdaDaugtherscut{"cDCALambdaDaugtherscut", 0.7, "Λ daughters DCA cut"}; - Configurable cDCALambdaToPVcut{"cDCALambdaToPVcut", 0.02, "Λ DCA to PV min"}; - Configurable cDCAPionToPVcut{"cDCAPionToPVcut", 0.06, "π DCA to PV min"}; - Configurable cDCAProtonToPVcut{"cDCAProtonToPVcut", 0.07, "p DCA to PV min"}; - Configurable cV0CosPACutPtDepP0{"cV0CosPACutPtDepP0", 0.25, "V0 CosPA p0"}; - Configurable cV0CosPACutPtDepP1{"cV0CosPACutPtDepP1", 0.022, "V0 CosPA p1"}; - Configurable cMaxV0radiuscut{"cMaxV0radiuscut", 200., "V0 radius max"}; - Configurable cMinV0radiuscut{"cMinV0radiuscut", 2.5, "V0 radius min"}; - Configurable cMasswindowV0cut{"cMasswindowV0cut", 0.005, "Λ mass window for cascade V0"}; - - // Cascade topology - Configurable cDCABachlorToPVcut{"cDCABachlorToPVcut", 0.06, "Bachelor DCA to PV min"}; - Configurable cDCAXiDaugthersCutPtRangeLower{"cDCAXiDaugthersCutPtRangeLower", 1., "Xi pt low boundary"}; - Configurable cDCAXiDaugthersCutPtRangeUpper{"cDCAXiDaugthersCutPtRangeUpper", 4., "Xi pt high boundary"}; - Configurable cDCAXiDaugthersCutPtDepLower{"cDCAXiDaugthersCutPtDepLower", 0.8, "Xi daugh DCA (pt cDCAXiDaugthersCutPtDepMiddle{"cDCAXiDaugthersCutPtDepMiddle", 0.5, "Xi daugh DCA (low<=pt cDCAXiDaugthersCutPtDepUpper{"cDCAXiDaugthersCutPtDepUpper", 0.2, "Xi daugh DCA (pt>=high)"}; - Configurable cCosPACascCutPtDepP0{"cCosPACascCutPtDepP0", 0.2, "Cascade CosPA p0"}; - Configurable cCosPACascCutPtDepP1{"cCosPACascCutPtDepP1", 0.022, "Cascade CosPA p1"}; - Configurable cMaxCascradiuscut{"cMaxCascradiuscut", 200., "Cascade radius max"}; - Configurable cMinCascradiuscut{"cMinCascradiuscut", 1.1, "Cascade radius min"}; - Configurable cMasswindowCasccut{"cMasswindowCasccut", 0.008, "Xi mass window"}; - Configurable cMassXiminus{"cMassXiminus", 1.32171, "Xi mass (GeV/c^2)"}; // PDG + // Selection shared with the Omega(2012) training-table task (plain JSON keys; the mode-B track cuts carry "trk.") + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); + XiCuts xiCuts; + K0sCuts k0sCuts; + Xi1530KCuts xi1530KCuts; + o2::analysis::resonance::TrackCuts trackCuts = makeXi1530KTrackCuts(); + PionPidCuts pionPID; + KaonPidCuts kaonPID; + CandidateCuts candidateCuts; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Require the matched Omega(2012) truth rapidity inside cfgRapidityCut"}; + Configurable cMCInjection{"cMCInjection", false, "Restrict MC numerator and denominator to Omega parents with generator status -11"}; + + struct OmegaParentTruth { + float pt = 0.f; + float y = 0.f; + int pdgCode = 0; + bool injected = false; + }; + using OmegaParentTruthMap = std::unordered_map; + + template + static bool isInjectedOmegaParent(const Parent& parent) + { + constexpr int InjectedGeneratorStatus = -11; + return o2::mcgenstatus::getGenStatusCode(parent.statusCode()) == InjectedGeneratorStatus; + } + + template + OmegaParentTruthMap omegaParentTruth(const Parents& parents) + { + OmegaParentTruthMap parentTruth; + for (const auto& parent : parents) { + if (std::abs(parent.pdgCode()) == kOmega2012Minus) { + parentTruth.emplace(parent.originalMcParticleId(), OmegaParentTruth{parent.pt(), parent.y(), parent.pdgCode(), isInjectedOmegaParent(parent)}); + } + } + return parentTruth; + } // Event Mixing Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; ConfigurableAxis cfgMultBins{"cfgMultBins", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Mixing bins - centrality"}; - // Enhanced K0s selections - Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm/c)"}; - Configurable cK0sArmenterosQtMin{"cK0sArmenterosQtMin", 0.0, "K0s Armenteros qt min"}; - Configurable cK0sArmenterosAlphaMax{"cK0sArmenterosAlphaMax", 0.8, "K0s Armenteros alpha max"}; - Configurable cK0sDauPosDCAtoPVMin{"cK0sDauPosDCAtoPVMin", 0.1, "K0s positive daughter DCA to PV min"}; - Configurable cK0sDauNegDCAtoPVMin{"cK0sDauNegDCAtoPVMin", 0.1, "K0s negative daughter DCA to PV min"}; - Configurable cK0sRadiusMin{"cK0sRadiusMin", 0.5, "K0s decay radius min"}; - Configurable cK0sRadiusMax{"cK0sRadiusMax", 100.0, "K0s decay radius max"}; - Configurable cK0sCrossMassRejection{"cK0sCrossMassRejection", true, "Enable Lambda mass rejection for K0s"}; - - // Pion track selections for 3-body decay - Configurable cPionPtMin{"cPionPtMin", 0.15, "Minimum pion pT"}; - Configurable cPionEtaMax{"cPionEtaMax", 0.8, "Maximum pion |eta|"}; - Configurable cPionDCAxyMax{"cPionDCAxyMax", 0.1, "Maximum pion DCAxy to PV"}; - Configurable cPionDCAzMax{"cPionDCAzMax", 0.2, "Maximum pion DCAz to PV"}; - Configurable cPionTPCNClusMin{"cPionTPCNClusMin", 70, "Minimum TPC clusters for pion"}; - - // Pion PID selections - Configurable cPionTPCNSigmaMax{"cPionTPCNSigmaMax", 3.0, "Maximum TPC NSigma for pion"}; - Configurable cPionTOFNSigmaMax{"cPionTOFNSigmaMax", 3.0, "Maximum TOF NSigma for pion"}; - Configurable cPionUsePtDepPID{"cPionUsePtDepPID", false, "Use pT-dependent PID cuts for pion"}; - Configurable> cPionPIDPtBins{"cPionPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for pion PID cuts"}; - Configurable> cPionTPCNSigmaCuts{"cPionTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (pion)"}; - Configurable> cPionTOFNSigmaCuts{"cPionTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (pion)"}; - Configurable> cPionTOFRequired{"cPionTOFRequired", {0, 0, 1, 1}, "Require TOF per pT bin (pion)"}; - - // Xi1530 mass window cut - Configurable cXi1530Mass{"cXi1530Mass", 1.53, "Xi(1530) mass (GeV/c^2)"}; - Configurable cXi1530MassWindow{"cXi1530MassWindow", 0.01, "Xi(1530) mass window (GeV/c^2)"}; - - // PDG masses - double massK0 = MassK0Short; + // Module-initializer collision and daughter tables (matches the resonance-module-initializer producer) + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCollisions = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCCascades = soa::Join; + using ResoMCV0s = soa::Join; + using ResoMCMicroTracks = soa::Join; using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; + BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}}; + + Omega2012AnalysisCore core; void init(InitContext&) { @@ -165,15 +159,21 @@ struct Omega2012Analysis { AxisSpec lifetimeAxis = {200, 0, 50, "Proper lifetime (cm/c)"}; AxisSpec armQtAxis = {100, 0, 0.3, "q_{T} (GeV/c)"}; AxisSpec armAlphaAxis = {100, -1.0, 1.0, "#alpha"}; + AxisSpec nsigmaAxis = {100, -5.0, 5.0, "N#sigma"}; + AxisSpec crossedRowsAxis = {160, 0, 160, "TPC crossed rows"}; + AxisSpec openingAngleAxis = {200, -0.15, 6.45, "#alpha_{oa}"}; + AxisSpec omegaKinPtAxis = {500, 0, 10, "#it{p}_{T} (GeV/#it{c})"}; // Event QA histograms + // Event counters are double precision (kTH1D): a single-precision bin stops counting at 2^24 entries per job, + // which truncated the zero-multiplicity bins of the multiplicity counters in large pp productions. histos.add("Event/posZ", "Event vertex Z position", kTH1F, {{200, -20., 20., "V_{z} (cm)"}}); - histos.add("Event/centrality", "Event centrality distribution", kTH1F, {centAxis}); + histos.add("Event/centrality", "Event centrality distribution", kTH1D, {centAxis}); histos.add("Event/posZvsCent", "Vertex Z vs Centrality", kTH2F, {{200, -20., 20., "V_{z} (cm)"}, centAxis}); - histos.add("Event/nCascades", "Number of cascades per event", kTH1F, {{100, 0., 100., "N_{cascades}"}}); - histos.add("Event/nV0s", "Number of V0s per event", kTH1F, {{200, 0., 200., "N_{V0s}"}}); - histos.add("Event/nCascadesAfterCuts", "Number of cascades per event after cuts", kTH1F, {{50, 0., 50., "N_{cascades}"}}); - histos.add("Event/nV0sAfterCuts", "Number of V0s per event after cuts", kTH1F, {{100, 0., 100., "N_{V0s}"}}); + histos.add("Event/nCascades", "Number of cascades per event", kTH1D, {{100, 0., 100., "N_{cascades}"}}); + histos.add("Event/nV0s", "Number of V0s per event", kTH1D, {{200, 0., 200., "N_{V0s}"}}); + histos.add("Event/nCascadesAfterCuts", "Number of cascades per event after cuts", kTH1D, {{50, 0., 50., "N_{cascades}"}}); + histos.add("Event/nV0sAfterCuts", "Number of V0s per event after cuts", kTH1D, {{100, 0., 100., "N_{V0s}"}}); // Xi QA histograms histos.add("QAbefore/xiMass", "Xi mass before cuts", kTH1F, {xiMassAxis}); @@ -212,6 +212,10 @@ struct Omega2012Analysis { histos.add("QAbefore/k0sArmenteros", "K0s Armenteros plot before cuts", kTH2F, {armAlphaAxis, armQtAxis}); histos.add("QAbefore/k0sDauPosDCA", "K0s positive daughter DCA before cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAbefore/k0sDauNegDCA", "K0s negative daughter DCA before cuts", kTH2F, {ptAxisQA, dcaAxis}); + histos.add("QAbefore/k0sDauTPCNsigmaPosPi", "K0s positive daughter pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/k0sDauTPCNsigmaNegPi", "K0s negative daughter pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/k0sNCrossedRowsPos", "K0s positive daughter crossed rows before cuts", kTH2F, {ptAxisQA, crossedRowsAxis}); + histos.add("QAbefore/k0sNCrossedRowsNeg", "K0s negative daughter crossed rows before cuts", kTH2F, {ptAxisQA, crossedRowsAxis}); histos.add("QAafter/k0sMassPt", "K0s mass vs pT after cuts", kTH2F, {ptAxisQA, k0sMassAxis}); histos.add("QAafter/k0sPt", "K0s pT after cuts", kTH1F, {ptAxisQA}); @@ -224,34 +228,18 @@ struct Omega2012Analysis { histos.add("QAafter/k0sArmenteros", "K0s Armenteros plot after cuts", kTH2F, {armAlphaAxis, armQtAxis}); histos.add("QAafter/k0sDauPosDCA", "K0s positive daughter DCA after cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAafter/k0sDauNegDCA", "K0s negative daughter DCA after cuts", kTH2F, {ptAxisQA, dcaAxis}); + histos.add("QAafter/k0sDauTPCNsigmaPosPi", "K0s positive daughter pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/k0sDauTPCNsigmaNegPi", "K0s negative daughter pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/k0sNCrossedRowsPos", "K0s positive daughter crossed rows after cuts", kTH2F, {ptAxisQA, crossedRowsAxis}); + histos.add("QAafter/k0sNCrossedRowsNeg", "K0s negative daughter crossed rows after cuts", kTH2F, {ptAxisQA, crossedRowsAxis}); // Resonance (2-body decay: Xi + K0s) histos.add("omega2012/invmass", "Invariant mass of Omega(2012) → Xi + K0s", kTH1F, {invMassAxis}); histos.add("omega2012/invmass_Mix", "Mixed event Invariant mass of Omega(2012) → Xi + K0s", kTH1F, {invMassAxis}); - histos.add("omega2012/massPtCent", "Omega(2012) mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); - histos.add("omega2012/massPtCent_Mix", "Mixed event Omega(2012) mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); - - // 3-body decay: Xi + pi + K0s - histos.add("omega2012_3body/invmass", "Invariant mass of Omega(2012) → Xi + #pi + K^{0}_{S}", kTH1F, {invMassAxis}); - histos.add("omega2012_3body/massPtCent", "Omega(2012) 3-body mass vs pT vs cent", kTH3F, {invMassAxis, ptAxis, centAxis}); - - // Pion QA histograms for 3-body - AxisSpec nsigmaAxis = {100, -5.0, 5.0, "N#sigma"}; - histos.add("QAbefore/pionPt", "Pion pT before cuts", kTH1F, {ptAxisQA}); - histos.add("QAbefore/pionEta", "Pion eta before cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); - histos.add("QAbefore/pionDCAxy", "Pion DCAxy before cuts", kTH2F, {ptAxisQA, dcaxyAxis}); - histos.add("QAbefore/pionDCAz", "Pion DCAz before cuts", kTH2F, {ptAxisQA, dcazAxis}); - histos.add("QAbefore/pionTPCNcls", "Pion TPC clusters before cuts", kTH1F, {{160, 0, 160, "N_{TPC clusters}"}}); - histos.add("QAbefore/pionTPCNSigma", "Pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - histos.add("QAbefore/pionTOFNSigma", "Pion TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - - histos.add("QAafter/pionPt", "Pion pT after cuts", kTH1F, {ptAxisQA}); - histos.add("QAafter/pionEta", "Pion eta after cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); - histos.add("QAafter/pionDCAxy", "Pion DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); - histos.add("QAafter/pionDCAz", "Pion DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); - histos.add("QAafter/pionTPCNcls", "Pion TPC clusters after cuts", kTH1F, {{160, 0, 160, "N_{TPC clusters}"}}); - histos.add("QAafter/pionTPCNSigma", "Pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); - histos.add("QAafter/pionTOFNSigma", "Pion TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("omega2012/massPtCent", "Omega(2012) mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("omega2012/massPtCent_Mix", "Mixed event Omega(2012) mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("QAbefore/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} before kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); + histos.add("QAafter/omegaAlphaVsPt", "#alpha_{oa} vs p_{T} after kinematic cuts", kTH2F, {omegaKinPtAxis, openingAngleAxis}); // MC truth histograms AxisSpec etaAxis = {100, -2.0, 2.0, "#eta"}; @@ -262,6 +250,20 @@ struct Omega2012Analysis { histos.add("MC/hMCGenOmega2012Y", "MC Generated Omega(2012) rapidity", kTH1F, {rapidityAxis}); histos.add("MC/hMCRecOmega2012Pt", "MC Reconstructed Omega(2012) pT", kTH1F, {ptAxis}); histos.add("MC/hMCRecOmega2012PtEta", "MC Reconstructed Omega(2012) pT vs eta", kTH2F, {ptAxis, etaAxis}); + if (doprocessMCGen) { + histos.add("MC/hMCGenSelectedXiK0sPt", "MC generated Omega(2012) #rightarrow #Xi K^{0}_{S} in selected events and rapidity window;p_{T} (GeV/c)", kTH1F, {ptAxis}); + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCGenSelectedXiK0sResponse", "Selected-event generated #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } + if (doprocessMC) { + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("MC/hMCRecXiK0sResponse", "Truth-matched selected #Omega #rightarrow #Xi K^{0}_{S}", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); + } // MC truth invariant mass (from MC particles) histos.add("MC/hMCTruthInvMassXiK0s", "MC Truth Inv Mass Xi + K^{0}_{S}", kTH1F, {invMassAxis}); @@ -272,331 +274,140 @@ struct Omega2012Analysis { histos.add("MC/hMCRecK0sPt", "MC Reconstructed K0s pT", kTH1F, {ptAxis}); histos.add("MC/hMCTrueXiPt", "MC True Xi pT", kTH1F, {ptAxis}); histos.add("MC/hMCTrueK0sPt", "MC True K0s pT", kTH1F, {ptAxis}); - } - - // Enhanced V0 selection (K0s) with detailed criteria - template - bool v0CutEnhanced(const CollisionType& collision, const V0Type& v0) - { - // Basic kinematic cuts - if (std::abs(v0.eta()) > cMaxV0Etacut) - return false; - if (v0.pt() < cMinPtcut) - return false; - - // Topological cuts - if (v0.v0CosPA() < cV0MinCosPA) - return false; - if (v0.daughDCA() > cV0MaxDaughDCA) - return false; - - // Enhanced selections from chk892Flow - // Daughter DCA to PV cuts - if (std::abs(v0.dcapostopv()) < cK0sDauPosDCAtoPVMin) - return false; - if (std::abs(v0.dcanegtopv()) < cK0sDauNegDCAtoPVMin) - return false; - - // Radius cuts - use transRadius instead of v0radius - auto radius = v0.transRadius(); - if (radius < cK0sRadiusMin || radius > cK0sRadiusMax) - return false; - - // DCA to PV - if (std::abs(v0.dcav0topv()) > kMaxDCAV0ToPV) - return false; // max DCA to PV - - // Proper lifetime cut - calculate manually - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallNumber)) * MassK0Short; - if (properLifetime > cK0sProperLifetimeMax) - return false; - - // Armenteros cut - skip for now as we don't have daughter momentum info in ResoV0s table - // If needed, this would require accessing daughter tracks separately or using alternative cuts - - // Mass window - if (std::abs(v0.mK0Short() - MassK0Short) > cV0MassWindow) - return false; - - // Competing V0 rejection: remove (Anti)Λ - if (cK0sCrossMassRejection) { - if (std::abs(v0.mLambda() - MassLambda) < cV0MassWindow) - return false; - if (std::abs(v0.mAntiLambda() - MassLambda) < cV0MassWindow) - return false; - } - - return true; - } - - // Original V0 selection for backward compatibility - template - bool v0Cut(const V0Type& v0) - { - if (std::abs(v0.eta()) > cMaxV0Etacut) - return false; - if (v0.v0CosPA() < cV0MinCosPA) - return false; - if (v0.daughDCA() > cV0MaxDaughDCA) - return false; - // Competing V0 rejection: remove (Anti)Λ - if (std::abs(v0.mLambda() - MassLambda) < cV0MassWindow) - return false; - if (std::abs(v0.mAntiLambda() - MassLambda) < cV0MassWindow) - return false; - return true; - } - - // Helper function to find pT bin index - int getPtBinIndex(float pt) - { - auto ptBins = static_cast>(cPionPIDPtBins); - for (size_t i = 0; i < ptBins.size() - 1; i++) { - if (pt >= ptBins[i] && pt < ptBins[i + 1]) { - return i; - } - } - return -1; - } - - // Pion PID selection - template - bool pionPidCut(const TrackType& track) - { - float pt = track.pt(); - - if constexpr (IsResoMicrotrack) { - // For ResoMicroTracks - decode PID from flags - float tpcNSigma = o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(track.pidNSigmaPiFlag()); - float tofNSigma = track.hasTOF() ? o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(track.pidNSigmaPiFlag()) : 999.f; - - if (cPionUsePtDepPID) { - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; - - auto tpcCuts = static_cast>(cPionTPCNSigmaCuts); - auto tofCuts = static_cast>(cPionTOFNSigmaCuts); - auto tofRequired = static_cast>(cPionTOFRequired); - - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; - } - - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) - return false; - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } - - return true; - } else { - bool tpcPass = std::abs(tpcNSigma) < cPionTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cPionTOFNSigmaMax : true; - return tpcPass && tofPass; - } - } else { - // For ResoTracks - direct access - float tpcNSigma = track.tpcNSigmaPi(); - float tofNSigma = track.hasTOF() ? track.tofNSigmaPi() : 999.f; - - if (cPionUsePtDepPID) { - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; - - auto tpcCuts = static_cast>(cPionTPCNSigmaCuts); - auto tofCuts = static_cast>(cPionTOFNSigmaCuts); - auto tofRequired = static_cast>(cPionTOFRequired); - - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; - } - - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) - return false; - - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } - - return true; - } else { - bool tpcPass = std::abs(tpcNSigma) < cPionTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cPionTOFNSigmaMax : true; - return tpcPass && tofPass; + ProcessModes modes; + modes.xiK0s = doprocessData || doprocessMixedEvent || doprocessMC; + modes.xi1530K = doprocessXi1530KMicro || doprocessXi1530KTracks || doprocessXi1530KMCMicro || doprocessXi1530KMCGen || doprocessXi1530KMixedMicro; + modes.microTracks = doprocessXi1530KMicro || doprocessXi1530KMCMicro || doprocessXi1530KMixedMicro; + modes.mcReco = doprocessMC || doprocessXi1530KMCMicro; + modes.mixing = doprocessMixedEvent || doprocessXi1530KMixedMicro; + core.init(histos, eventCuts, xiCuts, k0sCuts, xi1530KCuts, trackCuts, pionPID, kaonPID, candidateCuts, modes); + + if (modes.xi1530K) { + AxisSpec xiPiMassAxis = {300, 1.4, 1.7, "M_{#Xi#pi} (GeV/#it{c}^{2})"}; + AxisSpec patternAxis = {3, 0.5, 3.5, "charge pattern (1: wrong-sign #pi, 2: wrong-sign K, 3: both)"}; + AxisSpec etaAxisQA = {100, -2.0, 2.0, "#eta"}; + + // Signal charge pattern: Xi- pi+ K- and Xi+ pi- K+ + histos.add("xi1530K/invmass", "Invariant mass of Omega(2012) → #Xi(1530)^{0} K → #Xi #pi K", kTH1F, {invMassAxis}); + histos.add("xi1530K/massPtCent", "Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/invmass_Mix", "Mixed event invariant mass of Omega(2012) → #Xi(1530)^{0} K", kTH1F, {invMassAxis}); + histos.add("xi1530K/massPtCent_Mix", "Mixed event Omega(2012) → #Xi(1530)^{0} K mass vs pT vs cent", kTHnSparseD, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1530K/massXiPi", "#Xi #pi mass of selected candidates (before the rapidity cut)", kTH1F, {xiPiMassAxis}); + histos.add("xi1530K/massXiPiVsMass", "#Xi #pi mass vs #Xi #pi K mass", kTH2F, {invMassAxis, xiPiMassAxis}); + + // QA of the mode-B inputs (once per object and collision) + histos.add("xi1530K/QAbefore/xiMass", "Xi mass before cuts", kTH1F, {xiMassAxis}); + histos.add("xi1530K/QAbefore/xiPt", "Xi pT before cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAbefore/xiEta", "Xi eta before cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/xiMass", "Xi mass after cuts", kTH1F, {xiMassAxis}); + histos.add("xi1530K/QAafter/xiPt", "Xi pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/xiEta", "Xi eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAbefore/trackPt", "Track pT before cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAbefore/trackEta", "Track eta before cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAbefore/trackDCAxy", "Track DCAxy before cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAbefore/trackDCAz", "Track DCAz before cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAbefore/pionTPCNSigma", "Pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/pionTOFNSigma", "Pion TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/kaonTPCNSigma", "Kaon TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAbefore/kaonTOFNSigma", "Kaon TOF NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/pionPt", "Pion pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/pionEta", "Pion eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/pionDCAxy", "Pion DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAafter/pionDCAz", "Pion DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAafter/pionTPCNSigma", "Pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/pionTOFNSigma", "Pion TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/kaonPt", "Kaon pT after cuts", kTH1F, {ptAxisQA}); + histos.add("xi1530K/QAafter/kaonEta", "Kaon eta after cuts", kTH1F, {etaAxisQA}); + histos.add("xi1530K/QAafter/kaonDCAxy", "Kaon DCAxy after cuts", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("xi1530K/QAafter/kaonDCAz", "Kaon DCAz after cuts", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("xi1530K/QAafter/kaonTPCNSigma", "Kaon TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("xi1530K/QAafter/kaonTOFNSigma", "Kaon TOF NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + + // Charge-pattern controls (never part of the signal) + histos.add("xi1530K_wrongSign/invmassPattern", "Wrong-sign #Xi #pi K mass by charge pattern", kTH2F, {patternAxis, invMassAxis}); + histos.add("xi1530K_wrongSign/massPtPattern", "Wrong-sign #Xi #pi K mass vs pT by charge pattern", kTH3F, {invMassAxis, ptAxis, patternAxis}); + + if (doprocessXi1530KMCMicro) { + histos.add("xi1530K/MC/hMCRecOmega2012Pt", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecOmega2012PtEta", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K pT vs eta", kTH2F, {ptAxis, etaAxis}); + histos.add("xi1530K/MC/hMCRecMass", "MC reconstructed Omega(2012) → #Xi(1530)^{0} K mass", kTH1F, {invMassAxis}); + histos.add("xi1530K/MC/hMCRecMassXiPi", "MC reconstructed #Xi #pi mass", kTH1F, {xiPiMassAxis}); + histos.add("xi1530K/MC/hMCRecXiPt", "MC reconstructed Xi pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecPionPt", "MC reconstructed pion pT", kTH1F, {ptAxis}); + histos.add("xi1530K/MC/hMCRecKaonPt", "MC reconstructed kaon pT", kTH1F, {ptAxis}); + const AxisSpec recoPtAxis{binsPt, "p_{T}^{reco} (GeV/c)"}; + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCRecXi1530KResponse", "Truth-matched selected #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {recoPtAxis, truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); } - } - } - - // Pion track selection (for both ResoTracks and ResoMicroTracks) - template - bool pionCut(const TrackType& track) - { - // Basic kinematic cuts - if (track.pt() < cPionPtMin) - return false; - if (std::abs(track.eta()) > cPionEtaMax) - return false; - - // DCA cuts - different access for ResoMicroTracks - if constexpr (IsResoMicrotrack) { - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags()) > cPionDCAxyMax) - return false; - if (o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags()) > cPionDCAzMax) - return false; - } else { - if (std::abs(track.dcaXY()) > cPionDCAxyMax) - return false; - if (std::abs(track.dcaZ()) > cPionDCAzMax) - return false; - } - - // Track quality cuts - only for ResoTracks - if constexpr (!IsResoMicrotrack) { - if constexpr (requires { track.tpcNClsFound(); }) { - if (track.tpcNClsFound() < cPionTPCNClusMin) - return false; + if (doprocessXi1530KMCGen) { + const AxisSpec truthPtAxis{binsPt, "p_{T}^{truth} (GeV/c)"}; + const AxisSpec signAxis{2, -1.5, 1.5, "Omega PDG sign (+1 particle, -1 antiparticle)"}; + histos.add("xi1530K/MC/hMCGenSelectedXi1530KResponse", "Selected-event generated #Omega #rightarrow #Xi(1530) K", kTHnSparseD, + {truthPtAxis, centAxis, {500, 0., 500., "MC multiplicity"}, signAxis}); } } - // PID selection - if (!pionPidCut(track)) - return false; - - return true; - } - - // Xi1530 mass window cut - template - bool xi1530MassCut(const XiType& xi, const PionType& pion) - { - // Calculate Xi + pion invariant mass - ROOT::Math::PxPyPzEVector pXi, pPion, pXi1530; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pPion = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), MassPionCharged)); - pXi1530 = pXi + pPion; - - // Check if mass is within Xi(1530) window - float massDiff = std::abs(pXi1530.M() - cXi1530Mass); - return massDiff < cXi1530MassWindow; - } - - // Primary-level cascade kinematics - template - bool cascprimaryTrackCut(const CascT& c) - { - if (std::abs(c.eta()) > cMaxEtaCut) - return false; - if (std::abs(c.pt()) < cMinPtcut) - return false; - if (cDCAxyToPVAsPtForCasc) { - if (std::abs(c.dcaXYCascToPV()) > (cDCAxyToPVByPtCascP0 + cDCAxyToPVByPtCascExp * c.pt())) - return false; - } - if (cDCAzToPVAsPtForCasc) { - if (std::abs(c.dcaZCascToPV()) > (cDCAxyToPVByPtCascP0 + cDCAxyToPVByPtCascExp * std::pow(c.pt(), -1.1f))) - return false; - } - return true; - } - - // Cascade topological selections adapted from xi1530Analysisqa - template - bool casctopCut(const CascT& c) - { - // V0 (Λ) topology inside cascade - if (std::abs(c.daughDCA()) > cDCALambdaDaugtherscut) - return false; - if (std::abs(c.dcav0topv()) < cDCALambdaToPVcut) - return false; - - if (c.sign() < 0) { // Xi- - if (std::abs(c.dcanegtopv()) < cDCAPionToPVcut) - return false; - if (std::abs(c.dcapostopv()) < cDCAProtonToPVcut) - return false; - } else { // Anti-Xi - if (std::abs(c.dcanegtopv()) < cDCAProtonToPVcut) - return false; - if (std::abs(c.dcapostopv()) < cDCAPionToPVcut) - return false; - } - - if (c.v0CosPA() < std::cos(cV0CosPACutPtDepP0 - cV0CosPACutPtDepP1 * c.pt())) - return false; - if (c.transRadius() > cMaxV0radiuscut || c.transRadius() < cMinV0radiuscut) - return false; - if (std::abs(c.mLambda() - MassLambda) > cMasswindowV0cut) - return false; - - // Cascade-level topology - if (std::abs(c.dcabachtopv()) < cDCABachlorToPVcut) - return false; - - if (c.pt() < cDCAXiDaugthersCutPtRangeLower) { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepLower) - return false; - } else if (c.pt() < cDCAXiDaugthersCutPtRangeUpper) { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepMiddle) - return false; - } else { - if (c.cascDaughDCA() > cDCAXiDaugthersCutPtDepUpper) - return false; - } - - if (c.cascCosPA() < std::cos(cCosPACascCutPtDepP0 - cCosPACascCutPtDepP1 * c.pt())) - return false; - if (c.cascTransRadius() > cMaxCascradiuscut || c.cascTransRadius() < cMinCascradiuscut) - return false; - if (std::abs(c.mXi() - cMassXiminus) > cMasswindowCasccut) - return false; - - return true; + LOG(info) << "Size of the histograms in Omega(2012) analysis task"; + histos.print(); } - template - void fill(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s) + // Mode A, same event (data): event QA, Xi and K0S QA, kinematic-cut QA and the Xi K0S mass + template + void fillXiK0s(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s) { auto cent = collision.cent(); - // Fill event QA histograms (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/posZ"), collision.posZ()); - histos.fill(HIST("Event/centrality"), cent); - histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); - histos.fill(HIST("Event/nCascades"), cascades.size()); - histos.fill(HIST("Event/nV0s"), v0s.size()); - } + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centrality"), cent); + histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); + histos.fill(HIST("Event/nCascades"), cascades.size()); + histos.fill(HIST("Event/nV0s"), v0s.size()); // Count candidates after cuts int nCascAfterCuts = 0; int nV0sAfterCuts = 0; - for (const auto& xi : cascades) { - // QA before Xi cuts - detailed histograms + auto onK0s = [&](auto const& v0, double properLifetime, bool selected) { + histos.fill(HIST("QAbefore/k0sMassPt"), v0.pt(), v0.mK0Short()); + histos.fill(HIST("QAbefore/k0sPt"), v0.pt()); + histos.fill(HIST("QAbefore/k0sEta"), v0.eta()); + histos.fill(HIST("QAbefore/k0sCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAbefore/k0sRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAbefore/k0sDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAbefore/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); + histos.fill(HIST("QAbefore/k0sProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAbefore/k0sArmenteros"), v0.alpha(), v0.qtarm()); + histos.fill(HIST("QAbefore/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAbefore/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + if (!selected) { + return; + } + nV0sAfterCuts++; + histos.fill(HIST("QAafter/k0sMassPt"), v0.pt(), v0.mK0Short()); + histos.fill(HIST("QAafter/k0sPt"), v0.pt()); + histos.fill(HIST("QAafter/k0sEta"), v0.eta()); + histos.fill(HIST("QAafter/k0sCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAafter/k0sRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAafter/k0sDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAafter/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); + histos.fill(HIST("QAafter/k0sProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAafter/k0sArmenteros"), v0.alpha(), v0.qtarm()); + histos.fill(HIST("QAafter/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAafter/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAafter/k0sNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAafter/k0sNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + }; + + auto onXi = [&](auto const& xi, bool selected) { histos.fill(HIST("QAbefore/xiMass"), xi.mXi()); histos.fill(HIST("QAbefore/xiPt"), xi.pt()); histos.fill(HIST("QAbefore/xiEta"), xi.eta()); @@ -608,18 +419,10 @@ struct Omega2012Analysis { histos.fill(HIST("QAbefore/xiCascRadius"), xi.pt(), xi.cascTransRadius()); histos.fill(HIST("QAbefore/xiV0DauDCA"), xi.pt(), xi.daughDCA()); histos.fill(HIST("QAbefore/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); - - if (!cascprimaryTrackCut(xi)) - continue; - if (!casctopCut(xi)) - continue; - - // Count cascades passing cuts - if constexpr (!IsMix) { - nCascAfterCuts++; + if (!selected) { + return; } - - // QA after Xi cuts - detailed histograms + nCascAfterCuts++; histos.fill(HIST("QAafter/xiMass"), xi.mXi()); histos.fill(HIST("QAafter/xiPt"), xi.pt()); histos.fill(HIST("QAafter/xiEta"), xi.eta()); @@ -631,73 +434,28 @@ struct Omega2012Analysis { histos.fill(HIST("QAafter/xiCascRadius"), xi.pt(), xi.cascTransRadius()); histos.fill(HIST("QAafter/xiV0DauDCA"), xi.pt(), xi.daughDCA()); histos.fill(HIST("QAafter/xiCascDauDCA"), xi.pt(), xi.cascDaughDCA()); - - // Build Xi + K0s - for (const auto& v0 : v0s) { - // QA before K0s selection - detailed histograms - histos.fill(HIST("QAbefore/k0sMassPt"), v0.pt(), v0.mK0Short()); - histos.fill(HIST("QAbefore/k0sPt"), v0.pt()); - histos.fill(HIST("QAbefore/k0sEta"), v0.eta()); - histos.fill(HIST("QAbefore/k0sCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAbefore/k0sRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAbefore/k0sDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAbefore/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); - // Calculate proper lifetime manually - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + 1e-10)) * MassK0Short; - histos.fill(HIST("QAbefore/k0sProperLifetime"), v0.pt(), properLifetime); - // Skip Armenteros plot for now - requires daughter momentum info - // histos.fill(HIST("QAbefore/k0sArmenteros"), alpha, qt); - histos.fill(HIST("QAbefore/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAbefore/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - - if (!v0CutEnhanced(collision, v0)) - continue; - - // Count V0s passing cuts - if constexpr (!IsMix) { - nV0sAfterCuts++; - } - - // QA after K0s selection - detailed histograms - histos.fill(HIST("QAafter/k0sMassPt"), v0.pt(), v0.mK0Short()); - histos.fill(HIST("QAafter/k0sPt"), v0.pt()); - histos.fill(HIST("QAafter/k0sEta"), v0.eta()); - histos.fill(HIST("QAafter/k0sCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAafter/k0sRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAafter/k0sDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAafter/k0sDCAtoPV"), v0.pt(), std::abs(v0.dcav0topv())); - histos.fill(HIST("QAafter/k0sProperLifetime"), v0.pt(), properLifetime); - // Skip Armenteros plot for now - requires daughter momentum info - // histos.fill(HIST("QAafter/k0sArmenteros"), alpha, qt); - histos.fill(HIST("QAafter/k0sDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAafter/k0sDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - - // 4-vectors - ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pRes = pXi + pK0s; - - if constexpr (!IsMix) { - histos.fill(HIST("omega2012/invmass"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent"), pRes.M(), pRes.Pt(), cent); - } else { - histos.fill(HIST("omega2012/invmass_Mix"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent_Mix"), pRes.M(), pRes.Pt(), cent); + }; + + const bool kinCutsOn = core.kinCutsEnabled(); + auto onCandidate = [&](auto const& /*xi*/, auto const& /*v0*/, XiK0sCandidateValues const& c) { + if (kinCutsOn) { + histos.fill(HIST("QAbefore/omegaAlphaVsPt"), c.omega.Pt(), c.alpha); + if (!c.passesKinCut) { + return; } + histos.fill(HIST("QAafter/omegaAlphaVsPt"), c.omega.Pt(), c.alpha); } - } + if (!c.inRapidity) { + return; + } + histos.fill(HIST("omega2012/invmass"), c.omega.M()); + histos.fill(HIST("omega2012/massPtCent"), c.omega.M(), c.omega.Pt(), cent); + }; - // Fill event QA for after-cuts counters (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/nCascadesAfterCuts"), nCascAfterCuts); - histos.fill(HIST("Event/nV0sAfterCuts"), nV0sAfterCuts); - } + core.forEachXiK0sCandidate(histos, collision, collision, cascades, v0s, false, onXi, onK0s, onCandidate); + + histos.fill(HIST("Event/nCascadesAfterCuts"), nCascAfterCuts); + histos.fill(HIST("Event/nV0sAfterCuts"), nV0sAfterCuts); } void processDummy(aod::ResoCollision const& /*collision*/) @@ -706,78 +464,126 @@ struct Omega2012Analysis { } PROCESS_SWITCH(Omega2012Analysis, processDummy, "Process Dummy", true); - void processData(const aod::ResoCollision& collision, + void processData(ResoCollisions::iterator const& collision, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { - fill(collision, resocasc, resov0s); + if (!core.passesEventCuts(collision)) { + return; + } + fillXiK0s(collision, resocasc, resov0s); } PROCESS_SWITCH(Omega2012Analysis, processData, "Process Event for data", false); - void processMixedEvent(const aod::ResoCollisions& collisions, + void processMixedEvent(ResoCollisions const& collisions, aod::ResoCascades const& resocasc, aod::ResoV0s const& resov0s) { - auto cascV0sTuple = std::make_tuple(resocasc, resov0s); - Pair pairs{colBinning, nEvtMixing, -1, collisions, cascV0sTuple, &cache}; + Pair pairs{colBinning, nEvtMixing, -1, collisions, cascV0sTuple, &cache}; - for (auto& [collision1, casc1, collision2, v0s2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured binding cannot be const in this context) + const bool kinCutsOn = core.kinCutsEnabled(); + for (const auto& [collision1, casc1, collision2, v0s2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } auto cent = collision1.cent(); - - for (const auto& xi : casc1) { - if (!cascprimaryTrackCut(xi)) - continue; - if (!casctopCut(xi)) - continue; - - for (const auto& v0 : v0s2) { - if (!v0CutEnhanced(collision2, v0)) - continue; - - // 4-vectors for mixed event - ROOT::Math::PxPyPzEVector pXi, pK0s, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pRes = pXi + pK0s; - - histos.fill(HIST("omega2012/invmass_Mix"), pRes.M()); - histos.fill(HIST("omega2012/massPtCent_Mix"), pRes.M(), pRes.Pt(), cent); + // Xi from collision 1, K0s from collision 2 (selected with the vertex of collision 2) + auto onCandidate = [&](auto const& /*xi*/, auto const& /*v0*/, XiK0sCandidateValues const& c) { + if (kinCutsOn && !c.passesKinCut) { + return; } - } + if (!c.inRapidity) { + return; + } + histos.fill(HIST("omega2012/invmass_Mix"), c.omega.M()); + histos.fill(HIST("omega2012/massPtCent_Mix"), c.omega.M(), c.omega.Pt(), cent); + }; + core.forEachXiK0sCandidate(histos, collision1, collision2, casc1, v0s2, false, nullptr, nullptr, onCandidate); } } PROCESS_SWITCH(Omega2012Analysis, processMixedEvent, "Process Mixed Event", false); - // MC processes - placeholder for future implementation - void processMC(const aod::ResoCollision& /*collision*/, - aod::ResoCascades const& /*resocasc*/, - aod::ResoV0s const& /*resov0s*/, - aod::McParticles const& /*mcParticles*/) + // MC reconstructed processing: match reconstructed Xi + K0s pairs to a common Omega(2012) mother + void processMC(ResoMCCollisions::iterator const& collision, + ResoMCCascades const& resocasc, + ResoMCV0s const& resov0s, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + const auto omegaParents = omegaParentTruth(resoParents); + auto onCandidate = [&](auto const& xi, auto const& v0, XiK0sCandidateValues const& c) { + if (classifyXiK0sTruth(xi, v0) != XiK0sTruth::Matched) { + return; + } + const int motherId = xi.motherId(); + const auto parentIt = omegaParents.find(motherId); + if (parentIt == omegaParents.end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!(std::abs(xi.motherRap()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(xi.motherRap()))) { + return; + } + if ((core.kinCutsEnabled() && !c.passesKinCut) || !c.inRapidity) { + return; + } + histos.fill(HIST("MC/hMCRecOmega2012Pt"), c.omega.Pt()); + histos.fill(HIST("MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); + histos.fill(HIST("MC/hMCRecXiPt"), xi.pt()); + histos.fill(HIST("MC/hMCRecK0sPt"), v0.pt()); + const double sign = xi.motherPDG() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCRecXiK0sResponse"), c.omega.Pt(), xi.motherPt(), collision.cent(), collision.mcMultiplicity(), sign); + }; + core.forEachXiK0sCandidate(histos, collision, collision, resocasc, resov0s, false, nullptr, nullptr, onCandidate); + } + PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching", false); + + // Acceptance x efficiency denominator, counted per selected reconstructed collision, + // as in the Xi(1530) and Xi(1820) tasks. Split collisions repeat the parent set; + // this is not a unique-MC-event denominator or a signal-loss correction. + void processMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) { - // TODO: Implement MC truth matching for Xi + K0s - // - Match reconstructed Xi to MC Xi - // - Match reconstructed K0s to MC K0s - // - Fill MC truth histograms - // - Fill reconstruction efficiency histograms - // - Check if the Xi and K0s come from same Omega(2012) mother + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + for (const auto& part : resoParents) { + if (std::abs(part.pdgCode()) != kOmega2012Minus) { + continue; + } + if (!(std::abs(part.y()) < candidateCuts.cfgRapidityCut.value) || !std::isfinite(part.y())) { + continue; + } + if (cMCInjection && !isInjectedOmegaParent(part)) { + continue; + } + // Require the same immediate Xi + K0S channel as processMCGenerated. + if ((std::abs(part.daughterPDG1()) == kXiMinus && part.daughterPDG2() == kK0Short) || + (std::abs(part.daughterPDG2()) == kXiMinus && part.daughterPDG1() == kK0Short)) { + histos.fill(HIST("MC/hMCGenSelectedXiK0sPt"), part.pt()); + const double sign = part.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("MC/hMCGenSelectedXiK0sResponse"), part.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } + } } - PROCESS_SWITCH(Omega2012Analysis, processMC, "Process MC with truth matching (placeholder)", false); + PROCESS_SWITCH(Omega2012Analysis, processMCGen, "Process MC generated XiK0s parents in selected events", false); + // Inclusive MC-particle QA. The rapidity-limited source histogram is a legacy all-event QA denominator; + // selected-event efficiency denominators are filled by processMCGen and processXi1530KMCGen. void processMCGenerated(aod::McParticles const& mcParticles) { // Process MC generated particles (no reconstruction requirement) - // Omega(2012) PDG code: Need to check - likely custom or using generator-specific codes // This resonance decays to Xi + K0s for (const auto& mcParticle : mcParticles) { - // Look for Omega(2012) - PDG code may vary by generator + // Look for Omega(2012) int pdg = mcParticle.pdgCode(); - // TODO: Determine correct PDG code for Omega(2012) - // Placeholder check - update with correct PDG code - if (std::abs(pdg) != kPlaceholderPdgCode) // o2-linter: disable=pdg/explicit-code (placeholder for generator-specific PDG code) + if (std::abs(pdg) != kOmega2012Minus) { continue; + } // Fill generated level histograms auto pt = mcParticle.pt(); @@ -790,8 +596,9 @@ struct Omega2012Analysis { // Get daughters auto daughters = mcParticle.daughters_as(); - if (daughters.size() != kNumExpectedDaughters) + if (daughters.size() != NumExpectedDaughters) { continue; + } int daughter1PDG = 0, daughter2PDG = 0; ROOT::Math::PxPyPzEVector p1, p2, pMother; @@ -818,154 +625,212 @@ struct Omega2012Analysis { if ((std::abs(daughter1PDG) == kXiMinus && daughter2PDG == kK0Short) || (std::abs(daughter2PDG) == kXiMinus && daughter1PDG == kK0Short)) { histos.fill(HIST("MC/hMCTruthInvMassXiK0s"), motherM); - histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + // Legacy all-event rapidity-limited QA histogram; selected-event efficiency + // denominators are written by processMCGen and processXi1530KMCGen. + if (std::abs(y) < candidateCuts.cfgRapidityCut.value) { + histos.fill(HIST("MC/hMCTruthMassPtXiK0s"), motherM, motherPt); + } + + const bool isDaughter1Xi = std::abs(daughter1PDG) == kXiMinus; + const auto& pXiTruth = isDaughter1Xi ? p1 : p2; + const auto& pK0sTruth = isDaughter1Xi ? p2 : p1; + histos.fill(HIST("MC/hMCTrueXiPt"), pXiTruth.Pt()); + histos.fill(HIST("MC/hMCTrueK0sPt"), pK0sTruth.Pt()); } } } - PROCESS_SWITCH(Omega2012Analysis, processMCGenerated, "Process MC generated particles (placeholder)", false); + PROCESS_SWITCH(Omega2012Analysis, processMCGenerated, "Process MC generated particles", false); - // Fill function for 3-body decay analysis - template - void fillThreeBody(const CollisionT& collision, const CascadesT& cascades, const V0sT& v0s, const TracksT& tracks) + // Mode B: Xi(1530)0 K- -> Xi- pi+ K- (and charge conjugate) from one collision, or Xi and tracks from two mixed collisions + template + void fillXi1530K(const CollisionT& collision, float cent, const CascadesT& cascades, const TracksT& tracks, const TrackIdsT& trackIds, + const OmegaParentTruthMap* omegaParents = nullptr) { - auto cent = collision.cent(); - - // Collect track IDs used in xi and v0 construction to exclude them from pion selection - std::set usedTrackIds; - - // Collect track IDs from xi cascades - for (const auto& xi : cascades) { - if (!cascprimaryTrackCut(xi)) - continue; - if (!casctopCut(xi)) - continue; - - // Add xi daughter track IDs from cascadeIndices array (ordered: positive, negative, bachelor) - auto cascIndices = xi.cascadeIndices(); - usedTrackIds.insert(cascIndices[0]); // positive track - usedTrackIds.insert(cascIndices[1]); // negative track - usedTrackIds.insert(cascIndices[2]); // bachelor track - } - - // Collect track IDs from v0s - for (const auto& v0 : v0s) { - if (!v0CutEnhanced(collision, v0)) - continue; - - // Add v0 daughter track IDs from indices array - auto v0Indices = v0.indices(); - usedTrackIds.insert(v0Indices[0]); // positive track - usedTrackIds.insert(v0Indices[1]); // negative track - } - - // First loop: xi + pion to check xi1530 mass window - for (const auto& xi : cascades) { - if (!cascprimaryTrackCut(xi)) - continue; - if (!casctopCut(xi)) - continue; - - for (const auto& pion : tracks) { - // Skip pion tracks that are already used in xi construction - if (usedTrackIds.find(pion.globalIndex()) != usedTrackIds.end()) { - continue; + // Input QA: same event only (each object once per collision) + auto onXi = [&](auto const& xi, bool selected) { + if constexpr (IsMix) { + return; + } + histos.fill(HIST("xi1530K/QAbefore/xiMass"), xi.mXi()); + histos.fill(HIST("xi1530K/QAbefore/xiPt"), xi.pt()); + histos.fill(HIST("xi1530K/QAbefore/xiEta"), xi.eta()); + if (!selected) { + return; + } + histos.fill(HIST("xi1530K/QAafter/xiMass"), xi.mXi()); + histos.fill(HIST("xi1530K/QAafter/xiPt"), xi.pt()); + histos.fill(HIST("xi1530K/QAafter/xiEta"), xi.eta()); + }; + + auto onTrack = [&](auto const& track, int pionStage, int kaonStage) { + if constexpr (IsMix) { + return; + } + const bool hasTOF = track.hasTOF(); + histos.fill(HIST("xi1530K/QAbefore/trackPt"), track.pt()); + histos.fill(HIST("xi1530K/QAbefore/trackEta"), track.eta()); + histos.fill(HIST("xi1530K/QAbefore/trackDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAbefore/trackDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAbefore/pionTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + histos.fill(HIST("xi1530K/QAbefore/kaonTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAbefore/pionTOFNSigma"), track.pt(), track.tofNSigmaPi()); + histos.fill(HIST("xi1530K/QAbefore/kaonTOFNSigma"), track.pt(), track.tofNSigmaKa()); + } + if (pionStage == o2::analysis::resonance::kTrkPID) { + histos.fill(HIST("xi1530K/QAafter/pionPt"), track.pt()); + histos.fill(HIST("xi1530K/QAafter/pionEta"), track.eta()); + histos.fill(HIST("xi1530K/QAafter/pionDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAafter/pionDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAafter/pionTPCNSigma"), track.pt(), track.tpcNSigmaPi()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAafter/pionTOFNSigma"), track.pt(), track.tofNSigmaPi()); } - - // Pion QA before cuts - histos.fill(HIST("QAbefore/pionPt"), pion.pt()); - histos.fill(HIST("QAbefore/pionEta"), pion.eta()); - - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAbefore/pionDCAxy"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(pion.trackSelectionFlags())); - histos.fill(HIST("QAbefore/pionDCAz"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(pion.trackSelectionFlags())); - histos.fill(HIST("QAbefore/pionTPCNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(pion.pidNSigmaPiFlag())); - if (pion.hasTOF()) { - histos.fill(HIST("QAbefore/pionTOFNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(pion.pidNSigmaPiFlag())); - } - } else { - histos.fill(HIST("QAbefore/pionDCAxy"), pion.pt(), pion.dcaXY()); - histos.fill(HIST("QAbefore/pionDCAz"), pion.pt(), pion.dcaZ()); - histos.fill(HIST("QAbefore/pionTPCNSigma"), pion.pt(), pion.tpcNSigmaPi()); - if (pion.hasTOF()) { - histos.fill(HIST("QAbefore/pionTOFNSigma"), pion.pt(), pion.tofNSigmaPi()); - } - if constexpr (requires { pion.tpcNClsFound(); }) { - histos.fill(HIST("QAbefore/pionTPCNcls"), pion.tpcNClsFound()); - } + } + if (kaonStage == o2::analysis::resonance::kTrkPID) { + histos.fill(HIST("xi1530K/QAafter/kaonPt"), track.pt()); + histos.fill(HIST("xi1530K/QAafter/kaonEta"), track.eta()); + histos.fill(HIST("xi1530K/QAafter/kaonDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("xi1530K/QAafter/kaonDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("xi1530K/QAafter/kaonTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (hasTOF) { + histos.fill(HIST("xi1530K/QAafter/kaonTOFNSigma"), track.pt(), track.tofNSigmaKa()); } + } + }; - if (!pionCut(pion)) - continue; - - // Pion QA after cuts - histos.fill(HIST("QAafter/pionPt"), pion.pt()); - histos.fill(HIST("QAafter/pionEta"), pion.eta()); - - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAafter/pionDCAxy"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(pion.trackSelectionFlags())); - histos.fill(HIST("QAafter/pionDCAz"), pion.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(pion.trackSelectionFlags())); - histos.fill(HIST("QAafter/pionTPCNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(pion.pidNSigmaPiFlag())); - if (pion.hasTOF()) { - histos.fill(HIST("QAafter/pionTOFNSigma"), pion.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(pion.pidNSigmaPiFlag())); - } - } else { - histos.fill(HIST("QAafter/pionDCAxy"), pion.pt(), pion.dcaXY()); - histos.fill(HIST("QAafter/pionDCAz"), pion.pt(), pion.dcaZ()); - histos.fill(HIST("QAafter/pionTPCNSigma"), pion.pt(), pion.tpcNSigmaPi()); - if (pion.hasTOF()) { - histos.fill(HIST("QAafter/pionTOFNSigma"), pion.pt(), pion.tofNSigmaPi()); - } - if constexpr (requires { pion.tpcNClsFound(); }) { - histos.fill(HIST("QAafter/pionTPCNcls"), pion.tpcNClsFound()); + const bool fillWrongSign = core.fillWrongSign(); + auto onCandidate = [&]([[maybe_unused]] auto const& xi, [[maybe_unused]] auto const& pion, [[maybe_unused]] auto const& kaon, Xi1530KCandidateValues const& c) { + if (c.chargePattern != o2::analysis::omega2012ml::kSignalPattern) { + // Charge-pattern controls: same event only, never in the signal histograms + if constexpr (!IsMix) { + if (fillWrongSign && c.inRapidity) { + histos.fill(HIST("xi1530K_wrongSign/invmassPattern"), c.chargePattern, c.omega.M()); + histos.fill(HIST("xi1530K_wrongSign/massPtPattern"), c.omega.M(), c.omega.Pt(), c.chargePattern); } } + return; + } + if constexpr (IsMix) { + if (c.inRapidity) { + histos.fill(HIST("xi1530K/invmass_Mix"), c.omega.M()); + histos.fill(HIST("xi1530K/massPtCent_Mix"), c.omega.M(), c.omega.Pt(), cent); + } + return; + } + histos.fill(HIST("xi1530K/massXiPi"), c.massXiPi); + if (!c.inRapidity) { + return; + } + histos.fill(HIST("xi1530K/invmass"), c.omega.M()); + histos.fill(HIST("xi1530K/massPtCent"), c.omega.M(), c.omega.Pt(), cent); + histos.fill(HIST("xi1530K/massXiPiVsMass"), c.omega.M(), c.massXiPi); + if constexpr (IsMC) { + if (classifyXi1530KTruth(xi, pion, kaon) != Xi1530KTruth::Matched) { + return; + } + if (!omegaParents) { + return; + } + const auto parentIt = omegaParents->find(kaon.motherId()); + if (parentIt == omegaParents->end() || (cMCInjection && !parentIt->second.injected)) { + return; + } + if (cUseTruthRapidity && (!std::isfinite(parentIt->second.y) || !(std::abs(parentIt->second.y) < candidateCuts.cfgRapidityCut.value))) { + return; + } + histos.fill(HIST("xi1530K/MC/hMCRecOmega2012Pt"), c.omega.Pt()); + histos.fill(HIST("xi1530K/MC/hMCRecOmega2012PtEta"), c.omega.Pt(), c.omega.Eta()); + histos.fill(HIST("xi1530K/MC/hMCRecMass"), c.omega.M()); + histos.fill(HIST("xi1530K/MC/hMCRecMassXiPi"), c.massXiPi); + histos.fill(HIST("xi1530K/MC/hMCRecXiPt"), xi.pt()); + histos.fill(HIST("xi1530K/MC/hMCRecPionPt"), pion.pt()); + histos.fill(HIST("xi1530K/MC/hMCRecKaonPt"), kaon.pt()); + const double sign = parentIt->second.pdgCode > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCRecXi1530KResponse"), c.omega.Pt(), parentIt->second.pt, cent, collision.mcMultiplicity(), sign); + } + }; - // Check xi1530 mass window cut - if (!xi1530MassCut(xi, pion)) - continue; - - // Second loop: v0 for the selected xi-pion pair - for (const auto& v0 : v0s) { - if (!v0CutEnhanced(collision, v0)) - continue; + core.forEachXi1530KCandidate(histos, collision, cascades, tracks, trackIds, false, onXi, onTrack, onCandidate); + } - // 4-vectors for 3-body decay: Xi + K0s + pion - ROOT::Math::PxPyPzEVector pXi, pK0s, pPion, pRes; - pXi = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(xi.pt(), xi.eta(), xi.phi(), xi.mXi())); - pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), massK0)); - pPion = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(pion.pt(), pion.eta(), pion.phi(), MassPionCharged)); + void processXi1530KMicro(ResoCollisions::iterator const& collision, + aod::ResoCascades const& resocasc, + ResoMicroTracks const& resomicrotracks) + { + if (!core.passesEventCuts(collision)) { + return; + } + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr); + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMicro, "Process Xi(1530)0 K mode with ResoMicroTracks_001", false); - pRes = pXi + pK0s + pPion; + void processXi1530KTracks(ResoCollisions::iterator const& collision, + aod::ResoCascades const& resocasc, + aod::ResoTracks const& resotracks, + aod::ResoTrackTracks const& resotrackids) + { + if (!core.passesEventCuts(collision)) { + return; + } + fillXi1530K(collision, collision.cent(), resocasc, resotracks, resotrackids); + } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KTracks, "Process Xi(1530)0 K mode with ResoTracks", false); - histos.fill(HIST("omega2012_3body/invmass"), pRes.M()); - histos.fill(HIST("omega2012_3body/massPtCent"), pRes.M(), pRes.Pt(), cent); - } - } + void processXi1530KMCMicro(ResoMCCollisions::iterator const& collision, + ResoMCCascades const& resocasc, + ResoMCMicroTracks const& resomicrotracks, + aod::ResoMCParents_001 const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; } + const auto omegaParents = omegaParentTruth(resoParents); + fillXi1530K(collision, collision.cent(), resocasc, resomicrotracks, nullptr, &omegaParents); } + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCMicro, "Process Xi(1530)0 K mode with truth matching (ResoMicroTracks_001)", false); - // 3-body decay analysis: Xi + pi + K0s with ResoTracks - void processThreeBodyWithTracks(const aod::ResoCollision& collision, - aod::ResoCascades const& resocasc, - aod::ResoV0s const& resov0s, - aod::ResoTracks const& resotracks) + // Selected-event generated denominator for Omega -> Xi(1530) K, counted once + // per selected reconstructed collision. Split collisions repeat the MC parents. + void processXi1530KMCGen(ResoMCCollisions::iterator const& collision, + aod::ResoMCParents_001 const& resoParents) { - fillThreeBody(collision, resocasc, resov0s, resotracks); + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + for (const auto& parent : resoParents) { + if (std::abs(parent.pdgCode()) != kOmega2012Minus || + !std::isfinite(parent.y()) || !(std::abs(parent.y()) < candidateCuts.cfgRapidityCut.value) || + (cMCInjection && !isInjectedOmegaParent(parent))) { + continue; + } + if (classifyGeneratedOmega2012(parent.pdgCode(), parent.daughterPDG1(), parent.daughterPDG2()) != GeneratedChannel::Xi1530K) { + continue; + } + const double sign = parent.pdgCode() > 0 ? 1. : -1.; + histos.fill(HIST("xi1530K/MC/hMCGenSelectedXi1530KResponse"), parent.pt(), collision.cent(), collision.mcMultiplicity(), sign); + } } - PROCESS_SWITCH(Omega2012Analysis, processThreeBodyWithTracks, "Process 3-body decay with ResoTracks", false); + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMCGen, "Process generated Xi(1530) K parents in selected events", false); - // 3-body decay analysis: Xi + pi + K0s with ResoMicroTracks - void processThreeBodyWithMicroTracks(const aod::ResoCollision& collision, - aod::ResoCascades const& resocasc, - aod::ResoV0s const& resov0s, - aod::ResoMicroTracks const& resomicrotracks) + // Mixed events: Xi from collision 1, pion and kaon from collision 2 + void processXi1530KMixedMicro(ResoCollisions const& collisions, + aod::ResoCascades const& resocasc, + ResoMicroTracks const& resomicrotracks) { - fillThreeBody(collision, resocasc, resov0s, resomicrotracks); + auto cascTracksTuple = std::make_tuple(resocasc, resomicrotracks); + Pair pairs{colBinning, nEvtMixing, -1, collisions, cascTracksTuple, &cache}; + for (const auto& [collision1, casc1, collision2, tracks2] : pairs) { + if (!core.passesEventCuts(collision1) || !core.passesEventCuts(collision2)) { + continue; + } + fillXi1530K(collision1, collision1.cent(), casc1, tracks2, nullptr); + } } - PROCESS_SWITCH(Omega2012Analysis, processThreeBodyWithMicroTracks, "Process 3-body decay with ResoMicroTracks", false); + PROCESS_SWITCH(Omega2012Analysis, processXi1530KMixedMicro, "Process mixed events of the Xi(1530)0 K mode (ResoMicroTracks_001)", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx new file mode 100644 index 00000000000..d6c7eb317a3 --- /dev/null +++ b/PWGLF/Tasks/Resonances/omega2012TrainingTable.cxx @@ -0,0 +1,593 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file omega2012TrainingTable.cxx +/// \brief Derived Omega(2012) training tables, separately for the XiK0s and Xi1530K decay modes +/// \author Bong-Hwi Lim +/// +/// Mode A (XiK0s): processXiK0s, processXiK0sMC. Mode B (Xi1530K, Xi- pi+ K- with micro tracks): +/// processXi1530KMicro, processXi1530KMCMicro. Generated parents: processMCTrue. MC truth of reconstructed MC: processMcTruth. +/// Each candidate is written only to the tables of its own mode (PWGLF/DataModel/LFOmega2012MlTables.h); +/// the selection is the one of omega2012Analysis.cxx (PWGLF/Core/Omega2012AnalysisCore.h, same JSON keys). +/// Only collisions with written candidates get an event row. Generated Omega(2012) mothers are written separately to +/// OMMLGENAUDIT for every selected reconstructed MC collision, including collisions with no exported candidates. +/// In reconstructed MC, daughter labels of written objects and pruned McParticles rows carry candidate truth, so the +/// candidate output needs no source table. + +#include "PWGLF/Core/Omega2012AnalysisCore.h" +#include "PWGLF/Core/Omega2012MlFeatures.h" +#include "PWGLF/Core/ResoAnalysisSelectionCore.h" +#include "PWGLF/DataModel/LFOmega2012MlTables.h" +#include "PWGLF/DataModel/LFResonanceTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::analysis::omega2012; + +namespace ml = o2::analysis::omega2012ml; + +static_assert(std::extent_v == ml::XiK0sNMasterFeatures, + "OmXiK0sFeatures column size must match the XiK0s feature contract"); +static_assert(std::extent_v == ml::Xi1530KNMasterFeatures, + "OmXi1530KFeatures column size must match the Xi1530K feature contract"); + +struct Omega2012TrainingTable { + using ResoCollisions = aod::ResoCollisions_001; + using ResoMCCols = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCMicroTracks = soa::Join; + using ResoMCCascades = soa::Join; + using ResoMCV0s = soa::Join; + using ResoMCParents = aod::ResoMCParents_001; + + // FNV-1a over the bit patterns of the master features, for parity logs + static constexpr uint64_t FnvOffsetBasis = 14695981039346656037ULL; + static constexpr uint64_t FnvPrime = 1099511628211ULL; + static constexpr unsigned int BitsPerByte = 8; + static constexpr unsigned int BitsPerFloat = 32; + static constexpr uint32_t ByteMask = 0xffU; + static constexpr int NBuildStatus = 6; + // Pruned McParticles: generations of Omega(2012) descendants and mother steps above a daughter label + static constexpr int McTreeDepth = 3; + static constexpr int MaxAncestorSteps = 8; + static constexpr uint8_t McSelectionTree = 1; + static constexpr uint8_t McSelectionAncestry = 2; + static constexpr std::size_t NStoredMothers = 2; + + Produces mlEvents; + Produces mlCascades; + Produces mlV0s; + Produces mlTracks; + Produces mlXiK0sCandidates; + Produces mlXiK0sInputs; + Produces mlXiK0sTruth; + Produces mlXi1530KCandidates; + Produces mlXi1530KInputs; + Produces mlXi1530KTruth; + Produces mlGenAudit; + Produces mlEventMcs; + Produces mlCascadeMcs; + Produces mlV0Mcs; + Produces mlTrackMcs; + Produces mlMcParticles; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Selection shared with the Omega(2012) histogram task (same JSON keys) + o2::analysis::resonance::EventCuts eventCuts = makeOmega2012EventCuts(); + XiCuts xiCuts; + K0sCuts k0sCuts; + Xi1530KCuts xi1530KCuts; + o2::analysis::resonance::TrackCuts trackCuts = makeXi1530KTrackCuts(); + PionPidCuts pionPID; + KaonPidCuts kaonPID; + CandidateCuts candidateCuts; + + Configurable omega2012MlExportStage{"omega2012MlExportStage", "loose", "Candidate export stage: loose (pass bits >= 1) or selected (all pass bits of the mode)"}; + Configurable omega2012MlLooseAudit{"omega2012MlLooseAudit", true, "Record the loose cut flow and mass/pT/activity spectrum per mode"}; + Configurable omega2012MlParityRows{"omega2012MlParityRows", 0, "Log feature hashes of the first N exported candidates of each mode"}; + Configurable omega2012MlExportWrongSign{"omega2012MlExportWrongSign", false, "Also export the wrong-sign charge-pattern controls of the Xi1530K mode"}; + Configurable omega2012MlGenRapidityCut{"omega2012MlGenRapidityCut", 0.5f, "Generated-mother denominator rapidity limit; independent of candidate cfgRapidityCut"}; + + // Event row written before the first candidate of the collision + struct PendingEvent { + DecayMode mode = DecayMode::XiK0s; + int64_t recoCollision = -1; + float posZ = 0.f; + float bField = 0.f; + float centrality = 0.f; + float multiplicity = 0.f; + bool recINELgt0 = false; + bool isMC = false; + int64_t mcCollision = -1; + }; + + Omega2012AnalysisCore core; + PendingEvent pendingEvent; + int64_t mlEventRow = -1; + // MC tables of the current DF, set only by the reconstructed-MC process functions + aod::McParticles const* mcParticlesDf = nullptr; + aod::McTrackLabels const* mcTrackLabelsDf = nullptr; + std::unordered_set mlAncestryRows; + std::unordered_map mlCascadeRows; + std::unordered_map mlV0Rows; + std::unordered_map mlTrackRows; + int parityLoggedXiK0s = 0; + int parityLoggedXi1530K = 0; + + void init(InitContext&) + { + const bool xiK0s = doprocessXiK0s || doprocessXiK0sMC; + const bool xi1530K = doprocessXi1530KMicro || doprocessXi1530KMCMicro; + if ((doprocessXiK0s && doprocessXiK0sMC) || (doprocessXi1530KMicro && doprocessXi1530KMCMicro)) { + LOG(fatal) << "Enable at most one of processXiK0s/processXiK0sMC and one of processXi1530KMicro/processXi1530KMCMicro"; + } + if (!xiK0s && !xi1530K) { + LOG(fatal) << "Enable at least one candidate process function"; + } + if (doprocessMCTrue && !doprocessXiK0sMC && !doprocessXi1530KMCMicro) { + LOG(fatal) << "processMCTrue requires processXiK0sMC or processXi1530KMCMicro"; + } + if (doprocessMcTruth != (doprocessXiK0sMC || doprocessXi1530KMCMicro)) { + LOG(fatal) << "processMcTruth must be enabled exactly when processXiK0sMC or processXi1530KMCMicro is enabled"; + } + if (omega2012MlParityRows < 0) { + LOG(fatal) << "omega2012MlParityRows must not be negative"; + } + if (doprocessMCTrue && (!std::isfinite(omega2012MlGenRapidityCut.value) || omega2012MlGenRapidityCut.value <= 0.f)) { + LOG(fatal) << "omega2012MlGenRapidityCut must be finite and positive when processMCTrue is enabled"; + } + if (omega2012MlExportStage.value != "loose" && omega2012MlExportStage.value != "selected") { + LOG(fatal) << "omega2012MlExportStage must be loose or selected"; + } + + ProcessModes modes; + modes.xiK0s = xiK0s; + modes.xi1530K = xi1530K; + modes.microTracks = xi1530K; + modes.mcReco = doprocessXiK0sMC || doprocessXi1530KMCMicro; + modes.mcGen = doprocessMCTrue; + LooseStageOptions looseOptions; + looseOptions.audit = omega2012MlLooseAudit; + looseOptions.exportSelected = omega2012MlExportStage.value == "selected"; + core.init(histos, eventCuts, xiCuts, k0sCuts, xi1530KCuts, trackCuts, pionPID, kaonPID, candidateCuts, modes, looseOptions); + + // Candidates that violate the canonical or feature contract are skipped, never written. + const std::array statusLabels{"Ok", "InvalidChargePattern", "ReusedDaughter", "InvalidMomentum", "InvalidKinematics", "InvalidContract"}; + if (xiK0s) { + auto skipped = histos.add("ML/xiK0s/exportSkipped", "XiK0s mode skipped candidates;build status;candidates", HistType::kTH1D, {{NBuildStatus, -0.5, NBuildStatus - 0.5}}); + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + } + if (xi1530K) { + auto skipped = histos.add("ML/xi1530K/exportSkipped", "Xi1530K mode skipped candidates;build status;candidates", HistType::kTH1D, {{NBuildStatus, -0.5, NBuildStatus - 0.5}}); + for (std::size_t i = 0; i < statusLabels.size(); ++i) { + skipped->GetXaxis()->SetBinLabel(i + 1, statusLabels[i]); + } + histos.add("ML/xi1530K/chargePattern", "Xi1530K mode candidates handed to the export;charge pattern (0 signal);candidates", HistType::kTH1D, {{4, -0.5, 3.5}}); + } + + LOG(info) << "Size of the histograms in Omega(2012) training table task"; + histos.print(); + } + + template + static uint64_t featureHash(std::array const& features) + { + uint64_t hash = FnvOffsetBasis; + for (const auto& value : features) { + const auto bits = std::bit_cast(value); + for (unsigned int shift = 0; shift < BitsPerFloat; shift += BitsPerByte) { + hash = (hash ^ ((bits >> shift) & ByteMask)) * FnvPrime; + } + } + return hash; + } + + template + void beginEvent(Collision const& collision, DecayMode mode) + { + mlCascadeRows.clear(); + mlV0Rows.clear(); + mlTrackRows.clear(); + mlEventRow = -1; + // ResoCollisions_001 carries no run number or BC; the reduced collision row identifies the event within its DF. + pendingEvent = PendingEvent{mode, static_cast(collision.globalIndex()), collision.posZ(), collision.bMagField(), + collision.cent(), collision.multiplicity(), collision.isRecINELgt0(), false, -1}; + if constexpr (requires { collision.originalMcCollisionId(); }) { + pendingEvent.isMC = true; + pendingEvent.mcCollision = static_cast(collision.originalMcCollisionId()); + } + } + + void ensureEventRow() + { + if (mlEventRow >= 0) { + return; + } + mlEvents(static_cast(pendingEvent.mode), pendingEvent.recoCollision, pendingEvent.posZ, pendingEvent.bField, + pendingEvent.centrality, pendingEvent.multiplicity, pendingEvent.recINELgt0); + mlEventRow = mlEvents.lastIndex(); + if (pendingEvent.isMC) { + mlEventMcs(mlEventRow, pendingEvent.mcCollision); + } + } + + template + void writeMcParticle(Particle const& particle, uint8_t selection) + { + const auto mothers = particle.mothersIds(); + std::array motherRows{-1, -1}; + for (std::size_t i = 0; i < NStoredMothers && i < mothers.size(); ++i) { + motherRows[i] = mothers[i]; + } + const auto daughters = particle.daughtersIds(); + std::array daughterRows{daughters[0], daughters[1]}; + const auto nMothers = static_cast(std::min(mothers.size(), std::numeric_limits::max())); + mlMcParticles(static_cast(particle.globalIndex()), selection, particle.pdgCode(), static_cast(particle.mcCollisionId()), + nMothers, motherRows.data(), daughterRows.data(), particle.px(), particle.py(), particle.pz(), particle.e(), particle.weight()); + } + + // McParticles row of a source track, -1 without MC tables or label + int mcLabel(int64_t trackId) const + { + if (mcTrackLabelsDf == nullptr) { + return -1; + } + const auto row = trackId - static_cast(mcTrackLabelsDf->offset()); + if (row < 0 || row >= mcTrackLabelsDf->size()) { + return -1; + } + return mcTrackLabelsDf->rawIteratorAt(row).mcParticleId(); + } + + // Writes the label particle and its first-mother chain, once per DF + void writeAncestry(int label) + { + if (mcParticlesDf == nullptr) { + return; + } + int64_t row = label; + for (int step = 0; step <= MaxAncestorSteps; ++step) { + const auto local = row - static_cast(mcParticlesDf->offset()); + if (row < 0 || local < 0 || local >= mcParticlesDf->size()) { + return; + } + const auto particle = mcParticlesDf->rawIteratorAt(local); + if (mlAncestryRows.insert(row).second) { + writeMcParticle(particle, McSelectionAncestry); + } + const auto mothers = particle.mothersIds(); + if (mothers.empty()) { + return; + } + row = mothers[0]; + } + } + + template + int64_t writeCascade(Cascade const& xi) + { + const auto id = static_cast(xi.globalIndex()); + if (auto it = mlCascadeRows.find(id); it != mlCascadeRows.end()) { + return it->second; + } + const auto& indices = xi.cascadeIndices(); + const std::array daughterIds{indices[0], indices[1], indices[2]}; + mlCascades(mlEventRow, id, xi.px(), xi.py(), xi.pz(), static_cast(xi.sign()), xi.mXi(), xi.mLambda(), + xi.v0CosPA(), xi.cascCosPA(), xi.daughDCA(), xi.cascDaughDCA(), + xi.dcapostopv(), xi.dcanegtopv(), xi.dcabachtopv(), xi.dcav0topv(), xi.dcaXYCascToPV(), xi.dcaZCascToPV(), + xi.transRadius(), xi.cascTransRadius(), xi.decayVtxX(), xi.decayVtxY(), xi.decayVtxZ(), + xi.daughterTPCNSigmaPosPi10(), xi.daughterTPCNSigmaPosKa10(), xi.daughterTPCNSigmaPosPr10(), + xi.daughterTPCNSigmaNegPi10(), xi.daughterTPCNSigmaNegKa10(), xi.daughterTPCNSigmaNegPr10(), + xi.daughterTPCNSigmaBachPi10(), xi.daughterTPCNSigmaBachKa10(), xi.daughterTPCNSigmaBachPr10(), + xi.daughterTOFNSigmaPosPi10(), xi.daughterTOFNSigmaPosKa10(), xi.daughterTOFNSigmaPosPr10(), + xi.daughterTOFNSigmaNegPi10(), xi.daughterTOFNSigmaNegKa10(), xi.daughterTOFNSigmaNegPr10(), + xi.daughterTOFNSigmaBachPi10(), xi.daughterTOFNSigmaBachKa10(), xi.daughterTOFNSigmaBachPr10(), + xi.nCrossedRowsPos(), xi.nCrossedRowsNeg(), xi.nCrossedRowsBach(), daughterIds.data()); + const auto row = mlCascades.lastIndex(); + mlCascadeRows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + std::array labels{}; + for (std::size_t i = 0; i < labels.size(); ++i) { + labels[i] = mcLabel(daughterIds[i]); + writeAncestry(labels[i]); + } + mlCascadeMcs(row, labels.data()); + } + return row; + } + + template + int64_t writeV0(V0 const& v0) + { + const auto id = static_cast(v0.globalIndex()); + if (auto it = mlV0Rows.find(id); it != mlV0Rows.end()) { + return it->second; + } + const auto& indices = v0.indices(); + const std::array daughterIds{indices[0], indices[1]}; + mlV0s(mlEventRow, id, v0.px(), v0.py(), v0.pz(), v0.mK0Short(), v0.mLambda(), v0.mAntiLambda(), + v0.v0CosPA(), v0.daughDCA(), v0.dcapostopv(), v0.dcanegtopv(), v0.dcav0topv(), v0.transRadius(), + v0.decayVtxX(), v0.decayVtxY(), v0.decayVtxZ(), v0.alpha(), v0.qtarm(), + v0.daughterTPCNSigmaPosPi10(), v0.daughterTPCNSigmaPosKa10(), v0.daughterTPCNSigmaPosPr10(), + v0.daughterTPCNSigmaNegPi10(), v0.daughterTPCNSigmaNegKa10(), v0.daughterTPCNSigmaNegPr10(), + v0.daughterTOFNSigmaPosPi10(), v0.daughterTOFNSigmaPosKa10(), v0.daughterTOFNSigmaPosPr10(), + v0.daughterTOFNSigmaNegPi10(), v0.daughterTOFNSigmaNegKa10(), v0.daughterTOFNSigmaNegPr10(), + v0.nCrossedRowsPos(), v0.nCrossedRowsNeg(), daughterIds.data()); + const auto row = mlV0s.lastIndex(); + mlV0Rows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + std::array labels{}; + for (std::size_t i = 0; i < labels.size(); ++i) { + labels[i] = mcLabel(daughterIds[i]); + writeAncestry(labels[i]); + } + mlV0Mcs(row, labels.data()); + } + return row; + } + + template + int64_t writeTrack(Track const& track) + { + const auto id = static_cast(track.globalIndex()); + if (auto it = mlTrackRows.find(id); it != mlTrackRows.end()) { + return it->second; + } + mlTracks(mlEventRow, static_cast(track.trackId()), track.px(), track.py(), track.pz(), + track.pidNSigmaPiFlag(), track.pidNSigmaKaFlag(), track.pidNSigmaPrFlag(), + track.trackSelectionFlags(), track.trackFlags(), track.tpcNClsCrossedRows(), track.itsClusterMap()); + const auto row = mlTracks.lastIndex(); + mlTrackRows.emplace(id, row); + if (mcTrackLabelsDf != nullptr) { + const auto label = mcLabel(static_cast(track.trackId())); + writeAncestry(label); + mlTrackMcs(row, label); + } + return row; + } + + template + void writeXiK0sCandidate(Collision const& collision, Cascade const& xi, V0 const& v0, XiK0sCandidateValues const& values, uint16_t passBits) + { + const auto canonical = ml::canonicalizeXiK0s(ml::makeCascadeSnapshot(collision, xi), ml::makeV0Snapshot(collision, v0)); + if (canonical.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xiK0s/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = ml::buildXiK0sFeatures(canonical.candidate); + if (pack.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xiK0s/exportSkipped"), static_cast(pack.status)); + return; + } + if (parityLoggedXiK0s < omega2012MlParityRows) { + LOGP(info, "OMEGA2012MLPARITY mode=XiK0s collision={} cascade={} v0={} passBits={} featureHash={}", + collision.globalIndex(), xi.globalIndex(), v0.globalIndex(), passBits, featureHash(pack.master)); + ++parityLoggedXiK0s; + } + ensureEventRow(); + const auto cascadeRow = writeCascade(xi); + const auto v0Row = writeV0(v0); + mlXiK0sCandidates(mlEventRow, cascadeRow, v0Row, + static_cast(values.omega.M()), static_cast(values.omega.Pt()), + static_cast(values.omega.Rapidity()), static_cast(values.omega.Eta()), + static_cast(values.omega.Phi()), values.alpha, static_cast(xi.sign()), passBits); + const auto row = mlXiK0sCandidates.lastIndex(); + mlXiK0sInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = classifyXiK0sTruth(xi, v0) == XiK0sTruth::Matched; + mlXiK0sTruth(row, matched ? uint8_t{1} : uint8_t{2}, matched ? xi.motherPDG() : 0, + matched ? static_cast(xi.motherId()) : int64_t{-1}, v0.motherPDG(), xi.motherPDG()); + } else { + mlXiK0sTruth(row, uint8_t{0}, 0, int64_t{-1}, 0, 0); + } + } + + template + void writeXi1530KCandidate(Collision const& collision, Cascade const& xi, Track const& pion, Track const& kaon, + Xi1530KCandidateValues const& values, uint16_t passBits) + { + histos.fill(HIST("ML/xi1530K/chargePattern"), values.chargePattern); + if (values.chargePattern != ml::kSignalPattern && !omega2012MlExportWrongSign) { + return; // charge-pattern controls are exported only on request + } + const auto canonical = ml::canonicalizeXi1530K(ml::makeCascadeSnapshot(collision, xi), ml::makeTrackSnapshot(pion), ml::makeTrackSnapshot(kaon)); + if (canonical.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xi1530K/exportSkipped"), static_cast(canonical.status)); + return; + } + const auto pack = ml::buildXi1530KFeatures(canonical.candidate); + if (pack.status != ml::BuildStatus::Ok) { + histos.fill(HIST("ML/xi1530K/exportSkipped"), static_cast(pack.status)); + return; + } + if (parityLoggedXi1530K < omega2012MlParityRows) { + LOGP(info, "OMEGA2012MLPARITY mode=Xi1530K collision={} cascade={} tracks={},{} chargePattern={} passBits={} featureHash={}", + collision.globalIndex(), xi.globalIndex(), canonical.candidate.pion.sourceTrackId, canonical.candidate.kaon.sourceTrackId, + values.chargePattern, passBits, featureHash(pack.master)); + ++parityLoggedXi1530K; + } + ensureEventRow(); + const auto cascadeRow = writeCascade(xi); + const auto pionRow = writeTrack(pion); + const auto kaonRow = writeTrack(kaon); + mlXi1530KCandidates(mlEventRow, cascadeRow, pionRow, kaonRow, + static_cast(values.omega.M()), values.massXiPi, values.massXiK, values.massPiK, + static_cast(values.omega.Pt()), static_cast(values.omega.Rapidity()), + static_cast(values.omega.Eta()), static_cast(values.omega.Phi()), + values.chargePattern, passBits); + const auto row = mlXi1530KCandidates.lastIndex(); + mlXi1530KInputs(row, pack.master.data(), static_cast(pack.status)); + if constexpr (IsMC) { + const bool matched = classifyXi1530KTruth(xi, pion, kaon) == Xi1530KTruth::Matched; + mlXi1530KTruth(row, matched ? uint8_t{1} : uint8_t{2}, matched ? kaon.motherPDG() : 0, + matched ? static_cast(kaon.motherId()) : int64_t{-1}, static_cast(xi.motherId())); + } else { + mlXi1530KTruth(row, uint8_t{0}, 0, int64_t{-1}, int64_t{-1}); + } + } + + template + void exportXiK0s(Collision const& collision, Cascades const& cascades, V0s const& v0s) + { + beginEvent(collision, DecayMode::XiK0s); + // Selection only: the analysis histograms belong to the Omega(2012) analysis task + core.forEachXiK0sCandidate(histos, collision, collision, cascades, v0s, true, nullptr, nullptr, nullptr, + [&](auto const& coll, auto const& xi, auto const& v0, XiK0sCandidateValues const& values, uint16_t passBits) { + writeXiK0sCandidate(coll, xi, v0, values, passBits); + }); + } + + template + void exportXi1530K(Collision const& collision, Cascades const& cascades, Tracks const& tracks) + { + beginEvent(collision, DecayMode::Xi1530K); + core.forEachXi1530KCandidate(histos, collision, cascades, tracks, nullptr, true, nullptr, nullptr, nullptr, + [&](auto const& coll, auto const& xi, auto const& pion, auto const& kaon, + Xi1530KCandidateValues const& values, uint16_t passBits) { + writeXi1530KCandidate(coll, xi, pion, kaon, values, passBits); + }); + } + + void processXiK0s(ResoCollisions::iterator const& collision, aod::ResoCascades const& cascades, aod::ResoV0s const& v0s) + { + if (!core.passesEventCuts(collision)) { + return; + } + exportXiK0s(collision, cascades, v0s); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXiK0s, "Write XiK0s-mode candidates from data", true); + + void processXiK0sMC(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCV0s const& v0s, + aod::McParticles const& mcParticles, aod::McTrackLabels const& mcTrackLabels) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + mcParticlesDf = &mcParticles; + mcTrackLabelsDf = &mcTrackLabels; + exportXiK0s(collision, cascades, v0s); + // cppcheck-suppress redundantAssignment + mcParticlesDf = nullptr; + // cppcheck-suppress redundantAssignment + mcTrackLabelsDf = nullptr; + } + PROCESS_SWITCH(Omega2012TrainingTable, processXiK0sMC, "Write XiK0s-mode candidates with truth from reconstructed MC", false); + + void processXi1530KMicro(ResoCollisions::iterator const& collision, aod::ResoCascades const& cascades, ResoMicroTracks const& tracks) + { + if (!core.passesEventCuts(collision)) { + return; + } + exportXi1530K(collision, cascades, tracks); + } + PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMicro, "Write Xi1530K-mode candidates from data micro v001 tables", false); + + void processXi1530KMCMicro(ResoMCCols::iterator const& collision, ResoMCCascades const& cascades, ResoMCMicroTracks const& tracks, + aod::McParticles const& mcParticles, aod::McTrackLabels const& mcTrackLabels) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + mcParticlesDf = &mcParticles; + mcTrackLabelsDf = &mcTrackLabels; + exportXi1530K(collision, cascades, tracks); + // cppcheck-suppress redundantAssignment + mcParticlesDf = nullptr; + // cppcheck-suppress redundantAssignment + mcTrackLabelsDf = nullptr; + } + PROCESS_SWITCH(Omega2012TrainingTable, processXi1530KMCMicro, "Write Xi1530K-mode candidates with truth from reconstructed MC micro v001 tables", false); + + void processMCTrue(ResoMCCols::iterator const& collision, ResoMCParents const& resoParents) + { + if (!core.passesEventCuts(collision) || !core.passesMCEventCuts(collision)) { + return; + } + core.forEachGeneratedOmega2012(histos, resoParents, omega2012MlGenRapidityCut.value, [&](auto const& part, GeneratedChannel channel) { + mlGenAudit(collision.globalIndex(), static_cast(part.originalMcParticleId()), + part.pdgCode(), part.daughterPDG1(), part.daughterPDG2(), static_cast(channel), part.pt(), part.y()); + }); + } + PROCESS_SWITCH(Omega2012TrainingTable, processMCTrue, "Write generated Omega(2012) parents of selected reconstructed MC events", false); + + // Once per DF: every Omega(2012) with McTreeDepth generations of descendants. The ancestry set is reset here, so + // the result does not depend on the order of this call relative to the per-collision process functions. + void processMcTruth(aod::McParticles const& mcParticles) + { + mlAncestryRows.clear(); + const auto offset = static_cast(mcParticles.offset()); + const auto nRows = mcParticles.size(); + std::unordered_set treeRows; + std::vector> stack; + for (const auto& parent : mcParticles) { + if (std::abs(parent.pdgCode()) != o2::constants::physics::Pdg::kOmega2012Minus) { + continue; + } + stack.assign(1, {parent.globalIndex(), 0}); + while (!stack.empty()) { + const auto [row, depth] = stack.back(); + stack.pop_back(); + const auto local = row - offset; + if (local < 0 || local >= nRows || !treeRows.insert(row).second) { + continue; + } + const auto particle = mcParticles.rawIteratorAt(local); + writeMcParticle(particle, McSelectionTree); + const auto daughters = particle.daughtersIds(); + if (depth >= McTreeDepth || daughters[0] < 0 || daughters[1] < daughters[0]) { + continue; + } + for (int64_t daughter = daughters[0]; daughter <= daughters[1]; ++daughter) { + stack.emplace_back(daughter, depth + 1); + } + } + } + } + PROCESS_SWITCH(Omega2012TrainingTable, processMcTruth, "Write the pruned McParticles rows of the Omega(2012) decay trees; required with the reconstructed-MC process functions", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx b/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx index d1aae772d1b..29f2a75f55f 100644 --- a/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx +++ b/PWGLF/Tasks/Resonances/phi1020SpherocityAnalysis.cxx @@ -453,8 +453,8 @@ struct Phi1020SpherocityAnalysis { void processMix(ResoCols const& collisions, ResoTracks const& tracks) { LOGF(debug, "Event Mixing Started"); - BinningType1 binningPositions1{{mixVtxBins, mixMultBins, mixSphBins}, true}; - BinningType2 binningPositions2{{mixVtxBins, mixMultBins}, true}; + BinningType1 binningPositions1{{mixVtxBins, mixMultBins, mixSphBins}}; + BinningType2 binningPositions2{{mixVtxBins, mixMultBins}}; auto tracksTuple = std::make_tuple(tracks); if (mixSph) { SameKindPair pairs{binningPositions1, numMixEv, -1, collisions, tracksTuple, &cache}; diff --git a/PWGLF/Tasks/Resonances/phi1020analysis.cxx b/PWGLF/Tasks/Resonances/phi1020analysis.cxx index 609a69af24d..672e84c15b7 100644 --- a/PWGLF/Tasks/Resonances/phi1020analysis.cxx +++ b/PWGLF/Tasks/Resonances/phi1020analysis.cxx @@ -10,13 +10,14 @@ // or submit itself to any jurisdiction. /// \file phi1020analysis.cxx -/// \brief Reconstruction of Phi yield through track-track Minv correlations for resonance OO analysis -/// -/// -/// \author Adrian Fereydon Nassirpour #include #include #include @@ -32,196 +34,382 @@ #include #include #include +#include #include #include #include #include +#include #include +#include #include -#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include +#include #include -#include -#include +#include #include -#include #include -#include - using namespace o2; +using namespace o2::soa; +using namespace o2::aod; +using namespace o2::aod::rctsel; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::constants::physics; + +using LorentzVectorPtEtaPhiMass = ROOT::Math::PtEtaPhiMVector; + +enum TrackSelectionType { + AllTracks = 0, + GlobalTracks, + GlobalTracksWoPtEta, + GlobalTracksWoDCA, + QualityTracks, + InAcceptanceTracks, +}; -struct phi1020analysis { +enum PIDCutType { + SquareType = 1, + CircularType, +}; +struct Phi1020analysis { + // Define slice per collision + Preslice perCollision = o2::aod::track::collisionId; SliceCache cache; - Preslice perCollision = aod::track::collisionId; + Preslice perMcCollision = o2::aod::mcparticle::mcCollisionId; + SliceCache cacheMC; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Event configurables - Configurable cfg_Event_Sel{"cfg_Event_Sel", "sel8", "choose event selection"}; - Configurable cfg_Event_VtxCut{"cfg_Event_VtxCut", 10.0, "V_z cut selection"}; - Configurable cfg_Event_Timeframe{"cfg_Event_Timeframe", true, "Timeframe border cut"}; - Configurable cfg_Event_Timerange{"cfg_Event_Timerange", true, "Timerange border cut"}; - Configurable cfg_Event_Centrality{"cfg_Event_Centrality", true, "Centrality cut"}; - Configurable cfg_Event_CentralityMax{"cfg_Event_CentralityMax", 100, "CentralityMax cut"}; - Configurable cfg_Event_Pileup{"cfg_Event_Pileup", true, "Pileup border cut"}; - Configurable cfg_Event_OccupancyCut{"cfg_Event_OccupancyCut", true, "Occupancy border cut"}; - Configurable cfg_Event_MaxOccupancy{"cfg_Event_MaxOccupancy", 1, "Max TPC Occupancy"}; + Service pdg{}; + RCTFlagsChecker rctChecker; + + struct : ConfigurableGroup { + Configurable cfgEvtZvtx{"cfgEvtZvtx", 10.0f, "Evt sel: Max. z-Vertex (cm)"}; + Configurable cfgEvtTriggerTVXSel{"cfgEvtTriggerTVXSel", true, "Evt sel: triggerTVX selection (MB)"}; + Configurable cfgEvtNoTFBorderCut{"cfgEvtNoTFBorderCut", true, "Evt sel: apply TF border cut"}; + Configurable cfgEvtNoITSROFrameBorderCut{"cfgEvtNoITSROFrameBorderCut", false, "Evt sel: apply NoITSRO border cut"}; + Configurable cfgEvtIsRCTFlagpassed{"cfgEvtIsRCTFlagpassed", false, "Evt sel: apply RCT flag selection"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + Configurable cfgEvtSel8{"cfgEvtSel8", false, "Evt Sel 8 check for offline selection"}; + Configurable cfgEvtIsINELgt0{"cfgEvtIsINELgt0", false, "Evt sel: apply INEL>0 selection"}; + } configEvents; + + struct : ConfigurableGroup { + // Pre-selection Track cuts + Configurable trackSelection{"trackSelection", 0, "Track selection: 0 -> No Cut, 1 -> kGlobalTrack, 2 -> kGlobalTrackWoPtEta, 3 -> kGlobalTrackWoDCA, 4 -> kQualityTracks, 5 -> kInAcceptanceTracks"}; + Configurable cMinPtcut{"cMinPtcut", 0.15f, "Minimal pT for tracks"}; + Configurable cMinTPCNClsFound{"cMinTPCNClsFound", 120, "minimum TPCNClsFound value for good track"}; + Configurable cfgCutEta{"cfgCutEta", 0.8f, "Eta range for tracks"}; + Configurable cfgCutRapidity{"cfgCutRapidity", 0.5f, "rapidity range for particles"}; + Configurable cfgMinCrossedRows{"cfgMinCrossedRows", 70, "min crossed rows for good track"}; + + // DCA Selections + // DCAr to PV + Configurable cMaxDCArToPVcut{"cMaxDCArToPVcut", 0.1f, "Track DCAr cut to PV Maximum"}; + // DCAz to PV + Configurable cMaxDCAzToPVcut{"cMaxDCAzToPVcut", 0.1f, "Track DCAz cut to PV Maximum"}; + + // Track selections + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgGlobalWoDCATrack{"cfgGlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) + Configurable cfgGlobalTrack{"cfgGlobalTrack", false, "Global track selection"}; // kGoldenChi2 | kDCAxy | kDCAz + Configurable cfgPVContributor{"cfgPVContributor", false, "PV contributor track selection"}; // PV Contriuibutor + Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; + Configurable cfgUseTPCRefit{"cfgUseTPCRefit", false, "Require TPC Refit"}; + Configurable cfgUseITSRefit{"cfgUseITSRefit", false, "Require ITS Refit"}; + Configurable cTPCNClsFound{"cTPCNClsFound", false, "Switch to turn on/off TPCNClsFound cut"}; + Configurable cDCAr7SigCut{"cDCAr7SigCut", false, "Track DCAr 7 Sigma cut to PV Maximum"}; + } configTracks; + + struct : ConfigurableGroup { + /// PID Selections + Configurable pidnSigmaPreSelectionCut{"pidnSigmaPreSelectionCut", 4.0f, "pidnSigma Cut for pre-selection of tracks"}; + Configurable cByPassTOF{"cByPassTOF", false, "By pass TOF PID selection"}; // By pass TOF PID selection + Configurable cPIDcutType{"cPIDcutType", 2, "cPIDcutType = 1 for square cut, 2 for circular cut"}; // By pass TOF PID selection + Configurable ispTdepPID{"ispTdepPID", false, "enable pT dependent PID"}; + + // Kaon + Configurable> kaonTPCPIDpTintv{"kaonTPCPIDpTintv", {0.5f}, "pT intervals for Kaon TPC PID cuts"}; + Configurable> kaonTPCPIDcuts{"kaonTPCPIDcuts", {2}, "nSigma list for Kaon TPC PID cuts"}; + Configurable> kaonTOFPIDpTintv{"kaonTOFPIDpTintv", {999.0f}, "pT intervals for Kaon TOF PID cuts"}; + Configurable> kaonTOFPIDcuts{"kaonTOFPIDcuts", {2}, "nSigma list for Kaon TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedpTintv{"kaonTPCTOFCombinedpTintv", {999.0f}, "pT intervals for Kaon TPC-TOF PID cuts"}; + Configurable> kaonTPCTOFCombinedPIDcuts{"kaonTPCTOFCombinedPIDcuts", {2}, "nSigma list for Kaon TPC-TOF PID cuts"}; + } configPID; + + struct : ConfigurableGroup { + /// Event Mixing + Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {VARIABLE_WIDTH, -10.0f, -8.0f, -6.0f, -4.0f, -2.0f, 0.0f, 2.0f, 4.0f, 6.0f, 8.0f, 10.0f}, "Mixing bins - z-vertex"}; + ConfigurableAxis cfgMultPercentileBins{"cfgMultPercentileBins", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f, 110.0f}, "Mixing bins - multiplicity"}; + + // Rotational background + Configurable rotationalcut{"rotationalcut", 10, "Cut value (Rotation angle pi - pi/cut and pi + pi/cut)"}; + Configurable cNofRotations{"cNofRotations", 3, "Number of random rotations in the rotational background"}; + Configurable cfgRotTrk1{"cfgRotTrk1", true, "rotate Kaon 1"}; + } configBkg; + + // Additional purity check + Configurable crejectPion{"crejectPion", false, "Switch to turn on/off pion contamination"}; + Configurable cUseOpeningAngleCut{"cUseOpeningAngleCut", false, "Kinematic Cuts for p-K pair opening angle"}; + Configurable cMinOpeningAngle{"cMinOpeningAngle", 1.4f, "Minimum opening angle between daughters"}; + Configurable cMaxOpeningAngle{"cMaxOpeningAngle", 2.4f, "Maximum opening angle between daughters"}; + Configurable cfgUseDeltaEtaPhiCuts{"cfgUseDeltaEtaPhiCuts", false, "Switch to turn on/off delta eta and delta phi cuts"}; + Configurable cfgUseDaughterEtaCutMC{"cfgUseDaughterEtaCutMC", false, "Switch to turn on/off eta cuts for daughters in MC"}; + + // MC selection cut + Configurable cUseRapcutMC{"cUseRapcutMC", true, "Use rapidity cut for MC"}; + + // cuts on mother + Configurable cfgUseCutsOnMother{"cfgUseCutsOnMother", false, "Enable additional cuts on mother"}; + Configurable cMaxPtMotherCut{"cMaxPtMotherCut", 15.0f, "Maximum pt of mother cut"}; + Configurable cMaxMinvMotherCut{"cMaxMinvMotherCut", 1.5f, "Maximum Minv of mother cut"}; + Configurable cMaxDeltaEtaCut{"cMaxDeltaEtaCut", 0.7f, "Maximum deltaEta between daughters"}; + Configurable cMaxDeltaPhiCut{"cMaxDeltaPhiCut", 1.5f, "Maximum deltaPhi between daughters"}; + + // switches + Configurable cFillMultQA{"cFillMultQA", false, "Turn on/off additional QA plots"}; + Configurable cFillTrackQA{"cFillTrackQA", false, "Turn on/off additional QA plots"}; + Configurable cFilladditionalQAeventPlots{"cFilladditionalQAeventPlots", false, "Additional QA event plots"}; + Configurable cFilladditionalMEPlots{"cFilladditionalMEPlots", false, "Additional Mixed event plots"}; + Configurable cFilldeltaEtaPhiPlots{"cFilldeltaEtaPhiPlots", false, "Enable additional cuts on daughters"}; + Configurable cFill1DQAs{"cFill1DQAs", false, "Invariant mass 1D"}; Configurable centEstimator{"centEstimator", 0, "Select centrality estimator: 0 - FT0M, 1 - FT0A, 2 - FT0C"}; - // Track configurables - Configurable cfg_Track_Sel{"cfg_Track_Sel", "globalTracks", "set track selections"}; - Configurable cfg_Track_MinPt{"cfg_Track_MinPt", 0.15, "set track min pT"}; - Configurable cfg_Track_MaxEta{"cfg_Track_MaxEta", 0.9, "set track max Eta"}; - Configurable cfg_Track_MaxDCArToPVcut{"cfg_Track_MaxDCArToPVcut", 0.5, "Track DCAr cut to PV Maximum"}; - Configurable cfg_Track_MaxDCAzToPVcut{"cfg_Track_MaxDCAzToPVcut", 2.0, "Track DCAz cut to PV Maximum"}; - Configurable cfg_Track_PrimaryTrack{"cfg_Track_PrimaryTrack", true, "Primary track selection"}; // kGoldenChi2 | kDCAxy | kDCAz - Configurable cfg_Track_ConnectedToPV{"cfg_Track_ConnectedToPV", true, "PV contributor track selection"}; // PV Contributor - Configurable cfg_Track_GlobalWoDCATrack{"cfg_Track_GlobalWoDCATrack", true, "Global track selection without DCA"}; // kQualityTracks (kTrackType | kTPCNCls | kTPCCrossedRows | kTPCCrossedRowsOverNCls | kTPCChi2NDF | kTPCRefit | kITSNCls | kITSChi2NDF | kITSRefit | kITSHits) | kInAcceptanceTracks (kPtRange | kEtaRange) - Configurable cfg_Track_nFindableTPCClusters{"cfg_Track_FindableTPCClusters", 50, "nFindable TPC Clusters"}; - Configurable cfg_Track_nTPCCrossedRows{"cfg_Track_TPCCrossedRows", 70, "nCrossed TPC Rows"}; - Configurable cfg_Track_nRowsOverFindable{"cfg_Track_RowsOverFindable", 1.2, "nRowsOverFindable TPC CLusters"}; - Configurable cfg_Track_nTPCChi2{"cfg_Track_TPCChi2", 4.0, "nTPC Chi2 per Cluster"}; - Configurable cfg_Track_nITSChi2{"cfg_Track_ITSChi2", 36.0, "nITS Chi2 per Cluster"}; - Configurable cfg_Track_TPCPID{"cfg_Track_TPCPID", true, "Enables TPC PID"}; - Configurable cfg_Track_TOFPID{"cfg_Track_TOFPID", true, "Enables TOF PID"}; - Configurable cfg_Track_Hard_TOFPID{"cfg_Track_Hard_TOFPID", true, "Enables STRICT TOF Reqruirement"}; - Configurable cfg_Track_TPCPID_nSig{"cfg_Track_TPCPID_nSig", 4, "nTPC PID sigma"}; - Configurable cfg_Track_TOFPID_nSig{"cfg_Track_TOFPID_nSig", 4, "nTOF PID sigma"}; - Configurable cfg_Track_Explicit_PID{"cfg_Track_Explicit_PID", true, "Enables explicit pid cehck"}; - Configurable cDebugLevel{"cDebugLevel", 0, "Resolution of Debug"}; - - // Pair configurables - Configurable cfg_Pair_MinvBins{"cfg_Pair_MinvBins", 300, "Number of bins for Minv axis"}; - Configurable cfg_Pair_MinvMin{"cfg_Pair_MinvMin", 0.90, "Minimum Minv value"}; - Configurable cfg_Pair_MinvMax{"cfg_Pair_MinvMax", 1.50, "Maximum Minv value"}; - - Configurable cfg_Mix_NMixedEvents{"cfg_Mix_NMixedEvents", 5, "Number of mixed events per event"}; - - // MCGen configurables - Configurable cfg_Force_GenReco{"cfg_Force_GenReco", false, "Only consider events which are reconstructed (neglect event-loss)"}; - Configurable cfg_Force_BR{"cfg_Force_BR", false, "Only consider phi->K+K-"}; - Configurable cfg_Force_Kaon_Acceptence{"cfg_Force_Kaon_Acceptence", false, "Only consider phi's whose daughters decay inside acceptence (no signal loss)"}; - - // Histogram Configurables - Configurable cfg_Event_CutQA{"cfg_Event_CutsQA", true, "Enables Track QA plots"}; - Configurable cfg_Track_CutQA{"cfg_Track_CutsQA", true, "Enables Track QA plots"}; - - // Configurables for axis - ConfigurableAxis binsDCAz{"binsDCAz", {40, -0.2, 0.2}, ""}; - ConfigurableAxis binsDCAxy{"binsDCAxy", {40, -0.2, 0.2}, ""}; - ConfigurableAxis cfg_bins_Cent{"cfg_bins_Cent", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0}, "Binning of the centrality axis"}; - ConfigurableAxis cfg_bins_MixVtx{"cfg_bins_MixVtx", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; - ConfigurableAxis cfg_bins_MixMult{"cfg_bins_MixMult", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f}, "Mixing bins - z-vertex"}; + TRandom* rn = new TRandom(); + + // Pre-filters for efficient process + Filter zVtxFilter = (nabs(o2::aod::collision::posZ) <= configEvents.cfgEvtZvtx); + Filter collisionFilterMC = nabs(aod::mccollision::posZ) <= configEvents.cfgEvtZvtx; + + Filter acceptanceFilter = (nabs(aod::track::eta) < configTracks.cfgCutEta && nabs(aod::track::pt) > configTracks.cMinPtcut) && + (nabs(aod::track::dcaXY) < configTracks.cMaxDCArToPVcut) && (nabs(aod::track::dcaZ) < configTracks.cMaxDCAzToPVcut); + + using EventCandidates = soa::Join; + using TrackCandidates = soa::Filtered>; + + // for MC reco + using MCEventCandidates = soa::Join; + using MCTrackCandidates = soa::Filtered>; + + /// Figures + ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.12f, 0.14f, 0.16f, 0.18f, 0.2f, 0.25f, 0.3f, 0.35f, 0.4f, 0.45f, 0.5f, 0.55f, 0.6f, 0.65f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 0.95f, 1.0f, 1.1f, 1.2f, 1.25f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.75f, 1.8f, 1.9f, 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f, 2.7f, 2.8f, 2.9f, 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, 3.6f, 3.7f, 3.8f, 3.9f, 4.0f, 4.1f, 4.2f, 4.5f, 4.6f, 4.8f, 4.9f, 5.0f, 5.5f, 5.6f, 6.0f, 6.4f, 6.5f, 7.0f, 7.2f, 8.0f, 9.0f, 9.5f, 9.6f, 10.0f, 11.0f, 11.5f, 12.0f, 13.0f, 14.0f, 14.4f, 15.0f, 16.0f, 18.0f, 19.2f, 20.0f}, "Binning of the pT axis"}; + ConfigurableAxis binsEta{"binsEta", {150, -1.5f, 1.5f}, ""}; + ConfigurableAxis binsMass{"binsMass", {300, 0.9f, 1.6f}, "Invariant Mass (GeV/#it{c}^2)"}; + ConfigurableAxis binsMult{"binsMult", {105, 0.0f, 105.0f}, "mult_{FT0M}"}; + ConfigurableAxis binsDCAz{"binsDCAz", {40, -0.2f, 0.2f}, ""}; + ConfigurableAxis binsDCAxy{"binsDCAxy", {40, -0.2f, 0.2f}, ""}; + ConfigurableAxis binsTPCXrows{"binsTPCXrows", {100, 60, 160}, ""}; + ConfigurableAxis binsnSigma{"binsnSigma", {130, -6.5f, 6.5f}, ""}; + ConfigurableAxis binsnTPCSignal{"binsnTPCSignal", {1000, 0, 1000}, ""}; + ConfigurableAxis binsEtaPhi{"binsEtaPhi", {350, -3.5f, 3.5f}, ""}; void init(o2::framework::InitContext&) { - const AxisSpec MinvAxis = {cfg_Pair_MinvBins, cfg_Pair_MinvMin, cfg_Pair_MinvMax}; - const AxisSpec PtAxis = {200, 0, 20.0}; - const AxisSpec MultAxis = {100, 0, 100}; - const AxisSpec dRAxis = {100, 0, 100}; - const AxisSpec pidAxis = {100, -5, 5}; - const AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; - const AxisSpec axisDCAxy{binsDCAxy, "DCA_{XY}"}; - - // Event QA - if (cfg_Event_CutQA) { - histos.add("hPosZ_BC", "PosZ_BC", kTH1F, {{240, -12.0, 12.0}}); - histos.add("hcentFT0_BC", "centFT0_BC", kTH1F, {{110, 0.0, 110.0}}); - histos.add("hOccupancy_BC", "Occupancy_BC", kTH1F, {{100, 0.0, 20000}}); - // - histos.add("hcentFT0_AC", "centFT0_AC", kTH1F, {{110, 0.0, 110.0}}); - histos.add("hPosZ_AC", "PosZ_AC", kTH1F, {{240, -12.0, 12.0}}); - histos.add("hOccupancy_AC", "Occupancy_AC", kTH1F, {{100, 0.0, 20000}}); - } - // Track QA - if (cfg_Track_CutQA) { - histos.add("hDCArToPv_BC", "DCArToPv_BC", kTH1F, {axisDCAxy}); - histos.add("hDCAzToPv_BC", "DCAzToPv_BC", kTH1F, {axisDCAz}); - histos.add("hIsPrim_BC", "hIsPrim_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsGood_BC", "hIsGood_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsPrimCont_BC", "hIsPrimCont_BC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hFindableTPCClusters_BC", "hFindableTPCClusters_BC", kTH1F, {{200, 0, 200}}); - histos.add("hFindableTPCRows_BC", "hFindableTPCRows_BC", kTH1F, {{200, 0, 200}}); - histos.add("hClustersVsRows_BC", "hClustersVsRows_BC", kTH1F, {{200, 0, 2}}); - histos.add("hTPCChi2_BC", "hTPCChi2_BC", kTH1F, {{200, 0, 100}}); - histos.add("hITSChi2_BC", "hITSChi2_BC", kTH1F, {{200, 0, 100}}); - histos.add("hTPC_nSigma_BC", "hTPC_nSigma_BC", kTH1F, {pidAxis}); - histos.add("hTOF_nSigma_BC", "hTOF_nSigma_BC", kTH1F, {pidAxis}); - histos.add("hTPC_nSigma_v_pt_BC", "hTPC_nSigma_v_pt_BC", HistType::kTHnSparseD, {pidAxis, PtAxis}); - histos.add("hTOF_nSigma_v_pt_BC", "hTOF_nSigma_v_pt_BC", HistType::kTHnSparseD, {pidAxis, PtAxis}); - // - histos.add("hDCArToPv_AC", "DCArToPv_AC", kTH1F, {axisDCAxy}); - histos.add("hDCAzToPv_AC", "DCAzToPv_AC", kTH1F, {axisDCAz}); - histos.add("hIsPrim_AC", "hIsPrim_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsGood_AC", "hIsGood_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hIsPrimCont_AC", "hIsPrimCont_AC", kTH1F, {{2, -0.5, 1.5}}); - histos.add("hFindableTPCClusters_AC", "hFindableTPCClusters_AC", kTH1F, {{200, 0, 200}}); - histos.add("hFindableTPCRows_AC", "hFindableTPCRows_AC", kTH1F, {{200, 0, 200}}); - histos.add("hClustersVsRows_AC", "hClustersVsRows_AC", kTH1F, {{200, 0, 2}}); - histos.add("hTPCChi2_AC", "hTPCChi2_AC", kTH1F, {{200, 0, 100}}); - histos.add("hITSChi2_AC", "hITSChi2_AC", kTH1F, {{200, 0, 100}}); - histos.add("hTPC_nSigma_AC", "hTPC_nSigma_AC", kTH1F, {pidAxis}); - histos.add("hTOF_nSigma_AC", "hTOF_nSigma_AC", kTH1F, {pidAxis}); - histos.add("hTPC_nSigma_v_pt_AC", "hTPC_nSigma_v_pt_AC", HistType::kTHnSparseD, {pidAxis, PtAxis}); - histos.add("hTOF_nSigma_v_pt_AC", "hTOF_nSigma_v_pt_AC", HistType::kTHnSparseD, {pidAxis, PtAxis}); - } - // Data histos - histos.add("hUSS", "hUSS", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hLSS", "hLSS", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hUSS_Mix", "hUSS_Mix", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hLSS_Mix", "hLSS_Mix", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - - // MC histos - histos.add("hMC_USS", "hMC_USS", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hMC_LSS", "hMC_LSS", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hMC_USS_Mix", "hMC_USS_Mix", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hMC_LSS_Mix", "hMC_LSS_Mix", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hMC_USS_True", "hMC_USS_True", HistType::kTHnSparseD, {cfg_bins_Cent, MinvAxis, PtAxis}); - histos.add("hMC_Phi_True", "hMC_Phi_True", HistType::kTHnSparseD, {cfg_bins_Cent, PtAxis}); - - // Event Histograms - histos.add("hnEvents", "Event selection decision", kTH1I, {{10, -0.5, 9.5}}); - histos.add("hnEvents_MC", "Event selection decision", kTH1I, {{10, -0.5, 9.5}}); - histos.add("hnEvents_MC_True", "Event selection decision", kTH1I, {{10, -0.5, 9.5}}); + rctChecker.init(configEvents.cfgEvtRCTFlagCheckerLabel, configEvents.cfgEvtRCTFlagCheckerZDCCheck, configEvents.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + // axes + AxisSpec axisPt{binsPt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec axisEta{binsEta, "#eta"}; + AxisSpec axisRap{binsEta, "#it{y}"}; + AxisSpec axisMassphi1020{binsMass, "Invariant Mass (GeV/#it{c}^2)"}; + AxisSpec axisMult{binsMult, "mult_{FT0M}"}; + AxisSpec axisDCAz{binsDCAz, "DCA_{z}"}; + AxisSpec axisDCAxy{binsDCAxy, "DCA_{XY}"}; + AxisSpec axisTPCXrow{binsTPCXrows, "#Xrows_{TPC}"}; + AxisSpec axisPIDQA{binsnSigma, "#sigma"}; + AxisSpec axisTPCSignal{binsnTPCSignal, ""}; + AxisSpec axisEtaPhi{binsEtaPhi, ""}; + AxisSpec axisPhi{350, 0, 7, "#Phi"}; + AxisSpec axisMultMix{configBkg.cfgMultPercentileBins, "Multiplicity Percentile"}; + AxisSpec axisVtxMix{configBkg.cfgVtxBins, "Vertex Z (cm)"}; + + histos.add("CollCutCounts", "No. of event after cuts", kTH1I, {{10, 0, 10}}); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(1, "All Events"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(2, "|Vz| < cut"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(5, "kNoITSROFrameBorder"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(6, "rctChecker"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(7, "sel8"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(8, "IsINELgt0"); + histos.get(HIST("CollCutCounts"))->GetXaxis()->SetBinLabel(9, "All Passed Events"); + + histos.add("Event/posZ", "; vtx_{z} (cm); Entries", HistType::kTH1F, {{250, -12.5, 12.5}}); + histos.add("Event/centFT0M", "; FT0M Percentile; Entries", HistType::kTH1F, {{110, 0, 110}}); + + if (cFilladditionalQAeventPlots) { + // event histograms + if (doprocessData) { + histos.add("QAevent/hPairsCounterSameE", "total valid no. of pairs sameE", HistType::kTH1F, {{1, 0.5f, 1.5f}}); + histos.add("QAevent/hnTrksSameE", "n tracks per event SameE", HistType::kTH1F, {{1000, 0.0, 1000.0}}); + } + // Gen on Mixed event + if (doprocessME) { + + // Histograms for Mixed Event Pool characteristics + histos.add("QAevent/hMixPool_VtxZ", "Mixed Event Pool: Vertex Z;Vtx Z (cm);Counts", HistType::kTH1F, {axisVtxMix}); + histos.add("QAevent/hMixPool_Multiplicity", "Mixed Event Pool: Multiplicity;Multiplicity;Counts", HistType::kTH1F, {axisMultMix}); + histos.add("QAevent/hMixPool_VtxZ_vs_Multiplicity", "Mixed Event Pool: Vertex Z vs Multiplicity;Counts", HistType::kTH2F, {axisVtxMix, axisMultMix}); + + histos.add("QAevent/hPairsCounterMixedE", "total valid no. of pairs mixedE", HistType::kTH1F, {{1, 0.5f, 1.5f}}); + histos.add("QAevent/hVertexZMixedE", "Collision Vertex Z position", HistType::kTH1F, {{100, -15.0f, 15.0f}}); + histos.add("QAevent/hMultiplicityPercentMixedE", "Multiplicity percentile of collision", HistType::kTH1F, {{120, 0.0f, 120.0f}}); + histos.add("QAevent/hnTrksMixedE", "n tracks per event MixedE", HistType::kTH1F, {{1000, 0.0f, 1000.0f}}); + } + } + + if (doprocessData) { + // Track QA before cuts + // --- Track + if (cFillTrackQA) { + histos.add("QA/QAbefore/Track/TOF_TPC_Map_all", "TOF + TPC Combined PID for All Kaons;{#sigma_{TOF}^{Kaon}};{#sigma_{TPC}^{Kaon}}", {HistType::kTH2F, {axisPIDQA, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TOF_Nsigma_all", "TOF NSigma for All Kaons;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TPC_Nsigma_all", "TPC NSigma for All Kaons;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAbefore/Track/TPConly_Nsigma_all", "TPC NSigma for All Kaons;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTH2F, {axisPt, axisPIDQA}}); + histos.add("QA/QAbefore/Track/dcaZ_all", "DCA_{Z} distribution of selected All Kaons; #it{p}_{T} (GeV/#it{c}); DCA_{Z} (cm); ", HistType::kTH2F, {axisPt, axisDCAz}); + histos.add("QA/QAbefore/Track/dcaXY_all", "DCA_{XY} momentum distribution of All Kaons; #it{p}_{T} (GeV/#it{c}); DCA_{XY} (cm);", HistType::kTH2F, {axisPt, axisDCAxy}); + histos.add("QA/QAbefore/Track/TPC_CR_all", "# TPC Xrows distribution of All Kaons; #it{p}_{T} (GeV/#it{c}); TPC X rows", HistType::kTH2F, {axisPt, axisTPCXrow}); + histos.add("QA/QAbefore/Track/pT_all", "pT distribution of All Kaons; #it{p}_{T} (GeV/#it{c}); Counts;", {HistType::kTH1F, {axisPt}}); + histos.add("QA/QAbefore/Track/eta_all", "#eta distribution of All Kaons; #eta; Counts;", {HistType::kTH1F, {axisEta}}); + } + if (cFillMultQA) { + // Multiplicity correlation calibrations + histos.add("MultCalib/centGloPVTrk1", "Centrality vs Global-Tracks", kTHnSparseF, {{110, 0, 110, "Centrality"}, {500, 0, 5000, "Global Tracks"}, {500, 0, 5000, "PV tracks"}}); + histos.add("MultCalib/centGloPVTrk2", "Centrality vs Global-Tracks", kTHnSparseF, {{110, 0, 110, "Centrality"}, {500, 0, 5000, "Global Tracks"}, {500, 0, 5000, "PV tracks"}}); + } + + // PID QA after cuts + // --- Track 1 + histos.add("QA/QAafter/Trk1/TOF_TPC_Map_selected", "TOF + TPC Combined PID for selected Kaon 1;{#sigma_{TOF}^{Kaon}};{#sigma_{TPC}^{Kaon}}", {HistType::kTH2F, {axisPIDQA, axisPIDQA}}); + histos.add("QA/QAafter/Trk1/TOF_Nsigma_selected", "TOF NSigma for selected Kaon 1;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Trk1/TPC_Nsigma_selected", "TPC NSigma for selected Kaon 1;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Trk1/TPC_Nsigma_TPConly_selected", "TPC NSigma for selected Kaon 1;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTH2F, {axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Trk1/dcaZ_selected", "DCA_{Z} distribution of selected Kaon 1; #it{p}_{T} (GeV/#it{c}); DCA_{Z} (cm);", HistType::kTH2F, {axisPt, axisDCAz}); + histos.add("QA/QAafter/Trk1/dcaXY_selected", "DCA_{XY} momentum distribution of selected Kaon 1; #it{p}_{T} (GeV/#it{c}); DCA_{XY} (cm);", HistType::kTH2F, {axisPt, axisDCAxy}); + histos.add("QA/QAafter/Trk1/TPC_CR_selected", "# TPC Xrows distribution of selected Kaon 1; #it{p}_{T} (GeV/#it{c}); TPC X rows", HistType::kTH2F, {axisPt, axisTPCXrow}); + histos.add("QA/QAafter/Trk1/pT_selected", "pT distribution of selected Kaon 1; #it{p}_{T} (GeV/#it{c}); Counts;", {HistType::kTH1F, {axisPt}}); + histos.add("QA/QAafter/Trk1/eta_selected", "#eta distribution of selected Kaon 1; #eta; Counts;", {HistType::kTH1F, {axisEta}}); + histos.add("QA/QAafter/Trk1/TPC_Signal_selected", "TPC Signal for selected Kaon 1;#it{p} (GeV/#it{c});TPC Signal (A.U.)", {HistType::kTH2F, {axisPt, axisTPCSignal}}); + histos.add("QA/QAafter/Trk1/TPCnclusterPhipr_selected", "TPC ncluster vs phi for selected Kaon 1", kTHnSparseF, {{160, 0, 160, "TPC nCluster"}, {63, 0.0f, 6.28f, "#phi"}}); + + // --- Track 2 + histos.add("QA/QAafter/Trk2/TOF_TPC_Map_selected", "TOF + TPC Combined PID for selected Kaon 2;{#sigma_{TOF}^{Kaon}};{#sigma_{TPC}^{Kaon}}", {HistType::kTH2F, {axisPIDQA, axisPIDQA}}); + histos.add("QA/QAafter/Trk2/TOF_Nsigma_selected", "TOF NSigma for selected Kaon 2;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Trk2/TPC_Nsigma_selected", "TPC NSigma for selected Kaon 2;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Trk2/TPC_Nsigma_TPConly_selected", "TPC NSigma for selected Kaon 2;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTH2F, {axisPt, axisPIDQA}}); + histos.add("QA/QAafter/Trk2/dcaZ_selected", "DCA_{Z} distribution of selected Kaon 2; #it{p}_{T} (GeV/#it{c}); DCA_{Z} (cm); ", HistType::kTH2F, {axisPt, axisDCAz}); + histos.add("QA/QAafter/Trk2/dcaXY_selected", "DCA_{XY} momentum distribution of selected Kaon 2; #it{p}_{T} (GeV/#it{c}); DCA_{XY} (cm);", HistType::kTH2F, {axisPt, axisDCAxy}); + histos.add("QA/QAafter/Trk2/TPC_CR_selected", "# TPC Xrows distribution of selected Kaon 2; #it{p}_{T} (GeV/#it{c}); TPC X rows", HistType::kTH2F, {axisPt, axisTPCXrow}); + histos.add("QA/QAafter/Trk2/pT_selected", "pT distribution of selected Kaon 2; #it{p}_{T} (GeV/#it{c}); Counts;", {HistType::kTH1F, {axisPt}}); + histos.add("QA/QAafter/Trk2/eta_selected", "#eta distribution of selected Kaon 2; #eta; Counts;", {HistType::kTH1F, {axisEta}}); + histos.add("QA/QAafter/Trk2/TPC_Signal_selected", "TPC Signal for selected Kaon 2;#it{p} (GeV/#it{c});TPC Signal (A.U.)", {HistType::kTH2F, {axisPt, axisTPCSignal}}); + histos.add("QA/QAafter/Trk2/TPCnclusterPhika_selected", "TPC ncluster vs phi for selected Kaon 2", kTHnSparseF, {{160, 0, 160, "TPC nCluster"}, {63, 0.0f, 6.28f, "#phi"}}); + + // Mass QA 1D for quick check + if (cFill1DQAs) { + histos.add("Result/Data/phi1020invmass", "Invariant mass of #phi(1020) K^{#pm}K^{#mp}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassphi1020}}); + histos.add("Result/Data/phi1020invmassLSPP", "Invariant mass of #phi(1020) Like Sign Method K^{#plus}K^{#plus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassphi1020}}); // K+ + K+ + histos.add("Result/Data/phi1020invmassLSMM", "Invariant mass of #phi(1020) Like Sign Method K^{#minus}K^{#minus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassphi1020}}); // K- + K- + } + // eta phi QA + if (cFilldeltaEtaPhiPlots) { + histos.add("QAbefore/deltaEta", "deltaEta of kaon 1 and 2 candidates", HistType::kTH1F, {axisEtaPhi}); + histos.add("QAbefore/deltaPhi", "deltaPhi of kaon 1 and 2 candidates", HistType::kTH1F, {axisEtaPhi}); + + histos.add("QAafter/deltaEta", "deltaEta of kaon 1 and 2 candidates", HistType::kTH1F, {axisEtaPhi}); + histos.add("QAafter/deltaPhi", "deltaPhi of kaon 1 and 2 candidates", HistType::kTH1F, {axisEtaPhi}); + + histos.add("QAafter/PhiafterTrk1", "Phi of kaon 1 candidates", HistType::kTH1F, {axisPhi}); + histos.add("QAafter/PhiafterTrk2", "Phi of kaon 2 candidates", HistType::kTH1F, {axisPhi}); + } + + // 3d histogram + histos.add("Result/Data/h3phi1020invmass", "Invariant mass of #phi(1020) K^{#pm}K^{#mp}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); + histos.add("Result/Data/h3phi1020invmassLSPP", "Invariant mass of #phi(1020) Like Sign Method K^{#plus}K^{#plus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); // K+ + K+ + histos.add("Result/Data/h3phi1020invmassLSMM", "Invariant mass of #phi(1020) Like Sign Method K^{#minus}K^{#minus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); // K- + K- + } + + if (doprocessRotational) { + if (cFill1DQAs) { + histos.add("Result/Data/phi1020InvMassRotation", "Invariant mass of #phi(1020) rotational Method K^{#plus}K^{#plus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassphi1020}}); // K+ + K+ + } + histos.add("Result/Data/h3phi1020InvMassRotation", "Invariant mass of #phi(1020) rotational Method K^{#plus}K^{#plus}", kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); + } + // Mixed event histograms + if (doprocessME) { + if (cFill1DQAs) { + histos.add("Result/Data/phi1020invmassME", "Invariant mass of #phi(1020) mixed event K^{#pm}K^{#mp}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassphi1020}}); + } + histos.add("Result/Data/h3phi1020invmassME", "Invariant mass of #phi(1020) mixed event K^{#pm}K^{#mp}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); + + if (cFilladditionalMEPlots) { + if (cFill1DQAs) { + histos.add("Result/Data/phi1020invmassME_LSPP", "Invariant mass of #phi(1020) Like Sign Method K^{#plus}K^{#plus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassphi1020}}); // K+ + K+ + histos.add("Result/Data/phi1020invmassME_LSMM", "Invariant mass of #phi(1020) Like Sign Method K^{#minus}K^{#minus}; Invariant Mass (GeV/#it{c}^2); Counts;", {HistType::kTH1F, {axisMassphi1020}}); // K- + K- + } + histos.add("Result/Data/h3phi1020invmassME_LSPP", "Invariant mass of #phi(1020) mixed event Like Sign Method K^{#plus}K^{#plus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); // K+ + K+ + histos.add("Result/Data/h3phi1020invmassME_LSMM", "Invariant mass of #phi(1020) mixed event Like Sign Method K^{#minus}K^{#minus}", HistType::kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); // K- + K- + } + } + + // MC QA + if (doprocessEventFactor) { + histos.add("Event/MultiplicityGenEv", "hMCEventIndices", kTH1D, {axisMult}); + histos.add("Event/MultiplicityRecoEv", "Multiplicity of Reconstructed Events", kTH1D, {axisMult}); + } + + if (doprocessMCGen) { + histos.add("QA/MC/h2GenEtaPt_beforeanycut", " #eta-#it{p}_{T} distribution of Generated #phi(1020); #eta; #it{p}_{T}; Counts;", HistType::kTHnSparseF, {axisEta, axisPt}); + histos.add("QA/MC/h2GenPhiRapidity_beforeanycut", " #phi-y distribution of Generated #phi(1020); #phi; y; Counts;", HistType::kTHnSparseF, {axisPhi, axisRap}); + histos.add("QA/MC/h2GenEtaPt_afterRapcut", " #phi-#it{p}_{T} distribution of Generated #phi(1020); #eta; #it{p}_{T}; Counts;", HistType::kTHnSparseF, {axisEta, axisPt}); + histos.add("QA/MC/h2GenPhiRapidity_afterRapcut", " #phi-y distribution of Generated #phi(1020); #phi; y; Counts;", HistType::kTHnSparseF, {axisPhi, axisRap}); + + histos.add("Result/MC/Genphi1020pt", "pT distribution of True MC #phi(1020)0", kTH2F, {axisPt, axisMult}); + } + + if (doprocessMCRec) { + histos.add("QA/MC/h2RecoEtaPt_after", " #eta-#it{p}_{T} distribution of Reconstructed #phi(1020); #eta; #it{p}_{T}; Counts;", HistType::kTHnSparseF, {axisEta, axisPt}); + histos.add("QA/MC/h2RecoPhiRapidity_after", " #phi-y distribution of Reconstructed #phi(1020); #phi; y; Counts;", HistType::kTHnSparseF, {axisPhi, axisRap}); + histos.add("QA/MC/trkDCAxy_Trk1", "DCAxy distribution of kaon 1 track candidates", HistType::kTHnSparseF, {axisPt, axisDCAxy}); + histos.add("QA/MC/trkDCAxy_Trk2", "DCAxy distribution of kaon 2 track candidates", HistType::kTHnSparseF, {axisPt, axisDCAxy}); + histos.add("QA/MC/trkDCAz_Trk1", "DCAz distribution of kaon 1 track candidates", HistType::kTHnSparseF, {axisPt, axisDCAz}); + histos.add("QA/MC/trkDCAz_Trk2", "DCAz distribution of kaon 2 track candidates", HistType::kTHnSparseF, {axisPt, axisDCAz}); + histos.add("QA/MC/TOF_Nsigma_selected_Trk1", "TOF NSigma for Kaon 1;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/MC/TPC_Nsigma_selected_Trk1", "TPC NSigma for Kaon 1;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/MC/TOF_Nsigma_selected_Trk2", "TOF NSigma for Kaon 2;#it{p}_{T} (GeV/#it{c});{#sigma_{TOF}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + histos.add("QA/MC/TPC_Nsigma_selected_Trk2", "TPC NSigma for Kaon 2;#it{p}_{T} (GeV/#it{c});{#sigma_{TPC}^{Kaon}};", {HistType::kTHnSparseF, {axisMult, axisPt, axisPIDQA}}); + + histos.add("Result/MC/h3phi1020Recoinvmass", "Invariant mass of Reconstructed MC #phi(1020)0", kTHnSparseF, {axisMult, axisPt, axisMassphi1020}); + } + + if (doprocessSignalLoss) { + histos.add("Result/SignalLoss/GenTruephi1020pt_den", "True phi1020 (den)", kTH2F, {axisPt, axisMult}); + + histos.add("Result/SignalLoss/GenTruephi1020pt_num", "True phi1020 (num)", kTH2F, {axisPt, axisMult}); + } + + // Print output histograms statistics + LOG(info) << "Size of the histograms in Phi1020analysis:"; + histos.print(); } // end of init - Filter collisionFilter = nabs(aod::collision::posZ) <= cfg_Event_VtxCut; - Filter collisionFilter_MC = nabs(aod::mccollision::posZ) <= cfg_Event_VtxCut; - // Filter centralityFilter = nabs(aod::cent::centFT0C) <= cfg_Event_CentralityMax; - Filter acceptanceFilter = (nabs(aod::track::eta) < cfg_Track_MaxEta && nabs(aod::track::pt) >= cfg_Track_MinPt); - using EventCandidates = soa::Filtered>; - using EventCandidates_True = soa::Filtered; - - using TrackCandidates = soa::Filtered>; - using TrackCandidates_MC = soa::Filtered>; - - using BinningTypeVtxCent = ColumnBinningPolicy; - - Partition PosKaon_MC = - (aod::track::signed1Pt > static_cast(0)) && - (!cfg_Track_TPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfg_Track_TPCPID_nSig)); - Partition NegKaon_MC = - (aod::track::signed1Pt < static_cast(0)) && - (!cfg_Track_TPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfg_Track_TPCPID_nSig)); - Partition PosKaon = - (aod::track::signed1Pt > static_cast(0)) && - (!cfg_Track_TPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfg_Track_TPCPID_nSig)); - Partition NegKaon = - (aod::track::signed1Pt < static_cast(0)) && - (!cfg_Track_TPCPID || (nabs(aod::pidtpc::tpcNSigmaKa) <= cfg_Track_TPCPID_nSig)); - - double massKa = o2::constants::physics::MassKPlus; + float massKa = MassKaonCharged; // Centralicity estimator selection template - float centEst(Coll collisions) + float centEst(const Coll& collisions) { float returnValue = -999.0f; switch (centEstimator) { @@ -241,452 +429,845 @@ struct phi1020analysis { return returnValue; } - //***********************************// - // First, we declare some helper functions - template - void fillQA(const bool pass, const objType& obj, const int objecttype = 0) + template + bool isSelected(const Coll& collision, bool fillHist = true) { - - if (objecttype == 1) { - if constexpr (requires { obj.posZ(); }) { - if (!pass) { - histos.fill(HIST("hPosZ_BC"), obj.posZ()); - histos.fill(HIST("hcentFT0_BC"), centEst(obj)); - } else { - histos.fill(HIST("hPosZ_AC"), obj.posZ()); - histos.fill(HIST("hcentFT0_AC"), centEst(obj)); - } + auto applyCut = [&](bool enabled, bool condition, int bin) { + if (!enabled) { + return true; } - } - if constexpr (requires { obj.tpcCrossedRowsOverFindableCls(); }) { - if (objecttype == 2) { - if (!pass) { - histos.fill(HIST("hDCArToPv_BC"), obj.dcaXY()); - histos.fill(HIST("hDCAzToPv_BC"), obj.dcaZ()); - histos.fill(HIST("hIsPrim_BC"), obj.isPrimaryTrack()); - histos.fill(HIST("hIsGood_BC"), obj.isGlobalTrackWoDCA()); - histos.fill(HIST("hIsPrimCont_BC"), obj.isPVContributor()); - histos.fill(HIST("hFindableTPCClusters_BC"), obj.tpcNClsFindable()); - histos.fill(HIST("hFindableTPCRows_BC"), obj.tpcNClsCrossedRows()); - histos.fill(HIST("hClustersVsRows_BC"), obj.tpcCrossedRowsOverFindableCls()); - histos.fill(HIST("hTPCChi2_BC"), obj.tpcChi2NCl()); - } else { - histos.fill(HIST("hDCArToPv_AC"), obj.dcaXY()); - histos.fill(HIST("hDCAzToPv_AC"), obj.dcaZ()); - histos.fill(HIST("hIsPrim_AC"), obj.isPrimaryTrack()); - histos.fill(HIST("hIsGood_AC"), obj.isGlobalTrackWoDCA()); - histos.fill(HIST("hIsPrimCont_AC"), obj.isPVContributor()); - histos.fill(HIST("hFindableTPCClusters_AC"), obj.tpcNClsFindable()); - histos.fill(HIST("hFindableTPCRows_AC"), obj.tpcNClsCrossedRows()); - histos.fill(HIST("hClustersVsRows_AC"), obj.tpcCrossedRowsOverFindableCls()); - histos.fill(HIST("hTPCChi2_AC"), obj.tpcChi2NCl()); - } + if (!condition) { + return false; } - if (objecttype == 3) { - if (!pass) { - histos.fill(HIST("hTPC_nSigma_BC"), obj.tpcNSigmaKa()); - histos.fill(HIST("hTOF_nSigma_BC"), obj.tofNSigmaKa()); - histos.fill(HIST("hTPC_nSigma_v_pt_BC"), obj.tpcNSigmaKa(), obj.pt()); - histos.fill(HIST("hTOF_nSigma_v_pt_BC"), obj.tofNSigmaKa(), obj.pt()); - } else { - histos.fill(HIST("hTPC_nSigma_AC"), obj.tpcNSigmaKa()); - histos.fill(HIST("hTOF_nSigma_AC"), obj.tofNSigmaKa()); - histos.fill(HIST("hTPC_nSigma_v_pt_AC"), obj.tpcNSigmaKa(), obj.pt()); - histos.fill(HIST("hTOF_nSigma_v_pt_AC"), obj.tofNSigmaKa(), obj.pt()); - } + if (fillHist) { + histos.fill(HIST("CollCutCounts"), bin); } + return true; + }; + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 0); } - }; - //***********************************// - // evsel - template - std::pair eventSelection(const EventType event, const bool QA) - { + if (!applyCut(true, std::abs(collision.posZ()) <= configEvents.cfgEvtZvtx, 1)) { + return false; + } - if (cfg_Track_CutQA && QA) - fillQA(false, event, 1); - - if (!event.sel8()) - return {false, 1}; - if (std::abs(event.posZ()) > cfg_Event_VtxCut) - return {false, 2}; - if (cfg_Event_Timeframe && (!event.selection_bit(aod::evsel::kNoTimeFrameBorder) || !event.selection_bit(aod::evsel::kNoITSROFrameBorder))) - return {false, 3}; - if (cfg_Event_Timerange && (!event.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) - return {false, 4}; - if (cfg_Event_Pileup && (!event.selection_bit(aod::evsel::kNoSameBunchPileup) || !event.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) - return {false, 5}; - if (cfg_Event_Centrality && (centEst(event) > cfg_Event_CentralityMax)) - return {false, 6}; - if (cfg_Event_OccupancyCut && (event.trackOccupancyInTimeRange() > cfg_Event_MaxOccupancy)) - return {false, 7}; - - if (cfg_Track_CutQA && QA) - fillQA(true, event, 1); - - return {true, 8}; - }; - - // tracksel - template - bool trackSelection(const TrackType track, const bool QA) - { - if (cfg_Track_CutQA && QA) - fillQA(false, track, 2); + if (!applyCut(configEvents.cfgEvtTriggerTVXSel, + collision.selection_bit(aod::evsel::kIsTriggerTVX), 2)) { + return false; + } - // basic track cuts - if (track.pt() < cfg_Track_MinPt) + if (!applyCut(configEvents.cfgEvtNoTFBorderCut, + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), 3)) { + return false; + } + + if (!applyCut(configEvents.cfgEvtNoITSROFrameBorderCut, + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), 4)) { return false; - if (std::abs(track.eta()) > cfg_Track_MaxEta) + } + + if (!applyCut(configEvents.cfgEvtIsRCTFlagpassed, rctChecker(collision), 5)) { return false; - if (std::abs(track.dcaXY()) > cfg_Track_MaxDCArToPVcut) + } + + if (!applyCut(configEvents.cfgEvtSel8, collision.sel8(), 6)) { return false; - if (std::abs(track.dcaZ()) > cfg_Track_MaxDCAzToPVcut) + } + + if (!applyCut(configEvents.cfgEvtIsINELgt0, collision.isInelGt0(), 7)) { + return false; + } + + if (fillHist) { + histos.fill(HIST("CollCutCounts"), 8); + } + + return true; + } + + template + bool trackCut(const TrackType& track) + { + // basic track cuts + if (configTracks.cDCAr7SigCut && std::abs(track.dcaXY()) > (0.004f + 0.013f / (track.pt()))) // 7 - Sigma cut + { return false; - if (cfg_Track_PrimaryTrack && !track.isPrimaryTrack()) + } + if (configTracks.cTPCNClsFound && (track.tpcNClsFound() < configTracks.cMinTPCNClsFound)) { return false; - if (track.tpcNClsFindable() < cfg_Track_nFindableTPCClusters) + } + if (track.tpcNClsCrossedRows() < configTracks.cfgMinCrossedRows) { return false; - if (track.tpcNClsCrossedRows() < cfg_Track_nTPCCrossedRows) + } + if (configTracks.cfgHasTOF && !track.hasTOF()) { return false; - if (track.tpcCrossedRowsOverFindableCls() > cfg_Track_nRowsOverFindable) + } + if (configTracks.cfgPrimaryTrack && !track.isPrimaryTrack()) { return false; - if (track.tpcChi2NCl() > cfg_Track_nTPCChi2) + } + if (configTracks.cfgGlobalWoDCATrack && !track.isGlobalTrackWoDCA()) { return false; - if (track.itsChi2NCl() > cfg_Track_nITSChi2) + } + if (configTracks.cfgPVContributor && !track.isPVContributor()) { return false; - if (cfg_Track_ConnectedToPV && !track.isPVContributor()) + } + if (configTracks.cfgGlobalTrack && !track.isGlobalTrack()) { return false; + } - if (cfg_Track_CutQA && QA) - fillQA(true, track, 2); return true; - }; - - // trackpid + } - template - bool trackPIDKaon(const TrackPID& candidate, const bool QA) + template + bool ptDependentPidKaon(const T& candidate) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; + auto vKaonTPCPIDpTintv = configPID.kaonTPCPIDpTintv.value; + vKaonTPCPIDpTintv.insert(vKaonTPCPIDpTintv.begin(), configTracks.cMinPtcut); + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTOFPIDpTintv = configPID.kaonTOFPIDpTintv.value; + auto vKaonTPCTOFCombinedpTintv = configPID.kaonTPCTOFCombinedpTintv.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; + auto vKaonTOFPIDcuts = configPID.kaonTOFPIDcuts.value; + + float pt = candidate.pt(); + float ptSwitchToTOF = vKaonTPCPIDpTintv.back(); + float tpcNsigmaKa = candidate.tpcNSigmaKa(); + float tofNsigmaKa = candidate.tofNSigmaKa(); + + bool tpcPIDPassed = false; + + // TPC PID interval-based check + for (size_t i = 0; i < vKaonTPCPIDpTintv.size() - 1; ++i) { + if (pt > vKaonTPCPIDpTintv[i] && pt < vKaonTPCPIDpTintv[i + 1]) { + if (std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts[i]) { + tpcPIDPassed = true; + break; + } + } + } - if (cfg_Track_CutQA && QA) - fillQA(false, candidate, 3); + // TOF bypass option + if (configPID.cByPassTOF) { + return std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back(); + } - if (!cfg_Track_TPCPID) { - tpcPIDPassed = true; - } else { - if (std::abs(candidate.tpcNSigmaKa()) < cfg_Track_TPCPID_nSig) - tpcPIDPassed = true; + // Case 1: No TOF and pt ≤ ptSwitch → use TPC-only + if (!candidate.hasTOF() && pt <= ptSwitchToTOF) { + return tpcPIDPassed; + } + + // Case 2: No TOF but pt > ptSwitch → reject + if (!candidate.hasTOF() && pt > ptSwitchToTOF) { + return false; } - if (!cfg_Track_TOFPID) { - tofPIDPassed = true; - } else { - if (candidate.hasTOF()) { - if (std::abs(candidate.tofNSigmaKa()) < cfg_Track_TOFPID_nSig) { - tofPIDPassed = true; + // Case 3: TOF is available → apply TPC+TOF PID logic + if (candidate.hasTOF()) { + if (configPID.cPIDcutType == SquareType) { + // Rectangular cut + for (size_t i = 0; i < vKaonTOFPIDpTintv.size(); ++i) { + if (pt < vKaonTOFPIDpTintv[i]) { + if (std::abs(tofNsigmaKa) < vKaonTOFPIDcuts[i] && + std::abs(tpcNsigmaKa) < vKaonTPCPIDcuts.back()) { + return true; + } + } + } + } else if (configPID.cPIDcutType == CircularType) { + // Circular cut + for (size_t i = 0; i < vKaonTPCTOFCombinedpTintv.size(); ++i) { + if (pt < vKaonTPCTOFCombinedpTintv[i]) { + float combinedSigma2 = tpcNsigmaKa * tpcNsigmaKa + + tofNsigmaKa * tofNsigmaKa; + if (combinedSigma2 < vKaonTPCTOFCombinedPIDcuts[i] * vKaonTPCTOFCombinedPIDcuts[i]) { + return true; + } + } } - } else if (!cfg_Track_Hard_TOFPID) { - tofPIDPassed = true; - } - if (!candidate.hasTOF()) { - // std::cout << candidate.tofNSigmaKa() << std::endl; } } - if (tpcPIDPassed && tofPIDPassed) { - if (cfg_Track_CutQA && QA) { - fillQA(true, candidate, 3); - } + + return false; + } + + template + bool selectionPID(const T& candidate) + { + auto vKaonTPCPIDcuts = configPID.kaonTPCPIDcuts.value; + auto vKaonTPCTOFCombinedPIDcuts = configPID.kaonTPCTOFCombinedPIDcuts.value; + + if (!configPID.cByPassTOF && candidate.hasTOF() && (candidate.tofNSigmaKa() * candidate.tofNSigmaKa() + candidate.tpcNSigmaKa() * candidate.tpcNSigmaKa()) < (vKaonTPCTOFCombinedPIDcuts[0] * vKaonTPCTOFCombinedPIDcuts[0])) { + return true; + } + if (!configPID.cByPassTOF && !candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { + return true; + } + if (configPID.cByPassTOF && std::abs(candidate.tpcNSigmaKa()) < vKaonTPCPIDcuts[0]) { return true; } return false; } - template - void TrackSlicing(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool QA, const bool IsMix) + auto static constexpr MinPtforPionRejection = 1.0f; + auto static constexpr MaxPtforPionRejection = 2.0f; + auto static constexpr MaxnSigmaforPionRejection = 2.0f; + + template + bool rejectPion(const T& candidate) + { + return !(candidate.pt() > MinPtforPionRejection && candidate.pt() < MaxPtforPionRejection && !candidate.hasTOF() && candidate.tpcNSigmaPi() < MaxnSigmaforPionRejection); + } + + auto static constexpr MaxNophi1020Daughters = 2; + + template + void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) { - auto slicedtracks1 = PosKaon->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto slicedtracks2 = NegKaon->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = centEst(collision1); - for (auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(slicedtracks1, slicedtracks2))) { - auto [Minv, PhiPt] = minvReconstruction(track1, track2, QA); - if (Minv < 0) + auto centrality = centEst(collision); + + // Multiplicity correlation calibration plots + if (cFillMultQA) { + if constexpr (IsData) { + histos.fill(HIST("MultCalib/centGloPVTrk1"), centrality, dTracks1.size(), collision.multNTracksPV()); + histos.fill(HIST("MultCalib/centGloPVTrk2"), centrality, dTracks2.size(), collision.multNTracksPV()); + } + } + + if (cFilladditionalQAeventPlots) { + if constexpr (IsData) { + histos.fill(HIST("QAevent/hnTrksSameE"), dTracks1.size()); + } else if constexpr (IsMix) { + histos.fill(HIST("QAevent/hVertexZMixedE"), collision.posZ()); + histos.fill(HIST("QAevent/hMultiplicityPercentMixedE"), centrality); + histos.fill(HIST("QAevent/hnTrksMixedE"), dTracks1.size()); + } + } + // LOG(info) << "After pass, Collision index:" << collision.index() << "multiplicity: " << collision.centFT0M() << endl; + + LorentzVectorPtEtaPhiMass lDecayDaughter1, lDecayDaughter2, lResonance, ldaughterRot, lResonanceRot; + + for (const auto& [trk1, trk2] : combinations(CombinationsFullIndexPolicy(dTracks1, dTracks2))) { + LOG(info) << "Before pass, Track1 index:" << trk1.index() << ",Track2 index:" << trk2.index(); + + if (trk2.index() <= trk1.index()) { + continue; + } + + LOG(info) << "After pass, Track1 index:" << trk1.index() << ",Track2 index:" << trk2.index(); + + // apply the track cut + if (!trackCut(trk1) || !trackCut(trk2)) { continue; - double conjugate = track1.sign() * track2.sign(); - if (!IsMix) { - if (conjugate < 0) { - histos.fill(HIST("hUSS"), centrality, Minv, PhiPt); - } else if (conjugate > 0) { - histos.fill(HIST("hLSS"), centrality, Minv, PhiPt); + } + + //// Initialize variables + // Trk1: Proton + auto isTrk1hasTOF = trk1.hasTOF(); + auto trk1pt = trk1.pt(); + auto trk1eta = trk1.eta(); + auto trk1phi = trk1.phi(); + auto trk1NSigmaTPC = trk1.tpcNSigmaKa(); + auto trk1NSigmaTOF = (isTrk1hasTOF) ? trk1.tofNSigmaKa() : -999.0f; + + // Trk2: Kaon + auto isTrk2hasTOF = trk2.hasTOF(); + auto trk2pt = trk2.pt(); + auto trk2eta = trk2.eta(); + auto trk2phi = trk2.phi(); + auto trk2NSigmaTPC = trk2.tpcNSigmaKa(); + auto trk2NSigmaTOF = (isTrk2hasTOF) ? trk2.tofNSigmaKa() : -999.0f; + + auto deltaEta = 0.0f; + auto deltaPhi = 0.0f; + + if (cfgUseDeltaEtaPhiCuts) { + deltaEta = std::abs(trk1eta - trk2eta); + deltaPhi = std::abs(trk1phi - trk2phi); + deltaPhi = (deltaPhi > o2::constants::math::PI) ? (o2::constants::math::TwoPI - deltaPhi) : deltaPhi; + if (deltaEta >= cMaxDeltaEtaCut) { + continue; } - } else { - if (conjugate < 0) { - histos.fill(HIST("hUSS_Mix"), centrality, Minv, PhiPt); - } else if (conjugate > 0) { - histos.fill(HIST("hLSS_Mix"), centrality, Minv, PhiPt); + if (deltaPhi >= cMaxDeltaPhiCut) { + continue; } } - } - } // TrackSlicing - template - void TrackSlicing_MC(const CollisionType& collision1, const TracksType&, const CollisionType& collision2, const TracksType&, const bool QA, const bool IsMix) - { - auto slicedtracks1 = PosKaon_MC->sliceByCached(aod::track::collisionId, collision1.globalIndex(), cache); - auto slicedtracks2 = NegKaon_MC->sliceByCached(aod::track::collisionId, collision2.globalIndex(), cache); - auto centrality = centEst(collision1); - for (auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(slicedtracks1, slicedtracks2))) { - auto [Minv, PhiPt] = minvReconstruction(track1, track2, QA); - if (Minv < 0) - continue; - double conjugate = track1.sign() * track2.sign(); - if (!IsMix) { - if (conjugate < 0) { - histos.fill(HIST("hMC_USS"), centrality, Minv, PhiPt); - } else if (conjugate > 0) { - histos.fill(HIST("hMC_LSS"), centrality, Minv, PhiPt); + //// QA plots before the selection + // --- Track QA all + if constexpr (IsData) { + if (cFillTrackQA) { + histos.fill(HIST("QA/QAbefore/Track/TPC_Nsigma_all"), centrality, trk2pt, trk2NSigmaTPC); + if (isTrk2hasTOF) { + histos.fill(HIST("QA/QAbefore/Track/TOF_Nsigma_all"), centrality, trk2pt, trk2NSigmaTOF); + histos.fill(HIST("QA/QAbefore/Track/TOF_TPC_Map_all"), trk2NSigmaTOF, trk2NSigmaTPC); + } + if (!isTrk2hasTOF) { + histos.fill(HIST("QA/QAbefore/Track/TPConly_Nsigma_all"), trk2pt, trk2NSigmaTPC); + } + + histos.fill(HIST("QA/QAbefore/Track/dcaZ_all"), trk1pt, trk1.dcaZ()); + histos.fill(HIST("QA/QAbefore/Track/dcaXY_all"), trk1pt, trk1.dcaXY()); + histos.fill(HIST("QA/QAbefore/Track/TPC_CR_all"), trk1pt, trk1.tpcNClsCrossedRows()); + histos.fill(HIST("QA/QAbefore/Track/pT_all"), trk1pt); + histos.fill(HIST("QA/QAbefore/Track/eta_all"), trk1eta); } - } else { - if (conjugate < 0) { - histos.fill(HIST("hMC_USS_Mix"), centrality, Minv, PhiPt); - } else if (conjugate > 0) { - histos.fill(HIST("hMC_LSS_Mix"), centrality, Minv, PhiPt); + if (cFilldeltaEtaPhiPlots) { + histos.fill(HIST("QAbefore/deltaEta"), deltaEta); + histos.fill(HIST("QAbefore/deltaPhi"), deltaPhi); } } - // now we do mc true - if (!track1.has_mcParticle() || !track2.has_mcParticle()) + + //// Apply the pid selection + if (crejectPion && rejectPion(trk2)) // to remove pion contamination from the kaon track + { continue; - auto part1 = track1.mcParticle(); - auto part2 = track2.mcParticle(); - if (std::fabs(part1.pdgCode()) != 321) - continue; // Not Kaon - if (std::fabs(part2.pdgCode()) != 321) - continue; // Not Kaon + } + + if (configPID.ispTdepPID && (!ptDependentPidKaon(trk1) || !ptDependentPidKaon(trk2))) { + continue; + } + + if (!configPID.ispTdepPID && (!selectionPID(trk1) || !selectionPID(trk2))) { + continue; + } + + //// QA plots after the selection + if constexpr (IsData) { // --- PID QA Proton + histos.fill(HIST("QA/QAafter/Trk1/TPC_Nsigma_selected"), centrality, trk1pt, trk1NSigmaTPC); + histos.fill(HIST("QA/QAafter/Trk1/TPC_Signal_selected"), trk1.tpcInnerParam(), trk1.tpcSignal()); + if (isTrk1hasTOF) { + histos.fill(HIST("QA/QAafter/Trk1/TOF_Nsigma_selected"), centrality, trk1pt, trk1NSigmaTOF); + histos.fill(HIST("QA/QAafter/Trk1/TOF_TPC_Map_selected"), trk1NSigmaTOF, trk1NSigmaTPC); + } + if (!isTrk1hasTOF) { + histos.fill(HIST("QA/QAafter/Trk1/TPC_Nsigma_TPConly_selected"), trk1pt, trk1NSigmaTPC); + } + histos.fill(HIST("QA/QAafter/Trk1/dcaZ_selected"), trk1pt, trk1.dcaZ()); + histos.fill(HIST("QA/QAafter/Trk1/dcaXY_selected"), trk1pt, trk1.dcaXY()); + histos.fill(HIST("QA/QAafter/Trk1/TPC_CR_selected"), trk1pt, trk1.tpcNClsCrossedRows()); + histos.fill(HIST("QA/QAafter/Trk1/pT_selected"), trk1pt); + histos.fill(HIST("QA/QAafter/Trk1/eta_selected"), trk1eta); + histos.fill(HIST("QA/QAafter/Trk1/TPCnclusterPhipr_selected"), trk1.tpcNClsFound(), trk1phi); + + // --- PID QA Kaon + histos.fill(HIST("QA/QAafter/Trk2/TPC_Nsigma_selected"), centrality, trk2pt, trk2NSigmaTPC); + histos.fill(HIST("QA/QAafter/Trk2/TPC_Signal_selected"), trk2.tpcInnerParam(), trk2.tpcSignal()); + if (isTrk2hasTOF) { + histos.fill(HIST("QA/QAafter/Trk2/TOF_Nsigma_selected"), centrality, trk2pt, trk2NSigmaTOF); + histos.fill(HIST("QA/QAafter/Trk2/TOF_TPC_Map_selected"), trk2NSigmaTOF, trk2NSigmaTPC); + } + if (!isTrk2hasTOF) { + histos.fill(HIST("QA/QAafter/Trk2/TPC_Nsigma_TPConly_selected"), trk2pt, trk2NSigmaTPC); + } + histos.fill(HIST("QA/QAafter/Trk2/dcaZ_selected"), trk2pt, trk2.dcaZ()); + histos.fill(HIST("QA/QAafter/Trk2/dcaXY_selected"), trk2pt, trk2.dcaXY()); + histos.fill(HIST("QA/QAafter/Trk2/TPC_CR_selected"), trk2pt, trk2.tpcNClsCrossedRows()); + histos.fill(HIST("QA/QAafter/Trk2/pT_selected"), trk2pt); + histos.fill(HIST("QA/QAafter/Trk2/eta_selected"), trk2eta); + histos.fill(HIST("QA/QAafter/Trk2/TPCnclusterPhika_selected"), trk2.tpcNClsFound(), trk2phi); + + if (cFilldeltaEtaPhiPlots) { + histos.fill(HIST("QAafter/PhiafterTrk1"), trk1phi); + histos.fill(HIST("QAafter/PhiafterTrk2"), trk2phi); + histos.fill(HIST("QAafter/deltaEta"), deltaEta); + histos.fill(HIST("QAafter/deltaPhi"), deltaPhi); + } + } - if (!part1.has_mothers()) - continue; // Not decaying Kaon - if (!part2.has_mothers()) - continue; // Not decaying Kaon + //// Resonance reconstruction + lDecayDaughter1 = LorentzVectorPtEtaPhiMass(trk1pt, trk1eta, trk1phi, massKa); + lDecayDaughter2 = LorentzVectorPtEtaPhiMass(trk2pt, trk2eta, trk2phi, massKa); - std::vector mothers1{}; - std::vector mothers1PDG{}; - for (auto& part1_mom : part1.template mothers_as()) { - mothers1.push_back(part1_mom.globalIndex()); - mothers1PDG.push_back(part1_mom.pdgCode()); + // Apply kinematic opening angle cut + if (cUseOpeningAngleCut) { + auto v1 = lDecayDaughter1.Vect(); + auto v2 = lDecayDaughter2.Vect(); + float alpha = std::acos(v1.Dot(v2) / (v1.R() * v2.R())); + if (alpha > cMinOpeningAngle && alpha < cMaxOpeningAngle) { + continue; + } } - std::vector mothers2{}; - std::vector mothers2PDG{}; - for (auto& part2_mom : part2.template mothers_as()) { - mothers2.push_back(part2_mom.globalIndex()); - mothers2PDG.push_back(part2_mom.pdgCode()); + lResonance = lDecayDaughter1 + lDecayDaughter2; + + auto resonanceMass = lResonance.M(); + auto resonancePt = lResonance.Pt(); + auto resonanceRapidity = lResonance.Rapidity(); + + // Rapidity cut + if (std::abs(resonanceRapidity) > configTracks.cfgCutRapidity) { + continue; } - if (mothers1PDG[0] != 333) - continue; // mother not phi - if (mothers2PDG[0] != 333) - continue; // mother not phi + if (cfgUseCutsOnMother) { + if (resonancePt >= cMaxPtMotherCut) // excluding candidates in overflow + { + continue; + } + if (resonanceMass >= cMaxMinvMotherCut) // excluding candidates in overflow + { + continue; + } + } - if (mothers1[0] != mothers2[0]) - continue; // Kaons not from the same phi + if (cFilladditionalQAeventPlots) { + if constexpr (IsData) { + histos.fill(HIST("QAevent/hPairsCounterSameE"), 1.0f); + } else if (IsMix) { + histos.fill(HIST("QAevent/hPairsCounterMixedE"), 1.0f); + } + } - histos.fill(HIST("hMC_USS_True"), centrality, Minv, PhiPt); - } - } // TrackSlicing + //// Un-like sign pair only + if (trk1.sign() * trk2.sign() < 0) { + if constexpr (IsRot) { + for (int i = 0; i < configBkg.cNofRotations; i++) { + float theta = rn->Uniform(o2::constants::math::PI - o2::constants::math::PI / configBkg.rotationalcut, o2::constants::math::PI + o2::constants::math::PI / configBkg.rotationalcut); + if (configBkg.cfgRotTrk1) { + ldaughterRot = LorentzVectorPtEtaPhiMass(trk1pt, trk1eta, trk1phi + theta, massKa); + lResonanceRot = ldaughterRot + lDecayDaughter2; + } else { + ldaughterRot = LorentzVectorPtEtaPhiMass(trk2pt, trk2eta, trk2phi + theta, massKa); + lResonanceRot = lDecayDaughter1 + ldaughterRot; + } + auto resonanceRotMass = lResonanceRot.M(); + auto resonanceRotPt = lResonanceRot.Pt(); - // Invariant mass - template - std::pair minvReconstruction(const TracksType& trk1, const TracksType& trk2, const bool QA) - { - TLorentzVector lDecayDaughter1, lDecayDaughter2, lResonance; - //==================================================== + // Rapidity cut + if (std::abs(lResonanceRot.Rapidity()) >= configTracks.cfgCutRapidity) { + continue; + } - if (!trackSelection(trk1, QA) || !trackSelection(trk2, false)) - return {-1.0, -1.0}; + if (cfgUseCutsOnMother) { + if (resonanceRotPt >= cMaxPtMotherCut) // excluding candidates in overflow + { + continue; + } + if (resonanceRotMass >= cMaxMinvMotherCut) // excluding candidates in overflow + { + continue; + } + } + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/phi1020InvMassRotation"), resonanceRotMass); + } + histos.fill(HIST("Result/Data/h3phi1020InvMassRotation"), centrality, resonanceRotPt, resonanceRotMass); + } + } + if constexpr (IsData) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/phi1020invmass"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3phi1020invmass"), centrality, resonancePt, resonanceMass); + } else if (IsMix) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/phi1020invmassME"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3phi1020invmassME"), centrality, resonancePt, resonanceMass); + } - if (cfg_Track_Explicit_PID) { - if (!trackPIDKaon(trk1, QA) || !trackPIDKaon(trk2, false)) - return {-1.0, -1.0}; - } + // MC + if constexpr (IsMC) { + // now we do mc true + // ------ Temporal lambda function to prevent error in build + auto getMothersIndeces = [&](auto const& theMcParticle) { + std::vector lMothersIndeces{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother index lMother: %d", lMother.globalIndex()); + lMothersIndeces.push_back(lMother.globalIndex()); + } + return lMothersIndeces; + }; + auto getMothersPDGCodes = [&](auto const& theMcParticle) { + std::vector lMothersPDGs{}; + for (auto const& lMother : theMcParticle.template mothers_as()) { + LOGF(debug, " mother pdgcode lMother: %d", lMother.pdgCode()); + lMothersPDGs.push_back(lMother.pdgCode()); + } + return lMothersPDGs; + }; + // ------ + std::vector motherstrk1 = {-1, -1}; + std::vector mothersPDGtrk1 = {-1, -1}; + + std::vector motherstrk2 = {-1, -1}; + std::vector mothersPDGtrk2 = {-1, -1}; + + // + // Get the MC particle + const auto& mctrk1 = trk1.mcParticle(); + if (mctrk1.has_mothers()) { + motherstrk1 = getMothersIndeces(mctrk1); + mothersPDGtrk1 = getMothersPDGCodes(mctrk1); + } + while (motherstrk1.size() > MaxNophi1020Daughters) { + motherstrk1.pop_back(); + mothersPDGtrk1.pop_back(); + } - if (trk1.globalIndex() >= trk2.globalIndex()) - return {-1.0, -1.0}; + const auto& mctrk2 = trk2.mcParticle(); + if (mctrk2.has_mothers()) { + motherstrk2 = getMothersIndeces(mctrk2); + mothersPDGtrk2 = getMothersPDGCodes(mctrk2); + } + while (motherstrk2.size() > MaxNophi1020Daughters) { + motherstrk2.pop_back(); + mothersPDGtrk2.pop_back(); + } - lDecayDaughter1.SetXYZM(trk1.px(), trk1.py(), trk1.pz(), massKa); - lDecayDaughter2.SetXYZM(trk2.px(), trk2.py(), trk2.pz(), massKa); - lResonance = lDecayDaughter1 + lDecayDaughter2; + if (std::abs(mctrk1.pdgCode()) != kKPlus || std::abs(mctrk2.pdgCode()) != kKPlus) { + continue; + } - return {lResonance.M(), lResonance.Pt()}; - } // MinvReconstruction + if (motherstrk1[0] != motherstrk2[0]) // Same mother + { + continue; + } + + if (std::abs(mothersPDGtrk1[0]) != Pdg::kPhi) { + continue; + } - //***************// - // DATA - //***************// + // LOGF(info, "mother trk1 id: %d, mother trk1: %d, trk1 id: %d, trk1 pdgcode: %d, mother trk2 id: %d, mother trk2: %d, trk2 id: %d, trk2 pdgcode: %d", motherstrk1[0], mothersPDGtrk1[0], trk1.globalIndex(), mctrk1.pdgCode(), motherstrk2[0], mothersPDGtrk2[0], trk2.globalIndex(), mctrk2.pdgCode()); - int nEvents = 0; - void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks) - { - if (cDebugLevel > 0) { - ++nEvents; - if (nEvents % 10000 == 0) { - std::cout << "Processed Data Events: " << nEvents << std::endl; + histos.fill(HIST("QA/MC/h2RecoEtaPt_after"), lResonance.Eta(), resonancePt); + histos.fill(HIST("QA/MC/h2RecoPhiRapidity_after"), lResonance.Phi(), resonanceRapidity); + + // Track selection check. + histos.fill(HIST("QA/MC/trkDCAxy_Trk1"), trk1pt, trk1.dcaXY()); + histos.fill(HIST("QA/MC/trkDCAxy_Trk2"), trk2pt, trk2.dcaXY()); + histos.fill(HIST("QA/MC/trkDCAz_Trk1"), trk1pt, trk1.dcaZ()); + histos.fill(HIST("QA/MC/trkDCAz_Trk2"), trk2pt, trk2.dcaZ()); + + histos.fill(HIST("QA/MC/TPC_Nsigma_selected_Trk1"), centrality, trk1pt, trk1NSigmaTPC); + if (isTrk1hasTOF) { + histos.fill(HIST("QA/MC/TOF_Nsigma_selected_Trk1"), centrality, trk1pt, trk1NSigmaTOF); + } + histos.fill(HIST("QA/MC/TPC_Nsigma_selected_Trk2"), centrality, trk2pt, trk2NSigmaTPC); + if (isTrk2hasTOF) { + histos.fill(HIST("QA/MC/TOF_Nsigma_selected_Trk2"), centrality, trk2pt, trk2NSigmaTOF); + } + + // MC histograms + histos.fill(HIST("Result/MC/h3phi1020Recoinvmass"), centrality, resonancePt, resonanceMass); + } + } else { + if constexpr (IsData) { + // Like sign pair ++ + if (trk1.sign() > 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/phi1020invmassLSPP"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3phi1020invmassLSPP"), centrality, resonancePt, resonanceMass); + } else { // Like sign pair -- + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/phi1020invmassLSMM"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3phi1020invmassLSMM"), centrality, resonancePt, resonanceMass); + } + } else if (IsMix) { + if (cFilladditionalMEPlots) { + // Like sign pair ++ + if (trk1.sign() > 0) { + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/phi1020invmassME_LSPP"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3phi1020invmassME_LSPP"), centrality, resonancePt, resonanceMass); + } else { // Like sign pair -- + if (cFill1DQAs) { + histos.fill(HIST("Result/Data/phi1020invmassME_LSMM"), resonanceMass); + } + histos.fill(HIST("Result/Data/h3phi1020invmassME_LSMM"), centrality, resonancePt, resonanceMass); + } + } + } } } + } - auto [goodEv, code] = eventSelection(collision, true); - histos.fill(HIST("hnEvents"), code); - if (!goodEv) + void processData(EventCandidates::iterator const& collision, + TrackCandidates const& tracks) + { + if (!isSelected(collision)) // Default event selection + { return; - TrackSlicing(collision, tracks, collision, tracks, true, false); + } - } // end of process + auto centrality = centEst(collision); - PROCESS_SWITCH(phi1020analysis, processSameEvent, "Process Same events", true); + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centFT0M"), centrality); - //***************// - // DATA (MIX) - //***************// + fillHistograms(collision, tracks, tracks); + } + PROCESS_SWITCH(Phi1020analysis, processData, "Process Event for data without partition", false); - int nEvents_Mix = 0; - void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) + void processRotational(EventCandidates::iterator const& collision, TrackCandidates const& tracks) + { + if (!isSelected(collision, false)) // Default event selection + { + return; + } + + if (!collision.isInelGt0()) // <-- + { + return; + } + + fillHistograms(collision, tracks, tracks); + } + PROCESS_SWITCH(Phi1020analysis, processRotational, "Process Rotational Background", false); + + // Processing Event Mixing + using BinningTypeVtxZT0M = ColumnBinningPolicy; + + void processME(EventCandidates const& collision, + TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCent colBinning{{cfg_bins_MixVtx, cfg_bins_MixMult}, true}; - SameKindPair pairs{colBinning, cfg_Mix_NMixedEvents, -1, collisions, tracksTuple, &cache}; + + BinningTypeVtxZT0M colBinning{{configBkg.cfgVtxBins, configBkg.cfgMultPercentileBins}}; + SameKindPair pairs{colBinning, configBkg.nEvtMixing, -1, collision, tracksTuple, &cache}; // -1 is the number of the bin to skip for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - ++nEvents_Mix; - if (nEvents_Mix % 10000 == 0) { - std::cout << "Processed Mixed Events: " << nEvents_Mix << std::endl; - } + // LOGF(info, "Mixed event collisions: (%d, %d)", collision1.globalIndex(), collision2.globalIndex()); + + // for (auto& [t1, t2] : combinations(CombinationsFullIndexPolicy(tracks1, tracks2))) { + // LOGF(info, "Mixed event tracks pair: (%d, %d) from events (%d, %d)", t1.index(), t2.index(), collision1.index(), collision2.index()); + // } + + if (!isSelected(collision1, false)) // Default event selection + { + continue; } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - if (!goodEv1 || !goodEv2) + + if (!isSelected(collision2, false)) // Default event selection + { continue; - TrackSlicing(collision1, tracks1, collision2, tracks2, false, true); - } // mixing - } // end of process - PROCESS_SWITCH(phi1020analysis, processMixedEvent, "Process Mixed events", false); + } - //***************// - // RECONSTRUCTED MC - //***************// + if (!collision1.isInelGt0()) // <-- + { + continue; + } - int nEvents_MC = 0; - void processSameEvent_MC(EventCandidates::iterator const& collision, TrackCandidates_MC const& tracks, aod::McParticles const&) - { - if (cDebugLevel > 0) { - ++nEvents_MC; - if (nEvents_MC % 10000 == 0) { - std::cout << "Processed MC (REC) Events: " << nEvents_MC << std::endl; + if (!collision2.isInelGt0()) // <-- + { + continue; + } + + if (cFilladditionalQAeventPlots) { + // Fill histograms for the characteristics of the *mixed* events (collision1 and collision2) + // This will show the distribution of events that are actually being mixed. + if (cFill1DQAs) { + histos.fill(HIST("QAevent/hMixPool_VtxZ"), collision1.posZ()); + histos.fill(HIST("QAevent/hMixPool_Multiplicity"), collision1.centFT0M()); + } + histos.fill(HIST("QAevent/hMixPool_VtxZ_vs_Multiplicity"), collision1.posZ(), collision1.centFT0M()); + + // You might also want to fill for collision2 if you want to see both partners' distributions + // histos.fill(HIST("QAevent/hMixPool_VtxZ"), collision2.posZ()); + // histos.fill(HIST("QAevent/hMixPool_Multiplicity"), collision2.centFT0M()); + // histos.fill(HIST("QAevent/hMixPool_VtxZ_vs_Multiplicity"), collision2.posZ(), collision2.centFT0M()); } + fillHistograms(collision1, tracks1, tracks2); + } + } + PROCESS_SWITCH(Phi1020analysis, processME, "Process EventMixing light without partition", false); + + void processMCRec(MCEventCandidates::iterator const& collision, + aod::McCollisions const&, + MCTrackCandidates const& tracks, aod::McParticles const&) + { + if (!collision.has_mcCollision()) { + return; } - auto [goodEv, code] = eventSelection(collision, true); - histos.fill(HIST("hnEvents_MC"), code); - if (!goodEv) + if (!isSelected(collision)) { return; - TrackSlicing_MC(collision, tracks, collision, tracks, true, false); + } + + auto centrality = centEst(collision); - } // end of process - PROCESS_SWITCH(phi1020analysis, processSameEvent_MC, "Process Same events (MC)", true); + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centFT0M"), centrality); - //***************// - // RECONSTRUCTED MC (MIX) - //***************// + fillHistograms(collision, tracks, tracks); + } + PROCESS_SWITCH(Phi1020analysis, processMCRec, "Process Event for MC Rec without partition", false); + + Partition selectedMCParticles = (nabs(aod::mcparticle::pdgCode) == static_cast(Pdg::kPhi)); // Lambda(1520) - int nEvents_MC_Mix = 0; - void processMixedEvent_MC(EventCandidates const& collisions, TrackCandidates_MC const& tracks, aod::McParticles const&) + void processMCGen(MCEventCandidates::iterator const& collision, aod::McCollisions const&, aod::McParticles const& mcParticles) { - auto tracksTuple = std::make_tuple(tracks); - BinningTypeVtxCent colBinning{{cfg_bins_MixVtx, cfg_bins_MixMult}, true}; - SameKindPair pairs{colBinning, cfg_Mix_NMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - if (cDebugLevel > 0) { - ++nEvents_MC_Mix; - if (nEvents_MC_Mix % 10000 == 0) { - std::cout << "Processed Mixed Events: " << nEvents_MC_Mix << std::endl; - } + if (!collision.has_mcCollision()) { + return; + } + + bool isInAfterAllCuts = isSelected(collision, false); + + auto mcPartsAll = mcParticles.sliceBy(perMcCollision, collision.mcCollision().globalIndex()); + // bool isTrueINELgt0 = pwglf::isINELgt0mc(mcPartsAll, pdg); + bool isTrueINELgt0 = collision.isInelGt0(); // <-- + + auto centrality = centEst(collision); + + auto mcParts = selectedMCParticles->sliceBy(perMcCollision, collision.mcCollision().globalIndex()); + + // Not related to the real collisions + for (const auto& part : mcParts) { // loop over all Lambda(1520) particles + + std::vector daughterPDGs; + if (part.has_daughters()) { + auto daughter01 = mcParticles.rawIteratorAt(part.daughtersIds()[0] - mcParticles.offset()); + auto daughter02 = mcParticles.rawIteratorAt(part.daughtersIds()[1] - mcParticles.offset()); + daughterPDGs = {daughter01.pdgCode(), daughter02.pdgCode()}; + } else { + daughterPDGs = {-1, -1}; } - auto [goodEv1, code1] = eventSelection(collision1, false); - auto [goodEv2, code2] = eventSelection(collision2, false); - if (!goodEv1 || !goodEv2) + + if (std::abs(daughterPDGs[0]) != PDG_t::kKPlus || std::abs(daughterPDGs[1]) != PDG_t::kKPlus) { // At least one decay to Kaon continue; - TrackSlicing_MC(collision1, tracks1, collision2, tracks2, false, true); - } // mixing - } // end of process - PROCESS_SWITCH(phi1020analysis, processMixedEvent_MC, "Process Mixed events (MC)", false); + } - //***************// - // GENERATED MC - //***************// + // LOGF(info, "Part PDG: %d", part.pdgCode(), "DAU_ID1: %d", pass1, "DAU_ID2: %d", pass2); - int nEvents_True = 0; - void processParticles(EventCandidates_True::iterator const& collision, soa::SmallGroups> const& recocolls, aod::McParticles const& particles) - { - if (cDebugLevel > 0) { - ++nEvents_True; - if (nEvents_True % 10000 == 0) { - std::cout << "Processed MC (GEN) Events: " << nEvents_True << std::endl; + histos.fill(HIST("QA/MC/h2GenEtaPt_beforeanycut"), part.eta(), part.pt()); + histos.fill(HIST("QA/MC/h2GenPhiRapidity_beforeanycut"), part.phi(), part.y()); + + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) // rapidity cut + { + continue; + } + + if (cfgUseDaughterEtaCutMC) { + for (auto const& daughters : part.daughters_as()) { + if (std::fabs(daughters.eta()) > configTracks.cfgCutEta) { + continue; + } // eta cut for daughters + } // loop over daughters + } + + histos.fill(HIST("QA/MC/h2GenEtaPt_afterRapcut"), part.eta(), part.pt()); + histos.fill(HIST("QA/MC/h2GenPhiRapidity_afterRapcut"), part.phi(), part.y()); + + if (isInAfterAllCuts && isTrueINELgt0) { // after all event selection && INEL>0 + histos.fill(HIST("Result/MC/Genphi1020pt"), part.pt(), centrality); } } + } + PROCESS_SWITCH(Phi1020analysis, processMCGen, "Process Event for MC only", false); - if (fabs(collision.posZ()) > cfg_Event_VtxCut) - return; + void processEventFactor(MCEventCandidates const& collisions, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) + { + // Loop on reconstructed collisions + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + const auto& mcCollision = collision.mcCollision_as>(); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cacheMC); + + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); + bool isInAfterAllCuts = isSelected(collision, false); + + float centrality = mcCollision.centFT0M(); - if (recocolls.size() <= 0) { // not reconstructed - if (cfg_Force_GenReco) { - return; + if (isTrueINELgt0 && isInAfterAllCuts) { + histos.fill(HIST("Event/MultiplicityRecoEv"), centrality); } } - double centrality = -1; - for (auto& recocoll : recocolls) { // poorly reconstructed - centrality = centEst(recocoll); - auto [goodEv, code] = eventSelection(recocoll, false); - histos.fill(HIST("hnEvents_MC_True"), code); - if (!goodEv) - return; + // Loop on generated collisions to fill the event factor for the INEL>0 correction + for (const auto& mccolls : mcCollisions) { + float centrality = mccolls.centFT0M(); + bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; + + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolls.globalIndex(), cacheMC); + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); // QA for Trigger efficiency + + if (inVtx10 && isTrueINELgt0) { + histos.fill(HIST("Event/MultiplicityGenEv"), centrality); + } } + } + PROCESS_SWITCH(Phi1020analysis, processEventFactor, "Process Event factor", false); + + void processSignalLoss(MCEventCandidates const& collisions, soa::Join const& mcCollisions, aod::McParticles const& mcParticles) + { + // Loop on reconstructed collisions + for (const auto& collision : collisions) { + if (!collision.has_mcCollision()) { + continue; + } + const auto& mcCollision = collision.mcCollision_as>(); + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cacheMC); + + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); + bool isInAfterAllCuts = isSelected(collision, false); + bool inVtx10 = std::abs(mcCollision.posZ()) <= configEvents.cfgEvtZvtx; + + float centrality = mcCollision.centFT0M(); - for (auto& particle : particles) { - if (particle.pdgCode() != 333) + // ===== NUM ===== + if (!(inVtx10 && isTrueINELgt0)) { continue; - if (std::fabs(particle.eta()) > cfg_Track_MaxEta) + } + + if (!isInAfterAllCuts) { continue; + } - if (cfg_Force_BR) { - bool baddecay = false; - for (auto& phidaughter : particle.daughters_as()) { - if (std::fabs(phidaughter.pdgCode()) != 321) { - baddecay = true; - break; - } - if (cfg_Force_Kaon_Acceptence) { - if (std::fabs(phidaughter.eta()) > cfg_Track_MaxEta) { - baddecay = true; - break; - } - } - } // loop over daughters + for (const auto& part : particlesInCollision) { - if (baddecay) + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) { continue; - } // enforce BR restriction + } + + float pt = part.pt(); + + // phi + if (std::abs(part.pdgCode()) == Pdg::kPhi) { + histos.fill(HIST("Result/SignalLoss/GenTruephi1020pt_num"), pt, centrality); + } + } + } + + // Loop on generated collisions to fill the event factor for the INEL>0 correction + for (const auto& mccolls : mcCollisions) { + float centrality = mccolls.centFT0M(); + + bool inVtx10 = std::abs(mccolls.posZ()) <= configEvents.cfgEvtZvtx; - histos.fill(HIST("hMC_Phi_True"), centrality, particle.pt()); - } // loop over particles + const auto& particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mccolls.globalIndex(), cacheMC); + bool isTrueINELgt0 = pwglf::isINELgt0mc(particlesInCollision, pdg); - } // end of process - PROCESS_SWITCH(phi1020analysis, processParticles, "Process Particles", false); + if (!(inVtx10 && isTrueINELgt0)) { + continue; + } + for (const auto& part : particlesInCollision) { + + if (cUseRapcutMC && std::abs(part.y()) > configTracks.cfgCutRapidity) { + continue; + } + + // ========================= + // ===== PHI(1020) ====== + // ========================= + if (std::abs(part.pdgCode()) == Pdg::kPhi) { + + histos.fill(HIST("Result/SignalLoss/GenTruephi1020pt_den"), part.pt(), centrality); + } + } + } + } + PROCESS_SWITCH(Phi1020analysis, processSignalLoss, "Process SignalLoss", false); }; // end of main struct WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; }; diff --git a/PWGLF/Tasks/Resonances/phianalysis.cxx b/PWGLF/Tasks/Resonances/phianalysis.cxx index eff17aa8a87..1363e182fa6 100644 --- a/PWGLF/Tasks/Resonances/phianalysis.cxx +++ b/PWGLF/Tasks/Resonances/phianalysis.cxx @@ -138,7 +138,7 @@ struct phianalysis { double massKa = MassKaonCharged; template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -328,7 +328,7 @@ struct phianalysis { void processMELight(o2::aod::ResoCollisions& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { diff --git a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx index 1489f7109f6..04bfa017916 100644 --- a/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisTHnSparse.cxx @@ -13,8 +13,12 @@ /// \brief Analysis of phi resonance using THnSparse histograms. /// \author Veronika Barbasova (veronika.barbasova@cern.ch) +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/Utils/inelGt.h" #include "PWGLF/Utils/rsnOutput.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -40,11 +44,14 @@ #include #include +#include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include #include #include +#include #include @@ -56,48 +63,70 @@ using namespace o2; using namespace o2::analysis; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; struct PhianalysisTHnSparse { - SliceCache cache; + SliceCache cache, cacheMC, cacheME; struct : ConfigurableGroup { - Configurable produceQA{"produceQA", false, "Produce qa histograms."}; - Configurable produceStats{"produceStats", false, "Produce statistics histograms."}; - Configurable produceTrue{"produceTrue", false, "Produce True and Gen histograms."}; + Configurable produceQA{"produceQA", false, "Produce QA histograms."}; + Configurable produceMC{"produceMC", false, "Produce True and Gen histograms."}; Configurable produceLikesign{"produceLikesign", false, "Produce Like sign histograms."}; Configurable eventMixing{"eventMixing", "none", "Produce Event Mixing histograms of type."}; Configurable produceRotational{"produceRotational", false, "Produce Rotational histograms."}; } produce; - Configurable daughterPos{"daughterPos", 3, "Particle type of the positive dauther according to ReconstructionDataFormats/PID.h (Default = Kaon)"}; - Configurable daughterNeg{"daughterNeg", 3, "Particle type of the negative dauther according to ReconstructionDataFormats/PID.h (Default = Kaon)"}; + Configurable daughterPos{"daughterPos", 3, "Particle type of the positive daughter according to ReconstructionDataFormats/PID.h (Default = Kaon)"}; + Configurable daughterNeg{"daughterNeg", 3, "Particle type of the negative daughter according to ReconstructionDataFormats/PID.h (Default = Kaon)"}; Configurable motherPDG{"motherPDG", 333, "PDG code of mother particle."}; - Configurable daughterPosPDG{"daughterPosPDG", 321, "PDG code of positive dauther particle."}; - Configurable daughterNegPDG{"daughterNegPDG", 321, "PDG code of negative dauther particle."}; + Configurable daughterPosPDG{"daughterPosPDG", 321, "PDG code of positive daughter particle."}; + Configurable daughterNegPDG{"daughterNegPDG", 321, "PDG code of negative daughter particle."}; + + struct : ConfigurableGroup { + Configurable isTriggerTVX{"isTriggerTVX", false, "Apply IsTriggerTVX cut."}; + Configurable noTimeFrameBorder{"noTimeFrameBorder", false, "Apply NoTimeFrameBorder cut."}; + Configurable noITSROFrameBorder{"noITSROFrameBorder", false, "Apply NoITSROFrameBorder cut."}; + Configurable sel8{"sel8", false, "Apply Sel8 cut."}; + Configurable inelGt0{"inelGt0", false, "Select events with INEL>0."}; + Configurable vzCut{"vzCut", 10.0f, "Cut: Maximal value of Z vertex position."}; + Configurable noSameBunchPileup{"noSameBunchPileup", false, "Apply no same bunch pileup cut."}; + Configurable isVertexITSTPC{"isVertexITSTPC", false, "Apply IsVertexITSTPC cut."}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", false, "Apply IsGoodZvtxFT0vsPV cut."}; + } eventCuts; struct : ConfigurableGroup { - Configurable tpcnSigmaPos{"tpcnSigmaPos", 3.0f, "TPC NSigma cut of the positive particle."}; - Configurable tpcnSigmaNeg{"tpcnSigmaNeg", 3.0f, "TPC NSigma cut of the negative particle."}; - Configurable tpcPidOnly{"tpcPidOnly", false, "Use TPC only for PID."}; - Configurable combinedNSigma{"combinedNSigma", 3.0f, "Combined NSigma cut for combined TPC and TOF NSigma cut."}; - Configurable ptTOFThreshold{"ptTOFThreshold", 0.5f, "Threshold for applying TOF."}; - Configurable rapidity{"rapidity", 0.5f, "Rapidity cut (maximum)."}; - Configurable etatrack{"etatrack", 0.8f, "Eta cut for track."}; Configurable pt{"pt", 0.15f, "Cut: Minimal value of tracks pt."}; + Configurable etatrack{"etatrack", 1.0f, "Cut: Maximal value of tracks eta."}; Configurable dcaXY{"dcaXY", 1.0f, "Cut: Maximal value of tracks DCA XY."}; Configurable dcaZ{"dcaZ", 1.0f, "Cut: Maximal value of tracks DCA Z."}; - Configurable globalTrack{"globalTrack", false, "Use global track selection."}; - Configurable inelGrater0{"inelGrater0", true, "Select events with INEL>0."}; - Configurable tpcNClsFound{"tpcNClsFound", 70, "Cut: Minimal value of found TPC clasters"}; - Configurable vzCut{"vzCut", 10.0f, "Cut: Maximal value of Z vertex position."}; - } cut; + Configurable ptDependentDCA{"ptDependentDCA", false, "Use pT dependent DCA cut according to formula dcaXY = n_sigma * (0.0105 + 0.0350 * pow(pt, -1.1))."}; + Configurable dcaNSigma{"dcaNSigma", 7.0f, "Cut: Maximal value of tracks DCA NSigma."}; + Configurable tpcnSigmaPos{"tpcnSigmaPos", 10.0f, "Cut: Maximal value of TPC NSigma of the positive particle."}; + Configurable tpcnSigmaNeg{"tpcnSigmaNeg", 10.0f, "Cut: Maximal value of TPC NSigma of the negative particle."}; + Configurable tpcPidOnly{"tpcPidOnly", false, "Use TPC only for PID."}; + Configurable combinedNSigma{"combinedNSigma", 3.0f, "Cut: Maximal value of NSigma for combined TPC and TOF NSigma cut."}; + Configurable ptTOFThreshold{"ptTOFThreshold", 0.5f, "Cut: Minimal value of tracks pt for using TOF PID."}; + Configurable tofNSigmaPosShift{"tofNSigmaPosShift", 0.0f, "Shift of TOF NSigma of the positive particle."}; + Configurable tofNSigmaNegShift{"tofNSigmaNegShift", 0.0f, "Shift of TOF NSigma of the negative particle."}; + Configurable tpcNClsFound{"tpcNClsFound", 0, "Cut: Minimal value of found TPC clusters"}; + Configurable tpcNClsCrossedRows{"tpcNClsCrossedRows", 0, "Cut: Minimal value of crossed rows in TPC"}; + Configurable globalTrack{"globalTrack", false, "Use isGlobalTrack track selection."}; + Configurable primaryTrack{"primaryTrack", false, "Use isPrimaryTrack track selection."}; + Configurable pvContributor{"pvContributor", false, "Use isPVContributor track selection."}; + Configurable rapidity{"rapidity", 0.5f, "Cut: Maximal value of particle rapidity."}; + } trackCuts; struct : ConfigurableGroup { - Configurable verboselevel{"verboselevel", 0, "Verbose level"}; - Configurable refresh{"refresh", 0, "Freqency of print event information."}; - Configurable refreshIndex{"refreshIndex", 0, "Freqency of print event information index."}; - } verbose; + Configurable bcBorderCutsGen{"bcBorderCutsGen", false, "Use BC TF/ITS ROF border cuts on Gen events."}; + Configurable rctCheckGen{"rctCheckGen", false, "Use RCT flag cut on Gen events."}; + Configurable rctCheckerLabel{"rctCheckerLabel", "CBT_hadronPID", "RCT flag checker label"}; + Configurable rctCheckerZDC{"rctCheckerZDC", false, "RCT flag checker ZDC check"}; + Configurable rctCheckerLimitAcceptAsBad{"rctCheckerLimitAcceptAsBad", false, "RCT flag checker treat Limited Acceptance As Bad"}; + + } mcCuts; + + RCTFlagsChecker rctChecker; Configurable> sparseAxes{"sparseAxes", std::vector{o2::analysis::rsn::pair_axis::names}, "Axes."}; Configurable> sysAxes{"sysAxes", std::vector{o2::analysis::rsn::systematic_axis::names}, "Axes."}; @@ -107,69 +136,68 @@ struct PhianalysisTHnSparse { ConfigurableAxis vzaxis{"vzaxis", {40, -20., 20.}, "Z vertex position axis binning."}; ConfigurableAxis multiplicityaxis{"multiplicityaxis", {50, 0., 5000.}, "Multiplicity axis binning."}; ConfigurableAxis centralityaxis{"centralityaxis", {20, 0., 100.}, "Centrality axis binning."}; - ConfigurableAxis etaaxis{"etaaxis", {16., -1.0 * static_cast(cut.etatrack), static_cast(cut.etatrack)}, "Pseudorapidity axis binning."}; - ConfigurableAxis rapidityaxis{"rapidityaxis", {10., -1.0 * static_cast(cut.rapidity), static_cast(cut.rapidity)}, "Rapidity axis binning."}; - ConfigurableAxis nsigmaaxisPos{"nsigmaaxisPos", {1, -static_cast(cut.tpcnSigmaPos), static_cast(cut.tpcnSigmaPos)}, "NSigma of positive particle axis binning in THnSparse."}; - ConfigurableAxis nsigmaaxisNeg{"nsigmaaxisNeg", {1, -static_cast(cut.tpcnSigmaNeg), static_cast(cut.tpcnSigmaNeg)}, "NSigma of negative particle axis binning in THnSparse."}; + ConfigurableAxis etaaxis{"etaaxis", {16., -1.0 * static_cast(trackCuts.etatrack), static_cast(trackCuts.etatrack)}, "Pseudorapidity axis binning."}; + ConfigurableAxis rapidityaxis{"rapidityaxis", {10., -1.0 * static_cast(trackCuts.rapidity), static_cast(trackCuts.rapidity)}, "Rapidity axis binning."}; + ConfigurableAxis nsigmaaxisPos{"nsigmaaxisPos", {1, -static_cast(trackCuts.tpcnSigmaPos), static_cast(trackCuts.tpcnSigmaPos)}, "NSigma of positive particle axis binning in THnSparse."}; + ConfigurableAxis nsigmaaxisNeg{"nsigmaaxisNeg", {1, -static_cast(trackCuts.tpcnSigmaNeg), static_cast(trackCuts.tpcnSigmaNeg)}, "NSigma of negative particle axis binning in THnSparse."}; // mixing - using BinningTypeVzMu = ColumnBinningPolicy>; + using BinningTypeVzMu = ColumnBinningPolicy>; using BinningTypeVzCe = ColumnBinningPolicy; - Configurable numberofMixedEvents{"numberofMixedEvents", 5, "Number of events that should be mixed."}; + Configurable nMixedEvents{"nMixedEvents", 5, "Number of events that should be mixed."}; ConfigurableAxis axisVertexMixing{"axisVertexMixing", {5, -10, 10}, "Z vertex axis binning for mixing"}; - ConfigurableAxis axisMultiplicityMixing{"axisMultiplicityMixing", {5, 0, 5000}, "TPC multiplicity for bin"}; - ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {10, 0, 100}, "Multiplicity percentil binning for mixing"}; + ConfigurableAxis axisMultiplicityMixing{"axisMultiplicityMixing", {5, 0, 5000}, "FT0M amplitude binning for event mixing."}; + ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {10, 0, 100}, "FT0M centrality percentile binning for event mixing."}; // rotational - Configurable numberofRotations{"numberofRotations", 1, "Number of rotations for rotational background estimation."}; + Configurable nRotations{"nRotations", 1, "Number of rotations for rotational background estimation."}; Configurable startingAngle{"startingAngle", 0, "Starting angle for rotational background estimation."}; // other axes - ConfigurableAxis axisNch{"axisNch", {1000, 0.0f, +1000.0f}, "Number of charged particles."}; - ConfigurableAxis axisResolutionPt{"axisResolutionPt", {1001, -1.0f, +1.0f}, "Resolution of Pt."}; - ConfigurableAxis axisResolutionPtPhi{"axisResolutionPtPhi", {1001, -0.01f, +0.01f}, "Resolution of Pt and Phi."}; - ConfigurableAxis axisResolutionMass{"axisResolutionMass", {1001, -0.01f, +0.01f}, "Resolution of Mass."}; - ConfigurableAxis axisResolutionVz{"axisResolutionVz", {1001, -3.0f, +3.0f}, "Resolution of Vz."}; - ConfigurableAxis massShiftAxis{"massShiftAxis", {1001, -0.02f, 0.02f}, "Mass correction axis."}; + ConfigurableAxis axisNch{"axisNch", {1000, 0.0, +1000.0}, "Number of charged particles."}; + ConfigurableAxis axisResolutionPt{"axisResolutionPt", {1001, -1.0, +1.0}, "Resolution of Pt."}; + ConfigurableAxis axisResolutionPtPhi{"axisResolutionPtPhi", {1001, -0.01, +0.01}, "Resolution of Pt and Phi."}; + ConfigurableAxis axisResolutionMass{"axisResolutionMass", {1001, -0.01, +0.01}, "Resolution of Mass."}; + ConfigurableAxis axisResolutionVz{"axisResolutionVz", {1001, -3.0, +3.0}, "Resolution of Vz."}; + ConfigurableAxis axisQAPt{"axisQAPt", {15, 0.0, 15.0}, "QA Pt axis binning."}; + ConfigurableAxis axisQAMult{"axisQAMult", {10, 0.0, 100.0}, "QA Multiplicity axis binning."}; + ConfigurableAxis axisQACent{"axisQACent", {101, 0.0f, 101.0f}, "QA Centrality axis binning."}; // Axes specifications - AxisSpec posZaxis = {400, -20., 20., "V_{z} (cm)"}; - AxisSpec dcaXYaxis = {1000, -1.0, 1.0, "DCA_{xy} (cm)"}; - AxisSpec dcaZaxis = {1000, -1.0, 1.0, "DCA_{z} (cm)"}; - AxisSpec etaQAaxis = {1000, -1.0, 1.0, "#eta"}; - AxisSpec tpcNClsFoundQAaxis = {110, 50., 160., "tpcNClsFound"}; - AxisSpec massShiftRelAxis = {101, -0.03f, 0.03f, ""}; + AxisSpec vzQAaxis = {200, -20., 20., "V_{z} (cm)"}; + AxisSpec dcaXYQAaxis = {2000, -0.2, 0.2, "DCA_{xy} (cm)"}; + AxisSpec dcaZQAaxis = {2000, -0.2, 0.2, "DCA_{z} (cm)"}; + AxisSpec etaQAaxis = {200, -1.0, 1.0, "#eta"}; + AxisSpec rapidityQAaxis = {200, -1.0, 1.0, "y"}; + AxisSpec tpcNClsQAaxis = {200, 0., 200., "TPC NClusters"}; + AxisSpec nSigmaTPCQAaxis = {200, -10., 10., "n#sigma_{TPC} K^{#pm}"}; + AxisSpec nSigmaTOFQAaxis = {200, -10., 10., "n#sigma_{TOF} K^{#pm}"}; + AxisSpec pQAaxis = {1490, 0.1, 15.0, "p (GeV/c)"}; + AxisSpec dEdxQAaxis = {2000, 0., 200., "dE/dx (a.u.)"}; + AxisSpec betaQAaxis = {700, 0.5, 1.2, "#beta"}; + AxisSpec dPhiQAaxis = {100, -o2::constants::math::TwoPI, o2::constants::math::TwoPI, "#Delta#phi (rad)"}; + AxisSpec dThetaQAaxis = {100, -o2::constants::math::PI, o2::constants::math::PI, "#Delta#theta (rad)"}; + AxisSpec dEtaQAaxis = {200, -1.0, 1.0, "#Delta#eta"}; HistogramRegistry registry{"registry"}; o2::analysis::rsn::Output* rsnOutput = nullptr; - Service pdg; + Service pdg{}; - int n = 0; float massPos = o2::track::PID::getMass(3); float massNeg = o2::track::PID::getMass(3); int pion = 2; int kaon = 3; int proton = 4; double* pointPair = nullptr; - double* pointSys = nullptr; ROOT::Math::PxPyPzMVector d1, d2, mother, motherGen; - bool produceTrue, produceLikesign, produceQA, produceStats, produceRotational, dataQA, MCTruthQA, globalTrack, inelGrater0, tpcPidOnly = false; - float tpcnSigmaPos = 100.0f; - float tpcnSigmaNeg = 100.0f; - float combinedNSigma = 100.0f; - float ptTOFThreshold = 0.5f; - int tpcNClsFound = 70; + bool dataQA = false; int dauSize = 2; - float vzCut = 10.0f; rsn::MixingType mixingType = rsn::MixingType::none; - Filter triggerFilter = (o2::aod::evsel::sel8 == true); - Filter vtxFilter = (nabs(o2::aod::collision::posZ) < vzCut); - using EventCandidates = soa::Join; using EventCandidate = EventCandidates::iterator; - using TrackCandidates = soa::Join; + using TrackCandidates = soa::Join; using EventCandidatesMC = soa::Join; using TrackCandidatesMC = soa::Join; @@ -194,13 +222,18 @@ struct PhianalysisTHnSparse { LOGF(info, " Positive: %d, mass: %f", static_cast(daughterPos), massPos); LOGF(info, " Negative: %d, mass: %f", static_cast(daughterNeg), massNeg); + AxisSpec centQAAxis = {axisQACent, "FT0M (%)"}; + AxisSpec nchQAAxis = {axisNch, "N_{ch}"}; + AxisSpec multQAAxis = {axisQAMult, "FT0M (%)"}; + AxisSpec ptQAAxis = {axisQAPt, "p_{T} (GeV/c)"}; + // Sparse axes AxisSpec invAxis = {invaxis, "Inv. mass (GeV/c^{2})", "im"}; AxisSpec ptAxis = {ptaxis, "p_{T} (GeV/c)", "pt"}; - AxisSpec muAxis = {multiplicityaxis, "N", "mu"}; - AxisSpec mumAxis = {multiplicityaxis, "N", "mum"}; - AxisSpec ceAxis = {centralityaxis, "N", "ce"}; - AxisSpec cemAxis = {centralityaxis, "N", "cem"}; + AxisSpec muAxis = {multiplicityaxis, "FT0M (Ampl.)", "mu"}; + AxisSpec mumAxis = {multiplicityaxis, "FT0M (Ampl.)", "mum"}; + AxisSpec ceAxis = {centralityaxis, "FT0M (%)", "ce"}; + AxisSpec cemAxis = {centralityaxis, "FT0M (%)", "cem"}; AxisSpec etaAxis = {etaaxis, "#eta", "eta"}; AxisSpec yAxis = {rapidityaxis, "y", "y"}; AxisSpec nsAxisPos = {nsigmaaxisPos, fmt::format("nSigma of positive particle ({})", massPos), "ns1"}; @@ -216,55 +249,47 @@ struct PhianalysisTHnSparse { std::vector allAxes = {invAxis, ptAxis, muAxis, ceAxis, nsAxisPos, nsAxisNeg, etaAxis, yAxis, vzAxis, mumAxis, cemAxis, vzmAxis}; std::vector allAxesSys = {tpcNClsFoundAxis}; - produceQA = static_cast(produce.produceQA); - produceStats = static_cast(produce.produceStats); - produceTrue = static_cast(produce.produceTrue); - produceLikesign = static_cast(produce.produceLikesign); mixingType = rsn::mixingTypeName(static_cast(produce.eventMixing)); - produceRotational = static_cast(produce.produceRotational); - tpcnSigmaPos = static_cast(cut.tpcnSigmaPos); - tpcnSigmaNeg = static_cast(cut.tpcnSigmaNeg); - tpcNClsFound = static_cast(cut.tpcNClsFound); - globalTrack = static_cast(cut.globalTrack); - inelGrater0 = static_cast(cut.inelGrater0); - combinedNSigma = static_cast(cut.combinedNSigma); - tpcPidOnly = static_cast(cut.tpcPidOnly); - ptTOFThreshold = static_cast(cut.ptTOFThreshold); - vzCut = static_cast(cut.vzCut); pointPair = new double[static_cast(o2::analysis::rsn::PairAxisType::unknown)]; - pointSys = new double[static_cast(o2::analysis::rsn::SystematicsAxisType::unknown)]; rsnOutput = new o2::analysis::rsn::OutputSparse(); - rsnOutput->init(sparseAxes, allAxes, sysAxes, allAxesSys, produceTrue, mixingType, produceLikesign, produceRotational, ®istry); + rsnOutput->init(sparseAxes, allAxes, sysAxes, allAxesSys, static_cast(produce.produceMC), mixingType, static_cast(produce.produceLikesign), static_cast(produce.produceRotational), static_cast(eventCuts.inelGt0), ®istry); + + rctChecker.init(mcCuts.rctCheckerLabel, mcCuts.rctCheckerZDC, mcCuts.rctCheckerLimitAcceptAsBad, mcCuts.rctCheckGen); // Print summary of configuration LOGF(info, "=== PhianalysisTHnSparse configuration summary ==="); - LOGF(info, "produceQA: %s", produceQA ? "true" : "false"); - LOGF(info, "produceStats: %s", produceStats ? "true" : "false"); - LOGF(info, "produceTrue: %s", static_cast(produce.produceTrue) ? "true" : "false"); + LOGF(info, "produceMC: %s", static_cast(produce.produceMC) ? "true" : "false"); LOGF(info, "produceLikesign: %s", static_cast(produce.produceLikesign) ? "true" : "false"); LOGF(info, "produceRotational: %s", static_cast(produce.produceRotational) ? "true" : "false"); LOGF(info, "eventMixing: %s", static_cast(produce.eventMixing).c_str()); + LOGF(info, "inelGt0: %s", static_cast(eventCuts.inelGt0) ? "true" : "false"); + LOGF(info, "noSameBunchPileup: %s", static_cast(eventCuts.noSameBunchPileup) ? "true" : "false"); + LOGF(info, "isVertexITSTPC: %s", static_cast(eventCuts.isVertexITSTPC) ? "true" : "false"); + LOGF(info, "isGoodZvtxFT0vsPV: %s", static_cast(eventCuts.isGoodZvtxFT0vsPV) ? "true" : "false"); + LOGF(info, "vzCut: %.2f", static_cast(eventCuts.vzCut)); LOGF(info, "daughterPos: %d (PDG: %d)", static_cast(daughterPos), static_cast(daughterPosPDG)); LOGF(info, "daughterNeg: %d (PDG: %d)", static_cast(daughterNeg), static_cast(daughterNegPDG)); LOGF(info, "motherPDG: %d", static_cast(motherPDG)); - LOGF(info, "tpcnSigmaPos: %.2f", tpcnSigmaPos); - LOGF(info, "tpcnSigmaNeg: %.2f", tpcnSigmaNeg); - LOGF(info, "tpcPidOnly: %s", tpcPidOnly ? "true" : "false"); - LOGF(info, "combinedNSigma: %.2f", combinedNSigma); - LOGF(info, "ptTOFThreshold: %.2f", ptTOFThreshold); - LOGF(info, "rapidity: %.2f", static_cast(cut.rapidity)); - LOGF(info, "etatrack: %.2f", static_cast(cut.etatrack)); - LOGF(info, "pt (min): %.2f", static_cast(cut.pt)); - LOGF(info, "dcaXY: %.2f", static_cast(cut.dcaXY)); - LOGF(info, "dcaZ: %.2f", static_cast(cut.dcaZ)); - LOGF(info, "globalTrack: %s", globalTrack ? "true" : "false"); - LOGF(info, "inelGrater0: %s", inelGrater0 ? "true" : "false"); - LOGF(info, "tpcNClsFound: %d", tpcNClsFound); - LOGF(info, "vzCut: %.2f", vzCut); - LOGF(info, "mixingType: %d", static_cast(mixingType)); - LOGF(info, "numberofMixedEvents: %d", static_cast(numberofMixedEvents)); - LOGF(info, "numberofRotations: %d", static_cast(numberofRotations)); + LOGF(info, "pt (min): %.2f", static_cast(trackCuts.pt)); + LOGF(info, "eta (max): %.2f", static_cast(trackCuts.etatrack)); + LOGF(info, "dcaXY: %.2f", static_cast(trackCuts.dcaXY)); + LOGF(info, "dcaZ: %.2f", static_cast(trackCuts.dcaZ)); + LOGF(info, "tpcnSigmaPos: %.2f", static_cast(trackCuts.tpcnSigmaPos)); + LOGF(info, "tpcnSigmaNeg: %.2f", static_cast(trackCuts.tpcnSigmaNeg)); + LOGF(info, "tpcPidOnly: %s", static_cast(trackCuts.tpcPidOnly) ? "true" : "false"); + LOGF(info, "combinedNSigma: %.2f", static_cast(trackCuts.combinedNSigma)); + LOGF(info, "ptTOFThreshold: %.2f", static_cast(trackCuts.ptTOFThreshold)); + LOGF(info, "tofNSigmaPosShift: %.2f", static_cast(trackCuts.tofNSigmaPosShift)); + LOGF(info, "tofNSigmaNegShift: %.2f", static_cast(trackCuts.tofNSigmaNegShift)); + LOGF(info, "tpcNClsFound: %d", static_cast(trackCuts.tpcNClsFound)); + LOGF(info, "tpcNClsCrossedRows: %d", static_cast(trackCuts.tpcNClsCrossedRows)); + LOGF(info, "globalTrack: %s", static_cast(trackCuts.globalTrack) ? "true" : "false"); + LOGF(info, "primaryTrack: %s", static_cast(trackCuts.primaryTrack) ? "true" : "false"); + LOGF(info, "pvContributor: %s", static_cast(trackCuts.pvContributor) ? "true" : "false"); + LOGF(info, "rapidity: %.2f", static_cast(trackCuts.rapidity)); + LOGF(info, "nMixedEvents: %d", static_cast(nMixedEvents)); + LOGF(info, "nRotations: %d", static_cast(nRotations)); LOGF(info, "startingAngle: %d", static_cast(startingAngle)); LOGF(info, "sparseAxes: "); for (const auto& axis : static_cast>(sparseAxes)) { @@ -276,222 +301,323 @@ struct PhianalysisTHnSparse { } LOGF(info, "==============================================="); - if (produceQA) { - // Event QA - registry.add("QAEvent/hSelection", "Event selection statistics", kTH1D, {{4, 0.0f, 4.0f}}); - auto hEvent = registry.get(HIST("QAEvent/hSelection")); + // ------------------- Event QA ------------------- + + registry.add("QA/Event/hVtxZ", "Vertex position along the z-axis", kTH1F, {vzQAaxis}); + auto hVtxZ = registry.get(HIST("QA/Event/hVtxZ")); + + registry.add("QA/Event/hCent", "FT0M (%)", kTH1F, {{101, 0., 101.}}); + auto hCent = registry.get(HIST("QA/Event/hCent")); + hCent->GetXaxis()->SetTitle("FT0M (%)"); + registry.add("QA/Event/hMult", "Amplitude of non-zero channels in the FT0A + FT0C) ", kTH1F, {{300, 0., 30000.}}); + auto hMult = registry.get(HIST("QA/Event/hMult")); + hMult->GetXaxis()->SetTitle("FT0M Ampl."); + + if (static_cast(produce.produceQA)) { + + registry.add("QA/Event/hSelection", "Event selection statistics", kTH1D, {{11, 0.0f, 11.0f}}); + auto hEvent = registry.get(HIST("QA/Event/hSelection")); hEvent->GetXaxis()->SetBinLabel(1, "all events"); - hEvent->GetXaxis()->SetBinLabel(2, "Events passing trigger sel8"); - hEvent->GetXaxis()->SetBinLabel(3, "Events passing |V_{z}| cut"); - hEvent->GetXaxis()->SetBinLabel(4, "Events passing INEL>0 cut"); + hEvent->GetXaxis()->SetBinLabel(2, Form("|V_{z}| < %0.0f cm", static_cast(eventCuts.vzCut))); + hEvent->GetXaxis()->SetBinLabel(3, "isTriggerTVX"); + hEvent->GetXaxis()->SetBinLabel(4, "noTimeFrameBorder"); + hEvent->GetXaxis()->SetBinLabel(5, "noITSROFrameBorder"); + hEvent->GetXaxis()->SetBinLabel(6, "sel8"); + hEvent->GetXaxis()->SetBinLabel(7, "IsVertexITSTPC"); + hEvent->GetXaxis()->SetBinLabel(8, "noSameBunchPileup"); + hEvent->GetXaxis()->SetBinLabel(9, "IsGoodZvtxFT0vsPV"); + hEvent->GetXaxis()->SetBinLabel(10, "INEL"); + hEvent->GetXaxis()->SetBinLabel(11, "INEL>0"); hEvent->SetMinimum(0.1); - registry.add("QAEvent/hVtxZ", "Vertex position along the z-axis", kTH1F, {posZaxis}); - registry.add("QAEvent/hCent", "Distribution of multiplicity percentile", kTH1F, {{101, 0., 101.}}); - registry.add("QAEvent/hMult", "Multiplicity (amplitude of non-zero channels in the FT0A + FT0C) ", kTH1F, {{300, 0., 30000.}}); + registry.add("QA/Event/hCentNch", "Event centrality vs multiplicity", kTH2F, {centQAAxis, nchQAAxis}); - // Track QA - registry.add("QATrack/hSelection", "Tracks statistics", kTH1D, {{9, 0.0f, 9.0f}}); - auto hTrack = registry.get(HIST("QATrack/hSelection")); + // ----------------------- Track QA ----------------------- + registry.add("QA/Track/hSelection", "Track selection statistics", kTH1D, {{9, 0.0f, 9.0f}}); + auto hTrack = registry.get(HIST("QA/Track/hSelection")); hTrack->GetXaxis()->SetBinLabel(1, "all tracks"); - hTrack->GetXaxis()->SetBinLabel(2, "passed pT cut"); - hTrack->GetXaxis()->SetBinLabel(3, "passed eta cut"); - hTrack->GetXaxis()->SetBinLabel(4, "passed DCA cut"); - hTrack->GetXaxis()->SetBinLabel(5, "passed PID cut"); - hTrack->GetXaxis()->SetBinLabel(6, "passed tpcNClsFound cut"); - hTrack->GetXaxis()->SetBinLabel(7, "passed isPrimaryTrack cut"); - hTrack->GetXaxis()->SetBinLabel(8, "passed isPVContributor cut"); - hTrack->GetXaxis()->SetBinLabel(9, "passed all cuts"); + hTrack->GetXaxis()->SetBinLabel(2, Form("pT > %.2f", static_cast(trackCuts.pt))); + hTrack->GetXaxis()->SetBinLabel(3, Form("eta < %.1f", static_cast(trackCuts.etatrack))); + hTrack->GetXaxis()->SetBinLabel(4, "DCA cuts"); + hTrack->GetXaxis()->SetBinLabel(5, "PID cuts"); + hTrack->GetXaxis()->SetBinLabel(6, Form("tpcNClsFound > %d", static_cast(trackCuts.tpcNClsFound))); + hTrack->GetXaxis()->SetBinLabel(7, Form("tpcNClsCrossedRows > %d", static_cast(trackCuts.tpcNClsCrossedRows))); + hTrack->GetXaxis()->SetBinLabel(8, Form("%s", static_cast(trackCuts.globalTrack) ? "isGlobalTrack" : "isPrimaryTrack")); + hTrack->GetXaxis()->SetBinLabel(9, "isPVContributor"); hTrack->SetMinimum(0.1); - registry.add("QATrack/hRapidity", "Rapidity distribution of K^{+} and K^{-}", kTH1F, {{200, -1, 1}}); - registry.add("QATrack/hEta", "Pseudorapidity distribution of K^{+} and K^{-}", kTH1F, {{200, -1, 1}}); - registry.add("QATrack/hTPCNClsFound", "Distribution of TPC NClsFound of K^{+} and K^{-}", kTH1F, {tpcNClsFoundQAaxis}); - registry.add("QATrack/hDCAxy", "Distribution of DCA_{xy} of K^{+} and K^{-}", kTH1F, {dcaXYaxis}); - registry.add("QATrack/hDCAz", "Distribution of DCA_{z} of K^{+} and K^{-}", kTH1F, {dcaZaxis}); - registry.add("QATrack/hPt", "Distribution of p_{T} of K^{+} and K^{-}", kTH1F, {ptaxis}); - - // Phi candidate QA - registry.add("QAPhi/hRapidity", "Rapidity distribution of #Phi candidates", kTH1F, {{200, -1, 1}}); - registry.add("QAPhi/hEta", "Pseudorapidity distribution of #Phi candidates", kTH1F, {{200, -1, 1}}); - registry.add("QAPhi/hdPhi", "Azimuthal distribution (#Delta#phi) of #Phi candidates", kTH1F, {{100, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}}); - auto hdPhi = registry.get(HIST("QAPhi/hdPhi")); - hdPhi->GetXaxis()->SetTitle("#Delta#phi (rad)"); - - registry.add("QAPhi/h2dPhiPt", "Azimuthal distribution (#Delta#phi) of #Phi candidates vs p_{T}", kTH2F, {ptaxis, {100, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}}); - auto h2dPhiPt = registry.get(HIST("QAPhi/h2dPhiPt")); - h2dPhiPt->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - h2dPhiPt->GetYaxis()->SetTitle("#Delta#phi (rad)"); - - registry.add("QAPhi/hTheta", "Polar distribution of #Phi candidates", kTH1F, {{100, 0.0f, o2::constants::math::PI}}); - auto hTheta = registry.get(HIST("QAPhi/hTheta")); - hTheta->GetXaxis()->SetTitle("#theta (rad)"); - - registry.add("QAPhi/h2dThetaPt", "Polar distribution (#Delta#theta) of #Phi candidates vs p_{T}", kTH2F, {ptaxis, {100, -o2::constants::math::PI, o2::constants::math::PI}}); - - auto h2dThetaPt = registry.get(HIST("QAPhi/h2dThetaPt")); - h2dThetaPt->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - h2dThetaPt->GetYaxis()->SetTitle("#Delta#theta (rad)"); - - // Rotational background QA - if (produceRotational) { - registry.add("QARotational/hRapidity", "Rapidity distribution of #Phi candidates from rotational background", kTH1F, {{200, -1, 1}}); - registry.add("QARotational/hEta", "Pseudorapidity distribution of #Phi candidates from rotational background", kTH1F, {{200, -1, 1}}); - registry.add("QARotational/hdPhi", "Rotational background: Azimuthal distribution (#Delta#phi)", kTH1F, {{100, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}}); - auto hRPhi = registry.get(HIST("QARotational/hdPhi")); - hRPhi->GetXaxis()->SetTitle("#Delta#phi"); - - registry.add("QARotational/h2dPhiPt", "Rotational background: Azimuthal distribution (#Delta#phi) vs p_{T}", kTH2F, {ptaxis, {100, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}}); - auto hR2dPhiPt = registry.get(HIST("QARotational/h2dPhiPt")); - hR2dPhiPt->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - hR2dPhiPt->GetYaxis()->SetTitle("#Delta#phi"); - - registry.add("QARotational/hTheta", "Rotational background: Polar distribution (#theta)", kTH1F, {{100, 0.0f, o2::constants::math::PI}}); - auto hRdTheta = registry.get(HIST("QARotational/hTheta")); - hRdTheta->GetXaxis()->SetTitle("#theta (rad)"); - - registry.add("QARotational/h2dThetaPt", "Rotational background: Polar distribution (#Delta#theta) vs p_{T}", kTH2F, {ptaxis, {100, -o2::constants::math::PI, o2::constants::math::PI}}); - auto hR2dThetaPt = registry.get(HIST("QARotational/h2dThetaPt")); - hR2dThetaPt->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - hR2dThetaPt->GetYaxis()->SetTitle("#Delta#theta"); - } + registry.add("QA/Track/hRapidity", "Rapidity distribution of Tracks", kTH3F, {ptQAAxis, multQAAxis, rapidityQAaxis}); + registry.add("QA/Track/hEta", "Pseudorapidity distribution of Tracks", kTH3F, {ptQAAxis, multQAAxis, etaQAaxis}); + registry.add("QA/Track/hTPCNClsFound", "Number of found TPC clusters of Tracks", kTH3F, {ptQAAxis, multQAAxis, tpcNClsQAaxis}); + registry.add("QA/Track/hTPCNClsCrossedRows", "Number of crossed rows in TPC of Tracks", kTH3F, {ptQAAxis, multQAAxis, tpcNClsQAaxis}); + registry.add("QA/Track/hDCAxy", "Distribution of DCA_{xy} of Tracks", kTH3F, {ptQAAxis, multQAAxis, dcaXYQAaxis}); + registry.add("QA/Track/hDCAz", "Distribution of DCA_{z} of Tracks", kTH3F, {ptQAAxis, multQAAxis, dcaZQAaxis}); + registry.add("QA/Track/hPt", "Distribution of p_{T} of Tracks", kTH2F, {ptQAAxis, multQAAxis}); + + registry.add("QA/Kaon/hRapidity", "Rapidity distribution of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, rapidityQAaxis}); + registry.add("QA/Kaon/hEta", "Pseudorapidity distribution of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, etaQAaxis}); + registry.add("QA/Kaon/hTPCNClsFound", "Number of found TPC clusters of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, tpcNClsQAaxis}); + registry.add("QA/Kaon/hTPCNClsCrossedRows", "Number of crossed rows in TPC of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, tpcNClsQAaxis}); + registry.add("QA/Kaon/hDCAxy", "Distribution of DCA_{xy} of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, dcaXYQAaxis}); + registry.add("QA/Kaon/hDCAz", "Distribution of DCA_{z} of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, dcaZQAaxis}); + registry.add("QA/Kaon/hPt", "Distribution of p_{T} of K^{+} and K^{-}", kTH2F, {ptQAAxis, multQAAxis}); + + // ---------------------- PID QA ---------------------- + + registry.add("QA/PID/hTPCNSigma", "Distribution of TPC nSigma", kTH3F, {ptQAAxis, multQAAxis, nSigmaTPCQAaxis}); + registry.add("QA/PID/hTPCNSigmaK", "Distribution of TPC nSigma of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, nSigmaTPCQAaxis}); + registry.add("QA/PID/hTOFNSigma", "Distribution of TOF nSigma", kTH3F, {ptQAAxis, multQAAxis, nSigmaTOFQAaxis}); + registry.add("QA/PID/hTOFNSigmaK", "Distribution of TOF nSigma of K^{+} and K^{-}", kTH3F, {ptQAAxis, multQAAxis, nSigmaTOFQAaxis}); + registry.add("QA/PID/hTPCTOFnSigma", "", kTH3F, {ptQAAxis, nSigmaTPCQAaxis, nSigmaTOFQAaxis}); + + registry.add("QA/PID/hTPCdEdxP", "dE/dx vs p of charged particles", kTH2F, {pQAaxis, dEdxQAaxis}); + registry.add("QA/PID/hTPCdEdxPK", "dE/dx vs p of K^{+} and K^{-}", kTH2F, {pQAaxis, dEdxQAaxis}); + registry.add("QA/PID/hTOFBetaP", "TOF #beta vs p of charged particles", kTH2F, {pQAaxis, betaQAaxis}); + registry.add("QA/PID/hTOFBetaPK", "TOF #beta vs p of K^{+} and K^{-}", kTH2F, {pQAaxis, betaQAaxis}); + + // ----------------------- Phi candidate QA ----------------------- + registry.add("QA/Phi/hRapidity", "Rapidity distribution of #Phi candidates", kTH3F, {ptQAAxis, multQAAxis, rapidityQAaxis}); + registry.add("QA/Phi/hEta", "Pseudorapidity distribution of #Phi candidates", kTH3F, {ptQAAxis, multQAAxis, etaQAaxis}); + registry.add("QA/Phi/hdPhi", "Azimuthal distribution (#Delta#phi) of #Phi candidates", kTH3F, {ptQAAxis, multQAAxis, dPhiQAaxis}); + registry.add("QA/Phi/hdPhideta", "Azimuthal distribution (#Delta#phi) of #Phi candidates vs #eta", kTH2F, {dEtaQAaxis, dPhiQAaxis}); + registry.add("QA/Phi/hdTheta", "Polar distribution (#Delta#theta) of #Phi candidates vs p_{T}", kTH3F, {ptQAAxis, multQAAxis, dThetaQAaxis}); + } - // Mixing QA - if (mixingType != rsn::MixingType::none) { - registry.add("QAMixing/hSelection", "Event mixing selection statistics", kTH1D, {{1, 0.0f, 1.0f}}); - auto hEM = registry.get(HIST("QAMixing/hSelection")); - hEM->GetXaxis()->SetBinLabel(1, "Full event mixing statistics"); - hEM->SetMinimum(0.1); - - registry.add("QAMixing/h2mu1_mu2", "Event Mixing Multiplicity", kTH2F, {axisMultiplicityMixing, axisMultiplicityMixing}); - auto h2EMmu = registry.get(HIST("QAMixing/h2mu1_mu2")); - h2EMmu->GetXaxis()->SetTitle("1.Event multiplicity"); - h2EMmu->GetYaxis()->SetTitle("2.Event multiplicity"); - - registry.add("QAMixing/h2ce1_ce2", "Event Mixing Centrality", kTH2F, {axisCentralityMixing, axisCentralityMixing}); - auto h2EMce = registry.get(HIST("QAMixing/h2ce1_ce2")); - h2EMce->GetXaxis()->SetTitle("1.Event centrality"); - h2EMce->GetYaxis()->SetTitle("2.Event centrality"); - - registry.add("QAMixing/h2vz1_vz2", "Event Mixing Vertex z", kTH2F, {axisVertexMixing, axisVertexMixing}); - auto hEMTvz = registry.get(HIST("QAMixing/h2vz1_vz2")); - hEMTvz->GetXaxis()->SetTitle("1.Event V_{z}"); - hEMTvz->GetYaxis()->SetTitle("2.Event V_{z}"); - } + // Rotational background QA + if (static_cast(produce.produceRotational) && static_cast(produce.produceQA)) { + // Rotation around z axis + registry.add("QA/RotationZ/hRapidity", "Rapidity distribution of #Phi candidates from rotational background", kTH3F, {ptQAAxis, multQAAxis, rapidityQAaxis}); + registry.add("QA/RotationZ/hEta", "Pseudorapidity distribution of #Phi candidates from rotational background", kTH3F, {ptQAAxis, multQAAxis, etaQAaxis}); + registry.add("QA/RotationZ/hdPhi", "Rotational background: Azimuthal distribution (#Delta#phi)", kTH3F, {ptQAAxis, multQAAxis, dPhiQAaxis}); + registry.add("QA/RotationZ/hdPhideta", "Rotational background: Azimuthal distribution (#Delta#phi) vs #eta", kTH2F, {dEtaQAaxis, dPhiQAaxis}); + registry.add("QA/RotationZ/hdTheta", "Rotational background: Polar distribution (#Delta#theta) vs p_{T}", kTH3F, {ptQAAxis, multQAAxis, dThetaQAaxis}); + // Momentum-axis rotation + registry.add("QA/Rotation/hRapidity", "Rapidity distribution of #Phi candidates from rotational background", kTH3F, {ptQAAxis, multQAAxis, rapidityQAaxis}); + registry.add("QA/Rotation/hEta", "Pseudorapidity distribution of #Phi candidates from rotational background", kTH3F, {ptQAAxis, multQAAxis, etaQAaxis}); + registry.add("QA/Rotation/hdPhi", "Rotational background: Azimuthal distribution (#Delta#phi)", kTH3F, {ptQAAxis, multQAAxis, dPhiQAaxis}); + registry.add("QA/Rotation/hdPhideta", "Rotational background: Azimuthal distribution (#Delta#phi) vs #eta", kTH2F, {dEtaQAaxis, dPhiQAaxis}); + registry.add("QA/Rotation/hdTheta", "Rotational background: Polar distribution (#Delta#theta) vs p_{T}", kTH3F, {ptQAAxis, multQAAxis, dThetaQAaxis}); + } + + // Mixing QA + if (mixingType != rsn::MixingType::none && static_cast(produce.produceQA)) { + + registry.add("QA/Mixing/h2mu1_mu2", "Event Mixing Multiplicity", kTH2F, {axisMultiplicityMixing, axisMultiplicityMixing}); + auto h2EMmu = registry.get(HIST("QA/Mixing/h2mu1_mu2")); + h2EMmu->GetXaxis()->SetTitle("1.Event multiplicity"); + h2EMmu->GetYaxis()->SetTitle("2.Event multiplicity"); + + registry.add("QA/Mixing/h2ce1_ce2", "Event Mixing Centrality", kTH2F, {axisCentralityMixing, axisCentralityMixing}); + auto h2EMce = registry.get(HIST("QA/Mixing/h2ce1_ce2")); + h2EMce->GetXaxis()->SetTitle("1.Event centrality"); + h2EMce->GetYaxis()->SetTitle("2.Event centrality"); + + registry.add("QA/Mixing/h2vz1_vz2", "Event Mixing Vertex z", kTH2F, {axisVertexMixing, axisVertexMixing}); + auto hEMTvz = registry.get(HIST("QA/Mixing/h2vz1_vz2")); + hEMTvz->GetXaxis()->SetTitle("1.Event V_{z}"); + hEMTvz->GetYaxis()->SetTitle("2.Event V_{z}"); + + registry.add("QA/Mixing/hdPhideta", "Mixing background: Azimuthal distribution (#Delta#phi) vs #eta", kTH2F, {dEtaQAaxis, dPhiQAaxis}); + } - // PID QA - // TPC - registry.add("QAPID/hTPCnSigma", "Distribution of TPC nSigma of K^{+} and K^{-}", kTH1F, {{200, -10, 10}}); - auto hTPCnSigma = registry.get(HIST("QAPID/hTPCnSigma")); - hTPCnSigma->GetXaxis()->SetTitle("n#sigma_{TPC} K^{#pm}"); - - registry.add("QAPID/h2TPCnSigma", "", kTH2F, {{200, -10, 10}, {200, -10, 10}}); - auto h2TPCnSigma = registry.get(HIST("QAPID/h2TPCnSigma")); - h2TPCnSigma->GetXaxis()->SetTitle("n#sigma_{TPC} K^{+}"); - h2TPCnSigma->GetYaxis()->SetTitle("n#sigma_{TPC} K^{-}"); - - registry.add("QAPID/h2TPCnSigmaPt", "", kTH2F, {ptaxis, {200, -10, 10}}); - auto h2TPCnSigmaPt = registry.get(HIST("QAPID/h2TPCnSigmaPt")); - h2TPCnSigmaPt->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - h2TPCnSigmaPt->GetYaxis()->SetTitle("n#sigma_{TPC} K^{#pm}"); - - // TOF - registry.add("QAPID/hTOFnSigma", "Distribution of TOF nSigma of K^{+} and K^{-}", kTH1F, {{200, -10, 10}}); - auto hTOFnSigma = registry.get(HIST("QAPID/hTOFnSigma")); - hTOFnSigma->GetXaxis()->SetTitle("n#sigma_{TOF} K^{#pm}"); - - registry.add("QAPID/h2TOFnSigma", "", kTH2F, {{200, -10, 10}, {200, -10, 10}}); - auto h2TOFnSigma = registry.get(HIST("QAPID/h2TOFnSigma")); - h2TOFnSigma->GetXaxis()->SetTitle("n#sigma_{TOF} K^{+}"); - h2TOFnSigma->GetYaxis()->SetTitle("n#sigma_{TOF} K^{-}"); - - registry.add("QAPID/h2TOFnSigmaPt", "", kTH2F, {ptaxis, {200, -10, 10}}); - auto h2TOFnSigmaPt = registry.get(HIST("QAPID/h2TOFnSigmaPt")); - h2TOFnSigmaPt->GetXaxis()->SetTitle("p_{T} (GeV/c)"); - h2TOFnSigmaPt->GetYaxis()->SetTitle("n#sigma_{TOF} K^{#pm}"); - - // MC - if (static_cast(produce.produceTrue)) { - // Rec - registry.add("QAMC/Rec/hSelection", "MC Rec Event statistics", kTH1F, {{5, 0.0f, 5.0f}}); - auto hMCEventTruth = registry.get(HIST("QAMC/Rec/hSelection")); - hMCEventTruth->GetXaxis()->SetBinLabel(1, "Full MC Rec event statistics"); - hMCEventTruth->GetXaxis()->SetBinLabel(2, "MC Rec events passing sel8 cut"); - hMCEventTruth->GetXaxis()->SetBinLabel(3, "MC Rec events passing V_{z} cut"); - hMCEventTruth->GetXaxis()->SetBinLabel(4, "MC Rec events with V_{z} cut and INEL>0"); - hMCEventTruth->GetXaxis()->SetBinLabel(5, "Reconstructed #Phi candidates matched to true #Phi"); - hMCEventTruth->SetMinimum(0.1); - - registry.add("QAMC/Rec/hInvMassTrueFalse", "", kTH1F, {invAxis}); // not written events in True distribution due to repetition of mothers - - // Gen - registry.add("QAMC/Gen/hSelection", "MC Gen Event statistics", kTH1F, {{4, 0.0f, 4.0f}}); - auto hMCEventGen = registry.get(HIST("QAMC/Gen/hSelection")); - hMCEventGen->GetXaxis()->SetBinLabel(1, "Full MC Gen event statistics"); - hMCEventGen->GetXaxis()->SetBinLabel(2, "MC Gen events within V_{z} cut"); - hMCEventGen->GetXaxis()->SetBinLabel(3, "MC Gen events with V_{z} cut and INEL>0"); - hMCEventGen->GetXaxis()->SetBinLabel(4, "Generated #Phi candidates"); - hMCEventGen->SetMinimum(0.1); - - // Resolution - registry.add("Factors/h2ResolutionVz", "Resolution of collision V_{z}", kTH2F, {vzaxis, axisResolutionVz}); - auto hResVz = registry.get(HIST("Factors/h2ResolutionVz")); + // ------------------------- MC QA ------------------------- + if (static_cast(produce.produceMC)) { + // Rec + registry.add("QAMC/Rec/hSelection", "MC Rec True Event statistics", kTH1F, {{3, 0.0f, 3.0f}}); + auto hMCEventTruth = registry.get(HIST("QAMC/Rec/hSelection")); + hMCEventTruth->GetXaxis()->SetBinLabel(1, "all Rec"); // All reconstructed events + hMCEventTruth->GetXaxis()->SetBinLabel(2, "INEL"); // All reconstructed events passing event selection cuts + hMCEventTruth->GetXaxis()->SetBinLabel(3, "INEL>0"); // All reconstructed events passing event selection cuts and INEL>0 cut + hMCEventTruth->SetMinimum(0.1); + + // Gen + registry.add("QAMC/Gen/hSelection", "MC Gen Event statistics", kTH1F, {{4, 0.0f, 4.0f}}); + auto hMCEventGen = registry.get(HIST("QAMC/Gen/hSelection")); + hMCEventGen->GetXaxis()->SetBinLabel(1, "all"); // All generated events + hMCEventGen->GetXaxis()->SetBinLabel(2, "ALORE"); // All generated events with at least one reconstructed event + hMCEventGen->GetXaxis()->SetBinLabel(3, "INEL"); // All generated events passing event selection cuts + hMCEventGen->GetXaxis()->SetBinLabel(4, "INEL>0"); // All generated events passing event selection cuts and INEL>0 cut + hMCEventGen->SetMinimum(0.1); + + // Factors + registry.add("QAMC/Factors/hGenEvents", "Generated events", HistType::kTH2F, {nchQAAxis, {5, 0, 5}}); + auto hGenEvents = registry.get(HIST("QAMC/Factors/hGenEvents")); + hGenEvents->GetYaxis()->SetBinLabel(1, "all Gen"); + hGenEvents->GetYaxis()->SetBinLabel(2, "all Rec"); + hGenEvents->GetYaxis()->SetBinLabel(3, "ALORE"); + hGenEvents->GetYaxis()->SetBinLabel(4, "INEL"); + hGenEvents->GetYaxis()->SetBinLabel(5, "INEL>0"); + + registry.add("QAMC/Factors/hRecEvents", "Reconstructed events", HistType::kTH2F, {centQAAxis, {3, 0, 3}}); + auto hRecEvents = registry.get(HIST("QAMC/Factors/hRecEvents")); + hRecEvents->GetYaxis()->SetBinLabel(1, "all Rec"); + hRecEvents->GetYaxis()->SetBinLabel(2, "INEL"); + hRecEvents->GetYaxis()->SetBinLabel(3, "INEL>0"); + + registry.add("QAMC/Factors/hGenALORESelEvents", "Centrality vs. Multiplicity of Generated Events with at least one reconstructed event passing event selection", kTH2F, {centQAAxis, nchQAAxis}); + registry.add("QAMC/Factors/hGenALORESelEventsInelGt0", "Centrality vs. Multiplicity of Generated Events with at least one reconstructed event passing event selection and INEL>0 cut", kTH2F, {centQAAxis, nchQAAxis}); + registry.add("QAMC/Factors/hGenEventsCentNch", "Event centrality vs MC multiplicity", kTH2F, {centQAAxis, nchQAAxis}); + registry.add("QAMC/Factors/hNrecInGen", "Number of collisions in MC", kTH1F, {{4, -0.5, 3.5}}); + + registry.add("QAMC/Factors/hGenPhi", "Generated #Phi", kTH3D, {nchQAAxis, centQAAxis, ptAxis}); + registry.add("QAMC/Factors/hGenALOREPhi", "Generated #Phi in collisions with at least one reconstructed collision", kTH3F, {nchQAAxis, centQAAxis, ptAxis}); + registry.add("QAMC/Factors/hGenALOREPhiInelGt0", "Generated #Phi in collisions with at least one reconstructed collision passing INEL>0 cut", kTH3F, {nchQAAxis, centQAAxis, ptAxis}); + registry.add("QAMC/Factors/hRecPhi", "Reconstructed #Phi", kTH2F, {centQAAxis, ptAxis}); + + // ----------------------- MC Corrections ----------------------- + + registry.add("QAMC/Gen/hNEvents", "Number of MC Gen Events", kTH1F, {{8, 0.0f, 8.0f}}); + auto hNEventsGen = registry.get(HIST("QAMC/Gen/hNEvents")); + hNEventsGen->GetXaxis()->SetBinLabel(1, "all"); // All generated events + hNEventsGen->GetXaxis()->SetBinLabel(2, "V_z cut"); // Generated events passing Vz cut + hNEventsGen->GetXaxis()->SetBinLabel(3, "BC TF/ITS ROF border"); // Passing BC TF/ITS ROF border cut + hNEventsGen->GetXaxis()->SetBinLabel(4, "RCTFlagsChecker"); // Passing RCTFlagsChecker cut + hNEventsGen->GetXaxis()->SetBinLabel(5, "INEL"); // Generated events passing INEL cut + hNEventsGen->GetXaxis()->SetBinLabel(6, "INEL>0"); // Passing INELgt0 cut --> EL numerator for INEL>0 + hNEventsGen->GetXaxis()->SetBinLabel(7, "ALORE INEL"); // Passing ALORE INEL cut + hNEventsGen->GetXaxis()->SetBinLabel(8, "ALORE INEL>0"); // Passing ALORE INEL>0 cut + hNEventsGen->SetMinimum(0.1); + + registry.add("QAMC/Gen/hNEventsCent", "Generated events", HistType::kTH2F, {centQAAxis, {8, 0, 8}}); + auto hNEventsGenCent = registry.get(HIST("QAMC/Gen/hNEventsCent")); + hNEventsGenCent->GetYaxis()->SetBinLabel(1, "all"); + hNEventsGenCent->GetYaxis()->SetBinLabel(2, "V_z cut"); + hNEventsGenCent->GetYaxis()->SetBinLabel(3, "BC TF/ITS ROF border"); + hNEventsGenCent->GetYaxis()->SetBinLabel(4, "RCTFlagsChecker"); + hNEventsGenCent->GetYaxis()->SetBinLabel(5, "INEL"); + hNEventsGenCent->GetYaxis()->SetBinLabel(6, "INEL>0"); + hNEventsGenCent->GetYaxis()->SetBinLabel(7, "ALORE INEL"); + hNEventsGenCent->GetYaxis()->SetBinLabel(8, "ALORE INEL>0"); + + registry.add("QAMC/Rec/hNEvents", "Number of MC Rec Events", kTH1F, {{3, 0.0f, 3.0f}}); + auto hNEventsRec = registry.get(HIST("QAMC/Rec/hNEvents")); + hNEventsRec->GetXaxis()->SetBinLabel(1, "all"); // All reconstructed events from generated events passing Vz cut + hNEventsRec->GetXaxis()->SetBinLabel(2, "INEL"); // Reconstructed events passing event selection + hNEventsRec->GetXaxis()->SetBinLabel(3, "INEL>0"); // Reconstructed events passing INEL>0 cut + + registry.add("QAMC/Rec/hNEventsCent", "Reconstructed events", HistType::kTH2F, {centQAAxis, {3, 0, 3}}); + auto hNEventsRecCent = registry.get(HIST("QAMC/Rec/hNEventsCent")); + hNEventsRecCent->GetYaxis()->SetBinLabel(1, "all"); + hNEventsRecCent->GetYaxis()->SetBinLabel(2, "INEL"); + hNEventsRecCent->GetYaxis()->SetBinLabel(3, "INEL>0"); + + registry.add("QAMC/Gen/hNPhiPt", "Generated #Phi", kTH2D, {ptAxis, {5, 0, 5}}); + auto hNPhiGenPt = registry.get(HIST("QAMC/Gen/hNPhiPt")); + hNPhiGenPt->GetYaxis()->SetBinLabel(1, "all INEL"); // All generated #Phi in Gen Events passing Vz cut INEL + hNPhiGenPt->GetYaxis()->SetBinLabel(2, "all INEL>0"); // All generated #Phi in Gen Events passing Vz cut INEL>0 + hNPhiGenPt->GetYaxis()->SetBinLabel(3, "ALORE INEL"); // All generated #Phi in Gen Events with ALORE passing Vz cut INEL + hNPhiGenPt->GetYaxis()->SetBinLabel(4, "ALORE INEL>0"); // All generated #Phi in Gen Events with ALORE passing Vz cut INEL>0 + hNPhiGenPt->GetYaxis()->SetBinLabel(5, "Rec True"); // All reconstructed #Phi in Rec Events passing Vz cut INEL + + registry.add("QAMC/Gen/hNPhiPtCent", "Generated #Phi", kTH3D, {ptAxis, centQAAxis, {5, 0, 5}}); + + // Resolution + if (static_cast(produce.produceQA)) { + registry.add("QAMC/Resolution/h2ResolutionVz", "Resolution of collision V_{z}", kTH2F, {vzaxis, axisResolutionVz}); + auto hResVz = registry.get(HIST("QAMC/Resolution/h2ResolutionVz")); hResVz->GetXaxis()->SetTitle("V_{z}^{rec} (cm)"); hResVz->GetYaxis()->SetTitle("#DeltaV_{z} = V_{z}^{rec} - V_{z}^{gen} (cm)"); - registry.add("Factors/h2ResolutionPt", "Resolution of charged particles p_{T}", kTH2F, {ptaxis, axisResolutionPt}); - auto hResPt = registry.get(HIST("Factors/h2ResolutionPt")); + registry.add("QAMC/Resolution/h2ResolutionPt", "Resolution of charged particles p_{T}", kTH2F, {ptQAAxis, axisResolutionPt}); + auto hResPt = registry.get(HIST("QAMC/Resolution/h2ResolutionPt")); hResPt->GetXaxis()->SetTitle("p_{T}^{rec} (GeV/c)"); hResPt->GetYaxis()->SetTitle("#Deltap_{T} = p_{T}^{rec} - p_{T}^{gen} (GeV/c)"); - registry.add("Factors/h2ResolutionPtPhi", "p_{T} resolution vs p_{T}^{rec}", kTH2F, {ptaxis, axisResolutionPtPhi}); - auto hResPtPhi = registry.get(HIST("Factors/h2ResolutionPtPhi")); + + registry.add("QAMC/Resolution/h2ResolutionPtPhi", "p_{T} resolution vs p_{T}^{rec}", kTH2F, {ptQAAxis, axisResolutionPtPhi}); + auto hResPtPhi = registry.get(HIST("QAMC/Resolution/h2ResolutionPtPhi")); hResPtPhi->GetXaxis()->SetTitle("p_{T}^{rec} (GeV/c)"); hResPtPhi->GetYaxis()->SetTitle("#Deltap_{T} = p_{T}^{rec} - p_{T}^{gen} (GeV/c)"); - registry.add("Factors/h2MassResolution", "Mass resolution vs p_{T}^{rec}", kTH2F, {ptaxis, axisResolutionMass}); - auto hResMass = registry.get(HIST("Factors/h2MassResolution")); + registry.add("QAMC/Resolution/h2MassResolution", "Mass resolution vs p_{T}^{rec}", kTH2F, {ptQAAxis, axisResolutionMass}); + auto hResMass = registry.get(HIST("QAMC/Resolution/h2MassResolution")); hResMass->GetXaxis()->SetTitle("p_{T}^{rec} (GeV/c)"); hResMass->GetYaxis()->SetTitle("#Deltam = m^{gen}_{KK} - m^{rec}_{KK} (GeV/c^{2})"); - - registry.add("Factors/h2MassShift", "Mass shift vs p_{T}^{rec}", kTH2F, {ptaxis, massShiftAxis}); - auto hResMassGen = registry.get(HIST("Factors/h2MassShift")); - hResMassGen->GetXaxis()->SetTitle("p_{T}^{rec} (GeV/c)"); - hResMassGen->GetYaxis()->SetTitle("#Deltam = m^{gen}_{#phi} - m^{gen}_{KK} (GeV/c^{2})"); - - registry.add("Factors/h2MassShiftRel", "Relative mass shift vs p_{T}^{rec}", kTH2F, {ptaxis, massShiftRelAxis}); - auto hMassCorr = registry.get(HIST("Factors/h2MassShiftRel")); - hMassCorr->GetXaxis()->SetTitle("p_{T}^{rec} (GeV/c)"); - hMassCorr->GetYaxis()->SetTitle("m^{gen}_{#phi} - m^{gen}_{KK}/m^{gen}_{#phi}"); } } - // Factors - registry.add("Factors/hCentralityVsMultMC", "Event centrality vs MC multiplicity", kTH2F, {{101, 0.0f, 101.0f}, axisNch}); - registry.add("Factors/hCentralityVsMult", "Event centrality vs multiplicity", kTH2F, {{101, 0.0f, 101.0f}, axisNch}); - registry.add("Factors/hEventCentrality", "Event centrality", kTH1F, {{101, 0, 101}}); - registry.add("Factors/hNrecInGen", "Number of collisions in MC", kTH1F, {{3, -0.5, 2.5}}); - registry.add("Factors/hGenEvents", "Generated events", HistType::kTH2F, {{axisNch}, {4, 0, 4}}); - auto hGenEvents = registry.get(HIST("Factors/hGenEvents")); - hGenEvents->GetYaxis()->SetBinLabel(1, "All generated events"); - hGenEvents->GetYaxis()->SetBinLabel(2, "Generated events passing V_{z} cut"); - hGenEvents->GetYaxis()->SetBinLabel(3, "Generated events passing INEL>0"); - hGenEvents->GetYaxis()->SetBinLabel(4, "Generated events with at least one reconstructed event"); - registry.add("Factors/h2dGenPhi", "Centrality vs p_{T}", kTH2D, {{101, 0.0f, 101.0f}, ptaxis}); - registry.add("Factors/h3dGenPhiVsMultMCVsCentrality", "MC multiplicity vs centrality vs p_{T}", kTH3D, {axisNch, {101, 0.0f, 101.0f}, ptaxis}); } + template + bool selectedEvent(const T& collision) + { + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 0.5); // all events + } + + if (std::abs(collision.posZ()) > static_cast(eventCuts.vzCut)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 1.5); // events passing V_{z} cut + } + + if (static_cast(eventCuts.isTriggerTVX) && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 2.5); // events passing trigger TVX cut + } + + if (static_cast(eventCuts.noTimeFrameBorder) && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 3.5); // events passing no time frame border cut + } + + if (static_cast(eventCuts.noITSROFrameBorder) && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 4.5); // events passing no ITS RO frame border cut + } + + if (static_cast(eventCuts.sel8) && !collision.sel8()) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 5.5); // events passing sel8 cut (contains all the previous cuts) + } + if (static_cast(eventCuts.isVertexITSTPC) && !collision.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 6.5); // events passing IsVertexITSTPC cut + } + + if (static_cast(eventCuts.noSameBunchPileup) && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 7.5); // events passing no same bunch pileup cut + } + + if (static_cast(eventCuts.isGoodZvtxFT0vsPV) && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 8.5); // events passing IsGoodZvtxFT0vsPV cut + } + + if (dataQA) { + registry.fill(HIST("QA/Event/hSelection"), 9.5); // INEL + } + + return true; + } template float tpcNsigma(const T& track) { float tpcNsigma = 0.0f; int particleType = (track.sign() > 0) ? static_cast(daughterPos) : static_cast(daughterNeg); - if (particleType == pion) + if (particleType == pion) { tpcNsigma = track.tpcNSigmaPi(); - else if (particleType == kaon) + } else if (particleType == kaon) { tpcNsigma = track.tpcNSigmaKa(); - else if (particleType == proton) + } else if (particleType == proton) { tpcNsigma = track.tpcNSigmaPr(); + } return tpcNsigma; } template @@ -499,78 +625,100 @@ struct PhianalysisTHnSparse { { float tofNsigma = 0.0f; int particleType = (track.sign() > 0) ? static_cast(daughterPos) : static_cast(daughterNeg); - - if (particleType == pion) - tofNsigma = track.tofNSigmaPi(); - else if (particleType == kaon) - tofNsigma = track.tofNSigmaKa(); - else if (particleType == proton) - tofNsigma = track.tofNSigmaPr(); + float tofNSigmaShift = (track.sign() > 0) ? static_cast(trackCuts.tofNSigmaPosShift) : static_cast(trackCuts.tofNSigmaNegShift); + + if (particleType == pion) { + tofNsigma = track.tofNSigmaPi() + tofNSigmaShift; + } else if (particleType == kaon) { + tofNsigma = track.tofNSigmaKa() + tofNSigmaShift; + } else if (particleType == proton) { + tofNsigma = track.tofNSigmaPr() + tofNSigmaShift; + } return tofNsigma; } template bool selectedTrack(const T& track, bool isPositive) { - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 0.5); // all tracks + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 0.5); + } - // Apply pT cut - if (track.pt() < static_cast(cut.pt)) + if (track.pt() < static_cast(trackCuts.pt)) { return false; - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 1.5); + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 1.5); + } - // Apply eta cut - if (std::abs(track.eta()) >= static_cast(cut.etatrack)) + if (std::abs(track.eta()) >= static_cast(trackCuts.etatrack)) { return false; - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 2.5); + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 2.5); + } + + float dcaXY = static_cast(trackCuts.dcaXY); + if (static_cast(trackCuts.ptDependentDCA)) { + double dcaNSigma = static_cast(trackCuts.dcaNSigma); + dcaXY = (dcaNSigma / 7) * (0.0105 + 0.0350 / std::pow(track.pt(), 1.1)); + } - // Apply DCA cuts - if (std::abs(track.dcaXY()) >= static_cast(cut.dcaXY) || - std::abs(track.dcaZ()) >= static_cast(cut.dcaZ)) + if (std::abs(track.dcaXY()) >= dcaXY || + std::abs(track.dcaZ()) >= static_cast(trackCuts.dcaZ)) { return false; - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 3.5); + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 3.5); + } // PID selection: TPC-only for pt < threshold value, TPC+TOF for pt >= threshold value and have TOF, else TPC-only - float nSigmaCut = isPositive ? tpcnSigmaPos : tpcnSigmaNeg; - if (track.pt() < ptTOFThreshold || !track.hasTOF() || tpcPidOnly) { - if (std::abs(tpcNsigma(track)) >= nSigmaCut) + float nSigmaCut = isPositive ? static_cast(trackCuts.tpcnSigmaPos) : static_cast(trackCuts.tpcnSigmaNeg); + if (track.pt() < static_cast(trackCuts.ptTOFThreshold) || !track.hasTOF() || static_cast(trackCuts.tpcPidOnly)) { + if (std::abs(tpcNsigma(track)) >= nSigmaCut) { return false; + } } else { - if (std::sqrt(tpcNsigma(track) * tpcNsigma(track) + tofNsigma(track) * tofNsigma(track)) >= combinedNSigma) + if (std::sqrt(tpcNsigma(track) * tpcNsigma(track) + tofNsigma(track) * tofNsigma(track)) >= static_cast(trackCuts.combinedNSigma)) { return false; + } + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 4.5); + } + + if (track.tpcNClsFound() < static_cast(trackCuts.tpcNClsFound)) { + return false; + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 5.5); } - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 4.5); - // Apply tpcNClsFound cut - if (track.tpcNClsFound() < tpcNClsFound) + if (track.tpcNClsCrossedRows() < static_cast(trackCuts.tpcNClsCrossedRows)) { return false; - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 5.5); + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 6.5); + } - if (globalTrack) { - // Apply Global track cuts - if (!track.isGlobalTrack()) + if (static_cast(trackCuts.globalTrack)) { + if (!track.isGlobalTrack()) { return false; - } else { - // Apply Primary track cuts - if (!track.isPrimaryTrack()) + } + } else if (static_cast(trackCuts.primaryTrack)) { + if (!track.isPrimaryTrack()) { return false; + } + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 7.5); } - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 6.5); - // Apply PV Contributor cuts - if (!track.isPVContributor()) + if (static_cast(trackCuts.pvContributor) && !track.isPVContributor()) { return false; - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 7.5); - - if (produceQA && dataQA) - registry.fill(HIST("QATrack/hSelection"), 8.5); + } + if (dataQA) { + registry.fill(HIST("QA/Track/hSelection"), 8.5); + } return true; } @@ -581,11 +729,9 @@ struct PhianalysisTHnSparse { d2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massNeg); return d1 + d2; } - bool seletectedMother(const ROOT::Math::PxPyPzMVector& mother) + bool selectedMother(const ROOT::Math::PxPyPzMVector& motherCandidate) { - if (std::abs(mother.Rapidity()) > static_cast(cut.rapidity)) - return false; - return true; + return std::abs(motherCandidate.Rapidity()) <= static_cast(trackCuts.rapidity); } template float getMultiplicity(const T& collision) @@ -617,170 +763,325 @@ struct PhianalysisTHnSparse { return pointPair; } - void processData(EventCandidate const& collision, TrackCandidates const& /*tracks*/) + template + bool selectedGenEventBcBorderCuts(T const& bc) { - auto posDaughters = positive->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - auto negDaughters = negative->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - - int nch = 0; + if (!static_cast(mcCuts.bcBorderCutsGen)) { + return true; + } + if (static_cast(eventCuts.noTimeFrameBorder) && !bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (static_cast(eventCuts.noITSROFrameBorder) && !bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + return true; + } - if (produceQA) - registry.fill(HIST("QAEvent/hSelection"), 0.5); + template + bool selectedGenEventRCT(TBC const& bc) + { + if (!static_cast(mcCuts.rctCheckGen)) { + return true; + } + return !static_cast(mcCuts.rctCheckGen) || rctChecker(bc); + } - if (!collision.sel8()) + void processQA(EventCandidate const& collision, TrackCandidates const& tracks) + { + if (!static_cast(produce.produceQA)) { return; + } - if (produceQA) - registry.fill(HIST("QAEvent/hSelection"), 1.5); + dataQA = true; + bool selected = selectedEvent(collision); + dataQA = false; - if (std::abs(collision.posZ()) > vzCut) + if (!selected) { + return; + } + if (static_cast(eventCuts.inelGt0) && !collision.isInelGt0()) { return; + } + registry.fill(HIST("QA/Event/hSelection"), 10.5); // events passing INEL>0 cut - if (produceQA) - registry.fill(HIST("QAEvent/hSelection"), 2.5); + double centrality = getCentrality(collision); - if (inelGrater0 && !collision.isInelGt0()) - return; + int nch = 0; + for (const auto& track : tracks) { - registry.fill(HIST("Factors/hEventCentrality"), collision.centFT0M()); + registry.fill(HIST("QA/Track/hEta"), track.pt(), centrality, track.eta()); + registry.fill(HIST("QA/Track/hPt"), track.pt(), centrality); + registry.fill(HIST("QA/Track/hDCAxy"), track.pt(), centrality, track.dcaXY()); + registry.fill(HIST("QA/Track/hDCAz"), track.pt(), centrality, track.dcaZ()); + registry.fill(HIST("QA/Track/hTPCNClsFound"), track.pt(), centrality, track.tpcNClsFound()); + registry.fill(HIST("QA/Track/hTPCNClsCrossedRows"), track.pt(), centrality, track.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Track/hRapidity"), track.pt(), centrality, track.sign() > 0 ? track.rapidity(massPos) : track.rapidity(massNeg)); - if (produceQA) { - registry.fill(HIST("QAEvent/hSelection"), 3.5); - registry.fill(HIST("QAEvent/hVtxZ"), collision.posZ()); - registry.fill(HIST("QAEvent/hMult"), getMultiplicity(collision)); - registry.fill(HIST("QAEvent/hCent"), getCentrality(collision)); + registry.fill(HIST("QA/PID/hTPCNSigma"), track.pt(), centrality, tpcNsigma(track)); + registry.fill(HIST("QA/PID/hTPCdEdxP"), track.p(), track.tpcSignal()); - if (produceStats) { - dataQA = true; - for (const auto& track : posDaughters) { - if (track.isPrimaryTrack() && std::abs(track.eta()) >= static_cast(cut.etatrack)) - nch++; - selectedTrack(track, true); - } - for (const auto& track : negDaughters) { - if (track.isPrimaryTrack() && std::abs(track.eta()) >= static_cast(cut.etatrack)) - nch++; - selectedTrack(track, false); - } - dataQA = false; + if (track.hasTOF()) { + registry.fill(HIST("QA/PID/hTOFNSigma"), track.pt(), centrality, tofNsigma(track)); + registry.fill(HIST("QA/PID/hTOFBetaP"), track.p(), track.beta()); } - registry.fill(HIST("Factors/hCentralityVsMult"), getCentrality(collision), nch); + + if (track.isPrimaryTrack() && std::abs(track.eta()) < static_cast(trackCuts.etatrack)) { + nch++; + } + + dataQA = true; + bool selectedTrackCandidate = selectedTrack(track, track.sign() > 0); + dataQA = false; + + if (!selectedTrackCandidate) { + continue; + } + + registry.fill(HIST("QA/Kaon/hEta"), track.pt(), centrality, track.eta()); + registry.fill(HIST("QA/Kaon/hPt"), track.pt(), centrality); + registry.fill(HIST("QA/Kaon/hDCAxy"), track.pt(), centrality, track.dcaXY()); + registry.fill(HIST("QA/Kaon/hDCAz"), track.pt(), centrality, track.dcaZ()); + registry.fill(HIST("QA/Kaon/hTPCNClsFound"), track.pt(), centrality, track.tpcNClsFound()); + registry.fill(HIST("QA/Kaon/hTPCNClsCrossedRows"), track.pt(), centrality, track.tpcNClsCrossedRows()); + registry.fill(HIST("QA/Kaon/hRapidity"), track.pt(), centrality, track.sign() > 0 ? track.rapidity(massPos) : track.rapidity(massNeg)); + + registry.fill(HIST("QA/PID/hTPCNSigmaK"), track.pt(), centrality, tpcNsigma(track)); + registry.fill(HIST("QA/PID/hTPCdEdxPK"), track.p(), track.tpcSignal()); + + if (track.hasTOF()) { + registry.fill(HIST("QA/PID/hTOFNSigmaK"), track.pt(), centrality, tofNsigma(track)); + registry.fill(HIST("QA/PID/hTOFBetaPK"), track.p(), track.beta()); + registry.fill(HIST("QA/PID/hTPCTOFnSigma"), track.pt(), tpcNsigma(track), tofNsigma(track)); + } + } + registry.fill(HIST("QA/Event/hCentNch"), getCentrality(collision), nch); + } + PROCESS_SWITCH(PhianalysisTHnSparse, processQA, "Process Event for Data", false); + + void processData(EventCandidate const& collision, TrackCandidates const& /*tracks*/) + { + auto posDaughters = positive->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto negDaughters = negative->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + + if (!selectedEvent(collision)) { + return; + } + if (static_cast(eventCuts.inelGt0) && !collision.isInelGt0()) { + return; } - if (static_cast(verbose.verboselevel) > 0 && static_cast(verbose.refresh) > 0 && collision.globalIndex() % static_cast(verbose.refresh) == static_cast(verbose.refreshIndex)) - LOGF(info, "%d pos=%lld neg=%lld, Z vertex position: %f [cm]", collision.globalIndex(), posDaughters.size(), negDaughters.size(), collision.posZ()); + const auto multiplicity = getMultiplicity(collision); + const auto centrality = getCentrality(collision); + const auto vz = collision.posZ(); + + registry.fill(HIST("QA/Event/hVtxZ"), vz); + registry.fill(HIST("QA/Event/hMult"), multiplicity); + registry.fill(HIST("QA/Event/hCent"), centrality); for (const auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(posDaughters, negDaughters))) { - if (!selectedTrack(track1, true)) // track1 is positive + if (!selectedTrack(track1, true)) { continue; - if (!selectedTrack(track2, false)) // track2 is negative + } + if (!selectedTrack(track2, false)) { continue; + } mother = calculateMother(track1, track2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; + } - if (produceQA) { - registry.fill(HIST("QAPID/h2TPCnSigma"), tpcNsigma(track1), tpcNsigma(track2)); - registry.fill(HIST("QAPID/h2TPCnSigmaPt"), track1.pt(), tpcNsigma(track1)); - registry.fill(HIST("QAPID/h2TPCnSigmaPt"), track2.pt(), tpcNsigma(track2)); - - registry.fill(HIST("QAPID/h2TOFnSigma"), tofNsigma(track1), tofNsigma(track2)); - registry.fill(HIST("QAPID/h2TOFnSigmaPt"), track1.pt(), tofNsigma(track1)); - registry.fill(HIST("QAPID/h2TOFnSigmaPt"), track2.pt(), tofNsigma(track2)); - - registry.fill(HIST("QAPID/hTPCnSigma"), tpcNsigma(track1)); - registry.fill(HIST("QAPID/hTPCnSigma"), tpcNsigma(track2)); - if (track1.hasTOF()) - registry.fill(HIST("QAPID/hTOFnSigma"), tofNsigma(track1)); - if (track2.hasTOF()) - registry.fill(HIST("QAPID/hTOFnSigma"), tofNsigma(track2)); - - registry.fill(HIST("QATrack/hEta"), track1.eta()); - registry.fill(HIST("QATrack/hEta"), track2.eta()); - registry.fill(HIST("QATrack/hPt"), track1.pt()); - registry.fill(HIST("QATrack/hPt"), track2.pt()); - registry.fill(HIST("QATrack/hDCAxy"), track1.dcaXY()); - registry.fill(HIST("QATrack/hDCAxy"), track2.dcaXY()); - registry.fill(HIST("QATrack/hDCAz"), track1.dcaZ()); - registry.fill(HIST("QATrack/hDCAz"), track2.dcaZ()); - registry.fill(HIST("QATrack/hTPCNClsFound"), track1.tpcNClsFound()); - registry.fill(HIST("QATrack/hTPCNClsFound"), track2.tpcNClsFound()); - registry.fill(HIST("QATrack/hRapidity"), track1.rapidity(massPos)); - registry.fill(HIST("QATrack/hRapidity"), track2.rapidity(massNeg)); - - registry.fill(HIST("QAPhi/hRapidity"), mother.Rapidity()); - registry.fill(HIST("QAPhi/hEta"), mother.Eta()); - registry.fill(HIST("QAPhi/hdPhi"), track1.phi() - track2.phi()); - registry.fill(HIST("QAPhi/h2dPhiPt"), mother.Pt(), track1.phi() - track2.phi()); - registry.fill(HIST("QAPhi/hTheta"), mother.Theta()); - registry.fill(HIST("QAPhi/h2dThetaPt"), mother.Pt(), d1.Theta() - d2.Theta()); + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QA/Phi/hRapidity"), mother.Pt(), getCentrality(collision), mother.Rapidity()); + registry.fill(HIST("QA/Phi/hEta"), mother.Pt(), getCentrality(collision), mother.Eta()); + registry.fill(HIST("QA/Phi/hdPhi"), mother.Pt(), getCentrality(collision), track1.phi() - track2.phi()); + registry.fill(HIST("QA/Phi/hdPhideta"), track1.eta() - track2.eta(), track1.phi() - track2.phi()); + registry.fill(HIST("QA/Phi/hdTheta"), mother.Pt(), getCentrality(collision), d1.Theta() - d2.Theta()); } pointPair = fillPointPair(mother.M(), mother.Pt(), - getMultiplicity(collision), - getCentrality(collision), + multiplicity, + centrality, tpcNsigma(track1), tpcNsigma(track2), mother.Eta(), mother.Rapidity(), - collision.posZ(), + vz, 0, 0, 0); rsnOutput->fillUnlikepm(pointPair); - if (produceRotational) { - for (int i = 1; i <= static_cast(numberofRotations); i++) { + if (static_cast(produce.produceRotational)) { + + // Rotational background with rotation of track2 around track1 + for (int i = 1; i <= static_cast(nRotations); i++) { float starting = static_cast(startingAngle) * TMath::DegToRad(); - float angle = starting + i * ((o2::constants::math::TwoPI - 2 * starting) / (static_cast(numberofRotations) + 1)); + float angle = starting + i * ((o2::constants::math::TwoPI - 2 * starting) / (static_cast(nRotations) + 1)); float px2new = track2.px() * std::cos(angle) - track2.py() * std::sin(angle); float py2new = track2.px() * std::sin(angle) + track2.py() * std::cos(angle); - d2 = ROOT::Math::PxPyPzMVector(px2new, py2new, track2.pz(), massNeg); - mother = d1 + d2; - - if (produceQA) { - registry.fill(HIST("QARotational/hRapidity"), mother.Rapidity()); - registry.fill(HIST("QARotational/hEta"), mother.Eta()); - registry.fill(HIST("QARotational/hdPhi"), d1.Phi() - d2.Phi()); - registry.fill(HIST("QARotational/h2dPhiPt"), mother.Pt(), d1.Phi() - d2.Phi()); - registry.fill(HIST("QARotational/hTheta"), mother.Theta()); - registry.fill(HIST("QARotational/h2dThetaPt"), mother.Pt(), d1.Theta() - d2.Theta()); + ROOT::Math::PxPyPzMVector d2rot(px2new, py2new, track2.pz(), massNeg); + auto motherRotZ = d1 + d2rot; + + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QA/RotationZ/hRapidity"), motherRotZ.Pt(), getCentrality(collision), motherRotZ.Rapidity()); + registry.fill(HIST("QA/RotationZ/hEta"), motherRotZ.Pt(), getCentrality(collision), motherRotZ.Eta()); + registry.fill(HIST("QA/RotationZ/hdPhi"), motherRotZ.Pt(), getCentrality(collision), d1.Phi() - d2rot.Phi()); + registry.fill(HIST("QA/RotationZ/hdPhideta"), d1.Eta() - d2rot.Eta(), d1.Phi() - d2rot.Phi()); + registry.fill(HIST("QA/RotationZ/hdTheta"), motherRotZ.Pt(), getCentrality(collision), d1.Theta() - d2rot.Theta()); } - pointPair = fillPointPair(mother.M(), - mother.Pt(), - getMultiplicity(collision), - getCentrality(collision), + + pointPair = fillPointPair(motherRotZ.M(), + motherRotZ.Pt(), + multiplicity, + centrality, tpcNsigma(track1), tpcNsigma(track2), - mother.Eta(), - mother.Rapidity(), - collision.posZ(), + motherRotZ.Eta(), + motherRotZ.Rapidity(), + vz, 0, 0, 0); - rsnOutput->fillRotationpm(pointPair); + rsnOutput->fillRotationZ(pointPair); + } + + // Rotational background with rotation of 90 degrees around mother momentum axis + ROOT::Math::AxisAngle rotationAxis(mother.Vect(), constants::math::PIHalf); + ROOT::Math::Rotation3D rotationMatrix(rotationAxis); + + const auto rotD1 = rotationMatrix * d1; + const auto rotD2 = rotationMatrix * d2; + + if (negDaughters.size() > 1) { + for (const auto& track3 : negDaughters) { + if (track3.globalIndex() == track1.globalIndex() || track3.globalIndex() == track2.globalIndex()) { + continue; + } + if (!selectedTrack(track3, false)) { + continue; + } + ROOT::Math::PxPyPzMVector d3(track3.px(), track3.py(), track3.pz(), massNeg); + auto motherRot = rotD1 + d3; + + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QA/Rotation/hRapidity"), motherRot.Pt(), getCentrality(collision), motherRot.Rapidity()); + registry.fill(HIST("QA/Rotation/hEta"), motherRot.Pt(), getCentrality(collision), motherRot.Eta()); + registry.fill(HIST("QA/Rotation/hdPhi"), motherRot.Pt(), getCentrality(collision), rotD1.Phi() - d3.Phi()); + registry.fill(HIST("QA/Rotation/hdPhideta"), rotD1.Eta() - d3.Eta(), rotD1.Phi() - d3.Phi()); + registry.fill(HIST("QA/Rotation/hdTheta"), motherRot.Pt(), getCentrality(collision), rotD1.Theta() - d3.Theta()); + } + + pointPair = fillPointPair(motherRot.M(), + motherRot.Pt(), + multiplicity, + centrality, + tpcNsigma(track1), + tpcNsigma(track2), + motherRot.Eta(), + motherRot.Rapidity(), + vz, + 0, + 0, + 0); + + rsnOutput->fillRotation(pointPair); + + // Likesign rotation for negative daughters + motherRot = rotD2 + d3; + pointPair = fillPointPair(motherRot.M(), + motherRot.Pt(), + multiplicity, + centrality, + tpcNsigma(track1), + tpcNsigma(track2), + motherRot.Eta(), + motherRot.Rapidity(), + vz, + 0, + 0, + 0); + + rsnOutput->fillRotationLike(pointPair); + } + } + + if (posDaughters.size() > 1) { + for (const auto& track3 : posDaughters) { + + if (track3.globalIndex() == track1.globalIndex() || track3.globalIndex() == track2.globalIndex()) { + continue; + } + + if (!selectedTrack(track3, true)) { + continue; + } + + ROOT::Math::PxPyPzMVector d3(track3.px(), track3.py(), track3.pz(), massPos); + + auto motherRot = rotD2 + d3; + + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QA/Rotation/hRapidity"), motherRot.Pt(), getCentrality(collision), motherRot.Rapidity()); + registry.fill(HIST("QA/Rotation/hEta"), motherRot.Pt(), getCentrality(collision), motherRot.Eta()); + registry.fill(HIST("QA/Rotation/hdPhi"), motherRot.Pt(), getCentrality(collision), rotD2.Phi() - d3.Phi()); + registry.fill(HIST("QA/Rotation/hdPhideta"), rotD2.Eta() - d3.Eta(), rotD2.Phi() - d3.Phi()); + registry.fill(HIST("QA/Rotation/hdTheta"), motherRot.Pt(), getCentrality(collision), rotD2.Theta() - d3.Theta()); + } + + pointPair = fillPointPair(motherRot.M(), + motherRot.Pt(), + multiplicity, + centrality, + tpcNsigma(track1), + tpcNsigma(track2), + motherRot.Eta(), + motherRot.Rapidity(), + vz, + 0, + 0, + 0); + + rsnOutput->fillRotation(pointPair); + + // Likesign rotation for positive daughters + motherRot = rotD1 + d3; + pointPair = fillPointPair(motherRot.M(), + motherRot.Pt(), + multiplicity, + centrality, + tpcNsigma(track1), + tpcNsigma(track2), + motherRot.Eta(), + motherRot.Rapidity(), + vz, + 0, + 0, + 0); + + rsnOutput->fillRotationLike(pointPair); + } } } } - - if (produceLikesign) { + if (static_cast(produce.produceLikesign)) { for (const auto& [track1, track2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(posDaughters, posDaughters))) { - if (!selectedTrack(track1, true)) // both positive + if (!selectedTrack(track1, true)) { continue; - if (!selectedTrack(track2, true)) // both positive + } + if (!selectedTrack(track2, true)) { continue; + } mother = calculateMother(track1, track2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; - - if (static_cast(verbose.verboselevel) > 1) - LOGF(info, "Like-sign positive: d1=%ld , d2=%ld , mother=%f", track1.globalIndex(), track2.globalIndex(), mother.M()); + } pointPair = fillPointPair(mother.M(), mother.Pt(), @@ -799,17 +1100,17 @@ struct PhianalysisTHnSparse { } for (const auto& [track1, track2] : combinations(o2::soa::CombinationsStrictlyUpperIndexPolicy(negDaughters, negDaughters))) { - if (!selectedTrack(track1, false)) // both negative + if (!selectedTrack(track1, false)) { continue; - if (!selectedTrack(track2, false)) // both negative + } + if (!selectedTrack(track2, false)) { continue; + } mother = calculateMother(track1, track2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; - - if (static_cast(verbose.verboselevel) > 1) - LOGF(info, "Like-sign negative: d1=%ld , d2=%ld , mother=%f", track1.globalIndex(), track2.globalIndex(), mother.M()); + } pointPair = fillPointPair(mother.M(), mother.Pt(), @@ -828,111 +1129,100 @@ struct PhianalysisTHnSparse { } } } - PROCESS_SWITCH(PhianalysisTHnSparse, processData, "Process Event for Data", true); + PROCESS_SWITCH(PhianalysisTHnSparse, processData, "Process Event for Data", false); - void processTrue(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) + void processTrue(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, soa::Join const& /*mcCollisions*/) { - if (!static_cast(produce.produceTrue)) + if (!static_cast(produce.produceMC)) { return; + } registry.fill(HIST("QAMC/Rec/hSelection"), 0.5); + registry.fill(HIST("QAMC/Factors/hRecEvents"), getCentrality(collision), 0.5); - if (!collision.sel8()) + if (!selectedEvent(collision)) { return; + } + bool isINELgt0 = false; + if (collision.isInelGt0()) { + isINELgt0 = true; + } - if (produceQA) - registry.fill(HIST("QAMC/Rec/hSelection"), 1.5); + registry.fill(HIST("QAMC/Rec/hSelection"), 1.5); + registry.fill(HIST("QAMC/Factors/hRecEvents"), getCentrality(collision), 1.5); + if (isINELgt0) { + registry.fill(HIST("QAMC/Rec/hSelection"), 2.5); + registry.fill(HIST("QAMC/Factors/hRecEvents"), getCentrality(collision), 2.5); + } - auto posDaughtersMC = positiveMC->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); - auto negDaughtersMC = negativeMC->sliceByCached(aod::track::collisionId, collision.globalIndex(), cache); + auto posDaughtersMC = positiveMC->sliceByCached(aod::track::collisionId, collision.globalIndex(), cacheMC); + auto negDaughtersMC = negativeMC->sliceByCached(aod::track::collisionId, collision.globalIndex(), cacheMC); if (!collision.has_mcCollision()) { - if (static_cast(verbose.verboselevel) > 0) - LOGF(warning, "No MC collision for this collision, skip..."); return; } - auto mcCollision = collision.mcCollision(); - registry.fill(HIST("Factors/h2ResolutionVz"), collision.posZ(), (collision.posZ() - mcCollision.posZ())); - - if (std::abs(mcCollision.posZ()) > vzCut) - return; - if (produceQA) - registry.fill(HIST("QAMC/Rec/hSelection"), 2.5); - - if (inelGrater0 && !collision.isInelGt0()) - return; + const auto& mcCollision = collision.mcCollision_as>(); - if (produceQA) - registry.fill(HIST("QAMC/Rec/hSelection"), 3.5); + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QAMC/Resolution/h2ResolutionVz"), collision.posZ(), (collision.posZ() - mcCollision.posZ())); - for (const auto& track : tracks) { - if (track.has_mcParticle()) { - auto mctrack = track.mcParticle(); - registry.fill(HIST("Factors/h2ResolutionPt"), track.pt(), (track.pt() - mctrack.pt())); + for (const auto& track : tracks) { + if (track.has_mcParticle()) { + auto mctrack = track.mcParticle(); + registry.fill(HIST("QAMC/Resolution/h2ResolutionPt"), track.pt(), (track.pt() - mctrack.pt())); + } } } for (const auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(posDaughtersMC, negDaughtersMC))) { - - if (!track1.has_mcParticle()) { - if (static_cast(verbose.verboselevel) > 0) - LOGF(warning, "No MC particle for track, skip..."); + if (!track1.has_mcParticle() || !track2.has_mcParticle()) { continue; } - if (!track2.has_mcParticle()) { - if (static_cast(verbose.verboselevel) > 0) - LOGF(warning, "No MC particle for track, skip..."); + if (!selectedTrack(track1, true) || !selectedTrack(track2, false)) { continue; } - if (!selectedTrack(track1, true)) // track1 is positive - continue; - if (!selectedTrack(track2, false)) // track2 is negative - continue; - const auto mctrack1 = track1.mcParticle(); const auto mctrack2 = track2.mcParticle(); int track1PDG = std::abs(mctrack1.pdgCode()); int track2PDG = std::abs(mctrack2.pdgCode()); - if (!(track1PDG == daughterPosPDG && track2PDG == daughterNegPDG)) { + if (track1PDG != daughterPosPDG || track2PDG != daughterNegPDG) { continue; } - n = 0; + + int n = 0; for (const auto& mothertrack1 : mctrack1.mothers_as()) { for (const auto& mothertrack2 : mctrack2.mothers_as()) { - if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) + if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { continue; + } - if (mothertrack1.globalIndex() != mothertrack2.globalIndex()) + if (mothertrack1.globalIndex() != mothertrack2.globalIndex()) { continue; + } - if (std::abs(mothertrack1.y()) > static_cast(cut.rapidity)) + if (std::abs(mothertrack1.y()) > static_cast(trackCuts.rapidity)) { continue; + } - if (std::abs(mothertrack1.pdgCode()) != motherPDG) + if (std::abs(mothertrack1.pdgCode()) != motherPDG) { continue; + } mother = calculateMother(track1, track2); motherGen = calculateMother(mctrack1, mctrack2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; + } if (n > 0) { - if (produceQA) - registry.fill(HIST("QAMC/Rec/hInvMassTrueFalse"), mother.M()); continue; } - if (static_cast(verbose.verboselevel) > 1) { - LOGF(info, "Collision: %ld True: %d, d1=%d (%ld), d2=%d (%ld), mother=%d (%ld)", collision.globalIndex(), n, mctrack1.pdgCode(), mctrack1.globalIndex(), mctrack2.pdgCode(), mctrack2.globalIndex(), mothertrack1.pdgCode(), mothertrack1.globalIndex()); - LOGF(info, "Track %d px: %f, py=%f, pz=%f, px: %f, py=%f, pz=%f", n, track1.px(), track1.py(), track1.pz(), track2.px(), track2.py(), track2.pz()); - LOGF(info, "mcTrack %d px: %f, py=%f, pz=%f, px: %f, py=%f, pz=%f", n, mctrack1.px(), mctrack1.py(), mctrack1.pz(), mctrack2.px(), mctrack2.py(), mctrack2.pz()); - } - pointPair = fillPointPair(mother.M(), mother.Pt(), getMultiplicity(collision), @@ -946,22 +1236,12 @@ struct PhianalysisTHnSparse { 0, 0); - if (produceQA) - registry.fill(HIST("QAMC/Rec/hSelection"), 4.5); - - auto phiP = mothertrack1.p(); - auto phiE = mothertrack1.e(); - auto massGen = std::sqrt(phiE * phiE - phiP * phiP); - - registry.fill(HIST("Factors/h2ResolutionPtPhi"), mother.Pt(), (mother.Pt() - mothertrack1.pt())); - registry.fill(HIST("Factors/h2MassResolution"), mother.Pt(), (motherGen.M() - mother.M())); - registry.fill(HIST("Factors/h2MassShift"), mother.Pt(), (massGen - motherGen.M())); - registry.fill(HIST("Factors/h2MassShiftRel"), mother.Pt(), (massGen - motherGen.M()) / massGen); - - if (static_cast(verbose.verboselevel) > 1) - LOGF(info, "mother.M()=%f, motherGen.M()=%f, massGen =%f", mother.M(), motherGen.M(), massGen); + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QAMC/Resolution/h2ResolutionPtPhi"), mother.Pt(), (mother.Pt() - mothertrack1.pt())); + registry.fill(HIST("QAMC/Resolution/h2MassResolution"), mother.Pt(), (motherGen.M() - mother.M())); + } - rsnOutput->fillUnliketrue(pointPair); + rsnOutput->fillUnlikeTrueRec(pointPair); pointPair = fillPointPair(motherGen.M(), motherGen.Pt(), @@ -976,7 +1256,11 @@ struct PhianalysisTHnSparse { 0, 0); - rsnOutput->fillUnlikegenOld(pointPair); + rsnOutput->fillUnlikeTrueGen(pointPair); + + registry.fill(HIST("QAMC/Factors/hRecPhi"), getCentrality(collision), motherGen.Pt()); + registry.fill(HIST("QAMC/Gen/hNPhiPt"), mothertrack1.pt(), 4.5); + registry.fill(HIST("QAMC/Gen/hNPhiPtCent"), mothertrack1.pt(), mcCollision.centFT0M(), 4.5); n++; } @@ -987,160 +1271,247 @@ struct PhianalysisTHnSparse { void processGen(McCollisionMults::iterator const& mcCollision, soa::SmallGroups const& collisions, LabeledTracks const& /*particles*/, aod::McParticles const& mcParticles) { - if (!static_cast(produce.produceTrue)) + if (!static_cast(produce.produceMC)) { return; + } - if (produceQA) - registry.fill(HIST("QAMC/Gen/hSelection"), 0.5); - - if (std::abs(mcCollision.posZ()) > vzCut) - return; + registry.fill(HIST("QAMC/Factors/hNrecInGen"), collisions.size()); + registry.fill(HIST("QAMC/Gen/hSelection"), 0.5); + registry.fill(HIST("QAMC/Factors/hGenEvents"), mcCollision.multMCNParticlesEta05(), 0.5); - if (produceQA) + if (collisions.size() > 0) { registry.fill(HIST("QAMC/Gen/hSelection"), 1.5); + registry.fill(HIST("QAMC/Factors/hGenEvents"), mcCollision.multMCNParticlesEta05(), 2.5); + } - if (inelGrater0 && !mcCollision.isInelGt0()) - return; + int nContributors = -1; + bool hasSelectedCollision = false; + float centrality = 100.5f; + float multiplicity = 0.f; + bool isInelGt0 = false; - if (produceQA) - registry.fill(HIST("QAMC/Gen/hSelection"), 2.5); + for (const auto& collision : collisions) { + registry.fill(HIST("QAMC/Factors/hGenEvents"), mcCollision.multMCNParticlesEta05(), 1.5); + if (!selectedEvent(collision)) { + continue; + } - if (collisions.size() == 0) - return; + if (collision.isInelGt0()) { + isInelGt0 = true; + } - for (const auto& collision : collisions) { - auto centralityGen = getCentrality(collision); - auto multiplicityGen = getMultiplicity(collision); + if (collision.numContrib() > nContributors) { + nContributors = collision.numContrib(); + centrality = getCentrality(collision); + multiplicity = getMultiplicity(collision); + hasSelectedCollision = true; + } + } + + registry.fill(HIST("QAMC/Factors/hGenEventsCentNch"), centrality, mcCollision.multMCNParticlesEta05()); + + // All generated Phi mesons + for (const auto& particle : mcParticles) { + + if (std::abs(particle.y()) > static_cast(trackCuts.rapidity)) { + continue; + } - for (const auto& particle : mcParticles) { + if (particle.pdgCode() == motherPDG) { - if (std::abs(particle.y()) > static_cast(cut.rapidity)) + auto daughters = particle.daughters_as(); + if (daughters.size() != dauSize) { continue; + } - if (particle.pdgCode() == motherPDG) { + auto daup = false; + auto daun = false; - auto daughters = particle.daughters_as(); - if (daughters.size() != dauSize) - continue; + for (const auto& dau : daughters) { + if (dau.pdgCode() == daughterPosPDG) { + daup = true; + d1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massPos); + } else if (dau.pdgCode() == -daughterNegPDG) { + daun = true; + d2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massNeg); + } + } + if (!daup || !daun) { + continue; + } - auto daup = false; - auto daun = false; + mother = d1 + d2; - for (const auto& dau : daughters) { - if (dau.pdgCode() == daughterPosPDG) { - daup = true; - d1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massPos); - } else if (dau.pdgCode() == -daughterNegPDG) { - daun = true; - d2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massNeg); - } + registry.fill(HIST("QAMC/Factors/hGenPhi"), mcCollision.multMCNParticlesEta05(), centrality, particle.pt()); + } + } + + if (!hasSelectedCollision) { + return; + } + registry.fill(HIST("QAMC/Gen/hSelection"), 2.5); + registry.fill(HIST("QAMC/Factors/hGenEvents"), mcCollision.multMCNParticlesEta05(), 3.5); + registry.fill(HIST("QAMC/Factors/hGenALORESelEvents"), centrality, mcCollision.multMCNParticlesEta05()); + if (isInelGt0) { + registry.fill(HIST("QAMC/Gen/hSelection"), 3.5); + registry.fill(HIST("QAMC/Factors/hGenEvents"), mcCollision.multMCNParticlesEta05(), 4.5); + registry.fill(HIST("QAMC/Factors/hGenALORESelEventsInelGt0"), centrality, mcCollision.multMCNParticlesEta05()); + } + + // Generated Phi mesons in selected collisions + for (const auto& mcParticle : mcParticles) { + if (std::abs(mcParticle.y()) > static_cast(trackCuts.rapidity)) { + continue; + } + + if (mcParticle.pdgCode() == motherPDG) { + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != dauSize) { + continue; + } + + auto daup = false; + auto daun = false; + + for (const auto& dau : daughters) { + if (dau.pdgCode() == daughterPosPDG) { + daup = true; + d1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massPos); + } else if (dau.pdgCode() == -daughterNegPDG) { + daun = true; + d2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massNeg); } - if (!daup || !daun) - continue; + } + + if (!daup || !daun) { + continue; + } - mother = d1 + d2; + mother = d1 + d2; - pointPair = fillPointPair(mother.M(), - mother.Pt(), - multiplicityGen, - centralityGen, - 0, - 0, - mother.Eta(), - mother.Rapidity(), - mcCollision.posZ(), - 0, - 0, - 0); + pointPair = fillPointPair(mother.M(), + mother.Pt(), + multiplicity, + centrality, + 0, + 0, + mother.Eta(), + mother.Rapidity(), + mcCollision.posZ(), + 0, + 0, + 0); - rsnOutput->fillUnlikegen(pointPair); - if (produceQA) - registry.fill(HIST("QAMC/Gen/hSelection"), 3.5); + rsnOutput->fillUnlikeGen(pointPair); + registry.fill(HIST("QAMC/Factors/hGenALOREPhi"), mcCollision.multMCNParticlesEta05(), centrality, mother.Pt()); + if (isInelGt0) { + rsnOutput->fillUnlikeGenInelgt0(pointPair); + registry.fill(HIST("QAMC/Factors/hGenALOREPhiInelGt0"), mcCollision.multMCNParticlesEta05(), centrality, mother.Pt()); } } } } PROCESS_SWITCH(PhianalysisTHnSparse, processGen, "Process MC Generated.", false); - void processMixed(soa::Filtered const& collisions, TrackCandidates const& tracks) + void processMixed(EventCandidates const& collisions, TrackCandidates const& tracks) { - if (mixingType == rsn::MixingType::none) + if (mixingType == rsn::MixingType::none) { return; + } auto tracksTuple = std::make_tuple(tracks); - BinningTypeVzCe binningVzCe{{axisVertexMixing, axisCentralityMixing}, true}; - SameKindPair pairVzCe{binningVzCe, static_cast(numberofMixedEvents), -1, collisions, tracksTuple, &cache}; + BinningTypeVzCe binningVzCe{{axisVertexMixing, axisCentralityMixing}}; + SameKindPair pairVzCe{binningVzCe, static_cast(nMixedEvents), -1, collisions, tracksTuple, &cacheME}; - BinningTypeVzMu binningVzMu{{axisVertexMixing, axisMultiplicityMixing}, true}; - SameKindPair pairVzMu{binningVzMu, static_cast(numberofMixedEvents), -1, collisions, tracksTuple, &cache}; + BinningTypeVzMu binningVzMu{{axisVertexMixing, axisMultiplicityMixing}}; + SameKindPair pairVzMu{binningVzMu, static_cast(nMixedEvents), -1, collisions, tracksTuple, &cacheME}; if (mixingType == rsn::MixingType::ce) { for (const auto& [c1, tracks1, c2, tracks2] : pairVzCe) { - if (produceQA) - registry.fill(HIST("QAMixing/hSelection"), 0.5); - - auto posDaughtersc1 = positive->sliceByCached(aod::track::collisionId, c1.globalIndex(), cache); - auto posDaughtersc2 = positive->sliceByCached(aod::track::collisionId, c2.globalIndex(), cache); - auto negDaughtersc1 = negative->sliceByCached(aod::track::collisionId, c1.globalIndex(), cache); - auto negDaughtersc2 = negative->sliceByCached(aod::track::collisionId, c2.globalIndex(), cache); + if (!selectedEvent(c1) || !selectedEvent(c2)) { + continue; + } + if (static_cast(eventCuts.inelGt0) && (!c1.isInelGt0() || !c2.isInelGt0())) { + continue; + } - if (produceQA) { - registry.fill(HIST("QAMixing/h2mu1_mu2"), getMultiplicity(c1), getMultiplicity(c2)); - registry.fill(HIST("QAMixing/h2ce1_ce2"), getCentrality(c1), getCentrality(c2)); - registry.fill(HIST("QAMixing/h2vz1_vz2"), c1.posZ(), c2.posZ()); + const auto multiplicity1 = getMultiplicity(c1); + const auto multiplicity2 = getMultiplicity(c2); + const auto centrality1 = getCentrality(c1); + const auto centrality2 = getCentrality(c2); + const auto vz1 = c1.posZ(); + const auto vz2 = c2.posZ(); + + auto posDaughtersc1 = positive->sliceByCached(aod::track::collisionId, c1.globalIndex(), cacheME); + auto posDaughtersc2 = positive->sliceByCached(aod::track::collisionId, c2.globalIndex(), cacheME); + auto negDaughtersc1 = negative->sliceByCached(aod::track::collisionId, c1.globalIndex(), cacheME); + auto negDaughtersc2 = negative->sliceByCached(aod::track::collisionId, c2.globalIndex(), cacheME); + + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QA/Mixing/h2mu1_mu2"), multiplicity1, multiplicity2); + registry.fill(HIST("QA/Mixing/h2ce1_ce2"), centrality1, centrality2); + registry.fill(HIST("QA/Mixing/h2vz1_vz2"), vz1, vz2); } for (const auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(posDaughtersc1, negDaughtersc2))) { - - if (!selectedTrack(track1, true)) // track1 is positive + if (!selectedTrack(track1, true)) { continue; - if (!selectedTrack(track2, false)) // track2 is negative + } + if (!selectedTrack(track2, false)) { continue; + } mother = calculateMother(track1, track2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; + } + + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QA/Mixing/hdPhideta"), track1.eta() - track2.eta(), track1.phi() - track2.phi()); + } pointPair = fillPointPair(mother.M(), mother.Pt(), - getMultiplicity(c1), - getCentrality(c1), + multiplicity1, + centrality1, tpcNsigma(track1), tpcNsigma(track2), mother.Eta(), mother.Rapidity(), - c1.posZ(), - getMultiplicity(c2), - getCentrality(c2), - c2.posZ()); + vz1, + multiplicity2, + centrality2, + vz2); rsnOutput->fillMixingpm(pointPair); } for (const auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(posDaughtersc2, negDaughtersc1))) { - - if (!selectedTrack(track1, true)) // track1 is positive + if (!selectedTrack(track1, true)) { continue; - if (!selectedTrack(track2, false)) // track2 is negative + } + if (!selectedTrack(track2, false)) { continue; + } mother = calculateMother(track1, track2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; + } pointPair = fillPointPair(mother.M(), mother.Pt(), - getMultiplicity(c1), - getCentrality(c1), + multiplicity1, + centrality1, tpcNsigma(track1), tpcNsigma(track2), mother.Eta(), mother.Rapidity(), - c1.posZ(), - getMultiplicity(c2), - getCentrality(c2), - c2.posZ()); + vz1, + multiplicity2, + centrality2, + vz2); rsnOutput->fillMixingmp(pointPair); } @@ -1148,72 +1519,87 @@ struct PhianalysisTHnSparse { } if (mixingType == rsn::MixingType::mu) { for (const auto& [c1, tracks1, c2, tracks2] : pairVzMu) { - if (produceQA) - registry.fill(HIST("QAMixing/hSelection"), 0.5); + if (!selectedEvent(c1) || !selectedEvent(c2)) { + continue; + } + if (static_cast(eventCuts.inelGt0) && (!c1.isInelGt0() || !c2.isInelGt0())) { + continue; + } auto posDaughtersc1 = positive->sliceByCached(aod::track::collisionId, c1.globalIndex(), cache); auto posDaughtersc2 = positive->sliceByCached(aod::track::collisionId, c2.globalIndex(), cache); auto negDaughtersc1 = negative->sliceByCached(aod::track::collisionId, c1.globalIndex(), cache); auto negDaughtersc2 = negative->sliceByCached(aod::track::collisionId, c2.globalIndex(), cache); - - if (produceQA) { - registry.fill(HIST("QAMixing/h2mu1_mu2"), getMultiplicity(c1), getMultiplicity(c2)); - registry.fill(HIST("QAMixing/h2ce1_ce2"), getCentrality(c1), getCentrality(c2)); - registry.fill(HIST("QAMixing/h2vz1_vz2"), c1.posZ(), c2.posZ()); + const auto multiplicity1 = getMultiplicity(c1); + const auto multiplicity2 = getMultiplicity(c2); + const auto centrality1 = getCentrality(c1); + const auto centrality2 = getCentrality(c2); + const auto vz1 = c1.posZ(); + const auto vz2 = c2.posZ(); + + if (static_cast(produce.produceQA)) { + registry.fill(HIST("QA/Mixing/h2mu1_mu2"), multiplicity1, multiplicity2); + registry.fill(HIST("QA/Mixing/h2ce1_ce2"), centrality1, centrality2); + registry.fill(HIST("QA/Mixing/h2vz1_vz2"), vz1, vz2); } for (const auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(posDaughtersc1, negDaughtersc2))) { - if (!selectedTrack(track1, true)) // track1 is positive + if (!selectedTrack(track1, true)) { continue; + } - if (!selectedTrack(track2, false)) // track2 is negative + if (!selectedTrack(track2, false)) { continue; + } mother = calculateMother(track1, track2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; + } pointPair = fillPointPair(mother.M(), mother.Pt(), - getMultiplicity(c1), - getCentrality(c1), + multiplicity1, + centrality1, tpcNsigma(track1), tpcNsigma(track2), mother.Eta(), mother.Rapidity(), - c1.posZ(), - getMultiplicity(c2), - getCentrality(c2), - c2.posZ()); + vz1, + multiplicity2, + centrality2, + vz2); rsnOutput->fillMixingpm(pointPair); } for (const auto& [track1, track2] : combinations(o2::soa::CombinationsFullIndexPolicy(posDaughtersc2, negDaughtersc1))) { - if (!selectedTrack(track1, true)) - + if (!selectedTrack(track1, true)) { continue; - if (!selectedTrack(track2, false)) + } + if (!selectedTrack(track2, false)) { continue; + } mother = calculateMother(track1, track2); - if (!seletectedMother(mother)) + if (!selectedMother(mother)) { continue; + } pointPair = fillPointPair(mother.M(), mother.Pt(), - getMultiplicity(c1), - getCentrality(c1), + multiplicity1, + centrality1, tpcNsigma(track1), tpcNsigma(track2), mother.Eta(), mother.Rapidity(), - c1.posZ(), - getMultiplicity(c2), - getCentrality(c2), - c2.posZ()); + vz1, + multiplicity2, + centrality2, + vz2); rsnOutput->fillMixingmp(pointPair); } @@ -1222,38 +1608,59 @@ struct PhianalysisTHnSparse { } PROCESS_SWITCH(PhianalysisTHnSparse, processMixed, "Process Mixing Event.", false); - void processFactors(McCollisionMults::iterator const& mcCollision, soa::SmallGroups const& collisions, LabeledTracks const& /*particles*/, aod::McParticles const& mcParticles) + void processCorr(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles, + soa::SmallGroups> const& collisions, + soa::Join const&) { - registry.fill(HIST("Factors/hGenEvents"), mcCollision.multMCNParticlesEta08(), 0.5); - - if (std::abs(mcCollision.posZ()) > vzCut) + if (!static_cast(produce.produceMC)) { return; + } - registry.fill(HIST("Factors/hGenEvents"), mcCollision.multMCNParticlesEta08(), 1.5); + registry.fill(HIST("QAMC/Gen/hNEvents"), 0.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 0.5); - if (inelGrater0 && !mcCollision.isInelGt0()) + if (std::abs(mcCollision.posZ()) > static_cast(eventCuts.vzCut)) { return; + } + registry.fill(HIST("QAMC/Gen/hNEvents"), 1.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 1.5); - registry.fill(HIST("Factors/hGenEvents"), mcCollision.multMCNParticlesEta08(), 2.5); + // Accepted generated border cuts + const auto bc = mcCollision.bc_as>(); + if (!selectedGenEventBcBorderCuts(bc)) { + return; + } + registry.fill(HIST("QAMC/Gen/hNEvents"), 2.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 2.5); - float centrality = 100.5f; - for (auto const& collision : collisions) { - centrality = collision.centFT0M(); + // Accepted generated event RCT flags + if (!selectedGenEventRCT(bc)) { + return; } + registry.fill(HIST("QAMC/Gen/hNEvents"), 3.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 3.5); - registry.fill(HIST("Factors/hCentralityVsMultMC"), centrality, mcCollision.multMCNParticlesEta08()); - registry.fill(HIST("Factors/hNrecInGen"), collisions.size()); + registry.fill(HIST("QAMC/Gen/hNEvents"), 4.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 4.5); - for (const auto& particle : mcParticles) { + bool isINELgt0 = false; + if (pwglf::isINELgtNmc(mcParticles, 0, pdg)) { + isINELgt0 = true; + registry.fill(HIST("QAMC/Gen/hNEvents"), 5.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 5.5); // Event Loss Denominator + } - if (std::abs(particle.y()) > static_cast(cut.rapidity)) - continue; + for (const auto& mcParticle : mcParticles) { - if (particle.pdgCode() == motherPDG) { + if (std::abs(mcParticle.y()) > static_cast(trackCuts.rapidity)) { + continue; + } - auto daughters = particle.daughters_as(); - if (daughters.size() != dauSize) + if (mcParticle.pdgCode() == motherPDG) { + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != dauSize) { continue; + } auto daup = false; auto daun = false; @@ -1267,23 +1674,93 @@ struct PhianalysisTHnSparse { d2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massNeg); } } - if (!daup || !daun) + if (!daup || !daun) { continue; + } + registry.fill(HIST("QAMC/Gen/hNPhiPt"), mcParticle.pt(), 0.5); + registry.fill(HIST("QAMC/Gen/hNPhiPtCent"), mcParticle.pt(), mcCollision.centFT0M(), 0.5); + if (isINELgt0) { + registry.fill(HIST("QAMC/Gen/hNPhiPt"), mcParticle.pt(), 1.5); + registry.fill(HIST("QAMC/Gen/hNPhiPtCent"), mcParticle.pt(), mcCollision.centFT0M(), 1.5); // Signal Loss Denominator + } + } + } - mother = d1 + d2; + int nRecoInelColl = 0; + int nRecoInelGt0Coll = 0; + for (const auto& collision : collisions) { - registry.fill(HIST("Factors/h2dGenPhi"), centrality, mother.Pt()); - registry.fill(HIST("Factors/h3dGenPhiVsMultMCVsCentrality"), mcCollision.multMCNParticlesEta08(), centrality, mother.Pt()); + registry.fill(HIST("QAMC/Rec/hNEvents"), 0.5); + registry.fill(HIST("QAMC/Rec/hNEventsCent"), mcCollision.centFT0M(), 0.5); + + if (!selectedEvent(collision)) { + continue; + } + + nRecoInelColl++; + registry.fill(HIST("QAMC/Rec/hNEvents"), 1.5); + registry.fill(HIST("QAMC/Rec/hNEventsCent"), mcCollision.centFT0M(), 1.5); + + if (collision.isInelGt0()) { + nRecoInelGt0Coll++; + registry.fill(HIST("QAMC/Rec/hNEvents"), 2.5); + registry.fill(HIST("QAMC/Rec/hNEventsCent"), mcCollision.centFT0M(), 2.5); } } - if (collisions.size() == 0) + if (nRecoInelColl < 1) { // At least one reconstructed event/collision return; + } + + registry.fill(HIST("QAMC/Gen/hNEvents"), 6.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 6.5); + + if (nRecoInelGt0Coll > 0) { + registry.fill(HIST("QAMC/Gen/hNEvents"), 7.5); + registry.fill(HIST("QAMC/Gen/hNEventsCent"), mcCollision.centFT0M(), 7.5); // Event Loss Numerator + } + + for (const auto& mcParticle : mcParticles) { + + if (std::abs(mcParticle.y()) > static_cast(trackCuts.rapidity)) { + continue; + } + + if (mcParticle.pdgCode() == motherPDG) { + + auto daughters = mcParticle.daughters_as(); + if (daughters.size() != dauSize) { + continue; + } + + auto daup = false; + auto daun = false; + + for (const auto& dau : daughters) { + if (dau.pdgCode() == daughterPosPDG) { + daup = true; + d1 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massPos); + } else if (dau.pdgCode() == -daughterNegPDG) { + daun = true; + d2 = ROOT::Math::PxPyPzMVector(dau.px(), dau.py(), dau.pz(), massNeg); + } + } + if (!daup || !daun) { + continue; + } - registry.fill(HIST("Factors/hGenEvents"), mcCollision.multMCNParticlesEta08(), 3.5); + registry.fill(HIST("QAMC/Gen/hNPhiPt"), mcParticle.pt(), 2.5); + registry.fill(HIST("QAMC/Gen/hNPhiPtCent"), mcParticle.pt(), mcCollision.centFT0M(), 2.5); + if (nRecoInelGt0Coll > 0) { + registry.fill(HIST("QAMC/Gen/hNPhiPt"), mcParticle.pt(), 3.5); + registry.fill(HIST("QAMC/Gen/hNPhiPtCent"), mcParticle.pt(), mcCollision.centFT0M(), 3.5); // Signal Loss Numerator + } + } + } } - PROCESS_SWITCH(PhianalysisTHnSparse, processFactors, "Process to obtain normalization factors from MC.", false); + PROCESS_SWITCH(PhianalysisTHnSparse, processCorr, "Process to calculate corrections.", false); }; + WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ diff --git a/PWGLF/Tasks/Resonances/phianalysisrun3.cxx b/PWGLF/Tasks/Resonances/phianalysisrun3.cxx index b923303eb1d..084f4a5aae4 100644 --- a/PWGLF/Tasks/Resonances/phianalysisrun3.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisrun3.cxx @@ -241,14 +241,14 @@ struct phianalysisrun3 { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {2000, 0, 10000}, "TPC multiplicity for bin"}; // using BinningType = BinningPolicy>; - // BinningType binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + // BinningType binningOnPositions{{axisVertex, axisMultiplicityClass}}; // using BinningTypeTPCMultiplicity = ColumnBinningPolicy; using BinningTypeVertexContributor = ColumnBinningPolicy; // using BinningTypeCentrality = ColumnBinningPolicy; // using BinningType = ColumnBinningPolicy; - // BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; + // BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; void processSameEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { @@ -266,7 +266,7 @@ struct phianalysisrun3 { histos.fill(HIST("hCentrality"), multiplicity); histos.fill(HIST("hNcontributor"), collision.numContrib()); histos.fill(HIST("hVtxZ"), collision.posZ()); - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -276,7 +276,7 @@ struct phianalysisrun3 { histos.fill(HIST("hNsigmaKaonTPC"), track1.tpcNSigmaKa()); histos.fill(HIST("hNsigmaKaonTOF"), track1.tofNSigmaKa()); auto track1ID = track1.index(); - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!selectionTrack(track2)) { continue; } @@ -306,7 +306,7 @@ struct phianalysisrun3 { { auto tracksTuple = std::make_tuple(tracks); //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [c1, tracks1, c2, tracks2] : pair) { if (applyEvsel && !c1.sel8()) { @@ -394,7 +394,7 @@ struct phianalysisrun3 { } auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } @@ -421,7 +421,7 @@ struct phianalysisrun3 { } histos.fill(HIST("hMC"), 5.5); auto oldindex = -999; - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (!selectionTrack(track1)) { continue; } @@ -429,7 +429,7 @@ struct phianalysisrun3 { continue; } auto track1ID = track1.index(); - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (!track2.has_mcParticle()) { continue; } diff --git a/PWGLF/Tasks/Resonances/phianalysisrun3_PbPb.cxx b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx similarity index 72% rename from PWGLF/Tasks/Resonances/phianalysisrun3_PbPb.cxx rename to PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx index 9787970f588..c73ae59c466 100644 --- a/PWGLF/Tasks/Resonances/phianalysisrun3_PbPb.cxx +++ b/PWGLF/Tasks/Resonances/phianalysisrun3pbpb.cxx @@ -8,7 +8,8 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file phianalysisrun3_PbPb.cxx + +/// \file phianalysisrun3pbpb.cxx /// \brief Code for phi resonance without resonance initializer /// \author Sarjeeta Gami @@ -55,7 +56,7 @@ using namespace o2::framework::expressions; using std::array; using namespace o2::aod::rctsel; -struct phianalysisrun3_PbPb { +struct Phianalysisrun3pbpb { struct : ConfigurableGroup { Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; @@ -85,6 +86,8 @@ struct phianalysisrun3_PbPb { Configurable additionalEvSel4{"additionalEvSel4", true, "Additional evsel4"}; Configurable additionalEvSel5{"additionalEvSel5", true, "Additional evsel5"}; Configurable additionalEvSel6{"additionalEvSel6", true, "Additional evsel6"}; + Configurable selHasBC{"selHasBC", true, "Has BC?"}; + Configurable selHasFT0{"selHasFT0", true, "Has FT0?"}; Configurable cutvz{"cutvz", true, "Vz cut"}; Configurable cutvzgen{"cutvzgen", true, "Vz cut"}; Configurable isINELgt0{"isINELgt0", true, "INEL>0 selection"}; @@ -115,6 +118,7 @@ struct phianalysisrun3_PbPb { ConfigurableAxis ptAxisphi{"ptAxisphi", {200, 0.0f, 20.0f}, "phi pT axis"}; ConfigurableAxis centAxisphi{"centAxisphi", {200, 0.0, 200.0}, "phi centrality axis"}; ConfigurableAxis massAxisphi{"massAxisphi", {200, 0.9, 1.1}, "phi mass axis"}; + ConfigurableAxis deltaMassAxisphi{"deltaMassAxisphi", {200, -0.1, 0.1}, "phi Rec-Gen mass difference axis (mass resolution)"}; ConfigurableAxis axisNch{"axisNch", {100, 0.0f, 100.0f}, "Number of charged particles in |y| < 0.5"}; ConfigurableAxis binsImpactPar{"binsImpactPar", {VARIABLE_WIDTH, 0, 3.5, 5.67, 7.45, 8.85, 10.0, 11.21, 12.26, 13.28, 14.23, 15.27}, "Binning of the impact parameter axis"}; ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.6, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.5, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0, 10.0, 12.0}, "Binning of the pT axis"}; @@ -124,7 +128,7 @@ struct phianalysisrun3_PbPb { Configurable centestimator{"centestimator", 0, "Select multiplicity estimator: 0 - FT0C, 1 - FT0A, 2 - FT0M, 3 - FV0A, 4 - PVTracks"}; ConfigurableAxis binsMult{"binsMult", {500, 0.0f, +500.0f}, ""}; Configurable isApplyCentFT0C{"isApplyCentFT0C", true, "Centrality based on FT0C"}; - Configurable isApplyCentFT0M{"isApplyCentFV0M", false, "Centrality based on FT0M"}; + Configurable isApplyCentFT0M{"isApplyCentFT0M", false, "Centrality based on FT0M"}; Configurable isApplyInelgt0{"isApplyInelgt0", false, "Enable INEL > 0 condition"}; Configurable isApplyTVX{"isApplyTVX", false, "Enable TVX trigger sel"}; Configurable genacceptancecut{"genacceptancecut", true, "use acceptance cut for generated"}; @@ -145,12 +149,19 @@ struct phianalysisrun3_PbPb { histos.add("hOccupancy", "Occupancy distribution", kTH1F, {{500, 0, 50000}}); histos.add("hEvtSelInfo", "hEvtSelInfo", kTH1F, {{11, 0, 11.0}}); histos.add("h3PhiInvMassUnlikeSign", "Invariant mass of Phi meson Unlike Sign", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); + histos.add("h3PhiInvMassLikeSignPP", "Invariant mass of Phi meson Like Sign positive", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); + histos.add("h3PhiInvMassLikeSignMM", "Invariant mass of Phi meson Like Sign negative", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassMixed", "Invariant mass of Phi meson Mixed", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassRot", "Invariant mass of Phi meson Rotation", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassSame", "Invariant mass of Phi meson same", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h2PhiRapidity", "phi meson Rapidity", kTH2F, {ptAxisphi, {200, -4, 4}}); histos.add("hEta", "eta of kaon track candidates", HistType::kTH2F, {{200, -1.0f, 1.0f}, ptAxisphi}); histos.add("hPhi", "phi of kaon track candidates", HistType::kTH2F, {{65, 0, 6.5}, ptAxisphi}); + histos.add("QAbefore/TPC_Nsigma_all", "TPC NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{Kaon}", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); + histos.add("QAbefore/TOF_Nsigma_all", "TOF NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TOF}^{Kaon}", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); + histos.add("QAbefore/trkDCAxy", "DCAxy distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); + histos.add("QAbefore/trkDCAz", "DCAz distribution of kaon track candidates", HistType::kTH1F, {{150, -1.0f, 1.0f}}); + histos.add("QAbefore/TOF_TPC_Mapka_all", "TOF + TPC Combined PID for Kaon;#sigma_{TOF}^{Kaon};#sigma_{TPC}^{Kaon}", {HistType::kTH2D, {{100, -6, 6}, {100, -6, 6}}}); // DCA QA // DCA histograms: separate for positive and negative kaons, range [-1.0, 1.0] @@ -192,58 +203,49 @@ struct phianalysisrun3_PbPb { histos.add("QAafter/TPC_Nsigma_all_pos", "TPC NSigma for positive Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{K^{+}}", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); histos.add("QAafter/TPC_Nsigma_all_neg", "TPC NSigma for negative Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{K^{-}}", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); - } else if (isMC) { + } else { histos.add("hMC", "MC Event statistics", kTH1F, {{15, 0.0f, 15.0f}}); - histos.add("EL1", "MC Event statistics", kTH1F, {impactParAxis}); - histos.add("EL2", "MC Event statistics", kTH1F, {centAxis}); - histos.add("ES1", "MC Event statistics", kTH1F, {impactParAxis}); - histos.add("ES3", "MC Event statistics", kTH1F, {impactParAxis}); - histos.add("ES2", "MC Event statistics", kTH1F, {centAxis}); - histos.add("ES4", "MC Event statistics", kTH1F, {centAxis}); histos.add("h1PhiGen", "Phi meson Gen", kTH1F, {ptAxisphi}); histos.add("h1PhiGen1", "Phi meson Gen", kTH1F, {ptAxisphi}); histos.add("h1PhiRecsplit", "Phi meson Rec split", kTH1F, {ptAxisphi}); histos.add("Centrec", "MC Centrality", kTH1F, {centAxisphi}); histos.add("Centgen", "MC Centrality", kTH1F, {centAxisphi}); - histos.add("hVtxZgen", "Vertex distribution in Z;Z (cm)", kTH1F, {{400, -20.0, 20.0}}); histos.add("hVtxZrec", "Vertex distribution in Z;Z (cm)", kTH1F, {{400, -20.0, 20.0}}); histos.add("h2PhiRec2", "Phi meson Rec", kTH2F, {ptAxisphi, centAxisphi}); histos.add("h3PhiRec3", "Phi meson Rec", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); - histos.add("h3Phi1Rec3", "Phi meson Rec", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); - histos.add("h3PhiGen3", "Phi meson Gen", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); histos.add("h3PhiInvMassMixedMC", "Invariant mass of Phi meson Mixed", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassSameMC", "Invariant mass of Phi meson same", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassSameMC1", "Invariant mass of Phi meson same", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h3PhiInvMassRotMC", "Invariant mass of Phi meson Rotation", kTH3F, {centAxisphi, ptAxisphi, massAxisphi}); histos.add("h2PhiGen2", "Phi meson gen", kTH2F, {ptAxisphi, centAxisphi}); - histos.add("h2PhiGen1", "Phi meson gen", kTH2F, {ptAxis, impactParAxis}); histos.add("h1PhiRec1", "Phi meson Rec", kTH1F, {ptAxisphi}); histos.add("h1Phimassgen", "Phi meson gen", kTH1F, {massAxisphi}); histos.add("h1Phimassrec", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phimasssame", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phimassmix", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phimassrot", "Phi meson Rec", kTH1F, {massAxisphi}); - histos.add("h1Phi1massrec", "Phi meson Rec", kTH1F, {massAxisphi}); histos.add("h1Phipt", "Phi meson Rec", kTH1F, {ptAxisphi}); - histos.add("hOccupancy1", "Occupancy distribution", kTH1F, {{500, 0, 50000}}); histos.add("h1PhifinalRec", "Phi meson Rec", kTH1F, {ptAxisphi}); histos.add("h1Phifinalgenmass", "Phi meson gen mass", kTH1F, {massAxisphi}); histos.add("h3PhifinalRec", "Phi meson Rec", kTH3F, {ptAxisphi, centAxisphi, massAxisphi}); + // Mass resolution: Rec-Gen invariant mass difference per truth-matched candidate, + // binned in (reconstructed pT, centrality) so it lines up with how real data + // (and every other Rec-level histogram in this file) is binned - resolution vs. + // pT is only comparable to the data-driven Voigtian-width proxy if both use the + // same, measurable (reconstructed) pT axis, not the MC-truth generated pT. + // Lets the resolution be obtained directly from mean/RMS or a Gaussian fit of the + // projected deltaM distribution per pT bin, instead of only from the + // Voigtian-sigma method (resv.cxx) that h3PhifinalRec alone supports. + histos.add("h3PhiRecoResolution", "Phi meson Rec-Gen mass difference", kTH3F, {ptAxisphi, centAxisphi, deltaMassAxisphi}); histos.add("h1PhifinalGen", "Phi meson Gen", kTH1F, {ptAxisphi}); histos.add("h2PhifinalGen", "Phi meson Gen", kTH2F, {ptAxisphi, centAxisphi}); - histos.add("hMC1", "MC Event statistics", kTH1F, {{15, 0.0f, 15.0f}}); + histos.add("hMC1", "MC Event statistics", kTH1F, {{20, 0.0f, 20.0f}}); histos.add("Centrec1", "MC Centrality", kTH1F, {centAxisphi}); histos.add("Centsame", "MC Centrality", kTH1F, {centAxisphi}); histos.add("Centmc", "MC Centrality", kTH1F, {centAxisphi}); histos.add("Centmix", "MC Centrality", kTH1F, {centAxisphi}); histos.add("Centgen1", "MC Centrality", kTH1F, {centAxisphi}); histos.add("h1PhiRecsplit1", "Phi meson Rec split", kTH1F, {ptAxisphi}); - histos.add("hImpactParameterGen", "Impact parameter of generated MC events", kTH1F, {impactParAxis}); - histos.add("hImpactParameterRec", "Impact parameter of generated MC events", kTH1F, {impactParAxis}); - histos.add("hImpactParameterGenCen", "Impact parameter of generated MC events", kTH2F, {impactParAxis, centAxis}); - histos.add("hImpactParameterRecCen", "Impact parameter of generated MC events", kTH2F, {impactParAxis, centAxis}); - histos.add("TOF_Nsigma_MC", "TOF NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TOF}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); - histos.add("TPC_Nsigma_MC", "TPC NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); histos.add("TOF_Nsigma1_MC", "TOF NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TOF}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); histos.add("TPC_Nsigma1_MC", "TPC NSigma for Kaon;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{Kaon};", {HistType::kTH3D, {{200, -12, 12}, centAxisphi, ptAxisphi}}); histos.add("trkDCAxy", "DCAxy distribution of positive kaon track candidates", HistType::kTH3F, {{150, -1.0f, 1.0f}, centAxisphi, ptAxisphi}); @@ -296,16 +298,15 @@ struct phianalysisrun3_PbPb { } double massKa = o2::constants::physics::MassKPlus; - double rapidity; - double genMass, recMass, resolution; + double rapidity{0.}; + double genMass{0.}; + double recMass{0.}; ROOT::Math::PxPyPzMVector phiMother, daughter1, daughter2; ROOT::Math::PxPyPzMVector d1, d2, mother; double mass{0.}; - double massrotation{0.}; double pT{0.}; - array pvec0; - array pvec1; - array pvec1rotation; + array pvec0{}; + array pvec1{}; template bool selectionTrack(const T& candidate) { @@ -336,9 +337,9 @@ struct phianalysisrun3_PbPb { return false; } template - bool selectionPIDpTdependent(const T& candidate, int pid) + bool selectionPIDpTdependent(const T& candidate, int pidHypothesis) { - if (pid == 0) { + if (pidHypothesis == 0) { if (!candidate.hasTOF() && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmacutTPC) { return true; } @@ -347,13 +348,13 @@ struct phianalysisrun3_PbPb { return true; } return false; - - } else if (pid == 1) { - constexpr double kPtThresholdForTOF = 0.5; - if (candidate.pt() < kPtThresholdForTOF && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmacutTPC) { + } + if (pidHypothesis == 1) { + constexpr double PtThresholdForTOF = 0.5; + if (candidate.pt() < PtThresholdForTOF && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmacutTPC) { return true; } - if (candidate.pt() >= kPtThresholdForTOF && candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && + if (candidate.pt() >= PtThresholdForTOF && candidate.hasTOF() && candidate.beta() > cfgCutTOFBeta && std::abs(candidate.tpcNSigmaKa()) < selectionConfig.nsigmacutTPC && std::abs(candidate.tofNSigmaKa()) < selectionConfig.nsigmacutTOF) { return true; } @@ -368,35 +369,48 @@ struct phianalysisrun3_PbPb { template bool myEventSelections(const CollType& collision) { - if (selectionConfig.cutvz && !(std::abs(collision.posZ()) < selectionConfig.cfgCutVertex)) + if (selectionConfig.cutvz && !(std::abs(collision.posZ()) < selectionConfig.cfgCutVertex)) { + return false; + } + + if (!collision.sel8()) { + return false; + } + + if (selectionConfig.selHasBC && !collision.has_foundBC()) { return false; + } - if (!collision.sel8()) + if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { return false; + } - if (selectionConfig.additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) + if (selectionConfig.additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; + } - if (selectionConfig.additionalEvSel2 && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) + if (selectionConfig.additionalEvSel2 && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; + } - if (selectionConfig.additionalEvSel3 && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) + if (selectionConfig.additionalEvSel3 && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; + } - if (selectionConfig.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) + if (selectionConfig.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; - if (selectionConfig.additionalEvSel5 && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) + } + if (selectionConfig.additionalEvSel5 && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; - if (selectionConfig.additionalEvSel6 && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + } + if (selectionConfig.additionalEvSel6 && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; + } if (selectionConfig.isINELgt0 && !collision.isInelGt0()) { return false; } int occupancy = collision.trackOccupancyInTimeRange(); - if (selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) - return false; - - return true; + return !selectionConfig.fillOccupancy || (occupancy >= cfgMinOccupancy && occupancy <= cfgMaxOccupancy); } template float selColMultMC(CheckColCent const& col) @@ -427,21 +441,17 @@ struct phianalysisrun3_PbPb { template bool selectionPair(const T1& candidate1, const T2& candidate2) { - double pt1, pt2, pz1, pz2, p1, p2, angle; - pt1 = candidate1.pt(); - pt2 = candidate2.pt(); - pz1 = candidate1.pz(); - pz2 = candidate2.pz(); - p1 = candidate1.p(); - p2 = candidate2.p(); - angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); - if (isDeepAngle && angle < cfgDeepAngle) { - return false; - } - return true; + double pt1 = candidate1.pt(); + double pt2 = candidate2.pt(); + double pz1 = candidate1.pz(); + double pz2 = candidate2.pz(); + double p1 = candidate1.p(); + double p2 = candidate2.p(); + double angle = std::acos((pt1 * pt2 + pz1 * pz2) / (p1 * p2)); + return !isDeepAngle || angle >= cfgDeepAngle; } template - void fillinvMass(const T1& candidate1, const T2& candidate2, float multiplicity, bool unlike, bool mix, float massd1, float massd2) + void fillinvMass(const T1& candidate1, const T2& candidate2, float multiplicity, bool unlike, bool mix, bool likesign, float massd1, float massd2) { pvec0 = std::array{candidate1.px(), candidate1.py(), candidate1.pz()}; pvec1 = std::array{candidate2.px(), candidate2.py(), candidate2.pz()}; @@ -461,10 +471,10 @@ struct phianalysisrun3_PbPb { candidate1.pz() + candidate2.pz()}, mass); - constexpr int kOppositeCharge = 0; + constexpr int OppositeCharge = 0; // default filling - if (rapidity > rapiditycut1 && rapidity < rapiditycut2 && track1Sign * track2Sign < kOppositeCharge) { + if (rapidity > rapiditycut1 && rapidity < rapiditycut2 && track1Sign * track2Sign < OppositeCharge) { if (unlike) { histos.fill(HIST("h3PhiInvMassUnlikeSign"), multiplicity, pT, mass); histos.fill(HIST("h2PhiRapidity"), pT, rapidity); @@ -473,6 +483,41 @@ struct phianalysisrun3_PbPb { histos.fill(HIST("h3PhiInvMassMixed"), multiplicity, pT, mass); } } + + // like-sign background + if (rapidity > rapiditycut1 && rapidity < rapiditycut2 && likesign && track1Sign * track2Sign > OppositeCharge) { + if (track1Sign > 0 && track2Sign > 0) { + histos.fill(HIST("h3PhiInvMassLikeSignPP"), multiplicity, pT, mass); + } else { + histos.fill(HIST("h3PhiInvMassLikeSignMM"), multiplicity, pT, mass); + } + } + } + template + void fillMixedPairs(const T1& tracks1, const T2& tracks2, float multiplicity) + { + for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { + bool unlike = false; + bool likesign = false; + bool mix = true; + if (!selectionTrack(t1)) { + continue; + } + if (!selectionTrack(t2)) { + continue; + } + if (!selectionPair(t1, t2)) { + continue; + } + if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { + fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); + } + if (ispTdepPID && + (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && + (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { + fillinvMass(t1, t2, multiplicity, unlike, mix, likesign, massKa, massKa); + } + } } Filter collisionFilter = (!selectionConfig.cutvz) || @@ -518,6 +563,12 @@ struct phianalysisrun3_PbPb { if (cfgDoSel8 && !collision.sel8()) { return; } + if (selectionConfig.selHasBC && !collision.has_foundBC()) { + return; + } + if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { + return; + } histos.fill(HIST("hEvtSelInfo"), 2.5); if (selectionConfig.additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return; @@ -611,9 +662,8 @@ struct phianalysisrun3_PbPb { } bool unlike = true; bool mix = false; + bool likesign = true; if (!ispTdepPID && selectionPID(track1) && selectionPID(track2)) { - int track1Sign = track1.sign(); // Assuming `charge()` gives +1 or -1 - if (track1Sign > 0) { // Positive kaon histos.fill(HIST("QAafter/TPC_Nsigma_all_pos"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); histos.fill(HIST("QAafter/TOF_Nsigma_all_pos"), track1.tofNSigmaKa(), multiplicity, track1.pt()); @@ -632,14 +682,12 @@ struct phianalysisrun3_PbPb { histos.fill(HIST("QAafter/TOF_TPC_Mapka_all_neg"), track1.tofNSigmaKa(), track1.tpcNSigmaKa()); } - fillinvMass(track1, track2, multiplicity, unlike, mix, massKa, massKa); + fillinvMass(track1, track2, multiplicity, unlike, mix, likesign, massKa, massKa); } if (ispTdepPID && (selectionPIDpTdependent(track1, 0) || selectionPIDpTdependent(track1, 1)) && (selectionPIDpTdependent(track2, 0) || selectionPIDpTdependent(track2, 1))) { - int track1Sign = track1.sign(); // Same assumption as above - if (track1Sign > 0) { // Positive kaon histos.fill(HIST("QAafter/TPC_Nsigma_all_pos"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); histos.fill(HIST("QAafter/TOF_Nsigma_all_pos"), track1.tofNSigmaKa(), multiplicity, track1.pt()); @@ -658,338 +706,125 @@ struct phianalysisrun3_PbPb { histos.fill(HIST("QAafter/TOF_TPC_Mapka_all_neg"), track1.tofNSigmaKa(), track1.tpcNSigmaKa()); } - fillinvMass(track1, track2, multiplicity, unlike, mix, massKa, massKa); + fillinvMass(track1, track2, multiplicity, unlike, mix, likesign, massKa, massKa); } } } } - PROCESS_SWITCH(phianalysisrun3_PbPb, processSameEvent, "Process Same event", false); - void processMixedEvent1(EventCandidates const& collisions, TrackCandidates const& tracks) + PROCESS_SWITCH(Phianalysisrun3pbpb, processSameEvent, "Process Same event", false); + void processMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; - } - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; - } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; - } - float multiplicity; - multiplicity = c1.centFT0C(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + //////// currently mixing the event with similar multiplicity; estimator picked at runtime via centestimator //////// + const int kCentFT0C = 0; + const int kCentFT0A = 1; + const int kCentFT0M = 2; + const int kCentFV0A = 3; + if (centestimator == kCentFT0C) { + BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } - } - } - } - PROCESS_SWITCH(phianalysisrun3_PbPb, processMixedEvent1, "Process Mixed event", false); - void processMixedEvent2(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; - } - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; + fillMixedPairs(tracks1, tracks2, c1.centFT0C()); } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; - } - float multiplicity; - multiplicity = c1.centFT0A(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + } else if (centestimator == kCentFT0A) { + BinningTypeVertexContributor2 binningOnPositions{{axisVertex, axisMultiplicity}}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } + fillMixedPairs(tracks1, tracks2, c1.centFT0A()); } - } - } - - PROCESS_SWITCH(phianalysisrun3_PbPb, processMixedEvent2, "Process Mixed event", false); - void processMixedEvent3(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; - } - if (!c1.sel8()) { - continue; - } - if (!c2.sel8()) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; - } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; - } - float multiplicity; - multiplicity = c1.centFT0M(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + } else if (centestimator == kCentFT0M) { + BinningTypeVertexContributor3 binningOnPositions{{axisVertex, axisMultiplicity}}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } - } - } - } - - PROCESS_SWITCH(phianalysisrun3_PbPb, processMixedEvent3, "Process Mixed event", false); - void processMixedEvent4(EventCandidates const& collisions, TrackCandidates const& tracks) - { - auto tracksTuple = std::make_tuple(tracks); - //////// currently mixing the event with similar TPC multiplicity //////// - BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}, true}; - SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; - for (const auto& [c1, tracks1, c2, tracks2] : pair) { - if (rctCut.requireRCTFlagChecker && !rctChecker(c1)) { - continue; - } - if (rctCut.requireRCTFlagChecker && !rctChecker(c2)) { - continue; - } - if (!c1.sel8()) { - continue; + fillMixedPairs(tracks1, tracks2, c1.centFT0M()); } - if (!c2.sel8()) { - continue; - } - if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel2 && (!c1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !c2.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - continue; - } - if (selectionConfig.additionalEvSel3 && (!c1.selection_bit(aod::evsel::kNoSameBunchPileup) || !c2.selection_bit(aod::evsel::kNoSameBunchPileup))) { - continue; - } - if (selectionConfig.additionalEvSel4 && (!c1.selection_bit(aod::evsel::kIsGoodITSLayersAll) || !c2.selection_bit(aod::evsel::kIsGoodITSLayersAll))) { - continue; - } - if (selectionConfig.additionalEvSel5 && (!c1.selection_bit(aod::evsel::kNoCollInTimeRangeStandard) || !c2.selection_bit(aod::evsel::kNoCollInTimeRangeStandard))) { - continue; - } - if (selectionConfig.additionalEvSel6 && (!c1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !c2.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - if (selectionConfig.isINELgt0 && - (!c1.isInelGt0() || !c2.isInelGt0())) { - continue; - } - - int occupancy1 = c1.trackOccupancyInTimeRange(); - int occupancy2 = c2.trackOccupancyInTimeRange(); - - if (selectionConfig.fillOccupancy && - ((occupancy1 < cfgMinOccupancy || occupancy1 > cfgMaxOccupancy) || - (occupancy2 < cfgMinOccupancy || occupancy2 > cfgMaxOccupancy))) { - continue; - } - float multiplicity; - multiplicity = c1.centFV0A(); - for (const auto& [t1, t2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(tracks1, tracks2))) { - bool unlike = false; - bool mix = true; - if (!selectionTrack(t1)) { - continue; - } - if (!selectionTrack(t2)) { + } else if (centestimator == kCentFV0A) { + BinningTypeVertexContributor4 binningOnPositions{{axisVertex, axisMultiplicity}}; + SameKindPair pair{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; + for (const auto& [c1, tracks1, c2, tracks2] : pair) { + if (rctCut.requireRCTFlagChecker && (!rctChecker(c1) || !rctChecker(c2))) { continue; } - if (!selectionPair(t1, t2)) { + if (!myEventSelections(c1) || !myEventSelections(c2)) { continue; } - if (!ispTdepPID && selectionPID(t1) && selectionPID(t2)) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } - if (ispTdepPID && - (selectionPIDpTdependent(t1, 0) || selectionPIDpTdependent(t1, 1)) && - (selectionPIDpTdependent(t2, 0) || selectionPIDpTdependent(t2, 1))) { - fillinvMass(t1, t2, multiplicity, unlike, mix, massKa, massKa); - } + fillMixedPairs(tracks1, tracks2, c1.centFV0A()); } } } - - PROCESS_SWITCH(phianalysisrun3_PbPb, processMixedEvent4, "Process Mixed event", false); + PROCESS_SWITCH(Phianalysisrun3pbpb, processMixedEvent, "Process Mixed event", false); void processRotEvent(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::BCs const&) { - if (!collision.sel8()) { + if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { + return; + } + if (cfgDoSel8 && !collision.sel8()) { return; } - if (selectionConfig.additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { + if (selectionConfig.selHasBC && !collision.has_foundBC()) { return; } - if (selectionConfig.additionalEvSel3 && (!collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard))) { + if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { + return; + } + if (selectionConfig.additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return; + } + if (selectionConfig.additionalEvSel2 && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return; + } + if (selectionConfig.additionalEvSel3 && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return; + } + if (selectionConfig.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return; + } + if (selectionConfig.additionalEvSel5 && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return; + } + if (selectionConfig.additionalEvSel6 && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return; } int occupancy = collision.trackOccupancyInTimeRange(); if (selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { return; } + if (selectionConfig.isINELgt0 && !collision.isInelGt0()) { + return; + } float multiplicity{-1}; - if (cfgMultFT0) + const int kCentFT0C = 0; + const int kCentFT0A = 1; + const int kCentFT0M = 2; + const int kCentFV0A = 3; + if (centestimator == kCentFT0C) { multiplicity = collision.centFT0C(); + } else if (centestimator == kCentFT0A) { + multiplicity = collision.centFT0A(); + } else if (centestimator == kCentFT0M) { + multiplicity = collision.centFT0M(); + } else if (centestimator == kCentFV0A) { + multiplicity = collision.centFV0A(); + } histos.fill(HIST("hCentrality"), multiplicity); histos.fill(HIST("hVtxZ"), collision.posZ()); histos.fill(HIST("hOccupancy"), occupancy); @@ -1019,9 +854,13 @@ struct phianalysisrun3_PbPb { if (!ispTdepPID && (!selectionPID(track1) || !selectionPID(track2))) { continue; } - if (ispTdepPID && - (selectionPIDpTdependent(track1, 0) || selectionPIDpTdependent(track1, 1)) && - (selectionPIDpTdependent(track2, 0) || selectionPIDpTdependent(track2, 1))) { + if (ispTdepPID && !(selectionPIDpTdependent(track1, 0) || selectionPIDpTdependent(track1, 1))) { + continue; + } + if (ispTdepPID && !(selectionPIDpTdependent(track2, 0) || selectionPIDpTdependent(track2, 1))) { + continue; + } + if (track1.sign() * track2.sign() > 0) { continue; } @@ -1038,8 +877,7 @@ struct phianalysisrun3_PbPb { for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { auto anglestart = confMinRot; auto angleend = confMaxRot; - auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); - auto rotangle = anglestart + nrotbkg * anglestep; + auto rotangle = (nBkgRotations > 1) ? (anglestart + nrotbkg * (angleend - anglestart) / (1.0 * (nBkgRotations - 1))) : (0.5 * (anglestart + angleend)); if (track1.sign() * track2.sign() < 0) { auto rotkaonPx = track1.px() * std::cos(rotangle) - track1.py() * std::sin(rotangle); auto rotkaonPy = track1.px() * std::sin(rotangle) + track1.py() * std::cos(rotangle); @@ -1057,7 +895,7 @@ struct phianalysisrun3_PbPb { } } - PROCESS_SWITCH(phianalysisrun3_PbPb, processRotEvent, "Process Rot event", false); + PROCESS_SWITCH(Phianalysisrun3pbpb, processRotEvent, "Process Rot event", false); void processMC(CollisionMCTrueTable::iterator const& /*TrueCollision*/, CollisionMCRecTableCentFT0C const& RecCollisions, TrackMCTrueTable const& GenParticles, FilTrackMCRecTable const& RecTracks) { histos.fill(HIST("hMC"), 0); @@ -1074,6 +912,12 @@ struct phianalysisrun3_PbPb { if (cfgDoSel8 && !RecCollision.sel8()) { continue; } + if (selectionConfig.selHasBC && !RecCollision.has_foundBC()) { + continue; + } + if (selectionConfig.selHasFT0 && !RecCollision.has_foundFT0()) { + continue; + } if (std::abs(RecCollision.posZ()) > selectionConfig.cfgCutVertex) { continue; } @@ -1184,7 +1028,7 @@ struct phianalysisrun3_PbPb { histos.fill(HIST("h3PhiInvMassSameMC1"), centrality, phiMesonMother.pt(), phiMesonMother.M()); int track1PDG = std::abs(mctrack1.pdgCode()); int track2PDG = std::abs(mctrack2.pdgCode()); - if (!(track1PDG == PDG_t::kKPlus && track2PDG == PDG_t::kKPlus)) { + if (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kKPlus) { continue; } for (const auto& mothertrack1 : mctrack1.mothers_as()) { @@ -1270,227 +1114,16 @@ struct phianalysisrun3_PbPb { } // rec collision loop } // process MC - PROCESS_SWITCH(phianalysisrun3_PbPb, processMC, "Process Reconstructed", false); - void processGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions) - { - - histos.fill(HIST("hMC"), 0.5); - if (std::abs(mcCollision.posZ()) < selectionConfig.cfgCutVertex) { - histos.fill(HIST("hMC"), 1.5); - } - float imp = mcCollision.impactParameter(); - histos.fill(HIST("hImpactParameterGen"), imp); - std::vector selectedEvents(collisions.size()); - int nevts = 0; - auto multiplicity = 0; - for (const auto& collision : collisions) { - if (cfgDoSel8 && !collision.sel8()) { - continue; - } - if (std::abs(collision.mcCollision().posZ()) > selectionConfig.cfgCutVertex) { - continue; - } - - if (selectionConfig.additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { - continue; - } - int occupancy = collision.trackOccupancyInTimeRange(); - if (selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { - continue; - } - histos.fill(HIST("hOccupancy1"), occupancy); - multiplicity = collision.centFT0C(); - histos.fill(HIST("Centgen"), multiplicity); - histos.fill(HIST("hVtxZgen"), collision.mcCollision().posZ()); - histos.fill(HIST("hImpactParameterGenCen"), imp, multiplicity); - - selectedEvents[nevts++] = collision.mcCollision_as().globalIndex(); - histos.fill(HIST("hMC"), 2.5); - } - selectedEvents.resize(nevts); - - const auto evtReconstructedAndSelected = std::find(selectedEvents.begin(), selectedEvents.end(), mcCollision.globalIndex()) != selectedEvents.end(); - histos.fill(HIST("EL1"), imp); - histos.fill(HIST("EL2"), multiplicity); - if (reco && !evtReconstructedAndSelected) { // Check that the event is reconstructed and that the reconstructed events pass the selection - return; - } - histos.fill(HIST("ES1"), imp); - histos.fill(HIST("ES2"), multiplicity); - for (const auto& mcParticle : mcParticles) { - const double kMaxAcceptedRapidity = 0.5; - - if (std::abs(mcParticle.y()) >= kMaxAcceptedRapidity) { - - continue; - } - if (pdgcheck && mcParticle.pdgCode() != o2::constants::physics::kPhi) { - continue; - } - auto kDaughters = mcParticle.daughters_as(); - const size_t kExpectedNumberOfDaughters = 2; - - if (kDaughters.size() != kExpectedNumberOfDaughters) { - - continue; - } - auto daughtp = false; - auto daughtm = false; - for (const auto& kCurrentDaughter : kDaughters) { - if (!kCurrentDaughter.isPhysicalPrimary()) { - continue; - } - if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { - daughtp = true; - kaonPlus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); - } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { - daughtm = true; - kaonMinus = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), massKa); - } - } - if (daughtp && daughtm) { - phiMesonMother = kaonPlus + kaonMinus; - histos.fill(HIST("h1PhiGen"), phiMesonMother.pt()); - histos.fill(HIST("h2PhiGen2"), phiMesonMother.pt(), multiplicity); - histos.fill(HIST("h2PhiGen1"), phiMesonMother.pt(), imp); - histos.fill(HIST("h1Phimassgen"), phiMesonMother.M()); - histos.fill(HIST("h3PhiGen3"), phiMesonMother.pt(), multiplicity, phiMesonMother.M()); - } - } - } - PROCESS_SWITCH(phianalysisrun3_PbPb, processGen, "Process Generated", false); - void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) + PROCESS_SWITCH(Phianalysisrun3pbpb, processMC, "Process Reconstructed", false); + void processSameEventMC(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) { - if (!collision.has_mcCollision()) { - return; - } - if (cfgDoSel8 && !collision.sel8()) { - return; - } - if (std::abs(collision.mcCollision().posZ()) > selectionConfig.cfgCutVertex) { + if (!collision.sel8()) { return; } - - if (selectionConfig.additionalEvSel2 && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { + if (selectionConfig.selHasBC && !collision.has_foundBC()) { return; } - int occupancy = collision.trackOccupancyInTimeRange(); - if (selectionConfig.fillOccupancy && (occupancy < cfgMinOccupancy || occupancy > cfgMaxOccupancy)) { - return; - } - auto multiplicity = collision.centFT0C(); - histos.fill(HIST("Centrec"), multiplicity); - histos.fill(HIST("hVtxZrec"), collision.posZ()); - float imp = collision.mcCollision().impactParameter(); - histos.fill(HIST("hImpactParameterRec"), imp); - histos.fill(HIST("hImpactParameterRecCen"), imp, multiplicity); - histos.fill(HIST("ES3"), imp); - histos.fill(HIST("ES4"), multiplicity); - auto oldindex = -999; - for (const auto& track1 : tracks) { - if (!selectionTrack(track1)) { - continue; - } - if (!track1.has_mcParticle()) { - continue; - } - auto track1ID = track1.index(); - for (const auto& track2 : tracks) { - if (!track2.has_mcParticle()) { - continue; - } - if (!selectionTrack(track2)) { - continue; - } - auto track2ID = track2.index(); - if (track2ID <= track1ID) { - continue; - } - if (!selectionPair(track1, track2)) { - continue; - } - if (track1.sign() * track2.sign() > 0) { - continue; - } - const auto mctrack1 = track1.mcParticle(); - const auto mctrack2 = track2.mcParticle(); - int track1PDG = std::abs(mctrack1.pdgCode()); - int track2PDG = std::abs(mctrack2.pdgCode()); - if (!mctrack1.isPhysicalPrimary()) { - continue; - } - if (!mctrack2.isPhysicalPrimary()) { - continue; - } - if (!(track1PDG == PDG_t::kKPlus && track2PDG == PDG_t::kKPlus)) { - continue; - } - daughter1 = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - daughter2 = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - - phiMother = daughter1 + daughter2; - histos.fill(HIST("h1Phi1massrec"), phiMother.M()); - histos.fill(HIST("h3Phi1Rec3"), phiMother.pt(), multiplicity, phiMother.M()); - for (const auto& mothertrack1 : mctrack1.mothers_as()) { - for (const auto& mothertrack2 : mctrack2.mothers_as()) { - if (mothertrack1.pdgCode() != mothertrack2.pdgCode()) { - continue; - } - if (mothertrack1.globalIndex() != mothertrack2.globalIndex()) { - continue; - } - if (!mothertrack1.producedByGenerator()) { - continue; - } - const double kMaxRapidityCut = 0.5; - - if (std::abs(mothertrack1.y()) >= kMaxRapidityCut) { - continue; - } - - if (pdgcheck && std::abs(mothertrack1.pdgCode()) != o2::constants::physics::kPhi) { - continue; - } - if (!ispTdepPID && (!selectionPID(track1) || !selectionPID(track2))) { - continue; - } - if (ispTdepPID && - (selectionPIDpTdependent(track1, 0) || selectionPIDpTdependent(track1, 1)) && - (selectionPIDpTdependent(track2, 0) || selectionPIDpTdependent(track2, 1))) { - - continue; - } - - histos.fill(HIST("TPC_Nsigma_MC"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); - histos.fill(HIST("TOF_Nsigma_MC"), track1.tofNSigmaKa(), multiplicity, track1.pt()); - if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { - histos.fill(HIST("h1PhiRecsplit"), mothertrack1.pt()); - continue; - } - oldindex = mothertrack1.globalIndex(); - if (track1.sign() * track2.sign() < 0) { - kaonPlus = ROOT::Math::PxPyPzMVector(track1.px(), track1.py(), track1.pz(), massKa); - kaonMinus = ROOT::Math::PxPyPzMVector(track2.px(), track2.py(), track2.pz(), massKa); - } - phiMesonMother = kaonPlus + kaonMinus; - - if (std::abs(phiMesonMother.Rapidity()) > confRapidity) { - continue; - } - histos.fill(HIST("h1PhiRec1"), phiMesonMother.pt()); - histos.fill(HIST("h2PhiRec2"), phiMesonMother.pt(), multiplicity); - histos.fill(HIST("h1Phimassrec"), phiMesonMother.M()); - histos.fill(HIST("h3PhiRec3"), phiMesonMother.pt(), multiplicity, phiMesonMother.M()); - } - } - } - } - } - - PROCESS_SWITCH(phianalysisrun3_PbPb, processRec, "Process Reconstructed", false); - void processSameEventMC(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) - { - if (!collision.sel8()) { + if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { return; } if (selectionConfig.additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { @@ -1579,8 +1212,7 @@ struct phianalysisrun3_PbPb { for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { auto anglestart = confMinRot; auto angleend = confMaxRot; - auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); - auto rotangle = anglestart + nrotbkg * anglestep; + auto rotangle = (nBkgRotations > 1) ? (anglestart + nrotbkg * (angleend - anglestart) / (1.0 * (nBkgRotations - 1))) : (0.5 * (anglestart + angleend)); if (track1.sign() * track2.sign() < 0) { auto rotkaonPx = track1.px() * std::cos(rotangle) - track1.py() * std::sin(rotangle); auto rotkaonPy = track1.px() * std::sin(rotangle) + track1.py() * std::cos(rotangle); @@ -1599,12 +1231,12 @@ struct phianalysisrun3_PbPb { } } - PROCESS_SWITCH(phianalysisrun3_PbPb, processSameEventMC, "Process Same event", false); + PROCESS_SWITCH(Phianalysisrun3pbpb, processSameEventMC, "Process Same event", false); void processMixedEventMC(EventCandidatesMC const& recCollisions, TrackCandidatesMC const& RecTracks, aod::McParticles const&) { auto tracksTuple = std::make_tuple(RecTracks); - BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor1 binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pairs{binningOnPositions, selectionConfig.cfgNoMixedEvents, -1, recCollisions, tracksTuple, &cache}; for (const auto& [c1, tracks1, c2, tracks2] : pairs) { @@ -1614,6 +1246,12 @@ struct phianalysisrun3_PbPb { if (!c2.sel8()) { continue; } + if (selectionConfig.selHasBC && (!c1.has_foundBC() || !c2.has_foundBC())) { + continue; + } + if (selectionConfig.selHasFT0 && (!c1.has_foundFT0() || !c2.has_foundFT0())) { + continue; + } if (selectionConfig.additionalEvSel1 && (!c1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !c2.selection_bit(aod::evsel::kNoTimeFrameBorder))) { continue; } @@ -1684,28 +1322,29 @@ struct phianalysisrun3_PbPb { } } } - PROCESS_SWITCH(phianalysisrun3_PbPb, processMixedEventMC, "Process Mixed event MC", true); + PROCESS_SWITCH(Phianalysisrun3pbpb, processMixedEventMC, "Process Mixed event MC", true); void processGen1(McCollisionMults::iterator const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& collisions) { // all events histos.fill(HIST("hMC1"), 0.5); - // vertex QA (not selection) - if (!selectionConfig.cutvzgen || - std::abs(mcCollision.posZ()) < selectionConfig.cfgCutVertex) { - histos.fill(HIST("hMC1"), 1.5); + if (selectionConfig.cutvzgen && std::abs(mcCollision.posZ()) > selectionConfig.cfgCutVertex) { + return; } + histos.fill(HIST("hMC1"), 1.5); // INEL>0 selection - if (isApplyInelgt0 && !mcCollision.isInelGt0()) + if (isApplyInelgt0 && !mcCollision.isInelGt0()) { return; + } histos.fill(HIST("hMC1"), 2.5); // TVX selection if (isApplyTVX && - !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) + (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { return; + } histos.fill(HIST("hMC1"), 3.5); std::vector selectedEvents(collisions.size()); @@ -1716,9 +1355,15 @@ struct phianalysisrun3_PbPb { if (cfgDoSel8 && !collision.sel8()) { continue; } + if (selectionConfig.selHasBC && !collision.has_foundBC()) { + continue; + } + if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { + continue; + } histos.fill(HIST("hMC1"), 5.5); if (selectionConfig.cutvz && - std::abs(collision.mcCollision().posZ()) > selectionConfig.cfgCutVertex) { + std::abs(collision.posZ()) > selectionConfig.cfgCutVertex) { continue; } @@ -1771,7 +1416,7 @@ struct phianalysisrun3_PbPb { multiplicity = collision.centFV0A(); } histos.fill(HIST("Centgen1"), multiplicity); - selectedEvents[nevts++] = collision.mcCollision_as().globalIndex(); + selectedEvents[nevts++] = collision.mcCollision_as().globalIndex(); } selectedEvents.resize(nevts); const auto evtReconstructedAndSelected = std::find(selectedEvents.begin(), selectedEvents.end(), mcCollision.globalIndex()) != selectedEvents.end(); @@ -1815,8 +1460,8 @@ struct phianalysisrun3_PbPb { } } - PROCESS_SWITCH(phianalysisrun3_PbPb, processGen1, "Process Generated", false); - void processRec1(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, aod::McCollisions const& /*mcCollisions*/) + PROCESS_SWITCH(Phianalysisrun3pbpb, processGen1, "Process Generated", false); + void processRec1(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const& /*mcParticles*/, McCollisionMults const& /*mcCollisions*/) { if (!collision.has_mcCollision()) { return; @@ -1824,8 +1469,14 @@ struct phianalysisrun3_PbPb { if (cfgDoSel8 && !collision.sel8()) { return; } + if (selectionConfig.selHasBC && !collision.has_foundBC()) { + return; + } + if (selectionConfig.selHasFT0 && !collision.has_foundFT0()) { + return; + } if (selectionConfig.cutvz && - std::abs(collision.mcCollision().posZ()) > selectionConfig.cfgCutVertex) { + std::abs(collision.posZ()) > selectionConfig.cfgCutVertex) { return; } if (selectionConfig.additionalEvSel1 && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { @@ -1908,7 +1559,7 @@ struct phianalysisrun3_PbPb { if (!mctrack2.isPhysicalPrimary()) { continue; } - if (!(track1PDG == PDG_t::kKPlus && track2PDG == PDG_t::kKPlus)) { + if (track1PDG != PDG_t::kKPlus || track2PDG != PDG_t::kKPlus) { continue; } for (const auto& mothertrack1 : mctrack1.mothers_as()) { @@ -1931,40 +1582,49 @@ struct phianalysisrun3_PbPb { if (!ispTdepPID && (!selectionPID(track1) || !selectionPID(track2))) { continue; } - if (ispTdepPID && - (selectionPIDpTdependent(track1, 0) || selectionPIDpTdependent(track1, 1)) && - (selectionPIDpTdependent(track2, 0) || selectionPIDpTdependent(track2, 1))) { - + if (ispTdepPID && !(selectionPIDpTdependent(track1, 0) || selectionPIDpTdependent(track1, 1))) { + continue; + } + if (ispTdepPID && !(selectionPIDpTdependent(track2, 0) || selectionPIDpTdependent(track2, 1))) { continue; } histos.fill(HIST("TPC_Nsigma1_MC"), track1.tpcNSigmaKa(), multiplicity, track1.pt()); histos.fill(HIST("TOF_Nsigma1_MC"), track1.tofNSigmaKa(), multiplicity, track1.pt()); histos.fill(HIST("trkDCAxy"), track1.dcaXY(), multiplicity, track1.pt()); histos.fill(HIST("trkDCAz"), track1.dcaZ(), multiplicity, track1.pt()); + std::array pvec0Rec = {track1.px(), track1.py(), track1.pz()}; + std::array pvec1Rec = {track2.px(), track2.py(), track2.pz()}; + std::array, 2> arrMomrec = {pvec0Rec, pvec1Rec}; + recMass = RecoDecay::m(arrMomrec, std::array{massKa, massKa}); + auto recPt = RecoDecay::pt(std::array{track1.px() + track2.px(), track1.py() + track2.py()}); + + // reconstructed-pair rapidity, cut with the same rapiditycut1/rapiditycut2 that fillinvMass() applies to data + auto recRapidity = RecoDecay::y(std::array{track1.px() + track2.px(), track1.py() + track2.py(), track1.pz() + track2.pz()}, recMass); + if (recRapidity < rapiditycut1 || recRapidity > rapiditycut2) { + continue; + } + if (avoidsplitrackMC && oldindex == mothertrack1.globalIndex()) { histos.fill(HIST("h1PhiRecsplit1"), mothertrack1.pt()); continue; } oldindex = mothertrack1.globalIndex(); - std::array pvec0 = {track1.px(), track1.py(), track1.pz()}; - std::array pvec1 = {track2.px(), track2.py(), track2.pz()}; - std::array, 2> arrMomrec = {pvec0, pvec1}; auto motherP = mothertrack1.p(); auto motherE = mothertrack1.e(); genMass = std::sqrt(motherE * motherE - motherP * motherP); - recMass = RecoDecay::m(arrMomrec, std::array{massKa, massKa}); - histos.fill(HIST("h1PhifinalRec"), mothertrack1.pt()); - histos.fill(HIST("h3PhifinalRec"), mothertrack1.pt(), multiplicity, recMass); + histos.fill(HIST("h1PhifinalRec"), recPt); + histos.fill(HIST("h3PhifinalRec"), recPt, multiplicity, recMass); histos.fill(HIST("h1Phifinalgenmass"), genMass); + histos.fill(HIST("h3PhiRecoResolution"), recPt, multiplicity, recMass - genMass); } } } } } - PROCESS_SWITCH(phianalysisrun3_PbPb, processRec1, "Process Reconstructed", false); + PROCESS_SWITCH(Phianalysisrun3pbpb, processRec1, "Process Reconstructed", false); void processEvtLossSigLossMC(McCollisionMults::iterator const& mcCollision, aod::McParticles const& mcParticles, const soa::SmallGroups& recCollisions) { @@ -1976,7 +1636,7 @@ struct phianalysisrun3_PbPb { return; } - if (isApplyTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + if (isApplyTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { return; } // Event loss estimation @@ -1987,8 +1647,9 @@ struct phianalysisrun3_PbPb { bool isSel = false; auto centrality = -999.; for (const auto& RecCollision : recCollisions) { - if (!myEventSelections(RecCollision)) + if (!myEventSelections(RecCollision)) { continue; + } const int kCentFT0C = 0; const int kCentFT0A = 1; const int kCentFT0M = 2; @@ -2021,12 +1682,14 @@ struct phianalysisrun3_PbPb { // Generated MC for (const auto& mcPart : mcParticles) { - if (mcPart.y() < rapiditycut1 || mcPart.y() > rapiditycut2) + if (mcPart.y() < rapiditycut1 || mcPart.y() > rapiditycut2) { continue; + } // select phi meson - if (std::abs(mcPart.pdgCode()) != o2::constants::physics::kPhi) + if (std::abs(mcPart.pdgCode()) != o2::constants::physics::kPhi) { continue; + } // signal loss estimation histos.fill(HIST("QAevent/phigenBeforeEvtSel"), mcPart.pt(), impactPar); @@ -2040,7 +1703,7 @@ struct phianalysisrun3_PbPb { } } // end loop on gen particles } - PROCESS_SWITCH(phianalysisrun3_PbPb, processEvtLossSigLossMC, "Process Signal Loss, Event Loss", false); + PROCESS_SWITCH(Phianalysisrun3pbpb, processEvtLossSigLossMC, "Process Signal Loss, Event Loss", false); void processEvtLossSigLossMC1(McCollisionMults::iterator const& mcCollision, soa::SmallGroups const& collisions, aod::McParticles const& GenParticles) { if (selectionConfig.cutvzgen && @@ -2051,7 +1714,7 @@ struct phianalysisrun3_PbPb { return; } - if (isApplyTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { + if (isApplyTVX && (mcCollision.multMCFT0C() <= 0 || mcCollision.multMCFT0A() <= 0)) { return; } // All generated events @@ -2068,8 +1731,9 @@ struct phianalysisrun3_PbPb { bool atLeastOne = false; auto centrality = -999.; for (auto const& collision : collisions) { - if (!myEventSelections(collision)) + if (!myEventSelections(collision)) { continue; + } centrality = selColCent(collision); atLeastOne = true; } @@ -2085,16 +1749,19 @@ struct phianalysisrun3_PbPb { } for (const auto& particle : GenParticles) { - if (particle.y() < rapiditycut1 || particle.y() > rapiditycut2) + if (particle.y() < rapiditycut1 || particle.y() > rapiditycut2) { continue; + } - if (particle.pdgCode() != o2::constants::physics::kPhi) + if (particle.pdgCode() != o2::constants::physics::kPhi) { continue; + } auto daughters = particle.daughters_as(); - static constexpr int kPhiNDaughters = 2; - if (daughters.size() != kPhiNDaughters) + static constexpr int PhiNDaughters = 2; + if (daughters.size() != PhiNDaughters) { continue; + } bool daup = false, daun = false; @@ -2108,8 +1775,9 @@ struct phianalysisrun3_PbPb { } } - if (!daup || !daun) + if (!daup || !daun) { continue; + } mother = d1 + d2; @@ -2126,10 +1794,10 @@ struct phianalysisrun3_PbPb { } } } - PROCESS_SWITCH(phianalysisrun3_PbPb, processEvtLossSigLossMC1, "Process Signal Loss, Event Loss", false); + PROCESS_SWITCH(Phianalysisrun3pbpb, processEvtLossSigLossMC1, "Process Signal Loss, Event Loss", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"phianalysisrun3_PbPb"})}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Resonances/phiflowder.cxx b/PWGLF/Tasks/Resonances/phiflowder.cxx new file mode 100644 index 00000000000..8a855a5c1c2 --- /dev/null +++ b/PWGLF/Tasks/Resonances/phiflowder.cxx @@ -0,0 +1,615 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiflowder.cxx +/// \brief Derived task for same-event and mixed-event Phi reconstruction for v1 +/// +/// \author Prottay Das + +#include "PWGLF/DataModel/LFPhiFlowTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct phiflowder { + + Configurable centMin{"centMin", 0.f, "Minimum centrality"}; + Configurable centMax{"centMax", 80.f, "Maximum centrality"}; + + Configurable useStoredPID{"useStoredPID", false, "Apply PID-bit selection using stored kaon PID mask"}; + Configurable pidChoice{"pidChoice", 2, "PID choice: 1, 2, 3, or 4"}; + Configurable pidExclusive{"pidExclusive", false, "Require exactly the selected PID bit"}; + + Configurable applyDeepAngle{"applyDeepAngle", false, "Apply minimum opening-angle cut for K+K- pairs"}; + Configurable deepAngleCut{"deepAngleCut", 0.04f, "Minimum K+K- opening angle"}; + + Configurable massMin{"massMin", 0.9f, "Minimum K+K- mass to fill"}; + Configurable massMax{"massMax", 1.2f, "Maximum K+K- mass to fill"}; + + ConfigurableAxis axisInvMass{"axisInvMass", {120, 0.9f, 1.2f}, "#it{M}_{K^{+}K^{-}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPhiPt{"axisPhiPt", {100, 0.f, 10.f}, "#it{p}_{T}^{K^{+}K^{-}} (GeV/#it{c})"}; + ConfigurableAxis axisCent{"axisCent", {80, 0.f, 80.f}, "Centrality (%)"}; + ConfigurableAxis axisV1{"axisV1", {1000, -1.0f, 1.0f}, "v1"}; + ConfigurableAxis axisEta{"axisEta", {8, -0.8f, 0.8f}, "Eta"}; + ConfigurableAxis axisNPairs{"axisNPairs", {500, 0.f, 5000.f}, "Number of K^{+}K^{-} pairs per event"}; + + Configurable nEvtMixing{"nEvtMixing", 5, "Number of events to mix"}; + ConfigurableAxis cfgVtxBins{"cfgVtxBins", {10, -10, 10}, "Mixing bins - z-vertex"}; + ConfigurableAxis cfgCentBins{"cfgCentBins", {8, 0.0, 80}, "Mixing bins - centrality"}; + /*Configurable ptMix{"ptMix", 0.2f, "ME: pT bin width"}; + Configurable etaMix{"etaMix", 0.2f, "ME: eta bin width"}; + Configurable phiMix{"phiMix", 0.3f, "ME: phi bin width"};*/ + ConfigurableAxis cfgSPAngleBins{"cfgSPAngleBins", {12, 0.0, 2.0 * TMath::Pi()}, "Mixing bins - odd spectator-plane angle"}; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + static constexpr uint8_t kPID1 = 1u << 0; + static constexpr uint8_t kPID2 = 1u << 1; + static constexpr uint8_t kPID3 = 1u << 2; + static constexpr uint8_t kPID4 = 1u << 3; + + SliceCache cache; + + void init(InitContext const&) + { + histos.add("hCentrality", "Centrality distribution", kTH1F, {axisCent}); + histos.add("hSparseSame", "Same-event K^{+}K^{-};M;pT;centrality", kTHnSparseF, {axisInvMass, axisPhiPt, axisCent, axisEta, axisV1}); + histos.add("hSparseMix", "Mixed-event K^{+}K^{-};M;pT;centrality", kTHnSparseF, {axisInvMass, axisPhiPt, axisCent, axisEta, axisV1}); + histos.add("hpQxytpvscent", "hpQxytpvscent", HistType::kTHnSparseF, {axisCent, axisV1}, true); + histos.add("hpQxpvscent", "hpQxpvscent", HistType::kTHnSparseF, {axisCent, axisV1}, true); + histos.add("hpQxtvscent", "hpQxtvscent", HistType::kTHnSparseF, {axisCent, axisV1}, true); + histos.add("hpQypvscent", "hpQypvscent", HistType::kTHnSparseF, {axisCent, axisV1}, true); + histos.add("hpQytvscent", "hpQytvscent", HistType::kTHnSparseF, {axisCent, axisV1}, true); + histos.add("hMixpairs", "hMixpairs", HistType::kTHnSparseF, {axisNPairs}, true); + histos.add("hSparseSameUx", "Same-event #Sigma u_{x};M;pT;centrality;eta", kTHnSparseF, {axisInvMass, axisPhiPt, axisCent, axisEta, axisV1}, true); + histos.add("hSparseSameUy", "Same-event #Sigma u_{y};M;pT;centrality;eta", kTHnSparseF, {axisInvMass, axisPhiPt, axisCent, axisEta, axisV1}, true); + histos.add("hSparseSameUQA", "Same-event #Sigma u#upoint Q_{A};M;pT;centrality;eta", kTHnSparseF, {axisInvMass, axisPhiPt, axisCent, axisEta, axisV1}, true); + histos.add("hSparseSameUQC", "Same-event #Sigma u#upoint Q_{C};M;pT;centrality;eta", kTHnSparseF, {axisInvMass, axisPhiPt, axisCent, axisEta, axisV1}, true); + } + + uint8_t getRequiredPidBit() const + { + switch (pidChoice.value) { + case 1: + return kPID1; + case 2: + return kPID2; + case 3: + return kPID3; + case 4: + return kPID4; + default: + LOGF(warn, "Invalid pidChoice=%d. PID2 will be used.", pidChoice.value); + return kPID2; + } + } + + template + bool passStoredPID(const T& kaon) const + { + if (!useStoredPID.value) { + return true; + } + + const uint16_t mask = kaon.kaonPidMask(); + const uint8_t requiredBit = getRequiredPidBit(); + + if (pidExclusive.value) { + return mask == requiredBit; + } + + return (mask & requiredBit) != 0; + } + + bool passDeepAngle(float px1, float py1, float pz1, float px2, float py2, float pz2) const + { + if (!applyDeepAngle.value) { + return true; + } + + const double p1 = std::sqrt(px1 * px1 + py1 * py1 + pz1 * pz1); + const double p2 = std::sqrt(px2 * px2 + py2 * py2 + pz2 * pz2); + + if (p1 <= 0.0 || p2 <= 0.0) { + return false; + } + + const double cosAngle = std::clamp((px1 * px2 + py1 * py2 + pz1 * pz2) / (p1 * p2), -1.0, 1.0); + const double angle = std::acos(cosAngle); + + return angle >= deepAngleCut.value; + } + + template + int fillMixedPairs(const TPosKaons& posGroup, + const TNegKaons& negGroup, + float centrality, float qxZDCA, float qxZDCC, float qyZDCA, float qyZDCC) + { + int nMixedPairs = 0; + + for (const auto& kPlus : posGroup) { + if (!passStoredPID(kPlus)) { + continue; + } + + ROOT::Math::PxPyPzMVector plusVec( + kPlus.px(), + kPlus.py(), + kPlus.pz(), + o2::constants::physics::MassKPlus); + + for (const auto& kMinus : negGroup) { + if (!passStoredPID(kMinus)) { + continue; + } + + if (!passDeepAngle(kPlus.px(), + kPlus.py(), + kPlus.pz(), + kMinus.px(), + kMinus.py(), + kMinus.pz())) { + continue; + } + + ROOT::Math::PxPyPzMVector minusVec( + kMinus.px(), + kMinus.py(), + kMinus.pz(), + o2::constants::physics::MassKPlus); + + const auto phiCandidate = plusVec + minusVec; + + const float phiMass = phiCandidate.M(); + const float phiPt = phiCandidate.Pt(); + float etaCand = phiCandidate.Eta(); + double phiCand = phiCandidate.Phi(); + if (phiCand < 0.0) { + phiCand += 2.0 * TMath::Pi(); + } + double const cosNPhi = std::cos(phiCand); + double const sinNPhi = std::sin(phiCand); + + auto ux = cosNPhi; // real part of candidate q vector + auto uy = sinNPhi; // imaginary part of candidate q vector + auto oddv1 = ux * (qxZDCA - qxZDCC) + uy * (qyZDCA - qyZDCC); + // auto evenv1 = ux * (qxZDCA + qxZDCC) + uy * (qyZDCA + qyZDCC); + + if (phiMass < massMin.value || phiMass > massMax.value) { + continue; + } + + histos.fill(HIST("hSparseMix"), phiMass, phiPt, centrality, etaCand, oddv1); + + nMixedPairs++; + } + } + + return nMixedPairs; + } + + Filter centralityFilter = (aod::kaonevent::cent >= centMin) && (aod::kaonevent::cent < centMax); + + using EventCandidates = soa::Filtered; + + Partition posKaons = aod::kaonpair::charge > int8_t{0}; + Partition negKaons = aod::kaonpair::charge < int8_t{0}; + + void processSameData(EventCandidates::iterator const& collision, aod::KaonTracks const& /*kaontracks*/) + { + const float centrality = collision.cent(); + auto qxZDCA = collision.qxA(); + auto qyZDCA = collision.qyA(); + auto qxZDCC = collision.qxC(); + auto qyZDCC = collision.qyC(); + + auto qxtQxp = qxZDCC * qxZDCA; + auto qytQyp = qyZDCC * qyZDCA; + auto qxytp = qxtQxp + qytQyp; + // auto qxpQyt = qxZDCA * qyZDCC; + // auto qxtQyp = qxZDCC * qyZDCA; + + histos.fill(HIST("hCentrality"), centrality); + histos.fill(HIST("hpQxytpvscent"), centrality, qxytp); + histos.fill(HIST("hpQxpvscent"), centrality, qxZDCA); + histos.fill(HIST("hpQxtvscent"), centrality, qxZDCC); + histos.fill(HIST("hpQypvscent"), centrality, qyZDCA); + histos.fill(HIST("hpQytvscent"), centrality, qyZDCC); + + auto posThisColl = posKaons->sliceByCached(aod::kaonpair::kaoneventId, collision.globalIndex(), cache); + auto negThisColl = negKaons->sliceByCached(aod::kaonpair::kaoneventId, collision.globalIndex(), cache); + /* + int nPlus = 0; + int nMinus = 0; + int nPhiSame = 0; + + for (const auto& kPlus : posThisColl) { + if (passStoredPID(kPlus)) { + nPlus++; + } + } + + for (const auto& kMinus : negThisColl) { + if (passStoredPID(kMinus)) { + nMinus++; + } + } + + histos.fill(HIST("hNplusPerEvent"), nPlus); + histos.fill(HIST("hNminusPerEvent"), nMinus); + */ + for (const auto& kPlus : posThisColl) { + if (!passStoredPID(kPlus)) { + continue; + } + + ROOT::Math::PxPyPzMVector plusVec(kPlus.px(), kPlus.py(), kPlus.pz(), o2::constants::physics::MassKPlus); + + for (const auto& kMinus : negThisColl) { + if (!passStoredPID(kMinus)) { + continue; + } + + if (!passDeepAngle(kPlus.px(), kPlus.py(), kPlus.pz(), kMinus.px(), kMinus.py(), kMinus.pz())) { + continue; + } + + ROOT::Math::PxPyPzMVector minusVec(kMinus.px(), kMinus.py(), kMinus.pz(), o2::constants::physics::MassKPlus); + + const auto phiCandidate = plusVec + minusVec; + + const float phiMass = phiCandidate.M(); + const float phiPt = phiCandidate.Pt(); + const float etaCand = phiCandidate.Eta(); + double phiCand = phiCandidate.Phi(); + if (phiCand < 0.0) { + phiCand += 2.0 * TMath::Pi(); + } + double const cosNPhi = std::cos(phiCand); + double const sinNPhi = std::sin(phiCand); + + auto ux = cosNPhi; // real part of candidate q vector + auto uy = sinNPhi; // imaginary part of candidate q vector + auto oddv1 = ux * (qxZDCA - qxZDCC) + uy * (qyZDCA - qyZDCC); + // auto evenv1 = ux * (qxZDCA + qxZDCC) + uy * (qyZDCA + qyZDCC); + auto uQA = ux * qxZDCA + uy * qyZDCA; + auto uQC = ux * qxZDCC + uy * qyZDCC; + + if (phiMass < massMin || phiMass > massMax) { + continue; + } + + histos.fill(HIST("hSparseSame"), phiMass, phiPt, centrality, etaCand, oddv1); + histos.fill(HIST("hSparseSameUx"), phiMass, phiPt, centrality, etaCand, ux); + histos.fill(HIST("hSparseSameUy"), phiMass, phiPt, centrality, etaCand, uy); + histos.fill(HIST("hSparseSameUQA"), phiMass, phiPt, centrality, etaCand, uQA); + histos.fill(HIST("hSparseSameUQC"), phiMass, phiPt, centrality, etaCand, uQC); + // nPhiSame++; + } + } + } + + PROCESS_SWITCH(phiflowder, processSameData, "Process same-event K+K- pairs", true); + + void processLikeSign(EventCandidates::iterator const& collision, aod::KaonTracks const& /*kaontracks*/) + { + const float centrality = collision.cent(); + + auto posThisColl = posKaons->sliceByCached(aod::kaonpair::kaoneventId, collision.globalIndex(), cache); + auto negThisColl = negKaons->sliceByCached(aod::kaonpair::kaoneventId, collision.globalIndex(), cache); + /* + int nPlus = 0; + int nMinus = 0; + int nPhiLike = 0; + + for (const auto& kPlus : posThisColl) { + if (passStoredPID(kPlus)) { + nPlus++; + } + } + + for (const auto& kMinus : negThisColl) { + if (passStoredPID(kMinus)) { + nMinus++; + } + } + + histos.fill(HIST("hNplusLikePerEvent"), nPlus); + histos.fill(HIST("hNminusLikePerEvent"), nMinus); + */ + + // K+K+ pairs + for (const auto& kPlus1 : posThisColl) { + if (!passStoredPID(kPlus1)) { + continue; + } + + ROOT::Math::PxPyPzMVector plusVec1(kPlus1.px(), kPlus1.py(), kPlus1.pz(), o2::constants::physics::MassKPlus); + + for (const auto& kPlus2 : posThisColl) { + if (!passStoredPID(kPlus2)) { + continue; + } + + // Reject self-pairs and count every unique K+K+ pair only once. + if (kPlus2.kaonIndex() <= kPlus1.kaonIndex()) { + continue; + } + + if (!passDeepAngle(kPlus1.px(), kPlus1.py(), kPlus1.pz(), kPlus2.px(), kPlus2.py(), kPlus2.pz())) { + continue; + } + + ROOT::Math::PxPyPzMVector plusVec2(kPlus2.px(), kPlus2.py(), kPlus2.pz(), o2::constants::physics::MassKPlus); + + const auto likeCandidate = plusVec1 + plusVec2; + const float likeMass = likeCandidate.M(); + const float likePt = likeCandidate.Pt(); + + if (likeMass < massMin.value || likeMass > massMax.value) { + continue; + } + histos.fill(HIST("hSparseLike"), likeMass, likePt, centrality); + // nPhiLike++; + } + } + + // K-K- pairs + for (const auto& kMinus1 : negThisColl) { + if (!passStoredPID(kMinus1)) { + continue; + } + + ROOT::Math::PxPyPzMVector minusVec1(kMinus1.px(), kMinus1.py(), kMinus1.pz(), o2::constants::physics::MassKPlus); + + for (const auto& kMinus2 : negThisColl) { + if (!passStoredPID(kMinus2)) { + continue; + } + + // Reject self-pairs and count every unique K-K- pair only once. + if (kMinus2.kaonIndex() <= kMinus1.kaonIndex()) { + continue; + } + + if (!passDeepAngle(kMinus1.px(), kMinus1.py(), kMinus1.pz(), kMinus2.px(), kMinus2.py(), kMinus2.pz())) { + continue; + } + + ROOT::Math::PxPyPzMVector minusVec2(kMinus2.px(), kMinus2.py(), kMinus2.pz(), o2::constants::physics::MassKPlus); + + const auto likeCandidate = minusVec1 + minusVec2; + const float likeMass = likeCandidate.M(); + const float likePt = likeCandidate.Pt(); + + if (likeMass < massMin.value || likeMass > massMax.value) { + continue; + } + + histos.fill(HIST("hSparseLike"), likeMass, likePt, centrality); + // nPhiLike++; + } + } + } + + PROCESS_SWITCH(phiflowder, processLikeSign, "Process like-sign K+K+ and K-K- pairs", true); + + // Processing Event Mixing + using BinningType = ColumnBinningPolicy; + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; + + void processMixedData(EventCandidates const& collisions, + aod::KaonTracks const& /*kaontracks*/) + { + BinningType colBinning{{cfgVtxBins, cfgCentBins}}; + for (const auto& [collision1, collision2] : selfCombinations(colBinning, nEvtMixing.value, -1, collisions, collisions)) { + + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } + + const float centrality1 = collision1.cent(); + const float centrality2 = collision2.cent(); + + auto qxZDCA = collision1.qxA(); + auto qyZDCA = collision1.qyA(); + auto qxZDCC = collision1.qxC(); + auto qyZDCC = collision1.qyC(); + + // K+ from event 1 and K- from event 2 + auto posGroup1 = posKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision1.globalIndex(), + cache); + + auto negGroup2 = negKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision2.globalIndex(), + cache); + + // K+ from event 2 and K- from event 1 + auto posGroup2 = posKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision2.globalIndex(), + cache); + + auto negGroup1 = negKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision1.globalIndex(), + cache); + + int nMixedPairs = 0; + + nMixedPairs += fillMixedPairs(posGroup1, + negGroup2, + centrality1, qxZDCA, qxZDCC, qyZDCA, qyZDCC); + + nMixedPairs += fillMixedPairs(posGroup2, + negGroup1, + centrality2, qxZDCA, qxZDCC, qyZDCA, qyZDCC); + + histos.fill(HIST("hMixpairs"), nMixedPairs); + } + } + + PROCESS_SWITCH(phiflowder, processMixedData, "Process mixed-event K+K- pairs", true); + + // Processing Event Mixing + void processMixedData2(EventCandidates const& collisions, + aod::KaonTracks const& /*kaontracks*/) + { + // Calculate the odd spectator-plane angle directly + // from the Q vectors already stored in KaonEvents. + auto getSPAngle = + [](EventCandidates::iterator const& collision) -> float { + const float qxOdd = + collision.qxA() - collision.qxC(); + + const float qyOdd = + collision.qyA() - collision.qyC(); + + float psiSP = + std::atan2(qyOdd, qxOdd); + + // Put first-harmonic angle in [0, 2pi). + if (psiSP < 0.f) { + psiSP += 2.f * TMath::Pi(); + } + + return psiSP; + }; + + using BinningTypeSP = + FlexibleBinningPolicy< + std::tuple, + aod::kaonevent::Posz, + aod::kaonevent::Cent, + decltype(getSPAngle)>; + + BinningTypeSP binningOnSPAngle{ + {getSPAngle}, + {cfgVtxBins, + cfgCentBins, + cfgSPAngleBins}}; + + for (const auto& [collision1, collision2] : + selfCombinations(binningOnSPAngle, + nEvtMixing.value, + -1, + collisions, + collisions)) { + + if (collision1.globalIndex() == + collision2.globalIndex()) { + continue; + } + + const float centrality = + collision1.cent(); + + const float qxZDCA = + collision1.qxA(); + + const float qyZDCA = + collision1.qyA(); + + const float qxZDCC = + collision1.qxC(); + + const float qyZDCC = + collision1.qyC(); + + // K+ from event 1 and K- from event 2. + auto posGroup1 = + posKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision1.globalIndex(), + cache); + + auto negGroup2 = + negKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision2.globalIndex(), + cache); + + // K+ from event 2 and K- from event 1. + auto posGroup2 = + posKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision2.globalIndex(), + cache); + + auto negGroup1 = + negKaons->sliceByCached( + aod::kaonpair::kaoneventId, + collision1.globalIndex(), + cache); + + int nMixedPairs = 0; + + // Both combinations use centrality and Q vectors + // from collision1. + nMixedPairs += + fillMixedPairs(posGroup1, + negGroup2, + centrality, + qxZDCA, + qxZDCC, + qyZDCA, + qyZDCC); + + nMixedPairs += + fillMixedPairs(posGroup2, + negGroup1, + centrality, + qxZDCA, + qxZDCC, + qyZDCA, + qyZDCC); + + histos.fill(HIST("hMixpairs"), + nMixedPairs); + } + } + + PROCESS_SWITCH(phiflowder, processMixedData2, "Process mixed-event K+K- pairs in SP-angle bins", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phipbpb.cxx b/PWGLF/Tasks/Resonances/phipbpb.cxx index 21e731f8964..e02230387f5 100644 --- a/PWGLF/Tasks/Resonances/phipbpb.cxx +++ b/PWGLF/Tasks/Resonances/phipbpb.cxx @@ -635,7 +635,7 @@ struct phipbpb { int Npostrack = 0; float weight1 = 1.0; float weight2 = 1.0; - for (auto track1 : posThisColl) { + for (const auto& track1 : posThisColl) { // track selection if (!selectionTrack(track1)) { continue; @@ -673,7 +673,7 @@ struct phipbpb { weight1 = 1; } } - for (auto track2 : negThisColl) { + for (const auto& track2 : negThisColl) { // track selection if (!selectionTrack(track2)) { continue; @@ -862,7 +862,7 @@ struct phipbpb { } int Npostrack = 0; - for (auto track1 : posThisColl) { + for (const auto& track1 : posThisColl) { // track selection if (!selectionTrack(track1)) { continue; @@ -881,7 +881,7 @@ struct phipbpb { continue; } auto track1ID = track1.globalIndex(); - for (auto track2 : negThisColl) { + for (const auto& track2 : negThisColl) { // track selection if (!selectionTrack(track2)) { continue; @@ -941,8 +941,8 @@ struct phipbpb { } PROCESS_SWITCH(phipbpb, processSameEventv1, "Process Same event for v1", false); - BinningTypeVertexContributor binningOnEPAngle{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; - BinningTypeVertexContributorv1 binningOnSPAngle{{axisVertex, axisMultiplicityClass, axisSPAngle}, true}; + BinningTypeVertexContributor binningOnEPAngle{{axisVertex, axisMultiplicityClass, axisEPAngle}}; + BinningTypeVertexContributorv1 binningOnSPAngle{{axisVertex, axisMultiplicityClass, axisSPAngle}}; Preslice tracksPerCollision = aod::track::collisionId; void processMEAcc(EventCandidates const& collisions, TrackCandidates const& tracks) { @@ -1188,7 +1188,7 @@ struct phipbpb { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass, axisEPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { // if (!collision1.sel8() || !collision1.triggereventep() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision1.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { @@ -1310,7 +1310,7 @@ struct phipbpb { void processMixedEventOptiv1(EventCandidatesv1 const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributorv1 binningOnPositions{{axisVertex, axisMultiplicityClass, axisSPAngle}, true}; + BinningTypeVertexContributorv1 binningOnPositions{{axisVertex, axisMultiplicityClass, axisSPAngle}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!collision1.sel8() || !collision1.triggereventsp() || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup)) { @@ -1465,7 +1465,7 @@ struct phipbpb { auto oldindex = -999; auto Rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle - for (auto track1 : Rectrackspart) { + for (const auto& track1 : Rectrackspart) { if (!selectionTrack(track1)) { continue; } @@ -1479,7 +1479,7 @@ struct phipbpb { continue; } auto track1ID = track1.index(); - for (auto track2 : Rectrackspart) { + for (const auto& track2 : Rectrackspart) { auto track2ID = track2.index(); if (track2ID <= track1ID) { continue; @@ -1576,7 +1576,7 @@ struct phipbpb { } auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } @@ -1760,7 +1760,7 @@ struct phipbpb { auto oldindex = -999; auto Rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle - for (auto track1 : Rectrackspart) { + for (const auto& track1 : Rectrackspart) { if (!track1.has_mcParticle()) { continue; } @@ -1775,7 +1775,7 @@ struct phipbpb { histos.fill(HIST("hSparsePhiMCRecKaonMissMatchWeight"), centclass, GetPhiInRange(mctrack1.phi() - psiFT0C), TMath::Power(TMath::Cos(4.0 * GetPhiInRange(mctrack1.phi() - psiFT0C)), 1.0), mctrack1.pt(), mctrack1.eta()); } auto track1ID = track1.index(); - for (auto track2 : Rectrackspart) { + for (const auto& track2 : Rectrackspart) { if (!track2.has_mcParticle()) { continue; } @@ -1854,7 +1854,7 @@ struct phipbpb { } auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } diff --git a/PWGLF/Tasks/Resonances/phiphimcinjected.cxx b/PWGLF/Tasks/Resonances/phiphimcinjected.cxx new file mode 100644 index 00000000000..b5791fac21b --- /dev/null +++ b/PWGLF/Tasks/Resonances/phiphimcinjected.cxx @@ -0,0 +1,508 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiphimcinjected.cxx +/// \brief Code for injected doublephi resonance MC +/// \author Sawan + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include // std::abs +#include // size_t / std::size_t +#include + +using namespace o2; +using namespace o2::framework; + +struct PhiCandidateInjected { + ROOT::Math::PxPyPzMVector p4; + int t1Index = -1; + int t2Index = -1; + + // MC Truth attributes + bool isValidMcPhi = false; + bool isFrom999999 = false; + int mc999999Index = -1; +}; + +struct PhiPhiMCInjected { + + // Configurables + struct : ConfigurableGroup { + Configurable isSel8{"isSel8", true, "Event selection sel8"}; + Configurable cutDCAxy{"cutDCAxy", 0.1f, "DCAxy max cut"}; + Configurable cutDCAz{"cutDCAz", 0.1f, "DCAz max cut"}; + Configurable cutNsigmaTPC{"cutNsigmaTPC", 3.0f, "TPC Nsigma cut for Kaon"}; + Configurable isApplyPIDSelection{"isApplyPIDSelection", true, "Apply PID selection for Kaons"}; + Configurable trackEtaCut{"trackEtaCut", 0.8f, "Track Eta Cut"}; + Configurable phiMassCutLow{"phiMassCutLow", 0.9f, "Lower Cut for Phi Mass"}; + Configurable phiMassCutHigh{"phiMassCutHigh", 1.1f, "Upper Cut for Phi Mass"}; + + } config; + + const int injectedMotherPDG = 999999; + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + double normFactor = 1.0; + + ConfigurableAxis phiMassAxis{"phiMassAxis", {200, 0.98, 1.22}, "Phi Mass Axis"}; + ConfigurableAxis phiPtAxis{"phiPtAxis", {200, 0.0, 100.0}, "Phi Pt Axis"}; + ConfigurableAxis phiPhiMassAxis{"phiPhiMassAxis", {200, 2.0, 4.0}, "Phi-Phi Mass Axis"}; + ConfigurableAxis phiPhiPtAxis{"phiPhiPtAxis", {200, 0.0, 50.0}, "Phi-Phi Pt Axis"}; + + enum RecoSelectionStage { + kLoop1Track = 0, + kLoop1DCA, + kLoop1PID, + kLoop1MCLabel, + kLoop1Eta, + kLoop2Track, + kLoop2Index, + kLoop2Eta, + kLoop2DCA, + kLoop2PID, + kLoop2MCLabel, + kLoop2Charge, + kLoop2KaonDaughters, + kCommonMotherLoop, + kCommonMother, + kPhiMother, + kPhiMassCut, + kInjectedMother, + kLoop2PhiPairs, + kUniqueTracks, + kValidPhiPhiPair + }; + + void init(InitContext const&) + { + + // Event histogram + registry.add("h1_TrackCount", "Track Count;Track Count;Counts", kTH1F, {{25, -0.5, 24.5}}); + registry.add("h1_EventCount", "Event Count;Event Count;Counts", kTH1F, {{5, -0.5, 4.5}}); + // Generated MC Histograms + registry.add("Generated/h2_InvMassPhiPhi", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h2_InvMassPhiPhi2", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h1_PhiPhi_Phi", "Gen Phi-Phi: #phi Angle;#phi;Counts", kTH1F, {{100, -o2::constants::math::PI, o2::constants::math::PI}}); + registry.add("Generated/h1_PhiPhi_Eta", "Gen Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiPhi_Y", "Gen Phi-Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Generated/h2_InvMassPhiFromT4s", "Daughter phi from T4s decay: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiMassAxis}, {phiPtAxis}}); + registry.add("Generated/h2_InvMassPhiMinBias", "phi from minimum bias: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiMassAxis}, {phiPtAxis}}); + + // Reconstructed MC Histograms - 2D Mass vs pT of Mother Phi-Phi + registry.add("Reconstructed/AllPhiPairs/h2_PhiPhi_All", "Rec Phi-Phi All Pairs: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/AllPhiPairs/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/AllPhiPairs/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/PhiFromT4s/h2_PhiPhi_T4s", "Rec Phi-Phi Mother 999999: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/PhiFromT4s/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/PhiFromT4s/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/MinimumBiasPhi/h2_PhiPhi_MinBias", "Rec Phi-Phi Minimum Bias: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Pt", "Rec Phi-Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Eta", "Rec Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/MinimumBiasPhi/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + } + + // PID Selection function + bool checkPID(float ptcand, float nsigmaTPC, float nsigmaTOF, bool TOFHit) + { + constexpr float RadiusMax = 2.5f * 2.5f; + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + if (ptcand < 0.5f) { + return std::abs(nsigmaTPC) < config.cutNsigmaTPC; + } else { + return nsigmaTPC > -2.0f && nsigmaTPC < config.cutNsigmaTPC; + } + } else { + const float radius2 = nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF; + if (radius2 >= RadiusMax) { + return false; + } + if (ptcand < 2.0f) { + return true; + } + + float slope = 0.0f; + float intercept = 0.0f; + + if (ptcand < 2.5f) { + slope = 1.29f; + intercept = 3.34f; + } else if (ptcand < 3.0f) { + slope = 0.94f; + intercept = 2.45f; + } else if (ptcand < 4.0f) { + slope = 0.62f; + intercept = 1.55f; + } else if (ptcand < 5.0f) { + slope = 0.32f; + intercept = 1.45f; + } else if (ptcand < 7.0f) { + slope = 0.22f; + intercept = 1.35f; + } else { + slope = 0.10f; + intercept = 1.25f; + } + + return nsigmaTPC < slope * nsigmaTOF + intercept; + } + } + + // Define data table bindings + using EventCandidatesMC = soa::Join; + using TrackCandidatesMC = soa::Join; + + ROOT::Math::PxPyPzEVector vecGen, vecDau1, vecDau2; + ROOT::Math::PxPyPzMVector daughter1Gen, daughter2Gen, motherGen, daughter1Rec, daughter2Rec, motherRec; + + // Process Generated MC + void processGen(aod::McCollision const&, aod::McParticles const& mcParticles) + { + for (const auto& mcParticle : mcParticles) { + if (mcParticle.pdgCode() == injectedMotherPDG) { + + auto phiPhiDaughters = mcParticle.daughters_as(); + if (phiPhiDaughters.size() != 2) { + continue; + } + + int phiCount = 0; + for (const auto& kCurrentDaughter : phiPhiDaughters) { + + if (kCurrentDaughter.pdgCode() != o2::constants::physics::kPhi) { + continue; + } + + if (phiCount == 0) { + vecDau1 = ROOT::Math::PxPyPzEVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), kCurrentDaughter.e()); // Phi meson mass + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassPhi); // Phi meson mass + phiCount++; + registry.fill(HIST("Generated/h2_InvMassPhiFromT4s"), vecDau1.M(), daughter1Gen.Pt()); + } else if (phiCount == 1) { + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassPhi); // Phi meson mass + phiCount++; + } + } + if (phiCount != 2) { + continue; + } + motherGen = daughter1Gen + daughter2Gen; + vecGen = ROOT::Math::PxPyPzEVector(motherGen.Px(), motherGen.Py(), motherGen.Pz(), motherGen.E()); + + registry.fill(HIST("Generated/h2_InvMassPhiPhi"), motherGen.M(), motherGen.Pt()); + registry.fill(HIST("Generated/h2_InvMassPhiPhi2"), vecGen.M(), mcParticle.pt()); + + registry.fill(HIST("Generated/h1_PhiPhi_Phi"), vecGen.Phi()); + registry.fill(HIST("Generated/h1_PhiPhi_Eta"), vecGen.Eta()); + registry.fill(HIST("Generated/h1_PhiPhi_Y"), vecGen.Rapidity()); + } + + if (mcParticle.pdgCode() == o2::constants::physics::kPhi) { + + auto phiDaughters = mcParticle.daughters_as(); + if (phiDaughters.size() != 2) { + continue; + } + + bool passPosKaon = false; + bool passNegKaon = false; + for (const auto& kCurrentDaughter : phiDaughters) { + + if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { + passPosKaon = true; + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { + passNegKaon = true; + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + } + } + + // Ensure that the mother is not 999999 + auto mothers = mcParticle.mothers_as(); + bool isFrom999999 = false; + for (const auto& mother : mothers) { + if (mother.pdgCode() == injectedMotherPDG) { + isFrom999999 = true; + break; + } + } + + if (passPosKaon && passNegKaon && !isFrom999999) { + motherGen = daughter1Gen + daughter2Gen; + vecGen = ROOT::Math::PxPyPzEVector(motherGen.px(), motherGen.py(), motherGen.pz(), motherGen.E()); + + registry.fill(HIST("Generated/h2_InvMassPhiMinBias"), vecGen.M(), mcParticle.pt()); + } + } + } + } + PROCESS_SWITCH(PhiPhiMCInjected, processGen, "Process Generated MC", true); + + void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) + { + + registry.fill(HIST("h1_EventCount"), 0.0); + + if (collision.sel8()) { + registry.fill(HIST("h1_EventCount"), 1.0); + } + + if (!collision.sel8() && config.isSel8) { + return; + } + + std::vector phiCandidates; + + // STEP 1: Reconstruct individual Phi candidates (Kaon track pairs) + for (const auto& t1 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop1Track); + + if (std::abs(t1.dcaXY()) > config.cutDCAxy || std::abs(t1.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1DCA); + + if (config.isApplyPIDSelection && !checkPID(t1.pt(), t1.tpcNSigmaKa(), t1.tofNSigmaKa(), t1.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1PID); + + if (!t1.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1MCLabel); + + if (std::abs(t1.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1Eta); + + for (const auto& t2 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop2Track); + + if (t2.index() <= t1.index()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Index); + + if (std::abs(t2.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Eta); + + if (std::abs(t2.dcaXY()) > config.cutDCAxy || std::abs(t2.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2DCA); + + if (config.isApplyPIDSelection && !checkPID(t2.pt(), t2.tpcNSigmaKa(), t2.tofNSigmaKa(), t2.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2PID); + + if (!t2.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2MCLabel); + + if (t1.sign() * t2.sign() > 0) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Charge); + + auto mcKaTrk1 = t1.mcParticle(); + auto mcKaTrk2 = t2.mcParticle(); + + if (std::abs(mcKaTrk1.pdgCode()) != PDG_t::kKPlus || + std::abs(mcKaTrk2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2KaonDaughters); + + bool foundMother = false; + + // Search for common mother + for (const auto& KaMotherPhi1 : mcKaTrk1.template mothers_as()) { + + for (const auto& KaMotherPhi2 : mcKaTrk2.template mothers_as()) { + + registry.fill(HIST("h1_TrackCount"), kCommonMotherLoop); + + if (KaMotherPhi1.globalIndex() != KaMotherPhi2.globalIndex()) + continue; + registry.fill(HIST("h1_TrackCount"), kCommonMother); + + int motherPdg = KaMotherPhi1.pdgCode(); + + if (motherPdg != o2::constants::physics::kPhi) + continue; + registry.fill(HIST("h1_TrackCount"), kPhiMother); + + daughter1Rec = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKPlus); + daughter2Rec = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), o2::constants::physics::MassKPlus); + motherRec = daughter1Rec + daughter2Rec; + + // Phi invariant mass window cut (0.9 - 1.1 GeV/c^2) + if (motherRec.M() < config.phiMassCutLow || motherRec.M() > config.phiMassCutHigh) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kPhiMassCut); + + PhiCandidateInjected phiCand; + + phiCand.p4 = motherRec; + phiCand.t1Index = t1.globalIndex(); + phiCand.t2Index = t2.globalIndex(); + phiCand.isValidMcPhi = true; + + for (const auto& gm : KaMotherPhi1.template mothers_as()) { + + if (gm.pdgCode() == injectedMotherPDG) { + phiCand.isFrom999999 = true; + phiCand.mc999999Index = gm.globalIndex(); + registry.fill(HIST("h1_TrackCount"), kInjectedMother); + } + } + + phiCandidates.push_back(phiCand); + foundMother = true; + break; + } + if (foundMother) { + break; + } + } + } + } + + // Fill Single Reconstructed Phi Histograms (Loop ONCE per candidate) + for (const auto& phi : phiCandidates) { + // All Reconstructed Phi Candidates + if (phi.isValidMcPhi) { + + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_Phi1_Y"), phi.p4.Rapidity()); + } + + // Phis from Mother 999999 + if (phi.isFrom999999 && phi.isValidMcPhi) { + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_Phi1_Y"), phi.p4.Rapidity()); + } + + // Minimum Bias Phis (not from 999999) + if (!phi.isFrom999999 && phi.isValidMcPhi) { + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_Phi1_Y"), phi.p4.Rapidity()); + } + } + + // Reconstruct Phi-Phi Mother Candidates (Double Loop for Pairs Only) + for (size_t i = 0; i < phiCandidates.size(); ++i) { + const auto& phi1 = phiCandidates[i]; + + for (size_t j = i + 1; j < phiCandidates.size(); ++j) { + const auto& phi2 = phiCandidates[j]; + + registry.fill(HIST("h1_TrackCount"), kLoop2PhiPairs); + + // Ensure all 4 kaon tracks are unique + if (phi1.t1Index == phi2.t1Index || phi1.t1Index == phi2.t2Index || + phi1.t2Index == phi2.t1Index || phi1.t2Index == phi2.t2Index) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kUniqueTracks); + + if (!phi1.isValidMcPhi || !phi2.isValidMcPhi) { + continue; + } + + registry.fill(HIST("h1_TrackCount"), kValidPhiPhiPair); + + // Combined 4-momentum of the Mother Phi-Phi System + ROOT::Math::PxPyPzMVector phiPhiMother = phi1.p4 + phi2.p4; + + // All available Phi-Phi pairs + registry.fill(HIST("Reconstructed/AllPhiPairs/h2_PhiPhi_All"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/AllPhiPairs/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + + // Phi-Phi pairs from same Mother 999999 + if (phi1.isFrom999999 && phi2.isFrom999999 && (phi1.mc999999Index == phi2.mc999999Index)) { + registry.fill(HIST("Reconstructed/PhiFromT4s/h2_PhiPhi_T4s"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/PhiFromT4s/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + + if (!phi1.isFrom999999 && !phi2.isFrom999999) { + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h2_PhiPhi_MinBias"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Pt"), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Eta"), phiPhiMother.Eta()); + registry.fill(HIST("Reconstructed/MinimumBiasPhi/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + } + } + } + PROCESS_SWITCH(PhiPhiMCInjected, processRec, "Process Reconstructed MC", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phiphimctrigger.cxx b/PWGLF/Tasks/Resonances/phiphimctrigger.cxx new file mode 100644 index 00000000000..52704c1537c --- /dev/null +++ b/PWGLF/Tasks/Resonances/phiphimctrigger.cxx @@ -0,0 +1,434 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file phiphimctrigger.cxx +/// \brief Code for triggered doublephi resonance MC +/// \author Sawan + +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include // std::abs +#include // size_t / std::size_t +#include + +using namespace o2; +using namespace o2::framework; + +struct PhiCandidateTrigger { + ROOT::Math::PxPyPzMVector p4; + int t1Index = -1; + int t2Index = -1; + int mcPhiIndex = -1; + + // MC Truth attributes + bool isValidMcPhi = false; +}; + +struct PhiCandidateGen { + ROOT::Math::PxPyPzMVector p4; +}; + +struct PhiPhiMCTrigger { + + // Configurables + struct : ConfigurableGroup { + Configurable isSel8{"isSel8", true, "Event selection sel8"}; + Configurable cutDCAxy{"cutDCAxy", 0.1f, "DCAxy max cut"}; + Configurable cutDCAz{"cutDCAz", 0.1f, "DCAz max cut"}; + Configurable cutNsigmaTPC{"cutNsigmaTPC", 3.0f, "TPC Nsigma cut for Kaon"}; + Configurable isApplyPIDSelection{"isApplyPIDSelection", true, "Apply PID selection for Kaons"}; + Configurable trackEtaCut{"trackEtaCut", 0.8f, "Track Eta Cut"}; + Configurable phiMassCutLow{"phiMassCutLow", 0.9f, "Lower Cut for Phi Mass"}; + Configurable phiMassCutHigh{"phiMassCutHigh", 1.1f, "Upper Cut for Phi Mass"}; + } config; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; + ConfigurableAxis phiMassAxis{"phiMassAxis", {200, 0.98, 1.22}, "Phi Mass Axis"}; + ConfigurableAxis phiPtAxis{"phiPtAxis", {200, 0.0, 100.0}, "Phi Pt Axis"}; + ConfigurableAxis phiPhiMassAxis{"phiPhiMassAxis", {200, 2.0, 4.0}, "Phi-Phi Mass Axis"}; + ConfigurableAxis phiPhiPtAxis{"phiPhiPtAxis", {200, 0.0, 50.0}, "Phi-Phi Pt Axis"}; + + enum RecoSelectionStage { + kLoop1Track = 0, + kLoop1DCA, + kLoop1PID, + kLoop1MCLabel, + kLoop1Eta, + kLoop2Track, + kLoop2Index, + kLoop2Eta, + kLoop2DCA, + kLoop2PID, + kLoop2MCLabel, + kLoop2Charge, + kLoop2TrackSelected, + kLoop2KaonDaughters, + kCommonMotherLoop, + kCommonMother, + kPhiMother, + kPhiMassCut, + kLoop2PhiPairs, + kUniqueTracks, + kDifferentPhiMothers, + kValidPhiPhiPair + }; + + void init(InitContext const&) + { + + // Event histogram + registry.add("h1_TrackCount", "Track Count;Track Count;Counts", kTH1F, {{30, -0.5, 29.5}}); + registry.add("h1_EventCount", "Event Count;Event Count;Counts", kTH1F, {{15, -0.5, 14.5}}); + // Generated MC Histograms + registry.add("Generated/h1_GenEventAndParticleCount", "Gen Level Summary;Category;Counts", kTH1F, {{4, -0.5, 3.5}}); + registry.add("Generated/h2_InvMassPhiPhi", "Gen Phi-Phi: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Generated/h1_PhiPhi_Phi", "Gen Phi-Phi: #phi Angle;#phi;Counts", kTH1F, {{100, -o2::constants::math::PI, o2::constants::math::PI}}); + registry.add("Generated/h1_PhiPhi_Eta", "Gen Phi-Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiPhi_Y", "Gen Phi-Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_InvMassPhi", "Gen Phi: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Generated/h1_PhiPt", "Gen Phi: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Generated/h1_PhiEta", "Gen Phi: #eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Generated/h1_PhiY", "Gen Phi: Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + + // Reconstructed MC Histograms - 2D Mass vs pT of Mother Phi-Phi + registry.add("Reconstructed/h2_PhiPhi_InvMass", "Rec Phi-Phi All Pairs: Mass vs pT;Mass (GeV/#it{c}^{2});p_{T} (GeV/#it{c})", kTHnSparseF, {{phiPhiMassAxis}, {phiPhiPtAxis}}); + registry.add("Reconstructed/h1_Phi1_Mass", "Rec Phi Mass: Mass;Mass (GeV/#it{c}^{2});Counts", kTH1F, {{phiMassAxis}}); + registry.add("Reconstructed/h1_Phi1_Pt", "Rec Phi pT: pT;p_{T} (GeV/#it{c});Counts", kTH1F, {{phiPtAxis}}); + registry.add("Reconstructed/h1_Phi1_Eta", "Rec Phi Eta: Eta;#eta;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/h1_Phi1_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + registry.add("Reconstructed/h1_PhiPhi_Y", "Rec Phi Rapidity y;y;Counts", kTH1F, {{100, -1.0, 1.0}}); + } + + // PID Selection function + bool checkPID(float ptcand, float nsigmaTPC, float nsigmaTOF, bool TOFHit) + { + constexpr float Radius2Max = 2.5f * 2.5f; + const bool hasTOF = (TOFHit == 1); + + if (!hasTOF) { + if (ptcand < 0.5f) { + return std::abs(nsigmaTPC) < config.cutNsigmaTPC; + } else { + return nsigmaTPC > -2.0f && nsigmaTPC < config.cutNsigmaTPC; + } + } else { + const float radius2 = nsigmaTPC * nsigmaTPC + nsigmaTOF * nsigmaTOF; + if (radius2 >= Radius2Max) { + return false; + } + if (ptcand < 2.0f) { + return true; + } + + float slope = 0.0f; + float intercept = 0.0f; + + if (ptcand < 2.5f) { + slope = 1.29f; + intercept = 3.34f; + } else if (ptcand < 3.0f) { + slope = 0.94f; + intercept = 2.45f; + } else if (ptcand < 4.0f) { + slope = 0.62f; + intercept = 1.55f; + } else if (ptcand < 5.0f) { + slope = 0.32f; + intercept = 1.45f; + } else if (ptcand < 7.0f) { + slope = 0.22f; + intercept = 1.35f; + } else { + slope = 0.10f; + intercept = 1.25f; + } + + return nsigmaTPC < slope * nsigmaTOF + intercept; + } + } + + // Define data table bindings + using EventCandidatesMC = soa::Join; + using TrackCandidatesMC = soa::Join; + + ROOT::Math::PxPyPzEVector vecGen, vecDau1, vecDau2; + ROOT::Math::PxPyPzMVector daughter1Gen, daughter2Gen, motherGen, daughter1Rec, daughter2Rec, motherRec; + + // Process Generated MC + void processGen(aod::McCollision const&, aod::McParticles const& mcParticles) + { + // Fill total generated events counter + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 0.0); + + std::vector phiCandidatesGen; + for (const auto& mcParticle : mcParticles) { + + if (mcParticle.pdgCode() == o2::constants::physics::kPhi) { + + // Fill total phi produced counter + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 1.0); + + auto phiDaughters = mcParticle.daughters_as(); + if (phiDaughters.size() != 2) { + continue; + } + + bool passPosKaon = false; + bool passNegKaon = false; + for (const auto& kCurrentDaughter : phiDaughters) { + + if (kCurrentDaughter.pdgCode() == PDG_t::kKPlus) { + passPosKaon = true; + daughter1Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + // Fill total phi decay to K+ + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 2.0); + + } else if (kCurrentDaughter.pdgCode() == PDG_t::kKMinus) { + passNegKaon = true; + daughter2Gen = ROOT::Math::PxPyPzMVector(kCurrentDaughter.px(), kCurrentDaughter.py(), kCurrentDaughter.pz(), o2::constants::physics::MassKPlus); + + // Fill total phi decay to K- + registry.fill(HIST("Generated/h1_GenEventAndParticleCount"), 3.0); + } + } + if (passPosKaon && passNegKaon) { + motherGen = daughter1Gen + daughter2Gen; + PhiCandidateGen phiCand; + phiCand.p4 = motherGen; + phiCandidatesGen.push_back(phiCand); + + registry.fill(HIST("Generated/h1_InvMassPhi"), motherGen.M()); + registry.fill(HIST("Generated/h1_PhiPt"), motherGen.Pt()); + registry.fill(HIST("Generated/h1_PhiEta"), motherGen.Eta()); + registry.fill(HIST("Generated/h1_PhiY"), motherGen.Rapidity()); + } + } + } + + for (size_t i = 0; i < phiCandidatesGen.size(); ++i) { + for (size_t j = i + 1; j < phiCandidatesGen.size(); ++j) { + const auto& phi1 = phiCandidatesGen[i]; + const auto& phi2 = phiCandidatesGen[j]; + + ROOT::Math::PxPyPzMVector motherPhiPhi = phi1.p4 + phi2.p4; + + registry.fill(HIST("Generated/h2_InvMassPhiPhi"), motherPhiPhi.M(), motherPhiPhi.Pt()); + registry.fill(HIST("Generated/h1_PhiPhi_Phi"), motherPhiPhi.Phi()); + registry.fill(HIST("Generated/h1_PhiPhi_Eta"), motherPhiPhi.Eta()); + registry.fill(HIST("Generated/h1_PhiPhi_Y"), motherPhiPhi.Rapidity()); + } + } + } + PROCESS_SWITCH(PhiPhiMCTrigger, processGen, "Process Generated MC", true); + + // Process Reconstructed MC + void processRec(EventCandidatesMC::iterator const& collision, TrackCandidatesMC const& tracks, aod::McParticles const&) + { + + registry.fill(HIST("h1_EventCount"), 0.0); + if (collision.sel8()) { + registry.fill(HIST("h1_EventCount"), 1.0); + } + + if (config.isSel8 && !collision.sel8()) { + return; + } + + std::vector phiCandidates; + + // STEP 1: Reconstruct individual Phi candidates (Kaon track pairs) + for (const auto& t1 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop1Track); + + if (std::abs(t1.dcaXY()) > config.cutDCAxy || std::abs(t1.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1DCA); + + if (config.isApplyPIDSelection && !checkPID(t1.pt(), t1.tpcNSigmaKa(), t1.tofNSigmaKa(), t1.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1PID); + + if (!t1.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1MCLabel); + + if (std::abs(t1.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop1Eta); + + for (const auto& t2 : tracks) { + registry.fill(HIST("h1_TrackCount"), kLoop2Track); + + if (t2.index() <= t1.index()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Index); + + if (std::abs(t2.eta()) > config.trackEtaCut) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Eta); + + if (std::abs(t2.dcaXY()) > config.cutDCAxy || std::abs(t2.dcaZ()) > config.cutDCAz) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2DCA); + + if (config.isApplyPIDSelection && !checkPID(t2.pt(), t2.tpcNSigmaKa(), t2.tofNSigmaKa(), t2.hasTOF())) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2PID); + + if (!t2.has_mcParticle()) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2MCLabel); + + if (t1.sign() * t2.sign() > 0) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2Charge); + + registry.fill(HIST("h1_TrackCount"), kLoop2TrackSelected); + + auto mcKaTrk1 = t1.mcParticle(); + auto mcKaTrk2 = t2.mcParticle(); + + if (std::abs(mcKaTrk1.pdgCode()) != PDG_t::kKPlus || + std::abs(mcKaTrk2.pdgCode()) != PDG_t::kKPlus) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kLoop2KaonDaughters); + + bool foundMother = false; + + // Search for common mother + for (const auto& KaMotherPhi1 : mcKaTrk1.template mothers_as()) { + + for (const auto& KaMotherPhi2 : mcKaTrk2.template mothers_as()) { + + registry.fill(HIST("h1_TrackCount"), kCommonMotherLoop); + + if (KaMotherPhi1.globalIndex() != KaMotherPhi2.globalIndex()) + continue; + + registry.fill(HIST("h1_TrackCount"), kCommonMother); + + int motherPdg = KaMotherPhi1.pdgCode(); + + if (motherPdg != o2::constants::physics::kPhi) + continue; + + registry.fill(HIST("h1_TrackCount"), kPhiMother); + + daughter1Rec = ROOT::Math::PxPyPzMVector(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKPlus); + daughter2Rec = ROOT::Math::PxPyPzMVector(t2.px(), t2.py(), t2.pz(), o2::constants::physics::MassKPlus); + motherRec = daughter1Rec + daughter2Rec; + + if (motherRec.M() < config.phiMassCutLow || motherRec.M() > config.phiMassCutHigh) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kPhiMassCut); + + PhiCandidateTrigger phiCand; + + phiCand.p4 = motherRec; + phiCand.t1Index = t1.globalIndex(); + phiCand.t2Index = t2.globalIndex(); + phiCand.isValidMcPhi = true; + phiCand.mcPhiIndex = KaMotherPhi1.globalIndex(); + + phiCandidates.push_back(phiCand); + foundMother = true; + break; + } + if (foundMother) { + break; + } + } + } + } + + // STEP 2: Fill Single Reconstructed Phi Histograms (Loop ONCE per candidate) + for (const auto& phi : phiCandidates) { + // All Reconstructed Phi Candidates + if (phi.isValidMcPhi) { + + registry.fill(HIST("Reconstructed/h1_Phi1_Mass"), phi.p4.M()); + registry.fill(HIST("Reconstructed/h1_Phi1_Pt"), phi.p4.Pt()); + registry.fill(HIST("Reconstructed/h1_Phi1_Eta"), phi.p4.Eta()); + registry.fill(HIST("Reconstructed/h1_Phi1_Y"), phi.p4.Rapidity()); + } + } + + // STEP 3: Reconstruct Phi-Phi Mother Candidates (Double Loop for Pairs Only) + for (size_t i = 0; i < phiCandidates.size(); ++i) { + const auto& phi1 = phiCandidates[i]; + + for (size_t j = i + 1; j < phiCandidates.size(); ++j) { + const auto& phi2 = phiCandidates[j]; + + registry.fill(HIST("h1_TrackCount"), kLoop2PhiPairs); + + // Ensure all 4 kaon tracks are unique + if (phi1.t1Index == phi2.t1Index || phi1.t1Index == phi2.t2Index || + phi1.t2Index == phi2.t1Index || phi1.t2Index == phi2.t2Index) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kUniqueTracks); + + if (phi1.mcPhiIndex == phi2.mcPhiIndex) + continue; + registry.fill(HIST("h1_TrackCount"), kDifferentPhiMothers); + + if (!phi1.isValidMcPhi || !phi2.isValidMcPhi) { + continue; + } + registry.fill(HIST("h1_TrackCount"), kValidPhiPhiPair); + + // Combined 4-momentum of the Mother Phi-Phi System + ROOT::Math::PxPyPzMVector phiPhiMother = phi1.p4 + phi2.p4; + + registry.fill(HIST("Reconstructed/h2_PhiPhi_InvMass"), phiPhiMother.M(), phiPhiMother.Pt()); + registry.fill(HIST("Reconstructed/h1_PhiPhi_Y"), phiPhiMother.Rapidity()); + } + } + } + PROCESS_SWITCH(PhiPhiMCTrigger, processRec, "Process Reconstructed MC", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx b/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx index 061c8bd7a6c..27d024611de 100644 --- a/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx +++ b/PWGLF/Tasks/Resonances/phispectrapbpbqa.cxx @@ -362,7 +362,7 @@ struct phispectrapbpbqa { int Npostrack = 0; histos.fill(HIST("hOccupancy"), occupancy, centrality); - for (auto track1 : posThisColl) { + for (const auto& track1 : posThisColl) { if (!selectionTrack(track1)) { continue; } @@ -409,7 +409,7 @@ struct phispectrapbpbqa { // 1) φ at a chosen radius (e.g., outer pad rows ~247 cm) histos.fill(HIST("hPhiMommentum"), track1.phi(), track1.p(), occupancy); - for (auto track2 : negThisColl) { + for (const auto& track2 : negThisColl) { if (track1.sign() * track2.sign() > 0.0) { continue; } @@ -494,7 +494,7 @@ struct phispectrapbpbqa { void processMixedEventOpti(EventCandidates const& collisions, TrackCandidates const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningTypeVertexContributor binningOnPositions{{axisVertex, cnfgaxis.configThnAxisCentrality, axisOccupancy}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, cnfgaxis.configThnAxisCentrality, axisOccupancy}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, collisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, tracks2] : pair) { if (!collision1.sel8() || !collision1.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision1.selection_bit(aod::evsel::kNoITSROFrameBorder) || !collision1.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision1.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV) || !collision1.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { @@ -643,7 +643,7 @@ struct phispectrapbpbqa { auto Rectrackspart = RecTracks.sliceBy(perCollision, RecCollision.globalIndex()); // loop over reconstructed particle int ntrack1 = 0; - for (auto track1 : Rectrackspart) { + for (const auto& track1 : Rectrackspart) { if (!selectionTrack(track1)) { continue; } @@ -677,7 +677,7 @@ struct phispectrapbpbqa { histos.fill(HIST("hNsigmaTOFAfterCut"), nSigmaTOF, track1.p(), occupancy); } ntrack1 = ntrack1 + 1; - for (auto track2 : Rectrackspart) { + for (const auto& track2 : Rectrackspart) { auto track2ID = track2.index(); if (track2ID <= track1ID) { continue; @@ -785,7 +785,7 @@ struct phispectrapbpbqa { } auto daughtp = false; auto daughtm = false; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) { continue; } diff --git a/PWGLF/Tasks/Resonances/phitutorial.cxx b/PWGLF/Tasks/Resonances/phitutorial.cxx index 0cd3fecbbe8..84c591b9edb 100644 --- a/PWGLF/Tasks/Resonances/phitutorial.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial.cxx @@ -81,7 +81,7 @@ struct phitutorial { // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS //**************************************// template - bool eventSelection(const EventType event) + bool eventSelection(const EventType& event) { if (!event.sel8()) return false; @@ -98,7 +98,7 @@ struct phitutorial { }; //********************************************// template - bool trackSelection(const TracksType track) + bool trackSelection(const TracksType& track) { if (!track.isGlobalTrack()) return false; @@ -187,7 +187,7 @@ struct phitutorial { std::vector zBins{10, -10, 10}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy; - BinningType binning{{zBins, multBins}, true}; + BinningType binning{{zBins, multBins}}; SameKindPair pair{binning, 5, -1, &cache}; void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) diff --git a/PWGLF/Tasks/Resonances/phitutorial_step0.cxx b/PWGLF/Tasks/Resonances/phitutorial_step0.cxx index 6bd6ab64e3d..f3feee957d8 100644 --- a/PWGLF/Tasks/Resonances/phitutorial_step0.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial_step0.cxx @@ -70,7 +70,7 @@ struct phitutorial_step0 { // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS //**************************************// template - bool eventSelection(const EventType event) + bool eventSelection(const EventType& event) { if (!event.sel8()) // This is required to extract good events return false; diff --git a/PWGLF/Tasks/Resonances/phitutorial_step1.cxx b/PWGLF/Tasks/Resonances/phitutorial_step1.cxx index 1e6c5d129a8..6420ab51139 100644 --- a/PWGLF/Tasks/Resonances/phitutorial_step1.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial_step1.cxx @@ -75,7 +75,7 @@ struct phitutorial_step1 { // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS //**************************************// template - bool eventSelection(const EventType event) + bool eventSelection(const EventType& event) { if (!event.sel8()) // This is required to extract good events return false; diff --git a/PWGLF/Tasks/Resonances/phitutorial_step2.cxx b/PWGLF/Tasks/Resonances/phitutorial_step2.cxx index f1f0285e698..b77cccbea39 100644 --- a/PWGLF/Tasks/Resonances/phitutorial_step2.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial_step2.cxx @@ -75,7 +75,7 @@ struct phitutorial_step2 { // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS //**************************************// template - bool eventSelection(const EventType event) + bool eventSelection(const EventType& event) { if (!event.sel8()) // This is required to extract good events return false; @@ -84,7 +84,7 @@ struct phitutorial_step2 { }; //********************************************// template - bool trackSelection(const TracksType track) + bool trackSelection(const TracksType& track) { if (!track.isGlobalTrack()) return false; diff --git a/PWGLF/Tasks/Resonances/phitutorial_step3.cxx b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx index 5278d1b67f3..739c339f90b 100644 --- a/PWGLF/Tasks/Resonances/phitutorial_step3.cxx +++ b/PWGLF/Tasks/Resonances/phitutorial_step3.cxx @@ -81,7 +81,7 @@ struct phitutorial_step3 { // PREAMBLE COMPLETE, NOW WE DO HELPER FCNS //**************************************// template - bool eventSelection(const EventType event) + bool eventSelection(const EventType& event) { if (!event.sel8()) // This is required to extract good events return false; @@ -90,7 +90,7 @@ struct phitutorial_step3 { }; //********************************************// template - bool trackSelection(const TracksType track) + bool trackSelection(const TracksType& track) { if (!track.isGlobalTrack()) return false; @@ -188,7 +188,7 @@ struct phitutorial_step3 { std::vector zBins{10, -10, 10}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy; - BinningType binning{{zBins, multBins}, true}; + BinningType binning{{zBins, multBins}}; SameKindPair pair{binning, 5, -1, &cache}; void processDataMixedEvent(EventCandidates const& collisions, TrackCandidates const& tracks) // notice the collisions subscrition, it is not an iterator here! diff --git a/PWGLF/Tasks/Resonances/rho770analysis.cxx b/PWGLF/Tasks/Resonances/rho770analysis.cxx index 0b7c7f34d31..b07a83bb07b 100644 --- a/PWGLF/Tasks/Resonances/rho770analysis.cxx +++ b/PWGLF/Tasks/Resonances/rho770analysis.cxx @@ -151,7 +151,7 @@ struct rho770analysis { double massKa = MassKaonCharged; template - bool selTrack(const TrackType track) + bool selTrack(const TrackType& track) { if (std::abs(track.pt()) < cfgMinPt) return false; @@ -194,7 +194,7 @@ struct rho770analysis { } template - bool selPion(const TrackType track) + bool selPion(const TrackType& track) { const auto mode = static_cast(selectTypeInt.value); @@ -223,7 +223,7 @@ struct rho770analysis { } template - bool selKaon(const TrackType track) + bool selKaon(const TrackType& track) { const auto mode = static_cast(selectTypeInt.value); diff --git a/PWGLF/Tasks/Resonances/rhoanalysis.cxx b/PWGLF/Tasks/Resonances/rhoanalysis.cxx index a061f78375e..47367bd4ee4 100644 --- a/PWGLF/Tasks/Resonances/rhoanalysis.cxx +++ b/PWGLF/Tasks/Resonances/rhoanalysis.cxx @@ -267,7 +267,7 @@ struct rhoanalysis { void processMixedEvent(Event const& events, TrackPi const& tracks) { auto tracksTuple = std::make_tuple(tracks); - BinningType binningOnPositions{{axisVertex, axisMultiplicity}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{axisVertex, axisMultiplicity}}; SameKindPair pair{binningOnPositions, cfgNoMixedEvents, -1, events, tracksTuple, &cache}; float massPiplus = Ipdg->Mass(kPiPlus); float massPiminus = Ipdg->Mass(kPiMinus); @@ -305,11 +305,11 @@ struct rhoanalysis { if (std::abs(events.mcCollision().posZ()) > cfgCutVertex) { return; } - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { histos.fill(HIST("hNsigmaPionTPCvspT"), track1.pt(), track1.tpcNSigmaPi()); if (abs(track1.tpcNSigmaPi()) > nsigmaCutCombined) continue; - for (auto track2 : tracks) { + for (const auto& track2 : tracks) { if (abs(track2.tpcNSigmaPi()) > nsigmaCutCombined) continue; @@ -367,7 +367,7 @@ struct rhoanalysis { if (kDaughters.size() != 2) continue; - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (!kCurrentDaughter.isPhysicalPrimary()) continue; diff --git a/PWGLF/Tasks/Resonances/rsnanalysis.cxx b/PWGLF/Tasks/Resonances/rsnanalysis.cxx index eefe03403a2..9dd65838d5c 100644 --- a/PWGLF/Tasks/Resonances/rsnanalysis.cxx +++ b/PWGLF/Tasks/Resonances/rsnanalysis.cxx @@ -339,7 +339,7 @@ struct rsn_analysis { std::vector> kNegSelectedPions; // // Loop on Tracks - for (auto kCurrentTrack : kTracks) { + for (const auto& kCurrentTrack : kTracks) { // // Track Selection if (!uIsTrackSelected(kCurrentTrack)) @@ -576,7 +576,7 @@ struct rsn_analysis { std::vector> kNegSelectedPions; // // Loop on Tracks - for (auto kCurrentTrack : kTracks) { + for (const auto& kCurrentTrack : kTracks) { // // Track Selection if (!uIsTrackSelected(kCurrentTrack)) @@ -730,7 +730,7 @@ struct rsn_analysis { { // Loop on all mc particles - for (auto kCurrentParticle : mcParticles) { + for (const auto& kCurrentParticle : mcParticles) { // if (!kCurrentParticle.producedByGenerator()) continue; @@ -744,7 +744,7 @@ struct rsn_analysis { auto kHasKaonp = false; auto kHasKaonm = false; if (kDaughters.size() == 2) { - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (kCurrentDaughter.pdgCode() == +321) kHasKaonp = true; if (kCurrentDaughter.pdgCode() == -321) @@ -763,7 +763,7 @@ struct rsn_analysis { auto kHasKaonp = false; auto kHasKaonm = false; if (kDaughters.size() == 2) { - for (auto kCurrentDaughter : kDaughters) { + for (const auto& kCurrentDaughter : kDaughters) { if (fabs(kCurrentDaughter.pdgCode()) == 321) kHasKaonp = true; if (fabs(kCurrentDaughter.pdgCode()) == 211) diff --git a/PWGLF/Tasks/Resonances/sigma.cxx b/PWGLF/Tasks/Resonances/sigma.cxx index 6081fa5a8e5..61639e5f506 100644 --- a/PWGLF/Tasks/Resonances/sigma.cxx +++ b/PWGLF/Tasks/Resonances/sigma.cxx @@ -257,7 +257,7 @@ struct sigma { using BinningTypeCentralityM = ColumnBinningPolicy; using BinningTypeVertexContributor = ColumnBinningPolicy; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicity}}; Pair pair{binningOnPositions, cfgNoMixedEvents, -1, &cache}; void processSE(EventCandidates::iterator const& collision, TrackCandidates const& tracks, aod::V0Datas const& V0s, aod::BCs const&) @@ -286,7 +286,7 @@ struct sigma { rEventSelection.fill(HIST("hVertexZRec"), collision.posZ()); rEventSelection.fill(HIST("hmult"), multiplicity); - for (auto track1 : tracks) { + for (const auto& track1 : tracks) { if (QAbefore) { histos.fill(HIST("hNsigmaPionTPC_before"), track1.tpcNSigmaPi()); diff --git a/PWGLF/Tasks/Resonances/xi1530Analysis.cxx b/PWGLF/Tasks/Resonances/xi1530Analysis.cxx index ae911cf197a..3e5b70c04b1 100644 --- a/PWGLF/Tasks/Resonances/xi1530Analysis.cxx +++ b/PWGLF/Tasks/Resonances/xi1530Analysis.cxx @@ -229,7 +229,7 @@ struct cascadeXiAnalysis { // kinematic cuts method template - bool kinCuts(U trk1, K trk2, T p, float& alpha) + bool kinCuts(const U& trk1, const K& trk2, const T& p, float& alpha) { // initialize std::vector kinCutsPt = static_cast>(cKinCutsPt); diff --git a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx index d4169378e9f..ff27e613f62 100644 --- a/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx +++ b/PWGLF/Tasks/Resonances/xi1530Analysisqa.cxx @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -28,20 +27,30 @@ #include #include #include -#include +#include #include #include #include +#include #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include -#include #include -#include + +#include +#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include #include using namespace o2; @@ -52,34 +61,61 @@ using namespace o2::constants::physics; using LorentzVectorPtEtaPhiMass = ROOT::Math::PtEtaPhiMVector; using LorentzVectorSetXYZM = ROOT::Math::LorentzVector>; -Service pdgDB; - enum { kData = 0, - kLS, - kMixing, - kMCReco, - kMCTrue, - kMCTruePS, - kINEL10, - kINELg010, - kAllType, + kLS = 1, + kMixing = 2, + kMCReco = 3, + kMCTrue = 4, + kMCTruePS = 5, + kINEL10 = 6, + kINELg010 = 7, + kAllType = 8, kXiStar = 3324 }; struct Xi1530Analysisqa { + // Module-initializer tables; full tracks and cascades retain their schema. + using ResoCollisions = aod::ResoCollisions_001; + // Read original IDs only; no track_as() or original AO2D is needed here. + using ResoTracks = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCCols = soa::Join; + + // This native generator status is a production-specific injection tag. + static constexpr auto IsInjectedParent = [](auto const& mcparent) { + constexpr int InjectedGeneratorStatus = -11; + return o2::mcgenstatus::getGenStatusCode(mcparent.statusCode()) == InjectedGeneratorStatus; + }; + + // Own just the event rows: zero-copy slices would retain entire DF buffers. + struct MixingCollision { + float x, y, z, centrality, multiplicity; + int64_t index; + bool recINELgt0; + [[nodiscard]] float posX() const { return x; } + [[nodiscard]] float posY() const { return y; } + [[nodiscard]] float posZ() const { return z; } + [[nodiscard]] int64_t globalIndex() const { return index; } + }; + struct MixingEvent { + MixingCollision collision{}; + std::shared_ptr tracks; + }; + std::map> mixingPools; + float mixingBField = 0.f; + // Basic set-up // SliceCache cache; Preslice perRCol = aod::resodaughter::resoCollisionId; Preslice perCollision = aod::track::collisionId; - Preslice perResoCollision = + Preslice perResoCollision = aod::resodaughter::resoCollisionId; Preslice perResoCollisionCasc = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - using ResoMCCols = soa::Join; // Associated with histograms struct : ConfigurableGroup { ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0, 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.0, 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7, 6.8, 6.9, 7.0, 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7, 7.8, 7.9, 8.0, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 8.8, 8.9, 9.0, 9.1, 9.2, 9.3, 9.4, 9.5, 9.6, 9.7, 9.8, 9.9, 10.0, 10.1, 10.2, 10.3, 10.4, 10.5, 10.6, 10.7, 10.8, 10.9, 11.0, 11.1, 11.2, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8, 11.9, 12.0, 12.1, 12.2, 12.3, 12.4, 12.5, 12.6, 12.7, 12.8, 12.9, 13.0, 13.1, 13.2, 13.3, 13.4, 13.5, 13.6, 13.7, 13.8, 13.9, 14.0, 14.1, 14.2, 14.3, 14.4, 14.5, 14.6, 14.7, 14.8, 14.9, 15.0}, "Binning of the pT axis"}; @@ -114,8 +150,8 @@ struct Xi1530Analysisqa { struct : ConfigurableGroup { // Primary track selections - Configurable cMinPtcut{"cMinPtcut", 0.15, "Track minium pt cut"}; - Configurable cMaxetacut{"cMaxetacut", 0.8, "Track maximum eta cut"}; + Configurable cMinPtcut{"cMinPtcut", 0.00, "Track minium pt cut"}; + Configurable cMaxetacut{"cMaxetacut", 999., "Track maximum eta cut"}; Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Primary track selection"}; Configurable cfgPVContributor{"cfgPVContributor", true, "PV contributor track selection"}; @@ -124,7 +160,10 @@ struct Xi1530Analysisqa { Configurable cfgTPCRows{"cfgTPCRows", 80, "Minimum Number of TPC Crossed Rows "}; - Configurable cfgHasTOF{"cfgHasTOF", false, "Require TOF"}; + // Micro001 quality cuts use strict lower bounds; -1 preserves the previous selection. + // Full ResoTracks has no ITS cluster map and retains cfgTPCcluster/cfgTPCRows. + Configurable cfgITSNCls{"cfgITSNCls", -1, "Require ITS NCls > cut; -1 disables the cut"}; + Configurable cfgITSNClsInnerBarrel{"cfgITSNClsInnerBarrel", -1, "Require inner-barrel ITS NCls > cut; -1 disables the cut"}; Configurable cfgRapidityMinCut{"cfgRapidityMinCut", -0.5, "Rapidity cut for tracks"}; Configurable cfgRapidityMaxCut{"cfgRapidityMaxCut", 0.5, "Rapidity cut for tracks"}; @@ -132,15 +171,18 @@ struct Xi1530Analysisqa { // Primary track DCAxy to PV ConfigurableAxis cDCAtoPVBins{"cDCAtoPVBins", {1500, 0, 0.3}, "Bins for track DCA to PV"}; - Configurable cDCAxytoPVByPtPiFirstP0{"cDCAxytoPVByPtPiFirstP0", 0.004, "Coeff. Track DCAxy cut to PV by pt for Pion First (p0)"}; - Configurable cDCAxyToPVByPtPiFirstExp{"cDCAxyToPVByPtPiFirstExp", 0.013, "Coeff. Track DCAxy cut to PV by pt for Pion First (exp)"}; + Configurable cDCAxyToPVAsPt{"cDCAxyToPVAsPt", true, "Use the pT-dependent pion DCAxy cut; otherwise use cMaxDCAxyToPVCut"}; + Configurable cMaxDCAxyToPVCut{"cMaxDCAxyToPVCut", 0.5f, "Constant maximum pion |DCAxy| (cm) when its pT-dependent cut is disabled"}; + Configurable cDCAxytoPVByPtPiFirstP0{"cDCAxytoPVByPtPiFirstP0", 0.004, "Constant term of the pion pT-dependent DCAxy/z cut (cm)"}; + Configurable cDCAxyToPVByPtPiFirstExp{"cDCAxyToPVByPtPiFirstExp", 0.013, "Coefficient in pion DCAxy/z cut = P0 + coefficient / pT^power (legacy key)"}; + Configurable cDCAxyToPVByPtPiFirstPower{"cDCAxyToPVByPtPiFirstPower", 1.f, "Power in pion DCAxy/z cut = P0 + coefficient / pT^power"}; Configurable cDCAxyToPVAsPtForCasc{"cDCAxyToPVAsPtForCasc", true, "Set DCAxy to PV selection as pt cut"}; Configurable cDCAxyToPVByPtCascP0{"cDCAxyToPVByPtCascP0", 999., "Coeff. for Track DCAxy cut to PV by pt for Cascade (p0)"}; Configurable cDCAxyToPVByPtCascExp{"cDCAxyToPVByPtCascExp", 1., "Coeff. Track DCAxy cut to PV by pt for Cascade (exp)"}; // Primary track DCAz to PV - Configurable cDCAzToPVAsPt{"cDCAzToPVAsPt", true, "DCAz to PV selection as pt"}; + Configurable cDCAzToPVAsPt{"cDCAzToPVAsPt", true, "Use the shared pion pT-dependent DCA cut for DCAz; otherwise use cMaxDCAzToPVCut"}; Configurable cDCAzToPVAsPtForCasc{"cDCAzToPVAsPtForCasc", true, "Set DCA to PV selection as pt cut"}; Configurable cMaxDCAzToPVCut{"cMaxDCAzToPVCut", 0.5, "Track DCAz cut to PV Maximum"}; Configurable cMaxDCAzToPVCutCasc{"cMaxDCAzToPVCutCasc", 0.5, "Track DCAz cut to PV Maximum for casc"}; @@ -173,7 +215,7 @@ struct Xi1530Analysisqa { // Topological selections for Cascades ConfigurableAxis cDCASecondaryBins{"cDCASecondaryBins", {1000, 0, 0.1}, "Bins for DCA to Cascade secondary"}; ConfigurableAxis cProperLifetimeBins{"cProperLifetimeBins", {300, 0, 30}, "Bins for proper lifetime"}; - ConfigurableAxis cDCABachelorToPVBins{"cDCABachelorToPVcutBins", {1000, 0, 0.1}, "Bins for DCA bachelor to PV cut"}; + ConfigurableAxis cDCABachelorToPVcutBins{"cDCABachelorToPVcutBins", {1000, 0, 0.1}, "Bins for DCA bachelor to PV cut"}; Configurable cDCABachlorToPVcut{"cDCABachlorToPVcut", 0.06, "Bachelor DCA cut to PV"}; Configurable cDCAXiDaugthersCutPtRangeLower{"cDCAXiDaugthersCutPtRangeLower", 1., "Xi- DCA cut to PV as pt range lower"}; Configurable cDCAXiDaugthersCutPtRangeUpper{"cDCAXiDaugthersCutPtRangeUpper", 4., "Xi- DCA cut to PV as pt range upper"}; @@ -207,43 +249,55 @@ struct Xi1530Analysisqa { Configurable cPIDBound{"cPIDBound", 6.349, "configurable for replacing to .has"}; ConfigurableAxis cPIDnSigmaBins{"cPIDnSigmaBins", {131, -6.5, 6.5}, "Bins for nSigma PID"}; - Configurable tofAtHighPt{"tofAtHighPt", false, "Use TOF at high pT"}; - Configurable cMinTOFpt{"cMinTOFpt", 0.5, "Maximum TOF pt cut"}; + Configurable tofAtHighPt{"tofAtHighPt", false, "Apply TOF PID only above cMinTOFpt (cascade pT for its daughters)"}; + Configurable cMinTOFpt{"cMinTOFpt", 0.5, "Lower pT threshold for TOF PID when tofAtHighPt is enabled"}; // PID Selections for Pion First - Configurable cMaxtpcnSigmaPionFirst{"cMaxtpcnSigmaPionFirst", 4.0, "TPC nSigma cut for Pion First"}; - Configurable cMaxtofnSigmaPionFirst{"cMaxtofnSigmaPionFirst", 3.0, "TOF nSigma cut for Pion First"}; + Configurable cMintpcnSigmaPionFirst{"cMintpcnSigmaPionFirst", -4.0, "Strict lower TPC (nSigma - mean) bound for Pion First"}; + Configurable cMaxtpcnSigmaPionFirst{"cMaxtpcnSigmaPionFirst", 4.0, "Strict upper TPC (nSigma - mean) bound for Pion First"}; + Configurable cMeantpcnSigmaPionFirst{"cMeantpcnSigmaPionFirst", 0.0, "TPC nSigma mean subtracted for Pion First PID"}; + Configurable cMintofnSigmaPionFirst{"cMintofnSigmaPionFirst", -3.0, "Strict lower TOF (nSigma - mean) bound for Pion First"}; + Configurable cMaxtofnSigmaPionFirst{"cMaxtofnSigmaPionFirst", 3.0, "Strict upper TOF (nSigma - mean) bound for Pion First"}; + Configurable cMeantofnSigmaPionFirst{"cMeantofnSigmaPionFirst", 0.0, "TOF nSigma mean subtracted for Pion First PID"}; Configurable nsigmaCutCombinedPionFirst{"nsigmaCutCombinedPionFirst", -4.0, "Combined nSigma cut for Pion First"}; - Configurable cUseOnlyTOFTrackPionFirst{"cUseOnlyTOFTrackPionFirst", false, "Use only TOF track for PID selection Pion First"}; Configurable cByPassTOFPionFirst{"cByPassTOFPionFirst", true, "By pass TOF Pion First PID selection"}; // PID Selections for Pion Bachelor - Configurable cMaxtpcnSigmaPionBachelor{"cMaxtpcnSigmaPionBachelor", 4.0, "TPC nSigma cut for Pion Bachelor"}; - Configurable cMaxtofnSigmaPionBachelor{"cMaxtofnSigmaPionBachelor", 3.0, "TOF nSigma cut for Pion Bachelor"}; + Configurable cMintpcnSigmaPionBachelor{"cMintpcnSigmaPionBachelor", -4.0, "Strict lower TPC (nSigma - mean) bound for Pion Bachelor"}; + Configurable cMaxtpcnSigmaPionBachelor{"cMaxtpcnSigmaPionBachelor", 4.0, "Strict upper TPC (nSigma - mean) bound for Pion Bachelor"}; + Configurable cMeantpcnSigmaPionBachelor{"cMeantpcnSigmaPionBachelor", 0.0, "TPC nSigma mean subtracted for Pion Bachelor PID"}; + Configurable cMintofnSigmaPionBachelor{"cMintofnSigmaPionBachelor", -3.0, "Strict lower TOF (nSigma - mean) bound for Pion Bachelor"}; + Configurable cMaxtofnSigmaPionBachelor{"cMaxtofnSigmaPionBachelor", 3.0, "Strict upper TOF (nSigma - mean) bound for Pion Bachelor"}; + Configurable cMeantofnSigmaPionBachelor{"cMeantofnSigmaPionBachelor", 0.0, "TOF nSigma mean subtracted for Pion Bachelor PID"}; Configurable nsigmaCutCombinedPionBachelor{"nsigmaCutCombinedPionBachelor", -4.0, "Combined nSigma cut for Pion Bachelor"}; - Configurable cUseOnlyTOFTrackPionBachelor{"cUseOnlyTOFTrackPionBachelor", false, "Use only TOF track for PID selection Pion Bachelor"}; Configurable cByPassTOFPionBachelor{"cByPassTOFPionBachelor", true, "By pass TOF Pion Bachelor PID selection"}; // PID Selections for Pion - Configurable cMaxtpcnSigmaPion{"cMaxtpcnSigmaPion", 4.0, "TPC nSigma cut for Pion"}; - Configurable cMaxtofnSigmaPion{"cMaxtofnSigmaPion", 3.0, "TOF nSigma cut for Pion"}; + Configurable cMintpcnSigmaPion{"cMintpcnSigmaPion", -4.0, "Strict lower TPC (nSigma - mean) bound for Pion"}; + Configurable cMaxtpcnSigmaPion{"cMaxtpcnSigmaPion", 4.0, "Strict upper TPC (nSigma - mean) bound for Pion"}; + Configurable cMeantpcnSigmaPion{"cMeantpcnSigmaPion", 0.0, "TPC nSigma mean subtracted for Pion PID"}; + Configurable cMintofnSigmaPion{"cMintofnSigmaPion", -3.0, "Strict lower TOF (nSigma - mean) bound for Pion"}; + Configurable cMaxtofnSigmaPion{"cMaxtofnSigmaPion", 3.0, "Strict upper TOF (nSigma - mean) bound for Pion"}; + Configurable cMeantofnSigmaPion{"cMeantofnSigmaPion", 0.0, "TOF nSigma mean subtracted for Pion PID"}; Configurable nsigmaCutCombinedPion{"nsigmaCutCombinedPion", -4.0, "Combined nSigma cut for Pion"}; - Configurable cUseOnlyTOFTrackPion{"cUseOnlyTOFTrackPion", false, "Use only TOF track for PID selection Pion"}; Configurable cByPassTOFPion{"cByPassTOFPion", true, "By pass TOF Pion PID selection"}; // PID Selections for Proton - Configurable cMaxtpcnSigmaProton{"cMaxtpcnSigmaProton", 4.0, "TPC nSigma cut for Proton"}; - Configurable cMaxtofnSigmaProton{"cMaxtofnSigmaProton", 3.0, "TOF nSigma cut for Proton"}; + Configurable cMintpcnSigmaProton{"cMintpcnSigmaProton", -4.0, "Strict lower TPC (nSigma - mean) bound for Proton"}; + Configurable cMaxtpcnSigmaProton{"cMaxtpcnSigmaProton", 4.0, "Strict upper TPC (nSigma - mean) bound for Proton"}; + Configurable cMeantpcnSigmaProton{"cMeantpcnSigmaProton", 0.0, "TPC nSigma mean subtracted for Proton PID"}; + Configurable cMintofnSigmaProton{"cMintofnSigmaProton", -3.0, "Strict lower TOF (nSigma - mean) bound for Proton"}; + Configurable cMaxtofnSigmaProton{"cMaxtofnSigmaProton", 3.0, "Strict upper TOF (nSigma - mean) bound for Proton"}; + Configurable cMeantofnSigmaProton{"cMeantofnSigmaProton", 0.0, "TOF nSigma mean subtracted for Proton PID"}; Configurable nsigmaCutCombinedProton{"nsigmaCutCombinedProton", -4.0, "Combined nSigma cut for Proton"}; - Configurable cUseOnlyTOFTrackProton{"cUseOnlyTOFTrackProton", false, "Use only TOF track for PID selection Proton"}; Configurable cByPassTOFProton{"cByPassTOFProton", true, "By pass TOF Proton PID selection"}; } pidConfig; @@ -262,45 +316,48 @@ struct Xi1530Analysisqa { Configurable studyStableXi{"studyStableXi", false, "Study stable Xi"}; - Configurable cMultNTracksPVFull{"cMultNTracksPVFull", false, "Use full PV track multiplicity"}; - Configurable cMultNTracksPVeta1{"cMultNTracksPVeta1", false, "Use PV track multiplicity within |eta|<1"}; - Configurable cMultNTracksPVetaHalf{"cMultNTracksPVetaHalf", true, "Use PV track multiplicity within |eta|<0.5"}; - Configurable cRecoINELgt0{"cRecoINELgt0", true, "check if INEL>0 for reco events"}; + Configurable cMCINELgt0{"cMCINELgt0", true, "Require generator INEL>0 in reconstructed MC and generated-parent processes"}; + Configurable cMCVtxIn10{"cMCVtxIn10", true, "Require generator |vertex z| < 10 cm using isVtxIn10 in reconstructed MC and generated-parent processes"}; Configurable cUseFixedMassXi{"cUseFixedMassXi", false, "Use fixed mass for Xi-"}; - Configurable cUseTruthRapidity{"cUseTruthRapidity", false, "Use truth rapidity for Xi*"}; - - Configurable cConsiderPairOnly{"cConsiderPairOnly", true, "Consider only existing particle pairs in the event"}; + Configurable cUseTruthRapidity{"cUseTruthRapidity", true, "Use truth rapidity for Xi*"}; Configurable cfgFillRotBkg{"cfgFillRotBkg", true, "Fill rotated background"}; Configurable cfgMinRot{"cfgMinRot", 5.0 * constants::math::PI / 6.0, "Minimum of rotation"}; Configurable cfgMaxRot{"cfgMaxRot", 7.0 * constants::math::PI / 6.0, "Maximum of rotation"}; Configurable cfgRotPion{"cfgRotPion", true, "Rotate pion"}; Configurable cfgNrotBkg{"cfgNrotBkg", 4, "Number of rotated copies (background) per each original candidate"}; - } additionalConfig; - TRandom* rn = new TRandom(); + Configurable cMCInjection{"cMCInjection", true, "Select generated MC parents with native status -11 (production-specific injection tag)"}; + } additionalConfig; //*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*//*// struct PidSelectionParam { + float cMinTPCnSigma; float cMaxTPCnSigma; + float cMeanTPCnSigma; + float cMinTOFnSigma; float cMaxTOFnSigma; + float cMeanTOFnSigma; bool cByPassTOF; float nsigmaCutCombined; }; void init(o2::framework::InitContext&) { + if (doprocessMEDF && doprocessMEMicro) { + LOGF(fatal, "Enable only one mixing process: processMEDF or processMEMicro"); + } AxisSpec centAxis = {histoConfig.binsCent, "FT0M (%)"}; - AxisSpec dcaxyAxis = {primarytrackConfig.cDCAtoPVBins, "DCA_{#it{xy}} (cm)"}; - AxisSpec dcazAxis = {primarytrackConfig.cDCAtoPVBins, "DCA_{#it{z}} (cm)"}; + AxisSpec dcaxyAxis = {primarytrackConfig.cDCAtoPVBins, "|DCA_{#it{xy}}| (cm)"}; + AxisSpec dcazAxis = {primarytrackConfig.cDCAtoPVBins, "|DCA_{#it{z}}| (cm)"}; AxisSpec dcaSecondaryAxis = {cascadeConfig.cDCASecondaryBins, "DCA_{#it{Secondary}} (cm)"}; - AxisSpec dcaBachAxis = {cascadeConfig.cDCABachelorToPVBins, "DCA_{#it{Bach}} (cm)"}; + AxisSpec dcaBachAxis = {cascadeConfig.cDCABachelorToPVcutBins, "DCA_{#it{Bach}} (cm)"}; AxisSpec dcaDaugAxis = {v0sConfig.cDCADaughtersBins, "DCA_{#it{Daughter}} (cm)"}; AxisSpec cosPAAxis = {cascadeConfig.cCosPABins, "1-cos(PA)"}; - AxisSpec properLifetimeAxis = {cascadeConfig.cProperLifetimeBins, "Proper lifetime (fm/c)"}; + AxisSpec properLifetimeAxis = {cascadeConfig.cProperLifetimeBins, "c#tau (cm)"}; AxisSpec mcLabelAxis = {6, -1.5, 4.5, "MC Label"}; AxisSpec ptAxis = {histoConfig.binsPt, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec ptAxisQA = {histoConfig.binsPtQA, "#it{p}_{T} (GeV/#it{c})"}; @@ -313,7 +370,10 @@ struct Xi1530Analysisqa { if (histoConfig.multQA) { // multiplicity histograms - histos.add("multQA/h2MultCent", "Multiplicity vs Centrality", HistType::kTH2F, {centAxis, histoConfig.multNTracksAxis}); + histos.add("multQA/h2MultCent", "Same-event multiplicity vs centrality (one entry per event)", HistType::kTH2F, {centAxis, histoConfig.multNTracksAxis}); + if (doprocessMEMicro || doprocessMEDF) { + histos.add("multQA/h2MultCentME", "Mixed-event anchor multiplicity vs centrality (one entry per event pair)", HistType::kTH2F, {centAxis, histoConfig.multNTracksAxis}); + } histos.add("multQA/h2MultCentMC", "Multiplicity vs Centrality MC", HistType::kTH2F, {centAxis, histoConfig.multNTracksAxis}); } @@ -403,24 +463,24 @@ struct Xi1530Analysisqa { } // 3d histogram + Flags - histos.add("h3Xi1530invmassDS", "Invariant mass of Xi- differnt sign", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); - histos.add("h3XiinvmassDS", "Invariant mass of Xi- differnt sign", kTHnSparseF, {centAxis, ptAxis, invMassAxisCasc, flagAxis}); + histos.add("h3Xi1530invmassDS", "Invariant mass of Xi- differnt sign", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); + histos.add("h3XiinvmassDS", "Invariant mass of Xi- differnt sign", kTHnSparseD, {centAxis, ptAxis, invMassAxisCasc, flagAxis}); - histos.add("h3Xi1530invmassLS", "Invariant mass of Xi(1530)0 same sign", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); - histos.add("h3Xi1530invmassRotDS", "Invariant mass of Xi(1530)0 rotated DS", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); + histos.add("h3Xi1530invmassLS", "Invariant mass of Xi(1530)0 same sign", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); + histos.add("h3Xi1530invmassRotDS", "Invariant mass of Xi(1530)0 rotated DS", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); - histos.add("h3Xi1530invmassDSAnti", "Invariant mass of Anti-Xi(1530)0 differnt sign", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); - histos.add("h3XiinvmassDSAnti", "Invariant mass of Anti-Xi- differnt sign", kTHnSparseF, {centAxis, ptAxis, invMassAxisCasc, flagAxis}); + histos.add("h3Xi1530invmassDSAnti", "Invariant mass of Anti-Xi(1530)0 differnt sign", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); + histos.add("h3XiinvmassDSAnti", "Invariant mass of Anti-Xi- differnt sign", kTHnSparseD, {centAxis, ptAxis, invMassAxisCasc, flagAxis}); - histos.add("h3Xi1530invmassLSAnti", "Invariant mass of Anti-Xi(1530)0 same sign", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); - histos.add("h3Xi1530invmassRotDSAnti", "Invariant mass of Anti-Xi(1530)0 rotated DS", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); + histos.add("h3Xi1530invmassLSAnti", "Invariant mass of Anti-Xi(1530)0 same sign", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); + histos.add("h3Xi1530invmassRotDSAnti", "Invariant mass of Anti-Xi(1530)0 rotated DS", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); - if (doprocessMEMicro) { - histos.add("h3Xi1530invmassME_DS", "Invariant mass of Xi(1530)0 mixed event DS", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); - histos.add("h3Xi1530invmassME_DSAnti", "Invariant mass of Xi(1530)0 mixed event DSAnti", kTHnSparseF, {centAxis, ptAxis, invMassAxis, flagAxis}); + if (doprocessMEMicro || doprocessMEDF) { + histos.add("h3Xi1530invmassME_DS", "Invariant mass of Xi(1530)0 mixed event DS", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); + histos.add("h3Xi1530invmassME_DSAnti", "Invariant mass of Xi(1530)0 mixed event DSAnti", kTHnSparseD, {centAxis, ptAxis, invMassAxis, flagAxis}); } - if (doprocessMC) { + if (doprocessMC || doprocessMCMicro) { // MC QA histos.add("QAMCTrue/trkDCAxy_pi", "DCAxy distribution of pion track candidates", HistType::kTH2F, {ptAxis, dcaxyAxis}); histos.add("QAMCTrue/trkDCAxy_xi", "DCAxy distribution of Xi- track candidates", HistType::kTH2F, {ptAxis, dcaxyAxis}); @@ -449,19 +509,20 @@ struct Xi1530Analysisqa { histos.add("QAMCTrue/TPC_Nsigma_piminus_all", "TPC NSigma for Pion -;#it{p}_{T} (GeV/#it{c});#sigma_{TPC}^{Pion};", {HistType::kTH3F, {centAxis, ptAxisQA, pidQAAxis}}); } - histos.add("h3RecXi1530invmass", "Invariant mass of Reconstructed MC Xi(1530)0", kTHnSparseF, {centAxis, ptAxis, invMassAxis, ptAxis}); - histos.add("h3RecXiinvmass", "Invariant mass of Reconstructed MC Xi-", kTHnSparseF, {centAxis, ptAxis, invMassAxisCasc, ptAxis}); - - histos.add("h3RecXi1530invmassAnti", "Invariant mass of Reconstructed MC Anti-Xi(1530)0", kTHnSparseF, {centAxis, ptAxis, invMassAxis, ptAxis}); - histos.add("h3RecXiinvmassAnti", "Invariant mass of Reconstructed MC Anti-Xi-", kTHnSparseF, {centAxis, ptAxis, invMassAxisCasc, ptAxis}); + histos.add("h3RecXi1530invmass", "Invariant mass of Reconstructed MC Xi(1530)0", kTHnSparseD, {centAxis, ptAxis, invMassAxis, ptAxis}); + histos.add("h3RecXiinvmass", "Invariant mass of Reconstructed MC Xi-", kTHnSparseD, {centAxis, ptAxis, invMassAxisCasc, flagAxis}); - histos.add("h3Xi1530Gen", "pT distribution of True MC Xi(1530)0", kTHnSparseF, {ptAxis, centAxis, histoConfig.multNTracksAxis}); - histos.add("h3Xi1530GenAnti", "pT distribution of True MC Anti-Xi(1530)0", kTHnSparseF, {ptAxis, centAxis, histoConfig.multNTracksAxis}); + histos.add("h3RecXi1530invmassAnti", "Invariant mass of Reconstructed MC Anti-Xi(1530)0", kTHnSparseD, {centAxis, ptAxis, invMassAxis, ptAxis}); + histos.add("h3RecXiinvmassAnti", "Invariant mass of Reconstructed MC Anti-Xi-", kTHnSparseD, {centAxis, ptAxis, invMassAxisCasc, flagAxis}); - histos.add("Xi1530Rec", "pT distribution of Reconstructed MC Xi(1530)0", kTH2F, {ptAxis, centAxis}); - histos.add("Xi1530RecAnti", "pT distribution of Reconstructed MC Anti-Xi(1530)0", kTH2F, {ptAxis, centAxis}); + histos.add("Xi1530Rec", "pT distribution of Reconstructed MC Xi(1530)0", kTH2D, {ptAxis, centAxis}); + histos.add("Xi1530RecAnti", "pT distribution of Reconstructed MC Anti-Xi(1530)0", kTH2D, {ptAxis, centAxis}); histos.add("Xi1530Recinvmass", "Inv mass distribution of Reconstructed MC Xi(1530)0", kTH1F, {invMassAxis}); } + if (doprocessMC || doprocessMCMicro || doprocessMCTrue) { + histos.add("h3Xi1530Gen", "pT distribution of True MC Xi(1530)0", kTHnSparseD, {ptAxis, centAxis, histoConfig.multNTracksAxis}); + histos.add("h3Xi1530GenAnti", "pT distribution of True MC Anti-Xi(1530)0", kTHnSparseD, {ptAxis, centAxis, histoConfig.multNTracksAxis}); + } // QA for topological, kinematical cut for cascades if (histoConfig.additionalQAplots) { histos.add("QAbefore/V0DCATopPV", "V0s DCA to PV distribution as pt", HistType::kTH2F, {ptAxis, dcaxyAxis}); @@ -521,43 +582,48 @@ struct Xi1530Analysisqa { // Primary track selection for the first pion // template - bool primaryTrackCut(const TrackType track) + bool primaryTrackCut(const TrackType& track) { - if (std::abs(track.eta()) >= primarytrackConfig.cMaxetacut) + if (!std::isfinite(track.pt()) || !std::isfinite(track.eta()) || + !std::isfinite(track.dcaXY()) || !std::isfinite(track.dcaZ())) { + return false; + } + if (std::abs(track.eta()) >= primarytrackConfig.cMaxetacut) { + return false; + } + if (std::abs(track.pt()) <= primarytrackConfig.cMinPtcut) { return false; - if (std::abs(track.pt()) <= primarytrackConfig.cMinPtcut) + } + // Keep double precision and Power=1 equivalent to the previous pow(pt, -1.) cut. + // Evaluate the common threshold only when at least one axis needs it. + const double dcaPtCut = (primarytrackConfig.cDCAxyToPVAsPt || primarytrackConfig.cDCAzToPVAsPt) + ? primarytrackConfig.cDCAxytoPVByPtPiFirstP0.value + primarytrackConfig.cDCAxyToPVByPtPiFirstExp.value * std::pow(track.pt(), -static_cast(primarytrackConfig.cDCAxyToPVByPtPiFirstPower.value)) + : 0.; + const double dcaXYCut = primarytrackConfig.cDCAxyToPVAsPt ? dcaPtCut : primarytrackConfig.cMaxDCAxyToPVCut.value; + const double dcaZCut = primarytrackConfig.cDCAzToPVAsPt ? dcaPtCut : primarytrackConfig.cMaxDCAzToPVCut.value; + if (std::abs(track.dcaXY()) >= dcaXYCut || std::abs(track.dcaZ()) >= dcaZCut) { return false; + } if constexpr (IsResoMicrotrack) { - if (std::abs(o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags())) >= (primarytrackConfig.cDCAxytoPVByPtPiFirstP0 + primarytrackConfig.cDCAxyToPVByPtPiFirstExp * std::pow(track.pt(), -1.))) + if (track.itsNCls() <= primarytrackConfig.cfgITSNCls || + track.itsNClsInnerBarrel() <= primarytrackConfig.cfgITSNClsInnerBarrel || + track.tpcNClsCrossedRows() <= primarytrackConfig.cfgTPCRows) { return false; - if (primarytrackConfig.cDCAzToPVAsPt) { - if (std::abs(o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags())) >= (primarytrackConfig.cDCAxytoPVByPtPiFirstP0 + primarytrackConfig.cDCAxyToPVByPtPiFirstExp * std::pow(track.pt(), -1.))) - return false; - } else { - if (std::abs(o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags())) >= primarytrackConfig.cMaxDCAzToPVCut) - return false; } } else { - if (std::abs(track.dcaXY()) >= (primarytrackConfig.cDCAxytoPVByPtPiFirstP0 + primarytrackConfig.cDCAxyToPVByPtPiFirstExp * std::pow(track.pt(), -1.))) + if (track.tpcNClsFound() <= primarytrackConfig.cfgTPCcluster) { return false; - if (primarytrackConfig.cDCAzToPVAsPt) { - if (std::abs(track.dcaZ()) >= (primarytrackConfig.cDCAxytoPVByPtPiFirstP0 + primarytrackConfig.cDCAxyToPVByPtPiFirstExp * std::pow(track.pt(), -1.))) - return false; - } else { - if (std::abs(track.dcaZ()) >= primarytrackConfig.cMaxDCAzToPVCut) - return false; } - if (track.tpcNClsFound() <= primarytrackConfig.cfgTPCcluster) - return false; - if (track.tpcNClsCrossedRows() <= primarytrackConfig.cfgTPCRows) + if (track.tpcNClsCrossedRows() <= primarytrackConfig.cfgTPCRows) { return false; + } } - if (primarytrackConfig.cfgHasTOF && !track.hasTOF()) - return false; - if (primarytrackConfig.cfgPVContributor && !track.isPVContributor()) + if (primarytrackConfig.cfgPVContributor && !track.isPVContributor()) { return false; - if (primarytrackConfig.cfgPrimaryTrack && !track.isPrimaryTrack()) + } + if (primarytrackConfig.cfgPrimaryTrack && !track.isPrimaryTrack()) { return false; + } return true; } @@ -568,25 +634,34 @@ struct Xi1530Analysisqa { // Primary track selection for cascades, Need to more informations for cascades // template - bool cascprimaryTrackCut(const TracksTypeCasc track) + bool cascprimaryTrackCut(const TracksTypeCasc& track) { - if (std::abs(track.eta()) >= primarytrackConfig.cMaxetacut) + if (std::abs(track.eta()) >= primarytrackConfig.cMaxetacut) { return false; - if (std::abs(track.pt()) <= primarytrackConfig.cMinPtcut) + } + if (std::abs(track.pt()) <= primarytrackConfig.cMinPtcut) { return false; - if (track.nCrossedRowsPos() <= cascadeConfig.cMinNCrossedRowsTPCPos) + } + if (track.nCrossedRowsPos() <= cascadeConfig.cMinNCrossedRowsTPCPos) { return false; - if (track.nCrossedRowsNeg() <= cascadeConfig.cMinNCrossedRowsTPCNeg) + } + if (track.nCrossedRowsNeg() <= cascadeConfig.cMinNCrossedRowsTPCNeg) { return false; - if (track.nCrossedRowsBach() <= cascadeConfig.cMinNCrossedRowsTPCBach) + } + if (track.nCrossedRowsBach() <= cascadeConfig.cMinNCrossedRowsTPCBach) { return false; + } if (primarytrackConfig.cDCAxyToPVAsPtForCasc) { - if (std::abs(track.dcaXYCascToPV()) >= (primarytrackConfig.cDCAxyToPVByPtCascP0 + primarytrackConfig.cDCAxyToPVByPtCascExp * track.pt())) + if (std::abs(track.dcaXYCascToPV()) >= (primarytrackConfig.cDCAxyToPVByPtCascP0 + primarytrackConfig.cDCAxyToPVByPtCascExp * track.pt())) { return false; + } } if (primarytrackConfig.cDCAzToPVAsPtForCasc) { - if (std::abs(track.dcaZCascToPV()) >= (primarytrackConfig.cDCAxyToPVByPtCascP0 + primarytrackConfig.cDCAxyToPVByPtCascExp * std::pow(track.pt(), -1.))) + if (std::abs(track.dcaZCascToPV()) >= (primarytrackConfig.cDCAxyToPVByPtCascP0 + primarytrackConfig.cDCAxyToPVByPtCascExp * std::pow(track.pt(), -1.))) { return false; + } + } else if (std::abs(track.dcaZCascToPV()) >= primarytrackConfig.cMaxDCAzToPVCutCasc) { + return false; } return true; @@ -596,127 +671,105 @@ struct Xi1530Analysisqa { // Topological cuts for cascades template - bool casctopCut(const TracksTypeCasc track) + bool casctopCut(const TracksTypeCasc& track) { // Topological cuts for V0s - if (std::abs(track.daughDCA()) >= v0sConfig.cDCALambdaDaugtherscut) + if (std::abs(track.daughDCA()) >= v0sConfig.cDCALambdaDaugtherscut) { return false; - if (std::abs(track.dcav0topv()) <= v0sConfig.cDCALambdaToPVcut) + } + if (std::abs(track.dcav0topv()) <= v0sConfig.cDCALambdaToPVcut) { return false; + } if (track.sign() < 0) { - if (std::abs(track.dcanegtopv()) <= v0sConfig.cDCAPionToPVcut) + if (std::abs(track.dcanegtopv()) <= v0sConfig.cDCAPionToPVcut) { return false; - if (std::abs(track.dcapostopv()) <= v0sConfig.cDCAProtonToPVcut) + } + if (std::abs(track.dcapostopv()) <= v0sConfig.cDCAProtonToPVcut) { return false; + } } else { - if (std::abs(track.dcanegtopv()) <= v0sConfig.cDCAProtonToPVcut) + if (std::abs(track.dcanegtopv()) <= v0sConfig.cDCAProtonToPVcut) { return false; - if (std::abs(track.dcapostopv()) <= v0sConfig.cDCAPionToPVcut) + } + if (std::abs(track.dcapostopv()) <= v0sConfig.cDCAPionToPVcut) { return false; + } } - if (track.v0CosPA() <= std::cos(v0sConfig.cV0CosPACutPtDepP0 - v0sConfig.cV0CosPACutPtDepP1 * track.pt())) + if (track.v0CosPA() <= std::cos(v0sConfig.cV0CosPACutPtDepP0 - v0sConfig.cV0CosPACutPtDepP1 * track.pt())) { return false; - if (track.transRadius() >= v0sConfig.cMaxV0radiuscut || track.transRadius() <= v0sConfig.cMinV0radiuscut) + } + if (track.transRadius() >= v0sConfig.cMaxV0radiuscut || track.transRadius() <= v0sConfig.cMinV0radiuscut) { return false; - if (std::abs(track.mLambda() - MassLambda) >= v0sConfig.cMasswindowV0cut) + } + if (std::abs(track.mLambda() - MassLambda) >= v0sConfig.cMasswindowV0cut) { return false; + } // Topological Cuts for Cascades - if (std::abs(track.dcabachtopv()) <= cascadeConfig.cDCABachlorToPVcut) + if (std::abs(track.dcabachtopv()) <= cascadeConfig.cDCABachlorToPVcut) { return false; + } if (track.pt() <= cascadeConfig.cDCAXiDaugthersCutPtRangeLower) { - if (track.cascDaughDCA() >= cascadeConfig.cDCAXiDaugthersCutPtDepLower) + if (track.cascDaughDCA() >= cascadeConfig.cDCAXiDaugthersCutPtDepLower) { return false; + } } if (track.pt() >= cascadeConfig.cDCAXiDaugthersCutPtRangeLower && track.pt() <= cascadeConfig.cDCAXiDaugthersCutPtRangeUpper) { - if (track.cascDaughDCA() >= cascadeConfig.cDCAXiDaugthersCutPtDepMiddle) + if (track.cascDaughDCA() >= cascadeConfig.cDCAXiDaugthersCutPtDepMiddle) { return false; + } } if (track.pt() >= cascadeConfig.cDCAXiDaugthersCutPtRangeUpper) { - if (track.cascDaughDCA() >= cascadeConfig.cDCAXiDaugthersCutPtDepUpper) + if (track.cascDaughDCA() >= cascadeConfig.cDCAXiDaugthersCutPtDepUpper) { return false; + } } - if (track.cascCosPA() <= std::cos(cascadeConfig.cCosPACascCutPtDepP0 - cascadeConfig.cCosPACascCutPtDepP1 * track.pt())) + if (track.cascCosPA() <= std::cos(cascadeConfig.cCosPACascCutPtDepP0 - cascadeConfig.cCosPACascCutPtDepP1 * track.pt())) { return false; + } - if (track.cascTransRadius() >= cascadeConfig.cMaxCascradiuscut || track.cascTransRadius() <= cascadeConfig.cMinCascradiuscut) + if (track.cascTransRadius() >= cascadeConfig.cMaxCascradiuscut || track.cascTransRadius() <= cascadeConfig.cMinCascradiuscut) { return false; - if (std::abs(track.mXi() - cascadeConfig.cMassXiminus) >= cascadeConfig.cMasswindowCasccut) + } + if (std::abs(track.mXi() - cascadeConfig.cMassXiminus) >= cascadeConfig.cMasswindowCasccut) { return false; + } return true; } - bool pidSelector(float TPCNsigma, float TOFNsigma, const PidSelectionParam& params, bool tofAtHighPt, float trackPt) + bool pidSelector(float TPCNsigma, float TOFNsigma, const PidSelectionParam& params, bool tofAtHighPt, float trackPt, bool hasTOF) { - bool tpcPIDPassed{false}, tofPIDPassed{false}; - - if (tofAtHighPt && trackPt > pidConfig.cMinTOFpt) { - if (std::abs(TPCNsigma) < params.cMaxTPCnSigma) { - tpcPIDPassed = true; - } - - if (params.cByPassTOF && tpcPIDPassed) { - return true; - } - - if (hasSubsystemInfo(TOFNsigma)) { - if (std::abs(TOFNsigma) < params.cMaxTOFnSigma) { - tofPIDPassed = true; - } - if ((params.nsigmaCutCombined > 0) && - (TPCNsigma * TPCNsigma + TOFNsigma * TOFNsigma < params.nsigmaCutCombined * params.nsigmaCutCombined)) { - tofPIDPassed = true; - } - } else { - tofPIDPassed = true; - } - return tpcPIDPassed && tofPIDPassed; - } else { - - if (std::abs(TPCNsigma) < params.cMaxTPCnSigma) { - tpcPIDPassed = true; - } - - if (params.cByPassTOF && tpcPIDPassed) { - return true; - } - - if (hasSubsystemInfo(TOFNsigma)) { - if (std::abs(TOFNsigma) < params.cMaxTOFnSigma) { - tofPIDPassed = true; - } - if ((params.nsigmaCutCombined > 0) && - (TPCNsigma * TPCNsigma + TOFNsigma * TOFNsigma < params.nsigmaCutCombined * params.nsigmaCutCombined)) { - tofPIDPassed = true; - } - } else { - tofPIDPassed = true; - } - - return tpcPIDPassed && tofPIDPassed; + // Bounds are signed offsets from the detector-specific mean; exclude both edges. + const float centeredTPC = TPCNsigma - params.cMeanTPCnSigma; + const bool passesTPCWindow = params.cMinTPCnSigma < centeredTPC && centeredTPC < params.cMaxTPCnSigma; + if (!passesTPCWindow) { + return false; } + // Missing or bypassed TOF never vetoes a track that passes the TPC window. + if (params.cByPassTOF || (tofAtHighPt && trackPt <= pidConfig.cMinTOFpt) || !hasTOF) { + return true; + } + // Retain TOF-window OR combined acceptance; center both detectors for the latter. + // A present Micro001 TOF value at +/-infinity is overflow, not missing TOF. + const float centeredTOF = TOFNsigma - params.cMeanTOFnSigma; + return (params.cMinTOFnSigma < centeredTOF && centeredTOF < params.cMaxTOFnSigma) || + (params.nsigmaCutCombined > 0 && + centeredTPC * centeredTPC + centeredTOF * centeredTOF < params.nsigmaCutCombined * params.nsigmaCutCombined); } // PID selection for the First Pion // - template + template bool selectionPIDPionFirst(const T& candidate) { - float tpcNsigmaPionFirst, tofNsigmaPionFirst; + const float tpcNsigmaPionFirst = candidate.tpcNSigmaPi(); + const float tofNsigmaPionFirst = candidate.tofNSigmaPi(); float trackPt = candidate.pt(); - if constexpr (IsResoMicrotrack) { - tpcNsigmaPionFirst = o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(candidate.pidNSigmaPiFlag()); - tofNsigmaPionFirst = o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(candidate.pidNSigmaPiFlag()); - } else { - tpcNsigmaPionFirst = candidate.tpcNSigmaPi(); - tofNsigmaPionFirst = candidate.tofNSigmaPi(); - } - - PidSelectionParam pionFirstParams = {pidConfig.cMaxtpcnSigmaPionFirst, pidConfig.cMaxtofnSigmaPionFirst, pidConfig.cByPassTOFPionFirst, pidConfig.nsigmaCutCombinedPionFirst}; + PidSelectionParam pionFirstParams = {.cMinTPCnSigma = pidConfig.cMintpcnSigmaPionFirst, .cMaxTPCnSigma = pidConfig.cMaxtpcnSigmaPionFirst, .cMeanTPCnSigma = pidConfig.cMeantpcnSigmaPionFirst, .cMinTOFnSigma = pidConfig.cMintofnSigmaPionFirst, .cMaxTOFnSigma = pidConfig.cMaxtofnSigmaPionFirst, .cMeanTOFnSigma = pidConfig.cMeantofnSigmaPionFirst, .cByPassTOF = pidConfig.cByPassTOFPionFirst, .nsigmaCutCombined = pidConfig.nsigmaCutCombinedPionFirst}; - return pidSelector(tpcNsigmaPionFirst, tofNsigmaPionFirst, pionFirstParams, pidConfig.tofAtHighPt, trackPt); + return pidSelector(tpcNsigmaPionFirst, tofNsigmaPionFirst, pionFirstParams, pidConfig.tofAtHighPt, trackPt, candidate.hasTOF()); } template @@ -724,9 +777,9 @@ struct Xi1530Analysisqa { { bool lConsistentWithXi{false}, lConsistentWithLambda{false}, lConsistentWithPion{false}, lConsistentWithProton{false}; - float tpcNsigmaBachelor, tofNsigmaBachelor; - float tpcNsigmaPion, tofNsigmaPion; - float tpcNsigmaProton, tofNsigmaProton; + float tpcNsigmaBachelor = 0.f, tofNsigmaBachelor = 0.f; + float tpcNsigmaPion = 0.f, tofNsigmaPion = 0.f; + float tpcNsigmaProton = 0.f, tofNsigmaProton = 0.f; float trackPt = candidate.pt(); if (candidate.sign() < 0) { // Xi- candidates @@ -750,13 +803,13 @@ struct Xi1530Analysisqa { tofNsigmaProton = candidate.daughterTOFNSigmaNegPr(); } - PidSelectionParam bachelorParams = {pidConfig.cMaxtpcnSigmaPionBachelor, pidConfig.cMaxtofnSigmaPionBachelor, pidConfig.cByPassTOFPionBachelor, pidConfig.nsigmaCutCombinedPionBachelor}; - PidSelectionParam pionParams = {pidConfig.cMaxtpcnSigmaPion, pidConfig.cMaxtofnSigmaPion, pidConfig.cByPassTOFPion, pidConfig.nsigmaCutCombinedPion}; - PidSelectionParam protonParams = {pidConfig.cMaxtpcnSigmaProton, pidConfig.cMaxtofnSigmaProton, pidConfig.cByPassTOFProton, pidConfig.nsigmaCutCombinedProton}; + PidSelectionParam bachelorParams = {.cMinTPCnSigma = pidConfig.cMintpcnSigmaPionBachelor, .cMaxTPCnSigma = pidConfig.cMaxtpcnSigmaPionBachelor, .cMeanTPCnSigma = pidConfig.cMeantpcnSigmaPionBachelor, .cMinTOFnSigma = pidConfig.cMintofnSigmaPionBachelor, .cMaxTOFnSigma = pidConfig.cMaxtofnSigmaPionBachelor, .cMeanTOFnSigma = pidConfig.cMeantofnSigmaPionBachelor, .cByPassTOF = pidConfig.cByPassTOFPionBachelor, .nsigmaCutCombined = pidConfig.nsigmaCutCombinedPionBachelor}; + PidSelectionParam pionParams = {.cMinTPCnSigma = pidConfig.cMintpcnSigmaPion, .cMaxTPCnSigma = pidConfig.cMaxtpcnSigmaPion, .cMeanTPCnSigma = pidConfig.cMeantpcnSigmaPion, .cMinTOFnSigma = pidConfig.cMintofnSigmaPion, .cMaxTOFnSigma = pidConfig.cMaxtofnSigmaPion, .cMeanTOFnSigma = pidConfig.cMeantofnSigmaPion, .cByPassTOF = pidConfig.cByPassTOFPion, .nsigmaCutCombined = pidConfig.nsigmaCutCombinedPion}; + PidSelectionParam protonParams = {.cMinTPCnSigma = pidConfig.cMintpcnSigmaProton, .cMaxTPCnSigma = pidConfig.cMaxtpcnSigmaProton, .cMeanTPCnSigma = pidConfig.cMeantpcnSigmaProton, .cMinTOFnSigma = pidConfig.cMintofnSigmaProton, .cMaxTOFnSigma = pidConfig.cMaxtofnSigmaProton, .cMeanTOFnSigma = pidConfig.cMeantofnSigmaProton, .cByPassTOF = pidConfig.cByPassTOFProton, .nsigmaCutCombined = pidConfig.nsigmaCutCombinedProton}; - lConsistentWithXi = pidSelector(tpcNsigmaBachelor, tofNsigmaBachelor, bachelorParams, pidConfig.tofAtHighPt, trackPt); - lConsistentWithPion = pidSelector(tpcNsigmaPion, tofNsigmaPion, pionParams, pidConfig.tofAtHighPt, trackPt); - lConsistentWithProton = pidSelector(tpcNsigmaProton, tofNsigmaProton, protonParams, pidConfig.tofAtHighPt, trackPt); + lConsistentWithXi = pidSelector(tpcNsigmaBachelor, tofNsigmaBachelor, bachelorParams, pidConfig.tofAtHighPt, trackPt, hasSubsystemInfo(tofNsigmaBachelor)); + lConsistentWithPion = pidSelector(tpcNsigmaPion, tofNsigmaPion, pionParams, pidConfig.tofAtHighPt, trackPt, hasSubsystemInfo(tofNsigmaPion)); + lConsistentWithProton = pidSelector(tpcNsigmaProton, tofNsigmaProton, protonParams, pidConfig.tofAtHighPt, trackPt, hasSubsystemInfo(tofNsigmaProton)); lConsistentWithLambda = lConsistentWithProton && lConsistentWithPion; @@ -776,30 +829,44 @@ struct Xi1530Analysisqa { return properLifetime; } + // Build once per selected collision, not once per reconstructed pair. + std::unordered_set getInjectedMotherIds(aod::ResoMCParents_001 const& parents) + { + std::unordered_set motherIds; + if (!additionalConfig.cMCInjection) { + return motherIds; + } + for (auto const& parent : parents) { + if (std::abs(parent.pdgCode()) == kXiStar && IsInjectedParent(parent)) { + motherIds.insert(parent.originalMcParticleId()); + } + } + return motherIds; + } + template - void fillHistograms(const CollisionType& collision, const centType& inCent, const TracksType& dTracks1, const TracksTypeCasc& dTracks2) // Order: ResoColl, ResoTrack, ResoCascTrack + void fillHistograms(const CollisionType& collision, const centType& inCent, const TracksType& dTracks1, const TracksTypeCasc& dTracks2, + const MixingCollision* cascadeCollision = nullptr, + const std::unordered_set& injectedMotherIds = {}) // Order: ResoColl, ResoTrack, ResoCascTrack { - auto Cent = inCent; + auto cent = inCent; if (histoConfig.eventQA) { if constexpr (!IsMix) { histos.fill(HIST("QAevent/hVertexZSameE"), collision.posZ()); - histos.fill(HIST("QAevent/hMultiplicityPercentSameE"), Cent); + histos.fill(HIST("QAevent/hMultiplicityPercentSameE"), cent); histos.fill(HIST("QAevent/hCollisionIndexSameE"), collision.globalIndex()); histos.fill(HIST("QAevent/hnTrksSameE"), dTracks1.size()); histos.fill(HIST("QAevent/hnCascsSameE"), dTracks2.size()); } else { histos.fill(HIST("QAevent/hVertexZMixedE"), collision.posZ()); - histos.fill(HIST("QAevent/hMultiplicityPercentMixedE"), Cent); + histos.fill(HIST("QAevent/hMultiplicityPercentMixedE"), cent); histos.fill(HIST("QAevent/hCollisionIndexMixedE"), collision.globalIndex()); histos.fill(HIST("QAevent/hnTrksMixedE"), dTracks1.size()); histos.fill(HIST("QAevent/hnCascsMixedE"), dTracks2.size()); } } - if (additionalConfig.cConsiderPairOnly && (dTracks2.size() < 1 || dTracks1.size() < 1)) - return; - LorentzVectorPtEtaPhiMass lDecayDaughter1, lDecayDaughter2, lResonance, lDaughterRot, lResonanceRot; std::vector pionCandateIndicies = {}; std::vector xiCandateIndicies = {}; @@ -808,55 +875,44 @@ struct Xi1530Analysisqa { for (const auto& trk1 : dTracks1) { auto trk1ptPi = trk1.pt(); - float trk1DCAXY = -1.f; - float trk1DCAZ = -1.f; - float trk1NSigmaPiTPC = -999.f; - float trk1NSigmaPiTOF = -999.f; - if constexpr (IsResoMicrotrack) { - trk1DCAXY = o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(trk1.trackSelectionFlags()); - trk1DCAZ = o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(trk1.trackSelectionFlags()); - trk1NSigmaPiTPC = o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(trk1.pidNSigmaPiFlag()); - trk1NSigmaPiTOF = o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(trk1.pidNSigmaPiFlag()); - } else { - trk1DCAXY = trk1.dcaXY(); - trk1DCAZ = trk1.dcaZ(); - trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); - trk1NSigmaPiTOF = trk1.tofNSigmaPi(); - } + const float trk1DCAXY = trk1.dcaXY(); + const float trk1DCAZ = trk1.dcaZ(); + const float trk1NSigmaPiTPC = trk1.tpcNSigmaPi(); + const float trk1NSigmaPiTOF = trk1.tofNSigmaPi(); // QA before if constexpr (!IsMix) { if (histoConfig.pidPlots) { - histos.fill(HIST("QAbefore/TPC_Nsigma_pi_first_all"), Cent, trk1ptPi, trk1NSigmaPiTPC); - if (hasSubsystemInfo(trk1NSigmaPiTOF)) { - histos.fill(HIST("QAbefore/TOF_Nsigma_pi_first_all"), Cent, trk1ptPi, trk1NSigmaPiTOF); + histos.fill(HIST("QAbefore/TPC_Nsigma_pi_first_all"), cent, trk1ptPi, trk1NSigmaPiTPC); + if (trk1.hasTOF() && !std::isnan(trk1NSigmaPiTOF)) { + histos.fill(HIST("QAbefore/TOF_Nsigma_pi_first_all"), cent, trk1ptPi, trk1NSigmaPiTOF); histos.fill(HIST("QAbefore/TOF_TPC_Map_pi_first_all"), trk1NSigmaPiTOF, trk1NSigmaPiTPC); } } if (histoConfig.additionalQAplots) { - histos.fill(HIST("QAbefore/trkDCAxy_pi"), trk1ptPi, trk1DCAXY); - histos.fill(HIST("QAbefore/trkDCAz_pi"), trk1ptPi, trk1DCAZ); + histos.fill(HIST("QAbefore/trkDCAxy_pi"), trk1ptPi, std::abs(trk1DCAXY)); + histos.fill(HIST("QAbefore/trkDCAz_pi"), trk1ptPi, std::abs(trk1DCAZ)); } } - if (pidConfig.cUseOnlyTOFTrackPionBachelor && hasSubsystemInfo(trk1NSigmaPiTOF)) + if (!selectionPIDPionFirst(trk1)) { // PID selection for the first pion continue; - if (!selectionPIDPionFirst(trk1)) // PID selection for the first pion - continue; - if (!primaryTrackCut(trk1)) // Primary track selections + } + if (!primaryTrackCut(trk1)) { // Primary track selections continue; + } if constexpr (!IsMix) { if (histoConfig.pidPlots) { - histos.fill(HIST("QAafter/TPC_Nsigma_pi_first_all"), Cent, trk1ptPi, trk1NSigmaPiTPC); + histos.fill(HIST("QAafter/TPC_Nsigma_pi_first_all"), cent, trk1ptPi, trk1NSigmaPiTPC); - if (hasSubsystemInfo(trk1NSigmaPiTOF)) { - histos.fill(HIST("QAafter/TOF_Nsigma_pi_first_all"), Cent, trk1ptPi, trk1NSigmaPiTOF); + if (trk1.hasTOF() && !std::isnan(trk1NSigmaPiTOF)) { + histos.fill(HIST("QAafter/TOF_Nsigma_pi_first_all"), cent, trk1ptPi, trk1NSigmaPiTOF); histos.fill(HIST("QAafter/TOF_TPC_Map_pi_first_all"), trk1NSigmaPiTOF, trk1NSigmaPiTPC); } } if (histoConfig.additionalQAplots) { - histos.fill(HIST("QAafter/trkDCAxy_pi"), trk1ptPi, trk1DCAXY); - histos.fill(HIST("QAafter/trkDCAz_pi"), trk1ptPi, trk1DCAZ); + histos.fill(HIST("QAafter/trkDCAxy_pi"), trk1ptPi, std::abs(trk1DCAXY)); + histos.fill(HIST("QAafter/trkDCAz_pi"), trk1ptPi, std::abs(trk1DCAZ)); } } @@ -868,7 +924,7 @@ struct Xi1530Analysisqa { auto trk2ptXi = trk2.pt(); auto massLambdaCand = trk2.mLambda(); auto massXiCand = trk2.mXi(); - auto trk2ProperLifetime = properLifetime(collision, trk2); + auto trk2ProperLifetime = cascadeCollision ? properLifetime(*cascadeCollision, trk2) : properLifetime(collision, trk2); // Topological variables for cascades auto trk2DCAV0TopPV = trk2.dcav0topv(); @@ -909,27 +965,27 @@ struct Xi1530Analysisqa { //// QA plots before the selection // // --- PID QA if (histoConfig.pidPlots) { - histos.fill(HIST("QAbefore/TPC_Nsigma_pi_bachelor_all"), Cent, 0, trk2NSigmaPiBachelorTPC); // can't take pt information for the cascade secondary + histos.fill(HIST("QAbefore/TPC_Nsigma_pi_bachelor_all"), cent, 0, trk2NSigmaPiBachelorTPC); // can't take pt information for the cascade secondary if (hasSubsystemInfo(trk2NSigmaPiBachelorTOF)) { histos.fill(HIST("QAbefore/TOF_TPC_Map_pi_bachelor_all"), trk2NSigmaPiBachelorTOF, trk2NSigmaPiBachelorTPC); } - histos.fill(HIST("QAbefore/TPC_Nsigma_pr_all"), Cent, 0, trk2NSigmaPrPosTPC); + histos.fill(HIST("QAbefore/TPC_Nsigma_pr_all"), cent, 0, trk2NSigmaPrPosTPC); if (hasSubsystemInfo(trk2NSigmaPrPosTOF)) { histos.fill(HIST("QAbefore/TOF_TPC_Map_pr_all"), trk2NSigmaPrPosTOF, trk2NSigmaPrPosTPC); } - histos.fill(HIST("QAbefore/TPC_Nsigma_antipr_all"), Cent, 0, trk2NSigmaPrNegTPC); + histos.fill(HIST("QAbefore/TPC_Nsigma_antipr_all"), cent, 0, trk2NSigmaPrNegTPC); if (hasSubsystemInfo(trk2NSigmaPrNegTOF)) { histos.fill(HIST("QAbefore/TOF_TPC_Map_antipr_all"), trk2NSigmaPrNegTOF, trk2NSigmaPrNegTPC); } - histos.fill(HIST("QAbefore/TPC_Nsigma_pi_all"), Cent, 0, trk2NSigmaPiPosTPC); + histos.fill(HIST("QAbefore/TPC_Nsigma_pi_all"), cent, 0, trk2NSigmaPiPosTPC); if (hasSubsystemInfo(trk2NSigmaPiPosTOF)) { histos.fill(HIST("QAbefore/TOF_TPC_Map_pi_all"), trk2NSigmaPiPosTOF, trk2NSigmaPiPosTPC); } - histos.fill(HIST("QAbefore/TPC_Nsigma_piminus_all"), Cent, 0, trk2NSigmaPiNegTPC); + histos.fill(HIST("QAbefore/TPC_Nsigma_piminus_all"), cent, 0, trk2NSigmaPiNegTPC); if (hasSubsystemInfo(trk2NSigmaPiNegTOF)) { histos.fill(HIST("QAbefore/TOF_TPC_Map_piminus_all"), trk2NSigmaPiNegTOF, trk2NSigmaPiNegTPC); } @@ -937,8 +993,8 @@ struct Xi1530Analysisqa { if (histoConfig.additionalQAplots) { histos.fill(HIST("QAbefore/V0DCATopPV"), trk2ptXi, trk2DCAV0TopPV); - histos.fill(HIST("QAbefore/trkDCAxy_xi"), trk2ptXi, trk2DCAXY); - histos.fill(HIST("QAbefore/trkDCAz_xi"), trk2ptXi, trk2DCAZ); + histos.fill(HIST("QAbefore/trkDCAxy_xi"), trk2ptXi, std::abs(trk2DCAXY)); + histos.fill(HIST("QAbefore/trkDCAz_xi"), trk2ptXi, std::abs(trk2DCAZ)); histos.fill(HIST("QAbefore/V0DCADoughter"), trk2ptXi, trk2DCAV0sDougthers); histos.fill(HIST("QAbefore/CascDCADoughter"), trk2ptXi, trk2DCACascDougthers); histos.fill(HIST("QAbefore/CascDCABachPV"), trk2ptXi, trk2DCABachPV); @@ -957,22 +1013,15 @@ struct Xi1530Analysisqa { } } - if (pidConfig.cUseOnlyTOFTrackPionBachelor && !hasSubsystemInfo(trk2NSigmaPiBachelorTOF)) - continue; - if (pidConfig.cUseOnlyTOFTrackProton && !hasSubsystemInfo(trk2NSigmaPrPosTOF)) - continue; - if (pidConfig.cUseOnlyTOFTrackProton && !hasSubsystemInfo(trk2NSigmaPrNegTOF)) + if (!selectionPIDCascades(trk2)) { continue; - if (pidConfig.cUseOnlyTOFTrackPion && !hasSubsystemInfo(trk2NSigmaPiPosTOF)) - continue; - if (pidConfig.cUseOnlyTOFTrackPion && !hasSubsystemInfo(trk2NSigmaPiNegTOF)) - continue; - if (!selectionPIDCascades(trk2)) - continue; - if (!cascprimaryTrackCut(trk2) || !casctopCut(trk2)) // Primary track selections + } + if (!cascprimaryTrackCut(trk2) || !casctopCut(trk2)) { // Primary track selections continue; - if (trk2ProperLifetime >= cascadeConfig.cMaxProperLifetimeCut) + } + if (trk2ProperLifetime >= cascadeConfig.cMaxProperLifetimeCut) { continue; + } // QA after selections if constexpr (!IsMix) { @@ -981,34 +1030,34 @@ struct Xi1530Analysisqa { if (histoConfig.pidPlots) { if (trk2.sign() < 0) { - histos.fill(HIST("QAafter/TPC_Nsigma_pi_bachelor_all"), Cent, 0, trk2NSigmaPiBachelorTPC); // not exist pt information in resocascade yet. + histos.fill(HIST("QAafter/TPC_Nsigma_pi_bachelor_all"), cent, 0, trk2NSigmaPiBachelorTPC); // not exist pt information in resocascade yet. if (hasSubsystemInfo(trk2NSigmaPiBachelorTOF)) { histos.fill(HIST("QAafter/TOF_TPC_Map_pi_bachelor_all"), trk2NSigmaPiBachelorTOF, trk2NSigmaPiBachelorTPC); } - histos.fill(HIST("QAafter/TPC_Nsigma_pr_all"), Cent, 0, trk2NSigmaPrPosTPC); + histos.fill(HIST("QAafter/TPC_Nsigma_pr_all"), cent, 0, trk2NSigmaPrPosTPC); if (hasSubsystemInfo(trk2NSigmaPrPosTOF)) { histos.fill(HIST("QAafter/TOF_TPC_Map_pr_all"), trk2NSigmaPrPosTOF, trk2NSigmaPrPosTPC); } - histos.fill(HIST("QAafter/TPC_Nsigma_piminus_all"), Cent, 0, trk2NSigmaPiNegTPC); + histos.fill(HIST("QAafter/TPC_Nsigma_piminus_all"), cent, 0, trk2NSigmaPiNegTPC); if (hasSubsystemInfo(trk2NSigmaPiNegTOF)) { histos.fill(HIST("QAafter/TOF_TPC_Map_piminus_all"), trk2NSigmaPiNegTOF, trk2NSigmaPiNegTPC); } } else { - histos.fill(HIST("QAafter/TPC_Nsigma_pi_bachelor_all"), Cent, 0, trk2NSigmaPiBachelorTPC); // not exist pt information in resocascade yet. + histos.fill(HIST("QAafter/TPC_Nsigma_pi_bachelor_all"), cent, 0, trk2NSigmaPiBachelorTPC); // not exist pt information in resocascade yet. if (hasSubsystemInfo(trk2NSigmaPiBachelorTOF)) { histos.fill(HIST("QAafter/TOF_TPC_Map_pi_bachelor_all"), trk2NSigmaPiBachelorTOF, trk2NSigmaPiBachelorTPC); } - histos.fill(HIST("QAafter/TPC_Nsigma_antipr_all"), Cent, 0, trk2NSigmaPrNegTPC); + histos.fill(HIST("QAafter/TPC_Nsigma_antipr_all"), cent, 0, trk2NSigmaPrNegTPC); if (hasSubsystemInfo(trk2NSigmaPrNegTOF)) { histos.fill(HIST("QAafter/TOF_TPC_Map_antipr_all"), trk2NSigmaPrNegTOF, trk2NSigmaPrNegTPC); } - histos.fill(HIST("QAafter/TPC_Nsigma_pi_all"), Cent, 0, trk2NSigmaPiPosTPC); + histos.fill(HIST("QAafter/TPC_Nsigma_pi_all"), cent, 0, trk2NSigmaPiPosTPC); if (hasSubsystemInfo(trk2NSigmaPiPosTOF)) { histos.fill(HIST("QAafter/TOF_TPC_Map_pi_all"), trk2NSigmaPiPosTOF, trk2NSigmaPiPosTPC); } @@ -1016,8 +1065,8 @@ struct Xi1530Analysisqa { } if (histoConfig.additionalQAplots) { histos.fill(HIST("QAafter/V0DCATopPV"), trk2ptXi, trk2DCAV0TopPV); - histos.fill(HIST("QAafter/trkDCAxy_xi"), trk2ptXi, trk2DCAXY); - histos.fill(HIST("QAafter/trkDCAz_xi"), trk2ptXi, trk2DCAZ); + histos.fill(HIST("QAafter/trkDCAxy_xi"), trk2ptXi, std::abs(trk2DCAXY)); + histos.fill(HIST("QAafter/trkDCAz_xi"), trk2ptXi, std::abs(trk2DCAZ)); histos.fill(HIST("QAafter/V0DCADoughter"), trk2ptXi, trk2DCAV0sDougthers); histos.fill(HIST("QAafter/CascDCADoughter"), trk2ptXi, trk2DCACascDougthers); histos.fill(HIST("QAafter/CascDCABachPV"), trk2ptXi, trk2DCABachPV); @@ -1037,15 +1086,15 @@ struct Xi1530Analysisqa { if (additionalConfig.studyStableXi) { if (trk2.sign() < 0) { - histos.fill(HIST("h3XiinvmassDS"), Cent, trk2ptXi, massXiCand, kData); + histos.fill(HIST("h3XiinvmassDS"), cent, trk2ptXi, massXiCand, kData); } else if (trk2.sign() > 0) { - histos.fill(HIST("h3XiinvmassDSAnti"), Cent, trk2ptXi, massXiCand, kData); + histos.fill(HIST("h3XiinvmassDSAnti"), cent, trk2ptXi, massXiCand, kData); } if constexpr (IsMC) { - if (trk2.motherPDG() > 0) { - histos.fill(HIST("h3RecXiinvmass"), Cent, trk2ptXi, massXiCand, kMCReco); - } else { - histos.fill(HIST("h3RecXiinvmassAnti"), Cent, trk2ptXi, massXiCand, kMCReco); + if (trk2.pdgCode() == kXiMinus) { + histos.fill(HIST("h3RecXiinvmass"), cent, trk2ptXi, massXiCand, kMCReco); + } else if (trk2.pdgCode() == -kXiMinus) { + histos.fill(HIST("h3RecXiinvmassAnti"), cent, trk2ptXi, massXiCand, kMCReco); } } } @@ -1058,11 +1107,19 @@ struct Xi1530Analysisqa { auto pionCand = dTracks1.iteratorAt(trk1cand); for (const auto& trk2cand : xiCandateIndicies) { auto xiCand = dTracks2.iteratorAt(trk2cand); + if constexpr (!IsMix) { + const auto trackId = pionCand.trackId(); + const auto& daughterIds = xiCand.cascadeIndices(); + if (trackId >= 0 && (trackId == daughterIds[0] || trackId == daughterIds[1] || trackId == daughterIds[2])) { + continue; + } + } auto pionCandPt = pionCand.pt(); auto xiCandPt = xiCand.pt(); float massXiCand = xiCand.mXi(); - if (additionalConfig.cUseFixedMassXi) + if (additionalConfig.cUseFixedMassXi) { massXiCand = cascadeConfig.cMassXiminus; + } lDecayDaughter1 = LorentzVectorPtEtaPhiMass(pionCandPt, pionCand.eta(), pionCand.phi(), massPi); lDecayDaughter2 = LorentzVectorPtEtaPhiMass(xiCandPt, xiCand.eta(), xiCand.phi(), massXiCand); @@ -1071,27 +1128,35 @@ struct Xi1530Analysisqa { auto lResonanceMass = lResonance.M(); auto lResonancePt = lResonance.Pt(); - if ((lResonance.Rapidity() <= primarytrackConfig.cfgRapidityMinCut) || (lResonance.Rapidity() >= primarytrackConfig.cfgRapidityMaxCut)) + if ((lResonance.Rapidity() <= primarytrackConfig.cfgRapidityMinCut) || (lResonance.Rapidity() >= primarytrackConfig.cfgRapidityMaxCut)) { continue; + } if (additionalConfig.cfgCutsOnMother) { - if (lResonancePt >= additionalConfig.cMaxPtMotherCut) // excluding candidates in overflow + if (lResonancePt >= additionalConfig.cMaxPtMotherCut) { // excluding candidates in overflow continue; - if (lResonanceMass >= additionalConfig.cMaxMinvMotherCut) // excluding candidates in overflow + } + if (lResonanceMass >= additionalConfig.cMaxMinvMotherCut) { // excluding candidates in overflow continue; + } } if (pionCand.sign() * xiCand.sign() < 0) { // Signal candidates if constexpr (!IsMix) { if (pionCand.sign() > 0) { - if (histoConfig.invMass1D) + if (histoConfig.invMass1D) { histos.fill(HIST("Xi1530invmassDS"), lResonanceMass); - histos.fill(HIST("h3Xi1530invmassDS"), Cent, lResonancePt, lResonanceMass, kData); + } + histos.fill(HIST("h3Xi1530invmassDS"), cent, lResonancePt, lResonanceMass, kData); if (additionalConfig.cfgFillRotBkg) { for (int i = 0; i < additionalConfig.cfgNrotBkg; i++) { - auto lRotAngle = additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); - histos.fill(HIST("QAevent/hRotBkg"), lRotAngle); + const auto lRotAngle = additionalConfig.cfgNrotBkg == 1 + ? 0.5f * (additionalConfig.cfgMinRot + additionalConfig.cfgMaxRot) + : additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); + if (histoConfig.eventQA) { + histos.fill(HIST("QAevent/hRotBkg"), lRotAngle); + } if (additionalConfig.cfgRotPion) { lDaughterRot = lDecayDaughter1; ROOT::Math::RotationZ rot(lRotAngle); @@ -1105,18 +1170,27 @@ struct Xi1530Analysisqa { lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); lResonanceRot = lDecayDaughter1 + lDaughterRot; } - histos.fill(HIST("h3Xi1530invmassRotDS"), Cent, lResonanceRot.Pt(), lResonanceRot.M(), kData); + if (additionalConfig.cfgCutsOnMother && + (lResonanceRot.Pt() >= additionalConfig.cMaxPtMotherCut || lResonanceRot.M() >= additionalConfig.cMaxMinvMotherCut)) { + continue; + } + histos.fill(HIST("h3Xi1530invmassRotDS"), cent, lResonanceRot.Pt(), lResonanceRot.M(), kData); } } } else if (pionCand.sign() < 0) { - if (histoConfig.invMass1D) + if (histoConfig.invMass1D) { histos.fill(HIST("Xi1530invmassDSAnti"), lResonanceMass); - histos.fill(HIST("h3Xi1530invmassDSAnti"), Cent, lResonancePt, lResonanceMass, kData); + } + histos.fill(HIST("h3Xi1530invmassDSAnti"), cent, lResonancePt, lResonanceMass, kData); if (additionalConfig.cfgFillRotBkg) { for (int i = 0; i < additionalConfig.cfgNrotBkg; i++) { - auto lRotAngle = additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); - histos.fill(HIST("QAevent/hRotBkg"), lRotAngle); + const auto lRotAngle = additionalConfig.cfgNrotBkg == 1 + ? 0.5f * (additionalConfig.cfgMinRot + additionalConfig.cfgMaxRot) + : additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); + if (histoConfig.eventQA) { + histos.fill(HIST("QAevent/hRotBkg"), lRotAngle); + } if (additionalConfig.cfgRotPion) { lDaughterRot = lDecayDaughter1; ROOT::Math::RotationZ rot(lRotAngle); @@ -1130,46 +1204,48 @@ struct Xi1530Analysisqa { lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); lResonanceRot = lDecayDaughter1 + lDaughterRot; } - histos.fill(HIST("h3Xi1530invmassRotDSAnti"), Cent, lResonanceRot.Pt(), lResonanceRot.M(), kData); + if (additionalConfig.cfgCutsOnMother && + (lResonanceRot.Pt() >= additionalConfig.cMaxPtMotherCut || lResonanceRot.M() >= additionalConfig.cMaxMinvMotherCut)) { + continue; + } + histos.fill(HIST("h3Xi1530invmassRotDSAnti"), cent, lResonanceRot.Pt(), lResonanceRot.M(), kData); } } } } else { if (pionCand.sign() > 0) { - histos.fill(HIST("h3Xi1530invmassME_DS"), Cent, lResonancePt, lResonanceMass, kData); + histos.fill(HIST("h3Xi1530invmassME_DS"), cent, lResonancePt, lResonanceMass, kData); } else if (pionCand.sign() < 0) { - histos.fill(HIST("h3Xi1530invmassME_DSAnti"), Cent, lResonancePt, lResonanceMass, kData); + histos.fill(HIST("h3Xi1530invmassME_DSAnti"), cent, lResonancePt, lResonanceMass, kData); } } if constexpr (IsMC) { - if (std::abs(xiCand.motherPDG()) != kXiStar) - continue; - if (std::abs(pionCand.pdgCode()) != kPiPlus || std::abs(xiCand.pdgCode()) != kXiMinus) + if (std::abs(xiCand.motherPDG()) != kXiStar || pionCand.motherPDG() != xiCand.motherPDG()) { continue; - if (pionCand.motherId() != xiCand.motherId()) + } + const int motherSign = xiCand.motherPDG() > 0 ? 1 : -1; + if (pionCand.pdgCode() != motherSign * kPiPlus || xiCand.pdgCode() != motherSign * kXiMinus) { continue; - auto lResonancePtMC = xiCand.motherPt(); - if (additionalConfig.cUseTruthRapidity) + } + if (xiCand.motherId() < 0 || pionCand.motherId() != xiCand.motherId()) { continue; - if ((xiCand.motherRap() >= primarytrackConfig.cfgRapidityMaxCut) || (xiCand.motherRap() <= primarytrackConfig.cfgRapidityMinCut)) + } + if (additionalConfig.cMCInjection && !injectedMotherIds.contains(xiCand.motherId())) { continue; - if (histoConfig.truthQA) { - float trk1DCAXY = -1.f; - float trk1DCAZ = -1.f; - float trk1NSigmaPiTPC = -999.f; - float trk1NSigmaPiTOF = -999.f; - if constexpr (IsResoMicrotrack) { - trk1DCAXY = o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(pionCand.trackSelectionFlags()); - trk1DCAZ = o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(pionCand.trackSelectionFlags()); - trk1NSigmaPiTPC = o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(pionCand.pidNSigmaPiFlag()); - trk1NSigmaPiTOF = o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(pionCand.pidNSigmaPiFlag()); - } else { - trk1DCAXY = pionCand.dcaXY(); - trk1DCAZ = pionCand.dcaZ(); - trk1NSigmaPiTPC = pionCand.tpcNSigmaPi(); - trk1NSigmaPiTOF = pionCand.tofNSigmaPi(); + } + auto lResonancePtMC = xiCand.motherPt(); + if (additionalConfig.cUseTruthRapidity) { + const bool passesTruthRapidity = xiCand.motherRap() > primarytrackConfig.cfgRapidityMinCut && xiCand.motherRap() < primarytrackConfig.cfgRapidityMaxCut; + if (!passesTruthRapidity) { + continue; } + } + if (histoConfig.truthQA) { + const float trk1DCAXY = pionCand.dcaXY(); + const float trk1DCAZ = pionCand.dcaZ(); + const float trk1NSigmaPiTPC = pionCand.tpcNSigmaPi(); + const float trk1NSigmaPiTOF = pionCand.tofNSigmaPi(); auto trk2DCAXY = xiCand.dcaXYCascToPV(); auto trk2DCAZ = xiCand.dcaZCascToPV(); @@ -1207,80 +1283,88 @@ struct Xi1530Analysisqa { float trk2NSigmaPiPosTOF = xiCand.daughterTOFNSigmaPosPi(); float trk2NSigmaPiNegTOF = xiCand.daughterTOFNSigmaNegPi(); - histos.fill(HIST("QAMCTrue/trkDCAxy_pi"), pionCandPt, trk1DCAXY); - histos.fill(HIST("QAMCTrue/trkDCAz_pi"), pionCandPt, trk1DCAZ); - histos.fill(HIST("QAMCTrue/V0DCATopPV"), xiCandPt, trk2DCAV0TopPV); - histos.fill(HIST("QAMCTrue/trkDCAxy_xi"), xiCandPt, trk2DCAXY); - histos.fill(HIST("QAMCTrue/trkDCAz_xi"), xiCandPt, trk2DCAZ); - - histos.fill(HIST("QAMCTrue/V0DCADoughter"), xiCandPt, trk2DCAV0sDougthers); - histos.fill(HIST("QAMCTrue/CascDCADoughter"), xiCandPt, trk2DCACascDougthers); - histos.fill(HIST("QAMCTrue/CascDCABachPV"), xiCandPt, trk2DCABachPV); - histos.fill(HIST("QAMCTrue/CascDCAPosPV"), xiCandPt, trk2DCAPosPV); - histos.fill(HIST("QAMCTrue/CascDCANegPV"), xiCandPt, trk2DCANegPV); - histos.fill(HIST("QAMCTrue/V0CosPA"), xiCandPt, 1. - trk2V0CosPA); - histos.fill(HIST("QAMCTrue/CascCosPA"), xiCandPt, 1. - trk2CascCosPA); - histos.fill(HIST("QAMCTrue/V0Radius"), xiCandPt, trk2V0Radius); - histos.fill(HIST("QAMCTrue/CascRadius"), xiCandPt, trk2CascRadius); - histos.fill(HIST("QAMCTrue/V0Mass"), xiCandPt, massLambdaCand); - histos.fill(HIST("QAMCTrue/CascMass"), xiCandPt, massXiCand); - histos.fill(HIST("QAMCTrue/ProperLifetime"), xiCandPt, trk2ProperLifetime); - histos.fill(HIST("QAMCTrue/NCrossedRowsPos"), xiCandPt, trks2NCrossedRowsPos); - histos.fill(HIST("QAMCTrue/NCrossedRowsNeg"), xiCandPt, trks2NCrossedRowsNeg); - histos.fill(HIST("QAMCTrue/NCrossedRowsBach"), xiCandPt, trks2NCrossedRowsBach); - - histos.fill(HIST("QAMCTrue/TPC_Nsigma_pi_first_all"), Cent, pionCandPt, trk1NSigmaPiTPC); - if (hasSubsystemInfo(trk1NSigmaPiTOF)) { - histos.fill(HIST("QAMCTrue/TOF_Nsigma_pi_first_all"), Cent, pionCandPt, trk1NSigmaPiTOF); - histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pi_first_all"), trk1NSigmaPiTOF, trk1NSigmaPiTPC); + if (histoConfig.additionalQAplots) { + histos.fill(HIST("QAMCTrue/trkDCAxy_pi"), pionCandPt, std::abs(trk1DCAXY)); + histos.fill(HIST("QAMCTrue/trkDCAz_pi"), pionCandPt, std::abs(trk1DCAZ)); + histos.fill(HIST("QAMCTrue/V0DCATopPV"), xiCandPt, trk2DCAV0TopPV); + histos.fill(HIST("QAMCTrue/trkDCAxy_xi"), xiCandPt, std::abs(trk2DCAXY)); + histos.fill(HIST("QAMCTrue/trkDCAz_xi"), xiCandPt, std::abs(trk2DCAZ)); + + histos.fill(HIST("QAMCTrue/V0DCADoughter"), xiCandPt, trk2DCAV0sDougthers); + histos.fill(HIST("QAMCTrue/CascDCADoughter"), xiCandPt, trk2DCACascDougthers); + histos.fill(HIST("QAMCTrue/CascDCABachPV"), xiCandPt, trk2DCABachPV); + histos.fill(HIST("QAMCTrue/CascDCAPosPV"), xiCandPt, trk2DCAPosPV); + histos.fill(HIST("QAMCTrue/CascDCANegPV"), xiCandPt, trk2DCANegPV); + histos.fill(HIST("QAMCTrue/V0CosPA"), xiCandPt, 1. - trk2V0CosPA); + histos.fill(HIST("QAMCTrue/CascCosPA"), xiCandPt, 1. - trk2CascCosPA); + histos.fill(HIST("QAMCTrue/V0Radius"), xiCandPt, trk2V0Radius); + histos.fill(HIST("QAMCTrue/CascRadius"), xiCandPt, trk2CascRadius); + histos.fill(HIST("QAMCTrue/V0Mass"), xiCandPt, massLambdaCand); + histos.fill(HIST("QAMCTrue/CascMass"), xiCandPt, xiCand.mXi()); + histos.fill(HIST("QAMCTrue/ProperLifetime"), xiCandPt, trk2ProperLifetime); + histos.fill(HIST("QAMCTrue/NCrossedRowsPos"), xiCandPt, trks2NCrossedRowsPos); + histos.fill(HIST("QAMCTrue/NCrossedRowsNeg"), xiCandPt, trks2NCrossedRowsNeg); + histos.fill(HIST("QAMCTrue/NCrossedRowsBach"), xiCandPt, trks2NCrossedRowsBach); } - histos.fill(HIST("QAMCTrue/TPC_Nsigma_pi_bachelor_all"), Cent, 0, trk2NSigmaPiBachelorTPC); // not exist pt information in resocascade yet. - if (hasSubsystemInfo(trk2NSigmaPiBachelorTOF)) { - histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pi_bachelor_all"), trk2NSigmaPiBachelorTOF, trk2NSigmaPiBachelorTPC); - } + if (histoConfig.pidPlots) { + histos.fill(HIST("QAMCTrue/TPC_Nsigma_pi_first_all"), cent, pionCandPt, trk1NSigmaPiTPC); + if (pionCand.hasTOF() && !std::isnan(trk1NSigmaPiTOF)) { + histos.fill(HIST("QAMCTrue/TOF_Nsigma_pi_first_all"), cent, pionCandPt, trk1NSigmaPiTOF); + histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pi_first_all"), trk1NSigmaPiTOF, trk1NSigmaPiTPC); + } - histos.fill(HIST("QAMCTrue/TPC_Nsigma_pr_all"), Cent, 0, trk2NSigmaPrPosTPC); - if (hasSubsystemInfo(trk2NSigmaPrPosTOF)) { - histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pr_all"), trk2NSigmaPrPosTOF, trk2NSigmaPrPosTPC); - } - histos.fill(HIST("QAMCTrue/TPC_Nsigma_antipr_all"), Cent, 0, trk2NSigmaPrNegTPC); - if (hasSubsystemInfo(trk2NSigmaPrNegTOF)) { - histos.fill(HIST("QAMCTrue/TOF_TPC_Map_antipr_all"), trk2NSigmaPrNegTOF, trk2NSigmaPrNegTPC); - } + histos.fill(HIST("QAMCTrue/TPC_Nsigma_pi_bachelor_all"), cent, 0, trk2NSigmaPiBachelorTPC); // not exist pt information in resocascade yet. + if (hasSubsystemInfo(trk2NSigmaPiBachelorTOF)) { + histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pi_bachelor_all"), trk2NSigmaPiBachelorTOF, trk2NSigmaPiBachelorTPC); + } - histos.fill(HIST("QAMCTrue/TPC_Nsigma_piminus_all"), Cent, 0, trk2NSigmaPiNegTPC); - if (hasSubsystemInfo(trk2NSigmaPiNegTOF)) { - histos.fill(HIST("QAMCTrue/TOF_TPC_Map_piminus_all"), trk2NSigmaPiNegTOF, trk2NSigmaPiNegTPC); - } - histos.fill(HIST("QAMCTrue/TPC_Nsigma_pi_all"), Cent, 0, trk2NSigmaPiPosTPC); - if (hasSubsystemInfo(trk2NSigmaPiPosTOF)) { - histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pi_all"), trk2NSigmaPiPosTOF, trk2NSigmaPiPosTPC); + if (xiCand.sign() < 0) { + histos.fill(HIST("QAMCTrue/TPC_Nsigma_pr_all"), cent, 0, trk2NSigmaPrPosTPC); + if (hasSubsystemInfo(trk2NSigmaPrPosTOF)) { + histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pr_all"), trk2NSigmaPrPosTOF, trk2NSigmaPrPosTPC); + } + histos.fill(HIST("QAMCTrue/TPC_Nsigma_piminus_all"), cent, 0, trk2NSigmaPiNegTPC); + if (hasSubsystemInfo(trk2NSigmaPiNegTOF)) { + histos.fill(HIST("QAMCTrue/TOF_TPC_Map_piminus_all"), trk2NSigmaPiNegTOF, trk2NSigmaPiNegTPC); + } + } else { + histos.fill(HIST("QAMCTrue/TPC_Nsigma_antipr_all"), cent, 0, trk2NSigmaPrNegTPC); + if (hasSubsystemInfo(trk2NSigmaPrNegTOF)) { + histos.fill(HIST("QAMCTrue/TOF_TPC_Map_antipr_all"), trk2NSigmaPrNegTOF, trk2NSigmaPrNegTPC); + } + histos.fill(HIST("QAMCTrue/TPC_Nsigma_pi_all"), cent, 0, trk2NSigmaPiPosTPC); + if (hasSubsystemInfo(trk2NSigmaPiPosTOF)) { + histos.fill(HIST("QAMCTrue/TOF_TPC_Map_pi_all"), trk2NSigmaPiPosTOF, trk2NSigmaPiPosTPC); + } + } } } // truthQA // MC histograms if (xiCand.motherPDG() > 0) { - histos.fill(HIST("Xi1530Rec"), lResonancePt, Cent); + histos.fill(HIST("Xi1530Rec"), lResonancePt, cent); histos.fill(HIST("Xi1530Recinvmass"), lResonanceMass); - histos.fill(HIST("h3RecXi1530invmass"), Cent, lResonancePt, lResonanceMass, lResonancePtMC); + histos.fill(HIST("h3RecXi1530invmass"), cent, lResonancePt, lResonanceMass, lResonancePtMC); } else { - histos.fill(HIST("Xi1530RecAnti"), lResonancePt, Cent); + histos.fill(HIST("Xi1530RecAnti"), lResonancePt, cent); histos.fill(HIST("Xi1530Recinvmass"), lResonanceMass); - histos.fill(HIST("h3RecXi1530invmassAnti"), Cent, lResonancePt, lResonanceMass, lResonancePtMC); + histos.fill(HIST("h3RecXi1530invmassAnti"), cent, lResonancePt, lResonanceMass, lResonancePtMC); } } // is MC } else { // Bkg candidates if constexpr (!IsMix) { if (pionCand.sign() < 0) { - if (histoConfig.invMass1D) + if (histoConfig.invMass1D) { histos.fill(HIST("Xi1530invmassLS"), lResonanceMass); - histos.fill(HIST("h3Xi1530invmassLS"), Cent, lResonancePt, lResonanceMass, kLS); + } + histos.fill(HIST("h3Xi1530invmassLS"), cent, lResonancePt, lResonanceMass, kLS); } else if (pionCand.sign() > 0) { - if (histoConfig.invMass1D) + if (histoConfig.invMass1D) { histos.fill(HIST("Xi1530invmassLSAnti"), lResonanceMass); - histos.fill(HIST("h3Xi1530invmassLSAnti"), Cent, lResonancePt, lResonanceMass, kLS); + } + histos.fill(HIST("h3Xi1530invmassLSAnti"), cent, lResonancePt, lResonanceMass, kLS); } } } // -> End if signal or bkg @@ -1288,22 +1372,17 @@ struct Xi1530Analysisqa { } // -> End loop over pion and xi candidates } // -> End fillHistograms - void processData(aod::ResoCollision const& resoCollision, - aod::ResoTracks const& resoTracks, + void processData(ResoCollisions::iterator const& resoCollision, + ResoTracks const& resoTracks, aod::ResoCascades const& cascTracks) { auto inCent = resoCollision.cent(); - auto multiplicity = 0.f; + // Version 001 stores the estimator chosen by cfgMultiplicityEstimator in the producer. + const auto multiplicity = resoCollision.multiplicity(); - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision + if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) { // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision return; - if (additionalConfig.cMultNTracksPVFull) - multiplicity = resoCollision.multNTracksPV(); - else if (additionalConfig.cMultNTracksPVeta1) - multiplicity = resoCollision.multNTracksPVeta1(); - else if (additionalConfig.cMultNTracksPVetaHalf) - multiplicity = resoCollision.multNTracksPVetaHalf(); - + } if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCent"), inCent, multiplicity); } @@ -1313,74 +1392,90 @@ struct Xi1530Analysisqa { // Calculate numerator for the Acceptance x Efficiency void processMC(ResoMCCols::iterator const& resoCollision, soa::Join const& cascTracks, - soa::Join const& resoTracks) + soa::Join const& resoTracks, + aod::ResoMCParents_001 const& resoParents) { auto inCent = resoCollision.cent(); - float multiplicity = 0.f; - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision - return; - if (additionalConfig.cMultNTracksPVFull) - multiplicity = resoCollision.multNTracksPV(); - else if (additionalConfig.cMultNTracksPVeta1) - multiplicity = resoCollision.multNTracksPVeta1(); - else if (additionalConfig.cMultNTracksPVetaHalf) - multiplicity = resoCollision.multNTracksPVetaHalf(); - - if (!resoCollision.isInAfterAllCuts()) // MC event selection, all cuts missing vtx cut + const auto multiplicity = resoCollision.multiplicity(); + if ((additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) || + (additionalConfig.cMCINELgt0 && !resoCollision.isINELgt0()) || + (additionalConfig.cMCVtxIn10 && !resoCollision.isVtxIn10())) { return; + } + // The modular producer writes only selected reconstructed collisions. if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCent"), inCent, multiplicity); } - fillHistograms(resoCollision, inCent, resoTracks, cascTracks); + const auto injectedMotherIds = getInjectedMotherIds(resoParents); + fillHistograms(resoCollision, inCent, resoTracks, cascTracks, nullptr, injectedMotherIds); + } + + void processMCMicro(ResoMCCols::iterator const& resoCollision, + soa::Join const& cascTracks, + soa::Join const& resoTracks, + aod::ResoMCParents_001 const& reParent) + { + if ((additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) || + (additionalConfig.cMCINELgt0 && !resoCollision.isINELgt0()) || + (additionalConfig.cMCVtxIn10 && !resoCollision.isVtxIn10())) { + return; + } + if (histoConfig.multQA) { + histos.fill(HIST("multQA/h2MultCent"), resoCollision.cent(), resoCollision.multiplicity()); + } + const auto injectedMotherIds = getInjectedMotherIds(reParent); + fillHistograms(resoCollision, resoCollision.cent(), resoTracks, cascTracks, nullptr, injectedMotherIds); } // Calculate denominator for the Acceptance x Efficiency, actually it is not Trueth info... void processMCTrue(ResoMCCols::iterator const& resoCollision, - aod::ResoMCParents const& resoParents) + aod::ResoMCParents_001 const& resoParents) { auto multiplicity = resoCollision.mcMultiplicity(); auto inCent = resoCollision.cent(); - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision - return; - if (!resoCollision.isInAfterAllCuts()) + if ((additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) || + (additionalConfig.cMCINELgt0 && !resoCollision.isINELgt0()) || + (additionalConfig.cMCVtxIn10 && !resoCollision.isVtxIn10())) { return; + } if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCentMC"), inCent, multiplicity); } for (const auto& part : resoParents) { // loop over all pre-filtered MC particles - if (std::abs(part.pdgCode()) != kXiStar) + if (std::abs(part.pdgCode()) != kXiStar) { continue; - if ((part.y() <= primarytrackConfig.cfgRapidityMinCut) || (part.y() >= primarytrackConfig.cfgRapidityMaxCut)) + } + if (additionalConfig.cMCInjection && !IsInjectedParent(part)) { continue; + } + if ((part.y() <= primarytrackConfig.cfgRapidityMinCut) || (part.y() >= primarytrackConfig.cfgRapidityMaxCut)) { + continue; + } bool pass1 = std::abs(part.daughterPDG1()) == kPiPlus || std::abs(part.daughterPDG2()) == kPiPlus; bool pass2 = std::abs(part.daughterPDG1()) == kXiMinus || std::abs(part.daughterPDG2()) == kXiMinus; - if (!pass1 || !pass2) + if (!pass1 || !pass2) { continue; + } - if (part.pdgCode() > 0) + if (part.pdgCode() > 0) { histos.fill(HIST("h3Xi1530Gen"), part.pt(), inCent, multiplicity); - else + } else { histos.fill(HIST("h3Xi1530GenAnti"), part.pt(), inCent, multiplicity); + } } } - void processDataMicro(aod::ResoCollision const& resoCollision, - aod::ResoMicroTracks const& resomicrotracks, + void processDataMicro(ResoCollisions::iterator const& resoCollision, + ResoMicroTracks const& resomicrotracks, aod::ResoCascades const& cascTracks) { - float multiplicity = 0.f; + const auto multiplicity = resoCollision.multiplicity(); auto inCent = resoCollision.cent(); - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision + if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) { // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision return; - if (additionalConfig.cMultNTracksPVFull) - multiplicity = resoCollision.multNTracksPV(); - else if (additionalConfig.cMultNTracksPVeta1) - multiplicity = resoCollision.multNTracksPVeta1(); - else if (additionalConfig.cMultNTracksPVetaHalf) - multiplicity = resoCollision.multNTracksPVetaHalf(); - + } if (histoConfig.multQA) { histos.fill(HIST("multQA/h2MultCent"), inCent, multiplicity); } @@ -1388,64 +1483,98 @@ struct Xi1530Analysisqa { } using BinningTypeVtxZT0M = ColumnBinningPolicy; - void processMEMicro(aod::ResoCollisions const& resoCollisions, - aod::ResoMicroTracks const& resomicrotracks, + void processMEMicro(ResoCollisions const& resoCollisions, + ResoMicroTracks const& resomicrotracks, aod::ResoCascades const& cascTracks) { + if (mixingConfig.nEvtMixing <= 0) { + return; + } auto tracksTuple = std::make_tuple(resomicrotracks, cascTracks); - BinningTypeVtxZT0M colBinning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}, true}; - Pair pairs{colBinning, mixingConfig.nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; + BinningTypeVtxZT0M colBinning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}}; + Pair pairs{colBinning, mixingConfig.nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { - float multiplicity = 0.f; + const auto multiplicity = collision1.multiplicity(); auto inCent = collision1.cent(); - if (additionalConfig.cRecoINELgt0 && !collision1.isRecINELgt0()) // Check reco INELgt0 (at least one PV track in |eta| < 1) about the collision + if (additionalConfig.cRecoINELgt0 && (!collision1.isRecINELgt0() || !collision2.isRecINELgt0())) { continue; - if (additionalConfig.cMultNTracksPVFull) - multiplicity = collision1.multNTracksPV(); - else if (additionalConfig.cMultNTracksPVeta1) - multiplicity = collision1.multNTracksPVeta1(); - else if (additionalConfig.cMultNTracksPVetaHalf) - multiplicity = collision1.multNTracksPVetaHalf(); - + } + if (!std::isfinite(collision1.bMagField()) || collision1.bMagField() != collision2.bMagField()) { + continue; + } if (histoConfig.multQA) { - histos.fill(HIST("multQA/h2MultCent"), inCent, multiplicity); + histos.fill(HIST("multQA/h2MultCentME"), inCent, multiplicity); } - fillHistograms(collision1, inCent, tracks1, tracks2); + const MixingCollision cascadeCollision{.x = collision2.posX(), .y = collision2.posY(), .z = collision2.posZ(), .centrality = collision2.cent(), .multiplicity = collision2.multiplicity(), .index = collision2.globalIndex(), .recINELgt0 = collision2.isRecINELgt0()}; + fillHistograms(collision1, inCent, tracks1, tracks2, &cascadeCollision); } } - // void processMEDF(aod::ResoCollisionDFs const& resoCollisions, aod::ResoTrackDFs const& resotracks, aod::ResoCascadeDFs const& cascTracks) - // { - - /* Will be implemented once the DataFrame for cascade is ready. */ - - // auto tracksTuple = std::make_tuple(resotracks, cascTracks); - - // BinningTypeVtxZT0M colBinning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}, true}; - // Pair pairs{colBinning, mixingConfig.nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; - - // for (const auto& [collision1, tracks1, collision2, tracks2] : pairs) { + static std::shared_ptr copyMixingTracks(ResoMicroTracks const& tracks) + { + const auto source = tracks.asArrowTableConstrained(); + if (source->num_rows() == 0) { + return arrow::Table::MakeEmpty(source->schema()).ValueOrDie(); + } + std::vector> columns; + columns.reserve(source->num_columns()); + for (const auto& column : source->columns()) { + // Concatenate copies only the slice's rows, releasing the source DF buffers. + columns.push_back(std::make_shared(arrow::Concatenate(column->chunks()).ValueOrDie())); + } + return arrow::Table::Make(source->schema(), columns); + } - // float multiplicity = 0.f; - // auto inCent = collision1.cent(); - // if (histoConfig.multQA) { - // histos.fill(HIST("multQA/h2MultCent"), inCent, multiplicity); - // } - // fillHistograms(collision1, inCent, tracks1, tracks2); - // } - // } + // Cross-DF mixing on module v001 input; no legacy Reso*DF merger is needed. + // History is local to this task instance. Use inputs from one run/condition set. + void processMEDF(ResoCollisions::iterator const& collision, + ResoMicroTracks const& tracks, + aod::ResoCascades const& cascades) + { + if (mixingConfig.nEvtMixing <= 0 || !std::isfinite(collision.bMagField())) { + return; + } + if (mixingBField != collision.bMagField()) { + mixingPools.clear(); + mixingBField = collision.bMagField(); + } + BinningTypeVtxZT0M binning{{mixingConfig.cfgVtxBins, mixingConfig.cfgMultBins}}; + const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); + if (bin < 0) { + return; + } + const MixingCollision current{.x = collision.posX(), .y = collision.posY(), .z = collision.posZ(), .centrality = collision.cent(), .multiplicity = collision.multiplicity(), .index = collision.globalIndex(), .recINELgt0 = collision.isRecINELgt0()}; + auto& pool = mixingPools[bin]; + for (const auto& previous : pool) { + const auto& anchor = previous.collision; + if (additionalConfig.cRecoINELgt0 && (!anchor.recINELgt0 || !current.recINELgt0)) { + continue; + } + if (histoConfig.multQA) { + histos.fill(HIST("multQA/h2MultCentME"), anchor.centrality, anchor.multiplicity); + } + // Preserve the old pion -> new Cascade direction and the Cascade's own PV. + fillHistograms(anchor, anchor.centrality, ResoMicroTracks{previous.tracks}, cascades, ¤t); + } + // Insert after mixing: DF-local row IDs may repeat, but no event mixes with itself. + pool.push_back({current, copyMixingTracks(tracks)}); + while (pool.size() > static_cast(mixingConfig.nEvtMixing.value)) { + pool.pop_front(); + } + } PROCESS_SWITCH(Xi1530Analysisqa, processData, "Process Event for Data", false); PROCESS_SWITCH(Xi1530Analysisqa, processMC, "Process Event for MC (Reconstructed)", false); + PROCESS_SWITCH(Xi1530Analysisqa, processMCMicro, "Process Event for MC (Reconstructed MicroTrack 001)", false); PROCESS_SWITCH(Xi1530Analysisqa, processMCTrue, "Process Event for MC (Generated)", false); PROCESS_SWITCH(Xi1530Analysisqa, processDataMicro, "Process Event for Data (MicroTrack)", false); PROCESS_SWITCH(Xi1530Analysisqa, processMEMicro, "Process EventMixing (MicroTrack) ", false); - // PROCESS_SWITCH(Xi1530Analysisqa, processMEDF, "Process EventMixing (DataFrame) ", false); + PROCESS_SWITCH(Xi1530Analysisqa, processMEDF, "Process cross-DF EventMixing (MicroTrack 001)", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx b/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx new file mode 100644 index 00000000000..347058612ad --- /dev/null +++ b/PWGLF/Tasks/Resonances/xi1530kaoncorrelation.cxx @@ -0,0 +1,1067 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file xi1530kaoncorrelation.cxx +/// \brief Xi(1530)^0--K femtoscopic correlation task on compact reduced tables. +/// \author Prottay Das, prottay.das@cern.ch +/// +/// Some information: Same-event and mixed-event processing are intentionally implemented as +/// separate PROCESS_SWITCH functions. The task reconstructs all downstream +/// selections from the producer bitmaps/raw kaon PID, keeps M(Xi pi) as an +/// explicit axis for signal extraction in each k* bin, and uses the +/// reconstructed Xi-pi mass in the k* calculation. +/// +/// Default PID choices: +/// * prompt Xi* pion: TPC-only, 3 sigma; +/// * Xi bachelor pion: TPC-only, 3 sigma; +/// * Lambda pion/proton: TPC-only, 4 sigma; +/// * external K: TPC-only below pT=0.5 GeV/c and circular TPC+TOF above, +/// requiring TOF above threshold. +/// +/// The producer also stores alternative prompt/bachelor PID decisions and raw +/// kaon TPC/TOF PID, so those choices I kept configurable downstream (will change later). + +#include "PWGLF/DataModel/LFReducedXi1530KaonTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::aod; +using namespace o2::framework; +using namespace o2::soa; + +namespace +{ +constexpr float kKaonMass = o2::constants::physics::MassKPlus; +constexpr float kPi = 3.14159265358979323846f; +constexpr float kTwoPi = 2.f * kPi; + +inline bool hasTrackBit(uint64_t word, redxistark::XiStarTrackSelBit bit) +{ + return (word & redxistark::xiStarTrackSelMask(bit)) != 0u; +} + +inline bool hasTopoBit(uint64_t word, redxistark::XiStarTopoSelBit bit) +{ + return (word & redxistark::xiStarTopoSelMask(bit)) != 0u; +} + +inline bool hasKaonBit(uint8_t word, redxistarkaon::KaonSelBit bit) +{ + return (word & redxistarkaon::kaonSelMask(bit)) != 0u; +} + +inline float wrapToPi(float x) +{ + while (x > kPi) { + x -= kTwoPi; + } + while (x <= -kPi) { + x += kTwoPi; + } + return x; +} +} // namespace + +struct xi1530kaoncorrelation { + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // --------------------------------------------------------------------------- + // Xi(1530) selection reconstructed from the producer bitmaps. + // + // Default daughter PID is TPC-only; producer-stored alternatives remain selectable. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "xiStarSelection"; + + Configurable promptPionPIDMode{"promptPionPIDMode", 2, "Prompt pion PID mode: 0=availability hybrid, 1=pT-threshold hybrid, 2=TPC-only"}; + Configurable promptPionPIDWP{"promptPionPIDWP", 0, "Prompt pion TPC PID: 0=3sigma, 1=4sigma, 2=5sigma, 3=6sigma"}; + Configurable bachelorPionPIDMode{"bachelorPionPIDMode", 1, "Xi bachelor pion PID mode: 0=availability hybrid, 1=TPC-only"}; + Configurable bachelorPionPIDWP{"bachelorPionPIDWP", 0, "Xi bachelor pion PID: 0=3sigma, 1=4sigma, 2=4.8sigma, 3=5sigma, 4=6sigma; 4.8 is TPC-only"}; + Configurable lambdaPionPIDWP{"lambdaPionPIDWP", 1, "Lambda pion TPC PID: 0=3sigma, 1=4sigma, 2=4.8sigma, 3=5sigma, 4=6sigma"}; + Configurable lambdaProtonPIDWP{"lambdaProtonPIDWP", 1, "Lambda proton TPC PID: 0=3sigma, 1=4sigma, 2=5sigma, 3=6sigma"}; + + Configurable promptPionRowsWP{"promptPionRowsWP", 1, "Prompt-pion rows: 0=>70, 1=>80, 2=>90"}; + Configurable promptPionDCAzWP{"promptPionDCAzWP", 1, "Prompt-pion DCAz: 0=loose 1 cm bit, 1=producer default bit, 2=tight 0.1 cm bit"}; + Configurable v0DaughterRowsWP{"v0DaughterRowsWP", 1, "Cascade/V0 daughter rows: 0=all cascade daughters broad bit, 1=V0 rows nominal bit, 2=Var1, 3=Var2"}; + + Configurable v0PionDcaPVWP{"v0PionDcaPVWP", 3, "V0 pion DCA-to-PV bit: 0=0.05, 1=0.06, 2=0.10, 3=0.20 cm"}; + Configurable v0ProtonDcaPVWP{"v0ProtonDcaPVWP", 2, "V0 proton DCA-to-PV bit: 0=0.05, 1=0.06, 2=0.07, 3=0.10 cm"}; + Configurable v0DcaPVWP{"v0DcaPVWP", 1, "Minimum Lambda/V0 DCA-to-PV bit: 0=0, 1=0.03, 2=0.10 cm"}; + Configurable v0DcaDaughtersWP{"v0DcaDaughtersWP", 0, "V0 daughter DCA/native metric bit: 0=<1.0, 1=<0.5, 2=<0.1"}; + Configurable v0CosPAWP{"v0CosPAWP", 4, "V0 cosPA bit: 0=0.97, 1=0.98, 2=0.9876, 3=0.99, 4=0.995"}; + Configurable v0RadiusWP{"v0RadiusWP", 4, "V0 minimum-radius bit: 0=0.9, 1=1.01, 2=1.2, 3=2.5, 4=3.0 cm"}; + Configurable lambdaMassWP{"lambdaMassWP", 1, "Lambda mass-window bit: 0=10, 1=8, 2=6, 3=11.6 MeV/c2"}; + Configurable applyV0LifetimeCut{"applyV0LifetimeCut", true, "Apply producer Lambda-lifetime bit"}; + Configurable v0LifetimeWP{"v0LifetimeWP", 0, "Lambda lifetime bit: 0=default, 1=Var1, 2=Var2; thresholds are those used in producer JSON"}; + + Configurable cascBachelorDcaPVWP{"cascBachelorDcaPVWP", 1, "Xi bachelor DCA-to-PV bit: 0=0.05, 1=0.06, 2=0.10 cm"}; + Configurable cascDcaDaughtersWP{"cascDcaDaughtersWP", 1, "Cascade daughter DCA/native metric bit: 0=<1.0, 1=<0.25, 2=<0.20"}; + Configurable cascCosPAWP{"cascCosPAWP", 1, "Cascade cosPA bit: 0=0.97, 1=0.98, 2=0.9947, 3=0.995"}; + Configurable cascRadiusWP{"cascRadiusWP", 2, "Cascade minimum-radius bit: 0=0.9, 1=1.0, 2=1.01, 3=1.3 cm"}; + Configurable xiMassWP{"xiMassWP", 1, "Xi mass-window bit: 0=10, 1=8, 2=6 MeV/c2"}; + Configurable applyXiLifetimeCut{"applyXiLifetimeCut", true, "Apply producer Xi-lifetime bit"}; + Configurable xiLifetimeWP{"xiLifetimeWP", 0, "Xi lifetime bit: 0=default, 1=Var1, 2=Var2; thresholds are those used in producer JSON"}; + + Configurable requirePPBachBaryonDCAxy{"requirePPBachBaryonDCAxy", false, "Require pp-Xi bachelor-baryon DCAxy bit"}; + Configurable requirePPXiRapidity{"requirePPXiRapidity", false, "Require pp-Xi rapidity bit"}; + } xiCuts; + + // --------------------------------------------------------------------------- + // External K selection. + // Raw TPC/TOF n-sigma values are stored by the producer. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "kaonSelection"; + + Configurable ptMin{"ptMin", 0.15f, "Minimum external-K pT (GeV/c)"}; + Configurable ptMax{"ptMax", 100.f, "Maximum external-K pT (GeV/c)"}; + Configurable tpcRowsMin{"tpcRowsMin", 80, "Minimum TPC crossed rows, strict >"}; + + Configurable dcaXYWP{"dcaXYWP", 0, "K DCAxy bit: 0=default, 1=Var1, 2=Var2"}; + Configurable dcaZWP{"dcaZWP", 0, "K DCAz bit: 0=default, 1=Var1, 2=Var2"}; + + Configurable pidMode{"pidMode", 2, "K PID mode: 0=TPC-only, 1=availability hybrid, 2=pT-threshold hybrid"}; + Configurable pidPtThreshold{"pidPtThreshold", 0.5f, "pT threshold between TPC-only and circular TPC+TOF PID (GeV/c)"}; + Configurable tpcNSigmaMax{"tpcNSigmaMax", 3.f, "Maximum |TPC nSigma_K| below threshold"}; + Configurable circularNSigmaMax{"circularNSigmaMax", 3.f, "Maximum sqrt(TPC^2+TOF^2) above threshold"}; + Configurable requireTOFAboveThreshold{"requireTOFAboveThreshold", true, "Require TOF for K at/above PID threshold"}; + } kaonCuts; + + // --------------------------------------------------------------------------- + // Pair/channel selections. + // --------------------------------------------------------------------------- + struct : ConfigurableGroup { + std::string prefix = "pairSelection"; + + Configurable fillMatter{"fillMatter", true, "Process Xi*-K- matter channels"}; + Configurable fillAntimatter{"fillAntimatter", true, "Process anti-Xi*-K+ antimatter channels"}; + Configurable fillLikeSignControl{"fillLikeSignControl", true, "Keep like-sign Xi-pi control channels (+/-2)"}; + Configurable applySharedTrackCleaning{"applySharedTrackCleaning", true, "Reject SE kaons sharing any Xi* daughter track"}; + } pairCuts; + + // --------------------------------------------------------------------------- + // Event mixing. + // --------------------------------------------------------------------------- + Configurable nEvtMixing{"nEvtMixing", 10, "Number of previous events to mix"}; + ConfigurableAxis cfgMixVtxBins{"cfgMixVtxBins", {VARIABLE_WIDTH, -10.0, -8.0, -6.0, -4.0, -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0}, "Mixing bins in z vertex (cm)"}; + ConfigurableAxis cfgMixFT0MBins{"cfgMixFT0MBins", {VARIABLE_WIDTH, 0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 70.0, 100.0, 101.0, 110.0}, "Mixing bins in FT0M percentile"}; + ConfigurableAxis cfgMixBzBins{"cfgMixBzBins", {VARIABLE_WIDTH, -1.0, -0.1, 0.1, 1.0}, "Mixing bins in Bz (T)"}; + + using MixingBinning = ColumnBinningPolicy; + MixingBinning mixingBinning{{cfgMixVtxBins, cfgMixFT0MBins, cfgMixBzBins}}; + + Preslice xiStarsPerEvent = aod::redxistarcandidate::redXiStarKEventId; + Preslice kaonsPerEvent = aod::redxistarkaon::redXiStarKEventId; + + // --------------------------------------------------------------------------- + // Histogram axes. + // --------------------------------------------------------------------------- + ConfigurableAxis cfgAxisKstar{"cfgAxisKstar", {200, 0.0, 1.0}, "k* axis (GeV/c)"}; + ConfigurableAxis cfgAxisMass{"cfgAxisMass", {500, 1.45, 1.70}, "M(Xi pi) axis (GeV/c2)"}; + ConfigurableAxis cfgAxisMt{"cfgAxisMt", {100, 0.0, 5.0}, "pair mT axis (GeV/c2)"}; + ConfigurableAxis cfgAxisXiPt{"cfgAxisXiPt", {100, 0.0, 10.0}, "Xi(1530) pT axis (GeV/c)"}; + ConfigurableAxis cfgAxisKaonPt{"cfgAxisKaonPt", {100, 0.0, 5.0}, "external K pT axis (GeV/c)"}; + ConfigurableAxis cfgAxisPIDP{"cfgAxisPIDP", {200, 0.0, 10.0}, "momentum axis for PID QA (GeV/c)"}; + ConfigurableAxis cfgAxisPIDNSigma{"cfgAxisPIDNSigma", {160, -8.0, 8.0}, "n-sigma axis for PID QA"}; + + Configurable fillCandidateQA{"fillCandidateQA", true, "Fill selected Xi* and kaon QA"}; + Configurable fillKinematicQA{"fillKinematicQA", true, "Fill k*-mT and k*-pT QA histograms"}; + Configurable fillCPRQA{"fillCPRQA", true, "Fill K--prompt-pion close-pair QA; no CPR cut is applied"}; + Configurable cprKstarMax{"cprKstarMax", 0.5f, "Maximum k* filled in CPR QA (GeV/c)"}; + ConfigurableAxis cfgAxisCPRDEta{"cfgAxisCPRDEta", {120, -0.3, 0.3}, "Delta-eta axis for CPR QA"}; + ConfigurableAxis cfgAxisCPRDPhiStar{"cfgAxisCPRDPhiStar", {120, -0.3, 0.3}, "Delta-phi* axis for CPR QA"}; + ConfigurableAxis cfgAxisCPRKstar{"cfgAxisCPRKstar", {100, 0.0, 0.5}, "k* axis for CPR QA (GeV/c)"}; + Configurable cprRadiusMinCm{"cprRadiusMinCm", 85.f, "Minimum TPC radius used for phi* averaging (cm)"}; + Configurable cprRadiusMaxCm{"cprRadiusMaxCm", 245.f, "Maximum TPC radius used for phi* averaging (cm)"}; + Configurable cprRadiusStepCm{"cprRadiusStepCm", 20.f, "TPC-radius step used for phi* averaging (cm)"}; + + // --------------------------------------------------------------------------- + // Xi(1530) selection helpers. + // --------------------------------------------------------------------------- + bool passPromptPionPID(uint64_t bits) const + { + const int mode = xiCuts.promptPionPIDMode.value; + const int wp = xiCuts.promptPionPIDWP.value; + + if (mode == 0) { + switch (wp) { + case 0: + return hasTrackBit(bits, redxistark::kPiFirstPID3Sigma); + case 1: + return hasTrackBit(bits, redxistark::kPiFirstPID4Sigma); + case 2: + return hasTrackBit(bits, redxistark::kPiFirstPID5Sigma); + case 3: + return hasTrackBit(bits, redxistark::kPiFirstPID6Sigma); + default: + return false; + } + } + + if (mode == 1) { + switch (wp) { + case 0: + return hasTrackBit(bits, redxistark::kPiFirstPtThresholdPID3Sigma); + case 1: + return hasTrackBit(bits, redxistark::kPiFirstPtThresholdPID4Sigma); + case 2: + return hasTrackBit(bits, redxistark::kPiFirstPtThresholdPID5Sigma); + case 3: + return hasTrackBit(bits, redxistark::kPiFirstPtThresholdPID6Sigma); + default: + return false; + } + } + + switch (wp) { + case 0: + return hasTrackBit(bits, redxistark::kPiFirstTPC3Sigma); + case 1: + return hasTrackBit(bits, redxistark::kPiFirstTPC4Sigma); + case 2: + return hasTrackBit(bits, redxistark::kPiFirstTPC5Sigma); + case 3: + return hasTrackBit(bits, redxistark::kPiFirstTPC6Sigma); + default: + return false; + } + } + + bool passBachelorPionPID(uint64_t bits) const + { + const int mode = xiCuts.bachelorPionPIDMode.value; + const int wp = xiCuts.bachelorPionPIDWP.value; + + if (mode == 0) { + switch (wp) { + case 0: + return hasTrackBit(bits, redxistark::kXiBachelorPiHybridPID3Sigma); + case 1: + return hasTrackBit(bits, redxistark::kXiBachelorPiHybridPID4Sigma); + case 3: + return hasTrackBit(bits, redxistark::kXiBachelorPiHybridPID5Sigma); + case 4: + return hasTrackBit(bits, redxistark::kXiBachelorPiHybridPID6Sigma); + default: + return false; // 4.8 sigma exists only for TPC-only. + } + } + + switch (wp) { + case 0: + return hasTrackBit(bits, redxistark::kXiBachelorPiTPC3Sigma); + case 1: + return hasTrackBit(bits, redxistark::kXiBachelorPiTPC4Sigma); + case 2: + return hasTrackBit(bits, redxistark::kXiBachelorPiTPC4p8Sigma); + case 3: + return hasTrackBit(bits, redxistark::kXiBachelorPiTPC5Sigma); + case 4: + return hasTrackBit(bits, redxistark::kXiBachelorPiTPC6Sigma); + default: + return false; + } + } + + bool passLambdaPionTPCPID(uint64_t bits) const + { + switch (xiCuts.lambdaPionPIDWP.value) { + case 0: + return hasTrackBit(bits, redxistark::kLambdaPiTPC3Sigma); + case 1: + return hasTrackBit(bits, redxistark::kLambdaPiTPC4Sigma); + case 2: + return hasTrackBit(bits, redxistark::kLambdaPiTPC4p8Sigma); + case 3: + return hasTrackBit(bits, redxistark::kLambdaPiTPC5Sigma); + case 4: + return hasTrackBit(bits, redxistark::kLambdaPiTPC6Sigma); + default: + return false; + } + } + + bool passLambdaProtonTPCPID(uint64_t bits) const + { + switch (xiCuts.lambdaProtonPIDWP.value) { + case 0: + return hasTrackBit(bits, redxistark::kLambdaPrTPC3Sigma); + case 1: + return hasTrackBit(bits, redxistark::kLambdaPrTPC4Sigma); + case 2: + return hasTrackBit(bits, redxistark::kLambdaPrTPC5Sigma); + case 3: + return hasTrackBit(bits, redxistark::kLambdaPrTPC6Sigma); + default: + return false; + } + } + + template + bool passXiStarSelection(TXiStar const& candidate) const + { + const uint64_t trackBits = candidate.trackSelectionBits(); + const uint64_t topoBits = candidate.topologySelectionBits(); + + if (!passPromptPionPID(trackBits) || + !passBachelorPionPID(trackBits) || + !passLambdaPionTPCPID(trackBits) || + !passLambdaProtonTPCPID(trackBits)) { + return false; + } + + const std::array promptRowsBits{ + redxistark::kPiFirstTPCRows70, + redxistark::kPiFirstTPCRows80, + redxistark::kPiFirstTPCRows90}; + if (!hasTrackBit(trackBits, promptRowsBits[xiCuts.promptPionRowsWP.value])) { + return false; + } + + const std::array promptDCAzBits{ + redxistark::kPiFirstDCAz1cm, + redxistark::kPiFirstDCAzDefault, + redxistark::kPiFirstDCAz0p1cm}; + if (!hasTrackBit(trackBits, promptDCAzBits[xiCuts.promptPionDCAzWP.value])) { + return false; + } + + const std::array v0RowsBits{ + redxistark::kAllCascDaughtersTPCRows50, + redxistark::kV0DaughtersTPCRows70, + redxistark::kV0DaughtersTPCRowsVar1, + redxistark::kV0DaughtersTPCRowsVar2}; + if (!hasTrackBit(trackBits, v0RowsBits[xiCuts.v0DaughterRowsWP.value])) { + return false; + } + + const std::array pionDcaBits{ + redxistark::kV0PionDcaPV005, + redxistark::kV0PionDcaPV006, + redxistark::kV0PionDcaPV010, + redxistark::kV0PionDcaPV020}; + if (!hasTopoBit(topoBits, pionDcaBits[xiCuts.v0PionDcaPVWP.value])) { + return false; + } + + const std::array protonDcaBits{ + redxistark::kV0ProtonDcaPV005, + redxistark::kV0ProtonDcaPV006, + redxistark::kV0ProtonDcaPV007, + redxistark::kV0ProtonDcaPV010}; + if (!hasTopoBit(topoBits, protonDcaBits[xiCuts.v0ProtonDcaPVWP.value])) { + return false; + } + + const std::array v0DcaPVBits{ + redxistark::kV0DcaPV000, + redxistark::kV0DcaPV003, + redxistark::kV0DcaPV010}; + if (!hasTopoBit(topoBits, v0DcaPVBits[xiCuts.v0DcaPVWP.value])) { + return false; + } + + const std::array v0DaughterDCABits{ + redxistark::kV0DcaDaughters1p0, + redxistark::kV0DcaDaughters0p5, + redxistark::kV0DcaDaughters0p1}; + if (!hasTopoBit(topoBits, v0DaughterDCABits[xiCuts.v0DcaDaughtersWP.value])) { + return false; + } + + const std::array v0CosPABits{ + redxistark::kV0CosPA097, + redxistark::kV0CosPA098, + redxistark::kV0CosPA09876, + redxistark::kV0CosPA099, + redxistark::kV0CosPA0995}; + if (!hasTopoBit(topoBits, v0CosPABits[xiCuts.v0CosPAWP.value])) { + return false; + } + + const std::array v0RadiusBits{ + redxistark::kV0Radius0p9, + redxistark::kV0Radius1p01, + redxistark::kV0Radius1p2, + redxistark::kV0Radius2p5, + redxistark::kV0Radius3p0}; + if (!hasTopoBit(topoBits, v0RadiusBits[xiCuts.v0RadiusWP.value])) { + return false; + } + + const std::array lambdaMassBits{ + redxistark::kLambdaMass10MeV, + redxistark::kLambdaMass8MeV, + redxistark::kLambdaMass6MeV, + redxistark::kLambdaMass11p6MeV}; + if (!hasTopoBit(topoBits, lambdaMassBits[xiCuts.lambdaMassWP.value])) { + return false; + } + + if (xiCuts.applyV0LifetimeCut.value) { + const std::array v0LifetimeBits{ + redxistark::kV0LifetimeDefault, + redxistark::kV0LifetimeVar1, + redxistark::kV0LifetimeVar2}; + if (!hasTopoBit(topoBits, v0LifetimeBits[xiCuts.v0LifetimeWP.value])) { + return false; + } + } + + const std::array bachelorDcaBits{ + redxistark::kCascBachelorDcaPV005, + redxistark::kCascBachelorDcaPV006, + redxistark::kCascBachelorDcaPV010}; + if (!hasTopoBit(topoBits, bachelorDcaBits[xiCuts.cascBachelorDcaPVWP.value])) { + return false; + } + + const std::array cascDaughterDCABits{ + redxistark::kCascDcaDaughters1p0, + redxistark::kCascDcaDaughters0p25, + redxistark::kCascDcaDaughters0p20}; + if (!hasTopoBit(topoBits, cascDaughterDCABits[xiCuts.cascDcaDaughtersWP.value])) { + return false; + } + + const std::array cascCosPABits{ + redxistark::kCascCosPA097, + redxistark::kCascCosPA098, + redxistark::kCascCosPA09947, + redxistark::kCascCosPA0995}; + if (!hasTopoBit(topoBits, cascCosPABits[xiCuts.cascCosPAWP.value])) { + return false; + } + + const std::array cascRadiusBits{ + redxistark::kCascRadius0p9, + redxistark::kCascRadius1p0, + redxistark::kCascRadius1p01, + redxistark::kCascRadius1p3}; + if (!hasTopoBit(topoBits, cascRadiusBits[xiCuts.cascRadiusWP.value])) { + return false; + } + + const std::array xiMassBits{ + redxistark::kXiMass10MeV, + redxistark::kXiMass8MeV, + redxistark::kXiMass6MeV}; + if (!hasTopoBit(topoBits, xiMassBits[xiCuts.xiMassWP.value])) { + return false; + } + + if (xiCuts.applyXiLifetimeCut.value) { + const std::array xiLifetimeBits{ + redxistark::kXiLifetimeDefault, + redxistark::kXiLifetimeVar1, + redxistark::kXiLifetimeVar2}; + if (!hasTopoBit(topoBits, xiLifetimeBits[xiCuts.xiLifetimeWP.value])) { + return false; + } + } + + if (xiCuts.requirePPBachBaryonDCAxy.value && + !hasTopoBit(topoBits, redxistark::kBachBaryonDCAxy0020)) { + return false; + } + if (xiCuts.requirePPXiRapidity.value && + !hasTopoBit(topoBits, redxistark::kXiRapidity05)) { + return false; + } + + return true; + } + + // --------------------------------------------------------------------------- + // Kaon selection helpers. + // IMPORTANT: getter spelling follows the attached data model exactly: + // tPCNClsCrossedRows(), tPCNSigmaKa(), tOFNSigmaKa(). + // --------------------------------------------------------------------------- + bool passKaonDCA(uint8_t bits, int wp, bool xy) const + { + if (xy) { + switch (wp) { + case 0: + return hasKaonBit(bits, redxistarkaon::kKaonDCAxyDefault); + case 1: + return hasKaonBit(bits, redxistarkaon::kKaonDCAxyVar1); + case 2: + return hasKaonBit(bits, redxistarkaon::kKaonDCAxyVar2); + default: + return false; + } + } + + switch (wp) { + case 0: + return hasKaonBit(bits, redxistarkaon::kKaonDCAzDefault); + case 1: + return hasKaonBit(bits, redxistarkaon::kKaonDCAzVar1); + case 2: + return hasKaonBit(bits, redxistarkaon::kKaonDCAzVar2); + default: + return false; + } + } + + template + bool passKaonPID(TKaon const& kaon) const + { + const float tpc = kaon.tPCNSigmaKa(); + if (!std::isfinite(tpc)) { + return false; + } + + if (kaonCuts.pidMode.value == 0) { + return std::abs(tpc) < kaonCuts.tpcNSigmaMax.value; + } + + if (kaonCuts.pidMode.value == 1) { + if (!kaon.hasTOF()) { + return std::abs(tpc) < kaonCuts.tpcNSigmaMax.value; + } + const float tof = kaon.tOFNSigmaKa(); + return std::isfinite(tof) && std::hypot(tpc, tof) < kaonCuts.circularNSigmaMax.value; + } + + // Default: pT-threshold hybrid PID. + if (kaon.pt() < kaonCuts.pidPtThreshold.value) { + return std::abs(tpc) < kaonCuts.tpcNSigmaMax.value; + } + + if (!kaon.hasTOF()) { + if (kaonCuts.requireTOFAboveThreshold.value) { + return false; + } + return std::abs(tpc) < kaonCuts.tpcNSigmaMax.value; + } + + const float tof = kaon.tOFNSigmaKa(); + return std::isfinite(tof) && std::hypot(tpc, tof) < kaonCuts.circularNSigmaMax.value; + } + + template + bool passKaonSelection(TKaon const& kaon) const + { + if (kaon.pt() <= kaonCuts.ptMin.value || kaon.pt() >= kaonCuts.ptMax.value) { + return false; + } + if (kaon.tPCNClsCrossedRows() <= kaonCuts.tpcRowsMin.value) { + return false; + } + if (!passKaonDCA(kaon.selectionBits(), kaonCuts.dcaXYWP.value, true) || + !passKaonDCA(kaon.selectionBits(), kaonCuts.dcaZWP.value, false)) { + return false; + } + return passKaonPID(kaon); + } + + // --------------------------------------------------------------------------- + // Pair helpers. + // --------------------------------------------------------------------------- + template + bool sharesTrack(TXiStar const& xiStar, TKaon const& kaon) const + { + const int64_t kIndex = kaon.trackIndex(); + return kIndex == xiStar.xiStarPionIndex() || + kIndex == xiStar.xiBachelorIndex() || + kIndex == xiStar.v0PositiveIndex() || + kIndex == xiStar.v0NegativeIndex(); + } + + bool passChannelSelection(int8_t channel) const + { + if (channel == 0) { + return false; + } + if (channel > 0 && !pairCuts.fillMatter.value) { + return false; + } + if (channel < 0 && !pairCuts.fillAntimatter.value) { + return false; + } + if (std::abs(static_cast(channel)) == 2 && !pairCuts.fillLikeSignControl.value) { + return false; + } + return true; + } + + bool isRequestedChargeCombination(int8_t channel, int8_t kaonCharge) const + { + // +1/+2 are Xi- based -> K- ; -1/-2 are anti-Xi+ based -> K+. + if (channel > 0) { + return kaonCharge < 0; + } + if (channel < 0) { + return kaonCharge > 0; + } + return false; + } + + int promptPionCharge(int8_t channel) const + { + // +1 = Xi- pi+ US, +2 = Xi- pi- LS, + // -1 = antiXi+ pi- US, -2 = antiXi+ pi+ LS. + if (channel == redxistark::kXiStarUS || channel == redxistark::kAntiXiStarLS) { + return +1; + } + return -1; + } + + template + float getKstar(TXiStar const& xiStar, TKaon const& kaon) const + { + const auto m1 = static_cast(xiStar.mass()); + const auto m2 = static_cast(kKaonMass); + + const auto p1x = static_cast(xiStar.px()); + const auto p1y = static_cast(xiStar.py()); + const auto p1z = static_cast(xiStar.pz()); + const auto p2x = static_cast(kaon.px()); + const auto p2y = static_cast(kaon.py()); + const auto p2z = static_cast(kaon.pz()); + + const double e1 = std::sqrt(p1x * p1x + p1y * p1y + p1z * p1z + m1 * m1); + const double e2 = std::sqrt(p2x * p2x + p2y * p2y + p2z * p2z + m2 * m2); + + const double px = p1x + p2x; + const double py = p1y + p2y; + const double pz = p1z + p2z; + const double e = e1 + e2; + const double s = e * e - px * px - py * py - pz * pz; + + if (!std::isfinite(s) || s <= 0.0) { + return -1.f; + } + + const double mPlus = m1 + m2; + const double mMinus = m1 - m2; + const double lambda = (s - mPlus * mPlus) * (s - mMinus * mMinus); + if (!std::isfinite(lambda) || lambda <= 0.0) { + return -1.f; + } + + const double kstar = std::sqrt(lambda) / (2.0 * std::sqrt(s)); + return std::isfinite(kstar) ? static_cast(kstar) : -1.f; + } + + template + float getPairMt(TXiStar const& xiStar, TKaon const& kaon) const + { + const float pairPx = xiStar.px() + kaon.px(); + const float pairPy = xiStar.py() + kaon.py(); + const float kT = 0.5f * std::hypot(pairPx, pairPy); + const float meanMass = 0.5f * (xiStar.mass() + kKaonMass); + return std::sqrt(kT * kT + meanMass * meanMass); + } + + float phiAtTPCAverage(float pt, float phi, int charge, float bz) const + { + if (pt <= 0.f || !std::isfinite(pt) || !std::isfinite(phi) || !std::isfinite(bz)) { + return phi; + } + + float sum = 0.f; + int n = 0; + + const auto nRadiusSteps = static_cast( + (cprRadiusMaxCm.value - cprRadiusMinCm.value) / cprRadiusStepCm.value + 0.5f); + + for (int iRadius = 0; iRadius <= nRadiusSteps; ++iRadius) { + const auto radiusCm = cprRadiusMinCm.value + + static_cast(iRadius) * cprRadiusStepCm.value; + + const float arg = 0.3f * static_cast(charge) * bz * radiusCm * 0.01f / (2.f * pt); + if (std::abs(arg) < 1.f) { + sum += phi - std::asin(arg); + ++n; + } + } + return n > 0 ? sum / static_cast(n) : phi; + } + + template + void fillCPR(bool mixed, + TXiStar const& xiStar, + TKaon const& kaon, + float kstar, + float bzXiEvent, + float bzKaonEvent) + { + if (!fillCPRQA.value || kstar < 0.f || kstar >= cprKstarMax.value) { + return; + } + + const int pionCharge = promptPionCharge(xiStar.channel()); + const float pionPhiTPC = phiAtTPCAverage(xiStar.xiStarPionPt(), xiStar.xiStarPionPhi(), pionCharge, bzXiEvent); + const float kaonPhiTPC = phiAtTPCAverage(kaon.pt(), kaon.phi(), kaon.charge(), bzKaonEvent); + + const float dEta = kaon.eta() - xiStar.xiStarPionEta(); + const float dPhiStar = wrapToPi(kaonPhiTPC - pionPhiTPC); + + if (mixed) { + histos.fill(HIST("hCPRME"), dEta, dPhiStar, kstar, static_cast(xiStar.channel())); + } else { + histos.fill(HIST("hCPRSE"), dEta, dPhiStar, kstar, static_cast(xiStar.channel())); + } + } + + // --------------------------------------------------------------------------- + // Histogram filling helpers. + // --------------------------------------------------------------------------- + template + void fillSameEventPair(TCollision const& collision, TXiStar const& xiStar, TKaon const& kaon) + { + histos.fill(HIST("hPairCounterSE"), 1.f); + + if (!passChannelSelection(xiStar.channel()) || + !isRequestedChargeCombination(xiStar.channel(), kaon.charge())) { + return; + } + histos.fill(HIST("hPairCounterSE"), 2.f); + + if (pairCuts.applySharedTrackCleaning.value && sharesTrack(xiStar, kaon)) { + return; + } + histos.fill(HIST("hPairCounterSE"), 3.f); + + const float kstar = getKstar(xiStar, kaon); + if (kstar < 0.f) { + histos.fill(HIST("hInvalidKstar"), 0.f); + return; + } + histos.fill(HIST("hPairCounterSE"), 4.f); + + const auto channel = static_cast(xiStar.channel()); + const float mt = getPairMt(xiStar, kaon); + + // Getter spelling is fT0MPercentile(), exactly as declared in the data model. + histos.fill(HIST("SEPairs"), kstar, xiStar.mass(), xiStar.pt(), collision.fT0MPercentile(), channel); + histos.fill(HIST("hSEKstarVsChannel"), kstar, channel); + if (fillKinematicQA.value) { + histos.fill(HIST("hSEKstarVsMtVsChannel"), kstar, mt, channel); + histos.fill(HIST("hSEKstarVsXiPtVsChannel"), kstar, xiStar.pt(), channel); + histos.fill(HIST("hSEKstarVsKaonPtVsChannel"), kstar, kaon.pt(), channel); + } + + if (xiStar.channel() == redxistark::kXiStarUS) { + histos.fill(HIST("hSEKstarXiStarKminus"), kstar); + } else if (xiStar.channel() == redxistark::kAntiXiStarUS) { + histos.fill(HIST("hSEKstarAntiXiStarKplus"), kstar); + } + + fillCPR(false, xiStar, kaon, kstar, collision.bz(), collision.bz()); + } + + template + void fillMixedEventPair(TCollision1 const& collision1, + TCollision2 const& collision2, + TXiStar const& xiStar, + TKaon const& kaon) + { + histos.fill(HIST("hPairCounterME"), 1.f); + + if (!passChannelSelection(xiStar.channel()) || + !isRequestedChargeCombination(xiStar.channel(), kaon.charge())) { + return; + } + histos.fill(HIST("hPairCounterME"), 2.f); + + const float kstar = getKstar(xiStar, kaon); + if (kstar < 0.f) { + histos.fill(HIST("hInvalidKstar"), 1.f); + return; + } + histos.fill(HIST("hPairCounterME"), 3.f); + + const auto channel = static_cast(xiStar.channel()); + const float mt = getPairMt(xiStar, kaon); + + histos.fill(HIST("MEPairs"), kstar, xiStar.mass(), xiStar.pt(), collision1.fT0MPercentile(), channel); + histos.fill(HIST("hMEKstarVsChannel"), kstar, channel); + if (fillKinematicQA.value) { + histos.fill(HIST("hMEKstarVsMtVsChannel"), kstar, mt, channel); + } + + if (xiStar.channel() == redxistark::kXiStarUS) { + histos.fill(HIST("hMEKstarXiStarKminus"), kstar); + } else if (xiStar.channel() == redxistark::kAntiXiStarUS) { + histos.fill(HIST("hMEKstarAntiXiStarKplus"), kstar); + } + + fillCPR(true, xiStar, kaon, kstar, collision1.bz(), collision2.bz()); + } + + // --------------------------------------------------------------------------- + // Initialization. + // --------------------------------------------------------------------------- + void init(InitContext const&) + { + auto inRange = [](int value, int lo, int hi) { return value >= lo && value <= hi; }; + + if (!inRange(xiCuts.promptPionPIDMode.value, 0, 2) || + !inRange(xiCuts.bachelorPionPIDMode.value, 0, 1) || + !inRange(xiCuts.promptPionPIDWP.value, 0, 3) || + !inRange(xiCuts.bachelorPionPIDWP.value, 0, 4) || + !inRange(xiCuts.lambdaPionPIDWP.value, 0, 4) || + !inRange(xiCuts.lambdaProtonPIDWP.value, 0, 3)) { + LOGF(fatal, "Invalid Xi(1530) daughter PID mode/working point"); + } + if (xiCuts.bachelorPionPIDMode.value == 0 && xiCuts.bachelorPionPIDWP.value == 2) { + LOGF(fatal, "Xi-bachelor 4.8-sigma PID working point is available only in TPC-only mode"); + } + + if (!inRange(xiCuts.promptPionRowsWP.value, 0, 2) || + !inRange(xiCuts.promptPionDCAzWP.value, 0, 2) || + !inRange(xiCuts.v0DaughterRowsWP.value, 0, 3) || + !inRange(xiCuts.v0PionDcaPVWP.value, 0, 3) || + !inRange(xiCuts.v0ProtonDcaPVWP.value, 0, 3) || + !inRange(xiCuts.v0DcaPVWP.value, 0, 2) || + !inRange(xiCuts.v0DcaDaughtersWP.value, 0, 2) || + !inRange(xiCuts.v0CosPAWP.value, 0, 4) || + !inRange(xiCuts.v0RadiusWP.value, 0, 4) || + !inRange(xiCuts.lambdaMassWP.value, 0, 3) || + !inRange(xiCuts.v0LifetimeWP.value, 0, 2) || + !inRange(xiCuts.cascBachelorDcaPVWP.value, 0, 2) || + !inRange(xiCuts.cascDcaDaughtersWP.value, 0, 2) || + !inRange(xiCuts.cascCosPAWP.value, 0, 3) || + !inRange(xiCuts.cascRadiusWP.value, 0, 3) || + !inRange(xiCuts.xiMassWP.value, 0, 2) || + !inRange(xiCuts.xiLifetimeWP.value, 0, 2)) { + LOGF(fatal, "Invalid Xi(1530) topology/quality working-point index"); + } + + if (!inRange(kaonCuts.dcaXYWP.value, 0, 2) || + !inRange(kaonCuts.dcaZWP.value, 0, 2) || + !inRange(kaonCuts.pidMode.value, 0, 2)) { + LOGF(fatal, "Kaon DCA working points and PID mode must be 0, 1 or 2"); + } + if (kaonCuts.pidPtThreshold.value < 0.f || + kaonCuts.tpcNSigmaMax.value <= 0.f || + kaonCuts.circularNSigmaMax.value <= 0.f) { + LOGF(fatal, "Invalid kaon PID configuration"); + } + if (nEvtMixing.value < 1) { + LOGF(fatal, "nEvtMixing must be >= 1"); + } + if (cprRadiusStepCm.value <= 0.f || cprRadiusMaxCm.value < cprRadiusMinCm.value || + cprKstarMax.value <= 0.f) { + LOGF(fatal, "Invalid CPR configuration"); + } + + const AxisSpec axisKstar{cfgAxisKstar, "#it{k}^{*} (GeV/#it{c})"}; + const AxisSpec axisMass{cfgAxisMass, "#it{M}_{#Xi#pi} (GeV/#it{c}^{2})"}; + const AxisSpec axisFT0M{cfgMixFT0MBins, "FT0M percentile (%)"}; + const AxisSpec axisChannel{5, -2.5, 2.5, "#Xi#pi channel"}; + const AxisSpec axisMt{cfgAxisMt, "#it{m}_{T} (GeV/#it{c}^{2})"}; + const AxisSpec axisXiPt{cfgAxisXiPt, "#it{p}_{T}^{#Xi^{*}} (GeV/#it{c})"}; + const AxisSpec axisKaonPt{cfgAxisKaonPt, "#it{p}_{T}^{K} (GeV/#it{c})"}; + const AxisSpec axisPIDP{cfgAxisPIDP, "#it{p} (GeV/#it{c})"}; + const AxisSpec axisPIDNSigma{cfgAxisPIDNSigma, "n#sigma"}; + + histos.add("hEventCounterSE", "SE event flow;;events", HistType::kTH1F, {{4, 0.5, 4.5}}); + auto hEventSE = histos.get(HIST("hEventCounterSE")); + hEventSE->GetXaxis()->SetBinLabel(1, "reduced event"); + hEventSE->GetXaxis()->SetBinLabel(2, "selected XiPi"); + hEventSE->GetXaxis()->SetBinLabel(3, "selected K"); + hEventSE->GetXaxis()->SetBinLabel(4, "XiPi and K"); + + histos.add("hPairCounterSE", "SE pair flow;;pairs", HistType::kTH1F, {{4, 0.5, 4.5}}); + auto hPairSE = histos.get(HIST("hPairCounterSE")); + hPairSE->GetXaxis()->SetBinLabel(1, "selected XiPi x K"); + hPairSE->GetXaxis()->SetBinLabel(2, "charge matched"); + hPairSE->GetXaxis()->SetBinLabel(3, "pair cleaned"); + hPairSE->GetXaxis()->SetBinLabel(4, "valid kstar"); + + histos.add("hEventCounterME", "ME event-pair flow;;event pairs", HistType::kTH1F, {{2, 0.5, 2.5}}); + auto hEventME = histos.get(HIST("hEventCounterME")); + hEventME->GetXaxis()->SetBinLabel(1, "mixed event pair"); + hEventME->GetXaxis()->SetBinLabel(2, "nonempty selected groups"); + + histos.add("hPairCounterME", "ME pair flow;;pairs", HistType::kTH1F, {{3, 0.5, 3.5}}); + auto hPairME = histos.get(HIST("hPairCounterME")); + hPairME->GetXaxis()->SetBinLabel(1, "selected XiPi x K"); + hPairME->GetXaxis()->SetBinLabel(2, "charge matched"); + hPairME->GetXaxis()->SetBinLabel(3, "valid kstar"); + + histos.add("hInvalidKstar", "Invalid k*;sample;counts", HistType::kTH1F, {{2, -0.5, 1.5}}); + auto hInvalid = histos.get(HIST("hInvalidKstar")); + hInvalid->GetXaxis()->SetBinLabel(1, "SE"); + hInvalid->GetXaxis()->SetBinLabel(2, "ME"); + + histos.add("hSelectedXiStarMassVsChannel", "Selected #Xi#pi candidates;#it{M}_{#Xi#pi} (GeV/#it{c}^{2});channel", HistType::kTH2F, {axisMass, axisChannel}); + histos.add("hSelectedKaonTPCnSigmaVsP", "Selected external K;#it{p} (GeV/#it{c});n#sigma_{TPC}^{K}", HistType::kTH2F, {axisPIDP, axisPIDNSigma}); + histos.add("hSelectedKaonTOFnSigmaVsP", "Selected external K with TOF;#it{p} (GeV/#it{c});n#sigma_{TOF}^{K}", HistType::kTH2F, {axisPIDP, axisPIDNSigma}); + + histos.add("SEPairs", "SE Xi(1530)-K candidates", HistType::kTHnSparseF, {axisKstar, axisMass, axisXiPt, axisFT0M, axisChannel}); + histos.add("MEPairs", "ME Xi(1530)-K candidates", HistType::kTHnSparseF, {axisKstar, axisMass, axisXiPt, axisFT0M, axisChannel}); + + histos.add("hSEKstarVsChannel", "SE;#it{k}^{*} (GeV/#it{c});channel", HistType::kTH2F, {axisKstar, axisChannel}); + histos.add("hMEKstarVsChannel", "ME;#it{k}^{*} (GeV/#it{c});channel", HistType::kTH2F, {axisKstar, axisChannel}); + + histos.add("hSEKstarXiStarKminus", "SE #Xi^{*0}K^{-};#it{k}^{*} (GeV/#it{c});pairs", HistType::kTH1F, {axisKstar}); + histos.add("hSEKstarAntiXiStarKplus", "SE #bar{#Xi}^{*0}K^{+};#it{k}^{*} (GeV/#it{c});pairs", HistType::kTH1F, {axisKstar}); + histos.add("hMEKstarXiStarKminus", "ME #Xi^{*0}K^{-};#it{k}^{*} (GeV/#it{c});pairs", HistType::kTH1F, {axisKstar}); + histos.add("hMEKstarAntiXiStarKplus", "ME #bar{#Xi}^{*0}K^{+};#it{k}^{*} (GeV/#it{c});pairs", HistType::kTH1F, {axisKstar}); + + histos.add("hSEKstarVsMtVsChannel", "SE pair kinematics;#it{k}^{*};#it{m}_{T};channel", HistType::kTH3F, {axisKstar, axisMt, axisChannel}); + histos.add("hMEKstarVsMtVsChannel", "ME pair kinematics;#it{k}^{*};#it{m}_{T};channel", HistType::kTH3F, {axisKstar, axisMt, axisChannel}); + histos.add("hSEKstarVsXiPtVsChannel", "SE pair kinematics;#it{k}^{*};#it{p}_{T}^{#Xi^{*}};channel", HistType::kTH3F, {axisKstar, axisXiPt, axisChannel}); + histos.add("hSEKstarVsKaonPtVsChannel", "SE pair kinematics;#it{k}^{*};#it{p}_{T}^{K};channel", HistType::kTH3F, {axisKstar, axisKaonPt, axisChannel}); + + const AxisSpec axisDEta{cfgAxisCPRDEta, "#Delta#eta(K,#pi_{#Xi^{*}})"}; + const AxisSpec axisDPhiStar{cfgAxisCPRDPhiStar, "#Delta#varphi^{*}(K,#pi_{#Xi^{*}})"}; + const AxisSpec axisCPRKstar{cfgAxisCPRKstar, "#it{k}^{*} (GeV/#it{c})"}; + histos.add("hCPRSE", "SE close-pair QA", HistType::kTHnSparseF, {axisDEta, axisDPhiStar, axisCPRKstar, axisChannel}); + histos.add("hCPRME", "ME close-pair QA", HistType::kTHnSparseF, {axisDEta, axisDPhiStar, axisCPRKstar, axisChannel}); + } + + // --------------------------------------------------------------------------- + // Same-event numerator. + // The framework groups the two daughter tables by RedXiStarKEventId for the + // current reduced-event iterator. + // --------------------------------------------------------------------------- + void processSameEvent(aod::RedXiStarKEvents::iterator const& collision, + aod::XiStarCandidates const& xiStars, + aod::KaonCandidates const& kaons) + { + histos.fill(HIST("hEventCounterSE"), 1.f); + + bool hasSelectedXi = false; + for (auto const& xiStar : xiStars) { + if (!passXiStarSelection(xiStar) || !passChannelSelection(xiStar.channel())) { + continue; + } + hasSelectedXi = true; + if (fillCandidateQA.value) { + histos.fill(HIST("hSelectedXiStarMassVsChannel"), xiStar.mass(), static_cast(xiStar.channel())); + } + } + if (hasSelectedXi) { + histos.fill(HIST("hEventCounterSE"), 2.f); + } + + bool hasSelectedK = false; + for (auto const& kaon : kaons) { + if (!passKaonSelection(kaon)) { + continue; + } + hasSelectedK = true; + if (fillCandidateQA.value) { + histos.fill(HIST("hSelectedKaonTPCnSigmaVsP"), kaon.p(), kaon.tPCNSigmaKa()); + if (kaon.hasTOF()) { + histos.fill(HIST("hSelectedKaonTOFnSigmaVsP"), kaon.p(), kaon.tOFNSigmaKa()); + } + } + } + if (hasSelectedK) { + histos.fill(HIST("hEventCounterSE"), 3.f); + } + if (!(hasSelectedXi && hasSelectedK)) { + return; + } + histos.fill(HIST("hEventCounterSE"), 4.f); + + for (auto const& xiStar : xiStars) { + if (!passXiStarSelection(xiStar) || !passChannelSelection(xiStar.channel())) { + continue; + } + for (auto const& kaon : kaons) { + if (!passKaonSelection(kaon)) { + continue; + } + fillSameEventPair(collision, xiStar, kaon); + } + } + } + PROCESS_SWITCH(xi1530kaoncorrelation, processSameEvent, "Process same-event Xi(1530)-K pairs", true); + + // --------------------------------------------------------------------------- + // Mixed-event denominator. + // Xi*(event A) is paired with K(event B) using z-vertex, FT0M percentile and + // Bz mixing bins. No cross-event shared-track cleaning is applied for now. + // --------------------------------------------------------------------------- + void processMixedEvent(aod::RedXiStarKEvents const& collisions, + aod::XiStarCandidates const& xiStars, + aod::KaonCandidates const& kaons) + + { + for (auto const& [collision1, collision2] : selfCombinations(mixingBinning, nEvtMixing, -1, collisions, collisions)) { + if (collision1.globalIndex() == collision2.globalIndex()) { + continue; + } + histos.fill(HIST("hEventCounterME"), 1.f); + + auto groupXi = xiStars.sliceBy(xiStarsPerEvent, collision1.globalIndex()); + auto groupK = kaons.sliceBy(kaonsPerEvent, collision2.globalIndex()); + + bool hasSelectedXi = false; + for (auto const& xiStar : groupXi) { + if (passXiStarSelection(xiStar) && passChannelSelection(xiStar.channel())) { + hasSelectedXi = true; + break; + } + } + + bool hasSelectedK = false; + for (auto const& kaon : groupK) { + if (passKaonSelection(kaon)) { + hasSelectedK = true; + break; + } + } + + if (!(hasSelectedXi && hasSelectedK)) { + continue; + } + histos.fill(HIST("hEventCounterME"), 2.f); + + for (auto const& xiStar : groupXi) { + if (!passXiStarSelection(xiStar) || !passChannelSelection(xiStar.channel())) { + continue; + } + for (auto const& kaon : groupK) { + if (!passKaonSelection(kaon)) { + continue; + } + fillMixedEventPair(collision1, collision2, xiStar, kaon); + } + } + } + } + PROCESS_SWITCH(xi1530kaoncorrelation, processMixedEvent, "Process mixed-event Xi(1530)-K pairs", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx index 902797c7d77..e922020b6d8 100644 --- a/PWGLF/Tasks/Resonances/xi1820Analysis.cxx +++ b/PWGLF/Tasks/Resonances/xi1820Analysis.cxx @@ -12,10 +12,13 @@ /// \file xi1820Analysis.cxx /// \brief Invariant Mass Reconstruction of Xi(1820) Resonance /// -/// \author Bong-Hwi Lim , Minjae Kim +/// \author Adel Bensaoula , Bong-Hwi Lim , Minjae Kim #include "PWGLF/DataModel/LFResonanceTables.h" +#include "Common/Core/RecoDecay.h" + +#include #include #include #include @@ -26,16 +29,28 @@ #include #include #include +#include #include #include #include +#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include +#include +#include +#include + +#include +#include #include #include +#include +#include +#include +#include #include using namespace o2; @@ -43,20 +58,52 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::constants::physics; +using LorentzVectorSetXYZM = ROOT::Math::LorentzVector>; struct Xi1820Analysis { + // Module-initializer tables; full tracks and V0s retain their schema. + using ResoCollisions = aod::ResoCollisions_001; + // Original track IDs are used as numbers; no parent AO2D dereference is needed. + using ResoTracks = soa::Join; + using ResoMicroTracks = aod::ResoMicroTracks_001; + using ResoMCCols = soa::Join; + SliceCache cache; Preslice perResoCollisionV0 = aod::resodaughter::resoCollisionId; Preslice perResoCollisionTrack = aod::resodaughter::resoCollisionId; - Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; + Preslice perResoCollisionMicroTrack = aod::resodaughter::resoCollisionId; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - using ResoMCCols = soa::Join; + + struct MixingCollision { + float x, y, z, centrality; + bool recINELgt0; + [[nodiscard]] float posX() const { return x; } + [[nodiscard]] float posY() const { return y; } + [[nodiscard]] float posZ() const { return z; } + [[nodiscard]] float cent() const { return centrality; } + }; + struct MixingEvent { + MixingCollision collision{}; + std::shared_ptr daughters; + }; + std::map> mixingPools; + float mixingBField = 0.f; + std::map> neutralMixingPools; + float neutralMixingBField = 0.f; + + template + struct SelectedLambda { + Iterator candidate; + bool isLambda = false, isAntiLambda = false; + }; + + Configurable cfgFillQA{"cfgFillQA", true, "Fill reconstructed daughter QA"}; + Configurable cfgFillEventQA{"cfgFillEventQA", true, "Fill event and rotation QA"}; + Configurable cfgFillTruthQA{"cfgFillTruthQA", true, "Fill MC truth-matched daughter QA"}; // Constants - static constexpr float kSmallMomentumDenominator = 1e-10f; // Small value to avoid division by zero - static constexpr int kPdgChagedXi1820 = 123314; // o2-linter: disable=pdg/explicit-code (Xi(1820) PDG code not available in PDG_t or o2::constants::physics::Pdg) - static constexpr int kPdgXi1820Zero = 123324; // o2-linter: disable=pdg/explicit-code (Xi(1820) PDG code not available in PDG_t or o2::constants::physics::Pdg) - static constexpr int kExpectedDaughters = 2; // Expected number of daughters for two-body decay + static constexpr float SmallMomentumDenominator = 1e-10f; // Small value to avoid division by zero + static constexpr int ExpectedDaughters = 2; // Expected number of daughters for two-body decay // Axes ConfigurableAxis binsPt{"binsPt", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.6, 0.8, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0}, "pT"}; @@ -76,20 +123,24 @@ struct Xi1820Analysis { Configurable cKaonPtMin{"cKaonPtMin", 0.15, "Minimum kaon pT"}; Configurable cKaonEtaMax{"cKaonEtaMax", 0.8, "Maximum kaon |eta|"}; Configurable cKaonDCAxyMax{"cKaonDCAxyMax", 0.1, "Maximum kaon DCAxy to PV"}; - Configurable cKaonDCAzMax{"cKaonDCAzMax", 0.2, "Maximum kaon DCAz to PV"}; - Configurable cKaonTPCRefit{"cKaonTPCRefit", true, "Require TPC refit for kaon"}; - Configurable cKaonITSRefit{"cKaonITSRefit", true, "Require ITS refit for kaon"}; + Configurable cKaonDCAzMax{"cKaonDCAzMax", 0.1, "Maximum kaon DCAz to PV"}; Configurable cKaonTPCNClusMin{"cKaonTPCNClusMin", 70, "Minimum TPC clusters for kaon"}; Configurable cKaonITSNClusMin{"cKaonITSNClusMin", 2, "Minimum ITS clusters for kaon"}; // Kaon PID selections + Configurable cKaonTPCNSigmaMin{"cKaonTPCNSigmaMin", -3.5f, "Strict lower TPC (nSigma - mean) bound for kaons"}; + Configurable cKaonTPCNSigmaMean{"cKaonTPCNSigmaMean", 0.f, "TPC nSigma mean subtracted for kaons"}; Configurable cKaonTPCNSigmaMax{"cKaonTPCNSigmaMax", 3.5, "Maximum TPC NSigma for kaon (if not using pT-dependent)"}; + Configurable cKaonTOFNSigmaMin{"cKaonTOFNSigmaMin", -999.f, "Strict lower TOF (nSigma - mean) bound for kaons"}; + Configurable cKaonTOFNSigmaMean{"cKaonTOFNSigmaMean", 0.f, "TOF nSigma mean subtracted for kaons"}; + Configurable cKaonBypassTOF{"cKaonBypassTOF", false, "Bypass TOF PID even when TOF information is present"}; Configurable cKaonTOFNSigmaMax{"cKaonTOFNSigmaMax", 999., "Maximum TOF NSigma for kaon (if not using pT-dependent)"}; Configurable cKaonUsePtDepPID{"cKaonUsePtDepPID", false, "Use pT-dependent PID cuts"}; Configurable> cKaonPIDPtBins{"cKaonPIDPtBins", {0.0f, 0.5f, 0.8f, 2.0f, 999.0f}, "pT bin edges for PID cuts (N+1 values for N bins)"}; Configurable> cKaonTPCNSigmaCuts{"cKaonTPCNSigmaCuts", {3.0f, 3.0f, 2.0f, 2.0f}, "TPC NSigma cuts per pT bin (N values)"}; Configurable> cKaonTOFNSigmaCuts{"cKaonTOFNSigmaCuts", {3.0f, 3.0f, 3.0f, 3.0f}, "TOF NSigma cuts per pT bin (N values)"}; - Configurable> cKaonTOFRequired{"cKaonTOFRequired", {0, 0, 0, 0}, "Require TOF per pT bin (N values, 0=false, 1=true)"}; + Configurable> cKaonTPCNSigmaMinCuts{"cKaonTPCNSigmaMinCuts", {-3.f, -3.f, -2.f, -2.f}, "Strict lower centered TPC bounds per pT bin"}; + Configurable> cKaonTOFNSigmaMinCuts{"cKaonTOFNSigmaMinCuts", {-3.f, -3.f, -3.f, -3.f}, "Strict lower centered TOF bounds per pT bin"}; // V0 (Lambda) selections Configurable cV0MinCosPA{"cV0MinCosPA", 0.995, "V0 minimum pointing angle cosine"}; @@ -100,15 +151,23 @@ struct Xi1820Analysis { Configurable cV0RadiusMax{"cV0RadiusMax", 200.0, "V0 decay radius max"}; Configurable cV0DauPosDCAtoPVMin{"cV0DauPosDCAtoPVMin", 0.05, "V0 positive daughter DCA to PV min"}; Configurable cV0DauNegDCAtoPVMin{"cV0DauNegDCAtoPVMin", 0.05, "V0 negative daughter DCA to PV min"}; - Configurable cV0ProperLifetimeMax{"cV0ProperLifetimeMax", 30.0, "Lambda proper lifetime max (cm/c)"}; + Configurable cV0ProperLifetimeMax{"cV0ProperLifetimeMax", 30.0, "Lambda proper lifetime max (cm)"}; Configurable cV0sCrossMassRejection{"cV0sCrossMassRejection", true, "Enable K0s mass rejection for Lambda"}; Configurable cV0sCrossMassRejectionWindow{"cV0sCrossMassRejectionWindow", 0.005, "K0s mass rejection window for Lambda (GeV/c^2)"}; + Configurable cLambdaDaughterPiTPCNSigmaMin{"cLambdaDaughterPiTPCNSigmaMin", -5.f, "Strict lower centered TPC PID bound"}; + Configurable cLambdaDaughterPiTPCNSigmaMean{"cLambdaDaughterPiTPCNSigmaMean", 0.f, "TPC nSigma mean subtracted for the V0 daughter"}; + Configurable cLambdaDaughterPiTPCNSigmaMax{"cLambdaDaughterPiTPCNSigmaMax", 5.0, "Maximum TPC NSigma for Lambda daughter pions"}; + Configurable cLambdaDaughterPrTPCNSigmaMin{"cLambdaDaughterPrTPCNSigmaMin", -5.f, "Strict lower centered TPC PID bound"}; + Configurable cLambdaDaughterPrTPCNSigmaMean{"cLambdaDaughterPrTPCNSigmaMean", 0.f, "TPC nSigma mean subtracted for the V0 daughter"}; + Configurable cLambdaDaughterPrTPCNSigmaMax{"cLambdaDaughterPrTPCNSigmaMax", 5.0, "Maximum TPC NSigma for Lambda daughter protons"}; + Configurable cLambdaPosDaughterMinCrossedRows{"cLambdaPosDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for Lambda positive daughter"}; + Configurable cLambdaNegDaughterMinCrossedRows{"cLambdaNegDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for Lambda negative daughter"}; // K0s selections Configurable cK0sMinCosPA{"cK0sMinCosPA", 0.98, "K0s minimum pointing angle cosine"}; Configurable cK0sMaxDaughDCA{"cK0sMaxDaughDCA", 0.5, "K0s daughter DCA Maximum"}; Configurable cK0sMassWindow{"cK0sMassWindow", 0.025, "Mass window for K0s selection (GeV/c^2)"}; - Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm/c)"}; + Configurable cK0sProperLifetimeMax{"cK0sProperLifetimeMax", 20.0, "K0s proper lifetime max (cm)"}; Configurable cK0sArmenterosQtMin{"cK0sArmenterosQtMin", 0.0, "K0s Armenteros qt min"}; Configurable cK0sArmenterosAlphaCoeff{"cK0sArmenterosAlphaCoeff", 0.2, "K0s Armenteros alpha max"}; Configurable cK0sDauPosDCAtoPVMin{"cK0sDauPosDCAtoPVMin", 0.05, "K0s positive daughter DCA to PV min"}; @@ -117,6 +176,11 @@ struct Xi1820Analysis { Configurable cK0sRadiusMax{"cK0sRadiusMax", 200.0, "K0s decay radius max"}; Configurable cK0sCrossMassRejection{"cK0sCrossMassRejection", true, "Enable Lambda mass rejection for K0s"}; Configurable cK0sCrossMassRejectionWindow{"cK0sCrossMassRejectionWindow", 0.01, "Lambda mass rejection window for K0s (GeV/c^2)"}; + Configurable cK0sDaughterPiTPCNSigmaMin{"cK0sDaughterPiTPCNSigmaMin", -5.f, "Strict lower centered TPC PID bound"}; + Configurable cK0sDaughterPiTPCNSigmaMean{"cK0sDaughterPiTPCNSigmaMean", 0.f, "TPC nSigma mean subtracted for the V0 daughter"}; + Configurable cK0sDaughterPiTPCNSigmaMax{"cK0sDaughterPiTPCNSigmaMax", 5.0, "Maximum TPC NSigma for K0s daughter pions"}; + Configurable cK0sPosDaughterMinCrossedRows{"cK0sPosDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s positive daughter"}; + Configurable cK0sNegDaughterMinCrossedRows{"cK0sNegDaughterMinCrossedRows", 50, "Minimum TPC crossed rows for K0s negative daughter"}; // Event Mixing Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; @@ -125,25 +189,44 @@ struct Xi1820Analysis { // Additional QA and configurations struct : ConfigurableGroup { - Configurable cRecoINELgt0{"cRecoINELgt0", false, "Apply Reco INEL>0 event selection"}; - Configurable cConsiderPairOnly{"cConsiderPairOnly", true, "Consider only the pair of tracks for the charged K + Lambda analysis"}; - Configurable cConsiderHasV0s{"cConsiderHasV0s", true, "Consider only the pair of tracks for the K0s + Lambda analysis"}; + Configurable cRecoINELgt0{"cRecoINELgt0", true, "Apply Reco INEL>0 event selection"}; + Configurable cMCINELgt0{"cMCINELgt0", true, "Require generator INEL>0 in reconstructed MC and generated-parent processes"}; + Configurable cMCVtxIn10{"cMCVtxIn10", true, "Require generator |vertex z| < 10 cm using isVtxIn10 in reconstructed MC and generated-parent processes"}; Configurable cUseTruthRapidity{"cUseTruthRapidity", false, "Use truth rapidity for MC generated target"}; Configurable cUsePtDepDCAForKaons{"cUsePtDepDCAForKaons", true, "Use pT dependent DCA cuts for kaon tracks"}; Configurable cDCAToPVByPtFirstP0{"cDCAToPVByPtFirstP0", 0.004, "pT dependent DCA cut first parameter (cm)"}; - Configurable cDCAToPVByPtFirstExp{"cDCAToPVByPtFirstExp", 0.013, "pT dependent DCA cut second parameter (exponent)"}; - Configurable cMaxDcaToPVV0{"cMaxDcaToPVV0", 1.0, "Maximum DCA to PV for V0 candidates (cm)"}; + Configurable cDCAToPVByPtFirstExp{"cDCAToPVByPtFirstExp", 0.013, "Coefficient in kaon DCA cut = P0 + coefficient / pT^power (legacy key)"}; + Configurable cDCAToPVByPtFirstPower{"cDCAToPVByPtFirstPower", 1.f, "Power in kaon DCA cut = P0 + coefficient / pT^power"}; + Configurable cMaxDcaToPVV0Lambda{"cMaxDcaToPVV0Lambda", 1.0, "Maximum DCA to PV for Lambda candidates (cm)"}; + Configurable cMaxDcaToPVV0K0s{"cMaxDcaToPVV0K0s", 1.0, "Maximum DCA to PV for K0s candidates (cm)"}; Configurable cfgRapidityCut{"cfgRapidityCut", 0.5, "Rapidity cut"}; ConfigurableAxis multNTracksAxis{"multNTracksAxis", {500, 0, 500}, "N_{tracks}"}; + Configurable cfgFillRotBkg{"cfgFillRotBkg", true, "Fill rotated background"}; + Configurable cfgMinRot{"cfgMinRot", o2::constants::math::PIHalf + o2::constants::math::PIThird, "Minimum of rotation"}; + Configurable cfgMaxRot{"cfgMaxRot", o2::constants::math::TwoPI - o2::constants::math::PIHalf - o2::constants::math::PIThird, "Maximum of rotation"}; + Configurable cfgRotKaon{"cfgRotKaon", true, "Rotate Kaon"}; + Configurable cfgNrotBkg{"cfgNrotBkg", 10, "Number of rotated copies (background) per each original candidate"}; + + Configurable cfgDelCheck{"cfgDelCheck", true, "Check Delta R distribution of candidates"}; + Configurable cfgPVContributor{"cfgPVContributor", true, "Require PV contributor tracks"}; + Configurable cfgPrimaryTrack{"cfgPrimaryTrack", true, "Require primary tracks flags (just for check)"}; + } additionalConfig; using BinningTypeVertexContributor = ColumnBinningPolicy; void init(InitContext&) { + const int sameEventModes = static_cast(doprocessDataWithTracks) + static_cast(doprocessDataWithMicroTracks) + + static_cast(doprocessMCWithTracks) + static_cast(doprocessMCWithMicroTracks); + const int mixedEventModes = static_cast(doprocessMixedEventWithTracks) + static_cast(doprocessMixedEventWithMicroTracks) + static_cast(doprocessMEDF); + if (sameEventModes > 1 || mixedEventModes > 1 || (doprocessK0sLambda && doprocessMCK0sLambda) || + (doprocessK0sLambdaMixedEvent && doprocessK0sLambdaMEDF)) { + LOG(fatal) << "Enable at most one same-event mode and one mixing mode per charged/neutral channel"; + } AxisSpec centAxis = {binsCent, "V0M (%)"}; AxisSpec ptAxis = {binsPt, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec ptAxisQA = {binsPtQA, "#it{p}_{T} (GeV/#it{c})"}; @@ -154,22 +237,49 @@ struct Xi1820Analysis { AxisSpec dcazAxis = {400, -0.2, 0.2, "DCA_{z} (cm)"}; AxisSpec cosPAAxis = {1000, 0.95, 1.0, "cos(PA)"}; AxisSpec radiusAxis = {200, 0, 200, "Radius (cm)"}; - AxisSpec lifetimeAxis = {200, 0, 50, "Proper lifetime (cm/c)"}; + AxisSpec lifetimeAxis = {200, 0, 50, "Proper lifetime (cm)"}; AxisSpec nsigmaAxis = {100, -5.0, 5.0, "N#sigma"}; AxisSpec armenterosAlphaAxis = {200, -1.0, 1.0, "Armenteros alpha"}; AxisSpec armenterosQtAxis = {500, 0.0, 0.5, "Armenteros qt (GeV/c)"}; + AxisSpec axisRot = {360, -o2::constants::math::PI, o2::constants::math::PI}; + AxisSpec axisDel = {400, -0.5, 7.0, "#DeltaR"}; // Event QA histograms - histos.add("Event/posZ", "Event vertex Z position", kTH1F, {{200, -20., 20., "V_{z} (cm)"}}); - histos.add("Event/centrality", "Event centrality distribution", kTH1D, {centAxis}); - histos.add("Event/posZvsCent", "Vertex Z vs Centrality", kTH2F, {{200, -20., 20., "V_{z} (cm)"}, centAxis}); - histos.add("Event/nV0s", "Number of V0s per event", kTH1F, {{200, 0., 200., "N_{V0s}"}}); - histos.add("Event/nKaons", "Number of kaons per event", kTH1F, {{200, 0., 200., "N_{kaon}"}}); - histos.add("Event/nLambdasAfterCuts", "Number of Lambdas per event after cuts", kTH1F, {{100, 0., 100., "N_{Lambda}"}}); - histos.add("Event/nKaonsAfterCuts", "Number of kaons (or K0s) per event after cuts", kTH1F, {{100, 0., 100., "N_{Kaon}"}}); - - if (doprocessDataWithTracks || doprocessDataWithMicroTracks || doprocessMCWithTracks || doprocessK0sLambda || doprocessMCK0sLambda) { + if (cfgFillEventQA) { + histos.add("Event/posZ", "Event vertex Z position", kTH1F, {{200, -20., 20., "V_{z} (cm)"}}); + histos.add("Event/centrality", "Event centrality distribution", kTH1D, {centAxis}); + histos.add("Event/posZvsCent", "Vertex Z vs Centrality", kTH2F, {{200, -20., 20., "V_{z} (cm)"}, centAxis}); + histos.add("Event/nV0s", "Number of V0s per event", kTH1F, {{200, 0., 200., "N_{V0s}"}}); + histos.add("Event/nKaons", "Number of kaons per event", kTH1F, {{200, 0., 200., "N_{kaon}"}}); + histos.add("Event/nLambdasAfterCuts", "Number of Lambdas per event after cuts", kTH1F, {{100, 0., 100., "N_{Lambda}"}}); + histos.add("Event/nKaonsAfterCuts", "Number of charged kaons per event after cuts", kTH1F, {{100, 0., 100., "N_{Kaon}"}}); + if (doprocessK0sLambda || doprocessMCK0sLambda) { + histos.add("Event/nK0s", "Number of K0s candidates per event before cuts", kTH1F, {{200, 0., 200., "N_{K^{0}_{S}}"}}); + histos.add("Event/nK0sAfterCuts", "Number of K0s per event after cuts", kTH1F, {{100, 0., 100., "N_{K^{0}_{S}}"}}); + } + histos.add("Event/hRotBkg", "Rotated angle of rotated background", HistType::kTH1F, {axisRot}); + } + + if (cfgFillEventQA && doprocessK0sLambdaMEDF) { + const int mixingDepth = std::max(0, nEvtMixing.value); + const AxisSpec mixingCountAxis{mixingDepth + 1, -0.5, mixingDepth + 0.5, "N_{events}"}; + const AxisSpec mixingVertexAxis{200, -20., 20., "Current-event vertex z (cm)"}; + const AxisSpec mixingCentAxis{110, 0., 110., "Current-event centrality (%)"}; + const AxisSpec mixingV0Axis{201, -0.5, 200.5, "Current-event N_{V0s} before candidate cuts"}; + const AxisSpec previousVertexAxis{200, -20., 20., "Previous-event vertex z (cm)"}; + const AxisSpec previousCentAxis{110, 0., 110., "Previous-event centrality (%)"}; + const AxisSpec previousV0Axis{201, -0.5, 200.5, "Previous-event N_{V0s} before candidate cuts"}; + histos.add("MixingQA/K0sLambdaMEDF/hPosZvsCent", "Current mixing events;Vertex z (cm);Centrality (%)", kTH2F, {mixingVertexAxis, mixingCentAxis}); + histos.add("MixingQA/K0sLambdaMEDF/hPoolSize", "Buffered events before mixing, including INEL-rejected slots (one entry per accepted current event)", kTH1F, {mixingCountAxis}); + histos.add("MixingQA/K0sLambdaMEDF/hNPartners", "Accepted event partners before V0 cuts (one entry per accepted current event)", kTH1F, {mixingCountAxis}); + histos.add("MixingQA/K0sLambdaMEDF/hPosZPair", "Mixed event vertices", kTH2F, {previousVertexAxis, mixingVertexAxis}); + histos.add("MixingQA/K0sLambdaMEDF/hCentPair", "Mixed event centralities", kTH2F, {previousCentAxis, mixingCentAxis}); + histos.add("MixingQA/K0sLambdaMEDF/hV0CountsPair", "Mixed event V0 counts before candidate cuts", kTH2F, {previousV0Axis, mixingV0Axis}); + } + + if (cfgFillQA && (doprocessDataWithTracks || doprocessDataWithMicroTracks || doprocessMCWithTracks || doprocessMCWithMicroTracks || doprocessK0sLambda || doprocessMCK0sLambda)) { // Lambda QA histograms + histos.add("QAbefore/lambdaDCAtoPV", "Lambda DCA to PV before cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAbefore/lambdaMass", "Lambda mass before cuts", kTH1F, {lambdaMassAxis}); histos.add("QAbefore/lambdaMassAnti", "Anti-Lambda mass before cuts", kTH1F, {lambdaMassAxis}); histos.add("QAbefore/lambdaPt", "Lambda pT before cuts", kTH1F, {ptAxisQA}); @@ -180,8 +290,15 @@ struct Xi1820Analysis { histos.add("QAbefore/lambdaProperLifetime", "Lambda proper lifetime before cuts", kTH2F, {ptAxisQA, lifetimeAxis}); histos.add("QAbefore/lambdaDauPosDCA", "Lambda positive daughter DCA before cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAbefore/lambdaDauNegDCA", "Lambda negative daughter DCA before cuts", kTH2F, {ptAxisQA, dcaAxis}); + histos.add("QAbefore/lambdaDaughterTPCNSigmaPosPr", "Lambda daughter proton TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/lambdaDaughterTPCNSigmaNegPi", "Lambda daughter pi- TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/lambdaAntiDaughterTPCNSigmaPosPi", "Anti-Lambda daughter pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/lambdaAntiDaughterTPCNSigmaNegPr", "Anti-Lambda daughter proton TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/lambdaNCrossedRowsPos", "Lambda positive daughter crossed rows before cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); + histos.add("QAbefore/lambdaNCrossedRowsNeg", "Lambda negative daughter crossed rows before cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); histos.add("QAbefore/lambdaArmenterosPodolanski", "Lambda candidate Armenteros-Podolanski before cuts", kTH3F, {armenterosAlphaAxis, armenterosQtAxis, ptAxisQA}); + histos.add("QAafter/lambdaDCAtoPV", "Lambda DCA to PV after cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAafter/lambdaMass", "Lambda mass after cuts", kTH1F, {lambdaMassAxis}); histos.add("QAafter/lambdaMassAnti", "Anti-Lambda mass after cuts", kTH1F, {lambdaMassAxis}); histos.add("QAafter/lambdaPt", "Lambda pT after cuts", kTH1F, {ptAxisQA}); @@ -192,10 +309,16 @@ struct Xi1820Analysis { histos.add("QAafter/lambdaProperLifetime", "Lambda proper lifetime after cuts", kTH2F, {ptAxisQA, lifetimeAxis}); histos.add("QAafter/lambdaDauPosDCA", "Lambda positive daughter DCA after cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAafter/lambdaDauNegDCA", "Lambda negative daughter DCA after cuts", kTH2F, {ptAxisQA, dcaAxis}); + histos.add("QAafter/lambdaDaughterTPCNSigmaPosPr", "Lambda daughter proton TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/lambdaDaughterTPCNSigmaNegPi", "Lambda daughter pi- TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/lambdaAntiDaughterTPCNSigmaPosPi", "Anti-Lambda daughter pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/lambdaAntiDaughterTPCNSigmaNegPr", "Anti-Lambda daughter proton TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/lambdaNCrossedRowsPos", "Lambda positive daughter crossed rows after cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); + histos.add("QAafter/lambdaNCrossedRowsNeg", "Lambda negative daughter crossed rows after cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); histos.add("QAafter/lambdaArmenterosPodolanski", "Lambda candidate Armenteros-Podolanski after cuts", kTH3F, {armenterosAlphaAxis, armenterosQtAxis, ptAxisQA}); } - if (doprocessDataWithTracks || doprocessDataWithMicroTracks || doprocessMCWithTracks) { + if (cfgFillQA && (doprocessDataWithTracks || doprocessDataWithMicroTracks || doprocessMCWithTracks || doprocessMCWithMicroTracks)) { // Kaon QA histograms histos.add("QAbefore/kaonPt", "Kaon pT before cuts", kTH1F, {ptAxisQA}); histos.add("QAbefore/kaonEta", "Kaon eta before cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); @@ -218,42 +341,69 @@ struct Xi1820Analysis { // Resonance histograms - 4 combinations // K+ Lambda - if (doprocessDataWithTracks || doprocessDataWithMicroTracks || doprocessMixedEventWithTracks || doprocessMixedEventWithMicroTracks || doprocessMCWithTracks) { + if (doprocessDataWithTracks || doprocessDataWithMicroTracks || doprocessMixedEventWithTracks || doprocessMixedEventWithMicroTracks || doprocessMEDF || doprocessMCWithTracks || doprocessMCWithMicroTracks) { + histos.add("xi1820/kplus_lambda/hInvMassKplusLambda", "Invariant mass of K^{+} + #Lambda", kTH1F, {invMassAxis}); histos.add("xi1820/kplus_lambda/hInvMassKplusLambda_Mix", "Mixed event Invariant mass of K^{+} + #Lambda", kTH1F, {invMassAxis}); - histos.add("xi1820/kplus_lambda/hMassPtCentKplusLambda", "K^{+} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); - histos.add("xi1820/kplus_lambda/hMassPtCentKplusLambda_Mix", "Mixed event K^{+} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); // K+ Anti-Lambda histos.add("xi1820/kplus_antilambda/hInvMassKplusAntiLambda", "Invariant mass of K^{+} + #bar{#Lambda}", kTH1F, {invMassAxis}); histos.add("xi1820/kplus_antilambda/hInvMassKplusAntiLambda_Mix", "Mixed event Invariant mass of K^{+} + #bar{#Lambda}", kTH1F, {invMassAxis}); - histos.add("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda", "K^{+} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); - histos.add("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda_Mix", "Mixed event K^{+} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); // K- Lambda histos.add("xi1820/kminus_lambda/hInvMassKminusLambda", "Invariant mass of K^{-} + #Lambda", kTH1F, {invMassAxis}); histos.add("xi1820/kminus_lambda/hInvMassKminusLambda_Mix", "Mixed event Invariant mass of K^{-} + #Lambda", kTH1F, {invMassAxis}); - histos.add("xi1820/kminus_lambda/hMassPtCentKminusLambda", "K^{-} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); - histos.add("xi1820/kminus_lambda/hMassPtCentKminusLambda_Mix", "Mixed event K^{-} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); // K- Anti-Lambda histos.add("xi1820/kminus_antilambda/hInvMassKminusAntiLambda", "Invariant mass of K^{-} + #bar{#Lambda}", kTH1F, {invMassAxis}); histos.add("xi1820/kminus_antilambda/hInvMassKminusAntiLambda_Mix", "Mixed event Invariant mass of K^{-} + #bar{#Lambda}", kTH1F, {invMassAxis}); - histos.add("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda", "K^{-} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); - histos.add("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda_Mix", "Mixed event K^{-} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + + if (!(additionalConfig.cfgDelCheck)) { + histos.add("xi1820/kplus_lambda/hMassPtCentKplusLambda", "K^{+} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/kplus_lambda/hMassPtCentKplusLambda_Mix", "Mixed event K^{+} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + + histos.add("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda", "K^{+} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda_Mix", "Mixed event K^{+} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda_Rot", "Rotated background K^{+} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + + histos.add("xi1820/kminus_lambda/hMassPtCentKminusLambda", "K^{-} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/kminus_lambda/hMassPtCentKminusLambda_Mix", "Mixed event K^{-} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/kminus_lambda/hMassPtCentKminusLambda_Rot", "Rotated background K^{-} + #Lambda mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + + histos.add("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda", "K^{-} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda_Mix", "Mixed event K^{-} + #bar{#Lambda} mass vs pT vs cent", kTH3D, {invMassAxis, ptAxis, centAxis}); + } else { + histos.add("xi1820/kplus_lambda/hMassPtCentDelKplusLambda", "K^{+} + #Lambda mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + histos.add("xi1820/kplus_lambda/hMassPtCentDelKplusLambda_Mix", "Mixed event K^{+} + #Lambda mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + + // K+ Anti-Lambda + histos.add("xi1820/kplus_antilambda/hMassPtCentDelKplusAntiLambda", "K^{+} + #bar{#Lambda} mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + histos.add("xi1820/kplus_antilambda/hMassPtCentDelKplusAntiLambda_Mix", "Mixed event K^{+} + #bar{#Lambda} mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + histos.add("xi1820/kplus_antilambda/hMassPtCentDelKplusAntiLambda_Rot", "Rotated background K^{+} + #bar{#Lambda} mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + + // K- Lambda + histos.add("xi1820/kminus_lambda/hMassPtCentDelKminusLambda", "K^{-} + #Lambda mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + histos.add("xi1820/kminus_lambda/hMassPtCentDelKminusLambda_Mix", "Mixed event K^{-} + #Lambda mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + histos.add("xi1820/kminus_lambda/hMassPtCentDelKminusLambda_Rot", "Rotated background K^{-} + #Lambda mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + + // K- Anti-Lambda + histos.add("xi1820/kminus_antilambda/hMassPtCentDelKminusAntiLambda", "K^{-} + #bar{#Lambda} mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + histos.add("xi1820/kminus_antilambda/hMassPtCentDelKminusAntiLambda_Mix", "Mixed event K^{-} + #bar{#Lambda} mass vs pT vs cent vs #DeltaR", kTHnSparseD, {invMassAxis, ptAxis, centAxis, axisDel}); + } } // MC Reco histograms for charged K + Lambda channel - if (doprocessMCWithTracks) { + if (doprocessMCWithTracks || doprocessMCWithMicroTracks) { histos.add("MC/kplus_antilambda/hMCRecoInvMassKplusAntiLambda", "Invariant mass of Xi(1820) to K^{-} + #Lambda (MC Reco)", kTH1F, {invMassAxis}); histos.add("MC/kplus_antilambda/hMCRecoMassPtCentKplusAntiLambda", "Xi(1820) mass vs pT vs cent (K^{-} + #Lambda) (MC Reco)", kTHnSparseD, {invMassAxis, ptAxis, centAxis, ptAxis}); - histos.add("MC/kminus_antilambda/hMCRecoInvMassKminusAntiLambda", "Invariant mass of Xi(1820) to K^{+} + #bar{#Lambda} (MC Reco)", kTH1F, {invMassAxis}); - histos.add("MC/kminus_antilambda/hMCRecoMassPtCentKminusAntiLambda", "Xi(1820) mass vs pT vs cent (K^{+} + #bar{#Lambda}) (MC Reco)", kTHnSparseD, {invMassAxis, ptAxis, centAxis, ptAxis}); + histos.add("MC/kminus_lambda/hMCRecoInvMassKminusLambda", "Invariant mass of Xi(1820) to K^{-} + #Lambda (MC Reco)", kTH1F, {invMassAxis}); + histos.add("MC/kminus_lambda/hMCRecoMassPtCentKminusLambda", "Xi(1820) mass vs pT vs cent (K^{-} + #Lambda) (MC Reco)", kTHnSparseD, {invMassAxis, ptAxis, centAxis, ptAxis}); } // K0s QA histograms - if (doprocessK0sLambda || doprocessMCK0sLambda) { + if (cfgFillQA && (doprocessK0sLambda || doprocessMCK0sLambda)) { + histos.add("QAbefore/k0sDCAtoPV", "K0s DCA to PV before cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAbefore/k0sMass", "K0s mass before cuts", kTH1F, {{100, 0.4, 0.6, "K^{0}_{S} mass (GeV/#it{c}^{2})"}}); histos.add("QAbefore/k0sPt", "K0s pT before cuts", kTH1F, {ptAxisQA}); histos.add("QAbefore/k0sEta", "K0s eta before cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); @@ -261,8 +411,13 @@ struct Xi1820Analysis { histos.add("QAbefore/k0sRadius", "K0s radius before cuts", kTH2F, {ptAxisQA, radiusAxis}); histos.add("QAbefore/k0sDauDCA", "K0s daughter DCA before cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAbefore/k0sProperLifetime", "K0s proper lifetime before cuts", kTH2F, {ptAxisQA, lifetimeAxis}); + histos.add("QAbefore/k0sDauTPCNsigmaPosPi", "K0s daughter pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/k0sDauTPCNsigmaNegPi", "K0s daughter pion TPC NSigma before cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAbefore/k0sNCrossedRowsPos", "K0s positive daughter crossed rows before cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); + histos.add("QAbefore/k0sNCrossedRowsNeg", "K0s negative daughter crossed rows before cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); histos.add("QAbefore/k0sArmenterosPodolanski", "K0s candidate Armenteros-Podolanski before cuts", kTH3F, {armenterosAlphaAxis, armenterosQtAxis, ptAxisQA}); + histos.add("QAafter/k0sDCAtoPV", "K0s DCA to PV after cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAafter/k0sMass", "K0s mass after cuts", kTH1F, {{100, 0.4, 0.6, "K^{0}_{S} mass (GeV/#it{c}^{2})"}}); histos.add("QAafter/k0sPt", "K0s pT after cuts", kTH1F, {ptAxisQA}); histos.add("QAafter/k0sEta", "K0s eta after cuts", kTH1F, {{100, -2.0, 2.0, "#eta"}}); @@ -270,21 +425,40 @@ struct Xi1820Analysis { histos.add("QAafter/k0sRadius", "K0s radius after cuts", kTH2F, {ptAxisQA, radiusAxis}); histos.add("QAafter/k0sDauDCA", "K0s daughter DCA after cuts", kTH2F, {ptAxisQA, dcaAxis}); histos.add("QAafter/k0sProperLifetime", "K0s proper lifetime after cuts", kTH2F, {ptAxisQA, lifetimeAxis}); + histos.add("QAafter/k0sDauTPCNsigmaPosPi", "K0s daughter pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/k0sDauTPCNsigmaNegPi", "K0s daughter pion TPC NSigma after cuts", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAafter/k0sNCrossedRowsPos", "K0s positive daughter crossed rows after cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); + histos.add("QAafter/k0sNCrossedRowsNeg", "K0s negative daughter crossed rows after cuts", kTH2F, {ptAxisQA, {160, 0, 160, "TPC crossed rows"}}); histos.add("QAafter/k0sArmenterosPodolanski", "K0s candidate Armenteros-Podolanski after cuts", kTH3F, {armenterosAlphaAxis, armenterosQtAxis, ptAxisQA}); } // K0s + Lambda - if (doprocessK0sLambda || doprocessK0sLambdaMixedEvent || doprocessMCK0sLambda) { + if (doprocessK0sLambda || doprocessK0sLambdaMixedEvent || doprocessK0sLambdaMEDF || doprocessMCK0sLambda) { + histos.add("xi1820/k0s_lambda/hInvMassK0sLambda", "Invariant mass of Xi(1820) to K^{0}_{S} + #Lambda", kTH1F, {invMassAxis}); histos.add("xi1820/k0s_lambda/hInvMassK0sLambda_Mix", "Mixed event Invariant mass of Xi(1820) to K^{0}_{S} + #Lambda", kTH1F, {invMassAxis}); - histos.add("xi1820/k0s_lambda/hMassPtCentK0sLambda", "Xi(1820) mass vs pT vs cent (K^{0}_{S}#Lambda)", kTH3D, {invMassAxis, ptAxis, centAxis}); - histos.add("xi1820/k0s_lambda/hMassPtCentK0sLambda_Mix", "Mixed event Xi(1820) mass vs pT vs cent (K^{0}_{S}#Lambda)", kTH3D, {invMassAxis, ptAxis, centAxis}); - - // K0s + Anti-Lambda histos.add("xi1820/k0s_antilambda/hInvMassK0sAntiLambda", "Invariant mass of Xi(1820) to K^{0}_{S} + #bar{#Lambda}", kTH1F, {invMassAxis}); histos.add("xi1820/k0s_antilambda/hInvMassK0sAntiLambda_Mix", "Mixed event Invariant mass of Xi(1820) to K^{0}_{S} + #bar{#Lambda}", kTH1F, {invMassAxis}); - histos.add("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda", "Xi(1820) mass vs pT vs cent (K^{0}_{S}#bar{#Lambda})", kTH3D, {invMassAxis, ptAxis, centAxis}); - histos.add("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda_Mix", "Mixed event Xi(1820) mass vs pT vs cent (K^{0}_{S}#bar{#Lambda})", kTH3D, {invMassAxis, ptAxis, centAxis}); + + if (!(additionalConfig.cfgDelCheck)) { + histos.add("xi1820/k0s_lambda/hMassPtCentK0sLambda", "Xi(1820) mass vs pT vs cent (K^{0}_{S}#Lambda)", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/k0s_lambda/hMassPtCentK0sLambda_Mix", "Mixed event Xi(1820) mass vs pT vs cent (K^{0}_{S}#Lambda)", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/k0s_lambda/hMassPtCentK0sLambda_Rot", "Rotated background Xi(1820) mass vs pT vs cent (K^{0}_{S}#Lambda)", kTH3D, {invMassAxis, ptAxis, centAxis}); + + // K0s + Anti-Lambda + histos.add("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda", "Xi(1820) mass vs pT vs cent (K^{0}_{S}#bar{#Lambda})", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda_Mix", "Mixed event Xi(1820) mass vs pT vs cent (K^{0}_{S}#bar{#Lambda})", kTH3D, {invMassAxis, ptAxis, centAxis}); + histos.add("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda_Rot", "Rotated background Xi(1820) mass vs pT vs cent (K^{0}_{S}#bar{#Lambda})", kTH3D, {invMassAxis, ptAxis, centAxis}); + } else { + histos.add("xi1820/k0s_lambda/hMassPtCentDelK0sLambda", "Xi(1820) mass vs pT vs cent vs #DeltaR (K^{0}_{S}#Lambda)", kTHnSparseD, {{invMassAxis, ptAxis, centAxis, axisDel}}); + histos.add("xi1820/k0s_lambda/hMassPtCentDelK0sLambda_Mix", "Mixed event Xi(1820) mass vs pT vs cent vs #DeltaR (K^{0}_{S}#Lambda)", kTHnSparseD, {{invMassAxis, ptAxis, centAxis, axisDel}}); + histos.add("xi1820/k0s_lambda/hMassPtCentDelK0sLambda_Rot", "Rotated background Xi(1820) mass vs pT vs cent vs #DeltaR (K^{0}_{S}#Lambda)", kTHnSparseD, {{invMassAxis, ptAxis, centAxis, axisDel}}); + + // K0s + Anti-Lambda + histos.add("xi1820/k0s_antilambda/hMassPtCentDelK0sAntiLambda", "Xi(1820) mass vs pT vs cent vs #DeltaR (K^{0}_{S}#bar{#Lambda})", kTHnSparseD, {{invMassAxis, ptAxis, centAxis, axisDel}}); + histos.add("xi1820/k0s_antilambda/hMassPtCentDelK0sAntiLambda_Mix", "Mixed event Xi(1820) mass vs pT vs cent vs #DeltaR (K^{0}_{S}#bar{#Lambda})", kTHnSparseD, {{invMassAxis, ptAxis, centAxis, axisDel}}); + histos.add("xi1820/k0s_antilambda/hMassPtCentDelK0sAntiLambda_Rot", "Rotated background Xi(1820) mass vs pT vs cent vs #DeltaR (K^{0}_{S}#bar{#Lambda})", kTHnSparseD, {{invMassAxis, ptAxis, centAxis, axisDel}}); + } } if (doprocessMCK0sLambda) { @@ -295,6 +469,22 @@ struct Xi1820Analysis { histos.add("MC/k0s_antilambda/hMCRecoMassPtCentK0sAntiLambda", "Xi(1820) mass vs pT vs cent (K^{0}_{S}#bar{#Lambda}) (MC Reco)", kTHnSparseD, {invMassAxis, ptAxis, centAxis, ptAxis}); } + if (cfgFillTruthQA && (doprocessMCWithTracks || doprocessMCWithMicroTracks || doprocessMCK0sLambda)) { + histos.add("QAMCTrue/lambdaPt", "Truth-matched Lambda pT per signal pair", kTH1F, {ptAxisQA}); + histos.add("QAMCTrue/lambdaDaughterTPCNSigmaPi", "Truth-matched Lambda pion TPC PID", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAMCTrue/lambdaDaughterTPCNSigmaPr", "Truth-matched Lambda proton TPC PID", kTH2F, {ptAxisQA, nsigmaAxis}); + if (doprocessMCWithTracks || doprocessMCWithMicroTracks) { + histos.add("QAMCTrue/kaonPt", "Truth-matched kaon pT per signal pair", kTH1F, {ptAxisQA}); + histos.add("QAMCTrue/kaonDCAxy", "Truth-matched kaon DCAxy", kTH2F, {ptAxisQA, dcaxyAxis}); + histos.add("QAMCTrue/kaonDCAz", "Truth-matched kaon DCAz", kTH2F, {ptAxisQA, dcazAxis}); + histos.add("QAMCTrue/kaonTPCNSigma", "Truth-matched kaon TPC PID", kTH2F, {ptAxisQA, nsigmaAxis}); + histos.add("QAMCTrue/kaonTOFNSigma", "Truth-matched kaon TOF PID", kTH2F, {ptAxisQA, nsigmaAxis}); + } + if (doprocessMCK0sLambda) { + histos.add("QAMCTrue/k0sPt", "Truth-matched K0s pT per signal pair", kTH1F, {ptAxisQA}); + } + } + if (doprocessMCGen) { histos.add("multQA/h2MultCentMC", "Multiplicity vs Centrality MC", HistType::kTH2D, {centAxis, additionalConfig.multNTracksAxis}); // MC truth invariant mass vs pT (2D) @@ -309,21 +499,79 @@ struct Xi1820Analysis { AxisSpec rapidityAxis = {100, -2.0, 2.0, "y"}; if (doprocessMCTruth) { - histos.add("MC/hMCTruthXi1820Pt", "MC Generated Xi(1820) pT", kTH1F, {ptAxis}); - histos.add("MC/hMCTruthXi1820PtEta", "MC Generated Xi(1820) pT vs eta", kTH2F, {ptAxis, etaAxis}); - histos.add("MC/hMCTruthXi1820Y", "MC Generated Xi(1820) rapidity", kTH1F, {rapidityAxis}); + histos.add("MC/hMCTruthXi1820Pt", "MC Generated Xi(1820) pT", kTH1D, {ptAxis}); + histos.add("MC/hMCTruthXi1820PtEta", "MC Generated Xi(1820) pT vs eta", kTH2D, {ptAxis, etaAxis}); + histos.add("MC/hMCTruthXi1820Y", "MC Generated Xi(1820) rapidity", kTH1D, {rapidityAxis}); // MC truth invariant mass (from MC particles) - histos.add("MC/hMCTruthInvMassKminusLambda", "MC Truth Inv Mass K^{-}#Lambda", kTH1F, {invMassAxis}); - histos.add("MC/hMCTruthInvMassKplusAntiLambda", "MC Truth Inv Mass K^{+}#bar{#Lambda}", kTH1F, {invMassAxis}); - histos.add("MC/hMCTruthInvMassK0sLambda", "MC Truth Inv Mass K^{0}_{S}#Lambda", kTH1F, {invMassAxis}); - histos.add("MC/hMCTruthInvMassK0sAntiLambda", "MC Truth Inv Mass K^{0}_{S}#bar{#Lambda}", kTH1F, {invMassAxis}); + histos.add("MC/hMCTruthInvMassKminusLambda", "MC Truth Inv Mass K^{-}#Lambda", kTH1D, {invMassAxis}); + histos.add("MC/hMCTruthInvMassKplusAntiLambda", "MC Truth Inv Mass K^{+}#bar{#Lambda}", kTH1D, {invMassAxis}); + histos.add("MC/hMCTruthInvMassK0sLambda", "MC Truth Inv Mass K^{0}_{S}#Lambda", kTH1D, {invMassAxis}); + histos.add("MC/hMCTruthInvMassK0sAntiLambda", "MC Truth Inv Mass K^{0}_{S}#bar{#Lambda}", kTH1D, {invMassAxis}); // MC truth invariant mass vs pT (2D) - histos.add("MC/hMCTruthMassPtKminusLambda", "MC Truth Mass vs pT K^{-}#Lambda", kTH2F, {invMassAxis, ptAxis}); - histos.add("MC/hMCTruthMassPtKplusAntiLambda", "MC Truth Mass vs pT K^{+}#bar{#Lambda}", kTH2F, {invMassAxis, ptAxis}); - histos.add("MC/hMCTruthMassPtK0sLambda", "MC Truth Mass vs pT K^{0}_{S}#Lambda", kTH2F, {invMassAxis, ptAxis}); - histos.add("MC/hMCTruthMassPtK0sAntiLambda", "MC Truth Mass vs pT K^{0}_{S}#bar{#Lambda}", kTH2F, {invMassAxis, ptAxis}); + histos.add("MC/hMCTruthMassPtKminusLambda", "MC Truth Mass vs pT K^{-}#Lambda", kTH2D, {invMassAxis, ptAxis}); + histos.add("MC/hMCTruthMassPtKplusAntiLambda", "MC Truth Mass vs pT K^{+}#bar{#Lambda}", kTH2D, {invMassAxis, ptAxis}); + histos.add("MC/hMCTruthMassPtK0sLambda", "MC Truth Mass vs pT K^{0}_{S}#Lambda", kTH2D, {invMassAxis, ptAxis}); + histos.add("MC/hMCTruthMassPtK0sAntiLambda", "MC Truth Mass vs pT K^{0}_{S}#bar{#Lambda}", kTH2D, {invMassAxis, ptAxis}); + } + } + template + static std::array v0DaughterIds(const V0Type& v0) + { + const auto& indices = v0.indices(); + return {indices[0], indices[1]}; + } + + template + static bool sharesAnyDaughterId(const std::array& first, const std::array& second) + { + for (const auto& firstId : first) { + for (const auto& secondId : second) { + if (firstId == secondId) { + return true; + } + } + } + return false; + } + + template + static float deltaR(const FirstVectorType& first, const SecondVectorType& second) + { + const auto dPhi = RecoDecay::constrainAngle(first.Phi() - second.Phi(), -constants::math::PI); + const auto dEta = first.Eta() - second.Eta(); + return std::sqrt(dPhi * dPhi + dEta * dEta); + } + + bool passesRapidity(float rapidity) const + { + return -additionalConfig.cfgRapidityCut.value < rapidity && rapidity < additionalConfig.cfgRapidityCut.value; + } + + template + void fillTruthLambdaQA(const V0Type& lambda, bool isLambda) + { + if (!cfgFillTruthQA) { + return; + } + histos.fill(HIST("QAMCTrue/lambdaPt"), lambda.pt()); + histos.fill(HIST("QAMCTrue/lambdaDaughterTPCNSigmaPi"), lambda.pt(), isLambda ? lambda.daughterTPCNSigmaNegPi() : lambda.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAMCTrue/lambdaDaughterTPCNSigmaPr"), lambda.pt(), isLambda ? lambda.daughterTPCNSigmaPosPr() : lambda.daughterTPCNSigmaNegPr()); + } + + template + void fillTruthKaonQA(const TrackType& track) + { + if (!cfgFillTruthQA) { + return; + } + histos.fill(HIST("QAMCTrue/kaonPt"), track.pt()); + histos.fill(HIST("QAMCTrue/kaonDCAxy"), track.pt(), track.dcaXY()); + histos.fill(HIST("QAMCTrue/kaonDCAz"), track.pt(), track.dcaZ()); + histos.fill(HIST("QAMCTrue/kaonTPCNSigma"), track.pt(), track.tpcNSigmaKa()); + if (track.hasTOF() && !std::isnan(track.tofNSigmaKa())) { + histos.fill(HIST("QAMCTrue/kaonTOFNSigma"), track.pt(), track.tofNSigmaKa()); } } @@ -332,27 +580,39 @@ struct Xi1820Analysis { bool v0Cut(const CollisionType& collision, const V0Type& v0, bool isLambda) { // Basic kinematic cuts - if (std::abs(v0.eta()) > cMaxV0Etacut) + if (std::abs(v0.eta()) >= cMaxV0Etacut) { + return false; + } + if (v0.pt() <= cMinPtcut) { return false; - if (v0.pt() < cMinPtcut) + } + + // DCA to PV + if (std::abs(v0.dcav0topv()) >= additionalConfig.cMaxDcaToPVV0Lambda) { return false; + } // Topological cuts - if (v0.v0CosPA() < cV0MinCosPA) + if (v0.v0CosPA() <= cV0MinCosPA) { return false; - if (v0.daughDCA() > cV0MaxDaughDCA) + } + if (v0.daughDCA() >= cV0MaxDaughDCA) { return false; + } // Daughter DCA to PV cuts - if (std::abs(v0.dcapostopv()) < cV0DauPosDCAtoPVMin) + if (std::abs(v0.dcapostopv()) <= cV0DauPosDCAtoPVMin) { return false; - if (std::abs(v0.dcanegtopv()) < cV0DauNegDCAtoPVMin) + } + if (std::abs(v0.dcanegtopv()) <= cV0DauNegDCAtoPVMin) { return false; + } // Radius cuts float radius = v0.transRadius(); - if (radius < cV0RadiusMin || radius > cV0RadiusMax) + if (radius <= cV0RadiusMin || radius >= cV0RadiusMax) { return false; + } // Proper lifetime cut float dx = v0.decayVtxX() - collision.posX(); @@ -360,27 +620,53 @@ struct Xi1820Analysis { float dz = v0.decayVtxZ() - collision.posZ(); float l = std::sqrt(dx * dx + dy * dy + dz * dz); float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallMomentumDenominator)) * MassLambda; - if (properLifetime > cV0ProperLifetimeMax) + auto properLifetime = (l / (p + SmallMomentumDenominator)) * MassLambda; + if (properLifetime >= cV0ProperLifetimeMax) { return false; + } // Mass window if (isLambda) { - if (std::abs(v0.mLambda() - MassLambda) > cV0MassWindow) + if (std::abs(v0.mLambda() - MassLambda) >= cV0MassWindow) { return false; + } + // TPC Nsigma cuts for daughter tracks + if (!passesPIDWindow(v0.daughterTPCNSigmaNegPi(), cLambdaDaughterPiTPCNSigmaMin, cLambdaDaughterPiTPCNSigmaMax, cLambdaDaughterPiTPCNSigmaMean)) { + return false; + } + if (!passesPIDWindow(v0.daughterTPCNSigmaPosPr(), cLambdaDaughterPrTPCNSigmaMin, cLambdaDaughterPrTPCNSigmaMax, cLambdaDaughterPrTPCNSigmaMean)) { + return false; + } + // Note: TOF beta cut that calibrated by primary vertex is not applied for V0 daughters in this moment } else { - if (std::abs(v0.mAntiLambda() - MassLambda) > cV0MassWindow) + if (std::abs(v0.mAntiLambda() - MassLambda) >= cV0MassWindow) { return false; + } + if (!passesPIDWindow(v0.daughterTPCNSigmaPosPi(), cLambdaDaughterPiTPCNSigmaMin, cLambdaDaughterPiTPCNSigmaMax, cLambdaDaughterPiTPCNSigmaMean)) { + return false; + } + if (!passesPIDWindow(v0.daughterTPCNSigmaNegPr(), cLambdaDaughterPrTPCNSigmaMin, cLambdaDaughterPrTPCNSigmaMax, cLambdaDaughterPrTPCNSigmaMean)) { + return false; + } + // Note: TOF beta cut that calibrated by primary vertex is not applied for V0 daughters in this moment } if (cV0sCrossMassRejection) { - if (std::abs(v0.mK0Short() - MassK0Short) < cV0sCrossMassRejectionWindow) + if (std::abs(v0.mK0Short() - MassK0Short) <= cV0sCrossMassRejectionWindow) { return false; + } } - if (v0.qtarm() > cK0sArmenterosAlphaCoeff * std::fabs(v0.alpha())) + if (v0.nCrossedRowsPos() <= cLambdaPosDaughterMinCrossedRows) { + return false; + } + if (v0.nCrossedRowsNeg() <= cLambdaNegDaughterMinCrossedRows) { return false; + } + if (v0.qtarm() >= cK0sArmenterosAlphaCoeff * std::fabs(v0.alpha())) { + return false; + } return true; } @@ -389,31 +675,39 @@ struct Xi1820Analysis { bool k0sCut(const CollisionType& collision, const V0Type& v0) { // Basic kinematic cuts - if (std::abs(v0.eta()) > cMaxV0Etacut) + if (std::abs(v0.eta()) >= cMaxV0Etacut) { return false; - if (v0.pt() < cMinPtcut) + } + if (v0.pt() <= cMinPtcut) { return false; + } // Topological cuts - if (v0.v0CosPA() < cK0sMinCosPA) + if (v0.v0CosPA() <= cK0sMinCosPA) { return false; - if (v0.daughDCA() > cK0sMaxDaughDCA) + } + if (v0.daughDCA() >= cK0sMaxDaughDCA) { return false; + } // Daughter DCA to PV cuts - if (std::abs(v0.dcapostopv()) < cK0sDauPosDCAtoPVMin) + if (std::abs(v0.dcapostopv()) <= cK0sDauPosDCAtoPVMin) { return false; - if (std::abs(v0.dcanegtopv()) < cK0sDauNegDCAtoPVMin) + } + if (std::abs(v0.dcanegtopv()) <= cK0sDauNegDCAtoPVMin) { return false; + } // Radius cuts float radius = v0.transRadius(); - if (radius < cK0sRadiusMin || radius > cK0sRadiusMax) + if (radius <= cK0sRadiusMin || radius >= cK0sRadiusMax) { return false; + } // DCA to PV - if (std::abs(v0.dcav0topv()) > additionalConfig.cMaxDcaToPVV0) + if (std::abs(v0.dcav0topv()) >= additionalConfig.cMaxDcaToPVV0K0s) { return false; + } // Proper lifetime cut float dx = v0.decayVtxX() - collision.posX(); @@ -421,209 +715,249 @@ struct Xi1820Analysis { float dz = v0.decayVtxZ() - collision.posZ(); float l = std::sqrt(dx * dx + dy * dy + dz * dz); float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallMomentumDenominator)) * MassK0Short; - if (properLifetime > cK0sProperLifetimeMax) + auto properLifetime = (l / (p + SmallMomentumDenominator)) * MassK0Short; + if (properLifetime >= cK0sProperLifetimeMax) { return false; + } // Mass window - if (std::abs(v0.mK0Short() - MassK0Short) > cK0sMassWindow) + if (std::abs(v0.mK0Short() - MassK0Short) >= cK0sMassWindow) { return false; + } // Competing V0 rejection: remove (Anti)Λ if (cK0sCrossMassRejection) { - if (std::abs(v0.mLambda() - MassLambda) < cK0sCrossMassRejectionWindow) + if (std::abs(v0.mLambda() - MassLambda) <= cK0sCrossMassRejectionWindow) { return false; - if (std::abs(v0.mAntiLambda() - MassLambda) < cK0sCrossMassRejectionWindow) + } + if (std::abs(v0.mAntiLambda() - MassLambda) <= cK0sCrossMassRejectionWindow) { return false; + } } - if (v0.qtarm() < cK0sArmenterosAlphaCoeff * std::fabs(v0.alpha())) + if (!passesPIDWindow(v0.daughterTPCNSigmaPosPi(), cK0sDaughterPiTPCNSigmaMin, cK0sDaughterPiTPCNSigmaMax, cK0sDaughterPiTPCNSigmaMean)) { + return false; + } + if (!passesPIDWindow(v0.daughterTPCNSigmaNegPi(), cK0sDaughterPiTPCNSigmaMin, cK0sDaughterPiTPCNSigmaMax, cK0sDaughterPiTPCNSigmaMean)) { return false; + } + // Note: TOF beta cut that calibrated by primary vertex is not applied for V0 daughters in this moment + + if (v0.nCrossedRowsPos() <= cK0sPosDaughterMinCrossedRows) { + return false; + } + if (v0.nCrossedRowsNeg() <= cK0sNegDaughterMinCrossedRows) { + return false; + } + + if (v0.qtarm() <= cK0sArmenterosAlphaCoeff * std::fabs(v0.alpha())) { + return false; + } return true; } - // Helper function to find pT bin index + // Strict signed window, retaining NaN rejection without negated compound comparisons. + static bool passesPIDWindow(float nSigma, float minimum, float maximum, float mean) + { + const float centered = nSigma - mean; + return minimum < centered && centered < maximum; + } + + // Preserve pT-bin membership [low, high); PID window edges themselves are strict. int getPtBinIndex(float pt) { - auto ptBins = static_cast>(cKaonPIDPtBins); - for (size_t i = 0; i < ptBins.size() - 1; i++) { - if (pt >= ptBins[i] && pt < ptBins[i + 1]) { - return i; + const auto& ptBins = cKaonPIDPtBins.value; + for (std::size_t i = 1; i < ptBins.size(); ++i) { + if (pt >= ptBins[i - 1] && pt < ptBins[i]) { + return static_cast(i - 1); } } - return -1; // should not happen if bins are properly configured + return -1; } - // Kaon PID selection - template + template bool kaonPidCut(const TrackType& track) { - float pt = track.pt(); - - if constexpr (IsResoMicrotrack) { - // For ResoMicroTracks - decode PID from flags - float tpcNSigma = o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(track.pidNSigmaKaFlag()); - float tofNSigma = track.hasTOF() ? o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(track.pidNSigmaKaFlag()) : 999.f; - - if (cKaonUsePtDepPID) { - // pT-dependent PID with binning - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; // safety check - - auto tpcCuts = static_cast>(cKaonTPCNSigmaCuts); - auto tofCuts = static_cast>(cKaonTOFNSigmaCuts); - auto tofRequired = static_cast>(cKaonTOFRequired); - - // Check array sizes - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; // safety check - } - - // Apply TPC cut - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) - return false; - - // Apply TOF requirement and cut - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - // TOF optional but apply cut if present - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } - - return true; - } else { - // Standard PID - bool tpcPass = std::abs(tpcNSigma) < cKaonTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cKaonTOFNSigmaMax : true; - return tpcPass && tofPass; + const bool useTOF = !cKaonBypassTOF && track.hasTOF(); + float tpcMin = cKaonTPCNSigmaMin; + float tpcMax = cKaonTPCNSigmaMax; + float tofMin = cKaonTOFNSigmaMin; + float tofMax = cKaonTOFNSigmaMax; + if (cKaonUsePtDepPID) { + const int ptBin = getPtBinIndex(track.pt()); + if (ptBin < 0) { + return false; } - } else { - // For ResoTracks - direct access - float tpcNSigma = track.tpcNSigmaKa(); - float tofNSigma = track.hasTOF() ? track.tofNSigmaKa() : 999.f; - - if (cKaonUsePtDepPID) { - // pT-dependent PID with binning - int ptBin = getPtBinIndex(pt); - if (ptBin < 0) - return false; // safety check - - auto tpcCuts = static_cast>(cKaonTPCNSigmaCuts); - auto tofCuts = static_cast>(cKaonTOFNSigmaCuts); - auto tofRequired = static_cast>(cKaonTOFRequired); - - // Check array sizes - if (ptBin >= static_cast(tpcCuts.size()) || - ptBin >= static_cast(tofCuts.size()) || - ptBin >= static_cast(tofRequired.size())) { - return false; // safety check - } - - // Apply TPC cut - if (std::abs(tpcNSigma) >= tpcCuts[ptBin]) + const auto bin = static_cast(ptBin); + if (bin >= cKaonTPCNSigmaCuts.value.size() || bin >= cKaonTPCNSigmaMinCuts.value.size()) { + return false; + } + tpcMin = cKaonTPCNSigmaMinCuts.value[bin]; + tpcMax = cKaonTPCNSigmaCuts.value[bin]; + if (useTOF) { + if (bin >= cKaonTOFNSigmaCuts.value.size() || bin >= cKaonTOFNSigmaMinCuts.value.size()) { return false; - - // Apply TOF requirement and cut - if (tofRequired[ptBin] != 0) { - if (!track.hasTOF()) - return false; - if (std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; - } else { - // TOF optional but apply cut if present - if (track.hasTOF() && std::abs(tofNSigma) >= tofCuts[ptBin]) - return false; } - - return true; - } else { - // Standard PID - bool tpcPass = std::abs(tpcNSigma) < cKaonTPCNSigmaMax; - bool tofPass = track.hasTOF() ? std::abs(tofNSigma) < cKaonTOFNSigmaMax : true; - return tpcPass && tofPass; + tofMin = cKaonTOFNSigmaMinCuts.value[bin]; + tofMax = cKaonTOFNSigmaCuts.value[bin]; } } + if (!passesPIDWindow(track.tpcNSigmaKa(), tpcMin, tpcMax, cKaonTPCNSigmaMean)) { + return false; + } + // No TOF-presence veto: missing or explicitly bypassed TOF uses TPC alone. + return !useTOF || + passesPIDWindow(track.tofNSigmaKa(), tofMin, tofMax, cKaonTOFNSigmaMean); } // Kaon track selection (for both ResoTracks and ResoMicroTracks) template bool kaonCut(const TrackType& track) { - float candPt = track.pt(); - // Basic kinematic cuts - if (candPt < cKaonPtMin) + const float candPt = track.pt(); + if (!std::isfinite(candPt) || !std::isfinite(track.eta()) || + !std::isfinite(track.dcaXY()) || !std::isfinite(track.dcaZ())) { return false; - if (std::abs(track.eta()) > cKaonEtaMax) + } + if (candPt <= cKaonPtMin || std::abs(track.eta()) >= cKaonEtaMax) { + return false; + } + const double dcaPtCut = additionalConfig.cUsePtDepDCAForKaons + ? additionalConfig.cDCAToPVByPtFirstP0.value + additionalConfig.cDCAToPVByPtFirstExp.value * std::pow(candPt, -static_cast(additionalConfig.cDCAToPVByPtFirstPower.value)) + : 0.; + const double dcaXYCut = additionalConfig.cUsePtDepDCAForKaons ? dcaPtCut : cKaonDCAxyMax.value; + const double dcaZCut = additionalConfig.cUsePtDepDCAForKaons ? dcaPtCut : cKaonDCAzMax.value; + if (std::abs(track.dcaXY()) >= dcaXYCut || std::abs(track.dcaZ()) >= dcaZCut) { return false; - - float dcaXY = -999.f; - float dcaZ = -999.f; - - // DCA cuts - different access for ResoMicroTracks - if constexpr (IsResoMicrotrack) { - dcaXY = o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(track.trackSelectionFlags()); - dcaZ = o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(track.trackSelectionFlags()); - - if (additionalConfig.cUsePtDepDCAForKaons) { // Insert pT dependent DCAxy,z cut (tighter than global-track w DCA cut) - if (std::abs(dcaXY) > (additionalConfig.cDCAToPVByPtFirstP0 + additionalConfig.cDCAToPVByPtFirstExp * std::pow(candPt, -1.))) - return false; - if (std::abs(dcaZ) > (additionalConfig.cDCAToPVByPtFirstP0 + additionalConfig.cDCAToPVByPtFirstExp * std::pow(candPt, -1.))) - return false; - } else { - if (std::abs(dcaXY) > cKaonDCAxyMax) - return false; - if (std::abs(dcaZ) > cKaonDCAzMax) - return false; - } - } else { - if (additionalConfig.cUsePtDepDCAForKaons) { // Insert pT dependent DCAxy,z cut (tighter than global-track w DCA cut) - if (std::abs(track.dcaXY()) > (additionalConfig.cDCAToPVByPtFirstP0 + additionalConfig.cDCAToPVByPtFirstExp * std::pow(candPt, -1.))) - return false; - if (std::abs(track.dcaZ()) > (additionalConfig.cDCAToPVByPtFirstP0 + additionalConfig.cDCAToPVByPtFirstExp * std::pow(candPt, -1.))) - return false; - } else { - if (std::abs(track.dcaXY()) > cKaonDCAxyMax) - return false; - if (std::abs(track.dcaZ()) > cKaonDCAzMax) - return false; - } } // Track quality cuts - check if fields are available (only for ResoTracks) if constexpr (!IsResoMicrotrack) { if constexpr (requires { track.tpcNClsFound(); }) { - if (track.tpcNClsFound() < cKaonTPCNClusMin) + if (track.tpcNClsFound() <= cKaonTPCNClusMin) { return false; + } } if constexpr (requires { track.itsNCls(); }) { - if (track.itsNCls() < cKaonITSNClusMin) + if (track.itsNCls() <= cKaonITSNClusMin) { return false; + } } } // PID selection - if (!kaonPidCut(track)) + if (!kaonPidCut(track)) { return false; + } + + // Flag selections for Primary track selection + if (additionalConfig.cfgPVContributor && !track.isPVContributor()) { + return false; + } + if (additionalConfig.cfgPrimaryTrack && !track.isPrimaryTrack()) { + return false; + } return true; } - template - void fillChargedKLambda(const CollisionT& collision, const V0sT& v0s, const TracksT& tracks) // Xi(1820) analysis: charged K + Lambda channel + bool hasChargedSameEventProcess() + { + return doprocessDataWithTracks || doprocessDataWithMicroTracks || doprocessMCWithTracks || doprocessMCWithMicroTracks; + } + + // Select each V0 once; QA and event counts must not be weighted by kaon multiplicity. + template + auto selectLambdas(const CollisionT& collision, const V0sT& v0s, bool fillSharedQA = true) + { + std::vector> selected; + selected.reserve(v0s.size()); + for (const auto& v0 : v0s) { + // Lambda QA before cuts + if (!IsMix && cfgFillQA && fillSharedQA) { + histos.fill(HIST("QAbefore/lambdaDCAtoPV"), v0.pt(), v0.dcav0topv()); + histos.fill(HIST("QAbefore/lambdaMass"), v0.mLambda()); + histos.fill(HIST("QAbefore/lambdaMassAnti"), v0.mAntiLambda()); + histos.fill(HIST("QAbefore/lambdaPt"), v0.pt()); + histos.fill(HIST("QAbefore/lambdaEta"), v0.eta()); + histos.fill(HIST("QAbefore/lambdaCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAbefore/lambdaRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAbefore/lambdaDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAbefore/lambdaDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAbefore/lambdaDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAbefore/lambdaDaughterTPCNSigmaPosPr"), v0.pt(), v0.daughterTPCNSigmaPosPr()); + histos.fill(HIST("QAbefore/lambdaDaughterTPCNSigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAbefore/lambdaAntiDaughterTPCNSigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAbefore/lambdaAntiDaughterTPCNSigmaNegPr"), v0.pt(), v0.daughterTPCNSigmaNegPr()); + histos.fill(HIST("QAbefore/lambdaNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAbefore/lambdaNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + + // Calculate proper lifetime manually + float dx = v0.decayVtxX() - collision.posX(); + float dy = v0.decayVtxY() - collision.posY(); + float dz = v0.decayVtxZ() - collision.posZ(); + float l = std::sqrt(dx * dx + dy * dy + dz * dz); + float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + auto properLifetime = (l / (p + SmallMomentumDenominator)) * MassLambda; + histos.fill(HIST("QAbefore/lambdaProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAbefore/lambdaArmenterosPodolanski"), v0.alpha(), v0.qtarm(), v0.pt()); + } + + // Try Lambda + bool isLambda = v0Cut(collision, v0, true); + // Try Anti-Lambda + bool isAntiLambda = v0Cut(collision, v0, false); + + if (!isLambda && !isAntiLambda) { + continue; + } + + if (!IsMix && cfgFillQA && fillSharedQA) { + // QA after cuts (fill for whichever passes) + if (isLambda) { + histos.fill(HIST("QAafter/lambdaMass"), v0.mLambda()); + histos.fill(HIST("QAafter/lambdaDaughterTPCNSigmaPosPr"), v0.pt(), v0.daughterTPCNSigmaPosPr()); + histos.fill(HIST("QAafter/lambdaDaughterTPCNSigmaNegPi"), v0.pt(), v0.daughterTPCNSigmaNegPi()); + } + if (isAntiLambda) { + histos.fill(HIST("QAafter/lambdaMassAnti"), v0.mAntiLambda()); + histos.fill(HIST("QAafter/lambdaAntiDaughterTPCNSigmaPosPi"), v0.pt(), v0.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAafter/lambdaAntiDaughterTPCNSigmaNegPr"), v0.pt(), v0.daughterTPCNSigmaNegPr()); + } + histos.fill(HIST("QAafter/lambdaDCAtoPV"), v0.pt(), v0.dcav0topv()); + histos.fill(HIST("QAafter/lambdaPt"), v0.pt()); + histos.fill(HIST("QAafter/lambdaEta"), v0.eta()); + histos.fill(HIST("QAafter/lambdaCosPA"), v0.pt(), v0.v0CosPA()); + histos.fill(HIST("QAafter/lambdaRadius"), v0.pt(), v0.transRadius()); + histos.fill(HIST("QAafter/lambdaDauDCA"), v0.pt(), v0.daughDCA()); + histos.fill(HIST("QAafter/lambdaDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); + histos.fill(HIST("QAafter/lambdaDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); + histos.fill(HIST("QAafter/lambdaNCrossedRowsPos"), v0.pt(), v0.nCrossedRowsPos()); + histos.fill(HIST("QAafter/lambdaNCrossedRowsNeg"), v0.pt(), v0.nCrossedRowsNeg()); + + float dx = v0.decayVtxX() - collision.posX(); + float dy = v0.decayVtxY() - collision.posY(); + float dz = v0.decayVtxZ() - collision.posZ(); + float l = std::sqrt(dx * dx + dy * dy + dz * dz); + float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); + auto properLifetime = (l / (p + SmallMomentumDenominator)) * MassLambda; + histos.fill(HIST("QAafter/lambdaProperLifetime"), v0.pt(), properLifetime); + histos.fill(HIST("QAafter/lambdaArmenterosPodolanski"), v0.alpha(), v0.qtarm(), v0.pt()); + } + + selected.push_back({v0, isLambda, isAntiLambda}); + } + return selected; + } + + template + void fillChargedKLambda(const CollisionT& collision, const V0sT& v0s, const TracksT& tracks, const LambdaCollisionT& lambdaCollision) // Xi(1820) analysis: charged K + Lambda channel { auto cent = collision.cent(); // Fill event QA histograms (only for same-event) - if constexpr (!IsMix) { + if (!IsMix && cfgFillEventQA) { histos.fill(HIST("Event/posZ"), collision.posZ()); histos.fill(HIST("Event/centrality"), cent); histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); @@ -631,36 +965,25 @@ struct Xi1820Analysis { histos.fill(HIST("Event/nKaons"), tracks.size()); } - if (additionalConfig.cConsiderPairOnly && (v0s.size() < 1 || tracks.size() < 1)) - return; // skip events that cannot form pairs if the option is enabled (for increasing processing speed when only pairs are of interest) - - // Count candidates after cuts - int nV0sAfterCuts = 0; - int nKaonsAfterCuts = 0; + std::vector selectedKaons; + selectedKaons.reserve(tracks.size()); // Build 4 combinations - ROOT::Math::PxPyPzEVector pKaon, pLambda, pRes; + ROOT::Math::PxPyPzEVector pKaon, pLambda, pRes, lDaughterRot, lResonanceRot; // Loop over kaon candidates for (const auto& kaon : tracks) { // QA before cuts - if constexpr (!IsMix) { + if (!IsMix && cfgFillQA) { histos.fill(HIST("QAbefore/kaonPt"), kaon.pt()); histos.fill(HIST("QAbefore/kaonEta"), kaon.eta()); - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAbefore/kaonDCAxy"), kaon.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(kaon.trackSelectionFlags())); - histos.fill(HIST("QAbefore/kaonDCAz"), kaon.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(kaon.trackSelectionFlags())); - histos.fill(HIST("QAbefore/kaonTPCNSigma"), kaon.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(kaon.pidNSigmaKaFlag())); - if (kaon.hasTOF()) { - histos.fill(HIST("QAbefore/kaonTOFNSigma"), kaon.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(kaon.pidNSigmaKaFlag())); - } - } else { - histos.fill(HIST("QAbefore/kaonDCAxy"), kaon.pt(), kaon.dcaXY()); - histos.fill(HIST("QAbefore/kaonDCAz"), kaon.pt(), kaon.dcaZ()); - histos.fill(HIST("QAbefore/kaonTPCNSigma"), kaon.pt(), kaon.tpcNSigmaKa()); - if (kaon.hasTOF()) { - histos.fill(HIST("QAbefore/kaonTOFNSigma"), kaon.pt(), kaon.tofNSigmaKa()); - } + histos.fill(HIST("QAbefore/kaonDCAxy"), kaon.pt(), kaon.dcaXY()); + histos.fill(HIST("QAbefore/kaonDCAz"), kaon.pt(), kaon.dcaZ()); + histos.fill(HIST("QAbefore/kaonTPCNSigma"), kaon.pt(), kaon.tpcNSigmaKa()); + if (kaon.hasTOF() && !std::isnan(kaon.tofNSigmaKa())) { + histos.fill(HIST("QAbefore/kaonTOFNSigma"), kaon.pt(), kaon.tofNSigmaKa()); + } + if constexpr (!IsResoMicrotrack) { if constexpr (requires { kaon.tpcNClsFound(); }) { histos.fill(HIST("QAbefore/kaonTPCNcls"), kaon.tpcNClsFound()); } @@ -670,28 +993,21 @@ struct Xi1820Analysis { } } - if (!kaonCut(kaon)) + if (!kaonCut(kaon)) { continue; + } - if constexpr (!IsMix) { - nKaonsAfterCuts++; + if (!IsMix && cfgFillQA) { // QA after cuts histos.fill(HIST("QAafter/kaonPt"), kaon.pt()); histos.fill(HIST("QAafter/kaonEta"), kaon.eta()); - if constexpr (IsResoMicrotrack) { - histos.fill(HIST("QAafter/kaonDCAxy"), kaon.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAxy(kaon.trackSelectionFlags())); - histos.fill(HIST("QAafter/kaonDCAz"), kaon.pt(), o2::aod::resomicrodaughter::ResoMicroTrackSelFlag::decodeDCAz(kaon.trackSelectionFlags())); - histos.fill(HIST("QAafter/kaonTPCNSigma"), kaon.pt(), o2::aod::resomicrodaughter::PidNSigma::getTPCnSigma(kaon.pidNSigmaKaFlag())); - if (kaon.hasTOF()) { - histos.fill(HIST("QAafter/kaonTOFNSigma"), kaon.pt(), o2::aod::resomicrodaughter::PidNSigma::getTOFnSigma(kaon.pidNSigmaKaFlag())); - } - } else { - histos.fill(HIST("QAafter/kaonDCAxy"), kaon.pt(), kaon.dcaXY()); - histos.fill(HIST("QAafter/kaonDCAz"), kaon.pt(), kaon.dcaZ()); - histos.fill(HIST("QAafter/kaonTPCNSigma"), kaon.pt(), kaon.tpcNSigmaKa()); - if (kaon.hasTOF()) { - histos.fill(HIST("QAafter/kaonTOFNSigma"), kaon.pt(), kaon.tofNSigmaKa()); - } + histos.fill(HIST("QAafter/kaonDCAxy"), kaon.pt(), kaon.dcaXY()); + histos.fill(HIST("QAafter/kaonDCAz"), kaon.pt(), kaon.dcaZ()); + histos.fill(HIST("QAafter/kaonTPCNSigma"), kaon.pt(), kaon.tpcNSigmaKa()); + if (kaon.hasTOF() && !std::isnan(kaon.tofNSigmaKa())) { + histos.fill(HIST("QAafter/kaonTOFNSigma"), kaon.pt(), kaon.tofNSigmaKa()); + } + if constexpr (!IsResoMicrotrack) { if constexpr (requires { kaon.tpcNClsFound(); }) { histos.fill(HIST("QAafter/kaonTPCNcls"), kaon.tpcNClsFound()); } @@ -701,204 +1017,283 @@ struct Xi1820Analysis { } } - int kaonCharge = kaon.sign(); - - // Loop over V0 candidates - for (const auto& v0 : v0s) { - // Lambda QA before cuts - if constexpr (!IsMix) { - histos.fill(HIST("QAbefore/lambdaMass"), v0.mLambda()); - histos.fill(HIST("QAbefore/lambdaMassAnti"), v0.mAntiLambda()); - histos.fill(HIST("QAbefore/lambdaPt"), v0.pt()); - histos.fill(HIST("QAbefore/lambdaEta"), v0.eta()); - histos.fill(HIST("QAbefore/lambdaCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAbefore/lambdaRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAbefore/lambdaDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAbefore/lambdaDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAbefore/lambdaDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - - // Calculate proper lifetime manually - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallMomentumDenominator)) * MassLambda; - histos.fill(HIST("QAbefore/lambdaProperLifetime"), v0.pt(), properLifetime); - histos.fill(HIST("QAbefore/lambdaArmenterosPodolanski"), v0.alpha(), v0.qtarm(), v0.pt()); - } - - // Try Lambda - bool isLambda = v0Cut(collision, v0, true); - // Try Anti-Lambda - bool isAntiLambda = v0Cut(collision, v0, false); - - if (!isLambda && !isAntiLambda) - continue; + selectedKaons.push_back(kaon); + } + const auto selectedLambdas = selectLambdas(lambdaCollision, v0s); + for (const auto& kaon : selectedKaons) { + const int kaonCharge = kaon.sign(); + for (const auto& selected : selectedLambdas) { + const auto& v0 = selected.candidate; if constexpr (!IsMix) { - nV0sAfterCuts++; - // QA after cuts (fill for whichever passes) - if (isLambda) { - histos.fill(HIST("QAafter/lambdaMass"), v0.mLambda()); - } - if (isAntiLambda) { - histos.fill(HIST("QAafter/lambdaMassAnti"), v0.mAntiLambda()); + const auto daughterIds = v0DaughterIds(v0); + if (kaon.trackId() == daughterIds[0] || kaon.trackId() == daughterIds[1]) { + continue; } - histos.fill(HIST("QAafter/lambdaPt"), v0.pt()); - histos.fill(HIST("QAafter/lambdaEta"), v0.eta()); - histos.fill(HIST("QAafter/lambdaCosPA"), v0.pt(), v0.v0CosPA()); - histos.fill(HIST("QAafter/lambdaRadius"), v0.pt(), v0.transRadius()); - histos.fill(HIST("QAafter/lambdaDauDCA"), v0.pt(), v0.daughDCA()); - histos.fill(HIST("QAafter/lambdaDauPosDCA"), v0.pt(), std::abs(v0.dcapostopv())); - histos.fill(HIST("QAafter/lambdaDauNegDCA"), v0.pt(), std::abs(v0.dcanegtopv())); - - float dx = v0.decayVtxX() - collision.posX(); - float dy = v0.decayVtxY() - collision.posY(); - float dz = v0.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(v0.px() * v0.px() + v0.py() * v0.py() + v0.pz() * v0.pz()); - auto properLifetime = (l / (p + kSmallMomentumDenominator)) * MassLambda; - histos.fill(HIST("QAafter/lambdaProperLifetime"), v0.pt(), properLifetime); - histos.fill(HIST("QAafter/lambdaArmenterosPodolanski"), v0.alpha(), v0.qtarm(), v0.pt()); } pKaon = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(kaon.pt(), kaon.eta(), kaon.phi(), MassKaonCharged)); - // K+ + Lambda -> Bkg channel for charged Xi(1820) - if (kaonCharge > 0 && isLambda) { - pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mLambda())); - pRes = pKaon + pLambda; - auto pCandRapidity = pRes.Rapidity(); - if (std::abs(pCandRapidity) >= additionalConfig.cfgRapidityCut) // skip candidate if reconstructed rapidity is outside of cut + // A failed rapidity or truth match skips only this mass hypothesis. + for (const auto& isLambda : std::array{true, false}) { + if (isLambda ? !selected.isLambda : !selected.isAntiLambda) { continue; - if constexpr (!IsMix) { - histos.fill(HIST("xi1820/kplus_lambda/hInvMassKplusLambda"), pRes.M()); - histos.fill(HIST("xi1820/kplus_lambda/hMassPtCentKplusLambda"), pRes.M(), pRes.Pt(), cent); - - } else { - histos.fill(HIST("xi1820/kplus_lambda/hInvMassKplusLambda_Mix"), pRes.M()); - histos.fill(HIST("xi1820/kplus_lambda/hMassPtCentKplusLambda_Mix"), pRes.M(), pRes.Pt(), cent); } - } - // K+ + Anti-Lambda -> Signal channel for Anti-charged Xi(1820) - if (kaonCharge > 0 && isAntiLambda) { - pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mAntiLambda())); - pRes = pKaon + pLambda; - auto pCandRapidity = pRes.Rapidity(); - if (std::abs(pCandRapidity) >= additionalConfig.cfgRapidityCut) // skip candidate if reconstructed rapidity is outside of cut - continue; - if constexpr (!IsMix) { - histos.fill(HIST("xi1820/kplus_antilambda/hInvMassKplusAntiLambda"), pRes.M()); - histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda"), pRes.M(), pRes.Pt(), cent); - if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel - if (std::abs(v0.motherPDG()) != kPdgChagedXi1820) - continue; - if (kaon.pdgCode() != PDG_t::kKPlus || v0.pdgCode() != PDG_t::kLambda0Bar) - continue; - if (kaon.motherId() != v0.motherId()) - continue; - auto pMCPt = v0.motherPt(); // Check particle's pT resolution - if (additionalConfig.cUseTruthRapidity && std::abs(v0.motherRap()) >= additionalConfig.cfgRapidityCut) // skip candidate if True rapidity of mother particle is outside of cut - continue; - histos.fill(HIST("MC/kplus_antilambda/hMCRecoInvMassKplusAntiLambda"), pRes.M()); - histos.fill(HIST("MC/kplus_antilambda/hMCRecoMassPtCentKplusAntiLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); - - // Detail QA histograms for truth particle -> Will be updated + // K+ + Lambda -> Bkg channel for charged Xi(1820) + if (kaonCharge > 0 && isLambda) { + pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mLambda())); + pRes = pKaon + pLambda; + auto pCandRapidity = pRes.Rapidity(); + if (!passesRapidity(pCandRapidity)) { // skip candidate if reconstructed rapidity is outside of cut + continue; } - } else { - histos.fill(HIST("xi1820/kplus_antilambda/hInvMassKplusAntiLambda_Mix"), pRes.M()); - histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent); - } - } - - // K- + Lambda -> Signal channel for Xi(1820)- - if (kaonCharge < 0 && isLambda) { - pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mLambda())); - pRes = pKaon + pLambda; - auto pCandRapidity = pRes.Rapidity(); - if (std::abs(pCandRapidity) >= additionalConfig.cfgRapidityCut) // skip candidate if reconstructed rapidity is outside of cut - continue; - if constexpr (!IsMix) { - histos.fill(HIST("xi1820/kminus_lambda/hInvMassKminusLambda"), pRes.M()); - histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentKminusLambda"), pRes.M(), pRes.Pt(), cent); - if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel - if (std::abs(v0.motherPDG()) != kPdgChagedXi1820) - continue; - if (kaon.pdgCode() != PDG_t::kKMinus || v0.pdgCode() != PDG_t::kLambda0) - continue; - if (kaon.motherId() != v0.motherId()) - continue; - auto pMCPt = v0.motherPt(); // Check particle's pT resolution - if (additionalConfig.cUseTruthRapidity && std::abs(v0.motherRap()) >= additionalConfig.cfgRapidityCut) // skip candidate if True rapidity of mother particle is outside of cut - continue; - histos.fill(HIST("MC/kminus_lambda/hMCRecoInvMassKminusLambda"), pRes.M()); - histos.fill(HIST("MC/kminus_lambda/hMCRecoMassPtCentKminusLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); - - // Detail QA histograms for the truth particle -> Will be updated + if constexpr (!IsMix) { + histos.fill(HIST("xi1820/kplus_lambda/hInvMassKplusLambda"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kplus_lambda/hMassPtCentKplusLambda"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kplus_lambda/hMassPtCentDelKplusLambda"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + } else { + histos.fill(HIST("xi1820/kplus_lambda/hInvMassKplusLambda_Mix"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kplus_lambda/hMassPtCentKplusLambda_Mix"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kplus_lambda/hMassPtCentDelKplusLambda_Mix"), pRes.M(), pRes.Pt(), cent, pCandDel); + } } - } else { - histos.fill(HIST("xi1820/kminus_lambda/hInvMassKminusLambda_Mix"), pRes.M()); - histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentKminusLambda_Mix"), pRes.M(), pRes.Pt(), cent); - } - } - - // K- + Anti-Lambda -> Bkg channel for charged Xi(1820) - if (kaonCharge < 0 && isAntiLambda) { - pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mAntiLambda())); - pRes = pKaon + pLambda; - auto pCandRapidity = pRes.Rapidity(); - if (std::abs(pCandRapidity) >= additionalConfig.cfgRapidityCut) // skip candidate if reconstructed rapidity is outside of cut - continue; - if constexpr (!IsMix) { - histos.fill(HIST("xi1820/kminus_antilambda/hInvMassKminusAntiLambda"), pRes.M()); - histos.fill(HIST("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda"), pRes.M(), pRes.Pt(), cent); - } else { - histos.fill(HIST("xi1820/kminus_antilambda/hInvMassKminusAntiLambda_Mix"), pRes.M()); - histos.fill(HIST("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent); - } - } - } + } // End of Bkg + + // K+ + Anti-Lambda -> Signal channel for Anti-charged Xi(1820) + if (kaonCharge > 0 && !isLambda) { + pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mAntiLambda())); + pRes = pKaon + pLambda; + auto pCandRapidity = pRes.Rapidity(); + if (!passesRapidity(pCandRapidity)) { // skip candidate if reconstructed rapidity is outside of cut + continue; + } + if constexpr (!IsMix) { + histos.fill(HIST("xi1820/kplus_antilambda/hInvMassKplusAntiLambda"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentDelKplusAntiLambda"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + if (additionalConfig.cfgFillRotBkg) { + for (int i = 0; i < additionalConfig.cfgNrotBkg; i++) { + const auto lRotAngle = additionalConfig.cfgNrotBkg == 1 + ? 0.5f * (additionalConfig.cfgMinRot + additionalConfig.cfgMaxRot) + : additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); + if (cfgFillEventQA) { + histos.fill(HIST("Event/hRotBkg"), lRotAngle - o2::constants::math::PI); + } + if (additionalConfig.cfgRotKaon) { + lDaughterRot = pKaon; + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = lDaughterRot + pLambda; + } else { + lDaughterRot = pLambda; + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = pKaon + lDaughterRot; + } + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent); + } else { + auto pRotDel = additionalConfig.cfgRotKaon ? deltaR(lDaughterRot, pLambda) : deltaR(lDaughterRot, pKaon); + histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentDelKplusAntiLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent, pRotDel); + } + } + } + + if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel + if (v0.motherPDG() != -Pdg::kXi1820Minus || kaon.motherPDG() != v0.motherPDG()) { + continue; + } + if (kaon.pdgCode() != PDG_t::kKPlus || v0.pdgCode() != PDG_t::kLambda0Bar) { + continue; + } + if (v0.motherId() < 0 || kaon.motherId() != v0.motherId()) { + continue; + } + auto pMCPt = v0.motherPt(); // Check particle's pT resolution + if (additionalConfig.cUseTruthRapidity && !passesRapidity(v0.motherRap())) { // skip candidate if True rapidity of mother particle is outside of cut + continue; + } + histos.fill(HIST("MC/kplus_antilambda/hMCRecoInvMassKplusAntiLambda"), pRes.M()); + histos.fill(HIST("MC/kplus_antilambda/hMCRecoMassPtCentKplusAntiLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); + + fillTruthKaonQA(kaon); + fillTruthLambdaQA(v0, false); + } + } else { + histos.fill(HIST("xi1820/kplus_antilambda/hInvMassKplusAntiLambda_Mix"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentKplusAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kplus_antilambda/hMassPtCentDelKplusAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + } + } // End of signal + + // K- + Lambda -> Signal channel for Xi(1820)- + if (kaonCharge < 0 && isLambda) { + pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mLambda())); + pRes = pKaon + pLambda; + auto pCandRapidity = pRes.Rapidity(); + if (!passesRapidity(pCandRapidity)) { // skip candidate if reconstructed rapidity is outside of cut + continue; + } + if constexpr (!IsMix) { + histos.fill(HIST("xi1820/kminus_lambda/hInvMassKminusLambda"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentKminusLambda"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentDelKminusLambda"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + + if (additionalConfig.cfgFillRotBkg) { + for (int i = 0; i < additionalConfig.cfgNrotBkg; i++) { + const auto lRotAngle = additionalConfig.cfgNrotBkg == 1 + ? 0.5f * (additionalConfig.cfgMinRot + additionalConfig.cfgMaxRot) + : additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); + if (cfgFillEventQA) { + histos.fill(HIST("Event/hRotBkg"), lRotAngle - o2::constants::math::PI); + } + if (additionalConfig.cfgRotKaon) { + lDaughterRot = pKaon; + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = lDaughterRot + pLambda; + } else { + lDaughterRot = pLambda; + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = pKaon + lDaughterRot; + } + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentKminusLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent); + } else { + auto pCandDel = additionalConfig.cfgRotKaon ? deltaR(lDaughterRot, pLambda) : deltaR(lDaughterRot, pKaon); + histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentDelKminusLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent, pCandDel); + } + } + } + if constexpr (IsMC) { // Calculate Acceptance x efficiency for "the particle" channel + if (v0.motherPDG() != Pdg::kXi1820Minus || kaon.motherPDG() != v0.motherPDG()) { + continue; + } + if (kaon.pdgCode() != PDG_t::kKMinus || v0.pdgCode() != PDG_t::kLambda0) { + continue; + } + if (v0.motherId() < 0 || kaon.motherId() != v0.motherId()) { + continue; + } + auto pMCPt = v0.motherPt(); // Check particle's pT resolution + if (additionalConfig.cUseTruthRapidity && !passesRapidity(v0.motherRap())) { // skip candidate if True rapidity of mother particle is outside of cut + continue; + } + histos.fill(HIST("MC/kminus_lambda/hMCRecoInvMassKminusLambda"), pRes.M()); + histos.fill(HIST("MC/kminus_lambda/hMCRecoMassPtCentKminusLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); + + fillTruthKaonQA(kaon); + fillTruthLambdaQA(v0, true); + } + } else { + histos.fill(HIST("xi1820/kminus_lambda/hInvMassKminusLambda_Mix"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentKminusLambda_Mix"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kminus_lambda/hMassPtCentDelKminusLambda_Mix"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + } + } // End of Signal + + // K- + Anti-Lambda -> Bkg channel for charged Xi(1820) + if (kaonCharge < 0 && !isLambda) { + pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(v0.pt(), v0.eta(), v0.phi(), v0.mAntiLambda())); + pRes = pKaon + pLambda; + auto pCandRapidity = pRes.Rapidity(); + if (!passesRapidity(pCandRapidity)) { // skip candidate if reconstructed rapidity is outside of cut + continue; + } + if constexpr (!IsMix) { + histos.fill(HIST("xi1820/kminus_antilambda/hInvMassKminusAntiLambda"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kminus_antilambda/hMassPtCentDelKminusAntiLambda"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + } else { + histos.fill(HIST("xi1820/kminus_antilambda/hInvMassKminusAntiLambda_Mix"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/kminus_antilambda/hMassPtCentKminusAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pKaon); + histos.fill(HIST("xi1820/kminus_antilambda/hMassPtCentDelKminusAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + } + } // End of Bkg + } // End of mass-hypothesis loop + } // End of V0 loop } // Fill event QA for after-cuts counters (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/nLambdasAfterCuts"), nV0sAfterCuts); - histos.fill(HIST("Event/nKaonsAfterCuts"), nKaonsAfterCuts); + if (!IsMix && cfgFillEventQA) { + histos.fill(HIST("Event/nLambdasAfterCuts"), selectedLambdas.size()); + histos.fill(HIST("Event/nKaonsAfterCuts"), selectedKaons.size()); } } - template - void fillK0sLambda(const CollisionT& collision, const V0sT& k0sCands, const V0sT& lambdaCands) // Xi(1820) analysis: K0s + Lambda channel, No need to MicroTrack! + template + void fillK0sLambda(const CollisionT& collision, const V0sT& k0sCands, const V0sT& lambdaCands, const LambdaCollisionT& lambdaCollision) // Xi(1820) analysis: K0s + Lambda channel, No need to MicroTrack! { auto cent = collision.cent(); + // The charged same-event callback owns common event/Lambda QA when enabled. + const bool fillSharedQA = !hasChargedSameEventProcess(); // Fill event QA histograms - if constexpr (!IsMix) { - histos.fill(HIST("Event/posZ"), collision.posZ()); - histos.fill(HIST("Event/centrality"), cent); - histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); - histos.fill(HIST("Event/nV0s"), lambdaCands.size()); - histos.fill(HIST("Event/nKaons"), k0sCands.size()); + if (!IsMix && cfgFillEventQA) { + if (fillSharedQA) { + histos.fill(HIST("Event/posZ"), collision.posZ()); + histos.fill(HIST("Event/centrality"), cent); + histos.fill(HIST("Event/posZvsCent"), collision.posZ(), cent); + histos.fill(HIST("Event/nV0s"), lambdaCands.size()); + } + histos.fill(HIST("Event/nK0s"), k0sCands.size()); } - if (additionalConfig.cConsiderHasV0s && (lambdaCands.size() < 1)) - return; // skip events that do not have V0s if the option is enabled - - int nV0sAfterCuts = 0; - int nKaonsAfterCuts = 0; + std::vector selectedK0s; + selectedK0s.reserve(k0sCands.size()); // Loop over V0s for K0s for (const auto& k0s : k0sCands) { // K0s QA before cuts - if constexpr (!IsMix) { + if (!IsMix && cfgFillQA) { + histos.fill(HIST("QAbefore/k0sDCAtoPV"), k0s.pt(), k0s.dcav0topv()); histos.fill(HIST("QAbefore/k0sMass"), k0s.mK0Short()); histos.fill(HIST("QAbefore/k0sPt"), k0s.pt()); histos.fill(HIST("QAbefore/k0sEta"), k0s.eta()); histos.fill(HIST("QAbefore/k0sCosPA"), k0s.pt(), k0s.v0CosPA()); histos.fill(HIST("QAbefore/k0sRadius"), k0s.pt(), k0s.transRadius()); histos.fill(HIST("QAbefore/k0sDauDCA"), k0s.pt(), k0s.daughDCA()); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaPosPi"), k0s.pt(), k0s.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAbefore/k0sDauTPCNsigmaNegPi"), k0s.pt(), k0s.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsPos"), k0s.pt(), k0s.nCrossedRowsPos()); + histos.fill(HIST("QAbefore/k0sNCrossedRowsNeg"), k0s.pt(), k0s.nCrossedRowsNeg()); float dx = k0s.decayVtxX() - collision.posX(); float dy = k0s.decayVtxY() - collision.posY(); @@ -910,19 +1305,23 @@ struct Xi1820Analysis { histos.fill(HIST("QAbefore/k0sArmenterosPodolanski"), k0s.alpha(), k0s.qtarm(), k0s.pt()); } - if (!k0sCut(collision, k0s)) + if (!k0sCut(collision, k0s)) { continue; - auto indexK0s = k0s.index(); + } - if constexpr (!IsMix) { - nKaonsAfterCuts++; + if (!IsMix && cfgFillQA) { // K0s QA after cuts + histos.fill(HIST("QAafter/k0sDCAtoPV"), k0s.pt(), k0s.dcav0topv()); histos.fill(HIST("QAafter/k0sMass"), k0s.mK0Short()); histos.fill(HIST("QAafter/k0sPt"), k0s.pt()); histos.fill(HIST("QAafter/k0sEta"), k0s.eta()); histos.fill(HIST("QAafter/k0sCosPA"), k0s.pt(), k0s.v0CosPA()); histos.fill(HIST("QAafter/k0sRadius"), k0s.pt(), k0s.transRadius()); histos.fill(HIST("QAafter/k0sDauDCA"), k0s.pt(), k0s.daughDCA()); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaPosPi"), k0s.pt(), k0s.daughterTPCNSigmaPosPi()); + histos.fill(HIST("QAafter/k0sDauTPCNsigmaNegPi"), k0s.pt(), k0s.daughterTPCNSigmaNegPi()); + histos.fill(HIST("QAafter/k0sNCrossedRowsPos"), k0s.pt(), k0s.nCrossedRowsPos()); + histos.fill(HIST("QAafter/k0sNCrossedRowsNeg"), k0s.pt(), k0s.nCrossedRowsNeg()); float dx = k0s.decayVtxX() - collision.posX(); float dy = k0s.decayVtxY() - collision.posY(); @@ -934,139 +1333,179 @@ struct Xi1820Analysis { histos.fill(HIST("QAafter/k0sArmenterosPodolanski"), k0s.alpha(), k0s.qtarm(), k0s.pt()); } - // Loop over V0s for Lambda - for (const auto& lambda : lambdaCands) { - - auto indexLambda = lambda.index(); + selectedK0s.push_back(k0s); + } + const auto selectedLambdas = selectLambdas(lambdaCollision, lambdaCands, fillSharedQA); + for (const auto& k0s : selectedK0s) { + for (const auto& selected : selectedLambdas) { + const auto& lambda = selected.candidate; if constexpr (!IsMix) { - if (indexLambda == indexK0s) // Avoid self-combination + if (k0s.globalIndex() == lambda.globalIndex() || + sharesAnyDaughterId(v0DaughterIds(k0s), v0DaughterIds(lambda))) { continue; - histos.fill(HIST("QAbefore/lambdaMass"), lambda.mLambda()); - histos.fill(HIST("QAbefore/lambdaMassAnti"), lambda.mAntiLambda()); - histos.fill(HIST("QAbefore/lambdaPt"), lambda.pt()); - histos.fill(HIST("QAbefore/lambdaEta"), lambda.eta()); - histos.fill(HIST("QAbefore/lambdaCosPA"), lambda.pt(), lambda.v0CosPA()); - histos.fill(HIST("QAbefore/lambdaRadius"), lambda.pt(), lambda.transRadius()); - histos.fill(HIST("QAbefore/lambdaDauDCA"), lambda.pt(), lambda.daughDCA()); - histos.fill(HIST("QAbefore/lambdaDauPosDCA"), lambda.pt(), std::abs(lambda.dcapostopv())); - histos.fill(HIST("QAbefore/lambdaDauNegDCA"), lambda.pt(), std::abs(lambda.dcanegtopv())); - - // Calculate proper lifetime manually - float dx = lambda.decayVtxX() - collision.posX(); - float dy = lambda.decayVtxY() - collision.posY(); - float dz = lambda.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(lambda.px() * lambda.px() + lambda.py() * lambda.py() + lambda.pz() * lambda.pz()); - auto properLifetime = (l / (p + kSmallMomentumDenominator)) * MassLambda; - histos.fill(HIST("QAbefore/lambdaProperLifetime"), lambda.pt(), properLifetime); - histos.fill(HIST("QAbefore/lambdaArmenterosPodolanski"), lambda.alpha(), lambda.qtarm(), lambda.pt()); - } - - // Try Lambda - bool isLambda = v0Cut(collision, lambda, true); - // Try Anti-Lambda - bool isAntiLambda = v0Cut(collision, lambda, false); - - if (!isLambda && !isAntiLambda) - continue; - - if constexpr (!IsMix) { - nV0sAfterCuts++; - // QA after cuts (fill for whichever passes) - if (isLambda) { - histos.fill(HIST("QAafter/lambdaMass"), lambda.mLambda()); - } - if (isAntiLambda) { - histos.fill(HIST("QAafter/lambdaMassAnti"), lambda.mAntiLambda()); } - histos.fill(HIST("QAafter/lambdaPt"), lambda.pt()); - histos.fill(HIST("QAafter/lambdaEta"), lambda.eta()); - histos.fill(HIST("QAafter/lambdaCosPA"), lambda.pt(), lambda.v0CosPA()); - histos.fill(HIST("QAafter/lambdaRadius"), lambda.pt(), lambda.transRadius()); - histos.fill(HIST("QAafter/lambdaDauDCA"), lambda.pt(), lambda.daughDCA()); - histos.fill(HIST("QAafter/lambdaDauPosDCA"), lambda.pt(), std::abs(lambda.dcapostopv())); - histos.fill(HIST("QAafter/lambdaDauNegDCA"), lambda.pt(), std::abs(lambda.dcanegtopv())); - - float dx = lambda.decayVtxX() - collision.posX(); - float dy = lambda.decayVtxY() - collision.posY(); - float dz = lambda.decayVtxZ() - collision.posZ(); - float l = std::sqrt(dx * dx + dy * dy + dz * dz); - float p = std::sqrt(lambda.px() * lambda.px() + lambda.py() * lambda.py() + lambda.pz() * lambda.pz()); - auto properLifetime = (l / (p + kSmallMomentumDenominator)) * MassLambda; - histos.fill(HIST("QAafter/lambdaProperLifetime"), lambda.pt(), properLifetime); - histos.fill(HIST("QAafter/lambdaArmenterosPodolanski"), lambda.alpha(), lambda.qtarm(), lambda.pt()); } // 4-vectors - ROOT::Math::PxPyPzEVector pK0s, pLambda, pRes; + ROOT::Math::PxPyPzEVector pK0s, pLambda, pRes, lDaughterRot, lResonanceRot; pK0s = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(k0s.pt(), k0s.eta(), k0s.phi(), MassK0Short)); - if (isLambda) { - pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(lambda.pt(), lambda.eta(), lambda.phi(), lambda.mLambda())); - pRes = pK0s + pLambda; - auto pCandRapidity = pRes.Rapidity(); - if (std::abs(pCandRapidity) >= additionalConfig.cfgRapidityCut) // skip candidate if reconstructed rapidity is outside of cut + // Lambda and anti-Lambda may both pass; evaluate them independently. + for (const auto& isLambda : std::array{true, false}) { + if (isLambda ? !selected.isLambda : !selected.isAntiLambda) { continue; - if constexpr (!IsMix) { - histos.fill(HIST("xi1820/k0s_lambda/hInvMassK0sLambda"), pRes.M()); - histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentK0sLambda"), pRes.M(), pRes.Pt(), cent); - if constexpr (IsMC) { // Calculate Acceptance x efficiency - if (std::abs(lambda.motherPDG()) != kPdgXi1820Zero) - continue; - if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0) - continue; - if (k0s.motherId() != lambda.motherId()) - continue; - auto pMCPt = lambda.motherPt(); // Check particle's pT resolution - if (additionalConfig.cUseTruthRapidity && std::abs(lambda.motherRap()) >= additionalConfig.cfgRapidityCut) // skip candidate if True rapidity of mother particle is outside of cut - continue; - histos.fill(HIST("MC/k0s_lambda/hMCRecoInvMassK0sLambda"), pRes.M()); - histos.fill(HIST("MC/k0s_lambda/hMCRecoMassPtCentK0sLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); - // Detail QA histograms for truth particle -> Will be updated - } - } else { - histos.fill(HIST("xi1820/k0s_lambda/hInvMassK0sLambda_Mix"), pRes.M()); - histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentK0sLambda_Mix"), pRes.M(), pRes.Pt(), cent); } - } - if (isAntiLambda) { - pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(lambda.pt(), lambda.eta(), lambda.phi(), lambda.mAntiLambda())); - pRes = pK0s + pLambda; - auto pCandRapidity = pRes.Rapidity(); - if (std::abs(pCandRapidity) >= additionalConfig.cfgRapidityCut) // skip candidate if reconstructed rapidity is outside of cut - continue; - if constexpr (!IsMix) { - histos.fill(HIST("xi1820/k0s_antilambda/hInvMassK0sAntiLambda"), pRes.M()); - histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda"), pRes.M(), pRes.Pt(), cent); - - if constexpr (IsMC) { // Calculate Acceptance x efficiency - if (std::abs(lambda.motherPDG()) != kPdgXi1820Zero) - continue; - if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0Bar) - continue; - if (k0s.motherId() != lambda.motherId()) - continue; - auto pMCPt = lambda.motherPt(); // Check particle's pT resolution - if (additionalConfig.cUseTruthRapidity && std::abs(lambda.motherRap()) >= additionalConfig.cfgRapidityCut) // skip candidate if True rapidity of mother particle is outside of cut - continue; - histos.fill(HIST("MC/k0s_antilambda/hMCRecoInvMassK0sAntiLambda"), pRes.M()); - histos.fill(HIST("MC/k0s_antilambda/hMCRecoMassPtCentK0sAntiLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); - // Detail QA histograms for truth particle -> Will be updated + if (isLambda) { + pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(lambda.pt(), lambda.eta(), lambda.phi(), lambda.mLambda())); + pRes = pK0s + pLambda; + auto pCandRapidity = pRes.Rapidity(); + if (!passesRapidity(pCandRapidity)) { // skip candidate if reconstructed rapidity is outside of cut + continue; + } + if constexpr (!IsMix) { + histos.fill(HIST("xi1820/k0s_lambda/hInvMassK0sLambda"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentK0sLambda"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pK0s); + histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentDelK0sLambda"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + + if (additionalConfig.cfgFillRotBkg) { + for (int i = 0; i < additionalConfig.cfgNrotBkg; i++) { + const auto lRotAngle = additionalConfig.cfgNrotBkg == 1 + ? 0.5f * (additionalConfig.cfgMinRot + additionalConfig.cfgMaxRot) + : additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); + if (cfgFillEventQA) { + histos.fill(HIST("Event/hRotBkg"), lRotAngle - o2::constants::math::PI); + } + lDaughterRot = pK0s; + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = lDaughterRot + pLambda; + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentK0sLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent); + } else { + auto lRotDel = deltaR(lDaughterRot, pLambda); + histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentDelK0sLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent, lRotDel); + } + } + } + + if constexpr (IsMC) { // Calculate Acceptance x efficiency + if (lambda.motherPDG() != Pdg::kXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { + continue; + } + if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0) { + continue; + } + if (lambda.motherId() < 0 || k0s.motherId() != lambda.motherId()) { + continue; + } + auto pMCPt = lambda.motherPt(); // Check particle's pT resolution + if (additionalConfig.cUseTruthRapidity && !passesRapidity(lambda.motherRap())) { // skip candidate if True rapidity of mother particle is outside of cut + continue; + } + histos.fill(HIST("MC/k0s_lambda/hMCRecoInvMassK0sLambda"), pRes.M()); + histos.fill(HIST("MC/k0s_lambda/hMCRecoMassPtCentK0sLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); + fillTruthLambdaQA(lambda, true); + if (cfgFillTruthQA) { + histos.fill(HIST("QAMCTrue/k0sPt"), k0s.pt()); + } + } + } else { + histos.fill(HIST("xi1820/k0s_lambda/hInvMassK0sLambda_Mix"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentK0sLambda_Mix"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pK0s); + histos.fill(HIST("xi1820/k0s_lambda/hMassPtCentDelK0sLambda_Mix"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + } + } // End of Lambda + + if (!isLambda) { + pLambda = ROOT::Math::PxPyPzEVector(ROOT::Math::PtEtaPhiMVector(lambda.pt(), lambda.eta(), lambda.phi(), lambda.mAntiLambda())); + pRes = pK0s + pLambda; + auto pCandRapidity = pRes.Rapidity(); + if (!passesRapidity(pCandRapidity)) { // skip candidate if reconstructed rapidity is outside of cut + continue; + } + if constexpr (!IsMix) { + histos.fill(HIST("xi1820/k0s_antilambda/hInvMassK0sAntiLambda"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pK0s); + histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentDelK0sAntiLambda"), pRes.M(), pRes.Pt(), cent, pCandDel); + } + + if (additionalConfig.cfgFillRotBkg) { + for (int i = 0; i < additionalConfig.cfgNrotBkg; i++) { + const auto lRotAngle = additionalConfig.cfgNrotBkg == 1 + ? 0.5f * (additionalConfig.cfgMinRot + additionalConfig.cfgMaxRot) + : additionalConfig.cfgMinRot + i * ((additionalConfig.cfgMaxRot - additionalConfig.cfgMinRot) / (additionalConfig.cfgNrotBkg - 1)); + if (cfgFillEventQA) { + histos.fill(HIST("Event/hRotBkg"), lRotAngle - o2::constants::math::PI); + } + lDaughterRot = pK0s; + ROOT::Math::RotationZ rot(lRotAngle); + auto p3 = rot * lDaughterRot.Vect(); + lDaughterRot = LorentzVectorSetXYZM(p3.X(), p3.Y(), p3.Z(), lDaughterRot.M()); + lResonanceRot = lDaughterRot + pLambda; + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent); + } else { + auto lRotDel = deltaR(lDaughterRot, pLambda); + histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentDelK0sAntiLambda_Rot"), lResonanceRot.M(), lResonanceRot.Pt(), cent, lRotDel); + } + } + } + + if constexpr (IsMC) { // Calculate Acceptance x efficiency + if (lambda.motherPDG() != -Pdg::kXi1820Zero || k0s.motherPDG() != lambda.motherPDG()) { + continue; + } + if (std::abs(k0s.pdgCode()) != PDG_t::kK0Short || lambda.pdgCode() != PDG_t::kLambda0Bar) { + continue; + } + if (lambda.motherId() < 0 || k0s.motherId() != lambda.motherId()) { + continue; + } + auto pMCPt = lambda.motherPt(); // Check particle's pT resolution + if (additionalConfig.cUseTruthRapidity && !passesRapidity(lambda.motherRap())) { // skip candidate if True rapidity of mother particle is outside of cut + continue; + } + histos.fill(HIST("MC/k0s_antilambda/hMCRecoInvMassK0sAntiLambda"), pRes.M()); + histos.fill(HIST("MC/k0s_antilambda/hMCRecoMassPtCentK0sAntiLambda"), pRes.M(), pRes.Pt(), cent, pMCPt); + fillTruthLambdaQA(lambda, false); + if (cfgFillTruthQA) { + histos.fill(HIST("QAMCTrue/k0sPt"), k0s.pt()); + } + } + } else { + histos.fill(HIST("xi1820/k0s_antilambda/hInvMassK0sAntiLambda_Mix"), pRes.M()); + if (!(additionalConfig.cfgDelCheck)) { + histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent); + } else { + auto pCandDel = deltaR(pLambda, pK0s); + histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentDelK0sAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent, pCandDel); + } } - } else { - histos.fill(HIST("xi1820/k0s_antilambda/hInvMassK0sAntiLambda_Mix"), pRes.M()); - histos.fill(HIST("xi1820/k0s_antilambda/hMassPtCentK0sAntiLambda_Mix"), pRes.M(), pRes.Pt(), cent); } - } + } // End of mass-hypothesis loop } // End of loop over Lambda candidates } // End of loop over K0s candidates // Fill event QA for after-cuts counters (only for same-event) - if constexpr (!IsMix) { - histos.fill(HIST("Event/nLambdasAfterCuts"), nV0sAfterCuts); - histos.fill(HIST("Event/nKaonsAfterCuts"), nKaonsAfterCuts); + if (!IsMix && cfgFillEventQA) { + if (fillSharedQA) { + histos.fill(HIST("Event/nLambdasAfterCuts"), selectedLambdas.size()); + } + histos.fill(HIST("Event/nK0sAfterCuts"), selectedK0s.size()); } } @@ -1076,142 +1515,270 @@ struct Xi1820Analysis { } PROCESS_SWITCH(Xi1820Analysis, processDummy, "Process Dummy", true); - void processDataWithTracks(const aod::ResoCollision& resoCollision, + void processDataWithTracks(ResoCollisions::iterator const& resoCollision, aod::ResoV0s const& resoV0s, - aod::ResoTracks const& resoTracks) + ResoTracks const& resoTracks) { - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) + if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) { return; // skip event if RecoINEL>0 selection is enabled and event does not pass it - fillChargedKLambda(resoCollision, resoV0s, resoTracks); + } + fillChargedKLambda(resoCollision, resoV0s, resoTracks, resoCollision); } PROCESS_SWITCH(Xi1820Analysis, processDataWithTracks, "Process Event with ResoTracks", false); - void processDataWithMicroTracks(const aod::ResoCollision& resoCollision, + void processDataWithMicroTracks(ResoCollisions::iterator const& resoCollision, aod::ResoV0s const& resoV0s, - aod::ResoMicroTracks const& resoMicroTracks) + ResoMicroTracks const& resoMicroTracks) { - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) + if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) { return; // skip event if RecoINEL>0 selection is enabled and event does not pass it - fillChargedKLambda(resoCollision, resoV0s, resoMicroTracks); + } + fillChargedKLambda(resoCollision, resoV0s, resoMicroTracks, resoCollision); } PROCESS_SWITCH(Xi1820Analysis, processDataWithMicroTracks, "Process Event with ResoMicroTracks", false); - void processMixedEventWithTracks(const aod::ResoCollisions& resoCollisions, - aod::ResoV0s const& resoV0s, - aod::ResoTracks const& resoTracks) + template + bool acceptsMixedCollisions(const FirstCollision& first, const SecondCollision& second) const { + if (additionalConfig.cRecoINELgt0.value && (!first.isRecINELgt0() || !second.isRecINELgt0())) { + return false; + } + return std::isfinite(first.bMagField()) && first.bMagField() == second.bMagField(); + } - auto v0sTracksTuple = std::make_tuple(resoTracks, resoV0s); - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; - Pair pairs{colBinning, nEvtMixing, -1, resoCollisions, v0sTracksTuple, &cache}; - + void processMixedEventWithTracks(ResoCollisions const& resoCollisions, + aod::ResoV0s const& resoV0s, + ResoTracks const& resoTracks) + { + if (nEvtMixing <= 0) { + return; + } + auto tracksTuple = std::make_tuple(resoTracks, resoV0s); + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; + Pair pairs{binning, nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; for (auto& [collision1, tracks1, collision2, v0s2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) - if (additionalConfig.cRecoINELgt0 && !collision1.isRecINELgt0()) - continue; // skip event if RecoINEL>0 selection is enabled and event does not pass it - fillChargedKLambda(collision1, v0s2, tracks1); + if (!acceptsMixedCollisions(collision1, collision2)) { + continue; + } + fillChargedKLambda(collision1, v0s2, tracks1, collision2); } } PROCESS_SWITCH(Xi1820Analysis, processMixedEventWithTracks, "Process Mixed Event with ResoTracks", false); - void processMixedEventWithMicroTracks(const aod::ResoCollisions& resoCollisions, + void processMixedEventWithMicroTracks(ResoCollisions const& resoCollisions, aod::ResoV0s const& resoV0s, - aod::ResoMicroTracks const& resoMicroTracks) + ResoMicroTracks const& resoMicroTracks) { + if (nEvtMixing <= 0) { + return; + } + auto tracksTuple = std::make_tuple(resoMicroTracks, resoV0s); + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; + Pair pairs{binning, nEvtMixing, -1, resoCollisions, tracksTuple, &cache}; + for (auto& [collision1, tracks1, collision2, v0s2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) + if (!acceptsMixedCollisions(collision1, collision2)) { + continue; + } + fillChargedKLambda(collision1, v0s2, tracks1, collision2); + } + } + PROCESS_SWITCH(Xi1820Analysis, processMixedEventWithMicroTracks, "Process Mixed Event with ResoMicroTracks 001", false); - auto v0sTracksTuple = std::make_tuple(resoV0s, resoMicroTracks); - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; - Pair pairs{colBinning, nEvtMixing, -1, resoCollisions, v0sTracksTuple, &cache}; + template + static std::shared_ptr copyMixingRows(DaughterTable const& daughters) + { + // Copy only this collision's rows, without retaining the source DF buffers. + const auto source = daughters.asArrowTableConstrained(); + if (source->num_rows() == 0) { + return arrow::Table::MakeEmpty(source->schema()).ValueOrDie(); + } + std::vector> columns; + columns.reserve(source->num_columns()); + for (const auto& column : source->columns()) { + columns.push_back(std::make_shared(arrow::Concatenate(column->chunks()).ValueOrDie())); + } + return arrow::Table::Make(source->schema(), columns); + } - for (auto& [collision1, v0s1, collision2, tracks2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) - if (additionalConfig.cRecoINELgt0 && !collision2.isRecINELgt0()) - continue; // skip event if RecoINEL>0 selection is enabled and event does not pass it - fillChargedKLambda(collision2, v0s1, tracks2); + // FIFO history is task-local and retains only the copied MicroTrack rows. + // Use a single run/condition set; a field change clears all stored event pools. + void processMEDF(ResoCollisions::iterator const& collision, + ResoMicroTracks const& tracks, + aod::ResoV0s const& v0s) + { + if (nEvtMixing <= 0 || !std::isfinite(collision.bMagField())) { + return; + } + if (mixingBField != collision.bMagField()) { + mixingPools.clear(); + mixingBField = collision.bMagField(); + } + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; + const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); + if (bin < 0) { + return; + } + const MixingCollision current{.x = collision.posX(), .y = collision.posY(), .z = collision.posZ(), .centrality = collision.cent(), .recINELgt0 = collision.isRecINELgt0()}; + auto& pool = mixingPools[bin]; + for (const auto& previous : pool) { + if (additionalConfig.cRecoINELgt0 && (!previous.collision.recINELgt0 || !current.recINELgt0)) { + continue; + } + fillChargedKLambda(previous.collision, v0s, ResoMicroTracks{previous.daughters}, current); + } + // Insert after pairing; DF-local IDs may repeat across frames. + pool.push_back({current, copyMixingRows(tracks)}); + while (pool.size() > static_cast(nEvtMixing.value)) { + pool.pop_front(); } } - PROCESS_SWITCH(Xi1820Analysis, processMixedEventWithMicroTracks, "Process Mixed Event with ResoMicroTracks", false); + PROCESS_SWITCH(Xi1820Analysis, processMEDF, "Process cross-DF mixing with MicroTracks 001", false); // K0s + Lambda analysis - void processK0sLambda(const aod::ResoCollision& resoCollision, + void processK0sLambda(ResoCollisions::iterator const& resoCollision, aod::ResoV0s const& resoV0s) { - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) + if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) { return; // skip event if RecoINEL>0 selection is enabled and event does not pass it - fillK0sLambda(resoCollision, resoV0s, resoV0s); + } + fillK0sLambda(resoCollision, resoV0s, resoV0s, resoCollision); } PROCESS_SWITCH(Xi1820Analysis, processK0sLambda, "Process K0s + Lambda", false); // K0s + Lambda mixed event analysis - void processK0sLambdaMixedEvent(const aod::ResoCollisions& resoCollisions, + void processK0sLambdaMixedEvent(ResoCollisions const& resoCollisions, aod::ResoV0s const& resoV0s) { + if (nEvtMixing <= 0) { + return; + } auto v0sV0sTuple = std::make_tuple(resoV0s, resoV0s); - BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}, true}; - Pair pairs{colBinning, nEvtMixing, -1, resoCollisions, v0sV0sTuple, &cache}; + BinningTypeVertexContributor colBinning{{cfgVtxBins, cfgMultBins}}; + Pair pairs{colBinning, nEvtMixing, -1, resoCollisions, v0sV0sTuple, &cache}; for (auto& [collision1, k0s1, collision2, lambda2] : pairs) { // o2-linter: disable=const-ref-in-for-loop (structured bindings from Pair iterator cannot be const) - if (additionalConfig.cRecoINELgt0 && !collision1.isRecINELgt0()) - continue; // skip event if RecoINEL>0 selection is enabled and event does not pass it - fillK0sLambda(collision1, k0s1, lambda2); + if (!acceptsMixedCollisions(collision1, collision2)) { + continue; + } + fillK0sLambda(collision1, k0s1, lambda2, collision2); } } PROCESS_SWITCH(Xi1820Analysis, processK0sLambdaMixedEvent, "Process K0s + Lambda Mixed Event", false); + // Cross-DF neutral mixing: old-event K0s with current-event Lambda/anti-Lambda. + // Keep a separate bounded V0 pool; use inputs from one run/condition set. + void processK0sLambdaMEDF(ResoCollisions::iterator const& collision, + aod::ResoV0s const& v0s) + { + if (nEvtMixing <= 0 || !std::isfinite(collision.bMagField())) { + return; + } + if (neutralMixingBField != collision.bMagField()) { + neutralMixingPools.clear(); + neutralMixingBField = collision.bMagField(); + } + BinningTypeVertexContributor binning{{cfgVtxBins, cfgMultBins}}; + const int bin = binning.getBin(std::make_tuple(collision.posZ(), collision.cent())); + if (bin < 0) { + return; + } + const MixingCollision current{.x = collision.posX(), .y = collision.posY(), .z = collision.posZ(), .centrality = collision.cent(), .recINELgt0 = collision.isRecINELgt0()}; + auto& pool = neutralMixingPools[bin]; + const bool fillCurrentQA = cfgFillEventQA && (!additionalConfig.cRecoINELgt0 || current.recINELgt0); + if (fillCurrentQA) { + histos.fill(HIST("MixingQA/K0sLambdaMEDF/hPosZvsCent"), current.posZ(), current.cent()); + histos.fill(HIST("MixingQA/K0sLambdaMEDF/hPoolSize"), pool.size()); + } + int nPartners = 0; + for (const auto& previous : pool) { + if (additionalConfig.cRecoINELgt0 && (!previous.collision.recINELgt0 || !current.recINELgt0)) { + continue; + } + if (cfgFillEventQA) { + ++nPartners; + histos.fill(HIST("MixingQA/K0sLambdaMEDF/hPosZPair"), previous.collision.posZ(), current.posZ()); + histos.fill(HIST("MixingQA/K0sLambdaMEDF/hCentPair"), previous.collision.cent(), current.cent()); + histos.fill(HIST("MixingQA/K0sLambdaMEDF/hV0CountsPair"), previous.daughters->num_rows(), v0s.size()); + } + // Each V0 uses its own event's PV; original row IDs are not compared across events. + fillK0sLambda(previous.collision, aod::ResoV0s{previous.daughters}, v0s, current); + } + if (fillCurrentQA) { + histos.fill(HIST("MixingQA/K0sLambdaMEDF/hNPartners"), nPartners); + } + // Insert after pairing to avoid self-mixing, even when DF-local IDs repeat. + pool.push_back({current, copyMixingRows(v0s)}); + while (pool.size() > static_cast(nEvtMixing.value)) { + pool.pop_front(); + } + } + PROCESS_SWITCH(Xi1820Analysis, processK0sLambdaMEDF, "Process cross-DF K0s + Lambda mixing", false); + // MC processes for charged K + Lambda analysis void processMCWithTracks(ResoMCCols::iterator const& resoMCcollision, soa::Join const& resoMCV0s, - soa::Join const& resoMCTracks) + soa::Join const& resoMCTracks) { - if (additionalConfig.cRecoINELgt0 && !resoMCcollision.isRecINELgt0()) - return; // skip event if RecoINEL>0 selection is enabled and event does not pass it - if (!resoMCcollision.isInAfterAllCuts()) // MC event selection + if ((additionalConfig.cRecoINELgt0 && !resoMCcollision.isRecINELgt0()) || + (additionalConfig.cMCINELgt0 && !resoMCcollision.isINELgt0()) || + (additionalConfig.cMCVtxIn10 && !resoMCcollision.isVtxIn10())) { return; - fillChargedKLambda(resoMCcollision, resoMCV0s, resoMCTracks); + } + fillChargedKLambda(resoMCcollision, resoMCV0s, resoMCTracks, resoMCcollision); } PROCESS_SWITCH(Xi1820Analysis, processMCWithTracks, "Process MC for charged K + Lambda", false); void processMCK0sLambda(ResoMCCols::iterator const& resoMCCollision, soa::Join const& resoMCV0s) { - if (additionalConfig.cRecoINELgt0 && !resoMCCollision.isRecINELgt0()) - return; // skip event if RecoINEL>0 selection is enabled and event does not pass it - if (!resoMCCollision.isInAfterAllCuts()) // MC event selection + if ((additionalConfig.cRecoINELgt0 && !resoMCCollision.isRecINELgt0()) || + (additionalConfig.cMCINELgt0 && !resoMCCollision.isINELgt0()) || + (additionalConfig.cMCVtxIn10 && !resoMCCollision.isVtxIn10())) { return; - fillK0sLambda(resoMCCollision, resoMCV0s, resoMCV0s); + } + fillK0sLambda(resoMCCollision, resoMCV0s, resoMCV0s, resoMCCollision); } PROCESS_SWITCH(Xi1820Analysis, processMCK0sLambda, "Process MC K0s + Lambda", false); - void processMCWithMicroTracks(const aod::ResoCollision& /*resoCollision*/, - aod::ResoV0s const& /*resoV0s*/, - aod::ResoMicroTracks const& /*resoMicroTracks*/, - aod::McParticles const& /*mcParticles*/) + void processMCWithMicroTracks(ResoMCCols::iterator const& resoCollision, + soa::Join const& resoV0s, + soa::Join const& resoMicroTracks) { - // TODO: Implement MC truth matching for K± + Lambda with MicroTracks - // But is this really necessary? -> Most of the injected MC sizes are already within small-train limit. + if ((additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) || + (additionalConfig.cMCINELgt0 && !resoCollision.isINELgt0()) || + (additionalConfig.cMCVtxIn10 && !resoCollision.isVtxIn10())) { + return; + } + // Module 001 contains only selected reconstructed collisions. + fillChargedKLambda(resoCollision, resoV0s, resoMicroTracks, resoCollision); } - PROCESS_SWITCH(Xi1820Analysis, processMCWithMicroTracks, "Process MC with ResoMicroTracks (placeholder)", false); + PROCESS_SWITCH(Xi1820Analysis, processMCWithMicroTracks, "Process MC with ResoMicroTracks 001", false); void processMCGen(ResoMCCols::iterator const& resoCollision, // Calculate denominator for the acceptance x efficiency and a part of Event-factor (for selected evennts) - aod::ResoMCParents const& resoParents) + aod::ResoMCParents_001 const& resoParents) { auto multiplicity = resoCollision.mcMultiplicity(); auto inCent = resoCollision.cent(); - if (additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) // Check reco INELgt0 - return; - if (!resoCollision.isInAfterAllCuts()) + if ((additionalConfig.cRecoINELgt0 && !resoCollision.isRecINELgt0()) || + (additionalConfig.cMCINELgt0 && !resoCollision.isINELgt0()) || + (additionalConfig.cMCVtxIn10 && !resoCollision.isVtxIn10())) { return; + } histos.fill(HIST("multQA/h2MultCentMC"), inCent, multiplicity); for (const auto& part : resoParents) { // loop over all pre-filtered Gen particle on selected events auto pdgMother = part.pdgCode(); - if (std::abs(pdgMother) != kPdgChagedXi1820 && std::abs(pdgMother) != kPdgXi1820Zero) + if (std::abs(pdgMother) != Pdg::kXi1820Minus && std::abs(pdgMother) != Pdg::kXi1820Zero) { continue; - if (std::abs(part.y()) >= additionalConfig.cfgRapidityCut) + } + if (!passesRapidity(part.y())) { continue; // skip if rapidity of the particle is outside of cut + } auto motherPt = part.pt(); auto daughter1PDG = part.daughterPDG1(); auto daughter2PDG = part.daughterPDG2(); - if (std::abs(pdgMother) == kPdgChagedXi1820) { // Explicity check for the safety. + if (std::abs(pdgMother) == Pdg::kXi1820Minus) { // Explicity check for the safety. // K- + Anti-Lambda, K+ + Anti-Lambda if ((daughter1PDG == PDG_t::kKMinus && daughter2PDG == PDG_t::kLambda0) || (daughter1PDG == PDG_t::kLambda0 && daughter2PDG == PDG_t::kKMinus)) { @@ -1237,17 +1804,16 @@ struct Xi1820Analysis { void processMCTruth(aod::McParticles const& mcParticles) // ->Let's keep it and use for injected MC QA...! { // Process MC generated particles (no reconstruction requirement) - // Xi(1820)0 PDG code: 123314 (neutral, decays to K+ Lambda or K0s Lambda) - // Note: PDG doesn't have separate codes for charge states in this case + // Charged and neutral Xi(1820) states have distinct PDG codes. for (const auto& mcParticle : mcParticles) { // Look for Xi(1820) - PDG code can vary, check for resonance mass ~1820 MeV int pdg = mcParticle.pdgCode(); - // Xi(1820)0: PDG 123314 - // Check if it's Xi(1820) or similar resonance - if (std::abs(pdg) != kPdgChagedXi1820 && std::abs(pdg) != kPdgXi1820Zero) + // Select only the configured charged and neutral Xi(1820) states. + if (std::abs(pdg) != Pdg::kXi1820Minus && std::abs(pdg) != Pdg::kXi1820Zero) { continue; + } // Fill generated level histograms auto pt = mcParticle.pt(); @@ -1260,8 +1826,9 @@ struct Xi1820Analysis { // Get daughters auto daughters = mcParticle.daughters_as(); - if (daughters.size() != kExpectedDaughters) + if (daughters.size() != ExpectedDaughters) { continue; + } int daughter1PDG = 0, daughter2PDG = 0; ROOT::Math::PxPyPzEVector p1, p2, pMother; @@ -1315,7 +1882,7 @@ struct Xi1820Analysis { PROCESS_SWITCH(Xi1820Analysis, processMCTruth, "Process MC Truth particles", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +WorkflowSpec defineDataProcessing(ConfigContext const& context) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGLF/Tasks/Strangeness/CMakeLists.txt b/PWGLF/Tasks/Strangeness/CMakeLists.txt index d59685bc93d..5ddab116838 100644 --- a/PWGLF/Tasks/Strangeness/CMakeLists.txt +++ b/PWGLF/Tasks/Strangeness/CMakeLists.txt @@ -11,26 +11,31 @@ o2physics_add_dpl_workflow(hyperon-reco-test SOURCES hyperon-reco-test.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedlambdakzeroanalysis SOURCES derivedlambdakzeroanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdakzeroanalysis-mc SOURCES lambdakzeroanalysisMC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysis SOURCES cascadeanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysismc SOURCES cascadeanalysisMC.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -41,11 +46,13 @@ o2physics_add_dpl_workflow(v0postprocessing o2physics_add_dpl_workflow(cascadecorrelations SOURCES cascadecorrelations.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(non-prompt-cascade SOURCES nonPromptCascade.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::ReconstructionDataFormats O2Physics::AnalysisCore O2::DetectorsBase O2::DetectorsVertexing O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -56,19 +63,28 @@ o2physics_add_dpl_workflow(k0mixedevents o2physics_add_dpl_workflow(xi-lambda-corr-task SOURCES xiLambdaCorr.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(vzero-cascade-absorption SOURCES vzero_cascade_absorption.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedcascadeanalysis SOURCES derivedcascadeanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(derivedxi0analysis + SOURCES derivedXi0analysis.cxx + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(cascpostprocessing SOURCES cascpostprocessing.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore @@ -76,16 +92,19 @@ o2physics_add_dpl_workflow(cascpostprocessing o2physics_add_dpl_workflow(hstrangecorrelation SOURCES hStrangeCorrelation.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sigmaanalysis SOURCES sigmaanalysis.cxx - PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB + REUSE_FROM LfStrangenessPCH + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::MLCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(phik0shortanalysis SOURCES phik0shortanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) @@ -96,37 +115,44 @@ o2physics_add_dpl_workflow(phi-strange-correlation o2physics_add_dpl_workflow(lambdapolarization SOURCES lambdapolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdapolsp SOURCES lambdapolsp.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdak0seff SOURCES lambdak0seff.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(task-lambda-spin-corr SOURCES taskLambdaSpinCorr.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascpolsp SOURCES cascpolsp.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) if(FastJet_FOUND) o2physics_add_dpl_workflow(strangeness-in-jets SOURCES strangenessInJets.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strangeness-in-jets-ions SOURCES strangenessInJetsIons.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -138,31 +164,37 @@ endif() o2physics_add_dpl_workflow(v0topologicalcuts SOURCES v0topologicalcuts.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(v0ptinvmassplots SOURCES v0ptinvmassplots.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(derivedupcanalysis SOURCES derivedupcanalysis.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdak0sflattenicity SOURCES lambdak0sflattenicity.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdalambda SOURCES lambdalambda.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(lambdajetpolarization SOURCES lambdaJetpolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGJECore FastJet::FastJet FastJet::Contrib O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -178,21 +210,25 @@ o2physics_add_dpl_workflow(lambdaspincorrderived o2physics_add_dpl_workflow(strangenessderivedbinnedinfo SOURCES strangenessderivedbinnedinfo.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lambdatwopartpolarization +o2physics_add_dpl_workflow(lambda-two-part-polarization SOURCES lambdaTwoPartPolarization.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(cascadeanalysislightions SOURCES cascadeAnalysisLightIonsDerivedData.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(strange-casc-track SOURCES strangeCascTrack.cxx + REUSE_FROM LfStrangenessPCH PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) @@ -200,3 +236,8 @@ o2physics_add_dpl_workflow(zdccalderived SOURCES zdccalderived.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(forwardlambdakzeroanalysis + SOURCES forwardlambdakzeroanalysis.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2::GlobalTracking O2::DCAFitter + COMPONENT_NAME Analysis) diff --git a/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx b/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx index 5dba2604ec0..bde34b181eb 100644 --- a/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx +++ b/PWGLF/Tasks/Strangeness/cascadeAnalysisLightIonsDerivedData.cxx @@ -17,8 +17,10 @@ #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/Core/RecoDecay.h" #include @@ -48,12 +50,14 @@ using namespace o2::framework::expressions; using namespace o2::constants::math; using namespace o2::constants::physics; +using namespace o2::aod::rctsel; + using SelCollisions = soa::Join; using SimCollisions = soa::Join; using CascadeCandidates = soa::Join; using CascadeMCCandidates = soa::Join; using DaughterTracks = soa::Join; -using CollisionMCTrueTable = soa::Join; +using CollisionMCTrueTable = soa::Join; using CascadeMCCores = soa::Join; struct CascadeAnalysisLightIonsDerivedData { @@ -72,7 +76,7 @@ struct CascadeAnalysisLightIonsDerivedData { o2::ccdb::CcdbApi ccdbApi; Service ccdb; - int mRunNumber; + int mRunNumber{-1}; // Define histogram registries HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -137,8 +141,18 @@ struct CascadeAnalysisLightIonsDerivedData { ConfigurableAxis axisNch{"axisNch", {500, 0.0f, +5000.0f}, "Number of charged particles"}; ConfigurableAxis axisMult{"axisMult", {500, 0.0f, +100000.0f}, "Multiplicity"}; + struct : ConfigurableGroup { + std::string prefix = "rctConfigurations"; // JSON group name + Configurable cfgRCTLabel{"cfgRCTLabel", "", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "Include ZDC flags in the bit selection (for Pb-Pb only)"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } rctConfigurations; + + RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; + // Centrality estimator Configurable centralityEstimator{"centralityEstimator", 0, "0 = FT0C, 1 = FTOM, 2 = FV0A, 3 = NGlobal"}; + Configurable useMcCentrality{"useMcCentrality", false, "Use MC centrality?"}; // List of estimators enum Option { kFT0C, @@ -172,6 +186,9 @@ struct CascadeAnalysisLightIonsDerivedData { const AxisSpec nsigmaTOFAxis{200, -10, 10, "n#sigma_{TOF}"}; const AxisSpec nsigmaTPCAxis{200, -10, 10, "n#sigma_{TPC}"}; + // Initialise the RCTFlagsChecker + rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); + // Histograms for data if (doprocessData) { registryData.add("number_of_events_data", "number of events in data", HistType::kTH1D, {{20, -0.5f, +19.5f}}); @@ -185,7 +202,8 @@ struct CascadeAnalysisLightIonsDerivedData { registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); - registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(11, "kINELgr0"); + registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(11, "RCT flags"); + registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(12, "kINELgr0"); registryData.add("number_of_events_data_vs_centrality", "number of events in data vs centrality", HistType::kTH2D, {{20, -0.5f, +19.5f}, {101, 0.0f, 101.0f}}); registryData.get(HIST("number_of_events_data_vs_centrality"))->GetXaxis()->SetBinLabel(1, "All collisions"); @@ -198,7 +216,8 @@ struct CascadeAnalysisLightIonsDerivedData { registryData.get(HIST("number_of_events_data_vs_centrality"))->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); registryData.get(HIST("number_of_events_data_vs_centrality"))->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); registryData.get(HIST("number_of_events_data_vs_centrality"))->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); - registryData.get(HIST("number_of_events_data_vs_centrality"))->GetXaxis()->SetBinLabel(11, "kINELgr0"); + registryData.get(HIST("number_of_events_data_vs_centrality"))->GetXaxis()->SetBinLabel(11, "RCT flags"); + registryData.get(HIST("number_of_events_data_vs_centrality"))->GetXaxis()->SetBinLabel(12, "kINELgr0"); registryData.get(HIST("number_of_events_data_vs_centrality"))->GetYaxis()->SetTitle("Centrality (%)"); // QC Histograms @@ -248,7 +267,8 @@ struct CascadeAnalysisLightIonsDerivedData { registryMC.get(HIST("number_of_events_mc_rec"))->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); registryMC.get(HIST("number_of_events_mc_rec"))->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); registryMC.get(HIST("number_of_events_mc_rec"))->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); - registryMC.get(HIST("number_of_events_mc_rec"))->GetXaxis()->SetBinLabel(11, "kINELgr0"); + registryMC.get(HIST("number_of_events_mc_rec"))->GetXaxis()->SetBinLabel(11, "RCT flags"); + registryMC.get(HIST("number_of_events_mc_rec"))->GetXaxis()->SetBinLabel(12, "kINELgr0"); registryMC.add("number_of_events_mc_rec_vs_centrality", "number of events in mc_rec vs centrality", HistType::kTH2D, {{20, -0.5f, +19.5f}, {101, 0.0f, 101.0f}}); registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetXaxis()->SetBinLabel(1, "All collisions"); @@ -261,7 +281,8 @@ struct CascadeAnalysisLightIonsDerivedData { registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetXaxis()->SetBinLabel(8, "kIsVertexTOFmatched"); registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetXaxis()->SetBinLabel(9, "kIsVertexTRDmatched"); registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); - registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetXaxis()->SetBinLabel(11, "kINELgr0"); + registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetXaxis()->SetBinLabel(11, "RCT flags"); + registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetXaxis()->SetBinLabel(12, "kINELgr0"); registryMC.get(HIST("number_of_events_mc_rec_vs_centrality"))->GetYaxis()->SetTitle("Centrality (%)"); // QC Histograms @@ -280,6 +301,8 @@ struct CascadeAnalysisLightIonsDerivedData { // Multiplicity Histograms registryMC.add("hCentEstimator_truerec", "hCentEstimator_truerec", HistType::kTH1D, {{101, 0.0f, 101.0f}}); registryMC.add("hCentralityVsNch_truerec", "hCentralityVsNch_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, axisNch}); + if (useMcCentrality) + registryMC.add("hEventVsGenCentrality_truerec", "hEventVsGenCentrality_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, {101, 0.0f, 101.0f}}); if (centralityEstimator == Option::kNGlobal) registryMC.add("hCentralityVsMultiplicity_truerec", "hCentralityVsMultiplicity_truerec", HistType::kTH2D, {{101, 0.0f, 101.0f}, axisNch}); else @@ -303,27 +326,46 @@ struct CascadeAnalysisLightIonsDerivedData { // QC Histograms registryQC.add("hVertexZGen", "hVertexZGen", HistType::kTH1D, {{vertexZAxis}}); // Histograms for xi (mc) - registryMC.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); registryMC.add("h2dGenXiMinus", "h2dGenXiMinus", HistType::kTH2D, {centAxis, ptAxis}); registryMC.add("h2dGenXiPlus", "h2dGenXiPlus", HistType::kTH2D, {centAxis, ptAxis}); // Histograms for omega (mc) - registryMC.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); - registryMC.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); registryMC.add("h2dGenOmegaMinus", "h2dGenOmegaMinus", HistType::kTH2D, {centAxis, ptAxis}); registryMC.add("h2dGenOmegaPlus", "h2dGenOmegaPlus", HistType::kTH2D, {centAxis, ptAxis}); - // Histograms for event loss/splitting - registryMC.add("hGenEvents", "hGenEvents", HistType::kTH2D, {{axisNch}, {4, -0.5f, +3.5f}}); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); - registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + if (useMcCentrality) { + registryMC.add("h2dGenXiMinusVsMcCentrality_RecoedEvt", "h2dGenXiMinusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiPlusVsMcCentrality_RecoedEvt", "h2dGenXiPlusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiMinusVsMcCentrality", "h2dGenXiMinusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenXiPlusVsMcCentrality", "h2dGenXiPlusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + + registryMC.add("h2dGenOmegaMinusVsMcCentrality_RecoedEvt", "h2dGenOmegaMinusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMcCentrality_RecoedEvt", "h2dGenOmegaPlusVsMcCentrality_RecoedEvt", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaMinusVsMcCentrality", "h2dGenOmegaMinusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMcCentrality", "h2dGenOmegaPlusVsMcCentrality", HistType::kTH2D, {axisCentEstimator, ptAxis}); + + registryMC.add("hGenEventsVsMcCentrality", "hGenEventsVsMcCentrality", HistType::kTH2D, {axisCentEstimator, {4, -0.5f, +3.5f}}); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); + registryMC.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + } else { + registryMC.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + + registryMC.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + registryMC.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", HistType::kTH2D, {axisNch, ptAxis}); + + registryMC.add("hGenEvents", "hGenEvents", HistType::kTH2D, {axisNch, {4, -0.5f, +3.5f}}); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "All gen. events in INEL > 0"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(3, "Gen. with at least 1 rec. events"); + registryMC.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(4, "Gen. with at least 1 rec. events in INEL > 0"); + } registryMC.add("hGenEventCentrality", "hGenEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); registryMC.add("hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", HistType::kTH2D, {centAxis, {50, -0.5f, 49.5f}}); @@ -342,7 +384,7 @@ struct CascadeAnalysisLightIonsDerivedData { } template - void initCCDB(TCollision collision) + void initCCDB(const TCollision& collision) { if (mRunNumber == collision.runNumber()) { return; @@ -665,12 +707,29 @@ struct CascadeAnalysisLightIonsDerivedData { if (applyZVtxSelOnMCPV && std::fabs(mcCollision.posZ()) > zVtx) continue; - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); + float centralityMC = -1.f; + if (centralityEstimator == Option::kFT0C) + centralityMC = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMC = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMC = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMC = mcCollision.centNGlobal(); + + if (useMcCentrality) + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 0 /* all gen. events*/); + else + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); if (requireInel0OnMC && mcCollision.multMCNParticlesEta10() < 1) { continue; } - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* all gen. events in INEL > 0*/); + + if (useMcCentrality) + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 1 /* all gen. events in INEL > 0*/); + else + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* all gen. events in INEL > 0*/); auto groupedCollisions = getGroupedCollisions(collisions, mcCollision.globalIndex()); // Check if there is at least one of the reconstructed collisions associated to this MC collision @@ -721,14 +780,18 @@ struct CascadeAnalysisLightIonsDerivedData { if (biggestNContribs < collision.multPVTotalContributors()) { biggestNContribs = collision.multPVTotalContributors(); - if (centralityEstimator == Option::kFT0C) - centralitydata = collision.centFT0C(); - if (centralityEstimator == Option::kFT0M) - centralitydata = collision.centFT0M(); - if (centralityEstimator == Option::kFV0A) - centralitydata = collision.centFV0A(); - if (centralityEstimator == Option::kNGlobal) - centralitydata = collision.centNGlobal(); + if (useMcCentrality) { + centralitydata = centralityMC; + } else { + if (centralityEstimator == Option::kFT0C) + centralitydata = collision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralitydata = collision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralitydata = collision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralitydata = collision.centNGlobal(); + } } nCollisions++; @@ -742,8 +805,13 @@ struct CascadeAnalysisLightIonsDerivedData { registryQC.fill(HIST("hVertexZGen"), mcCollision.posZ()); if (atLeastOne) { - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 2 /* at least 1 rec. event*/); - registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 3 /* at least 1 rec. event in INEL > 0*/); + if (useMcCentrality) { + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 2 /* at least 1 rec. event*/); + registryMC.fill(HIST("hGenEventsVsMcCentrality"), centralityMC, 3 /* at least 1 rec. event in INEL > 0*/); + } else { + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 2 /* at least 1 rec. event*/); + registryMC.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 3 /* at least 1 rec. event in INEL > 0*/); + } registryMC.fill(HIST("hGenEventCentrality"), centralitydata); } } @@ -836,12 +904,18 @@ struct CascadeAnalysisLightIonsDerivedData { registryData.fill(HIST("number_of_events_data"), 9 /* Not at same bunch pile-up */); registryData.fill(HIST("number_of_events_data_vs_centrality"), 9, centrality); - if (requireInel0 && collision.multNTracksPVeta1() < 1) { + if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) { return; } - registryData.fill(HIST("number_of_events_data"), 10 /* INEL > 0 */); + registryData.fill(HIST("number_of_events_data"), 10 /* Pass CBT condition */); registryData.fill(HIST("number_of_events_data_vs_centrality"), 10, centrality); + if (requireInel0 && collision.multNTracksPVeta1() < 1) { + return; + } + registryData.fill(HIST("number_of_events_data"), 11 /* INEL > 0 */); + registryData.fill(HIST("number_of_events_data_vs_centrality"), 11, centrality); + // Store the Zvtx registryQC.fill(HIST("hVertexZdata"), collision.posZ()); @@ -930,6 +1004,25 @@ struct CascadeAnalysisLightIonsDerivedData { multiplicityMcRec = RecCol.multNTracksGlobal(); } + if (useMcCentrality) { + const float recoCentrality = centralityMcRec; + centralityMcRec = -1.f; + + if (RecCol.has_straMCCollision()) { + auto mcCollision = RecCol.template straMCCollision_as(); + if (centralityEstimator == Option::kFT0C) + centralityMcRec = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMcRec = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMcRec = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMcRec = mcCollision.centNGlobal(); + } + + registryMC.fill(HIST("hEventVsGenCentrality_truerec"), recoCentrality, centralityMcRec); + } + registryMC.fill(HIST("number_of_events_mc_rec_vs_centrality"), 0, centralityMcRec); // event selections @@ -985,12 +1078,18 @@ struct CascadeAnalysisLightIonsDerivedData { registryMC.fill(HIST("number_of_events_mc_rec"), 9 /* Not at same bunch pile-up */); registryMC.fill(HIST("number_of_events_mc_rec_vs_centrality"), 9, centralityMcRec); - if (requireInel0 && RecCol.multNTracksPVeta1() < 1) { + if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(RecCol)) { return; } - registryMC.fill(HIST("number_of_events_mc_rec"), 10 /* INEL > 0 */); + registryMC.fill(HIST("number_of_events_mc_rec"), 10 /* Pass CBT condition */); registryMC.fill(HIST("number_of_events_mc_rec_vs_centrality"), 10, centralityMcRec); + if (requireInel0 && RecCol.multNTracksPVeta1() < 1) { + return; + } + registryMC.fill(HIST("number_of_events_mc_rec"), 11 /* INEL > 0 */); + registryMC.fill(HIST("number_of_events_mc_rec_vs_centrality"), 11, centralityMcRec); + // Store the Zvtx registryQC.fill(HIST("hVertexZRec"), RecCol.posZ()); @@ -1061,7 +1160,7 @@ struct CascadeAnalysisLightIonsDerivedData { bool isTrueMCCascadeDecay = false; bool isCorrectLambdaDecay = false; - if (isPhysPrim && (isXiMC || isOmegaMC)) + if (isXiMC || isOmegaMC) isTrueMCCascade = true; if (isTrueMCCascade && ((casc.sign() > 0 && cascMC.pdgCodePositive() == PDG_t::kPiPlus && cascMC.pdgCodeNegative() == PDG_t::kProtonBar) || (casc.sign() < 0 && cascMC.pdgCodePositive() == PDG_t::kProton && cascMC.pdgCodeNegative() == PDG_t::kPiMinus))) isCorrectLambdaDecay = true; @@ -1117,47 +1216,86 @@ struct CascadeAnalysisLightIonsDerivedData { } float centralityMC = 100.5f; + float centralityMCtrue = -1.f; + + if (centralityEstimator == Option::kFT0C) + centralityMCtrue = mcCollision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMCtrue = mcCollision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMCtrue = mcCollision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMCtrue = mcCollision.centNGlobal(); if (listBestCollisionIdx[mcCollision.globalIndex()] > -1) { auto collision = RecCols.iteratorAt(listBestCollisionIdx[mcCollision.globalIndex()]); - if (centralityEstimator == Option::kFT0C) - centralityMC = collision.centFT0C(); - if (centralityEstimator == Option::kFT0M) - centralityMC = collision.centFT0M(); - if (centralityEstimator == Option::kFV0A) - centralityMC = collision.centFV0A(); - if (centralityEstimator == Option::kNGlobal) - centralityMC = collision.centNGlobal(); + + if (useMcCentrality) { + centralityMC = centralityMCtrue; + } else { + if (centralityEstimator == Option::kFT0C) + centralityMC = collision.centFT0C(); + if (centralityEstimator == Option::kFT0M) + centralityMC = collision.centFT0M(); + if (centralityEstimator == Option::kFV0A) + centralityMC = collision.centFV0A(); + if (centralityEstimator == Option::kNGlobal) + centralityMC = collision.centNGlobal(); + } if (cascMC.pdgCode() == kXiMinus && std::abs(cascMC.rapidityMC(0)) < rapcut) { - registryMC.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiMinusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kXiPlusBar && std::abs(cascMC.rapidityMC(0)) < rapcut) { - registryMC.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiPlusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaMinus && std::abs(cascMC.rapidityMC(2)) < rapcut) { - registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaMinusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaPlusBar && std::abs(cascMC.rapidityMC(2)) < rapcut) { - registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaPlusVsMcCentrality_RecoedEvt"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); } } if (cascMC.pdgCode() == kXiMinus && std::abs(cascMC.rapidityMC(0)) < rapcut) { registryMC.fill(HIST("h2dGenXiMinus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiMinusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kXiPlusBar && std::abs(cascMC.rapidityMC(0)) < rapcut) { registryMC.fill(HIST("h2dGenXiPlus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenXiPlusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaMinus && std::abs(cascMC.rapidityMC(2)) < rapcut) { registryMC.fill(HIST("h2dGenOmegaMinus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaMinusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } if (cascMC.pdgCode() == kOmegaPlusBar && std::abs(cascMC.rapidityMC(2)) < rapcut) { registryMC.fill(HIST("h2dGenOmegaPlus"), centralityMC, ptmc); - registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (useMcCentrality) + registryMC.fill(HIST("h2dGenOmegaPlusVsMcCentrality"), centralityMCtrue, ptmc); + else + registryMC.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); } } // cascMC loop } diff --git a/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx b/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx index c73632268dd..d8fbf39ca88 100644 --- a/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx +++ b/PWGLF/Tasks/Strangeness/cascadecorrelations.cxx @@ -300,7 +300,7 @@ struct CascadeSelector { } template - void fillMatchedHistos(LabeledCascades::iterator rec, int flag, TCollision collision) + void fillMatchedHistos(const LabeledCascades::iterator& rec, int flag, const TCollision& collision) { if (flag == 0) return; @@ -932,9 +932,9 @@ struct CascadeCorrelations { ConfigurableAxis axisVtxZ{"axisVtxZ", {VARIABLE_WIDTH, -10.0f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "Mixing bins - z-vertex"}; // ConfigurableAxis axisMult{"axisMult", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100, 1000}, "Mixing bins - multiplicity"}; // using BinningType = ColumnBinningPolicy; - // BinningType colBinning{{axisVtxZ, axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + // BinningType colBinning{{axisVtxZ, axisMult}}; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVtxZ}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningType colBinning{{axisVtxZ}}; void processSameEvent(MyCollisionsMult::iterator const& collision, MyCascades const& Cascades, FullTracksExtIU const&, aod::BCsWithTimestamps const&) { diff --git a/PWGLF/Tasks/Strangeness/cascpolsp.cxx b/PWGLF/Tasks/Strangeness/cascpolsp.cxx index 49f642f653d..123f9605154 100644 --- a/PWGLF/Tasks/Strangeness/cascpolsp.cxx +++ b/PWGLF/Tasks/Strangeness/cascpolsp.cxx @@ -243,7 +243,7 @@ struct cascpolsp { } template - bool IsCascAccepted(TCascade casc, collision_t collision) + bool IsCascAccepted(const TCascade& casc, const collision_t& collision) { if (casc.cascradius() < cfgcasc_radius) diff --git a/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx b/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx index ee7823637bd..00bb3a69e10 100644 --- a/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx +++ b/PWGLF/Tasks/Strangeness/cascpostprocessing.cxx @@ -18,18 +18,19 @@ #include "PWGLF/DataModel/cascqaanalysis.h" -#include +#include #include #include #include #include #include -#include #include +#include #include -#include +#include #include +#include #include #include @@ -37,14 +38,60 @@ #include // constants -const float ctauxiPDG = 4.91; // from PDG -const float ctauomegaPDG = 2.461; // from PDG +const float kCtauXi = 4.91; // from PDG +const float kCtauOmega = 2.461; // from PDG using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; -struct cascpostprocessing { +struct LfCascpostprocessing { + static constexpr int TrackQualityTofHighPt = 2; + static constexpr int TrackQualityMinITS = 3; + + enum MisidentifiedParticleBin { + kMisIdUnknown = 0, + kMisIdElectron, + kMisIdMuon, + kMisIdPion, + kMisIdKaon, + kMisIdProton, + kMisIdK0Short, + kMisIdLambda, + kMisIdXi, + kMisIdOmega, + kMisIdOther, + kNMisidentifiedParticleBins + }; + + static int getMisidentifiedParticleBin(int pdgCode) + { + switch (TMath::Abs(pdgCode)) { + case 0: + return kMisIdUnknown; + case PDG_t::kElectron: + return kMisIdElectron; + case PDG_t::kMuonMinus: + return kMisIdMuon; + case PDG_t::kPiPlus: + return kMisIdPion; + case PDG_t::kKPlus: + return kMisIdKaon; + case PDG_t::kProton: + return kMisIdProton; + case PDG_t::kK0Short: + return kMisIdK0Short; + case PDG_t::kLambda0: + return kMisIdLambda; + case PDG_t::kXiMinus: + return kMisIdXi; + case PDG_t::kOmegaMinus: + return kMisIdOmega; + default: + return kMisIdOther; + } + } + // Xi or Omega Configurable isXi{"isXi", 1, "Apply cuts for Xi identification"}; Configurable hastof{"hastof", 0, "Apply nsigmaTOF to daughter tracks of cascade"}; @@ -78,23 +125,22 @@ struct cascpostprocessing { Configurable nsigmatofPr{"nsigmatofPr", 6, "N sigma TOF Proton"}; Configurable nsigmatofKa{"nsigmatofKa", 6, "N sigma TOF Kaon"}; Configurable mintpccrrows{"mintpccrrows", 50, "min N TPC crossed rows"}; - Configurable dooobrej{"dooobrej", 0, "OOB rejection: 0 no selection, 1 = ITS||TOF, 2 = TOF only for pT > ptthrtof"}; + Configurable dooobrej{"dooobrej", 0, "Track association requirement: 0 no selection, 1 = ITS||TOF, 2 = TOF only for pT > ptthrtof, 3 = ITS hits >= trackQualityMinITS"}; + Configurable trackQualityMinITS{"trackQualityMinITS", 2, "Minimum ITS hits for dooobrej=3 track-quality variation"}; Configurable isSelectBachBaryon{"isSelectBachBaryon", 0, "Bachelor-baryon cascade selection"}; Configurable bachBaryonCosPA{"bachBaryonCosPA", 0.9999, "Bachelor baryon CosPA"}; Configurable bachBaryonDCAxyToPV{"bachBaryonDCAxyToPV", 0.05, "DCA bachelor baryon to PV"}; - Configurable isMC{"isMC", 0, "0 - data, 1 - MC"}; + bool isMC = false; Configurable evSelFlag{"evSelFlag", 2, "1 - INEL; 2 - INEL>0; 3 - INEL>1"}; HistogramRegistry registry{"registryts"}; - // Necessary for particle charges - Service pdgDB; - void init(InitContext const&) { + isMC = doprocessGen; AxisSpec ximassAxis = {200, 1.28f, 1.36f}; AxisSpec omegamassAxis = {200, 1.59f, 1.75f}; @@ -102,24 +148,33 @@ struct cascpostprocessing { if (!isXi) massAxis = omegamassAxis; AxisSpec ptAxis = {200, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec ptRecAxis = {200, 0.0f, 10.0f, "#it{p}_{T}^{rec} (GeV/#it{c})"}; + AxisSpec ptGenAxis = {200, 0.0f, 10.0f, "#it{p}_{T}^{gen} (GeV/#it{c})"}; AxisSpec ptAxisTopoVar = {50, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec ptAxisPID = {50, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; ConfigurableAxis etaAxis{"etaAxis", {40, -2.0f, 2.0f}, "#eta"}; + AxisSpec pdgCodeAxis = {10001, -5000.5f, 5000.5f, "MC PDG code (0 = no MC assoc.)"}; + AxisSpec misidentifiedParticleAxis = {kNMisidentifiedParticleBins, -0.5f, static_cast(kNMisidentifiedParticleBins) - 0.5f, "Associated MC particle"}; + auto setMisidentifiedParticleLabels = [](TAxis* axis) { + TString misidentifiedParticleLabels[kNMisidentifiedParticleBins] = {"Unknown/no assoc.", "e", "#mu", "#pi", "K", "p", "K^{0}_{S}", "#Lambda", "#Xi", "#Omega", "Other"}; + for (int n = 1; n <= kNMisidentifiedParticleBins; n++) { + axis->SetBinLabel(n, misidentifiedParticleLabels[n - 1]); + } + }; - ConfigurableAxis centFT0MAxis{"FT0M", + ConfigurableAxis centFT0MAxis{"centFT0MAxis", {VARIABLE_WIDTH, 0., 0.01, 0.05, 0.1, 0.5, 1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 101, 105.5}, "FT0M (%)"}; - ConfigurableAxis centFV0AAxis{"FV0A", + ConfigurableAxis centFV0AAxis{"centFV0AAxis", {VARIABLE_WIDTH, 0., 0.01, 0.05, 0.1, 0.5, 1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100, 101, 105.5}, "FV0A (%)"}; ConfigurableAxis nChargedFT0MGenAxis{"nChargedFT0MGenAxis", {300, 0, 300}, "N_{FT0M, gen.}"}; AxisSpec rapidityAxis = {200, -2.0f, 2.0f, "y"}; - AxisSpec phiAxis = {100, -TMath::Pi() / 2, 3. * TMath::Pi() / 2, "#varphi"}; - TString CutLabel[26] = {"All", "MassWin", "y", "EtaDau", "DCADauToPV", "CascCosPA", "V0CosPA", "DCACascDau", "DCAV0Dau", "rCasc", "rCascMax", "rV0", "rV0Max", "DCAV0ToPV", "LambdaMass", "TPCPr", "TPCPi", "TOFPr", "TOFPi", "TPCBach", "TOFBach", "ctau", "CompDecayMass", "Bach-baryon", "NTPCrows", "OOBRej"}; - TString CutLabelSummary[29] = {"MassWin", "y", "EtaDau", "dcapostopv", "dcanegtopv", "dcabachtopv", "CascCosPA", "V0CosPA", "DCACascDau", "DCAV0Dau", "rCasc", "rV0", "DCAV0ToPV", "LambdaMass", "TPCPr", "TPCPi", "TOFPr", "TOFPi", "TPCBach", "TOFBach", "proplifetime", "rejcomp", "ptthrtof", "bachBaryonCosPA", "bachBaryonDCAxyToPV", "NTPCrows", "OOBRej", "rCascMax", "rV0Max"}; + TString CutLabel[26] = {"All", "MassWin", "y", "EtaDau", "DCADauToPV", "CascCosPA", "V0CosPA", "DCACascDau", "DCAV0Dau", "rCasc", "rCascMax", "rV0", "rV0Max", "DCAV0ToPV", "LambdaMass", "TPCPr", "TPCPi", "TOFPr", "TOFPi", "TPCBach", "TOFBach", "ctau", "CompDecayMass", "Bach-baryon", "NTPCrows", "TrackQuality"}; + TString CutLabelSummary[29] = {"MassWin", "y", "EtaDau", "dcapostopv", "dcanegtopv", "dcabachtopv", "CascCosPA", "V0CosPA", "DCACascDau", "DCAV0Dau", "rCasc", "rV0", "DCAV0ToPV", "LambdaMass", "TPCPr", "TPCPi", "TOFPr", "TOFPi", "TPCBach", "TOFBach", "proplifetime", "rejcomp", "ptthrtof", "bachBaryonCosPA", "bachBaryonDCAxyToPV", "NTPCrows", "TrackQuality", "rCascMax", "rV0Max"}; registry.add("hCandidate", "hCandidate", HistType::kTH1F, {{26, -0.5, 25.5}}); for (Int_t n = 1; n <= registry.get(HIST("hCandidate"))->GetNbinsX(); n++) { @@ -167,6 +222,10 @@ struct cascpostprocessing { registry.get(HIST("CascadeSelectionSummary"))->SetBinContent(29, v0radiusMax); registry.add("hPt", "hPt", {HistType::kTH1F, {ptAxis}}); + registry.add("hBachPtMinus", "hBachPtMinus", {HistType::kTH1F, {ptAxis}}); + registry.add("hBachPtPlus", "hBachPtPlus", {HistType::kTH1F, {ptAxis}}); + registry.add("hBachPtVsCascPtMinus", "hBachPtVsCascPtMinus", {HistType::kTH2F, {ptAxis, ptAxis}}); + registry.add("hBachPtVsCascPtPlus", "hBachPtVsCascPtPlus", {HistType::kTH2F, {ptAxis, ptAxis}}); registry.add("hCascMinusInvMassvsPt", "hCascMinusInvMassvsPt", HistType::kTH2F, {ptAxis, massAxis}); registry.add("hCascPlusInvMassvsPt", "hCascPlusInvMassvsPt", HistType::kTH2F, {ptAxis, massAxis}); registry.add("hCascMinusInvMassvsPt_FT0M", "hCascMinusInvMassvsPt_FT0M", HistType::kTH3F, {centFT0MAxis, ptAxis, massAxis}); @@ -201,8 +260,6 @@ struct cascpostprocessing { registry.add("hRapMinus1D", "hRapMinus1D", {HistType::kTH1F, {rapidityAxis}}); registry.add("hRapPlus", "hRapPlus", {HistType::kTH2F, {ptAxis, rapidityAxis}}); registry.add("hRapPlus1D", "hRapPlus1D", {HistType::kTH1F, {rapidityAxis}}); - registry.add("hPhiMinus", "hPhiMinus", {HistType::kTH2F, {ptAxis, phiAxis}}); - registry.add("hPhiPlus", "hPhiPlus", {HistType::kTH2F, {ptAxis, phiAxis}}); registry.add("hCtauMinus", "hCtauMinus", {HistType::kTH1F, {{100, 0.0f, 40.0f}}}); registry.add("hCtauPlus", "hCtauPlus", {HistType::kTH1F, {{100, 0.0f, 40.0f}}}); @@ -214,24 +271,45 @@ struct cascpostprocessing { registry.add("hTPCNSigmaBachPi", "hTPCNSigmaBachPi", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTPCNSigmaBachKa", "hTPCNSigmaBachKa", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaPosPi", "hTOFNSigmaPosPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaPosPiVsPt", "hTOFNSigmaPosPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaNegPi", "hTOFNSigmaNegPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaNegPiVsPt", "hTOFNSigmaNegPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaPosPr", "hTOFNSigmaPosPr", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaPosPrVsPt", "hTOFNSigmaPosPrVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaNegPr", "hTOFNSigmaNegPr", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaNegPrVsPt", "hTOFNSigmaNegPrVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaBachPi", "hTOFNSigmaBachPi", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaBachPiVsPt", "hTOFNSigmaBachPiVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hTOFNSigmaBachKa", "hTOFNSigmaBachKa", {HistType::kTH1F, {{120, -6.0f, 6.0f}}}); + registry.add("hTOFNSigmaBachKaVsPt", "hTOFNSigmaBachKaVsPt", {HistType::kTH2F, {ptAxisPID, {120, -6.0f, 6.0f}}}); registry.add("hCascMinusEtaPos", "hCascMinusEtaPos", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); registry.add("hCascMinusEtaNeg", "hCascMinusEtaNeg", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); registry.add("hCascMinusEtaBach", "hCascMinusEtaBach", {HistType::kTH1F, {{100, -1.0f, 1.0f}}}); - // Info for eff x acc from MC - registry.add("hPtCascPlusTrueRec", "hPtCascPlusTrueRec", {HistType::kTH3F, {ptAxis, rapidityAxis, centFT0MAxis}}); - registry.add("hPtCascMinusTrueRec", "hPtCascMinusTrueRec", {HistType::kTH3F, {ptAxis, rapidityAxis, centFT0MAxis}}); - - registry.add("hCascMinusMassvsPtTrueRec", "hCascMinusMassvsPtTrueRec", {HistType::kTH3F, {ptAxis, massAxis, centFT0MAxis}}); - registry.add("hCascPlusMassvsPtTrueRec", "hCascPlusMassvsPtTrueRec", {HistType::kTH3F, {ptAxis, massAxis, centFT0MAxis}}); - registry.add("hCascMinusMassvsPtBG", "hCascMinusMassvsPtBG", {HistType::kTH2F, {ptAxis, massAxis}}); - registry.add("hCascPlusMassvsPtBG", "hCascPlusMassvsPtBG", {HistType::kTH2F, {ptAxis, massAxis}}); if (isMC) { + // Info for eff x acc from MC + registry.add("hPtCascPlusTrueRec", "hPtCascPlusTrueRec", {HistType::kTH3F, {ptAxis, rapidityAxis, centFT0MAxis}}); + registry.add("hPtCascMinusTrueRec", "hPtCascMinusTrueRec", {HistType::kTH3F, {ptAxis, rapidityAxis, centFT0MAxis}}); + registry.add("hPtCascPlusTrueRecVsGen", "hPtCascPlusTrueRecVsGen", {HistType::kTH2F, {ptRecAxis, ptGenAxis}}); + registry.add("hPtCascMinusTrueRecVsGen", "hPtCascMinusTrueRecVsGen", {HistType::kTH2F, {ptRecAxis, ptGenAxis}}); + + registry.add("hCascMinusMassvsPtTrueRec", "hCascMinusMassvsPtTrueRec", {HistType::kTH3F, {ptAxis, massAxis, centFT0MAxis}}); + registry.add("hCascPlusMassvsPtTrueRec", "hCascPlusMassvsPtTrueRec", {HistType::kTH3F, {ptAxis, massAxis, centFT0MAxis}}); + registry.add("hCascMinusMassvsPtBG", "hCascMinusMassvsPtBG", {HistType::kTH2F, {ptAxis, massAxis}}); + registry.add("hCascPlusMassvsPtBG", "hCascPlusMassvsPtBG", {HistType::kTH2F, {ptAxis, massAxis}}); + registry.add("hCascMinusMassvsPtBG_FT0M", "hCascMinusMassvsPtBG_FT0M", {HistType::kTH3F, {ptAxis, massAxis, centFT0MAxis}}); + registry.add("hCascPlusMassvsPtBG_FT0M", "hCascPlusMassvsPtBG_FT0M", {HistType::kTH3F, {ptAxis, massAxis, centFT0MAxis}}); + registry.add("hMisidentifiedCascPdgCode", "hMisidentifiedCascPdgCode", {HistType::kTH1F, {pdgCodeAxis}}); + registry.add("hMisidentifiedCascMinusPdgCode", "hMisidentifiedCascMinusPdgCode", {HistType::kTH1F, {pdgCodeAxis}}); + registry.add("hMisidentifiedCascPlusPdgCode", "hMisidentifiedCascPlusPdgCode", {HistType::kTH1F, {pdgCodeAxis}}); + registry.add("hMisidentifiedCascParticle", "hMisidentifiedCascParticle", {HistType::kTH1F, {misidentifiedParticleAxis}}); + registry.add("hMisidentifiedCascMinusParticle", "hMisidentifiedCascMinusParticle", {HistType::kTH1F, {misidentifiedParticleAxis}}); + registry.add("hMisidentifiedCascPlusParticle", "hMisidentifiedCascPlusParticle", {HistType::kTH1F, {misidentifiedParticleAxis}}); + registry.add("hMisidentifiedCascParticleVsPt", "hMisidentifiedCascParticleVsPt", {HistType::kTH2F, {ptAxis, misidentifiedParticleAxis}}); + setMisidentifiedParticleLabels(registry.get(HIST("hMisidentifiedCascParticle"))->GetXaxis()); + setMisidentifiedParticleLabels(registry.get(HIST("hMisidentifiedCascMinusParticle"))->GetXaxis()); + setMisidentifiedParticleLabels(registry.get(HIST("hMisidentifiedCascPlusParticle"))->GetXaxis()); + setMisidentifiedParticleLabels(registry.get(HIST("hMisidentifiedCascParticleVsPt"))->GetYaxis()); registry.add("hPtXiPlusTrue", "hPtXiPlusTrue", {HistType::kTH3F, {ptAxis, rapidityAxis, centFT0MAxis}}); registry.add("hPtXiMinusTrue", "hPtXiMinusTrue", {HistType::kTH3F, {ptAxis, rapidityAxis, centFT0MAxis}}); registry.add("hPtOmegaPlusTrue", "hPtOmegaPlusTrue", {HistType::kTH3F, {ptAxis, rapidityAxis, centFT0MAxis}}); @@ -252,7 +330,7 @@ struct cascpostprocessing { int counter = -1; bool isCorrectlyRec = 0; - for (auto& candidate : mycascades) { + for (auto const& candidate : mycascades) { isCandidate = false; isCorrectlyRec = false; @@ -280,6 +358,9 @@ struct cascpostprocessing { counter = -1; registry.fill(HIST("hCandidate"), ++counter); + if (candidate.pt() < minpt) + continue; + // To have trace of how it was before selections if (candidate.sign() < 0) { registry.fill(HIST("hXiMinusInvMassvsPt_BefSels"), candidate.pt(), candidate.massxi()); @@ -291,14 +372,14 @@ struct cascpostprocessing { } if (isXi) { - if (TMath::Abs(candidate.massxi() - pdgDB->Mass(3312)) > masswin) + if (TMath::Abs(candidate.massxi() - o2::constants::physics::MassXiMinus) > masswin) continue; registry.fill(HIST("hCandidate"), ++counter); if (TMath::Abs(candidate.rapxi()) > rap) continue; registry.fill(HIST("hCandidate"), ++counter); } else { - if (TMath::Abs(candidate.massomega() - pdgDB->Mass(3334)) > masswin) + if (TMath::Abs(candidate.massomega() - o2::constants::physics::MassOmegaMinus) > masswin) continue; registry.fill(HIST("hCandidate"), ++counter); if (TMath::Abs(candidate.rapomega()) > rap) @@ -340,7 +421,7 @@ struct cascpostprocessing { if (TMath::Abs(candidate.dcav0topv()) < dcav0topv) continue; registry.fill(HIST("hCandidate"), ++counter); - if (TMath::Abs(candidate.masslambdadau() - pdgDB->Mass(3122)) > lambdamasswin) + if (TMath::Abs(candidate.masslambdadau() - o2::constants::physics::MassLambda0) > lambdamasswin) continue; registry.fill(HIST("hCandidate"), ++counter); if (candidate.sign() < 0) { @@ -385,10 +466,10 @@ struct cascpostprocessing { if (hastof && TMath::Abs(candidate.ntofsigmabachpi()) > nsigmatofPi && candidate.bachpt() > ptthrtof && candidate.bachhastof()) continue; registry.fill(HIST("hCandidate"), ++counter); - if (candidate.ctauxi() > proplifetime * ctauxiPDG) + if (candidate.ctauxi() > proplifetime * kCtauXi) continue; registry.fill(HIST("hCandidate"), ++counter); - if (TMath::Abs(candidate.massomega() - pdgDB->Mass(3334)) < rejcomp) + if (TMath::Abs(candidate.massomega() - o2::constants::physics::MassOmegaMinus) < rejcomp) continue; registry.fill(HIST("hCandidate"), ++counter); rapidity = candidate.rapxi(); @@ -401,17 +482,17 @@ struct cascpostprocessing { if (hastof && TMath::Abs(candidate.ntofsigmabachka()) > nsigmatofKa && candidate.bachpt() > ptthrtof && candidate.bachhastof()) continue; registry.fill(HIST("hCandidate"), ++counter); - if (candidate.ctauomega() > proplifetime * ctauomegaPDG) + if (candidate.ctauomega() > proplifetime * kCtauOmega) continue; registry.fill(HIST("hCandidate"), ++counter); - if (TMath::Abs(candidate.massxi() - pdgDB->Mass(3312)) < rejcomp) + if (TMath::Abs(candidate.massxi() - o2::constants::physics::MassXiMinus) < rejcomp) continue; registry.fill(HIST("hCandidate"), ++counter); rapidity = candidate.rapomega(); ctau = candidate.ctauomega(); invmass = candidate.massomega(); } - if (isSelectBachBaryon && (candidate.bachBaryonCosPA() > bachBaryonCosPA || fabs(candidate.bachBaryonDCAxyToPV()) < bachBaryonDCAxyToPV)) { // Bach-baryon selection if required + if (isSelectBachBaryon && (candidate.bachBaryonCosPA() > bachBaryonCosPA || std::fabs(candidate.bachBaryonDCAxyToPV()) < bachBaryonDCAxyToPV)) { // Bach-baryon selection if required continue; } registry.fill(HIST("hCandidate"), ++counter); @@ -420,14 +501,19 @@ struct cascpostprocessing { registry.fill(HIST("hCandidate"), ++counter); bool kHasTOF = (candidate.poshastof() || candidate.neghastof() || candidate.bachhastof()); bool kHasITS = ((candidate.positshits() > 1) || (candidate.negitshits() > 1) || (candidate.bachitshits() > 1)); + bool kPassMinITS = ((candidate.positshits() >= trackQualityMinITS) || (candidate.negitshits() >= trackQualityMinITS) || (candidate.bachitshits() >= trackQualityMinITS)); if (dooobrej == 1) { if (!kHasTOF && !kHasITS) continue; registry.fill(HIST("hCandidate"), ++counter); - } else if (dooobrej == 2) { + } else if (dooobrej == TrackQualityTofHighPt) { if (!kHasTOF && (candidate.pt() > ptthrtof)) continue; registry.fill(HIST("hCandidate"), ++counter); + } else if (dooobrej == TrackQualityMinITS) { + if (!kPassMinITS) + continue; + registry.fill(HIST("hCandidate"), ++counter); } else { ++counter; } @@ -448,27 +534,33 @@ struct cascpostprocessing { registry.fill(HIST("hBachBaryonCosPA"), candidate.pt(), candidate.bachBaryonCosPA()); registry.fill(HIST("hBachBaryonDCAxyToPV"), candidate.pt(), candidate.bachBaryonDCAxyToPV()); if (candidate.sign() > 0) { + registry.fill(HIST("hBachPtPlus"), candidate.bachpt()); + registry.fill(HIST("hBachPtVsCascPtPlus"), candidate.pt(), candidate.bachpt()); registry.fill(HIST("hCtauPlus"), ctau); registry.fill(HIST("hEtaPlus"), candidate.pt(), candidate.eta()); registry.fill(HIST("hRapPlus"), candidate.pt(), rapidity); registry.fill(HIST("hRapPlus1D"), rapidity); - // registry.fill(HIST("hPhiPlus"), candidate.pt(), candidate.phi()); } else { + registry.fill(HIST("hBachPtMinus"), candidate.bachpt()); + registry.fill(HIST("hBachPtVsCascPtMinus"), candidate.pt(), candidate.bachpt()); registry.fill(HIST("hCtauMinus"), ctau); registry.fill(HIST("hEtaMinus"), candidate.pt(), candidate.eta()); registry.fill(HIST("hRapMinus"), candidate.pt(), rapidity); registry.fill(HIST("hRapMinus1D"), rapidity); - // registry.fill(HIST("hPhiMinus"), candidate.pt(), candidate.phi()); } if (isXi) { - isCorrectlyRec = ((TMath::Abs(candidate.mcPdgCode()) == 3312) && (candidate.isPrimary() == 1)) ? 1 : 0; - if (TMath::Abs(candidate.massxi() - pdgDB->Mass(3312)) < masswintpc) { + if (isMC) { + isCorrectlyRec = ((TMath::Abs(candidate.mcPdgCode()) == PDG_t::kXiMinus) && (candidate.isPrimary() == 1)) ? 1 : 0; + } + if (TMath::Abs(candidate.massxi() - o2::constants::physics::MassXiMinus) < masswintpc) { isCandidate = 1; } - } else if (!isXi) { - isCorrectlyRec = ((TMath::Abs(candidate.mcPdgCode()) == 3334) && (candidate.isPrimary() == 1)) ? 1 : 0; - if (TMath::Abs(candidate.massomega() - pdgDB->Mass(3334)) < masswintpc) { + } else { + if (isMC) { + isCorrectlyRec = ((TMath::Abs(candidate.mcPdgCode()) == PDG_t::kOmegaMinus) && (candidate.isPrimary() == 1)) ? 1 : 0; + } + if (TMath::Abs(candidate.massomega() - o2::constants::physics::MassOmegaMinus) < masswintpc) { isCandidate = 1; } } @@ -477,7 +569,13 @@ struct cascpostprocessing { registry.fill(HIST("hTPCNSigmaPosPr"), candidate.pt(), candidate.ntpcsigmapospr()); registry.fill(HIST("hTPCNSigmaNegPi"), candidate.pt(), candidate.ntpcsigmanegpi()); registry.fill(HIST("hTOFNSigmaPosPr"), candidate.ntofsigmapospr()); + if (candidate.poshastof()) { + registry.fill(HIST("hTOFNSigmaPosPrVsPt"), candidate.pt(), candidate.ntofsigmapospr()); + } registry.fill(HIST("hTOFNSigmaNegPi"), candidate.ntofsigmanegpi()); + if (candidate.neghastof()) { + registry.fill(HIST("hTOFNSigmaNegPiVsPt"), candidate.pt(), candidate.ntofsigmanegpi()); + } registry.fill(HIST("hCascMinusEtaPos"), candidate.poseta()); registry.fill(HIST("hCascMinusEtaNeg"), candidate.negeta()); registry.fill(HIST("hCascMinusEtaBach"), candidate.bacheta()); @@ -485,37 +583,71 @@ struct cascpostprocessing { registry.fill(HIST("hTPCNSigmaPosPi"), candidate.pt(), candidate.ntpcsigmapospi()); registry.fill(HIST("hTPCNSigmaNegPr"), candidate.pt(), candidate.ntpcsigmanegpr()); registry.fill(HIST("hTOFNSigmaPosPi"), candidate.ntofsigmapospi()); + if (candidate.poshastof()) { + registry.fill(HIST("hTOFNSigmaPosPiVsPt"), candidate.pt(), candidate.ntofsigmapospi()); + } registry.fill(HIST("hTOFNSigmaNegPr"), candidate.ntofsigmanegpr()); + if (candidate.neghastof()) { + registry.fill(HIST("hTOFNSigmaNegPrVsPt"), candidate.pt(), candidate.ntofsigmanegpr()); + } } if (isXi) { registry.fill(HIST("hTPCNSigmaBachPi"), candidate.pt(), candidate.ntpcsigmabachpi()); registry.fill(HIST("hTOFNSigmaBachPi"), candidate.ntofsigmabachpi()); + if (candidate.bachhastof()) { + registry.fill(HIST("hTOFNSigmaBachPiVsPt"), candidate.pt(), candidate.ntofsigmabachpi()); + } } else { registry.fill(HIST("hTPCNSigmaBachKa"), candidate.pt(), candidate.ntpcsigmabachka()); registry.fill(HIST("hTOFNSigmaBachKa"), candidate.ntofsigmabachka()); + if (candidate.bachhastof()) { + registry.fill(HIST("hTOFNSigmaBachKaVsPt"), candidate.pt(), candidate.ntofsigmabachka()); + } } } // registry.fill(HIST("hPosITSHits"), candidate.positshits()); // registry.fill(HIST("hNegITSHits"), candidate.negitshits()); // registry.fill(HIST("hBachITSHits"), candidate.bachitshits()); + if (isMC && !isCorrectlyRec) { + const int misidentifiedParticleBin = getMisidentifiedParticleBin(candidate.mcPdgCode()); + registry.fill(HIST("hMisidentifiedCascPdgCode"), candidate.mcPdgCode()); + registry.fill(HIST("hMisidentifiedCascParticle"), misidentifiedParticleBin); + registry.fill(HIST("hMisidentifiedCascParticleVsPt"), candidate.pt(), misidentifiedParticleBin); + if (candidate.sign() < 0) { + registry.fill(HIST("hMisidentifiedCascMinusPdgCode"), candidate.mcPdgCode()); + registry.fill(HIST("hMisidentifiedCascMinusParticle"), misidentifiedParticleBin); + } else if (candidate.sign() > 0) { + registry.fill(HIST("hMisidentifiedCascPlusPdgCode"), candidate.mcPdgCode()); + registry.fill(HIST("hMisidentifiedCascPlusParticle"), misidentifiedParticleBin); + } + } + if (candidate.sign() < 0) { - if (isCorrectlyRec) { - registry.fill(HIST("hPtCascMinusTrueRec"), candidate.pt(), rapidity, candidate.centFT0M()); // 3rd axis is from MC calibration - registry.fill(HIST("hCascMinusMassvsPtTrueRec"), candidate.pt(), invmass, candidate.centFT0M()); - } else { - registry.fill(HIST("hCascMinusMassvsPtBG"), candidate.pt(), invmass); + if (isMC) { + if (isCorrectlyRec) { + registry.fill(HIST("hPtCascMinusTrueRec"), candidate.pt(), rapidity, candidate.centFT0M()); // 3rd axis is from MC calibration + registry.fill(HIST("hPtCascMinusTrueRecVsGen"), candidate.pt(), candidate.genPt()); + registry.fill(HIST("hCascMinusMassvsPtTrueRec"), candidate.pt(), invmass, candidate.centFT0M()); + } else { + registry.fill(HIST("hCascMinusMassvsPtBG"), candidate.pt(), invmass); + registry.fill(HIST("hCascMinusMassvsPtBG_FT0M"), candidate.pt(), invmass, candidate.centFT0M()); + } } registry.fill(HIST("hCascMinusInvMassvsPt"), candidate.pt(), invmass); registry.fill(HIST("hCascMinusInvMassvsPt_FT0M"), candidate.centFT0M(), candidate.pt(), invmass); registry.fill(HIST("hCascMinusInvMassvsPt_FV0A"), candidate.centFV0A(), candidate.pt(), invmass); } if (candidate.sign() > 0) { - if (isCorrectlyRec) { - registry.fill(HIST("hPtCascPlusTrueRec"), candidate.pt(), rapidity, candidate.centFT0M()); // 3rd axis is from MC calibration - registry.fill(HIST("hCascPlusMassvsPtTrueRec"), candidate.pt(), invmass, candidate.centFT0M()); - } else { - registry.fill(HIST("hCascPlusMassvsPtBG"), candidate.pt(), invmass); + if (isMC) { + if (isCorrectlyRec) { + registry.fill(HIST("hPtCascPlusTrueRec"), candidate.pt(), rapidity, candidate.centFT0M()); // 3rd axis is from MC calibration + registry.fill(HIST("hPtCascPlusTrueRecVsGen"), candidate.pt(), candidate.genPt()); + registry.fill(HIST("hCascPlusMassvsPtTrueRec"), candidate.pt(), invmass, candidate.centFT0M()); + } else { + registry.fill(HIST("hCascPlusMassvsPtBG"), candidate.pt(), invmass); + registry.fill(HIST("hCascPlusMassvsPtBG_FT0M"), candidate.pt(), invmass, candidate.centFT0M()); + } } registry.fill(HIST("hCascPlusInvMassvsPt"), candidate.pt(), invmass); registry.fill(HIST("hCascPlusInvMassvsPt_FT0M"), candidate.centFT0M(), candidate.pt(), invmass); @@ -524,11 +656,11 @@ struct cascpostprocessing { } } - PROCESS_SWITCH(cascpostprocessing, processRec, "Process Run 3 reconstructed data", true); + PROCESS_SWITCH(LfCascpostprocessing, processRec, "Process Run 3 reconstructed data", true); void processGen(aod::MyMCCascades const& myMCcascades) { - for (auto& genCascade : myMCcascades) { + for (auto const& genCascade : myMCcascades) { if (genCascade.isPrimary() == 0) continue; // Consider only primaries @@ -557,16 +689,16 @@ struct cascpostprocessing { continue; // Histos of generated cascades from generated events with accepted z vrtx + chosen event type (evSelFlag) (for signal loss correction) - if (genCascade.pdgCode() == -3312) { + if (genCascade.pdgCode() == PDG_t::kXiPlusBar) { registry.fill(HIST("hPtXiPlusTrue"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } - if (genCascade.pdgCode() == 3312) { + if (genCascade.pdgCode() == PDG_t::kXiMinus) { registry.fill(HIST("hPtXiMinusTrue"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } - if (genCascade.pdgCode() == -3334) { + if (genCascade.pdgCode() == PDG_t::kOmegaPlusBar) { registry.fill(HIST("hPtOmegaPlusTrue"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } - if (genCascade.pdgCode() == 3334) { + if (genCascade.pdgCode() == PDG_t::kOmegaMinus) { registry.fill(HIST("hPtOmegaMinusTrue"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } @@ -592,25 +724,25 @@ struct cascpostprocessing { break; } - if (genCascade.pdgCode() == -3312) { + if (genCascade.pdgCode() == PDG_t::kXiPlusBar) { registry.fill(HIST("hPtXiPlusTrueAssocWithSelColl"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } - if (genCascade.pdgCode() == 3312) { + if (genCascade.pdgCode() == PDG_t::kXiMinus) { registry.fill(HIST("hPtXiMinusTrueAssocWithSelColl"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } - if (genCascade.pdgCode() == -3334) { + if (genCascade.pdgCode() == PDG_t::kOmegaPlusBar) { registry.fill(HIST("hPtOmegaPlusTrueAssocWithSelColl"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } - if (genCascade.pdgCode() == 3334) { + if (genCascade.pdgCode() == PDG_t::kOmegaMinus) { registry.fill(HIST("hPtOmegaMinusTrueAssocWithSelColl"), genCascade.pt(), genCascade.y(), genCascade.centFT0M()); } } } - PROCESS_SWITCH(cascpostprocessing, processGen, "Process Run 3 MC generated data", false); + PROCESS_SWITCH(LfCascpostprocessing, processGen, "Process Run 3 MC generated data", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"lf-cascpostprocessing"})}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx new file mode 100644 index 00000000000..bf84dd619ba --- /dev/null +++ b/PWGLF/Tasks/Strangeness/derivedXi0analysis.cxx @@ -0,0 +1,1271 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file derivedXi0Analysis.cxx +/// \brief Xi0 (--> Lambda pi0 --> Lambda gamma gamma) analysis task using strangeness derived data (produced with the Xi0 builder) +/// +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// +// Xi0 analysis task +// ================= +// +// This code loops over the Xi0Cores table produced by the sigma0builder +// (PWGLF/TableProducer/Strangeness/sigma0builder.cxx, fillXi0Tables = true) +// and produces some standard analysis output. It is meant to be run over +// strangeness derived data. +// +// The Xi0 candidate is built out of three V0s: two photon conversions +// (forming the pi0) and one Lambda. The daughter V0s are reached by +// de-referencing the V0Cores table through the Xi0Indices table. +// +// Three process functions are provided: +// - processRealData : real data +// - processMonteCarlo : reconstructed information in MC +// - processGenerated : pure generated information in MC (from CascMCCores) +// +// N.B.: when running over reconstructed MC information, the rapidity and the +// pT used both in the selections and in the histograms are the generated +// ones, so that the numerator and the denominator of the +// acceptance x efficiency share exactly the same definition. +// +// Comments, questions, complaints, suggestions? +// Please write to: +// romain.schotter@cern.ch +// + +#include "PWGLF/DataModel/LFSigmaTables.h" +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// simple checkers, but ensure 64 bit integers +#define BITSET(var, nbit) ((var) |= (static_cast(1) << static_cast(nbit))) +#define BITCHECK(var, nbit) ((var) & (static_cast(1) << static_cast(nbit))) + +// Xi0 candidates: cores + collision reference + indices to the daughter V0s. +// N.B.: aod::Xi0Indices and aod::Xi0MCIndices must never be joined together, +// since they share the very same column names (photon1Index, ...). +using Xi0Candidates = soa::Join; +using Xi0McCandidates = soa::Join; + +using V0Candidates = soa::Join; +using DauTracks = soa::Join; + +enum CentEstimator { + kCentFT0C = 0, + kCentFT0M, + kCentFT0CVariant1, + kCentMFT, + kCentNGlobal, + kCentFV0A +}; + +struct DerivedXi0Analysis { + Service ccdb; + ctpRateFetcher rateFetcher; + + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Xi0 proper decay length, in cm (PDG: tau = 2.90e-10 s) + static constexpr float CtauXi0 = 8.71f; + // number of QA histogram sets: before and after the candidate selections + static constexpr int NSelectionStages = 2; + + //__________________________________________________ + // Selection bits: one per selection criterion, so that the full selection is + // a single mask operation and the bookkeeping fits in a single histogram + enum Selectionbits : int { // o2-linter: disable=name/enum (bit names follow derivedlambdakzeroanalysis) + // photon selections (both photons of the pi0 have to pass) + selPhotonV0Type = 0, + selPhotonMass, + selPhotonRapidity, + selPhotonDauEta, + selPhotonDCADauToPV, + selPhotonDCADau, + selPhotonRadius, + selPhotonCosPA, + selPhotonArmenteros, + selPhotonPt, + selPhotonTPCCrossedRows, + selPhotonTPCPID, + // Lambda selections, common to Lambda and anti-Lambda + selLambdaV0Type, + selLambdaRapidity, + selLambdaDauEta, + selLambdaDCAPosToPV, + selLambdaDCANegToPV, + selLambdaDCADau, + selLambdaRadius, + selLambdaCosPA, + selLambdaLifetime, + selLambdaTPCCrossedRows, + selLambdaITSClusters, + // Lambda selections, specific to one of the two mass hypotheses + selLambdaMass, + selAntiLambdaMass, + selLambdaArmenteros, // Armenteros alpha > 0 --> the positive daughter is the baryon + selAntiLambdaArmenteros, // Armenteros alpha < 0 --> the negative daughter is the baryon + selTPCPIDPositiveProton, + selTPCPIDNegativePion, + selTPCPIDNegativeProton, + selTPCPIDPositivePion, + // pi0 and Xi0 (cascade) selections + selPi0Mass, + selPi0Radius, + selPi0CosPA, + selPi0DCADau, + selXi0Radius, + selXi0CosPA, + selXi0DCADau, + selXi0DCAxyToPV, + selXi0DCAzToPV, + selXi0Lifetime, + selXi0Rapidity, + selXi0Pt, + // MC tagging + selConsiderXi0, + selConsiderAntiXi0, + selPhysPrimXi0, + selPhysPrimAntiXi0, + }; + + uint64_t maskTopological = 0; + uint64_t maskXi0Specific = 0; + uint64_t maskAntiXi0Specific = 0; + uint64_t maskSelectionXi0 = 0; + uint64_t maskSelectionAntiXi0 = 0; + + //__________________________________________________ + // Event level + Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; + Configurable fGetIR{"fGetIR", false, "Flag to retrieve the IR info."}; + Configurable fIRCrashOnNull{"fIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; + Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds"}; + Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold"}; + Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + Configurable useEvtSelInDenomEff{"useEvtSelInDenomEff", false, "Consider event selections in the recoed <-> gen collision association for the denominator (or numerator) of the acc. x eff. (or signal loss)?"}; + Configurable applyZVtxSelOnMCPV{"applyZVtxSelOnMCPV", false, "Apply Z-vtx cut on the PV of the generated collision?"}; + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; + Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; + Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + } eventSelections; + + //__________________________________________________ + // Photon (from the pi0) selections, applied on the de-referenced V0Cores + struct : ConfigurableGroup { + std::string prefix = "photonSelections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 7, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable maxMass{"maxMass", 0.1, "Max photon mass (GeV/c^2)"}; + Configurable minRapidity{"minRapidity", -0.8, "Min photon rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.8, "Max photon rapidity"}; + Configurable maxDauEta{"maxDauEta", 0.8, "Max |eta| of the daughter tracks"}; + Configurable minDCADauToPV{"minDCADauToPV", 0.0, "Min DCA of the daughter tracks to PV (cm)"}; + Configurable maxDCADau{"maxDCADau", 3.5, "Max DCA between the V0 daughters (cm)"}; + Configurable minRadius{"minRadius", 0.0, "Min photon conversion radius (cm)"}; + Configurable maxRadius{"maxRadius", 240.0, "Max photon conversion radius (cm)"}; + Configurable minCosPA{"minCosPA", 0.8, "Min photon cosine of pointing angle"}; + Configurable maxQt{"maxQt", 0.05, "Max qt (Armenteros-Podolanski) (GeV/c)"}; + Configurable maxAlpha{"maxAlpha", 0.95, "Max |alpha| (Armenteros-Podolanski)"}; + Configurable minPt{"minPt", 0.0, "Min photon pT (GeV/c)"}; + Configurable maxPt{"maxPt", 50.0, "Max photon pT (GeV/c)"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 30, "Min number of TPC crossed rows of the daughter tracks"}; + Configurable minTPCNSigmaEl{"minTPCNSigmaEl", -7, "Min TPC NSigma (electron) of the daughter tracks"}; + Configurable maxTPCNSigmaEl{"maxTPCNSigmaEl", +7, "Max TPC NSigma (electron) of the daughter tracks"}; + } photonSelections; + + //__________________________________________________ + // Lambda selections, applied on the de-referenced V0Cores + struct : ConfigurableGroup { + std::string prefix = "lambdaSelections"; // JSON group name + Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; + Configurable massWindow{"massWindow", 0.015, "Lambda mass window around the PDG value (GeV/c^2)"}; + Configurable minRapidity{"minRapidity", -0.8, "Min Lambda rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.8, "Max Lambda rapidity"}; + Configurable maxDauEta{"maxDauEta", 0.8, "Max |eta| of the daughter tracks"}; + Configurable minDCAPosToPV{"minDCAPosToPV", 0.05, "Min DCA of the positive daughter to PV (cm)"}; + Configurable minDCANegToPV{"minDCANegToPV", 0.05, "Min DCA of the negative daughter to PV (cm)"}; + Configurable maxDCADau{"maxDCADau", 1.0, "Max DCA between the V0 daughters (cm)"}; + Configurable minRadius{"minRadius", 0.5, "Min Lambda decay radius (cm)"}; + Configurable maxRadius{"maxRadius", 200.0, "Max Lambda decay radius (cm)"}; + Configurable minCosPA{"minCosPA", 0.95, "Min Lambda cosine of pointing angle (w.r.t. the PV)"}; + Configurable maxLifetime{"maxLifetime", 30.0, "Max Lambda proper lifetime, m*L/p (cm)"}; + Configurable minTPCCrossedRows{"minTPCCrossedRows", 70, "Min number of TPC crossed rows of the daughter tracks"}; + Configurable minITSclusters{"minITSclusters", -1, "Min number of ITS clusters of the daughter tracks (leave negative if no selection desired)"}; + Configurable maxTPCNSigmaPr{"maxTPCNSigmaPr", 5, "Max |TPC NSigma| (proton) of the baryon daughter"}; + Configurable maxTPCNSigmaPi{"maxTPCNSigmaPi", 5, "Max |TPC NSigma| (pion) of the meson daughter"}; + // Lambda / anti-Lambda are told apart with the Armenteros-Podolanski alpha: + // the baryon takes most of the momentum, so alpha > 0 for Lambda (positive + // daughter = proton) and alpha < 0 for anti-Lambda (negative daughter = anti-proton) + Configurable minAbsAlpha{"minAbsAlpha", 0.25, "Min |alpha| (Armenteros-Podolanski) to tag the Lambda charge"}; + Configurable maxAbsAlpha{"maxAbsAlpha", 1.0, "Max |alpha| (Armenteros-Podolanski)"}; + } lambdaSelections; + + //__________________________________________________ + // Xi0 selections, applied on the cascade itself + struct : ConfigurableGroup { + std::string prefix = "xi0Selections"; // JSON group name + Configurable minRapidity{"minRapidity", -0.5, "Min Xi0 rapidity"}; + Configurable maxRapidity{"maxRapidity", 0.5, "Max Xi0 rapidity"}; + Configurable minPt{"minPt", 0.0, "Min Xi0 pT (GeV/c)"}; + Configurable maxPt{"maxPt", 50.0, "Max Xi0 pT (GeV/c)"}; + Configurable pi0MassWindow{"pi0MassWindow", 0.035, "pi0 mass window around the PDG value (GeV/c^2)"}; + Configurable minPi0Radius{"minPi0Radius", -1, "Min pi0 decay radius (cm), leave negative if no selection desired"}; + Configurable maxPi0Radius{"maxPi0Radius", -1, "Max pi0 decay radius (cm), leave negative if no selection desired"}; + Configurable minPi0CosPA{"minPi0CosPA", -2, "Min pi0 cosine of pointing angle, leave below -1 if no selection desired"}; + Configurable maxDCAPi0Daughters{"maxDCAPi0Daughters", -1, "Max DCA between the two photons (cm), leave negative if no selection desired"}; + Configurable minCascRadius{"minCascRadius", 0.5, "Min Xi0 decay radius (cm)"}; + Configurable maxCascRadius{"maxCascRadius", 200.0, "Max Xi0 decay radius (cm)"}; + Configurable minCascCosPA{"minCascCosPA", 0.98, "Min Xi0 cosine of pointing angle"}; + Configurable maxDCACascDaughters{"maxDCACascDaughters", 1.0, "Max DCA between the Xi0 daughters (cm)"}; + Configurable maxDCAxyCascToPV{"maxDCAxyCascToPV", 1.0, "Max |DCAxy| of the Xi0 to the PV (cm), leave negative if no selection desired"}; + Configurable maxDCAzCascToPV{"maxDCAzCascToPV", -1, "Max |DCAz| of the Xi0 to the PV (cm), leave negative if no selection desired"}; + Configurable maxLifetime{"maxLifetime", 3.0, "Max Xi0 proper lifetime, in units of c*tau"}; + } xi0Selections; + + //__________________________________________________ + // MC-specific selections + struct : ConfigurableGroup { + std::string prefix = "mcSelections"; // JSON group name + Configurable doMCAssociation{"doMCAssociation", false, "Keep only candidates whose full decay chain is matched to a true Xi0/anti-Xi0"}; + Configurable requirePhysicalPrimary{"requirePhysicalPrimary", true, "Keep only physical primary Xi0, at reconstructed and generated level"}; + } mcSelections; + + //__________________________________________________ + // QA switches + Configurable doEventQA{"doEventQA", true, "do event QA histograms"}; + Configurable doCandidateQA{"doCandidateQA", true, "do candidate-level QA histograms"}; + Configurable doMCQA{"doMCQA", true, "do MC-specific QA histograms (pT resolution, background composition)"}; + + //__________________________________________________ + // Axes + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "Centrality (%)"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.0f, 15.0f, 20.0f}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisXi0Mass{"axisXi0Mass", {200, 1.2f, 1.5f}, "#it{M}_{#Lambda#pi^{0}} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPi0Mass{"axisPi0Mass", {200, 0.0f, 0.4f}, "#it{M}_{#gamma#gamma} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.07f, 1.17f}, "#it{M}_{p#pi} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisRadius{"axisRadius", {200, 0.0f, 100.0f}, "Decay radius (cm)"}; + ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.9f, 1.0f}, "cos(#theta_{PA})"}; + ConfigurableAxis axisDCADau{"axisDCADau", {200, 0.0f, 5.0f}, "DCA between daughters (cm)"}; + ConfigurableAxis axisDCAToPV{"axisDCAToPV", {200, -5.0f, 5.0f}, "DCA to PV (cm)"}; + ConfigurableAxis axisLifetime{"axisLifetime", {200, 0.0f, 50.0f}, "Proper lifetime (cm)"}; + ConfigurableAxis axisRapidity{"axisRapidity", {200, -1.0f, 1.0f}, "#it{y}"}; + ConfigurableAxis axisArmAlpha{"axisArmAlpha", {200, -1.0f, 1.0f}, "#alpha (Armenteros-Podolanski)"}; + ConfigurableAxis axisArmQt{"axisArmQt", {200, 0.0f, 0.5f}, "#it{q}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisNch{"axisNch", {300, 0.0f, 3000.0f}, "#it{N}_{ch}"}; + ConfigurableAxis axisPtResolution{"axisPtResolution", {200, -1.0f, 1.0f}, "(#it{p}_{T}^{rec} - #it{p}_{T}^{gen}) / #it{p}_{T}^{gen}"}; + ConfigurableAxis axisVtxZ{"axisVtxZ", {40, -20.0f, 20.0f}, "PV #it{z} (cm)"}; + ConfigurableAxis axisPDGCode{"axisPDGCode", {10001, -5000.5f, 5000.5f}, "PDG code"}; + ConfigurableAxis axisNCandidates{"axisNCandidates", {10, -0.5f, 9.5f}, "Number of candidates"}; + + void init(InitContext const&) + { + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + //_______________________________________________ + // Assemble the selection masks once and for all + maskTopological = 0; + BITSET(maskTopological, selPhotonV0Type); + BITSET(maskTopological, selPhotonMass); + BITSET(maskTopological, selPhotonRapidity); + BITSET(maskTopological, selPhotonDauEta); + BITSET(maskTopological, selPhotonDCADauToPV); + BITSET(maskTopological, selPhotonDCADau); + BITSET(maskTopological, selPhotonRadius); + BITSET(maskTopological, selPhotonCosPA); + BITSET(maskTopological, selPhotonArmenteros); + BITSET(maskTopological, selPhotonPt); + BITSET(maskTopological, selPhotonTPCCrossedRows); + BITSET(maskTopological, selPhotonTPCPID); + BITSET(maskTopological, selLambdaV0Type); + BITSET(maskTopological, selLambdaRapidity); + BITSET(maskTopological, selLambdaDauEta); + BITSET(maskTopological, selLambdaDCAPosToPV); + BITSET(maskTopological, selLambdaDCANegToPV); + BITSET(maskTopological, selLambdaDCADau); + BITSET(maskTopological, selLambdaRadius); + BITSET(maskTopological, selLambdaCosPA); + BITSET(maskTopological, selLambdaLifetime); + BITSET(maskTopological, selLambdaTPCCrossedRows); + BITSET(maskTopological, selLambdaITSClusters); + BITSET(maskTopological, selPi0Mass); + BITSET(maskTopological, selPi0Radius); + BITSET(maskTopological, selPi0CosPA); + BITSET(maskTopological, selPi0DCADau); + BITSET(maskTopological, selXi0Radius); + BITSET(maskTopological, selXi0CosPA); + BITSET(maskTopological, selXi0DCADau); + BITSET(maskTopological, selXi0DCAxyToPV); + BITSET(maskTopological, selXi0DCAzToPV); + BITSET(maskTopological, selXi0Lifetime); + BITSET(maskTopological, selXi0Rapidity); + BITSET(maskTopological, selXi0Pt); + + maskXi0Specific = 0; + BITSET(maskXi0Specific, selLambdaMass); + BITSET(maskXi0Specific, selLambdaArmenteros); + BITSET(maskXi0Specific, selTPCPIDPositiveProton); + BITSET(maskXi0Specific, selTPCPIDNegativePion); + BITSET(maskXi0Specific, selConsiderXi0); + + maskAntiXi0Specific = 0; + BITSET(maskAntiXi0Specific, selAntiLambdaMass); + BITSET(maskAntiXi0Specific, selAntiLambdaArmenteros); + BITSET(maskAntiXi0Specific, selTPCPIDNegativeProton); + BITSET(maskAntiXi0Specific, selTPCPIDPositivePion); + BITSET(maskAntiXi0Specific, selConsiderAntiXi0); + + if (mcSelections.requirePhysicalPrimary) { + BITSET(maskXi0Specific, selPhysPrimXi0); + BITSET(maskAntiXi0Specific, selPhysPrimAntiXi0); + } + + maskSelectionXi0 = maskTopological | maskXi0Specific; + maskSelectionAntiXi0 = maskTopological | maskAntiXi0Specific; + + //_______________________________________________ + // Event selection bookkeeping + auto hEventSelection = histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +22.5f}}); + hEventSelection->GetXaxis()->SetBinLabel(1, "All collisions"); + hEventSelection->GetXaxis()->SetBinLabel(2, "sel8 cut"); + hEventSelection->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + hEventSelection->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + hEventSelection->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + hEventSelection->GetXaxis()->SetBinLabel(6, "posZ cut"); + hEventSelection->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); + hEventSelection->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + hEventSelection->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); + hEventSelection->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); + hEventSelection->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); + hEventSelection->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); + hEventSelection->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); + hEventSelection->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); + hEventSelection->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); + hEventSelection->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + hEventSelection->GetXaxis()->SetBinLabel(17, "INEL>0"); + hEventSelection->GetXaxis()->SetBinLabel(18, "INEL>1"); + hEventSelection->GetXaxis()->SetBinLabel(19, "Below min occup."); + hEventSelection->GetXaxis()->SetBinLabel(20, "Above max occup."); + hEventSelection->GetXaxis()->SetBinLabel(21, "Below min IR"); + hEventSelection->GetXaxis()->SetBinLabel(22, "Above max IR"); + hEventSelection->GetXaxis()->SetBinLabel(23, "Selected collisions"); + + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); + histos.add("hEventPVz", "hEventPVz", kTH1D, {axisVtxZ}); + if (doEventQA) { + histos.add("EventQA/hCentralityVsNch", "hCentralityVsNch", kTH2D, {axisCentrality, axisNch}); + histos.add("EventQA/hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {axisCentrality, axisVtxZ}); + } + + //_______________________________________________ + // Single candidate-selection bookkeeping histogram + auto hSelections = histos.add("hSelections", "hSelections", kTH1D, {{static_cast(selPhysPrimAntiXi0) + 3, -0.5f, static_cast(selPhysPrimAntiXi0) + 2.5f}}); + hSelections->GetXaxis()->SetBinLabel(1, "All"); + hSelections->GetXaxis()->SetBinLabel(selPhotonV0Type + 2, "#gamma V0 type"); + hSelections->GetXaxis()->SetBinLabel(selPhotonMass + 2, "#gamma mass"); + hSelections->GetXaxis()->SetBinLabel(selPhotonRapidity + 2, "#gamma rapidity"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDauEta + 2, "#gamma dau. #eta"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDCADauToPV + 2, "#gamma DCA dau. to PV"); + hSelections->GetXaxis()->SetBinLabel(selPhotonDCADau + 2, "#gamma DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selPhotonRadius + 2, "#gamma radius"); + hSelections->GetXaxis()->SetBinLabel(selPhotonCosPA + 2, "#gamma cosPA"); + hSelections->GetXaxis()->SetBinLabel(selPhotonArmenteros + 2, "#gamma Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selPhotonPt + 2, "#gamma #it{p}_{T}"); + hSelections->GetXaxis()->SetBinLabel(selPhotonTPCCrossedRows + 2, "#gamma TPC rows"); + hSelections->GetXaxis()->SetBinLabel(selPhotonTPCPID + 2, "#gamma TPC PID"); + hSelections->GetXaxis()->SetBinLabel(selLambdaV0Type + 2, "#Lambda V0 type"); + hSelections->GetXaxis()->SetBinLabel(selLambdaRapidity + 2, "#Lambda rapidity"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDauEta + 2, "#Lambda dau. #eta"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCAPosToPV + 2, "#Lambda DCA pos. to PV"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCANegToPV + 2, "#Lambda DCA neg. to PV"); + hSelections->GetXaxis()->SetBinLabel(selLambdaDCADau + 2, "#Lambda DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selLambdaRadius + 2, "#Lambda radius"); + hSelections->GetXaxis()->SetBinLabel(selLambdaCosPA + 2, "#Lambda cosPA"); + hSelections->GetXaxis()->SetBinLabel(selLambdaLifetime + 2, "#Lambda lifetime"); + hSelections->GetXaxis()->SetBinLabel(selLambdaTPCCrossedRows + 2, "#Lambda TPC rows"); + hSelections->GetXaxis()->SetBinLabel(selLambdaITSClusters + 2, "#Lambda ITS clusters"); + hSelections->GetXaxis()->SetBinLabel(selLambdaMass + 2, "#Lambda mass"); + hSelections->GetXaxis()->SetBinLabel(selAntiLambdaMass + 2, "#bar{#Lambda} mass"); + hSelections->GetXaxis()->SetBinLabel(selLambdaArmenteros + 2, "#Lambda Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selAntiLambdaArmenteros + 2, "#bar{#Lambda} Arm. pod."); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDPositiveProton + 2, "TPC PID p"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDNegativePion + 2, "TPC PID #pi^{-}"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDNegativeProton + 2, "TPC PID #bar{p}"); + hSelections->GetXaxis()->SetBinLabel(selTPCPIDPositivePion + 2, "TPC PID #pi^{+}"); + hSelections->GetXaxis()->SetBinLabel(selPi0Mass + 2, "#pi^{0} mass"); + hSelections->GetXaxis()->SetBinLabel(selPi0Radius + 2, "#pi^{0} radius"); + hSelections->GetXaxis()->SetBinLabel(selPi0CosPA + 2, "#pi^{0} cosPA"); + hSelections->GetXaxis()->SetBinLabel(selPi0DCADau + 2, "#pi^{0} DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selXi0Radius + 2, "#Xi^{0} radius"); + hSelections->GetXaxis()->SetBinLabel(selXi0CosPA + 2, "#Xi^{0} cosPA"); + hSelections->GetXaxis()->SetBinLabel(selXi0DCADau + 2, "#Xi^{0} DCA dau."); + hSelections->GetXaxis()->SetBinLabel(selXi0DCAxyToPV + 2, "#Xi^{0} DCA_{xy} to PV"); + hSelections->GetXaxis()->SetBinLabel(selXi0DCAzToPV + 2, "#Xi^{0} DCA_{z} to PV"); + hSelections->GetXaxis()->SetBinLabel(selXi0Lifetime + 2, "#Xi^{0} lifetime"); + hSelections->GetXaxis()->SetBinLabel(selXi0Rapidity + 2, "#Xi^{0} rapidity"); + hSelections->GetXaxis()->SetBinLabel(selXi0Pt + 2, "#Xi^{0} #it{p}_{T}"); + hSelections->GetXaxis()->SetBinLabel(selConsiderXi0 + 2, "True #Xi^{0}"); + hSelections->GetXaxis()->SetBinLabel(selConsiderAntiXi0 + 2, "True #bar{#Xi^{0}}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimXi0 + 2, "Phys. prim. #Xi^{0}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimAntiXi0 + 2, "Phys. prim. #bar{#Xi^{0}}"); + hSelections->GetXaxis()->SetBinLabel(selPhysPrimAntiXi0 + 3, "Cand. selected"); + + //_______________________________________________ + // Main analysis output + histos.add("h3dMassXi0", "h3dMassXi0", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("h3dMassAntiXi0", "h3dMassAntiXi0", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("h2dNbrOfXi0VsCentrality", "h2dNbrOfXi0VsCentrality", kTH2D, {axisCentrality, axisNCandidates}); + histos.add("h2dNbrOfAntiXi0VsCentrality", "h2dNbrOfAntiXi0VsCentrality", kTH2D, {axisCentrality, axisNCandidates}); + + //_______________________________________________ + // Candidate QA, before and after the selections + if (doCandidateQA) { + for (int mode = 0; mode < NSelectionStages; mode++) { + const std::string dir = (mode == 0) ? "QA/BeforeSel/" : "QA/AfterSel/"; + histos.add(dir + "h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add(dir + "hMassPi0", "hMassPi0", kTH1D, {axisPi0Mass}); + histos.add(dir + "hMassLambda", "hMassLambda", kTH1D, {axisLambdaMass}); + histos.add(dir + "hPt", "hPt", kTH1D, {axisPt}); + histos.add(dir + "hRapidity", "hRapidity", kTH1D, {axisRapidity}); + histos.add(dir + "hCascRadius", "hCascRadius", kTH1D, {axisRadius}); + histos.add(dir + "hCascCosPA", "hCascCosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hDCACascDaughters", "hDCACascDaughters", kTH1D, {axisDCADau}); + histos.add(dir + "hDCAxyCascToPV", "hDCAxyCascToPV", kTH1D, {axisDCAToPV}); + histos.add(dir + "hDCAzCascToPV", "hDCAzCascToPV", kTH1D, {axisDCAToPV}); + histos.add(dir + "hLifetime", "hLifetime", kTH1D, {axisLifetime}); + histos.add(dir + "hLambdaRadius", "hLambdaRadius", kTH1D, {axisRadius}); + histos.add(dir + "hLambdaCosPA", "hLambdaCosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hPi0Radius", "hPi0Radius", kTH1D, {axisRadius}); + histos.add(dir + "hPi0CosPA", "hPi0CosPA", kTH1D, {axisCosPA}); + histos.add(dir + "hDCAPi0Daughters", "hDCAPi0Daughters", kTH1D, {axisDCADau}); + histos.add(dir + "h2dArmenterosLambda", "h2dArmenterosLambda", kTH2D, {axisArmAlpha, axisArmQt}); + } + } + + //_______________________________________________ + // MC-specific histograms + if (doprocessMonteCarlo && doMCQA) { + histos.add("MCQA/h2dPtResolution", "h2dPtResolution", kTH2D, {axisPt, axisPtResolution}); + histos.add("MCQA/h2dPtRecoVsPtGen", "h2dPtRecoVsPtGen", kTH2D, {axisPt, axisPt}); + histos.add("MCQA/h3dMassXi0VsPtReco", "h3dMassXi0VsPtReco", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("MCQA/h3dMassAntiXi0VsPtReco", "h3dMassAntiXi0VsPtReco", kTH3D, {axisCentrality, axisPt, axisXi0Mass}); + histos.add("MCQA/hXi0PDGCode", "hXi0PDGCode", kTH1D, {axisPDGCode}); + histos.add("MCQA/hXi0PDGCodeMother", "hXi0PDGCodeMother", kTH1D, {axisPDGCode}); + histos.add("MCQA/h2dPhoton1VsPhoton2PDGCode", "h2dPhoton1VsPhoton2PDGCode", kTH2D, {axisPDGCode, axisPDGCode}); + histos.add("MCQA/hLambdaPDGCode", "hLambdaPDGCode", kTH1D, {axisPDGCode}); + } + + //_______________________________________________ + // Generated-level histograms + if (doprocessGenerated) { + histos.add("Gen/hGenEvents", "hGenEvents", kTH2D, {axisNch, {2, -0.5f, +1.5f}}); + histos.add("Gen/hGenEventCentrality", "hGenEventCentrality", kTH1D, {axisCentrality}); + histos.add("Gen/hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", kTH2D, {axisCentrality, {50, -0.5f, +49.5f}}); + histos.add("Gen/hCentralityVsNcoll_afterEvSel", "hCentralityVsNcoll_afterEvSel", kTH2D, {axisCentrality, {50, -0.5f, +49.5f}}); + histos.add("Gen/hCentralityVsMultMC", "hCentralityVsMultMC", kTH2D, {axisCentrality, axisNch}); + + histos.add("Gen/h2dGenXi0", "h2dGenXi0", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenAntiXi0", "h2dGenAntiXi0", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenXi0VsMultMC", "h2dGenXi0VsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("Gen/h2dGenAntiXi0VsMultMC", "h2dGenAntiXi0VsMultMC", kTH2D, {axisNch, axisPt}); + histos.add("Gen/h2dGenXi0_RecoedEvt", "h2dGenXi0_RecoedEvt", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/h2dGenAntiXi0_RecoedEvt", "h2dGenAntiXi0_RecoedEvt", kTH2D, {axisCentrality, axisPt}); + histos.add("Gen/hGenXi0Rapidity", "hGenXi0Rapidity", kTH1D, {axisRapidity}); + } + } + + //_______________________________________________ + // Check that all the bits of a mask are set in a bitmap + bool verifyMask(uint64_t bitmap, uint64_t mask) + { + return (bitmap & mask) == mask; + } + + //_______________________________________________ + // Centrality getter (Run 3) + template + float getCentralityRun3(TCollision const& collision) + { + if (centralityEstimator == kCentFT0C) + return collision.centFT0C(); + else if (centralityEstimator == kCentFT0M) + return collision.centFT0M(); + else if (centralityEstimator == kCentFT0CVariant1) + return collision.centFT0CVariant1(); + else if (centralityEstimator == kCentMFT) + return collision.centMFT(); + else if (centralityEstimator == kCentNGlobal) + return collision.centNGlobal(); + else if (centralityEstimator == kCentFV0A) + return collision.centFV0A(); + + return -1.f; + } + + //_______________________________________________ + // Check whether the collision passes our collision selections + template + bool isEventAccepted(TCollision const& collision, bool fillHists) + { + if (fillHists) + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 3 /* Not at ITS ROF border */); + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 4 /* Not at TF border */); + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 5 /* vertex-Z selected */); + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 6 /* Contains at least one ITS-TPC track */); + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 7 /* PV position consistency check */); + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 8 /* PV with at least one contributor matched with TOF */); + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 9 /* PV with at least one contributor matched with TRD */); + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 10 /* Not at same bunch pile-up */); + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds */); + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 12 /* No other collision within +/- 10 microseconds */); + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 13 /* No other collision within +/- 2 microseconds */); + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */); + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 15 /* No other collision within the same ITS ROF */); + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 18 /* Below min occupancy */); + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 19 /* Above max occupancy */); + + // Fetch interaction rate only if required (in order to limit ccdb calls) + float interactionRate = (fGetIR) ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource, fIRCrashOnNull) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 20 /* Below min IR */); + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 21 /* Above max IR */); + + if (fillHists) { + histos.fill(HIST("hEventSelection"), 22 /* selected collisions */); + float centrality = getCentralityRun3(collision); + histos.fill(HIST("hEventCentrality"), centrality); + histos.fill(HIST("hEventPVz"), collision.posZ()); + if (doEventQA) { + histos.fill(HIST("EventQA/hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + histos.fill(HIST("EventQA/hCentralityVsPVz"), centrality, collision.posZ()); + } + } + + return true; + } + + //_______________________________________________ + // Photon (pi0 daughter) selection bitmap, computed on the de-referenced V0Cores entry. + // The bitmaps of the two photons are AND-ed, so that a bit is set only if + // both photons of the pi0 satisfy the corresponding criterion. + template + uint64_t computePhotonBitmap(TV0Object const& gamma) + { + uint64_t bitMap = 0; + + if (photonSelections.v0TypeSelection < 0 || gamma.v0Type() == photonSelections.v0TypeSelection) + BITSET(bitMap, selPhotonV0Type); + + if (gamma.mGamma() > 0 && gamma.mGamma() < photonSelections.maxMass) + BITSET(bitMap, selPhotonMass); + + const float photonY = RecoDecay::y(std::array{gamma.px(), gamma.py(), gamma.pz()}, o2::constants::physics::MassGamma); + if (photonY > photonSelections.minRapidity && photonY < photonSelections.maxRapidity) + BITSET(bitMap, selPhotonRapidity); + + if (std::abs(gamma.positiveeta()) < photonSelections.maxDauEta && std::abs(gamma.negativeeta()) < photonSelections.maxDauEta) + BITSET(bitMap, selPhotonDauEta); + + if (std::abs(gamma.dcapostopv()) > photonSelections.minDCADauToPV && std::abs(gamma.dcanegtopv()) > photonSelections.minDCADauToPV) + BITSET(bitMap, selPhotonDCADauToPV); + + if (std::abs(gamma.dcaV0daughters()) < photonSelections.maxDCADau) + BITSET(bitMap, selPhotonDCADau); + + if (gamma.v0radius() > photonSelections.minRadius && gamma.v0radius() < photonSelections.maxRadius) + BITSET(bitMap, selPhotonRadius); + + if (gamma.v0cosPA() > photonSelections.minCosPA) + BITSET(bitMap, selPhotonCosPA); + + if (gamma.qtarm() < photonSelections.maxQt && std::abs(gamma.alpha()) < photonSelections.maxAlpha) + BITSET(bitMap, selPhotonArmenteros); + + if (gamma.pt() > photonSelections.minPt && gamma.pt() < photonSelections.maxPt) + BITSET(bitMap, selPhotonPt); + + auto posTrack = gamma.template posTrackExtra_as(); + auto negTrack = gamma.template negTrackExtra_as(); + + if (posTrack.tpcCrossedRows() >= photonSelections.minTPCCrossedRows && negTrack.tpcCrossedRows() >= photonSelections.minTPCCrossedRows) + BITSET(bitMap, selPhotonTPCCrossedRows); + + if (posTrack.tpcNSigmaEl() > photonSelections.minTPCNSigmaEl && posTrack.tpcNSigmaEl() < photonSelections.maxTPCNSigmaEl && + negTrack.tpcNSigmaEl() > photonSelections.minTPCNSigmaEl && negTrack.tpcNSigmaEl() < photonSelections.maxTPCNSigmaEl) + BITSET(bitMap, selPhotonTPCPID); + + return bitMap; + } + + //_______________________________________________ + // Lambda selection bitmap, computed on the de-referenced V0Cores entry + template + uint64_t computeLambdaBitmap(TV0Object const& lambda, TCollision const& collision) + { + uint64_t bitMap = 0; + + if (lambdaSelections.v0TypeSelection < 0 || lambda.v0Type() == lambdaSelections.v0TypeSelection) + BITSET(bitMap, selLambdaV0Type); + + if (lambda.yLambda() > lambdaSelections.minRapidity && lambda.yLambda() < lambdaSelections.maxRapidity) + BITSET(bitMap, selLambdaRapidity); + + if (std::abs(lambda.positiveeta()) < lambdaSelections.maxDauEta && std::abs(lambda.negativeeta()) < lambdaSelections.maxDauEta) + BITSET(bitMap, selLambdaDauEta); + + if (std::abs(lambda.dcapostopv()) > lambdaSelections.minDCAPosToPV) + BITSET(bitMap, selLambdaDCAPosToPV); + + if (std::abs(lambda.dcanegtopv()) > lambdaSelections.minDCANegToPV) + BITSET(bitMap, selLambdaDCANegToPV); + + if (std::abs(lambda.dcaV0daughters()) < lambdaSelections.maxDCADau) + BITSET(bitMap, selLambdaDCADau); + + if (lambda.v0radius() > lambdaSelections.minRadius && lambda.v0radius() < lambdaSelections.maxRadius) + BITSET(bitMap, selLambdaRadius); + + if (lambda.v0cosPA() > lambdaSelections.minCosPA) + BITSET(bitMap, selLambdaCosPA); + + const float lambdaLifetime = lambda.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * o2::constants::physics::MassLambda0; + if (lambdaSelections.maxLifetime < 0 || lambdaLifetime < lambdaSelections.maxLifetime) + BITSET(bitMap, selLambdaLifetime); + + // Lambda / anti-Lambda separation via the Armenteros-Podolanski alpha: + // in the Lambda decay the baryon carries most of the longitudinal momentum + if (lambda.alpha() > lambdaSelections.minAbsAlpha && lambda.alpha() < lambdaSelections.maxAbsAlpha) + BITSET(bitMap, selLambdaArmenteros); + if (lambda.alpha() < -lambdaSelections.minAbsAlpha && lambda.alpha() > -lambdaSelections.maxAbsAlpha) + BITSET(bitMap, selAntiLambdaArmenteros); + + if (std::abs(lambda.mLambda() - o2::constants::physics::MassLambda0) < lambdaSelections.massWindow) + BITSET(bitMap, selLambdaMass); + if (std::abs(lambda.mAntiLambda() - o2::constants::physics::MassLambda0) < lambdaSelections.massWindow) + BITSET(bitMap, selAntiLambdaMass); + + auto posTrack = lambda.template posTrackExtra_as(); + auto negTrack = lambda.template negTrackExtra_as(); + + if (posTrack.tpcCrossedRows() >= lambdaSelections.minTPCCrossedRows && negTrack.tpcCrossedRows() >= lambdaSelections.minTPCCrossedRows) + BITSET(bitMap, selLambdaTPCCrossedRows); + + if (lambdaSelections.minITSclusters < 0 || + (posTrack.itsNCls() >= lambdaSelections.minITSclusters && negTrack.itsNCls() >= lambdaSelections.minITSclusters)) + BITSET(bitMap, selLambdaITSClusters); + + // TPC PID, for both mass hypotheses + if (std::abs(posTrack.tpcNSigmaPr()) < lambdaSelections.maxTPCNSigmaPr) + BITSET(bitMap, selTPCPIDPositiveProton); + if (std::abs(negTrack.tpcNSigmaPi()) < lambdaSelections.maxTPCNSigmaPi) + BITSET(bitMap, selTPCPIDNegativePion); + if (std::abs(negTrack.tpcNSigmaPr()) < lambdaSelections.maxTPCNSigmaPr) + BITSET(bitMap, selTPCPIDNegativeProton); + if (std::abs(posTrack.tpcNSigmaPi()) < lambdaSelections.maxTPCNSigmaPi) + BITSET(bitMap, selTPCPIDPositivePion); + + return bitMap; + } + + //_______________________________________________ + // pi0 and Xi0 (cascade) selection bitmap. + // rapidity and pt are the generated ones when running over MC. + template + uint64_t computeCascadeBitmap(TXi0Object const& xi0, TCollision const& collision, float rapidity, float pt) + { + uint64_t bitMap = 0; + + if (std::abs(xi0.pi0Mass() - o2::constants::physics::MassPi0) < xi0Selections.pi0MassWindow) + BITSET(bitMap, selPi0Mass); + + if ((xi0Selections.minPi0Radius < 0 || xi0.radiusPi0() > xi0Selections.minPi0Radius) && + (xi0Selections.maxPi0Radius < 0 || xi0.radiusPi0() < xi0Selections.maxPi0Radius)) + BITSET(bitMap, selPi0Radius); + + if (xi0Selections.minPi0CosPA < -1 || xi0.pi0CosPA(collision.posX(), collision.posY(), collision.posZ()) > xi0Selections.minPi0CosPA) + BITSET(bitMap, selPi0CosPA); + + if (xi0Selections.maxDCAPi0Daughters < 0 || xi0.dcadaughtersPi0() < xi0Selections.maxDCAPi0Daughters) + BITSET(bitMap, selPi0DCADau); + + if (xi0.radius() > xi0Selections.minCascRadius && xi0.radius() < xi0Selections.maxCascRadius) + BITSET(bitMap, selXi0Radius); + + if (xi0.cascCosPA(collision.posX(), collision.posY(), collision.posZ()) > xi0Selections.minCascCosPA) + BITSET(bitMap, selXi0CosPA); + + if (xi0.dcadaughters() < xi0Selections.maxDCACascDaughters) + BITSET(bitMap, selXi0DCADau); + + if (xi0Selections.maxDCAxyCascToPV < 0 || std::abs(xi0.dcaXYCascToPV()) < xi0Selections.maxDCAxyCascToPV) + BITSET(bitMap, selXi0DCAxyToPV); + + if (xi0Selections.maxDCAzCascToPV < 0 || std::abs(xi0.dcaZCascToPV()) < xi0Selections.maxDCAzCascToPV) + BITSET(bitMap, selXi0DCAzToPV); + + if (xi0Selections.maxLifetime < 0 || getProperLifetime(xi0, collision) < xi0Selections.maxLifetime * CtauXi0) + BITSET(bitMap, selXi0Lifetime); + + if (rapidity > xi0Selections.minRapidity && rapidity < xi0Selections.maxRapidity) + BITSET(bitMap, selXi0Rapidity); + + if (pt > xi0Selections.minPt && pt < xi0Selections.maxPt) + BITSET(bitMap, selXi0Pt); + + return bitMap; + } + + //_______________________________________________ + // MC association bitmap. + // The builder sets the Xi0 PDG code only if the full decay chain is matched, + // i.e. two true photons from the same true pi0 and a true Lambda sharing the + // same true Xi0 mother, so a single PDG check is enough here. + template + uint64_t computeMCAssociation(TXi0Object const& xi0) + { + uint64_t bitMap = 0; + + if (xi0.pdgCode() == o2::constants::physics::Pdg::kXi0) { + BITSET(bitMap, selConsiderXi0); + if (xi0.isPhysicalPrimary()) + BITSET(bitMap, selPhysPrimXi0); + } + if (xi0.pdgCode() == -o2::constants::physics::Pdg::kXi0) { + BITSET(bitMap, selConsiderAntiXi0); + if (xi0.isPhysicalPrimary()) + BITSET(bitMap, selPhysPrimAntiXi0); + } + + return bitMap; + } + + //_______________________________________________ + // Xi0 proper lifetime, m*L/p, in cm + template + float getProperLifetime(TXi0Object const& xi0, TCollision const& collision) + { + const float decayLength = RecoDecay::distance(std::array{collision.posX(), collision.posY(), collision.posZ()}, + std::array{xi0.x(), xi0.y(), xi0.z()}); + const float totalMomentum = xi0.p(); + return (totalMomentum > 0) ? o2::constants::physics::MassXi0 * decayLength / totalMomentum : 1e6f; + } + + //_______________________________________________ + // Fill the candidate QA histograms. mode = 0: before selections, 1: after selections + template + void fillCandidateQA(TXi0Object const& xi0, TV0Object const& lambda, TCollision const& collision, float pt, float rapidity, float centrality, float massLambda) + { + static constexpr std::string_view MainDir[] = {"QA/BeforeSel", "QA/AfterSel"}; + + histos.fill(HIST(MainDir[mode]) + HIST("/h3dMass"), centrality, pt, xi0.xi0Mass()); + histos.fill(HIST(MainDir[mode]) + HIST("/hMassPi0"), xi0.pi0Mass()); + histos.fill(HIST(MainDir[mode]) + HIST("/hMassLambda"), massLambda); + histos.fill(HIST(MainDir[mode]) + HIST("/hPt"), pt); + histos.fill(HIST(MainDir[mode]) + HIST("/hRapidity"), rapidity); + histos.fill(HIST(MainDir[mode]) + HIST("/hCascRadius"), xi0.radius()); + histos.fill(HIST(MainDir[mode]) + HIST("/hCascCosPA"), xi0.cascCosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCACascDaughters"), xi0.dcadaughters()); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAxyCascToPV"), xi0.dcaXYCascToPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAzCascToPV"), xi0.dcaZCascToPV()); + histos.fill(HIST(MainDir[mode]) + HIST("/hLifetime"), getProperLifetime(xi0, collision)); + histos.fill(HIST(MainDir[mode]) + HIST("/hLambdaRadius"), xi0.radiusLambda()); + histos.fill(HIST(MainDir[mode]) + HIST("/hLambdaCosPA"), xi0.lambdaCosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hPi0Radius"), xi0.radiusPi0()); + histos.fill(HIST(MainDir[mode]) + HIST("/hPi0CosPA"), xi0.pi0CosPA(collision.posX(), collision.posY(), collision.posZ())); + histos.fill(HIST(MainDir[mode]) + HIST("/hDCAPi0Daughters"), xi0.dcadaughtersPi0()); + histos.fill(HIST(MainDir[mode]) + HIST("/h2dArmenterosLambda"), lambda.alpha(), lambda.qtarm()); + } + + //_______________________________________________ + // Main reconstructed-level analysis function, common to data and MC + template + void analyseRecoedXi0s(TCollisions const& collisions, TXi0s const& fullXi0s, TV0s const& fullV0s) + { + // Custom grouping: the Xi0 candidates are not sorted per collision + std::vector> xi0grouped(collisions.size()); + for (const auto& xi0 : fullXi0s) { + xi0grouped[xi0.straCollisionId()].push_back(xi0.globalIndex()); + } + + const int64_t nV0s = fullV0s.size(); + + for (const auto& coll : collisions) { + // Event selection + if (!isEventAccepted(coll, true)) { + continue; + } + const float centrality = getCentralityRun3(coll); + + int nXi0s = 0; + int nAntiXi0s = 0; + + // Xi0 candidates loop + for (size_t i = 0; i < xi0grouped[coll.globalIndex()].size(); i++) { + auto xi0 = fullXi0s.rawIteratorAt(xi0grouped[coll.globalIndex()][i]); + + histos.fill(HIST("hSelections"), 0 /* all candidates */); + + //_______________________________________________ + // De-reference the three daughter V0s + if (xi0.photon1Index() < 0 || xi0.photon1Index() >= nV0s || + xi0.photon2Index() < 0 || xi0.photon2Index() >= nV0s || + xi0.lambdaIndex() < 0 || xi0.lambdaIndex() >= nV0s) { + continue; + } + auto photon1 = fullV0s.rawIteratorAt(xi0.photon1Index()); + auto photon2 = fullV0s.rawIteratorAt(xi0.photon2Index()); + auto lambda = fullV0s.rawIteratorAt(xi0.lambdaIndex()); + + //_______________________________________________ + // Kinematics entering both the selections and the histograms. + // Over MC, the generated rapidity and pT are used instead of the + // reconstructed ones, so that the acceptance x efficiency numerator + // and denominator are defined identically. + const float ptRecoed = xi0.pt(); + float pt = ptRecoed; + float rapidity = xi0.rapidity(); + bool hasMCInfo = false; + if constexpr (requires { xi0.pdgCode(); }) { + // the MC information is available only if the three daughter V0s + // could be associated to a V0MCCore entry by the builder + hasMCInfo = (xi0.photon1PDGCode() != 0 && xi0.photon2PDGCode() != 0 && xi0.lambdaPDGCode() != 0); + if (hasMCInfo) { + pt = xi0.mcpt(); + rapidity = xi0.rapidityMC(); + } + } + + //_______________________________________________ + // Assemble the selection bitmap + uint64_t selMap = computePhotonBitmap(photon1) & computePhotonBitmap(photon2); + selMap |= computeLambdaBitmap(lambda, coll); + selMap |= computeCascadeBitmap(xi0, coll, rapidity, pt); + + if constexpr (requires { xi0.pdgCode(); }) { + selMap |= computeMCAssociation(xi0); + + if (doMCQA) { + histos.fill(HIST("MCQA/hXi0PDGCode"), xi0.pdgCode()); + histos.fill(HIST("MCQA/hXi0PDGCodeMother"), xi0.pdgCodeMother()); + histos.fill(HIST("MCQA/h2dPhoton1VsPhoton2PDGCode"), xi0.photon1PDGCode(), xi0.photon2PDGCode()); + histos.fill(HIST("MCQA/hLambdaPDGCode"), xi0.lambdaPDGCode()); + } + + // disregard the MC association if not explicitly asked for + if (!mcSelections.doMCAssociation) { + BITSET(selMap, selConsiderXi0); + BITSET(selMap, selConsiderAntiXi0); + BITSET(selMap, selPhysPrimXi0); + BITSET(selMap, selPhysPrimAntiXi0); + } + } else { + // real data: every candidate is considered for both hypotheses, + // the Armenteros bits alone tell Xi0 and anti-Xi0 apart + BITSET(selMap, selConsiderXi0); + BITSET(selMap, selConsiderAntiXi0); + BITSET(selMap, selPhysPrimXi0); + BITSET(selMap, selPhysPrimAntiXi0); + } + + // Selection bookkeeping: one bin per criterion + for (int bit = 0; bit <= static_cast(selPhysPrimAntiXi0); bit++) { + if (BITCHECK(selMap, bit)) { + histos.fill(HIST("hSelections"), bit + 1); + } + } + + const bool passXi0 = verifyMask(selMap, maskSelectionXi0); + const bool passAntiXi0 = verifyMask(selMap, maskSelectionAntiXi0); + // the sign of the Armenteros alpha already tells the two hypotheses apart + const float massLambda = (lambda.alpha() < 0) ? lambda.mAntiLambda() : lambda.mLambda(); + + if (doCandidateQA) { + fillCandidateQA<0>(xi0, lambda, coll, pt, rapidity, centrality, massLambda); + } + + if (!passXi0 && !passAntiXi0) { + continue; + } + histos.fill(HIST("hSelections"), static_cast(selPhysPrimAntiXi0) + 2 /* candidate selected */); + + if (doCandidateQA) { + fillCandidateQA<1>(xi0, lambda, coll, pt, rapidity, centrality, massLambda); + } + + if (passXi0) { + nXi0s++; + histos.fill(HIST("h3dMassXi0"), centrality, pt, xi0.xi0Mass()); + } + if (passAntiXi0) { + nAntiXi0s++; + histos.fill(HIST("h3dMassAntiXi0"), centrality, pt, xi0.xi0Mass()); + } + + //_______________________________________________ + // MC-specific QA + if constexpr (requires { xi0.pdgCode(); }) { + if (doMCQA) { + if (passXi0) { + histos.fill(HIST("MCQA/h3dMassXi0VsPtReco"), centrality, ptRecoed, xi0.xi0Mass()); + } + if (passAntiXi0) { + histos.fill(HIST("MCQA/h3dMassAntiXi0VsPtReco"), centrality, ptRecoed, xi0.xi0Mass()); + } + if (hasMCInfo && pt > 0) { + histos.fill(HIST("MCQA/h2dPtResolution"), pt, (ptRecoed - pt) / pt); + histos.fill(HIST("MCQA/h2dPtRecoVsPtGen"), pt, ptRecoed); + } + } + } + } + + histos.fill(HIST("h2dNbrOfXi0VsCentrality"), centrality, nXi0s); + histos.fill(HIST("h2dNbrOfAntiXi0VsCentrality"), centrality, nAntiXi0s); + } + } + + //_______________________________________________ + // Generated-level processing + // Return the list of indices of the reconstructed collision associated to each MC collision + template + std::vector getListOfRecoCollIndices(TMCCollisions const& mcCollisions, TCollisions const& collisions) + { + std::vector listBestCollisionIdx(mcCollisions.size(), -1); + std::vector> groupedCollisions(mcCollisions.size()); + + for (const auto& coll : collisions) { + if (coll.straMCCollisionId() < 0) { + continue; + } + groupedCollisions[coll.straMCCollisionId()].push_back(coll.globalIndex()); + } + + for (auto const& mcCollision : mcCollisions) { + int biggestNContribs = -1; + int bestCollisionIndex = -1; + for (size_t i = 0; i < groupedCollisions[mcCollision.globalIndex()].size(); i++) { + auto collision = collisions.rawIteratorAt(groupedCollisions[mcCollision.globalIndex()][i]); + + if (eventSelections.useEvtSelInDenomEff && !isEventAccepted(collision, false)) { + continue; + } + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + bestCollisionIndex = collision.globalIndex(); + } + } + listBestCollisionIdx[mcCollision.globalIndex()] = bestCollisionIndex; + } + return listBestCollisionIdx; + } + + //_______________________________________________ + // Event selection applied at generated level + template + bool isGeneratedEventAccepted(TMCCollision const& mcCollision) + { + if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + return false; + } + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + return false; + } + return true; + } + + //_______________________________________________ + // Generated-level processing: generated event properties (event loss/splitting) + template + void fillGeneratedEventProperties(TMCCollisions const& mcCollisions, TCollisions const& collisions) + { + std::vector> groupedCollisions(mcCollisions.size()); + for (const auto& coll : collisions) { + if (coll.straMCCollisionId() < 0) { + continue; + } + groupedCollisions[coll.straMCCollisionId()].push_back(coll.globalIndex()); + } + + for (auto const& mcCollision : mcCollisions) { + if (!isGeneratedEventAccepted(mcCollision)) { + continue; + } + + histos.fill(HIST("Gen/hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events */); + + bool atLeastOne = false; + int biggestNContribs = -1; + float centrality = 100.5f; + int nCollisions = 0; + for (size_t i = 0; i < groupedCollisions[mcCollision.globalIndex()].size(); i++) { + auto collision = collisions.rawIteratorAt(groupedCollisions[mcCollision.globalIndex()][i]); + + if (!isEventAccepted(collision, false)) { + continue; + } + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + centrality = getCentralityRun3(collision); + } + nCollisions++; + atLeastOne = true; + } + + histos.fill(HIST("Gen/hCentralityVsNcoll_beforeEvSel"), centrality, static_cast(groupedCollisions[mcCollision.globalIndex()].size())); + histos.fill(HIST("Gen/hCentralityVsNcoll_afterEvSel"), centrality, nCollisions); + histos.fill(HIST("Gen/hCentralityVsMultMC"), centrality, mcCollision.multMCNParticlesEta05()); + + if (atLeastOne) { + histos.fill(HIST("Gen/hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* at least 1 rec. event */); + histos.fill(HIST("Gen/hGenEventCentrality"), centrality); + } + } + } + + //_______________________________________________ + // Main generated-level analysis function. + // The generated Xi0 are read from the CascMCCores table. + template + void analyseGeneratedXi0s(TMCCollisions const& mcCollisions, TCollisions const& collisions, TCascMCs const& cascMCCores) + { + fillGeneratedEventProperties(mcCollisions, collisions); + std::vector listBestCollisionIdx = getListOfRecoCollIndices(mcCollisions, collisions); + + for (auto const& cascMC : cascMCCores) { + if (std::abs(cascMC.pdgCode()) != o2::constants::physics::Pdg::kXi0) { + continue; + } + if (!cascMC.has_straMCCollision()) { + continue; + } + if (mcSelections.requirePhysicalPrimary && !cascMC.isPhysicalPrimary()) { + continue; + } + + // N.B.: cascdata::RapidityMC only knows about the Xi- and Omega- masses, + // so the Xi0 rapidity is computed here explicitly + const float ptMC = cascMC.ptMC(); + const float rapidityMC = RecoDecay::y(std::array{cascMC.pxMC(), cascMC.pyMC(), cascMC.pzMC()}, o2::constants::physics::MassXi0); + + histos.fill(HIST("Gen/hGenXi0Rapidity"), rapidityMC); + + // same rapidity window as at reconstructed level + if (rapidityMC < xi0Selections.minRapidity || rapidityMC > xi0Selections.maxRapidity) { + continue; + } + + auto mcCollision = cascMC.template straMCCollision_as(); + if (!isGeneratedEventAccepted(mcCollision)) { + continue; + } + + float centrality = 100.5f; + const int bestCollisionIdx = listBestCollisionIdx[mcCollision.globalIndex()]; + if (bestCollisionIdx > -1) { + auto collision = collisions.rawIteratorAt(bestCollisionIdx); + centrality = getCentralityRun3(collision); + + if (cascMC.pdgCode() > 0) { + histos.fill(HIST("Gen/h2dGenXi0_RecoedEvt"), centrality, ptMC); + } else { + histos.fill(HIST("Gen/h2dGenAntiXi0_RecoedEvt"), centrality, ptMC); + } + } + + if (cascMC.pdgCode() > 0) { + histos.fill(HIST("Gen/h2dGenXi0"), centrality, ptMC); + histos.fill(HIST("Gen/h2dGenXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptMC); + } else { + histos.fill(HIST("Gen/h2dGenAntiXi0"), centrality, ptMC); + histos.fill(HIST("Gen/h2dGenAntiXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptMC); + } + } + } + + //_______________________________________________ + // Process functions + void processRealData(soa::Join const& collisions, + Xi0Candidates const& fullXi0s, + V0Candidates const& fullV0s, + DauTracks const&) + { + analyseRecoedXi0s(collisions, fullXi0s, fullV0s); + } + + void processMonteCarlo(soa::Join const& collisions, + Xi0McCandidates const& fullXi0s, + V0Candidates const& fullV0s, + DauTracks const&) + { + analyseRecoedXi0s(collisions, fullXi0s, fullV0s); + } + + void processGenerated(soa::Join const& mcCollisions, + soa::Join const& collisions, + soa::Join const& cascMCCores) + { + analyseGeneratedXi0s(mcCollisions, collisions, cascMCCores); + } + + PROCESS_SWITCH(DerivedXi0Analysis, processRealData, "process as if real data", true); + PROCESS_SWITCH(DerivedXi0Analysis, processMonteCarlo, "process reconstructed information in MC", false); + PROCESS_SWITCH(DerivedXi0Analysis, processGenerated, "process pure generated information in MC", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx index 5bd49843543..c356183ffb2 100644 --- a/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedcascadeanalysis.cxx @@ -17,6 +17,7 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/TriggerAliases.h" #include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" @@ -24,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -52,8 +52,8 @@ #include // constants -const float ctauxiPDG = 4.91; // from PDG -const float ctauomegaPDG = 2.461; // from PDG +const float ctauxi = 4.91; // from PDG +const float ctauomega = 2.461; // from PDG using namespace o2; using namespace o2::framework; @@ -74,17 +74,27 @@ struct Derivedcascadeanalysis { ConfigurableAxis axisMass{"axisMass", {200, 1.222f, 1.422f}, "range of invariant mass, in case of omega take 1.572f, 1.772f"}; ConfigurableAxis axisIR{"axisIR", {510, -1, 50}, "Binning for the interaction rate (kHz)"}; - Configurable isXi{"isXi", 1, "Apply cuts for Xi identification"}; - Configurable ispO{"ispO", 0, "Analyse p--O collisions"}; - Configurable useCentralityFT0M{"useCentralityFT0M", 0, "If true, use centFT0M"}; - Configurable useCentralityFT0A{"useCentralityFT0A", 0, "If true, use centFT0A"}; - Configurable useCentralityFT0Cvar1{"useCentralityFT0Cvar1", 0, "If true, use centFT0FT0Cvar1"}; + Configurable isXi{"isXi", true, "Apply cuts for Xi identification"}; + Configurable ispO{"ispO", false, "Analyse p--O collisions"}; + Configurable useCentralityFT0M{"useCentralityFT0M", false, "If true, use centFT0M"}; + Configurable useCentralityFT0A{"useCentralityFT0A", false, "If true, use centFT0A"}; + Configurable useCentralityFT0Cvar1{"useCentralityFT0Cvar1", false, "If true, use centFT0FT0Cvar1"}; Configurable useTrackOccupancyDef{"useTrackOccupancyDef", true, "Use occupancy definition based on the tracks"}; Configurable useFT0OccupancyDef{"useFT0OccupancyDef", false, "se occupancy definition based on the FT0 signals"}; Configurable minPt{"minPt", 0.0f, "minPt"}; Configurable nPtBinsForNsigmaTPC{"nPtBinsForNsigmaTPC", 100, ""}; Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; Configurable ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable histoCCDBPath{"histoCCDBPath", "Users/l/lhusova/LHC2025/PointingAngleSelection", "Path for the selection histograms in the CCDB"}; + + o2::aod::rctsel::RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; + + struct : ConfigurableGroup { + std::string prefix = "rctConfigurations"; // JSON group name + Configurable cfgRCTLabel{"cfgRCTLabel", "", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "Include ZDC flags in the bit selection (for Pb-Pb only)"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } rctConfigurations; struct : ConfigurableGroup { std::string prefix = "qa"; @@ -115,6 +125,7 @@ struct Derivedcascadeanalysis { Configurable doNoCollInRofStandardCut{"doNoCollInRofStandardCut", true, "Enable an evevnt selection which rejects a collision if there are other events within the same ITS ROF with mult above threshold"}; Configurable doMultiplicityCorrCut{"doMultiplicityCorrCut", false, "Enable multiplicity vs centrality correlation cut"}; Configurable doITSallLayersCut{"doITSallLayersCut", false, "Enable event selection which rejects collisions when ITS was rebooting."}; + Configurable doCBTselection{"doCBTselection", true, "Require only events with good PID (CBT_hadronPID)"}; Configurable minOccupancy{"minOccupancy", -1, "Minimal occupancy"}; Configurable maxOccupancy{"maxOccupancy", -1, "Maximal occupancy"}; Configurable minOccupancyFT0{"minOccupancyFT0", -1, "Minimal occupancy"}; @@ -186,10 +197,12 @@ struct Derivedcascadeanalysis { Configurable doNTOFSigmaProtonCut{"doNTOFSigmaProtonCut", true, "Enable n sigma TOF PID cut for proton from V0"}; Configurable doNTOFSigmaV0PionCut{"doNTOFSigmaV0PionCut", false, "Enable n sigma TOF PID cut for pion from V0"}; Configurable doNTOFSigmaBachelorCut{"doNTOFSigmaBachelorCut", false, "Enable n sigma TOF PID cut for bachelor track"}; - Configurable dooobrej{"dooobrej", 0, "OOB rejection: 0 no selection, 1 = ITS||TOF, 2 = TOF only for pT > ptthrtof"}; + Configurable dooobrej{"dooobrej", false, "OOB rejection: if true ITS||TOF is required, 2 = TOF only for pT > ptthrtof"}; + Configurable dooobrejPtThrTOF{"dooobrejPtThrTOF", false, "OOB rejection: if true TOF is required only for pT > ptthrtof"}; Configurable doAtLeastOneTrackAB{"doAtLeastOneTrackAB", false, "require that at least one of the daughter tracks is from Afterburner"}; Configurable doBachelorITSTracking{"doBachelorITSTracking", false, "require that the bachelor track is from the ITS tracking"}; Configurable doAllTracksMinITSClusters{"doAllTracksMinITSClusters", false, "require that all daughter tracks have minimal ITS hits"}; + Configurable useInterpolationForCosPACut{"useInterpolationForCosPACut", true, "use interpolation for pt dependent cosPA cut"}; } candidateSelectionFlags; struct : ConfigurableGroup { @@ -218,6 +231,8 @@ struct Derivedcascadeanalysis { Configurable mintpccrrows{"mintpccrrows", 70, "min N TPC crossed rows"}; Configurable cosPApar0{"cosPApar0", 0.2, "const par for pt dep cosPA cut"}; Configurable cosPApar1{"cosPApar1", -0.022, "linear par for pt dep cosPA cut"}; + Configurable v0cosPApar0{"v0cosPApar0", 0.2, "const par for pt dep cosPA cut of the V0 daughter"}; + Configurable v0cosPApar1{"v0cosPApar1", -0.022, "linear par for pt dep cosPA cut of the V0 daughter"}; Configurable parCascRadius0{"parCascRadius0", 1.216159, "const par for pt dep radius cut"}; Configurable parCascRadius1{"parCascRadius1", 0.064462, "linear par for pt dep radius cut"}; Configurable parV0Radius0{"parV0Radius0", 2.136381, "const par for pt dep V0 radius cut"}; @@ -228,6 +243,7 @@ struct Derivedcascadeanalysis { Configurable lambdaMassWin{"lambdaMassWin", 0.005, "V0 Mass window limit"}; Configurable proplifetime{"proplifetime", 3, "ctau/"}; Configurable ptthrtof{"ptthrtof", 2, "Pt threshold for applying only tof oob rejection"}; + Configurable ptthrnSigmaTof{"ptthrnSigmaTof", 0.6, "Pt threshold for applying n sigma TOF for proton PID"}; Configurable rejcomp{"rejcomp", 0.008, "Competing Cascade rejection"}; Configurable masswin{"masswin", 0.05, "Mass window limit"}; Configurable rapCut{"rapCut", 0.5, "Rapidity acceptance"}; @@ -235,25 +251,34 @@ struct Derivedcascadeanalysis { Configurable maxRapCut{"maxRapCut", 0.155, "maximal rapidity acceptance in case of p--o"}; Configurable etaDauCut{"etaDauCut", 0.8, "Pseudorapidity acceptance of the cascade daughters"}; Configurable minITSclusters{"minITSclusters", 3, "minimal number of ITS hits for the daughter tracks"}; + Configurable ptdepCosPACutMaxValue{"ptdepCosPACutMaxValue", 0.3, "maximal PA value for the pt dependent cut, if the cut is above this value, it will be set to this value"}; + Configurable ptdepCosPACutMinValue{"ptdepCosPACutMinValue", 0.012, "minimal PA value for the pt dependent cut, if the cut is below this value, it will be set to this value"}; + Configurable minPtForCosPAcut{"minPtForCosPAcut", 0.5, "minimal pT for the pt dependent cut cosPA cut, the pt is lower, the PA is set tom its maximum value ptdepCosPACutMaxValue"}; + Configurable dcaCacsDauPtSelectionLow{"dcaCacsDauPtSelectionLow", 1.0, "low pt selection for range where DCA selection changes to Par1"}; + Configurable dcaCacsDauPtSelectionHigh{"dcaCacsDauPtSelectionHigh", 4.0, "high pt selection for range where DCA selection changes to Par2"}; } candidateSelectionValues; + TH1F* hXiPointingAngleSelection = nullptr; + TH1F* hOmegaPointingAngleSelection = nullptr; + TH1F* hXiLambdaPointingAngleSelection = nullptr; + TH1F* hOmegaLambdaPointingAngleSelection = nullptr; + o2::ccdb::CcdbApi ccdbApi; Service ccdb; ctpRateFetcher rateFetcher; Service pdgDB; - uint16_t selectionCheckMask; - double selectionCheck; + uint16_t selectionCheckMask = 0; + double selectionCheck = -1; - static constexpr std::string_view kCentIndex[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}; - static constexpr float kCentralityIntervals[11] = {0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90.}; - static constexpr std::string_view kCharge[] = {"Positive", "Negative"}; - static constexpr std::string_view kSelectionNames[] = {"BachelorBaryonDCA", "DCAV0ToPV", "V0Radius", "CascadeRadius", "DCAV0Daughters", "DCACascDaughters", "V0pa", "CascPA", "DCABachelorToPV", "DCAMesonToPV", "DCABaryonToPV", "CascadeProperLifeTime"}; + static constexpr std::array CentIndex = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}; + static constexpr std::array CentralityIntervals = {0., 5., 10., 20., 30., 40., 50., 60., 70., 80., 90.}; + static constexpr std::array SelectionNames = {"BachelorBaryonDCA", "DCAV0ToPV", "V0Radius", "CascadeRadius", "DCAV0Daughters", "DCACascDaughters", "V0pa", "CascPA", "DCABachelorToPV", "DCAMesonToPV", "DCABaryonToPV", "CascadeProperLifeTime"}; // For manual sliceBy // Preslice> perMcCollision = aod::v0data::straMCCollisionId; - PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; PresliceUnsorted> perMcCollisionRun2 = aod::v0data::straMCCollisionId; void init(InitContext const&) @@ -267,32 +292,44 @@ struct Derivedcascadeanalysis { ccdb->setCaching(true); ccdb->setFatalWhenNull(false); - selectionCheck = -1; - selectionCheckMask = 0; - if (!candidateSelectionFlags.doBachelorBaryonCut) + if (!candidateSelectionFlags.doBachelorBaryonCut) { SETBIT(selectionCheckMask, 0); - if (!candidateSelectionFlags.doDCAV0ToPVCut) + } + if (!candidateSelectionFlags.doDCAV0ToPVCut) { SETBIT(selectionCheckMask, 1); - if (!candidateSelectionFlags.doV0RadiusCut) + } + if (!candidateSelectionFlags.doV0RadiusCut) { SETBIT(selectionCheckMask, 2); - if (!candidateSelectionFlags.doCascadeRadiusCut) + } + if (!candidateSelectionFlags.doCascadeRadiusCut) { SETBIT(selectionCheckMask, 3); - if (!candidateSelectionFlags.doDCAV0DauCut) + } + if (!candidateSelectionFlags.doDCAV0DauCut) { SETBIT(selectionCheckMask, 4); - if (!candidateSelectionFlags.doDCACascadeDauCut) + } + if (!candidateSelectionFlags.doDCACascadeDauCut) { SETBIT(selectionCheckMask, 5); - if (!candidateSelectionFlags.doV0CosPaCut) + } + if (!candidateSelectionFlags.doV0CosPaCut) { SETBIT(selectionCheckMask, 6); - if (!candidateSelectionFlags.doCascadeCosPaCut) + } + if (!candidateSelectionFlags.doCascadeCosPaCut) { SETBIT(selectionCheckMask, 7); - if (!candidateSelectionFlags.doDCAbachToPVCut) + } + if (!candidateSelectionFlags.doDCAbachToPVCut) { SETBIT(selectionCheckMask, 8); - if (!candidateSelectionFlags.doDCAmesonToPVCut) + } + if (!candidateSelectionFlags.doDCAmesonToPVCut) { SETBIT(selectionCheckMask, 9); - if (!candidateSelectionFlags.doDCAbaryonToPVCut) + } + if (!candidateSelectionFlags.doDCAbaryonToPVCut) { SETBIT(selectionCheckMask, 10); - if (!candidateSelectionFlags.doProperLifeTimeCut) + } + if (!candidateSelectionFlags.doProperLifeTimeCut) { SETBIT(selectionCheckMask, 11); + } + + rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); histos.add("hEventVertexZ", "hEventVertexZ", kTH1F, {vertexZ}); histos.add("hEventMultFt0C", "", kTH1F, {{500, 0, 5000}}); @@ -300,8 +337,8 @@ struct Derivedcascadeanalysis { histos.add("hEventSelection", "hEventSelection", kTH1F, {{22, 0, 22}}); // TODO adjust labels - TString eventSelLabelRun3[22] = {"all", "sel8", "TVX", "PV_{z}", "cent", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kIsVertexITSTPC", "kIsVertexTOFmatched", "kIsVertexTRDmatched", "kNoITSROFrameBorder", "kNoTimeFrameBorder", "MultCorrCut", "kNoCollInTimeRangeStrict", "kNoCollInTimeRangeStandard", "min Occup", "mxOccup", "kNoCollInRofStrict", "kNoCollInRofStandard", "kIsGoodITSLayersAll", "occupFt0", "-"}; - TString eventSelLabelRun2[22] = {"all", "sel8", "TVX", "PV_{z}", "cent", "sel7", "kINT7", "kNoIncompleteDAQ", "kNoInconsistentVtx", "kNoPileupFromSPD", "kNoV0PFPileup", "kNoPileupInMultBins", "kNoPileupMV", "kNoPileupTPC", "kNoV0MOnVsOfPileup", "kNoSPDOnVsOfPileup", "kNoSPDClsVsTklBG", "INEL0", "-", "-", "-", "-"}; + std::array eventSelLabelRun3 = {"all", "sel8", "TVX", "PV_{z}", "cent", "kNoSameBunchPileup", "kIsGoodZvtxFT0vsPV", "kIsVertexITSTPC", "kIsVertexTOFmatched", "kIsVertexTRDmatched", "kNoITSROFrameBorder", "kNoTimeFrameBorder", "MultCorrCut", "kNoCollInTimeRangeStrict", "kNoCollInTimeRangeStandard", "min Occup", "mxOccup", "kNoCollInRofStrict", "kNoCollInRofStandard", "kIsGoodITSLayersAll", "occupFt0", "CBT sel"}; + std::array eventSelLabelRun2 = {"all", "sel8", "TVX", "PV_{z}", "cent", "sel7", "kINT7", "kNoIncompleteDAQ", "kNoInconsistentVtx", "kNoPileupFromSPD", "kNoV0PFPileup", "kNoPileupInMultBins", "kNoPileupMV", "kNoPileupTPC", "kNoV0MOnVsOfPileup", "kNoSPDOnVsOfPileup", "kNoSPDClsVsTklBG", "INEL0", "-", "-", "-", "-"}; for (int i = 1; i <= histos.get(HIST("hEventSelection"))->GetNbinsX(); i++) { if (doprocessCascades || doprocessCascadesMCrec || doprocessCascadesMCforEff) { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabelRun3[i - 1]); @@ -344,21 +381,24 @@ struct Derivedcascadeanalysis { histos.add("hNsigmaPionPosBach", "hNsigmaPionPosBach", HistType::kTH3D, {{280, -7, 7}, {nPtBinsForNsigmaTPC, 0, 2}, {100, 0, 100}}); histos.add("hNsigmaPionNegBach", "hNsigmaPionNegBach", HistType::kTH3D, {{280, -7, 7}, {nPtBinsForNsigmaTPC, 0, 2}, {100, 0, 100}}); } - if (qaFlags.doFillNsigmaTPCHistKaonBach) + if (qaFlags.doFillNsigmaTPCHistKaonBach) { histos.add("hNsigmaKaon", "hNsigmaKaon", HistType::kTH3D, {{280, -7, 7}, {nPtBinsForNsigmaTPC, 0, 6}, {100, 0, 100}}); - - if (candidateSelectionFlags.doNTOFSigmaProtonCut) + } + if (candidateSelectionFlags.doNTOFSigmaProtonCut) { histos.add("hNsigmaTOFProton", "", HistType::kTH3D, {{70, -7, 7}, {100, 0, 10}, {100, 0, 100}}); - if (candidateSelectionFlags.doNTOFSigmaV0PionCut) + } + if (candidateSelectionFlags.doNTOFSigmaV0PionCut) { histos.add("hNsigmaTOFV0Pion", "", HistType::kTH3D, {{70, -7, 7}, {100, 0, 10}, {100, 0, 100}}); + } if (candidateSelectionFlags.doNTOFSigmaBachelorCut) { - if (isXi) + if (isXi) { histos.add("hNsigmaTOFBachelorPion", "", HistType::kTH3D, {{70, -7, 7}, {100, 0, 10}, {100, 0, 100}}); - else + } else { histos.add("hNsigmaTOFBachelorKaon", "", HistType::kTH3D, {{70, -7, 7}, {100, 0, 10}, {100, 0, 100}}); + } } - TString cutLabel[22] = {"All", "MassWin", "y", "DCACascDau", "DCAV0Dau", "rCasc", "rCascMax", "rV0", "rV0Max", "LambdaMass", "Bach-baryon", "V0CosPA", "CompDecayMass", "DCADauToPV", "EtaDau", "CascCosPA", "DCAV0ToPV", "nSigmaTPCV0Dau", "NTPCrows", "OOBRej", "nSigmaTPCbachelor", "ctau"}; + std::array cutLabel = {"All", "MassWin", "y", "DCACascDau", "DCAV0Dau", "rCasc", "rCascMax", "rV0", "rV0Max", "LambdaMass", "Bach-baryon", "CompDecayMass", "DCADauToPV", "EtaDau", "V0CosPA", "CascCosPA", "DCAV0ToPV", "nSigmaTPCV0Dau", "NTPCrows", "OOBRej", "nSigmaTPCbachelor", "ctau"}; for (int i = 1; i <= histos.get(HIST("hCandidate"))->GetNbinsX(); i++) { histos.get(HIST("hCandidate"))->GetXaxis()->SetBinLabel(i, cutLabel[i - 1]); histos.get(HIST("hCutValue"))->GetXaxis()->SetBinLabel(i, cutLabel[i - 1]); @@ -408,11 +448,13 @@ struct Derivedcascadeanalysis { histos.add("hInteractionRate", "hInteractionRate", kTH1F, {axisIR}); histos.add("hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2F, {{101, 0.0f, 101.0f}, axisIR}); - if (qaFlags.doBefSelCheck) + if (qaFlags.doBefSelCheck) { histos.addClone("InvMassAfterSel/", "InvMassBefSel/"); + } - if (doprocessCascadesMCrec || doprocessCascadesMCrecRun2) + if (doprocessCascadesMCrec || doprocessCascadesMCrecRun2) { histos.addClone("InvMassAfterSel/", "InvMassAfterSelMCrecTruth/"); + } if (qaFlags.doPtDepCutStudy && !candidateSelectionFlags.doProperLifeTimeCut) { histos.add("PtDepCutStudy/hNegativeCascadeProperLifeTime", "hNegativeCascadeProperLifeTime", HistType::kTH3F, {axisPt, axisMass, {100, 0, 10}}); @@ -539,6 +581,20 @@ struct Derivedcascadeanalysis { } } } + void initCosPASelectionHistograms(float timeStamp) + { + + auto* listHistograms = ccdb->getForTimeStamp(histoCCDBPath, timeStamp); + + if (!listHistograms) { + LOG(fatal) << "Problem getting TList object with histograms!"; + } + + hOmegaLambdaPointingAngleSelection = dynamic_cast(listHistograms->FindObject("hOmegaLambdaPointingAngleSelection")); + hXiPointingAngleSelection = dynamic_cast(listHistograms->FindObject("hXiPointingAngleSelection")); + hOmegaPointingAngleSelection = dynamic_cast(listHistograms->FindObject("hOmegaPointingAngleSelection")); + hXiLambdaPointingAngleSelection = dynamic_cast(listHistograms->FindObject("hXiLambdaPointingAngleSelection")); + } // Return slicing output template auto getGroupedCollisions(TCollisions const& collisions, int globalIndex) @@ -550,65 +606,96 @@ struct Derivedcascadeanalysis { } } template - bool isCosPAAccepted(TCascade casc, float x, float y, float z, bool ptdepcut, bool isCascPa) + bool isCosPAAccepted(TCascade const& casc, float x, float y, float z, bool ptdepcut, bool isCascPa) { if (ptdepcut) { - double ptdepCut; - if (isCascPa) - ptdepCut = candidateSelectionValues.cosPApar0 + candidateSelectionValues.cosPApar1 * casc.pt(); - else - ptdepCut = candidateSelectionValues.cosPApar0 + candidateSelectionValues.cosPApar1 * casc.pt(); - if (ptdepCut > 0.3 && casc.pt() < 0.5) - ptdepCut = 0.3; - if (ptdepCut < 0.012) - ptdepCut = 0.012; - if (isCascPa) + double ptdepCut = -1; + if (candidateSelectionFlags.useInterpolationForCosPACut) { + if (isCascPa) { + if (isXi) { + ptdepCut = hXiPointingAngleSelection->Interpolate(casc.pt()); + } else { + ptdepCut = hOmegaPointingAngleSelection->Interpolate(casc.pt()); + } + } else { + if (isXi) { + ptdepCut = hXiLambdaPointingAngleSelection->Interpolate(casc.pt()); + } else { + ptdepCut = hOmegaLambdaPointingAngleSelection->Interpolate(casc.pt()); + } + } + } else { + if (isCascPa) { + ptdepCut = candidateSelectionValues.cosPApar0 + candidateSelectionValues.cosPApar1 * casc.pt(); + } else { + ptdepCut = candidateSelectionValues.v0cosPApar0 + candidateSelectionValues.v0cosPApar1 * casc.pt(); + } + } + if (ptdepCut > candidateSelectionValues.ptdepCosPACutMaxValue) { + ptdepCut = candidateSelectionValues.ptdepCosPACutMaxValue; + } + if (ptdepCut < candidateSelectionValues.ptdepCosPACutMinValue) { + ptdepCut = candidateSelectionValues.ptdepCosPACutMinValue; + } + if (isCascPa) { histos.fill(HIST("hCutValue"), 15, std::cos(ptdepCut)); - if (!isCascPa) - histos.fill(HIST("hCutValue"), 11, std::cos(ptdepCut)); - if (isCascPa && casc.casccosPA(x, y, z) < std::cos(ptdepCut)) + } + if (!isCascPa) { + histos.fill(HIST("hCutValue"), 14, std::cos(ptdepCut)); + } + if (isCascPa && casc.casccosPA(x, y, z) < std::cos(ptdepCut)) { return false; - if (!isCascPa && casc.v0cosPA(x, y, z) < std::cos(ptdepCut)) + } + if (!isCascPa && casc.v0cosPA(x, y, z) < std::cos(ptdepCut)) { return false; + } } else { float cut = candidateSelectionValues.casccospa; float cutV0 = candidateSelectionValues.v0cospa; - if (isCascPa) + if (isCascPa) { histos.fill(HIST("hCutValue"), 15, cut); - if (!isCascPa) - histos.fill(HIST("hCutValue"), 11, cutV0); - if (isCascPa && casc.casccosPA(x, y, z) < candidateSelectionValues.casccospa) + } + if (!isCascPa) { + histos.fill(HIST("hCutValue"), 14, cutV0); + } + if (isCascPa && casc.casccosPA(x, y, z) < candidateSelectionValues.casccospa) { return false; - if (!isCascPa && casc.v0cosPA(x, y, z) < candidateSelectionValues.v0cospa) + } + if (!isCascPa && casc.v0cosPA(x, y, z) < candidateSelectionValues.v0cospa) { return false; + } } return true; } template - bool isEventAccepted(TCollision coll, bool fillHists) + bool isEventAccepted(TCollision const& coll, bool fillHists) { - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 0.5 /* all collisions */); + } if (eventSelectionCommonFlags.doTriggerSel8EventCut && !coll.sel8()) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 1.5 /* collisions after sel8*/); + } if (eventSelectionCommonFlags.doTriggerTVXEventCut && !coll.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 2.5 /* collisions after TVX cut*/); + } if (std::abs(coll.posZ()) > eventSelectionCommonFlags.zVertexCut) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 3.5 /* collisions after sel pvz sel*/); + } float centrality = -10; double interactionRate = -10; int occupancy = -2; @@ -616,12 +703,15 @@ struct Derivedcascadeanalysis { if constexpr (requires { coll.centFT0C(); }) { centrality = coll.centFT0C(); - if (useCentralityFT0M) + if (useCentralityFT0M) { centrality = coll.centFT0M(); - if (useCentralityFT0A) + } + if (useCentralityFT0A) { centrality = coll.centFV0A(); - if (useCentralityFT0Cvar1) + } + if (useCentralityFT0Cvar1) { centrality = coll.centFT0CVariant1(); + } if (qaFlags.doBefSelEventMultCorr) { histos.fill(HIST("hEventNchCorrelationBefCuts"), coll.multNTracksPVeta1(), coll.multNTracksGlobal()); @@ -632,101 +722,119 @@ struct Derivedcascadeanalysis { if (centrality > eventSelectionCommonFlags.centMax || centrality < eventSelectionCommonFlags.centMin) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 4.5 /* collisions after centrality sel*/); + } if (eventSelectionRun3Flags.doSameBunchPileUpEventCut && !coll.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 5.5 /* Not same Bunch pile up */); + } if (eventSelectionRun3Flags.doGoodPVFT0EventCut && !coll.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 6.5 /* No large vertexZ difference from tracks and FT0*/); + } if (eventSelectionRun3Flags.doITSTPCvertexEventCut && !coll.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 7.5 /* At least one ITS-TPC track in the event*/); + } if (eventSelectionRun3Flags.doVertexTOFmatch && !coll.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 8.5 /* At least one of vertex contributors is matched to TOF*/); + } if (eventSelectionRun3Flags.doVertexTRDmatch && !coll.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 9.5 /* At least one of vertex contributors is matched to TRD*/); + } if (eventSelectionRun3Flags.doITSFrameBorderCut && !coll.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 10.5 /* Not at ITS ROF border */); + } if (eventSelectionRun3Flags.doTFeventCut && !coll.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 11.5 /* Not at TF border */); + } if (eventSelectionRun3Flags.doMultiplicityCorrCut) { - if (coll.multNTracksGlobal() < (eventSelectionRun3Flags.globalTracksCorrelpar0Low * std::exp(eventSelectionRun3Flags.globalTracksCorrelpar1Low * centrality) + eventSelectionRun3Flags.globalTracksCorrelpar2Low) || coll.multNTracksGlobal() > (eventSelectionRun3Flags.globalTracksCorrelpar0High * std::exp(eventSelectionRun3Flags.globalTracksCorrelpar1High * centrality) + eventSelectionRun3Flags.globalTracksCorrelpar2High)) + if (coll.multNTracksGlobal() < (eventSelectionRun3Flags.globalTracksCorrelpar0Low * std::exp(eventSelectionRun3Flags.globalTracksCorrelpar1Low * centrality) + eventSelectionRun3Flags.globalTracksCorrelpar2Low) || coll.multNTracksGlobal() > (eventSelectionRun3Flags.globalTracksCorrelpar0High * std::exp(eventSelectionRun3Flags.globalTracksCorrelpar1High * centrality) + eventSelectionRun3Flags.globalTracksCorrelpar2High)) { return false; - if (coll.multNTracksPVeta1() < (eventSelectionRun3Flags.pvContribCorrelpar0Low * std::exp(eventSelectionRun3Flags.pvContribCorrelpar1Low * centrality) + eventSelectionRun3Flags.pvContribCorrelpar2Low) || coll.multNTracksPVeta1() > (eventSelectionRun3Flags.pvContribCorrelpar0High * std::exp(eventSelectionRun3Flags.pvContribCorrelpar1High * centrality) + eventSelectionRun3Flags.pvContribCorrelpar2High)) + } + if (coll.multNTracksPVeta1() < (eventSelectionRun3Flags.pvContribCorrelpar0Low * std::exp(eventSelectionRun3Flags.pvContribCorrelpar1Low * centrality) + eventSelectionRun3Flags.pvContribCorrelpar2Low) || coll.multNTracksPVeta1() > (eventSelectionRun3Flags.pvContribCorrelpar0High * std::exp(eventSelectionRun3Flags.pvContribCorrelpar1High * centrality) + eventSelectionRun3Flags.pvContribCorrelpar2High)) { return false; + } } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 12.5 /* Remove outlyers */); + } if (eventSelectionRun3Flags.doTimeRangeStrictCut && !coll.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 13.5 /* Rejection of events too close in time, dtime +/- 10 μs */); + } if (eventSelectionRun3Flags.doTimeRangeStandardCut && !coll.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 14.5 /* No other collision within +/- 2 μs, or mult above some threshold in -4..-2 μs */); + } occupancy = coll.trackOccupancyInTimeRange(); if (eventSelectionRun3Flags.minOccupancy > 0 && occupancy < eventSelectionRun3Flags.minOccupancy) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 15.5 /* Below min occupancy */); + } if (eventSelectionRun3Flags.maxOccupancy > 0 && occupancy > eventSelectionRun3Flags.maxOccupancy) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 16.5 /* Above max occupancy */); + } if (eventSelectionRun3Flags.doNoCollInRofStrictCut && !coll.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 17.5 /*rejects a collision if there are other events within the same ITS ROF*/); + } if (eventSelectionRun3Flags.doNoCollInRofStandardCut && !coll.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 18.5 /*rejects a collision if there are other events within the same ITS ROF above mult threshold*/); + } if (eventSelectionRun3Flags.doITSallLayersCut && !coll.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 19.5 /*rejects collisions if ITS was in rebooting stage*/); + } occupancyFT0 = coll.ft0cOccupancyInTimeRange(); if (eventSelectionRun3Flags.minOccupancyFT0 > -1 && occupancyFT0 < eventSelectionRun3Flags.minOccupancyFT0) { @@ -735,95 +843,114 @@ struct Derivedcascadeanalysis { if (eventSelectionRun3Flags.maxOccupancyFT0 > -1 && occupancyFT0 > eventSelectionRun3Flags.maxOccupancyFT0) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 20.5 /* Occupancy FT0 selection */); + } interactionRate = rateFetcher.fetch(ccdb.service, coll.timestamp(), coll.runNumber(), irSource) * 1.e-3; + + if (eventSelectionRun3Flags.doCBTselection && !rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(coll)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 21.5 /* Pass CBT condition */); + } } else { centrality = eventSelectionRun2Flags.useSPDTrackletsCent ? coll.centRun2SPDTracklets() : coll.centRun2V0M(); if (centrality > eventSelectionCommonFlags.centMax || centrality < eventSelectionCommonFlags.centMin) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 4.5 /* collisions after centrality sel*/); + } if (eventSelectionRun2Flags.doSel7 && !coll.sel7()) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 5.5 /* sel7 selection */); + } if (eventSelectionRun2Flags.doINT7 && !coll.alias_bit(kINT7)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 6.5 /* INT7-triggered collisions */); + } if (eventSelectionRun2Flags.doIncompleteDAQCut && !coll.selection_bit(o2::aod::evsel::kNoIncompleteDAQ)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 7.5 /* Complete events according to DAQ flags */); + } if (eventSelectionRun2Flags.doConsistentSPDAndTrackVtx && !coll.selection_bit(o2::aod::evsel::kNoInconsistentVtx)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 8.5 /* No inconsistency in SPD and Track vertices */); + } if (eventSelectionRun2Flags.doPileupFromSPDCut && !coll.selection_bit(o2::aod::evsel::kNoPileupFromSPD)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 9.5 /* No pileup according to SPD vertexer */); + } if (eventSelectionRun2Flags.doV0PFPileupCut && !coll.selection_bit(o2::aod::evsel::kNoV0PFPileup)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 10.5 /* No out-of-bunch pileup according to V0 past-future info */); + } if (eventSelectionRun2Flags.doPileupInMultBinsCut && !coll.selection_bit(o2::aod::evsel::kNoPileupInMultBins)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 11.5 /* No pileup according to multiplicity-differential pileup checks */); + } if (eventSelectionRun2Flags.doPileupMVCut && !coll.selection_bit(o2::aod::evsel::kNoPileupMV)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 12.5 /* No pileup according to multi-vertexer */); + } if (eventSelectionRun2Flags.doTPCPileupCut && !coll.selection_bit(o2::aod::evsel::kNoPileupTPC)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 13.5 /* No pileup in TPC */); + } if (eventSelectionRun2Flags.doNoV0MOnVsOffPileup && !coll.selection_bit(o2::aod::evsel::kNoV0MOnVsOfPileup)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 14.5 /* No out-of-bunch pileup according to online-vs-offline VOM correlation */); + } if (eventSelectionRun2Flags.doNoSPDOnVsOffPileup && !coll.selection_bit(o2::aod::evsel::kNoSPDOnVsOfPileup)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 15.5 /* No out-of-bunch pileup according to online-vs-offline SPD correlation */); + } if (eventSelectionRun2Flags.doNoSPDClsVsTklBG && !coll.selection_bit(o2::aod::evsel::kNoSPDClsVsTklBG)) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 16.5 /* No beam-gas according to cluster-vs-tracklet correlation */); + } } if (eventSelectionCommonFlags.doInel0 && coll.multNTracksPVeta1() < 1) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 27.5 /* INEL > 0 selection */); - if (fillHists) { + histos.fill(HIST("hEventSelection"), 27.5 /* INEL > 0 selection */); histos.fill(HIST("hInteractionRate"), interactionRate); histos.fill(HIST("hCentralityVsInteractionRate"), centrality, interactionRate); histos.fill(HIST("hOccupancyVsOccupFt0VsCentrality"), occupancy, occupancyFT0, centrality); @@ -839,68 +966,79 @@ struct Derivedcascadeanalysis { } template - bool isCascadeCandidateAccepted(TCascade casc, int counter, float /*centrality*/) + bool isCascadeCandidateAccepted(TCascade const& casc, int counter, float /*centrality*/) { float cut = candidateSelectionValues.masswin; histos.fill(HIST("hCutValue"), 1, cut); cut = candidateSelectionValues.rapCut; - if (ispO) + if (ispO) { cut = candidateSelectionValues.maxRapCut; + } histos.fill(HIST("hCutValue"), 2, cut); if (isXi) { if (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) > candidateSelectionValues.masswin) { return false; } histos.fill(HIST("hCandidate"), ++counter); - if (ispO && (casc.yXi() < candidateSelectionValues.minRapCut || casc.yXi() > candidateSelectionValues.maxRapCut)) + if (ispO && (casc.yXi() < candidateSelectionValues.minRapCut || casc.yXi() > candidateSelectionValues.maxRapCut)) { return false; - else if (std::abs(casc.yXi()) > candidateSelectionValues.rapCut) + } + if (!ispO && std::abs(casc.yXi()) > candidateSelectionValues.rapCut) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } else { if (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) > candidateSelectionValues.masswin) { return false; } histos.fill(HIST("hCandidate"), ++counter); - if (ispO && (casc.yOmega() < candidateSelectionValues.minRapCut || casc.yOmega() > candidateSelectionValues.maxRapCut)) + if (ispO && (casc.yOmega() < candidateSelectionValues.minRapCut || casc.yOmega() > candidateSelectionValues.maxRapCut)) { return false; - else if (std::abs(casc.yOmega()) > candidateSelectionValues.rapCut) + } + if (!ispO && std::abs(casc.yOmega()) > candidateSelectionValues.rapCut) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } if (candidateSelectionFlags.doDCACascadeDauCut) { if (candidateSelectionFlags.doPtDepDCAcascDauCut) { float ptDepCut = candidateSelectionValues.dcaCacsDauPar0; - if (casc.pt() > 1 && casc.pt() < 4) + if (casc.pt() > candidateSelectionValues.dcaCacsDauPtSelectionLow && casc.pt() < candidateSelectionValues.dcaCacsDauPtSelectionHigh) { ptDepCut = candidateSelectionValues.dcaCacsDauPar1; - else if (casc.pt() > 4) + } else if (casc.pt() > candidateSelectionValues.dcaCacsDauPtSelectionHigh) { ptDepCut = candidateSelectionValues.dcaCacsDauPar2; + } histos.fill(HIST("hCutValue"), 3, ptDepCut); - if (casc.dcacascdaughters() > ptDepCut) + if (casc.dcacascdaughters() > ptDepCut) { return false; + } } else { cut = candidateSelectionValues.dcacascdau; histos.fill(HIST("hCutValue"), 3, cut); - if (casc.dcacascdaughters() > candidateSelectionValues.dcacascdau) + if (casc.dcacascdaughters() > candidateSelectionValues.dcacascdau) { return false; + } } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = casc.dcacascdaughters(); + } ++counter; } if (candidateSelectionFlags.doDCAV0DauCut) { cut = candidateSelectionValues.dcav0dau; histos.fill(HIST("hCutValue"), 4, cut); - if (casc.dcaV0daughters() > candidateSelectionValues.dcav0dau) + if (casc.dcaV0daughters() > candidateSelectionValues.dcav0dau) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = casc.dcaV0daughters(); + } ++counter; } @@ -908,22 +1046,26 @@ struct Derivedcascadeanalysis { if (candidateSelectionFlags.doPtDepCascRadiusCut) { double ptdepminRadius = candidateSelectionValues.parCascRadius0 + candidateSelectionValues.parCascRadius1 * casc.pt(); histos.fill(HIST("hCutValue"), 5, ptdepminRadius); - if (casc.cascradius() < ptdepminRadius) + if (casc.cascradius() < ptdepminRadius) { return false; + } } else { cut = candidateSelectionValues.minRadius; histos.fill(HIST("hCutValue"), 5, cut); - if (casc.cascradius() < candidateSelectionValues.minRadius) + if (casc.cascradius() < candidateSelectionValues.minRadius) { return false; + } } histos.fill(HIST("hCandidate"), ++counter); - if (casc.cascradius() > candidateSelectionValues.maxRadius) + if (casc.cascradius() > candidateSelectionValues.maxRadius) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = casc.cascradius(); + } counter += 2; } @@ -931,28 +1073,33 @@ struct Derivedcascadeanalysis { if (candidateSelectionFlags.doPtDepV0RadiusCut) { float cut = candidateSelectionValues.parV0Radius0 + casc.pt() * candidateSelectionValues.parV0Radius1; histos.fill(HIST("hCutValue"), 7, cut); - if (casc.v0radius() < cut) + if (casc.v0radius() < cut) { return false; + } } else { cut = candidateSelectionValues.minV0Radius; histos.fill(HIST("hCutValue"), 7, cut); - if (casc.v0radius() < candidateSelectionValues.minV0Radius) + if (casc.v0radius() < candidateSelectionValues.minV0Radius) { return false; + } } histos.fill(HIST("hCandidate"), ++counter); - if (casc.v0radius() > candidateSelectionValues.maxV0Radius) + if (casc.v0radius() > candidateSelectionValues.maxV0Radius) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = casc.v0radius(); + } counter += 2; } cut = candidateSelectionValues.lambdaMassWin; histos.fill(HIST("hCutValue"), 9, cut); - if (std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > candidateSelectionValues.lambdaMassWin) + if (std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > candidateSelectionValues.lambdaMassWin) { return false; + } histos.fill(HIST("hCandidate"), ++counter); cut = candidateSelectionValues.bachBaryonDCAxyToPV; @@ -963,61 +1110,62 @@ struct Derivedcascadeanalysis { } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = casc.bachBaryonDCAxyToPV(); - ++counter; - } - - if (candidateSelectionFlags.doV0CosPaCut) { - if (!isCosPAAccepted(casc, casc.x(), casc.y(), casc.z(), candidateSelectionFlags.doPtDepV0CosPaCut, false)) - return false; - histos.fill(HIST("hCandidate"), ++counter); - } else { - if (qaFlags.doPtDepCutStudy) - selectionCheck = std::acos(casc.v0cosPA(casc.x(), casc.y(), casc.z())); + } ++counter; } cut = candidateSelectionValues.rejcomp; - histos.fill(HIST("hCutValue"), 12, cut); + histos.fill(HIST("hCutValue"), 11, cut); if (isXi) { - if (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) < candidateSelectionValues.rejcomp) + if (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) < candidateSelectionValues.rejcomp) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) < candidateSelectionValues.rejcomp) + if (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) < candidateSelectionValues.rejcomp) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } cut = candidateSelectionValues.dcaBachToPV; - histos.fill(HIST("hCutValue"), 13, cut); + histos.fill(HIST("hCutValue"), 12, cut); cut = candidateSelectionValues.dcaMesonToPV; - histos.fill(HIST("hCutValue"), 13, cut); + histos.fill(HIST("hCutValue"), 12, cut); cut = candidateSelectionValues.dcaBaryonToPV; - histos.fill(HIST("hCutValue"), 13, cut); + histos.fill(HIST("hCutValue"), 12, cut); if (candidateSelectionFlags.doDCAbachToPVCut || candidateSelectionFlags.doDCAmesonToPVCut || candidateSelectionFlags.doDCAbaryonToPVCut) { - if (candidateSelectionFlags.doDCAbachToPVCut && std::abs(casc.dcabachtopv()) < candidateSelectionValues.dcaBachToPV) + if (candidateSelectionFlags.doDCAbachToPVCut && std::abs(casc.dcabachtopv()) < candidateSelectionValues.dcaBachToPV) { return false; - if (casc.sign() > 0 && ((std::abs(casc.dcanegtopv()) < candidateSelectionValues.dcaBaryonToPV && candidateSelectionFlags.doDCAbaryonToPVCut) || (std::abs(casc.dcapostopv()) < candidateSelectionValues.dcaMesonToPV && candidateSelectionFlags.doDCAmesonToPVCut))) + } + if (casc.sign() > 0 && ((std::abs(casc.dcanegtopv()) < candidateSelectionValues.dcaBaryonToPV && candidateSelectionFlags.doDCAbaryonToPVCut) || (std::abs(casc.dcapostopv()) < candidateSelectionValues.dcaMesonToPV && candidateSelectionFlags.doDCAmesonToPVCut))) { return false; - if (casc.sign() < 0 && ((std::abs(casc.dcapostopv()) < candidateSelectionValues.dcaBaryonToPV && candidateSelectionFlags.doDCAbaryonToPVCut) || (std::abs(casc.dcanegtopv()) < candidateSelectionValues.dcaMesonToPV && candidateSelectionFlags.doDCAmesonToPVCut))) + } + if (casc.sign() < 0 && ((std::abs(casc.dcapostopv()) < candidateSelectionValues.dcaBaryonToPV && candidateSelectionFlags.doDCAbaryonToPVCut) || (std::abs(casc.dcanegtopv()) < candidateSelectionValues.dcaMesonToPV && candidateSelectionFlags.doDCAmesonToPVCut))) { return false; + } histos.fill(HIST("hCandidate"), ++counter); } else { if (qaFlags.doPtDepCutStudy) { - if (!candidateSelectionFlags.doDCAbachToPVCut) + if (!candidateSelectionFlags.doDCAbachToPVCut) { selectionCheck = std::abs(casc.dcabachtopv()); - if (!candidateSelectionFlags.doDCAbaryonToPVCut && casc.sign() > 0) + } + if (!candidateSelectionFlags.doDCAbaryonToPVCut && casc.sign() > 0) { selectionCheck = std::abs(casc.dcanegtopv()); - if (!candidateSelectionFlags.doDCAbaryonToPVCut && casc.sign() < 0) + } + if (!candidateSelectionFlags.doDCAbaryonToPVCut && casc.sign() < 0) { selectionCheck = std::abs(casc.dcapostopv()); - if (!candidateSelectionFlags.doDCAmesonToPVCut && casc.sign() > 0) + } + if (!candidateSelectionFlags.doDCAmesonToPVCut && casc.sign() > 0) { selectionCheck = std::abs(casc.dcapostopv()); - if (!candidateSelectionFlags.doDCAmesonToPVCut && casc.sign() < 0) + } + if (!candidateSelectionFlags.doDCAmesonToPVCut && casc.sign() < 0) { selectionCheck = std::abs(casc.dcanegtopv()); + } } - ++counter; + counter += 1; } return true; @@ -1026,11 +1174,12 @@ struct Derivedcascadeanalysis { { static_for<0, 9>([&](auto i) { constexpr int In = i.value; - if (centrality < kCentralityIntervals[In + 1] && centrality > kCentralityIntervals[In]) { - if (isPos) - histos.fill(HIST("InvMassAfterSelCent") + HIST(kCentIndex[In]) + HIST("/hPositiveCascade"), pt, mass, occup); - else - histos.fill(HIST("InvMassAfterSelCent") + HIST(kCentIndex[In]) + HIST("/hNegativeCascade"), pt, mass, occup); + if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { + if (isPos) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascade"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascade"), pt, mass, occup); + } } }); } @@ -1038,11 +1187,12 @@ struct Derivedcascadeanalysis { { static_for<0, 9>([&](auto i) { constexpr int In = i.value; - if (centrality < kCentralityIntervals[In + 1] && centrality > kCentralityIntervals[In]) { - if (isPos) - histos.fill(HIST("InvMassAfterSelCent") + HIST(kCentIndex[In]) + HIST("/hPositiveCascadeIR"), pt, mass, ir); - else - histos.fill(HIST("InvMassAfterSelCent") + HIST(kCentIndex[In]) + HIST("/hNegativeCascadeIR"), pt, mass, ir); + if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { + if (isPos) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascadeIR"), pt, mass, ir); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascadeIR"), pt, mass, ir); + } } }); } @@ -1050,11 +1200,25 @@ struct Derivedcascadeanalysis { { static_for<0, 9>([&](auto i) { constexpr int In = i.value; - if (centrality < kCentralityIntervals[In + 1] && centrality > kCentralityIntervals[In]) { - if (isPos) - histos.fill(HIST("InvMassAfterSelCent") + HIST(kCentIndex[In]) + HIST("/hPositiveCascadeMCTruthIR"), pt, mass, ir); - else - histos.fill(HIST("InvMassAfterSelCent") + HIST(kCentIndex[In]) + HIST("/hNegativeCascadeMCTruthIR"), pt, mass, ir); + if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { + if (isPos) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascadeMCTruthIR"), pt, mass, ir); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascadeMCTruthIR"), pt, mass, ir); + } + } + }); + } + void fillHistOccupancyCheckMC(double pt, double mass, float occup, float centrality, bool isPos) + { + static_for<0, 9>([&](auto i) { + constexpr int In = i.value; + if (centrality < CentralityIntervals[In + 1] && centrality > CentralityIntervals[In]) { + if (isPos) { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hPositiveCascadeMCTruth"), pt, mass, occup); + } else { + histos.fill(HIST("InvMassAfterSelCent") + HIST(CentIndex[In]) + HIST("/hNegativeCascadeMCTruth"), pt, mass, occup); + } } }); } @@ -1063,10 +1227,11 @@ struct Derivedcascadeanalysis { static_for<0, 11>([&](auto i) { constexpr int In = i.value; if (TESTBIT(selectionCheckMask, In)) { - if (isPos) - histos.fill(HIST("PtDepCutStudy/hPositive") + HIST(kSelectionNames[In]), pt, mass, sel); - else - histos.fill(HIST("PtDepCutStudy/hNegative") + HIST(kSelectionNames[In]), pt, mass, sel); + if (isPos) { + histos.fill(HIST("PtDepCutStudy/hPositive") + HIST(SelectionNames[In]), pt, mass, sel); + } else { + histos.fill(HIST("PtDepCutStudy/hNegative") + HIST(SelectionNames[In]), pt, mass, sel); + } } }); } @@ -1075,10 +1240,11 @@ struct Derivedcascadeanalysis { static_for<0, 11>([&](auto i) { constexpr int In = i.value; if (TESTBIT(selectionCheckMask, In)) { - if (isPos) - histos.fill(HIST("PtDepCutStudyMCTruth/hPositive") + HIST(kSelectionNames[In]), pt, mass, sel); - else - histos.fill(HIST("PtDepCutStudyMCTruth/hNegative") + HIST(kSelectionNames[In]), pt, mass, sel); + if (isPos) { + histos.fill(HIST("PtDepCutStudyMCTruth/hPositive") + HIST(SelectionNames[In]), pt, mass, sel); + } else { + histos.fill(HIST("PtDepCutStudyMCTruth/hNegative") + HIST(SelectionNames[In]), pt, mass, sel); + } } }); } @@ -1086,21 +1252,29 @@ struct Derivedcascadeanalysis { void analyseCascades(TCollision const& coll, TCascade const& Cascades) //(soa::Join::iterator const& coll, soa::Join const& Cascades, DauTracks const&)//(TCollision const& coll, TCascade const& Cascades) { - if (!isEventAccepted(coll, true)) + if (!isEventAccepted(coll, true)) { return; + } float centrality = -1; float nChEta1 = -1; float occupancy = -2; + if (candidateSelectionFlags.useInterpolationForCosPACut) { + initCosPASelectionHistograms(coll.timestamp()); + } + if constexpr (requires { coll.centFT0C(); }) { nChEta1 = coll.multNTracksPVeta1(); centrality = coll.centFT0C(); - if (useCentralityFT0M) + if (useCentralityFT0M) { centrality = coll.centFT0M(); - if (useCentralityFT0A) + } + if (useCentralityFT0A) { centrality = coll.centFV0A(); - if (useCentralityFT0Cvar1) + } + if (useCentralityFT0Cvar1) { centrality = coll.centFT0CVariant1(); + } occupancy = useTrackOccupancyDef ? coll.trackOccupancyInTimeRange() : coll.ft0cOccupancyInTimeRange(); } else { centrality = eventSelectionRun2Flags.useSPDTrackletsCent ? coll.centRun2SPDTracklets() : coll.centRun2V0M(); @@ -1115,21 +1289,26 @@ struct Derivedcascadeanalysis { bool isNegative = false; bool isPositive = false; - if (casc.sign() > 0) + if (casc.sign() > 0) { isPositive = true; - if (casc.sign() < 0) + } + if (casc.sign() < 0) { isNegative = true; - if (!isNegative && !isPositive) + } + if (!isNegative && !isPositive) { continue; + } double invmass = -10; invmass = isXi ? casc.mXi() : casc.mOmega(); // To have trace of how it was before selections if (qaFlags.doBefSelCheck) { - if (isPositive) + if (isPositive) { histos.fill(HIST("InvMassBefSel/hPositiveCascade"), recoPt, invmass, centrality); - if (isNegative) + } + if (isNegative) { histos.fill(HIST("InvMassBefSel/hNegativeCascade"), recoPt, invmass, centrality); + } } bool isTrueMCCascade = false; @@ -1139,17 +1318,21 @@ struct Derivedcascadeanalysis { // MC part if constexpr (requires { casc.has_cascMCCore(); }) { - if (!casc.has_cascMCCore()) + if (!casc.has_cascMCCore()) { continue; + } auto cascMC = casc.template cascMCCore_as>(); ptmc = RecoDecay::sqrtSumOfSquares(cascMC.pxMC(), cascMC.pyMC()); - if (cascMC.isPhysicalPrimary() && ((isXi && std::abs(cascMC.pdgCode()) == PDG_t::kXiMinus) || (!isXi && std::abs(cascMC.pdgCode()) == PDG_t::kOmegaMinus))) + if (cascMC.isPhysicalPrimary() && ((isXi && std::abs(cascMC.pdgCode()) == PDG_t::kXiMinus) || (!isXi && std::abs(cascMC.pdgCode()) == PDG_t::kOmegaMinus))) { isTrueMCCascade = true; - if (isTrueMCCascade && ((isPositive && cascMC.pdgCodePositive() == PDG_t::kPiPlus && cascMC.pdgCodeNegative() == PDG_t::kProtonBar) || (isNegative && cascMC.pdgCodePositive() == PDG_t::kProton && cascMC.pdgCodeNegative() == PDG_t::kPiMinus))) + } + if (isTrueMCCascade && ((isPositive && cascMC.pdgCodePositive() == PDG_t::kPiPlus && cascMC.pdgCodeNegative() == PDG_t::kProtonBar) || (isNegative && cascMC.pdgCodePositive() == PDG_t::kProton && cascMC.pdgCodeNegative() == PDG_t::kPiMinus))) { isCorrectLambdaDecay = true; - if (isTrueMCCascade && isCorrectLambdaDecay && ((isXi && std::abs(cascMC.pdgCodeBachelor()) == PDG_t::kPiPlus) || (!isXi && std::abs(cascMC.pdgCodeBachelor()) == PDG_t::kKPlus))) + } + if (isTrueMCCascade && isCorrectLambdaDecay && ((isXi && std::abs(cascMC.pdgCodeBachelor()) == PDG_t::kPiPlus) || (!isXi && std::abs(cascMC.pdgCodeBachelor()) == PDG_t::kKPlus))) { isTrueMCCascadeDecay = true; + } if (qaFlags.doBefSelCheck && isTrueMCCascade) { if (isPositive) { @@ -1163,9 +1346,10 @@ struct Derivedcascadeanalysis { } } - if (!isCascadeCandidateAccepted(casc, counter, centrality)) + if (!isCascadeCandidateAccepted(casc, counter, centrality)) { continue; - counter += 13; + } + counter += 12; auto negExtra = casc.template negTrackExtra_as>(); auto posExtra = casc.template posTrackExtra_as>(); @@ -1181,69 +1365,96 @@ struct Derivedcascadeanalysis { float cut = candidateSelectionValues.etaDauCut; histos.fill(HIST("hCutValue"), counter + 1, cut); - if (std::abs(poseta) > candidateSelectionValues.etaDauCut || std::abs(negeta) > candidateSelectionValues.etaDauCut || std::abs(bacheta) > candidateSelectionValues.etaDauCut) + if (std::abs(poseta) > candidateSelectionValues.etaDauCut || std::abs(negeta) > candidateSelectionValues.etaDauCut || std::abs(bacheta) > candidateSelectionValues.etaDauCut) { continue; + } histos.fill(HIST("hCandidate"), ++counter); histos.fill(HIST("hPseudorapPosDaughter"), poseta); histos.fill(HIST("hPseudorapNegDaughter"), negeta); histos.fill(HIST("hPseudorapBachelor"), bacheta); + if (candidateSelectionFlags.doV0CosPaCut) { + if (!isCosPAAccepted(casc, coll.posX(), coll.posY(), coll.posZ(), candidateSelectionFlags.doPtDepV0CosPaCut, false)) { + continue; + } + histos.fill(HIST("hCandidate"), ++counter); + } else { + if (qaFlags.doPtDepCutStudy) { + selectionCheck = std::acos(casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); + } + ++counter; + } + if (candidateSelectionFlags.doCascadeCosPaCut) { - if (!isCosPAAccepted(casc, coll.posX(), coll.posY(), coll.posZ(), candidateSelectionFlags.doPtDepCosPaCut, true)) + if (!isCosPAAccepted(casc, coll.posX(), coll.posY(), coll.posZ(), candidateSelectionFlags.doPtDepCosPaCut, true)) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = std::acos(casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); + } ++counter; } cut = candidateSelectionValues.dcaV0ToPV; histos.fill(HIST("hCutValue"), 16, cut); if (candidateSelectionFlags.doDCAV0ToPVCut) { - if (std::abs(casc.dcav0topv(coll.posX(), coll.posY(), coll.posZ())) < candidateSelectionValues.dcaV0ToPV) + if (std::abs(casc.dcav0topv(coll.posX(), coll.posY(), coll.posZ())) < candidateSelectionValues.dcaV0ToPV) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = std::abs(casc.dcav0topv(coll.posX(), coll.posY(), coll.posZ())); + } ++counter; } if (isNegative) { - if (qaFlags.doFillNsigmaTPCHistProton) + if (qaFlags.doFillNsigmaTPCHistProton) { histos.fill(HIST("hNsigmaProton"), posExtra.tpcNSigmaPr(), fullMomentumPosDaugh, centrality); - if (qaFlags.doFillNsigmaTPCHistV0Pion) + } + if (qaFlags.doFillNsigmaTPCHistV0Pion) { histos.fill(HIST("hNsigmaPionNeg"), negExtra.tpcNSigmaPi(), fullmomentumNegDaugh, centrality); - if (qaFlags.doFillNsigmaTPCHistPionBach && isXi) + } + if (qaFlags.doFillNsigmaTPCHistPionBach && isXi) { histos.fill(HIST("hNsigmaPionNegBach"), bachExtra.tpcNSigmaPi(), fullmomentumBachelor, centrality); - if (qaFlags.doFillNsigmaTPCHistPionBach && !isXi) + } + if (qaFlags.doFillNsigmaTPCHistPionBach && !isXi) { histos.fill(HIST("hNsigmaKaon"), bachExtra.tpcNSigmaPi(), fullmomentumBachelor, centrality); + } } else { - if (qaFlags.doFillNsigmaTPCHistV0Pion) + if (qaFlags.doFillNsigmaTPCHistV0Pion) { histos.fill(HIST("hNsigmaPionPos"), posExtra.tpcNSigmaPi(), fullMomentumPosDaugh, centrality); - if (qaFlags.doFillNsigmaTPCHistProton) + } + if (qaFlags.doFillNsigmaTPCHistProton) { histos.fill(HIST("hNsigmaProtonNeg"), negExtra.tpcNSigmaPr(), fullmomentumNegDaugh, centrality); - if (qaFlags.doFillNsigmaTPCHistPionBach && isXi) + } + if (qaFlags.doFillNsigmaTPCHistPionBach && isXi) { histos.fill(HIST("hNsigmaPionPosBach"), bachExtra.tpcNSigmaPi(), fullmomentumBachelor, centrality); - if (qaFlags.doFillNsigmaTPCHistPionBach && !isXi) + } + if (qaFlags.doFillNsigmaTPCHistPionBach && !isXi) { histos.fill(HIST("hNsigmaKaon"), bachExtra.tpcNSigmaPi(), fullmomentumBachelor, centrality); + } } if (isNegative) { if (candidateSelectionFlags.doNTPCSigmaCut) { - if (std::abs(posExtra.tpcNSigmaPr()) > candidateSelectionValues.nsigmatpcPr || std::abs(negExtra.tpcNSigmaPi()) > candidateSelectionValues.nsigmatpcPi) + if (std::abs(posExtra.tpcNSigmaPr()) > candidateSelectionValues.nsigmatpcPr || std::abs(negExtra.tpcNSigmaPi()) > candidateSelectionValues.nsigmatpcPi) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { ++counter; } } else { if (candidateSelectionFlags.doNTPCSigmaCut) { - if (std::abs(posExtra.tpcNSigmaPi()) > candidateSelectionValues.nsigmatpcPi || std::abs(negExtra.tpcNSigmaPr()) > candidateSelectionValues.nsigmatpcPr) + if (std::abs(posExtra.tpcNSigmaPi()) > candidateSelectionValues.nsigmatpcPi || std::abs(negExtra.tpcNSigmaPr()) > candidateSelectionValues.nsigmatpcPr) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { ++counter; @@ -1263,41 +1474,53 @@ struct Derivedcascadeanalysis { histos.fill(HIST("histITSTPCmatchNegTrack"), ptNegDaugh, centrality, 0.5); histos.fill(HIST("histITSTPCmatchBachTrack"), ptBachelor, centrality, 0.5); if (candidateSelectionFlags.doAllTracksMinITSClusters) { - if (posExtra.hasITS() && posExtra.itsNCls() >= candidateSelectionValues.minITSclusters) + if (posExtra.hasITS() && posExtra.itsNCls() >= candidateSelectionValues.minITSclusters) { histos.fill(HIST("histITSTPCmatchPosTrack"), ptPosDaugh, centrality, 1.5); - if (negExtra.hasITS() && negExtra.itsNCls() >= candidateSelectionValues.minITSclusters) + } + if (negExtra.hasITS() && negExtra.itsNCls() >= candidateSelectionValues.minITSclusters) { histos.fill(HIST("histITSTPCmatchNegTrack"), ptNegDaugh, centrality, 1.5); - if (bachExtra.hasITS() && bachExtra.itsNCls() >= candidateSelectionValues.minITSclusters) + } + if (bachExtra.hasITS() && bachExtra.itsNCls() >= candidateSelectionValues.minITSclusters) { histos.fill(HIST("histITSTPCmatchBachTrack"), ptBachelor, centrality, 1.5); - if (posExtra.hasITS() && posExtra.itsNCls() >= candidateSelectionValues.minITSclusters && posExtra.hasTPC() && std::abs(posExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) + } + if (posExtra.hasITS() && posExtra.itsNCls() >= candidateSelectionValues.minITSclusters && posExtra.hasTPC() && std::abs(posExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) { histos.fill(HIST("histITSTPCmatchPosTrack"), ptPosDaugh, centrality, 2.5); - if (negExtra.hasITS() && negExtra.itsNCls() >= candidateSelectionValues.minITSclusters && negExtra.hasTPC() && std::abs(negExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) + } + if (negExtra.hasITS() && negExtra.itsNCls() >= candidateSelectionValues.minITSclusters && negExtra.hasTPC() && std::abs(negExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) { histos.fill(HIST("histITSTPCmatchNegTrack"), ptNegDaugh, centrality, 2.5); - if (bachExtra.hasITS() && bachExtra.itsNCls() >= candidateSelectionValues.minITSclusters && bachExtra.hasTPC() && std::abs(bachExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) + } + if (bachExtra.hasITS() && bachExtra.itsNCls() >= candidateSelectionValues.minITSclusters && bachExtra.hasTPC() && std::abs(bachExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) { histos.fill(HIST("histITSTPCmatchBachTrack"), ptBachelor, centrality, 2.5); + } } else { - if (posExtra.hasTPC() && std::abs(posExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) + if (posExtra.hasTPC() && std::abs(posExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) { histos.fill(HIST("histITSTPCmatchPosTrack"), ptPosDaugh, centrality, 2.5); - if (negExtra.hasTPC() && std::abs(negExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) + } + if (negExtra.hasTPC() && std::abs(negExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) { histos.fill(HIST("histITSTPCmatchNegTrack"), ptNegDaugh, centrality, 2.5); - if (bachExtra.hasTPC() && std::abs(bachExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) + } + if (bachExtra.hasTPC() && std::abs(bachExtra.tpcCrossedRows()) >= candidateSelectionValues.mintpccrrows) { histos.fill(HIST("histITSTPCmatchBachTrack"), ptBachelor, centrality, 2.5); + } } } - if (std::abs(posExtra.tpcCrossedRows()) < candidateSelectionValues.mintpccrrows || std::abs(negExtra.tpcCrossedRows()) < candidateSelectionValues.mintpccrrows || std::abs(bachExtra.tpcCrossedRows()) < candidateSelectionValues.mintpccrrows) + if (std::abs(posExtra.tpcCrossedRows()) < candidateSelectionValues.mintpccrrows || std::abs(negExtra.tpcCrossedRows()) < candidateSelectionValues.mintpccrrows || std::abs(bachExtra.tpcCrossedRows()) < candidateSelectionValues.mintpccrrows) { continue; + } histos.fill(HIST("hCandidate"), ++counter); bool kHasTOF = (posExtra.hasTOF() || negExtra.hasTOF() || bachExtra.hasTOF()); bool kHasITS = (posExtra.hasITS() || negExtra.hasITS() || bachExtra.hasITS()); - if (candidateSelectionFlags.dooobrej == 1) { - if (!kHasTOF && !kHasITS) + if (candidateSelectionFlags.dooobrej && !candidateSelectionFlags.dooobrejPtThrTOF) { + if (!kHasTOF && !kHasITS) { continue; + } histos.fill(HIST("hCandidate"), ++counter); - } else if (candidateSelectionFlags.dooobrej == 2) { - if (!kHasTOF && (casc.pt() > candidateSelectionValues.ptthrtof)) + } else if (!candidateSelectionFlags.dooobrej && candidateSelectionFlags.dooobrejPtThrTOF) { + if (!kHasTOF && (casc.pt() > candidateSelectionValues.ptthrtof)) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { ++counter; @@ -1314,13 +1537,15 @@ struct Derivedcascadeanalysis { if (candidateSelectionFlags.doNTOFSigmaProtonCut && casc.sign() < 0) { histos.fill(HIST("hNsigmaTOFProton"), casc.tofNSigmaXiLaPr(), fullMomentumPosDaugh, centrality); - if (std::abs(casc.tofNSigmaXiLaPr()) > candidateSelectionValues.nsigmatofPr && fullMomentumPosDaugh > 0.6) + if (std::abs(casc.tofNSigmaXiLaPr()) > candidateSelectionValues.nsigmatofPr && fullMomentumPosDaugh > candidateSelectionValues.ptthrnSigmaTof) { continue; + } } if (candidateSelectionFlags.doNTOFSigmaV0PionCut && casc.sign() > 0) { histos.fill(HIST("hNsigmaTOFV0Pion"), casc.tofNSigmaXiLaPi(), fullMomentumPosDaugh, centrality); - if (std::abs(casc.tofNSigmaXiLaPi()) > candidateSelectionValues.nsigmatofPion) + if (std::abs(casc.tofNSigmaXiLaPi()) > candidateSelectionValues.nsigmatofPion) { continue; + } } } @@ -1328,34 +1553,34 @@ struct Derivedcascadeanalysis { if (candidateSelectionFlags.doNTOFSigmaProtonCut && casc.sign() > 0) { histos.fill(HIST("hNsigmaTOFProton"), casc.tofNSigmaXiLaPr(), fullmomentumNegDaugh, centrality); - if (std::abs(casc.tofNSigmaXiLaPr()) > candidateSelectionValues.nsigmatofPr && fullmomentumNegDaugh > 0.6) + if (std::abs(casc.tofNSigmaXiLaPr()) > candidateSelectionValues.nsigmatofPr && fullmomentumNegDaugh > candidateSelectionValues.ptthrnSigmaTof) { continue; + } } if (candidateSelectionFlags.doNTOFSigmaV0PionCut && casc.sign() < 0) { histos.fill(HIST("hNsigmaTOFV0Pion"), casc.tofNSigmaXiLaPi(), fullmomentumNegDaugh, centrality); - if (std::abs(casc.tofNSigmaXiLaPi()) > candidateSelectionValues.nsigmatofPion) + if (std::abs(casc.tofNSigmaXiLaPi()) > candidateSelectionValues.nsigmatofPion) { continue; + } } } - if (candidateSelectionFlags.doAtLeastOneTrackAB) { - if (bachExtra.hasITSTracker() && negExtra.hasITSTracker() && posExtra.hasITSTracker()) - continue; + if (candidateSelectionFlags.doAtLeastOneTrackAB && bachExtra.hasITSTracker() && negExtra.hasITSTracker() && posExtra.hasITSTracker()) { + continue; } - if (candidateSelectionFlags.doBachelorITSTracking) { - if (!bachExtra.hasITSTracker()) - continue; + if (candidateSelectionFlags.doBachelorITSTracking && !bachExtra.hasITSTracker()) { + continue; } - if (candidateSelectionFlags.doAllTracksMinITSClusters) { - if (bachExtra.itsNCls() < candidateSelectionValues.minITSclusters || posExtra.itsNCls() < candidateSelectionValues.minITSclusters || negExtra.itsNCls() < candidateSelectionValues.minITSclusters) - continue; + if (candidateSelectionFlags.doAllTracksMinITSClusters && (bachExtra.itsNCls() < candidateSelectionValues.minITSclusters || posExtra.itsNCls() < candidateSelectionValues.minITSclusters || negExtra.itsNCls() < candidateSelectionValues.minITSclusters)) { + continue; } if (isXi) { if (candidateSelectionFlags.doNTPCSigmaCut) { - if (std::abs(bachExtra.tpcNSigmaPi()) > candidateSelectionValues.nsigmatpcPi) + if (std::abs(bachExtra.tpcNSigmaPi()) > candidateSelectionValues.nsigmatpcPi) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { ++counter; @@ -1363,24 +1588,28 @@ struct Derivedcascadeanalysis { if (bachExtra.hasTOF() && candidateSelectionFlags.doNTOFSigmaBachelorCut) { histos.fill(HIST("hNsigmaTOFBachelorPion"), casc.tofNSigmaXiPi(), fullmomentumBachelor, centrality); - if (std::abs(casc.tofNSigmaXiPi()) > candidateSelectionValues.nsigmatofBachPion) + if (std::abs(casc.tofNSigmaXiPi()) > candidateSelectionValues.nsigmatofBachPion) { continue; + } } - ctau = o2::constants::physics::MassXiMinus * cascpos / ((cascptotmom + 1e-13) * ctauxiPDG); + ctau = o2::constants::physics::MassXiMinus * cascpos / ((cascptotmom + 1e-13) * ctauxi); if (candidateSelectionFlags.doProperLifeTimeCut) { - if (ctau > candidateSelectionValues.proplifetime) + if (ctau > candidateSelectionValues.proplifetime) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = ctau; + } ++counter; } } else { if (candidateSelectionFlags.doNTPCSigmaCut) { - if (std::abs(bachExtra.tpcNSigmaKa()) > candidateSelectionValues.nsigmatpcKa) + if (std::abs(bachExtra.tpcNSigmaKa()) > candidateSelectionValues.nsigmatpcKa) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { ++counter; @@ -1388,24 +1617,30 @@ struct Derivedcascadeanalysis { if (bachExtra.hasTOF() && candidateSelectionFlags.doNTOFSigmaBachelorCut) { histos.fill(HIST("hNsigmaTOFBachelorKaon"), casc.tofNSigmaOmKa(), std::sqrt(std::pow(casc.pxbach(), 2) + std::pow(casc.pybach(), 2) + std::pow(casc.pzbach(), 2)), centrality); - if (std::abs(casc.tofNSigmaOmKa()) > candidateSelectionValues.nsigmatofBachKaon) + if (std::abs(casc.tofNSigmaOmKa()) > candidateSelectionValues.nsigmatofBachKaon) { continue; + } } - ctau = o2::constants::physics::MassOmegaMinus * cascpos / ((cascptotmom + 1e-13) * ctauomegaPDG); + ctau = o2::constants::physics::MassOmegaMinus * cascpos / ((cascptotmom + 1e-13) * ctauomega); if (candidateSelectionFlags.doProperLifeTimeCut) { - if (ctau > candidateSelectionValues.proplifetime) + if (ctau > candidateSelectionValues.proplifetime) { continue; + } histos.fill(HIST("hCandidate"), ++counter); } else { - if (qaFlags.doPtDepCutStudy) + if (qaFlags.doPtDepCutStudy) { selectionCheck = ctau; + } ++counter; } } if (qaFlags.doOccupancyCheck) { fillHistOccupancyCheck(recoPt, invmass, occupancy, centrality, isPositive); + if (isTrueMCCascadeDecay) { + fillHistOccupancyCheckMC(ptmc, invmass, occupancy, centrality, isPositive); + } } if (isPositive) { histos.fill(HIST("InvMassAfterSel/hPositiveCascade"), recoPt, invmass, centrality); @@ -1413,16 +1648,18 @@ struct Derivedcascadeanalysis { histos.fill(HIST("hPositiveCascadePtForEfficiency"), ptmc, invmass, centrality); histos.fill(HIST("hPositiveCascadePtForEfficiencyVsNch"), ptmc, invmass, nChEta1); } - if (isTrueMCCascadeDecay) + if (isTrueMCCascadeDecay) { histos.fill(HIST("InvMassAfterSelMCrecTruth/hPositiveCascade"), ptmc, invmass, centrality); + } } else { histos.fill(HIST("InvMassAfterSel/hNegativeCascade"), recoPt, invmass, centrality); if (isTrueMCCascadeDecay) { histos.fill(HIST("hNegativeCascadePtForEfficiency"), ptmc, invmass, centrality); histos.fill(HIST("hNegativeCascadePtForEfficiencyVsNch"), ptmc, invmass, nChEta1); } - if (isTrueMCCascadeDecay) + if (isTrueMCCascadeDecay) { histos.fill(HIST("InvMassAfterSelMCrecTruth/hNegativeCascade"), ptmc, invmass, centrality); + } } if (qaFlags.doIRCheck) { @@ -1430,15 +1667,17 @@ struct Derivedcascadeanalysis { if constexpr (requires { coll.trackOccupancyInTimeRange(); }) { interactionRate = rateFetcher.fetch(ccdb.service, coll.timestamp(), coll.runNumber(), irSource) * 1.e-3; fillHistIRCheckData(recoPt, invmass, interactionRate, centrality, isPositive); - if (isTrueMCCascadeDecay) + if (isTrueMCCascadeDecay) { fillHistIRCheckMC(ptmc, invmass, interactionRate, centrality, isPositive); + } } } if (qaFlags.doPtDepCutStudy && selectionCheck != -1) { fillHistPtDepSelectionStudy(recoPt, invmass, selectionCheck, selectionCheckMask, isPositive); - if (isTrueMCCascade) + if (isTrueMCCascade) { fillHistPtDepSelectionStudyMC(ptmc, invmass, selectionCheck, selectionCheckMask, isPositive); + } } } } @@ -1449,23 +1688,28 @@ struct Derivedcascadeanalysis { std::vector listBestCollisionIdx = fillGenEventHist(mcCollisions, collisions); for (auto const& cascMC : Cascades) { - if (!cascMC.has_straMCCollision()) + if (!cascMC.has_straMCCollision()) { continue; + } - if (!cascMC.isPhysicalPrimary()) + if (!cascMC.isPhysicalPrimary()) { continue; + } float ptmc = RecoDecay::sqrtSumOfSquares(cascMC.pxMC(), cascMC.pyMC()); float ymc = 1e3; - if (std::abs(cascMC.pdgCode()) == PDG_t::kXiMinus) + if (std::abs(cascMC.pdgCode()) == PDG_t::kXiMinus) { ymc = RecoDecay::y(std::array{cascMC.pxMC(), cascMC.pyMC(), cascMC.pzMC()}, o2::constants::physics::MassXiMinus); - else if (std::abs(cascMC.pdgCode()) == PDG_t::kOmegaMinus) + } else if (std::abs(cascMC.pdgCode()) == PDG_t::kOmegaMinus) { ymc = RecoDecay::y(std::array{cascMC.pxMC(), cascMC.pyMC(), cascMC.pzMC()}, o2::constants::physics::MassOmegaMinus); + } - if (ispO && (ymc > candidateSelectionValues.maxRapCut || ymc < candidateSelectionValues.minRapCut)) + if (ispO && (ymc > candidateSelectionValues.maxRapCut || ymc < candidateSelectionValues.minRapCut)) { continue; - else if (std::abs(ymc) > candidateSelectionValues.rapCut) + } + if (!ispO && std::abs(ymc) > candidateSelectionValues.rapCut) { continue; + } auto mcCollision = cascMC.template straMCCollision_as>(); if (eventSelectionCommonFlags.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelectionCommonFlags.zVertexCut) { @@ -1485,16 +1729,21 @@ struct Derivedcascadeanalysis { if constexpr (requires { collision.centFT0C(); }) { intRate = rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3; centrality = collision.centFT0C(); - if (useCentralityFT0M) + if (useCentralityFT0M) { centrality = collision.centFT0M(); - if (useCentralityFT0A) + } + if (useCentralityFT0A) { centrality = collision.centFV0A(); - if (useCentralityFT0Cvar1) + } + if (useCentralityFT0Cvar1) { centrality = collision.centFT0CVariant1(); - if (useTrackOccupancyDef) + } + if (useTrackOccupancyDef) { occupancy = collision.trackOccupancyInTimeRange(); - if (useFT0OccupancyDef) + } + if (useFT0OccupancyDef) { occupancy = collision.ft0cOccupancyInTimeRange(); + } } else { centrality = eventSelectionRun2Flags.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); } @@ -1588,12 +1837,15 @@ struct Derivedcascadeanalysis { if constexpr (requires { collision.centFT0C(); }) { centrality = collision.centFT0C(); intRate = rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3; - if (useCentralityFT0M) + if (useCentralityFT0M) { centrality = collision.centFT0M(); - if (useCentralityFT0A) + } + if (useCentralityFT0A) { centrality = collision.centFV0A(); - if (useCentralityFT0Cvar1) + } + if (useCentralityFT0Cvar1) { centrality = collision.centFT0CVariant1(); + } } else { centrality = eventSelectionRun2Flags.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); } @@ -1623,7 +1875,7 @@ struct Derivedcascadeanalysis { return listBestCollisionIdx; } - void processCascades(soa::Join::iterator const& coll, soa::Join const& Cascades, DauTracks const&) + void processCascades(soa::Join::iterator const& coll, soa::Join const& Cascades, DauTracks const&) { analyseCascades(coll, Cascades); } @@ -1632,7 +1884,7 @@ struct Derivedcascadeanalysis { analyseCascades(coll, Cascades); } - void processCascadesMCrec(soa::Join::iterator const& coll, CascMCCandidates const& Cascades, DauTracks const&, soa::Join const&) + void processCascadesMCrec(soa::Join::iterator const& coll, CascMCCandidates const& Cascades, DauTracks const&, soa::Join const&) { analyseCascades(coll, Cascades); } @@ -1641,7 +1893,7 @@ struct Derivedcascadeanalysis { analyseCascades(coll, Cascades); } - void processCascadesMCforEff(soa::Join const& mcCollisions, soa::Join const& Cascades, soa::Join const& collisions) + void processCascadesMCforEff(soa::Join const& mcCollisions, soa::Join const& Cascades, soa::Join const& collisions) { analyseCascadesMCforEff(mcCollisions, Cascades, collisions); } diff --git a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx index 1d2ccd5f701..49ddabbcc10 100644 --- a/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx @@ -31,6 +31,7 @@ #include "PWGLF/DataModel/LFStrangenessMLTables.h" #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" #include "PWGUD/Core/SGSelector.h" #include "Common/CCDB/EventSelectionParams.h" @@ -59,6 +60,7 @@ #include #include +#include #include #include @@ -143,7 +145,7 @@ struct derivedlambdakzeroanalysis { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - bool isRun3; + bool isRun3 = true; // master analysis switches Configurable analyseK0Short{"analyseK0Short", true, "process K0Short-like candidates"}; @@ -151,10 +153,10 @@ struct derivedlambdakzeroanalysis { Configurable analyseAntiLambda{"analyseAntiLambda", true, "process AntiLambda-like candidates"}; Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "fill feeddown matrix if MC"}; - Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true"}; Configurable irSource{"irSource", "", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; Configurable centralityEstimator{"centralityEstimator", kCentFT0C, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; Configurable doUPCanalysis{"doUPCanalysis", true, "Study V0s in hadronic and UPC collisions"}; + Configurable useMcCentrality{"useMcCentrality", false, "Use MC centrality?"}; Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; @@ -162,6 +164,7 @@ struct derivedlambdakzeroanalysis { Configurable doTOFQA{"doTOFQA", false, "do TOF QA histograms"}; Configurable doDetectPropQA{"doDetectPropQA", 0, "do Detector/ITS map QA: 0: no, 1: 4D, 2: 5D with mass; 3: plain in 3D"}; Configurable doEtaPhiQA{"doEtaPhiQA", false, "do Eta/Phi QA histograms"}; + Configurable doEventMCQA{"doEventMCQA", false, "do MC event QA histograms"}; Configurable fillV0infoTable{"fillV0infoTable", false, "fill V0 info table for TOF matching QA (only filled for candidates passing the topological selection)"}; Configurable doPlainTopoQA{"doPlainTopoQA", true, "do simple 1D QA of candidates"}; @@ -171,9 +174,10 @@ struct derivedlambdakzeroanalysis { // for MC Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; - Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; + Configurable doTreatPiToMuon{"doTreatPiToMuon", true, "Take pi decay into muon into account in MC"}; Configurable doCollisionAssociationQA{"doCollisionAssociationQA", true, "check collision association"}; - Configurable doSecondaryV0s{"doSecondaryV0s", false, "Look at secondary V0s?"}; + Configurable doSecondaryV0s{"doSecondaryV0s", 0, "Look at secondary V0s? 0: No; 1: yes via a loop on V0MCCores; 2: yes via a loop on CascMCCores"}; + Configurable doQAfeeddown{"doQAfeeddown", true, "if MC, fill feeddown QA histograms"}; struct : ConfigurableGroup { std::string prefix = "eventSelections"; // JSON group name @@ -230,7 +234,8 @@ struct derivedlambdakzeroanalysis { Configurable v0TypeSelection{"v0TypeSelection", 1, "select on a certain V0 type (leave negative if no selection desired)"}; // Selection criteria: acceptance - Configurable rapidityCut{"rapidityCut", 0.5, "rapidity"}; + Configurable rapidityMinCut{"rapidityMinCut", -0.5, "min rapidity"}; + Configurable rapidityMaxCut{"rapidityMaxCut", 0.5, "max rapidity"}; Configurable daughterEtaCut{"daughterEtaCut", 0.8, "max eta for daughters"}; // Standard 5 topological criteria @@ -353,8 +358,8 @@ struct derivedlambdakzeroanalysis { o2::ccdb::CcdbApi ccdbApi; Service ccdb; ctpRateFetcher rateFetcher; - int mRunNumber; - float magField; + int mRunNumber = 0.; + float magField = 0.; std::map metadata; o2::parameters::GRPMagField* grpmag = nullptr; @@ -366,11 +371,13 @@ struct derivedlambdakzeroanalysis { ConfigurableAxis axisK0Mass{"axisK0Mass", {200, 0.4f, 0.6f}, ""}; ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, ""}; ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f}, "Centrality"}; + ConfigurableAxis axisCentralityFine{"axisCentralityFine", {101, 0.0f, 101.0f}, "Centrality"}; ConfigurableAxis axisNch{"axisNch", {500, 0.0f, +5000.0f}, "Number of charged particles"}; ConfigurableAxis axisIRBinning{"axisIRBinning", {500, 0, 50}, "Binning for the interaction rate (kHz)"}; ConfigurableAxis axisMultFT0M{"axisMultFT0M", {500, 0.0f, +100000.0f}, "Multiplicity FT0M"}; ConfigurableAxis axisMultFT0C{"axisMultFT0C", {500, 0.0f, +10000.0f}, "Multiplicity FT0C"}; ConfigurableAxis axisMultFV0A{"axisMultFV0A", {500, 0.0f, +100000.0f}, "Multiplicity FV0A"}; + ConfigurableAxis axisRapidity{"axisRapidity", {200, -1.0f, 1.0f}, "rapidity axis for analysis"}; ConfigurableAxis axisRawCentrality{"axisRawCentrality", {VARIABLE_WIDTH, 0.000f, 52.320f, 75.400f, 95.719f, 115.364f, 135.211f, 155.791f, 177.504f, 200.686f, 225.641f, 252.645f, 281.906f, 313.850f, 348.302f, 385.732f, 426.307f, 470.146f, 517.555f, 568.899f, 624.177f, 684.021f, 748.734f, 818.078f, 892.577f, 973.087f, 1058.789f, 1150.915f, 1249.319f, 1354.279f, 1465.979f, 1584.790f, 1710.778f, 1844.863f, 1985.746f, 2134.643f, 2291.610f, 2456.943f, 2630.653f, 2813.959f, 3006.631f, 3207.229f, 3417.641f, 3637.318f, 3865.785f, 4104.997f, 4354.938f, 4615.786f, 4885.335f, 5166.555f, 5458.021f, 5762.584f, 6077.881f, 6406.834f, 6746.435f, 7097.958f, 7462.579f, 7839.165f, 8231.629f, 8635.640f, 9052.000f, 9484.268f, 9929.111f, 10389.350f, 10862.059f, 11352.185f, 11856.823f, 12380.371f, 12920.401f, 13476.971f, 14053.087f, 14646.190f, 15258.426f, 15890.617f, 16544.433f, 17218.024f, 17913.465f, 18631.374f, 19374.983f, 20136.700f, 20927.783f, 21746.796f, 22590.880f, 23465.734f, 24372.274f, 25314.351f, 26290.488f, 27300.899f, 28347.512f, 29436.133f, 30567.840f, 31746.818f, 32982.664f, 34276.329f, 35624.859f, 37042.588f, 38546.609f, 40139.742f, 41837.980f, 43679.429f, 45892.130f, 400000.000f}, "raw centrality signal"}; // for QA @@ -411,6 +418,8 @@ struct derivedlambdakzeroanalysis { // MC coll assoc QA axis ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; + + ConfigurableAxis axisPDGCodeMothers{"axisPDGCodeMothers", {7001, -3500.5, 3500.5}, "PDG code of mother particle"}; } axisConfigurations; // UPC selections @@ -426,7 +435,7 @@ struct derivedlambdakzeroanalysis { // For manual sliceBy // Preslice> perMcCollision = aod::v0data::straMCCollisionId; - PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollision = aod::v0data::straMCCollisionId; PresliceUnsorted> perMcCollisionRun2 = aod::v0data::straMCCollisionId; enum Selection : uint64_t { selCosPA = 0, @@ -478,24 +487,25 @@ struct derivedlambdakzeroanalysis { selPhysPrimAntiLambda, // for mc tagging }; - uint64_t maskTopological; - uint64_t maskTopoNoV0Radius; - uint64_t maskTopoNoDCANegToPV; - uint64_t maskTopoNoDCAPosToPV; - uint64_t maskTopoNoCosPA; - uint64_t maskTopoNoDCAV0Dau; - uint64_t maskTrackProperties; + uint64_t maskTopological = 0; + uint64_t maskTopoNoV0Radius = 0; + uint64_t maskTopoNoDCANegToPV = 0; + uint64_t maskTopoNoDCAPosToPV = 0; + uint64_t maskTopoNoCosPA = 0; + uint64_t maskTopoNoDCAV0Dau = 0; + uint64_t maskTrackProperties = 0; - uint64_t maskK0ShortSpecific; - uint64_t maskLambdaSpecific; - uint64_t maskAntiLambdaSpecific; + uint64_t maskK0ShortSpecific = 0; + uint64_t maskLambdaSpecific = 0; + uint64_t maskAntiLambdaSpecific = 0; - uint64_t maskSelectionK0Short; - uint64_t maskSelectionLambda; - uint64_t maskSelectionAntiLambda; + uint64_t maskSelectionK0Short = 0; + uint64_t maskSelectionLambda = 0; + uint64_t maskSelectionAntiLambda = 0; - uint64_t secondaryMaskSelectionLambda; - uint64_t secondaryMaskSelectionAntiLambda; + uint64_t secondaryMaskSelectionK0Short = 0; + uint64_t secondaryMaskSelectionLambda = 0; + uint64_t secondaryMaskSelectionAntiLambda = 0; void init(InitContext const&) { @@ -706,14 +716,28 @@ struct derivedlambdakzeroanalysis { BITSET(maskSelectionAntiLambda, selPhysPrimAntiLambda); // No primary requirement for feeddown matrix + secondaryMaskSelectionK0Short = maskTopological | maskTrackProperties | maskK0ShortSpecific; secondaryMaskSelectionLambda = maskTopological | maskTrackProperties | maskLambdaSpecific; secondaryMaskSelectionAntiLambda = maskTopological | maskTrackProperties | maskAntiLambdaSpecific; // Initialise the RCTFlagsChecker rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); + // + if (doprocessAnalysedCollisions) { + histos.add("hEventPreSelection", "hEventPreSelection", kTH1D, {{8, -0.5f, +7.5f}}); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(2, "kIsTriggerTVX"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(3, "kNoITSROFrameBorder"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(5, "posZ cut"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(6, "kNoSameBunchPileup"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(7, "RCT flags"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(8, "Preselected collisions"); + } + // Event Counters - histos.add("hEventSelection", "hEventSelection", kTH1D, {{21, -0.5f, +20.5f}}); + histos.add("hEventSelection", "hEventSelection", kTH1D, {{23, -0.5f, +22.5f}}); if (isRun3) { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); @@ -731,16 +755,13 @@ struct derivedlambdakzeroanalysis { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); - } else { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "Below min occup."); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "Above max occup."); - } - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Below min IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "RCT flags"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Below min occup."); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max occup."); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(21, "Below min IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(22, "Above max IR"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(23, "RCT flags"); } else { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); @@ -758,17 +779,18 @@ struct derivedlambdakzeroanalysis { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(14, "kNoV0MOnVsOfPileup"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(15, "kNoSPDOnVsOfPileup"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(16, "kNoSPDClsVsTklBG"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); - } + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(18, "INEL>1"); } - histos.add("hEventCentrality", "hEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); - histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisConfigurations.axisCentralityFine}); + histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisNch}); + if (useMcCentrality) { + histos.add("hEventVsGenCentrality", "hEventVsGenCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisCentralityFine}); + } if (doEventQA) { if (isRun3) { - histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{21, -0.5f, +20.5f}, {101, 0.0f, 101.0f}}); + histos.add("hEventSelectionVsCentrality", "hEventSelectionVsCentrality", kTH2D, {{23, -0.5f, +22.5f}, axisConfigurations.axisCentralityFine}); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(1, "All collisions"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(2, "sel8 cut"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); @@ -785,22 +807,19 @@ struct derivedlambdakzeroanalysis { histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); - if (doPPAnalysis) { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "INEL>1"); - } else { - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "Below min occup."); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "Above max occup."); - } - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "Below min IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max IR"); - histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "RCT flags"); - - histos.add("hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); - histos.add("hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0M}); - histos.add("hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFT0C}); - histos.add("hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); - histos.add("hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisMultFV0A}); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(17, "INEL>0"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(18, "INEL>1"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(19, "Below min occup."); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(20, "Above max occup."); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(21, "Below min IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(22, "Above max IR"); + histos.get(HIST("hEventSelectionVsCentrality"))->GetXaxis()->SetBinLabel(23, "RCT flags"); + + histos.add("hCentralityVsNGlobal", "hCentralityVsNGlobal", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisNch}); + histos.add("hEventCentVsMultFT0M", "hEventCentVsMultFT0M", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisMultFT0M}); + histos.add("hEventCentVsMultFT0C", "hEventCentVsMultFT0C", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisMultFT0C}); + histos.add("hEventCentVsMultNGlobal", "hEventCentVsMultNGlobal", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisNch}); + histos.add("hEventCentVsMultFV0A", "hEventCentVsMultFV0A", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisMultFV0A}); histos.add("hEventMultFT0MvsMultNGlobal", "hEventMultFT0MvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); histos.add("hEventMultFT0CvsMultNGlobal", "hEventMultFT0CvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); histos.add("hEventMultFV0AvsMultNGlobal", "hEventMultFV0AvsMultNGlobal", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); @@ -809,24 +828,31 @@ struct derivedlambdakzeroanalysis { } } + if (doEventMCQA) { + histos.add("hMultFT0MVsMultMC", "hMultFT0MVsMultMC;FT0M amplitude;#it{N}_{ch}(|#it{#eta}|<0.5)", kTH2D, {axisConfigurations.axisMultFT0M, axisConfigurations.axisNch}); + histos.add("hMultFT0CVsMultMC", "hMultFT0CVsMultMC;FT0C amplitude;#it{N}_{ch}(|#it{#eta}|<0.5)", kTH2D, {axisConfigurations.axisMultFT0C, axisConfigurations.axisNch}); + histos.add("hMultNGlobalVsMultMC", "hMultNGlobalVsMultMC;#it{N}_{ch}(global tracks);#it{N}_{ch} (|#it{#eta}|<0.5)", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisNch}); + histos.add("hMultFV0AVsMultMC", "hMultFV0AVsMultMC;FV0A amplitude;#it{N}_{ch}(|#it{#eta}|<0.5)", kTH2D, {axisConfigurations.axisMultFV0A, axisConfigurations.axisNch}); + } + histos.add("hEventPVz", "hEventPVz", kTH1D, {{100, -20.0f, +20.0f}}); - histos.add("hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); + histos.add("hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {axisConfigurations.axisCentralityFine, {100, -20.0f, +20.0f}}); if (doprocessGeneratedRun3 || doprocessGeneratedRun2) { histos.add("hEventPVzMC", "hEventPVzMC", kTH1D, {{100, -20.0f, +20.0f}}); - histos.add("hCentralityVsPVzMC", "hCentralityVsPVzMC", kTH2D, {{101, 0.0f, 101.0f}, {100, -20.0f, +20.0f}}); + histos.add("hCentralityVsPVzMC", "hCentralityVsPVzMC", kTH2D, {axisConfigurations.axisCentralityFine, {100, -20.0f, +20.0f}}); } histos.add("hEventOccupancy", "hEventOccupancy", kTH1D, {axisConfigurations.axisOccupancy}); - histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisOccupancy}); + histos.add("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisOccupancy}); if (doUPCanalysis) { histos.add("hGapSide", "Gap side; Entries", kTH1D, {{5, -0.5, 4.5}}); histos.add("hSelGapSide", "Selected gap side; Entries", kTH1D, {axisConfigurations.axisSelGap}); - histos.add("hEventCentralityVsSelGapSide", ";Centrality (%); Selected gap side", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisSelGap}); + histos.add("hEventCentralityVsSelGapSide", ";Centrality (%); Selected gap side", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisSelGap}); } histos.add("hInteractionRate", "hInteractionRate", kTH1D, {axisConfigurations.axisIRBinning}); - histos.add("hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisIRBinning}); + histos.add("hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisIRBinning}); histos.add("hInteractionRateVsOccupancy", "hInteractionRateVsOccupancy", kTH2D, {axisConfigurations.axisIRBinning, axisConfigurations.axisOccupancy}); @@ -889,6 +915,11 @@ struct derivedlambdakzeroanalysis { hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimAntiLambda + 2, "Phys. prim. #bar{#Lambda}"); hSelectionV0s->GetXaxis()->SetBinLabel(selPhysPrimAntiLambda + 3, "Cand. selected"); + if (doTOFQA) { + histos.add("hCollisionTimes", "hCollisionTimes", kTH1F, {{2000, -1000.0f, 1000.0f}}); + histos.add("hCollisionTimesError", "hCollisionTimesError", kTH1F, {{2000, -1000.0f, 1000.0f}}); + } + // histograms versus mass if (analyseK0Short) { histos.add("h2dNbrOfK0ShortVsCentrality", "h2dNbrOfK0ShortVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); @@ -916,6 +947,8 @@ struct derivedlambdakzeroanalysis { histos.add("K0Short/h3dNegTPCsignalVsTrackPt", "h3dNegTPCsignalVsTrackPt", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTPCsignal}); } if (doTOFQA) { + histos.add("K0Short/h1dPosNsigmaTOF", "h1dPosNsigmaTOF", kTH1D, {axisConfigurations.axisNsigmaTOF}); + histos.add("K0Short/h1dNegNsigmaTOF", "h1dNegNsigmaTOF", kTH1D, {axisConfigurations.axisNsigmaTOF}); histos.add("K0Short/h3dPosNsigmaTOF", "h3dPosNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); histos.add("K0Short/h3dNegNsigmaTOF", "h3dNegNsigmaTOF", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisNsigmaTOF}); histos.add("K0Short/h3dPosTOFdeltaT", "h3dPosTOFdeltaT", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPtCoarse, axisConfigurations.axisTOFdeltaT}); @@ -1134,6 +1167,25 @@ struct derivedlambdakzeroanalysis { } } + if (analyseK0Short && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dK0ShortMothers", "h2dK0ShortMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dK0ShortMothersIsPrimary", "h2dK0ShortMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dK0ShortMothers", "h3dK0ShortMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dK0ShortMothersIsPrimary", "h3dK0ShortMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseLambda && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dLambdaMothers", "h2dLambdaMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dLambdaMothersIsPrimary", "h2dLambdaMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dLambdaMothers", "h3dLambdaMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dLambdaMothersIsPrimary", "h3dLambdaMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseAntiLambda && doQAfeeddown && (doprocessMonteCarloRun3 || doprocessMonteCarloRun2)) { + histos.add("h2dAntiLambdaMothers", "h2dAntiLambdaMothers", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h2dAntiLambdaMothersIsPrimary", "h2dAntiLambdaMothersIsPrimary", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dAntiLambdaMothers", "h3dAntiLambdaMothers", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + histos.add("h3dAntiLambdaMothersIsPrimary", "h3dAntiLambdaMothersIsPrimary", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPDGCodeMothers}); + } + if (analyseK0Short) histos.add("hMassK0Short", "hMassK0Short", kTH1D, {axisConfigurations.axisK0Mass}); if (analyseLambda) @@ -1178,6 +1230,7 @@ struct derivedlambdakzeroanalysis { histos.add("h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); histos.add("h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); histos.add("h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("h2dMassK0ShortVsMassLambda", "h2dMassK0ShortVsMassLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); if (analyseK0Short) { histos.add("K0Short/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); histos.add("K0Short/hNegDCAToPV", "hNegDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); @@ -1188,6 +1241,7 @@ struct derivedlambdakzeroanalysis { histos.add("K0Short/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); histos.add("K0Short/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); histos.add("K0Short/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("K0Short/h2dMassK0ShortVsMassLambda", "h2dMassK0ShortVsMassLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); } if (analyseLambda) { histos.add("Lambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); @@ -1199,6 +1253,7 @@ struct derivedlambdakzeroanalysis { histos.add("Lambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); histos.add("Lambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); histos.add("Lambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("Lambda/h2dMassK0ShortVsMassLambda", "h2dMassK0ShortVsMassLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); } if (analyseAntiLambda) { histos.add("AntiLambda/hPosDCAToPV", "hPosDCAToPV", kTH1D, {axisConfigurations.axisDCAtoPV}); @@ -1210,6 +1265,7 @@ struct derivedlambdakzeroanalysis { histos.add("AntiLambda/h2dNegativeITSvsTPCpts", "h2dNegativeITSvsTPCpts", kTH2D, {axisConfigurations.axisTPCrows, axisConfigurations.axisITSclus}); histos.add("AntiLambda/h2dPositivePtVsPhi", "h2dPositivePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); histos.add("AntiLambda/h2dNegativePtVsPhi", "h2dNegativePtVsPhi", kTH2D, {axisConfigurations.axisPtCoarse, axisConfigurations.axisPhiMod}); + histos.add("AntiLambda/h2dMassK0ShortVsMassLambda", "h2dMassK0ShortVsMassLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); } } @@ -1217,12 +1273,25 @@ struct derivedlambdakzeroanalysis { histos.add("GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); histos.add("GeneralQA/h2dArmenterosSelected", "h2dArmenterosSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { + histos.add("GeneralQA/h2dRapVsRapGen", "h2dRapVsRapGen;Rapidity;Generated rapidity", kTH2D, {axisConfigurations.axisRapidity, axisConfigurations.axisRapidity}); + histos.add("GeneralQA/h2dPtVsPtGen", "h2dPtVsPtGen;#it{p}_{T} (GeV/#it{c});#it{p}_{T}^{MC} (GeV/#it{c})", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPt}); + histos.add("GeneralQA/h3dRapVsRapGen", "h3dRapVsRapGen;Centrality (%);Rapidity;Generated rapidity", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisRapidity, axisConfigurations.axisRapidity}); + histos.add("GeneralQA/h3dPtVsPtGen", "h3dPtVsPtGen;Centrality (%);#it{p}_{T} (GeV/#it{c});#it{p}_{T}^{MC} (GeV/#it{c})", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPt}); + } + // Creation of histograms: MC generated if ((doprocessGeneratedRun3 || doprocessGeneratedRun2)) { - histos.add("hGenEvents", "hGenEvents", kTH2D, {{axisConfigurations.axisNch}, {2, -0.5f, +1.5f}}); - histos.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); - histos.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "Gen. with at least 1 rec. events"); - histos.add("hGenEventCentrality", "hGenEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); + if (useMcCentrality) { + histos.add("hGenEventsVsMcCentrality", "hGenEventsVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, {2, -0.5f, +1.5f}}); + histos.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + histos.get(HIST("hGenEventsVsMcCentrality"))->GetYaxis()->SetBinLabel(2, "Gen. with at least 1 rec. events"); + } else { + histos.add("hGenEvents", "hGenEvents", kTH2D, {{axisConfigurations.axisNch}, {2, -0.5f, +1.5f}}); + histos.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(1, "All gen. events"); + histos.get(HIST("hGenEvents"))->GetYaxis()->SetBinLabel(2, "Gen. with at least 1 rec. events"); + } + histos.add("hGenEventCentrality", "hGenEventCentrality", kTH1D, {axisConfigurations.axisCentralityFine}); histos.add("hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", kTH2D, {axisConfigurations.axisCentrality, {50, -0.5f, 49.5f}}); histos.add("hCentralityVsNcoll_afterEvSel", "hCentralityVsNcoll_afterEvSel", kTH2D, {axisConfigurations.axisCentrality, {50, -0.5f, 49.5f}}); @@ -1237,41 +1306,68 @@ struct derivedlambdakzeroanalysis { histos.add("h2dGenOmegaMinus", "h2dGenOmegaMinus", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); histos.add("h2dGenOmegaPlus", "h2dGenOmegaPlus", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); - histos.add("h2dGenK0ShortVsMultMC_RecoedEvt", "h2dGenK0ShortVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenLambdaVsMultMC_RecoedEvt", "h2dGenLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenAntiLambdaVsMultMC_RecoedEvt", "h2dGenAntiLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - - histos.add("h2dGenK0ShortVsMultMC", "h2dGenK0ShortVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenLambdaVsMultMC", "h2dGenLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenAntiLambdaVsMultMC", "h2dGenAntiLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + if (useMcCentrality) { + histos.add("h2dGenK0ShortVsMcCentrality_RecoedEvt", "h2dGenK0ShortVsMcCentrality_RecoedEvt", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenLambdaVsMcCentrality_RecoedEvt", "h2dGenLambdaVsMcCentrality_RecoedEvt", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenAntiLambdaVsMcCentrality_RecoedEvt", "h2dGenAntiLambdaVsMcCentrality_RecoedEvt", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenXiMinusVsMcCentrality_RecoedEvt", "h2dGenXiMinusVsMcCentrality_RecoedEvt", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenXiPlusVsMcCentrality_RecoedEvt", "h2dGenXiPlusVsMcCentrality_RecoedEvt", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaMinusVsMcCentrality_RecoedEvt", "h2dGenOmegaMinusVsMcCentrality_RecoedEvt", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaPlusVsMcCentrality_RecoedEvt", "h2dGenOmegaPlusVsMcCentrality_RecoedEvt", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + + histos.add("h2dGenK0ShortVsMcCentrality", "h2dGenK0ShortVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenLambdaVsMcCentrality", "h2dGenLambdaVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenAntiLambdaVsMcCentrality", "h2dGenAntiLambdaVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenXiMinusVsMcCentrality", "h2dGenXiMinusVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenXiPlusVsMcCentrality", "h2dGenXiPlusVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaMinusVsMcCentrality", "h2dGenOmegaMinusVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaPlusVsMcCentrality", "h2dGenOmegaPlusVsMcCentrality", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisPt}); + } else { + histos.add("h2dGenK0ShortVsMultMC_RecoedEvt", "h2dGenK0ShortVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenLambdaVsMultMC_RecoedEvt", "h2dGenLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenAntiLambdaVsMultMC_RecoedEvt", "h2dGenAntiLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + + histos.add("h2dGenK0ShortVsMultMC", "h2dGenK0ShortVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenLambdaVsMultMC", "h2dGenLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenAntiLambdaVsMultMC", "h2dGenAntiLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + } if (doSecondaryV0s) { + histos.add("h2dGenSecK0Short", "h2dGenSecK0Short", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); histos.add("h2dGenSecLambda", "h2dGenSecLambda", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); histos.add("h2dGenSecAntiLambda", "h2dGenSecAntiLambda", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); histos.add("h2dGenSecLambdaFromXi", "h2dGenSecLambdaFromXi", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); histos.add("h2dGenSecAntiLambdaFromXi", "h2dGenSecAntiLambdaFromXi", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + histos.add("h2dGenSecLambdaFromXiAndXi0", "h2dGenSecLambdaFromXiAndXi0", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + histos.add("h2dGenSecAntiLambdaFromXiAndXi0", "h2dGenSecAntiLambdaFromXiAndXi0", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); histos.add("h2dGenSecLambdaFromOmega", "h2dGenSecLambdaFromOmega", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); histos.add("h2dGenSecAntiLambdaFromOmega", "h2dGenSecAntiLambdaFromOmega", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); - histos.add("h2dGenSecLambda_VsRecoedEvt", "h2dGenSecLambda_VsRecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenSecAntiLambda_VsRecoedEvt", "h2dGenSecAntiLambda_VsRecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenSecLambdaFromXiVsMultMC_VsRecoedEvt", "h2dGenSecLambdaFromXiVsMultMC_VsRecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenSecAntiLambdaFromXiVsMultMC_VsRecoedEvt", "h2dGenSecAntiLambdaFromXiVsMultMC_VsRecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenSecLambdaFromOmegaVsMultMC_VsRecoedEvt", "h2dGenSecLambdaFromOmegaVsMultMC_VsRecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - histos.add("h2dGenSecAntiLambdaFromOmegaVsMultMC_VsRecoedEvt", "h2dGenSecAntiLambdaFromOmegaVsMultMC_VsRecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); - + histos.add("h2dGenSecK0ShortVsMultMC_RecoedEvt", "h2dGenSecK0ShortVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecLambdaVsMultMC_RecoedEvt", "h2dGenSecLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecAntiLambdaVsMultMC_RecoedEvt", "h2dGenSecAntiLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecLambdaFromXiVsMultMC_RecoedEvt", "h2dGenSecLambdaFromXiVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecAntiLambdaFromXiVsMultMC_RecoedEvt", "h2dGenSecAntiLambdaFromXiVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecLambdaFromXiAndXi0VsMultMC_RecoedEvt", "h2dGenSecLambdaFromXiAndXi0VsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecAntiLambdaFromXiAndXi0VsMultMC_RecoedEvt", "h2dGenSecAntiLambdaFromXiAndXi0VsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecLambdaFromOmegaVsMultMC_RecoedEvt", "h2dGenSecLambdaFromOmegaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecAntiLambdaFromOmegaVsMultMC_RecoedEvt", "h2dGenSecAntiLambdaFromOmegaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + + histos.add("h2dGenSecK0ShortVsMultMC", "h2dGenSecK0ShortVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); histos.add("h2dGenSecLambdaVsMultMC", "h2dGenSecLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); histos.add("h2dGenSecAntiLambdaVsMultMC", "h2dGenSecAntiLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); histos.add("h2dGenSecLambdaFromXiVsMultMC", "h2dGenSecLambdaFromXiVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); histos.add("h2dGenSecAntiLambdaFromXiVsMultMC", "h2dGenSecAntiLambdaFromXiVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecLambdaFromXiAndXi0VsMultMC", "h2dGenSecLambdaFromXiAndXi0VsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + histos.add("h2dGenSecAntiLambdaFromXiAndXi0VsMultMC", "h2dGenSecAntiLambdaFromXiAndXi0VsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); histos.add("h2dGenSecLambdaFromOmegaVsMultMC", "h2dGenSecLambdaFromOmegaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); histos.add("h2dGenSecAntiLambdaFromOmegaVsMultMC", "h2dGenSecAntiLambdaFromOmegaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); } @@ -1291,24 +1387,46 @@ struct derivedlambdakzeroanalysis { } // ______________________________________________________ - // Return slicing output + // Return centrality estimate for a given collision. + // If takeMcCentrality is enabled, the centrality is taken from the MC collision; otherwise it is taken + // from the reconstructed collision. Returns -1 if no corresponding centrality estimator is found or if no MC collision is associated to the recoed collision. template - auto getCentralityRun3(TCollision const& collision) + auto getCentralityRun3(TCollision const& collision, bool takeMcCentrality = false) { - if (centralityEstimator == kCentFT0C) - return collision.centFT0C(); - else if (centralityEstimator == kCentFT0M) - return collision.centFT0M(); - else if (centralityEstimator == kCentFT0CVariant1) - return collision.centFT0CVariant1(); - else if (centralityEstimator == kCentMFT) - return collision.centMFT(); - else if (centralityEstimator == kCentNGlobal) - return collision.centNGlobal(); - else if (centralityEstimator == kCentFV0A) - return collision.centFV0A(); - - return -1.f; + // Helper lambda to extract centrality from any object exposing the cent* columns + auto extractCentrality = [this](auto const& coll) -> float { + switch (centralityEstimator) { + case kCentFT0C: + return coll.centFT0C(); + case kCentFT0M: + return coll.centFT0M(); + case kCentFT0CVariant1: + return coll.centFT0CVariant1(); + // case kCentMFT: return coll.centMFT(); + case kCentNGlobal: + return coll.centNGlobal(); + case kCentFV0A: + return coll.centFV0A(); + default: + return -1.f; + } + }; + + // check if we are in MC + if constexpr (requires { collision.straMCCollisionId(); }) { + if (takeMcCentrality) { // check if we need to extract centrality from recoed or generated collision + // if recoed collision has a MC collision, de-reference it and extract centrality from it + // otherwise return -1.f + if (collision.has_straMCCollision()) { + auto mcCollision = collision.template straMCCollision_as>(); + return extractCentrality(mcCollision); + } else { + return -1.f; + } + } + } + + return extractCentrality(collision); } // ______________________________________________________ @@ -1324,7 +1442,7 @@ struct derivedlambdakzeroanalysis { } template - void initCCDB(TCollision collision) + void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { return; @@ -1385,7 +1503,7 @@ struct derivedlambdakzeroanalysis { } template - uint64_t computeReconstructionBitmap(TV0 v0, TCollision collision, float rapidityLambda, float rapidityK0Short, float /*pT*/) + uint64_t computeReconstructionBitmap(TV0 const& v0, TCollision const& collision, float rapidityLambda, float rapidityK0Short) // precalculate this information so that a check is one mask operation, not many { uint64_t bitMap = 0; @@ -1404,9 +1522,11 @@ struct derivedlambdakzeroanalysis { BITSET(bitMap, selDCAV0Dau); // rapidity - if (std::abs(rapidityLambda) < v0Selections.rapidityCut) + if (rapidityLambda > v0Selections.rapidityMinCut && + rapidityLambda < v0Selections.rapidityMaxCut) BITSET(bitMap, selLambdaRapidity); - if (std::abs(rapidityK0Short) < v0Selections.rapidityCut) + if (rapidityK0Short > v0Selections.rapidityMinCut && + rapidityK0Short < v0Selections.rapidityMaxCut) BITSET(bitMap, selK0ShortRapidity); // @@ -1646,7 +1766,7 @@ struct derivedlambdakzeroanalysis { } template - uint64_t computeMCAssociation(TV0 v0) + uint64_t computeMCAssociation(TV0 const& v0) // precalculate this information so that a check is one mask operation, not many { uint64_t bitMap = 0; @@ -1657,18 +1777,21 @@ struct derivedlambdakzeroanalysis { if (v0.pdgCode() == PDG_t::kK0Short && isPositivePion && isNegativePion) { BITSET(bitMap, selConsiderK0Short); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimK0Short); + } } if (v0.pdgCode() == PDG_t::kLambda0 && isPositiveProton && isNegativePion) { BITSET(bitMap, selConsiderLambda); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimLambda); + } } if (v0.pdgCode() == PDG_t::kLambda0Bar && isPositivePion && isNegativeProton) { BITSET(bitMap, selConsiderAntiLambda); - if (v0.isPhysicalPrimary()) + if (v0.isPhysicalPrimary()) { BITSET(bitMap, selPhysPrimAntiLambda); + } } return bitMap; } @@ -1829,7 +1952,7 @@ struct derivedlambdakzeroanalysis { } template - void analyseCandidate(TV0 v0, float pt, float centrality, uint64_t selMap, uint8_t gapSide, int& nK0Shorts, int& nLambdas, int& nAntiLambdas) + void analyseCandidate(TV0 const& v0, float pt, float rapidityLambda, float rapidityK0Short, float centrality, uint64_t selMap, uint8_t gapSide, int& nK0Shorts, int& nLambdas, int& nAntiLambdas) // precalculate this information so that a check is one mask operation, not many { bool passK0ShortSelections = false; @@ -1844,7 +1967,7 @@ struct derivedlambdakzeroanalysis { float k0shortScore = -1; if (mlConfigurations.calculateK0ShortScores) { // evaluate machine-learning scores - float* k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); + const std::vector k0shortProbability = mlCustomModelK0Short.evalModel(inputFeatures); k0shortScore = k0shortProbability[1]; } else { k0shortScore = v0.k0ShortBDTScore(); @@ -1859,7 +1982,7 @@ struct derivedlambdakzeroanalysis { float lambdaScore = -1; if (mlConfigurations.calculateLambdaScores) { // evaluate machine-learning scores - float* lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); + const std::vector lambdaProbability = mlCustomModelLambda.evalModel(inputFeatures); lambdaScore = lambdaProbability[1]; } else { lambdaScore = v0.lambdaBDTScore(); @@ -1874,7 +1997,7 @@ struct derivedlambdakzeroanalysis { float antiLambdaScore = -1; if (mlConfigurations.calculateAntiLambdaScores) { // evaluate machine-learning scores - float* antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); + const std::vector antilambdaProbability = mlCustomModelAntiLambda.evalModel(inputFeatures); antiLambdaScore = antilambdaProbability[1]; } else { antiLambdaScore = v0.antiLambdaBDTScore(); @@ -1913,6 +2036,7 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); histos.fill(HIST("h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); histos.fill(HIST("h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + histos.fill(HIST("h2dMassK0ShortVsMassLambda"), invMassK0Short, analyseAntiLambda ? invMassAntiLambda : invMassLambda); } // Fill first bin: all candidates @@ -1951,6 +2075,7 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("K0Short/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); histos.fill(HIST("K0Short/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); histos.fill(HIST("K0Short/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + histos.fill(HIST("K0Short/h2dMassK0ShortVsMassLambda"), invMassK0Short, invMassLambda); } if (doDetectPropQA == 1) { histos.fill(HIST("K0Short/h6dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pt); @@ -1992,6 +2117,8 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("K0Short/h3dNegTPCsignalVsTrackPt"), centrality, v0.negativept(), negTrackExtra.tpcSignal()); } if (doTOFQA) { + histos.fill(HIST("K0Short/h1dPosNsigmaTOF"), v0.tofNSigmaK0PiPlus()); + histos.fill(HIST("K0Short/h1dNegNsigmaTOF"), v0.tofNSigmaK0PiMinus()); histos.fill(HIST("K0Short/h3dPosNsigmaTOF"), centrality, pt, v0.tofNSigmaK0PiPlus()); histos.fill(HIST("K0Short/h3dNegNsigmaTOF"), centrality, pt, v0.tofNSigmaK0PiMinus()); histos.fill(HIST("K0Short/h3dPosTOFdeltaT"), centrality, pt, v0.posTOFDeltaTK0Pi()); @@ -2010,6 +2137,11 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("K0Short/h5dPosPhiVsEta"), centrality, v0.positivept(), invMassK0Short, v0.positivephi(), v0.positiveeta()); histos.fill(HIST("K0Short/h5dNegPhiVsEta"), centrality, v0.negativept(), invMassK0Short, v0.negativephi(), v0.negativeeta()); } + if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { + histos.fill(HIST("GeneralQA/h2dRapVsRapGen"), v0.yK0Short(), rapidityK0Short); + histos.fill(HIST("GeneralQA/h2dPtVsPtGen"), v0.pt(), pt); + histos.fill(HIST("GeneralQA/h3dRapVsRapGen"), centrality, v0.yK0Short(), rapidityK0Short); + histos.fill(HIST("GeneralQA/h3dPtVsPtGen"), centrality, v0.pt(), pt); // fill V0 info table if requested if (fillV0infoTable) { v0TOFinfo(v0.mK0Short(), @@ -2053,6 +2185,7 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("Lambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); histos.fill(HIST("Lambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); histos.fill(HIST("Lambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + histos.fill(HIST("Lambda/h2dMassK0ShortVsMassLambda"), invMassK0Short, invMassLambda); } if (doDetectPropQA == 1) { histos.fill(HIST("Lambda/h6dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pt); @@ -2116,6 +2249,11 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("Lambda/h5dPosPhiVsEta"), centrality, v0.positivept(), invMassLambda, v0.positivephi(), v0.positiveeta()); histos.fill(HIST("Lambda/h5dNegPhiVsEta"), centrality, v0.negativept(), invMassLambda, v0.negativephi(), v0.negativeeta()); } + if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { + histos.fill(HIST("GeneralQA/h2dRapVsRapGen"), v0.yLambda(), rapidityLambda); + histos.fill(HIST("GeneralQA/h2dPtVsPtGen"), v0.pt(), pt); + histos.fill(HIST("GeneralQA/h3dRapVsRapGen"), centrality, v0.yLambda(), rapidityLambda); + histos.fill(HIST("GeneralQA/h3dPtVsPtGen"), centrality, v0.pt(), pt); // fill V0 info table if requested if (fillV0infoTable) { v0TOFinfo(v0.mLambda(), @@ -2159,6 +2297,7 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("AntiLambda/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); histos.fill(HIST("AntiLambda/h2dPositivePtVsPhi"), v0.positivept(), computePhiMod(v0.positivephi(), 1)); histos.fill(HIST("AntiLambda/h2dNegativePtVsPhi"), v0.negativept(), computePhiMod(v0.negativephi(), -1)); + histos.fill(HIST("AntiLambda/h2dMassK0ShortVsMassLambda"), invMassK0Short, invMassAntiLambda); } if (doDetectPropQA == 1) { histos.fill(HIST("AntiLambda/h6dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pt); @@ -2218,6 +2357,11 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("AntiLambda/h5dPosPhiVsEta"), centrality, v0.positivept(), invMassAntiLambda, v0.positivephi(), v0.positiveeta()); histos.fill(HIST("AntiLambda/h5dNegPhiVsEta"), centrality, v0.negativept(), invMassAntiLambda, v0.negativephi(), v0.negativeeta()); } + if (doprocessMonteCarloRun3 || doprocessMonteCarloRun2) { + histos.fill(HIST("GeneralQA/h2dRapVsRapGen"), v0.yLambda(), rapidityLambda); + histos.fill(HIST("GeneralQA/h2dPtVsPtGen"), v0.pt(), pt); + histos.fill(HIST("GeneralQA/h3dRapVsRapGen"), centrality, v0.yLambda(), rapidityLambda); + histos.fill(HIST("GeneralQA/h3dPtVsPtGen"), centrality, v0.pt(), pt); // fill V0 info table if requested if (fillV0infoTable) { v0TOFinfo(v0.mAntiLambda(), @@ -2281,7 +2425,7 @@ struct derivedlambdakzeroanalysis { } template - void analyseCollisionAssociation(TV0 /*v0*/, float pt, int mcNch, bool correctAssociation, uint64_t selMap) + void analyseCollisionAssociation(TV0 const& /*v0*/, float pt, int mcNch, bool correctAssociation, uint64_t selMap) // analyse collision association { // __________________________________________ @@ -2304,7 +2448,7 @@ struct derivedlambdakzeroanalysis { } template - void fillFeeddownMatrix(TV0 v0, float pt, float centrality, uint64_t selMap) + void fillFeeddownMatrix(TV0 const& v0, float pt, float centrality, uint64_t selMap) // fill feeddown matrix for Lambdas or AntiLambdas // fixme: a potential improvement would be to consider mass windows for the l/al { @@ -2318,7 +2462,8 @@ struct derivedlambdakzeroanalysis { if (std::abs(v0mother.pdgCode()) == o2::constants::physics::Pdg::kXi0) rapidityXi = RecoDecay::y(std::array{v0mother.px(), v0mother.py(), v0mother.pz()}, o2::constants::physics::MassXi0); - if (std::fabs(rapidityXi) > 0.5f) + if (rapidityXi < v0Selections.rapidityMinCut || + rapidityXi > v0Selections.rapidityMaxCut) return; // not a valid mother rapidity (PDG selection is later) // __________________________________________ @@ -2356,8 +2501,45 @@ struct derivedlambdakzeroanalysis { } } + template + void fillFeeddownQA(TV0 const& v0, float pt, float centrality, uint64_t selMap) + // fill feeddown matrix for Lambdas or AntiLambdas + // fixme: a potential improvement would be to consider mass windows for the l/al + { + if (!v0.has_motherMCPart()) + return; // does not have mother particle in record, skip + + auto v0mother = v0.motherMCPart(); + + // __________________________________________ + if (verifyMask(selMap, secondaryMaskSelectionK0Short) && analyseK0Short) { + histos.fill(HIST("h2dK0ShortMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dK0ShortMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dK0ShortMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dK0ShortMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + if (verifyMask(selMap, secondaryMaskSelectionLambda) && analyseLambda) { + histos.fill(HIST("h2dLambdaMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dLambdaMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dLambdaMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dLambdaMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + if (verifyMask(selMap, secondaryMaskSelectionAntiLambda) && analyseAntiLambda) { + histos.fill(HIST("h2dAntiLambdaMothers"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dAntiLambdaMothers"), centrality, pt, v0mother.pdgCode()); + if (v0mother.isPhysicalPrimary()) { + histos.fill(HIST("h2dAntiLambdaMothersIsPrimary"), pt, v0mother.pdgCode()); + histos.fill(HIST("h3dAntiLambdaMothersIsPrimary"), centrality, pt, v0mother.pdgCode()); + } + } + } + template - bool isEventAccepted(TCollision collision, bool fillHists) + bool isEventAccepted(TCollision const& collision, bool fillHists) // check whether the collision passes our collision selections { float centrality = -1.0f; @@ -2365,7 +2547,7 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("hEventSelection"), 0. /* all collisions */); if (doEventQA) { if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - centrality = getCentralityRun3(collision); + centrality = getCentralityRun3(collision, useMcCentrality); } histos.fill(HIST("hEventSelectionVsCentrality"), 0. /* all collisions */, centrality); } @@ -2522,47 +2704,44 @@ struct derivedlambdakzeroanalysis { } } - if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 16 /* INEL > 0 */, centrality); - } + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 16 /* INEL > 0 */, centrality); } + } - if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 17 /* INEL > 1 */, centrality); - } + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 17 /* INEL > 1 */, centrality); } + } - } else { // we are in Pb-Pb - float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); - if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 16 /* Below min occupancy */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 16 /* Below min occupancy */, centrality); - } + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 18 /* Below min occupancy */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 18 /* Below min occupancy */, centrality); } + } - if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { - return false; - } - if (fillHists) { - histos.fill(HIST("hEventSelection"), 17 /* Above max occupancy */); - if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 17 /* Above max occupancy */, centrality); - } + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 19 /* Above max occupancy */); + if (doEventQA) { + histos.fill(HIST("hEventSelectionVsCentrality"), 19 /* Above max occupancy */, centrality); } } @@ -2572,9 +2751,9 @@ struct derivedlambdakzeroanalysis { return false; } if (fillHists) { - histos.fill(HIST("hEventSelection"), 18 /* Below min IR */); + histos.fill(HIST("hEventSelection"), 20 /* Below min IR */); if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 18 /* Below min IR */, centrality); + histos.fill(HIST("hEventSelectionVsCentrality"), 20 /* Below min IR */, centrality); } } @@ -2582,9 +2761,9 @@ struct derivedlambdakzeroanalysis { return false; } if (fillHists) { - histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); + histos.fill(HIST("hEventSelection"), 21 /* Above max IR */); if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 19 /* Above max IR */, centrality); + histos.fill(HIST("hEventSelectionVsCentrality"), 21 /* Above max IR */, centrality); } } @@ -2592,9 +2771,9 @@ struct derivedlambdakzeroanalysis { return false; } if (fillHists) { - histos.fill(HIST("hEventSelection"), 20 /* Pass CBT condition */); + histos.fill(HIST("hEventSelection"), 22 /* Pass CBT condition */); if (doEventQA) { - histos.fill(HIST("hEventSelectionVsCentrality"), 20 /* Pass CBT condition */, centrality); + histos.fill(HIST("hEventSelectionVsCentrality"), 22 /* Pass CBT condition */, centrality); } } @@ -2689,19 +2868,17 @@ struct derivedlambdakzeroanalysis { if (fillHists) histos.fill(HIST("hEventSelection"), 15 /* No beam-gas according to cluster-vs-tracklet correlation */); - if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) + histos.fill(HIST("hEventSelection"), 16 /* INEL > 0 */); - if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { - return false; - } - if (fillHists) - histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; } + if (fillHists) + histos.fill(HIST("hEventSelection"), 17 /* INEL > 1 */); } return true; @@ -2750,14 +2927,18 @@ struct derivedlambdakzeroanalysis { void fillReconstructedEventProperties(TCollision const& collision, float& centrality, float& collisionOccupancy, double& interactionRate, int& gapSide, int& selGapSide) { if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - centrality = getCentralityRun3(collision); + centrality = getCentralityRun3(collision, useMcCentrality); collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); // Fetch interaction rate only if required (in order to limit ccdb calls) interactionRate = !irSource.value.empty() ? rateFetcher.fetch(ccdb.service, collision.timestamp(), collision.runNumber(), irSource) * 1.e-3 : -1; if (qaCentrality) { auto hRawCentrality = histos.get(HIST("hRawCentrality")); - centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(doPPAnalysis ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); + centrality = hRawCentrality->GetBinContent(hRawCentrality->FindBin(centralityEstimator == kCentFT0M ? collision.multFT0A() + collision.multFT0C() : collision.multFT0C())); + } + + if (useMcCentrality) { + histos.fill(HIST("hEventVsGenCentrality"), getCentralityRun3(collision, false), getCentralityRun3(collision, true)); } if (doUPCanalysis) { @@ -2773,6 +2954,10 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("hSelGapSide"), selGapSide); histos.fill(HIST("hEventCentralityVsSelGapSide"), centrality, selGapSide <= 2 ? selGapSide : -1); } + if (doTOFQA) { + histos.fill(HIST("hCollisionTimes"), collision.eventTime()); + histos.fill(HIST("hCollisionTimesError"), collision.eventTimeErr()); + } } else { // no, we are in Run 2 centrality = eventSelections.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); } @@ -2820,23 +3005,33 @@ struct derivedlambdakzeroanalysis { if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { continue; } - if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { - continue; - } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + continue; + } - if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { - continue; - } + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + continue; } - histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("hGenEventsVsMcCentrality"), getCentralityRun3(mcCollision), 0 /* all gen. events*/); + } else { + histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); + } + } else { + histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); + } auto groupedCollisions = getGroupedCollisions(collisions, mcCollision.globalIndex()); // Check if there is at least one of the reconstructed collisions associated to this MC collision // If so, we consider it bool atLeastOne = false; int biggestNContribs = -1; + float multFT0M = -1.f; + float multFT0C = -1.f; + float multNGlobal = -1.f; + float multFV0A = -1.f; float centrality = 100.5f; int nCollisions = 0; for (auto const& collision : groupedCollisions) { @@ -2848,7 +3043,11 @@ struct derivedlambdakzeroanalysis { if constexpr (run3) { // check if we are in Run 3 if (biggestNContribs < collision.multPVTotalContributors()) { biggestNContribs = collision.multPVTotalContributors(); - centrality = getCentralityRun3(collision); + centrality = getCentralityRun3(collision, useMcCentrality); + multFT0M = collision.multFT0A() + collision.multFT0C(); + multFT0C = collision.multFT0C(); + multNGlobal = collision.multNTracksGlobal(); + multFV0A = collision.multFV0A(); } } else { // we are in Run 2: there should be only one collision in groupedCollisions centrality = eventSelections.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); @@ -2865,8 +3064,23 @@ struct derivedlambdakzeroanalysis { histos.fill(HIST("hCentralityVsPVzMC"), centrality, mcCollision.posZ()); histos.fill(HIST("hEventPVzMC"), mcCollision.posZ()); + if (doEventMCQA) { + histos.fill(HIST("hMultFT0MVsMultMC"), multFT0M, mcCollision.multMCNParticlesEta05()); + histos.fill(HIST("hMultFT0CVsMultMC"), multFT0C, mcCollision.multMCNParticlesEta05()); + histos.fill(HIST("hMultNGlobalVsMultMC"), multNGlobal, mcCollision.multMCNParticlesEta05()); + histos.fill(HIST("hMultFV0AVsMultMC"), multFV0A, mcCollision.multMCNParticlesEta05()); + } + if (atLeastOne) { - histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* at least 1 rec. event*/); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("hGenEventsVsMcCentrality"), getCentralityRun3(mcCollision), 1 /* at least 1 rec. event*/); + } else { + histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* at least 1 rec. event*/); + } + } else { + histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* at least 1 rec. event*/); + } histos.fill(HIST("hGenEventCentrality"), centrality); } @@ -2875,9 +3089,9 @@ struct derivedlambdakzeroanalysis { } // ______________________________________________________ - // Real data processing - no MC subscription - template - void analyzeRecoedV0sInRealData(TCollision const& collision, TV0s const& fullV0s) + // Recoed data and MC processing (can use MC info if subscribed to it) + template + void analyzeRecoedV0s(TCollision const& collision, TV0s const& fullV0s, TMCCollisions const& /*mcCollisions*/) { // Fire up CCDB if ((mlConfigurations.useK0ShortScores && mlConfigurations.calculateK0ShortScores) || @@ -2900,8 +3114,6 @@ struct derivedlambdakzeroanalysis { // Fill recoed event properties fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); - // __________________________________________ // perform main analysis int nK0Shorts = 0; @@ -2914,99 +3126,58 @@ struct derivedlambdakzeroanalysis { if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) continue; // skip V0s that are not standard - // fill AP plot for all V0s - histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - - uint64_t selMap = computeReconstructionBitmap(v0, collision, v0.yLambda(), v0.yK0Short(), v0.pt()); - - // consider for histograms for all species - BITSET(selMap, selConsiderK0Short); - BITSET(selMap, selConsiderLambda); - BITSET(selMap, selConsiderAntiLambda); - - BITSET(selMap, selPhysPrimK0Short); - BITSET(selMap, selPhysPrimLambda); - BITSET(selMap, selPhysPrimAntiLambda); - - analyseCandidate(v0, v0.pt(), centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); - } // end v0 loop - - // fill the histograms with the number of reconstructed K0s/Lambda/antiLambda per collision - if (analyseK0Short) { - histos.fill(HIST("h2dNbrOfK0ShortVsCentrality"), centrality, nK0Shorts); - } - if (analyseLambda) { - histos.fill(HIST("h2dNbrOfLambdaVsCentrality"), centrality, nLambdas); - } - if (analyseAntiLambda) { - histos.fill(HIST("h2dNbrOfAntiLambdaVsCentrality"), centrality, nAntiLambdas); - } - } - - // ______________________________________________________ - // Simulated processing (subscribes to MC information too) - template - void analyzeRecoedV0sInMonteCarlo(TCollision const& collision, TV0s const& fullV0s) - { - // Fire up CCDB - if ((mlConfigurations.useK0ShortScores && mlConfigurations.calculateK0ShortScores) || - (mlConfigurations.useLambdaScores && mlConfigurations.calculateLambdaScores) || - (mlConfigurations.useAntiLambdaScores && mlConfigurations.calculateAntiLambdaScores) || - v0Selections.rejectTPCsectorBoundary) { - initCCDB(collision); - } - - if (!isEventAccepted(collision, true)) { - return; - } - - float centrality = -1; - float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated - double interactionRate = -1; - // gap side - int gapSide = -1; - int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides - // Fill recoed event properties - fillReconstructedEventProperties(collision, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide); - - histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + float pt = v0.pt(); + float ptMC = -1.f; + float yK0Short = v0.yK0Short(); + float yLambda = v0.yLambda(); + bool correctCollision = false; + int mcNch = -1; + uint64_t selMapMCassociation = 0; + + if constexpr (requires { v0.v0MCCoreId(); }) { + if (doMCAssociation) { + if (!v0.has_v0MCCore()) + continue; - // __________________________________________ - // perform main analysis - int nK0Shorts = 0; - int nLambdas = 0; - int nAntiLambdas = 0; - for (auto const& v0 : fullV0s) { - if (std::abs(v0.negativeeta()) > v0Selections.daughterEtaCut || std::abs(v0.positiveeta()) > v0Selections.daughterEtaCut) - continue; // remove acceptance that's badly reproduced by MC / superfluous in future + auto v0MC = v0.template v0MCCore_as>(); - if (v0.v0Type() != v0Selections.v0TypeSelection && v0Selections.v0TypeSelection > -1) - continue; // skip V0s that are not standard + ptMC = RecoDecay::sqrtSumOfSquares(v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC()); + pt = ptMC; + yK0Short = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassKaonNeutral); + yLambda = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassLambda); - if (!v0.has_v0MCCore()) - continue; + selMapMCassociation = computeMCAssociation(v0MC); - auto v0MC = v0.template v0MCCore_as>(); + if constexpr (requires { collision.straMCCollisionId(); }) { + // check collision association explicitly + if (collision.has_straMCCollision()) { + auto mcCollision = collision.template straMCCollision_as(); + mcNch = mcCollision.multMCNParticlesEta05(); + correctCollision = (v0MC.straMCCollisionId() == mcCollision.globalIndex()); + } + } + } + } // fill AP plot for all V0s histos.fill(HIST("GeneralQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - float ptmc = RecoDecay::sqrtSumOfSquares(v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC()); - float ymc = 1e-3; - if (v0MC.pdgCode() == PDG_t::kK0Short) - ymc = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassKaonNeutral); - else if (std::abs(v0MC.pdgCode()) == PDG_t::kLambda0) - ymc = RecoDecay::y(std::array{v0MC.pxPosMC() + v0MC.pxNegMC(), v0MC.pyPosMC() + v0MC.pyNegMC(), v0MC.pzPosMC() + v0MC.pzNegMC()}, o2::constants::physics::MassLambda); - - uint64_t selMap = computeReconstructionBitmap(v0, collision, ymc, ymc, ptmc); - selMap = selMap | computeMCAssociation(v0MC); + uint64_t selMap = computeReconstructionBitmap(v0, collision, yLambda, yK0Short); + selMap |= selMapMCassociation; // feeddown matrix always with association - if (calculateFeeddownMatrix) - fillFeeddownMatrix(v0, ptmc, centrality, selMap); + if constexpr (requires { v0.motherMCPartId(); }) { + if (calculateFeeddownMatrix) { + fillFeeddownMatrix(v0, ptMC, centrality, selMap); + } + if (doQAfeeddown) { + fillFeeddownQA(v0, ptMC, centrality, selMap); + } + } // consider only associated candidates if asked to do so, disregard association - if (!doMCAssociation) { + // do not perform MC association when running over real data + if (doprocessRealDataRun3 || doprocessRealDataRun2 || !doMCAssociation) { BITSET(selMap, selConsiderK0Short); BITSET(selMap, selConsiderLambda); BITSET(selMap, selConsiderAntiLambda); @@ -3016,18 +3187,13 @@ struct derivedlambdakzeroanalysis { BITSET(selMap, selPhysPrimAntiLambda); } - analyseCandidate(v0, ptmc, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); + analyseCandidate(v0, pt, yLambda, yK0Short, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas); - if (doCollisionAssociationQA) { - // check collision association explicitly - bool correctCollision = false; - int mcNch = -1; - if (collision.has_straMCCollision()) { - auto mcCollision = collision.template straMCCollision_as>(); - mcNch = mcCollision.multMCNParticlesEta05(); - correctCollision = (v0MC.straMCCollisionId() == mcCollision.globalIndex()); + if constexpr (requires { collision.straMCCollisionId(); }) { + if (doCollisionAssociationQA) { + // check collision association explicitly + analyseCollisionAssociation(v0, ptMC, mcNch, correctCollision, selMap); } - analyseCollisionAssociation(v0, ptmc, mcNch, correctCollision, selMap); } } // end v0 loop @@ -3065,54 +3231,209 @@ struct derivedlambdakzeroanalysis { else if (std::abs(v0MC.pdgCode()) == PDG_t::kLambda0) ymc = v0MC.rapidityMC(1); - if (std::abs(ymc) > v0Selections.rapidityCut) + if (ymc < v0Selections.rapidityMinCut || + ymc > v0Selections.rapidityMaxCut) continue; - auto mcCollision = v0MC.template straMCCollision_as>(); + auto mcCollision = v0MC.template straMCCollision_as(); if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { continue; } - if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { - continue; - } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + continue; + } - if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { - continue; - } + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + continue; } float centrality = 100.5f; if (listBestCollisionIdx[mcCollision.globalIndex()] > -1) { auto collision = collisions.iteratorAt(listBestCollisionIdx[mcCollision.globalIndex()]); if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - centrality = getCentralityRun3(collision); + centrality = getCentralityRun3(collision, useMcCentrality); } else { // no, we are in Run 2 centrality = eventSelections.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); } if (v0MC.pdgCode() == PDG_t::kK0Short) { - histos.fill(HIST("h2dGenK0ShortVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenK0ShortVsMcCentrality_RecoedEvt"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenK0ShortVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenK0ShortVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (v0MC.pdgCode() == PDG_t::kLambda0) { - histos.fill(HIST("h2dGenLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenLambdaVsMcCentrality_RecoedEvt"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (v0MC.pdgCode() == PDG_t::kLambda0Bar) { - histos.fill(HIST("h2dGenAntiLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenAntiLambdaVsMcCentrality_RecoedEvt"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenAntiLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenAntiLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } } } if (v0MC.pdgCode() == PDG_t::kK0Short) { histos.fill(HIST("h2dGenK0Short"), centrality, ptmc); - histos.fill(HIST("h2dGenK0ShortVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenK0ShortVsMcCentrality"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenK0ShortVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenK0ShortVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (v0MC.pdgCode() == PDG_t::kLambda0) { histos.fill(HIST("h2dGenLambda"), centrality, ptmc); - histos.fill(HIST("h2dGenLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenLambdaVsMcCentrality"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (v0MC.pdgCode() == PDG_t::kLambda0Bar) { histos.fill(HIST("h2dGenAntiLambda"), centrality, ptmc); - histos.fill(HIST("h2dGenAntiLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenAntiLambdaVsMcCentrality"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenAntiLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenAntiLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } + } + + if (doSecondaryV0s == 1) { + for (auto const& v0MC : V0MCCores) { + if (!v0MC.has_straMCCollision()) + continue; + + if (v0MC.isPhysicalPrimary()) // select only secondary Lambda + continue; + + float ptmc = v0MC.ptMC(); + float ymc = 1e3; + if (v0MC.pdgCode() == PDG_t::kK0Short) + ymc = v0MC.rapidityMC(0); + else if (std::abs(v0MC.pdgCode()) == PDG_t::kLambda0) + ymc = v0MC.rapidityMC(1); + + if (ymc < v0Selections.rapidityMinCut || + ymc > v0Selections.rapidityMaxCut) + continue; + + auto mcCollision = v0MC.template straMCCollision_as(); + if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { + continue; + } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + continue; + } + + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + continue; + } + + float centrality = 100.5f; + if (listBestCollisionIdx[mcCollision.globalIndex()] > -1) { + auto collision = collisions.iteratorAt(listBestCollisionIdx[mcCollision.globalIndex()]); + if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 + centrality = getCentralityRun3(collision, useMcCentrality); + } else { // no, we are in Run 2 + centrality = eventSelections.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); + } + + if (v0MC.pdgCode() == PDG_t::kK0Short) { + histos.fill(HIST("h2dGenSecK0ShortVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCode() == PDG_t::kLambda0) { + histos.fill(HIST("h2dGenSecLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (v0MC.pdgCodeMother() == PDG_t::kXiMinus) { + histos.fill(HIST("h2dGenSecLambdaFromXiVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kXiMinus || v0MC.pdgCodeMother() == o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2dGenSecLambdaFromXiAndXi0VsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kOmegaMinus) { + histos.fill(HIST("h2dGenSecLambdaFromOmegaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } + if (v0MC.pdgCode() == PDG_t::kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if (v0MC.pdgCodeMother() == PDG_t::kXiPlusBar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromXiVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kXiPlusBar || v0MC.pdgCodeMother() == -o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2dGenSecAntiLambdaFromXiAndXi0VsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kOmegaPlusBar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromOmegaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } + } + + if (v0MC.pdgCode() == PDG_t::kK0Short) { + histos.fill(HIST("h2dGenSecK0Short"), centrality, ptmc); + histos.fill(HIST("h2dGenSecK0ShortVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCode() == PDG_t::kLambda0) { + histos.fill(HIST("h2dGenSecLambda"), centrality, ptmc); + histos.fill(HIST("h2dGenSecLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (v0MC.pdgCodeMother() == PDG_t::kXiMinus) { + histos.fill(HIST("h2dGenSecLambdaFromXi"), centrality, ptmc); + histos.fill(HIST("h2dGenSecLambdaFromXiVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kXiMinus || v0MC.pdgCodeMother() == o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2dGenSecLambdaFromXiAndXi0"), centrality, ptmc); + histos.fill(HIST("h2dGenSecLambdaFromXiAndXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kOmegaMinus) { + histos.fill(HIST("h2dGenSecLambdaFromOmega"), centrality, ptmc); + histos.fill(HIST("h2dGenSecLambdaFromOmegaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } + if (v0MC.pdgCode() == PDG_t::kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambda"), centrality, ptmc); + histos.fill(HIST("h2dGenSecAntiLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if (v0MC.pdgCodeMother() == PDG_t::kXiPlusBar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromXi"), centrality, ptmc); + histos.fill(HIST("h2dGenSecAntiLambdaFromXiVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kXiPlusBar || v0MC.pdgCodeMother() == -o2::constants::physics::Pdg::kXi0) { + histos.fill(HIST("h2dGenSecAntiLambdaFromXiAndXi0"), centrality, ptmc); + histos.fill(HIST("h2dGenSecAntiLambdaFromXiAndXi0VsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (v0MC.pdgCodeMother() == PDG_t::kOmegaPlusBar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromOmega"), centrality, ptmc); + histos.fill(HIST("h2dGenSecAntiLambdaFromOmegaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } } } @@ -3130,153 +3451,239 @@ struct derivedlambdakzeroanalysis { else if (std::abs(cascMC.pdgCode()) == PDG_t::kOmegaMinus) ymc = cascMC.rapidityMC(2); - if (std::abs(ymc) > v0Selections.rapidityCut) + if (ymc < v0Selections.rapidityMinCut || + ymc > v0Selections.rapidityMaxCut) continue; - auto mcCollision = cascMC.template straMCCollision_as>(); + auto mcCollision = cascMC.template straMCCollision_as(); if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { continue; } - if (doPPAnalysis) { // we are in pp - if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { - continue; - } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + continue; + } - if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { - continue; - } + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + continue; } float centrality = 100.5f; if (listBestCollisionIdx[mcCollision.globalIndex()] > -1) { auto collision = collisions.iteratorAt(listBestCollisionIdx[mcCollision.globalIndex()]); if constexpr (requires { collision.centFT0C(); }) { // check if we are in Run 3 - centrality = getCentralityRun3(collision); + centrality = getCentralityRun3(collision, useMcCentrality); } else { // no, we are in Run 2 centrality = eventSelections.useSPDTrackletsCent ? collision.centRun2SPDTracklets() : collision.centRun2V0M(); } if (cascMC.pdgCode() == PDG_t::kXiMinus) { - histos.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenXiMinusVsMcCentrality_RecoedEvt"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (cascMC.pdgCode() == PDG_t::kXiPlusBar) { - histos.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenXiPlusVsMcCentrality_RecoedEvt"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (cascMC.pdgCode() == PDG_t::kOmegaMinus) { - histos.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenOmegaMinusVsMcCentrality_RecoedEvt"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (cascMC.pdgCode() == PDG_t::kOmegaPlusBar) { - histos.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenOmegaPlusVsMcCentrality_RecoedEvt"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } } - if (doSecondaryV0s && std::abs(cascMC.pdgCodeV0()) == kLambda0) { + if (doSecondaryV0s == 2 && std::abs(cascMC.pdgCodeV0()) == kLambda0) { float v0PtMc = std::hypot(cascMC.pxPosMC() + cascMC.pxNegMC(), cascMC.pyPosMC() + cascMC.pyNegMC()); - if (cascMC.pdgCodeV0() == kLambda0) { - histos.fill(HIST("h2dGenSecLambda_VsRecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCodeV0() == kLambda0Bar) { - histos.fill(HIST("h2dGenSecAntiLambda_VsRecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kXiMinus && cascMC.pdgCodeV0() == kLambda0) { - histos.fill(HIST("h2dGenSecLambdaFromXiVsMultMC_VsRecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kXiPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { - histos.fill(HIST("h2dGenSecAntiLambdaFromXiVsMultMC_VsRecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kOmegaMinus && cascMC.pdgCodeV0() == kLambda0) { - histos.fill(HIST("h2dGenSecLambdaFromOmegaVsMultMC_VsRecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kOmegaPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { - histos.fill(HIST("h2dGenSecAntiLambdaFromOmegaVsMultMC_VsRecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); + float v0RapMc = RecoDecay::y(std::array{cascMC.pxPosMC() + cascMC.pxNegMC(), cascMC.pyPosMC() + cascMC.pyNegMC(), cascMC.pzPosMC() + cascMC.pzNegMC()}, o2::constants::physics::MassLambda); + if (v0Selections.rapidityMinCut < v0RapMc && v0RapMc < v0Selections.rapidityMaxCut) { + if (cascMC.pdgCodeV0() == kLambda0) { + histos.fill(HIST("h2dGenSecLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCodeV0() == kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kXiMinus && cascMC.pdgCodeV0() == kLambda0) { + histos.fill(HIST("h2dGenSecLambdaFromXiVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kXiPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromXiVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kOmegaMinus && cascMC.pdgCodeV0() == kLambda0) { + histos.fill(HIST("h2dGenSecLambdaFromOmegaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kOmegaPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromOmegaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } } } } if (cascMC.pdgCode() == PDG_t::kXiMinus) { histos.fill(HIST("h2dGenXiMinus"), centrality, ptmc); - histos.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenXiMinusVsMcCentrality"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (cascMC.pdgCode() == PDG_t::kXiPlusBar) { histos.fill(HIST("h2dGenXiPlus"), centrality, ptmc); - histos.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenXiPlusVsMcCentrality"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (cascMC.pdgCode() == PDG_t::kOmegaMinus) { histos.fill(HIST("h2dGenOmegaMinus"), centrality, ptmc); - histos.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenOmegaMinusVsMcCentrality"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } } if (cascMC.pdgCode() == PDG_t::kOmegaPlusBar) { histos.fill(HIST("h2dGenOmegaPlus"), centrality, ptmc); - histos.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + if constexpr (run3) { + if (useMcCentrality) { + histos.fill(HIST("h2dGenOmegaPlusVsMcCentrality"), getCentralityRun3(mcCollision), ptmc); + } else { + histos.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } else { + histos.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } } - if (doSecondaryV0s && std::abs(cascMC.pdgCodeV0()) == kLambda0) { + if (doSecondaryV0s == 2 && std::abs(cascMC.pdgCodeV0()) == kLambda0) { float v0PtMc = std::hypot(cascMC.pxPosMC() + cascMC.pxNegMC(), cascMC.pyPosMC() + cascMC.pyNegMC()); - if (cascMC.pdgCodeV0() == kLambda0) { - histos.fill(HIST("h2dGenSecLambda"), centrality, v0PtMc); - histos.fill(HIST("h2dGenSecLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCodeV0() == kLambda0Bar) { - histos.fill(HIST("h2dGenSecAntiLambda"), centrality, v0PtMc); - histos.fill(HIST("h2dGenSecAntiLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kXiMinus && cascMC.pdgCodeV0() == kLambda0) { - histos.fill(HIST("h2dGenSecLambdaFromXi"), centrality, v0PtMc); - histos.fill(HIST("h2dGenSecLambdaFromXiVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kXiPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { - histos.fill(HIST("h2dGenSecAntiLambdaFromXi"), centrality, v0PtMc); - histos.fill(HIST("h2dGenSecAntiLambdaFromXiVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kOmegaMinus && cascMC.pdgCodeV0() == kLambda0) { - histos.fill(HIST("h2dGenSecLambdaFromOmega"), centrality, v0PtMc); - histos.fill(HIST("h2dGenSecLambdaFromOmegaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); - } - if (cascMC.pdgCode() == PDG_t::kOmegaPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { - histos.fill(HIST("h2dGenSecAntiLambdaFromOmega"), centrality, v0PtMc); - histos.fill(HIST("h2dGenSecAntiLambdaFromOmegaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); + float v0RapMc = RecoDecay::y(std::array{cascMC.pxPosMC() + cascMC.pxNegMC(), cascMC.pyPosMC() + cascMC.pyNegMC(), cascMC.pzPosMC() + cascMC.pzNegMC()}, o2::constants::physics::MassLambda); + if (v0Selections.rapidityMinCut < v0RapMc && v0RapMc < v0Selections.rapidityMaxCut) { + if (cascMC.pdgCodeV0() == kLambda0) { + histos.fill(HIST("h2dGenSecLambda"), centrality, v0PtMc); + histos.fill(HIST("h2dGenSecLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCodeV0() == kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambda"), centrality, v0PtMc); + histos.fill(HIST("h2dGenSecAntiLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kXiMinus && cascMC.pdgCodeV0() == kLambda0) { + histos.fill(HIST("h2dGenSecLambdaFromXi"), centrality, v0PtMc); + histos.fill(HIST("h2dGenSecLambdaFromXiVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kXiPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromXi"), centrality, v0PtMc); + histos.fill(HIST("h2dGenSecAntiLambdaFromXiVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kOmegaMinus && cascMC.pdgCodeV0() == kLambda0) { + histos.fill(HIST("h2dGenSecLambdaFromOmega"), centrality, v0PtMc); + histos.fill(HIST("h2dGenSecLambdaFromOmegaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } + if (cascMC.pdgCode() == PDG_t::kOmegaPlusBar && cascMC.pdgCodeV0() == kLambda0Bar) { + histos.fill(HIST("h2dGenSecAntiLambdaFromOmega"), centrality, v0PtMc); + histos.fill(HIST("h2dGenSecAntiLambdaFromOmegaVsMultMC"), mcCollision.multMCNParticlesEta05(), v0PtMc); + } } } } } + // ______________________________________________________ + // Simulated processing in Run 2 (subscribes to MC information too) + void processAnalysedCollisions(aod::StraSelections const& straSelections) + { + for (auto const& straSelection : straSelections) { + // Event selection criteria + histos.get(HIST("hEventPreSelection"))->AddBinContent(1, straSelection.totalNbrOfCollisions() /* all collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(2, straSelection.totalIsTriggerTVXCollisions() /* preselected IsTriggerTVX collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(3, straSelection.totalNoITSROFBorderCollisions() /* + preselected NoITSROF collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(4, straSelection.totalNoTFBorderCollisions() /* + preselected NoTF collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(5, straSelection.totalIsGoodZvtxCollisions() /* + preselected |Zvtx| < X cm collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(6, straSelection.totalNoSBPileupCollisions() /* + preselected NoSameBunchPileup collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(7, straSelection.totalIsGoodRCTCollisions() /* + preselected Good RCT collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(8, straSelection.totalNbrOfSelCollisions() /* total number of preselected collisions */); + } + } + // ______________________________________________________ // Real data processing in Run 3 - no MC subscription - void processRealDataRun3(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) + void processRealDataRun3(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) { - analyzeRecoedV0sInRealData(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, static_cast(nullptr)); } // ______________________________________________________ // Real data processing in Run 2 - no MC subscription - void processRealDataRun2(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) + void processRealDataRun2(soa::Join::iterator const& collision, V0Candidates const& fullV0s, DauTracks const&) { - analyzeRecoedV0sInRealData(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, static_cast(nullptr)); } // ______________________________________________________ // Simulated processing in Run 3 (subscribes to MC information too) - void processMonteCarloRun3(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& /*mccollisions*/, soa::Join const&) + void processMonteCarloRun3(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& mccollisions, soa::Join const&) { - analyzeRecoedV0sInMonteCarlo(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, mccollisions); } // ______________________________________________________ // Simulated processing in Run 2 (subscribes to MC information too) - void processMonteCarloRun2(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& /*mccollisions*/, soa::Join const&) + void processMonteCarloRun2(soa::Join::iterator const& collision, V0McCandidates const& fullV0s, DauTracks const&, aod::MotherMCParts const&, soa::Join const& mccollisions, soa::Join const&) { - analyzeRecoedV0sInMonteCarlo(collision, fullV0s); + analyzeRecoedV0s(collision, fullV0s, mccollisions); } // ______________________________________________________ // Simulated processing in Run 3 (subscribes to MC information too) - void processGeneratedRun3(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& CascMCCores, soa::Join const& collisions) + void processGeneratedRun3(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& CascMCCores, soa::Join const& collisions) { analyzeGeneratedV0s(mcCollisions, V0MCCores, CascMCCores, collisions); } // ______________________________________________________ // Simulated processing in Run 2 (subscribes to MC information too) - void processGeneratedRun2(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& CascMCCores, soa::Join const& collisions) + void processGeneratedRun2(soa::Join const& mcCollisions, soa::Join const& V0MCCores, soa::Join const& CascMCCores, soa::Join const& collisions) { analyzeGeneratedV0s(mcCollisions, V0MCCores, CascMCCores, collisions); } @@ -3346,6 +3753,7 @@ struct derivedlambdakzeroanalysis { } } + PROCESS_SWITCH(derivedlambdakzeroanalysis, processAnalysedCollisions, "process filtered events for bookkeeping", false); PROCESS_SWITCH(derivedlambdakzeroanalysis, processRealDataRun3, "process as if real data in Run 3", true); PROCESS_SWITCH(derivedlambdakzeroanalysis, processRealDataRun2, "process as if real data in Run 2", false); PROCESS_SWITCH(derivedlambdakzeroanalysis, processMonteCarloRun3, "process as if MC in Run 3", false); diff --git a/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx b/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx index 6f7f84203c7..5470d63e14a 100644 --- a/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/derivedupcanalysis.cxx @@ -23,6 +23,7 @@ #include "Common/Core/RecoDecay.h" #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -93,7 +95,7 @@ struct Derivedupcanalysis { Configurable analyseOmega{"analyseOmega", true, "process Omega-like candidates"}; Configurable analyseAntiOmega{"analyseAntiOmega", true, "process AntiOmega-like candidates"}; - Configurable> generatorIds{"generatorIds", std::vector{-1}, "MC generatorIds to process"}; + Configurable> generatorIds{"generatorIds", std::vector{-1}, "generator IDs for candidate truth origins and generated particles; event QA uses vertex labels"}; // Event selections struct : ConfigurableGroup { @@ -166,6 +168,28 @@ struct Derivedupcanalysis { } casccuts; Configurable doBachelorBaryonCut{"doBachelorBaryonCut", false, "Enable Bachelor-Baryon cut "}; static constexpr float kNCtauCutsCasc[1][2] = {{6., 6.}}; + static constexpr int kDetectPropQABase = 1; + static constexpr int kDetectPropQAWithMass = 2; + static constexpr int kK0ShortPartID = 0; + static constexpr int kLambdaPartID = 1; + static constexpr int kAntiLambdaPartID = 2; + static constexpr int kFirstCascadePartID = 3; + static constexpr int kXiPartID = 3; + static constexpr int kAntiXiPartID = 4; + static constexpr int kOmegaPartID = 5; + static constexpr int kAntiOmegaPartID = 6; + static constexpr float kNoPidNsigmaCut = 1e5f; + static constexpr float kNoTofDeltaTimeCut = 1e9f; + static constexpr float kDefaultInvalidRapidity = 1e6f; + static constexpr float kDefaultInvalidCtau = 1e6f; + static constexpr float kDisabledArmenterosCut = 1e-4f; + static constexpr float kMotherRapidityMax = 0.5f; + static constexpr float kMissingSignal = -999.f; + static constexpr float kMissingSignalThreshold = 998.f; + static constexpr float kInvalidZdcTime = -12.f; + static constexpr float kMissingZdcTime = -11.f; + static constexpr float kInvalidFITTime = -42.f; + static constexpr float kMissingFITTime = -41.f; Configurable> nCtauCutCasc{"nCtauCutCasc", {kNCtauCutsCasc[0], 2, {"lifetimecutXi", "lifetimecutOmega"}}, "nCtauCutCasc"}; // UPC selections @@ -176,10 +200,15 @@ struct Derivedupcanalysis { Configurable ft0c{"ft0c", 50., "FT0C threshold"}; Configurable zdc{"zdc", 1., "ZDC threshold"}; Configurable fddaTimeCut{"fddaTimeCut", -1., "FDDA timing cut (ns); negative: no cut"}; + Configurable requireFDDATiming{"requireFDDATiming", true, "require valid FDDA timing for gap-side selection when fddaTimeCut is enabled"}; Configurable fddcTimeCut{"fddcTimeCut", -1., "FDDC timing cut (ns); negative: no cut"}; + Configurable requireFDDCTiming{"requireFDDCTiming", true, "require valid FDDC timing for gap-side selection when fddcTimeCut is enabled"}; Configurable fv0aTimeCut{"fv0aTimeCut", -1., "FV0A timing cut (ns); negative: no cut"}; + Configurable requireFV0ATiming{"requireFV0ATiming", true, "require valid FV0A timing for gap-side selection when fv0aTimeCut is enabled"}; Configurable ft0aTimeCut{"ft0aTimeCut", -1., "FT0A timing cut (ns); negative: no cut"}; + Configurable requireFT0ATiming{"requireFT0ATiming", true, "require valid FT0A timing for gap-side selection when ft0aTimeCut is enabled"}; Configurable ft0cTimeCut{"ft0cTimeCut", -1., "FT0C timing cut (ns); negative: no cut"}; + Configurable requireFT0CTiming{"requireFT0CTiming", true, "require valid FT0C timing for gap-side selection when ft0cTimeCut is enabled"}; Configurable zdcTimeCut{"zdcTimeCut", 2., "ZDC timing cut (ns)"}; Configurable requireZDCTiming{"requireZDCTiming", true, "require valid ZDC timing for gap-side selection"}; Configurable genGapSide{"genGapSide", 0, "0 -- A, 1 -- C, 2 -- double"}; @@ -232,11 +261,12 @@ struct Derivedupcanalysis { } axisDetectors; // for MC - Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; + Configurable doMCAssociation{"doMCAssociation", true, "identify candidate species and generator from MC truth; if disabled, select events by vertex generator"}; Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "fill feeddown matrix if MC"}; ConfigurableAxis axisGeneratorIds{"axisGeneratorIds", {256, -0.5f, 255.5f}, "axis for generatorIds"}; Configurable checkNeutronsInMC{"checkNeutronsInMC", true, "require no neutrons for single-gap in MC"}; + Configurable requireGeneratedZDCNeutrons{"requireGeneratedZDCNeutrons", false, "require generated ZDC neutron topology in processGenerated"}; Configurable neutronEtaCut{"neutronEtaCut", 8.8, "ZN acceptance"}; // Occupancy cut @@ -313,8 +343,6 @@ struct Derivedupcanalysis { ConfigurableAxis axisCtau{"axisCtau", {200, 0.0f, 20.0f}, "c x tau (cm)"}; static constexpr std::string_view kParticlenames[] = {"K0Short", "Lambda", "AntiLambda", "Xi", "AntiXi", "Omega", "AntiOmega"}; - static constexpr uint8_t kFT0TriggerBitIsActiveA = 5; - static constexpr uint8_t kFT0TriggerBitIsActiveC = 6; void setBits(std::bitset& mask, std::initializer_list selections) { @@ -323,148 +351,158 @@ struct Derivedupcanalysis { } } + void setTOFSelection(std::bitset& mask, float nsigmaCut, float deltaTimeCut, int nsigmaSelection, int deltaTimeSelection) + { + if (nsigmaCut < kNoPidNsigmaCut) + mask.set(nsigmaSelection); + if (deltaTimeCut < kNoTofDeltaTimeCut) + mask.set(deltaTimeSelection); + } + template - void addTopoHistograms(HistogramRegistry& histos) + void addTopoHistograms(HistogramRegistry& registry) { - const bool isCascade = (partID > 2.5) ? true : false; - if (isCascade) { - histos.add(Form("%s/hCascCosPA", kParticlenames[partID].data()), "hCascCosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); - histos.add(Form("%s/hDCACascDaughters", kParticlenames[partID].data()), "hDCACascDaughters", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); - histos.add(Form("%s/hCascRadius", kParticlenames[partID].data()), "hCascRadius", kTH2D, {axisPtCoarse, {500, 0.0f, 50.0f}}); - histos.add(Form("%s/hMesonDCAToPV", kParticlenames[partID].data()), "hMesonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); - histos.add(Form("%s/hBaryonDCAToPV", kParticlenames[partID].data()), "hBaryonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); - histos.add(Form("%s/hBachDCAToPV", kParticlenames[partID].data()), "hBachDCAToPV", kTH2F, {axisPtCoarse, {200, -1.0f, 1.0f}}); - histos.add(Form("%s/hV0CosPA", kParticlenames[partID].data()), "hV0CosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); - histos.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH2D, {axisPtCoarse, axisV0Radius}); - histos.add(Form("%s/hDCAV0Daughters", kParticlenames[partID].data()), "hDCAV0Daughters", kTH2F, {axisPtCoarse, axisDCAdau}); - histos.add(Form("%s/hDCAV0ToPV", kParticlenames[partID].data()), "hDCAV0ToPV", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); - histos.add(Form("%s/hMassLambdaDau", kParticlenames[partID].data()), "hMassLambdaDau", kTH2F, {axisPtCoarse, axisLambdaMass}); - histos.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); + // Template instantiations intentionally make this particle-type comparison constant. + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/hCascCosPA", kParticlenames[partID].data()), "hCascCosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); + registry.add(Form("%s/hDCACascDaughters", kParticlenames[partID].data()), "hDCACascDaughters", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); + registry.add(Form("%s/hCascRadius", kParticlenames[partID].data()), "hCascRadius", kTH2D, {axisPtCoarse, {500, 0.0f, 50.0f}}); + registry.add(Form("%s/hMesonDCAToPV", kParticlenames[partID].data()), "hMesonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); + registry.add(Form("%s/hBaryonDCAToPV", kParticlenames[partID].data()), "hBaryonDCAToPV", kTH2F, {axisPtCoarse, axisDCAtoPV}); + registry.add(Form("%s/hBachDCAToPV", kParticlenames[partID].data()), "hBachDCAToPV", kTH2F, {axisPtCoarse, {200, -1.0f, 1.0f}}); + registry.add(Form("%s/hV0CosPA", kParticlenames[partID].data()), "hV0CosPA", kTH2F, {axisPtCoarse, {100, 0.9f, 1.0f}}); + registry.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH2D, {axisPtCoarse, axisV0Radius}); + registry.add(Form("%s/hDCAV0Daughters", kParticlenames[partID].data()), "hDCAV0Daughters", kTH2F, {axisPtCoarse, axisDCAdau}); + registry.add(Form("%s/hDCAV0ToPV", kParticlenames[partID].data()), "hDCAV0ToPV", kTH2F, {axisPtCoarse, {44, 0.0f, 2.2f}}); + registry.add(Form("%s/hMassLambdaDau", kParticlenames[partID].data()), "hMassLambdaDau", kTH2F, {axisPtCoarse, axisLambdaMass}); + registry.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); if (doBachelorBaryonCut) { - histos.add(Form("%s/hBachBaryonCosPA", kParticlenames[partID].data()), "hBachBaryonCosPA", kTH2F, {axisPtCoarse, {100, 0.0f, 1.0f}}); - histos.add(Form("%s/hBachBaryonDCAxyToPV", kParticlenames[partID].data()), "hBachBaryonDCAxyToPV", kTH2F, {axisPtCoarse, {300, -3.0f, 3.0f}}); + registry.add(Form("%s/hBachBaryonCosPA", kParticlenames[partID].data()), "hBachBaryonCosPA", kTH2F, {axisPtCoarse, {100, 0.0f, 1.0f}}); + registry.add(Form("%s/hBachBaryonDCAxyToPV", kParticlenames[partID].data()), "hBachBaryonDCAxyToPV", kTH2F, {axisPtCoarse, {300, -3.0f, 3.0f}}); } } else { - histos.add(Form("%s/hPosDCAToPV", kParticlenames[partID].data()), "hPosDCAToPV", kTH1F, {axisDCAtoPV}); - histos.add(Form("%s/hNegDCAToPV", kParticlenames[partID].data()), "hNegDCAToPV", kTH1F, {axisDCAtoPV}); - histos.add(Form("%s/hDCADaughters", kParticlenames[partID].data()), "hDCADaughters", kTH1F, {axisDCAdau}); - histos.add(Form("%s/hPointingAngle", kParticlenames[partID].data()), "hPointingAngle", kTH1F, {axisPointingAngle}); - histos.add(Form("%s/hCosPA", kParticlenames[partID].data()), "hCosPA", kTH1F, {axisCosPA}); - histos.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH1F, {axisV0Radius}); - histos.add(Form("%s/h2dPositiveITSvsTPCpts", kParticlenames[partID].data()), "h2dPositiveITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); - histos.add(Form("%s/h2dNegativeITSvsTPCpts", kParticlenames[partID].data()), "h2dNegativeITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); - histos.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); + registry.add(Form("%s/hPosDCAToPV", kParticlenames[partID].data()), "hPosDCAToPV", kTH1F, {axisDCAtoPV}); + registry.add(Form("%s/hNegDCAToPV", kParticlenames[partID].data()), "hNegDCAToPV", kTH1F, {axisDCAtoPV}); + registry.add(Form("%s/hDCADaughters", kParticlenames[partID].data()), "hDCADaughters", kTH1F, {axisDCAdau}); + registry.add(Form("%s/hPointingAngle", kParticlenames[partID].data()), "hPointingAngle", kTH1F, {axisPointingAngle}); + registry.add(Form("%s/hCosPA", kParticlenames[partID].data()), "hCosPA", kTH1F, {axisCosPA}); + registry.add(Form("%s/hV0Radius", kParticlenames[partID].data()), "hV0Radius", kTH1F, {axisV0Radius}); + registry.add(Form("%s/h2dPositiveITSvsTPCpts", kParticlenames[partID].data()), "h2dPositiveITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); + registry.add(Form("%s/h2dNegativeITSvsTPCpts", kParticlenames[partID].data()), "h2dNegativeITSvsTPCpts", kTH2F, {axisTPCrows, axisITSclus}); + registry.add(Form("%s/hCtau", kParticlenames[partID].data()), "hCtau", kTH2F, {axisPtCoarse, axisCtau}); } } template - void addTPCQAHistograms(HistogramRegistry& histos) + void addTPCQAHistograms(HistogramRegistry& registry) { - const bool isCascade = (partID > 2.5) ? true : false; - histos.add(Form("%s/h3dPosNsigmaTPC", kParticlenames[partID].data()), "h3dPosNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPC", kParticlenames[partID].data()), "h3dNegNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignal", kParticlenames[partID].data()), "h3dPosTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignal", kParticlenames[partID].data()), "h3dNegTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosNsigmaTPC", kParticlenames[partID].data()), "h3dPosNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPC", kParticlenames[partID].data()), "h3dNegNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosTPCsignal", kParticlenames[partID].data()), "h3dPosTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignal", kParticlenames[partID].data()), "h3dNegTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dPosNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dPosNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dNegNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dPosNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dPosNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dNegNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dNegNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dPosTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dNegTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dPosTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dPosTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dNegTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dNegTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - if (isCascade) { - histos.add(Form("%s/h3dBachTPCsignal", kParticlenames[partID].data()), "h3dBachTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dBachNsigmaTPC", kParticlenames[partID].data()), "h3dBachNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); - histos.add(Form("%s/h3dBachNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); - histos.add(Form("%s/h3dBachTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachTPCsignal", kParticlenames[partID].data()), "h3dBachTPCsignal", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dBachNsigmaTPC", kParticlenames[partID].data()), "h3dBachNsigmaTPC", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachNsigmaTPCvsTrackPtot", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachTPCsignalVsTrackPtot", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); + registry.add(Form("%s/h3dBachNsigmaTPCvsTrackPt", kParticlenames[partID].data()), "h3dBachNsigmaTPCvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisNsigmaTPC}); + registry.add(Form("%s/h3dBachTPCsignalVsTrackPt", kParticlenames[partID].data()), "h3dBachTPCsignalVsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTPCsignal}); } } template - void addTOFQAHistograms(HistogramRegistry& histos) + void addTOFQAHistograms(HistogramRegistry& registry) { - const bool isCascade = (partID > 2.5) ? true : false; - histos.add(Form("%s/h3dPosTOFdeltaT", kParticlenames[partID].data()), "h3dPosTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaT", kParticlenames[partID].data()), "h3dNegTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dPosTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dPosTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dNegTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - if (isCascade) { - histos.add(Form("%s/h3dBachTOFdeltaT", kParticlenames[partID].data()), "h3dBachTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dBachTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); - histos.add(Form("%s/h3dBachTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaT", kParticlenames[partID].data()), "h3dPosTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaT", kParticlenames[partID].data()), "h3dNegTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dPosTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dPosTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dNegTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dNegTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachTOFdeltaT", kParticlenames[partID].data()), "h3dBachTOFdeltaT", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dBachTOFdeltaTvsTrackPtot", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPtot", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); + registry.add(Form("%s/h3dBachTOFdeltaTvsTrackPt", kParticlenames[partID].data()), "h3dBachTOFdeltaTvsTrackPt", kTH3F, {axisDetectors.axisFT0ampl, axisPtCoarse, axisTOFdeltaT}); } } template - void addKinematicQAHistograms(HistogramRegistry& histos) + void addKinematicQAHistograms(HistogramRegistry& registry) { - const bool isCascade = (partID > 2.5) ? true : false; - histos.add(Form("%s/h3dPosEtaPt", kParticlenames[partID].data()), "h3dPosEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); - histos.add(Form("%s/h3dNegEtaPt", kParticlenames[partID].data()), "h3dNegEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); - histos.add(Form("%s/h3dRapPt", kParticlenames[partID].data()), "h3dRapPt", kTH3F, {axisPtCoarse, axisRap, axisSelGap}); - if (isCascade) { - histos.add(Form("%s/h3dBachEtaPt", kParticlenames[partID].data()), "h3dBachEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dPosEtaPt", kParticlenames[partID].data()), "h3dPosEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dNegEtaPt", kParticlenames[partID].data()), "h3dNegEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); + registry.add(Form("%s/h3dRapPt", kParticlenames[partID].data()), "h3dRapPt", kTH3F, {axisPtCoarse, axisRap, axisSelGap}); + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h3dBachEtaPt", kParticlenames[partID].data()), "h3dBachEtaPt", kTH3F, {axisPtCoarse, axisEta, axisSelGap}); } } template - void addDetectorPropHistograms(HistogramRegistry& histos) + void addDetectorPropHistograms(HistogramRegistry& registry) { - const bool isCascade = (partID > 2.5) ? true : false; - if (doDetectPropQA == 1) { - if (isCascade) { - histos.add(Form("%s/h8dDetectPropVsCentrality", kParticlenames[partID].data()), "h8dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); + if (doDetectPropQA == kDetectPropQABase) { + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h8dDetectPropVsCentrality", kParticlenames[partID].data()), "h8dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); } else { - histos.add(Form("%s/h6dDetectPropVsCentrality", kParticlenames[partID].data()), "h6dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); + registry.add(Form("%s/h6dDetectPropVsCentrality", kParticlenames[partID].data()), "h6dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse}); } - histos.add(Form("%s/h4dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h4dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); - histos.add(Form("%s/h4dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h4dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); - histos.add(Form("%s/h4dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h4dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h4dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h4dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); + registry.add(Form("%s/h4dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h4dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse}); } - if (doDetectPropQA == 2) { - if (isCascade) { - histos.add(Form("%s/h9dDetectPropVsCentrality", kParticlenames[partID].data()), "h9dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); + if (doDetectPropQA == kDetectPropQAWithMass) { + // cppcheck-suppress knownConditionTrueFalse + if constexpr (partID >= kFirstCascadePartID) { + registry.add(Form("%s/h9dDetectPropVsCentrality", kParticlenames[partID].data()), "h9dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); } else { - histos.add(Form("%s/h7dDetectPropVsCentrality", kParticlenames[partID].data()), "h7dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h7dDetectPropVsCentrality", kParticlenames[partID].data()), "h7dDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMapCoarse, axisITScluMapCoarse, axisDetMapCoarse, axisITScluMapCoarse, axisPtCoarse, axisInvMass.at(partID)}); } - histos.add(Form("%s/h5dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h5dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); - histos.add(Form("%s/h5dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h5dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); - histos.add(Form("%s/h5dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h5dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dPosDetectPropVsCentrality", kParticlenames[partID].data()), "h5dPosDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dNegDetectPropVsCentrality", kParticlenames[partID].data()), "h5dNegDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); + registry.add(Form("%s/h5dBachDetectPropVsCentrality", kParticlenames[partID].data()), "h5dBachDetectPropVsCentrality", kTHnSparseF, {axisDetectors.axisFT0ampl, axisDetMap, axisITScluMap, axisPtCoarse, axisInvMass.at(partID)}); } } template - void addHistograms(HistogramRegistry& histos) + void addHistograms(HistogramRegistry& registry) { - histos.add(Form("%s/h7dMass", kParticlenames[partID].data()), "h7dMass", kTHnSparseF, {axisDetectors.axisFT0ampl, axisPt, axisInvMass.at(partID), axisSelGap, axisNchInvMass, axisRap, axisEta}); - histos.add(Form("%s/h2dMass", kParticlenames[partID].data()), "h2dMass", kTH2F, {axisInvMass.at(partID), axisSelGap}); + registry.add(Form("%s/h7dMass", kParticlenames[partID].data()), "h7dMass", kTHnSparseF, {axisDetectors.axisFT0ampl, axisPt, axisInvMass.at(partID), axisSelGap, axisNchInvMass, axisRap, axisEta}); + registry.add(Form("%s/h2dMass", kParticlenames[partID].data()), "h2dMass", kTH2F, {axisInvMass.at(partID), axisSelGap}); if (doPlainTopoQA) { - addTopoHistograms(histos); + addTopoHistograms(registry); } if (PIDConfigurations.doTPCQA) { - addTPCQAHistograms(histos); + addTPCQAHistograms(registry); } if (PIDConfigurations.doTOFQA) { - addTOFQAHistograms(histos); + addTOFQAHistograms(registry); } if (doKienmaticQA) { - addKinematicQAHistograms(histos); + addKinematicQAHistograms(registry); } - addDetectorPropHistograms(histos); + addDetectorPropHistograms(registry); } template - void fillHistogramsV0(TCand cand, TCollision coll, int gap) + void fillHistogramsV0(const TCand& cand, const TCollision& coll, int gap) { float invMass = 0; float ft0ampl = -1.f; @@ -474,7 +512,7 @@ struct Derivedupcanalysis { ft0ampl = coll.totalFT0AmplitudeA(); } float pT = cand.pt(); - float rapidity = 1e6; + float rapidity = kDefaultInvalidRapidity; // c x tau float ctau = 0; @@ -495,8 +533,9 @@ struct Derivedupcanalysis { uint negDetMap = computeDetBitmap(negTrackExtra.detectorMap()); int negITSclusMap = computeITSclusBitmap(negTrackExtra.itsClusterMap(), negIsFromAfterburner); - if (partID == 0) { - histos.fill(HIST("generalQA/h2dArmenterosSelected"), cand.alpha(), cand.qtarm()); + if (partID == kK0ShortPartID) { + if (doPlainTopoQA) + histos.fill(HIST("generalQA/h2dArmenterosSelected"), cand.alpha(), cand.qtarm()); invMass = cand.mK0Short(); rapidity = cand.yK0Short(); ctau = cand.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassK0Short; @@ -508,7 +547,7 @@ struct Derivedupcanalysis { tpcNsigmaPos = posTrackExtra.tpcNSigmaPi(); tpcNsigmaNeg = negTrackExtra.tpcNSigmaPi(); } - } else if (partID == 1) { + } else if (partID == kLambdaPartID) { invMass = cand.mLambda(); rapidity = cand.yLambda(); ctau = cand.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassLambda0; @@ -520,7 +559,7 @@ struct Derivedupcanalysis { tpcNsigmaPos = posTrackExtra.tpcNSigmaPr(); tpcNsigmaNeg = negTrackExtra.tpcNSigmaPi(); } - } else if (partID == 2) { + } else if (partID == kAntiLambdaPartID) { invMass = cand.mAntiLambda(); rapidity = cand.yLambda(); ctau = cand.distovertotmom(coll.posX(), coll.posY(), coll.posZ()) * o2::constants::physics::MassLambda0Bar; @@ -554,13 +593,13 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hCtau"), pT, ctau); } - if (doDetectPropQA == 1) { + if (doDetectPropQA == kDetectPropQABase) { histos.fill(HIST(kParticlenames[partID]) + HIST("/h6dDetectPropVsCentrality"), ft0ampl, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pT); histos.fill(HIST(kParticlenames[partID]) + HIST("/h4dPosDetectPropVsCentrality"), ft0ampl, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pT); histos.fill(HIST(kParticlenames[partID]) + HIST("/h4dNegDetectPropVsCentrality"), ft0ampl, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pT); } - if (doDetectPropQA == 2) { - histos.fill(HIST(kParticlenames[partID]) + HIST("/h7dPosDetectPropVsCentrality"), ft0ampl, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pT, invMass); + if (doDetectPropQA == kDetectPropQAWithMass) { + histos.fill(HIST(kParticlenames[partID]) + HIST("/h7dDetectPropVsCentrality"), ft0ampl, posDetMap, posITSclusMap, negDetMap, negITSclusMap, pT, invMass); histos.fill(HIST(kParticlenames[partID]) + HIST("/h5dPosDetectPropVsCentrality"), ft0ampl, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pT, invMass); histos.fill(HIST(kParticlenames[partID]) + HIST("/h5dNegDetectPropVsCentrality"), ft0ampl, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pT, invMass); } @@ -571,8 +610,8 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegTPCsignalVsTrackPtot"), ft0ampl, cand.negativept() * std::cosh(cand.negativeeta()), negTrackExtra.tpcSignal()); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosTPCsignalVsTrackPt"), ft0ampl, cand.positivept(), posTrackExtra.tpcSignal()); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegTPCsignalVsTrackPt"), ft0ampl, cand.negativept(), negTrackExtra.tpcSignal()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPt"), ft0ampl, cand.positivept(), posTrackExtra.tpcNSigmaPi()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPt"), ft0ampl, cand.negativept(), negTrackExtra.tpcNSigmaPi()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPt"), ft0ampl, cand.positivept(), tpcNsigmaPos); + histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPt"), ft0ampl, cand.negativept(), tpcNsigmaNeg); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPCvsTrackPtot"), ft0ampl, cand.positivept() * std::cosh(cand.positiveeta()), tpcNsigmaPos); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegNsigmaTPCvsTrackPtot"), ft0ampl, cand.negativept() * std::cosh(cand.negativeeta()), tpcNsigmaNeg); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dPosNsigmaTPC"), ft0ampl, pT, tpcNsigmaPos); @@ -589,7 +628,7 @@ struct Derivedupcanalysis { } template - void fillHistogramsCasc(TCand cand, TCollision coll, const int gap) + void fillHistogramsCasc(const TCand& cand, const TCollision& coll, const int gap) { float invMass = 0; float centrality = -1.f; @@ -599,7 +638,7 @@ struct Derivedupcanalysis { centrality = coll.totalFT0AmplitudeA(); } float pT = cand.pt(); - float rapidity = 1e6; + float rapidity = kDefaultInvalidRapidity; // Access daughter tracks auto posTrackExtra = cand.template posTrackExtra_as(); @@ -630,9 +669,9 @@ struct Derivedupcanalysis { float tofDeltaTNeg = 0; float tofDeltaTBach = 0; - if (partID == 3) { + if (partID == kXiPartID) { invMass = cand.mXi(); - ctau = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / (totalMom * ctauxiPDG) : 1e6; + ctau = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yXi(); if (PIDConfigurations.doTPCQA) { @@ -645,9 +684,9 @@ struct Derivedupcanalysis { tofDeltaTNeg = cand.negTOFDeltaTXiPi(); tofDeltaTBach = cand.bachTOFDeltaTXiPi(); } - } else if (partID == 4) { + } else if (partID == kAntiXiPartID) { invMass = cand.mXi(); - ctau = totalMom != 0 ? o2::constants::physics::MassXiPlusBar * decayPos / (totalMom * ctauxiPDG) : 1e6; + ctau = totalMom != 0 ? o2::constants::physics::MassXiPlusBar * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yXi(); if (PIDConfigurations.doTPCQA) { @@ -661,9 +700,9 @@ struct Derivedupcanalysis { tofDeltaTBach = cand.bachTOFDeltaTXiPi(); } - } else if (partID == 5) { + } else if (partID == kOmegaPartID) { invMass = cand.mOmega(); - ctau = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / (totalMom * ctauomegaPDG) : 1e6; + ctau = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yOmega(); if (PIDConfigurations.doTPCQA) { @@ -672,14 +711,14 @@ struct Derivedupcanalysis { tpcNsigmaBach = bachTrackExtra.tpcNSigmaKa(); } if (PIDConfigurations.doTOFQA) { - tofDeltaTPos = cand.posTOFDeltaTOmPi(); - tofDeltaTNeg = cand.posTOFDeltaTOmPr(); + tofDeltaTPos = cand.posTOFDeltaTOmPr(); + tofDeltaTNeg = cand.negTOFDeltaTOmPi(); tofDeltaTBach = cand.bachTOFDeltaTOmKa(); } - } else if (partID == 6) { + } else if (partID == kAntiOmegaPartID) { invMass = cand.mOmega(); - ctau = totalMom != 0 ? o2::constants::physics::MassOmegaPlusBar * decayPos / (totalMom * ctauomegaPDG) : 1e6; + ctau = totalMom != 0 ? o2::constants::physics::MassOmegaPlusBar * decayPos / totalMom : kDefaultInvalidCtau; rapidity = cand.yOmega(); if (PIDConfigurations.doTPCQA) { @@ -688,8 +727,8 @@ struct Derivedupcanalysis { tpcNsigmaBach = bachTrackExtra.tpcNSigmaKa(); } if (PIDConfigurations.doTOFQA) { - tofDeltaTPos = cand.posTOFDeltaTOmPr(); - tofDeltaTNeg = cand.posTOFDeltaTOmPi(); + tofDeltaTPos = cand.posTOFDeltaTOmPi(); + tofDeltaTNeg = cand.negTOFDeltaTOmPr(); tofDeltaTBach = cand.bachTOFDeltaTOmKa(); } } @@ -705,8 +744,8 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/hCascCosPA"), pT, cand.casccosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST(kParticlenames[partID]) + HIST("/hDCACascDaughters"), pT, cand.dcacascdaughters()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hCascRadius"), pT, cand.cascradius()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/hMesonDCAToPV"), pT, cand.dcanegtopv()); - histos.fill(HIST(kParticlenames[partID]) + HIST("/hBaryonDCAToPV"), pT, cand.dcapostopv()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/hMesonDCAToPV"), pT, cand.sign() > 0 ? cand.dcapostopv() : cand.dcanegtopv()); + histos.fill(HIST(kParticlenames[partID]) + HIST("/hBaryonDCAToPV"), pT, cand.sign() > 0 ? cand.dcanegtopv() : cand.dcapostopv()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hBachDCAToPV"), pT, cand.dcabachtopv()); histos.fill(HIST(kParticlenames[partID]) + HIST("/hV0CosPA"), pT, cand.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST(kParticlenames[partID]) + HIST("/hV0Radius"), pT, cand.v0radius()); @@ -746,13 +785,13 @@ struct Derivedupcanalysis { histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dNegTOFdeltaTvsTrackPt"), centrality, cand.negativept(), tofDeltaTNeg); histos.fill(HIST(kParticlenames[partID]) + HIST("/h3dBachTOFdeltaTvsTrackPt"), centrality, cand.bachelorpt(), tofDeltaTBach); } - if (doDetectPropQA == 1) { + if (doDetectPropQA == kDetectPropQABase) { histos.fill(HIST(kParticlenames[partID]) + HIST("/h8dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, bachDetMap, bachITSclusMap, pT); histos.fill(HIST(kParticlenames[partID]) + HIST("/h4dPosDetectPropVsCentrality"), centrality, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pT); histos.fill(HIST(kParticlenames[partID]) + HIST("/h4dNegDetectPropVsCentrality"), centrality, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pT); histos.fill(HIST(kParticlenames[partID]) + HIST("/h4dBachDetectPropVsCentrality"), centrality, bachTrackExtra.detectorMap(), bachTrackExtra.itsClusterMap(), pT); } - if (doDetectPropQA == 2) { + if (doDetectPropQA == kDetectPropQAWithMass) { histos.fill(HIST(kParticlenames[partID]) + HIST("/h9dDetectPropVsCentrality"), centrality, posDetMap, posITSclusMap, negDetMap, negITSclusMap, bachDetMap, bachITSclusMap, pT, invMass); histos.fill(HIST(kParticlenames[partID]) + HIST("/h5dPosDetectPropVsCentrality"), centrality, posTrackExtra.detectorMap(), posTrackExtra.itsClusterMap(), pT, invMass); histos.fill(HIST(kParticlenames[partID]) + HIST("/h5dNegDetectPropVsCentrality"), centrality, negTrackExtra.detectorMap(), negTrackExtra.itsClusterMap(), pT, invMass); @@ -762,7 +801,7 @@ struct Derivedupcanalysis { void init(InitContext const&) { - if (doprocessV0s && doprocessCascades) { + if ((doprocessV0s && doprocessCascades) || (doprocessV0sMC && doprocessCascadesMC)) { LOG(fatal) << "Unable to analyze both v0s and cascades simultaneously. Please enable only one process at a time"; } @@ -807,7 +846,7 @@ struct Derivedupcanalysis { } else { setBits(maskTrackPropertiesV0, {selPosGoodTPCTrack, selPosGoodITSTrack}); // TPC signal is available: ask for positive track PID - if (PIDConfigurations.tpcPidNsigmaCut < 1e+5) { // safeguard for no cut + if (PIDConfigurations.tpcPidNsigmaCut < kNoPidNsigmaCut) { // safeguard for no cut maskK0ShortSpecific.set(selTPCPIDPositivePion); maskLambdaSpecific.set(selTPCPIDPositiveProton); maskAntiLambdaSpecific.set(selTPCPIDPositivePion); @@ -817,20 +856,14 @@ struct Derivedupcanalysis { maskOmegaSpecific.set(selTPCPIDPositiveProton); maskAntiOmegaSpecific.set(selTPCPIDPositivePion); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutK0Pi < 1e+5) { // safeguard for no cut - setBits(maskK0ShortSpecific, {selTOFNSigmaPositivePionK0Short, selTOFDeltaTPositivePionK0Short}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPr < 1e+5) { // safeguard for no cut - setBits(maskLambdaSpecific, {selTOFNSigmaPositiveProtonLambda, selTOFDeltaTPositiveProtonLambda}); - setBits(maskXiSpecific, {selTOFNSigmaPositiveProtonLambdaXi, selTOFDeltaTPositiveProtonLambdaXi}); - setBits(maskOmegaSpecific, {selTOFNSigmaPositiveProtonLambdaOmega, selTOFDeltaTPositiveProtonLambdaOmega}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPi < 1e+5) { // safeguard for no cut - setBits(maskAntiLambdaSpecific, {selTOFNSigmaPositivePionLambda, selTOFDeltaTPositivePionLambda}); - setBits(maskAntiXiSpecific, {selTOFNSigmaPositivePionLambdaXi, selTOFDeltaTPositivePionLambdaXi}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaPositivePionLambdaOmega, selTOFDeltaTPositivePionLambdaOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskK0ShortSpecific, PIDConfigurations.tofPidNsigmaCutK0Pi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionK0Short, selTOFDeltaTPositivePionK0Short); + setTOFSelection(maskLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambda, selTOFDeltaTPositiveProtonLambda); + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambdaXi, selTOFDeltaTPositiveProtonLambdaXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaPositiveProtonLambdaOmega, selTOFDeltaTPositiveProtonLambdaOmega); + setTOFSelection(maskAntiLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambda, selTOFDeltaTPositivePionLambda); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambdaXi, selTOFDeltaTPositivePionLambdaXi); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaPositivePionLambdaOmega, selTOFDeltaTPositivePionLambdaOmega); } // negative track if (TrackConfigurations.requireNegITSonly) { @@ -838,30 +871,24 @@ struct Derivedupcanalysis { } else { setBits(maskTrackPropertiesV0, {selNegGoodTPCTrack, selNegGoodITSTrack}); // TPC signal is available: ask for negative track PID - if (PIDConfigurations.tpcPidNsigmaCut < 1e+5) { // safeguard for no cut + if (PIDConfigurations.tpcPidNsigmaCut < kNoPidNsigmaCut) { // safeguard for no cut maskK0ShortSpecific.set(selTPCPIDNegativePion); maskLambdaSpecific.set(selTPCPIDNegativePion); maskAntiLambdaSpecific.set(selTPCPIDNegativeProton); maskXiSpecific.set(selTPCPIDNegativePion); - maskAntiXiSpecific.set(selTPCPIDPositiveProton); + maskAntiXiSpecific.set(selTPCPIDNegativeProton); maskOmegaSpecific.set(selTPCPIDNegativePion); - maskAntiOmegaSpecific.set(selTPCPIDPositiveProton); - } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutK0Pi < 1e+5) { // safeguard for no cut - setBits(maskK0ShortSpecific, {selTOFNSigmaNegativePionK0Short, selTOFDeltaTNegativePionK0Short}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPr < 1e+5) { // safeguard for no cut - setBits(maskAntiLambdaSpecific, {selTOFNSigmaNegativeProtonLambda, selTOFDeltaTNegativeProtonLambda}); - setBits(maskAntiXiSpecific, {selTOFNSigmaNegativeProtonLambdaXi, selTOFDeltaTNegativeProtonLambdaXi}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaNegativeProtonLambdaOmega, selTOFDeltaTNegativeProtonLambdaOmega}); - } - if (PIDConfigurations.tofPidNsigmaCutLaPi < 1e+5) { // safeguard for no cut - setBits(maskLambdaSpecific, {selTOFNSigmaNegativePionLambda, selTOFDeltaTNegativePionLambda}); - setBits(maskXiSpecific, {selTOFNSigmaNegativePionLambdaXi, selTOFDeltaTNegativePionLambdaXi}); - setBits(maskOmegaSpecific, {selTOFNSigmaNegativePionLambdaOmega, selTOFDeltaTNegativePionLambdaOmega}); + maskAntiOmegaSpecific.set(selTPCPIDNegativeProton); } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskK0ShortSpecific, PIDConfigurations.tofPidNsigmaCutK0Pi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionK0Short, selTOFDeltaTNegativePionK0Short); + setTOFSelection(maskAntiLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambda, selTOFDeltaTNegativeProtonLambda); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambdaXi, selTOFDeltaTNegativeProtonLambdaXi); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPr, PIDConfigurations.maxDeltaTimeProton, selTOFNSigmaNegativeProtonLambdaOmega, selTOFDeltaTNegativeProtonLambdaOmega); + setTOFSelection(maskLambdaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambda, selTOFDeltaTNegativePionLambda); + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambdaXi, selTOFDeltaTNegativePionLambdaXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutLaPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaNegativePionLambdaOmega, selTOFDeltaTNegativePionLambdaOmega); } // bachelor track maskTrackPropertiesCasc = maskTrackPropertiesV0; @@ -870,21 +897,17 @@ struct Derivedupcanalysis { } else { setBits(maskTrackPropertiesCasc, {selBachGoodTPCTrack, selBachGoodITSTrack}); // TPC signal is available: ask for positive track PID - if (PIDConfigurations.tpcPidNsigmaCut < 1e+5) { // safeguard for no cut + if (PIDConfigurations.tpcPidNsigmaCut < kNoPidNsigmaCut) { // safeguard for no cut maskXiSpecific.set(selTPCPIDBachPion); maskAntiXiSpecific.set(selTPCPIDBachPion); maskOmegaSpecific.set(selTPCPIDBachKaon); maskAntiOmegaSpecific.set(selTPCPIDBachKaon); } - // TOF PID - if (PIDConfigurations.tofPidNsigmaCutXiPi < 1e+5) { // safeguard for no cut - setBits(maskXiSpecific, {selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi}); - setBits(maskAntiXiSpecific, {selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi}); - } - if (PIDConfigurations.tofPidNsigmaCutOmegaKaon < 1e+5) { // safeguard for no cut - setBits(maskOmegaSpecific, {selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega}); - setBits(maskAntiOmegaSpecific, {selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega}); - } + // TOF PID: nsigma and time selections can be enabled independently. + setTOFSelection(maskXiSpecific, PIDConfigurations.tofPidNsigmaCutXiPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi); + setTOFSelection(maskAntiXiSpecific, PIDConfigurations.tofPidNsigmaCutXiPi, PIDConfigurations.maxDeltaTimePion, selTOFNSigmaBachPionXi, selTOFDeltaTBachPionXi); + setTOFSelection(maskOmegaSpecific, PIDConfigurations.tofPidNsigmaCutOmegaKaon, PIDConfigurations.maxDeltaTimeKaon, selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega); + setTOFSelection(maskAntiOmegaSpecific, PIDConfigurations.tofPidNsigmaCutOmegaKaon, PIDConfigurations.maxDeltaTimeKaon, selTOFNSigmaBachKaonOmega, selTOFDeltaTBachKaonOmega); } if (TrackConfigurations.skipTPConly) { @@ -924,8 +947,8 @@ struct Derivedupcanalysis { histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(10, "kNoSameBunchPileup"); histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(11, "kNoCollInTimeRangeStd"); histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeNarrow"); - histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(13, "Below min occup."); - histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(14, "Above max occup."); + histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(13, "Above min occup."); + histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(14, "Below max occup."); histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(15, "RCTFlagsChecker"); histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(16, "isUPC"); histos.get(HIST("eventQA/hEventSelection"))->GetXaxis()->SetBinLabel(17, "has UPC flag"); @@ -982,6 +1005,14 @@ struct Derivedupcanalysis { histos.add("eventQA/mc/hSelGeneratorsId", "hSelGeneratorsId", kTH1D, {axisGeneratorIds}); } + if (doprocessV0sMC || doprocessCascadesMC || doprocessGenerated) { + histos.add("generalQA/mc/hInvalidMCReferences", "Out-of-range MC references; Reference type; Entries", kTH1D, {{7, -0.5, 6.5}}); + const std::array referenceLabels{"Vertex collision", "V0 core", "V0 collision", "Cascade core", "Cascade collision", "Generated V0 collision", "Generated cascade collision"}; + for (std::size_t i = 0; i < referenceLabels.size(); ++i) { + histos.get(HIST("generalQA/mc/hInvalidMCReferences"))->GetXaxis()->SetBinLabel(i + 1, referenceLabels[i]); + } + } + if (doprocessV0sMC || doprocessCascadesMC) { // Event QA histos.add("eventQA/mc/hFakeEvents", "hFakeEvents", {kTH1D, {{1, -0.5f, 0.5f}}}); @@ -993,16 +1024,29 @@ struct Derivedupcanalysis { } if (doprocessV0sMC) { - if (analyseLambda && calculateFeeddownMatrix) - histos.add(Form("%s/h3dLambdaFeeddown", kParticlenames[1].data()), "h3dLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); - if (analyseAntiLambda && calculateFeeddownMatrix) - histos.add(Form("%s/h3dAntiLambdaFeeddown", kParticlenames[2].data()), "h3dAntiLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); + histos.add("generalQA/mc/h3dV0CollisionAssociation", "V0 truth candidates before candidate selection; Candidate generator; Vertex generator; Same MC collision", kTH3D, {axisGeneratorIds, axisGeneratorIds, {2, -0.5f, 1.5f}}); + if (analyseLambda && calculateFeeddownMatrix) { + histos.add(Form("%s/h3dLambdaFeeddown", kParticlenames[kLambdaPartID].data()), "h3dLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); + histos.add(Form("%s/h5dLambdaFeeddown", kParticlenames[kLambdaPartID].data()), "h5dLambdaFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisPt, axisSelGap, axisInvMass.at(kLambdaPartID)}); + } + if (analyseAntiLambda && calculateFeeddownMatrix) { + histos.add(Form("%s/h3dAntiLambdaFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h3dAntiLambdaFeeddown", kTH3F, {axisNTracksGlobal, axisPt, axisPt}); + histos.add(Form("%s/h5dAntiLambdaFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h5dAntiLambdaFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisPt, axisSelGap, axisInvMass.at(kAntiLambdaPartID)}); + } + } + + if (doprocessCascadesMC) { + histos.add("generalQA/mc/h3dCascadeCollisionAssociation", "Cascade truth candidates before candidate selection; Candidate generator; Vertex generator; Same MC collision", kTH3D, {axisGeneratorIds, axisGeneratorIds, {2, -0.5f, 1.5f}}); } if (doprocessGenerated) { - for (int partID = 0; partID <= 6; partID++) { + for (int partID = kK0ShortPartID; partID <= kAntiOmegaPartID; partID++) { histos.add(Form("%s/mc/h7dGen", kParticlenames[partID].data()), "h7dGen", kTHnSparseF, {axisDetectors.axisFT0ampl, axisNchInvMass, axisNchInvMass, axisPt, axisSelGap, axisRap, axisGeneratorIds}); } + if (analyseLambda && calculateFeeddownMatrix) + histos.add(Form("%s/mc/h4dGenXiFeeddown", kParticlenames[kLambdaPartID].data()), "h4dGenXiFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisRap, axisGeneratorIds}); + if (analyseAntiLambda && calculateFeeddownMatrix) + histos.add(Form("%s/mc/h4dGenAntiXiFeeddown", kParticlenames[kAntiLambdaPartID].data()), "h4dGenAntiXiFeeddown", kTHnSparseF, {axisNTracksGlobal, axisPt, axisRap, axisGeneratorIds}); } if (doprocessV0s || doprocessV0sMC) { @@ -1023,21 +1067,24 @@ struct Derivedupcanalysis { // K0s if (analyseK0Short) { - addHistograms<0>(histos); + addHistograms(histos); } // Lambda if (analyseLambda) { - addHistograms<1>(histos); + addHistograms(histos); } // Anti-Lambda if (analyseAntiLambda) { - addHistograms<2>(histos); + addHistograms(histos); } } if (doprocessCascades || doprocessCascadesMC) { + histos.add("eventQA/hCascadeChargeSelection", "Selected Xi charge; Charge; Entries", kTH1D, {{2, -0.5f, 1.5f}}); + histos.get(HIST("eventQA/hCascadeChargeSelection"))->GetXaxis()->SetBinLabel(1, "Xi-"); + histos.get(HIST("eventQA/hCascadeChargeSelection"))->GetXaxis()->SetBinLabel(2, "AntiXi+"); // For all candidates if (doPlainTopoQA) { histos.add("generalQA/hPt", "hPt", kTH1F, {axisPtCoarse}); @@ -1059,22 +1106,22 @@ struct Derivedupcanalysis { // Xi if (analyseXi) { - addHistograms<3>(histos); + addHistograms(histos); } // Anti-Xi if (analyseAntiXi) { - addHistograms<4>(histos); + addHistograms(histos); } // Omega if (analyseOmega) { - addHistograms<5>(histos); + addHistograms(histos); } // Anti-Omega if (analyseAntiOmega) { - addHistograms<6>(histos); + addHistograms(histos); } } @@ -1097,14 +1144,10 @@ struct Derivedupcanalysis { const float timeZNC = collision.timeZNC(); const float cut = upcCuts.zdcTimeCut; - auto isInvalidTime = [](float time) { - return !std::isfinite(time) || (std::abs(time) == 999.f); - }; - - const bool gapA = isInvalidTime(timeZNA) || (std::abs(timeZNA) > cut); - const bool gapC = isInvalidTime(timeZNC) || (std::abs(timeZNC) > cut); - const bool neutronA = !isInvalidTime(timeZNA) && (std::abs(timeZNA) < cut); - const bool neutronC = !isInvalidTime(timeZNC) && (std::abs(timeZNC) < cut); + const bool gapA = hasNoZdcNeutrons(timeZNA, cut); + const bool gapC = hasNoZdcNeutrons(timeZNC, cut); + const bool neutronA = hasZdcNeutrons(timeZNA, cut); + const bool neutronC = hasZdcNeutrons(timeZNC, cut); if (selGapSide == o2::aod::sgselector::SingleGapA) { // 0nXn if (!(gapA && neutronC)) { @@ -1124,29 +1167,29 @@ struct Derivedupcanalysis { return selGapSide; } - bool isInvalidTime(float time) const + bool hasNoZdcNeutrons(float time, float cut) const { - return !std::isfinite(time) || (std::abs(time) >= 998.f); + return (time != kMissingSignal) && (std::abs(time) > cut); } - bool isTimingCutEnabled(float cut) const + bool hasZdcNeutrons(float time, float cut) const { - return cut >= 0.f; + return std::abs(time) < cut; } - bool isTimingGap(float time, float cut) const + bool isTimingCutEnabled(float cut, bool requireTiming) const { - return isInvalidTime(time) || (std::abs(time) > cut); + return requireTiming && cut >= 0.f; } - bool isTimingActivity(float time, float cut) const + bool isTimingGap(float time, float cut) const { - return !isInvalidTime(time) && (std::abs(time) < cut); + return (time != kMissingSignal) && (std::abs(time) > cut); } - bool hasFT0Activity(uint8_t triggerMask, uint8_t bit) const + bool isTimingActivity(float time, float cut) const { - return (triggerMask & (static_cast(1u) << bit)) != 0; + return std::abs(time) < cut; } template @@ -1158,29 +1201,26 @@ struct Derivedupcanalysis { return selGapSide; } - const bool useFDDA = isTimingCutEnabled(upcCuts.fddaTimeCut); - const bool useFDDC = isTimingCutEnabled(upcCuts.fddcTimeCut); - const bool useFV0A = isTimingCutEnabled(upcCuts.fv0aTimeCut); - const bool useFT0A = isTimingCutEnabled(upcCuts.ft0aTimeCut); - const bool useFT0C = isTimingCutEnabled(upcCuts.ft0cTimeCut); + const bool useFDDA = isTimingCutEnabled(upcCuts.fddaTimeCut, upcCuts.requireFDDATiming); + const bool useFDDC = isTimingCutEnabled(upcCuts.fddcTimeCut, upcCuts.requireFDDCTiming); + const bool useFV0A = isTimingCutEnabled(upcCuts.fv0aTimeCut, upcCuts.requireFV0ATiming); + const bool useFT0A = isTimingCutEnabled(upcCuts.ft0aTimeCut, upcCuts.requireFT0ATiming); + const bool useFT0C = isTimingCutEnabled(upcCuts.ft0cTimeCut, upcCuts.requireFT0CTiming); if (!(useFDDA || useFDDC || useFV0A || useFT0A || useFT0C)) { return selGapSide; } - const bool ft0ActiveA = hasFT0Activity(collision.triggerMaskFT0(), kFT0TriggerBitIsActiveA); - const bool ft0ActiveC = hasFT0Activity(collision.triggerMaskFT0(), kFT0TriggerBitIsActiveC); - const bool gapFDDA = !useFDDA || isTimingGap(collision.timeFDDA(), upcCuts.fddaTimeCut); const bool actFDDA = !useFDDA || isTimingActivity(collision.timeFDDA(), upcCuts.fddaTimeCut); const bool gapFDDC = !useFDDC || isTimingGap(collision.timeFDDC(), upcCuts.fddcTimeCut); const bool actFDDC = !useFDDC || isTimingActivity(collision.timeFDDC(), upcCuts.fddcTimeCut); const bool gapFV0A = !useFV0A || isTimingGap(collision.timeFV0A(), upcCuts.fv0aTimeCut); const bool actFV0A = !useFV0A || isTimingActivity(collision.timeFV0A(), upcCuts.fv0aTimeCut); - const bool gapFT0A = !useFT0A || !ft0ActiveA || isTimingGap(collision.timeFT0A(), upcCuts.ft0aTimeCut); - const bool actFT0A = !useFT0A || (ft0ActiveA && isTimingActivity(collision.timeFT0A(), upcCuts.ft0aTimeCut)); - const bool gapFT0C = !useFT0C || !ft0ActiveC || isTimingGap(collision.timeFT0C(), upcCuts.ft0cTimeCut); - const bool actFT0C = !useFT0C || (ft0ActiveC && isTimingActivity(collision.timeFT0C(), upcCuts.ft0cTimeCut)); + const bool gapFT0A = !useFT0A || isTimingGap(collision.timeFT0A(), upcCuts.ft0aTimeCut); + const bool actFT0A = !useFT0A || isTimingActivity(collision.timeFT0A(), upcCuts.ft0aTimeCut); + const bool gapFT0C = !useFT0C || isTimingGap(collision.timeFT0C(), upcCuts.ft0cTimeCut); + const bool actFT0C = !useFT0C || isTimingActivity(collision.timeFT0C(), upcCuts.ft0cTimeCut); if (selGapSide == o2::aod::sgselector::SingleGapA) { if (!(gapFV0A && gapFDDA && gapFT0A && actFDDC && actFT0C)) { @@ -1199,21 +1239,81 @@ struct Derivedupcanalysis { return selGapSide; } + template + int getGapSideWithoutRecoZDC(TCollision const& collision) + { + int selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, std::numeric_limits::infinity()); + return applyFITTiming(selGapSide, collision); + } + + template + bool generatedNeutronsMatchGapSide(int selGapSide, TNeutrons const& neutrons) + { + bool neutronA = false; + bool neutronC = false; + for (const auto& neutron : neutrons) { + neutronA = neutronA || (neutron.eta() > neutronEtaCut); + neutronC = neutronC || (neutron.eta() < -neutronEtaCut); + } + + if (selGapSide == o2::aod::sgselector::SingleGapA) { // 0nXn + return !neutronA && neutronC; + } + if (selGapSide == o2::aod::sgselector::SingleGapC) { // Xn0n + return neutronA && !neutronC; + } + if (selGapSide == o2::aod::sgselector::DoubleGap) { + return !neutronA && !neutronC; + } + + return false; + } + + template + int applyGeneratedNeutronSelection(int selGapSide, TNeutrons const& neutrons) + { + if (!checkNeutronsInMC) { + return selGapSide; + } + if (selGapSide != o2::aod::sgselector::SingleGapA && + selGapSide != o2::aod::sgselector::SingleGapC && + selGapSide != o2::aod::sgselector::DoubleGap) { + return selGapSide; + } + + if (!generatedNeutronsMatchGapSide(selGapSide, neutrons)) { + selGapSide = o2::aod::sgselector::NoGap; + } + + return selGapSide; + } + + template + int getGapSideMC(TCollision const& collision, TNeutrons const& neutrons) + { + return applyGeneratedNeutronSelection(getGapSideWithoutRecoZDC(collision), neutrons); + } + + template + bool acceptGeneratedNeutronSelection(TNeutrons const& neutrons) + { + return !requireGeneratedZDCNeutrons || generatedNeutronsMatchGapSide(static_cast(upcCuts.genGapSide), neutrons); + } + template int getGapSide(TCollision const& collision) { - int selGapSide = o2::aod::sgselector::NoGap; - selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, upcCuts.zdc); + int selGapSide = sgSelector.trueGap(collision, upcCuts.fv0a, upcCuts.ft0a, upcCuts.ft0c, upcCuts.zdc); selGapSide = applyZDCTiming(selGapSide, collision); return applyFITTiming(selGapSide, collision); } float sanitizeZdcTime(float time) const { if (!std::isfinite(time)) { - return -12.f; + return kInvalidZdcTime; } - if (std::abs(time) >= 998.f) { - return -11.f; + if (std::abs(time) >= kMissingSignalThreshold) { + return kMissingZdcTime; } return time; } @@ -1221,10 +1321,10 @@ struct Derivedupcanalysis { float sanitizeFITTime(float time) const { if (!std::isfinite(time)) { - return -42.f; + return kInvalidFITTime; } - if (std::abs(time) >= 998.f) { - return -41.f; + if (std::abs(time) >= kMissingSignalThreshold) { + return kMissingFITTime; } return time; } @@ -1281,9 +1381,9 @@ struct Derivedupcanalysis { histos.fill(HIST("eventQA/hZN"), -1, znc, gap); } else if (znc == -inf_f) { histos.fill(HIST("eventQA/hZN"), zna, -1, gap); - } else if (zna == -999 && znc == -999) { + } else if (zna == kMissingSignal && znc == kMissingSignal) { histos.fill(HIST("eventQA/hZN"), -2, -2, gap); - } else if (zna == -999 || znc == -999) { + } else if (zna == kMissingSignal || znc == kMissingSignal) { LOG(warning) << "Only one ZDC signal is -999"; } else { histos.fill(HIST("eventQA/hZN"), zna, znc, gap); @@ -1342,10 +1442,11 @@ struct Derivedupcanalysis { } // Additional check for UPC collision flag - if (evSels.useUPCflag && collision.flags() < 1) { + const bool hasUPCFlag = (collision.flags() & o2::dataformats::Vertex>::Flags::UPCMode) != 0; + if (evSels.useUPCflag && !hasUPCFlag) { return false; } - if (collision.flags() >= 1 && fillQA) { + if (hasUPCFlag && fillQA) { histos.fill(HIST("eventQA/hEventSelection"), 16.0); // UPC event } @@ -1433,8 +1534,8 @@ struct Derivedupcanalysis { // c x tau float decayPos = std::hypot(casc.x() - coll.posX(), casc.y() - coll.posY(), casc.z() - coll.posZ()); float totalMom = std::hypot(casc.px(), casc.py(), casc.pz()); - float ctauXi = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / totalMom : 1e6; - float ctauOmega = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / totalMom : 1e6; + float ctauXi = totalMom != 0 ? o2::constants::physics::MassXiMinus * decayPos / totalMom : kDefaultInvalidCtau; + float ctauOmega = totalMom != 0 ? o2::constants::physics::MassOmegaMinus * decayPos / totalMom : kDefaultInvalidCtau; std::bitset bitMap = 0; @@ -1497,9 +1598,9 @@ struct Derivedupcanalysis { if (std::fabs(rapidityOmega) < rapidityCut) bitMap.set(selOmegaRapidity); if (std::fabs(poseta) < daughterEtaCut) - bitMap.set(selNegEta); - if (std::fabs(negeta) < daughterEtaCut) bitMap.set(selPosEta); + if (std::fabs(negeta) < daughterEtaCut) + bitMap.set(selNegEta); if (std::fabs(bacheta) < daughterEtaCut) bitMap.set(selBachEta); @@ -1593,8 +1694,8 @@ struct Derivedupcanalysis { bitMap.set(selTOFNSigmaPositiveProtonLambdaXi); } if (std::fabs(casc.tofNSigmaOmLaPr()) < PIDConfigurations.tofPidNsigmaCutLaPr) { - bitMap.set(selTOFNSigmaNegativePionLambdaOmega); - bitMap.set(selTOFNSigmaPositivePionLambdaOmega); + bitMap.set(selTOFNSigmaNegativeProtonLambdaOmega); + bitMap.set(selTOFNSigmaPositiveProtonLambdaOmega); } // bachelor track if (std::fabs(casc.tofNSigmaXiPi()) < PIDConfigurations.tofPidNsigmaCutXiPi) { @@ -1754,7 +1855,7 @@ struct Derivedupcanalysis { bitMap.set(selK0ShortCTau); // armenteros - if (v0.qtarm() * v0cuts.armPodCut > std::fabs(v0.alpha()) || v0cuts.armPodCut < 1e-4) + if (v0.qtarm() * v0cuts.armPodCut > std::fabs(v0.alpha()) || v0cuts.armPodCut < kDisabledArmenterosCut) bitMap.set(selK0ShortArmenteros); return bitMap; @@ -1773,8 +1874,8 @@ struct Derivedupcanalysis { histos.fill(HIST("generalQA/hCascCosPA"), casc.pt(), casc.casccosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST("generalQA/hDCACascDaughters"), casc.pt(), casc.dcacascdaughters()); histos.fill(HIST("generalQA/hCascRadius"), casc.pt(), casc.cascradius()); - histos.fill(HIST("generalQA/hMesonDCAToPV"), casc.pt(), casc.dcanegtopv()); - histos.fill(HIST("generalQA/hBaryonDCAToPV"), casc.pt(), casc.dcapostopv()); + histos.fill(HIST("generalQA/hMesonDCAToPV"), casc.pt(), casc.sign() > 0 ? casc.dcapostopv() : casc.dcanegtopv()); + histos.fill(HIST("generalQA/hBaryonDCAToPV"), casc.pt(), casc.sign() > 0 ? casc.dcanegtopv() : casc.dcapostopv()); histos.fill(HIST("generalQA/hBachDCAToPV"), casc.pt(), casc.dcabachtopv()); histos.fill(HIST("generalQA/hV0CosPA"), casc.pt(), casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); histos.fill(HIST("generalQA/hV0Radius"), casc.pt(), casc.v0radius()); @@ -1787,22 +1888,24 @@ struct Derivedupcanalysis { } // Xi - if (verifyMask(selMap, maskSelectionXi) && analyseXi) { + if (casc.sign() < 0 && verifyMask(selMap, maskSelectionXi) && analyseXi) { + histos.fill(HIST("eventQA/hCascadeChargeSelection"), 0.); fillHistogramsCasc<3>(casc, coll, gap); } // Anti-Xi - if (verifyMask(selMap, maskSelectionAntiXi) && analyseAntiXi) { + if (casc.sign() > 0 && verifyMask(selMap, maskSelectionAntiXi) && analyseAntiXi) { + histos.fill(HIST("eventQA/hCascadeChargeSelection"), 1.); fillHistogramsCasc<4>(casc, coll, gap); } // Omega - if (verifyMask(selMap, maskSelectionOmega) && analyseOmega) { + if (casc.sign() < 0 && verifyMask(selMap, maskSelectionOmega) && analyseOmega) { fillHistogramsCasc<5>(casc, coll, gap); } // Anti-Omega - if (verifyMask(selMap, maskSelectionAntiOmega) && analyseAntiOmega) { + if (casc.sign() > 0 && verifyMask(selMap, maskSelectionAntiOmega) && analyseAntiOmega) { fillHistogramsCasc<6>(casc, coll, gap); } } @@ -1856,13 +1959,13 @@ struct Derivedupcanalysis { const bool isPositiveProton = (pdgPos == PDG_t::kProton); - const bool isPositivePion = (pdgPos == PDG_t::kPiPlus); - const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus); + const bool isPositivePion = (pdgPos == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgPos == PDG_t::kMuonPlus); + const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonPlus); const bool isNegativeProton = (pdgNeg == kProtonBar); - const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus); - const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus); + const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgNeg == PDG_t::kMuonMinus); + const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonMinus); const bool isBachelorPositiveKaon = (pdgBach == PDG_t::kKPlus); const bool isBachelorNegativeKaon = (pdgBach == PDG_t::kKMinus); @@ -1916,10 +2019,9 @@ struct Derivedupcanalysis { histos.fill(HIST("generalQA/hV0Radius"), v0.v0radius()); histos.fill(HIST("generalQA/h2dPositiveITSvsTPCpts"), posTrackExtra.tpcCrossedRows(), posTrackExtra.itsNCls()); histos.fill(HIST("generalQA/h2dNegativeITSvsTPCpts"), negTrackExtra.tpcCrossedRows(), negTrackExtra.itsNCls()); + histos.fill(HIST("generalQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); } - histos.fill(HIST("generalQA/h2dArmenterosAll"), v0.alpha(), v0.qtarm()); - // K0s if (verifyMask(selMap, maskSelectionK0Short) && analyseK0Short) { fillHistogramsV0<0>(v0, coll, gap); @@ -1946,6 +2048,9 @@ struct Derivedupcanalysis { std::vector listBestCollisionIds(mcCollisions.size(), -1); for (auto const& mcCollision : mcCollisions) { + if (std::abs(mcCollision.posZ()) > maxZVtxPosition) + continue; + if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { continue; } @@ -1953,6 +2058,9 @@ struct Derivedupcanalysis { // Group collisions and neutrons by MC collision index auto groupedCollisions = collisions.sliceBy(perMcCollision, mcCollision.globalIndex()); auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) + continue; + // Find the collision with the biggest nbr of PV contributors // Follows what was done here: https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/mcCollsExtra.cxx#L93 int biggestNContribs = -1; @@ -1962,31 +2070,7 @@ struct Derivedupcanalysis { continue; } - int selGapSide = collision.isUPC() ? getGapSide(collision) : -1; - if (checkNeutronsInMC) { - for (const auto& neutron : groupedNeutrons) { - if (selGapSide < -0.5) - break; - - const float eta = neutron.eta(); - switch (selGapSide) { - case 0: // SGA - if (eta > neutronEtaCut) - selGapSide = -1; - break; - case 1: // SGC - if (eta < -neutronEtaCut) - selGapSide = -1; - break; - case 2: // DG - if (eta > neutronEtaCut) - selGapSide = 1; - else if (eta < -neutronEtaCut) - selGapSide = 0; - break; - } - } - } + int selGapSide = collision.isUPC() ? getGapSideMC(collision, groupedNeutrons) : -1; if (evSels.studyUPConly && (selGapSide != static_cast(upcCuts.genGapSide))) continue; @@ -2014,6 +2098,10 @@ struct Derivedupcanalysis { continue; } + auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) + continue; + histos.fill(HIST("eventQA/mc/hSelGeneratorsId"), mcCollision.generatorsID()); histos.fill(HIST("eventQA/mc/hEventSelectionMC"), 0.0, mcCollision.multMCNParticlesEta08(), mcCollision.generatorsID()); @@ -2025,7 +2113,6 @@ struct Derivedupcanalysis { // Group collisions and neutrons by MC collision index auto groupedCollisions = collisions.sliceBy(perMcCollision, mcCollision.globalIndex()); - auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); bool atLeastOne = false; float centrality = -1.f; @@ -2040,31 +2127,7 @@ struct Derivedupcanalysis { continue; } - int selGapSide = collision.isUPC() ? getGapSide(collision) : -1; - if (checkNeutronsInMC) { - for (const auto& neutron : groupedNeutrons) { - if (selGapSide < -0.5) - break; - - const float eta = neutron.eta(); - switch (selGapSide) { - case 0: // SGA - if (eta > neutronEtaCut) - selGapSide = -1; - break; - case 1: // SGC - if (eta < -neutronEtaCut) - selGapSide = -1; - break; - case 2: // DG - if (eta > neutronEtaCut) - selGapSide = 1; - else if (eta < -neutronEtaCut) - selGapSide = 0; - break; - } - } - } + int selGapSide = collision.isUPC() ? getGapSideMC(collision, groupedNeutrons) : -1; const bool passStd = !evSels.studyUPConly || (selGapSide == static_cast(upcCuts.genGapSide)); if (passStd) { @@ -2101,19 +2164,19 @@ struct Derivedupcanalysis { } template - void fillFeeddownMatrix(TCollision const& collision, TV0 const& v0, std::bitset const& selMap) + void fillFeeddownMatrix(TCollision const& collision, TV0 const& v0, std::bitset const& selMap, int gap) { if (!v0.has_motherMCPart()) { return; } const auto v0mother = v0.template motherMCPart_as(); - if (v0mother.size() < 1) { + if (v0mother.size() == 0) { return; } const float rapidityXi = RecoDecay::y(std::array{v0mother.px(), v0mother.py(), v0mother.pz()}, o2::constants::physics::MassXiMinus); - if (std::fabs(rapidityXi) > 0.5f) { + if (std::fabs(rapidityXi) > kMotherRapidityMax) { return; } @@ -2124,11 +2187,13 @@ struct Derivedupcanalysis { if (analyseLambda && verifyMask(selMap, secondaryMaskSelectionLambda) && (v0mother.pdgCode() == PDG_t::kXiMinus) && v0mother.isPhysicalPrimary()) { histos.fill(HIST(kParticlenames[1]) + HIST("/h3dLambdaFeeddown"), mult, v0pt, motherPt); + histos.fill(HIST(kParticlenames[1]) + HIST("/h5dLambdaFeeddown"), mult, v0pt, motherPt, gap, v0.mLambda()); } if (analyseAntiLambda && verifyMask(selMap, secondaryMaskSelectionAntiLambda) && (v0mother.pdgCode() == PDG_t::kXiPlusBar) && v0mother.isPhysicalPrimary()) { histos.fill(HIST(kParticlenames[2]) + HIST("/h3dAntiLambdaFeeddown"), mult, v0pt, motherPt); + histos.fill(HIST(kParticlenames[2]) + HIST("/h5dAntiLambdaFeeddown"), mult, v0pt, motherPt, gap, v0.mAntiLambda()); } } @@ -2149,8 +2214,8 @@ struct Derivedupcanalysis { } histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); - int selGapSide = collision.isUPC() ? getGapSide(collision) : -1; - if (evSels.studyUPConly && (selGapSide < -0.5)) + int selGapSide = collision.isUPC() ? getGapSide(collision) : o2::aod::sgselector::NoGap; + if (evSels.studyUPConly && (selGapSide < 0)) continue; fillHistogramsQA(collision, selGapSide); @@ -2173,12 +2238,25 @@ struct Derivedupcanalysis { } } + bool hasValidMCReference(int64_t index, int64_t tableSize, int referenceType) + { + if (index < 0) { + return false; + } + // has_*() only checks the sign; older derived files can contain stale positive indices. + if (index >= tableSize) { + histos.fill(HIST("generalQA/mc/hInvalidMCReferences"), referenceType); + return false; + } + return true; + } + void processV0sMC(StraCollisonsFullMC const& collisions, V0CandidatesMC const& fullV0s, DauTracks const&, aod::MotherMCParts const&, - StraMCCollisionsFull const&, - V0MCCoresFull const&, + StraMCCollisionsFull const& mcCollisions, + V0MCCoresFull const& v0MCCores, NeutronsMC const& neutrons) { v0sGrouped.clear(); @@ -2188,65 +2266,51 @@ struct Derivedupcanalysis { } for (const auto& collision : collisions) { - if (!collision.has_straMCCollision()) { + if (!hasValidMCReference(collision.straMCCollisionId(), mcCollisions.size(), 0)) { histos.fill(HIST("eventQA/mc/hFakeEvents"), 0); // no assoc. MC collisions continue; } const auto& mcCollision = collision.straMCCollision_as(); // take gen. collision associated to the rec. collision - if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { + // Vertex labels classify event QA, not the origin of particles at this vertex. + const bool selectedVertexGenerator = std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) != generatorIds->end(); + if (!doMCAssociation && !selectedVertexGenerator) { continue; } - if (!acceptEvent(collision, true)) { + if (!acceptEvent(collision, selectedVertexGenerator)) { continue; } // event is accepted - if (collision.isUPC()) { - fillPreSelTimingHistograms(collision); + if (selectedVertexGenerator) { + if (collision.isUPC()) { + fillPreSelTimingHistograms(collision); + } + histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); } - histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); - - int selGapSide = collision.isUPC() ? getGapSide(collision) : -1; - int selGapSideNoNeutrons = selGapSide; auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); - if (checkNeutronsInMC) { - for (const auto& neutron : groupedNeutrons) { - if (selGapSide < -0.5) - break; - - const float eta = neutron.eta(); - switch (selGapSide) { - case 0: // SGA - if (eta > neutronEtaCut) - selGapSide = -1; - break; - case 1: // SGC - if (eta < -neutronEtaCut) - selGapSide = -1; - break; - case 2: // DG - if (eta > neutronEtaCut) - selGapSide = 1; - else if (eta < -neutronEtaCut) - selGapSide = 0; - break; - } - } - } + int selGapSideNoNeutrons = collision.isUPC() ? getGapSideWithoutRecoZDC(collision) : o2::aod::sgselector::NoGap; - if (evSels.studyUPConly && (selGapSide < -0.5)) + if (evSels.studyUPConly && (selGapSideNoNeutrons < 0)) continue; - histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - fillHistogramsQA(collision, selGapSide); + if (selectedVertexGenerator) + histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + int selGapSide = applyGeneratedNeutronSelection(selGapSideNoNeutrons, groupedNeutrons); + + if (evSels.studyUPConly && (selGapSide < 0)) + continue; + + if (selectedVertexGenerator) { + fillHistogramsQA(collision, selGapSide); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + } std::size_t nV0sThisColl = v0sGrouped[collision.globalIndex()].size(); @@ -2258,11 +2322,17 @@ struct Derivedupcanalysis { std::bitset selMap = computeBitmapV0(v0, collision); if (doMCAssociation) { - if (v0.has_v0MCCore()) { + if (hasValidMCReference(v0.v0MCCoreId(), v0MCCores.size(), 1)) { const auto& v0MC = v0.v0MCCore_as(); - computeV0MCAssociation(v0MC, selMap); - if (calculateFeeddownMatrix) { - fillFeeddownMatrix(collision, v0, selMap); + if (hasValidMCReference(v0MC.straMCCollisionId(), mcCollisions.size(), 2) && v0MC.pdgCode() != -1) { + const auto& candidateCollision = v0MC.straMCCollision_as(); + if (std::find(generatorIds->begin(), generatorIds->end(), candidateCollision.generatorsID()) != generatorIds->end()) { + histos.fill(HIST("generalQA/mc/h3dV0CollisionAssociation"), candidateCollision.generatorsID(), mcCollision.generatorsID(), v0MC.straMCCollisionId() == mcCollision.globalIndex()); + computeV0MCAssociation(v0MC, selMap); + if (calculateFeeddownMatrix) { + fillFeeddownMatrix(collision, v0, selMap, selGapSide); + } + } } } } else { @@ -2296,8 +2366,8 @@ struct Derivedupcanalysis { } histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); - int selGapSide = collision.isUPC() ? getGapSide(collision) : -1; - if (evSels.studyUPConly && (selGapSide < -0.5)) + int selGapSide = collision.isUPC() ? getGapSide(collision) : o2::aod::sgselector::NoGap; + if (evSels.studyUPConly && (selGapSide < 0)) continue; fillHistogramsQA(collision, selGapSide); @@ -2320,8 +2390,8 @@ struct Derivedupcanalysis { CascadeCandidatesMC const& fullCascades, DauTracks const&, aod::MotherMCParts const&, - StraMCCollisionsFull const&, - CascMCCoresFull const&, + StraMCCollisionsFull const& mcCollisions, + CascMCCoresFull const& cascMCCores, NeutronsMC const& neutrons) { cascadesGrouped.clear(); @@ -2331,65 +2401,51 @@ struct Derivedupcanalysis { } for (const auto& collision : collisions) { - if (!collision.has_straMCCollision()) { + if (!hasValidMCReference(collision.straMCCollisionId(), mcCollisions.size(), 0)) { histos.fill(HIST("eventQA/mc/hFakeEvents"), 0); // no assoc. MC collisions continue; } const auto& mcCollision = collision.straMCCollision_as(); // take gen. collision associated to the rec. collision - if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { + // Vertex labels classify event QA, not the origin of particles at this vertex. + const bool selectedVertexGenerator = std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) != generatorIds->end(); + if (!doMCAssociation && !selectedVertexGenerator) { continue; } - if (!acceptEvent(collision, true)) { + if (!acceptEvent(collision, selectedVertexGenerator)) { continue; } // event is accepted - if (collision.isUPC()) { - fillPreSelTimingHistograms(collision); + if (selectedVertexGenerator) { + if (collision.isUPC()) { + fillPreSelTimingHistograms(collision); + } + histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); } - histos.fill(HIST("eventQA/hRawGapSide"), collision.gapSide()); - - int selGapSide = collision.isUPC() ? getGapSide(collision) : -1; - int selGapSideNoNeutrons = selGapSide; auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); - if (checkNeutronsInMC) { - for (const auto& neutron : groupedNeutrons) { - if (selGapSide < -0.5) - break; - - const float eta = neutron.eta(); - switch (selGapSide) { - case 0: // SGA - if (eta > neutronEtaCut) - selGapSide = -1; - break; - case 1: // SGC - if (eta < -neutronEtaCut) - selGapSide = -1; - break; - case 2: // DG - if (eta > neutronEtaCut) - selGapSide = 1; - else if (eta < -neutronEtaCut) - selGapSide = 0; - break; - } - } - } + int selGapSideNoNeutrons = collision.isUPC() ? getGapSideWithoutRecoZDC(collision) : o2::aod::sgselector::NoGap; - if (evSels.studyUPConly && (selGapSide < -0.5)) + if (evSels.studyUPConly && (selGapSideNoNeutrons < 0)) continue; - histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - fillHistogramsQA(collision, selGapSide); + if (selectedVertexGenerator) + histos.fill(HIST("eventQA/hSelGapSideNoNeutrons"), selGapSideNoNeutrons); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); - histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); - histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + int selGapSide = applyGeneratedNeutronSelection(selGapSideNoNeutrons, groupedNeutrons); + + if (evSels.studyUPConly && (selGapSide < 0)) + continue; + + if (selectedVertexGenerator) { + fillHistogramsQA(collision, selGapSide); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvsMCNParticlesEta08rec"), collision.multNTracksGlobal(), mcCollision.multMCNParticlesEta08()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vsMCNParticlesEta10rec"), collision.multNTracksPVeta1(), mcCollision.multMCNParticlesEta10()); + histos.fill(HIST("eventQA/mc/hNTracksGlobalvstotalMultMCParticles"), collision.multNTracksGlobal(), mcCollision.totalMultMCParticles()); + histos.fill(HIST("eventQA/mc/hNTracksPVeta1vstotalMultMCParticles"), collision.multNTracksPVeta1(), mcCollision.totalMultMCParticles()); + } std::size_t nCascadesThisColl = cascadesGrouped[collision.globalIndex()].size(); @@ -2398,9 +2454,15 @@ struct Derivedupcanalysis { std::bitset selMap = computeBitmapCascade(casc, collision); if (doMCAssociation) { - if (casc.has_cascMCCore()) { + if (hasValidMCReference(casc.cascMCCoreId(), cascMCCores.size(), 3)) { const auto& cascMC = casc.cascMCCore_as(); - computeCascadeMCAssociation(cascMC, selMap); + if (hasValidMCReference(cascMC.straMCCollisionId(), mcCollisions.size(), 4) && cascMC.pdgCode() != -1) { + const auto& candidateCollision = cascMC.straMCCollision_as(); + if (std::find(generatorIds->begin(), generatorIds->end(), candidateCollision.generatorsID()) != generatorIds->end()) { + histos.fill(HIST("generalQA/mc/h3dCascadeCollisionAssociation"), candidateCollision.generatorsID(), mcCollision.generatorsID(), cascMC.straMCCollisionId() == mcCollision.globalIndex()); + computeCascadeMCAssociation(cascMC, selMap); + } + } } } else { // the candidate may belong to any particle species @@ -2424,7 +2486,7 @@ struct Derivedupcanalysis { // V0 start for (auto const& v0MC : V0MCCores) { // Consider only primaries - if (!v0MC.has_straMCCollision() || !v0MC.isPhysicalPrimary()) + if (!v0MC.isPhysicalPrimary() || !hasValidMCReference(v0MC.straMCCollisionId(), mcCollisions.size(), 5)) continue; // Kinematics (|y| < rapidityCut) @@ -2447,6 +2509,11 @@ struct Derivedupcanalysis { continue; } + auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) { + continue; + } + // float centrality = -1.f; float ft0ampl = -1.f; int nTracksGlobal = -1; @@ -2486,7 +2553,7 @@ struct Derivedupcanalysis { // Cascade start for (auto const& cascMC : CascMCCores) { // Consider only primaries - if (!cascMC.has_straMCCollision() || !cascMC.isPhysicalPrimary()) + if (!cascMC.isPhysicalPrimary() || !hasValidMCReference(cascMC.straMCCollisionId(), mcCollisions.size(), 6)) continue; // Kinematics (|y| < rapidityCut) float pTmc = cascMC.ptMC(); @@ -2496,13 +2563,20 @@ struct Derivedupcanalysis { } else if ((cascMC.pdgCode() == PDG_t::kOmegaMinus) || (cascMC.pdgCode() == PDG_t::kOmegaPlusBar)) { ymc = cascMC.rapidityMC(2); } - if (std::abs(ymc) > rapidityCut) - continue; const auto& mcCollision = cascMC.straMCCollision_as(); // take gen. collision if (std::abs(mcCollision.posZ()) > maxZVtxPosition) continue; + if (std::find(generatorIds->begin(), generatorIds->end(), mcCollision.generatorsID()) == generatorIds->end()) { + continue; + } + + auto groupedNeutrons = neutrons.sliceBy(neutronsPerMcCollision, mcCollision.globalIndex()); + if (!acceptGeneratedNeutronSelection(groupedNeutrons)) { + continue; + } + // float centrality = -1.f; float ft0ampl = -1.f; int nTracksGlobal = -1; @@ -2515,8 +2589,23 @@ struct Derivedupcanalysis { } else if (static_cast(upcCuts.genGapSide) == 1) { ft0ampl = collision.totalFT0AmplitudeA(); } + nTracksGlobal = collision.multNTracksGlobal(); } + // All generated Xi parents, before daughter topology cuts. Requires asymmetric + // CascMCCores populated with generated Xi of both charges in this rapidity range. + if (calculateFeeddownMatrix && nTracksGlobal >= 0 && std::abs(ymc) <= kMotherRapidityMax) { + if (analyseLambda && cascMC.pdgCode() == PDG_t::kXiMinus) { + histos.fill(HIST(kParticlenames[kLambdaPartID]) + HIST("/mc/h4dGenXiFeeddown"), nTracksGlobal, pTmc, ymc, mcCollision.generatorsID()); + } + if (analyseAntiLambda && cascMC.pdgCode() == PDG_t::kXiPlusBar) { + histos.fill(HIST(kParticlenames[kAntiLambdaPartID]) + HIST("/mc/h4dGenAntiXiFeeddown"), nTracksGlobal, pTmc, ymc, mcCollision.generatorsID()); + } + } + + if (std::abs(ymc) > rapidityCut) + continue; + const int pdgPos = cascMC.pdgCodePositive(); const int pdgNeg = cascMC.pdgCodeNegative(); const int pdgBach = cascMC.pdgCodeBachelor(); @@ -2524,13 +2613,13 @@ struct Derivedupcanalysis { const bool isPositiveProton = (pdgPos == PDG_t::kProton); - const bool isPositivePion = (pdgPos == PDG_t::kPiPlus); - const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus); + const bool isPositivePion = (pdgPos == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgPos == PDG_t::kMuonPlus); + const bool isBachelorPositivePion = (pdgBach == PDG_t::kPiPlus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonPlus); const bool isNegativeProton = (pdgNeg == kProtonBar); - const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus); - const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus); + const bool isNegativePion = (pdgNeg == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgNeg == PDG_t::kMuonMinus); + const bool isBachelorNegativePion = (pdgBach == PDG_t::kPiMinus) || (doTreatPiToMuon && pdgBach == PDG_t::kMuonMinus); const bool isBachelorPositiveKaon = (pdgBach == PDG_t::kKPlus); const bool isBachelorNegativeKaon = (pdgBach == PDG_t::kKMinus); diff --git a/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx new file mode 100644 index 00000000000..f524b7dfa8e --- /dev/null +++ b/PWGLF/Tasks/Strangeness/forwardlambdakzeroanalysis.cxx @@ -0,0 +1,3024 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file forwardlambdakzeroanalysis.cxx +/// \brief V0s (K0s, Lambda and antiLambda) analysis task using MFT-only tracks +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Romain Schotter , Austrian Academy of Sciences & MBI +// + +#include "PWGMM/Mult/DataModel/bestCollisionTable.h" +#include "PWGUD/Core/SGSelector.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using std::array; + +using namespace o2::aod::rctsel; + +using Vec3D = ROOT::Math::SVector; +using SMatrix55 = ROOT::Math::SMatrix>; +using SMatrix5 = ROOT::Math::SVector; + +// simple checkers, but ensure 64 bit integers +#define BITSET(var, nbit) ((var) |= (static_cast(1) << static_cast(nbit))) +#define BITCHECK(var, nbit) ((var) & (static_cast(1) << static_cast(nbit))) + +enum CentEstimator { + kCentFT0C = 0, + kCentFT0M, + kCentFT0CVariant1, + kCentMFT, + kCentNGlobal, + kCentFV0A +}; + +enum Mode { kDefault = 0, // same event pairs + kEvtMixing, // event mixing + kNModes }; + +static const std::vector modePrefixes{ + "", // Default : no prefix + "EvtMixing/" // Event mixing +}; + +struct forwardlambdakzeroanalysis { + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + std::map histPointers; + + // master analysis switches + Configurable analyseK0Short{"analyseK0Short", true, "process K0Short-like candidates"}; + Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; + Configurable analyseAntiLambda{"analyseAntiLambda", true, "process AntiLambda-like candidates"}; + Configurable analyseD0{"analyseD0", true, "process D0-like candidates"}; + Configurable analyseAntiD0{"analyseAntiD0", true, "process AntiD0-like candidates"}; + Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "fill feeddown matrix if MC"}; + + Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true"}; + Configurable irSource{"irSource", "", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + Configurable centralityEstimator{"centralityEstimator", kCentFT0C, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; + Configurable doUPCanalysis{"doUPCanalysis", true, "Study V0s in hadronic and UPC collisions"}; + + Configurable doEventQA{"doEventQA", false, "do event QA histograms"}; + Configurable doCompleteTopoQA{"doCompleteTopoQA", false, "do topological variable QA histograms"}; + Configurable doPlainTopoQA{"doPlainTopoQA", true, "do simple 1D QA of candidates"}; + + // for MC + Configurable doTreatPiToMuon{"doTreatPiToMuon", false, "Take pi decay into muon into account in MC"}; + Configurable doMCAssociation{"doMCAssociation", true, "if MC, do MC association"}; + + // propagation option + Configurable useNewPropagationToVtx{"useNewPropagationToVtx", true, "Use propagation to vtx based on propagateToDCAhelix (most recent) instead of the one based on propagateToVtxhelixWithMCS"}; + + struct : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; + Configurable requireTriggerTVX{"requireTriggerTVX", true, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level"}; + Configurable rejectITSROFBorder{"rejectITSROFBorder", true, "reject events at ITS ROF border (Run 3 only)"}; + Configurable rejectTFBorder{"rejectTFBorder", true, "reject events at TF border (Run 3 only)"}; + Configurable requireIsVertexITSTPC{"requireIsVertexITSTPC", false, "require events with at least one ITS-TPC track (Run 3 only)"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference (Run 3 only)"}; + Configurable requireIsVertexTOFmatched{"requireIsVertexTOFmatched", false, "require events with at least one of vertex contributors matched to TOF (Run 3 only)"}; + Configurable requireIsVertexTRDmatched{"requireIsVertexTRDmatched", false, "require events with at least one of vertex contributors matched to TRD (Run 3 only)"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC (Run 3 only)"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds (Run 3 only)"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds (Run 3 only)"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 2 microseconds (Run 3 only)"}; + Configurable requireNoCollInROFStd{"requireNoCollInROFStd", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF with mult. above a certain threshold (Run 3 only)"}; + Configurable requireNoCollInROFStrict{"requireNoCollInROFStrict", false, "reject collisions corrupted by the cannibalism, with other collisions within the same ITS ROF (Run 3 only)"}; + Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", false, "reject collisions if previous ROF has high multiplicity (Run 3 only)"}; + Configurable requireIsGoodITSLayersAll{"requireIsGoodITSLayersAll", false, "require that the number of inactive chips on all ITS layers is below the maximum allowed values"}; + Configurable requireINEL0{"requireINEL0", true, "require INEL>0 event selection"}; + Configurable requireINEL1{"requireINEL1", false, "require INEL>1 event selection"}; + + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + + Configurable useEvtSelInDenomEff{"useEvtSelInDenomEff", false, "Consider event selections in the recoed <-> gen collision association for the denominator (or numerator) of the acc. x eff. (or signal loss)?"}; + Configurable applyZVtxSelOnMCPV{"applyZVtxSelOnMCPV", false, "Apply Z-vtx cut on the PV of the generated collision?"}; + Configurable useFT0CbasedOccupancy{"useFT0CbasedOccupancy", false, "Use sum of FT0-C amplitudes for estimating occupancy? (if not, use track-based definition)"}; + // fast check on occupancy + Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; + Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; + // fast check on interaction rate + Configurable minIR{"minIR", -1, "minimum IR collisions"}; + Configurable maxIR{"maxIR", -1, "maximum IR collisions"}; + } eventSelections; + + static constexpr float DefaultLifetimeCuts[1][3] = {{20., 30., 20.}}; + // Armenteros-Podolanski elliptic band: inner and outer ellipse half-axes, in units of the ideal + // two-body ones. The alpha and qT axes are scaled independently, so the band can be made much + // wider in alpha than in qT (the resolution smears the two very differently, in particular for + // Lambda, whose ideal alpha half-width is only ~0.18). A zero inner scale means "no inner ellipse". + // Columns: alphaScaleMin, qtScaleMin, alphaScaleMax, qtScaleMax + static constexpr float DefaultArmPodBand[3][4] = {{0.60, 0.60, 1.15, 1.35}, // K0Short + {0.00, 0.00, 2.00, 1.50}, // Lambda + {0.00, 0.00, 1.30, 1.30}}; // D0 + + struct : ConfigurableGroup { + std::string prefix = "v0Selections"; // JSON group name + + // Selection criteria: acceptance + Configurable rapidityMin{"rapidityMin", 0.5, "min rapidity"}; + Configurable rapidityMax{"rapidityMax", 0.5, "max rapidity"}; + Configurable daughterEtaCutMin{"daughterEtaCutMin", 0.8, "min eta for daughters"}; + Configurable daughterEtaCutMax{"daughterEtaCutMax", 0.8, "max eta for daughters"}; + Configurable minTrackPt{"minTrackPt", 0., "min track pT (GeV/c)"}; + Configurable maxTrackPt{"maxTrackPt", 1e+09, "max track pT (GeV/c)"}; + Configurable minTrackPz{"minTrackPz", -1e+09, "min track pZ (GeV/c)"}; + Configurable maxTrackPz{"maxTrackPz", 1e+09, "max track pZ (GeV/c)"}; + + // Standard 5 topological criteria + Configurable v0CosPA{"v0CosPA", 0.97, "min V0 CosPA"}; + Configurable v0OpAngle{"v0OpAngle", 0., "min V0 opening angle"}; + Configurable dcaV0Dau{"dcaV0Dau", 1.0, "max DCA V0 Daughters (cm)"}; + Configurable dcaNegToPVxy{"dcaNegToPVxy", .05, "min DCAxy Neg To PV (cm)"}; + Configurable dcaPosToPVxy{"dcaPosToPVxy", .05, "min DCAxy Pos To PV (cm)"}; + Configurable dcaNegToPVz{"dcaNegToPVz", .05, "min DCAz Neg To PV (cm)"}; + Configurable dcaPosToPVz{"dcaPosToPVz", .05, "min DCAz Pos To PV (cm)"}; + Configurable v0RadiusMin{"v0RadiusMin", 1.2, "minimum V0 radius (cm)"}; + Configurable v0RadiusMax{"v0RadiusMax", 1e+09, "maximum V0 radius (cm)"}; + Configurable v0Zmin{"v0Zmin", 1.2, "minimum V0 z (cm)"}; + Configurable v0Zmax{"v0Zmax", 1e+09, "maximum V0 z (cm)"}; + Configurable minPseudolifetime{"minPseudolifetime", -1e+09, "minimum V0 pseudo-proper lifetime (cm)"}; + Configurable maxPseudolifetime{"maxPseudolifetime", 1e+09, "maximum V0 pseudo-proper lifetime (cm)"}; + Configurable> lifetimeCut{"lifetimeCut", {DefaultLifetimeCuts[0], 3, {"lifetimecutD0", "lifetimecutLambda", "lifetimecutK0S"}}, "lifetimeCut"}; + Configurable rejectFailedPropagation{"rejectFailedPropagation", true, "Reject tracks which could not be propagated to the primary vertex (DCA = DefaultDCA = 999.)"}; + + // invariant mass selection + Configurable compMassRejectionK0Short{"compMassRejectionK0Short", -1, "Competing K^{0}_{S} mass rejection (GeV/#it{c}^{2})"}; + Configurable compMassRejectionLambda{"compMassRejectionLambda", -1, "Competing Lambda mass rejection (GeV/#it{c}^{2})"}; + + // Additional selection on the AP plot (exclusive for K0Short) + // original equation: lArmPt*5>TMath::Abs(lArmAlpha) + Configurable armPodCut{"armPodCut", 5.0f, "pT * (cut) > |alpha|, AP cut. Negative: no cut"}; + Configurable minQt{"minQt", -1, "Min Arm. Qt. Negative value means not cut"}; + Configurable maxQt{"maxQt", 1e+09, "Max Arm Qt."}; + Configurable minAlpha{"minAlpha", -1e+09, "Min Arm. Alpha."}; + Configurable maxAlpha{"maxAlpha", 1e+09, "Max Arm Alpha."}; + + // Armenteros-Podolanski elliptic band: keeps only the two-body decay arc of a given species. + // A decay M -> pos + neg populates the ellipse + // ((alpha - alphaCenter) / alphaHalfWidth)^2 + (qT / qStar)^2 = 1 + // with, in the mother rest frame, qStar the daughter momentum (= maximum qT), + // alphaCenter = (E*_pos - E*_neg) / M and alphaHalfWidth = 2 qStar / (beta M). + // The band is the crescent between two ellipses concentric with that one, each with its two + // half-axes scaled independently (the reconstructed distribution is smeared much more along + // alpha than along qT, so a single scale factor for both axes does not describe it): + // qT_up (alpha) = qtScaleMax * qStar * sqrt(1 - ((alpha - alphaCenter) / (alphaScaleMax * alphaHalfWidth))^2) + // qT_low(alpha) = qtScaleMin * qStar * sqrt(1 - ((alpha - alphaCenter) / (alphaScaleMin * alphaHalfWidth))^2) + // (square roots set to zero where their argument is negative). A candidate is kept if it lies + // inside the outer ellipse and outside the inner one; setting an inner scale to zero drops the + // inner ellipse altogether, i.e. keeps the whole filled outer ellipse. + Configurable useArmPodBand{"useArmPodBand", false, "Select the Armenteros-Podolanski arc with an elliptic band (per-species)"}; + Configurable armPodBandUseBeta{"armPodBandUseBeta", true, "Scale the alpha half-width by 1/beta of the candidate (exact ellipse). If false, use the beta -> 1 limit"}; + Configurable> armPodBand{"armPodBand", {DefaultArmPodBand[0], 3, 4, {"K0Short", "Lambda", "D0"}, {"alphaScaleMin", "qtScaleMin", "alphaScaleMax", "qtScaleMax"}}, "Arm.-Pod. band: inner and outer ellipse half-axes, in units of the ideal ones"}; + + // Track quality + Configurable minMFTclusters{"minMFTclusters", -1, "minimum MFT clusters"}; + Configurable maxMFTchi2PerNcls{"maxMFTchi2PerNcls", 1e+09, "maximum MFT chi2 per clusters"}; + Configurable maxMFTchi2{"maxMFTchi2", 1e+09, "max. MFT chi2"}; + } v0Selections; + + struct : ConfigurableGroup { + std::string prefix = "rctConfigurations"; // JSON group name + Configurable cfgRCTLabel{"cfgRCTLabel", "", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; + Configurable cfgCheckZDC{"cfgCheckZDC", false, "Include ZDC flags in the bit selection (for Pb-Pb only)"}; + Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; + } rctConfigurations; + + RCTFlagsChecker rctFlagsChecker{rctConfigurations.cfgRCTLabel.value}; + + // CCDB options + struct : ConfigurableGroup { + std::string prefix = "ccdbConfigurations"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable grpPath{"grpPath", "GLO/GRP/GRP", "Path of the grp file"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + Configurable lutPath{"lutPath", "GLO/Param/MatLUT", "Path of the Lut parametrization"}; + Configurable geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"}; + Configurable mVtxPath{"mVtxPath", "GLO/Calib/MeanVertex", "Path of the mean vertex file"}; + + // manual + Configurable useCustomMagField{"useCustomMagField", false, "Use custom magnetic field value"}; + Configurable customMagField{"customMagField", 5.0f, "Manually set magnetic field"}; + } ccdbConfigurations; + + o2::ccdb::CcdbApi ccdbApi; + Service ccdb{}; + ctpRateFetcher rateFetcher; + int mRunNumber = 0; + float magField = 0.; + o2::parameters::GRPMagField* grpmag = nullptr; + + // CCDB options + struct : ConfigurableGroup { + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for analysis"}; + ConfigurableAxis axisPz{"axisPz", {100, -10, 10}, "pz axis for analysis"}; + ConfigurableAxis axisPtXi{"axisPtXi", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for feeddown from Xi"}; + ConfigurableAxis axisPtCoarse{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; + ConfigurableAxis axisPtResol{"axisPtResol", {100, -1.0f, 1.0f}, "Axis for transverse momentum resolution (GeV/c)"}; + ConfigurableAxis axisPzResol{"axisPzResol", {100, -1.0f, 1.0f}, "Axis for longitudinal momentum resolution (GeV/c)"}; + ConfigurableAxis axisK0Mass{"axisK0Mass", {200, 0.4f, 0.6f}, "Axis for K0S invariant mass (GeV/c2)"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.101f, 1.131f}, "Axis for Lambda invariant mass (GeV/c2)"}; + ConfigurableAxis axisD0Mass{"axisD0Mass", {200, 1.75f, 1.95f}, "Axis for D0 invariant mass (GeV/c2)"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "Centrality"}; + ConfigurableAxis axisCentralityFine{"axisCentralityFine", {101, 0.0f, 101.0f}, "Centrality"}; + ConfigurableAxis axisNch{"axisNch", {500, 0.0f, +5000.0f}, "Number of charged particles"}; + ConfigurableAxis axisIRBinning{"axisIRBinning", {500, 0, 50}, "Binning for the interaction rate (kHz)"}; + ConfigurableAxis axisMultFT0M{"axisMultFT0M", {500, 0.0f, +100000.0f}, "Multiplicity FT0M"}; + ConfigurableAxis axisMultFT0C{"axisMultFT0C", {500, 0.0f, +10000.0f}, "Multiplicity FT0C"}; + ConfigurableAxis axisMultFV0A{"axisMultFV0A", {500, 0.0f, +100000.0f}, "Multiplicity FV0A"}; + + ConfigurableAxis axisRawCentrality{"axisRawCentrality", {VARIABLE_WIDTH, 0.000f, 52.320f, 75.400f, 95.719f, 115.364f, 135.211f, 155.791f, 177.504f, 200.686f, 225.641f, 252.645f, 281.906f, 313.850f, 348.302f, 385.732f, 426.307f, 470.146f, 517.555f, 568.899f, 624.177f, 684.021f, 748.734f, 818.078f, 892.577f, 973.087f, 1058.789f, 1150.915f, 1249.319f, 1354.279f, 1465.979f, 1584.790f, 1710.778f, 1844.863f, 1985.746f, 2134.643f, 2291.610f, 2456.943f, 2630.653f, 2813.959f, 3006.631f, 3207.229f, 3417.641f, 3637.318f, 3865.785f, 4104.997f, 4354.938f, 4615.786f, 4885.335f, 5166.555f, 5458.021f, 5762.584f, 6077.881f, 6406.834f, 6746.435f, 7097.958f, 7462.579f, 7839.165f, 8231.629f, 8635.640f, 9052.000f, 9484.268f, 9929.111f, 10389.350f, 10862.059f, 11352.185f, 11856.823f, 12380.371f, 12920.401f, 13476.971f, 14053.087f, 14646.190f, 15258.426f, 15890.617f, 16544.433f, 17218.024f, 17913.465f, 18631.374f, 19374.983f, 20136.700f, 20927.783f, 21746.796f, 22590.880f, 23465.734f, 24372.274f, 25314.351f, 26290.488f, 27300.899f, 28347.512f, 29436.133f, 30567.840f, 31746.818f, 32982.664f, 34276.329f, 35624.859f, 37042.588f, 38546.609f, 40139.742f, 41837.980f, 43679.429f, 45892.130f, 400000.000f}, "raw centrality signal"}; // for QA + + ConfigurableAxis axisOccupancy{"axisOccupancy", {VARIABLE_WIDTH, 0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 4500.0f, 6000.0f, 8000.0f, 10000.0f, 50000.0f}, "Occupancy"}; + + // topological variable QA axes + ConfigurableAxis axisDCAtoPVxy{"axisDCAtoPVxy", {100, 0.0f, 5.0f}, "DCAxy (cm)"}; + ConfigurableAxis axisDCAtoPVz{"axisDCAtoPVz", {100, -5.0f, 5.0f}, "DCAz (cm)"}; + ConfigurableAxis axisDCAdau{"axisDCAdau", {20, 0.0f, 2.0f}, "DCA (cm)"}; + ConfigurableAxis axisCosPA{"axisCosPA", {1000, 0.0f, 1.0f}, "cos(PA)"}; + ConfigurableAxis axisOpeningAngle{"axisOpeningAngle", {1000, -o2::constants::math::TwoPI, o2::constants::math::TwoPI}, "Opening angle (rad)"}; + ConfigurableAxis axisV0Radius{"axisV0Radius", {20, 0.0f, 60.0f}, "V0 2D radius (cm)"}; + ConfigurableAxis axisV0Z{"axisV0Z", {100, -50.0f, 0.0f}, "V0 Z (cm)"}; + ConfigurableAxis axisV0Rapidity{"axisV0Rapidity", {100, -4.0f, -1.0f}, "Rapidity"}; + ConfigurableAxis axisV0ProperLifeTime{"axisV0ProperLifeTime", {100, 0.0f, 50.0f}, "Proper lifetime (cm)"}; + ConfigurableAxis axisV0PseudoProperLifeTime{"axisV0PseudoProperLifeTime", {100, -50.0f, 50.0f}, "Pseudo-proper lifetime (cm)"}; + + ConfigurableAxis axisMFTchi2{"axisMFTchi2", {1000, 0.0f, 1000.0f}, "#chi^{2}"}; + ConfigurableAxis axisMFTchi2NDF{"axisMFTchi2NDF", {100, 0.0f, 100.0f}, "#chi^{2} per MFT clusters"}; + ConfigurableAxis axisMFTclus{"axisMFTclus", {10, 0.0f, 10.0f}, "N MFT Clusters"}; + ConfigurableAxis axisPosition{"axisPosition", {100, -50, 50.0f}, "Position x, y, z (cm)"}; + ConfigurableAxis axisPositionDiff{"axisPositionDiff", {100, -10.0f, 10.0f}, "Position x, y, z recoed - generated (cm)"}; + + // UPC axes + ConfigurableAxis axisSelGap{"axisSelGap", {4, -1.5, 2.5}, "Gap side"}; + + // AP plot axes + ConfigurableAxis axisAPAlpha{"axisAPAlpha", {220, -1.1f, 1.1f}, "V0 AP alpha"}; + ConfigurableAxis axisAPQt{"axisAPQt", {220, 0.0f, 0.5f}, "V0 AP alpha"}; + + // MC coll assoc QA axis + ConfigurableAxis axisMonteCarloNch{"axisMonteCarloNch", {300, 0.0f, 3000.0f}, "N_{ch} MC"}; + } axisConfigurations; + + // UPC selections + SGSelector sgSelector; + struct : ConfigurableGroup { + std::string prefix = "upcCuts"; // JSON group name + Configurable fv0Cut{"fv0Cut", 100., "FV0A threshold"}; + Configurable ft0Acut{"ft0Acut", 200., "FT0A threshold"}; + Configurable ft0Ccut{"ft0Ccut", 100., "FT0C threshold"}; + Configurable zdcCut{"zdcCut", 10., "ZDC threshold"}; + // Configurable gapSel{"gapSel", 2, "Gap selection"}; + } upcCuts; + + // DCA fitter configurations + struct : ConfigurableGroup { + std::string prefix = "fitterConfigurations"; // JSON group name + Configurable propagateToPCA{"propagateToPCA", true, "Propagate to PCA?"}; + Configurable minParamChange{"minParamChange", 4., "Stop minimization iterations if largest change of any X is smaller than this."}; + Configurable minRelChi2Change{"minRelChi2Change", 0.9, "Stop iterations is chi2/chi2old > this"}; + Configurable useAbsDCA{"useAbsDCA", true, "Use abs. distance minimization rather than chi2"}; + Configurable useLUTMatCorr{"useLUTMatCorr", true, "Use material LUT correction, instead of TGeo material correction or no material correction."}; + Configurable useTGeoMatCorr{"useTGeoMatCorr", false, "Use material TGeo correction, instead of LUT material correction or no material correction."}; + } fitterConfigurations; + + // Event mixing options + struct : ConfigurableGroup { + std::string prefix = "eventMixingConfigurations"; // JSON group name + Configurable doEventMixing{"doEventMixing", false, "Perform event-mixing?"}; + Configurable nEventMixing{"nEventMixing", 5, "Number of events in the mixing pool"}; + Configurable deltaCollision{"deltaCollision", 25., "Min globalIndex distance for event mixing"}; + ConfigurableAxis axisVertexMixing{"axisVertexMixing", {VARIABLE_WIDTH, -10.f, -8.f, -6.f, -4.f, -2.f, 0.f, 2.f, 4.f, 6.f, 8.f, 10.f}, "z-vertex bins for event mixing"}; + ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "centrality bins for event mixing"}; + } eventMixingConfigurations; + + o2::base::MatLayerCylSet* lut{}; // material LUT for DCA fitter + o2::vertexing::FwdDCAFitterN<2> fitter; + + // Build the mixing binning locally: a struct member initialized from a + // ConfigurableAxis captures the default bins at task construction time + ColumnBinningPolicy bkgColBinning{{eventMixingConfigurations.axisVertexMixing, eventMixingConfigurations.axisCentralityMixing}}; + + // Taken from https://github.com/AliceO2Group/O2Physics/blob/master/PWGLF/TableProducer/Strangeness/sigma0builder.cxx#L319 + // Thanks Gianni! + // ______________________________________________________ + // Struct to store V0Pair properties + struct PairTopoInfo { + float X = -999.f; + float Y = -999.f; + float Z = -999.f; + float Radius = -999.f; + float Zdist = -999.f; + std::array positiveMomentum = {0.0f, 0.0f, 0.0f}; + std::array negativeMomentum = {0.0f, 0.0f, 0.0f}; + float dcaPosToPVxy = -999.f; + float dcaNegToPVxy = -999.f; + float dcaPosToPVz = -999.f; + float dcaNegToPVz = -999.f; + float pTot = -999.f; + float pT = -999.f; + float pZ = -999.f; + float DistOverTotMom = -1.f; + float ZdistOverPz = -1.f; + float DCADau = -999.f; + float CosPA = -1.f; + float OpAngle = -999.f; + float mK0s = -999.f; + float mLambda = -999.f; + float mAntiLambda = -999.f; + float mD0 = -999.f; + float mAntiD0 = -999.f; + float QtArm = -999.f; + float AlphaArm = -999.f; + float rapidityK0s = -999.f; + float rapidityLambda = -999.f; + float rapidityD0 = -999.f; + int posNclusters = -1; + int negNclusters = -1; + float posChi2 = 999.f; + float negChi2 = 999.f; + float posChi2PerNclus = 999.f; + float negChi2PerNclus = 999.f; + + // MC specific information + int label = -1; + int motherLabel = -1; + float xMc = -999.f; + float yMc = -999.f; + float zMc = -999.f; + float RadiusMc = -999.f; + float ZdistMc = -999.f; + std::array momentumMc = {0.0f, 0.0f, 0.0f}; + std::array positiveMomentumMc = {0.0f, 0.0f, 0.0f}; + std::array negativeMomentumMc = {0.0f, 0.0f, 0.0f}; + std::array momentumMotherMc = {0.0f, 0.0f, 0.0f}; + int processPositive = -1; + int processNegative = -1; + float pTotMc = -999.f; + float pTMc = -999.f; + float pZMc = -999.f; + float rapMc = -999.f; + int pdgCodePositive = 0; + int pdgCodeNegative = 0; + int pdgCode = 0; + int pdgCodeMother = 0; + int mcCollision = -1; + bool isPhysicalPrimary = false; + bool motherIsPhysicalPrimary = false; + + float positivePt() const { return std::hypot(positiveMomentum[0], positiveMomentum[1]); } + float negativePt() const { return std::hypot(negativeMomentum[0], negativeMomentum[1]); } + float positivePz() const { return positiveMomentum[2]; } + float negativePz() const { return negativeMomentum[2]; } + float positivePtMc() const { return std::hypot(positiveMomentumMc[0], positiveMomentumMc[1]); } + float negativePtMc() const { return std::hypot(negativeMomentumMc[0], negativeMomentumMc[1]); } + float positivePzMc() const { return positiveMomentumMc[2]; } + float negativePzMc() const { return negativeMomentumMc[2]; } + }; + + // For manual sliceBy + // Preslice> perMcCollision = aod::v0data::straMCCollisionId; + PresliceUnsorted> perMcCollision = o2::aod::mccollisionlabel::mcCollisionId; + PresliceUnsorted perColReassociated3dTracks = o2::aod::fwdtrack::collisionId; + PresliceUnsorted> perColReassociated3dTracksMC = o2::aod::fwdtrack::collisionId; + + enum Selection : uint64_t { selCosPA = 0, + selOpAngle, + selRadius, + selRadiusMax, + selZmin, + selZmax, + selDCANegToPVxy, + selDCAPosToPVxy, + selDCANegToPVz, + selDCAPosToPVz, + selDCAV0Dau, + selK0ShortRapidityMin, + selK0ShortRapidityMax, + selLambdaRapidityMin, + selLambdaRapidityMax, + selD0RapidityMin, + selD0RapidityMax, + selK0ShortMassRejection, + selLambdaMassRejection, + selK0ShortCTau, + selLambdaCTau, + selD0CTau, + selK0ShortPseudoLifetimeMin, + selK0ShortPseudoLifetimeMax, + selLambdaPseudoLifetimeMin, + selLambdaPseudoLifetimeMax, + selD0PseudoLifetimeMin, + selD0PseudoLifetimeMax, + selK0ShortArmenteros, + selLambdaArmenteros, + selAntiLambdaArmenteros, + selD0Armenteros, + selAntiD0Armenteros, + selPosGoodMFTTrack, + selNegGoodMFTTrack, + selConsiderK0Short, // for mc tagging + selConsiderLambda, // for mc tagging + selConsiderAntiLambda, // for mc tagging + selConsiderD0, // for mc tagging + selConsiderAntiD0, // for mc tagging + selPhysPrimK0Short, // for mc tagging + selPhysPrimLambda, // for mc tagging + selPhysPrimAntiLambda, // for mc tagging + selPhysPrimD0, // for mc tagging + selPhysPrimAntiD0, // for mc tagging + }; + + uint64_t maskTopological = 0; + uint64_t maskTrackProperties = 0; + + uint64_t maskK0ShortSpecific = 0; + uint64_t maskLambdaSpecific = 0; + uint64_t maskAntiLambdaSpecific = 0; + uint64_t maskD0Specific = 0; + uint64_t maskAntiD0Specific = 0; + + uint64_t maskSelectionK0Short = 0; + uint64_t maskSelectionLambda = 0; + uint64_t maskSelectionAntiLambda = 0; + uint64_t maskSelectionD0 = 0; + uint64_t maskSelectionAntiD0 = 0; + + uint64_t secondaryMaskSelectionLambda = 0; + uint64_t secondaryMaskSelectionAntiLambda = 0; + + template + void insertHist(const std::string& name, const std::string& title, HistType type, const std::vector& axes) + { + histPointers[name] = histos.add(name.c_str(), title.c_str(), type, axes); + } + + template + std::shared_ptr& getHist(const std::string& name) + { + return std::get>(histPointers[name]); + } + + void init(InitContext const&) + { + // setting CCDB service + ccdb->setURL(ccdbConfigurations.ccdbUrl); + ccdb->setCaching(true); + ccdb->setFatalWhenNull(false); + + // initialise bit masks + // Mask with all topologic selections + maskTopological = 0; + BITSET(maskTopological, selCosPA); + BITSET(maskTopological, selRadius); + BITSET(maskTopological, selRadiusMax); + BITSET(maskTopological, selDCANegToPVxy); + BITSET(maskTopological, selDCAPosToPVxy); + BITSET(maskTopological, selDCANegToPVz); + BITSET(maskTopological, selDCAPosToPVz); + BITSET(maskTopological, selDCAV0Dau); + BITSET(maskTopological, selZmin); + BITSET(maskTopological, selZmax); + BITSET(maskTopological, selOpAngle); + + // Mask for specifically selecting K0Short + maskK0ShortSpecific = 0; + BITSET(maskK0ShortSpecific, selK0ShortRapidityMin); + BITSET(maskK0ShortSpecific, selK0ShortRapidityMax); + BITSET(maskK0ShortSpecific, selK0ShortCTau); + BITSET(maskK0ShortSpecific, selK0ShortPseudoLifetimeMin); + BITSET(maskK0ShortSpecific, selK0ShortPseudoLifetimeMax); + BITSET(maskK0ShortSpecific, selK0ShortArmenteros); + BITSET(maskK0ShortSpecific, selConsiderK0Short); + BITSET(maskK0ShortSpecific, selLambdaMassRejection); + // Mask for specifically selecting Lambda + maskLambdaSpecific = 0; + BITSET(maskLambdaSpecific, selLambdaRapidityMin); + BITSET(maskLambdaSpecific, selLambdaRapidityMax); + BITSET(maskLambdaSpecific, selLambdaCTau); + BITSET(maskLambdaSpecific, selLambdaPseudoLifetimeMin); + BITSET(maskLambdaSpecific, selLambdaPseudoLifetimeMax); + BITSET(maskLambdaSpecific, selConsiderLambda); + BITSET(maskLambdaSpecific, selK0ShortMassRejection); + BITSET(maskLambdaSpecific, selLambdaArmenteros); + // Mask for specifically selecting AntiLambda + maskAntiLambdaSpecific = 0; + BITSET(maskAntiLambdaSpecific, selLambdaRapidityMin); + BITSET(maskAntiLambdaSpecific, selLambdaRapidityMax); + BITSET(maskAntiLambdaSpecific, selLambdaCTau); + BITSET(maskAntiLambdaSpecific, selLambdaPseudoLifetimeMin); + BITSET(maskAntiLambdaSpecific, selLambdaPseudoLifetimeMax); + BITSET(maskAntiLambdaSpecific, selConsiderAntiLambda); + BITSET(maskAntiLambdaSpecific, selK0ShortMassRejection); + BITSET(maskAntiLambdaSpecific, selAntiLambdaArmenteros); + // Mask for specifically selecting D0 + maskD0Specific = 0; + BITSET(maskD0Specific, selD0RapidityMin); + BITSET(maskD0Specific, selD0RapidityMax); + BITSET(maskD0Specific, selD0CTau); + BITSET(maskD0Specific, selD0PseudoLifetimeMin); + BITSET(maskD0Specific, selD0PseudoLifetimeMax); + BITSET(maskD0Specific, selConsiderD0); + BITSET(maskD0Specific, selK0ShortMassRejection); + BITSET(maskD0Specific, selD0Armenteros); + BITSET(maskD0Specific, selLambdaMassRejection); + // Mask for specifically selecting D0 + maskAntiD0Specific = 0; + BITSET(maskAntiD0Specific, selD0RapidityMin); + BITSET(maskAntiD0Specific, selD0RapidityMax); + BITSET(maskAntiD0Specific, selD0CTau); + BITSET(maskAntiD0Specific, selD0PseudoLifetimeMin); + BITSET(maskAntiD0Specific, selD0PseudoLifetimeMax); + BITSET(maskAntiD0Specific, selConsiderAntiD0); + BITSET(maskAntiD0Specific, selK0ShortMassRejection); + BITSET(maskAntiD0Specific, selAntiD0Armenteros); + BITSET(maskAntiD0Specific, selLambdaMassRejection); + + // ask for specific TPC/TOF PID selections + maskTrackProperties = 0; + BITSET(maskTrackProperties, selPosGoodMFTTrack); + BITSET(maskTrackProperties, selNegGoodMFTTrack); + + // Primary particle selection, central to analysis + maskSelectionK0Short = maskTopological | maskTrackProperties | maskK0ShortSpecific; + maskSelectionLambda = maskTopological | maskTrackProperties | maskLambdaSpecific; + maskSelectionAntiLambda = maskTopological | maskTrackProperties | maskAntiLambdaSpecific; + maskSelectionD0 = maskTopological | maskTrackProperties | maskD0Specific; + maskSelectionAntiD0 = maskTopological | maskTrackProperties | maskAntiD0Specific; + + BITSET(maskSelectionK0Short, selPhysPrimK0Short); + BITSET(maskSelectionLambda, selPhysPrimLambda); + BITSET(maskSelectionAntiLambda, selPhysPrimAntiLambda); + BITSET(maskSelectionD0, selPhysPrimD0); + BITSET(maskSelectionAntiD0, selPhysPrimAntiD0); + + // No primary requirement for feeddown matrix + secondaryMaskSelectionLambda = maskTopological | maskTrackProperties | maskLambdaSpecific; + secondaryMaskSelectionAntiLambda = maskTopological | maskTrackProperties | maskAntiLambdaSpecific; + + // Initialise the RCTFlagsChecker + rctFlagsChecker.init(rctConfigurations.cfgRCTLabel.value, rctConfigurations.cfgCheckZDC, rctConfigurations.cfgTreatLimitedAcceptanceAsBad); + + // Event Counters + insertHist("hEventSelection", "hEventSelection", kTH1D, {{25, -0.5f, +24.5f}}); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(1, "All collisions"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(2, "sel8 cut"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(3, "kIsTriggerTVX"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(4, "kNoITSROFrameBorder"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(5, "kNoTimeFrameBorder"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(6, "posZ cut"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(7, "kIsVertexITSTPC"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(8, "kIsGoodZvtxFT0vsPV"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(9, "kIsVertexTOFmatched"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(10, "kIsVertexTRDmatched"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(11, "kNoSameBunchPileup"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(12, "kNoCollInTimeRangeStd"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(13, "kNoCollInTimeRangeStrict"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(14, "kNoCollInTimeRangeNarrow"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(15, "kNoCollInRofStd"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(16, "kNoCollInRofStrict"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(17, "kNoHighMultCollInPrevRof"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(18, "kIsGoodITSLayersAll"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(19, "INEL>0"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(20, "INEL>1"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(21, "Below min occup."); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(22, "Above max occup."); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(23, "Below min IR"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(24, "Above max IR"); + getHist("hEventSelection")->GetXaxis()->SetBinLabel(25, "RCT flags"); + + insertHist("hEventCentrality", "hEventCentrality", kTH1D, {axisConfigurations.axisCentralityFine}); + insertHist("hCentralityVsNch", "hCentralityVsNch", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisNch}); + + insertHist("hEventPVz", "hEventPVz", kTH1D, {{100, -20.0f, +20.0f}}); + insertHist("hCentralityVsPVz", "hCentralityVsPVz", kTH2D, {axisConfigurations.axisCentralityFine, {100, -20.0f, +20.0f}}); + if (doprocessGenerated) { + insertHist("hEventPVzMC", "hEventPVzMC", kTH1D, {{100, -20.0f, +20.0f}}); + insertHist("hCentralityVsPVzMC", "hCentralityVsPVzMC", kTH2D, {axisConfigurations.axisCentralityFine, {100, -20.0f, +20.0f}}); + } + if (doprocessMonteCarlo) { + insertHist("hEventPVxDiff", "hEventPVxDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist("hEventPVyDiff", "hEventPVyDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist("hEventPVzDiff", "hEventPVzDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + } + + insertHist("hEventOccupancy", "hEventOccupancy", kTH1D, {axisConfigurations.axisOccupancy}); + insertHist("hCentralityVsOccupancy", "hCentralityVsOccupancy", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisOccupancy}); + + if (doUPCanalysis) { + insertHist("hGapSide", "Gap side; Entries", kTH1D, {{5, -0.5, 4.5}}); + insertHist("hSelGapSide", "Selected gap side; Entries", kTH1D, {axisConfigurations.axisSelGap}); + insertHist("hEventCentralityVsSelGapSide", ";Centrality (%); Selected gap side", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisSelGap}); + } + + insertHist("hInteractionRate", "hInteractionRate", kTH1D, {axisConfigurations.axisIRBinning}); + insertHist("hCentralityVsInteractionRate", "hCentralityVsInteractionRate", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisIRBinning}); + + insertHist("hInteractionRateVsOccupancy", "hInteractionRateVsOccupancy", kTH2D, {axisConfigurations.axisIRBinning, axisConfigurations.axisOccupancy}); + + if (eventMixingConfigurations.doEventMixing) { + insertHist("hDeltaCollision", "hDeltaCollision", kTH1D, {{2000, -1000.f, 1000.f}}); + insertHist("h2dCentralityCollPair", "h2dCentralityCollPair", kTH2D, {axisConfigurations.axisCentralityFine, axisConfigurations.axisCentralityFine}); + insertHist("h2dPVzCollPair", "h2dPVzCollPair", kTH2D, {{100, -20.0f, +20.0f}, {100, -20.0f, +20.0f}}); + } + + for (int iMode = 0; iMode < kNModes; ++iMode) { + if (!eventMixingConfigurations.doEventMixing && iMode == kEvtMixing) { + continue; + } + insertHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s", "hSelectionV0s", kTH1D, {{static_cast(selPhysPrimAntiD0) + 3, -0.5f, static_cast(selPhysPrimAntiD0) + 2.5f}}); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(1, "All"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selCosPA + 2, "cosPA"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selOpAngle + 2, "Op. angle"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selRadius + 2, "Radius min."); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selRadiusMax + 2, "Radius max."); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selZmin + 2, "Z min."); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selZmax + 2, "Z max."); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selDCANegToPVxy + 2, "DCAxy neg. to PV"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selDCAPosToPVxy + 2, "DCAxy pos. to PV"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selDCANegToPVz + 2, "DCAz neg. to PV"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selDCAPosToPVz + 2, "DCAz pos. to PV"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selDCAV0Dau + 2, "DCA V0 dau."); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selK0ShortRapidityMin + 2, "K^{0}_{S} rap. min"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selK0ShortRapidityMax + 2, "K^{0}_{S} rap. max"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selLambdaRapidityMin + 2, "#Lambda rap. min"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selLambdaRapidityMax + 2, "#Lambda rap. max"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selD0RapidityMin + 2, "D^{0} rap. min"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selD0RapidityMax + 2, "D^{0} rap. max"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selK0ShortMassRejection + 2, "K^{0}_{S} mass rej."); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selLambdaMassRejection + 2, "#Lambda mass rej."); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selK0ShortCTau + 2, "K^{0}_{S} lifetime"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selLambdaCTau + 2, "#Lambda lifetime"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selD0CTau + 2, "D^{0} lifetime"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selK0ShortPseudoLifetimeMin + 2, "K^{0}_{S} pseudo-time min"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selK0ShortPseudoLifetimeMax + 2, "K^{0}_{S} pseudo-time max"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selLambdaPseudoLifetimeMin + 2, "#Lambda pseudo-time min"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selLambdaPseudoLifetimeMax + 2, "#Lambda pseudo-time max"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selD0PseudoLifetimeMin + 2, "D^{0} pseudo-time min"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selD0PseudoLifetimeMax + 2, "D^{0} pseudo-time max"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selK0ShortArmenteros + 2, "K^{0}_{S} Arm. pod. cut"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selLambdaArmenteros + 2, "#Lambda Arm. pod. cut"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selAntiLambdaArmenteros + 2, "#bar{#Lambda} Arm. pod. cut"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selD0Armenteros + 2, "D^{0} Arm. pod. cut"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selAntiD0Armenteros + 2, "#bar{D}^{0} Arm. pod. cut"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selPosGoodMFTTrack + 2, "Pos. good MFT track"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selNegGoodMFTTrack + 2, "Neg. good MFT track"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selConsiderK0Short + 2, "True K^{0}_{S}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selConsiderLambda + 2, "True #Lambda"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selConsiderAntiLambda + 2, "True #bar{#Lambda}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selConsiderD0 + 2, "True D^{0}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selConsiderAntiD0 + 2, "True #bar{D}^{0}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selPhysPrimK0Short + 2, "Phys. prim. K^{0}_{S}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selPhysPrimLambda + 2, "Phys. prim. #Lambda"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selPhysPrimAntiLambda + 2, "Phys. prim. #bar{#Lambda}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selPhysPrimD0 + 2, "Phys. prim. D^{0}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selPhysPrimAntiD0 + 2, "Phys. prim. #bar{D}^{0}"); + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->GetXaxis()->SetBinLabel(selPhysPrimAntiD0 + 3, "Cand. selected"); + + // histograms versus mass + if (analyseK0Short) { + insertHist(modePrefixes[iMode] + "h2dNbrOfK0ShortVsCentrality", "h2dNbrOfK0ShortVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + insertHist(modePrefixes[iMode] + "h3dMassK0Short", "h3dMassK0Short", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisK0Mass}); + if (doUPCanalysis) { + // Non-UPC info + insertHist(modePrefixes[iMode] + "h3dMassK0ShortHadronic", "h3dMassK0ShortHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisK0Mass}); + // UPC info + insertHist(modePrefixes[iMode] + "h3dMassK0ShortSGA", "h3dMassK0ShortSGA", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "h3dMassK0ShortSGC", "h3dMassK0ShortSGC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "h3dMassK0ShortDG", "h3dMassK0ShortDG", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisK0Mass}); + } + } + if (analyseLambda) { + insertHist(modePrefixes[iMode] + "h2dNbrOfLambdaVsCentrality", "h2dNbrOfLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + insertHist(modePrefixes[iMode] + "h3dMassLambda", "h3dMassLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + if (doUPCanalysis) { + // Non-UPC info + insertHist(modePrefixes[iMode] + "h3dMassLambdaHadronic", "h3dMassLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // UPC info + insertHist(modePrefixes[iMode] + "h3dMassLambdaSGA", "h3dMassLambdaSGA", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "h3dMassLambdaSGC", "h3dMassLambdaSGC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "h3dMassLambdaDG", "h3dMassLambdaDG", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + } + } + if (analyseAntiLambda) { + insertHist(modePrefixes[iMode] + "h2dNbrOfAntiLambdaVsCentrality", "h2dNbrOfAntiLambdaVsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + insertHist(modePrefixes[iMode] + "h3dMassAntiLambda", "h3dMassAntiLambda", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + if (doUPCanalysis) { + // Non-UPC info + insertHist(modePrefixes[iMode] + "h3dMassAntiLambdaHadronic", "h3dMassAntiLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + // UPC info + insertHist(modePrefixes[iMode] + "h3dMassAntiLambdaSGA", "h3dMassAntiLambdaSGA", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "h3dMassAntiLambdaSGC", "h3dMassAntiLambdaSGC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "h3dMassAntiLambdaDG", "h3dMassAntiLambdaDG", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisLambdaMass}); + } + } + if (analyseD0) { + insertHist(modePrefixes[iMode] + "h2dNbrOfD0VsCentrality", "h2dNbrOfD0VsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + insertHist(modePrefixes[iMode] + "h3dMassD0", "h3dMassD0", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + if (doUPCanalysis) { + // Non-UPC info + insertHist(modePrefixes[iMode] + "h3dMassD0Hadronic", "h3dMassD0Hadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + // UPC info + insertHist(modePrefixes[iMode] + "h3dMassD0SGA", "h3dMassD0SGA", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "h3dMassD0SGC", "h3dMassD0SGC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "h3dMassD0DG", "h3dMassD0DG", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + } + } + if (analyseAntiD0) { + insertHist(modePrefixes[iMode] + "h2dNbrOfAntiD0VsCentrality", "h2dNbrOfAntiD0VsCentrality", kTH2D, {axisConfigurations.axisCentrality, {10, -0.5f, 9.5f}}); + insertHist(modePrefixes[iMode] + "h3dMassAntiD0", "h3dMassAntiD0", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + if (doUPCanalysis) { + // Non-UPC info + insertHist(modePrefixes[iMode] + "h3dMassAntiD0Hadronic", "h3dMassAntiLambdaHadronic", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + // UPC info + insertHist(modePrefixes[iMode] + "h3dMassAntiD0SGA", "h3dMassAntiD0SGA", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "h3dMassAntiD0SGC", "h3dMassAntiD0SGC", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "h3dMassAntiD0DG", "h3dMassAntiD0DG", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisD0Mass}); + } + } + + if (analyseLambda && calculateFeeddownMatrix && doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "h3dLambdaFeeddown", "h3dLambdaFeeddown", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPtXi}); + insertHist(modePrefixes[iMode] + "h3dLambdaFeeddownFromXi0", "h3dLambdaFeeddownFromXi0", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPtXi}); + } + if (analyseAntiLambda && calculateFeeddownMatrix && doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "h3dAntiLambdaFeeddown", "h3dAntiLambdaFeeddown", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPtXi}); + insertHist(modePrefixes[iMode] + "h3dAntiLambdaFeeddownFromXi0", "h3dAntiLambdaFeeddownFromXi0", kTH3D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt, axisConfigurations.axisPtXi}); + } + + if (analyseK0Short) + insertHist(modePrefixes[iMode] + "hMassK0Short", "hMassK0Short", kTH1D, {axisConfigurations.axisK0Mass}); + if (analyseLambda) + insertHist(modePrefixes[iMode] + "hMassLambda", "hMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + if (analyseAntiLambda) + insertHist(modePrefixes[iMode] + "hMassAntiLambda", "hMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + if (analyseD0) + insertHist(modePrefixes[iMode] + "hMassD0", "hMassD0", kTH1D, {axisConfigurations.axisD0Mass}); + if (analyseAntiD0) + insertHist(modePrefixes[iMode] + "hMassAntiD0", "hMassAntiD0", kTH1D, {axisConfigurations.axisD0Mass}); + + if (doPlainTopoQA) { + // All candidates received + insertHist(modePrefixes[iMode] + "hPosDCAToPVxy", "hPosDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "hNegDCAToPVxy", "hNegDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "hPosDCAToPVz", "hPosDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "hNegDCAToPVz", "hNegDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + insertHist(modePrefixes[iMode] + "hCosPA", "hCosPA", kTH1D, {axisConfigurations.axisCosPA}); + insertHist(modePrefixes[iMode] + "hOpeningAngle", "hOpeningAngle", kTH1D, {axisConfigurations.axisOpeningAngle}); + insertHist(modePrefixes[iMode] + "hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + insertHist(modePrefixes[iMode] + "hV0Z", "hV0Z", kTH1D, {axisConfigurations.axisV0Z}); + insertHist(modePrefixes[iMode] + "hV0Rapidity", "hV0Rapidity", kTH1D, {axisConfigurations.axisV0Rapidity}); + insertHist(modePrefixes[iMode] + "hV0LifetimeK0s", "hV0LifetimeK0s", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "hV0LifetimeLambda", "hV0LifetimeLambda", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "hV0LifetimeD0", "hV0LifetimeD0", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "hV0PseudoLifetimeK0s", "hV0PseudoLifetimeK0s", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "hV0PseudoLifetimeLambda", "hV0PseudoLifetimeLambda", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "hV0PseudoLifetimeD0", "hV0PseudoLifetimeD0", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "hV0InvMassK0s", "hV0InvMassK0s", kTH1D, {axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "hV0InvMassLambda", "hV0InvMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "hV0InvMassAntiLambda", "hV0InvMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "hV0InvMassD0", "hV0InvMassD0", kTH1D, {axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "hV0InvMassAntiD0", "hV0InvMassAntiD0", kTH1D, {axisConfigurations.axisD0Mass}); + if (doCompleteTopoQA) { + insertHist(modePrefixes[iMode] + "hV0InvMassK0sVsLambda", "hV0InvMassK0sVsLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "hV0InvMassK0sVsAntiLambda", "hV0InvMassK0sVsAntiLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "hV0InvMassK0sVsD0", "hV0InvMassK0sVsD0", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "hV0InvMassK0sVsAntiD0", "hV0InvMassK0sVsAntiD0", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "hV0InvMassLambdaVsD0", "hV0InvMassLambdaVsD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "hV0InvMassAntiLambdaVsD0", "hV0InvMassAntiLambdaVsD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "hV0InvMassLambdaVsAntiD0", "hV0InvMassLambdaVsAntiD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "hV0InvMassAntiLambdaVsAntiD0", "hV0InvMassAntiLambdaVsAntiD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + } + + insertHist(modePrefixes[iMode] + "hPositiveMFTcls", "hPositiveMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "hNegativeMFTcls", "hNegativeMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "hPositiveMFTchi2PerNcls", "hPositiveMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "hNegativeMFTchi2PerNcls", "hNegativeMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "hPositiveMFTchi2", "hPositiveMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + insertHist(modePrefixes[iMode] + "hNegativeMFTchi2", "hNegativeMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + if (doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "hPositivePtResolution", "hPositivePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "hNegativePtResolution", "hNegativePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "hPositivePzResolution", "hPositivePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + insertHist(modePrefixes[iMode] + "hNegativePzResolution", "hNegativePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + + insertHist(modePrefixes[iMode] + "hV0XDiff", "hV0XDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "hV0YDiff", "hV0YDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "hV0ZDiff", "hV0ZDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "hV0RadiusDiff", "hV0RadiusDiff", kTH2D, {axisConfigurations.axisV0Radius, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "hV0DistZDiff", "hV0DistZDiff", kTH2D, {axisConfigurations.axisV0Z, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "hV0PtResolution", "hV0PtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "hV0PzResolution", "hV0PzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + } + if (analyseK0Short) { + insertHist(modePrefixes[iMode] + "K0Short/hPosDCAToPVxy", "hPosDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "K0Short/hNegDCAToPVxy", "hNegDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "K0Short/hPosDCAToPVz", "hPosDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "K0Short/hNegDCAToPVz", "hNegDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "K0Short/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + insertHist(modePrefixes[iMode] + "K0Short/hCosPA", "hCosPA", kTH1D, {axisConfigurations.axisCosPA}); + insertHist(modePrefixes[iMode] + "K0Short/hOpeningAngle", "hOpeningAngle", kTH1D, {axisConfigurations.axisOpeningAngle}); + insertHist(modePrefixes[iMode] + "K0Short/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + insertHist(modePrefixes[iMode] + "K0Short/hV0Z", "hV0Z", kTH1D, {axisConfigurations.axisV0Z}); + insertHist(modePrefixes[iMode] + "K0Short/hV0Rapidity", "hV0Rapidity", kTH1D, {axisConfigurations.axisV0Rapidity}); + insertHist(modePrefixes[iMode] + "K0Short/hV0LifetimeK0s", "hV0LifetimeK0s", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "K0Short/hV0LifetimeLambda", "hV0LifetimeLambda", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "K0Short/hV0LifetimeD0", "hV0LifetimeD0", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "K0Short/hV0PseudoLifetimeK0s", "hV0PseudoLifetimeK0s", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "K0Short/hV0PseudoLifetimeLambda", "hV0PseudoLifetimeLambda", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "K0Short/hV0PseudoLifetimeD0", "hV0PseudoLifetimeD0", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0s", "hV0InvMassK0s", kTH1D, {axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassLambda", "hV0InvMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassAntiLambda", "hV0InvMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassD0", "hV0InvMassD0", kTH1D, {axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassAntiD0", "hV0InvMassAntiD0", kTH1D, {axisConfigurations.axisD0Mass}); + if (doCompleteTopoQA) { + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsLambda", "hV0InvMassK0sVsLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsAntiLambda", "hV0InvMassK0sVsAntiLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsD0", "hV0InvMassK0sVsD0", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsAntiD0", "hV0InvMassK0sVsAntiD0", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisD0Mass}); + } + + insertHist(modePrefixes[iMode] + "K0Short/hPositiveMFTcls", "hPositiveMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "K0Short/hNegativeMFTcls", "hNegativeMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "K0Short/hPositiveMFTchi2PerNcls", "hPositiveMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "K0Short/hNegativeMFTchi2PerNcls", "hNegativeMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "K0Short/hPositiveMFTchi2", "hPositiveMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + insertHist(modePrefixes[iMode] + "K0Short/hNegativeMFTchi2", "hNegativeMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + if (doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "K0Short/hPositivePtResolution", "hPositivePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "K0Short/hNegativePtResolution", "hNegativePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "K0Short/hPositivePzResolution", "hPositivePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + insertHist(modePrefixes[iMode] + "K0Short/hNegativePzResolution", "hNegativePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + + insertHist(modePrefixes[iMode] + "K0Short/hV0XDiff", "hV0XDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "K0Short/hV0YDiff", "hV0YDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "K0Short/hV0ZDiff", "hV0ZDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "K0Short/hV0RadiusDiff", "hV0RadiusDiff", kTH2D, {axisConfigurations.axisV0Radius, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "K0Short/hV0DistZDiff", "hV0DistZDiff", kTH2D, {axisConfigurations.axisV0Z, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "K0Short/hV0PtResolution", "hV0PtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "K0Short/hV0PzResolution", "hV0PzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + } + } + if (analyseLambda) { + insertHist(modePrefixes[iMode] + "Lambda/hPosDCAToPVxy", "hPosDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "Lambda/hNegDCAToPVxy", "hNegDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "Lambda/hPosDCAToPVz", "hPosDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "Lambda/hNegDCAToPVz", "hNegDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "Lambda/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + insertHist(modePrefixes[iMode] + "Lambda/hCosPA", "hCosPA", kTH1D, {axisConfigurations.axisCosPA}); + insertHist(modePrefixes[iMode] + "Lambda/hOpeningAngle", "hOpeningAngle", kTH1D, {axisConfigurations.axisOpeningAngle}); + insertHist(modePrefixes[iMode] + "Lambda/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + insertHist(modePrefixes[iMode] + "Lambda/hV0Z", "hV0Z", kTH1D, {axisConfigurations.axisV0Z}); + insertHist(modePrefixes[iMode] + "Lambda/hV0Rapidity", "hV0Rapidity", kTH1D, {axisConfigurations.axisV0Rapidity}); + insertHist(modePrefixes[iMode] + "Lambda/hV0LifetimeK0s", "hV0LifetimeK0s", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "Lambda/hV0LifetimeLambda", "hV0LifetimeLambda", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "Lambda/hV0LifetimeD0", "hV0LifetimeD0", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "Lambda/hV0PseudoLifetimeK0s", "hV0PseudoLifetimeK0s", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "Lambda/hV0PseudoLifetimeLambda", "hV0PseudoLifetimeLambda", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "Lambda/hV0PseudoLifetimeD0", "hV0PseudoLifetimeD0", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassK0s", "hV0InvMassK0s", kTH1D, {axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassLambda", "hV0InvMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassAntiLambda", "hV0InvMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassD0", "hV0InvMassD0", kTH1D, {axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassAntiD0", "hV0InvMassAntiD0", kTH1D, {axisConfigurations.axisD0Mass}); + if (doCompleteTopoQA) { + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassK0sVsLambda", "hV0InvMassK0sVsLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassLambdaVsD0", "hV0InvMassLambdaVsD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "Lambda/hV0InvMassLambdaVsAntiD0", "hV0InvMassLambdaVsAntiD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + } + + insertHist(modePrefixes[iMode] + "Lambda/hPositiveMFTcls", "hPositiveMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "Lambda/hNegativeMFTcls", "hNegativeMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "Lambda/hPositiveMFTchi2PerNcls", "hPositiveMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "Lambda/hNegativeMFTchi2PerNcls", "hNegativeMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "Lambda/hPositiveMFTchi2", "hPositiveMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + insertHist(modePrefixes[iMode] + "Lambda/hNegativeMFTchi2", "hNegativeMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + if (doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "Lambda/hPositivePtResolution", "hPositivePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "Lambda/hNegativePtResolution", "hNegativePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "Lambda/hPositivePzResolution", "hPositivePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + insertHist(modePrefixes[iMode] + "Lambda/hNegativePzResolution", "hNegativePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + + insertHist(modePrefixes[iMode] + "Lambda/hV0XDiff", "hV0XDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "Lambda/hV0YDiff", "hV0YDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "Lambda/hV0ZDiff", "hV0ZDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "Lambda/hV0RadiusDiff", "hV0RadiusDiff", kTH2D, {axisConfigurations.axisV0Radius, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "Lambda/hV0DistZDiff", "hV0DistZDiff", kTH2D, {axisConfigurations.axisV0Z, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "Lambda/hV0PtResolution", "hV0PtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "Lambda/hV0PzResolution", "hV0PzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + } + } + if (analyseAntiLambda) { + insertHist(modePrefixes[iMode] + "AntiLambda/hPosDCAToPVxy", "hPosDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "AntiLambda/hNegDCAToPVxy", "hNegDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "AntiLambda/hPosDCAToPVz", "hPosDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "AntiLambda/hNegDCAToPVz", "hNegDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "AntiLambda/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + insertHist(modePrefixes[iMode] + "AntiLambda/hCosPA", "hCosPA", kTH1D, {axisConfigurations.axisCosPA}); + insertHist(modePrefixes[iMode] + "AntiLambda/hOpeningAngle", "hOpeningAngle", kTH1D, {axisConfigurations.axisOpeningAngle}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0Z", "hV0Z", kTH1D, {axisConfigurations.axisV0Z}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0Rapidity", "hV0Rapidity", kTH1D, {axisConfigurations.axisV0Rapidity}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0LifetimeK0s", "hV0LifetimeK0s", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0LifetimeLambda", "hV0LifetimeLambda", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0LifetimeD0", "hV0LifetimeD0", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0PseudoLifetimeK0s", "hV0PseudoLifetimeK0s", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0PseudoLifetimeLambda", "hV0PseudoLifetimeLambda", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0PseudoLifetimeD0", "hV0PseudoLifetimeD0", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassK0s", "hV0InvMassK0s", kTH1D, {axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassLambda", "hV0InvMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiLambda", "hV0InvMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassD0", "hV0InvMassD0", kTH1D, {axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiD0", "hV0InvMassAntiD0", kTH1D, {axisConfigurations.axisD0Mass}); + if (doCompleteTopoQA) { + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassK0sVsAntiLambda", "hV0InvMassK0sVsAntiLambda", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiLambdaVsD0", "hV0InvMassAntiLambdaVsD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiLambdaVsAntiD0", "hV0InvMassAntiLambdaVsAntiD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + } + + insertHist(modePrefixes[iMode] + "AntiLambda/hPositiveMFTcls", "hPositiveMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "AntiLambda/hNegativeMFTcls", "hNegativeMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "AntiLambda/hPositiveMFTchi2PerNcls", "hPositiveMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "AntiLambda/hNegativeMFTchi2PerNcls", "hNegativeMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "AntiLambda/hPositiveMFTchi2", "hPositiveMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + insertHist(modePrefixes[iMode] + "AntiLambda/hNegativeMFTchi2", "hNegativeMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + if (doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "AntiLambda/hPositivePtResolution", "hPositivePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "AntiLambda/hNegativePtResolution", "hNegativePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "AntiLambda/hPositivePzResolution", "hPositivePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + insertHist(modePrefixes[iMode] + "AntiLambda/hNegativePzResolution", "hNegativePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + + insertHist(modePrefixes[iMode] + "AntiLambda/hV0XDiff", "hV0XDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0YDiff", "hV0YDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0ZDiff", "hV0ZDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0RadiusDiff", "hV0RadiusDiff", kTH2D, {axisConfigurations.axisV0Radius, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0DistZDiff", "hV0DistZDiff", kTH2D, {axisConfigurations.axisV0Z, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0PtResolution", "hV0PtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "AntiLambda/hV0PzResolution", "hV0PzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + } + } + if (analyseD0) { + insertHist(modePrefixes[iMode] + "D0/hPosDCAToPVxy", "hPosDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "D0/hNegDCAToPVxy", "hNegDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "D0/hPosDCAToPVz", "hPosDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "D0/hNegDCAToPVz", "hNegDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "D0/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + insertHist(modePrefixes[iMode] + "D0/hCosPA", "hCosPA", kTH1D, {axisConfigurations.axisCosPA}); + insertHist(modePrefixes[iMode] + "D0/hOpeningAngle", "hOpeningAngle", kTH1D, {axisConfigurations.axisOpeningAngle}); + insertHist(modePrefixes[iMode] + "D0/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + insertHist(modePrefixes[iMode] + "D0/hV0Z", "hV0Z", kTH1D, {axisConfigurations.axisV0Z}); + insertHist(modePrefixes[iMode] + "D0/hV0Rapidity", "hV0Rapidity", kTH1D, {axisConfigurations.axisV0Rapidity}); + insertHist(modePrefixes[iMode] + "D0/hV0LifetimeK0s", "hV0LifetimeK0s", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "D0/hV0LifetimeLambda", "hV0LifetimeLambda", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "D0/hV0LifetimeD0", "hV0LifetimeD0", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "D0/hV0PseudoLifetimeK0s", "hV0PseudoLifetimeK0s", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "D0/hV0PseudoLifetimeLambda", "hV0PseudoLifetimeLambda", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "D0/hV0PseudoLifetimeD0", "hV0PseudoLifetimeD0", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "D0/hV0InvMassK0s", "hV0InvMassK0s", kTH1D, {axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "D0/hV0InvMassLambda", "hV0InvMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "D0/hV0InvMassAntiLambda", "hV0InvMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "D0/hV0InvMassD0", "hV0InvMassD0", kTH1D, {axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "D0/hV0InvMassAntiD0", "hV0InvMassAntiD0", kTH1D, {axisConfigurations.axisD0Mass}); + if (doCompleteTopoQA) { + insertHist(modePrefixes[iMode] + "D0/hV0InvMassK0sVsD0", "hV0InvMassK0sVsD0", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "D0/hV0InvMassLambdaVsD0", "hV0InvMassLambdaVsD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "D0/hV0InvMassAntiLambdaVsD0", "hV0InvMassAntiLambdaVsD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + } + + insertHist(modePrefixes[iMode] + "D0/hPositiveMFTcls", "hPositiveMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "D0/hNegativeMFTcls", "hNegativeMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "D0/hPositiveMFTchi2PerNcls", "hPositiveMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "D0/hNegativeMFTchi2PerNcls", "hNegativeMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "D0/hPositiveMFTchi2", "hPositiveMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + insertHist(modePrefixes[iMode] + "D0/hNegativeMFTchi2", "hNegativeMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + if (doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "D0/hPositivePtResolution", "hPositivePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "D0/hNegativePtResolution", "hNegativePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "D0/hPositivePzResolution", "hPositivePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + insertHist(modePrefixes[iMode] + "D0/hNegativePzResolution", "hNegativePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + + insertHist(modePrefixes[iMode] + "D0/hV0XDiff", "hV0XDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "D0/hV0YDiff", "hV0YDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "D0/hV0ZDiff", "hV0ZDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "D0/hV0RadiusDiff", "hV0RadiusDiff", kTH2D, {axisConfigurations.axisV0Radius, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "D0/hV0DistZDiff", "hV0DistZDiff", kTH2D, {axisConfigurations.axisV0Z, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "D0/hV0PtResolution", "hV0PtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "D0/hV0PzResolution", "hV0PzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + } + } + if (analyseAntiD0) { + insertHist(modePrefixes[iMode] + "AntiD0/hPosDCAToPVxy", "hPosDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "AntiD0/hNegDCAToPVxy", "hNegDCAToPVxy", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "AntiD0/hPosDCAToPVz", "hPosDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVxy}); + insertHist(modePrefixes[iMode] + "AntiD0/hNegDCAToPVz", "hNegDCAToPVz", kTH1D, {axisConfigurations.axisDCAtoPVz}); + insertHist(modePrefixes[iMode] + "AntiD0/hDCADaughters", "hDCADaughters", kTH1D, {axisConfigurations.axisDCAdau}); + insertHist(modePrefixes[iMode] + "AntiD0/hCosPA", "hCosPA", kTH1D, {axisConfigurations.axisCosPA}); + insertHist(modePrefixes[iMode] + "AntiD0/hOpeningAngle", "hOpeningAngle", kTH1D, {axisConfigurations.axisOpeningAngle}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0Radius", "hV0Radius", kTH1D, {axisConfigurations.axisV0Radius}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0Z", "hV0Z", kTH1D, {axisConfigurations.axisV0Z}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0Rapidity", "hV0Rapidity", kTH1D, {axisConfigurations.axisV0Rapidity}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0LifetimeK0s", "hV0LifetimeK0s", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0LifetimeLambda", "hV0LifetimeLambda", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0LifetimeD0", "hV0LifetimeD0", kTH1D, {axisConfigurations.axisV0ProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0PseudoLifetimeK0s", "hV0PseudoLifetimeK0s", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0PseudoLifetimeLambda", "hV0PseudoLifetimeLambda", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0PseudoLifetimeD0", "hV0PseudoLifetimeD0", kTH1D, {axisConfigurations.axisV0PseudoProperLifeTime}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassK0s", "hV0InvMassK0s", kTH1D, {axisConfigurations.axisK0Mass}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassLambda", "hV0InvMassLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassAntiLambda", "hV0InvMassAntiLambda", kTH1D, {axisConfigurations.axisLambdaMass}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassD0", "hV0InvMassD0", kTH1D, {axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassAntiD0", "hV0InvMassAntiD0", kTH1D, {axisConfigurations.axisD0Mass}); + if (doCompleteTopoQA) { + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassK0sVsAntiD0", "hV0InvMassK0sVsAntiD0", kTH2D, {axisConfigurations.axisK0Mass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassLambdaVsAntiD0", "hV0InvMassLambdaVsAntiD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0InvMassAntiLambdaVsAntiD0", "hV0InvMassAntiLambdaVsAntiD0", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisD0Mass}); + } + + insertHist(modePrefixes[iMode] + "AntiD0/hPositiveMFTcls", "hPositiveMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "AntiD0/hNegativeMFTcls", "hNegativeMFTcls", kTH1D, {axisConfigurations.axisMFTclus}); + insertHist(modePrefixes[iMode] + "AntiD0/hPositiveMFTchi2PerNcls", "hPositiveMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "AntiD0/hNegativeMFTchi2PerNcls", "hNegativeMFTchi2PerNcls", kTH1D, {axisConfigurations.axisMFTchi2NDF}); + insertHist(modePrefixes[iMode] + "AntiD0/hPositiveMFTchi2", "hPositiveMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + insertHist(modePrefixes[iMode] + "AntiD0/hNegativeMFTchi2", "hNegativeMFTchi2", kTH1D, {axisConfigurations.axisMFTchi2}); + if (doprocessMonteCarlo && iMode == kDefault) { + insertHist(modePrefixes[iMode] + "AntiD0/hPositivePtResolution", "hPositivePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "AntiD0/hNegativePtResolution", "hNegativePtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "AntiD0/hPositivePzResolution", "hPositivePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + insertHist(modePrefixes[iMode] + "AntiD0/hNegativePzResolution", "hNegativePzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + + insertHist(modePrefixes[iMode] + "AntiD0/hV0XDiff", "hV0XDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0YDiff", "hV0YDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0ZDiff", "hV0ZDiff", kTH2D, {axisConfigurations.axisPosition, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0RadiusDiff", "hV0RadiusDiff", kTH2D, {axisConfigurations.axisV0Radius, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0DistZDiff", "hV0DistZDiff", kTH2D, {axisConfigurations.axisV0Z, axisConfigurations.axisPositionDiff}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0PtResolution", "hV0PtResolution", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisPtResol}); + insertHist(modePrefixes[iMode] + "AntiD0/hV0PzResolution", "hV0PzResolution", kTH2D, {axisConfigurations.axisPz, axisConfigurations.axisPzResol}); + } + } + } + + // Check if doing the right thing in AP space please + insertHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosAll", "h2dArmenterosAll", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + insertHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosK0sSelected", "h2dArmenterosK0sSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + insertHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosLambdaSelected", "h2dArmenterosLambdaSelected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + insertHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosD0Selected", "h2dArmenterosD0Selected", kTH2D, {axisConfigurations.axisAPAlpha, axisConfigurations.axisAPQt}); + } + + // Creation of histograms: MC generated + if (doprocessGenerated) { + insertHist("hGenEvents", "hGenEvents", kTH2D, {{axisConfigurations.axisNch}, {2, -0.5f, +1.5f}}); + getHist("hGenEvents")->GetYaxis()->SetBinLabel(1, "All gen. events"); + getHist("hGenEvents")->GetYaxis()->SetBinLabel(2, "Gen. with at least 1 rec. events"); + insertHist("hGenEventCentrality", "hGenEventCentrality", kTH1D, {{101, 0.0f, 101.0f}}); + + insertHist("hCentralityVsNcoll_beforeEvSel", "hCentralityVsNcoll_beforeEvSel", kTH2D, {axisConfigurations.axisCentrality, {50, -0.5f, 49.5f}}); + insertHist("hCentralityVsNcoll_afterEvSel", "hCentralityVsNcoll_afterEvSel", kTH2D, {axisConfigurations.axisCentrality, {50, -0.5f, 49.5f}}); + + insertHist("hCentralityVsMultMC", "hCentralityVsMultMC", kTH2D, {{101, 0.0f, 101.0f}, axisConfigurations.axisNch}); + + insertHist("h2dGenK0Short", "h2dGenK0Short;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + insertHist("h2dGenLambda", "h2dGenLambda;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + insertHist("h2dGenAntiLambda", "h2dGenAntiLambda;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + insertHist("h2dGenXiMinus", "h2dGenXiMinus;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + insertHist("h2dGenXiPlus", "h2dGenXiPlus;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + insertHist("h2dGenOmegaMinus", "h2dGenOmegaMinus;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + insertHist("h2dGenOmegaPlus", "h2dGenOmegaPlus;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + + insertHist("h2dGenD0", "h2dGenD0;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + insertHist("h2dGenAntiD0", "h2dGenAntiD0;Centrality (%); #it{p}_{T} (GeV/#it{c})", kTH2D, {axisConfigurations.axisCentrality, axisConfigurations.axisPt}); + + insertHist("h2dGenK0ShortVsMultMC_RecoedEvt", "h2dGenK0ShortVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenLambdaVsMultMC_RecoedEvt", "h2dGenLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenAntiLambdaVsMultMC_RecoedEvt", "h2dGenAntiLambdaVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenXiMinusVsMultMC_RecoedEvt", "h2dGenXiMinusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenXiPlusVsMultMC_RecoedEvt", "h2dGenXiPlusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenOmegaMinusVsMultMC_RecoedEvt", "h2dGenOmegaMinusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenOmegaPlusVsMultMC_RecoedEvt", "h2dGenOmegaPlusVsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + + insertHist("h2dGenD0VsMultMC_RecoedEvt", "h2dGenD0VsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenAntiD0VsMultMC_RecoedEvt", "h2dGenAntiD0VsMultMC_RecoedEvt", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + + insertHist("h2dGenK0ShortVsMultMC", "h2dGenK0ShortVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenLambdaVsMultMC", "h2dGenLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenAntiLambdaVsMultMC", "h2dGenAntiLambdaVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenXiMinusVsMultMC", "h2dGenXiMinusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenXiPlusVsMultMC", "h2dGenXiPlusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenOmegaMinusVsMultMC", "h2dGenOmegaMinusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenOmegaPlusVsMultMC", "h2dGenOmegaPlusVsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + + insertHist("h2dGenD0VsMultMC", "h2dGenD0VsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + insertHist("h2dGenAntiD0VsMultMC", "h2dGenAntiD0VsMultMC", kTH2D, {axisConfigurations.axisNch, axisConfigurations.axisPt}); + } + + if (fitterConfigurations.useLUTMatCorr && fitterConfigurations.useTGeoMatCorr) { + LOG(fatal) << "Cannot run with both useLUTMatCorr = on and useTGeoMatCorr = on. Please check your configuration!"; + } + + // standards hardcoded in builder ... + // ...but can be changed easily since fitter is public + fitter.setPropagateToPCA(fitterConfigurations.propagateToPCA); + fitter.setMaxR(200.); + fitter.setMinParamChange(fitterConfigurations.minParamChange); // 1e-3 for DCAfitter ; 4.0 for FwdDCAfitter + fitter.setMinRelChi2Change(fitterConfigurations.minRelChi2Change); // 0.9 for DCAfitter ; 1e-3 for FwdDCAfitter + // fitter.setMaxDZIni(1e9); + // fitter.setMaxDXYIni(4.0f); + // fitter.setMaxChi2(1e9); + fitter.setUseAbsDCA(fitterConfigurations.useAbsDCA); + // fitter.setWeightedFinalPCA(false); + fitter.setTGeoMat(fitterConfigurations.useTGeoMatCorr); + // LUT has to be loaded later + lut = nullptr; + // fitter.setMatCorrType(o2::base::Propagator::MatCorrType::USEMatCorrLUT); + + // mag field has to be set later + fitter.setBz(-999.9f); // will NOT make sense if not changed + + // inspect histogram sizes, please + histos.print(); + } + + // ______________________________________________________ + // Return centrality estimate for a given collision. + // If takeMcCentrality is enabled, the centrality is taken from the MC collision; otherwise it is taken + // from the reconstructed collision. Returns -1 if no corresponding centrality estimator is found or if no MC collision is associated to the recoed collision. + template + auto getCentralityRun3(TCollision const& collision) + { + // Helper lambda to extract centrality from any object exposing the cent* columns + auto extractCentrality = [this](auto const& coll) -> float { + switch (centralityEstimator) { + case kCentFT0C: + return coll.centFT0C(); + case kCentFT0M: + return coll.centFT0M(); + case kCentFT0CVariant1: + return coll.centFT0CVariant1(); + // case kCentMFT: return coll.centMFT(); + case kCentNGlobal: + return coll.centNGlobal(); + case kCentFV0A: + return coll.centFV0A(); + default: + return -1.f; + } + }; + return extractCentrality(collision); + } + + // ______________________________________________________ + // Return slicing output + template + auto getGroupedCollisions(TCollisions const& collisions, int globalIndex) + { + return collisions.sliceBy(perMcCollision, globalIndex); + } + + template + void initCCDB(TBCs const& bcs, TCollision const& collision) + { + auto bc = collision.template bc_as(); + if (!bcs.size()) { + LOGF(warn, "No BC found, skipping this DF."); + return; // signal to skip this DF + } + + if (mRunNumber == bc.runNumber()) { + return; + } + + mRunNumber = bc.runNumber(); + // Fetching magnetic field if requested + // In case override, don't proceed, please - no CCDB access required + if (ccdbConfigurations.useCustomMagField) { + magField = ccdbConfigurations.customMagField; + } else { + grpmag = ccdb->getForRun(ccdbConfigurations.grpmagPath, mRunNumber); + if (!grpmag) { + LOG(fatal) << "Got nullptr from CCDB for path " << ccdbConfigurations.grpmagPath << " of object GRPMagField and " << ccdbConfigurations.grpPath << " of object GRPObject for run " << mRunNumber; + } + o2::base::Propagator::initFieldFromGRP(grpmag); + // Fetch magnetic field from ccdb for current collision + magField = std::lround(5.f * grpmag->getL3Current() / 30000.f); + LOG(info) << "Retrieved GRP for run " << mRunNumber << " with magnetic field of " << magField << " kZG"; + } + // load matLUT for this timestamp + if (!lut) { + LOG(info) << "Loading material look-up table for timestamp: " << mRunNumber; + lut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdb->template getForRun(ccdbConfigurations.lutPath.value, mRunNumber)); + } else { + LOG(info) << "Material look-up table already in place. Not reloading."; + } + LOG(info) << "Setting global propagator material propagation LUT"; + o2::base::Propagator::Instance()->setMatLUT(lut); + + if (fitterConfigurations.useLUTMatCorr) { + fitter.setMatLUT(lut); + } + fitter.setBz(magField); + } + + // Ideal Armenteros-Podolanski ellipse of a two-body decay hypothesis: centre and half-axes + struct ArmenterosEllipse { + float alphaCenter; // (E*_pos - E*_neg) / M, vanishes for symmetric decays + float alphaHalfWidth; // 2 qStar / (beta M) + float qStar; // daughter momentum in the mother rest frame = maximum qT + }; + + ArmenterosEllipse armenterosEllipse(float pTot, float massMother, float massPositive, float massNegative) + { + const float energyPositive = (massMother * massMother + massPositive * massPositive - massNegative * massNegative) / (2.f * massMother); + const float qStar = std::sqrt(std::max(0.f, energyPositive * energyPositive - massPositive * massPositive)); + float alphaHalfWidth = 2.f * qStar / massMother; // beta -> 1 limit + if (v0Selections.armPodBandUseBeta && pTot > 1e-3f) { + alphaHalfWidth *= std::hypot(pTot, massMother) / pTot; // 1 / beta of the candidate + } + return ArmenterosEllipse{2.f * energyPositive / massMother - 1.f, alphaHalfWidth, qStar}; + } + + // Normalised distance to the centre of the ideal ellipse, measured with its two half-axes scaled + // independently: 1 on the scaled ellipse itself, smaller inside it, larger outside it. + float armenterosDistance(ArmenterosEllipse const& ellipse, float alphaArm, float qtArm, float alphaScale, float qtScale) + { + return std::hypot((alphaArm - ellipse.alphaCenter) / (alphaScale * ellipse.alphaHalfWidth), qtArm / (qtScale * ellipse.qStar)); + } + + // Crescent-shaped band around the two-body decay arc in the Armenteros-Podolanski plane + template + bool passesArmenterosBand(TV0 const& v0, float massMother, float massPositive, float massNegative, const char* species) + { + if (!v0Selections.useArmPodBand) { + return true; + } + const ArmenterosEllipse ellipse = armenterosEllipse(v0.pTot, massMother, massPositive, massNegative); + if (armenterosDistance(ellipse, v0.AlphaArm, v0.QtArm, v0Selections.armPodBand->get(species, "alphaScaleMax"), v0Selections.armPodBand->get(species, "qtScaleMax")) > 1.f) { + return false; // outside the outer ellipse + } + const float alphaScaleMin = v0Selections.armPodBand->get(species, "alphaScaleMin"); + const float qtScaleMin = v0Selections.armPodBand->get(species, "qtScaleMin"); + if (alphaScaleMin < 1e-4f || qtScaleMin < 1e-4f) { + return true; // no inner ellipse requested: the whole outer ellipse is kept + } + return armenterosDistance(ellipse, v0.AlphaArm, v0.QtArm, alphaScaleMin, qtScaleMin) > 1.f; + } + + template + uint64_t computeReconstructionBitmap(TV0 const& v0, float rapK0s, float rapLambda, float rapD0) + // precalculate this information so that a check is one mask operation, not many + { + uint64_t bitMap = 0; + // + // Base topological variables + // + // v0 radius min/max selections + if (v0.Radius > v0Selections.v0RadiusMin) { + BITSET(bitMap, selRadius); + } + if (v0.Radius < v0Selections.v0RadiusMax) { + BITSET(bitMap, selRadiusMax); + } + // v0 radius min/max selections + if (v0.Zdist > v0Selections.v0Zmin) { + BITSET(bitMap, selZmin); + } + if (v0.Zdist < v0Selections.v0Zmax) { + BITSET(bitMap, selZmax); + } + // DCA proton and pion to PV for Lambda and AntiLambda decay hypotheses + if ((!v0Selections.rejectFailedPropagation || std::fabs(v0.dcaPosToPVxy) < o2::track::DefaultDCA) && + std::fabs(v0.dcaPosToPVxy) > v0Selections.dcaPosToPVxy) { + BITSET(bitMap, selDCAPosToPVxy); + } + if ((!v0Selections.rejectFailedPropagation || std::fabs(v0.dcaNegToPVxy) < o2::track::DefaultDCA) && + std::fabs(v0.dcaNegToPVxy) > v0Selections.dcaNegToPVxy) { + BITSET(bitMap, selDCANegToPVxy); + } + // DCA proton and pion to PV for Lambda and AntiLambda decay hypotheses + if ((!v0Selections.rejectFailedPropagation || std::fabs(v0.dcaPosToPVz) < o2::track::DefaultDCA) && + std::fabs(v0.dcaPosToPVz) > v0Selections.dcaPosToPVz) { + BITSET(bitMap, selDCAPosToPVz); + } + if ((!v0Selections.rejectFailedPropagation || std::fabs(v0.dcaNegToPVz) < o2::track::DefaultDCA) && + std::fabs(v0.dcaNegToPVz) > v0Selections.dcaNegToPVz) { + BITSET(bitMap, selDCANegToPVz); + } + // V0 cosine of pointing angle + if (v0.CosPA > v0Selections.v0CosPA) { + BITSET(bitMap, selCosPA); + } + // V0 opening angle + if (v0.OpAngle > v0Selections.v0OpAngle) { + BITSET(bitMap, selOpAngle); + } + // DCA between v0 daughters + if (v0.DCADau < v0Selections.dcaV0Dau) { + BITSET(bitMap, selDCAV0Dau); + } + + // + // rapidity + // + if (rapK0s > v0Selections.rapidityMin) { + BITSET(bitMap, selK0ShortRapidityMin); + } + if (rapK0s < v0Selections.rapidityMax) { + BITSET(bitMap, selK0ShortRapidityMax); + } + if (rapLambda > v0Selections.rapidityMin) { + BITSET(bitMap, selLambdaRapidityMin); + } + if (rapLambda < v0Selections.rapidityMax) { + BITSET(bitMap, selLambdaRapidityMax); + } + if (rapD0 > v0Selections.rapidityMin) { + BITSET(bitMap, selD0RapidityMin); + } + if (rapD0 < v0Selections.rapidityMax) { + BITSET(bitMap, selD0RapidityMax); + } + + // + // competing mass rejection + // + if ((analyseD0 || analyseAntiD0) && + (std::fabs(v0.mK0s - o2::constants::physics::MassK0Short) > v0Selections.compMassRejectionK0Short)) { + BITSET(bitMap, selK0ShortMassRejection); + } + if ((analyseD0 || analyseAntiD0) && + (std::fabs(v0.mLambda - o2::constants::physics::MassLambda0) > v0Selections.compMassRejectionLambda)) { + BITSET(bitMap, selLambdaMassRejection); + } + if ((analyseLambda || analyseAntiLambda) && std::fabs(v0.mK0s - o2::constants::physics::MassK0Short) > v0Selections.compMassRejectionK0Short) { + BITSET(bitMap, selK0ShortMassRejection); + } + if (analyseK0Short && std::fabs(v0.mLambda - o2::constants::physics::MassLambda0) > v0Selections.compMassRejectionLambda) { + BITSET(bitMap, selLambdaMassRejection); + } + + // + // MFT quality flags + // + if (v0.posNclusters > v0Selections.minMFTclusters && + v0.posChi2 < v0Selections.maxMFTchi2 && + v0.posChi2PerNclus < v0Selections.maxMFTchi2PerNcls) { + BITSET(bitMap, selPosGoodMFTTrack); + } + if (v0.negNclusters > v0Selections.minMFTclusters && + v0.negChi2 < v0Selections.maxMFTchi2 && + v0.negChi2PerNclus < v0Selections.maxMFTchi2PerNcls) { + BITSET(bitMap, selNegGoodMFTTrack); + } + + // + // proper lifetime + if ((analyseD0 || analyseAntiD0) && v0.DistOverTotMom * o2::constants::physics::MassD0 < v0Selections.lifetimeCut->get("lifetimecutD0")) { + BITSET(bitMap, selD0CTau); + } + if ((analyseLambda || analyseAntiLambda) && v0.DistOverTotMom * o2::constants::physics::MassLambda0 < v0Selections.lifetimeCut->get("lifetimecutLambda")) { + BITSET(bitMap, selLambdaCTau); + } + if (analyseK0Short && v0.DistOverTotMom * o2::constants::physics::MassK0Short < v0Selections.lifetimeCut->get("lifetimecutK0S")) { + BITSET(bitMap, selK0ShortCTau); + } + + // + // Pseudo-proper lifetime + if ((analyseD0 || analyseAntiD0) && v0.ZdistOverPz * o2::constants::physics::MassD0 > v0Selections.minPseudolifetime) { + BITSET(bitMap, selD0PseudoLifetimeMin); + } + if ((analyseD0 || analyseAntiD0) && v0.ZdistOverPz * o2::constants::physics::MassD0 < v0Selections.maxPseudolifetime) { + BITSET(bitMap, selD0PseudoLifetimeMax); + } + if ((analyseLambda || analyseAntiLambda) && v0.ZdistOverPz * o2::constants::physics::MassLambda0 > v0Selections.minPseudolifetime) { + BITSET(bitMap, selLambdaPseudoLifetimeMin); + } + if ((analyseLambda || analyseAntiLambda) && v0.ZdistOverPz * o2::constants::physics::MassLambda0 < v0Selections.maxPseudolifetime) { + BITSET(bitMap, selLambdaPseudoLifetimeMax); + } + if (analyseK0Short && v0.ZdistOverPz * o2::constants::physics::MassK0Short > v0Selections.minPseudolifetime) { + BITSET(bitMap, selK0ShortPseudoLifetimeMin); + } + if (analyseK0Short && v0.ZdistOverPz * o2::constants::physics::MassK0Short < v0Selections.maxPseudolifetime) { + BITSET(bitMap, selK0ShortPseudoLifetimeMax); + } + + // + // armenteros + // legacy K0Short "V" cut, plus an optional box and the optional elliptic band around the decay arc + const bool armPodVCut = (v0Selections.armPodCut < 1e-4 || v0.QtArm * v0Selections.armPodCut > std::abs(v0.AlphaArm)); + const bool armPodBox = (v0.QtArm > v0Selections.minQt && v0.QtArm < v0Selections.maxQt && + v0.AlphaArm > v0Selections.minAlpha && v0.AlphaArm < v0Selections.maxAlpha); + if (armPodVCut && armPodBox && + passesArmenterosBand(v0, o2::constants::physics::MassK0Short, o2::constants::physics::MassPiPlus, o2::constants::physics::MassPiMinus, "K0Short")) { + BITSET(bitMap, selK0ShortArmenteros); + } + if (armPodBox && passesArmenterosBand(v0, o2::constants::physics::MassLambda0, o2::constants::physics::MassProton, o2::constants::physics::MassPiMinus, "Lambda")) { + BITSET(bitMap, selLambdaArmenteros); + } + if (armPodBox && passesArmenterosBand(v0, o2::constants::physics::MassLambda0, o2::constants::physics::MassPiPlus, o2::constants::physics::MassProtonBar, "Lambda")) { + BITSET(bitMap, selAntiLambdaArmenteros); + } + if (armPodBox && passesArmenterosBand(v0, o2::constants::physics::MassD0, o2::constants::physics::MassKPlus, o2::constants::physics::MassPiMinus, "D0")) { + BITSET(bitMap, selD0Armenteros); + } + if (armPodBox && passesArmenterosBand(v0, o2::constants::physics::MassD0, o2::constants::physics::MassPiPlus, o2::constants::physics::MassKMinus, "D0")) { + BITSET(bitMap, selAntiD0Armenteros); + } + + return bitMap; + } + + template + uint64_t computeMCAssociation(TV0 v0) + // precalculate this information so that a check is one mask operation, not many + { + uint64_t bitMap = 0; + bool isPositiveProton = v0.pdgCodePositive == PDG_t::kProton; + bool isPositiveKaon = v0.pdgCodePositive == PDG_t::kKPlus; + bool isPositivePion = v0.pdgCodePositive == PDG_t::kPiPlus || (doTreatPiToMuon && v0.pdgCodePositive == PDG_t::kMuonPlus); + bool isNegativeProton = v0.pdgCodeNegative == PDG_t::kProtonBar; + bool isNegativeKaon = v0.pdgCodeNegative == PDG_t::kKMinus; + bool isNegativePion = v0.pdgCodeNegative == PDG_t::kPiMinus || (doTreatPiToMuon && v0.pdgCodeNegative == PDG_t::kMuonMinus); + + if (v0.pdgCode == PDG_t::kK0Short && isPositivePion && isNegativePion) { + BITSET(bitMap, selConsiderK0Short); + if (v0.isPhysicalPrimary) + BITSET(bitMap, selPhysPrimK0Short); + } + if (v0.pdgCode == PDG_t::kLambda0 && isPositiveProton && isNegativePion) { + BITSET(bitMap, selConsiderLambda); + if (v0.isPhysicalPrimary) + BITSET(bitMap, selPhysPrimLambda); + } + if (v0.pdgCode == PDG_t::kLambda0Bar && isPositivePion && isNegativeProton) { + BITSET(bitMap, selConsiderAntiLambda); + if (v0.isPhysicalPrimary) + BITSET(bitMap, selPhysPrimAntiLambda); + } + if (v0.pdgCode == o2::constants::physics::kD0 && isPositivePion && isNegativeKaon) { + BITSET(bitMap, selConsiderD0); + if (v0.isPhysicalPrimary) + BITSET(bitMap, selPhysPrimD0); + } + if (v0.pdgCode == o2::constants::physics::kD0Bar && isPositiveKaon && isNegativePion) { + BITSET(bitMap, selConsiderAntiD0); + if (v0.isPhysicalPrimary) + BITSET(bitMap, selPhysPrimAntiD0); + } + return bitMap; + } + + bool verifyMask(uint64_t bitmap, uint64_t mask) + { + return (bitmap & mask) == mask; + } + + template + void analyseCandidate(TV0 v0, float pt, float centrality, uint64_t selMap, uint8_t gapSide, int& nK0Shorts, int& nLambdas, int& nAntiLambdas, int& nD0s, int& nAntiD0s, int iMode = kDefault) + // precalculate this information so that a check is one mask operation, not many + { + bool passK0ShortSelections = false; + bool passLambdaSelections = false; + bool passAntiLambdaSelections = false; + bool passD0Selections = false; + bool passAntiD0Selections = false; + + // machine learning is on, go for calculation of thresholds + // FIXME THIS NEEDS ADJUSTING + passK0ShortSelections = verifyMask(selMap, maskSelectionK0Short); + passLambdaSelections = verifyMask(selMap, maskSelectionLambda); + passAntiLambdaSelections = verifyMask(selMap, maskSelectionAntiLambda); + passD0Selections = verifyMask(selMap, maskSelectionD0); + passAntiD0Selections = verifyMask(selMap, maskSelectionAntiD0); + + double invMassK0Short = v0.mK0s; + double invMassLambda = v0.mLambda; + double invMassAntiLambda = v0.mAntiLambda; + double invMassD0 = v0.mD0; + double invMassAntiD0 = v0.mAntiD0; + + // fill AP plot for all V0s + getHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosAll")->Fill(v0.AlphaArm, v0.QtArm); + + // __________________________________________ + // fill with no selection if plain QA requested + if (doPlainTopoQA) { + getHist(modePrefixes[iMode] + "hPosDCAToPVxy")->Fill(v0.dcaPosToPVxy); + getHist(modePrefixes[iMode] + "hNegDCAToPVxy")->Fill(v0.dcaNegToPVxy); + getHist(modePrefixes[iMode] + "hPosDCAToPVz")->Fill(v0.dcaPosToPVz); + getHist(modePrefixes[iMode] + "hNegDCAToPVz")->Fill(v0.dcaNegToPVz); + getHist(modePrefixes[iMode] + "hDCADaughters")->Fill(v0.DCADau); + getHist(modePrefixes[iMode] + "hCosPA")->Fill(v0.CosPA); + getHist(modePrefixes[iMode] + "hOpeningAngle")->Fill(v0.OpAngle); + getHist(modePrefixes[iMode] + "hV0Radius")->Fill(v0.Radius); + getHist(modePrefixes[iMode] + "hV0Z")->Fill(v0.Zdist); + if (analyseK0Short) { + getHist(modePrefixes[iMode] + "hV0Rapidity")->Fill(v0.rapidityK0s); + } else if (analyseLambda || analyseAntiLambda) { + getHist(modePrefixes[iMode] + "hV0Rapidity")->Fill(v0.rapidityLambda); + } else if (analyseD0 || analyseAntiD0) { + getHist(modePrefixes[iMode] + "hV0Rapidity")->Fill(v0.rapidityD0); + } + getHist(modePrefixes[iMode] + "hV0LifetimeK0s")->Fill(v0.DistOverTotMom * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "hV0LifetimeLambda")->Fill(v0.DistOverTotMom * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "hV0LifetimeD0")->Fill(v0.DistOverTotMom * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "hV0PseudoLifetimeK0s")->Fill(v0.ZdistOverPz * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "hV0PseudoLifetimeLambda")->Fill(v0.ZdistOverPz * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "hV0PseudoLifetimeD0")->Fill(v0.ZdistOverPz * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "hV0InvMassK0s")->Fill(invMassK0Short); + getHist(modePrefixes[iMode] + "hV0InvMassLambda")->Fill(invMassLambda); + getHist(modePrefixes[iMode] + "hV0InvMassAntiLambda")->Fill(invMassAntiLambda); + getHist(modePrefixes[iMode] + "hV0InvMassD0")->Fill(invMassD0); + getHist(modePrefixes[iMode] + "hV0InvMassAntiD0")->Fill(invMassAntiD0); + if (doCompleteTopoQA) { + getHist(modePrefixes[iMode] + "hV0InvMassK0sVsLambda")->Fill(invMassK0Short, invMassLambda); + getHist(modePrefixes[iMode] + "hV0InvMassK0sVsAntiLambda")->Fill(invMassK0Short, invMassAntiLambda); + getHist(modePrefixes[iMode] + "hV0InvMassK0sVsD0")->Fill(invMassK0Short, invMassD0); + getHist(modePrefixes[iMode] + "hV0InvMassK0sVsAntiD0")->Fill(invMassK0Short, invMassAntiD0); + getHist(modePrefixes[iMode] + "hV0InvMassLambdaVsD0")->Fill(invMassLambda, invMassD0); + getHist(modePrefixes[iMode] + "hV0InvMassAntiLambdaVsD0")->Fill(invMassAntiLambda, invMassD0); + getHist(modePrefixes[iMode] + "hV0InvMassLambdaVsAntiD0")->Fill(invMassLambda, invMassAntiD0); + getHist(modePrefixes[iMode] + "hV0InvMassAntiLambdaVsAntiD0")->Fill(invMassAntiLambda, invMassAntiD0); + } + + getHist(modePrefixes[iMode] + "hPositiveMFTcls")->Fill(v0.posNclusters); + getHist(modePrefixes[iMode] + "hNegativeMFTcls")->Fill(v0.negNclusters); + getHist(modePrefixes[iMode] + "hPositiveMFTchi2PerNcls")->Fill(v0.posChi2PerNclus); + getHist(modePrefixes[iMode] + "hNegativeMFTchi2PerNcls")->Fill(v0.negChi2PerNclus); + getHist(modePrefixes[iMode] + "hPositiveMFTchi2")->Fill(v0.posChi2); + getHist(modePrefixes[iMode] + "hNegativeMFTchi2")->Fill(v0.negChi2); + if (doprocessMonteCarlo && iMode == kDefault) { + getHist(modePrefixes[iMode] + "hPositivePtResolution")->Fill(v0.positivePtMc(), (v0.positivePtMc() - v0.positivePt()) / v0.positivePtMc()); + getHist(modePrefixes[iMode] + "hNegativePtResolution")->Fill(v0.negativePtMc(), (v0.negativePtMc() - v0.negativePt()) / v0.negativePtMc()); + getHist(modePrefixes[iMode] + "hPositivePzResolution")->Fill(v0.positivePzMc(), (v0.positivePzMc() - v0.positivePz()) / v0.positivePzMc()); + getHist(modePrefixes[iMode] + "hNegativePzResolution")->Fill(v0.negativePzMc(), (v0.negativePzMc() - v0.negativePz()) / v0.negativePzMc()); + + getHist(modePrefixes[iMode] + "hV0XDiff")->Fill(v0.xMc, (v0.xMc - v0.X)); + getHist(modePrefixes[iMode] + "hV0YDiff")->Fill(v0.yMc, (v0.yMc - v0.Y)); + getHist(modePrefixes[iMode] + "hV0ZDiff")->Fill(v0.zMc, (v0.zMc - v0.Z)); + getHist(modePrefixes[iMode] + "hV0RadiusDiff")->Fill(v0.RadiusMc, (v0.RadiusMc - v0.Radius) / v0.RadiusMc); + getHist(modePrefixes[iMode] + "hV0DistZDiff")->Fill(v0.ZdistMc, (v0.ZdistMc - v0.Zdist) / v0.ZdistMc); + getHist(modePrefixes[iMode] + "hV0PtResolution")->Fill(v0.pTMc, (v0.pTMc - v0.pT) / v0.pTMc); + getHist(modePrefixes[iMode] + "hV0PzResolution")->Fill(v0.pZMc, (v0.pZMc - v0.pZ) / v0.pZMc); + } + } + + // Fill first bin: all candidates + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->Fill(0); + // Loop over all bits in the enum and fill if passed + for (uint64_t i = 0; i <= selPhysPrimAntiD0; i++) { + if (BITCHECK(selMap, i)) { + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->Fill(i + 1); // +1 because bin 0 = "All" + } + } + + // __________________________________________ + // main analysis + if (passK0ShortSelections && analyseK0Short) { + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->Fill(selPhysPrimAntiD0 + 2); // + getHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosK0sSelected")->Fill(v0.AlphaArm, v0.QtArm); // cross-check + getHist(modePrefixes[iMode] + "h3dMassK0Short")->Fill(centrality, pt, invMassK0Short); + if (doUPCanalysis) { + if (gapSide == 0) + getHist(modePrefixes[iMode] + "h3dMassK0ShortSGA")->Fill(centrality, pt, invMassK0Short); + else if (gapSide == 1) + getHist(modePrefixes[iMode] + "h3dMassK0ShortSGC")->Fill(centrality, pt, invMassK0Short); + else if (gapSide == 2) + getHist(modePrefixes[iMode] + "h3dMassK0ShortDG")->Fill(centrality, pt, invMassK0Short); + else + getHist(modePrefixes[iMode] + "h3dMassK0ShortHadronic")->Fill(centrality, pt, invMassK0Short); + } + getHist(modePrefixes[iMode] + "hMassK0Short")->Fill(invMassK0Short); + if (doPlainTopoQA) { + getHist(modePrefixes[iMode] + "K0Short/hPosDCAToPVxy")->Fill(v0.dcaPosToPVxy); + getHist(modePrefixes[iMode] + "K0Short/hNegDCAToPVxy")->Fill(v0.dcaNegToPVxy); + getHist(modePrefixes[iMode] + "K0Short/hPosDCAToPVz")->Fill(v0.dcaPosToPVz); + getHist(modePrefixes[iMode] + "K0Short/hNegDCAToPVz")->Fill(v0.dcaNegToPVz); + getHist(modePrefixes[iMode] + "K0Short/hDCADaughters")->Fill(v0.DCADau); + getHist(modePrefixes[iMode] + "K0Short/hCosPA")->Fill(v0.CosPA); + getHist(modePrefixes[iMode] + "K0Short/hOpeningAngle")->Fill(v0.OpAngle); + getHist(modePrefixes[iMode] + "K0Short/hV0Radius")->Fill(v0.Radius); + getHist(modePrefixes[iMode] + "K0Short/hV0Z")->Fill(v0.Zdist); + getHist(modePrefixes[iMode] + "K0Short/hV0Rapidity")->Fill(v0.rapidityK0s); + getHist(modePrefixes[iMode] + "K0Short/hV0LifetimeK0s")->Fill(v0.DistOverTotMom * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "K0Short/hV0LifetimeLambda")->Fill(v0.DistOverTotMom * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "K0Short/hV0LifetimeD0")->Fill(v0.DistOverTotMom * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "K0Short/hV0PseudoLifetimeK0s")->Fill(v0.ZdistOverPz * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "K0Short/hV0PseudoLifetimeLambda")->Fill(v0.ZdistOverPz * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "K0Short/hV0PseudoLifetimeD0")->Fill(v0.ZdistOverPz * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0s")->Fill(invMassK0Short); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassLambda")->Fill(invMassLambda); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassAntiLambda")->Fill(invMassAntiLambda); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassD0")->Fill(invMassD0); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassAntiD0")->Fill(invMassAntiD0); + if (doCompleteTopoQA) { + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsLambda")->Fill(invMassK0Short, invMassLambda); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsAntiLambda")->Fill(invMassK0Short, invMassAntiLambda); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsD0")->Fill(invMassK0Short, invMassD0); + getHist(modePrefixes[iMode] + "K0Short/hV0InvMassK0sVsAntiD0")->Fill(invMassK0Short, invMassAntiD0); + } + + getHist(modePrefixes[iMode] + "K0Short/hPositiveMFTcls")->Fill(v0.posNclusters); + getHist(modePrefixes[iMode] + "K0Short/hNegativeMFTcls")->Fill(v0.negNclusters); + getHist(modePrefixes[iMode] + "K0Short/hPositiveMFTchi2PerNcls")->Fill(v0.posChi2PerNclus); + getHist(modePrefixes[iMode] + "K0Short/hNegativeMFTchi2PerNcls")->Fill(v0.negChi2PerNclus); + getHist(modePrefixes[iMode] + "K0Short/hPositiveMFTchi2")->Fill(v0.posChi2); + getHist(modePrefixes[iMode] + "K0Short/hNegativeMFTchi2")->Fill(v0.negChi2); + if (doprocessMonteCarlo && iMode == kDefault) { + getHist(modePrefixes[iMode] + "K0Short/hPositivePtResolution")->Fill(v0.positivePtMc(), (v0.positivePtMc() - v0.positivePt()) / v0.positivePtMc()); + getHist(modePrefixes[iMode] + "K0Short/hNegativePtResolution")->Fill(v0.negativePtMc(), (v0.negativePtMc() - v0.negativePt()) / v0.negativePtMc()); + getHist(modePrefixes[iMode] + "K0Short/hPositivePzResolution")->Fill(v0.positivePzMc(), (v0.positivePzMc() - v0.positivePz()) / v0.positivePzMc()); + getHist(modePrefixes[iMode] + "K0Short/hNegativePzResolution")->Fill(v0.negativePzMc(), (v0.negativePzMc() - v0.negativePz()) / v0.negativePzMc()); + + getHist(modePrefixes[iMode] + "K0Short/hV0XDiff")->Fill(v0.xMc, (v0.xMc - v0.X)); + getHist(modePrefixes[iMode] + "K0Short/hV0YDiff")->Fill(v0.yMc, (v0.yMc - v0.Y)); + getHist(modePrefixes[iMode] + "K0Short/hV0ZDiff")->Fill(v0.zMc, (v0.zMc - v0.Z)); + getHist(modePrefixes[iMode] + "K0Short/hV0RadiusDiff")->Fill(v0.RadiusMc, (v0.RadiusMc - v0.Radius) / v0.RadiusMc); + getHist(modePrefixes[iMode] + "K0Short/hV0DistZDiff")->Fill(v0.ZdistMc, (v0.ZdistMc - v0.Zdist) / v0.ZdistMc); + getHist(modePrefixes[iMode] + "K0Short/hV0PtResolution")->Fill(v0.pTMc, (v0.pTMc - v0.pT) / v0.pTMc); + getHist(modePrefixes[iMode] + "K0Short/hV0PzResolution")->Fill(v0.pZMc, (v0.pZMc - v0.pZ) / v0.pZMc); + } + } + nK0Shorts++; + } + if (passLambdaSelections && analyseLambda) { + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->Fill(selPhysPrimAntiD0 + 2); // + getHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosLambdaSelected")->Fill(v0.AlphaArm, v0.QtArm); // cross-check + getHist(modePrefixes[iMode] + "h3dMassLambda")->Fill(centrality, pt, invMassLambda); + if (doUPCanalysis) { + if (gapSide == 0) + getHist(modePrefixes[iMode] + "h3dMassLambdaSGA")->Fill(centrality, pt, invMassLambda); + else if (gapSide == 1) + getHist(modePrefixes[iMode] + "h3dMassLambdaSGC")->Fill(centrality, pt, invMassLambda); + else if (gapSide == 2) + getHist(modePrefixes[iMode] + "h3dMassLambdaDG")->Fill(centrality, pt, invMassLambda); + else + getHist(modePrefixes[iMode] + "h3dMassLambdaHadronic")->Fill(centrality, pt, invMassLambda); + } + getHist(modePrefixes[iMode] + "hMassLambda")->Fill(invMassLambda); + if (doPlainTopoQA) { + getHist(modePrefixes[iMode] + "Lambda/hPosDCAToPVxy")->Fill(v0.dcaPosToPVxy); + getHist(modePrefixes[iMode] + "Lambda/hNegDCAToPVxy")->Fill(v0.dcaNegToPVxy); + getHist(modePrefixes[iMode] + "Lambda/hPosDCAToPVz")->Fill(v0.dcaPosToPVz); + getHist(modePrefixes[iMode] + "Lambda/hNegDCAToPVz")->Fill(v0.dcaNegToPVz); + getHist(modePrefixes[iMode] + "Lambda/hDCADaughters")->Fill(v0.DCADau); + getHist(modePrefixes[iMode] + "Lambda/hCosPA")->Fill(v0.CosPA); + getHist(modePrefixes[iMode] + "Lambda/hOpeningAngle")->Fill(v0.OpAngle); + getHist(modePrefixes[iMode] + "Lambda/hV0Radius")->Fill(v0.Radius); + getHist(modePrefixes[iMode] + "Lambda/hV0Z")->Fill(v0.Zdist); + getHist(modePrefixes[iMode] + "Lambda/hV0Rapidity")->Fill(v0.rapidityLambda); + getHist(modePrefixes[iMode] + "Lambda/hV0LifetimeK0s")->Fill(v0.DistOverTotMom * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "Lambda/hV0LifetimeLambda")->Fill(v0.DistOverTotMom * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "Lambda/hV0LifetimeD0")->Fill(v0.DistOverTotMom * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "Lambda/hV0PseudoLifetimeK0s")->Fill(v0.ZdistOverPz * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "Lambda/hV0PseudoLifetimeLambda")->Fill(v0.ZdistOverPz * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "Lambda/hV0PseudoLifetimeD0")->Fill(v0.ZdistOverPz * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassK0s")->Fill(invMassK0Short); + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassLambda")->Fill(invMassLambda); + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassAntiLambda")->Fill(invMassAntiLambda); + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassD0")->Fill(invMassD0); + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassAntiD0")->Fill(invMassAntiD0); + if (doCompleteTopoQA) { + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassK0sVsLambda")->Fill(invMassK0Short, invMassLambda); + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassLambdaVsD0")->Fill(invMassLambda, invMassD0); + getHist(modePrefixes[iMode] + "Lambda/hV0InvMassLambdaVsAntiD0")->Fill(invMassLambda, invMassAntiD0); + } + + getHist(modePrefixes[iMode] + "Lambda/hPositiveMFTcls")->Fill(v0.posNclusters); + getHist(modePrefixes[iMode] + "Lambda/hNegativeMFTcls")->Fill(v0.negNclusters); + getHist(modePrefixes[iMode] + "Lambda/hPositiveMFTchi2PerNcls")->Fill(v0.posChi2PerNclus); + getHist(modePrefixes[iMode] + "Lambda/hNegativeMFTchi2PerNcls")->Fill(v0.negChi2PerNclus); + getHist(modePrefixes[iMode] + "Lambda/hPositiveMFTchi2")->Fill(v0.posChi2); + getHist(modePrefixes[iMode] + "Lambda/hNegativeMFTchi2")->Fill(v0.negChi2); + if (doprocessMonteCarlo && iMode == kDefault) { + getHist(modePrefixes[iMode] + "Lambda/hPositivePtResolution")->Fill(v0.positivePtMc(), (v0.positivePtMc() - v0.positivePt()) / v0.positivePtMc()); + getHist(modePrefixes[iMode] + "Lambda/hNegativePtResolution")->Fill(v0.negativePtMc(), (v0.negativePtMc() - v0.negativePt()) / v0.negativePtMc()); + getHist(modePrefixes[iMode] + "Lambda/hPositivePzResolution")->Fill(v0.positivePzMc(), (v0.positivePzMc() - v0.positivePz()) / v0.positivePzMc()); + getHist(modePrefixes[iMode] + "Lambda/hNegativePzResolution")->Fill(v0.negativePzMc(), (v0.negativePzMc() - v0.negativePz()) / v0.negativePzMc()); + + getHist(modePrefixes[iMode] + "Lambda/hV0XDiff")->Fill(v0.xMc, (v0.xMc - v0.X)); + getHist(modePrefixes[iMode] + "Lambda/hV0YDiff")->Fill(v0.yMc, (v0.yMc - v0.Y)); + getHist(modePrefixes[iMode] + "Lambda/hV0ZDiff")->Fill(v0.zMc, (v0.zMc - v0.Z)); + getHist(modePrefixes[iMode] + "Lambda/hV0RadiusDiff")->Fill(v0.RadiusMc, (v0.RadiusMc - v0.Radius) / v0.RadiusMc); + getHist(modePrefixes[iMode] + "Lambda/hV0DistZDiff")->Fill(v0.ZdistMc, (v0.ZdistMc - v0.Zdist) / v0.ZdistMc); + getHist(modePrefixes[iMode] + "Lambda/hV0PtResolution")->Fill(v0.pTMc, (v0.pTMc - v0.pT) / v0.pTMc); + getHist(modePrefixes[iMode] + "Lambda/hV0PzResolution")->Fill(v0.pZMc, (v0.pZMc - v0.pZ) / v0.pZMc); + } + } + nLambdas++; + } + if (passAntiLambdaSelections && analyseAntiLambda) { + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->Fill(selPhysPrimAntiD0 + 2); // + getHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosLambdaSelected")->Fill(v0.AlphaArm, v0.QtArm); // cross-check + getHist(modePrefixes[iMode] + "h3dMassAntiLambda")->Fill(centrality, pt, invMassAntiLambda); + if (doUPCanalysis) { + if (gapSide == 0) + getHist(modePrefixes[iMode] + "h3dMassAntiLambdaSGA")->Fill(centrality, pt, invMassAntiLambda); + else if (gapSide == 1) + getHist(modePrefixes[iMode] + "h3dMassAntiLambdaSGC")->Fill(centrality, pt, invMassAntiLambda); + else if (gapSide == 2) + getHist(modePrefixes[iMode] + "h3dMassAntiLambdaDG")->Fill(centrality, pt, invMassAntiLambda); + else + getHist(modePrefixes[iMode] + "h3dMassAntiLambdaHadronic")->Fill(centrality, pt, invMassAntiLambda); + } + getHist(modePrefixes[iMode] + "hMassAntiLambda")->Fill(invMassAntiLambda); + if (doPlainTopoQA) { + getHist(modePrefixes[iMode] + "AntiLambda/hPosDCAToPVxy")->Fill(v0.dcaPosToPVxy); + getHist(modePrefixes[iMode] + "AntiLambda/hNegDCAToPVxy")->Fill(v0.dcaNegToPVxy); + getHist(modePrefixes[iMode] + "AntiLambda/hPosDCAToPVz")->Fill(v0.dcaPosToPVz); + getHist(modePrefixes[iMode] + "AntiLambda/hNegDCAToPVz")->Fill(v0.dcaNegToPVz); + getHist(modePrefixes[iMode] + "AntiLambda/hDCADaughters")->Fill(v0.DCADau); + getHist(modePrefixes[iMode] + "AntiLambda/hCosPA")->Fill(v0.CosPA); + getHist(modePrefixes[iMode] + "AntiLambda/hOpeningAngle")->Fill(v0.OpAngle); + getHist(modePrefixes[iMode] + "AntiLambda/hV0Radius")->Fill(v0.Radius); + getHist(modePrefixes[iMode] + "AntiLambda/hV0Z")->Fill(v0.Zdist); + getHist(modePrefixes[iMode] + "AntiLambda/hV0Rapidity")->Fill(v0.rapidityLambda); + getHist(modePrefixes[iMode] + "AntiLambda/hV0LifetimeK0s")->Fill(v0.DistOverTotMom * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "AntiLambda/hV0LifetimeLambda")->Fill(v0.DistOverTotMom * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "AntiLambda/hV0LifetimeD0")->Fill(v0.DistOverTotMom * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "AntiLambda/hV0PseudoLifetimeK0s")->Fill(v0.ZdistOverPz * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "AntiLambda/hV0PseudoLifetimeLambda")->Fill(v0.ZdistOverPz * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "AntiLambda/hV0PseudoLifetimeD0")->Fill(v0.ZdistOverPz * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassK0s")->Fill(invMassK0Short); + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassLambda")->Fill(invMassLambda); + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiLambda")->Fill(invMassAntiLambda); + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassD0")->Fill(invMassD0); + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiD0")->Fill(invMassAntiD0); + if (doCompleteTopoQA) { + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassK0sVsAntiLambda")->Fill(invMassK0Short, invMassAntiLambda); + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiLambdaVsD0")->Fill(invMassAntiLambda, invMassD0); + getHist(modePrefixes[iMode] + "AntiLambda/hV0InvMassAntiLambdaVsAntiD0")->Fill(invMassAntiLambda, invMassAntiD0); + } + + getHist(modePrefixes[iMode] + "AntiLambda/hPositiveMFTcls")->Fill(v0.posNclusters); + getHist(modePrefixes[iMode] + "AntiLambda/hNegativeMFTcls")->Fill(v0.negNclusters); + getHist(modePrefixes[iMode] + "AntiLambda/hPositiveMFTchi2PerNcls")->Fill(v0.posChi2PerNclus); + getHist(modePrefixes[iMode] + "AntiLambda/hNegativeMFTchi2PerNcls")->Fill(v0.negChi2PerNclus); + getHist(modePrefixes[iMode] + "AntiLambda/hPositiveMFTchi2")->Fill(v0.posChi2); + getHist(modePrefixes[iMode] + "AntiLambda/hNegativeMFTchi2")->Fill(v0.negChi2); + if (doprocessMonteCarlo && iMode == kDefault) { + getHist(modePrefixes[iMode] + "AntiLambda/hPositivePtResolution")->Fill(v0.positivePtMc(), (v0.positivePtMc() - v0.positivePt()) / v0.positivePtMc()); + getHist(modePrefixes[iMode] + "AntiLambda/hNegativePtResolution")->Fill(v0.negativePtMc(), (v0.negativePtMc() - v0.negativePt()) / v0.negativePtMc()); + getHist(modePrefixes[iMode] + "AntiLambda/hPositivePzResolution")->Fill(v0.positivePzMc(), (v0.positivePzMc() - v0.positivePz()) / v0.positivePzMc()); + getHist(modePrefixes[iMode] + "AntiLambda/hNegativePzResolution")->Fill(v0.negativePzMc(), (v0.negativePzMc() - v0.negativePz()) / v0.negativePzMc()); + + getHist(modePrefixes[iMode] + "AntiLambda/hV0XDiff")->Fill(v0.xMc, (v0.xMc - v0.X)); + getHist(modePrefixes[iMode] + "AntiLambda/hV0YDiff")->Fill(v0.yMc, (v0.yMc - v0.Y)); + getHist(modePrefixes[iMode] + "AntiLambda/hV0ZDiff")->Fill(v0.zMc, (v0.zMc - v0.Z)); + getHist(modePrefixes[iMode] + "AntiLambda/hV0RadiusDiff")->Fill(v0.RadiusMc, (v0.RadiusMc - v0.Radius) / v0.RadiusMc); + getHist(modePrefixes[iMode] + "AntiLambda/hV0DistZDiff")->Fill(v0.ZdistMc, (v0.ZdistMc - v0.Zdist) / v0.Zdist); + getHist(modePrefixes[iMode] + "AntiLambda/hV0PtResolution")->Fill(v0.pTMc, (v0.pTMc - v0.pT) / v0.pTMc); + getHist(modePrefixes[iMode] + "AntiLambda/hV0PzResolution")->Fill(v0.pZMc, (v0.pZMc - v0.pZ) / v0.pZMc); + } + } + nAntiLambdas++; + } + if (passD0Selections && analyseD0) { + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->Fill(selPhysPrimAntiD0 + 2); // + getHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosD0Selected")->Fill(v0.AlphaArm, v0.QtArm); // cross-check + getHist(modePrefixes[iMode] + "h3dMassD0")->Fill(centrality, pt, invMassD0); + if (doUPCanalysis) { + if (gapSide == 0) + getHist(modePrefixes[iMode] + "h3dMassD0SGA")->Fill(centrality, pt, invMassD0); + else if (gapSide == 1) + getHist(modePrefixes[iMode] + "h3dMassD0SGC")->Fill(centrality, pt, invMassD0); + else if (gapSide == 2) + getHist(modePrefixes[iMode] + "h3dMassD0DG")->Fill(centrality, pt, invMassD0); + else + getHist(modePrefixes[iMode] + "h3dMassD0Hadronic")->Fill(centrality, pt, invMassD0); + } + getHist(modePrefixes[iMode] + "hMassD0")->Fill(invMassD0); + if (doPlainTopoQA) { + getHist(modePrefixes[iMode] + "D0/hPosDCAToPVxy")->Fill(v0.dcaPosToPVxy); + getHist(modePrefixes[iMode] + "D0/hNegDCAToPVxy")->Fill(v0.dcaNegToPVxy); + getHist(modePrefixes[iMode] + "D0/hPosDCAToPVz")->Fill(v0.dcaPosToPVz); + getHist(modePrefixes[iMode] + "D0/hNegDCAToPVz")->Fill(v0.dcaNegToPVz); + getHist(modePrefixes[iMode] + "D0/hDCADaughters")->Fill(v0.DCADau); + getHist(modePrefixes[iMode] + "D0/hCosPA")->Fill(v0.CosPA); + getHist(modePrefixes[iMode] + "D0/hOpeningAngle")->Fill(v0.OpAngle); + getHist(modePrefixes[iMode] + "D0/hV0Radius")->Fill(v0.Radius); + getHist(modePrefixes[iMode] + "D0/hV0Z")->Fill(v0.Zdist); + getHist(modePrefixes[iMode] + "D0/hV0Rapidity")->Fill(v0.rapidityD0); + getHist(modePrefixes[iMode] + "D0/hV0LifetimeK0s")->Fill(v0.DistOverTotMom * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "D0/hV0LifetimeLambda")->Fill(v0.DistOverTotMom * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "D0/hV0LifetimeD0")->Fill(v0.DistOverTotMom * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "D0/hV0PseudoLifetimeK0s")->Fill(v0.ZdistOverPz * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "D0/hV0PseudoLifetimeLambda")->Fill(v0.ZdistOverPz * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "D0/hV0PseudoLifetimeD0")->Fill(v0.ZdistOverPz * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "D0/hV0InvMassK0s")->Fill(invMassK0Short); + getHist(modePrefixes[iMode] + "D0/hV0InvMassLambda")->Fill(invMassLambda); + getHist(modePrefixes[iMode] + "D0/hV0InvMassAntiLambda")->Fill(invMassAntiLambda); + getHist(modePrefixes[iMode] + "D0/hV0InvMassD0")->Fill(invMassD0); + getHist(modePrefixes[iMode] + "D0/hV0InvMassAntiD0")->Fill(invMassAntiD0); + if (doCompleteTopoQA) { + getHist(modePrefixes[iMode] + "D0/hV0InvMassK0sVsD0")->Fill(invMassK0Short, invMassD0); + getHist(modePrefixes[iMode] + "D0/hV0InvMassLambdaVsD0")->Fill(invMassLambda, invMassD0); + getHist(modePrefixes[iMode] + "D0/hV0InvMassAntiLambdaVsD0")->Fill(invMassAntiLambda, invMassD0); + } + + getHist(modePrefixes[iMode] + "D0/hPositiveMFTcls")->Fill(v0.posNclusters); + getHist(modePrefixes[iMode] + "D0/hNegativeMFTcls")->Fill(v0.negNclusters); + getHist(modePrefixes[iMode] + "D0/hPositiveMFTchi2PerNcls")->Fill(v0.posChi2PerNclus); + getHist(modePrefixes[iMode] + "D0/hNegativeMFTchi2PerNcls")->Fill(v0.negChi2PerNclus); + getHist(modePrefixes[iMode] + "D0/hPositiveMFTchi2")->Fill(v0.posChi2); + getHist(modePrefixes[iMode] + "D0/hNegativeMFTchi2")->Fill(v0.negChi2); + if (doprocessMonteCarlo && iMode == kDefault) { + getHist(modePrefixes[iMode] + "D0/hPositivePtResolution")->Fill(v0.positivePtMc(), (v0.positivePtMc() - v0.positivePt()) / v0.positivePtMc()); + getHist(modePrefixes[iMode] + "D0/hNegativePtResolution")->Fill(v0.negativePtMc(), (v0.negativePtMc() - v0.negativePt()) / v0.negativePtMc()); + getHist(modePrefixes[iMode] + "D0/hPositivePzResolution")->Fill(v0.positivePzMc(), (v0.positivePzMc() - v0.positivePz()) / v0.positivePzMc()); + getHist(modePrefixes[iMode] + "D0/hNegativePzResolution")->Fill(v0.negativePzMc(), (v0.negativePzMc() - v0.negativePz()) / v0.negativePzMc()); + + getHist(modePrefixes[iMode] + "D0/hV0XDiff")->Fill(v0.xMc, (v0.xMc - v0.X)); + getHist(modePrefixes[iMode] + "D0/hV0YDiff")->Fill(v0.yMc, (v0.yMc - v0.Y)); + getHist(modePrefixes[iMode] + "D0/hV0ZDiff")->Fill(v0.zMc, (v0.zMc - v0.Z)); + getHist(modePrefixes[iMode] + "D0/hV0RadiusDiff")->Fill(v0.RadiusMc, (v0.RadiusMc - v0.Radius) / v0.RadiusMc); + getHist(modePrefixes[iMode] + "D0/hV0DistZDiff")->Fill(v0.ZdistMc, (v0.ZdistMc - v0.Zdist) / v0.ZdistMc); + getHist(modePrefixes[iMode] + "D0/hV0PtResolution")->Fill(v0.pTMc, (v0.pTMc - v0.pT) / v0.pTMc); + getHist(modePrefixes[iMode] + "D0/hV0PzResolution")->Fill(v0.pZMc, (v0.pZMc - v0.pZ) / v0.pZMc); + } + } + nD0s++; + } + if (passAntiD0Selections && analyseAntiD0) { + getHist(modePrefixes[iMode] + "GeneralQA/hSelectionV0s")->Fill(selPhysPrimAntiD0 + 2); // + getHist(modePrefixes[iMode] + "GeneralQA/h2dArmenterosD0Selected")->Fill(v0.AlphaArm, v0.QtArm); // cross-check + getHist(modePrefixes[iMode] + "h3dMassAntiD0")->Fill(centrality, pt, invMassAntiD0); + if (doUPCanalysis) { + if (gapSide == 0) + getHist(modePrefixes[iMode] + "h3dMassAntiD0SGA")->Fill(centrality, pt, invMassAntiD0); + else if (gapSide == 1) + getHist(modePrefixes[iMode] + "h3dMassAntiD0SGC")->Fill(centrality, pt, invMassAntiD0); + else if (gapSide == 2) + getHist(modePrefixes[iMode] + "h3dMassAntiD0DG")->Fill(centrality, pt, invMassAntiD0); + else + getHist(modePrefixes[iMode] + "h3dMassAntiD0Hadronic")->Fill(centrality, pt, invMassAntiD0); + } + getHist(modePrefixes[iMode] + "hMassAntiD0")->Fill(invMassAntiD0); + if (doPlainTopoQA) { + getHist(modePrefixes[iMode] + "AntiD0/hPosDCAToPVxy")->Fill(v0.dcaPosToPVxy); + getHist(modePrefixes[iMode] + "AntiD0/hNegDCAToPVxy")->Fill(v0.dcaNegToPVxy); + getHist(modePrefixes[iMode] + "AntiD0/hPosDCAToPVz")->Fill(v0.dcaPosToPVz); + getHist(modePrefixes[iMode] + "AntiD0/hNegDCAToPVz")->Fill(v0.dcaNegToPVz); + getHist(modePrefixes[iMode] + "AntiD0/hDCADaughters")->Fill(v0.DCADau); + getHist(modePrefixes[iMode] + "AntiD0/hCosPA")->Fill(v0.CosPA); + getHist(modePrefixes[iMode] + "AntiD0/hOpeningAngle")->Fill(v0.OpAngle); + getHist(modePrefixes[iMode] + "AntiD0/hV0Radius")->Fill(v0.Radius); + getHist(modePrefixes[iMode] + "AntiD0/hV0Z")->Fill(v0.Zdist); + getHist(modePrefixes[iMode] + "AntiD0/hV0Rapidity")->Fill(v0.rapidityD0); + getHist(modePrefixes[iMode] + "AntiD0/hV0LifetimeK0s")->Fill(v0.DistOverTotMom * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "AntiD0/hV0LifetimeLambda")->Fill(v0.DistOverTotMom * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "AntiD0/hV0LifetimeD0")->Fill(v0.DistOverTotMom * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "AntiD0/hV0PseudoLifetimeK0s")->Fill(v0.ZdistOverPz * o2::constants::physics::MassK0Short); + getHist(modePrefixes[iMode] + "AntiD0/hV0PseudoLifetimeLambda")->Fill(v0.ZdistOverPz * o2::constants::physics::MassLambda); + getHist(modePrefixes[iMode] + "AntiD0/hV0PseudoLifetimeD0")->Fill(v0.ZdistOverPz * o2::constants::physics::MassD0); + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassK0s")->Fill(invMassK0Short); + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassLambda")->Fill(invMassLambda); + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassAntiLambda")->Fill(invMassAntiLambda); + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassD0")->Fill(invMassD0); + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassAntiD0")->Fill(invMassAntiD0); + if (doCompleteTopoQA) { + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassK0sVsAntiD0")->Fill(invMassK0Short, invMassAntiD0); + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassLambdaVsAntiD0")->Fill(invMassLambda, invMassAntiD0); + getHist(modePrefixes[iMode] + "AntiD0/hV0InvMassAntiLambdaVsAntiD0")->Fill(invMassAntiLambda, invMassAntiD0); + } + + getHist(modePrefixes[iMode] + "AntiD0/hPositiveMFTcls")->Fill(v0.posNclusters); + getHist(modePrefixes[iMode] + "AntiD0/hNegativeMFTcls")->Fill(v0.negNclusters); + getHist(modePrefixes[iMode] + "AntiD0/hPositiveMFTchi2PerNcls")->Fill(v0.posChi2PerNclus); + getHist(modePrefixes[iMode] + "AntiD0/hNegativeMFTchi2PerNcls")->Fill(v0.negChi2PerNclus); + getHist(modePrefixes[iMode] + "AntiD0/hPositiveMFTchi2")->Fill(v0.posChi2); + getHist(modePrefixes[iMode] + "AntiD0/hNegativeMFTchi2")->Fill(v0.negChi2); + if (doprocessMonteCarlo && iMode == kDefault) { + getHist(modePrefixes[iMode] + "AntiD0/hPositivePtResolution")->Fill(v0.positivePtMc(), (v0.positivePtMc() - v0.positivePt()) / v0.positivePtMc()); + getHist(modePrefixes[iMode] + "AntiD0/hNegativePtResolution")->Fill(v0.negativePtMc(), (v0.negativePtMc() - v0.negativePt()) / v0.negativePtMc()); + getHist(modePrefixes[iMode] + "AntiD0/hPositivePzResolution")->Fill(v0.positivePzMc(), (v0.positivePzMc() - v0.positivePz()) / v0.positivePzMc()); + getHist(modePrefixes[iMode] + "AntiD0/hNegativePzResolution")->Fill(v0.negativePzMc(), (v0.negativePzMc() - v0.negativePz()) / v0.negativePzMc()); + + getHist(modePrefixes[iMode] + "AntiD0/hV0XDiff")->Fill(v0.xMc, (v0.xMc - v0.X)); + getHist(modePrefixes[iMode] + "AntiD0/hV0YDiff")->Fill(v0.yMc, (v0.yMc - v0.Y)); + getHist(modePrefixes[iMode] + "AntiD0/hV0ZDiff")->Fill(v0.zMc, (v0.zMc - v0.Z)); + getHist(modePrefixes[iMode] + "AntiD0/hV0RadiusDiff")->Fill(v0.RadiusMc, (v0.RadiusMc - v0.Radius) / v0.RadiusMc); + getHist(modePrefixes[iMode] + "AntiD0/hV0DistZDiff")->Fill(v0.ZdistMc, (v0.ZdistMc - v0.Zdist) / v0.ZdistMc); + getHist(modePrefixes[iMode] + "AntiD0/hV0PtResolution")->Fill(v0.pTMc, (v0.pTMc - v0.pT) / v0.pTMc); + getHist(modePrefixes[iMode] + "AntiD0/hV0PzResolution")->Fill(v0.pZMc, (v0.pZMc - v0.pZ) / v0.pZMc); + } + } + nAntiD0s++; + } + } + + template + void fillFeeddownMatrix(TV0 v0, float pt, float centrality, uint64_t selMap, int iMode = kDefault) + // fill feeddown matrix for Lambdas or AntiLambdas + // fixme: a potential improvement would be to consider mass windows for the l/al + { + if (v0.motherLabel < 0) + return; // does not have mother particle in record, skip + + float rapidityXi = 999.f; + if (std::abs(v0.pdgCodeMother) == PDG_t::kXiMinus) + rapidityXi = RecoDecay::y(std::array{v0.momentumMotherMc[0], v0.momentumMotherMc[1], v0.momentumMotherMc[2]}, o2::constants::physics::MassXiMinus); + if (std::abs(v0.pdgCodeMother) == o2::constants::physics::Pdg::kXi0) + rapidityXi = RecoDecay::y(std::array{v0.momentumMotherMc[0], v0.momentumMotherMc[1], v0.momentumMotherMc[2]}, o2::constants::physics::MassXi0); + + if (std::fabs(rapidityXi) > 0.5f) + return; // not a valid mother rapidity (PDG selection is later) + + // __________________________________________ + if (verifyMask(selMap, secondaryMaskSelectionLambda) && analyseLambda) { + if (v0.motherIsPhysicalPrimary) { + if (v0.pdgCodeMother == PDG_t::kXiMinus) { + getHist(modePrefixes[iMode] + "h3dLambdaFeeddown")->Fill(centrality, pt, std::hypot(v0.momentumMotherMc[0], v0.momentumMotherMc[1])); + } + if (v0.pdgCodeMother == PDG_t::kXiMinus || v0.pdgCodeMother == o2::constants::physics::Pdg::kXi0) { + getHist(modePrefixes[iMode] + "h3dLambdaFeeddownFromXi0")->Fill(centrality, pt, std::hypot(v0.momentumMotherMc[0], v0.momentumMotherMc[1])); + } + } + } + if (verifyMask(selMap, secondaryMaskSelectionAntiLambda) && analyseAntiLambda) { + if (v0.motherIsPhysicalPrimary) { + if (v0.pdgCodeMother == PDG_t::kXiPlusBar) { + getHist(modePrefixes[iMode] + "h3dAntiLambdaFeeddown")->Fill(centrality, pt, std::hypot(v0.momentumMotherMc[0], v0.momentumMotherMc[1])); + } + if (v0.pdgCodeMother == PDG_t::kXiPlusBar || v0.pdgCodeMother == -o2::constants::physics::Pdg::kXi0) { + getHist(modePrefixes[iMode] + "h3dAntiLambdaFeeddownFromXi0")->Fill(centrality, pt, std::hypot(v0.momentumMotherMc[0], v0.momentumMotherMc[1])); + } + } + } + } + + template + bool isEventAccepted(TCollision const& collision, TBCs const& /*bcs*/, bool fillHists) + // check whether the collision passes our collision selections + { + if (fillHists) { + histos.fill(HIST("hEventSelection"), 0. /* all collisions */); + } + if (eventSelections.requireSel8 && !collision.sel8()) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 1 /* sel8 collisions */); + } + + if (eventSelections.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 2 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + } + + if (eventSelections.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 3 /* Not at ITS ROF border */); + } + + if (eventSelections.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 4 /* Not at TF border */); + } + + if (std::abs(collision.posZ()) > eventSelections.maxZVtxPosition) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 5 /* vertex-Z selected */); + } + + if (eventSelections.requireIsVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 6 /* Contains at least one ITS-TPC track */); + } + + if (eventSelections.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 7 /* PV position consistency check */); + } + + if (eventSelections.requireIsVertexTOFmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTOFmatched)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 8 /* PV with at least one contributor matched with TOF */); + } + + if (eventSelections.requireIsVertexTRDmatched && !collision.selection_bit(o2::aod::evsel::kIsVertexTRDmatched)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 9 /* PV with at least one contributor matched with TRD */); + } + + if (eventSelections.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 10 /* Not at same bunch pile-up */); + } + + if (eventSelections.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 11 /* No other collision within +/- 2 microseconds or mult above a certain threshold in -4 - -2 microseconds*/); + } + + if (eventSelections.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 12 /* No other collision within +/- 10 microseconds */); + } + + if (eventSelections.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 13 /* No other collision within +/- 2 microseconds */); + } + + if (eventSelections.requireNoCollInROFStd && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 14 /* No other collision within the same ITS ROF with mult. above a certain threshold */); + } + + if (eventSelections.requireNoCollInROFStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 15 /* No other collision within the same ITS ROF */); + } + + if (eventSelections.requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 16 /* veto an event if FT0C amplitude in previous ITS ROF is above threshold */); + } + + if (eventSelections.requireIsGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 17 /* numbers of inactive chips on all ITS layers are below maximum allowed values */); + } + + if (eventSelections.requireINEL0 && collision.multNTracksPVeta1() < 1) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 18 /* INEL > 0 */); + } + + if (eventSelections.requireINEL1 && collision.multNTracksPVeta1() < 2) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 19 /* INEL > 1 */); + } + + float collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + if (eventSelections.minOccupancy >= 0 && collisionOccupancy < eventSelections.minOccupancy) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 20 /* Below min occupancy */); + } + + if (eventSelections.maxOccupancy >= 0 && collisionOccupancy > eventSelections.maxOccupancy) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 21 /* Above max occupancy */); + } + + // Fetch interaction rate only if required (in order to limit ccdb calls) + auto bc = collision.template bc_as(); + double interactionRate = (eventSelections.minIR >= 0 || eventSelections.maxIR >= 0) ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3 : -1; + if (eventSelections.minIR >= 0 && interactionRate < eventSelections.minIR) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 22 /* Below min IR */); + } + + if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 23 /* Above max IR */); + } + + if (!rctConfigurations.cfgRCTLabel.value.empty() && !rctFlagsChecker(collision)) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 24 /* Pass CBT condition */); + } + return true; + } + + // ______________________________________________________ + // Simulated processing + // Return the list of indices to the recoed collision associated to a given MC collision. + template + std::vector getListOfRecoCollIndices(TMCollisions const& mcCollisions, TCollisions const& collisions, TBCs const& bcs) + { + std::vector listBestCollisionIdx(mcCollisions.size()); + for (auto const& mcCollision : mcCollisions) { + auto groupedCollisions = getGroupedCollisions(collisions, mcCollision.globalIndex()); + int biggestNContribs = -1; + int bestCollisionIndex = -1; + for (auto const& collision : groupedCollisions) { + // consider event selections in the recoed <-> gen collision association, for the denominator (or numerator) of the efficiency (or signal loss)? + if (eventSelections.useEvtSelInDenomEff) { + if (!isEventAccepted(collision, bcs, false)) { + continue; + } + } + + // Find the collision with the biggest nbr of PV contributors + // Follows what was done here: https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/mcCollsExtra.cxx#L93 + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + bestCollisionIndex = collision.globalIndex(); + } + } + listBestCollisionIdx[mcCollision.globalIndex()] = bestCollisionIndex; + } + return listBestCollisionIdx; + } + + // ______________________________________________________ + // Reconstructed data processing + // Fill reconstructed event information + // Return centrality, occupancy, interaction rate, gap side and selGapside via reference-passing in arguments + template + void fillReconstructedEventProperties(TCollision const& collision, TBCs const& /*bcs*/, float& centrality, float& collisionOccupancy, double& interactionRate, int& gapSide, int& selGapSide, bool fillHists) + { + centrality = getCentralityRun3(collision); + collisionOccupancy = eventSelections.useFT0CbasedOccupancy ? collision.ft0cOccupancyInTimeRange() : collision.trackOccupancyInTimeRange(); + // Fetch interaction rate only if required (in order to limit ccdb calls) + auto bc = collision.template bc_as(); + interactionRate = !irSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), irSource) * 1.e-3 : -1; + + if (doUPCanalysis) { + // gap side + // gapSide = collision.gapSide(); + gapSide = -1; + // -1 --> Hadronic + // 0 --> Single Gap - A side + // 1 --> Single Gap - C side + // 2 --> Double Gap - both A & C sides + // selGapSide = sgSelector.trueGap(collision, upcCuts.fv0Cut, upcCuts.ft0Acut, upcCuts.ft0Ccut, upcCuts.zdcCut); + + if (fillHists) { + histos.fill(HIST("hGapSide"), gapSide); + histos.fill(HIST("hSelGapSide"), selGapSide); + histos.fill(HIST("hEventCentralityVsSelGapSide"), centrality, selGapSide <= 2 ? selGapSide : -1); + } + } + + if (fillHists) { + histos.fill(HIST("hEventCentrality"), centrality); + + histos.fill(HIST("hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); + if (doEventQA) { + histos.fill(HIST("hCentralityVsNGlobal"), centrality, collision.multNTracksGlobal()); + histos.fill(HIST("hEventCentVsMultFT0M"), collision.centFT0M(), collision.multFT0A() + collision.multFT0C()); + histos.fill(HIST("hEventCentVsMultFT0C"), collision.centFT0C(), collision.multFT0C()); + histos.fill(HIST("hEventCentVsMultNGlobal"), collision.centNGlobal(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventCentVsMultFV0A"), collision.centFV0A(), collision.multFV0A()); + histos.fill(HIST("hEventMultFT0MvsMultNGlobal"), collision.multFT0A() + collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFT0CvsMultNGlobal"), collision.multFT0C(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFV0AvsMultNGlobal"), collision.multFV0A(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultPVvsMultNGlobal"), collision.multNTracksPVeta1(), collision.multNTracksGlobal()); + histos.fill(HIST("hEventMultFT0CvsMultFV0A"), collision.multFT0C(), collision.multFV0A()); + } + + histos.fill(HIST("hCentralityVsPVz"), centrality, collision.posZ()); + histos.fill(HIST("hEventPVz"), collision.posZ()); + + histos.fill(HIST("hEventOccupancy"), collisionOccupancy); + histos.fill(HIST("hCentralityVsOccupancy"), centrality, collisionOccupancy); + + histos.fill(HIST("hInteractionRate"), interactionRate); + histos.fill(HIST("hCentralityVsInteractionRate"), centrality, interactionRate); + + histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + } + } + + // ______________________________________________________ + // Simulated processing + // Fill generated event information (for event loss/splitting estimation) + template + void fillGeneratedEventProperties(TMCCollisions const& mcCollisions, TCollisions const& collisions, TBCs const& bcs) + { + std::vector listBestCollisionIdx(mcCollisions.size()); + for (auto const& mcCollision : mcCollisions) { + // Apply selections on MC collisions + if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { + continue; + } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + continue; + } + + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + continue; + } + + histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 0 /* all gen. events*/); + + auto groupedCollisions = getGroupedCollisions(collisions, mcCollision.globalIndex()); + // Check if there is at least one of the reconstructed collisions associated to this MC collision + // If so, we consider it + bool atLeastOne = false; + int biggestNContribs = -1; + float centrality = 100.5f; + int nCollisions = 0; + for (auto const& collision : groupedCollisions) { + + if (!isEventAccepted(collision, bcs, false)) { + continue; + } + + if (biggestNContribs < collision.multPVTotalContributors()) { + biggestNContribs = collision.multPVTotalContributors(); + centrality = getCentralityRun3(collision); + } + nCollisions++; + + atLeastOne = true; + } + + histos.fill(HIST("hCentralityVsNcoll_beforeEvSel"), centrality, groupedCollisions.size()); + histos.fill(HIST("hCentralityVsNcoll_afterEvSel"), centrality, nCollisions); + + histos.fill(HIST("hCentralityVsMultMC"), centrality, mcCollision.multMCNParticlesEta05()); + histos.fill(HIST("hCentralityVsPVzMC"), centrality, mcCollision.posZ()); + histos.fill(HIST("hEventPVzMC"), mcCollision.posZ()); + + if (atLeastOne) { + histos.fill(HIST("hGenEvents"), mcCollision.multMCNParticlesEta05(), 1 /* at least 1 rec. event*/); + + histos.fill(HIST("hGenEventCentrality"), centrality); + } + } + } + + // BUILDER PART: largely inspired of the strangenessBuilderModule + // FIXME: to be put in a dedicated table producer if it works + //__________________________________________________ + // MC kink handling + template + int getOriginatingParticle(mcpart const& part, int& indexForPositionOfDecay, bool treatPiToMuDecays) + { + int returnValue = -1; + if (part.has_mothers()) { + auto const& motherList = part.template mothers_as(); + if (motherList.size() == 1) { + for (const auto& mother : motherList) { + if (std::abs(part.pdgCode()) == PDG_t::kMuonMinus && treatPiToMuDecays) { + // muon decay, de-ref mother twice + if (mother.has_mothers()) { + auto grandMotherList = mother.template mothers_as(); + if (grandMotherList.size() == 1) { + for (const auto& grandMother : grandMotherList) { + returnValue = grandMother.globalIndex(); + indexForPositionOfDecay = mother.globalIndex(); // for V0 decay position: grab muon + } + } + } + } else { + returnValue = mother.globalIndex(); + indexForPositionOfDecay = part.globalIndex(); + } + } + } + } + return returnValue; + } + + //___________________________________________________________________ + // Taken from https://github.com/AliceO2Group/AliceO2/blob/be5a2bb6c1be6757b7a496f9e90d470304d98fe7/Common/DCAFitter/include/DCAFitter/FwdDCAFitterN.h#L1281 + // Re-adapted for this task + template + void propagateToVtx(o2::track::TrackParCovFwd& t, TCollision const& collision, std::array& dca) const + { + dca[0] = o2::track::DefaultDCA; + dca[1] = o2::track::DefaultDCA; + dca[2] = o2::track::DefaultDCA; + + // propagate track to vertex including MCS effects if material budget included, simple propagation to Z otherwise + float x2x0 = 0; + auto mb = lut->getMatBudget(t.getX(), t.getY(), t.getZ(), collision.posX(), collision.posY(), collision.posZ()); + x2x0 = static_cast(mb.meanX2X0); + t.propagateToVtxhelixWithMCS(collision.posZ(), {collision.posX(), collision.posY()}, std::array{collision.covXX(), collision.covYY()}, magField, x2x0); + + dca[0] = t.getX() - collision.posX(); + dca[1] = t.getY() - collision.posY(); + dca[2] = t.getZ() - collision.posZ(); + } + + //___________________________________________________________________ + // Taken from https://github.com/AliceO2Group/O2Physics/blob/master/Common/Core/fwdtrackUtilities.h#L183 + // Re-adapted for this task + template + void propagateToVtxNew(o2::track::TrackParCovFwd& t, TCollision const& collision, std::array& dca) const + { + dca[0] = o2::track::DefaultDCA; + dca[1] = o2::track::DefaultDCA; + dca[2] = o2::track::DefaultDCA; + t.propagateToDCAhelix(magField, {collision.posX(), collision.posY(), collision.posZ()}, dca); + } + + template + std::vector buildV0s(TCollision const& collision, TMFTTracks const& /*mftTracks*/, TTracks const& besttracks1, TTracks const& besttracks2, TMCCollisions const& /*mccollisions*/, TMCParticles const& mcParticles) + { + std::vector v0; + for (const auto& [amft1, amft2] : combinations(besttracks1, besttracks2)) { + auto mftPositive = amft1.template mfttrack_as(); + auto mftNegative = amft2.template mfttrack_as(); + + if (mftPositive.eta() < v0Selections.daughterEtaCutMin) + continue; // remove acceptance that's badly reproduced by MC / superfluous in future + if (mftPositive.eta() > v0Selections.daughterEtaCutMax) + continue; // remove acceptance that's badly reproduced by MC / superfluous in future + if (mftNegative.eta() < v0Selections.daughterEtaCutMin) + continue; // remove acceptance that's badly reproduced by MC / superfluous in future + if (mftNegative.eta() > v0Selections.daughterEtaCutMax) + continue; // remove acceptance that's badly reproduced by MC / superfluous in future + + if (mftPositive.pt() < v0Selections.minTrackPt) + continue; + if (mftNegative.pt() < v0Selections.minTrackPt) + continue; + if (mftPositive.pt() > v0Selections.maxTrackPt) + continue; + if (mftNegative.pt() > v0Selections.maxTrackPt) + continue; + + if (mftPositive.pz() < v0Selections.minTrackPz) + continue; + if (mftNegative.pz() < v0Selections.minTrackPz) + continue; + if (mftPositive.pz() > v0Selections.maxTrackPz) + continue; + if (mftNegative.pz() > v0Selections.maxTrackPz) + continue; + + // Consider only tracks of opposite charges + if (mftPositive.sign() < 0) + continue; + if (mftNegative.sign() > 0) + continue; + + SMatrix5 tpars1(mftPositive.x(), mftPositive.y(), mftPositive.phi(), mftPositive.tgl(), mftPositive.signed1Pt()); + std::vector v1; + SMatrix55 tcovs1(v1.begin(), v1.end()); + o2::track::TrackParCovFwd pars1{mftPositive.z(), tpars1, tcovs1, mftPositive.chi2()}; + o2::track::TrackParCovFwd pars1Copy{mftPositive.z(), tpars1, tcovs1, mftPositive.chi2()}; + + SMatrix5 tpars2(mftNegative.x(), mftNegative.y(), mftNegative.phi(), mftNegative.tgl(), mftNegative.signed1Pt()); + std::vector v2; + SMatrix55 tcovs2(v2.begin(), v2.end()); + o2::track::TrackParCovFwd pars2{mftNegative.z(), tpars2, tcovs2, mftNegative.chi2()}; + o2::track::TrackParCovFwd pars2Copy{mftNegative.z(), tpars2, tcovs2, mftNegative.chi2()}; + + std::array dcaPosToPV{o2::track::DefaultDCA, o2::track::DefaultDCA, o2::track::DefaultDCA}; + std::array dcaNegToPV{o2::track::DefaultDCA, o2::track::DefaultDCA, o2::track::DefaultDCA}; + if (useNewPropagationToVtx) { + propagateToVtxNew(pars1Copy, collision, dcaPosToPV); + propagateToVtxNew(pars2Copy, collision, dcaNegToPV); + } else { + propagateToVtx(pars1Copy, collision, dcaPosToPV); + propagateToVtx(pars2Copy, collision, dcaNegToPV); + } + + // Move close to minima + int nCand = 0; + try { + nCand = fitter.process(pars1, pars2); + } catch (...) { + continue; + } + if (nCand == 0) { + continue; + } + + fitter.FwdpropagateTracksToVertex(); // propagate e and K to D vertex + if (!fitter.isPropagateTracksToVertexDone()) { + continue; + } + + auto lTrack1 = fitter.getTrack(0); + auto lTrack2 = fitter.getTrack(1); + + PairTopoInfo pairInfo; + // DCA between cascade daughters + pairInfo.DCADau = std::sqrt(fitter.getChi2AtPCACandidate()); + + // get decay vertex coordinates + Vec3D vtx = fitter.getPCACandidate(); + pairInfo.X = vtx[0]; + pairInfo.Y = vtx[1]; + pairInfo.Z = vtx[2]; + + // get daughter DCA to PV + pairInfo.dcaPosToPVxy = std::hypot(dcaPosToPV[0], dcaPosToPV[1]); + pairInfo.dcaNegToPVxy = std::hypot(dcaNegToPV[0], dcaNegToPV[1]); + pairInfo.dcaPosToPVz = dcaPosToPV[2]; + pairInfo.dcaNegToPVz = dcaNegToPV[2]; + + // get daughter momenta + pairInfo.positiveMomentum[0] = lTrack1.getPx(); + pairInfo.positiveMomentum[1] = lTrack1.getPy(); + pairInfo.positiveMomentum[2] = lTrack1.getPz(); + pairInfo.negativeMomentum[0] = lTrack2.getPx(); + pairInfo.negativeMomentum[1] = lTrack2.getPy(); + pairInfo.negativeMomentum[2] = lTrack2.getPz(); + + pairInfo.CosPA = RecoDecay::cpa( + std::array{collision.posX(), collision.posY(), collision.posZ()}, + std::array{vtx[0], vtx[1], vtx[2]}, + std::array{pairInfo.positiveMomentum[0] + pairInfo.negativeMomentum[0], + pairInfo.positiveMomentum[1] + pairInfo.negativeMomentum[1], + pairInfo.positiveMomentum[2] + pairInfo.negativeMomentum[2]}); + + // Momenta + TVector3 track1Momentum(pairInfo.positiveMomentum[0], pairInfo.positiveMomentum[1], pairInfo.positiveMomentum[2]); + TVector3 track2Momentum(pairInfo.negativeMomentum[0], pairInfo.negativeMomentum[1], pairInfo.negativeMomentum[2]); + + pairInfo.OpAngle = track1Momentum.Angle(track2Momentum); + + // Radius + pairInfo.Radius = std::hypot(pairInfo.X - collision.posX(), pairInfo.Y - collision.posY()); + pairInfo.Zdist = (pairInfo.Z - collision.posZ()); + + // Dist over tot mom. + float px = pairInfo.positiveMomentum[0] + pairInfo.negativeMomentum[0]; + float py = pairInfo.positiveMomentum[1] + pairInfo.negativeMomentum[1]; + float pz = pairInfo.positiveMomentum[2] + pairInfo.negativeMomentum[2]; + pairInfo.DistOverTotMom = std::hypot(pairInfo.X - collision.posX(), pairInfo.Y - collision.posY(), pairInfo.Z - collision.posZ()) / std::hypot(px, py, pz); + + // Z dist over pz + pairInfo.ZdistOverPz = (pairInfo.Z - collision.posZ()) / pz; + + // V0 Momenta + pairInfo.pT = std::hypot(px, py); + pairInfo.pTot = std::hypot(px, py, pz); + pairInfo.pZ = pz; + + // Armenteros-Podolanski variables + float momTot2 = RecoDecay::p2(px, py, pz); + float dp = RecoDecay::dotProd(std::array{pairInfo.negativeMomentum[0], pairInfo.negativeMomentum[1], pairInfo.negativeMomentum[2]}, std::array{px, py, pz}); + pairInfo.QtArm = std::sqrt(RecoDecay::p2(pairInfo.negativeMomentum[0], pairInfo.negativeMomentum[1], pairInfo.negativeMomentum[2]) - dp * dp / momTot2); // qtarm + + float momTot = RecoDecay::p(px, py, pz); + float lQlNeg = RecoDecay::dotProd(std::array{pairInfo.negativeMomentum[0], pairInfo.negativeMomentum[1], pairInfo.negativeMomentum[2]}, std::array{px, py, pz}) / momTot; + float lQlPos = RecoDecay::dotProd(std::array{pairInfo.positiveMomentum[0], pairInfo.positiveMomentum[1], pairInfo.positiveMomentum[2]}, std::array{px, py, pz}) / momTot; + pairInfo.AlphaArm = (lQlPos - lQlNeg) / (lQlPos + lQlNeg); // alphav0 + + // Inv. mass + pairInfo.mK0s = RecoDecay::m(std::array{pairInfo.positiveMomentum, pairInfo.negativeMomentum}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassPiMinus}); + pairInfo.mLambda = RecoDecay::m(std::array{pairInfo.positiveMomentum, pairInfo.negativeMomentum}, std::array{o2::constants::physics::MassProton, o2::constants::physics::MassPiMinus}); + pairInfo.mAntiLambda = RecoDecay::m(std::array{pairInfo.positiveMomentum, pairInfo.negativeMomentum}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassProtonBar}); + pairInfo.mD0 = RecoDecay::m(std::array{pairInfo.positiveMomentum, pairInfo.negativeMomentum}, std::array{o2::constants::physics::MassKPlus, o2::constants::physics::MassPiMinus}); + pairInfo.mAntiD0 = RecoDecay::m(std::array{pairInfo.positiveMomentum, pairInfo.negativeMomentum}, std::array{o2::constants::physics::MassPiPlus, o2::constants::physics::MassKMinus}); + + // Rapidity + pairInfo.rapidityK0s = RecoDecay::y(std::array{pairInfo.positiveMomentum[0] + pairInfo.negativeMomentum[0], pairInfo.positiveMomentum[1] + pairInfo.negativeMomentum[1], pairInfo.positiveMomentum[2] + pairInfo.negativeMomentum[2]}, o2::constants::physics::MassKaonNeutral); + pairInfo.rapidityLambda = RecoDecay::y(std::array{pairInfo.positiveMomentum[0] + pairInfo.negativeMomentum[0], pairInfo.positiveMomentum[1] + pairInfo.negativeMomentum[1], pairInfo.positiveMomentum[2] + pairInfo.negativeMomentum[2]}, o2::constants::physics::MassLambda); + pairInfo.rapidityD0 = RecoDecay::y(std::array{pairInfo.positiveMomentum[0] + pairInfo.negativeMomentum[0], pairInfo.positiveMomentum[1] + pairInfo.negativeMomentum[1], pairInfo.positiveMomentum[2] + pairInfo.negativeMomentum[2]}, o2::constants::physics::MassD0); + + // + pairInfo.posNclusters = mftPositive.nClusters(); + pairInfo.posChi2 = mftPositive.chi2(); + pairInfo.posChi2PerNclus = mftPositive.chi2() / mftPositive.nClusters(); + + pairInfo.negNclusters = mftNegative.nClusters(); + pairInfo.negChi2 = mftNegative.chi2(); + pairInfo.negChi2PerNclus = mftNegative.chi2() / mftNegative.nClusters(); + + //_________________________________________________________ + // MC handling part + if constexpr (requires { mftNegative.mcParticleId(); mftPositive.mcParticleId(); }) { + // Association check + // There might be smarter ways of doing this in the future + if (mftNegative.has_mcParticle() && mftPositive.has_mcParticle()) { + auto lMCNegTrack = mftNegative.template mcParticle_as(); + auto lMCPosTrack = mftPositive.template mcParticle_as(); + + pairInfo.pdgCodePositive = lMCPosTrack.pdgCode(); + pairInfo.pdgCodeNegative = lMCNegTrack.pdgCode(); + pairInfo.processPositive = lMCPosTrack.getProcess(); + pairInfo.processNegative = lMCNegTrack.getProcess(); + pairInfo.positiveMomentumMc[0] = lMCPosTrack.px(); + pairInfo.positiveMomentumMc[1] = lMCPosTrack.py(); + pairInfo.positiveMomentumMc[2] = lMCPosTrack.pz(); + pairInfo.negativeMomentumMc[0] = lMCNegTrack.px(); + pairInfo.negativeMomentumMc[1] = lMCNegTrack.py(); + pairInfo.negativeMomentumMc[2] = lMCNegTrack.pz(); + + // check for pi -> mu + antineutrino decay + // if present, de-reference original V0 correctly and provide label to original object + // NOTA BENE: the prong info will still correspond to a muon, treat carefully! + int negOriginating = -1, posOriginating = -1, particleForDecayPositionIdx = -1; + negOriginating = getOriginatingParticle(lMCNegTrack, particleForDecayPositionIdx, doTreatPiToMuon); + posOriginating = getOriginatingParticle(lMCPosTrack, particleForDecayPositionIdx, doTreatPiToMuon); + + if (negOriginating > -1 && negOriginating == posOriginating) { + auto originatingV0 = mcParticles.rawIteratorAt(negOriginating); + auto particleForDecayPosition = mcParticles.rawIteratorAt(particleForDecayPositionIdx); + + pairInfo.label = originatingV0.globalIndex(); + pairInfo.xMc = particleForDecayPosition.vx(); + pairInfo.yMc = particleForDecayPosition.vy(); + pairInfo.zMc = particleForDecayPosition.vz(); + + if (originatingV0.has_mcCollision()) { + pairInfo.mcCollision = originatingV0.mcCollisionId(); // save this reference, please + + auto mcCollision = originatingV0.template mcCollision_as(); + // Radius + pairInfo.RadiusMc = std::hypot(pairInfo.xMc - mcCollision.posX(), pairInfo.yMc - mcCollision.posY()); + pairInfo.ZdistMc = (pairInfo.zMc - mcCollision.posZ()); + } + + // acquire information + pairInfo.pdgCode = originatingV0.pdgCode(); + pairInfo.isPhysicalPrimary = originatingV0.isPhysicalPrimary(); + pairInfo.momentumMc[0] = originatingV0.px(); + pairInfo.momentumMc[1] = originatingV0.py(); + pairInfo.momentumMc[2] = originatingV0.pz(); + pairInfo.pTotMc = std::hypot(pairInfo.momentumMc[0], pairInfo.momentumMc[1], pairInfo.momentumMc[2]); + pairInfo.pTMc = std::hypot(pairInfo.momentumMc[0], pairInfo.momentumMc[1]); + pairInfo.pZMc = pairInfo.momentumMc[2]; + + if (pairInfo.pdgCode == PDG_t::kK0Short) + pairInfo.rapMc = RecoDecay::y(std::array{pairInfo.momentumMc[0], pairInfo.momentumMc[1], pairInfo.momentumMc[2]}, o2::constants::physics::MassKaonNeutral); + else if (std::abs(pairInfo.pdgCode) == PDG_t::kLambda0) + pairInfo.rapMc = RecoDecay::y(std::array{pairInfo.momentumMc[0], pairInfo.momentumMc[1], pairInfo.momentumMc[2]}, o2::constants::physics::MassLambda); + else if (std::abs(pairInfo.pdgCode) == o2::constants::physics::kD0) + pairInfo.rapMc = RecoDecay::y(std::array{pairInfo.momentumMc[0], pairInfo.momentumMc[1], pairInfo.momentumMc[2]}, o2::constants::physics::MassD0); + + if (originatingV0.has_mothers()) { + for (const auto& lV0Mother : originatingV0.template mothers_as()) { + pairInfo.pdgCodeMother = lV0Mother.pdgCode(); + pairInfo.motherLabel = lV0Mother.globalIndex(); + pairInfo.momentumMotherMc[0] = lV0Mother.px(); + pairInfo.momentumMotherMc[1] = lV0Mother.py(); + pairInfo.momentumMotherMc[2] = lV0Mother.pz(); + pairInfo.motherIsPhysicalPrimary = lV0Mother.isPhysicalPrimary(); + } + } + } + } // end association check + } // end MC handling + + v0.push_back(pairInfo); + } // end v0 loop + return v0; + } + + // ______________________________________________________ + // Real data processing - no MC subscription + template + void analyzeRecoedV0sInRealData(TCollisions const& collisions, TMFTTracks const& mftTracks, TTracks const& besttracks, TBCs const& bcs) + { + for (auto const& collision : collisions) { + // Fire up CCDB + initCCDB(bcs, collision); + + if (!isEventAccepted(collision, bcs, true)) { + continue; + } + + float centrality = -1; + float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated + double interactionRate = -1; + // gap side + int gapSide = -1; + int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides + // Fill recoed event properties + fillReconstructedEventProperties(collision, bcs, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide, true); + + histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + + // __________________________________________ + // perform main analysis + int nK0Shorts = 0; + int nLambdas = 0; + int nAntiLambdas = 0; + int nD0s = 0; + int nAntiD0s = 0; + + auto thisBestTracks = besttracks.sliceBy(perColReassociated3dTracks, collision.globalIndex()); + + std::vector V0s = buildV0s(collision, mftTracks, thisBestTracks, thisBestTracks, static_cast(nullptr), static_cast(nullptr)); + for (const auto& v0 : V0s) { + uint64_t selMap = computeReconstructionBitmap(v0, v0.rapidityK0s, v0.rapidityLambda, v0.rapidityD0); + + // consider for histograms for all species + BITSET(selMap, selConsiderK0Short); + BITSET(selMap, selConsiderLambda); + BITSET(selMap, selConsiderAntiLambda); + BITSET(selMap, selConsiderD0); + BITSET(selMap, selConsiderAntiD0); + + BITSET(selMap, selPhysPrimK0Short); + BITSET(selMap, selPhysPrimLambda); + BITSET(selMap, selPhysPrimAntiLambda); + BITSET(selMap, selPhysPrimD0); + BITSET(selMap, selPhysPrimAntiD0); + + analyseCandidate(v0, v0.pT, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas, nD0s, nAntiD0s, kDefault); + } // end v0 loop + + // fill the histograms with the number of reconstructed K0s/Lambda/antiLambda per collision + if (analyseK0Short) { + getHist(modePrefixes[kDefault] + "h2dNbrOfK0ShortVsCentrality")->Fill(centrality, nK0Shorts); + } + if (analyseLambda) { + getHist(modePrefixes[kDefault] + "h2dNbrOfLambdaVsCentrality")->Fill(centrality, nLambdas); + } + if (analyseAntiLambda) { + getHist(modePrefixes[kDefault] + "h2dNbrOfAntiLambdaVsCentrality")->Fill(centrality, nAntiLambdas); + } + if (analyseD0) { + getHist(modePrefixes[kDefault] + "h2dNbrOfD0VsCentrality")->Fill(centrality, nD0s); + } + if (analyseAntiD0) { + getHist(modePrefixes[kDefault] + "h2dNbrOfAntiD0VsCentrality")->Fill(centrality, nAntiD0s); + } + } + + if (eventMixingConfigurations.doEventMixing) { + for (const auto& [coll1, coll2] : selfCombinations(bkgColBinning, eventMixingConfigurations.nEventMixing, -1, + collisions, collisions)) { + if (coll1.globalIndex() == coll2.globalIndex()) + continue; + + // Fire up CCDB + initCCDB(bcs, coll1); + + if (!isEventAccepted(coll1, bcs, false) || !isEventAccepted(coll2, bcs, false)) { + continue; + } + + getHist("hDeltaCollision")->Fill(coll1.globalIndex() - coll2.globalIndex()); + getHist("h2dCentralityCollPair")->Fill(getCentralityRun3(coll1), getCentralityRun3(coll2)); + getHist("h2dPVzCollPair")->Fill(coll1.posZ(), coll2.posZ()); + + if (std::abs(static_cast(coll1.globalIndex()) - static_cast(coll2.globalIndex())) < eventMixingConfigurations.deltaCollision) + continue; + + float centrality = -1; + float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated + double interactionRate = -1; + // gap side + int gapSide = -1; + int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides + // Fill recoed event properties + fillReconstructedEventProperties(coll1, bcs, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide, false); + + int nK0Shorts = 0; + int nLambdas = 0; + int nAntiLambdas = 0; + int nD0s = 0; + int nAntiD0s = 0; + + auto thisBestTracks1 = besttracks.sliceBy(perColReassociated3dTracks, coll1.globalIndex()); + auto thisBestTracks2 = besttracks.sliceBy(perColReassociated3dTracks, coll2.globalIndex()); + + std::vector V0s = buildV0s(coll1, mftTracks, thisBestTracks1, thisBestTracks2, static_cast(nullptr), static_cast(nullptr)); + for (const auto& v0 : V0s) { + uint64_t selMap = computeReconstructionBitmap(v0, v0.rapidityK0s, v0.rapidityLambda, v0.rapidityD0); + + // consider for histograms for all species + BITSET(selMap, selConsiderK0Short); + BITSET(selMap, selConsiderLambda); + BITSET(selMap, selConsiderAntiLambda); + BITSET(selMap, selConsiderD0); + BITSET(selMap, selConsiderAntiD0); + + BITSET(selMap, selPhysPrimK0Short); + BITSET(selMap, selPhysPrimLambda); + BITSET(selMap, selPhysPrimAntiLambda); + BITSET(selMap, selPhysPrimD0); + BITSET(selMap, selPhysPrimAntiD0); + + analyseCandidate(v0, v0.pT, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas, nD0s, nAntiD0s, kEvtMixing); + } // end v0 loop + } + } + } + + // ______________________________________________________ + // Simulated processing (subscribes to MC information too) + template + void analyzeRecoedV0sInMonteCarlo(TCollisions const& collisions, TMCCollisions const& mcCollisions, TMFTTracks const& mftTracks, TTracks const& besttracks, TBCs const& bcs, TMCParticles const& mcParticles) + { + for (auto const& collision : collisions) { + // Fire up CCDB + initCCDB(bcs, collision); + + if (!isEventAccepted(collision, bcs, true)) { + continue; + } + + float centrality = -1; + float collisionOccupancy = -2; // -1 already taken for the case where occupancy cannot be evaluated + double interactionRate = -1; + // gap side + int gapSide = -1; + int selGapSide = -1; // -1 --> Hadronic ; 0 --> Single Gap - A side ; 1 --> Single Gap - C side ; 2 --> Double Gap - both A & C sides + // Fill recoed event properties + fillReconstructedEventProperties(collision, bcs, centrality, collisionOccupancy, interactionRate, gapSide, selGapSide, true); + + if (collision.has_mcCollision()) { + auto mcCollision = collision.template mcCollision_as(); + histos.fill(HIST("hEventPVxDiff"), mcCollision.posX(), (mcCollision.posX() - collision.posX())); + histos.fill(HIST("hEventPVyDiff"), mcCollision.posY(), (mcCollision.posY() - collision.posY())); + histos.fill(HIST("hEventPVzDiff"), mcCollision.posZ(), (mcCollision.posZ() - collision.posZ())); + } + + histos.fill(HIST("hInteractionRateVsOccupancy"), interactionRate, collisionOccupancy); + + // __________________________________________ + // perform main analysis + int nK0Shorts = 0; + int nLambdas = 0; + int nAntiLambdas = 0; + int nD0s = 0; + int nAntiD0s = 0; + + auto thisBestTracks = besttracks.sliceBy(perColReassociated3dTracksMC, collision.globalIndex()); + + std::vector V0s = buildV0s(collision, mftTracks, thisBestTracks, thisBestTracks, mcCollisions, mcParticles); + for (const auto& v0 : V0s) { + if (v0.label < 0) { + continue; + } + + uint64_t selMap = computeReconstructionBitmap(v0, v0.rapMc, v0.rapMc, v0.rapMc); + selMap = selMap | computeMCAssociation(v0); + + // feeddown matrix always with association + if (calculateFeeddownMatrix) + fillFeeddownMatrix(v0, v0.pTMc, centrality, selMap, kDefault); + + // consider only associated candidates if asked to do so, disregard association + if (!doMCAssociation) { + BITSET(selMap, selConsiderK0Short); + BITSET(selMap, selConsiderLambda); + BITSET(selMap, selConsiderAntiLambda); + BITSET(selMap, selConsiderD0); + BITSET(selMap, selConsiderAntiD0); + + BITSET(selMap, selPhysPrimK0Short); + BITSET(selMap, selPhysPrimLambda); + BITSET(selMap, selPhysPrimAntiLambda); + BITSET(selMap, selPhysPrimD0); + BITSET(selMap, selPhysPrimAntiD0); + } + + analyseCandidate(v0, v0.pTMc, centrality, selMap, selGapSide, nK0Shorts, nLambdas, nAntiLambdas, nD0s, nAntiD0s, kDefault); + } // end v0 loop + + // fill the histograms with the number of reconstructed K0s/Lambda/antiLambda per collision + if (analyseK0Short) { + getHist(modePrefixes[kDefault] + "h2dNbrOfK0ShortVsCentrality")->Fill(centrality, nK0Shorts); + } + if (analyseLambda) { + getHist(modePrefixes[kDefault] + "h2dNbrOfLambdaVsCentrality")->Fill(centrality, nLambdas); + } + if (analyseAntiLambda) { + getHist(modePrefixes[kDefault] + "h2dNbrOfAntiLambdaVsCentrality")->Fill(centrality, nAntiLambdas); + } + if (analyseD0) { + getHist(modePrefixes[kDefault] + "h2dNbrOfD0VsCentrality")->Fill(centrality, nD0s); + } + if (analyseAntiD0) { + getHist(modePrefixes[kDefault] + "h2dNbrOfAntiD0VsCentrality")->Fill(centrality, nAntiD0s); + } + } + } + + // ______________________________________________________ + // Simulated processing (subscribes to MC information too) + template + void analyzeGeneratedV0s(TMCCollisions const& mcCollisions, TMcParticles const& mcParticles, TCollisions const& collisions, TBCs const& bcs) + { + fillGeneratedEventProperties(mcCollisions, collisions, bcs); + std::vector listBestCollisionIdx = getListOfRecoCollIndices(mcCollisions, collisions, bcs); + for (auto const& mcParticle : mcParticles) { + if (!mcParticle.has_mcCollision()) + continue; + + auto mcCollision = mcParticle.template mcCollision_as>(); + if (eventSelections.applyZVtxSelOnMCPV && std::abs(mcCollision.posZ()) > eventSelections.maxZVtxPosition) { + continue; + } + if (eventSelections.requireINEL0 && mcCollision.multMCNParticlesEta10() < 1) { + continue; + } + + if (eventSelections.requireINEL1 && mcCollision.multMCNParticlesEta10() < 2) { + continue; + } + + if (!mcParticle.isPhysicalPrimary()) + continue; + + float ptmc = mcParticle.pt(); + float ymc = 1e3; + if (mcParticle.pdgCode() == PDG_t::kK0Short) + ymc = RecoDecay::y(std::array{mcParticle.px(), mcParticle.py(), mcParticle.pz()}, o2::constants::physics::MassKaonNeutral); + else if (std::abs(mcParticle.pdgCode()) == PDG_t::kLambda0) + ymc = RecoDecay::y(std::array{mcParticle.px(), mcParticle.py(), mcParticle.pz()}, o2::constants::physics::MassLambda); + else if (std::abs(mcParticle.pdgCode()) == o2::constants::physics::kD0) + ymc = RecoDecay::y(std::array{mcParticle.px(), mcParticle.py(), mcParticle.pz()}, o2::constants::physics::MassD0); + else if (std::abs(mcParticle.pdgCode()) == PDG_t::kXiMinus) + ymc = RecoDecay::y(std::array{mcParticle.px(), mcParticle.py(), mcParticle.pz()}, o2::constants::physics::MassXiMinus); + else if (std::abs(mcParticle.pdgCode()) == PDG_t::kOmegaMinus) + ymc = RecoDecay::y(std::array{mcParticle.px(), mcParticle.py(), mcParticle.pz()}, o2::constants::physics::MassOmegaMinus); + + if (ymc > v0Selections.rapidityMax) + continue; + if (ymc < v0Selections.rapidityMin) + continue; + + float centrality = 100.5f; + if (listBestCollisionIdx[mcCollision.globalIndex()] > -1) { + auto collision = collisions.iteratorAt(listBestCollisionIdx[mcCollision.globalIndex()]); + centrality = getCentralityRun3(collision); + + if (mcParticle.pdgCode() == PDG_t::kK0Short) { + histos.fill(HIST("h2dGenK0ShortVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kLambda0) { + histos.fill(HIST("h2dGenLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kLambda0Bar) { + histos.fill(HIST("h2dGenAntiLambdaVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kXiMinus) { + histos.fill(HIST("h2dGenXiMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kXiPlusBar) { + histos.fill(HIST("h2dGenXiPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kOmegaMinus) { + histos.fill(HIST("h2dGenOmegaMinusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kOmegaPlusBar) { + histos.fill(HIST("h2dGenOmegaPlusVsMultMC_RecoedEvt"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } + + if (mcParticle.pdgCode() == PDG_t::kK0Short) { + histos.fill(HIST("h2dGenK0Short"), centrality, ptmc); + histos.fill(HIST("h2dGenK0ShortVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kLambda0) { + histos.fill(HIST("h2dGenLambda"), centrality, ptmc); + histos.fill(HIST("h2dGenLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kLambda0Bar) { + histos.fill(HIST("h2dGenAntiLambda"), centrality, ptmc); + histos.fill(HIST("h2dGenAntiLambdaVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kXiMinus) { + histos.fill(HIST("h2dGenXiMinus"), centrality, ptmc); + histos.fill(HIST("h2dGenXiMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kXiPlusBar) { + histos.fill(HIST("h2dGenXiPlus"), centrality, ptmc); + histos.fill(HIST("h2dGenXiPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kOmegaMinus) { + histos.fill(HIST("h2dGenOmegaMinus"), centrality, ptmc); + histos.fill(HIST("h2dGenOmegaMinusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + if (mcParticle.pdgCode() == PDG_t::kOmegaPlusBar) { + histos.fill(HIST("h2dGenOmegaPlus"), centrality, ptmc); + histos.fill(HIST("h2dGenOmegaPlusVsMultMC"), mcCollision.multMCNParticlesEta05(), ptmc); + } + } + } + + // ______________________________________________________ + // Real data processing in Run 3 - no MC subscription + void processRealData(soa::Join const& collisions, + aod::MFTTracks const& tracks, + aod::BestCollisionsFwd3d const& besttracks, + aod::BCsWithTimestamps const& bcs) + { + analyzeRecoedV0sInRealData(collisions, tracks, besttracks, bcs); + } + + // ______________________________________________________ + // Simulated processing in Run 3 (subscribes to MC information too) + void processMonteCarlo(soa::Join const& collisions, + soa::Join const& tracks, + soa::Join const& besttracks, + aod::BCsWithTimestamps const& bcs, + soa::Join const& mccollisions, + aod::McParticles const& mcParticles) + { + analyzeRecoedV0sInMonteCarlo(collisions, mccollisions, tracks, besttracks, bcs, mcParticles); + } + + // ______________________________________________________ + // Simulated processing in Run 3 (subscribes to MC information too) + void processGenerated(soa::Join const& mcCollisions, aod::McParticles const& mcParticles, soa::Join const& collisions, aod::BCsWithTimestamps const& bcs) + { + analyzeGeneratedV0s(mcCollisions, mcParticles, collisions, bcs); + } + + PROCESS_SWITCH(forwardlambdakzeroanalysis, processRealData, "process as if real data", true); + PROCESS_SWITCH(forwardlambdakzeroanalysis, processMonteCarlo, "process as if MC", false); + PROCESS_SWITCH(forwardlambdakzeroanalysis, processGenerated, "process MC generated", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx index 96920535e21..1e67da5fac5 100644 --- a/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/hStrangeCorrelation.cxx @@ -23,6 +23,8 @@ #include "PWGLF/DataModel/LFHStrangeCorrelationTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/Utils/inelGt.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/Core/RecoDecay.h" @@ -52,10 +54,12 @@ #include #include +#include #include #include #include #include +#include #include #include #include @@ -64,12 +68,20 @@ #include +#include +#include #include #include #include +#include +#include #include +#include #include #include +#include +#include +#include #include #include @@ -79,14 +91,17 @@ using namespace o2::framework; using namespace o2::framework::expressions; using TracksComplete = soa::Join; +using TracksCompleteMC = soa::Join; using V0DatasWithoutTrackX = soa::Join; using V0DatasWithoutTrackXMC = soa::Join; +using V0DatasWithoutTrackXWithMCCores = soa::Join; struct HStrangeCorrelation { // for efficiency corrections if requested - Service ccdb; + Service ccdb{}; - Service pdgDB; + Service pdgDB{}; + o2::pwglf::ParticleCounter mCounter; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -111,22 +126,37 @@ struct HStrangeCorrelation { Configurable doCorrelationOmegaPlus{"doCorrelationOmegaPlus", false, "do OmegaPlus correlation"}; Configurable doCorrelationPion{"doCorrelationPion", false, "do Pion correlation"}; Configurable doGenEventSelection{"doGenEventSelection", true, "use event selections when performing closure test for the gen events"}; + Configurable doClosureTestPureMC{"doClosureTestPureMC", false, "fill regular ClosureTest histograms without event or reconstructed-trigger selection, using MC vertex z and centrality 0.05; keep truth-particle selections"}; Configurable selectINELgtZERO{"selectINELgtZERO", true, "select INEL>0 events"}; + Configurable selectINELgtN{"selectINELgtN", false, "select INEL>N events (more than N charged particles in |eta| < 1), N = inelGtNThreshold"}; + Configurable inelGtNThreshold{"inelGtNThreshold", 1, "N of the INEL>N selection (reco: PV contributors, gen: charged physical primaries, both in |eta| < 1)"}; Configurable zVertexCut{"zVertexCut", 10, "Cut on PV position"}; Configurable requireAllGoodITSLayers{"requireAllGoodITSLayers", false, " require that in the event all ITS are good"}; + Configurable rejectSameBunchPileup{"rejectSameBunchPileup", false, "reject collisions associated with the same found-by-T0 bunch crossing"}; Configurable requireGoodTriggerTVX{"requireGoodTriggerTVX", false, " require acceptable FT0C-FT0A time difference"}; Configurable requireGoodZvtxFT0vsPV{"requireGoodZvtxFT0vsPV", false, " require small difference between z-vertex from PV and from FT0"}; Configurable skipUnderOverflowInTHn{"skipUnderOverflowInTHn", false, "skip under/overflow in THns"}; Configurable mixingParameter{"mixingParameter", 10, "how many events are mixed"}; Configurable doMCassociation{"doMCassociation", false, "fill everything only for MC associated"}; Configurable doTriggPhysicalPrimary{"doTriggPhysicalPrimary", false, "require physical primary for trigger particles"}; + Configurable doClosureTestTriggerWithRecoTrackMatch{"doClosureTestTriggerWithRecoTrackMatch", false, "add closure-test correlations requiring the truth trigger to have at least one reconstructed track with a matching MC label"}; Configurable applyNewMCSelection{"applyNewMCSelection", false, "apply new MC Generated selection"}; + Configurable doSeparateFT0Prediction{"doSeparateFT0Prediction", false, "separate FT0M to FT0A and FT0C in prediction process"}; + Configurable useCentralityinPrediction{"useCentralityinPrediction", false, "if true, use centrality instead of multiplisity"}; + Configurable doMirroringInDelataEta{"doMirroringInDelataEta", false, "if true, fill only positive delta eta and mirror the negative side in post processing, Adjust the delta axis!"}; + Configurable doMassSpectrumCheck{"doMassSpectrumCheck", false, "if true, add and fill invariant-mass spectrum"}; + Configurable doCorrelationsHadronV0daughter{"doCorrelationsHadronV0daughter", false, "if true, do correlations of hadrons with V0 daughters"}; + Configurable doLocalDensityStudy{"doLocalDensityStudy", false, "if true, create and fill the pt vs eta vs local density spectra of triggers and V0s"}; + Configurable localDensityConeRadius{"localDensityConeRadius", 0.4, "radius of the cone in which the local density is counted"}; + Configurable doMultEstimatorResponse{"doMultEstimatorResponse", false, "processSameEventHV0s: fill MultEstimatorResponse, multNTracksPVeta1 vs FT0M percentile and amplitude, with the event selection of PairLossK0/EventRecoEfficiency (no INEL cuts)"}; + Configurable performPLambdacorrelation{"performPLambdacorrelation", 3, "do p-Lambda correlation study"}; + Configurable mixingEventForClosureTest{"mixingEventForClosureTest", false, "if true, mix events for closure test"}; + Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; + Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; + Configurable doLambdaPrimary{"doLambdaPrimary", false, "do primary selection for lambda"}; } masterConfigurations; // master analysis switches - Configurable doAssocPhysicalPrimary{"doAssocPhysicalPrimary", false, "require physical primary for associated particles"}; - Configurable doAssocPhysicalPrimaryInGen{"doAssocPhysicalPrimaryInGen", false, "require physical primary for associated particles in Generated Partilces"}; - Configurable doLambdaPrimary{"doLambdaPrimary", false, "do primary selection for lambda"}; Configurable doAutocorrelationRejection{"doAutocorrelationRejection", true, "reject pairs where trigger Id is the same as daughter particle Id"}; Configurable doMixingQAandEventQA{"doMixingQAandEventQA", true, "if true, add EvnetQA and MixingQA hist to histos"}; Configurable doITSClustersQA{"doITSClustersQA", true, "if true, add ITSCluster hist to histos"}; @@ -154,8 +184,18 @@ struct HStrangeCorrelation { ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt associated axis for histograms"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.0, 1.0, 2.0, 3.0, 100}, "pt associated axis for histograms"}; ConfigurableAxis axisPtQA{"axisPtQA", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for QA histograms"}; - ConfigurableAxis axisMultCount{"axisMultCount", {VARIABLE_WIDTH, 0, 200, 400, 600, 800, 1000, 1400, 1800, 2300, 2800, 3300, 4000, 5000, 6000}, "Mixing bins - multiplicity"}; ConfigurableAxis axisMassNSigma{"axisMassNSigma", {40, -2, 2}, "Axis for mass Nsigma"}; + ConfigurableAxis axisLocalDensity{"axisLocalDensity", {21, -0.5, 20.5}, "local density (number of tracks in the cone)"}; + ConfigurableAxis axisK0ShortMass{"axisK0ShortMass", {200, 0.400f, 0.600f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {200, 1.01f, 1.21f}, "Inv. Mass (GeV/c^{2})"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Multiplicity axis in model prediction process"}; + ConfigurableAxis axisMidrapidityMultiplicity{"axisMidrapidityMultiplicity", {VARIABLE_WIDTH, 0, 20, 40, 60, 100, 120, 140, 160, 180, 200, 220, 240, 260, 280, 300}, "Binning of the Midrapidity Multiplicity axis in model prediction process"}; + ConfigurableAxis axisNchGen{"axisNchGen", {151, -0.5f, 150.5f}, "generated charged primaries in |eta| < 1"}; + ConfigurableAxis axisNRecoCollisions{"axisNRecoCollisions", {6, -0.5f, 5.5f}, "reconstructed collisions per MC collision"}; + // finer than axisMult but containing all its default edges, so the analysis classes can be recovered by merging bins + ConfigurableAxis axisCentFT0M{"axisCentFT0M", {VARIABLE_WIDTH, 0.0f, 0.01f, 0.1f, 0.5f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 15.0f, 20.0f, 25.0f, 30.0f, 35.0f, 40.0f, 45.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "FT0M percentile of the multNTracksPVeta1 vs FT0M response"}; + ConfigurableAxis axisMultFT0M{"axisMultFT0M", {1000, 0.0f, 20000.0f}, "FT0M amplitude of the multNTracksPVeta1 vs FT0M response"}; + } axesConfigurations; // for topo var QA @@ -193,18 +233,27 @@ struct HStrangeCorrelation { Configurable assocMaxTPCSharedClusters{"assocMaxTPCSharedClusters", 200, "maximum number of shared TPC clusters (inclusive) for assoc primary tracks"}; Configurable triggerRequireL0{"triggerRequireL0", false, "require ITS L0 cluster for trigger"}; Configurable assocRequireL0{"assocRequireL0", true, "require ITS L0 cluster for assoc primary track"}; - Configurable minTPCChi2PerClusterAssociated{"minTPCChi2PerClusterAssociated", 4.0f, "Minimum TPC chi2 per cluster for associated primary tracks"}; + Configurable maxTPCChi2PerClusterAssociated{"maxTPCChi2PerClusterAssociated", 4.0f, "Maximum TPC chi2 per cluster for associated primary tracks"}; Configurable checksRequireTPCChi2{"checksRequireTPCChi2", false, "require TPC chi2 per cluster for trigger and associated primary tracks"}; Configurable requireClusterInITS{"requireClusterInITS", false, "require cluster in ITS for V0 and cascade daughter tracks"}; Configurable minITSClustersForDaughterTracks{"minITSClustersForDaughterTracks", 1, "Minimum number of ITS clusters for V0 daughter tracks"}; - + Configurable requireDCAzCut{"requireDCAzCut", false, "require DCAz cut for trigger and associated primary tracks"}; + Configurable doITSChi2Selection{"doITSChi2Selection", false, "require ITS chi2 cut for trigger and associated primary tracks"}; + Configurable checkForITSTPCMissmatchMC{"checkForITSTPCMissmatchMC", false, "if true, reject tracks with MC mask bit 13 (ITS-TPC mismatch) for trigger and associated primary tracks"}; // --- Trigger: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstant{"dcaXYconstant", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdep{"dcaXYpTdep", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; // --- Assoc track: DCA variation from basic formula: |DCAxy| < 0.004f + (0.013f / pt) Configurable dcaXYconstantAssoc{"dcaXYconstantAssoc", 0.004, "[0] in |DCAxy| < [0]+[1]/pT"}; Configurable dcaXYpTdepAssoc{"dcaXYpTdepAssoc", 0.013, "[1] in |DCAxy| < [0]+[1]/pT"}; + + Configurable dcaZconstant{"dcaZconstant", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZpTdep{"dcaZpTdep", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZconstantAssoc{"dcaZconstantAssoc", 0.004, "[0] in |DCAz| < [0]+[1]/pT"}; + Configurable dcaZpTdepAssoc{"dcaZpTdepAssoc", 0.013, "[1] in |DCAz| < [0]+[1]/pT"}; Configurable dEdxCompatibility{"dEdxCompatibility", 1, "0: loose, 1: normal, 2: tight. Defined in HStrangeCorrelationFilter"}; + Configurable chi2ITSfit{"chi2ITSfit", 36, "chi2 ITS fit for trigger and associated primary tracks"}; + } trackSelection; struct : ConfigurableGroup { std::string prefix = "v0Selection"; @@ -218,6 +267,7 @@ struct HStrangeCorrelation { Configurable v0RadiusMax{"v0RadiusMax", 200, "v0radius"}; Configurable dcaBaryonToPV{"dcaBaryonToPV", 0.2, "DCA of baryon daughter track To PV"}; Configurable dcaMesonToPV{"dcaMesonToPV", 0.05, "DCA of meson daughter track To PV"}; + Configurable dcaDaugToPVForK0s{"dcaDaugToPVForK0s", 0, "DCA of K0s daughter tracks To PV"}; Configurable lifetimecutK0S{"lifetimecutK0S", 20, "lifetimecutK0S"}; Configurable lifetimecutLambda{"lifetimecutLambda", 30, "lifetimecutLambda"}; // original equation: lArmPt*2>TMath::Abs(lArmAlpha) only for K0S @@ -253,14 +303,14 @@ struct HStrangeCorrelation { } cascadeSelections; struct : ConfigurableGroup { - std::string prefix = "`"; + std::string prefix = "checks"; // cascade selections // more cascade selections in PbPb // Configurable bachBaryonCosPA{"bachBaryonCosPA", 0.9999, "Bachelor baryon CosPA"}; // Configurable bachBaryonDCAxyToPV{"bachBaryonDCAxyToPV", 0.08, "DCA bachelor baryon to PV"}; // // on the fly correction instead of mixingParameter - Configurable doOnTheFlyFlattening{"doOnTheFlyFlattening", 0, "enable an on-the-fly correction instead of using mixing"}; + Configurable doOnTheFlyFlattening{"doOnTheFlyFlattening", false, "enable an on-the-fly correction instead of using mixing"}; // delta eta ranges for toward and transverse checks Configurable transwerseDeltaEtaRangeMin{"transwerseDeltaEtaRangeMin", 1.0, "minimum delta eta for transverse region"}; @@ -269,6 +319,94 @@ struct HStrangeCorrelation { // (N.B.: sources that can be investigated in post are not listed!) } checks; + struct : ConfigurableGroup { + std::string prefix = "pairLossK0Configurations"; + // processPairLossK0MC is split into three independent parts. Each has its own + // switch, they write into disjoint folders, and any combination of them may + // run in the same job -- including all three at once. + // doStageDiagnostics PairLossK0/{Stage,State,Geometry,Matching,Response, + // TrackQA,V0QA}: the truth-pair reconstruction ladder + // and its close-pair diagnostics + // doRecComparison PairLossK0/Comparison: the cumulative + // Rec/Truth/Gen/Final variant ladder. Runs the exact + // reconstructed correlation path internally, so + // processSameEventHV0s must be off when it is on. + // doGenLevelStudy PairLossK0/GenStudy: generator-level only, see the + // comment on runGenLevelStudy in processPairLossK0MC + // doEventRecoEfficiencyStudy + // PairLossK0/EventRecoEfficiency: MC-collision + // reconstruction efficiency, Nch^gen vs Nch^reco and + // Nch^reco vs FT0M (percentile and amplitude) responses, + // see runEventRecoEfficiencyStudy + // doPVQualityStudy PairLossK0/PVQuality: best-collision PV position and + // reco - MC residuals vs multNTracksPVeta1, and the + // MC-collision reconstruction and selection stages vs + // Nch^gen, split by the truth h-K0 content of the event, + // see runPVQualityStudy + Configurable doStageDiagnostics{"doStageDiagnostics", true, "part 1: fill the PairLossK0 truth-pair reconstruction ladder and its diagnostics"}; + Configurable doRecComparison{"doRecComparison", false, "part 2: fill the PairLossK0/Comparison cumulative Rec/Truth/Gen/Final ladder (runs the exact reconstructed path internally)"}; + Configurable recComparisonBestCollisionOnly{"recComparisonBestCollisionOnly", false, "part 2: run the Rec path of PairLossK0/Comparison only in the best reconstructed collision (most PV contributors) of each MC collision, the one the Gen, Truth and Final stages are selected on. Set false to run it in every reconstructed collision, as the data path does"}; + Configurable doGenLevelStudy{"doGenLevelStudy", false, "part 3: fill the PairLossK0/GenStudy generated-vs-reconstructed split, using generator-level event selection only"}; + Configurable doEventRecoEfficiencyStudy{"doEventRecoEfficiencyStudy", false, "fill PairLossK0/EventRecoEfficiency: Nch^gen (charged primaries, |eta| < 1) of all, reconstructed and selected MC collisions, Nch^gen vs multNTracksPVeta1 and multNTracksPVeta1 vs FT0M percentile and amplitude"}; + Configurable nchGenResponseBestCollisionOnly{"nchGenResponseBestCollisionOnly", false, "fill PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1 with the best reconstructed collision (most PV contributors) of each MC collision only, instead of with every reconstructed collision; the multNTracksPVeta1 vs FT0M responses stay per reconstructed collision"}; + Configurable doPVQualityStudy{"doPVQualityStudy", false, "fill PairLossK0/PVQuality: best-collision PV x, y and reco - MC residuals vs multNTracksPVeta1, and the MC-collision reconstruction and selection stages vs Nch^gen, split by the truth h-K0 content of the event"}; + // Coarse on purpose: the PV histograms are dense THnF with a fine position axis + ConfigurableAxis axisPVQualityNTracks{"axisPVQualityNTracks", {VARIABLE_WIDTH, -0.5f, 0.5f, 1.5f, 2.5f, 3.5f, 4.5f, 5.5f, 6.5f, 8.5f, 10.5f, 12.5f, 15.5f, 20.5f, 25.5f, 30.5f, 40.5f, 50.5f, 70.5f, 100.5f, 150.5f}, "multNTracksPVeta1 classes of PairLossK0/PVQuality"}; + ConfigurableAxis axisPVPosition{"axisPVPosition", {400, -0.2f, 0.2f}, "reconstructed PV x and y (cm) of PairLossK0/PVQuality"}; + ConfigurableAxis axisPVResidual{"axisPVResidual", {200, -0.1f, 0.1f}, "reconstructed minus generated PV x, y and z (cm) of PairLossK0/PVQuality"}; + Configurable applyINELSelectionAtGenLevel{"applyINELSelectionAtGenLevel", false, "part 2 (pp only): replace the reconstructed INEL cuts by Nch^gen >= 1 of the MC collision in the Rec, Gen, Truth and Final stages (selectINELgtN is ignored) and fill only PairLossK0/Comparison/NchGen (Rec and Gen vs Nch^gen, integrated over vertex z and multiplicity); requires doRecComparison and applyNewMCSelection"}; + ConfigurableAxis axisComparisonNchGen{"axisComparisonNchGen", {VARIABLE_WIDTH, 0.5f, 4.5f, 9.5f, 19.5f, 1000.5f}, "Nch^gen classes (charged primaries in |eta| < 1) of PairLossK0/Comparison/NchGen"}; + // MC-only event quality cut on the Nch^gen vs multNTracksPVeta1 response + // (PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1): a reconstructed collision + // is kept only if |multNTracksPVeta1 - mu(Nch^gen)| <= nSigma * max(sigma(Nch^gen), sigmaMin), + // with mu a polynomial in Nch^gen and sigma a polynomial in sqrt(Nch^gen), e.g. fits of + // the mean and width of each Nch^gen column of that response. It uses the generated + // multiplicity, so it has no data counterpart + Configurable applyNchResponseBandCut{"applyNchResponseBandCut", false, "part 2 (MC only): keep only reconstructed collisions inside the mu +- nSigma*sigma band of the Nch^gen vs multNTracksPVeta1 response, in the Rec (every collision), Gen, Truth and Final (best collision) stages of PairLossK0/Comparison"}; + Configurable> nchResponseBandMeanPars{"nchResponseBandMeanPars", {0.0, 0.9}, "mean multNTracksPVeta1 = p0 + p1*x + p2*x^2 + ..., x = Nch^gen"}; + Configurable> nchResponseBandSigmaPars{"nchResponseBandSigmaPars", {0.0, 0.35}, "multNTracksPVeta1 width = p0 + p1*sqrt(x) + p2*x + p3*x^1.5 + ..., x = Nch^gen"}; + Configurable nchResponseBandNSigma{"nchResponseBandNSigma", 3.0f, "half-width of the band in units of sigma"}; + Configurable nchResponseBandSigmaMin{"nchResponseBandSigmaMin", 1.0f, "lower bound of sigma, which keeps the band open at low Nch^gen"}; + // Generated charged multiplicity of the MC collision, counted in |eta| < 0.8 + // by mCounter. Plain ConfigurableAxis: unlike the correlation axes it is NOT + // trimmed by skipUnderOverflowInTHn, so what you configure is what you get. + ConfigurableAxis axisGenStudyNch{"axisGenStudyNch", {VARIABLE_WIDTH, 0.0f, 2.0f, 5.0f, 10.0f, 15.0f, 20.0f, 25.0f, 30.0f, 40.0f, 60.0f, 100.0f}, "generated charged multiplicity in |#eta| < 0.8"}; + Configurable doClosureTestStages{"doClosureTestStages", true, "create and fill the whole ClosureTest/PairLossK0 folder: the truth and any-reconstructed-object stages of the truth h-K0 pair, mirroring the first processPairLossK0MC stages"}; + Configurable doEventQualityStudy{"doEventQualityStudy", false, "add eight EventQuality folders to the closure PairLossK0 study; requires doClosureTestStages"}; + Configurable eventQualityDeltaPhiBins{"eventQualityDeltaPhiBins", 36, "delta phi bins in the sparse event-quality histograms"}; + Configurable eventQualityDeltaEtaBins{"eventQualityDeltaEtaBins", 20, "delta eta bins in the sparse event-quality histograms"}; + Configurable applyRecoEventSelection{"applyRecoEventSelection", true, "apply the standard reconstructed-event selection in the K0 pair-loss diagnostic"}; + Configurable fillFinalPairOnce{"fillFinalPairOnce", true, "part 1: fill each truth h-K0 pair at most once at the final stage. Set false to loop over all reconstructed trigger x K0 combinations, as the data path does"}; + Configurable finalPairUseBestCollisionOnly{"finalPairUseBestCollisionOnly", true, "part 1: build the final trigger and K0 objects only in the best collision. Set false to use every associated reconstructed collision that passes the event selection, pairing within one collision, as the data path does"}; + Configurable daughterPtMin{"daughterPtMin", 0.05f, "minimum generated daughter pT for the findable K0 category"}; + Configurable daughterEtaMax{"daughterEtaMax", 0.9f, "maximum absolute generated daughter eta for the findable K0 category"}; + Configurable minRadius{"minRadius", 0.8f, "minimum radius in metres used for the minimum delta-phi-star search"}; + Configurable maxRadius{"maxRadius", 2.5f, "maximum radius in metres used for the minimum delta-phi-star search"}; + Configurable radiusStep{"radiusStep", 0.05f, "radius step in metres used for the minimum delta-phi-star search"}; + ConfigurableAxis axisMinDeltaPhiStar{"axisMinDeltaPhiStar", {120, 0.0f, 0.3f}, "minimum absolute delta phi star"}; + ConfigurableAxis axisSignedDeltaPhiStar{"axisSignedDeltaPhiStar", {120, -0.3f, 0.3f}, "signed delta phi star at the minimum"}; + ConfigurableAxis axisDaughterDeltaEta{"axisDaughterDeltaEta", {100, -0.2f, 0.2f}, "trigger-daughter delta eta"}; + ConfigurableAxis axisDaughterDeltaPhi{"axisDaughterDeltaPhi", {120, -0.3f, 0.3f}, "trigger-daughter delta phi"}; + ConfigurableAxis axisDecayRadius{"axisDecayRadius", {100, 0.0f, 200.0f}, "generated K0 decay radius (cm)"}; + // The following mirror the isValidTrigger() cuts applied by hStrangeCorrelationFilter.cxx + // when building the TriggerTracks table. They must be kept in sync by hand with whatever + // values are configured for that (separate) filter task, since this diagnostic cannot read + // them directly: it only sees the already-filtered TriggerTracks table, not why a given + // best-collision track failed to enter it. + Configurable triggerTracksEtaMin{"triggerTracksEtaMin", -0.8f, "must match triggerEtaMin in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksEtaMax{"triggerTracksEtaMax", 0.8f, "must match triggerEtaMax in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksPtMin{"triggerTracksPtMin", 3.0f, "must match triggerPtCutMin in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksPtMax{"triggerTracksPtMax", 20.0f, "must match triggerPtCutMax in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksMinCrossedRows{"triggerTracksMinCrossedRows", 70, "must match minTPCNCrossedRows in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksRequireITS{"triggerTracksRequireITS", true, "must match triggerRequireITS in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksMaxSharedClusters{"triggerTracksMaxSharedClusters", 200, "must match triggerMaxTPCSharedClusters in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksRequireLayer0{"triggerTracksRequireLayer0", false, "must match triggerRequireL0 in hStrangeCorrelationFilter.cxx"}; + // Separate from trackSelection.dcaXY*, which is this task's systematic-variation knob: the + // replica must follow the value the filter was run with, not the varied analysis-level one. + Configurable triggerTracksDcaXYconstant{"triggerTracksDcaXYconstant", 0.004f, "must match dcaXYconstant in hStrangeCorrelationFilter.cxx"}; + Configurable triggerTracksDcaXYpTdep{"triggerTracksDcaXYpTdep", 0.013f, "must match dcaXYpTdep in hStrangeCorrelationFilter.cxx"}; + } pairLossK0Configurations; + struct ValidCollision { struct ValidParticle { float eta; @@ -279,13 +417,11 @@ struct HStrangeCorrelation { float efficiencyError; int type; }; - float pvz; - float mult; std::vector trigParticles; std::vector assocParticles; void addValidParticle(float eta, float phi, float pt, int region, float efficiency, float efficiencyError, int type) { - ValidParticle particle{eta, phi, pt, region, efficiency, efficiencyError, type}; + ValidParticle particle{.eta = eta, .phi = phi, .pt = pt, .region = region, .efficiency = efficiency, .efficiencyError = efficiencyError, .type = type}; if (type == -1) { trigParticles.push_back(particle); @@ -298,44 +434,51 @@ struct HStrangeCorrelation { using ValidCollisions = std::vector>; ValidCollisions validCollisions; + struct ValidTriggerInGen { + double eta; + double phi; + double pt; + }; + using GenLevelCollisions = std::vector>>; + GenLevelCollisions genLevelCollisions; // objects to use for efficiency corrections - TH2F* hEfficiencyTrigger; - TH3F* hEfficiencyTriggerMult; - THnF* hEfficiencyTriggerMultVsPhi; - TH2F* hEfficiencyPion; - TH2F* hEfficiencyK0Short; - THnF* hEfficiencyK0ShortMultVsPhi; - TH2F* hEfficiencyLambda; - THnF* hEfficiencyLambdaMultVsPhi; - TH2F* hEfficiencyAntiLambda; - THnF* hEfficiencyAntiLambdaMultVsPhi; - TH2F* hEfficiencyXiMinus; - THnF* hEfficiencyXiMinusMultVsPhi; - TH2F* hEfficiencyXiPlus; - THnF* hEfficiencyXiPlusMultVsPhi; - TH2F* hEfficiencyOmegaMinus; - THnF* hEfficiencyOmegaMinusMultVsPhi; - TH2F* hEfficiencyOmegaPlus; - THnF* hEfficiencyOmegaPlusMultVsPhi; - TH2F* hEfficiencyHadron; - TH3F* hEfficiencyHadronMult; - TH1F* hPurityHadron; - TH2F* hPurityHadronMult; + TH2F* hEfficiencyTrigger = nullptr; + TH3F* hEfficiencyTriggerMult = nullptr; + THnT* hEfficiencyTriggerMultVsPhi = nullptr; + TH2F* hEfficiencyPion = nullptr; + TH2F* hEfficiencyK0Short = nullptr; + THnT* hEfficiencyK0ShortMultVsPhi = nullptr; + TH2F* hEfficiencyLambda = nullptr; + THnT* hEfficiencyLambdaMultVsPhi = nullptr; + TH2F* hEfficiencyAntiLambda = nullptr; + THnT* hEfficiencyAntiLambdaMultVsPhi = nullptr; + TH2F* hEfficiencyXiMinus = nullptr; + THnT* hEfficiencyXiMinusMultVsPhi = nullptr; + TH2F* hEfficiencyXiPlus = nullptr; + THnT* hEfficiencyXiPlusMultVsPhi = nullptr; + TH2F* hEfficiencyOmegaMinus = nullptr; + THnT* hEfficiencyOmegaMinusMultVsPhi = nullptr; + TH2F* hEfficiencyOmegaPlus = nullptr; + THnT* hEfficiencyOmegaPlusMultVsPhi = nullptr; + TH2F* hEfficiencyHadron = nullptr; + TH3F* hEfficiencyHadronMult = nullptr; + TH1F* hPurityHadron = nullptr; + TH2F* hPurityHadronMult = nullptr; // objects to propagate the efficiency uncertainty - TH2F* hEfficiencyUncertaintyTrigger; - TH3F* hEfficiencyUncertaintyTriggerMult; - TH2F* hEfficiencyUncertaintyPion; - TH2F* hEfficiencyUncertaintyK0Short; - TH2F* hEfficiencyUncertaintyLambda; - TH2F* hEfficiencyUncertaintyAntiLambda; - TH2F* hEfficiencyUncertaintyXiMinus; - TH2F* hEfficiencyUncertaintyXiPlus; - TH2F* hEfficiencyUncertaintyOmegaMinus; - TH2F* hEfficiencyUncertaintyOmegaPlus; - TH2F* hEfficiencyUncertaintyHadron; - TH3F* hEfficiencyUncertaintyHadronMult; - TH1F* hPurityUncertaintyHadron; - TH2F* hPurityUncertaintyHadronMult; + TH2F* hEfficiencyUncertaintyTrigger = nullptr; + TH3F* hEfficiencyUncertaintyTriggerMult = nullptr; + TH2F* hEfficiencyUncertaintyPion = nullptr; + TH2F* hEfficiencyUncertaintyK0Short = nullptr; + TH2F* hEfficiencyUncertaintyLambda = nullptr; + TH2F* hEfficiencyUncertaintyAntiLambda = nullptr; + TH2F* hEfficiencyUncertaintyXiMinus = nullptr; + TH2F* hEfficiencyUncertaintyXiPlus = nullptr; + TH2F* hEfficiencyUncertaintyOmegaMinus = nullptr; + TH2F* hEfficiencyUncertaintyOmegaPlus = nullptr; + TH2F* hEfficiencyUncertaintyHadron = nullptr; + TH3F* hEfficiencyUncertaintyHadronMult = nullptr; + TH1F* hPurityUncertaintyHadron = nullptr; + TH2F* hPurityUncertaintyHadronMult = nullptr; using BinningTypePP = ColumnBinningPolicy; using BinningTypePbPb = ColumnBinningPolicy; @@ -347,11 +490,13 @@ struct HStrangeCorrelation { // Preslice collisionSlicePions = aod::assocHadrons::collisionId; Preslice collisionSliceHadrons = aod::assocHadrons::collisionId; Preslice perCollision = aod::mcparticle::mcCollisionId; + Preslice pairLossTracksPerCollision = aod::track::collisionId; + Preslice pairLossV0sPerCollision = aod::v0data::collisionId; - static constexpr std::string_view V0names[] = {"K0Short", "Lambda", "AntiLambda"}; - static constexpr std::string_view Cascadenames[] = {"XiMinus", "XiPlus", "OmegaMinus", "OmegaPlus"}; - static constexpr std::string_view Particlenames[] = {"K0Short", "Lambda", "AntiLambda", "XiMinus", "XiPlus", "OmegaMinus", "OmegaPlus", "Pion", "Hadron"}; - static constexpr int PdgCodes[] = {310, 3122, -3122, 3312, -3312, 3334, -3334, 211}; + static constexpr std::array V0names = {"K0Short", "Lambda", "AntiLambda"}; + static constexpr std::array Cascadenames = {"XiMinus", "XiPlus", "OmegaMinus", "OmegaPlus"}; + static constexpr std::array Particlenames = {"K0Short", "Lambda", "AntiLambda", "XiMinus", "XiPlus", "OmegaMinus", "OmegaPlus", "Pion", "Hadron"}; + static constexpr std::array PdgCodes = {310, 3122, -3122, 3312, -3312, 3334, -3334, 211}; static constexpr int IndexPion = 7; static constexpr int IndexXiMinus = 0; @@ -362,14 +507,27 @@ struct HStrangeCorrelation { static constexpr int IndexLambda = 1; static constexpr int IndexAntiLambda = 2; - uint16_t doCorrelation; - int mRunNumber; - int mRunNumberZorro; + template + static float getV0InvariantMass(TV0 const& v0) + { + static_assert(Index >= IndexK0 && Index <= IndexAntiLambda); + if constexpr (Index == IndexK0) { + return v0.mK0Short(); + } else if constexpr (Index == IndexLambda) { + return v0.mLambda(); + } else { + return v0.mAntiLambda(); + } + } + + uint16_t doCorrelation = 0; + int mRunNumber = -1; + int mRunNumberZorro = -1; std::vector> axisRanges; - const float ctauxi = 4.91; // from PDG - const float ctauomega = 2.461; // from PDG + static constexpr float ctauxi = 4.91; // from PDG + static constexpr float ctauomega = 2.461; // from PDG static constexpr float MinRadiusTPC = 0.8; static constexpr float MaxRadiusTPC = 2.5; @@ -378,6 +536,320 @@ struct HStrangeCorrelation { static constexpr int AssocParticleTypes = 9; // K0S, Lambda, AntiLambda, Xi-, Xi+, Omega-, Omega+, Pion, Hadron static constexpr int AssocParticleTypesNoHadron = 8; // K0S, Lambda, AntiLambda, Xi-, Xi+, Omega-, Omega+, Pion + static constexpr int AssocV0Types = 3; // K0S, Lambda, AntiLambda, + static constexpr int AssocCascadeTypes = 4; // Xi-, Xi+, Omega-, Omega+ + + // Cumulative reconstruction ladder for one truth h-K0 pair: every stage is a + // strictly narrower requirement than the one before it, so the ratio of two + // neighbouring stages is the efficiency of exactly the step between them. + // + // Two "pure reconstruction" levels anchor the chain. Both mean "the object is + // present in the reconstruction with no selection applied whatsoever": + // PairLossTriggerPureReco a track carrying the trigger's MC label exists + // PairLossV0PureReco both K0 daughters have a reconstructed track + // The V0 one sits deliberately at daughter-track level rather than at V0Datas + // level: a row in V0Datas has already survived the V0 builder's own cuts + // (cos(PA), daughter DCA, radius, crossed rows), so the step + // PairLossV0PureReco -> PairLossV0Candidate isolates exactly what the builder + // throws away, which no other stage can show. + // + // Everything is evaluated in the best collision. The "any reconstructed + // collision" variants live in the ClosureTest/PairLossK0/AnyTrack* folders + // instead, so keeping them here as well would only duplicate them. + enum PairLossK0Stage : int { + PairLossGenPair = 0, + PairLossFindablePair, + PairLossTriggerPureReco, + PairLossTriggerInTable, + PairLossTriggerFinal, + PairLossV0PureReco, + PairLossV0Candidate, + PairLossV0InTable, + PairLossV0Final, + PairLossFinalPair, + PairLossK0NStages + }; + + static constexpr std::array PairLossK0StageNames = { + "Gen pair", + "Findable K0->pi+pi-", + "Trigger pure reco (best collision)", + "Trigger in TriggerTracks", + "Trigger final selection", + "V0 pure reco (both daughters)", + "V0 candidate (best collision)", + "V0 in AssocV0s", + "V0 final selection", + "Final reconstructed pair"}; + + // Independent event classifications, not cumulative cuts. Values are fixed + // diagnostic bins (labels below), and never change the parent pair selection. + static constexpr int PairLossNEventQualityGroups = 8; + static constexpr std::array PairLossEventQualityNames = { + "NContributors", "PVMaxPull", "PVPurity", "CollisionAssociation", + "TimeResolution", "Occupancy", "GenMultiplicity", "EventShape"}; + static constexpr std::array, PairLossNEventQualityGroups> PairLossEventQualityLabels = {{ + {"no contributors", "1-2", "3-5", "6-10", "11-20", ">=21"}, + {"invalid covariance/pull", "max |pull| <1", "1<=max |pull|<3", "3<=max |pull|<5", "max |pull|>=5", "unused"}, + {"no PV tracks", "missing MC labels", "purity <0.95", "0.95<=purity<1", "purity =1", "unused"}, + {"no matched tracks", "one rec collision", "multiple: best fraction >=0.9", "multiple: 0.5<=fraction<0.9", "multiple: fraction <0.5", "unused"}, + {"invalid time resolution", "0=500 ns", "unused"}, + {"unavailable", "0", "1-99", "100-499", "500-999", ">=1000"}, + {"0", "1-4", "5-9", "10-19", "20-39", ">=40"}, + {"fewer than 3 shape tracks", "S_T<0.3", "0.3<=S_T<0.7", "S_T>=0.7", "unused", "unused"}, + }}; + + struct PairLossTrackInfo { + int64_t globalIndex = -1; + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + int sign = 0; + int tpcCrossedRows = 0; + int tpcSharedClusters = 0; + int itsClusters = 0; + bool hasLayer0 = false; + // Needed to replicate the declarative preFilterTracks DCAxy cut of + // hStrangeCorrelationFilter.cxx, which uses exactly these two columns. + float dcaXY = 0.0f; + float signed1Pt = 0.0f; + int64_t collisionId = -1; + }; + + // First-failing-condition breakdown for the stage3->4 gate ("Trigger track, best collision" + // -> "Trigger in TriggerTracks"). Order mirrors the order in which hStrangeCorrelationFilter.cxx + // applies its conditions -- the declarative preFilterTracks DCAxy cut first, then the early + // returns of isValidTrigger() -- so "first reason to fail" means the same thing here. + // + // Pass/fail itself is decided by the real TriggerTracks table, not by the replica below; + // the replica only attributes a reason once the table has already said "not in". That keeps + // the Passed bin numerically identical to stage 4 even when the replica's configurables have + // drifted away from the filter's, and routes any such drift into the two dedicated bins. + enum PairLossTriggerTracksFailureReason : int { + PairLossTriggerTracksPassed = 0, + // First, because preFilterTracks is applied to the track table before isValidTrigger() ever + // sees the track, so DCAxy is the earliest condition a trigger candidate can fail. + PairLossTriggerTracksFailDcaXY, + PairLossTriggerTracksFailEta, + PairLossTriggerTracksFailPt, + PairLossTriggerTracksFailCrossedRows, + PairLossTriggerTracksFailITS, + PairLossTriggerTracksFailSharedClusters, + PairLossTriggerTracksFailLayer0, + // Really not in the TriggerTracks table, yet the replica below claims that at least one + // candidate track should have passed. The only possible source is a mismatch between this + // task's triggerTracks* configurables and hStrangeCorrelationFilter.cxx, so this bin acts + // directly as a "are the configurations aligned?" monitor: it should be zero. + PairLossTriggerTracksNotInTableUnexplained, + // The opposite drift: really in the table, but the replica would reject it. + PairLossTriggerTracksInTableButCutRejects, + PairLossTriggerTracksNReasons + }; + + static constexpr std::array PairLossTriggerTracksFailureNames = { + "Passed (really in TriggerTracks)", + "Failed DCAxy prefilter", + "Failed eta window", + "Failed pT window", + "Failed min TPC crossed rows", + "Failed hasITS requirement", + "Failed max TPC shared clusters", + "Failed ITS layer-0 requirement", + "Not in table, no cut explains it", + "In table, but replica would reject"}; + + // Breakdown of the Rec variant 0 -> 1 step of PairLossK0/Comparison ("+ truth-trigger + // match/acceptance"). Every Rec trigger is counted in the All bin and in exactly one + // other bin: Matched if it passes variant 1, otherwise the first condition it fails. + // The order follows the variant 1 lookup -- MC label, MC collision of the label, then + // the truth-trigger acceptance in the order pairLossTruthTriggerRejection checks it -- + // so All = Matched + the sum of the rejection bins. + enum PairLossTriggerMatchQABin : int { + PairLossTriggerMatchAll = 0, + PairLossTriggerMatchMatched, + PairLossTriggerMatchNoMcLabel, + PairLossTriggerMatchOtherMcCollision, + PairLossTriggerMatchFailPdg, + PairLossTriggerMatchFailEta, + PairLossTriggerMatchFailPt, + PairLossTriggerMatchFailCharge, + PairLossTriggerMatchNBins + }; + + static constexpr std::array PairLossTriggerMatchQANames = { + "All Rec (variant 0)", + "Matched (variant 1)", + "No MC label", + "Label in another MC collision", + "Truth PDG not pi/K/p/e/mu", + "|eta truth| > etaSel", + "pT truth outside trigger axis", + "Truth charge"}; + + // Truth content of an MC collision for PairLossK0/PVQuality. Trigger and K0 follow + // the acceptance, primary and leading-trigger requirements of PairLossK0/Comparison/Truth, + // and a pair excludes a trigger that is one of the K0 daughters. An MC collision enters + // every class it belongs to, so the classes overlap and must not be summed: + // HasTrigger, HasPair and K0DaughterPVContributor are subsets of All, NearPair and + // AwayPair subsets of HasPair, and one MC collision can be both NearPair and AwayPair. + // K0DaughterPVContributor needs the best reconstructed collision, so it is empty at the + // first stage of hEventStage. + enum PairLossPVQualityClass : int { + PairLossPVQualityAll = 0, + PairLossPVQualityHasTrigger, + PairLossPVQualityHasPair, + PairLossPVQualityNearPair, + PairLossPVQualityAwayPair, + PairLossPVQualityK0DaughterPVContributor, + PairLossPVQualityNClasses + }; + + static constexpr std::array PairLossPVQualityClassNames = { + "all", + "truth trigger", + "truth h-K^{0}_{S} pair", + "near-side pair (|#Delta#varphi| < #pi/3)", + "away-side pair (|#Delta#varphi - #pi| < #pi/3)", + "K^{0}_{S} daughter is a PV contributor"}; + + // MC-collision stages of PairLossK0/PVQuality/hEventStage, each a subset of the previous one + enum PairLossPVQualityStage : int { + PairLossPVQualityStageAll = 0, + PairLossPVQualityStageHasRecoColl, + PairLossPVQualityStageSelected, + PairLossPVQualityNStages + }; + + static constexpr std::array PairLossPVQualityStageNames = { + "MC collision", + "has reconstructed collision", + "best collision selected"}; + + struct PairLossV0Info { + int64_t globalIndex = -1; + int64_t positiveTrackId = -1; + int64_t negativeTrackId = -1; + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + float radius = 0.0f; + float cosPA = 0.0f; + float dcaDaughters = 0.0f; + float massNSigma = 0.0f; + int64_t collisionId = -1; + }; + + struct PairLossTruthTrackInfo { + int64_t globalIndex = -1; + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + int sign = 0; + bool physicalPrimary = false; + }; + + // One object of a GenStudy h-K0 pair: generated kinematics plus whether it has a + // reconstructed counterpart, in exactly the sense the GenStudy single-particle + // folders use. + struct GenStudyPairObject { + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + int64_t globalIndex = -1; + int64_t motherIndex = -1; + bool reconstructed = false; + }; + + struct PairLossTruthK0Info { + int64_t globalIndex = -1; + float pt = 0.0f; + float eta = 0.0f; + float phi = 0.0f; + float decayRadius = -1.0f; + bool findable = false; + bool physicalPrimary = false; + PairLossTruthTrackInfo positiveDaughter; + PairLossTruthTrackInfo negativeDaughter; + }; + + struct PairLossDeltaPhiStarInfo { + bool valid = false; + float minAbs = std::numeric_limits::max(); + float signedAtMin = 0.0f; + float radiusAtMin = -1.0f; + }; + + using PairLossTrackMap = std::unordered_map>; + using PairLossV0Map = std::unordered_map>; + + struct PairLossPairKey { + int64_t triggerId = -1; + int64_t k0Id = -1; + bool operator==(PairLossPairKey const&) const = default; + }; + struct PairLossPairKeyHash { + size_t operator()(PairLossPairKey const& key) const + { + const auto h1 = std::hash{}(key.triggerId); + const auto h2 = std::hash{}(key.k0Id); + return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); + } + }; + + // Per-MC-collision context used only while the ordinary Rec implementation + // is running inside the PairLoss Rec comparison part. It is thread-local + // static because adding another task data member exceeds the number of + // elements supported by the O2 task-reflection machinery. + struct PairLossComparisonContext { + bool active = false; + int64_t bestCollisionId = -1; + float vtxZ = 0.0f; + float multiplicity = 0.0f; + // Nch^gen of the MC collision; >= 0 only with applyINELSelectionAtGenLevel, + // then the Rec branches fill only PairLossK0/Comparison/NchGen + int nChGen = -1; + std::unordered_map trackToMc; + std::unordered_map v0ToMc; + std::unordered_map truthTriggers; + std::unordered_map truthK0s; + std::unordered_set primaryTruthTriggerIds; + std::unordered_set primaryTruthK0Ids; + std::unordered_set oldTruthTriggerIds; + std::unordered_set oldTruthK0Ids; + // Exact old-Final truth-pair keys for the current MC collision. Used at + // Rec variant 14 to form the Rec-minus-Final set difference. + std::unordered_set finalTruthPairs; + std::unordered_set countedPairs; + std::unordered_set countedTriggers; + // PairLossTriggerMatchQABin of the MC particle behind every labelled trigger + // track, keyed by MC particle. Only particles of this MC collision are present. + std::unordered_map triggerTruthRejection; + + void clear() + { + active = false; + bestCollisionId = -1; + vtxZ = 0.0f; + multiplicity = 0.0f; + nChGen = -1; + trackToMc.clear(); + v0ToMc.clear(); + truthTriggers.clear(); + truthK0s.clear(); + primaryTruthTriggerIds.clear(); + primaryTruthK0Ids.clear(); + oldTruthTriggerIds.clear(); + oldTruthK0Ids.clear(); + finalTruthPairs.clear(); + countedPairs.clear(); + countedTriggers.clear(); + triggerTruthRejection.clear(); + } + }; + static thread_local PairLossComparisonContext pairLossComparison; + + int mPairLossRunNumber = -1; + double mPairLossMagneticField = 0.0; /// Function to aid in calculating delta-phi /// \param phi1 first phi value @@ -389,6 +861,196 @@ struct HStrangeCorrelation { return shiftedDeltaPhi; } + /// Counts how many associated-quality tracks lie inside a cone of radius localDensityConeRadius around (etaRef, phiRef), skipping the listed track indices + template + int computeLocalDensity(float etaRef, float phiRef, TTracks const& tracks, std::initializer_list skipIds) + { + int localDensity = 0; + for (auto const& track : tracks) { + if (std::find(skipIds.begin(), skipIds.end(), track.globalIndex()) != skipIds.end() || !isValidAssocHadron(track)) { + continue; + } + double deltaEta = track.eta() - etaRef; + double deltaPhi = RecoDecay::constrainAngle(track.phi() - phiRef, -PI); + if (std::hypot(deltaEta, deltaPhi) < masterConfigurations.localDensityConeRadius) { + localDensity++; + } + } + return localDensity; + } + + /// Collects the MC index of a particle and of its decay products, so that a particle never contributes to its own local density + template + void collectDescendantIds(TMcParticle const& mcParticle, std::vector& ids, int depth = 0) + { + ids.push_back(mcParticle.globalIndex()); + if (depth >= 3 || !mcParticle.has_daughters()) { + return; + } + for (auto const& daughter : mcParticle.template daughters_as()) { + collectDescendantIds(daughter, ids, depth + 1); + } + } + + /// Generated-level counterpart: the density the reconstruction would have measured around the generated + /// direction, i.e. the same associated-quality tracks of the same collision, so that the axis means the + /// same thing here as in the reconstructed histograms and can be used to correct data binned in it + template + int computeLocalDensityGen(TMcParticle const& mcParticle, TTracks const& tracks) + { + std::vector skipIds; + collectDescendantIds(mcParticle, skipIds); + int localDensity = 0; + for (auto const& track : tracks) { + if (!isValidAssocHadron(track)) { + continue; + } + if (track.has_mcParticle() && std::find(skipIds.begin(), skipIds.end(), track.mcParticleId()) != skipIds.end()) { + continue; + } + double deltaEta = track.eta() - mcParticle.eta(); + double deltaPhi = RecoDecay::constrainAngle(track.phi() - mcParticle.phi(), -PI); + if (std::hypot(deltaEta, deltaPhi) < masterConfigurations.localDensityConeRadius) { + localDensity++; + } + } + return localDensity; + } + + static bool isPairLossTriggerPdg(int pdgCode) + { + const int absPdg = std::abs(pdgCode); + return absPdg == PDG_t::kPiPlus || absPdg == PDG_t::kKPlus || absPdg == PDG_t::kProton || absPdg == PDG_t::kElectron || absPdg == PDG_t::kMuonMinus; + } + + // Truth-trigger acceptance of the PairLoss Rec comparison (comparisonTruthTriggers): + // species, |eta|, pT and charge, checked in this order. Returns + // PairLossTriggerMatchMatched if the generated particle passes all of them, otherwise + // the PairLossTriggerMatchQABin of the first one it fails. sign is set to the charge + // sign once species, |eta| and pT have passed, and to 0 before. + template + int pairLossTruthTriggerRejection(TMcParticle const& mcParticle, int& sign) + { + sign = 0; + if (!isPairLossTriggerPdg(mcParticle.pdgCode())) { + return PairLossTriggerMatchFailPdg; + } + if (std::abs(mcParticle.eta()) > etaSel) { + return PairLossTriggerMatchFailEta; + } + if (mcParticle.pt() < axisRanges[3][0] || mcParticle.pt() > axisRanges[3][1]) { + return PairLossTriggerMatchFailPt; + } + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; + sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); + if (sign == 0 || (triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0)) { + return PairLossTriggerMatchFailCharge; + } + return PairLossTriggerMatchMatched; + } + + // PairLossTriggerMatchQABin of a Rec trigger track while the PairLoss Rec comparison + // runs: the two lookups of Rec variant 1, resolved into the first one that fails + static int pairLossTriggerMatchBin(int64_t trackGlobalIndex) + { + const auto mcIt = pairLossComparison.trackToMc.find(trackGlobalIndex); + if (mcIt == pairLossComparison.trackToMc.end()) { + return PairLossTriggerMatchNoMcLabel; + } + const auto rejectionIt = pairLossComparison.triggerTruthRejection.find(mcIt->second); + if (rejectionIt == pairLossComparison.triggerTruthRejection.end()) { + return PairLossTriggerMatchOtherMcCollision; + } + return rejectionIt->second; + } + + template + PairLossTrackInfo makePairLossTrackInfo(TTrack const& track) const + { + return PairLossTrackInfo{ + .globalIndex = static_cast(track.globalIndex()), + .pt = track.pt(), + .eta = track.eta(), + .phi = track.phi(), + .sign = track.sign(), + .tpcCrossedRows = track.tpcNClsCrossedRows(), + .tpcSharedClusters = track.tpcNClsShared(), + .itsClusters = track.itsNCls(), + .hasLayer0 = static_cast(TESTBIT(track.itsClusterMap(), 0)), + .dcaXY = track.dcaXY(), + .signed1Pt = track.signed1Pt(), + .collisionId = static_cast(track.collisionId())}; + } + + // Replicates preFilterTracks and isValidTrigger() from hStrangeCorrelationFilter.cxx + // condition-by-condition (same order) so that, for a trigger already known to exist in the best + // collision (stage 3), we can tell which single cut is responsible for it not making it + // into the TriggerTracks table (stage 4), instead of only knowing that it failed overall. + int classifyTriggerTracksFailure(PairLossTrackInfo const& info) + { + // Negation of "Filter preFilterTracks = nabs(dcaXY) < dcaXYconstant + dcaXYpTdep * nabs(signed1Pt)": + // same columns, same formula, and >= so that the boundary is rejected exactly as the filter does. + if (std::abs(info.dcaXY) >= pairLossK0Configurations.triggerTracksDcaXYconstant + pairLossK0Configurations.triggerTracksDcaXYpTdep * std::abs(info.signed1Pt)) { + return PairLossTriggerTracksFailDcaXY; + } + if (info.eta > pairLossK0Configurations.triggerTracksEtaMax || info.eta < pairLossK0Configurations.triggerTracksEtaMin) { + return PairLossTriggerTracksFailEta; + } + if (info.pt > pairLossK0Configurations.triggerTracksPtMax || info.pt < pairLossK0Configurations.triggerTracksPtMin) { + return PairLossTriggerTracksFailPt; + } + if (info.tpcCrossedRows < pairLossK0Configurations.triggerTracksMinCrossedRows) { + return PairLossTriggerTracksFailCrossedRows; + } + if (info.itsClusters <= 0 && pairLossK0Configurations.triggerTracksRequireITS) { + return PairLossTriggerTracksFailITS; + } + if (info.tpcSharedClusters > pairLossK0Configurations.triggerTracksMaxSharedClusters) { + return PairLossTriggerTracksFailSharedClusters; + } + if (!info.hasLayer0 && pairLossK0Configurations.triggerTracksRequireLayer0) { + return PairLossTriggerTracksFailLayer0; + } + return PairLossTriggerTracksPassed; + } + + PairLossDeltaPhiStarInfo calculateMinimumDeltaPhiStar(PairLossTruthTrackInfo const& trigger, PairLossTruthTrackInfo const& daughter, double magneticField, float decayRadiusCm) + { + PairLossDeltaPhiStarInfo result; + if (trigger.pt <= 0.0f || daughter.pt <= 0.0f || pairLossK0Configurations.radiusStep <= 0.0f) { + return result; + } + + const double triggerPhase = (-0.3 * magneticField * trigger.sign) / (2.0 * trigger.pt); + const double daughterPhase = (-0.3 * magneticField * daughter.sign) / (2.0 * daughter.pt); + const double deltaPhi = daughter.phi - trigger.phi; + const double startRadius = std::max(static_cast(pairLossK0Configurations.minRadius), static_cast(std::max(decayRadiusCm, 0.0f)) / 100.0); + const double maxRadius = pairLossK0Configurations.maxRadius; + const double radiusStep = pairLossK0Configurations.radiusStep; + + // radii startRadius + i * radiusStep up to maxRadius, with half a step of tolerance; + // nRadii <= 0 (no radius) if startRadius is beyond that + const int nRadii = static_cast(std::floor((maxRadius - startRadius) / radiusStep + 0.5)) + 1; + for (int iRadius = 0; iRadius < nRadii; ++iRadius) { + const double radius = startRadius + iRadius * radiusStep; + const double daughterArgument = daughterPhase * radius; + const double triggerArgument = triggerPhase * radius; + if (std::abs(daughterArgument) > 1.0 || std::abs(triggerArgument) > 1.0) { + continue; + } + const double signedDeltaPhiStar = RecoDecay::constrainAngle(deltaPhi + std::asin(daughterArgument) - std::asin(triggerArgument), -PI); + const float absDeltaPhiStar = std::abs(signedDeltaPhiStar); + if (absDeltaPhiStar < result.minAbs) { + result.valid = true; + result.minAbs = absDeltaPhiStar; + result.signedAtMin = signedDeltaPhiStar; + result.radiusAtMin = radius; + } + } + return result; + } + /// Function to load zorro /// \param bc provided such that the run number + timestamp can be used void initZorro(aod::BCsWithTimestamps::iterator const& bc) @@ -414,90 +1076,99 @@ struct HStrangeCorrelation { LOG(info) << "Loading efficiencies from CCDB for run " << mRunNumber << " now..."; auto timeStamp = bc.timestamp(); - TList* listEfficiencies = ccdb->getForTimeStamp(efficiencyCCDBPath, timeStamp); + auto* listEfficiencies = ccdb->getForTimeStamp(efficiencyCCDBPath, timeStamp); if (!listEfficiencies) { LOG(fatal) << "Problem getting TList object with efficiencies!"; } - hEfficiencyTrigger = static_cast(listEfficiencies->FindObject("hEfficiencyTrigger")); - hEfficiencyTriggerMult = static_cast(listEfficiencies->FindObject("hEfficiencyTriggerMult")); - hEfficiencyTriggerMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyTriggerMultVsPhi")); - hEfficiencyK0Short = static_cast(listEfficiencies->FindObject("hEfficiencyK0Short")); - hEfficiencyK0ShortMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyK0ShortMultVsPhi")); - hEfficiencyLambda = static_cast(listEfficiencies->FindObject("hEfficiencyLambda")); - hEfficiencyLambdaMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyLambdaMultVsPhi")); - hEfficiencyAntiLambda = static_cast(listEfficiencies->FindObject("hEfficiencyAntiLambda")); - hEfficiencyAntiLambdaMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyAntiLambdaMultVsPhi")); - hEfficiencyXiMinus = static_cast(listEfficiencies->FindObject("hEfficiencyXiMinus")); - hEfficiencyXiMinusMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyXiMinusMultVsPhi")); - hEfficiencyXiPlus = static_cast(listEfficiencies->FindObject("hEfficiencyXiPlus")); - hEfficiencyXiPlusMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyXiPlusMultVsPhi")); - hEfficiencyOmegaMinus = static_cast(listEfficiencies->FindObject("hEfficiencyOmegaMinus")); - hEfficiencyOmegaMinusMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyOmegaMinusMultVsPhi")); - hEfficiencyOmegaPlus = static_cast(listEfficiencies->FindObject("hEfficiencyOmegaPlus")); - hEfficiencyOmegaPlusMultVsPhi = static_cast(listEfficiencies->FindObject("hEfficiencyOmegaPlusMultVsPhi")); - hEfficiencyHadron = static_cast(listEfficiencies->FindObject("hEfficiencyHadron")); - hEfficiencyHadronMult = static_cast(listEfficiencies->FindObject("hEfficiencyHadronMult")); - hEfficiencyPion = static_cast(listEfficiencies->FindObject("hEfficiencyPion")); - hPurityHadron = static_cast(listEfficiencies->FindObject("hPurityHadron")); - hPurityHadronMult = static_cast(listEfficiencies->FindObject("hPurityHadronMult")); - hEfficiencyUncertaintyTrigger = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTrigger")); - hEfficiencyUncertaintyTriggerMult = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTriggerMult")); - hEfficiencyUncertaintyK0Short = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyK0Short")); - hEfficiencyUncertaintyLambda = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyLambda")); - hEfficiencyUncertaintyAntiLambda = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyAntiLambda")); - hEfficiencyUncertaintyXiMinus = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyXiMinus")); - hEfficiencyUncertaintyXiPlus = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyXiPlus")); - hEfficiencyUncertaintyOmegaMinus = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyOmegaMinus")); - hEfficiencyUncertaintyOmegaPlus = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyOmegaPlus")); - hEfficiencyUncertaintyPion = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyPion")); - hEfficiencyUncertaintyHadron = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadron")); - hEfficiencyUncertaintyHadronMult = static_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadronMult")); - hPurityUncertaintyHadron = static_cast(listEfficiencies->FindObject("hPurityUncertaintyHadron")); - hPurityUncertaintyHadronMult = static_cast(listEfficiencies->FindObject("hPurityUncertaintyHadronMult")); - if (efficiencyFlags.applyEfficiencyPropagation && !efficiencyFlags.applyEffAsFunctionOfMultAndPhi && !hEfficiencyUncertaintyTrigger) + hEfficiencyTrigger = dynamic_cast(listEfficiencies->FindObject("hEfficiencyTrigger")); + hEfficiencyTriggerMult = dynamic_cast(listEfficiencies->FindObject("hEfficiencyTriggerMult")); + hEfficiencyTriggerMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyTriggerMultVsPhi")); + hEfficiencyK0Short = dynamic_cast(listEfficiencies->FindObject("hEfficiencyK0Short")); + hEfficiencyK0ShortMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyK0ShortMultVsPhi")); + hEfficiencyLambda = dynamic_cast(listEfficiencies->FindObject("hEfficiencyLambda")); + hEfficiencyLambdaMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyLambdaMultVsPhi")); + hEfficiencyAntiLambda = dynamic_cast(listEfficiencies->FindObject("hEfficiencyAntiLambda")); + hEfficiencyAntiLambdaMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyAntiLambdaMultVsPhi")); + hEfficiencyXiMinus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyXiMinus")); + hEfficiencyXiMinusMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyXiMinusMultVsPhi")); + hEfficiencyXiPlus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyXiPlus")); + hEfficiencyXiPlusMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyXiPlusMultVsPhi")); + hEfficiencyOmegaMinus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyOmegaMinus")); + hEfficiencyOmegaMinusMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyOmegaMinusMultVsPhi")); + hEfficiencyOmegaPlus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyOmegaPlus")); + hEfficiencyOmegaPlusMultVsPhi = dynamic_cast*>(listEfficiencies->FindObject("hEfficiencyOmegaPlusMultVsPhi")); + hEfficiencyHadron = dynamic_cast(listEfficiencies->FindObject("hEfficiencyHadron")); + hEfficiencyHadronMult = dynamic_cast(listEfficiencies->FindObject("hEfficiencyHadronMult")); + hEfficiencyPion = dynamic_cast(listEfficiencies->FindObject("hEfficiencyPion")); + hPurityHadron = dynamic_cast(listEfficiencies->FindObject("hPurityHadron")); + hPurityHadronMult = dynamic_cast(listEfficiencies->FindObject("hPurityHadronMult")); + hEfficiencyUncertaintyTrigger = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTrigger")); + hEfficiencyUncertaintyTriggerMult = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyTriggerMult")); + hEfficiencyUncertaintyK0Short = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyK0Short")); + hEfficiencyUncertaintyLambda = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyLambda")); + hEfficiencyUncertaintyAntiLambda = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyAntiLambda")); + hEfficiencyUncertaintyXiMinus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyXiMinus")); + hEfficiencyUncertaintyXiPlus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyXiPlus")); + hEfficiencyUncertaintyOmegaMinus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyOmegaMinus")); + hEfficiencyUncertaintyOmegaPlus = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyOmegaPlus")); + hEfficiencyUncertaintyPion = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyPion")); + hEfficiencyUncertaintyHadron = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadron")); + hEfficiencyUncertaintyHadronMult = dynamic_cast(listEfficiencies->FindObject("hEfficiencyUncertaintyHadronMult")); + hPurityUncertaintyHadron = dynamic_cast(listEfficiencies->FindObject("hPurityUncertaintyHadron")); + hPurityUncertaintyHadronMult = dynamic_cast(listEfficiencies->FindObject("hPurityUncertaintyHadronMult")); + if (efficiencyFlags.applyEfficiencyPropagation && !efficiencyFlags.applyEffAsFunctionOfMultAndPhi && !hEfficiencyUncertaintyTrigger) { LOG(fatal) << "Problem getting hEfficiencyUncertaintyTrigger!"; - if (efficiencyFlags.applyEffAsFunctionOfMult && !hEfficiencyTriggerMult) + } + if (efficiencyFlags.applyEffAsFunctionOfMult && !hEfficiencyTriggerMult) { LOG(fatal) << "Problem getting hEfficiencyTriggerMult!"; + } LOG(info) << "Efficiencies now loaded for " << mRunNumber; } template - uint64_t v0selectionBitmap(TV0 v0, float pvx, float pvy, float pvz) + uint64_t v0selectionBitmap(TV0 const& v0, float pvx, float pvy, float pvz) // precalculate this information so that a check is one mask operation, not many { uint64_t bitMap = 0; // proper lifetime , DCA daughter to prim.vtx if (masterConfigurations.doCorrelationK0Short) { // proper lifetime - if (v0.distovertotmom(pvx, pvy, pvz) * o2::constants::physics::MassK0Short < v0Selection.lifetimecutK0S) + if (v0.distovertotmom(pvx, pvy, pvz) * o2::constants::physics::MassK0Short < v0Selection.lifetimecutK0S) { SETBIT(bitMap, 0); + } // DCA daughter to prim.vtx and armenteros - if (std::abs(v0.dcapostopv()) > v0Selection.dcaMesonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaMesonToPV && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha())) + float dcaCut = v0Selection.dcaDaugToPVForK0s == 0 ? v0Selection.dcaMesonToPV : v0Selection.dcaDaugToPVForK0s; + if (std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha())) { SETBIT(bitMap, 3); + } } if (masterConfigurations.doCorrelationLambda) { // proper lifetime - if (v0.distovertotmom(pvx, pvy, pvz) * o2::constants::physics::MassLambda0 < v0Selection.lifetimecutLambda) + if (v0.distovertotmom(pvx, pvy, pvz) * o2::constants::physics::MassLambda0 < v0Selection.lifetimecutLambda) { SETBIT(bitMap, 1); + } // DCA daughter to prim.vtx - if (std::abs(v0.dcapostopv()) > v0Selection.dcaBaryonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaMesonToPV) + if (std::abs(v0.dcapostopv()) > v0Selection.dcaBaryonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaMesonToPV) { SETBIT(bitMap, 4); + } } if (masterConfigurations.doCorrelationAntiLambda) { // proper lifetime - if (v0.distovertotmom(pvx, pvy, pvz) * o2::constants::physics::MassLambda0 < v0Selection.lifetimecutLambda) + if (v0.distovertotmom(pvx, pvy, pvz) * o2::constants::physics::MassLambda0 < v0Selection.lifetimecutLambda) { SETBIT(bitMap, 2); + } // DCA daughter to prim.vtx - if (std::abs(v0.dcapostopv()) > v0Selection.dcaMesonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaBaryonToPV) + if (std::abs(v0.dcapostopv()) > v0Selection.dcaMesonToPV && std::abs(v0.dcanegtopv()) > v0Selection.dcaBaryonToPV) { SETBIT(bitMap, 5); + } } return bitMap; } template - uint64_t cascadeselectionBitmap(TCascade casc, float pvx, float pvy, float pvz) + uint64_t cascadeselectionBitmap(TCascade const& casc, float pvx, float pvy, float pvz) { uint64_t bitMap = 0; float cascpos = std::hypot(casc.x() - pvx, casc.y() - pvy, casc.z() - pvz); @@ -512,64 +1183,80 @@ struct HStrangeCorrelation { // TPC PID and DCA daughter to prim.vtx and comopeting casc.rej and life time if (masterConfigurations.doCorrelationXiMinus) { // DCA daughter to prim.vtx - if (isGoodDCANegCasc) + if (isGoodDCANegCasc) { SETBIT(bitMap, 0); + } // comopeting casc.rej - if (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) > cascadeSelections.rejcomp) + if (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) > cascadeSelections.rejcomp) { SETBIT(bitMap, 4); - if (ctauXi < cascadeSelections.proplifetime) + } + if (ctauXi < cascadeSelections.proplifetime) { SETBIT(bitMap, 8); + } // y cut - if (std::abs(casc.yXi()) < cascadeSelections.rapCut) + if (std::abs(casc.yXi()) < cascadeSelections.rapCut) { SETBIT(bitMap, 12); + } } if (masterConfigurations.doCorrelationXiPlus) { // DCA daughter to prim.vtx - if (isGoodDCAPosCasc) + if (isGoodDCAPosCasc) { SETBIT(bitMap, 1); + } // comopeting casc.rej - if (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) > cascadeSelections.rejcomp) + if (std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) > cascadeSelections.rejcomp) { SETBIT(bitMap, 5); + } // life time - if (ctauXi < cascadeSelections.proplifetime) + if (ctauXi < cascadeSelections.proplifetime) { SETBIT(bitMap, 9); + } // y cut - if (std::abs(casc.yXi()) > cascadeSelections.rapCut) + if (std::abs(casc.yXi()) > cascadeSelections.rapCut) { SETBIT(bitMap, 13); + } } if (masterConfigurations.doCorrelationOmegaMinus) { // DCA daughter to prim.vtx - if (isGoodDCANegCasc) + if (isGoodDCANegCasc) { SETBIT(bitMap, 2); + } // comopeting casc.rej - if (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) > cascadeSelections.rejcomp) + if (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) > cascadeSelections.rejcomp) { SETBIT(bitMap, 6); + } // life time - if (ctauOmega < cascadeSelections.proplifetime) + if (ctauOmega < cascadeSelections.proplifetime) { SETBIT(bitMap, 10); + } // y cut - if (std::abs(casc.yOmega()) < cascadeSelections.rapCut) + if (std::abs(casc.yOmega()) < cascadeSelections.rapCut) { SETBIT(bitMap, 14); + } } if (masterConfigurations.doCorrelationOmegaPlus) { // DCA daughter to prim.vtx - if (isGoodDCAPosCasc) + if (isGoodDCAPosCasc) { SETBIT(bitMap, 3); + } // comopeting casc.rej - if (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) > cascadeSelections.rejcomp) + if (std::abs(casc.mXi() - o2::constants::physics::MassXiMinus) > cascadeSelections.rejcomp) { SETBIT(bitMap, 7); + } // life time - if (ctauOmega < cascadeSelections.proplifetime) + if (ctauOmega < cascadeSelections.proplifetime) { SETBIT(bitMap, 11); + } // y cut - if (std::abs(casc.yOmega()) > cascadeSelections.rapCut) + if (std::abs(casc.yOmega()) > cascadeSelections.rapCut) { SETBIT(bitMap, 15); + } } return bitMap; } template - bool isValidTrigger(TTrack track, bool isLeading) + bool isValidTrigger(TTrack const& track, bool isLeading) { if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsTrigger) { return false; // crossed rows @@ -587,6 +1274,10 @@ struct HStrangeCorrelation { if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstant + trackSelection.dcaXYpTdep * std::abs(track.signed1Pt())) { return false; } + // systematic variations: trigger DCAz + if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstant + trackSelection.dcaZpTdep * std::abs(track.signed1Pt())) { + return false; + } if (track.pt() > axisRanges[3][1] || track.pt() < axisRanges[3][0]) { return false; } @@ -599,10 +1290,13 @@ struct HStrangeCorrelation { if (useTheLeadingParticleAsTrigger && !isLeading) { return false; } + if (trackSelection.doITSChi2Selection && track.itsChi2NCl() > trackSelection.chi2ITSfit) { + return false; + } return true; } template - bool isValidAssocHadron(TTrack track) + bool isValidAssocHadron(TTrack const& track) { if (track.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { return false; // crossed rows @@ -620,71 +1314,93 @@ struct HStrangeCorrelation { if (std::abs(track.dcaXY()) > trackSelection.dcaXYconstantAssoc + trackSelection.dcaXYpTdepAssoc * std::abs(track.signed1Pt())) { return false; } + // systematic variations: trigger DCAz + if (trackSelection.requireDCAzCut && std::abs(track.dcaZ()) > trackSelection.dcaZconstantAssoc + trackSelection.dcaZpTdepAssoc * std::abs(track.signed1Pt())) { + return false; + } if (track.pt() > axisRanges[2][1] || track.pt() < axisRanges[2][0]) { return false; } + if (trackSelection.doITSChi2Selection && track.itsChi2NCl() > trackSelection.chi2ITSfit) { + return false; + } return true; } // V0selection in PbPb template - bool v0SelectedPbPb(TV0 v0) + bool v0SelectedPbPb(TV0 const& v0) { // v0radius - if (v0.v0radius() < v0Selection.v0RadiusMin) + if (v0.v0radius() < v0Selection.v0RadiusMin) { return false; - if (v0.v0radius() > v0Selection.v0RadiusMax) + } + if (v0.v0radius() > v0Selection.v0RadiusMax) { return false; + } // v0cosPA - if (v0.v0cosPA() < v0Selection.v0cospa) + if (v0.v0cosPA() < v0Selection.v0cospa) { return false; + } // dcaV0daughters - if (v0.dcaV0daughters() > v0Selection.dcaV0dau) + if (v0.dcaV0daughters() > v0Selection.dcaV0dau) { return false; + } return true; } // cascadeselection in PbPb template - bool cascadeSelectedPbPb(TCascade casc, float pvx, float pvy, float pvz) + bool cascadeSelectedPbPb(TCascade const& casc, float pvx, float pvy, float pvz) { // bachBaryonCosPA - if (casc.bachBaryonCosPA() < cascadeSelections.bachBaryonCosPA) + if (casc.bachBaryonCosPA() < cascadeSelections.bachBaryonCosPA) { return false; + } // bachBaryonDCAxyToPV - if (std::abs(casc.bachBaryonDCAxyToPV()) > cascadeSelections.bachBaryonDCAxyToPV) + if (std::abs(casc.bachBaryonDCAxyToPV()) > cascadeSelections.bachBaryonDCAxyToPV) { return false; + } // casccosPA - if (casc.casccosPA(pvx, pvy, pvz) < cascadeSelections.cascCospa) + if (casc.casccosPA(pvx, pvy, pvz) < cascadeSelections.cascCospa) { return false; + } // dcacascdaughters float ptDepCut = cascadeSelections.dcaCacsDauPar0; - if (casc.pt() > cascadeSelections.lowPtForCascDaugPtDep && casc.pt() < cascadeSelections.highPtForCascDaugPtDep) + if (casc.pt() > cascadeSelections.lowPtForCascDaugPtDep && casc.pt() < cascadeSelections.highPtForCascDaugPtDep) { ptDepCut = cascadeSelections.dcaCacsDauPar1; - else if (casc.pt() > cascadeSelections.highPtForCascDaugPtDep) + } else if (casc.pt() > cascadeSelections.highPtForCascDaugPtDep) { ptDepCut = cascadeSelections.dcaCacsDauPar2; - if (casc.dcacascdaughters() > ptDepCut) + } + if (casc.dcacascdaughters() > ptDepCut) { return false; + } // dcaV0daughters - if (casc.dcaV0daughters() > cascadeSelections.cascdcaV0dau) + if (casc.dcaV0daughters() > cascadeSelections.cascdcaV0dau) { return false; + } // dcav0topv - if (std::abs(casc.dcav0topv(pvx, pvy, pvz)) < cascadeSelections.cascdcaV0ToPV) + if (std::abs(casc.dcav0topv(pvx, pvy, pvz)) < cascadeSelections.cascdcaV0ToPV) { return false; + } // cascradius - if (casc.cascradius() < cascadeSelections.cascRadius) + if (casc.cascradius() < cascadeSelections.cascRadius) { return false; + } // v0radius - if (casc.v0radius() < cascadeSelections.cascv0RadiusMin) + if (casc.v0radius() < cascadeSelections.cascv0RadiusMin) { return false; + } // v0cosPA - if (casc.v0cosPA(casc.x(), casc.y(), casc.z()) < cascadeSelections.cascv0cospa) + if (casc.v0cosPA(pvx, pvy, pvz) < cascadeSelections.cascv0cospa) { return false; + } // lambdaMassWin - if (std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascadeSelections.cascV0masswindow) + if (std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascadeSelections.cascV0masswindow) { return false; + } return true; } - double calculateAverageDeltaPhiStar(double* trigg, double* assoc, double B) + double calculateAverageDeltaPhiStar(std::array const& trigg, std::array const& assoc, double B) { double dPhiStar = 0; double dPhiStarMean = 0; @@ -693,14 +1409,18 @@ struct HStrangeCorrelation { double phaseProton = (-0.3 * B * assoc[2]) / (2 * assoc[1]); double phaseTrack = (-0.3 * B * trigg[2]) / (2 * trigg[1]); - for (double r = MinRadiusTPC; r <= MaxRadiusTPC; r += 0.05) { + // radii MinRadiusTPC + i * RadiusStep below MaxRadiusTPC: 34 radii, 0.80 to 2.45 m + constexpr double RadiusStep = 0.05; + constexpr int NRadii = static_cast((MaxRadiusTPC - MinRadiusTPC) / RadiusStep + 0.5); + for (int iRadius = 0; iRadius < NRadii; ++iRadius) { + const double r = MinRadiusTPC + iRadius * RadiusStep; dPhiStar = dPhi + std::asin(phaseProton * r) - std::asin(phaseTrack * r); - dPhiStarMean += (dPhiStar / 34); + dPhiStarMean += (dPhiStar / NRadii); } return dPhiStarMean; } - void fillTriggerHistogram(std::shared_ptr hist, double pt, double mult, float eff, float effUncert, float purity, float purityErr) + void fillTriggerHistogram(std::shared_ptr const& hist, double pt, double mult, float eff, float effUncert, float purity, float purityErr) { int binx = hist->GetXaxis()->FindBin(pt); int biny = hist->GetYaxis()->FindBin(mult); @@ -711,10 +1431,11 @@ struct HStrangeCorrelation { hist->SetBinContent(binx, biny, newContent); hist->SetBinError(binx, biny, newUncert); } - void fillCorrelationHistogram(std::shared_ptr hist, double binFillThn[], float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) + template + void fillCorrelationHistogram(std::shared_ptr const& hist, std::array const& binFillThn, float etaWeight, float efficiency, float totalEffUncert, float purity, float totalPurityUncert) { - float previousContent, previousError2, currentContent, currentError2; - int bin = hist->GetBin(binFillThn); + float previousContent = 0.0f, previousError2 = 0.0f, currentContent = 0.0f, currentError2 = 0.0f; + int bin = hist->GetBin(binFillThn.data()); previousContent = hist->GetBinContent(bin); previousError2 = hist->GetBinError2(bin); currentContent = previousContent + etaWeight * purity / (efficiency); @@ -722,47 +1443,49 @@ struct HStrangeCorrelation { hist->SetBinContent(bin, currentContent); hist->SetBinError2(bin, currentError2); } + // Rec coordinates for the PairLossK0/Comparison/NchGen histograms: the vertex-z + // and multiplicity coordinates are replaced by the Nch^gen of the current + // PairLoss MC collision + static std::array toPairLossNchGenCoordinates(std::array const& binFillThn) + { + return {binFillThn[0], binFillThn[1], binFillThn[2], binFillThn[3], static_cast(pairLossComparison.nChGen)}; + } void fillCorrelationsV0(aod::TriggerTracks const& triggers, aod::AssocV0s const& assocs, bool mixing, bool mixingInBf, float pvx, float pvy, float pvz, float mult, double bField) { ValidCollision currentCollision; - int binMult = 0; - int nBinsMult = 0; - int nBinsVtxZ = 0; - int binVtxZ = 0; - currentCollision.pvz = pvz; - currentCollision.mult = mult; - if (mixingInBf) { - nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); - binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; - nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); - binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; - } bool firstLoop = false; for (auto const& triggerTrack : triggers) { - if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { continue; + } auto trigg = triggerTrack.track_as(); - if (!isValidTrigger(trigg, triggerTrack.isLeading())) + if (!isValidTrigger(trigg, triggerTrack.isLeading())) { continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { + continue; + } float efficiencyTrigg = 1.0f; float efficiencyTriggError = 0.0f; float purityTrigg = 1.0f; float purityTriggErr = 0.0; - double bintrig[4] = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; + std::array bintrig = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; if (efficiencyFlags.applyEfficiencyForTrigger) { if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { efficiencyTrigg = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); - if (efficiencyFlags.applyPurityTrigger) + if (efficiencyFlags.applyPurityTrigger) { purityTrigg = hPurityHadron->Interpolate(trigg.pt()); + } if (efficiencyFlags.applyEfficiencyPropagation) { efficiencyTriggError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); - if (efficiencyFlags.applyPurityTrigger) + if (efficiencyFlags.applyPurityTrigger) { purityTriggErr = hPurityHadron->Interpolate(trigg.pt()); + } } } else { - efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig.data())); if (efficiencyFlags.applyEfficiencyPropagation) { - efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig.data())); } } if (efficiencyTrigg == 0) { // check for zero efficiency, do not apply if the case @@ -770,18 +1493,64 @@ struct HStrangeCorrelation { efficiencyTriggError = 0; } } + // PairLossTriggerMatchQABin of this trigger, set only while the PairLoss Rec + // comparison fills its variants, and reused for every pair of the trigger + int pairLossTriggerMatch = PairLossTriggerMatchAll; if (!mixing) { fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesV0")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + if (pairLossComparison.active && pairLossComparison.nChGen >= 0) { + fillTriggerHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/TriggerRec")), trigg.pt(), pairLossComparison.nChGen, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); + } + + // Trigger-side control chain, filled from the exact Rec trigger loop so + // that it is the same denominator the ordinary Rec correlation uses. + // Each pair variant needs the matching trigger variant, otherwise only + // shapes and not per-trigger amplitudes can be compared. Not booked with + // applyINELSelectionAtGenLevel (pairLossComparison.nChGen >= 0). + if (pairLossComparison.active && pairLossComparison.nChGen < 0) { + const double triggerWeight = purityTrigg / efficiencyTrigg; + auto fillTriggerVariant = [&](int variant, double fillPtTrigger, double fillVtxZ, double fillMult, double weight) { + histos.fill(HIST("PairLossK0/Comparison/TriggerVariants"), variant, fillPtTrigger, fillVtxZ, fillMult, weight); + }; + fillTriggerVariant(0, trigg.pt(), pvz, mult, triggerWeight); + pairLossTriggerMatch = pairLossTriggerMatchBin(trigg.globalIndex()); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger"), PairLossTriggerMatchAll, trigg.pt(), triggerWeight); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger"), pairLossTriggerMatch, trigg.pt(), triggerWeight); + const auto triggerLadderMcIt = pairLossComparison.trackToMc.find(trigg.globalIndex()); + if (triggerLadderMcIt != pairLossComparison.trackToMc.end()) { + const auto triggerLadderTruthIt = pairLossComparison.truthTriggers.find(triggerLadderMcIt->second); + if (triggerLadderTruthIt != pairLossComparison.truthTriggers.end()) { + auto const& ladderTruthTrigger = triggerLadderTruthIt->second; + fillTriggerVariant(1, trigg.pt(), pvz, mult, triggerWeight); + if (pairLossComparison.primaryTruthTriggerIds.contains(ladderTruthTrigger.globalIndex)) { + fillTriggerVariant(2, trigg.pt(), pvz, mult, triggerWeight); + if (pairLossComparison.oldTruthTriggerIds.contains(ladderTruthTrigger.globalIndex)) { + fillTriggerVariant(3, trigg.pt(), pvz, mult, triggerWeight); + if (static_cast(triggerTrack.collisionId()) == pairLossComparison.bestCollisionId) { + fillTriggerVariant(4, trigg.pt(), pvz, mult, triggerWeight); + fillTriggerVariant(5, ladderTruthTrigger.pt, pvz, mult, triggerWeight); + fillTriggerVariant(6, ladderTruthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity, triggerWeight); + fillTriggerVariant(7, ladderTruthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity, 1.0); + if (pairLossComparison.countedTriggers.insert(ladderTruthTrigger.globalIndex).second) { + fillTriggerVariant(8, ladderTruthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity, 1.0); + } + } + } + } + } + } + } } double triggSign = trigg.sign(); - double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + std::array triggForDeltaPhiStar = {trigg.phi(), trigg.pt(), triggSign}; if (mixingInBf) { currentCollision.addValidParticle(trigg.eta(), trigg.phi(), trigg.pt(), -1, efficiencyTrigg, efficiencyTriggError, -1); - if (firstLoop) + if (firstLoop) { continue; + } } for (auto const& assocCandidate : assocs) { firstLoop = true; @@ -790,54 +1559,63 @@ struct HStrangeCorrelation { //---] syst cuts [--- if ((masterConfigurations.doPPAnalysis && (assoc.v0radius() < v0Selection.v0RadiusMin || assoc.v0radius() > v0Selection.v0RadiusMax || std::abs(assoc.dcapostopv()) < v0Selection.dcapostopv || std::abs(assoc.dcanegtopv()) < v0Selection.dcanegtopv || - assoc.v0cosPA() < v0Selection.v0cospa || assoc.dcaV0daughters() > v0Selection.dcaV0dau))) + assoc.v0cosPA() < v0Selection.v0cospa || assoc.dcaV0daughters() > v0Selection.dcaV0dau))) { continue; + } - if (!masterConfigurations.doPPAnalysis && !v0SelectedPbPb(assoc)) + if (!masterConfigurations.doPPAnalysis && !v0SelectedPbPb(assoc)) { continue; + } uint64_t selMap = v0selectionBitmap(assoc, pvx, pvy, pvz); //---] removing autocorrelations [--- auto postrack = assoc.posTrack_as(); auto negtrack = assoc.negTrack_as(); - if (!mixingInBf) { - if (doAutocorrelationRejection) { - if (trigg.globalIndex() == postrack.globalIndex()) { - histos.fill(HIST("hNumberOfRejectedPairsV0"), 0.5); - continue; - } - if (trigg.globalIndex() == negtrack.globalIndex()) { - histos.fill(HIST("hNumberOfRejectedPairsV0"), 0.5); - continue; - } + if (doAutocorrelationRejection) { + if (trigg.globalIndex() == postrack.globalIndex()) { + histos.fill(HIST("hNumberOfRejectedPairsV0"), 0.5); + continue; + } + if (trigg.globalIndex() == negtrack.globalIndex()) { + histos.fill(HIST("hNumberOfRejectedPairsV0"), 0.5); + continue; } } //---] track quality check [--- - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) + if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; - if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated)) + } + if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { continue; - if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) + } + if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) { continue; + } float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); float deltaeta = trigg.eta() - assoc.eta(); + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } float ptassoc = assoc.pt(); float pttrigger = trigg.pt(); // skip if basic ranges not met - if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) { continue; - if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + } + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) { continue; - if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + } + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) { continue; + } - TH2F* hEfficiencyV0[3]; - TH2F* hEfficiencyUncertaintyV0[3]; - THnF* hEfficiencyV0MultVsPhi[3]; + std::array hEfficiencyV0{nullptr, nullptr, nullptr}; + std::array hEfficiencyUncertaintyV0{nullptr, nullptr, nullptr}; + std::array hEfficiencyV0MultVsPhi{nullptr, nullptr, nullptr}; if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { hEfficiencyV0MultVsPhi[0] = hEfficiencyK0ShortMultVsPhi; hEfficiencyV0MultVsPhi[1] = hEfficiencyLambdaMultVsPhi; @@ -865,30 +1643,43 @@ struct HStrangeCorrelation { double ptProton = postrack.pt(); double ptPion = negtrack.pt(); double signProton = postrack.sign(); + double signPion = negtrack.sign(); if (assocCandidate.compatible(2, trackSelection.dEdxCompatibility)) { phiProton = negtrack.phi(); etaProton = negtrack.eta(); ptProton = negtrack.pt(); signProton = negtrack.sign(); + phiPion = postrack.phi(); + etaPion = postrack.eta(); + ptPion = postrack.pt(); + signPion = postrack.sign(); } - double assocForDeltaPhiStar[] = {phiProton, ptProton, signProton}; - double assocForDeltaPhiStarPion[] = {phiPion, ptPion, -1}; + std::array assocForDeltaPhiStar = {phiProton, ptProton, signProton}; + std::array assocForDeltaPhiStarPion = {phiPion, ptPion, signPion}; static_for<0, 2>([&](auto i) { constexpr int Index = i.value; float efficiency = 1.0f; float totalEffUncert = 0.0; float efficiencyError = 0.0f; + if (masterConfigurations.performPLambdacorrelation == 0 && ((Index == 1 && triggSign == 1) || (Index == 2 && triggSign == -1))) { + return; + } + if (masterConfigurations.performPLambdacorrelation == 1 && ((Index == 1 && triggSign == -1) || (Index == 2 && triggSign == 1))) { + return; + } if (efficiencyFlags.applyEfficiencyCorrection) { if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { - double bin[4] = {ptassoc, assoc.eta(), assoc.phi(), mult}; - efficiency = hEfficiencyV0MultVsPhi[Index]->GetBinContent(hEfficiencyV0MultVsPhi[Index]->GetBin(bin)); - if (efficiencyFlags.applyEfficiencyPropagation) - efficiencyError = hEfficiencyV0MultVsPhi[Index]->GetBinError(hEfficiencyV0MultVsPhi[Index]->GetBin(bin)); + std::array bin = {ptassoc, assoc.eta(), assoc.phi(), mult}; + efficiency = hEfficiencyV0MultVsPhi[Index]->GetBinContent(hEfficiencyV0MultVsPhi[Index]->GetBin(bin.data())); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyError = hEfficiencyV0MultVsPhi[Index]->GetBinError(hEfficiencyV0MultVsPhi[Index]->GetBin(bin.data())); + } } else { efficiency = hEfficiencyV0[Index]->Interpolate(ptassoc, assoc.eta()); - if (efficiencyFlags.applyEfficiencyPropagation) + if (efficiencyFlags.applyEfficiencyPropagation) { efficiencyError = hEfficiencyUncertaintyV0[Index]->Interpolate(ptassoc, assoc.eta()); + } } } if (efficiency == 0) { // check for zero efficiency, do not apply if the case @@ -898,10 +1689,41 @@ struct HStrangeCorrelation { if (efficiencyFlags.applyEfficiencyPropagation) { totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyError, 2) + std::pow(efficiencyTriggError * efficiency, 2)); } - double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + + std::array binFillThnOppositeSignDaugher = {computeDeltaPhi(trigg.phi(), phiPion), trigg.eta() - etaPion, ptPion, pttrigger, pvz, mult}; + std::array binFillThnSameSignDaugher = {computeDeltaPhi(trigg.phi(), phiProton), trigg.eta() - etaProton, ptProton, pttrigger, pvz, mult}; + if ((triggSign < Neutral && !assocCandidate.compatible(2, trackSelection.dEdxCompatibility)) || (triggSign > Neutral && assocCandidate.compatible(2, trackSelection.dEdxCompatibility))) { + std::swap(binFillThnOppositeSignDaugher[1], binFillThnSameSignDaugher[1]); + std::swap(binFillThnOppositeSignDaugher[0], binFillThnSameSignDaugher[0]); + std::swap(binFillThnOppositeSignDaugher[2], binFillThnSameSignDaugher[2]); + } + std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; if (TESTBIT(doCorrelation, Index) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0) && (masterConfigurations.doPPAnalysis || (TESTBIT(selMap, Index) && TESTBIT(selMap, Index + 3)))) { - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && + ((-massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) || + (-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) || + (+massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma))) { + if (std::abs(deltaphi) < 0.5) { + histos.fill(HIST("sameEvent/InvariantMass/") + HIST(V0names[Index]) + HIST("/hNearSide"), ptassoc, pttrigger, getV0InvariantMass(assoc)); + } + if (std::abs(PI - deltaphi) < 0.5) { + histos.fill(HIST("sameEvent/InvariantMass/") + HIST(V0names[Index]) + HIST("/hAwaySide"), ptassoc, pttrigger, getV0InvariantMass(assoc)); + } + if (deltaphi > 1.0 && deltaphi < 1.5) { + histos.fill(HIST("sameEvent/InvariantMass/") + HIST(V0names[Index]) + HIST("/hUnderlyingEvent"), ptassoc, pttrigger, getV0InvariantMass(assoc)); + } + } + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/") + HIST(V0names[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (pairLossComparison.active && Index == IndexK0) { + // Exact Rec left sideband, with the same reconstructed + // selections, coordinates and weight as the ordinary Rec path. + if (pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecLeftBg")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } else { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecLeftBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); double deltaPhiStarPion = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPion, bField); @@ -918,8 +1740,157 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && ((-massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma))) { + if (masterConfigurations.doCorrelationsHadronV0daughter) { + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(V0names[Index]) + HIST("_hSameSign")), binFillThnSameSignDaugher, 1, 1, 1, 1, 1); + fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(V0names[Index]) + HIST("_hOppositeSign")), binFillThnOppositeSignDaugher, 1, 1, 1, 1, 1); + } fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(V0names[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + + // PairLoss Rec control study. Stage 0 is filled from this + // exact Rec signal branch, so it is not a hand-written + // approximation of the reconstructed pair selection. Each next + // stage adds exactly one cumulative condition. With + // applyINELSelectionAtGenLevel (pairLossComparison.nChGen >= 0) + // only the Nch^gen-binned Rec is filled; fake K0 = Rec - RecPeakTrueK0. + if (pairLossComparison.active && Index == IndexK0 && pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/Rec")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (assocCandidate.mcTrue(IndexK0)) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecPeakTrueK0")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } else if (pairLossComparison.active && Index == IndexK0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/Rec")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + // Rec is the complete peak-window sample. Split it into true + // and fake K0 components without changing any reconstructed + // selection, coordinate or weight. This makes the MC-truth + // background removal independently observable. + if (assocCandidate.mcTrue(IndexK0)) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecPeakTrueK0")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } else { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecPeakFakeK0")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + const double pairWeight = etaWeight * purityTrigg / (efficiency * efficiencyTrigg); + auto fillWeightedRecVariant = [&](int variant, double fillDeltaPhi, double fillDeltaEta, double fillPtAssoc, double fillPtTrigger, double fillVtxZ, double fillMult) { + histos.fill(HIST("PairLossK0/Comparison/RecVariants"), variant, fillDeltaPhi, fillDeltaEta, fillPtAssoc, fillPtTrigger, fillVtxZ, fillMult, pairWeight); + }; + + // 0: the exact Rec entry, including reconstructed coordinates + // and the ordinary Rec weight. + fillWeightedRecVariant(0, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair"), PairLossTriggerMatchAll, deltaphi, deltaeta, ptassoc, pttrigger, pairWeight); + histos.fill(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair"), pairLossTriggerMatch, deltaphi, deltaeta, ptassoc, pttrigger, pairWeight); + + const auto triggerMcIdIt = pairLossComparison.trackToMc.find(trigg.globalIndex()); + if (triggerMcIdIt != pairLossComparison.trackToMc.end()) { + const auto truthTriggerIt = pairLossComparison.truthTriggers.find(triggerMcIdIt->second); + if (truthTriggerIt != pairLossComparison.truthTriggers.end()) { + auto const& truthTrigger = truthTriggerIt->second; + + // 1: add only a valid PairLoss truth-trigger match. The + // truth-trigger map already contains its species, charge, + // eta and pT acceptance. Primary and truth-leading choices + // are deliberately kept for later, separate stages. + fillWeightedRecVariant(1, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + if (assocCandidate.mcTrue(IndexK0)) { + // 2: remove only the fake/combinatorial peak component. + // No truth-K0 eta or pT acceptance is imposed yet. + fillWeightedRecVariant(2, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + const auto k0McIdIt = pairLossComparison.v0ToMc.find(assoc.globalIndex()); + if (k0McIdIt != pairLossComparison.v0ToMc.end()) { + const auto truthK0It = pairLossComparison.truthK0s.find(k0McIdIt->second); + if (truthK0It != pairLossComparison.truthK0s.end()) { + auto const& truthK0 = truthK0It->second; + + // 3: additionally require the matched truth K0 to + // satisfy the old PairLoss truth acceptance. + fillWeightedRecVariant(3, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + const bool triggerIsK0Daughter = truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex; + if (!triggerIsK0Daughter) { + // 4: additionally apply the PairLoss truth-level + // trigger-versus-K0-daughter exclusion. + fillWeightedRecVariant(4, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + if (pairLossComparison.primaryTruthTriggerIds.contains(truthTrigger.globalIndex)) { + // 5: additionally impose only the configured truth- + // trigger primary requirement. + fillWeightedRecVariant(5, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + if (pairLossComparison.primaryTruthK0Ids.contains(truthK0.globalIndex)) { + // 6: additionally impose only the configured truth- + // K0 primary requirement. + fillWeightedRecVariant(6, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + if (pairLossComparison.oldTruthTriggerIds.contains(truthTrigger.globalIndex) && + pairLossComparison.oldTruthK0Ids.contains(truthK0.globalIndex)) { + // 7: additionally reproduce the old PairLoss + // truth-leading choice, made after primary cuts. + fillWeightedRecVariant(7, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + if (static_cast(assocCandidate.collisionId()) == pairLossComparison.bestCollisionId) { + // 8: additionally retain only pairs from the + // largest-numContrib reconstructed collision. + fillWeightedRecVariant(8, deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult); + + // 9 changes ONLY the two pT coordinates. Its + // ratio to stage 8 isolates pT-bin migration: + // delta eta/phi and the event coordinates are + // deliberately left reconstructed. + fillWeightedRecVariant(9, deltaphi, deltaeta, truthK0.pt, truthTrigger.pt, pvz, mult); + + // 10 additionally moves the event coordinates + // to the best reconstructed collision, so that + // vertex-z and multiplicity binning is separable + // from pT migration. + fillWeightedRecVariant(10, deltaphi, deltaeta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); + + float truthDeltaEta = truthTrigger.eta - truthK0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + truthDeltaEta = std::abs(truthDeltaEta); + } + const float truthDeltaPhi = computeDeltaPhi(truthTrigger.phi, truthK0.phi); + const bool truthDeltaEtaInRange = truthDeltaEta >= axisRanges[1][0] && truthDeltaEta <= axisRanges[1][1]; + const bool truthDeltaPhiInRange = truthDeltaPhi >= axisRanges[0][0] && truthDeltaPhi <= axisRanges[0][1]; + if (truthDeltaEtaInRange) { + // 11 changes only delta eta and applies its + // truth range. Delta phi stays reconstructed, + // so a step here is delta-eta resolution alone. + fillWeightedRecVariant(11, deltaphi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); + + if (truthDeltaPhiInRange) { + // 12 additionally changes delta phi and + // completes the truth angular range. + fillWeightedRecVariant(12, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); + + // 13 removes only the ordinary Rec pair + // weight. Reconstructed matches remain separate. + histos.fill(HIST("PairLossK0/Comparison/RecVariants"), 13, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); + + const PairLossPairKey truthPairKey{.triggerId = truthTrigger.globalIndex, .k0Id = truthK0.globalIndex}; + if (pairLossComparison.countedPairs.insert(truthPairKey).second) { + // 14 additionally keeps one reconstructed + // match per truth pair. Stage 15 and Final + // are filled by the literal old finalPair. + histos.fill(HIST("PairLossK0/Comparison/RecVariants"), 14, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); + if (!pairLossComparison.finalTruthPairs.contains(truthPairKey)) { + histos.fill(HIST("PairLossK0/Comparison/RecNotInFinal"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } if (std::abs(deltaphi) < checks.towardDeltaEtaRange && doITSClustersQA) { histos.fill(HIST("hITSClusters") + HIST(V0names[Index]) + HIST("NegativeDaughterToward"), ptassoc, negtrack.itsNCls(), assoc.v0radius()); histos.fill(HIST("hITSClusters") + HIST(V0names[Index]) + HIST("PositiveDaughterToward"), ptassoc, postrack.itsNCls(), assoc.v0radius()); @@ -944,8 +1915,17 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/") + HIST(V0names[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + if (pairLossComparison.active && Index == IndexK0) { + // Exact Rec right sideband, with the same reconstructed + // selections, coordinates and weight as the ordinary Rec path. + if (pairLossComparison.nChGen >= 0) { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/NchGen/RecRightBg")), toPairLossNchGenCoordinates(binFillThn), etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } else { + fillCorrelationHistogram(histos.get(HIST("PairLossK0/Comparison/RecRightBg")), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); + } + } if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); double deltaPhiStarPion = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStarPion, bField); @@ -962,23 +1942,27 @@ struct HStrangeCorrelation { } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 0, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/") + HIST(V0names[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { if (mixingInBf) { - currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 1, efficiencyTrigg, efficiencyError, Index); + currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 1, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(V0names[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } + if (masterConfigurations.doCorrelationsHadronV0daughter) { + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(V0names[Index]) + HIST("_hSameSign")), binFillThnSameSignDaugher, 1, 1, 1, 1, 1); + fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(V0names[Index]) + HIST("_hOppositeSign")), binFillThnOppositeSignDaugher, 1, 1, 1, 1, 1); + } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { if (mixingInBf) { - currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 2, efficiencyTrigg, efficiencyError, Index); + currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 2, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/RightBg/") + HIST(V0names[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } @@ -987,23 +1971,25 @@ struct HStrangeCorrelation { }); } } - if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + if (!mixingInBf) { return; - int binnumb = binMult * nBinsVtxZ + binVtxZ; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + // When 'collisionHasTriggOrAssoc' = 0: + // binContent(hMECollisionBins) = Σ(Submitted jobs(done))[binContent(the same bin of hSECollisionBins) * masterConfigurations.mixingParameter - Σ(k=0 to min(masterConfigurations.mixingParameter,binContent)) k] + // When 'collisionHasTriggOrAssoc' = 3 + // More collision loss at higher peripheral centrality (fewer target particles); higher bincontent of HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index]) + // due to avoiding vector occupancy by collisions with no trigger&associated particles + int binnumb = colBinning.getBin({pvz, mult}); int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || (hastirgorassoc == 3 && currentCollision.trigParticles.empty() && currentCollision.assocParticles.empty()) || - (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) + (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) { return; + } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; - // When 'collisionHasTriggOrAssoc' = 0: - // binContent(hMECollisionBins) = Σ(RunNumbers)[binContent(the same bin of hSECollisionBins) * masterConfigurations.mixingParameter - Σ(k=0 to min(masterConfigurations.mixingParameter,binContent)) k] - // When 'collisionHasTriggOrAssoc' = 3 - // More collision loss at higher peripheral centrality (fewer target particles); higher bincontent of HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index]) - // due to avoiding vector occupancy by collisions with no trigger&associated particles - histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + histos.fill(HIST("MixingQA/hMECollisionBins"), binnumb); for (const auto& trigger : collision.trigParticles) { for (const auto& assoc : currentCollision.assocParticles) { float deltaeta = trigger.eta - assoc.eta; @@ -1018,7 +2004,10 @@ struct HStrangeCorrelation { if (efficiencyFlags.applyEfficiencyPropagation) { totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyAssocError, 2) + std::pow(efficiencyTriggError * efficiencyAssoc, 2)); } - double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; static_for<0, 2>([&](auto i) { constexpr int Index = i.value; if (Index == assoc.type && assoc.region == 0) { @@ -1037,63 +2026,61 @@ struct HStrangeCorrelation { if (validCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter)) { validCollisions[binnumb].erase(validCollisions[binnumb].begin()); } - validCollisions[binnumb].push_back(currentCollision); + if (!currentCollision.trigParticles.empty()) { + validCollisions[binnumb].push_back(currentCollision); + } } void fillCorrelationsCascade(aod::TriggerTracks const& triggers, aod::AssocCascades const& assocs, bool mixing, bool mixingInBf, float pvx, float pvy, float pvz, float mult, double bField) { ValidCollision currentCollision; - int nBinsMult = 0; - int binMult = 0; - int nBinsVtxZ = 0; - int binVtxZ = 0; - currentCollision.pvz = pvz; - currentCollision.mult = mult; - if (mixingInBf) { - nBinsMult = histos.get(HIST("axes/hMultAxis"))->GetNbinsX(); - binMult = histos.get(HIST("axes/hMultAxis"))->GetXaxis()->FindBin(mult) - 1; - nBinsVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX(); - binVtxZ = histos.get(HIST("axes/hVertexZAxis"))->GetXaxis()->FindBin(pvz) - 1; - } bool firstLoop = false; for (auto const& triggerTrack : triggers) { - if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { continue; + } auto trigg = triggerTrack.track_as(); - if (!isValidTrigger(trigg, triggerTrack.isLeading())) + if (!isValidTrigger(trigg, triggerTrack.isLeading())) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { continue; + } float efficiencyTrigg = 1.0f; float efficiencyTriggError = 0.0f; float purityTrigg = 1.0f; float purityTriggErr = 0.0f; if (efficiencyFlags.applyEfficiencyForTrigger) { - double bintrig[4] = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; + std::array bintrig = {trigg.pt(), trigg.eta(), trigg.phi(), mult}; if (efficiencyFlags.applyEffAsFunctionOfMult) { efficiencyTrigg = hEfficiencyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); } else if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { - efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); + efficiencyTrigg = hEfficiencyTriggerMultVsPhi->GetBinContent(hEfficiencyTriggerMultVsPhi->GetBin(bintrig.data())); } else { efficiencyTrigg = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); } if (efficiencyFlags.applyPurityTrigger) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { purityTrigg = hPurityHadronMult->Interpolate(trigg.pt(), mult); - else + } else { purityTrigg = hPurityHadron->Interpolate(trigg.pt()); + } } if (efficiencyFlags.applyEfficiencyPropagation) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { efficiencyTriggError = hEfficiencyUncertaintyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); - else if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) - efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig)); - else + } else if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + efficiencyTriggError = hEfficiencyTriggerMultVsPhi->GetBinError(hEfficiencyTriggerMultVsPhi->GetBin(bintrig.data())); + } else { efficiencyTriggError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + } if (efficiencyFlags.applyPurityTrigger) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { purityTriggErr = hPurityUncertaintyHadronMult->Interpolate(trigg.pt(), mult); - else + } else { purityTriggErr = hPurityUncertaintyHadron->Interpolate(trigg.pt()); + } } } if (efficiencyTrigg == 0) { // check for zero efficiency, do not apply if the case @@ -1105,12 +2092,13 @@ struct HStrangeCorrelation { fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesCascade")), trigg.pt(), mult, efficiencyTrigg, efficiencyTriggError, purityTrigg, purityTriggErr); } double triggSign = trigg.sign(); - double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + std::array triggForDeltaPhiStar = {trigg.phi(), trigg.pt(), triggSign}; if (mixingInBf) { currentCollision.addValidParticle(trigg.eta(), trigg.phi(), trigg.pt(), -1, efficiencyTrigg, efficiencyTriggError, -1); - if (firstLoop) + if (firstLoop) { continue; + } } for (auto const& assocCandidate : assocs) { firstLoop = true; @@ -1126,31 +2114,32 @@ struct HStrangeCorrelation { assoc.casccosPA(pvx, pvy, pvz) < cascadeSelections.cascCospa || assoc.cascradius() < cascadeSelections.cascRadius || std::abs(assoc.dcav0topv(pvx, pvy, pvz)) < cascadeSelections.cascdcaV0ToPV || - std::abs(assoc.mLambda() - o2::constants::physics::MassLambda0) > cascadeSelections.cascV0masswindow)) + std::abs(assoc.mLambda() - o2::constants::physics::MassLambda0) > cascadeSelections.cascV0masswindow)) { continue; - if (!masterConfigurations.doPPAnalysis && !cascadeSelectedPbPb(assoc, pvx, pvy, pvz)) + } + if (!masterConfigurations.doPPAnalysis && !cascadeSelectedPbPb(assoc, pvx, pvy, pvz)) { continue; + } uint64_t cascselMap = cascadeselectionBitmap(assoc, pvx, pvy, pvz); //---] removing autocorrelations [--- auto postrack = assoc.posTrack_as(); auto negtrack = assoc.negTrack_as(); auto bachtrack = assoc.bachelor_as(); - if (!mixingInBf) { - if (doAutocorrelationRejection) { - if (trigg.globalIndex() == postrack.globalIndex()) { - histos.fill(HIST("hNumberOfRejectedPairsCascades"), 0.5); - continue; - } - if (trigg.globalIndex() == negtrack.globalIndex()) { - histos.fill(HIST("hNumberOfRejectedPairsCascades"), 0.5); - continue; - } - if (trigg.globalIndex() == bachtrack.globalIndex()) { - histos.fill(HIST("hNumberOfRejectedPairsCascades"), 0.5); - continue; - } + if (doAutocorrelationRejection) { + if (trigg.globalIndex() == postrack.globalIndex()) { + histos.fill(HIST("hNumberOfRejectedPairsCascades"), 0.5); + continue; + } + if (trigg.globalIndex() == negtrack.globalIndex()) { + histos.fill(HIST("hNumberOfRejectedPairsCascades"), 0.5); + continue; + } + if (trigg.globalIndex() == bachtrack.globalIndex()) { + histos.fill(HIST("hNumberOfRejectedPairsCascades"), 0.5); + continue; } } + double phiProton = postrack.phi(); double etaProton = postrack.eta(); double ptProton = postrack.pt(); @@ -1161,30 +2150,39 @@ struct HStrangeCorrelation { ptProton = negtrack.pt(); signProton = negtrack.sign(); } - double assocForDeltaPhiStar[] = {phiProton, ptProton, signProton}; + std::array assocForDeltaPhiStar = {phiProton, ptProton, signProton}; //---] track quality check [--- - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) + if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; - if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated)) + } + if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { continue; - if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || bachtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) + } + if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || bachtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) { continue; + } float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); float deltaeta = trigg.eta() - assoc.eta(); + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } float ptassoc = assoc.pt(); float pttrigger = trigg.pt(); // skip if basic ranges not met - if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) { continue; - if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + } + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) { continue; - if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + } + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) { continue; + } - TH2F* hEfficiencyCascade[4]; - THnF* hEfficiencyCascadeMultVsPhi[4]; + std::array hEfficiencyCascade{nullptr, nullptr, nullptr, nullptr}; + std::array hEfficiencyCascadeMultVsPhi{nullptr, nullptr, nullptr, nullptr}; if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { hEfficiencyCascadeMultVsPhi[0] = hEfficiencyXiMinusMultVsPhi; hEfficiencyCascadeMultVsPhi[1] = hEfficiencyXiPlusMultVsPhi; @@ -1197,7 +2195,7 @@ struct HStrangeCorrelation { hEfficiencyCascade[3] = hEfficiencyOmegaPlus; } - TH2F* hEfficiencyUncertaintyCascade[4]; + std::array hEfficiencyUncertaintyCascade{nullptr, nullptr, nullptr, nullptr}; hEfficiencyUncertaintyCascade[0] = hEfficiencyUncertaintyXiMinus; hEfficiencyUncertaintyCascade[1] = hEfficiencyUncertaintyXiPlus; hEfficiencyUncertaintyCascade[2] = hEfficiencyUncertaintyOmegaMinus; @@ -1211,19 +2209,24 @@ struct HStrangeCorrelation { static_for<0, 3>([&](auto i) { constexpr int Index = i.value; + if ((Index == IndexOmegaMinus || Index == IndexOmegaPlus) && assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && std::abs(assocCandidate.invMassNSigma(Index - 2)) < massWindowConfigurations.nSigmaNearXiMassCenter) { + return; + } float efficiency = 1.0f; float totalEffUncert = 0.0; float efficiencyError = 0.0f; if (efficiencyFlags.applyEfficiencyCorrection) { if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { - double bin[4] = {ptassoc, assoc.eta(), assoc.phi(), mult}; - efficiency = hEfficiencyCascadeMultVsPhi[Index]->GetBinContent(hEfficiencyCascadeMultVsPhi[Index]->GetBin(bin)); - if (efficiencyFlags.applyEfficiencyPropagation) - efficiencyError = hEfficiencyCascadeMultVsPhi[Index]->GetBinError(hEfficiencyCascadeMultVsPhi[Index]->GetBin(bin)); + std::array bin = {ptassoc, assoc.eta(), assoc.phi(), mult}; + efficiency = hEfficiencyCascadeMultVsPhi[Index]->GetBinContent(hEfficiencyCascadeMultVsPhi[Index]->GetBin(bin.data())); + if (efficiencyFlags.applyEfficiencyPropagation) { + efficiencyError = hEfficiencyCascadeMultVsPhi[Index]->GetBinError(hEfficiencyCascadeMultVsPhi[Index]->GetBin(bin.data())); + } } else { efficiency = hEfficiencyCascade[Index]->Interpolate(ptassoc, assoc.eta()); - if (efficiencyFlags.applyEfficiencyPropagation) + if (efficiencyFlags.applyEfficiencyPropagation) { efficiencyError = hEfficiencyUncertaintyCascade[Index]->Interpolate(ptassoc, assoc.eta()); + } } } if (efficiency == 0) { // check for zero efficiency, do not apply if the case @@ -1233,53 +2236,57 @@ struct HStrangeCorrelation { if (efficiencyFlags.applyEfficiencyPropagation) { totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyError, 2) + std::pow(efficiencyTriggError * efficiency, 2)); } - double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + + std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; if (TESTBIT(doCorrelation, Index + 3) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0) && (masterConfigurations.doPPAnalysis || (TESTBIT(cascselMap, Index) && TESTBIT(cascselMap, Index + 4) && TESTBIT(cascselMap, Index + 8) && TESTBIT(cascselMap, Index + 12)))) { - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/LeftBg/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); - if ((Index == IndexXiMinus && triggSign > Neutral) || (Index == IndexXiPlus && triggSign < Neutral) || (Index == IndexOmegaMinus && triggSign > Neutral) || (Index == IndexOmegaPlus && triggSign < 0)) + if ((Index == IndexXiMinus && triggSign > Neutral) || (Index == IndexXiPlus && triggSign < Neutral) || (Index == IndexOmegaMinus && triggSign > Neutral) || (Index == IndexOmegaPlus && triggSign < 0)) { histos.fill(HIST("sameEvent/LeftBg/") + HIST(Cascadenames[Index]) + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - etaProton, 0.5); - else + } else { histos.fill(HIST("sameEvent/LeftBg/") + HIST(Cascadenames[Index]) + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - etaProton, -0.5); + } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/Signal/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); - if ((Index == IndexXiMinus && triggSign > Neutral) || (Index == IndexXiPlus && triggSign < Neutral) || (Index == IndexOmegaMinus && triggSign > Neutral) || (Index == IndexOmegaPlus && triggSign < 0)) + if ((Index == IndexXiMinus && triggSign > Neutral) || (Index == IndexXiPlus && triggSign < Neutral) || (Index == IndexOmegaMinus && triggSign > Neutral) || (Index == IndexOmegaPlus && triggSign < 0)) { histos.fill(HIST("sameEvent/Signal/") + HIST(Cascadenames[Index]) + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - etaProton, 0.5); - else + } else { histos.fill(HIST("sameEvent/Signal/") + HIST(Cascadenames[Index]) + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - etaProton, -0.5); + } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && !mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { fillCorrelationHistogram(histos.get(HIST("sameEvent/RightBg/") + HIST(Cascadenames[Index])), binFillThn, etaWeight, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); if (doDeltaPhiStarCheck) { double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); - if ((Index == IndexXiMinus && triggSign > Neutral) || (Index == IndexXiPlus && triggSign < Neutral) || (Index == IndexOmegaMinus && triggSign > Neutral) || (Index == IndexOmegaPlus && triggSign < 0)) + if ((Index == IndexXiMinus && triggSign > Neutral) || (Index == IndexXiPlus && triggSign < Neutral) || (Index == IndexOmegaMinus && triggSign > Neutral) || (Index == IndexOmegaPlus && triggSign < 0)) { histos.fill(HIST("sameEvent/RightBg/") + HIST(Cascadenames[Index]) + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - etaProton, 0.5); - else + } else { histos.fill(HIST("sameEvent/RightBg/") + HIST(Cascadenames[Index]) + HIST("DeltaPhiStar"), deltaPhiStar, trigg.eta() - etaProton, -0.5); + } } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 0, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/LeftBg/") + HIST(Cascadenames[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && -massWindowConfigurations.maxPeakNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 1, efficiency, efficiencyError, Index); } else { fillCorrelationHistogram(histos.get(HIST("mixedEvent/Signal/") + HIST(Cascadenames[Index])), binFillThn, 1, efficiency * efficiencyTrigg, totalEffUncert, purityTrigg, purityTriggErr); } } - if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { + if (assocCandidate.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || assocCandidate.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || assocCandidate.mcPhysicalPrimary()) && mixing && +massWindowConfigurations.minBgNSigma < assocCandidate.invMassNSigma(Index) && assocCandidate.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma) { if (mixingInBf) { currentCollision.addValidParticle(assoc.eta(), assoc.phi(), assoc.pt(), 2, efficiency, efficiencyError, Index); } else { @@ -1290,18 +2297,20 @@ struct HStrangeCorrelation { }); } } - if (!mixingInBf || binVtxZ < 0 || binVtxZ > nBinsVtxZ - 1 || binMult < 0 || binMult > nBinsMult - 1) + if (!mixingInBf) { return; - int binnumb = binMult * nBinsVtxZ + binVtxZ; + } + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + int binnumb = colBinning.getBin({pvz, mult}); int hastirgorassoc = masterConfigurations.collisionHasTriggOrAssoc; if ((hastirgorassoc == 1 && currentCollision.trigParticles.empty()) || (hastirgorassoc == 2 && currentCollision.assocParticles.empty()) || (hastirgorassoc == 3 && currentCollision.trigParticles.empty() && currentCollision.assocParticles.empty()) || - (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) + (hastirgorassoc == 4 && (currentCollision.trigParticles.empty() || currentCollision.assocParticles.empty()))) { return; + } for (const auto& collision : validCollisions[binnumb]) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; - histos.fill(HIST("MixingQA/hMECollisionBins"), colBinning.getBin({collision.pvz, collision.mult})); + histos.fill(HIST("MixingQA/hMECollisionBins"), binnumb); for (const auto& trigger : collision.trigParticles) { for (const auto& assoc : currentCollision.assocParticles) { float deltaeta = trigger.eta - assoc.eta; @@ -1316,7 +2325,10 @@ struct HStrangeCorrelation { if (efficiencyFlags.applyEfficiencyPropagation) { totalEffUncert = std::sqrt(std::pow(efficiencyTrigg * efficiencyAssocError, 2) + std::pow(efficiencyTriggError * efficiencyAssoc, 2)); } - double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } + std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; static_for<0, 3>([&](auto i) { constexpr int Index = i.value; if (Index == assoc.type && assoc.region == 0) { @@ -1335,44 +2347,55 @@ struct HStrangeCorrelation { if (validCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter)) { validCollisions[binnumb].erase(validCollisions[binnumb].begin()); } - validCollisions[binnumb].push_back(currentCollision); + if (!currentCollision.trigParticles.empty()) { + validCollisions[binnumb].push_back(currentCollision); + } } template void fillCorrelationsHadron(TTriggers const& triggers, THadrons const& assocs, bool mixing, float pvz, float mult, double bField) { for (auto const& triggerTrack : triggers) { - if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { continue; + } auto trigg = triggerTrack.template track_as(); - if (!isValidTrigger(trigg, triggerTrack.isLeading())) + if (!isValidTrigger(trigg, triggerTrack.isLeading())) { continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { + continue; + } float efficiencyTrigger = 1.0f; float efficiencyTriggerError = 0.0f; float purityTrigger = 1.0f; float purityTriggerError = 0.0f; if (efficiencyFlags.applyEfficiencyForTrigger) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { efficiencyTrigger = hEfficiencyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); - else + } else { efficiencyTrigger = hEfficiencyTrigger->Interpolate(trigg.pt(), trigg.eta()); + } if (efficiencyFlags.applyPurityTrigger) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { purityTrigger = hPurityHadronMult->Interpolate(trigg.pt(), mult); - else + } else { purityTrigger = hPurityHadron->Interpolate(trigg.pt()); + } } if (efficiencyFlags.applyEfficiencyPropagation) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { efficiencyTriggerError = hEfficiencyUncertaintyTriggerMult->Interpolate(trigg.pt(), trigg.eta(), mult); - else + } else { efficiencyTriggerError = hEfficiencyUncertaintyTrigger->Interpolate(trigg.pt(), trigg.eta()); + } if (efficiencyFlags.applyPurityTrigger) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { purityTriggerError = hPurityUncertaintyHadronMult->Interpolate(trigg.pt(), mult); - else + } else { purityTriggerError = hPurityUncertaintyHadron->Interpolate(trigg.pt()); + } } } if (efficiencyTrigger == 0) { // check for zero efficiency, do not apply if the case @@ -1381,39 +2404,48 @@ struct HStrangeCorrelation { } } if (!mixing) { - if constexpr (requires { triggerTrack.extra(); }) + if constexpr (requires { triggerTrack.extra(); }) { fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesPion")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); - else + } else { fillTriggerHistogram(histos.get(HIST("sameEvent/TriggerParticlesHadron")), trigg.pt(), mult, efficiencyTrigger, efficiencyTriggerError, purityTrigger, purityTriggerError); + } } double triggSign = trigg.sign(); - double triggForDeltaPhiStar[] = {trigg.phi(), trigg.pt(), triggSign}; + std::array triggForDeltaPhiStar = {trigg.phi(), trigg.pt(), triggSign}; for (auto const& assocTrack : assocs) { auto assoc = assocTrack.template track_as(); //---] removing autocorrelations [--- if (doAutocorrelationRejection) { if (trigg.globalIndex() == assoc.globalIndex()) { - if constexpr (requires { assocTrack.nSigmaTPCPi(); }) + if constexpr (requires { assocTrack.nSigmaTPCPi(); }) { histos.fill(HIST("hNumberOfRejectedPairsPion"), 0.5); - else + } else { histos.fill(HIST("hNumberOfRejectedPairsHadron"), 0.5); + } continue; } } //---] track quality check [--- - if (!isValidAssocHadron(assoc)) + if (!isValidAssocHadron(assoc)) { continue; - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(assocTrack.mcMask(), 13)) { + continue; + } + if (masterConfigurations.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; } float deltaphi = computeDeltaPhi(trigg.phi(), assoc.phi()); float deltaeta = trigg.eta() - assoc.eta(); + if (masterConfigurations.doMirroringInDelataEta) { + deltaeta = std::abs(deltaeta); + } float ptassoc = assoc.pt(); float pttrigger = trigg.pt(); double assocSign = assoc.sign(); - double assocForDeltaPhiStar[] = {assoc.phi(), assoc.pt(), assocSign}; + std::array assocForDeltaPhiStar = {assoc.phi(), assoc.pt(), assocSign}; float etaWeight = 1.; if (checks.doOnTheFlyFlattening) { @@ -1422,12 +2454,15 @@ struct HStrangeCorrelation { } // skip if basic ranges not met - if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) { continue; - if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + } + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) { continue; - if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + } + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) { continue; + } float efficiency = 1; float purity = 1.0f; @@ -1438,29 +2473,34 @@ struct HStrangeCorrelation { if (efficiencyFlags.applyEfficiencyCorrection) { if constexpr (requires { assocTrack.nSigmaTPCPi(); }) { efficiency = hEfficiencyPion->Interpolate(ptassoc, assoc.eta()); - if (efficiencyFlags.applyEfficiencyPropagation) + if (efficiencyFlags.applyEfficiencyPropagation) { efficiencyUncertainty = hEfficiencyUncertaintyPion->Interpolate(ptassoc, assoc.eta()); + } } else { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { efficiency = hEfficiencyHadronMult->Interpolate(ptassoc, assoc.eta(), mult); - else + } else { efficiency = hEfficiencyHadron->Interpolate(ptassoc, assoc.eta()); + } if (efficiencyFlags.applyPurityHadron) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { purity = hPurityHadronMult->Interpolate(ptassoc, mult); - else + } else { purity = hPurityHadron->Interpolate(ptassoc); + } } if (efficiencyFlags.applyEfficiencyPropagation) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { efficiencyUncertainty = hEfficiencyUncertaintyHadronMult->Interpolate(ptassoc, assoc.eta(), mult); - else + } else { efficiencyUncertainty = hEfficiencyUncertaintyHadron->Interpolate(ptassoc, assoc.eta()); + } if (efficiencyFlags.applyPurityHadron) { - if (efficiencyFlags.applyEffAsFunctionOfMult) + if (efficiencyFlags.applyEffAsFunctionOfMult) { purityUncertainty = hPurityUncertaintyHadronMult->Interpolate(ptassoc, mult); - else + } else { purityUncertainty = hPurityUncertaintyHadron->Interpolate(ptassoc); + } } } } @@ -1473,7 +2513,8 @@ struct HStrangeCorrelation { totalEffUncert = std::sqrt(std::pow(efficiencyTrigger * efficiencyUncertainty, 2) + std::pow(efficiencyTriggerError * efficiency, 2)); totalPurityUncert = std::sqrt(std::pow(purityTrigger * purityUncertainty, 2) + std::pow(purity * purityTriggerError, 2)); } - double binFillThn[6] = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; + + std::array binFillThn = {deltaphi, deltaeta, ptassoc, pttrigger, pvz, mult}; double deltaPhiStar = calculateAverageDeltaPhiStar(triggForDeltaPhiStar, assocForDeltaPhiStar, bField); if (!mixing) { if constexpr (requires { assocTrack.nSigmaTPCPi(); }) { @@ -1505,51 +2546,58 @@ struct HStrangeCorrelation { void init(InitContext const&) { zorroSummary.setObject(zorro.getZorroSummary()); - mRunNumber = 0; - mRunNumberZorro = 0; - hEfficiencyPion = 0x0; - hEfficiencyK0Short = 0x0; - hEfficiencyLambda = 0x0; - hEfficiencyAntiLambda = 0x0; - hEfficiencyXiMinus = 0x0; - hEfficiencyXiPlus = 0x0; - hEfficiencyOmegaMinus = 0x0; - hEfficiencyOmegaPlus = 0x0; - hEfficiencyUncertaintyTrigger = 0x0; - hEfficiencyUncertaintyXiMinus = 0x0; - hEfficiencyUncertaintyXiPlus = 0x0; - hEfficiencyUncertaintyOmegaMinus = 0x0; - hEfficiencyUncertaintyOmegaPlus = 0x0; - hEfficiencyUncertaintyPion = 0x0; - hEfficiencyUncertaintyK0Short = 0x0; - hEfficiencyUncertaintyLambda = 0x0; - hEfficiencyUncertaintyAntiLambda = 0x0; - - hEfficiencyHadron = 0x0; - hPurityHadron = 0x0; - hPurityUncertaintyHadron = 0x0; - hEfficiencyUncertaintyHadron = 0x0; + hEfficiencyPion = nullptr; + hEfficiencyK0Short = nullptr; + hEfficiencyLambda = nullptr; + hEfficiencyAntiLambda = nullptr; + hEfficiencyXiMinus = nullptr; + hEfficiencyXiPlus = nullptr; + hEfficiencyOmegaMinus = nullptr; + hEfficiencyOmegaPlus = nullptr; + hEfficiencyUncertaintyTrigger = nullptr; + hEfficiencyUncertaintyXiMinus = nullptr; + hEfficiencyUncertaintyXiPlus = nullptr; + hEfficiencyUncertaintyOmegaMinus = nullptr; + hEfficiencyUncertaintyOmegaPlus = nullptr; + hEfficiencyUncertaintyPion = nullptr; + hEfficiencyUncertaintyK0Short = nullptr; + hEfficiencyUncertaintyLambda = nullptr; + hEfficiencyUncertaintyAntiLambda = nullptr; + + hEfficiencyHadron = nullptr; + hPurityHadron = nullptr; + hPurityUncertaintyHadron = nullptr; + hEfficiencyUncertaintyHadron = nullptr; // set bitmap for convenience doCorrelation = 0; - if (masterConfigurations.doCorrelationK0Short) + if (masterConfigurations.doCorrelationK0Short) { SETBIT(doCorrelation, 0); - if (masterConfigurations.doCorrelationLambda) + } + if (masterConfigurations.doCorrelationLambda) { SETBIT(doCorrelation, 1); - if (masterConfigurations.doCorrelationAntiLambda) + } + if (masterConfigurations.doCorrelationAntiLambda) { SETBIT(doCorrelation, 2); - if (masterConfigurations.doCorrelationXiMinus) + } + if (masterConfigurations.doCorrelationXiMinus) { SETBIT(doCorrelation, 3); - if (masterConfigurations.doCorrelationXiPlus) + } + if (masterConfigurations.doCorrelationXiPlus) { SETBIT(doCorrelation, 4); - if (masterConfigurations.doCorrelationOmegaMinus) + } + if (masterConfigurations.doCorrelationOmegaMinus) { SETBIT(doCorrelation, 5); - if (masterConfigurations.doCorrelationOmegaPlus) + } + if (masterConfigurations.doCorrelationOmegaPlus) { SETBIT(doCorrelation, 6); - if (masterConfigurations.doCorrelationPion) + } + if (masterConfigurations.doCorrelationPion) { SETBIT(doCorrelation, 7); - if (masterConfigurations.doCorrelationHadron) + } + if (masterConfigurations.doCorrelationHadron) { SETBIT(doCorrelation, 8); + } // Store axis ranges to prevent spurious filling // axis status: @@ -1568,6 +2616,7 @@ struct HStrangeCorrelation { const AxisSpec preAxisMult{axesConfigurations.axisMult, "mult percentile"}; const AxisSpec axisPtLambda{axesConfigurations.axisPtAssoc, "#it{p}_{T}^{#Lambda} (GeV/c)"}; const AxisSpec axisPtCascade{axesConfigurations.axisPtAssoc, "#it{p}_{T}^{Mother} (GeV/c)"}; + const AxisSpec preAxisMultiplicity{axesConfigurations.axisMultiplicity, "multiplicity"}; // store the original axes in specific TH1Cs for completeness histos.add("axes/hDeltaPhiAxis", "", kTH1C, {preAxisDeltaPhi}); @@ -1576,6 +2625,7 @@ struct HStrangeCorrelation { histos.add("axes/hPtTriggerAxis", "", kTH1C, {preAxisPtTrigger}); histos.add("axes/hVertexZAxis", "", kTH1C, {preAxisVtxZ}); histos.add("axes/hMultAxis", "", kTH1C, {preAxisMult}); + histos.add("axes/hMultiplicityAxis", "", kTH1C, {preAxisMultiplicity}); std::vector edgesDeltaPhiOrig = preAxisDeltaPhi.binEdges; std::vector edgesDeltaEtaOrig = preAxisDeltaEta.binEdges; @@ -1583,6 +2633,7 @@ struct HStrangeCorrelation { std::vector edgesPtTriggerOrig = preAxisPtTrigger.binEdges; std::vector edgesVtxZOrig = preAxisVtxZ.binEdges; std::vector edgesMultOrig = preAxisMult.binEdges; + std::vector edgesMultiplicityOrig = preAxisMultiplicity.binEdges; std::vector rangesDeltaPhi = {static_cast(edgesDeltaPhiOrig[0]), static_cast(edgesDeltaPhiOrig[edgesDeltaPhiOrig.size() - 1])}; std::vector rangesDeltaEta = {static_cast(edgesDeltaEtaOrig[0]), static_cast(edgesDeltaEtaOrig[edgesDeltaEtaOrig.size() - 1])}; @@ -1590,6 +2641,7 @@ struct HStrangeCorrelation { std::vector rangesPtTrigger = {static_cast(edgesPtTriggerOrig[0]), static_cast(edgesPtTriggerOrig[edgesPtTriggerOrig.size() - 1])}; std::vector rangesVtxZ = {static_cast(edgesVtxZOrig[0]), static_cast(edgesVtxZOrig[edgesVtxZOrig.size() - 1])}; std::vector rangesMult = {static_cast(edgesMultOrig[0]), static_cast(edgesMultOrig[edgesMultOrig.size() - 1])}; + std::vector rangesMultiplicity = {static_cast(edgesMultiplicityOrig[0]), static_cast(edgesMultiplicityOrig[edgesMultiplicityOrig.size() - 1])}; axisRanges.emplace_back(rangesDeltaPhi); axisRanges.emplace_back(rangesDeltaEta); @@ -1597,6 +2649,7 @@ struct HStrangeCorrelation { axisRanges.emplace_back(rangesPtTrigger); axisRanges.emplace_back(rangesVtxZ); axisRanges.emplace_back(rangesMult); + axisRanges.emplace_back(rangesMultiplicity); std::vector edgesDeltaPhi; std::vector edgesDeltaEta; @@ -1604,6 +2657,7 @@ struct HStrangeCorrelation { std::vector edgesPtTrigger; std::vector edgesVtxZ; std::vector edgesMult; + std::vector edgesMultiplicity; // v--- skipUnderOverflowInTHn ---v // @@ -1619,74 +2673,100 @@ struct HStrangeCorrelation { // ===] delta-phi [=== if (!preAxisDeltaPhi.nBins.has_value()) { // variable binning, use bins provided - for (int i = offset; i < static_cast(edgesDeltaPhiOrig.size()) - offset; i++) + for (int i = offset; i < static_cast(edgesDeltaPhiOrig.size()) - offset; i++) { edgesDeltaPhi.emplace_back(edgesDeltaPhiOrig[i]); + } } else { // fixed binning, generate the bin edges on-the-spot double min = edgesDeltaPhiOrig[0]; double delta = (edgesDeltaPhiOrig[1] - edgesDeltaPhiOrig[0]) / preAxisDeltaPhi.nBins.value(); - for (int i = offset; i < preAxisDeltaPhi.nBins.value() + 1 - offset; i++) + for (int i = offset; i < preAxisDeltaPhi.nBins.value() + 1 - offset; i++) { edgesDeltaPhi.emplace_back(min + static_cast(i) * delta); + } } // ===] delta-eta [=== if (!preAxisDeltaEta.nBins.has_value()) { // variable binning, use bins provided - for (int i = offset; i < static_cast(edgesDeltaEtaOrig.size()) - offset; i++) + for (int i = offset; i < static_cast(edgesDeltaEtaOrig.size()) - offset; i++) { edgesDeltaEta.emplace_back(edgesDeltaEtaOrig[i]); + } } else { // fixed binning, generate the bin edges on-the-spot double min = edgesDeltaEtaOrig[0]; double delta = (edgesDeltaEtaOrig[1] - edgesDeltaEtaOrig[0]) / preAxisDeltaEta.nBins.value(); - for (int i = offset; i < preAxisDeltaEta.nBins.value() + 1 - offset; i++) + for (int i = offset; i < preAxisDeltaEta.nBins.value() + 1 - offset; i++) { edgesDeltaEta.emplace_back(min + static_cast(i) * delta); + } } // ===] pt assoc [=== if (!preAxisPtAssoc.nBins.has_value()) { // variable binning, use bins provided - for (int i = offset; i < static_cast(edgesPtAssocOrig.size()) - offset; i++) + for (int i = offset; i < static_cast(edgesPtAssocOrig.size()) - offset; i++) { edgesPtAssoc.emplace_back(edgesPtAssocOrig[i]); + } } else { // fixed binning, generate the bin edges on-the-spot double min = edgesPtAssocOrig[0]; double delta = (edgesPtAssocOrig[1] - edgesPtAssocOrig[0]) / preAxisPtAssoc.nBins.value(); - for (int i = offset; i < preAxisPtAssoc.nBins.value() + 1 - offset; i++) + for (int i = offset; i < preAxisPtAssoc.nBins.value() + 1 - offset; i++) { edgesPtAssoc.emplace_back(min + static_cast(i) * delta); + } } // ===] pt trigger [=== if (!preAxisPtTrigger.nBins.has_value()) { // variable binning, use bins provided - for (int i = offset; i < static_cast(edgesPtTriggerOrig.size()) - offset; i++) + for (int i = offset; i < static_cast(edgesPtTriggerOrig.size()) - offset; i++) { edgesPtTrigger.emplace_back(edgesPtTriggerOrig[i]); + } } else { // fixed binning, generate the bin edges on-the-spot double min = edgesPtTriggerOrig[0]; double delta = (edgesPtTriggerOrig[1] - edgesPtTriggerOrig[0]) / preAxisPtTrigger.nBins.value(); - for (int i = offset; i < preAxisPtTrigger.nBins.value() + 1 - offset; i++) + for (int i = offset; i < preAxisPtTrigger.nBins.value() + 1 - offset; i++) { edgesPtTrigger.emplace_back(min + static_cast(i) * delta); + } } // ===] vtx Z [=== if (!preAxisVtxZ.nBins.has_value()) { // variable binning, use bins provided - for (int i = offset; i < static_cast(edgesVtxZOrig.size()) - offset; i++) + for (int i = offset; i < static_cast(edgesVtxZOrig.size()) - offset; i++) { edgesVtxZ.emplace_back(edgesVtxZOrig[i]); + } } else { // fixed binning, generate the bin edges on-the-spot double min = edgesVtxZOrig[0]; double delta = (edgesVtxZOrig[1] - edgesVtxZOrig[0]) / preAxisVtxZ.nBins.value(); - for (int i = offset; i < preAxisVtxZ.nBins.value() + 1 - offset; i++) + for (int i = offset; i < preAxisVtxZ.nBins.value() + 1 - offset; i++) { edgesVtxZ.emplace_back(min + static_cast(i) * delta); + } } // ===] mult percentile [=== if (!preAxisMult.nBins.has_value()) { // variable binning, use bins provided - for (int i = offset; i < static_cast(edgesMultOrig.size()) - offset; i++) + for (int i = offset; i < static_cast(edgesMultOrig.size()) - offset; i++) { edgesMult.emplace_back(edgesMultOrig[i]); + } } else { // fixed binning, generate the bin edges on-the-spot double min = edgesMultOrig[0]; double delta = (edgesMultOrig[1] - edgesMultOrig[0]) / preAxisMult.nBins.value(); - for (int i = offset; i < preAxisMult.nBins.value() + 1 - offset; i++) + for (int i = offset; i < preAxisMult.nBins.value() + 1 - offset; i++) { edgesMult.emplace_back(min + static_cast(i) * delta); + } + } + // ===] multiplicity count [=== + if (!preAxisMultiplicity.nBins.has_value()) { + // variable binning, use bins provided + for (int i = offset; i < static_cast(edgesMultiplicityOrig.size()) - offset; i++) { + edgesMultiplicity.emplace_back(edgesMultiplicityOrig[i]); + } + } else { + // fixed binning, generate the bin edges on-the-spot + double min = edgesMultiplicityOrig[0]; + double delta = (edgesMultiplicityOrig[1] - edgesMultiplicityOrig[0]) / preAxisMultiplicity.nBins.value(); + for (int i = offset; i < preAxisMultiplicity.nBins.value() + 1 - offset; i++) { + edgesMultiplicity.emplace_back(min + static_cast(i) * delta); + } } LOGF(info, "Initialized THnF axis delta-phi with %i bins.", edgesDeltaPhi.size() - 1); @@ -1694,7 +2774,8 @@ struct HStrangeCorrelation { LOGF(info, "Initialized THnF axis pTassoc with %i bins.", edgesPtAssoc.size() - 1); LOGF(info, "Initialized THnF axis pTtrigger with %i bins.", edgesPtTrigger.size() - 1); LOGF(info, "Initialized THnF axis vertex-Z with %i bins.", edgesVtxZ.size() - 1); - LOGF(info, "Initialized THnF axis multiplicity with %i bins.", edgesMult.size() - 1); + LOGF(info, "Initialized THnF axis mult cent with %i bins.", edgesMult.size() - 1); + LOGF(info, "Initialized THnF axis multiplicity with %i bins.", edgesMultiplicity.size() - 1); const AxisSpec axisDeltaPhiNDim{edgesDeltaPhi, "#Delta#varphi"}; const AxisSpec axisDeltaEtaNDim{edgesDeltaEta, "#Delta#eta"}; @@ -1702,19 +2783,440 @@ struct HStrangeCorrelation { const AxisSpec axisPtTriggerNDim{edgesPtTrigger, "#it{p}_{T}^{trigger} (GeV/c)"}; const AxisSpec axisVtxZNDim{edgesVtxZ, "vertex Z (cm)"}; const AxisSpec axisMultNDim{edgesMult, "mult percentile"}; + const AxisSpec axisMultiplicityNDim{edgesMultiplicity, "Multiplicity"}; + + if (doprocessPairLossK0MC && pairLossK0Configurations.doStageDiagnostics) { + const AxisSpec axisPairLossEventStage{6, -0.5, 5.5, "Event-selection stage"}; + const AxisSpec axisPairLossNRecoCollisions{11, -0.5, 10.5, "#it{N}_{reco collisions} per MC collision"}; + const AxisSpec axisPairLossStage{PairLossK0NStages, -0.5, static_cast(PairLossK0NStages) - 0.5, "Reconstruction stage"}; + const AxisSpec axisPairLossFinalObjectState{4, -0.5, 3.5, "Final h-K^{0}_{S} object state"}; + const AxisSpec axisPairLossTrackLevelState{4, -0.5, 3.5, "Trigger and K^{0}_{S}-daughter track state"}; + const AxisSpec axisPairLossDaughterState{4, -0.5, 3.5, "K^{0}_{S}-daughter track state"}; + const AxisSpec axisPairLossFieldSign{3, -1.5, 1.5, "sgn(#it{B}_{z})"}; + const AxisSpec axisPairLossChargeProduct{3, -1.5, 1.5, "sgn(#it{q}_{h}#it{q}_{#pi})"}; + const AxisSpec axisPairLossMatchType{7, -0.5, 6.5, "Matched-object category"}; + const AxisSpec axisPairLossMatchCount{11, -0.5, 10.5, "#it{N}_{matched objects} per truth pair"}; + const AxisSpec axisPairLossTPCCrossedRows{161, -0.5, 160.5, "#it{N}_{TPC crossed rows}"}; + const AxisSpec axisPairLossTPCSharedClusters{161, -0.5, 160.5, "#it{N}_{TPC shared clusters}"}; + const AxisSpec axisPairLossITSClusters{8, -0.5, 7.5, "#it{N}_{ITS clusters}"}; + const AxisSpec axisPairLossLayer0{2, -0.5, 1.5, "Hit in ITS layer 0"}; + const AxisSpec axisPairLossMassNSigma{120, -12.0, 12.0, "#it{n}_{#sigma}(m_{#pi^{+}#pi^{-}}; K^{0}_{S})"}; + const AxisSpec axisPairLossCosPA{100, 0.9, 1.0, "cos(#theta_{PA})"}; + const AxisSpec axisPairLossDcaDaughters{100, 0.0, 2.0, "DCA(#pi^{+}, #pi^{-}) (cm)"}; + const AxisSpec axisPairLossDeltaPhiStarValidity{2, -0.5, 1.5, "Validity of min |#Delta#varphi^{*}_{h-#pi}|"}; + const AxisSpec axisPairLossTruthDeltaPhi{edgesDeltaPhi, "#Delta#varphi_{h-K^{0}_{S}}^{gen} (rad)"}; + const AxisSpec axisPairLossRecoDeltaPhi{edgesDeltaPhi, "#Delta#varphi_{h-K^{0}_{S}}^{reco} (rad)"}; + const AxisSpec axisPairLossTruthDeltaEta{edgesDeltaEta, "#Delta#eta_{h-K^{0}_{S}}^{gen}"}; + const AxisSpec axisPairLossRecoDeltaEta{edgesDeltaEta, "#Delta#eta_{h-K^{0}_{S}}^{reco}"}; + const AxisSpec axisPairLossTruthK0Pt{edgesPtAssoc, "#it{p}_{T,K^{0}_{S}}^{gen} (GeV/#it{c})"}; + const AxisSpec axisPairLossRecoK0Pt{edgesPtAssoc, "#it{p}_{T,K^{0}_{S}}^{reco} (GeV/#it{c})"}; + const AxisSpec axisPairLossTruthTriggerPt{edgesPtTrigger, "#it{p}_{T,h}^{gen} (GeV/#it{c})"}; + const AxisSpec axisPairLossRecoTriggerPt{edgesPtTrigger, "#it{p}_{T,h}^{reco} (GeV/#it{c})"}; + const AxisSpec axisPairLossMinDeltaPhiStar{pairLossK0Configurations.axisMinDeltaPhiStar, "min_{#it{r}} |#Delta#varphi^{*}_{h-#pi}(#it{r})| (rad)"}; + const AxisSpec axisPairLossSignedDeltaPhiStar{pairLossK0Configurations.axisSignedDeltaPhiStar, "#Delta#varphi^{*}_{h-#pi}(#it{r}_{min}) (rad)"}; + const AxisSpec axisPairLossDaughterDeltaEta{pairLossK0Configurations.axisDaughterDeltaEta, "#Delta#eta_{h-#pi}^{gen}"}; + const AxisSpec axisPairLossDaughterDeltaPhi{pairLossK0Configurations.axisDaughterDeltaPhi, "#Delta#varphi_{h-#pi}^{gen} (rad)"}; + const AxisSpec axisPairLossTruthDecayRadius{pairLossK0Configurations.axisDecayRadius, "#it{R}_{decay}^{gen}(K^{0}_{S}) (cm)"}; + const AxisSpec axisPairLossRecoV0Radius{pairLossK0Configurations.axisDecayRadius, "#it{R}_{V0}^{reco} (cm)"}; + const double pairLossRadiusMinimum = pairLossK0Configurations.minRadius; + const double pairLossRadiusMaximum = std::max(static_cast(pairLossK0Configurations.maxRadius), pairLossRadiusMinimum + 0.01); + const double pairLossRadiusStep = std::max(static_cast(pairLossK0Configurations.radiusStep), 0.001); + const int pairLossRadiusBins = std::max(1, static_cast(std::ceil((pairLossRadiusMaximum - pairLossRadiusMinimum) / pairLossRadiusStep))); + const AxisSpec axisPairLossRadiusAtMinimum{pairLossRadiusBins, pairLossRadiusMinimum, pairLossRadiusMaximum, "#it{r}_{min} (m)"}; + const AxisSpec axisPairLossTriggerTracksFailureReason{PairLossTriggerTracksNReasons, -0.5, static_cast(PairLossTriggerTracksNReasons) - 0.5, "TriggerTracks first-failing condition"}; + + histos.add("PairLossK0/Event/hCounter", "K0 pair-loss event counter", kTH1F, {axisPairLossEventStage}); + histos.add("PairLossK0/Event/hNRecoCollisions", "reconstructed collisions per MC collision", kTH1F, {axisPairLossNRecoCollisions}); + histos.add("PairLossK0/Stage/hCounts", "pair-loss diagnostic stage counts", kTH1F, {axisPairLossStage}); + histos.add("PairLossK0/Stage/hCountsFindable", "pair-loss diagnostic stage counts for findable K0", kTH1F, {axisPairLossStage}); + histos.add("PairLossK0/Stage/hPhysics", "stages in h-K0 physics variables", kTHnF, {axisPairLossStage, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisMultNDim}); + histos.add("PairLossK0/Stage/hPhysicsFindable", "stages in h-K0 physics variables for findable K0", kTHnF, {axisPairLossStage, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisMultNDim}); + histos.add("PairLossK0/Stage/hClose", "stages in trigger-daughter close-pair variables", kTHnF, {axisPairLossStage, axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct}); + histos.add("PairLossK0/Stage/hTriggerTracksFailureReason", "first-failing TriggerTracks condition for best-collision triggers, in h-K0 physics variables", kTHnF, {axisPairLossTriggerTracksFailureReason, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt}); + // 1/N_trigger denominator of the stage ladder: same stage axis as hCounts/hPhysics, generated trigger pT, eta, phi. + // Sparse because the eta-phi part would cost tens of MB dense for an occupancy of a few percent. + histos.add("PairLossK0/Stage/hTriggers", "triggers per stage: the 1/#it{N}_{trigger} normalisation of the stage ladder", kTHnSparseF, {axisPairLossStage, axisPairLossTruthTriggerPt, axesConfigurations.axisEta, axesConfigurations.axisPhi, axisVtxZNDim, axisMultNDim}); + + histos.add("PairLossK0/State/hFinalObjectStatePhysics", "00/01/10/11 final trigger-K0 object state", kTHnF, {axisPairLossFinalObjectState, axisPairLossTruthDeltaPhi, axisPairLossTruthDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign}); + histos.add("PairLossK0/State/hFinalObjectStateClose", "00/01/10/11 final trigger-K0 object state in close-pair variables", kTHnF, {axisPairLossFinalObjectState, axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct}); + histos.add("PairLossK0/State/hTrackLevelStateClose", "00/01/10/11 trigger-track and both-daughters state", kTHnF, {axisPairLossTrackLevelState, axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct}); + histos.add("PairLossK0/State/hDaughterTrackStateClose", "daughter state: none/negative/positive/both", kTHnF, {axisPairLossDaughterState, axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct}); + + histos.add("PairLossK0/Geometry/hGeneratedDaughters", "generated trigger-daughter geometry", kTHnF, {axisPairLossSignedDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct}); + histos.add("PairLossK0/Geometry/hFinalDaughters", "geometry for final reconstructed h-K0 pairs", kTHnF, {axisPairLossSignedDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign, axisPairLossChargeProduct}); + histos.add("PairLossK0/Geometry/hRawDeltaPhiVsSignedDeltaPhiStar", "closest daughter raw delta phi versus signed delta phi star", kTH3F, {axisPairLossDaughterDeltaPhi, axisPairLossSignedDeltaPhiStar, axisPairLossFieldSign}); + histos.add("PairLossK0/Geometry/hGeneratedMinDeltaPhiStarVsDecayRadius", "generated minimum delta phi star versus K0 decay radius", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossTruthDecayRadius, axisPairLossTruthK0Pt}); + histos.add("PairLossK0/Geometry/hFinalMinDeltaPhiStarVsDecayRadius", "final-pair minimum delta phi star versus K0 decay radius", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossTruthDecayRadius, axisPairLossTruthK0Pt}); + histos.add("PairLossK0/Geometry/hGeneratedRadiusAtMinimum", "radius of the closest trigger-daughter approach", kTH3F, {axisPairLossRadiusAtMinimum, axisPairLossMinDeltaPhiStar, axisPairLossTruthK0Pt}); + histos.add("PairLossK0/Geometry/hFinalRadiusAtMinimum", "radius of the closest approach for final pairs", kTH3F, {axisPairLossRadiusAtMinimum, axisPairLossMinDeltaPhiStar, axisPairLossTruthK0Pt}); + histos.add("PairLossK0/Geometry/hDeltaPhiStarValidity", "minimum delta-phi-star validity", kTH2F, {axisPairLossDeltaPhiStarValidity, axisPairLossTruthK0Pt}); + histos.add("PairLossK0/Geometry/hAutocorrelationRejected", "pairs rejected only by reconstructed track-ID autocorrelation", kTHnF, {axisPairLossMinDeltaPhiStar, axisPairLossDaughterDeltaEta, axisPairLossTruthK0Pt, axisPairLossTruthTriggerPt, axisPairLossFieldSign}); + + histos.add("PairLossK0/Matching/hNMatches", "number of matches for each truth pair", kTH2F, {axisPairLossMatchType, axisPairLossMatchCount}); + histos.add("PairLossK0/Response/hTriggerPt", "trigger pT response", kTH2F, {axisPairLossTruthTriggerPt, axisPairLossRecoTriggerPt}); + histos.add("PairLossK0/Response/hK0Pt", "K0 pT response", kTH2F, {axisPairLossTruthK0Pt, axisPairLossRecoK0Pt}); + histos.add("PairLossK0/Response/hDeltaPhi", "h-K0 delta-phi response", kTH2F, {axisPairLossTruthDeltaPhi, axisPairLossRecoDeltaPhi}); + histos.add("PairLossK0/Response/hDeltaEta", "h-K0 delta-eta response", kTH2F, {axisPairLossTruthDeltaEta, axisPairLossRecoDeltaEta}); + + histos.add("PairLossK0/TrackQA/hTriggerTPCPairWeighted", "pair-weighted trigger TPC quality", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossTPCCrossedRows, axisPairLossTPCSharedClusters}); + histos.add("PairLossK0/TrackQA/hTriggerITSPairWeighted", "pair-weighted trigger ITS quality", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossITSClusters, axisPairLossLayer0}); + histos.add("PairLossK0/TrackQA/hPositiveDaughterTPCPairWeighted", "pair-weighted positive-daughter TPC quality", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossTPCCrossedRows, axisPairLossTPCSharedClusters}); + histos.add("PairLossK0/TrackQA/hPositiveDaughterITSPairWeighted", "pair-weighted positive-daughter ITS quality", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossITSClusters, axisPairLossLayer0}); + histos.add("PairLossK0/TrackQA/hNegativeDaughterTPCPairWeighted", "pair-weighted negative-daughter TPC quality", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossTPCCrossedRows, axisPairLossTPCSharedClusters}); + histos.add("PairLossK0/TrackQA/hNegativeDaughterITSPairWeighted", "pair-weighted negative-daughter ITS quality", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossITSClusters, axisPairLossLayer0}); + + histos.add("PairLossK0/V0QA/hCosPAVsMinDeltaPhiStar", "matched V0 cos(PA) versus minimum delta phi star", kTH2F, {axisPairLossMinDeltaPhiStar, axisPairLossCosPA}); + histos.add("PairLossK0/V0QA/hDcaDaughtersVsMinDeltaPhiStar", "matched V0 daughter DCA versus minimum delta phi star", kTH2F, {axisPairLossMinDeltaPhiStar, axisPairLossDcaDaughters}); + histos.add("PairLossK0/V0QA/hRadiusVsMinDeltaPhiStar", "matched V0 radius versus minimum delta phi star", kTH2F, {axisPairLossMinDeltaPhiStar, axisPairLossRecoV0Radius}); + histos.add("PairLossK0/V0QA/hMassNSigmaVsMinDeltaPhiStar", "AssocV0 K0 mass n-sigma versus minimum delta phi star", kTH3F, {axisPairLossMinDeltaPhiStar, axisPairLossMassNSigma, axisPairLossTruthK0Pt}); + + auto eventCounter = histos.get(HIST("PairLossK0/Event/hCounter")); + const std::array eventLabels = {"MC collision", "has rec collision", "best collision selected", "has truth trigger", "has truth K0", "has truth pair"}; + for (size_t i = 0; i < eventLabels.size(); ++i) { + eventCounter->GetXaxis()->SetBinLabel(i + 1, eventLabels[i].data()); + } + eventCounter->GetYaxis()->SetTitle("Entries"); + histos.get(HIST("PairLossK0/Event/hNRecoCollisions"))->GetYaxis()->SetTitle("MC collisions"); + auto setStageAxisLabels = [&](TAxis* stageAxis) { + for (int i = 0; i < PairLossK0NStages; ++i) { + stageAxis->SetBinLabel(i + 1, PairLossK0StageNames[i].data()); + } + }; + auto setStageLabels = [&](auto const& stageHistogram) { + setStageAxisLabels(stageHistogram->GetXaxis()); + }; + auto stageCounts = histos.get(HIST("PairLossK0/Stage/hCounts")); + auto stageCountsFindable = histos.get(HIST("PairLossK0/Stage/hCountsFindable")); + setStageLabels(stageCounts); + setStageLabels(stageCountsFindable); + setStageAxisLabels(histos.get(HIST("PairLossK0/Stage/hTriggers"))->GetAxis(0)); + stageCounts->GetYaxis()->SetTitle("Truth-pair entries"); + stageCountsFindable->GetYaxis()->SetTitle("Findable truth-pair entries"); + + auto triggerTracksFailureHistogram = histos.get(HIST("PairLossK0/Stage/hTriggerTracksFailureReason")); + for (int i = 0; i < PairLossTriggerTracksNReasons; ++i) { + triggerTracksFailureHistogram->GetAxis(0)->SetBinLabel(i + 1, PairLossTriggerTracksFailureNames[i].data()); + } + + const std::array objectStateLabels = {"00 neither", "01 K0 only", "10 trigger only", "11 both"}; + for (auto const& histogram : {histos.get(HIST("PairLossK0/State/hFinalObjectStatePhysics")), histos.get(HIST("PairLossK0/State/hFinalObjectStateClose"))}) { + for (size_t i = 0; i < objectStateLabels.size(); ++i) { + histogram->GetAxis(0)->SetBinLabel(i + 1, objectStateLabels[i].data()); + } + } + const std::array trackLevelStateLabels = {"00 neither", "01 both daughters only", "10 trigger only", "11 trigger + both daughters"}; + auto trackLevelStateHistogram = histos.get(HIST("PairLossK0/State/hTrackLevelStateClose")); + for (size_t i = 0; i < trackLevelStateLabels.size(); ++i) { + trackLevelStateHistogram->GetAxis(0)->SetBinLabel(i + 1, trackLevelStateLabels[i].data()); + } + const std::array daughterStateLabels = {"none", "negative only", "positive only", "both"}; + auto daughterStateHistogram = histos.get(HIST("PairLossK0/State/hDaughterTrackStateClose")); + for (size_t i = 0; i < daughterStateLabels.size(); ++i) { + daughterStateHistogram->GetAxis(0)->SetBinLabel(i + 1, daughterStateLabels[i].data()); + } + + auto deltaPhiStarValidityHistogram = histos.get(HIST("PairLossK0/Geometry/hDeltaPhiStarValidity")); + deltaPhiStarValidityHistogram->GetXaxis()->SetBinLabel(1, "invalid"); + deltaPhiStarValidityHistogram->GetXaxis()->SetBinLabel(2, "valid"); + const std::array matchLabels = {"trigger all collisions", "trigger best collision", "trigger final", "V0 all collisions", "V0 best collision", "V0 AssocV0s", "V0 final"}; + auto matchHistogram = histos.get(HIST("PairLossK0/Matching/hNMatches")); + for (size_t i = 0; i < matchLabels.size(); ++i) { + matchHistogram->GetXaxis()->SetBinLabel(i + 1, matchLabels[i].data()); + } + + for (auto const& histogram : {histos.get(HIST("PairLossK0/TrackQA/hTriggerITSPairWeighted")), + histos.get(HIST("PairLossK0/TrackQA/hPositiveDaughterITSPairWeighted")), + histos.get(HIST("PairLossK0/TrackQA/hNegativeDaughterITSPairWeighted"))}) { + histogram->GetZaxis()->SetBinLabel(1, "no"); + histogram->GetZaxis()->SetBinLabel(2, "yes"); + } + + for (auto const& histogram : {deltaPhiStarValidityHistogram, + matchHistogram, + histos.get(HIST("PairLossK0/Response/hTriggerPt")), + histos.get(HIST("PairLossK0/Response/hK0Pt")), + histos.get(HIST("PairLossK0/Response/hDeltaPhi")), + histos.get(HIST("PairLossK0/Response/hDeltaEta")), + histos.get(HIST("PairLossK0/V0QA/hCosPAVsMinDeltaPhiStar")), + histos.get(HIST("PairLossK0/V0QA/hDcaDaughtersVsMinDeltaPhiStar")), + histos.get(HIST("PairLossK0/V0QA/hRadiusVsMinDeltaPhiStar"))}) { + histogram->GetZaxis()->SetTitle("Entries"); + } + } + + if (pairLossK0Configurations.applyINELSelectionAtGenLevel) { + if (!doprocessPairLossK0MC || !pairLossK0Configurations.doRecComparison) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel acts on the Rec/Gen/Truth/Final stages of part 2; enable processPairLossK0MC and pairLossK0Configurations.doRecComparison"); + } + if (!masterConfigurations.doPPAnalysis) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel is only defined for pp: the Pb-Pb event selection has no INEL cut"); + } + if (!masterConfigurations.applyNewMCSelection) { + LOGF(fatal, "pairLossK0Configurations.applyINELSelectionAtGenLevel requires masterConfigurations.applyNewMCSelection=true so that the Gen stage uses the same event selection function as the Rec stage"); + } + if (masterConfigurations.selectINELgtN) { + LOGF(warning, "pairLossK0Configurations.applyINELSelectionAtGenLevel: masterConfigurations.selectINELgtN is ignored in the PairLossK0/Comparison stages, which select Nch^gen >= 1 only"); + } + } + + if (pairLossK0Configurations.applyNchResponseBandCut && !doprocessPairLossK0MC) { + LOGF(fatal, "pairLossK0Configurations.applyNchResponseBandCut needs the generated multiplicity; enable processPairLossK0MC"); + } + + if (doprocessPairLossK0MC && pairLossK0Configurations.doEventRecoEfficiencyStudy) { + // Per MC collision with |vz_gen| <= zVertexCut, each histogram a subset of + // the previous one: HasRecoColl/All is the MC-collision reconstruction + // efficiency vs Nch^gen. The event selection is the one of the Rec path + // (trigger presence + isCollisionSelected) evaluated on the best collision + histos.add("PairLossK0/EventRecoEfficiency/hNchGenAll", "all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl", "MC collisions with at least one reconstructed collision;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel", "best reconstructed collision passes the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSel", "best reconstructed collision passes the Rec event selection with reconstructed-level INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl", "all MC collisions;#it{N}_{ch}^{gen} (|#eta| < 1);reconstructed collisions", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNRecoCollisions}); + // Per reconstructed collision passing the Rec event selection without INEL + // cuts, no |vz_gen| cut: the same collisions the Rec path sees before its INEL cut. + // With nchGenResponseBestCollisionOnly the Nch^gen response takes only the best + // collision, which drops the split and duplicate vertices of the MC collision + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1", + pairLossK0Configurations.nchGenResponseBestCollisionOnly ? "best reconstructed collision passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1" + : "reconstructed collisions passing the Rec event selection without INEL cuts;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", + kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisCentFT0M}); + histos.add("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisMultFT0M}); + if (pairLossK0Configurations.applyNchResponseBandCut) { + // Checks of applyNchResponseBandCut: the part of the Nch^gen response it keeps, + // and hNchGenEvSelNoInelInBand / hNchGenEvSelNoInel, its efficiency vs Nch^gen + histos.add("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand", "hNchGenVsNTracksPVeta1 inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);multNTracksPVeta1", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisNchGen}); + histos.add("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand", "best reconstructed collision passes the Rec event selection without INEL cuts and lies inside the Nch^gen vs multNTracksPVeta1 band;#it{N}_{ch}^{gen} (|#eta| < 1);MC collisions", kTH1D, {axesConfigurations.axisNchGen}); + } + } + + if (doprocessPairLossK0MC && pairLossK0Configurations.doPVQualityStudy) { + // One entry per MC collision with |vz_gen| <= zVertexCut, in every truth-content + // class it belongs to (PairLossPVQualityClass): the classes overlap. + // hEventStage: stages nested as in PairLossK0/EventRecoEfficiency, so + // HasRecoColl/All and Selected/HasRecoColl per class give whether events with a + // trigger and a K0 lose their PV, or have it rejected, more often. + // hPos*: the data-like check -- the beam spot is common to all collisions, so a + // broadening or tail of the best-collision x and y towards low multNTracksPVeta1 + // is a reconstruction effect. hRes*: the same with the generated vertex + // subtracted. Selection bin 0 holds every best collision, bin 1 only those passing + // the event selection of PairLossK0/Comparison, so bin 1 is a subset of bin 0. + const AxisSpec axisPVQualityClass{PairLossPVQualityNClasses, -0.5, static_cast(PairLossPVQualityNClasses) - 0.5, "truth content of the MC collision"}; + const AxisSpec axisPVQualityStage{PairLossPVQualityNStages, -0.5, static_cast(PairLossPVQualityNStages) - 0.5, "MC-collision stage"}; + const AxisSpec axisPVQualitySelection{2, -0.5, 1.5, "best collision"}; + const AxisSpec axisPVQualityNchGen{axesConfigurations.axisNchGen, "#it{N}_{ch}^{gen} (|#eta| < 1)"}; + const AxisSpec axisPVQualityNTracksPVeta1{pairLossK0Configurations.axisPVQualityNTracks, "multNTracksPVeta1"}; + const AxisSpec axisPVQualityPosX{pairLossK0Configurations.axisPVPosition, "#it{x}_{PV}^{reco} (cm)"}; + const AxisSpec axisPVQualityPosY{pairLossK0Configurations.axisPVPosition, "#it{y}_{PV}^{reco} (cm)"}; + const AxisSpec axisPVQualityResX{pairLossK0Configurations.axisPVResidual, "#it{x}_{PV}^{reco} #minus #it{x}_{PV}^{gen} (cm)"}; + const AxisSpec axisPVQualityResY{pairLossK0Configurations.axisPVResidual, "#it{y}_{PV}^{reco} #minus #it{y}_{PV}^{gen} (cm)"}; + const AxisSpec axisPVQualityResZ{pairLossK0Configurations.axisPVResidual, "#it{z}_{PV}^{reco} #minus #it{z}_{PV}^{gen} (cm)"}; + histos.add("PairLossK0/PVQuality/hEventStage", "MC collisions by truth content and stage", kTH3D, {axisPVQualityClass, axisPVQualityStage, axisPVQualityNchGen}); + histos.add("PairLossK0/PVQuality/hPosX", "best-collision PV x", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityPosX}); + histos.add("PairLossK0/PVQuality/hPosY", "best-collision PV y", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityPosY}); + histos.add("PairLossK0/PVQuality/hResX", "best-collision PV x residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResX}); + histos.add("PairLossK0/PVQuality/hResY", "best-collision PV y residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResY}); + histos.add("PairLossK0/PVQuality/hResZ", "best-collision PV z residual", kTHnF, {axisPVQualityClass, axisPVQualitySelection, axisPVQualityNTracksPVeta1, axisPVQualityResZ}); + + auto eventStage = histos.get(HIST("PairLossK0/PVQuality/hEventStage")); + for (int i = 0; i < PairLossPVQualityNClasses; ++i) { + eventStage->GetXaxis()->SetBinLabel(i + 1, PairLossPVQualityClassNames[i].data()); + } + for (int i = 0; i < PairLossPVQualityNStages; ++i) { + eventStage->GetYaxis()->SetBinLabel(i + 1, PairLossPVQualityStageNames[i].data()); + } + for (auto const& pvHistogram : {histos.get(HIST("PairLossK0/PVQuality/hPosX")), histos.get(HIST("PairLossK0/PVQuality/hPosY")), + histos.get(HIST("PairLossK0/PVQuality/hResX")), histos.get(HIST("PairLossK0/PVQuality/hResY")), + histos.get(HIST("PairLossK0/PVQuality/hResZ"))}) { + for (int i = 0; i < PairLossPVQualityNClasses; ++i) { + pvHistogram->GetAxis(0)->SetBinLabel(i + 1, PairLossPVQualityClassNames[i].data()); + } + pvHistogram->GetAxis(1)->SetBinLabel(1, "all"); + pvHistogram->GetAxis(1)->SetBinLabel(2, "event selection"); + } + } + + if (doprocessSameEventHV0s && masterConfigurations.doMultEstimatorResponse) { + // Same histograms and event selection as the multNTracksPVeta1 vs FT0M responses of + // PairLossK0/EventRecoEfficiency, filled per reconstructed collision in data + // (or in MC reco, which then reproduces the PairLossK0 ones) + histos.add("MultEstimatorResponse/hNTracksPVeta1VsCentFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M percentile", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisCentFT0M}); + histos.add("MultEstimatorResponse/hNTracksPVeta1VsMultFT0M", "reconstructed collisions passing the Rec event selection without INEL cuts;multNTracksPVeta1;FT0M amplitude", kTH2D, {axesConfigurations.axisNchGen, axesConfigurations.axisMultFT0M}); + } + + if (doprocessPairLossK0MC && pairLossK0Configurations.doRecComparison) { + if (doprocessSameEventHV0s) { + LOGF(fatal, "pairLossK0Configurations.doRecComparison already runs the exact Rec path internally; set processSameEventHV0s=false to avoid double filling"); + } + if (pairLossK0Configurations.applyINELSelectionAtGenLevel) { + // Only Rec and Gen, with the Nch^gen of the MC collision in place of the + // vertex-z and multiplicity axes (memory), for a closure test per Nch^gen + // class; the rest of the Comparison ladder (Truth, Final, variants, set + // differences) is neither booked nor filled + const AxisSpec axisNchGenClass{pairLossK0Configurations.axisComparisonNchGen, "#it{N}_{ch}^{gen} (|#eta| < 1)"}; + histos.add("PairLossK0/Comparison/NchGen/Rec", "Comparison/Rec vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecPeakTrueK0", "Comparison/RecPeakTrueK0 vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecLeftBg", "Comparison/RecLeftBg vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/RecRightBg", "Comparison/RecRightBg vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/Gen", "Comparison/Gen vs Nch^gen, integrated over vertex z and multiplicity", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisNchGenClass}); + // Trigger counts for the per-trigger normalisation of the pairs above, with + // the same trigger and event selections: TriggerRec next to + // sameEvent/TriggerParticlesV0 with the same trigger weight, TriggerGen in + // the Gen trigger loop, including events without a K0 + histos.add("PairLossK0/Comparison/NchGen/TriggerRec", "Rec triggers;#it{p}_{T}^{trigger} (GeV/#it{c});#it{N}_{ch}^{gen} (|#eta| < 1)", kTH2D, {axesConfigurations.axisPtQA, axisNchGenClass}); + histos.add("PairLossK0/Comparison/NchGen/TriggerGen", "Gen triggers;#it{p}_{T}^{trigger} (GeV/#it{c});#it{N}_{ch}^{gen} (|#eta| < 1)", kTH2D, {axesConfigurations.axisPtQA, axisNchGenClass}); + } else { + constexpr int PairLossComparisonNVariants = 16; + const AxisSpec axisPairLossComparisonVariant{PairLossComparisonNVariants, -0.5, PairLossComparisonNVariants - 0.5, "cumulative Rec control variant"}; + constexpr int PairLossComparisonNTriggerVariants = 10; + const AxisSpec axisPairLossComparisonTriggerVariant{PairLossComparisonNTriggerVariants, -0.5, PairLossComparisonNTriggerVariants - 0.5, "cumulative Rec trigger control variant"}; + histos.add("PairLossK0/Comparison/Rec", "exact Rec peak-window h-K0 pairs; all candidates", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecPeakTrueK0", "exact Rec peak-window pairs with a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecPeakFakeK0", "exact Rec peak-window pairs without a true K0", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecLeftBg", "exact Rec left invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecRightBg", "exact Rec right invariant-mass sideband pairs", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Truth", "PairLoss truth pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Gen", "Closure-test Gen pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/Final", "PairLoss final truth-matched pairs; same axes as Rec", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecVariants", "cumulative controlled variants of the Rec pair definition", kTHnF, {axisPairLossComparisonVariant, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + // The two directional set differences between Rec variant 14 and the + // exact old Final definition. They make non-nested pair definitions + // explicit instead of hiding the mismatch in a single yield ratio. + histos.add("PairLossK0/Comparison/FinalNotInRec", "old-Final truth pairs absent from Rec variant 14", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("PairLossK0/Comparison/RecNotInFinal", "Rec variant-14 truth pairs absent from old Final", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + // Trigger-side counterpart of the pair ladder. Pair variant i has to be + // divided by the trigger variant listed in the label to obtain a + // per-trigger yield that is narrowed by the same cumulative conditions. + histos.add("PairLossK0/Comparison/TriggerVariants", "cumulative controlled variants of the Rec trigger normalisation", kTHnF, {axisPairLossComparisonTriggerVariant, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + + const std::array variantLabels = { + "0 exact Rec", + "1 + truth-trigger match/acceptance", + "2 + true K0 (remove peak background)", + "3 + truth-K0 match/acceptance", + "4 + truth daughter exclusion", + "5 + truth-trigger primary", + "6 + truth-K0 primary", + "7 + old truth-leading choice", + "8 + best collision", + "9 + truth pT coordinates only", + "10 + truth event coordinates (vtxZ, mult)", + "11 + truth delta eta only", + "12 + truth delta phi and truth angular range", + "13 + unit pair weight", + "14 + one entry per truth pair", + "15 exact old Final reference"}; + auto recVariants = histos.get(HIST("PairLossK0/Comparison/RecVariants")); + for (size_t i = 0; i < variantLabels.size(); ++i) { + recVariants->GetAxis(0)->SetBinLabel(i + 1, variantLabels[i].data()); + } + + // Pair variant -> trigger variant pairing for per-trigger yields: + // pair 0-3 with trigger 0-1, pair 4-6 with trigger 2, pair 7 with + // trigger 3, pair 8-9 with trigger 4, pair 10-12 with trigger 6, + // pair 13-14 with trigger 7-8, pair 15 with trigger 9. + const std::array triggerVariantLabels = { + "0 exact Rec trigger", + "1 + truth-trigger match/acceptance", + "2 + truth-trigger primary", + "3 + old truth-leading choice", + "4 + best collision", + "5 + truth pT coordinate only", + "6 + truth event coordinates (vtxZ, mult)", + "7 + unit trigger weight", + "8 + one entry per truth trigger", + "9 exact old Final trigger reference"}; + auto triggerVariants = histos.get(HIST("PairLossK0/Comparison/TriggerVariants")); + for (size_t i = 0; i < triggerVariantLabels.size(); ++i) { + triggerVariants->GetAxis(0)->SetBinLabel(i + 1, triggerVariantLabels[i].data()); + } + + // Where the Rec triggers lost at the variant 0 -> 1 step fail, per trigger and per + // pair, with the weights and reconstructed coordinates of the variants: the All and + // Matched bins of hTrigger integrate to TriggerVariants 0 and 1, those of hPair to + // RecVariants 0 and 1. hPair keeps the angular axes so that the shape of the lost + // pairs (correlated or flat) is visible. + const AxisSpec axisPairLossTriggerMatchQA{PairLossTriggerMatchNBins, -0.5, static_cast(PairLossTriggerMatchNBins) - 0.5, "Rec variant 0 #rightarrow 1 trigger match"}; + histos.add("PairLossK0/Comparison/TriggerMatchQA/hTrigger", "Rec triggers by first failing truth-trigger condition;Rec variant 0 #rightarrow 1 trigger match;#it{p}_{T}^{trigger, reco} (GeV/#it{c})", kTH2D, {axisPairLossTriggerMatchQA, axesConfigurations.axisPtQA}); + histos.add("PairLossK0/Comparison/TriggerMatchQA/hPair", "Rec peak-window h-K0 pairs by first failing truth-trigger condition of their trigger", kTHnF, {axisPairLossTriggerMatchQA, axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim}); + auto triggerMatchQATrigger = histos.get(HIST("PairLossK0/Comparison/TriggerMatchQA/hTrigger")); + auto triggerMatchQAPair = histos.get(HIST("PairLossK0/Comparison/TriggerMatchQA/hPair")); + for (int i = 0; i < PairLossTriggerMatchNBins; ++i) { + triggerMatchQATrigger->GetXaxis()->SetBinLabel(i + 1, PairLossTriggerMatchQANames[i].data()); + triggerMatchQAPair->GetAxis(0)->SetBinLabel(i + 1, PairLossTriggerMatchQANames[i].data()); + } + } + } + + if (doprocessPairLossK0MC && pairLossK0Configurations.doGenLevelStudy) { + const AxisSpec axisGenStudyNch{pairLossK0Configurations.axisGenStudyNch, "#it{N}_{ch}^{gen} (|#eta| < 0.8)"}; + const AxisSpec axisGenStudyEventStage{4, -0.5, 3.5, "Generated-event selection stage"}; + // Findability of the K0, in exactly the sense the stage ladder uses: it + // decayed to pi+ pi- and both charged daughters are inside the tracking + // acceptance set by daughterPtMin / daughterEtaMax. Kept as an axis rather + // than as a separate folder so that the inclusive and the findable-only + // answer come out of one and the same object. + const AxisSpec axisGenStudyFindable{2, -0.5, 1.5, "K^{0}_{S} findable"}; + + histos.add("PairLossK0/GenStudy/hEventCounter", "generator-level event selection", kTH1F, {axisGenStudyEventStage}); + histos.add("PairLossK0/GenStudy/hNch", "generated charged multiplicity of selected MC collisions", kTH1F, {axisGenStudyNch}); + histos.add("PairLossK0/GenStudy/hNRecoCollisions", "reconstructed collisions per selected MC collision", kTH1F, {{11, -0.5, 10.5}}); + + // Gen/ holds every generated object that passes the generator-level + // selection; Reconstructed/ and NotReconstructed/ split that same set by + // whether the object has a reconstructed counterpart. All three are filled + // with generated coordinates, so Gen == Reconstructed + NotReconstructed + // bin by bin and NotReconstructed/Gen reads directly as the loss. + histos.add("PairLossK0/GenStudy/Gen/hTrigger", "generated triggers;#it{p}_{T}^{gen} (GeV/#it{c});#eta^{gen};#varphi^{gen};#it{N}_{ch}^{gen}", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axisGenStudyNch}); + histos.add("PairLossK0/GenStudy/Gen/hK0Short", "generated K0s;#it{p}_{T}^{gen} (GeV/#it{c});#eta^{gen};#varphi^{gen};#it{N}_{ch}^{gen};findable", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axisGenStudyNch, axisGenStudyFindable}); + // h-K0 correlations of the very same objects. Gen/ is every generated pair and the + // four exclusive classes below split it by which of the two objects was + // reconstructed, so Reconstructed + OnlyTriggerReconstructed + OnlyK0Reconstructed + // + NotReconstructed equals Gen bin by bin. + histos.add("PairLossK0/GenStudy/Gen/hCorrelation", "generated h-K0s pairs;#Delta#eta;#Delta#varphi;#it{p}_{T}^{trigger} (GeV/#it{c});#it{p}_{T}^{K^{0}_{S}} (GeV/#it{c});#it{N}_{ch}^{gen}", kTHnF, {axisDeltaEtaNDim, axisDeltaPhiNDim, axisPtTriggerNDim, axisPtAssocNDim, axisGenStudyNch}); + histos.addClone("PairLossK0/GenStudy/Gen/", "PairLossK0/GenStudy/Reconstructed/"); + histos.addClone("PairLossK0/GenStudy/Gen/", "PairLossK0/GenStudy/NotReconstructed/"); + // Only the correlation exists for the two mixed classes -- a single particle is + // either reconstructed or not, so cloning the single-particle folders here would + // only produce histograms with no meaning. + histos.add("PairLossK0/GenStudy/OnlyTriggerReconstructed/hCorrelation", "h-K0s pairs with only the trigger reconstructed;#Delta#eta;#Delta#varphi;#it{p}_{T}^{trigger} (GeV/#it{c});#it{p}_{T}^{K^{0}_{S}} (GeV/#it{c});#it{N}_{ch}^{gen}", kTHnF, {axisDeltaEtaNDim, axisDeltaPhiNDim, axisPtTriggerNDim, axisPtAssocNDim, axisGenStudyNch}); + histos.add("PairLossK0/GenStudy/OnlyK0Reconstructed/hCorrelation", "h-K0s pairs with only the K0s reconstructed;#Delta#eta;#Delta#varphi;#it{p}_{T}^{trigger} (GeV/#it{c});#it{p}_{T}^{K^{0}_{S}} (GeV/#it{c});#it{N}_{ch}^{gen}", kTHnF, {axisDeltaEtaNDim, axisDeltaPhiNDim, axisPtTriggerNDim, axisPtAssocNDim, axisGenStudyNch}); + + for (auto const& histogram : {histos.get(HIST("PairLossK0/GenStudy/Gen/hK0Short")), + histos.get(HIST("PairLossK0/GenStudy/Reconstructed/hK0Short")), + histos.get(HIST("PairLossK0/GenStudy/NotReconstructed/hK0Short"))}) { + histogram->GetAxis(4)->SetBinLabel(1, "not findable"); + histogram->GetAxis(4)->SetBinLabel(2, "findable"); + } + + auto genStudyEventCounter = histos.get(HIST("PairLossK0/GenStudy/hEventCounter")); + const std::array genStudyEventLabels = {"MC collisions", "INEL>0 (generated)", "|vtx z| < cut (generated)", "has >= 1 rec collision"}; + for (size_t i = 0; i < genStudyEventLabels.size(); ++i) { + genStudyEventCounter->GetXaxis()->SetBinLabel(i + 1, genStudyEventLabels[i].data()); + } + genStudyEventCounter->GetYaxis()->SetTitle("MC collisions"); + } if (doprocessMixedEventHV0sInBuffer || doprocessMixedEventHCascadesInBuffer) { validCollisions.resize(histos.get(HIST("axes/hMultAxis"))->GetNbinsX() * histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX()); - for (std::vector& inner_vec : validCollisions) { - inner_vec.reserve(masterConfigurations.mixingParameter); + for (size_t i = 0; i < validCollisions.size(); ++i) { + validCollisions[i].reserve(masterConfigurations.mixingParameter); + } + } + if (masterConfigurations.mixingEventForClosureTest) { + genLevelCollisions.resize(histos.get(HIST("axes/hMultAxis"))->GetNbinsX() * + histos.get(HIST("axes/hVertexZAxis"))->GetNbinsX()); + for (size_t i = 0; i < genLevelCollisions.size(); ++i) { + genLevelCollisions[i].reserve(masterConfigurations.mixingParameter); } } if (!masterConfigurations.doPPAnalysis) { // event selections in Pb-Pb - histos.add("hEventSelection", "hEventSelection", kTH1F, {{10, 0, 10}}); - TString eventSelLabel[] = {"all", "sel8", "kIsTriggerTVX", "PV_{z}", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "OccupCut", "kNoITSROFrameBorder", "kNoSameBunchPileup ", " kNoCollInTimeRangeStandard"}; + histos.add("hEventSelection", "hEventSelection", kTH1F, {{11, 0, 11}}); + std::array eventSelLabel = {"all", "sel8", "kIsTriggerTVX", "Centrality", "PV_{z}", "kIsGoodITSLayersAll", "kIsGoodZvtxFT0vsPV", "OccupCut", "kNoTimeFrameBorder", "kNoITSROFrameBorder", "kNoSameBunchPileup "}; for (int i = 1; i <= histos.get(HIST("hEventSelection"))->GetNbinsX(); i++) { - histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); + histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1].Data()); } } // Some QA plots @@ -1729,7 +3231,7 @@ struct HStrangeCorrelation { if (doprocessMCGenerated || doprocessClosureTest) { histos.add("hClosureTestEventCounter", "hClosureTestEventCounter", kTH1F, {{10, 0, 10}}); } - if (doprocessSameEventHV0s || doprocessSameEventHCascades || doprocessSameEventHPions || doprocessSameEventHHadrons) { + if (doprocessSameEventHV0s || doprocessPairLossK0MC || doprocessSameEventHCascades || doprocessSameEventHPions || doprocessSameEventHHadrons) { histos.add("hTriggerAllSelectedEtaVsPt", "hTriggerAllSelectedEtaVsPt", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); histos.add("hPositiveTriggerPrimaryEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); histos.add("hNegativeTriggerPrimaryEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); @@ -1742,13 +3244,22 @@ struct HStrangeCorrelation { } histos.add("hTrackEtaVsPtVsPhi", "hTrackEtaVsPtVsPhi", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); histos.add("hAssocTrackEtaVsPtVsPhi", "hAssocTrackEtaVsPtVsPhi", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + if (masterConfigurations.doLocalDensityStudy) { + histos.add("hTrackEtaVsPtVsLocalDensity", "hTrackEtaVsPtVsLocalDensity", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisLocalDensity}); + } // histos.add("hTrackAttempt", "Attempt", kTH3F, {axisPtQA, axisEta, axisPhi}); } if (doprocessSameEventHPions || doprocessSameEventHHadrons || doprocessMixedEventHPions || doprocessMixedEventHHadrons) { histos.add("hNumberOfRejectedPairsHadron", "hNumberOfRejectedPairsHadron", kTH1F, {{1, 0, 1}}); histos.add("hNumberOfRejectedPairsPion", "hNumberOfRejectedPairsPion", kTH1F, {{1, 0, 1}}); } - if (doprocessSameEventHV0s || doprocessMixedEventHV0s) { + if (doprocessSameEventHHadrons) { + histos.add("hDCAzTriggerHadron", "hDCAzTriggerHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAxyTriggerHadron", "hDCAxyTriggerHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAzAssociatedHadron", "hDCAzAssociatedHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + histos.add("hDCAxyAssociatedHadron", "hDCAxyAssociatedHadron", kTH2F, {{200, -0.5, 0.5}, axesConfigurations.axisPtQA}); + } + if (doprocessSameEventHV0s || doprocessPairLossK0MC || doprocessMixedEventHV0s) { histos.add("hNumberOfRejectedPairsV0", "hNumberOfRejectedPairsV0", kTH1F, {{1, 0, 1}}); } if (doprocessSameEventHCascades || doprocessMixedEventHCascades) { @@ -1768,12 +3279,12 @@ struct HStrangeCorrelation { histos.add("EventQA/hPvz", ";pvz;Entries", kTH1F, {{30, -15, 15}}); histos.add("EventQA/hMultFT0vsTPC", ";centFT0M;multNTracksPVeta1", kTH2F, {{100, 0, 100}, {300, 0, 300}}); } - bool hStrange = false; for (int i = 0; i < AssocParticleTypes; i++) { if (TESTBIT(doCorrelation, i)) { - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { histos.add(fmt::format("sameEvent/Signal/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } if (doDeltaPhiStarCheck && masterConfigurations.doFullCorrelationStudy) { histos.add(fmt::format("sameEvent/Signal/{}DeltaPhiStar", Particlenames[i]).c_str(), "", kTH3F, {{100, -0.3, 0.3}, {50, -0.05, 0.05}, {2, -1, 1}}); // -1 oposite charge, 1 same charge } @@ -1785,6 +3296,10 @@ struct HStrangeCorrelation { histos.add(fmt::format("h{}EtaVsPtVsPhiVsCent", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); histos.add(fmt::format("h{}EtaVsPtVsPhiVsCentBg", Particlenames[i]).c_str(), "", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi, axesConfigurations.axisMult}); } + if (masterConfigurations.doLocalDensityStudy && i < AssocV0Types) { + histos.add(fmt::format("h{}EtaVsPtVsLocalDensity", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisLocalDensity}); + histos.add(fmt::format("h{}EtaVsPtVsLocalDensityBg", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisLocalDensity}); + } histos.add(fmt::format("h3d{}Spectrum", Particlenames[i]).c_str(), fmt::format("h3d{}Spectrum", Particlenames[i]).c_str(), kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult, axesConfigurations.axisMassNSigma}); histos.add(fmt::format("h3d{}SpectrumY", Particlenames[i]).c_str(), fmt::format("h3d{}SpectrumY", Particlenames[i]).c_str(), kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult, axesConfigurations.axisMassNSigma}); hStrange = true; @@ -1797,13 +3312,13 @@ struct HStrangeCorrelation { } } } - if (TESTBIT(doCorrelation, 7)) { + if (TESTBIT(doCorrelation, 7) && doprocessSameEventHPions) { histos.add("hPionEtaVsPtAllSelected", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); histos.add("hPionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); histos.add("hPositivePionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); histos.add("hNegativePionEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); } - if (TESTBIT(doCorrelation, 8)) { + if (TESTBIT(doCorrelation, 8) && doprocessSameEventHHadrons) { histos.add("hAsssocTrackEtaVsPtVsPhi", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); histos.add("hAssocPrimaryEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); histos.add("hAssocHadronsAllSelectedEtaVsPt", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); @@ -1815,19 +3330,63 @@ struct HStrangeCorrelation { histos.addClone("sameEvent/Signal/", "sameEvent/RightBg/"); } + if (masterConfigurations.doFullCorrelationStudy && (doprocessSameEventHV0s || doprocessPairLossK0MC) && masterConfigurations.doCorrelationsHadronV0daughter) { + if (TESTBIT(doCorrelation, 0)) { + histos.add("sameEvent/Signal/K0Short_hSameSign", "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("sameEvent/Signal/K0Short_hOppositeSign", "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (TESTBIT(doCorrelation, 1)) { + histos.add("sameEvent/Signal/Lambda_hSameSign", "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("sameEvent/Signal/Lambda_hOppositeSign", "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (TESTBIT(doCorrelation, 2)) { + histos.add("sameEvent/Signal/AntiLambda_hSameSign", "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("sameEvent/Signal/AntiLambda_hOppositeSign", "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + } + if (TESTBIT(doCorrelation, 0) && (doprocessSameEventHV0s || doprocessPairLossK0MC) && masterConfigurations.doMassSpectrumCheck) { + histos.add("hK0ShortPtVsMass", "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisK0ShortMass}); + } + if (TESTBIT(doCorrelation, 1) && (doprocessSameEventHV0s || doprocessPairLossK0MC) && masterConfigurations.doMassSpectrumCheck) { + histos.add("hLambdaPtVsMass", "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisLambdaMass}); + } + if (TESTBIT(doCorrelation, 2) && (doprocessSameEventHV0s || doprocessPairLossK0MC) && masterConfigurations.doMassSpectrumCheck) { + histos.add("hAntiLambdaPtVsMass", "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisLambdaMass}); + } LOGF(info, "Init THnFs done"); // mixed-event correlation functions if ((doprocessMixedEventHV0sInBuffer || doprocessMixedEventHCascadesInBuffer || doprocessMixedEventHV0s || doprocessMixedEventHCascades || doprocessMixedEventHPions || doprocessMixedEventHHadrons) && masterConfigurations.doFullCorrelationStudy) { histos.addClone("sameEvent/", "mixedEvent/"); } - if (doprocessSameEventHHadrons && masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy && (doprocessSameEventHV0s || doprocessPairLossK0MC)) { + if (TESTBIT(doCorrelation, 0)) { + histos.add("sameEvent/InvariantMass/K0Short/hNearSide", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisK0ShortMass}); + histos.add("sameEvent/InvariantMass/K0Short/hAwaySide", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisK0ShortMass}); + histos.add("sameEvent/InvariantMass/K0Short/hUnderlyingEvent", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisK0ShortMass}); + } + if (TESTBIT(doCorrelation, 1)) { + histos.add("sameEvent/InvariantMass/Lambda/hNearSide", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisLambdaMass}); + histos.add("sameEvent/InvariantMass/Lambda/hAwaySide", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisLambdaMass}); + histos.add("sameEvent/InvariantMass/Lambda/hUnderlyingEvent", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisLambdaMass}); + } + if (TESTBIT(doCorrelation, 2)) { + histos.add("sameEvent/InvariantMass/AntiLambda/hNearSide", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisLambdaMass}); + histos.add("sameEvent/InvariantMass/AntiLambda/hAwaySide", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisLambdaMass}); + histos.add("sameEvent/InvariantMass/AntiLambda/hUnderlyingEvent", "", kTH3F, {axesConfigurations.axisPtAssoc, axesConfigurations.axisPtTrigger, axesConfigurations.axisLambdaMass}); + } + } + if (doprocessSameEventHHadrons && masterConfigurations.doFullCorrelationStudy) { histos.add("sameEvent/TriggerParticlesHadron", "TriggersHadron", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); - if (doprocessSameEventHV0s && masterConfigurations.doFullCorrelationStudy) + } + if ((doprocessSameEventHV0s || doprocessPairLossK0MC) && masterConfigurations.doFullCorrelationStudy) { histos.add("sameEvent/TriggerParticlesV0", "TriggersV0", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); - if (doprocessSameEventHCascades && masterConfigurations.doFullCorrelationStudy) + } + if (doprocessSameEventHCascades && masterConfigurations.doFullCorrelationStudy) { histos.add("sameEvent/TriggerParticlesCascade", "TriggersCascade", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); - if (doprocessSameEventHPions && masterConfigurations.doFullCorrelationStudy) + } + if (doprocessSameEventHPions && masterConfigurations.doFullCorrelationStudy) { histos.add("sameEvent/TriggerParticlesPion", "TriggersPion", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); + } // MC generated plots if (doprocessMCGenerated) { @@ -1851,6 +3410,15 @@ struct HStrangeCorrelation { } histos.addClone("Generated/", "GeneratedWithPV/"); + // The density axis is the reconstructed one on both sides: these generated histograms are counted + // from the tracks of the best collision, exactly like their reconstructed counterparts. + if (masterConfigurations.doLocalDensityStudy && masterConfigurations.doPPAnalysis) { + histos.add("GeneratedWithPV/hTriggerLocalDensity", "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisLocalDensity}); + for (int i = 0; i < AssocParticleTypesNoHadron; i++) { + histos.add(fmt::format("GeneratedWithPV/h{}LocalDensity", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisLocalDensity}); + } + } + // histograms within |y|<0.5, vs multiplicity for (int i = 0; i < AssocParticleTypesNoHadron; i++) { histos.add(fmt::format("GeneratedWithPV/h{}_MidYVsMult", Particlenames[i]).c_str(), "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisMult}); @@ -1858,13 +3426,103 @@ struct HStrangeCorrelation { } } if (doprocessClosureTest) { + if (pairLossK0Configurations.doClosureTestStages) { + // Naming inside ClosureTest/PairLossK0: each folder is one reconstruction + // requirement imposed on the same truth h-K0 pair. "Any" means the object + // must have at least one reconstructed counterpart (a track with a + // matching MC label for the trigger, a V0 candidate with a matching MC + // core for the K0) in any reconstructed collision associated with this MC + // collision, with no quality selection whatsoever. + // folder trigger K0 reconstruction requirement + // Truth truth truth none (PairLossGenPair) + // AnyTrack any truth trigger has a track in any collision + // AnyTrackK0 truth any K0 has a V0 candidate in any collision + // AnyTrackBoth any any both requirements at once + // The "any reconstructed collision" level exists only here: the + // processPairLossK0MC stage ladder is evaluated in the best collision. + // Final final final fully selected, both in one collision + // "final" means the object has a reconstructed counterpart that survives + // every selection the reconstructed correlation applies, and for the pair + // both counterparts must live in the same reconstructed collision. Final/ + // is therefore the direct truth-coordinate counterpart of the ordinary + // reconstructed correlation: Rec/Final isolates what is left once pair + // loss is divided out -- duplicate reconstructed objects, fakes and bin + // migration. + // Every folder has the same three objects -- sameEvent/K0Short, hTrigger, + // hK0Short -- and each of them is filled at the level its own folder + // prescribes, so a folder can be normalised without looking at any other. + // Consequence: the single-particle spectra repeat across folders in pairs + // (Truth/hTrigger == AnyTrackK0/hTrigger, AnyTrack/hTrigger == + // AnyTrackBoth/hTrigger, Truth/hK0Short == AnyTrack/hK0Short, + // AnyTrackK0/hK0Short == AnyTrackBoth/hK0Short). That is intended. + histos.add("ClosureTest/PairLossK0/Truth/sameEvent/K0Short", "truth h-K0 pairs with the processPairLossK0MC selections", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("ClosureTest/PairLossK0/AnyTrack/sameEvent/K0Short", "truth h-K0 pairs whose truth trigger has a reconstructed-track match in any associated collision", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("ClosureTest/PairLossK0/AnyTrackK0/sameEvent/K0Short", "truth h-K0 pairs whose truth K0 has a V0-candidate match in any associated collision", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("ClosureTest/PairLossK0/AnyTrackBoth/sameEvent/K0Short", "truth h-K0 pairs with both the trigger track match and the K0 V0-candidate match in any associated collision", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("ClosureTest/PairLossK0/Truth/hTrigger", "truth triggers with the processPairLossK0MC selections;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/Truth/hK0Short", "truth K0s with the processPairLossK0MC selections;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/AnyTrack/hTrigger", "truth triggers with a reconstructed-track match in any associated collision;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/AnyTrack/hK0Short", "truth K0s; the K0 stays at truth level in this folder;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/AnyTrackK0/hTrigger", "truth triggers; the trigger stays at truth level in this folder;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/AnyTrackK0/hK0Short", "truth K0s with a V0-candidate match in any associated collision;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/AnyTrackBoth/hTrigger", "truth triggers with a reconstructed-track match in any associated collision;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/AnyTrackBoth/hK0Short", "truth K0s with a V0-candidate match in any associated collision;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/Final/sameEvent/K0Short", "truth h-K0 pairs whose trigger and K0 both have a fully selected reconstructed counterpart in the same reconstructed collision", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add("ClosureTest/PairLossK0/Final/hTrigger", "truth triggers with a fully selected reconstructed counterpart;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + histos.add("ClosureTest/PairLossK0/Final/hK0Short", "truth K0s with a fully selected reconstructed counterpart;#it{p}_{T}^{truth} (GeV/#it{c});#eta^{truth};#varphi^{truth}", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + if (pairLossK0Configurations.doEventQualityStudy) { + if (pairLossK0Configurations.eventQualityDeltaPhiBins <= 0 || pairLossK0Configurations.eventQualityDeltaEtaBins <= 0) { + LOGF(fatal, "Event-quality angular bin counts must be positive"); + } + // Six sparse axes; omit vertex and multiplicity dimensions. Each folder + // has exactly three histograms, including its event/trigger denominators. + const std::vector qualityAxes = { + {6, -0.5, 5.5, "event class"}, + {3, -0.5, 2.5, "entry kind"}, + {pairLossK0Configurations.eventQualityDeltaPhiBins, axisRanges[0][0], axisRanges[0][1], "#Delta#varphi^{truth}"}, + {pairLossK0Configurations.eventQualityDeltaEtaBins, axisRanges[1][0], axisRanges[1][1], "#Delta#eta^{truth}"}, + axisPtAssocNDim, + axisPtTriggerNDim, + }; + for (int group = 0; group < PairLossNEventQualityGroups; ++group) { + for (auto const& stage : {"Truth", "AnyTrackBoth", "Final"}) { + auto histogram = histos.add(fmt::format("ClosureTest/PairLossK0/EventQuality/{}/{}", PairLossEventQualityNames[group], stage), "unit-weight counts; select entry kind before projection", kTHnSparseF, qualityAxes); + for (size_t bin = 0; bin < PairLossEventQualityLabels[group].size(); ++bin) { + histogram->GetAxis(0)->SetBinLabel(bin + 1, PairLossEventQualityLabels[group][bin].data()); + } + histogram->GetAxis(1)->SetBinLabel(1, "event (common baseline)"); + histogram->GetAxis(1)->SetBinLabel(2, "trigger"); + histogram->GetAxis(1)->SetBinLabel(3, "pair"); + } + } + } + } for (int i = 0; i < AssocParticleTypes; i++) { - if (TESTBIT(doCorrelation, i)) + if (TESTBIT(doCorrelation, i)) { histos.add(fmt::format("ClosureTest/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); - if (TESTBIT(doCorrelation, i)) + if (masterConfigurations.mixingEventForClosureTest) { + histos.add(fmt::format("ClosureTest/mixedEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { + histos.add(fmt::format("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/{}", Particlenames[i]).c_str(), "truth pairs whose trigger has at least one reconstructed track with a matching MC label", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + if (masterConfigurations.doCorrelationsHadronV0daughter) { + histos.add(fmt::format("ClosureTest/sameEvent/{}_SameSignDaughter", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add(fmt::format("ClosureTest/sameEvent/{}_OppSignDaughter", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { + histos.add(fmt::format("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/{}_SameSignDaughter", Particlenames[i]).c_str(), "truth trigger-daughter pairs whose trigger has a reconstructed-track match; same-sign daughter", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + histos.add(fmt::format("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/{}_OppSignDaughter", Particlenames[i]).c_str(), "truth trigger-daughter pairs whose trigger has a reconstructed-track match; opposite-sign daughter", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + } + } + if (TESTBIT(doCorrelation, i)) { histos.add(fmt::format("ClosureTest/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + } + } + histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTHnF, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult, axesConfigurations.axisPhi}); + if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { + histos.add("ClosureTest/TriggerWithRecoTrackMatch/hTrigger", "Truth triggers with at least one reconstructed track with a matching MC label;#it{p}_{T}^{truth} (GeV/c);#eta^{truth};centrality (%)", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); } - histos.add("ClosureTest/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); } if (doprocessFeedDown) { histos.add("hLambdaXiMinusFeeddownMatrix", "hLambdaXiMinusFeeddownMatrix", kTH2F, {axisPtLambda, axisPtCascade}); @@ -1882,13 +3540,58 @@ struct HStrangeCorrelation { histos.add("GeneratedWithPV/hAntiLambdaFromXiZero", "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta}); histos.add("GeneratedWithPV/hAntiLambdaFromXiPlus", "", kTH2F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta}); } - + if (doprocessPrediction) { + mCounter.mPdgDatabase = pdgDB.service; + mCounter.mSelectPrimaries = masterConfigurations.doAssocPhysicalPrimary.value; + histos.add("Prediction/hEventSelection", "hEventSelection", kTH1F, {{3, 0, 3}}); + std::array eventSelLabel = {"Read", "INELgt0", "|Z|<10"}; + for (int i = 1; i <= histos.get(HIST("Prediction/hEventSelection"))->GetNbinsX(); i++) { + histos.get(HIST("Prediction/hEventSelection"))->GetXaxis()->SetBinLabel(i, eventSelLabel[i - 1]); + } + if (masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.add("Prediction/hTriggerFT0A", "Trigger Tracks FT0A", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + histos.add("Prediction/hTriggerFT0C", "Trigger Tracks FT0C", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } + histos.add("Prediction/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMult}); + } else { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.add("Prediction/hTriggerFT0A", "Trigger Tracks FT0A", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0AvsNchEta08", "Nch in 0.8 vs FT0A multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0AvsNchEta05", "Nch in 0.5 vs FT0A multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hTriggerFT0C", "Trigger Tracks FT0C", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0CvsNchEta08", "Nch in 0.8 vs FT0C multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0CvsNchEta05", "Nch in 0.5 vs FT0C multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + } + histos.add("Prediction/hTrigger", "Trigger Tracks", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisMultiplicity}); + histos.add("Prediction/hFT0MvsNchEta08", "Nch in 0.8 vs FT0M multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + histos.add("Prediction/hFT0MvsNchEta05", "Nch in 0.5 vs FT0M multiplicity", kTH2F, {axesConfigurations.axisMultiplicity, axesConfigurations.axisMidrapidityMultiplicity}); + } + for (int i = 0; i < AssocParticleTypes; i++) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("Prediction/h{}", Particlenames[i]).c_str(), "", kTH3F, {axesConfigurations.axisPtQA, axesConfigurations.axisEta, axesConfigurations.axisPhi}); + } + if (masterConfigurations.useCentralityinPrediction) { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("Prediction/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultNDim}); + } + } else { + if (TESTBIT(doCorrelation, i)) { + histos.add(fmt::format("Prediction/sameEvent/{}", Particlenames[i]).c_str(), "", kTHnF, {axisDeltaPhiNDim, axisDeltaEtaNDim, axisPtAssocNDim, axisPtTriggerNDim, axisVtxZNDim, axisMultiplicityNDim}); + } + } + } + if (masterConfigurations.doSeparateFT0Prediction) { + histos.addClone("Prediction/sameEvent/", "Prediction/sameEventFT0A/"); + histos.addClone("Prediction/sameEvent/", "Prediction/sameEventFT0C/"); + } + } // visual inspection of sizes histos.print(); // initialize CCDB *only* if efficiency correction requested // skip if not requested, saves a bit of time - if (efficiencyFlags.applyEfficiencyCorrection) { + if (efficiencyFlags.applyEfficiencyCorrection || doprocessPairLossK0MC) { ccdb->setURL(ccdburl); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); @@ -1896,28 +3599,123 @@ struct HStrangeCorrelation { } } + // INEL>N at reconstructed level: more than N PV contributors in |eta| < 1. + // multNTracksPVeta1 is exactly this count (filled by multcenttable), so this is + // collision.isInelGt0() / isInelGt1() with a free N and needs no track table + template + bool isInelGtNReco(TCollision const& collision, int threshold) + { + return collision.multNTracksPVeta1() > threshold; + } + + // Number of charged physical primaries in |eta| < 1 at generated level, + // counted the same way as multcenttable fills multMCNParticlesEta10 + template + int countNchGenEta1(TMcParticles const& mcParticles) + { + constexpr double ChargeTolerance = 1e-3; // |charge| below this counts as neutral + int nChEta1 = 0; + for (auto const& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + if (pdgParticle == nullptr || std::abs(pdgParticle->Charge()) < ChargeTolerance) { + continue; + } + if (std::abs(mcParticle.eta()) < 1.0f) { + ++nChEta1; + } + } + return nChEta1; + } + + // INEL>N at generated level: more than N charged physical primaries in |eta| < 1 + template + bool isInelGtNGen(TMcParticles const& mcParticles, int threshold) + { + return countNchGenEta1(mcParticles) > threshold; + } + + // reconstructed INEL>0 / INEL>N cuts of the pp event selection + template + bool isCollisionInelSelected(TCollision const& collision) + { + if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { + return false; + } + if (!isInelGtNReco(collision, masterConfigurations.inelGtNThreshold) && masterConfigurations.selectINELgtN) { + return false; + } + return true; + } + + // generated-level INEL>0 of the PairLoss Comparison stages with + // applyINELSelectionAtGenLevel: Nch^gen (countNchGenEta1) >= 1 + static bool isGenInelGt0(int nChGen) + { + return nChGen > 0; + } + + // MC-only event quality cut of the PairLoss Comparison stages (applyNchResponseBandCut): + // multNTracksPVeta1 inside mu(Nch^gen) +- nSigma * max(sigma(Nch^gen), sigmaMin), with mu + // a polynomial in Nch^gen and sigma one in sqrt(Nch^gen). Evaluated here rather than + // through TF1 members: the task struct is at the StructToTuple member limit + bool isInNchResponseBand(int nChGen, int nTracksPVeta1) + { + const double sqrtNchGen = std::sqrt(static_cast(std::max(nChGen, 0))); + double mean = 0.0; + double power = 1.0; + for (const double par : pairLossK0Configurations.nchResponseBandMeanPars.value) { + mean += par * power; + power *= nChGen; + } + double sigma = 0.0; + power = 1.0; + for (const double par : pairLossK0Configurations.nchResponseBandSigmaPars.value) { + sigma += par * power; + power *= sqrtNchGen; + } + sigma = std::max(sigma, static_cast(pairLossK0Configurations.nchResponseBandSigmaMin)); + return std::abs(nTracksPVeta1 - mean) <= pairLossK0Configurations.nchResponseBandNSigma * sigma; + } + // this function allows for all event selections to be done in a modular way + // applyInelSelection = false skips only the reconstructed INEL>0 / INEL>N cuts template - bool isCollisionSelected(TCollision const& collision) + bool isCollisionSelected(TCollision const& collision, bool applyInelSelection = true) { // ________________________________________________ // Perform basic event selection if (!collision.sel8()) { return false; } + if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && masterConfigurations.requireGoodTriggerTVX) { + // FT0 vertex (acceptable FT0C-FT0A time difference) collisions + return false; + } if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { return false; } if (collision.centFT0M() > axisRanges[5][1] || collision.centFT0M() < axisRanges[5][0]) { return false; } - if (!collision.isInelGt0() && masterConfigurations.selectINELgtZERO) { + if (applyInelSelection && !isCollisionInelSelected(collision)) { return false; } if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { return false; } - if (zorroMask.value != "") { + if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && masterConfigurations.requireGoodZvtxFT0vsPV) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return false; + } + if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup) && masterConfigurations.rejectSameBunchPileup) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + return false; + } + if (!zorroMask.value.empty()) { auto bc = collision.template bc_as(); initZorro(bc); bool zorroSelected = zorro.isSelected(collision.template bc_as().globalBC()); /// Just let Zorro do the accounting @@ -1930,50 +3728,65 @@ struct HStrangeCorrelation { // event selections in Pb-Pb template - bool isCollisionSelectedPbPb(TCollision collision, bool fillHists) + bool isCollisionSelectedPbPb(TCollision const& collision, bool fillHists) { - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 0.5 /* all collisions */); + } // Perform basic event selection if (!collision.sel8()) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 1.5 /* collisions after sel8*/); + } if (!collision.selection_bit(aod::evsel::kIsTriggerTVX) && masterConfigurations.requireGoodTriggerTVX) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 2.5 /* FT0 vertex (acceptable FT0C-FT0A time difference) collisions */); + } + + if (collision.centFT0C() > axisRanges[5][1] || collision.centFT0C() < axisRanges[5][0]) { + return false; + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 3.5 /* collisions after sel centrality sel*/); + } if (std::abs(collision.posZ()) > masterConfigurations.zVertexCut) { return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 3.5 /* collisions after sel pvz sel*/); + if (fillHists) { + histos.fill(HIST("hEventSelection"), 4.5 /* collisions after sel pvz sel*/); + } if (!collision.selection_bit(aod::evsel::kIsGoodITSLayersAll) && masterConfigurations.requireAllGoodITSLayers) { // cut time intervals with dead ITS staves return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 4.5 /* collisions after cut time intervals with dead ITS staves*/); + if (fillHists) { + histos.fill(HIST("hEventSelection"), 5.5 /* collisions after cut time intervals with dead ITS staves*/); + } if (!collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV) && masterConfigurations.requireGoodZvtxFT0vsPV) { // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference // use this cut at low multiplicities with caution return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 5.5 /* removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference*/); + if (fillHists) { + histos.fill(HIST("hEventSelection"), 6.5 /* removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference*/); + } auto occupancy = collision.trackOccupancyInTimeRange(); - if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) + if (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh) { return false; - if (fillHists) - histos.fill(HIST("hEventSelection"), 6.5 /* Below min occupancy and Above max occupancy*/); + } + if (fillHists) { + histos.fill(HIST("hEventSelection"), 7.5 /* Below min occupancy and Above max occupancy*/); + } /* if (collision.alias_bit(kTVXinTRD)) { @@ -1987,27 +3800,41 @@ struct HStrangeCorrelation { // O2-4623 return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 7.5 /* reject collisions close to Time Frame borders*/); + if (fillHists) { + histos.fill(HIST("hEventSelection"), 8.5 /* reject collisions close to Time Frame borders*/); + } if (!collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { // reject events affected by the ITS ROF border // O2-4309 return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 8.5 /* reject events affected by the ITS ROF border*/); + if (fillHists) { + histos.fill(HIST("hEventSelection"), 9.5 /* reject events affected by the ITS ROF border*/); + } if (!collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { // rejects collisions which are associated with the same "found-by-T0" bunch crossing // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof return false; } - if (fillHists) - histos.fill(HIST("hEventSelection"), 9.5 /* rejects collisions which are associated with the same "found-by-T0" bunch crossing*/); + if (fillHists) { + histos.fill(HIST("hEventSelection"), 10.5 /* rejects collisions which are associated with the same "found-by-T0" bunch crossing*/); + } return true; } + // event selection of runSameEventHV0s before its INEL cut: trigger presence + + // isCollisionSelected (pp) or isCollisionSelectedPbPb, without filling hEventSelection + template + bool isCollisionSelectedNoInel(TCollision const& collision) + { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return false; + } + return masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, false) : isCollisionSelectedPbPb(collision, false); + } + double getMagneticField(uint64_t timestamp) { static parameters::GRPMagField* grpo = nullptr; @@ -2022,6 +3849,22 @@ struct HStrangeCorrelation { return 0.1 * (grpo->getNominalL3Field()); // 1 T = 10 kG } + + double getPairLossMagneticField(int runNumber, uint64_t timestamp) + { + if (mPairLossRunNumber == runNumber) { + return mPairLossMagneticField; + } + auto* grpo = ccdb->getForTimeStamp("GLO/Config/GRPMagField", timestamp); + if (grpo == nullptr) { + LOGF(fatal, "GRP object not found for K0 pair-loss diagnostic at timestamp %llu", timestamp); + return 0.0; + } + mPairLossRunNumber = runNumber; + mPairLossMagneticField = 0.1 * grpo->getNominalL3Field(); // 1 T = 10 kG + LOGF(info, "K0 pair-loss diagnostic loaded %.1f T for run %d", mPairLossMagneticField, runNumber); + return mPairLossMagneticField; + } // if this process function is enabled, it will be such that only events with trigger particles within a given // trigger pt bin are taken for the entire processing. This allows for the calculation of e.g. efficiencies // within an event class that has a trigger (which may differ with respect to other cases, to be checked) @@ -2038,8 +3881,12 @@ struct HStrangeCorrelation { for (auto const& collision : collisions) { // ________________________________________________ - // Perform basic event selection - if (!isCollisionSelected(collision)) { + // Perform basic event selection. The INEL cuts are left to the consumers of + // triggerPresenceMap, which apply their own event selection afterwards, so that + // a generated-level INEL selection (applyINELSelectionAtGenLevel) is not + // overridden. Only the generated-level closure paths with applyNewMCSelection + // and doGenEventSelection both false have no event selection of their own + if (!isCollisionSelected(collision, false)) { continue; } @@ -2051,6 +3898,9 @@ struct HStrangeCorrelation { if (!isValidTrigger(track, triggerTrack.isLeading())) { continue; } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { + continue; + } auto binNumber = histos.get(HIST("axes/hPtTriggerAxis"))->FindFixBin(track.pt()) - 1; SETBIT(triggerPresenceMap[collision.globalIndex()], binNumber); } @@ -2061,11 +3911,11 @@ struct HStrangeCorrelation { aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; // true is for 'ignore overflows' (true by default). Underflows and overflows will have bin -1. + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -2092,8 +3942,14 @@ struct HStrangeCorrelation { if (!doprocessSameEventHCascades && !doprocessSameEventHV0s && !doprocessSameEventHPions) { for (auto const& triggerTrack : triggerTracks) { auto track = triggerTrack.track_as(); - if (!isValidTrigger(track, triggerTrack.isLeading())) + if (!isValidTrigger(track, triggerTrack.isLeading())) { continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { + continue; + } + histos.fill(HIST("hDCAzTriggerHadron"), track.dcaZ(), track.pt()); + histos.fill(HIST("hDCAxyTriggerHadron"), track.dcaXY(), track.pt()); float efficiency = 1.0f; if (efficiencyFlags.applyEfficiencyCorrection) { efficiency = hEfficiencyTrigger->Interpolate(track.pt(), track.eta()); @@ -2104,22 +3960,30 @@ struct HStrangeCorrelation { float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); - if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { continue; + } histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi(), weight); } } for (auto const& assocTrack : assocHadrons) { auto assoc = assocTrack.track_as(); - if (!isValidAssocHadron(assoc)) + if (!isValidAssocHadron(assoc)) { continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(assocTrack.mcMask(), 13)) { + continue; + } float efficiency = 1.0f; float purity = 1.0f; + histos.fill(HIST("hDCAzAssociatedHadron"), assoc.dcaZ(), assoc.pt()); + histos.fill(HIST("hDCAxyAssociatedHadron"), assoc.dcaXY(), assoc.pt()); if (efficiencyFlags.applyEfficiencyCorrection) { efficiency = hEfficiencyHadron->Interpolate(assoc.pt(), assoc.eta()); - if (efficiencyFlags.applyPurityHadron) + if (efficiencyFlags.applyPurityHadron) { purity = hPurityHadron->Interpolate(assoc.pt()); + } } if (efficiency == 0) { // check for zero efficiency, do not apply if the case efficiency = 1; @@ -2127,58 +3991,42 @@ struct HStrangeCorrelation { float weight = efficiencyFlags.applyEfficiencyCorrection ? purity / efficiency : 1.0f; histos.fill(HIST("hAssocHadronsAllSelectedEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M(), weight); histos.fill(HIST("hAssocPtResolution"), assoc.pt(), assocTrack.mcOriginalPt()); - if (doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) + if (masterConfigurations.doAssocPhysicalPrimary && !assocTrack.mcPhysicalPrimary()) { continue; + } histos.fill(HIST("hAssocPrimaryEtaVsPt"), assoc.pt(), assoc.eta(), collision.centFT0M()); histos.fill(HIST("hAsssocTrackEtaVsPtVsPhi"), assoc.pt(), assoc.eta(), assoc.phi(), weight); } // ________________________________________________ // Do hadron - hadron correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsHadron(triggerTracks, assocHadrons, false, collision.posZ(), collision.centFT0M(), bField); + } } - void processSameEventHV0s(soa::Join::iterator const& collision, - aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, - V0DatasWithoutTrackX const&, TracksComplete const&, aod::BCsWithTimestamps const&) + void runSameEventHV0sCore(aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, TracksComplete const& tracks, + float pvx, float pvy, float pvz, float cent, float multNTracksPVeta1, double bField) { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; - - double cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); - // ________________________________________________ - // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { - return; - } + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; - // ________________________________________________ - // Perform basic event selection - if (((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision))) || (!masterConfigurations.doPPAnalysis && !isCollisionSelectedPbPb(collision, true))) { - return; - } - // ________________________________________________ if (!doprocessSameEventHCascades && doMixingQAandEventQA) { std::visit([&](auto const& binning) { - histos.fill(HIST("MixingQA/hSECollisionBins"), binning.getBin({collision.posZ(), cent})); + histos.fill(HIST("MixingQA/hSECollisionBins"), binning.getBin({pvz, cent})); }, colBinning); histos.fill(HIST("EventQA/hMult"), cent); - histos.fill(HIST("EventQA/hPvz"), collision.posZ()); - histos.fill(HIST("EventQA/hMultFT0vsTPC"), cent, collision.multNTracksPVeta1()); + histos.fill(HIST("EventQA/hPvz"), pvz); + histos.fill(HIST("EventQA/hMultFT0vsTPC"), cent, multNTracksPVeta1); } + // Do basic QA - auto bc = collision.bc_as(); - auto bField = getMagneticField(bc.timestamp()); - if (efficiencyFlags.applyEfficiencyCorrection) { - initEfficiencyFromCCDB(bc); - } - TH2F* hEfficiencyV0[3]; - THnF* hEfficiencyV0MultVsPhi[3]; + std::array hEfficiencyV0{nullptr, nullptr, nullptr}; + std::array hEfficiencyV0MultVsPhi{nullptr, nullptr, nullptr}; if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { hEfficiencyV0MultVsPhi[0] = hEfficiencyK0ShortMultVsPhi; hEfficiencyV0MultVsPhi[1] = hEfficiencyLambdaMultVsPhi; @@ -2195,28 +4043,34 @@ struct HStrangeCorrelation { //---] track quality check [--- auto postrack = v0Data.posTrack_as(); auto negtrack = v0Data.negTrack_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) + if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; - if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated)) + } + if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { continue; - if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) + } + if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) { continue; + } //---] syst cuts [--- if (masterConfigurations.doPPAnalysis && (v0Data.v0radius() < v0Selection.v0RadiusMin || v0Data.v0radius() > v0Selection.v0RadiusMax || std::abs(v0Data.dcapostopv()) < v0Selection.dcapostopv || std::abs(v0Data.dcanegtopv()) < v0Selection.dcanegtopv || - v0Data.v0cosPA() < v0Selection.v0cospa || v0Data.dcaV0daughters() > v0Selection.dcaV0dau)) + v0Data.v0cosPA() < v0Selection.v0cospa || v0Data.dcaV0daughters() > v0Selection.dcaV0dau)) { continue; - if (!masterConfigurations.doPPAnalysis && !v0SelectedPbPb(v0Data)) + } + if (!masterConfigurations.doPPAnalysis && !v0SelectedPbPb(v0Data)) { continue; - uint64_t selMap = v0selectionBitmap(v0Data, collision.posX(), collision.posY(), collision.posZ()); + } + uint64_t selMap = v0selectionBitmap(v0Data, pvx, pvy, pvz); + const int localDensityV0 = masterConfigurations.doLocalDensityStudy ? computeLocalDensity(v0Data.eta(), v0Data.phi(), tracks, {v0Data.posTrackId(), v0Data.negTrackId()}) : 0; static_for<0, 2>([&](auto i) { constexpr int Index = i.value; float efficiency = 1.0f; if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { if (efficiencyFlags.applyEfficiencyCorrection) { - double bin[4] = {v0Data.pt(), v0Data.eta(), v0Data.phi(), cent}; - efficiency = hEfficiencyV0MultVsPhi[Index]->GetBinContent(hEfficiencyV0MultVsPhi[Index]->GetBin(bin)); + std::array bin = {v0Data.pt(), v0Data.eta(), v0Data.phi(), cent}; + efficiency = hEfficiencyV0MultVsPhi[Index]->GetBinContent(hEfficiencyV0MultVsPhi[Index]->GetBin(bin.data())); } } else { if (efficiencyFlags.applyEfficiencyCorrection) { @@ -2227,9 +4081,12 @@ struct HStrangeCorrelation { efficiency = 1; } float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; - if (v0.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || v0.mcTrue(Index)) && (!doAssocPhysicalPrimary || v0.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { + if (v0.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || v0.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || v0.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { if ((TESTBIT(doCorrelation, Index)) && (masterConfigurations.doPPAnalysis || (TESTBIT(selMap, Index) && TESTBIT(selMap, Index + 3)))) { histos.fill(HIST("h3d") + HIST(V0names[Index]) + HIST("Spectrum"), v0Data.pt(), cent, v0.invMassNSigma(Index), weight); + if (masterConfigurations.doMassSpectrumCheck) { + histos.fill(HIST("h") + HIST(V0names[Index]) + HIST("PtVsMass"), v0Data.pt(), getV0InvariantMass(v0Data)); + } if (std::abs(v0Data.rapidity(Index)) < ySel) { histos.fill(HIST("h3d") + HIST(V0names[Index]) + HIST("SpectrumY"), v0Data.pt(), cent, v0.invMassNSigma(Index), weight); } @@ -2248,6 +4105,14 @@ struct HStrangeCorrelation { histos.fill(HIST("h") + HIST(V0names[Index]) + HIST("EtaVsPtVsPhiVsCent"), v0Data.pt(), v0Data.eta(), v0Data.phi(), cent, weight); } } + if (masterConfigurations.doLocalDensityStudy) { + if ((-massWindowConfigurations.maxBgNSigma < v0.invMassNSigma(Index) && v0.invMassNSigma(Index) < -massWindowConfigurations.minBgNSigma) || (+massWindowConfigurations.minBgNSigma < v0.invMassNSigma(Index) && v0.invMassNSigma(Index) < +massWindowConfigurations.maxBgNSigma)) { + histos.fill(HIST("h") + HIST(V0names[Index]) + HIST("EtaVsPtVsLocalDensityBg"), v0Data.pt(), v0Data.eta(), localDensityV0, weight); + } + if (-massWindowConfigurations.maxPeakNSigma < v0.invMassNSigma(Index) && v0.invMassNSigma(Index) < +massWindowConfigurations.maxPeakNSigma) { + histos.fill(HIST("h") + HIST(V0names[Index]) + HIST("EtaVsPtVsLocalDensity"), v0Data.pt(), v0Data.eta(), localDensityV0, weight); + } + } } } }); @@ -2255,25 +4120,76 @@ struct HStrangeCorrelation { if (!doprocessSameEventHCascades) { for (auto const& triggerTrack : triggerTracks) { auto track = triggerTrack.track_as(); - if (!isValidTrigger(track, triggerTrack.isLeading())) + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { continue; + } histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), cent); histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); - if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { continue; + } if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), cent); } else { histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), track.phi(), cent); } histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); + if (masterConfigurations.doLocalDensityStudy) { + histos.fill(HIST("hTrackEtaVsPtVsLocalDensity"), track.pt(), track.eta(), computeLocalDensity(track.eta(), track.phi(), tracks, {track.globalIndex()})); + } } } // ________________________________________________ // Do hadron - V0 correlations - if (masterConfigurations.doFullCorrelationStudy) - fillCorrelationsV0(triggerTracks, associatedV0s, false, false, collision.posX(), collision.posY(), collision.posZ(), cent, bField); + if (masterConfigurations.doFullCorrelationStudy) { + fillCorrelationsV0(triggerTracks, associatedV0s, false, false, pvx, pvy, pvz, cent, bField); + } + } + + template + void runSameEventHV0s(TCollision const& collision, aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, TracksComplete const& tracks, + std::optional nChGenForInelSelection = std::nullopt) + { + const float cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); + + // Keep the trigger-presence and reconstructed-event decisions identical + // for the ordinary Rec process and for its PairLoss replica. When + // nChGenForInelSelection is given (PairLoss replica only), the reconstructed + // INEL cuts are replaced by Nch^gen >= 1 of that generated multiplicity + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + return; + } + if ((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision, !nChGenForInelSelection.has_value())) || + (!masterConfigurations.doPPAnalysis && !isCollisionSelectedPbPb(collision, true))) { + return; + } + if (nChGenForInelSelection.has_value() && !isGenInelGt0(*nChGenForInelSelection)) { + return; + } + + auto bc = collision.template bc_as(); + const auto bField = getMagneticField(bc.timestamp()); + if (efficiencyFlags.applyEfficiencyCorrection) { + initEfficiencyFromCCDB(bc); + } + runSameEventHV0sCore(associatedV0s, triggerTracks, tracks, + collision.posX(), collision.posY(), collision.posZ(), cent, collision.multNTracksPVeta1(), bField); + } + + void processSameEventHV0s(soa::Join::iterator const& collision, + aod::AssocV0s const& associatedV0s, aod::TriggerTracks const& triggerTracks, + V0DatasWithoutTrackX const&, TracksComplete const& tracks, aod::BCsWithTimestamps const&) + { + // filled here and not in runSameEventHV0s, which the PairLoss Rec replica also runs + if (masterConfigurations.doMultEstimatorResponse && isCollisionSelectedNoInel(collision)) { + histos.fill(HIST("MultEstimatorResponse/hNTracksPVeta1VsCentFT0M"), collision.multNTracksPVeta1(), collision.centFT0M()); + histos.fill(HIST("MultEstimatorResponse/hNTracksPVeta1VsMultFT0M"), collision.multNTracksPVeta1(), collision.multFT0M()); + } + runSameEventHV0s(collision, associatedV0s, triggerTracks, tracks); } void processSameEventHCascades(soa::Join::iterator const& collision, @@ -2283,13 +4199,13 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; double cent = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -2313,8 +4229,8 @@ struct HStrangeCorrelation { if (efficiencyFlags.applyEfficiencyCorrection) { initEfficiencyFromCCDB(bc); } - TH2F* hEfficiencyCascade[4]; - THnF* hEfficiencyCascadeMultVsPhi[4]; + std::array hEfficiencyCascade{nullptr, nullptr, nullptr, nullptr}; + std::array hEfficiencyCascadeMultVsPhi{nullptr, nullptr, nullptr, nullptr}; if (efficiencyFlags.applyEfficiencyCorrection) { if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { hEfficiencyCascadeMultVsPhi[0] = hEfficiencyXiMinusMultVsPhi; @@ -2341,21 +4257,26 @@ struct HStrangeCorrelation { cascData.casccosPA(collision.posX(), collision.posY(), collision.posZ()) < cascadeSelections.cascCospa || cascData.cascradius() < cascadeSelections.cascRadius || std::abs(cascData.dcav0topv(collision.posX(), collision.posY(), collision.posZ())) < cascadeSelections.cascdcaV0ToPV || - std::abs(cascData.mLambda() - o2::constants::physics::MassLambda0) > cascadeSelections.cascV0masswindow)) + std::abs(cascData.mLambda() - o2::constants::physics::MassLambda0) > cascadeSelections.cascV0masswindow)) { continue; - if (!masterConfigurations.doPPAnalysis && !cascadeSelectedPbPb(cascData, collision.posX(), collision.posY(), collision.posZ())) + } + if (!masterConfigurations.doPPAnalysis && !cascadeSelectedPbPb(cascData, collision.posX(), collision.posY(), collision.posZ())) { continue; + } uint64_t cascselMap = cascadeselectionBitmap(cascData, collision.posX(), collision.posY(), collision.posZ()); //---] track quality check [--- auto postrack = cascData.posTrack_as(); auto negtrack = cascData.negTrack_as(); auto bachtrack = cascData.bachelor_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) + if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || bachtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; - if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() < trackSelection.minTPCChi2PerClusterAssociated)) + } + if (trackSelection.checksRequireTPCChi2 && (postrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || negtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated || bachtrack.tpcChi2NCl() > trackSelection.maxTPCChi2PerClusterAssociated)) { continue; - if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) + } + if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) { continue; + } static_for<0, 3>([&](auto i) { constexpr int Index = i.value; @@ -2365,8 +4286,8 @@ struct HStrangeCorrelation { float efficiency = 1.0f; if (efficiencyFlags.applyEfficiencyCorrection) { if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { - double bin[4] = {cascData.pt(), cascData.eta(), cascData.phi(), cent}; - efficiency = hEfficiencyCascadeMultVsPhi[Index]->GetBinContent(hEfficiencyCascadeMultVsPhi[Index]->GetBin(bin)); + std::array bin = {cascData.pt(), cascData.eta(), cascData.phi(), cent}; + efficiency = hEfficiencyCascadeMultVsPhi[Index]->GetBinContent(hEfficiencyCascadeMultVsPhi[Index]->GetBin(bin.data())); } else { efficiency = hEfficiencyCascade[Index]->Interpolate(cascData.pt(), cascData.eta()); } @@ -2375,7 +4296,7 @@ struct HStrangeCorrelation { efficiency = 1; } float weight = efficiencyFlags.applyEfficiencyCorrection ? 1. / efficiency : 1.0f; - if (casc.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || casc.mcTrue(Index)) && (!doAssocPhysicalPrimary || casc.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { + if (casc.compatible(Index, trackSelection.dEdxCompatibility) && (!masterConfigurations.doMCassociation || casc.mcTrue(Index)) && (!masterConfigurations.doAssocPhysicalPrimary || casc.mcPhysicalPrimary()) && (!efficiencyFlags.applyEfficiencyCorrection || efficiency != 0)) { if (TESTBIT(doCorrelation, Index + 3) && (masterConfigurations.doPPAnalysis || (TESTBIT(cascselMap, Index) && TESTBIT(cascselMap, Index + 4) && TESTBIT(cascselMap, Index + 8) && TESTBIT(cascselMap, Index + 12)))) { histos.fill(HIST("h3d") + HIST(Cascadenames[Index]) + HIST("Spectrum"), cascData.pt(), cent, casc.invMassNSigma(Index), weight); if (std::abs(cascData.rapidity(Index)) < ySel) { @@ -2402,37 +4323,44 @@ struct HStrangeCorrelation { } for (auto const& triggerTrack : triggerTracks) { auto track = triggerTrack.track_as(); - if (!isValidTrigger(track, triggerTrack.isLeading())) + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { continue; + } histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), cent); histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); - if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { continue; + } if (!efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), cent); } else { histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), track.phi(), cent); } - if (track.sign() > 0) + if (track.sign() > 0) { histos.fill(HIST("hPositiveTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), cent); - else + } else { histos.fill(HIST("hNegativeTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), cent); + } histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } // ________________________________________________ // Do hadron - cascade correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsCascade(triggerTracks, associatedCascades, false, false, collision.posX(), collision.posY(), collision.posZ(), cent, bField); + } } void processSameEventHPions(soa::Join::iterator const& collision, soa::Join const& associatedPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } auto bc = collision.bc_as(); @@ -2456,29 +4384,41 @@ struct HStrangeCorrelation { // Do basic QA for (auto const& pion : associatedPions) { auto pionTrack = pion.track_as(); - if (!isValidAssocHadron(pionTrack)) + if (!isValidAssocHadron(pionTrack)) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(pion.mcMask(), 13)) { continue; + } histos.fill(HIST("hPionEtaVsPtAllSelected"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); - if (doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) + if (masterConfigurations.doAssocPhysicalPrimary && !pion.mcPhysicalPrimary()) { continue; - if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) + } + if (masterConfigurations.doMCassociation && std::abs(pion.pdgCode()) != PdgCodes[IndexPion]) { continue; + } histos.fill(HIST("hPionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); - if (pionTrack.sign() > 0) + if (pionTrack.sign() > 0) { histos.fill(HIST("hPositivePionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); - else + } else { histos.fill(HIST("hNegativePionEtaVsPt"), pionTrack.pt(), pionTrack.eta(), collision.centFT0M()); + } } if (!doprocessSameEventHCascades && !doprocessSameEventHV0s) { for (auto const& triggerTrack : triggerTracks) { auto track = triggerTrack.track_as(); - if (!isValidTrigger(track, triggerTrack.isLeading())) + if (!isValidTrigger(track, triggerTrack.isLeading())) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerTrack.mcMask(), 13)) { continue; + } histos.fill(HIST("hTriggerAllSelectedEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); histos.fill(HIST("hTriggerPtResolution"), track.pt(), triggerTrack.mcOriginalPt()); - if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) + if (masterConfigurations.doTriggPhysicalPrimary && !triggerTrack.mcPhysicalPrimary()) { continue; + } histos.fill(HIST("hTriggerPrimaryEtaVsPt"), track.pt(), track.eta(), collision.centFT0M()); histos.fill(HIST("hTrackEtaVsPtVsPhi"), track.pt(), track.eta(), track.phi()); } @@ -2486,15 +4426,16 @@ struct HStrangeCorrelation { // ________________________________________________ // Do hadron - Pion correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsHadron(triggerTracks, associatedPions, false, collision.posZ(), collision.centFT0M(), bField); + } } void processMixedEventHHadrons(soa::Join const& collisions, aod::AssocHadrons const& assocHadrons, aod::TriggerTracks const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2504,7 +4445,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { return; } @@ -2513,10 +4454,12 @@ struct HStrangeCorrelation { if (!isCollisionSelected(collision1) || !isCollisionSelected(collision2)) { continue; } - if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) { continue; - if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + } + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) { continue; + } if (doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting @@ -2531,8 +4474,9 @@ struct HStrangeCorrelation { auto slicedAssocHadrons = assocHadrons.sliceBy(collisionSliceHadrons, collision2.globalIndex()); // ________________________________________________ // Do hadron - hadron correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsHadron(slicedTriggerTracks, slicedAssocHadrons, true, collision1.posZ(), collision1.centFT0M(), bField); + } } } @@ -2543,8 +4487,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; std::visit([&](auto const& binning) { for (auto const& [collision1, collision2] : soa::selfCombinations(binning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2558,7 +4502,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -2566,10 +4510,12 @@ struct HStrangeCorrelation { if ((masterConfigurations.doPPAnalysis && (!isCollisionSelected(collision1) || !isCollisionSelected(collision2))) || (!masterConfigurations.doPPAnalysis && (!isCollisionSelectedPbPb(collision1, false) || (!isCollisionSelectedPbPb(collision2, false))))) { continue; } - if (cent1 > axisRanges[5][1] || cent1 < axisRanges[5][0]) + if (cent1 > axisRanges[5][1] || cent1 < axisRanges[5][0]) { continue; - if (cent2 > axisRanges[5][1] || cent2 < axisRanges[5][0]) + } + if (cent2 > axisRanges[5][1] || cent2 < axisRanges[5][0]) { continue; + } if (!doprocessMixedEventHCascades && doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { @@ -2585,8 +4531,9 @@ struct HStrangeCorrelation { auto slicedAssocV0s = associatedV0s.sliceBy(collisionSliceV0s, collision2.globalIndex()); // ________________________________________________ // Do hadron - V0 correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsV0(slicedTriggerTracks, slicedAssocV0s, true, false, collision1.posX(), collision1.posY(), collision1.posZ(), cent1, bField); + } } }, colBinning); @@ -2599,8 +4546,8 @@ struct HStrangeCorrelation { std::variant colBinning = masterConfigurations.doPPAnalysis ? std::variant{ - BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}} - : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}}; + BinningTypePP{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}} + : std::variant{BinningTypePbPb{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}}; std::visit([&](auto const& binning) { for (auto const& [collision1, collision2] : soa::selfCombinations(binning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { @@ -2614,7 +4561,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -2622,10 +4569,12 @@ struct HStrangeCorrelation { if ((masterConfigurations.doPPAnalysis && (!isCollisionSelected(collision1) || !isCollisionSelected(collision2))) || (!masterConfigurations.doPPAnalysis && (!isCollisionSelectedPbPb(collision1, false) || (!isCollisionSelectedPbPb(collision2, false))))) { continue; } - if (cent1 > axisRanges[5][1] || cent1 < axisRanges[5][0]) + if (cent1 > axisRanges[5][1] || cent1 < axisRanges[5][0]) { continue; - if (cent2 > axisRanges[5][1] || cent2 < axisRanges[5][0]) + } + if (cent2 > axisRanges[5][1] || cent2 < axisRanges[5][0]) { continue; + } if (doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting @@ -2640,8 +4589,9 @@ struct HStrangeCorrelation { auto slicedAssocCascades = associatedCascades.sliceBy(collisionSliceCascades, collision2.globalIndex()); // ________________________________________________ // Do hadron - cascade correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsCascade(slicedTriggerTracks, slicedAssocCascades, true, false, collision1.posX(), collision1.posY(), collision1.posZ(), cent1, bField); + } } }, colBinning); @@ -2651,7 +4601,7 @@ struct HStrangeCorrelation { soa::Join const& assocPions, soa::Join const& triggerTracks, TracksComplete const&, aod::BCsWithTimestamps const&) { - BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}, true}; + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; for (auto const& [collision1, collision2] : soa::selfCombinations(colBinning, masterConfigurations.mixingParameter, -1, collisions, collisions)) { auto bc = collision1.bc_as(); auto bField = getMagneticField(bc.timestamp()); @@ -2661,7 +4611,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { + if (!triggerPresenceMap.empty() && (!TESTBIT(triggerPresenceMap[collision1.globalIndex()], triggerBinToSelect) || !TESTBIT(triggerPresenceMap[collision2.globalIndex()], triggerBinToSelect))) { continue; } @@ -2670,10 +4620,12 @@ struct HStrangeCorrelation { if (!isCollisionSelected(collision1) || !isCollisionSelected(collision2)) { continue; } - if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) + if (collision1.centFT0M() > axisRanges[5][1] || collision1.centFT0M() < axisRanges[5][0]) { continue; - if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) + } + if (collision2.centFT0M() > axisRanges[5][1] || collision2.centFT0M() < axisRanges[5][0]) { continue; + } if (doMixingQAandEventQA) { if (collision1.globalIndex() == collision2.globalIndex()) { histos.fill(HIST("MixingQA/hMixingQA"), 0.0f); // same-collision pair counting @@ -2688,12 +4640,13 @@ struct HStrangeCorrelation { auto slicedAssocPions = assocPions.sliceBy(collisionSliceHadrons, collision2.globalIndex()); // ________________________________________________ // Do hadron - cascade correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsHadron(slicedTriggerTracks, slicedAssocPions, true, collision1.posZ(), collision1.centFT0M(), bField); + } } } - void processMCGenerated(aod::McCollision const& /*mcCollision*/, soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) + void processMCGenerated(aod::McCollision const& /*mcCollision*/, soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles, TracksCompleteMC const& tracks) { histos.fill(HIST("hClosureTestEventCounter"), 2.5f); @@ -2709,7 +4662,7 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hGeneratedQAPtAssociatedK0"), gpt, 0.0f); // step 1: before all selections } @@ -2717,32 +4670,47 @@ struct HStrangeCorrelation { } for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; + } static_for<0, 7>([&](auto i) { constexpr int Index = i.value; - if (i == IndexPion && mcParticle.pdgCode() > Neutral) + if (i == IndexPion && mcParticle.pdgCode() > Neutral) { histos.fill(HIST("Generated/hPositive") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); - else if (i == IndexPion && mcParticle.pdgCode() < Neutral) + } else if (i == IndexPion && mcParticle.pdgCode() < Neutral) { histos.fill(HIST("Generated/hNegative") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } else if (mcParticle.pdgCode() == PdgCodes[i]) { + if (efficiencyFlags.applyEffAsFunctionOfMultAndPhi) { + histos.fill(HIST("Generated/h") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), mcParticle.phi(), 1); + } else { + histos.fill(HIST("Generated/h") + HIST(Particlenames[Index]), mcParticle.pt(), mcParticle.eta(), 1); + } + } }); } - if (collisions.size() < 1) + if (collisions.size() < 1) { return; + } // determine best collision properties int biggestNContribs = -1; - int bestCollisionFT0Mpercentile = -1; - int bestCollisionFT0Cpercentile = -1; + int64_t bestCollisionId = -1; + float bestCollisionFT0Mpercentile = -1; + float bestCollisionFT0Cpercentile = -1; float bestCollisionVtxZ = 0.0f; bool bestCollisionSel8 = false; bool bestCollisionINELgtZERO = false; + bool bestCollisionINELgtN = false; + bool bestCollisionNoSameBunchPileup = false; + bool bestCollisionGoodTriggerTVX = false; + bool bestCollisionGoodZvtxFT0vsPV = false; bool isCollisionSelect = false; uint32_t bestCollisionTriggerPresenceMap = 0; for (auto const& collision : collisions) { if (biggestNContribs < collision.numContrib()) { biggestNContribs = collision.numContrib(); + bestCollisionId = collision.globalIndex(); bestCollisionFT0Mpercentile = collision.centFT0M(); bestCollisionFT0Cpercentile = collision.centFT0C(); if (masterConfigurations.applyNewMCSelection) { @@ -2751,23 +4719,31 @@ struct HStrangeCorrelation { bestCollisionSel8 = collision.sel8(); bestCollisionVtxZ = collision.posZ(); bestCollisionINELgtZERO = collision.isInelGt0(); + bestCollisionINELgtN = isInelGtNReco(collision, masterConfigurations.inelGtNThreshold); + bestCollisionNoSameBunchPileup = collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + bestCollisionGoodTriggerTVX = collision.selection_bit(aod::evsel::kIsTriggerTVX); + bestCollisionGoodZvtxFT0vsPV = collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); } - if (triggerPresenceMap.size() > 0) + if (!triggerPresenceMap.empty()) { bestCollisionTriggerPresenceMap = triggerPresenceMap[collision.globalIndex()]; + } } } if (collisions.size() > 1) { for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; - if (std::abs(mcParticle.y()) > ySel) + } + if (std::abs(mcParticle.y()) > ySel) { continue; + } static_for<0, 7>([&](auto i) { constexpr int Index = i.value; - if (mcParticle.pdgCode() == PdgCodes[i]) + if (mcParticle.pdgCode() == PdgCodes[i]) { histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult_TwoPVsOrMore"), mcParticle.pt(), bestCollisionFT0Mpercentile); + } }); } } @@ -2778,19 +4754,35 @@ struct HStrangeCorrelation { // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { return; } if (masterConfigurations.applyNewMCSelection) { - if (!isCollisionSelect) + if (!isCollisionSelect) { return; + } } else { - if (!bestCollisionSel8) + if (!bestCollisionSel8) { + return; + } + if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) { + return; + } + if (!bestCollisionINELgtZERO) { + return; + } + if (masterConfigurations.selectINELgtN && !bestCollisionINELgtN) { return; - if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) + } + if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { + return; + } + if (masterConfigurations.requireGoodTriggerTVX && !bestCollisionGoodTriggerTVX) { return; - if (!bestCollisionINELgtZERO) + } + if (masterConfigurations.requireGoodZvtxFT0vsPV && !bestCollisionGoodZvtxFT0vsPV) { return; + } } histos.fill(HIST("hClosureTestEventCounter"), 3.5f); @@ -2807,15 +4799,19 @@ struct HStrangeCorrelation { } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { histos.fill(HIST("hGeneratedQAPtAssociatedK0"), gpt, 1.0f); // step 2: before all selections } } } + // The local density of a generated particle is counted from the tracks of the best collision, + // the same collision whose centrality already labels these generated histograms. + const auto bestCollisionTracks = tracks.sliceBy(pairLossTracksPerCollision, bestCollisionId); + for (auto const& mcParticle : mcParticles) { - if (doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { continue; } double geta = mcParticle.eta(); @@ -2826,13 +4822,17 @@ struct HStrangeCorrelation { } else { histos.fill(HIST("GeneratedWithPV/hTrigger"), gpt, geta, bestCollisionFT0Mpercentile); } - if (mcParticle.pdgCode() > 0) + if (masterConfigurations.doLocalDensityStudy && masterConfigurations.doPPAnalysis) { + histos.fill(HIST("GeneratedWithPV/hTriggerLocalDensity"), gpt, geta, computeLocalDensityGen(mcParticle, bestCollisionTracks)); + } + if (mcParticle.pdgCode() > 0) { histos.fill(HIST("GeneratedWithPV/hPositiveTrigger"), gpt, geta, bestCollisionFT0Mpercentile); - else + } else { histos.fill(HIST("GeneratedWithPV/hNegativeTrigger"), gpt, geta, bestCollisionFT0Mpercentile); + } } - if (mcParticle.pdgCode() == PDG_t::kLambda0 && !doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kLambda0 && !masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { if (std::abs(geta) > etaSel) { continue; } @@ -2853,7 +4853,7 @@ struct HStrangeCorrelation { } } } - if (mcParticle.pdgCode() == PDG_t::kLambda0Bar && !doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kLambda0Bar && !masterConfigurations.doAssocPhysicalPrimaryInGen && !mcParticle.isPhysicalPrimary()) { if (std::abs(geta) > etaSel) { continue; } @@ -2887,130 +4887,2170 @@ struct HStrangeCorrelation { } else { histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]), gpt, geta, bestCollisionFT0Mpercentile); } - if (std::abs(mcParticle.y()) < ySel) + if (std::abs(mcParticle.y()) < ySel) { histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("_MidYVsMult"), gpt, bestCollisionFT0Mpercentile); + } + if (masterConfigurations.doLocalDensityStudy && masterConfigurations.doPPAnalysis) { + histos.fill(HIST("GeneratedWithPV/h") + HIST(Particlenames[Index]) + HIST("LocalDensity"), gpt, geta, computeLocalDensityGen(mcParticle, bestCollisionTracks)); + } } }); } } - void processClosureTest(aod::McCollision const& /*mcCollision*/, soa::SmallGroups> const& recCollisions, aod::McParticles const& mcParticles) + void processPairLossK0MC(aod::McCollision const& mcCollision, + soa::SmallGroups> const& recCollisions, + aod::McParticles const& mcParticles, + aod::McTrackLabels const& trackLabels, + aod::V0MCCores const& v0MCCores, + aod::TriggerTracks const& triggerTracks, + aod::AssocV0s const& associatedV0s, + aod::BCsWithTimestamps const&, + V0DatasWithoutTrackX const& v0Candidates, + TracksComplete const& tracks) { - - std::vector triggerIndices; - std::vector> associatedIndices; - std::vector assocHadronIndices; - std::vector piIndices; - std::vector k0ShortIndices; - std::vector lambdaIndices; - std::vector antiLambdaIndices; - std::vector xiMinusIndices; - std::vector xiPlusIndices; - std::vector omegaMinusIndices; - std::vector omegaPlusIndices; - - for (auto const& mcParticle : mcParticles) { - double geta = mcParticle.eta(); - if (std::abs(geta) > etaSel) { - continue; + // Nch^gen of this MC collision (charged primaries in |eta| < 1), shared by the + // event-reconstruction-efficiency and PV-quality studies, the generated-level INEL + // selection and the Nch^gen vs multNTracksPVeta1 band cut + const bool applyGenInel = pairLossK0Configurations.applyINELSelectionAtGenLevel; + const bool applyBandCut = pairLossK0Configurations.applyNchResponseBandCut; + const int nChGen = (pairLossK0Configurations.doEventRecoEfficiencyStudy || pairLossK0Configurations.doPVQualityStudy || applyGenInel || applyBandCut) ? countNchGenEta1(mcParticles) : -1; + + // Part 3: a self-contained generator-level study. It deliberately touches no + // reconstructed quantity in its event selection or in any of its axes: the + // event is selected on generated INEL>0 (INEL>N if enabled) and the generated + // vertex only, the multiplicity is counted from generated particles, and every + // object is filled with generated coordinates. + // + // Reconstruction enters in exactly one place -- whether a generated object + // has a reconstructed counterpart at all -- and that splits the very same + // generated sample into Reconstructed/ and NotReconstructed/. Because all + // three folders share generated coordinates, Gen == Reconstructed + + // NotReconstructed bin by bin, so NotReconstructed/Gen reads directly as + // "in which pT, eta, phi and multiplicity region do generated objects fail + // to be reconstructed". + // + // "Reconstructed" is the loosest possible statement, with no quality + // selection of any kind: for a trigger, some track in some reconstructed + // collision of this MC collision carries its MC label; for a K0, some V0 + // candidate carries its MC core. Objects belonging to an MC collision that + // produced no reconstructed collision at all therefore land in + // NotReconstructed/ too; hEventCounter and hNRecoCollisions are there so + // that contribution can be separated out afterwards. + auto runGenLevelStudy = [&]() { + histos.fill(HIST("PairLossK0/GenStudy/hEventCounter"), 0.0f); + + // Generated-level event selection. No reconstructed variable is used. + // INEL>N (N >= 0) implies INEL>0, so only the tighter enabled selection has to be evaluated + if (masterConfigurations.selectINELgtN) { + if (!isInelGtNGen(mcParticles, masterConfigurations.inelGtNThreshold)) { + return; + } + } else if (masterConfigurations.selectINELgtZERO) { + if (!o2::pwglf::isINELgt0mc(mcParticles, pdgDB)) { + return; + } } - double gpt = mcParticle.pt(); - if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { - if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("hClosureQAPtTrigger"), gpt, 0.0f); // step 1: no event selection whatsoever + histos.fill(HIST("PairLossK0/GenStudy/hEventCounter"), 1.0f); + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + histos.fill(HIST("PairLossK0/GenStudy/hEventCounter"), 2.0f); + if (recCollisions.size() > 0) { + histos.fill(HIST("PairLossK0/GenStudy/hEventCounter"), 3.0f); + } + histos.fill(HIST("PairLossK0/GenStudy/hNRecoCollisions"), recCollisions.size()); + + // Multiplicity of this MC collision: generated charged physical primaries + // within |eta| < 0.8. Primaries always, independent of every analysis + // configurable, so that the multiplicity axis keeps one fixed meaning. + // + // Deliberately a local counter rather than a member: the shared mCounter + // only gets its PDG database wired up when processPrediction runs, and its + // mSelectPrimaries follows masterConfigurations.doAssocPhysicalPrimary. Adding a second counter + // as a task member is not an option either -- the struct is already at the + // member limit that Framework/StructToTuple.h can destructure. The object + // is a bool and a pointer, so building it per MC collision costs nothing. + o2::pwglf::ParticleCounter genStudyCounter; + genStudyCounter.mPdgDatabase = pdgDB.service; + genStudyCounter.mSelectPrimaries = true; + const float generatedNch = genStudyCounter.countEta08(mcParticles); + histos.fill(HIST("PairLossK0/GenStudy/hNch"), generatedNch); + + // Reconstructed-object bookkeeping. The framework has already grouped + // recCollisions by this MC collision, so dereferencing a reconstructed + // collision back to its MC collision needs no extra work here, and the + // generated event selection above is by construction identical for all of + // them. + std::unordered_set reconstructedTrackMcIds; + std::unordered_set reconstructedV0McIds; + std::vector genStudyTriggers; + std::vector genStudyK0s; + for (auto const& collision : recCollisions) { + const auto trackSlice = tracks.sliceBy(pairLossTracksPerCollision, collision.globalIndex()); + for (auto const& track : trackSlice) { + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (trackLabel.has_mcParticle()) { + reconstructedTrackMcIds.insert(trackLabel.mcParticleId()); + } + } + const auto v0Slice = v0Candidates.sliceBy(pairLossV0sPerCollision, collision.globalIndex()); + for (auto const& v0 : v0Slice) { + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() < 0 || v0MC.pdgCode() != PDG_t::kK0Short) { + continue; + } + reconstructedV0McIds.insert(v0MC.particleIdMC()); } } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { - if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { - histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 0.0f); // step 1: no event selection whatsoever + for (auto const& mcParticle : mcParticles) { + const float genPt = mcParticle.pt(); + const float genEta = mcParticle.eta(); + const float genPhi = mcParticle.phi(); + if (std::abs(genEta) > etaSel) { + continue; } - } - } - histos.fill(HIST("hClosureTestEventCounter"), 0.5f); + if (isPairLossTriggerPdg(mcParticle.pdgCode()) && + genPt >= axisRanges[3][0] && genPt <= axisRanges[3][1] && + (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + // Same charge requirement the other two parts apply, so that the three + // trigger definitions stay comparable. + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; + const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); + if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { + histos.fill(HIST("PairLossK0/GenStudy/Gen/hTrigger"), genPt, genEta, genPhi, generatedNch); + if (reconstructedTrackMcIds.contains(mcParticle.globalIndex())) { + histos.fill(HIST("PairLossK0/GenStudy/Reconstructed/hTrigger"), genPt, genEta, genPhi, generatedNch); + } else { + histos.fill(HIST("PairLossK0/GenStudy/NotReconstructed/hTrigger"), genPt, genEta, genPhi, generatedNch); + } + genStudyTriggers.push_back(GenStudyPairObject{ + .pt = genPt, + .eta = genEta, + .phi = genPhi, + .globalIndex = mcParticle.globalIndex(), + .motherIndex = mcParticle.has_mothers() ? mcParticle.mothers_first_as().globalIndex() : -1, + .reconstructed = reconstructedTrackMcIds.contains(mcParticle.globalIndex())}); + } + } - int bestCollisionFT0Mpercentile = -1; - float bestCollisionVtxZ = 0.0f; - bool bestCollisionSel8 = false; - bool bestCollisionINELgtZERO = false; - int biggestNContribs = -1; - uint32_t bestCollisionTriggerPresenceMap = 0; + if (mcParticle.pdgCode() == PDG_t::kK0Short && + genPt >= axisRanges[2][0] && genPt <= axisRanges[2][1] && + (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + // Same findability definition the stage ladder uses: decayed to + // pi+ pi- with both charged daughters inside the tracking acceptance. + // A K0 that is not findable could never have been reconstructed, so + // splitting on it is what makes NotReconstructed/ interpretable -- + // without it the folder is dominated by decays whose daughters simply + // left the acceptance. + bool hasPositiveDaughter = false; + bool hasNegativeDaughter = false; + bool daughtersInAcceptance = true; + for (auto const& daughter : mcParticle.daughters_as()) { + if (daughter.pdgCode() != PDG_t::kPiPlus && daughter.pdgCode() != -PDG_t::kPiPlus) { + continue; + } + if (daughter.pdgCode() == PDG_t::kPiPlus) { + hasPositiveDaughter = true; + } else { + hasNegativeDaughter = true; + } + if (daughter.pt() < pairLossK0Configurations.daughterPtMin || + std::abs(daughter.eta()) > pairLossK0Configurations.daughterEtaMax) { + daughtersInAcceptance = false; + } + } + const float k0Findable = (hasPositiveDaughter && hasNegativeDaughter && daughtersInAcceptance) ? 1.0f : 0.0f; - for (auto const& recCollision : recCollisions) { - if (biggestNContribs < recCollision.numContrib()) { - biggestNContribs = recCollision.numContrib(); - bestCollisionFT0Mpercentile = recCollision.centFT0M(); - bestCollisionSel8 = recCollision.sel8(); - bestCollisionVtxZ = recCollision.posZ(); - bestCollisionINELgtZERO = recCollision.isInelGt0(); - if (triggerPresenceMap.size() > 0) - bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; + histos.fill(HIST("PairLossK0/GenStudy/Gen/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); + if (reconstructedV0McIds.contains(mcParticle.globalIndex())) { + histos.fill(HIST("PairLossK0/GenStudy/Reconstructed/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); + } else { + histos.fill(HIST("PairLossK0/GenStudy/NotReconstructed/hK0Short"), genPt, genEta, genPhi, generatedNch, k0Findable); + } + genStudyK0s.push_back(GenStudyPairObject{ + .pt = genPt, + .eta = genEta, + .phi = genPhi, + .globalIndex = mcParticle.globalIndex(), + .motherIndex = -1, + .reconstructed = reconstructedV0McIds.contains(mcParticle.globalIndex())}); + } } - } - // ________________________________________________ - // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { - return; - } - if (masterConfigurations.doGenEventSelection) { - if (!bestCollisionSel8) - return; - if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) - return; - if (!bestCollisionINELgtZERO) - return; - if (bestCollisionFT0Mpercentile > axisRanges[5][1] || bestCollisionFT0Mpercentile < axisRanges[5][0]) { - return; + // h-K0 correlations of the objects collected above, in generated coordinates. + // Same delta-phi / delta-eta convention as every other correlation in this task + // (trigger minus associated), and the same autocorrelation rejection: a trigger + // that is a decay product of the K0 it would be paired with is skipped. + // Every pair goes into Gen/ and into exactly one of the four exclusive classes. + for (auto const& trigger : genStudyTriggers) { + for (auto const& k0 : genStudyK0s) { + if (trigger.globalIndex == k0.globalIndex || trigger.motherIndex == k0.globalIndex) { + continue; + } + const float deltaPhi = computeDeltaPhi(trigger.phi, k0.phi); + const float deltaEta = trigger.eta - k0.eta; + histos.fill(HIST("PairLossK0/GenStudy/Gen/hCorrelation"), deltaEta, deltaPhi, trigger.pt, k0.pt, generatedNch); + if (trigger.reconstructed && k0.reconstructed) { + histos.fill(HIST("PairLossK0/GenStudy/Reconstructed/hCorrelation"), deltaEta, deltaPhi, trigger.pt, k0.pt, generatedNch); + } else if (trigger.reconstructed) { + histos.fill(HIST("PairLossK0/GenStudy/OnlyTriggerReconstructed/hCorrelation"), deltaEta, deltaPhi, trigger.pt, k0.pt, generatedNch); + } else if (k0.reconstructed) { + histos.fill(HIST("PairLossK0/GenStudy/OnlyK0Reconstructed/hCorrelation"), deltaEta, deltaPhi, trigger.pt, k0.pt, generatedNch); + } else { + histos.fill(HIST("PairLossK0/GenStudy/NotReconstructed/hCorrelation"), deltaEta, deltaPhi, trigger.pt, k0.pt, generatedNch); + } + } } - } - - histos.fill(HIST("hClosureTestEventCounter"), 1.5f); + }; - for (auto const& mcParticle : mcParticles) { - double geta = mcParticle.eta(); - if (std::abs(geta) > etaSel) { - continue; + // Part 2. Wrapped in a lambda so that its own early exits leave the other + // two parts free to run: the three parts are independent, not exclusive. + auto runRecComparison = [&]() { + if (recCollisions.size() == 0) { + return; } - double gpt = mcParticle.pt(); - if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { - if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { - histos.fill(HIST("hClosureQAPtTrigger"), gpt, 1.0f); // step 2: after event selection + + int64_t bestCollisionId = -1; + int largestNContributors = -1; + float bestCollisionVtxX = 0.0f; + float bestCollisionVtxY = 0.0f; + float bestCollisionVtxZ = 0.0f; + float bestCollisionMultiplicity = -1.0f; + bool pairLossEventSelected = false; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; } + largestNContributors = collision.numContrib(); + bestCollisionId = collision.globalIndex(); + bestCollisionVtxX = collision.posX(); + bestCollisionVtxY = collision.posY(); + bestCollisionVtxZ = collision.posZ(); + bestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); + // with applyGenInel (pp only) the reconstructed INEL cuts are replaced by + // Nch^gen >= 1, as in the Rec and Gen stages; the band cut acts on top, + // independently of applyRecoEventSelection + pairLossEventSelected = (!pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) + : isCollisionSelectedPbPb(collision, false))) && + (!applyBandCut || isInNchResponseBand(nChGen, collision.multNTracksPVeta1())); + } + if (bestCollisionId < 0) { + return; } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { - if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { - histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 1.0f); // step 2: after event selection + // First build inclusive truth-acceptance lists for the early Rec control + // stages. Exact old PairLoss truth lists are derived separately below so + // that configured primary and leading-trigger requirements are applied + // in precisely their original order. + std::vector comparisonTruthTriggers; + std::vector comparisonTruthK0s; + for (auto const& mcParticle : mcParticles) { + // same acceptance as the TriggerMatchQA rejection bins + int sign = 0; + if (pairLossTruthTriggerRejection(mcParticle, sign) == PairLossTriggerMatchMatched) { + comparisonTruthTriggers.push_back(PairLossTruthTrackInfo{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .sign = sign, + .physicalPrimary = mcParticle.isPhysicalPrimary()}); } - } - } - int iteratorNum = -1; - for (auto const& mcParticle : mcParticles) { - iteratorNum = iteratorNum + 1; - double geta = mcParticle.eta(); - double gpt = mcParticle.pt(); - double gphi = mcParticle.phi(); - if (std::abs(geta) > etaSel) { - continue; - } - if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { - if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { - triggerIndices.emplace_back(iteratorNum); - histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionFT0Mpercentile); + if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || + mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1]) { + continue; } - if (masterConfigurations.doCorrelationHadron) { - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { - assocHadronIndices.emplace_back(iteratorNum); - histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); + PairLossTruthK0Info truthK0{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .decayRadius = -1.0f, + .findable = false, + .physicalPrimary = mcParticle.isPhysicalPrimary(), + .positiveDaughter = {}, + .negativeDaughter = {}}; + for (auto const& daughter : mcParticle.daughters_as()) { + PairLossTruthTrackInfo daughterInfo{ + .globalIndex = daughter.globalIndex(), + .pt = daughter.pt(), + .eta = daughter.eta(), + .phi = daughter.phi(), + .sign = daughter.pdgCode() > 0 ? 1 : -1, + .physicalPrimary = daughter.isPhysicalPrimary()}; + if (daughter.pdgCode() == PDG_t::kPiPlus) { + truthK0.positiveDaughter = daughterInfo; + } else if (daughter.pdgCode() == -PDG_t::kPiPlus) { + truthK0.negativeDaughter = daughterInfo; } } + comparisonTruthK0s.push_back(truthK0); } - if (!doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { - if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { - piIndices.emplace_back(iteratorNum); - histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); + + auto primaryTruthTriggers = comparisonTruthTriggers; + if (masterConfigurations.doTriggPhysicalPrimary) { + std::erase_if(primaryTruthTriggers, [](auto const& trigger) { return !trigger.physicalPrimary; }); + } + auto truthTriggers = primaryTruthTriggers; + if (useTheLeadingParticleAsTrigger && truthTriggers.size() > 1) { + const auto leadingTrigger = std::max_element(truthTriggers.begin(), truthTriggers.end(), [](auto const& lhs, auto const& rhs) { return lhs.pt < rhs.pt; }); + const auto leadingTriggerCopy = *leadingTrigger; + truthTriggers.clear(); + truthTriggers.push_back(leadingTriggerCopy); + } + auto truthK0s = comparisonTruthK0s; + if (masterConfigurations.doAssocPhysicalPrimary) { + std::erase_if(truthK0s, [](auto const& k0) { return !k0.physicalPrimary; }); + } + + // Truth: identical pair definition to the original PairLoss truth stage. + // Not booked with applyGenInel, like the rest of the ladder except Rec and Gen. + if (pairLossEventSelected && !applyGenInel) { + for (auto const& truthTrigger : truthTriggers) { + for (auto const& truthK0 : truthK0s) { + if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { + continue; + } + const float truthDeltaPhi = computeDeltaPhi(truthTrigger.phi, truthK0.phi); + float truthDeltaEta = truthTrigger.eta - truthK0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + truthDeltaEta = std::abs(truthDeltaEta); + } + if (truthDeltaPhi < axisRanges[0][0] || truthDeltaPhi > axisRanges[0][1] || + truthDeltaEta < axisRanges[1][0] || truthDeltaEta > axisRanges[1][1]) { + continue; + } + histos.fill(HIST("PairLossK0/Comparison/Truth"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); + } + } + } + + // Gen: reproduce the ClosureTest Gen event and particle/pair definition. + float genBestCollisionMultiplicity = -1.0f; + float genBestCollisionVtxZ = 0.0f; + bool genBestCollisionSel8 = false; + bool genBestCollisionINELgtZERO = false; + bool genBestCollisionINELgtN = false; + bool genBestCollisionNoSameBunchPileup = false; + bool genBestCollisionGoodTriggerTVX = false; + bool genBestCollisionGoodZvtxFT0vsPV = false; + bool genCollisionSelected = false; + int genLargestNContributors = -1; + int genBestCollisionNTracksPVeta1 = -1; + uint32_t genBestCollisionTriggerPresenceMap = 0; + for (auto const& recCollision : recCollisions) { + if (genLargestNContributors >= recCollision.numContrib()) { + continue; + } + genLargestNContributors = recCollision.numContrib(); + genBestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? recCollision.centFT0M() : recCollision.centFT0C(); + // Both branches fill the Gen THn with this vertex position + genBestCollisionVtxZ = recCollision.posZ(); + genBestCollisionNTracksPVeta1 = recCollision.multNTracksPVeta1(); + if (masterConfigurations.applyNewMCSelection) { + genCollisionSelected = (masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision, !applyGenInel)) || + (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb(recCollision, false)); + } else { + genBestCollisionSel8 = recCollision.sel8(); + genBestCollisionINELgtZERO = recCollision.isInelGt0(); + genBestCollisionINELgtN = isInelGtNReco(recCollision, masterConfigurations.inelGtNThreshold); + genBestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + genBestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); + genBestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); + } + if (!triggerPresenceMap.empty()) { + genBestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; + } + } + bool genEventSelected = triggerPresenceMap.empty() || TESTBIT(genBestCollisionTriggerPresenceMap, triggerBinToSelect); + if (masterConfigurations.applyNewMCSelection) { + genEventSelected = genEventSelected && genCollisionSelected && (!applyGenInel || isGenInelGt0(nChGen)); + } else if (masterConfigurations.doGenEventSelection) { + genEventSelected = genEventSelected && genBestCollisionSel8 && std::abs(genBestCollisionVtxZ) <= masterConfigurations.zVertexCut && + genBestCollisionINELgtZERO && (!masterConfigurations.selectINELgtN || genBestCollisionINELgtN) && + (!masterConfigurations.rejectSameBunchPileup || genBestCollisionNoSameBunchPileup) && + (!masterConfigurations.requireGoodTriggerTVX || genBestCollisionGoodTriggerTVX) && + (!masterConfigurations.requireGoodZvtxFT0vsPV || genBestCollisionGoodZvtxFT0vsPV) && + genBestCollisionMultiplicity >= axisRanges[5][0] && genBestCollisionMultiplicity <= axisRanges[5][1]; + } + // the band cut acts on top of either Gen event selection, on the best collision + if (applyBandCut) { + genEventSelected = genEventSelected && isInNchResponseBand(nChGen, genBestCollisionNTracksPVeta1); + } + + if (genEventSelected && masterConfigurations.doCorrelationK0Short && TESTBIT(doCorrelation, IndexK0)) { + std::vector genTriggerIndices; + std::vector genK0Indices; + int iteratorNumber = -1; + for (auto const& mcParticle : mcParticles) { + ++iteratorNumber; + if (std::abs(mcParticle.eta()) > etaSel) { + continue; + } + if (isPairLossTriggerPdg(mcParticle.pdgCode()) && (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + genTriggerIndices.push_back(iteratorNumber); + } + if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short && (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + genK0Indices.push_back(iteratorNumber); + } + } + for (auto const& triggerIndex : genTriggerIndices) { + auto triggerParticle = mcParticles.iteratorAt(triggerIndex); + if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { + continue; + } + if (applyGenInel) { + histos.fill(HIST("PairLossK0/Comparison/NchGen/TriggerGen"), triggerParticle.pt(), nChGen); + } + auto const& triggerMother = triggerParticle.mothers_first_as(); + const auto triggerMotherIndex = triggerMother.globalIndex(); + for (auto const& k0Index : genK0Indices) { + auto k0Particle = mcParticles.iteratorAt(k0Index); + if (triggerIndex == k0Index || triggerMotherIndex == k0Particle.globalIndex()) { + continue; + } + const float deltaPhi = computeDeltaPhi(triggerParticle.phi(), k0Particle.phi()); + const float deltaEta = triggerParticle.eta() - k0Particle.eta(); + if (deltaPhi < axisRanges[0][0] || deltaPhi > axisRanges[0][1] || deltaEta < axisRanges[1][0] || deltaEta > axisRanges[1][1] || + k0Particle.pt() < axisRanges[2][0] || k0Particle.pt() > axisRanges[2][1]) { + continue; + } + if (applyGenInel) { + histos.fill(HIST("PairLossK0/Comparison/NchGen/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), nChGen); + } else { + histos.fill(HIST("PairLossK0/Comparison/Gen"), deltaPhi, deltaEta, k0Particle.pt(), triggerParticle.pt(), genBestCollisionVtxZ, genBestCollisionMultiplicity); + } + } + } + } + + // Old Final control: reproduce the original PairLoss final-pair + // definition directly. This is deliberately independent of the Rec + // control chain below: it uses the old best-collision object maps, + // reconstructed-pair range and autocorrelation decision, then fills + // each accepted truth pair once with unit weight and truth coordinates. + PairLossTrackMap oldFinalTriggers; + const auto oldFinalTriggerSlice = triggerTracks.sliceBy(collisionSliceTracks, bestCollisionId); + for (auto const& triggerEntry : oldFinalTriggerSlice) { + auto track = triggerEntry.track_as(); + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (!trackLabel.has_mcParticle()) { + continue; + } + if (!isValidTrigger(track, triggerEntry.isLeading())) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerEntry.mcMask(), 13)) { + continue; + } + if (masterConfigurations.doTriggPhysicalPrimary && !triggerEntry.mcPhysicalPrimary()) { + continue; + } + oldFinalTriggers[trackLabel.mcParticleId()].push_back(makePairLossTrackInfo(track)); + } + + PairLossV0Map oldFinalV0s; + const auto oldFinalV0Slice = associatedV0s.sliceBy(collisionSliceV0s, bestCollisionId); + for (auto const& assocEntry : oldFinalV0Slice) { + auto v0 = assocEntry.v0Core_as(); + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() < 0 || v0MC.pdgCode() != PDG_t::kK0Short || !assocEntry.mcTrue(IndexK0)) { + continue; + } + auto positiveTrack = v0.posTrack_as(); + auto negativeTrack = v0.negTrack_as(); + bool passesFinalSelection = true; + if (masterConfigurations.doPPAnalysis) { + passesFinalSelection = v0.v0radius() >= v0Selection.v0RadiusMin && v0.v0radius() <= v0Selection.v0RadiusMax && + std::abs(v0.dcapostopv()) >= v0Selection.dcapostopv && std::abs(v0.dcanegtopv()) >= v0Selection.dcanegtopv && + v0.v0cosPA() >= v0Selection.v0cospa && v0.dcaV0daughters() <= v0Selection.dcaV0dau; + } else { + const float dcaCut = v0Selection.dcaDaugToPVForK0s == 0.0f ? v0Selection.dcaMesonToPV : v0Selection.dcaDaugToPVForK0s; + const bool passesLifetime = v0.distovertotmom(bestCollisionVtxX, bestCollisionVtxY, bestCollisionVtxZ) * + o2::constants::physics::MassK0Short < + v0Selection.lifetimecutK0S; + const bool passesDaughterDcaAndArmenteros = std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); + passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; + } + passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + if (trackSelection.checksRequireTPCChi2) { + passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && + negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; + } + if (trackSelection.requireClusterInITS) { + passesFinalSelection = passesFinalSelection && positiveTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks && + negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; + } + passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && + assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && + v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; + if (!passesFinalSelection) { + continue; + } + oldFinalV0s[v0MC.particleIdMC()].push_back(PairLossV0Info{ + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .radius = v0.v0radius(), + .cosPA = v0.v0cosPA(), + .dcaDaughters = v0.dcaV0daughters(), + .massNSigma = assocEntry.invMassNSigma(IndexK0)}); + } + + // Final pairs are recorded here and compared with Rec variant 14 after + // the exact Rec replica below has run. + struct PairLossFinalRecord { + PairLossPairKey key{}; + float deltaPhi = 0.0f; + float deltaEta = 0.0f; + float k0Pt = 0.0f; + float triggerPt = 0.0f; + }; + std::vector finalPairRecords; + + // Not filled with applyGenInel: finalPairRecords then stays empty, so + // FinalNotInRec below is not filled either. + if (pairLossEventSelected && !applyGenInel) { + for (auto const& truthTrigger : truthTriggers) { + if (!oldFinalTriggers.contains(truthTrigger.globalIndex)) { + continue; + } + histos.fill(HIST("PairLossK0/Comparison/TriggerVariants"), 9, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); + } + + for (auto const& truthTrigger : truthTriggers) { + const auto triggerMatches = oldFinalTriggers.find(truthTrigger.globalIndex); + if (triggerMatches == oldFinalTriggers.end()) { + continue; + } + for (auto const& truthK0 : truthK0s) { + if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { + continue; + } + float truthDeltaEta = truthTrigger.eta - truthK0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + truthDeltaEta = std::abs(truthDeltaEta); + } + const float truthDeltaPhi = computeDeltaPhi(truthTrigger.phi, truthK0.phi); + if (truthDeltaPhi < axisRanges[0][0] || truthDeltaPhi > axisRanges[0][1] || + truthDeltaEta < axisRanges[1][0] || truthDeltaEta > axisRanges[1][1]) { + continue; + } + const auto v0Matches = oldFinalV0s.find(truthK0.globalIndex); + if (v0Matches == oldFinalV0s.end()) { + continue; + } + bool oldFinalPair = false; + for (auto const& reconstructedTrigger : triggerMatches->second) { + for (auto const& reconstructedV0 : v0Matches->second) { + float reconstructedDeltaEta = reconstructedTrigger.eta - reconstructedV0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + reconstructedDeltaEta = std::abs(reconstructedDeltaEta); + } + const float reconstructedDeltaPhi = computeDeltaPhi(reconstructedTrigger.phi, reconstructedV0.phi); + if (reconstructedDeltaPhi < axisRanges[0][0] || reconstructedDeltaPhi > axisRanges[0][1] || + reconstructedDeltaEta < axisRanges[1][0] || reconstructedDeltaEta > axisRanges[1][1]) { + continue; + } + if (doAutocorrelationRejection && (reconstructedTrigger.globalIndex == reconstructedV0.positiveTrackId || reconstructedTrigger.globalIndex == reconstructedV0.negativeTrackId)) { + continue; + } + oldFinalPair = true; + break; + } + if (oldFinalPair) { + break; + } + } + if (oldFinalPair) { + histos.fill(HIST("PairLossK0/Comparison/Final"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); + histos.fill(HIST("PairLossK0/Comparison/RecVariants"), 15, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, bestCollisionVtxZ, bestCollisionMultiplicity); + finalPairRecords.push_back(PairLossFinalRecord{ + .key = PairLossPairKey{.triggerId = truthTrigger.globalIndex, .k0Id = truthK0.globalIndex}, + .deltaPhi = truthDeltaPhi, + .deltaEta = truthDeltaEta, + .k0Pt = truthK0.pt, + .triggerPt = truthTrigger.pt}); + } + } + } + } + + // Prepare MC-label lookup for the hook in the exact Rec signal branch. + pairLossComparison.clear(); + for (auto const& collision : recCollisions) { + for (auto const& track : tracks.sliceBy(pairLossTracksPerCollision, collision.globalIndex())) { + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (trackLabel.has_mcParticle()) { + pairLossComparison.trackToMc[track.globalIndex()] = trackLabel.mcParticleId(); + } + } + for (auto const& v0 : v0Candidates.sliceBy(pairLossV0sPerCollision, collision.globalIndex())) { + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() >= 0 && v0MC.pdgCode() == PDG_t::kK0Short) { + pairLossComparison.v0ToMc[v0.globalIndex()] = v0MC.particleIdMC(); + } + } + } + // Truth-trigger rejection bin of the MC particle behind every labelled trigger + // track, for PairLossK0/Comparison/TriggerMatchQA. mcParticles holds only the + // particles of this MC collision, so a label missing from the map points to + // another MC collision. + std::unordered_set triggerLabelIds; + for (auto const& collision : recCollisions) { + for (auto const& triggerEntry : triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex())) { + const auto triggerMcIt = pairLossComparison.trackToMc.find(triggerEntry.trackId()); + if (triggerMcIt != pairLossComparison.trackToMc.end()) { + triggerLabelIds.insert(triggerMcIt->second); + } + } + } + for (auto const& mcParticle : mcParticles) { + if (triggerLabelIds.contains(mcParticle.globalIndex())) { + int sign = 0; + pairLossComparison.triggerTruthRejection[mcParticle.globalIndex()] = pairLossTruthTriggerRejection(mcParticle, sign); + } + } + for (auto const& truthTrigger : comparisonTruthTriggers) { + pairLossComparison.truthTriggers[truthTrigger.globalIndex] = truthTrigger; + } + for (auto const& truthK0 : comparisonTruthK0s) { + pairLossComparison.truthK0s[truthK0.globalIndex] = truthK0; + } + for (auto const& truthTrigger : primaryTruthTriggers) { + pairLossComparison.primaryTruthTriggerIds.insert(truthTrigger.globalIndex); + } + for (auto const& truthK0 : truthK0s) { + pairLossComparison.primaryTruthK0Ids.insert(truthK0.globalIndex); + } + for (auto const& truthTrigger : truthTriggers) { + pairLossComparison.oldTruthTriggerIds.insert(truthTrigger.globalIndex); + } + for (auto const& truthK0 : truthK0s) { + pairLossComparison.oldTruthK0Ids.insert(truthK0.globalIndex); + } + for (auto const& record : finalPairRecords) { + pairLossComparison.finalTruthPairs.insert(record.key); + } + pairLossComparison.bestCollisionId = bestCollisionId; + pairLossComparison.vtxZ = bestCollisionVtxZ; + pairLossComparison.multiplicity = bestCollisionMultiplicity; + pairLossComparison.nChGen = applyGenInel ? nChGen : -1; + pairLossComparison.active = true; + for (auto const& recCollision : recCollisions) { + // recComparisonBestCollisionOnly: one reconstructed collision per MC collision, + // the best one, as in the Gen stage + if (pairLossK0Configurations.recComparisonBestCollisionOnly && recCollision.globalIndex() != bestCollisionId) { + continue; + } + // The band cut tests every reconstructed collision against the Nch^gen of + // this MC collision, so it also rejects split and duplicate vertices + if (applyBandCut && !isInNchResponseBand(nChGen, recCollision.multNTracksPVeta1())) { + continue; + } + const auto recTriggerSlice = triggerTracks.sliceBy(collisionSliceTracks, recCollision.globalIndex()); + const auto recV0Slice = associatedV0s.sliceBy(collisionSliceV0s, recCollision.globalIndex()); + const auto recTrackSlice = tracks.sliceBy(pairLossTracksPerCollision, recCollision.globalIndex()); + runSameEventHV0s(recCollision, recV0Slice, recTriggerSlice, recTrackSlice, + applyGenInel ? std::optional{nChGen} : std::nullopt); + } + + // Final-minus-Rec counterpart of RecNotInFinal. Both set differences use + // exactly the same truth-pair key and the same unit-weight truth axes. + for (auto const& record : finalPairRecords) { + if (pairLossComparison.countedPairs.contains(record.key)) { + continue; + } + histos.fill(HIST("PairLossK0/Comparison/FinalNotInRec"), record.deltaPhi, record.deltaEta, record.k0Pt, record.triggerPt, pairLossComparison.vtxZ, pairLossComparison.multiplicity); + } + + pairLossComparison.clear(); + }; + + // MC-collision reconstruction efficiency vs Nch^gen and the Nch^gen vs + // multNTracksPVeta1 and multNTracksPVeta1 vs FT0M (percentile and amplitude) + // responses. The event selection is the one runSameEventHV0s applies before + // its INEL cut: trigger presence + isCollisionSelected (isCollisionSelectedNoInel) + auto runEventRecoEfficiencyStudy = [&]() { + // Best collision: the one with the most PV contributors, as in the Rec comparison + int largestNContributors = -1; + bool bestCollisionSelectedNoInel = false; + bool bestCollisionSelected = false; + int bestCollisionNTracksPVeta1 = -1; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; + } + largestNContributors = collision.numContrib(); + bestCollisionSelectedNoInel = isCollisionSelectedNoInel(collision); + // reconstructed-level INEL cuts; the Pb-Pb event selection has none + bestCollisionSelected = bestCollisionSelectedNoInel && (!masterConfigurations.doPPAnalysis || isCollisionInelSelected(collision)); + bestCollisionNTracksPVeta1 = collision.multNTracksPVeta1(); + } + + // Nch^gen response, and with applyNchResponseBandCut the part of it inside the band + auto fillNchGenResponse = [&](int nTracksPVeta1) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1"), nChGen, nTracksPVeta1); + if (applyBandCut && isInNchResponseBand(nChGen, nTracksPVeta1)) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNTracksPVeta1InBand"), nChGen, nTracksPVeta1); + } + }; + + // Responses: one entry per reconstructed collision, as in the Rec path, except + // the Nch^gen response with nchGenResponseBestCollisionOnly (best collision only) + for (auto const& collision : recCollisions) { + if (isCollisionSelectedNoInel(collision)) { + if (!pairLossK0Configurations.nchGenResponseBestCollisionOnly) { + fillNchGenResponse(collision.multNTracksPVeta1()); + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsCentFT0M"), collision.multNTracksPVeta1(), collision.centFT0M()); + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNTracksPVeta1VsMultFT0M"), collision.multNTracksPVeta1(), collision.multFT0M()); + } + } + if (pairLossK0Configurations.nchGenResponseBestCollisionOnly && bestCollisionSelectedNoInel) { + fillNchGenResponse(bestCollisionNTracksPVeta1); + } + + // Efficiency: one entry per MC collision, evaluated on the best collision + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenAll"), nChGen); + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenVsNRecoColl"), nChGen, recCollisions.size()); + if (recCollisions.size() == 0) { + return; + } + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenHasRecoColl"), nChGen); + if (bestCollisionSelectedNoInel) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInel"), nChGen); + if (applyBandCut && isInNchResponseBand(nChGen, bestCollisionNTracksPVeta1)) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSelNoInelInBand"), nChGen); + } + } + if (bestCollisionSelected) { + histos.fill(HIST("PairLossK0/EventRecoEfficiency/hNchGenEvSel"), nChGen); + } + }; + + // PV quality vs midrapidity multiplicity, split by the truth h-K0 content of the MC + // collision (PairLossPVQualityClass). Tests whether PVs built from few tracks are + // biased or badly defined, and whether a trigger and a K0 in the event -- whose + // displaced daughters can enter the vertex fit -- make it worse: in the PV position + // and residuals of the best collision, and in how often that collision is missing + // or fails the event selection of PairLossK0/Comparison + auto runPVQualityStudy = [&]() { + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + + // Truth triggers and K0s as in PairLossK0/Comparison/Truth: same acceptance, + // primary requirements applied before the leading-trigger choice + struct PVQualityTrigger { + int64_t globalIndex = -1; + float pt = 0.0f; + float phi = 0.0f; + }; + struct PVQualityK0 { + float phi = 0.0f; + int64_t positiveDaughterId = -1; + int64_t negativeDaughterId = -1; + }; + std::vector pvTriggers; + std::vector pvK0s; + for (auto const& mcParticle : mcParticles) { + int sign = 0; + if (pairLossTruthTriggerRejection(mcParticle, sign) == PairLossTriggerMatchMatched && + (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + pvTriggers.push_back(PVQualityTrigger{mcParticle.globalIndex(), mcParticle.pt(), mcParticle.phi()}); + } + if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || + mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + continue; + } + PVQualityK0 pvK0{mcParticle.phi(), -1, -1}; + for (auto const& daughter : mcParticle.daughters_as()) { + if (daughter.pdgCode() == PDG_t::kPiPlus) { + pvK0.positiveDaughterId = daughter.globalIndex(); + } else if (daughter.pdgCode() == -PDG_t::kPiPlus) { + pvK0.negativeDaughterId = daughter.globalIndex(); + } + } + pvK0s.push_back(pvK0); + } + if (useTheLeadingParticleAsTrigger && pvTriggers.size() > 1) { + const auto leadingTrigger = *std::max_element(pvTriggers.begin(), pvTriggers.end(), [](auto const& lhs, auto const& rhs) { return lhs.pt < rhs.pt; }); + pvTriggers.assign(1, leadingTrigger); + } + + constexpr double PVQualitySideHalfWidth = PI / 3.0; + std::array inClass{}; + inClass[PairLossPVQualityAll] = true; + inClass[PairLossPVQualityHasTrigger] = !pvTriggers.empty(); + for (auto const& trigger : pvTriggers) { + for (auto const& k0 : pvK0s) { + if (trigger.globalIndex == k0.positiveDaughterId || trigger.globalIndex == k0.negativeDaughterId) { + continue; + } + const double deltaPhi = computeDeltaPhi(trigger.phi, k0.phi); + inClass[PairLossPVQualityHasPair] = true; + if (std::abs(deltaPhi) < PVQualitySideHalfWidth) { + inClass[PairLossPVQualityNearPair] = true; + } + if (std::abs(deltaPhi - PI) < PVQualitySideHalfWidth) { + inClass[PairLossPVQualityAwayPair] = true; + } + } + } + + auto fillEventStage = [&](int stage) { + for (int eventClass = 0; eventClass < PairLossPVQualityNClasses; ++eventClass) { + if (inClass[eventClass]) { + histos.fill(HIST("PairLossK0/PVQuality/hEventStage"), eventClass, stage, nChGen); + } + } + }; + fillEventStage(PairLossPVQualityStageAll); + if (recCollisions.size() == 0) { + return; + } + + // Best collision: the one with the most PV contributors, as in the Rec comparison + int largestNContributors = -1; + int64_t bestCollisionId = -1; + float bestCollisionVtxX = 0.0f; + float bestCollisionVtxY = 0.0f; + float bestCollisionVtxZ = 0.0f; + int bestCollisionNTracksPVeta1 = -1; + bool bestCollisionSelected = false; + for (auto const& collision : recCollisions) { + if (collision.numContrib() <= largestNContributors) { + continue; + } + largestNContributors = collision.numContrib(); + bestCollisionId = collision.globalIndex(); + bestCollisionVtxX = collision.posX(); + bestCollisionVtxY = collision.posY(); + bestCollisionVtxZ = collision.posZ(); + bestCollisionNTracksPVeta1 = collision.multNTracksPVeta1(); + // same event selection as pairLossEventSelected in runRecComparison + bestCollisionSelected = (!pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision, !applyGenInel) && (!applyGenInel || isGenInelGt0(nChGen)) + : isCollisionSelectedPbPb(collision, false))) && + (!applyBandCut || isInNchResponseBand(nChGen, collision.multNTracksPVeta1())); + } + if (bestCollisionId < 0) { + return; + } + + // Direct check of the K0 side of the hypothesis: a daughter of a truth K0 above + // was used in the vertex fit of the best collision + std::unordered_set pvK0DaughterIds; + for (auto const& k0 : pvK0s) { + for (const int64_t daughterId : {k0.positiveDaughterId, k0.negativeDaughterId}) { + if (daughterId >= 0) { + pvK0DaughterIds.insert(daughterId); + } + } + } + if (!pvK0DaughterIds.empty()) { + const auto trackSlice = tracks.sliceBy(pairLossTracksPerCollision, bestCollisionId); + for (auto const& track : trackSlice) { + if (!track.isPVContributor()) { + continue; + } + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (trackLabel.has_mcParticle() && pvK0DaughterIds.contains(trackLabel.mcParticleId())) { + inClass[PairLossPVQualityK0DaughterPVContributor] = true; + break; + } + } + } + + fillEventStage(PairLossPVQualityStageHasRecoColl); + if (bestCollisionSelected) { + fillEventStage(PairLossPVQualityStageSelected); + } + + const float residualX = bestCollisionVtxX - mcCollision.posX(); + const float residualY = bestCollisionVtxY - mcCollision.posY(); + const float residualZ = bestCollisionVtxZ - mcCollision.posZ(); + auto fillBestCollision = [&](int selectionBin) { + for (int eventClass = 0; eventClass < PairLossPVQualityNClasses; ++eventClass) { + if (!inClass[eventClass]) { + continue; + } + histos.fill(HIST("PairLossK0/PVQuality/hPosX"), eventClass, selectionBin, bestCollisionNTracksPVeta1, bestCollisionVtxX); + histos.fill(HIST("PairLossK0/PVQuality/hPosY"), eventClass, selectionBin, bestCollisionNTracksPVeta1, bestCollisionVtxY); + histos.fill(HIST("PairLossK0/PVQuality/hResX"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualX); + histos.fill(HIST("PairLossK0/PVQuality/hResY"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualY); + histos.fill(HIST("PairLossK0/PVQuality/hResZ"), eventClass, selectionBin, bestCollisionNTracksPVeta1, residualZ); + } + }; + fillBestCollision(0); + if (bestCollisionSelected) { + fillBestCollision(1); + } + }; + + if (pairLossK0Configurations.doEventRecoEfficiencyStudy) { + runEventRecoEfficiencyStudy(); + } + if (pairLossK0Configurations.doPVQualityStudy) { + runPVQualityStudy(); + } + if (pairLossK0Configurations.doGenLevelStudy) { + runGenLevelStudy(); + } + if (pairLossK0Configurations.doRecComparison) { + runRecComparison(); + } + if (!pairLossK0Configurations.doStageDiagnostics) { + return; + } + + // Part 1 follows. + histos.fill(HIST("PairLossK0/Event/hCounter"), 0.0f); + histos.fill(HIST("PairLossK0/Event/hNRecoCollisions"), recCollisions.size()); + + if (recCollisions.size() == 0) { + return; + } + histos.fill(HIST("PairLossK0/Event/hCounter"), 1.0f); + + int64_t bestCollisionId = -1; + int largestNContributors = -1; + for (auto const& collision : recCollisions) { + if (collision.numContrib() > largestNContributors) { + largestNContributors = collision.numContrib(); + bestCollisionId = collision.globalIndex(); + } + } + if (bestCollisionId < 0) { + return; + } + + PairLossTrackMap tracksAnyCollision; + PairLossV0Map v0sAnyCollision; + for (auto const& collision : recCollisions) { + const auto trackSlice = tracks.sliceBy(pairLossTracksPerCollision, collision.globalIndex()); + for (auto const& track : trackSlice) { + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (!trackLabel.has_mcParticle()) { + continue; + } + tracksAnyCollision[trackLabel.mcParticleId()].push_back(makePairLossTrackInfo(track)); + } + const auto v0Slice = v0Candidates.sliceBy(pairLossV0sPerCollision, collision.globalIndex()); + for (auto const& v0 : v0Slice) { + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() < 0 || v0MC.pdgCode() != PDG_t::kK0Short) { + continue; + } + v0sAnyCollision[v0MC.particleIdMC()].push_back(PairLossV0Info{ + .globalIndex = v0.globalIndex(), + .positiveTrackId = static_cast(v0.posTrackId()), + .negativeTrackId = static_cast(v0.negTrackId()), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .radius = v0.v0radius(), + .cosPA = v0.v0cosPA(), + .dcaDaughters = v0.dcaV0daughters(), + .massNSigma = 0.0f}); + } + } + + for (auto const& collision : recCollisions) { + if (collision.globalIndex() != bestCollisionId) { + continue; + } + + const bool collisionSelected = !pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision) : isCollisionSelectedPbPb(collision, false)); + if (!collisionSelected) { + return; + } + histos.fill(HIST("PairLossK0/Event/hCounter"), 2.0f); + + const auto bc = collision.bc_as(); + const double magneticField = getPairLossMagneticField(bc.runNumber(), bc.timestamp()); + const int magneticFieldSign = magneticField > 0.0 ? 1 : (magneticField < 0.0 ? -1 : 0); + const float multiplicity = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); + + PairLossTrackMap tracksBestCollision; + const auto bestTrackSlice = tracks.sliceBy(pairLossTracksPerCollision, bestCollisionId); + for (auto const& track : bestTrackSlice) { + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (!trackLabel.has_mcParticle()) { + continue; + } + tracksBestCollision[trackLabel.mcParticleId()].push_back(makePairLossTrackInfo(track)); + } + + PairLossV0Map v0sBestCollision; + const auto bestV0Slice = v0Candidates.sliceBy(pairLossV0sPerCollision, bestCollisionId); + for (auto const& v0 : bestV0Slice) { + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() < 0 || v0MC.pdgCode() != PDG_t::kK0Short) { + continue; + } + v0sBestCollision[v0MC.particleIdMC()].push_back(PairLossV0Info{ + .globalIndex = v0.globalIndex(), + .positiveTrackId = static_cast(v0.posTrackId()), + .negativeTrackId = static_cast(v0.negTrackId()), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .radius = v0.v0radius(), + .cosPA = v0.v0cosPA(), + .dcaDaughters = v0.dcaV0daughters(), + .massNSigma = 0.0f}); + } + + PairLossTrackMap triggersInTable; + PairLossTrackMap triggersFinal; + PairLossV0Map v0sInTable; + PairLossV0Map v0sFinal; + for (auto const& finalCollision : recCollisions) { + const int64_t finalCollisionId = finalCollision.globalIndex(); + const bool isBestCollision = finalCollisionId == bestCollisionId; + if (!isBestCollision) { + if (pairLossK0Configurations.finalPairUseBestCollisionOnly) { + continue; + } + if (pairLossK0Configurations.applyRecoEventSelection && + !(masterConfigurations.doPPAnalysis ? isCollisionSelected(finalCollision) : isCollisionSelectedPbPb(finalCollision, false))) { + continue; + } + } + + const auto triggerSlice = triggerTracks.sliceBy(collisionSliceTracks, finalCollisionId); + for (auto const& triggerEntry : triggerSlice) { + auto track = triggerEntry.track_as(); + const auto trackLabel = trackLabels.iteratorAt(track.globalIndex()); + if (!trackLabel.has_mcParticle()) { + continue; + } + const int64_t mcParticleId = trackLabel.mcParticleId(); + auto trackInfo = makePairLossTrackInfo(track); + trackInfo.collisionId = finalCollisionId; + if (isBestCollision) { + triggersInTable[mcParticleId].push_back(trackInfo); + } + if (!isValidTrigger(track, triggerEntry.isLeading())) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerEntry.mcMask(), 13)) { + continue; + } + if (masterConfigurations.doTriggPhysicalPrimary && !triggerEntry.mcPhysicalPrimary()) { + continue; + } + triggersFinal[mcParticleId].push_back(trackInfo); + } + + const auto assocV0Slice = associatedV0s.sliceBy(collisionSliceV0s, finalCollisionId); + for (auto const& assocEntry : assocV0Slice) { + auto v0 = assocEntry.v0Core_as(); + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() < 0 || v0MC.pdgCode() != PDG_t::kK0Short || !assocEntry.mcTrue(IndexK0)) { + continue; + } + auto positiveTrack = v0.posTrack_as(); + auto negativeTrack = v0.negTrack_as(); + const PairLossV0Info v0Info{ + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .radius = v0.v0radius(), + .cosPA = v0.v0cosPA(), + .dcaDaughters = v0.dcaV0daughters(), + .massNSigma = assocEntry.invMassNSigma(IndexK0), + .collisionId = finalCollisionId}; + if (isBestCollision) { + v0sInTable[v0MC.particleIdMC()].push_back(v0Info); + } + + bool passesFinalSelection = true; + if (masterConfigurations.doPPAnalysis) { + passesFinalSelection = v0.v0radius() >= v0Selection.v0RadiusMin && v0.v0radius() <= v0Selection.v0RadiusMax && + std::abs(v0.dcapostopv()) >= v0Selection.dcapostopv && std::abs(v0.dcanegtopv()) >= v0Selection.dcanegtopv && + v0.v0cosPA() >= v0Selection.v0cospa && v0.dcaV0daughters() <= v0Selection.dcaV0dau; + } else { + const float dcaCut = v0Selection.dcaDaugToPVForK0s == 0.0f ? v0Selection.dcaMesonToPV : v0Selection.dcaDaugToPVForK0s; + const bool passesLifetime = v0.distovertotmom(finalCollision.posX(), finalCollision.posY(), finalCollision.posZ()) * o2::constants::physics::MassK0Short < v0Selection.lifetimecutK0S; + const bool passesDaughterDcaAndArmenteros = std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); + passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; + } + passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + if (trackSelection.checksRequireTPCChi2) { + passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && + negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; + } + if (trackSelection.requireClusterInITS) { + passesFinalSelection = passesFinalSelection && positiveTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks && + negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; + } + passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && + assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && + v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; + if (passesFinalSelection) { + v0sFinal[v0MC.particleIdMC()].push_back(v0Info); + } + } + } + + std::vector truthTriggers; + std::vector truthK0s; + for (auto const& mcParticle : mcParticles) { + if (isPairLossTriggerPdg(mcParticle.pdgCode()) && std::abs(mcParticle.eta()) <= etaSel && + mcParticle.pt() >= axisRanges[3][0] && mcParticle.pt() <= axisRanges[3][1] && + (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; + const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); + if ((triggerParticleCharge > 0 && sign < 0) || (triggerParticleCharge < 0 && sign > 0) || sign == 0) { + continue; + } + truthTriggers.push_back(PairLossTruthTrackInfo{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .sign = sign}); + } + + if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || + mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + continue; + } + PairLossTruthK0Info truthK0{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .decayRadius = -1.0f, + .findable = false, + .positiveDaughter = {}, + .negativeDaughter = {}}; + const auto daughters = mcParticle.daughters_as(); + for (auto const& daughter : daughters) { + PairLossTruthTrackInfo daughterInfo{ + .globalIndex = daughter.globalIndex(), + .pt = daughter.pt(), + .eta = daughter.eta(), + .phi = daughter.phi(), + .sign = daughter.pdgCode() > 0 ? 1 : -1}; + if (daughter.pdgCode() == PDG_t::kPiPlus) { + truthK0.positiveDaughter = daughterInfo; + } else if (daughter.pdgCode() == -PDG_t::kPiPlus) { + truthK0.negativeDaughter = daughterInfo; + } else { + continue; + } + const float daughterDecayRadius = std::hypot(daughter.vx() - mcCollision.posX(), daughter.vy() - mcCollision.posY()); + if (truthK0.decayRadius < 0.0f) { + truthK0.decayRadius = daughterDecayRadius; + } + } + const bool hasPionDaughters = truthK0.positiveDaughter.globalIndex >= 0 && truthK0.negativeDaughter.globalIndex >= 0; + truthK0.findable = hasPionDaughters && truthK0.positiveDaughter.pt >= pairLossK0Configurations.daughterPtMin && + truthK0.negativeDaughter.pt >= pairLossK0Configurations.daughterPtMin && + std::abs(truthK0.positiveDaughter.eta) <= pairLossK0Configurations.daughterEtaMax && + std::abs(truthK0.negativeDaughter.eta) <= pairLossK0Configurations.daughterEtaMax; + truthK0s.push_back(truthK0); + } + + if (useTheLeadingParticleAsTrigger && truthTriggers.size() > 1) { + const auto leadingTrigger = std::max_element(truthTriggers.begin(), truthTriggers.end(), [](auto const& lhs, auto const& rhs) { return lhs.pt < rhs.pt; }); + const auto leadingTriggerCopy = *leadingTrigger; + truthTriggers.clear(); + truthTriggers.push_back(leadingTriggerCopy); + } + if (!truthTriggers.empty()) { + histos.fill(HIST("PairLossK0/Event/hCounter"), 3.0f); + } + if (!truthK0s.empty()) { + histos.fill(HIST("PairLossK0/Event/hCounter"), 4.0f); + } + + auto contains = [](auto const& map, int64_t key) { + auto const iterator = map.find(key); + return iterator != map.end() && !iterator->second.empty(); + }; + auto numberOfMatches = [](auto const& map, int64_t key) -> size_t { + auto const iterator = map.find(key); + return iterator == map.end() ? 0 : iterator->second.size(); + }; + auto bestTrack = [](PairLossTrackMap const& map, int64_t key) -> PairLossTrackInfo const* { + auto const iterator = map.find(key); + if (iterator == map.end() || iterator->second.empty()) { + return nullptr; + } + return &*std::max_element(iterator->second.begin(), iterator->second.end(), [](auto const& lhs, auto const& rhs) { + if (lhs.tpcCrossedRows != rhs.tpcCrossedRows) { + return lhs.tpcCrossedRows < rhs.tpcCrossedRows; + } + return lhs.itsClusters < rhs.itsClusters; + }); + }; + auto bestV0 = [](PairLossV0Map const& map, int64_t key) -> PairLossV0Info const* { + auto const iterator = map.find(key); + if (iterator == map.end() || iterator->second.empty()) { + return nullptr; + } + return &*std::max_element(iterator->second.begin(), iterator->second.end(), [](auto const& lhs, auto const& rhs) { return lhs.cosPA < rhs.cosPA; }); + }; + + // Separate from the pair loop, which would count a trigger once per truth K0 and drop it entirely in events without one. + // Each stage holds the trigger-side condition of the same stage of hCounts/hPhysics, so the ratio keeps one trigger population. + for (auto const& truthTrigger : truthTriggers) { + const std::array triggerStagePassed = { + true, // Gen pair + true, // Findable K0->pi+pi-: K0 side only + contains(tracksBestCollision, truthTrigger.globalIndex), // Trigger pure reco (best collision) + contains(triggersInTable, truthTrigger.globalIndex), // Trigger in TriggerTracks + contains(triggersFinal, truthTrigger.globalIndex), // Trigger final selection + true, // V0 pure reco: K0 side only + true, // V0 candidate: K0 side only + true, // V0 in AssocV0s: K0 side only + true, // V0 final selection: K0 side only + contains(triggersFinal, truthTrigger.globalIndex)}; // Final reconstructed pair + std::array triggerStageFills{}; + triggerStageFills.fill(1); + if (!pairLossK0Configurations.fillFinalPairOnce) { + triggerStageFills[PairLossTriggerPureReco] = static_cast(numberOfMatches(tracksBestCollision, truthTrigger.globalIndex)); + triggerStageFills[PairLossTriggerInTable] = static_cast(numberOfMatches(triggersInTable, truthTrigger.globalIndex)); + triggerStageFills[PairLossTriggerFinal] = static_cast(numberOfMatches(triggersFinal, truthTrigger.globalIndex)); + triggerStageFills[PairLossFinalPair] = static_cast(numberOfMatches(triggersFinal, truthTrigger.globalIndex)); + } + for (int stage = 0; stage < PairLossK0NStages; ++stage) { + if (!triggerStagePassed[stage]) { + continue; + } + for (int fill = 0; fill < triggerStageFills[stage]; ++fill) { + histos.fill(HIST("PairLossK0/Stage/hTriggers"), stage, truthTrigger.pt, truthTrigger.eta, truthTrigger.phi, collision.posZ(), multiplicity); + } + } + } + + bool hasTruthPair = false; + for (auto const& truthTrigger : truthTriggers) { + for (auto const& truthK0 : truthK0s) { + if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { + continue; + } + const float truthDeltaPhi = computeDeltaPhi(truthTrigger.phi, truthK0.phi); + float truthDeltaEta = truthTrigger.eta - truthK0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + truthDeltaEta = std::abs(truthDeltaEta); + } + if (truthDeltaPhi < axisRanges[0][0] || truthDeltaPhi > axisRanges[0][1] || truthDeltaEta < axisRanges[1][0] || truthDeltaEta > axisRanges[1][1]) { + continue; + } + hasTruthPair = true; + + const auto positiveDeltaPhiStar = calculateMinimumDeltaPhiStar(truthTrigger, truthK0.positiveDaughter, magneticField, truthK0.decayRadius); + const auto negativeDeltaPhiStar = calculateMinimumDeltaPhiStar(truthTrigger, truthK0.negativeDaughter, magneticField, truthK0.decayRadius); + PairLossDeltaPhiStarInfo closestDeltaPhiStar; + PairLossTruthTrackInfo const* closestDaughter = nullptr; + if (positiveDeltaPhiStar.valid && (!negativeDeltaPhiStar.valid || positiveDeltaPhiStar.minAbs <= negativeDeltaPhiStar.minAbs)) { + closestDeltaPhiStar = positiveDeltaPhiStar; + closestDaughter = &truthK0.positiveDaughter; + } else if (negativeDeltaPhiStar.valid) { + closestDeltaPhiStar = negativeDeltaPhiStar; + closestDaughter = &truthK0.negativeDaughter; + } + const float closestDeltaEta = closestDaughter != nullptr ? truthTrigger.eta - closestDaughter->eta : 999.0f; + const int closestChargeProduct = closestDaughter != nullptr ? truthTrigger.sign * closestDaughter->sign : 0; + + if (truthK0.findable) { + histos.fill(HIST("PairLossK0/Geometry/hDeltaPhiStarValidity"), closestDeltaPhiStar.valid, truthK0.pt); + if (positiveDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/Geometry/hGeneratedDaughters"), positiveDeltaPhiStar.signedAtMin, truthTrigger.eta - truthK0.positiveDaughter.eta, truthK0.pt, truthTrigger.pt, magneticFieldSign, truthTrigger.sign * truthK0.positiveDaughter.sign); + } + if (negativeDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/Geometry/hGeneratedDaughters"), negativeDeltaPhiStar.signedAtMin, truthTrigger.eta - truthK0.negativeDaughter.eta, truthK0.pt, truthTrigger.pt, magneticFieldSign, truthTrigger.sign * truthK0.negativeDaughter.sign); + } + if (closestDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/Geometry/hRawDeltaPhiVsSignedDeltaPhiStar"), computeDeltaPhi(truthTrigger.phi, closestDaughter->phi), closestDeltaPhiStar.signedAtMin, magneticFieldSign); + histos.fill(HIST("PairLossK0/Geometry/hGeneratedMinDeltaPhiStarVsDecayRadius"), closestDeltaPhiStar.minAbs, truthK0.decayRadius, truthK0.pt); + histos.fill(HIST("PairLossK0/Geometry/hGeneratedRadiusAtMinimum"), closestDeltaPhiStar.radiusAtMin, closestDeltaPhiStar.minAbs, truthK0.pt); + } + } + + const bool triggerBestCollision = contains(tracksBestCollision, truthTrigger.globalIndex); + const bool triggerInTable = contains(triggersInTable, truthTrigger.globalIndex); + const bool triggerFinal = contains(triggersFinal, truthTrigger.globalIndex); + const bool positiveDaughterBestCollision = contains(tracksBestCollision, truthK0.positiveDaughter.globalIndex); + const bool negativeDaughterBestCollision = contains(tracksBestCollision, truthK0.negativeDaughter.globalIndex); + const bool bothDaughtersBestCollision = positiveDaughterBestCollision && negativeDaughterBestCollision; + const bool v0BestCollision = contains(v0sBestCollision, truthK0.globalIndex); + const bool v0InTable = contains(v0sInTable, truthK0.globalIndex); + const bool v0Final = contains(v0sFinal, truthK0.globalIndex); + + histos.fill(HIST("PairLossK0/Matching/hNMatches"), 0.0f, numberOfMatches(tracksAnyCollision, truthTrigger.globalIndex)); + histos.fill(HIST("PairLossK0/Matching/hNMatches"), 1.0f, numberOfMatches(tracksBestCollision, truthTrigger.globalIndex)); + histos.fill(HIST("PairLossK0/Matching/hNMatches"), 2.0f, numberOfMatches(triggersFinal, truthTrigger.globalIndex)); + histos.fill(HIST("PairLossK0/Matching/hNMatches"), 3.0f, numberOfMatches(v0sAnyCollision, truthK0.globalIndex)); + histos.fill(HIST("PairLossK0/Matching/hNMatches"), 4.0f, numberOfMatches(v0sBestCollision, truthK0.globalIndex)); + histos.fill(HIST("PairLossK0/Matching/hNMatches"), 5.0f, numberOfMatches(v0sInTable, truthK0.globalIndex)); + histos.fill(HIST("PairLossK0/Matching/hNMatches"), 6.0f, numberOfMatches(v0sFinal, truthK0.globalIndex)); + + bool pairBeforeAutocorrelation = false; + bool rejectedByAutocorrelation = false; + std::vector> finalPairCombinations; + if (triggerFinal && v0Final) { + for (auto const& reconstructedTrigger : triggersFinal.at(truthTrigger.globalIndex)) { + for (auto const& reconstructedV0 : v0sFinal.at(truthK0.globalIndex)) { + if (reconstructedTrigger.collisionId != reconstructedV0.collisionId) { + continue; + } + const float reconstructedDeltaPhi = computeDeltaPhi(reconstructedTrigger.phi, reconstructedV0.phi); + float reconstructedDeltaEta = reconstructedTrigger.eta - reconstructedV0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + reconstructedDeltaEta = std::abs(reconstructedDeltaEta); + } + if (reconstructedDeltaPhi < axisRanges[0][0] || reconstructedDeltaPhi > axisRanges[0][1] || reconstructedDeltaEta < axisRanges[1][0] || reconstructedDeltaEta > axisRanges[1][1]) { + continue; + } + pairBeforeAutocorrelation = true; + if (doAutocorrelationRejection && (reconstructedTrigger.globalIndex == reconstructedV0.positiveTrackId || reconstructedTrigger.globalIndex == reconstructedV0.negativeTrackId)) { + rejectedByAutocorrelation = true; + continue; + } + finalPairCombinations.emplace_back(reconstructedTrigger, reconstructedV0); + if (pairLossK0Configurations.fillFinalPairOnce) { + break; + } + } + if (pairLossK0Configurations.fillFinalPairOnce && !finalPairCombinations.empty()) { + break; + } + } + } + const bool finalPair = !finalPairCombinations.empty(); + const int finalPairFills = static_cast(finalPairCombinations.size()); + + // Order must match PairLossK0Stage / PairLossK0StageNames one to one. + // positiveDaughterBestCollision and negativeDaughterBestCollision are + // intentionally absent: the per-daughter breakdown lives in + // State/hDaughterTrackStateClose, and pairBeforeAutocorrelation in + // Geometry/hAutocorrelationRejected. + const std::array stagePassed = { + true, + truthK0.findable, + triggerBestCollision, + triggerInTable, + triggerFinal, + bothDaughtersBestCollision, + v0BestCollision, + v0InTable, + v0Final, + finalPair}; + for (int stage = 0; stage < PairLossK0NStages; ++stage) { + if (!stagePassed[stage]) { + continue; + } + const int stageFills = (stage == PairLossFinalPair && !pairLossK0Configurations.fillFinalPairOnce) ? finalPairFills : 1; + for (int fill = 0; fill < stageFills; ++fill) { + histos.fill(HIST("PairLossK0/Stage/hCounts"), stage); + histos.fill(HIST("PairLossK0/Stage/hPhysics"), stage, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, multiplicity); + if (truthK0.findable) { + histos.fill(HIST("PairLossK0/Stage/hCountsFindable"), stage); + histos.fill(HIST("PairLossK0/Stage/hPhysicsFindable"), stage, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, multiplicity); + if (closestDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/Stage/hClose"), stage, closestDeltaPhiStar.minAbs, closestDeltaEta, truthK0.pt, truthTrigger.pt, magneticFieldSign, closestChargeProduct); + } + } + } + } + + // Attribution of the stage3 -> stage4 gate. Only meaningful for triggers that already + // exist in the best collision. + // + // Key convention: the Passed bin is decided by the real triggerInTable, not by the + // replica. An earlier version ran the replica on bestTrack() and took that as the final + // answer, which left the Passed count of this plot more than ten percent away from + // stage 4 (the replica uses this task's triggerTracks* configurables, while the real + // table is produced by hStrangeCorrelationFilter.cxx from its own configurables; the two + // are kept aligned by hand through comments and diverge as soon as the json changes only + // the filter). + // + // The present definition is aligned by construction: + // count(Passed) == count(stage 4) + // count(Passed) + count(Failed bins) == count(stage 3) + // The replica is used for attribution only once the real table has already said "not in"; + // the two Unexplained/Rejects bins capture configuration drift, and a non-zero entry in + // either means this task's triggerTracks* have to be realigned with the filter. + if (triggerBestCollision) { + int failureReason = PairLossTriggerTracksPassed; + if (triggerInTable) { + // Really passed. Also check whether the replica agrees, to monitor configuration drift. + auto const* bestCollisionTrigger = bestTrack(tracksBestCollision, truthTrigger.globalIndex); + if (bestCollisionTrigger != nullptr && + classifyTriggerTracksFailure(*bestCollisionTrigger) != PairLossTriggerTracksPassed) { + failureReason = PairLossTriggerTracksInTableButCutRejects; + } + } else { + // Really failed. Attribute using the candidate track in the best collision that got + // "furthest": classifyTriggerTracksFailure returns following the early-return order of + // isValidTrigger, so a larger return value means the track was rejected later, and the + // maximum therefore corresponds to the candidate closest to passing. The semantics are + // "even the best candidate fell at this cut". + failureReason = PairLossTriggerTracksNotInTableUnexplained; + auto const iterator = tracksBestCollision.find(truthTrigger.globalIndex); + if (iterator != tracksBestCollision.end()) { + int furthest = -1; + for (auto const& candidate : iterator->second) { + const int reason = classifyTriggerTracksFailure(candidate); + if (reason == PairLossTriggerTracksPassed) { + // The replica says this track should be in the table, but it is not: + // configuration drift. + furthest = -1; + break; + } + furthest = std::max(furthest, reason); + } + if (furthest > 0) { + failureReason = furthest; + } + } + } + histos.fill(HIST("PairLossK0/Stage/hTriggerTracksFailureReason"), failureReason, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt); + } + + const int finalObjectState = (triggerFinal ? 2 : 0) + (v0Final ? 1 : 0); + const int trackLevelState = (triggerBestCollision ? 2 : 0) + (bothDaughtersBestCollision ? 1 : 0); + const int daughterTrackState = (positiveDaughterBestCollision ? 2 : 0) + (negativeDaughterBestCollision ? 1 : 0); + histos.fill(HIST("PairLossK0/State/hFinalObjectStatePhysics"), finalObjectState, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, magneticFieldSign); + if (truthK0.findable && closestDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/State/hFinalObjectStateClose"), finalObjectState, closestDeltaPhiStar.minAbs, closestDeltaEta, truthK0.pt, truthTrigger.pt, magneticFieldSign, closestChargeProduct); + histos.fill(HIST("PairLossK0/State/hTrackLevelStateClose"), trackLevelState, closestDeltaPhiStar.minAbs, closestDeltaEta, truthK0.pt, truthTrigger.pt, magneticFieldSign, closestChargeProduct); + histos.fill(HIST("PairLossK0/State/hDaughterTrackStateClose"), daughterTrackState, closestDeltaPhiStar.minAbs, closestDeltaEta, truthK0.pt, truthTrigger.pt, magneticFieldSign, closestChargeProduct); + + if (auto const* matchedTrigger = bestTrack(tracksBestCollision, truthTrigger.globalIndex)) { + histos.fill(HIST("PairLossK0/TrackQA/hTriggerTPCPairWeighted"), closestDeltaPhiStar.minAbs, matchedTrigger->tpcCrossedRows, matchedTrigger->tpcSharedClusters); + histos.fill(HIST("PairLossK0/TrackQA/hTriggerITSPairWeighted"), closestDeltaPhiStar.minAbs, matchedTrigger->itsClusters, matchedTrigger->hasLayer0); + } + if (auto const* matchedPositiveDaughter = bestTrack(tracksBestCollision, truthK0.positiveDaughter.globalIndex)) { + histos.fill(HIST("PairLossK0/TrackQA/hPositiveDaughterTPCPairWeighted"), closestDeltaPhiStar.minAbs, matchedPositiveDaughter->tpcCrossedRows, matchedPositiveDaughter->tpcSharedClusters); + histos.fill(HIST("PairLossK0/TrackQA/hPositiveDaughterITSPairWeighted"), closestDeltaPhiStar.minAbs, matchedPositiveDaughter->itsClusters, matchedPositiveDaughter->hasLayer0); + } + if (auto const* matchedNegativeDaughter = bestTrack(tracksBestCollision, truthK0.negativeDaughter.globalIndex)) { + histos.fill(HIST("PairLossK0/TrackQA/hNegativeDaughterTPCPairWeighted"), closestDeltaPhiStar.minAbs, matchedNegativeDaughter->tpcCrossedRows, matchedNegativeDaughter->tpcSharedClusters); + histos.fill(HIST("PairLossK0/TrackQA/hNegativeDaughterITSPairWeighted"), closestDeltaPhiStar.minAbs, matchedNegativeDaughter->itsClusters, matchedNegativeDaughter->hasLayer0); + } + if (auto const* matchedV0 = bestV0(v0sBestCollision, truthK0.globalIndex)) { + histos.fill(HIST("PairLossK0/V0QA/hCosPAVsMinDeltaPhiStar"), closestDeltaPhiStar.minAbs, matchedV0->cosPA); + histos.fill(HIST("PairLossK0/V0QA/hDcaDaughtersVsMinDeltaPhiStar"), closestDeltaPhiStar.minAbs, matchedV0->dcaDaughters); + histos.fill(HIST("PairLossK0/V0QA/hRadiusVsMinDeltaPhiStar"), closestDeltaPhiStar.minAbs, matchedV0->radius); + } + if (auto const* matchedAssocV0 = bestV0(v0sInTable, truthK0.globalIndex)) { + histos.fill(HIST("PairLossK0/V0QA/hMassNSigmaVsMinDeltaPhiStar"), closestDeltaPhiStar.minAbs, matchedAssocV0->massNSigma, truthK0.pt); + } + if (pairBeforeAutocorrelation && !finalPair && rejectedByAutocorrelation) { + histos.fill(HIST("PairLossK0/Geometry/hAutocorrelationRejected"), closestDeltaPhiStar.minAbs, closestDeltaEta, truthK0.pt, truthTrigger.pt, magneticFieldSign); + } + } + + for (auto const& [selectedTrigger, selectedV0] : finalPairCombinations) { + histos.fill(HIST("PairLossK0/Response/hTriggerPt"), truthTrigger.pt, selectedTrigger.pt); + histos.fill(HIST("PairLossK0/Response/hK0Pt"), truthK0.pt, selectedV0.pt); + histos.fill(HIST("PairLossK0/Response/hDeltaPhi"), truthDeltaPhi, computeDeltaPhi(selectedTrigger.phi, selectedV0.phi)); + float reconstructedDeltaEta = selectedTrigger.eta - selectedV0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + reconstructedDeltaEta = std::abs(reconstructedDeltaEta); + } + histos.fill(HIST("PairLossK0/Response/hDeltaEta"), truthDeltaEta, reconstructedDeltaEta); + if (truthK0.findable) { + if (positiveDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/Geometry/hFinalDaughters"), positiveDeltaPhiStar.signedAtMin, truthTrigger.eta - truthK0.positiveDaughter.eta, truthK0.pt, truthTrigger.pt, magneticFieldSign, truthTrigger.sign * truthK0.positiveDaughter.sign); + } + if (negativeDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/Geometry/hFinalDaughters"), negativeDeltaPhiStar.signedAtMin, truthTrigger.eta - truthK0.negativeDaughter.eta, truthK0.pt, truthTrigger.pt, magneticFieldSign, truthTrigger.sign * truthK0.negativeDaughter.sign); + } + if (closestDeltaPhiStar.valid) { + histos.fill(HIST("PairLossK0/Geometry/hFinalMinDeltaPhiStarVsDecayRadius"), closestDeltaPhiStar.minAbs, truthK0.decayRadius, truthK0.pt); + histos.fill(HIST("PairLossK0/Geometry/hFinalRadiusAtMinimum"), closestDeltaPhiStar.radiusAtMin, closestDeltaPhiStar.minAbs, truthK0.pt); + } + } + } + } + } + if (hasTruthPair) { + histos.fill(HIST("PairLossK0/Event/hCounter"), 5.0f); + } + return; + } + } + + void processClosureTest(aod::McCollision const& mcCollision, + soa::SmallGroups> const& recCollisions, + aod::McParticles const& mcParticles, + aod::V0MCCores const& v0MCCores, + V0DatasWithoutTrackX const& v0Candidates, + TracksCompleteMC const& tracks, + aod::TriggerTracks const& triggerTracks, + aod::AssocV0s const& associatedV0s) + { + + // Reproduce the first processPairLossK0MC stages without changing the + // selections or output of the pre-existing closure-test analysis below. All + // histograms remain anchored to the same truth h-K0 pair; the folders differ + // only in which reconstruction requirement is imposed: + // AnyTrack the truth trigger must have at least one reconstructed track + // pointing back to it through its MC label, in any + // reconstructed collision associated with this MC collision + // AnyTrackK0 the truth K0 must have at least one reconstructed V0 + // candidate pointing back to it, in any associated collision + // AnyTrackBoth both requirements at the same time + // Final the strictest stage: both the trigger and the K0 must have a + // *fully selected* reconstructed counterpart -- the very + // TriggerTracks and AssocV0s entries, passing the very final + // selections, that the reconstructed correlation is built from -- + // and both in one and the same reconstructed collision + auto fillPairLossK0TruthAndAnyTrack = [&]() { + if (!pairLossK0Configurations.doClosureTestStages) { + return; + } + if (recCollisions.size() == 0) { + return; + } + + int64_t pairLossBestCollisionId = -1; + int pairLossLargestNContributors = -1; + for (auto const& collision : recCollisions) { + if (collision.numContrib() > pairLossLargestNContributors) { + pairLossLargestNContributors = collision.numContrib(); + pairLossBestCollisionId = collision.globalIndex(); + } + } + if (pairLossBestCollisionId < 0) { + return; + } + + std::unordered_set pairLossAnyTrackMcParticleIds; + std::unordered_set pairLossAnyV0McParticleIds; + for (auto const& collision : recCollisions) { + const auto trackSlice = tracks.sliceBy(pairLossTracksPerCollision, collision.globalIndex()); + for (auto const& track : trackSlice) { + if (track.has_mcParticle()) { + pairLossAnyTrackMcParticleIds.insert(track.mcParticleId()); + } + } + // Same V0-side bookkeeping as tracksAnyCollision/v0sAnyCollision in + // processPairLossK0MC: any V0 candidate in any associated collision whose + // MC core is a true K0 short, with no candidate-quality selection at all. + const auto v0Slice = v0Candidates.sliceBy(pairLossV0sPerCollision, collision.globalIndex()); + for (auto const& v0 : v0Slice) { + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() < 0 || v0MC.pdgCode() != PDG_t::kK0Short) { + continue; + } + pairLossAnyV0McParticleIds.insert(v0MC.particleIdMC()); + } + } + + // Final-stage bookkeeping. A truth object counts here only if it has at least + // one fully selected reconstructed counterpart, where "fully selected" means + // the exact same conditions the reconstructed correlation imposes in + // fillCorrelationsV0(): the object is in the TriggerTracks / AssocV0s table + // to begin with, and it then passes isValidTrigger() resp. the whole V0 + // selection chain including the dE/dx compatibility bit and the peak mass + // window. The selections are read from the filter tables instead of being + // recomputed from raw tracks so that this stage cannot drift away from the + // reconstructed analysis it exists to be compared against. + // + // The stage is restricted to the best reconstructed collision and keeps the + // reconstructed kinematics of every matched object, so that the pair test below + // can also impose the reconstructed-coordinate angular range and the + // reconstructed autocorrelation rejection. Both are conditions the reconstructed + // same-event correlation imposes as well, so this Final stage is by construction + // the same object as PairLossK0/Comparison/Final: the truth pairs that really do + // end up in the reconstructed correlation. + PairLossTrackMap pairLossFinalTriggers; + PairLossV0Map pairLossFinalV0s; + for (auto const& collision : recCollisions) { + if (collision.globalIndex() != pairLossBestCollisionId) { + continue; + } + + const auto finalTriggerSlice = triggerTracks.sliceBy(collisionSliceTracks, collision.globalIndex()); + for (auto const& triggerEntry : finalTriggerSlice) { + auto track = triggerEntry.track_as(); + if (!track.has_mcParticle()) { + continue; + } + if (!isValidTrigger(track, triggerEntry.isLeading())) { + continue; + } + if (trackSelection.checkForITSTPCMissmatchMC && bitcheck(triggerEntry.mcMask(), 13)) { + continue; + } + if (masterConfigurations.doTriggPhysicalPrimary && !triggerEntry.mcPhysicalPrimary()) { + continue; + } + pairLossFinalTriggers[track.mcParticleId()].push_back(makePairLossTrackInfo(track)); + } + + const auto finalV0Slice = associatedV0s.sliceBy(collisionSliceV0s, collision.globalIndex()); + for (auto const& assocEntry : finalV0Slice) { + auto v0 = assocEntry.v0Core_as(); + const auto v0MC = v0MCCores.iteratorAt(v0.globalIndex()); + if (v0MC.particleIdMC() < 0 || v0MC.pdgCode() != PDG_t::kK0Short || !assocEntry.mcTrue(IndexK0)) { + continue; + } + auto positiveTrack = v0.posTrack_as(); + auto negativeTrack = v0.negTrack_as(); + bool passesFinalSelection = true; + if (masterConfigurations.doPPAnalysis) { + passesFinalSelection = v0.v0radius() >= v0Selection.v0RadiusMin && v0.v0radius() <= v0Selection.v0RadiusMax && + std::abs(v0.dcapostopv()) >= v0Selection.dcapostopv && std::abs(v0.dcanegtopv()) >= v0Selection.dcanegtopv && + v0.v0cosPA() >= v0Selection.v0cospa && v0.dcaV0daughters() <= v0Selection.dcaV0dau; + } else { + const float dcaCut = v0Selection.dcaDaugToPVForK0s == 0.0f ? v0Selection.dcaMesonToPV : v0Selection.dcaDaugToPVForK0s; + const bool passesLifetime = v0.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * + o2::constants::physics::MassK0Short < + v0Selection.lifetimecutK0S; + const bool passesDaughterDcaAndArmenteros = std::abs(v0.dcapostopv()) > dcaCut && std::abs(v0.dcanegtopv()) > dcaCut && + v0.qtarm() * v0Selection.armPodCut > std::abs(v0.alpha()); + passesFinalSelection = v0SelectedPbPb(v0) && passesLifetime && passesDaughterDcaAndArmenteros; + } + passesFinalSelection = passesFinalSelection && positiveTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated && + negativeTrack.tpcNClsCrossedRows() >= trackSelection.minTPCNCrossedRowsAssociated; + if (trackSelection.checksRequireTPCChi2) { + passesFinalSelection = passesFinalSelection && positiveTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated && + negativeTrack.tpcChi2NCl() <= trackSelection.maxTPCChi2PerClusterAssociated; + } + if (trackSelection.requireClusterInITS) { + passesFinalSelection = passesFinalSelection && positiveTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks && + negativeTrack.itsNCls() >= trackSelection.minITSClustersForDaughterTracks; + } + passesFinalSelection = passesFinalSelection && assocEntry.compatible(IndexK0, trackSelection.dEdxCompatibility) && + (!masterConfigurations.doAssocPhysicalPrimary || assocEntry.mcPhysicalPrimary()) && + assocEntry.invMassNSigma(IndexK0) > -massWindowConfigurations.maxPeakNSigma && + assocEntry.invMassNSigma(IndexK0) < massWindowConfigurations.maxPeakNSigma && + v0.pt() >= axisRanges[2][0] && v0.pt() <= axisRanges[2][1]; + if (!passesFinalSelection) { + continue; + } + pairLossFinalV0s[v0MC.particleIdMC()].push_back(PairLossV0Info{ + .globalIndex = v0.globalIndex(), + .positiveTrackId = positiveTrack.globalIndex(), + .negativeTrackId = negativeTrack.globalIndex(), + .pt = v0.pt(), + .eta = v0.eta(), + .phi = v0.phi(), + .radius = v0.v0radius(), + .cosPA = v0.v0cosPA(), + .dcaDaughters = v0.dcaV0daughters(), + .massNSigma = assocEntry.invMassNSigma(IndexK0)}); + } + } + + // Object-level membership, used only for the single-particle spectra: the object + // has a fully selected reconstructed counterpart in the best collision. The pair + // histogram uses pairLossHasFinalPair() instead, which is stricter: it also + // requires the reconstructed pair itself to fall in the reconstructed angular + // range and to survive the reconstructed autocorrelation rejection. + auto pairLossHasFinalTrigger = [&](int64_t mcId) { + return pairLossFinalTriggers.contains(mcId); + }; + auto pairLossHasFinalK0 = [&](int64_t mcId) { + return pairLossFinalV0s.contains(mcId); + }; + auto pairLossHasFinalPair = [&](int64_t triggerMcId, int64_t k0McId) { + const auto triggerMatches = pairLossFinalTriggers.find(triggerMcId); + if (triggerMatches == pairLossFinalTriggers.end()) { + return false; + } + const auto v0Matches = pairLossFinalV0s.find(k0McId); + if (v0Matches == pairLossFinalV0s.end()) { + return false; + } + for (auto const& reconstructedTrigger : triggerMatches->second) { + for (auto const& reconstructedV0 : v0Matches->second) { + float reconstructedDeltaEta = reconstructedTrigger.eta - reconstructedV0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + reconstructedDeltaEta = std::abs(reconstructedDeltaEta); + } + const float reconstructedDeltaPhi = computeDeltaPhi(reconstructedTrigger.phi, reconstructedV0.phi); + if (reconstructedDeltaPhi < axisRanges[0][0] || reconstructedDeltaPhi > axisRanges[0][1] || + reconstructedDeltaEta < axisRanges[1][0] || reconstructedDeltaEta > axisRanges[1][1]) { + continue; + } + if (doAutocorrelationRejection && (reconstructedTrigger.globalIndex == reconstructedV0.positiveTrackId || reconstructedTrigger.globalIndex == reconstructedV0.negativeTrackId)) { + continue; + } + return true; + } + } + return false; + }; + + for (auto const& collision : recCollisions) { + if (collision.globalIndex() != pairLossBestCollisionId) { + continue; + } + + const bool collisionSelected = !pairLossK0Configurations.applyRecoEventSelection || + (masterConfigurations.doPPAnalysis ? isCollisionSelected(collision) : isCollisionSelectedPbPb(collision, false)); + if (!collisionSelected) { + return; + } + + const float pairLossBestCollisionVtxZ = collision.posZ(); + const float pairLossBestCollisionMultiplicity = masterConfigurations.doPPAnalysis ? collision.centFT0M() : collision.centFT0C(); + std::array eventQualityClasses{}; + if (pairLossK0Configurations.doEventQualityStudy) { + // Category edges correspond to the labels declared above. + auto classFromEdges = [](auto value, auto const& edges) { + return static_cast(std::upper_bound(edges.begin(), edges.end(), value) - edges.begin()); + }; + constexpr double PurePVThreshold = 0.95; + constexpr double DominantCollisionFraction = 0.9; + constexpr double MajorityCollisionFraction = 0.5; + constexpr float GenEtaMax = 0.8f; + constexpr float ShapePtMin = 0.15f; + constexpr int MinShapeTracks = 3; + const auto nContributors = collision.numContrib(); + eventQualityClasses[0] = classFromEdges(nContributors, std::array{1u, 3u, 6u, 11u, 21u}); + + // Maximum component pull, not a 3D chi-square: covariance correlations + // are deliberately not used. Invalid covariance gets its own category. + const std::array residuals = {collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ()}; + const std::array variances = {collision.covXX(), collision.covYY(), collision.covZZ()}; + double maxPull = 0.; + bool validPull = true; + for (size_t component = 0; component < variances.size(); ++component) { + if (!std::isfinite(variances[component]) || variances[component] <= 0. || !std::isfinite(residuals[component])) { + validPull = false; + break; + } + maxPull = std::max(maxPull, std::abs(residuals[component]) / std::sqrt(variances[component])); + } + eventQualityClasses[1] = !validPull || !std::isfinite(maxPull) ? 0 : 1 + classFromEdges(maxPull, std::array{1., 3., 5.}); + + std::unordered_set currentMcIds; + int nAnyMatched = 0; + int nGenCharged = 0; + int nShapeTracks = 0; + double sumPt = 0., q2x = 0., q2y = 0.; + for (auto const& particle : mcParticles) { + currentMcIds.insert(particle.globalIndex()); + nAnyMatched += pairLossAnyTrackMcParticleIds.contains(particle.globalIndex()); + if (!particle.isPhysicalPrimary() || !std::isfinite(particle.eta()) || std::abs(particle.eta()) >= GenEtaMax) { + continue; + } + auto const* pdgParticle = pdgDB->GetParticle(particle.pdgCode()); + if (pdgParticle == nullptr || pdgParticle->Charge() == 0.) { + continue; + } + ++nGenCharged; + // Linearized transverse sphericity, using truth primary charged + // particles with |eta|<0.8 and pT>=0.15 GeV/c (not spherocity). + if (std::isfinite(particle.pt()) && particle.pt() >= ShapePtMin && std::isfinite(particle.phi())) { + ++nShapeTracks; + sumPt += particle.pt(); + q2x += particle.pt() * std::cos(2. * particle.phi()); + q2y += particle.pt() * std::sin(2. * particle.phi()); + } + } + int nPVTracks = 0, nLabeledPVTracks = 0, nSameMcPVTracks = 0; + std::unordered_set bestMatchedMcIds; + const auto bestTracks = tracks.sliceBy(pairLossTracksPerCollision, collision.globalIndex()); + for (auto const& track : bestTracks) { + const bool sameMc = track.has_mcParticle() && currentMcIds.contains(track.mcParticleId()); + if (sameMc) { + bestMatchedMcIds.insert(track.mcParticleId()); + } + if (track.isPVContributor()) { + ++nPVTracks; + nLabeledPVTracks += track.has_mcParticle(); + nSameMcPVTracks += sameMc; + } + } + // Missing labels take precedence; purity otherwise uses all PV tracks. + const double purity = nPVTracks > 0 ? static_cast(nSameMcPVTracks) / nPVTracks : 0.; + eventQualityClasses[2] = nPVTracks == 0 ? 0 : nLabeledPVTracks < nPVTracks ? 1 + : purity < PurePVThreshold ? 2 + : nSameMcPVTracks < nPVTracks ? 3 + : 4; + const double bestFraction = nAnyMatched > 0 ? static_cast(bestMatchedMcIds.size()) / nAnyMatched : 0.; + eventQualityClasses[3] = nAnyMatched == 0 ? 0 : recCollisions.size() == 1 ? 1 + : bestFraction >= DominantCollisionFraction ? 2 + : bestFraction >= MajorityCollisionFraction ? 3 + : 4; + const double timeResolution = collision.collisionTimeRes(); // ns + eventQualityClasses[4] = !std::isfinite(timeResolution) || timeResolution <= 0. ? 0 : 1 + classFromEdges(timeResolution, std::array{25., 100., 500.}); + const auto occupancy = collision.trackOccupancyInTimeRange(); + eventQualityClasses[5] = occupancy < 0 ? 0 : 1 + classFromEdges(occupancy, std::array{1, 100, 500, 1000}); + eventQualityClasses[6] = classFromEdges(nGenCharged, std::array{1, 5, 10, 20, 40}); + if (nShapeTracks >= MinShapeTracks && sumPt > 0.) { + const double sphericity = std::clamp(1. - std::hypot(q2x, q2y) / sumPt, 0., 1.); + eventQualityClasses[7] = 1 + classFromEdges(sphericity, std::array{0.3, 0.7}); + } + } + auto fillEventQuality = [&](int entryKind, float deltaPhi, float deltaEta, float assocPt, float triggerPt, bool hasAny, bool hasFinal) { + if (!pairLossK0Configurations.doEventQualityStudy) { + return; + } + static_for<0, PairLossNEventQualityGroups - 1>([&](auto i) { + constexpr int Index = i.value; + histos.fill(HIST("ClosureTest/PairLossK0/EventQuality/") + HIST(PairLossEventQualityNames[Index]) + HIST("/Truth"), eventQualityClasses[Index], entryKind, deltaPhi, deltaEta, assocPt, triggerPt); + if (hasAny) { + histos.fill(HIST("ClosureTest/PairLossK0/EventQuality/") + HIST(PairLossEventQualityNames[Index]) + HIST("/AnyTrackBoth"), eventQualityClasses[Index], entryKind, deltaPhi, deltaEta, assocPt, triggerPt); + } + if (hasFinal) { + histos.fill(HIST("ClosureTest/PairLossK0/EventQuality/") + HIST(PairLossEventQualityNames[Index]) + HIST("/Final"), eventQualityClasses[Index], entryKind, deltaPhi, deltaEta, assocPt, triggerPt); + } + }); + }; + // Unused coordinates lie inside regular bins. Select entry kind before + // projecting; reset pair-coordinate ranges for event/trigger counts. + const float countDeltaPhi = (axisRanges[0][0] + axisRanges[0][1]) * 0.5f; + const float countDeltaEta = (axisRanges[1][0] + axisRanges[1][1]) * 0.5f; + fillEventQuality(0, countDeltaPhi, countDeltaEta, axisRanges[2][0], axisRanges[3][0], true, true); + std::vector pairLossTruthTriggers; + std::vector pairLossTruthK0s; + + for (auto const& mcParticle : mcParticles) { + if (isPairLossTriggerPdg(mcParticle.pdgCode()) && std::abs(mcParticle.eta()) <= etaSel && + mcParticle.pt() >= axisRanges[3][0] && mcParticle.pt() <= axisRanges[3][1] && + (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary())) { + auto const* pdgParticle = pdgDB->GetParticle(mcParticle.pdgCode()); + const double charge = pdgParticle != nullptr ? pdgParticle->Charge() : 0.0; + const int sign = charge > 0.0 ? 1 : (charge < 0.0 ? -1 : 0); + if ((triggerParticleCharge <= 0 || sign >= 0) && (triggerParticleCharge >= 0 || sign <= 0) && sign != 0) { + pairLossTruthTriggers.push_back(PairLossTruthTrackInfo{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .sign = sign}); + } + } + + if (mcParticle.pdgCode() != PDG_t::kK0Short || std::abs(mcParticle.eta()) > etaSel || + mcParticle.pt() < axisRanges[2][0] || mcParticle.pt() > axisRanges[2][1] || + (masterConfigurations.doAssocPhysicalPrimary && !mcParticle.isPhysicalPrimary())) { + continue; + } + + PairLossTruthK0Info truthK0{ + .globalIndex = mcParticle.globalIndex(), + .pt = mcParticle.pt(), + .eta = mcParticle.eta(), + .phi = mcParticle.phi(), + .decayRadius = -1.0f, + .findable = false, + .positiveDaughter = {}, + .negativeDaughter = {}}; + for (auto const& daughter : mcParticle.daughters_as()) { + PairLossTruthTrackInfo daughterInfo{ + .globalIndex = daughter.globalIndex(), + .pt = daughter.pt(), + .eta = daughter.eta(), + .phi = daughter.phi(), + .sign = daughter.pdgCode() > 0 ? 1 : -1}; + if (daughter.pdgCode() == PDG_t::kPiPlus) { + truthK0.positiveDaughter = daughterInfo; + } else if (daughter.pdgCode() == -PDG_t::kPiPlus) { + truthK0.negativeDaughter = daughterInfo; + } + } + pairLossTruthK0s.push_back(truthK0); + } + + if (useTheLeadingParticleAsTrigger && pairLossTruthTriggers.size() > 1) { + const auto leadingTrigger = std::max_element(pairLossTruthTriggers.begin(), pairLossTruthTriggers.end(), [](auto const& lhs, auto const& rhs) { return lhs.pt < rhs.pt; }); + const auto leadingTriggerCopy = *leadingTrigger; + pairLossTruthTriggers.clear(); + pairLossTruthTriggers.push_back(leadingTriggerCopy); + } + + // One entry per object per folder, at the level that folder prescribes: + // the trigger is at truth level in Truth/ and AnyTrackK0/, at any level in + // AnyTrack/ and AnyTrackBoth/, at fully-selected level in Final/; the K0 is + // at truth level in Truth/ and AnyTrack/, at any level in AnyTrackK0/ and + // AnyTrackBoth/, at fully-selected level in Final/. + for (auto const& truthTrigger : pairLossTruthTriggers) { + if (pairLossK0Configurations.doEventQualityStudy) { + fillEventQuality(1, countDeltaPhi, countDeltaEta, axisRanges[2][0], truthTrigger.pt, pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex), pairLossHasFinalTrigger(truthTrigger.globalIndex)); + } + histos.fill(HIST("ClosureTest/PairLossK0/Truth/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackK0/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); + if (pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex)) { + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrack/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackBoth/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); + } + if (pairLossHasFinalTrigger(truthTrigger.globalIndex)) { + histos.fill(HIST("ClosureTest/PairLossK0/Final/hTrigger"), truthTrigger.pt, truthTrigger.eta, truthTrigger.phi); + } + } + for (auto const& truthK0 : pairLossTruthK0s) { + histos.fill(HIST("ClosureTest/PairLossK0/Truth/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrack/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); + if (pairLossAnyV0McParticleIds.contains(truthK0.globalIndex)) { + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackK0/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackBoth/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); + } + if (pairLossHasFinalK0(truthK0.globalIndex)) { + histos.fill(HIST("ClosureTest/PairLossK0/Final/hK0Short"), truthK0.pt, truthK0.eta, truthK0.phi); + } + } + + for (auto const& truthTrigger : pairLossTruthTriggers) { + const bool triggerHasAnyTrack = pairLossAnyTrackMcParticleIds.contains(truthTrigger.globalIndex); + for (auto const& truthK0 : pairLossTruthK0s) { + if (truthTrigger.globalIndex == truthK0.positiveDaughter.globalIndex || truthTrigger.globalIndex == truthK0.negativeDaughter.globalIndex) { + continue; + } + const float truthDeltaPhi = computeDeltaPhi(truthTrigger.phi, truthK0.phi); + float truthDeltaEta = truthTrigger.eta - truthK0.eta; + if (masterConfigurations.doMirroringInDelataEta) { + truthDeltaEta = std::abs(truthDeltaEta); + } + if (truthDeltaPhi < axisRanges[0][0] || truthDeltaPhi > axisRanges[0][1] || + truthDeltaEta < axisRanges[1][0] || truthDeltaEta > axisRanges[1][1]) { + continue; + } + + const bool k0HasAnyV0 = pairLossAnyV0McParticleIds.contains(truthK0.globalIndex); + + histos.fill(HIST("ClosureTest/PairLossK0/Truth/sameEvent/K0Short"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity); + if (triggerHasAnyTrack) { + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrack/sameEvent/K0Short"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity); + } + if (k0HasAnyV0) { + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackK0/sameEvent/K0Short"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity); + } + if (triggerHasAnyTrack && k0HasAnyV0) { + histos.fill(HIST("ClosureTest/PairLossK0/AnyTrackBoth/sameEvent/K0Short"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity); + } + const bool hasFinalPair = pairLossHasFinalPair(truthTrigger.globalIndex, truthK0.globalIndex); + fillEventQuality(2, truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, triggerHasAnyTrack && k0HasAnyV0, hasFinalPair); + if (hasFinalPair) { + histos.fill(HIST("ClosureTest/PairLossK0/Final/sameEvent/K0Short"), truthDeltaPhi, truthDeltaEta, truthK0.pt, truthTrigger.pt, pairLossBestCollisionVtxZ, pairLossBestCollisionMultiplicity); + } + } + } + return; + } + }; + fillPairLossK0TruthAndAnyTrack(); + + std::vector triggerIndices; + std::vector> associatedIndices; + std::vector assocHadronIndices; + std::vector piIndices; + std::vector k0ShortIndices; + std::vector lambdaIndices; + std::vector antiLambdaIndices; + std::vector xiMinusIndices; + std::vector xiPlusIndices; + std::vector omegaMinusIndices; + std::vector omegaPlusIndices; + + // A truth trigger belongs to this set when at least one reconstructed track + // in any reconstructed collision associated with the current MC collision + // points back to it through its MC label. No best-collision, track-quality, + // or final-trigger selection is imposed here: this is the "any track" stage. + std::unordered_set mcParticleIdsWithRecoTrackMatch; + if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch) { + for (auto const& recCollision : recCollisions) { + const auto trackSlice = tracks.sliceBy(pairLossTracksPerCollision, recCollision.globalIndex()); + for (auto const& track : trackSlice) { + if (track.has_mcParticle()) { + mcParticleIdsWithRecoTrackMatch.insert(track.mcParticleId()); + } + } + } + } + + for (auto const& mcParticle : mcParticles) { + double geta = mcParticle.eta(); + if (std::abs(geta) > etaSel) { + continue; + } + double gpt = mcParticle.pt(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hClosureQAPtTrigger"), gpt, 0.0f); // step 1: no event selection whatsoever + } + } + + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { + histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 0.0f); // step 1: no event selection whatsoever + } + } + } + + histos.fill(HIST("hClosureTestEventCounter"), 0.5f); + + float bestCollisionCentpercentile = -1; + float bestCollisionVtxZ = 0.0f; + // Pure MC defines an inclusive generated-event reference, independent of + // reconstructed collisions and triggerPresenceMap. Particle/pair selections + // below still define the observable. PairLossK0 diagnostics above retain + // their own event selection. + if (masterConfigurations.doClosureTestPureMC) { + bestCollisionCentpercentile = 0.05f; + bestCollisionVtxZ = mcCollision.posZ(); + } else { + bool bestCollisionSel8 = false; + bool bestCollisionINELgtZERO = false; + bool bestCollisionINELgtN = false; + bool bestCollisionNoSameBunchPileup = false; + bool bestCollisionGoodTriggerTVX = false; + bool bestCollisionGoodZvtxFT0vsPV = false; + bool isCollisionSelect = false; + int biggestNContribs = -1; + uint32_t bestCollisionTriggerPresenceMap = 0; + + for (auto const& recCollision : recCollisions) { + if (biggestNContribs < recCollision.numContrib()) { + biggestNContribs = recCollision.numContrib(); + bestCollisionCentpercentile = masterConfigurations.doPPAnalysis ? recCollision.centFT0M() : recCollision.centFT0C(); + // Both branches fill the generated-level THns with this vertex position, so it + // is taken from the best collision regardless of which event selection is used. + bestCollisionVtxZ = recCollision.posZ(); + if (masterConfigurations.applyNewMCSelection) { + isCollisionSelect = ((masterConfigurations.doPPAnalysis && isCollisionSelected(recCollision)) || (!masterConfigurations.doPPAnalysis && isCollisionSelectedPbPb(recCollision, false))); + } else { + bestCollisionSel8 = recCollision.sel8(); + bestCollisionINELgtZERO = recCollision.isInelGt0(); + bestCollisionINELgtN = isInelGtNReco(recCollision, masterConfigurations.inelGtNThreshold); + bestCollisionNoSameBunchPileup = recCollision.selection_bit(o2::aod::evsel::kNoSameBunchPileup); + bestCollisionGoodTriggerTVX = recCollision.selection_bit(aod::evsel::kIsTriggerTVX); + bestCollisionGoodZvtxFT0vsPV = recCollision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV); + } + if (!triggerPresenceMap.empty()) { + bestCollisionTriggerPresenceMap = triggerPresenceMap[recCollision.globalIndex()]; + } + } + } + // ________________________________________________ + // skip if desired trigger not found + if (!triggerPresenceMap.empty() && !TESTBIT(bestCollisionTriggerPresenceMap, triggerBinToSelect)) { + return; + } + + if (masterConfigurations.applyNewMCSelection) { + if (!isCollisionSelect) { + return; + } + } else { + if (masterConfigurations.doGenEventSelection) { + if (!bestCollisionSel8) { + return; + } + if (std::abs(bestCollisionVtxZ) > masterConfigurations.zVertexCut) { + return; + } + if (!bestCollisionINELgtZERO) { + return; + } + if (masterConfigurations.selectINELgtN && !bestCollisionINELgtN) { + return; + } + if (masterConfigurations.rejectSameBunchPileup && !bestCollisionNoSameBunchPileup) { + return; + } + if (masterConfigurations.requireGoodTriggerTVX && !bestCollisionGoodTriggerTVX) { + return; + } + if (masterConfigurations.requireGoodZvtxFT0vsPV && !bestCollisionGoodZvtxFT0vsPV) { + return; + } + if (bestCollisionCentpercentile > axisRanges[5][1] || bestCollisionCentpercentile < axisRanges[5][0]) { + return; + } + } + } + } + histos.fill(HIST("hClosureTestEventCounter"), 1.5f); + BinningTypePP colBinning{{axesConfigurations.axisVtxZ, axesConfigurations.axisMult}}; + int binnumb = colBinning.getBin({bestCollisionVtxZ, bestCollisionCentpercentile}); + for (auto const& mcParticle : mcParticles) { + double geta = mcParticle.eta(); + if (std::abs(geta) > etaSel) { + continue; + } + double gpt = mcParticle.pt(); + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + histos.fill(HIST("hClosureQAPtTrigger"), gpt, 1.0f); // step 2: after event selection + } + } + + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { + histos.fill(HIST("hClosureQAPtAssociatedK0"), gpt, 1.0f); // step 2: after event selection + } + } + } + + int iteratorNum = -1; + if (masterConfigurations.mixingEventForClosureTest) { + genLevelCollisions[binnumb].emplace_back(); + } + for (auto const& mcParticle : mcParticles) { + iteratorNum = iteratorNum + 1; + double geta = mcParticle.eta(); + double gpt = mcParticle.pt(); + double gphi = mcParticle.phi(); + if (std::abs(geta) > etaSel) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + triggerIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hTrigger"), gpt, geta, bestCollisionCentpercentile, gphi); + if (masterConfigurations.mixingEventForClosureTest) { + ValidTriggerInGen TriggerInGen; + TriggerInGen.pt = gpt; + TriggerInGen.eta = geta; + TriggerInGen.phi = gphi; + if (gpt <= axisRanges[3][1] && gpt >= axisRanges[3][0]) + genLevelCollisions[binnumb].back().emplace_back(TriggerInGen); + } + if (masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(mcParticle.globalIndex())) { + histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/hTrigger"), gpt, geta, bestCollisionCentpercentile); + } + } + if (masterConfigurations.doCorrelationHadron) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + assocHadronIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hHadron"), gpt, geta, gphi); + } + } + } + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { + piIndices.emplace_back(iteratorNum); + histos.fill(HIST("ClosureTest/hPion"), gpt, geta, gphi); } if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { k0ShortIndices.emplace_back(iteratorNum); @@ -3062,6 +7102,9 @@ struct HStrangeCorrelation { double getatrigger = triggerParticle.eta(); double gphitrigger = triggerParticle.phi(); double pttrigger = triggerParticle.pt(); + const bool triggerHasRecoTrackMatch = masterConfigurations.doClosureTestTriggerWithRecoTrackMatch && mcParticleIdsWithRecoTrackMatch.contains(triggerParticle.globalIndex()); + auto const* triggerPdg = pdgDB->GetParticle(triggerParticle.pdgCode()); + const double triggerCharge = triggerPdg ? triggerPdg->Charge() : 0.; auto const& mother = triggerParticle.mothers_first_as(); auto globalIndex = mother.globalIndex(); static_for<0, 8>([&](auto i) { // associated loop @@ -3076,26 +7119,82 @@ struct HStrangeCorrelation { double deltaeta = getatrigger - getaassoc; // skip if basic ranges not met - if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) { continue; - if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) + } + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) { continue; - if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) + } + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) { continue; - if (TESTBIT(doCorrelation, i)) - histos.fill(HIST("ClosureTest/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionFT0Mpercentile); + } + if (TESTBIT(doCorrelation, i)) { + histos.fill(HIST("ClosureTest/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + if (triggerHasRecoTrackMatch) { + histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + } + } + if (i < 3 && TESTBIT(doCorrelation, i) && masterConfigurations.doCorrelationsHadronV0daughter) { + auto const assocParticleDaughters = assocParticle.daughters_as(); + for (const auto& assocParticleDaughter : assocParticleDaughters) { + auto const* daughterPdg = pdgDB->GetParticle(assocParticleDaughter.pdgCode()); + const double daughterCharge = daughterPdg ? daughterPdg->Charge() : 0.; + // Neutral daughters, including pi0, do not correspond to reconstructed V0 daughter tracks. + if (triggerCharge == 0. || daughterCharge == 0.) { + continue; + } + if (triggerCharge * daughterCharge > 0.) { + histos.fill(HIST("ClosureTest/sameEvent/") + HIST(Particlenames[Index]) + HIST("_SameSignDaughter"), computeDeltaPhi(gphitrigger, assocParticleDaughter.phi()), getatrigger - assocParticleDaughter.eta(), assocParticleDaughter.pt(), pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + if (triggerHasRecoTrackMatch) { + histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/") + HIST(Particlenames[Index]) + HIST("_SameSignDaughter"), computeDeltaPhi(gphitrigger, assocParticleDaughter.phi()), getatrigger - assocParticleDaughter.eta(), assocParticleDaughter.pt(), pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + } + } else { + histos.fill(HIST("ClosureTest/sameEvent/") + HIST(Particlenames[Index]) + HIST("_OppSignDaughter"), computeDeltaPhi(gphitrigger, assocParticleDaughter.phi()), getatrigger - assocParticleDaughter.eta(), assocParticleDaughter.pt(), pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + if (triggerHasRecoTrackMatch) { + histos.fill(HIST("ClosureTest/TriggerWithRecoTrackMatch/sameEvent/") + HIST(Particlenames[Index]) + HIST("_OppSignDaughter"), computeDeltaPhi(gphitrigger, assocParticleDaughter.phi()), getatrigger - assocParticleDaughter.eta(), assocParticleDaughter.pt(), pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + } + } + } + } } } }); } + if (!masterConfigurations.mixingEventForClosureTest) { + return; + } + for (std::size_t iCollision = 0; iCollision < genLevelCollisions[binnumb].size(); iCollision++) { + for (std::size_t iTrigger = 0; iTrigger < genLevelCollisions[binnumb][iCollision].size(); iTrigger++) { + auto triggerParticle = genLevelCollisions[binnumb][iCollision][iTrigger]; + double getatrigger = triggerParticle.eta; + double gphitrigger = triggerParticle.phi; + double pttrigger = triggerParticle.pt; + static_for<0, 8>([&](auto i) { // associated loop + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaeta = getatrigger - getaassoc; + if (TESTBIT(doCorrelation, i)) { + histos.fill(HIST("ClosureTest/mixedEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, bestCollisionVtxZ, bestCollisionCentpercentile); + } + } + }); + } + } + if (genLevelCollisions[binnumb].size() >= static_cast(masterConfigurations.mixingParameter) + 1) { + genLevelCollisions[binnumb].erase(genLevelCollisions[binnumb].begin()); + } } - void processFeedDown(soa::Join::iterator const& collision, aod::AssocV0s const& associatedV0s, aod::McParticles const&, V0DatasWithoutTrackXMC const&, TracksComplete const&, aod::BCsWithTimestamps const&) + void processFeedDown(soa::Join::iterator const& collision, aod::AssocV0s const& associatedV0s, aod::McParticles const&, V0DatasWithoutTrackXMC const&, TracksComplete const&, aod::BCsWithTimestamps const&) { // ________________________________________________ // Perform basic event selection - if (!isCollisionSelected(collision)) { + if (!(masterConfigurations.doPPAnalysis ? isCollisionSelected(collision) : isCollisionSelectedPbPb(collision, false))) { return; } @@ -3105,14 +7204,18 @@ struct HStrangeCorrelation { //---] track quality check [--- auto postrack = v0Data.posTrack_as(); auto negtrack = v0Data.negTrack_as(); - if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) + if (postrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated || negtrack.tpcNClsCrossedRows() < trackSelection.minTPCNCrossedRowsAssociated) { continue; - + } + if (trackSelection.requireClusterInITS && (postrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks || negtrack.itsNCls() < trackSelection.minITSClustersForDaughterTracks)) { + continue; + } //---] syst cuts [--- if (v0Data.v0radius() < v0Selection.v0RadiusMin || v0Data.v0radius() > v0Selection.v0RadiusMax || std::abs(v0Data.dcapostopv()) < v0Selection.dcapostopv || std::abs(v0Data.dcanegtopv()) < v0Selection.dcanegtopv || - v0Data.v0cosPA() < v0Selection.v0cospa || v0Data.dcaV0daughters() > v0Selection.dcaV0dau) + v0Data.v0cosPA() < v0Selection.v0cospa || v0Data.dcaV0daughters() > v0Selection.dcaV0dau) { continue; + } if (v0Data.has_mcParticle()) { auto v0mcParticle = v0Data.mcParticle_as(); @@ -3184,7 +7287,7 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } @@ -3192,12 +7295,14 @@ struct HStrangeCorrelation { if (((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision))) || (!masterConfigurations.doPPAnalysis && !isCollisionSelectedPbPb(collision, false))) { return; } - if (cent > axisRanges[5][1] || cent < axisRanges[5][0]) + if (cent > axisRanges[5][1] || cent < axisRanges[5][0]) { return; + } // ________________________________________________ - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsV0(triggerTracks, associatedV0s, true, true, collision.posX(), collision.posY(), collision.posZ(), cent, bField); + } } void processMixedEventHCascadesInBuffer(soa::Join::iterator const& collision, aod::AssocV0s const&, aod::AssocCascades const& associatedCascades, aod::TriggerTracks const& triggerTracks, @@ -3212,19 +7317,209 @@ struct HStrangeCorrelation { } // ________________________________________________ // skip if desired trigger not found - if (triggerPresenceMap.size() > 0 && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { + if (!triggerPresenceMap.empty() && !TESTBIT(triggerPresenceMap[collision.globalIndex()], triggerBinToSelect)) { return; } // Perform basic event selection on both collisions if ((masterConfigurations.doPPAnalysis && !isCollisionSelected(collision)) || (!masterConfigurations.doPPAnalysis && (!isCollisionSelectedPbPb(collision, false)))) { return; } - if (cent > axisRanges[5][1] || cent < axisRanges[5][0]) + if (cent > axisRanges[5][1] || cent < axisRanges[5][0]) { return; + } // ________________________________________________ // Do hadron - cascade correlations - if (masterConfigurations.doFullCorrelationStudy) + if (masterConfigurations.doFullCorrelationStudy) { fillCorrelationsCascade(triggerTracks, associatedCascades, true, true, collision.posX(), collision.posY(), collision.posZ(), cent, bField); + } + } + void processPrediction(soa::Join::iterator const& mcCollision, aod::McParticles const& mcParticles) + { + std::vector triggerIndices; + std::vector> associatedIndices; + std::vector assocHadronIndices; + std::vector piIndices; + std::vector k0ShortIndices; + std::vector lambdaIndices; + std::vector antiLambdaIndices; + std::vector xiMinusIndices; + std::vector xiPlusIndices; + std::vector omegaMinusIndices; + std::vector omegaPlusIndices; + float centMultFT0M = -1; + float centMultFT0A = -1; + float centMultFT0C = -1; + float multFT0M = -1; + float multFT0A = -1; + float multFT0C = -1; + float multEta08 = -1; + float multEta05 = -1; + histos.fill(HIST("Prediction/hEventSelection"), 0.5); + // INEL>N (N >= 0) implies INEL>0, so only the tighter enabled selection has to be evaluated + if (masterConfigurations.selectINELgtN) { + if (!isInelGtNGen(mcParticles, masterConfigurations.inelGtNThreshold)) { + return; + } + } else if (masterConfigurations.selectINELgtZERO) { + if (!o2::pwglf::isINELgt0mc(mcParticles, pdgDB)) { + return; + } + } + histos.fill(HIST("Prediction/hEventSelection"), 1.5); + if (std::abs(mcCollision.posZ()) > masterConfigurations.zVertexCut) { + return; + } + histos.fill(HIST("Prediction/hEventSelection"), 2.5); + if (masterConfigurations.useCentralityinPrediction) { + centMultFT0M = mcCollision.centFT0M(); + centMultFT0A = mcCollision.centFT0A(); + centMultFT0C = mcCollision.centFT0C(); + } else { + multFT0M = mCounter.countFT0A(mcParticles) + mCounter.countFT0C(mcParticles); + multFT0A = mCounter.countFT0A(mcParticles); + multFT0C = mCounter.countFT0C(mcParticles); + multEta08 = mCounter.countEta08(mcParticles); + multEta05 = mCounter.countEta05(mcParticles); + } + if (!masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hFT0AvsNchEta08"), multFT0A, multEta08); + histos.fill(HIST("Prediction/hFT0AvsNchEta05"), multFT0A, multEta05); + histos.fill(HIST("Prediction/hFT0CvsNchEta08"), multFT0C, multEta08); + histos.fill(HIST("Prediction/hFT0CvsNchEta05"), multFT0C, multEta05); + } + histos.fill(HIST("Prediction/hFT0MvsNchEta08"), multFT0M, multEta08); + histos.fill(HIST("Prediction/hFT0MvsNchEta05"), multFT0M, multEta05); + } + int iteratorNum = -1; + for (auto const& mcParticle : mcParticles) { + iteratorNum = iteratorNum + 1; + double geta = mcParticle.eta(); + double gpt = mcParticle.pt(); + double gphi = mcParticle.phi(); + if (std::abs(geta) > etaSel) { + continue; + } + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus || std::abs(mcParticle.pdgCode()) == PDG_t::kProton || std::abs(mcParticle.pdgCode()) == PDG_t::kElectron || std::abs(mcParticle.pdgCode()) == PDG_t::kMuonMinus) { + if (!masterConfigurations.doTriggPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + triggerIndices.emplace_back(iteratorNum); + if (masterConfigurations.useCentralityinPrediction) { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hTriggerFT0A"), gpt, geta, centMultFT0A); + histos.fill(HIST("Prediction/hTriggerFT0C"), gpt, geta, centMultFT0C); + } + histos.fill(HIST("Prediction/hTrigger"), gpt, geta, centMultFT0M); + } else { + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/hTriggerFT0A"), gpt, geta, multFT0A); + histos.fill(HIST("Prediction/hTriggerFT0C"), gpt, geta, multFT0C); + } + histos.fill(HIST("Prediction/hTrigger"), gpt, geta, multFT0M); + } + } + if (masterConfigurations.doCorrelationHadron) { + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + assocHadronIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hHadron"), gpt, geta, gphi); + } + } + } + if (!masterConfigurations.doAssocPhysicalPrimary || mcParticle.isPhysicalPrimary()) { + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus && masterConfigurations.doCorrelationPion) { + piIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hPion"), gpt, geta, gphi); + } + if (mcParticle.pdgCode() == PDG_t::kK0Short && masterConfigurations.doCorrelationK0Short) { + k0ShortIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hK0Short"), gpt, geta, gphi); + } + if (mcParticle.pdgCode() == PDG_t::kLambda0 && masterConfigurations.doCorrelationLambda) { + lambdaIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hLambda"), gpt, geta, gphi); + } + if (mcParticle.pdgCode() == PDG_t::kLambda0Bar && masterConfigurations.doCorrelationAntiLambda) { + antiLambdaIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hAntiLambda"), gpt, geta, gphi); + } + if (mcParticle.pdgCode() == PDG_t::kXiMinus && masterConfigurations.doCorrelationXiMinus) { + xiMinusIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hXiMinus"), gpt, geta, gphi); + } + if (mcParticle.pdgCode() == PDG_t::kXiPlusBar && masterConfigurations.doCorrelationXiPlus) { + xiPlusIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hXiPlus"), gpt, geta, gphi); + } + if (mcParticle.pdgCode() == PDG_t::kOmegaMinus && masterConfigurations.doCorrelationOmegaMinus) { + omegaMinusIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hOmegaMinus"), gpt, geta, gphi); + } + if (mcParticle.pdgCode() == PDG_t::kOmegaPlusBar && masterConfigurations.doCorrelationOmegaPlus) { + omegaPlusIndices.emplace_back(iteratorNum); + histos.fill(HIST("Prediction/hOmegaPlus"), gpt, geta, gphi); + } + } + } + + associatedIndices.emplace_back(k0ShortIndices); + associatedIndices.emplace_back(lambdaIndices); + associatedIndices.emplace_back(antiLambdaIndices); + associatedIndices.emplace_back(xiMinusIndices); + associatedIndices.emplace_back(xiPlusIndices); + associatedIndices.emplace_back(omegaMinusIndices); + associatedIndices.emplace_back(omegaPlusIndices); + associatedIndices.emplace_back(piIndices); + associatedIndices.emplace_back(assocHadronIndices); + for (std::size_t iTrigger = 0; iTrigger < triggerIndices.size(); iTrigger++) { + auto triggerParticle = mcParticles.iteratorAt(triggerIndices[iTrigger]); + // check range of trigger particle + if (triggerParticle.pt() > axisRanges[3][1] || triggerParticle.pt() < axisRanges[3][0]) { + continue; + } + double getatrigger = triggerParticle.eta(); + double gphitrigger = triggerParticle.phi(); + double pttrigger = triggerParticle.pt(); + auto const& mother = triggerParticle.mothers_first_as(); + auto globalIndex = mother.globalIndex(); + static_for<0, 8>([&](auto i) { // associated loop + constexpr int Index = i.value; + for (std::size_t iassoc = 0; iassoc < associatedIndices[Index].size(); iassoc++) { + auto assocParticle = mcParticles.iteratorAt(associatedIndices[Index][iassoc]); + if (triggerIndices[iTrigger] != associatedIndices[Index][iassoc] && globalIndex != assocParticle.globalIndex()) { // avoid self + double getaassoc = assocParticle.eta(); + double gphiassoc = assocParticle.phi(); + double ptassoc = assocParticle.pt(); + double deltaphi = computeDeltaPhi(gphitrigger, gphiassoc); + double deltaeta = getatrigger - getaassoc; + + // skip if basic ranges not met + if (deltaphi < axisRanges[0][0] || deltaphi > axisRanges[0][1]) { + continue; + } + if (deltaeta < axisRanges[1][0] || deltaeta > axisRanges[1][1]) { + continue; + } + if (ptassoc < axisRanges[2][0] || ptassoc > axisRanges[2][1]) { + continue; + } + if (TESTBIT(doCorrelation, i)) { + if (masterConfigurations.useCentralityinPrediction) { + histos.fill(HIST("Prediction/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0M); + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/sameEventFT0A/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0A); + histos.fill(HIST("Prediction/sameEventFT0C/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), centMultFT0C); + } + } else { + histos.fill(HIST("Prediction/sameEvent/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0M); + if (masterConfigurations.doSeparateFT0Prediction) { + histos.fill(HIST("Prediction/sameEventFT0A/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0A); + histos.fill(HIST("Prediction/sameEventFT0C/") + HIST(Particlenames[Index]), computeDeltaPhi(gphitrigger, gphiassoc), deltaeta, ptassoc, pttrigger, mcCollision.posZ(), multFT0C); + } + } + } + } + } + }); + } } PROCESS_SWITCH(HStrangeCorrelation, processSelectEventWithTrigger, "Select events with trigger only", true); PROCESS_SWITCH(HStrangeCorrelation, processSameEventHV0s, "Process same events, h-V0s", true); @@ -3241,11 +7536,15 @@ struct HStrangeCorrelation { PROCESS_SWITCH(HStrangeCorrelation, processMCGenerated, "Process MC generated", false); PROCESS_SWITCH(HStrangeCorrelation, processClosureTest, "Process Closure Test", false); + PROCESS_SWITCH(HStrangeCorrelation, processPairLossK0MC, "Diagnose truth-matched h-K0 pair loss", false); PROCESS_SWITCH(HStrangeCorrelation, processFeedDown, "process Feed Down", false); + PROCESS_SWITCH(HStrangeCorrelation, processPrediction, "process model prediction", false); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +thread_local HStrangeCorrelation::PairLossComparisonContext HStrangeCorrelation::pairLossComparison{}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(context)}; } diff --git a/PWGLF/Tasks/Strangeness/k0_mixed_events.cxx b/PWGLF/Tasks/Strangeness/k0_mixed_events.cxx index a5932d939a6..6790d9e2c57 100644 --- a/PWGLF/Tasks/Strangeness/k0_mixed_events.cxx +++ b/PWGLF/Tasks/Strangeness/k0_mixed_events.cxx @@ -78,34 +78,37 @@ using RecoTracks = soa::Join; -typedef std::shared_ptr trkType; -typedef std::shared_ptr trkTypeData; +using TrackType = const FilteredTracks::iterator; +using TrackTypePtr = std::shared_ptr; +using TrackTypeData = const RecoTracks::iterator; +using TrackTypeDataPtr = std::shared_ptr; -typedef std::shared_ptr colType; +using ColType = const FilteredCollisions::iterator; +using ColTypePtr = std::shared_ptr; -using MyFemtoPair = o2::aod::singletrackselector::FemtoPair; +using MyFemtoPair = o2::aod::singletrackselector::FemtoPair; class ResoPair : public MyFemtoPair { public: ResoPair() {} - ResoPair(trkType const& first, trkType const& second) : MyFemtoPair(first, second) + ResoPair(TrackTypePtr const& first, TrackTypePtr const& second) : MyFemtoPair(first, second) { setPair(first, second); } - ResoPair(trkType const& first, trkType const& second, const bool& isidentical) : MyFemtoPair(first, second, isidentical) {} + ResoPair(TrackTypePtr const& first, TrackTypePtr const& second, const bool& isidentical) : MyFemtoPair(first, second, isidentical) {} bool isClosePair() const { return MyFemtoPair::IsClosePair(mDeltaEta, mDeltaPhi, mRadius); } void setEtaDiff(const float deta) { mDeltaEta = deta; } void setPhiStarDiff(const float dphi) { mDeltaPhi = dphi; } void setRadius(const float r) { mRadius = r; } - void setPair(trkType const& first, trkType const& second) + void setPair(TrackTypePtr const& first, TrackTypePtr const& second) { MyFemtoPair::SetPair(first, second); lDecayDaughter1.SetPtEtaPhiM(first->pt(), first->eta(), first->phi(), particle_mass(GetPDG1())); lDecayDaughter2.SetPtEtaPhiM(second->pt(), second->eta(), second->phi(), particle_mass(GetPDG2())); lResonance = lDecayDaughter1 + lDecayDaughter2; } - void setPair(trkTypeData const& first, trkTypeData const& second) + void setPair(TrackTypeDataPtr const& first, TrackTypeDataPtr const& second) { // MyFemtoPair::SetPair(first, second); lDecayDaughter1.SetPtEtaPhiM(first->pt(), first->eta(), first->phi(), particle_mass(GetPDG1())); @@ -369,8 +372,8 @@ struct K0MixedEvents { void mixTracks(Type const& tracks1, Type const& tracks2, const float centrality) { LOG(debug) << "Mixing tracks of two different events"; - for (auto trk1 : tracks1) { - for (auto trk2 : tracks2) { + for (const auto& trk1 : tracks1) { + for (const auto& trk2 : tracks2) { Pair->setPair(trk1, trk2); @@ -529,9 +532,9 @@ struct K0MixedEvents { void processDerived(FilteredTracks const& tracks, FilteredCollisions const& collisions) { LOG(debug) << "Processing " << collisions.size() << " collisions and " << tracks.size() << " tracks"; - std::map> selectedtracks_1; - std::map> selectedtracks_2; - std::map, std::vector> mixbins; + std::map> selectedtracks_1; + std::map> selectedtracks_2; + std::map, std::vector> mixbins; if (_particlePDG_1 == 0 || _particlePDG_2 == 0) { LOGF(fatal, "One of passed PDG is 0!!!"); } @@ -542,7 +545,7 @@ struct K0MixedEvents { registry.fill(HIST("multPerc"), collision.multPerc()); } - for (auto track : tracks) { + for (const auto& track : tracks) { LOG(debug) << "Track index " << track.singleCollSelId(); if (!isTrackSelected(track)) { continue; @@ -559,7 +562,7 @@ struct K0MixedEvents { if ((track.sign() == _sign_1) && (track.p() < _PIDtrshld_1 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_1) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_1))) { // filling the map: eventID <-> selected particles1 - selectedtracks_1[track.singleCollSelId()].push_back(std::make_shared(track)); + selectedtracks_1[track.singleCollSelId()].push_back(std::make_shared(track)); registry.fill(HIST("p_first"), track.p()); registry.fill(HIST("dcaXY_first"), track.pt(), track.dcaXY()); @@ -591,7 +594,7 @@ struct K0MixedEvents { } else if ((track.sign() == _sign_2) && (_particlePDGtoReject != 0 || !o2::aod::singletrackselector::TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF))) && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) - selectedtracks_2[track.singleCollSelId()].push_back(std::make_shared(track)); + selectedtracks_2[track.singleCollSelId()].push_back(std::make_shared(track)); registry.fill(HIST("p_second"), track.p()); registry.fill(HIST("dcaXY_second"), track.pt(), track.dcaXY()); @@ -619,7 +622,7 @@ struct K0MixedEvents { } } - for (auto collision : collisions) { + for (const auto& collision : collisions) { if (selectedtracks_1.find(collision.globalIndex()) == selectedtracks_1.end()) { if (IsIdentical) continue; @@ -627,7 +630,7 @@ struct K0MixedEvents { continue; } - mixbins[std::pair{round(collision.posZ() / _vertexbinwidth), floor(collision.mult() / _multbinwidth)}].push_back(std::make_shared(collision)); + mixbins[std::pair{round(collision.posZ() / _vertexbinwidth), floor(collision.mult() / _multbinwidth)}].push_back(std::make_shared(collision)); } //====================================== mixing starts here ====================================== @@ -711,15 +714,15 @@ struct K0MixedEvents { { initCCDB(bcs.iteratorAt(0)); LOG(debug) << "Processing " << collisions.size() << " collisions and " << tracks.size() << " tracks"; - std::map> selectedtracks_1; - std::map> selectedtracks_2; - std::map, std::vector>> mixbins; + std::map> selectedtracks_1; + std::map> selectedtracks_2; + std::map, std::vector>> mixbins; if (_particlePDG_1 == 0 || _particlePDG_2 == 0) { LOGF(fatal, "One of passed PDG is 0!!!"); } registry.fill(HIST("Trks"), 2.f, tracks.size()); - for (auto collision : collisions) { + for (const auto& collision : collisions) { if (!acceptEvent(collision)) continue; LOG(debug) << "Collision index " << collision.globalIndex(); @@ -727,7 +730,7 @@ struct K0MixedEvents { registry.fill(HIST("multPerc"), collision.centFT0M()); } - for (auto track : tracks) { + for (const auto& track : tracks) { if (!isTrackSelected(track)) { continue; } @@ -757,7 +760,7 @@ struct K0MixedEvents { if ((track.sign() == _sign_1) && (track.p() < _PIDtrshld_1 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_1) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_1))) { // filling the map: eventID <-> selected particles1 - selectedtracks_1[track.collisionId()].push_back(std::make_shared(track)); + selectedtracks_1[track.collisionId()].push_back(std::make_shared(track)); registry.fill(HIST("p_first"), track.p()); registry.fill(HIST("dcaXY_first"), track.pt(), track.dcaXY()); @@ -789,7 +792,7 @@ struct K0MixedEvents { } else if ((track.sign() == _sign_2) && (_particlePDGtoReject != 0 || !o2::aod::singletrackselector::TOFselection(track, std::make_pair(_particlePDGtoReject, _rejectWithinNsigmaTOF))) && (track.p() < _PIDtrshld_2 ? o2::aod::singletrackselector::TPCselection(track, TPCcuts_2) : o2::aod::singletrackselector::TOFselection(track, TOFcuts_2))) { // filling the map: eventID <-> selected particles2 if (see condition above ^) - selectedtracks_2[track.collisionId()].push_back(std::make_shared(track)); + selectedtracks_2[track.collisionId()].push_back(std::make_shared(track)); registry.fill(HIST("p_second"), track.p()); registry.fill(HIST("dcaXY_second"), track.pt(), track.dcaXY()); @@ -817,7 +820,7 @@ struct K0MixedEvents { } } - for (auto collision : collisions) { + for (const auto& collision : collisions) { if (selectedtracks_1.find(collision.globalIndex()) == selectedtracks_1.end()) { if (IsIdentical) continue; @@ -825,7 +828,7 @@ struct K0MixedEvents { continue; } - mixbins[std::pair{round(collision.posZ() / _vertexbinwidth), floor(collision.multNTracksPVeta1() / _multbinwidth)}].push_back(std::make_shared(collision)); + mixbins[std::pair{round(collision.posZ() / _vertexbinwidth), floor(collision.multNTracksPVeta1() / _multbinwidth)}].push_back(std::make_shared(collision)); } //====================================== mixing starts here ====================================== diff --git a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx index 1ad7c388aeb..3eb74ce6662 100644 --- a/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaJetPolarizationIonsDerived.cxx @@ -9,9 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -/// \file lambdajetpolarizationionsderived.cxx +/// \file lambdaJetPolarizationIonsDerived.cxx /// \brief Lambda and antiLambda polarization analysis task using derived data -/// /// \author Cicero Domenico Muncinelli , Campinas State University // // Jet Polarization Ions task -- Derived data @@ -32,15 +31,20 @@ #include "PWGLF/DataModel/lambdaJetPolarizationIons.h" +#include "Common/Core/RecoDecay.h" + #include #include #include #include #include +#include #include +#include #include #include #include +#include #include #include @@ -48,12 +52,22 @@ #include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include +#include +#include #include +#include #include // For perpendicular jet direction QAs +#include // std::fill, for resetting the Delta Method accumulators +#include #include +#include +#include #include +#include #include +#include #include using namespace o2; @@ -61,14 +75,13 @@ using namespace o2::framework; using namespace o2::framework::expressions; using ROOT::Math::PtEtaPhiMVector; using ROOT::Math::XYZVector; -// using namespace o2::aod::lambdajetpol; // Used it explicitly along the code for clarity // Declaring constants: -constexpr double protonMass = o2::constants::physics::MassProton; // Assumes particle identification for daughter is perfect -constexpr double lambdaWeakDecayConstant = 0.749; // DPG 2025 update -constexpr double antiLambdaWeakDecayConstant = -0.758; // DPG 2025 update -constexpr double polPrefactorLambda = 3.0 / lambdaWeakDecayConstant; -constexpr double polPrefactorAntiLambda = 3.0 / antiLambdaWeakDecayConstant; +constexpr double ProtonMass = o2::constants::physics::MassProton; // Assumes particle identification for daughter is perfect +constexpr double LambdaWeakDecayConstant = 0.749; // DPG 2025 update +constexpr double AntiLambdaWeakDecayConstant = -0.758; // DPG 2025 update +constexpr double PolPrefactorLambda = 3.0 / LambdaWeakDecayConstant; +constexpr double PolPrefactorAntiLambda = 3.0 / AntiLambdaWeakDecayConstant; enum CentEstimator { kCentFT0C = 0, @@ -80,18 +93,31 @@ enum CentEstimator { #define RING_OBSERVABLE_FILL_LIST(X, FOLDER) \ /* Counters */ \ X(FOLDER "/QA/hDeltaPhi", deltaPhiJet) \ + X(FOLDER "/QA/hDeltaPhiVsDeltaEta", deltaPhiJet, deltaEtaJet) \ X(FOLDER "/QA/hDeltaTheta", deltaThetaJet) \ + X(FOLDER "/QA/hCosDeltaTheta", cosDeltaThetaJet) \ X(FOLDER "/QA/hIntegrated", 0.) \ + X(FOLDER "/QA/hPtJet", leadingJetPt) \ /* Lambda pT variation -- Youpeng's proposal */ \ X(FOLDER "/QA/hLambdaPt", v0pt) \ /* Counters */ \ X(FOLDER "/QA/h2dDeltaPhiVsLambdaPt", deltaPhiJet, v0pt) \ X(FOLDER "/QA/h2dDeltaThetaVsLambdaPt", deltaThetaJet, v0pt) \ + X(FOLDER "/QA/hDeltaPhiVsLeadJetPhi", deltaPhiJet, leadingJetPhi) \ /* Additional plots for instant gratification - 1D Profiles */ \ + X(FOLDER "/hRingObservableCounts", ringObservable) \ X(FOLDER "/pRingObservableDeltaPhi", deltaPhiJet, ringObservable) \ + X(FOLDER "/pRingObservablePhiJet", leadingJetPhi, ringObservable) \ + X(FOLDER "/pRingObservablePhiLambda", v0phi, ringObservable) \ X(FOLDER "/pRingObservableDeltaTheta", deltaThetaJet, ringObservable) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambda", v0eta, ringObservable) \ + X(FOLDER "/EtaDependence/pRingObservableEtaJet", leadingJetEta, ringObservable) \ X(FOLDER "/pRingObservableIntegrated", 0., ringObservable) \ X(FOLDER "/pRingObservableLambdaPt", v0pt, ringObservable) \ + X(FOLDER "/pRingObservableLeadJetPVz", collisionPVz, ringObservable) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtJet", leadingJetPt, ringObservable) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtJetVsEtaJet", leadingJetPt, leadingJetEta, ringObservable) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtJetVsEtaV0", leadingJetPt, v0eta, ringObservable) \ /* 2D Profiles */ \ X(FOLDER "/p2dRingObservableDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsLambdaPt", deltaThetaJet, v0pt, ringObservable) \ @@ -118,6 +144,13 @@ enum CentEstimator { /* 2D Profiles: Angle vs Mass */ \ X(FOLDER "/p2dRingObservableDeltaPhiVsMass", deltaPhiJet, v0LambdaLikeMass, ringObservable) \ X(FOLDER "/p2dRingObservableDeltaThetaVsMass", deltaThetaJet, v0LambdaLikeMass, ringObservable) \ + /* 2D Profile: Ring vs Eta variables */ \ + X(FOLDER "/EtaDependence/hCounterEtaLambdaMinusEtaJet", v0eta - leadingJetEta) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambdaMinusEtaJet", v0eta - leadingJetEta, ringObservable) \ + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet", v0eta, leadingJetEta, ringObservable) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaJet", v0eta, leadingJetEta) \ + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins", v0eta, leadingJetEta, ringObservable) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins", v0eta, leadingJetEta) \ /* 3D Profiles: Angle vs Mass vs Lambda pT */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsLambdaPt", deltaPhiJet, v0LambdaLikeMass, v0pt, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsLambdaPt", deltaThetaJet, v0LambdaLikeMass, v0pt, ringObservable) \ @@ -129,67 +162,224 @@ enum CentEstimator { /* 3D Profiles: Angle vs Mass vs Centrality */ \ X(FOLDER "/p3dRingObservableDeltaPhiVsMassVsCent", deltaPhiJet, v0LambdaLikeMass, centrality, ringObservable) \ X(FOLDER "/p3dRingObservableDeltaThetaVsMassVsCent", deltaThetaJet, v0LambdaLikeMass, centrality, ringObservable) \ - X(FOLDER "/pRingIntVsCentrality", centrality, ringObservable) + X(FOLDER "/pRingVsCentrality", centrality, ringObservable) \ + X(FOLDER "/p2dRingObservableVsPxPy", v0px, v0py, ringObservable) \ + X(FOLDER "/p2dRingObservableVsPzPx", v0pz, v0px, ringObservable) // (TODO: add counters for regular TH2Ds about centrality) // For leading particle -#define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ - X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ - X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) +#define RING_OBSERVABLE_LEADP_FILL_LIST(X, FOLDER) \ + X(FOLDER "/QA/hDeltaPhiLeadP", deltaPhiLeadP) \ + X(FOLDER "/QA/hDeltaThetaLeadP", deltaThetaLeadP) \ + X(FOLDER "/QA/hPtLeadP", leadPPt) \ + X(FOLDER "/QA/hCosDeltaThetaLeadP", cosDeltaThetaLeadP) \ + X(FOLDER "/hRingObservableLeadPCounts", ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPDeltaPhi", deltaPhiLeadP, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPDeltaTheta", deltaThetaLeadP, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambdaLeadP", v0eta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLeadP", leadPEta, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPIntegrated", 0., ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPLambdaPt", v0pt, ringObservableLeadP) \ + X(FOLDER "/pRingObservableLeadPPVz", collisionPVz, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadP", leadPPt, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP", leadPPt, leadPEta, ringObservableLeadP) \ + X(FOLDER "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0", leadPPt, v0eta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP", v0eta, leadPEta, ringObservableLeadP) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP", v0eta, leadPEta) \ + X(FOLDER "/p2dRingObservableLeadPVsPxPy", v0px, v0py, ringObservableLeadP) \ + X(FOLDER "/p2dRingObservableLeadPVsPzPx", v0pz, v0px, ringObservableLeadP) + +// A macro that encapsulates all eta checks for leading particle and V0s, along with the fills +// Parameters: +// FOLDER -- histogram folder string (compile-time literal) +// LEADP_IS_POS -- bool: leadPEtaPos +// V0_IS_POS -- bool: lambdaEtaPos +#define RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST(FOLDER, LEADP_IS_POS, V0_IS_POS) \ + do { \ + if (LEADP_IS_POS) { \ + /* leadP marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP", leadPPt, ringObservableLeadP) \ + if (V0_IS_POS) { \ + /* V0 marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (+leadP, +V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + } else { \ + /* V0 marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (+leadP, -V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + } \ + } else { \ + /* leadP marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP", leadPPt, ringObservableLeadP) \ + if (V0_IS_POS) { \ + /* V0 marginal: positive side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (-leadP, +V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0", leadPPt, ringObservableLeadP) \ + } else { \ + /* V0 marginal: negative side */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + /* Joint: (-leadP, -V0) */ \ + APPLY_HISTO_FILL(FOLDER "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0", leadPPt, ringObservableLeadP) \ + } \ + } \ + } while (0) // For subleading jet: -#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ - X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ - X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) +#define RING_OBSERVABLE_2NDJET_FILL_LIST(X, FOLDER) \ + X(FOLDER "/QA/hDeltaPhi2ndJet", deltaPhi2ndJet) \ + X(FOLDER "/QA/hDeltaTheta2ndJet", deltaTheta2ndJet) \ + X(FOLDER "/QA/hCosDeltaTheta2ndJet", cosDeltaTheta2ndJet) \ + X(FOLDER "/QA/hPt2ndJet", subleadingJetPt) \ + X(FOLDER "/hRingObservable2ndJetCounter", ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetDeltaPhi", deltaPhi2ndJet, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetDeltaTheta", deltaTheta2ndJet, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEtaLambda2ndJet", v0eta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/pRingObservableEta2ndJet", subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetIntegrated", 0., ringObservable2ndJet) \ + X(FOLDER "/pRingObservable2ndJetLambdaPt", v0pt, ringObservable2ndJet) \ + X(FOLDER "/pRingObservableSubLeadPVz", collisionPVz, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJet", subleadingJetPt, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet", subleadingJetPt, subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0", subleadingJetPt, v0eta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta, ringObservable2ndJet) \ + X(FOLDER "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet", v0eta, subleadingJetEta) #define POLARIZATION_PROFILE_FILL_LIST(X, FOLDER) \ /* =============================== */ \ /* 1D TProfiles vs v0phi */ \ /* =============================== */ \ - X(FOLDER "/QA/pPxStarPhi", v0phiToFillHists, PolStarX) \ - X(FOLDER "/QA/pPyStarPhi", v0phiToFillHists, PolStarY) \ - X(FOLDER "/QA/pPzStarPhi", v0phiToFillHists, PolStarZ) \ + X(FOLDER "/QA/pPxStarPhi", v0phiToFillHists, polStarX) \ + X(FOLDER "/QA/pPyStarPhi", v0phiToFillHists, polStarY) \ + X(FOLDER "/QA/pPzStarPhi", v0phiToFillHists, polStarZ) \ /* =============================== */ \ /* 1D TProfiles vs DeltaPhi_jet */ \ /* =============================== */ \ - X(FOLDER "/QA/pPxStarDeltaPhi", deltaPhiJet, PolStarX) \ - X(FOLDER "/QA/pPyStarDeltaPhi", deltaPhiJet, PolStarY) \ - X(FOLDER "/QA/pPzStarDeltaPhi", deltaPhiJet, PolStarZ) \ + X(FOLDER "/QA/pPxStarDeltaPhi", deltaPhiJet, polStarX) \ + X(FOLDER "/QA/pPyStarDeltaPhi", deltaPhiJet, polStarY) \ + X(FOLDER "/QA/pPzStarDeltaPhi", deltaPhiJet, polStarZ) \ /* =============================== */ \ /* 2D TProfiles vs DeltaPhi_jet and Lambda pT */ \ /* =============================== */ \ - X(FOLDER "/QA/p2dPxStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarX) \ - X(FOLDER "/QA/p2dPyStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarY) \ - X(FOLDER "/QA/p2dPzStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, PolStarZ) + X(FOLDER "/QA/p2dPxStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, polStarX) \ + X(FOLDER "/QA/p2dPyStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, polStarY) \ + X(FOLDER "/QA/p2dPzStarDeltaPhiVsLambdaPt", deltaPhiJet, v0pt, polStarZ) \ + /* 2D vector-field profiles vs Lambda momentum plane */ \ + X(FOLDER "/QA/p2dPxStar_vsPxPy", v0px, v0py, polStarX) \ + X(FOLDER "/QA/p2dPyStar_vsPxPy", v0px, v0py, polStarY) \ + X(FOLDER "/QA/p2dPzStar_vsPxPy", v0px, v0py, polStarZ) \ + X(FOLDER "/QA/p2dPxStar_vsPzPx", v0pz, v0px, polStarX) \ + X(FOLDER "/QA/p2dPyStar_vsPzPx", v0pz, v0px, polStarY) \ + X(FOLDER "/QA/p2dPzStar_vsPzPx", v0pz, v0px, polStarZ) // Apply the macros (notice I had to include the semicolon (";") after the function, so you don't need to // write that when calling this APPLY_HISTO_FILL. The code will look weird, but without this the compiler // would not know to end each statement with a semicolon): #define APPLY_HISTO_FILL(NAME, ...) histos.fill(HIST(NAME), __VA_ARGS__); -struct lambdajetpolarizationionsderived { +// Delta Method Fill Lists +#define DELTA_INTEGRATED_FILL_LIST(X, FOLDER, r, n) \ + X(FOLDER "/DeltaMethod/hIntegrated", 0.5, r) \ + X(FOLDER "/DeltaMethod/hIntegrated", 1.5, (double)(n)) \ + X(FOLDER "/DeltaMethod/hIntegrated", 2.5, (r) * (r)) \ + X(FOLDER "/DeltaMethod/hIntegrated", 3.5, (double)((n) * (n))) \ + X(FOLDER "/DeltaMethod/hIntegrated", 4.5, (r) * (n)) + +#define DELTA_2D_FILL_LIST(X, FOLDER, HIST_NAME, center, r, n) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 0.5, r) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 1.5, (double)(n)) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 2.5, (r) * (r)) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 3.5, (double)((n) * (n))) \ + X(FOLDER "/DeltaMethod/" HIST_NAME, center, 4.5, (r) * (n)) + +// Master flush macro to dump an event tracker into the histograms: +#define FLUSH_DELTA_TRACKER(FOLDER, TRACKER, AXIS_PT, AXIS_MASS, AXIS_DTHETA) \ + if ((TRACKER).nInt > 0) { \ + DELTA_INTEGRATED_FILL_LIST(APPLY_HISTO_FILL, FOLDER, (TRACKER).rInt, (TRACKER).nInt) \ + } \ + for (size_t bin = 0; bin < (TRACKER).rPt.size(); ++bin) { \ + int nVal = (TRACKER).nPt[bin]; \ + if (nVal == 0) \ + continue; \ + double rVal = (TRACKER).rPt[bin]; \ + double center = (AXIS_PT)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dLambdaPtVsDeltaComp", center, rVal, nVal) \ + } \ + for (size_t bin = 0; bin < (TRACKER).rMass.size(); ++bin) { \ + int nVal = (TRACKER).nMass[bin]; \ + if (nVal == 0) \ + continue; \ + double rVal = (TRACKER).rMass[bin]; \ + double center = (AXIS_MASS)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dMassVsDeltaComp", center, rVal, nVal) \ + } \ + for (size_t bin = 0; bin < (TRACKER).rDtheta.size(); ++bin) { \ + int nVal = (TRACKER).nDtheta[bin]; \ + if (nVal == 0) \ + continue; \ + double rVal = (TRACKER).rDtheta[bin]; \ + double center = (AXIS_DTHETA)->GetBinCenter(bin); \ + DELTA_2D_FILL_LIST(APPLY_HISTO_FILL, FOLDER, "h2dDeltaThetaVsDeltaComp", center, rVal, nVal) \ + } +struct lambdajetpolarizationionsderived { // Define histogram registries: HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Master analysis switches + // Master analysis switches: Configurable analyseLambda{"analyseLambda", true, "process Lambda-like candidates"}; Configurable analyseAntiLambda{"analyseAntiLambda", false, "process AntiLambda-like candidates"}; - Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; + Configurable analyseMagField{"analyseMagField", true, "analyse efficiency effects wrt magnetic field"}; // Older DerivedData lacks magField. "if constexpr (requires { collision.magField(); })" would only see the current header definition, so need a flag for retro-comp. + // Configurable doPPAnalysis{"doPPAnalysis", false, "if in pp, set to true. Default is HI"}; + Configurable doJetProxy5dQA{"doJetProxy5dQA", false, "generates expensive THnSparse histograms for joint distribution QA of the jet proxies and collisions"}; + + // A very inexpensive "signal extraction" imitation based on v0InMassPeak bool: + // (Uses a mass interval to remove or include V0s from the final AnalysisResults to take advantage of existing post-processing codes) + Configurable excludeOutOfPeakQA{"excludeOutOfPeakQA", false, "removes all V0s outside an approximate +/- 5*sigma window from the mass peak"}; // A naive estimator of signal + Configurable excludeInPeakQA{"excludeInPeakQA", false, "uses only the V0s outside an approximate +/- 5*sigma window from the mass peak"}; // A naive estimator of background + + // Per-family histogram switches: + // (Each family books >100 histograms, so it is necessary to keep some of these switches off to avoid the HistogramRegistry limit) + struct : ConfigurableGroup { + std::string prefix = "familySwitches"; // JSON group name + Configurable doFamilyRing{"doFamilyRing", true, "Book and fill the 'Ring' family (no additional cuts). Keep this on for most passes."}; + Configurable doFamilyRingKinematicCuts{"doFamilyRingKinematicCuts", false, "Book and fill the 'RingKinematicCuts' family (Lambda kinematic cuts applied)."}; + Configurable doFamilyJetKinematicCuts{"doFamilyJetKinematicCuts", true, "Book and fill the 'JetKinematicCuts' family (jet kinematic cuts applied)."}; + Configurable doFamilyJetAndLambdaKinematicCuts{"doFamilyJetAndLambdaKinematicCuts", false, "Book and fill the 'JetAndLambdaKinematicCuts' family (both cuts applied)."}; + } familySwitches; + + // QA switches: + struct : ConfigurableGroup { + std::string prefix = "qaSwitches"; // JSON group name + Configurable doFakePolDiagnosticsQA{"doFakePolDiagnosticsQA", true, "Book and fill the EtaStudy/ and HelicityEfficiencyQA/ folders."}; // The largest per-V0 fill cost in this task + } qaSwitches; // Centrality: Configurable centralityEstimator{"centralityEstimator", kCentFT0M, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFV0A)"}; // Default is FT0M + Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position [cm]"}; // An additional post-processing cut after derived data was written. Same default as lambdaJetPolarizationIons.cxx producer - // QAs that purposefully break the analysis + // QAs that purposefully "break" the analysis // -- All of these tests should give us zero signal if the source is truly Lambda Polarization from vortices - Configurable forcePolSignQA{"forcePolSignQA", false, "force antiLambda decay constant to be positive: should kill all the signal, if any. For QA"}; - Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; - Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; - Configurable jetRForSmuding{"jetRForSmuding", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; + struct : ConfigurableGroup { + std::string prefix = "fakePolSwitches"; // JSON group name + Configurable forcePolSignQA{"forcePolSignQA", false, "force antiLambda decay constant to be positive: should kill all the signal, if any. For QA"}; + Configurable forcePerpToJet{"forcePerpToJet", false, "force jet direction to be perpendicular to jet estimator. For QA"}; + Configurable forceJetDirectionSmudge{"forceJetDirectionSmudge", false, "fluctuate jet direction by 10% of R around original axis. For QA (tests sensibility)"}; + Configurable forceRandJet{"forceRandJet", false, "makes jet direction random. A QA for AEE fake signal and its removal"}; + Configurable forcePreviousJet{"forcePreviousJet", false, "uses previous event's jet direction instead of a random sample. A baseline for fake signal removal"}; + Configurable forceDatalikeJet{"forceDatalikeJet", false, "a compromise between forceRandJet and forcePreviousJet. Parameterized distribution from data"}; + Configurable doMixedEventProxies{"doMixedEventProxies", false, "mix leadP/leadJet/subJet directions between events using (proxy pt, Zvtx, centrality) bins -- three independent mixings, one per proxy"}; + Configurable mixedEventWindowSize{"mixedEventWindowSize", 10, "number of neighbours for doMixedEventProxies: how many similar collisions stay eligible as mixing partners at once (shared by all 3 proxies)."}; + Configurable nProxyResamples{"nProxyResamples", 1, "The amount of resamplings of jet direction per event. Use ONLY for forceRandJet and forceDatalikeJet"}; + } fakePolSwitches; + + // Configurable jetRForSmudging{"jetRForSmudging", 0.4, "QA quantity: the chosen R scale for the jet direction smudge"}; // Superseeded by jetR: kept the same scale in analysis and QA + Configurable jetR{"jetR", 0.4f, "Radius of the jet"}; // Provide manually, please. + Configurable minLeadParticlePt{"minLeadParticlePt", 2.0f, "Minimum Pt for a lead track to be considered a valid proxy for a jet (may be more restrictive than TableProducer)"}; + Configurable minLeadJetPt{"minLeadJetPt", 10.0f, "Minimum Pt for leading jet to be considered valid (may be more restrictive than TableProducer)"}; + Configurable minSubLeadJetPt{"minSubLeadJetPt", 5.0f, "Minimum Pt for subleading jet to be considered valid (may be more restrictive than TableProducer)"}; ///////////////////////// // Configurable blocks: @@ -197,14 +387,44 @@ struct lambdajetpolarizationionsderived { struct : ConfigurableGroup { std::string prefix = "axisConfigurations"; // JSON group name ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 17.0f, 19.0f, 21.0f, 23.0f, 25.0f, 30.0f, 35.0f, 40.0f, 50.0f}, "pt axis for analysis"}; - ConfigurableAxis axisPtCoarseQA{"axisPtCoarse", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; - ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, "Lambda mass in GeV/c"}; // Default is {200, 1.101f, 1.131f} + ConfigurableAxis axisPtCoarseQA{"axisPtCoarseQA", {VARIABLE_WIDTH, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 7.0f, 10.0f, 15.0f}, "pt axis for QA"}; + ConfigurableAxis axisLambdaMass{"axisLambdaMass", {450, 1.08f, 1.15f}, "#Lambda mass in GeV/c"}; // Default is {200, 1.101f, 1.131f} + + // Symmetric momentum-plane axes for the vector-field / ring 2D profiles: + ConfigurableAxis axisLambdaPx{"axisLambdaPx", {40, -3.0f, 3.0f}, "#Lambda p_{x} (GeV/c)"}; + ConfigurableAxis axisLambdaPy{"axisLambdaPy", {40, -3.0f, 3.0f}, "#Lambda p_{y} (GeV/c)"}; + ConfigurableAxis axisLambdaPz{"axisLambdaPz", {40, -4.0f, 4.0f}, "#Lambda p_{z} (GeV/c)"}; + + // Event properties: + ConfigurableAxis axisPVz{"axisPVz", {60, -15.0f, +15.0f}, "Primary Vertex Z [cm]"}; // Jet axes: - ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! - ConfigurableAxis axisJetPt{"axisJetPt", {50, 0.f, 200.f}, "Jet p_{t} (GeV)"}; + // ConfigurableAxis axisLeadingParticlePt{"axisLeadingParticlePt", {100, 0.f, 200.f}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! + // ConfigurableAxis axisJetPt{"axisJetPt", {50, 0.f, 200.f}, "Jet p_{t} (GeV)"}; + ConfigurableAxis axisJetPt{ + "axisJetPt", + {VARIABLE_WIDTH, + 0, 2, 4, 6, 8, 10, // 2 GeV bins + 15, 20, // 5 GeV bins + 30, 40, // 10 GeV bins + 60, 80, // 20 GeV bins + 120, 160, 200}, // 40 GeV bins + "Jet p_{T} (GeV)"}; + ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; + ConfigurableAxis axisEta{"axisEta", {50, -1.0f, 1.0f}, "#eta"}; + ConfigurableAxis axisEtaCoarse{"axisEtaCoarse", {20, -0.9f, 0.9f}, "#eta coarse axis"}; + ConfigurableAxis axisV0Eta{"axisV0Eta", {75, -1.5f, 1.5f}, "V0 #eta"}; // An axis for V0 eta, which can go up to 1.5 given standard producer selections + ConfigurableAxis axisV0EtaCoarse{"axisV0EtaCoarse", {32, -1.5f, 1.5f}, "V0 #eta coarse"}; + ConfigurableAxis axisDeltaEtaCoarse{"axisDeltaEtaCoarse", {40, -1.8f, 1.8f}, "#Delta#eta coarse axis"}; ConfigurableAxis axisDeltaTheta{"axisDeltaTheta", {40, 0, constants::math::PI}, "#Delta #theta_{jet}"}; + ConfigurableAxis axisCosTheta{"axisCosTheta", {50, -1, 1}, "cos(#theta)"}; + ConfigurableAxis axisPhi{"axisPhi", {40, 0., constants::math::TwoPI}, "#varphi"}; ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {40, -constants::math::PI, constants::math::PI}, "#Delta #phi_{jet}"}; + ConfigurableAxis axisRingCounts{"axisRingCounts", {90, -4.5, 4.5}, "<#it{R}>"}; + ConfigurableAxis axisDeltaCollisionIndex{"axisDeltaCollisionIndex", {200, -0.5f, 199.5f}, "#Delta collision index"}; // Always positive: SameKindPair pairs strictly upper + + ConfigurableAxis axisDCAdau{"axisDCAdau", {10, 0., 2.0}, "DCA V0 daughters (cm)"}; + ConfigurableAxis axisDCAdauPV{"axisDCAdauPV", {10, 0., 1.2}, "DCA dauPV (cm)"}; // v0Selections.dcav0dau's default maximum is 1.2f in the TableProducer // Coarser axes for signal extraction: ConfigurableAxis axisPtSigExtract{"axisPtSigExtract", {VARIABLE_WIDTH, 0.0f, 0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 2.0f, 2.5f, 3.0f, 4.0f, 6.0f, 8.0f, 10.0f, 15.0f, 20.0f, 30.0f, 50.0f}, "pt axis for signal extraction"}; @@ -223,12 +443,15 @@ struct lambdajetpolarizationionsderived { 1.1248, 1.1288, 1.1328, 1.1368, 1.1408, 1.1448, 1.1488}, "Lambda mass in GeV/c"}; - ConfigurableAxis axisLeadingParticlePtSigExtract{"axisLeadingParticlePtSigExtract", {VARIABLE_WIDTH, 0, 4, 8, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! - ConfigurableAxis axisJetPtSigExtract{"axisJetPtSigExtract", {VARIABLE_WIDTH, 0, 5, 10, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Jet p_{t} (GeV)"}; + // ConfigurableAxis axisLeadingParticlePtSigExtract{"axisLeadingParticlePtSigExtract", {VARIABLE_WIDTH, 0, 4, 8, 12, 16, 20, 25, 30, 35, 40, 60, 100, 200}, "Leading particle p_{T} (GeV/c)"}; // Simpler version! // (TODO: add a lambdaPt axis that is pre-selected only on the 0.5 to 1.5 Pt region for the Ring observable with lambda cuts to not store a huge histogram with empty bins by construction) - ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "Centrality"}; + // ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 5.0f, 10.0f, 20.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f, 90.0f, 100.0f}, "Centrality"}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0.0f, 20.0f, 50.0f, 100.0f}, "Centrality"}; + + // For the delta method error propagation (slightly better than just SEM error propagation with TProfiles): + ConfigurableAxis axisDeltaComponents{"axisDeltaComponents", {5, 0.0, 5.0}, "0: r_k, 1: n_k, 2: r_k^2, 3: n_k^2, 4: r_k*n_k"}; } axisConfigurations; // Helper functions: @@ -244,8 +467,76 @@ struct lambdajetpolarizationionsderived { return phi; } + // A small tracker struct for convenience -- Accumulates values for the Delta Method error estimator: + struct EventDeltaTracker { + double rInt = 0.0; // Ring accumulator + int nInt = 0; // Counts accumulator + std::vector rPt, rMass, rDtheta; + std::vector nPt, nMass, nDtheta; + + /// \brief Resizes every accumulator. Size includes ROOT's under/overflow bins, so the indices + /// returned by TAxis::FindBin() (0 .. nBins+1) can be used directly for dereferencing here. + void resize(int nBinsPt, int nBinsMass, int nBinsDTheta) + { + rPt.assign(nBinsPt + 2, 0.0); + rMass.assign(nBinsMass + 2, 0.0); + rDtheta.assign(nBinsDTheta + 2, 0.0); + nPt.assign(nBinsPt + 2, 0); + nMass.assign(nBinsMass + 2, 0); + nDtheta.assign(nBinsDTheta + 2, 0); + } + + void reset() + { + rInt = 0.0; + nInt = 0; + std::fill(rPt.begin(), rPt.end(), 0.0); + std::fill(rMass.begin(), rMass.end(), 0.0); + std::fill(rDtheta.begin(), rDtheta.end(), 0.0); + std::fill(nPt.begin(), nPt.end(), 0); + std::fill(nMass.begin(), nMass.end(), 0); + std::fill(nDtheta.begin(), nDtheta.end(), 0); + } + + void addV0(double ringObs, int binPt, int binMass, int binDTheta) + { + rInt += ringObs; + rPt[binPt] += ringObs; + rMass[binMass] += ringObs; + rDtheta[binDTheta] += ringObs; + nInt += 1; + nPt[binPt] += 1; + nMass[binMass] += 1; + nDtheta[binDTheta] += 1; + } + }; + + // Allocating one tracker per family so the accumulators are allocated only once: + EventDeltaTracker trackRing, trackRingKinCuts, trackJetKinCuts, trackJetLambdaKinCuts; + + // Axis pointers for Delta Method binning (fetched once in init, declared once here) + TAxis* mAxisPt = nullptr; + TAxis* mAxisMass = nullptr; + TAxis* mAxisDTheta = nullptr; + void init(InitContext const&) { + // Configuration validation: + // These combinations would not crash otherwise, so they are rejected at init() time. + const int nDistortionsOn = static_cast(fakePolSwitches.forcePerpToJet) + static_cast(fakePolSwitches.forceJetDirectionSmudge) + + static_cast(fakePolSwitches.forceRandJet) + static_cast(fakePolSwitches.forcePreviousJet) + + static_cast(fakePolSwitches.forceDatalikeJet) + static_cast(fakePolSwitches.doMixedEventProxies); + if (nDistortionsOn > 1) // applyProxyDistortion() is an if/else chain, so extra switches are silently ignored + LOG(fatal) << "fakePolSwitches: " << nDistortionsOn << " proxy distortions enabled at once. They are mutually exclusive -- " + << "applyProxyDistortion() would apply only the first and silently drop the rest."; + if (fakePolSwitches.nProxyResamples > 1 && (fakePolSwitches.forcePreviousJet || fakePolSwitches.doMixedEventProxies)) + LOG(fatal) << "fakePolSwitches: nProxyResamples > 1 is only meaningful for forceRandJet/forceDatalikeJet. " + << "Previous-jet/Mixed-Event proxies do not change between resamplings, so every extra pass would double-count the same proxy."; + if (excludeOutOfPeakQA && excludeInPeakQA) // Complementary selections + LOG(fatal) << "excludeOutOfPeakQA and excludeInPeakQA are complementary: enabling both rejects every V0."; + if (!familySwitches.doFamilyRing) // Todo: think of a smarter way of handling the axis getters for the DeltaMethod + LOG(fatal) << "doFamilyRing must be on: the Delta Method accumulators take their binning from the Ring/ histograms."; + // Ring observable histograms: // Helper to register one full histogram family (kinematic cut variation of ring observable) auto addRingObservableFamily = [&](const std::string& folder) { @@ -253,35 +544,67 @@ struct lambdajetpolarizationionsderived { // QA histograms: angle and pT distributions // (No mass dependency -- useful to check kinematic sculpting from cuts) // =============================== - histos.add((folder + "/QA/hDeltaPhi").c_str(), "hDeltaPhi", kTH1D, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/hDeltaTheta").c_str(), "hDeltaTheta", kTH1D, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/QA/hIntegrated").c_str(), "hIntegrated", kTH1D, {{1, -0.5, 0.5}}); + histos.add((folder + "/QA/hDeltaPhi").c_str(), "#Delta#varphi_{jet};#Delta#varphi_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaPhiVsDeltaEta").c_str(), "#Delta#varphi_{jet};#Delta#varphi_{jet}; #eta_{#Lambda}-#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDeltaEtaCoarse}); + histos.add((folder + "/QA/hDeltaPhiVsLeadJetPhi").c_str(), "#Delta#varphi_{jet};#varphi_{Jet};#varphi_{Jet};Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaTheta").c_str(), "#Delta#theta_{jet};#Delta#theta_{jet};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaTheta").c_str(), "cos(#Delta#theta_{jet});cos(#Delta#theta_{jet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + histos.add((folder + "/QA/hIntegrated").c_str(), "Integrated counts; ;Counts", kTH1D, {{1, -0.5, 0.5}}); + + histos.add((folder + "/QA/hDeltaPhiLeadP").c_str(), "#Delta#varphi_{LeadP};#Delta#varphi_{LeadP};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaThetaLeadP").c_str(), "#Delta#theta_{LeadP};#Delta#theta_{LeadP};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaThetaLeadP").c_str(), "cos(#Delta#theta_{LeadP});cos(#Delta#theta_{LeadP});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + histos.add((folder + "/QA/hDeltaPhi2ndJet").c_str(), "#Delta#varphi_{SubJet};#Delta#varphi_{SubJet};Counts", kTH1D, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/hDeltaTheta2ndJet").c_str(), "#Delta#theta_{SubJet};#Delta#theta_{SubJet};Counts", kTH1D, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/QA/hCosDeltaTheta2ndJet").c_str(), "cos(#Delta#theta_{SubJet});cos(#Delta#theta_{SubJet});Counts", kTH1D, {axisConfigurations.axisCosTheta}); // Should actually be flat due to the geometry + // =============================== // Lambda pT dependence // =============================== - histos.add((folder + "/QA/hLambdaPt").c_str(), "hLambdaPt", kTH1D, {axisConfigurations.axisPt}); - histos.add((folder + "/QA/h2dDeltaPhiVsLambdaPt").c_str(), "h2dDeltaPhiVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/QA/h2dDeltaThetaVsLambdaPt").c_str(), "h2dDeltaThetaVsLambdaPt", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); + histos.add((folder + "/QA/hLambdaPt").c_str(), "#Lambda #it{p}_{T};#it{p}_{T}^{#Lambda} (GeV/c);Counts", kTH1D, {axisConfigurations.axisPt}); + histos.add((folder + "/QA/h2dDeltaPhiVsLambdaPt").c_str(), "#Delta#varphi_{jet} vs #Lambda #it{p}_{T};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c)", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + histos.add((folder + "/QA/h2dDeltaThetaVsLambdaPt").c_str(), "#Delta#theta_{jet} vs #Lambda #it{p}_{T};#Delta#theta_{jet};#it{p}_{T}^{#Lambda} (GeV/c)", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); // =============================== // Polarization observable QAs // (not Ring: actual polarization!) // =============================== - // Will implement these as TProfiles, as polarization is also a measure like P_\Lambda = (3/\alpha_\Lambda) * , so the error is similar + // Will implement these as TProfiles, as polarization is also a measure like P_Lambda = (3/\alpha_Lambda) * , so the error is similar // =============================== // 1D TProfiles // =============================== - histos.add((folder + "/QA/pPxStarPhi").c_str(), "pPxStarPhi;#varphi_{#Lambda};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPyStarPhi").c_str(), "pPyStarPhi;#varphi_{#Lambda};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPzStarPhi").c_str(), "pPzStarPhi;#varphi_{#Lambda};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPxStarDeltaPhi").c_str(), "pPxStarDeltaPhi;#Delta#varphi_{jet};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPyStarDeltaPhi").c_str(), "pPyStarDeltaPhi;#Delta#varphi_{jet};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/QA/pPzStarDeltaPhi").c_str(), "pPzStarDeltaPhi;#Delta#varphi_{jet};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPxStarPhi").c_str(), "_{x} vs #varphi_{#Lambda};#varphi_{#Lambda};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPyStarPhi").c_str(), "_{y} vs #varphi_{#Lambda};#varphi_{#Lambda};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPzStarPhi").c_str(), "_{z} vs #varphi_{#Lambda};#varphi_{#Lambda};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPxStarDeltaPhi").c_str(), "_{x} vs #Delta#varphi_{jet};#Delta#varphi_{jet};_{x}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPyStarDeltaPhi").c_str(), "_{y} vs #Delta#varphi_{jet};#Delta#varphi_{jet};_{y}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/QA/pPzStarDeltaPhi").c_str(), "_{z} vs #Delta#varphi_{jet};#Delta#varphi_{jet};_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); // =============================== // 2D TProfiles (Lambda correlations) // =============================== - histos.add((folder + "/QA/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "p2dPxStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{x}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/QA/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "p2dPyStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/QA/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "p2dPzStarDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/p2dPxStarDeltaPhiVsLambdaPt").c_str(), "_{x} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{x}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/p2dPyStarDeltaPhiVsLambdaPt").c_str(), "_{y} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{y}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/p2dPzStarDeltaPhiVsLambdaPt").c_str(), "_{z} vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);_{z}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPtSigExtract}); + + // =============================== + // Vector-field profiles: vs Lambda momentum plane: + // (a companion code plots these as a vector field) + // =============================== + histos.add((folder + "/QA/p2dPxStar_vsPxPy").c_str(), "_{x} vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);_{x}", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/QA/p2dPyStar_vsPxPy").c_str(), "_{y} vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);_{y}", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/QA/p2dPzStar_vsPxPy").c_str(), "_{z} vs (p_{x}^{#Lambda},p_{y}^{#Lambda}) [colormap];p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);_{z}", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + + histos.add((folder + "/QA/p2dPxStar_vsPzPx").c_str(), "_{x} vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);_{x}", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + histos.add((folder + "/QA/p2dPyStar_vsPzPx").c_str(), "_{y} vs (p_{z}^{#Lambda},p_{x}^{#Lambda}) [colormap];p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);_{y}", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + histos.add((folder + "/QA/p2dPzStar_vsPzPx").c_str(), "_{z} vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);_{z}", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + + // =============================== + // Ring observable, single scalar 2D profile: + // =============================== + histos.add((folder + "/p2dRingObservableVsPxPy").c_str(), "<#it{R}> vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/p2dRingObservableVsPzPx").c_str(), "<#it{R}> vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); + // For LeadP estimators: + histos.add((folder + "/p2dRingObservableLeadPVsPxPy").c_str(), "<#it{R}>_{LeadP} vs (p_{x}^{#Lambda},p_{y}^{#Lambda});p_{x}^{#Lambda} (GeV/c);p_{y}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPx, axisConfigurations.axisLambdaPy}); + histos.add((folder + "/p2dRingObservableLeadPVsPzPx").c_str(), "<#it{R}>_{LeadP} vs (p_{z}^{#Lambda},p_{x}^{#Lambda});p_{z}^{#Lambda} (GeV/c);p_{x}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaPz, axisConfigurations.axisLambdaPx}); // TProfiles with correct error bars:: // -- TProfiles will handle the error estimate of the Ring Observable via the variance, even though @@ -294,85 +617,149 @@ struct lambdajetpolarizationionsderived { // =============================== // 1D TProfiles // =============================== - histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "pRingObservableDeltaPhi;#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "pRingObservableDeltaTheta;#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/pRingObservableIntegrated").c_str(), "pRingObservableIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); - histos.add((folder + "/pRingObservableLambdaPt").c_str(), "pRingObservableLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + histos.add((folder + "/pRingObservableDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{jet};#Delta#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + // To see the actual distribution of counts in data (another differential-like shape of the distribution we are taking an average of): + histos.add((folder + "/hRingObservableCounts").c_str(), "Counts vs <#it{R}>_{jet};<#it{R}>; Counts", kTH1D, {axisConfigurations.axisRingCounts}); + histos.add((folder + "/pRingObservablePhiJet").c_str(), "<#it{R}> vs #varphi_{jet};#varphi_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); + histos.add((folder + "/pRingObservablePhiLambda").c_str(), "<#it{R}> vs #varphi_{#Lambda};#varphi_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPhi}); + histos.add((folder + "/pRingObservableDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{jet};#Delta#theta_{jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/pRingObservableIntegrated").c_str(), "Integrated <#it{R}>; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); + histos.add((folder + "/pRingObservableLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda};#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + + // Ring vs Jet proxy pT: + histos.add((folder + "/ProxyPtDependence/pRingVsPtJet").c_str(), "<#it{R}> vs Jet #it{p}_{T};#it{p}_{T}^{Jet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T};#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJet").c_str(), "<#it{R}> vs SubJet #it{p}_{T};#it{p}_{T}^{SubJet} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + // And some counters to be aware of the amount of Lambdas (and jets) in each pT interval: + histos.add((folder + "/QA/hPtJet").c_str(), "Jet #it{p}_{T};#it{p}_{T}^{Jet} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add((folder + "/QA/hPtLeadP").c_str(), "LeadP #it{p}_{T};#it{p}_{T}^{LeadP} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); + histos.add((folder + "/QA/hPt2ndJet").c_str(), "SubJet #it{p}_{T};#it{p}_{T}^{SubJet} (GeV/c);Counts", kTH1D, {axisConfigurations.axisJetPt}); + + // Splitting into positive and negative eta contributions: + histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaJet").c_str(), "<#it{R}> vs Jet #it{p}_{T} vs #eta_{Jet};#it{p}_{T}^{Jet} (GeV/c);#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} vs #eta_{LeadP};#it{p}_{T}^{LeadP} (GeV/c);#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEta2ndJet").c_str(), "<#it{R}> vs SubJet #it{p}_{T} vs #eta_{SubJet};#it{p}_{T}^{SubJet} (GeV/c);#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + // For each Lambda's eta: + histos.add((folder + "/ProxyPtDependence/pRingVsPtJetVsEtaV0").c_str(), "<#it{R}> vs Jet #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{Jet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadPVsEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{LeadP} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + histos.add((folder + "/ProxyPtDependence/pRingVsPt2ndJetVsEtaV0").c_str(), "<#it{R}> vs SubJet #it{p}_{T} vs #eta_{V0};#it{p}_{T}^{SubJet} (GeV/c);#eta_{V0};<#it{R}>", kTProfile2D, {axisConfigurations.axisJetPt, {2, -0.9, 0.9}}); + + // Rasterizing, only for LeadP the TProfile2D into two TProfile 1Ds (easier to draw with "same") + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + // V0 eta: + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + + // Looking at V0Eta and JetEta combinations (only for LeadP): + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0, #eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_PosEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0, #eta_{V0}>0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_PosEtaLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}>0, #eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + histos.add((folder + "/ProxyPtDependence/pRingVsPtLeadP_NegEtaLeadP_NegEtaV0").c_str(), "<#it{R}> vs LeadP #it{p}_{T} (#eta_{LeadP}<0, #eta_{V0}<0);#it{p}_{T}^{LeadP} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisJetPt}); + + // Understanding eta dependence seen in pRingEtaCuts: + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda").c_str(), "<#it{R}> vs #eta_{#Lambda};#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaJet").c_str(), "<#it{R}> vs #eta_{Jet};#eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + + histos.add((folder + "/EtaDependence/pRingObservableEtaLambda2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} (SubJet);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEta2ndJet").c_str(), "<#it{R}> vs #eta_{SubJet};#eta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); + + histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} (LeadP);#eta_{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisV0EtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLeadP").c_str(), "<#it{R}> vs #eta_{LeadP};#eta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisEtaCoarse}); // For the leading particle: - histos.add((folder + "/pRingObservableLeadPDeltaPhi").c_str(), "pRingObservableLeadPDeltaPhi;#Delta#varphi_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pRingObservableLeadPDeltaTheta").c_str(), "pRingObservableLeadPDeltaTheta;#Delta#theta_{leadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/pRingObservableLeadPIntegrated").c_str(), "pRingObservableLeadPIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); - histos.add((folder + "/pRingObservableLeadPLambdaPt").c_str(), "pRingObservableLeadPLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + histos.add((folder + "/hRingObservableLeadPCounts").c_str(), "Counts vs <#it{R}>_{LeadP};<#it{R}>; Counts", kTH1D, {axisConfigurations.axisRingCounts}); + histos.add((folder + "/pRingObservableLeadPDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{LeadP};#Delta#varphi_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pRingObservableLeadPDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{LeadP};#Delta#theta_{LeadP};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/pRingObservableLeadPIntegrated").c_str(), "Integrated <#it{R}> (LeadP); ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); + histos.add((folder + "/pRingObservableLeadPLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda} (LeadP);#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); // For the second-to-leading jet: - histos.add((folder + "/pRingObservable2ndJetDeltaPhi").c_str(), "pRingObservable2ndJetDeltaPhi;#Delta#varphi_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); - histos.add((folder + "/pRingObservable2ndJetDeltaTheta").c_str(), "pRingObservable2ndJetDeltaTheta;#Delta#theta_{2ndJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); - histos.add((folder + "/pRingObservable2ndJetIntegrated").c_str(), "pRingObservable2ndJetIntegrated; ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); - histos.add((folder + "/pRingObservable2ndJetLambdaPt").c_str(), "pRingObservable2ndJetLambdaPt;#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + histos.add((folder + "/hRingObservable2ndJetCounter").c_str(), "Counts vs <#it{R}>_{SubJet};<#it{R}>; Counts", kTH1D, {axisConfigurations.axisRingCounts}); + histos.add((folder + "/pRingObservable2ndJetDeltaPhi").c_str(), "<#it{R}> vs #Delta#varphi_{SubJet};#Delta#varphi_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add((folder + "/pRingObservable2ndJetDeltaTheta").c_str(), "<#it{R}> vs #Delta#theta_{SubJet};#Delta#theta_{SubJet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaTheta}); + histos.add((folder + "/pRingObservable2ndJetIntegrated").c_str(), "Integrated <#it{R}> (SubJet); ;<#it{R}>", kTProfile, {{1, -0.5, 0.5}}); + histos.add((folder + "/pRingObservable2ndJetLambdaPt").c_str(), "<#it{R}> vs #it{p}_{T}^{#Lambda} (SubJet);#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile, {axisConfigurations.axisPt}); + + // For the Zvtx dependence: + histos.add((folder + "/pRingObservableLeadJetPVz").c_str(), "<#it{R}>_{LeadJet} vs PVz;PVz (cm);<#it{R}>", kTProfile, {axisConfigurations.axisPVz}); + histos.add((folder + "/pRingObservableSubLeadPVz").c_str(), "<#it{R}>_{SubLead} vs PVz;PVz (cm);<#it{R}>", kTProfile, {axisConfigurations.axisPVz}); + histos.add((folder + "/pRingObservableLeadPPVz").c_str(), "<#it{R}>_{LeadP} vs PVz;PVz (cm);<#it{R}>", kTProfile, {axisConfigurations.axisPVz}); // =============================== // 2D TProfiles (Lambda correlations) // =============================== - histos.add((folder + "/p2dRingObservableDeltaPhiVsLambdaPt").c_str(), "p2dRingObservableDeltaPhiVsLambdaPt;#Delta#varphi_{jet};#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); - histos.add((folder + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str(), "p2dRingObservableDeltaThetaVsLambdaPt;#Delta#theta_{jet};#it{p}_{T}^{#Lambda};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); + histos.add((folder + "/p2dRingObservableDeltaPhiVsLambdaPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisPt}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsLambdaPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs #it{p}_{T}^{#Lambda};#Delta#theta_{jet};#it{p}_{T}^{#Lambda} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisPt}); // =============================== // 2D TProfiles (Jet correlations) // =============================== - histos.add((folder + "/p2dRingObservableDeltaPhiVsLeadJetPt").c_str(), "p2dRingObservableDeltaPhiVsLeadJetPt;#Delta#varphi_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); - histos.add((folder + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "p2dRingObservableDeltaThetaVsLeadJetPt;#Delta#theta_{jet};#it{p}_{T}^{lead jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); + histos.add((folder + "/p2dRingObservableDeltaPhiVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Lead Jet #it{p}_{T};#Delta#varphi_{jet};#it{p}_{T}^{LeadJet} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisJetPt}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Lead Jet #it{p}_{T};#Delta#theta_{jet};#it{p}_{T}^{LeadJet} (GeV/c);<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisJetPt}); // =============================== // Multi-dimensional histograms for signal extraction // (Mass-dependent polarization extraction) // =============================== // Simple invariant mass plot for QA: - histos.add((folder + "/QA/hMass").c_str(), "hMass", kTH1D, {axisConfigurations.axisLambdaMass}); - histos.add((folder + "/hMassSigExtract").c_str(), "hMassSigExtract", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/hMass").c_str(), "#Lambda Mass;m_{p#pi} (GeV/c^{2});Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add((folder + "/hMassSigExtract").c_str(), "#Lambda Mass (Sig Extract);m_{p#pi} (GeV/c^{2});Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); // 1D Mass dependence of observable numerator: - histos.add((folder + "/QA/hRingObservableNumMass").c_str(), "hRingObservableNumMass", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/hRingObservableNumMass").c_str(), "Ring Observable Numerator vs Mass;m_{p#pi} (GeV/c^{2});Counts", kTH1D, {axisConfigurations.axisLambdaMassSigExtract}); // --- 2D counters: Angle vs Mass vs --- - histos.add((folder + "/QA/h2dDeltaPhiVsMass").c_str(), "h2dDeltaPhiVsMass", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/QA/h2dDeltaThetaVsMass").c_str(), "h2dDeltaThetaVsMass", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/h2dDeltaPhiVsMass").c_str(), "#Delta#varphi_{jet} vs Mass;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2})", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/QA/h2dDeltaThetaVsMass").c_str(), "#Delta#theta_{jet} vs Mass;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2})", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); // --- 3D counters: Angle vs Mass vs Lambda pT --- - histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "h3dDeltaPhiVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); - histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "h3dDeltaThetaVsMassVsLambdaPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLambdaPt").c_str(), "#Delta#varphi_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLambdaPt").c_str(), "#Delta#theta_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- 3D counters: Angle vs Mass vs Lead Jet pT --- - histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "h3dDeltaPhiVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); - histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "h3dDeltaThetaVsMassVsLeadJetPt", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaPhiVsMassVsLeadJetPt").c_str(), "#Delta#varphi_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/QA/h3dDeltaThetaVsMassVsLeadJetPt").c_str(), "#Delta#theta_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // =============================== // TProfiles vs Mass: quick glancing before signal extraction // =============================== - // TProfile of ring vs mass (integrated in all phi, and properly normalized by N_\Lambda): - histos.add((folder + "/pRingObservableMass").c_str(), "pRingObservableMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/pRingObservableLeadPMass").c_str(), "pRingObservableLeadPMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); - histos.add((folder + "/pRingObservable2ndJetMass").c_str(), "pRingObservable2ndJetMass;m_{p#pi};<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); + // TProfile of ring vs mass (integrated in all phi, and properly normalized by N_Lambda): + histos.add((folder + "/pRingObservableMass").c_str(), "<#it{R}> vs Mass;m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/pRingObservableLeadPMass").c_str(), "<#it{R}> vs Mass (LeadP);m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/pRingObservable2ndJetMass").c_str(), "<#it{R}> vs Mass (SubJet);m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile, {axisConfigurations.axisLambdaMassSigExtract}); // TProfile2D: vs Mass (DeltaPhi) - histos.add((folder + "/p2dRingObservableDeltaPhiVsMass").c_str(), "p2dRingObservableDeltaPhiVsMass;#Delta#varphi;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/p2dRingObservableDeltaPhiVsMass").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract}); // TProfile2D: vs Mass (DeltaTheta) - histos.add((folder + "/p2dRingObservableDeltaThetaVsMass").c_str(), "p2dRingObservableDeltaThetaVsMass;#Delta#theta;m_{p#pi};<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + histos.add((folder + "/p2dRingObservableDeltaThetaVsMass").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});<#it{R}>", kTProfile2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract}); + // TProfile2D: vs Eta Lambda vs Eta Jet (Understanding eta dependence seen in pRingEtaCuts) + histos.add((folder + "/EtaDependence/hCounterEtaLambdaMinusEtaJet").c_str(), "N_{V0s} vs #eta_{#Lambda} - #eta_{Jet};#eta_{#Lambda} - #eta_{Jet}; N_{V0s}", kTH1D, {axisConfigurations.axisDeltaEtaCoarse}); + histos.add((folder + "/EtaDependence/pRingObservableEtaLambdaMinusEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} - #eta_{Jet};#eta_{#Lambda} - #eta_{Jet};<#it{R}>", kTProfile, {axisConfigurations.axisDeltaEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaJet_FineBins").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{Jet} (fine bins);#eta_{#Lambda};#eta_{Jet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0Eta, axisConfigurations.axisEta}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEtaLeadP").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/p2dRingObservableEtaLambdaVsEta2ndJet").c_str(), "<#it{R}> vs #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};<#it{R}>", kTProfile2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + // Counters for these histograms, instead of only TProfile2Ds: + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaJet_FineBins").c_str(), "Counts (fine bins), #eta_{#Lambda} vs #eta_{Jet};#eta_{#Lambda};#eta_{Jet};Counts", kTH2D, {axisConfigurations.axisV0Eta, axisConfigurations.axisEta}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEtaLeadP").c_str(), "Counts, #eta_{#Lambda} vs #eta_{LeadP};#eta_{#Lambda};#eta_{LeadP};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add((folder + "/EtaDependence/h2dCounterEtaLambdaVsEta2ndJet").c_str(), "Counts, #eta_{#Lambda} vs #eta_{SubJet};#eta_{#Lambda};#eta_{SubJet};Counts", kTH2D, {axisConfigurations.axisV0EtaCoarse, axisConfigurations.axisEtaCoarse}); // --- TProfile3D: vs DeltaPhi vs Mass vs LambdaPt --- - histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaPhiVsMassVsLambdaPt;#Delta#varphi;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLambdaPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- TProfile3D: vs DeltaTheta vs Mass vs LambdaPt --- - histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "p3dRingObservableDeltaThetaVsMassVsLambdaPt;#Delta#theta;m_{p#pi};p_{T}^{#Lambda};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLambdaPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass vs #it{p}_{T}^{#Lambda};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{#Lambda} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisPtSigExtract}); // --- TProfile3D: vs DeltaPhi vs Mass vs LeadJetPt --- - histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "p3dRingObservableDeltaPhiVsMassVsLeadJetPt;#Delta#varphi;m_{p#pi};p_{T}^{jet};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // --- TProfile3D: vs DeltaTheta vs Mass vs LeadJetPt --- - histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "p3dRingObservableDeltaThetaVsMassVsLeadJetPt;#Delta#theta;m_{p#pi};p_{T}^{jet};<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsLeadJetPt").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass vs Lead Jet #it{p}_{T};#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});#it{p}_{T}^{LeadJet} (GeV/c)", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisJetPtSigExtract}); // =============================== // Mass histograms with centrality // =============================== // Counters - histos.add((folder + "/QA/h3dDeltaPhiVsMassVsCent").c_str(), "h3dDeltaPhiVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); - histos.add((folder + "/QA/h3dDeltaThetaVsMassVsCent").c_str(), "h3dDeltaThetaVsMassVsCent", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/QA/h3dDeltaPhiVsMassVsCent").c_str(), "#Delta#varphi_{jet} vs Mass vs Centrality;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTH3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/QA/h3dDeltaThetaVsMassVsCent").c_str(), "#Delta#theta_{jet} vs Mass vs Centrality;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTH3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // Useful TProfiles: // --- TProfile1D: Integrated vs Centrality: - histos.add((folder + "/pRingIntVsCentrality").c_str(), "pRingIntVsCentrality; Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); + histos.add((folder + "/pRingVsCentrality").c_str(), "<#it{R}> vs Centrality;Centrality (%);<#it{R}>", kTProfile, {axisConfigurations.axisCentrality}); // --- TProfile2D: vs Mass vs Centrality --- - histos.add((folder + "/p2dRingObservableMassVsCent").c_str(), "p2dRingObservableMassVsCent;m_{p#pi};Centrality;<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/p2dRingObservableMassVsCent").c_str(), "<#it{R}> vs Mass vs Centrality;m_{p#pi} (GeV/c^{2});Centrality (%);<#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // --- TProfile3D: vs DeltaPhi vs Mass vs Centrality --- - histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "p3dRingObservableDeltaPhiVsMassVsCent;#Delta#varphi;m_{p#pi};Centrality;<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/p3dRingObservableDeltaPhiVsMassVsCent").c_str(), "<#it{R}> vs #Delta#varphi_{jet} vs Mass vs Centrality;#Delta#varphi_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // --- TProfile3D: vs DeltaTheta vs Mass vs Centrality --- - histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsCent").c_str(), "p3dRingObservableDeltaThetaVsMassVsCent;#Delta#theta;m_{p#pi};Centrality;<#it{R}>", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); + histos.add((folder + "/p3dRingObservableDeltaThetaVsMassVsCent").c_str(), "<#it{R}> vs #Delta#theta_{jet} vs Mass vs Centrality;#Delta#theta_{jet};m_{p#pi} (GeV/c^{2});Centrality (%)", kTProfile3D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisCentrality}); // =============================== // QA histograms - Useful numbers @@ -382,31 +769,390 @@ struct lambdajetpolarizationionsderived { // Added to a separate folder for further control (changed the usage of the "folder" string): // histos.add(("QA_Numbers/" + folder + "/hValidLeadJets").c_str(), "hValidLeadJets", kTH1D, {{1,0,1}}); // TODO: Add "frequency of jets per pT" histograms either here or in the TableProducer + + // Estimating error bars with the Delta Method for = A/B: + // 1D Delta Method for Integrated observable: + histos.add((folder + "/DeltaMethod/hIntegrated").c_str(), "Delta Method Accumulators Integrated;Component;Counts", kTH1D, {axisConfigurations.axisDeltaComponents}); + + // 2D Delta Method for Differentials + histos.add((folder + "/DeltaMethod/h2dDeltaThetaVsDeltaComp").c_str(), "Delta Method vs #Delta#theta_{jet};#Delta#theta_{jet};Component", kTH2D, {axisConfigurations.axisDeltaTheta, axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/h2dLambdaPtVsDeltaComp").c_str(), "Delta Method vs #Lambda #it{p}_{T};#it{p}_{T}^{#Lambda} (GeV/c);Component", kTH2D, {axisConfigurations.axisPt, axisConfigurations.axisDeltaComponents}); + histos.add((folder + "/DeltaMethod/h2dMassVsDeltaComp").c_str(), "Delta Method vs Mass;m_{p#pi} (GeV/c^{2});Component", kTH2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaComponents}); }; // Execute local lambda to register histogram families: - addRingObservableFamily("Ring"); - addRingObservableFamily("RingKinematicCuts"); - addRingObservableFamily("JetKinematicCuts"); - addRingObservableFamily("JetAndLambdaKinematicCuts"); + if (familySwitches.doFamilyRing) + addRingObservableFamily("Ring"); + if (familySwitches.doFamilyRingKinematicCuts) + addRingObservableFamily("RingKinematicCuts"); + if (familySwitches.doFamilyJetKinematicCuts) + addRingObservableFamily("JetKinematicCuts"); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) + addRingObservableFamily("JetAndLambdaKinematicCuts"); + + histos.add("IntegratedCuts/pRingCuts", "pRingCuts; ;<#it{R}>", kTProfile, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); // (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(3, "|Jet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCuts"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); - histos.add("pRingCuts", "pRingCuts; ;<#it{R}>", kTProfile, {{4, 0, 4}}); - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); // (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(3, "|Jet_{#eta}|<0.5"); - histos.get(HIST("pRingCuts"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); + // Same for subleading jet and leading particle: + histos.add("IntegratedCuts/pRingCutsSubLeadingJet", "pRingCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "p_{T,#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "|SubJet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + SubJet cuts"); + + histos.add("IntegratedCuts/pRingCutsLeadingP", "pRingCutsLeadingP; ;<#it{R}>", kTProfile, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + + // Counters for each case to understand statistics loss: + histos.add("IntegratedCuts/hCountCuts", "hCountCuts; ;N V0s", kTH1D, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); // (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(3, "|Jet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCuts"))->GetXaxis()->SetBinLabel(4, "#Lambda + Jet cuts"); // Same for subleading jet and leading particle: - histos.add("pRingCutsSubLeadingJet", "pRingCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{4, 0, 4}}); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "p_{T,#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "|SubJet_{#eta}|<0.5"); - histos.get(HIST("pRingCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + SubJet cuts"); - - histos.add("pRingCutsLeadingP", "pRingCutsLeadingP; ;<#it{R}>", kTProfile, {{4, 0, 4}}); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); - histos.get(HIST("pRingCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + histos.add("IntegratedCuts/hCountCutsSubLeadingJet", "hCountCutsSubLeadingJet; ;N V0s", kTH1D, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "p_{T,#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "|SubJet_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#Lambda + SubJet cuts"); + + histos.add("IntegratedCuts/hCountCutsLeadingP", "hCountCutsLeadingP; ;N V0s", kTH1D, {{4, 0, 4}}); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "p_{T}^{#Lambda}@[0.5,1.5],|y_{#Lambda}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "|LeadP_{#eta}|<0.5"); + histos.get(HIST("IntegratedCuts/hCountCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#Lambda + LeadP cuts"); + + // Fake-polarization diagnostics: + if (qaSwitches.doFakePolDiagnosticsQA) { + // Integrated observable dependent on jet proxy #eta to unfold possible asymmetries in detector: + histos.add("EtaStudy/pRingEtaCuts", "pRingEtaCuts; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(10, "#eta_{Jet} > R"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(11, "#eta_{Jet} < -R"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(12, "#eta_{Jet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(13, "#eta_{Jet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(14, "#eta_{Jet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCuts"))->GetXaxis()->SetBinLabel(15, "#eta_{Jet} < -R, #eta_{#Lambda} < 0"); + + histos.add("EtaStudy/pRingEtaCutsSubLeadingJet", "pRingEtaCutsSubLeadingJet; ;<#it{R}>", kTProfile, {{15, 0, 15}}); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(2, "#eta_{SubJet} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(3, "#eta_{SubJet} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(6, "#eta_{SubJet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(7, "#eta_{SubJet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(8, "#eta_{SubJet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(9, "#eta_{SubJet} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(10, "#eta_{SubJet} > R"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(11, "#eta_{SubJet} < -R"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(12, "#eta_{SubJet} > R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(13, "#eta_{SubJet} > R, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(14, "#eta_{SubJet} < -R, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"))->GetXaxis()->SetBinLabel(15, "#eta_{SubJet} < -R, #eta_{#Lambda} < 0"); + + histos.add("EtaStudy/pRingEtaCutsLeadingP", "pRingEtaCutsLeadingP; ;<#it{R}>", kTProfile, {{9, 0, 9}}); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + + // Studying the signal Vs background integral (a very naive estimative of the invariant mass peak) + histos.add("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground", "pRingEtaCutsLeadingP_MassSignalVsBackground; ; ;<#it{R}>", kTProfile2D, {{9, 0, 9}, {2, 0, 2}}); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetXaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(1, "#Lambda out of mass peak"); + histos.get(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"))->GetYaxis()->SetBinLabel(2, "#Lambda in mass peak"); + + // Fake polarization signal QA + // --> The "negative helicity problem", where topologies with a proton decaying opposite to the Lambda momentum are enhanced by + // efficiency of reconstruction. The geometries where the proton moves in the same direction as the boost will have a very small + // momentum pion, which is not as easily detected as the opposite case! This may insert a fake signal of polarization in the measurement! + histos.add("EtaStudy/hFakePolCounts", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCounts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // The same, but for actual signal instead of counts: + histos.add("EtaStudy/pFakePolSignalVsCosTheta", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTProfile2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalVsCosTheta"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Seeing the dependence between phi* = atan2(p_p_star \cdot (p_Lambda_hat \times (z_hat \cross p_Lambda)), p_p_star \cdot (z_hat \cross p_Lambda)) + // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; + // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); + histos.add("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar", "FakePolSignal; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTProfile2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + // Specific counter for when we have leading jets (relates directly to pFakePolSignal_CosThetaVsPhiStar): + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar", "FakePolCounts - HasValidLeadJet OK; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #phi^{*}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaPhi}); + + // Similar split, but for AEE instead of HEE: + histos.add("EtaStudy/hCountsVsPhiStar", "FakePolCounts, AEE dependence; #phi^{*};", kTH2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetZaxis()->SetTitle("Counts"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hCountsVsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + // For the ring observable as well: + // Explicitly, checking the Phi* dependence on a series of #eta cuts. + // The fake, AEE-induced, signal should be zero when integrating on full solid angle, and then have some shape for each eta slice. + histos.add("EtaStudy/pFakePolSignalvsPhiStar", "FakePolSignal, AEE dependence; #phi^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // For the phi_Lambda - phi_D^* dependency as well: + histos.add("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar", "FakePolSignal, AEE dependence; #phi_{#Lambda} - #phi_{p}^{*};", kTProfile2D, {axisConfigurations.axisDeltaPhi, {9, 0, 9}}); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetZaxis()->SetTitle("<#it{R}>"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // More about possible AEE dependencies (should see an invariance with JetEta and /Jz): + // TODO: think about these error bars: do they still make sense via regular TProfile's SEM error? These are just a quick check, so wouldn't bother much about it. + histos.add("HelicityEfficiencyQA/pRingVsJetZcomponent", "<#it{R}> vs #hat{t}_{z}; #hat{t}_{z}; <#it{R}>", kTProfile, {{40, -1, 1}}); // Numerically stable and can also show the sign flip (essentially the <#it{R}> vs Eta Jet plot in another scale) + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE", "<#it{R}>/#hat{t}_{z} Vs cos(#theta) HEE; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisCosTheta}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar", "<#it{R}>/#hat{t}_{z} Vs #phi^{*}; #phi^{*} = atan2(#vec{p}^{*}_{p} #cdot [#hat{p}_{#Lambda} #times (#hat{z} #times #hat{p}_{#Lambda})] , #vec{p}^{*}_{p} #cdot (#hat{z} #times #hat{p}_{#Lambda})); <#it{R}>/#hat{t}_{z}", kTProfile, {axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisCosTheta}); + histos.add("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar", "<#it{R}>/#hat{t}_{z}; #eta_{Jet}; #phi^{*}; <#it{R}>/#hat{t}_{z}", kTProfile2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisDeltaPhi}); + + // Seeing if phi* is indeed influenced by the DCA between daughters: + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{V0 Daughters}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau}); + + // Adding a way to check if the jet eta is positive or negative as well + // (AEE signal could be closer to zero otherwise: phi^* dependency may not make it fall to zero as we are no longer integrating in full solid angle, yet analyzing this other dependency is also important) + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{V0 Daughters}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdau, {2, -0.9, 0.9}}); + + // Similar checks for dcaPosToPV and dcaNegToPV, which influence AEE the strongest: + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{PosPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike", "FakePolSignal; #phi^{*}; DCA_{PosPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{PosPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + + histos.add("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike", "FakePolCounts - HasValidLeadJet OK; #phi^{*}; DCA_{NegPV}", kTH2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike", "FakePolSignal; #phi^{*}; DCA_{NegPV}", kTProfile2D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{Jet} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + histos.add("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda", "FakePolSignal; #phi^{*}; DCA_{NegPV}; #eta_{#Lambda} sign", kTProfile3D, {axisConfigurations.axisDeltaPhi, axisConfigurations.axisDCAdauPV, {2, -0.9, 0.9}}); + + // Doing the same HEE study for leading particles: + // (eta_{Jet} may be a bad estimator!) + histos.add("EtaStudy/hFakePolCountsLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(2, "#eta_{LeadP} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(3, "#eta_{LeadP} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(6, "#eta_{LeadP} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(7, "#eta_{LeadP} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(8, "#eta_{LeadP} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLeadP"))->GetYaxis()->SetBinLabel(9, "#eta_{LeadP} < 0, #eta_{#Lambda} < 0"); + + // Avoid fake signal by jets boosting the Lambda in its own direction, then modifying efficiency of reconstruction in a similar way: + histos.add("EtaStudy/hFakePolCountsLambdaPtCut", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5]; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtCut"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Even stricter cut (also demands rapidity cut stricter than jets, so may see different boosting): + histos.add("EtaStudy/hFakePolCountsLambdaPtYCuts", "FakePol,p_{T}^{#Lambda}#in[0.5,1.5],|y_{#Lambda}|<0.5; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda};", kTH2D, {axisConfigurations.axisCosTheta, {9, 0, 9}}); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetZaxis()->SetTitle("N_{V0s}"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(1, "All #Lambda"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(2, "#eta_{Jet} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(3, "#eta_{Jet} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(4, "#eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(5, "#eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(6, "#eta_{Jet} #geq 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(7, "#eta_{Jet} #geq 0, #eta_{#Lambda} < 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(8, "#eta_{Jet} < 0, #eta_{#Lambda} #geq 0"); + histos.get(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"))->GetYaxis()->SetBinLabel(9, "#eta_{Jet} < 0, #eta_{#Lambda} < 0"); + + // Another useful quantity -- How much is the fake signal related to the jet's momentum (how much the fake signal is correlated with the Jet-Lambda angular separation): + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{Jet}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + histos.add("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; #Delta#theta_{LeadP}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisDeltaTheta}); + + // Understanding the dip at the cos = -1 end: + histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); + histos.add("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP", "FakePolCounts; cos(#theta)=#hat{p}^{*}_{D} . #vec{p}_{#Lambda}; p_{T}^{#Lambda}", kTH2D, {axisConfigurations.axisCosTheta, axisConfigurations.axisPtCoarseQA}); + + // Studying the magnetic field dependence of particle reconstruction efficiency (not magnitude, just sign of field): + // (also for the "negative helicity" problem) + if (analyseMagField) { + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomRight", "hLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft", "hLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight", "hAntiLambdaMassDecayGeomRight; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft", "hAntiLambdaMassDecayGeomLeft; m_{Inv}; Counts", kTH1D, {axisConfigurations.axisLambdaMass}); + } + + // Also including an observable that probes spectrum broadening due to the "Azimuthal Efficiency Effect": + histos.add("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#Lambda}, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "m_{#bar{#Lambda}}, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; Counts", kTH2D, {axisConfigurations.axisLambdaMass, axisConfigurations.axisDeltaPhi}); + // Watching the effect on the ring observable as well: + histos.add("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#Lambda} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); + histos.add("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar", "<#it{R}>, AEE probe; m_{Inv}; #phi_{#bar{#Lambda}} - #phi_{p}^{*} ; <#it{R}>", kTProfile2D, {axisConfigurations.axisLambdaMassSigExtract, axisConfigurations.axisDeltaPhi}); + } // end doFakePolDiagnosticsQA bookings + + // Integrated observable for events with NLambda+NAntiLambda V0s per event + // (an interesting measurement of correlation between and Lambda-like V0s multiplicity. A proxy of covariance) + // (calculated for leading jets only) + histos.add("IntegratedCuts/pRingVsNV0s", "pRingVsNV0s; N_{#Lambda}+N_{#bar{#Lambda}};<#it{R}>", kTProfile, {{20, 0, 20}}); // See hNV0sVsCentrality below for the correlation between number of V0s and centrality + histos.add("hNV0sVsCentrality", "hNV0sVsCentrality; N_{#Lambda}+N_{#bar{#Lambda}};Centrality (%)", kTH2D, {{20, 0, 20}, axisConfigurations.axisCentrality}); + + // Proxy Eta QA: + histos.add("JetKinematicsQA/hLeadJetEta", "hLeadJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hSubLeadJetEta", "hSubLeadJetEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + histos.add("JetKinematicsQA/hLeadPEta", "hLeadPEta;#eta;Counts", kTH1D, {axisConfigurations.axisEta}); + + // Proxy Phi QA: + histos.add("JetKinematicsQA/hLeadJetPhi", "hLeadJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hSubLeadJetPhi", "hSubLeadJetPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + histos.add("JetKinematicsQA/hLeadPPhi", "hLeadPPhi;#varphi;Counts", kTH1D, {axisConfigurations.axisPhi}); + + // Counting the number of jets/proxies themselves (these count at most once per event) -- Similar to the FOLDER/QA/hPtJet counters: + histos.add("JetKinematicsQA/hJetCounterPtJet", "hJetCounterPtJet; p_{T}^{Jet} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hJetCounterPtLeadP", "hJetCounterPtLeadP; p_{T}^{LeadP} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); + histos.add("JetKinematicsQA/hJetCounterPt2ndJet", "hJetCounterPt2ndJet; p_{T}^{SubJet} (GeV/c)", kTH1D, {axisConfigurations.axisJetPt}); + + // Proxy Eta vs PVz: + histos.add("JetKinematicsQA/h2dLeadJetEtaVsPVz", "Lead Jet #eta Vs PVz;#eta;Primary Vertex Z [cm];Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVz}); + histos.add("JetKinematicsQA/h2dSubLeadJetEtaVsPVz", "SubLead Jet #eta Vs PVz;#eta;Primary Vertex Z [cm];Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVz}); + histos.add("JetKinematicsQA/h2dLeadPEtaVsPVz", "Lead Ptc #eta Vs PVz;#eta;Primary Vertex Z [cm];Counts", kTH2D, {axisConfigurations.axisEta, axisConfigurations.axisPVz}); + + // For building and event-mixing-like procedure similar to forceDatalikeJet: + if (doJetProxy5dQA) { + histos.add("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent", "h5dLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + histos.add("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent", "h5dSubLeadJetEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + histos.add("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent", "h5dLeadPEtaPhiPtPVzCent;#eta;#phi;p_{t};Primary Vertex Z [cm];Centrality (%);Counts", kTHnSparseF, + {axisConfigurations.axisEtaCoarse, axisConfigurations.axisPhi, axisConfigurations.axisJetPt, axisConfigurations.axisPVz, axisConfigurations.axisCentrality}); + } + + // doMixedEventProxies QA: gauge the size of the "too few collisions per bin" problem (see resonanceMergeDF.cxx) + // Booked per proxy, since the three mixings are independent and can succeed/fail at different rates. + if (fakePolSwitches.doMixedEventProxies) { + histos.add("EventMixingQA/hMixedEventLeadPOutcome", "hMixedEventLeadPOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("EventMixingQA/hMixedEventLeadJetOutcome", "hMixedEventLeadJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + histos.add("EventMixingQA/hMixedEventSubJetOutcome", "hMixedEventSubJetOutcome;Outcome (0=skipped, 1=found);Counts", kTH1D, {{2, -0.5f, 1.5f}}); + + histos.add("EventMixingQA/hMixedEventLeadPWindowNeighbours", "hMixedEventLeadPWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + histos.add("EventMixingQA/hMixedEventLeadJetWindowNeighbours", "hMixedEventLeadJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + histos.add("EventMixingQA/hMixedEventSubJetWindowNeighbours", "hMixedEventSubJetWindowNeighbours;Neighbours found in bin window;Counts", kTH1D, {{22, -0.5f, 21.5f}}); + + histos.add("EventMixingQA/h2dMixedLeadPEtaVsLeadPEta", "MixedLeadP #eta vs LeadP #eta;MixedLeadP #eta; LeadP #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/h2dMixedLeadPPhiVsLeadPPhi", "MixedLeadP #phi vs LeadP #phi;MixedLeadP #phi; LeadP #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + histos.add("EventMixingQA/h2dMixedLeadJetEtaVsLeadJetEta", "MixedLeadJet #eta vs LeadJet #eta;MixedLeadJet #eta; LeadJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/h2dMixedLeadJetPhiVsLeadJetPhi", "MixedLeadJet #phi vs LeadJet #phi;MixedLeadJet #phi; LeadJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + histos.add("EventMixingQA/h2dMixedSubJetEtaVsSubJetEta", "MixedSubJet #eta vs SubJet #eta;MixedSubJet #eta; SubJet #eta;Counts", kTH2D, {axisConfigurations.axisEtaCoarse, axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/h2dMixedSubJetPhiVsSubJetPhi", "MixedSubJet #phi vs SubJet #phi;MixedSubJet #phi; SubJet #phi;Counts", kTH2D, {axisConfigurations.axisPhi, axisConfigurations.axisPhi}); + + // Collision-index proximity QA: + // (To understand possible continous readout effects on the choice of event mixing sources -- do notice index and time are both monotonic, but there is a conversion factor) + // The shape of the whole candidate pool: + histos.add("EventMixingQA/hMixedEventLeadPDeltaIndexEligible", "hMixedEventLeadPDeltaIndexEligible;#Delta collision index (pair);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventLeadJetDeltaIndexEligible", "hMixedEventLeadJetDeltaIndexEligible;#Delta collision index (pair);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventSubJetDeltaIndexEligible", "hMixedEventSubJetDeltaIndexEligible;#Delta collision index (pair);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + + // What the reservoir actually picked: + // (should be equally as narrow as the whole candidate pool, if no preferential mixing exists) + histos.add("EventMixingQA/hMixedEventLeadPDeltaIndexSelected", "hMixedEventLeadPDeltaIndexSelected;#Delta collision index (selected partner);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventLeadJetDeltaIndexSelected", "hMixedEventLeadJetDeltaIndexSelected;#Delta collision index (selected partner);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + histos.add("EventMixingQA/hMixedEventSubJetDeltaIndexSelected", "hMixedEventSubJetDeltaIndexSelected;#Delta collision index (selected partner);Counts", kTH1D, {axisConfigurations.axisDeltaCollisionIndex}); + + // Event mixing source QA -- How repeated is each collision in the mix: + // For each source collision for mixing, counts how many times it was used. Flat distribution is ideal. + histos.add("EventMixingQA/hMixedEventLeadPSourceUsageCount", "hMixedEventLeadPSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + histos.add("EventMixingQA/hMixedEventLeadJetSourceUsageCount", "hMixedEventLeadJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + histos.add("EventMixingQA/hMixedEventSubJetSourceUsageCount", "hMixedEventSubJetSourceUsageCount;Times collision was used;Counts", kTH1D, {{50, -0.5f, 49.5f}}); + // Useful TProfiles -- the mean number of times a given source was used, as a function of eta or phi + // (more convenient than a single TH1, as it gives an average, not the raw counter) + histos.add("EventMixingQA/pMixedEventLeadPSourceUsageVsEta", "pMixedEventLeadPSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/pMixedEventLeadPSourceUsageVsPhi", "pMixedEventLeadPSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + histos.add("EventMixingQA/pMixedEventLeadJetSourceUsageVsEta", "pMixedEventLeadJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/pMixedEventLeadJetSourceUsageVsPhi", "pMixedEventLeadJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + histos.add("EventMixingQA/pMixedEventSubJetSourceUsageVsEta", "pMixedEventSubJetSourceUsageVsEta;Source #eta;", kTProfile, {axisConfigurations.axisEtaCoarse}); + histos.add("EventMixingQA/pMixedEventSubJetSourceUsageVsPhi", "pMixedEventSubJetSourceUsageVsPhi;Source #varphi;", kTProfile, {axisConfigurations.axisPhi}); + } + + // Fetch the X-axes from one of the families (since they all share the same ConfigurableAxis binning) + mAxisPt = histos.get(HIST("Ring/DeltaMethod/h2dLambdaPtVsDeltaComp"))->GetXaxis(); + mAxisMass = histos.get(HIST("Ring/DeltaMethod/h2dMassVsDeltaComp"))->GetXaxis(); + mAxisDTheta = histos.get(HIST("Ring/DeltaMethod/h2dDeltaThetaVsDeltaComp"))->GetXaxis(); + for (auto const& tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) + tracker->resize(mAxisPt->GetNbins(), mAxisMass->GetNbins(), mAxisDTheta->GetNbins()); } // Helper to get centrality (same from TableProducer, thanks to templating!): @@ -424,346 +1170,1241 @@ struct lambdajetpolarizationionsderived { // Initializing a random number generator for the worker (for perpendicular-to-jet direction QAs): TRandom3 randomGen{0}; // 0 means we auto-seed from machine entropy. This is called once per device in the pipeline, so we should not see repeated seeds across workers + std::mt19937 rng{std::random_device{}()}; + + // Pre-computed values for helper below: + const double smearSigma = 0.05 * jetR; + // Normalized eta weights spanning -0.9 to 0.9 (46 bins) and phi weights for the 50 bins spanning [0, 2pi]: + static constexpr std::array etaLeadPWeights = {{0.01782505198123039, 0.01826119427561306, 0.01890047073124532, 0.01942224199989093, 0.01993380780273602, 0.02047274597515178, 0.02094135547756474, 0.02140259654932778, 0.02178490245182078, 0.02218346916434517, 0.02252861343224298, 0.02278214932340838, 0.02297395476452691, 0.02311861709583109, 0.02322295246943318, 0.02329274166449468, 0.02335344516182264, 0.02335971904087711, 0.02340163522424806, 0.02352868368676468, 0.02345839093195849, 0.02295391718536531, 0.02306543716698383, 0.02293131780181040, 0.02265098126991631, 0.02318893563623931, 0.02322457978177088, 0.02316188601954564, 0.02308812992419636, 0.02305831097751334, 0.02300695504254397, 0.02296398287895598, 0.02286956017362988, 0.02274321888486162, 0.02254049413132752, 0.02233024817234042, 0.02204811997596179, 0.02170252191737713, 0.02130517220864903, 0.02086349641950970, 0.02036590755841183, 0.01987946074337928, 0.01934550418314029, 0.01879487530409137, 0.01812577211265362, 0.01766945805710696}}; + static constexpr std::array phiLeadPWeights = {{0.01907529231698144, 0.02044679008716434, 0.01948618554713157, 0.02046288887443206, 0.02142057576726765, 0.01961361841611185, 0.02174981627752354, 0.02160945937846856, 0.02027231236207667, 0.02153799273983672, 0.02107609996106984, 0.02001899849606885, 0.02196516947939817, 0.02047654705787587, 0.02059561382369167, 0.02148625027035289, 0.02001510925188416, 0.02183059661361331, 0.02111406548114694, 0.01881826666371129, 0.02112797285609031, 0.02034071592473790, 0.01968993216337670, 0.02126946766383166, 0.02025580366897253, 0.02061136834815962, 0.02083881238552183, 0.01994368379135331, 0.02046280212696892, 0.02131631148368759, 0.01967275608960357, 0.02064965975476278, 0.02155758091535052, 0.02012557837329328, 0.02084718400924722, 0.02065094443305587, 0.01969546187428681, 0.02136531489392276, 0.02084491659074202, 0.01970883494847568, 0.02080349992636310, 0.02098440611808174, 0.02159984658204099, 0.02045819959592145, 0.01952755742791563, 0.02166002908586709, 0.02017512590511229, 0.01932658087972190, 0.02108933627170306, 0.02019288778648293}}; + // Build discrete eta distribution for sampling: + std::discrete_distribution etaLeadPDist{etaLeadPWeights.begin(), etaLeadPWeights.end()}; // Will be passed as the etaDist variable + std::discrete_distribution phiLeadPDist{phiLeadPWeights.begin(), phiLeadPWeights.end()}; + + /// \brief One jet proxy (leadP, leadJet or subJet). Bound by reference so applyProxyDistortion() edits in-place. + struct ProxyState { + bool& hasValidProxy; //! whether the proxy is valid. Re-evaluated against minPtThreshold after distortion + float& pt; + float& eta; + float& phi; + XYZVector& unitVec; //! the proxy direction as a unit vector + }; + + /// \brief The cache slots shared by forcePreviousJet and doMixedEventProxies, also bound by reference. + /// \note Fallback skips this event using ProxyState::hasValidProxy. + /// \note forcePreviousJet updates these here for the next collision, in a simplistic event event mixing approach. + // doMixedEventProxies instead overwrites the caller's cache fields with this collision's mixed proxy right before the call. + struct ProxyCacheRef { + bool& hadProxy; + float& eta; + float& phi; + }; + + /// \brief Applies whichever fakePolSwitches distortion is active to a jet-proxy direction, in place. No-op if none are on. + /// \param proxy input/edited in-place: the proxy's kinematics, overwritten by whichever distortion is active. + /// \param minPtThreshold pT threshold for re-evaluating hasValidProxy after distortion. + /// \param cache input/edited in-place: the previous-jet/mixed-event caches. + /// \param etaDist,phiDist,rng sampling distributions/generator for forceRandJet and forceDatalikeJet. + /// \note Shared across leadP/leadJet/subJet: the caller resolves which proxy-specific procedure (e.g., LUT for evtMixing) applies. + // Helper to modify the jet direction for QA and for spurious signal baseline removal tests: + inline void applyProxyDistortion(ProxyState proxy, float minPtThreshold, ProxyCacheRef cache, + std::discrete_distribution& etaDist, std::discrete_distribution& phiDist, std::mt19937& rng) + { + if (!fakePolSwitches.forcePerpToJet && !fakePolSwitches.forceJetDirectionSmudge && !fakePolSwitches.forceRandJet && !fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet && !fakePolSwitches.doMixedEventProxies) [[likely]] { + return; // Skip this function if none of the modifications are actually being executed! + } + + // QA block -- Purposefully changing the jet direction (should kill signal, if any): + if (fakePolSwitches.forcePerpToJet) { + // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet + XYZVector perpVec; + if (std::abs(proxy.unitVec.X()) > 0.99) { + perpVec = XYZVector(-proxy.unitVec.Z(), 0., proxy.unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) + } else { + perpVec = XYZVector(0., proxy.unitVec.Z(), -proxy.unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) + } + + // Now we rotate around the jet axis by a random angle, just to make sure we are not introducing a bias in the QA: + // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) + const double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); + proxy.unitVec = perpVec * std::cos(randomAngle) + proxy.unitVec.Cross(perpVec) * std::sin(randomAngle); + } else if (fakePolSwitches.forceJetDirectionSmudge) { + // Smear the jet direction by a small random angle to estimate sensitivity to + // jet axis uncertainty. We rotate the jet axis by angle theta around a uniformly + // random perpendicular axis -- this is isotropic and coordinate-independent, + // unlike smearing eta and phi separately (which would break azimuthal symmetry + // around the jet axis and depend on where in eta the jet sits). + + // 1) We pick a uniformly random axis perpendicular to the jet. + // (re-using the same Rodrigues formula as in the forcePerpToJet block above) + XYZVector perpVec; + if (std::abs(proxy.unitVec.X()) > 0.99) { + perpVec = XYZVector(-proxy.unitVec.Z(), 0., proxy.unitVec.X()).Unit(); // Cross product with Y-axis (0, 1, 0) + } else { + perpVec = XYZVector(0., proxy.unitVec.Z(), -proxy.unitVec.Y()).Unit(); // Cross product with X-axis (1, 0, 0) + } + + // Rotate perpVec around the jet axis by a uniform random azimuth to get + // a uniformly distributed random perpendicular direction (the smear axis): + const double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); + XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + proxy.unitVec.Cross(perpVec) * std::sin(smearAzimuth); + + // 2) draw the smearing polar angle from a Gaussian: + // sigma = 0.05 * R --> ~68% of events smeared within 5% of R, + // ~95% of events smeared within 10% of R, + // ~5% see a displacement > 0.1*R (a very "badly determined jet", for our QA purposes) + // std::abs() folds the symmetric Gaussian onto a half-normal ([0, inf)) + // -- R is not really an angle: just gives me a scale for the angular shift I am performing. + // -- This may pose problems for forward jets: a small displacement in \theta becomes a large displacement in \eta space + const double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); + + // 3) rotate the jet axis by smearAngle around smearAxis. + // Rodrigues is v_rot = v*cos(theta) + (k \cross v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) + // But the last term vanishes because smearAxis is perpendicular to unitVec: + proxy.unitVec = proxy.unitVec * std::cos(smearAngle) + smearAxis.Cross(proxy.unitVec) * std::sin(smearAngle); + // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. + } else if (fakePolSwitches.forceRandJet) { + // This randomization was made different for each proxy (LeadP, LeadJet, SubLeadJet): bear that in mind! + // 1) Uniformly sample cos(theta) and phi to ensure an isotropic distribution (could also use TRandom::Sphere as well, but may be slower) + // Notice that uniformly sampling theta would make the distribution non-isotropic, thus we use cos(theta)! + const double cosTheta = randomGen.Uniform(-1., 1.); + const double sinTheta = std::sqrt(1. - cosTheta * cosTheta); + const double randPhi = randomGen.Uniform(0., o2::constants::math::TwoPI); + + // 2) Construct the new random unit vector (there is no need to use the magnitude at all! We only need direction here): + proxy.unitVec = XYZVector(sinTheta * std::cos(randPhi), sinTheta * std::sin(randPhi), cosTheta); + } else if (fakePolSwitches.forcePreviousJet) { // Use the jet direction from the immediately preceding collision. The simplest event mixing + const bool usableProxy = cache.hadProxy; + if (usableProxy) { + const double inverseCoshEta = 1.0 / std::cosh(cache.eta); + const double sinPhi = std::sin(cache.phi); + const double cosPhi = std::cos(cache.phi); + proxy.unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cache.eta)); + } + + // Update cache with the current event data: + cache.hadProxy = proxy.hasValidProxy; + cache.eta = proxy.eta; + cache.phi = proxy.phi; + + // Current event cannot use previous-jet mixing if previous event lacked a proxy + if (!usableProxy) + proxy.hasValidProxy = false; + } else if (fakePolSwitches.forceDatalikeJet) { // A compromise between forceRandJet and forcePreviousJet, using data-like weights for sampling jets + const float etaMin = -0.92f; + const float etaBinWidth = 0.04f; + constexpr float phiBinWidth = constants::math::TwoPI / 50.f; + + // Pick one of the 46 bins according to etaWeights: + const int binEtaIdx = etaDist(rng); + const int binPhiIdx = phiDist(rng); + + // Uniformly smear inside the chosen bin: + proxy.eta = etaMin + etaBinWidth * (binEtaIdx + std::generate_canonical(rng)); + proxy.phi = phiBinWidth * (binPhiIdx + std::generate_canonical(rng)); + + const double inverseCoshEta = 1.0 / std::cosh(proxy.eta); + const double sinPhi = std::sin(proxy.phi); + const double cosPhi = std::cos(proxy.phi); + proxy.unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(proxy.eta)); + } else if (fakePolSwitches.doMixedEventProxies) { + // Mixes this proxy's direction from a real, similar other collision, rather than sampling a fitted distribution as datalikeJet. + // Only unitVec is rebuilt, eta/phi are left as this collision's own real values -- same convention forcePreviousJet uses above. + if (cache.hadProxy) { + const double inverseCoshEta = 1.0 / std::cosh(cache.eta); + const double sinPhi = std::sin(cache.phi); + const double cosPhi = std::cos(cache.phi); + proxy.unitVec = XYZVector(cosPhi * inverseCoshEta, sinPhi * inverseCoshEta, std::tanh(cache.eta)); + } else { + // No mixing partner found for this collision (sparse bins!). + // Same skip that forcePreviousJet does above: an event without a valid mixed proxy cannot be used. + proxy.hasValidProxy = false; + } + } + + // Recalculating pT, phi and eta after distortions: + // (without this, later kinematic selections make no sense at all! In the forceRandJet case, the ring observable would always sum zero) + if (proxy.hasValidProxy) { // If you don't check this flag here, the "if (!usableProxy)" change would be silently overwritten + if (!fakePolSwitches.forcePreviousJet && !fakePolSwitches.forceDatalikeJet) { + // (In these two cases, we already know the eta and phi variables. No need to recompute) + // Calculating total jet momentum, which should be preserved, just to recalculate the new jet Pt: + const double mag = proxy.pt * std::cosh(proxy.eta); + + // For stability (Rho is the projection on the transverse plane, badly behaved for high |eta|): + const double transverseNorm = std::max(proxy.unitVec.Rho(), 1e-12); // Stability guard + + // Recalculate pT: + proxy.pt = mag * transverseNorm; + + // Recalculate phi: + proxy.phi = RecoDecay::constrainAngle(std::atan2(proxy.unitVec.Y(), proxy.unitVec.X()), 0.0f); // atan2 outputs [-PI, PI), and DataModel convention was [0,2PI) as per FastJet's phi() getter + // proxy.phi = std::atan2(proxy.unitVec.Y(), proxy.unitVec.X()); + // if (proxy.phi < 0.0f) + // proxy.phi += o2::constants::math::TwoPI; + + // Stable eta computation: + // Stabler than 0.5 * std::log((1. + cosTheta) / (1. - cosTheta)) + proxy.eta = std::asinh(proxy.unitVec.Z() / transverseNorm); + } + + // Recalculate the bool after distortion to see if we proceed with this jet proxy: + proxy.hasValidProxy = proxy.pt > minPtThreshold; + } + } + + // Caching the previous collision's jet directions -- An optional feature for forcePreviousJet QA: + struct PrevJetCache { + // Leading jet + bool hadLeadJet; + float leadJetEta; + float leadJetPhi; + // Subleading jet + bool hadSubJet; + float subJetEta; + float subJetPhi; + // Leading particle + bool hadLeadP; + float leadPEta; + float leadPPhi; + }; + + // Per-collision leading/subleading jet, computed once per dataframe and shared by both the main loop below + // (instead of re-scanning RingJets once per resampling pass) and the leadJet/subJet event mixing functions: + struct JetProxyCache { + bool hasValidLeadingJet = false; + float leadingJetPt = -1.f; + float leadingJetEta = 0.f; + float leadingJetPhi = 0.f; + bool hasValidSubJet = false; + float subleadingJetPt = -1.f; + float subleadingJetEta = 0.f; + float subleadingJetPhi = 0.f; + }; + + // A simple struct for doMixedEventProxies. Each proxy (leadP/leadJet/subJet) gets its own independent cache. + struct MixedProxyInfo { + float eta; + float phi; + int64_t sourceCollisionId; + }; + + /// \brief Uniform random pick among each target's candidate window, via reservoir sampling + /// (cannibalization of proxies by neighbouring collisions in Continuous Readout is not a worry as ITS hits are being demanded) + /// \note Reuses the task's own rng member rather than a separate generator per proxy. + void reservoirInsert(std::unordered_map& candidateCount, std::unordered_map& lut, + int64_t targetId, const MixedProxyInfo& candidate) + { + int& nSeen = candidateCount[targetId]; + ++nSeen; + std::uniform_int_distribution pick(1, nSeen); + if (pick(rng) == 1) { + lut[targetId] = candidate; + } + } + + /// \brief How many different target collisions each source collision ended up supplying, from a finished LUT. + static std::unordered_map tallySourceUsage(std::unordered_map const& lut) + { + std::unordered_map usage; + for (auto const& kv : lut) + usage[kv.second.sourceCollisionId]++; + return usage; + } + + // Defining filters for events: + Filter zvtxFilter = (nabs(o2::aod::lambdajetpol::zvtx) < maxZVtxPosition); // Preslices for correct collisions association: - // (TODO: test using custom grouping) + // (tested custom grouping and performs worse here) Preslice perColJets = o2::aod::lambdajetpol::ringCollisionId; Preslice perColV0s = o2::aod::lambdajetpol::ringCollisionId; Preslice perColLeadPs = o2::aod::lambdajetpol::ringCollisionId; - void processPolarizationData(o2::aod::RingCollisions const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, + // For doMixedEventProxies: + SliceCache mixCache; + /// \brief Main analysis loop: for each collision, rebuilds the leading jet/particle proxies (with optional fakePolSwitches distortions), then loops over V0s computing the ring observable and polarization-vector profiles for every enabled kinematic-cut family. + void processPolarizationData(soa::Filtered const& collisions, o2::aod::RingJets const& jets, o2::aod::RingLaV0s const& v0s, o2::aod::RingLeadPs const& leadPs) { + // Event mixing, built once per dataframe + // Simplistic event mixing using previous collision: + PrevJetCache prevJetCache{}; // Initializing struct before the collisions loop. This zero-initializes, so bools start as false + // This should not be used along nProxyResamples > 1, as it does not apply to that case. + + // Leading/subleading jet per collision, resolved once here instead of inside the (possibly nProxyResamples times resampled) main loop. + // Both the main loop and the leadJet/subJet mixing functions read from this, so RingJets is scanned exactly once + std::unordered_map jetProxyByCollision; for (auto const& collision : collisions) { - const auto collId = collision.globalIndex(); // The self-index accessor - const double centrality = getCentrality(collision); - - // Slice jets, V0s and leading particle belonging to this collision: - // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) - auto v0sInColl = v0s.sliceBy(perColV0s, collId); - auto leadPsInColl = leadPs.sliceBy(perColLeadPs, collId); - - // Check if there is at least one V0 and one jet in the collision: - // (in the way I fill the table, there is always at least one V0 in - // the stored collision, but the jets table can not be filled for - // that collision, and a collision may not be filled when the jets - // table is. Be mindful of that!) - // 1) Require at least one V0: - if (!v0sInColl.size()) - continue; - - // 2) We require at least a leading particle, then we get the leading jet only if it exists: - // (The goal is to see how diluted the signal gets with events which don't even have a loose FastJet jet) - // (The leading particle is built from all tracks that passed the pseudojet - // selection, so it exists whenever FastJet was run on this collision. - // Events that have a leading jet always have a leading particle too, but - // the converse is not true: events can have a leading particle with no jet - // if no jet survives the pT threshold/the background subtraction) - float leadPPt = -1.; // pT = -1 means "table entry not found for this collision". - float leadPEta = 0.; - float leadPPhi = 0.; - float leadPPx = 0., leadPPy = 0., leadPPz = 0.; - for (auto const& lp : leadPsInColl) { - // Table should contain exactly one entry per collision, - // but we break immediately to be safe: - leadPPt = lp.leadParticlePt(); - leadPEta = lp.leadParticleEta(); - leadPPhi = lp.leadParticlePhi(); - // Using dynamic columns to make code cleaner: - leadPPx = lp.leadParticlePx(); - leadPPy = lp.leadParticlePy(); - leadPPz = lp.leadParticlePz(); - break; - } - // Discard events with no leading particle (FastJet didn't even run in these cases!): - if (leadPPt < 0.) - continue; - - // Build leading particle unit vector, outside the V0 loop for performance: - XYZVector leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); - - // 3) Checking if the event has a leading jet: - auto jetsInColl = jets.sliceBy(perColJets, collId); - float leadingJetPt = -1.; - float subleadingJetPt = -1.; + const auto collId = collision.globalIndex(); + JetProxyCache cache; // std::optional avoids undefined behaviour from a default-constructed iterator: - // (essentially, just protection for when we fetch jetEta() and the such) std::optional leadingJet; std::optional subleadingJet; - for (auto const& jet : jetsInColl) { + for (auto const& jet : jets.sliceBy(perColJets, collId)) { const auto jetpt = jet.jetPt(); - if (jetpt > leadingJetPt) { + if (jetpt > cache.leadingJetPt) { // Current leading becomes subleading: - subleadingJetPt = leadingJetPt; + cache.subleadingJetPt = cache.leadingJetPt; subleadingJet = leadingJet; // may still be std::nullopt on first pass -- that is fine! // Now update the leading jet: - leadingJetPt = jetpt; + cache.leadingJetPt = jetpt; leadingJet = jet; - } else if (jetpt > subleadingJetPt) { // Update subleading only: - subleadingJetPt = jetpt; + } else if (jetpt > cache.subleadingJetPt) { // Update subleading only: + cache.subleadingJetPt = jetpt; subleadingJet = jet; } } + // Finer control on jet momentum, further than TableProducer pre-selection: + cache.hasValidLeadingJet = cache.leadingJetPt > minLeadJetPt; + cache.hasValidSubJet = cache.subleadingJetPt > minSubLeadJetPt; + if (cache.hasValidLeadingJet) { + cache.leadingJetEta = leadingJet->jetEta(); + cache.leadingJetPhi = leadingJet->jetPhi(); + } + if (cache.hasValidSubJet) { + cache.subleadingJetEta = subleadingJet->jetEta(); + cache.subleadingJetPhi = subleadingJet->jetPhi(); + } + jetProxyByCollision[collId] = cache; + } + + // doMixedEventProxies caches: three independent mixings, one per proxy (a collision may have a valid LeadP, but no SubLeadJet). + // Mixing is binned on (Zvtx, proxy pt, centrality), varying only eta/phi. + std::unordered_map mixedLeadPByCollision; + std::unordered_map mixedLeadJetByCollision; + std::unordered_map mixedSubJetByCollision; + // First we build lookup tables based on current dataframe's collisions (connects pairs of jet proxies from similar collisions): + // (these proxies may come from collisions with no valid Lambdas, by construction, enabling more mixes) + // (This is performed out of the resampling loop, so nProxyResamples will not resample event mixing candidates) + if (fakePolSwitches.doMixedEventProxies) { + auto getMixCentrality = [this](o2::aod::RingCollision const& col) { return this->getCentrality(col); }; // Already filtered even though referencing only RingCollision, not an iterator to Filtered table - // Some useful bools to check if we have a leading jet and a subleading jet: - const bool hasValidLeadingJet = leadingJetPt > 0.; - const bool hasValidSubJet = subleadingJetPt > 0.; - - // Build jet vectors (only when the corresponding jet exists): - // Dummy initialisations are safe: all jet-dependent fills are gated on hasValidLeadingJet / hasValidSubJet. - float leadingJetEta = 0.; - float leadingJetPhi = 0.; - XYZVector leadingJetUnitVec(1., 0., 0.); // dummy (overwritten below) - if (hasValidLeadingJet) { - leadingJetEta = leadingJet->jetEta(); - leadingJetPhi = leadingJet->jetPhi(); - // Using internal getters to make code cleaner: - leadingJetUnitVec = XYZVector(leadingJet->jetPx(), leadingJet->jetPy(), leadingJet->jetPz()).Unit(); - - // QA block -- Purposefully changing the jet direction (should kill signal, if any): - if (forcePerpToJet) { // Use modified jet direction (done outside loop to guarantee all V0s inside event use same fake jet) - // First, we build a vector perpendicular to the jet by picking an arbitrary vector not parallel to the jet - XYZVector refVec(1., 0., 0.); - if (std::abs(leadingJetUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - // Now we get a perpendicular vector to the jet direction: - XYZVector perpVec = leadingJetUnitVec.Cross(refVec).Unit(); - // Now we rotate around the jet axis by a random angle, just to make sure we are not introducing a bias in the QA: - // We will use Rodrigues' rotation formula (v_rot = v*cos(randomAngle) + (Jet \cross v)*sin(randomAngle)) - double randomAngle = randomGen.Uniform(0., o2::constants::math::TwoPI); - leadingJetUnitVec = perpVec * std::cos(randomAngle) + leadingJetUnitVec.Cross(perpVec) * std::sin(randomAngle); - } else if (forceJetDirectionSmudge) { - // Smear the jet direction by a small random angle to estimate sensitivity to - // jet axis uncertainty. We rotate the jet axis by angle theta around a uniformly - // random perpendicular axis -- this is isotropic and coordinate-independent, - // unlike smearing eta and phi separately (which would break azimuthal symmetry - // around the jet axis and depend on where in eta the jet sits). - - // 1) We pick a uniformly random axis perpendicular to the jet. - // (re-using the same Rodrigues formula as in the forcePerpToJet block above) - XYZVector refVec(1., 0., 0.); - if (std::abs(leadingJetUnitVec.Dot(refVec)) > 0.99) - refVec = XYZVector(0., 1., 0.); - XYZVector perpVec = leadingJetUnitVec.Cross(refVec).Unit(); - // Rotate perpVec around the jet axis by a uniform random azimuth to get - // a uniformly distributed random perpendicular direction (the smear axis): - double smearAzimuth = randomGen.Uniform(0., o2::constants::math::TwoPI); - XYZVector smearAxis = perpVec * std::cos(smearAzimuth) + leadingJetUnitVec.Cross(perpVec) * std::sin(smearAzimuth); - - // Step 2: draw the smearing polar angle from a Gaussian: - // sigma = 0.05 * R --> ~68% of events smeared within 5% of R, - // ~95% of events smeared within 10% of R, - // ~5% see a displacement > 0.1*R (a very "badly determined jet", for our QA purposes) - // std::abs() folds the symmetric Gaussian onto a half-normal ([0, inf)) - // -- R is not really an angle: just gives me a scale for the angular shift I am performing. - // -- This may pose problems for forward jets: a small displacemente in \theta becomes a large displacement in \eta space - double smearSigma = 0.05 * jetRForSmuding; - double smearAngle = std::abs(randomGen.Gaus(0., smearSigma)); - - // Step 3: rotate the jet axis by smearAngle around smearAxis. - // Rodrigues is v_rot = v*cos(theta) + (k \croos v)*sin(theta) + k*(k \cdot v)*(1-cos(theta)) - // But the last term vanishes because smearAxis is perpendicular to leadingJetUnitVec: - leadingJetUnitVec = leadingJetUnitVec * std::cos(smearAngle) + smearAxis.Cross(leadingJetUnitVec) * std::sin(smearAngle); - // Also, rotation preserves the norm, so no re-normalisation is needed for this to be a unit vector. + // For the leading particle: + // Pattern follows MixedEventsLambdaBinning tutorial (captures leadPs table and cache define in task's struct): + auto getMixLeadPPt = [&leadPs, this](o2::aod::RingCollision const& col) { + auto rows = leadPs.sliceByCached(o2::aod::lambdajetpol::ringCollisionId, col.globalIndex(), this->mixCache); // As it is cached, grouping happens only once + return rows.size() > 0 ? rows.begin().leadParticlePt() : -999.f; + }; + using LeadPBinningType = FlexibleBinningPolicy, + o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadPPt), decltype(getMixCentrality)>; + LeadPBinningType leadPBinning{{getMixLeadPPt, getMixCentrality}, + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; // Ignore overflows true + + // SameKindPair defaults to CombinationsBlockStrictlyUpperSameIndexPolicy, so no same-event mixing should happen: + //(Already filtered by Zvtx even though we call by aod::RingCollisions, so no need to access the filtered table) + SameKindPair leadPPair{ + leadPBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(leadPs), &mixCache}; + + std::unordered_map leadPCandidateCount; + for (auto it = leadPPair.begin(); it != leadPPair.end(); ++it) { + auto& [c1, leadP1, c2, leadP2] = *it; // Iterates over collision pairs and leading particle pairs (structured binding) + if (leadP1.size() > 0 && leadP2.size() > 0) { // There should always be at least one leadP, given the overflow exclusion above + float eta1 = 0.f, phi1 = 0.f, eta2 = 0.f, phi2 = 0.f; + for (auto const& lp : leadP1) { + eta1 = lp.leadParticleEta(); + phi1 = lp.leadParticlePhi(); + break; + } // Retrieves the first entry + for (auto const& lp : leadP2) { + eta2 = lp.leadParticleEta(); + phi2 = lp.leadParticlePhi(); + break; + } + // Each side of the pair is one more candidate for the other collision's reservoir: + reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c1.globalIndex(), {eta2, phi2, c2.globalIndex()}); + reservoirInsert(leadPCandidateCount, mixedLeadPByCollision, c2.globalIndex(), {eta1, phi1, c1.globalIndex()}); + histos.fill(HIST("EventMixingQA/hMixedEventLeadPDeltaIndexEligible"), c2.globalIndex() - c1.globalIndex()); + } + if (it.isNewWindow()) { // Count each bin-window once, not once per pair inside it + histos.fill(HIST("EventMixingQA/hMixedEventLeadPWindowNeighbours"), it.currentWindowNeighbours()); } } - float subleadingJetEta = 0.; - float subleadingJetPhi = 0.; - XYZVector subJetUnitVec(1., 0., 0.); - if (hasValidSubJet) { - subleadingJetEta = subleadingJet->jetEta(); - subleadingJetPhi = subleadingJet->jetPhi(); - // Using internal getters to make code cleaner: - subJetUnitVec = XYZVector(subleadingJet->jetPx(), subleadingJet->jetPy(), subleadingJet->jetPz()).Unit(); + // Leading jet: + // pt comes from the jetProxyByCollision pre-pass above. + // (Collisions whose leading jet failed minLeadJetPt get the -999 sentinel, so they never enter this mixing) + auto getMixLeadJetPt = [&jetProxyByCollision](o2::aod::RingCollision const& col) { + auto cacheIt = jetProxyByCollision.find(col.globalIndex()); + if (cacheIt == jetProxyByCollision.end() || !cacheIt->second.hasValidLeadingJet) + return -999.f; + return cacheIt->second.leadingJetPt; + }; + using LeadJetBinningType = FlexibleBinningPolicy, + o2::aod::lambdajetpol::Zvtx, decltype(getMixLeadJetPt), decltype(getMixCentrality)>; + LeadJetBinningType leadJetBinning{{getMixLeadJetPt, getMixCentrality}, + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; + + // RingJets is still the associated table (SameKindPair requires one), but its sliced content goes unused here: + // the borrowed direction comes from jetProxyByCollision, which already knows which jet is the leading one. + SameKindPair leadJetPair{ + leadJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; + + std::unordered_map leadJetCandidateCount; + for (auto it = leadJetPair.begin(); it != leadJetPair.end(); ++it) { + auto& [c1, jets1, c2, jets2] = *it; // jets1/jets2 intentionally unused: the leading/subleading jet is already resolved in jetProxyByCollision + auto cachedLeadJet1 = jetProxyByCollision.find(c1.globalIndex()); + auto cachedLeadJet2 = jetProxyByCollision.find(c2.globalIndex()); + if (cachedLeadJet1 != jetProxyByCollision.end() && cachedLeadJet2 != jetProxyByCollision.end() && + cachedLeadJet1->second.hasValidLeadingJet && cachedLeadJet2->second.hasValidLeadingJet) { + reservoirInsert(leadJetCandidateCount, mixedLeadJetByCollision, c1.globalIndex(), + {cachedLeadJet2->second.leadingJetEta, cachedLeadJet2->second.leadingJetPhi, c2.globalIndex()}); + reservoirInsert(leadJetCandidateCount, mixedLeadJetByCollision, c2.globalIndex(), + {cachedLeadJet1->second.leadingJetEta, cachedLeadJet1->second.leadingJetPhi, c1.globalIndex()}); + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetDeltaIndexEligible"), c2.globalIndex() - c1.globalIndex()); + } + if (it.isNewWindow()) { + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetWindowNeighbours"), it.currentWindowNeighbours()); + } } - // (jet eta cuts only meaningful when the jet actually exists) - const bool kinematicJetCheck = hasValidLeadingJet && (std::abs(leadingJetEta) < 0.5); - const bool kinematic2ndJetCheck = hasValidSubJet && (std::abs(subleadingJetEta) < 0.5); - const bool kinematicLeadPCheck = std::abs(leadPEta) < 0.5; - - for (auto const& v0 : v0sInColl) { - const bool isLambda = v0.isLambda(); - const bool isAntiLambda = v0.isAntiLambda(); - // For now, removing the ambiguous candidates from the analysis. Derived data permits handling both. - // (From Podolanski-Armenteros plots, the population of ambiguous is ~2% without TOF, and without - // competing mass rejection. From those, ~99% seem to be K0s, so no real gain in considering the - // ambiguous candidates in the analysis) - if (isLambda && isAntiLambda) - continue; - const float v0pt = v0.v0Pt(); - const float v0eta = v0.v0Eta(); - const float v0phi = v0.v0Phi(); - - float v0LambdaLikeMass = 0; // Initialized just to catch any stray behavior - float protonLikePt = 0; - float protonLikeEta = 0; - float protonLikePhi = 0; - if (isLambda) { - if (!analyseLambda) - continue; - v0LambdaLikeMass = v0.massLambda(); - protonLikePt = v0.posPt(); - protonLikeEta = v0.posEta(); - protonLikePhi = v0.posPhi(); - } else if (isAntiLambda) { // (TODO: add a split histogram where you consider Lambda and AntiLambda polarization separately?) - if (!analyseAntiLambda) - continue; - v0LambdaLikeMass = v0.massAntiLambda(); - protonLikePt = v0.negPt(); - protonLikeEta = v0.negEta(); - protonLikePhi = v0.negPhi(); + // Subleading jet: + auto getMixSubJetPt = [&jetProxyByCollision](o2::aod::RingCollision const& col) { + auto cacheIt = jetProxyByCollision.find(col.globalIndex()); + if (cacheIt == jetProxyByCollision.end() || !cacheIt->second.hasValidSubJet) + return -999.f; + return cacheIt->second.subleadingJetPt; + }; + using SubJetBinningType = FlexibleBinningPolicy, + o2::aod::lambdajetpol::Zvtx, decltype(getMixSubJetPt), decltype(getMixCentrality)>; + SubJetBinningType subJetBinning{{getMixSubJetPt, getMixCentrality}, + {axisConfigurations.axisPVz, axisConfigurations.axisJetPt, axisConfigurations.axisCentrality}}; + + SameKindPair subJetPair{ + subJetBinning, fakePolSwitches.mixedEventWindowSize, -1, collisions, std::make_tuple(jets), &mixCache}; + + std::unordered_map subJetCandidateCount; + for (auto it = subJetPair.begin(); it != subJetPair.end(); ++it) { + auto& [c1, jets1, c2, jets2] = *it; // jets1/jets2 intentionally unused: the leading/subleading jet is already resolved in jetProxyByCollision + auto cachedSubJet1 = jetProxyByCollision.find(c1.globalIndex()); + auto cachedSubJet2 = jetProxyByCollision.find(c2.globalIndex()); + if (cachedSubJet1 != jetProxyByCollision.end() && cachedSubJet2 != jetProxyByCollision.end() && + cachedSubJet1->second.hasValidSubJet && cachedSubJet2->second.hasValidSubJet) { + reservoirInsert(subJetCandidateCount, mixedSubJetByCollision, c1.globalIndex(), + {cachedSubJet2->second.subleadingJetEta, cachedSubJet2->second.subleadingJetPhi, c2.globalIndex()}); + reservoirInsert(subJetCandidateCount, mixedSubJetByCollision, c2.globalIndex(), + {cachedSubJet1->second.subleadingJetEta, cachedSubJet1->second.subleadingJetPhi, c1.globalIndex()}); + histos.fill(HIST("EventMixingQA/hMixedEventSubJetDeltaIndexEligible"), c2.globalIndex() - c1.globalIndex()); } - - PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); - PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, protonMass); - float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections - - // Boosting proton into lambda frame: - XYZVector beta = -lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to the rest frame - auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); - - // Getting unit vectors and 3-components: - XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); - XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); - - //////////////////////////////////////////// - // Ring observable: Leading particle proxy - // Always computed -- leading particle existence is guaranteed by the second check above - //////////////////////////////////////////// - // Cross product - XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); - float ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); - // Adding the prefactor related to the CP-violating decay (decay constants have different signs) - if (!forcePolSignQA) - ringObservableLeadP *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else - ringObservableLeadP *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - float deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, pi), for convenience - float deltaThetaLeadP = ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); // 3D angular separation - - ////////////////////////////////////////// - // Ring observable: Leading jet proxy - // Only computed when a leading jet exists in this collision. - ////////////////////////////////////////// - float ringObservable = 0.; - float deltaPhiJet = 0.; - float deltaThetaJet = 0.; - if (hasValidLeadingJet) { - // Cross product - XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); - ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); - // Adding prefactor - if (!forcePolSignQA) - ringObservable *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else - ringObservable *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); - deltaThetaJet = ROOT::Math::VectorUtil::Angle(leadingJetUnitVec, lambdaLike3Vec); + if (it.isNewWindow()) { + histos.fill(HIST("EventMixingQA/hMixedEventSubJetWindowNeighbours"), it.currentWindowNeighbours()); } + } - ////////////////////////////////////////// - // Ring observable: Subleading jet proxy - // Only computed when a subleading jet exists in this collision. - ////////////////////////////////////////// - float ringObservable2ndJet = 0.; - float deltaPhi2ndJet = 0.; - float deltaTheta2ndJet = 0.; - if (hasValidSubJet) { - XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); - ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); - // Adding prefactor - if (!forcePolSignQA) - ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : polPrefactorAntiLambda; - else - ringObservable2ndJet *= (isLambda) ? polPrefactorLambda : -1.0 * polPrefactorAntiLambda; - // Angular variables - deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); - deltaTheta2ndJet = ROOT::Math::VectorUtil::Angle(subJetUnitVec, lambdaLike3Vec); + // Selected-partner proximity: |target - source| for the partner the reservoir actually kept. + for (auto const& kv : mixedLeadPByCollision) + histos.fill(HIST("EventMixingQA/hMixedEventLeadPDeltaIndexSelected"), std::abs(kv.first - kv.second.sourceCollisionId)); + for (auto const& kv : mixedLeadJetByCollision) + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetDeltaIndexSelected"), std::abs(kv.first - kv.second.sourceCollisionId)); + for (auto const& kv : mixedSubJetByCollision) + histos.fill(HIST("EventMixingQA/hMixedEventSubJetDeltaIndexSelected"), std::abs(kv.first - kv.second.sourceCollisionId)); + + // Source-usage QA: + // (Per-proxy (HIST() needs literal names, so the fills stay unrolled here) + auto leadPUsage = tallySourceUsage(mixedLeadPByCollision); + for (auto const& kv : leadPUsage) + histos.fill(HIST("EventMixingQA/hMixedEventLeadPSourceUsageCount"), kv.second); + // Eta/phi of a mixed proxy is stored under the collision that receives the mixing -- reuse the first hit found: + for (auto const& kv : mixedLeadPByCollision) { + auto usageIt = leadPUsage.find(kv.second.sourceCollisionId); + if (usageIt == leadPUsage.end()) // already consumed below + continue; + histos.fill(HIST("EventMixingQA/pMixedEventLeadPSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventLeadPSourceUsageVsPhi"), kv.second.phi, usageIt->second); + leadPUsage.erase(usageIt); // Fill this source exactly once, not once per target it supplied + } + + auto leadJetUsage = tallySourceUsage(mixedLeadJetByCollision); + for (auto const& kv : leadJetUsage) + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetSourceUsageCount"), kv.second); + for (auto const& kv : mixedLeadJetByCollision) { + auto usageIt = leadJetUsage.find(kv.second.sourceCollisionId); + if (usageIt == leadJetUsage.end()) + continue; + histos.fill(HIST("EventMixingQA/pMixedEventLeadJetSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventLeadJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); + leadJetUsage.erase(usageIt); + } + + auto subJetUsage = tallySourceUsage(mixedSubJetByCollision); + for (auto const& kv : subJetUsage) + histos.fill(HIST("EventMixingQA/hMixedEventSubJetSourceUsageCount"), kv.second); + for (auto const& kv : mixedSubJetByCollision) { + auto usageIt = subJetUsage.find(kv.second.sourceCollisionId); + if (usageIt == subJetUsage.end()) + continue; + histos.fill(HIST("EventMixingQA/pMixedEventSubJetSourceUsageVsEta"), kv.second.eta, usageIt->second); + histos.fill(HIST("EventMixingQA/pMixedEventSubJetSourceUsageVsPhi"), kv.second.phi, usageIt->second); + subJetUsage.erase(usageIt); + } + } + + for (int idxResampling = 0; idxResampling < fakePolSwitches.nProxyResamples; idxResampling++) { // resampling loop for forceRandJet and forceDatalikeJet + for (auto const& collision : collisions) { + const float collisionPVz = collision.zvtx(); + + const auto collId = collision.globalIndex(); // The self-index accessor + const float centrality = getCentrality(collision); + + // Used this dummy for backwards compatibility, under the reasonable assumption that the field points always in the same *direction* in the used runs + // (it is not worth it to fetch and store the magnetic field in the datamodel) + const float magField = 1.f; // Purely geometric. + + // Slice jets, V0s and leading particle belonging to this collision: + // (global collision indices repeat a lot, but they are unique to a same TimeFrame (TF) subfolder in the derived data) + auto v0sInColl = v0s.sliceBy(perColV0s, collId); + auto leadPsInColl = leadPs.sliceBy(perColLeadPs, collId); + + // Check if there is at least one V0 and one jet in the collision: + // (in the way I fill the table, there is always at least one V0 in + // the stored collision, but the jets table can not be filled for + // that collision, and a collision may not be filled when the jets + // table is. Be mindful of that!) + // 1) Require at least one V0: + const int nLambdaLikeV0s = v0sInColl.size(); // Caching this variable, as it will be reused in the loop + // In the latest datamodel format, only unambiguous V0s (Lambda XOR antiLambda) are saved, + // so the number of V0s in the collision table is the number of Lambdas/antiLambda identified + // by the table producer. + if (!nLambdaLikeV0s) + continue; + + // 2) We require at least one leading particle: + // (The goal is to see how diluted the signal gets with events which don't even have a loose FastJet jet) + // (The leading particle is built from all tracks that passed the pseudojet + // selection, so it exists whenever FastJet was run on this collision. + // Events that have a leading jet always have a leading particle too, but + // the converse is not true: events can have a leading particle with no jet + // if no jet survives the pT threshold/the background subtraction) + // (At least that is the case when minLeadParticlePt = 0) + float leadPPt = -1.; // pT = -1 means "table entry not found for this collision". + float leadPEta = 0.; + float leadPPhi = 0.; + float leadPPx = 0., leadPPy = 0., leadPPz = 0.; + for (auto const& lp : leadPsInColl) { + // Table should contain exactly one entry per collision, but we break immediately to be safe: + leadPPt = lp.leadParticlePt(); + leadPEta = lp.leadParticleEta(); + leadPPhi = lp.leadParticlePhi(); + // Using dynamic columns to make code cleaner: + leadPPx = lp.leadParticlePx(); + leadPPy = lp.leadParticlePy(); + leadPPz = lp.leadParticlePz(); } + // // Discard events with no leading particle (FastJet didn't even run in these cases!): + // if (leadPPt < 0.) + // continue; + + // Apply minimum pT selection for the leading particle (not necessarily the same as in derived data builder. Can be a stricter cut!): + bool hasValidLeadingP = leadPPt > minLeadParticlePt; + + // Build leading particle unit vector, outside the V0 loop for performance. + XYZVector leadPUnitVec(1., 0., 0.); // dummy (overwritten below when hasValidLeadingP) + if (hasValidLeadingP) { + leadPUnitVec = XYZVector(leadPPx, leadPPy, leadPPz).Unit(); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadLeadP; + + // doMixedEventProxies: get this collision's mixing partner (if any) from the LUT built above: + // (this check is performed only if hasValidLeadingP is true for performance, as the LeadPPt matching for the event mixing would also demand a minimum jet pT) + if (fakePolSwitches.doMixedEventProxies) { + auto itMix = mixedLeadPByCollision.find(collId); + prevJetCache.hadLeadP = (itMix != mixedLeadPByCollision.end()); // Check if this event had a valid mixing target. Reusing prevJetCache + if (prevJetCache.hadLeadP) { + prevJetCache.leadPEta = itMix->second.eta; + prevJetCache.leadPPhi = itMix->second.phi; + } + histos.fill(HIST("EventMixingQA/hMixedEventLeadPOutcome"), prevJetCache.hadLeadP ? 1 : 0); + if (prevJetCache.hadLeadP) { // Only fill the comparison histograms on an actual hit: + histos.fill(HIST("EventMixingQA/h2dMixedLeadPEtaVsLeadPEta"), prevJetCache.leadPEta, leadPEta); + histos.fill(HIST("EventMixingQA/h2dMixedLeadPPhiVsLeadPPhi"), prevJetCache.leadPPhi, leadPPhi); + } + } + + applyProxyDistortion({hasValidLeadingP, leadPPt, leadPEta, leadPPhi, leadPUnitVec}, + minLeadParticlePt, {prevJetCache.hadLeadP, prevJetCache.leadPEta, prevJetCache.leadPPhi}, + etaLeadPDist, phiLeadPDist, rng); + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidLeadingP (a pT cut) value here! + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hLeadPEta"), leadPEta); + histos.fill(HIST("JetKinematicsQA/hLeadPPhi"), leadPPhi); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtLeadP"), leadPPt); - // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): - // To be precise, not actually the polarization, but a part of the summand in P^*_\Lambda = (3/\alpha_\Lambda) * - float PolStarX = 0, PolStarY = 0, PolStarZ = 0; // Dummy initialization: avoid warnings in compile time - if (isLambda) { // Notice there is no need to check analyseLambda again due to previous checks. - PolStarX = polPrefactorLambda * protonLikeStarUnit3Vec.X(); - PolStarY = polPrefactorLambda * protonLikeStarUnit3Vec.Y(); - PolStarZ = polPrefactorLambda * protonLikeStarUnit3Vec.Z(); - } else if (isAntiLambda) { - PolStarX = polPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); - PolStarY = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); - PolStarZ = polPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); + histos.fill(HIST("JetKinematicsQA/h2dLeadPEtaVsPVz"), leadPEta, collisionPVz); + if (doJetProxy5dQA) + histos.fill(HIST("JetKinematicsQA/h5dLeadPEtaPhiPtPVzCent"), leadPEta, leadPPhi, leadPPt, collisionPVz, centrality); + } } - float v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes + // 3) Fetching leading jet and subleading jet -- Resolved once per collision in the jetProxyByCollision pre-pass: + const JetProxyCache& jetProxies = jetProxyByCollision.at(collId); // .at() makes it sure we don't create a new key in the map + float leadingJetPt = jetProxies.leadingJetPt; + float subleadingJetPt = jetProxies.subleadingJetPt; - // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! - // No, there should NOT be any ";" here! Read the macro definition for an explanation - histos.fill(HIST("pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + // Defining local bools that may be changed by applyProxyDistortion: + bool hasValidLeadingJet = jetProxies.hasValidLeadingJet; + bool hasValidSubJet = jetProxies.hasValidSubJet; + // Build jet vectors (only when the corresponding jet exists): + // Dummy initialisations are safe: all jet-dependent fills are gated on hasValidLeadingJet / hasValidSubJet. + float leadingJetEta = 0.; + float leadingJetPhi = 0.; + XYZVector leadingJetUnitVec(1., 0., 0.); // dummy (overwritten below) if (hasValidLeadingJet) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") - histos.fill(HIST("pRingCuts"), 0, ringObservable); + leadingJetEta = jetProxies.leadingJetEta; + leadingJetPhi = jetProxies.leadingJetPhi; + // Rebuild the direction from the cached eta/phi (cheaper than calling jetPx() internal getters and then normalizing with .Unit()): + const double inverseCoshEta = 1.0 / std::cosh(leadingJetEta); + leadingJetUnitVec = XYZVector(std::cos(leadingJetPhi) * inverseCoshEta, std::sin(leadingJetPhi) * inverseCoshEta, std::tanh(leadingJetEta)); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadLeadJet; + if (fakePolSwitches.doMixedEventProxies) { + // Get this collision's leading-jet mixing partner (if any) from the LUT built above: + auto itMix = mixedLeadJetByCollision.find(collId); + prevJetCache.hadLeadJet = (itMix != mixedLeadJetByCollision.end()); + if (prevJetCache.hadLeadJet) { + prevJetCache.leadJetEta = itMix->second.eta; + prevJetCache.leadJetPhi = itMix->second.phi; + } + histos.fill(HIST("EventMixingQA/hMixedEventLeadJetOutcome"), prevJetCache.hadLeadJet ? 1 : 0); + if (prevJetCache.hadLeadJet) { + histos.fill(HIST("EventMixingQA/h2dMixedLeadJetEtaVsLeadJetEta"), prevJetCache.leadJetEta, leadingJetEta); + histos.fill(HIST("EventMixingQA/h2dMixedLeadJetPhiVsLeadJetPhi"), prevJetCache.leadJetPhi, leadingJetPhi); + } + } + applyProxyDistortion({hasValidLeadingJet, leadingJetPt, leadingJetEta, leadingJetPhi, leadingJetUnitVec}, + minLeadJetPt, {prevJetCache.hadLeadJet, prevJetCache.leadJetEta, prevJetCache.leadJetPhi}, + etaLeadPDist, phiLeadPDist, rng); + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidLeadingJet (a pT cut) value here! + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hLeadJetEta"), leadingJetEta); + histos.fill(HIST("JetKinematicsQA/hLeadJetPhi"), leadingJetPhi); + histos.fill(HIST("JetKinematicsQA/hJetCounterPtJet"), leadingJetPt); + + histos.fill(HIST("JetKinematicsQA/h2dLeadJetEtaVsPVz"), leadingJetEta, collisionPVz); + if (doJetProxy5dQA) + histos.fill(HIST("JetKinematicsQA/h5dLeadJetEtaPhiPtPVzCent"), leadingJetEta, leadingJetPhi, leadingJetPt, collisionPVz, centrality); + } } + + float subleadingJetEta = 0.; + float subleadingJetPhi = 0.; + XYZVector subJetUnitVec(1., 0., 0.); if (hasValidSubJet) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") - histos.fill(HIST("pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); + subleadingJetEta = jetProxies.subleadingJetEta; + subleadingJetPhi = jetProxies.subleadingJetPhi; + const double inverseCoshEtaSub = 1.0 / std::cosh(subleadingJetEta); + subJetUnitVec = XYZVector(std::cos(subleadingJetPhi) * inverseCoshEtaSub, std::sin(subleadingJetPhi) * inverseCoshEtaSub, std::tanh(subleadingJetEta)); + + // Apply distortion logic: + const bool hadPreviousProxy = prevJetCache.hadSubJet; + if (fakePolSwitches.doMixedEventProxies) { + // Get this collision's subleading-jet mixing partner (if any) from the LUT built above: + auto itMix = mixedSubJetByCollision.find(collId); + prevJetCache.hadSubJet = (itMix != mixedSubJetByCollision.end()); + if (prevJetCache.hadSubJet) { + prevJetCache.subJetEta = itMix->second.eta; + prevJetCache.subJetPhi = itMix->second.phi; + } + histos.fill(HIST("EventMixingQA/hMixedEventSubJetOutcome"), prevJetCache.hadSubJet ? 1 : 0); + if (prevJetCache.hadSubJet) { + histos.fill(HIST("EventMixingQA/h2dMixedSubJetEtaVsSubJetEta"), prevJetCache.subJetEta, subleadingJetEta); + histos.fill(HIST("EventMixingQA/h2dMixedSubJetPhiVsSubJetPhi"), prevJetCache.subJetPhi, subleadingJetPhi); + } + } + applyProxyDistortion({hasValidSubJet, subleadingJetPt, subleadingJetEta, subleadingJetPhi, subJetUnitVec}, + minSubLeadJetPt, {prevJetCache.hadSubJet, prevJetCache.subJetEta, prevJetCache.subJetPhi}, + etaLeadPDist, phiLeadPDist, rng); + + // Fill distorted-proxy QA histograms: + // Do not gate on the post-distortion hasValidSubJet (a pT cut) value here! + if (!fakePolSwitches.forcePreviousJet || hadPreviousProxy) { + histos.fill(HIST("JetKinematicsQA/hSubLeadJetEta"), subleadingJetEta); + histos.fill(HIST("JetKinematicsQA/hSubLeadJetPhi"), subleadingJetPhi); + histos.fill(HIST("JetKinematicsQA/hJetCounterPt2ndJet"), subleadingJetPt); + + histos.fill(HIST("JetKinematicsQA/h2dSubLeadJetEtaVsPVz"), subleadingJetEta, collisionPVz); + if (doJetProxy5dQA) + histos.fill(HIST("JetKinematicsQA/h5dSubLeadJetEtaPhiPtPVzCent"), subleadingJetEta, subleadingJetPhi, subleadingJetPt, collisionPVz, centrality); + } } - // Extra kinematic criteria for Lambda candidates (removes polarization background): - const bool kinematicLambdaCheck = (v0pt > 0.5 && v0pt < 1.5) && std::abs(lambdaRapidity) < 0.5; - if (kinematicLambdaCheck) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 1, ringObservableLeadP); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + // (jet eta cuts only meaningful when the jet actually exists) + const bool kinematicJetCheck = hasValidLeadingJet && (std::abs(leadingJetEta) < 0.5); + const bool kinematic2ndJetCheck = hasValidSubJet && (std::abs(subleadingJetEta) < 0.5); + const bool kinematicLeadPCheck = hasValidLeadingP && (std::abs(leadPEta) < 0.5); + + // Quick bools that are useful for detector asymmetry QA: + const bool jetEtaPos = hasValidLeadingJet && (leadingJetEta >= 0.); // Only perform >= check if has validJet + const bool subJetEtaPos = hasValidSubJet && (subleadingJetEta >= 0.); + const bool leadPEtaPos = hasValidLeadingP && (leadPEta >= 0.); + + // Stricter QA version of the bools -- Jets have a radius that makes it possible eta_{jet} > 0, yet half its tracks are in eta < 0 + // (This does not apply to leading particles, obviously. They have no substructure in eta) + const bool jetEtaStrict = hasValidLeadingJet && (std::abs(leadingJetEta) >= jetR); + const bool subJetEtaStrict = hasValidSubJet && (std::abs(subleadingJetEta) >= jetR); + // If one was to define bools for each side of the detector (not needed in the current if-else structure on TProfile fills) + // const bool jetEtaStrictPos = jetEtaPos && jetEtaStrict; + // const bool jetEtaStrictNeg = !jetEtaPos && jetEtaStrict; + // const bool subJetEtaStrictPos = subJetEtaPos && subJetEtaStrict; + // const bool subJetEtaStrictNeg = !subJetEtaPos && subJetEtaStrict; + + // Fetching number of Lambda-like V0s in collision (must be known before full loop, to fill "pRingVsNV0s"): + // int nLambdaLikeV0s = 0; + // for (auto const& v0 : v0sInColl) { + // if (v0.isLambda() ^ v0.isAntiLambda()){ // XOR (only the non-ambiguous candidates) + // nLambdaLikeV0s++; + // } + // } + // Code above was superseeded: new datamodel does not store ambiguous candidates! + // The new getter comes at the very start of processPolarizationData() now. + + // Initialize delta method accumulators (reset for new collision): + for (auto const& tracker : {&trackRing, &trackRingKinCuts, &trackJetKinCuts, &trackJetLambdaKinCuts}) + tracker->reset(); + for (auto const& v0 : v0sInColl) { + const bool isLambda = v0.isLambda(); // true: is a Lambda. false: is an antiLambda. + // For now, removing the ambiguous candidates from the analysis. New datamodel does NOT save ambiguous candidates. + // (From Podolanski-Armenteros plots, the population of ambiguous is ~3.8% without TOF, and without + // competing mass rejection. From those, ~99% seem to be K0s, so no real gain in considering the + // ambiguous candidates in the analysis) + // const bool isAntiLambda = v0.isAntiLambda(); // No longer used! + // if (isLambda && isAntiLambda) continue; + const float v0pt = v0.v0Pt(); + const float v0eta = v0.v0Eta(); + const float v0phi = v0.v0Phi(); + const float dcaDau = v0.dcaV0Daughters(); + + float v0LambdaLikeMass = v0.massV0(); + float protonLikePt = 0; + float protonLikeEta = 0; + float protonLikePhi = 0; + float protonLikeDCADauToPV = 0; + float pionLikeDCADauToPV = 0; + if (isLambda) { + if (!analyseLambda) + continue; + protonLikePt = v0.posPt(); + protonLikeEta = v0.posEta(); + protonLikePhi = v0.posPhi(); + protonLikeDCADauToPV = v0.dcaPosToPV(); + pionLikeDCADauToPV = v0.dcaNegToPV(); + } else { // Guaranteed to be an antiLambda candidate, not an ambiguous candidate + if (!analyseAntiLambda) + continue; + protonLikePt = v0.negPt(); + protonLikeEta = v0.negEta(); + protonLikePhi = v0.negPhi(); + protonLikeDCADauToPV = v0.dcaNegToPV(); + pionLikeDCADauToPV = v0.dcaPosToPV(); + } + + PtEtaPhiMVector lambdaLike4Vec(v0pt, v0eta, v0phi, v0LambdaLikeMass); + PtEtaPhiMVector protonLike4Vec(protonLikePt, protonLikeEta, protonLikePhi, ProtonMass); + const float lambdaRapidity = lambdaLike4Vec.Rapidity(); // For further kinematic selections + const int v0InMassPeak = (v0LambdaLikeMass >= 1.1020593 && v0LambdaLikeMass <= 1.1288811); // Very naive estimator. Based on signal extractions from outside this code + + // Inexpensive estimates of signal extraction effects on the observable: + if (excludeOutOfPeakQA && !v0InMassPeak) + continue; + else if (excludeInPeakQA && v0InMassPeak) + continue; + + // Boosting proton into lambda frame: + XYZVector beta = lambdaLike4Vec.BoostToCM(); // Boost trivector that goes from laboratory frame to Lambda's rest frame (convenient new function, different from TLorentzVector's BoostVector()) + auto protonLike4VecStar = ROOT::Math::VectorUtil::boost(protonLike4Vec, beta); + + // Getting unit vectors and 3-components: + XYZVector lambdaLike3Vec = lambdaLike4Vec.Vect(); + auto lambdaLikeUnit3Vec = lambdaLike3Vec.Unit(); + XYZVector protonLikeStarUnit3Vec = protonLike4VecStar.Vect().Unit(); + + // Lab-frame Lambda momentum components -- Not for polarization, but for actual momenta plotting in XY and ZX planes: + // (Used for the (px,py) and (pz,px) polarization vector-field / ring 2D profiles) + const float v0px = lambdaLike3Vec.X(); + const float v0py = lambdaLike3Vec.Y(); + const float v0pz = lambdaLike3Vec.Z(); + + // Calculating fake polarization ("negative helicity problem") estimator: + // (this estimator is calculated outside of any gate, as it does not depend on jet proxy used) + float cosFakePol = protonLikeStarUnit3Vec.Dot(lambdaLikeUnit3Vec); + + // Calculating the azimuthal angle between the Lambda and the proton: + float deltaPhiLambdaProtonStar = wrapToPiFast(lambdaLikeUnit3Vec.Phi() - protonLikeStarUnit3Vec.Phi()); // Phi is defined from -PI to PI in ROOT::Math::Cartesian3D, thus kept the wrapping + + // Calculating the phi* angle: + // e_z = p_Lambda_hat; // e_x = normalize(z_hat cross p_Lambda); // e_y = e_z cross e_x; + // // phi_star = atan2(p_p_star dot e_y, p_p_star dot e_x); + // XYZVector e_x(-lambdaLikeUnit3Vec.Y(), lambdaLikeUnit3Vec.X(), 0.); // Same as e_x = zHat.Cross(lambdaLikeUnit3Vec); + // XYZVector e_y = lambdaLikeUnit3Vec.Cross(e_x); // e_y completes the right-handed coordinate system (e_z is lambdaLikeUnit3Vec) + // float pX = protonLikeStarUnit3Vec.Dot(e_x); + // float pY = protonLikeStarUnit3Vec.Dot(e_y); + // float phiStar = std::atan2(pY, pX); + // Faster implementation: + // pX = p_y * L_x - p_x * L_y + float pX = protonLikeStarUnit3Vec.Y() * lambdaLikeUnit3Vec.X() - protonLikeStarUnit3Vec.X() * lambdaLikeUnit3Vec.Y(); + // pY = p_z - L_z * (p_proton_star dot p_lambda_hat) + float pY = protonLikeStarUnit3Vec.Z() - lambdaLikeUnit3Vec.Z() * cosFakePol; // (Reusing cosFakePol calculated earlier!) + float phiStar = std::atan2(pY, pX); // This will give an output from -PI to PI + + if (qaSwitches.doFakePolDiagnosticsQA) { + // Another reconstruction efficiency measure: + // (Formula is: p_{Lambda} \cross p_{Daughter}^{*} \cdot B, and B points in Z) + if (analyseMagField) { + auto crossGeom = lambdaLike3Vec.Cross(protonLikeStarUnit3Vec); + const bool positiveGeom = crossGeom.Z() * magField > 0; + + if (isLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else if (isLambda && !positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + else if (!isLambda && positiveGeom) + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomRight"), v0LambdaLikeMass); + else + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassDecayGeomLeft"), v0LambdaLikeMass); + } + + // Measuring the AEE effect differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + if (isLambda) + histos.fill(HIST("HelicityEfficiencyQA/hLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); + else + histos.fill(HIST("HelicityEfficiencyQA/hAntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar); + // AEE and HEE correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCounts_CosThetaVsPhiStar"), cosFakePol, phiStar); + } // end doFakePolDiagnosticsQA (per-V0 AEE/HEE) + + // Useful kinematic bools: + const bool lambdaEtaPos = v0eta >= 0.; + const bool pTLambdaCheck = v0pt > 0.5 && v0pt < 1.5; + const bool rapidityLambdaCheck = std::abs(lambdaRapidity) < 0.5; + const bool kinematicLambdaCheck = pTLambdaCheck && rapidityLambdaCheck; + + //////////////////////////////////////////// + // Ring observable: Leading particle proxy + // Only computed when a valid leading particle exists (pT > minLeadParticlePt) + //////////////////////////////////////////// + float ringObservableLeadP = 0.; + float deltaPhiLeadP = 0.; + float deltaThetaLeadP = 0.; + float cosDeltaThetaLeadP = 0.; + if (hasValidLeadingP) { + XYZVector crossLeadP = leadPUnitVec.Cross(lambdaLike3Vec); + ringObservableLeadP = protonLikeStarUnit3Vec.Dot(crossLeadP) / crossLeadP.R(); + // Adding the prefactor related to the CP-violating decay (decay constants have different signs) + if (!fakePolSwitches.forcePolSignQA) + ringObservableLeadP *= (isLambda) ? PolPrefactorLambda : PolPrefactorAntiLambda; + else + ringObservableLeadP *= (isLambda) ? PolPrefactorLambda : -1.0 * PolPrefactorAntiLambda; + // Angular variables + deltaPhiLeadP = wrapToPiFast(v0phi - leadPPhi); // Wrapped to [-PI, PI), for convenience + + cosDeltaThetaLeadP = leadPUnitVec.Dot(lambdaLikeUnit3Vec); // Uses the pre-calculated unit vectors to avoid recomputation + deltaThetaLeadP = std::acos(cosDeltaThetaLeadP); // 3D angular separation. Same as ROOT::Math::VectorUtil::Angle(leadPUnitVec, lambdaLike3Vec); + } + + ////////////////////////////////////////// + // Ring observable: Leading jet proxy + // Only computed when a leading jet exists in this collision. + ////////////////////////////////////////// + float ringObservable = 0.; + float deltaPhiJet = 0.; + float deltaEtaJet = 0.; + float deltaThetaJet = 0.; + float cosDeltaThetaJet = 0.; + float Jz = 0.; + float ringObservableOverJetZ = 0.; if (hasValidLeadingJet) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCuts"), 1, ringObservable); + XYZVector cross = leadingJetUnitVec.Cross(lambdaLike3Vec); + ringObservable = protonLikeStarUnit3Vec.Dot(cross) / cross.R(); + // Adding prefactor + if (!fakePolSwitches.forcePolSignQA) + ringObservable *= (isLambda) ? PolPrefactorLambda : PolPrefactorAntiLambda; + else + ringObservable *= (isLambda) ? PolPrefactorLambda : -1.0 * PolPrefactorAntiLambda; + // Angular variables + deltaPhiJet = wrapToPiFast(v0phi - leadingJetPhi); + deltaEtaJet = v0eta - leadingJetEta; + + cosDeltaThetaJet = leadingJetUnitVec.Dot(lambdaLikeUnit3Vec); + deltaThetaJet = std::acos(cosDeltaThetaJet); + + // Testing an invariance -- /\hat{t}_z -- Possible source of an artificial (trivial) sign flip in the observable: + Jz = leadingJetUnitVec.Z(); + if (std::abs(Jz) > 1e-4) + ringObservableOverJetZ = ringObservable / Jz; + else + ringObservableOverJetZ = 0.0; // A simple guard. May not be the best, but works + + // // A second projection schema, where e_x = normalize(t_hat cross p_Lambda), using t_hat instead of z_hat (different from phi*): + // // (this decomposes in an orthogonal basis related to the jet coordinates) + // XYZVector ez = lambdaLikeUnit3Vec; + // XYZVector ex = leadingJetUnitVec.Cross(lambdaLike3Vec); + // XYZVector ey = ez.Cross(ex); + + // ringObservableExProjection = ringObservable; + // ringObservableEyProjection = + // // Assuming that energy can get inside the average in: + // // P_Lambda \cdot p_Lambda = E_Lambda/m_Lambda * P_Lambda^* \cdot p_Lambda = * p_Lambda + // ringObservableEzProjection = cosFakePol * lambdaLike4Vec.E()/v0LambdaLikeMass; } + + ////////////////////////////////////////// + // Ring observable: Subleading jet proxy + // Only computed when a subleading jet exists in this collision. + ////////////////////////////////////////// + float ringObservable2ndJet = 0.; + float deltaPhi2ndJet = 0.; + float deltaTheta2ndJet = 0.; + float cosDeltaTheta2ndJet = 0.; if (hasValidSubJet) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); + XYZVector cross2ndJet = subJetUnitVec.Cross(lambdaLike3Vec); + ringObservable2ndJet = protonLikeStarUnit3Vec.Dot(cross2ndJet) / cross2ndJet.R(); + // Adding prefactor + if (!fakePolSwitches.forcePolSignQA) + ringObservable2ndJet *= (isLambda) ? PolPrefactorLambda : PolPrefactorAntiLambda; + else + ringObservable2ndJet *= (isLambda) ? PolPrefactorLambda : -1.0 * PolPrefactorAntiLambda; + // Angular variables + deltaPhi2ndJet = wrapToPiFast(v0phi - subleadingJetPhi); + cosDeltaTheta2ndJet = subJetUnitVec.Dot(lambdaLikeUnit3Vec); + deltaTheta2ndJet = std::acos(cosDeltaTheta2ndJet); } - } - // Extra selection criteria on jet candidates: - // (redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta) - if (kinematicJetCheck) { // Already includes hasValidLeadingJet in the bool! (no need to check again) - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCuts"), 2, ringObservable); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - } + // Calculating polarization observables (in the Lambda frame, because that is easier -- does not require boosts): + // To be precise, not actually the polarization, but a part of the summand in P^*_Lambda = (3/\alpha_Lambda) * + float polStarX = 0, polStarY = 0, polStarZ = 0; // Dummy initialization: avoid warnings in compile time + if (isLambda) { // Notice there is no need to check analyseLambda again due to previous checks. + polStarX = PolPrefactorLambda * protonLikeStarUnit3Vec.X(); + polStarY = PolPrefactorLambda * protonLikeStarUnit3Vec.Y(); + polStarZ = PolPrefactorLambda * protonLikeStarUnit3Vec.Z(); + } else { + polStarX = PolPrefactorAntiLambda * protonLikeStarUnit3Vec.X(); + polStarY = PolPrefactorAntiLambda * protonLikeStarUnit3Vec.Y(); + polStarZ = PolPrefactorAntiLambda * protonLikeStarUnit3Vec.Z(); + } - // Extra selection criteria on both Lambda and jet candidates: - if (kinematicLambdaCheck && kinematicJetCheck) { - RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCuts"), 3, ringObservable); - POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - } + float v0phiToFillHists = wrapToPiFast(v0phi); // A short wrap to reuse some predefined axes + + // Fill ring histograms: (1D, lambda 2D correlations and jet 2D correlations): + if (hasValidLeadingP) { + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "Ring") // Notice the usage of macros! If you change the variable names, this WILL break the code! + // No, there should NOT be any ";" here! Read the macro definition for an explanation + } + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 0, ringObservableLeadP); // First bin of comparison + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 0); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("Ring", leadPEtaPos, lambdaEtaPos); + } + } + if (familySwitches.doFamilyRing) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + } + + // Binary search using the pre-fetched axes for delta method of error bar estimation: + int binPt = 0; // Dummy declarations + int binMass = 0; + int binDTheta = 0; + if (hasValidLeadingJet) { + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "Ring") + } + histos.fill(HIST("IntegratedCuts/pRingCuts"), 0, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 0); + histos.fill(HIST("IntegratedCuts/pRingVsNV0s"), nLambdaLikeV0s, ringObservable); + histos.fill(HIST("hNV0sVsCentrality"), nLambdaLikeV0s, centrality); + + // Properly fetching values as they are needed: + binPt = mAxisPt->FindBin(v0pt); + binMass = mAxisMass->FindBin(v0LambdaLikeMass); + binDTheta = mAxisDTheta->FindBin(deltaThetaJet); + if (familySwitches.doFamilyRing) { + trackRing.addV0(ringObservable, binPt, binMass, binDTheta); + } + + if (qaSwitches.doFakePolDiagnosticsQA) { + // Measuring the AEE differentially (azimuthal efficiency effect, which causes different V0 topologies to be enhanced/suppressed): + if (isLambda) + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_LambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + else + histos.fill(HIST("HelicityEfficiencyQA/p2dRing_AntiLambdaMassVsPhiLambdaMinusPhiProtonStar"), v0LambdaLikeMass, deltaPhiLambdaProtonStar, ringObservable); + // AEE and HEE correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_CosThetaVsPhiStar"), cosFakePol, phiStar); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignal_CosThetaVsPhiStar"), cosFakePol, phiStar, ringObservable); + + // AEE and DCA between daughters correlation: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAdau"), phiStar, dcaDau); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdau"), phiStar, dcaDau, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaJet"), phiStar, dcaDau, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAdauVsEtaLambda"), phiStar, dcaDau, v0eta, ringObservable); + + // DCA dau to PV correlation: + // For proton-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLike"), phiStar, protonLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaJet"), phiStar, protonLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAProLikeVsEtaLambda"), phiStar, protonLikeDCADauToPV, v0eta, ringObservable); + // For pion-like daughter: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLike"), phiStar, pionLikeDCADauToPV, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaJet"), phiStar, pionLikeDCADauToPV, leadingJetEta, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pFakePolSignalJet_PhiStarVsDCAPiLikeVsEtaLambda"), phiStar, pionLikeDCADauToPV, v0eta, ringObservable); + + histos.fill(HIST("HelicityEfficiencyQA/pRingVsJetZcomponent"), Jz, ringObservable); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEta"), leadingJetEta, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsCosThetaHEE"), cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsPhiStar"), phiStar, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsCosThetaHEE"), leadingJetEta, cosFakePol, ringObservableOverJetZ); + histos.fill(HIST("HelicityEfficiencyQA/pRingOverJetZcomponent_VsJetEtaVsPhiStar"), leadingJetEta, phiStar, ringObservableOverJetZ); + } // end doFakePolDiagnosticsQA (leading-jet AEE/HEE) + } + if (hasValidSubJet) { + if (familySwitches.doFamilyRing) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "Ring") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 0); + } + + if (qaSwitches.doFakePolDiagnosticsQA) { + // Filling eta dependence QAs of the result (both for V0 and jet proxy): + // Defining shared binning which depend on the V0 only: + const int etaLambdaBin = lambdaEtaPos ? 3 : 4; + if (hasValidLeadingJet) { + histos.fill(HIST("EtaStudy/pRingEtaCuts"), 0, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaLambdaBin, ringObservable); + + // Bin indices for this proxy: + // Bin 0: all, 1/2: proxy #eta sign, 3/4: #Lambda #eta sign, 5-8: joint, + // 9/10: |#eta_{proxy}| >= R, 11-14: strict joint. + const int etaProxyBin = jetEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (jetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + const int etaProxyStrictBin = jetEtaPos ? 9 : 10; + const int etaProxyStrictLambdaBin = (jetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyLambdaBin, ringObservable); + + // HEE study (helicity efficiency effect): + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCounts"), cosFakePol, etaProxyLambdaBin); + // Same for signal: + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalVsCosTheta"), cosFakePol, etaProxyLambdaBin, ringObservable); + // Counter and ring accumulators for AEE study: + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, 0); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaLambdaBin); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyBin); + histos.fill(HIST("EtaStudy/hCountsVsPhiStar"), phiStar, etaProxyLambdaBin); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiStar"), phiStar, etaProxyLambdaBin, ringObservable); + + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, 0, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaLambdaBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyBin, ringObservable); + histos.fill(HIST("EtaStudy/pFakePolSignalvsPhiLambdaMinusPhiProtonStar"), deltaPhiLambdaProtonStar, etaProxyLambdaBin, ringObservable); + + // Extra correlations test: + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJet"), cosFakePol, deltaThetaJet); + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForJets"), cosFakePol, v0pt); + // Split by proxy #eta sign: + if (jetEtaPos) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetPosEta"), cosFakePol, deltaThetaJet); + else + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaJetNegEta"), cosFakePol, deltaThetaJet); + + if (pTLambdaCheck) { + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtCut"), cosFakePol, etaProxyLambdaBin); + if (rapidityLambdaCheck) { // Stricter check + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLambdaPtYCuts"), cosFakePol, etaProxyLambdaBin); + } + } + if (jetEtaStrict) { // |eta_{Jet}| >= R + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictBin, ringObservable); + histos.fill(HIST("EtaStudy/pRingEtaCuts"), etaProxyStrictLambdaBin, ringObservable); + } + } + if (hasValidSubJet) { + // Same bin scheme as the leading jet above: + const int etaProxyBin = subJetEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (subJetEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + const int etaProxyStrictBin = subJetEtaPos ? 9 : 10; + const int etaProxyStrictLambdaBin = (subJetEtaPos ? 11 : 13) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), 0, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaLambdaBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyLambdaBin, ringObservable2ndJet); + if (subJetEtaStrict) { // |eta_{SubJet}| >= R + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictBin, ringObservable2ndJet); + histos.fill(HIST("EtaStudy/pRingEtaCutsSubLeadingJet"), etaProxyStrictLambdaBin, ringObservable2ndJet); + } + } + if (hasValidLeadingP) { + // Same bin scheme, without the strict variant (this axis stops at 9 bins): + const int etaProxyBin = leadPEtaPos ? 1 : 2; + const int etaProxyLambdaBin = (leadPEtaPos ? 5 : 7) + (lambdaEtaPos ? 0 : 1); + + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), 0, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaLambdaBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP"), etaProxyLambdaBin, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), 0, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaLambdaBin, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyBin, v0InMassPeak, ringObservableLeadP); + histos.fill(HIST("EtaStudy/pRingEtaCutsLeadingP_MassSignalVsBackground"), etaProxyLambdaBin, v0InMassPeak, ringObservableLeadP); + + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, 0); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaLambdaBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyBin); + histos.fill(HIST("EtaStudy/hFakePolCountsLeadP"), cosFakePol, etaProxyLambdaBin); + + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsCosThetaVsPtForLeadP"), cosFakePol, v0pt); // Understanding the population of events that has a leading particle (even though this does not need one to be calculated!) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadP"), cosFakePol, deltaThetaLeadP); + if (leadPEtaPos) + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPPosEta"), cosFakePol, deltaThetaLeadP); + else + histos.fill(HIST("HelicityEfficiencyQA/hFakePolCountsVsDeltaThetaLeadPNegEta"), cosFakePol, deltaThetaLeadP); + } + } // end doFakePolDiagnosticsQA (eta-dependence block) + + // Extra kinematic criteria for Lambda candidates (removes polarization background): + if (kinematicLambdaCheck) { + if (hasValidLeadingP) { + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 1, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 1); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("RingKinematicCuts", leadPEtaPos, lambdaEtaPos); + } + } + if (familySwitches.doFamilyRingKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + if (hasValidLeadingJet) { + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCuts"), 1, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 1); + if (familySwitches.doFamilyRingKinematicCuts) { + trackRingKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } + } + if (hasValidSubJet) { + if (familySwitches.doFamilyRingKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "RingKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 1, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 1); + } + } + + // Extra selection criteria on jet candidates: + // (redundant for jets with R=0.4, but for jets with R<0.4 the leading jet may be farther in eta) + if (kinematicJetCheck) { // Already includes hasValidLeadingJet in the bool! (no need to check again) + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCuts"), 2, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 2); + if (familySwitches.doFamilyJetKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + trackJetKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } + } + + // Extra selection criteria on both Lambda and jet candidates: + if (kinematicLambdaCheck && kinematicJetCheck) { + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCuts"), 3, ringObservable); + histos.fill(HIST("IntegratedCuts/hCountCuts"), 3); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + POLARIZATION_PROFILE_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + trackJetLambdaKinCuts.addV0(ringObservable, binPt, binMass, binDTheta); + } + } + + // Same variations for the leading particle and for the subleading jet: + // (kinematicLeadPCheck already encodes hasValidLeadingP, so no extra gate needed here) + if (kinematicLeadPCheck) { + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 2, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 2); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetKinematicCuts", leadPEtaPos, lambdaEtaPos); + } + } + if (kinematic2ndJetCheck) { + if (familySwitches.doFamilyJetKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 2); + } + if (kinematicLambdaCheck && kinematicLeadPCheck) { + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsLeadingP"), 3, ringObservableLeadP); + histos.fill(HIST("IntegratedCuts/hCountCutsLeadingP"), 3); + + // Filling checks that rely on Eta>0 or Eta<0 checks for V0 and LeadingP eta: + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_LEADP_ETA_SPLIT_FILL_LIST("JetAndLambdaKinematicCuts", leadPEtaPos, lambdaEtaPos); + } + } + if (kinematicLambdaCheck && kinematic2ndJetCheck) { + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") + } + histos.fill(HIST("IntegratedCuts/pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); + histos.fill(HIST("IntegratedCuts/hCountCutsSubLeadingJet"), 3); + } + } // end v0s loop - // Same variations for the leading particle and for the subleading jet: - if (kinematicLeadPCheck) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 2, ringObservableLeadP); + // Flush trackers to the actual O2 histograms (via macros, so that O2 compiles properly): + if (familySwitches.doFamilyRing) { + FLUSH_DELTA_TRACKER("Ring", trackRing, mAxisPt, mAxisMass, mAxisDTheta) } - if (kinematic2ndJetCheck) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 2, ringObservable2ndJet); + if (familySwitches.doFamilyRingKinematicCuts) { + FLUSH_DELTA_TRACKER("RingKinematicCuts", trackRingKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } - if (kinematicLambdaCheck && kinematicLeadPCheck) { - RING_OBSERVABLE_LEADP_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCutsLeadingP"), 3, ringObservableLeadP); + if (familySwitches.doFamilyJetKinematicCuts) { + FLUSH_DELTA_TRACKER("JetKinematicCuts", trackJetKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } - if (kinematicLambdaCheck && kinematic2ndJetCheck) { - RING_OBSERVABLE_2NDJET_FILL_LIST(APPLY_HISTO_FILL, "JetAndLambdaKinematicCuts") - histos.fill(HIST("pRingCutsSubLeadingJet"), 3, ringObservable2ndJet); + if (familySwitches.doFamilyJetAndLambdaKinematicCuts) { + FLUSH_DELTA_TRACKER("JetAndLambdaKinematicCuts", trackJetLambdaKinCuts, mAxisPt, mAxisMass, mAxisDTheta) } - } // end v0s loop - } // end collisions + } // end collisions + } // end of resampling loop for forceRandJet and forceDatalikeJet } PROCESS_SWITCH(lambdajetpolarizationionsderived, processPolarizationData, "Process derived data in Run 3 Data", true); diff --git a/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx b/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx index 71ba4a84e19..2eeee1ccae9 100644 --- a/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaTwoPartPolarization.cxx @@ -10,10 +10,13 @@ // or submit itself to any jurisdiction. /// \author Junlee Kim (jikim1290@gmail.com) +/// \brief longitudinal polarization from 2PC +/// \file lambdaTwoPartPolarization.cxx #include "PWGLF/DataModel/LFStrangenessTables.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" @@ -41,7 +44,6 @@ #include #include #include -#include #include #include @@ -57,7 +59,12 @@ using namespace o2::framework::expressions; using namespace o2::soa; using namespace o2::constants::physics; -struct lambdaTwoPartPolarization { +struct LfLambdaTwoPartPolarization { + enum centSel { + kFT0C = 0, + kFT0M + }; + using EventCandidates = soa::Join; using TrackCandidates = soa::Join; using V0TrackCandidate = aod::V0Datas; @@ -68,17 +75,14 @@ struct lambdaTwoPartPolarization { OutputObjHandlingPolicy::AnalysisObject}; struct : ConfigurableGroup { - Configurable cfgURL{"cfgURL", - "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; - Configurable nolaterthan{"ccdb-no-later-than", - std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), - "Latest acceptable timestamp of creation for the object"}; + Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; + Configurable nolaterthan{"nolaterthan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; } cfgCcdbParam; - Service ccdb; + Service ccdb{}; o2::ccdb::CcdbApi ccdbApi; Configurable cfgCentSel{"cfgCentSel", 100., "Centrality selection"}; - Configurable cfgCentEst{"cfgCentEst", 2, "Centrality estimator, 1: FT0C, 2: FT0M"}; + Configurable cfgCentEst{"cfgCentEst", 1, "Centrality estimator, 0: FT0C, 1: FT0M"}; Configurable cfgEvtSel{"cfgEvtSel", true, "event selection flag"}; Configurable cfgPVSel{"cfgPVSel", true, "Additional PV selection flag for syst"}; @@ -108,6 +112,11 @@ struct lambdaTwoPartPolarization { Configurable cfgDaughPrPt{"cfgDaughPrPt", 0.5, "minimum daughter proton pt"}; Configurable cfgDaughPiPt{"cfgDaughPiPt", 0.2, "minimum daughter pion pt"}; + Configurable cfgTrackPtMin{"cfgTrackPtMin", 0.2, "minimum primary track pT selection"}; + Configurable cfgTrackPtMax{"cfgTrackPtMax", 3., "minimum primary track pT selection"}; + Configurable cfgTrackEtaMax{"cfgTrackEtaMax", 0.8, "maximum primary track Eta selection"}; + Configurable cfgMinTPCClustersTrack{"cfgMinTPCClustersTrack", 70, "minimum primary tpc track ncluster selection"}; + Configurable cfgHypMassWindow{"cfgHypMassWindow", 0.005, "single lambda mass selection"}; Configurable cfgEffCor{"cfgEffCor", false, "flag to apply efficiency correction"}; @@ -122,9 +131,10 @@ struct lambdaTwoPartPolarization { ConfigurableAxis ptAxis{"ptAxis", {VARIABLE_WIDTH, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.5, 8.0, 10.0, 100.0}, "Transverse momentum bins"}; ConfigurableAxis centAxis{"centAxis", {VARIABLE_WIDTH, 0, 10, 20, 30, 40, 50, 60, 70, 100}, "Centrality interval"}; - ConfigurableAxis RapAxis{"RapAxis", {10, -0.5, 0.5}, "Rapidity axis"}; - ConfigurableAxis detaAxis{"dyAxis", {20, -1, 1}, "relative rapidity axis"}; + ConfigurableAxis rapAxis{"rapAxis", {10, -0.5, 0.5}, "Rapidity axis"}; + ConfigurableAxis detaAxis{"detaAxis", {20, -1, 1}, "relative rapidity axis"}; ConfigurableAxis dphiAxis{"dphiAxis", {20, -constants::math::PI * 0.5, constants::math::PI * 1.5}, "relative azimuth axis"}; + ConfigurableAxis massAxis{"massAxis", {30, 1.10, 1.1}, "lambda mass axis"}; ConfigurableAxis cosSigAxis{"cosSigAxis", {110, -1.05, 1.05}, "Signal cosine axis"}; ConfigurableAxis cosAccAxis{"cosAccAxis", {110, -7.05, 7.05}, "Accepatance cosine axis"}; @@ -134,9 +144,9 @@ struct lambdaTwoPartPolarization { TF1* fMultPVCutLow = nullptr; TF1* fMultPVCutHigh = nullptr; - float centrality; - float dphi; - float weight; + float centrality = 1.; + float dphi = 1.; + float weight = 1.; TProfile2D* EffMap = nullptr; TProfile2D* AccMap = nullptr; @@ -164,7 +174,18 @@ struct lambdaTwoPartPolarization { histos.add("Ana/Signal", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, cosSigAxis}}); histos.add("Ana/SignalCos2", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, cosSigAxis}}); - histos.add("Ana/Acceptance", "", {HistType::kTHnSparseF, {ptAxis, centAxis, RapAxis, cosAccAxis}}); + histos.add("Ana/Acceptance", "", {HistType::kTHnSparseF, {ptAxis, centAxis, rapAxis, cosAccAxis}}); + + histos.add("AnaHL/LambdaSignal", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, massAxis, cosSigAxis}}); + histos.add("AnaHL/ALambdaSignal", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, massAxis, cosSigAxis}}); + + histos.add("AnaHL/LambdaSignalSin2", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, massAxis, cosSigAxis}}); + histos.add("AnaHL/LambdaSignalCos2", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, massAxis, cosSigAxis}}); + histos.add("AnaHL/ALambdaSignalSin2", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, massAxis, cosSigAxis}}); + histos.add("AnaHL/ALambdaSignalCos2", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis, massAxis, cosSigAxis}}); + + histos.add("AnaHL/RefTrig", "", {HistType::kTH2F, {ptAxis, centAxis}}); + histos.add("AnaHL/Ref", "", {HistType::kTHnSparseF, {ptAxis, ptAxis, detaAxis, dphiAxis, centAxis}}); fMultPVCutLow = new TF1("fMultPVCutLow", "[0]+[1]*x+[2]*x*x+[3]*x*x*x - 2.5*([4]+[5]*x+[6]*x*x+[7]*x*x*x+[8]*x*x*x*x)", 0, 100); fMultPVCutLow->SetParameters(2834.66, -87.0127, 0.915126, -0.00330136, 332.513, -12.3476, 0.251663, -0.00272819, 1.12242e-05); @@ -184,13 +205,14 @@ struct lambdaTwoPartPolarization { ROOT::Math::PxPyPzMVector ProtonVec1, PionVec1, LambdaVec1, ProtonBoostedVec1, PionBoostedVec1; ROOT::Math::PxPyPzMVector ProtonVec2, PionVec2, LambdaVec2, ProtonBoostedVec2, PionBoostedVec2; - int V01Tag; - int V02Tag; - double costhetastar1; - double costhetastar2; + int V01Tag = 1; + int V02Tag = 1; + double costhetastar1 = 0.0; + double costhetastar2 = 0.0; + double lambdaRecMass = 1.; template - bool eventSelected(TCollision collision) + bool eventSelected(TCollision const& collision) { if (!collision.sel8()) { return 0; @@ -218,33 +240,44 @@ struct lambdaTwoPartPolarization { } // event selection template - bool SelectionV0(TCollision const& collision, V0 const& candidate, int LambdaTag) + bool selectionV0(TCollision const& collision, V0 const& candidate, int lambdaPid) { - if (candidate.v0radius() < cfgv0radiusMin) + if (candidate.v0radius() < cfgv0radiusMin) { return false; - if (LambdaTag) { - if (std::abs(candidate.dcapostopv()) < cfgDCAPrToPVMin) + } + if (lambdaPid == 1) { + if (std::abs(candidate.dcapostopv()) < cfgDCAPrToPVMin) { return false; - if (std::abs(candidate.dcanegtopv()) < cfgDCAPiToPVMin) + } + if (std::abs(candidate.dcanegtopv()) < cfgDCAPiToPVMin) { return false; - } else if (!LambdaTag) { - if (std::abs(candidate.dcapostopv()) < cfgDCAPiToPVMin) + } + } else if (lambdaPid == 0) { + if (std::abs(candidate.dcapostopv()) < cfgDCAPiToPVMin) { return false; - if (std::abs(candidate.dcanegtopv()) < cfgDCAPrToPVMin) + } + if (std::abs(candidate.dcanegtopv()) < cfgDCAPrToPVMin) { return false; + } } - if (candidate.v0cosPA() < cfgv0CosPA) + if (candidate.v0cosPA() < cfgv0CosPA) { return false; - if (std::abs(candidate.dcaV0daughters()) > cfgDCAV0Dau) + } + if (std::abs(candidate.dcaV0daughters()) > cfgDCAV0Dau) { return false; - if (candidate.pt() < cfgV0PtMin) + } + if (candidate.pt() < cfgV0PtMin) { return false; - if (candidate.yLambda() < cfgV0EtaMin) + } + if (candidate.yLambda() < cfgV0EtaMin) { return false; - if (candidate.yLambda() > cfgV0EtaMax) + } + if (candidate.yLambda() > cfgV0EtaMax) { return false; - if (candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massLambda > cfgV0LifeTime) + } + if (candidate.distovertotmom(collision.posX(), collision.posY(), collision.posZ()) * massLambda > cfgV0LifeTime) { return false; + } return true; } @@ -252,28 +285,150 @@ struct lambdaTwoPartPolarization { template bool isSelectedV0Daughter(T const& track, int pid) // pid 0: proton, pid 1: pion { - if (track.tpcNClsFound() < cfgDaughTPCnclsMin) + if (track.tpcNClsFound() < cfgDaughTPCnclsMin) { return false; - if (pid == 0 && std::abs(track.tpcNSigmaPr()) > cfgDaughPIDCutsTPCPr) + } + if (pid == 0 && std::abs(track.tpcNSigmaPr()) > cfgDaughPIDCutsTPCPr) { return false; - if (pid == 1 && std::abs(track.tpcNSigmaPi()) > cfgDaughPIDCutsTPCPi) + } + if (pid == 1 && std::abs(track.tpcNSigmaPi()) > cfgDaughPIDCutsTPCPi) { return false; - if (track.eta() > cfgDaughEtaMax) + } + if (track.eta() > cfgDaughEtaMax) { return false; - if (track.eta() < cfgDaughEtaMin) + } + if (track.eta() < cfgDaughEtaMin) { return false; - if (pid == 0 && track.pt() < cfgDaughPrPt) + } + if (pid == 0 && track.pt() < cfgDaughPrPt) { return false; - if (pid == 1 && track.pt() < cfgDaughPiPt) + } + if (pid == 1 && track.pt() < cfgDaughPiPt) { return false; + } return true; } + template + bool selectTrack(Track const& track) + { + if (!track.isGlobalTrack()) { + return false; + } + if (track.pt() < cfgTrackPtMin || track.pt() > cfgTrackPtMax) { + return false; + } + if (std::abs(track.eta()) > cfgTrackEtaMax) { + return false; + } + if (track.tpcNClsFound() < cfgMinTPCClustersTrack) { + return false; + } + return true; + } + + template + void FillHistogramsRef(Trk1 const& trks1, Trk2 const& trks2) + { + for (const auto& trk1 : trks1) { + if (!selectTrack(trk1)) { + continue; + } + + histos.fill(HIST("AnaHL/RefTrig"), trk1.pt(), centrality); + for (const auto& trk2 : trks2) { + if (!selectTrack(trk2)) { + continue; + } + + if (doprocessDataSame && trk1.globalIndex() >= trk2.globalIndex()) { + continue; + } + + histos.fill(HIST("AnaHL/Ref"), trk1.pt(), trk2.pt(), trk1.eta() - trk2.eta(), RecoDecay::constrainAngle(trk1.phi() - trk2.phi(), -constants::math::PI * 0.5), centrality); + } + } + } + + template + void FillHistogramsLH(C const& c1, V0 const& v0s, Trk const& trks) + { + for (const auto& trk : trks) { + if (!selectTrack(trk)) { + continue; + } + + for (const auto& v01 : v0s) { + auto postrack_v01 = v01.template posTrack_as(); + auto negtrack_v01 = v01.template negTrack_as(); + + int LambdaTag = 0; + int aLambdaTag = 0; + + if (isSelectedV0Daughter(postrack_v01, 0) && isSelectedV0Daughter(negtrack_v01, 1)) { + LambdaTag = 1; + } + if (isSelectedV0Daughter(negtrack_v01, 0) && isSelectedV0Daughter(postrack_v01, 1)) { + aLambdaTag = 1; + } + + if (doprocessDataSame) { + if (postrack_v01.globalIndex() == trk.globalIndex() || negtrack_v01.globalIndex() == trk.globalIndex()) { + continue; + } + } + + if (LambdaTag == aLambdaTag) { + continue; + } + + if (!selectionV0(c1, v01, LambdaTag)) { + continue; + } + + if (LambdaTag) { + ProtonVec1 = ROOT::Math::PxPyPzMVector(v01.pxpos(), v01.pypos(), v01.pzpos(), massPr); + PionVec1 = ROOT::Math::PxPyPzMVector(v01.pxneg(), v01.pyneg(), v01.pzneg(), massPi); + V01Tag = 0; + } + if (aLambdaTag) { + ProtonVec1 = ROOT::Math::PxPyPzMVector(v01.pxneg(), v01.pyneg(), v01.pzneg(), massPr); + PionVec1 = ROOT::Math::PxPyPzMVector(v01.pxpos(), v01.pypos(), v01.pzpos(), massPi); + V01Tag = 1; + } + + LambdaVec1 = ProtonVec1 + PionVec1; + LambdaVec1.SetM(massLambda); + + ROOT::Math::Boost boost1{LambdaVec1.BoostToCM()}; + ProtonBoostedVec1 = boost1(ProtonVec1); + + costhetastar1 = ProtonBoostedVec1.Pz() / ProtonBoostedVec1.P(); + dphi = RecoDecay::constrainAngle(v01.phi() - trk.phi(), -constants::math::PI * 0.5); + + weight = 1.0; + weight *= cfgEffCor ? 1.0 / EffMap->GetBinContent(EffMap->GetXaxis()->FindBin(v01.pt()), EffMap->GetYaxis()->FindBin(centrality)) : 1.; + weight *= cfgAccCor ? 1.0 / AccMap->GetBinContent(AccMap->GetXaxis()->FindBin(v01.pt()), AccMap->GetYaxis()->FindBin(v01.yLambda())) : 1.; + + if (LambdaTag) { + histos.fill(HIST("AnaHL/LambdaSignal"), trk.pt(), v01.pt(), v01.eta() - trk.eta(), dphi, centrality, v01.mLambda(), costhetastar1 * weight); + histos.fill(HIST("AnaHL/LambdaSignalSin2"), trk.pt(), v01.pt(), v01.eta() - trk.eta(), dphi, centrality, v01.mLambda(), costhetastar1 * std::sin(2.0 * dphi) * weight); + histos.fill(HIST("AnaHL/LambdaSignalCos2"), trk.pt(), v01.pt(), v01.eta() - trk.eta(), dphi, centrality, v01.mLambda(), costhetastar1 * std::cos(2.0 * dphi) * weight); + } + if (aLambdaTag) { + histos.fill(HIST("AnaHL/ALambdaSignal"), trk.pt(), v01.pt(), v01.eta() - trk.eta(), dphi, centrality, v01.mAntiLambda(), costhetastar1 * weight); + histos.fill(HIST("AnaHL/ALambdaSignalSin2"), trk.pt(), v01.pt(), v01.eta() - trk.eta(), dphi, centrality, v01.mAntiLambda(), costhetastar1 * std::sin(2.0 * dphi) * weight); + histos.fill(HIST("AnaHL/ALambdaSignalCos2"), trk.pt(), v01.pt(), v01.eta() - trk.eta(), dphi, centrality, v01.mAntiLambda(), costhetastar1 * std::cos(2.0 * dphi) * weight); + } + } + } + } + template void FillHistograms(C1 const& c1, C2 const& c2, V01 const& V01s, V02 const& V02s) { - for (auto& v01 : V01s) { + for (const auto& v01 : V01s) { auto postrack_v01 = v01.template posTrack_as(); auto negtrack_v01 = v01.template negTrack_as(); @@ -287,11 +442,13 @@ struct lambdaTwoPartPolarization { aLambdaTag = 1; } - if (LambdaTag == aLambdaTag) + if (LambdaTag == aLambdaTag) { continue; + } - if (!SelectionV0(c1, v01, LambdaTag)) + if (!selectionV0(c1, v01, LambdaTag)) { continue; + } if (LambdaTag) { ProtonVec1 = ROOT::Math::PxPyPzMVector(v01.pxpos(), v01.pypos(), v01.pzpos(), massPr); @@ -313,9 +470,10 @@ struct lambdaTwoPartPolarization { histos.fill(HIST("Ana/Acceptance"), v01.pt(), centrality, v01.yLambda(), costhetastar1 * costhetastar1); - for (auto& v02 : V02s) { - if (v01.v0Id() <= v02.v0Id() && doprocessDataSame) + for (const auto& v02 : V02s) { + if (v01.v0Id() <= v02.v0Id() && doprocessDataSame) { continue; + } auto postrack_v02 = v02.template posTrack_as(); auto negtrack_v02 = v02.template negTrack_as(); @@ -329,15 +487,18 @@ struct lambdaTwoPartPolarization { aLambdaTag = 1; } - if (LambdaTag == aLambdaTag) + if (LambdaTag == aLambdaTag) { continue; + } - if (!SelectionV0(c2, v02, LambdaTag)) + if (!selectionV0(c2, v02, LambdaTag)) { continue; + } if (doprocessDataSame) { - if (postrack_v01.globalIndex() == postrack_v02.globalIndex() || postrack_v01.globalIndex() == negtrack_v02.globalIndex() || negtrack_v01.globalIndex() == postrack_v02.globalIndex() || negtrack_v01.globalIndex() == negtrack_v02.globalIndex()) + if (postrack_v01.globalIndex() == postrack_v02.globalIndex() || postrack_v01.globalIndex() == negtrack_v02.globalIndex() || negtrack_v01.globalIndex() == postrack_v02.globalIndex() || negtrack_v01.globalIndex() == negtrack_v02.globalIndex()) { continue; // no shared decay products + } } if (LambdaTag) { @@ -368,10 +529,7 @@ struct lambdaTwoPartPolarization { weight *= -1.0; } - dphi = TVector2::Phi_0_2pi(v01.phi() - v02.phi()); - if (dphi > constants::math::PI * 1.5) { - dphi -= constants::math::PI * 2.0; - } + dphi = RecoDecay::constrainAngle(v01.phi() - v02.phi(), -constants::math::PI * 0.5); histos.fill(HIST("Ana/Signal"), v01.pt(), v02.pt(), v01.yLambda() - v02.yLambda(), dphi, centrality, costhetastar1 * costhetastar2 * weight); histos.fill(HIST("Ana/SignalCos2"), v01.pt(), v02.pt(), v01.yLambda() - v02.yLambda(), dphi, centrality, costhetastar1 * costhetastar2 * std::cos(2.0 * dphi) * weight); @@ -379,13 +537,45 @@ struct lambdaTwoPartPolarization { } } + void processDataSameHadron(EventCandidates::iterator const& collision, + TrackCandidates const& tracks, aod::V0Datas const& V0s, + aod::BCsWithTimestamps const&) + { + centrality = collision.centFT0M(); + if (cfgCentEst == kFT0C) { + centrality = collision.centFT0C(); + } else if (cfgCentEst == kFT0M) { + centrality = collision.centFT0M(); + } + if (!eventSelected(collision) && cfgEvtSel) { + return; + } + + histos.fill(HIST("QA/CentDist"), centrality, 1.0); + histos.fill(HIST("QA/PVzDist"), collision.posZ(), 1.0); + + auto bc = collision.bc_as(); + if (cfgEffCor) { + EffMap = ccdb->getForTimeStamp(cfgEffCorPath.value, bc.timestamp()); + } + if (cfgAccCor) { + AccMap = ccdb->getForTimeStamp(cfgAccCorPath.value, bc.timestamp()); + } + + // FillHistograms(collision, collision, V0s, V0s); + FillHistogramsRef(tracks, tracks); + FillHistogramsLH(collision, V0s, tracks); + } + PROCESS_SWITCH(LfLambdaTwoPartPolarization, processDataSameHadron, "Process event for same data with hadrons", true); + void processDataSame(EventCandidates::iterator const& collision, TrackCandidates const& /*tracks*/, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) { - if (cfgCentEst == 1) { + centrality = collision.centFT0M(); + if (cfgCentEst == kFT0C) { centrality = collision.centFT0C(); - } else if (cfgCentEst == 2) { + } else if (cfgCentEst == kFT0M) { centrality = collision.centFT0M(); } if (!eventSelected(collision) && cfgEvtSel) { @@ -405,31 +595,34 @@ struct lambdaTwoPartPolarization { FillHistograms(collision, collision, V0s, V0s); } - PROCESS_SWITCH(lambdaTwoPartPolarization, processDataSame, "Process event for same data", true); + PROCESS_SWITCH(LfLambdaTwoPartPolarization, processDataSame, "Process event for same data", true); SliceCache cache; Preslice tracksPerCollisionV0 = aod::v0data::collisionId; using BinningTypeT0C = ColumnBinningPolicy; - BinningTypeT0C colBinningT0C{{vertexAxis, centAxis}, true}; + BinningTypeT0C colBinningT0C{{vertexAxis, centAxis}}; void processDataMixedT0C(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) { - for (auto& [c1, c2] : selfCombinations(colBinningT0C, cfgNoMixedEvents, -1, collisions, collisions)) { + for (const auto& [c1, c2] : selfCombinations(colBinningT0C, cfgNoMixedEvents, -1, collisions, collisions)) { - if (c1.index() == c2.index()) + if (c1.index() == c2.index()) { continue; + } centrality = c1.centFT0C(); if (cfgAccCor) { auto bc = c1.bc_as(); AccMap = ccdb->getForTimeStamp(cfgAccCorPath.value, bc.timestamp()); } - if (!eventSelected(c1)) + if (!eventSelected(c1)) { continue; - if (!eventSelected(c2)) + } + if (!eventSelected(c2)) { continue; + } auto tracks1 = V0s.sliceBy(tracksPerCollisionV0, c1.globalIndex()); auto tracks2 = V0s.sliceBy(tracksPerCollisionV0, c2.globalIndex()); @@ -437,28 +630,31 @@ struct lambdaTwoPartPolarization { FillHistograms(c1, c2, tracks1, tracks2); } } - PROCESS_SWITCH(lambdaTwoPartPolarization, processDataMixedT0C, "Process event for mixed data in PbPb", false); + PROCESS_SWITCH(LfLambdaTwoPartPolarization, processDataMixedT0C, "Process event for mixed data in PbPb", false); using BinningTypeT0M = ColumnBinningPolicy; - BinningTypeT0M colBinningT0M{{vertexAxis, centAxis}, true}; + BinningTypeT0M colBinningT0M{{vertexAxis, centAxis}}; void processDataMixedT0M(EventCandidates const& collisions, TrackCandidates const& /*tracks*/, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) { - for (auto& [c1, c2] : selfCombinations(colBinningT0M, cfgNoMixedEvents, -1, collisions, collisions)) { + for (const auto& [c1, c2] : selfCombinations(colBinningT0M, cfgNoMixedEvents, -1, collisions, collisions)) { - if (c1.index() == c2.index()) + if (c1.index() == c2.index()) { continue; + } centrality = c1.centFT0M(); if (cfgAccCor) { auto bc = c1.bc_as(); AccMap = ccdb->getForTimeStamp(cfgAccCorPath.value, bc.timestamp()); } - if (!eventSelected(c1)) + if (!eventSelected(c1)) { continue; - if (!eventSelected(c2)) + } + if (!eventSelected(c2)) { continue; + } auto tracks1 = V0s.sliceBy(tracksPerCollisionV0, c1.globalIndex()); auto tracks2 = V0s.sliceBy(tracksPerCollisionV0, c2.globalIndex()); @@ -466,11 +662,47 @@ struct lambdaTwoPartPolarization { FillHistograms(c1, c2, tracks1, tracks2); } } - PROCESS_SWITCH(lambdaTwoPartPolarization, processDataMixedT0M, "Process event for mixed data in pp", false); + PROCESS_SWITCH(LfLambdaTwoPartPolarization, processDataMixedT0M, "Process event for mixed data in pp", false); + + Preslice tracksPerCollisionPri = aod::track::collisionId; + + void processDataMixedHadronT0M(EventCandidates const& collisions, + TrackCandidates const& tracks, aod::V0Datas const& V0s, aod::BCsWithTimestamps const&) + { + for (const auto& [c1, c2] : selfCombinations(colBinningT0M, cfgNoMixedEvents, -1, collisions, collisions)) { + + if (c1.index() == c2.index()) { + continue; + } + + centrality = c1.centFT0M(); + if (cfgAccCor) { + auto bc = c1.bc_as(); + AccMap = ccdb->getForTimeStamp(cfgAccCorPath.value, bc.timestamp()); + } + if (!eventSelected(c1)) { + continue; + } + if (!eventSelected(c2)) { + continue; + } + + auto v01tracks = V0s.sliceBy(tracksPerCollisionV0, c1.globalIndex()); + auto v02tracks = V0s.sliceBy(tracksPerCollisionV0, c2.globalIndex()); + + auto pri1tracks = tracks.sliceBy(tracksPerCollisionPri, c1.globalIndex()); + auto pri2tracks = tracks.sliceBy(tracksPerCollisionPri, c2.globalIndex()); + + FillHistogramsRef(pri1tracks, pri2tracks); + + FillHistogramsLH(c1, v01tracks, pri2tracks); + FillHistogramsLH(c2, v02tracks, pri1tracks); + } + } + PROCESS_SWITCH(LfLambdaTwoPartPolarization, processDataMixedHadronT0M, "Process event for mixed data in pp with hadrons", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"lf-lambdaTwoPartPolarization"})}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Strangeness/lambdak0sflattenicity.cxx b/PWGLF/Tasks/Strangeness/lambdak0sflattenicity.cxx index ec59b986c51..05c71967ed5 100644 --- a/PWGLF/Tasks/Strangeness/lambdak0sflattenicity.cxx +++ b/PWGLF/Tasks/Strangeness/lambdak0sflattenicity.cxx @@ -21,13 +21,15 @@ #include "PWGLF/Utils/inelGt.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" -#include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include +#include #include #include #include @@ -39,16 +41,16 @@ #include #include -#include +#include #include -#include -#include +#include +#include #include #include #include -#include #include +#include #include #include @@ -58,7 +60,7 @@ using namespace o2::framework::expressions; struct Lambdak0sflattenicity { // Histograms are defined with HistogramRegistry - Service pdg; + Service pdg{}; HistogramRegistry rEventSelection{"eventSelection", {}, OutputObjHandlingPolicy::AnalysisObject, @@ -100,198 +102,459 @@ struct Lambdak0sflattenicity { OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry rCharged{ + "charged", + {}, + OutputObjHandlingPolicy::AnalysisObject, + true, + true}; - static constexpr std::string_view kHEst[8] = { + static constexpr int kNEstimators = 7; + // forward detector segmentation + static constexpr int kNChannelsPerT0Sector = 4; + static constexpr int kNSectorsT0A = 24; + static constexpr int kNSectorsT0C = 28; + // One lattice cell per FT0 readout channel: the four channels of a sector are + // separate quadrants. Gain and vertex maps stay per sector, as CCDB holds them. + static constexpr int kNChannelsFT0A = kNSectorsT0A * kNChannelsPerT0Sector; + static constexpr int kNChannelsFT0C = kNSectorsT0C * kNChannelsPerT0Sector; + static constexpr int kNChannelsPerFV0Ring = 8; + static constexpr int kNFV0EtaRings = 5; + static constexpr int kOuterFV0RingIndex = kNFV0EtaRings - 1; + static constexpr int kNSectorsFV0OuterRing = 16; + // TParticlePDG::Charge() is in units of e/3 + static constexpr float kMinCharge = 0.01f; + + // eFV0FT0C is the weighted amplitude sum, a multiplicity estimator, not a 1-rho + static constexpr std::array kHEst = { "eGlobaltrack", "eFV0", "e1flatencityFV0", "eFT0", - "e1flatencityFT0", "eFV0FT0C", "e1flatencityFV0FT0C", "ePtTrig"}; - static constexpr std::string_view kTEst[8] = { + "e1flatencityFT0", "eFV0FT0C", "ePtTrig"}; + static constexpr std::array kTEst = { "GlobalTrk", "FV0", "1-flatencity_FV0", "FT0", - "1-flatencityFT0", "FV0_FT0C", "1-flatencity_FV0_FT0C", "PtTrig"}; - static constexpr std::string_view kHPtEst[8] = { + "1-flatencityFT0", "FV0_FT0C", "PtTrig"}; + static constexpr std::array kHPtEst = { "ptVsGlobaltrack", "ptVsFV0", "ptVs1flatencityFV0", "ptVsFT0", "ptVs1flatencityFT0", "ptVsFV0FT0C", - "ptVs1flatencityFV0FT0C", "pTVsPtTrig"}; - - // Configurable for histograms - Configurable nBinsVz{"nBinsVz", 100, "N bins in Vz"}; - Configurable nBinsK0sMass{"nBinsK0sMass", 400, "N bins in K0sMass"}; - Configurable nBinsLambdaMass{"nBinsLambdaMass", 400, - "N bins in LambdaMass"}; - Configurable nBinsXiMass{"nBinsXiMass", 400, "N bins in XiMass"}; - - Configurable kK0sEPshiftfromMass{"kK0sEPshiftfromMass", 0.1, "distance of K0s Inv mass histogram start and end points from PDG mass"}; - Configurable kLambdaEPshiftfromMass{"kLambdaEPshiftfromMass", 0.05, "distance of Lambda Inv mass histogram start and end points from PDG mass"}; - Configurable kXiEPshiftfromMass{"kXiEPshiftfromMass", 0.05, "distance of Xi Inv mass histogram start and end points from PDG mass"}; - - Configurable nBinspT{"nBinspT", 250, "N bins in pT"}; - Configurable nBinsFlattenicity{"nBinsFlattenicity", 100, "N bins in Flattenicity"}; - - // Configurable for event selection - - Configurable applyEvSel{"applyEvSel", true, - "Apply event selection to Data and MCRec"}; - Configurable issel8{"issel8", true, - "Accept events that pass sel8 selection"}; - Configurable cutzvertex{"cutzvertex", 10.0f, - "Accepted z-vertex range (cm)"}; - Configurable isINELgt0{"isINELgt0", true, "is INEL gt 0"}; - Configurable isNoTimeFrameBorder{"isNoTimeFrameBorder", true, - "cut branch crossing at the beginning/end of TF"}; - Configurable isNoITSROFrameBorder{"isNoITSROFrameBorder", true, - "cut branch crossing at the beginning/end of ITS ROF"}; - Configurable isVertexITSTPC{"isVertexITSTPC", false, - "Is Vertex ITSTPC"}; - Configurable isNoSameBunchPileup{"isNoSameBunchPileup", false, - "Is No Same Bunch Pileup"}; - Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", false, - "Is Good Zvtx FT0 vs PV"}; - Configurable isTriggerTVX{"isTriggerTVX", true, - "coincidence of a signal in FT0A and FT0C"}; - - // Configurables for Flattenicity - Configurable flattenicityQA{"flattenicityQA", true, "Store Flattenicity QA plots"}; - Configurable applyCalibCh{"applyCalibCh", false, "equalize FV0"}; - Configurable applyCalibVtx{"applyCalibVtx", false, - "equalize FV0 vs vtx"}; - Configurable applyNorm{"applyNorm", false, "normalization to eta"}; - Configurable isflattenicitywithFV0{"isflattenicitywithFV0", true, - "Calculate Flattenicity with FV0"}; - Configurable isflattenicitywithFT0{"isflattenicitywithFT0", true, - "Calculate Flattenicity with FT0"}; - Configurable isflattenicitywithFV0FT0C{"isflattenicitywithFV0FT0C", true, - "Calculate Flattenicity with FV0+FT0C"}; - - Configurable flattenicityforanalysis{"flattenicityforanalysis", 0, - "Which Flattenicity to be used for analysis, 0 for FV0, 1 for FT0, 2 for FV0+FT0C"}; - Configurable flattenicityforLossCorrRec{"flattenicityforLossCorrRec", true, - "Flattenicity from Rec Tracks are used for Signal and Event loss calculations"}; - // Common Configurable parameters for V0 selection - Configurable v0settingDCAv0dau{"v0settingDCAv0dau", 1, - "DCA V0 Daughters"}; - Configurable v0settingDCApostopv{"v0settingDCApostopv", 0.06, - "DCA Pos To PV"}; - Configurable v0settingDCAnegtopv{"v0settingDCAnegtopv", 0.06, - "DCA Neg To PV"}; - Configurable v0settingDCAbactopv{"v0settingDCAbactopv", 0.06, - "DCA Bchelor To PV"}; - Configurable v0settingRapidity{"v0settingRapidity", 0.5, - "V0 rapidity cut"}; - - // Configurable parameters for V0 selection for KOs - Configurable v0settingCosPAK0s{"v0settingCosPAK0s", 0.97, - "V0 CosPA for K0s"}; - Configurable v0settingRadiusK0s{"v0settingRadiusK0s", 0.5, - "v0radius for K0s"}; - Configurable v0settingcTauK0s{"v0settingcTauK0s", 20, - "v0ctau for K0s"}; - Configurable v0settingMassRejectionK0s{"v0settingMassRejectionK0s", 0.005, - "Competing Mass Rejection cut for K0s"}; - Configurable v0settingArmePodoK0s{"v0settingArmePodoK0s", 0.2, - "Armenteros-Podolanski cut for K0s"}; - - // Configurable parameters for V0 selection for Lambda - Configurable v0settingCosPALambda{"v0settingCosPALambda", 0.995, - "V0 CosPA for Lambda"}; - Configurable v0settingRadiusLambda{"v0settingRadiusLambda", 0.5, - "v0radius for Lambda"}; - Configurable v0settingcTauLambda{"v0settingcTauLambda", 30, - "v0ctau for Lambda"}; - Configurable v0settingMassRejectionLambda{"v0settingMassRejectionLambda", 0.01, - "Competing Mass Rejection cut for Lambda"}; - - // Configurable parameters for PID selection - Configurable nSigmaTPCPion{"nSigmaTPCPion", 5, "nSigmaTPCPion"}; - Configurable nSigmaTPCProton{"nSigmaTPCProton", 5, "nSigmaTPCProton"}; - Configurable nSigmaTPCKaon{"nSigmaTPCKaon", 5, "nSigmaTPCKaon"}; + "pTVsPtTrig"}; + + // Histogram binning. The pT and 1-rho axes are ConfigurableAxis: pass + // {nbins, lo, hi} for uniform bins or {VARIABLE_WIDTH, e0, e1, ...} for the + // edges of the published binning. The 1-rho edges are meant to be the + // percentile class boundaries measured by the processFlatDist* pass, so that + // one class is exactly one bin and no rebinning happens downstream. + struct : ConfigurableGroup { + std::string prefix = "binning"; + Configurable nBinsVz{"nBinsVz", 100, "N bins in Vz"}; + Configurable nBinsK0sMass{"nBinsK0sMass", 400, "N bins in K0sMass"}; + Configurable nBinsLambdaMass{"nBinsLambdaMass", 400, + "N bins in LambdaMass"}; + Configurable nBinsXiMass{"nBinsXiMass", 400, "N bins in XiMass"}; + Configurable kK0sEPshiftfromMass{"kK0sEPshiftfromMass", 0.1, "distance of K0s Inv mass histogram start and end points from PDG mass"}; + Configurable kLambdaEPshiftfromMass{"kLambdaEPshiftfromMass", 0.05, "distance of Lambda Inv mass histogram start and end points from PDG mass"}; + Configurable kXiEPshiftfromMass{"kXiEPshiftfromMass", 0.05, "distance of Xi Inv mass histogram start and end points from PDG mass"}; + ConfigurableAxis axisPtK0s{"axisPtK0s", {250, 0.0f, 25.0f}, "#it{p}_{T} of K0s"}; + ConfigurableAxis axisPtLambda{"axisPtLambda", {250, 0.0f, 25.0f}, "#it{p}_{T} of Lambda and AntiLambda"}; + ConfigurableAxis axisPtXi{"axisPtXi", {250, 0.0f, 25.0f}, "#it{p}_{T} of Xi"}; + ConfigurableAxis axisPtCharged{"axisPtCharged", {250, 0.0f, 25.0f}, "#it{p}_{T} of charged particles"}; + ConfigurableAxis axisPtPid{"axisPtPid", {250, 0.0f, 25.0f}, "#it{p}_{TPC} of the daughter tracks"}; + ConfigurableAxis axisFlat{"axisFlat", {100, 0.0f, 1.0f}, "1-#rho_{ch} of the spectra"}; + // detector effects move the percentiles, so the closure denominator needs + // its own class edges, taken from hFlatDistGen + ConfigurableAxis axisFlatTrue{"axisFlatTrue", {100, 0.0f, 1.0f}, "true 1-#rho_{ch}"}; + ConfigurableAxis axisFlatFine{"axisFlatFine", {2000, 0.0f, 1.0f}, "1-#rho_{ch} used to locate the percentiles"}; + ConfigurableAxis axisCent{"axisCent", {100, 0.0f, 100.0f}, "FT0M percentile"}; + ConfigurableAxis axisNch{"axisNch", {150, -0.5f, 149.5f}, "N_{ch} in the tracking acceptance"}; + ConfigurableAxis axisDcaXy{"axisDcaXy", {200, -1.0f, 1.0f}, "DCA_{xy} (cm)"}; + ConfigurableAxis axisDcaV0ToPv{"axisDcaV0ToPv", {200, 0.0f, 2.0f}, "DCA of the V0 to the PV (cm)"}; + ConfigurableAxis axisPtRes{"axisPtRes", {200, -0.5f, 0.5f}, "(#it{p}_{T}^{rec} - #it{p}_{T}^{gen})/#it{p}_{T}^{gen}"}; + } binning; + + // Event selection + struct : ConfigurableGroup { + std::string prefix = "evSel"; + Configurable applyEvSel{"applyEvSel", true, + "Apply event selection to Data and MCRec"}; + Configurable issel8{"issel8", true, + "Accept events that pass sel8 selection"}; + Configurable cutzvertex{"cutzvertex", 10.0f, + "Accepted z-vertex range (cm)"}; + Configurable isINELgt0{"isINELgt0", true, "is INEL gt 0"}; + Configurable isNoTimeFrameBorder{"isNoTimeFrameBorder", true, + "cut branch crossing at the beginning/end of TF"}; + Configurable isNoITSROFrameBorder{"isNoITSROFrameBorder", true, + "cut branch crossing at the beginning/end of ITS ROF"}; + Configurable isVertexITSTPC{"isVertexITSTPC", false, + "Is Vertex ITSTPC"}; + Configurable isNoSameBunchPileup{"isNoSameBunchPileup", false, + "Is No Same Bunch Pileup"}; + Configurable isGoodZvtxFT0vsPV{"isGoodZvtxFT0vsPV", false, + "Is Good Zvtx FT0 vs PV"}; + Configurable isTriggerTVX{"isTriggerTVX", true, + "coincidence of a signal in FT0A and FT0C"}; + } evSel; + + // Flattenicity estimation + struct : ConfigurableGroup { + std::string prefix = "flatSel"; + Configurable flattenicityQA{"flattenicityQA", true, "Store Flattenicity QA plots"}; + // Both corrections reshape the lattice and therefore move the flattenicity. + // With both off nothing is read from CCDB at all. + Configurable applyCalibCh{"applyCalibCh", false, + "equalize the per-channel gain, from CCDB"}; + Configurable applyCalibVtx{"applyCalibVtx", false, + "equalize the per-cell z-vertex dependence, from CCDB"}; + Configurable applyNorm{"applyNorm", false, "normalization to eta"}; + Configurable isflattenicitywithFV0{"isflattenicitywithFV0", true, + "Calculate Flattenicity with FV0"}; + Configurable isflattenicitywithFT0{"isflattenicitywithFT0", true, + "Calculate Flattenicity with FT0"}; + Configurable flattenicityforanalysis{"flattenicityforanalysis", 0, + "Which Flattenicity to be used for analysis, 0 for FV0, 1 for FT0"}; + // On a sparse lattice sigma/ is quantised -- one lit cell gives exactly + // sqrt((N-1)/N) whatever the amplitude -- which puts spikes in 1-rho. Tune + // against the hNActiveCells* histograms. 0 disables. + Configurable minLitCellsFV0{"minLitCellsFV0", 0, + "Minimum lit FV0 cells (of 48), applied when flattenicityforanalysis=0"}; + Configurable minLitCellsFT0A{"minLitCellsFT0A", 0, + "Minimum lit FT0A cells (of 96), applied when flattenicityforanalysis=1"}; + Configurable minLitCellsFT0C{"minLitCellsFT0C", 0, + "Minimum lit FT0C cells (of 112), applied when flattenicityforanalysis=1"}; + Configurable flattenicityforLossCorrRec{"flattenicityforLossCorrRec", true, + "Flattenicity from Rec Tracks are used for Signal and Event loss calculations"}; + // FV0 only: same cell convention as the detector lattice, so gen vs rec is + // a detector effect and not a difference of definitions + Configurable genFlatDetectorLikeNorm{"genFlatDetectorLikeNorm", true, + "Weight the generator-level FV0 cells the way the detector lattice does"}; + } flatSel; + + // Calibration objects, read per run and only when the corresponding switch is + // on. Same layout as PWGLF/Tasks/GlobalEventProperties/flattenictyPikp.cxx, so + // the objects are interchangeable between the two tasks: the gain is a + // std::vector indexed by the raw detector channel, the z-vertex + // equalization a TProfile2D of (lattice cell, z_vtx). + struct : ConfigurableGroup { + std::string prefix = "ccdbConf"; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", + "url of the ccdb repository"}; + Configurable gainEqPath{"gainEqPath", "Users/s/sprasad/flattenicity/GainEq", + "CCDB directory holding FV0, FT0A and FT0C gain vectors"}; + Configurable vtxEqPath{"vtxEqPath", "Users/s/sprasad/flattenicity/ZvtxEq", + "CCDB directory holding the FV0, FT0A and FT0C z-vertex maps"}; + } ccdbConf; + + // V0 selection + struct : ConfigurableGroup { + std::string prefix = "v0Sel"; + Configurable v0settingDCAv0dau{"v0settingDCAv0dau", 1, + "DCA V0 Daughters"}; + Configurable v0settingDCApostopv{"v0settingDCApostopv", 0.06, + "DCA Pos To PV"}; + Configurable v0settingDCAnegtopv{"v0settingDCAnegtopv", 0.06, + "DCA Neg To PV"}; + Configurable v0settingDCAbactopv{"v0settingDCAbactopv", 0.06, + "DCA Bchelor To PV"}; + Configurable v0settingRapidity{"v0settingRapidity", 0.5, + "V0 rapidity cut"}; + Configurable v0settingCosPAK0s{"v0settingCosPAK0s", 0.97, + "V0 CosPA for K0s"}; + Configurable v0settingRadiusK0s{"v0settingRadiusK0s", 0.5, + "v0radius for K0s"}; + Configurable v0settingcTauK0s{"v0settingcTauK0s", 20, + "v0ctau for K0s"}; + Configurable v0settingMassRejectionK0s{"v0settingMassRejectionK0s", 0.005, + "Competing Mass Rejection cut for K0s"}; + Configurable v0settingArmePodoK0s{"v0settingArmePodoK0s", 0.2, + "Armenteros-Podolanski cut for K0s"}; + Configurable v0settingCosPALambda{"v0settingCosPALambda", 0.995, + "V0 CosPA for Lambda"}; + Configurable v0settingRadiusLambda{"v0settingRadiusLambda", 0.5, + "v0radius for Lambda"}; + Configurable v0settingcTauLambda{"v0settingcTauLambda", 30, + "v0ctau for Lambda"}; + Configurable v0settingMassRejectionLambda{"v0settingMassRejectionLambda", 0.01, + "Competing Mass Rejection cut for Lambda"}; + Configurable v0settingNTPCcrossedRows{"v0settingNTPCcrossedRows", 70, + "Minimum number of TPC crossed pad rows of the V0 daughters, negative: no cut"}; + } v0Sel; + + // Daughter track acceptance and PID + struct : ConfigurableGroup { + std::string prefix = "trkPid"; + Configurable cfgTrkEtaCut{"cfgTrkEtaCut", 0.8f, + "Eta range for tracks"}; + Configurable cfgTrkLowPtCut{"cfgTrkLowPtCut", 0.0f, "Minimum pT"}; + Configurable nSigmaTPCPion{"nSigmaTPCPion", 5, "nSigmaTPCPion"}; + Configurable nSigmaTPCProton{"nSigmaTPCProton", 5, "nSigmaTPCProton"}; + Configurable pidQAWindowK0s{"pidQAWindowK0s", 0.05, + "Half width of the K0s mass window used for the PID QA plots"}; + Configurable pidQAWindowLambda{"pidQAWindowLambda", 0.1, + "Half width of the Lambda mass window used for the PID QA plots"}; + // negative values disable a cut, so the defaults reproduce the previous selection + Configurable minCrossedRowsOverFindable{"minCrossedRowsOverFindable", -1.f, + "Minimum TPC crossed rows over findable clusters"}; + Configurable maxTpcChi2NCl{"maxTpcChi2NCl", -1.f, "Maximum TPC chi2 per cluster"}; + Configurable maxItsChi2NCl{"maxItsChi2NCl", -1.f, "Maximum ITS chi2 per cluster"}; + Configurable minItsNCls{"minItsNCls", -1, "Minimum number of ITS clusters"}; + } trkPid; + + // Cascade selection + struct : ConfigurableGroup { + std::string prefix = "cascSel"; + Configurable nTPCcrossedRows{"nTPCcrossedRows", 52, "Number of TPC crossed pad raws"}; + Configurable cascsettingDCAv0toPV{"cascsettingDCAv0toPV", 0.03, "DCA V0 To PV"}; + Configurable cascsettingDCAv0bach{"cascsettingDCAv0bach", 0.25, "DCA V0 To bachelor"}; + Configurable cascsettingDCAbaryontopv{"cascsettingDCAbaryontopv", 0.05, "DCA of the baryon daughter To PV"}; + Configurable cascsettingDCAmesontopv{"cascsettingDCAmesontopv", 0.1, "DCA of the meson daughter To PV"}; + Configurable cascsettingDCAxybaryonbach{"cascsettingDCAxybaryonbach", 0.02, "DCAxy Bachelor-Baryon To PV, below which the candidate is vetoed"}; + Configurable cascsettingCosPAbaryonbach{"cascsettingCosPAbaryonbach", 0.9999, "CosThetap Bachelor-Baryon, above which the candidate is vetoed"}; + Configurable cascsettingCosPAcascPV{"cascsettingCosPAcascPV", 0.9947, "CosThetap for Cascade to PV"}; + Configurable cascsettingCosPAv0PV{"cascsettingCosPAv0PV", 0.9876, "CosThetap for V0 to PV"}; + Configurable cascsettingv0radius{"cascsettingv0radius", 0.55, "V0 decay radius for cadcades in cm"}; + Configurable cascsettingcascradius{"cascsettingcascradius", 1.01, "Cascade decay radius for cadcades in cm"}; + Configurable cascsettingRapidity{"cascsettingRapidity", 0.5, "Cascade rapidity cut"}; + Configurable cascsettingMassRejectionLambdaXi{"cascsettingMassRejectionLambdaXi", 0.0116, "Casc Mass Rejection cut of Lambda for Xi"}; + Configurable cascsettingMassRejectioOmegaXi{"cascsettingMassRejectioOmegaXi", -1, "Casc Mass Rejection cut of Omega for Xi"}; + Configurable cascsettingproplifetime{"cascsettingproplifetime", 4.6, "Scale for lifetime cut on ctau Xi"}; + } cascSel; + + // values for flattenicityforanalysis + static constexpr int kFlatFromFV0 = 0; + static constexpr int kFlatFromFT0 = 1; + + // FT0M percentile class: run once over the full range for MB and once with a + // narrow window for HM. Grouped because the framework only decomposes 100 + // struct members (Framework/StructToTuple.h) + struct : ConfigurableGroup { + std::string prefix = "eventClass"; + Configurable applyCentSel{"applyCentSel", false, + "Select events in a FT0M percentile window"}; + Configurable cfgCentMin{"cfgCentMin", 0.0f, "Minimum FT0M percentile"}; + Configurable cfgCentMax{"cfgCentMax", 100.0f, "Maximum FT0M percentile"}; + // the processFlatDist* pass fills the MB and the HM 1-rho distributions + // together, so this window is applied to a histogram and not to the event + Configurable cfgCentMaxHM{"cfgCentMaxHM", 1.0f, + "Upper FT0M percentile of the high-multiplicity class"}; + + // keep only primaries in the MC-matched spectra, the rest go to the + // feed-down histograms + Configurable requirePrimaryMC{"requirePrimaryMC", true, + "Require isPhysicalPrimary() on the MC-matched candidate"}; + Configurable genFlatPrimariesOnly{"genFlatPrimariesOnly", true, + "Use only primaries in the generator-level flattenicity"}; + Configurable fillChargedQA{"fillChargedQA", true, + "Fill the charged-particle histograms used for and Qpp"}; + Configurable cfgEtaChargedCut{"cfgEtaChargedCut", 0.8f, + "Eta window of the charged-particle measurement"}; + // mothers outside the Lambda window still feed it, so the matrix is + // normalised in a wider one + Configurable cfgFeedDownMotherRapidity{"cfgFeedDownMotherRapidity", 1.5f, + "Rapidity window of the generated feed-down mothers"}; + } eventClass; // Configurable v0daughter_etacut{"V0DaughterEtaCut", 0.8, // "V0DaughterEtaCut"}; // Configurable v0etacut{"v0etacut", 0.8, "v0etacut"}; - // acceptance cuts for Flattenicity correlation - Configurable cfgTrkEtaCut{"cfgTrkEtaCut", 0.8f, - "Eta range for tracks"}; - Configurable cfgTrkLowPtCut{"cfgTrkLowPtCut", 0.0f, "Minimum pT"}; - - // Additional Cut configurables for Cascades - Configurable nTPCcrossedRows{"nTPCcrossedRows", 52, "Number of TPC crossed pad raws"}; - Configurable cascsettingDCAv0toPV{"cascsettingDCAv0toPV", 0.03, "DCA V0 To PV"}; - Configurable cascsettingDCAv0bach{"cascsettingDCAv0bach", 0.25, "DCA V0 To bachelor"}; - Configurable cascsettingDCAxybaryonbach{"cascsettingDCAxybaryonbach", 0.02, "DCA Baryon To bachelor"}; - Configurable cascsettingCosPAcascPV{"cascsettingCosPAcascPV", 0.9947, "CosThetap for Cascade to PV"}; - Configurable cascsettingCosPAv0PV{"cascsettingCosPAv0PV", 0.9876, "CosThetap for V0 to PV"}; - Configurable cascsettingv0radius{"cascsettingv0radius", 0.55, "V0 decay radius for cadcades in cm"}; - Configurable cascsettingcascradius{"cascsettingcascradius", 1.01, "Cascade decay radius for cadcades in cm"}; - Configurable cascsettingRapidity{"cascsettingRapidity", 0.5, "Cascade rapidity cut"}; - Configurable cascsettingMassRejectionLambdaXi{"cascsettingMassRejectionLambdaXi", 0.0116, "Casc Mass Rejection cut of Lambda for Xi"}; - Configurable cascsettingMassRejectioOmegaXi{"cascsettingMassRejectioOmegaXi", -1, "Casc Mass Rejection cut of Omega for Xi"}; - Configurable cascsettingproplifetime{"cascsettingproplifetime", 4.6, "Scale for lifetime cut on ctau Xi"}; - int nbin = 1; + // hEventsSelected bins for the stages inside estimateFlattenicity(), -1 if unused + int nbinFlatDetHit = -1; + int nbinFlatOccupancy = -1; + int nbinFlattenicity = -1; + + // how far estimateFlattenicity() got, so its rejections show in the cut flow + static constexpr int kFlatStageNoDetector = 0; + static constexpr int kFlatStageLowOccupancy = 1; + static constexpr int kFlatStageNoSignal = 2; + static constexpr int kFlatStageOk = 3; + int flatStage = kFlatStageNoDetector; + // do not fill the detector QA twice when processGenMC runs with a rec-level process + bool fillFlattenicityQAInGenMC = true; + + // the per-event histograms are shared by every rec-level process function, + // so only the one designated in init() fills them + static constexpr int kOwnerNone = -1; + static constexpr int kOwnerFlatDistData = 0; + static constexpr int kOwnerFlatDistMC = 1; + static constexpr int kOwnerRecMCV0 = 2; + static constexpr int kOwnerDataV0 = 3; + static constexpr int kOwnerRecMCCasc = 4; + static constexpr int kOwnerDataCasc = 5; + int eventHistOwner = kOwnerNone; + + // calibration objects, refreshed when the run changes + Service ccdb{}; + int mRunNumber = -1; + std::vector gainFV0; + std::vector gainFT0A; + std::vector gainFT0C; + TProfile2D* vtxEqFV0 = nullptr; + TProfile2D* vtxEqFT0A = nullptr; + TProfile2D* vtxEqFT0C = nullptr; void init(InitContext const&) { // Axes - AxisSpec k0sMassAxis = {nBinsK0sMass, 0.49f - kK0sEPshiftfromMass, 0.49f + kK0sEPshiftfromMass, + AxisSpec k0sMassAxis = {binning.nBinsK0sMass, o2::constants::physics::MassK0Short - binning.kK0sEPshiftfromMass, + o2::constants::physics::MassK0Short + binning.kK0sEPshiftfromMass, "#it{M}_{#pi^{+}#pi^{-}} [GeV/#it{c}^{2}]"}; - AxisSpec lambdaMassAxis = {nBinsLambdaMass, 1.115f - kLambdaEPshiftfromMass, 1.115f + kLambdaEPshiftfromMass, + AxisSpec lambdaMassAxis = {binning.nBinsLambdaMass, o2::constants::physics::MassLambda0 - binning.kLambdaEPshiftfromMass, + o2::constants::physics::MassLambda0 + binning.kLambdaEPshiftfromMass, "#it{M}_{p#pi^{-}} [GeV/#it{c}^{2}]"}; - AxisSpec antilambdaMassAxis = {nBinsLambdaMass, 1.115f - kLambdaEPshiftfromMass, 1.115f + kLambdaEPshiftfromMass, + AxisSpec antilambdaMassAxis = {binning.nBinsLambdaMass, o2::constants::physics::MassLambda0 - binning.kLambdaEPshiftfromMass, + o2::constants::physics::MassLambda0 + binning.kLambdaEPshiftfromMass, "#it{M}_{#pi^{+}#bar{p}} [GeV/#it{c}^{2}]"}; - AxisSpec xiMassAxis = {nBinsXiMass, 1.32f - kXiEPshiftfromMass, 1.32f + kXiEPshiftfromMass, + AxisSpec xiMassAxis = {binning.nBinsXiMass, o2::constants::physics::MassXiMinus - binning.kXiEPshiftfromMass, + o2::constants::physics::MassXiMinus + binning.kXiEPshiftfromMass, "#it{M}_{#Lambda#pi} [GeV/#it{c}^{2}]"}; - AxisSpec vertexZAxis = {nBinsVz, -15., 15., "vrtx_{Z} [cm]"}; - AxisSpec ptAxis = {nBinspT, 0.0f, 25.0f, "#it{p}_{T} (GeV/#it{c})"}; - AxisSpec flatAxis = {nBinsFlattenicity, 0.0f, 1.0f, "1-#rho_{ch}"}; - - int nBinsEst[8] = {100, 500, 102, 500, 102, 500, 102, 150}; - float lowEdgeEst[8] = {-0.5, -0.5, -0.01, -0.5, -0.01, -0.5, -0.01, .0}; - float upEdgeEst[8] = {99.5, 49999.5, 1.01, 499.5, 1.01, 499.5, 1.01, 150.0}; + AxisSpec vertexZAxis = {binning.nBinsVz, -15., 15., "vrtx_{Z} [cm]"}; + AxisSpec ptK0sAxis = {binning.axisPtK0s, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec ptLambdaAxis = {binning.axisPtLambda, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec ptXiAxis = {binning.axisPtXi, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec ptChargedAxis = {binning.axisPtCharged, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec pTPCAxis = {binning.axisPtPid, "#it{p}_{TPC} (GeV/#it{c})"}; + AxisSpec decayRadiusAxis = {100, 0.0f, 100.0f, "Decay Radius (cm)"}; + AxisSpec flatAxis = {binning.axisFlat, "1-#rho_{ch}"}; + AxisSpec flatTrueAxis = {binning.axisFlatTrue, "true 1-#rho_{ch}"}; + AxisSpec flatFineAxis = {binning.axisFlatFine, "1-#rho_{ch}"}; + AxisSpec centAxis = {binning.axisCent, "FT0M percentile"}; + AxisSpec nchAxis = {binning.axisNch, "#it{N}_{ch}"}; + AxisSpec dcaXyAxis = {binning.axisDcaXy, "DCA_{xy} (cm)"}; + AxisSpec dcaV0ToPvAxis = {binning.axisDcaV0ToPv, "DCA_{V0-PV} (cm)"}; + AxisSpec ptResAxis = {binning.axisPtRes, "(#it{p}_{T}^{rec} - #it{p}_{T}^{gen})/#it{p}_{T}^{gen}"}; + AxisSpec motherAxis = {kNFeedDownMothers, -0.5, kNFeedDownMothers - 0.5, "mother"}; + + std::array nBinsEst = {100, 500, 102, 500, 102, 500, 150}; + std::array lowEdgeEst = {-0.5, -0.5, -0.01, -0.5, -0.01, -0.5, .0}; + std::array upEdgeEst = {99.5, 49999.5, 1.01, 499.5, 1.01, 499.5, 150.0}; + + ccdb->setURL(ccdbConf.ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); // Histograms // Event selection rEventSelection.add("hVertexZ", "hVertexZ", {HistType::kTH1D, {vertexZAxis}}); rEventSelection.add("hEventsSelected", "hEventsSelected", - {HistType::kTH1D, {{12, 0, 12}}}); + {HistType::kTH1D, {{15, 0, 15}}}); rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "all"); - if (issel8) { + if (evSel.issel8) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "sel8"); } rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "zvertex"); - if (isNoTimeFrameBorder) { + if (evSel.isNoTimeFrameBorder) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "TFBorder"); } - if (isNoITSROFrameBorder) { + if (evSel.isNoITSROFrameBorder) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "ITSROFBorder"); } - if (isVertexITSTPC) { + if (evSel.isVertexITSTPC) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "VertexITSTPC"); } - if (isNoSameBunchPileup) { + if (evSel.isNoSameBunchPileup) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "SameBunchPileup"); } - if (isGoodZvtxFT0vsPV) { + if (evSel.isGoodZvtxFT0vsPV) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "isGoodZvtxFT0vsPV"); } - if (isTriggerTVX) { + if (evSel.isTriggerTVX) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "TVX"); } - if (isINELgt0) { + if (evSel.isINELgt0) { rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "INEL>0"); } + // estimateFlattenicity() drops events on three grounds: no detector record, + // too few lit cells, undefined 1-rho. One counter each. + if (doprocessDataRun3LambdaK0s || doprocessRecMCLambdaK0s || + doprocessDataRun3Cascade || doprocessRecMCRun3Cascade || + doprocessFlatDistData || doprocessFlatDistMC) { + nbinFlatDetHit = nbin; + rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "flatDetHit"); + nbinFlatOccupancy = nbin; + rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "flatOccupancy"); + nbinFlattenicity = nbin; + rEventSelection.get(HIST("hEventsSelected"))->GetXaxis()->SetBinLabel(nbin++, "flattenicity"); + } rEventSelection.add("hFlattenicityDistribution", "hFlattenicityDistribution", {HistType::kTH1D, {flatAxis}}); - if (doprocessRecMCLambdaK0s || doprocessRecMCRun3Cascade) { + rEventSelection.add("hCentFT0M", "hCentFT0M", {HistType::kTH1D, {centAxis}}); + rEventSelection.add("hCentFT0MvsFlattenicity", "hCentFT0MvsFlattenicity", + {HistType::kTH2D, {centAxis, flatAxis}}); + + // 1-rho on the fine axis, the input to the percentile boundaries of + // binning.axisFlat. Filled once per accepted collision, by the owner. + rEventSelection.add("hFlatDistRec", "hFlatDistRec", {HistType::kTH1D, {flatFineAxis}}); + rEventSelection.add("hFlatDistRecINELgt0", "hFlatDistRecINELgt0", {HistType::kTH1D, {flatFineAxis}}); + rEventSelection.add("hFlatDistRecHM", "hFlatDistRecHM", {HistType::kTH1D, {flatFineAxis}}); + rEventSelection.add("hCentFT0MFine", "hCentFT0MFine", {HistType::kTH1D, {{1000, 0.0f, 100.0f, "FT0M percentile"}}}); + if (doprocessRecMCLambdaK0s || doprocessRecMCRun3Cascade || doprocessGenMC || + doprocessFlatDistMC) { + // one entry per generated collision, the sample the closure denominator + // lives in: binning.axisFlatTrue has to be set from this one, or the + // numerator and the denominator do not share a class definition + rEventSelection.add("hFlatDistGen", "hFlatDistGen", {HistType::kTH1D, {flatFineAxis}}); + rEventSelection.add("hFlatDistGenINELgt0", "hFlatDistGenINELgt0", {HistType::kTH1D, {flatFineAxis}}); + rEventSelection.add("hFlatDistGenHM", "hFlatDistGenHM", {HistType::kTH1D, {flatFineAxis}}); + // same, restricted to accepted reconstructed collisions: the ratio to + // hFlatDistGen is the event selection bias on the class assignment + rEventSelection.add("hFlatDistGenInRec", "hFlatDistGenInRec", {HistType::kTH1D, {flatFineAxis}}); + rEventSelection.add("hFlatDistGenInRecINELgt0", "hFlatDistGenInRecINELgt0", {HistType::kTH1D, {flatFineAxis}}); + rEventSelection.add("hFlatDistGenInRecHM", "hFlatDistGenInRecHM", {HistType::kTH1D, {flatFineAxis}}); + } + if (doprocessRecMCLambdaK0s || doprocessRecMCRun3Cascade || doprocessGenMC || + doprocessFlatDistMC) { + rEventSelection.add("hTrueFV0amplvsFlat", "TrueFV0MvsFlat", HistType::kTH2D, + {{500, -0.5, +499.5, "True Nch in FV0 region"}, flatTrueAxis}); + rEventSelection.add("hTrueFT0amplvsFlat", "TrueFT0MvsFlat", HistType::kTH2D, + {{500, -0.5, +499.5, "True Nch in FT0 region"}, flatTrueAxis}); + rEventSelection.add("hNActiveCellsFT0AMCGen", "hNActiveCellsFT0AMCGen", HistType::kTH1D, + {{kNChannelsFT0A + 1, -0.5, kNChannelsFT0A + 0.5, "occupied generated FT0A cells"}}); + rEventSelection.add("hNActiveCellsFT0CMCGen", "hNActiveCellsFT0CMCGen", HistType::kTH1D, + {{kNChannelsFT0C + 1, -0.5, kNChannelsFT0C + 0.5, "occupied generated FT0C cells"}}); + } + if (doprocessRecMCLambdaK0s || doprocessRecMCRun3Cascade || doprocessFlatDistMC) { rEventSelection.add("hFlattenicityDistributionMCGen_Rec", "hFlattenicityDistributionMCGen_Rec", - {HistType::kTH1D, {flatAxis}}); + {HistType::kTH1D, {flatTrueAxis}}); rEventSelection.add("hFlattenicity_Corr_Gen_vs_Rec", "hFlattenicity_Corr_Gen_vs_Rec", - {HistType::kTH2D, {flatAxis, flatAxis}}); + {HistType::kTH2D, {flatTrueAxis, flatAxis}}); + // migration of the class assignment, needed pT-differentially to unfold + rEventSelection.add("hFlatGenVsRecFine", "hFlatGenVsRecFine", + {HistType::kTH2D, {flatFineAxis, flatFineAxis}}); + } + + // Charged particles in |eta| < cfgEtaChargedCut: per class is + // the scale factor of Qpp, and the DCAxy templates give the secondary + // contamination the same way the published analysis obtains it. + if (eventClass.fillChargedQA) { + rCharged.add("hNchVsFlat", "hNchVsFlat", {HistType::kTH2D, {nchAxis, flatAxis}}); + rCharged.add("hPtChVsFlat", "hPtChVsFlat", {HistType::kTH2D, {ptChargedAxis, flatAxis}}); + if (doprocessRecMCLambdaK0s || doprocessRecMCRun3Cascade || doprocessFlatDistMC) { + rCharged.add("hPtChVsFlatRecPrim", "hPtChVsFlatRecPrim", + {HistType::kTH2D, {ptChargedAxis, flatAxis}}); + rCharged.add("hPtChVsFlatGenInRec", "hPtChVsFlatGenInRec", + {HistType::kTH2D, {ptChargedAxis, flatAxis}}); + rCharged.add("hNchVsFlatGenInRec", "hNchVsFlatGenInRec", {HistType::kTH2D, {nchAxis, flatAxis}}); + rCharged.add("hDcaXyPtChPrim", "hDcaXyPtChPrim", + {HistType::kTH3D, {dcaXyAxis, ptChargedAxis, flatAxis}}); + rCharged.add("hDcaXyPtChSec", "hDcaXyPtChSec", + {HistType::kTH3D, {dcaXyAxis, ptChargedAxis, flatAxis}}); + } + if (doprocessDataRun3LambdaK0s || doprocessDataRun3Cascade || doprocessFlatDistData) { + rCharged.add("hDcaXyPtCh", "hDcaXyPtCh", + {HistType::kTH3D, {dcaXyAxis, ptChargedAxis, flatAxis}}); + } + if (doprocessGenMC) { + rCharged.add("hPtChVsFlatGen", "hPtChVsFlatGen", {HistType::kTH2D, {ptChargedAxis, flatAxis}}); + rCharged.add("hNchVsFlatGen", "hNchVsFlatGen", {HistType::kTH2D, {nchAxis, flatAxis}}); + rCharged.add("hPtChVsTrueFlatGen", "hPtChVsTrueFlatGen", {HistType::kTH2D, {ptChargedAxis, flatTrueAxis}}); + rCharged.add("hNchVsTrueFlatGen", "hNchVsTrueFlatGen", {HistType::kTH2D, {nchAxis, flatTrueAxis}}); + } } if (doprocessDataRun3LambdaK0s || doprocessRecMCLambdaK0s) { @@ -306,27 +569,28 @@ struct Lambdak0sflattenicity { {HistType::kTH1D, {{40, -2.0f, 2.0f, "y"}}}); rKzeroShort.add("hctauK0s", "hctauK0s", {HistType::kTH1D, {{40, 0.0f, 40.0f, "c#tau (cm)"}}}); - rKzeroShort.add( - "h2DdecayRadiusK0s", "h2DdecayRadiusK0s", - {HistType::kTH1D, {{100, 0.0f, 1.0f, "Decay Radius (cm)"}}}); + rKzeroShort.add("h2DdecayRadiusK0s", "h2DdecayRadiusK0s", + {HistType::kTH1D, {decayRadiusAxis}}); rKzeroShort.add("hDCAV0DaughtersK0s", "hDCAV0DaughtersK0s", {HistType::kTH1D, {{55, 0.0f, 2.2f, "DCA Daughters"}}}); rKzeroShort.add("hV0CosPAK0s", "hV0CosPAK0s", {HistType::kTH1D, {{100, 0.95f, 1.f, "CosPA"}}}); rKzeroShort.add("hNSigmaPosPionFromK0s", "hNSigmaPosPionFromK0s", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); rKzeroShort.add("hNSigmaNegPionFromK0s", "hNSigmaNegPionFromK0s", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); rKzeroShort.add("hMassK0spT", "hMassK0spT", - {HistType::kTH2D, {{k0sMassAxis}, {ptAxis}}}); + {HistType::kTH2D, {{k0sMassAxis}, {ptK0sAxis}}}); rKzeroShort.add("hMassK0spTFlat", "hMassK0spTFlat", - {HistType::kTH3D, {{k0sMassAxis}, {ptAxis}, {flatAxis}}}); + {HistType::kTH3D, {{k0sMassAxis}, {ptK0sAxis}, {flatAxis}}}); rKzeroShort.add("hArmPodoAlphavsQTK0sAfterCut", "hArmPodoAlphavsQTK0sAfterCut", {HistType::kTH2D, {{200, -1, 1, "#alpha"}, {70, 0, 0.35, "Q_{T}"}}}); if (doprocessRecMCLambdaK0s) { rKzeroShort.add("Generated_MCRecoCollCheck_INEL_K0Short", "Generated_MCRecoCollCheck_INEL_K0Short", - {HistType::kTH2D, {{ptAxis}, {flatAxis}}}); + {HistType::kTH2D, {{ptK0sAxis}, {flatAxis}}}); + rKzeroShort.add("Generated_MCRecoCollCheck_INELgt0_K0Short", "Generated_MCRecoCollCheck_INELgt0_K0Short", + {HistType::kTH2D, {{ptK0sAxis}, {flatAxis}}}); } // Lambda reconstruction Mass @@ -340,23 +604,42 @@ struct Lambdak0sflattenicity { {HistType::kTH1D, {{55, 0.0f, 2.2f, "DCA Daughters"}}}); rLambda.add("hV0CosPALambda", "hV0CosPALambda", {HistType::kTH1D, {{100, 0.95f, 1.f, "CosPA"}}}); - rLambda.add("hNSigmaPosPionFromLambda", "hNSigmaPosPionFromLambda", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); + rLambda.add("hNSigmaPosProtonFromLambda", "hNSigmaPosProtonFromLambda", + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); rLambda.add("hNSigmaNegPionFromLambda", "hNSigmaNegPionFromLambda", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); rLambda.add("hrapidityLambda", "hrapidityLambda", {HistType::kTH1D, {{40, -2.0f, 2.0f, "y"}}}); rLambda.add("hctauLambda", "hctauLambda", {HistType::kTH1D, {{40, 0.0f, 40.0f, "c#tau (cm)"}}}); rLambda.add("h2DdecayRadiusLambda", "h2DdecayRadiusLambda", - {HistType::kTH1D, {{100, 0.0f, 1.0f, "c#tau (cm)"}}}); + {HistType::kTH1D, {decayRadiusAxis}}); rLambda.add("hMassLambdapT", "hMassLambdapT", - {HistType::kTH2D, {{lambdaMassAxis}, {ptAxis}}}); + {HistType::kTH2D, {{lambdaMassAxis}, {ptLambdaAxis}}}); rLambda.add("hMassLambdapTFlat", "hMassLambdapTFlat", - {HistType::kTH3D, {{lambdaMassAxis}, {ptAxis}, {flatAxis}}}); + {HistType::kTH3D, {{lambdaMassAxis}, {ptLambdaAxis}, {flatAxis}}}); if (doprocessRecMCLambdaK0s) { rLambda.add("Generated_MCRecoCollCheck_INEL_Lambda", "Generated_MCRecoCollCheck_INEL_Lambda", - {HistType::kTH2D, {{ptAxis}, {flatAxis}}}); + {HistType::kTH2D, {{ptLambdaAxis}, {flatAxis}}}); + rLambda.add("Generated_MCRecoCollCheck_INELgt0_Lambda", "Generated_MCRecoCollCheck_INELgt0_Lambda", + {HistType::kTH2D, {{ptLambdaAxis}, {flatAxis}}}); + rLambda.add("hMassFeedDownLambdapTFlat", "hMassFeedDownLambdapTFlat", + {HistType::kTH3D, {{lambdaMassAxis}, {ptLambdaAxis}, {flatAxis}}}); + rLambda.add("hFeedDownLambdaPtVsMotherPt", "hFeedDownLambdaPtVsMotherPt", + {HistType::kTH2D, {{ptLambdaAxis}, {ptLambdaAxis}}}); + rLambda.add("hFeedDownLambdaMatrix", "hFeedDownLambdaMatrix", + {HistType::kTHnSparseF, {{ptLambdaAxis}, {ptLambdaAxis}, {flatAxis}, {motherAxis}}}); + rLambda.add("hFeedDownLambdaMotherPdg", "hFeedDownLambdaMotherPdg", + {HistType::kTH1D, {{motherAxis}}}); + rLambda.get(HIST("hFeedDownLambdaMotherPdg"))->GetXaxis()->SetBinLabel(1, "#Xi^{-}"); + rLambda.get(HIST("hFeedDownLambdaMotherPdg"))->GetXaxis()->SetBinLabel(2, "#Xi^{0}"); + rLambda.get(HIST("hFeedDownLambdaMotherPdg"))->GetXaxis()->SetBinLabel(3, "#Omega^{-}"); + rLambda.get(HIST("hFeedDownLambdaMotherPdg"))->GetXaxis()->SetBinLabel(4, "other"); + // generated mothers in the same events and classes as + // hFeedDownLambdaMatrix, so the matrix is a probability per mother that + // folds with the measured Xi. Xi0 is not measurable and only lives here. + rLambda.add("hGenFeedDownMotherPtFlat", "hGenFeedDownMotherPtFlat", + {HistType::kTHnSparseF, {{ptLambdaAxis}, {flatAxis}, {motherAxis}}}); } // AntiLambda reconstruction @@ -373,27 +656,77 @@ struct Lambdak0sflattenicity { {HistType::kTH1D, {{100, 0.95f, 1.f, "CosPA"}}}); rAntiLambda.add("hNSigmaPosPionFromAntiLambda", "hNSigmaPosPionFromAntiLambda", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); - rAntiLambda.add("hNSigmaNegPionFromAntiLambda", - "hNSigmaNegPionFromAntiLambda", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); + rAntiLambda.add("hNSigmaNegProtonFromAntiLambda", + "hNSigmaNegProtonFromAntiLambda", + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); rAntiLambda.add("hrapidityAntiLambda", "hrapidityAntiLambda", {HistType::kTH1D, {{40, -2.0f, 2.0f, "y"}}}); rAntiLambda.add("hctauAntiLambda", "hctauAntiLambda", {HistType::kTH1D, {{40, 0.0f, 40.0f, "c#tau (cm)"}}}); rAntiLambda.add("h2DdecayRadiusAntiLambda", "h2DdecayRadiusAntiLambda", - {HistType::kTH1D, {{100, 0.0f, 1.0f, "c#tau (cm)"}}}); + {HistType::kTH1D, {decayRadiusAxis}}); rAntiLambda.add("hMassAntiLambdapT", "hMassAntiLambdapT", - {HistType::kTH2D, {{antilambdaMassAxis}, {ptAxis}}}); + {HistType::kTH2D, {{antilambdaMassAxis}, {ptLambdaAxis}}}); rAntiLambda.add("hMassAntiLambdapTFlat", "hMassAntiLambdapTFlat", - {HistType::kTH3D, {{antilambdaMassAxis}, {ptAxis}, {flatAxis}}}); + {HistType::kTH3D, {{antilambdaMassAxis}, {ptLambdaAxis}, {flatAxis}}}); if (doprocessRecMCLambdaK0s) { rAntiLambda.add("Generated_MCRecoCollCheck_INEL_AntiLambda", "Generated_MCRecoCollCheck_INEL_AntiLambda", - {HistType::kTH2D, {{ptAxis}, {flatAxis}}}); + {HistType::kTH2D, {{ptLambdaAxis}, {flatAxis}}}); + rAntiLambda.add("Generated_MCRecoCollCheck_INELgt0_AntiLambda", "Generated_MCRecoCollCheck_INELgt0_AntiLambda", + {HistType::kTH2D, {{ptLambdaAxis}, {flatAxis}}}); + rAntiLambda.add("hMassFeedDownAntiLambdapTFlat", "hMassFeedDownAntiLambdapTFlat", + {HistType::kTH3D, {{antilambdaMassAxis}, {ptLambdaAxis}, {flatAxis}}}); + rAntiLambda.add("hFeedDownAntiLambdaPtVsMotherPt", "hFeedDownAntiLambdaPtVsMotherPt", + {HistType::kTH2D, {{ptLambdaAxis}, {ptLambdaAxis}}}); + rAntiLambda.add("hFeedDownAntiLambdaMatrix", "hFeedDownAntiLambdaMatrix", + {HistType::kTHnSparseF, {{ptLambdaAxis}, {ptLambdaAxis}, {flatAxis}, {motherAxis}}}); + rAntiLambda.add("hFeedDownAntiLambdaMotherPdg", "hFeedDownAntiLambdaMotherPdg", + {HistType::kTH1D, {{motherAxis}}}); + rAntiLambda.get(HIST("hFeedDownAntiLambdaMotherPdg"))->GetXaxis()->SetBinLabel(1, "#Xi^{-}"); + rAntiLambda.get(HIST("hFeedDownAntiLambdaMotherPdg"))->GetXaxis()->SetBinLabel(2, "#Xi^{0}"); + rAntiLambda.get(HIST("hFeedDownAntiLambdaMotherPdg"))->GetXaxis()->SetBinLabel(3, "#Omega^{-}"); + rAntiLambda.get(HIST("hFeedDownAntiLambdaMotherPdg"))->GetXaxis()->SetBinLabel(4, "other"); } rCommonHist.add("hArmPodoAlphavsQT", "hArmPodoAlphavsQT", {HistType::kTH2D, {{200, -1, 1, "#alpha"}, {70, 0, 0.35, "Q_{T}"}}}); + + // DCA of the V0 to the PV: primaries peak at zero, feed-down does not, so + // the data distribution can be fitted with the two MC templates + rLambda.add("hDcaV0ToPVLambda", "hDcaV0ToPVLambda", + {HistType::kTH3D, {{dcaV0ToPvAxis}, {ptLambdaAxis}, {flatAxis}}}); + rAntiLambda.add("hDcaV0ToPVAntiLambda", "hDcaV0ToPVAntiLambda", + {HistType::kTH3D, {{dcaV0ToPvAxis}, {ptLambdaAxis}, {flatAxis}}}); + if (doprocessRecMCLambdaK0s) { + rLambda.add("hDcaV0ToPVLambdaPrim", "hDcaV0ToPVLambdaPrim", + {HistType::kTH3D, {{dcaV0ToPvAxis}, {ptLambdaAxis}, {flatAxis}}}); + rLambda.add("hDcaV0ToPVLambdaSec", "hDcaV0ToPVLambdaSec", + {HistType::kTH3D, {{dcaV0ToPvAxis}, {ptLambdaAxis}, {flatAxis}}}); + rAntiLambda.add("hDcaV0ToPVAntiLambdaPrim", "hDcaV0ToPVAntiLambdaPrim", + {HistType::kTH3D, {{dcaV0ToPvAxis}, {ptLambdaAxis}, {flatAxis}}}); + rAntiLambda.add("hDcaV0ToPVAntiLambdaSec", "hDcaV0ToPVAntiLambdaSec", + {HistType::kTH3D, {{dcaV0ToPvAxis}, {ptLambdaAxis}, {flatAxis}}}); + + rKzeroShort.add("hPtResK0s", "hPtResK0s", {HistType::kTH2D, {{ptK0sAxis}, {ptResAxis}}}); + rLambda.add("hPtResLambda", "hPtResLambda", {HistType::kTH2D, {{ptLambdaAxis}, {ptResAxis}}}); + rAntiLambda.add("hPtResAntiLambda", "hPtResAntiLambda", {HistType::kTH2D, {{ptLambdaAxis}, {ptResAxis}}}); + + // numerator classified by the measured 1-rho, denominator by the true + // one: the pair is the MC closure + rKzeroShort.add("Generated_MCRecoCollCheck_INELgt0_K0Short_TrueFlat", "Generated_MCRecoCollCheck_INELgt0_K0Short_TrueFlat", + {HistType::kTH2D, {{ptK0sAxis}, {flatTrueAxis}}}); + rLambda.add("Generated_MCRecoCollCheck_INELgt0_Lambda_TrueFlat", "Generated_MCRecoCollCheck_INELgt0_Lambda_TrueFlat", + {HistType::kTH2D, {{ptLambdaAxis}, {flatTrueAxis}}}); + rAntiLambda.add("Generated_MCRecoCollCheck_INELgt0_AntiLambda_TrueFlat", "Generated_MCRecoCollCheck_INELgt0_AntiLambda_TrueFlat", + {HistType::kTH2D, {{ptLambdaAxis}, {flatTrueAxis}}}); + rKzeroShort.add("hMassK0spTTrueFlat", "hMassK0spTTrueFlat", + {HistType::kTH3D, {{k0sMassAxis}, {ptK0sAxis}, {flatTrueAxis}}}); + rLambda.add("hMassLambdapTTrueFlat", "hMassLambdapTTrueFlat", + {HistType::kTH3D, {{lambdaMassAxis}, {ptLambdaAxis}, {flatTrueAxis}}}); + rAntiLambda.add("hMassAntiLambdapTTrueFlat", "hMassAntiLambdapTTrueFlat", + {HistType::kTH3D, {{antilambdaMassAxis}, {ptLambdaAxis}, {flatTrueAxis}}}); + } } if (doprocessRecMCRun3Cascade || doprocessDataRun3Cascade) { @@ -406,24 +739,45 @@ struct Lambdak0sflattenicity { {HistType::kTH1D, {{40, -2.0f, 2.0f, "y"}}}); rXi.add("hctauXi", "hctauXi", {HistType::kTH1D, {{40, 0.0f, 40.0f, "c#tau (cm)"}}}); - rXi.add( - "h2DdecayRadiusXi", "h2DdecayRadiusXi", - {HistType::kTH1D, {{100, 0.0f, 1.0f, "Decay Radius (cm)"}}}); + rXi.add("h2DdecayRadiusXi", "h2DdecayRadiusXi", + {HistType::kTH1D, {decayRadiusAxis}}); rXi.add("hDCAV0DaughtersXi", "hDCAV0DaughtersXi", {HistType::kTH1D, {{55, 0.0f, 2.2f, "DCA Daughters"}}}); rXi.add("hV0CosPAXi", "hV0CosPAXi", {HistType::kTH1D, {{100, 0.95f, 1.f, "CosPA"}}}); - rXi.add("hNSigmaPosPionFromXi", "hNSigmaPosPionFromXi", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); - rXi.add("hNSigmaNegPionFromXi", "hNSigmaNegPionFromXi", - {HistType::kTH2D, {{100, -5.f, 5.f}, {ptAxis}}}); + rXi.add("hNSigmaProtonFromXi", "hNSigmaProtonFromXi", + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); + rXi.add("hNSigmaPionFromXi", "hNSigmaPionFromXi", + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); + rXi.add("hNSigmaBachPionFromXi", "hNSigmaBachPionFromXi", + {HistType::kTH2D, {{100, -5.f, 5.f, "n#sigma_{TPC}"}, {pTPCAxis}}}); rXi.add("hMassXipT", "hMassXipT", - {HistType::kTH2D, {{xiMassAxis}, {ptAxis}}}); + {HistType::kTH2D, {{xiMassAxis}, {ptXiAxis}}}); rXi.add("hMassXipTFlat", "hMassXipTFlat", - {HistType::kTH3D, {{xiMassAxis}, {ptAxis}, {flatAxis}}}); + {HistType::kTH3D, {{xiMassAxis}, {ptXiAxis}, {flatAxis}}}); if (doprocessRecMCRun3Cascade) { rXi.add("Generated_MCRecoCollCheck_INEL_Xi", "Generated_MCRecoCollCheck_INEL_Xi", - {HistType::kTH2D, {{ptAxis}, {flatAxis}}}); + {HistType::kTH2D, {{ptXiAxis}, {flatAxis}}}); + rXi.add("Generated_MCRecoCollCheck_INELgt0_Xi", "Generated_MCRecoCollCheck_INELgt0_Xi", + {HistType::kTH2D, {{ptXiAxis}, {flatAxis}}}); + rXi.add("hMassFeedDownXipTFlat", "hMassFeedDownXipTFlat", + {HistType::kTH3D, {{xiMassAxis}, {ptXiAxis}, {flatAxis}}}); + rXi.add("hFeedDownXiPtVsMotherPt", "hFeedDownXiPtVsMotherPt", + {HistType::kTH2D, {{ptXiAxis}, {ptXiAxis}}}); + rXi.add("hFeedDownXiMatrix", "hFeedDownXiMatrix", + {HistType::kTHnSparseF, {{ptXiAxis}, {ptXiAxis}, {flatAxis}, {motherAxis}}}); + rXi.add("hFeedDownXiMotherPdg", "hFeedDownXiMotherPdg", + {HistType::kTH1D, {{motherAxis}}}); + rXi.get(HIST("hFeedDownXiMotherPdg"))->GetXaxis()->SetBinLabel(1, "#Xi^{-}"); + rXi.get(HIST("hFeedDownXiMotherPdg"))->GetXaxis()->SetBinLabel(2, "#Xi^{0}"); + rXi.get(HIST("hFeedDownXiMotherPdg"))->GetXaxis()->SetBinLabel(3, "#Omega^{-}"); + rXi.get(HIST("hFeedDownXiMotherPdg"))->GetXaxis()->SetBinLabel(4, "other"); + + rXi.add("hPtResXi", "hPtResXi", {HistType::kTH2D, {{ptXiAxis}, {ptResAxis}}}); + rXi.add("Generated_MCRecoCollCheck_INELgt0_Xi_TrueFlat", "Generated_MCRecoCollCheck_INELgt0_Xi_TrueFlat", + {HistType::kTH2D, {{ptXiAxis}, {flatTrueAxis}}}); + rXi.add("hMassXipTTrueFlat", "hMassXipTTrueFlat", + {HistType::kTH3D, {{xiMassAxis}, {ptXiAxis}, {flatTrueAxis}}}); } } if (doprocessGenMC) { @@ -434,7 +788,7 @@ struct Lambdak0sflattenicity { {HistType::kTH1D, {vertexZAxis}}); rEventSelection.add("hFlattenicityDistributionMCGen", "hFlattenicityDistributionMCGen", - {HistType::kTH1D, {flatAxis}}); + {HistType::kTH1D, {flatTrueAxis}}); rEventSelection.add("hFlattenicityDistributionRecMCGen", "hFlattenicityDistributionRecMCGen", {HistType::kTH1D, {flatAxis}}); @@ -452,93 +806,124 @@ struct Lambdak0sflattenicity { rEventSelection.add("hNEventsMCGenReco", "hNEventsMCGenReco", {HistType::kTH1D, {{2, 0.f, 2.f}}}); rEventSelection.get(HIST("hNEventsMCGenReco"))->GetXaxis()->SetBinLabel(1, "INEL"); rEventSelection.get(HIST("hNEventsMCGenReco"))->GetXaxis()->SetBinLabel(2, "INELgt0"); - rEventSelection.add("hNEventsMCReco", "hNEventsMCReco", {HistType::kTH1D, {{4, 0.f, 4.f}}}); + rEventSelection.add("hNEventsMCReco", "hNEventsMCReco", {HistType::kTH1D, {{3, 0.f, 3.f}}}); rEventSelection.get(HIST("hNEventsMCReco"))->GetXaxis()->SetBinLabel(1, "all"); rEventSelection.get(HIST("hNEventsMCReco"))->GetXaxis()->SetBinLabel(2, "pass ev sel"); rEventSelection.get(HIST("hNEventsMCReco"))->GetXaxis()->SetBinLabel(3, "INELgt0"); - rEventSelection.get(HIST("hNEventsMCReco"))->GetXaxis()->SetBinLabel(4, "check"); - rEventSelection.add("hTrueFV0amplvsFlat", "TrueFV0MvsFlat", HistType::kTH2D, - {{500, -0.5, +499.5, "True Nch in FV0 region"}, flatAxis}); - rKzeroShort.add("pGen_MCGenRecoColl_INEL_K0Short", "pGen_MCGenRecoColl_INEL_K0Short", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptK0sAxis, flatAxis}}); rKzeroShort.add("Generated_MCRecoColl_INEL_K0Short", "Generated_MCRecoColl_INEL_K0Short", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptK0sAxis, flatAxis}}); rKzeroShort.add("pGen_MCGenColl_INEL_K0Short", "pGen_MCGenColl_INEL_K0Short", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptK0sAxis, flatAxis}}); rKzeroShort.add("pGen_MCGenRecoColl_INELgt0_K0Short", "pGen_MCGenRecoColl_INELgt0_K0Short", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptK0sAxis, flatAxis}}); rKzeroShort.add("Generated_MCRecoColl_INELgt0_K0Short", "Generated_MCRecoColl_INELgt0_K0Short", - {HistType::kTH2D, {ptAxis, flatAxis}}); - rKzeroShort.add("Generated_MCRecoCollCheck_INELgt0_K0Short", "Generated_MCRecoCollCheck_INELgt0_K0Short", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptK0sAxis, flatAxis}}); rKzeroShort.add("pGen_MCGenColl_INELgt0_K0Short", "pGen_MCGenColl_INELgt0_K0Short", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptK0sAxis, flatAxis}}); rLambda.add("pGen_MCGenRecoColl_INEL_Lambda", "pGen_MCGenRecoColl_INEL_Lambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rLambda.add("Generated_MCRecoColl_INEL_Lambda", "Generated_MCRecoColl_INEL_Lambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rLambda.add("pGen_MCGenColl_INEL_Lambda", "pGen_MCGenColl_INEL_Lambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rLambda.add("pGen_MCGenRecoColl_INELgt0_Lambda", "pGen_MCGenRecoColl_INELgt0_Lambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rLambda.add("Generated_MCRecoColl_INELgt0_Lambda", "Generated_MCRecoColl_INELgt0_Lambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); - rLambda.add("Generated_MCRecoCollCheck_INELgt0_Lambda", "Generated_MCRecoCollCheck_INELgt0_Lambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rLambda.add("pGen_MCGenColl_INELgt0_Lambda", "pGen_MCGenColl_INELgt0_Lambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rAntiLambda.add("pGen_MCGenRecoColl_INEL_AntiLambda", "pGen_MCGenRecoColl_INEL_AntiLambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rAntiLambda.add("Generated_MCRecoColl_INEL_AntiLambda", "Generated_MCRecoColl_INEL_AntiLambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rAntiLambda.add("pGen_MCGenColl_INEL_AntiLambda", "pGen_MCGenColl_INEL_AntiLambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rAntiLambda.add("pGen_MCGenRecoColl_INELgt0_AntiLambda", "pGen_MCGenRecoColl_INELgt0_AntiLambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rAntiLambda.add("Generated_MCRecoColl_INELgt0_AntiLambda", "Generated_MCRecoColl_INELgt0_AntiLambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); - rAntiLambda.add("Generated_MCRecoCollCheck_INELgt0_AntiLambda", "Generated_MCRecoCollCheck_INELgt0_AntiLambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rAntiLambda.add("pGen_MCGenColl_INELgt0_AntiLambda", "pGen_MCGenColl_INELgt0_AntiLambda", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptLambdaAxis, flatAxis}}); rXi.add("pGen_MCGenRecoColl_INEL_Xi", "pGen_MCGenRecoColl_INEL_Xi", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptXiAxis, flatAxis}}); rXi.add("Generated_MCRecoColl_INEL_Xi", "Generated_MCRecoColl_INEL_Xi", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptXiAxis, flatAxis}}); rXi.add("pGen_MCGenColl_INEL_Xi", "pGen_MCGenColl_INEL_Xi", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptXiAxis, flatAxis}}); rXi.add("pGen_MCGenRecoColl_INELgt0_Xi", "pGen_MCGenRecoColl_INELgt0_Xi", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptXiAxis, flatAxis}}); rXi.add("Generated_MCRecoColl_INELgt0_Xi", "Generated_MCRecoColl_INELgt0_Xi", - {HistType::kTH2D, {ptAxis, flatAxis}}); - rXi.add("Generated_MCRecoCollCheck_INELgt0_Xi", "Generated_MCRecoCollCheck_INELgt0_Xi", - {HistType::kTH2D, {ptAxis, flatAxis}}); + {HistType::kTH2D, {ptXiAxis, flatAxis}}); rXi.add("pGen_MCGenColl_INELgt0_Xi", "pGen_MCGenColl_INELgt0_Xi", - {HistType::kTH2D, {ptAxis, flatAxis}}); - } - - if (flattenicityQA) { + {HistType::kTH2D, {ptXiAxis, flatAxis}}); + + // The same INEL>0 counters against the true 1-rho. Together with the + // reconstructed ones above they give the closure without a second job. + rEventSelection.add("hFlat_RecoColl_MC_INELgt0_TrueFlat", "hFlat_RecoColl_MC_INELgt0_TrueFlat", + {HistType::kTH1D, {flatTrueAxis}}); + rEventSelection.add("hFlat_GenRecoColl_MC_INELgt0_TrueFlat", "hFlat_GenRecoColl_MC_INELgt0_TrueFlat", + {HistType::kTH1D, {flatTrueAxis}}); + rEventSelection.add("hFlat_GenColl_MC_INELgt0_TrueFlat", "hFlat_GenColl_MC_INELgt0_TrueFlat", + {HistType::kTH1D, {flatTrueAxis}}); + rKzeroShort.add("pGen_MCGenRecoColl_INELgt0_K0Short_TrueFlat", "pGen_MCGenRecoColl_INELgt0_K0Short_TrueFlat", + {HistType::kTH2D, {ptK0sAxis, flatTrueAxis}}); + rKzeroShort.add("Generated_MCRecoColl_INELgt0_K0Short_TrueFlat", "Generated_MCRecoColl_INELgt0_K0Short_TrueFlat", + {HistType::kTH2D, {ptK0sAxis, flatTrueAxis}}); + rKzeroShort.add("pGen_MCGenColl_INELgt0_K0Short_TrueFlat", "pGen_MCGenColl_INELgt0_K0Short_TrueFlat", + {HistType::kTH2D, {ptK0sAxis, flatTrueAxis}}); + rLambda.add("pGen_MCGenRecoColl_INELgt0_Lambda_TrueFlat", "pGen_MCGenRecoColl_INELgt0_Lambda_TrueFlat", + {HistType::kTH2D, {ptLambdaAxis, flatTrueAxis}}); + rLambda.add("Generated_MCRecoColl_INELgt0_Lambda_TrueFlat", "Generated_MCRecoColl_INELgt0_Lambda_TrueFlat", + {HistType::kTH2D, {ptLambdaAxis, flatTrueAxis}}); + rLambda.add("pGen_MCGenColl_INELgt0_Lambda_TrueFlat", "pGen_MCGenColl_INELgt0_Lambda_TrueFlat", + {HistType::kTH2D, {ptLambdaAxis, flatTrueAxis}}); + rAntiLambda.add("pGen_MCGenRecoColl_INELgt0_AntiLambda_TrueFlat", "pGen_MCGenRecoColl_INELgt0_AntiLambda_TrueFlat", + {HistType::kTH2D, {ptLambdaAxis, flatTrueAxis}}); + rAntiLambda.add("Generated_MCRecoColl_INELgt0_AntiLambda_TrueFlat", "Generated_MCRecoColl_INELgt0_AntiLambda_TrueFlat", + {HistType::kTH2D, {ptLambdaAxis, flatTrueAxis}}); + rAntiLambda.add("pGen_MCGenColl_INELgt0_AntiLambda_TrueFlat", "pGen_MCGenColl_INELgt0_AntiLambda_TrueFlat", + {HistType::kTH2D, {ptLambdaAxis, flatTrueAxis}}); + rXi.add("pGen_MCGenRecoColl_INELgt0_Xi_TrueFlat", "pGen_MCGenRecoColl_INELgt0_Xi_TrueFlat", + {HistType::kTH2D, {ptXiAxis, flatTrueAxis}}); + rXi.add("Generated_MCRecoColl_INELgt0_Xi_TrueFlat", "Generated_MCRecoColl_INELgt0_Xi_TrueFlat", + {HistType::kTH2D, {ptXiAxis, flatTrueAxis}}); + rXi.add("pGen_MCGenColl_INELgt0_Xi_TrueFlat", "pGen_MCGenColl_INELgt0_Xi_TrueFlat", + {HistType::kTH2D, {ptXiAxis, flatTrueAxis}}); + } + + if (flatSel.flattenicityQA) { rFlattenicity.add("hEv", "Ev", HistType::kTH1D, {{6, -0.5, 5.5, "index activated detector"}}); rFlattenicity.add("hFV0amplRing1to4", "FV01to4", HistType::kTH1D, {{4000, -0.5, +49999.5, "FV0 amplitude"}}); rFlattenicity.add("hFT0Aampl", "FTAampl", HistType::kTH1D, - {{50000, -0.5, +199999.5, "FT0A amplitude"}}); + {{2000, -0.5, +199999.5, "Summed FT0A amplitude"}}); rFlattenicity.add("hFT0Campl", "FTCampl", HistType::kTH1D, - {{10000, -0.5, +4999.5, "FT0C amplitude"}}); + {{2000, -0.5, +99999.5, "Summed FT0C amplitude"}}); rFlattenicity.add("hFT0C", "FT0C", HistType::kTH1D, - {{50000, -0.5, 199999.5, "FT0C amplitudes"}}); + {{2000, -0.5, 1999.5, "FT0C amplitude per channel"}}); rFlattenicity.add("hFT0A", "FT0A", HistType::kTH1D, - {{2000, -0.5, 1999.5, "FT0A amplitudes"}}); + {{2000, -0.5, 1999.5, "FT0A amplitude per channel"}}); + // lattice occupancy, what the minLitCells cuts are tuned on + rFlattenicity.add("hNActiveFT0Channels", "hNActiveFT0Channels", HistType::kTH1D, + {{kNChannelsFT0 + 1, -0.5, kNChannelsFT0 + 0.5, + "FT0 channels with amplitude > 0"}}); + rFlattenicity.add("hNActiveCellsFV0", "hNActiveCellsFV0", HistType::kTH1D, + {{kNCells + 1, -0.5, kNCells + 0.5, "occupied FV0 lattice cells"}}); + rFlattenicity.add("hNActiveCellsFT0A", "hNActiveCellsFT0A", HistType::kTH1D, + {{kNChannelsFT0A + 1, -0.5, kNChannelsFT0A + 0.5, "occupied FT0A lattice cells"}}); + rFlattenicity.add("hNActiveCellsFT0C", "hNActiveCellsFT0C", HistType::kTH1D, + {{kNChannelsFT0C + 1, -0.5, kNChannelsFT0C + 0.5, "occupied FT0C lattice cells"}}); rFlattenicity.add("hFV0amplvsFlat", "FV0MvsFlat", HistType::kTH2D, {{4000, -0.5, +49999.5, "FV0 amplitude"}, flatAxis}); // estimators - for (int iEe = 0; iEe < 8; ++iEe) { + for (int iEe = 0; iEe < kNEstimators; ++iEe) { rFlattenicity.add( kHEst[iEe].data(), "", HistType::kTH2D, {{nBinsEst[iEe], lowEdgeEst[iEe], upEdgeEst[iEe], kTEst[iEe].data()}, @@ -546,7 +931,7 @@ struct Lambdak0sflattenicity { } // vs pT - for (int iEe = 0; iEe < 8; ++iEe) { + for (int iEe = 0; iEe < kNEstimators; ++iEe) { rFlattenicity.add( kHPtEst[iEe].data(), "", HistType::kTProfile, {{nBinsEst[iEe], lowEdgeEst[iEe], upEdgeEst[iEe], kTEst[iEe].data()}}); @@ -610,149 +995,74 @@ struct Lambdak0sflattenicity { if ((doprocessDataRun3LambdaK0s || doprocessDataRun3Cascade) && doprocessGenMC) { LOGF(fatal, "Can not run MCGen and Data process functions together. Try one of these at a time"); } + + // the spectra passes come first, their event counts normalise the yields + if (doprocessRecMCLambdaK0s) { + eventHistOwner = kOwnerRecMCV0; + } else if (doprocessDataRun3LambdaK0s) { + eventHistOwner = kOwnerDataV0; + } else if (doprocessRecMCRun3Cascade) { + eventHistOwner = kOwnerRecMCCasc; + } else if (doprocessDataRun3Cascade) { + eventHistOwner = kOwnerDataCasc; + } else if (doprocessFlatDistMC) { + eventHistOwner = kOwnerFlatDistMC; + } else if (doprocessFlatDistData) { + eventHistOwner = kOwnerFlatDistData; + } + + // tied to the owner so a new process function cannot bring the double fill back + fillFlattenicityQAInGenMC = (eventHistOwner == kOwnerNone); + + // both estimators have a generator-level counterpart, so only check the switch + if (flatSel.flattenicityforanalysis != kFlatFromFV0 && + flatSel.flattenicityforanalysis != kFlatFromFT0) { + LOGF(fatal, "flattenicityforanalysis must be 0 (FV0) or 1 (FT0)"); + } + if (flatSel.flattenicityforanalysis == kFlatFromFV0 && !flatSel.isflattenicitywithFV0) { + LOGF(fatal, "flattenicityforanalysis=0 (FV0) needs isflattenicitywithFV0 enabled"); + } + if (flatSel.flattenicityforanalysis == kFlatFromFT0 && !flatSel.isflattenicitywithFT0) { + LOGF(fatal, "flattenicityforanalysis=1 (FT0) needs isflattenicitywithFT0 enabled"); + } } + // FT0 sectors group kNChannelsPerT0Sector consecutive channels int getT0ASector(int iCh) { - int iSecT0a = -1; - for (int iSec = 0; iSec < 24; ++iSec) { - if (iCh >= 4 * iSec && iCh <= 3 + 4 * iSec) { - iSecT0a = iSec; - break; - } - } - return iSecT0a; + const int iSec = iCh / kNChannelsPerT0Sector; + return (iCh >= 0 && iSec < kNSectorsT0A) ? iSec : -1; } int getT0CSector(int iCh) { - int iSecT0c = -1; - for (int iSec = 0; iSec < 28; ++iSec) { - if (iCh >= 4 * iSec && iCh <= 3 + 4 * iSec) { - iSecT0c = iSec; - break; - } - } - return iSecT0c; + const int iSec = iCh / kNChannelsPerT0Sector; + return (iCh >= 0 && iSec < kNSectorsT0C) ? iSec : -1; } + // four inner rings of kNChannelsPerFV0Ring channels each, the rest is the outer ring int getFV0Ring(int iCh) { - int iRing = -1; - if (iCh < 8) { - iRing = 0; - } else if (iCh >= 8 && iCh < 16) { - iRing = 1; - } else if (iCh >= 16 && iCh < 24) { - iRing = 2; - } else if (iCh >= 24 && iCh < 32) { - iRing = 3; - } else { - iRing = 4; + const int iRing = iCh / kNChannelsPerFV0Ring; + if (iRing < 0) { + return 0; } - return iRing; + return (iRing < kOuterFV0RingIndex) ? iRing : kOuterFV0RingIndex; } + // FV0 channel -> phi-ordered index, a fixed permutation of the 48 channels int getFV0IndexPhi(int iCh) { - int iRing = -1; - - if (iCh >= 0 && iCh < 8) { - if (iCh < 4) { - iRing = iCh; - } else { - if (iCh == 7) { - iRing = 4; - } else if (iCh == 6) { - iRing = 5; - } else if (iCh == 5) { - iRing = 6; - } else if (iCh == 4) { - iRing = 7; - } - } - } else if (iCh >= 8 && iCh < 16) { - if (iCh < 12) { - iRing = iCh; - } else { - if (iCh == 15) { - iRing = 12; - } else if (iCh == 14) { - iRing = 13; - } else if (iCh == 13) { - iRing = 14; - } else if (iCh == 12) { - iRing = 15; - } - } - } else if (iCh >= 16 && iCh < 24) { - if (iCh < 20) { - iRing = iCh; - } else { - if (iCh == 23) { - iRing = 20; - } else if (iCh == 22) { - iRing = 21; - } else if (iCh == 21) { - iRing = 22; - } else if (iCh == 20) { - iRing = 23; - } - } - } else if (iCh >= 24 && iCh < 32) { - if (iCh < 28) { - iRing = iCh; - } else { - if (iCh == 31) { - iRing = 28; - } else if (iCh == 30) { - iRing = 29; - } else if (iCh == 29) { - iRing = 30; - } else if (iCh == 28) { - iRing = 31; - } - } - } else if (iCh == 32) { - iRing = 32; - } else if (iCh == 40) { - iRing = 33; - } else if (iCh == 33) { - iRing = 34; - } else if (iCh == 41) { - iRing = 35; - } else if (iCh == 34) { - iRing = 36; - } else if (iCh == 42) { - iRing = 37; - } else if (iCh == 35) { - iRing = 38; - } else if (iCh == 43) { - iRing = 39; - } else if (iCh == 47) { - iRing = 40; - } else if (iCh == 39) { - iRing = 41; - } else if (iCh == 46) { - iRing = 42; - } else if (iCh == 38) { - iRing = 43; - } else if (iCh == 45) { - iRing = 44; - } else if (iCh == 37) { - iRing = 45; - } else if (iCh == 44) { - iRing = 46; - } else if (iCh == 36) { - iRing = 47; - } - return iRing; + if (iCh < 0 || iCh >= kNCells) { + return -1; + } + return kFV0PhiIndex[iCh]; } float getFlatenicity(std::span signals) { int entries = signals.size(); - float flat = 9999; + float flat = kFlatUndefined; float mRho = 0; for (int iCell = 0; iCell < entries; ++iCell) { mRho += 1.0 * signals[iCell]; @@ -771,207 +1081,419 @@ struct Lambdak0sflattenicity { } return flat; } - float pdgmassK0s = 0.497614; - float pdgmassLambda = 1.115683; - float pdgmassXi = 1.3217; - float pdgmassOmega = 1.67243; - // V0A signal and flatenicity calculation - static constexpr float kCalib[48] = { - 1.01697, 1.122, 1.03854, 1.108, 1.11634, 1.14971, 1.19321, - 1.06866, 0.954675, 0.952695, 0.969853, 0.957557, 0.989784, 1.01549, - 1.02182, 0.976005, 1.01865, 1.06871, 1.06264, 1.02969, 1.07378, - 1.06622, 1.15057, 1.0433, 0.83654, 0.847178, 0.890027, 0.920814, - 0.888271, 1.04662, 0.8869, 0.856348, 0.863181, 0.906312, 0.902166, - 1.00122, 1.03303, 0.887866, 0.892437, 0.906278, 0.884976, 0.864251, - 0.917221, 1.10618, 1.04028, 0.893184, 0.915734, 0.892676}; - // calibration T0C - static constexpr float kCalibT0C[28] = { - 0.949829, 1.05408, 1.00681, 1.00724, 0.990663, 0.973571, 0.9855, - 1.03726, 1.02526, 1.00467, 0.983008, 0.979349, 0.952352, 0.985775, - 1.013, 1.01721, 0.993948, 0.996421, 0.971871, 1.02921, 0.989641, - 1.01885, 1.01259, 0.929502, 1.03969, 1.02496, 1.01385, 1.01711}; - // calibration T0A - static constexpr float kCalibT0A[24] = { - 0.86041, 1.10607, 1.17724, 0.756397, 1.14954, 1.0879, - 0.829438, 1.09014, 1.16515, 0.730077, 1.06722, 0.906344, - 0.824167, 1.14716, 1.20692, 0.755034, 1.11734, 1.00556, - 0.790522, 1.09138, 1.16225, 0.692458, 1.12428, 1.01127}; - // calibration factor MFT vs vtx - static constexpr float kBiningVtxt[30] = { - -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, -7.5, -6.5, -5.5, - -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, - 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5}; - - // calibration factor FV0 vs vtx - static constexpr float kCalibFV0vtx[30] = { - 0.907962, 0.934607, 0.938929, 0.950987, 0.950817, 0.966362, - 0.968509, 0.972741, 0.982412, 0.984872, 0.994543, 0.996003, - 0.99435, 1.00266, 0.998245, 1.00584, 1.01078, 1.01003, - 1.00726, 1.00872, 1.01726, 1.02015, 1.0193, 1.01106, - 1.02229, 1.02104, 1.03435, 1.00822, 1.01921, 1.01736}; - // calibration FT0A vs vtx - static constexpr float kCalibFT0Avtx[30] = { - 0.924334, 0.950988, 0.959604, 0.965607, 0.970016, 0.979057, - 0.978384, 0.982005, 0.992825, 0.990048, 0.998588, 0.997338, - 1.00102, 1.00385, 0.99492, 1.01083, 1.00703, 1.00494, - 1.00063, 1.0013, 1.00777, 1.01238, 1.01179, 1.00577, - 1.01028, 1.017, 1.02975, 1.0085, 1.00856, 1.01662}; - // calibration FT0C vs vtx - static constexpr float kCalibFT0Cvtx[30] = { - 1.02096, 1.01245, 1.02148, 1.03605, 1.03561, 1.03667, - 1.04229, 1.0327, 1.03674, 1.02764, 1.01828, 1.02331, - 1.01864, 1.015, 1.01197, 1.00615, 0.996845, 0.993051, - 0.985635, 0.982883, 0.981914, 0.964635, 0.967812, 0.95475, - 0.956687, 0.932816, 0.92773, 0.914892, 0.891724, 0.872382}; + // occupied cells of a flattenicity lattice + template + static int countActiveCells(TArray const& lattice) + { + int nActive = 0; + for (const auto& cell : lattice) { + if (cell > 0.f) { + nActive++; + } + } + return nActive; + } static constexpr int kNeta5 = 2; // FT0C + FT0A - static constexpr float kWeigthsEta5[kNeta5] = {0.0490638, 0.010958415}; - static constexpr float kDeltaEeta5[kNeta5] = {1.1, 1.2}; + static constexpr std::array kWeigthsEta5 = {0.0490638, 0.010958415}; + static constexpr std::array kDeltaEeta5 = {1.1, 1.2}; static constexpr int kNeta6 = 2; // FT0C + FV0 - static constexpr float kWeigthsEta6[kNeta6] = {0.0490638, 0.00353962}; - static constexpr float kDeltaEeta6[kNeta6] = {1.1, 2.9}; + static constexpr std::array kWeigthsEta6 = {0.0490638, 0.00353962}; + static constexpr std::array kDeltaEeta6 = {1.1, 2.9}; static constexpr int kInnerFV0 = 32; static constexpr float kMaxEtaFV0 = 5.1; static constexpr float kMinEtaFV0 = 2.2; static constexpr float kDetaFV0 = (kMaxEtaFV0 - kMinEtaFV0) / 5.0; - static constexpr int kNCells = 48; // 48 sectors in FV0 - std::array rhoLattice; - std::array rhoLatticeFV0AMC; - std::array ampchannel; - std::array ampchannelBefore; - static constexpr int kNCellsT0A = 24; - std::array rhoLatticeT0A; - static constexpr int kNCellsT0C = 28; - std::array rhoLatticeT0C; - - std::array estimator; + // 1-rho not available for this collision + static constexpr float kInvalidFlattenicity = -1.f; + // lattice carries no signal, sigma/ undefined + static constexpr float kFlatUndefined = 9999.f; + static constexpr int kNCells = 48; // 48 sectors in FV0 + static constexpr std::array kFV0PhiIndex = { + 0, 1, 2, 3, 7, 6, 5, 4, + 8, 9, 10, 11, 15, 14, 13, 12, + 16, 17, 18, 19, 23, 22, 21, 20, + 24, 25, 26, 27, 31, 30, 29, 28, + 32, 34, 36, 38, 47, 45, 43, 41, + 33, 35, 37, 39, 46, 44, 42, 40}; + std::array rhoLattice{}; + std::array rhoLatticeFV0AMC{}; + std::array ampchannel{}; + std::array ampchannelBefore{}; + std::array rhoLatticeT0A{}; + std::array rhoLatticeT0C{}; + std::array rhoLatticeFT0AMC{}; + std::array rhoLatticeFT0CMC{}; + static constexpr int kNChannelsFT0 = kNChannelsFT0A + kNChannelsFT0C; + + // Generator-level FT0 lattice: detector cell counts and acceptance. The real + // channel map is not an eta-phi grid, so cell shapes are approximate. + static constexpr float kMinEtaFT0A = 3.5f; + static constexpr float kMaxEtaFT0A = 4.9f; + static constexpr float kMinEtaFT0C = -3.3f; + static constexpr float kMaxEtaFT0C = -2.1f; + static constexpr int kNEtaBinsFT0MC = 4; + static constexpr int kNPhiBinsFT0AMC = kNChannelsFT0A / kNEtaBinsFT0MC; + static constexpr int kNPhiBinsFT0CMC = kNChannelsFT0C / kNEtaBinsFT0MC; + + std::array estimator{}; + + // fillCounter=false runs the same cuts without filling hEventsSelected template - bool isEventSelected(TCollision const& collision) + bool isEventSelected(TCollision const& collision, bool fillCounter = true) { float nbinev = 0.5; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } - if (issel8 && !collision.sel8()) { + if (evSel.issel8 && !collision.sel8()) { return false; } - if (issel8) { + if (evSel.issel8) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } - if (std::abs(collision.posZ()) > cutzvertex) { + if (std::abs(collision.posZ()) > evSel.cutzvertex) { return false; } nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } - if (isNoTimeFrameBorder && + if (evSel.isNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { return false; } - if (isNoTimeFrameBorder) { + if (evSel.isNoTimeFrameBorder) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } - if (isNoITSROFrameBorder && + if (evSel.isNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { return false; } - if (isNoITSROFrameBorder) { + if (evSel.isNoITSROFrameBorder) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } - if (isVertexITSTPC && + if (evSel.isVertexITSTPC && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { return false; } - if (isVertexITSTPC) { + if (evSel.isVertexITSTPC) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } - if (isNoSameBunchPileup && + if (evSel.isNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } - if (isNoSameBunchPileup) { + if (evSel.isNoSameBunchPileup) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } - if (isGoodZvtxFT0vsPV && + if (evSel.isGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } - if (isGoodZvtxFT0vsPV) { + if (evSel.isGoodZvtxFT0vsPV) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } - if (isTriggerTVX && + if (evSel.isTriggerTVX && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { return false; } - if (isTriggerTVX) { + if (evSel.isTriggerTVX) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } - if (isINELgt0 && (collision.isInelGt0() == false)) { + if (eventClass.applyCentSel && (collision.centFT0M() < eventClass.cfgCentMin || collision.centFT0M() > eventClass.cfgCentMax)) { return false; } - if (isINELgt0) { + + if (evSel.isINELgt0 && (collision.isInelGt0() == false)) { + return false; + } + if (evSel.isINELgt0) { nbinev++; - rEventSelection.fill(HIST("hEventsSelected"), nbinev); + if (fillCounter) { + rEventSelection.fill(HIST("hEventsSelected"), nbinev); + } } return true; } - // ============== Flattenicity estimation begins ===================== // - template - float estimateFlattenicity(TCollision const& collision, Tracks const& tracks) + // Daughter track acceptance and quality. The quality cuts are off by default + // and exist so a systematic variation can move them from the JSON. + template + bool isSelectedDaughterTrack(TTrack const& track, float minCrossedRows) + { + if (std::abs(track.eta()) > trkPid.cfgTrkEtaCut || track.pt() < trkPid.cfgTrkLowPtCut) { + return false; + } + if (track.tpcNClsCrossedRows() < minCrossedRows) { + return false; + } + if (trkPid.minCrossedRowsOverFindable > 0.f && + track.tpcCrossedRowsOverFindableCls() < trkPid.minCrossedRowsOverFindable) { + return false; + } + if (trkPid.maxTpcChi2NCl > 0.f && track.tpcChi2NCl() > trkPid.maxTpcChi2NCl) { + return false; + } + if (trkPid.maxItsChi2NCl > 0.f && track.itsChi2NCl() > trkPid.maxItsChi2NCl) { + return false; + } + if (trkPid.minItsNCls > 0 && static_cast(track.itsNCls()) < trkPid.minItsNCls) { + return false; + } + return true; + } + + // cut-flow bins for the stages inside estimateFlattenicity(); without these + // the events it drops leave no trace + void fillFlattenicityStages() + { + if (nbinFlatDetHit > 0 && flatStage >= kFlatStageLowOccupancy) { + rEventSelection.fill(HIST("hEventsSelected"), nbinFlatDetHit - 0.5); + } + if (nbinFlatOccupancy > 0 && flatStage >= kFlatStageNoSignal) { + rEventSelection.fill(HIST("hEventsSelected"), nbinFlatOccupancy - 0.5); + } + if (nbinFlattenicity > 0 && flatStage == kFlatStageOk) { + rEventSelection.fill(HIST("hEventsSelected"), nbinFlattenicity - 0.5); + } + } + + // 1-rho on the fine axis for the percentile boundaries, inclusive, INEL>0 and + // in the top FT0M class + void fillFlatDistRec(float flattenicity, float centFT0M, bool isInelGt0) + { + rEventSelection.fill(HIST("hFlatDistRec"), flattenicity); + rEventSelection.fill(HIST("hCentFT0MFine"), centFT0M); + if (isInelGt0) { + rEventSelection.fill(HIST("hFlatDistRecINELgt0"), flattenicity); + if (centFT0M < eventClass.cfgCentMaxHM) { + rEventSelection.fill(HIST("hFlatDistRecHM"), flattenicity); + } + } + } + + // processGenMC only. A negative centFT0M means no accepted reconstructed + // counterpart, so the collision has no FT0M class and stays out of the HM one. + void fillFlatDistGen(float flattenicityGen, float centFT0M, bool isInelGt0) { - const int nDetVtx = 3; - TGraph* gVtx[nDetVtx]; - const char* nameDet[nDetVtx] = {"AmpV0", "AmpT0A", "AmpT0C"}; + rEventSelection.fill(HIST("hFlatDistGen"), flattenicityGen); + if (isInelGt0) { + rEventSelection.fill(HIST("hFlatDistGenINELgt0"), flattenicityGen); + if (centFT0M >= 0.f && centFT0M < eventClass.cfgCentMaxHM) { + rEventSelection.fill(HIST("hFlatDistGenHM"), flattenicityGen); + } + } + } - float ampl5[kNeta5] = {0, 0}; - float ampl6[kNeta6] = {0, 0}; + // the same from the rec-level process functions, diagnostic only + void fillFlatDistGenInRec(float flattenicityGen, float centFT0M, bool isInelGt0) + { + rEventSelection.fill(HIST("hFlatDistGenInRec"), flattenicityGen); + if (isInelGt0) { + rEventSelection.fill(HIST("hFlatDistGenInRecINELgt0"), flattenicityGen); + if (centFT0M >= 0.f && centFT0M < eventClass.cfgCentMaxHM) { + rEventSelection.fill(HIST("hFlatDistGenInRecHM"), flattenicityGen); + } + } + } - for (int i_d = 0; i_d < nDetVtx; ++i_d) { - gVtx[i_d] = 0; - gVtx[i_d] = new TGraph(); + // Charged tracks in the tracking acceptance: per class is the + // scale factor of Qpp, and the DCAxy distribution carries the secondary + // contamination. + template + int fillChargedRec(TTracks const& tracks, float flattenicity) + { + if (!eventClass.fillChargedQA) { + return 0; } - for (int i_v = 0; i_v < 30; ++i_v) { - gVtx[0]->SetPoint(i_v, kBiningVtxt[i_v], kCalibFV0vtx[i_v]); + int nch = 0; + for (const auto& track : tracks) { + if (!track.isGlobalTrack() || std::abs(track.eta()) > eventClass.cfgEtaChargedCut) { + continue; + } + nch++; + rCharged.fill(HIST("hPtChVsFlat"), track.pt(), flattenicity); + if constexpr (isMC) { + if (!track.has_mcParticle()) { + continue; + } + const auto& mcParticle = track.mcParticle(); + if (mcParticle.isPhysicalPrimary()) { + rCharged.fill(HIST("hPtChVsFlatRecPrim"), track.pt(), flattenicity); + rCharged.fill(HIST("hDcaXyPtChPrim"), track.dcaXY(), track.pt(), flattenicity); + } else { + rCharged.fill(HIST("hDcaXyPtChSec"), track.dcaXY(), track.pt(), flattenicity); + } + } else { + rCharged.fill(HIST("hDcaXyPtCh"), track.dcaXY(), track.pt(), flattenicity); + } } - for (int i_v = 0; i_v < 30; ++i_v) { - gVtx[1]->SetPoint(i_v, kBiningVtxt[i_v], kCalibFT0Avtx[i_v]); + rCharged.fill(HIST("hNchVsFlat"), nch, flattenicity); + return nch; + } + + template + int fillChargedGen(TMcParticles const& mcParticles, float flattenicity, bool trueFlat) + { + if (!eventClass.fillChargedQA) { + return 0; } - for (int i_v = 0; i_v < 30; ++i_v) { - gVtx[2]->SetPoint(i_v, kBiningVtxt[i_v], kCalibFT0Cvtx[i_v]); + int nch = 0; + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary() || std::abs(mcParticle.eta()) > eventClass.cfgEtaChargedCut) { + continue; + } + auto pdgParticle = pdg->GetParticle(mcParticle.pdgCode()); + if (!(pdgParticle && std::abs(pdgParticle->Charge()) > kMinCharge)) { + continue; + } + nch++; + if (trueFlat) { + rCharged.fill(HIST("hPtChVsTrueFlatGen"), mcParticle.pt(), flattenicity); + } else { + rCharged.fill(HIST("hPtChVsFlatGen"), mcParticle.pt(), flattenicity); + } + } + if (trueFlat) { + rCharged.fill(HIST("hNchVsTrueFlatGen"), nch, flattenicity); + } else { + rCharged.fill(HIST("hNchVsFlatGen"), nch, flattenicity); + } + return nch; + } + + // ================= Calibration objects from CCDB ==================== // + // A missing or malformed object falls back to unity, so a run without + // calibration is left uncorrected instead of being equalized with somebody + // else's constants. + + std::vector fetchGainEq(const std::string& path, int run, std::size_t nChannels) + { + const auto* obj = ccdb->getForRun>(path, run); + if (!obj || obj->size() != nChannels) { + LOGF(warning, "No gain equalization of size %zu at %s for run %d, using unity", + nChannels, path.c_str(), run); + return std::vector(nChannels, 1.f); + } + return *obj; + } + + TProfile2D* fetchVtxEq(const std::string& path, int run) + { + auto* obj = ccdb->getForRun(path, run); + if (!obj) { + LOGF(warning, "No z-vertex equalization at %s for run %d, lattice left uncorrected", + path.c_str(), run); } + return obj; + } - for (int i_d = 0; i_d < nDetVtx; ++i_d) { - gVtx[i_d]->SetName(Form("g%s", nameDet[i_d])); + // called per collision, does nothing unless a correction is switched on + template + void initCcdb(TBC const& bc) + { + const int run = bc.runNumber(); + if (run == mRunNumber) { + return; } + mRunNumber = run; + if (flatSel.applyCalibCh) { + gainFV0 = fetchGainEq(ccdbConf.gainEqPath.value + "/FV0", run, kNCells); + gainFT0A = fetchGainEq(ccdbConf.gainEqPath.value + "/FT0A", run, kNSectorsT0A); + gainFT0C = fetchGainEq(ccdbConf.gainEqPath.value + "/FT0C", run, kNSectorsT0C); + } + if (flatSel.applyCalibVtx) { + vtxEqFV0 = fetchVtxEq(ccdbConf.vtxEqPath.value + "/FV0", run); + vtxEqFT0A = fetchVtxEq(ccdbConf.vtxEqPath.value + "/FT0A", run); + vtxEqFT0C = fetchVtxEq(ccdbConf.vtxEqPath.value + "/FT0C", run); + } + } + + // the gain is divided out, the convention of flattenictyPikp + static float gainEqFactor(std::vector const& gain, std::size_t channel) + { + if (channel >= gain.size() || !(gain[channel] > 0.f)) { + return 1.f; + } + return gain[channel]; + } + + // 1 for a missing map or an empty bin, so a bad bin never zeroes a cell + static float vtxEqFactor(TProfile2D const* map, int cell, float vtxZ) + { + if (!map) { + return 1.f; + } + const float factor = map->GetBinContent(map->GetXaxis()->FindBin(cell), + map->GetYaxis()->FindBin(vtxZ)); + return (factor > 0.f) ? factor : 1.f; + } + + // ============== Flattenicity estimation begins ===================== // + // fillQA=false skips the QA registry, for callers that would fill it twice + template + float estimateFlattenicity(TCollision const& collision, Tracks const& tracks, bool fillQA = true) + { + const bool flattenicityQAhere = flatSel.flattenicityQA && fillQA; + flatStage = kFlatStageNoDetector; + // a detector contributes only if it has a record and the config reads it + const bool hasFV0 = collision.has_foundFV0(); + const bool hasFT0 = collision.has_foundFT0(); + const bool fv0Read = flatSel.isflattenicitywithFV0 && hasFV0; + const bool ft0Read = flatSel.isflattenicitywithFT0 && hasFT0; + if (flatSel.applyCalibCh || flatSel.applyCalibVtx) { + initCcdb(collision.template bc_as>()); + } + std::array ampl5 = {0, 0}; + std::array ampl6 = {0, 0}; + auto vtxZ = collision.posZ(); float sumAmpFV0 = 0; float sumAmpFV01to4Ch = 0; + // gain-equalized but not yet vertex-equalized, the input to the z-vertex QA + float sumAmpFV0BeforeVtx = 0; ampchannel.fill(0.0); ampchannelBefore.fill(0.0); rhoLattice.fill(0); - if ((isflattenicitywithFV0 || isflattenicitywithFV0FT0C) && - collision.has_foundFV0()) { + if (fv0Read) { auto fv0 = collision.foundFV0(); for (std::size_t ich = 0; ich < fv0.amplitude().size(); ich++) { @@ -988,15 +1510,19 @@ struct Lambdak0sflattenicity { phiv0 = ((2.0 * channelv0phi) + 1 - 64.0) * constants::math::TwoPI / (32.0); } ampchannelBefore[channelv0phi] = amplCh; - if (applyCalibCh) { - amplCh *= kCalib[channelv0phi]; + if (flatSel.applyCalibCh) { + amplCh /= gainEqFactor(gainFV0, channelv0); + } + sumAmpFV0BeforeVtx += amplCh; + if (flatSel.applyCalibVtx) { + amplCh *= vtxEqFactor(vtxEqFV0, channelv0phi, vtxZ); } sumAmpFV0 += amplCh; - if (channelv0 >= 8) { // exclude the 1st ch, eta 2.2,4.52 + if (channelv0 >= kNChannelsPerFV0Ring) { // exclude the 1st ring, eta 2.2,4.52 sumAmpFV01to4Ch += amplCh; } - if (flattenicityQA) { + if (flattenicityQAhere) { rFlattenicity.fill(HIST("fEtaPhiFv0"), phiv0, etav0, amplCh); } ampchannel[channelv0phi] = amplCh; @@ -1007,20 +1533,14 @@ struct Lambdak0sflattenicity { } } - if (flattenicityQA) { - rFlattenicity.fill(HIST("hAmpV0vsVtxBeforeCalibration"), vtxZ, sumAmpFV0); - } - if (applyCalibVtx) { - sumAmpFV0 *= gVtx[0]->Eval(vtxZ); - sumAmpFV01to4Ch *= gVtx[0]->Eval(vtxZ); - } - if (flattenicityQA) { + if (flattenicityQAhere) { + rFlattenicity.fill(HIST("hAmpV0vsVtxBeforeCalibration"), vtxZ, sumAmpFV0BeforeVtx); rFlattenicity.fill(HIST("hAmpV0vsVtx"), vtxZ, sumAmpFV0); } } - float flattenicityfv0 = 9999; - if (isflattenicitywithFV0 || isflattenicitywithFV0FT0C) { + float flattenicityfv0 = kFlatUndefined; + if (fv0Read) { flattenicityfv0 = getFlatenicity({rhoLattice.data(), rhoLattice.size()}); } @@ -1040,82 +1560,106 @@ struct Lambdak0sflattenicity { // FT0 float sumAmpFT0A = 0.f; float sumAmpFT0C = 0.f; + int nActiveFT0Ch = 0; + float sumAmpFT0ABeforeVtx = 0.f; + float sumAmpFT0CBeforeVtx = 0.f; rhoLatticeT0A.fill(0); rhoLatticeT0C.fill(0); - if ((isflattenicitywithFT0 || isflattenicitywithFV0FT0C) && - collision.has_foundFT0()) { + if (ft0Read) { auto ft0 = collision.foundFT0(); - if (isflattenicitywithFT0) { - for (std::size_t i_a = 0; i_a < ft0.amplitudeA().size(); i_a++) { - float amplitude = ft0.amplitudeA()[i_a]; - uint8_t channel = ft0.channelA()[i_a]; - int sector = getT0ASector(channel); - if (sector >= 0 && sector < 24) { - rhoLatticeT0A[sector] += amplitude; - if (flattenicityQA) { - rFlattenicity.fill(HIST("hAmpT0AVsChBeforeCalibration"), sector, - amplitude); - } - if (applyCalibCh) { - amplitude *= kCalibT0A[sector]; - } - if (flattenicityQA) { - rFlattenicity.fill(HIST("hAmpT0AVsCh"), sector, amplitude); - } + for (std::size_t i_a = 0; i_a < ft0.amplitudeA().size(); i_a++) { + float amplitude = ft0.amplitudeA()[i_a]; + const int channel = ft0.channelA()[i_a]; + if (amplitude > 0.f) { + nActiveFT0Ch++; + } + const int sector = getT0ASector(channel); + float amplitudeBeforeVtx = amplitude; + if (channel >= 0 && channel < kNChannelsFT0A && sector >= 0) { + if (flattenicityQAhere) { + rFlattenicity.fill(HIST("hAmpT0AVsChBeforeCalibration"), sector, + amplitude); + } + if (flatSel.applyCalibCh) { + amplitude /= gainEqFactor(gainFT0A, sector); + } + if (flattenicityQAhere) { + rFlattenicity.fill(HIST("hAmpT0AVsCh"), sector, amplitude); } - sumAmpFT0A += amplitude; - if (flattenicityQA) { - rFlattenicity.fill(HIST("hFT0A"), amplitude); + amplitudeBeforeVtx = amplitude; + if (flatSel.applyCalibVtx) { + amplitude *= vtxEqFactor(vtxEqFT0A, sector, vtxZ); } + rhoLatticeT0A[channel] += amplitude; + } + sumAmpFT0A += amplitude; + sumAmpFT0ABeforeVtx += amplitudeBeforeVtx; + if (flattenicityQAhere) { + rFlattenicity.fill(HIST("hFT0A"), amplitude); } } for (std::size_t i_c = 0; i_c < ft0.amplitudeC().size(); i_c++) { float amplitude = ft0.amplitudeC()[i_c]; - sumAmpFT0C += amplitude; - uint8_t channel = ft0.channelC()[i_c]; - int sector = getT0CSector(channel); - if (sector >= 0 && sector < 28) { - rhoLatticeT0C[sector] += amplitude; - if (flattenicityQA) { + const int channel = ft0.channelC()[i_c]; + if (amplitude > 0.f) { + nActiveFT0Ch++; + } + const int sector = getT0CSector(channel); + float amplitudeBeforeVtx = amplitude; + if (channel >= 0 && channel < kNChannelsFT0C && sector >= 0) { + if (flattenicityQAhere) { rFlattenicity.fill(HIST("hAmpT0CVsChBeforeCalibration"), sector, amplitude); } - if (applyCalibCh) { - amplitude *= kCalibT0C[sector]; + if (flatSel.applyCalibCh) { + amplitude /= gainEqFactor(gainFT0C, sector); } - if (flattenicityQA) { + if (flattenicityQAhere) { rFlattenicity.fill(HIST("hAmpT0CVsCh"), sector, amplitude); } + amplitudeBeforeVtx = amplitude; + if (flatSel.applyCalibVtx) { + amplitude *= vtxEqFactor(vtxEqFT0C, sector, vtxZ); + } + rhoLatticeT0C[channel] += amplitude; } - if (flattenicityQA) { + sumAmpFT0C += amplitude; + sumAmpFT0CBeforeVtx += amplitudeBeforeVtx; + if (flattenicityQAhere) { rFlattenicity.fill(HIST("hFT0C"), amplitude); } } - if (flattenicityQA) { + if (flattenicityQAhere) { rFlattenicity.fill(HIST("hAmpT0AvsVtxBeforeCalibration"), vtxZ, - sumAmpFT0A); + sumAmpFT0ABeforeVtx); rFlattenicity.fill(HIST("hAmpT0CvsVtxBeforeCalibration"), vtxZ, - sumAmpFT0C); - } - if (applyCalibVtx) { - sumAmpFT0A *= gVtx[1]->Eval(vtxZ); - sumAmpFT0C *= gVtx[2]->Eval(vtxZ); - } - if (flattenicityQA) { + sumAmpFT0CBeforeVtx); rFlattenicity.fill(HIST("hAmpT0AvsVtx"), vtxZ, sumAmpFT0A); rFlattenicity.fill(HIST("hAmpT0CvsVtx"), vtxZ, sumAmpFT0C); } } - float flatenicityT0a = 9999; - if (isflattenicitywithFT0) { + const int nLitFV0 = countActiveCells(rhoLattice); + const int nLitFT0A = countActiveCells(rhoLatticeT0A); + const int nLitFT0C = countActiveCells(rhoLatticeT0C); + if (flattenicityQAhere) { + if (fv0Read) { + rFlattenicity.fill(HIST("hNActiveCellsFV0"), nLitFV0); + } + if (ft0Read) { + rFlattenicity.fill(HIST("hNActiveFT0Channels"), nActiveFT0Ch); + rFlattenicity.fill(HIST("hNActiveCellsFT0A"), nLitFT0A); + rFlattenicity.fill(HIST("hNActiveCellsFT0C"), nLitFT0C); + } + } + + float flatenicityT0a = kFlatUndefined; + float flatenicityT0c = kFlatUndefined; + if (ft0Read) { flatenicityT0a = getFlatenicity({rhoLatticeT0A.data(), rhoLatticeT0A.size()}); - } - float flatenicityT0c = 9999; - if (isflattenicitywithFT0 || isflattenicitywithFV0FT0C) { flatenicityT0c = getFlatenicity({rhoLatticeT0C.data(), rhoLatticeT0C.size()}); } @@ -1125,163 +1669,241 @@ struct Lambdak0sflattenicity { float combinedEstimator5 = 0; float combinedEstimator6 = 0; - for (int iEe = 0; iEe < 8; ++iEe) { + for (int iEe = 0; iEe < kNEstimators; ++iEe) { estimator[iEe] = 0; } - if (collision.has_foundFV0() && collision.has_foundFT0()) { - float allWeights = 0; - // option 5 - ampl5[0] = sumAmpFT0C; - ampl5[1] = sumAmpFT0A; - if (sumAmpFT0C > 0 && sumAmpFT0A > 0) { - isOKEstimator5 = true; - } - if (isOKEstimator5) { - if (applyNorm) { - allWeights = 0; - for (int i5 = 0; i5 < kNeta5; ++i5) { - combinedEstimator5 += - ampl5[i5] * kWeigthsEta5[i5] / kDeltaEeta5[i5]; - allWeights += kWeigthsEta5[i5]; - } - combinedEstimator5 /= allWeights; - } else { - for (int i5 = 0; i5 < kNeta5; ++i5) { - combinedEstimator5 += ampl5[i5] * kWeigthsEta5[i5]; - } + // FV0 and FT0 are missing on different collisions, so require only the + // detector the selected estimator reads + const bool detectorsOk = + (flatSel.flattenicityforanalysis == kFlatFromFT0) ? hasFT0 : hasFV0; + if (!detectorsOk) { + return kInvalidFlattenicity; + } + flatStage = kFlatStageLowOccupancy; + + // Reject a lattice too sparse for sigma/ to be continuous. Only the one + // the analysis estimator reads is tested; the other still fills QA. + const bool occupancyOk = + (flatSel.flattenicityforanalysis == kFlatFromFT0) + ? (nLitFT0A >= flatSel.minLitCellsFT0A && nLitFT0C >= flatSel.minLitCellsFT0C) + : (nLitFV0 >= flatSel.minLitCellsFV0); + if (!occupancyOk) { + return kInvalidFlattenicity; + } + flatStage = kFlatStageNoSignal; + + // an estimator with an unread detector holds a sentinel, keep it out of the QA + const bool estFV0Ok = fv0Read; + const bool estFT0Ok = ft0Read; + const bool estFV0FT0COk = fv0Read && ft0Read; + + float allWeights = 0; + // option 5 + ampl5[0] = sumAmpFT0C; + ampl5[1] = sumAmpFT0A; + if (sumAmpFT0C > 0 && sumAmpFT0A > 0) { + isOKEstimator5 = true; + } + if (isOKEstimator5) { + if (flatSel.applyNorm) { + allWeights = 0; + for (int i5 = 0; i5 < kNeta5; ++i5) { + combinedEstimator5 += + ampl5[i5] * kWeigthsEta5[i5] / kDeltaEeta5[i5]; + allWeights += kWeigthsEta5[i5]; + } + combinedEstimator5 /= allWeights; + } else { + for (int i5 = 0; i5 < kNeta5; ++i5) { + combinedEstimator5 += ampl5[i5] * kWeigthsEta5[i5]; } } - // option 6: FT0C + FV0 - ampl6[0] = sumAmpFT0C; - ampl6[1] = sumAmpFV0; - if (sumAmpFT0C > 0 && sumAmpFV0 > 0) { - isOKEstimator6 = true; - } - if (isOKEstimator6) { - if (applyNorm) { - allWeights = 0; - for (int i6 = 0; i6 < kNeta6; ++i6) { - combinedEstimator6 += - ampl6[i6] * kWeigthsEta6[i6] / kDeltaEeta6[i6]; - allWeights += kWeigthsEta6[i6]; - } - combinedEstimator6 /= allWeights; - } else { - for (int i6 = 0; i6 < kNeta6; ++i6) { - combinedEstimator6 += ampl6[i6] * kWeigthsEta6[i6]; - } + } + // option 6: FT0C + FV0 + ampl6[0] = sumAmpFT0C; + ampl6[1] = sumAmpFV0; + if (sumAmpFT0C > 0 && sumAmpFV0 > 0) { + isOKEstimator6 = true; + } + if (isOKEstimator6) { + if (flatSel.applyNorm) { + allWeights = 0; + for (int i6 = 0; i6 < kNeta6; ++i6) { + combinedEstimator6 += + ampl6[i6] * kWeigthsEta6[i6] / kDeltaEeta6[i6]; + allWeights += kWeigthsEta6[i6]; + } + combinedEstimator6 /= allWeights; + } else { + for (int i6 = 0; i6 < kNeta6; ++i6) { + combinedEstimator6 += ampl6[i6] * kWeigthsEta6[i6]; } } - if (flattenicityQA) { + } + if (flattenicityQAhere) { + if (ft0Read) { rFlattenicity.fill(HIST("hFT0Aampl"), sumAmpFT0A); rFlattenicity.fill(HIST("hFT0Campl"), sumAmpFT0C); + } + if (fv0Read) { rFlattenicity.fill(HIST("hFV0amplRing1to4"), sumAmpFV01to4Ch); - rFlattenicity.fill(HIST("hEv"), 4); - } - estimator[0] = multGlob; - estimator[1] = sumAmpFV0; - estimator[2] = 1.0 - flattenicityfv0; - estimator[3] = combinedEstimator5; - float flatenicityFT0 = (flatenicityT0a + flatenicityT0c) / 2.0; - estimator[4] = 1.0 - flatenicityFT0; - estimator[5] = combinedEstimator6; - float flatenicityFT0v0 = 0.5 * flattenicityfv0 + 0.5 * flatenicityT0c; - estimator[6] = 1.0 - flatenicityFT0v0; - estimator[7] = ptT; - if (flattenicityQA) { - rFlattenicity.fill(HIST(kHEst[0]), estimator[0], estimator[0]); + } + rFlattenicity.fill(HIST("hEv"), 4); + } + estimator[0] = multGlob; + estimator[1] = sumAmpFV0; + estimator[2] = 1.0 - flattenicityfv0; + estimator[3] = combinedEstimator5; + float flatenicityFT0 = (flatenicityT0a + flatenicityT0c) / 2.0; + estimator[4] = 1.0 - flatenicityFT0; + estimator[5] = combinedEstimator6; + estimator[6] = ptT; + if (flattenicityQAhere) { + rFlattenicity.fill(HIST(kHEst[0]), estimator[0], estimator[0]); + rFlattenicity.fill(HIST(kHEst[6]), estimator[6], estimator[0]); + if (estFV0Ok) { rFlattenicity.fill(HIST(kHEst[1]), estimator[1], estimator[0]); rFlattenicity.fill(HIST(kHEst[2]), estimator[2], estimator[0]); + } + if (estFT0Ok) { rFlattenicity.fill(HIST(kHEst[3]), estimator[3], estimator[0]); rFlattenicity.fill(HIST(kHEst[4]), estimator[4], estimator[0]); + } + if (estFV0FT0COk) { rFlattenicity.fill(HIST(kHEst[5]), estimator[5], estimator[0]); - rFlattenicity.fill(HIST(kHEst[6]), estimator[6], estimator[0]); - rFlattenicity.fill(HIST(kHEst[7]), estimator[7], estimator[0]); + } - // plot pt vs estimators - for (const auto& track : tracks) { - if (!track.isGlobalTrack()) { - continue; - } - float pt = track.pt(); - rFlattenicity.fill(HIST(kHPtEst[0]), estimator[0], pt); + // plot pt vs estimators + for (const auto& track : tracks) { + if (!track.isGlobalTrack()) { + continue; + } + float pt = track.pt(); + rFlattenicity.fill(HIST(kHPtEst[0]), estimator[0], pt); + rFlattenicity.fill(HIST(kHPtEst[6]), estimator[6], pt); + if (estFV0Ok) { rFlattenicity.fill(HIST(kHPtEst[1]), estimator[1], pt); rFlattenicity.fill(HIST(kHPtEst[2]), estimator[2], pt); + } + if (estFT0Ok) { rFlattenicity.fill(HIST(kHPtEst[3]), estimator[3], pt); rFlattenicity.fill(HIST(kHPtEst[4]), estimator[4], pt); + } + if (estFV0FT0COk) { rFlattenicity.fill(HIST(kHPtEst[5]), estimator[5], pt); - rFlattenicity.fill(HIST(kHPtEst[6]), estimator[6], pt); - rFlattenicity.fill(HIST(kHPtEst[7]), estimator[7], pt); } + } - if (isflattenicitywithFV0) { - for (int iCh = 0; iCh < 48; ++iCh) { - rFlattenicity.fill(HIST("hAmpV0VsCh"), iCh, ampchannel[iCh]); - rFlattenicity.fill(HIST("hAmpV0VsChBeforeCalibration"), iCh, - ampchannelBefore[iCh]); - } + if (estFV0Ok) { + for (int iCh = 0; iCh < kNCells; ++iCh) { + rFlattenicity.fill(HIST("hAmpV0VsCh"), iCh, ampchannel[iCh]); + rFlattenicity.fill(HIST("hAmpV0VsChBeforeCalibration"), iCh, + ampchannelBefore[iCh]); } - rFlattenicity.fill(HIST("fMultFv0"), sumAmpFV0); + } + if (estFT0Ok) { rFlattenicity.fill(HIST("hFlatFT0CvsFlatFT0A"), flatenicityT0c, flatenicityT0a); } } float finalflattenicity = estimator[2]; - rFlattenicity.fill(HIST("hFV0amplvsFlat"), sumAmpFV0, estimator[2]); + if (flattenicityQAhere && estFV0Ok) { + rFlattenicity.fill(HIST("hFV0amplvsFlat"), sumAmpFV0, estimator[2]); + } - if (flattenicityforanalysis == 1) { + if (flatSel.flattenicityforanalysis == kFlatFromFT0) { finalflattenicity = estimator[4]; } - if (flattenicityforanalysis == 2) { - finalflattenicity = estimator[6]; + // a lattice with no signal leaves a sentinel, which the callers reject as negative + if (finalflattenicity >= 0.f) { + flatStage = kFlatStageOk; } return finalflattenicity; } + // which weak decay produced a non-primary candidate, bins of hFeedDown*MotherPdg + static constexpr int kFdXiMinus = 0; + static constexpr int kFdXiZero = 1; + static constexpr int kFdOmegaMinus = 2; + static constexpr int kFdOther = 3; + static constexpr int kNFeedDownMothers = 4; + + // -1 for a species that does not feed the measured hadrons + static int feedDownSpecies(int pdgCode) + { + switch (std::abs(pdgCode)) { + case PDG_t::kXiMinus: + return kFdXiMinus; + case o2::constants::physics::Pdg::kXi0: + return kFdXiZero; + case PDG_t::kOmegaMinus: + return kFdOmegaMinus; + default: + return -1; + } + } + + template + int getFeedDownMother(TMcParticle const& mcParticle, float& motherPt) + { + motherPt = -1.f; + if (!mcParticle.has_mothers()) { + return kFdOther; + } + for (const auto& mother : mcParticle.template mothers_as()) { + motherPt = mother.pt(); + const int species = feedDownSpecies(mother.pdgCode()); + return (species >= 0) ? species : kFdOther; + } + return kFdOther; + } + + // fillQA=false skips hTrueFV0amplvsFlat, for callers that would fill it twice template - float estimateFlattenicityFV0MC(McParticles const& mcParticles) + float estimateFlattenicityFV0MC(McParticles const& mcParticles, bool fillQA = true) { rhoLatticeFV0AMC.fill(0); - float flattenicity = -1; - float etamin, etamax, minphi, maxphi, dphi; - int isegment = 0, nsectors; int multFV0 = 0; for (const auto& mcParticle : mcParticles) { - if (!(mcParticle.isPhysicalPrimary() && mcParticle.pt() > 0)) { + // the measured 1-rho also sees secondaries; dropping the primary + // requirement is the handle on the leading MC non-closure source + if (eventClass.genFlatPrimariesOnly && !mcParticle.isPhysicalPrimary()) { + continue; + } + if (!(mcParticle.pt() > 0)) { continue; } auto pdgParticle = pdg->GetParticle(mcParticle.pdgCode()); - if (!(pdgParticle && pdgParticle->Charge() > 0.01)) { + if (!(pdgParticle && std::abs(pdgParticle->Charge()) > kMinCharge)) { continue; } - float etap = mcParticle.eta(); - float phip = mcParticle.phi(); - isegment = 0; - - for (int ieta = 0; ieta < 5; ieta++) { - etamax = kMaxEtaFV0 - ieta * kDetaFV0; - if (ieta == 0) { - etamax = kMaxEtaFV0; - } - etamin = kMaxEtaFV0 - (ieta + 1) * kDetaFV0; - if (ieta == 4) { - etamin = kMinEtaFV0; - } - nsectors = 8; - if (ieta == 4) { - nsectors = 16; - } + const float etap = mcParticle.eta(); + const float phip = mcParticle.phi(); + int isegment = 0; + + for (int ieta = 0; ieta < kNFV0EtaRings; ieta++) { + // the outer ring ends exactly at kMinEtaFV0, the rest follow from the ring width + const float etamax = kMaxEtaFV0 - ieta * kDetaFV0; + const float etamin = (ieta == kOuterFV0RingIndex) ? kMinEtaFV0 + : kMaxEtaFV0 - (ieta + 1) * kDetaFV0; + const int nsectors = (ieta == kOuterFV0RingIndex) ? kNSectorsFV0OuterRing + : kNChannelsPerFV0Ring; for (int iphi = 0; iphi < nsectors; iphi++) { - minphi = iphi * constants::math::TwoPI / nsectors; - maxphi = (iphi + 1) * constants::math::TwoPI / nsectors; - dphi = std::abs(maxphi - minphi); + const float minphi = iphi * constants::math::TwoPI / nsectors; + const float maxphi = (iphi + 1) * constants::math::TwoPI / nsectors; + const float dphi = std::abs(maxphi - minphi); if (etap >= etamin && etap < etamax && phip >= minphi && phip < maxphi) { - rhoLatticeFV0AMC[isegment] += 1.0 / std::abs(dphi * kDetaFV0); + // yield per cell with the outer ring halved, as the amplitude is in + // estimateFlattenicity; the alternative normalises to the cell area + rhoLatticeFV0AMC[isegment] += + flatSel.genFlatDetectorLikeNorm + ? ((ieta == kOuterFV0RingIndex) ? 0.5f : 1.0f) + : 1.0 / std::abs(dphi * kDetaFV0); multFV0++; } isegment++; @@ -1289,37 +1911,121 @@ struct Lambdak0sflattenicity { } } - flattenicity = - 1.0 - getFlatenicity({rhoLatticeFV0AMC.data(), rhoLatticeFV0AMC.size()}); - rEventSelection.fill(HIST("hTrueFV0amplvsFlat"), multFV0, estimator[2]); + const float flatFV0 = + getFlatenicity({rhoLatticeFV0AMC.data(), rhoLatticeFV0AMC.size()}); + if (flatFV0 >= kFlatUndefined) { + return kInvalidFlattenicity; + } + const float flattenicity = 1.0 - flatFV0; + if (fillQA) { + rEventSelection.fill(HIST("hTrueFV0amplvsFlat"), multFV0, flattenicity); + } return flattenicity; } + + // Generated counterpart of the FT0 branch of estimateFlattenicity(): same cell + // counts, same average of the two rho. Weights are uniform and a per-side + // constant cancels in sigma/, so genFlatDetectorLikeNorm does not apply. + template + float estimateFlattenicityFT0MC(McParticles const& mcParticles, bool fillQA = true) + { + rhoLatticeFT0AMC.fill(0); + rhoLatticeFT0CMC.fill(0); + int multFT0 = 0; + + const float detaFT0A = (kMaxEtaFT0A - kMinEtaFT0A) / kNEtaBinsFT0MC; + const float detaFT0C = (kMaxEtaFT0C - kMinEtaFT0C) / kNEtaBinsFT0MC; + + for (const auto& mcParticle : mcParticles) { + if (eventClass.genFlatPrimariesOnly && !mcParticle.isPhysicalPrimary()) { + continue; + } + if (!(mcParticle.pt() > 0)) { + continue; + } + auto pdgParticle = pdg->GetParticle(mcParticle.pdgCode()); + if (!(pdgParticle && std::abs(pdgParticle->Charge()) > kMinCharge)) { + continue; + } + + const float etap = mcParticle.eta(); + const float phip = mcParticle.phi(); + const bool inA = (etap >= kMinEtaFT0A && etap < kMaxEtaFT0A); + const bool inC = (etap >= kMinEtaFT0C && etap < kMaxEtaFT0C); + if (!inA && !inC) { + continue; + } + + const int nphi = inA ? kNPhiBinsFT0AMC : kNPhiBinsFT0CMC; + const float eta0 = inA ? kMinEtaFT0A : kMinEtaFT0C; + const float deta = inA ? detaFT0A : detaFT0C; + int ieta = static_cast((etap - eta0) / deta); + int iphi = static_cast(phip * nphi / constants::math::TwoPI); + ieta = std::clamp(ieta, 0, kNEtaBinsFT0MC - 1); + iphi = std::clamp(iphi, 0, nphi - 1); + const int icell = ieta * nphi + iphi; + + if (inA) { + rhoLatticeFT0AMC[icell] += 1.f; + } else { + rhoLatticeFT0CMC[icell] += 1.f; + } + multFT0++; + } + + const float flatA = + getFlatenicity({rhoLatticeFT0AMC.data(), rhoLatticeFT0AMC.size()}); + const float flatC = + getFlatenicity({rhoLatticeFT0CMC.data(), rhoLatticeFT0CMC.size()}); + // in pp one side can be left completely empty, and the average is then undefined + if (flatA >= kFlatUndefined || flatC >= kFlatUndefined) { + return kInvalidFlattenicity; + } + const float flattenicity = 1.0 - (flatA + flatC) / 2.0; + if (fillQA) { + rEventSelection.fill(HIST("hTrueFT0amplvsFlat"), multFT0, flattenicity); + rEventSelection.fill(HIST("hNActiveCellsFT0AMCGen"), countActiveCells(rhoLatticeFT0AMC)); + rEventSelection.fill(HIST("hNActiveCellsFT0CMCGen"), countActiveCells(rhoLatticeFT0CMC)); + } + return flattenicity; + } + + // the generator-level counterpart of whichever estimator the analysis uses + template + float estimateFlattenicityGen(McParticles const& mcParticles, bool fillQA = true) + { + if (flatSel.flattenicityforanalysis == kFlatFromFT0) { + return estimateFlattenicityFT0MC(mcParticles, fillQA); + } + return estimateFlattenicityFV0MC(mcParticles, fillQA); + } // ====================== Flattenicity estimation ends ===================== // Filters on V0s // Cannot filter on dynamic columns, so we cut on DCA to PV and DCA between // daughters only - Filter preFilterV0 = (nabs(aod::v0data::dcapostopv) > v0settingDCApostopv && - nabs(aod::v0data::dcanegtopv) > v0settingDCAnegtopv && - aod::v0data::dcaV0daughters < v0settingDCAv0dau); + Filter preFilterV0 = (nabs(aod::v0data::dcapostopv) > v0Sel.v0settingDCApostopv && + nabs(aod::v0data::dcanegtopv) > v0Sel.v0settingDCAnegtopv && + aod::v0data::dcaV0daughters < v0Sel.v0settingDCAv0dau); Filter trackFilter = - (nabs(aod::track::eta) < cfgTrkEtaCut && aod::track::pt > cfgTrkLowPtCut); + (nabs(aod::track::eta) < trkPid.cfgTrkEtaCut && aod::track::pt > trkPid.cfgTrkLowPtCut); using TrackCandidates = soa::Filtered< soa::Join>; + aod::TrackSelection, aod::pidTPCFullPi, aod::pidTPCFullPr>>; void processDataRun3LambdaK0s( - soa::Join::iterator const& collision, soa::Filtered const& V0s, TrackCandidates const& tracks, soa::Join const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0s*/) { - if (applyEvSel && - !(isEventSelected(collision))) { // Checking if the event passes the - // selection criteria + const bool own = (eventHistOwner == kOwnerDataV0); + if (evSel.applyEvSel && + !(isEventSelected(collision, own))) { // Checking if the event passes the + // selection criteria return; } @@ -1328,18 +2034,28 @@ struct Lambdak0sflattenicity { auto vtxX = collision.posX(); float flattenicity = estimateFlattenicity(collision, tracks); + if (own) { + fillFlattenicityStages(); + } + if (flattenicity < 0.f) { + return; + } + if (own) { - rEventSelection.fill(HIST("hVertexZ"), vtxZ); - rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hVertexZ"), vtxZ); + rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hCentFT0M"), collision.centFT0M()); + rEventSelection.fill(HIST("hCentFT0MvsFlattenicity"), collision.centFT0M(), flattenicity); + fillFlatDistRec(flattenicity, collision.centFT0M(), collision.isInelGt0()); + fillChargedRec(tracks, flattenicity); + } for (const auto& v0 : V0s) { const auto& posDaughterTrack = v0.posTrack_as(); const auto& negDaughterTrack = v0.negTrack_as(); - if (std::abs(posDaughterTrack.eta()) > cfgTrkEtaCut || - std::abs(negDaughterTrack.eta()) > cfgTrkEtaCut || - negDaughterTrack.pt() < cfgTrkLowPtCut || - posDaughterTrack.pt() < cfgTrkLowPtCut) { + if (!isSelectedDaughterTrack(posDaughterTrack, v0Sel.v0settingNTPCcrossedRows) || + !isSelectedDaughterTrack(negDaughterTrack, v0Sel.v0settingNTPCcrossedRows)) { continue; } float massK0s = v0.mK0Short(); @@ -1359,9 +2075,9 @@ struct Lambdak0sflattenicity { std::pow(decayvtxZ - vtxZ, 2)); float v0p = std::sqrt(v0.pt() * v0.pt() + v0.pz() * v0.pz()); - float ctauK0s = decaylength * massK0s / v0p; - float ctauLambda = decaylength * massLambda / v0p; - float ctauAntiLambda = decaylength * massAntiLambda / v0p; + float ctauK0s = decaylength * o2::constants::physics::MassK0Short / v0p; + // same PDG mass for Lambda and AntiLambda + float ctauLambda = decaylength * o2::constants::physics::MassLambda0 / v0p; float alpha = v0.alpha(); float qtarm = v0.qtarm(); @@ -1369,16 +2085,16 @@ struct Lambdak0sflattenicity { // Cut on dynamic columns for K0s rCommonHist.fill(HIST("hArmPodoAlphavsQT"), alpha, qtarm); - if (v0.v0cosPA() >= v0settingCosPAK0s && - v0.v0radius() >= v0settingRadiusK0s && - std::abs(posDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - std::abs(negDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - ctauK0s < v0settingcTauK0s && - std::abs(v0.rapidity(0)) <= v0settingRapidity && - std::abs(massLambda - pdgmassLambda) > v0settingMassRejectionK0s && - std::abs(massAntiLambda - pdgmassLambda) > - v0settingMassRejectionK0s && - qtarm > v0settingArmePodoK0s * std::abs(alpha)) { + if (v0.v0cosPA() >= v0Sel.v0settingCosPAK0s && + v0.v0radius() >= v0Sel.v0settingRadiusK0s && + std::abs(posDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + std::abs(negDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + ctauK0s < v0Sel.v0settingcTauK0s && + std::abs(v0.rapidity(0)) <= v0Sel.v0settingRapidity && + std::abs(massLambda - o2::constants::physics::MassLambda0) > v0Sel.v0settingMassRejectionK0s && + std::abs(massAntiLambda - o2::constants::physics::MassLambda0) > + v0Sel.v0settingMassRejectionK0s && + qtarm > v0Sel.v0settingArmePodoK0s * std::abs(alpha)) { rKzeroShort.fill(HIST("hMassK0sSelected"), massK0s); rKzeroShort.fill(HIST("hDCAV0DaughtersK0s"), v0.dcaV0daughters()); @@ -1391,7 +2107,7 @@ struct Lambdak0sflattenicity { rKzeroShort.fill(HIST("hArmPodoAlphavsQTK0sAfterCut"), alpha, qtarm); // Filling the PID of the V0 daughters in the region of the K0s peak - if (0.45 < massK0s && massK0s < 0.55) { + if (std::abs(massK0s - o2::constants::physics::MassK0Short) < trkPid.pidQAWindowK0s) { rKzeroShort.fill(HIST("hNSigmaPosPionFromK0s"), posDaughterTrack.tpcNSigmaPi(), posDaughterTrack.tpcInnerParam()); @@ -1402,13 +2118,13 @@ struct Lambdak0sflattenicity { } // Cut on dynamic columns for Lambda - if (v0.v0cosPA() >= v0settingCosPALambda && - v0.v0radius() >= v0settingRadiusLambda && - std::abs(posDaughterTrack.tpcNSigmaPr()) <= nSigmaTPCProton && - std::abs(negDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - ctauLambda < v0settingcTauLambda && - std::abs(v0.rapidity(1)) <= v0settingRapidity && - std::abs(massK0s - pdgmassK0s) > v0settingMassRejectionLambda) { + if (v0.v0cosPA() >= v0Sel.v0settingCosPALambda && + v0.v0radius() >= v0Sel.v0settingRadiusLambda && + std::abs(posDaughterTrack.tpcNSigmaPr()) <= trkPid.nSigmaTPCProton && + std::abs(negDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + ctauLambda < v0Sel.v0settingcTauLambda && + std::abs(v0.rapidity(1)) <= v0Sel.v0settingRapidity && + std::abs(massK0s - o2::constants::physics::MassK0Short) > v0Sel.v0settingMassRejectionLambda) { rLambda.fill(HIST("hMassLambdaSelected"), massLambda); rLambda.fill(HIST("hDCAV0DaughtersLambda"), v0.dcaV0daughters()); @@ -1418,10 +2134,11 @@ struct Lambdak0sflattenicity { rLambda.fill(HIST("h2DdecayRadiusLambda"), v0.v0radius()); rLambda.fill(HIST("hMassLambdapT"), massLambda, v0.pt()); rLambda.fill(HIST("hMassLambdapTFlat"), massLambda, v0.pt(), flattenicity); + rLambda.fill(HIST("hDcaV0ToPVLambda"), v0.dcav0topv(), v0.pt(), flattenicity); // Filling the PID of the V0 daughters in the region of the Lambda peak - if (1.015 < massLambda && massLambda < 1.215) { - rLambda.fill(HIST("hNSigmaPosPionFromLambda"), + if (std::abs(massLambda - o2::constants::physics::MassLambda0) < trkPid.pidQAWindowLambda) { + rLambda.fill(HIST("hNSigmaPosProtonFromLambda"), posDaughterTrack.tpcNSigmaPr(), posDaughterTrack.tpcInnerParam()); rLambda.fill(HIST("hNSigmaNegPionFromLambda"), @@ -1431,31 +2148,32 @@ struct Lambdak0sflattenicity { } // Cut on dynamic columns for AntiLambda - if (v0.v0cosPA() >= v0settingCosPALambda && - v0.v0radius() >= v0settingRadiusLambda && - std::abs(posDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - std::abs(negDaughterTrack.tpcNSigmaPr()) <= nSigmaTPCProton && - ctauAntiLambda < v0settingcTauLambda && - std::abs(v0.rapidity(2)) <= v0settingRapidity && - std::abs(massK0s - pdgmassK0s) > v0settingMassRejectionLambda) { + if (v0.v0cosPA() >= v0Sel.v0settingCosPALambda && + v0.v0radius() >= v0Sel.v0settingRadiusLambda && + std::abs(posDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + std::abs(negDaughterTrack.tpcNSigmaPr()) <= trkPid.nSigmaTPCProton && + ctauLambda < v0Sel.v0settingcTauLambda && + std::abs(v0.rapidity(2)) <= v0Sel.v0settingRapidity && + std::abs(massK0s - o2::constants::physics::MassK0Short) > v0Sel.v0settingMassRejectionLambda) { rAntiLambda.fill(HIST("hMassAntiLambdaSelected"), massAntiLambda); rAntiLambda.fill(HIST("hDCAV0DaughtersAntiLambda"), v0.dcaV0daughters()); rAntiLambda.fill(HIST("hV0CosPAAntiLambda"), v0.v0cosPA()); rAntiLambda.fill(HIST("hrapidityAntiLambda"), v0.rapidity(2)); - rAntiLambda.fill(HIST("hctauAntiLambda"), ctauAntiLambda); + rAntiLambda.fill(HIST("hctauAntiLambda"), ctauLambda); rAntiLambda.fill(HIST("h2DdecayRadiusAntiLambda"), v0.v0radius()); rAntiLambda.fill(HIST("hMassAntiLambdapT"), massAntiLambda, v0.pt()); rAntiLambda.fill(HIST("hMassAntiLambdapTFlat"), massAntiLambda, v0.pt(), flattenicity); + rAntiLambda.fill(HIST("hDcaV0ToPVAntiLambda"), v0.dcav0topv(), v0.pt(), flattenicity); // Filling the PID of the V0 daughters in the region of the AntiLambda // peak - if (1.015 < massAntiLambda && massAntiLambda < 1.215) { + if (std::abs(massAntiLambda - o2::constants::physics::MassLambda0) < trkPid.pidQAWindowLambda) { rAntiLambda.fill(HIST("hNSigmaPosPionFromAntiLambda"), posDaughterTrack.tpcNSigmaPi(), posDaughterTrack.tpcInnerParam()); - rAntiLambda.fill(HIST("hNSigmaNegPionFromAntiLambda"), + rAntiLambda.fill(HIST("hNSigmaNegProtonFromAntiLambda"), negDaughterTrack.tpcNSigmaPr(), negDaughterTrack.tpcInnerParam()); } @@ -1465,24 +2183,31 @@ struct Lambdak0sflattenicity { using TrackCandidatesMC = soa::Filtered>; - Preslice>> perCol = aod::track::collisionId; + Preslice>> perCol = aod::v0data::collisionId; + Preslice perColTracksMC = aod::track::collisionId; + // needed by the sliceByCached calls below, else they throw "Disabled cache" Preslice perMCCol = aod::mcparticle::mcCollisionId; SliceCache cache1; void processRecMCLambdaK0s( - soa::Join const& collisions, soa::Filtered> const& V0s, aod::McCollisions const&, TrackCandidatesMC const& tracks, soa::Join const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0s*/, aod::McParticles const& mcParticles) { + const bool own = (eventHistOwner == kOwnerRecMCV0); for (const auto& collision : collisions) { - if (applyEvSel && - !(isEventSelected(collision))) { // Checking if the event passes the - // selection criteria + if (evSel.applyEvSel && + !(isEventSelected(collision, own))) { // Checking if the event passes the + // selection criteria + continue; + } + + if (!collision.has_mcCollision()) { continue; } @@ -1490,23 +2215,43 @@ struct Lambdak0sflattenicity { auto vtxY = collision.posY(); auto vtxX = collision.posX(); - float flattenicity = estimateFlattenicity(collision, tracks); - - rEventSelection.fill(HIST("hVertexZ"), vtxZ); - rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + auto tracksThisCollision = tracks.sliceBy(perColTracksMC, collision.globalIndex()); + float flattenicity = estimateFlattenicity(collision, tracksThisCollision); + if (own) { + fillFlattenicityStages(); + } + if (flattenicity < 0.f) { + continue; + } + if (own) { + + rEventSelection.fill(HIST("hVertexZ"), vtxZ); + rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hCentFT0M"), collision.centFT0M()); + rEventSelection.fill(HIST("hCentFT0MvsFlattenicity"), collision.centFT0M(), flattenicity); + fillFlatDistRec(flattenicity, collision.centFT0M(), collision.isInelGt0()); + fillChargedRec(tracksThisCollision, flattenicity); + } auto v0sThisCollision = V0s.sliceBy(perCol, collision.globalIndex()); const auto& mcCollision = collision.mcCollision_as(); + const auto particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache1); + const float flattenicityMCGen = estimateFlattenicityGen(particlesInCollision, own); + if (own) { + rEventSelection.fill(HIST("hFlattenicityDistributionMCGen_Rec"), flattenicityMCGen); + rEventSelection.fill(HIST("hFlattenicity_Corr_Gen_vs_Rec"), flattenicityMCGen, flattenicity); + rEventSelection.fill(HIST("hFlatGenVsRecFine"), flattenicityMCGen, flattenicity); + fillFlatDistGenInRec(flattenicityMCGen, collision.centFT0M(), collision.isInelGt0()); + } + for (const auto& v0 : v0sThisCollision) { const auto& posDaughterTrack = v0.posTrack_as(); const auto& negDaughterTrack = v0.negTrack_as(); - if (std::abs(posDaughterTrack.eta()) > cfgTrkEtaCut || - std::abs(negDaughterTrack.eta()) > cfgTrkEtaCut || - negDaughterTrack.pt() < cfgTrkLowPtCut || - posDaughterTrack.pt() < cfgTrkLowPtCut) { + if (!isSelectedDaughterTrack(posDaughterTrack, v0Sel.v0settingNTPCcrossedRows) || + !isSelectedDaughterTrack(negDaughterTrack, v0Sel.v0settingNTPCcrossedRows)) { continue; } @@ -1531,27 +2276,31 @@ struct Lambdak0sflattenicity { std::pow(decayvtxZ - vtxZ, 2)); float v0p = std::sqrt(v0.pt() * v0.pt() + v0.pz() * v0.pz()); - float ctauK0s = decaylength * massK0s / v0p; - float ctauLambda = decaylength * massLambda / v0p; - float ctauAntiLambda = decaylength * massAntiLambda / v0p; + float ctauK0s = decaylength * o2::constants::physics::MassK0Short / v0p; + // same PDG mass for Lambda and AntiLambda + float ctauLambda = decaylength * o2::constants::physics::MassLambda0 / v0p; float alpha = v0.alpha(); float qtarm = v0.qtarm(); rCommonHist.fill(HIST("hArmPodoAlphavsQT"), alpha, qtarm); auto v0mcParticle = v0.mcParticle(); + const bool isPrimaryV0 = v0mcParticle.isPhysicalPrimary(); + const bool keepForEfficiency = isPrimaryV0 || !eventClass.requirePrimaryMC; + float motherPt = -1.f; // Cut on dynamic columns for K0s - if (v0mcParticle.pdgCode() == PDG_t::kK0Short && v0.v0cosPA() >= v0settingCosPAK0s && - v0.v0radius() >= v0settingRadiusK0s && - std::abs(posDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - std::abs(negDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - ctauK0s < v0settingcTauK0s && - std::abs(v0.rapidity(0)) <= v0settingRapidity && - std::abs(massLambda - pdgmassLambda) > v0settingMassRejectionK0s && - std::abs(massAntiLambda - pdgmassLambda) > - v0settingMassRejectionK0s && - qtarm > v0settingArmePodoK0s * std::abs(alpha)) { + if (v0mcParticle.pdgCode() == PDG_t::kK0Short && keepForEfficiency && + v0.v0cosPA() >= v0Sel.v0settingCosPAK0s && + v0.v0radius() >= v0Sel.v0settingRadiusK0s && + std::abs(posDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + std::abs(negDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + ctauK0s < v0Sel.v0settingcTauK0s && + std::abs(v0.rapidity(0)) <= v0Sel.v0settingRapidity && + std::abs(massLambda - o2::constants::physics::MassLambda0) > v0Sel.v0settingMassRejectionK0s && + std::abs(massAntiLambda - o2::constants::physics::MassLambda0) > + v0Sel.v0settingMassRejectionK0s && + qtarm > v0Sel.v0settingArmePodoK0s * std::abs(alpha)) { rKzeroShort.fill(HIST("hMassK0sSelected"), massK0s); rKzeroShort.fill(HIST("hDCAV0DaughtersK0s"), v0.dcaV0daughters()); @@ -1561,10 +2310,13 @@ struct Lambdak0sflattenicity { rKzeroShort.fill(HIST("h2DdecayRadiusK0s"), v0.v0radius()); rKzeroShort.fill(HIST("hMassK0spT"), massK0s, v0.pt()); rKzeroShort.fill(HIST("hMassK0spTFlat"), massK0s, v0.pt(), flattenicity); + rKzeroShort.fill(HIST("hMassK0spTTrueFlat"), massK0s, v0.pt(), flattenicityMCGen); + rKzeroShort.fill(HIST("hPtResK0s"), v0mcParticle.pt(), + (v0.pt() - v0mcParticle.pt()) / v0mcParticle.pt()); rKzeroShort.fill(HIST("hArmPodoAlphavsQTK0sAfterCut"), alpha, qtarm); // Filling the PID of the V0 daughters in the region of the K0s peak - if (0.45 < massK0s && massK0s < 0.55) { + if (std::abs(massK0s - o2::constants::physics::MassK0Short) < trkPid.pidQAWindowK0s) { rKzeroShort.fill(HIST("hNSigmaPosPionFromK0s"), posDaughterTrack.tpcNSigmaPi(), posDaughterTrack.tpcInnerParam()); @@ -1576,114 +2328,189 @@ struct Lambdak0sflattenicity { // Cut on dynamic columns for Lambda if (v0mcParticle.pdgCode() == PDG_t::kLambda0 && - v0.v0cosPA() >= v0settingCosPALambda && - v0.v0radius() >= v0settingRadiusLambda && - std::abs(posDaughterTrack.tpcNSigmaPr()) <= nSigmaTPCProton && - std::abs(negDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - ctauLambda < v0settingcTauLambda && - std::abs(v0.rapidity(1)) <= v0settingRapidity && - std::abs(massK0s - pdgmassK0s) > v0settingMassRejectionLambda) { - - rLambda.fill(HIST("hMassLambdaSelected"), massLambda); - rLambda.fill(HIST("hDCAV0DaughtersLambda"), v0.dcaV0daughters()); - rLambda.fill(HIST("hV0CosPALambda"), v0.v0cosPA()); - rLambda.fill(HIST("hrapidityLambda"), v0.rapidity(1)); - rLambda.fill(HIST("hctauLambda"), ctauLambda); - rLambda.fill(HIST("h2DdecayRadiusLambda"), v0.v0radius()); - rLambda.fill(HIST("hMassLambdapT"), massLambda, v0.pt()); - rLambda.fill(HIST("hMassLambdapTFlat"), massLambda, v0.pt(), flattenicity); - - // Filling the PID of the V0 daughters in the region of the Lambda peak - if (1.015 < massLambda && massLambda < 1.215) { - rLambda.fill(HIST("hNSigmaPosPionFromLambda"), - posDaughterTrack.tpcNSigmaPr(), - posDaughterTrack.tpcInnerParam()); - rLambda.fill(HIST("hNSigmaNegPionFromLambda"), - negDaughterTrack.tpcNSigmaPi(), - negDaughterTrack.tpcInnerParam()); + v0.v0cosPA() >= v0Sel.v0settingCosPALambda && + v0.v0radius() >= v0Sel.v0settingRadiusLambda && + std::abs(posDaughterTrack.tpcNSigmaPr()) <= trkPid.nSigmaTPCProton && + std::abs(negDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + ctauLambda < v0Sel.v0settingcTauLambda && + std::abs(v0.rapidity(1)) <= v0Sel.v0settingRapidity && + std::abs(massK0s - o2::constants::physics::MassK0Short) > v0Sel.v0settingMassRejectionLambda) { + + // secondaries kept out of the efficiency numerator, booked for the feed-down + if (!isPrimaryV0) { + const int motherIndex = getFeedDownMother(v0mcParticle, motherPt); + rLambda.fill(HIST("hMassFeedDownLambdapTFlat"), massLambda, v0.pt(), flattenicity); + rLambda.fill(HIST("hFeedDownLambdaPtVsMotherPt"), v0.pt(), motherPt); + rLambda.fill(HIST("hFeedDownLambdaMatrix"), v0.pt(), motherPt, flattenicity, motherIndex); + rLambda.fill(HIST("hFeedDownLambdaMotherPdg"), motherIndex); + rLambda.fill(HIST("hDcaV0ToPVLambdaSec"), v0.dcav0topv(), v0.pt(), flattenicity); + } else { + rLambda.fill(HIST("hDcaV0ToPVLambdaPrim"), v0.dcav0topv(), v0.pt(), flattenicity); + } + if (keepForEfficiency) { + + rLambda.fill(HIST("hMassLambdaSelected"), massLambda); + rLambda.fill(HIST("hDCAV0DaughtersLambda"), v0.dcaV0daughters()); + rLambda.fill(HIST("hV0CosPALambda"), v0.v0cosPA()); + rLambda.fill(HIST("hrapidityLambda"), v0.rapidity(1)); + rLambda.fill(HIST("hctauLambda"), ctauLambda); + rLambda.fill(HIST("h2DdecayRadiusLambda"), v0.v0radius()); + rLambda.fill(HIST("hMassLambdapT"), massLambda, v0.pt()); + rLambda.fill(HIST("hMassLambdapTFlat"), massLambda, v0.pt(), flattenicity); + rLambda.fill(HIST("hMassLambdapTTrueFlat"), massLambda, v0.pt(), flattenicityMCGen); + rLambda.fill(HIST("hDcaV0ToPVLambda"), v0.dcav0topv(), v0.pt(), flattenicity); + rLambda.fill(HIST("hPtResLambda"), v0mcParticle.pt(), + (v0.pt() - v0mcParticle.pt()) / v0mcParticle.pt()); + + // Filling the PID of the V0 daughters in the region of the Lambda peak + if (std::abs(massLambda - o2::constants::physics::MassLambda0) < trkPid.pidQAWindowLambda) { + rLambda.fill(HIST("hNSigmaPosProtonFromLambda"), + posDaughterTrack.tpcNSigmaPr(), + posDaughterTrack.tpcInnerParam()); + rLambda.fill(HIST("hNSigmaNegPionFromLambda"), + negDaughterTrack.tpcNSigmaPi(), + negDaughterTrack.tpcInnerParam()); + } } } // Cut on dynamic columns for AntiLambda if (v0mcParticle.pdgCode() == PDG_t::kLambda0Bar && - v0.v0cosPA() >= v0settingCosPALambda && - v0.v0radius() >= v0settingRadiusLambda && - std::abs(posDaughterTrack.tpcNSigmaPi()) <= nSigmaTPCPion && - std::abs(negDaughterTrack.tpcNSigmaPr()) <= nSigmaTPCProton && - ctauAntiLambda < v0settingcTauLambda && - std::abs(v0.rapidity(2)) <= v0settingRapidity && - std::abs(massK0s - pdgmassK0s) > v0settingMassRejectionLambda) { - - rAntiLambda.fill(HIST("hMassAntiLambdaSelected"), massAntiLambda); - rAntiLambda.fill(HIST("hDCAV0DaughtersAntiLambda"), - v0.dcaV0daughters()); - rAntiLambda.fill(HIST("hV0CosPAAntiLambda"), v0.v0cosPA()); - rAntiLambda.fill(HIST("hrapidityAntiLambda"), v0.rapidity(2)); - rAntiLambda.fill(HIST("hctauAntiLambda"), ctauAntiLambda); - rAntiLambda.fill(HIST("h2DdecayRadiusAntiLambda"), v0.v0radius()); - rAntiLambda.fill(HIST("hMassAntiLambdapT"), massAntiLambda, v0.pt()); - rAntiLambda.fill(HIST("hMassAntiLambdapTFlat"), massAntiLambda, v0.pt(), flattenicity); - - // Filling the PID of the V0 daughters in the region of the AntiLambda - // peak - if (1.015 < massAntiLambda && massAntiLambda < 1.215) { - rAntiLambda.fill(HIST("hNSigmaPosPionFromAntiLambda"), - posDaughterTrack.tpcNSigmaPi(), - posDaughterTrack.tpcInnerParam()); - rAntiLambda.fill(HIST("hNSigmaNegPionFromAntiLambda"), - negDaughterTrack.tpcNSigmaPr(), - negDaughterTrack.tpcInnerParam()); + v0.v0cosPA() >= v0Sel.v0settingCosPALambda && + v0.v0radius() >= v0Sel.v0settingRadiusLambda && + std::abs(posDaughterTrack.tpcNSigmaPi()) <= trkPid.nSigmaTPCPion && + std::abs(negDaughterTrack.tpcNSigmaPr()) <= trkPid.nSigmaTPCProton && + ctauLambda < v0Sel.v0settingcTauLambda && + std::abs(v0.rapidity(2)) <= v0Sel.v0settingRapidity && + std::abs(massK0s - o2::constants::physics::MassK0Short) > v0Sel.v0settingMassRejectionLambda) { + + // secondaries kept out of the efficiency numerator, booked for the feed-down + if (!isPrimaryV0) { + const int motherIndex = getFeedDownMother(v0mcParticle, motherPt); + rAntiLambda.fill(HIST("hMassFeedDownAntiLambdapTFlat"), massAntiLambda, v0.pt(), flattenicity); + rAntiLambda.fill(HIST("hFeedDownAntiLambdaPtVsMotherPt"), v0.pt(), motherPt); + rAntiLambda.fill(HIST("hFeedDownAntiLambdaMatrix"), v0.pt(), motherPt, flattenicity, motherIndex); + rAntiLambda.fill(HIST("hFeedDownAntiLambdaMotherPdg"), motherIndex); + rAntiLambda.fill(HIST("hDcaV0ToPVAntiLambdaSec"), v0.dcav0topv(), v0.pt(), flattenicity); + } else { + rAntiLambda.fill(HIST("hDcaV0ToPVAntiLambdaPrim"), v0.dcav0topv(), v0.pt(), flattenicity); + } + if (keepForEfficiency) { + + rAntiLambda.fill(HIST("hMassAntiLambdaSelected"), massAntiLambda); + rAntiLambda.fill(HIST("hDCAV0DaughtersAntiLambda"), + v0.dcaV0daughters()); + rAntiLambda.fill(HIST("hV0CosPAAntiLambda"), v0.v0cosPA()); + rAntiLambda.fill(HIST("hrapidityAntiLambda"), v0.rapidity(2)); + rAntiLambda.fill(HIST("hctauAntiLambda"), ctauLambda); + rAntiLambda.fill(HIST("h2DdecayRadiusAntiLambda"), v0.v0radius()); + rAntiLambda.fill(HIST("hMassAntiLambdapT"), massAntiLambda, v0.pt()); + rAntiLambda.fill(HIST("hMassAntiLambdapTFlat"), massAntiLambda, v0.pt(), flattenicity); + rAntiLambda.fill(HIST("hMassAntiLambdapTTrueFlat"), massAntiLambda, v0.pt(), flattenicityMCGen); + rAntiLambda.fill(HIST("hDcaV0ToPVAntiLambda"), v0.dcav0topv(), v0.pt(), flattenicity); + rAntiLambda.fill(HIST("hPtResAntiLambda"), v0mcParticle.pt(), + (v0.pt() - v0mcParticle.pt()) / v0mcParticle.pt()); + + // Filling the PID of the V0 daughters in the region of the AntiLambda + // peak + if (std::abs(massAntiLambda - o2::constants::physics::MassLambda0) < trkPid.pidQAWindowLambda) { + rAntiLambda.fill(HIST("hNSigmaPosPionFromAntiLambda"), + posDaughterTrack.tpcNSigmaPi(), + posDaughterTrack.tpcInnerParam()); + rAntiLambda.fill(HIST("hNSigmaNegProtonFromAntiLambda"), + negDaughterTrack.tpcNSigmaPr(), + negDaughterTrack.tpcInnerParam()); + } } } } - const auto particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache1); - float flattenicityMCGen = estimateFlattenicityFV0MC(particlesInCollision); - rEventSelection.fill(HIST("hFlattenicityDistributionMCGen_Rec"), flattenicityMCGen); - rEventSelection.fill(HIST("hFlattenicity_Corr_Gen_vs_Rec"), flattenicityMCGen, flattenicity); + const bool isInelGt0Rec = collision.isInelGt0(); + int nchGenInRec = 0; for (const auto& mcParticle : particlesInCollision) { if (!mcParticle.isPhysicalPrimary()) { continue; } - if (std::abs(mcParticle.y()) > 0.5f) { + if (eventClass.fillChargedQA && std::abs(mcParticle.eta()) <= eventClass.cfgEtaChargedCut) { + auto pdgParticle = pdg->GetParticle(mcParticle.pdgCode()); + if (pdgParticle && std::abs(pdgParticle->Charge()) > kMinCharge) { + nchGenInRec++; + rCharged.fill(HIST("hPtChVsFlatGenInRec"), mcParticle.pt(), flattenicity); + } + } + + // normalisation of hFeedDownLambdaMatrix, before the Lambda rapidity cut + if (std::abs(mcParticle.y()) <= eventClass.cfgFeedDownMotherRapidity) { + const int motherSpecies = feedDownSpecies(mcParticle.pdgCode()); + if (motherSpecies >= 0) { + rLambda.fill(HIST("hGenFeedDownMotherPtFlat"), mcParticle.pt(), flattenicity, motherSpecies); + } + } + + if (std::abs(mcParticle.y()) > v0Sel.v0settingRapidity) { continue; } if (mcParticle.pdgCode() == PDG_t::kK0Short) { - rKzeroShort.fill(HIST("Generated_MCRecoCollCheck_INEL_K0Short"), mcParticle.pt(), flattenicity); // K0s + rKzeroShort.fill(HIST("Generated_MCRecoCollCheck_INEL_K0Short"), mcParticle.pt(), flattenicity); + if (isInelGt0Rec) { + rKzeroShort.fill(HIST("Generated_MCRecoCollCheck_INELgt0_K0Short"), mcParticle.pt(), flattenicity); + rKzeroShort.fill(HIST("Generated_MCRecoCollCheck_INELgt0_K0Short_TrueFlat"), mcParticle.pt(), flattenicityMCGen); + } } if (mcParticle.pdgCode() == PDG_t::kLambda0) { - rLambda.fill(HIST("Generated_MCRecoCollCheck_INEL_Lambda"), mcParticle.pt(), flattenicity); // Lambda + rLambda.fill(HIST("Generated_MCRecoCollCheck_INEL_Lambda"), mcParticle.pt(), flattenicity); + if (isInelGt0Rec) { + rLambda.fill(HIST("Generated_MCRecoCollCheck_INELgt0_Lambda"), mcParticle.pt(), flattenicity); + rLambda.fill(HIST("Generated_MCRecoCollCheck_INELgt0_Lambda_TrueFlat"), mcParticle.pt(), flattenicityMCGen); + } } if (mcParticle.pdgCode() == PDG_t::kLambda0Bar) { - rAntiLambda.fill(HIST("Generated_MCRecoCollCheck_INEL_AntiLambda"), mcParticle.pt(), flattenicity); // AntiLambda + rAntiLambda.fill(HIST("Generated_MCRecoCollCheck_INEL_AntiLambda"), mcParticle.pt(), flattenicity); + if (isInelGt0Rec) { + rAntiLambda.fill(HIST("Generated_MCRecoCollCheck_INELgt0_AntiLambda"), mcParticle.pt(), flattenicity); + rAntiLambda.fill(HIST("Generated_MCRecoCollCheck_INELgt0_AntiLambda_TrueFlat"), mcParticle.pt(), flattenicityMCGen); + } } } + if (eventClass.fillChargedQA) { + rCharged.fill(HIST("hNchVsFlatGenInRec"), nchGenInRec, flattenicity); + } } } - // Filter posZFilterMC = (nabs(o2::aod::mccollision::posZ) < cutzvertex); + // Filter posZFilterMC = (nabs(o2::aod::mccollision::posZ) < evSel.cutzvertex); void processGenMC( - o2::aod::McCollision const& mcCollision, const soa::SmallGroups>& collisions, TrackCandidatesMC const& tracks, aod::FT0s const& /*ft0s*/, + o2::aod::McCollision const& mcCollision, const soa::SmallGroups>& collisions, TrackCandidatesMC const& tracks, + soa::Join const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0s*/, o2::aod::McParticles const& mcParticles) { - float flattenicity; - if (flattenicityforLossCorrRec) { - float flattenicityRec = 999.0; - for (const auto& collision : collisions) { - flattenicityRec = estimateFlattenicity(collision, tracks); - // printf("FoundFlattenicity, Gen=%f, Rec=%f \n", flattenicity, flattenicityRec); + // Both estimates are always computed: the reconstructed one classifies the + // numerator of the closure, the true one its denominator. Without a + // reconstructed counterpart the sentinel is kept, it falls in the underflow + // so the loss counters stay complete. + float flattenicityRec = kInvalidFlattenicity; + float centFT0M = -1.f; + for (const auto& collision : collisions) { + if (evSel.applyEvSel && !isEventSelected(collision, false)) { + continue; + } + auto tracksThisCollision = tracks.sliceBy(perColTracksMC, collision.globalIndex()); + flattenicityRec = estimateFlattenicity(collision, tracksThisCollision, fillFlattenicityQAInGenMC); + if (flattenicityRec >= 0.f) { + centFT0M = collision.centFT0M(); + break; } - rEventSelection.fill(HIST("hFlattenicityDistributionRecMCGen"), flattenicityRec); - flattenicity = flattenicityRec; - } else { - float flattenicityGen = estimateFlattenicityFV0MC(mcParticles); - rEventSelection.fill(HIST("hFlattenicityDistributionMCGen"), flattenicityGen); - flattenicity = flattenicityGen; } + const float flattenicityTrue = estimateFlattenicityGen(mcParticles, fillFlattenicityQAInGenMC); + rEventSelection.fill(HIST("hFlattenicityDistributionRecMCGen"), flattenicityRec); + rEventSelection.fill(HIST("hFlattenicityDistributionMCGen"), flattenicityTrue); + + // which of the two drives the loss corrections written without a suffix + const float flattenicity = flatSel.flattenicityforLossCorrRec ? flattenicityRec : flattenicityTrue; //==================================== //===== Event Loss Denominator ======= @@ -1691,20 +2518,33 @@ struct Lambdak0sflattenicity { rEventSelection.fill(HIST("hNEventsMCGen"), 0.5); - if (std::abs(mcCollision.posZ()) > cutzvertex) { + if (std::abs(mcCollision.posZ()) > evSel.cutzvertex) { return; } rEventSelection.fill(HIST("hNEventsMCGen"), 1.5); + + // The FT0M class has no generator-level counterpart, so with a window + // requested the collision only belongs to it through an accepted + // reconstructed one, which is what leaves centFT0M non-negative above. + // Otherwise the generated denominators stay MB while the numerator is HM. + if (eventClass.applyCentSel && centFT0M < 0.f) { + return; + } + rEventSelection.fill(HIST("hFlat_GenColl_MC"), flattenicity); - bool isINELgt0true = false; + const bool isINELgt0true = pwglf::isINELgtNmc(mcParticles, 0, pdg); - if (pwglf::isINELgtNmc(mcParticles, 0, pdg)) { - isINELgt0true = true; + if (isINELgt0true) { rEventSelection.fill(HIST("hNEventsMCGen"), 2.5); rEventSelection.fill(HIST("hFlat_GenColl_MC_INELgt0"), flattenicity); + rEventSelection.fill(HIST("hFlat_GenColl_MC_INELgt0_TrueFlat"), flattenicityTrue); } + fillFlatDistGen(flattenicityTrue, centFT0M, isINELgt0true); + fillChargedGen(mcParticles, flattenicity, false); + fillChargedGen(mcParticles, flattenicityTrue, true); + //===================================== //===== Signal Loss Denominator ======= //===================================== @@ -1714,32 +2554,35 @@ struct Lambdak0sflattenicity { if (!mcParticle.isPhysicalPrimary()) { continue; } - if (std::abs(mcParticle.y()) > 0.5f) { - continue; - } + const bool inV0Rapidity = std::abs(mcParticle.y()) <= v0Sel.v0settingRapidity; + const bool inCascRapidity = std::abs(mcParticle.y()) <= cascSel.cascsettingRapidity; - if (mcParticle.pdgCode() == PDG_t::kK0Short) { - rKzeroShort.fill(HIST("pGen_MCGenColl_INEL_K0Short"), mcParticle.pt(), flattenicity); // K0s + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kK0Short) { + rKzeroShort.fill(HIST("pGen_MCGenColl_INEL_K0Short"), mcParticle.pt(), flattenicity); if (isINELgt0true) { - rKzeroShort.fill(HIST("pGen_MCGenColl_INELgt0_K0Short"), mcParticle.pt(), flattenicity); // K0s + rKzeroShort.fill(HIST("pGen_MCGenColl_INELgt0_K0Short"), mcParticle.pt(), flattenicity); + rKzeroShort.fill(HIST("pGen_MCGenColl_INELgt0_K0Short_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (mcParticle.pdgCode() == PDG_t::kLambda0) { - rLambda.fill(HIST("pGen_MCGenColl_INEL_Lambda"), mcParticle.pt(), flattenicity); // Lambda + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kLambda0) { + rLambda.fill(HIST("pGen_MCGenColl_INEL_Lambda"), mcParticle.pt(), flattenicity); if (isINELgt0true) { - rLambda.fill(HIST("pGen_MCGenColl_INELgt0_Lambda"), mcParticle.pt(), flattenicity); // Lambda + rLambda.fill(HIST("pGen_MCGenColl_INELgt0_Lambda"), mcParticle.pt(), flattenicity); + rLambda.fill(HIST("pGen_MCGenColl_INELgt0_Lambda_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (mcParticle.pdgCode() == PDG_t::kLambda0Bar) { - rAntiLambda.fill(HIST("pGen_MCGenColl_INEL_AntiLambda"), mcParticle.pt(), flattenicity); // AntiLambda + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kLambda0Bar) { + rAntiLambda.fill(HIST("pGen_MCGenColl_INEL_AntiLambda"), mcParticle.pt(), flattenicity); if (isINELgt0true) { - rAntiLambda.fill(HIST("pGen_MCGenColl_INELgt0_AntiLambda"), mcParticle.pt(), flattenicity); // AntiLambda + rAntiLambda.fill(HIST("pGen_MCGenColl_INELgt0_AntiLambda"), mcParticle.pt(), flattenicity); + rAntiLambda.fill(HIST("pGen_MCGenColl_INELgt0_AntiLambda_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (std::abs(mcParticle.pdgCode()) == PDG_t::kXiPlusBar) { - rXi.fill(HIST("pGen_MCGenColl_INEL_Xi"), mcParticle.pt(), flattenicity); // Xi + if (inCascRapidity && std::abs(mcParticle.pdgCode()) == PDG_t::kXiMinus) { + rXi.fill(HIST("pGen_MCGenColl_INEL_Xi"), mcParticle.pt(), flattenicity); if (isINELgt0true) { - rXi.fill(HIST("pGen_MCGenColl_INELgt0_Xi"), mcParticle.pt(), flattenicity); // Xi + rXi.fill(HIST("pGen_MCGenColl_INELgt0_Xi"), mcParticle.pt(), flattenicity); + rXi.fill(HIST("pGen_MCGenColl_INELgt0_Xi_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } } @@ -1753,7 +2596,11 @@ struct Lambdak0sflattenicity { //===================================== rEventSelection.fill(HIST("hNEventsMCReco"), 0.5); - if (applyEvSel && !isEventSelected(collision)) { + // the per-cut ladder of hEventsSelected belongs to the reconstructed-level + // process functions; filling it here as well would double count every + // collision whenever processGenMC runs alongside one of them, which is the + // normal MC configuration. hNEventsMCReco already records all/passed here. + if (evSel.applyEvSel && !isEventSelected(collision, false)) { continue; } rEventSelection.fill(HIST("hEventsSelected"), nbin - 0.5); @@ -1765,6 +2612,7 @@ struct Lambdak0sflattenicity { if (collision.isInelGt0() && isINELgt0true) { rEventSelection.fill(HIST("hNEventsMCReco"), 2.5); rEventSelection.fill(HIST("hFlat_RecoColl_MC_INELgt0"), flattenicity); + rEventSelection.fill(HIST("hFlat_RecoColl_MC_INELgt0_TrueFlat"), flattenicityTrue); recoCollIndexINELgt0++; } @@ -1778,33 +2626,35 @@ struct Lambdak0sflattenicity { if (!mcParticle.isPhysicalPrimary()) { continue; } + const bool inV0Rapidity = std::abs(mcParticle.y()) <= v0Sel.v0settingRapidity; + const bool inCascRapidity = std::abs(mcParticle.y()) <= cascSel.cascsettingRapidity; - if (std::abs(mcParticle.y()) > 0.5f) { - continue; - } - - if (mcParticle.pdgCode() == PDG_t::kK0Short) { - rKzeroShort.fill(HIST("Generated_MCRecoColl_INEL_K0Short"), mcParticle.pt(), flattenicity); // K0s + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kK0Short) { + rKzeroShort.fill(HIST("Generated_MCRecoColl_INEL_K0Short"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rKzeroShort.fill(HIST("Generated_MCRecoColl_INELgt0_K0Short"), mcParticle.pt(), flattenicity); // K0s + rKzeroShort.fill(HIST("Generated_MCRecoColl_INELgt0_K0Short"), mcParticle.pt(), flattenicity); + rKzeroShort.fill(HIST("Generated_MCRecoColl_INELgt0_K0Short_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (mcParticle.pdgCode() == PDG_t::kLambda0) { - rLambda.fill(HIST("Generated_MCRecoColl_INEL_Lambda"), mcParticle.pt(), flattenicity); // Lambda + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kLambda0) { + rLambda.fill(HIST("Generated_MCRecoColl_INEL_Lambda"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rLambda.fill(HIST("Generated_MCRecoColl_INELgt0_Lambda"), mcParticle.pt(), flattenicity); // Lambda + rLambda.fill(HIST("Generated_MCRecoColl_INELgt0_Lambda"), mcParticle.pt(), flattenicity); + rLambda.fill(HIST("Generated_MCRecoColl_INELgt0_Lambda_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (mcParticle.pdgCode() == PDG_t::kLambda0Bar) { - rAntiLambda.fill(HIST("Generated_MCRecoColl_INEL_AntiLambda"), mcParticle.pt(), flattenicity); // AntiLambda + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kLambda0Bar) { + rAntiLambda.fill(HIST("Generated_MCRecoColl_INEL_AntiLambda"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rAntiLambda.fill(HIST("Generated_MCRecoColl_INELgt0_AntiLambda"), mcParticle.pt(), flattenicity); // AntiLambda + rAntiLambda.fill(HIST("Generated_MCRecoColl_INELgt0_AntiLambda"), mcParticle.pt(), flattenicity); + rAntiLambda.fill(HIST("Generated_MCRecoColl_INELgt0_AntiLambda_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (std::abs(mcParticle.pdgCode()) == PDG_t::kXiPlusBar) { - rXi.fill(HIST("Generated_MCRecoColl_INEL_Xi"), mcParticle.pt(), flattenicity); // Xi + if (inCascRapidity && std::abs(mcParticle.pdgCode()) == PDG_t::kXiMinus) { + rXi.fill(HIST("Generated_MCRecoColl_INEL_Xi"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rXi.fill(HIST("Generated_MCRecoColl_INELgt0_Xi"), mcParticle.pt(), flattenicity); // Xi + rXi.fill(HIST("Generated_MCRecoColl_INELgt0_Xi"), mcParticle.pt(), flattenicity); + rXi.fill(HIST("Generated_MCRecoColl_INELgt0_Xi_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } } @@ -1825,6 +2675,7 @@ struct Lambdak0sflattenicity { if (recoCollIndexINELgt0 > 0) { rEventSelection.fill(HIST("hNEventsMCGenReco"), 1.5); rEventSelection.fill(HIST("hFlat_GenRecoColl_MC_INELgt0"), flattenicity); + rEventSelection.fill(HIST("hFlat_GenRecoColl_MC_INELgt0_TrueFlat"), flattenicityTrue); } //===================================== @@ -1836,54 +2687,117 @@ struct Lambdak0sflattenicity { if (!mcParticle.isPhysicalPrimary()) { continue; } + const bool inV0Rapidity = std::abs(mcParticle.y()) <= v0Sel.v0settingRapidity; + const bool inCascRapidity = std::abs(mcParticle.y()) <= cascSel.cascsettingRapidity; - if (std::abs(mcParticle.y()) > 0.5f) { - continue; - } - - if (mcParticle.pdgCode() == PDG_t::kK0Short) { - rKzeroShort.fill(HIST("pGen_MCGenRecoColl_INEL_K0Short"), mcParticle.pt(), flattenicity); // K0s + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kK0Short) { + rKzeroShort.fill(HIST("pGen_MCGenRecoColl_INEL_K0Short"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rKzeroShort.fill(HIST("pGen_MCGenRecoColl_INELgt0_K0Short"), mcParticle.pt(), flattenicity); // K0s + rKzeroShort.fill(HIST("pGen_MCGenRecoColl_INELgt0_K0Short"), mcParticle.pt(), flattenicity); + rKzeroShort.fill(HIST("pGen_MCGenRecoColl_INELgt0_K0Short_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (mcParticle.pdgCode() == PDG_t::kLambda0) { - rLambda.fill(HIST("pGen_MCGenRecoColl_INEL_Lambda"), mcParticle.pt(), flattenicity); // Lambda + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kLambda0) { + rLambda.fill(HIST("pGen_MCGenRecoColl_INEL_Lambda"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rLambda.fill(HIST("pGen_MCGenRecoColl_INELgt0_Lambda"), mcParticle.pt(), flattenicity); // Lambda + rLambda.fill(HIST("pGen_MCGenRecoColl_INELgt0_Lambda"), mcParticle.pt(), flattenicity); + rLambda.fill(HIST("pGen_MCGenRecoColl_INELgt0_Lambda_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (mcParticle.pdgCode() == PDG_t::kLambda0Bar) { - rAntiLambda.fill(HIST("pGen_MCGenRecoColl_INEL_AntiLambda"), mcParticle.pt(), flattenicity); // AntiLambda + if (inV0Rapidity && mcParticle.pdgCode() == PDG_t::kLambda0Bar) { + rAntiLambda.fill(HIST("pGen_MCGenRecoColl_INEL_AntiLambda"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rAntiLambda.fill(HIST("pGen_MCGenRecoColl_INELgt0_AntiLambda"), mcParticle.pt(), flattenicity); // AntiLambda + rAntiLambda.fill(HIST("pGen_MCGenRecoColl_INELgt0_AntiLambda"), mcParticle.pt(), flattenicity); + rAntiLambda.fill(HIST("pGen_MCGenRecoColl_INELgt0_AntiLambda_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } - if (std::abs(mcParticle.pdgCode()) == PDG_t::kXiPlusBar) { - rXi.fill(HIST("pGen_MCGenRecoColl_INEL_Xi"), mcParticle.pt(), flattenicity); // Xi + if (inCascRapidity && std::abs(mcParticle.pdgCode()) == PDG_t::kXiMinus) { + rXi.fill(HIST("pGen_MCGenRecoColl_INEL_Xi"), mcParticle.pt(), flattenicity); if (recoCollIndexINELgt0 > 0) { - rXi.fill(HIST("pGen_MCGenRecoColl_INELgt0_Xi"), mcParticle.pt(), flattenicity); // Xi + rXi.fill(HIST("pGen_MCGenRecoColl_INELgt0_Xi"), mcParticle.pt(), flattenicity); + rXi.fill(HIST("pGen_MCGenRecoColl_INELgt0_Xi_TrueFlat"), mcParticle.pt(), flattenicityTrue); } } } } - TRandom2* fRand = new TRandom2(); // Cascade Analysis Starts here - using DauTracks = soa::Join; + using DauTracks = soa::Join; + using LabeledDauTracks = soa::Join; using LabeledCascades = soa::Join; - // float ctauxiPDG = 4.91; // from PDG - // float ctauomegaPDG = 2.461; // from PDG - void processDataRun3Cascade(soa::Join + bool isSelectedXi(TCollision const& collision, TCascade const& casc, + TTrack const& posDaughterTrack, TTrack const& negDaughterTrack, + TTrack const& bacDaughterTrack, float ctauXi) + { + const bool isXiMinus = casc.sign() < 0; + const auto& protonDaughter = isXiMinus ? posDaughterTrack : negDaughterTrack; + const auto& pionDaughter = isXiMinus ? negDaughterTrack : posDaughterTrack; + + // PID, one hypothesis per daughter + if (std::abs(protonDaughter.tpcNSigmaPr()) > trkPid.nSigmaTPCProton || + std::abs(pionDaughter.tpcNSigmaPi()) > trkPid.nSigmaTPCPion || + std::abs(bacDaughterTrack.tpcNSigmaPi()) > trkPid.nSigmaTPCPion) { + return false; + } + + // track quality + if (!isSelectedDaughterTrack(posDaughterTrack, cascSel.nTPCcrossedRows) || + !isSelectedDaughterTrack(negDaughterTrack, cascSel.nTPCcrossedRows) || + !isSelectedDaughterTrack(bacDaughterTrack, cascSel.nTPCcrossedRows)) { + return false; + } + + // topology + const float dcav0pv = casc.dcav0topv(collision.posX(), collision.posY(), collision.posZ()); + const float cosPAcasc = casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()); + const float cosPAv0 = casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()); + + // DCA to PV per daughter role, not per charge + const float dcaBaryonToPV = isXiMinus ? casc.dcapostopv() : casc.dcanegtopv(); + const float dcaMesonToPV = isXiMinus ? casc.dcanegtopv() : casc.dcapostopv(); + + if (std::abs(dcaBaryonToPV) < cascSel.cascsettingDCAbaryontopv || std::abs(dcaMesonToPV) < cascSel.cascsettingDCAmesontopv || + std::abs(casc.dcabachtopv()) < v0Sel.v0settingDCAbactopv || casc.dcaV0daughters() > v0Sel.v0settingDCAv0dau || + dcav0pv < cascSel.cascsettingDCAv0toPV || casc.dcacascdaughters() > cascSel.cascsettingDCAv0bach) { + return false; + } + // bachelor-baryon veto + if (casc.bachBaryonCosPA() > cascSel.cascsettingCosPAbaryonbach || + std::abs(casc.bachBaryonDCAxyToPV()) < cascSel.cascsettingDCAxybaryonbach) { + return false; + } + if (cosPAcasc < cascSel.cascsettingCosPAcascPV || cosPAv0 < cascSel.cascsettingCosPAv0PV || + casc.cascradius() < cascSel.cascsettingcascradius || casc.v0radius() < cascSel.cascsettingv0radius) { + return false; + } + + // kinematics and competing species + if (std::abs(casc.yXi()) > cascSel.cascsettingRapidity || ctauXi > kCtauXi * cascSel.cascsettingproplifetime) { + return false; + } + if (std::abs(casc.mLambda() - o2::constants::physics::MassLambda0) > cascSel.cascsettingMassRejectionLambdaXi || + std::abs(casc.mOmega() - o2::constants::physics::MassOmegaMinus) < cascSel.cascsettingMassRejectioOmegaXi) { + return false; + } + + return true; + } + + void processDataRun3Cascade(soa::Join::iterator const& collision, aod::CascDataExt const& Cascades, aod::V0Datas const&, DauTracks const& tracks, soa::Join const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0s*/) { - if (applyEvSel && - !(isEventSelected(collision))) { // Checking if the event passes the - // selection criteria + const bool own = (eventHistOwner == kOwnerDataCasc); + if (evSel.applyEvSel && + !(isEventSelected(collision, own))) { // Checking if the event passes the + // selection criteria return; } @@ -1892,9 +2806,21 @@ struct Lambdak0sflattenicity { auto vtxX = collision.posX(); float flattenicity = estimateFlattenicity(collision, tracks); + if (own) { + fillFlattenicityStages(); + } + if (flattenicity < 0.f) { + return; + } + if (own) { - rEventSelection.fill(HIST("hVertexZ"), vtxZ); - rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hVertexZ"), vtxZ); + rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hCentFT0M"), collision.centFT0M()); + rEventSelection.fill(HIST("hCentFT0MvsFlattenicity"), collision.centFT0M(), flattenicity); + fillFlatDistRec(flattenicity, collision.centFT0M(), collision.isInelGt0()); + fillChargedRec(tracks, flattenicity); + } for (const auto& casc : Cascades) { @@ -1904,119 +2830,241 @@ struct Lambdak0sflattenicity { float cascPos = std::hypot(casc.x() - vtxX, casc.y() - vtxY, casc.z() - vtxZ); float cascTotMom = std::hypot(casc.px(), casc.py(), casc.pz()); - float ctauXi = pdgmassXi * cascPos / (cascTotMom + 1e-13); - // float ctauOmega =pdgmassOmega * cascPos / (cascTotMom + 1e-13); - float dcav0pv = casc.dcav0topv(vtxX, vtxY, vtxZ); - float cosPAcasc = casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()); + float ctauXi = o2::constants::physics::MassXiMinus * cascPos / (cascTotMom + 1e-13); float cosPAv0 = casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()); float massXi = casc.mXi(); rXi.fill(HIST("hMassXi"), massXi); // Cascade - if (posDaughterTrack.tpcNSigmaPi() < nSigmaTPCPion && negDaughterTrack.tpcNSigmaPi() < nSigmaTPCPion && bacDaughterTrack.tpcNSigmaPi() < nSigmaTPCPion && - posDaughterTrack.tpcNSigmaKa() < nSigmaTPCKaon && negDaughterTrack.tpcNSigmaKa() < nSigmaTPCKaon && bacDaughterTrack.tpcNSigmaKa() < nSigmaTPCKaon && - posDaughterTrack.tpcNSigmaPr() < nSigmaTPCProton && negDaughterTrack.tpcNSigmaPr() < nSigmaTPCProton && bacDaughterTrack.tpcNSigmaPr() < nSigmaTPCProton && - posDaughterTrack.tpcNClsCrossedRows() > nTPCcrossedRows && negDaughterTrack.tpcNClsCrossedRows() > nTPCcrossedRows && bacDaughterTrack.tpcNClsCrossedRows() > nTPCcrossedRows && - std::abs(posDaughterTrack.eta()) < cfgTrkEtaCut && std::abs(negDaughterTrack.eta()) < cfgTrkEtaCut && std::abs(bacDaughterTrack.eta()) < cfgTrkEtaCut && - casc.dcapostopv() > v0settingDCApostopv && casc.dcanegtopv() > v0settingDCAnegtopv && casc.dcabachtopv() > v0settingDCAbactopv && casc.dcaV0daughters() < v0settingDCAv0dau && dcav0pv > cascsettingDCAv0toPV && - casc.dcacascdaughters() < cascsettingDCAv0bach && casc.bachBaryonDCAxyToPV() < cascsettingDCAxybaryonbach && cosPAcasc > cascsettingCosPAcascPV && cosPAv0 > cascsettingCosPAv0PV && - casc.cascradius() > cascsettingcascradius && casc.v0radius() > cascsettingv0radius && - std::abs(casc.yXi()) < cascsettingRapidity && ctauXi < 4.91 * cascsettingproplifetime && - std::abs(casc.mLambda() - pdgmassLambda) < cascsettingMassRejectionLambdaXi && std::abs(casc.mOmega() - pdgmassOmega) > cascsettingMassRejectioOmegaXi) { + if (isSelectedXi(collision, casc, posDaughterTrack, negDaughterTrack, bacDaughterTrack, ctauXi)) { + + const bool isXiMinus = casc.sign() < 0; + const auto& protonDaughter = isXiMinus ? posDaughterTrack : negDaughterTrack; + const auto& pionDaughter = isXiMinus ? negDaughterTrack : posDaughterTrack; rXi.fill(HIST("hMassXiSelected"), massXi); rXi.fill(HIST("hDCAV0DaughtersXi"), casc.dcaV0daughters()); rXi.fill(HIST("hV0CosPAXi"), cosPAv0); - rXi.fill(HIST("hrapidityXi"), casc.rapidity(1)); + rXi.fill(HIST("hrapidityXi"), casc.yXi()); rXi.fill(HIST("hctauXi"), ctauXi); - rXi.fill(HIST("h2DdecayRadiusXi"), casc.v0radius()); + rXi.fill(HIST("h2DdecayRadiusXi"), casc.cascradius()); rXi.fill(HIST("hMassXipT"), massXi, casc.pt()); rXi.fill(HIST("hMassXipTFlat"), massXi, casc.pt(), flattenicity); + rXi.fill(HIST("hNSigmaProtonFromXi"), protonDaughter.tpcNSigmaPr(), protonDaughter.tpcInnerParam()); + rXi.fill(HIST("hNSigmaPionFromXi"), pionDaughter.tpcNSigmaPi(), pionDaughter.tpcInnerParam()); + rXi.fill(HIST("hNSigmaBachPionFromXi"), bacDaughterTrack.tpcNSigmaPi(), bacDaughterTrack.tpcInnerParam()); } } } - Preslice> perColCasc = aod::track::collisionId; + Preslice perColCasc = aod::cascade::collisionId; + Preslice perColDauTracksMC = aod::track::collisionId; SliceCache cacheCasc; - void processRecMCRun3Cascade(soa::Join const& collisions, - soa::Join const& Cascades, - aod::V0Datas const&, soa::Join const& tracks, + LabeledCascades const& Cascades, + aod::V0Datas const&, LabeledDauTracks const& tracks, soa::Join const& /*bcs*/, aod::FT0s const& /*ft0s*/, aod::FV0As const& /*fv0s*/, aod::McCollisions const&, aod::McParticles const& mcParticles) { + const bool own = (eventHistOwner == kOwnerRecMCCasc); for (const auto& collision : collisions) { - if (applyEvSel && - !(isEventSelected(collision))) { // Checking if the event passes the - // selection criteria - return; + if (evSel.applyEvSel && + !(isEventSelected(collision, own))) { // Checking if the event passes the + // selection criteria + continue; + } + + if (!collision.has_mcCollision()) { + continue; } auto vtxZ = collision.posZ(); auto vtxY = collision.posY(); auto vtxX = collision.posX(); - float flattenicity = estimateFlattenicity(collision, tracks); - - rEventSelection.fill(HIST("hVertexZ"), vtxZ); - rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + auto tracksThisCollision = tracks.sliceBy(perColDauTracksMC, collision.globalIndex()); + float flattenicity = estimateFlattenicity(collision, tracksThisCollision); + if (own) { + fillFlattenicityStages(); + } + if (flattenicity < 0.f) { + continue; + } + if (own) { + + rEventSelection.fill(HIST("hVertexZ"), vtxZ); + rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hCentFT0M"), collision.centFT0M()); + rEventSelection.fill(HIST("hCentFT0MvsFlattenicity"), collision.centFT0M(), flattenicity); + fillFlatDistRec(flattenicity, collision.centFT0M(), collision.isInelGt0()); + fillChargedRec(tracksThisCollision, flattenicity); + } auto cascsThisCollision = Cascades.sliceBy(perColCasc, collision.globalIndex()); const auto& mcCollision = collision.mcCollision_as(); + const auto particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cacheCasc); + const float flattenicityMCGen = estimateFlattenicityGen(particlesInCollision, own); + if (own) { + rEventSelection.fill(HIST("hFlattenicityDistributionMCGen_Rec"), flattenicityMCGen); + rEventSelection.fill(HIST("hFlattenicity_Corr_Gen_vs_Rec"), flattenicityMCGen, flattenicity); + rEventSelection.fill(HIST("hFlatGenVsRecFine"), flattenicityMCGen, flattenicity); + fillFlatDistGenInRec(flattenicityMCGen, collision.centFT0M(), collision.isInelGt0()); + } + for (const auto& casc : cascsThisCollision) { - auto posDaughterTrack = casc.posTrack_as(); - auto negDaughterTrack = casc.negTrack_as(); - auto bacDaughterTrack = casc.bachelor_as(); + // MC truth matching, else the spectra keep the combinatorial background + if (!casc.has_mcParticle()) { + continue; + } + const auto& cascMcParticle = casc.mcParticle(); + + auto posDaughterTrack = casc.posTrack_as(); + auto negDaughterTrack = casc.negTrack_as(); + auto bacDaughterTrack = casc.bachelor_as(); float cascPos = std::hypot(casc.x() - vtxX, casc.y() - vtxY, casc.z() - vtxZ); float cascTotMom = std::hypot(casc.px(), casc.py(), casc.pz()); - float ctauXi = pdgmassXi * cascPos / (cascTotMom + 1e-13); - // float ctauOmega =pdgmassOmega * cascPos / (cascTotMom + 1e-13); - float dcav0pv = casc.dcav0topv(vtxX, vtxY, vtxZ); - float cosPAcasc = casc.casccosPA(collision.posX(), collision.posY(), collision.posZ()); + float ctauXi = o2::constants::physics::MassXiMinus * cascPos / (cascTotMom + 1e-13); float cosPAv0 = casc.v0cosPA(collision.posX(), collision.posY(), collision.posZ()); float massXi = casc.mXi(); rXi.fill(HIST("hMassXi"), massXi); // Cascade - if (posDaughterTrack.tpcNSigmaPi() < nSigmaTPCPion && negDaughterTrack.tpcNSigmaPi() < nSigmaTPCPion && bacDaughterTrack.tpcNSigmaPi() < nSigmaTPCPion && - posDaughterTrack.tpcNSigmaKa() < nSigmaTPCKaon && negDaughterTrack.tpcNSigmaKa() < nSigmaTPCKaon && bacDaughterTrack.tpcNSigmaKa() < nSigmaTPCKaon && - posDaughterTrack.tpcNSigmaPr() < nSigmaTPCProton && negDaughterTrack.tpcNSigmaPr() < nSigmaTPCProton && bacDaughterTrack.tpcNSigmaPr() < nSigmaTPCProton && - posDaughterTrack.tpcNClsCrossedRows() > nTPCcrossedRows && negDaughterTrack.tpcNClsCrossedRows() > nTPCcrossedRows && bacDaughterTrack.tpcNClsCrossedRows() > nTPCcrossedRows && - std::abs(posDaughterTrack.eta()) < cfgTrkEtaCut && std::abs(negDaughterTrack.eta()) < cfgTrkEtaCut && std::abs(bacDaughterTrack.eta()) < cfgTrkEtaCut && - casc.dcapostopv() > v0settingDCApostopv && casc.dcanegtopv() > v0settingDCAnegtopv && casc.dcabachtopv() > v0settingDCAbactopv && casc.dcaV0daughters() < v0settingDCAv0dau && dcav0pv > cascsettingDCAv0toPV && - casc.dcacascdaughters() < cascsettingDCAv0bach && casc.bachBaryonDCAxyToPV() < cascsettingDCAxybaryonbach && cosPAcasc > cascsettingCosPAcascPV && cosPAv0 > cascsettingCosPAv0PV && - casc.cascradius() > cascsettingcascradius && casc.v0radius() > cascsettingv0radius && - std::abs(casc.yXi()) < cascsettingRapidity && ctauXi < 4.91 * cascsettingproplifetime && - std::abs(casc.mLambda() - pdgmassLambda) < cascsettingMassRejectionLambdaXi && std::abs(casc.mOmega() - pdgmassOmega) > cascsettingMassRejectioOmegaXi) { + if (std::abs(cascMcParticle.pdgCode()) == PDG_t::kXiMinus && + isSelectedXi(collision, casc, posDaughterTrack, negDaughterTrack, bacDaughterTrack, ctauXi)) { + + const bool isPrimaryCasc = cascMcParticle.isPhysicalPrimary(); + if (!isPrimaryCasc) { + float motherPt = -1.f; + const int motherIndex = getFeedDownMother(cascMcParticle, motherPt); + rXi.fill(HIST("hMassFeedDownXipTFlat"), massXi, casc.pt(), flattenicity); + rXi.fill(HIST("hFeedDownXiPtVsMotherPt"), casc.pt(), motherPt); + rXi.fill(HIST("hFeedDownXiMatrix"), casc.pt(), motherPt, flattenicity, motherIndex); + rXi.fill(HIST("hFeedDownXiMotherPdg"), motherIndex); + } + if (!isPrimaryCasc && eventClass.requirePrimaryMC) { + continue; + } + + const bool isXiMinus = casc.sign() < 0; + const auto& protonDaughter = isXiMinus ? posDaughterTrack : negDaughterTrack; + const auto& pionDaughter = isXiMinus ? negDaughterTrack : posDaughterTrack; rXi.fill(HIST("hMassXiSelected"), massXi); rXi.fill(HIST("hDCAV0DaughtersXi"), casc.dcaV0daughters()); rXi.fill(HIST("hV0CosPAXi"), cosPAv0); - rXi.fill(HIST("hrapidityXi"), casc.rapidity(1)); + rXi.fill(HIST("hrapidityXi"), casc.yXi()); rXi.fill(HIST("hctauXi"), ctauXi); - rXi.fill(HIST("h2DdecayRadiusXi"), casc.v0radius()); + rXi.fill(HIST("h2DdecayRadiusXi"), casc.cascradius()); rXi.fill(HIST("hMassXipT"), massXi, casc.pt()); rXi.fill(HIST("hMassXipTFlat"), massXi, casc.pt(), flattenicity); + rXi.fill(HIST("hMassXipTTrueFlat"), massXi, casc.pt(), flattenicityMCGen); + rXi.fill(HIST("hPtResXi"), cascMcParticle.pt(), + (casc.pt() - cascMcParticle.pt()) / cascMcParticle.pt()); + rXi.fill(HIST("hNSigmaProtonFromXi"), protonDaughter.tpcNSigmaPr(), protonDaughter.tpcInnerParam()); + rXi.fill(HIST("hNSigmaPionFromXi"), pionDaughter.tpcNSigmaPi(), pionDaughter.tpcInnerParam()); + rXi.fill(HIST("hNSigmaBachPionFromXi"), bacDaughterTrack.tpcNSigmaPi(), bacDaughterTrack.tpcInnerParam()); } } - const auto particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cacheCasc); - float flattenicityMCGen = estimateFlattenicityFV0MC(particlesInCollision); - rEventSelection.fill(HIST("hFlattenicityDistributionMCGen_Rec"), flattenicityMCGen); - rEventSelection.fill(HIST("hFlattenicity_Corr_Gen_vs_Rec"), flattenicityMCGen, flattenicity); + const bool isInelGt0Rec = collision.isInelGt0(); for (const auto& mcParticle : particlesInCollision) { - if (mcParticle.isPhysicalPrimary() && std::abs(mcParticle.y()) < 0.5f && std::abs(mcParticle.pdgCode()) == PDG_t::kXiPlusBar) { - rXi.fill(HIST("Generated_MCRecoCollCheck_INEL_Xi"), mcParticle.pt(), flattenicity); // K0s + if (mcParticle.isPhysicalPrimary() && std::abs(mcParticle.y()) <= cascSel.cascsettingRapidity && + std::abs(mcParticle.pdgCode()) == PDG_t::kXiMinus) { + rXi.fill(HIST("Generated_MCRecoCollCheck_INEL_Xi"), mcParticle.pt(), flattenicity); + if (isInelGt0Rec) { + rXi.fill(HIST("Generated_MCRecoCollCheck_INELgt0_Xi"), mcParticle.pt(), flattenicity); + rXi.fill(HIST("Generated_MCRecoCollCheck_INELgt0_Xi_TrueFlat"), mcParticle.pt(), flattenicityMCGen); + } } } } } + // ================== Percentile determination pass ====================== // + // + // Event selection and flattenicity only, so a short run gives the 1-rho + // distribution the percentile boundaries are read off. The boundaries then go + // back into binning.axisFlat for the spectra pass, where one class is exactly + // one bin. + + void processFlatDistData( + soa::Join::iterator const& collision, + TrackCandidates const& tracks, + soa::Join const& /*bcs*/, aod::FT0s const& /*ft0s*/, + aod::FV0As const& /*fv0s*/) + { + const bool own = (eventHistOwner == kOwnerFlatDistData); + if (evSel.applyEvSel && !isEventSelected(collision, own)) { + return; + } + const float flattenicity = estimateFlattenicity(collision, tracks); + if (own) { + fillFlattenicityStages(); + } + if (flattenicity < 0.f) { + return; + } + if (own) { + rEventSelection.fill(HIST("hVertexZ"), collision.posZ()); + rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hCentFT0M"), collision.centFT0M()); + rEventSelection.fill(HIST("hCentFT0MvsFlattenicity"), collision.centFT0M(), flattenicity); + fillFlatDistRec(flattenicity, collision.centFT0M(), collision.isInelGt0()); + fillChargedRec(tracks, flattenicity); + } + } + + void processFlatDistMC( + soa::Join const& collisions, + aod::McCollisions const&, TrackCandidatesMC const& tracks, + soa::Join const& /*bcs*/, aod::FT0s const& /*ft0s*/, + aod::FV0As const& /*fv0s*/, aod::McParticles const& mcParticles) + { + const bool own = (eventHistOwner == kOwnerFlatDistMC); + for (const auto& collision : collisions) { + if (evSel.applyEvSel && !isEventSelected(collision, own)) { + continue; + } + if (!collision.has_mcCollision()) { + continue; + } + auto tracksThisCollision = tracks.sliceBy(perColTracksMC, collision.globalIndex()); + const float flattenicity = estimateFlattenicity(collision, tracksThisCollision); + if (own) { + fillFlattenicityStages(); + } + if (flattenicity < 0.f) { + continue; + } + if (!own) { + continue; + } + rEventSelection.fill(HIST("hVertexZ"), collision.posZ()); + rEventSelection.fill(HIST("hFlattenicityDistribution"), flattenicity); + rEventSelection.fill(HIST("hCentFT0M"), collision.centFT0M()); + rEventSelection.fill(HIST("hCentFT0MvsFlattenicity"), collision.centFT0M(), flattenicity); + fillFlatDistRec(flattenicity, collision.centFT0M(), collision.isInelGt0()); + fillChargedRec(tracksThisCollision, flattenicity); + + const auto& mcCollision = collision.mcCollision_as(); + const auto particlesInCollision = mcParticles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache1); + const float flattenicityMCGen = estimateFlattenicityGen(particlesInCollision); + rEventSelection.fill(HIST("hFlattenicityDistributionMCGen_Rec"), flattenicityMCGen); + rEventSelection.fill(HIST("hFlattenicity_Corr_Gen_vs_Rec"), flattenicityMCGen, flattenicity); + rEventSelection.fill(HIST("hFlatGenVsRecFine"), flattenicityMCGen, flattenicity); + fillFlatDistGenInRec(flattenicityMCGen, collision.centFT0M(), collision.isInelGt0()); + } + } + + PROCESS_SWITCH(Lambdak0sflattenicity, processFlatDistData, "Flattenicity distribution only, data", false); + PROCESS_SWITCH(Lambdak0sflattenicity, processFlatDistMC, "Flattenicity distribution only, MC", false); PROCESS_SWITCH(Lambdak0sflattenicity, processDataRun3LambdaK0s, "Process Run 3 Data LambdaK0s", false); PROCESS_SWITCH(Lambdak0sflattenicity, processRecMCLambdaK0s, "Process Run 3 MC reconstructed LambdaK0s", false); PROCESS_SWITCH(Lambdak0sflattenicity, processGenMC, "Process Run 3 MC generated", false); - PROCESS_SWITCH(Lambdak0sflattenicity, processDataRun3Cascade, "Process Run 3 Data Cascade", true); + PROCESS_SWITCH(Lambdak0sflattenicity, processDataRun3Cascade, "Process Run 3 Data Cascade", false); PROCESS_SWITCH(Lambdak0sflattenicity, processRecMCRun3Cascade, "Process Run 3 mc Rec Cascade", false); }; diff --git a/PWGLF/Tasks/Strangeness/lambdakzeroanalysisMC.cxx b/PWGLF/Tasks/Strangeness/lambdakzeroanalysisMC.cxx index e95ac8eb5ee..2b43a75921d 100644 --- a/PWGLF/Tasks/Strangeness/lambdakzeroanalysisMC.cxx +++ b/PWGLF/Tasks/Strangeness/lambdakzeroanalysisMC.cxx @@ -256,7 +256,7 @@ struct lambdakzeroAnalysisMc { } if (particleMotherOfNeg.has_mothers()) { auto particleGrandMothersOfNegTable = particleMotherOfNeg.mothers_as(); - auto particleGrandMotherOfNeg = particleGrandMothersOfNegTable[0]; + const auto& particleGrandMotherOfNeg = particleGrandMothersOfNegTable[0]; if (particleGrandMotherOfNeg.pdgCode() == 3312 || particleGrandMotherOfNeg.pdgCode() == 3322) { registry.fill(HIST("hLambdaFeedDownMatrix"), particleMotherOfNeg.pt(), particleGrandMotherOfNeg.pt()); } @@ -289,7 +289,7 @@ struct lambdakzeroAnalysisMc { } if (particleMotherOfNeg.has_mothers()) { auto particleGrandMothersOfNegTable = particleMotherOfNeg.mothers_as(); - auto particleGrandMotherOfNeg = particleGrandMothersOfNegTable[0]; + const auto& particleGrandMotherOfNeg = particleGrandMothersOfNegTable[0]; if (particleGrandMotherOfNeg.pdgCode() == -3312 || particleGrandMotherOfNeg.pdgCode() == -3322) { registry.fill(HIST("hAntiLambdaFeedDownMatrix"), particleMotherOfNeg.pt(), particleGrandMotherOfNeg.pt()); } diff --git a/PWGLF/Tasks/Strangeness/lambdalambda.cxx b/PWGLF/Tasks/Strangeness/lambdalambda.cxx index 2a861ea7ac9..91ddb9c66ae 100644 --- a/PWGLF/Tasks/Strangeness/lambdalambda.cxx +++ b/PWGLF/Tasks/Strangeness/lambdalambda.cxx @@ -224,7 +224,7 @@ struct lambdalambda { ROOT::Math::PxPyPzMVector RecoV02Rot, RecoV0V0Rot; template - bool eventSelected(TCollision collision) + bool eventSelected(const TCollision& collision) { if (!collision.sel8()) { return 0; @@ -527,6 +527,9 @@ struct lambdalambda { RanPhi += RecoV02.Phi(); RecoV02Rot = ROOT::Math::PxPyPzMVector(RecoV02.Pt() * std::cos(RanPhi), RecoV02.Pt() * std::sin(RanPhi), RecoV02.Pz(), RecoV02.M()); RecoV0V0Rot = RecoV01 + RecoV02Rot; + if (std::abs(RecoV0V0Rot.Rapidity()) > cfgV0V0RapMax) + continue; + histos.fill(HIST("h_InvMass_rot"), RecoV0V0Rot.M(), RecoV0V0Rot.Pt(), centrality, V01Tag + V02Tag); } } @@ -582,7 +585,7 @@ struct lambdalambda { SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{vertexAxis, centAxis}, true}; + BinningType colBinning{{vertexAxis, centAxis}}; Preslice tracksPerCollisionV0 = aod::v0data::collisionId; void processDataMixed(EventCandidates const& collisions, diff --git a/PWGLF/Tasks/Strangeness/lambdapolarization.cxx b/PWGLF/Tasks/Strangeness/lambdapolarization.cxx index a35a319ef3b..df2e1f48373 100644 --- a/PWGLF/Tasks/Strangeness/lambdapolarization.cxx +++ b/PWGLF/Tasks/Strangeness/lambdapolarization.cxx @@ -397,7 +397,7 @@ struct lambdapolarization { ROOT::Math::PxPyPzMVector ProtonVec, PionVec, LambdaVec, ProtonBoostedVec, PionBoostedVec; template - bool eventSelected(TCollision collision) + bool eventSelected(const TCollision& collision) { if (!collision.sel8()) { return 0; diff --git a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx index d2d3064d37e..45d7fd49b01 100644 --- a/PWGLF/Tasks/Strangeness/lambdapolsp.cxx +++ b/PWGLF/Tasks/Strangeness/lambdapolsp.cxx @@ -14,6 +14,7 @@ #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/SPCalibrationTables.h" +#include "PWGLF/DataModel/ZDC2StageCalibrationTables.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" @@ -39,15 +40,18 @@ #include #include -#include #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include #include #include +#include #include #include #include +#include + +#include #include #include #include @@ -83,7 +87,6 @@ struct lambdapolsp { struct : ConfigurableGroup { Configurable additionalEvSel{"additionalEvSel", false, "additionalEvSel"}; Configurable additionalEvSel2{"additionalEvSel2", false, "additionalEvSel2"}; - Configurable additionalEvSel3{"additionalEvSel3", false, "additionalEvSel3"}; Configurable additionalEvSel4{"additionalEvSel4", false, "additionalEvSel4"}; Configurable cfgMaxOccupancy{"cfgMaxOccupancy", 1000, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; Configurable cfgMinOccupancy{"cfgMinOccupancy", 0, "maximum occupancy of tracks in neighbouring collisions in a given time range"}; @@ -92,10 +95,9 @@ struct lambdapolsp { Configurable cqvas{"cqvas", false, "change q vectors after shift correction"}; Configurable normbymag{"normbymag", false, "normalize by magnitude of q vectors for SP"}; Configurable useprofile{"useprofile", 3, "flag to select profile vs Sparse"}; - Configurable sys{"sys", 1, "flag to select systematic source"}; Configurable centestim{"centestim", 0, "flag to select centrality estimator"}; - Configurable dosystematic{"dosystematic", false, "flag to perform systematic study"}; Configurable needetaaxis{"needetaaxis", false, "flag to use last axis"}; + struct : ConfigurableGroup { Configurable doRandomPsi{"doRandomPsi", true, "randomize psi"}; Configurable doRandomPsiAC{"doRandomPsiAC", true, "randomize psiAC"}; @@ -170,18 +172,7 @@ struct lambdapolsp { Configurable spNbins{"spNbins", 2000, "Number of bins in sp"}; Configurable lbinsp{"lbinsp", -1.0, "lower bin value in sp histograms"}; Configurable hbinsp{"hbinsp", 1.0, "higher bin value in sp histograms"}; - // Configurable CentNbins{"CentNbins", 16, "Number of bins in cent histograms"}; - // Configurable lbinCent{"lbinCent", 0.0, "lower bin value in cent histograms"}; - // Configurable hbinCent{"hbinCent", 80.0, "higher bin value in cent histograms"}; } binGrp; - /* - ConfigurableAxis configcentAxis{"configcentAxis", {VARIABLE_WIDTH, 0.0, 10.0, 40.0, 80.0}, "Cent V0M"}; - ConfigurableAxis configthnAxispT{"configthnAxisPt", {VARIABLE_WIDTH, 0.2, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 6.5, 8.0, 10.0, 100.0}, "#it{p}_{T} (GeV/#it{c})"}; - ConfigurableAxis configetaAxis{"configetaAxis", {VARIABLE_WIDTH, -0.8, -0.4, -0.2, 0, 0.2, 0.4, 0.8}, "Eta"}; - ConfigurableAxis configthnAxisPol{"configthnAxisPol", {VARIABLE_WIDTH, -1.0, -0.6, -0.2, 0, 0.2, 0.4, 0.8}, "Pol"}; - ConfigurableAxis configbinAxis{"configbinAxis", {VARIABLE_WIDTH, -0.8, -0.4, -0.2, 0, 0.2, 0.4, 0.8}, "BA"}; - */ - // ConfigurableAxis configphiAxis{"configphiAxis", {VARIABLE_WIDTH, 0.0, 0.2, 0.4, 0.8, 1.0, 2.0, 2.5, 3.0, 4.0, 5.0, 5.5, 6.28}, "PhiAxis"}; struct : ConfigurableGroup { Configurable isQA{"isQA", true, "Flag to fill QA"}; @@ -191,7 +182,19 @@ struct lambdapolsp { ConfigurableAxis vzfineAxis{"vzfineAxis", {VARIABLE_WIDTH, 0.0, 10.0, 40.0, 80.0}, "vz fine axis"}; ConfigurableAxis qxZDCAxis{"qxZDCAxis", {VARIABLE_WIDTH, 0.0, 10.0, 40.0, 80.0}, "qx axis"}; ConfigurableAxis psiAxis{"psiAxis", {VARIABLE_WIDTH, 0.0, 10.0, 40.0, 80.0}, "psi axis"}; + + Configurable fillNUA{"fillNUA", true, "fillNUA"}; + Configurable useNUA{"useNUA", true, "useNUA"}; + ConfigurableAxis nuacentAxis{"nuaCentAxis", {4, 10.f, 50.f}, "centrality (%)"}; + ConfigurableAxis nuavzAxis{"nuaVzAxis", {5, -10.f, 10.f}, "V_{z} (cm)"}; + ConfigurableAxis nuaetaAxis{"nuaEtaAxis", {4, -0.8f, 0.8f}, "#eta"}; + ConfigurableAxis nuasignAxis{"nuaSignAxis", {2, -1.5f, 1.5f}, "charge sign"}; + ConfigurableAxis nuaphiAxis{"nuaPhiAxis", {72, 0.f, static_cast(TMath::TwoPi())}, "#varphi"}; + Configurable ConfNUA{"ConfNUA", "Users/p/prottay/My/Object/NUAwgtschk", "Path to NUA"}; + + Configurable fillnominal{"fillnominal", true, "flag to fill default histograms"}; } QAgrp; + struct : ConfigurableGroup { Configurable requireRCTFlagChecker{"requireRCTFlagChecker", true, "Check event quality in run condition table"}; Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_hadronPID", "Evt sel: RCT flag checker label"}; @@ -227,11 +230,91 @@ struct lambdapolsp { ConfigurableAxis axiseta{"axiseta", {16, -0.8, 0.8}, "eta axis"}; } distGrp; + struct : ConfigurableGroup { + + Configurable doTopoSyst{ + "doTopoSyst", false, + "Run randomized topology systematic variations"}; + + Configurable nTopoSyst{ + "nTopoSyst", 400, + "Number of randomized topological selections"}; + + Configurable topoSystSeed{ + "topoSystSeed", 12345, + "Random seed for topology systematics"}; + + // ---------------------------------------------------- + // Range of the CUT VALUE that will be randomized. + // + // Set min/max to the desired systematic ranges in JSON. + // ---------------------------------------------------- + + Configurable systLifeCutMin{ + "systLifeCutMin", 20.f, + "Minimum value from which lifetime upper cut is sampled"}; + + Configurable systLifeCutMax{ + "systLifeCutMax", 20.f, + "Maximum value from which lifetime upper cut is sampled"}; + + Configurable systCPACutMin{ + "systCPACutMin", 0.9998f, + "Minimum value from which CPA lower cut is sampled"}; + + Configurable systCPACutMax{ + "systCPACutMax", 0.9998f, + "Maximum value from which CPA lower cut is sampled"}; + + Configurable systDCADaughCutMin{ + "systDCADaughCutMin", 0.2f, + "Minimum value from which DCA daughters upper cut is sampled"}; + + Configurable systDCADaughCutMax{ + "systDCADaughCutMax", 0.2f, + "Maximum value from which DCA daughters upper cut is sampled"}; + + Configurable systDCAPrCutMin{ + "systDCAPrCutMin", 0.05f, + "Minimum value from which proton DCA-to-PV lower cut is sampled"}; + + Configurable systDCAPrCutMax{ + "systDCAPrCutMax", 0.05f, + "Maximum value from which proton DCA-to-PV lower cut is sampled"}; + + Configurable systDCAPiCutMin{ + "systDCAPiCutMin", 0.05f, + "Minimum value from which pion DCA-to-PV lower cut is sampled"}; + + Configurable systDCAPiCutMax{ + "systDCAPiCutMax", 0.05f, + "Maximum value from which pion DCA-to-PV lower cut is sampled"}; + + Configurable systRadiusCutMin{ + "systRadiusCutMin", 0.8f, + "Minimum value from which V0 minimum-radius cut is sampled"}; + + Configurable systRadiusCutMax{ + "systRadiusCutMax", 4.0f, + "Maximum value from which V0 minimum-radius cut is sampled"}; + + } systGrp; + RCTFlagsChecker rctChecker; SliceCache cache; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + struct TopoSystCuts { + float lifeMax; + float cpaMin; + float dcaDaughtersMax; + float dcaPrMin; + float dcaPiMin; + float radiusMin; + }; + std::vector topoSystCuts; + void init(o2::framework::InitContext&) { @@ -240,13 +323,23 @@ struct lambdapolsp { AxisSpec thnAxisres{binGrp.resNbins, binGrp.lbinres, binGrp.hbinres, "Reso"}; AxisSpec thnAxisInvMass{binGrp.IMNbins, binGrp.lbinIM, binGrp.hbinIM, "#it{M} (GeV/#it{c}^{2})"}; AxisSpec spAxis = {binGrp.spNbins, binGrp.lbinsp, binGrp.hbinsp, "Sp"}; - // AxisSpec qxZDCAxis = {binGrp.QxyNbins, binGrp.lbinQxy, binGrp.hbinQxy, "Qx"}; - // AxisSpec centAxis = {CentNbins, lbinCent, hbinCent, "V0M (%)"}; std::vector runaxes = {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configthnAxisPol, axisGrp.configcentAxis}; if (needetaaxis) runaxes.insert(runaxes.end(), {axisGrp.configbinAxis}); - std::vector runaxes2 = {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configcentAxis}; + + AxisSpec systIDAxis{systGrp.nTopoSyst.value, -0.5, static_cast(systGrp.nTopoSyst.value) - 0.5, "systID"}; + std::vector runaxesSyst = { + thnAxisInvMass, + axisGrp.configthnAxispT, + axisGrp.configthnAxisPol, + axisGrp.configcentAxis}; + + if (needetaaxis) { + runaxesSyst.push_back(axisGrp.configbinAxis); + } + + runaxesSyst.push_back(systIDAxis); if (checkwithpub) { if (useprofile == 2) { @@ -258,49 +351,8 @@ struct lambdapolsp { histos.add("hpuxyQxypvscentpteta", "hpuxyQxypvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpoddv1vscentpteta", "hpoddv1vscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpevenv1vscentpteta", "hpevenv1vscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpv21", "hpv21", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpv22", "hpv22", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpv23", "hpv23", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Tx1Ax1Cvscentpteta", "hpx2Tx1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Ty1Ay1Cvscentpteta", "hpx2Ty1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tx1Ay1Cvscentpteta", "hpy2Tx1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Ty1Ax1Cvscentpteta", "hpy2Ty1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Ax1Cvscentpteta", "hpx1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Ay1Cvscentpteta", "hpy1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Avscentpteta", "hpx1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Cvscentpteta", "hpx1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Avscentpteta", "hpy1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Cvscentpteta", "hpy1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - - histos.add("hpx2Tx1Avscentpteta", "hpx2Tx1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Tx1Cvscentpteta", "hpx2Tx1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Ty1Avscentpteta", "hpx2Ty1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Ty1Cvscentpteta", "hpx2Ty1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tx1Avscentpteta", "hpy2Tx1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Ty1Cvscentpteta", "hpy2Ty1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Ty1Avscentpteta", "hpy2Ty1Avscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tx1Cvscentpteta", "hpy2Tx1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx1Ay1Cvscentpteta", "hpx1Ay1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy1Ax1Cvscentpteta", "hpy1Ax1Cvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpx2Tvscentpteta", "hpx2Tvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpy2Tvscentpteta", "hpy2Tvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuxvscentpteta", "hpuxvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpuyvscentpteta", "hpuyvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - /* - histos.add("hpuxvscentptetaneg", "hpuxvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuyvscentptetaneg", "hpuyvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - - histos.add("hpuxQxpvscentptetaneg", "hpuxQxpvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuyQypvscentptetaneg", "hpuyQypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuxQxtvscentptetaneg", "hpuxQxtvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuyQytvscentptetaneg", "hpuyQytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuxyQxytvscentptetaneg", "hpuxyQxytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpuxyQxypvscentptetaneg", "hpuxyQxypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpoddv1vscentptetaneg", "hpoddv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - histos.add("hpevenv1vscentptetaneg", "hpevenv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, configthnAxispT, configetaAxis, spAxis}, true); - */ - histos.add("hpQxtQxpvscent", "hpQxtQxpvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQytQypvscent", "hpQytQypvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQxytpvscent", "hpQxytpvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); @@ -311,6 +363,31 @@ struct lambdapolsp { histos.add("hpQxtvscent", "hpQxtvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQypvscent", "hpQypvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQytvscent", "hpQytvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); + + histos.add("hpQxtQxpvscentptetaTrack", + "QxZNAQxZNC track weighted;centrality;p_{T};#eta;Q_{x}^{ZNA}Q_{x}^{ZNC}", + kTHnSparseF, + {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}); + + histos.add("hpQytQypvscentptetaTrack", + "QyZNAQyZNC track weighted;centrality;p_{T};#eta;Q_{y}^{ZNA}Q_{y}^{ZNC}", + kTHnSparseF, + {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}); + + histos.add("hpQxytpvscentptetaTrack", + "QxQx + QyQy track weighted;centrality;p_{T};#eta;Q_{x}Q_{x}+Q_{y}Q_{y}", + kTHnSparseF, + {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}); + + histos.add("hpQxpQytvscentptetaTrack", + "QxZNAQyZNC track weighted;centrality;p_{T};#eta;Q_{x}^{ZNA}Q_{y}^{ZNC}", + kTHnSparseF, + {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}); + + histos.add("hpQxtQypvscentptetaTrack", + "QxZNCQyZNA track weighted;centrality;p_{T};#eta;Q_{x}^{ZNC}Q_{y}^{ZNA}", + kTHnSparseF, + {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}); } else { histos.add("hpuxQxpvscentpteta", "hpuxQxpvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpuyQypvscentpteta", "hpuyQypvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); @@ -323,17 +400,6 @@ struct lambdapolsp { histos.add("hpuxvscentpteta", "hpuxvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); histos.add("hpuyvscentpteta", "hpuyvscentpteta", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - /*histos.add("hpuxvscentptetaneg", "hpuxvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuyvscentptetaneg", "hpuyvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - - histos.add("hpuxQxpvscentptetaneg", "hpuxQxpvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuyQypvscentptetaneg", "hpuyQypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuxQxtvscentptetaneg", "hpuxQxtvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuyQytvscentptetaneg", "hpuyQytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuxyQxytvscentptetaneg", "hpuxyQxytvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpuxyQxypvscentptetaneg", "hpuxyQxypvscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpoddv1vscentptetaneg", "hpoddv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true); - histos.add("hpevenv1vscentptetaneg", "hpevenv1vscentptetaneg", HistType::kTHnSparseF, {axisGrp.configcentAxis, axisGrp.configthnAxispT, axisGrp.configetaAxis, spAxis}, true);*/ histos.add("hpQxtQxpvscent", "hpQxtQxpvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQytQypvscent", "hpQytQypvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); @@ -349,14 +415,6 @@ struct lambdapolsp { } histos.add("hCentrality", "Centrality distribution", kTH1F, {{axisGrp.configcentAxis}}); - // histos.add("hpsiApsiC", "hpsiApsiC", kTHnSparseF, {psiACAxis, psiACAxis}); - // histos.add("hpsiApsiC", "hpsiApsiC", kTH2F, {psiACAxis, psiACAxis}); - // histos.add("hphiminuspsiA", "hphiminuspisA", kTH1F, {{50, 0, 6.28}}, true); - // histos.add("hphiminuspsiC", "hphiminuspisC", kTH1F, {{50, 0, 6.28}}, true); - // histos.add("hCentrality0", "Centrality distribution0", kTH1F, {{centAxis}}); - // histos.add("hCentrality1", "Centrality distribution1", kTH1F, {{centAxis}}); - // histos.add("hCentrality2", "Centrality distribution2", kTH1F, {{centAxis}}); - // histos.add("hCentrality3", "Centrality distribution3", kTH1F, {{centAxis}}); if (!checkwithpub) { // histos.add("hVtxZ", "Vertex distribution in Z;Z (cm)", kTH1F, {{20, -10.0, 10.0}}); @@ -377,7 +435,18 @@ struct lambdapolsp { histos.add("hpQxytpvscent", "hpQxytpvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQxtQypvscent", "hpQxtQypvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); histos.add("hpQxpQytvscent", "hpQxpQytvscent", HistType::kTHnSparseF, {axisGrp.configcentAxis, spAxis}, true); + + histos.add("hSparseLambdaPolSP_xAwgt", "hSparseLambdaPolSP_xAwgt", HistType::kTHnSparseF, runaxes, true); + histos.add("hSparseLambdaPolSP_yAwgt", "hSparseLambdaPolSP_yAwgt", HistType::kTHnSparseF, runaxes, true); + histos.add("hSparseLambdaPolSP_xCwgt", "hSparseLambdaPolSP_xCwgt", HistType::kTHnSparseF, runaxes, true); + histos.add("hSparseLambdaPolSP_yCwgt", "hSparseLambdaPolSP_yCwgt", HistType::kTHnSparseF, runaxes, true); + + histos.add("hSparseAntiLambdaPolSP_xAwgt", "hSparseAntiLambdaPolSP_xAwgt", HistType::kTHnSparseF, runaxes, true); + histos.add("hSparseAntiLambdaPolSP_yAwgt", "hSparseAntiLambdaPolSP_yAwgt", HistType::kTHnSparseF, runaxes, true); + histos.add("hSparseAntiLambdaPolSP_xCwgt", "hSparseAntiLambdaPolSP_xCwgt", HistType::kTHnSparseF, runaxes, true); + histos.add("hSparseAntiLambdaPolSP_yCwgt", "hSparseAntiLambdaPolSP_yCwgt", HistType::kTHnSparseF, runaxes, true); } + if (usesubdet) { histos.add("hSparseLambdaCosPsiA", "hSparseLambdaCosPsiA", HistType::kTHnSparseF, runaxes, true); histos.add("hSparseLambdaSinPsiA", "hSparseLambdaSinPsiA", HistType::kTHnSparseF, runaxes, true); @@ -422,12 +491,12 @@ struct lambdapolsp { // histos.add("hSparseLambda_corr2b", "hSparseLambda_corr2b", HistType::kTHnSparseF, runaxes, true); histos.add("hSparseAntiLambda_corr2a", "hSparseAntiLambda_corr2a", HistType::kTHnSparseF, runaxes, true); // histos.add("hSparseAntiLambda_corr2b", "hSparseAntiLambda_corr2b", HistType::kTHnSparseF, runaxes, true); - if (randGrp.useSP) { + /*if (randGrp.useSP) { histos.add("hSparseAntiLambda_avgux", "hSparseAntiLambda_avgux", HistType::kTHnSparseF, {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configthnAxisPol, axisGrp.configcentAxis}, true); histos.add("hSparseAntiLambda_avguy", "hSparseAntiLambda_avguy", HistType::kTHnSparseF, {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configthnAxisPol, axisGrp.configcentAxis}, true); histos.add("hSparseLambda_avgux", "hSparseLambda_avgux", HistType::kTHnSparseF, {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configthnAxisPol, axisGrp.configcentAxis}, true); histos.add("hSparseLambda_avguy", "hSparseLambda_avguy", HistType::kTHnSparseF, {thnAxisInvMass, axisGrp.configthnAxispT, axisGrp.configthnAxisPol, axisGrp.configcentAxis}, true); - } + }*/ } if (distGrp.filldist) { @@ -462,12 +531,12 @@ struct lambdapolsp { histos.add("hLcosphiminuspsiAvseta", "hLcosphiminuspsiAvseta", HistType::kTH2D, {{distGrp.axiscosphiminuspsi}, {distGrp.axiseta}}); histos.add("hLcosphiminuspsivseta", "hLcosphiminuspsivseta", HistType::kTH2D, {{distGrp.axiscosphiminuspsi}, {distGrp.axiseta}}); } - + /* histos.add("hSparseGenLambda", "hSparseGenLambda", HistType::kTHnSparseF, runaxes2, true); histos.add("hSparseGenAntiLambda", "hSparseGenAntiLambda", HistType::kTHnSparseF, runaxes2, true); histos.add("hSparseRecLambda", "hSparseRecLambda", HistType::kTHnSparseF, runaxes2, true); histos.add("hSparseRecAntiLambda", "hSparseRecAntiLambda", HistType::kTHnSparseF, runaxes2, true); - + */ if (QAgrp.isQA) { histos.add("hCentQxZDCA", "hCentQxZDCA", kTH2F, {{QAgrp.centfineAxis}, {QAgrp.qxZDCAxis}}); histos.add("hCentQyZDCA", "hCentQyZDCA", kTH2F, {{QAgrp.centfineAxis}, {QAgrp.qxZDCAxis}}); @@ -494,6 +563,10 @@ struct lambdapolsp { histos.add("PsiZDC", "PsiZDC", kTH2F, {QAgrp.centfineAxis, QAgrp.psiAxis}); } + if (QAgrp.fillNUA) { + histos.add("hNUA", "hNUA", HistType::kTHnSparseF, {QAgrp.nuacentAxis, QAgrp.nuavzAxis, QAgrp.nuaetaAxis, QAgrp.nuasignAxis, QAgrp.nuaphiAxis}); + } + ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); @@ -507,6 +580,101 @@ struct lambdapolsp { } if (useResoRBR) hwgtRESO = ccdb->getForTimeStamp(ConfResoPath.value, cfgCcdbParam.nolaterthan.value); + + if (systGrp.doTopoSyst) { + + topoSystCuts.clear(); + topoSystCuts.reserve(systGrp.nTopoSyst.value); + + TRandom3 systRnd(systGrp.topoSystSeed.value); + + auto drawUniform = [&](float a, float b) -> float { + const float lo = std::min(a, b); + const float hi = std::max(a, b); + + if (std::abs(hi - lo) < 1.e-12f) { + return lo; + } + + return systRnd.Uniform(lo, hi); + }; + + for (int i = 0; i < systGrp.nTopoSyst.value; ++i) { + + TopoSystCuts cuts; + + cuts.lifeMax = + drawUniform(systGrp.systLifeCutMin.value, + systGrp.systLifeCutMax.value); + + cuts.cpaMin = + drawUniform(systGrp.systCPACutMin.value, + systGrp.systCPACutMax.value); + + cuts.dcaDaughtersMax = + drawUniform(systGrp.systDCADaughCutMin.value, + systGrp.systDCADaughCutMax.value); + + cuts.dcaPrMin = + drawUniform(systGrp.systDCAPrCutMin.value, + systGrp.systDCAPrCutMax.value); + + cuts.dcaPiMin = + drawUniform(systGrp.systDCAPiCutMin.value, + systGrp.systDCAPiCutMax.value); + + cuts.radiusMin = + drawUniform(systGrp.systRadiusCutMin.value, + systGrp.systRadiusCutMax.value); + + topoSystCuts.push_back(cuts); + + LOGF(info, + "TopoSyst %d: lifeMax=%.4f, cpaMin=%.6f, dcaDaughtersMax=%.4f, " + "dcaPrMin=%.4f, dcaPiMin=%.4f, radiusMin=%.4f", + i, cuts.lifeMax, cuts.cpaMin, cuts.dcaDaughtersMax, cuts.dcaPrMin, cuts.dcaPiMin, cuts.radiusMin); + } + + LOGF(info, + "Generated %d random topology systematic combinations with seed %d", + systGrp.nTopoSyst.value, + systGrp.topoSystSeed.value); + } + + if (systGrp.doTopoSyst) { + + histos.add("hSparseLambdaPolSyst", + "hSparseLambdaPolSyst", + HistType::kTHnSparseF, + runaxesSyst, + true); + + histos.add("hSparseLambdaPolwgtSyst", + "hSparseLambdaPolwgtSyst", + HistType::kTHnSparseF, + runaxesSyst, + true); + + histos.add("hSparseAntiLambdaPolSyst", + "hSparseAntiLambdaPolSyst", + HistType::kTHnSparseF, + runaxesSyst, + true); + + histos.add("hSparseAntiLambdaPolwgtSyst", + "hSparseAntiLambdaPolwgtSyst", + HistType::kTHnSparseF, + runaxesSyst, + true); + + histos.add("hSparseAntiLambda_corr1aSyst", "hSparseAntiLambda_corr1aSyst", HistType::kTHnSparseF, runaxesSyst, true); + histos.add("hSparseAntiLambda_corr1bSyst", "hSparseAntiLambda_corr1bSyst", HistType::kTHnSparseF, runaxesSyst, true); + histos.add("hSparseAntiLambda_corr2aSyst", "hSparseAntiLambda_corr2aSyst", HistType::kTHnSparseF, runaxesSyst, true); + + histos.add("hSparseLambda_corr1aSyst", "hSparseLambda_corr1aSyst", HistType::kTHnSparseF, runaxesSyst, true); + histos.add("hSparseLambda_corr1bSyst", "hSparseLambda_corr1bSyst", HistType::kTHnSparseF, runaxesSyst, true); + histos.add("hSparseLambda_corr2aSyst", "hSparseLambda_corr2aSyst", HistType::kTHnSparseF, runaxesSyst, true); + } } template @@ -519,7 +687,8 @@ struct lambdapolsp { } template - bool SelectionV0(Collision const& collision, V0 const& candidate) + // bool SelectionV0(Collision const& collision, V0 const& candidate) + bool SelectionV0(Collision const& collision, V0 const& candidate, bool applyTopoCuts = true) { if (TMath::Abs(candidate.dcav0topv()) > cMaxV0DCA) { return false; @@ -537,21 +706,27 @@ struct lambdapolsp { if (pT < ConfV0PtMin) { return false; } - if (dcaDaughv0 > ConfV0DCADaughMax) { - return false; - } - if (cpav0 < ConfV0CPAMin) { - return false; - } - if (tranRad < ConfV0TranRadV0Min) { - return false; + + if (applyTopoCuts) { + if (dcaDaughv0 > ConfV0DCADaughMax) { + return false; + } + if (cpav0 < ConfV0CPAMin) { + return false; + } + if (tranRad < ConfV0TranRadV0Min) { + return false; + } + + if (analyzeLambda && TMath::Abs(CtauLambda) > cMaxV0LifeTime) { + return false; + } } + if (tranRad > ConfV0TranRadV0Max) { return false; } - if (analyzeLambda && TMath::Abs(CtauLambda) > cMaxV0LifeTime) { - return false; - } + if (analyzeK0s && TMath::Abs(CtauK0s) > cMaxV0LifeTime) { return false; } @@ -611,7 +786,8 @@ struct lambdapolsp { } template - bool isCompatible(TV0 const& v0, int pid /*0: lambda, 1: antilambda*/) + // bool isCompatible(TV0 const& v0, int pid /*0: lambda, 1: antilambda*/) + bool isCompatible(TV0 const& v0, int pid /*0: lambda, 1: antilambda*/, bool applyDcaCuts = true) { // checks if this V0 is compatible with the requested hypothesis @@ -651,14 +827,82 @@ struct lambdapolsp { return false; } - if (pid == 0 && (TMath::Abs(v0.dcapostopv()) < cMinV0DCAPr || TMath::Abs(v0.dcanegtopv()) < cMinV0DCAPi)) { + if (applyDcaCuts) { + + if (pid == 0 && (TMath::Abs(v0.dcapostopv()) < cMinV0DCAPr || TMath::Abs(v0.dcanegtopv()) < cMinV0DCAPi)) { + return false; + } + if (pid == 1 && (TMath::Abs(v0.dcapostopv()) < cMinV0DCAPi || TMath::Abs(v0.dcanegtopv()) < cMinV0DCAPr)) { + return false; + } + } + // if we made it this far, it's good + return true; + } + + template + bool passesTopoSystematic(Collision const& collision, + TV0 const& v0, + int pid, + const TopoSystCuts& cut) + { + const float dcaDaughters = + TMath::Abs(v0.dcaV0daughters()); + + const float cpa = + v0.v0cosPA(); + + const float radius = + v0.v0radius(); + + const float ctau = + TMath::Abs( + v0.distovertotmom(collision.posX(), + collision.posY(), + collision.posZ()) * + massLambda); + + // randomized V0 topology cuts + if (dcaDaughters > cut.dcaDaughtersMax) { + return false; + } + + if (cpa < cut.cpaMin) { return false; } - if (pid == 1 && (TMath::Abs(v0.dcapostopv()) < cMinV0DCAPi || TMath::Abs(v0.dcanegtopv()) < cMinV0DCAPr)) { + + if (ctau > cut.lifeMax) { return false; } - // if we made it this far, it's good + if (radius < cut.radiusMin) { + return false; + } + + // Lambda + if (pid == 0) { + + if (TMath::Abs(v0.dcapostopv()) < cut.dcaPrMin) { + return false; + } + + if (TMath::Abs(v0.dcanegtopv()) < cut.dcaPiMin) { + return false; + } + } + + // AntiLambda + if (pid == 1) { + + if (TMath::Abs(v0.dcanegtopv()) < cut.dcaPrMin) { + return false; + } + + if (TMath::Abs(v0.dcapostopv()) < cut.dcaPiMin) { + return false; + } + } + return true; } @@ -739,7 +983,7 @@ struct lambdapolsp { void fillHistograms(bool tag1, bool tag2, const ROOT::Math::PxPyPzMVector& particle, const ROOT::Math::PxPyPzMVector& daughter, double psiZDCC, double psiZDCA, double psiZDC, double centrality, - double candmass, double candpt, float desbinvalue, double acvalue, double wgtfactor, double resowgt) + double candmass, double candpt, float desbinvalue, double acvalue, double wgtfactor, double resowgt, const std::vector* systIDs = nullptr, int systSpecies = 0) { TRandom3 randPhi(0); @@ -790,6 +1034,20 @@ struct lambdapolsp { auto PolSP_A = uy * modqxZDCA - ux * modqyZDCA; // u_y QxA - u_x QyA auto PolSP_C = uy * modqxZDCC - ux * modqyZDCC; // u_y QxC - u_x QyC + // SP numerator components separately + auto PolSP_xA = -ux * modqyZDCA; + auto PolSP_yA = uy * modqxZDCA; + + auto PolSP_xC = -ux * modqyZDCC; + auto PolSP_yC = uy * modqxZDCC; + + // acceptance-corrected versions + auto PolSP_xAwgt = PolSP_xA / acvalue; + auto PolSP_yAwgt = PolSP_yA / acvalue; + + auto PolSP_xCwgt = PolSP_xC / acvalue; + auto PolSP_yCwgt = PolSP_yC / acvalue; + if (randGrp.useSP) { Pol = PolSP; PolA = PolSP_A; @@ -798,130 +1056,278 @@ struct lambdapolsp { PolAwgt = PolSP_A / acvalue; PolCwgt = PolSP_C / acvalue; } + ////////////////////////////// - // Fill histograms using constructed names - if (tag2) { - if (needetaaxis) { - if (usesubdet) { - histos.fill(HIST("hSparseAntiLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); - } - histos.fill(HIST("hSparseAntiLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); - if (usesubdet) { - histos.fill(HIST("hSparseAntiLambdaPolA"), candmass, candpt, PolA, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolC"), candmass, candpt, PolC, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, desbinvalue, wgtfactor); - } - histos.fill(HIST("hSparseAntiLambdaPol"), candmass, candpt, Pol, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolwgt"), candmass, candpt, Polwgt, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, desbinvalue, wgtfactor); - // histos.fill(HIST("hSparseAntiLambda_corr1c"), candmass, candpt, phiphiStar, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseAntiLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, desbinvalue, wgtfactor); - // histos.fill(HIST("hSparseAntiLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, desbinvalue, wgtfactor); - if (randGrp.useSP) { - histos.fill(HIST("hSparseAntiLambda_avgux"), candmass, candpt, ux, centrality); - histos.fill(HIST("hSparseAntiLambda_avguy"), candmass, candpt, uy, centrality); - } - } else { - if (usesubdet) { - histos.fill(HIST("hSparseAntiLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, wgtfactor); - } - histos.fill(HIST("hSparseAntiLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, wgtfactor); - if (usesubdet) { - histos.fill(HIST("hSparseAntiLambdaPolA"), candmass, candpt, PolA, centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolC"), candmass, candpt, PolC, centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, wgtfactor); + if (QAgrp.fillnominal) { + // Fill histograms using constructed names + if (tag2) { + if (needetaaxis) { + if (usesubdet) { + histos.fill(HIST("hSparseAntiLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); + } + histos.fill(HIST("hSparseAntiLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); + if (usesubdet) { + histos.fill(HIST("hSparseAntiLambdaPolA"), candmass, candpt, PolA, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolC"), candmass, candpt, PolC, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, desbinvalue, wgtfactor); + } + histos.fill(HIST("hSparseAntiLambdaPol"), candmass, candpt, Pol, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolwgt"), candmass, candpt, Polwgt, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, desbinvalue, wgtfactor); + // histos.fill(HIST("hSparseAntiLambda_corr1c"), candmass, candpt, phiphiStar, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, desbinvalue, wgtfactor); + // histos.fill(HIST("hSparseAntiLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, desbinvalue, wgtfactor); + if (randGrp.useSP) { + // histos.fill(HIST("hSparseAntiLambda_avgux"), candmass, candpt, ux, centrality); + // histos.fill(HIST("hSparseAntiLambda_avguy"), candmass, candpt, uy, centrality); + + histos.fill(HIST("hSparseAntiLambdaPolwgt"), candmass, candpt, Polwgt, centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_xAwgt"), + candmass, candpt, PolSP_xAwgt, + centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_yAwgt"), + candmass, candpt, PolSP_yAwgt, + centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_xCwgt"), + candmass, candpt, PolSP_xCwgt, + centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_yCwgt"), + candmass, candpt, PolSP_yCwgt, + centrality, desbinvalue, wgtfactor); + } + + } else { + if (usesubdet) { + histos.fill(HIST("hSparseAntiLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, wgtfactor); + } + histos.fill(HIST("hSparseAntiLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, wgtfactor); + if (usesubdet) { + histos.fill(HIST("hSparseAntiLambdaPolA"), candmass, candpt, PolA, centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolC"), candmass, candpt, PolC, centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, wgtfactor); + } + histos.fill(HIST("hSparseAntiLambdaPol"), candmass, candpt, Pol, centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambdaPolwgt"), candmass, candpt, Polwgt, centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, wgtfactor); + // histos.fill(HIST("hSparseAntiLambda_corr1c"), candmass, candpt, phiphiStar, centrality, wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, wgtfactor); + // histos.fill(HIST("hSparseAntiLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, wgtfactor); + if (randGrp.useSP) { + // histos.fill(HIST("hSparseAntiLambda_avgux"), candmass, candpt, ux, centrality); + // histos.fill(HIST("hSparseAntiLambda_avguy"), candmass, candpt, uy, centrality); + + histos.fill(HIST("hSparseAntiLambdaPolwgt"), candmass, candpt, Polwgt, centrality, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_xAwgt"), + candmass, candpt, PolSP_xAwgt, + centrality, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_yAwgt"), + candmass, candpt, PolSP_yAwgt, + centrality, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_xCwgt"), + candmass, candpt, PolSP_xCwgt, + centrality, wgtfactor); + + histos.fill(HIST("hSparseAntiLambdaPolSP_yCwgt"), + candmass, candpt, PolSP_yCwgt, + centrality, wgtfactor); + } } - histos.fill(HIST("hSparseAntiLambdaPol"), candmass, candpt, Pol, centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambdaPolwgt"), candmass, candpt, Polwgt, centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, wgtfactor); - // histos.fill(HIST("hSparseAntiLambda_corr1c"), candmass, candpt, phiphiStar, centrality, wgtfactor); - histos.fill(HIST("hSparseAntiLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, wgtfactor); - // histos.fill(HIST("hSparseAntiLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, wgtfactor); - if (randGrp.useSP) { - histos.fill(HIST("hSparseAntiLambda_avgux"), candmass, candpt, ux, centrality); - histos.fill(HIST("hSparseAntiLambda_avguy"), candmass, candpt, uy, centrality); + } + if (tag1) { + if (needetaaxis) { + if (usesubdet) { + histos.fill(HIST("hSparseLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); + } + histos.fill(HIST("hSparseLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); + if (usesubdet) { + histos.fill(HIST("hSparseLambdaPolA"), candmass, candpt, PolA, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaPolC"), candmass, candpt, PolC, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, desbinvalue, wgtfactor); + } + histos.fill(HIST("hSparseLambdaPol"), candmass, candpt, Pol, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambdaPolwgt"), candmass, candpt, Polwgt, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, desbinvalue, wgtfactor); + // histos.fill(HIST("hSparseLambda_corr1c"), candmass, candpt, phiphiStar, centrality, desbinvalue, wgtfactor); + histos.fill(HIST("hSparseLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, desbinvalue, wgtfactor); + // histos.fill(HIST("hSparseLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, desbinvalue, wgtfactor); + if (randGrp.useSP) { + // histos.fill(HIST("hSparseLambda_avgux"), candmass, candpt, ux, centrality); + // histos.fill(HIST("hSparseLambda_avguy"), candmass, candpt, uy, centrality); + + histos.fill(HIST("hSparseLambdaPolwgt"), candmass, candpt, Polwgt, centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_xAwgt"), + candmass, candpt, PolSP_xAwgt, + centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_yAwgt"), + candmass, candpt, PolSP_yAwgt, + centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_xCwgt"), + candmass, candpt, PolSP_xCwgt, + centrality, desbinvalue, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_yCwgt"), + candmass, candpt, PolSP_yCwgt, + centrality, desbinvalue, wgtfactor); + } + } else { + if (usesubdet) { + histos.fill(HIST("hSparseLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, wgtfactor); + } + histos.fill(HIST("hSparseLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, wgtfactor); + if (usesubdet) { + histos.fill(HIST("hSparseLambdaPolA"), candmass, candpt, PolA, centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaPolC"), candmass, candpt, PolC, centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, wgtfactor); + } + histos.fill(HIST("hSparseLambdaPol"), candmass, candpt, Pol, centrality, wgtfactor); + histos.fill(HIST("hSparseLambdaPolwgt"), candmass, candpt, Polwgt, centrality, wgtfactor); + histos.fill(HIST("hSparseLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, wgtfactor); + histos.fill(HIST("hSparseLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, wgtfactor); + // histos.fill(HIST("hSparseLambda_corr1c"), candmass, candpt, phiphiStar, centrality, wgtfactor); + histos.fill(HIST("hSparseLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, wgtfactor); + // histos.fill(HIST("hSparseLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, wgtfactor); + if (randGrp.useSP) { + // histos.fill(HIST("hSparseLambda_avgux"), candmass, candpt, ux, centrality); + // histos.fill(HIST("hSparseLambda_avguy"), candmass, candpt, uy, centrality); + + histos.fill(HIST("hSparseLambdaPolwgt"), candmass, candpt, Polwgt, centrality, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_xAwgt"), + candmass, candpt, PolSP_xAwgt, + centrality, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_yAwgt"), + candmass, candpt, PolSP_yAwgt, + centrality, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_xCwgt"), + candmass, candpt, PolSP_xCwgt, + centrality, wgtfactor); + + histos.fill(HIST("hSparseLambdaPolSP_yCwgt"), + candmass, candpt, PolSP_yCwgt, + centrality, wgtfactor); + } } } } - if (tag1) { - if (needetaaxis) { - if (usesubdet) { - histos.fill(HIST("hSparseLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, desbinvalue, wgtfactor); - } - histos.fill(HIST("hSparseLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, desbinvalue, wgtfactor); - if (usesubdet) { - histos.fill(HIST("hSparseLambdaPolA"), candmass, candpt, PolA, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaPolC"), candmass, candpt, PolC, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, desbinvalue, wgtfactor); - } - histos.fill(HIST("hSparseLambdaPol"), candmass, candpt, Pol, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambdaPolwgt"), candmass, candpt, Polwgt, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, desbinvalue, wgtfactor); - // histos.fill(HIST("hSparseLambda_corr1c"), candmass, candpt, phiphiStar, centrality, desbinvalue, wgtfactor); - histos.fill(HIST("hSparseLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, desbinvalue, wgtfactor); - // histos.fill(HIST("hSparseLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, desbinvalue, wgtfactor); - if (randGrp.useSP) { - histos.fill(HIST("hSparseLambda_avgux"), candmass, candpt, ux, centrality); - histos.fill(HIST("hSparseLambda_avguy"), candmass, candpt, uy, centrality); - } - } else { - if (usesubdet) { - histos.fill(HIST("hSparseLambdaCosPsiA"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCA))), centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaCosPsiC"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDCC))), centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaSinPsiA"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCA))), centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaSinPsiC"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDCC))), centrality, wgtfactor); - } - histos.fill(HIST("hSparseLambdaCosPsi"), candmass, candpt, (TMath::Cos(GetPhiInRange(psiZDC))), centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaSinPsi"), candmass, candpt, (TMath::Sin(GetPhiInRange(psiZDC))), centrality, wgtfactor); - if (usesubdet) { - histos.fill(HIST("hSparseLambdaPolA"), candmass, candpt, PolA, centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaPolC"), candmass, candpt, PolC, centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaPolAwgt"), candmass, candpt, PolAwgt, centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaPolCwgt"), candmass, candpt, PolCwgt, centrality, wgtfactor); + if (systGrp.doTopoSyst && + systIDs != nullptr && + !systIDs->empty()) { + + for (const auto isyst : *systIDs) { + + // Lambda + if (systSpecies == 1) { + + if (needetaaxis) { + + histos.fill(HIST("hSparseLambdaPolSyst"), + candmass, + candpt, + Pol, + centrality, + desbinvalue, + static_cast(isyst), + wgtfactor); + + histos.fill(HIST("hSparseLambda_corr1aSyst"), candmass, candpt, sinPhiStar, centrality, desbinvalue, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseLambda_corr1bSyst"), candmass, candpt, cosPhiStar, centrality, desbinvalue, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseLambda_corr2aSyst"), candmass, candpt, sinThetaStar, centrality, desbinvalue, static_cast(isyst), wgtfactor); + + } else { + + histos.fill(HIST("hSparseLambdaPolSyst"), + candmass, + candpt, + Pol, + centrality, + static_cast(isyst), + wgtfactor); + + histos.fill(HIST("hSparseLambda_corr1aSyst"), candmass, candpt, sinPhiStar, centrality, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseLambda_corr1bSyst"), candmass, candpt, cosPhiStar, centrality, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseLambda_corr2aSyst"), candmass, candpt, sinThetaStar, centrality, static_cast(isyst), wgtfactor); + } } - histos.fill(HIST("hSparseLambdaPol"), candmass, candpt, Pol, centrality, wgtfactor); - histos.fill(HIST("hSparseLambdaPolwgt"), candmass, candpt, Polwgt, centrality, wgtfactor); - histos.fill(HIST("hSparseLambda_corr1a"), candmass, candpt, sinPhiStar, centrality, wgtfactor); - histos.fill(HIST("hSparseLambda_corr1b"), candmass, candpt, cosPhiStar, centrality, wgtfactor); - // histos.fill(HIST("hSparseLambda_corr1c"), candmass, candpt, phiphiStar, centrality, wgtfactor); - histos.fill(HIST("hSparseLambda_corr2a"), candmass, candpt, sinThetaStar, centrality, wgtfactor); - // histos.fill(HIST("hSparseLambda_corr2b"), candmass, candpt, sinThetaStarcosphiphiStar, centrality, wgtfactor); - if (randGrp.useSP) { - histos.fill(HIST("hSparseLambda_avgux"), candmass, candpt, ux, centrality); - histos.fill(HIST("hSparseLambda_avguy"), candmass, candpt, uy, centrality); + + // AntiLambda + if (systSpecies == 2) { + + if (needetaaxis) { + + histos.fill(HIST("hSparseAntiLambdaPolSyst"), + candmass, + candpt, + Pol, + centrality, + desbinvalue, + static_cast(isyst), + wgtfactor); + + histos.fill(HIST("hSparseAntiLambda_corr1aSyst"), candmass, candpt, sinPhiStar, centrality, desbinvalue, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr1bSyst"), candmass, candpt, cosPhiStar, centrality, desbinvalue, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr2aSyst"), candmass, candpt, sinThetaStar, centrality, desbinvalue, static_cast(isyst), wgtfactor); + + } else { + + histos.fill(HIST("hSparseAntiLambdaPolSyst"), + candmass, + candpt, + Pol, + centrality, + static_cast(isyst), + wgtfactor); + + histos.fill(HIST("hSparseAntiLambda_corr1aSyst"), candmass, candpt, sinPhiStar, centrality, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr1bSyst"), candmass, candpt, cosPhiStar, centrality, static_cast(isyst), wgtfactor); + histos.fill(HIST("hSparseAntiLambda_corr2aSyst"), candmass, candpt, sinThetaStar, centrality, static_cast(isyst), wgtfactor); + } } } } } ROOT::Math::PxPyPzMVector Lambda, AntiLambda, Lambdadummy, AntiLambdadummy, Proton, Pion, AntiProton, AntiPion, fourVecDauCM, K0sdummy, K0s; - ROOT::Math::XYZVector threeVecDauCM, threeVecDauCMXY; - double phiangle = 0.0; - // double angleLambda=0.0; - // double angleAntiLambda=0.0; - double massLambda = o2::constants::physics::MassLambda; - double massK0s = o2::constants::physics::MassK0Short; - double massPr = o2::constants::physics::MassProton; - double massPi = o2::constants::physics::MassPionCharged; + + static constexpr double massLambda = o2::constants::physics::MassLambda; + static constexpr double massK0s = o2::constants::physics::MassK0Short; + static constexpr double massPr = o2::constants::physics::MassProton; + static constexpr double massPi = o2::constants::physics::MassPionCharged; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; Filter centralityFilter = (nabs(aod::cent::centFT0C) < cfgCutCentralityMax && nabs(aod::cent::centFT0C) > cfgCutCentralityMin); @@ -929,17 +1335,151 @@ struct lambdapolsp { Filter dcaCutFilter = (nabs(aod::track::dcaXY) < cfgCutDCAxy) && (nabs(aod::track::dcaZ) < cfgCutDCAz); using EventCandidates = soa::Filtered>; + using EventCandidatescalibstage = soa::Filtered>; using EventCandidatesMC = soa::Filtered>; using AllTrackCandidates = soa::Filtered>; using ResoV0s = aod::V0Datas; TProfile2D* accprofileL; TProfile2D* accprofileAL; - // int currentRunNumber = -999; - // int lastRunNumber = -999; + int currentRunNumber = -999; + int lastRunNumber = -999; + THnSparseF* hNUAWeights = nullptr; using BCsRun3 = soa::Join; + void processDatav1(EventCandidatescalibstage::iterator const& collision, AllTrackCandidates const& tracks, BCsRun3 const&) + { + + if (!collision.sel8()) { + return; + } + double centrality = -999.; + if (centestim == 0) + centrality = collision.centFT0C(); + + if (!collision.triggerSP()) { + return; + } + + if (evselGrp.additionalEvSel && (!collision.selection_bit(aod::evsel::kNoSameBunchPileup) || !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV))) { + return; + } + if (evselGrp.additionalEvSel2 && (collision.trackOccupancyInTimeRange() > evselGrp.cfgMaxOccupancy || collision.trackOccupancyInTimeRange() < evselGrp.cfgMinOccupancy)) { + return; + } + if (evselGrp.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return; + } + + if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { + return; + } + + auto qxZDCA = collision.qxZDCA(); + auto qxZDCC = collision.qxZDCC(); + auto qyZDCA = collision.qyZDCA(); + auto qyZDCC = collision.qyZDCC(); + + histos.fill(HIST("hCentrality"), centrality); + + auto QxtQxp = qxZDCA * qxZDCC; + auto QytQyp = qyZDCA * qyZDCC; + auto Qxytp = QxtQxp + QytQyp; + auto QxpQyt = qxZDCA * qyZDCC; + auto QxtQyp = qxZDCC * qyZDCA; + + histos.fill(HIST("hpQxtQxpvscent"), centrality, QxtQxp); + histos.fill(HIST("hpQytQypvscent"), centrality, QytQyp); + histos.fill(HIST("hpQxytpvscent"), centrality, Qxytp); + histos.fill(HIST("hpQxpQytvscent"), centrality, QxpQyt); + histos.fill(HIST("hpQxtQypvscent"), centrality, QxtQyp); + + int wNUA = 1; + + for (const auto& track : tracks) { + if (!selectionTrack(track)) { + continue; + } + + float sign = track.sign(); + if (sign == 0.0) // removing neutral particles + continue; + + auto ux = TMath::Cos(GetPhiInRange(track.phi())); + auto uy = TMath::Sin(GetPhiInRange(track.phi())); + + auto uxQxp = ux * qxZDCA; + auto uyQyp = uy * qyZDCA; + auto uxyQxyp = uxQxp + uyQyp; + auto uxQxt = ux * qxZDCC; + auto uyQyt = uy * qyZDCC; + auto uxyQxyt = uxQxt + uyQyt; + auto oddv1 = ux * (qxZDCA - qxZDCC) + uy * (qyZDCA - qyZDCC); + auto evenv1 = ux * (qxZDCA + qxZDCC) + uy * (qyZDCA + qyZDCC); + + if (globalpt) { + histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.pt(), track.eta(), uxQxp, wNUA); + histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.pt(), track.eta(), uyQyp, wNUA); + histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.pt(), track.eta(), uxQxt, wNUA); + histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.pt(), track.eta(), uyQyt, wNUA); + + histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux, wNUA); + histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy, wNUA); + + histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyt, wNUA); + histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyp, wNUA); + histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1, wNUA); + histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1, wNUA); + + histos.fill(HIST("hpQxtQxpvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQxp, wNUA); + + histos.fill(HIST("hpQytQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QytQyp, wNUA); + + histos.fill(HIST("hpQxytpvscentptetaTrack"), + centrality, track.pt(), track.eta(), Qxytp, wNUA); + + histos.fill(HIST("hpQxpQytvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxpQyt, wNUA); + + histos.fill(HIST("hpQxtQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQyp, wNUA); + } else { + histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxp, wNUA); + histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyp, wNUA); + histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxt, wNUA); + histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyt, wNUA); + + histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux, wNUA); + histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy, wNUA); + + histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyt, wNUA); + histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyp, wNUA); + histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1, wNUA); + histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1, wNUA); + + histos.fill(HIST("hpQxtQxpvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQxp, wNUA); + + histos.fill(HIST("hpQytQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QytQyp, wNUA); + + histos.fill(HIST("hpQxytpvscentptetaTrack"), + centrality, track.pt(), track.eta(), Qxytp, wNUA); + + histos.fill(HIST("hpQxpQytvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxpQyt, wNUA); + + histos.fill(HIST("hpQxtQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQyp, wNUA); + } + } + } + + PROCESS_SWITCH(lambdapolsp, processDatav1, "Process datav1", false); + void processData(EventCandidates::iterator const& collision, AllTrackCandidates const& tracks, ResoV0s const& V0s, BCsRun3 const&) { @@ -971,10 +1511,6 @@ struct lambdapolsp { return; } // histos.fill(HIST("hCentrality3"), centrality); - if (evselGrp.additionalEvSel3 && (!collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - return; - } - if (evselGrp.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return; } @@ -983,8 +1519,8 @@ struct lambdapolsp { return; } - // currentRunNumber = collision.foundBC_as().runNumber(); auto bc = collision.foundBC_as(); + currentRunNumber = collision.foundBC_as().runNumber(); auto vz = collision.vz(); auto vx = collision.vx(); @@ -1100,6 +1636,32 @@ struct lambdapolsp { auto uy = TMath::Sin(GetPhiInRange(track.phi())); // auto py=track.py(); + if (QAgrp.fillNUA) { + histos.fill(HIST("hNUA"), centrality, collision.posZ(), track.eta(), track.sign(), GetPhiInRange(track.phi())); + } + + float wNUA = 1.f; + if (QAgrp.useNUA && (currentRunNumber != lastRunNumber)) { + hNUAWeights = ccdb->getForTimeStamp(QAgrp.ConfNUA.value, bc.timestamp()); + } + + if (QAgrp.useNUA) { + constexpr int kCent = 0; + constexpr int kVz = 1; + constexpr int kEta = 2; + constexpr int kSign = 3; + constexpr int kPhi = 4; + + Int_t nuaBins[5] = { + hNUAWeights->GetAxis(kCent)->FindFixBin(centrality + 0.00001), + hNUAWeights->GetAxis(kVz)->FindFixBin(collision.posZ() + 0.00001), + hNUAWeights->GetAxis(kEta)->FindFixBin(track.eta() + 0.00001), + hNUAWeights->GetAxis(kSign)->FindFixBin(track.sign()), + hNUAWeights->GetAxis(kPhi)->FindFixBin(GetPhiInRange(track.phi()))}; + + wNUA = hNUAWeights->GetBinContent(nuaBins); + } + auto uxQxp = ux * modqxZDCA; auto uyQyp = uy * modqyZDCA; auto uxyQxyp = uxQxp + uyQyp; @@ -1108,7 +1670,7 @@ struct lambdapolsp { auto uxyQxyt = uxQxt + uyQyt; auto oddv1 = ux * (modqxZDCA - modqxZDCC) + uy * (modqyZDCA - modqyZDCC); auto evenv1 = ux * (modqxZDCA + modqxZDCC) + uy * (modqyZDCA + modqyZDCC); - auto v21 = TMath::Cos(2 * (GetPhiInRange(track.phi()) - psiZDCA - psiZDCC)); + /*auto v21 = TMath::Cos(2 * (GetPhiInRange(track.phi()) - psiZDCA - psiZDCC)); auto v22 = TMath::Cos(2 * (GetPhiInRange(track.phi()) + psiZDCA - psiZDCC)); auto v23 = TMath::Cos(2 * (GetPhiInRange(track.phi()) - psiZDC)); @@ -1135,77 +1697,107 @@ struct lambdapolsp { auto y2Tx1A = TMath::Sin(2 * GetPhiInRange(track.phi())) * modqxZDCA; auto y2Tx1C = TMath::Sin(2 * GetPhiInRange(track.phi())) * modqxZDCC; auto y2Ty1A = TMath::Sin(2 * GetPhiInRange(track.phi())) * modqyZDCA; - auto y2Ty1C = TMath::Sin(2 * GetPhiInRange(track.phi())) * modqyZDCC; + auto y2Ty1C = TMath::Sin(2 * GetPhiInRange(track.phi())) * modqyZDCC;*/ if (globalpt) { // if (sign > 0) { - histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.pt(), track.eta(), uxQxp); - histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.pt(), track.eta(), uyQyp); - histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.pt(), track.eta(), uxQxt); - histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.pt(), track.eta(), uyQyt); - - histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux); - histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy); - - histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyt); - histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyp); - histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1); - histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1); - - histos.fill(HIST("hpv21"), centrality, track.pt(), track.eta(), v21); - histos.fill(HIST("hpv22"), centrality, track.pt(), track.eta(), v22); - histos.fill(HIST("hpv23"), centrality, track.pt(), track.eta(), v23); - - histos.fill(HIST("hpx2Tx1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Tx1Ax1C); - histos.fill(HIST("hpx2Ty1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Ty1Ay1C); - histos.fill(HIST("hpy2Tx1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Tx1Ay1C); - histos.fill(HIST("hpy2Ty1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Ty1Ax1C); - histos.fill(HIST("hpx2Tvscentpteta"), centrality, track.pt(), track.eta(), x2T); - histos.fill(HIST("hpy2Tvscentpteta"), centrality, track.pt(), track.eta(), y2T); - histos.fill(HIST("hpx2Tx1Avscentpteta"), centrality, track.pt(), track.eta(), x2Tx1A); - histos.fill(HIST("hpx2Tx1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Tx1C); - histos.fill(HIST("hpx2Ty1Avscentpteta"), centrality, track.pt(), track.eta(), x2Ty1A); - histos.fill(HIST("hpx2Ty1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Ty1C); - histos.fill(HIST("hpy2Tx1Avscentpteta"), centrality, track.pt(), track.eta(), y2Tx1A); - histos.fill(HIST("hpy2Ty1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Ty1C); - histos.fill(HIST("hpy2Ty1Avscentpteta"), centrality, track.pt(), track.eta(), y2Ty1A); - histos.fill(HIST("hpy2Tx1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Tx1C); - histos.fill(HIST("hpx1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), x1Ax1C); - histos.fill(HIST("hpy1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), y1Ay1C); - histos.fill(HIST("hpx1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), x1Ay1C); - histos.fill(HIST("hpy1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), x1Cy1A); - histos.fill(HIST("hpx1Avscentpteta"), centrality, track.pt(), track.eta(), x1A); - histos.fill(HIST("hpx1Cvscentpteta"), centrality, track.pt(), track.eta(), x1C); - histos.fill(HIST("hpy1Avscentpteta"), centrality, track.pt(), track.eta(), y1A); - histos.fill(HIST("hpy1Cvscentpteta"), centrality, track.pt(), track.eta(), y1C); - + histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.pt(), track.eta(), uxQxp, wNUA); + histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.pt(), track.eta(), uyQyp, wNUA); + histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.pt(), track.eta(), uxQxt, wNUA); + histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.pt(), track.eta(), uyQyt, wNUA); + + histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux, wNUA); + histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy, wNUA); + + histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyt, wNUA); + histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.pt(), track.eta(), uxyQxyp, wNUA); + histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1, wNUA); + histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1, wNUA); + + histos.fill(HIST("hpQxtQxpvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQxp, wNUA); + + histos.fill(HIST("hpQytQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QytQyp, wNUA); + + histos.fill(HIST("hpQxytpvscentptetaTrack"), + centrality, track.pt(), track.eta(), Qxytp, wNUA); + + histos.fill(HIST("hpQxpQytvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxpQyt, wNUA); + + histos.fill(HIST("hpQxtQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQyp, wNUA); + /* + histos.fill(HIST("hpv21"), centrality, track.pt(), track.eta(), v21,wNUA); + histos.fill(HIST("hpv22"), centrality, track.pt(), track.eta(), v22,wNUA); + histos.fill(HIST("hpv23"), centrality, track.pt(), track.eta(), v23,wNUA); + + histos.fill(HIST("hpx2Tx1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Tx1Ax1C,wNUA); + histos.fill(HIST("hpx2Ty1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Ty1Ay1C,wNUA); + histos.fill(HIST("hpy2Tx1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Tx1Ay1C,wNUA); + histos.fill(HIST("hpy2Ty1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Ty1Ax1C,wNUA); + histos.fill(HIST("hpx2Tvscentpteta"), centrality, track.pt(), track.eta(), x2T,wNUA); + histos.fill(HIST("hpy2Tvscentpteta"), centrality, track.pt(), track.eta(), y2T,wNUA); + histos.fill(HIST("hpx2Tx1Avscentpteta"), centrality, track.pt(), track.eta(), x2Tx1A,wNUA); + histos.fill(HIST("hpx2Tx1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Tx1C,wNUA); + histos.fill(HIST("hpx2Ty1Avscentpteta"), centrality, track.pt(), track.eta(), x2Ty1A,wNUA); + histos.fill(HIST("hpx2Ty1Cvscentpteta"), centrality, track.pt(), track.eta(), x2Ty1C,wNUA); + histos.fill(HIST("hpy2Tx1Avscentpteta"), centrality, track.pt(), track.eta(), y2Tx1A,wNUA); + histos.fill(HIST("hpy2Ty1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Ty1C,wNUA); + histos.fill(HIST("hpy2Ty1Avscentpteta"), centrality, track.pt(), track.eta(), y2Ty1A,wNUA); + histos.fill(HIST("hpy2Tx1Cvscentpteta"), centrality, track.pt(), track.eta(), y2Tx1C,wNUA); + histos.fill(HIST("hpx1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), x1Ax1C,wNUA); + histos.fill(HIST("hpy1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), y1Ay1C,wNUA); + histos.fill(HIST("hpx1Ay1Cvscentpteta"), centrality, track.pt(), track.eta(), x1Ay1C,wNUA); + histos.fill(HIST("hpy1Ax1Cvscentpteta"), centrality, track.pt(), track.eta(), x1Cy1A,wNUA); + histos.fill(HIST("hpx1Avscentpteta"), centrality, track.pt(), track.eta(), x1A,wNUA); + histos.fill(HIST("hpx1Cvscentpteta"), centrality, track.pt(), track.eta(), x1C,wNUA); + histos.fill(HIST("hpy1Avscentpteta"), centrality, track.pt(), track.eta(), y1A,wNUA); + histos.fill(HIST("hpy1Cvscentpteta"), centrality, track.pt(), track.eta(), y1C,wNUA); + */ /*} else { - histos.fill(HIST("hpuxQxpvscentptetaneg"), centrality, track.pt(), track.eta(), uxQxp); - histos.fill(HIST("hpuyQypvscentptetaneg"), centrality, track.pt(), track.eta(), uyQyp); - histos.fill(HIST("hpuxQxtvscentptetaneg"), centrality, track.pt(), track.eta(), uxQxt); - histos.fill(HIST("hpuyQytvscentptetaneg"), centrality, track.pt(), track.eta(), uyQyt); - - histos.fill(HIST("hpuxvscentptetaneg"), centrality, track.pt(), track.eta(), ux); - histos.fill(HIST("hpuyvscentptetaneg"), centrality, track.pt(), track.eta(), uy); - - histos.fill(HIST("hpuxyQxytvscentptetaneg"), centrality, track.pt(), track.eta(), uxyQxyt); - histos.fill(HIST("hpuxyQxypvscentptetaneg"), centrality, track.pt(), track.eta(), uxyQxyp); - histos.fill(HIST("hpoddv1vscentptetaneg"), centrality, track.pt(), track.eta(), oddv1); - histos.fill(HIST("hpevenv1vscentptetaneg"), centrality, track.pt(), track.eta(), evenv1); + histos.fill(HIST("hpuxQxpvscentptetaneg"), centrality, track.pt(), track.eta(), uxQxp,wNUA); + histos.fill(HIST("hpuyQypvscentptetaneg"), centrality, track.pt(), track.eta(), uyQyp,wNUA); + histos.fill(HIST("hpuxQxtvscentptetaneg"), centrality, track.pt(), track.eta(), uxQxt,wNUA); + histos.fill(HIST("hpuyQytvscentptetaneg"), centrality, track.pt(), track.eta(), uyQyt,wNUA); + + histos.fill(HIST("hpuxvscentptetaneg"), centrality, track.pt(), track.eta(), ux,wNUA); + histos.fill(HIST("hpuyvscentptetaneg"), centrality, track.pt(), track.eta(), uy,wNUA); + + histos.fill(HIST("hpuxyQxytvscentptetaneg"), centrality, track.pt(), track.eta(), uxyQxyt,wNUA); + histos.fill(HIST("hpuxyQxypvscentptetaneg"), centrality, track.pt(), track.eta(), uxyQxyp,wNUA); + histos.fill(HIST("hpoddv1vscentptetaneg"), centrality, track.pt(), track.eta(), oddv1,wNUA); + histos.fill(HIST("hpevenv1vscentptetaneg"), centrality, track.pt(), track.eta(), evenv1,wNUA); }*/ } else { - histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxp); - histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyp); - histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxt); - histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyt); - - histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux); - histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy); - - histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyt); - histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyp); - histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1); - histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1); + histos.fill(HIST("hpuxQxpvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxp, wNUA); + histos.fill(HIST("hpuyQypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyp, wNUA); + histos.fill(HIST("hpuxQxtvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxQxt, wNUA); + histos.fill(HIST("hpuyQytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uyQyt, wNUA); + + histos.fill(HIST("hpuxvscentpteta"), centrality, track.pt(), track.eta(), ux, wNUA); + histos.fill(HIST("hpuyvscentpteta"), centrality, track.pt(), track.eta(), uy, wNUA); + + histos.fill(HIST("hpuxyQxytvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyt, wNUA); + histos.fill(HIST("hpuxyQxypvscentpteta"), centrality, track.tpcInnerParam(), track.eta(), uxyQxyp, wNUA); + histos.fill(HIST("hpoddv1vscentpteta"), centrality, track.pt(), track.eta(), oddv1, wNUA); + histos.fill(HIST("hpevenv1vscentpteta"), centrality, track.pt(), track.eta(), evenv1, wNUA); + + histos.fill(HIST("hpQxtQxpvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQxp, wNUA); + + histos.fill(HIST("hpQytQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QytQyp, wNUA); + + histos.fill(HIST("hpQxytpvscentptetaTrack"), + centrality, track.pt(), track.eta(), Qxytp, wNUA); + + histos.fill(HIST("hpQxpQytvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxpQyt, wNUA); + + histos.fill(HIST("hpQxtQypvscentptetaTrack"), + centrality, track.pt(), track.eta(), QxtQyp, wNUA); } } } else { @@ -1275,6 +1867,9 @@ struct lambdapolsp { wgtvalue = 1.0; } + std::vector lambdaSystIDs; + std::vector antiLambdaSystIDs; + if (LambdaTag) { Lambda = Proton + AntiPion; tagb = 0; @@ -1305,7 +1900,7 @@ struct lambdapolsp { histos.fill(HIST("hLcosphiminuspsiAvseta"), LcosphiminuspsiA, v0.eta()); histos.fill(HIST("hLcosphiminuspsivseta"), Lcosphiminuspsi, v0.eta()); } - fillHistograms(taga, tagb, Lambda, Proton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mLambda(), v0.pt(), v0.eta(), acvalue, 1.0, resowgt); + fillHistograms(taga, tagb, Lambda, Proton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mLambda(), v0.pt(), v0.eta(), acvalue, 1.0, resowgt, &lambdaSystIDs, 1); } tagb = aLambdaTag; @@ -1339,15 +1934,16 @@ struct lambdapolsp { histos.fill(HIST("hALcosphiminuspsiAvseta"), ALcosphiminuspsiA, v0.eta()); histos.fill(HIST("hALcosphiminuspsivseta"), ALcosphiminuspsi, v0.eta()); } - fillHistograms(taga, tagb, AntiLambda, AntiProton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mAntiLambda(), v0.pt(), v0.eta(), acvalue, wgtvalue, resowgt); + fillHistograms(taga, tagb, AntiLambda, AntiProton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mAntiLambda(), v0.pt(), v0.eta(), acvalue, wgtvalue, resowgt, &antiLambdaSystIDs, 2); } } } + lastRunNumber = currentRunNumber; } PROCESS_SWITCH(lambdapolsp, processData, "Process data", true); // process function for derived data - mimics the functionality of the original data - void processDerivedData(soa::Join::iterator const& collision, v0Candidates const& V0s, dauTracks const&) + void processDerivedData(soa::Join::iterator const& collision, v0Candidates const& V0s, dauTracks const&) { //___________________________________________________________________________________________________ // event selection @@ -1386,9 +1982,6 @@ struct lambdapolsp { return; } // histos.fill(HIST("hCentrality3"), centrality); - if (evselGrp.additionalEvSel3 && (!collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !collision.selection_bit(aod::evsel::kNoITSROFrameBorder))) { - return; - } if (evselGrp.additionalEvSel4 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return; @@ -1509,34 +2102,73 @@ struct lambdapolsp { if (!analyzeLambda && !analyzeK0s) continue; - bool LambdaTag = isCompatible(v0, 0); - bool aLambdaTag = isCompatible(v0, 1); + // bool LambdaTag = isCompatible(v0, 0); + // bool aLambdaTag = isCompatible(v0, 1); + + bool LambdaPreSelTag = isCompatible(v0, 0, false); // for systematic + bool aLambdaPreSelTag = isCompatible(v0, 1, false); + bool LambdaTag = isCompatible(v0, 0, true); // for default + bool aLambdaTag = isCompatible(v0, 1, true); bool K0sTag = isCompatibleK0s(v0); - if (analyzeLambda && !LambdaTag && !aLambdaTag) + // if (analyzeLambda && !LambdaTag && !aLambdaTag) + // continue; + if (analyzeLambda && !LambdaPreSelTag && !aLambdaPreSelTag) { continue; + } if (analyzeK0s && !K0sTag) continue; - if (!SelectionV0(collision, v0)) { - continue; + // if (!SelectionV0(collision, v0)) { + // continue; + // } + + bool passNominalV0 = true; + if (analyzeLambda) { + // only fixed/common cuts here + if (!SelectionV0(collision, v0, false)) { + continue; + } + // remember whether nominal topology is passed + passNominalV0 = SelectionV0(collision, v0, true); + + LambdaTag = LambdaTag && passNominalV0; + aLambdaTag = aLambdaTag && passNominalV0; + + } else if (analyzeK0s) { + // K0 analysis remains exactly nominal + if (!SelectionV0(collision, v0, true)) { + continue; + } } + if (analyzeLambda) { - if (LambdaTag) { + // if (LambdaTag) { + if (LambdaPreSelTag) { Proton = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), massPr); AntiPion = ROOT::Math::PxPyPzMVector(v0.pxneg(), v0.pyneg(), v0.pzneg(), massPi); Lambdadummy = Proton + AntiPion; } - if (aLambdaTag) { + // if (aLambdaTag) { + if (aLambdaPreSelTag) { AntiProton = ROOT::Math::PxPyPzMVector(v0.pxneg(), v0.pyneg(), v0.pzneg(), massPr); Pion = ROOT::Math::PxPyPzMVector(v0.pxpos(), v0.pypos(), v0.pzpos(), massPi); AntiLambdadummy = AntiProton + Pion; } - if (shouldReject(LambdaTag, aLambdaTag, Lambdadummy, AntiLambdadummy)) { - continue; + // if (shouldReject(LambdaTag, aLambdaTag, Lambdadummy, AntiLambdadummy)) { + // continue; + // } + + if (shouldReject(LambdaTag, + aLambdaTag, + Lambdadummy, + AntiLambdadummy)) { + + LambdaTag = false; + aLambdaTag = false; } } @@ -1548,9 +2180,45 @@ struct lambdapolsp { } } - if (TMath::Abs(v0.eta()) > 0.8) + if (TMath::Abs(v0.eta()) >= 0.8) continue; + std::vector K0sSystIDs; + std::vector lambdaSystIDs; + std::vector antiLambdaSystIDs; + + if (systGrp.doTopoSyst && analyzeLambda) { + + lambdaSystIDs.reserve(systGrp.nTopoSyst.value); + antiLambdaSystIDs.reserve(systGrp.nTopoSyst.value); + + for (int isyst = 0; isyst < systGrp.nTopoSyst.value; ++isyst) { + + const auto& cuts = topoSystCuts[isyst]; + bool passLambda = LambdaPreSelTag && passesTopoSystematic(collision, v0, 0, cuts); + bool passAntiLambda = aLambdaPreSelTag && passesTopoSystematic(collision, v0, 1, cuts); + + // Apply the same Lambda/AntiLambda ambiguity logic + // separately for THIS systematic variation. + if (shouldReject(passLambda, + passAntiLambda, + Lambdadummy, + AntiLambdadummy)) { + + passLambda = false; + passAntiLambda = false; + } + + if (passLambda) { + lambdaSystIDs.push_back(isyst); + } + + if (passAntiLambda) { + antiLambdaSystIDs.push_back(isyst); + } + } + } + int taga = LambdaTag; int tagb = aLambdaTag; int tagc = K0sTag; @@ -1558,7 +2226,7 @@ struct lambdapolsp { if (analyzeK0s && K0sTag) { K0s = Pion + AntiPion; double acvalue = 1.0; - fillHistograms(tagc, 0, K0s, Pion, psiZDCC, psiZDCA, psiZDC, centrality, v0.mK0Short(), v0.pt(), v0.eta(), acvalue, 1.0, resowgt); + fillHistograms(tagc, 0, K0s, Pion, psiZDCC, psiZDCA, psiZDC, centrality, v0.mK0Short(), v0.pt(), v0.eta(), acvalue, 1.0, resowgt, &K0sSystIDs, 1); } int binxwgt; @@ -1582,7 +2250,8 @@ struct lambdapolsp { effwgtvalueL = 1.0; } - if (analyzeLambda && LambdaTag) { + // if (analyzeLambda && LambdaTag) { + if (analyzeLambda && LambdaPreSelTag && (LambdaTag || !lambdaSystIDs.empty())) { Lambda = Proton + AntiPion; tagb = 0; double acvalue = 1.0; @@ -1604,11 +2273,12 @@ struct lambdapolsp { histos.fill(HIST("hLcosphiminuspsivseta"), Lcosphiminuspsi, v0.eta()); } - fillHistograms(taga, tagb, Lambda, Proton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mLambda(), v0.pt(), v0.eta(), acvalue, (1. / effwgtvalueL), resowgt); + fillHistograms(taga, tagb, Lambda, Proton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mLambda(), v0.pt(), v0.eta(), acvalue, (1. / effwgtvalueL), resowgt, &lambdaSystIDs, 1); } tagb = aLambdaTag; - if (analyzeLambda && aLambdaTag) { + // if (analyzeLambda && aLambdaTag) { + if (analyzeLambda && aLambdaPreSelTag && (aLambdaTag || !antiLambdaSystIDs.empty())) { AntiLambda = AntiProton + Pion; taga = 0; double acvalue = 1.0; @@ -1630,7 +2300,7 @@ struct lambdapolsp { histos.fill(HIST("hALcosphiminuspsivseta"), ALcosphiminuspsi, v0.eta()); } - fillHistograms(taga, tagb, AntiLambda, AntiProton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mAntiLambda(), v0.pt(), v0.eta(), acvalue, wgtvalue * (1. / effwgtvalueAL), resowgt); + fillHistograms(taga, tagb, AntiLambda, AntiProton, psiZDCC, psiZDCA, psiZDC, centrality, v0.mAntiLambda(), v0.pt(), v0.eta(), acvalue, wgtvalue * (1. / effwgtvalueAL), resowgt, &antiLambdaSystIDs, 2); } } // lastRunNumber = currentRunNumber; @@ -1775,14 +2445,14 @@ struct lambdapolsp { // Processing Event Mixing /* using BinningType = ColumnBinningPolicy; - BinningType colBinning{{meGrp.axisVertex, meGrp.axisMultiplicityClass}, true}; + BinningType colBinning{{meGrp.axisVertex, meGrp.axisMultiplicityClass}}; Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only - void processDerivedDataMixed(soa::Join const& collisions, v0Candidates const& V0s, dauTracks const&) + void processDerivedDataMixed(soa::Join const& collisions, v0Candidates const& V0s, dauTracks const&) { TRandom3 randGen(0); - for (auto& [collision1, collision2] : selfCombinations(colBinning, meGrp.nMix, -1, collisions, collisions)) { + for (const auto& [collision1, collision2] : selfCombinations(colBinning, meGrp.nMix, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) { continue; @@ -1913,11 +2583,11 @@ struct lambdapolsp { PROCESS_SWITCH(lambdapolsp, processDerivedDataMixed, "Process mixed event using derived data", false); - void processDerivedDataMixed2(soa::Join const& collisions, v0Candidates const& V0s, dauTracks const&) + void processDerivedDataMixed2(soa::Join const& collisions, v0Candidates const& V0s, dauTracks const&) { TRandom3 randGen(0); - for (auto& [collision1, collision2] : selfCombinations(colBinning, meGrp.nMix, -1, collisions, collisions)) { + for (const auto& [collision1, collision2] : selfCombinations(colBinning, meGrp.nMix, -1, collisions, collisions)) { if (collision1.index() == collision2.index()) { continue; diff --git a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx index 5c11ad662de..1a3b71e02f8 100644 --- a/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx +++ b/PWGLF/Tasks/Strangeness/lambdaspincorrderived.cxx @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -42,13 +43,12 @@ #include #include +#include #include #include // for std::abs #include #include #include -#include -#include #include #include #include @@ -59,6 +59,121 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::soa; +// Compute the competing mass under the K0s (pi+, pi-) hypothesis. +template +static double getK0sCompetingMass(T const& v0) +{ + const ROOT::Math::PtEtaPhiMVector lambda( + v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), v0.lambdaMass()); + + const ROOT::Math::PtEtaPhiMVector posDaugh( + v0.protonPt(), v0.protonEta(), v0.protonPhi(), + o2::constants::physics::MassProton); + + const auto negDaugh = lambda - posDaugh; + + const ROOT::Math::PtEtaPhiMVector pi1( + posDaugh.Pt(), posDaugh.Eta(), posDaugh.Phi(), + o2::constants::physics::MassPionCharged); + + const ROOT::Math::PtEtaPhiMVector pi2( + negDaugh.Pt(), negDaugh.Eta(), negDaugh.Phi(), + o2::constants::physics::MassPionCharged); + + return (pi1 + pi2).M(); +} + +template +static std::vector getCleanCandidateIndices( + const Container& candidates, + SelectionFunc&& passSelection) +{ + std::vector selected; + selected.reserve(candidates.size()); + + for (auto const& c : candidates) { + if (passSelection(c)) { + selected.push_back(c.globalIndex()); + } + } + + std::vector dirty(selected.size(), false); + for (size_t i = 0; i < selected.size(); ++i) { + for (size_t j = i + 1; j < selected.size(); ++j) { + auto a = candidates.iteratorAt(selected[i]); + auto b = candidates.iteratorAt(selected[j]); + if (hasSharedDaughters(a, b)) { + dirty[i] = true; + dirty[j] = true; + } + } + } + + std::vector clean; + clean.reserve(selected.size()); + for (size_t i = 0; i < selected.size(); ++i) { + if (!dirty[i]) { + clean.push_back(selected[i]); + } + } + return clean; +} + +static inline double getKStar( + const ROOT::Math::PtEtaPhiMVector& p1, + const ROOT::Math::PtEtaPhiMVector& p2) +{ + const auto pair = p1 + p2; + + ROOT::Math::Boost boostToPairCM(pair.BoostToCM()); + const auto p1CM = boostToPairCM(p1); + + return std::sqrt( + p1CM.Px() * p1CM.Px() + + p1CM.Py() * p1CM.Py() + + p1CM.Pz() * p1CM.Pz()); +} + +struct PionKin { + double pt, eta, phi; +}; + +static inline PionKin getPionKinematics( + double lambdaPt, double lambdaEta, double lambdaPhi, + double protonPt, double protonEta, double protonPhi) +{ + // const double mP = o2::constants::physics::MassProton; + + // Λ momentum energy + const double pxL = lambdaPt * std::cos(lambdaPhi); + const double pyL = lambdaPt * std::sin(lambdaPhi); + const double pzL = lambdaPt * std::sinh(lambdaEta); + // const double EL = std::sqrt(pxL*pxL + pyL*pyL + pzL*pzL + lambdaMass*lambdaMass); + + // proton momentum and energy + const double pxP = protonPt * std::cos(protonPhi); + const double pyP = protonPt * std::sin(protonPhi); + const double pzP = protonPt * std::sinh(protonEta); + // const double EP = std::sqrt(pxP*pxP + pyP*pyP + pzP*pzP + mP*mP); + + // π momentum and energy + const double pxPi = pxL - pxP; + const double pyPi = pyL - pyP; + const double pzPi = pzL - pzP; + + PionKin out; + out.pt = std::sqrt(pxPi * pxPi + pyPi * pyPi); + + if (out.pt < 1e-6) { + out.eta = 0.0; + out.phi = 0.0; + return out; + } + out.eta = std::asinh(pzPi / out.pt); + out.phi = std::atan2(pyPi, pxPi); + return out; +} + namespace mcacc { // event @@ -170,17 +285,42 @@ static inline int piIdx(const T& t) { return t.pionIndexmc(); } + +template +static inline float dcaV0ToPVMC(const T& t) +{ + return t.dcaV0ToPVmc(); +} + } // namespace mcacc +// Optional fixed-leg correction pointers kept outside the task struct. +static TH3D* gFixedLLRep1 = nullptr; +static TH3D* gFixedULRep1 = nullptr; +static TH3D* gFixedALALRep1 = nullptr; + +static TH3D* gFixedLLRep2 = nullptr; +static TH3D* gFixedULRep2 = nullptr; +static TH3D* gFixedALALRep2 = nullptr; + struct lambdaspincorrderived { // BinningType colBinning; struct : ConfigurableGroup { Configurable cfgURL{"cfgURL", "http://alice-ccdb.cern.ch", "Address of the CCDB to browse"}; Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "Latest acceptable timestamp of creation for the object"}; + Configurable useFixedWeight{"useFixedWeight", false, "Use additional fixed-leg correction with useweight"}; + Configurable> ConfWeightPaths{ + "ConfWeightPaths", + std::vector{}, + "Replaced-leg CCDB paths in order: REP_LL_leg1,REP_UL_leg1,REP_ALAL_leg1,REP_LL_leg2,REP_UL_leg2,REP_ALAL_leg2"}; + Configurable> ConfFixedWeightPaths{ + "ConfFixedWeightPaths", + std::vector{}, + "Fixed-leg CCDB paths in order: FIX_LL_forRepLeg1,FIX_UL_forRepLeg1,FIX_ALAL_forRepLeg1,FIX_LL_forRepLeg2,FIX_UL_forRepLeg2,FIX_ALAL_forRepLeg2"}; } cfgCcdbParam; struct : ConfigurableGroup { - ConfigurableAxis cfgMixRadiusBins{"cfgMixRadiusBins", {VARIABLE_WIDTH, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 15.0, 20.0, 25.0, 30.0}, "Radius bins for V6 radius buffer"}; + ConfigurableAxis cfgMixRadiusBins{"cfgMixRadiusBins", {VARIABLE_WIDTH, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 10.0, 12.0, 15.0, 20.0, 25.0, 33.0}, "Radius bins for V6 radius buffer"}; } cfgMixRadiusParam; // Enable access to the CCDB for the offset and correction constants and save them in dedicated variables. @@ -200,30 +340,27 @@ struct lambdaspincorrderived { TH2D* hNUALambda = nullptr; TH2D* hNUAAntiLambda = nullptr; - Configurable ConfWeightPathLL{"ConfWeightPathLL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathALAL{"ConfWeightPathALAL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathLAL{"ConfWeightPathLAL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathALL{"ConfWeightPathALL", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path"}; - Configurable ConfWeightPathLL2{"ConfWeightPathLL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; - Configurable ConfWeightPathALAL2{"ConfWeightPathALAL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; - Configurable ConfWeightPathLAL2{"ConfWeightPathLAL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; - Configurable ConfWeightPathALL2{"ConfWeightPathALL2", "Users/s/skundu/My/Object/spincorr/cent010LL", "Weight path 2"}; Configurable useNUA{"useNUA", false, "Apply single-candidate NUA weight in (phi,eta)"}; Configurable ConfNUAPathLambda{"ConfNUAPathLambda", "", "CCDB path for Lambda NUA TH2D(phi,eta)"}; Configurable ConfNUAPathAntiLambda{"ConfNUAPathAntiLambda", "", "CCDB path for AntiLambda NUA TH2D(phi,eta)"}; + + // Mixing selection and binning Configurable> massMixEdges{"massMixEdges", {1.09f, 1.108f, 1.122f, 1.14f}, "Mass-mixing region edges: [SB low | signal | SB high]"}; Configurable cfgMixLegMode{"cfgMixLegMode", 0, "0=replace leg-1 only, 1=replace leg-2 only, 2=do both one-leg replacements"}; Configurable cfgV5MassBins{"cfgV5MassBins", 5, "Number of fixed mass bins for V5 mixing"}; Configurable cfgV5NeighborPt{"cfgV5NeighborPt", 0, "v5: neighbor bins in pT (use symmetric ±N, edge-safe)"}; Configurable cfgV5NeighborEta{"cfgV5NeighborEta", 0, "v5: neighbor bins in eta (use symmetric ±N, edge-safe)"}; Configurable cfgV5NeighborPhi{"cfgV5NeighborPhi", 0, "v5: neighbor bins in phi (use symmetric ±N, periodic wrap)"}; - Configurable usePairKineMatch{"usePairKineMatch", true, "Require pair-level matching between (A,B) and (C,B)"}; Configurable cfgV5MaxMatches{"cfgV5MaxMatches", 50, "v5: max ME replacements per SE pair (after all cuts)"}; Configurable cfgMixSeed{"cfgMixSeed", 0xdecafbadULL, "RNG seed for downsampling matches (deterministic)"}; + Configurable cfgV6CarryUnmatched{"cfgV6CarryUnmatched", false, "Carry data and MC V6 replacement branches with zero matches to later data frames"}; + Configurable cfgV6MaxPendingBranches{"cfgV6MaxPendingBranches", 0, "Maximum retained V6 branches per data or MC buffer; <=0 means unlimited"}; + Configurable cfgV6MaxPendingAge{"cfgV6MaxPendingAge", 0, "Maximum later data frames searched by a retained V6 branch; <=0 means unlimited"}; + Configurable cfgV6LogPending{"cfgV6LogPending", false, "Print one V6 pending-branch summary per data frame"}; + + // Event and candidate mixing cuts Configurable centMin{"centMin", 0, "Minimum Centrality"}; Configurable centMax{"centMax", 80, "Maximum Centrality"}; - Configurable rngSeed{"rngSeed", 12345, "Seed for random mixing (reproducible)"}; - std::mt19937 rng{12345}; Configurable nEvtMixing{"nEvtMixing", 10, "Number of events to mix"}; ConfigurableAxis CfgVtxBins{"CfgVtxBins", {VARIABLE_WIDTH, -10, -8, -6, -4, -2, 0, 2, 4, 6, 8, 10}, "Mixing bins - z-vertex"}; ConfigurableAxis CfgMultBins{"CfgMultBins", {VARIABLE_WIDTH, 0, 110}, "Mixing bins - centrality"}; @@ -238,22 +375,57 @@ struct lambdaspincorrderived { Configurable harmonicDphi{"harmonicDphi", 2, "Harmonic delta phi"}; Configurable useweight{"useweight", 0, "Use weight"}; Configurable usePDGM{"usePDGM", 1, "Use PDG mass"}; - Configurable useAdditionalHisto{"useAdditionalHisto", 0, "Use additional histogram"}; - Configurable checkDoubleStatus{"checkDoubleStatus", 0, "Check Double status"}; - Configurable cosPA{"cosPA", 0.995, "Cosine Pointing Angle"}; - Configurable radiusMin{"radiusMin", 3, "Minimum V0 radius"}; - Configurable radiusMax{"radiusMax", 30, "Maximum V0 radius"}; - Configurable dcaProton{"dcaProton", 0.1, "DCA Proton"}; - Configurable dcaPion{"dcaPion", 0.2, "DCA Pion"}; - Configurable dcaDaughters{"dcaDaughters", 1.0, "DCA between daughters"}; + Configurable useAdditionalHisto{"useAdditionalHisto", 0, "Backward-compatible switch for extra Rap/Phi/PairMass THnSparse"}; + + // Output-control switches. Keep the final mass-mass-costheta sparse on by default, + // but allow heavy QA/additional sparses to be disabled in production. + + struct : ConfigurableGroup { + Configurable fillBasicQAHistos{"fillBasicQAHistos", true, "Fill light QA histograms: pT-y, phi-eta, centrality, dphi, pt/eta vs cent"}; + Configurable fillReplacementQAHistos{"fillReplacementQAHistos", true, "Fill raw TGT/REP single-candidate replacement QA maps"}; + Configurable fillFixedLegQAHistos{"fillFixedLegQAHistos", false, "Fill fixed-leg TGT/SUC QA maps"}; + Configurable fillWeightQAHistos{"fillWeightQAHistos", true, "Fill weighted/final-weighted REP/FIX QA maps"}; + Configurable fillAnalysisSparses{"fillAnalysisSparses", false, "Fill extra deltaR/deltaRap/deltaPhi Analysis THnSparse objects"}; + Configurable fillAdditionalSparses{"fillAdditionalSparses", false, "Fill extra rapidity/dphi/pair-mass THnSparse objects"}; + Configurable checkDoubleStatus{"checkDoubleStatus", 0, "Check Double status"}; + + Configurable fillkStarSparses{"fillkStarSparses", true, "Fill extra deltaR/deltaRap/deltaPhi kStar THnSparse objects"}; + + } fillHistConfig; + Configurable ptMin{"ptMin", 0.5, "V0 Pt minimum"}; Configurable ptMax{"ptMax", 3.0, "V0 Pt maximum"}; Configurable MassMin{"MassMin", 1.09, "V0 Mass minimum"}; Configurable MassMax{"MassMax", 1.14, "V0 Mass maximum"}; + Configurable rapidity{"rapidity", 0.5, "Rapidity cut on lambda"}; - Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.8, "Eta cut on mix event buffer"}; Configurable v0eta{"v0eta", 0.8, "Eta cut on lambda"}; + struct : ConfigurableGroup { + std::string prefix = "v0Configuration"; + Configurable cosPA{"cosPA", 0.995, "Cosine Pointing Angle"}; + Configurable radiusMin{"radiusMin", 3, "Minimum V0 radius"}; + Configurable radiusMax{"radiusMax", 30, "Maximum V0 radius"}; + Configurable dcaProton{"dcaProton", 0.1, "DCA Proton"}; + Configurable dcaPion{"dcaPion", 0.2, "DCA Pion"}; + Configurable dcaDaughters{"dcaDaughters", 1.0, "DCA between daughters"}; + Configurable dcaV0ToPV{"dcaV0ToPV", 1.2, "DCA V0 to PV cut on lambda"}; + Configurable v0etaMixBuffer{"v0etaMixBuffer", 0.5, "Eta cut on mix event buffer"}; + + Configurable useK0sRejection{"useK0sRejection", true, "Apply K0s competing mass rejection"}; + Configurable confK0sMassWindow{"confK0sMassWindow", 0.01f, "K0s competing mass rejection window (GeV/c^2)"}; + + Configurable useAdditionalCut{"useAdditionalCut", false, "Apply additional cut"}; + + // tpcnclustercorssedrows () + Configurable minTpcNClsCrossedRows{"minTpcNClsCrossedRows", 70.f, "Minimum TPC crossed rows for daughter"}; + // ctau (lifetime) + Configurable maxCtau{"maxCtau", 30.f, "Maximum ctau of Lambda candidate (cm)"}; + // TPC nsigma + Configurable maxTpcNsigma{"maxTpcNsigma", 4.f, "Maximum |TPC nsigma| for Lambda daughters"}; + + } v0Configurations; + // Event Mixing Configurable cosDef{"cosDef", 1, "Defination of cos"}; @@ -264,6 +436,8 @@ struct lambdaspincorrderived { // THnsparse bining ConfigurableAxis configThnAxisInvMass{"configThnAxisInvMass", {50, 1.09, 1.14}, "#it{M} (GeV/#it{c}^{2})"}; ConfigurableAxis configThnAxisR{"configThnAxisR", {VARIABLE_WIDTH, 0.0, 8.0}, "#it{R}"}; + ConfigurableAxis configThnAxisPt{"configThnAxisPt", {40, 0.5, 4.5}, "#it{R}"}; + ConfigurableAxis configThnAxisRap{"configThnAxisRap", {16, -0.8, 0.8}, "#it{R}"}; ConfigurableAxis configThnAxisPol{"configThnAxisPol", {VARIABLE_WIDTH, 0.0, 8.0}, "cos#it{#theta *}"}; ConfigurableAxis configThnAxisCentrality{"configThnAxisCentrality", {VARIABLE_WIDTH, 0.0, 80.0}, "Centrality"}; ConfigurableAxis configThnAxisRapidity{"configThnAxisRapidity", {VARIABLE_WIDTH, 0.0, 1.0}, "Rapidity"}; @@ -272,22 +446,240 @@ struct lambdaspincorrderived { ConfigurableAxis configThnAxisDeltaPhi{"configThnAxisDeltaPhi", {VARIABLE_WIDTH, 0.0, TMath::Pi() / 6, 2.0 * TMath::Pi() / 6, 3.0 * TMath::Pi() / 6, 4.0 * TMath::Pi() / 6, 5.0 * TMath::Pi() / 6, TMath::Pi()}, "Delta Phi"}; ConfigurableAxis configThnAxisDeltaR{"configThnAxisDeltaR", {VARIABLE_WIDTH, 0.0, 0.5, 1.2, 2.0, 3.1, 4.0}, "Delta R"}; + ConfigurableAxis configThnAxisDeltaRnew{"configThnAxisDeltaRnew", {VARIABLE_WIDTH, 0.0, 0.4, 0.8, 1.2, 1.8, 2.4, 3.1}, "Delta R"}; ConfigurableAxis configThnAxisDeltaRap{"configThnAxisDeltaRap", {VARIABLE_WIDTH, 0.0, 0.2, 0.5, 1.0, 1.6}, "Delta Rap"}; + ConfigurableAxis configThnAxisKStar{"configThnAxisKStar", {VARIABLE_WIDTH, 0.0, 0.1, 0.2, 1.0}, "#it{k}^{*} (GeV/#it{c})"}; + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // ------------------------- + // Systematics control + // ------------------------- + + struct SysCuts { + // Topology cut + float maxDcaDaughters; + float minRadius; + float maxRadius; + float mincosPA; + float mindcaProton; + float mindcaPion; + float maxdcaV0ToPV; + }; + + std::vector sysCuts; // sysCuts[0] = default; sysCuts[1..] random unique + int nSysTotal = 1; + + void buildSystematicCuts() + { + + // 2/3 options per cut: index 0 = DEFAULT, index 1/2 = variations + // Fill these with the exact values you want (I used your def as index 0 + your old options as 1/2) + const std::array optDcaDaughter{1.00f, 0.90f}; + const std::array optminRadius{1.20f, 1.00f}; + const std::array optmaxRadius{23.0f, 35.0f}; + const std::array optcosPA{0.999f, 0.995f}; + + const std::array optdcaProton{0.07f, 0.05f, 0.08f}; + const std::array optdcaPion{0.2f, 0.1f, 0.3f}; + const std::array optdcaV0ToPV{1.2f, 1.0f, 1.4f}; + + // Helper: build SysCuts from chosen indices (0..2) + auto buildFromIdx = [&](int i0, int i1, int i2, int i3, + int i4, int i5, int i6) -> SysCuts { + SysCuts c{}; + c.maxDcaDaughters = optDcaDaughter[i0]; + c.minRadius = optminRadius[i1]; + c.maxRadius = optmaxRadius[i2]; + c.mincosPA = optcosPA[i3]; + + c.mindcaProton = optdcaProton[i4]; + c.mindcaPion = optdcaPion[i5]; + c.maxdcaV0ToPV = optdcaV0ToPV[i6]; + return c; + }; + sysCuts.clear(); + // sysId=0 must be strict default (all indices = 0) + SysCuts def = buildFromIdx(0, 0, 0, 0, 0, 0, 0); + sysCuts.push_back(def); + // sysId = 1: + sysCuts.push_back(buildFromIdx(1, 0, 0, 0, 0, 0, 0)); + // sysId = 2: + sysCuts.push_back(buildFromIdx(0, 1, 0, 0, 0, 0, 0)); + // sysId = 3 + sysCuts.push_back(buildFromIdx(0, 0, 1, 0, 0, 0, 0)); + // sysId = 4 + sysCuts.push_back(buildFromIdx(0, 0, 0, 1, 0, 0, 0)); + + // sysId = 5 + sysCuts.push_back(buildFromIdx(0, 0, 0, 0, 1, 0, 0)); + // sysId = 6 + sysCuts.push_back(buildFromIdx(0, 0, 0, 0, 2, 0, 0)); + // sysId = 7 + sysCuts.push_back(buildFromIdx(0, 0, 0, 0, 0, 1, 0)); + // sysId = 8 + sysCuts.push_back(buildFromIdx(0, 0, 0, 0, 0, 2, 0)); + // sysId = 9 + sysCuts.push_back(buildFromIdx(0, 0, 0, 0, 0, 0, 1)); + // sysId = 10 + sysCuts.push_back(buildFromIdx(0, 0, 0, 0, 0, 0, 2)); + + nSysTotal = (int)sysCuts.size(); + + // print all configurations + LOGF(info, "========================================"); + LOGF(info, "Total systematic configurations: %d", nSysTotal); + LOGF(info, "sysId=0: DEFAULT (all cuts at default)"); + LOGF(info, "sysId=1: maxDcaDaughters %.2f -> %.2f", + optDcaDaughter[0], optDcaDaughter[1]); + LOGF(info, "sysId=2: minRadius %.2f -> %.2f", + optminRadius[0], optminRadius[1]); + LOGF(info, "sysId=3: maxRadius %.2f -> %.2f", + optmaxRadius[0], optmaxRadius[1]); + LOGF(info, "sysId=4: mincosPA %.3f -> %.3f", + optcosPA[0], optcosPA[1]); + LOGF(info, "sysId=5: mindcaProton %.2f -> %.2f (tighter)", + optdcaProton[0], optdcaProton[1]); + LOGF(info, "sysId=6: mindcaProton %.2f -> %.2f (looser)", + optdcaProton[0], optdcaProton[2]); + LOGF(info, "sysId=7: mindcaPion %.2f -> %.2f (tighter)", + optdcaPion[0], optdcaPion[1]); + LOGF(info, "sysId=8: mindcaPion %.2f -> %.2f (looser)", + optdcaPion[0], optdcaPion[2]); + LOGF(info, "sysId=9: maxdcaV0ToPV %.2f -> %.2f (tighter)", + optdcaV0ToPV[0], optdcaV0ToPV[1]); + LOGF(info, "sysId=10: maxdcaV0ToPV %.2f -> %.2f (looser)", + optdcaV0ToPV[0], optdcaV0ToPV[2]); + LOGF(info, "========================================"); + } + void init(o2::framework::InitContext&) { - histos.add("hPtRadiusV0", "V0 QA;#it{p}_{T}^{V0} (GeV/#it{c});V0 decay radius (cm)", kTH2F, {{100, 0.0, 10.0}, {120, 0.0, 45.0}}); - histos.add("hPtYSame", "hPtYSame", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); - histos.add("hPtYMix", "hPtYMix", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); - histos.add("hPhiEtaSame", "hPhiEtaSame", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); - histos.add("hPhiEtaMix", "hPhiEtaMix", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); - histos.add("hCentrality", "Centrality distribution", kTH1F, {{configThnAxisCentrality}}); - histos.add("deltaPhiSame", "deltaPhiSame", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); - histos.add("deltaPhiMix", "deltaPhiMix", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); - histos.add("ptCent", "ptCent", HistType::kTH2D, {{100, 0.0, 10.0}, {8, 0.0, 80.0}}, true); - histos.add("etaCent", "etaCent", HistType::kTH2D, {{32, -0.8, 0.8}, {8, 0.0, 80.0}}, true); + + // Laura: Δη–Δφ for same-charge ΛΛ in the first ΔR bin + const int nBinsDEta = 80; + const double dEtaMin = -0.15; + const double dEtaMax = 0.15; + + const int nBinsDPhi = 80; + const double dPhiMin = -0.15; + const double dPhiMax = 0.15; + + histos.add("hDEtaDPhiKStar_pp_SE", + "p-p #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + + histos.add("hDEtaDPhiKStar_pipi_SE", + "#pi-#pi #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + + histos.add("hDEtaDPhiKStar_ppi_SE", + "p-#pi #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + + histos.add("hDEtaDPhiKStar_pip_SE", + "#pi-p #Delta#eta vs #Delta#phi vs #DeltaR vs #it{k}^{*} (same-charge #Lambda#Lambda);#Delta#eta;#Delta#phi;#DeltaR;#it{k}^{*}", + HistType::kTHnSparseF, + {{nBinsDEta, dEtaMin, dEtaMax}, + {nBinsDPhi, dPhiMin, dPhiMax}, + {configThnAxisDeltaRnew}, + {configThnAxisKStar}}); + + buildSystematicCuts(); + + nSysTotal = (int)sysCuts.size(); // or whatever vector you fill + LOGF(info, "sysCuts.size()=%zu nSysTotal=%d", sysCuts.size(), nSysTotal); + const AxisSpec thnAxisSys{nSysTotal, -0.5f, float(nSysTotal) - 0.5f, "sysId"}; + + if (fillHistConfig.fillBasicQAHistos) { + histos.add("hPtRadiusV0", "V0 QA;#it{p}_{T}^{V0} (GeV/#it{c});V0 decay radius (cm)", kTH2F, {{100, 0.0, 10.0}, {120, 0.0, 45.0}}); + histos.add("hPtYSame", "hPtYSame", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); + histos.add("hPtYMix", "hPtYMix", kTH2F, {{100, 0.0, 10.0}, {200, -1.0, 1.0}}); + histos.add("hPhiEtaSame", "hPhiEtaSame", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); + histos.add("hPhiEtaMix", "hPhiEtaMix", kTH2F, {{720, 0.0, 2.0 * TMath::Pi()}, {200, -1.0, 1.0}}); + histos.add("hCentrality", "Centrality distribution", kTH1F, {{configThnAxisCentrality}}); + histos.add("deltaPhiSame", "deltaPhiSame", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); + histos.add("deltaPhiMix", "deltaPhiMix", HistType::kTH1D, {{72, -TMath::Pi(), TMath::Pi()}}, true); + histos.add("ptCent", "ptCent", HistType::kTH2D, {{100, 0.0, 10.0}, {8, 0.0, 80.0}}, true); + histos.add("etaCent", "etaCent", HistType::kTH2D, {{32, -0.8, 0.8}, {8, 0.0, 80.0}}, true); + histos.add("hNMEperSE_leg1", "ME replacements per SE pair, leg1", HistType::kTH1D, {{12, -0.5, 11.5}}, true); + histos.add("hNMEperSE_leg2", "ME replacements per SE pair, leg2", HistType::kTH1D, {{12, -0.5, 11.5}}, true); + } + + if (fillHistConfig.fillWeightQAHistos) { + // QA for weighting + histos.add("REP_LL_leg1_weighted", + "Weighted repl LL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LAL_leg1_weighted", + "Weighted repl LAL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALL_leg1_weighted", + "Weighted repl ALL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALAL_leg1_weighted", + "Weighted repl ALAL leg1;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg2_weighted", + "Weighted repl LL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LAL_leg2_weighted", + "Weighted repl LAL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALL_leg2_weighted", + "Weighted repl ALL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_ALAL_leg2_weighted", + "Weighted repl ALAL leg2;#phi;y/#eta;p_{T}", + HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } + if (fillHistConfig.fillFixedLegQAHistos) { + // Fixed-leg QA for replacement-leg bias check. + // For repLeg1: leg1 is replaced, fixed leg is original leg2. + // For repLeg2: leg2 is replaced, fixed leg is original leg1. + + // Target fixed-leg maps: all requested SE pairs + histos.add("TGT_FIX_LL_forRepLeg1", "Target fixed LL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_LAL_forRepLeg1", "Target fixed LAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALL_forRepLeg1", "Target fixed ALL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALAL_forRepLeg1", "Target fixed ALAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("TGT_FIX_LL_forRepLeg2", "Target fixed LL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_LAL_forRepLeg2", "Target fixed LAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALL_forRepLeg2", "Target fixed ALL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_FIX_ALAL_forRepLeg2", "Target fixed ALAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + // Successful fixed-leg maps: only pairs where replacement was found + histos.add("SUC_FIX_LL_forRepLeg1", "Successful fixed LL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_LAL_forRepLeg1", "Successful fixed LAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALL_forRepLeg1", "Successful fixed ALL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg1", "Successful fixed ALAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("SUC_FIX_LL_forRepLeg2", "Successful fixed LL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_LAL_forRepLeg2", "Successful fixed LAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALL_forRepLeg2", "Successful fixed ALL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg2", "Successful fixed ALAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } histos.add("hEtaPhiLambdaRaw", "Lambda raw;#phi;#eta", kTH2D, {{360, 0.0, 2.0 * TMath::Pi()}, {32, -0.8, 0.8}}); @@ -297,27 +689,48 @@ struct lambdaspincorrderived { histos.add("hNUAWeightLambda", "Lambda NUA weight", kTH1D, {{200, 0.0, 5.0}}); histos.add("hNUAWeightAntiLambda", "AntiLambda NUA weight", kTH1D, {{200, 0.0, 5.0}}); - // --- target/replacement single-leg occupancy maps for replacement correction - histos.add("TGT_LL_leg1", "Target LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_LAL_leg1", "Target LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALL_leg1", "Target ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALAL_leg1", "Target ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - - histos.add("REP_LL_leg1", "Repl LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_LAL_leg1", "Repl LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALL_leg1", "Repl ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALAL_leg1", "Repl ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - - histos.add("TGT_LL_leg2", "Target LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_LAL_leg2", "Target LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALL_leg2", "Target ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("TGT_ALAL_leg2", "Target ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - - histos.add("REP_LL_leg2", "Repl LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_LAL_leg2", "Repl LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALL_leg2", "Repl ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - histos.add("REP_ALAL_leg2", "Repl ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); - + if (fillHistConfig.fillReplacementQAHistos) { + // --- target/replacement single-leg occupancy maps for replacement correction + histos.add("TGT_LL_leg1", "Target LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_LAL_leg1", "Target LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALL_leg1", "Target ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALAL_leg1", "Target ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg1", "Repl LL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_LAL_leg1", "Repl LAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALL_leg1", "Repl ALL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg1", "Repl ALAL leg1", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("TGT_LL_leg2", "Target LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_LAL_leg2", "Target LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALL_leg2", "Target ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("TGT_ALAL_leg2", "Target ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg2", "Repl LL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_LAL_leg2", "Repl LAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALL_leg2", "Repl ALL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg2", "Repl ALAL leg2", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } + if (fillHistConfig.fillWeightQAHistos) { + // Final-weighted QA: filled with the exact same mixing correction weight + // used for the mixed-event sparse, excluding NUA. + // Correction categories are LL, UL=(LAL+ALL), ALAL. + histos.add("REP_LL_leg1_finalWeighted", "Final weighted REP LL leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_UL_leg1_finalWeighted", "Final weighted REP UL leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg1_finalWeighted", "Final weighted REP ALAL leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("REP_LL_leg2_finalWeighted", "Final weighted REP LL leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_UL_leg2_finalWeighted", "Final weighted REP UL leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("REP_ALAL_leg2_finalWeighted", "Final weighted REP ALAL leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("SUC_FIX_LL_forRepLeg1_finalWeighted", "Final weighted fixed LL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_UL_forRepLeg1_finalWeighted", "Final weighted fixed UL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg1_finalWeighted", "Final weighted fixed ALAL for rep leg1;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + + histos.add("SUC_FIX_LL_forRepLeg2_finalWeighted", "Final weighted fixed LL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_UL_forRepLeg2_finalWeighted", "Final weighted fixed UL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + histos.add("SUC_FIX_ALAL_forRepLeg2_finalWeighted", "Final weighted fixed ALAL for rep leg2;#phi;y/#eta;p_{T}", HistType::kTH3D, {ax_dphi_h, ax_deta, ax_ptpair}, true); + } histos.add("hSparseLambdaLambda", "hSparseLambdaLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseLambdaAntiLambda", "hSparseLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaLambda", "hSparseAntiLambdLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); @@ -328,17 +741,43 @@ struct lambdaspincorrderived { histos.add("hSparseAntiLambdaLambdaMixed", "hSparseAntiLambdaLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); histos.add("hSparseAntiLambdaAntiLambdaMixed", "hSparseAntiLambdaAntiLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR}, true); - histos.add("hSparseLambdaLambdaAnalysis", "hSparseLambdaLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseLambdaAntiLambdaAnalysis", "hSparseLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaLambdaAnalysis", "hSparseAntiLambdLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaAntiLambdaAnalysis", "hSparseAntiLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + // Systematic THnSparse for analysis + histos.add("hSparseLambdaLambdaSys", "hSparseLambdaLambdaSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + histos.add("hSparseLambdaAntiLambdaSys", "hSparseLambdaAntiLambdaSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + histos.add("hSparseAntiLambdaLambdaSys", "hSparseAntiLambdLambdaSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + histos.add("hSparseAntiLambdaAntiLambdaSys", "hSparseAntiLambdaAntiLambdaSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + + histos.add("hSparseLambdaLambdaMixedSys", "hSparseLambdaLambdaMixedSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + histos.add("hSparseLambdaAntiLambdaMixedSys", "hSparseLambdaAntiLambdaMixedSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + histos.add("hSparseAntiLambdaLambdaMixedSys", "hSparseAntiLambdaLambdaMixedSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + histos.add("hSparseAntiLambdaAntiLambdaMixedSys", "hSparseAntiLambdaAntiLambdaMixedSys", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, thnAxisSys}, true); + + if (fillHistConfig.fillkStarSparses) { - histos.add("hSparseLambdaLambdaMixedAnalysis", "hSparseLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseLambdaAntiLambdaMixedAnalysis", "hSparseLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaLambdaMixedAnalysis", "hSparseAntiLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); - histos.add("hSparseAntiLambdaAntiLambdaMixedAnalysis", "hSparseAntiLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseLambdaLambda_kStar", "hSparseLambdaLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseLambdaAntiLambda_kStar", "hSparseLambdaAntiLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaLambda_kStar", "hSparseAntiLambdaLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaAntiLambda_kStar", "hSparseAntiLambdaAntiLambda_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); - if (useAdditionalHisto) { + histos.add("hSparseLambdaLambdaMixed_kStar", "hSparseLambdaLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseLambdaAntiLambdaMixed_kStar", "hSparseLambdaAntiLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaLambdaMixed_kStar", "hSparseAntiLambdaLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + histos.add("hSparseAntiLambdaAntiLambdaMixed_kStar", "hSparseAntiLambdaAntiLambdaMixed_kStar", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisR, configThnAxisKStar}, true); + } + + if (fillHistConfig.fillAnalysisSparses) { + histos.add("hSparseLambdaLambdaAnalysis", "hSparseLambdaLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseLambdaAntiLambdaAnalysis", "hSparseLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaLambdaAnalysis", "hSparseAntiLambdLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaAntiLambdaAnalysis", "hSparseAntiLambdaAntiLambdaAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + + histos.add("hSparseLambdaLambdaMixedAnalysis", "hSparseLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseLambdaAntiLambdaMixedAnalysis", "hSparseLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaLambdaMixedAnalysis", "hSparseAntiLambdaLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + histos.add("hSparseAntiLambdaAntiLambdaMixedAnalysis", "hSparseAntiLambdaAntiLambdaMixedAnalysis", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisDeltaR, configThnAxisDeltaRap, configThnAxisDeltaPhi}, true); + } + + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.add("hSparseRapLambdaLambda", "hSparseRapLambdaLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); histos.add("hSparseRapLambdaAntiLambda", "hSparseRapLambdaAntiLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); histos.add("hSparseRapAntiLambdaLambda", "hSparseRapAntiLambdLambda", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisRapidity}, true); @@ -369,23 +808,74 @@ struct lambdaspincorrderived { histos.add("hSparsePairMassAntiLambdaLambdaMixed", "hSparsePairMassAntiLambdaLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisPairMass}, true); histos.add("hSparsePairMassAntiLambdaAntiLambdaMixed", "hSparsePairMassAntiLambdaAntiLambdaMixed", HistType::kTHnSparseF, {configThnAxisInvMass, configThnAxisInvMass, configThnAxisPol, configThnAxisPairMass}, true); } - rng.seed(static_cast(rngSeed.value)); ccdb->setURL(cfgCcdbParam.cfgURL); ccdbApi.init("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); - LOGF(info, "Getting alignment offsets from the CCDB..."); + ccdb->setCreatedNotAfter(cfgCcdbParam.nolaterthan.value); + LOGF(info, "Getting alignment offsets from the CCDB (check carefully)..."); if (useweight) { - hweight1 = ccdb->getForTimeStamp(ConfWeightPathLL.value, cfgCcdbParam.nolaterthan.value); - hweight2 = ccdb->getForTimeStamp(ConfWeightPathLAL.value, cfgCcdbParam.nolaterthan.value); - hweight3 = ccdb->getForTimeStamp(ConfWeightPathALL.value, cfgCcdbParam.nolaterthan.value); - hweight4 = ccdb->getForTimeStamp(ConfWeightPathALAL.value, cfgCcdbParam.nolaterthan.value); + const auto& repPaths = cfgCcdbParam.ConfWeightPaths.value; + if (repPaths.size() < 6) { + LOGF(fatal, "ConfWeightPaths must contain 6 paths: REP_LL_leg1, REP_UL_leg1, REP_ALAL_leg1, REP_LL_leg2, REP_UL_leg2, REP_ALAL_leg2. Found %zu", repPaths.size()); + } + for (size_t i = 0; i < repPaths.size(); ++i) { + LOGF(info, "ConfWeightPaths[%zu] = '%s'", i, repPaths[i].c_str()); + } + + auto loadRep = [&](int idx, const char* name) -> TH3D* { + if (idx < 0 || idx >= static_cast(repPaths.size()) || repPaths[idx].empty()) { + LOGF(fatal, "Missing replaced-leg path index %d for %s", idx, name); + return nullptr; + } + auto* h = ccdb->getForTimeStamp(repPaths[idx], cfgCcdbParam.nolaterthan.value); + if (!h) { + LOGF(fatal, "Could not load replaced-leg weight %s from path %s", name, repPaths[idx].c_str()); + } + return h; + }; + + hweight1 = loadRep(0, "REP_LL_leg1"); + hweight2 = loadRep(1, "REP_UL_leg1"); + hweight4 = loadRep(2, "REP_ALAL_leg1"); + hweight12 = loadRep(3, "REP_LL_leg2"); + hweight22 = loadRep(4, "REP_UL_leg2"); + hweight42 = loadRep(5, "REP_ALAL_leg2"); + + // Keep the old ALL pointers as aliases to the UL maps to avoid accidental null access. + hweight3 = hweight2; + hweight32 = hweight22; + + if (cfgCcdbParam.useFixedWeight) { + const auto& fixPaths = cfgCcdbParam.ConfFixedWeightPaths.value; + if (fixPaths.size() < 6) { + LOGF(fatal, "ConfFixedWeightPaths must contain 6 paths: FIX_LL_forRepLeg1, FIX_UL_forRepLeg1, FIX_ALAL_forRepLeg1, FIX_LL_forRepLeg2, FIX_UL_forRepLeg2, FIX_ALAL_forRepLeg2. Found %zu", fixPaths.size()); + } + for (size_t i = 0; i < fixPaths.size(); ++i) { + LOGF(info, "ConfFixedWeightPaths[%zu] = '%s'", i, fixPaths[i].c_str()); + } + + auto loadFixed = [&](int idx, const char* name) -> TH3D* { + if (idx < 0 || idx >= static_cast(fixPaths.size()) || fixPaths[idx].empty()) { + LOGF(fatal, "Missing fixed-leg path index %d for %s", idx, name); + return nullptr; + } + auto* h = ccdb->getForTimeStamp(fixPaths[idx], cfgCcdbParam.nolaterthan.value); + if (!h) { + LOGF(fatal, "Could not load fixed-leg weight %s from path %s", name, fixPaths[idx].c_str()); + } + return h; + }; - hweight12 = ccdb->getForTimeStamp(ConfWeightPathLL2.value, cfgCcdbParam.nolaterthan.value); - hweight22 = ccdb->getForTimeStamp(ConfWeightPathLAL2.value, cfgCcdbParam.nolaterthan.value); - hweight32 = ccdb->getForTimeStamp(ConfWeightPathALL2.value, cfgCcdbParam.nolaterthan.value); - hweight42 = ccdb->getForTimeStamp(ConfWeightPathALAL2.value, cfgCcdbParam.nolaterthan.value); + gFixedLLRep1 = loadFixed(0, "FIX_LL_forRepLeg1"); + gFixedULRep1 = loadFixed(1, "FIX_UL_forRepLeg1"); + gFixedALALRep1 = loadFixed(2, "FIX_ALAL_forRepLeg1"); + gFixedLLRep2 = loadFixed(3, "FIX_LL_forRepLeg2"); + gFixedULRep2 = loadFixed(4, "FIX_UL_forRepLeg2"); + gFixedALALRep2 = loadFixed(5, "FIX_ALAL_forRepLeg2"); + + LOGF(info, "Fixed-leg weights enabled. Loaded paths: %zu", fixPaths.size()); + } } if (useNUA) { hNUALambda = ccdb->getForTimeStamp(ConfNUAPathLambda.value, cfgCcdbParam.nolaterthan.value); @@ -410,25 +900,104 @@ struct lambdaspincorrderived { if (candidate.lambdaMass() < MassMin || candidate.lambdaMass() > MassMax) { return false; } - if (candidate.v0Cospa() < cosPA) { + if (candidate.v0Cospa() < v0Configurations.cosPA) { + return false; + } + if (fillHistConfig.checkDoubleStatus && candidate.doubleStatus()) { + return false; + } + if (candidate.v0Radius() > v0Configurations.radiusMax) { + return false; + } + if (candidate.v0Radius() < v0Configurations.radiusMin) { + return false; + } + if (candidate.dcaBetweenDaughter() > v0Configurations.dcaDaughters) { + return false; + } + + if (candidate.dcaV0ToPV() > v0Configurations.dcaV0ToPV) { + return false; + } + + if (candidate.v0Status() == 0 && (std::abs(candidate.dcaPositive()) < v0Configurations.dcaProton || std::abs(candidate.dcaNegative()) < v0Configurations.dcaPion)) { + return false; + } + if (candidate.v0Status() == 1 && (std::abs(candidate.dcaPositive()) < v0Configurations.dcaPion || std::abs(candidate.dcaNegative()) < v0Configurations.dcaProton)) { + return false; + } + if (candidate.lambdaPt() < ptMin) { + return false; + } + if (candidate.lambdaPt() > ptMax) { + return false; + } + + if (v0Configurations.useAdditionalCut) { + // additional cuts + if (candidate.v0Status() == 0 && (std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma)) { + return false; + } + if (candidate.v0Status() == 1 && (std::abs(candidate.tpcNsigmaNeg()) > v0Configurations.maxTpcNsigma || std::abs(candidate.tpcNsigmaPos()) > v0Configurations.maxTpcNsigma)) { + return false; + } + + if (candidate.tpcPosNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows) { + return false; + } + + if (candidate.tpcNegNClsCrossedRows() < v0Configurations.minTpcNClsCrossedRows) { + return false; + } + + if (candidate.ctau() > v0Configurations.maxCtau) { + return false; + } + + if (v0Configurations.useK0sRejection) { + if (std::abs(candidate.mK0Short() - o2::constants::physics::MassK0Short) < v0Configurations.confK0sMassWindow) { + return false; + } + } + } + + return true; + } + + template + bool selectionV0Sys(T1 const& candidate, T2 const& SysCuts) + { + auto particle = ROOT::Math::PtEtaPhiMVector(candidate.lambdaPt(), candidate.lambdaEta(), candidate.lambdaPhi(), candidate.lambdaMass()); + if (std::abs(particle.Rapidity()) > rapidity || std::abs(particle.Eta()) > v0eta) { + return false; + } + if (candidate.lambdaMass() < MassMin || candidate.lambdaMass() > MassMax) { + return false; + } + if (candidate.v0Cospa() < SysCuts.mincosPA) { + return false; + } + if (fillHistConfig.checkDoubleStatus && candidate.doubleStatus()) { return false; } - if (checkDoubleStatus && candidate.doubleStatus()) { + if (candidate.v0Radius() > SysCuts.maxRadius) { return false; } - if (candidate.v0Radius() > radiusMax) { + if (candidate.v0Radius() < SysCuts.minRadius) { return false; } - if (candidate.v0Radius() < radiusMin) { + if (candidate.dcaBetweenDaughter() > SysCuts.maxDcaDaughters) { return false; } - if (candidate.dcaBetweenDaughter() > dcaDaughters) { + + if (candidate.dcaV0ToPV() > SysCuts.maxdcaV0ToPV) { return false; } - if (candidate.v0Status() == 0 && (std::abs(candidate.dcaPositive()) < dcaProton || std::abs(candidate.dcaNegative()) < dcaPion)) { + + if (candidate.v0Status() == 0 && (std::abs(candidate.dcaPositive()) < SysCuts.mindcaProton || std::abs(candidate.dcaNegative()) < SysCuts.mindcaPion)) { return false; } - if (candidate.v0Status() == 1 && (std::abs(candidate.dcaPositive()) < dcaPion || std::abs(candidate.dcaNegative()) < dcaProton)) { + if (candidate.v0Status() == 1 && (std::abs(candidate.dcaPositive()) < SysCuts.mindcaPion || std::abs(candidate.dcaNegative()) < SysCuts.mindcaProton)) { return false; } if (candidate.lambdaPt() < ptMin) { @@ -495,10 +1064,112 @@ struct lambdaspincorrderived { return w; } + int getWeightCategory(int tag1, int tag2) const + { + // Correction categories: + // 0 = LL, 1 = UL = LAL + ALL, 2 = ALAL + if (tag1 == 0 && tag2 == 0) { + return 0; + } + if (tag1 == 1 && tag2 == 1) { + return 2; + } + return 1; + } + + template + void fillFinalWeightedMixingQA(int tag1, int tag2, + int mapLeg, + int replacedPos, + LV const& particle1, + LV const& particle2, + double weight) + { + // This QA is filled with the exact same mixing-correction weight used + // for the mixed-event sparse, before the NUA factor is applied. + // mapLeg = physical replacement branch used for the CCDB map (1 or 2) + // replacedPos = position of the replaced candidate after possible L#bar{L} reordering (1 or 2) + if (!fillHistConfig.fillWeightQAHistos) { + return; + } + if (!std::isfinite(weight) || weight <= 0.0) { + return; + } + if (mapLeg != 1 && mapLeg != 2) { + return; + } + + auto getEtaOrY = [&](LV const& p) { + return userapidity ? p.Rapidity() : p.Eta(); + }; + + const LV& rep = (replacedPos == 2) ? particle2 : particle1; + const LV& fix = (replacedPos == 2) ? particle1 : particle2; + + const double ptRep = rep.Pt(); + const double phiRep = RecoDecay::constrainAngle(rep.Phi(), 0.0F, harmonic); + const double etaRep = getEtaOrY(rep); + + const double ptFix = fix.Pt(); + const double phiFix = RecoDecay::constrainAngle(fix.Phi(), 0.0F, harmonic); + const double etaFix = getEtaOrY(fix); + + // Exact four pair tags are filled for the legacy REP_*_weighted maps. + // These are useful because UL can be formed later as LAL+ALL. + if (mapLeg == 1) { + if (tag1 == 0 && tag2 == 0) { + histos.fill(HIST("REP_LL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 0 && tag2 == 1) { + histos.fill(HIST("REP_LAL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 0) { + histos.fill(HIST("REP_ALL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 1) { + histos.fill(HIST("REP_ALAL_leg1_weighted"), phiRep, etaRep, ptRep, weight); + } + } else { // mapLeg == 2 + if (tag1 == 0 && tag2 == 0) { + histos.fill(HIST("REP_LL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 0 && tag2 == 1) { + histos.fill(HIST("REP_LAL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 0) { + histos.fill(HIST("REP_ALL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } else if (tag1 == 1 && tag2 == 1) { + histos.fill(HIST("REP_ALAL_leg2_weighted"), phiRep, etaRep, ptRep, weight); + } + } + + // Combined categories are filled for the final-weighted QA maps. + const int wcat = getWeightCategory(tag1, tag2); // 0=LL, 1=UL, 2=ALAL + + if (mapLeg == 1) { + if (wcat == 0) { + histos.fill(HIST("REP_LL_leg1_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_LL_forRepLeg1_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 1) { + histos.fill(HIST("REP_UL_leg1_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_UL_forRepLeg1_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 2) { + histos.fill(HIST("REP_ALAL_leg1_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg1_finalWeighted"), phiFix, etaFix, ptFix, weight); + } + } else { // mapLeg == 2 + if (wcat == 0) { + histos.fill(HIST("REP_LL_leg2_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_LL_forRepLeg2_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 1) { + histos.fill(HIST("REP_UL_leg2_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_UL_forRepLeg2_finalWeighted"), phiFix, etaFix, ptFix, weight); + } else if (wcat == 2) { + histos.fill(HIST("REP_ALAL_leg2_finalWeighted"), phiRep, etaRep, ptRep, weight); + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg2_finalWeighted"), phiFix, etaFix, ptFix, weight); + } + } + } + void fillHistograms(int tag1, int tag2, const ROOT::Math::PtEtaPhiMVector& particle1, const ROOT::Math::PtEtaPhiMVector& particle2, const ROOT::Math::PtEtaPhiMVector& daughpart1, const ROOT::Math::PtEtaPhiMVector& daughpart2, - int datatype, float mixpairweight, int replacedLeg = 1) + int datatype, float mixpairweight, int replacedLeg = 1, int weightMapLeg = -1) { auto lambda1Mass = 0.0; auto lambda2Mass = 0.0; @@ -510,6 +1181,24 @@ struct lambdaspincorrderived { lambda2Mass = o2::constants::physics::MassLambda; } + const auto pion1 = getPionKinematics( + particle1.Pt(), particle1.Eta(), particle1.Phi(), + daughpart1.Pt(), daughpart1.Eta(), daughpart1.Phi()); + + const auto pion2 = getPionKinematics( + particle2.Pt(), particle2.Eta(), particle2.Phi(), + daughpart2.Pt(), daughpart2.Eta(), daughpart2.Phi()); + + const auto pion1Vec = ROOT::Math::PtEtaPhiMVector( + pion1.pt, pion1.eta, pion1.phi, o2::constants::physics::MassPionCharged); + const auto pion2Vec = ROOT::Math::PtEtaPhiMVector( + pion2.pt, pion2.eta, pion2.phi, o2::constants::physics::MassPionCharged); + + const double kStar_pp = getKStar(daughpart1, daughpart2); + const double kStar_pipi = getKStar(pion1Vec, pion2Vec); + const double kStar_ppi = getKStar(daughpart1, pion2Vec); + const double kStar_pip = getKStar(pion1Vec, daughpart2); + auto particle1Dummy = ROOT::Math::PtEtaPhiMVector(particle1.Pt(), particle1.Eta(), particle1.Phi(), lambda1Mass); auto particle2Dummy = ROOT::Math::PtEtaPhiMVector(particle2.Pt(), particle2.Eta(), particle2.Phi(), lambda2Mass); auto pairDummy = particle1Dummy + particle2Dummy; @@ -576,86 +1265,230 @@ struct lambdaspincorrderived { double deltaRap = std::abs(particle1.Rapidity() - particle2.Rapidity()); double deltaR = TMath::Sqrt(deltaRap * deltaRap + dphi_pair * dphi_pair); - double epsWeight1 = 1.0; - double epsWeight2 = 1.0; + // only for weight lookup; must match fillReplacementControlMap() + double yOrEta1_forWeight = deta1; + double yOrEta2_forWeight = deta2; + + if (userapidity) { + yOrEta1_forWeight = particle1.Rapidity(); + yOrEta2_forWeight = particle2.Rapidity(); + } + + // `replacedLeg` is the position of the replaced candidate in the ordered pair + // passed to fillHistograms: 1 -> particle1, 2 -> particle2. + // `weightMapLeg` is the physical replacement branch used to select the CCDB map: + // 1 -> REP_*_leg1, 2 -> REP_*_leg2. This distinction is needed for unlike-sign + // pairs where the pair is reordered to Lambda-AntiLambda before filling. + const int replacedPos = replacedLeg; + const int ccdbMapLeg = (weightMapLeg > 0) ? weightMapLeg : replacedLeg; + + double epsWeightReplaced = 1.0; + double epsWeightFixed = 1.0; if (useweight && datatype == 1) { - if (tag1 == 0 && tag2 == 0) { - epsWeight1 = hweight1->GetBinContent(hweight1->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight12->GetBinContent(hweight12->FindBin(dphi2, deta2, pt2)); - } else if (tag1 == 0 && tag2 == 1) { - epsWeight1 = hweight2->GetBinContent(hweight2->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight22->GetBinContent(hweight22->FindBin(dphi2, deta2, pt2)); - } else if (tag1 == 1 && tag2 == 0) { - epsWeight1 = hweight3->GetBinContent(hweight3->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight32->GetBinContent(hweight32->FindBin(dphi2, deta2, pt2)); - } else if (tag1 == 1 && tag2 == 1) { - epsWeight1 = hweight4->GetBinContent(hweight4->FindBin(dphi1, deta1, pt1)); - epsWeight2 = hweight42->GetBinContent(hweight42->FindBin(dphi2, deta2, pt2)); + const int wcat = getWeightCategory(tag1, tag2); + + auto getRepEps = [&](int mapLeg, double phi, double yOrEta, double pt) -> double { + TH3D* h = nullptr; + if (mapLeg == 1) { + if (wcat == 0) + h = hweight1; + else if (wcat == 1) + h = hweight2; + else if (wcat == 2) + h = hweight4; + } else if (mapLeg == 2) { + if (wcat == 0) + h = hweight12; + else if (wcat == 1) + h = hweight22; + else if (wcat == 2) + h = hweight42; + } + if (!h) { + return 1.0; + } + return h->GetBinContent(h->FindBin(phi, yOrEta, pt)); + }; + + auto getFixedEps = [&](int mapLeg, double phi, double yOrEta, double pt) -> double { + TH3D* h = nullptr; + if (mapLeg == 1) { + if (wcat == 0) + h = gFixedLLRep1; + else if (wcat == 1) + h = gFixedULRep1; + else if (wcat == 2) + h = gFixedALALRep1; + } else if (mapLeg == 2) { + if (wcat == 0) + h = gFixedLLRep2; + else if (wcat == 1) + h = gFixedULRep2; + else if (wcat == 2) + h = gFixedALALRep2; + } + if (!h) { + return 1.0; + } + return h->GetBinContent(h->FindBin(phi, yOrEta, pt)); + }; + + const double phiRep = (replacedPos == 2) ? dphi2 : dphi1; + const double yRep = (replacedPos == 2) ? yOrEta2_forWeight : yOrEta1_forWeight; + const double ptRep = (replacedPos == 2) ? pt2 : pt1; + epsWeightReplaced = getRepEps(ccdbMapLeg, phiRep, yRep, ptRep); + + if (cfgCcdbParam.useFixedWeight) { + const int fixedPos = (replacedPos == 2) ? 1 : 2; + const double phiFix = (fixedPos == 2) ? dphi2 : dphi1; + const double yFix = (fixedPos == 2) ? yOrEta2_forWeight : yOrEta1_forWeight; + const double ptFix = (fixedPos == 2) ? pt2 : pt1; + epsWeightFixed = getFixedEps(ccdbMapLeg, phiFix, yFix, ptFix); } } if (datatype == 0) { const double weight = pairNUAWeight; + + // ---- Δη–Δφ for same-charge ΛΛ, first ΔR bin ---- + if (tag1 == tag2) { + if (particle1.M() > 1.11 && particle1.M() < 1.12 && + particle2.M() > 1.11 && particle2.M() < 1.12) { + + if (deltaR >= 0.0 && deltaR < 3.1) { + + histos.fill(HIST("hDEtaDPhiKStar_pp_SE"), + daughpart1.Eta() - daughpart2.Eta(), + deltaPhiMinusPiToPi(daughpart1.Phi(), daughpart2.Phi()), + deltaR, + kStar_pp); + + histos.fill(HIST("hDEtaDPhiKStar_pipi_SE"), + pion1.eta - pion2.eta, + deltaPhiMinusPiToPi(pion1.phi, pion2.phi), + deltaR, + kStar_pipi); + + histos.fill(HIST("hDEtaDPhiKStar_ppi_SE"), + daughpart1.Eta() - pion2.eta, + deltaPhiMinusPiToPi(daughpart1.Phi(), pion2.phi), + deltaR, + kStar_ppi); + + histos.fill(HIST("hDEtaDPhiKStar_pip_SE"), + pion1.eta - daughpart2.Eta(), + deltaPhiMinusPiToPi(pion1.phi, daughpart2.Phi()), + deltaR, + kStar_pip); + } + } + } + if (tag1 == 0 && tag2 == 0) { - histos.fill(HIST("hPtYSame"), particle1.Pt(), particle1.Rapidity(), nuaWeight1); - histos.fill(HIST("hPhiEtaSame"), dphi1, particle1.Eta(), nuaWeight1); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("hPtYSame"), particle1.Pt(), particle1.Rapidity(), nuaWeight1); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("hPhiEtaSame"), dphi1, particle1.Eta(), nuaWeight1); histos.fill(HIST("hSparseLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } + + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaAntiLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } + } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaAntiLambda"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambda_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } } else if (datatype == 1) { double weight = mixpairweight; if (useweight) { - const double epsWeightReplaced = (replacedLeg == 2) ? epsWeight2 : epsWeight1; - if (!std::isfinite(epsWeightReplaced) || epsWeightReplaced <= 0.0) { + const double epsWeightTotal = epsWeightReplaced * epsWeightFixed; + + if (!std::isfinite(epsWeightTotal) || epsWeightTotal <= 0.0) { return; } - weight = mixpairweight / epsWeightReplaced; + weight = mixpairweight / epsWeightTotal; + } + + // This is the pure mixing-correction weight. + // Do not include NUA here, because TGT/REP/FIX QA maps were filled without NUA. + const double weightMixingQA = weight; + + if (useweight) { + fillFinalWeightedMixingQA(tag1, tag2, ccdbMapLeg, replacedPos, particle1, particle2, weightMixingQA); } + weight *= pairNUAWeight; if (!std::isfinite(weight) || weight <= 0.0) { return; } if (tag1 == 0 && tag2 == 0) { - if (replacedLeg == 1) { - histos.fill(HIST("hPtYMix"), particle1.Pt(), particle1.Rapidity(), nuaWeight1 * mixpairweight); - histos.fill(HIST("hPhiEtaMix"), dphi1, particle1.Eta(), nuaWeight1 * mixpairweight); + if (replacedLeg == 1 || replacedLeg == 2) { + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("hPtYMix"), particle1.Pt(), particle1.Rapidity(), nuaWeight1 * mixpairweight); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("hPhiEtaMix"), dphi1, particle1.Eta(), nuaWeight1 * mixpairweight); } histos.fill(HIST("hSparseLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } + + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); @@ -663,37 +1496,267 @@ struct lambdaspincorrderived { } else if (tag1 == 0 && tag2 == 1) { histos.fill(HIST("hSparseLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseLambdaAntiLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } else if (tag1 == 1 && tag2 == 0) { histos.fill(HIST("hSparseAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } else if (tag1 == 1 && tag2 == 1) { histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, weight); - histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); - if (useAdditionalHisto) { + if (fillHistConfig.fillAnalysisSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedAnalysis"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, deltaRap, std::abs(dphi_pair), weight); + } + if (useAdditionalHisto || fillHistConfig.fillAdditionalSparses) { histos.fill(HIST("hSparseRapAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, deltaRap, weight); histos.fill(HIST("hSparsePhiAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, dphi_pair, weight); histos.fill(HIST("hSparsePairMassAntiLambdaAntiLambdaMixed"), particle1.M(), particle2.M(), cosThetaDiff, pairDummy.M(), weight); } + if (fillHistConfig.fillkStarSparses) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixed_kStar"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, kStar_pp, weight); + } } } } - static inline int pairTypeCode(int tag1, int tag2) + + void fillHistogramsSys(int tag1, int tag2, + const ROOT::Math::PtEtaPhiMVector& particle1, const ROOT::Math::PtEtaPhiMVector& particle2, + const ROOT::Math::PtEtaPhiMVector& daughpart1, const ROOT::Math::PtEtaPhiMVector& daughpart2, + int datatype, float mixpairweight, int SysId, int replacedLeg = 1, int weightMapLeg = -1) { - if (tag1 == 0 && tag2 == 0) { - return 0; // LL - } else if (tag1 == 0 && tag2 == 1) { - return 1; // LAL + + auto lambda1Mass = 0.0; + auto lambda2Mass = 0.0; + if (!usePDGM) { + lambda1Mass = particle1.M(); + lambda2Mass = particle2.M(); + } else { + lambda1Mass = o2::constants::physics::MassLambda; + lambda2Mass = o2::constants::physics::MassLambda; + } + + auto particle1Dummy = ROOT::Math::PtEtaPhiMVector(particle1.Pt(), particle1.Eta(), particle1.Phi(), lambda1Mass); + auto particle2Dummy = ROOT::Math::PtEtaPhiMVector(particle2.Pt(), particle2.Eta(), particle2.Phi(), lambda2Mass); + auto pairDummy = particle1Dummy + particle2Dummy; + ROOT::Math::Boost boostPairToCM{pairDummy.BoostToCM()}; + + // Step1: Boost both Lambdas to pair rest frame + auto lambda1CM = boostPairToCM(particle1Dummy); + auto lambda2CM = boostPairToCM(particle2Dummy); + + // Step2: Boost each Lambda to its own rest frame + ROOT::Math::Boost boostLambda1ToCM{lambda1CM.BoostToCM()}; + ROOT::Math::Boost boostLambda2ToCM{lambda2CM.BoostToCM()}; + + // Also boost daughter protons to pair CM + auto proton1pairCM = boostPairToCM(daughpart1); + auto proton2pairCM = boostPairToCM(daughpart2); + + // Then into each Lambda rest frame + auto proton1LambdaRF = boostLambda1ToCM(proton1pairCM); + auto proton2LambdaRF = boostLambda2ToCM(proton2pairCM); + + // STAR-style alternative + ROOT::Math::Boost boostL1_LabToRF{particle1Dummy.BoostToCM()}; + ROOT::Math::Boost boostL2_LabToRF{particle2Dummy.BoostToCM()}; + + auto p1_LRF = boostL1_LabToRF(daughpart1); + auto p2_LRF = boostL2_LabToRF(daughpart2); + + TVector3 u1 = TVector3(p1_LRF.Px(), p1_LRF.Py(), p1_LRF.Pz()).Unit(); + TVector3 u2 = TVector3(p2_LRF.Px(), p2_LRF.Py(), p2_LRF.Pz()).Unit(); + + TVector3 k1(proton1LambdaRF.Px(), proton1LambdaRF.Py(), proton1LambdaRF.Pz()); + k1 = k1.Unit(); + TVector3 k2(proton2LambdaRF.Px(), proton2LambdaRF.Py(), proton2LambdaRF.Pz()); + k2 = k2.Unit(); + + double cosDeltaTheta_STAR_naive = u1.Dot(u2); + if (cosDeltaTheta_STAR_naive > 1.0) + cosDeltaTheta_STAR_naive = 111.0; + if (cosDeltaTheta_STAR_naive < -1.0) + cosDeltaTheta_STAR_naive = -111.0; + + double cosDeltaTheta_hel = k1.Dot(k2); + if (cosDeltaTheta_hel > 1.0) + cosDeltaTheta_hel = 111.0; + if (cosDeltaTheta_hel < -1.0) + cosDeltaTheta_hel = -111.0; + + double cosThetaDiff = (cosDef == 0) ? cosDeltaTheta_STAR_naive : cosDeltaTheta_hel; + + double pt1 = particle1.Pt(); + double dphi1 = RecoDecay::constrainAngle(particle1.Phi(), 0.0F, harmonic); + double deta1 = particle1.Eta(); + + double pt2 = particle2.Pt(); + double dphi2 = RecoDecay::constrainAngle(particle2.Phi(), 0.0F, harmonic); + double deta2 = particle2.Eta(); + + double nuaWeight1 = getNUAWeight(tag1, particle1.Phi(), particle1.Eta()); + double nuaWeight2 = getNUAWeight(tag2, particle2.Phi(), particle2.Eta()); + const double pairNUAWeight = nuaWeight1 * nuaWeight2; + + double dphi_pair = RecoDecay::constrainAngle(dphi1 - dphi2, -TMath::Pi(), harmonicDphi); + double deltaRap = std::abs(particle1.Rapidity() - particle2.Rapidity()); + double deltaR = TMath::Sqrt(deltaRap * deltaRap + dphi_pair * dphi_pair); + + // only for weight lookup; must match fillReplacementControlMap() + double yOrEta1_forWeight = deta1; + double yOrEta2_forWeight = deta2; + + if (userapidity) { + yOrEta1_forWeight = particle1.Rapidity(); + yOrEta2_forWeight = particle2.Rapidity(); + } + + // `replacedLeg` is the position of the replaced candidate in the ordered pair + // passed to fillHistograms: 1 -> particle1, 2 -> particle2. + // `weightMapLeg` is the physical replacement branch used to select the CCDB map: + // 1 -> REP_*_leg1, 2 -> REP_*_leg2. This distinction is needed for unlike-sign + // pairs where the pair is reordered to Lambda-AntiLambda before filling. + const int replacedPos = replacedLeg; + const int ccdbMapLeg = (weightMapLeg > 0) ? weightMapLeg : replacedLeg; + + double epsWeightReplaced = 1.0; + double epsWeightFixed = 1.0; + + if (useweight && datatype == 1) { + const int wcat = getWeightCategory(tag1, tag2); + + auto getRepEps = [&](int mapLeg, double phi, double yOrEta, double pt) -> double { + TH3D* h = nullptr; + if (mapLeg == 1) { + if (wcat == 0) + h = hweight1; + else if (wcat == 1) + h = hweight2; + else if (wcat == 2) + h = hweight4; + } else if (mapLeg == 2) { + if (wcat == 0) + h = hweight12; + else if (wcat == 1) + h = hweight22; + else if (wcat == 2) + h = hweight42; + } + if (!h) { + return 1.0; + } + return h->GetBinContent(h->FindBin(phi, yOrEta, pt)); + }; + + auto getFixedEps = [&](int mapLeg, double phi, double yOrEta, double pt) -> double { + TH3D* h = nullptr; + if (mapLeg == 1) { + if (wcat == 0) + h = gFixedLLRep1; + else if (wcat == 1) + h = gFixedULRep1; + else if (wcat == 2) + h = gFixedALALRep1; + } else if (mapLeg == 2) { + if (wcat == 0) + h = gFixedLLRep2; + else if (wcat == 1) + h = gFixedULRep2; + else if (wcat == 2) + h = gFixedALALRep2; + } + if (!h) { + return 1.0; + } + return h->GetBinContent(h->FindBin(phi, yOrEta, pt)); + }; + + const double phiRep = (replacedPos == 2) ? dphi2 : dphi1; + const double yRep = (replacedPos == 2) ? yOrEta2_forWeight : yOrEta1_forWeight; + const double ptRep = (replacedPos == 2) ? pt2 : pt1; + epsWeightReplaced = getRepEps(ccdbMapLeg, phiRep, yRep, ptRep); + + if (cfgCcdbParam.useFixedWeight) { + const int fixedPos = (replacedPos == 2) ? 1 : 2; + const double phiFix = (fixedPos == 2) ? dphi2 : dphi1; + const double yFix = (fixedPos == 2) ? yOrEta2_forWeight : yOrEta1_forWeight; + const double ptFix = (fixedPos == 2) ? pt2 : pt1; + epsWeightFixed = getFixedEps(ccdbMapLeg, phiFix, yFix, ptFix); + } + } + + if (datatype == 0) { + const double weight = pairNUAWeight; + if (tag1 == 0 && tag2 == 0) { + histos.fill(HIST("hSparseLambdaLambdaSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } else if (tag1 == 0 && tag2 == 1) { + histos.fill(HIST("hSparseLambdaAntiLambdaSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } else if (tag1 == 1 && tag2 == 0) { + histos.fill(HIST("hSparseAntiLambdaLambdaSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } else if (tag1 == 1 && tag2 == 1) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } + + } else if (datatype == 1) { + double weight = mixpairweight; + + if (useweight) { + const double epsWeightTotal = epsWeightReplaced * epsWeightFixed; + + if (!std::isfinite(epsWeightTotal) || epsWeightTotal <= 0.0) { + return; + } + weight = mixpairweight / epsWeightTotal; + } + + // This is the pure mixing-correction weight. + // Do not include NUA here, because TGT/REP/FIX QA maps were filled without NUA. + const double weightMixingQA = weight; + + if (useweight) { + fillFinalWeightedMixingQA(tag1, tag2, ccdbMapLeg, replacedPos, particle1, particle2, weightMixingQA); + } + + weight *= pairNUAWeight; + if (!std::isfinite(weight) || weight <= 0.0) { + return; + } + + if (tag1 == 0 && tag2 == 0) { + histos.fill(HIST("hSparseLambdaLambdaMixedSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } else if (tag1 == 0 && tag2 == 1) { + histos.fill(HIST("hSparseLambdaAntiLambdaMixedSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } else if (tag1 == 1 && tag2 == 0) { + histos.fill(HIST("hSparseAntiLambdaLambdaMixedSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } else if (tag1 == 1 && tag2 == 1) { + histos.fill(HIST("hSparseAntiLambdaAntiLambdaMixedSys"), particle1.M(), particle2.M(), cosThetaDiff, deltaR, SysId, weight); + } + } + } + + static inline int pairTypeCode(int tag1, int tag2) + { + if (tag1 == 0 && tag2 == 0) { + return 0; // LL + } else if (tag1 == 0 && tag2 == 1) { + return 1; // LAL } else if (tag1 == 1 && tag2 == 0) { return 2; // ALL } else { @@ -735,9 +1798,15 @@ struct lambdaspincorrderived { if (!selectionV0(v0)) { continue; } - histos.fill(HIST("hPtRadiusV0"), v0.lambdaPt(), v0.v0Radius()); - histos.fill(HIST("ptCent"), v0.lambdaPt(), centrality); - histos.fill(HIST("etaCent"), v0.lambdaEta(), centrality); + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("hPtRadiusV0"), v0.lambdaPt(), v0.v0Radius()); + } + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("ptCent"), v0.lambdaPt(), centrality); + } + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("etaCent"), v0.lambdaEta(), centrality); + } proton = ROOT::Math::PtEtaPhiMVector(v0.protonPt(), v0.protonEta(), v0.protonPhi(), o2::constants::physics::MassProton); lambda = ROOT::Math::PtEtaPhiMVector(v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), v0.lambdaMass()); const double phi = RecoDecay::constrainAngle(v0.lambdaPhi(), 0.0F, harmonic); @@ -759,7 +1828,12 @@ struct lambdaspincorrderived { continue; proton2 = ROOT::Math::PtEtaPhiMVector(v02.protonPt(), v02.protonEta(), v02.protonPhi(), o2::constants::physics::MassProton); lambda2 = ROOT::Math::PtEtaPhiMVector(v02.lambdaPt(), v02.lambdaEta(), v02.lambdaPhi(), v02.lambdaMass()); - histos.fill(HIST("deltaPhiSame"), RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), -TMath::Pi(), harmonicDphi)); + if ((v0.v0Status() == 0 && v02.v0Status() == 1) || (v0.v0Status() == 1 && v02.v0Status() == 0)) { + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("deltaPhiSame"), RecoDecay::constrainAngle(v0.lambdaPhi() - v02.lambdaPhi(), -TMath::Pi(), harmonicDphi)); + } + } + // const int ptype = pairTypeCode(v0.v0Status(), v02.v0Status()); if (v0.v0Status() == 0 && v02.v0Status() == 0) { fillHistograms(0, 0, lambda, lambda2, proton, proton2, 0, 1.0); @@ -768,7 +1842,7 @@ struct lambdaspincorrderived { fillHistograms(0, 1, lambda, lambda2, proton, proton2, 0, 1.0); } if (v0.v0Status() == 1 && v02.v0Status() == 0) { - fillHistograms(1, 0, lambda, lambda2, proton, proton2, 0, 1.0); + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 0, 1.0); } if (v0.v0Status() == 1 && v02.v0Status() == 1) { fillHistograms(1, 1, lambda, lambda2, proton, proton2, 0, 1.0); @@ -778,9 +1852,77 @@ struct lambdaspincorrderived { } PROCESS_SWITCH(lambdaspincorrderived, processData, "Process data", true); + void processDataSys(EventCandidates::iterator const&, AllTrackCandidates const& V0s) + { + for (const auto& v0 : V0s) { + + std::vector activeSys; + activeSys.reserve((size_t)nSysTotal); + + for (int sysId = 0; sysId < nSysTotal; ++sysId) { + const auto& sc = sysCuts[sysId]; + if (selectionV0Sys(v0, sc)) { + activeSys.push_back(sysId); + } + } + + if (activeSys.empty()) + continue; + + proton = ROOT::Math::PtEtaPhiMVector(v0.protonPt(), v0.protonEta(), v0.protonPhi(), + o2::constants::physics::MassProton); + lambda = ROOT::Math::PtEtaPhiMVector(v0.lambdaPt(), v0.lambdaEta(), v0.lambdaPhi(), + v0.lambdaMass()); + + for (const auto& v02 : V0s) { + if (v02.index() <= v0.index()) + continue; + if (hasSharedDaughters(v0, v02)) + continue; + + std::vector activePair; + activePair.reserve(activeSys.size()); + + for (const auto& sysId : activeSys) { + const auto& sc = sysCuts[sysId]; + if (selectionV0Sys(v02, sc)) { + activePair.push_back(sysId); + } + } + + if (activePair.empty()) { + continue; + } + + proton2 = ROOT::Math::PtEtaPhiMVector(v02.protonPt(), v02.protonEta(), v02.protonPhi(), + o2::constants::physics::MassProton); + lambda2 = ROOT::Math::PtEtaPhiMVector(v02.lambdaPt(), v02.lambdaEta(), v02.lambdaPhi(), + v02.lambdaMass()); + + for (const auto& sysId : activePair) { + + if (v0.v0Status() == 0 && v02.v0Status() == 0) { + fillHistogramsSys(0, 0, lambda, lambda2, proton, proton2, 0, 1.0, sysId); + } else if (v0.v0Status() == 0 && v02.v0Status() == 1) { + fillHistogramsSys(0, 1, lambda, lambda2, proton, proton2, 0, 1.0, sysId); + } else if (v0.v0Status() == 1 && v02.v0Status() == 0) { + fillHistogramsSys(0, 1, lambda2, lambda, proton2, proton, 0, 1.0, sysId); + } else if (v0.v0Status() == 1 && v02.v0Status() == 1) { + fillHistogramsSys(1, 1, lambda, lambda2, proton, proton2, 0, 1.0, sysId); + } + } + } + } + } + PROCESS_SWITCH(lambdaspincorrderived, processDataSys, "Process Sys analysis", true); + template void fillReplacementControlMap(int tag1, int tag2, int leg, bool isTarget, LV const& particle, float weight) { + + if (!fillHistConfig.fillReplacementQAHistos) { + return; + } const double pt = particle.Pt(); const double phi = RecoDecay::constrainAngle(particle.Phi(), 0.0F, harmonic); @@ -802,6 +1944,8 @@ struct lambdaspincorrderived { } if (leg == 1 && !isTarget) { + + // Raw REP map: used to produce CCDB weight. if (tag1 == 0 && tag2 == 0) histos.fill(HIST("REP_LL_leg1"), phi, etaOrY, pt, weight); else if (tag1 == 0 && tag2 == 1) @@ -810,6 +1954,7 @@ struct lambdaspincorrderived { histos.fill(HIST("REP_ALL_leg1"), phi, etaOrY, pt, weight); else if (tag1 == 1 && tag2 == 1) histos.fill(HIST("REP_ALAL_leg1"), phi, etaOrY, pt, weight); + // Correct weighted REP QA is filled in fillHistograms(), after the exact final weight is computed. return; } @@ -826,6 +1971,8 @@ struct lambdaspincorrderived { } if (leg == 2 && !isTarget) { + + // Raw REP map: used to produce CCDB weight. if (tag1 == 0 && tag2 == 0) histos.fill(HIST("REP_LL_leg2"), phi, etaOrY, pt, weight); else if (tag1 == 0 && tag2 == 1) @@ -834,10 +1981,76 @@ struct lambdaspincorrderived { histos.fill(HIST("REP_ALL_leg2"), phi, etaOrY, pt, weight); else if (tag1 == 1 && tag2 == 1) histos.fill(HIST("REP_ALAL_leg2"), phi, etaOrY, pt, weight); + // Correct weighted REP QA is filled in fillHistograms(), after the exact final weight is computed. return; } } + template + void fillFixedLegControlMap(int tag1, int tag2, + int repLeg, + bool isTarget, + LV const& fixedParticle, + float weight) + { + if (!fillHistConfig.fillFixedLegQAHistos) { + return; + } + const double pt = fixedParticle.Pt(); + const double phi = RecoDecay::constrainAngle(fixedParticle.Phi(), 0.0F, harmonic); + + double etaOrY = fixedParticle.Eta(); + if (userapidity) { + etaOrY = fixedParticle.Rapidity(); + } + + if (repLeg == 1) { + // leg1 is replaced, fixed leg is original leg2 + if (isTarget) { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("TGT_FIX_LL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("TGT_FIX_LAL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("TGT_FIX_ALL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("TGT_FIX_ALAL_forRepLeg1"), phi, etaOrY, pt, weight); + } else { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("SUC_FIX_LL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("SUC_FIX_LAL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("SUC_FIX_ALL_forRepLeg1"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg1"), phi, etaOrY, pt, weight); + } + return; + } + if (repLeg == 2) { + // leg2 is replaced, fixed leg is original leg1 + if (isTarget) { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("TGT_FIX_LL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("TGT_FIX_LAL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("TGT_FIX_ALL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("TGT_FIX_ALAL_forRepLeg2"), phi, etaOrY, pt, weight); + } else { + if (tag1 == 0 && tag2 == 0) + histos.fill(HIST("SUC_FIX_LL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 0 && tag2 == 1) + histos.fill(HIST("SUC_FIX_LAL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 0) + histos.fill(HIST("SUC_FIX_ALL_forRepLeg2"), phi, etaOrY, pt, weight); + else if (tag1 == 1 && tag2 == 1) + histos.fill(HIST("SUC_FIX_ALAL_forRepLeg2"), phi, etaOrY, pt, weight); + } + return; + } + } // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; @@ -849,7 +2062,7 @@ struct lambdaspincorrderived { auto nBins = colBinning.getAllBinsCount(); std::vector>> eventPools(nBins); - for (auto& collision1 : collisions) { + for (const auto& collision1 : collisions) { const int bin = colBinning.getBin(std::make_tuple(collision1.posz(), collision1.cent())); if (bin < 0) { continue; @@ -866,7 +2079,7 @@ struct lambdaspincorrderived { continue; } - for (auto& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { + for (const auto& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { if (!selectionV0(t1) || !selectionV0(t2)) { continue; } @@ -878,17 +2091,6 @@ struct lambdaspincorrderived { const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); - if (doMixLeg1) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - 1.0f); - } - if (doMixLeg2) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - 1.0f); - } - struct PV { AllTrackCandidates* pool; int nRepl1 = 0; @@ -897,6 +2099,8 @@ struct lambdaspincorrderived { std::vector usable; int totalRepl = 0; + int totalRepl1 = 0; + int totalRepl2 = 0; int mixes = 0; for (auto it = eventPools[bin].rbegin(); it != eventPools[bin].rend() && mixes < nEvtMixing; ++it, ++mixes) { @@ -910,19 +2114,21 @@ struct lambdaspincorrderived { int nRepl1 = 0; int nRepl2 = 0; - for (auto& tX : poolB) { + for (const auto& tX : poolB) { if (!selectionV0(tX)) { continue; } if (doMixLeg1) { - if (checkKinematics(t1, tX)) { + // Single-track replacement: replace a candidate only by the same species. + if (tX.v0Status() == t1.v0Status() && checkKinematics(t1, tX)) { ++nRepl1; } } if (doMixLeg2) { - if (checkKinematics(t2, tX)) { + // Single-track replacement: replace a candidate only by the same species. + if (tX.v0Status() == t2.v0Status() && checkKinematics(t2, tX)) { ++nRepl2; } } @@ -931,6 +2137,8 @@ struct lambdaspincorrderived { if (nRepl1 > 0 || nRepl2 > 0) { usable.push_back(PV{&poolB, nRepl1, nRepl2}); totalRepl += nRepl1 + nRepl2; + totalRepl1 += nRepl1; + totalRepl2 += nRepl2; } } @@ -940,17 +2148,39 @@ struct lambdaspincorrderived { const float wBase = 1.0f / static_cast(totalRepl); - for (auto& pv : usable) { + // Single-track replacement target must use the same branch normalization + // as the actually used replacement candidates. Per SE pair: + // sum REP_leg1 weights = totalRepl1 / totalRepl + // sum REP_leg2 weights = totalRepl2 / totalRepl + // Therefore TGT leg1/leg2 are filled with the same weights. + if (doMixLeg1 && totalRepl1 > 0) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + static_cast(totalRepl1) * wBase); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + static_cast(totalRepl1) * wBase); + } + if (doMixLeg2 && totalRepl2 > 0) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + static_cast(totalRepl2) * wBase); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + static_cast(totalRepl2) * wBase); + } + + for (const auto& pv : usable) { auto& poolB = *pv.pool; - for (auto& tX : poolB) { + for (const auto& tX : poolB) { if (!selectionV0(tX)) { continue; } // -------- leg-1 replacement: (tX, t2) if (doMixLeg1) { - if (checkKinematics(t1, tX)) { + if (tX.v0Status() == t1.v0Status() && checkKinematics(t1, tX)) { fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wBase); @@ -968,7 +2198,8 @@ struct lambdaspincorrderived { RecoDecay::constrainAngle(lambda2.Phi(), 0.0F, harmonic), -TMath::Pi(), harmonicDphi); - histos.fill(HIST("deltaPhiMix"), dPhi, wBase); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, wBase); fillHistograms(tX.v0Status(), t2.v0Status(), lambda, lambda2, proton, proton2, 1, wBase, 1); @@ -977,7 +2208,7 @@ struct lambdaspincorrderived { // -------- leg-2 replacement: (t1, tX) if (doMixLeg2) { - if (checkKinematics(t2, tX)) { + if (tX.v0Status() == t2.v0Status() && checkKinematics(t2, tX)) { fillReplacementControlMap(t1.v0Status(), tX.v0Status(), 2, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), wBase); @@ -995,7 +2226,8 @@ struct lambdaspincorrderived { RecoDecay::constrainAngle(lambda2.Phi(), 0.0F, harmonic), -TMath::Pi(), harmonicDphi); - histos.fill(HIST("deltaPhiMix"), dPhi, wBase); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, wBase); fillHistograms(t1.v0Status(), tX.v0Status(), lambda, lambda2, proton, proton2, 1, wBase, 2); @@ -1129,8 +2361,6 @@ struct lambdaspincorrderived { inline int ptBin(float pt) const { return binFromValue(pt, ptMin, ptStep, nPt_); } inline int etaBin(float eta) const { return binFromValue(eta, etaMin, etaStep, nEta_); } inline int phiBin(float phi) const { return binFromValue(phi, phiMin, phiStep, nPhi_); } - inline int massBin(float m) const { return binFromValue(m, mMin, mStep, nM_); } - inline int radiusBin(float r) const { if (!std::isfinite(r) || nR_ <= 0) { @@ -1151,6 +2381,210 @@ struct lambdaspincorrderived { uint16_t ptBin, etaBin, phiBin, mBin, rBin; }; + struct StoredV6Candidate { + int64_t collisionIdx = -1; + int64_t globalIdx = -1; + int status = -1; + bool isDouble = false; + float cospa = 0.f; + float radius = 0.f; + float dcaPos = 0.f; + float dcaNeg = 0.f; + float dcaDau = 0.f; + float lPt = 0.f; + float lEta = 0.f; + float lPhi = 0.f; + float lMass = 0.f; + float pPt = 0.f; + float pEta = 0.f; + float pPhi = 0.f; + int64_t pIndex = -1; + int64_t piIndex = -1; + + int v0Status() const { return status; } + bool doubleStatus() const { return isDouble; } + float v0Cospa() const { return cospa; } + float v0Radius() const { return radius; } + float dcaPositive() const { return dcaPos; } + float dcaNegative() const { return dcaNeg; } + float dcaBetweenDaughter() const { return dcaDau; } + float lambdaPt() const { return lPt; } + float lambdaEta() const { return lEta; } + float lambdaPhi() const { return lPhi; } + float lambdaMass() const { return lMass; } + float protonPt() const { return pPt; } + float protonEta() const { return pEta; } + float protonPhi() const { return pPhi; } + int64_t protonIndex() const { return pIndex; } + int64_t pionIndex() const { return piIndex; } + int64_t globalIndex() const { return globalIdx; } + }; + + struct PendingV6Branch { + StoredV6Candidate target; + StoredV6Candidate fixed; + int colBin = -1; + int replacedLeg = 1; + int age = 0; + uint64_t seed = 0; + }; + + template + StoredV6Candidate storeV6Candidate(T const& t, int64_t collisionIdx) const + { + return {collisionIdx, static_cast(t.globalIndex()), static_cast(t.v0Status()), static_cast(t.doubleStatus()), + t.v0Cospa(), t.v0Radius(), t.dcaPositive(), t.dcaNegative(), t.dcaBetweenDaughter(), + t.lambdaPt(), t.lambdaEta(), t.lambdaPhi(), t.lambdaMass(), + t.protonPt(), t.protonEta(), t.protonPhi(), + static_cast(t.protonIndex()), static_cast(t.pionIndex())}; + } + + template + void fillV6MixedBranch(TRep const& replacement, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) + { + const auto repProton = ROOT::Math::PtEtaPhiMVector(replacement.protonPt(), replacement.protonEta(), replacement.protonPhi(), o2::constants::physics::MassProton); + const auto repLambda = ROOT::Math::PtEtaPhiMVector(replacement.lambdaPt(), replacement.lambdaEta(), replacement.lambdaPhi(), replacement.lambdaMass()); + const auto fixedProton = ROOT::Math::PtEtaPhiMVector(fixed.protonPt(), fixed.protonEta(), fixed.protonPhi(), o2::constants::physics::MassProton); + const auto fixedLambda = ROOT::Math::PtEtaPhiMVector(fixed.lambdaPt(), fixed.lambdaEta(), fixed.lambdaPhi(), fixed.lambdaMass()); + const int repStatus = replacement.v0Status(); + const int fixedStatus = fixed.v0Status(); + + if (replacedLeg == 1) { + fillReplacementControlMap(repStatus, fixedStatus, 1, false, repLambda, controlWeight); + fillFixedLegControlMap(repStatus, fixedStatus, 1, false, fixedLambda, controlWeight); + if ((repStatus == 0 && fixedStatus == 1) || (repStatus == 1 && fixedStatus == 0)) { + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)repLambda.Phi(), (float)fixedLambda.Phi()), mixWeight); + } + } + if (repStatus == 0 && fixedStatus == 1) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } else if (repStatus == 1 && fixedStatus == 0) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 1); + } else { + fillHistograms(repStatus, fixedStatus, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } + return; + } + + fillReplacementControlMap(fixedStatus, repStatus, 2, false, repLambda, controlWeight); + fillFixedLegControlMap(fixedStatus, repStatus, 2, false, fixedLambda, controlWeight); + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)fixedLambda.Phi(), (float)repLambda.Phi()), mixWeight); + } + if (fixedStatus == 0 && repStatus == 1) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } else if (fixedStatus == 1 && repStatus == 0) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 2); + } else { + fillHistograms(fixedStatus, repStatus, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } + } + + struct StoredV6CandidateMC { + int64_t globalIdx = -1; + int status = -1; + bool isDouble = false; + float cospa = 0.f; + float radius = 0.f; + float dcaPos = 0.f; + float dcaNeg = 0.f; + float dcaDau = 0.f; + float lPt = 0.f; + float lEta = 0.f; + float lPhi = 0.f; + float lMass = 0.f; + float pPt = 0.f; + float pEta = 0.f; + float pPhi = 0.f; + int64_t pIndex = -1; + int64_t piIndex = -1; + + int v0Statusmc() const { return status; } + bool doubleStatusmc() const { return isDouble; } + float v0Cospamc() const { return cospa; } + float v0Radiusmc() const { return radius; } + float dcaPositivemc() const { return dcaPos; } + float dcaNegativemc() const { return dcaNeg; } + float dcaBetweenDaughtermc() const { return dcaDau; } + float lambdaPtmc() const { return lPt; } + float lambdaEtamc() const { return lEta; } + float lambdaPhimc() const { return lPhi; } + float lambdaMassmc() const { return lMass; } + float protonPtmc() const { return pPt; } + float protonEtamc() const { return pEta; } + float protonPhimc() const { return pPhi; } + int64_t protonIndexmc() const { return pIndex; } + int64_t pionIndexmc() const { return piIndex; } + int64_t globalIndex() const { return globalIdx; } + }; + + struct PendingV6BranchMC { + StoredV6CandidateMC target; + StoredV6CandidateMC fixed; + int colBin = -1; + int replacedLeg = 1; + int age = 0; + uint64_t seed = 0; + }; + + struct V6PendingState { + std::deque data; + std::deque mc; + }; + + V6PendingState v6Pending; + + template + StoredV6CandidateMC storeV6CandidateMC(T const& t) const + { + return {static_cast(t.globalIndex()), mcacc::v0Status(t), mcacc::doubleStatus(t), + mcacc::v0CosPA(t), mcacc::v0Radius(t), mcacc::dcaPos(t), mcacc::dcaNeg(t), mcacc::dcaDau(t), + mcacc::lamPt(t), mcacc::lamEta(t), mcacc::lamPhi(t), mcacc::lamMass(t), + mcacc::prPt(t), mcacc::prEta(t), mcacc::prPhi(t), + static_cast(mcacc::prIdx(t)), static_cast(mcacc::piIdx(t))}; + } + + template + void fillV6MixedBranchMC(TRep const& replacement, TFixed const& fixed, int replacedLeg, float controlWeight, float mixWeight) + { + const auto repProton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(replacement), mcacc::prEta(replacement), mcacc::prPhi(replacement), o2::constants::physics::MassProton); + const auto repLambda = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(replacement), mcacc::lamEta(replacement), mcacc::lamPhi(replacement), mcacc::lamMass(replacement)); + const auto fixedProton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(fixed), mcacc::prEta(fixed), mcacc::prPhi(fixed), o2::constants::physics::MassProton); + const auto fixedLambda = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(fixed), mcacc::lamEta(fixed), mcacc::lamPhi(fixed), mcacc::lamMass(fixed)); + const int repStatus = mcacc::v0Status(replacement); + const int fixedStatus = mcacc::v0Status(fixed); + + if (replacedLeg == 1) { + fillReplacementControlMap(repStatus, fixedStatus, 1, false, repLambda, controlWeight); + fillFixedLegControlMap(repStatus, fixedStatus, 1, false, fixedLambda, controlWeight); + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)repLambda.Phi(), (float)fixedLambda.Phi()), mixWeight); + } + if (repStatus == 0 && fixedStatus == 1) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } else if (repStatus == 1 && fixedStatus == 0) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 1); + } else { + fillHistograms(repStatus, fixedStatus, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 1); + } + return; + } + + fillReplacementControlMap(fixedStatus, repStatus, 2, false, repLambda, controlWeight); + fillFixedLegControlMap(fixedStatus, repStatus, 2, false, fixedLambda, controlWeight); + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("deltaPhiMix"), deltaPhiMinusPiToPi((float)fixedLambda.Phi(), (float)repLambda.Phi()), mixWeight); + } + if (fixedStatus == 0 && repStatus == 1) { + fillHistograms(0, 1, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } else if (fixedStatus == 1 && repStatus == 0) { + fillHistograms(0, 1, repLambda, fixedLambda, repProton, fixedProton, 1, mixWeight, 1, 2); + } else { + fillHistograms(fixedStatus, repStatus, fixedLambda, repLambda, fixedProton, repProton, 1, mixWeight, 2, 2); + } + } + static inline size_t linearKeyR(int colBin, int statBin, int ptBin, int etaBin, int phiBin, int mBin, int rBin, int nStatus, int nPt, int nEta, int nPhi, int nM, int nR) @@ -1174,25 +2608,30 @@ struct lambdaspincorrderived { if (mcacc::lamMass(candidate) < MassMin || mcacc::lamMass(candidate) > MassMax) { return false; } - if (mcacc::v0CosPA(candidate) < cosPA) { + if (mcacc::v0CosPA(candidate) < v0Configurations.cosPA) { return false; } - if (checkDoubleStatus && mcacc::doubleStatus(candidate)) { + if (fillHistConfig.checkDoubleStatus && mcacc::doubleStatus(candidate)) { return false; } - if (mcacc::v0Radius(candidate) > radiusMax) { + if (mcacc::v0Radius(candidate) > v0Configurations.radiusMax) { return false; } - if (mcacc::v0Radius(candidate) < radiusMin) { + if (mcacc::v0Radius(candidate) < v0Configurations.radiusMin) { return false; } - if (mcacc::dcaDau(candidate) > dcaDaughters) { + if (mcacc::dcaDau(candidate) > v0Configurations.dcaDaughters) { return false; } - if (mcacc::v0Status(candidate) == 0 && (std::abs(mcacc::dcaPos(candidate)) < dcaProton || std::abs(mcacc::dcaNeg(candidate)) < dcaPion)) { + + if (mcacc::dcaV0ToPVMC(candidate) > v0Configurations.dcaV0ToPV) { + return false; + } + + if (mcacc::v0Status(candidate) == 0 && (std::abs(mcacc::dcaPos(candidate)) < v0Configurations.dcaProton || std::abs(mcacc::dcaNeg(candidate)) < v0Configurations.dcaPion)) { return false; } - if (mcacc::v0Status(candidate) == 1 && (std::abs(mcacc::dcaPos(candidate)) < dcaPion || std::abs(mcacc::dcaNeg(candidate)) < dcaProton)) { + if (mcacc::v0Status(candidate) == 1 && (std::abs(mcacc::dcaPos(candidate)) < v0Configurations.dcaPion || std::abs(mcacc::dcaNeg(candidate)) < v0Configurations.dcaProton)) { return false; } if (mcacc::lamPt(candidate) < ptMin) { @@ -1269,9 +2708,15 @@ struct lambdaspincorrderived { if (!selectionV0MC(v0)) { continue; } - histos.fill(HIST("hPtRadiusV0"), mcacc::lamPt(v0), mcacc::v0Radius(v0)); - histos.fill(HIST("ptCent"), mcacc::lamPt(v0), centrality); - histos.fill(HIST("etaCent"), mcacc::lamEta(v0), centrality); + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("hPtRadiusV0"), mcacc::lamPt(v0), mcacc::v0Radius(v0)); + } + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("ptCent"), mcacc::lamPt(v0), centrality); + } + if (fillHistConfig.fillBasicQAHistos) { + histos.fill(HIST("etaCent"), mcacc::lamEta(v0), centrality); + } proton = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(v0), mcacc::prEta(v0), mcacc::prPhi(v0), o2::constants::physics::MassProton); @@ -1301,11 +2746,18 @@ struct lambdaspincorrderived { RecoDecay::constrainAngle(mcacc::lamPhi(v0) - mcacc::lamPhi(v02), -TMath::Pi(), harmonicDphi)); - // const int ptype = pairTypeCode(mcacc::v0Status(v0), mcacc::v0Status(v02)); - // datatype=0 (same event) - fillHistograms(mcacc::v0Status(v0), mcacc::v0Status(v02), - lambda, lambda2, proton, proton2, - /*datatype=*/0, /*mixpairweight=*/1.0f); + const int s1 = mcacc::v0Status(v0); + const int s2 = mcacc::v0Status(v02); + + if (s1 == 0 && s2 == 0) { + fillHistograms(0, 0, lambda, lambda2, proton, proton2, 0, 1.0f); + } else if (s1 == 0 && s2 == 1) { + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 0, 1.0f); + } else if (s1 == 1 && s2 == 0) { + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 0, 1.0f); + } else if (s1 == 1 && s2 == 1) { + fillHistograms(1, 1, lambda, lambda2, proton, proton2, 0, 1.0f); + } } } } @@ -1328,20 +2780,36 @@ struct lambdaspincorrderived { { return std::abs(deltaPhiMinusPiToPi(phiA, phiB)); } - // symmetric neighbors for phi: no wrap at edge + + // symmetric neighbors for phi with periodic wrap at 0/2pi static inline void collectNeighborBinsPhi(int b, int nPhi, int nNeighbor, std::vector& out) { out.clear(); + if (nPhi <= 0 || b < 0 || b >= nPhi) { + return; + } + + if (2 * nNeighbor + 1 >= nPhi) { + out.reserve(nPhi); + for (int bb = 0; bb < nPhi; ++bb) { + out.push_back(bb); + } + return; + } + out.reserve(2 * nNeighbor + 1); for (int d = -nNeighbor; d <= nNeighbor; ++d) { - const int bb = b + d; - if (bb >= 0 && bb < nPhi) { - out.push_back(bb); + int bb = (b + d) % nPhi; + if (bb < 0) { + bb += nPhi; } + out.push_back(bb); } + std::sort(out.begin(), out.end()); out.erase(std::unique(out.begin(), out.end()), out.end()); } + static inline void collectNeighborBinsClamp(int b, int nBins, int nNeighbor, std::vector& out) { out.clear(); @@ -1353,6 +2821,22 @@ struct lambdaspincorrderived { } } } + static inline void collectNeighborBinsMass(int b, int nBins, int nNeighbor, std::vector& out) + { + out.clear(); + out.reserve(2 * nNeighbor + 1); + + for (int d = -nNeighbor; d <= nNeighbor; ++d) { + const int bb = b + d; + if (bb >= 0 && bb < nBins) { + out.push_back(bb); + } + } + + std::sort(out.begin(), out.end()); + out.erase(std::unique(out.begin(), out.end()), out.end()); + } + static inline int getMassRegionFromEdges(float m, const std::vector& edges) { if (edges.size() != 4) { @@ -1366,46 +2850,588 @@ struct lambdaspincorrderived { if (m >= edges[2] && m < edges[3]) return 2; // high sideband - return -1; - } - static inline int getMassMixClassFromEdges(float m, const std::vector& edges) - { - // no mass separation - if (edges.size() == 2) { - if (m >= edges[0] && m < edges[1]) - return 0; - return -1; + return -1; + } + static inline int getMassMixClassFromEdges(float m, const std::vector& edges) + { + // no mass separation + if (edges.size() == 2) { + if (m >= edges[0] && m < edges[1]) + return 0; + return -1; + } + + // 3 regions: SB low, signal, SB high + if (edges.size() == 4) { + if (m >= edges[0] && m < edges[1]) + return 0; // low sideband + if (m >= edges[1] && m < edges[2]) + return 1; // signal + if (m >= edges[2] && m < edges[3]) + return 0; // high sideband merged with low sideband + return -1; + } + + return -1; + } + static inline uint64_t splitmix64(uint64_t x) + { + // simple deterministic hash for reproducible shuffling + x += 0x9e3779b97f4a7c15ULL; + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + return x ^ (x >> 31); + } + + void processMEV6(EventCandidates const& collisions, AllTrackCandidates const& V0s) + { + MixBinnerR mb{ + ptMin.value, + ptMax.value, + ptMix.value, + v0Configurations.v0etaMixBuffer.value, + etaMix.value, + phiMix.value, + MassMin.value, + MassMax.value, + cfgV5MassBins.value, + cfgMixRadiusParam.cfgMixRadiusBins.value}; + + const int nCol = colBinning.getAllBinsCount(); + const int nStat = N_STATUS; + const int nPt = mb.nPt(); + const int nEta = mb.nEta(); + const int nPhi = mb.nPhi(); + const int nM = mb.nM(); + const int nR = mb.nR(); + + const size_t nKeys = static_cast(nCol) * nStat * nPt * nEta * nPhi * nM * nR; + std::vector> buffer(nKeys); + + // -------- PASS 1: fill buffer -------- + for (auto const& col : collisions) { + const int colBin = colBinning.getBin(std::make_tuple(col.posz(), col.cent())); + if (colBin < 0) { + continue; + } + + auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); + + for (auto const& t : slice) { + if (!selectionV0(t)) { + continue; + } + + const int status = static_cast(t.v0Status()); + if (status < 0 || status >= nStat) { + continue; + } + + const int ptB = mb.ptBin(t.lambdaPt()); + + int etaB = mb.etaBin(t.lambdaEta()); + if (userapidity) { + const auto lv = ROOT::Math::PtEtaPhiMVector(t.lambdaPt(), t.lambdaEta(), t.lambdaPhi(), t.lambdaMass()); + etaB = mb.etaBin(lv.Rapidity()); + } + + const int phiB = mb.phiBin(RecoDecay::constrainAngle(t.lambdaPhi(), 0.0, harmonic)); + const int mB = getMassMixClassFromEdges(t.lambdaMass(), massMixEdges.value); + const int rB = mb.radiusBin(t.v0Radius()); + + if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0 || rB < 0) { + continue; + } + + const size_t key = linearKeyR(colBin, status, ptB, etaB, phiB, mB, rB, + nStat, nPt, nEta, nPhi, nM, nR); + + buffer[key].push_back(BufferCandR{ + .collisionIdx = static_cast(col.index()), + .rowIndex = static_cast(t.globalIndex()), + .v0Status = static_cast(status), + .ptBin = static_cast(ptB), + .etaBin = static_cast(etaB), + .phiBin = static_cast(phiB), + .mBin = static_cast(mB), + .rBin = static_cast(rB)}); + } + } + + const int nN_pt = std::max(0, cfgV5NeighborPt.value); + const int nN_eta = std::max(0, cfgV5NeighborEta.value); + const int nN_phi = std::max(0, cfgV5NeighborPhi.value); + + std::vector ptBins, etaBins, phiBins; + std::vector matches1, matches2; + matches1.reserve(256); + matches2.reserve(256); + + auto collectMatchesForReplacedLeg = [&](auto const& tRep, auto const& tKeep, int colBin, int64_t curColIdx, std::vector& matches) { + matches.clear(); + + const int status = static_cast(tRep.v0Status()); + if (status < 0 || status >= nStat) { + return; + } + + const int ptB = mb.ptBin(tRep.lambdaPt()); + + int etaB = mb.etaBin(tRep.lambdaEta()); + if (userapidity) { + const auto lv = ROOT::Math::PtEtaPhiMVector(tRep.lambdaPt(), tRep.lambdaEta(), tRep.lambdaPhi(), tRep.lambdaMass()); + etaB = mb.etaBin(lv.Rapidity()); + } + + const int phiB = mb.phiBin(RecoDecay::constrainAngle(tRep.lambdaPhi(), 0.0, harmonic)); + const int mB = getMassMixClassFromEdges(tRep.lambdaMass(), massMixEdges.value); + const int rB = mb.radiusBin(tRep.v0Radius()); + + if (ptB < 0 || etaB < 0 || phiB < 0 || mB < 0 || rB < 0) { + return; + } + auto collectFromBins = [&](const std::vector& ptUseBins, + const std::vector& etaUseBins, + const std::vector& phiUseBins) { + for (const auto& ptUse : ptUseBins) { + for (const auto& etaUse : etaUseBins) { + for (const auto& phiUse : phiUseBins) { + const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, + nStat, nPt, nEta, nPhi, nM, nR)]; + + for (auto const& bc : vec) { + if (bc.collisionIdx == curColIdx) + continue; + + auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); + + if (!selectionV0(tX)) + continue; + if (!checkKinematics(tRep, tX)) + continue; + + if (tX.globalIndex() == tRep.globalIndex()) + continue; + if (tX.globalIndex() == tKeep.globalIndex()) + continue; + + if (hasSharedDaughters(tX, tKeep)) + continue; + if (hasSharedDaughters(tX, tRep)) + continue; + + matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); + } + } + } + } + }; + + matches.clear(); + + // 1) exact bin first + ptBins.clear(); + etaBins.clear(); + phiBins.clear(); + + ptBins.push_back(ptB); + etaBins.push_back(etaB); + phiBins.push_back(phiB); + + collectFromBins(ptBins, etaBins, phiBins); + + // 2) if exact bin gives fewer than required matches, also search neighbors + const int targetMatches = (cfgV5MaxMatches.value > 0) ? cfgV5MaxMatches.value : 1; + + if ((int)matches.size() < targetMatches) { + std::vector ptBinsN, etaBinsN, phiBinsN; + collectNeighborBinsClamp(ptB, nPt, nN_pt, ptBinsN); + collectNeighborBinsClamp(etaB, nEta, nN_eta, etaBinsN); + collectNeighborBinsPhi(phiB, nPhi, nN_phi, phiBinsN); + + for (const auto& ptUse : ptBinsN) { + for (const auto& etaUse : etaBinsN) { + for (const auto& phiUse : phiBinsN) { + if (ptUse == ptB && etaUse == etaB && phiUse == phiB) + continue; + + const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, + nStat, nPt, nEta, nPhi, nM, nR)]; + + for (auto const& bc : vec) { + if (bc.collisionIdx == curColIdx) + continue; + + auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); + + if (!selectionV0(tX)) + continue; + if (!checkKinematics(tRep, tX)) + continue; + + if (tX.globalIndex() == tRep.globalIndex()) + continue; + if (tX.globalIndex() == tKeep.globalIndex()) + continue; + + if (hasSharedDaughters(tX, tKeep)) + continue; + if (hasSharedDaughters(tX, tRep)) + continue; + + matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); + } + } + } + } + } + + std::sort(matches.begin(), matches.end(), + [](auto const& a, auto const& b) { + return std::tie(a.collisionIdx, a.rowIndex) < std::tie(b.collisionIdx, b.rowIndex); + }); + matches.erase(std::unique(matches.begin(), matches.end(), + [](auto const& a, auto const& b) { + return a.collisionIdx == b.collisionIdx && a.rowIndex == b.rowIndex; + }), + matches.end()); + }; + + auto downsampleMatches = [&](std::vector& matches, uint64_t seedBase) { + if (cfgV5MaxMatches.value > 0 && (int)matches.size() > cfgV5MaxMatches.value) { + uint64_t seed = cfgMixSeed.value ^ splitmix64(seedBase); + const int K = cfgV5MaxMatches.value; + for (int i = 0; i < K; ++i) { + seed = splitmix64(seed); + const int j = i + (int)(seed % (uint64_t)(matches.size() - i)); + std::swap(matches[i], matches[j]); + } + matches.resize(K); + } + }; + + const size_t pendingAtStart = v6Pending.data.size(); + size_t pendingMatched = 0; + size_t pendingExpired = 0; + size_t pendingAdded = 0; + if (!cfgV6CarryUnmatched) { + v6Pending.data.clear(); + } else { + for (auto it = v6Pending.data.begin(); it != v6Pending.data.end();) { + auto& pending = *it; + ++pending.age; + if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { + ++pendingExpired; + it = v6Pending.data.erase(it); + continue; + } + + auto& matches = pending.replacedLeg == 1 ? matches1 : matches2; + collectMatchesForReplacedLeg(pending.target, pending.fixed, pending.colBin, -1, matches); + limitMatchesToNEvents(matches, nEvtMixing.value); + downsampleMatches(matches, pending.seed ^ splitmix64(static_cast(pending.age))); + + int nAccepted = 0; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + ++nAccepted; + } + + if (nAccepted == 0) { + ++it; + continue; + } + + const float controlWeight = 1.0f / static_cast(nAccepted); + const float branchNorm = cfgMixLegMode.value == 2 ? 0.5f : 1.0f; + const float mixWeight = branchNorm * controlWeight; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + fillV6MixedBranch(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + } + ++pendingMatched; + it = v6Pending.data.erase(it); + } + } + + // -------- PASS 2: configurable one-leg / two-leg mixing -------- + for (auto const& col1 : collisions) { + const int colBin = colBinning.getBin(std::make_tuple(col1.posz(), col1.cent())); + if (colBin < 0) { + continue; + } + + const int64_t curColIdx = static_cast(col1.index()); + auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); + + for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { + if (!selectionV0(t1) || !selectionV0(t2)) { + continue; + } + if (t2.index() <= t1.index()) { + continue; + } + if (hasSharedDaughters(t1, t2)) + continue; + const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); + const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); + + // Fill TGT maps before searching for replacements. This makes TGT the + // true same-event target phase space for the selected pair, matching the + // hPtYSame definition. REP below is filled only for accepted replacements. + if (doMixLeg1) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + } + if (doMixLeg2) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + } + + if (doMixLeg1) { + collectMatchesForReplacedLeg(t1, t2, colBin, curColIdx, matches1); + limitMatchesToNEvents(matches1, nEvtMixing.value); + downsampleMatches(matches1, (uint64_t)t1.globalIndex() ^ (splitmix64((uint64_t)t2.globalIndex()) + 0x111ULL) ^ splitmix64((uint64_t)curColIdx)); + } else { + matches1.clear(); + } + + if (doMixLeg2) { + collectMatchesForReplacedLeg(t2, t1, colBin, curColIdx, matches2); + limitMatchesToNEvents(matches2, nEvtMixing.value); + downsampleMatches(matches2, (uint64_t)t2.globalIndex() ^ (splitmix64((uint64_t)t1.globalIndex()) + 0x222ULL) ^ splitmix64((uint64_t)curColIdx)); + } else { + matches2.clear(); + } + + // Do not fill TGT here. TGT has already been filled above as the + // selected same-event target phase space, independent of replacement success. + int nFill1 = 0; + int nFill2 = 0; + // count actual accepted fills for leg-1 replacement + if (doMixLeg1) { + for (auto const& m : matches1) { + auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tX)) + continue; + if (tX.v0Status() != t1.v0Status()) + continue; + if (!checkKinematics(t1, tX)) + continue; + if (tX.globalIndex() == t1.globalIndex()) + continue; + if (tX.globalIndex() == t2.globalIndex()) + continue; + if (hasSharedDaughters(tX, t2)) + continue; + if (hasSharedDaughters(tX, t1)) + continue; + ++nFill1; + } + } + + // count actual accepted fills for leg-2 replacement + if (doMixLeg2) { + for (auto const& m : matches2) { + auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tY)) + continue; + if (tY.v0Status() != t2.v0Status()) + continue; + if (!checkKinematics(t2, tY)) + continue; + if (tY.globalIndex() == t2.globalIndex()) + continue; + if (tY.globalIndex() == t1.globalIndex()) + continue; + if (hasSharedDaughters(tY, t2)) + continue; + if (hasSharedDaughters(tY, t1)) + continue; + ++nFill2; + } + } + + if (cfgV6CarryUnmatched) { + const auto hasPendingSpace = [&]() { + return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.data.size()) < cfgV6MaxPendingBranches.value; + }; + if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), colBin, 1, 0, + static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t2, curColIdx), storeV6Candidate(t1, curColIdx), colBin, 2, 0, + static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + } + histos.fill(HIST("hNMEperSE_leg2"), nFill2); + histos.fill(HIST("hNMEperSE_leg1"), nFill1); + + if (nFill1 <= 0 && nFill2 <= 0) { + continue; + } + // Residual-weight QA needs a leg-specific normalization: + // TGT_leg is filled once per same-event target candidate with weight 1. + // REP_leg is the average replacement distribution for that same target. + const float wSELeg1 = (nFill1 > 0) ? 1.0f / static_cast(nFill1) : 0.0f; + const float wSELeg2 = (nFill2 > 0) ? 1.0f / static_cast(nFill2) : 0.0f; + + const int nActiveMixBranches = ((doMixLeg1 && nFill1 > 0) ? 1 : 0) + + ((doMixLeg2 && nFill2 > 0) ? 1 : 0); + float branchNorm = 1.0f; + if (cfgMixLegMode.value == 2) { + branchNorm = cfgV6CarryUnmatched ? 0.5f : 1.0f / static_cast(nActiveMixBranches); + } + const float finalMixWeightLeg1 = branchNorm * wSELeg1; + const float finalMixWeightLeg2 = branchNorm * wSELeg2; + + // Do not fill target maps here: TGT has already been filled above, + // before searching for replacements, so that TGT represents all selected + // same-event targets rather than only targets with successful replacements. + + if (doMixLeg1 && nFill1 > 0) { + for (auto const& m : matches1) { + auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tX)) { + continue; + } + if (tX.v0Status() != t1.v0Status()) + continue; + if (!checkKinematics(t1, tX)) + continue; + if (tX.globalIndex() == t1.globalIndex()) + continue; + if (tX.globalIndex() == t2.globalIndex()) + continue; + if (hasSharedDaughters(tX, t1)) + continue; + if (hasSharedDaughters(tX, t2)) + continue; + fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, + ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), + wSELeg1); + // Fixed leg for leg1 replacement is original t2. + // Fill only for successful replacement. + fillFixedLegControlMap(tX.v0Status(), t2.v0Status(), 1, false, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + wSELeg1); + auto proton = ROOT::Math::PtEtaPhiMVector(tX.protonPt(), tX.protonEta(), tX.protonPhi(), o2::constants::physics::MassProton); + auto lambda = ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()); + auto proton2 = ROOT::Math::PtEtaPhiMVector(t2.protonPt(), t2.protonEta(), t2.protonPhi(), o2::constants::physics::MassProton); + auto lambda2 = ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()); + + // const int ptype = pairTypeCode(tX.v0Status(), t2.v0Status()); + + const float meWeight = finalMixWeightLeg1; + const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); + if ((tX.v0Status() == 0 && t2.v0Status() == 1) || (tX.v0Status() == 1 && t2.v0Status() == 0)) + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + const int s1 = tX.v0Status(); + const int s2 = t2.v0Status(); + + if (s1 == 0 && s2 == 1) { + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); + } else if (s1 == 1 && s2 == 0) { + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 2, 1); + } else { + fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 1, 1); + } + } + } + + if (doMixLeg2 && nFill2 > 0) { + for (auto const& m : matches2) { + auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(tY)) { + continue; + } + if (tY.v0Status() != t2.v0Status()) + continue; + if (!checkKinematics(t2, tY)) + continue; + if (tY.globalIndex() == t2.globalIndex()) + continue; + if (tY.globalIndex() == t1.globalIndex()) + continue; + if (hasSharedDaughters(tY, t1)) + continue; + if (hasSharedDaughters(tY, t2)) + continue; + fillReplacementControlMap(t1.v0Status(), tY.v0Status(), 2, false, + ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()), + wSELeg2); + // Fixed leg for leg2 replacement is original t1. + // Fill only for successful replacement. + fillFixedLegControlMap(t1.v0Status(), tY.v0Status(), 2, false, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + wSELeg2); + auto proton = ROOT::Math::PtEtaPhiMVector(t1.protonPt(), t1.protonEta(), t1.protonPhi(), o2::constants::physics::MassProton); + auto lambda = ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()); + auto proton2 = ROOT::Math::PtEtaPhiMVector(tY.protonPt(), tY.protonEta(), tY.protonPhi(), o2::constants::physics::MassProton); + auto lambda2 = ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()); + + // const int ptype = pairTypeCode(t1.v0Status(), tY.v0Status()); + const float meWeight = finalMixWeightLeg2; + const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + const int s1 = t1.v0Status(); + const int s2 = tY.v0Status(); + if (s1 == 0 && s2 == 1) { + fillHistograms(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); + } else if (s1 == 1 && s2 == 0) { + fillHistograms(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, 1, 2); + } else { + fillHistograms(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, 2, 2); + } + } + } + } } - - // 3 regions: SB low, signal, SB high - if (edges.size() == 4) { - if (m >= edges[0] && m < edges[1]) - return 0; // low sideband - if (m >= edges[1] && m < edges[2]) - return 1; // signal - if (m >= edges[2] && m < edges[3]) - return 0; // high sideband merged with low sideband - return -1; + if (cfgV6LogPending) { + LOGF(info, "MEV6 data pending branches: carriedIn=%zu matched=%zu expired=%zu newlyPropagated=%zu carriedToNext=%zu", + pendingAtStart, pendingMatched, pendingExpired, pendingAdded, v6Pending.data.size()); } - - return -1; - } - static inline uint64_t splitmix64(uint64_t x) - { - // simple deterministic hash for reproducible shuffling - x += 0x9e3779b97f4a7c15ULL; - x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; - x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; - return x ^ (x >> 31); } + PROCESS_SWITCH(lambdaspincorrderived, processMEV6, "Process data ME v6 with radius buffer", false); - void processMEV6(EventCandidates const& collisions, AllTrackCandidates const& V0s) + void processMEV6Sys(EventCandidates const& collisions, AllTrackCandidates const& V0s) { MixBinnerR mb{ ptMin.value, ptMax.value, ptMix.value, - v0etaMixBuffer.value, + v0Configurations.v0etaMixBuffer.value, etaMix.value, phiMix.value, MassMin.value, @@ -1434,9 +3460,16 @@ struct lambdaspincorrderived { auto slice = V0s.sliceBy(tracksPerCollisionV0, col.index()); for (auto const& t : slice) { - if (!selectionV0(t)) { - continue; + + bool passedAny = false; + for (int sysId = 0; sysId < nSysTotal; ++sysId) { + if (selectionV0Sys(t, sysCuts[sysId])) { + passedAny = true; + break; + } } + if (!passedAny) + continue; const int status = static_cast(t.v0Status()); if (status < 0 || status >= nStat) { @@ -1509,9 +3542,9 @@ struct lambdaspincorrderived { auto collectFromBins = [&](const std::vector& ptUseBins, const std::vector& etaUseBins, const std::vector& phiUseBins) { - for (int ptUse : ptUseBins) { - for (int etaUse : etaUseBins) { - for (int phiUse : phiUseBins) { + for (const auto& ptUse : ptUseBins) { + for (const auto& etaUse : etaUseBins) { + for (const auto& phiUse : phiUseBins) { const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, nStat, nPt, nEta, nPhi, nM, nR)]; @@ -1521,8 +3554,16 @@ struct lambdaspincorrderived { auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); - if (!selectionV0(tX)) + bool tXPassed = false; + for (int sysId = 0; sysId < nSysTotal; ++sysId) { + if (selectionV0Sys(tX, sysCuts[sysId])) { + tXPassed = true; + break; + } + } + if (!tXPassed) continue; + if (!checkKinematics(tRep, tX)) continue; @@ -1565,9 +3606,9 @@ struct lambdaspincorrderived { collectNeighborBinsClamp(etaB, nEta, nN_eta, etaBinsN); collectNeighborBinsPhi(phiB, nPhi, nN_phi, phiBinsN); - for (int ptUse : ptBinsN) { - for (int etaUse : etaBinsN) { - for (int phiUse : phiBinsN) { + for (const auto& ptUse : ptBinsN) { + for (const auto& etaUse : etaBinsN) { + for (const auto& phiUse : phiBinsN) { if (ptUse == ptB && etaUse == etaB && phiUse == phiB) continue; @@ -1580,8 +3621,16 @@ struct lambdaspincorrderived { auto tX = V0s.iteratorAt(static_cast(bc.rowIndex)); - if (!selectionV0(tX)) + bool tXPassed = false; + for (int sysId = 0; sysId < nSysTotal; ++sysId) { + if (selectionV0Sys(tX, sysCuts[sysId])) { + tXPassed = true; + break; + } + } + if (!tXPassed) continue; + if (!checkKinematics(tRep, tX)) continue; @@ -1626,6 +3675,68 @@ struct lambdaspincorrderived { } }; + const size_t pendingAtStart = v6Pending.data.size(); + size_t pendingMatched = 0; + size_t pendingExpired = 0; + size_t pendingAdded = 0; + if (!cfgV6CarryUnmatched) { + v6Pending.data.clear(); + } else { + for (auto it = v6Pending.data.begin(); it != v6Pending.data.end();) { + auto& pending = *it; + ++pending.age; + if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { + ++pendingExpired; + it = v6Pending.data.erase(it); + continue; + } + + auto& matches = pending.replacedLeg == 1 ? matches1 : matches2; + collectMatchesForReplacedLeg(pending.target, pending.fixed, pending.colBin, -1, matches); + limitMatchesToNEvents(matches, nEvtMixing.value); + downsampleMatches(matches, pending.seed ^ splitmix64(static_cast(pending.age))); + + int nAccepted = 0; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + ++nAccepted; + } + + if (nAccepted == 0) { + ++it; + continue; + } + + const float controlWeight = 1.0f / static_cast(nAccepted); + const float branchNorm = cfgMixLegMode.value == 2 ? 0.5f : 1.0f; + const float mixWeight = branchNorm * controlWeight; + for (auto const& m : matches) { + auto replacement = V0s.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0(replacement) || !checkKinematics(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughters(replacement, pending.target) || hasSharedDaughters(replacement, pending.fixed)) { + continue; + } + fillV6MixedBranch(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + } + ++pendingMatched; + it = v6Pending.data.erase(it); + } + } + // -------- PASS 2: configurable one-leg / two-leg mixing -------- for (auto const& col1 : collisions) { const int colBin = colBinning.getBin(std::make_tuple(col1.posz(), col1.cent())); @@ -1637,9 +3748,16 @@ struct lambdaspincorrderived { auto poolA = V0s.sliceBy(tracksPerCollisionV0, col1.index()); for (auto const& [t1, t2] : soa::combinations(o2::soa::CombinationsFullIndexPolicy(poolA, poolA))) { - if (!selectionV0(t1) || !selectionV0(t2)) { - continue; + + std::vector commonSys; + for (int sysId = 0; sysId < nSysTotal; ++sysId) { + if (selectionV0Sys(t1, sysCuts[sysId]) && selectionV0Sys(t2, sysCuts[sysId])) { + commonSys.push_back(sysId); + } } + if (commonSys.empty()) + continue; + if (t2.index() <= t1.index()) { continue; } @@ -1648,6 +3766,24 @@ struct lambdaspincorrderived { const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); + // Fill TGT maps before searching for replacements. + if (doMixLeg1) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 1, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + } + if (doMixLeg2) { + fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + 1.0f); + fillFixedLegControlMap(t1.v0Status(), t2.v0Status(), 2, true, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + 1.0f); + } + if (doMixLeg1) { collectMatchesForReplacedLeg(t1, t2, colBin, curColIdx, matches1); limitMatchesToNEvents(matches1, nEvtMixing.value); @@ -1664,25 +3800,24 @@ struct lambdaspincorrderived { matches2.clear(); } - // --- fill target distributions for the original SE leg that is to be replaced - if (doMixLeg1) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 1, true, - ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), - 1.0f); - } - - if (doMixLeg2) { - fillReplacementControlMap(t1.v0Status(), t2.v0Status(), 2, true, - ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), - 1.0f); - } int nFill1 = 0; int nFill2 = 0; + // count actual accepted fills for leg-1 replacement if (doMixLeg1) { for (auto const& m : matches1) { auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(tX)) + + bool hasCommon = false; + for (const auto& sysId : commonSys) { + if (selectionV0Sys(tX, sysCuts[sysId])) { + hasCommon = true; + break; + } + } + if (!hasCommon) + continue; + if (tX.v0Status() != t1.v0Status()) continue; if (!checkKinematics(t1, tX)) continue; @@ -1698,13 +3833,21 @@ struct lambdaspincorrderived { } } - // count actual accepted fills for leg-2 replacement if (doMixLeg2) { for (auto const& m : matches2) { auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!checkKinematics(t2, tY)) + bool hasCommon = false; + for (const auto& sysId : commonSys) { + if (selectionV0Sys(tY, sysCuts[sysId])) { + hasCommon = true; + break; + } + } + if (!hasCommon) continue; - if (!selectionV0(tY)) + if (tY.v0Status() != t2.v0Status()) + continue; + if (!checkKinematics(t2, tY)) continue; if (tY.globalIndex() == t2.globalIndex()) continue; @@ -1718,18 +3861,45 @@ struct lambdaspincorrderived { } } + if (cfgV6CarryUnmatched) { + const auto hasPendingSpace = [&]() { + return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.data.size()) < cfgV6MaxPendingBranches.value; + }; + if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t1, curColIdx), storeV6Candidate(t2, curColIdx), colBin, 1, 0, + static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { + v6Pending.data.push_back({storeV6Candidate(t2, curColIdx), storeV6Candidate(t1, curColIdx), colBin, 2, 0, + static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAdded; + } + } + if (nFill1 <= 0 && nFill2 <= 0) { continue; } - const int nUse = nFill1 + nFill2; - const float wSE = (nUse > 0) ? 1.0f / static_cast(nUse) : 0.0f; + + const float wSELeg1 = (nFill1 > 0) ? 1.0f / static_cast(nFill1) : 0.0f; + const float wSELeg2 = (nFill2 > 0) ? 1.0f / static_cast(nFill2) : 0.0f; + + const int nActiveMixBranches = ((doMixLeg1 && nFill1 > 0) ? 1 : 0) + + ((doMixLeg2 && nFill2 > 0) ? 1 : 0); + float branchNorm = 1.0f; + if (cfgMixLegMode.value == 2) { + branchNorm = cfgV6CarryUnmatched ? 0.5f : 1.0f / static_cast(nActiveMixBranches); + } + const float finalMixWeightLeg1 = branchNorm * wSELeg1; + const float finalMixWeightLeg2 = branchNorm * wSELeg2; if (doMixLeg1 && nFill1 > 0) { for (auto const& m : matches1) { auto tX = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(tX)) { + if (!selectionV0(tX)) + continue; + if (tX.v0Status() != t1.v0Status()) continue; - } if (!checkKinematics(t1, tX)) continue; if (tX.globalIndex() == t1.globalIndex()) @@ -1740,29 +3910,49 @@ struct lambdaspincorrderived { continue; if (hasSharedDaughters(tX, t2)) continue; + fillReplacementControlMap(tX.v0Status(), t2.v0Status(), 1, false, ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()), - wSE); + wSELeg1); + fillFixedLegControlMap(tX.v0Status(), t2.v0Status(), 1, false, + ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()), + wSELeg1); + auto proton = ROOT::Math::PtEtaPhiMVector(tX.protonPt(), tX.protonEta(), tX.protonPhi(), o2::constants::physics::MassProton); auto lambda = ROOT::Math::PtEtaPhiMVector(tX.lambdaPt(), tX.lambdaEta(), tX.lambdaPhi(), tX.lambdaMass()); auto proton2 = ROOT::Math::PtEtaPhiMVector(t2.protonPt(), t2.protonEta(), t2.protonPhi(), o2::constants::physics::MassProton); auto lambda2 = ROOT::Math::PtEtaPhiMVector(t2.lambdaPt(), t2.lambdaEta(), t2.lambdaPhi(), t2.lambdaMass()); - // const int ptype = pairTypeCode(tX.v0Status(), t2.v0Status()); - - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg1; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); - fillHistograms(tX.v0Status(), t2.v0Status(), lambda, lambda2, proton, proton2, 1, meWeight, 1); + if ((tX.v0Status() == 0 && t2.v0Status() == 1) || (tX.v0Status() == 1 && t2.v0Status() == 0)) + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + + const int s1 = tX.v0Status(); + const int s2 = t2.v0Status(); + + for (const auto& sysId : commonSys) { + if (selectionV0Sys(tX, sysCuts[sysId])) { + if (s1 == 0 && s2 == 1) { + fillHistogramsSys(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, sysId, 1, 1); + } else if (s1 == 1 && s2 == 0) { + fillHistogramsSys(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, sysId, 2, 1); + } else { + fillHistogramsSys(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, sysId, 1, 1); + } + } + } } } if (doMixLeg2 && nFill2 > 0) { for (auto const& m : matches2) { auto tY = V0s.iteratorAt(static_cast(m.rowIndex)); - if (!selectionV0(tY)) { + if (!selectionV0(tY)) + continue; + if (tY.v0Status() != t2.v0Status()) continue; - } if (!checkKinematics(t2, tY)) continue; if (tY.globalIndex() == t2.globalIndex()) @@ -1773,32 +3963,56 @@ struct lambdaspincorrderived { continue; if (hasSharedDaughters(tY, t2)) continue; + fillReplacementControlMap(t1.v0Status(), tY.v0Status(), 2, false, ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()), - wSE); + wSELeg2); + fillFixedLegControlMap(t1.v0Status(), tY.v0Status(), 2, false, + ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()), + wSELeg2); + auto proton = ROOT::Math::PtEtaPhiMVector(t1.protonPt(), t1.protonEta(), t1.protonPhi(), o2::constants::physics::MassProton); auto lambda = ROOT::Math::PtEtaPhiMVector(t1.lambdaPt(), t1.lambdaEta(), t1.lambdaPhi(), t1.lambdaMass()); auto proton2 = ROOT::Math::PtEtaPhiMVector(tY.protonPt(), tY.protonEta(), tY.protonPhi(), o2::constants::physics::MassProton); auto lambda2 = ROOT::Math::PtEtaPhiMVector(tY.lambdaPt(), tY.lambdaEta(), tY.lambdaPhi(), tY.lambdaMass()); - // const int ptype = pairTypeCode(t1.v0Status(), tY.v0Status()); - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg2; const float dPhi = deltaPhiMinusPiToPi((float)lambda.Phi(), (float)lambda2.Phi()); - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); - fillHistograms(t1.v0Status(), tY.v0Status(), lambda, lambda2, proton, proton2, 1, meWeight, 2); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + + const int s1 = t1.v0Status(); + const int s2 = tY.v0Status(); + + for (const auto& sysId : commonSys) { + if (selectionV0Sys(tY, sysCuts[sysId])) { + if (s1 == 0 && s2 == 1) { + fillHistogramsSys(0, 1, lambda, lambda2, proton, proton2, 1, meWeight, sysId, 2, 2); + } else if (s1 == 1 && s2 == 0) { + fillHistogramsSys(0, 1, lambda2, lambda, proton2, proton, 1, meWeight, sysId, 1, 2); + } else { + fillHistogramsSys(s1, s2, lambda, lambda2, proton, proton2, 1, meWeight, sysId, 2, 2); + } + } + } } } } } + if (cfgV6LogPending) { + LOGF(info, "MEV6 data pending branches: carriedIn=%zu matched=%zu expired=%zu newlyPropagated=%zu carriedToNext=%zu", + pendingAtStart, pendingMatched, pendingExpired, pendingAdded, v6Pending.data.size()); + } } - PROCESS_SWITCH(lambdaspincorrderived, processMEV6, "Process data ME v6 with radius buffer", false); + PROCESS_SWITCH(lambdaspincorrderived, processMEV6Sys, "Process data ME v6 Sys analysis", false); + void processMCMEV6(EventCandidatesMC const& collisions, AllTrackCandidatesMC const& V0sMC) { MixBinnerR mb{ ptMin.value, ptMax.value, ptMix.value, - v0etaMixBuffer.value, + v0Configurations.v0etaMixBuffer.value, etaMix.value, phiMix.value, MassMin.value, @@ -1902,9 +4116,9 @@ struct lambdaspincorrderived { auto collectFromBins = [&](const std::vector& ptUseBins, const std::vector& etaUseBins, const std::vector& phiUseBins) { - for (int ptUse : ptUseBins) { - for (int etaUse : etaUseBins) { - for (int phiUse : phiUseBins) { + for (const auto& ptUse : ptUseBins) { + for (const auto& etaUse : etaUseBins) { + for (const auto& phiUse : phiUseBins) { const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, nStat, nPt, nEta, nPhi, nM, nR)]; @@ -1948,26 +4162,59 @@ struct lambdaspincorrderived { collectFromBins(ptBins, etaBins, phiBins); - // 2) if exact bin does not give enough, top up from neighbors + // 2) if exact bin gives fewer than required matches, also search neighbors const int targetMatches = (cfgV5MaxMatches.value > 0) ? cfgV5MaxMatches.value : 1; if ((int)matches.size() < targetMatches) { - collectNeighborBinsClamp(ptB, nPt, nN_pt, ptBins); - collectNeighborBinsClamp(etaB, nEta, nN_eta, etaBins); - collectNeighborBinsPhi(phiB, nPhi, nN_phi, phiBins); + std::vector ptBinsN, etaBinsN, phiBinsN; + collectNeighborBinsClamp(ptB, nPt, nN_pt, ptBinsN); + collectNeighborBinsClamp(etaB, nEta, nN_eta, etaBinsN); + collectNeighborBinsPhi(phiB, nPhi, nN_phi, phiBinsN); - collectFromBins(ptBins, etaBins, phiBins); - } + for (const auto& ptUse : ptBinsN) { + for (const auto& etaUse : etaBinsN) { + for (const auto& phiUse : phiBinsN) { + if (ptUse == ptB && etaUse == etaB && phiUse == phiB) { + continue; + } - // if nothing found, then try neighboring bins - if (matches.empty()) { - collectNeighborBinsClamp(ptB, nPt, nN_pt, ptBins); - collectNeighborBinsClamp(etaB, nEta, nN_eta, etaBins); - collectNeighborBinsPhi(phiB, nPhi, nN_phi, phiBins); + const auto& vec = buffer[linearKeyR(colBin, status, ptUse, etaUse, phiUse, mB, rB, + nStat, nPt, nEta, nPhi, nM, nR)]; - collectFromBins(ptBins, etaBins, phiBins); - } + for (auto const& bc : vec) { + if (bc.collisionIdx == curColIdx) { + continue; + } + + auto tX = V0sMC.iteratorAt(static_cast(bc.rowIndex)); + + if (!selectionV0MC(tX)) { + continue; + } + if (!checkKinematicsMC(tRep, tX)) { + continue; + } + + if (tX.globalIndex() == tRep.globalIndex()) { + continue; + } + if (tX.globalIndex() == tKeep.globalIndex()) { + continue; + } + + if (hasSharedDaughtersMC(tX, tKeep)) { + continue; + } + if (hasSharedDaughtersMC(tX, tRep)) { + continue; + } + matches.push_back(MatchRef{bc.collisionIdx, bc.rowIndex}); + } + } + } + } + } std::sort(matches.begin(), matches.end(), [](auto const& a, auto const& b) { return std::tie(a.collisionIdx, a.rowIndex) < std::tie(b.collisionIdx, b.rowIndex); @@ -1992,6 +4239,68 @@ struct lambdaspincorrderived { } }; + const size_t pendingAtStartMC = v6Pending.mc.size(); + size_t pendingMatchedMC = 0; + size_t pendingExpiredMC = 0; + size_t pendingAddedMC = 0; + if (!cfgV6CarryUnmatched) { + v6Pending.mc.clear(); + } else { + for (auto it = v6Pending.mc.begin(); it != v6Pending.mc.end();) { + auto& pending = *it; + ++pending.age; + if (cfgV6MaxPendingAge.value > 0 && pending.age > cfgV6MaxPendingAge.value) { + ++pendingExpiredMC; + it = v6Pending.mc.erase(it); + continue; + } + + auto& matches = pending.replacedLeg == 1 ? matches1 : matches2; + collectMatchesForReplacedLeg(pending.target, pending.fixed, pending.colBin, -1, matches); + limitMatchesToNEvents(matches, nEvtMixing.value); + downsampleMatches(matches, pending.seed ^ splitmix64(static_cast(pending.age))); + + int nAccepted = 0; + for (auto const& m : matches) { + auto replacement = V0sMC.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0MC(replacement) || !checkKinematicsMC(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughtersMC(replacement, pending.target) || hasSharedDaughtersMC(replacement, pending.fixed)) { + continue; + } + ++nAccepted; + } + + if (nAccepted == 0) { + ++it; + continue; + } + + const float controlWeight = 1.0f / static_cast(nAccepted); + const float branchNorm = cfgMixLegMode.value == 2 ? 0.5f : 1.0f; + const float mixWeight = branchNorm * controlWeight; + for (auto const& m : matches) { + auto replacement = V0sMC.iteratorAt(static_cast(m.rowIndex)); + if (!selectionV0MC(replacement) || !checkKinematicsMC(pending.target, replacement)) { + continue; + } + if (replacement.globalIndex() == pending.target.globalIndex() || replacement.globalIndex() == pending.fixed.globalIndex()) { + continue; + } + if (hasSharedDaughtersMC(replacement, pending.target) || hasSharedDaughtersMC(replacement, pending.fixed)) { + continue; + } + fillV6MixedBranchMC(replacement, pending.fixed, pending.replacedLeg, controlWeight, mixWeight); + } + ++pendingMatchedMC; + it = v6Pending.mc.erase(it); + } + } + // -------- PASS 2: configurable one-leg / two-leg mixing -------- for (auto const& col1 : collisions) { const int colBin = colBinning.getBin(std::make_tuple(mcacc::posz(col1), mcacc::cent(col1))); @@ -2014,6 +4323,25 @@ struct lambdaspincorrderived { const bool doMixLeg1 = (cfgMixLegMode.value == 0 || cfgMixLegMode.value == 2); const bool doMixLeg2 = (cfgMixLegMode.value == 1 || cfgMixLegMode.value == 2); + // Fill MC TGT maps before searching for replacements, same definition as data: + // TGT is the selected same-event target phase space, independent of replacement success. + if (doMixLeg1) { + fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 1, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), mcacc::lamMass(t1)), + 1.0f); + fillFixedLegControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 1, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), mcacc::lamEta(t2), mcacc::lamPhi(t2), mcacc::lamMass(t2)), + 1.0f); + } + if (doMixLeg2) { + fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 2, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), mcacc::lamEta(t2), mcacc::lamPhi(t2), mcacc::lamMass(t2)), + 1.0f); + fillFixedLegControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 2, true, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), mcacc::lamMass(t1)), + 1.0f); + } + if (doMixLeg1) { collectMatchesForReplacedLeg(t1, t2, colBin, curColIdx, matches1); limitMatchesToNEvents(matches1, nEvtMixing.value); @@ -2028,18 +4356,9 @@ struct lambdaspincorrderived { } else { matches2.clear(); } - if (doMixLeg1) { - fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 1, true, - ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), mcacc::lamMass(t1)), - 1.0f); - } - - if (doMixLeg2) { - fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(t2), 2, true, - ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), mcacc::lamEta(t2), mcacc::lamPhi(t2), mcacc::lamMass(t2)), - 1.0f); - } + // Do not fill TGT here. TGT has already been filled above as the + // selected same-event target phase space, independent of replacement success. int nFill1 = 0; int nFill2 = 0; @@ -2050,6 +4369,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tX)) continue; + if (mcacc::v0Status(tX) != mcacc::v0Status(t1)) + continue; if (!checkKinematicsMC(t1, tX)) continue; if (tX.globalIndex() == t1.globalIndex()) @@ -2071,6 +4392,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tY)) continue; + if (mcacc::v0Status(tY) != mcacc::v0Status(t2)) + continue; if (!checkKinematicsMC(t2, tY)) continue; if (tY.globalIndex() == t2.globalIndex()) @@ -2085,11 +4408,37 @@ struct lambdaspincorrderived { } } + if (cfgV6CarryUnmatched) { + const auto hasPendingSpace = [&]() { + return cfgV6MaxPendingBranches.value <= 0 || static_cast(v6Pending.mc.size()) < cfgV6MaxPendingBranches.value; + }; + if (doMixLeg1 && nFill1 == 0 && hasPendingSpace()) { + v6Pending.mc.push_back({storeV6CandidateMC(t1), storeV6CandidateMC(t2), colBin, 1, 0, + static_cast(t1.globalIndex()) ^ splitmix64(static_cast(t2.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAddedMC; + } + if (doMixLeg2 && nFill2 == 0 && hasPendingSpace()) { + v6Pending.mc.push_back({storeV6CandidateMC(t2), storeV6CandidateMC(t1), colBin, 2, 0, + static_cast(t2.globalIndex()) ^ splitmix64(static_cast(t1.globalIndex())) ^ splitmix64(static_cast(curColIdx))}); + ++pendingAddedMC; + } + } + if (nFill1 <= 0 && nFill2 <= 0) { continue; } - const int nUse = nFill1 + nFill2; - const float wSE = (nUse > 0) ? 1.0f / static_cast(nUse) : 0.0f; + // Residual-weight QA needs the same leg-specific REP normalization in MC. + const float wSELeg1 = (nFill1 > 0) ? 1.0f / static_cast(nFill1) : 0.0f; + const float wSELeg2 = (nFill2 > 0) ? 1.0f / static_cast(nFill2) : 0.0f; + + const int nActiveMixBranches = ((doMixLeg1 && nFill1 > 0) ? 1 : 0) + + ((doMixLeg2 && nFill2 > 0) ? 1 : 0); + float branchNorm = 1.0f; + if (cfgMixLegMode.value == 2) { + branchNorm = cfgV6CarryUnmatched ? 0.5f : 1.0f / static_cast(nActiveMixBranches); + } + const float finalMixWeightLeg1 = branchNorm * wSELeg1; + const float finalMixWeightLeg2 = branchNorm * wSELeg2; if (doMixLeg1 && nFill1 > 0) { for (auto const& m : matches1) { @@ -2097,6 +4446,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tX)) continue; + if (mcacc::v0Status(tX) != mcacc::v0Status(t1)) + continue; if (!checkKinematicsMC(t1, tX)) continue; if (tX.globalIndex() == t1.globalIndex()) @@ -2109,8 +4460,15 @@ struct lambdaspincorrderived { continue; fillReplacementControlMap(mcacc::v0Status(tX), mcacc::v0Status(t2), 1, false, ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tX), mcacc::lamEta(tX), mcacc::lamPhi(tX), mcacc::lamMass(tX)), - wSE); - + wSELeg1); + // Fixed leg for MC leg1 replacement is original t2. + // Fill only for successful replacement. + fillFixedLegControlMap(mcacc::v0Status(tX), mcacc::v0Status(t2), 1, false, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t2), + mcacc::lamEta(t2), + mcacc::lamPhi(t2), + mcacc::lamMass(t2)), + wSELeg1); auto pX = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(tX), mcacc::prEta(tX), mcacc::prPhi(tX), o2::constants::physics::MassProton); auto lX = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tX), mcacc::lamEta(tX), mcacc::lamPhi(tX), @@ -2121,12 +4479,20 @@ struct lambdaspincorrderived { mcacc::lamMass(t2)); // const int ptype = pairTypeCode(mcacc::v0Status(tX), mcacc::v0Status(t2)); - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg1; const float dPhi = deltaPhiMinusPiToPi((float)lX.Phi(), (float)l2.Phi()); - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); - fillHistograms(mcacc::v0Status(tX), mcacc::v0Status(t2), - lX, l2, pX, p2, - 1, meWeight, 1); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + const int s1 = mcacc::v0Status(tX); + const int s2 = mcacc::v0Status(t2); + + if (s1 == 0 && s2 == 1) { + fillHistograms(0, 1, lX, l2, pX, p2, 1, meWeight, 1, 1); + } else if (s1 == 1 && s2 == 0) { + fillHistograms(0, 1, l2, lX, p2, pX, 1, meWeight, 2, 1); + } else { + fillHistograms(s1, s2, lX, l2, pX, p2, 1, meWeight, 1, 1); + } } } if (doMixLeg2 && nFill2 > 0) { @@ -2135,6 +4501,8 @@ struct lambdaspincorrderived { if (!selectionV0MC(tY)) continue; + if (mcacc::v0Status(tY) != mcacc::v0Status(t2)) + continue; if (!checkKinematicsMC(t2, tY)) continue; if (tY.globalIndex() == t2.globalIndex()) @@ -2147,8 +4515,15 @@ struct lambdaspincorrderived { continue; fillReplacementControlMap(mcacc::v0Status(t1), mcacc::v0Status(tY), 2, false, ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(tY), mcacc::lamEta(tY), mcacc::lamPhi(tY), mcacc::lamMass(tY)), - wSE); - + wSELeg2); + // Fixed leg for MC leg2 replacement is original t1. + // Fill only for successful replacement. + fillFixedLegControlMap(mcacc::v0Status(t1), mcacc::v0Status(tY), 2, false, + ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), + mcacc::lamEta(t1), + mcacc::lamPhi(t1), + mcacc::lamMass(t1)), + wSELeg2); auto p1 = ROOT::Math::PtEtaPhiMVector(mcacc::prPt(t1), mcacc::prEta(t1), mcacc::prPhi(t1), o2::constants::physics::MassProton); auto l1 = ROOT::Math::PtEtaPhiMVector(mcacc::lamPt(t1), mcacc::lamEta(t1), mcacc::lamPhi(t1), @@ -2159,18 +4534,39 @@ struct lambdaspincorrderived { mcacc::lamMass(tY)); // const int ptype = pairTypeCode(mcacc::v0Status(t1), mcacc::v0Status(tY)); - const float meWeight = wSE; + const float meWeight = finalMixWeightLeg2; const float dPhi = deltaPhiMinusPiToPi((float)l1.Phi(), (float)lY.Phi()); - histos.fill(HIST("deltaPhiMix"), dPhi, wSE); - fillHistograms(mcacc::v0Status(t1), mcacc::v0Status(tY), - l1, lY, p1, pY, - 1, meWeight, 2); + if (fillHistConfig.fillBasicQAHistos) + histos.fill(HIST("deltaPhiMix"), dPhi, meWeight); + const int s1 = mcacc::v0Status(t1); + const int s2 = mcacc::v0Status(tY); + + if (s1 == 0 && s2 == 1) { + fillHistograms(0, 1, l1, lY, p1, pY, 1, meWeight, 2, 2); + } else if (s1 == 1 && s2 == 0) { + fillHistograms(0, 1, lY, l1, pY, p1, 1, meWeight, 1, 2); + } else { + fillHistograms(s1, s2, l1, lY, p1, pY, 1, meWeight, 2, 2); + } } } } } + if (cfgV6LogPending) { + LOGF(info, "MEV6 MC pending branches: carriedIn=%zu matched=%zu expired=%zu newlyPropagated=%zu carriedToNext=%zu", + pendingAtStartMC, pendingMatchedMC, pendingExpiredMC, pendingAddedMC, v6Pending.mc.size()); + } } PROCESS_SWITCH(lambdaspincorrderived, processMCMEV6, "Process MC ME v6 with radius buffer", false); + + void endOfStream(EndOfStreamContext&) + { + if (cfgV6LogPending) { + LOGF(info, "MEV6 end of stream: unmatchedData=%zu unmatchedMC=%zu", v6Pending.data.size(), v6Pending.mc.size()); + } + v6Pending.data.clear(); + v6Pending.mc.clear(); + } }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { diff --git a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx index 79c74df4307..3e0a451e21b 100644 --- a/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx +++ b/PWGLF/Tasks/Strangeness/nonPromptCascade.cxx @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -50,7 +51,6 @@ #include #include #include -#include #include @@ -111,6 +111,8 @@ struct NPCascCandidate { float pionDCAz; float bachDCAxy; float bachDCAz; + float cascDCADaughters; + float v0DCADaughters; float casccosPA; float v0cosPA; double massXi; @@ -195,6 +197,7 @@ struct NonPromptCascadeTask { // Produces NPCollsTable; Produces NPMCNTable; + Produces NPMCRecoCollAssocTable; Produces NPRecoCandTable; using TracksExtData = soa::Join; @@ -203,6 +206,7 @@ struct NonPromptCascadeTask { using CollisionCandidatesRun3MC = soa::Join; using TracksWithLabel = soa::Join; using TracksWithSel = soa::Join; + using TracksWithLabelSel = soa::Join; Preslice perCollision = aod::track::collisionId; Preslice perCollisionMC = aod::track::collisionId; @@ -232,6 +236,7 @@ struct NonPromptCascadeTask { Configurable cfgMaxMult{"cfgMaxMult", 8000.f, "Upper range of multiplicty histo"}; Configurable cfgMaxMultFV0{"cfgMaxMultFV0", 10000.f, "Upper range of multiplicty FV0 histo"}; Configurable cfgPtEdgesdNdeta{"ptEdges", "0,0.2,0.4,0.6,0.8,1,1.2,1.6,2.0,2.4,2.8,3.2,3.6,4,4.5,5,5.5,6,7,8,10", "Pt bin edges (comma-separated)"}; + Configurable cfgDownscaleCascTF{"cfgDownscaleCascTF", 1, "Keep one in every N input timeframes for cascade reco and generator output (1 keeps all)"}; Configurable cfgDownscaleMB{"cfgDownscaleMB", 1, "Downscaling for pile up study sample"}; Configurable cfgEtaCutdNdeta{"cfgEtaCutdNdeta", 0.8, "Eta cut for charged tracks"}; @@ -240,6 +245,8 @@ struct NonPromptCascadeTask { SliceCache cache; Service mCCDB; + uint64_t mTimeframeCount = 0; + bool mKeepTimeframe = true; int mRunNumber = 0; float mBz = 0.f; o2::vertexing::DCAFitterN<2> mDCAFitter; @@ -271,8 +278,21 @@ struct NonPromptCascadeTask { } } + // The framework calls run once before all enabled process functions for a timeframe. + void run(ProcessingContext&) + { + mKeepTimeframe = (mTimeframeCount++ % static_cast(cfgDownscaleCascTF.value)) == 0; + mRegistry.fill(HIST("hTimeframeSelection"), mKeepTimeframe ? 1.f : 0.f); + } + void init(InitContext& context) { + if (cfgDownscaleCascTF.value < 1) { + LOG(fatal) << "cfgDownscaleCascTF must be at least 1"; + } + auto timeframeSelection = mRegistry.add("hTimeframeSelection", ";Timeframe selection;Timeframes", HistType::kTH1D, {{2, -0.5, 1.5}}); + timeframeSelection->GetXaxis()->SetBinLabel(1, "Skipped"); + timeframeSelection->GetXaxis()->SetBinLabel(2, "Kept"); mZorroSummary.setObject(mZorro.getZorroSummary()); mCCDB->setURL(ccdbUrl); mCCDB->setFatalWhenNull(true); @@ -305,7 +325,6 @@ struct NonPromptCascadeTask { AxisSpec nTracksAxis = {100, 0., 100., "NTracksGlobal"}; AxisSpec nTracksAxisMC = {100, 0., 100., "NTracksMC"}; std::vector centBinning; - std::vector multBinning; std::vector trackBinning; std::vector runsBinning; double cent = -0.0; @@ -529,11 +548,14 @@ struct NonPromptCascadeTask { o2::math_utils::SVector cascadePos, v0Pos; float cascCpa = -1, v0Cpa = -1; + float cascDCADaughters = -1.f, v0DCADaughters = -1.f; o2::track::TrackParCov ntCascadeTrack; if (mDCAFitter.process(pionTrkParCov, protonTrkParCov)) { + v0DCADaughters = std::sqrt(mDCAFitter.getChi2AtPCACandidate()); auto trackParCovV0 = mDCAFitter.createParentTrackParCov(0); // V0 track retrieved from p and pi daughters v0Pos = mDCAFitter.getPCACandidate(); if (mDCAFitter.process(trackParCovV0, bachTrkParCov)) { + cascDCADaughters = std::sqrt(mDCAFitter.getChi2AtPCACandidate()); mDCAFitter.getTrackParamAtPCA(0).getPxPyPzGlo(momenta[0]); mDCAFitter.getTrackParamAtPCA(1).getPxPyPzGlo(momenta[1]); ntCascadeTrack = mDCAFitter.createParentTrackParCov(); @@ -655,7 +677,7 @@ struct NonPromptCascadeTask { cascadeLvector.pt(), cascadeLvector.eta(), cascadeLvector.phi(), protonTrack.pt(), protonTrack.eta(), pionTrack.pt(), pionTrack.eta(), bachelor.pt(), bachelor.eta(), motherDCA.getY(), motherDCA.getZ(), protonDCA.getY(), protonDCA.getZ(), pionDCA.getY(), pionDCA.getZ(), bachDCA.getY(), bachDCA.getZ(), - cascCpa, v0Cpa, massXi, massOmega, v0mass, + cascDCADaughters, v0DCADaughters, cascCpa, v0Cpa, massXi, massOmega, v0mass, static_cast(std::hypot(cascadePos[0], cascadePos[1])), static_cast(std::hypot(v0Pos[0], v0Pos[1])), static_cast(std::hypot(cascadePos[0], cascadePos[1], cascadePos[2])), static_cast(std::hypot(v0Pos[0], v0Pos[1], v0Pos[2])), cascITSclusters, protonTrack.itsNCls(), pionTrack.itsNCls(), bachelor.itsNCls(), protonTrack.tpcNClsFound(), pionTrack.tpcNClsFound(), bachelor.tpcNClsFound(), protonTrack.tpcNSigmaPr(), pionTrack.tpcNSigmaPi(), bachelor.tpcNSigmaKa(), bachelor.tpcNSigmaPi(), @@ -673,7 +695,7 @@ struct NonPromptCascadeTask { c.cascPt, c.cascEta, c.cascPhi, c.protonPt, c.protonEta, c.pionPt, c.pionEta, c.bachPt, c.bachEta, c.cascDCAxy, c.cascDCAz, c.protonDCAxy, c.protonDCAz, c.pionDCAxy, c.pionDCAz, c.bachDCAxy, c.bachDCAz, - c.casccosPA, c.v0cosPA, + c.cascDCADaughters, c.v0DCADaughters, c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, c.cascNClusITS, c.protonNClusITS, c.pionNClusITS, c.bachNClusITS, c.protonNClusTPC, c.pionNClusTPC, c.bachNClusTPC, @@ -712,7 +734,7 @@ struct NonPromptCascadeTask { c.pvContributors, c.cascPVContribs, c.pvTimeResolution, c.pvX, c.pvY, c.pvZ, c.cascPt, c.cascEta, c.cascPhi, c.protonPt, c.protonEta, c.pionPt, c.pionEta, c.bachPt, c.bachEta, c.cascDCAxy, c.cascDCAz, c.protonDCAxy, c.protonDCAz, c.pionDCAxy, c.pionDCAz, c.bachDCAxy, c.bachDCAz, - c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, + c.cascDCADaughters, c.v0DCADaughters, c.casccosPA, c.v0cosPA, c.massXi, c.massOmega, c.massV0, c.cascRadius, c.v0radius, c.cascLength, c.v0length, c.cascNClusITS, c.protonNClusITS, c.pionNClusITS, c.bachNClusITS, c.protonNClusTPC, c.pionNClusTPC, c.bachNClusTPC, c.protonTPCNSigma, c.pionTPCNSigma, c.bachKaonTPCNSigma, c.bachPionTPCNSigma, c.protonHasTOF, c.pionHasTOF, c.bachHasTOF, c.protonTOFNSigma, c.pionTOFNSigma, c.bachKaonTOFNSigma, c.bachPionTOFNSigma, @@ -748,6 +770,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } fillCandidatesVector(collisions, tracks, trackedCascades, gCandidates); fillMCtable(mcParticles, collisions, gCandidates); } @@ -757,6 +782,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtMC const& tracks, aod::McParticles const& mcParticles, aod::McCollisions const&, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } fillCandidatesVector(collisions, tracks, cascades, gCandidatesNT); fillMCtable(mcParticles, collisions, gCandidatesNT); // fillMultHistos(collisions); @@ -765,6 +793,9 @@ struct NonPromptCascadeTask { void processGenParticles(aod::McParticles const& mcParticles, aod::McCollisions const&) { + if (!mKeepTimeframe) { + return; + } for (const auto& p : mcParticles) { auto absCode = std::abs(p.pdgCode()); if (absCode != 3312 && absCode != 3334) { @@ -795,6 +826,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtData const& tracks, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } mProcessCounter[0]++; zorroAccounting(collisions); fillCandidatesVector(collisions, tracks, trackedCascades, gCandidates); @@ -806,6 +840,9 @@ struct NonPromptCascadeTask { aod::V0s const& /*v0s*/, TracksExtData const& tracks, aod::BCsWithTimestamps const&) { + if (!mKeepTimeframe) { + return; + } mProcessCounter[1]++; zorroAccounting(collisions); fillCandidatesVector(collisions, tracks, cascades, gCandidatesNT); @@ -818,185 +855,383 @@ struct NonPromptCascadeTask { // tracks: Join // mcCollisions: aod::McCollisions // mcParticles : aod::McParticles - void processdNdetaMC(CollisionCandidatesRun3MC const& colls, aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, - TracksWithLabel const& tracks) + TracksWithLabelSel const& tracks) { - //------------------------------------------------------------ - // Downscaling output table by BC as there is no pileup in MC - //------------------------------------------------------------ - int ds = 1; - //------------------------------------------------------------- - // MC mult for all MC coll - //-------------------------------------------------------------- - std::vector mcMult(mcCollisions.size(), 0); - for (auto const& mcp : mcParticles) { - int mcid = mcp.mcCollisionId(); - if (mcid < 0 || static_cast(mcid) >= mcMult.size()) - continue; + // ------------------------------------------------------------ + // Helper: accepted generated charged primary + // ------------------------------------------------------------ + auto isAcceptedMCParticle = [&](auto const& mcp) { + if (!mcp.isPhysicalPrimary()) { + return false; + } + if (std::abs(mcp.eta()) > cfgEtaCutdNdeta) { + return false; + } - // apply your primary/eta/charge definition here - if (!mcp.isPhysicalPrimary()) - continue; - if (std::abs(mcp.eta()) > cfgEtaCutdNdeta) - continue; int q = 0; if (auto pdg = pdgDB->GetParticle(mcp.pdgCode())) { q = static_cast(std::round(pdg->Charge() / 3.0)); } - if (q == 0) - continue; + return q != 0; + }; + + static constexpr float MinEtaFT0A = 3.5f; + static constexpr float MaxEtaFT0A = 4.9f; + static constexpr float MinEtaFT0C = -3.3f; + static constexpr float MaxEtaFT0C = -2.1f; + static constexpr float InvalidEta = -999.f; + static constexpr int InvalidId = -1; + + auto isAcceptedMCParticleFT0 = [&](auto const& mcp) { + if (!mcp.isPhysicalPrimary()) { + return false; + } - ++mcMult[mcid]; + const float eta = mcp.eta(); + if (!((eta > MinEtaFT0A && eta < MaxEtaFT0A) || (eta > MinEtaFT0C && eta < MaxEtaFT0C))) { + return false; + } + + int q = 0; + if (auto pdg = pdgDB->GetParticle(mcp.pdgCode())) { + q = static_cast(std::round(pdg->Charge() / 3.0)); + } + return q != 0; + }; + + // ------------------------------------------------------------ + // Helper: accepted reconstructed track + // Use same cuts as data. + // ------------------------------------------------------------ + auto isAcceptedRecoTrack = [&](auto const& trk) { + if (std::fabs(trk.eta()) >= cfgEtaCutdNdeta) { + return false; + } + if (trk.tpcNClsFound() < 80) { + return false; + } + if (trk.tpcNClsCrossedRows() < 100) { + return false; + } + if (!trk.isGlobalTrack()) { + return false; + } + return true; + }; + + // ------------------------------------------------------------ + // 1) MC charged multiplicity per MC collision + // mcid: mc collision row of mc particle + // ------------------------------------------------------------ + std::vector mcMult(mcCollisions.size(), 0); + std::vector mcMultFT0(mcCollisions.size(), 0); + // std::cout << "mcCollisions size:" << mcCollisions.size() << std::endl; + for (auto const& mcp : mcParticles) { + const int mcid = mcp.mcCollisionId(); + // std::cout << "mcid:" << mcid << std::endl; + if (mcid < 0 || static_cast(mcid) >= mcMult.size()) { + LOG(info) << "0 This should never happen ?"; + continue; + } + if (isAcceptedMCParticle(mcp)) { + ++mcMult[mcid]; + } + if (isAcceptedMCParticleFT0(mcp)) { + ++mcMultFT0[mcid]; + } } // ------------------------------------------------------------ - // Build mapping: (aod::Collisions row id used by tracks.collisionId()) - // -> dense index in 'colls' (0..colls.size()-1) - // We assume col.globalIndex() refers to the original aod::Collisions row. + // 2) Map aod::Collisions row id -> dense index in 'colls' // ------------------------------------------------------------ int maxCollRowId = -1; + for (auto const& col : colls) { + maxCollRowId = std::max(maxCollRowId, static_cast(col.globalIndex())); + } for (auto const& trk : tracks) { maxCollRowId = std::max(maxCollRowId, static_cast(trk.collisionId())); } + std::vector collRowIdToDense(maxCollRowId + 1, -1); - int dense = 0; + int denseIdx = 0; for (auto const& col : colls) { - const int collRowId = col.globalIndex(); // row id in aod::Collisions - if (collRowId >= 0 && static_cast(collRowId) < collRowIdToDense.size()) { - collRowIdToDense[collRowId] = dense; + const int collRowId = col.globalIndex(); + + if (collRowId >= 0 && collRowId <= maxCollRowId) { + collRowIdToDense[collRowId] = denseIdx; + } else { + LOG(info) << "1 This should never happen ?"; } - ++dense; + ++denseIdx; } // ------------------------------------------------------------ - // Reco multiplicity per *dense collision index in colls* + // 3) Reco multiplicity per reconstructed collision // ------------------------------------------------------------ std::vector recoMultDense(colls.size(), 0); + for (auto const& trk : tracks) { - if (std::abs(trk.eta()) > cfgEtaCutdNdeta) { + if (!isAcceptedRecoTrack(trk)) { continue; } + const int collRowId = trk.collisionId(); - if (collRowId < 0 || static_cast(collRowId) >= collRowIdToDense.size()) { + // std::cout << collRowId << std::endl; + if (collRowId < 0 || collRowId > maxCollRowId) { + // std::cout << "2 This should never happen ?" << std::endl; continue; } const int dIdx = collRowIdToDense[collRowId]; - if (dIdx >= 0) { - ++recoMultDense[dIdx]; + if (dIdx < 0) { + // Tracks has collId which is not in the list created above + LOG(info) << "3 This should never happen ?"; + continue; } + ++recoMultDense[dIdx]; } // ------------------------------------------------------------ - // MC bookkeeping: index by ROW INDEX (0..size-1), not globalIndex() + // 4) Bookkeeping // ------------------------------------------------------------ std::vector isReco(mcParticles.size(), 0); - std::vector isRecoMult(mcParticles.size(), 0.f); - std::vector mcReconstructed(mcCollisions.size(), 0); - // Optional cache of MC multiplicity per MC collision - std::vector mcMultCache(mcCollisions.size(), -1.f); + // Downscale output table per MC collision + std::vector writeMcCollision(mcCollisions.size(), 0); + int ds = 1; + for (auto const& mcColl : mcCollisions) { + const int mcid = mcColl.globalIndex(); + if (mcid < 0 || static_cast(mcid) >= writeMcCollision.size()) { + LOG(info) << "5 This should never happen ?"; + continue; + } + if ((ds % cfgDownscaleMB) == 0) { + writeMcCollision[mcid] = 1; + } + ++ds; + } + + std::vector writeRecoCollision(colls.size(), 0); + for (int iColl = 0; iColl < static_cast(colls.size()); ++iColl) { + if (((iColl + 1) % cfgDownscaleMB) == 0) { + writeRecoCollision[iColl] = 1; + } + } + + // ------------------------------------------------------------ + // 5) MC-to-reco collision associations. + // Save all associated reconstructed collisions, and keep one + // deterministic representative per MC collision for legacy row + // columns in NPMCChargedTable. + // ------------------------------------------------------------ + std::vector mcCollisionHasRecoCollision(mcCollisions.size(), 0); + std::vector mcCollisionBestRecoCollisionId(mcCollisions.size(), InvalidId); + std::vector mcCollisionBestRecoDenseIndex(mcCollisions.size(), InvalidId); + std::vector mcCollisionRecoMultFT0M(mcCollisions.size(), InvalidEta); + std::vector mcCollisionRecoCentFT0M(mcCollisions.size(), InvalidEta); + int nAssocRows = 0; + + denseIdx = 0; + for (auto const& col : colls) { + const int mcid = col.mcCollisionId(); + const int recoCollisionId = static_cast(col.globalIndex()); + if (mcid >= 0 && static_cast(mcid) < mcCollisionHasRecoCollision.size()) { + mcCollisionHasRecoCollision[mcid] = 1; + + if (writeRecoCollision[denseIdx]) { + NPMCRecoCollAssocTable(mcid, + recoCollisionId, + recoMultDense[denseIdx], + mcMult[mcid], + mcMultFT0[mcid], + col.multFT0M(), + col.centFT0M(), + col.selection_bit(aod::evsel::kNoSameBunchPileup)); + ++nAssocRows; + } + + if (mcCollisionBestRecoDenseIndex[mcid] == InvalidId || + col.multFT0M() > mcCollisionRecoMultFT0M[mcid]) { + mcCollisionBestRecoDenseIndex[mcid] = denseIdx; + mcCollisionBestRecoCollisionId[mcid] = recoCollisionId; + mcCollisionRecoMultFT0M[mcid] = col.multFT0M(); + mcCollisionRecoCentFT0M[mcid] = col.centFT0M(); + } + } else { + LOG(info) << "4 This should never happen ?"; + } + ++denseIdx; + } + + for (int mcid = 0; mcid < static_cast(mcCollisions.size()); ++mcid) { + if (mcCollisionHasRecoCollision[mcid] || !writeMcCollision[mcid]) { + continue; + } + NPMCRecoCollAssocTable(mcid, + InvalidId, + -1, + mcMult[mcid], + mcMultFT0[mcid], + InvalidEta, + InvalidEta, + false); + ++nAssocRows; + } + LOG(info) << "NPMCRecoCollisionAssoc rows filled: " << nAssocRows; // ------------------------------------------------------------ - // Single pass over tracks: fill RM for tracks whose collision is in colls + // 6) Matched reconstructed tracks // ------------------------------------------------------------ for (auto const& trk : tracks) { - // Accept reco track - if (std::abs(trk.eta()) > cfgEtaCutdNdeta) { + if (!isAcceptedRecoTrack(trk)) { continue; } - // Map track's collision row id -> dense colls index const int collRowId = trk.collisionId(); - if (collRowId < 0 || static_cast(collRowId) >= collRowIdToDense.size()) { + if (collRowId < 0 || collRowId > maxCollRowId) { continue; } + const int dIdx = collRowIdToDense[collRowId]; if (dIdx < 0) { - continue; // this track's collision is not in our 'colls' view + continue; } - // Get the collision row (dense index in colls view) auto col = colls.rawIteratorAt(dIdx); - // MC collision id (row index in aod::McCollisions) const int mcCollId = col.mcCollisionId(); + const int recoCollisionId = static_cast(col.globalIndex()); + const float multReco = recoMultDense[dIdx]; + const float ptReco = trk.pt(); + const float etaReco = trk.eta(); + const float multFT0M = col.multFT0M(); + const float centFT0M = col.centFT0M(); + if (mcCollId < 0 || static_cast(mcCollId) >= mcCollisions.size()) { + if (writeRecoCollision[dIdx]) { + // Fake: accepted reco track whose reconstructed collision has no valid MC collision label. + NPMCNTable(InvalidId, recoCollisionId, InvalidId, -1.f, ptReco, InvalidEta, etaReco, multReco, -1.f, -1.f, multFT0M, centFT0M); + } continue; } - mcReconstructed[mcCollId] = 1; - // MC particle id (row index in aod::McParticles) const int mcPid = trk.mcParticleId(); if (mcPid < 0 || static_cast(mcPid) >= mcParticles.size()) { + if (writeMcCollision[mcCollId]) { + // Fake: accepted reco track with invalid or missing MC particle label. + NPMCNTable(mcCollId, recoCollisionId, InvalidId, -2.f, ptReco, InvalidEta, etaReco, multReco, -2.f, mcMultFT0[mcCollId], multFT0M, centFT0M); + } continue; } - // MC multiplicity for that MC collision (cache) - float mult = mcMultCache[mcCollId]; - if (mult < 0.f) { - std::vector tmp; - mult = mcMult[mcCollId]; - mcMultCache[mcCollId] = mult; - } - auto mcPar = mcParticles.rawIteratorAt(mcPid); - // Apply the same acceptance as in MC multiplicity definition - if (!mcPar.isPhysicalPrimary()) { - continue; - } - if (std::abs(mcPar.eta()) > cfgEtaCutdNdeta) { + const int mcParCollId = mcPar.mcCollisionId(); + if (mcParCollId != mcCollId) { + if (writeMcCollision[mcCollId]) { + // Fake: reco collision and particle label point to different MC collisions. + NPMCNTable(mcCollId, recoCollisionId, mcPid, -3.f, ptReco, InvalidEta, etaReco, multReco, -3.f, mcMultFT0[mcCollId], multFT0M, centFT0M); + } continue; } - int q = 0; - if (auto pdgEntry = pdgDB->GetParticle(mcPar.pdgCode())) { - q = static_cast(std::round(pdgEntry->Charge() / 3.0)); - } - if (q == 0) { + if (!isAcceptedMCParticle(mcPar)) { + if (writeMcCollision[mcCollId]) { + // Feed-in: accepted reco track matched to truth outside fiducial phase space. + NPMCNTable(mcCollId, recoCollisionId, mcPid, -4.f, ptReco, mcPar.eta(), etaReco, multReco, -4.f, mcMultFT0[mcCollId], multFT0M, centFT0M); + } continue; } - // Mark reconstructed MC particle (now that it truly passed & matched) isReco[mcPid] = 1; - isRecoMult[mcPid] = mult; - const float multReco = col.multNTracksGlobal(); // or recoMultDense[dIdx] - const float ptReco = trk.pt(); + const float multMC = mcMult[mcCollId]; const float ptMC = mcPar.pt(); + const float etaMC = mcPar.eta(); - mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRM"), mult, multReco, ptMC, ptReco); - if (ds % cfgDownscaleMB == 0) { - NPMCNTable(ptMC, ptReco, mult, multReco); + mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRM"), + multMC, + multReco, + ptMC, + ptReco); + + if (writeMcCollision[mcCollId]) { + // Matched: accepted truth particle reconstructed inside the fiducial reco phase space. + NPMCNTable(mcCollId, recoCollisionId, mcPid, ptMC, ptReco, etaMC, etaReco, multReco, multMC, mcMultFT0[mcCollId], multFT0M, centFT0M); } - ds++; } // ------------------------------------------------------------ - // MC particles with no reco track (iterate by row index) + // 6) MC particles with no accepted reco track, + // but from MC collisions that have a reconstructed collision // ------------------------------------------------------------ - for (int pid = 0; pid < static_cast(mcParticles.size()); ++pid) { - if (!isReco[pid]) { - auto mcp = mcParticles.rawIteratorAt(pid); - mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoTrk"), isRecoMult[pid], mcp.pt()); - NPMCNTable(mcp.pt(), -1, isRecoMult[pid], -1); + for (int mcParticleIndex = 0; mcParticleIndex < static_cast(mcParticles.size()); ++mcParticleIndex) { + if (isReco[mcParticleIndex]) { + continue; + } + + auto mcp = mcParticles.rawIteratorAt(mcParticleIndex); + + if (!isAcceptedMCParticle(mcp)) { + continue; + } + + const int mcid = mcp.mcCollisionId(); + if (mcid < 0 || static_cast(mcid) >= mcMult.size()) { + continue; + } + + if (!mcCollisionHasRecoCollision[mcid]) { + continue; + } + + const float multMC = mcMult[mcid]; + const float multMCFT0 = mcMultFT0[mcid]; + const float multFT0M = mcCollisionRecoMultFT0M[mcid]; + const float centFT0M = mcCollisionRecoCentFT0M[mcid]; + const int recoCollisionId = mcCollisionBestRecoCollisionId[mcid]; + + mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoTrk"), + multMC, + mcp.pt()); + + if (writeMcCollision[mcid]) { + // Missed track: accepted truth particle in a reconstructed MC collision, but no accepted reco track. + NPMCNTable(mcid, recoCollisionId, mcParticleIndex, mcp.pt(), -1.f, mcp.eta(), InvalidEta, -1.f, multMC, multMCFT0, multFT0M, centFT0M); } } // ------------------------------------------------------------ - // Unreconstructed MC collisions (iterate by row index) + // 7) MC particles from MC collisions with no reconstructed collision // ------------------------------------------------------------ for (int mcid = 0; mcid < static_cast(mcCollisions.size()); ++mcid) { - if (!mcReconstructed[mcid]) { - std::vector mcptvec; - const int mult = mcMult[mcid]; - for (auto const& pt : mcptvec) { - mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoCol"), mult, pt); - NPMCNTable(pt, -2, mult, -2); + if (mcCollisionHasRecoCollision[mcid]) { + continue; + } + + const float multMC = mcMult[mcid]; + const float multMCFT0 = mcMultFT0[mcid]; + + for (int mcParticleIndex = 0; mcParticleIndex < static_cast(mcParticles.size()); ++mcParticleIndex) { + auto mcp = mcParticles.rawIteratorAt(mcParticleIndex); + if (mcp.mcCollisionId() != mcid) { + continue; + } + + if (!isAcceptedMCParticle(mcp)) { + continue; + } + + mRegistrydNdeta.fill(HIST("hdNdetaRM/hdNdetaRMNotInRecoCol"), + multMC, + mcp.pt()); + + if (writeMcCollision[mcid]) { + // Missed collision: accepted truth particle from an MC collision with no reconstructed collision. + NPMCNTable(mcid, InvalidId, mcParticleIndex, mcp.pt(), -2.f, mcp.eta(), InvalidEta, -2.f, multMC, multMCFT0, InvalidEta, InvalidEta); } } } @@ -1022,6 +1257,22 @@ struct NonPromptCascadeTask { } ds++; } + auto tracksThisColl = tracks.sliceBy(perCollisionSel, coll.globalIndex()); + int multreco = 0; + std::vector recoPts; + std::vector recoEtas; + // std::cout << "tracks:" << tracksThisColl.size() << std::endl; + for (auto const& track : tracksThisColl) { + // std::cout << track.pt() << " tracks " << track.isGlobalTrack() << std::endl; + if (std::fabs(track.eta()) < cfgEtaCutdNdeta && track.tpcNClsFound() >= 80 && track.tpcNClsCrossedRows() >= 100) { + if (track.isGlobalTrack()) { + multreco++; + recoPts.push_back(track.pt()); + recoEtas.push_back(track.eta()); + } + } + } + mRegistrydNdeta.fill(HIST("hdNdetaData"), multreco); if (writeFlag) { if (mRunNumber != bc.runNumber()) { mRunNumber = bc.runNumber(); @@ -1030,23 +1281,14 @@ struct NonPromptCascadeTask { globalBC, coll.numContrib(), coll.multNTracksGlobal(), + multreco, coll.centFT0M(), - coll.multFT0M()); - + coll.multFT0M(), + coll.selection_bit(aod::evsel::kNoSameBunchPileup)); auto collIdx = NPCollsTable.lastIndex(); - auto tracksThisColl = tracks.sliceBy(perCollisionSel, coll.globalIndex()); - float multreco = 0.; - // std::cout << "tracks:" << tracksThisColl.size() << std::endl; - for (auto const& track : tracksThisColl) { - // std::cout << track.pt() << " tracks " << track.isGlobalTrack() << std::endl; - if (std::fabs(track.eta()) < cfgEtaCutdNdeta && track.tpcNClsFound() >= 80 && track.tpcNClsCrossedRows() >= 100) { - if (track.isGlobalTrack()) { - multreco++; - NPRecoCandTable(collIdx, track.pt()); - } - } + for (size_t i = 0; i < recoPts.size(); ++i) { + NPRecoCandTable(collIdx, recoPts[i], recoEtas[i]); } - mRegistrydNdeta.fill(HIST("hdNdetaData"), multreco); } } } @@ -1077,7 +1319,7 @@ struct NonPromptCascadeTask { } float centFT0M = coll.centFT0M(); float multFT0M = coll.multFT0M(); - NPCollsTable(mRunNumber, globalBC, coll.numContrib(), coll.multNTracksGlobal(), centFT0M, multFT0M); + NPCollsTable(mRunNumber, globalBC, coll.numContrib(), coll.multNTracksGlobal(), 0, centFT0M, multFT0M, coll.selection_bit(aod::evsel::kNoSameBunchPileup)); } } }; diff --git a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx index e9aa45fd7c8..4f95c222ce5 100644 --- a/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx +++ b/PWGLF/Tasks/Strangeness/phiStrangeCorrelation.cxx @@ -39,9 +39,9 @@ #include #include -#include #include #include +#include #include #include @@ -52,8 +52,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -75,93 +77,61 @@ enum AnalysisMode { enum AssociatedParticleType { kK0S = 0, + kLambda, + kAntiLambda, kXi, + kOmega, kPion, kAssocPartSize }; -/*enum ParticleOfInterest { - Phi = 0, - K0S, - Pion, - ParticleOfInterestSize -};*/ - -/* -#define LIST_OF_PARTICLES_OF_INTEREST \ - X(Phi) \ - X(K0S) \ - X(Pion) \ - //X(PionTPC) \ - //X(PionTPCTOF) - -enum ParticleOfInterest { -#define X(name) name, - LIST_OF_PARTICLES_OF_INTEREST -#undef X - ParticleOfInterestSize +enum PhiMassRegion { + kSignalRegion = 0, + kSidebandRegion, + kPhiMassRegions }; -static constexpr std::array particleOfInterestLabels{ -#define X(name) #name, - LIST_OF_PARTICLES_OF_INTEREST -#undef X -}; - -static constexpr auto particleOfInterestLabels = std::to_array({ -#define X(name) #name, - LIST_OF_PARTICLES_OF_INTEREST -#undef X -}); -*/ - -using EffMapPtr = std::variant, std::shared_ptr>; - -/*struct BoundEfficiencyMap { - using CoordsTuple = std::tuple; - - const TH3* effMap; - CoordsTuple coords; - - BoundEfficiencyMap(const std::shared_ptr& effMap, float x, float y, float z) : effMap(effMap.get()), coords(x, y, z) {} - BoundEfficiencyMap(const std::shared_ptr& effMap, const CoordsTuple& coords) : effMap(effMap.get()), coords(coords) {} - - float getBinEfficiency() const - { - if (!effMap) { - return 1.0f; - } - - const auto& [x, y, z] = coords; - return effMap->GetBinContent(effMap->FindFixBin(x, y, z)); +template +constexpr int getPdgCode() +{ + if constexpr (PartType == kK0S) { + return PDG_t::kK0Short; + } else if constexpr (PartType == kLambda) { + return PDG_t::kLambda0; + } else if constexpr (PartType == kAntiLambda) { + return PDG_t::kLambda0Bar; + } else if constexpr (PartType == kXi) { + return PDG_t::kXiMinus; + } else if constexpr (PartType == kOmega) { + return PDG_t::kOmegaMinus; + } else if constexpr (PartType == kPion) { + return PDG_t::kPiPlus; + } else { + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in getPdgCode"); + return 0; } +} - float interpolateEfficiency() const - { - if (!effMap) { - return 1.0f; - } - - const auto& [x, y, z] = coords; - return effMap->Interpolate(x, y, z); - } -};*/ +using EffMapPtr = std::variant, std::shared_ptr>; struct BoundEfficiencyMap { using CoordsTuple = std::tuple; - const EffMapPtr& effMap; + const EffMapPtr* effMap; CoordsTuple coords; - BoundEfficiencyMap(const EffMapPtr& effMap, float x, float y, float z) : effMap(effMap), coords(x, y, z) {} - BoundEfficiencyMap(const EffMapPtr& effMap, const CoordsTuple& coords) : effMap(effMap), coords(coords) {} + BoundEfficiencyMap(const EffMapPtr& effMap, float x, float y, float z) : effMap(&effMap), coords(x, y, z) {} + BoundEfficiencyMap(const EffMapPtr& effMap, const CoordsTuple& coords) : effMap(&effMap), coords(coords) {} - float getBinEfficiency() const + [[nodiscard]] std::pair getBinEfficiencyAndError() const { return std::visit( - [this](auto&& mapPtr) -> float { - if (!mapPtr) - return 1.0f; + [this](auto&& mapPtr) -> std::pair { + if (!mapPtr) { + return {1.0f, 0.0f}; + } const auto& [x, y, z] = coords; @@ -170,20 +140,23 @@ struct BoundEfficiencyMap { // Compile-time branching: generates the exact correct function call if constexpr (std::is_same_v) { - return mapPtr->GetBinContent(mapPtr->FindFixBin(y, z)); // 2D case only + int bin = mapPtr->FindFixBin(y, z); // 2D case only + return {mapPtr->GetBinContent(bin), mapPtr->GetBinError(bin)}; } else { - return mapPtr->GetBinContent(mapPtr->FindFixBin(x, y, z)); // Full 3D case + int bin = mapPtr->FindFixBin(x, y, z); // Full 3D case + return {mapPtr->GetBinContent(bin), mapPtr->GetBinError(bin)}; } }, - effMap); + *effMap); } - float interpolateEfficiency() const + [[nodiscard]] float interpolateEfficiency() const { return std::visit( [this](auto&& mapPtr) -> float { - if (!mapPtr) + if (!mapPtr) { return 1.0f; + } const auto& [x, y, z] = coords; @@ -195,11 +168,23 @@ struct BoundEfficiencyMap { return mapPtr->Interpolate(x, y, z); // Native 3D interpolation } }, - effMap); + *effMap); } }; -struct PhiStrangenessCorrelation { +template +struct AssocInput { + static constexpr AssociatedParticleType Type = PartType; + TCands const* candidates; +}; + +template +constexpr auto makeAssocInput(TCands const& cands) +{ + return AssocInput{&cands}; +} + +struct PhiStrangeCorrelation { HistogramRegistry histos{"phiStrangenessCorrelation", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Configurable for selection type @@ -226,6 +211,24 @@ struct PhiStrangenessCorrelation { Configurable> rangeMK0sSignal{"rangeMK0sSignal", {0.47f, 0.53f}, "K0S mass range for signal extraction"}; } k0sConfigs; + // Configurables for Lambda selection + struct : ConfigurableGroup { + Configurable selectLambdaInSigRegion{"selectLambdaInSigRegion", true, "Select Lambda candidates in signal region"}; + Configurable> rangeMLambdaSignal{"rangeMLambdaSignal", {1.11f, 1.12f}, "Lambda mass range for signal extraction"}; + } lambdaConfigs; + + // Configurables for Xi selection + struct : ConfigurableGroup { + Configurable selectXiInSigRegion{"selectXiInSigRegion", true, "Select Xi candidates in signal region"}; + Configurable> rangeMXiSignal{"rangeMXiSignal", {1.31f, 1.33f}, "Xi mass range for signal extraction"}; + } xiConfigs; + + // Configurables for Omega selection + struct : ConfigurableGroup { + Configurable selectOmegaInSigRegion{"selectOmegaInSigRegion", true, "Select Omega candidates in signal region"}; + Configurable> rangeMOmegaSignal{"rangeMOmegaSignal", {1.66f, 1.68f}, "Omega mass range for signal extraction"}; + } omegaConfigs; + // Configurables for Pions selection struct : ConfigurableGroup { Configurable selectPionInSigRegion{"selectPionInSigRegion", true, "Select Pion candidates in signal region"}; @@ -242,15 +245,23 @@ struct PhiStrangenessCorrelation { Configurable> binspTK0S{"binspTK0S", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S"}; Configurable> binspTK0SExt{"binspTK0SExt", {0.0, 0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for K0S extended for MC Gen"}; + // Configurable on Lambda pT bins + Configurable> binspTLambda{"binspTLambda", {0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Lambda"}; + Configurable> binspTLambdaExt{"binspTLambdaExt", {0.0, 0.1, 0.5, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Lambda extended for MC Gen"}; + + // Configurable on Xi pT bins + Configurable> binspTXi{"binspTXi", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi"}; + Configurable> binspTXiExt{"binspTXiExt", {0.0, 0.4, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Xi extended for MC Gen"}; + + // Configurable on Omega pT bins + Configurable> binspTOmega{"binspTOmega", {0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Omega"}; + Configurable> binspTOmegaExt{"binspTOmegaExt", {0.0, 0.4, 0.8, 1.2, 1.6, 2.0, 2.5, 3.0, 4.0, 6.0}, "pT bin limits for Omega extended for MC Gen"}; + // Configurable on pion pT bins Configurable> binspTPi{"binspTPi", {0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 3.0}, "pT bin limits for Pions"}; Configurable> binspTPiExt{"binspTPiExt", {0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.8, 1.0, 1.2, 1.5, 2.0, 3.0}, "pT bin limits for Pions extended for MC Gen"}; - struct : ConfigurableGroup { - Configurable doK0SCorrelation{"doK0SCorrelation", true, "Enable Phi-K0S correlation"}; - Configurable doXiCorrelation{"doXiCorrelation", false, "Enable Phi-Xi correlation"}; - Configurable doPionCorrelation{"doPionCorrelation", true, "Enable Phi-Pion correlation"}; - } activeCorrelationConfigs; + Configurable> activeCorrelationTypes{"activeCorrelationTypes", {true, false, false, false, false, true}, "Enable correlation types: 0 - K0S, 1 - Lambda, 2 - AntiLambda, 3 - Xi, 4 - Omega, 5 - Pion"}; // Configurables for delta y selection struct : ConfigurableGroup { @@ -264,7 +275,9 @@ struct PhiStrangenessCorrelation { struct : ConfigurableGroup { Configurable applyEfficiency{"applyEfficiency", false, "Use efficiency for filling histograms"}; Configurable useEffInterpolation{"useEffInterpolation", false, "If true, interpolates efficiency map, else uses bin center"}; - Configurable applyPhiEfficiency{"applyPhiEfficiency", true, "Apply efficiency for Phi candidates"}; + Configurable applyPhiEfficiency{"applyPhiEfficiency", false, "Apply efficiency for Phi candidates"}; + Configurable propagateEffError{"propagateEffError", false, "Propagate efficiency error"}; + Configurable perPeriodEfficiency{"perPeriodEfficiency", false, "Use per period efficiency maps"}; } efficiencyConfigs; // Configurable for event mixing @@ -276,9 +289,12 @@ struct PhiStrangenessCorrelation { // Configurables for minimum pt selection in McGen struct : ConfigurableGroup { - Configurable minPhiPt{"minPhiPt", 0.4f, "Minimum pT for Phi candidates"}; - Configurable v0SettingMinPt{"v0SettingMinPt", 0.1f, "V0 min pt"}; - Configurable cMinPionPtcut{"cMinPionPtcut", 0.2f, "Track minimum pt cut"}; + Configurable minPtPhi{"minPtPhi", 0.4f, "Minimum pT for Phi candidates"}; + Configurable minPtK0S{"minPtK0S", 0.1f, "Minimum pT for K0S candidates"}; + Configurable minPtLambda{"minPtLambda", 0.2f, "Minimum pT for Lambda candidates"}; + Configurable minPtXi{"minPtXi", 0.8f, "Minimum pT for Xi candidates"}; + Configurable minPtOmega{"minPtOmega", 0.8f, "Minimum pT for Omega candidates"}; + Configurable minPtPion{"minPtPion", 0.2f, "Minimum pT for Pion candidates"}; Configurable bypassPtCut{"bypassPtCut", false, "Bypass the minimum pt cut at MCGen level"}; } minPtMcGenConfigs; @@ -295,13 +311,39 @@ struct PhiStrangenessCorrelation { SliceCache cache; struct : PresliceGroup { - Preslice phiCandDataPerCollision = aod::lf_selection_phi_candidate::collisionId; - // PresliceUnsorted phiCandMcRecoPerCollision = aod::lf_selection_phi_candidate::collisionId; +#define DEFINE_PRESLICE(PREF, TAB_PREF, COL_ID) \ + Preslice PREF##DataPerCollision = aod::COL_ID::collisionId; \ + Preslice PREF##McRecoPerCollision = aod::COL_ID::collisionId; + + // Trigger + DEFINE_PRESLICE(phiCand, Phimeson, lf_selection_phi_candidate) + + // Associated Strangeness + DEFINE_PRESLICE(k0s, K0sReduced, v0) + DEFINE_PRESLICE(lambda, LambdaReduced, v0) + DEFINE_PRESLICE(antiLambda, AntiLambdaReduced, v0) + DEFINE_PRESLICE(xi, XiReduced, cascade) + DEFINE_PRESLICE(omega, OmegaReduced, cascade) +#undef DEFINE_PRESLICE + + /*Preslice phiCandDataPerCollision = aod::lf_selection_phi_candidate::collisionId; Preslice phiCandMcRecoPerCollision = aod::lf_selection_phi_candidate::collisionId; Preslice k0sDataPerCollision = aod::v0::collisionId; Preslice k0sMcRecoPerCollision = aod::v0::collisionId; + Preslice lambdaDataPerCollision = aod::v0::collisionId; + Preslice lambdaMcRecoPerCollision = aod::v0::collisionId; + + Preslice antiLambdaDataPerCollision = aod::v0::collisionId; + Preslice antiLambdaMcRecoPerCollision = aod::v0::collisionId; + + Preslice xiDataPerCollision = aod::cascade::collisionId; + Preslice xiMcRecoPerCollision = aod::cascade::collisionId; + + Preslice omegaDataPerCollision = aod::cascade::collisionId; + Preslice omegaMcRecoPerCollision = aod::cascade::collisionId;*/ + Preslice pionTrackDataPerCollision = aod::track::collisionId; Preslice pionTrackMcRecoPerCollision = aod::track::collisionId; @@ -309,12 +351,9 @@ struct PhiStrangenessCorrelation { } preslices; // Necessary service to retrieve efficiency maps from CCDB - Service ccdb; - - // std::shared_ptr effMapPhi{}; - // std::array, kAssocPartSize> effMapsAssoc{}; + Service ccdb{}; - EffMapPtr effMapPhi{}; + EffMapPtr effMapPhi; std::array effMapsAssoc{}; // Binning policy and axes for mixed event @@ -322,30 +361,35 @@ struct PhiStrangenessCorrelation { ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {VARIABLE_WIDTH, 0.0f, 1.0f, 5.0f, 10.0f, 15.0f, 20.0f, 30.0f, 40.0f, 50.0f, 70.0f, 100.0f}, "Multiplicity percentage binning for mixing"}; using BinningTypeVertexCent = ColumnBinningPolicy; - BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}}; - static constexpr std::array phiMassRegionLabels{"Signal", "Sideband"}; - // static constexpr std::array particleOfInterestLabels{"Phi", "K0S", "Pion" /*"PionTPC", "PionTPCTOF"*/}; - static constexpr std::array assocParticleLabels{"K0S", "Xi", "Pi"}; + static constexpr std::array PhiMassRegionLabels{"Signal", "Sideband"}; + static constexpr std::array AssocParticleLabels{"K0S", "Lambda", "AntiLambda", "Xi", "Omega", "Pi"}; // Light structures to store only the necessary information for the correlation analysis at MCGen level struct MiniParticle { - float pt; - float y; - float phi; + float pt{}; + float y{}; + float phi{}; }; struct MiniEvent { - float multiplicity; + float multiplicity{}; std::vector phiParticles; - std::vector k0sParticles; + std::array, kAssocPartSize> assocParticles; + /*std::vector k0sParticles; + std::vector lambdaParticles; + std::vector antilambdaParticles; std::vector xiParticles; - std::vector pionParticles; + std::vector omegaParticles; + std::vector pionParticles;*/ }; // Buffer for mixed event, organized as a vector of deques, one for each multiplicity bin, containing the past events with their particles of interest needed for mixing std::vector> eventBuffer; + int mLastRunNumber{-1}; + void init(InitContext&) { AxisSpec vertexZAxis = {100, -cutZVertex, cutZVertex, "vrtx_{Z} [cm]"}; // TO BE REMOVED @@ -362,27 +406,64 @@ struct PhiStrangenessCorrelation { AxisSpec pTK0SAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTK0SAxis{(std::vector)binspTK0S, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTK0SAxisExt{(std::vector)binspTK0SExt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec massLambdaAxis = {200, 1.1f, 1.13f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec pTLambdaAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTLambdaAxis{(std::vector)binspTLambda, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTLambdaAxisExt{(std::vector)binspTLambdaExt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec massXiAxis = {200, 1.3f, 1.4f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec pTXiAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTXiAxis{(std::vector)binspTXi, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTXiAxisExt{(std::vector)binspTXiExt, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec massOmegaAxis = {200, 1.65f, 1.7f, "#it{M}_{inv} [GeV/#it{c}^{2}]"}; + AxisSpec pTOmegaAxis = {100, 0.0f, 10.0f, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTOmegaAxis{(std::vector)binspTOmega, "#it{p}_{T} (GeV/#it{c})"}; + AxisSpec binnedpTOmegaAxisExt{(std::vector)binspTOmegaExt, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec nSigmaPiAxis = {100, -10.0f, 10.0f, "N#sigma #pi"}; AxisSpec pTPiAxis = {50, 0.0f, 5.0f, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTPiAxis{(std::vector)binspTPi, "#it{p}_{T} (GeV/#it{c})"}; AxisSpec binnedpTPiAxisExt{(std::vector)binspTPiExt, "#it{p}_{T} (GeV/#it{c})"}; + std::array massAxes = {massK0SAxis, massLambdaAxis, massLambdaAxis, massXiAxis, massOmegaAxis, nSigmaPiAxis}; + std::array binnedpTAxes = {binnedpTK0SAxis, binnedpTLambdaAxis, binnedpTLambdaAxis, binnedpTXiAxis, binnedpTOmegaAxis, binnedpTPiAxis}; + std::array binnedpTExtAxes = {binnedpTK0SAxisExt, binnedpTLambdaAxisExt, binnedpTLambdaAxisExt, binnedpTXiAxisExt, binnedpTOmegaAxisExt, binnedpTPiAxisExt}; + + std::array assocTitles = {"K0Short", "Lambda", "AntiLambda", "Xi", "Omega", "Pion"}; + std::array dirNames = {"k0s", "lambda", "antilambda", "xi", "omega", "pi"}; + histos.add("phi/h3PhiData", "Invariant mass of Phi in Data", kTH3F, {binnedmultAxis, binnedpTPhiAxis, massPhiAxis}); - histos.add("phiK0S/h6PhiK0SData", "Invariant mass of Phi vs Invariant mass of K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); - histos.add("phiPi/h6PhiPiTPCData", "Invariant mass of Phi vs nSigmaTPC Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); - histos.add("phiPi/h6PhiPiTOFData", "Invariant mass of Phi vs nSigmaTOF Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); + histos.add("phi/h4PhiMCReco", "Phi in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); + histos.add("phi/h3PhiMCGen", "Phi in MC Gen", kTH3F, {binnedmultAxis, binnedpTPhiAxis, yAxis}); + histos.add("phi/h4PhiMCGenAssocReco", "Phi in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); - histos.add("phiK0S/h6PhiK0SDataME", "Invariant mass of Phi vs Invariant mass of K0Short in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); - histos.add("phiPi/h6PhiPiTPCDataME", "Invariant mass of Phi vs nSigmaTPC Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); - histos.add("phiPi/h6PhiPiTOFDataME", "Invariant mass of Phi vs nSigmaTOF Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); + histos.add("phi/h3PhiMCClosureGen", "Phi in MC Gen for MC Closure Test", kTH3F, {binnedmultAxis, binnedpTPhiAxisExt, yAxis}); - for (const auto& label : phiMassRegionLabels) { - histos.add(fmt::format("phiK0S/h5PhiK0SData{}", label).c_str(), "Deltay vs deltaphi for Phi and K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, deltaphiAxis}); - histos.add(fmt::format("phiPi/h5PhiPiData{}", label).c_str(), "Deltay vs deltaphi for Phi and Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, deltaphiAxis}); + for (size_t i = 0; i < kAssocPartSize; ++i) { + std::string pName(AssocParticleLabels[i]); + std::string pTitle = assocTitles[i]; + std::string dir = dirNames[i]; + + if (i == kPion) { + histos.add("phiPi/h6PhiPiTPCData", "Invariant mass of Phi vs nSigmaTPC of Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add("phiPi/h6PhiPiTPCDataME", "Invariant mass of Phi vs nSigmaTPC of Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add("phiPi/h6PhiPiTOFData", "Invariant mass of Phi vs nSigmaTOF of Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add("phiPi/h6PhiPiTOFDataME", "Invariant mass of Phi vs nSigmaTOF of Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + } else { + histos.add(fmt::format("phi{}/h6Phi{}Data", pName, pName).c_str(), fmt::format("Invariant mass of Phi vs Invariant mass of {} in Data", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + histos.add(fmt::format("phi{}/h6Phi{}DataME", pName, pName).c_str(), fmt::format("Invariant mass of Phi vs Invariant mass of {} in Data ME", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, massPhiAxis, massAxes[i]}); + } - histos.add(fmt::format("phiK0S/h5PhiK0SDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and K0Short in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, deltaphiAxis}); - histos.add(fmt::format("phiPi/h5PhiPiDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, deltaphiAxis}); + for (const auto& label : PhiMassRegionLabels) { + histos.add(fmt::format("phi{}/h5Phi{}Data{}", pName, pName, label).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in Data", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phi{}/h5Phi{}DataME{}", pName, pName, label).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in Data ME", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTAxes[i], deltayAxis, deltaphiAxis}); + } + + histos.add(fmt::format("{}/h4{}MCReco", dir, pName).c_str(), fmt::format("{} in MC Reco", pTitle).c_str(), kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTAxes[i], yAxis}); + histos.add(fmt::format("{}/h3{}MCGen", dir, pName).c_str(), fmt::format("{} in MC Gen", pTitle).c_str(), kTH3F, {binnedmultAxis, binnedpTAxes[i], yAxis}); + histos.add(fmt::format("{}/h4{}MCGenAssocReco", dir, pName).c_str(), fmt::format("{} in MC Gen Assoc Reco", pTitle).c_str(), kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTAxes[i], yAxis}); + + histos.add(fmt::format("phi{}/h5Phi{}ClosureMCGen", pName, pName).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in MCGen", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTExtAxes[i], deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phi{}/h5Phi{}ClosureMCGenME", pName, pName).c_str(), fmt::format("Deltay vs deltaphi for Phi and {} in MCGen ME", pTitle).c_str(), kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTExtAxes[i], deltayAxis, deltaphiAxis}); } histos.add("event/hRecoMCMultiplicityPercent", "RecoMC Multiplicity Percentile", kTH1F, {binnedmultAxis}); @@ -392,25 +473,71 @@ struct PhiStrangenessCorrelation { histos.add("event/hGenMCAssocRecoMultiplicityPercent", "Generated MC associated Multiplicity Percentile", kTH1F, {binnedmultAxis}); histos.add("event/h2GenMCAssocRecoVertexZvsMult", "Generated MC associated reco Vertex Z vs multiplicity", kTH2F, {vertexZAxis, binnedmultAxis}); - histos.add("phi/h4PhiMCReco", "Phi in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); - histos.add("phi/h3PhiMCGen", "Phi in MC Gen", kTH3F, {binnedmultAxis, binnedpTPhiAxis, yAxis}); - histos.add("phi/h4PhiMCGenAssocReco", "Phi in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPhiAxis, yAxis}); + /*histos.add("phiK0S/h6PhiK0SData", "Invariant mass of Phi vs Invariant mass of K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); + histos.add("phiLambda/h6PhiLambdaData", "Invariant mass of Phi vs Invariant mass of Lambda in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, massPhiAxis, massLambdaAxis}); + histos.add("phiXi/h6PhiXiData", "Invariant mass of Phi vs Invariant mass of Xi in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, massPhiAxis, massXiAxis}); + histos.add("phiOmega/h6PhiOmegaData", "Invariant mass of Phi vs Invariant mass of Omega in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, massPhiAxis, massOmegaAxis}); + histos.add("phiPi/h6PhiPiTPCData", "Invariant mass of Phi vs nSigmaTPC Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); + histos.add("phiPi/h6PhiPiTOFData", "Invariant mass of Phi vs nSigmaTOF Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); - histos.add("k0s/h4K0SMCReco", "K0S in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTK0SAxis, yAxis}); + histos.add("phiK0S/h6PhiK0SDataME", "Invariant mass of Phi vs Invariant mass of K0Short in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, massPhiAxis, massK0SAxis}); + histos.add("phiLambda/h6PhiLambdaDataME", "Invariant mass of Phi vs Invariant mass of Lambda in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, massPhiAxis, massLambdaAxis}); + histos.add("phiXi/h6PhiXiDataME", "Invariant mass of Phi vs Invariant mass of Xi in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, massPhiAxis, massXiAxis}); + histos.add("phiOmega/h6PhiOmegaDataME", "Invariant mass of Phi vs Invariant mass of Omega in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, massPhiAxis, massOmegaAxis}); + histos.add("phiPi/h6PhiPiTPCDataME", "Invariant mass of Phi vs nSigmaTPC Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis}); + histos.add("phiPi/h6PhiPiTOFDataME", "Invariant mass of Phi vs nSigmaTOF Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, massPhiAxis, nSigmaPiAxis});*/ + + /*for (const auto& label : PhiMassRegionLabels) { + histos.add(fmt::format("phiK0S/h5PhiK0SData{}", label).c_str(), "Deltay vs deltaphi for Phi and K0Short in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiLambda/h5PhiLambdaData{}", label).c_str(), "Deltay vs deltaphi for Phi and Lambda in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiXi/h5PhiXiData{}", label).c_str(), "Deltay vs deltaphi for Phi and Xi in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiOmega/h5PhiOmegaData{}", label).c_str(), "Deltay vs deltaphi for Phi and Omega in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiPi/h5PhiPiData{}", label).c_str(), "Deltay vs deltaphi for Phi and Pion in Data", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, deltaphiAxis}); + + histos.add(fmt::format("phiK0S/h5PhiK0SDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and K0Short in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTK0SAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiLambda/h5PhiLambdaDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Lambda in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTLambdaAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiXi/h5PhiXiDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Xi in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTXiAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiOmega/h5PhiOmegaDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Omega in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTOmegaAxis, deltayAxis, deltaphiAxis}); + histos.add(fmt::format("phiPi/h5PhiPiDataME{}", label).c_str(), "Deltay vs deltaphi for Phi and Pion in Data ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxis, binnedpTPiAxis, deltayAxis, deltaphiAxis}); + }*/ + + /*histos.add("k0s/h4K0SMCReco", "K0S in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTK0SAxis, yAxis}); histos.add("k0s/h3K0SMCGen", "K0S in MC Gen", kTH3F, {binnedmultAxis, binnedpTK0SAxis, yAxis}); histos.add("k0s/h4K0SMCGenAssocReco", "K0S in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTK0SAxis, yAxis}); + histos.add("lambda/h4LambdaMCReco", "Lambda in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("lambda/h3LambdaMCGen", "Lambda in MC Gen", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("lambda/h4LambdaMCGenAssocReco", "Lambda in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + + histos.add("antilambda/h4AntiLambdaMCReco", "AntiLambda in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("antilambda/h3AntiLambdaMCGen", "AntiLambda in MC Gen", kTH3F, {binnedmultAxis, binnedpTLambdaAxis, yAxis}); + histos.add("antilambda/h4AntiLambdaMCGenAssocReco", "AntiLambda in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTLambdaAxis, yAxis}); + + histos.add("xi/h4XiMCReco", "Xi in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTXiAxis, yAxis}); + histos.add("xi/h3XiMCGen", "Xi in MC Gen", kTH3F, {binnedmultAxis, binnedpTXiAxis, yAxis}); + histos.add("xi/h4XiMCGenAssocReco", "Xi in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTXiAxis, yAxis}); + + histos.add("omega/h4OmegaMCReco", "Omega in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTOmegaAxis, yAxis}); + histos.add("omega/h3OmegaMCGen", "Omega in MC Gen", kTH3F, {binnedmultAxis, binnedpTOmegaAxis, yAxis}); + histos.add("omega/h4OmegaMCGenAssocReco", "Omega in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTOmegaAxis, yAxis}); + histos.add("pi/h4PiMCReco", "Pion in MC Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis}); histos.add("pi/h3PiMCGen", "Pion in MC Gen", kTH3F, {binnedmultAxis, binnedpTPiAxis, yAxis}); - histos.add("pi/h4PiMCGenAssocReco", "Pion in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis}); - - histos.add("phi/h3PhiMCClosureGen", "Phi in MC Gen for MC Closure Test", kTH3F, {binnedmultAxis, binnedpTPhiAxisExt, yAxis}); + histos.add("pi/h4PiMCGenAssocReco", "Pion in MC Gen Assoc Reco", kTHnSparseF, {vertexZAxis, binnedmultAxis, binnedpTPiAxis, yAxis});*/ - histos.add("phiK0S/h5PhiK0SClosureMCGen", "Deltay vs deltaphi for Phi and K0Short in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTK0SAxisExt, deltayAxis, deltaphiAxis}); + /*histos.add("phiK0S/h5PhiK0SClosureMCGen", "Deltay vs deltaphi for Phi and K0Short in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTK0SAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiLambda/h5PhiLambdaClosureMCGen", "Deltay vs deltaphi for Phi and Lambda in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiAntiLambda/h5PhiAntiLambdaClosureMCGen", "Deltay vs deltaphi for Phi and AntiLambda in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiXi/h5PhiXiClosureMCGen", "Deltay vs deltaphi for Phi and Xi in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTXiAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiOmega/h5PhiOmegaClosureMCGen", "Deltay vs deltaphi for Phi and Omega in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTOmegaAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiPi/h5PhiPiClosureMCGen", "Deltay vs deltaphi for Phi and Pion in MCGen", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTPiAxisExt, deltayAxis, deltaphiAxis}); histos.add("phiK0S/h5PhiK0SClosureMCGenME", "Deltay vs deltaphi for Phi and K0Short in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTK0SAxisExt, deltayAxis, deltaphiAxis}); - histos.add("phiPi/h5PhiPiClosureMCGenME", "Deltay vs deltaphi for Phi and Pion in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTPiAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiLambda/h5PhiLambdaClosureMCGenME", "Deltay vs deltaphi for Phi and Lambda in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiAntiLambda/h5PhiAntiLambdaClosureMCGenME", "Deltay vs deltaphi for Phi and AntiLambda in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTLambdaAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiXi/h5PhiXiClosureMCGenME", "Deltay vs deltaphi for Phi and Xi in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTXiAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiOmega/h5PhiOmegaClosureMCGenME", "Deltay vs deltaphi for Phi and Omega in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTOmegaAxisExt, deltayAxis, deltaphiAxis}); + histos.add("phiPi/h5PhiPiClosureMCGenME", "Deltay vs deltaphi for Phi and Pion in MCGen ME", kTHnSparseF, {binnedmultAxis, binnedpTPhiAxisExt, binnedpTPiAxisExt, deltayAxis, deltaphiAxis});*/ // Load efficiency maps from CCDB if (efficiencyConfigs.applyEfficiency) { @@ -419,68 +546,111 @@ struct PhiStrangenessCorrelation { ccdb->setLocalObjectValidityChecking(); ccdb->setFatalWhenNull(false); - /*for (int i = 0; i < ParticleOfInterestSize; ++i) { - loadEfficiencyMapFromCCDB(static_cast(i)); - }*/ - loadEfficiencyMaps(); + if (!efficiencyConfigs.perPeriodEfficiency) { + loadEfficiencyMaps(); + } } eventBuffer.resize(binsMult->size() - 1); } - void fetchSingleEfficiencyMapFromCCDB(EffMapPtr& effMap, std::string_view particleName) + void fetchSingleEfficiencyMapFromCCDB(EffMapPtr& effMap, std::string_view particleName, std::optional runNumber = std::nullopt) { std::string path = fmt::format("{}{}", ccdbEfficiencyPath.value, particleName); - if (auto map3D = std::shared_ptr(ccdb->get(path))) { + auto fetchFromCCDB = [&]() -> std::shared_ptr { + auto mapPtr = runNumber ? ccdb->getForRun(path, *runNumber) : ccdb->get(path); + return std::shared_ptr(mapPtr); + }; + + if (auto map3D = fetchFromCCDB.operator()()) { effMap = map3D; - LOG(info) << "Efficiency map (TH3) for " << particleName << " loaded from CCDB"; + LOG(debug) << "Efficiency map (TH3) for " << particleName << " loaded from CCDB"; return; } - if (auto map2D = std::shared_ptr(ccdb->get(path))) { + if (auto map2D = fetchFromCCDB.operator()()) { effMap = map2D; - LOG(info) << "Efficiency map (TH2) for " << particleName << " loaded from CCDB"; + LOG(debug) << "Efficiency map (TH2) for " << particleName << " loaded from CCDB"; return; } - LOG(fatal) << "Could not load efficiency map (neither TH3 nor TH2) for " << particleName << " from CCDB!"; + std::string runSuffix = runNumber ? fmt::format(" for run {}", *runNumber) : ""; + LOG(fatal) << "Could not load efficiency map (neither TH3 nor TH2) for " << particleName << " from CCDB" << runSuffix << "!"; } - void loadEfficiencyMaps() + void loadEfficiencyMaps(std::optional runNumber = std::nullopt) { - // Load the Trigger (Phi) map if requested by analysis method - if (efficiencyConfigs.applyPhiEfficiency) - fetchSingleEfficiencyMapFromCCDB(effMapPhi, "Phi"); + if (runNumber) { + if (*runNumber == mLastRunNumber) { + return; + } + mLastRunNumber = *runNumber; + } - // Map the user configurations for the associated particles - bool doAssocCorrelations[kAssocPartSize] = {activeCorrelationConfigs.doK0SCorrelation, activeCorrelationConfigs.doXiCorrelation, activeCorrelationConfigs.doPionCorrelation}; + // Load the Trigger (Phi) map if requested by analysis method + if (efficiencyConfigs.applyPhiEfficiency) { + fetchSingleEfficiencyMapFromCCDB(effMapPhi, "Phi", runNumber); + } // Only load the associated maps that are explicitly enabled for (size_t i = 0; i < kAssocPartSize; ++i) { - if (doAssocCorrelations[i]) - fetchSingleEfficiencyMapFromCCDB(effMapsAssoc[i], assocParticleLabels[i]); + if (activeCorrelationTypes->at(i)) { + fetchSingleEfficiencyMapFromCCDB(effMapsAssoc[i], AssocParticleLabels[i], runNumber); + } } - } - /*void loadEfficiencyMapFromCCDB(ParticleOfInterest poi) - { - effMaps[poi] = std::shared_ptr(ccdb->get(fmt::format("{}{}", ccdbEfficiencyPath.value, particleOfInterestLabels[poi]))); - if (!effMaps[poi]) - LOG(fatal) << "Could not load efficiency map for " << particleOfInterestLabels[poi] << "!"; - LOG(info) << "Efficiency map for " << particleOfInterestLabels[poi] << " loaded from CCDB"; - }*/ + if (runNumber) { + LOG(info) << "Efficiency maps successfully updated from CCDB for run " << *runNumber; + } else { + LOG(info) << "Global efficiency maps successfully loaded from CCDB"; + } + } // Compute weight based on efficiencies template - float computeWeight(const BoundEffMaps&... boundEffMaps) + std::pair computeWeightAndError(const BoundEffMaps&... boundEffMaps) { - if (!efficiencyConfigs.applyEfficiency) - return 1.0f; + if (!efficiencyConfigs.applyEfficiency) { + return {1.0f, 0.0f}; + } + + float effTot = 1.0f; + float relErrSqSum = 0.0f; + + auto processMap = [&](const auto& boundMap) { + auto [eff, err] = boundMap.getBinEfficiencyAndError(); // Unpack efficiency and error from the map + + if (efficiencyConfigs.useEffInterpolation) { + eff = boundMap.interpolateEfficiency(); + // For simplicity, we keep the error from the bin content even when interpolating, but this can be refined if needed + } - float totalEfficiency = ((efficiencyConfigs.useEffInterpolation ? boundEffMaps.interpolateEfficiency() : boundEffMaps.getBinEfficiency()) * ...); + effTot *= eff; - return totalEfficiency <= 0.0f ? 1.0f : 1.0f / totalEfficiency; + if (eff > 0.0f) { + float mapErr = efficiencyConfigs.propagateEffError ? err : 0.0f; // Optionally propagate error, otherwise treat as zero + relErrSqSum += (mapErr / eff) * (mapErr / eff); + } + }; + + (processMap(boundEffMaps), ...); // Fold expression to process all bound efficiency maps + + if (effTot <= 0.0f) { + return {1.0f, 0.0f}; + } + + float weight = 1.0f / effTot; + float weightErr = weight * std::sqrt(relErrSqSum); // Propagate relative error to the weight + + return {weight, weightErr}; + } + + template + std::pair computeAssocWeight(float multiplicity, const auto& phiCand, const auto& assocCand) + { + return computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), + BoundEfficiencyMap(effMapsAssoc[PartType], multiplicity, assocCand.pt(), assocCand.y())); } float getDeltaPhi(float phiTrigger, float phiAssociated) @@ -490,365 +660,604 @@ struct PhiStrangenessCorrelation { int getCentBin(float multiplicity) { - if (multiplicity < binsMult->front() || multiplicity >= binsMult->back()) + if (multiplicity < binsMult->front() || multiplicity >= binsMult->back()) { return -1; + } auto it = std::upper_bound(binsMult->begin(), binsMult->end(), multiplicity); return std::distance(binsMult->begin(), it) - 1; } - template - void processPhiK0SPionSE(TCollision const& collision, TPhiCands const& phiCandidates, TK0SCands const& k0sReduced, TPionCands const& pionTracks) + template + auto getPreslice() { - float multiplicity = collision.centFT0M(); + if constexpr (PartType == kK0S) { + return preslices.k0sMcRecoPerCollision; + } else if constexpr (PartType == kLambda) { + return preslices.lambdaMcRecoPerCollision; + } else if constexpr (PartType == kAntiLambda) { + return preslices.antiLambdaMcRecoPerCollision; + } else if constexpr (PartType == kXi) { + return preslices.xiMcRecoPerCollision; + } else if constexpr (PartType == kOmega) { + return preslices.omegaMcRecoPerCollision; + } else if constexpr (PartType == kPion) { + return preslices.pionTrackMcRecoPerCollision; + } else { + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in getPreslice"); + return preslices.k0sMcRecoPerCollision; + } + } - const std::array, 2> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; + template + float getMinGenPt() const + { + if constexpr (PartType == kK0S) { + return minPtMcGenConfigs.minPtK0S.value; + } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + return minPtMcGenConfigs.minPtLambda.value; + } else if constexpr (PartType == kXi) { + return minPtMcGenConfigs.minPtXi.value; + } else if constexpr (PartType == kOmega) { + return minPtMcGenConfigs.minPtOmega.value; + } else if constexpr (PartType == kPion) { + return minPtMcGenConfigs.minPtPion.value; + } else { + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in getMinGenPt"); + return 0.f; + } + } - bool doAssocCorrelations[kAssocPartSize] = {activeCorrelationConfigs.doK0SCorrelation, - activeCorrelationConfigs.doXiCorrelation, - activeCorrelationConfigs.doPionCorrelation}; + template + void customFillHist(auto histId, const std::pair& weightPair, Args... coords) + { + auto hist = histos.get(histId); + if (!hist) { + return; + } - const bool applyK0sMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; - auto isK0sValid = [&](const auto& k0s) { - return (!efficiencyConfigs.applyEfficiency || k0s.pt() < binspTK0S->back()) && (!applyK0sMassCut || k0s.inMassRegion(minMassK0s, maxMassK0s)); - }; + // Extract the weight and its propagated uncertainty + auto [w, wErr] = weightPair; - const bool applyPionNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; - const float& pidTPCMax = pionConfigs.pidTPCMax; - const float& pidTOFMax = pionConfigs.pidTOFMax; - // const float& tofPIDThreshold = pionConfigs.tofPIDThreshold; + // Find the global bin number for the given physical coordinates + int bin = hist->FindFixBin(coords...); - auto isPionValid = [&](const auto& pion) { - return (!efficiencyConfigs.applyEfficiency || pion.pt() < binspTPi->back()) && (!applyPionNSigmaCut || pion.inNSigmaRegion(pidTPCMax, pidTOFMax)); - }; + // Retrieve the previous bin content and its absolute error + double prevContent = hist->GetBinContent(bin); + double prevErr = hist->GetBinError(bin); - for (const auto& phiCand : phiCandidates) { - if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) - continue; + // Calculate the new content by adding the current weight + double newContent = prevContent + w; - float weightPhi = computeWeight(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y())); + // Error propagation in quadrature: + // prevErr^2 : previous accumulated variance + // (w * w) : statistical fluctuation added by this specific particle count + // (wErr * wErr) : systematic uncertainty added by the efficiency map inaccuracy + double newErr = std::sqrt(prevErr * prevErr + (w * w) + (wErr * wErr)); - histos.fill(HIST("phi/h3PhiData"), multiplicity, phiCand.pt(), phiCand.m(), weightPhi); + // Update the histogram bin + hist->SetBinContent(bin, newContent); + hist->SetBinError(bin, newErr); + } - auto processCorrelations = [&](auto fillK0S, auto fillPion) { - if (doAssocCorrelations[kK0S]) { - // Loop over all reduced K0S candidates - for (const auto& k0s : k0sReduced) { - if (!isK0sValid(k0s)) - continue; + template + void customFillTHn(auto histId, const std::pair& weightPair, Args... coords) + { + auto hist = histos.get(histId); + if (!hist) { + return; + } - float weightPhiK0S = computeWeight(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMapsAssoc[kK0S], multiplicity, k0s.pt(), k0s.y())); - fillK0S(k0s, weightPhiK0S); - } - } + // Extract the weight and its propagated uncertainty + auto [w, wErr] = weightPair; - if (doAssocCorrelations[kPion]) { - // Loop over all primary pion candidates - for (const auto& pionTrack : pionTracks) { - if (!isPionValid(pionTrack)) - continue; + // Create an array of floats for the coordinates to pass to GetBin and SetBinContent + std::array coordArray{static_cast(coords)...}; - float weightPhiPion = computeWeight(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMapsAssoc[kPion], multiplicity, pionTrack.pt(), pionTrack.y())); - fillPion(pionTrack, weightPhiPion); - } - } - }; - - if (analysisMode == kMassvsMass) { - auto k0sHistID = HIST("phiK0S/h6PhiK0SData"); - auto piTPCHistID = HIST("phiPi/h6PhiPiTPCData"); - auto piTOFHistID = HIST("phiPi/h6PhiPiTOFData"); - - processCorrelations( - [&](const auto& k0s, float w) { - histos.fill(k0sHistID, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), phiCand.m(), k0s.m(), w); - }, - [&](const auto& pion, float w) { - histos.fill(piTPCHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTPC(), w); - histos.fill(piTOFHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTOF(), w); - }); - } else if (analysisMode == kDeltaYvsDeltaPhi) { - auto k0sHistID = std::make_tuple(HIST("phiK0S/h5PhiK0SDataSignal"), HIST("phiK0S/h5PhiK0SDataSideband")); - auto piHistID = std::make_tuple(HIST("phiPi/h5PhiPiDataSignal"), HIST("phiPi/h5PhiPiDataSideband")); - - static_for<0, phiMassRegionLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; - - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[i]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; + // Find the bin number. + // The 'true' flag is mandatory: it allocates the bin in memory if it doesn't exist yet. + auto bin = hist->GetBin(coordArray.data(), true); - // auto k0sHistID = HIST("phiK0S/h5PhiK0SData") + HIST(phiMassRegionLabels[i]); - // auto piHistID = HIST("phiPi/h5PhiPiData") + HIST(phiMassRegionLabels[i]); + // Retrieve the previous content and the squared error (variance) + double prevContent = hist->GetBinContent(bin); + double prevErr2 = hist->GetBinError2(bin); - processCorrelations( - [&](const auto& k0s, float w) { - histos.fill(std::get(k0sHistID), multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi()), w); - }, - [&](const auto& pion, float w) { - histos.fill(std::get(piHistID), multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), getDeltaPhi(phiCand.phi(), pion.phi()), w); - }); - }); - } + // Calculate the new content + double newContent = prevContent + w; - /*static_for<0, phiMassRegionLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; + // Add the new variances to the accumulated variance + // (w * w) is the statistical term, (wErr * wErr) is the map uncertainty term + double newErr2 = prevErr2 + (w * w) + (wErr * wErr); - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[i]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; + // Update the THnSparse bin + hist->SetBinContent(bin, newContent); + hist->SetBinError2(bin, newErr2); // Note: SetBinError2 takes the squared error directly + } - // Loop over all reduced K0S candidates - for (const auto& k0s : k0sReduced) { - if (k0sConfigs.selectK0sInSigRegion) { - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; - if (!k0s.inMassRegion(minMassK0s, maxMassK0s)) - continue; - } + template + bool isAssocValid(TAssoc const& assoc) + { + if constexpr (PartType == kK0S) { + const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; + const auto& [minMass, maxMass] = k0sConfigs.rangeMK0sSignal.value; + return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTK0S->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); + } else if constexpr (PartType == kLambda || PartType == kAntiLambda) { + const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && lambdaConfigs.selectLambdaInSigRegion; + const auto& [minMass, maxMass] = lambdaConfigs.rangeMLambdaSignal.value; + return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTLambda->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); + } else if constexpr (PartType == kXi) { + const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && xiConfigs.selectXiInSigRegion; + const auto& [minMass, maxMass] = xiConfigs.rangeMXiSignal.value; + return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTXi->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); + } else if constexpr (PartType == kOmega) { + const bool applyMassCut = (analysisMode == kDeltaYvsDeltaPhi) && omegaConfigs.selectOmegaInSigRegion; + const auto& [minMass, maxMass] = omegaConfigs.rangeMOmegaSignal.value; + return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTOmega->back()) && (!applyMassCut || assoc.inMassRegion(minMass, maxMass)); + } else if constexpr (PartType == kPion) { + const bool applyNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; + return (!efficiencyConfigs.applyEfficiency || assoc.pt() < binspTPi->back()) && + (!applyNSigmaCut || assoc.inNSigmaRegion(pionConfigs.pidTPCMax, pionConfigs.pidTOFMax)); + } else { + static_assert(PartType == kK0S || PartType == kLambda || PartType == kAntiLambda || + PartType == kXi || PartType == kOmega || PartType == kPion, + "Unsupported particle type in isAssocValid"); + return false; + } + } - float weightPhiK0S = computeWeight(BoundEfficiencyMap(effMaps[Phi], multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMaps[K0S], multiplicity, k0s.pt(), k0s.y())); + template + bool isGenSpeciesValid(const auto& mcParticle, bool allowBypass) const + { + return mcParticle.isPhysicalPrimary() && (allowBypass || mcParticle.pt() >= getMinGenPt()); + } - histos.fill(HIST("phiK0S/h5PhiK0SData") + HIST(phiMassRegionLabels[i]), multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi()), weightPhiK0S); - } + template + void fillPhiAssocCorrelation(TPhiCand const& phiCand, TAssoc const& assoc, float multiplicity, const std::pair& weight) + { + const std::array, kPhiMassRegions> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; - // Loop over all primary pion candidates - for (const auto& pionTrack : pionTracks) { - float weightPhiPion = computeWeight(BoundEfficiencyMap(effMaps[Phi], multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMaps[Pion], multiplicity, pionTrack.pt(), pionTrack.y())); + float deltaY = phiCand.y() - assoc.y(); + float deltaPhi = getDeltaPhi(phiCand.phi(), assoc.phi()); - histos.fill(HIST("phiPi/h5PhiPiData") + HIST(phiMassRegionLabels[i]), multiplicity, phiCand.pt(), pionTrack.pt(), phiCand.y() - pionTrack.y(), getDeltaPhi(phiCand.phi(), pionTrack.phi()), weightPhiPion); - } - });*/ - } + if (analysisMode == kMassvsMass) { + +#define FILL_MASS_CASE(PART_ENUM, PART_STR) \ + if constexpr (PartType == PART_ENUM) { \ + if constexpr (IsME) { \ + customFillTHn(HIST("phi" PART_STR "/h6Phi" PART_STR "DataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.m()); \ + } else { \ + customFillTHn(HIST("phi" PART_STR "/h6Phi" PART_STR "Data"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.m()); \ + } \ } - // PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiK0SPionSE, "Process function for Phi-K0S and Phi-Pion Deltay and Deltaphi 2D Correlations in SE", true); + FILL_MASS_CASE(kK0S, "K0S"); + FILL_MASS_CASE(kLambda, "Lambda"); + FILL_MASS_CASE(kAntiLambda, "AntiLambda"); + FILL_MASS_CASE(kXi, "Xi"); + FILL_MASS_CASE(kOmega, "Omega"); + if constexpr (PartType == kPion) { + if constexpr (IsME) { + customFillTHn(HIST("phiPi/h6PhiPiTPCDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTPC()); + customFillTHn(HIST("phiPi/h6PhiPiTOFDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTOF()); + } else { + customFillTHn(HIST("phiPi/h6PhiPiTPCData"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTPC()); + customFillTHn(HIST("phiPi/h6PhiPiTOFData"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, phiCand.m(), assoc.nSigmaTOF()); + } + } +#undef FILL_MASS_CASE - void processPhiK0SPionSEDataLike(SelCollisions::iterator const& collision, aod::PhimesonCandidatesData const& phiCandidates, aod::K0sReducedCandidatesData const& k0sReduced, aod::PionTracksData const& pionTracks) - { - processPhiK0SPionSE(collision, phiCandidates, k0sReduced, pionTracks); + /*if constexpr (PartType == kK0S) { + if constexpr (IsME) { + customFillTHn(HIST("phiK0S/h6PhiK0SDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiK0S/h6PhiK0SData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } + } else if constexpr (PartType == kLambda) { + if constexpr (IsME) { + customFillTHn(HIST("phiLambda/h6PhiLambdaDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiLambda/h6PhiLambdaData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } + } else if constexpr (PartType == kAntiLambda) { + if constexpr (IsME) { + customFillTHn(HIST("phiAntiLambda/h6PhiAntiLambdaDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiAntiLambda/h6PhiAntiLambdaData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } + } else if constexpr (PartType == kXi) { + if constexpr (IsME) { + customFillTHn(HIST("phiXi/h6PhiXiDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiXi/h6PhiXiData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } + } else if constexpr (PartType == kOmega) { + if constexpr (IsME) { + customFillTHn(HIST("phiOmega/h6PhiOmegaDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } else { + customFillTHn(HIST("phiOmega/h6PhiOmegaData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.m()); + } + } else if constexpr (PartType == kPion) { + if constexpr (IsME) { + customFillTHn(HIST("phiPi/h6PhiPiTPCDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.nSigmaTPC()); + customFillTHn(HIST("phiPi/h6PhiPiTOFDataME"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.nSigmaTOF()); + } else { + customFillTHn(HIST("phiPi/h6PhiPiTPCData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.nSigmaTPC()); + customFillTHn(HIST("phiPi/h6PhiPiTOFData"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), phiCand.m(), assoc.nSigmaTOF()); + } + }*/ + } else if (analysisMode == kDeltaYvsDeltaPhi) { + std::function fillSignal; + std::function fillSideband; + +#define FILL_DYDP_CASE(PART_ENUM, PART_STR) \ + if constexpr (PartType == PART_ENUM) { \ + if constexpr (IsME) { \ + fillSignal = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + fillSideband = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + } else { \ + fillSignal = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + fillSideband = [&]() { customFillTHn(HIST("phi" PART_STR "/h5Phi" PART_STR "DataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), deltaY, deltaPhi); }; \ + } \ } - PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiK0SPionSEDataLike, "Process function for Phi-K0S and Phi-Pion 2D Correlations in Data or MC w/o PDG SE", true); + FILL_DYDP_CASE(kK0S, "K0S"); + FILL_DYDP_CASE(kLambda, "Lambda"); + FILL_DYDP_CASE(kAntiLambda, "AntiLambda"); + FILL_DYDP_CASE(kXi, "Xi"); + FILL_DYDP_CASE(kOmega, "Omega"); + FILL_DYDP_CASE(kPion, "Pi"); +#undef FILL_DYDP_CASE + + /*if constexpr (PartType == kK0S) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiK0S/h5PhiK0SDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } + } else if constexpr (PartType == kLambda) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiLambda/h5PhiLambdaDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } + } else if constexpr (PartType == kAntiLambda) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiAntiLambda/h5PhiAntiLambdaDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } + } else if constexpr (PartType == kXi) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiXi/h5PhiXiDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } + } else if constexpr (PartType == kOmega) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiOmega/h5PhiOmegaDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } + } else if constexpr (PartType == kPion) { + if constexpr (IsME) { + fillSignal = [&]() { customFillTHn(HIST("phiPi/h5PhiPiDataMESignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiPi/h5PhiPiDataMESideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } else { + fillSignal = [&]() { customFillTHn(HIST("phiPi/h5PhiPiDataSignal"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + fillSideband = [&]() { customFillTHn(HIST("phiPi/h5PhiPiDataSideband"), weight, multiplicity, phiCand.pt(), assoc.pt(), phiCand.y() - assoc.y(), getDeltaPhi(phiCand.phi(), assoc.phi())); }; + } + }*/ - void processPhiK0SPionSEMCWithPDG(SimCollisions::iterator const& collision, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::K0sReducedCandidatesMcReco const& k0sReduced, aod::PionTracksMcReco const& pionTracks) - { - processPhiK0SPionSE(collision, phiCandidates, k0sReduced, pionTracks); + static_for<0, kPhiMassRegions - 1>([&](auto i_idx) { + constexpr unsigned int Idx = i_idx.value; + const auto& [minMassPhi, maxMassPhi] = phiMassRegions[Idx]; + if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) { + return; + } + if constexpr (Idx == kSignalRegion) + fillSignal(); + else + fillSideband(); + }); + } } - PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiK0SPionSEMCWithPDG, "Process function for Phi-K0S and Phi-Pion 2D Correlations in MC with PDG SE", true); - - template - void processPhiK0SME(TCollisions const& collisions, TPhiCands const& phiCandidates, TK0SCands const& k0sReduced) + template + void processOneAssocPair(TPhiCand const& phiCand, TAssoc const& assoc, float multiplicity) { - const std::array, 2> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; - - const bool applyK0sMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; + if (!isAssocValid(assoc)) { + return; + } - auto isK0sValid = [&](const auto& k0s) { - return (!efficiencyConfigs.applyEfficiency || k0s.pt() < binspTK0S->back()) && (!applyK0sMassCut || k0s.inMassRegion(minMassK0s, maxMassK0s)); - }; + auto weight = computeAssocWeight(multiplicity, phiCand, assoc); + fillPhiAssocCorrelation(phiCand, assoc, multiplicity, weight); + } - auto tuplePhiK0S = std::make_tuple(phiCandidates, k0sReduced); - Pair pairPhiK0S{binningOnVertexAndCent, cfgNoMixedEvents, -1, collisions, tuplePhiK0S, &cache}; + template + void processOneAssocSpeciesSE(TPhiCand const& phiCand, TAssocCands const& assocCands, float multiplicity) + { + if (!activeCorrelationTypes->at(PartType)) { + return; + } - for (const auto& [c1, phiCands, c2, k0sRed] : pairPhiK0S) { + for (const auto& assoc : assocCands) { + processOneAssocPair(phiCand, assoc, multiplicity); + } + } - float multiplicity = c1.centFT0M(); + template + void processPhiAssocSE(TCollision const& collision, TPhiCands const& phiCandidates, TAssocInputs const&... assocInputs) + { + float multiplicity = collision.centFT0M(); - for (const auto& [phiCand, k0s] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(phiCands, k0sRed))) { - if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) - continue; - if (!isK0sValid(k0s)) - continue; + if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.perPeriodEfficiency) { + auto bc = collision.template bc_as(); + loadEfficiencyMaps(bc.runNumber()); + } - auto processCorrelations = [&](auto fillK0S) { - float weightPhiK0S = computeWeight(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMapsAssoc[kK0S], multiplicity, k0s.pt(), k0s.y())); - fillK0S(k0s, weightPhiK0S); - }; + for (const auto& phiCand : phiCandidates) { + if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) { + continue; + } - if (analysisMode == kMassvsMass) { - auto k0sHistID = HIST("phiK0S/h6PhiK0SDataME"); + auto weightPhi = computeWeightAndError(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y())); + customFillHist(HIST("phi/h3PhiData"), weightPhi, multiplicity, phiCand.pt(), phiCand.m()); - processCorrelations( - [&](const auto& k0s, float w) { - histos.fill(k0sHistID, multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), phiCand.m(), k0s.m(), w); - }); - } else if (analysisMode == kDeltaYvsDeltaPhi) { - auto k0sHistID = std::make_tuple(HIST("phiK0S/h5PhiK0SDataMESignal"), HIST("phiK0S/h5PhiK0SDataMESideband")); + // Fold expression: elaborate the correlation for each associated particle type in the parameter pack + (processOneAssocSpeciesSE(phiCand, *assocInputs.candidates, multiplicity), ...); + } + } - static_for<0, phiMassRegionLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; + void processPhiAssocSEDataLike(SelCollisions::iterator const& collision, + aod::PhimesonCandidatesData const& phiCandidates, + aod::K0sReducedCandidatesData const& k0sReduced, + aod::LambdaReducedCandidatesData const& lambdaReduced, + aod::AntiLambdaReducedCandidatesData const& antilambdaReduced, + aod::XiReducedCandidatesData const& xiReduced, + aod::OmegaReducedCandidatesData const& omegaReduced, + aod::PionTracksData const& pionTracks, + aod::BCs const&) + { + processPhiAssocSE(collision, phiCandidates, + makeAssocInput(k0sReduced), + makeAssocInput(lambdaReduced), + makeAssocInput(antilambdaReduced), + makeAssocInput(xiReduced), + makeAssocInput(omegaReduced), + makeAssocInput(pionTracks)); + } - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[i]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAssocSEDataLike, "Process function for Phi-Associated 2D Correlations in Data or MC w/o PDG SE", true); + + void processPhiAssocSEMCWithPDG(SimCollisions::iterator const& collision, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::K0sReducedCandidatesMcReco const& k0sReduced, + aod::LambdaReducedCandidatesMcReco const& lambdaReduced, + aod::AntiLambdaReducedCandidatesMcReco const& antilambdaReduced, + aod::XiReducedCandidatesMcReco const& xiReduced, + aod::OmegaReducedCandidatesMcReco const& omegaReduced, + aod::PionTracksMcReco const& pionTracks, + aod::BCs const&) + { + processPhiAssocSE(collision, phiCandidates, + makeAssocInput(k0sReduced), + makeAssocInput(lambdaReduced), + makeAssocInput(antilambdaReduced), + makeAssocInput(xiReduced), + makeAssocInput(omegaReduced), + makeAssocInput(pionTracks)); + } - processCorrelations( - [&](const auto& k0s, float w) { - histos.fill(std::get(k0sHistID), multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi()), w); - }); - }); - } + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAssocSEMCWithPDG, "Process function for Phi-Associated 2D Correlations in MC with PDG SE", true); - /*static_for<0, phiMassRegionLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; + template + void processPhiAssocME(TCollisions const& collisions, TPhiCands const& phiCandidates, TAssocCands const& assocCands) + { + auto tuplePhiAssoc = std::make_tuple(phiCandidates, assocCands); + Pair pairPhiAssoc{binningOnVertexAndCent, cfgNoMixedEvents, -1, collisions, tuplePhiAssoc, &cache}; - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[i]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; + for (const auto& [c1, phiCands, c2, assocRed] : pairPhiAssoc) { + float multiplicity = c1.centFT0M(); - if (k0sConfigs.selectK0sInSigRegion) { - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; - if (!k0s.inMassRegion(minMassK0s, maxMassK0s)) - return; - } + if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.perPeriodEfficiency) { + auto bc = c1.template bc_as(); + loadEfficiencyMaps(bc.runNumber()); + } - float weightPhiK0S = computeWeight(BoundEfficiencyMap(effMaps[Phi], multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMaps[K0S], multiplicity, k0s.pt(), k0s.y())); + for (const auto& [phiCand, assoc] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(phiCands, assocRed))) { + if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) { + continue; + } - histos.fill(HIST("phiK0S/h5PhiK0SDataME") + HIST(phiMassRegionLabels[i]), multiplicity, phiCand.pt(), k0s.pt(), phiCand.y() - k0s.y(), getDeltaPhi(phiCand.phi(), k0s.phi()), weightPhiK0S); - });*/ + processOneAssocPair(phiCand, assoc, multiplicity); } } } - // PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiK0SME, "Process function for Phi-K0S and Deltay and Deltaphi 2D Correlations in Data ME", false); - - void processPhiK0SMEDataLike(SelCollisions const& collisions, aod::PhimesonCandidatesData const& phiCandidates, aod::K0sReducedCandidatesData const& k0sReduced) +#define DO_PROCESS_SWITCH(CLASS, FUNC_NAME, DESC) \ + PROCESS_SWITCH(CLASS, FUNC_NAME, DESC, true) + +#define DEFINE_ME_PROCESSES(PART_NAME, PART_ENUM, TABLE_DATA, TABLE_MC) \ + void processPhi##PART_NAME##MEDataLike(SelCollisions const& collisions, \ + aod::PhimesonCandidatesData const& phiCandidates, \ + TABLE_DATA const& assocReduced, \ + aod::BCs const&) \ + { \ + processPhiAssocME(collisions, phiCandidates, assocReduced); \ + } \ + \ + DO_PROCESS_SWITCH(PhiStrangeCorrelation, processPhi##PART_NAME##MEDataLike, "Phi-" #PART_NAME " ME Data or MC w/o PDG"); \ + \ + void processPhi##PART_NAME##MEMCWithPDG(SimCollisions const& collisions, \ + aod::PhimesonCandidatesMcReco const& phiCandidates, \ + TABLE_MC const& assocReduced, \ + aod::BCs const&) \ + { \ + processPhiAssocME(collisions, phiCandidates, assocReduced); \ + } \ + \ + DO_PROCESS_SWITCH(PhiStrangeCorrelation, processPhi##PART_NAME##MEMCWithPDG, "Phi-" #PART_NAME " ME MC w/ PDG"); + + DEFINE_ME_PROCESSES(K0S, kK0S, aod::K0sReducedCandidatesData, aod::K0sReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Lambda, kLambda, aod::LambdaReducedCandidatesData, aod::LambdaReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(AntiLambda, kAntiLambda, aod::AntiLambdaReducedCandidatesData, aod::AntiLambdaReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Xi, kXi, aod::XiReducedCandidatesData, aod::XiReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Omega, kOmega, aod::OmegaReducedCandidatesData, aod::OmegaReducedCandidatesMcReco) + DEFINE_ME_PROCESSES(Pion, kPion, aod::PionTracksData, aod::PionTracksMcReco) +#undef DEFINE_ME_PROCESSES +#undef DO_PROCESS_SWITCH + + /*void processPhiK0SMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::K0sReducedCandidatesData const& k0sReduced, + aod::BCs const&) { - processPhiK0SME(collisions, phiCandidates, k0sReduced); + processPhiAssocME(collisions, phiCandidates, k0sReduced); } - PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiK0SMEDataLike, "Process function for Phi-K0S 2D Correlations in Data or MC w/o PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiK0SMEDataLike, "Process function for Phi-K0S 2D Correlations in Data or MC w/o PDG ME", true); - void processPhiK0SMEMCWithPDG(SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::K0sReducedCandidatesMcReco const& k0sReduced) + void processPhiK0SMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::K0sReducedCandidatesMcReco const& k0sReduced, + aod::BCs const&) { - processPhiK0SME(collisions, phiCandidates, k0sReduced); + processPhiAssocME(collisions, phiCandidates, k0sReduced); } - PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiK0SMEMCWithPDG, "Process function for Phi-K0S 2D Correlations in MC with PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiK0SMEMCWithPDG, "Process function for Phi-K0S 2D Correlations in MC with PDG ME", true); - template - void processPhiPionME(TCollisions const& collisions, TPhiCands const& phiCandidates, TPionCands const& pionTracks) + void processPhiLambdaMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::LambdaReducedCandidatesData const& lambdaReduced, + aod::BCs const&) { - const std::array, 2> phiMassRegions = {phiConfigs.rangeMPhiSignal, phiConfigs.rangeMPhiSideband}; - - const bool applyPionNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; - const float& pidTPCMax = pionConfigs.pidTPCMax; - const float& pidTOFMax = pionConfigs.pidTOFMax; - // const float& tofPIDThreshold = pionConfigs.tofPIDThreshold; - - auto isPionValid = [&](const auto& pion) { - return (!efficiencyConfigs.applyEfficiency || pion.pt() < binspTPi->back()) && (!applyPionNSigmaCut || pion.inNSigmaRegion(pidTPCMax, pidTOFMax)); - }; - - auto tuplePhiPion = std::make_tuple(phiCandidates, pionTracks); - Pair pairPhiPion{binningOnVertexAndCent, cfgNoMixedEvents, -1, collisions, tuplePhiPion, &cache}; - - for (const auto& [c1, phiCands, c2, piTracks] : pairPhiPion) { - - float multiplicity = c1.centFT0M(); - - for (const auto& [phiCand, piTrack] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(phiCands, piTracks))) { - if (efficiencyConfigs.applyEfficiency && efficiencyConfigs.applyPhiEfficiency && phiCand.pt() >= binspTPhi->back()) - continue; - if (!isPionValid(piTrack)) - continue; + processPhiAssocME(collisions, phiCandidates, lambdaReduced); + } - auto processCorrelations = [&](auto fillPion) { - float weightPhiPion = computeWeight(BoundEfficiencyMap(effMapPhi, multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMapsAssoc[kPion], multiplicity, piTrack.pt(), piTrack.y())); - fillPion(piTrack, weightPhiPion); - }; + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiLambdaMEDataLike, "Process function for Phi-Lambda 2D Correlations in Data or MC w/o PDG ME", true); - if (analysisMode == kMassvsMass) { - auto piTPCHistID = HIST("phiPi/h6PhiPiTPCDataME"); - auto piTOFHistID = HIST("phiPi/h6PhiPiTOFDataME"); + void processPhiLambdaMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::LambdaReducedCandidatesMcReco const& lambdaReduced, + aod::BCs const&) + { + processPhiAssocME(collisions, phiCandidates, lambdaReduced); + } - processCorrelations( - [&](const auto& pion, float w) { - histos.fill(piTPCHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTPC(), w); - histos.fill(piTOFHistID, multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), phiCand.m(), pion.nSigmaTOF(), w); - }); - } else if (analysisMode == kDeltaYvsDeltaPhi) { - auto piHistID = std::make_tuple(HIST("phiPi/h5PhiPiDataMESignal"), HIST("phiPi/h5PhiPiDataMESideband")); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiLambdaMEMCWithPDG, "Process function for Phi-Lambda 2D Correlations in MC with PDG ME", true); - static_for<0, phiMassRegionLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; + void processPhiAntiLambdaMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::AntiLambdaReducedCandidatesData const& antilambdaReduced, + aod::BCs const&) + { + processPhiAssocME(collisions, phiCandidates, antilambdaReduced); + } - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[i]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAntiLambdaMEDataLike, "Process function for Phi-AntiLambda 2D Correlations in Data or MC w/o PDG ME", true); - processCorrelations( - [&](const auto& pion, float w) { - histos.fill(std::get(piHistID), multiplicity, phiCand.pt(), pion.pt(), phiCand.y() - pion.y(), getDeltaPhi(phiCand.phi(), pion.phi()), w); - }); - }); - } + void processPhiAntiLambdaMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::AntiLambdaReducedCandidatesMcReco const& antilambdaReduced, + aod::BCs const&) + { + processPhiAssocME(collisions, phiCandidates, antilambdaReduced); + } - /*static_for<0, phiMassRegionLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiAntiLambdaMEMCWithPDG, "Process function for Phi-AntiLambda 2D Correlations in MC with PDG ME", true); - const auto& [minMassPhi, maxMassPhi] = phiMassRegions[i]; - if (!phiCand.inMassRegion(minMassPhi, maxMassPhi)) - return; + void processPhiXiMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::XiReducedCandidatesData const& xiReduced, + aod::BCs const&) + { + processPhiAssocME(collisions, phiCandidates, xiReduced); + } - float weightPhiPion = computeWeight(BoundEfficiencyMap(effMaps[Phi], multiplicity, phiCand.pt(), phiCand.y()), - BoundEfficiencyMap(effMaps[Pion], multiplicity, piTrack.pt(), piTrack.y())); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiXiMEDataLike, "Process function for Phi-Xi 2D Correlations in Data or MC w/o PDG ME", true); - histos.fill(HIST("phiPi/h5PhiPiDataME") + HIST(phiMassRegionLabels[i]), multiplicity, phiCand.pt(), piTrack.pt(), phiCand.y() - piTrack.y(), getDeltaPhi(phiCand.phi(), piTrack.phi()), weightPhiPion); - });*/ - } - } + void processPhiXiMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::XiReducedCandidatesMcReco const& xiReduced, + aod::BCs const&) + { + processPhiAssocME(collisions, phiCandidates, xiReduced); } - // PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiPionME, "Process function for Phi-Pion Deltay and Deltaphi 2D Correlations in Data ME", false); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiXiMEMCWithPDG, "Process function for Phi-Xi 2D Correlations in MC with PDG ME", true); - void processPhiPionMEDataLike(SelCollisions const& collisions, aod::PhimesonCandidatesData const& phiCandidates, aod::PionTracksData const& pionTracks) + void processPhiOmegaMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::OmegaReducedCandidatesData const& omegaReduced, + aod::BCs const&) { - processPhiPionME(collisions, phiCandidates, pionTracks); + processPhiAssocME(collisions, phiCandidates, omegaReduced); } - PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiPionMEDataLike, "Process function for Phi-Pion 2D Correlations in Data or MC w/o PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiOmegaMEDataLike, "Process function for Phi-Omega 2D Correlations in Data or MC w/o PDG ME", true); - void processPhiPionMEMCWithPDG(SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::PionTracksMcReco const& pionTracks) + void processPhiOmegaMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::OmegaReducedCandidatesMcReco const& omegaReduced, + aod::BCs const&) { - processPhiPionME(collisions, phiCandidates, pionTracks); + processPhiAssocME(collisions, phiCandidates, omegaReduced); } - PROCESS_SWITCH(PhiStrangenessCorrelation, processPhiPionMEMCWithPDG, "Process function for Phi-Pion 2D Correlations in MC with PDG ME", true); + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiOmegaMEMCWithPDG, "Process function for Phi-Omega 2D Correlations in MC with PDG ME", true); - void processParticleEfficiency(MCCollisions const& mcCollisions, SimCollisions const& collisions, aod::PhimesonCandidatesMcReco const& phiCandidates, aod::K0sReducedCandidatesMcReco const& k0sReduced, aod::PionTracksMcReco const& pionTracks, aod::McParticles const& mcParticles) + void processPhiPionMEDataLike(SelCollisions const& collisions, + aod::PhimesonCandidatesData const& phiCandidates, + aod::PionTracksData const& pionTracks, + aod::BCs const&) { - const bool applyK0sMassCut = (analysisMode == kDeltaYvsDeltaPhi) && k0sConfigs.selectK0sInSigRegion; - const auto& [minMassK0s, maxMassK0s] = k0sConfigs.rangeMK0sSignal.value; - auto isK0sValid = [&](const auto& k0s) { - return !applyK0sMassCut || k0s.inMassRegion(minMassK0s, maxMassK0s); - }; + processPhiAssocME(collisions, phiCandidates, pionTracks); + } - const bool applyPionNSigmaCut = (analysisMode == kDeltaYvsDeltaPhi) && pionConfigs.selectPionInSigRegion; - const float& pidTPCMax = pionConfigs.pidTPCMax; - const float& pidTOFMax = pionConfigs.pidTOFMax; - // const float& tofPIDThreshold = pionConfigs.tofPIDThreshold; + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEDataLike, "Process function for Phi-Pion 2D Correlations in Data or MC w/o PDG ME", true); - auto isPionValid = [&](const auto& pion) { - return !applyPionNSigmaCut || pion.inNSigmaRegion(pidTPCMax, pidTOFMax); - }; + void processPhiPionMEMCWithPDG(SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::PionTracksMcReco const& pionTracks, + aod::BCs const&) + { + processPhiAssocME(collisions, phiCandidates, pionTracks); + } + PROCESS_SWITCH(PhiStrangeCorrelation, processPhiPionMEMCWithPDG, "Process function for Phi-Pion 2D Correlations in MC with PDG ME", true);*/ + + void processParticleEfficiency(MCCollisions const& mcCollisions, + SimCollisions const& collisions, + aod::PhimesonCandidatesMcReco const& phiCandidates, + aod::K0sReducedCandidatesMcReco const& k0sReduced, + aod::LambdaReducedCandidatesMcReco const& lambdaReduced, + aod::AntiLambdaReducedCandidatesMcReco const& antilambdaReduced, + aod::XiReducedCandidatesMcReco const& xiReduced, + aod::OmegaReducedCandidatesMcReco const& omegaReduced, + aod::PionTracksMcReco const& pionTracks, aod::McParticles const& mcParticles) + { std::unordered_map> collsGrouped; collsGrouped.reserve(mcCollisions.size()); for (const auto& collision : collisions) { - if (!collision.has_mcCollision()) + if (!collision.has_mcCollision()) { continue; + } const auto& mcCollision = collision.mcCollision_as(); collsGrouped[mcCollision.globalIndex()].push_back(collision.globalIndex()); } @@ -880,22 +1289,28 @@ struct PhiStrangenessCorrelation { } } - const auto k0sThisColl = k0sReduced.sliceBy(preslices.k0sMcRecoPerCollision, collision.globalIndex()); - const auto pionTracksThisColl = pionTracks.sliceBy(preslices.pionTrackMcRecoPerCollision, collision.globalIndex()); + auto fillRecoAssocSpecies = [&](TAssocInputs const& assocCands, auto histoKey) { + if (!activeCorrelationTypes->at(TAssocInputs::Type)) { + return; + } - for (const auto& k0s : k0sThisColl) { - if (!isK0sValid(k0s)) - continue; + const auto assocThisColl = assocCands.candidates->sliceBy(getPreslice(), collision.globalIndex()); - histos.fill(HIST("k0s/h4K0SMCReco"), collision.posZ(), mcCollision.centFT0M(), k0s.pt(), k0s.y()); - } + for (const auto& assoc : assocThisColl) { + if (!isAssocValid(assoc)) { + continue; + } - for (const auto& pionTrack : pionTracksThisColl) { - if (!isPionValid(pionTrack)) - continue; + histos.fill(histoKey, collision.posZ(), mcCollision.centFT0M(), assoc.pt(), assoc.y()); + } + }; // NOLINT(readability/braces) - histos.fill(HIST("pi/h4PiMCReco"), collision.posZ(), mcCollision.centFT0M(), pionTrack.pt(), pionTrack.y()); - } + fillRecoAssocSpecies(makeAssocInput(k0sReduced), HIST("k0s/h4K0SMCReco")); + fillRecoAssocSpecies(makeAssocInput(lambdaReduced), HIST("lambda/h4LambdaMCReco")); + fillRecoAssocSpecies(makeAssocInput(antilambdaReduced), HIST("antilambda/h4AntiLambdaMCReco")); + fillRecoAssocSpecies(makeAssocInput(xiReduced), HIST("xi/h4XiMCReco")); + fillRecoAssocSpecies(makeAssocInput(omegaReduced), HIST("omega/h4OmegaMCReco")); + fillRecoAssocSpecies(makeAssocInput(pionTracks), HIST("pi/h4PiMCReco")); numberAssocColls++; } @@ -906,7 +1321,6 @@ struct PhiStrangenessCorrelation { const bool hasAssoc = (numberAssocColls > 0); const float zVtxRef = hasAssoc ? zVtxs[0] : 0.0f; - //////TOBECHANGED////// if (hasAssoc) { if (zVtxs.size() > 1) { for (size_t i = 1; i < zVtxs.size(); ++i) { @@ -917,36 +1331,52 @@ struct PhiStrangenessCorrelation { histos.fill(HIST("event/hGenMCAssocRecoMultiplicityPercent"), mcCollision.centFT0M()); histos.fill(HIST("event/h2GenMCAssocRecoVertexZvsMult"), zVtxRef, mcCollision.centFT0M()); } - /////////////////////// const auto mcParticlesThisMcColl = mcParticles.sliceBy(preslices.mcPartPerMcCollision, mcCollision.globalIndex()); - auto inYAcceptance = [&](const auto& mcParticle) { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; - }; + for (const auto& mcParticle : mcParticlesThisMcColl) { + if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { + continue; + } - auto fillGenHistos = [&](auto h3Key, auto h4Key, const auto& mcParticle) { - histos.fill(h3Key, mcCollision.centFT0M(), mcParticle.pt(), mcParticle.y()); - if (hasAssoc) - histos.fill(h4Key, zVtxRef, mcCollision.centFT0M(), mcParticle.pt(), mcParticle.y()); - }; + auto fillGenHistos = [&](auto h3Key, auto h4Key) { + histos.fill(h3Key, mcCollision.centFT0M(), mcParticle.pt(), mcParticle.y()); + if (hasAssoc) { + histos.fill(h4Key, zVtxRef, mcCollision.centFT0M(), mcParticle.pt(), mcParticle.y()); + } + }; - for (const auto& mcParticle : mcParticlesThisMcColl /*| std::views::filter(inYAcceptance)*/) { - if (!inYAcceptance(mcParticle)) - continue; + auto fillGenAssocSpecies = [&](auto h3Key, auto h4Key) { + if (!activeCorrelationTypes->at(PartType) || !isGenSpeciesValid(mcParticle, false)) { + return; + } + + fillGenHistos(h3Key, h4Key); + }; // NOLINT(readability/braces) switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPhiPt) - fillGenHistos(HIST("phi/h3PhiMCGen"), HIST("phi/h4PhiMCGenAssocReco"), mcParticle); + if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPtPhi) { + fillGenHistos(HIST("phi/h3PhiMCGen"), HIST("phi/h4PhiMCGenAssocReco")); + } break; - case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt) - fillGenHistos(HIST("k0s/h3K0SMCGen"), HIST("k0s/h4K0SMCGenAssocReco"), mcParticle); + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("k0s/h3K0SMCGen"), HIST("k0s/h4K0SMCGenAssocReco")); break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut) - fillGenHistos(HIST("pi/h3PiMCGen"), HIST("pi/h4PiMCGenAssocReco"), mcParticle); + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("lambda/h3LambdaMCGen"), HIST("lambda/h4LambdaMCGenAssocReco")); + break; + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("antilambda/h3AntiLambdaMCGen"), HIST("antilambda/h4AntiLambdaMCGenAssocReco")); + break; + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("xi/h3XiMCGen"), HIST("xi/h4XiMCGenAssocReco")); + break; + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("omega/h3OmegaMCGen"), HIST("omega/h4OmegaMCGenAssocReco")); + break; + case getPdgCode(): + fillGenAssocSpecies.template operator()(HIST("pi/h3PiMCGen"), HIST("pi/h4PiMCGenAssocReco")); break; default: break; @@ -955,188 +1385,90 @@ struct PhiStrangenessCorrelation { } } - PROCESS_SWITCH(PhiStrangenessCorrelation, processParticleEfficiency, "Process function for Efficiency Computation for Particles of Interest", false); + PROCESS_SWITCH(PhiStrangeCorrelation, processParticleEfficiency, "Process function for Efficiency Computation for Particles of Interest", false); - /*void processMCGenClosureSE(MCCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) + void processMCGenClosure(MCCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) { float multiplicity = mcCollision.centFT0M(); - std::vector phiIndices; - std::vector k0sIndices; - std::vector pionIndices; - std::vector> assocIndices; - - auto inYAcceptance = [&](const auto& mcParticle) { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; - }; + std::vector phiParticles; + std::array, kAssocPartSize> assocParticles; + /*std::vector k0sParticles; + std::vector lambdaParticles; + std::vector antilambdaParticles; + std::vector xiParticles; + std::vector omegaParticles; + std::vector pionParticles;*/ + // Preliminary loop to fill vectors of particles of interest for the current event, applying pt and y cuts for (const auto& mcParticle : mcParticles) { - if (!inYAcceptance(mcParticle)) + if (std::abs(mcParticle.y()) > yConfigs.cfgYAcceptance) { continue; - - switch (std::abs(mcParticle.pdgCode())) { - case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPhiPt) - phiIndices.push_back(mcParticle.globalIndex()); - break; - case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt) - k0sIndices.push_back(mcParticle.globalIndex()); - break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut) - pionIndices.push_back(mcParticle.globalIndex()); - break; - default: - break; } - } - - assocIndices.push_back(k0sIndices); - assocIndices.push_back(pionIndices); - - for (std::size_t iTrigg{0}; iTrigg < phiIndices.size(); ++iTrigg) { - auto& phiParticle = mcParticles.rawIteratorAt(phiIndices[iTrigg]); - - static_for<0, assocIndices.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; - - for (std::size_t iAssoc{0}; iAssoc < assocIndices[i].size(); ++iAssoc) { - auto& assocParticle = mcParticles.rawIteratorAt(assocIndices[i][iAssoc]); - histos.fill(HIST("mcGenClosure/h5Phi") + HIST(assocParticleLabels[i]) + HIST("ClosureGenSE"), multiplicity, phiParticle.pt(), assocParticle.pt(), phiParticle.y() - assocParticle.y(), getDeltaPhi(phiParticle.phi(), assocParticle.phi())); + auto fillPartCollection = [&]() { + if (activeCorrelationTypes->at(PartType) && isGenSpeciesValid(mcParticle, minPtMcGenConfigs.bypassPtCut.value)) { + assocParticles[PartType].emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); } - }); - } - } - - PROCESS_SWITCH(PhiStrangenessCorrelation, processMCGenClosureSE, "Process function for MC Gen Closure Test in SE", false); - - void processMCGenClosureME(MCCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) - { - float multiplicity = mcCollision.centFT0M(); - - std::vector phiParticles; - std::vector k0sParticles; - std::vector pionParticles; - - auto inYAcceptance = [&](const auto& mcParticle) { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; - }; - - for (const auto& mcParticle : mcParticles) { - if (!inYAcceptance(mcParticle)) - continue; + }; // NOLINT(readability/braces) switch (std::abs(mcParticle.pdgCode())) { case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && mcParticle.pt() >= minPtMcGenConfigs.minPhiPt) + if (eventSelectionType == 0 && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.minPtPhi)) { phiParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); + } break; - case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt) - k0sParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); + case getPdgCode(): + fillPartCollection.template operator()(); break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut) - pionParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); + case getPdgCode(): + fillPartCollection.template operator()(); break; - default: + case getPdgCode(): + fillPartCollection.template operator()(); break; - } - } - - if (phiParticles.empty() && k0sParticles.empty() && pionParticles.empty()) - return; - - int multBin = getCentBin(multiplicity); - - // Loop over past events in the same multiplicity bin and fill histograms with all combinations of current phi particles and past K0S and pion particles - for (const auto& pastEvent : eventBuffer[multBin]) { - for (const auto& phiParticle : phiParticles) { - for (const auto& k0sParticle : pastEvent.k0sParticles) { - histos.fill(HIST("mcGenClosure/h5PhiK0SClosureGenME"), multiplicity, phiParticle.pt, k0sParticle.pt, phiParticle.y - k0sParticle.y, getDeltaPhi(phiParticle.phi, k0sParticle.phi)); - } - for (const auto& pionParticle : pastEvent.pionParticles) { - histos.fill(HIST("mcGenClosure/h5PhiPiClosureGenME"), multiplicity, phiParticle.pt, pionParticle.pt, phiParticle.y - pionParticle.y, getDeltaPhi(phiParticle.phi, pionParticle.phi)); - } - } - } - - // Add current event to buffer - MiniEvent currentEvent; - currentEvent.multiplicity = multiplicity; - currentEvent.phiParticles = std::move(phiParticles); - currentEvent.k0sParticles = std::move(k0sParticles); - currentEvent.pionParticles = std::move(pionParticles); - - eventBuffer[multBin].push_front(std::move(currentEvent)); - if (eventBuffer[multBin].size() > static_cast(cfgNoMixedEvents.value)) - eventBuffer[multBin].pop_back(); - } - - PROCESS_SWITCH(PhiStrangenessCorrelation, processMCGenClosureME, "Process function for MC Gen Closure Test in ME", false);*/ - - void processMCGenClosure(MCCollisions::iterator const& mcCollision, aod::McParticles const& mcParticles) - { - float multiplicity = mcCollision.centFT0M(); - - std::vector phiParticles; - std::vector k0sParticles; - std::vector xiParticles; - std::vector pionParticles; - - auto inYAcceptance = [&](const auto& mcParticle) { - return std::abs(mcParticle.y()) <= yConfigs.cfgYAcceptance; - }; - - // Preliminary loop to fill vectors of particles of interest for the current event, applying pt and y cuts - for (const auto& mcParticle : mcParticles) { - if (!inYAcceptance(mcParticle)) - continue; - - switch (std::abs(mcParticle.pdgCode())) { - case o2::constants::physics::Pdg::kPhi: - if (eventSelectionType == 0 && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.minPhiPt)) - phiParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); + case getPdgCode(): + fillPartCollection.template operator()(); break; - case PDG_t::kK0Short: - if (mcParticle.isPhysicalPrimary() && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.v0SettingMinPt)) - k0sParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); + case getPdgCode(): + fillPartCollection.template operator()(); break; - case PDG_t::kPiPlus: - if (mcParticle.isPhysicalPrimary() && (minPtMcGenConfigs.bypassPtCut || mcParticle.pt() >= minPtMcGenConfigs.cMinPionPtcut)) - pionParticles.emplace_back(mcParticle.pt(), mcParticle.y(), mcParticle.phi()); + case getPdgCode(): + fillPartCollection.template operator()(); break; default: break; } } - if (phiParticles.empty() && k0sParticles.empty() && pionParticles.empty()) + bool hasAnyAssoc = false; + for (size_t i = 0; i < kAssocPartSize; ++i) { + if (!assocParticles[i].empty()) { + hasAnyAssoc = true; + } + } + + if (phiParticles.empty() && !hasAnyAssoc) { return; + } int multBin = getCentBin(multiplicity); - if (multBin < 0) + if (multBin < 0) { return; - - bool doAssocCorrelations[kAssocPartSize] = {activeCorrelationConfigs.doK0SCorrelation, - activeCorrelationConfigs.doXiCorrelation, - activeCorrelationConfigs.doPionCorrelation}; + } // Same Event Correlations - std::vector* currentAssocParticles[] = {&k0sParticles, &xiParticles, &pionParticles}; - for (const auto& phiParticle : phiParticles) { histos.fill(HIST("phi/h3PhiMCClosureGen"), multiplicity, phiParticle.pt, phiParticle.y); - static_for<0, assocParticleLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; - if (!doAssocCorrelations[i]) + static_for<0, AssocParticleLabels.size() - 1>([&](auto i_idx) { + constexpr unsigned int Idx = i_idx.value; + if (!activeCorrelationTypes->at(Idx)) { return; + } - for (const auto& assocParticle : *(currentAssocParticles[i])) { - histos.fill(HIST("phi") + HIST(assocParticleLabels[i]) + HIST("/h5Phi") + HIST(assocParticleLabels[i]) + HIST("ClosureMCGen"), + for (const auto& assocParticle : assocParticles[Idx]) { + histos.fill(HIST("phi") + HIST(AssocParticleLabels[Idx]) + HIST("/h5Phi") + HIST(AssocParticleLabels[Idx]) + HIST("ClosureMCGen"), multiplicity, phiParticle.pt, assocParticle.pt, phiParticle.y - assocParticle.y, getDeltaPhi(phiParticle.phi, assocParticle.phi)); @@ -1145,18 +1477,17 @@ struct PhiStrangenessCorrelation { } // Mixed Event Correlations + // Loop over past events in the same multiplicity bin and fill histograms with all combinations of current phi particles and past associated particles for (const auto& pastEvent : eventBuffer[multBin]) { - const std::vector* pastAssocParticles[] = {&pastEvent.k0sParticles, &pastEvent.xiParticles, &pastEvent.pionParticles}; - - // Loop over past events in the same multiplicity bin and fill histograms with all combinations of current phi particles and past associated particles for (const auto& phiParticle : phiParticles) { - static_for<0, assocParticleLabels.size() - 1>([&](auto i_idx) { - constexpr unsigned int i = i_idx.value; - if (!doAssocCorrelations[i]) + static_for<0, AssocParticleLabels.size() - 1>([&](auto i_idx) { + constexpr unsigned int Idx = i_idx.value; + if (!activeCorrelationTypes->at(Idx)) { return; + } - for (const auto& assocParticle : *(pastAssocParticles[i])) { - histos.fill(HIST("phi") + HIST(assocParticleLabels[i]) + HIST("/h5Phi") + HIST(assocParticleLabels[i]) + HIST("ClosureMCGenME"), + for (const auto& assocParticle : pastEvent.assocParticles[Idx]) { + histos.fill(HIST("phi") + HIST(AssocParticleLabels[Idx]) + HIST("/h5Phi") + HIST(AssocParticleLabels[Idx]) + HIST("ClosureMCGenME"), multiplicity, phiParticle.pt, assocParticle.pt, phiParticle.y - assocParticle.y, getDeltaPhi(phiParticle.phi, assocParticle.phi)); @@ -1168,19 +1499,24 @@ struct PhiStrangenessCorrelation { MiniEvent currentEvent; currentEvent.multiplicity = multiplicity; currentEvent.phiParticles = std::move(phiParticles); - currentEvent.k0sParticles = std::move(k0sParticles); + currentEvent.assocParticles = std::move(assocParticles); + /*currentEvent.k0sParticles = std::move(k0sParticles); + currentEvent.lambdaParticles = std::move(lambdaParticles); + currentEvent.antilambdaParticles = std::move(antilambdaParticles); currentEvent.xiParticles = std::move(xiParticles); - currentEvent.pionParticles = std::move(pionParticles); + currentEvent.omegaParticles = std::move(omegaParticles); + currentEvent.pionParticles = std::move(pionParticles);*/ eventBuffer[multBin].push_front(std::move(currentEvent)); - if (eventBuffer[multBin].size() > static_cast(cfgNoMixedEvents.value)) + if (eventBuffer[multBin].size() > static_cast(cfgNoMixedEvents.value)) { eventBuffer[multBin].pop_back(); + } } - PROCESS_SWITCH(PhiStrangenessCorrelation, processMCGenClosure, "Process function for MC Gen Closure Test in SE and ME", false); + PROCESS_SWITCH(PhiStrangeCorrelation, processMCGenClosure, "Process function for MC Gen Closure Test in SE and ME", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - return WorkflowSpec{adaptAnalysisTask(cfgc)}; + return WorkflowSpec{adaptAnalysisTask(cfgc)}; } diff --git a/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx b/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx index 51975c776f2..40b9cc698cb 100644 --- a/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/phik0shortanalysis.cxx @@ -290,7 +290,7 @@ struct Phik0shortanalysis { ConfigurableAxis axisCentralityMixing{"axisCentralityMixing", {20, 0, 100}, "Multiplicity percentil binning for mixing"}; using BinningTypeVertexCent = ColumnBinningPolicy; - BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}, true}; + BinningTypeVertexCent binningOnVertexAndCent{{axisVertexMixing, axisCentralityMixing}}; // Cache for manual slicing SliceCache cache; diff --git a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx index 73b655ce62c..b7205446f78 100644 --- a/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx +++ b/PWGLF/Tasks/Strangeness/sigmaanalysis.cxx @@ -21,17 +21,21 @@ #include "PWGLF/DataModel/LFSigmaTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" +#include "PWGLF/Utils/ResonanceMlResponse.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/ctpRateFetcher.h" +#include "Tools/ML/MlResponse.h" #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -47,6 +51,7 @@ #include #include +#include #include #include #include @@ -54,6 +59,7 @@ #include using namespace o2; +using namespace o2::ml; using namespace o2::framework; using namespace o2::framework::expressions; @@ -84,11 +90,17 @@ enum CentEstimator { struct sigmaanalysis { Service ccdb; + o2::ccdb::CcdbApi ccdbApi; ctpRateFetcher rateFetcher; + o2::analysis::ResonanceMlResponse mlResponse; //__________________________________________________ HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Species + Configurable doLambdaStar{"doLambdaStar", false, "Build Lambda(1520) instead of Sigma0"}; + Configurable doArm{"doArm", true, "Fill the 3D Armenteros histograms"}; + // Event level Configurable doPPAnalysis{"doPPAnalysis", true, "if in pp, set to true"}; Configurable centralityEstimator{"centralityEstimator", kCentFT0C, "Run 3 centrality estimator (0:CentFT0C, 1:CentFT0M, 2:CentFT0CVariant1, 3:CentMFT, 4:CentNGlobal, 5:CentFV0A)"}; @@ -96,6 +108,24 @@ struct sigmaanalysis { Configurable fIRCrashOnNull{"fIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash."}; Configurable irSource{"irSource", "T0VTX", "Estimator of the interaction rate (Recommended: pp --> T0VTX, Pb-Pb --> ZNC hadronic)"}; + struct : ConfigurableGroup { + std::string prefix = "bdt"; // JSON group name + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"BDTModel.onnx"}, "Local .onnx file names, one per pT bin"}; + Configurable> modelPathsCCDB{"modelPathsCCDB", std::vector{"Users/o/obenchik/MLModels/BDT"}, "Model paths on CCDB, one per pT bin (each model needs its own folder)"}; + Configurable timestampCCDB{"timestampCCDB", 1695750420200, "timestamp of the ONNX file for ML model used to query in CCDB. Please use 1695750420200"}; + Configurable loadModelsFromCCDB{"loadModelsFromCCDB", false, "Flag to enable or disable the loading of models from CCDB"}; + Configurable enableOptimizations{"enableOptimizations", false, "Enables the ONNX extended model-optimization: sessionOptions.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED)"}; + Configurable numThreads{"numThreads", 1, "ONNX intra-op threads. 0 lets ONNX Runtime default to one thread per physical core"}; + Configurable enableML{"enableML", false, "Enables bdt model"}; + Configurable> ptBinEdges{"ptBinEdges", {0., 30.}, "Candidate-pT bin edges of the BDT models, one model per bin (candidates outside are rejected)"}; + Configurable> scoreCuts{"scoreCuts", {std::array{0., 0.}.data(), 1, 2, {"pT bin 0"}, {"Background score", "Signal score"}}, "BDT score cuts, one row per pT bin"}; + Configurable> cutDir{"cutDir", std::vector{o2::cuts_ml::CutNot, o2::cuts_ml::CutNot}, "Cut direction per class: 0 = keep score < cut, 1 = keep score >= cut, 2 = no cut"}; + // Configurable mlCorrThreshold{"mlCorrThreshold", 0.5, "Threshold for correlated K* score"}; + Configurable> namesInputFeatures{"namesInputFeatures", std::vector{"lambdaDCADau", "lambdaAlpha", "lambdaDCANegPV", "lambdaDCAPosPV", "lambdaQt", "photonAlpha", "photonCosPA", "photonDCADau", "photonDCANegPV", "photonDCAPosPV", "photonQt", "photonRadius", "opAngle"}, "Names and order of the BDT input features (see ResonanceMlResponse.h): must match FeaturesToTrain"}; + + } bdt; + struct : ConfigurableGroup { std::string prefix = "eventSelections"; // JSON group name Configurable requireSel8{"requireSel8", true, "require sel8 event selection"}; @@ -283,12 +313,15 @@ struct sigmaanalysis { ConfigurableAxis axisV0Radius{"axisV0Radius", {240, 0.0f, 120.0f}, "V0 radius (cm)"}; ConfigurableAxis axisV0PairRadius{"axisV0PairRadius", {200, 0.0f, 20.0f}, "V0Pair radius (cm)"}; ConfigurableAxis axisDCAtoPV{"axisDCAtoPV", {500, 0.0f, 50.0f}, "DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPV{"axisSignedDCAtoPV", {1000, -50.0f, 50.0f}, "signed DCA (cm)"}; + ConfigurableAxis axisSignedDCAtoPVLambda{"axisSignedDCAtoPVLambda", {500, -10.0f, 10.0f}, "signed DCA (cm)"}; ConfigurableAxis axisDCAdau{"axisDCAdau", {50, 0.0f, 5.0f}, "DCA (cm)"}; ConfigurableAxis axisCosPA{"axisCosPA", {200, 0.5f, 1.0f}, "Cosine of pointing angle"}; ConfigurableAxis axisPA{"axisPA", {100, 0.0f, 1}, "Pointing angle"}; ConfigurableAxis axisPsiPair{"axisPsiPair", {250, -5.0f, 5.0f}, "Psipair for photons"}; ConfigurableAxis axisPhi{"axisPhi", {200, 0, 2 * o2::constants::math::PI}, "Phi for photons"}; ConfigurableAxis axisZ{"axisZ", {120, -120.0f, 120.0f}, "V0 Z position (cm)"}; + ConfigurableAxis axisOPAngle{"axisOPAngle", {140, 0.0f, 7.0f}, "Opening angle (rad)"}; // EMCal-specifc ConfigurableAxis axisClrDefinition{"axisClrDefinition", {51, -0.5, 50.5}, "Cluster Definition"}; @@ -300,7 +333,7 @@ struct sigmaanalysis { ConfigurableAxis axisCandSel{"axisCandSel", {20, 0.5f, +20.5f}, "Candidate Selection"}; // ML - ConfigurableAxis MLProb{"MLOutput", {100, 0.0f, 1.0f}, ""}; + ConfigurableAxis mlProb{"MLOutput", {100, 0.0f, 1.0f}, ""}; int NSigma0Cand = 0; void init(InitContext const&) @@ -316,6 +349,67 @@ struct sigmaanalysis { ccdb->setCaching(true); ccdb->setFatalWhenNull(false); + if (bdt.enableML) { + ccdb->setURL(bdt.ccdbUrl.value); + + // One model per candidate-pT bin + constexpr uint8_t NClassesML = 2; // background, signal + if (bdt.scoreCuts.value.rows() != bdt.ptBinEdges.value.size() - 1 || bdt.scoreCuts.value.cols() != NClassesML) { + LOG(fatal) << "bdt.scoreCuts needs one row per pT bin and " << static_cast(NClassesML) << " columns"; + } + mlResponse.configure(bdt.ptBinEdges.value, bdt.scoreCuts.value, bdt.cutDir.value, NClassesML); + mlResponse.cacheInputFeaturesIndices(bdt.namesInputFeatures); + + if (bdt.loadModelsFromCCDB) { + ccdbApi.init(bdt.ccdbUrl); + LOG(info) << "Fetching models for timestamp: " << bdt.timestampCCDB.value; + mlResponse.setModelPathsCCDB(bdt.onnxFileNames.value, ccdbApi, bdt.modelPathsCCDB.value, bdt.timestampCCDB.value); + } else { + mlResponse.setModelPathsLocal(bdt.onnxFileNames.value); + } + mlResponse.init(bdt.enableOptimizations.value, bdt.numThreads.value); + + // The model is trained on PCM (V0) photon features, which EMCal clusters do not have + if (doprocessRealDataWithEMCal || doprocessMonteCarloWithEMCal) { + LOG(fatal) << "BDT selection is only available for PCM photons: disable bdt.enableML or use processRealData/processMonteCarlo."; + } + + // BDT performance QA + histos.add("BDT/hScoreSignal", "hScoreSignal", kTH1D, {mlProb}); + histos.add("BDT/hScoreBackground", "hScoreBackground", kTH1D, {mlProb}); + histos.add("BDT/h2dScoreVsMassSignal", "h2dScoreVsMassSignal", kTH2D, {axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsPtSignal", "h2dScoreVsPtSignal", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h3dScoreSignal", "h3dScoreSignal", kTH3D, {axisPt, axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsMassBackground", "h2dScoreVsMassBackground", kTH2D, {axisSigmaMass, mlProb}); + histos.add("BDT/h2dScoreVsPtBackground", "h2dScoreVsPtBackground", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h3dScoreBackground", "h3dScoreBackground", kTH3D, {axisPt, axisSigmaMass, mlProb}); + + // Signal score vs the main topological variables + histos.add("BDT/h2dDCADaughters", "h2dDCADaughters", kTH2D, {mlProb, axisDCAdau}); + histos.add("BDT/h2dLambdaAlpha", "h2dLambdaAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dLambdaDCANegPV", "h2dLambdaDCANegPV", kTH2D, {mlProb, axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaDCAPosPV", "h2dLambdaDCAPosPV", kTH2D, {mlProb, axisSignedDCAtoPVLambda}); + histos.add("BDT/h2dLambdaQt", "h2dLambdaQt", kTH2D, {mlProb, axisAPQt}); + histos.add("BDT/h2dPhotonAlpha", "h2dPhotonAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dPhotonCosPA", "h2dPhotonCosPA", kTH2D, {mlProb, axisCosPA}); + histos.add("BDT/h2dPhotonDCADau", "h2dPhotonDCADau", kTH2D, {mlProb, axisDCAdau}); + histos.add("BDT/h2dPhotonDCANegPV", "h2dPhotonDCANegPV", kTH2D, {mlProb, axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonDCAPosPV", "h2dPhotonDCAPosPV", kTH2D, {mlProb, axisSignedDCAtoPV}); + histos.add("BDT/h2dPhotonQt", "h2dPhotonQt", kTH2D, {mlProb, axisAPQt}); + histos.add("BDT/h2dPhotonRadius", "h2dPhotonRadius", kTH2D, {mlProb, axisV0Radius}); + histos.add("BDT/h2dOPAngle", "h2dOPAngle", kTH2D, {mlProb, axisOPAngle}); + histos.add("BDT/h2dAPAlpha", "h2dAPAlpha", kTH2D, {mlProb, axisAPAlpha}); + histos.add("BDT/h2dAPQt", "h2dAPQt", kTH2D, {mlProb, axisAPQt}); + + // MC-truth-based score + if (doprocessMonteCarlo) { + histos.add("BDT/hScoreTrueSignal", "hScoreTrueSignal", kTH1D, {mlProb}); + histos.add("BDT/hScoreTrueBackground", "hScoreTrueBackground", kTH1D, {mlProb}); + histos.add("BDT/h2dScoreVsPtTrueSignal", "h2dScoreVsPtTrueSignal", kTH2D, {axisPt, mlProb}); + histos.add("BDT/h2dScoreVsPtTrueBackground", "h2dScoreVsPtTrueBackground", kTH2D, {axisPt, mlProb}); + } + } + // Event Counters histos.add("hEventCentrality", "hEventCentrality", kTH1D, {axisCentrality}); histos.add("hCentralityVsNch", "hCentralityVsNch", kTH2D, {{101, 0.0f, 101.0f}, axisNch}); @@ -347,6 +441,19 @@ struct sigmaanalysis { histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(19, "Below min IR"); histos.get(HIST("hEventSelection"))->GetXaxis()->SetBinLabel(20, "Above max IR"); + // + if (doprocessAnalysedCollisions) { + histos.add("hEventPreSelection", "hEventPreSelection", kTH1D, {{8, -0.5f, +7.5f}}); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(1, "All collisions"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(2, "kIsTriggerTVX"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(3, "kNoITSROFrameBorder"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(4, "kNoTimeFrameBorder"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(5, "posZ cut"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(6, "kNoSameBunchPileup"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(7, "RCT flags"); + histos.get(HIST("hEventPreSelection"))->GetXaxis()->SetBinLabel(8, "Preselected collisions"); + } + if (fGetIR) { histos.add("GeneralQA/hRunNumberNegativeIR", "", kTH1D, {{1, 0., 1.}}); histos.add("GeneralQA/hInteractionRate", "hInteractionRate", kTH1D, {axisIRBinning}); @@ -439,7 +546,10 @@ struct sigmaanalysis { histos.add(histodir + "/Sigma0/h2dRadiusVspT", "h2dRadiusVspT", kTH2D, {axisV0PairRadius, axisPt}); histos.add(histodir + "/Sigma0/hDCAPairDau", "hDCAPairDau", kTH1D, {axisDCAdau}); histos.add(histodir + "/Sigma0/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisSigmaMass}); - histos.add(histodir + "/Sigma0/h3dOPAngleVsMass", "h3dOPAngleVsMass", kTH3D, {{140, 0.0f, +7.0f}, axisPt, axisSigmaMass}); + histos.add(histodir + "/Sigma0/h3dOPAngleVsMass", "h3dOPAngleVsMass", kTH3D, {axisOPAngle, axisPt, axisSigmaMass}); + if (doArm) { + histos.add(histodir + "/Sigma0/h4dAlphaVsQtarmVsMass", "h4dAlphaVsQtarmVsMass", kTHnD, {axisAPAlpha, axisAPQt, axisPt, axisSigmaMass}); + } histos.add(histodir + "/ASigma0/hMass", "hMass", kTH1D, {axisSigmaMass}); histos.add(histodir + "/ASigma0/hPt", "hPt", kTH1D, {axisPt}); @@ -448,7 +558,10 @@ struct sigmaanalysis { histos.add(histodir + "/ASigma0/h2dRadiusVspT", "h2dRadiusVspT", kTH2D, {axisV0PairRadius, axisPt}); histos.add(histodir + "/ASigma0/hDCAPairDau", "hDCAPairDau", kTH1D, {axisDCAdau}); histos.add(histodir + "/ASigma0/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisSigmaMass}); - histos.add(histodir + "/ASigma0/h3dOPAngleVsMass", "h3dOPAngleVsMass", kTH3D, {{140, 0.0f, +7.0f}, axisPt, axisSigmaMass}); + histos.add(histodir + "/ASigma0/h3dOPAngleVsMass", "h3dOPAngleVsMass", kTH3D, {axisOPAngle, axisPt, axisSigmaMass}); + if (doArm) { + histos.add(histodir + "/ASigma0/h4dAlphaVsQtarmVsMass", "h4dAlphaVsQtarmVsMass", kTHnD, {axisAPAlpha, axisAPQt, axisPt, axisSigmaMass}); + } // Process MC if (doprocessMonteCarlo || doprocessMonteCarloWithEMCal) { @@ -487,6 +600,9 @@ struct sigmaanalysis { histos.add(histodir + "/MC/Sigma0/h2dMCProcessVsGenRadius", "h2dMCProcessVsGenRadius", kTH2D, {{50, -0.5f, 49.5f}, axisV0PairRadius}); histos.add(histodir + "/MC/Sigma0/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisSigmaMass}); histos.add(histodir + "/MC/Sigma0/h3dMCProcess", "h3dMCProcess", kTH3D, {{50, -0.5f, 49.5f}, axisPt, axisSigmaMass}); + if (doArm) { + histos.add(histodir + "/MC/Sigma0/h4dAlphaVsQtarmVsMass", "h4dAlphaVsQtarmVsMass", kTHnD, {axisAPAlpha, axisAPQt, axisPt, axisSigmaMass}); + } histos.add(histodir + "/MC/ASigma0/hPt", "hPt", kTH1D, {axisPt}); histos.add(histodir + "/MC/ASigma0/hMCPt", "hMCPt", kTH1D, {axisPt}); @@ -498,6 +614,12 @@ struct sigmaanalysis { histos.add(histodir + "/MC/ASigma0/h2dMCProcessVsGenRadius", "h2dMCProcessVsGenRadius", kTH2D, {{50, -0.5f, 49.5f}, axisV0PairRadius}); histos.add(histodir + "/MC/ASigma0/h3dMass", "h3dMass", kTH3D, {axisCentrality, axisPt, axisSigmaMass}); histos.add(histodir + "/MC/ASigma0/h3dMCProcess", "h3dMCProcess", kTH3D, {{50, -0.5f, 49.5f}, axisPt, axisSigmaMass}); + if (doArm) { + histos.add(histodir + "/MC/ASigma0/h4dAlphaVsQtarmVsMass", "h4dAlphaVsQtarmVsMass", kTHnD, {axisAPAlpha, axisAPQt, axisPt, axisSigmaMass}); + } + + histos.add(histodir + "/MC/LambdaStar/h3dMCPtvsOPAngle_Sig", "h3dMCPtvsOPAngle_Sig", kTH3D, {{140, 0.f, 7.f}, axisPt, axisSigmaMass}); + histos.add(histodir + "/MC/LambdaStar/h3dMCPtvsOPAngle_Bkg", "h3dMCPtvsOPAngle_Bkg", kTH3D, {{140, 0.f, 7.f}, axisPt, axisSigmaMass}); // pT Resolution: if (fillResoQAhistos) { @@ -765,12 +887,11 @@ struct sigmaanalysis { if (eventSelections.maxIR >= 0 && interactionRate > eventSelections.maxIR) { return false; } - if (fillHists) + if (fillHists) { histos.fill(HIST("hEventSelection"), 19 /* Above max IR */); - - // Fill centrality histogram after event selection - if (fillHists) + // Fill centrality histogram after event selection histos.fill(HIST("hEventCentrality"), centrality); + } histos.fill(HIST("hCentralityVsNch"), centrality, collision.multNTracksPVeta1()); return true; @@ -891,7 +1012,7 @@ struct sigmaanalysis { fillGeneratedEventProperties(mcCollisions, collisions); std::vector listBestCollisionIdx = getListOfRecoCollIndices(mcCollisions, collisions); - for (auto& genParticle : genParticles) { + for (const auto& genParticle : genParticles) { float centrality = 100.5f; // Has MC collision @@ -1037,14 +1158,14 @@ struct sigmaanalysis { //_______________________________________ // Sigma and AntiSigma MC association - if (sigma.isSigma0()) { + if (doLambdaStar ? sigma.isLambdaStar() : sigma.isSigma0()) { histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dSigma0RadiusResolution"), sigma.mcpt(), sigma.radius() - sigma.mcradius()); // pT resolution if (sigma.mcpt() > 0) { histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dSigma0PtResolution"), sigma.mcpt(), (sigma.pt() / sigma.mcpt()) - 1.f); // pT resolution histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dSigma0InvPtResolution"), 1.f / sigma.mcpt(), 1.f / sigma.pt() - 1.f / sigma.mcpt()); // pT resolution } } - if (sigma.isAntiSigma0()) { + if (doLambdaStar ? sigma.isAntiLambdaStar() : sigma.isAntiSigma0()) { histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dASigma0RadiusResolution"), sigma.mcpt(), sigma.radius() - sigma.mcradius()); // pT resolution if (sigma.mcpt() > 0) histos.fill(HIST(MainDir[mode]) + HIST("/MC/Reso/h2dAntiSigma0PtResolution"), 1.f / sigma.mcpt(), 1.f / sigma.pt() - 1.f / sigma.mcpt()); // pT resolution @@ -1058,8 +1179,8 @@ struct sigmaanalysis { // Check whether it is before or after selections static constexpr std::string_view MainDir[] = {"BeforeSel", "AfterSel"}; - bool fIsSigma = sigma.isSigma0(); - bool fIsAntiSigma = sigma.isAntiSigma0(); + bool fIsSigma = doLambdaStar ? sigma.isLambdaStar() : sigma.isSigma0(); + bool fIsAntiSigma = doLambdaStar ? sigma.isAntiLambdaStar() : sigma.isAntiSigma0(); int PhotonPDGCode = sigma.photonPDGCode(); int PhotonPDGCodeMother = sigma.photonPDGCodeMother(); int LambdaPDGCode = sigma.lambdaPDGCode(); @@ -1177,6 +1298,8 @@ struct sigmaanalysis { //_______________________________________ // Sigmas and Lambdas + float rapidity = doLambdaStar ? sigma.lambdaStarY() : sigma.sigma0Y(); + if (sigma.lambdaAlpha() > 0) { if (fillSelhistos) { histos.fill(HIST(MainDir[mode]) + HIST("/Lambda/h2dTPCvsTOFNSigma_LambdaPr"), sigma.lambdaPosPrTPCNSigma(), sigma.lambdaPrTOFNSigma()); @@ -1190,12 +1313,16 @@ struct sigmaanalysis { histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/hMass"), sigma.sigma0Mass()); histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/hPt"), sigma.pt()); - histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/hY"), sigma.sigma0Y()); + histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/hY"), rapidity); histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/hRadius"), sigma.radius()); histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/h2dRadiusVspT"), sigma.radius(), sigma.pt()); histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/hDCAPairDau"), sigma.dcadaughters()); histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/h3dMass"), centrality, sigma.pt(), sigma.sigma0Mass()); histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/h3dOPAngleVsMass"), sigma.opAngle(), sigma.pt(), sigma.sigma0Mass()); + if (doArm) { + histos.fill(HIST(MainDir[mode]) + HIST("/Sigma0/h4dAlphaVsQtarmVsMass"), sigma.lStarAlpha(), sigma.lStarQtarm(), sigma.pt(), sigma.sigma0Mass()); + } + } else { if (fillSelhistos) { histos.fill(HIST(MainDir[mode]) + HIST("/Lambda/h2dTPCvsTOFNSigma_ALambdaPr"), sigma.lambdaNegPrTPCNSigma(), sigma.aLambdaPrTOFNSigma()); @@ -1209,12 +1336,15 @@ struct sigmaanalysis { histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/hMass"), sigma.sigma0Mass()); histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/hPt"), sigma.pt()); - histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/hY"), sigma.sigma0Y()); + histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/hY"), rapidity); histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/hRadius"), sigma.radius()); histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/h2dRadiusVspT"), sigma.radius(), sigma.pt()); histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/hDCAPairDau"), sigma.dcadaughters()); histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/h3dMass"), centrality, sigma.pt(), sigma.sigma0Mass()); histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/h3dOPAngleVsMass"), sigma.opAngle(), sigma.pt(), sigma.sigma0Mass()); + if (doArm) { + histos.fill(HIST(MainDir[mode]) + HIST("/ASigma0/h4dAlphaVsQtarmVsMass"), sigma.lStarAlpha(), sigma.lStarQtarm(), sigma.pt(), sigma.sigma0Mass()); + } } //_______________________________________ @@ -1263,7 +1393,7 @@ struct sigmaanalysis { } //_______________________________________ // Sigma0 MC association - if (sigma.isSigma0()) { + if (doLambdaStar ? sigma.isLambdaStar() : sigma.isSigma0()) { histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/hPt"), sigma.pt()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/hMCPt"), sigma.mcpt()); @@ -1271,6 +1401,9 @@ struct sigmaanalysis { histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/h2dMCPtVsPhotonMCPt"), sigma.mcpt(), sigma.photonmcpt()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/hMass"), sigma.sigma0Mass()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/h3dMass"), centrality, sigma.mcpt(), sigma.sigma0Mass()); + if (doArm) { + histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/h4dAlphaVsQtarmVsMass"), sigma.lStarAlpha(), sigma.lStarQtarm(), sigma.mcpt(), sigma.sigma0Mass()); + } histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/hMCProcess"), sigma.mcprocess()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/Sigma0/hGenRadius"), sigma.mcradius()); @@ -1280,7 +1413,7 @@ struct sigmaanalysis { //_______________________________________ // AntiSigma0 MC association - if (sigma.isAntiSigma0()) { + if (doLambdaStar ? sigma.isAntiLambdaStar() : sigma.isAntiSigma0()) { histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/hPt"), sigma.pt()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/hMCPt"), sigma.mcpt()); @@ -1288,6 +1421,9 @@ struct sigmaanalysis { histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/h2dMCPtVsPhotonMCPt"), sigma.mcpt(), sigma.photonmcpt()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/hMass"), sigma.sigma0Mass()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/h3dMass"), centrality, sigma.mcpt(), sigma.sigma0Mass()); + if (doArm) { + histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/h4dAlphaVsQtarmVsMass"), sigma.lStarAlpha(), sigma.lStarQtarm(), sigma.mcpt(), sigma.sigma0Mass()); + } histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/hMCProcess"), sigma.mcprocess()); histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/hGenRadius"), sigma.mcradius()); @@ -1295,6 +1431,15 @@ struct sigmaanalysis { histos.fill(HIST(MainDir[mode]) + HIST("/MC/ASigma0/h3dMCProcess"), sigma.mcprocess(), sigma.mcpt(), sigma.sigma0Mass()); } + if (doLambdaStar) { + if (sigma.isAntiLambdaStar() || sigma.isLambdaStar()) { + histos.fill(HIST(MainDir[mode]) + HIST("/MC/LambdaStar/h3dMCPtvsOPAngle_Sig"), sigma.mcopAngle(), sigma.mcpt(), sigma.sigma0Mass()); + } + + if (!sigma.isAntiLambdaStar() && !sigma.isLambdaStar()) { + histos.fill(HIST(MainDir[mode]) + HIST("/MC/LambdaStar/h3dMCPtvsOPAngle_Bkg"), sigma.mcopAngle(), sigma.mcpt(), sigma.sigma0Mass()); + } + } // For background studies: if (fillBkgQAhistos) runBkgAnalysis(sigma); @@ -1596,10 +1741,84 @@ struct sigmaanalysis { return true; } + // Fill BDT performance QA + template + void fillBDTPerformance(TSigma0Object const& cand, float score) + { + float pt = cand.pt(); + float mass = cand.sigma0Mass(); + float bkgScore = 1.0f - score; + + // Signal-probability output + histos.fill(HIST("BDT/hScoreSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsMassSignal"), mass, score); + histos.fill(HIST("BDT/h2dScoreVsPtSignal"), pt, score); + histos.fill(HIST("BDT/h3dScoreSignal"), pt, mass, score); + + // Background-probability output + histos.fill(HIST("BDT/hScoreBackground"), bkgScore); + histos.fill(HIST("BDT/h2dScoreVsMassBackground"), mass, bkgScore); + histos.fill(HIST("BDT/h2dScoreVsPtBackground"), pt, bkgScore); + histos.fill(HIST("BDT/h3dScoreBackground"), pt, mass, bkgScore); + + // Signal score vs the main topological variables + histos.fill(HIST("BDT/h2dDCADaughters"), score, cand.lambdaDCADau()); + histos.fill(HIST("BDT/h2dLambdaAlpha"), score, cand.lambdaAlpha()); + histos.fill(HIST("BDT/h2dLambdaDCANegPV"), score, cand.lambdaDCANegPV()); + histos.fill(HIST("BDT/h2dLambdaDCAPosPV"), score, cand.lambdaDCAPosPV()); + histos.fill(HIST("BDT/h2dLambdaQt"), score, cand.lambdaQt()); + histos.fill(HIST("BDT/h2dPhotonAlpha"), score, cand.photonAlpha()); + histos.fill(HIST("BDT/h2dPhotonCosPA"), score, cand.photonCosPA()); + histos.fill(HIST("BDT/h2dPhotonDCADau"), score, cand.photonDCADau()); + histos.fill(HIST("BDT/h2dPhotonDCANegPV"), score, cand.photonDCANegPV()); + histos.fill(HIST("BDT/h2dPhotonDCAPosPV"), score, cand.photonDCAPosPV()); + histos.fill(HIST("BDT/h2dPhotonQt"), score, cand.photonQt()); + histos.fill(HIST("BDT/h2dPhotonRadius"), score, cand.photonRadius()); + histos.fill(HIST("BDT/h2dOPAngle"), score, cand.opAngle()); + histos.fill(HIST("BDT/h2dAPAlpha"), score, cand.lStarAlpha()); + histos.fill(HIST("BDT/h2dAPQt"), score, cand.lStarQtarm()); + + // MC-truth-based separation (signal = particle + antiparticle) + if constexpr (requires { cand.isSigma0(); cand.isLambdaStar(); }) { + bool isTrueSignal = doLambdaStar ? (cand.isLambdaStar() || cand.isAntiLambdaStar()) : (cand.isSigma0() || cand.isAntiSigma0()); + if (isTrueSignal) { + histos.fill(HIST("BDT/hScoreTrueSignal"), score); + histos.fill(HIST("BDT/h2dScoreVsPtTrueSignal"), pt, score); + } else { + histos.fill(HIST("BDT/hScoreTrueBackground"), score); + histos.fill(HIST("BDT/h2dScoreVsPtTrueBackground"), pt, score); + } + } + } + + template + bool selectML(TSigma0Object const& cand) + { + // No model outside the bdt.ptBinEdges range + const float pt = cand.pt(); + if (pt < bdt.ptBinEdges.value.front() || pt >= bdt.ptBinEdges.value.back()) + return false; + + // Features in the order of bdt.namesInputFeatures + auto inputFeatures = mlResponse.getInputFeatures(cand, cand, cand.opAngle(), cand.lStarAlpha(), cand.lStarQtarm()); + std::vector outputMl; // [background, signal] + const bool isSelected = mlResponse.isSelectedMl(inputFeatures, pt, outputMl); // model and cut of the pT bin + + fillBDTPerformance(cand, outputMl[1]); + + return isSelected; + } + // Apply selections in sigma0 candidates template bool processSigma0Candidate(TSigma0Object const& cand) { + // BDT selection: model trained on PCM photon features only + if constexpr (requires { cand.photonV0Type(); }) { + if (bdt.enableML && !selectML(cand)) + return false; + } + // Photon specific selections if constexpr (requires { cand.photonV0Type(); }) { // Processing PCM photon if (!selectPhoton(cand)) @@ -1616,13 +1835,13 @@ struct sigmaanalysis { return false; // Sigma0 specific selections - float sigma0Y = cand.sigma0Y(); + float rapidity = doLambdaStar ? cand.lambdaStarY() : cand.sigma0Y(); if constexpr (requires { cand.sigma0MCY(); }) { // If MC - sigma0Y = cand.sigma0MCY(); + rapidity = doLambdaStar ? cand.lambdaStarMCY() : cand.sigma0MCY(); } // Rapidity - if ((sigma0Y < sigma0Selections.Sigma0MinRapidity) || (sigma0Y > sigma0Selections.Sigma0MaxRapidity)) + if ((rapidity < sigma0Selections.Sigma0MinRapidity) || (rapidity > sigma0Selections.Sigma0MaxRapidity)) return false; // V0Pair Radius @@ -1664,7 +1883,8 @@ struct sigmaanalysis { // if MC if constexpr (requires { sigma0.isSigma0(); sigma0.isAntiSigma0(); }) { - if (doMCAssociation && !(sigma0.isSigma0() || sigma0.isAntiSigma0())) + bool fIsMCAssociated = doLambdaStar ? (sigma0.isLambdaStar() || sigma0.isAntiLambdaStar()) : (sigma0.isSigma0() || sigma0.isAntiSigma0()); + if (doMCAssociation && !fIsMCAssociated) continue; if (selRecoFromGenerator && !sigma0.isProducedByGenerator()) @@ -1832,45 +2052,62 @@ struct sigmaanalysis { } } - void processRealData(soa::Join const& collisions, Sigma0s const& fullSigma0s) + // ______________________________________________________ + // Simulated processing in Run 2 (subscribes to MC information too) + void processAnalysedCollisions(aod::StraSelections const& straSelections) + { + for (auto const& straSelection : straSelections) { + // Event selection criteria + histos.get(HIST("hEventPreSelection"))->AddBinContent(1, straSelection.totalNbrOfCollisions() /* all collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(2, straSelection.totalIsTriggerTVXCollisions() /* preselected IsTriggerTVX collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(3, straSelection.totalNoITSROFBorderCollisions() /* + preselected NoITSROF collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(4, straSelection.totalNoTFBorderCollisions() /* + preselected NoTF collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(5, straSelection.totalIsGoodZvtxCollisions() /* + preselected |Zvtx| < X cm collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(6, straSelection.totalNoSBPileupCollisions() /* + preselected NoSameBunchPileup collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(7, straSelection.totalIsGoodRCTCollisions() /* + preselected Good RCT collisions */); + histos.get(HIST("hEventPreSelection"))->AddBinContent(8, straSelection.totalNbrOfSelCollisions() /* total number of preselected collisions */); + } + } + + void processRealData(soa::Join const& collisions, Sigma0s const& fullSigma0s) { analyzeRecoeSigma0s(collisions, fullSigma0s); } - void processRealDataWithEMCal(soa::Join const& collisions, Sigma0sWithEMCal const& fullSigma0s) + void processRealDataWithEMCal(soa::Join const& collisions, Sigma0sWithEMCal const& fullSigma0s) { analyzeRecoeSigma0s(collisions, fullSigma0s); } - void processMonteCarlo(soa::Join const& collisions, MCSigma0s const& fullSigma0s) + void processMonteCarlo(soa::Join const& collisions, MCSigma0s const& fullSigma0s) { analyzeRecoeSigma0s(collisions, fullSigma0s); } - void processMonteCarloWithEMCal(soa::Join const& collisions, MCSigma0sWithEMCal const& fullSigma0s) + void processMonteCarloWithEMCal(soa::Join const& collisions, MCSigma0sWithEMCal const& fullSigma0s) { analyzeRecoeSigma0s(collisions, fullSigma0s); } // Simulated processing in Run 3 - void processGeneratedRun3(soa::Join const& mcCollisions, soa::Join const& collisions, soa::Join const& Sigma0Gens) + void processGeneratedRun3(soa::Join const& mcCollisions, soa::Join const& collisions, soa::Join const& Sigma0Gens) { analyzeGenerated(mcCollisions, collisions, Sigma0Gens); } // _____________________________________________________ // Pi0 QA - void processPi0RealData(soa::Join const& collisions, soa::Join const& fullPi0s) + void processPi0RealData(soa::Join const& collisions, soa::Join const& fullPi0s) { analyzeRecoePi0s(collisions, fullPi0s); } - void processPi0MonteCarlo(soa::Join const& collisions, soa::Join const& fullPi0s) + void processPi0MonteCarlo(soa::Join const& collisions, soa::Join const& fullPi0s) { analyzeRecoePi0s(collisions, fullPi0s); } - void processPi0GeneratedRun3(soa::Join const& mcCollisions, soa::Join const& collisions, soa::Join const& Pi0Gens) + void processPi0GeneratedRun3(soa::Join const& mcCollisions, soa::Join const& collisions, soa::Join const& Pi0Gens) { analyzeGenerated(mcCollisions, collisions, Pi0Gens); } @@ -1884,6 +2121,7 @@ struct sigmaanalysis { PROCESS_SWITCH(sigmaanalysis, processPi0RealData, "Do real data analysis for pi0 QA", false); PROCESS_SWITCH(sigmaanalysis, processPi0MonteCarlo, "Do Monte-Carlo-based analysis for pi0 QA", false); PROCESS_SWITCH(sigmaanalysis, processPi0GeneratedRun3, "process MC generated Run 3 for pi0 QA", false); + PROCESS_SWITCH(sigmaanalysis, processAnalysedCollisions, "process filtered events for bookkeeping", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/Strangeness/strangeCascTrack.cxx b/PWGLF/Tasks/Strangeness/strangeCascTrack.cxx index 1967d59c695..6d115211d88 100644 --- a/PWGLF/Tasks/Strangeness/strangeCascTrack.cxx +++ b/PWGLF/Tasks/Strangeness/strangeCascTrack.cxx @@ -170,7 +170,7 @@ struct StrangeCascTrack { } // checks general selection criteria for collisions template - bool isValidEvent(TEvent collision, bool fillHists) + bool isValidEvent(const TEvent& collision, bool fillHists) { bool passedAllSels = true; //* inel>0 cut @@ -238,7 +238,7 @@ struct StrangeCascTrack { } // checks cascade pt template - bool isValidPt(TCascade cascade, TString particle, int Type) + bool isValidPt(const TCascade& cascade, const TString& particle, int Type) { bool passedSel = true; double ptMin = 0.0; @@ -265,7 +265,7 @@ struct StrangeCascTrack { } // checks general selection criteria for cascades template - std::array isValidCasc(TEvent collision, TCascade cascade, TStdCascade stdcasc, TString particle) + std::array isValidCasc(const TEvent& collision, const TCascade& cascade, const TStdCascade& stdcasc, const TString& particle) { bool passedAllSels = true; // cascade rapidity @@ -342,7 +342,7 @@ struct StrangeCascTrack { } // checks TPC PID of dau tracks template - bool passesTPC(TCascade cascade, TString particle) + bool passesTPC(const TCascade& cascade, const TString& particle) { bool passedSel = true; const auto& posTrack = cascade.template posTrackExtra_as(); @@ -375,7 +375,7 @@ struct StrangeCascTrack { } // checks TOF PID of dau tracks template - bool passesTOF(TCascade cascade, TString particle) + bool passesTOF(const TCascade& cascade, const TString& particle) { bool passedSel = true; if (particle == "Xi") @@ -386,7 +386,7 @@ struct StrangeCascTrack { } // checks whether gen cascade corresponds to PDG code template - bool isValidPDG(TCascade cascade, TString particle) + bool isValidPDG(const TCascade& cascade, const TString& particle) { if (particle == "Xi" && std::abs(cascade.pdgCode()) == PDG_t::kXiMinus) return true; @@ -396,7 +396,7 @@ struct StrangeCascTrack { } // checks whether rec cascade is a truth primary xi or omega template - bool isMCTruth(const TCascade& cascade, TString particle) + bool isMCTruth(const TCascade& cascade, const TString& particle) { if constexpr (requires { cascade.has_cascMCCore(); }) { // safety check: discard rec cascade without gen reference auto cascmccore = cascade.template cascMCCore_as(); @@ -413,7 +413,7 @@ struct StrangeCascTrack { // applies selections for and fills histograms template - void analyseCascade(TEvent collision, TCasc cascade) + void analyseCascade(const TEvent& collision, TCasc cascade) { if constexpr (requires { cascade.topologyChi2(); }) { if (!cascade.has_standardCascade()) @@ -666,7 +666,6 @@ struct StrangeCascTrack { if (passedAllSelsOmega) { histos.fill(HIST(TypeNames[Type]) + HIST("/Rec/FiltersOmega"), 5.5); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec/MassOmega"), massOmega); - histos.fill(HIST(TypeNames[Type]) + HIST("/Rec/Xi"), massXi, pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec/Omega"), massOmega, pt, mult); // fill for particle-antiparticle type if (cascade.sign() < 0) { @@ -688,7 +687,8 @@ struct StrangeCascTrack { } // statistics - compare gen and reco pt and rapidity - int charmBeautyCodes = 4000; + int charmBeautyCodesLow = 4000; + int charmBeautyCodesHigh = 6000; if constexpr (requires { collision.straMCCollisionId(); }) { if constexpr (requires { stdCasc.has_cascMCCore(); }) { auto cascmccore = stdCasc.template cascMCCore_as(); @@ -709,10 +709,10 @@ struct StrangeCascTrack { histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/GenRecRapidityXi"), genYXi, cascade.yXi()); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAxyPrimaryXi"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAzPrimaryXi"), cascade.dcaZCascToPV(), pt, mult); - if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodes) { + if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodesLow && std::abs(cascmccore.pdgCodeMother()) < charmBeautyCodesHigh) { histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAxyDecayXi"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAzDecayXi"), cascade.dcaZCascToPV(), pt, mult); - } else { + } else if (cascmccore.pdgCodeMother() == -1) { histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAxyDirectXi"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAzDirectXi"), cascade.dcaZCascToPV(), pt, mult); } @@ -724,10 +724,10 @@ struct StrangeCascTrack { histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAxyPrimaryXi"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAzPrimaryXi"), cascade.dcaZCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/Radius/PrimaryXi"), cascade.cascradius(), pt); - if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodes) { + if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodesLow && std::abs(cascmccore.pdgCodeMother()) < charmBeautyCodesHigh) { histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAxyDecayXi"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAzDecayXi"), cascade.dcaZCascToPV(), pt, mult); - } else { + } else if (cascmccore.pdgCodeMother() == -1) { histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAxyDirectXi"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAzDirectXi"), cascade.dcaZCascToPV(), pt, mult); } @@ -737,10 +737,10 @@ struct StrangeCascTrack { histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/GenRecRapidityOmega"), genYOmega, cascade.yOmega()); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAxyPrimaryOmega"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAzPrimaryOmega"), cascade.dcaZCascToPV(), pt, mult); - if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodes) { + if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodesLow && std::abs(cascmccore.pdgCodeMother()) < charmBeautyCodesHigh) { histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAxyDecayOmega"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAzDecayOmega"), cascade.dcaZCascToPV(), pt, mult); - } else { + } else if (cascmccore.pdgCodeMother() == -1) { histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAxyDirectOmega"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/NoSel-Truth/DCA/DCAzDirectOmega"), cascade.dcaZCascToPV(), pt, mult); } @@ -752,10 +752,10 @@ struct StrangeCascTrack { histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAxyPrimaryOmega"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAzPrimaryOmega"), cascade.dcaZCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/Radius/PrimaryOmega"), cascade.cascradius(), pt); - if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodes) { + if (std::abs(cascmccore.pdgCodeMother()) > charmBeautyCodesLow && std::abs(cascmccore.pdgCodeMother()) < charmBeautyCodesHigh) { histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAxyDecayOmega"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAzDecayOmega"), cascade.dcaZCascToPV(), pt, mult); - } else { + } else if (cascmccore.pdgCodeMother() == -1) { histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAxyDirectOmega"), cascade.dcaXYCascToPV(), pt, mult); histos.fill(HIST(TypeNames[Type]) + HIST("/Rec-Truth/DCA/DCAzDirectOmega"), cascade.dcaZCascToPV(), pt, mult); } diff --git a/PWGLF/Tasks/Strangeness/strangenessInJets.cxx b/PWGLF/Tasks/Strangeness/strangenessInJets.cxx index 1be5e3c83d6..54b8fc923cf 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJets.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJets.cxx @@ -444,6 +444,15 @@ struct StrangenessInJets { registryMC.add("OmegaPos_reconstructed_ue", "OmegaPos_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_jet", "OmegaNeg_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_ue", "OmegaNeg_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); + // Generated cascades associated to reconstructed jet / UE axes. + registryMC.add("XiPos_generated_recojet_jet", "XiPos_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiPos_generated_recojet_ue", "XiPos_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_recojet_jet", "XiNeg_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_recojet_ue", "XiNeg_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_recojet_jet", "OmegaPos_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_recojet_ue", "OmegaPos_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_recojet_jet", "OmegaNeg_generated_recojet_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_recojet_ue", "OmegaNeg_generated_recojet_ue", HistType::kTH2F, {multAxis, ptAxis}); // Histograms to calculate probability of hyperons to be found within jets registryMC.add("XiPos_reconstructed_fullevent", "XiPos_reconstructed_fullevent", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiNeg_reconstructed_fullevent", "XiNeg_reconstructed_fullevent", HistType::kTH2F, {multAxis, ptAxis}); @@ -1455,7 +1464,7 @@ struct StrangenessInJets { // Set up two perpendicular cone axes for underlying event estimation const TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - const double coneRadius = std::sqrt(jet.area() / PI); + const double coneRadius = rJet; TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { @@ -1664,7 +1673,8 @@ struct StrangenessInJets { if (!motherPos.isPhysicalPrimary()) continue; - if (std::abs(motherPos.eta()) > 0.8) + const double maxPseudorap = 0.8; + if (std::abs(motherPos.eta()) > maxPseudorap) continue; // K0s @@ -1744,8 +1754,8 @@ struct StrangenessInJets { if (!mcParticle.isPhysicalPrimary()) { continue; } - - if (std::abs(mcParticle.eta()) > 0.8) { + const double maxPseudorap = 0.8; + if (std::abs(mcParticle.eta()) > maxPseudorap) { continue; } @@ -1786,9 +1796,10 @@ struct StrangenessInJets { } } - for (auto& particle : mcParticlesPerColl) { + const double maxPseudorap = 0.8; + for (auto const& particle : mcParticlesPerColl) { - if (particle.isPhysicalPrimary() && std::abs(particle.eta()) <= 0.8) { + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) <= maxPseudorap) { switch (particle.pdgCode()) { case kK0Short: if (enabledSignals.value[ParticleOfInterest::kV0Particles]) { @@ -1889,8 +1900,7 @@ struct StrangenessInJets { continue; if (!motherPos.isPhysicalPrimary()) continue; - - if (std::abs(motherPos.eta()) > 0.8) + if (std::abs(motherPos.eta()) > maxPseudorap) continue; // K0s @@ -1908,9 +1918,8 @@ struct StrangenessInJets { } } - for (auto& particle : mcParticlesPerColl) { - - if (particle.isPhysicalPrimary() && std::abs(particle.eta()) <= 0.8) { + for (auto const& particle : mcParticlesPerColl) { + if (particle.isPhysicalPrimary() && std::abs(particle.eta()) <= maxPseudorap) { switch (particle.pdgCode()) { case kK0Short: if (enabledSignals.value[ParticleOfInterest::kV0Particles]) { @@ -1936,7 +1945,7 @@ struct StrangenessInJets { // Loop over selected jets for (int i = 0; i < static_cast(selectedJet.size()); i++) { - for (auto& particle : mcParticlesPerColl) { + for (auto const& particle : mcParticlesPerColl) { const ParticlePositionWithRespectToJet positionMC{particle.px(), particle.py(), particle.pz(), selectedJet[i], ue1[i], ue2[i]}; @@ -1972,6 +1981,50 @@ struct StrangenessInJets { } } break; + + // Cascades: generated particles classified with respect to the + // reconstructed jet and perpendicular-cone axes, exactly as for V0s. + case kXiPlusBar: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("XiPos_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("XiPos_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kXiMinus: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("XiNeg_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("XiNeg_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kOmegaPlusBar: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("OmegaPos_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("OmegaPos_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + case kOmegaMinus: + if (enabledSignals.value[ParticleOfInterest::kCascades]) { + if (positionMC.isInJet()) { + registryMC.fill(HIST("OmegaNeg_generated_recojet_jet"), multiplicity, particle.pt()); + } + if (positionMC.isInUE1() || positionMC.isInUE2()) { + registryMC.fill(HIST("OmegaNeg_generated_recojet_ue"), multiplicity, particle.pt()); + } + } + break; + default: break; } diff --git a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx index c8c665f1aa2..974350c21dd 100644 --- a/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessInJetsIons.cxx @@ -26,6 +26,7 @@ #include "PWGLF/DataModel/mcCentrality.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/Zorro.h" #include "Common/Core/ZorroSummary.h" #include "Common/DataModel/Centrality.h" @@ -51,8 +52,11 @@ #include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) #include +#include #include +#include #include +#include #include #include @@ -62,11 +66,15 @@ #include #include +#include #include #include +#include #include #include #include +#include +#include #include using namespace std; @@ -89,6 +97,7 @@ using DaughterTracks = soa::Join; using DaughterTracksMC = soa::Join; +using DaughterTracksMB = soa::Join; struct StrangenessInJetsIons { @@ -102,8 +111,9 @@ struct StrangenessInJetsIons { // Define histogram registries HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryDataMB{"registryDataMB", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + // HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // Global analysis parameters // List of Particles @@ -120,6 +130,19 @@ struct StrangenessInJetsIons { ParticleOfInterest::kProtons}; std::map particleOfInterestDict; + // --- Variables for the thermal toy-model + struct BkgFitParams { + float centMin; // Lower edge centrality bin + float centMax; // Upper edge centrality bin + double mass; // Levy-Tsallis: mass [GeV/c^2] + double n; // Levy-Tsallis: n + double C; // Levy-Tsallis: T (temperature) + double dNch_deta; // for FT0M + }; + TRandom3 fRng{0}; + TF1* fTsallisToy = nullptr; + // --- + Configurable> particleOfInterest{"particleOfInterest", {0, 0, 0, 0, 0}, "Particles to study: [K0 and Lambda, Xi and Omega, Pion, Kaon, Proton]"}; Configurable minJetPt{"minJetPt", 10.0, "Minimum reconstructed pt of the jet (GeV/c)"}; Configurable rJet{"rJet", 0.4, "Jet resolution parameter (R)"}; @@ -128,6 +151,12 @@ struct StrangenessInJetsIons { Configurable cfgSkimmedProcessing{"cfgSkimmedProcessing", false, "Enable processing of skimmed data"}; Configurable triggerName{"triggerName", "fOmega", "Software trigger name"}; Configurable centrEstimator{"centrEstimator", 1, "Select centrality estimator. Options: 0 = FT0C, 1 = FT0M. CCDB objects available only for FT0M."}; + Configurable calculateFeeddownMatrix{"calculateFeeddownMatrix", true, "Fill feeddown matrix for Lambda if MC"}; + Configurable useV0inJetRec{"useV0inJetRec", true, "Include V0s in jet reconstruction"}; + Configurable doThermalToyModel{"doThermalToyModel", false, "Use the thermal toy model to embed background particles to the jet finder input list"}; + Configurable doPlotRho{"doPlotRho", false, "Plot rho distributions computed with perpendicular cone and area median method."}; + Configurable doRandomConeSys{"doRandomConeSys", false, "Study particle contribution outside jets using random cones with same pseudorapidity and phi generated in [pi/3, 2*pi/3]."}; + Configurable saveChargedParticleMB{"saveChargedParticleMB", false, "Store charged particle information to build inclusive spectra."}; // Event selection Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", true, "Require kNoSameBunchPileup selection"}; @@ -186,17 +215,21 @@ struct StrangenessInJetsIons { // Cascade analysis parameters struct : ConfigurableGroup { // std::string prefix = "configCasc"; // name in JSON - Configurable minimumCascRadius{"minimumCascRadius", 0.1f, "Minimum cascade radius"}; + Configurable minimumV0RadiusCasc{"minimumV0RadiusCasc", 1.5f, "Minimum V0 radius (cm) for cascade analysis"}; + Configurable minimumCascRadius{"minimumCascRadius", 1.5f, "Minimum cascade radius (cm)"}; // Configurable maximumCascRadius{"maximumCascRadius", 40.0f, "Maximum cascade radius"}; + Configurable dcabachtopvMin{"dcabachtopvMin", 0.07f, "Minimum DCA of bachelor to primary vertex (cm)"}; + Configurable dcaV0topvMin{"dcaV0topvMin", 0.02f, "Minimum DCA of V0 to primary vertex (cm)"}; + Configurable dcaV0DaughtersMaxCasc{"dcaV0DaughtersMaxCasc", 0.7f, "Maximum DCA between V0 daughters (cm) for cascade analysis"}; + Configurable dcaCascDaughtersMax{"dcaCascDaughtersMax", 0.8f, "Maximum DCA between daughters (cm)"}; + Configurable dcaPosToPVminV0{"dcaPosToPVminV0", 0.1f, "Minimum DCA of V0 positive track to primary vertex in cascades (cm)"}; + Configurable dcaNegToPVminV0{"dcaNegToPVminV0", 0.1f, "Minimum DCA of V0 negative track to primary vertex in cascades (cm)"}; + Configurable v0cospaMinCasc{"v0cospaMinCasc", 0.99f, "Minimum V0 cosine of pointing angle for cascade analysis"}; Configurable casccospaMin{"casccospaMin", 0.99f, "Minimum cascade cosine of pointing angle"}; - Configurable dcabachtopvMin{"dcabachtopvMin", 0.1f, "Minimum DCA of bachelor to primary vertex"}; - Configurable dcaV0topvMin{"dcaV0topvMin", 0.1f, "Minimum DCA of V0 to primary vertex"}; - Configurable dcaCascDaughtersMax{"dcaCascDaughtersMax", 0.5f, "Maximum DCA between daughters"}; - Configurable dcaNegToPVminV0{"dcaNegToPVminV0", 0.1f, "Minimum DCA of V0 negative track to primary vertex in cascades"}; - Configurable dcaPosToPVminV0{"dcaPosToPVminV0", 0.1f, "Minimum DCA of V0 positive track to primary vertex in cascades"}; - Configurable deltaMassXi{"deltaMassXi", 0.02f, "Mass window for Xi rejection"}; - Configurable deltaMassOmega{"deltaMassOmega", 0.02f, "Mass window for Omega rejection"}; - Configurable deltaMassLambda{"deltaMassLambda", 0.02f, "Mass window for Lambda inclusion"}; + + Configurable deltaMassXi{"deltaMassXi", 0.008f, "[Omega] Mass window for Xi rejection"}; + Configurable deltaMassOmega{"deltaMassOmega", 0.008f, "[Xi] Mass window for Omega rejection"}; + Configurable deltaMassLambda{"deltaMassLambda", 0.005f, "[Xi, Omega] Mass window for Lambda inclusion"}; } configCasc; // Axes @@ -239,8 +272,11 @@ struct StrangenessInJetsIons { } AxisSpec multAxis = {multBinning, multAxTitle}; - const AxisSpec ptAxis{100, 0.0, 10.0, "#it{p}_{T} (GeV/#it{c})"}; - const AxisSpec ptJetAxis{101, 0.0, 100.0, "#it{p}_{T,jet} (GeV/#it{c})"}; + const AxisSpec ptAxis{500, 0.0, 50.0, "#it{p}_{T} (GeV/#it{c})"}; + const AxisSpec ptJetAxis{100, 0.0, 100.0, "#it{p}_{T,jet} (GeV/#it{c})"}; + const AxisSpec ptJetRecoAxisUnfold{200, 0.0, 200.0, "#it{p}_{T,jet}^{rec} (GeV/#it{c})"}; + const AxisSpec ptJetGenAxisUnfold{200, 0.0, 200.0, "#it{p}_{T,jet}^{gen} (GeV/#it{c})"}; + const AxisSpec ptChargedAxis{10000, 0.0, 100.0, "#it{p}_{T} (GeV/#it{c})"}; const AxisSpec numJets{21, -0.5, 20.5, "Number of jets per collision"}; const AxisSpec invMassK0sAxis{200, 0.44, 0.56, "m_{#pi#pi} (GeV/#it{c}^{2})"}; const AxisSpec invMassLambdaAxis{200, 1.09, 1.14, "m_{p#pi} (GeV/#it{c}^{2})"}; @@ -252,6 +288,9 @@ struct StrangenessInJetsIons { const AxisSpec dcaAxis{longLivedOptions.longLivedBinsDca, "DCA_{xy} (cm)"}; const AxisSpec alphaArmAxis{1000, -1.0f, 1.0f, "#alpha^{arm}"}; const AxisSpec qtarmAxis{1000, 0.0f, 0.30f, "q_{T}^{arm}"}; + const AxisSpec numBkgParticles{500, 0.0, 500.0, "Number of particles"}; + const AxisSpec deltaPtAxis{2400, -60.0, 60.0, "#delta #it{p}_{T} (GeV/#it{c})"}; + const AxisSpec rhoAxis{100, 0.0, 50.0, "#it{#rho} (GeV/#it{c})"}; // Join enum ParticleOfInterest and the configurable vector particlesOfInterest in a map particleOfInterestDict const std::vector& particleOnOff = particleOfInterest; @@ -269,6 +308,12 @@ struct StrangenessInJetsIons { LOG(fatal) << "No particles selected. Select at least one particle." << endl; } + // --- Distribution for the thermal toy model + fTsallisToy = new TF1("fTsallisToy", LevyTsallis_Func, 0.0, 50.0, 4); + fTsallisToy->SetParNames("mass", "n", "C", "norm"); + fTsallisToy->SetNpx(1000); + // --- + // Histograms for checks // registryQC.add("V0_type", "V0_type", HistType::kTH1F, {{10, -0.5, 9.5, "V0 type"}}); @@ -280,7 +325,7 @@ struct StrangenessInJetsIons { registryData.add("number_of_events_vsmultiplicity", "number of events in data vs multiplicity", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); // For MB - registryData.add("number_of_events_vsmultiplicity_MB", "number of events in data vs multiplicity (MB)", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); + // registryData.add("number_of_events_vsmultiplicity_MB", "number of events in data vs multiplicity (MB)", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(1, "All collisions"); registryData.get(HIST("number_of_events_data"))->GetXaxis()->SetBinLabel(2, "Zorro selection"); @@ -295,6 +340,14 @@ struct StrangenessInJetsIons { registryData.add("n_jets_vs_mult_pT", "n_jets_vs_mult_pT", HistType::kTH2F, {multAxis, ptJetAxis}); registryData.add("n_jets_vs_mult", "n_jets_vs_mult", HistType::kTH2F, {multAxis, numJets}); + // Delta pT distribution + registryData.add("h2_centrality_deltaPt_RandomCone", "h2_centrality_deltaPt_RandomCone", HistType::kTH2F, {multAxis, deltaPtAxis}); + registryData.add("h2_centrality_rhoPerp", "h2_centrality_rhoPerp", HistType::kTH2F, {multAxis, rhoAxis}); + + if (doPlotRho) { + registryData.add("rho_perp", "rho_perp", HistType::kTH2F, {multAxis, rhoAxis}); + registryData.add("rho_median", "rho_median", HistType::kTH2F, {multAxis, rhoAxis}); + } // Armenteros-Podolanski plot // registryQC.add("ArmenterosPreSel_DATA", "ArmenterosPreSel_DATA", HistType::kTH2F, {alphaArmAxis, qtarmAxis}); @@ -306,6 +359,11 @@ struct StrangenessInJetsIons { registryData.add("AntiLambda_in_ue", "AntiLambda_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); registryData.add("K0s_in_jet", "K0s_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); registryData.add("K0s_in_ue", "K0s_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); + if (doRandomConeSys) { + registryData.add("Lambda_in_rc", "Lambda_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("AntiLambda_in_rc", "AntiLambda_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryData.add("K0s_in_rc", "K0s_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); + } } if (particleOfInterestDict[ParticleOfInterest::kCascades]) { registryData.add("XiPos_in_jet", "XiPos_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassXiAxis}); @@ -316,6 +374,12 @@ struct StrangenessInJetsIons { registryData.add("OmegaPos_in_ue", "OmegaPos_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); registryData.add("OmegaNeg_in_jet", "OmegaNeg_in_jet", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); registryData.add("OmegaNeg_in_ue", "OmegaNeg_in_ue", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + if (doRandomConeSys) { + registryData.add("XiPos_in_rc", "XiPos_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassXiAxis}); + registryData.add("XiNeg_in_rc", "XiNeg_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassXiAxis}); + registryData.add("OmegaPos_in_rc", "OmegaPos_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + registryData.add("OmegaNeg_in_rc", "OmegaNeg_in_rc", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + } } if (particleOfInterestDict[ParticleOfInterest::kPions]) { registryData.add("Pion_in_jet", "Pion_in_jet", HistType::kTHnSparseF, {multAxis, ptAxisLongLived, nsigmaTPCAxis, nsigmaTOFAxis, dcaAxis}); @@ -332,7 +396,10 @@ struct StrangenessInJetsIons { } // Histograms for mc generated - if (doprocessMCgenerated) { + if (doprocessMCgenerated || doprocessMCmodels) { + if (doprocessMCgenerated && doprocessMCmodels) { + LOG(fatal) << "processMCgenerated and processMCmodels cannot be selected at the same time. Exit." << endl; + } // Event counter registryMC.add("number_of_events_mc_gen", "number of gen events in mc", HistType::kTH1D, {{10, 0, 10, "Event Cuts"}}); @@ -344,14 +411,24 @@ struct StrangenessInJetsIons { // Add histogram to store multiplicity of the event registryMC.add("number_of_events_vsmultiplicity_gen", "number of events vs multiplicity", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); + registryMC.add("number_of_events_vsmultiplicity_gen_w_reco", "number of events vs multiplicity (gen with reco)", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); // For MB registryMC.add("number_of_events_vsmultiplicity_gen_MB", "number of events vs multiplicity (MB)", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); + registryMC.add("number_of_events_vsmultiplicity_gen_w_reco_MB", "number of events vs multiplicity (MB gen with reco)", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); // Jet counters registryMC.add("n_jets_vs_mult_pT_mc_gen", "n_jets_vs_mult_pT_mc_gen", HistType::kTH2F, {multAxis, ptJetAxis}); registryMC.add("n_jets_vs_mult_mc_gen", "n_jets_vs_mult_mc_gen", HistType::kTH2F, {multAxis, numJets}); + if (doThermalToyModel) { + registryMC.add("thermalToy_pT_gen", "thermalToy_pT_gen", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("thermalToy_nBkg_gen", "thermalToy_nBkg_gen", HistType::kTH2F, {multAxis, numBkgParticles}); + } + + registryMC.add("h2_centrality_deltaPt_RandomCone_gen", "h2_centrality_deltaPt_RandomCone_gen", HistType::kTH2F, {multAxis, deltaPtAxis}); + registryMC.add("h2_centrality_rhoPerp_gen", "h2_centrality_rhoPerp_gen", HistType::kTH2F, {multAxis, rhoAxis}); + // Histograms for analysis if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { registryMC.add("K0s_generated_jet", "K0s_generated_jet", HistType::kTH2F, {multAxis, ptAxis}); @@ -360,6 +437,11 @@ struct StrangenessInJetsIons { registryMC.add("Lambda_generated_ue", "Lambda_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_generated_jet", "AntiLambda_generated_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_generated_ue", "AntiLambda_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("Lambda_generated_rc", "Lambda_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("AntiLambda_generated_rc", "AntiLambda_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("K0s_generated_rc", "K0s_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + } // --- Histograms for the full event (without jets) registryMC.add("K0s_generated_MB", "K0s_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); @@ -372,6 +454,11 @@ struct StrangenessInJetsIons { registryMC.add("AntiLambda_generated_w_reco_MB", "AntiLambda_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); // ----- } + + if (particleOfInterestDict[ParticleOfInterest::kCascades] || calculateFeeddownMatrix) { + registryMC.add("XiPos_generated_MB", "XiPos_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_MB", "XiNeg_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); + } if (particleOfInterestDict[ParticleOfInterest::kCascades]) { registryMC.add("XiPos_generated_jet", "XiPos_generated_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiPos_generated_ue", "XiPos_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); @@ -381,13 +468,24 @@ struct StrangenessInJetsIons { registryMC.add("OmegaPos_generated_ue", "OmegaPos_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_generated_jet", "OmegaNeg_generated_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_generated_ue", "OmegaNeg_generated_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_generated_rc", "XiPos_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_rc", "XiNeg_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_rc", "OmegaPos_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_rc", "OmegaNeg_generated_rc", HistType::kTH2F, {multAxis, ptAxis}); + } // Histograms for the full event (without jets) - registryMC.add("XiPos_generated_MB", "XiPos_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); - registryMC.add("XiNeg_generated_MB", "XiNeg_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaPos_generated_MB", "OmegaPos_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_generated_MB", "OmegaNeg_generated_MB", HistType::kTH2F, {multAxis, ptAxis}); + + // In generated events with at least one reco collision + registryMC.add("XiPos_generated_w_reco_MB", "XiPos_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_generated_w_reco_MB", "XiNeg_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_generated_w_reco_MB", "OmegaPos_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_generated_w_reco_MB", "OmegaNeg_generated_w_reco_MB", HistType::kTH2F, {multAxis, ptAxis}); } + if (particleOfInterestDict[ParticleOfInterest::kPions]) { registryMC.add("PionPos_generated_jet", "PionPos_generated_jet", HistType::kTH2F, {multAxis, ptAxisLongLived}); registryMC.add("PionNeg_generated_jet", "PionNeg_generated_jet", HistType::kTH2F, {multAxis, ptAxisLongLived}); @@ -444,6 +542,18 @@ struct StrangenessInJetsIons { registryMC.add("n_jets_vs_mult_pT_mc_rec", "n_jets_vs_mult_pT_mc_rec", HistType::kTH2F, {multAxis, ptJetAxis}); registryMC.add("n_jets_vs_mult_mc_rec", "n_jets_vs_mult_mc_rec", HistType::kTH2F, {multAxis, numJets}); + if (doThermalToyModel) { + registryMC.add("thermalToy_pT_rec", "thermalToy_pT_rec", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("thermalToy_nBkg_rec", "thermalToy_nBkg_rec", HistType::kTH2F, {multAxis, numBkgParticles}); + registryMC.add("chargedBkg_toy_rec_jet", "chargedBkg_toy_rec_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("chargedBkg_toy_rec_ue", "chargedBkg_toy_rec_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("charged_embedJets_rec_jet", "charged_embedJets_rec_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("charged_embedJets_rec_ue", "charged_embedJets_rec_ue", HistType::kTH2F, {multAxis, ptAxis}); + } + + registryMC.add("h2_centrality_deltaPt_RandomCone_rec", "h2_centrality_deltaPt_RandomCone_rec", HistType::kTH2F, {multAxis, deltaPtAxis}); + registryMC.add("h2_centrality_rhoPerp_rec", "h2_centrality_rhoPerp_rec", HistType::kTH2F, {multAxis, rhoAxis}); + // Armenteros-Podolanski plot // registryQC.add("ArmenterosPreSel_REC", "ArmenterosPreSel_REC", HistType::kTH2F, {alphaArmAxis, qtarmAxis}); @@ -455,6 +565,11 @@ struct StrangenessInJetsIons { registryMC.add("Lambda_reconstructed_ue", "Lambda_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_reconstructed_jet", "AntiLambda_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_reconstructed_ue", "AntiLambda_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("Lambda_reconstructed_rc", "Lambda_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("AntiLambda_reconstructed_rc", "AntiLambda_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("K0s_reconstructed_rc", "K0s_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + } // Histograms for secondary hadrons registryMC.add("K0s_reconstructed_jet_incl", "K0s_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); @@ -463,6 +578,11 @@ struct StrangenessInJetsIons { registryMC.add("Lambda_reconstructed_ue_incl", "Lambda_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_reconstructed_jet_incl", "AntiLambda_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_reconstructed_ue_incl", "AntiLambda_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("Lambda_reconstructed_rc_incl", "Lambda_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("AntiLambda_reconstructed_rc_incl", "AntiLambda_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("K0s_reconstructed_rc_incl", "K0s_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + } // Histograms for generated particles in reconstructed events registryMC.add("K0s_gen_recoEvent_jet", "K0s_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); @@ -471,6 +591,11 @@ struct StrangenessInJetsIons { registryMC.add("Lambda_gen_recoEvent_ue", "Lambda_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_gen_recoEvent_jet", "AntiLambda_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("AntiLambda_gen_recoEvent_ue", "AntiLambda_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("Lambda_gen_recoEvent_rc", "Lambda_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("AntiLambda_gen_recoEvent_rc", "AntiLambda_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("K0s_gen_recoEvent_rc", "K0s_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + } // --- Histograms for the full event (without jets) registryMC.add("K0s_reconstructed_MB", "K0s_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); @@ -495,13 +620,62 @@ struct StrangenessInJetsIons { registryMC.add("OmegaPos_reconstructed_ue", "OmegaPos_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_jet", "OmegaNeg_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_ue", "OmegaNeg_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_reconstructed_rc", "XiPos_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_rc", "XiNeg_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_rc", "OmegaPos_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_rc", "OmegaNeg_reconstructed_rc", HistType::kTH2F, {multAxis, ptAxis}); + } + + // Histograms for secondary hadrons + registryMC.add("XiPos_reconstructed_jet_incl", "XiPos_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiPos_reconstructed_ue_incl", "XiPos_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_jet_incl", "XiNeg_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_ue_incl", "XiNeg_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_jet_incl", "OmegaPos_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_ue_incl", "OmegaPos_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_jet_incl", "OmegaNeg_reconstructed_jet_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_ue_incl", "OmegaNeg_reconstructed_ue_incl", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_reconstructed_rc_incl", "XiPos_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_rc_incl", "XiNeg_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_rc_incl", "OmegaPos_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_rc_incl", "OmegaNeg_reconstructed_rc_incl", HistType::kTH2F, {multAxis, ptAxis}); + } + + // Histograms for generated particles in reconstructed events + registryMC.add("XiPos_gen_recoEvent_jet", "XiPos_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiPos_gen_recoEvent_ue", "XiPos_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_jet", "XiNeg_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_ue", "XiNeg_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_jet", "OmegaPos_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_ue", "OmegaPos_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_jet", "OmegaNeg_gen_recoEvent_jet", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_ue", "OmegaNeg_gen_recoEvent_ue", HistType::kTH2F, {multAxis, ptAxis}); + if (doRandomConeSys) { + registryMC.add("XiPos_gen_recoEvent_rc", "XiPos_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_rc", "XiNeg_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_rc", "OmegaPos_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_rc", "OmegaNeg_gen_recoEvent_rc", HistType::kTH2F, {multAxis, ptAxis}); + } // Histograms for the full event (without jets) registryMC.add("XiPos_reconstructed_MB", "XiPos_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("XiNeg_reconstructed_MB", "XiNeg_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaPos_reconstructed_MB", "OmegaPos_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); registryMC.add("OmegaNeg_reconstructed_MB", "OmegaNeg_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxis}); + + registryMC.add("XiPos_reconstructed_MB_incl", "XiPos_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_reconstructed_MB_incl", "XiNeg_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_reconstructed_MB_incl", "OmegaPos_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_reconstructed_MB_incl", "OmegaNeg_reconstructed_MB_incl", HistType::kTH2F, {multAxis, ptAxis}); + + registryMC.add("XiPos_gen_recoEvent_MB", "XiPos_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("XiNeg_gen_recoEvent_MB", "XiNeg_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaPos_gen_recoEvent_MB", "OmegaPos_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); + registryMC.add("OmegaNeg_gen_recoEvent_MB", "OmegaNeg_gen_recoEvent_MB", HistType::kTH2F, {multAxis, ptAxis}); } + if (particleOfInterestDict[ParticleOfInterest::kPions]) { registryMC.add("Pion_reconstructed_jet", "Pion_reconstructed_jet", HistType::kTH2F, {multAxis, ptAxisLongLived}); registryMC.add("Pion_reconstructed_ue", "Pion_reconstructed_ue", HistType::kTH2F, {multAxis, ptAxisLongLived}); @@ -526,6 +700,71 @@ struct StrangenessInJetsIons { registryMC.add("ProtonPos_reconstructed_MB", "ProtonPos_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxisLongLived}); registryMC.add("ProtonNeg_reconstructed_MB", "ProtonNeg_reconstructed_MB", HistType::kTH2F, {multAxis, ptAxisLongLived}); } + + if (calculateFeeddownMatrix) { + registryMC.add("h3dLambdaFeeddown", "h3dLambdaFeeddown", kTH3D, {multAxis, ptAxis, ptAxis}); + registryMC.add("h3dLambdaFeeddownFromXi0", "h3dLambdaFeeddownFromXi0", kTH3D, {multAxis, ptAxis, ptAxis}); + registryMC.add("h3dAntiLambdaFeeddown", "h3dAntiLambdaFeeddown", kTH3D, {multAxis, ptAxis, ptAxis}); + registryMC.add("h3dAntiLambdaFeeddownFromXi0", "h3dAntiLambdaFeeddownFromXi0", kTH3D, {multAxis, ptAxis, ptAxis}); + } + } + + // Histograms for real data in MB events + if (doprocessDataMB) { + + // Event counters + registryDataMB.add("number_of_events_data_MB", "number of events in data (MB)", HistType::kTH1D, {{20, 0, 20, "Event Cuts"}}); + registryDataMB.add("number_of_events_vsmultiplicity_MB", "number of events in data vs multiplicity (MB)", HistType::kTH1D, {{101, 0, 101, "Multiplicity percentile"}}); + + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(1, "All collisions"); + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(2, "Zorro selection"); + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(3, "sel8"); + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(4, "posZ cut"); + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(5, "kNoSameBunchPileup"); + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(6, "kIsGoodZvtxFT0vsPV"); + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(7, "No empty events"); + registryDataMB.get(HIST("number_of_events_data_MB"))->GetXaxis()->SetBinLabel(8, "At least one jet"); + + // Histograms for analysis of strange hadrons + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryDataMB.add("Lambda_in_MB", "Lambda_in_MB", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryDataMB.add("AntiLambda_in_MB", "AntiLambda_in_MB", HistType::kTH3F, {multAxis, ptAxis, invMassLambdaAxis}); + registryDataMB.add("K0s_in_MB", "K0s_in_MB", HistType::kTH3F, {multAxis, ptAxis, invMassK0sAxis}); + } + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryDataMB.add("XiPos_in_MB", "XiPos_in_MB", HistType::kTH3F, {multAxis, ptAxis, invMassXiAxis}); + registryDataMB.add("XiNeg_in_MB", "XiNeg_in_MB", HistType::kTH3F, {multAxis, ptAxis, invMassXiAxis}); + registryDataMB.add("OmegaPos_in_MB", "OmegaPos_in_MB", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + registryDataMB.add("OmegaNeg_in_MB", "OmegaNeg_in_MB", HistType::kTH3F, {multAxis, ptAxis, invMassOmegaAxis}); + } + if (particleOfInterestDict[ParticleOfInterest::kPions]) { + registryDataMB.add("Pion_in_MB", "Pion_in_MB", HistType::kTHnSparseF, {multAxis, ptAxisLongLived, nsigmaTPCAxis, nsigmaTOFAxis, dcaAxis}); + } + if (particleOfInterestDict[ParticleOfInterest::kKaons]) { + registryDataMB.add("Kaon_in_MB", "Kaon_in_MB", HistType::kTHnSparseF, {multAxis, ptAxisLongLived, nsigmaTPCAxis, nsigmaTOFAxis, dcaAxis}); + } + if (particleOfInterestDict[ParticleOfInterest::kProtons]) { + registryDataMB.add("Proton_in_MB", "Proton_in_MB", HistType::kTHnSparseF, {multAxis, ptAxisLongLived, nsigmaTPCAxis, nsigmaTOFAxis, dcaAxis}); + } + if (saveChargedParticleMB) { + registryDataMB.add("ChargedTrack_in_MB", "ChargedTrack_in_MB", HistType::kTH2F, {multAxis, ptChargedAxis}); + } + } + + if (doprocessPrepareUnfolding) { + // Response matrix detector (only mathced) + // Assi: X = Centrality, Y = pT_gen, Z = pT_reco + registryMC.add("hDetectorResponse", "hDetectorResponse", HistType::kTH3F, {multAxis, ptJetGenAxisUnfold, ptJetRecoAxisUnfold}); + + // Efficiency components (GEN level) + // Assi: X = Centrality, Y = pT_gen + registryMC.add("hJetPtGenTotal", "hJetPtGenTotal", HistType::kTH2F, {multAxis, ptJetGenAxisUnfold}); + registryMC.add("hJetPtGenMatched", "hJetPtGenMatched", HistType::kTH2F, {multAxis, ptJetGenAxisUnfold}); + + // Purity components (RECO level) + // Assi: X = Centrality, Y = pT_reco + registryMC.add("hJetPtRecoTotal", "hJetPtRecoTotal", HistType::kTH2F, {multAxis, ptJetRecoAxisUnfold}); + registryMC.add("hJetPtRecoMatched", "hJetPtRecoMatched", HistType::kTH2F, {multAxis, ptJetRecoAxisUnfold}); } } @@ -545,6 +784,12 @@ struct StrangenessInJetsIons { return deltaPhi; } + // Compute energy + double GetEnergy(double px, double py, double pz, double mass) + { + return std::sqrt(px * px + py * py + pz * pz + mass * mass); + } + // Check if particle is a physical primary or a decay product of a heavy-flavor hadron bool isPhysicalPrimaryOrFromHF(aod::McParticle const& particle, aod::McParticles const& mcParticles) { @@ -634,6 +879,81 @@ struct StrangenessInJetsIons { u2.SetXYZ(u2x, u2y, pz); } + // Compute two random directions in phi to vector p + void getRandomConeDirections(const fastjet::PseudoJet& jet, TVector3& u1, TVector3& u2, double minDeltaPhi, double maxDeltaPhi) + { + // Generate random deltaPhi in specified range + double randomDeltaPhi = fRng.Uniform(minDeltaPhi, maxDeltaPhi); + + u1.SetPtEtaPhi(jet.pt(), jet.eta(), jet.phi() + randomDeltaPhi); + u2.SetPtEtaPhi(jet.pt(), jet.eta(), jet.phi() - randomDeltaPhi); + } + + float getDeltaR(double eta1, double phi1, double etaAxis, double phiAxis) + { + float deltaEta = eta1 - etaAxis; + float deltaPhi = getDeltaPhi(phi1, phiAxis); + return std::sqrt(deltaEta * deltaEta + deltaPhi * deltaPhi); + } + + // selProcess = 0 (data), = 1 (MC GEN), = 2 (MC REC) + void computeRandomConeDeltaPt(const std::vector& fjParticles, + const std::vector& jets, + float multiplicity, double rhoPerp, + int selProcess = 0) + { + // Generate eta and phi for random cone in acceptance region + double randomConeEta = fRng.Uniform(configTracks.etaMin + rJet, configTracks.etaMax - rJet); + double randomConePhi = fRng.Uniform(0.0, TMath::TwoPi()); + + // Exclude leading jet region + if (!jets.empty()) { + float dPhiLeadingJet = getDeltaPhi(jets[0].phi(), randomConePhi); + float dEtaLeadingJet = jets[0].eta() - randomConeEta; + float dRLeadingJet = std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet); + + int attempts = 0; + const int maxAttempts = 100; + // If the random cone overlaps with the leading jet (distance < 1.5), then generate the coordinates again + while (dRLeadingJet < 1.5 && attempts < maxAttempts) { + randomConeEta = fRng.Uniform(configTracks.etaMin + rJet, configTracks.etaMax - rJet); + randomConePhi = fRng.Uniform(0.0, TMath::TwoPi()); + + dPhiLeadingJet = getDeltaPhi(jets[0].phi(), randomConePhi); + dEtaLeadingJet = jets[0].eta() - randomConeEta; + dRLeadingJet = std::sqrt(dEtaLeadingJet * dEtaLeadingJet + dPhiLeadingJet * dPhiLeadingJet); + attempts++; + } + } + + // Sum pT of all the particles (charged tracks + V0 if included) falling in the random cone + float randomConePt = 0.0; + for (auto const& part : fjParticles) { + float dPhi = getDeltaPhi(part.phi(), randomConePhi); + float dEta = part.eta() - randomConeEta; + float dR = std::sqrt(dEta * dEta + dPhi * dPhi); + + if (dR < rJet) { + randomConePt += part.pt(); + } + } + + // Compute delta pT: pT_cone - (Area_cone * rho) + double coneArea = TMath::Pi() * rJet * rJet; + double deltaPtRandomCone = randomConePt - (coneArea * rhoPerp); + + if (selProcess == 0) { + registryData.fill(HIST("h2_centrality_deltaPt_RandomCone"), multiplicity, deltaPtRandomCone); + registryData.fill(HIST("h2_centrality_rhoPerp"), multiplicity, rhoPerp); + } else if (selProcess == 1) { + registryMC.fill(HIST("h2_centrality_deltaPt_RandomCone_gen"), multiplicity, deltaPtRandomCone); + registryMC.fill(HIST("h2_centrality_rhoPerp_gen"), multiplicity, rhoPerp); + } else if (selProcess == 2) { + registryMC.fill(HIST("h2_centrality_deltaPt_RandomCone_rec"), multiplicity, deltaPtRandomCone); + registryMC.fill(HIST("h2_centrality_rhoPerp_rec"), multiplicity, rhoPerp); + } + } + // Find ITS hit template bool hasITSHitOnLayer(const TrackIts& track, int layer) @@ -943,12 +1263,12 @@ struct StrangenessInJetsIons { } // V0 selections - if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configV0.v0cospaMin) + if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configCasc.v0cospaMinCasc) return false; // if (casc.v0radius() < configV0.minimumV0Radius || casc.v0radius() > configV0.maximumV0Radius) - if (casc.v0radius() < configV0.minimumV0Radius) + if (casc.v0radius() < configCasc.minimumV0RadiusCasc) return false; - if (std::fabs(casc.dcaV0daughters()) > configV0.dcaV0DaughtersMax) + if (std::fabs(casc.dcaV0daughters()) > configCasc.dcaV0DaughtersMaxCasc) return false; if (std::fabs(casc.dcapostopv()) < configCasc.dcaPosToPVminV0) return false; @@ -1059,12 +1379,12 @@ struct StrangenessInJetsIons { } // V0 selections - if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configV0.v0cospaMin) + if (casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ()) < configCasc.v0cospaMinCasc) return false; // if (casc.v0radius() < configV0.minimumV0Radius || casc.v0radius() > configV0.maximumV0Radius) - if (casc.v0radius() < configV0.minimumV0Radius) + if (casc.v0radius() < configCasc.minimumV0RadiusCasc) return false; - if (std::fabs(casc.dcaV0daughters()) > configV0.dcaV0DaughtersMax) + if (std::fabs(casc.dcaV0daughters()) > configCasc.dcaV0DaughtersMaxCasc) return false; if (std::fabs(casc.dcapostopv()) < configCasc.dcaPosToPVminV0) return false; @@ -1121,6 +1441,64 @@ struct StrangenessInJetsIons { return true; } + // --- MB Selections ------------------------------------ + // The rapidty cut is added, which is not required in jet and UE + // K0s selections (MB) + template + bool passedK0ShortSelectionMB(const K0short& v0, const TrackPos& ptrack, const TrackNeg& ntrack, const TVector3& vtxPos) + { + bool passedSel = passedK0ShortSelection(v0, ptrack, ntrack, vtxPos); + + if (!passedRapidityCut(v0.yK0Short(), configV0.rapidityMax)) + return false; + return passedSel; + } + + // Lambda selections (MB) + template + bool passedLambdaSelectionMB(const Lambda& v0, const TrackPos& ptrack, const TrackNeg& ntrack, const TVector3& vtxPos) + { + bool passedSel = passedLambdaSelection(v0, ptrack, ntrack, vtxPos); + + if (!passedRapidityCut(v0.yLambda(), configV0.rapidityMax)) + return false; + return passedSel; + } + + // AntiLambda selections (MB) + template + bool passedAntiLambdaSelectionMB(const AntiLambda& v0, const TrackPos& ptrack, const TrackNeg& ntrack, const TVector3& vtxPos) + { + bool passedSel = passedAntiLambdaSelection(v0, ptrack, ntrack, vtxPos); + + if (!passedRapidityCut(v0.yLambda(), configV0.rapidityMax)) + return false; + return passedSel; + } + + // Xi Selections (MB) + template + bool passedXiSelectionMB(const Xi& casc, const TrackPos& ptrack, const TrackNeg& ntrack, const TrackBac& btrack, const Coll& coll) + { + bool passedSel = passedXiSelection(casc, ptrack, ntrack, btrack, coll); + + if (!passedRapidityCut(casc.yXi(), configV0.rapidityMax)) + return false; + return passedSel; + } + + // Omega selections (MB) + template + bool passedOmegaSelectionMB(const Omega& casc, const TrackPos& ptrack, const TrackNeg& ntrack, const TrackBac& btrack, const Coll& coll) + { + bool passedSel = passedOmegaSelection(casc, ptrack, ntrack, btrack, coll); + + if (!passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) + return false; + return passedSel; + } + // ------------------------------------------------------ + // Event selection for MC Reco collision template bool selectRecoEvent(const coll& recoColl) @@ -1142,7 +1520,7 @@ struct StrangenessInJetsIons { // Return true if McCollisions has at least 1 RECO collisions that passed event selections template - bool HasRecoEvent(int mcCollIndex, RecoColls recoCollisions) + bool HasRecoEvent(int mcCollIndex, const RecoColls& recoCollisions) { for (const auto& recoColl : recoCollisions) { if (mcCollIndex == recoColl.mcCollisionId()) { @@ -1174,9 +1552,9 @@ struct StrangenessInJetsIons { return std::abs(yParticle) < rapidityCut; } - void FillFullEventHistoMCGEN(aod::McParticle const& particle, - const double& genMultiplicity, - bool hasReco = false) + void FillMBEventHistoMCGEN(aod::McParticle const& particle, + const double& genMultiplicity, + bool hasReco = false) { if (particle.isPhysicalPrimary() && passedRapidityCut(particle.y(), configV0.rapidityMax)) { switch (particle.pdgCode()) { @@ -1205,23 +1583,35 @@ struct StrangenessInJetsIons { } break; case kXiMinus: - if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + if (particleOfInterestDict[ParticleOfInterest::kCascades] || calculateFeeddownMatrix) { registryMC.fill(HIST("XiNeg_generated_MB"), genMultiplicity, particle.pt()); } + if (particleOfInterestDict[ParticleOfInterest::kCascades] && hasReco) { + registryMC.fill(HIST("XiNeg_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } break; case kXiPlusBar: - if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + if (particleOfInterestDict[ParticleOfInterest::kCascades] || calculateFeeddownMatrix) { registryMC.fill(HIST("XiPos_generated_MB"), genMultiplicity, particle.pt()); } + if (particleOfInterestDict[ParticleOfInterest::kCascades] && hasReco) { + registryMC.fill(HIST("XiPos_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } break; case kOmegaMinus: if (particleOfInterestDict[ParticleOfInterest::kCascades]) { registryMC.fill(HIST("OmegaNeg_generated_MB"), genMultiplicity, particle.pt()); + if (hasReco) { + registryMC.fill(HIST("OmegaNeg_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } } break; case kOmegaPlusBar: if (particleOfInterestDict[ParticleOfInterest::kCascades]) { registryMC.fill(HIST("OmegaPos_generated_MB"), genMultiplicity, particle.pt()); + if (hasReco) { + registryMC.fill(HIST("OmegaPos_generated_w_reco_MB"), genMultiplicity, particle.pt()); + } } break; case kPiPlus: @@ -1262,12 +1652,12 @@ struct StrangenessInJetsIons { // Fill Minimum Bias histograms template - void FillFullEventHistoMCREC(MCRecoCollision collision, - aod::McParticles const& mcParticles, - V0PerColl const& v0sPerColl, - CascPerColl const& cascPerColl, - TracksPerColl const& tracksPerColl, - const double& multiplicity) + void FillMBEventHistoMCREC(const MCRecoCollision& collision, + aod::McParticles const& mcParticles, + V0PerColl const& v0sPerColl, + CascPerColl const& cascPerColl, + TracksPerColl const& tracksPerColl, + const double& multiplicity) { // V0 particles if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { @@ -1296,6 +1686,9 @@ struct StrangenessInJetsIons { // Vertex position vector TVector3 vtxPos(collision.posX(), collision.posY(), collision.posZ()); + bool passedLambda = false; // true if Lambda selections are passed + bool passedAntiLambda = false; // true if AntiLambda selections are passed + // K0s if (passedK0ShortSelection(v0, pos, neg, vtxPos) && motherPos.pdgCode() == kK0Short && @@ -1307,12 +1700,19 @@ struct StrangenessInJetsIons { motherPos.pdgCode() == kLambda0 && passedRapidityCut(v0.yLambda(), configV0.rapidityMax)) { registryMC.fill(HIST("Lambda_reconstructed_MB_incl"), multiplicity, v0.pt()); + passedLambda = true; } // AntiLambda if (passedAntiLambdaSelection(v0, pos, neg, vtxPos) && motherPos.pdgCode() == kLambda0Bar && passedRapidityCut(v0.yLambda(), configV0.rapidityMax)) { registryMC.fill(HIST("AntiLambda_reconstructed_MB_incl"), multiplicity, v0.pt()); + passedAntiLambda = true; + } + + if (calculateFeeddownMatrix) { + double ptGen = motherPos.pt(); + fillFeeddownMatrix(v0, ptGen, multiplicity, passedLambda, passedAntiLambda); } if (!motherPos.isPhysicalPrimary()) @@ -1376,27 +1776,80 @@ struct StrangenessInJetsIons { continue; if (std::abs(motherPos.pdgCode()) != kLambda0) continue; - if (!motherBach.isPhysicalPrimary()) - continue; - - if (std::abs(motherPos.eta()) > 0.8) + if (std::abs(motherBach.eta()) > 0.8) continue; // Xi+ - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && motherBach.pdgCode() == kXiPlusBar) { - registryMC.fill(HIST("XiPos_reconstructed_MB"), multiplicity, casc.pt()); + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + motherBach.pdgCode() == kXiPlusBar && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiPos_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Xi- - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && motherBach.pdgCode() == kXiMinus) { - registryMC.fill(HIST("XiNeg_reconstructed_MB"), multiplicity, casc.pt()); + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + motherBach.pdgCode() == kXiMinus && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiNeg_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Omega+ - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && motherBach.pdgCode() == kOmegaPlusBar) { - registryMC.fill(HIST("OmegaPos_reconstructed_MB"), multiplicity, casc.pt()); + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + motherBach.pdgCode() == kOmegaPlusBar && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaPos_reconstructed_MB_incl"), multiplicity, casc.pt()); } // Omega- - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && motherBach.pdgCode() == kOmegaMinus) { - registryMC.fill(HIST("OmegaNeg_reconstructed_MB"), multiplicity, casc.pt()); + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + motherBach.pdgCode() == kOmegaMinus && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaNeg_reconstructed_MB_incl"), multiplicity, casc.pt()); + } + + if (!motherBach.isPhysicalPrimary()) + continue; + + // Rapidity generated particle + double yGen = motherBach.y(); + + // Histograms below are filled only for PhysicalPrimary particles + // Xi+ (primary) + if (motherBach.pdgCode() == kXiPlusBar) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("XiPos_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiPos_reconstructed_MB"), multiplicity, casc.pt()); + } + } + // Xi- (primary) + if (motherBach.pdgCode() == kXiMinus) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + passedRapidityCut(casc.yXi(), configV0.rapidityMax)) { + registryMC.fill(HIST("XiNeg_reconstructed_MB"), multiplicity, casc.pt()); + } + } + // Omega+ (primary) + if (motherBach.pdgCode() == kOmegaPlusBar) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaPos_reconstructed_MB"), multiplicity, casc.pt()); + } + } + // Omega- (primary) + if (motherBach.pdgCode() == kOmegaMinus) { + if (passedRapidityCut(yGen, configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_MB"), multiplicity, casc.pt()); + } + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && + passedRapidityCut(casc.yOmega(), configV0.rapidityMax)) { + registryMC.fill(HIST("OmegaNeg_reconstructed_MB"), multiplicity, casc.pt()); + } } } } @@ -1459,95 +1912,576 @@ struct StrangenessInJetsIons { } } - // Process data - void processData(SelCollisions::iterator const& collision, aod::V0Datas const& fullV0s, - aod::CascDataExt const& Cascades, DaughterTracks const& tracks, - aod::BCsWithTimestamps const&) + template + void fillFeeddownMatrix(const TV0& v0, float pt, float centrality, bool passedLambda, bool passedAntiLambda) + // Fill feeddown matrix for Lambdas or AntiLambdas + // Adapted from: PWGLF/Tasks/Strangeness/derivedlambdakzeroanalysis.cxx { - // Fill event counter before event selection - registryData.fill(HIST("number_of_events_data"), 0.5); + if (!v0.has_mcMotherParticle()) + return; // does not have mother particle in record, skip + + auto v0mother = v0.mcMotherParticle(); + float rapidityXi = 999.; + if (std::abs(v0mother.pdgCode()) == PDG_t::kXiMinus) + rapidityXi = RecoDecay::y(std::array{v0mother.px(), v0mother.py(), v0mother.pz()}, o2::constants::physics::MassXiMinus); + if (std::abs(v0mother.pdgCode()) == o2::constants::physics::Pdg::kXi0) + rapidityXi = RecoDecay::y(std::array{v0mother.px(), v0mother.py(), v0mother.pz()}, o2::constants::physics::MassXi0); + + if (std::fabs(rapidityXi) > 0.5f) + return; // not a valid mother rapidity (PDG selection is later) + + // __________________________________________ + if (passedLambda) { + if (v0mother.isPhysicalPrimary()) { + if (v0mother.pdgCode() == PDG_t::kXiMinus) { + registryMC.fill(HIST("h3dLambdaFeeddown"), centrality, pt, std::hypot(v0mother.px(), v0mother.py())); + } + if (v0mother.pdgCode() == PDG_t::kXiMinus || v0mother.pdgCode() == o2::constants::physics::Pdg::kXi0) { + registryMC.fill(HIST("h3dLambdaFeeddownFromXi0"), centrality, pt, std::hypot(v0mother.px(), v0mother.py())); + } + } + } - // Get the bunch crossing (BC) information associated with the collision - auto bc = collision.template bc_as(); + if (passedAntiLambda) { + if (v0mother.isPhysicalPrimary()) { + if (v0mother.pdgCode() == PDG_t::kXiPlusBar) { + registryMC.fill(HIST("h3dAntiLambdaFeeddown"), centrality, pt, std::hypot(v0mother.px(), v0mother.py())); + } + if (v0mother.pdgCode() == PDG_t::kXiPlusBar || v0mother.pdgCode() == -o2::constants::physics::Pdg::kXi0) { + registryMC.fill(HIST("h3dAntiLambdaFeeddownFromXi0"), centrality, pt, std::hypot(v0mother.px(), v0mother.py())); + } + } + } + } - // Initialize CCDB objects using the BC info - initCCDB(bc); + /** + * @brief Identifies the index of the common mother for two MC particles. + * + * @tparam T Type of the MC particle objects + * @param posParticle MC particle associated with the positive daughter + * @param negParticle MC particle associated with the negative daughter + * @return int The index of the common mother, or -1 if no common mother exists. + */ + template + int GetCommonMotherId(aod::McParticles const& mcParticles, + const T& posParticle, const T& negParticle) + { + int motherIdPos = posParticle.mothersIds()[0]; + int motherIdNeg = negParticle.mothersIds()[0]; - // If skimmed processing is enabled, skip this event unless it passes Zorro selection - if (cfgSkimmedProcessing && !zorro.isSelected(collision.template bc_as().globalBC())) { - return; + const auto& motherPos = mcParticles.iteratorAt(motherIdPos); + const auto& motherNeg = mcParticles.iteratorAt(motherIdNeg); + if (motherPos != motherNeg) { + return -1; // Return -1 if they originate from different parents } - // Fill event counter after zorro selection - registryData.fill(HIST("number_of_events_data"), 1.5); + return motherIdPos; // Return mother ID + } - // Event selection - if (!collision.sel8()) - return; + /** + * Returns true if the track is a daughter of the V0 candidate + * Adapted from: PWGJE/Core/JetV0Utilities.h + * + * @param track track that is being checked + * @param candidate V0 candidate that is being checked + */ + template + bool isV0DaughterTrack(T& track, U& candidate) + { + if (candidate.posTrackId() == track.globalIndex() || candidate.negTrackId() == track.globalIndex()) { + return true; + } else { + return false; + } + } - // Fill event counter after sel8 selection - registryData.fill(HIST("number_of_events_data"), 2.5); + /** + * @brief Add V0s as input for the jet finder algorithm in DATA + * + * @tparam Track + * @tparam V0PerColl The container type holding the sliced V0 candidates for the current collision. + * + * @param[in,out] fjInput Vector of FastJet PseudoJets where valid V0s will be appended. + * @param[in] fjTracks Vector containing tracks already selected for jet finder input. + * @param[in] v0sPerColl V0 candidates belonging to this specific collision. + * @param[in] vtxPos TVector3 object representing the vertex position. + */ + template + void AddV0sForJetReconstructionData(std::vector& fjInput, + const std::vector& fjTracks, + V0PerColl const& v0sPerColl, + const TVector3& vtxPos) + { + // Vector of labels: if true remove the element from fjInput + std::vector isTrackReplaced(fjTracks.size(), false); + std::vector v0PseudoJets; // V0s to use as input for jet finder + + for (const auto& v0 : v0sPerColl) { + const auto& pos = v0.template posTrack_as(); + const auto& neg = v0.template negTrack_as(); + + bool isK0S = false, isLambda = false, isAntiLambda = false; + // K0s + if (passedK0ShortSelection(v0, pos, neg, vtxPos)) { + // LOG(info) << "[AddV0sForJetReconstructionData] Add K0S as input for jet finder."; + double energy = GetEnergy(v0.px(), v0.py(), v0.pz(), o2::constants::physics::MassK0Short); + fastjet::PseudoJet fourMomentum(v0.px(), v0.py(), v0.pz(), energy); + v0PseudoJets.emplace_back(fourMomentum); + isK0S = true; + } + // Lambda + if (passedLambdaSelection(v0, pos, neg, vtxPos)) { + // LOG(info) << "[AddV0sForJetReconstructionData] Add Lambda as input for jet finder."; + double energy = GetEnergy(v0.px(), v0.py(), v0.pz(), o2::constants::physics::MassLambda0); + fastjet::PseudoJet fourMomentum(v0.px(), v0.py(), v0.pz(), energy); + v0PseudoJets.emplace_back(fourMomentum); + isLambda = true; + } + // AntiLambda + if (passedAntiLambdaSelection(v0, pos, neg, vtxPos)) { + // LOG(info) << "[AddV0sForJetReconstructionData] Add AntiLambda as input for jet finder."; + double energy = GetEnergy(v0.px(), v0.py(), v0.pz(), o2::constants::physics::MassLambda0Bar); + fastjet::PseudoJet fourMomentum(v0.px(), v0.py(), v0.pz(), energy); + v0PseudoJets.emplace_back(fourMomentum); + isAntiLambda = true; + } - // Require vertex position within the allowed z range - if (std::fabs(collision.posZ()) > zVtx) - return; + // Exclude daughter tracks in case a V0 is found + bool isV0 = isK0S || isLambda || isAntiLambda; + if (!isV0) + continue; + for (uint64_t i = 0; i < fjTracks.size(); ++i) { + if (isV0DaughterTrack(fjTracks[i], v0)) { + // LOG(info) << "[AddV0sForJetReconstructionData] V0 daughter track found in fjTracks."; + isTrackReplaced[i] = true; + } + } + } - // Fill event counter after z vertex selection - registryData.fill(HIST("number_of_events_data"), 3.5); + std::vector cleanFjInput; + cleanFjInput.reserve(fjInput.size()); + for (uint64_t i = 0; i < fjInput.size(); ++i) { + if (!isTrackReplaced[i]) + cleanFjInput.push_back(fjInput[i]); + } + for (const auto& v0pj : v0PseudoJets) { + cleanFjInput.push_back(v0pj); + } + // LOG(info) << "[AddV0sForJetReconstructionData] Size fjInput: " << fjInput.size(); + fjInput = std::move(cleanFjInput); + // LOG(info) << "[AddV0sForJetReconstructionData] Size fjInput dopo move: " << fjInput.size(); + } - // Reject collisions associated to the same found BC - if (requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) - return; + /** + * @brief Add V0s as input for the jet finder algorithm in MC reco (detector level) + * + * @tparam Track + * @tparam V0PerColl The container type holding the sliced V0 candidates for the current collision. + * + * @param[in,out] fjInput Vector of FastJet PseudoJets where valid V0s will be appended. + * @param[in] fjTracks Vector containing tracks already selected for jet finder input. + * @param[in] v0sPerColl Sliced V0 candidates belonging to this specific collision. + * @param[in] mcParticles Global MC particles table used to look up mother particle truth information. + * @param[in] vtxPos TVector3 object representing the vertex position. + */ + template + void AddV0sForJetReconstructionMCD(std::vector& fjInput, + const std::vector& fjTracks, + V0PerColl const& v0sPerColl, + aod::McParticles const& mcParticles, + const TVector3& vtxPos) + { + // Vector of labels: if true remove the element from fjInput + std::vector isTrackReplaced(fjTracks.size(), false); + std::vector v0PseudoJets; // V0s to use as input for jet finder - // Fill event counter after selection kNoSameBunchPileup - registryData.fill(HIST("number_of_events_data"), 4.5); + for (const auto& v0 : v0sPerColl) { + const auto& pos = v0.template posTrack_as(); + const auto& neg = v0.template negTrack_as(); - // Compatible z_vtx from FT0 and from PV - if (requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) - return; + // Get MC particles + if (!pos.has_mcParticle() || !neg.has_mcParticle()) { + continue; + } + const auto& posParticle = pos.template mcParticle_as(); + const auto& negParticle = neg.template mcParticle_as(); + if (!posParticle.has_mothers() || !negParticle.has_mothers()) { + continue; + } - // Fill event counter after selection kIsGoodZvtxFT0vsPV - registryData.fill(HIST("number_of_events_data"), 5.5); + // Select particles originating from the same parent + int motherId = GetCommonMotherId(mcParticles, posParticle, negParticle); + if (motherId < 0) + continue; // if motherId is -1, it means no common mother was found + const auto& mother = mcParticles.iteratorAt(motherId); + const bool isPhysPrim = mother.isPhysicalPrimary(); + int pdgParent = mother.pdgCode(); + + /// NOTE: V0s are required to be primary particles; + bool isK0S = false, isLambda = false, isAntiLambda = false; + // K0s + if (passedK0ShortSelection(v0, pos, neg, vtxPos) && pdgParent == kK0Short && isPhysPrim) { + // LOG(info) << "[AddV0sForJetReconstructionMCD] Add K0S as input for jet finder."; + double energy = GetEnergy(v0.px(), v0.py(), v0.pz(), o2::constants::physics::MassK0Short); + fastjet::PseudoJet fourMomentum(v0.px(), v0.py(), v0.pz(), energy); + v0PseudoJets.emplace_back(fourMomentum); + isK0S = true; + } + // Lambda + if (passedLambdaSelection(v0, pos, neg, vtxPos) && pdgParent == kLambda0 && isPhysPrim) { + // LOG(info) << "[AddV0sForJetReconstructionMCD] Add Lambda as input for jet finder."; + double energy = GetEnergy(v0.px(), v0.py(), v0.pz(), o2::constants::physics::MassLambda0); + fastjet::PseudoJet fourMomentum(v0.px(), v0.py(), v0.pz(), energy); + v0PseudoJets.emplace_back(fourMomentum); + isLambda = true; + } + // AntiLambda + if (passedAntiLambdaSelection(v0, pos, neg, vtxPos) && pdgParent == kLambda0Bar && isPhysPrim) { + // LOG(info) << "[AddV0sForJetReconstructionMCD] Add AntiLambda as input for jet finder."; + double energy = GetEnergy(v0.px(), v0.py(), v0.pz(), o2::constants::physics::MassLambda0Bar); + fastjet::PseudoJet fourMomentum(v0.px(), v0.py(), v0.pz(), energy); + v0PseudoJets.emplace_back(fourMomentum); + isAntiLambda = true; + } - // Event multiplicity - float centrality; - if (centrEstimator == 0) { - centrality = collision.centFT0C(); - } else { - centrality = collision.centFT0M(); + // Exclude daughter tracks in case a V0 is found + bool isV0 = isK0S || isLambda || isAntiLambda; + if (!isV0) + continue; + for (uint64_t i = 0; i < fjTracks.size(); ++i) { + if (isV0DaughterTrack(fjTracks[i], v0)) { + // LOG(info) << "[AddV0sForJetReconstructionMCD] V0 daughter track found in fjTracks."; + isTrackReplaced[i] = true; + } + } } - registryData.fill(HIST("number_of_events_vsmultiplicity_MB"), centrality); - // Loop over reconstructed tracks - std::vector fjParticles; - for (auto const& track : tracks) { + std::vector cleanFjInput; + cleanFjInput.reserve(fjInput.size()); + for (uint64_t i = 0; i < fjInput.size(); ++i) { + if (!isTrackReplaced[i]) + cleanFjInput.push_back(fjInput[i]); + } + for (const auto& v0pj : v0PseudoJets) { + cleanFjInput.push_back(v0pj); + } + // LOG(info) << "[AddV0sForJetReconstructionMCD] Size fjInput: " << fjInput.size(); + fjInput = std::move(cleanFjInput); + // LOG(info) << "[AddV0sForJetReconstructionMCD] Size fjInput dopo move: " << fjInput.size(); + } - // Require that tracks pass selection criteria - if (!passedTrackSelectionForJetReconstruction(track)) - continue; + /** + * @brief Add V0s as input for the jet finder algorithm at Particle Level (MC Generated) + * + * @tparam McParticleType + * @tparam ParticleColl The container type holding the sliced MC particles. + * + * @param[in,out] fjInput Vector of FastJet PseudoJets where V0s will be appended. + * @param[in] fjParticleObj Vector containing the MC particles already selected for jet finder input. + * @param[in] mcParticlesPerColl MC particles belonging to this specific collision. + * @param[in] mcParticles Full McParticles. + */ + template + void AddV0sForJetReconstructionMCP(std::vector& fjInput, + const std::vector& fjParticleObj, + ParticleColl const& mcParticlesPerColl, + aod::McParticles const& mcParticles) + { + std::vector isTrackReplaced(fjParticleObj.size(), false); + std::vector v0PseudoJets; + + // Add V0s to the input list for the jet finder and eventually remove their daughter particles + for (const auto& p : mcParticlesPerColl) { + int pdgAbs = std::abs(p.pdgCode()); + /// NOTE: p.isPhysicalPrimary() is required + if (p.isPhysicalPrimary() && (pdgAbs == kK0Short || pdgAbs == kLambda0)) { + double mass = (pdgAbs == kK0Short) ? o2::constants::physics::MassK0Short : o2::constants::physics::MassLambda0; + double energy = std::sqrt(p.p() * p.p() + mass * mass); + + fastjet::PseudoJet pj(p.px(), p.py(), p.pz(), energy); + pj.set_user_index(p.pdgCode()); + v0PseudoJets.push_back(pj); + // LOG(info) << "[AddV0sForJetReconstructionMCP] Add V0 as input for jet finder."; + } + } - // 4-momentum representation of a particle - fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); - fjParticles.emplace_back(fourMomentum); + // Remove V0 daughter particles if already in the input list for the jet finder + for (uint64_t i = 0; i < fjParticleObj.size(); ++i) { + const auto& mcPart = fjParticleObj[i]; + if (!mcPart.has_mothers()) + continue; + auto mother = mcParticles.iteratorAt(mcPart.mothersIds()[0]); + int motherPdg = std::abs(mother.pdgCode()); + // LOG(info) << "[AddV0sForJetReconstructionMCP] Mother pdg code:" << motherPdg; + if (motherPdg == kK0Short || motherPdg == kLambda0) { + isTrackReplaced[i] = true; + // LOG(info) << "[AddV0sForJetReconstructionMCP] V0 daughter particle found in fjParticleObj."; + } } - // Reject empty events - if (fjParticles.size() < 1) - return; - registryData.fill(HIST("number_of_events_data"), 6.5); + std::vector cleanFjInput; + cleanFjInput.reserve(fjInput.size()); + for (uint64_t i = 0; i < fjInput.size(); ++i) { + if (!isTrackReplaced[i]) + cleanFjInput.push_back(fjInput[i]); + } + for (const auto& v0pj : v0PseudoJets) { + cleanFjInput.push_back(v0pj); + } + // LOG(info) << "[AddV0sForJetReconstructionMCP] Size fjInput: " << fjInput.size(); + fjInput = std::move(cleanFjInput); + // LOG(info) << "[AddV0sForJetReconstructionMCP] Size fjInput dopo move: " << fjInput.size(); + } - // Cluster particles using the anti-kt algorithm + // --- LEVY-TSALLIS --- + // Implementation adapted from: https://github.com/alisw/AliPhysics/blob/master/PWGLF/SPECTRA/UTILS/SpectraUtils.C + static double LevyTsallis_Func(const double* x, const double* p) + { + double pt = x[0]; + double mass = p[0]; + double mt = std::sqrt(pt * pt + mass * mass); + double n = p[1]; + double C = p[2]; + double norm = p[3]; + + double part1 = (n - 1.) * (n - 2.); + double part2 = n * C * (n * C + mass * (n - 2.)); + double part3 = part1 / part2; + double part4 = 1. + (mt - mass) / n / C; + double part5 = TMath::Power(part4, -n); + return pt * norm * part3 * part5; + } + // -------------------- + + /** + * @brief Inject particles of the thermal toy model into the input list for the jet finder + * @param[in,out] fjInput Vector of FastJet PseudoJets where thermal background will be appended. + * @param[in] centrality + * @param[in] isReco if true (RECO MC), do not store id -999 with set_user_index() + */ + void InjectThermalBackground(std::vector& fjInput, std::vector& toyTracks, float centrality, bool isReco = false) + { + // Fit results of charged-particle spectra in MB OO data using Levy-Tsallis + static const std::vector bkgTable = { + // centMin, centMax, mass, n, C, dNch_deta + {0.0, 5.0, 0.50369, 6.496, 0.1778, 126.764}, + {5.0, 10.0, 0.51298, 6.346, 0.1724, 105.196}, + {10.0, 20.0, 0.51757, 6.221, 0.1685, 86.0196}, + {20.0, 30.0, 0.52036, 6.089, 0.1646, 66.2334}, + {30.0, 50.0, 0.52361, 5.927, 0.1594, 44.3346}, + {50.0, 100.0, 0.56123, 5.570, 0.1432, 24.30225}}; + // {0.0, 100.0, 0.52145, 6.072, 0.1635, 37.8073} + + // Extract parameters for this centrality value + BkgFitParams currentParams = bkgTable.back(); // fallback to last element + for (const auto& params : bkgTable) { + if (centrality >= params.centMin && centrality < params.centMax) { + currentParams = params; + break; + } + } + + fTsallisToy->SetParameters(currentParams.mass, currentParams.n, currentParams.C, 1.0); + + // Compute number of background particles in the event using poissonian fluctuations + double etaWindow = std::fabs(configTracks.etaMax - configTracks.etaMin); + double meanMultiplicity = currentParams.dNch_deta * etaWindow; + int nBkgParticles = fRng.Poisson(meanMultiplicity); + if (isReco) { + registryMC.fill(HIST("thermalToy_nBkg_rec"), centrality, nBkgParticles); + } else { + registryMC.fill(HIST("thermalToy_nBkg_gen"), centrality, nBkgParticles); + } + // Generation and embedding of background particles + for (int i = 0; i < nBkgParticles; ++i) { + // Compute pT using Levy-Tsallis + isotropic background + double pt = fTsallisToy->GetRandom(&fRng); + double phi = fRng.Uniform(0.0, TMath::TwoPi()); + double eta = fRng.Uniform(configTracks.etaMin, configTracks.etaMax); + + if (pt < 0.1f) + continue; + + double px = pt * std::cos(phi); + double py = pt * std::sin(phi); + double pz = pt * std::sinh(eta); + double energy = GetEnergy(px, py, pz, currentParams.mass); + fastjet::PseudoJet bkgParticle(px, py, pz, energy); + + // ID = -999, to identify these background particles + if (isReco) { + registryMC.fill(HIST("thermalToy_pT_rec"), centrality, pt); + } else { + bkgParticle.set_user_index(-999); + registryMC.fill(HIST("thermalToy_pT_gen"), centrality, pt); + } + + fjInput.push_back(bkgParticle); + toyTracks.emplace_back(bkgParticle); + } + } + + void ProcessJetMatchingFactorized(const std::vector& jetsGen, + const std::vector& jetsReco, + float centrality) + { + const double maxDeltaR = 0.24; + std::vector isRecoJetMatched(jetsReco.size(), false); + + for (const auto& jetGen : jetsGen) { + registryMC.fill(HIST("hJetPtGenTotal"), centrality, jetGen.pt()); + } + + for (const auto& jetReco : jetsReco) { + registryMC.fill(HIST("hJetPtRecoTotal"), centrality, jetReco.pt()); + } + + // --- Geometrical matching reco <----> gen + for (const auto& jetGen : jetsGen) { + int bestRecoIdx = -1; + double minDeltaR = maxDeltaR; + + // Search closest jet RECO in (eta,phi) space + for (uint64_t iReco = 0; iReco < jetsReco.size(); ++iReco) { + if (isRecoJetMatched[iReco]) + continue; + + double dR = jetGen.delta_R(jetsReco[iReco]); + if (dR < minDeltaR) { + minDeltaR = dR; + bestRecoIdx = iReco; + } + } + + if (bestRecoIdx != -1) { + isRecoJetMatched[bestRecoIdx] = true; // RECO matched + + registryMC.fill(HIST("hDetectorResponse"), centrality, jetGen.pt(), jetsReco[bestRecoIdx].pt()); + registryMC.fill(HIST("hJetPtGenMatched"), centrality, jetGen.pt()); + registryMC.fill(HIST("hJetPtRecoMatched"), centrality, jetsReco[bestRecoIdx].pt()); + } + } + } + + // Process data + void processData(SelCollisions::iterator const& collision, aod::V0Datas const& fullV0s, + aod::CascDataExt const& Cascades, DaughterTracks const& tracks, + aod::BCsWithTimestamps const&) + { + // Vertex position vector + TVector3 vtxPos(collision.posX(), collision.posY(), collision.posZ()); + + // Fill event counter before event selection + registryData.fill(HIST("number_of_events_data"), 0.5); + + // Get the bunch crossing (BC) information associated with the collision + auto bc = collision.template bc_as(); + + // Initialize CCDB objects using the BC info + initCCDB(bc); + + // If skimmed processing is enabled, skip this event unless it passes Zorro selection + if (cfgSkimmedProcessing && !zorro.isSelected(collision.template bc_as().globalBC())) { + return; + } + + // Fill event counter after zorro selection + registryData.fill(HIST("number_of_events_data"), 1.5); + + // Event selection + if (!collision.sel8()) + return; + + // Fill event counter after sel8 selection + registryData.fill(HIST("number_of_events_data"), 2.5); + + // Require vertex position within the allowed z range + if (std::fabs(collision.posZ()) > zVtx) + return; + + // Fill event counter after z vertex selection + registryData.fill(HIST("number_of_events_data"), 3.5); + + // Reject collisions associated to the same found BC + if (requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + return; + + // Fill event counter after selection kNoSameBunchPileup + registryData.fill(HIST("number_of_events_data"), 4.5); + + // Compatible z_vtx from FT0 and from PV + if (requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + return; + + // Fill event counter after selection kIsGoodZvtxFT0vsPV + registryData.fill(HIST("number_of_events_data"), 5.5); + + // Event multiplicity + // float centrality; + // if (centrEstimator == 0) { + // centrality = collision.centFT0C(); + // } else { + // centrality = collision.centFT0M(); + // } + // registryData.fill(HIST("number_of_events_vsmultiplicity_MB"), centrality); + + // Loop over reconstructed tracks + std::vector fjParticles; + std::vector> fjTracks; + for (auto const& track : tracks) { + + // Require that tracks pass selection criteria + if (!passedTrackSelectionForJetReconstruction(track)) + continue; + + // 4-momentum representation of a particle + fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); + fjParticles.emplace_back(fourMomentum); + fjTracks.push_back(track); + } + + // Include V0s as tracks for jet reconstruction + if (useV0inJetRec && particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + AddV0sForJetReconstructionData(fjParticles, fjTracks, fullV0s, vtxPos); + } + fjTracks.clear(); + + // Reject empty events + if (fjParticles.size() < 1) + return; + registryData.fill(HIST("number_of_events_data"), 6.5); + + // Cluster particles using the anti-kt algorithm fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); + // Event multiplicity + float multiplicity; + if (centrEstimator == 0) { + multiplicity = collision.centFT0C(); + } else { + multiplicity = collision.centFT0M(); + } + + if (doPlotRho) { + auto [rhoMedian, rhoMMedian] = backgroundSub.estimateRhoAreaMedian(fjParticles, true); + registryData.fill(HIST("rho_perp"), multiplicity, rhoPerp); + registryData.fill(HIST("rho_median"), multiplicity, rhoMedian); + } + + // Delta pT distributions with random cone technique + computeRandomConeDeltaPt(fjParticles, jets, multiplicity, rhoPerp); + // Jet selection bool isAtLeastOneJetSelected = false; std::vector selectedJet; std::vector ue1; std::vector ue2; + std::vector rcSys1; // randomn cone 1 + std::vector rcSys2; // randomn cone 2 std::vector jetPt; // Loop over reconstructed jets @@ -1572,6 +2506,13 @@ struct StrangenessInJetsIons { continue; } + TVector3 rcSysAxis1(0, 0, 0), rcSysAxis2(0, 0, 0); + if (doRandomConeSys) { + getRandomConeDirections(jet, rcSysAxis1, rcSysAxis2, TMath::Pi() / 3.0, 2.0 * TMath::Pi() / 3.0); + rcSys1.emplace_back(rcSysAxis1); + rcSys2.emplace_back(rcSysAxis2); + } + // Store jet and UE axes selectedJet.emplace_back(jetAxis); ue1.emplace_back(ueAxis1); @@ -1584,14 +2525,6 @@ struct StrangenessInJetsIons { // Fill event counter with events with at least one jet registryData.fill(HIST("number_of_events_data"), 7.5); - // Event multiplicity - float multiplicity; - if (centrEstimator == 0) { - multiplicity = collision.centFT0C(); - } else { - multiplicity = collision.centFT0M(); - } - // Fill event multiplicity registryData.fill(HIST("number_of_events_vsmultiplicity"), multiplicity); registryData.fill(HIST("n_jets_vs_mult"), multiplicity, static_cast(selectedJet.size())); @@ -1621,8 +2554,12 @@ struct StrangenessInJetsIons { const float deltaPhiUe2 = getDeltaPhi(v0dir.Phi(), ue2[i].Phi()); const float deltaRue2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - // Vertex position vector - TVector3 vtxPos(collision.posX(), collision.posY(), collision.posZ()); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(v0dir.Eta(), v0dir.Phi(), rcSys1[i].Eta(), rcSys1[i].Phi()); + const float deltaRrc2 = getDeltaR(v0dir.Eta(), v0dir.Phi(), rcSys2[i].Eta(), rcSys2[i].Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } // Fill Armenteros-Podolanski TH2 // registryQC.fill(HIST("ArmenterosPreSel_DATA"), v0.alpha(), v0.qtarm()); @@ -1635,6 +2572,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("K0s_in_ue"), multiplicity, v0.pt(), v0.mK0Short()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("K0s_in_rc"), multiplicity, v0.pt(), v0.mK0Short()); + } } // Lambda if (passedLambdaSelection(v0, pos, neg, vtxPos)) { @@ -1644,6 +2584,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("Lambda_in_ue"), multiplicity, v0.pt(), v0.mLambda()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("Lambda_in_rc"), multiplicity, v0.pt(), v0.mLambda()); + } } // AntiLambda if (passedAntiLambdaSelection(v0, pos, neg, vtxPos)) { @@ -1653,6 +2596,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("AntiLambda_in_ue"), multiplicity, v0.pt(), v0.mAntiLambda()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("AntiLambda_in_rc"), multiplicity, v0.pt(), v0.mAntiLambda()); + } } } } @@ -1675,6 +2621,13 @@ struct StrangenessInJetsIons { const double deltaPhiUe2 = getDeltaPhi(cascadeDir.Phi(), ue2[i].Phi()); const double deltaRue2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys1[i].Eta(), rcSys1[i].Phi()); + const float deltaRrc2 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys2[i].Eta(), rcSys2[i].Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } + // Xi+ if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0) { if (deltaRjet < rJet) { @@ -1683,6 +2636,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("XiPos_in_ue"), multiplicity, casc.pt(), casc.mXi()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("XiPos_in_rc"), multiplicity, casc.pt(), casc.mXi()); + } } // Xi- if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0) { @@ -1692,6 +2648,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("XiNeg_in_ue"), multiplicity, casc.pt(), casc.mXi()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("XiNeg_in_rc"), multiplicity, casc.pt(), casc.mXi()); + } } // Omega+ if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0) { @@ -1701,6 +2660,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("OmegaPos_in_ue"), multiplicity, casc.pt(), casc.mOmega()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("OmegaPos_in_rc"), multiplicity, casc.pt(), casc.mOmega()); + } } // Omega- if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0) { @@ -1710,6 +2672,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryData.fill(HIST("OmegaNeg_in_ue"), multiplicity, casc.pt(), casc.mOmega()); } + if (doRandomConeSys && isInRC) { + registryData.fill(HIST("OmegaNeg_in_rc"), multiplicity, casc.pt(), casc.mOmega()); + } } } } @@ -1783,6 +2748,7 @@ struct StrangenessInJetsIons { { // Define per-event particle containers std::vector fjParticles; + std::vector toyTracks; std::vector strHadronMomentum; std::vector pdg; @@ -1824,6 +2790,7 @@ struct StrangenessInJetsIons { // Clear containers at the start of the event loop fjParticles.clear(); + toyTracks.clear(); strHadronMomentum.clear(); pdg.clear(); @@ -1848,11 +2815,14 @@ struct StrangenessInJetsIons { genMultiplicity = collision.centFT0M(); } registryMC.fill(HIST("number_of_events_vsmultiplicity_gen_MB"), genMultiplicity); + if (hasReco) + registryMC.fill(HIST("number_of_events_vsmultiplicity_gen_w_reco_MB"), genMultiplicity); // MC particles per collision auto mcParticlesPerColl = mcParticles.sliceBy(perMCCollision, collision.globalIndex()); // Loop over all MC particles and select physical primaries within acceptance + std::vector> fjParticleObj; for (const auto& particle : mcParticlesPerColl) { // Store properties of strange hadrons int pdgAbs = std::abs(particle.pdgCode()); @@ -1865,7 +2835,7 @@ struct StrangenessInJetsIons { if (particle.eta() < configTracks.etaMin || particle.eta() > configTracks.etaMax || particle.pt() < minPtParticle) continue; - FillFullEventHistoMCGEN(particle, genMultiplicity, hasReco); + FillMBEventHistoMCGEN(particle, genMultiplicity, hasReco); // Select physical primary particles or HF decay products if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) @@ -1877,8 +2847,16 @@ struct StrangenessInJetsIons { fastjet::PseudoJet fourMomentum(particle.px(), particle.py(), particle.pz(), energy); fourMomentum.set_user_index(particle.pdgCode()); fjParticles.emplace_back(fourMomentum); + fjParticleObj.push_back(particle); + } + + if (useV0inJetRec && particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + AddV0sForJetReconstructionMCP(fjParticles, fjParticleObj, mcParticlesPerColl, mcParticles); } + if (doThermalToyModel) + InjectThermalBackground(fjParticles, toyTracks, genMultiplicity); + // Skip events with no particles if (fjParticles.size() < 1) continue; @@ -1891,6 +2869,9 @@ struct StrangenessInJetsIons { // Estimate background energy density (rho) in perpendicular cone auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); + // Delta pT distributions with random cone technique + computeRandomConeDeltaPt(fjParticles, jets, genMultiplicity, rhoPerp, 1); + // Loop over clustered jets int countSelJet = 0; // number of selected jets for (const auto& jet : jets) { @@ -1909,19 +2890,26 @@ struct StrangenessInJetsIons { countSelJet++; registryMC.fill(HIST("number_of_events_mc_gen"), 3.5); registryMC.fill(HIST("number_of_events_vsmultiplicity_gen"), genMultiplicity); + if (hasReco) + registryMC.fill(HIST("number_of_events_vsmultiplicity_gen_w_reco"), genMultiplicity); // Fill jet counter registryMC.fill(HIST("n_jets_vs_mult_pT_mc_gen"), genMultiplicity, jetMinusBkg.pt()); // Set up two perpendicular cone axes for underlying event estimation TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); - double coneRadius = std::sqrt(jet.area() / PI); + double coneRadius = std::sqrt(jet.area() / PI); // TODO: replace with rJet (similar results) TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { continue; } + TVector3 rcSysAxis1(0, 0, 0), rcSysAxis2(0, 0, 0); + if (doRandomConeSys) { + getRandomConeDirections(jet, rcSysAxis1, rcSysAxis2, TMath::Pi() / 3.0, 2.0 * TMath::Pi() / 3.0); + } + // Loop over strange hadrons int index = -1; for (const auto& hadron : strHadronMomentum) { @@ -1939,6 +2927,13 @@ struct StrangenessInJetsIons { double deltaPhiUe2 = getDeltaPhi(hadron.Phi(), ueAxis2.Phi()); double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(hadron.Eta(), hadron.Phi(), rcSysAxis1.Eta(), rcSysAxis1.Phi()); + const float deltaRrc2 = getDeltaR(hadron.Eta(), hadron.Phi(), rcSysAxis2.Eta(), rcSysAxis2.Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } + // Select particles inside jet if (deltaRJet < coneRadius) { switch (pdg[index]) { @@ -2084,6 +3079,49 @@ struct StrangenessInJetsIons { break; } } + + // Select particles inside random cones (RC) + if (doRandomConeSys && isInRC) { + switch (pdg[index]) { + case kK0Short: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("K0s_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kLambda0: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("Lambda_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kLambda0Bar: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("AntiLambda_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_generated_rc"), genMultiplicity, hadron.Pt()); + } + break; + default: + break; + } + } } } // Fill jet counter @@ -2095,14 +3133,17 @@ struct StrangenessInJetsIons { // Reconstructed MC events void processMCreconstructed(SimCollisions const& collisions, soa::Join const&, - DaughterTracksMC const& mcTracks, aod::V0Datas const& fullV0s, + DaughterTracksMC const& mcTracks, soa::Join const& fullV0s, aod::CascDataExt const& Cascades, aod::McParticles const& mcParticles) { // Define per-event containers std::vector fjParticles; + std::vector toyTracks; std::vector selectedJet; std::vector ue1; std::vector ue2; + std::vector rcSys1; // randomn cone 1 + std::vector rcSys2; // randomn cone 2 // Jet and area definitions fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); @@ -2116,11 +3157,17 @@ struct StrangenessInJetsIons { const auto& mcCollision = collision.mcCollision_as>(); + // Vertex position vector + TVector3 vtxPos(collision.posX(), collision.posY(), collision.posZ()); + // Clear containers at the start of the event loop fjParticles.clear(); + toyTracks.clear(); selectedJet.clear(); ue1.clear(); ue2.clear(); + rcSys1.clear(); + rcSys2.clear(); // Fill event counter before any selection registryMC.fill(HIST("number_of_events_mc_rec"), 0.5); @@ -2164,10 +3211,11 @@ struct StrangenessInJetsIons { auto tracksPerColl = mcTracks.sliceBy(perCollisionTrk, collision.globalIndex()); const auto& mcParticlesPerColl = mcParticles.sliceBy(perMCCollision, mcCollision.globalIndex()); - FillFullEventHistoMCREC(collision, mcParticles, v0sPerColl, - cascPerColl, tracksPerColl, multiplicity); + FillMBEventHistoMCREC(collision, mcParticles, v0sPerColl, + cascPerColl, tracksPerColl, multiplicity); // Loop over reconstructed tracks + std::vector> fjTracks; for (auto const& track : tracksPerColl) { if (!passedTrackSelectionForJetReconstruction(track)) continue; @@ -2175,7 +3223,17 @@ struct StrangenessInJetsIons { // 4-momentum representation of a particle fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); fjParticles.emplace_back(fourMomentum); + fjTracks.push_back(track); + } + + // Include V0s as tracks for jet reconstruction + if (useV0inJetRec && particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + AddV0sForJetReconstructionMCD(fjParticles, fjTracks, v0sPerColl, mcParticles, vtxPos); } + fjTracks.clear(); + + if (doThermalToyModel) + InjectThermalBackground(fjParticles, toyTracks, multiplicity, true); // Reject empty events if (fjParticles.size() < 1) @@ -2187,6 +3245,9 @@ struct StrangenessInJetsIons { std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); + // Delta pT distributions with random cone technique + computeRandomConeDeltaPt(fjParticles, jets, multiplicity, rhoPerp, 2); + // Jet selection bool isAtLeastOneJetSelected = false; std::vector jetPt; @@ -2213,6 +3274,13 @@ struct StrangenessInJetsIons { continue; } + TVector3 rcSysAxis1(0, 0, 0), rcSysAxis2(0, 0, 0); + if (doRandomConeSys) { + getRandomConeDirections(jet, rcSysAxis1, rcSysAxis2, TMath::Pi() / 3.0, 2.0 * TMath::Pi() / 3.0); + rcSys1.emplace_back(rcSysAxis1); + rcSys2.emplace_back(rcSysAxis2); + } + // Store selected jet and UE cone axes selectedJet.emplace_back(jetAxis); ue1.emplace_back(ueAxis1); @@ -2256,6 +3324,13 @@ struct StrangenessInJetsIons { const double deltaPhiUe2 = getDeltaPhi(momVec.Phi(), ue2[i].Phi()); const double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(momVec.Eta(), momVec.Phi(), rcSys1[i].Eta(), rcSys1[i].Phi()); + const float deltaRrc2 = getDeltaR(momVec.Eta(), momVec.Phi(), rcSys2[i].Eta(), rcSys2[i].Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } + // Select particles inside jet if (deltaRJet < rJet) { switch (particle.pdgCode()) { @@ -2274,6 +3349,26 @@ struct StrangenessInJetsIons { registryMC.fill(HIST("AntiLambda_gen_recoEvent_jet"), multiplicity, momVec.Pt()); } break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_jet"), multiplicity, momVec.Pt()); + } + break; default: break; } @@ -2295,42 +3390,114 @@ struct StrangenessInJetsIons { registryMC.fill(HIST("AntiLambda_gen_recoEvent_ue"), multiplicity, momVec.Pt()); } break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_ue"), multiplicity, momVec.Pt()); + } + break; default: break; } } - } - // ---------------------------------------- - - // V0 particles - if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { - for (const auto& v0 : v0sPerColl) { - const auto& pos = v0.posTrack_as(); - const auto& neg = v0.negTrack_as(); - TVector3 v0dir(v0.px(), v0.py(), v0.pz()); - - // Get MC particles - if (!pos.has_mcParticle() || !neg.has_mcParticle()) - continue; - auto posParticle = pos.mcParticle_as(); - auto negParticle = neg.mcParticle_as(); - if (!posParticle.has_mothers() || !negParticle.has_mothers()) - continue; - - // Select particles originating from the same parent - int pdgParent(0); - bool isPhysPrim = false; - for (const auto& particleMotherOfNeg : negParticle.mothers_as()) { - for (const auto& particleMotherOfPos : posParticle.mothers_as()) { - if (particleMotherOfNeg == particleMotherOfPos) { - pdgParent = particleMotherOfNeg.pdgCode(); - isPhysPrim = particleMotherOfNeg.isPhysicalPrimary(); + if (doRandomConeSys && isInRC) { + switch (particle.pdgCode()) { + case kK0Short: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("K0s_gen_recoEvent_rc"), multiplicity, momVec.Pt()); } - } - } + break; + case kLambda0: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("Lambda_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kLambda0Bar: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("AntiLambda_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_gen_recoEvent_rc"), multiplicity, momVec.Pt()); + } + break; + default: + break; + } + } + } + // ---------------------------------------- + + // V0 particles + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + for (const auto& v0 : v0sPerColl) { + const auto& pos = v0.posTrack_as(); + const auto& neg = v0.negTrack_as(); + TVector3 v0dir(v0.px(), v0.py(), v0.pz()); + + // Get MC particles + if (!pos.has_mcParticle() || !neg.has_mcParticle()) + continue; + auto posParticle = pos.mcParticle_as(); + auto negParticle = neg.mcParticle_as(); + if (!posParticle.has_mothers() || !negParticle.has_mothers()) + continue; + + // Select particles originating from the same parent + int pdgParent(0); + bool isPhysPrim = false; + for (const auto& particleMotherOfNeg : negParticle.mothers_as()) { + for (const auto& particleMotherOfPos : posParticle.mothers_as()) { + if (particleMotherOfNeg == particleMotherOfPos) { + pdgParent = particleMotherOfNeg.pdgCode(); + isPhysPrim = particleMotherOfNeg.isPhysicalPrimary(); + } + } + } if (pdgParent == 0) continue; + // --- alternative method to avoid double for loop + // int motherId = GetCommonMotherId(mcParticles, posParticle, negParticle); + // if (motherId < 0) + // continue; // if motherId is -1, it means no common mother was found + // const auto& motherTest = mcParticles.iteratorAt(motherId); + // // const bool isPhysPrimTest = motherTest.isPhysicalPrimary(); + // int pdgParentTest = motherTest.pdgCode(); + // if (pdgParent != pdgParentTest) + // LOG(warning) << "pdgParent != pdgParentTest" << endl; + // --- + // Compute distance from jet and UE axes double deltaEtaJet = v0dir.Eta() - selectedJet[i].Eta(); double deltaPhiJet = getDeltaPhi(v0dir.Phi(), selectedJet[i].Phi()); @@ -2342,8 +3509,12 @@ struct StrangenessInJetsIons { double deltaPhiUe2 = getDeltaPhi(v0dir.Phi(), ue2[i].Phi()); double deltaRue2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); - // Vertex position vector - TVector3 vtxPos(collision.posX(), collision.posY(), collision.posZ()); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(v0dir.Eta(), v0dir.Phi(), rcSys1[i].Eta(), rcSys1[i].Phi()); + const float deltaRrc2 = getDeltaR(v0dir.Eta(), v0dir.Phi(), rcSys2[i].Eta(), rcSys2[i].Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } // Fill Armenteros-Podolanski TH2 // registryQC.fill(HIST("ArmenterosPreSel_REC"), v0.alpha(), v0.qtarm()); @@ -2356,6 +3527,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("K0s_reconstructed_ue"), multiplicity, v0.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("K0s_reconstructed_rc"), multiplicity, v0.pt()); + } } // Lambda if (passedLambdaSelection(v0, pos, neg, vtxPos) && pdgParent == kLambda0 && isPhysPrim) { @@ -2365,6 +3539,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("Lambda_reconstructed_ue"), multiplicity, v0.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("Lambda_reconstructed_rc"), multiplicity, v0.pt()); + } } // AntiLambda if (passedAntiLambdaSelection(v0, pos, neg, vtxPos) && pdgParent == kLambda0Bar && isPhysPrim) { @@ -2374,6 +3551,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("AntiLambda_reconstructed_ue"), multiplicity, v0.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("AntiLambda_reconstructed_rc"), multiplicity, v0.pt()); + } } // Fill inclusive spectra @@ -2385,6 +3565,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("K0s_reconstructed_ue_incl"), multiplicity, v0.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("K0s_reconstructed_rc_incl"), multiplicity, v0.pt()); + } } // Lambda if (passedLambdaSelection(v0, pos, neg, vtxPos) && pdgParent == kLambda0) { @@ -2394,6 +3577,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("Lambda_reconstructed_ue_incl"), multiplicity, v0.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("Lambda_reconstructed_rc_incl"), multiplicity, v0.pt()); + } } // AntiLambda if (passedAntiLambdaSelection(v0, pos, neg, vtxPos) && pdgParent == kLambda0Bar) { @@ -2403,6 +3589,9 @@ struct StrangenessInJetsIons { if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("AntiLambda_reconstructed_ue_incl"), multiplicity, v0.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("AntiLambda_reconstructed_rc_incl"), multiplicity, v0.pt()); + } } } } @@ -2440,8 +3629,8 @@ struct StrangenessInJetsIons { } if (pdgParent == 0) continue; - if (!isPhysPrim) - continue; + // if (!isPhysPrim) + // continue; // Compute distances from jet and UE axes TVector3 cascadeDir(casc.px(), casc.py(), casc.pz()); @@ -2455,49 +3644,758 @@ struct StrangenessInJetsIons { double deltaPhiUe2 = getDeltaPhi(cascadeDir.Phi(), ue2[i].Phi()); double deltaRue2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + bool isInRC = false; + if (doRandomConeSys) { + const float deltaRrc1 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys1[i].Eta(), rcSys1[i].Phi()); + const float deltaRrc2 = getDeltaR(cascadeDir.Eta(), cascadeDir.Phi(), rcSys2[i].Eta(), rcSys2[i].Phi()); + isInRC = (deltaRrc1 < rJet) || (deltaRrc2 < rJet); + } + // Xi+ - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kXiPlusBar) { + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kXiPlusBar && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("XiPos_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("XiPos_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiPos_reconstructed_rc"), multiplicity, casc.pt()); + } } // Xi- - if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kXiMinus) { + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kXiMinus && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("XiNeg_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("XiNeg_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiNeg_reconstructed_rc"), multiplicity, casc.pt()); + } } // Omega+ - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kOmegaPlusBar) { + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kOmegaPlusBar && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("OmegaPos_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("OmegaPos_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaPos_reconstructed_rc"), multiplicity, casc.pt()); + } } // Omega- - if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kOmegaMinus) { + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kOmegaMinus && isPhysPrim) { if (deltaRjet < rJet) { registryMC.fill(HIST("OmegaNeg_reconstructed_jet"), multiplicity, casc.pt()); } if (deltaRue1 < rJet || deltaRue2 < rJet) { registryMC.fill(HIST("OmegaNeg_reconstructed_ue"), multiplicity, casc.pt()); } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaNeg_reconstructed_rc"), multiplicity, casc.pt()); + } + } + + // Fill inclusive histograms + // Xi+ + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kXiPlusBar) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("XiPos_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("XiPos_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiPos_reconstructed_rc_incl"), multiplicity, casc.pt()); + } + } + // Xi- + if (passedXiSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kXiMinus) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("XiNeg_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("XiNeg_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("XiNeg_reconstructed_rc_incl"), multiplicity, casc.pt()); + } + } + // Omega+ + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() > 0 && pdgParent == kOmegaPlusBar) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("OmegaPos_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("OmegaPos_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaPos_reconstructed_rc_incl"), multiplicity, casc.pt()); + } + } + // Omega- + if (passedOmegaSelection(casc, pos, neg, bach, collision) && bach.sign() < 0 && pdgParent == kOmegaMinus) { + if (deltaRjet < rJet) { + registryMC.fill(HIST("OmegaNeg_reconstructed_jet_incl"), multiplicity, casc.pt()); + } + if (deltaRue1 < rJet || deltaRue2 < rJet) { + registryMC.fill(HIST("OmegaNeg_reconstructed_ue_incl"), multiplicity, casc.pt()); + } + if (doRandomConeSys && isInRC) { + registryMC.fill(HIST("OmegaNeg_reconstructed_rc_incl"), multiplicity, casc.pt()); + } + } + } + } + + // ------------------------------------------------------------------ + // Background toy particles + if (doThermalToyModel) { + // Store charged particles reconstructed in embeded jets + for (auto const& track : tracksPerColl) { + if (!passedTrackSelectionForJetReconstruction(track)) + continue; + + const double deltaEtaJet = track.eta() - selectedJet[i].Eta(); + const double deltaPhiJet = getDeltaPhi(track.phi(), selectedJet[i].Phi()); + const double deltaRJet = std::sqrt(deltaEtaJet * deltaEtaJet + deltaPhiJet * deltaPhiJet); + + const double deltaEtaUe1 = track.eta() - ue1[i].Eta(); + const double deltaPhiUe1 = getDeltaPhi(track.phi(), ue1[i].Phi()); + const double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); + + const double deltaEtaUe2 = track.eta() - ue2[i].Eta(); + const double deltaPhiUe2 = getDeltaPhi(track.phi(), ue2[i].Phi()); + const double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + + // Charged track in jet + if (deltaRJet < rJet) { + registryMC.fill(HIST("charged_embedJets_rec_jet"), multiplicity, track.pt()); + } + + // Charged track in UE + if (deltaRUe1 < rJet || deltaRUe2 < rJet) { + registryMC.fill(HIST("charged_embedJets_rec_ue"), multiplicity, track.pt()); + } + } + + // Store toy charged particles (background particles) + for (const auto& toyPart : toyTracks) { + TVector3 toyMom(toyPart.px(), toyPart.py(), toyPart.pz()); + + const double deltaEtaJet = toyMom.Eta() - selectedJet[i].Eta(); + const double deltaPhiJet = getDeltaPhi(toyMom.Phi(), selectedJet[i].Phi()); + const double deltaRJet = std::sqrt(deltaEtaJet * deltaEtaJet + deltaPhiJet * deltaPhiJet); + + const double deltaEtaUe1 = toyMom.Eta() - ue1[i].Eta(); + const double deltaPhiUe1 = getDeltaPhi(toyMom.Phi(), ue1[i].Phi()); + const double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); + + const double deltaEtaUe2 = toyMom.Eta() - ue2[i].Eta(); + const double deltaPhiUe2 = getDeltaPhi(toyMom.Phi(), ue2[i].Phi()); + const double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + + // Background particles in jet + if (deltaRJet < rJet) { + registryMC.fill(HIST("chargedBkg_toy_rec_jet"), multiplicity, toyMom.Pt()); + } + // Background particles in UE + if (deltaRUe1 < rJet || deltaRUe2 < rJet) { + registryMC.fill(HIST("chargedBkg_toy_rec_ue"), multiplicity, toyMom.Pt()); } } } + // ------------------------------------------------------------------ } jetPt.clear(); } } PROCESS_SWITCH(StrangenessInJetsIons, processMCreconstructed, "process reconstructed events", false); + + void processMCmodels(soa::Join const& collisions, + aod::McParticles const& mcParticles) + { + // Define per-event particle containers + std::vector fjParticles; + std::vector strHadronMomentum; + std::vector pdg; + + // Jet and area definitions + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); + + // Loop over MC collisions + for (const auto& collision : collisions) { + // Clear containers at the start of the event loop + fjParticles.clear(); + strHadronMomentum.clear(); + pdg.clear(); + + // Fill event counter before any selection + registryMC.fill(HIST("number_of_events_mc_gen"), 0.5); + + // Require vertex position within the allowed z range + // if (std::fabs(collision.posZ()) > zVtx) + // continue; + + // Fill event counter after selection on z-vertex + registryMC.fill(HIST("number_of_events_mc_gen"), 1.5); + + // Multiplicity of generated event + float genMultiplicity; + if (centrEstimator == 0) { + genMultiplicity = collision.centFT0C(); + } else { + genMultiplicity = collision.centFT0M(); + } + registryMC.fill(HIST("number_of_events_vsmultiplicity_gen_MB"), genMultiplicity); + + // MC particles per collision + auto mcParticlesPerColl = mcParticles.sliceBy(perMCCollision, collision.globalIndex()); + + // Loop over all MC particles and select physical primaries within acceptance + std::vector> fjParticleObj; + for (const auto& particle : mcParticlesPerColl) { + // Store properties of strange hadrons + int pdgAbs = std::abs(particle.pdgCode()); + if (particle.isPhysicalPrimary() && (pdgAbs == kK0Short || pdgAbs == kLambda0 || pdgAbs == kXiMinus || pdgAbs == kOmegaMinus)) { // TODO: add protons, kaons, pions + pdg.emplace_back(particle.pdgCode()); + strHadronMomentum.emplace_back(particle.px(), particle.py(), particle.pz()); + } + + double minPtParticle = 0.1f; + if (particle.eta() < configTracks.etaMin || particle.eta() > configTracks.etaMax || particle.pt() < minPtParticle) + continue; + + // false --> do not fill histograms with reco events + FillMBEventHistoMCGEN(particle, genMultiplicity, false); + + // Select physical primary particles or HF decay products + if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) + continue; + + // Build 4-momentum assuming charged pion mass + static constexpr float kMassPionChargedSquared = o2::constants::physics::MassPionCharged * o2::constants::physics::MassPionCharged; + const double energy = std::sqrt(particle.p() * particle.p() + kMassPionChargedSquared); + fastjet::PseudoJet fourMomentum(particle.px(), particle.py(), particle.pz(), energy); + fourMomentum.set_user_index(particle.pdgCode()); + fjParticles.emplace_back(fourMomentum); + fjParticleObj.push_back(particle); + } + + if (useV0inJetRec && particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + AddV0sForJetReconstructionMCP(fjParticles, fjParticleObj, mcParticlesPerColl, mcParticles); + } + + // Skip events with no particles + if (fjParticles.size() < 1) + continue; + registryMC.fill(HIST("number_of_events_mc_gen"), 2.5); + + // Cluster MC particles into jets using anti-kt algorithm + fastjet::ClusterSequenceArea cs(fjParticles, jetDef, areaDef); + std::vector jets = fastjet::sorted_by_pt(cs.inclusive_jets()); + + // Estimate background energy density (rho) in perpendicular cone + auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticles, jets[0], rJet); + + // Loop over clustered jets + int countSelJet = 0; // number of selected jets + for (const auto& jet : jets) { + + // Jet must be fully contained in acceptance + if ((std::fabs(jet.eta()) + rJet) > (configTracks.etaMax - deltaEtaEdge)) + continue; + + // Subtract background energy from jet + auto jetForSub = jet; + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + + // Apply jet pT threshold + if (jetMinusBkg.pt() < minJetPt) + continue; + countSelJet++; + registryMC.fill(HIST("number_of_events_mc_gen"), 3.5); + registryMC.fill(HIST("number_of_events_vsmultiplicity_gen"), genMultiplicity); + + // Fill jet counter + registryMC.fill(HIST("n_jets_vs_mult_pT_mc_gen"), genMultiplicity, jetMinusBkg.pt()); + + // Set up two perpendicular cone axes for underlying event estimation + TVector3 jetAxis(jet.px(), jet.py(), jet.pz()); + double coneRadius = std::sqrt(jet.area() / PI); + TVector3 ueAxis1(0, 0, 0), ueAxis2(0, 0, 0); + getPerpendicularDirections(jetAxis, ueAxis1, ueAxis2); + if (ueAxis1.Mag() == 0 || ueAxis2.Mag() == 0) { + continue; + } + + // Loop over strange hadrons + int index = -1; + for (const auto& hadron : strHadronMomentum) { + // Particle index + index++; + + // Compute distance of particles from jet and UE axes + double deltaEtaJet = hadron.Eta() - jetAxis.Eta(); + double deltaPhiJet = getDeltaPhi(hadron.Phi(), jetAxis.Phi()); + double deltaRJet = std::sqrt(deltaEtaJet * deltaEtaJet + deltaPhiJet * deltaPhiJet); + double deltaEtaUe1 = hadron.Eta() - ueAxis1.Eta(); + double deltaPhiUe1 = getDeltaPhi(hadron.Phi(), ueAxis1.Phi()); + double deltaRUe1 = std::sqrt(deltaEtaUe1 * deltaEtaUe1 + deltaPhiUe1 * deltaPhiUe1); + double deltaEtaUe2 = hadron.Eta() - ueAxis2.Eta(); + double deltaPhiUe2 = getDeltaPhi(hadron.Phi(), ueAxis2.Phi()); + double deltaRUe2 = std::sqrt(deltaEtaUe2 * deltaEtaUe2 + deltaPhiUe2 * deltaPhiUe2); + + // Select particles inside jet + if (deltaRJet < coneRadius) { + switch (pdg[index]) { + case kK0Short: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("K0s_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kLambda0: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("Lambda_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kLambda0Bar: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("AntiLambda_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kPiPlus: + if (particleOfInterestDict[ParticleOfInterest::kPions]) { + registryMC.fill(HIST("PionPos_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kKPlus: + if (particleOfInterestDict[ParticleOfInterest::kKaons]) { + registryMC.fill(HIST("KaonPos_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kProton: + if (particleOfInterestDict[ParticleOfInterest::kProtons]) { + registryMC.fill(HIST("ProtonPos_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kPiMinus: + if (particleOfInterestDict[ParticleOfInterest::kPions]) { + registryMC.fill(HIST("PionNeg_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kKMinus: + if (particleOfInterestDict[ParticleOfInterest::kKaons]) { + registryMC.fill(HIST("KaonNeg_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + case kProtonBar: + if (particleOfInterestDict[ParticleOfInterest::kProtons]) { + registryMC.fill(HIST("ProtonNeg_generated_jet"), genMultiplicity, hadron.Pt()); + } + break; + default: + break; + } + } + + // Select particles inside UE cones + if (deltaRUe1 < coneRadius || deltaRUe2 < coneRadius) { + switch (pdg[index]) { + case kK0Short: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("K0s_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kLambda0: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("Lambda_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kLambda0Bar: + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + registryMC.fill(HIST("AntiLambda_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kXiMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiNeg_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kXiPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("XiPos_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaMinus: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaNeg_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kOmegaPlusBar: + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { + registryMC.fill(HIST("OmegaPos_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kPiPlus: + if (particleOfInterestDict[ParticleOfInterest::kPions]) { + registryMC.fill(HIST("PionPos_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kKPlus: + if (particleOfInterestDict[ParticleOfInterest::kKaons]) { + registryMC.fill(HIST("KaonPos_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kProton: + if (particleOfInterestDict[ParticleOfInterest::kProtons]) { + registryMC.fill(HIST("ProtonPos_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kPiMinus: + if (particleOfInterestDict[ParticleOfInterest::kPions]) { + registryMC.fill(HIST("PionNeg_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kKMinus: + if (particleOfInterestDict[ParticleOfInterest::kKaons]) { + registryMC.fill(HIST("KaonNeg_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + case kProtonBar: + if (particleOfInterestDict[ParticleOfInterest::kProtons]) { + registryMC.fill(HIST("ProtonNeg_generated_ue"), genMultiplicity, hadron.Pt()); + } + break; + default: + break; + } + } + } + } + // Fill jet counter + registryMC.fill(HIST("n_jets_vs_mult_mc_gen"), genMultiplicity, countSelJet); + } + } + PROCESS_SWITCH(StrangenessInJetsIons, processMCmodels, "Process MC generated events (with models)", false); + + // --- Process Minimum Bias events --- + // Process data MB + void processDataMB(SelCollisions::iterator const& collision, aod::V0Datas const& fullV0s, + aod::CascDataExt const& Cascades, DaughterTracksMB const& tracks, + aod::BCsWithTimestamps const&) + { + // Vertex position vector + TVector3 vtxPos(collision.posX(), collision.posY(), collision.posZ()); + + // Fill event counter before event selection + registryDataMB.fill(HIST("number_of_events_data_MB"), 0.5); + + // Get the bunch crossing (BC) information associated with the collision + auto bc = collision.template bc_as(); + + // Initialize CCDB objects using the BC info + initCCDB(bc); + + // If skimmed processing is enabled, skip this event unless it passes Zorro selection + if (cfgSkimmedProcessing && !zorro.isSelected(collision.template bc_as().globalBC())) { + return; + } + + // Fill event counter after zorro selection + registryDataMB.fill(HIST("number_of_events_data_MB"), 1.5); + + // Event selection + if (!collision.sel8()) + return; + + // Fill event counter after sel8 selection + registryDataMB.fill(HIST("number_of_events_data_MB"), 2.5); + + // Require vertex position within the allowed z range + if (std::fabs(collision.posZ()) > zVtx) + return; + + // Fill event counter after z vertex selection + registryDataMB.fill(HIST("number_of_events_data_MB"), 3.5); + + // Reject collisions associated to the same found BC + if (requireNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + return; + + // Fill event counter after selection kNoSameBunchPileup + registryDataMB.fill(HIST("number_of_events_data_MB"), 4.5); + + // Compatible z_vtx from FT0 and from PV + if (requireGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + return; + + // Fill event counter after selection kIsGoodZvtxFT0vsPV + registryDataMB.fill(HIST("number_of_events_data_MB"), 5.5); + + // Event multiplicity + float centrality; + if (centrEstimator == 0) { + centrality = collision.centFT0C(); + } else { + centrality = collision.centFT0M(); + } + registryDataMB.fill(HIST("number_of_events_vsmultiplicity_MB"), centrality); + + if (saveChargedParticleMB) { + for (const auto& trk : tracks) { + if (!passedTrackSelectionForJetReconstruction(trk) || !trk.isGlobalTrack()) { + continue; + } + registryDataMB.fill(HIST("ChargedTrack_in_MB"), centrality, trk.pt()); + } + } + + if (particleOfInterestDict[ParticleOfInterest::kV0Particles]) { // V0s + for (const auto& v0 : fullV0s) { + + // Get V0 daughters + const auto& pos = v0.posTrack_as(); + const auto& neg = v0.negTrack_as(); + TVector3 v0dir(v0.px(), v0.py(), v0.pz()); + + // K0s + if (passedK0ShortSelectionMB(v0, pos, neg, vtxPos)) { + registryDataMB.fill(HIST("K0s_in_MB"), centrality, v0.pt(), v0.mK0Short()); + } + // Lambda + if (passedLambdaSelectionMB(v0, pos, neg, vtxPos)) { + registryDataMB.fill(HIST("Lambda_in_MB"), centrality, v0.pt(), v0.mLambda()); + } + // AntiLambda + if (passedAntiLambdaSelectionMB(v0, pos, neg, vtxPos)) { + registryDataMB.fill(HIST("AntiLambda_in_MB"), centrality, v0.pt(), v0.mAntiLambda()); + } + } + } + + if (particleOfInterestDict[ParticleOfInterest::kCascades]) { // Cascades + for (const auto& casc : Cascades) { + // Get cascade daughters + const auto& bach = casc.bachelor_as(); + const auto& pos = casc.posTrack_as(); + const auto& neg = casc.negTrack_as(); + TVector3 cascadeDir(casc.px(), casc.py(), casc.pz()); + + // Xi+ + if (passedXiSelectionMB(casc, pos, neg, bach, collision) && bach.sign() > 0) { + registryDataMB.fill(HIST("XiPos_in_MB"), centrality, casc.pt(), casc.mXi()); + } + // Xi- + if (passedXiSelectionMB(casc, pos, neg, bach, collision) && bach.sign() < 0) { + registryDataMB.fill(HIST("XiNeg_in_MB"), centrality, casc.pt(), casc.mXi()); + } + // Omega+ + if (passedOmegaSelectionMB(casc, pos, neg, bach, collision) && bach.sign() > 0) { + registryDataMB.fill(HIST("OmegaPos_in_MB"), centrality, casc.pt(), casc.mOmega()); + } + // Omega- + if (passedOmegaSelectionMB(casc, pos, neg, bach, collision) && bach.sign() < 0) { + registryDataMB.fill(HIST("OmegaNeg_in_MB"), centrality, casc.pt(), casc.mOmega()); + } + } + } + if (particleOfInterestDict[ParticleOfInterest::kPions] || + particleOfInterestDict[ParticleOfInterest::kKaons] || + particleOfInterestDict[ParticleOfInterest::kProtons]) { + for (const auto& trk : tracks) { + + if (!passedSingleTrackSelection(trk)) { + continue; + } + + float nsigmaTPC = 0.f; + float nsigmaTOF = 0.f; + + if (particleOfInterestDict[ParticleOfInterest::kPions]) { + nsigmaTPC = trk.tpcNSigmaPi(); + nsigmaTOF = trk.tofNSigmaPi(); + registryDataMB.fill(HIST("Pion_in_MB"), centrality, trk.pt() * trk.sign(), nsigmaTPC, nsigmaTOF, trk.dcaXY()); + } + if (particleOfInterestDict[ParticleOfInterest::kKaons]) { + nsigmaTPC = trk.tpcNSigmaKa(); + nsigmaTOF = trk.tofNSigmaKa(); + registryDataMB.fill(HIST("Kaon_in_MB"), centrality, trk.pt() * trk.sign(), nsigmaTPC, nsigmaTOF, trk.dcaXY()); + } + if (particleOfInterestDict[ParticleOfInterest::kProtons]) { + nsigmaTPC = trk.tpcNSigmaPr(); + nsigmaTOF = trk.tofNSigmaPr(); + registryDataMB.fill(HIST("Proton_in_MB"), centrality, trk.pt() * trk.sign(), nsigmaTPC, nsigmaTOF, trk.dcaXY()); + } + } + } + } + PROCESS_SWITCH(StrangenessInJetsIons, processDataMB, "Process data in minimum bias events", false); + + void processPrepareUnfolding(SimCollisions const& recoCollisions, + soa::Join const&, + DaughterTracksMC const& mcTracks, soa::Join const& fullV0s, + aod::McParticles const& mcParticles) + { + fastjet::JetDefinition jetDef(fastjet::antikt_algorithm, rJet); + fastjet::AreaDefinition areaDef(fastjet::active_area, fastjet::GhostedAreaSpec(1.0)); + + // Loop over reco collisions + for (const auto& recoColl : recoCollisions) { + if (!recoColl.has_mcCollision()) { + continue; + } + + // Event selections + if (!recoColl.sel8()) + continue; + if (std::fabs(recoColl.posZ()) > zVtx) + continue; + if (requireNoSameBunchPileup && !recoColl.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) + continue; + if (requireGoodZvtxFT0vsPV && !recoColl.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) + continue; + + // Centrality + const auto& mcCollision = recoColl.mcCollision_as>(); + float centrality; + if (centrEstimator == 0) { + centrality = mcCollision.centFT0C(); + } else { + centrality = mcCollision.centFT0M(); + } + + std::vector jetsGenThisEvent; + std::vector jetsRecoThisEvent; + + // ---- Extract reconstructed jets ---- + // Number of V0 and cascades per collision + auto v0sPerColl = fullV0s.sliceBy(perCollisionV0, recoColl.globalIndex()); + auto tracksPerColl = mcTracks.sliceBy(perCollisionTrk, recoColl.globalIndex()); + const auto& mcParticlesPerColl = mcParticles.sliceBy(perMCCollision, mcCollision.globalIndex()); + + // Vertex position vector + TVector3 vtxPos(recoColl.posX(), recoColl.posY(), recoColl.posZ()); + std::vector fjParticlesReco; + std::vector> fjTracks; + for (auto const& track : tracksPerColl) { + if (!passedTrackSelectionForJetReconstruction(track)) + continue; + + fastjet::PseudoJet fourMomentum(track.px(), track.py(), track.pz(), track.energy(o2::constants::physics::MassPionCharged)); + fjParticlesReco.emplace_back(fourMomentum); + fjTracks.push_back(track); + } + + // Include V0s as tracks for jet reconstruction + if (useV0inJetRec && particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + AddV0sForJetReconstructionMCD(fjParticlesReco, fjTracks, v0sPerColl, mcParticles, vtxPos); + } + fjTracks.clear(); + + if (fjParticlesReco.size() >= 1) { // Reject empty events + fastjet::ClusterSequenceArea csReco(fjParticlesReco, jetDef, areaDef); + std::vector recoJets = fastjet::sorted_by_pt(csReco.inclusive_jets()); + if (recoJets.empty()) + continue; + auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticlesReco, recoJets[0], rJet); + + for (const auto& jet : recoJets) { + // jet must be fully contained in the acceptance + if ((std::fabs(jet.eta()) + rJet) > (configTracks.etaMax - deltaEtaEdge)) + continue; + + // jet pt must be larger than threshold + auto jetForSub = jet; + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + if (jetMinusBkg.pt() < minJetPt) + continue; + + jetsRecoThisEvent.push_back(jetMinusBkg); + } + } + // ---------------------------------------- + + // ---- Extract generated jets ---- + // LOG(info) << "recoCollisions.globalIndex()" << recoColl.globalIndex(); + // LOG(info) << "recoColl.mcCollisionId()" << recoColl.mcCollisionId(); + // LOG(info) << "mcCollision.globalIndex()" << mcCollision.globalIndex(); + std::vector fjParticlesGen; + std::vector> fjParticleObj; + + for (const auto& particle : mcParticlesPerColl) { + double minPtParticle = 0.1f; + if (particle.eta() < configTracks.etaMin || particle.eta() > configTracks.etaMax || particle.pt() < minPtParticle) + continue; + + // Select physical primary particles or HF decay products + if (!isPhysicalPrimaryOrFromHF(particle, mcParticles)) + continue; + + // Build 4-momentum assuming charged pion mass + static constexpr float kMassPionChargedSquared = o2::constants::physics::MassPionCharged * o2::constants::physics::MassPionCharged; + const double energy = std::sqrt(particle.p() * particle.p() + kMassPionChargedSquared); + fastjet::PseudoJet fourMomentum(particle.px(), particle.py(), particle.pz(), energy); + fourMomentum.set_user_index(particle.pdgCode()); + fjParticlesGen.emplace_back(fourMomentum); + fjParticleObj.push_back(particle); + } + + if (useV0inJetRec && particleOfInterestDict[ParticleOfInterest::kV0Particles]) { + AddV0sForJetReconstructionMCP(fjParticlesGen, fjParticleObj, mcParticlesPerColl, mcParticles); + } + + if (fjParticlesGen.size() >= 1) { // Skip events with no particles + fastjet::ClusterSequenceArea csGen(fjParticlesGen, jetDef, areaDef); + std::vector genJets = fastjet::sorted_by_pt(csGen.inclusive_jets()); + if (genJets.empty()) + continue; + auto [rhoPerp, rhoMPerp] = jetutilities::estimateRhoPerpCone(fjParticlesGen, genJets[0], rJet); + + for (const auto& jet : genJets) { + if ((std::fabs(jet.eta()) + rJet) > (configTracks.etaMax - deltaEtaEdge)) + continue; + + auto jetForSub = jet; + fastjet::PseudoJet jetMinusBkg = backgroundSub.doRhoAreaSub(jetForSub, rhoPerp, rhoMPerp); + if (jetMinusBkg.pt() < minJetPt) + continue; + + jetsGenThisEvent.push_back(jetMinusBkg); + } + } + // ---------------------------------------- + + // Fill histograms for unfolding + ProcessJetMatchingFactorized(jetsGenThisEvent, jetsRecoThisEvent, centrality); + } + } + PROCESS_SWITCH(StrangenessInJetsIons, processPrepareUnfolding, "Process to prepare hsitograms for the unfolding procedure", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Tasks/Strangeness/strangenessderivedbinnedinfo.cxx b/PWGLF/Tasks/Strangeness/strangenessderivedbinnedinfo.cxx index 240e0b0922e..01c2bf31acc 100644 --- a/PWGLF/Tasks/Strangeness/strangenessderivedbinnedinfo.cxx +++ b/PWGLF/Tasks/Strangeness/strangenessderivedbinnedinfo.cxx @@ -331,7 +331,7 @@ struct strangenessderivedbinnedinfo { histos.print(); } - template // TCollision should be of the type: soa::Join::iterator or so + template // TCollision should be of the type: soa::Join::iterator or so void initCCDB(TCollision const& collision) { if (mRunNumber == collision.runNumber()) { @@ -351,7 +351,7 @@ struct strangenessderivedbinnedinfo { } template - bool isEventAccepted(TCollision collision, bool fillHists) + bool isEventAccepted(const TCollision& collision, bool fillHists) // check whether the collision passes our collision selections { if (fillHists) @@ -499,7 +499,7 @@ struct strangenessderivedbinnedinfo { } template - void fillEventHistograms(TCollision collision, float& centrality, float& occupancy) + void fillEventHistograms(const TCollision& collision, float& centrality, float& occupancy) { if (isPP) { // centrality = collision.centFT0M(); @@ -516,7 +516,7 @@ struct strangenessderivedbinnedinfo { } template - bool isV0Selected(TV0 v0, TCollision collision, float rapidity) + bool isV0Selected(const TV0& v0, const TCollision& collision, float rapidity) // precalculate this information so that a check is one mask operation, not many { // @@ -644,7 +644,7 @@ struct strangenessderivedbinnedinfo { } template - bool isCascadeSelected(TCascade casc, TCollision collision, float rapidity) + bool isCascadeSelected(const TCascade& casc, const TCollision& collision, float rapidity) // precalculate this information so that a check is one mask operation, not many { // @@ -769,7 +769,7 @@ struct strangenessderivedbinnedinfo { // ______________________________________________________ // Real data processing - no MC subscription - void process(soa::Join::iterator const& collision, V0Candidates const& fullV0s, CascadeCandidates const& fullCascades, DauTracks const&) + void process(soa::Join::iterator const& collision, V0Candidates const& fullV0s, CascadeCandidates const& fullCascades, DauTracks const&) { // Fire up CCDB if (cfgSkimmedProcessing) { diff --git a/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx b/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx index 3bf53942f44..e0c175fae1e 100644 --- a/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx +++ b/PWGLF/Tasks/Strangeness/taskLambdaSpinCorr.cxx @@ -575,7 +575,7 @@ struct LfTaskLambdaSpinCorr { // Processing Event Mixing SliceCache cache; using BinningType = ColumnBinningPolicy; - BinningType colBinning{{axisVertex, axisMultiplicityClass}, true}; + BinningType colBinning{{axisVertex, axisMultiplicityClass}}; Preslice tracksPerCollisionV0 = aod::v0data::collisionId; void processME(EventCandidates const& collisions, AllTrackCandidates const&, ResoV0s const& V0s) { @@ -804,7 +804,7 @@ struct LfTaskLambdaSpinCorr { using v0Cand = soa::Filtered; // void processDerivedData(soa::Join::iterator const& collision, v0Candidates const& V0s, dauTracks const&) - void processDerivedData(soa::Join::iterator const& collision, v0Cand const& V0s, dauTracks const&) + void processDerivedData(soa::Join::iterator const& collision, v0Cand const& V0s, dauTracks const&) { histos.fill(HIST("hEvtSelInfo"), 0.5); if (rctCut.requireRCTFlagChecker && !rctChecker(collision)) { @@ -926,7 +926,7 @@ struct LfTaskLambdaSpinCorr { // Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only Preslice tracksPerCollisionV0Mixed = o2::aod::v0data::straCollisionId; // for derived data only // void processDerivedDataMixed(soa::Join const& collisions, v0Candidates const& V0s, dauTracks const&) - void processDerivedDataMixed(soa::Join const& collisions, v0Cand const& V0s, dauTracks const&) + void processDerivedDataMixed(soa::Join const& collisions, v0Cand const& V0s, dauTracks const&) { diff --git a/PWGLF/Tasks/Strangeness/v0postprocessing.cxx b/PWGLF/Tasks/Strangeness/v0postprocessing.cxx index 6266e18344d..aaaa08c4a0b 100644 --- a/PWGLF/Tasks/Strangeness/v0postprocessing.cxx +++ b/PWGLF/Tasks/Strangeness/v0postprocessing.cxx @@ -267,7 +267,7 @@ struct v0postprocessing { return false; registry.fill(HIST("QA/hK0sSelection"), 1.5); - if (candidate.v0radius() < radius && candidate.v0radius() > maxradius) + if (candidate.v0radius() < radius || candidate.v0radius() > maxradius) return false; registry.fill(HIST("QA/hK0sSelection"), 2.5); @@ -330,7 +330,7 @@ struct v0postprocessing { void process(aod::MyV0Candidates const& myv0s) { - for (auto& candidate : myv0s) { + for (const auto& candidate : myv0s) { if (doQA) { registry.fill(HIST("QA/hK0sSelection"), 0.5); diff --git a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx index ec4360f2a5a..fb66f7e07e8 100644 --- a/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx +++ b/PWGLF/Tasks/Strangeness/v0ptinvmassplots.cxx @@ -23,6 +23,7 @@ This analysis includes three processes, one for Real Data and two for MC at the #include "PWGLF/DataModel/LFStrangenessPIDTables.h" #include "PWGLF/DataModel/LFStrangenessTables.h" #include "PWGLF/DataModel/mcCentrality.h" +#include "PWGLF/Utils/inelGt.h" #include "Common/CCDB/EventSelectionParams.h" #include "Common/DataModel/Centrality.h" @@ -88,70 +89,90 @@ struct V0PtInvMassPlots { // Configurable for histograms Configurable nBins{"nBins", 100, "N bins in all histos"}; - Configurable nBinsArmenteros{"nBinsArmenteros", 500, "N bins in Armenteros histos"}; + Configurable nBinsArmenteros{"nBinsArmenteros", 600, "N bins in Armenteros histos"}; // Configurables for Cuts Configurable cutZVertex{"cutZVertex", 10.0f, "Accepted z-vertex range (cm)"}; + Configurable cutZVertexGen{"cutZVertexGen", 100.0f, "Accepted z-vertex range (cm) for generated events"}; Configurable nSigmaTPCPion{"nSigmaTPCPion", 4, "nSigmaTPCPion"}; Configurable nSigmaTPCProton{"nSigmaTPCProton", 4, "nSigmaTPCProton"}; Configurable compv0masscut{"compv0masscut", 0.01, "CompetitiveV0masscut (GeV)"}; Configurable etadau{"etadau", 0.8, "Eta Daughters"}; Configurable etagen{"etagen", 0.8, "Eta Generated"}; + Configurable etagenstrict{"etagenstrict", 0.5, "Strict Eta Generated"}; Configurable rapidityCut{"rapidityCut", 0.5, "V0 Rapidity Window"}; Configurable itsMinHits{"itsMinHits", 1.0, "Minimum Hits of Daughter Tracks in the ITS"}; // Configurables switches for event selection - Configurable dosel8{"dosel8", true, "Enable sel8 event selection"}; - Configurable doNoTimeFrameBorder{"doNoTimeFrameBorder", true, "Enable NoTimeFrameBorder event selection"}; - Configurable doNoITSROFrameBorder{"doNoITSROFrameBorder", true, "Enable NoITSROFrameBorder event selection"}; - Configurable doIsTriggerTVX{"doIsTriggerTVX", true, "Enable IsTriggerTVX event selection"}; - Configurable docutZVertex{"docutZVertex", true, "Enable cutZVertex event selection"}; - Configurable doIsVertexTOFmatched{"doIsVertexTOFmatched", true, "Enable IsVertexTOFmatched event selection"}; - Configurable doNoSameBunchPileup{"doNoSameBunchPileup", true, "Enable NoSameBunchPileup event selection"}; - Configurable doIsVertexITSTPC{"doIsVertexITSTPC", true, "Enable IsVertexITSTPC event selection"}; - Configurable doisInelGt0{"doisInelGt0", true, "Enable isInelGt0 event selection"}; + struct EventSelectionConfig : ConfigurableGroup { + std::string prefix = "eventSelections"; // JSON group name + Configurable dosel8{"dosel8", true, "Enable sel8 event selection"}; + Configurable doNoTimeFrameBorder{"doNoTimeFrameBorder", true, "Enable NoTimeFrameBorder event selection"}; + Configurable doNoITSROFrameBorder{"doNoITSROFrameBorder", true, "Enable NoITSROFrameBorder event selection"}; + Configurable doIsTriggerTVX{"doIsTriggerTVX", true, "Enable IsTriggerTVX event selection"}; + Configurable docutZVertex{"docutZVertex", true, "Enable cutZVertex event selection"}; + Configurable doIsVertexTOFmatched{"doIsVertexTOFmatched", true, "Enable IsVertexTOFmatched event selection"}; + Configurable doNoSameBunchPileup{"doNoSameBunchPileup", true, "Enable NoSameBunchPileup event selection"}; + Configurable doIsVertexITSTPC{"doIsVertexITSTPC", true, "Enable IsVertexITSTPC event selection"}; + Configurable doisInelGt0{"doisInelGt0", true, "Enable isInelGt0 event selection"}; + Configurable doZvertexGenCut{"doZvertexGenCut", false, "Enable Generated Z vertex cut"}; + }; // Configurables switches for v0 selection - Configurable doRapidityCut{"doRapidityCut", true, "Enable rapidity v0 selection"}; - Configurable doDaughterPseudorapidityCut{"doDaughterPseudorapidityCut", true, "Enable Daughter pseudorapidity v0 selection"}; - Configurable doisNotITSAfterburner{"doisNotITSAfterburner", true, "Enable Tracks do not come from Afterburner"}; - Configurable doitsMinHits{"doitsMinHits", true, "Enable ITS Minimum hits"}; + struct V0SelectionConfig : ConfigurableGroup { + std::string prefix = "v0Selections"; // JSON group name + Configurable doDaughterPseudorapidityCut{"doDaughterPseudorapidityCut", true, "Enable Daughter pseudorapidity v0 selection"}; + Configurable doisNotITSAfterburner{"doisNotITSAfterburner", true, "Enable Tracks do not come from Afterburner"}; + Configurable doitsMinHits{"doitsMinHits", true, "Enable ITS Minimum hits"}; + }; // Configurables switches for K0sh selection - Configurable dotruthK0sh{"dotruthK0sh", true, "Enable K0sh MC Matching"}; - Configurable doK0shTPCPID{"doK0shTPCPID", true, "Enable K0sh TPC PID"}; - Configurable doK0shcomptmasscut{"doK0shcomptmasscut", true, "Enable K0sh Competitive V0 Mass Cut"}; - Configurable doK0shMaxct{"doK0shMaxct", true, "Enable K0sh Max ct Cut"}; - Configurable doK0shArmenterosCut{"doK0shArmenterosCut", true, "Enable K0sh Armenteros Cut"}; - Configurable doK0shcosPACut{"doK0shcosPACut", true, "Enable K0sh cosPA Topological Cut"}; - Configurable doK0shDCAdauCut{"doK0shDCAdauCut", true, "Enable K0sh DCA daughters Topological Cut"}; - Configurable doK0shv0radiusCut{"doK0shv0radiusCut", true, "Enable K0sh v0radius Topological Cut"}; - Configurable doK0shdcaposdautopv{"doK0shdcaposdautopv", true, "Enable K0sh DCA pos daughter to PV Topological Cut"}; - Configurable doK0shdcanegdautopv{"doK0shdcanegdautopv", true, "Enable K0sh DCA neg daughter to PV Topological Cut"}; + struct K0shSelectionConfig : ConfigurableGroup { + std::string prefix = "k0shSelections"; // JSON group name + Configurable doK0shRapidityCut{"doK0shRapidityCut", true, "Enable rapidity K0sh selection"}; + Configurable dotruthK0sh{"dotruthK0sh", true, "Enable K0sh MC Matching"}; + Configurable doK0shTPCPID{"doK0shTPCPID", true, "Enable K0sh TPC PID"}; + Configurable doK0shcomptmasscut{"doK0shcomptmasscut", true, "Enable K0sh Competitive V0 Mass Cut"}; + Configurable doK0shMaxct{"doK0shMaxct", true, "Enable K0sh Max ct Cut"}; + Configurable doK0shArmenterosCut{"doK0shArmenterosCut", true, "Enable K0sh Armenteros Cut"}; + Configurable doK0shcosPACut{"doK0shcosPACut", true, "Enable K0sh cosPA Topological Cut"}; + Configurable doK0shDCAdauCut{"doK0shDCAdauCut", true, "Enable K0sh DCA daughters Topological Cut"}; + Configurable doK0shv0radiusCut{"doK0shv0radiusCut", true, "Enable K0sh v0radius Topological Cut"}; + Configurable doK0shdcaposdautopv{"doK0shdcaposdautopv", true, "Enable K0sh DCA pos daughter to PV Topological Cut"}; + Configurable doK0shdcanegdautopv{"doK0shdcanegdautopv", true, "Enable K0sh DCA neg daughter to PV Topological Cut"}; + }; // Configurables switches for Lambda selection - Configurable dotruthLambda{"dotruthLambda", true, "Enable Lambda MC Matching"}; - Configurable doLambdaTPCPID{"doLambdaTPCPID", true, "Enable Lambda TPC PID"}; - Configurable doLambdacomptmasscut{"doLambdacomptmasscut", true, "Enable Lambda Competitive V0 Mass Cut"}; - Configurable doLambdaMaxct{"doLambdaMaxct", true, "Enable Lambda Max ct Cut"}; - Configurable doLambdaArmenterosCut{"doLambdaArmenterosCut", true, "Enable Lambda Armenteros Cut"}; - Configurable doLambdacosPACut{"doLambdacosPACut", true, "Enable Lambda cosPA Topological Cut"}; - Configurable doLambdaDCAdauCut{"doLambdaDCAdauCut", true, "Enable Lambda DCA daughters Topological Cut"}; - Configurable doLambdav0radiusCut{"doLambdav0radiusCut", true, "Enable Lambda v0radius Topological Cut"}; - Configurable doLambdadcaposdautopv{"doLambdadcaposdautopv", true, "Enable Lambda DCA pos daughter to PV Topological Cut"}; - Configurable doLambdadcanegdautopv{"doLambdadcanegdautopv", true, "Enable Lambda DCA neg daughter to PV Topological Cut"}; + struct LambdaSelectionConfig : ConfigurableGroup { + std::string prefix = "lambdaSelections"; // JSON group name + Configurable doLambdaRapidityCut{"doLambdaRapidityCut", true, "Enable rapidity Lambda selection"}; + Configurable dotruthLambda{"dotruthLambda", true, "Enable Lambda MC Matching"}; + Configurable doLambdaTPCPID{"doLambdaTPCPID", true, "Enable Lambda TPC PID"}; + Configurable doLambdacomptmasscut{"doLambdacomptmasscut", true, "Enable Lambda Competitive V0 Mass Cut"}; + Configurable doLambdaMaxct{"doLambdaMaxct", true, "Enable Lambda Max ct Cut"}; + Configurable doLambdaArmenterosCut{"doLambdaArmenterosCut", true, "Enable Lambda Armenteros Cut"}; + Configurable doLambdacosPACut{"doLambdacosPACut", true, "Enable Lambda cosPA Topological Cut"}; + Configurable doLambdaDCAdauCut{"doLambdaDCAdauCut", true, "Enable Lambda DCA daughters Topological Cut"}; + Configurable doLambdav0radiusCut{"doLambdav0radiusCut", true, "Enable Lambda v0radius Topological Cut"}; + Configurable doLambdadcaposdautopv{"doLambdadcaposdautopv", true, "Enable Lambda DCA pos daughter to PV Topological Cut"}; + Configurable doLambdadcanegdautopv{"doLambdadcanegdautopv", true, "Enable Lambda DCA neg daughter to PV Topological Cut"}; + }; // Configurables switches for AntiLambda selection - Configurable dotruthAntiLambda{"dotruthAntiLambda", true, "Enable AntiLambda MC Matching"}; - Configurable doAntilambdaTPCPID{"doAntilambdaTPCPID", true, "Enable AntiLambda TPC PID"}; - Configurable doAntilambdacomptmasscut{"doAntilambdacomptmasscut", true, "Enable AntiLambda Competitive V0 Mass Cut"}; - Configurable doAntilambdaMaxct{"doAntilambdaMaxct", true, "Enable AntiLambda Max ct Cut"}; - Configurable doAntilambdaArmenterosCut{"doAntilambdaArmenterosCut", true, "Enable AntiLambda Armenteros Cut"}; - Configurable doAntilambdacosPACut{"doAntilambdacosPACut", true, "Enable AntiLambda cosPA Topological Cut"}; - Configurable doAntilambdaDCAdauCut{"doAntilambdaDCAdauCut", true, "Enable AntiLambda DCA daughters Topological Cut"}; - Configurable doAntilambdav0radiusCut{"doAntilambdav0radiusCut", true, "Enable AntiLambda v0radius Topological Cut"}; - Configurable doAntilambdadcaposdautopv{"doAntilambdadcaposdautopv", true, "Enable AntiLambda DCA pos daughter to PV Topological Cut"}; - Configurable doAntilambdadcanegdautopv{"doAntilambdadcanegdautopv", true, "Enable AntiLambda DCA neg daughter to PV Topological Cut"}; + struct AntiLambdaSelectionConfig : ConfigurableGroup { + std::string prefix = "antilambdaSelections"; // JSON group name + Configurable doAntiLambdaRapidityCut{"doAntiLambdaRapidityCut", true, "Enable rapidity AntiLambda selection"}; + Configurable dotruthAntiLambda{"dotruthAntiLambda", true, "Enable AntiLambda MC Matching"}; + Configurable doAntilambdaTPCPID{"doAntilambdaTPCPID", true, "Enable AntiLambda TPC PID"}; + Configurable doAntilambdacomptmasscut{"doAntilambdacomptmasscut", true, "Enable AntiLambda Competitive V0 Mass Cut"}; + Configurable doAntilambdaMaxct{"doAntilambdaMaxct", true, "Enable AntiLambda Max ct Cut"}; + Configurable doAntilambdaArmenterosCut{"doAntilambdaArmenterosCut", true, "Enable AntiLambda Armenteros Cut"}; + Configurable doAntilambdacosPACut{"doAntilambdacosPACut", true, "Enable AntiLambda cosPA Topological Cut"}; + Configurable doAntilambdaDCAdauCut{"doAntilambdaDCAdauCut", true, "Enable AntiLambda DCA daughters Topological Cut"}; + Configurable doAntilambdav0radiusCut{"doAntilambdav0radiusCut", true, "Enable AntiLambda v0radius Topological Cut"}; + Configurable doAntilambdadcaposdautopv{"doAntilambdadcaposdautopv", true, "Enable AntiLambda DCA pos daughter to PV Topological Cut"}; + Configurable doAntilambdadcanegdautopv{"doAntilambdadcanegdautopv", true, "Enable AntiLambda DCA neg daughter to PV Topological Cut"}; + }; // Configurable K0sh Cuts (best cuts determined by v0topologicalcuts task) Configurable k0shSettingdcav0dau{"k0shSettingdcav0dau", 0.3, "DCA V0 Daughters"}; @@ -343,9 +364,6 @@ struct V0PtInvMassPlots { rMCCorrections.add("hK0shBeforeEventSelectionPtSpectrum", "hK0shBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); // not filled rMCCorrections.add("hLambdaBeforeEventSelectionPtSpectrum", "hLambdaBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hAntiLambdaBeforeEventSelectionPtSpectrum", "hAntiLambdaBeforeEventSelectionPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); - rMCCorrections.add("hK0shAfterEventSelectionPtSpectrum", "hK0shAfterEventSelectionPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); - rMCCorrections.add("hLambdaAfterEventSelectionPtSpectrum", "hLambdaAfterEventSelectionPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); - rMCCorrections.add("hAntiLambdaAfterEventSelectionPtSpectrum", "hAntiLambdaAfterEventSelectionPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); // Event and V0s Corrections rMCCorrections.add("hNEvents_Corrections", "hNEvents_Corrections", {HistType::kTH2D, {{10, 0.f, 10.f}, centAxis}}); @@ -353,8 +371,11 @@ struct V0PtInvMassPlots { // Generated Level Pt Spectrums (with rapidity cut) rMCCorrections.add("GenParticleRapidity", "GenParticleRapidity", {HistType::kTH1F, {{nBins, -10.0f, 10.0f}}}); rMCCorrections.add("hK0shGeneratedPtSpectrum", "hK0shGeneratedPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); + rMCCorrections.add("hK0shGeneratedPtSpectrumMult", "hK0shGeneratedPtSpectrumMult", {HistType::kTH2D, {k0ShortPtAxis, nchAxis}}); rMCCorrections.add("hLambdaGeneratedPtSpectrum", "hLambdaGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); + rMCCorrections.add("hLambdaGeneratedPtSpectrumMult", "hLambdaGeneratedPtSpectrumMult", {HistType::kTH2D, {lambdaPtAxis, nchAxis}}); rMCCorrections.add("hAntiLambdaGeneratedPtSpectrum", "hAntiLambdaGeneratedPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); + rMCCorrections.add("hAntiLambdaGeneratedPtSpectrumMult", "hAntiLambdaGeneratedPtSpectrumMult", {HistType::kTH2D, {antilambdaPtAxis, nchAxis}}); rMCCorrections.add("hXiMinusGeneratedPtSpectrum", "hXiMinusGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hXiZeroGeneratedPtSpectrum", "hXiZeroGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); rMCCorrections.add("hOmegaGeneratedPtSpectrum", "hOmegaGeneratedPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); @@ -364,45 +385,66 @@ struct V0PtInvMassPlots { rMCCorrections.add("hPhiGeneratedPtSpectrum", "hPhiGeneratedPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); rMCCorrections.add("hGenPartcles", "hGenPartcles", {HistType::kTH2D, {nchAxis, centAxis}}); + // Generated Level Pt Spectrums (with rapidity cut for RecoINEL>0) + rMCCorrections.add("hK0shGeneratedRecoPtSpectrum", "hK0shGeneratedRecoPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); + rMCCorrections.add("hLambdaGeneratedRecoPtSpectrum", "hLambdaGeneratedRecoPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); + rMCCorrections.add("hAntiLambdaGeneratedRecoPtSpectrum", "hAntiLambdaGeneratedRecoPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); + rMCCorrections.add("hXiMinusGeneratedRecoPtSpectrum", "hXiMinusGeneratedRecoPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); + rMCCorrections.add("hXiZeroGeneratedRecoPtSpectrum", "hXiZeroGeneratedRecoPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); + rMCCorrections.add("hOmegaGeneratedRecoPtSpectrum", "hOmegaGeneratedRecoPtSpectrum", {HistType::kTH2D, {lambdaPtAxis, centAxis}}); + rMCCorrections.add("hXiPlusGeneratedRecoPtSpectrum", "hXiPlusGeneratedRecoPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); + rMCCorrections.add("hAntiXiZeroGeneratedRecoPtSpectrum", "hAntiXiZeroGeneratedRecoPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); + rMCCorrections.add("hAntiOmegaGeneratedRecoPtSpectrum", "hAntiOmegaGeneratedRecoPtSpectrum", {HistType::kTH2D, {antilambdaPtAxis, centAxis}}); + rMCCorrections.add("hPhiGeneratedRecoPtSpectrum", "hPhiGeneratedRecoPtSpectrum", {HistType::kTH2D, {k0ShortPtAxis, centAxis}}); + // NCh Analysis rNchAnalysis.add("hNchCentralityGenerated", "hNchCentralityGenerated", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality Generated rNchAnalysis.add("hNchCentralityGeneratedAfterEventSelection", "hNchCentralityGeneratedAfterEventSelection", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality Generated After Event Selection - rNchAnalysis.add("hNchCentrality", "hNchCentrality", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality + rNchAnalysis.add("hNchCentrality", "hNchCentrality", {HistType::kTH2D, {centAxis, nchAxis}}); + rNchAnalysis.add("hNchCentralityEtaHalf", "hNchCentralityEtaHalf", {HistType::kTH2D, {centAxis, nchAxis}}); // Nch vs Centrality EtaHalfCut + rNchAnalysis.add("hNchCentralityEtaHalfGen", "hNchCentralityEtaHalfGen", {HistType::kTH2D, {nchAxis, centAxis}}); // Nch vs Centrality EtaHalfCut } + // Grouped Configurables + EventSelectionConfig eventSelection; + V0SelectionConfig v0Selection; + K0shSelectionConfig k0shSelection; + LambdaSelectionConfig lambdaSelection; + AntiLambdaSelectionConfig antilambdaSelection; + // Event selection function template bool acceptEvent(TCollision const& collision) { rPtAnalysis.fill(HIST("hNEvents"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(1, "All"); - if (dosel8 && !collision.sel8()) { + if (eventSelection.dosel8 && !collision.sel8()) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(2, "sel 8"); - if (doNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + if (eventSelection.doNoTimeFrameBorder && !collision.selection_bit(aod::evsel::kNoTimeFrameBorder)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(3, "NoTimeFrameBorder"); - if (doNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + if (eventSelection.doNoITSROFrameBorder && !collision.selection_bit(aod::evsel::kNoITSROFrameBorder)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(4, "NoITSROFrameBorder"); - if (doIsTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + if (eventSelection.doIsTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(5, "IsTriggerTVX"); - if (docutZVertex && !(std::abs(collision.posZ()) < cutZVertex)) { + if (eventSelection.docutZVertex && !(std::abs(collision.posZ()) < cutZVertex)) { return false; } rPtAnalysis.fill(HIST("hNEvents"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNEvents"))->GetXaxis()->SetBinLabel(6, "cutZVertex"); - if (doisInelGt0 && !(collision.multNTracksPVeta1() > 0)) { - // if (doisInelGt0 && !(collision.multMCNParticlesEta10() > 0)) { //CHANGE TO THIS + if (eventSelection.doisInelGt0 && !(collision.multNTracksPVeta1() > 0)) { + // if (eventSelection.doisInelGt0 && !(collision.multMCNParticlesEta10() > 0)) { //CHANGE TO THIS return false; } rPtAnalysis.fill(HIST("hNEvents"), 6.5, collision.centFT0M()); @@ -418,21 +460,17 @@ struct V0PtInvMassPlots { { rMCCorrections.fill(HIST("hGenPartcles"), 0.5, mcCollision.centFT0M()); rMCCorrections.get(HIST("hGenPartcles"))->GetXaxis()->SetBinLabel(1, "All Gen Particles"); - if (!particle.isPhysicalPrimary()) { // Daughters Pseudorapidity Cut + if (!particle.isPhysicalPrimary()) { return false; } rMCCorrections.fill(HIST("hGenPartcles"), 1.5, mcCollision.centFT0M()); rMCCorrections.get(HIST("hGenPartcles"))->GetXaxis()->SetBinLabel(2, "Physical Primary"); + if (!particle.producedByGenerator()) { return false; } rMCCorrections.fill(HIST("hGenPartcles"), 2.5, mcCollision.centFT0M()); rMCCorrections.get(HIST("hGenPartcles"))->GetXaxis()->SetBinLabel(3, "Produced by Generator"); - if (std::abs(particle.eta()) > etagen) { // Eta cut - return false; - } - rMCCorrections.fill(HIST("hGenPartcles"), 3.5, mcCollision.centFT0M()); - rMCCorrections.get(HIST("hGenPartcles"))->GetXaxis()->SetBinLabel(4, "Eta Cut"); auto pdgParticle = pdgDB->GetParticle(particle.pdgCode()); if (pdgParticle == nullptr) { return false; @@ -440,8 +478,21 @@ struct V0PtInvMassPlots { if (std::abs(pdgParticle->Charge()) < 3) { return false; } - rMCCorrections.fill(HIST("hGenPartcles"), 4.5, mcCollision.centFT0M()); + rMCCorrections.fill(HIST("hGenPartcles"), 3.5, mcCollision.centFT0M()); rMCCorrections.get(HIST("hGenPartcles"))->GetXaxis()->SetBinLabel(4, "Charge Cut"); + + // Standard eta cut + if (std::abs(particle.eta()) > etagen) { + return false; + } + rMCCorrections.fill(HIST("hGenPartcles"), 4.5, mcCollision.centFT0M()); + rMCCorrections.get(HIST("hGenPartcles"))->GetXaxis()->SetBinLabel(5, "Eta Cut"); + // Stricter eta cut + if (std::abs(particle.eta()) > etagenstrict) { + return false; + } + rMCCorrections.fill(HIST("hGenPartcles"), 5.5, mcCollision.centFT0M()); + rMCCorrections.get(HIST("hGenPartcles"))->GetXaxis()->SetBinLabel(6, "Strict Eta Cut"); return true; } @@ -451,17 +502,17 @@ struct V0PtInvMassPlots { { rPtAnalysis.fill(HIST("hNV0s"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(1, "All V0s"); - if (doDaughterPseudorapidityCut && !(std::abs(posDaughterTrack.eta()) < etadau && std::abs(negDaughterTrack.eta()) < etadau)) { // Daughters Pseudorapidity Cut + if (v0Selection.doDaughterPseudorapidityCut && !(std::abs(posDaughterTrack.eta()) < etadau && std::abs(negDaughterTrack.eta()) < etadau)) { // Daughters Pseudorapidity Cut return false; } rPtAnalysis.fill(HIST("hNV0s"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(2, "Dau Pseudorapidity"); - if (doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks + if (v0Selection.doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks return false; } rPtAnalysis.fill(HIST("hNV0s"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(3, "ITS Afterburner"); - if (doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS + if (v0Selection.doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS return false; } rPtAnalysis.fill(HIST("hNV0s"), 3.5, collision.centFT0M()); @@ -481,61 +532,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 0.5, v0.mK0Short()); - if (doRapidityCut && (std::abs(v0.rapidity(0)) > rapidityCut)) { // V0 Rapidity Cut + if (k0shSelection.doK0shRapidityCut && (std::abs(v0.rapidity(0)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 1.5, v0.mK0Short()); - if (doK0shTPCPID && (std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion || std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID for two pions + if (k0shSelection.doK0shTPCPID && (std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion || std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID for two pions return false; } rPtAnalysis.fill(HIST("hNK0sh"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 2.5, v0.mK0Short()); - if (doK0shcomptmasscut && ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // K0sh competitive v0 mass cut (cut out Lambdas and Anti-Lambdas) + if (k0shSelection.doK0shcomptmasscut && ((std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // K0sh competitive v0 mass cut (cut out Lambdas and Anti-Lambdas) return false; } rPtAnalysis.fill(HIST("hNK0sh"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 3.5, v0.mK0Short()); - if (doK0shMaxct && (v0.v0radius() > k0shmaxct)) { // K0sh max ct + if (k0shSelection.doK0shMaxct && (v0.v0radius() > k0shmaxct)) { // K0sh max ct return false; } rPtAnalysis.fill(HIST("hNK0sh"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 4.5, v0.mK0Short()); - if (doK0shArmenterosCut && (v0.qtarm() < (k0shparamArmenterosCut * std::abs(v0.alpha())))) { // K0sh Armenteros Cut + if (k0shSelection.doK0shArmenterosCut && (v0.qtarm() < (k0shparamArmenterosCut * std::abs(v0.alpha())))) { // K0sh Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 5.5, v0.mK0Short()); - if (doK0shcosPACut && (v0.v0cosPA() < k0shSettingcosPA)) { // K0sh cosPA Topological Cut + if (k0shSelection.doK0shcosPACut && (v0.v0cosPA() < k0shSettingcosPA)) { // K0sh cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 6.5, v0.mK0Short()); - if (doK0shDCAdauCut && (v0.dcaV0daughters() > k0shSettingdcav0dau)) { // K0sh DCAdaughters Topological Cut + if (k0shSelection.doK0shDCAdauCut && (v0.dcaV0daughters() > k0shSettingdcav0dau)) { // K0sh DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 7.5, v0.mK0Short()); - if (doK0shv0radiusCut && (v0.v0radius() < k0shSettingradius)) { // K0sh v0radius Topological Cut + if (k0shSelection.doK0shv0radiusCut && (v0.v0radius() < k0shSettingradius)) { // K0sh v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 8.5, v0.mK0Short()); - if (doK0shdcaposdautopv && (std::abs(v0.dcapostopv()) < k0shSettingdcapostopv)) { // K0sh DCAPosDaughterToPV Topological Cut + if (k0shSelection.doK0shdcaposdautopv && (std::abs(v0.dcapostopv()) < k0shSettingdcapostopv)) { // K0sh DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNK0sh"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassK0ShortvsCuts"), 9.5, v0.mK0Short()); - if (doK0shdcanegdautopv && (std::abs(v0.dcanegtopv()) < k0shSettingdcanegtopv)) { // K0sh DCANegDaughterToPV Topological Cut + if (k0shSelection.doK0shdcanegdautopv && (std::abs(v0.dcanegtopv()) < k0shSettingdcanegtopv)) { // K0sh DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNK0sh"), 10.5, collision.centFT0M()); @@ -563,61 +614,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 0.5, v0.mLambda()); - if (doRapidityCut && (std::abs(v0.rapidity(1)) > rapidityCut)) { // V0 Rapidity Cut + if (lambdaSelection.doLambdaRapidityCut && (std::abs(v0.rapidity(1)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 1.5, v0.mLambda()); - if (doLambdaTPCPID && ((std::abs(posDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton) || (std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion))) { // TPC PID on daughter pion and proton for Lambda + if (lambdaSelection.doLambdaTPCPID && ((std::abs(posDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton) || (std::abs(negDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion))) { // TPC PID on daughter pion and proton for Lambda return false; } rPtAnalysis.fill(HIST("hNLambda"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 2.5, v0.mLambda()); - if (doLambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // Lambda competitive v0 mass cut (cut out Kaons) + if (lambdaSelection.doLambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mAntiLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // Lambda competitive v0 mass cut (cut out Kaons) return false; } rPtAnalysis.fill(HIST("hNLambda"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 3.5, v0.mLambda()); - if (doLambdaMaxct && (v0.v0radius() > lambdamaxct)) { // Lambda max ct + if (lambdaSelection.doLambdaMaxct && (v0.v0radius() > lambdamaxct)) { // Lambda max ct return false; } rPtAnalysis.fill(HIST("hNLambda"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 4.5, v0.mLambda()); - if (doLambdaArmenterosCut && v0.qtarm() < (lambdaparamArmenterosCut * std::abs(v0.alpha()))) { // Lambda Armenteros Cut + if (lambdaSelection.doLambdaArmenterosCut && (v0.alpha() <= 0 || v0.qtarm() > lambdaparamArmenterosCut * v0.alpha())) { // Lambda Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 5.5, v0.mLambda()); - if (doLambdacosPACut && (v0.v0cosPA() < lambdaSettingcosPA)) { // Lambda cosPA Topological Cut + if (lambdaSelection.doLambdacosPACut && (v0.v0cosPA() < lambdaSettingcosPA)) { // Lambda cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 6.5, v0.mLambda()); - if (doLambdaDCAdauCut && (v0.dcaV0daughters() > lambdaSettingdcav0dau)) { // Lambda DCAdaughters Topological Cut + if (lambdaSelection.doLambdaDCAdauCut && (v0.dcaV0daughters() > lambdaSettingdcav0dau)) { // Lambda DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 7.5, v0.mLambda()); - if (doLambdav0radiusCut && (v0.v0radius() < lambdaSettingradius)) { // Lambda v0radius Topological Cut + if (lambdaSelection.doLambdav0radiusCut && (v0.v0radius() < lambdaSettingradius)) { // Lambda v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 8.5, v0.mLambda()); - if (doLambdadcaposdautopv && (std::abs(v0.dcapostopv()) < lambdaSettingdcapostopv)) { // Lambda DCAPosDaughterToPV Topological Cut + if (lambdaSelection.doLambdadcaposdautopv && (std::abs(v0.dcapostopv()) < lambdaSettingdcapostopv)) { // Lambda DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNLambda"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassLambdavsCuts"), 9.5, v0.mLambda()); - if (doLambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < lambdaSettingdcanegtopv)) { // Lambda DCANegDaughterToPV Topological Cut + if (lambdaSelection.doLambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < lambdaSettingdcanegtopv)) { // Lambda DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNLambda"), 10.5, collision.centFT0M()); @@ -645,61 +696,61 @@ struct V0PtInvMassPlots { rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(1, "All"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 0.5, v0.mAntiLambda()); - if (doRapidityCut && (std::abs(v0.rapidity(2)) > rapidityCut)) { // V0 Rapidity Cut + if (antilambdaSelection.doAntiLambdaRapidityCut && (std::abs(v0.rapidity(2)) > rapidityCut)) { // V0 Rapidity Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(2, "Rapidity"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 1.5, v0.mAntiLambda()); - if (doAntilambdaTPCPID && (std::abs(negDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton || std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID on daughter pion and proton for AntiLambda + if (antilambdaSelection.doAntilambdaTPCPID && (std::abs(negDaughterTrack.tpcNSigmaPr()) > nSigmaTPCProton || std::abs(posDaughterTrack.tpcNSigmaPi()) > nSigmaTPCPion)) { // TPC PID on daughter pion and proton for AntiLambda return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(3, "TPC_PID"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 2.5, v0.mAntiLambda()); - if (doAntilambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // AntiLambda competitive v0 mass cut (cut out Kaons) + if (antilambdaSelection.doAntilambdacomptmasscut && ((std::abs(v0.mK0Short() - o2::constants::physics::MassK0Short) < compv0masscut) || (std::abs(v0.mLambda() - o2::constants::physics::MassLambda0) < compv0masscut))) { // AntiLambda competitive v0 mass cut (cut out Kaons) return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 3.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(4, "Compt_Mass"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 3.5, v0.mAntiLambda()); - if (doAntilambdaMaxct && (v0.v0radius() > antilambdamaxct)) { // AntiLambda max ct + if (antilambdaSelection.doAntilambdaMaxct && (v0.v0radius() > antilambdamaxct)) { // AntiLambda max ct return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 4.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(5, "Max_ct"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 4.5, v0.mAntiLambda()); - if (doAntilambdaArmenterosCut && (v0.qtarm() < (antilambdaparamArmenterosCut * std::abs(v0.alpha())))) { // AntiLambda Armenteros Cut + if (antilambdaSelection.doAntilambdaArmenterosCut && (v0.alpha() > 0 || v0.qtarm() > antilambdaparamArmenterosCut * std::abs(v0.alpha()))) { // AntiLambda Armenteros Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 5.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(6, "Armenteros"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 5.5, v0.mAntiLambda()); - if (doAntilambdacosPACut && (v0.v0cosPA() < antilambdaSettingcosPA)) { // AntiLambda cosPA Topological Cut + if (antilambdaSelection.doAntilambdacosPACut && (v0.v0cosPA() < antilambdaSettingcosPA)) { // AntiLambda cosPA Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 6.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(7, "cosPA"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 6.5, v0.mAntiLambda()); - if (doAntilambdaDCAdauCut && (v0.dcaV0daughters() > antilambdaSettingdcav0dau)) { // AntiLambda DCAdaughters Topological Cut + if (antilambdaSelection.doAntilambdaDCAdauCut && (v0.dcaV0daughters() > antilambdaSettingdcav0dau)) { // AntiLambda DCAdaughters Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 7.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(8, "DCAdau"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 7.5, v0.mAntiLambda()); - if (doAntilambdav0radiusCut && (v0.v0radius() < antilambdaSettingradius)) { // AntiLambda v0radius Topological Cut + if (antilambdaSelection.doAntilambdav0radiusCut && (v0.v0radius() < antilambdaSettingradius)) { // AntiLambda v0radius Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 8.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(9, "v0radius"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 8.5, v0.mAntiLambda()); - if (doAntilambdadcaposdautopv && (std::abs(v0.dcapostopv()) < antilambdaSettingdcapostopv)) { // AntiLambda DCAPosDaughterToPV Topological Cut + if (antilambdaSelection.doAntilambdadcaposdautopv && (std::abs(v0.dcapostopv()) < antilambdaSettingdcapostopv)) { // AntiLambda DCAPosDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 9.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNAntiLambda"))->GetXaxis()->SetBinLabel(10, "DCAPosDautoPV"); rPtAnalysis.fill(HIST("hMassAntiLambdavsCuts"), 9.5, v0.mAntiLambda()); - if (doAntilambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < antilambdaSettingdcanegtopv)) { // AntiLambda DCANegDaughterToPV Topological Cut + if (antilambdaSelection.doAntilambdadcanegdautopv && (std::abs(v0.dcanegtopv()) < antilambdaSettingdcanegtopv)) { // AntiLambda DCANegDaughterToPV Topological Cut return false; } rPtAnalysis.fill(HIST("hNAntiLambda"), 10.5, collision.centFT0M()); @@ -725,17 +776,17 @@ struct V0PtInvMassPlots { { rPtAnalysis.fill(HIST("hNV0s"), 0.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(1, "All V0s"); - if (doDaughterPseudorapidityCut && !(std::abs(v0.positiveeta()) < etadau && std::abs(v0.negativeeta()) < etadau)) { // Daughters Pseudorapidity Cut + if (v0Selection.doDaughterPseudorapidityCut && !(std::abs(v0.positiveeta()) < etadau && std::abs(v0.negativeeta()) < etadau)) { // Daughters Pseudorapidity Cut return false; } rPtAnalysis.fill(HIST("hNV0s"), 1.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(2, "Dau Pseudorapidity"); - if (doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks + if (v0Selection.doisNotITSAfterburner && (posDaughterTrack.isITSAfterburner() || negDaughterTrack.isITSAfterburner())) { // ITS After Burner on daughter tracks return false; } rPtAnalysis.fill(HIST("hNV0s"), 2.5, collision.centFT0M()); rPtAnalysis.get(HIST("hNV0s"))->GetXaxis()->SetBinLabel(3, "ITS Afterburner"); - if (doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS + if (v0Selection.doitsMinHits && !(posDaughterTrack.itsNCls() >= itsMinHits && negDaughterTrack.itsNCls() >= itsMinHits)) { // Minimum hits in the ITS return false; } rPtAnalysis.fill(HIST("hNV0s"), 3.5, collision.centFT0M()); @@ -757,102 +808,149 @@ struct V0PtInvMassPlots { soa::SmallGroups> const& collisions, aod::McParticles const& mcParticles) { - // Event Efficiency, Event Split and V0 Signal Loss Corrections - rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events - if (std::abs(mcCollision.posZ()) > cutZVertex) { + rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events + if (eventSelection.doZvertexGenCut && std::abs(mcCollision.posZ()) > cutZVertexGen) { // Generated Z vertex cut return; } - if (!(mcCollision.multMCNParticlesEta10() > 0)) { // TRY TO CHANGE TO THIS - // if (!pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { - return; + // if (!(mcCollision.multMCNParticlesEta10() > 0)) { // TRY TO CHANGE TO THIS + bool isINELgt0 = false; + if (pwglf::isINELgtNmc(mcParticles, 0, pdgDB)) { + // return; + isINELgt0 = true; + rMCCorrections.fill(HIST("hNEvents_Corrections"), 1.5, mcCollision.centFT0M()); // Event Efficiency Denominator } - rMCCorrections.fill(HIST("hNEvents_Corrections"), 1.5, mcCollision.centFT0M()); // Event Efficiency Denominator - int NParticlesPerCollision = 0; // Counter for the number of particles per collision for the Nch analysis + int nParticlesPerCollision = 0; // Counter for the number of particles per collision for the Nch analysis // Particles (of interest) Generated Pt Spectrum and Signal Loss Denominator Loop for (const auto& mcParticle : mcParticles) { - if (std::abs(mcParticle.y()) < rapidityCut) { - if (mcParticle.isPhysicalPrimary()) { - rMCCorrections.fill(HIST("GenParticleRapidity"), mcParticle.y()); - if (mcParticle.pdgCode() == kK0Short) // K0sh matched - { - rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kLambda0) // Lambda matched - { - rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kLambda0Bar) // AntiLambda matched - { - rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kXiMinus) // Xi Minus matched - { - rMCCorrections.fill(HIST("hXiMinusGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kXi0) // Xi Zero matched - { - rMCCorrections.fill(HIST("hXiZeroGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kOmegaMinus) // Omega matched - { - rMCCorrections.fill(HIST("hOmegaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kXiPlusBar) // Xi Plus matched - { - rMCCorrections.fill(HIST("hXiPlusGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == -kXi0) // Anti-Xi Zero matched - { - rMCCorrections.fill(HIST("hAntiXiZeroGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kOmegaPlusBar) // Anti-Omega matched - { - rMCCorrections.fill(HIST("hAntiOmegaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); - } - if (mcParticle.pdgCode() == kPhi) // Phi - { - rMCCorrections.fill(HIST("hPhiGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + if (isINELgt0) { + if (std::abs(mcParticle.y()) < rapidityCut) { + if (mcParticle.isPhysicalPrimary()) { + rMCCorrections.fill(HIST("GenParticleRapidity"), mcParticle.y()); + if (mcParticle.pdgCode() == kK0Short) // K0sh matched + { + rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kLambda0) // Lambda matched + { + rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kLambda0Bar) // AntiLambda matched + { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kXiMinus) // Xi Minus matched + { + rMCCorrections.fill(HIST("hXiMinusGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kXi0) // Xi Zero matched + { + rMCCorrections.fill(HIST("hXiZeroGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kOmegaMinus) // Omega matched + { + rMCCorrections.fill(HIST("hOmegaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kXiPlusBar) // Xi Plus matched + { + rMCCorrections.fill(HIST("hXiPlusGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == -kXi0) // Anti-Xi Zero matched + { + rMCCorrections.fill(HIST("hAntiXiZeroGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kOmegaPlusBar) // Anti-Omega matched + { + rMCCorrections.fill(HIST("hAntiOmegaGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kPhi) // Phi + { + rMCCorrections.fill(HIST("hPhiGeneratedPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } } } + if (acceptGeneratedParticle(mcParticle, mcCollision)) { + nParticlesPerCollision++; + } } - if (acceptGeneratedParticle(mcParticle, mcCollision)) { - NParticlesPerCollision++; - } - } - rNchAnalysis.fill(HIST("hNchCentralityGenerated"), mcCollision.centFT0M(), NParticlesPerCollision); - // Signal Loss Numenator Loop + } // End of MCParticle Loop For Signal Loss Denominator + rNchAnalysis.fill(HIST("hNchCentralityGenerated"), mcCollision.centFT0M(), nParticlesPerCollision); + + // Signal Loss Start, Make sure we have INEL Collisions + + int recoCollINEL = 0; + int recoCollINELgt0 = 0; for (const auto& collision : collisions) { rMCCorrections.fill(HIST("hNEvents_Corrections"), 2.5, mcCollision.centFT0M()); // Number of Events Reconsctructed if (!acceptEvent(collision)) { // Event Selection - return; + continue; } - rMCCorrections.fill(HIST("hNEvents_Corrections"), 3.5, mcCollision.centFT0M()); // Event Split Denomimator and Event Efficiency Numenator - for (const auto& mcParticle : mcParticles) { - if (acceptGeneratedParticle(mcParticle, mcCollision)) { - NParticlesPerCollision++; // Counter to fill the NchCerntralityPlot after the loop + rMCCorrections.fill(HIST("hNEvents_Corrections"), 3.5, mcCollision.centFT0M()); // Event Split Numerator + recoCollINEL++; + if (collision.isInelGt0()) { // Check if the reconstructed collision fulfills the INEL>0 requirement + rMCCorrections.fill(HIST("hNEvents_Corrections"), 4.5, mcCollision.centFT0M()); // Event Split Denominator + recoCollINELgt0++; + } + } + + // From now on keep only mc collisions with at least one reconstructed collision (INEL) + if (recoCollINEL < 1) { + return; + } + if (recoCollINELgt0 > 0) { + rMCCorrections.fill(HIST("hNEvents_Corrections"), 5.5, mcCollision.centFT0M()); // Event Efficiency Numerator and Event Split Denominator + } + + // Singnal Loss Numerator Loop + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::abs(mcParticle.y()) > rapidityCut) { + continue; + } + if (recoCollINELgt0 > 0) { + if (mcParticle.pdgCode() == kK0Short) // K0sh matched + { + rMCCorrections.fill(HIST("hK0shGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); } - if (!mcParticle.isPhysicalPrimary()) { - continue; + if (mcParticle.pdgCode() == kLambda0) // Lambda matched + { + rMCCorrections.fill(HIST("hLambdaGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); } - if (std::abs(mcParticle.y()) > rapidityCut) { - continue; + if (mcParticle.pdgCode() == kLambda0Bar) // AntiLambda matched + { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kXiMinus) // Xi Minus matched + { + rMCCorrections.fill(HIST("hXiMinusGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); } - if (mcParticle.pdgCode() == kK0Short) // kzero matched + if (mcParticle.pdgCode() == kXi0) // Xi Zero matched { - rMCCorrections.fill(HIST("hK0shAfterEventSelectionPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + rMCCorrections.fill(HIST("hXiZeroGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); } - if (mcParticle.pdgCode() == kLambda0) // lambda matched + if (mcParticle.pdgCode() == kOmegaMinus) // Omega matched { - rMCCorrections.fill(HIST("hLambdaAfterEventSelectionPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + rMCCorrections.fill(HIST("hOmegaGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); } - if (mcParticle.pdgCode() == kLambda0Bar) // antilambda matched + if (mcParticle.pdgCode() == kXiPlusBar) // Xi Plus matched { - rMCCorrections.fill(HIST("hAntiLambdaAfterEventSelectionPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + rMCCorrections.fill(HIST("hXiPlusGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == -kXi0) // Anti-Xi Zero matched + { + rMCCorrections.fill(HIST("hAntiXiZeroGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kOmegaPlusBar) // Anti-Omega matched + { + rMCCorrections.fill(HIST("hAntiOmegaGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); + } + if (mcParticle.pdgCode() == kPhi) // Phi + { + rMCCorrections.fill(HIST("hPhiGeneratedRecoPtSpectrum"), mcParticle.pt(), mcCollision.centFT0M()); } } } - rNchAnalysis.fill(HIST("hNchCentralityGeneratedAfterEventSelection"), mcCollision.centFT0M(), NParticlesPerCollision); - // End of Signal Loss Numenator Loop } // This is the Process for the MC reconstructed Data // void recMCProcess(soa::Join::iterator const& collision, @@ -866,7 +964,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -893,8 +990,12 @@ struct V0PtInvMassPlots { if (!acceptEvent(collision)) { // Event Selection return; } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Event Split Numenator - rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + if (!collision.has_mcCollision()) { + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Event Split Numenator + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrack_as(); @@ -914,7 +1015,7 @@ struct V0PtInvMassPlots { // K0sh Signla Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched + if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nKaonHistograms; i++) { if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges @@ -948,7 +1049,7 @@ struct V0PtInvMassPlots { // Lambda Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched + if (lambdaSelection.dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nLambdaHistograms; i++) { if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { @@ -988,7 +1089,7 @@ struct V0PtInvMassPlots { // AntiLambda Signal Split Numerator End if (v0.has_mcParticle()) { auto v0mcParticle = v0.mcParticle(); - if (dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched + if (antilambdaSelection.dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { @@ -1027,7 +1128,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1048,11 +1148,13 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms + 1; i++) { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } + if (!acceptEvent(collision)) { // Event Selection return; } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events - rNchAnalysis.fill(HIST("hNchCentrality"), collision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), collision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), collision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrack_as(); @@ -1084,7 +1186,7 @@ struct V0PtInvMassPlots { if (antiLambdaAnalysis == true) { if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection for (int i = 0; i < nAntilambdaHistograms; i++) { - if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); } } @@ -1101,7 +1203,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1123,10 +1224,12 @@ struct V0PtInvMassPlots { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } - // if (!acceptEvent(collision)) { // Event Selection - // return; - // } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), collision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), collision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track @@ -1167,7 +1270,7 @@ struct V0PtInvMassPlots { } } void recMCProcessDerived(soa::Join::iterator const& collision, - // To add McCentFT0Ms + soa::Join const& /*mcCollisions*/, soa::Join const& V0s, DaughterTracksDerived const&) { @@ -1175,7 +1278,6 @@ struct V0PtInvMassPlots { pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); - pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); // Calculate number of histograms for each particle type int nKaonHistograms = pthistos::kaonPtBins.size() - 1; @@ -1196,10 +1298,16 @@ struct V0PtInvMassPlots { for (int i = 0; i < nAntilambdaHistograms + 1; i++) { antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); } - // if (!acceptEvent(collision)) { // Event Selection - // return; - // } - rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, collision.centFT0M()); // Event Split Numenator + + // For centrality estimation + const auto& mcCollision = collision.straMCCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Event Split Numenator + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut for (const auto& v0 : V0s) { // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) const auto& posDaughterTrack = v0.template posTrackExtra_as(); // Positive Daughter track @@ -1213,25 +1321,25 @@ struct V0PtInvMassPlots { // K0sh Signal Split Numerator Start for (int i = 0; i < nKaonHistograms; i++) { if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges for K0sh Splitting Numerator - pthistos::kaonSplit[i]->Fill(v0.mK0Short(), collision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator + pthistos::kaonSplit[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms for K0sh Splitting Numerator } } // K0sh SignaL Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched + if (k0shSelection.dotruthK0sh && (v0mcParticle.pdgCode() == kK0Short)) { // kzero matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nKaonHistograms; i++) { if (kaonptedgevalues[i] <= v0.ptMC() && v0.ptMC() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges - pthistos::kaonPt[i]->Fill(v0.mK0Short(), collision.centFT0M()); // filling the k0s namespace histograms + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms } } } if (!v0mcParticle.isPhysicalPrimary()) { auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hK0shPhiFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kPhi) { // Phi Mother Matched - rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hK0shFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1244,31 +1352,31 @@ struct V0PtInvMassPlots { // Lambda Signal Split Numerator Start for (int i = 0; i < nLambdaHistograms; i++) { if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { - pthistos::lambdaSplit[i]->Fill(v0.mLambda(), collision.centFT0M()); + pthistos::lambdaSplit[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } // Lambda Signal Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched + if (lambdaSelection.dotruthLambda && (v0mcParticle.pdgCode() == kLambda0)) { // lambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nLambdaHistograms; i++) { if (lambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < lambdaptedgevalues[i + 1]) { - pthistos::lambdaPt[i]->Fill(v0.mLambda(), collision.centFT0M()); + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiMinus) { // Xi Minus Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaXiMinusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaXiMinusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kXi0) { // Xi Zero Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kOmegaMinus) { // Omega Mother Matched - rFeeddownMatrices.fill(HIST("hLambdaOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hLambdaOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1281,31 +1389,31 @@ struct V0PtInvMassPlots { // AntiLambda Signal Split Numerator End for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { - pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); + pthistos::antilambdaSplit[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } // AntiLambda Signal Split Numerator End if (v0.has_v0MCCore()) { auto v0mcParticle = v0.v0MCCore_as(); - if (dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched + if (antilambdaSelection.dotruthAntiLambda && (v0mcParticle.pdgCode() == kLambda0Bar)) { // antilambda matched if (v0mcParticle.isPhysicalPrimary()) { for (int i = 0; i < nAntilambdaHistograms; i++) { if (antilambdaptedgevalues[i] <= v0.ptMC() && v0.ptMC() < antilambdaptedgevalues[i + 1]) { - pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), collision.centFT0M()); + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); } } } if (!v0mcParticle.isPhysicalPrimary()) { auto v0mother = v0.motherMCPart(); // Get mothers - rFeeddownMatrices.fill(HIST("hAntiLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); if (v0mother.pdgCode() == kXiPlusBar) { // Xi Plus Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaXiPlusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaXiPlusFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == -kXi0) { // Anti-Xi Zero Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaAntiXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaAntiXiZeroFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } if (v0mother.pdgCode() == kOmegaPlusBar) { // Anti-Omega (minus) Mother Matched - rFeeddownMatrices.fill(HIST("hAntiLambdaAntiOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), collision.centFT0M()); + rFeeddownMatrices.fill(HIST("hAntiLambdaAntiOmegaFeeddownMatrix"), v0mcParticle.ptMC(), std::hypot(v0mother.px(), v0mother.py()), mcCollision.centFT0M()); } } } @@ -1314,12 +1422,209 @@ struct V0PtInvMassPlots { } } } + // This ii the process for the MC generated derived data + void genMCProcessDerived( + soa::Join::iterator const& mcCollision, + // soa::SmallGroups> const& collision, + soa::SmallGroups> const& collisions, + aod::V0MCCores const& V0s, + aod::CascMCCores const& cascs, + DaughterTracksDerived const&) + { + // Event Efficiency, Event Split and V0 Signal Loss Corrections + rMCCorrections.fill(HIST("hNEvents_Corrections"), 0.5, mcCollision.centFT0M()); // All Events + if (std::abs(mcCollision.posZ()) > cutZVertex) { + return; + } + rMCCorrections.fill(HIST("hNEvents_Corrections"), 1.5, mcCollision.centFT0M()); // Event Efficiency Denominator + // rNchAnalysis.fill(HIST("hNchCentralityEtaHalfGen"), collisions.multNGlobalTracksPVetaHalf(), mcCollision.centFT0M()); // Nch vs Centrality EtaHalfCut + // Particles (of interest) Generated Pt Spectrum and Signal Loss Denominator Loop + for (const auto& v0 : V0s) { + if (v0.isPhysicalPrimary()) { + if (v0.pdgCode() == kK0Short) // kzero matched + { + if (std::abs(v0.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hK0shGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multMCNParticlesEta05()); // K0sh vs Centrality EtaHalfCut + } + } + if (v0.pdgCode() == kLambda0) // lambda matched + { + if (std::abs(v0.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hLambdaGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multNGlobalTracksPVetaHalf()); // Lambda vs Centrality EtaHalfCut + } + } + if (v0.pdgCode() == kLambda0Bar) // antilambda matched + { + if (std::abs(v0.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + // rMCCorrections.fill(HIST("hAntiLambdaGeneratedPtSpectrumMult"), v0.ptMC(), collisions.multNGlobalTracksPVetaHalf()); // Anti-Lambda vs Centrality EtaHalfCut + } + } + } + } + for (const auto& casc : cascs) { + if (casc.isPhysicalPrimary()) { + // Make rapidity cuts, from O2's documentation: rapidity (0, 1: Xi; 2, 3: Omega) + if (casc.pdgCode() == kXiMinus) // Xi Minus matched + { + if (std::abs(casc.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hXiMinusGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kXi0) // Xi Zero matched + { + if (std::abs(casc.rapidityMC(0)) < rapidityCut) { // Using the Xi mass assumption + rMCCorrections.fill(HIST("hXiZeroGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kOmegaMinus) // Omega matched + { + if (std::abs(casc.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hOmegaGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kXiPlusBar) // Xi Plus matched + { + if (std::abs(casc.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hXiPlusGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == -kXi0) // Anti-Xi Zero matched + { + if (std::abs(casc.rapidityMC(1)) < rapidityCut) { // Using the Xi mass assumption + rMCCorrections.fill(HIST("hAntiXiZeroGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + if (casc.pdgCode() == kOmegaPlusBar) // Anti-Omega matched + { + if (std::abs(casc.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiOmegaGeneratedPtSpectrum"), casc.ptMC(), mcCollision.centFT0M()); + } + } + } + } // End of MCParticle Loop For Signal Loss Denominator + + // Signal Loss Numenator Loop + for (const auto& collision : collisions) { + rMCCorrections.fill(HIST("hNEvents_Corrections"), 2.5, mcCollision.centFT0M()); // Number of Events Reconsctructed + if (!acceptEvent(collision)) { // Event Selection + continue; + } + rMCCorrections.fill(HIST("hNEvents_Corrections"), 3.5, mcCollision.centFT0M()); // Event Split Denomimator and Event Efficiency Numenator + for (const auto& v0 : V0s) { + if (!v0.isPhysicalPrimary()) { + continue; + } + if (v0.pdgCode() == kK0Short) // kzero matched + { + if (std::abs(v0.rapidityMC(0)) < rapidityCut) { + rMCCorrections.fill(HIST("hK0shGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + if (v0.pdgCode() == kLambda0) // lambda matched + { + if (std::abs(v0.rapidityMC(1)) < rapidityCut) { + rMCCorrections.fill(HIST("hLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + if (v0.pdgCode() == kLambda0Bar) // antilambda matched + { + if (std::abs(v0.rapidityMC(2)) < rapidityCut) { + rMCCorrections.fill(HIST("hAntiLambdaGeneratedRecoPtSpectrum"), v0.ptMC(), mcCollision.centFT0M()); + } + } + } + } + // End of Signal Loss Numenator Loop + } + // This is the process for Real Data MC Closure Test + void dataProcessClosureTest(soa::Join::iterator const& collision, + soa::Join const& /*mcCollisions*/, + aod::V0Datas const& V0s, + DaughterTracks const&) + { + // tokenise strings into individual values + pthistos::kaonPtBins = o2::utils::Str::tokenize(kzeroSettingPtBinsString, ','); + pthistos::lambdaPtBins = o2::utils::Str::tokenize(lambdaSettingPtBinsString, ','); + pthistos::antilambdaPtBins = o2::utils::Str::tokenize(antilambdaSettingPtBinsString, ','); + + // Calculate number of histograms for each particle type + int nKaonHistograms = pthistos::kaonPtBins.size() - 1; + int nLambdaHistograms = pthistos::lambdaPtBins.size() - 1; + int nAntilambdaHistograms = pthistos::antilambdaPtBins.size() - 1; + + // initialize and convert tokenized strings into vector of doubles for Pt Bin Edges + std::vector kaonptedgevalues(nKaonHistograms + 1); + std::vector lambdaptedgevalues(nLambdaHistograms + 1); + std::vector antilambdaptedgevalues(nAntilambdaHistograms + 1); + + for (int i = 0; i < nKaonHistograms + 1; i++) { + kaonptedgevalues[i] = std::stod(pthistos::kaonPtBins[i]); + } + for (int i = 0; i < nLambdaHistograms + 1; i++) { + lambdaptedgevalues[i] = std::stod(pthistos::lambdaPtBins[i]); + } + for (int i = 0; i < nAntilambdaHistograms + 1; i++) { + antilambdaptedgevalues[i] = std::stod(pthistos::antilambdaPtBins[i]); + } + + // For centrality estimation + const auto& mcCollision = collision.mcCollision_as>(); + + if (!acceptEvent(collision)) { // Event Selection + return; + } + rPtAnalysis.fill(HIST("hNRecEvents"), 0.5, mcCollision.centFT0M()); // Number of recorded events + rNchAnalysis.fill(HIST("hNchCentrality"), mcCollision.centFT0M(), collision.multNTracksGlobal()); // Nch vs Centrality + rNchAnalysis.fill(HIST("hNchCentralityEtaHalf"), mcCollision.centFT0M(), collision.multNGlobalTracksPVetaHalf()); // Nch vs Centrality EtaHalfCut + for (const auto& v0 : V0s) { + // Checking that the V0 is a true K0s/Lambdas/Antilambdas and then filling the parameter histograms and the invariant mass plots for different cuts (which are taken from namespace) + const auto& posDaughterTrack = v0.template posTrack_as(); + const auto& negDaughterTrack = v0.template negTrack_as(); + if (!acceptV0(v0, posDaughterTrack, negDaughterTrack, collision)) { // V0 Selection + continue; + } + // K0sh analysis + if (kzeroAnalysis == true) { + if (acceptK0sh(v0, posDaughterTrack, negDaughterTrack, collision)) { // K0sh Selection + for (int i = 0; i < nKaonHistograms; i++) { + if (kaonptedgevalues[i] <= v0.pt() && v0.pt() < kaonptedgevalues[i + 1]) { // finding v0s with pt within the range of our bin edges + pthistos::kaonPt[i]->Fill(v0.mK0Short(), mcCollision.centFT0M()); // filling the k0s namespace histograms + } + } + } + } + // Lambda analysis + if (lambdaAnalysis == true) { + if (acceptLambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // Lambda Selection + for (int i = 0; i < nLambdaHistograms; i++) { + if (lambdaptedgevalues[i] <= v0.pt() && v0.pt() < lambdaptedgevalues[i + 1]) { + pthistos::lambdaPt[i]->Fill(v0.mLambda(), mcCollision.centFT0M()); + } + } + } + } + // Anti-Lambda analysis + if (antiLambdaAnalysis == true) { + if (acceptAntilambda(v0, posDaughterTrack, negDaughterTrack, collision)) { // AntiLambda Selection + for (int i = 0; i < nAntilambdaHistograms; i++) { + if (antilambdaptedgevalues[i] <= v0.pt() && v0.pt() < antilambdaptedgevalues[i + 1]) { + pthistos::antilambdaPt[i]->Fill(v0.mAntiLambda(), mcCollision.centFT0M()); + } + } + } + } + } + } PROCESS_SWITCH(V0PtInvMassPlots, genMCProcess, "Process Run 3 MC Generated", false); PROCESS_SWITCH(V0PtInvMassPlots, recMCProcess, "Process Run 3 MC Reconstructed", false); - PROCESS_SWITCH(V0PtInvMassPlots, dataProcess, "Process Run 3 Data,", false); - // PROCESS_SWITCH(V0PtInvMassPlots, genMCProcessDerived, "Process Run 3 MC Generated", false); - PROCESS_SWITCH(V0PtInvMassPlots, recMCProcessDerived, "Process Run 3 MC Reconstructed", false); - PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerived, "Process Run 3 Data,", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcess, "Process Run 3 Data", false); + PROCESS_SWITCH(V0PtInvMassPlots, genMCProcessDerived, "Process Run 3 MC Generated Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, recMCProcessDerived, "Process Run 3 MC Reconstructed Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessDerived, "Process Run 3 Data Derived", false); + PROCESS_SWITCH(V0PtInvMassPlots, dataProcessClosureTest, "Process Run 3 Data Closure Test", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGLF/Utils/CMakeLists.txt b/PWGLF/Utils/CMakeLists.txt index fad54f18617..c0996bebe49 100644 --- a/PWGLF/Utils/CMakeLists.txt +++ b/PWGLF/Utils/CMakeLists.txt @@ -23,4 +23,59 @@ o2physics_add_library(collisionCutsGroup o2physics_target_root_dictionary(collisionCutsGroup HEADERS collisionCutsGroup.h - LINKDEF collisionCutsGroupLinkDef.h) \ No newline at end of file + LINKDEF collisionCutsGroupLinkDef.h) + +o2physics_add_library(LfDecay3bodyBuilderHelper + SOURCES decay3bodyBuilderHelper.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore KFParticle::KFParticle) + +o2physics_add_library(LfStrangenessBuilderHelper + SOURCES strangenessBuilderHelper.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore KFParticle::KFParticle) + +o2physics_add_library(LfStrangenessBuilderModule + SOURCES strangenessBuilderModule.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore KFParticle::KFParticle) + +o2physics_add_library(LfMlResponse + SOURCES CascadeMlResponse.cxx + ResonanceMlResponse.cxx + PUBLIC_LINK_LIBRARIES O2Physics::MLCore) + +# Precompiled header shared across the PWGLF strangeness workflows. Percentages +# are the share of the group's workflow translation units that include the header, +# measured over the tree. LFStrangenessPIDTables.h is deliberately left out: it +# reaches only 26% of them, and it includes LFStrangenessTables.h anyway. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(LfStrangenessPCH OBJECT strangenessPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET LfStrangenessPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(LfStrangenessPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(LfStrangenessPCH PRIVATE + # Framework baseline -- same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows precompile + # LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGLF/DataModel/LFStrangenessTables.h"]] # 100%, 2045 lines + ) +endif() diff --git a/PWGLF/Utils/CascadeMlResponse.cxx b/PWGLF/Utils/CascadeMlResponse.cxx new file mode 100644 index 00000000000..3be1d105e50 --- /dev/null +++ b/PWGLF/Utils/CascadeMlResponse.cxx @@ -0,0 +1,18 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CascadeMlResponse.cxx +/// \brief Helper file providing the compilation command for the CascadeMlResponse header. +/// +/// \author ALICE Collaboration + +// o2-linter: disable=name/workflow-file (not a workflow file) +#include "CascadeMlResponse.h" // IWYU pragma: keep diff --git a/PWGLF/Utils/CascadeMlResponse.h b/PWGLF/Utils/CascadeMlResponse.h new file mode 100644 index 00000000000..9a2ba4ddf54 --- /dev/null +++ b/PWGLF/Utils/CascadeMlResponse.h @@ -0,0 +1,121 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CascadeMlResponse.h +/// \brief Class to compute the ML response for Strangeness-analysis selections +/// \author Gianni Shigeru Setoue Liveraro Catalano , UNICAMP +/// \author Romain Schotter , Austrian Academy of Sciences +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences + +#ifndef PWGLF_UTILS_CASCADEMLRESPONSE_H_ +#define PWGLF_UTILS_CASCADEMLRESPONSE_H_ + +#include "Tools/ML/MlResponse.h" + +#include +#include + +namespace o2::analysis +{ +// list of input features that can be requested via the mlConfigurations.namesInputFeatures configurable +enum class InputFeaturesCasc : uint8_t { + cascradius = 0, + v0radius, + casccosPA, + v0cosPA, + dcapostopv, + dcanegtopv, + dcabachtopv, + dcacascdaughters, + dcaV0daughters, + dcav0topv, + bachBaryonCosPA, + bachBaryonDCAxyToPV +}; + +template +class CascadeMlResponse : public MlResponse +{ + public: + CascadeMlResponse() = default; + ~CascadeMlResponse() override = default; + + template + std::vector getInputFeatures(TCascade const& casc, TCollision const& coll) + { + std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); + + for (const auto& idx : MlResponse::mCachedIndices) { + switch (static_cast(idx)) { + case InputFeaturesCasc::cascradius: + inputFeatures.emplace_back(casc.cascradius()); + break; + case InputFeaturesCasc::v0radius: + inputFeatures.emplace_back(casc.v0radius()); + break; + case InputFeaturesCasc::casccosPA: + inputFeatures.emplace_back(casc.casccosPA(coll.posX(), coll.posY(), coll.posZ())); + break; + case InputFeaturesCasc::v0cosPA: + inputFeatures.emplace_back(casc.v0cosPA(coll.posX(), coll.posY(), coll.posZ())); + break; + case InputFeaturesCasc::dcapostopv: + inputFeatures.emplace_back(casc.dcapostopv()); + break; + case InputFeaturesCasc::dcanegtopv: + inputFeatures.emplace_back(casc.dcanegtopv()); + break; + case InputFeaturesCasc::dcabachtopv: + inputFeatures.emplace_back(casc.dcabachtopv()); + break; + case InputFeaturesCasc::dcacascdaughters: + inputFeatures.emplace_back(casc.dcacascdaughters()); + break; + case InputFeaturesCasc::dcaV0daughters: + inputFeatures.emplace_back(casc.dcaV0daughters()); + break; + case InputFeaturesCasc::dcav0topv: + inputFeatures.emplace_back(casc.dcav0topv(coll.posX(), coll.posY(), coll.posZ())); + break; + case InputFeaturesCasc::bachBaryonCosPA: + inputFeatures.emplace_back(casc.bachBaryonCosPA()); + break; + case InputFeaturesCasc::bachBaryonDCAxyToPV: + inputFeatures.emplace_back(casc.bachBaryonDCAxyToPV()); + break; + } + } + return inputFeatures; + } + + protected: + void setAvailableInputFeatures() override + { + MlResponse::mAvailableInputFeatures = { + {"cascradius", static_cast(InputFeaturesCasc::cascradius)}, + {"v0radius", static_cast(InputFeaturesCasc::v0radius)}, + {"casccosPA", static_cast(InputFeaturesCasc::casccosPA)}, + {"v0cosPA", static_cast(InputFeaturesCasc::v0cosPA)}, + {"dcapostopv", static_cast(InputFeaturesCasc::dcapostopv)}, + {"dcanegtopv", static_cast(InputFeaturesCasc::dcanegtopv)}, + {"dcabachtopv", static_cast(InputFeaturesCasc::dcabachtopv)}, + {"dcacascdaughters", static_cast(InputFeaturesCasc::dcacascdaughters)}, + {"dcaV0daughters", static_cast(InputFeaturesCasc::dcaV0daughters)}, + {"dcav0topv", static_cast(InputFeaturesCasc::dcav0topv)}, + {"bachBaryonCosPA", static_cast(InputFeaturesCasc::bachBaryonCosPA)}, + {"bachBaryonDCAxyToPV", static_cast(InputFeaturesCasc::bachBaryonDCAxyToPV)}}; + } +}; + +} // namespace o2::analysis + +#endif // PWGLF_UTILS_CASCADEMLRESPONSE_H_ diff --git a/PWGLF/Utils/ResonanceMlResponse.cxx b/PWGLF/Utils/ResonanceMlResponse.cxx new file mode 100644 index 00000000000..71dc6dc9d7b --- /dev/null +++ b/PWGLF/Utils/ResonanceMlResponse.cxx @@ -0,0 +1,18 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ResonanceMlResponse.cxx +/// \brief Helper file providing the compilation command for the ResonanceMlResponse header. +/// +/// \author ALICE Collaboration + +// o2-linter: disable=name/workflow-file (not a workflow file) +#include "ResonanceMlResponse.h" // IWYU pragma: keep diff --git a/PWGLF/Utils/ResonanceMlResponse.h b/PWGLF/Utils/ResonanceMlResponse.h new file mode 100644 index 00000000000..e3051ef81c6 --- /dev/null +++ b/PWGLF/Utils/ResonanceMlResponse.h @@ -0,0 +1,328 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ResonanceMlResponse.h +/// \brief Class to compute the ML response for Lambda + photon resonance selections (Sigma0, Lambda(1520)), +/// either from sigma0builder candidates or from a Lambda and a photon V0 of the strangeness derived data +/// \author Oussama Benchikhi + +#ifndef PWGLF_UTILS_RESONANCEMLRESPONSE_H_ +#define PWGLF_UTILS_RESONANCEMLRESPONSE_H_ + +#include "PWGLF/DataModel/LFStrangenessPIDTables.h" +#include "PWGLF/DataModel/LFStrangenessTables.h" + +#include "Tools/ML/MlResponse.h" + +#include + +#include +#include + +namespace o2::analysis +{ +// list of input features that can be requested via the namesInputFeatures configurable (sigma0builder getter names) +enum class InputFeaturesResonance : uint8_t { + // Lambda + lambdaQt = 0, + lambdaAlpha, + lambdaRadius, + lambdaCosPA, + lambdaDCADau, + lambdaDCANegPV, + lambdaDCAPosPV, + lambdaPosEta, + lambdaNegEta, + lambdaPosPrTPCNSigma, + lambdaPosPiTPCNSigma, + lambdaNegPrTPCNSigma, + lambdaNegPiTPCNSigma, + // Photon + photonQt, + photonAlpha, + photonCosPA, + photonDCADau, + photonDCANegPV, + photonDCAPosPV, + photonRadius, + photonZconv, + photonPsiPair, + photonPosEta, + photonNegEta, + photonPosTPCNSigmaEl, + photonNegTPCNSigmaEl, + // Photon-Lambda pair + opAngle, + apAlpha, + apQt +}; + +template +class ResonanceMlResponse : public MlResponse +{ + public: + ResonanceMlResponse() = default; + ~ResonanceMlResponse() override = default; + + /// Input features of a photon-Lambda pair + template , typename TLambda, typename TPhoton> + std::vector getInputFeatures(TLambda const& lambda, TPhoton const& photon, float opAngle, float apAlpha, float apQt) + { + // sigma0builder candidates carry prefixed columns (lambdaQt(), photonQt(), ...), derived V0s the plain V0 getters + constexpr bool LambdaFromSigma0 = requires(TLambda const& cand) { cand.lambdaQt(); }; + constexpr bool PhotonFromSigma0 = requires(TPhoton const& cand) { cand.photonQt(); }; + + std::vector inputFeatures; + inputFeatures.reserve(MlResponse::mCachedIndices.size()); + + for (const auto& idx : MlResponse::mCachedIndices) { + switch (static_cast(idx)) { + // Lambda + case InputFeaturesResonance::lambdaQt: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaQt()); + } else { + inputFeatures.emplace_back(lambda.qtarm()); + } + break; + case InputFeaturesResonance::lambdaAlpha: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaAlpha()); + } else { + inputFeatures.emplace_back(lambda.alpha()); + } + break; + case InputFeaturesResonance::lambdaRadius: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaRadius()); + } else { + inputFeatures.emplace_back(lambda.v0radius()); + } + break; + case InputFeaturesResonance::lambdaCosPA: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaCosPA()); + } else { + inputFeatures.emplace_back(lambda.v0cosPA()); + } + break; + case InputFeaturesResonance::lambdaDCADau: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCADau()); + } else { + inputFeatures.emplace_back(lambda.dcaV0daughters()); + } + break; + case InputFeaturesResonance::lambdaDCANegPV: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCANegPV()); + } else { + inputFeatures.emplace_back(lambda.dcanegtopv()); + } + break; + case InputFeaturesResonance::lambdaDCAPosPV: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaDCAPosPV()); + } else { + inputFeatures.emplace_back(lambda.dcapostopv()); + } + break; + case InputFeaturesResonance::lambdaPosEta: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosEta()); + } else { + inputFeatures.emplace_back(lambda.positiveeta()); + } + break; + case InputFeaturesResonance::lambdaNegEta: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegEta()); + } else { + inputFeatures.emplace_back(lambda.negativeeta()); + } + break; + case InputFeaturesResonance::lambdaPosPrTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosPrTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template posTrackExtra_as().tpcNSigmaPr()); + } + break; + case InputFeaturesResonance::lambdaPosPiTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaPosPiTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template posTrackExtra_as().tpcNSigmaPi()); + } + break; + case InputFeaturesResonance::lambdaNegPrTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegPrTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template negTrackExtra_as().tpcNSigmaPr()); + } + break; + case InputFeaturesResonance::lambdaNegPiTPCNSigma: + if constexpr (LambdaFromSigma0) { + inputFeatures.emplace_back(lambda.lambdaNegPiTPCNSigma()); + } else { + inputFeatures.emplace_back(lambda.template negTrackExtra_as().tpcNSigmaPi()); + } + break; + // Photon + case InputFeaturesResonance::photonQt: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonQt()); + } else { + inputFeatures.emplace_back(photon.qtarm()); + } + break; + case InputFeaturesResonance::photonAlpha: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonAlpha()); + } else { + inputFeatures.emplace_back(photon.alpha()); + } + break; + case InputFeaturesResonance::photonCosPA: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonCosPA()); + } else { + inputFeatures.emplace_back(photon.v0cosPA()); + } + break; + case InputFeaturesResonance::photonDCADau: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCADau()); + } else { + inputFeatures.emplace_back(photon.dcaV0daughters()); + } + break; + case InputFeaturesResonance::photonDCANegPV: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCANegPV()); + } else { + inputFeatures.emplace_back(photon.dcanegtopv()); + } + break; + case InputFeaturesResonance::photonDCAPosPV: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonDCAPosPV()); + } else { + inputFeatures.emplace_back(photon.dcapostopv()); + } + break; + case InputFeaturesResonance::photonRadius: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonRadius()); + } else { + inputFeatures.emplace_back(photon.v0radius()); + } + break; + case InputFeaturesResonance::photonZconv: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonZconv()); + } else { + inputFeatures.emplace_back(photon.z()); + } + break; + case InputFeaturesResonance::photonPsiPair: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPsiPair()); + } else { + inputFeatures.emplace_back(photon.psipair()); + } + break; + case InputFeaturesResonance::photonPosEta: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPosEta()); + } else { + inputFeatures.emplace_back(photon.positiveeta()); + } + break; + case InputFeaturesResonance::photonNegEta: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonNegEta()); + } else { + inputFeatures.emplace_back(photon.negativeeta()); + } + break; + case InputFeaturesResonance::photonPosTPCNSigmaEl: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonPosTPCNSigmaEl()); + } else { + inputFeatures.emplace_back(photon.template posTrackExtra_as().tpcNSigmaEl()); + } + break; + case InputFeaturesResonance::photonNegTPCNSigmaEl: + if constexpr (PhotonFromSigma0) { + inputFeatures.emplace_back(photon.photonNegTPCNSigmaEl()); + } else { + inputFeatures.emplace_back(photon.template negTrackExtra_as().tpcNSigmaEl()); + } + break; + // Photon-Lambda pair + case InputFeaturesResonance::opAngle: + inputFeatures.emplace_back(opAngle); + break; + + case InputFeaturesResonance::apAlpha: + inputFeatures.emplace_back(apAlpha); + break; + + case InputFeaturesResonance::apQt: + inputFeatures.emplace_back(apQt); + break; + } + } + return inputFeatures; + } + + protected: + /// Method to fill the map of available input features + void setAvailableInputFeatures() override + { + MlResponse::mAvailableInputFeatures = { + {"lambdaQt", static_cast(InputFeaturesResonance::lambdaQt)}, + {"lambdaAlpha", static_cast(InputFeaturesResonance::lambdaAlpha)}, + {"lambdaRadius", static_cast(InputFeaturesResonance::lambdaRadius)}, + {"lambdaCosPA", static_cast(InputFeaturesResonance::lambdaCosPA)}, + {"lambdaDCADau", static_cast(InputFeaturesResonance::lambdaDCADau)}, + {"lambdaDCANegPV", static_cast(InputFeaturesResonance::lambdaDCANegPV)}, + {"lambdaDCAPosPV", static_cast(InputFeaturesResonance::lambdaDCAPosPV)}, + {"lambdaPosEta", static_cast(InputFeaturesResonance::lambdaPosEta)}, + {"lambdaNegEta", static_cast(InputFeaturesResonance::lambdaNegEta)}, + {"lambdaPosPrTPCNSigma", static_cast(InputFeaturesResonance::lambdaPosPrTPCNSigma)}, + {"lambdaPosPiTPCNSigma", static_cast(InputFeaturesResonance::lambdaPosPiTPCNSigma)}, + {"lambdaNegPrTPCNSigma", static_cast(InputFeaturesResonance::lambdaNegPrTPCNSigma)}, + {"lambdaNegPiTPCNSigma", static_cast(InputFeaturesResonance::lambdaNegPiTPCNSigma)}, + {"photonQt", static_cast(InputFeaturesResonance::photonQt)}, + {"photonAlpha", static_cast(InputFeaturesResonance::photonAlpha)}, + {"photonCosPA", static_cast(InputFeaturesResonance::photonCosPA)}, + {"photonDCADau", static_cast(InputFeaturesResonance::photonDCADau)}, + {"photonDCANegPV", static_cast(InputFeaturesResonance::photonDCANegPV)}, + {"photonDCAPosPV", static_cast(InputFeaturesResonance::photonDCAPosPV)}, + {"photonRadius", static_cast(InputFeaturesResonance::photonRadius)}, + {"photonZconv", static_cast(InputFeaturesResonance::photonZconv)}, + {"photonPsiPair", static_cast(InputFeaturesResonance::photonPsiPair)}, + {"photonPosEta", static_cast(InputFeaturesResonance::photonPosEta)}, + {"photonNegEta", static_cast(InputFeaturesResonance::photonNegEta)}, + {"photonPosTPCNSigmaEl", static_cast(InputFeaturesResonance::photonPosTPCNSigmaEl)}, + {"photonNegTPCNSigmaEl", static_cast(InputFeaturesResonance::photonNegTPCNSigmaEl)}, + {"opAngle", static_cast(InputFeaturesResonance::opAngle)}, + {"apAlpha", static_cast(InputFeaturesResonance::apAlpha)}, + {"apQt", static_cast(InputFeaturesResonance::apQt)}}; + } +}; + +} // namespace o2::analysis + +#endif // PWGLF_UTILS_RESONANCEMLRESPONSE_H_ diff --git a/PWGLF/Utils/collisionCutsGroupLinkDef.h b/PWGLF/Utils/collisionCutsGroupLinkDef.h index e327556fbd2..a6aa000a66e 100644 --- a/PWGLF/Utils/collisionCutsGroupLinkDef.h +++ b/PWGLF/Utils/collisionCutsGroupLinkDef.h @@ -17,8 +17,7 @@ /// /// \author Bong-Hwi Lim -#ifndef PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ -#define PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -30,5 +29,3 @@ #pragma link C++ class std::vector < int> + ; #pragma link C++ class std::vector < float> + ; #pragma link C++ class std::vector < bool> + ; - -#endif // PWGLF_UTILS_COLLISIONCUTSGROUPLINKDEF_H_ diff --git a/PWGLF/Utils/decay3bodyBuilderHelper.cxx b/PWGLF/Utils/decay3bodyBuilderHelper.cxx new file mode 100644 index 00000000000..dd993cf412a --- /dev/null +++ b/PWGLF/Utils/decay3bodyBuilderHelper.cxx @@ -0,0 +1,21 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file decay3bodyBuilderHelper.cxx +/// \brief Helper file providing the compilation command for the decay3bodyBuilderHelper header. +/// The module implementation is provided in the corresponding header and maintained by Carolina Anna Jauch; +/// this file only provides the compilation unit. +/// +/// \author Carolina Anna Jauch , Darmstadt GSI +/// \author Vít Kučera , Inha University + +// o2-linter: disable=name/workflow-file (not a workflow file) +#include "decay3bodyBuilderHelper.h" // IWYU pragma: keep diff --git a/PWGLF/Utils/decay3bodyBuilderHelper.h b/PWGLF/Utils/decay3bodyBuilderHelper.h index 347b53d37cb..0eada168320 100644 --- a/PWGLF/Utils/decay3bodyBuilderHelper.h +++ b/PWGLF/Utils/decay3bodyBuilderHelper.h @@ -16,31 +16,28 @@ #define HomogeneousField #endif +#include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" #include "Tools/KFparticle/KFUtilities.h" #include #include +#include #include #include #include +#include #include #include -#include -#include #include -#include -#include #include #include #include #include -namespace o2 -{ -namespace pwglf +namespace o2::pwglf { //_______________________________________________________________________ @@ -54,40 +51,50 @@ struct decay3bodyCandidate { int deuteronID = -1; // daughter properties - std::array momProton = {0.0f, 0.0f, 0.0f}; - std::array momPion = {0.0f, 0.0f, 0.0f}; - std::array momDeuteron = {0.0f, 0.0f, 0.0f}; - std::array posProton = {0.0f, 0.0f, 0.0f}; - std::array posPion = {0.0f, 0.0f, 0.0f}; - std::array posDeuteron = {0.0f, 0.0f, 0.0f}; - std::array trackDCAxyToPV = {0.0f, 0.0f, 0.0f}; // 0 - proton, 1 - pion, 2 - deuteron - std::array trackDCAToPV = {0.0f, 0.0f, 0.0f}; // 0 - proton, 1 - pion, 2 - deuteron - std::array trackDCAxyToPVprop = {0.0f, 0.0f, 0.0f}; // 0 - proton, 1 - pion, 2 - deuteron - std::array trackDCAToPVprop = {0.0f, 0.0f, 0.0f}; // 0 - proton, 1 - pion, 2 - deuteron - std::array tpcNsigma = {0.0f, 0.0f, 0.0f, 0.0f}; // 0 - proton, 1 - pion, 2 - deuteron, 3 - bach with pion hyp - double tofNsigmaDeuteron = 0.0f; - std::array averageITSClSize = {0.0f, 0.0f, 0.0f}; // 0 - proton, 1 - pion, 2 - deuteron - std::array tpcNCl = {0.0f, 0.0f, 0.0f}; // 0 - proton, 1 - pion, 2 - deuteron - int pidForTrackingDeuteron = 0; + std::array momProton{}; + std::array momPion{}; + std::array momDeuteron{}; + std::array tpcInnerMomProton{}; + std::array tpcInnerMomPion{}; + std::array tpcInnerMomDeuteron{}; + std::array posProton{}; + std::array posPion{}; + std::array posDeuteron{}; + float xProton{}; + float xPion{}; + float xDeuteron{}; + std::array trackDCAxyToPV{}; // 0 - proton, 1 - pion, 2 - deuteron + std::array trackDCAToPV{}; // 0 - proton, 1 - pion, 2 - deuteron + std::array trackDCAxyToPVprop{}; // 0 - proton, 1 - pion, 2 - deuteron + std::array trackDCAToPVprop{}; // 0 - proton, 1 - pion, 2 - deuteron + std::array tpcNsigma{}; // 0 - proton, 1 - pion, 2 - deuteron, 3 - bach with pion hyp + std::array tpcSignal{}; // 0 - proton, 1 - pion, 2 - deuteron + double tofNsigmaDeuteron{}; + std::array averageITSClSize{}; // 0 - proton, 1 - pion, 2 - deuteron + std::array tpcNCl{}; // 0 - proton, 1 - pion, 2 - deuteron + int pidForTrackingDeuteron{}; // vertex properties - float mass; - float massV0; - int sign; - float momentum[3]; - float position[3]; - float chi2 = 0.0f; - float trackedClSize = 0.0f; - float cosPA = 0.0f; // cosine of pointing angle - float ctau = 0.0f; // ctau of the candidate - float daughterDCAtoSVaverage = 0.0f; // average of quadratic sum of daughter DCAs to SV - std::array daughterDCAtoSV = {0.0f, 0.0f, 0.0f}; // 0 - pos, 1 - neg, 2 - bach + float mass{}; + float massV0{}; + int sign{}; + std::array momentum{}; + std::array position{}; + float chi2{}; + float trackedClSize{}; + float cosPA{}; // cosine of pointing angle + float ctau{}; // ctau of the candidate + float daughterDCAtoSVaverage{}; // average of quadratic sum of daughter DCAs to SV + std::array daughterDCAtoSV{}; // 0 - pos, 1 - neg, 2 - bach // covariance matrix - float covProton[21] = {0.0f}; - float covPion[21] = {0.0f}; - float covDeuteron[21] = {0.0f}; - float covariance[21] = {0.0f}; + std::array covProton{}; + std::array covPion{}; + std::array covDeuteron{}; + std::array covariance{}; + + // strangeness tracking + std::array itsTrackDCAToSV{}; }; //_______________________________________________________________________ @@ -160,7 +167,7 @@ class decay3bodyBuilderHelper float maxCtau; float minCosPA; float maxChi2; - } decay3bodyselections; + } decay3bodyselections{}; // SVertexer selection criteria struct { @@ -174,7 +181,7 @@ class decay3bodyBuilderHelper float maxTgl3Body; float maxDCAXY3Body; float maxDCAZ3Body; - } svertexerselections; + } svertexerselections{}; //_______________________________________________________________________ // build Decay3body from three tracks, including V0 building. @@ -212,6 +219,10 @@ class decay3bodyBuilderHelper decay3body.pionID = trackPion.globalIndex(); decay3body.deuteronID = trackDeuteron.globalIndex(); + decay3body.xProton = trackProton.x(); + decay3body.xPion = trackPion.x(); + decay3body.xDeuteron = trackDeuteron.x(); + //_______________________________________________________________________ // track selections if (useSelections) { @@ -288,8 +299,8 @@ class decay3bodyBuilderHelper //_______________________________________________________________________ // daughter track DCA to PV associated with decay3body --> computed with KFParticle - float pvXY[2] = {pvX, pvY}; - float pv[3] = {pvX, pvY, pvZ}; + std::array pvXY = {pvX, pvY}; + std::array pv = {pvX, pvY, pvZ}; auto trackParCovProtonCopy = trackParCovProton; auto trackParCovPionCopy = trackParCovPion; auto trackParCovDeuteronCopy = trackParCovDeuteron; @@ -298,14 +309,14 @@ class decay3bodyBuilderHelper KFParticle kfdeuteron = createKFParticleFromTrackParCov(trackParCovDeuteronCopy, trackDeuteron.sign(), constants::physics::MassDeuteron); // proton DCA to PV - decay3body.trackDCAxyToPV[0] = kfproton.GetDistanceFromVertexXY(pvXY); - decay3body.trackDCAToPV[0] = kfproton.GetDistanceFromVertex(pv); + decay3body.trackDCAxyToPV[0] = kfproton.GetDistanceFromVertexXY(pvXY.data()); + decay3body.trackDCAToPV[0] = kfproton.GetDistanceFromVertex(pv.data()); // pion DCA to PV - decay3body.trackDCAxyToPV[1] = kfpion.GetDistanceFromVertexXY(pvXY); - decay3body.trackDCAToPV[1] = kfpion.GetDistanceFromVertex(pv); + decay3body.trackDCAxyToPV[1] = kfpion.GetDistanceFromVertexXY(pvXY.data()); + decay3body.trackDCAToPV[1] = kfpion.GetDistanceFromVertex(pv.data()); // deuteron DCA to PV - decay3body.trackDCAxyToPV[2] = kfdeuteron.GetDistanceFromVertexXY(pvXY); - decay3body.trackDCAToPV[2] = kfdeuteron.GetDistanceFromVertex(pv); + decay3body.trackDCAxyToPV[2] = kfdeuteron.GetDistanceFromVertexXY(pvXY.data()); + decay3body.trackDCAToPV[2] = kfdeuteron.GetDistanceFromVertex(pv.data()); // selection if (useSelections) { if (decay3body.trackDCAToPV[0] < decay3bodyselections.minDCAProtonToPV) { @@ -431,14 +442,14 @@ class decay3bodyBuilderHelper } // rapidity - float rapidity = RecoDecay::y(std::array{decay3body.momentum[0], decay3body.momentum[1], decay3body.momentum[2]}, o2::constants::physics::MassHyperTriton); + float rapidity = RecoDecay::y(decay3body.momentum, o2::constants::physics::MassHyperTriton); if (std::fabs(rapidity) > decay3bodyselections.maxRapidity) { decay3body = {}; return false; } // pT - float pT = RecoDecay::pt(std::array{decay3body.momentum[0], decay3body.momentum[1], decay3body.momentum[2]}); + float pT = RecoDecay::pt(decay3body.momentum); if (pT < decay3bodyselections.minPt || pT > decay3bodyselections.maxPt) { decay3body = {}; return false; @@ -458,7 +469,7 @@ class decay3bodyBuilderHelper } // pointing angle - float cpa = RecoDecay::cpa(std::array{pvX, pvY, pvZ}, std::array{decay3body.position[0], decay3body.position[1], decay3body.position[2]}, std::array{decay3body.momentum[0], decay3body.momentum[1], decay3body.momentum[2]}); + float cpa = RecoDecay::cpa(std::array{pvX, pvY, pvZ}, decay3body.position, decay3body.momentum); if (useSelections) { if (cpa < decay3bodyselections.minCosPA) { decay3body = {}; @@ -491,6 +502,9 @@ class decay3bodyBuilderHelper decay3body.tpcNsigma[1] = trackPion.tpcNSigmaPi(); decay3body.tpcNsigma[2] = trackDeuteron.tpcNSigmaDe(); decay3body.tpcNsigma[3] = trackDeuteron.tpcNSigmaPi(); + decay3body.tpcSignal[0] = trackProton.tpcSignal(); + decay3body.tpcSignal[1] = trackPion.tpcSignal(); + decay3body.tpcSignal[2] = trackDeuteron.tpcSignal(); // recalculated bachelor TOF PID decay3body.tofNsigmaDeuteron = tofNsigmaDeuteron; @@ -548,8 +562,7 @@ class decay3bodyBuilderHelper auto trackParCovDeuteron = getTrackParCov(trackDeuteron); // initialise KF primary vertex - KFVertex kfpVertex = createKFPVertexFromCollision(collision); - KFParticle kfpv(kfpVertex); + KFParticle kfpv(createKFPVertexFromCollision(collision)); // create KFParticle objects KFParticle kfpProton, kfpPion, kfpDeuteron; @@ -599,9 +612,9 @@ class decay3bodyBuilderHelper decay3body.sign = KFH3L.GetQ() / std::abs(KFH3L.GetQ()); // transport all daughter tracks to hypertriton vertex - kfpProton.TransportToPoint(decay3body.position); - kfpPion.TransportToPoint(decay3body.position); - kfpDeuteron.TransportToPoint(decay3body.position); + kfpProton.TransportToPoint(decay3body.position.data()); + kfpPion.TransportToPoint(decay3body.position.data()); + kfpDeuteron.TransportToPoint(decay3body.position.data()); // daughter positions decay3body.posProton[0] = kfpProton.GetX(); @@ -631,7 +644,8 @@ class decay3bodyBuilderHelper decay3body.mass = mass; // V0 mass - float massV0, massV0Err; + float massV0{}; + float massV0Err{}; KFV0.GetMass(massV0, massV0Err); decay3body.massV0 = massV0; @@ -641,10 +655,8 @@ class decay3bodyBuilderHelper // caluclate covariance matrices if (calculateCovariance) { // candidate covariance matrix - std::array covKF; for (int i = 0; i < 21; i++) { // get covariance matrix elements (lower triangle) - covKF[i] = KFH3L.GetCovariance(i); - decay3body.covariance[i] = covKF[i]; + decay3body.covariance[i] = KFH3L.GetCovariance(i); } // daughter track covariance matrices for (int i = 0; i < 21; i++) { // get covariance matrix elements (lower triangle) @@ -653,8 +665,6 @@ class decay3bodyBuilderHelper decay3body.covDeuteron[i] = kfpDeuteron.GetCovariance(i); } } - - return; } //_______________________________________________________________________ @@ -728,21 +738,13 @@ class decay3bodyBuilderHelper decay3body.covariance[4] = covVtxV(2, 1); decay3body.covariance[5] = covVtxV(2, 2); // daughter covariance matrices - std::array covTproton = {0.}; - std::array covTpion = {0.}; - std::array covTdeuteron = {0.}; - propagatedTrackProton.getCovXYZPxPyPzGlo(covTproton); - propagatedTrackPion.getCovXYZPxPyPzGlo(covTpion); - propagatedTrackDeuteron.getCovXYZPxPyPzGlo(covTdeuteron); - for (int i = 0; i < 21; i++) { - decay3body.covProton[i] = covTproton[i]; - decay3body.covPion[i] = covTpion[i]; - decay3body.covDeuteron[i] = covTdeuteron[i]; - } + propagatedTrackProton.getCovXYZPxPyPzGlo(decay3body.covProton); + propagatedTrackPion.getCovXYZPxPyPzGlo(decay3body.covPion); + propagatedTrackDeuteron.getCovXYZPxPyPzGlo(decay3body.covDeuteron); // candidate momentum covairance matrix constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component for (int i = 0; i < 6; i++) { - decay3body.covariance[MomInd[i]] = covTproton[MomInd[i]] + covTpion[MomInd[i]] + covTdeuteron[MomInd[i]]; + decay3body.covariance[MomInd[i]] = decay3body.covProton[MomInd[i]] + decay3body.covPion[MomInd[i]] + decay3body.covDeuteron[MomInd[i]]; } /// WARNING: position-momentum covariances are not calculated in the DCAFitter - remain zero } @@ -876,8 +878,6 @@ class decay3bodyBuilderHelper std::abs(dca.getY()) > svertexerselections.maxDCAXY3Body || std::abs(dca.getZ()) > svertexerselections.maxDCAZ3Body) { return; } - - return; } private: @@ -888,7 +888,6 @@ class decay3bodyBuilderHelper } }; -} // namespace pwglf -} // namespace o2 +} // namespace o2::pwglf #endif // PWGLF_UTILS_DECAY3BODYBUILDERHELPER_H_ diff --git a/PWGLF/Utils/doubleOmegaBuilderHelper.h b/PWGLF/Utils/doubleOmegaBuilderHelper.h new file mode 100644 index 00000000000..ac071658516 --- /dev/null +++ b/PWGLF/Utils/doubleOmegaBuilderHelper.h @@ -0,0 +1,628 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file doubleOmegaBuilderHelper.h +/// \brief Building blocks of the double-Omega (Omega + Lambda + kaon) reconstruction, shared by the +/// candidate builder (derived data producer) and the tree creator. + +#ifndef PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ +#define PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ + +#include "Common/Core/RecoDecay.h" +#include "Common/Core/trackUtilities.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace o2::pwglf::doubleomega +{ + +struct DoubleOmegaCandidate { + float pt = -999.f; + float eta = -999.f; + float phi = -999.f; + float x = -999.f; + float y = -999.f; + float z = -999.f; + float pvX = -999.f; + float pvY = -999.f; + float pvZ = -999.f; + float cosPAOmega = -999.f; + float cosPADirectLambda = -999.f; + float cosPADoubleOmega = -999.f; + float cosPAOmegaToPV = -999.f; + float cosPADirectLambdaToPV = -999.f; + float dcaXYOmegaToPV = -999.f; + float dcaXYDirectLambdaToPV = -999.f; + float dcaXYDirectKaonToPV = -999.f; + float dcaXYOmegaKaonToPV = -999.f; + float dcaXYOmegaProtonToPV = -999.f; + float dcaXYOmegaPionToPV = -999.f; + float dcaXYDirectProtonToPV = -999.f; + float dcaXYDirectPionToPV = -999.f; + float tpcNSigmaDirectKaon = -999.f; + float tofNSigmaDirectKaon = -999.f; + uint32_t daughterDetectorMap = 0; + float mass = -999.f; + float massOmega = -999.f; + float massXi = -999.f; + int8_t sign = 0; +}; + +struct LambdaCandidate { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float mass = -999.f; +}; + +struct BuiltLambdaCandidate { + LambdaCandidate candidate; + o2::track::TrackParCov parentTrack; + std::array decayVertex{}; + int64_t posTrackId = -1; + int64_t negTrackId = -1; + uint32_t daughterDetectorMap = 0; // Proton, pion; three detector bits each. + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; + float dcaDaughters = -999.f; +}; + +struct BuiltOmegaCandidate { + float px = 0.f; + float py = 0.f; + float pz = 0.f; + float x = 0.f; + float y = 0.f; + float z = 0.f; + float massOmega = -999.f; + float massXi = -999.f; + int8_t sign = 0; + int64_t posTrackId = -1; + int64_t negTrackId = -1; + int64_t bachelorId = -1; + o2::track::TrackParCov parentTrack; + uint32_t daughterDetectorMap = 0; // Kaon, proton, pion; three detector bits each. + float dcaXYKaonToPV = -999.f; + float dcaXYProtonToPV = -999.f; + float dcaXYPionToPV = -999.f; + float dcaDaughters = -999.f; +}; + +struct BuiltKaonCandidate { + o2::track::TrackParCov track; + int64_t trackId = -1; + int8_t sign = 0; + float tpcNSigma = -999.f; // -999 when TPC is unavailable. + float tofNSigma = -999.f; // -999 when TOF is unavailable. + uint32_t detectorMap = 0; +}; + +// Omega, Lambda and kaon candidates of one collision, built once and paired afterwards. +// Lambdas and kaons are stored per charge of the Omega they can be paired with (see chargeIndex). +struct CollisionCandidates { + std::array primaryVertex{}; + std::vector omegas; + std::array, 2> lambdas; + std::array, 2> kaons; +}; + +struct DoubleOmegaSelections { + float etaMax = 0.9f; + float minNITSClus = 0.f; + float minNTPCClus = 80.f; + float minNCrossedRows = 100.f; + float maxNSharedTPCClus = 5.f; + float nSigmaTPCCut = 3.f; + float dcaBachToPV = 0.05f; + float dcaKaonToPV = 0.05f; + float dcaOmegaToPV = 0.f; + float dcaDirectLambdaToPV = 0.f; + float dcaV0DauToPV = 0.05f; + float dcaV0Bach = 1.f; + float dcaLambdaDaughters = 1.f; + float mOmegaWindow = 0.01f; + float mLambdaWindow = 0.01f; + float maxDoubleOmegaMass = 3.6f; + float minCosPAOmega = -1.f; + float minCosPADirectLambda = -1.f; + float minCosPADoubleOmega = -1.f; + float minDoubleOmegaDecayRadius = 1.f; +}; + +class DoubleOmegaBuilderHelper +{ + public: + DoubleOmegaSelections selections; + o2::base::Propagator::MatCorrType matCorr = o2::base::Propagator::MatCorrType::USEMatCorrLUT; + o2::vertexing::DCAFitterN<2> fitter; + o2::vertexing::DCAFitterN<3> fitter3Body; + + void initFitters() + { + configureFitter(fitter); + configureFitter(fitter3Body); + } + + void setBz(float bz) + { + fitter.setBz(bz); + fitter3Body.setBz(bz); + } + + void setMatCorrType(o2::base::Propagator::MatCorrType matCorrType) + { + matCorr = matCorrType; + fitter.setMatCorrType(matCorrType); + fitter3Body.setMatCorrType(matCorrType); + } + + static int chargeIndex(int8_t charge) + { + return charge < 0 ? 0 : 1; + } + + template + static uint32_t detectorMap(Track const& track) + { + return static_cast(track.hasITS()) | + (static_cast(track.hasTPC()) << 1) | + (static_cast(track.hasTOF()) << 2); + } + + template + static BuiltKaonCandidate buildKaon(Track const& track) + { + return {getTrackParCov(track), + track.globalIndex(), + static_cast(track.sign()), + track.hasTPC() ? track.tpcNSigmaKa() : -999.f, + track.hasTOF() ? track.tofNSigmaKa() : -999.f, + detectorMap(track)}; + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda) + { + const std::array omegaTrackIds{omega.posTrackId, omega.negTrackId, omega.bachelorId}; + return std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.posTrackId) != omegaTrackIds.end() || + std::find(omegaTrackIds.begin(), omegaTrackIds.end(), lambda.negTrackId) != omegaTrackIds.end(); + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltKaonCandidate const& kaon) + { + return kaon.trackId == omega.posTrackId || kaon.trackId == omega.negTrackId || kaon.trackId == omega.bachelorId; + } + + static bool sharesTracks(BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& lambda, BuiltKaonCandidate const& kaon) + { + return sharesTracks(omega, lambda) || sharesTracks(omega, kaon) || + kaon.trackId == lambda.posTrackId || kaon.trackId == lambda.negTrackId; + } + + template + bool selectTrack(T const& track) const + { + if (std::abs(track.eta()) > selections.etaMax) { + return false; + } + if (track.itsNCls() < selections.minNITSClus || + track.tpcNClsFound() < selections.minNTPCClus || + track.tpcNClsCrossedRows() < selections.minNCrossedRows || + track.tpcNClsCrossedRows() < 0.8f * track.tpcNClsFindable() || + track.tpcNClsShared() > selections.maxNSharedTPCClus) { + LOG(debug) << "Track failed selection: itsNCls=" << static_cast(track.itsNCls()) << ", tpcNClsFound=" << track.tpcNClsFound() << ", tpcNClsCrossedRows=" << static_cast(track.tpcNClsCrossedRows()) << ", tpcNClsFindable=" << static_cast(track.tpcNClsFindable()) << ", tpcNClsShared=" << static_cast(track.tpcNClsShared()); + return false; + } + return true; + } + + template + bool isSelectedKaon(Track const& track, int8_t charge) const + { + return track.sign() == charge && isSelectedKaon(track); + } + + template + bool isSelectedKaon(Track const& track) const + { + return selectTrack(track) && + std::abs(track.tpcNSigmaKa()) <= selections.nSigmaTPCCut; + } + + // Transverse DCA of a track to the collision primary vertex, computed by propagating + // a copy of the track parameters. Returns false if the propagation fails. + template + bool dcaXYToPV(C const& collision, Track const& track, float& dcaXY) const + { + auto trackParCov = getTrackParCov(track); + const o2::math_utils::Point3D primaryVertex{collision.posX(), collision.posY(), collision.posZ()}; + std::array dca{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertex, trackParCov, 2.f, matCorr, &dca)) { + return false; + } + dcaXY = dca[0]; + return true; + } + + template + bool buildLambda(C const& collision, Track const& posTrack, Track const& negTrack, int8_t charge, BuiltLambdaCandidate& builtLambda) + { + if (posTrack.sign() <= 0 || negTrack.sign() >= 0 || + !selectTrack(posTrack) || !selectTrack(negTrack)) { + return false; + } + + const bool isMatter = charge < 0; + const float protonNSigma = isMatter ? posTrack.tpcNSigmaPr() : negTrack.tpcNSigmaPr(); + const float pionNSigma = isMatter ? negTrack.tpcNSigmaPi() : posTrack.tpcNSigmaPi(); + if (std::abs(protonNSigma) > selections.nSigmaTPCCut || std::abs(pionNSigma) > selections.nSigmaTPCCut) { + return false; + } + float dcaXYPos = -999.f; + float dcaXYNeg = -999.f; + if (!dcaXYToPV(collision, posTrack, dcaXYPos) || !dcaXYToPV(collision, negTrack, dcaXYNeg)) { + LOG(debug) << "Propagation to the PV failed for Lambda daughters " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); + return false; + } + if (std::abs(dcaXYPos) < selections.dcaV0DauToPV || std::abs(dcaXYNeg) < selections.dcaV0DauToPV) { + LOG(debug) << "Lambda dau don't pass selections: " << "nsigmaPR: " << protonNSigma << ", pionNSigma: " << pionNSigma << ", dcaXYPos: " << dcaXYPos << ", dcaXYNeg: " << dcaXYNeg; + return false; + } + + auto posTrackParCov = getTrackParCov(posTrack); + auto negTrackParCov = getTrackParCov(negTrack); + int nCandidates = 0; + try { + nCandidates = fitter.process(posTrackParCov, negTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting Lambda daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex(); + return false; + } + if (nCandidates == 0) { + return false; + } + + std::array posMomentum{}; + std::array negMomentum{}; + fitter.getTrack(0).getPxPyPzGlo(posMomentum); + fitter.getTrack(1).getPxPyPzGlo(negMomentum); + const std::array lambdaMomentum{ + posMomentum[0] + negMomentum[0], + posMomentum[1] + negMomentum[1], + posMomentum[2] + negMomentum[2]}; + const auto& fittedVertex = fitter.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const float mass = isMatter ? RecoDecay::m(std::array{posMomentum, negMomentum}, + std::array{o2::constants::physics::MassProton, o2::constants::physics::MassPionCharged}) + : RecoDecay::m(std::array{posMomentum, negMomentum}, + std::array{o2::constants::physics::MassPionCharged, o2::constants::physics::MassProton}); + const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); + const float eta = RecoDecay::eta(lambdaMomentum); + if (std::abs(eta) > selections.etaMax || + dcaDaughters > selections.dcaLambdaDaughters || + std::abs(mass - o2::constants::physics::MassLambda0) > selections.mLambdaWindow) { + return false; + } + + builtLambda.candidate = { + .px = lambdaMomentum[0], + .py = lambdaMomentum[1], + .pz = lambdaMomentum[2], + .mass = mass}; + builtLambda.parentTrack = fitter.createParentTrackParCov(0); + builtLambda.decayVertex = decayVertex; + builtLambda.posTrackId = posTrack.globalIndex(); + builtLambda.negTrackId = negTrack.globalIndex(); + // Keep particle-role ordering for matter and antimatter alike. + builtLambda.daughterDetectorMap = isMatter ? detectorMap(posTrack) | (detectorMap(negTrack) << 3) + : detectorMap(negTrack) | (detectorMap(posTrack) << 3); + builtLambda.dcaXYProtonToPV = isMatter ? dcaXYPos : dcaXYNeg; + builtLambda.dcaXYPionToPV = isMatter ? dcaXYNeg : dcaXYPos; + builtLambda.dcaDaughters = dcaDaughters; + return true; + } + + template + bool buildOmega(C const& collision, Track const& posTrack, Track const& negTrack, Track const& bachelor, BuiltOmegaCandidate& builtOmega) + { + if (bachelor.sign() == 0 || + !selectTrack(bachelor) || + std::abs(bachelor.tpcNSigmaKa()) > selections.nSigmaTPCCut) { + return false; + } + float dcaXYBachelor = -999.f; + if (!dcaXYToPV(collision, bachelor, dcaXYBachelor) || std::abs(dcaXYBachelor) < selections.dcaBachToPV) { + return false; + } + + BuiltLambdaCandidate lambda; + if (!buildLambda(collision, posTrack, negTrack, bachelor.sign(), lambda)) { + return false; + } + + auto v0TrackParCov = lambda.parentTrack; + auto bachelorTrackParCov = getTrackParCov(bachelor); + int nCandidates = 0; + try { + nCandidates = fitter.process(v0TrackParCov, bachelorTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting Omega daughter tracks " << posTrack.globalIndex() << ", " << negTrack.globalIndex() << ", " << bachelor.globalIndex(); + return false; + } + if (nCandidates == 0) { + return false; + } + + std::array lambdaMomentum{}; + std::array bachelorMomentum{}; + fitter.getTrack(0).getPxPyPzGlo(lambdaMomentum); + fitter.getTrack(1).getPxPyPzGlo(bachelorMomentum); + const std::array omegaMomentum{ + lambdaMomentum[0] + bachelorMomentum[0], + lambdaMomentum[1] + bachelorMomentum[1], + lambdaMomentum[2] + bachelorMomentum[2]}; + const auto& fittedVertex = fitter.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const float massOmega = RecoDecay::m(std::array{lambdaMomentum, bachelorMomentum}, + std::array{o2::constants::physics::MassLambda0, o2::constants::physics::MassKaonCharged}); + const float massXi = RecoDecay::m(std::array{lambdaMomentum, bachelorMomentum}, + std::array{o2::constants::physics::MassLambda0, o2::constants::physics::MassPionCharged}); + const float dcaDaughters = std::sqrt(std::abs(fitter.getChi2AtPCACandidate())); + const float eta = RecoDecay::eta(omegaMomentum); + if (dcaDaughters > selections.dcaV0Bach || + std::abs(eta) > selections.etaMax || + std::abs(massOmega - o2::constants::physics::MassOmegaMinus) > selections.mOmegaWindow) { + return false; + } + + builtOmega = { + omegaMomentum[0], + omegaMomentum[1], + omegaMomentum[2], + decayVertex[0], + decayVertex[1], + decayVertex[2], + massOmega, + massXi, + static_cast(bachelor.sign()), + posTrack.globalIndex(), + negTrack.globalIndex(), + bachelor.globalIndex(), + fitter.createParentTrackParCov(), + detectorMap(bachelor) | (lambda.daughterDetectorMap << 3), + dcaXYBachelor, + lambda.dcaXYProtonToPV, + lambda.dcaXYPionToPV, + dcaDaughters}; + return true; + } + + // Selections of the Omega, Lambda and kaon candidates that can be applied again on their stored + // properties (e.g. when reading them back from derived data), to tighten the production selections. + bool isSelectedOmega(BuiltOmegaCandidate const& omega) const + { + return std::abs(RecoDecay::eta(std::array{omega.px, omega.py, omega.pz})) <= selections.etaMax && + std::abs(omega.massOmega - o2::constants::physics::MassOmegaMinus) <= selections.mOmegaWindow && + omega.dcaDaughters <= selections.dcaV0Bach && + std::abs(omega.dcaXYKaonToPV) >= selections.dcaBachToPV && + std::abs(omega.dcaXYProtonToPV) >= selections.dcaV0DauToPV && + std::abs(omega.dcaXYPionToPV) >= selections.dcaV0DauToPV; + } + + bool isSelectedLambda(BuiltLambdaCandidate const& lambda) const + { + return std::abs(RecoDecay::eta(std::array{lambda.candidate.px, lambda.candidate.py, lambda.candidate.pz})) <= selections.etaMax && + std::abs(lambda.candidate.mass - o2::constants::physics::MassLambda0) <= selections.mLambdaWindow && + lambda.dcaDaughters <= selections.dcaLambdaDaughters && + std::abs(lambda.dcaXYProtonToPV) >= selections.dcaV0DauToPV && + std::abs(lambda.dcaXYPionToPV) >= selections.dcaV0DauToPV; + } + + bool isSelectedKaon(BuiltKaonCandidate const& kaon) const + { + return kaon.tpcNSigma > -999.f && std::abs(kaon.tpcNSigma) <= selections.nSigmaTPCCut; + } + + bool buildDoubleOmega(std::array const& primaryVertex, BuiltOmegaCandidate const& omega, BuiltLambdaCandidate const& directLambda, BuiltKaonCandidate const& kaon, DoubleOmegaCandidate& cand) + { + auto omegaTrackParCov = omega.parentTrack; + auto lambdaTrackParCov = directLambda.parentTrack; + auto kaonTrackParCov = kaon.track; + int nCandidates = 0; + try { + nCandidates = fitter3Body.process(omegaTrackParCov, lambdaTrackParCov, kaonTrackParCov); + } catch (...) { + LOG(error) << "Exception while fitting the double-Omega candidate"; + return false; + } + if (nCandidates == 0) { + return false; + } + + auto omegaTrackAtVertex = fitter3Body.getTrack(0); + auto lambdaTrackAtVertex = fitter3Body.getTrack(1); + auto kaonTrackAtVertex = fitter3Body.getTrack(2); + std::array omegaMomentum{}; + std::array lambdaMomentum{}; + std::array kaonMomentum{}; + omegaTrackAtVertex.getPxPyPzGlo(omegaMomentum); + lambdaTrackAtVertex.getPxPyPzGlo(lambdaMomentum); + kaonTrackAtVertex.getPxPyPzGlo(kaonMomentum); + + const std::array totalMomentum{ + omegaMomentum[0] + lambdaMomentum[0] + kaonMomentum[0], + omegaMomentum[1] + lambdaMomentum[1] + kaonMomentum[1], + omegaMomentum[2] + lambdaMomentum[2] + kaonMomentum[2]}; + const auto& fittedVertex = fitter3Body.getPCACandidate(); + const std::array decayVertex{ + static_cast(fittedVertex[0]), static_cast(fittedVertex[1]), static_cast(fittedVertex[2])}; + const o2::math_utils::Point3D primaryVertexPoint{primaryVertex[0], primaryVertex[1], primaryVertex[2]}; + const std::array omegaDecayVertex{omega.x, omega.y, omega.z}; + + std::array dcaOmega{}; + std::array dcaDirectLambda{}; + std::array dcaDirectKaon{}; + if (!o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, omegaTrackAtVertex, 2.f, matCorr, &dcaOmega) || + !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, lambdaTrackAtVertex, 2.f, matCorr, &dcaDirectLambda) || + !o2::base::Propagator::Instance()->propagateToDCABxByBz(primaryVertexPoint, kaonTrackAtVertex, 2.f, matCorr, &dcaDirectKaon)) { + return false; + } + + const float massSquared = RecoDecay::m2(std::array{omegaMomentum, lambdaMomentum, kaonMomentum}, + std::array{o2::constants::physics::MassOmegaMinus, o2::constants::physics::MassLambda0, o2::constants::physics::MassKaonCharged}); + + cand.pt = std::hypot(totalMomentum[0], totalMomentum[1]); + cand.eta = RecoDecay::eta(totalMomentum); + cand.phi = std::atan2(totalMomentum[1], totalMomentum[0]); + cand.x = decayVertex[0]; + cand.y = decayVertex[1]; + cand.z = decayVertex[2]; + cand.pvX = primaryVertex[0]; + cand.pvY = primaryVertex[1]; + cand.pvZ = primaryVertex[2]; + cand.cosPAOmega = RecoDecay::cpa(decayVertex, omegaDecayVertex, omegaMomentum); + cand.cosPADirectLambda = RecoDecay::cpa(decayVertex, directLambda.decayVertex, lambdaMomentum); + cand.cosPADoubleOmega = RecoDecay::cpa(primaryVertex, decayVertex, totalMomentum); + // PV pointing angles use the daughter momenta at their own decay vertices. + cand.cosPAOmegaToPV = RecoDecay::cpa(primaryVertex, omegaDecayVertex, std::array{omega.px, omega.py, omega.pz}); + cand.cosPADirectLambdaToPV = RecoDecay::cpa(primaryVertex, directLambda.decayVertex, std::array{directLambda.candidate.px, directLambda.candidate.py, directLambda.candidate.pz}); + cand.dcaXYOmegaToPV = dcaOmega[0]; + cand.dcaXYDirectLambdaToPV = dcaDirectLambda[0]; + cand.dcaXYDirectKaonToPV = dcaDirectKaon[0]; + cand.dcaXYOmegaKaonToPV = omega.dcaXYKaonToPV; + cand.dcaXYOmegaProtonToPV = omega.dcaXYProtonToPV; + cand.dcaXYOmegaPionToPV = omega.dcaXYPionToPV; + cand.dcaXYDirectProtonToPV = directLambda.dcaXYProtonToPV; + cand.dcaXYDirectPionToPV = directLambda.dcaXYPionToPV; + cand.tpcNSigmaDirectKaon = kaon.tpcNSigma; + cand.tofNSigmaDirectKaon = kaon.tofNSigma; + cand.daughterDetectorMap = omega.daughterDetectorMap | + (directLambda.daughterDetectorMap << 9) | + (kaon.detectorMap << 15); + cand.mass = std::sqrt(std::max(0.f, massSquared)); + if (cand.cosPAOmega < selections.minCosPAOmega || + cand.cosPADirectLambda < selections.minCosPADirectLambda || + cand.cosPADoubleOmega < selections.minCosPADoubleOmega || + std::hypot(decayVertex[0], decayVertex[1]) < selections.minDoubleOmegaDecayRadius || + std::abs(cand.dcaXYOmegaToPV) < selections.dcaOmegaToPV || + std::abs(cand.dcaXYDirectLambdaToPV) < selections.dcaDirectLambdaToPV || + std::abs(cand.dcaXYDirectKaonToPV) < selections.dcaKaonToPV || + cand.mass > selections.maxDoubleOmegaMass) { + return false; + } + cand.massOmega = omega.massOmega; + cand.massXi = omega.massXi; + cand.sign = omega.sign; + return true; + } + + // Builds the Omega, Lambda and kaon candidates of a collision. If requireOmega is set, Lambdas and + // kaons are built only if the collision has at least one Omega. + template + CollisionCandidates buildCollisionCandidates(C const& collision, T const& tracks, Cascades const& cascades, V0s const& v0s, bool requireOmega) + { + CollisionCandidates candidates; + candidates.primaryVertex = {collision.posX(), collision.posY(), collision.posZ()}; + for (const auto& rawCascade : cascades) { + const auto omegaV0 = rawCascade.template v0_as(); + BuiltOmegaCandidate omega; + if (buildOmega(collision, omegaV0.template posTrack_as(), omegaV0.template negTrack_as(), rawCascade.template bachelor_as(), omega)) { + candidates.omegas.push_back(omega); + } + } + if (requireOmega && candidates.omegas.empty()) { + return candidates; + } + + for (const int8_t charge : {int8_t{-1}, int8_t{1}}) { + for (const auto& rawV0 : v0s) { + BuiltLambdaCandidate lambda; + if (buildLambda(collision, rawV0.template posTrack_as(), rawV0.template negTrack_as(), charge, lambda)) { + candidates.lambdas[chargeIndex(charge)].push_back(lambda); + } + } + for (const auto& track : tracks) { + if (isSelectedKaon(track, charge)) { + candidates.kaons[chargeIndex(charge)].push_back(buildKaon(track)); + } + } + } + return candidates; + } + + // Pairs the Omegas, Lambdas and kaons of (possibly different) collisions. The double-Omega candidate + // is built with respect to the primary vertex of the reference collision. + template + void combine(CollisionCandidates const& reference, CollisionCandidates const& omegaSource, + CollisionCandidates const& lambdaSource, CollisionCandidates const& kaonSource, F&& onCandidate) + { + for (const auto& omega : omegaSource.omegas) { + for (const auto& lambda : lambdaSource.lambdas[chargeIndex(omega.sign)]) { + for (const auto& kaon : kaonSource.kaons[chargeIndex(omega.sign)]) { + // Tracks can also be shared by V0s and cascades associated to different collisions. + if (sharesTracks(omega, lambda, kaon)) { + continue; + } + DoubleOmegaCandidate cand; + if (!buildDoubleOmega(reference.primaryVertex, omega, lambda, kaon, cand)) { + continue; + } + onCandidate(cand, omega, lambda, kaon); + } + } + } + } + + // True if at least one Omega has a Lambda and a kaon of compatible charge that do not share its tracks. + static bool hasOmegaLambdaKaon(CollisionCandidates const& candidates) + { + for (const auto& omega : candidates.omegas) { + const auto& lambdas = candidates.lambdas[chargeIndex(omega.sign)]; + const auto& kaons = candidates.kaons[chargeIndex(omega.sign)]; + const bool hasLambda = std::any_of(lambdas.begin(), lambdas.end(), [&](BuiltLambdaCandidate const& lambda) { return !sharesTracks(omega, lambda); }); + const bool hasKaon = std::any_of(kaons.begin(), kaons.end(), [&](BuiltKaonCandidate const& kaon) { return !sharesTracks(omega, kaon); }); + if (hasLambda && hasKaon) { + return true; + } + } + return false; + } + + private: + template + static void configureFitter(Fitter& dcaFitter) + { + dcaFitter.setPropagateToPCA(true); + dcaFitter.setMaxR(200.); + dcaFitter.setMaxDZIni(4.); + dcaFitter.setMinParamChange(1.e-3); + dcaFitter.setMinRelChi2Change(0.9); + dcaFitter.setUseAbsDCA(true); + } +}; + +} // namespace o2::pwglf::doubleomega + +#endif // PWGLF_UTILS_DOUBLEOMEGABUILDERHELPER_H_ diff --git a/PWGLF/Utils/inelGt.h b/PWGLF/Utils/inelGt.h index 85d07381609..8c4033f1b9a 100644 --- a/PWGLF/Utils/inelGt.h +++ b/PWGLF/Utils/inelGt.h @@ -114,7 +114,7 @@ struct ParticleCounter { continue; } // is charged - if (abs(p->Charge()) == 0) { + if (std::abs(p->Charge()) == 0) { continue; } // in acceptance diff --git a/PWGLF/Utils/mcCentralityModule.h b/PWGLF/Utils/mcCentralityModule.h new file mode 100644 index 00000000000..dd53bbb2ac8 --- /dev/null +++ b/PWGLF/Utils/mcCentralityModule.h @@ -0,0 +1,737 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file mcCentralityModule.cxx +/// \author Romain Schotter romain.schotter@cern.ch +/// \brief Module to produce MC centrality table based on ANY table containing MC multiplicity +/// + +#ifndef PWGLF_UTILS_MCCENTRALITYMODULE_H_ +#define PWGLF_UTILS_MCCENTRALITYMODULE_H_ + +#include "PWGLF/DataModel/mcCentrality.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//__________________________________________ +// strangeness builder module + +namespace o2 +{ +namespace pwglf +{ +namespace mccentrality // avoid polluting other namespaces +{ + +// statics necessary for the configurables in this namespace +static constexpr int nParameters = 1; +static const std::vector tableNames{ + "McCentFV0As", + "McCentFT0Ms", + "McCentFT0As", + "McCentFT0Cs", + "McCentFT0CVariant1s", + "McCentFT0CVariant2s", + "McCentFDDMs", + "McCentNTPVs", + "McCentNGlobals", + "McCentMFTs"}; + +static constexpr int nTablesConst = 10; +static const std::vector parameterNames{"enable"}; +static const int defaultParameters[nTablesConst][nParameters]{ + {-1}, + {-1}, + {-1}, + {-1}, + {-1}, + {-1}, + {-1}, + {-1}, + {-1}, + {-1}}; + +// table index : match order above +enum tableIndex { kFV0A = 0, + kFT0M, + kFT0A, + kFT0C, + kFT0CVariant1, + kFT0CVariant2, + kFDDM, + kNTPV, + kNGlobal, + kMFT, + kNestimators }; + +static constexpr const char* DirList[] = { + "FV0A", + "FT0M", + "FT0A", + "FT0C", + "FT0CVariant1", + "FT0CVariant2", + "FDDM", + "NTPV", + "NGlobal", + "MFT"}; + +// mcCentralityModule: 1st-order configurables +struct coreConfigurables : o2::framework::ConfigurableGroup { + o2::framework::Configurable> enabledTables{"enabledTables", + {defaultParameters[0], nTablesConst, nParameters, tableNames, parameterNames}, + "Produce this table: -1 for autodetect; otherwise, 0/1 is false/true"}; + std::vector mEnabledTables; // Vector of enabled tables + + o2::framework::Configurable recalibrateCentrality{"recalibrateCentrality", false, "If true, re-calibrate the MC centrality for the binning in binPercentile."}; + o2::framework::Configurable recalibrateMode{"recalibrateMode", 1, "Strategy to calibrate MC centrality? 0: from low to high mult.; 1: from high to low mult."}; + o2::framework::Configurable minEntries{"minEntries", 100, "Minimum number of entries for estimating the mean value for the recalibration"}; + o2::framework::Configurable doNotCrashOnNull{"doNotCrashOnNull", false, "If ccdb object does not exist, fill with dummy values"}; + + o2::framework::Configurable assignCentralityPerCandidate{"assignCentralityPerCandidate", false, "If true, assign centrality by sampling P(centrality|generated multiplicity) per MC collision instead of the class-averaged (statistical) value"}; + o2::framework::Configurable centralitySamplingSeed{"centralitySamplingSeed", 137, "Base seed of the RNG used to sample centrality in candidate-by-candidate mode (assignCentralityPerCandidate)"}; + o2::framework::ConfigurableAxis binsPercentile{"binsPercentile", {o2::framework::VARIABLE_WIDTH, 0, 1.0, 5.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}, "Binning of the percentile axis"}; + o2::framework::ConfigurableAxis binsPercentileFine{"binsPercentileFine", {o2::framework::VARIABLE_WIDTH, 0, 0.001, 0.01, 1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0}, "Binning of the percentile axis"}; + o2::framework::ConfigurableAxis binsMultiplicity{"binsMultiplicity", {1000, 0, 5000}, "Binning of the multiplicity axis"}; + + // ccdb configurables + o2::framework::Configurable path{"path", "/tmp/InputCalibMC.root", "path to calib file or ccdb path if begins with ccdb://"}; + + // debug option + o2::framework::Configurable verbose{"verbose", false, "If true, display more messages"}; +}; + +struct products : o2::framework::ProducesGroup { + // Tables to produce + o2::framework::Produces centFV0A; + o2::framework::Produces centFT0M; + o2::framework::Produces centFT0A; + o2::framework::Produces centFT0C; + o2::framework::Produces centFT0CVariant1; + o2::framework::Produces centFT0CVariant2; + o2::framework::Produces centFDDM; + o2::framework::Produces centNTPV; + o2::framework::Produces centNGlobal; + o2::framework::Produces centMFT; +}; + +template +concept HasMcMults = requires(typename T::iterator a) { + { a.multMCFT0A() } -> std::convertible_to; + { a.multMCFT0C() } -> std::convertible_to; + { a.multMCFV0A() } -> std::convertible_to; + { a.multMCFDDA() } -> std::convertible_to; + { a.multMCFDDC() } -> std::convertible_to; + { a.multMCNParticlesEta08() } -> std::convertible_to; + { a.multMCNParticlesEta05() } -> std::convertible_to; +}; + +/// Task to produce the response table +struct BuilderModule { + // Input parameters + o2::framework::Service ccdb; + + // declaration of structs here + // (N.B.: will be invisible to the outside, create your own copies) + coreConfigurables baseOpts; + + TList* MCCentralityCalibObjects = nullptr; + TH1D* h1dFV0A = nullptr; + TH1D* h1dFT0M = nullptr; + TH1D* h1dFT0A = nullptr; + TH1D* h1dFT0C = nullptr; + TH1D* h1dFT0CVariant1 = nullptr; + TH1D* h1dFT0CVariant2 = nullptr; + TH1D* h1dFDDM = nullptr; + TH1D* h1dNTPV = nullptr; + TH1D* h1dNGlobal = nullptr; + TH1D* h1dMFT = nullptr; + + // Calibration objects for candidate-by-candidate sampling (assignCentralityPerCandidate): + // x = centrality (%) of the matched reconstructed MC collision, y = generated multiplicity (|eta| < 0.5) + std::vector h2dCentVsGenMult; + TRandom3 fRandomSampler; + + // QA of the on-the-fly calibration (recalibrateCentrality): mean _{|eta|<0.5} in data and MC + // (reco), one entry per centrality class -- filled in extractCentralityCalibration(). + // Must be TProfile, not TH1D+SetBinContent/SetBinError: every grid job redundantly derives the exact + // same calibration from the same CCDB input, and grid-merged output histograms are combined via a + // plain bin-content sum (TH1::Merge), which would turn a mean into N_jobs*mean. TProfile stores the + // underlying sum(w)/sum(wy)/sum(wy^2) per bin and merges those correctly, so the mean/error recomputed + // from the merged output stay correct however many jobs contributed. For the same reason there is no + // ratio histogram here: a ratio of two already-merged means can't be reconstructed after the fact, so + // compute PVMC/PVData downstream from the merged output if you need it, not inside the task. + std::vector> hCalibPVData; + std::vector> hCalibPVMC; + + int nEnabledTables = 0; + int mRunNumber = 0; + + // TAxis + int nCentBins = 0; + std::vector centralityBins; + + // Registers the on-the-fly-calibration QA histograms for one estimator (no-op unless recalibrateCentrality + // is on): mean _{|eta|<0.5} in data and in MC (reco), one entry per centrality class, as TProfile + // (see the comment on hCalibPVData/hCalibPVMC for why TH1D+SetBinContent doesn't survive grid merging). + // Uses the runtime add() overload (returning the type-erased HistPtr) since the estimator name/directory + // is only known at runtime here, unlike fillHistograms() which relies on a compile-time HIST() lookup. + template + void registerCalibQAHistos(THistoRegistry& histos, int idx, const char* name) + { + if (!baseOpts.recalibrateCentrality) { + return; + } + o2::framework::AxisSpec axisCentClass{baseOpts.binsPercentile, Form("%s percentile (%%)", name)}; + hCalibPVData[idx] = std::get>(histos.add(Form("%s/calibPVData", name), Form("#LT #it{N}_{PV}^{Data} #GT vs %s percentile;%s percentile (%%);#LT #it{N}_{PV}^{Data} #GT_{|#it{#eta}|<0.5}", name, name), o2::framework::HistType::kTProfile, {axisCentClass})); + hCalibPVMC[idx] = std::get>(histos.add(Form("%s/calibPVMC", name), Form("#LT #it{N}_{PV}^{MC} #GT (reco) vs %s percentile;%s percentile (%%);#LT #it{N}_{PV}^{MC} #GT_{|#it{#eta}|<0.5} (reco)", name, name), o2::framework::HistType::kTProfile, {axisCentClass})); + } + + // Registers the candidate-by-candidate sampling QA (no-op unless assignCentralityPerCandidate is on): + // sampled percentile vs the generated multiplicity (|eta| < 0.5) actually used to key the sampling, so + // it can be visually compared against the input calibration (hGenMultEta05VsCentrality) to + // check that the sampled distribution reproduces the expected P(centrality|genMult). This is a plain + // TH2D (not a TProfile): unlike the calibration QA above, it stores a distribution to look at, not a + // mean to merge, so ordinary bin-content-sum merging across grid jobs is exactly what's wanted here. + template + void registerCandidateQAHistos(THistoRegistry& histos, const char* name) + { + if (!baseOpts.assignCentralityPerCandidate) { + return; + } + histos.add(Form("%s/percentileVsGenMultEta05", name), Form("Sampled %s percentile vs generated mult (|#it{#eta}|<0.5);%s percentile (%%);generated mult (|#it{#eta}|<0.5)", name, name), o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, Form("%s percentile", name)}, {baseOpts.binsMultiplicity, "generated mult (|#eta|<0.5)"}}); + } + + template + void init(TBaseConfigurables const& inputBaseOpts, THistoRegistry& histos, TInitContext& context) + { + // read in configurations from the task where it's used + // could be grouped even further, but should work + baseOpts = inputBaseOpts; + + baseOpts.mEnabledTables.resize(nTablesConst, 0); + h2dCentVsGenMult.resize(nTablesConst, nullptr); + hCalibPVData.resize(nTablesConst); + hCalibPVMC.resize(nTablesConst); + fRandomSampler.SetSeed(baseOpts.centralitySamplingSeed); + + LOGF(info, "Checking if MC centrality is required"); + auto& workflows = context.services().template get(); + + nEnabledTables = 0; + + std::array listOfRequestors{}; + for (int i = 0; i < nTablesConst; i++) { + int f = baseOpts.enabledTables->get(tableNames[i].c_str(), "enable"); + if (f == 1) { + baseOpts.mEnabledTables[i] = 1; + listOfRequestors[i] = "manual enabling"; + nEnabledTables++; + } + if (f == -1) { + // autodetect this table in other devices + for (o2::framework::DeviceSpec const& device : workflows.devices) { + // Step 1: check if this device subscribed to the V0data table + for (auto const& input : device.inputs) { + if (o2::framework::DataSpecUtils::partialMatch(input.matcher, o2::header::DataOrigin("AOD"))) { + auto&& [origin, description, version] = o2::framework::DataSpecUtils::asConcreteDataMatcher(input.matcher); + std::string tableNameWithVersion = tableNames[i]; + if (version > 0) { + tableNameWithVersion += Form("_%03d", version); + } + if (input.matcher.binding == tableNameWithVersion) { + LOGF(info, "Device %s has subscribed to %s (version %i)", device.name, tableNames[i], version); + listOfRequestors[i].Append(Form("%s ", device.name.c_str())); + baseOpts.mEnabledTables[i] = 1; + nEnabledTables++; + } + } + } + } + } + } + + if (nEnabledTables == 0) { + LOGF(info, "MC centrality not required. Will suppress all functionality, including logs, from this point forward."); + return; + } + mRunNumber = 0; + + // TAxis + o2::framework::AxisSpec axisBinsPercentile{baseOpts.binsPercentile, "axisBinsPercentile"}; + nCentBins = axisBinsPercentile.binEdges.size() - 1; + for (std::size_t iCent = 0; iCent < axisBinsPercentile.binEdges.size(); iCent++) { + centralityBins.push_back(axisBinsPercentile.binEdges[iCent]); + } + + if (baseOpts.mEnabledTables[kFV0A]) { + histos.add("FV0A/percentile", "FV0A percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "FV0A percentile"}}); + histos.add("FV0A/percentilevsMult", "FV0A percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "FV0A percentile"}, {baseOpts.binsMultiplicity, "FV0A mult."}}); + registerCalibQAHistos(histos, kFV0A, "FV0A"); + registerCandidateQAHistos(histos, "FV0A"); + } + if (baseOpts.mEnabledTables[kFT0M]) { + histos.add("FT0M/percentile", "FT0M percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "FT0M percentile"}}); + histos.add("FT0M/percentilevsMult", "FT0M percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "FT0M percentile"}, {baseOpts.binsMultiplicity, "FT0M mult."}}); + registerCalibQAHistos(histos, kFT0M, "FT0M"); + registerCandidateQAHistos(histos, "FT0M"); + } + if (baseOpts.mEnabledTables[kFT0A]) { + histos.add("FT0A/percentile", "FT0A percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "FT0A percentile"}}); + histos.add("FT0A/percentilevsMult", "FT0A percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "FT0A percentile"}, {baseOpts.binsMultiplicity, "FT0A mult."}}); + registerCalibQAHistos(histos, kFT0A, "FT0A"); + registerCandidateQAHistos(histos, "FT0A"); + } + if (baseOpts.mEnabledTables[kFT0C]) { + histos.add("FT0C/percentile", "FT0C percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "FT0C percentile"}}); + histos.add("FT0C/percentilevsMult", "FT0C percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "FT0C percentile"}, {baseOpts.binsMultiplicity, "FT0C mult."}}); + registerCalibQAHistos(histos, kFT0C, "FT0C"); + registerCandidateQAHistos(histos, "FT0C"); + } + if (baseOpts.mEnabledTables[kFT0CVariant1]) { + histos.add("FT0CVariant1/percentile", "FT0CVariant1 percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "FT0CVariant1 percentile"}}); + histos.add("FT0CVariant1/percentilevsMult", "FT0CVariant1 percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "FT0CVariant1 percentile"}, {baseOpts.binsMultiplicity, "FT0C mult."}}); + registerCalibQAHistos(histos, kFT0CVariant1, "FT0CVariant1"); + registerCandidateQAHistos(histos, "FT0CVariant1"); + } + if (baseOpts.mEnabledTables[kFT0CVariant2]) { + histos.add("FT0CVariant2/percentile", "FT0CVariant2 percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "FT0CVariant2 percentile"}}); + histos.add("FT0CVariant2/percentilevsMult", "FT0CVariant2 percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "FT0CVariant2 percentile"}, {baseOpts.binsMultiplicity, "FT0C mult."}}); + registerCalibQAHistos(histos, kFT0CVariant2, "FT0CVariant2"); + registerCandidateQAHistos(histos, "FT0CVariant2"); + } + if (baseOpts.mEnabledTables[kFDDM]) { + histos.add("FDDM/percentile", "FDDM percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "FDDM percentile"}}); + histos.add("FDDM/percentilevsMult", "FDDM percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "FDDM percentile"}, {baseOpts.binsMultiplicity, "FDDM mult."}}); + registerCalibQAHistos(histos, kFDDM, "FDDM"); + registerCandidateQAHistos(histos, "FDDM"); + } + if (baseOpts.mEnabledTables[kNTPV]) { + histos.add("NTPV/percentile", "NTPV percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "NTPV percentile"}}); + histos.add("NTPV/percentilevsMult", "NTPV percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "NTPV percentile"}, {baseOpts.binsMultiplicity, "NTPV mult."}}); + registerCalibQAHistos(histos, kNTPV, "NTPV"); + registerCandidateQAHistos(histos, "NTPV"); + } + if (baseOpts.mEnabledTables[kNGlobal]) { + histos.add("NGlobal/percentile", "NGlobal percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "NGlobal percentile"}}); + histos.add("NGlobal/percentilevsMult", "NGlobal percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "NGlobal percentile"}, {baseOpts.binsMultiplicity, "NGlobal mult."}}); + registerCalibQAHistos(histos, kNGlobal, "NGlobal"); + registerCandidateQAHistos(histos, "NGlobal"); + } + if (baseOpts.mEnabledTables[kMFT]) { + histos.add("MFT/percentile", "MFT percentile.", o2::framework::HistType::kTH1D, {{baseOpts.binsPercentileFine, "MFT percentile"}}); + histos.add("MFT/percentilevsMult", "MFT percentile.", o2::framework::HistType::kTH2D, {{baseOpts.binsPercentileFine, "MFT percentile"}, {baseOpts.binsMultiplicity, "MFT mult."}}); + } + + LOGF(info, "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"); + LOGF(info, " MC centrality: basic configuration listing"); + LOGF(info, "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"); + // list enabled tables + for (int i = 0; i < nTablesConst; i++) { + // printout to be improved in the future + if (baseOpts.mEnabledTables[i]) { + LOGF(info, " -~> Table enabled: %s, requested by %s", tableNames[i], listOfRequestors[i].Data()); + } + } + } + + template + THist* getHist(const char* name) + { + if (!this->MCCentralityCalibObjects) { + return (THist*)0x0; + } + auto hist = reinterpret_cast(this->MCCentralityCalibObjects->FindObject(name)); + if (!hist) { + if (this->baseOpts.verbose) { + this->MCCentralityCalibObjects->ls(); + } + if (this->baseOpts.doNotCrashOnNull) { + LOG(info) << "Could not open histogram " << name << " from TList, will fill tables with dummy values"; + } else { + LOG(fatal) << "Could not open histogram " << name << " from TList"; + } + } + return hist; + } + + TH1D* extractCentralityCalibration(int idx, const char* name, bool reverse = false) + { + + auto CalibMC = [&](TString const& estimator) { + LOGF(info, "Calibration for %s estimator -> Starting...\n", estimator.Data()); + + std::vector percentile_center(nCentBins); + std::vector epercentile_center(nCentBins); + + // Histograms + TH2D* h2dMultVsCent_Data = getHist(Form("hMultEta05VsCent%s_Data", estimator.Data())); + TH2D* h2dMultRecoVsMultGen_MC = getHist(Form("hMultEta05VsGenMult%s", estimator.Data())); + if (!h2dMultVsCent_Data || !h2dMultRecoVsMultGen_MC) { + return (TH1D*)0x0; + } + + // QA of this on-the-fly calibration: mean _{|eta|<0.5} in data/MC per centrality class, + // registered by registerCalibQAHistos() (only non-null when recalibrateCentrality is on). Filled + // bin-by-bin from projData/projMC below (TProfile::Fill(x, y, weight)), not via SetBinContent, so + // that the mean/error reconstructed from the merged grid output stay statistically correct. + TProfile* hPVData = hCalibPVData[idx].get(); + TProfile* hPVMC = hCalibPVMC[idx].get(); + + TH1D* h1dCalib = h2dMultRecoVsMultGen_MC->ProjectionX(Form("h1d%s", estimator.Data()), 1, h2dMultRecoVsMultGen_MC->GetNbinsX()); + h1dCalib->Reset(); + h1dCalib->SetTitle(Form("%s calibration object", estimator.Data())); + h1dCalib->GetXaxis()->SetTitle(Form("#it{N}_{%s, gen.}", estimator.Data())); + h1dCalib->GetYaxis()->SetTitle(Form("%s percentile (%%)", estimator.Data())); + + // NOTE: candidate-by-candidate assignment (assignCentralityPerCandidate) does not go through this + // mean-matching path at all. It samples directly from the "hGenMultEta05VsCentrality" + // joint histogram (x = reco centrality of the matched MC collision, y = generated mult |eta|<0.5) + // retrieved in initCCDB() below, see h2dCentVsGenMult and dataProcess(). + + if (reverse) { + for (int i = 0; i < nCentBins; i++) { + int irev = i; + percentile_center[i] = (centralityBins[irev] + centralityBins[irev + 1]) / 2; + epercentile_center[i] = (centralityBins[irev] - centralityBins[irev + 1]) / 2; + } + + int startBinMc = h1dCalib->GetNbinsX(); + for (int i = 0; i < nCentBins; i++) { // Loop over centrality bins + // start from the end (from the high multiplicity collisions) + int irev = i; + TH1D* projData = h2dMultVsCent_Data->ProjectionY(Form("projData_%d", i), h2dMultVsCent_Data->GetXaxis()->FindBin(centralityBins[irev] + 1e-5), + h2dMultVsCent_Data->GetXaxis()->FindBin(centralityBins[irev + 1] - 1e-5)); + + double meanMult_Data = projData->GetMean(); + + double meanMult_MC = -1; + double diffMultDataVsMC = 1e+09; + int endBinMc = startBinMc; + for (int j = startBinMc - 1; j >= 1; j--) { + // Loop over MC bins + TH1D* projMC = h2dMultRecoVsMultGen_MC->ProjectionY("", j, startBinMc); + int nEntries = projMC->Integral(); + double meanMC = projMC->GetMean(); + double ldiff = std::abs(meanMC - meanMult_Data); + // std::cout << ldiff << std::endl; + + // if less than 100 entries for estimating the mean, do not consider it + if (nEntries < baseOpts.minEntries) + continue; + + // Find the minimum difference and corresponding MC bin + if (ldiff < diffMultDataVsMC) { + diffMultDataVsMC = ldiff; + meanMult_MC = meanMC; + endBinMc = j; + } + } + if (i == nCentBins - 1) { + endBinMc = 1; + } + + TH1D* projMC = h2dMultRecoVsMultGen_MC->ProjectionY(Form("projMC_%d", i), endBinMc, startBinMc); + + // Refill the per-class mean from the underlying (data/MC) multiplicity distributions, bin by + // bin, so the TProfile accumulates real sum(w)/sum(wy)/sum(wy^2) rather than a precomputed mean. + if (hPVData) { + for (int ybin = 1; ybin <= projData->GetNbinsX(); ybin++) { + double w = projData->GetBinContent(ybin); + if (w > 0) { + hPVData->Fill(percentile_center[i], projData->GetBinCenter(ybin), w); + } + } + } + if (hPVMC) { + for (int ybin = 1; ybin <= projMC->GetNbinsX(); ybin++) { + double w = projMC->GetBinContent(ybin); + if (w > 0) { + hPVMC->Fill(percentile_center[i], projMC->GetBinCenter(ybin), w); + } + } + } + + LOGF(info, "Calibration for %s estimator -> Data centrality bin %g-%g%%\n", estimator.Data(), centralityBins[irev], centralityBins[irev + 1]); + LOGF(info, "Calibration for %s estimator -> MC multiplicity range %g-%g\n", estimator.Data(), projMC->GetBinLowEdge(endBinMc), projMC->GetBinLowEdge(startBinMc)); + LOGF(info, "Calibration for %s estimator -> data = %.4f Vs MC = %.4f (MC/Data = %.4f%%)\n", estimator.Data(), meanMult_Data, meanMult_MC, (meanMult_MC - meanMult_Data) * 100 / meanMult_Data); + LOGF(info, "Calibration for %s estimator -> N entries Data = %g Vs N entries MC = %g\n", estimator.Data(), projData->Integral(), projMC->Integral()); + for (int ibin = 1; ibin <= h1dCalib->GetNbinsX(); ibin++) { + if (ibin <= startBinMc && ibin >= endBinMc) { + h1dCalib->SetBinContent(ibin, percentile_center[i]); + } + } + startBinMc = endBinMc; + } // End loop over centrality bins + } else { + for (int i = 0; i < nCentBins; i++) { + int irev = nCentBins - i; + percentile_center[i] = (centralityBins[irev - 1] + centralityBins[irev]) / 2; + epercentile_center[i] = (centralityBins[irev] - centralityBins[irev - 1]) / 2; + } + int startBinMc = 1; + for (int i = 0; i < nCentBins; i++) { // Loop over centrality bins + // start from the end (from the low multiplicity collisions) + int irev = nCentBins - i; + TH1D* projData = h2dMultVsCent_Data->ProjectionY(Form("projData_%d", i), h2dMultVsCent_Data->GetXaxis()->FindBin(centralityBins[irev - 1] + 1e-5), + h2dMultVsCent_Data->GetXaxis()->FindBin(centralityBins[irev] - 1e-5)); + + double meanMult_Data = projData->GetMean(); + + double meanMult_MC = -1; + double diffMultDataVsMC = 1e+09; + int endBinMc = h1dCalib->GetNbinsX(); + for (int j = startBinMc + 1; j <= h1dCalib->GetNbinsX(); j++) { + // Loop over MC bins + TH1D* projMC = h2dMultRecoVsMultGen_MC->ProjectionY("", startBinMc, j); + int nEntries = projMC->Integral(); + double meanMC = projMC->GetMean(); + double ldiff = std::abs(meanMC - meanMult_Data); + // std::cout << ldiff << std::endl; + + // if less than 100 entries for estimating the mean, do not consider it + if (nEntries < baseOpts.minEntries) + continue; + + // Find the minimum difference and corresponding MC bin + if (ldiff < diffMultDataVsMC) { + diffMultDataVsMC = ldiff; + meanMult_MC = meanMC; + endBinMc = j; + } + } + if (i == nCentBins - 1) { + endBinMc = h2dMultRecoVsMultGen_MC->GetNbinsX(); + } + + TH1D* projMC = h2dMultRecoVsMultGen_MC->ProjectionY(Form("projMC_%d", i), startBinMc, endBinMc); + + // Refill the per-class mean from the underlying (data/MC) multiplicity distributions, bin by + // bin, so the TProfile accumulates real sum(w)/sum(wy)/sum(wy^2) rather than a precomputed mean. + if (hPVData) { + for (int ybin = 1; ybin <= projData->GetNbinsX(); ybin++) { + double w = projData->GetBinContent(ybin); + if (w > 0) { + hPVData->Fill(percentile_center[i], projData->GetBinCenter(ybin), w); + } + } + } + if (hPVMC) { + for (int ybin = 1; ybin <= projMC->GetNbinsX(); ybin++) { + double w = projMC->GetBinContent(ybin); + if (w > 0) { + hPVMC->Fill(percentile_center[i], projMC->GetBinCenter(ybin), w); + } + } + } + + LOGF(info, "Calibration for %s estimator -> Data centrality bin %g-%g%%\n", estimator.Data(), centralityBins[irev - 1], centralityBins[irev]); + LOGF(info, "Calibration for %s estimator -> MC multiplicity range %g-%g\n", estimator.Data(), projMC->GetBinLowEdge(startBinMc), projMC->GetBinLowEdge(endBinMc + 1)); + LOGF(info, "Calibration for %s estimator -> data = %.4f Vs MC = %.4f (MC/Data = %.4f%%)\n", estimator.Data(), meanMult_Data, meanMult_MC, (meanMult_MC - meanMult_Data) * 100 / meanMult_Data); + LOGF(info, "Calibration for %s estimator -> N entries Data = %g Vs N entries MC = %g\n", estimator.Data(), projData->Integral(), projMC->Integral()); + for (int ibin = 1; ibin <= h1dCalib->GetNbinsX(); ibin++) { + if (ibin <= endBinMc && ibin >= startBinMc) { + h1dCalib->SetBinContent(ibin, percentile_center[i]); + } + } + + startBinMc = endBinMc + 1; + } // End loop over centrality bins + } + + LOGF(info, "Calibration for %s estimator -> Done!\n", estimator.Data()); + + return h1dCalib; + }; + return CalibMC(name); + } + + // Loads the calibration object(s) for a single estimator: either the class-averaged TH1D (statistical + // assignment) or, in candidate-by-candidate mode, the raw joint TH2D used to sample P(centrality|genMult). + void loadEstimatorCalibration(int idx, const char* name, TH1D*& h1dOut) + { + if (!baseOpts.mEnabledTables[idx]) { + return; + } + if (baseOpts.assignCentralityPerCandidate) { + // x = reco centrality of the matched MC collision, y = generated multiplicity (|eta| < 0.5) + // filled directly by the calibration-producing task (e.g. centralityQa.cxx); used as-is, no + // mean-matching required since it is already a genuine joint distribution. + h2dCentVsGenMult[idx] = getHist(Form("hGenMultEta05VsCentrality%s", name)); + return; + } + h1dOut = baseOpts.recalibrateCentrality ? extractCentralityCalibration(idx, name, baseOpts.recalibrateMode) : getHist(Form("h1d%s", name)); + } + + template + bool initCCDB(TCCDB& ccdb, TBCs const& bcs) + { + if (!bcs.size()) { + LOGF(warn, "No BC found, skipping this DF."); + return false; // signal to skip this DF + } + + if (mRunNumber == bcs.iteratorAt(0).runNumber()) { + return true; + } + // mark this run as configured + mRunNumber = bcs.iteratorAt(0).runNumber(); + + MCCentralityCalibObjects = ccdb->template getForRun(baseOpts.path, mRunNumber); + if (MCCentralityCalibObjects) { + LOGF(info, "loaded TList with this many objects: %i", MCCentralityCalibObjects->GetEntries()); + } + + loadEstimatorCalibration(kFV0A, "FV0A", h1dFV0A); + loadEstimatorCalibration(kFT0M, "FT0M", h1dFT0M); + loadEstimatorCalibration(kFT0A, "FT0A", h1dFT0A); + loadEstimatorCalibration(kFT0C, "FT0C", h1dFT0C); + loadEstimatorCalibration(kFT0CVariant1, "FT0CVariant1", h1dFT0CVariant1); + loadEstimatorCalibration(kFT0CVariant2, "FT0CVariant2", h1dFT0CVariant2); + loadEstimatorCalibration(kFDDM, "FDDM", h1dFDDM); + loadEstimatorCalibration(kNTPV, "NTPV", h1dNTPV); + loadEstimatorCalibration(kNGlobal, "NGlobal", h1dNGlobal); + if (baseOpts.mEnabledTables[kMFT]) { + // to be added later (multMCMFT not yet available), kept as a dedicated block on purpose + } + LOG(info) << "Fully configured for run: " << mRunNumber; + + return true; + } + + template + void fillHistograms(o2::framework::HistogramRegistry& histos, double percentile, double multiplicity, double genMultEta05) + { + histos.fill(HIST(DirList[tableIndex]) + HIST("/percentile"), percentile); + histos.fill(HIST(DirList[tableIndex]) + HIST("/percentilevsMult"), percentile, multiplicity); + if (baseOpts.assignCentralityPerCandidate) { + // QA of the sampling itself: sampled percentile vs the generated multiplicity it was actually keyed + // on (registered by registerCandidateQAHistos()), so it can be compared to the input calibration. + histos.fill(HIST(DirList[tableIndex]) + HIST("/percentileVsGenMultEta05"), percentile, genMultEta05); + } + } + + // Combines the run number, a per-DF collision sequence number and the estimator index into a single RNG + // seed, so that a given generated collision always draws the same sampled centrality for a given + // estimator, regardless of processing order (note: the sequence number is only unique within one DF, + // it is not a persistent table index). + // This is a simple hash-combine, not a cryptographic hash: the strides just keep the three fields from + // landing on the same seed value for the collision counts typically seen in one run, they don't + // guarantee it. A rare accidental collision only means two unrelated collisions share a random draw, + // which has no systematic effect on the sampled distribution. + ULong64_t computeSamplingSeed(int64_t collisionIndex, int tableIdx) const + { + static constexpr ULong64_t kCollisionStride = 131ull; // > kNestimators, so tableIdx can't alias into the collision term + static constexpr ULong64_t kRunStride = 1000003ull; // prime, well above kCollisionStride * (typical collisions per DF) + return baseOpts.centralitySamplingSeed.value + kRunStride * static_cast(mRunNumber) + + kCollisionStride * static_cast(collisionIndex) + static_cast(tableIdx); + } + + //__________________________________________________ + template + void dataProcess(TCCDB& ccdb, THistoRegistry& histos, TBCs const& bcs, const HasMcMults auto& mcCollisions, TProducts& products) + { + if (nEnabledTables == 0) { + return; // fully suppressed + } + + if (!initCCDB(ccdb, bcs)) + return; + + // Local per-DF sequence number used only to seed the candidate-by-candidate sampling below: not every + // joined mcCollisions type carries an index column (globalIndex()), so we cannot rely on that instead. + int64_t mcCollisionCounter = 0; + for (const auto& mcCollision : mcCollisions) { + const double nFV0A = mcCollision.multMCFV0A(); + const double nFT0A = mcCollision.multMCFT0A(); + const double nFT0C = mcCollision.multMCFT0C(); + const double nFDDA = mcCollision.multMCFDDA(); + const double nFDDC = mcCollision.multMCFDDC(); + const double nGlobal = mcCollision.multMCNParticlesEta08(); + const double nGenMultEta05 = mcCollision.multMCNParticlesEta05(); + // const double nMFT = mcCollision.multMCMFT(); // to be added later + const double nFT0M = nFT0A + nFT0C; + const double nFDDM = nFDDA + nFDDC; + + auto populateTable = [&](auto& table, TH1D* h1dMultCalib, TH2D* h2dCentCalib, double multiplicity, auto tableIndex) { + double percentile = 105.0f; + if (baseOpts.mEnabledTables[tableIndex]) { + if (baseOpts.assignCentralityPerCandidate && h2dCentCalib) { + // Sample P(centrality | genMult) for this generated collision: slice h2dCentCalib at the + // y-bin (generated mult |eta|<0.5) matching this collision and draw from that 1-D distribution. + fRandomSampler.SetSeed(computeSamplingSeed(mcCollisionCounter, tableIndex.value)); + int yBin = h2dCentCalib->GetYaxis()->FindBin(nGenMultEta05); + std::unique_ptr pdf{h2dCentCalib->ProjectionX(Form("pdfCent_%d", tableIndex.value), yBin, yBin)}; + if (pdf->Integral() > 0) { + percentile = pdf->GetRandom(&fRandomSampler); + } + fillHistograms(histos, percentile, multiplicity, nGenMultEta05); + } else if (h1dMultCalib) { + int bin = h1dMultCalib->FindBin(multiplicity); + percentile = h1dMultCalib->GetBinContent(bin); + fillHistograms(histos, percentile, multiplicity, nGenMultEta05); + } + table(percentile); + } + return percentile; + }; + + populateTable(products.centFV0A, h1dFV0A, h2dCentVsGenMult[kFV0A], nFV0A, std::integral_constant{}); + populateTable(products.centFT0M, h1dFT0M, h2dCentVsGenMult[kFT0M], nFT0M, std::integral_constant{}); + populateTable(products.centFT0A, h1dFT0A, h2dCentVsGenMult[kFT0A], nFT0A, std::integral_constant{}); + populateTable(products.centFT0C, h1dFT0C, h2dCentVsGenMult[kFT0C], nFT0C, std::integral_constant{}); + populateTable(products.centFT0CVariant1, h1dFT0CVariant1, h2dCentVsGenMult[kFT0CVariant1], nFT0C, std::integral_constant{}); + populateTable(products.centFT0CVariant2, h1dFT0CVariant2, h2dCentVsGenMult[kFT0CVariant2], nFT0C, std::integral_constant{}); + populateTable(products.centFDDM, h1dFDDM, h2dCentVsGenMult[kFDDM], nFDDM, std::integral_constant{}); + populateTable(products.centNTPV, h1dNTPV, h2dCentVsGenMult[kNTPV], nGlobal, std::integral_constant{}); + populateTable(products.centNGlobal, h1dNGlobal, h2dCentVsGenMult[kNGlobal], nGlobal, std::integral_constant{}); + // populateTable(products.centMFT, h1dMFT, h2dCentVsGenMult[kMFT], nMFT, std::integral_constant{}); // to be added later + + mcCollisionCounter++; + } + } +}; // end mcCentralityModule + +} // namespace mccentrality +} // namespace pwglf +} // namespace o2 + +#endif // PWGLF_UTILS_MCCENTRALITYMODULE_H_ diff --git a/PWGLF/Utils/mcParticle.h b/PWGLF/Utils/mcParticle.h index ce6ff18989b..0209ff4e500 100644 --- a/PWGLF/Utils/mcParticle.h +++ b/PWGLF/Utils/mcParticle.h @@ -130,7 +130,12 @@ class PIDExtended static constexpr ID XiCCPlusPlus = PIDCounts + 36; static constexpr ID XiCPlus = PIDCounts + 37; static constexpr ID XiC0 = PIDCounts + 38; - static constexpr ID NIDsTot = PIDCounts + 39; + static constexpr ID Kstar = PIDCounts + 39; + static constexpr ID KstarPM = PIDCounts + 40; + static constexpr ID Kshort = PIDCounts + 41; + static constexpr ID Xi1530 = PIDCounts + 42; + static constexpr ID Lambda1520 = PIDCounts + 43; + static constexpr ID NIDsTot = PIDCounts + 44; static constexpr const char* sNames[NIDsTot + 1] = { o2::track::pid_constants::sNames[Electron], // Electron @@ -191,6 +196,11 @@ class PIDExtended "XiCCPlusPlus", // XiCCPlusPlus "XiCPlus", // XiCPlus "XiC0", // XiC0 + "Kstar", // Kstar + "KstarPM", // KstarPM + "Kshort", // Kshort + "Xi1530", // Xi1530 + "Lambda1520", // Lambda1520 nullptr}; static std::vector arrayNames() @@ -325,6 +335,24 @@ class PIDExtended return XiCPlus; case o2::constants::physics::Pdg::kXiC0: return XiC0; + case o2::constants::physics::Pdg::kK0Star892: + return Kstar; + case -o2::constants::physics::Pdg::kK0Star892: + return Kstar; + case o2::constants::physics::Pdg::kKPlusStar892: + return KstarPM; + case -o2::constants::physics::Pdg::kKPlusStar892: + return KstarPM; + case 310: + return Kshort; + case 3324: + return Xi1530; + case -3324: + return Xi1530; + case 3124: + return Lambda1520; + case -3124: + return Lambda1520; default: LOG(debug) << "Cannot identify particle with PDG code " << particle.pdgCode(); break; diff --git a/PWGLF/Utils/nucleiUtils.h b/PWGLF/Utils/nucleiUtils.h index 773a34c27f1..4d06854cefb 100644 --- a/PWGLF/Utils/nucleiUtils.h +++ b/PWGLF/Utils/nucleiUtils.h @@ -13,6 +13,7 @@ #define PWGLF_UTILS_NUCLEIUTILS_H_ #include "Common/CCDB/EventSelectionParams.h" +#include "Common/Core/PID/PIDTOF.h" #include "Common/DataModel/Centrality.h" #include "Common/DataModel/PIDResponseITS.h" @@ -24,7 +25,6 @@ #include #include #include -#include #include #include @@ -122,6 +122,8 @@ struct SlimCandidate { uint64_t mcProcess = TMCProcess::kPNoProcess; float nsigmaTpc = -999.f; float nsigmaTof = -999.f; + float vx = -999.f; // production vertex x coordinate + float vy = -999.f; }; enum Species { @@ -148,6 +150,21 @@ enum Flags { kIsSecondaryFromWeakDecay = BIT(11) /// the last 4 bits are reserved for the PID in tracking }; +enum QcFlags { + kQcHasReconstructedCollision = BIT(0), + kQcPlaceholder0 = BIT(1), /// placeholdedrs + kQcPlaceholder1 = BIT(2), /// placeholdedrs + kQcPlaceholder2 = BIT(3), /// placeholdedrs + kQcPlaceholder3 = BIT(4), /// placeholdedrs + kQcHasTOF = BIT(5), + kQcHasTRD = BIT(6), + kQcIsAmbiguous = BIT(7), /// just a placeholder now + kQcITSrof = BIT(8), + kQcIsPhysicalPrimary = BIT(9), /// MC flags starting from the second half of the short + kQcIsSecondaryFromMaterial = BIT(10), + kQcIsSecondaryFromWeakDecay = BIT(11) /// the last 4 bits are reserved for the PID in tracking +}; + constexpr int getSpeciesFromPdg(int pdg) { switch (std::abs(pdg)) { @@ -290,13 +307,16 @@ enum evSel { kIsGoodZvtxFT0vsPV, kIsGoodITSLayersAll, kIsEPtriggered, + kNoCollInRofStandard, + kNoHighMultCollInPrevRof, + kNoCollInTimeRangeStandard, kNevSels }; static const std::vector eventSelectionTitle{"Event selections"}; -static const std::vector eventSelectionLabels{"TVX", "Z vtx", "TF border", "ITS ROF border", "No same-bunch pile-up", "kIsGoodZvtxFT0vsPV", "isGoodITSLayersAll", "isEPtriggered"}; +static const std::vector eventSelectionLabels{"TVX", "Z vtx", "TF border", "ITS ROF border", "No same-bunch pile-up", "kIsGoodZvtxFT0vsPV", "isGoodITSLayersAll", "isEPtriggered", "No collision in ROF standard", "No high-multiplicity collision in previous ROF", "No collision in time range standard"}; -constexpr int EvSelDefault[8][1]{ +constexpr int EvSelDefault[evSel::kNevSels][1]{ {1}, {1}, {0}, @@ -304,11 +324,28 @@ constexpr int EvSelDefault[8][1]{ {0}, {0}, {0}, + {0}, + {0}, + {0}, {0}}; -template // move to nucleiUtils -bool eventSelection(const Tcollision& collision, o2::framework::HistogramRegistry& registry, o2::framework::LabeledArray eventSelections, const float cutVertex) +template +bool eventSelection(const Tcollision& collision, o2::framework::HistogramRegistry& registry, const o2::framework::LabeledArray& eventSelections, const float cutVertex, uint32_t& selectionFlag) { + selectionFlag = 0; + bool isSelected = true; + auto checkCut = [&](int selIndex, bool pass) { + if (pass) { + selectionFlag |= (1u << selIndex); + } + if (eventSelections.get(selIndex) && !pass) { + isSelected = false; + } + if (isSelected) { + registry.fill(HIST("hEventSelections"), selIndex + 1); + } + }; + if (!registry.contains(HIST("hVtxZBefore"))) { registry.add("hVtxZBefore", "Vertex distribution in Z before selections;Z (cm)", {o2::framework::HistType::kTH1F, {{400, -20.0, 20.0}}}); } @@ -321,53 +358,32 @@ bool eventSelection(const Tcollision& collision, o2::framework::HistogramRegistr registry.fill(HIST("hEventSelections"), 0); registry.fill(HIST("hVtxZBefore"), collision.posZ()); - if (eventSelections.get(evSel::kTVX) && !collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)) { - return false; - } - registry.fill(HIST("hEventSelections"), evSel::kTVX + 1); + checkCut(evSel::kTVX, collision.selection_bit(o2::aod::evsel::kIsTriggerTVX)); + checkCut(evSel::kZvtx, std::abs(collision.posZ()) <= cutVertex); + checkCut(evSel::kTFborder, collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)); + checkCut(evSel::kITSROFborder, collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)); + checkCut(evSel::kNoSameBunchPileup, collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)); + checkCut(evSel::kIsGoodZvtxFT0vsPV, collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)); + checkCut(evSel::kIsGoodITSLayersAll, collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)); - if (eventSelections.get(evSel::kZvtx) && std::abs(collision.posZ()) > cutVertex) { - return false; + if constexpr (requires { collision.triggereventep(); }) { + checkCut(evSel::kIsEPtriggered, collision.triggereventep()); } - registry.fill(HIST("hEventSelections"), evSel::kZvtx + 1); - if (eventSelections.get(evSel::kTFborder) && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { - return false; - } - registry.fill(HIST("hEventSelections"), evSel::kTFborder + 1); - - if (eventSelections.get(evSel::kITSROFborder) && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { - return false; - } - registry.fill(HIST("hEventSelections"), evSel::kITSROFborder + 1); + checkCut(evSel::kNoCollInRofStandard, collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)); + checkCut(evSel::kNoHighMultCollInPrevRof, collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)); + checkCut(evSel::kNoCollInTimeRangeStandard, collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)); - if (eventSelections.get(evSel::kNoSameBunchPileup) && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { - return false; - } - registry.fill(HIST("hEventSelections"), evSel::kNoSameBunchPileup + 1); - - if (eventSelections.get(evSel::kIsGoodZvtxFT0vsPV) && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { - return false; - } - registry.fill(HIST("hEventSelections"), evSel::kIsGoodZvtxFT0vsPV + 1); - - if (eventSelections.get(evSel::kIsGoodITSLayersAll) && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { - return false; - } - registry.fill(HIST("hEventSelections"), evSel::kIsGoodITSLayersAll + 1); - - if constexpr ( - requires { - collision.triggereventep(); - }) { - if (eventSelections.get(evSel::kIsEPtriggered) && !collision.triggereventep()) { - return false; - } - registry.fill(HIST("hEventSelections"), evSel::kIsEPtriggered + 1); - } registry.fill(HIST("hVtxZ"), collision.posZ()); - return true; + return isSelected; +} + +template +bool eventSelection(const Tcollision& collision, o2::framework::HistogramRegistry& registry, o2::framework::LabeledArray eventSelections, const float cutVertex) +{ + uint32_t dummyFlag = 0; + return eventSelection(collision, registry, eventSelections, cutVertex, dummyFlag); } /** @@ -375,16 +391,16 @@ bool eventSelection(const Tcollision& collision, o2::framework::HistogramRegistr * then fills 1. if the centrality filling fails (return = -1.) */ template -float getCentrality(Tcollision const& collision, const int centralityEstimator, std::shared_ptr hFailCentrality = nullptr) +float getCentrality(Tcollision const& collision, const int centralityEstimator, const std::shared_ptr& hFailCentrality = nullptr) { if (hFailCentrality) { hFailCentrality->Fill(0.); } if constexpr (!o2::aod::HasCentrality) { // requires aod::CentFV0As, aod::CentFT0Ms, aod::CentFT0As, aod::CentFT0Cs, aod::CentNTPVs - return -1.f; if (hFailCentrality) { hFailCentrality->Fill(1.); } + return -1.f; } if (centralityEstimator == centDetectors::kFV0A) { return collision.centFV0A(); @@ -419,15 +435,15 @@ void createHistogramRegistryNucleus(o2::framework::HistogramRegistry& registry) constexpr int index = iSpecies; if (!checkSpeciesValidity(index)) { - std::runtime_error("species contains invalid nucleus index"); + throw std::runtime_error("species contains invalid nucleus index"); } registry.add(fmt::format("{}/hTrackSelections", cNames[index]).c_str(), (fmt::format("{} track selections;", cNames[index]) + std::string("Selection step; Counts")).c_str(), o2::framework::HistType::kTH1D, {{trackSelection::kNtrackSelections, -0.5f, static_cast(trackSelection::kNtrackSelections) - 0.5f}}); registry.add(fmt::format("{}/hPtReconstructed", cNames[index]).c_str(), (fmt::format("{} - reconstructed variables;", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c}); Counts")).c_str(), o2::framework::HistType::kTH1F, {{400, -10.0f, 10.0f}}); registry.add(fmt::format("{}/h2PtVsCentralityReconstructed", cNames[index]).c_str(), (fmt::format("{} - reconstructed variables;", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c}); CentralityFT0C (%)")).c_str(), o2::framework::HistType::kTH2F, {{400, -10.0f, 10.0f}, {20, 0.0f, 100.0f}}); registry.add(fmt::format("{}/h3PhiVsEtaVsCentralityReconstructed", cNames[index]).c_str(), (fmt::format("{} - reconstructed variables;", cNames[index]) + std::string("#phi (radians); #eta; CentralityFT0C (%)")).c_str(), o2::framework::HistType::kTH3F, {{40, 0, o2::constants::math::TwoPI}, {40, -1.0f, 1.f}, {20, 0.0f, 100.0f}}); - registry.add(fmt::format("{}/h3DCAxyVsPtVsCentrality", cNames[index]).c_str(), (fmt::format(";", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c}); DCA_{xy} (cm); CentralityFT0C (%)")).c_str(), o2::framework::HistType::kTH3F, {{400, -10.0f, 10.0f}, {200, -0.5f, 0.5f}, {20, 0.0f, 100.0f}}); - registry.add(fmt::format("{}/h3DCAzVsPtVsCentrality", cNames[index]).c_str(), (fmt::format("{};", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c}); DCA_{z} (cm); CentralityFT0C (%)")).c_str(), o2::framework::HistType::kTH3F, {{400, -10.0f, 10.0f}, {200, -0.5f, 0.5f}, {20, 0.0f, 100.0f}}); + registry.add(fmt::format("{}/h3DCAxyVsPtVsCentrality", cNames[index]).c_str(), (fmt::format(";", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c}); DCA_{xy} (cm); CentralityFT0C (%)")).c_str(), o2::framework::HistType::kTH3F, {{400, -10.0f, 10.0f}, {400, -0.1f, 0.1f}, {20, 0.0f, 100.0f}}); + registry.add(fmt::format("{}/h3DCAzVsPtVsCentrality", cNames[index]).c_str(), (fmt::format("{};", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c}); DCA_{z} (cm); CentralityFT0C (%)")).c_str(), o2::framework::HistType::kTH3F, {{400, -10.0f, 10.0f}, {400, -0.1f, 0.1f}, {20, 0.0f, 100.0f}}); registry.add(fmt::format("{}/h3NsigmaTPC_preselectionVsCentrality", cNames[index]).c_str(), (fmt::format("Nsigma{} TPC distribution;", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c});") + fmt::format("n#sigma_{{TPC}}({}); CentralityFT0C (%)", cNames[index])).c_str(), o2::framework::HistType::kTH3F, {{100, -5.0f, 5.0f}, {400, -10.0f, 10.0f}, {20, 0.0f, 100.0f}}); registry.add(fmt::format("{}/h3NsigmaTPCVsCentrality", cNames[index]).c_str(), (fmt::format("Nsigma{} TPC distribution;", cNames[index]) + std::string("#it{p}_{T} / |#it{Z}| (GeV/#it{c});") + fmt::format("n#sigma_{{TPC}}({}); Centrality FT0C (%)", cNames[index])).c_str(), o2::framework::HistType::kTH3F, {{20, -5.0f, 5.0f}, {200, -5.0f, 5.0f}, {20, 0.0f, 100.0f}}); registry.add(fmt::format("{}/h3NsigmaITS_preselectionVsCentrality", cNames[index]).c_str(), (fmt::format("Nsigma{} ITS distribution;", cNames[index]) + std::string("signed #it{p}_{T} / |#it{Z}| (GeV/#it{c});") + fmt::format("n#sigma_{{ITS}}({}); Centrality FT0C (%)", cNames[index])).c_str(), o2::framework::HistType::kTH3F, {{50, -5.0f, 5.0f}, {120, -3.0f, 3.0f}, {20, 0.0f, 100.0f}}); @@ -457,7 +473,7 @@ class PidManager : mSpecies(species) { if (!checkSpeciesValidity(species)) { - std::runtime_error("species contains invalid nucleus index"); + throw std::runtime_error("species contains invalid nucleus index"); } if (!tpcBetheBlochParams) { @@ -594,11 +610,11 @@ class PidManager } private: - float mTpcBetheBlochParams[6]; + float mTpcBetheBlochParams[6] = {-999.f, -999.f, -999.f, -999.f, -999.f, -999.f}; bool mUseTpcCentralCalibration = true; // this just becomes a check for the null pointer in the parameters o2::aod::ITSResponse mResponseITS; float mMomScaling[2]{1., 0.}; - int mSpecies; + int mSpecies = -1; }; } // namespace nuclei diff --git a/PWGLF/Utils/pidTOFGeneric.h b/PWGLF/Utils/pidTOFGeneric.h index 18b3a937565..8625db1f4e8 100644 --- a/PWGLF/Utils/pidTOFGeneric.h +++ b/PWGLF/Utils/pidTOFGeneric.h @@ -68,14 +68,14 @@ struct TOFCalibConfig { } template - void getCfg(o2::framework::InitContext& initContext, const std::string name, VType& v, const std::string task) + void getCfg(o2::framework::InitContext& initContext, const std::string& name, VType& v, const std::string& task) { if (!o2::common::core::getTaskOptionValue(initContext, task, name, v, false)) { LOG(fatal) << "Could not get " << name << " from " << task << " task"; } } - void inheritFromBaseTask(o2::framework::InitContext& initContext, const std::string task = "tof-signal") + void inheritFromBaseTask(o2::framework::InitContext& initContext, const std::string& task = "tof-signal") { mInitMode = 2; getCfg(initContext, "ccdb-url", mUrl, task); diff --git a/PWGLF/Utils/rsnOutput.h b/PWGLF/Utils/rsnOutput.h index 214d6aa8118..5dfb5e30d09 100644 --- a/PWGLF/Utils/rsnOutput.h +++ b/PWGLF/Utils/rsnOutput.h @@ -28,9 +28,7 @@ #include #include -namespace o2::analysis -{ -namespace rsn +namespace o2::analysis::rsn { enum class EventType { zvertex, @@ -47,14 +45,21 @@ enum class TrackType { enum class PairType { unlikepm, unlikemp, + unlikepmInelgt0, + unlikempInelgt0, likepp, likemm, - unliketrue, + unliketruerec, + unliketruerecInelgt0, + unliketruegen, + unliketruegenInelgt0, unlikegen, - unlikegenold, + unlikegenInelgt0, mixingpm, mixingmp, - rotationpm, + rotationz, + rotation, + rotationlike, all }; @@ -80,13 +85,14 @@ enum class MixingType { none }; -MixingType mixingTypeName(std::string name) +MixingType mixingTypeName(const std::string& name) { - if (name == "ce") + if (name == "ce") { return MixingType::ce; - else if (name == "mu") + } + if (name == "mu") { return MixingType::mu; - + } return MixingType::none; } @@ -94,14 +100,15 @@ enum class SystematicsAxisType { ncl, unknown }; + namespace pair_axis { -std::vector names{"im", "pt", "mu", "ce", "ns1", "ns2", "eta", "y", "vz", "mum", "cem", "vzm"}; +const std::vector names{"im", "pt", "mu", "ce", "ns1", "ns2", "eta", "y", "vz", "mum", "cem", "vzm"}; } namespace systematic_axis { -std::vector names{"ncl"}; +const std::vector names{"ncl"}; } class Output @@ -109,22 +116,23 @@ class Output public: virtual ~Output() = default; - virtual void init(std::vector const& sparseAxes, std::vector const& allAxes, std::vector const& sysAxes, std::vector const& allAxes_sys, bool /*produceTrue*/ = false, MixingType /*eventMixing*/ = MixingType::none, bool /*produceLikesign*/ = false, bool /*produceRotational*/ = false, o2::framework::HistogramRegistry* registry = nullptr) + virtual void init(std::vector const& sparseAxes, std::vector const& allAxes, std::vector const& sysAxes, std::vector const& allAxes_sys, bool /*produceTrue*/, MixingType /*eventMixing*/, bool /*produceLikesign*/, bool /*produceRotational*/, bool /*produceInelgt0*/, o2::framework::HistogramRegistry* registry) { mHistogramRegistry = registry; - if (mHistogramRegistry == nullptr) + if (mHistogramRegistry == nullptr) { mHistogramRegistry = new o2::framework::HistogramRegistry("registry"); + } // check if all axes are added in correct order for (int i = 0; i < static_cast(PairAxisType::unknown); i++) { auto aname = *std::move(allAxes[i].name); LOGF(debug, "Check axis '%s' %d", aname.c_str(), i); - if (aname.compare(pair_axis::names[static_cast(i)])) { + if (aname != pair_axis::names[i]) { LOGF(fatal, "rsn::Output::Error: Order in allAxes is not correct !!! Expected axis '%s' and has '%s'.", aname.c_str(), pair_axis::names[static_cast(i)]); } } - PairAxisType currentType; + PairAxisType currentType = PairAxisType::unknown; for (const auto& c : sparseAxes) { currentType = type(c); if (currentType >= PairAxisType::unknown) { @@ -136,8 +144,8 @@ class Output mCurrentAxisTypes.push_back(currentType); } - if (mFillPoint != nullptr) - delete mFillPoint; + delete mFillPoint; + mFillPoint = new double[mCurrentAxisTypes.size()]; LOGF(info, "Number of axis added: %d", mCurrentAxes.size()); @@ -147,12 +155,12 @@ class Output for (int i = 0; i < static_cast(SystematicsAxisType::unknown); i++) { auto aname = *std::move(allAxes_sys[i].name); LOGF(debug, "Check axis '%s' %d", aname.c_str(), i); - if (aname.compare(systematic_axis::names[static_cast(i)])) { + if (aname != systematic_axis::names[i]) { LOGF(fatal, "rsn::Output::Error: Order in allAxes_sys is not correct !!! Expected axis '%s' and has '%s'.", aname.c_str(), systematic_axis::names[static_cast(i)]); } } - SystematicsAxisType currentTypeSys; + SystematicsAxisType currentTypeSys = SystematicsAxisType::unknown; for (const auto& c : sysAxes) { currentTypeSys = typeSys(c); if (currentTypeSys >= SystematicsAxisType::unknown) { @@ -164,8 +172,8 @@ class Output mCurrentAxisTypesSys.push_back(currentTypeSys); } - if (mFillPointSys != nullptr) - delete mFillPointSys; + delete mFillPointSys; + mFillPointSys = new double[mCurrentAxisTypesSys.size()]; LOGF(info, "Number of systematic axis added: %d", mCurrentAxesSys.size()); @@ -173,7 +181,7 @@ class Output } template - void fillSparse(const T& h, double* point) + void fillSparse(const T& h, const double* point) { int i = 0; for (const auto& at : mCurrentAxisTypes) { @@ -183,7 +191,7 @@ class Output } template - void fillSparseSys(const T& h, double* point) + void fillSparseSys(const T& h, const double* point) { int i = 0; for (const auto& at : mCurrentAxisTypesSys) { @@ -207,17 +215,24 @@ class Output virtual void fillUnlikepm(double* point) = 0; virtual void fillUnlikemp(double* point) = 0; + virtual void fillUnlikepmInelgt0(double* point) = 0; + virtual void fillUnlikempInelgt0(double* point) = 0; virtual void fillLikepp(double* point) = 0; virtual void fillLikemm(double* point) = 0; - virtual void fillUnliketrue(double* point) = 0; - virtual void fillUnlikegen(double* point) = 0; - virtual void fillUnlikegenOld(double* point) = 0; + virtual void fillUnlikeTrueRec(double* point) = 0; + virtual void fillUnlikeTrueGen(double* point) = 0; + virtual void fillUnlikeGen(double* point) = 0; + virtual void fillUnlikeTrueRecInelgt0(double* point) = 0; + virtual void fillUnlikeTrueGenInelgt0(double* point) = 0; + virtual void fillUnlikeGenInelgt0(double* point) = 0; virtual void fillMixingpm(double* point) = 0; virtual void fillMixingmp(double* point) = 0; - virtual void fillRotationpm(double* point) = 0; + virtual void fillRotationZ(double* point) = 0; + virtual void fillRotation(double* point) = 0; + virtual void fillRotationLike(double* point) = 0; virtual void fillSystematics(double* point) = 0; - PairAxisType type(std::string name) + PairAxisType type(const std::string& name) { auto it = std::find(pair_axis::names.begin(), pair_axis::names.end(), name); if (it == pair_axis::names.end()) { @@ -226,7 +241,7 @@ class Output return static_cast(std::distance(pair_axis::names.begin(), it)); } - SystematicsAxisType typeSys(std::string name) + SystematicsAxisType typeSys(const std::string& name) { auto it = std::find(systematic_axis::names.begin(), systematic_axis::names.end(), name); if (it == systematic_axis::names.end()) { @@ -235,22 +250,22 @@ class Output return static_cast(std::distance(systematic_axis::names.begin(), it)); } - std::string name(PairAxisType type) + std::string name(PairAxisType axisType) { - return pair_axis::names[(static_cast(type))]; + return pair_axis::names[(static_cast(axisType))]; } - std::string nameSys(SystematicsAxisType type) + std::string nameSys(SystematicsAxisType axisType) { - return systematic_axis::names[(static_cast(type))]; + return systematic_axis::names[(static_cast(axisType))]; } - o2::framework::AxisSpec axis(std::vector const& allAxes, PairAxisType type) + o2::framework::AxisSpec axis(std::vector const& allAxes, PairAxisType axisType) { - const o2::framework::AxisSpec unknownAxis = {1, 0., 1., "unknown axis", "unknown"}; - if (type == PairAxisType::unknown) - return unknownAxis; - return allAxes[static_cast(type)]; + if (axisType == PairAxisType::unknown) { + return {1, 0., 1., "unknown axis", "unknown"}; + } + return allAxes[static_cast(axisType)]; } protected: @@ -268,32 +283,41 @@ class Output class OutputSparse : public Output { public: - virtual void init(std::vector const& sparseAxes, std::vector const& allAxes, std::vector const& sysAxes, std::vector const& allAxes_sys, bool produceTrue = false, MixingType eventMixing = MixingType::none, bool produceLikesign = false, bool produceRotational = false, o2::framework::HistogramRegistry* registry = nullptr) + void init(std::vector const& sparseAxes, std::vector const& allAxes, std::vector const& sysAxes, std::vector const& allAxes_sys, bool produceTrue, MixingType eventMixing, bool produceLikesign, bool produceRotational, bool produceInelgt0, o2::framework::HistogramRegistry* registry) override { - Output::init(sparseAxes, allAxes, sysAxes, allAxes_sys, produceTrue, eventMixing, produceLikesign, produceRotational, registry); + Output::init(sparseAxes, allAxes, sysAxes, allAxes_sys, produceTrue, eventMixing, produceLikesign, produceRotational, produceInelgt0, registry); mHistogramRegistry->add("unlikepm", "Unlike pm", *mPairHisto); + mHistogramRegistry->add("unlikemp", "Unlike mp", *mPairHisto); + if (produceInelgt0) { + mHistogramRegistry->add("unlikepmInelgt0", "Unlike pm (INEL>0)", *mPairHisto); + mHistogramRegistry->add("unlikempInelgt0", "Unlike mp (INEL>0)", *mPairHisto); + } if (produceLikesign) { mHistogramRegistry->add("likepp", "Like PP", *mPairHisto); mHistogramRegistry->add("likemm", "Like MM", *mPairHisto); } if (produceTrue) { - mHistogramRegistry->add("unliketrue", "Unlike True", *mPairHisto); + mHistogramRegistry->add("unliketruerec", "Unlike True (Rec)", *mPairHisto); + mHistogramRegistry->add("unliketruegen", "Unlike True (Gen)", *mPairHisto); + mHistogramRegistry->add("unliketruerecInelgt0", "Unlike True (Rec) (INEL>0)", *mPairHisto); + mHistogramRegistry->add("unliketruegenInelgt0", "Unlike True (Gen) (INEL>0)", *mPairHisto); mHistogramRegistry->add("unlikegen", "Unlike Gen", *mPairHisto); - mHistogramRegistry->add("unlikegenold", "Unlike Gen Old", *mPairHisto); + mHistogramRegistry->add("unlikegenInelgt0", "Unlike Gen (INEL>0)", *mPairHisto); } if (eventMixing != MixingType::none) { mHistogramRegistry->add("mixingpm", "Event Mixing pm", *mPairHisto); mHistogramRegistry->add("mixingmp", "Event Mixing mp", *mPairHisto); } if (produceRotational) { - mHistogramRegistry->add("rotationpm", "Rotational pm", *mPairHisto); + mHistogramRegistry->add("rotationz", "Rotation around z axis", *mPairHisto); + mHistogramRegistry->add("rotation", "Momentum-axis rotation, unlike-sign", *mPairHisto); + mHistogramRegistry->add("rotationlike", "Momentum-axis rotation, like-sign", *mPairHisto); } mHistogramRegistry->add("Mapping/systematics", "Systematics mapping", *mPairHistoSys); } - virtual void - fill(EventType t, double* point) + void fill(EventType t, double* point) override { switch (t) { case EventType::zvertex: @@ -304,7 +328,7 @@ class OutputSparse : public Output } } - virtual void fill(PairType t, double* point) + void fill(PairType t, double* point) override { switch (t) { case PairType::unlikepm: @@ -319,14 +343,29 @@ class OutputSparse : public Output case PairType::likemm: fillLikemm(point); break; - case PairType::unliketrue: - fillUnliketrue(point); + case PairType::unliketruerec: + fillUnlikeTrueRec(point); break; case PairType::unlikegen: - fillUnlikegen(point); + fillUnlikeGen(point); + break; + case PairType::unliketruegen: + fillUnlikeTrueGen(point); + break; + case PairType::unlikepmInelgt0: + fillUnlikepmInelgt0(point); break; - case PairType::unlikegenold: - fillUnlikegenOld(point); + case PairType::unlikempInelgt0: + fillUnlikempInelgt0(point); + break; + case PairType::unliketruerecInelgt0: + fillUnlikeTrueRecInelgt0(point); + break; + case PairType::unliketruegenInelgt0: + fillUnlikeTrueGenInelgt0(point); + break; + case PairType::unlikegenInelgt0: + fillUnlikeGenInelgt0(point); break; case PairType::mixingpm: fillMixingpm(point); @@ -334,60 +373,94 @@ class OutputSparse : public Output case PairType::mixingmp: fillMixingmp(point); break; - case PairType::rotationpm: - fillRotationpm(point); + case PairType::rotationz: + fillRotationZ(point); + break; + case PairType::rotation: + fillRotation(point); + break; + case PairType::rotationlike: + fillRotationLike(point); break; default: break; } } - virtual void fillUnlikepm(double* point) + void fillUnlikepm(double* point) override { fillSparse(HIST("unlikepm"), point); } - virtual void fillUnlikemp(double* point) + void fillUnlikemp(double* point) override { fillSparse(HIST("unlikemp"), point); } - virtual void fillLikepp(double* point) + void fillLikepp(double* point) override { fillSparse(HIST("likepp"), point); } - virtual void fillLikemm(double* point) + void fillLikemm(double* point) override { fillSparse(HIST("likemm"), point); } - virtual void fillUnliketrue(double* point) + void fillUnlikeTrueRec(double* point) override + { + fillSparse(HIST("unliketruerec"), point); + } + void fillUnlikeTrueGen(double* point) override { - fillSparse(HIST("unliketrue"), point); + fillSparse(HIST("unliketruegen"), point); } - virtual void fillUnlikegen(double* point) + void fillUnlikeGen(double* point) override { fillSparse(HIST("unlikegen"), point); } - virtual void fillUnlikegenOld(double* point) + void fillUnlikepmInelgt0(double* point) override + { + fillSparse(HIST("unlikepmInelgt0"), point); + } + void fillUnlikempInelgt0(double* point) override + { + fillSparse(HIST("unlikempInelgt0"), point); + } + void fillUnlikeTrueRecInelgt0(double* point) override + { + fillSparse(HIST("unliketruerecInelgt0"), point); + } + void fillUnlikeTrueGenInelgt0(double* point) override + { + fillSparse(HIST("unliketruegenInelgt0"), point); + } + void fillUnlikeGenInelgt0(double* point) override { - fillSparse(HIST("unlikegenold"), point); + fillSparse(HIST("unlikegenInelgt0"), point); } - virtual void fillMixingpm(double* point) + + void fillMixingpm(double* point) override { fillSparse(HIST("mixingpm"), point); } - virtual void fillMixingmp(double* point) + void fillMixingmp(double* point) override { fillSparse(HIST("mixingmp"), point); } - virtual void fillRotationpm(double* point) + void fillRotationZ(double* point) override + { + fillSparse(HIST("rotationz"), point); + } + void fillRotation(double* point) override + { + fillSparse(HIST("rotation"), point); + } + void fillRotationLike(double* point) override { - fillSparse(HIST("rotationpm"), point); + fillSparse(HIST("rotationlike"), point); } - virtual void fillSystematics(double* point) + void fillSystematics(double* point) override { fillSparse(HIST("Mapping/systematics"), point); } }; -} // namespace rsn -} // namespace o2::analysis +} // namespace o2::analysis::rsn #endif // PWGLF_UTILS_RSNOUTPUT_H_ diff --git a/PWGLF/Utils/strangenessBuilderHelper.cxx b/PWGLF/Utils/strangenessBuilderHelper.cxx new file mode 100644 index 00000000000..6e438bc5fac --- /dev/null +++ b/PWGLF/Utils/strangenessBuilderHelper.cxx @@ -0,0 +1,21 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file strangenessBuilderHelper.cxx +/// \brief Helper file providing the compilation command for the strangenessBuilderHelper header. +/// The module implementation is provided in the corresponding header and maintained by David Dobrigkeit Chinellato; +/// this file only provides the compilation unit. +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Vít Kučera , Inha University + +// o2-linter: disable=name/workflow-file (not a workflow file) +#include "strangenessBuilderHelper.h" // IWYU pragma: keep diff --git a/PWGLF/Utils/strangenessBuilderHelper.h b/PWGLF/Utils/strangenessBuilderHelper.h index c6e2674079d..6d5b525dcd4 100644 --- a/PWGLF/Utils/strangenessBuilderHelper.h +++ b/PWGLF/Utils/strangenessBuilderHelper.h @@ -16,28 +16,28 @@ #define HomogeneousField #endif +#include "Common/Core/RecoDecay.h" #include "Common/Core/trackUtilities.h" #include "Tools/KFparticle/KFUtilities.h" #include #include #include +#include #include #include #include #include // IWYU pragma: keep -#include #include #include -#include -#include #include #include #include #include +#include #include #include @@ -253,6 +253,7 @@ class strangenessBuilderHelper fitter.setMaxChi2(1e9); fitter.setUseAbsDCA(true); fitter.setWeightedFinalPCA(false); + fitter.setOldMode(true); // enable old convariance matrix calculation until this has been fully tested v0selections.minCrossedRows = -1; v0selections.dcanegtopv = -1.0f; @@ -1059,6 +1060,11 @@ class strangenessBuilderHelper return false; } + // Calculate V0 mass before mass constraint + float MLambda, SigmaLambda; + KFV0.GetMass(MLambda, SigmaLambda); + cascade.kfMLambda = MLambda; + if (kfUseV0MassConstraint) { KFV0.SetNonlinearMassConstraint(o2::constants::physics::MassLambda); } @@ -1117,9 +1123,20 @@ class strangenessBuilderHelper cascade = {}; return false; } + + // Calculate masses a priori before potentially applying mass constraint + // --> this is the invariant mass of the decay products, not the mass-constrained value + float MXi, SigmaXi, MOmega, SigmaOmega; + KFXi.GetMass(MXi, SigmaXi); + KFOmega.GetMass(MOmega, SigmaOmega); + cascade.massXi = MXi; + cascade.massOmega = MOmega; + if (kfUseCascadeMassConstraint) { // set mass constraint if requested // WARNING: this is only adequate for decay chains, i.e. XiC -> Xi or OmegaC -> Omega + // WARNING: be aware of the fact that if enabled, the stored particle momentum and covariance matrix will be mass-constrained + // while the stored mass will be the invariant mass of the decay products (i.e. not mass-constrained) KFXi.SetNonlinearMassConstraint(o2::constants::physics::MassXiMinus); KFOmega.SetNonlinearMassConstraint(o2::constants::physics::MassOmegaMinus); } @@ -1215,15 +1232,6 @@ class strangenessBuilderHelper } cascade.pointingAngle = TMath::ACos(cosPA); - // Calculate masses a priori - float MLambda, SigmaLambda, MXi, SigmaXi, MOmega, SigmaOmega; - KFV0.GetMass(MLambda, SigmaLambda); - KFXi.GetMass(MXi, SigmaXi); - KFOmega.GetMass(MOmega, SigmaOmega); - cascade.kfMLambda = MLambda; - cascade.massXi = MXi; - cascade.massOmega = MOmega; - // KF Cascade covariance matrix std::array covCascKF; for (int i = 0; i < 21; i++) { // get covariance matrix elements (lower triangle) diff --git a/PWGLF/Utils/strangenessBuilderModule.cxx b/PWGLF/Utils/strangenessBuilderModule.cxx new file mode 100644 index 00000000000..f5390ebc17b --- /dev/null +++ b/PWGLF/Utils/strangenessBuilderModule.cxx @@ -0,0 +1,21 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file strangenessBuilderModule.cxx +/// \brief Helper file providing the compilation command for the strangenessBuilderModule header. +/// The module implementation is provided in the corresponding header and maintained by David Dobrigkeit Chinellato; +/// this file only provides the compilation unit. +/// +/// \author David Dobrigkeit Chinellato , Austrian Academy of Sciences & MBI +/// \author Vít Kučera , Inha University + +// o2-linter: disable=name/workflow-file (not a workflow file) +#include "strangenessBuilderModule.h" // IWYU pragma: keep diff --git a/PWGLF/Utils/strangenessBuilderModule.h b/PWGLF/Utils/strangenessBuilderModule.h index 2af133c228b..45b28701685 100644 --- a/PWGLF/Utils/strangenessBuilderModule.h +++ b/PWGLF/Utils/strangenessBuilderModule.h @@ -19,11 +19,19 @@ #include "PWGLF/DataModel/LFStrangenessTables.h" // IWYU pragma: keep #include "PWGLF/Utils/strangenessBuilderHelper.h" +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/RecoDecay.h" #include "Common/Core/TPCVDriftManager.h" +#include +#include #include #include +#include +#include #include +#include #include #include #include @@ -35,13 +43,16 @@ #include #include +#include #include #include #include #include +#include #include #include +#include #include #include @@ -182,15 +193,51 @@ enum tableIndex { kV0Indices = 0, kCascFoundTags, nTables }; +// statics necessary for the configurables in this namespace +static constexpr int nPreselectParameters = 1; +static const std::vector preselectParticleNames{ + "Photon", + "K0s", + "Lambda", + "AntiLambda", + "XiMinus", + "XiPlus", + "OmegaMinus", + "OmegaPlus"}; + +static constexpr int nPreselectParticles = 8; +static const int defaultPreselectParameters[nPreselectParticles][nPreselectParameters]{ + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}, + {0}}; + +// table index : match order above +enum preselectParticleIndex { kGamma = 0, + kK0Short, + kLambda, + kAntiLambda, + kXiMinus, + kXiPlus, + kOmegaMinus, + kOmegaPlus, + nPartTypes }; + enum V0PreSelection : uint8_t { selGamma = 0, selK0Short, selLambda, - selAntiLambda }; + selAntiLambda, + nSelV0Types }; enum CascPreSelection : uint8_t { selXiMinus = 0, selXiPlus, selOmegaMinus, - selOmegaPlus }; + selOmegaPlus, + nSelCascTypes }; static constexpr float defaultK0MassWindowParameters[1][4] = {{2.81882e-03, 1.14057e-03, 1.72138e-03, 5.00262e-01}}; static constexpr float defaultLambdaWindowParameters[1][4] = {{1.17518e-03, 1.24099e-04, 5.47937e-03, 3.08009e-01}}; @@ -288,6 +335,9 @@ struct coreConfigurables : o2::framework::ConfigurableGroup { // test the possibility of refitting with material corrections (DCA Fitter option) o2::framework::Configurable refitWithMaterialCorrection{"refitWithMaterialCorrection", false, "do refit after material corrections were applied"}; + + // enable old convariance matrix calculation until this has been fully tested + o2::framework::Configurable useOldModeDCAFitter{"useOldModeDCAFitter", true, "Use old mode for DCA fitter?"}; }; // strangenessBuilder: V0 building options @@ -295,6 +345,7 @@ struct v0Configurables : o2::framework::ConfigurableGroup { std::string prefix = "v0BuilderOpts"; o2::framework::Configurable generatePhotonCandidates{"generatePhotonCandidates", false, "generate gamma conversion candidates (V0s using TPC-only tracks)"}; o2::framework::Configurable moveTPCOnlyTracks{"moveTPCOnlyTracks", true, "if dealing with TPC-only tracks, move them according to TPC drift / time info"}; + o2::framework::Configurable useSideBasedCorrection{"useSideBasedCorrection", false, "when moving TPC-only tracks, use TPC side flags (with legacy fallback) instead of tgl sign"}; // baseline conditionals of V0 building o2::framework::Configurable minCrossedRows{"minCrossedRows", 50, "minimum TPC crossed rows for daughter tracks"}; @@ -357,9 +408,11 @@ struct cascadeConfigurables : o2::framework::ConfigurableGroup { // preselection options struct preSelectOpts : o2::framework::ConfigurableGroup { std::string prefix = "preSelectOpts"; - o2::framework::Configurable preselectOnlyDesiredV0s{"preselectOnlyDesiredV0s", false, "preselect only V0s with compatible TPC PID and mass info"}; - o2::framework::Configurable preselectOnlyDesiredCascades{"preselectOnlyDesiredCascades", false, "preselect only Cascades with compatible TPC PID and mass info"}; + o2::framework::Configurable> preselectedSpecies{"preselectedSpecies", + {defaultPreselectParameters[0], nPreselectParticles, nPreselectParameters, preselectParticleNames, parameterNames}, + "Preselect this species with compatible TPC PID and mass info: 0/1 is false/true"}; + std::vector mEnabledPreselectedSpecies; // Vector of enabled preselected particle species // lifetime preselection options // apply lifetime cuts to V0 and cascade candidates // unit of measurement: centimeters @@ -382,6 +435,22 @@ struct preSelectOpts : o2::framework::ConfigurableGroup { o2::framework::Configurable maxTPCpidNsigma{"maxTPCpidNsigma", 5.0, "Maximum TPC PID N sigma (in abs value)"}; }; +// collision preselection options +struct eventSelectOpts : o2::framework::ConfigurableGroup { + std::string prefix = "eventSelectOpts"; // JSON group name + o2::framework::Configurable fillOnlySelectedCollisions{"fillOnlySelectedCollisions", false, "Fill only tables for selected collisions"}; + o2::framework::Configurable requireTriggerTVX{"requireTriggerTVX", false, "require FT0 vertex (acceptable FT0C-FT0A time difference) at trigger level (Run 3 only)"}; + o2::framework::Configurable rejectITSROFBorder{"rejectITSROFBorder", false, "reject events at ITS ROF border (Run 3 only)"}; + o2::framework::Configurable rejectTFBorder{"rejectTFBorder", false, "reject events at TF border (Run 3 only)"}; + o2::framework::Configurable rejectSameBunchPileup{"rejectSameBunchPileup", false, "reject collisions in case of pileup with another collision in the same foundBC (Run 3 only)"}; + o2::framework::Configurable maxZVtxPosition{"maxZVtxPosition", 10., "max Z vtx position"}; + + o2::framework::Configurable cfgApplyRCTrequirement{"cfgApplyRCTrequirement", false, "Apply RCT requirement?"}; + o2::framework::Configurable cfgRCTLabel{"cfgRCTLabel", "", "Which detector condition requirements? (CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo)"}; + o2::framework::Configurable cfgCheckZDC{"cfgCheckZDC", false, "Include ZDC flags in the bit selection (for Pb-Pb only)"}; + o2::framework::Configurable cfgTreatLimitedAcceptanceAsBad{"cfgTreatLimitedAcceptanceAsBad", false, "reject all events where the detectors relevant for the specified Runlist are flagged as LimitedAcceptance"}; +}; + class BuilderModule { public: @@ -390,6 +459,37 @@ class BuilderModule // constructor } + template + bool isCollisionAccepted(TCollision collision) + // check whether the collision passes our collision selections + { + if (eventSelectOpts.requireTriggerTVX && !collision.selection_bit(aod::evsel::kIsTriggerTVX)) { + return false; + } + + if (eventSelectOpts.rejectITSROFBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return false; + } + + if (eventSelectOpts.rejectTFBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return false; + } + + if (std::abs(collision.posZ()) > eventSelectOpts.maxZVtxPosition) { + return false; + } + + if (eventSelectOpts.rejectSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + return false; + } + + if (!eventSelectOpts.cfgApplyRCTrequirement && !rctFlagsChecker(collision)) { + return false; + } + + return true; + } + // mass windows float getMassSigmaK0Short(float pt) { @@ -521,9 +621,12 @@ class BuilderModule o2::pwglf::strangenessbuilder::v0Configurables v0BuilderOpts; o2::pwglf::strangenessbuilder::cascadeConfigurables cascadeBuilderOpts; o2::pwglf::strangenessbuilder::preSelectOpts preSelectOpts; + o2::pwglf::strangenessbuilder::eventSelectOpts eventSelectOpts; + + o2::aod::rctsel::RCTFlagsChecker rctFlagsChecker; - template - void init(TBaseConfigurables const& inputBaseOpts, TV0Configurables const& inputV0BuilderOpts, TCascadeConfigurables const& inputCascadeBuilderOpts, TPreSelOpts const& inputPreSelectOpts, THistoRegistry& histos, TInitContext& context) + template + void init(TBaseConfigurables const& inputBaseOpts, TV0Configurables const& inputV0BuilderOpts, TCascadeConfigurables const& inputCascadeBuilderOpts, TPreSelOpts const& inputPreSelectOpts, TPreEventSelOpts const& inputEventSelectOpts, THistoRegistry& histos, TInitContext& context) { // read in configurations from the task where it's used // could be grouped even further, but should work @@ -531,6 +634,7 @@ class BuilderModule v0BuilderOpts = inputV0BuilderOpts; cascadeBuilderOpts = inputCascadeBuilderOpts; preSelectOpts = inputPreSelectOpts; + eventSelectOpts = inputEventSelectOpts; baseOpts.mEnabledTables.resize(nTables, 0); @@ -546,6 +650,7 @@ class BuilderModule if (f == 1) { baseOpts.mEnabledTables[i] = 1; listOfRequestors[i] = "manual enabling"; + nEnabledTables++; } if (f == -1) { // autodetect this table in other devices @@ -559,6 +664,8 @@ class BuilderModule tableNameWithVersion += Form("_%03d", version); } if (input.matcher.binding == tableNameWithVersion) { + if (device.name == "strangenesstofpid") + continue; LOGF(info, "Device %s has subscribed to %s (version %i)", device.name, tableNames[i], version); listOfRequestors[i].Append(Form("%s ", device.name.c_str())); baseOpts.mEnabledTables[i] = 1; @@ -586,7 +693,16 @@ class BuilderModule hDeduplicationStatistics->GetXaxis()->SetBinLabel(2, "Deduplicated V0s"); } - if (preSelectOpts.preselectOnlyDesiredV0s.value == true) { + preSelectOpts.mEnabledPreselectedSpecies.resize(nPreselectParticles, 0); + LOGF(info, "Checking if preselections on V0s and cascades are required"); + for (int i = 0; i < nPreselectParticles; i++) { + if (preSelectOpts.preselectedSpecies->get(preselectParticleNames[i].c_str(), "enable")) { + preSelectOpts.mEnabledPreselectedSpecies[i] = 1; + LOGF(info, " ---> Preselection of %s enabled", preselectParticleNames[i]); + } + } + + if (preSelectOpts.mEnabledPreselectedSpecies[kGamma] || preSelectOpts.mEnabledPreselectedSpecies[kK0Short] || preSelectOpts.mEnabledPreselectedSpecies[kLambda] || preSelectOpts.mEnabledPreselectedSpecies[kAntiLambda]) { auto hPreselectionV0s = histos.template add("hPreselectionV0s", "hPreselectionV0s", o2::framework::HistType::kTH1D, {{16, -0.5f, 15.5f}}); hPreselectionV0s->GetXaxis()->SetBinLabel(1, "Not preselected"); hPreselectionV0s->GetXaxis()->SetBinLabel(2, "#gamma"); @@ -606,7 +722,7 @@ class BuilderModule hPreselectionV0s->GetXaxis()->SetBinLabel(16, "#gamma, K^{0}_{S}, #Lambda, #bar{#Lambda}"); } - if (preSelectOpts.preselectOnlyDesiredCascades.value == true) { + if (preSelectOpts.mEnabledPreselectedSpecies[kXiMinus] || preSelectOpts.mEnabledPreselectedSpecies[kXiPlus] || preSelectOpts.mEnabledPreselectedSpecies[kOmegaMinus] || preSelectOpts.mEnabledPreselectedSpecies[kOmegaPlus]) { auto hPreselectionCascades = histos.template add("hPreselectionCascades", "hPreselectionCascades", o2::framework::HistType::kTH1D, {{16, -0.5f, 15.5f}}); hPreselectionCascades->GetXaxis()->SetBinLabel(1, "Not preselected"); hPreselectionCascades->GetXaxis()->SetBinLabel(2, "#Xi^{-}"); @@ -712,6 +828,16 @@ class BuilderModule // Set option to refit with material corrections straHelper.fitter.setRefitWithMatCorr(baseOpts.refitWithMaterialCorrection.value); + + // set option to enabled the old mode + straHelper.fitter.setOldMode(baseOpts.useOldModeDCAFitter.value); + + // Initialise the RCTFlagsChecker + if (eventSelectOpts.cfgApplyRCTrequirement) { + rctFlagsChecker.init(eventSelectOpts.cfgRCTLabel.value, eventSelectOpts.cfgCheckZDC, eventSelectOpts.cfgTreatLimitedAcceptanceAsBad); + } + + mVDriftMgr.setUseSideBasedCorrection(v0BuilderOpts.useSideBasedCorrection.value); } // for sorting @@ -728,10 +854,34 @@ class BuilderModule return idx; } - template - bool initCCDB(TCCDB& ccdb, aod::BCsWithTimestamps const& bcs, TCollisions const& collisions) + // Feed the V-drift manager from the aod::TpcCalibCCDBObjects column when the BC table + // carries it, else fall back to a CCDB query. Lets migrated and un-migrated tasks share + // this module unchanged. + template + void updateVDrift(TCollision const& collision) + { + auto const& bc = collision.template bc_as(); + if constexpr (requires { bc.vdriftTgl(); }) { + mVDriftMgr.update(bc.vdriftTgl()); + } else { + mVDriftMgr.update(bc.timestamp()); + } + } + + // Overload for tasks whose BC table carries the V-drift CCDB column: nothing in here + // needs a CCDB manager any more, so they need not own one. + template + bool initCCDB(TBCs const& bcs, TCollisions const& collisions) + { + static_assert(requires(typename TBCs::iterator bc) { bc.vdriftTgl(); }, "initCCDB without a CCDB manager needs a BC table joined with aod::TpcCalibCCDBObjects"); + std::nullptr_t noCCDB{}; + return initCCDB(noCCDB, bcs, collisions); + } + + template + bool initCCDB(TCCDB& ccdb, TBCs const& bcs, TCollisions const& collisions) { - auto bc = collisions.size() ? collisions.begin().template bc_as() : bcs.begin(); + auto bc = collisions.size() ? collisions.begin().template bc_as() : bcs.begin(); if (!bcs.size()) { LOGF(warn, "No BC found, skipping this DF."); return false; // signal to skip this DF @@ -756,8 +906,12 @@ class BuilderModule if (v0BuilderOpts.generatePhotonCandidates.value && v0BuilderOpts.moveTPCOnlyTracks.value) { // initialize only if needed, avoid unnecessary CCDB calls - mVDriftMgr.init(&ccdb->instance()); - mVDriftMgr.update(timestamp); + if constexpr (requires { bc.vdriftTgl(); }) { + mVDriftMgr.update(bc.vdriftTgl()); + } else { + mVDriftMgr.init(&ccdb->instance()); + mVDriftMgr.update(timestamp); + } } return true; @@ -905,7 +1059,7 @@ class BuilderModule // handle TPC-only tracks properly (photon conversions) if (v0BuilderOpts.moveTPCOnlyTracks) { if (collision.has_bc()) { - mVDriftMgr.update(collision.template bc_as().timestamp()); + updateVDrift(collision); } if (isPosTPCOnly) { // Nota bene: positive is TPC-only -> this entire V0 merits treatment as photon candidate @@ -1111,7 +1265,7 @@ class BuilderModule sorted_v0 = sort_indices(v0List, (baseOpts.mc_findableMode.value > 0)); // Cascade part if cores are requested, skip otherwise - if (baseOpts.mEnabledTables[kStoredCascCores] || baseOpts.mEnabledTables[kStoredKFCascCores]) { + if (baseOpts.mEnabledTables[kStoredCascCores] || baseOpts.mEnabledTables[kStoredKFCascCores] || baseOpts.mEnabledTables[kStoredTraCascCores]) { if (baseOpts.mc_findableMode.value < 2) { // simple passthrough: copy existing cascades to build list for (const auto& cascade : cascades) { @@ -1378,8 +1532,11 @@ class BuilderModule pvX = collision.posX(); pvY = collision.posY(); pvZ = collision.posZ(); + if (eventSelectOpts.fillOnlySelectedCollisions && !isCollisionAccepted(collision)) { + continue; + } if (v0BuilderOpts.generatePhotonCandidates && v0BuilderOpts.moveTPCOnlyTracks && collision.has_bc()) { - mVDriftMgr.update(collision.template bc_as().timestamp()); + updateVDrift(collision); } } auto const& posTrack = tracks.rawIteratorAt(v0.posTrackId); @@ -1419,65 +1576,102 @@ class BuilderModule } } - if (!straHelper.buildV0Candidate(v0.collisionId, pvX, pvY, pvZ, posTrack, negTrack, posTrackPar, negTrackPar, v0.isCollinearV0, baseOpts.mEnabledTables[kV0Covs], v0BuilderOpts.generatePhotonCandidates)) { - products.v0dataLink(-1, -1); - continue; - } - if constexpr (requires { posTrack.tpcNSigmaEl(); }) { - if (preSelectOpts.preselectOnlyDesiredV0s) { - float lPt = RecoDecay::sqrtSumOfSquares( - straHelper.v0.positiveMomentum[0] + straHelper.v0.negativeMomentum[0], - straHelper.v0.positiveMomentum[1] + straHelper.v0.negativeMomentum[1]); - - float lPtot = RecoDecay::sqrtSumOfSquares( - straHelper.v0.positiveMomentum[0] + straHelper.v0.negativeMomentum[0], - straHelper.v0.positiveMomentum[1] + straHelper.v0.negativeMomentum[1], - straHelper.v0.positiveMomentum[2] + straHelper.v0.negativeMomentum[2]); - - float lLengthTraveled = RecoDecay::sqrtSumOfSquares( - straHelper.v0.position[0] - pvX, - straHelper.v0.position[1] - pvY, - straHelper.v0.position[2] - pvZ); - - uint8_t maskV0Preselection = 0; - - if ( // photon PID, mass, lifetime selection + std::vector preSelectedPIDV0s; + if (preSelectOpts.mEnabledPreselectedSpecies[kGamma] || preSelectOpts.mEnabledPreselectedSpecies[kK0Short] || preSelectOpts.mEnabledPreselectedSpecies[kLambda] || preSelectOpts.mEnabledPreselectedSpecies[kAntiLambda]) { + if constexpr (requires { posTrack.tpcNSigmaEl(); }) { // check PID for each particle species and mark which one passes the check + preSelectedPIDV0s.resize(nSelV0Types, 0); + if ( // photon PID selection + preSelectOpts.mEnabledPreselectedSpecies[kGamma] && std::abs(posTrack.tpcNSigmaEl()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaEl()) < preSelectOpts.maxTPCpidNsigma && - std::abs(straHelper.v0.massGamma) < preSelectOpts.massCutPhoton) { - BITSET(maskV0Preselection, selGamma); + std::abs(negTrack.tpcNSigmaEl()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDV0s[kGamma] = 1; } - if ( // K0Short PID, mass, lifetime selection + if ( // K0Short PID selection + preSelectOpts.mEnabledPreselectedSpecies[kK0Short] && std::abs(posTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - o2::constants::physics::MassKaonNeutral * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutK0S") && - std::abs(straHelper.v0.massK0Short - o2::constants::physics::MassKaonNeutral) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaK0Short(lPt) + preSelectOpts.massWindowSafetyMargin) { - BITSET(maskV0Preselection, selK0Short); + std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDV0s[kK0Short] = 1; } - if ( // Lambda PID, mass, lifetime selection + if ( // Lambda PID selection + preSelectOpts.mEnabledPreselectedSpecies[kLambda] && std::abs(posTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - o2::constants::physics::MassLambda * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && - std::abs(straHelper.v0.massLambda - o2::constants::physics::MassLambda) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaLambda(lPt) + preSelectOpts.massWindowSafetyMargin) { - BITSET(maskV0Preselection, selLambda); + std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDV0s[kLambda] = 1; } if ( // antiLambda PID, mass, lifetime selection + preSelectOpts.mEnabledPreselectedSpecies[kAntiLambda] && std::abs(posTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && - o2::constants::physics::MassLambda * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && - std::abs(straHelper.v0.massAntiLambda - o2::constants::physics::MassLambda) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaLambda(lPt) + preSelectOpts.massWindowSafetyMargin) { - BITSET(maskV0Preselection, selAntiLambda); + std::abs(negTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDV0s[kAntiLambda] = 1; } - histos.fill(HIST("hPreselectionV0s"), maskV0Preselection); - - if (maskV0Preselection == 0) { + // if particle species pass the PID selections, move onto the next candidates + if (!preSelectedPIDV0s[kGamma] && !preSelectedPIDV0s[kK0Short] && !preSelectedPIDV0s[kLambda] && !preSelectedPIDV0s[kAntiLambda]) { + histos.fill(HIST("hPreselectionV0s"), 0); products.v0dataLink(-1, -1); continue; } + } else { // if no PID information is available, do not cut on it and mark all PID checks as true + preSelectedPIDV0s.resize(nSelV0Types, 1); + } + } + + if (!straHelper.buildV0Candidate(v0.collisionId, pvX, pvY, pvZ, posTrack, negTrack, posTrackPar, negTrackPar, v0.isCollinearV0, baseOpts.mEnabledTables[kV0Covs], v0BuilderOpts.generatePhotonCandidates)) { + products.v0dataLink(-1, -1); + continue; + } + if (preSelectOpts.mEnabledPreselectedSpecies[kGamma] || preSelectOpts.mEnabledPreselectedSpecies[kK0Short] || preSelectOpts.mEnabledPreselectedSpecies[kLambda] || preSelectOpts.mEnabledPreselectedSpecies[kAntiLambda]) { + float lPt = RecoDecay::sqrtSumOfSquares( + straHelper.v0.positiveMomentum[0] + straHelper.v0.negativeMomentum[0], + straHelper.v0.positiveMomentum[1] + straHelper.v0.negativeMomentum[1]); + + float lPtot = RecoDecay::sqrtSumOfSquares( + straHelper.v0.positiveMomentum[0] + straHelper.v0.negativeMomentum[0], + straHelper.v0.positiveMomentum[1] + straHelper.v0.negativeMomentum[1], + straHelper.v0.positiveMomentum[2] + straHelper.v0.negativeMomentum[2]); + + float lLengthTraveled = RecoDecay::sqrtSumOfSquares( + straHelper.v0.position[0] - pvX, + straHelper.v0.position[1] - pvY, + straHelper.v0.position[2] - pvZ); + + uint8_t maskV0Preselection = 0; + + if ( // photon PID, mass, lifetime selection + preSelectOpts.mEnabledPreselectedSpecies[kGamma] && preSelectedPIDV0s[kGamma] && + std::abs(straHelper.v0.massGamma) < preSelectOpts.massCutPhoton) { + BITSET(maskV0Preselection, selGamma); + } + + if ( // K0Short PID, mass, lifetime selection + preSelectOpts.mEnabledPreselectedSpecies[kK0Short] && preSelectedPIDV0s[kK0Short] && + o2::constants::physics::MassKaonNeutral * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutK0S") && + std::abs(straHelper.v0.massK0Short - o2::constants::physics::MassKaonNeutral) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaK0Short(lPt) + preSelectOpts.massWindowSafetyMargin) { + BITSET(maskV0Preselection, selK0Short); + } + + if ( // Lambda PID, mass, lifetime selection + preSelectOpts.mEnabledPreselectedSpecies[kLambda] && preSelectedPIDV0s[kLambda] && + o2::constants::physics::MassLambda * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && + std::abs(straHelper.v0.massLambda - o2::constants::physics::MassLambda) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaLambda(lPt) + preSelectOpts.massWindowSafetyMargin) { + BITSET(maskV0Preselection, selLambda); + } + + if ( // antiLambda PID, mass, lifetime selection + preSelectOpts.mEnabledPreselectedSpecies[kAntiLambda] && preSelectedPIDV0s[kAntiLambda] && + o2::constants::physics::MassLambda * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && + std::abs(straHelper.v0.massAntiLambda - o2::constants::physics::MassLambda) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaLambda(lPt) + preSelectOpts.massWindowSafetyMargin) { + BITSET(maskV0Preselection, selAntiLambda); + } + + histos.fill(HIST("hPreselectionV0s"), maskV0Preselection); + + if (maskV0Preselection == 0) { + products.v0dataLink(-1, -1); + continue; } } if (v0Map[iv0] == -1 && baseOpts.useV0BufferForCascades) { @@ -1736,11 +1930,31 @@ class BuilderModule if (mcParticle.has_daughters()) { auto const& daughters = mcParticle.template daughters_as(); + // For the K0s (main channel of interest): + // K0s --> pi+ pi- (69.20%) + // K0s --> pi0 pi0 (30.69%) + // For the Lambda (main channel of interest): + // Lambda --> p pi- (64.1%) + // Lambda --> n pi0 (35.9%) for (const auto& dau : daughters) { if (dau.getProcess() != TMCProcess::kPDecay) continue; + // proton, neutron, pi+, e+, pi0 if (dau.pdgCode() > 0) { + // special treatment of Lambda --> neutron pi0 (both PDG codes > 0) + // and K0s --> pi0 pi0 (both PDG codes > 0) + // take neutron as positive daughter and pi0 as negative daughter + // if the first daughter was the pi0 (already assigned as positive), + // then transfer the information to the negative and fill the information + // of the positive daughter with those of the neutrons + if (thisInfo.pdgCodePositive > -1 && thisInfo.pdgCodePositive == PDG_t::kPi0) { + thisInfo.pdgCodeNegative = thisInfo.pdgCodePositive; + thisInfo.processNegative = thisInfo.processPositive; + thisInfo.negP[0] = thisInfo.posP[0]; + thisInfo.negP[1] = thisInfo.posP[1]; + thisInfo.negP[2] = thisInfo.posP[2]; + } thisInfo.pdgCodePositive = dau.pdgCode(); thisInfo.processPositive = dau.getProcess(); thisInfo.posP[0] = dau.px(); @@ -1750,6 +1964,7 @@ class BuilderModule thisInfo.xyz[1] = dau.vy(); thisInfo.xyz[2] = dau.vz(); } + // antiproton, antineutron, pi-, e- (pi0 cannot have negative PDG code) if (dau.pdgCode() < 0) { thisInfo.pdgCodeNegative = dau.pdgCode(); thisInfo.processNegative = dau.getProcess(); @@ -1915,10 +2130,64 @@ class BuilderModule pvX = collision.posX(); pvY = collision.posY(); pvZ = collision.posZ(); + if (eventSelectOpts.fillOnlySelectedCollisions && !isCollisionAccepted(collision)) { + products.cascdataLink(-1); + interlinks.cascadeToCascCores.push_back(-1); + continue; + } } auto const& posTrack = tracks.rawIteratorAt(cascade.posTrackId); auto const& negTrack = tracks.rawIteratorAt(cascade.negTrackId); auto const& bachTrack = tracks.rawIteratorAt(cascade.bachTrackId); + + std::vector preSelectedPIDCascades; + if (preSelectOpts.mEnabledPreselectedSpecies[kXiMinus] || preSelectOpts.mEnabledPreselectedSpecies[kXiPlus] || preSelectOpts.mEnabledPreselectedSpecies[kOmegaMinus] || preSelectOpts.mEnabledPreselectedSpecies[kOmegaPlus]) { + if constexpr (requires { posTrack.tpcNSigmaEl(); }) { // check PID for each particle species and mark which one passes the check + preSelectedPIDCascades.resize(nPartTypes, 0); + if ( // XiMinus PID selection + preSelectOpts.mEnabledPreselectedSpecies[kXiMinus] && + std::abs(posTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && + std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && + std::abs(bachTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDCascades[kXiMinus] = 1; + } + + if ( // XiPlus PID selection + preSelectOpts.mEnabledPreselectedSpecies[kXiPlus] && + std::abs(posTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && + std::abs(negTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && + std::abs(bachTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDCascades[kXiPlus] = 1; + } + + if ( // OmegaMinus PID selection + preSelectOpts.mEnabledPreselectedSpecies[kOmegaMinus] && + std::abs(posTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && + std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && + std::abs(bachTrack.tpcNSigmaKa()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDCascades[kOmegaMinus] = 1; + } + + if ( // OmegaPlus PID selection + preSelectOpts.mEnabledPreselectedSpecies[kOmegaPlus] && + std::abs(posTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && + std::abs(negTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && + std::abs(bachTrack.tpcNSigmaKa()) < preSelectOpts.maxTPCpidNsigma) { + preSelectedPIDCascades[kOmegaPlus] = 1; + } + + // if particle species pass the PID selections, move onto the next candidates + if (!preSelectedPIDCascades[kXiMinus] && !preSelectedPIDCascades[kXiPlus] && !preSelectedPIDCascades[kOmegaMinus] && !preSelectedPIDCascades[kOmegaPlus]) { + histos.fill(HIST("hPreselectionCascades"), 0); + products.cascdataLink(-1); + interlinks.cascadeToCascCores.push_back(-1); + continue; + } + } else { // if no PID information is available, do not cut on it and mark all PID checks as true + preSelectedPIDCascades.resize(nPartTypes, 1); + } + } + if (baseOpts.useV0BufferForCascades) { // this processing path uses a buffer of V0s so that no // additional minimization step is redone. It consumes less @@ -1962,85 +2231,71 @@ class BuilderModule } nCascades++; - if constexpr (requires { posTrack.tpcNSigmaEl(); }) { - if (preSelectOpts.preselectOnlyDesiredCascades) { - float lPt = RecoDecay::sqrtSumOfSquares( - straHelper.cascade.bachelorMomentum[0] + straHelper.cascade.positiveMomentum[0] + straHelper.cascade.negativeMomentum[0], - straHelper.cascade.bachelorMomentum[1] + straHelper.cascade.positiveMomentum[1] + straHelper.cascade.negativeMomentum[1]); - - float lPtot = RecoDecay::sqrtSumOfSquares( - straHelper.cascade.bachelorMomentum[0] + straHelper.cascade.positiveMomentum[0] + straHelper.cascade.negativeMomentum[0], - straHelper.cascade.bachelorMomentum[1] + straHelper.cascade.positiveMomentum[1] + straHelper.cascade.negativeMomentum[1], - straHelper.cascade.bachelorMomentum[2] + straHelper.cascade.positiveMomentum[2] + straHelper.cascade.negativeMomentum[2]); - - float lV0Ptot = RecoDecay::sqrtSumOfSquares( - straHelper.cascade.positiveMomentum[0] + straHelper.cascade.negativeMomentum[0], - straHelper.cascade.positiveMomentum[1] + straHelper.cascade.negativeMomentum[1], - straHelper.cascade.positiveMomentum[2] + straHelper.cascade.negativeMomentum[2]); - - float lLengthTraveled = RecoDecay::sqrtSumOfSquares( - straHelper.cascade.cascadePosition[0] - pvX, - straHelper.cascade.cascadePosition[1] - pvY, - straHelper.cascade.cascadePosition[2] - pvZ); - - float lV0LengthTraveled = RecoDecay::sqrtSumOfSquares( - straHelper.cascade.v0Position[0] - straHelper.cascade.cascadePosition[0], - straHelper.cascade.v0Position[1] - straHelper.cascade.cascadePosition[1], - straHelper.cascade.v0Position[2] - straHelper.cascade.cascadePosition[2]); - - uint8_t maskCascadePreselection = 0; - - if ( // XiMinus PID and mass selection - straHelper.cascade.charge < 0 && - std::abs(posTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - std::abs(bachTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && - o2::constants::physics::MassXiMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutXi") && - std::abs(straHelper.cascade.massXi - o2::constants::physics::MassXiMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaXi(lPt) + preSelectOpts.massWindowSafetyMargin) { - BITSET(maskCascadePreselection, selXiMinus); - } + if (preSelectOpts.mEnabledPreselectedSpecies[kXiMinus] || preSelectOpts.mEnabledPreselectedSpecies[kXiPlus] || preSelectOpts.mEnabledPreselectedSpecies[kOmegaMinus] || preSelectOpts.mEnabledPreselectedSpecies[kOmegaPlus]) { + float lPt = RecoDecay::sqrtSumOfSquares( + straHelper.cascade.bachelorMomentum[0] + straHelper.cascade.positiveMomentum[0] + straHelper.cascade.negativeMomentum[0], + straHelper.cascade.bachelorMomentum[1] + straHelper.cascade.positiveMomentum[1] + straHelper.cascade.negativeMomentum[1]); + + float lPtot = RecoDecay::sqrtSumOfSquares( + straHelper.cascade.bachelorMomentum[0] + straHelper.cascade.positiveMomentum[0] + straHelper.cascade.negativeMomentum[0], + straHelper.cascade.bachelorMomentum[1] + straHelper.cascade.positiveMomentum[1] + straHelper.cascade.negativeMomentum[1], + straHelper.cascade.bachelorMomentum[2] + straHelper.cascade.positiveMomentum[2] + straHelper.cascade.negativeMomentum[2]); + + float lV0Ptot = RecoDecay::sqrtSumOfSquares( + straHelper.cascade.positiveMomentum[0] + straHelper.cascade.negativeMomentum[0], + straHelper.cascade.positiveMomentum[1] + straHelper.cascade.negativeMomentum[1], + straHelper.cascade.positiveMomentum[2] + straHelper.cascade.negativeMomentum[2]); + + float lLengthTraveled = RecoDecay::sqrtSumOfSquares( + straHelper.cascade.cascadePosition[0] - pvX, + straHelper.cascade.cascadePosition[1] - pvY, + straHelper.cascade.cascadePosition[2] - pvZ); + + float lV0LengthTraveled = RecoDecay::sqrtSumOfSquares( + straHelper.cascade.v0Position[0] - straHelper.cascade.cascadePosition[0], + straHelper.cascade.v0Position[1] - straHelper.cascade.cascadePosition[1], + straHelper.cascade.v0Position[2] - straHelper.cascade.cascadePosition[2]); + + uint8_t maskCascadePreselection = 0; + + if ( // XiMinus PID and mass selection + preSelectOpts.mEnabledPreselectedSpecies[kXiMinus] && straHelper.cascade.charge < 0 && preSelectedPIDCascades[kXiMinus] && + o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && + o2::constants::physics::MassXiMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutXi") && + std::abs(straHelper.cascade.massXi - o2::constants::physics::MassXiMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaXi(lPt) + preSelectOpts.massWindowSafetyMargin) { + BITSET(maskCascadePreselection, selXiMinus); + } - if ( // XiPlus PID and mass selection - straHelper.cascade.charge > 0 && - std::abs(posTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && - std::abs(bachTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && - o2::constants::physics::MassXiMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutXi") && - std::abs(straHelper.cascade.massXi - o2::constants::physics::MassXiMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaXi(lPt) + preSelectOpts.massWindowSafetyMargin) { - BITSET(maskCascadePreselection, selXiPlus); - } + if ( // XiPlus PID and mass selection + preSelectOpts.mEnabledPreselectedSpecies[kXiPlus] && straHelper.cascade.charge > 0 && preSelectedPIDCascades[kXiPlus] && + o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && + o2::constants::physics::MassXiMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutXi") && + std::abs(straHelper.cascade.massXi - o2::constants::physics::MassXiMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaXi(lPt) + preSelectOpts.massWindowSafetyMargin) { + BITSET(maskCascadePreselection, selXiPlus); + } - if ( // OmegaMinus PID and mass selection - straHelper.cascade.charge < 0 && - std::abs(posTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - std::abs(bachTrack.tpcNSigmaKa()) < preSelectOpts.maxTPCpidNsigma && - o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && - o2::constants::physics::MassOmegaMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutOmega") && - std::abs(straHelper.cascade.massOmega - o2::constants::physics::MassOmegaMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaOmega(lPt) + preSelectOpts.massWindowSafetyMargin) { - BITSET(maskCascadePreselection, selOmegaMinus); - } + if ( // OmegaMinus PID and mass selection + preSelectOpts.mEnabledPreselectedSpecies[kOmegaMinus] && straHelper.cascade.charge < 0 && preSelectedPIDCascades[kOmegaMinus] && + o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && + o2::constants::physics::MassOmegaMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutOmega") && + std::abs(straHelper.cascade.massOmega - o2::constants::physics::MassOmegaMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaOmega(lPt) + preSelectOpts.massWindowSafetyMargin) { + BITSET(maskCascadePreselection, selOmegaMinus); + } - if ( // OmegaPlus PID and mass selection - straHelper.cascade.charge > 0 && - std::abs(posTrack.tpcNSigmaPi()) < preSelectOpts.maxTPCpidNsigma && - std::abs(negTrack.tpcNSigmaPr()) < preSelectOpts.maxTPCpidNsigma && - std::abs(bachTrack.tpcNSigmaKa()) < preSelectOpts.maxTPCpidNsigma && - o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && - o2::constants::physics::MassOmegaMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutOmega") && - std::abs(straHelper.cascade.massOmega - o2::constants::physics::MassOmegaMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaOmega(lPt) + preSelectOpts.massWindowSafetyMargin) { - BITSET(maskCascadePreselection, selOmegaPlus); - } + if ( // OmegaPlus PID and mass selection + preSelectOpts.mEnabledPreselectedSpecies[kOmegaPlus] && straHelper.cascade.charge > 0 && preSelectedPIDCascades[kOmegaPlus] && + o2::constants::physics::MassLambda * lV0LengthTraveled / (lV0Ptot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutLambda") && + o2::constants::physics::MassOmegaMinus * lLengthTraveled / (lPtot + 1e-13) < preSelectOpts.lifetimeCut->get("lifetimeCutOmega") && + std::abs(straHelper.cascade.massOmega - o2::constants::physics::MassOmegaMinus) < preSelectOpts.massWindownumberOfSigmas * getMassSigmaOmega(lPt) + preSelectOpts.massWindowSafetyMargin) { + BITSET(maskCascadePreselection, selOmegaPlus); + } - histos.fill(HIST("hPreselectionCascades"), maskCascadePreselection); + histos.fill(HIST("hPreselectionCascades"), maskCascadePreselection); - if (maskCascadePreselection == 0) { - products.cascdataLink(-1); - interlinks.cascadeToCascCores.push_back(-1); - continue; - } + if (maskCascadePreselection == 0) { + products.cascdataLink(-1); + interlinks.cascadeToCascCores.push_back(-1); + continue; } } @@ -2246,7 +2501,16 @@ class BuilderModule if (dau.getProcess() != TMCProcess::kPDecay) // check whether the daughter comes from a decay continue; - if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus || std::abs(dau.pdgCode()) == PDG_t::kKPlus) { + // Dominant decay channels for charged Xi and Omega are: + // Xi- --> Lambda pi- ; Xi+ --> Lambda pi+ (99.887%) --> recover information of all daughters + // Omega- --> Lambda K- ; Omega+ --> Lambda K+ (67.7%) --> recover information of all daughters + // Omega- --> Xi0 pi- ; Omega+ --> Xi0 pi+ (24.3%) --> recover information of only Xi0 and pions + // Omega- --> Xi- pi0 ; Omega+ --> Xi+ pi0 (8.55%) --> recover information of only Xi and pions + // + // For the Lambda (main channel of interest): + // Lambda --> p pi- (64.1%) + // Lambda --> n pi0 (35.9%) + if (std::abs(dau.pdgCode()) == PDG_t::kPiPlus || std::abs(dau.pdgCode()) == PDG_t::kPi0 || std::abs(dau.pdgCode()) == PDG_t::kKPlus) { thisCascInfo.pdgCodeBachelor = dau.pdgCode(); thisCascInfo.bachP[0] = dau.px(); thisCascInfo.bachP[1] = dau.py(); @@ -2256,14 +2520,29 @@ class BuilderModule thisCascInfo.xyz[2] = dau.vz(); thisCascInfo.mcParticleBachelor = dau.globalIndex(); } - if (std::abs(dau.pdgCode()) == PDG_t::kProton) { + // Treatment of Lambda --> p pi ; Lambda --> n pi + if (std::abs(dau.pdgCode()) == PDG_t::kLambda0) { thisCascInfo.pdgCodeV0 = dau.pdgCode(); for (const auto& v0Dau : dau.template daughters_as()) { if (v0Dau.getProcess() != TMCProcess::kPDecay) continue; + // proton, neutron, pi+, pi0 if (v0Dau.pdgCode() > 0) { + // special treatment of Lambda --> neutron pi0 (both PDG codes > 0) + // take neutron as positive daughter and pi0 as negative daughter + // if the first daughter was the pi0 (already assigned as positive), + // then transfer the information to the negative and fill the information + // of the positive daughter with those of the neutrons + if (thisCascInfo.pdgCodePositive > -1 && thisCascInfo.pdgCodePositive == PDG_t::kPi0) { + thisCascInfo.pdgCodeNegative = thisCascInfo.pdgCodePositive; + thisCascInfo.processNegative = thisCascInfo.processPositive; + thisCascInfo.negP[0] = thisCascInfo.posP[0]; + thisCascInfo.negP[1] = thisCascInfo.posP[1]; + thisCascInfo.negP[2] = thisCascInfo.posP[2]; + thisCascInfo.mcParticleNegative = thisCascInfo.mcParticlePositive; + } thisCascInfo.pdgCodePositive = v0Dau.pdgCode(); thisCascInfo.processPositive = v0Dau.getProcess(); thisCascInfo.posP[0] = v0Dau.px(); @@ -2274,6 +2553,7 @@ class BuilderModule thisCascInfo.lxyz[2] = v0Dau.vz(); thisCascInfo.mcParticlePositive = v0Dau.globalIndex(); } + // antiproton, antineutron, pi- (pi0 cannot have negative PDG code) if (v0Dau.pdgCode() < 0) { thisCascInfo.pdgCodeNegative = v0Dau.pdgCode(); thisCascInfo.processNegative = v0Dau.getProcess(); @@ -2284,6 +2564,37 @@ class BuilderModule } } } + // Special treatment of Xi0 and Xi from Omega --> Xi0 pi ; Omega --> Xi pi0 + if (std::abs(dau.pdgCode()) == PDG_t::kXiMinus || std::abs(dau.pdgCode()) == o2::constants::physics::Pdg::kXi0) { + thisCascInfo.pdgCodeV0 = dau.pdgCode(); + + for (const auto& xiDau : dau.template daughters_as()) { + if (xiDau.getProcess() != TMCProcess::kPDecay) + continue; + + // always put the Lambda in the positive + if (std::abs(xiDau.pdgCode()) == PDG_t::kLambda0) { + thisCascInfo.pdgCodePositive = xiDau.pdgCode(); + thisCascInfo.processPositive = xiDau.getProcess(); + thisCascInfo.posP[0] = xiDau.px(); + thisCascInfo.posP[1] = xiDau.py(); + thisCascInfo.posP[2] = xiDau.pz(); + thisCascInfo.lxyz[0] = xiDau.vx(); + thisCascInfo.lxyz[1] = xiDau.vy(); + thisCascInfo.lxyz[2] = xiDau.vz(); + thisCascInfo.mcParticlePositive = xiDau.globalIndex(); + } + // always put the bachelor or the photon or the pi0 in the negative + if (std::abs(xiDau.pdgCode()) == PDG_t::kPiPlus || std::abs(xiDau.pdgCode()) == PDG_t::kPi0 || std::abs(xiDau.pdgCode()) == PDG_t::kGamma) { + thisCascInfo.pdgCodeNegative = xiDau.pdgCode(); + thisCascInfo.processNegative = xiDau.getProcess(); + thisCascInfo.negP[0] = xiDau.px(); + thisCascInfo.negP[1] = xiDau.py(); + thisCascInfo.negP[2] = xiDau.pz(); + thisCascInfo.mcParticleNegative = xiDau.globalIndex(); + } + } + } } } @@ -2338,6 +2649,11 @@ class BuilderModule pvX = collision.posX(); pvY = collision.posY(); pvZ = collision.posZ(); + if (eventSelectOpts.fillOnlySelectedCollisions && !isCollisionAccepted(collision)) { + products.kfcascdataLink(-1); + interlinks.cascadeToKFCascCores.push_back(-1); + continue; + } } auto const& posTrack = tracks.rawIteratorAt(cascade.posTrackId); auto const& negTrack = tracks.rawIteratorAt(cascade.negTrackId); @@ -2437,6 +2753,9 @@ class BuilderModule pvX = collision.posX(); pvY = collision.posY(); pvZ = collision.posZ(); + if (eventSelectOpts.fillOnlySelectedCollisions && !isCollisionAccepted(collision)) { + continue; + } } auto const& cascade = cascadeTrack.cascade(); auto const& v0 = cascade.v0(); @@ -2491,7 +2810,7 @@ class BuilderModule // interlink always produced if base core table generated traCascIndices[cascade.globalIndex()] = products.tracascdata.lastIndex(); } - if (baseOpts.mEnabledTables[kCascCovs]) { + if (baseOpts.mEnabledTables[kTraCascCovs]) { std::array traCovMat = {0.}; strangeTrackParCov.getCovXYZPxPyPzGlo(traCovMat); float traCovMatArray[o2::track::kLabCovMatSize]; @@ -2499,7 +2818,7 @@ class BuilderModule traCovMatArray[ii] = traCovMat[ii]; } products.tracasccovs(traCovMatArray); - histos.fill(HIST("hTableBuildingStatistics"), kCascCovs); + histos.fill(HIST("hTableBuildingStatistics"), kTraCascCovs); } //_________________________________________________________ @@ -2553,6 +2872,16 @@ class BuilderModule return returnValue; } + //__________________________________________________ + // Overload for tasks sourcing every conditions object from CCDB columns; see initCCDB above. + template + void dataProcess(THistoRegistry& histos, TCollisions const& collisions, TMCCollisions const& mccollisions, TV0s const& v0s, TCascades const& cascades, TTrackedCascades const& trackedCascades, TTracks const& tracks, TBCs const& bcs, TMCParticles const& mcParticles, TProducts& products) + { + static_assert(requires(typename TBCs::iterator bc) { bc.vdriftTgl(); }, "dataProcess without a CCDB manager needs a BC table joined with aod::TpcCalibCCDBObjects"); + std::nullptr_t noCCDB{}; + dataProcess(noCCDB, histos, collisions, mccollisions, v0s, cascades, trackedCascades, tracks, bcs, mcParticles, products); + } + //__________________________________________________ template void dataProcess(TCCDB& ccdb, THistoRegistry& histos, TCollisions const& collisions, TMCCollisions const& mccollisions, TV0s const& v0s, TCascades const& cascades, TTrackedCascades const& trackedCascades, TTracks const& tracks, TBCs const& bcs, TMCParticles const& mcParticles, TProducts& products) diff --git a/PWGLF/Utils/strangenessPCH.cxx b/PWGLF/Utils/strangenessPCH.cxx new file mode 100644 index 00000000000..e9bcd249723 --- /dev/null +++ b/PWGLF/Utils/strangenessPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the LfStrangenessPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGLF/Utils/svPoolCreator.h b/PWGLF/Utils/svPoolCreator.h index 68a621fd977..fedcaa95ce7 100644 --- a/PWGLF/Utils/svPoolCreator.h +++ b/PWGLF/Utils/svPoolCreator.h @@ -9,6 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// \file svPoolCreator.h +/// \brief Time-compatible secondary-vertex track-pair and triplet pools +/// \author ALICE Collaboration + #ifndef PWGLF_UTILS_SVPOOLCREATOR_H_ #define PWGLF_UTILS_SVPOOLCREATOR_H_ @@ -32,6 +36,7 @@ using CollBracket = o2::math_utils::Bracket; constexpr uint64_t bOffsetMax = 241; // track compatibility can never go beyond 6 mus (ITS) +constexpr int NChargeSigns = 2; struct TrackCand { int Idxtr; @@ -44,6 +49,13 @@ struct SVCand { CollBracket collBracket{}; }; +struct SVCand3 { + int tr0Idx; + int tr1Idx; + int tr2Idx; + CollBracket collBracket{}; +}; + class svPoolCreator { public: @@ -61,7 +73,7 @@ class svPoolCreator void clearPools() { - for (auto& pool : trackCandPool) { + for (auto& pool : trackCandPool) { // o2-linter: disable=const-ref-in-for-loop (The pool is cleared in place.) pool.clear(); } tmap.clear(); @@ -87,8 +99,8 @@ class svPoolCreator } } - template - void appendTrackCand(const T& trackCand, const C& collisions, int pdgHypo, o2::aod::AmbiguousTracks const& ambiTracks, BC const&) + template + void appendTrackCand(const T& trackCand, const C& collisions, int pdgHypo, const AT& ambiTracks, BC const&) { if (pdgHypo != track0Pdg && pdgHypo != track1Pdg) { LOGP(debug, "Wrong pdg hypothesis"); @@ -106,11 +118,11 @@ class svPoolCreator if (ambTrack.trackId() != trackCand.globalIndex()) { continue; } - if (!ambTrack.has_bc() || ambTrack.bc_as().size() == 0) { + if (!ambTrack.has_bc() || ambTrack.template bc_as().size() == 0) { globalBC = BcInvalid; break; } - globalBC = ambTrack.bc_as().begin().globalBC(); + globalBC = ambTrack.template bc_as().begin().globalBC(); break; } } else { @@ -170,7 +182,6 @@ class svPoolCreator } else if (TESTBIT(trackCand.flags(), o2::aod::track::TrackTimeResIsRange)) { thresholdTime = std::sqrt(sigmaTimeRes2); thresholdTime += timeMarginNS; - } else { thresholdTime = 4. * std::sqrt(sigmaTimeRes2); thresholdTime += timeMarginNS; @@ -197,48 +208,46 @@ class svPoolCreator // is Sorting Needed ? TBD } + template std::vector& getSVCandPool(const C& collisions, bool combineLikeSign = false) { - gsl::span> track0Pool{trackCandPool.data(), 2}; - gsl::span> track1Pool{trackCandPool.data() + 2, 2}; - std::array, 2> mVtxTrack0{}; // 1st pos. and neg. track of the kink pool for each vertex + gsl::span> track0Pool{trackCandPool.data(), NChargeSigns}; + gsl::span> track1Pool{trackCandPool.data() + NChargeSigns, NChargeSigns}; + std::array, NChargeSigns> mVtxTrack0{}; // 1st pos. and neg. track of the kink pool for each vertex - for (int i = 0; i < 2; i++) { + for (int i = 0; i < NChargeSigns; i++) { mVtxTrack0[i].clear(); mVtxTrack0[i].resize(collisions.size(), -1); } - - for (int pn = 0; pn < 2; pn++) { - auto& vtxFirstT = mVtxTrack0[pn]; - const auto& signTrack0Pool = track0Pool[pn]; + for (int iCharge = 0; iCharge < NChargeSigns; iCharge++) { + auto& vtxFirstT = mVtxTrack0[iCharge]; + const auto& signTrack0Pool = track0Pool[iCharge]; for (unsigned i = 0; i < signTrack0Pool.size(); i++) { const auto& track0Seed = signTrack0Pool[i]; - LOG(debug) << "Processsing track0 with index: " << track0Seed.Idxtr << " min bracket: " << track0Seed.collBracket.getMin() << " max bracket: " << track0Seed.collBracket.getMax(); for (int j{track0Seed.collBracket.getMin()}; j <= track0Seed.collBracket.getMax(); ++j) { if (vtxFirstT[j] == -1) { vtxFirstT[j] = i; } } } - int track1sign = combineLikeSign ? pn : 1 - pn; + int track1sign = combineLikeSign ? iCharge : 1 - iCharge; auto& signTrack1 = track1Pool[track1sign]; - for (unsigned itp = 0; itp < signTrack1.size(); itp++) { - auto& track1Seed = signTrack1[itp]; - LOG(debug) << "Processing track1 with index: " << track1Seed.Idxtr << " min bracket: " << track1Seed.collBracket.getMin() << " max bracket: " << track1Seed.collBracket.getMax(); - int firsOverlapIdx = -1; + for (unsigned iTrack1 = 0; iTrack1 < signTrack1.size(); iTrack1++) { + auto& track1Seed = signTrack1[iTrack1]; + int firstOverlapIdx = -1; for (int j{track1Seed.collBracket.getMin()}; j <= track1Seed.collBracket.getMax(); ++j) { LOG(debug) << "Checking vtxFirstT at position " << j << " with value " << vtxFirstT[j]; if (vtxFirstT[j] != -1) { - firsOverlapIdx = vtxFirstT[j]; + firstOverlapIdx = vtxFirstT[j]; break; } } - if (firsOverlapIdx < 0) { + if (firstOverlapIdx < 0) { continue; } - for (unsigned itn = firsOverlapIdx; itn < signTrack0Pool.size(); itn++) { - auto& track0Seed = signTrack0Pool[itn]; + for (unsigned iTrack0 = firstOverlapIdx; iTrack0 < signTrack0Pool.size(); iTrack0++) { + auto& track0Seed = signTrack0Pool[iTrack0]; if (track0Seed.collBracket.getMin() > track1Seed.collBracket.getMax()) { break; @@ -274,4 +283,108 @@ class svPoolCreator TrackCand trForpool; }; +/// Build time-compatible three-track combinations by joining two svPoolCreator +/// pools on a common first prong. The class deliberately only handles +/// combinatorics; the caller remains responsible for fitting and physics +/// selections. +class svPoolCreator3Body +{ + public: + svPoolCreator3Body() = default; + svPoolCreator3Body(int track0Pdg, int track1Pdg, int track2Pdg) + : track0Pdg(track0Pdg), + track1Pdg(track1Pdg), + track2Pdg(track2Pdg), + pool01(track0Pdg, track1Pdg), + pool02(track0Pdg, track2Pdg) + { + } + + void setPDGs(int pdg0, int pdg1, int pdg2) + { + track0Pdg = pdg0; + track1Pdg = pdg1; + track2Pdg = pdg2; + pool01.setPDGs(track0Pdg, track1Pdg); + pool02.setPDGs(track0Pdg, track2Pdg); + } + + void clearPools() + { + pool01.clearPools(); + pool02.clearPools(); + svCandPool.clear(); + } + + void setTimeMargin(float timeMargin) + { + pool01.setTimeMargin(timeMargin); + pool02.setTimeMargin(timeMargin); + } + + void setSkipAmbiTracks() + { + pool01.setSkipAmbiTracks(); + pool02.setSkipAmbiTracks(); + } + + template + void fillBC2Coll(const C& collisions, BC const& bcs) + { + pool01.fillBC2Coll(collisions, bcs); + pool02.fillBC2Coll(collisions, bcs); + } + + template + void appendTrackCand(const T& trackCand, const C& collisions, int pdgHypo, const AT& ambiTracks, BC const& bcs) + { + if (pdgHypo == track0Pdg) { + pool01.appendTrackCand(trackCand, collisions, pdgHypo, ambiTracks, bcs); + pool02.appendTrackCand(trackCand, collisions, pdgHypo, ambiTracks, bcs); + } else if (pdgHypo == track1Pdg) { + pool01.appendTrackCand(trackCand, collisions, pdgHypo, ambiTracks, bcs); + } else if (pdgHypo == track2Pdg) { + pool02.appendTrackCand(trackCand, collisions, pdgHypo, ambiTracks, bcs); + } else { + LOGP(debug, "Wrong PDG hypothesis for three-body pool"); + } + } + + template + std::vector& getSVCandPool(const C& collisions, bool combineLikeSign01 = false, bool combineLikeSign02 = false) + { + auto& candidates01 = pool01.getSVCandPool(collisions, combineLikeSign01); + auto& candidates02 = pool02.getSVCandPool(collisions, combineLikeSign02); + std::unordered_multimap candidates02ByTrack0; + candidates02ByTrack0.reserve(candidates02.size()); + for (const auto& candidate02 : candidates02) { + candidates02ByTrack0.emplace(candidate02.tr0Idx, &candidate02); + } + + for (const auto& candidate01 : candidates01) { + const auto [begin, end] = candidates02ByTrack0.equal_range(candidate01.tr0Idx); + for (auto candidate02It = begin; candidate02It != end; ++candidate02It) { + const auto& candidate02 = *candidate02It->second; + if (candidate01.tr1Idx == candidate02.tr1Idx || + candidate01.collBracket.isOutside(candidate02.collBracket)) { + continue; + } + svCandPool.emplace_back(SVCand3{candidate01.tr0Idx, + candidate01.tr1Idx, + candidate02.tr1Idx, + candidate01.collBracket.getOverlap(candidate02.collBracket)}); + } + } + return svCandPool; + } + + private: + int track0Pdg = 0; + int track1Pdg = 0; + int track2Pdg = 0; + svPoolCreator pool01; + svPoolCreator pool02; + std::vector svCandPool; +}; + #endif // PWGLF_UTILS_SVPOOLCREATOR_H_ diff --git a/PWGLF/Utils/v0SelectionGroupLinkDef.h b/PWGLF/Utils/v0SelectionGroupLinkDef.h index 3c81add2fc2..c40840a0625 100644 --- a/PWGLF/Utils/v0SelectionGroupLinkDef.h +++ b/PWGLF/Utils/v0SelectionGroupLinkDef.h @@ -9,8 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ -#define PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; @@ -21,5 +20,3 @@ #pragma link C++ class std::vector < float> + ; #pragma link C++ class std::vector < double> + ; #pragma link C++ class std::vector < bool> + ; - -#endif // PWGLF_UTILS_V0SELECTIONGROUPLINKDEF_H_ diff --git a/PWGMM/Lumi/Tasks/CMakeLists.txt b/PWGMM/Lumi/Tasks/CMakeLists.txt index 000f831a5c8..d684780eeea 100644 --- a/PWGMM/Lumi/Tasks/CMakeLists.txt +++ b/PWGMM/Lumi/Tasks/CMakeLists.txt @@ -17,7 +17,15 @@ o2physics_add_dpl_workflow(lumi O2::DetectorsCommonDataFormats O2::DetectorsVertexing COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lumifddft0 +o2physics_add_dpl_workflow(lumi-vertex + SOURCES lumiVertex.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCCDB O2Physics::AnalysisCore + O2::ReconstructionDataFormats + O2::DetectorsBase + O2::DetectorsCommonDataFormats + O2::DetectorsVertexing + COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(lumifddft0 # o2-linter: disable=name/o2-workflow (legacy workflow keeps its existing file name) SOURCES LumiFDDFT0.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats @@ -26,7 +34,7 @@ o2physics_add_dpl_workflow(lumifddft0 O2::DetectorsVertexing COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(fitvdm +o2physics_add_dpl_workflow(fitvdm # o2-linter: disable=name/o2-workflow (legacy workflow keeps its existing name) SOURCES fitLumi.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats @@ -35,7 +43,7 @@ o2physics_add_dpl_workflow(fitvdm O2::DetectorsVertexing COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(lumistab +o2physics_add_dpl_workflow(lumistab # o2-linter: disable=name/o2-workflow (legacy workflow keeps its existing name) SOURCES lumiStability.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats diff --git a/PWGMM/Lumi/Tasks/lumiVertex.cxx b/PWGMM/Lumi/Tasks/lumiVertex.cxx new file mode 100644 index 00000000000..d31f74b8159 --- /dev/null +++ b/PWGMM/Lumi/Tasks/lumiVertex.cxx @@ -0,0 +1,760 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file lumiVertex.cxx +/// \brief Codes for the vdM scan analysis based on the Primary Vertex reconstruction. The code is based on the lumiTask in O2Physics/PWGMM/Lumi/Tasks/lumi.cxx. +/// +/// \author Minjae Kim +/// \since Aug.06. 2026 + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/Core/trackUtilities.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace o2::aod +{ +namespace full +{ +DECLARE_SOA_COLUMN(TimeStamp, timeStamp, uint64_t); +DECLARE_SOA_COLUMN(VertexX, vertexX, double); +DECLARE_SOA_COLUMN(VertexY, vertexY, double); +DECLARE_SOA_COLUMN(VertexZ, vertexZ, double); + +DECLARE_SOA_COLUMN(VertexXX, vertexXX, double); +DECLARE_SOA_COLUMN(VertexYY, vertexYY, double); +DECLARE_SOA_COLUMN(VertexXY, vertexXY, double); + +DECLARE_SOA_COLUMN(VertexChi2, vertexChi2, double); +DECLARE_SOA_COLUMN(NContrib, nContrib, int); +DECLARE_SOA_COLUMN(Bcid, bcid, int32_t); +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); +DECLARE_SOA_COLUMN(LocalBC, localBC, int32_t); +DECLARE_SOA_COLUMN(InputMask, inputMask, uint64_t); +DECLARE_SOA_COLUMN(HasFT0, hasFT0, bool); +DECLARE_SOA_COLUMN(HasFoundFT0, hasFoundFT0, bool); +DECLARE_SOA_COLUMN(HasFT0TVX, hasFT0TVX, bool); +DECLARE_SOA_COLUMN(IsCollisionTVX, isCollisionTVX, bool); +DECLARE_SOA_COLUMN(HasAnyFT0Trigger, hasAnyFT0Trigger, bool); +DECLARE_SOA_COLUMN(HasFT0AC, hasFT0AC, bool); +DECLARE_SOA_COLUMN(Ft0TriggerMask, ft0TriggerMask, uint8_t); +DECLARE_SOA_COLUMN(Ft0Rate, ft0Rate, float); +DECLARE_SOA_COLUMN(NCollisions, nCollisions, int32_t); +DECLARE_SOA_COLUMN(NSelectedCollisions, nSelectedCollisions, int32_t); +DECLARE_SOA_COLUMN(IsCollidingBC, isCollidingBC, bool); +DECLARE_SOA_COLUMN(IsFT0SuperLeading, isFT0SuperLeading, bool); +DECLARE_SOA_COLUMN(HasPreviousFT0Activity, hasPreviousFT0Activity, bool); //! Whether a preceding FT0 CTP activity was observed +DECLARE_SOA_COLUMN(BcsSinceLastFT0Activity, bcsSinceLastFT0Activity, int64_t); //! -1 when the preceding activity is unknown +DECLARE_SOA_COLUMN(PvRefitValid, pvRefitValid, bool); +} // namespace full +DECLARE_SOA_TABLE(EventInfo, "AOD", "EventInfo", full::TimeStamp, full::VertexX, + full::VertexY, full::VertexZ, + + full::VertexXX, full::VertexYY, full::VertexXY, + + full::VertexChi2, full::NContrib, full::Bcid, full::RunNumber, + full::GlobalBC, full::HasFT0TVX, full::IsCollisionTVX, + full::HasAnyFT0Trigger, + full::IsCollidingBC, full::IsFT0SuperLeading, + full::HasPreviousFT0Activity, full::BcsSinceLastFT0Activity, + full::PvRefitValid); +DECLARE_SOA_TABLE(EventInfoBC, "AOD", "EventInfoBC", full::RunNumber, + full::TimeStamp, full::GlobalBC, full::LocalBC, + full::InputMask, full::HasFT0, full::HasFoundFT0, + full::HasFT0TVX, full::HasAnyFT0Trigger, full::HasFT0AC, + full::Ft0TriggerMask, full::Ft0Rate, full::NCollisions, + full::NSelectedCollisions, full::IsCollidingBC, + full::IsFT0SuperLeading, full::HasPreviousFT0Activity, + full::BcsSinceLastFT0Activity); +} // namespace o2::aod + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using MyBCs = soa::Join; +using CollisionsWithEvSels = soa::Join; +using UnfilteredTracks = soa::Join; + +struct LumiVertex { + Produces rowEventInfo; + Produces rowEventInfoBC; + Service ccdb{}; + const char* ccdbPathGrp = "GLO/Config/GRPMagField"; + const char* ccdbPathLut = "GLO/Param/MatLUT"; + const char* ccdburl = "http://alice-ccdb.cern.ch"; + int mFieldRunNumber = -1; + o2::base::MatLayerCylSet* mMatLUT = nullptr; + int mLHCIFRunNumber = -1; + o2::parameters::GRPLHCIFData* mLHCIFData = nullptr; + std::bitset mCollidingBCPattern; + + bool mHasPreviousFT0Activity = false; + uint64_t mGlobalBCOfLastFT0Activity = 0; + bool mHasLastProcessedBC = false; + uint64_t mLastProcessedGlobalBC = 0; + uint64_t mSkippedZeroTimestampBCs = 0; + + ctpRateFetcher mRateFetcher; + SliceCache cache; + PresliceUnsorted collisionsPerFoundBC = + aod::evsel::foundBCId; + Preslice tracksPerCollision = + aod::track::collisionId; + + struct : ConfigurableGroup { + Configurable ftts{"ftts", 0, + "Timestamp reference in ms; zero uses the fill start from GRPLHCIF"}; + Configurable nContribMax{"nContribMax", 2500, + "Maximum number of contributors"}; + Configurable nContribMin{"nContribMin", 1, + "Minimum number of contributors"}; + Configurable minVertexChi2{"minVertexChi2", 0, + "Minimum chi2 for PV selection"}; + Configurable maxVertexChi2PerContributor{"maxVertexChi2PerContributor", 999, + "Maximum refitted-vertex chi2 per contributor"}; + Configurable doBasicCheck{"doBasicCheck", true, + "Perform basic checks on the refitted vertex"}; + Configurable doCheckTVXCollision{"doCheckTVXCollision", true, + "Require the collision-level kIsTriggerTVX selection bit"}; + Configurable doPVrefit{"doPVrefit", true, + "Refit the primary vertex"}; + Configurable useMatCorrLUT{"useMatCorrLUT", true, + "Use the CCDB material LUT during PV refit; false disables material correction"}; + Configurable doHistogramAnalyisis{"doHistogramAnalyisis", true, + "Fill histograms for PV refit analysis"}; + Configurable doFetchFT0Rate{"doFetchFT0Rate", false, + "Fetch T0VTX rate from CTP scalers and store it in the BC table"}; + Configurable nBCsBeforeFT0SuperLeading{"nBCsBeforeFT0SuperLeading", 1, + "Minimum global-BC distance from the previous FT0 CTP activity"}; + + } basicConfig; + + struct : ConfigurableGroup { + Configurable requirePVContributor{"requirePVContributor", true, + "Use only tracks marked as PV contributors in the refit"}; + Configurable itsTrackPt{"itsTrackPt", 0.5, + "Minimum pt of tracks used for PV refit"}; + Configurable itsNCls{"itsNCls", 5, + "Minimum number of ITS clusters for tracks used for PV refit"}; + } trackConfig; + + HistogramRegistry histos{ + "histos", + {{"vertexx", "", {HistType::kTH1F, {{1000, -1, 1, "x"}}}}, // + {"vertexy", "", {HistType::kTH1F, {{1000, -1, 1, "y"}}}}, // + {"timestamp", "", {HistType::kTH1F, {{20000, 0, 2e7, "t"}}}}, // + {"vertexx_timestamp", + "", + {HistType::kTH2F, {{1000, 0, 4e6, "t"}, {2000, -1, 1, "x"}}}}, // + {"vertexy_timestamp", + "", + {HistType::kTH2F, {{1000, 0, 4e6, "t"}, {2000, -1, 1, "y"}}}}, // + {"chisquare", "", {HistType::kTH1F, {{1000, 0, 100, "#chi^{2}"}}}}, // + + {"vertexx_Refitted", "", {HistType::kTH1F, {{1000, -1, 1, "x"}}}}, // + {"vertexy_Refitted", "", {HistType::kTH1F, {{1000, -1, 1, "y"}}}}, // + {"vertexx_Refitted_timestamp", + "", + {HistType::kTH2F, {{1000, 0, 4e6, "t"}, {2000, -1, 1, "x"}}}}, // + {"vertexy_Refitted_timestamp", + "", + {HistType::kTH2F, {{1000, 0, 4e6, "t"}, {2000, -1, 1, "y"}}}}, // + {"chisquare_Refitted", + "", + {HistType::kTH1F, {{1000, 0, 100, "#chi^{2}"}}}}, // + + {"vertexx_Refitted_vertexx", + "", + {HistType::kTH2F, {{1000, -1, 1, "x"}, {1000, -1, 1, "rx"}}}}, // + {"vertexy_Refitted_vertexy", + "", + {HistType::kTH2F, {{1000, -1, 1, "y"}, {1000, -1, 1, "ry"}}}}, // + + {"bc/nStoredBCsVsBCID", + "Stored BC rows;BC ID in orbit;Entries", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"bc/nSkippedZeroTimestamp", + "BC rows skipped because timestamp is zero;;Entries", + {HistType::kTH1D, {{1, 0.5, 1.5}}}}, + {"bc/nCollidingBCsVsBCID", + "Stored colliding BC rows;BC ID in orbit;Entries", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"bc/nFT0ActivityBCsVsBCID", + "Stored BC rows with any FT0 CTP activity;BC ID in orbit;Entries", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"bc/nSuperLeadingFT0BCsVsBCID", + "TVX super-leading colliding BC rows;BC ID in orbit;Entries", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"bc/nNonSuperLeadingFT0BCsVsBCID", + "TVX non-super-leading colliding BC rows;BC ID in orbit;Entries", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"bc/nNoPreviousFT0ActivityBCsVsBCID", + "Colliding TVX BC rows without an observed preceding FT0 activity;BC ID in orbit;Entries", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + + {"collision/nCollisionsVsBCID", + "Collisions after the optional collision-TVX selection;found BC ID in orbit;Collisions", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/nSelectedCollisionsVsBCID", + "Collisions passing the basic PV-refit selection;BC ID in orbit;Collisions", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/nSuperLeadingFT0CollisionsVsBCID", + "Collisions in TVX super-leading BCs;BC ID in orbit;Collisions", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/nNonSuperLeadingFT0CollisionsVsBCID", + "Collisions in TVX non-super-leading BCs;BC ID in orbit;Collisions", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/nNoPreviousFT0ActivityCollisionsVsBCID", + "Collisions in TVX BCs without an observed preceding FT0 activity;BC ID in orbit;Collisions", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/nSelectedSuperLeadingFT0CollisionsVsBCID", + "Selected collisions in TVX super-leading BCs;BC ID in orbit;Collisions", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/nSelectedNonSuperLeadingFT0CollisionsVsBCID", + "Selected collisions in TVX non-super-leading BCs;BC ID in orbit;Collisions", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanVertexXVsBCID", + "Mean original vertex x by BC ID;BC ID in orbit;#LT x #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanVertexYVsBCID", + "Mean original vertex y by BC ID;BC ID in orbit;#LT y #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanRefittedVertexXVsBCID", + "Mean refitted vertex x by BC ID;BC ID in orbit;#LT x_{refit} #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanRefittedVertexYVsBCID", + "Mean refitted vertex y by BC ID;BC ID in orbit;#LT y_{refit} #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanRefittedVertexXSuperLeadingVsBCID", + "Mean refitted vertex x in TVX super-leading BCs;BC ID in orbit;#LT x_{refit} #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanRefittedVertexYSuperLeadingVsBCID", + "Mean refitted vertex y in TVX super-leading BCs;BC ID in orbit;#LT y_{refit} #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanRefittedVertexXNonSuperLeadingVsBCID", + "Mean refitted vertex x in TVX non-super-leading BCs;BC ID in orbit;#LT x_{refit} #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}, + {"collision/meanRefittedVertexYNonSuperLeadingVsBCID", + "Mean refitted vertex y in TVX non-super-leading BCs;BC ID in orbit;#LT y_{refit} #GT (cm)", + {HistType::kTProfile, {{o2::constants::lhc::LHCMaxBunches, -0.5, o2::constants::lhc::LHCMaxBunches - 0.5, "BC ID in orbit"}}}}}}; + // CTP input 3 is MTVX; std::bitset uses a zero-based bit index. + static constexpr uint8_t FT0TVXInputBit = 2; + + static bool hasFT0ACCoincidence(uint8_t triggerMask) + { + return (triggerMask & (1U << o2::ft0::Triggers::bitA)) != 0U && + (triggerMask & (1U << o2::ft0::Triggers::bitC)) != 0U; + } + + static bool hasAnyFT0Trigger(const std::bitset<64>& ctpInputMask) + { + return ctpInputMask.test(0) || ctpInputMask.test(1) || + ctpInputMask.test(FT0TVXInputBit) || ctpInputMask.test(3) || + ctpInputMask.test(4); + } + + struct FT0SuperLeadingInfo { + bool isCollidingBC = false; + bool isSuperLeading = false; + bool hasPreviousFT0Activity = false; + bool hasFT0Activity = false; + bool hasFT0TVX = false; + int64_t bcsSinceLastActivity = -1; + }; + + void resetFT0SuperLeadingHistory() + { + mHasPreviousFT0Activity = false; + mGlobalBCOfLastFT0Activity = 0; + mHasLastProcessedBC = false; + mLastProcessedGlobalBC = 0; + } + + void setLHCIFData(const auto& bc) + { + if (mLHCIFRunNumber == bc.runNumber()) { + return; + } + if (mLHCIFRunNumber >= 0 && basicConfig.doHistogramAnalyisis) { + LOGF(warn, + "Run changed from %d to %d: BCID histograms aggregate runs. " + "Use EventInfo tables or one run per job for run-separated results.", + mLHCIFRunNumber, bc.runNumber()); + } + + mLHCIFData = ccdb->getForTimeStamp( + "GLO/Config/GRPLHCIF", bc.timestamp()); + if (mLHCIFData == nullptr) { + LOGF(fatal, + "GRPLHCIFData is not available for run=%d at timestamp=%llu", + bc.runNumber(), bc.timestamp()); + return; + } + + const auto beamPatternA = + mLHCIFData->getBunchFilling().getBeamPattern(0); + const auto beamPatternC = + mLHCIFData->getBunchFilling().getBeamPattern(1); + mCollidingBCPattern = beamPatternA & beamPatternC; + mLHCIFRunNumber = bc.runNumber(); + resetFT0SuperLeadingHistory(); + + LOGF(info, "Loaded filling scheme for run %d: %zu colliding BC IDs", + mLHCIFRunNumber, mCollidingBCPattern.count()); + } + + uint64_t getRelativeTimestamp(const auto& bc) const + { + const uint64_t referenceTimestamp = + basicConfig.ftts.value > 0 + ? basicConfig.ftts.value + : static_cast(mLHCIFData->getFillNumberTime()); + if (bc.timestamp() < referenceTimestamp) { + return 0; + } + return bc.timestamp() - referenceTimestamp; + } + + FT0SuperLeadingInfo classifyFT0SuperLeading( + uint64_t globalBC, + int32_t localBC, + const std::bitset<64>& ctpInputMask) + { + if (mHasLastProcessedBC && globalBC <= mLastProcessedGlobalBC) { + LOGF(warn, + "Non-increasing global BC sequence (%llu after %llu); " + "resetting FT0 super-leading history", + globalBC, mLastProcessedGlobalBC); + resetFT0SuperLeadingHistory(); + } + + FT0SuperLeadingInfo result; + result.isCollidingBC = mCollidingBCPattern.test(localBC); + result.hasFT0Activity = hasAnyFT0Trigger(ctpInputMask); + result.hasFT0TVX = ctpInputMask.test(FT0TVXInputBit); + result.hasPreviousFT0Activity = + mHasPreviousFT0Activity && globalBC > mGlobalBCOfLastFT0Activity; + + if (result.hasPreviousFT0Activity) { + result.bcsSinceLastActivity = + static_cast(globalBC - mGlobalBCOfLastFT0Activity); + } + const bool hasSufficientFT0Gap = + !result.hasPreviousFT0Activity || + result.bcsSinceLastActivity >= + static_cast(basicConfig.nBCsBeforeFT0SuperLeading.value); + result.isSuperLeading = result.hasFT0TVX && + result.isCollidingBC && + hasSufficientFT0Gap; + + // The current BC must be classified against preceding activity, so update + // the state only after the classification above. + if (result.hasFT0Activity) { + mGlobalBCOfLastFT0Activity = globalBC; + mHasPreviousFT0Activity = true; + } + mLastProcessedGlobalBC = globalBC; + mHasLastProcessedBC = true; + return result; + } + + void updateFieldAndMaterial(const auto& bc) + { + if (mFieldRunNumber == bc.runNumber()) { + return; + } + + auto* grpo = ccdb->getForTimeStamp( + ccdbPathGrp, bc.timestamp()); + if (grpo == nullptr) { + LOGF(fatal, + "GRP object is not available in CCDB for run=%d at timestamp=%llu", + bc.runNumber(), bc.timestamp()); + } + o2::base::Propagator::initFieldFromGRP(grpo); + + if (basicConfig.useMatCorrLUT) { + auto* rawLUT = ccdb->getForTimeStamp( + ccdbPathLut, bc.timestamp()); + if (rawLUT == nullptr) { + LOGF(fatal, + "Material LUT is not available in CCDB for run=%d at timestamp=%llu", + bc.runNumber(), bc.timestamp()); + } + mMatLUT = o2::base::MatLayerCylSet::rectifyPtrFromFile(rawLUT); + o2::base::Propagator::Instance()->setMatLUT(mMatLUT); + } + + mFieldRunNumber = bc.runNumber(); + } + + void init(InitContext&) + { + if (basicConfig.nBCsBeforeFT0SuperLeading < 1) { + LOGF(fatal, + "nBCsBeforeFT0SuperLeading must be at least 1 (received %d)", + basicConfig.nBCsBeforeFT0SuperLeading.value); + } + if (basicConfig.nContribMin < 0 || + basicConfig.nContribMax <= basicConfig.nContribMin) { + LOG(fatal) << "Require 0 <= nContribMin < nContribMax"; + } + if (basicConfig.maxVertexChi2PerContributor <= 0) { + LOG(fatal) << "maxVertexChi2PerContributor must be positive"; + } + if (!basicConfig.doPVrefit && basicConfig.doBasicCheck) { + LOG(warn) << "doPVrefit=false with doBasicCheck=true will reject all " + "collision rows; set doBasicCheck=false to store them"; + } + + // A mean-vertex constraint requires loading and passing the CCDB mean + // vertex object to PVertexer. This standalone task intentionally refits + // without that external constraint. + o2::conf::ConfigurableParam::updateFromString( + "pvertexer.useMeanVertexConstraint=true"); + + ccdb->setURL(ccdburl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + uint64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + } + + bool processCollision(auto const& collision, + auto const& bc, + auto const& unfilteredTracks, + uint64_t relativeTimestamp, + uint64_t globalBC, + int32_t localBC, + const FT0SuperLeadingInfo& ft0SLInfo) + { + // Check only the collision-level TVX bit. EvSels::sel8() would also apply + // the time-frame and ITS readout-frame border selections. + const bool isCollisionTVX = + collision.selection_bit(o2::aod::evsel::kIsTriggerTVX); + if (basicConfig.doCheckTVXCollision && !isCollisionTVX) { + return false; + } + + std::vector pvContributorTracks; + int nContrib = 0; + for (const auto& track : unfilteredTracks) { + if (trackConfig.requirePVContributor && !track.isPVContributor()) { + continue; + } + if (!track.hasITS()) { + continue; + } + if (track.pt() < trackConfig.itsTrackPt || + track.itsNCls() < trackConfig.itsNCls) { + continue; + } + pvContributorTracks.push_back(getTrackParCov(track)); + nContrib++; + } + + o2::dataformats::VertexBase primaryVertex; + primaryVertex.setX(collision.posX()); + primaryVertex.setY(collision.posY()); + primaryVertex.setZ(collision.posZ()); + primaryVertex.setCov(collision.covXX(), collision.covXY(), collision.covYY(), + collision.covXZ(), collision.covYZ(), collision.covZZ()); + + double chi2 = -1.; + double refitX = -9999.; + double refitY = -9999.; + double refitZ = -9999.; + double refitXX = -9999.; + double refitYY = -9999.; + double refitXY = -9999.; + + bool pvRefitValid = false; + if (basicConfig.doPVrefit && !pvContributorTracks.empty()) { + updateFieldAndMaterial(bc); + o2::vertexing::PVertexer vertexer; + vertexer.init(); + vertexer.setMatCorrType( + basicConfig.useMatCorrLUT + ? o2::base::Propagator::MatCorrType::USEMatCorrLUT + : o2::base::Propagator::MatCorrType::USEMatCorrNONE); + + const bool pvRefitDoable = + vertexer.prepareVertexRefit(pvContributorTracks, primaryVertex); + nContrib = static_cast(vertexer.getTracksPool().size()); + if (pvRefitDoable) { + std::vector useTrackForPVRefit(pvContributorTracks.size(), true); + const auto refittedVertex = + vertexer.refitVertex(useTrackForPVRefit, primaryVertex); + const double candidateChi2 = refittedVertex.getChi2(); + if (candidateChi2 >= 0. && + std::isfinite(candidateChi2) && + std::isfinite(refittedVertex.getX()) && + std::isfinite(refittedVertex.getY()) && + std::isfinite(refittedVertex.getZ())) { + pvRefitValid = true; + nContrib = refittedVertex.getNContributors(); + chi2 = candidateChi2; + refitX = refittedVertex.getX(); + refitY = refittedVertex.getY(); + refitZ = refittedVertex.getZ(); + refitXX = refittedVertex.getSigmaX2(); + refitYY = refittedVertex.getSigmaY2(); + refitXY = refittedVertex.getSigmaXY(); + } + } + } + + const bool passesBasicCheck = + pvRefitValid && + chi2 >= basicConfig.minVertexChi2 && + nContrib >= basicConfig.nContribMin && + nContrib <= basicConfig.nContribMax && + (chi2 / nContrib) <= + basicConfig.maxVertexChi2PerContributor; + const bool passesOutputSelection = + !basicConfig.doBasicCheck || passesBasicCheck; + if (passesOutputSelection) { + rowEventInfo(relativeTimestamp, refitX, refitY, refitZ, refitXX, + refitYY, refitXY, chi2, nContrib, localBC, + static_cast(bc.runNumber()), globalBC, + ft0SLInfo.hasFT0TVX, isCollisionTVX, + ft0SLInfo.hasFT0Activity, + ft0SLInfo.isCollidingBC, ft0SLInfo.isSuperLeading, + ft0SLInfo.hasPreviousFT0Activity, + ft0SLInfo.bcsSinceLastActivity, pvRefitValid); + } + + if (!basicConfig.doHistogramAnalyisis) { + return passesOutputSelection; + } + + histos.fill(HIST("collision/nCollisionsVsBCID"), localBC); + const bool isCollidingTVX = + ft0SLInfo.isCollidingBC && ft0SLInfo.hasFT0TVX && isCollisionTVX; + if (isCollidingTVX) { + if (!ft0SLInfo.hasPreviousFT0Activity) { + histos.fill( + HIST("collision/nNoPreviousFT0ActivityCollisionsVsBCID"), + localBC); + } + if (ft0SLInfo.isSuperLeading) { + histos.fill( + HIST("collision/nSuperLeadingFT0CollisionsVsBCID"), localBC); + } else { + histos.fill( + HIST("collision/nNonSuperLeadingFT0CollisionsVsBCID"), + localBC); + } + } + if (passesBasicCheck) { + histos.fill(HIST("collision/nSelectedCollisionsVsBCID"), localBC); + if (isCollidingTVX) { + if (ft0SLInfo.isSuperLeading) { + histos.fill( + HIST("collision/nSelectedSuperLeadingFT0CollisionsVsBCID"), + localBC); + } else { + histos.fill( + HIST("collision/nSelectedNonSuperLeadingFT0CollisionsVsBCID"), + localBC); + } + } + } + + if (pvRefitValid) { + histos.fill(HIST("chisquare_Refitted"), chi2); + } + const bool passesRefitHistogramSelection = passesBasicCheck; + if (passesRefitHistogramSelection) { + histos.fill(HIST("vertexx_Refitted"), refitX); + histos.fill(HIST("vertexy_Refitted"), refitY); + histos.fill(HIST("vertexx_Refitted_timestamp"), relativeTimestamp, + refitX); + histos.fill(HIST("vertexy_Refitted_timestamp"), relativeTimestamp, + refitY); + histos.fill(HIST("collision/meanRefittedVertexXVsBCID"), localBC, + refitX); + histos.fill(HIST("collision/meanRefittedVertexYVsBCID"), localBC, + refitY); + + if (isCollidingTVX) { + if (ft0SLInfo.isSuperLeading) { + histos.fill( + HIST("collision/meanRefittedVertexXSuperLeadingVsBCID"), + localBC, refitX); + histos.fill( + HIST("collision/meanRefittedVertexYSuperLeadingVsBCID"), + localBC, refitY); + } else { + histos.fill( + HIST("collision/meanRefittedVertexXNonSuperLeadingVsBCID"), + localBC, refitX); + histos.fill( + HIST("collision/meanRefittedVertexYNonSuperLeadingVsBCID"), + localBC, refitY); + } + } + } + + histos.fill(HIST("chisquare"), collision.chi2()); + const int originalNContrib = collision.numContrib(); + const bool passesOriginalHistogramSelection = + originalNContrib > 0 && + originalNContrib >= basicConfig.nContribMin && + originalNContrib <= basicConfig.nContribMax && + (collision.chi2() / originalNContrib) <= + basicConfig.maxVertexChi2PerContributor; + if (passesOriginalHistogramSelection) { + histos.fill(HIST("vertexx"), collision.posX()); + histos.fill(HIST("vertexy"), collision.posY()); + histos.fill(HIST("timestamp"), relativeTimestamp); + histos.fill(HIST("vertexx_timestamp"), relativeTimestamp, + collision.posX()); + histos.fill(HIST("vertexy_timestamp"), relativeTimestamp, + collision.posY()); + histos.fill(HIST("collision/meanVertexXVsBCID"), localBC, + collision.posX()); + histos.fill(HIST("collision/meanVertexYVsBCID"), localBC, + collision.posY()); + } + + if (passesOriginalHistogramSelection && + passesRefitHistogramSelection) { + histos.fill(HIST("vertexx_Refitted_vertexx"), collision.posX(), + refitX); + histos.fill(HIST("vertexy_Refitted_vertexy"), collision.posY(), + refitY); + } + return passesOutputSelection; + } + + void process(MyBCs const& bcs, CollisionsWithEvSels const& collisions, + UnfilteredTracks const& unfilteredTracks, aod::FT0s const&) + { + for (const auto& bc : bcs) { + if (bc.timestamp() == 0) { + mSkippedZeroTimestampBCs++; + resetFT0SuperLeadingHistory(); + if (basicConfig.doHistogramAnalyisis) { + histos.fill(HIST("bc/nSkippedZeroTimestamp"), 1.); + } + if (mSkippedZeroTimestampBCs == 1) { + LOG(warn) << "Skipping BC rows with timestamp zero and resetting " + "the FT0 super-leading history"; + } + continue; + } + + setLHCIFData(bc); + + const uint64_t relativeTimestamp = getRelativeTimestamp(bc); + const uint64_t globalBC = bc.globalBC(); + const auto localBC = static_cast(globalBC % o2::constants::lhc::LHCMaxBunches); + const std::bitset<64> ctpInputMask(bc.inputMask()); + const bool hasFT0 = bc.has_ft0(); + const uint8_t ft0TriggerMask = + hasFT0 ? bc.ft0().triggerMask() : 0; + const auto ft0SLInfo = + classifyFT0SuperLeading(globalBC, localBC, ctpInputMask); + + const bool hasFoundFT0 = bc.has_foundFT0(); + const bool hasFT0TVX = ft0SLInfo.hasFT0TVX; + const bool hasFT0AC = hasFT0ACCoincidence(ft0TriggerMask); + const float ft0Rate = basicConfig.doFetchFT0Rate ? static_cast(mRateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), "T0VTX", true) * 1.e-3) : -1.f; + + // EvSel TVX is evaluated on the collision's found BC, so group + // collisions by foundBCId rather than the nominal collision::bcId. + auto collisionsInBC = + collisions.sliceBy(collisionsPerFoundBC, bc.globalIndex()); + + if (basicConfig.doHistogramAnalyisis) { + histos.fill(HIST("bc/nStoredBCsVsBCID"), localBC); + if (ft0SLInfo.hasFT0Activity) { + histos.fill(HIST("bc/nFT0ActivityBCsVsBCID"), localBC); + } + if (ft0SLInfo.isCollidingBC) { + histos.fill(HIST("bc/nCollidingBCsVsBCID"), localBC); + if (ft0SLInfo.hasFT0TVX) { + if (!ft0SLInfo.hasPreviousFT0Activity) { + histos.fill( + HIST("bc/nNoPreviousFT0ActivityBCsVsBCID"), localBC); + } + if (ft0SLInfo.isSuperLeading) { + histos.fill( + HIST("bc/nSuperLeadingFT0BCsVsBCID"), localBC); + } else { + histos.fill( + HIST("bc/nNonSuperLeadingFT0BCsVsBCID"), localBC); + } + } + } + } + + int32_t nSelectedCollisions = 0; + for (const auto& collision : collisionsInBC) { + auto tracksInCollision = + unfilteredTracks.sliceBy( + tracksPerCollision, + collision.globalIndex()); + + if (processCollision(collision, bc, tracksInCollision, + relativeTimestamp, globalBC, localBC, + ft0SLInfo)) { + nSelectedCollisions++; + } + } + + rowEventInfoBC( + static_cast(bc.runNumber()), relativeTimestamp, globalBC, + localBC, bc.inputMask(), hasFT0, hasFoundFT0, hasFT0TVX, + ft0SLInfo.hasFT0Activity, hasFT0AC, ft0TriggerMask, ft0Rate, + static_cast(collisionsInBC.size()), nSelectedCollisions, + ft0SLInfo.isCollidingBC, ft0SLInfo.isSuperLeading, + ft0SLInfo.hasPreviousFT0Activity, + ft0SLInfo.bcsSinceLastActivity); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec w{adaptAnalysisTask(cfgc)}; + return w; +} diff --git a/PWGMM/Mult/TableProducer/percentiles.cxx b/PWGMM/Mult/TableProducer/percentiles.cxx index d34802ab2ae..bba44ad0243 100644 --- a/PWGMM/Mult/TableProducer/percentiles.cxx +++ b/PWGMM/Mult/TableProducer/percentiles.cxx @@ -32,6 +32,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/PWGMM/Mult/Tasks/CMakeLists.txt b/PWGMM/Mult/Tasks/CMakeLists.txt index b9e85153285..89261999e06 100644 --- a/PWGMM/Mult/Tasks/CMakeLists.txt +++ b/PWGMM/Mult/Tasks/CMakeLists.txt @@ -39,6 +39,10 @@ o2physics_add_dpl_workflow(dndeta-mft-pbpb PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(qa-reassoc-mft-pbpb + SOURCES qaReassocMFTPbPb.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(mft-reassociation-validation SOURCES mftReassociationValidation.cxx diff --git a/PWGMM/Mult/Tasks/dndeta.cxx b/PWGMM/Mult/Tasks/dndeta.cxx index df1ebc63cd8..6f69f60e42c 100644 --- a/PWGMM/Mult/Tasks/dndeta.cxx +++ b/PWGMM/Mult/Tasks/dndeta.cxx @@ -42,6 +42,7 @@ #include #include +#include #include using namespace o2; @@ -842,7 +843,10 @@ struct MultiplicityCounter { using LabeledTracksEx = soa::Join; using FiLTracks = soa::Filtered; using ParticlesI = soa::Filtered>; - expressions::Filter primaries = ncheckbit(aod::mcparticle::flags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary); + expressions::Filter primaries = + ifnode(nsqrt(aod::mcparticle::vx * aod::mcparticle::vx + aod::mcparticle::vy * aod::mcparticle::vy) > 5.f, + false, + ncheckbit(aod::mcparticle_v2::storedFlags, (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary)); bool isChargedParticle(int code) { diff --git a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx index f932d186bf2..a3e9d5ed97a 100644 --- a/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx +++ b/PWGMM/Mult/Tasks/dndetaMFTPbPb.cxx @@ -22,12 +22,11 @@ #include "Common/CCDB/EventSelectionParams.h" #include "Common/CCDB/RCTSelectionFlags.h" #include "Common/CCDB/ctpRateFetcher.h" -#include "Common/Core/fwdtrackUtilities.h" #include "Common/DataModel/Centrality.h" -#include "Common/DataModel/CollisionAssociationTables.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" +#include "Common/DataModel/TrackSelectionTables.h" #include #include @@ -46,16 +45,13 @@ #include #include #include -#include -#include -#include -#include #include #include #include #include #include +#include #include #include @@ -66,17 +62,14 @@ #include #include #include -#include #include -#include #include +#include #include #include +#include #include -using SMatrix55 = ROOT::Math::SMatrix>; -using SMatrix5 = ROOT::Math::SVector; - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; @@ -89,23 +82,8 @@ using namespace o2::aod::rctsel; auto static constexpr CminCharge = 3.f; auto static constexpr CintZero = 0; -auto static constexpr Czero = 0.f; -auto static constexpr Cninety = 90.f; -auto static constexpr ConeHeighty = 180.f; -auto static constexpr CminAccFT0A = 3.5f; -auto static constexpr CmaxAccFT0A = 4.9f; -auto static constexpr CminAccFT0C = -3.3f; -auto static constexpr CmaxAccFT0C = -2.1f; - -constexpr int CevtSel = 15; -constexpr int CtrkSel = 7; -constexpr int CtrkTrkBestSel = 6; -constexpr int CambTrkType = 7; -constexpr int CselAmbTrkTypeAssocFlag = 16; -constexpr int CtrackToCollEvtType = 5; -constexpr int CreassocVtxType = 18; -constexpr int CevtReAsReAssocMCEventStatus = 5; -constexpr int CreAssocMCTrackStatus = 29; +auto static constexpr CintOne = 1; +auto static constexpr CInvalid = -999.f; enum class EvtSel { evtAll = 0, @@ -119,6 +97,7 @@ enum class EvtSel { evtNoCollInRofStrict, evtNoCollInRofStandard, evtNoHighMultCollInPrevRof, + evtGoodITSLayersAll, evtBelowMinOccup, evtAboveMaxOccup, evtRCTFlagChecker, @@ -131,20 +110,21 @@ enum class TrkSel { trkSelNCls, trkSelChi2Ncl, trkSelEta, + trkSelPhi, trkSelPhiCut, trkSelPt, trkSelCA, nTrkSel }; -enum class TrkTrkBestSel { - trkTrkBestSelAll = 0, - trkTrkBestSelCollID, - trkTrkBestSelOrphan, - trkTrkBestSelDCAxyCut, - trkTrkBestSelDCAzCut, - trkTrkBestSelNumReassoc, - nTrkTrkBestSel +enum class TrkBestSel { + trkBestSelAll = 0, + trkBestSelCollID, + trkBestSelOrphan, + trkBestSelDCAxyCut, + trkBestSelDCAzCut, + trkBestSelNumReassoc, + nTrkBestSel }; enum class AmbTrkType { @@ -158,204 +138,180 @@ enum class AmbTrkType { nAmbTrkType }; -enum AmbTrkTypeAssocFlag { - kSel = 0, - kSelGoodVtxTrue, - kSelGoodVtxBad, - kSelNonAmbAll, - kSelNonAmbGoodVtxTrue, - kSelNonAmbGoodVtxBad, - kSelNonAmbSameAll, - kSelNonAmbSameGoodVtxTrue, - kSelNonAmbSameGoodVtxBad, - kSelAmbAll, - kSelAmbGoodVtxTrue, - kSelAmbGoodVtxBad, - kSelAmbGt1All, - kSelAmbGt1GoodVtxTrue, - kSelAmbGt1GoodVtxBad, - kSelOrphanNull, - nSelAmbTrkTypeAssocFlag +enum class McGenTrkType { + kGenAll = 0, + kGenRecEvt, + nGenTrkType }; -enum class TrackToCollEvtType { - kAllRecColl = 0, - kIsInelGt0wMft, - kEvtSel, - kBestCollIdx, - kIsMcColl, - nTrackToCollEvtType +enum class GenIdxTrkType { + kGenIdxAll = 0, + kGenIdxDupl, + kGenIdxFake, + nGenIdxTrkType }; -enum class VertexStatusMC { - kNull = 0, - kGood, - kBad +enum class GenIdxTrkLightType { + kGenIdxAll = 0, + nGenIdxTrkType }; -enum class ReassocCheckVtxType { - kIsTrueVtxAllTrue = 0, - kIsTrueVtxAllFalse, - kIsTrueVtxVsGoodVtxTrue, - kIsTrueVtxVsGoodVtxFalse, - kIsTrueVtxVsBadVtxTrue, - kIsTrueVtxVsBadVtxFalse, - kIsRecGoodAllTrue, - kIsRecGoodAllFalse, - kIsRecGoodMatchAllTrue, - kIsRecGoodMatchAllFalse, - kIsRecGoodVsGoodVtxTrue, - kIsRecGoodVsGoodVtxFalse, - kIsRecGoodMatchVsGoodVtxTrue, - kIsRecGoodMatchVsGoodVtxFalse, - kIsRecGoodVsBadVtxTrue, - kIsRecGoodVsBadVtxFalse, - kIsRecGoodMatchVsBadVtxTrue, - kIsRecGoodMatchVsBadVtxFalse, - nReassocVtxType +enum class RecTrkType { + kRecAll = 0, + kRecFake, + nRecTrkType }; -enum class ReAssocMCEventStatus { - kEvtReAsAll = 0, - kEvtReAsSelected, - kEvtReAsHasMcColl, - kEvtReAsSplitVtxRemoved, - kEvtReAsZVtxCutMC, - nEvtReAsReAssocMCEventStatus +enum class RecIdxTrkType { + kRecIdxPrim = 0, + kRecIdxSec, + kRecIdxDupl, + nRecIdxTrkType }; -enum class ReAssocMCTrackStatus { - kTrkReAssocAll = 0, - kTrkBestSel, - kTrkSel, +enum class RecIdxTrkLightType { + kRecIdxPrim = 0, + kRecIdxSec, + nRecIdxTrkType +}; + +enum class EvtLossType { + kGenAll = 0, + kGenSel, + kGenSplit, + kGenRecEvt, + nEvtLossType +}; + +enum class DataEvtStatus { + kDataAll = 0, + kDataSel, + kDataRej, + nDataEvtStatus +}; + +enum class McEvtStatus { + kMcRecAll = 0, + kMcRecSel, + kMcRecHasMcColl, + kMcRecNoSplitVtx, + kMcGenAll, + nMcEvtStatus +}; + +enum class McEvtLossStatus { + kMcEvtAll = 0, + kMcEvtVtxZ, + kMcEvtTVX, + kMcEvtInelGt0, + kMcEvtInelGt0wMft, + kMcEvtStatus +}; + +enum class McEffStatus { + kMcEffAll = 0, + kMcEffSel, + kMcEffHasMcColl, + kMcEffNoSplitVtx, + nMcEfftStatus +}; + +enum class McTrackStatus { + kBestTrkAll = 0, + kBesTrktSel, + kTrkAsBestSel, kTrkHasColl, kTrkReassignedRemoved, - kTrkHasMcPart, - kTrkNonAmbAll, - kTrkNonAmbGood, - kTrkNonAmbBad, - kTrkAmbAll, - kTrkAmbGood, - kTrkAmbBad, - kTrkNonAmbAllE, - kTrkNonAmbGoodE, - kTrkNonAmbBadE, - kAssoc, - kAssocGood, - kAssocGoodIsCompTrue, - kAssocGoodIsCompFalse, - kAssocBad, - kAssocBadIsCompTrue, - kAssocBadIsCompFalse, - kReAssoc, - kReAssocGood, - kReAssocGoodIsCompTrue, - kReAssocGoodIsCompFalse, - kReAssocBad, - kReAssocBadIsCompTrue, - kReAssocBadIsCompFalse, - nReAssocMCTrackStatus + kTrkFake, + nMcTrackStatus +}; + +enum class OccupancyEst { + TrkITS = 1, + Ft0C }; -enum class HistStatusReAssocVtx { - kTrkNonAmbAll = 0, - kTrkNonAmbGood, - kTrkNonAmbBad, - kTrkAmbAll, - kTrkAmbGood, - kTrkAmbBad, - kTrkNonAmbAllE, - kTrkNonAmbGoodE, - kTrkNonAmbBadE, - kAssoc, - kAssocGood, - kAssocGoodIsCompTrue, - kAssocGoodIsCompFalse, - kAssocBad, - kAssocBadIsCompTrue, - kAssocBadIsCompFalse, - kReAssoc, - kReAssocGood, - kReAssocGoodIsCompTrue, - kReAssocGoodIsCompFalse, - kReAssocBad, - kReAssocBadIsCompTrue, - kReAssocBadIsCompFalse, - nHistStatusReAssocVtx +enum class McRecTrkType { + kPrimary = 0, + kSecondary, + kStrangeDecay, + kTrkSel, + kTrkFake, + nMcRecTrkStatus +}; + +struct MultE { + static constexpr int CnoMult = 0; + static constexpr int CmultFT0C = 1; + static constexpr int CmultFT0M = 2; + static constexpr int CmultTPC = 3; + static constexpr int CmultMFT = 4; }; -std::unordered_map mapVtxXrec; -std::unordered_map mapVtxYrec; -std::unordered_map mapVtxZrec; -std::unordered_map mapVtxXgen; -std::unordered_map mapVtxYgen; -std::unordered_map mapVtxZgen; -std::unordered_map mapMcCollIdPerRecColl; +static constexpr TrackSelectionFlags::flagtype TrackSelectionITS = + TrackSelectionFlags::kITSNCls | TrackSelectionFlags::kITSChi2NDF | + TrackSelectionFlags::kITSHits; +static constexpr TrackSelectionFlags::flagtype TrackSelectionTPC = + TrackSelectionFlags::kTPCNCls | + TrackSelectionFlags::kTPCCrossedRowsOverNCls | + TrackSelectionFlags::kTPCChi2NDF; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDCA = + TrackSelectionFlags::kDCAz | TrackSelectionFlags::kDCAxy; +static constexpr TrackSelectionFlags::flagtype TrackSelectionDCAXYonly = + TrackSelectionFlags::kDCAxy; struct DndetaMFTPbPb { SliceCache cache; - std::array, 4> hCollAssoc; - std::array, 23> hReAssocVtxRes; - std::array, 23> hReAssocDCA; - // std::array, 23> hReAssocDCAPrim; - std::array, 21> hTimeAssocWithReassocMC; - - enum OccupancyEst { TrkITS = 1, - Ft0C }; - - HistogramRegistry registry{ - "registry", - {}, - OutputObjHandlingPolicy::AnalysisObject}; - HistogramRegistry qaregistry{ - "qaregistry", - {}, - OutputObjHandlingPolicy::AnalysisObject, - false, - true}; - - HistogramRegistry registryAlign{"registryAlign", {}}; - std::array mftQuadrant = {"Q0", "Q1", "Q2", "Q3"}; - std::array, 3>, 4>, 2> hAlignment; + Preslice perMCCol = aod::mcparticle::mcCollisionId; + PresliceUnsorted perColBestTrks = aod::fwdtrack::bestCollisionId; + + HistogramRegistry registryData{"registryData", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryMC{"registryMC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry registryQC{"registryQC", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; struct : ConfigurableGroup { - Configurable cfgDoIR{"cfgDoIR", false, "Flag to retrieve Interaction rate from CCDB"}; - Configurable cfgUseIRCut{"cfgUseIRCut", false, "Flag to cut on IR rate"}; - Configurable cfgIRCrashOnNull{"cfgIRCrashOnNull", false, "Flag to avoid CTP RateFetcher crash"}; - Configurable cfgIRSource{"cfgIRSource", "ZNC hadronic", "Estimator of the interaction rate (Pb-Pb: ZNC hadronic)"}; - Configurable cfgUseTrackSel{"cfgUseTrackSel", false, "Flag to apply track selection"}; - Configurable cfgUseParticleSel{"cfgUseParticleSel", false, "Flag to apply particle selection"}; - Configurable cfgUsePrimaries{"cfgUsePrimaries", false, "Select primary particles"}; - Configurable cfgRemoveTrivialAssoc{"cfgRemoveTrivialAssoc", false, "Skip trivial associations"}; - Configurable cfgRemoveAmbiguousTracks{"cfgRemoveAmbiguousTracks", false, "Remove ambiguous tracks"}; - Configurable cfgRemoveOrphanTracks{"cfgRemoveOrphanTracks", true, "Remove orphan tracks"}; - Configurable cfgRemoveReassigned{"cfgRemoveReassigned", false, "Remove reassgined tracks"}; - Configurable cfgRemoveSplitVertex{"cfgRemoveSplitVertex", true, "Remove split vertices"}; - Configurable cfgUseTrackParExtra{"cfgUseTrackParExtra", false, "Use table with refitted track parameters"}; - Configurable cfgUseInelgt0{"cfgUseInelgt0", false, "Use INEL > 0 condition"}; - Configurable cfgUseInelgt0wMFT{"cfgUseInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; + Configurable dcaZmidrap{"dcaZmidrap", 0.2f, "Custom DCA Z cut (ignored if negative)"}; + Configurable useEvtSel{"useEvtSel", true, "Use event selection"}; + Configurable useIRFromCCDB{"useIRFromCCDB", false, "Flag to retrieve Interaction rate from CCDB"}; + Configurable useIRCut{"useIRCut", false, "Flag to cut on IR rate"}; + Configurable intRateCrashOnNull{"intRateCrashOnNull", false, "Flag to avoid CTP RateFetcher crash"}; + Configurable intRateSource{"intRateSource", "ZNC hadronic", "Estimator of the interaction rate (Pb-Pb: ZNC hadronic)"}; + Configurable useTrackSel{"useTrackSel", false, "Flag to apply track selection"}; + Configurable useParticleSel{"useParticleSel", true, "Flag to apply particle selection"}; + Configurable usePrimaries{"usePrimaries", true, "Select primary particles"}; + Configurable useSecondaries{"useSecondaries", false, "Select secondary particles"}; + Configurable rmReassigned{"rmReassigned", false, "Remove reassgined tracks"}; + Configurable rmSplitVertex{"rmSplitVertex", true, "Remove split vertices"}; + Configurable useInelgt0{"useInelgt0", false, "Use INEL > 0 condition"}; + Configurable useInelgt0wMFT{"useInelgt0wMFT", false, "Use INEL > 0 condition with MFT acceptance"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; - Configurable cfgDCAtype{"cfgDCAtype", 2, "DCA coordinate type [0: DCA-X, 1: DCA-Y, 2: DCA-XY]"}; + Configurable fillTrackQaHist{"fillTrackQaHist", false, "fill track QA histograms"}; + Configurable multEstimator{"multEstimator", 1, "0: without multiplicity; 1: MultFT0C; 2: MultFT0M; 3: MultTPC, 4: MultMFT"}; + Configurable useMultMCatMidRapidity{"useMultMCatMidRapidity", true, "use generated Nch in ∣eta∣ < 0.8"}; + Configurable useMultMCatMFTacc{"useMultMCatMFTacc", false, "use generated Nch in MFT acceptance"}; } gConf; struct : ConfigurableGroup { ConfigurableAxis interactionRateBins{"interactionRateBins", {500, 0, 50}, "Binning for the interaction rate (kHz)"}; ConfigurableAxis occupancyBins{"occupancyBins", {VARIABLE_WIDTH, 0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 4500.0f, 6000.0f, 8000.0f, 10000.0f, 50000.0f}, "Occupancy"}; ConfigurableAxis centralityBins{"centralityBins", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Centrality"}; + Configurable> genMultBins{"genMultBins", std::vector{500, 300, 200, 120, 80, 50, 30, 10, 0}, "Generated multiplicity low values matching reco cent bins"}; + Configurable> recCentBinCenters{"recCentBinCenters", std::vector{2.5, 7.5, 15.0, 25.0, 35.0, 45.0, 55.0, 75.0, 95.0}, "Reco cent bin centers"}; ConfigurableAxis irBins{"irBins", {500, 0, 50}, "Interaction rate (kHz)"}; ConfigurableAxis pvBins{"pvBins", {501, -0.5, 500.5}, ""}; ConfigurableAxis fv0aMultBins{"fv0aMultBins", {501, -0.5, 500.5}, ""}; ConfigurableAxis ft0aMultBins{"ft0aMultBins", {501, -0.5, 500.5}, ""}; ConfigurableAxis ft0cMultBins{"ft0cMultBins", {501, -0.5, 500.5}, ""}; - ConfigurableAxis ptBins{"ptBins", {101, -0.5, 10.5}, "pT binning (GeV/c)"}; - ConfigurableAxis multBins{"multBins", {701, -0.5, 700.5}, "Multiplicity binning"}; + ConfigurableAxis ptBins{"ptBins", {11, -0.5, 10.5}, "pT binning (GeV/c)"}; + ConfigurableAxis multBins{"multBins", {1001, -0.5, 1000.5}, "Multiplicity binning"}; ConfigurableAxis zvtxBins{"zvtxBins", {60, -30., 30.}, "Z-vtx binning (cm)"}; - ConfigurableAxis deltaZBins{"deltaZBins", {800, -10., 10.}, "Delta Z-vtx binning (cm)"}; - ConfigurableAxis dcaXYBins{"dcaXYBins", {800, -1., 1.}, "DCAxy binning (cm)"}; - ConfigurableAxis dcaZBins{"dcaZBins", {800, -1., 1.}, "DCAz binning (cm)"}; + ConfigurableAxis deltaZBins{"deltaZBins", {200, -1., 1.}, "Delta Z-vtx binning (cm)"}; + ConfigurableAxis dcaXYBins{"dcaXYBins", {100, -0.5, 0.5}, "DCAxy binning (cm)"}; + ConfigurableAxis dcaZBins{"dcaZBins", {100, -0.5, 0.5}, "DCAz binning (cm)"}; ConfigurableAxis phiBins{"phiBins", {629, 0., TwoPI}, "#varphi binning (rad)"}; ConfigurableAxis etaBins{"etaBins", {20, -4., -2.}, "#eta binning"}; + ConfigurableAxis etaMidrapBins{"etaMidrapBins", {40, -2., 2.}, "#eta midrapidity binning"}; ConfigurableAxis chiSqPerNclBins{"chiSqPerNclBins", {100, 0, 100}, "#chi^{2} binning"}; ConfigurableAxis nClBins{"nClBins", {10, 0.5, 10.5}, "number of clusters binning"}; ConfigurableAxis tanLambdaBins{"tanLambdaBins", {100, -25, 0}, "binning for tan(lambda)"}; @@ -371,71 +327,73 @@ struct DndetaMFTPbPb { } rctCuts; struct : ConfigurableGroup { + Configurable etaRangeMidrap{"etaRangeMidrap", 1.0f, "Eta range for midrapidity tracks"}; Configurable usephiCut{"usephiCut", false, "use azimuthal angle cut"}; Configurable phiCut{"phiCut", 0.1f, "Cut on azimuthal angle of MFT tracks"}; Configurable minPhi{"minPhi", 0.f, ""}; Configurable maxPhi{"maxPhi", 6.2832, ""}; Configurable minEta{"minEta", -3.6f, ""}; Configurable maxEta{"maxEta", -2.5f, ""}; + Configurable minEtaGenMult{"minEtaGenMult", -0.5, "Min eta for generated mid-rapidity multiplicity"}; + Configurable maxEtaGenMult{"maxEtaGenMult", 0.5, "Max eta for generated mid-rapidity multiplicity"}; Configurable minNclusterMft{"minNclusterMft", 5, "minimum number of MFT clusters"}; - Configurable useChi2Cut{"useChi2Cut", false, "use track chi2 cut"}; - Configurable maxChi2NCl{"maxChi2NCl", 1000.f, "maximum chi2 per MFT clusters"}; + Configurable useChi2Cut{"useChi2Cut", true, "use track chi2 cut"}; + Configurable maxChi2NCl{"maxChi2NCl", 40.0f, "maximum chi2 per MFT clusters"}; Configurable usePtCut{"usePtCut", false, "use track pT cut"}; Configurable minPt{"minPt", 0., "minimum pT of the MFT tracks"}; Configurable requireCA{"requireCA", false, "Use Cellular Automaton track-finding algorithm"}; - Configurable maxDCAxy{"maxDCAxy", 0.01f, "Cut on dca XY"}; - Configurable maxDCAz{"maxDCAz", 0.01f, "Cut on dca Z"}; + Configurable maxDCAxy{"maxDCAxy", 2.0f, "Cut on dca XY"}; + Configurable useDCAzCut{"useDCAzCut", true, "use dca Z cut"}; + Configurable maxDCAz{"maxDCAz", 2.0f, "Cut on dca Z"}; + Configurable removeFakeByMcMask{"removeFakeByMcMask", 1, "Remove fakes based on MC mask"}; + Configurable selMcMask{"selMcMask", 0, "McMask for correct match"}; } trackCuts; struct : ConfigurableGroup { - Configurable maxZvtx{"maxZvtx", 20.0f, "maximum cut on z-vtx (cm)"}; - Configurable minZvtx{"minZvtx", -20.0f, "minimum cut on z-vtx (cm)"}; + Configurable minCentrality{"minCentrality", 0.0f, "minimum centrality selection"}; + Configurable maxCentrality{"maxCentrality", 100.0f, "maximum centrality selection"}; + Configurable maxZvtx{"maxZvtx", 10.0f, "maximum cut on z-vtx (cm)"}; + Configurable minZvtx{"minZvtx", -10.0f, "minimum cut on z-vtx (cm)"}; Configurable useZDiffCut{"useZDiffCut", false, "use Zvtx reco-mc diff. cut"}; Configurable maxZvtxDiff{"maxZvtxDiff", 1.0f, "max allowed Z vtx difference for reconstruced collisions (cm)"}; - Configurable useZVtxCutMC{"useZVtxCutMC", false, "use Zvtx cut in MC"}; + Configurable useZVtxCutMC{"useZVtxCutMC", true, "use Zvtx cut in MC"}; Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; Configurable requireRejectSameBunchPileup{"requireRejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, " requireNoCollInTimeRangeStrict"}; Configurable requireNoCollInRofStrict{"requireNoCollInRofStrict", true, "requireNoCollInRofStrict"}; - Configurable requireNoCollInRofStandard{"requireNoCollInRofStandard", false, "requireNoCollInRofStandard"}; - Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", false, "requireNoHighMultCollInPrevRof"}; - Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; + Configurable requireNoCollInRofStandard{"requireNoCollInRofStandard", true, "requireNoCollInRofStandard"}; + Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", true, "requireNoHighMultCollInPrevRof"}; + Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", true, "requireGoodITSLayersAll"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", true, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; Configurable occupancyEstimator{"occupancyEstimator", 1, "Occupancy estimator: 1 = trackOccupancyInTimeRange, 2 = ft0cOccupancyInTimeRange"}; Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; Configurable minIR{"minIR", -1, "minimum IR (kHz) collisions"}; Configurable maxIR{"maxIR", -1, "maximum IR (kHz) collisions"}; + Configurable requireTVX{"requireTVX", false, "Use TVX trigger, i.e. FT0A and FT0C acceptance"}; + Configurable useGenMult{"useGenMult", false, "use MultMC for centrality"}; + Configurable checkCollisionId{"checkCollisionId", false, "check collision ids"}; } eventCuts; - Service pdg; - Service ccdb; + Service pdg{}; + Service ccdb{}; Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift from CCDB"}; - Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; - Configurable cfgManualZShift{"cfgManualZShift", 0.0f, "manual z-shift for propagation of global muon to PV"}; int mRunNumber{-1}; uint64_t mSOR{0}; float mMinSeconds{-1.}; std::unordered_map gHadronicRate; ctpRateFetcher rateFetcher; - TH2* gCurrentHadronicRate; + TH2* gCurrentHadronicRate = nullptr; RCTFlagsChecker rctChecker; RCTFlagsChecker rctCheckerExtra{kFT0Bad, kITSBad, kTPCBadTracking, kMFTBad}; - float bZ = 0; // Magnetic field for MFT - static constexpr double CcenterMFT[3] = {0, 0, -61.4}; // Field at center of MFT - float mZShift = 0; // z-vertex shift - + float bZ = 0; // Magnetic field for MFT + std::array centerMFT{0, 0, -61.4}; // Field at center of MFT o2::parameters::GRPMagField* grpmag = nullptr; - std::vector ambiguousTrkIds; - std::vector reassignedTrkIds; - std::vector ambiguousTrkIdsMC; - std::vector reassignedTrkIdsMC; - /// @brief init function, definition of histograms void init(InitContext&) { @@ -454,10 +412,18 @@ struct DndetaMFTPbPb { const AxisSpec dcazAxis = {binOpt.dcaZBins, "DCA-z axis"}; const AxisSpec phiAxis = {binOpt.phiBins, "#phi axis"}; const AxisSpec etaAxis = {binOpt.etaBins, "#eta axis"}; + const AxisSpec etaMidrapAxis = {binOpt.etaMidrapBins, "#eta midrapidity axis"}; const AxisSpec chiSqAxis = {binOpt.chiSqPerNclBins, "Chi2 axis"}; const AxisSpec nclsAxis = {binOpt.nClBins, "Number of clusters axis"}; const AxisSpec tanLambdaAxis{binOpt.tanLambdaBins, "tan(#lambda)"}; const AxisSpec invQPtAxis{binOpt.invQPtBins, "q/p_{T} (1/GeV)"}; + const AxisSpec recTrkTypeAxis = {static_cast(McRecTrkType::nMcRecTrkStatus), -0.5, +static_cast(McRecTrkType::nMcRecTrkStatus) - 0.5, "", "MC Rec-Trk Type axis"}; + const AxisSpec genTrkTypeAxis = {static_cast(McGenTrkType::nGenTrkType), -0.5, +static_cast(McGenTrkType::nGenTrkType) - 0.5, "", "MC Gen-Trk Type axis"}; + const AxisSpec genEvtIdxTypeAxis = {static_cast(GenIdxTrkType::nGenIdxTrkType), -0.5, +static_cast(GenIdxTrkType::nGenIdxTrkType) - 0.5, "", "Gen-idx-Trk Type axis"}; + const AxisSpec recEvtIdxTypeAxis = {static_cast(RecIdxTrkType::nRecIdxTrkType), -0.5, +static_cast(RecIdxTrkType::nRecIdxTrkType) - 0.5, "", "Rec-idx-Trk Type axis"}; + const AxisSpec genEvtIdxLightTypeAxis = {static_cast(GenIdxTrkLightType::nGenIdxTrkType), -0.5, +static_cast(GenIdxTrkLightType::nGenIdxTrkType) - 0.5, "", "Gen-idx-Trk Light Type axis"}; + const AxisSpec recEvtIdxLightTypeAxis = {static_cast(RecIdxTrkLightType::nRecIdxTrkType), -0.5, +static_cast(RecIdxTrkLightType::nRecIdxTrkType) - 0.5, "", "Rec-idx-Trk Light Type axis"}; + const AxisSpec evtLossTypeAxis = {static_cast(EvtLossType::nEvtLossType), -0.5, +static_cast(EvtLossType::nEvtLossType) - 0.5, "", "Evt-loss Type axis"}; rctChecker.init(rctCuts.cfgEvtRCTFlagCheckerLabel, rctCuts.cfgEvtRCTFlagCheckerZDCCheck, rctCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); @@ -467,1224 +433,629 @@ struct DndetaMFTPbPb { ccdb->setCreatedNotAfter(ccdbNoLaterThan.value); ccdb->setFatalWhenNull(false); - if (static_cast(doprocessDataInclusive) + - static_cast(doprocessDatawBestTracksInclusive) > - 1) { - LOGP(fatal, - "Either processDataInclusive OR " - "processDatawBestTracksInclusive should be enabled!"); - } - if ((static_cast(doprocessDataCentFT0C) + - static_cast(doprocessDatawBestTracksCentFT0C) > - 1) || - (static_cast(doprocessDataCentFT0CVariant1) + - static_cast(doprocessDatawBestTracksCentFT0CVariant1) > - 1) || - (static_cast(doprocessDataCentFT0M) + - static_cast(doprocessDatawBestTracksCentFT0M) > - 1) || - (static_cast(doprocessDataCentNGlobal) + - static_cast(doprocessDatawBestTracksCentNGlobal) > - 1) || - (static_cast(doprocessDataCentMFT) + - static_cast(doprocessDatawBestTracksCentMFT) > - 1)) { - LOGP(fatal, - "Either processDataCent[ESTIMATOR] OR " - "processDatawBestTracksCent[ESTIMATOR] should " - "be enabled!"); - } - if (static_cast(doprocessMCInclusive) + - static_cast(doprocessMCwBestTracksInclusive) > - 1) { - LOGP(fatal, - "Either processMCInclusive OR processMCwBestTracksInclusive " - "should be enabled!"); - } - if ((static_cast(doprocessMCCentFT0C) + - static_cast(doprocessMCwBestTracksCentFT0C) > - 1) || - (static_cast(doprocessMCCentFT0CVariant1) + - static_cast(doprocessMCwBestTracksCentFT0CVariant1) > - 1) || - (static_cast(doprocessMCCentFT0M) + - static_cast(doprocessMCwBestTracksCentFT0M) > - 1) || - (static_cast(doprocessMCCentNGlobal) + - static_cast(doprocessMCwBestTracksCentNGlobal) > - 1) || - (static_cast(doprocessMCCentMFT) + - static_cast(doprocessMCwBestTracksCentMFT) > - 1)) { - LOGP(fatal, - "Either processMCCent[ESTIMATOR] OR " - "processMCwBestTracksCent[ESTIMATOR] should " - "be enabled!"); - } - - // registry.add("Events/hRCTSel", "Event accepted if RCT not selected;RCT Status;", {HistType::kTH1F, {{2, 0.5, 2.5}}}); - // auto hrctSel = registry.get(HIST("Events/hRCTSel")); - // auto* x = hrctSel->GetXaxis(); - // x->SetBinLabel(1, "All"); - // x->SetBinLabel(2, "kFT0Bad"); - - registry.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); - std::string labelEvtSel[CevtSel]; - labelEvtSel[static_cast(EvtSel::evtAll)] = "All coll."; - labelEvtSel[static_cast(EvtSel::evtSel)] = "Sel 8"; - labelEvtSel[static_cast(EvtSel::evtIsGoodZvtx)] = "kIsGoodZvtxFT0vsPV"; - labelEvtSel[static_cast(EvtSel::evtNoSameBunchPileup)] = "NoSameBunchPileup"; - labelEvtSel[static_cast(EvtSel::evtZvtxCut)] = "Z-vtx cut"; - labelEvtSel[static_cast(EvtSel::evtNoCollInTimeRangeStd)] = "kNoCollInTimeRangeStd"; - labelEvtSel[static_cast(EvtSel::evtNoCollInTimeRangeNarrow)] = "kNoCollInTimeRangeNarrow"; - labelEvtSel[static_cast(EvtSel::evtNoCollInTimeRangeStrict)] = "kNoCollInTimeRangeStrict"; - labelEvtSel[static_cast(EvtSel::evtNoCollInRofStrict)] = "kNoCollInRofStrict"; - labelEvtSel[static_cast(EvtSel::evtNoCollInRofStandard)] = "kNoCollInRofStandard"; - labelEvtSel[static_cast(EvtSel::evtNoHighMultCollInPrevRof)] = "kNoHighMultCollInPrevRof"; - labelEvtSel[static_cast(EvtSel::evtBelowMinOccup)] = "Below min occup."; - labelEvtSel[static_cast(EvtSel::evtAboveMaxOccup)] = "Above max occup."; - labelEvtSel[static_cast(EvtSel::evtRCTFlagChecker)] = "RCT Flag Checker"; - labelEvtSel[static_cast(EvtSel::evtRCTFlagCheckerExtra)] = "RCT Flag Checker Extra"; - registry.get(HIST("Events/hEvtSel"))->SetMinimum(0.1); + if (static_cast(doprocessDataInclusive) + static_cast(doprocessDatawBestTracksInclusive) > 1) { + LOGP(fatal, "Either processDataInclusive OR processDatawBestTracksInclusive should be enabled!"); + } + if (static_cast(doprocessDataCentFT0C) + static_cast(doprocessDatawBestTracksCentFT0C) > 1) { + LOGP(fatal, "Either processDataCent[ESTIMATOR] OR processDatawBestTracksCent[ESTIMATOR] should be enabled!"); + } + if (static_cast(doprocessMcInclusive) + static_cast(doprocessMcBestInclusive) > 1) { + LOGP(fatal, "Either processMcInclusive OR processMcBestInclusive should be enabled!"); + } + if (static_cast(doprocessMcCentFT0C) + static_cast(doprocessMcBestCentFT0C) > 1) { + LOGP(fatal, "Either processMCCent[ESTIMATOR] OR processMCbestCent[ESTIMATOR] should be enabled!"); + } + if (static_cast(doprocessMcEfficiencyInclusive) + static_cast(doprocessMcEfficiencyBestInclusive) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyInclusive OR doprocessMcEfficiencyBestInclusive should be enabled!"); + } + if (static_cast(doprocessMcEfficiencyCentFT0C) + static_cast(doprocessMcEfficiencyBestCentFT0C) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyCentFT0C OR doprocessMcEfficiencyBestCentFT0C should be enabled!"); + } + if (static_cast(doprocessMcEfficiencyIdxInlusive) + static_cast(doprocessMcEfficiencyIdxBestInlusive) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxInlusive OR doprocessMcEfficiencyIdxBestInlusive should be enabled!"); + } + if (static_cast(doprocessMcEfficiencyIdxBestInlusive) + static_cast(doprocessMcEfficiencyIdxBestLightInlusive) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxBestInlusive OR doprocessMcEfficiencyIdxBestLightInlusive should be enabled!"); + } + if (static_cast(doprocessMcEfficiencyIdxCentFT0C) + static_cast(doprocessMcEfficiencyIdxBestCentFT0C) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxCentFT0C OR doprocessMcEfficiencyIdxBestCentFT0C should be enabled!"); + } + if (static_cast(doprocessMcEfficiencyIdxBestCentFT0C) + static_cast(doprocessMcEfficiencyIdxBestLightCentFT0C) > 1) { + LOGP(fatal, "Either doprocessMcEfficiencyIdxBestCentFT0C OR doprocessMcEfficiencyIdxBestLightCentFT0C should be enabled!"); + } + + registryQC.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); + std::array(EvtSel::nEvtSel)> labelEvtSel{ + "All coll.", + "Sel 8", + "kIsGoodZvtxFT0vsPV", + "NoSameBunchPileup", + "Z-vtx cut", + "kNoCollInTimeRangeStd", + "kNoCollInTimeRangeNarrow", + "kNoCollInTimeRangeStrict", + "kNoCollInRofStrict", + "kNoCollInRofStandard", + "kNoHighMultCollInPrevRof", + "kIsGoodITSLayersAll", + "Below min occup.", + "Above max occup.", + "RCT Flag Checker", + "RCT Flag Checker Extra"}; + registryQC.get(HIST("Events/hEvtSel"))->SetMinimum(0.1); for (int iBin = 0; iBin < static_cast(EvtSel::nEvtSel); iBin++) { - registry.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); - } - - registry.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkTrkBestSel::nTrkTrkBestSel), -0.5, +static_cast(TrkTrkBestSel::nTrkTrkBestSel) - 0.5}}}); - std::string labelTrkTrkBestSel[CtrkTrkBestSel]; - labelTrkTrkBestSel[static_cast(TrkTrkBestSel::trkTrkBestSelAll)] = "All"; - labelTrkTrkBestSel[static_cast(TrkTrkBestSel::trkTrkBestSelCollID)] = "Assigned (ID>=0)"; - labelTrkTrkBestSel[static_cast(TrkTrkBestSel::trkTrkBestSelOrphan)] = "No orphans"; - labelTrkTrkBestSel[static_cast(TrkTrkBestSel::trkTrkBestSelDCAxyCut)] = "DCA xy cut"; - labelTrkTrkBestSel[static_cast(TrkTrkBestSel::trkTrkBestSelDCAzCut)] = "DCA z cut"; - labelTrkTrkBestSel[static_cast(TrkTrkBestSel::trkTrkBestSelNumReassoc)] = "#Reassoc"; - registry.get(HIST("Tracks/hBestTrkSel"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(TrkTrkBestSel::nTrkTrkBestSel); iBin++) { - registry.get(HIST("Tracks/hBestTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkTrkBestSel[iBin].data()); - } - - registry.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); - std::string labelTrkSel[CtrkSel]; - labelTrkSel[static_cast(TrkSel::trkSelAll)] = "All"; - labelTrkSel[static_cast(TrkSel::trkSelNCls)] = "Ncls"; - labelTrkSel[static_cast(TrkSel::trkSelChi2Ncl)] = "Chi2"; - labelTrkSel[static_cast(TrkSel::trkSelEta)] = "Eta"; - labelTrkSel[static_cast(TrkSel::trkSelPhiCut)] = "Phi cut"; - labelTrkSel[static_cast(TrkSel::trkSelPt)] = "Pt"; - labelTrkSel[static_cast(TrkSel::trkSelCA)] = "CA"; - registry.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { - registry.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); - } - - auto hBcSel = registry.add("hBcSel", "hBcSel", HistType::kTH1F, - {{3, -0.5f, +2.5f}}); - hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); - hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); - hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); + registryQC.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); + } - if (doprocessDataInclusive || doprocessDatawBestTracksInclusive || - doprocessMCInclusive || doprocessMCwBestTracksInclusive) { - registry.add({"Events/Selection", - ";status;occupancy", - {HistType::kTH2F, {{2, 0.5, 2.5}, occupancyAxis}}}); - auto hstat = registry.get(HIST("Events/Selection")); - auto* x = hstat->GetXaxis(); - x->SetBinLabel(1, "All"); - x->SetBinLabel(2, "Selected"); - - registry.add("Events/hInteractionRate", "; occupancy; IR (kHz)", kTH2F, {occupancyAxis, irAxis}); - - registry.add({"Events/NtrkZvtx", - "; N_{trk}; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registry.add({"Tracks/EtaZvtx", - "; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/PhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - - qaregistry.add( - {"Tracks/Chi2Eta", - "; #chi^{2}; #it{#eta}; occupancy", - {HistType::kTHnSparseF, {chiSqAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Chi2", - "; #chi^{2};", - {HistType::kTH2F, {chiSqAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/NclustersEta", - "; nClusters; #eta; occupancy", - {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/NchSel", - "; N_{ch}; occupancy", - {HistType::kTH2F, {multAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/NchBestSel", - "; N_{ch}; occupancy", - {HistType::kTH2F, {multAxis, occupancyAxis}}}); - - qaregistry.add({"Tracks/TanLambda", "; TanLambda; centrality; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/InvQPt", "; InvQPt; centrality; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Eta", "; #eta; centrality; occupancy", {HistType::kTH2F, {etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Phi", "; #varphi; centrality; occupancy", {HistType::kTH2F, {phiAxis, occupancyAxis}}}); + if (doprocessTagging) { + auto hBcSel = registryQC.add("hBcSel", "hBcSel", HistType::kTH1F, {{3, -0.5f, +2.5f}}); + hBcSel->GetXaxis()->SetBinLabel(1, "Good BCs"); + hBcSel->GetXaxis()->SetBinLabel(2, "BCs with collisions"); + hBcSel->GetXaxis()->SetBinLabel(3, "BCs with pile-up/splitting"); + } + if (doprocessDataInclusive || doprocessDatawBestTracksInclusive || + doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C || + doprocessMcInclusive || doprocessMcBestInclusive || + doprocessMcCentFT0C || doprocessMcBestCentFT0C || + doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive || + doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C || + doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxCentFT0C || + doprocessMcEfficiencyIdxBestLightInlusive || doprocessMcEfficiencyIdxBestLightCentFT0C || + doprocessDataCorrelationwBestTracksInclusive || doprocessMcReassocDCA) { + registryQC.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); + std::array(TrkSel::nTrkSel)> labelTrkSel{ + "All", + "Ncls", + "Chi2", + "Eta", + "Phi", + "Phi cut", + "Pt", + "CA"}; + registryQC.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { + registryQC.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); + } + } + + if (doprocessDatawBestTracksInclusive || doprocessDatawBestTracksCentFT0C || + doprocessMcBestInclusive || doprocessMcBestCentFT0C || + doprocessMcEfficiencyBestInclusive || doprocessMcEfficiencyBestCentFT0C || + doprocessMcEfficiencyIdxBestInlusive || doprocessMcEfficiencyIdxBestCentFT0C || + doprocessMcEfficiencyIdxBestLightInlusive || doprocessMcEfficiencyIdxBestLightCentFT0C || + doprocessDataCorrelationwBestTracksInclusive) { + registryQC.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkBestSel::nTrkBestSel), -0.5, +static_cast(TrkBestSel::nTrkBestSel) - 0.5}}}); + std::array(TrkBestSel::nTrkBestSel)> labelTrkTrkBestSel{ + "All", + "Assigned (ID>=0)", + "No orphans", + "DCA xy cut", + "DCA z cut", + "#Reassoc"}; + registryQC.get(HIST("Tracks/hBestTrkSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(TrkBestSel::nTrkBestSel); iBin++) { + registryQC.get(HIST("Tracks/hBestTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkTrkBestSel[iBin].data()); + } + } + if (doprocessDataInclusive || doprocessDatawBestTracksInclusive) { + if (gConf.useIRFromCCDB) { + registryData.add({"Events/hInteractionRate", "; IR (kHz); occupancy", {HistType::kTH2F, {irAxis, occupancyAxis}}}); + } + registryData.add("Events/DataEvtStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, occupancyAxis}}); + std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatus{ + "Data all", + "Data sel", + "Data rejected"}; + for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { + registryData.get(HIST("Events/DataEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelDataEvtStatus[iBin].data()); + } + registryData.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); + registryData.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryData.add({"Events/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/Chi2Eta", "; #chi^{2}; #eta; occupancy", {HistType::kTH2F, {chiSqAxis, occupancyAxis}}}); + registryData.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTH2F, {nclsAxis, occupancyAxis}}}); + } if (doprocessDatawBestTracksInclusive) { - registry.add( - {"Events/NtrkZvtxBest", - "; N_{trk}; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/EtaZvtxBest", - "; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/PhiEtaBest", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/NclustersEtaBest", - "; nClusters; #eta; occupancy", - {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/DCA3d", - "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); occupancy", - {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/ReTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/ReTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/OrigTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/OrigTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/RestTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/RestTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/TrackAmbDegree", - "; N_{coll}^{comp}; occupancy", - {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); - qaregistry.add({"Tracks/TanLambdaExtra", "; TanLambda; occupancy", {HistType::kTH2F, {tanLambdaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/InvQPtExtra", "; InvQPt; occupancy", {HistType::kTH2F, {invQPtAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/EtaExtra", "; #eta; occupancy", {HistType::kTH2F, {etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/PhiExtra", "; #varphi; occupancy", {HistType::kTH2F, {phiAxis, occupancyAxis}}}); - } - } - - if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C || - doprocessMCCentFT0C || doprocessMCwBestTracksCentFT0C || - doprocessDataCentFT0CVariant1 || - doprocessDatawBestTracksCentFT0CVariant1 || - doprocessMCCentFT0CVariant1 || doprocessMCwBestTracksCentFT0CVariant1 || - doprocessDataCentFT0M || doprocessDatawBestTracksCentFT0M || - doprocessMCCentFT0M || doprocessMCwBestTracksCentFT0M || - doprocessDataCentNGlobal || doprocessDatawBestTracksCentNGlobal || - doprocessMCCentNGlobal || doprocessMCwBestTracksCentNGlobal || - doprocessDataCentMFT || doprocessDatawBestTracksCentMFT || - doprocessMCCentMFT || doprocessMCwBestTracksCentMFT) { - registry.add({"Events/Centrality/Selection", - ";status;centrality;occupancy", - {HistType::kTHnSparseF, - {{2, 0.5, 2.5}, centralityAxis, occupancyAxis}}}); - auto hstat = registry.get(HIST("Events/Centrality/Selection")); - hstat->GetAxis(0)->SetBinLabel(1, "All"); - hstat->GetAxis(0)->SetBinLabel(2, "Selected"); - - registry.add("Events/Centrality/hInteractionRate", "; centrality; occupancy; IR (kHz)", kTHnSparseF, {centralityAxis, occupancyAxis, irAxis}); - - qaregistry.add({"Events/Centrality/hCent", - "; centrality; occupancy", - {HistType::kTH2F, {centralityAxis, occupancyAxis}}, - true}); - qaregistry.add( - {"Events/Centrality/hZvtxCent", - "; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Events/Centrality/NtrkZvtx", - "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtx", - "; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/PhiEta", - "; #varphi; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - - qaregistry.add( - {"Tracks/Centrality/NchSel", - "; N_{ch}; centrality; occupancy", - {HistType::kTHnSparseF, {multAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NchBestSel", - "; N_{ch}; centrality; occupancy", - {HistType::kTHnSparseF, {multAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/Chi2Eta", - "; #chi^{2}; #it{#eta}; centrality; occupancy", - {HistType::kTHnSparseF, - {chiSqAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/Chi2", - "; #chi^{2}; centrality; occupancy", - {HistType::kTHnSparseF, - {chiSqAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/NclustersEta", - "; nClusters; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - - qaregistry.add({"Tracks/Centrality/TanLambda", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/InvQPt", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/Eta", "; #eta; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/Phi", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); - - if (doprocessDatawBestTracksCentFT0C || - doprocessDatawBestTracksCentFT0CVariant1 || - doprocessDatawBestTracksCentFT0M || - doprocessDatawBestTracksCentNGlobal || - doprocessDatawBestTracksCentMFT) { - registry.add({"Events/Centrality/NtrkZvtxBest", - "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtxBest", - "; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/PhiEtaBest", - "; #varphi; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NclustersEtaBest", - "; nClusters; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/TrackAmbDegree", - "; N_{coll}^{comp}; centrality; occupancy", - {HistType::kTHnSparseF, - {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/TanLambdaExtra", "; TanLambda; centrality; occupancy", {HistType::kTHnSparseF, {tanLambdaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/InvQPtExtra", "; InvQPt; centrality; occupancy", {HistType::kTHnSparseF, {invQPtAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/EtaExtra", "; #eta; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/PhiExtra", "; #varphi; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/DCA3d", - "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/ReTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/ReTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/OrigTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/OrigTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/RestTracksEtaZvtx", - "; #eta; #it{z}_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/RestTracksPhiEta", - "; #varphi; #eta; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - } - } - - if (doprocessMCInclusive || doprocessMCwBestTracksInclusive) { - registry.add({"Events/EvtEffGen", - ";status;occupancy", - {HistType::kTH2F, {{3, 0.5, 3.5}, occupancyAxis}}}); - auto heff = registry.get(HIST("Events/EvtEffGen")); - auto* h = heff->GetXaxis(); - h->SetBinLabel(1, "All reconstructed"); - h->SetBinLabel(2, "Selected reconstructed"); - h->SetBinLabel(3, "All generated"); - - registry.add({"Events/NtrkZvtxGen_t", - "; N_{trk}; Z_{vtx} (cm);", - {HistType::kTH2F, {multAxis, zAxis}}}); - registry.add({"Events/NtrkZvtxGen", - "; N_{trk}; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); - registry.add({"Tracks/EtaZvtxGen", - "; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); - registry.add( - {"Tracks/PhiEtaGen", - "; #varphi; #eta;", - {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); - registry.add({"Tracks/EtaZvtxGen_t", - "; #eta; Z_{vtx} (cm);", - {HistType::kTH2F, {etaAxis, zAxis}}}); - registry.add({"Tracks/PhiEtaGen_t", - "; #varphi; #eta;", - {HistType::kTH2F, {phiAxis, etaAxis}}}); - qaregistry.add({"Events/NotFoundEventZvtx", - "; #it{z}_{vtx} (cm)", - {HistType::kTH1F, {zAxis}}}); - qaregistry.add({"Events/ZvtxDiff", - "; Z_{rec} - Z_{gen} (cm)", - {HistType::kTH1F, {deltaZAxis}}}); - qaregistry.add({"Events/SplitMult", - "; N_{gen}; #it{z}_{vtx} (cm)", - {HistType::kTH2F, {multAxis, zAxis}}}); - } - - if (doprocessMCCentFT0C || doprocessMCwBestTracksCentFT0C || - doprocessMCCentFT0CVariant1 || doprocessMCwBestTracksCentFT0CVariant1 || - doprocessMCCentFT0M || doprocessMCwBestTracksCentFT0M || - doprocessMCCentNGlobal || doprocessMCwBestTracksCentNGlobal || - doprocessMCCentMFT || doprocessMCwBestTracksCentMFT) { - registry.add({"Events/Centrality/EvtEffGen", - ";status;centrality;occupancy", - {HistType::kTHnSparseF, - {{3, 0.5, 3.5}, centralityAxis, occupancyAxis}}}); - auto heff = registry.get(HIST("Events/Centrality/EvtEffGen")); - heff->GetAxis(0)->SetBinLabel(1, "All reconstructed"); - heff->GetAxis(0)->SetBinLabel(2, "Selected reconstructed"); - heff->GetAxis(0)->SetBinLabel(3, "All generated"); - - registry.add( - {"Events/Centrality/NtrkZvtxGen_t", - "; N_{trk}; Z_{vtx} (cm); centrality", - {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis}}}); - registry.add({"Events/Centrality/NtrkZvtxGen", - "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {multAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Events/Centrality/hRecCent", - "; centrality; occupancy", - {HistType::kTH2F, {centralityAxis, occupancyAxis}}}); - registry.add( - {"Events/Centrality/hRecZvtxCent", - "; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtxGen", - "; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/PhiEtaGen", - "; #varphi; #eta; centrality; occupancy", - {HistType::kTHnSparseF, - {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); - registry.add({"Tracks/Centrality/EtaZvtxGen_t", - "; #eta; Z_{vtx} (cm); centrality", - {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis}}}); - registry.add( - {"Tracks/Centrality/PhiEtaGen_t", - "; #varphi; #eta; centrality", - {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis}}}); - qaregistry.add({"Events/Centrality/NotFoundEventZvtx", - "; #it{z}_{vtx} (cm); centrality", - {HistType::kTH2F, {zAxis, centralityAxis}}}); - qaregistry.add({"Events/Centrality/ZvtxDiff", - "; Z_{rec} - Z_{gen} (cm); centrality", - {HistType::kTH2F, {deltaZAxis, centralityAxis}}}); - qaregistry.add( - {"Events/Centrality/SplitMult", - "; N_{gen}; #it{z}_{vtx} (cm); centrality", - {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis}}}); - } - - if (doprocessTrkEffIdxBestInlusive) { - qaregistry.add({"Tracks/hPtEtaEffGenFakeBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffGenBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffPrimBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffSecBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffGenDuplBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffDuplBest", - "; p_{T} (GeV/c); #eta", - {HistType::kTH2F, - {ptAxis, etaAxis}}}); - qaregistry.add({"Tracks/NmftTrkPerPartBest", - "; #it{N}_{mft tracks per particle}", - {HistType::kTH1F, {multAxis}}}); - } - - if (doprocessTrkEffIdxBestCentFT0C) { - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenFakeBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffPrimBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffSecBest", - "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenDuplBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffDuplBest", - "; p_{T} (GeV/c); #eta; centrality", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NmftTrkPerPartBest", - "; #it{N}_{mft tracks per particle}; centrality", - {HistType::kTHnSparseF, {multAxis, centralityAxis}}}); - } - - if (doprocessTrkEffIdxInlusive) { - qaregistry.add({"Tracks/hPtEtaEffGen", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffPrim", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffSec", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffGenDupl", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEtaEffDupl", - "; p_{T} (GeV/c); #eta; occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/NmftTrkPerPart", - "; #it{N}_{mft tracks per particle}; occupancy", - {HistType::kTH2F, {multAxis, occupancyAxis}}}); - } - - if (doprocessTrkEffIdxCentFT0C) { - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGen", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffPrim", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffSec", - "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffGenDupl", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEtaEffDupl", - "; p_{T} (GeV/c); #eta; centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, etaAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/NmftTrkPerPart", - "; #it{N}_{mft tracks per particle}; centrality; occupancy", - {HistType::kTHnSparseF, {multAxis, centralityAxis, occupancyAxis}}}); - } - - if (doprocessTrkEffBestInclusive) { - qaregistry.add({"Tracks/hPtPhiEtaZvtxEffBestGen", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtPhiEtaZvtxEffBestRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hPtEffBestFakeRec", - " ; p_{T} (GeV/c); occupancy", - {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - } - - if (doprocessTrkEffBestCentFT0C) { - qaregistry.add( - {"Tracks/Centrality/hPtPhiEtaZvtxEffBestGen", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtPhiEtaZvtxEffBestRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; " - "occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hPtEffBestFakeRec", - "; p_{T} (GeV/c); centrality; occupancy", - {HistType::kTHnSparseF, {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - } - - if (doprocessMcQAInclusive) { - qaregistry.add({"Events/hRecPerGenColls", - "; #it{N}_{reco collisions} / #it{N}_{gen collisions};", - {HistType::kTH2F, {{200, 0., 2.}, occupancyAxis}}}); - qaregistry.add({"Tracks/hNmftTrks", - "; #it{N}_{mft tracks};", - {HistType::kTH2F, {{200, -0.5, 200.}, occupancyAxis}}}); - qaregistry.add({"Tracks/hFracAmbiguousMftTrks", - "; #it{N}_{ambiguous tracks} / #it{N}_{tracks};", - {HistType::kTH2F, {{100, 0., 1.}, occupancyAxis}}}); - } - - if (doprocessMcQACentFT0C) { - qaregistry.add( - {"Events/Centrality/hRecPerGenColls", - "; #it{N}_{reco collisions} / #it{N}_{gen collisions}; centrality", - {HistType::kTHnSparseF, - {{200, 0., 2.}, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/hNmftTrks", - "; #it{N}_{mft tracks}; centrality", - {HistType::kTHnSparseF, - {{200, -0.5, 200.}, centralityAxis, occupancyAxis}}}); - qaregistry.add( - {"Tracks/Centrality/hFracAmbiguousMftTrks", - "; #it{N}_{ambiguous tracks} / #it{N}_{tracks}; centrality", - {HistType::kTHnSparseF, - {{100, 0., 1.}, centralityAxis, occupancyAxis}}}); - } - - if (doprocessCheckAmbiguousMftTracks) { - qaregistry.add({"Tracks/hMftTracksAmbDegree", - " ; N_{coll}^{comp}", - {HistType::kTH1F, {{41, -0.5, 40.5}}}}); - qaregistry.add({"Tracks/hMftTracksAmbDegreeWithTrivial", - " ; N_{coll}^{comp}", - {HistType::kTH1F, {{41, -0.5, 40.5}}}}); - - qaregistry.add("Tracks/hAmbTrackType", "hAmbTrackType", {HistType::kTH1F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}}}); - std::string labelAmbiguity[CambTrkType]; - labelAmbiguity[static_cast(AmbTrkType::kAll)] = "all"; - labelAmbiguity[static_cast(AmbTrkType::kOrphan)] = "orphan"; - labelAmbiguity[static_cast(AmbTrkType::kOrphanNull)] = "orphanNull"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmb)] = "nonAmb"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmbSame)] = "nonAmbSame"; - labelAmbiguity[static_cast(AmbTrkType::kAmb)] = "Amb"; - labelAmbiguity[static_cast(AmbTrkType::kAmbGt1)] = "AmbGt1"; - qaregistry.get(HIST("Tracks/hAmbTrackType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { - qaregistry.get(HIST("Tracks/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); - } - } - - if (doprocessAssocMC) { - registry.add("TrackToColl/hAmbTrackType", "hAmbTrackType", {HistType::kTH1F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}}}); - std::string labelAmbiguity[CambTrkType]; - labelAmbiguity[static_cast(AmbTrkType::kAll)] = "all"; - labelAmbiguity[static_cast(AmbTrkType::kOrphan)] = "orphan"; - labelAmbiguity[static_cast(AmbTrkType::kOrphanNull)] = "orphanNull"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmb)] = "nonAmb"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmbSame)] = "nonAmbSame"; - labelAmbiguity[static_cast(AmbTrkType::kAmb)] = "Amb"; - labelAmbiguity[static_cast(AmbTrkType::kAmbGt1)] = "AmbGt1"; - registry.get(HIST("TrackToColl/hAmbTrackType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { - registry.get(HIST("TrackToColl/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); - } - - // tracks not associated to any collision - hCollAssoc[0] = registry.add("TrackToColl/hNonAssocTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - // tracks associasted to a collision - hCollAssoc[1] = registry.add("TrackToColl/hAssocTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - // tracks associated to the correct collision considering only first reco collision (based on the MC collision index) - hCollAssoc[2] = registry.add("TrackToColl/hGoodAssocTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - // tracks associated to the correct collision considering all ambiguous reco collisions (based on the MC collision index) - hCollAssoc[3] = registry.add("TrackToColl/hGoodAssocTracksAmb", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - - registry.add("TrackToColl/histFracTracksFakeMcColl", "Fraction of tracks originating from fake collision; fraction; entries", {HistType::kTH1F, {{101, 0., 1.01}}}); - registry.add("TrackToColl/histFracGoodTracks", "Fraction of tracks originating from the correct collision; fraction; entries", {HistType::kTH1F, {{101, 0., 1.01}}}); - registry.add("TrackToColl/histAmbTrackNumColls", "Number of collisions associated to an ambiguous track; no. collisions; entries", {HistType::kTH1F, {{30, -0.5, 29.5}}}); - registry.add("TrackToColl/histTrackNumColls", "Number of collisions associated to track; no. collisions; entries", {HistType::kTH1F, {{30, -0.5, 29.5}}}); - registry.add("TrackToColl/histNonAmbTrackNumColls", "Number of collisions associated to non-ambiguous track; no. collisions; entries", {HistType::kTH1F, {{30, -0.5, 29.5}}}); - registry.add("TrackToColl/histAmbTrackZvtxRMS", "RMS of #it{Z}^{reco} of collisions associated to a track; RMS(#it{Z}^{reco}) (cm); entries", {HistType::kTH1F, {{100, 0., 10.}}}); - } - - if (doprocessReAssoc3dMC) { - - registry.add("ReAssocMC/hNReAssocRecoColls", "Number of times generated collisions are reconstructed; N; Counts", HistType::kTH1F, {{10, -0.5, 9.5}}); - - registry.add("ReAssocMC/hReAssocMCEventStatus", ";status", {HistType::kTH1F, {{static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus), -0.5, +static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus) - 0.5}}}); - std::string labelReAssocMCEventStatus[CevtReAsReAssocMCEventStatus]; - labelReAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsAll)] = "All"; - labelReAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsSelected)] = "Selected"; - labelReAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsHasMcColl)] = "Has Mc Coll"; - labelReAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsSplitVtxRemoved)] = "Split Vtx Removed"; - labelReAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsZVtxCutMC)] = "Vtx-z cut MC"; - registry.get(HIST("ReAssocMC/hReAssocMCEventStatus"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus); iBin++) { - registry.get(HIST("ReAssocMC/hReAssocMCEventStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelReAssocMCEventStatus[iBin].data()); - } - - registry.add("ReAssocMC/hReAssocMCTrackStatus", ";status", {HistType::kTH1F, {{static_cast(ReAssocMCTrackStatus::nReAssocMCTrackStatus), -0.5, +static_cast(ReAssocMCTrackStatus::nReAssocMCTrackStatus) - 0.5}}}); - std::string labelReAssocMCTrackStatus[CreAssocMCTrackStatus]; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkReAssocAll)] = "All"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkBestSel)] = "Best sel"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkSel)] = "Trk sel"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkHasColl)] = "Has coll"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkReassignedRemoved)] = "Reas rm"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkHasMcPart)] = "Has part"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkNonAmbAll)] = "Non-amb"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkNonAmbGood)] = "Non-amb good coll."; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkNonAmbBad)] = "Non-amb bad coll."; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkAmbAll)] = "Amb"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkAmbGood)] = "Amb good coll."; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkAmbBad)] = "Amb bad coll."; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkNonAmbAllE)] = "Non-amb (ex)"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkNonAmbGoodE)] = "Non-amb good coll. (ex)"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kTrkNonAmbBadE)] = "Non-amb bad coll. (ex)"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kAssoc)] = "Assoc (gt1 amb)"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kAssocGood)] = "Assoc good"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kAssocGoodIsCompTrue)] = "Assoc good Comp True"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kAssocGoodIsCompFalse)] = "Assoc good Comp False"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kAssocBad)] = "Assoc bad"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kAssocBadIsCompTrue)] = "Assoc bad Comp True"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kAssocBadIsCompFalse)] = "Assoc bad Comp False"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kReAssoc)] = "ReAssoc"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kReAssocGood)] = "ReAssoc good"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kReAssocGoodIsCompTrue)] = "ReAssoc good Comp True"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kReAssocGoodIsCompFalse)] = "ReAssoc good Comp False"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kReAssocBad)] = "ReAssoc bad"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kReAssocBadIsCompTrue)] = "ReAssoc bad Comp True"; - labelReAssocMCTrackStatus[static_cast(ReAssocMCTrackStatus::kReAssocBadIsCompFalse)] = "ReAssoc bad Comp False"; - registry.get(HIST("ReAssocMC/hReAssocMCTrackStatus"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(ReAssocMCTrackStatus::nReAssocMCTrackStatus); iBin++) { - registry.get(HIST("ReAssocMC/hReAssocMCTrackStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelReAssocMCTrackStatus[iBin].data()); - } - - // Vertex resolution - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbAll)] = registry.add("ReAssocMC/hVtxResNonAmbAll", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)] = registry.add("ReAssocMC/hVtxResNonAmbGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)] = registry.add("ReAssocMC/hVtxResNonAmbBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbAll)] = registry.add("ReAssocMC/hVtxResAmbAll", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbGood)] = registry.add("ReAssocMC/hVtxResAmbGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbBad)] = registry.add("ReAssocMC/hVtxResAmbBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbAllE)] = registry.add("ReAssocMC/hVtxResNonAmbAllE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbGoodE)] = registry.add("ReAssocMC/hVtxResNonAmbGoodE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbBadE)] = registry.add("ReAssocMC/hVtxResNonAmbBadE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssoc)] = registry.add("ReAssocMC/hVtxResAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGood)] = registry.add("ReAssocMC/hVtxResAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hVtxResAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hVtxResAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBad)] = registry.add("ReAssocMC/hVtxResAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)] = registry.add("ReAssocMC/hVtxResAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)] = registry.add("ReAssocMC/hVtxResAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssoc)] = registry.add("ReAssocMC/hVtxResReAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGood)] = registry.add("ReAssocMC/hVtxResReAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hVtxResReAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hVtxResReAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBad)] = registry.add("ReAssocMC/hVtxResReAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)] = registry.add("ReAssocMC/hVtxResReAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)] = registry.add("ReAssocMC/hVtxResReAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); - - // DCA - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbAll)] = registry.add("ReAssocMC/hDCANonAmbAll", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)] = registry.add("ReAssocMC/hDCANonAmbGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)] = registry.add("ReAssocMC/hDCANonAmbBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbAll)] = registry.add("ReAssocMC/hDCAAmbAll", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbGood)] = registry.add("ReAssocMC/hDCAAmbGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbBad)] = registry.add("ReAssocMC/hDCAAmbBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbAllE)] = registry.add("ReAssocMC/hDCANonAmbAllE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbGoodE)] = registry.add("ReAssocMC/hDCANonAmbGoodE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbBadE)] = registry.add("ReAssocMC/hDCANonAmbBadE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssoc)] = registry.add("ReAssocMC/hDCAAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGood)] = registry.add("ReAssocMC/hDCAAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hDCAAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hDCAAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBad)] = registry.add("ReAssocMC/hDCAAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)] = registry.add("ReAssocMC/hDCAAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)] = registry.add("ReAssocMC/hDCAAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssoc)] = registry.add("ReAssocMC/hDCAReAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGood)] = registry.add("ReAssocMC/hDCAReAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hDCAReAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hDCAReAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBad)] = registry.add("ReAssocMC/hDCAReAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)] = registry.add("ReAssocMC/hDCAReAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)] = registry.add("ReAssocMC/hDCAReAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis}); - } - - if (doprocessEfficiencyInclusive) { - qaregistry.add({"Tracks/hEffRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/hEffFake", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, occupancyAxis}}}); - } - - if (doprocessEfficiencyCentFT0C) { - qaregistry.add({"Tracks/Centrality/hEffRec", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - qaregistry.add({"Tracks/Centrality/hEffFake", - "; p_{T} (GeV/c); #varphi; #eta; Z_{vtx} (cm); centrality; occupancy", - {HistType::kTHnSparseF, - {ptAxis, phiAxis, etaAxis, zAxis, centralityAxis, occupancyAxis}}}); - } - - if (doprocessCorrelationwBestTracksInclusive) { - qaregistry.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); - qaregistry.add("Events/hMultMFTvsFT0C", "MultMFT_vs_FT0C", {HistType::kTH2F, {multAxis, multFT0cAxis}}); - qaregistry.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); - qaregistry.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); - qaregistry.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); - } - - if (doprocessSecondariesMCInlcusive || doprocessSecondariesMCCentFT0C) { - auto hNevt = registry.add("Events/hNGenRecColls", "Number of generated and reconstructed MC collisions", HistType::kTH1F, {{3, 0.5, 3.5}}); - hNevt->GetXaxis()->SetBinLabel(1, "Reconstructed collisions"); - hNevt->GetXaxis()->SetBinLabel(2, "Generated collisions"); - - if (doprocessSecondariesMCInlcusive) { - qaregistry.add("Events/hTrackToCollEvtType", ";status", {HistType::kTH1F, {{static_cast(TrackToCollEvtType::nTrackToCollEvtType), -0.5, +static_cast(TrackToCollEvtType::nTrackToCollEvtType) - 0.5}}}); - std::string labelTrkToCollEvt[CtrackToCollEvtType]; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kAllRecColl)] = "all rec"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kIsInelGt0wMft)] = "inel>0 mft"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kEvtSel)] = "evt sel"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kBestCollIdx)] = "bestColl Idx (numContrib)"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kIsMcColl)] = "is mc"; - qaregistry.get(HIST("Events/hTrackToCollEvtType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(TrackToCollEvtType::nTrackToCollEvtType); iBin++) { - qaregistry.get(HIST("Events/hTrackToCollEvtType"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkToCollEvt[iBin].data()); - } - // registry.add({"Events/EvtGenRec", ";status", {HistType::kTH1F, {{3, 0.5, 3.5}}}}); - // auto heff = registry.get(HIST("Events/EvtGenRec")); - // auto* h = heff->GetXaxis(); - // h->SetBinLabel(1, "All generated"); - // h->SetBinLabel(2, "All reconstructed"); - // h->SetBinLabel(3, "Selected reconstructed"); - - qaregistry.add("TrkCompColls/hAmbTrackType", ";status", {HistType::kTH1F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}}}); - std::string labelAmbiguity[CambTrkType]; - labelAmbiguity[static_cast(AmbTrkType::kAll)] = "all"; - labelAmbiguity[static_cast(AmbTrkType::kOrphan)] = "orphan"; - labelAmbiguity[static_cast(AmbTrkType::kOrphanNull)] = "orphanNull"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmb)] = "nonAmb"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmbSame)] = "nonAmbSame"; - labelAmbiguity[static_cast(AmbTrkType::kAmb)] = "Amb"; - labelAmbiguity[static_cast(AmbTrkType::kAmbGt1)] = "AmbGt1"; - qaregistry.get(HIST("TrkCompColls/hAmbTrackType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { - qaregistry.get(HIST("TrkCompColls/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); - } - - registry.add({"Tracks/THnRecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnRec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnRecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnRecAmbRest", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenSecWeak", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenSecMat", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenSecWeakAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - registry.add({"Tracks/THnGenSecMatAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis}}}); - } - if (doprocessSecondariesMCCentFT0C) { - qaregistry.add("Events/Centrality/hTrackToCollEvtType", ";status;centrality", {HistType::kTH2F, {{static_cast(TrackToCollEvtType::nTrackToCollEvtType), -0.5, +static_cast(TrackToCollEvtType::nTrackToCollEvtType) - 0.5}, centralityAxis}}); - std::string labelTrkToCollEvt[CtrackToCollEvtType]; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kAllRecColl)] = "all rec"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kIsInelGt0wMft)] = "inel>0 mft"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kEvtSel)] = "evt sel"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kBestCollIdx)] = "bestColl Idx (numContrib)"; - labelTrkToCollEvt[static_cast(TrackToCollEvtType::kIsMcColl)] = "is mc"; - qaregistry.get(HIST("Events/Centrality/hTrackToCollEvtType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(TrackToCollEvtType::nTrackToCollEvtType); iBin++) { - qaregistry.get(HIST("Events/Centrality/hTrackToCollEvtType"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkToCollEvt[iBin].data()); - } - // registry.add({"Events/Centrality/EvtGenRec", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}}); - // auto heff = registry.get(HIST("Events/Centrality/EvtGenRec")); - // auto* h = heff->GetXaxis(); - // h->SetBinLabel(1, "All generated"); - // h->SetBinLabel(2, "All reconstructed"); - // h->SetBinLabel(3, "Selected reconstructed"); - - qaregistry.add("TrkCompColls/Centrality/hAmbTrackType", ";status;centrality", {HistType::kTH2F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}, centralityAxis}}); - std::string labelAmbiguity[CambTrkType]; - labelAmbiguity[static_cast(AmbTrkType::kAll)] = "all"; - labelAmbiguity[static_cast(AmbTrkType::kOrphan)] = "orphan"; - labelAmbiguity[static_cast(AmbTrkType::kOrphanNull)] = "orphanNull"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmb)] = "nonAmb"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmbSame)] = "nonAmbSame"; - labelAmbiguity[static_cast(AmbTrkType::kAmb)] = "Amb"; - labelAmbiguity[static_cast(AmbTrkType::kAmbGt1)] = "AmbGt1"; - qaregistry.get(HIST("TrkCompColls/Centrality/hAmbTrackType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { - qaregistry.get(HIST("TrkCompColls/Centrality/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); - } - - registry.add({"Tracks/Centrality/THnRecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnRec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnRecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnRecAmbRest", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenSecWeak", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenSecMat", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenSecWeakAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/THnGenSecMatAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, centralityAxis}}}); - } - } - - if (doprocessAlignmentInclusive) { - for (size_t j = 0; j < mftQuadrant.size(); j++) { - const auto& quadrant = mftQuadrant[j]; - std::string hPath = std::string("Alignment/") + quadrant + "/"; - hAlignment[0][j][0]["DCA_x_vs_z"] = registryAlign.add((hPath + "DCA_x_vs_z").c_str(), std::format("DCA(x) vs. z - {}", quadrant).c_str(), {HistType::kTH2F, {zAxis, dcaxyAxis}}); - hAlignment[0][j][0]["DCA_y_vs_z"] = registryAlign.add((hPath + "DCA_y_vs_z").c_str(), std::format("DCA(y) vs. z - {}", quadrant).c_str(), {HistType::kTH2F, {zAxis, dcaxyAxis}}); - } - } - - if (doprocessReassocEfficiency) { - registry.add("Events/hNReEffColls", "Number of times generated collisions are reconstructed; N; Counts", HistType::kTH1F, {{10, -0.5, 9.5}}); - registry.add("Events/hNchTVX", "; status;", HistType::kTH2F, {multAxis, {2, 0, 2}}); - - registry.add("Events/Centrality/ReEffStatus", ";status;centrality", HistType::kTH2F, {{9, 0.5, 9.5}, centralityAxis}); - auto hstat = registry.get(HIST("Events/Centrality/ReEffStatus")); - hstat->GetXaxis()->SetBinLabel(1, "All tracks"); - hstat->GetXaxis()->SetBinLabel(2, "Ambiguous tracks"); - hstat->GetXaxis()->SetBinLabel(3, "Reassigned tracks"); - hstat->GetXaxis()->SetBinLabel(4, "Extra tracks"); - hstat->GetXaxis()->SetBinLabel(5, "Originally correctly reassgined"); - hstat->GetXaxis()->SetBinLabel(6, "Correctly reassigned"); - hstat->GetXaxis()->SetBinLabel(7, "Not reassigned (reassigned)"); - hstat->GetXaxis()->SetBinLabel(8, "Not reassigned"); - hstat->GetXaxis()->SetBinLabel(9, "Correctly assigned true"); - - registry.add({"AmbTracks/hVtxzMCrec", " ; Z_{vtx} (cm)", {HistType::kTH1F, {zAxis}}}); - registry.add({"AmbTracks/DCAXY", " ; DCA_{XY} (cm)", {HistType::kTH1F, {dcaxyAxis}}}); - registry.add({"AmbTracks/DCAZ", " ; DCA_{Z} (cm)", {HistType::kTH1F, {dcazAxis}}}); - registry.add({"AmbTracks/DCAXYBest", " ; DCA_{XY} (cm)", {HistType::kTH1F, {dcaxyAxis}}}); - registry.add({"AmbTracks/DCAZBest", " ; DCA_{Z} (cm)", {HistType::kTH1F, {dcazAxis}}}); - registry.add({"AmbTracks/DCAXYBestPrim", " ; DCA_{XY} (cm)", {HistType::kTH1F, {dcaxyAxis}}}); - registry.add({"AmbTracks/DCAZBestPrim", " ; DCA_{Z} (cm)", {HistType::kTH1F, {dcazAxis}}}); - registry.add({"AmbTracks/DCAXYBestTrue", " ; DCA_{XY} (cm)", {HistType::kTH1F, {dcaxyAxis}}}); - registry.add({"AmbTracks/DCAZBestTrue", " ; DCA_{Z} (cm)", {HistType::kTH1F, {dcazAxis}}}); - registry.add({"AmbTracks/DCAXYBestFalse", " ; DCA_{XY} (cm)", {HistType::kTH1F, {dcaxyAxis}}}); - registry.add({"AmbTracks/DCAZBestFalse", " ; DCA_{Z} (cm)", {HistType::kTH1F, {dcazAxis}}}); - registry.add({"AmbTracks/DCAXYBestTrueOrigAssoc", " ; DCA_{XY} (cm)", {HistType::kTH1F, {dcaxyAxis}}}); - registry.add({"AmbTracks/DCAXYBestTrueOrigReAssoc", " ; DCA_{XY} (cm)", {HistType::kTH1F, {dcaxyAxis}}}); - registry.add({"AmbTracks/DCAZBestTrueOrigAssoc", " ; DCA_{Z} (cm)", {HistType::kTH1F, {dcazAxis}}}); - registry.add({"AmbTracks/DCAZBestTrueOrigReAssoc", " ; DCA_{Z} (cm)", {HistType::kTH1F, {dcazAxis}}}); - - registry.add({"AmbTracks/Centrality/THnDCAxyBestGen", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis}}}); - registry.add({"AmbTracks/Centrality/THnDCAxyBestGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis}}}); - registry.add({"AmbTracks/Centrality/THnDCAxyBestTrue", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis}}}); - registry.add({"AmbTracks/Centrality/THnDCAxyBestFalse", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis}}}); - registry.add({"AmbTracks/Centrality/THnDCAxyBestTrueOrigAssoc", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis}}}); - registry.add({"AmbTracks/Centrality/THnDCAxyBestTrueOrigReAssoc", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis}}}); - - registry.add({"AmbTracks/BestGenDxyz", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis, dcaxyAxis, dcazAxis}}}); - registry.add({"AmbTracks/BestPrimDxyz", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis, dcaxyAxis, dcazAxis}}}); - registry.add({"AmbTracks/BestTrueDxyz", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis, dcaxyAxis, dcazAxis}}}); - registry.add({"AmbTracks/BestFalseDxyz", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis, dcaxyAxis, dcazAxis}}}); - registry.add({"AmbTracks/BestTrueOrigReAssocDxyz", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis, dcaxyAxis, dcazAxis}}}); - registry.add({"AmbTracks/BestTrueOrigAssocDxyz", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis, dcaxyAxis, dcazAxis}}}); - } - - if (doprocessEventAndSignalLossCentFT0C) { - auto hNevt = registry.add("Events/Centrality/hNRecCollsSigEvtLoss", "Number of reconstructed MC collisions", HistType::kTH1F, {{1, 0.5, 1.5}}); - hNevt->GetXaxis()->SetBinLabel(1, "Reconstructed collisions"); - - registry.add("Events/Centrality/EvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); - auto hstat = registry.get(HIST("Events/Centrality/EvtSigLossStatus")); + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryData.add({"Tracks/ReTracksPhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryData.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + } + } + LOG(info) << "doprocessData[BestTrack]Inclusive -> Size of the Data histograms:"; + registryData.print(); + } + if (doprocessDataCentFT0C || doprocessDatawBestTracksCentFT0C) { + if (gConf.useIRFromCCDB) { + registryData.add({"Events/Centrality/hInteractionRate", "; IR (kHz); centrality; occupancy", {HistType::kTHnSparseF, {irAxis, centralityAxis, occupancyAxis}}}); + } + registryData.add("Events/Centrality/DataEvtStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(DataEvtStatus::nDataEvtStatus), -0.5, +static_cast(DataEvtStatus::nDataEvtStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(DataEvtStatus::nDataEvtStatus)> labelDataEvtStatusCent{ + "Data all", + "Data sel", + "Data rejected"}; + for (int iBin = 0; iBin < static_cast(DataEvtStatus::nDataEvtStatus); iBin++) { + registryData.get(HIST("Events/Centrality/DataEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelDataEvtStatusCent[iBin].data()); + } + registryData.add({"Events/Centrality/hZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + if (gConf.fillTrackQaHist) { + registryData.add({"Events/Centrality/NtrkZvtx", "; N_{trk}; Z_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/Chi2Eta", "; #chi^{2}; centrality; occupancy", {HistType::kTHnSparseF, {chiSqAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessDatawBestTracksCentFT0C) { + if (gConf.fillTrackQaHist) { + registryData.add({"Tracks/Centrality/ReTracksEtaZvtx", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/ReTracksPhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryData.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + } + } + LOG(info) << "doprocessData[BestTrack]CentFT0C -> Size of the Data histograms:"; + registryData.print(); + } + if (doprocessDataCorrelationwBestTracksInclusive) { + registryData.add("Events/hMultMFTvsFT0A", "MultMFT_vs_FT0A", {HistType::kTH2F, {multAxis, multFT0aAxis}}); + registryData.add("Events/hMultMFTvsFT0C", "MultMFT_vs_FT0C", {HistType::kTH2F, {multAxis, multFT0cAxis}}); + registryData.add("Events/hNPVtracksVsFT0C", "NPVtracks_vs_FT0C", {HistType::kTH2F, {pvAxis, multFT0cAxis}}); + registryData.add("Events/hMultMFTvsFV0A", "MultMFT_vs_FV0A", {HistType::kTH2F, {multAxis, multFV0aAxis}}); + registryData.add("Events/hNPVtracksVsMultMFT", "NPVtracks_vs_MultMFT", {HistType::kTH2F, {pvAxis, multAxis}}); + LOG(info) << "doprocessDataCorrelationwBestTracksInclusive -> Size of the Data histograms:"; + registryData.print(); + } + if (doprocessMcInclusive || doprocessMcBestInclusive) { + registryMC.add("Events/McEvtStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEvtStatus::nMcEvtStatus), -0.5, +static_cast(McEvtStatus::nMcEvtStatus) - 0.5}, occupancyAxis}}); + std::array(McEvtStatus::nMcEvtStatus)> labelMcEvtStatus{ + "Rec all", + "Rec sel", + "Rec w/ mc coll", + "Rec w/o split vtx", + "Gen all"}; + for (int iBin = 0; iBin < static_cast(McEvtStatus::nMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/McEvtStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEvtStatus[iBin].data()); + } + if (doprocessMcBestInclusive) { + registryMC.add({"Tracks/hMcTrackStatus", "Number of tracks; Cut; occupancy", {HistType::kTH2F, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, occupancyAxis}}}); + std::array(McTrackStatus::nMcTrackStatus)> labelMcTrackStatus{ + "Best all", + "Best sel", + "Trk sel", + "Has coll", + "Reas rm", + "Fake"}; + for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { + registryMC.get(HIST("Tracks/hMcTrackStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcTrackStatus[iBin].data()); + } + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/TrackAmbDegree", "; N_{coll}^{comp}; occupancy", {HistType::kTH2F, {{51, -0.5, 50.5}, occupancyAxis}}}); + } + } + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/NclustersEta", "; nClusters; #eta; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxGenRecEvt", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NtrkZvtxRec", "; N_{trk}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); + registryMC.add({"Events/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); occupancy", {HistType::kTH2F, {deltaZAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Events/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, occupancyAxis}}}); + } + if (doprocessMcBestInclusive) { + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + } + if (doprocessMcInclusive) { + registryMC.add({"TracksMidrap/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaMidrapAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } + registryMC.add({"Tracks/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEta", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGen_t", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGen", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + registryMC.add({"Tracks/PhiEtaGenRecEvt", "; #varphi; #eta; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, occupancyAxis}}}); + LOG(info) << "doprocessMc[Best]Inclusive -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcCentFT0C || doprocessMcBestCentFT0C) { + registryMC.add("Events/Centrality/McEvtStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEvtStatus::nMcEvtStatus), -0.5, +static_cast(McEvtStatus::nMcEvtStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(McEvtStatus::nMcEvtStatus)> labelMcEvtStatusCent{ + "Rec all", + "Rec sel", + "Rec w/ mc coll", + "Rec w/o split vtx", + "Gen all"}; + for (int iBin = 0; iBin < static_cast(McEvtStatus::nMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/Centrality/McEvtStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCent[iBin].data()); + } + if (doprocessMcBestCentFT0C) { + registryMC.add({"Tracks/Centrality/hMcTrackStatus", "Number of tracks; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McTrackStatus::nMcTrackStatus), -0.5, +static_cast(McTrackStatus::nMcTrackStatus) - 0.5}, centralityAxis, occupancyAxis}}}); + std::array(McTrackStatus::nMcTrackStatus)> labelMcEvtStatusCentBest{ + "Best all", + "Best sel", + "Trk sel", + "Has coll", + "Reas rm", + "Fake"}; + for (int iBin = 0; iBin < static_cast(McTrackStatus::nMcTrackStatus); iBin++) { + registryMC.get(HIST("Tracks/Centrality/hMcTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEvtStatusCentBest[iBin].data()); + } + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/Centrality/TrackAmbDegree", "; N_{coll}^{comp}; centrality; occupancy", {HistType::kTHnSparseF, {{51, -0.5, 50.5}, centralityAxis, occupancyAxis}}}); + } + } + if (gConf.fillTrackQaHist) { + registryMC.add({"Tracks/Centrality/NclustersEta", "; nClusters; #eta; centrality; occupancy", {HistType::kTHnSparseF, {nclsAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGen_t", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxGenRecEvt", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NtrkZvtxRec", "; N_{trk}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NotFoundEventZvtx", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/ZvtxDiff", "; Z_{rec} - Z_{gen} (cm); centrality; occupancy", {HistType::kTHnSparseF, {deltaZAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/NgenZvtxNoSplit", "; N_{gen}; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {multAxis, zAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessMcBestCentFT0C) { + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } + if (doprocessMcCentFT0C) { + registryMC.add({"TracksMidrap/Centrality/EtaZvtx", "; #eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaMidrapAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } + registryMC.add({"Tracks/Centrality/EtaZvtxGen_t", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGen", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/EtaZvtxGenRecEvt", "; #eta; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEta", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGen_t", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGen", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/PhiEtaGenRecEvt", "; #varphi; #eta; centrality; occupancy", {HistType::kTHnSparseF, {phiAxis, etaAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/hRecZvtxCent", "; #it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMc[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyInclusive || doprocessMcEfficiencyBestInclusive) { + registryMC.add("Events/hMcEffStatus", "Number of events; Cut; occupancy", {HistType::kTH2F, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, occupancyAxis}}); + std::array(McEffStatus::nMcEfftStatus)> labelMcEffStatus{ + "All", + "Selected", + "Has Mc Coll", + "Split Vtx Removed"}; + for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { + registryMC.get(HIST("Events/hMcEffStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEffStatus[iBin].data()); + } + if (doprocessMcEfficiencyBestInclusive) { + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/hEffRec", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, occupancyAxis}}}); + } + registryMC.add({"Events/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, occupancyAxis}}}); + registryMC.add({"Events/hVtxZRec", "#it{z}_{vtx} (cm); occupancy", {HistType::kTH2F, {zAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEtaRes", "#eta resolution;;(#eta_{rec} - #eta_{gen})/#eta_{gen}; occupancy", {HistType::kTHnSparseF, {etaAxis, {100, -1.0, 1.0}, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiency[Best]Inclusive -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyCentFT0C || doprocessMcEfficiencyBestCentFT0C) { + registryMC.add("Events/Centrality/hMcEffStatus", "Number of events; Cut; centrality; occupancy", {HistType::kTHnSparseF, {{static_cast(McEffStatus::nMcEfftStatus), -0.5, +static_cast(McEffStatus::nMcEfftStatus) - 0.5}, centralityAxis, occupancyAxis}}); + std::array(McEffStatus::nMcEfftStatus)> labelMcEffStatusCent{ + "All", + "Selected", + "Has Mc Coll", + "Split Vtx Removed"}; + for (int iBin = 0; iBin < static_cast(McEffStatus::nMcEfftStatus); iBin++) { + registryMC.get(HIST("Events/Centrality/hMcEffStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelMcEffStatusCent[iBin].data()); + } + if (doprocessMcEfficiencyBestCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, dcaxyAxis, dcazAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } else { + registryMC.add({"Tracks/Centrality/hEffRec", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + } + registryMC.add({"Events/Centrality/hVtxZGen", ";#it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffGen", ";#eta; #it{z}_{vtx} (cm); genEvtType; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, genTrkTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Events/Centrality/hVtxZRec", "#it{z}_{vtx} (cm); centrality; occupancy", {HistType::kTHnSparseF, {zAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffRecClosure", ";#eta; #it{z}_{vtx} (cm); recTrkTypeAxis; centrality; occupancy", {HistType::kTHnSparseF, {etaAxis, zAxis, recTrkTypeAxis, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiency[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyIdxInlusive || doprocessMcEfficiencyIdxBestInlusive) { + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; recEvtIdxType; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, occupancyAxis}}}); + registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; occupancy", {HistType::kTH2F, {{10, 0.5, 10.5}, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]Inlusive -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyIdxCentFT0C || doprocessMcEfficiencyIdxBestCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; recEvtIdxType; centrality; occupancy", {HistType::kTHnSparseF, {ptAxis, etaAxis, recEvtIdxTypeAxis, centralityAxis, occupancyAxis}}}); + registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality; occupancy", {HistType::kTHnSparseF, {{10, 0.5, 10.5}, centralityAxis, occupancyAxis}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]CentFT0C -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyIdxBestLightInlusive) { + registryMC.add({"Tracks/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxLightTypeAxis", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxLightTypeAxis}}}); + registryMC.add({"Tracks/hEffIdxRec", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); recEvtIdxLightTypeAxis", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, recEvtIdxLightTypeAxis}}}); + registryMC.add({"Tracks/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}", {HistType::kTH1F, {{10, 0.5, 10.5}}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]LightInlusive -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcEfficiencyIdxBestLightCentFT0C) { + registryMC.add({"Tracks/Centrality/hEffIdxGen", "; p_{T} (GeV/c); #eta; genEvtIdxLightTypeAxis; centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, genEvtIdxLightTypeAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/hEffIdxRec", "; p_{T} (GeV/c); #eta; DCA_{XY} (cm); DCA_{Z} (cm); recEvtIdxLightTypeAxis; centrality", {HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, recEvtIdxLightTypeAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/NmftTrkPerPart", "; #it{N}_{mft tracks per particle}; centrality", {HistType::kTH1F, {{10, 0.5, 10.5}}}}); + LOG(info) << "doprocessMcEfficiencyIdx[Best]LightCentFT0C -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcSgnEvtLossCentFT0C) { + registryMC.add("Events/hMcEvtLossStatus", "Number of events; Cut", {HistType::kTH1F, {{static_cast(McEvtLossStatus::kMcEvtStatus), -0.5, +static_cast(McEvtLossStatus::kMcEvtStatus) - 0.5}}}); + std::array(McEvtLossStatus::kMcEvtStatus)> labelMcEvtLossStatus{ + "All", + "Z-vtx", + "TVX", + "InelGt0 coll", + "InelGt0 in MFT"}; + for (int iBin = 0; iBin < static_cast(McEvtLossStatus::kMcEvtStatus); iBin++) { + registryMC.get(HIST("Events/hMcEvtLossStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelMcEvtLossStatus[iBin].data()); + } + registryMC.add("Events/Centrality/hEvtSigLossStatus", ";status;centrality", {HistType::kTH2F, {{3, 0.5, 3.5}, centralityAxis}}); + auto hstat = registryMC.get(HIST("Events/Centrality/hEvtSigLossStatus")); hstat->GetXaxis()->SetBinLabel(1, "All MC gen events"); hstat->GetXaxis()->SetBinLabel(2, "MC gen events with rec event with event selection"); hstat->GetXaxis()->SetBinLabel(3, "MC gen events with no rec events"); - - registry.add("Events/Centrality/hMultGenVsCent", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}); - registry.add("Events/Centrality/hMultGenVsCentNParticlesEta05", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}); - registry.add("Events/Centrality/hMultGenVsCentNParticlesEtaMFT", "event mult MC gen", {HistType::kTH2F, {centralityAxis, multAxis}}); - registry.add("Events/Centrality/hMultGenVsCentRec", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multFT0cAxis}}); - registry.add("Events/Centrality/hMultGenVsCentRecNParticlesEta05", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}); - registry.add("Events/Centrality/hMultGenVsCentRecNParticlesEtaMFT", "event mult MC gen vs centrality", {HistType::kTH2F, {centralityAxis, multAxis}}); - - registry.add({"Tracks/Centrality/EtaCentVsMultGen_t", "; #eta; centrality; mult gen", {HistType::kTHnSparseF, {etaAxis, centralityAxis, multFT0cAxis}}}); - registry.add({"Tracks/Centrality/EtaCentVsMultGen", "; #eta; centrality; mult gen", {HistType::kTHnSparseF, {etaAxis, centralityAxis, multFT0cAxis}}}); - - registry.add({"Tracks/Centrality/EtaGen_t", "; #eta; centrality; occupancy", {HistType::kTH2F, {etaAxis, centralityAxis}}}); - registry.add({"Tracks/Centrality/EtaGen", "; #eta; centrality; occupancy", {HistType::kTH2F, {etaAxis, centralityAxis}}}); - } - - if (doprocessTimeAssocMC) { - - registry.add("TimeAssocMC/hTimeAssocMCEventStatus", ";status", {HistType::kTH1F, {{static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus), -0.5, +static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus) - 0.5}}}); - std::string labelTimeAssocMCEventStatus[CevtReAsReAssocMCEventStatus]; - labelTimeAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsAll)] = "All"; - labelTimeAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsSelected)] = "Selected"; - labelTimeAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsHasMcColl)] = "Has Mc Coll"; - labelTimeAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsSplitVtxRemoved)] = "Split Vtx Removed"; - labelTimeAssocMCEventStatus[static_cast(ReAssocMCEventStatus::kEvtReAsZVtxCutMC)] = "Vtx-z cut MC"; - registry.get(HIST("TimeAssocMC/hTimeAssocMCEventStatus"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus); iBin++) { - registry.get(HIST("TimeAssocMC/hTimeAssocMCEventStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelTimeAssocMCEventStatus[iBin].data()); - } - - registry.add("TimeAssocMC/VtxStatus", ";status", {HistType::kTH1F, {{2, 0.5, 2.5}}}); - auto hstat = registry.get(HIST("TimeAssocMC/VtxStatus")); - hstat->GetXaxis()->SetBinLabel(1, "Good vtx"); - hstat->GetXaxis()->SetBinLabel(2, "Wrong vtx"); - - registry.add({"TimeAssocMC/hVertexResV1", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVertexResV2", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - - registry.add({"TimeAssocMC/hVTXkSelGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelGoodVtxBad", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelNonAmbAll", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelNonAmbGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelNonAmbGoodVtxBad", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelNonAmbSameAll", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelNonAmbSameGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelNonAmbSameGoodVtxBad", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelAmbAll", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelAmbGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelAmbGoodVtxBad", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelAmbGt1All", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelAmbGt1GoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - registry.add({"TimeAssocMC/hVTXkSelAmbGt1GoodVtxBad", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); - - registry.add("TimeAssocMC/hTimeAssocCheckVtxType", ";status", {HistType::kTH1F, {{static_cast(ReassocCheckVtxType::nReassocVtxType), -0.5, +static_cast(ReassocCheckVtxType::nReassocVtxType) - 0.5}}}); - std::string labelReAssocVtxType[CreassocVtxType]; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxAllTrue)] = "kIsTrueVtxAll=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxAllFalse)] = "kIsTrueVtxAll=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxTrue)] = "kIsTrueVtxVsGoodVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxFalse)] = "kIsTrueVtxVsGoodVtx=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxTrue)] = "kIsTrueVtxVsBadVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxFalse)] = "kIsTrueVtxVsBadVtx=False"; - registry.get(HIST("TimeAssocMC/hTimeAssocCheckVtxType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(ReassocCheckVtxType::nReassocVtxType); iBin++) { - registry.get(HIST("TimeAssocMC/hTimeAssocCheckVtxType"))->GetXaxis()->SetBinLabel(iBin + 1, labelReAssocVtxType[iBin].data()); - } - - registry.add("TimeAssocMC/hAmbTrackType", ";status", {HistType::kTH1F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}}}); - std::string labelAmbiguity[CambTrkType]; - labelAmbiguity[static_cast(AmbTrkType::kAll)] = "all"; - labelAmbiguity[static_cast(AmbTrkType::kOrphan)] = "orphan"; - labelAmbiguity[static_cast(AmbTrkType::kOrphanNull)] = "orphanNull"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmb)] = "nonAmb"; - labelAmbiguity[static_cast(AmbTrkType::kNonAmbSame)] = "nonAmbSame"; - labelAmbiguity[static_cast(AmbTrkType::kAmb)] = "Amb"; - labelAmbiguity[static_cast(AmbTrkType::kAmbGt1)] = "AmbGt1"; - registry.get(HIST("TimeAssocMC/hAmbTrackType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { - registry.get(HIST("TimeAssocMC/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); - } - - registry.add("TimeAssocMC/hAmbTrkTypeAssocFlag", ";status", {HistType::kTH1F, {{static_cast(AmbTrkTypeAssocFlag::nSelAmbTrkTypeAssocFlag), -0.5, +static_cast(AmbTrkTypeAssocFlag::nSelAmbTrkTypeAssocFlag) - 0.5}}}); - std::string lAmbTrackType[CselAmbTrkTypeAssocFlag]; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSel)] = "all sel"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelGoodVtxTrue)] = "all good vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelGoodVtxBad)] = "all bad vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelNonAmbAll)] = "non-amb"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelNonAmbGoodVtxTrue)] = "non-amb good vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelNonAmbGoodVtxBad)] = "non-amb bad vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelNonAmbSameAll)] = "non-amb (same)"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelNonAmbSameGoodVtxTrue)] = "non-amb (same) good vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelNonAmbSameGoodVtxBad)] = "non-amb (same) bad vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelAmbAll)] = "amb"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelAmbGoodVtxTrue)] = "amb good vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelAmbGoodVtxBad)] = "amb bad vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelAmbGt1All)] = "ambGt1"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelAmbGt1GoodVtxTrue)] = "ambGt1 good vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelAmbGt1GoodVtxBad)] = "ambGt1 bad vtx"; - lAmbTrackType[static_cast(AmbTrkTypeAssocFlag::kSelOrphanNull)] = "orhpan null"; - registry.get(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(AmbTrkTypeAssocFlag::nSelAmbTrkTypeAssocFlag); iBin++) { - registry.get(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"))->GetXaxis()->SetBinLabel(iBin + 1, lAmbTrackType[iBin].data()); - } - } - - if (doprocessTimeAssocWithReassocMC) { - registry.add("TimeAssocWithReassocMC/VtxStatus", ";status", {HistType::kTH1F, {{2, 0.5, 2.5}}}); - auto hstat = registry.get(HIST("TimeAssocWithReassocMC/VtxStatus")); - hstat->GetXaxis()->SetBinLabel(1, "Good vtx"); - hstat->GetXaxis()->SetBinLabel(2, "Wrong vtx"); - - registry.add("TimeAssocWithReassocMC/hReassocCheckVtxType", ";status", {HistType::kTH1F, {{static_cast(ReassocCheckVtxType::nReassocVtxType), -0.5, +static_cast(ReassocCheckVtxType::nReassocVtxType) - 0.5}}}); - std::string labelReAssocVtxType[CreassocVtxType]; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxAllTrue)] = "kIsTrueVtxAll=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxAllFalse)] = "kIsTrueVtxAll=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodAllTrue)] = "IsRecGoodAll=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodAllFalse)] = "kIsRecGoodAll=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodMatchAllTrue)] = "kIsRecGoodMatchAll=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodMatchAllFalse)] = "kIsRecGoodMatchAll=False"; - // - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxTrue)] = "kIsTrueVtxVsGoodVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxFalse)] = "kIsTrueVtxVsGoodVtx=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodVsGoodVtxTrue)] = "kIsRecGoodVsGoodVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodVsGoodVtxFalse)] = "kIsRecGoodVsGoodVtx=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsGoodVtxTrue)] = "kIsRecGoodMatchVsGoodVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsGoodVtxFalse)] = "kIsRecGoodMatchVsGoodVtx=False"; - // - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxTrue)] = "kIsTrueVtxVsBadVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxFalse)] = "kIsTrueVtxVsBadVtx=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodVsBadVtxTrue)] = "kIsRecGoodVsBadVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodVsBadVtxFalse)] = "kIsRecGoodVsBadVtx=False"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsBadVtxTrue)] = "kIsRecGoodMatchVsBadVtx=True"; - labelReAssocVtxType[static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsBadVtxFalse)] = "kIsRecGoodMatchVsBadVtx=False"; - registry.get(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"))->SetMinimum(0.1); - for (int iBin = 0; iBin < static_cast(ReassocCheckVtxType::nReassocVtxType); iBin++) { - registry.get(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"))->GetXaxis()->SetBinLabel(iBin + 1, labelReAssocVtxType[iBin].data()); - } - - hTimeAssocWithReassocMC[0] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrig", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[1] = registry.add("TimeAssocWithReassocMC/hDCAReassocBest", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[2] = registry.add("TimeAssocWithReassocMC/hDCAReassocTruth", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - - hTimeAssocWithReassocMC[3] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigTrueVtx", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[4] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[5] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodMatch", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - - hTimeAssocWithReassocMC[6] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigTrueVtxVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[7] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[8] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodMatchVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - - hTimeAssocWithReassocMC[9] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigTrueVtxVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[10] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[11] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodMatchVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - - hTimeAssocWithReassocMC[12] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestTrueVtx", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[13] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[14] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodMatch", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - - hTimeAssocWithReassocMC[15] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestTrueVtxVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[16] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[17] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodMatchVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - - hTimeAssocWithReassocMC[18] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestTrueVtxVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[19] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - hTimeAssocWithReassocMC[20] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodMatchVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); - } - } - - /// Filters - tracks - Filter filtTrkEta = (aod::fwdtrack::eta < trackCuts.maxEta) && - (aod::fwdtrack::eta > trackCuts.minEta); - Filter filtATrackID = (aod::fwdtrack::bestCollisionId >= CintZero); - Filter filtATrackDCAxy = (nabs(aod::fwdtrack::bestDCAXY) < trackCuts.maxDCAxy); - Filter filtATrackDCAz = (nabs(aod::fwdtrack::bestDCAZ) < trackCuts.maxDCAz); - - /// Filters - mc particles - Filter primaries = (aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary && (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); + registryMC.add({"Events/hNchGen", "Evt loss; Gen Nch FT0C; evtLossType", {HistType::kTH2F, {multAxis, evtLossTypeAxis}}}); + registryMC.add({"Events/hMultGenVsCentSplit", "Split MC events: Gen Nch vs Rec Cent; rec cent; Gen Nch ", {HistType::kTH2F, {centralityAxis, multAxis}}}); + registryMC.add({"Events/hMultGenVsCent", "Gen Nch vs rec cent.; Gen Nch; Gen Nch; cent rec.", {HistType::kTHnSparseF, {multAxis, multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentMFTacc", "Gen Nch (MFT acc) vs rec cent.; Gen Nch (MFT acc); cent rec.", {HistType::kTH2F, {multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecEvt", "Gen Nch w/ Nrec > 0 vs rec cent.; Gen Nch; Gen Nch; cent rec.", {HistType::kTHnSparseF, {multAxis, multAxis, centralityAxis}}}); + registryMC.add({"Events/hMultGenVsCentRecEvtMFTacc", "Gen Nch (MFT acc) w/ Nrec > 0 vs rec cent.; Gen Nch (MFT acc); cent rec.", {HistType::kTH2F, {multAxis, centralityAxis}}}); + registryMC.add({"Tracks/hNchGenVsEta", "Gen Nch vs #eta; Gen Nch; #eta", {HistType::kTH2F, {multAxis, etaAxis}}}); + registryMC.add({"Tracks/hNchGenVsEtaRecEvt", "Gen Nch w/ Nrec > 0 vs #eta; Gen Nch; #eta", {HistType::kTH2F, {multAxis, etaAxis}}}); + LOG(info) << "doprocessMcSgnEvtLossCentFT0C -> Size of the MC histograms:"; + registryMC.print(); + } + if (doprocessMcReassocDCA) { + registryMC.add({"Events/Centrality/EvtGenRecReassoc", ";status;centrality", {HistType::kTHnSparseF, {{3, 0.5, 3.5}, centralityAxis}}}); + auto heff = registryMC.get(HIST("Events/Centrality/EvtGenRecReassoc")); + heff->GetAxis(0)->SetBinLabel(1, "All reconstructed"); + heff->GetAxis(0)->SetBinLabel(2, "Selected reconstructed"); + heff->GetAxis(0)->SetBinLabel(3, "Remove split vertices"); + registryMC.add("Events/hCentBest", "; centrality", HistType::kTH1F, {centralityAxis}); + registryMC.add("Events/hGenMult", "Sel rec evt. vs generated mult; mult", HistType::kTH1F, {multAxis}); + registryMC.add("Events/hCentRecVsGenMult", "Rec cent vs gen mid-rapidity multiplicity; centrality; mult", HistType::kTH2F, {centralityAxis, multAxis}); + registryMC.add("Events/hCentRecFromGenMult", "Centrality from generated mult", HistType::kTH1F, {centralityAxis}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestRec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestRecFake", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrim", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenPrimAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthPrimAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSec", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenTruthSecAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeak", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMat", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatNonAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmb", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + registryMC.add({"Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl", "; p_{T} (GeV/c); #eta; Z_{vtx} (cm); DCA_{XY} (cm); DCA_{Z} (cm)", {HistType::kTHnSparseF, {ptAxis, etaAxis, zAxis, dcaxyAxis, dcazAxis, centralityAxis, centralityAxis}}}); + LOG(info) << "doprocessMcReassocDCA -> Size of the MC histograms:"; + registryMC.print(); + } + } + + /// Filter (midrapidity selections) + expressions::Filter fTrackSelectionITS = ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::ITS) && + ncheckbit(aod::track::trackCutFlag, TrackSelectionITS); + expressions::Filter fTrackSelectionTPC = ifnode(ncheckbit(aod::track::v001::detectorMap, (uint8_t)o2::aod::track::TPC), + ncheckbit(aod::track::trackCutFlag, TrackSelectionTPC), true); + expressions::Filter fTrackSelectionDCA = ifnode(gConf.dcaZmidrap.node() > 0.f, nabs(aod::track::dcaZ) <= dcaZ && ncheckbit(aod::track::trackCutFlag, TrackSelectionDCAXYonly), + ncheckbit(aod::track::trackCutFlag, TrackSelectionDCA)); /// Joined tables using FullBCs = soa::Join; using CollBCs = soa::Join; using ExtBCs = soa::Join; - /// Collisions using Colls = soa::Join; - using Coll = Colls::iterator; using CollsCentFT0C = soa::Join; - using CollsCentFT0CVariant1 = - soa::Join; - using CollsCentFT0M = soa::Join; - using CollsCentNGlobal = - soa::Join; - using CollsCentMFT = soa::Join; - using CollCentFT0C = CollsCentFT0C::iterator; - using CollsGenCentFT0C = soa::Join; - using CollisionsWithMCLabels = soa::Join; - using CollGenCent = CollsGenCentFT0C::iterator; + using CollsGenCentFT0C = soa::Join; + using CollsGenCentFT0CExtra = soa::Join; using CollsCorr = soa::Join; using CollsMCExtra = soa::Join; using CollsMCExtraMult = soa::Join; - /// Tracks - using MFTTracksLabeled = soa::Join; - using MftTracksWColls = soa::Join; - using MftTracksWCollsMC = soa::Join; - using BestTracksMC = soa::Join; - using BestTracks3dWCollsMC = soa::Join; - using BestTracks2dWCollsMC = soa::Join; - - /// Filtered tables - using FiltMftTracks = soa::Filtered; - using FiltMcMftTracks = soa::Filtered; - using FiltBestTracks = soa::Filtered; - using FiltMcBestTracks = soa::Filtered; - - using FiltParticles = soa::Filtered; - - /// \brief RMS calculation - /// \param vec vector of values to compute RMS + using MftTracksLabeled = soa::Join; + using MftBestTracksLabeled = soa::Join; + // midrapidity tracks + using MidrapTracksLabeled = soa::Join; + using FiltMidrapTracksLabeled = soa::Filtered; + + /// Particles + using ParticlesIdx = soa::Join; + + template + float getMult(C const& collision) + { + float val = CInvalid; + switch (gConf.multEstimator.value) { + case MultE::CnoMult: + return val; + case MultE::CmultFT0C: + if constexpr (!isMC) { + return collision.centFT0C(); + } else { + return collision.multMCFT0C(); + } + break; + case MultE::CmultFT0M: + if constexpr (!isMC) { + return collision.centFT0M(); + } else { + return collision.multMCFT0C() + collision.multMCFT0A(); + } + break; + case MultE::CmultTPC: + if constexpr (!isMC) { + return collision.multTPC(); + } else { + return collision.multMCNParticlesEta08(); + } + break; + case MultE::CmultMFT: + if constexpr (!isMC) { + return collision.centMFT(); + } else { + return CInvalid; + // return collision.multMCMFT(); // FIXME: uncomment when MC MFT mult is in available mcCentralityModule.cxx or in aod::MultMCExtras + } + break; + default: + LOGF(fatal, "No valid multiplicity estimator: %s", gConf.multEstimator.value); + return val; + } + } + + float getMultMC(CollsMCExtraMult::iterator const& collision) + { + return getMult(collision); + } + template - T computeRMS(std::vector& vec) + float getDCAz(const T& track) + { + if constexpr (requires { track.bestDCAZ(); }) { + return track.bestDCAZ(); + } else { + return 999.; + } + } + + std::unordered_map mapMcCollIdPerRecColl; + template + void buildLookupTable(C const& collisions) + { + const auto& nRecoColls = collisions.size(); + mapMcCollIdPerRecColl.clear(); + mapMcCollIdPerRecColl.reserve(nRecoColls); + int maxNcontributors = -1; + for (auto const& collision : collisions) { + // const int nContrib = collision.multPVTotalContributors(); + const int nContrib = collision.numContrib(); + if (maxNcontributors < nContrib) { + maxNcontributors = nContrib; + mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); + } + } + } + + std::unordered_set mcIds; + std::unordered_set setRecCollSel; + std::unordered_map refCentByMcId; + std::unordered_map genCentByMcId; + std::unordered_map mapRecToMc; + std::unordered_map mapMcToRec; + + PresliceUnsorted perCollMcRec = aod::mccollisionlabel::mcCollisionId; + + template + void createMCIds(MC const& mcCollisions, CollsGenCentFT0CExtra const& collisions, P const& particles) { - T sum = std::accumulate(vec.begin(), vec.end(), 0.0); - T mean = sum / vec.size(); + const auto& nMcColls = mcCollisions.size(); + LOG(info) << "MC collisions: " << nMcColls; + const auto& nRecoColls = collisions.size(); + LOG(info) << "Reconstructed collisions: " << nRecoColls; + + mcIds.clear(); + mcIds.reserve(nMcColls); + setRecCollSel.clear(); + setRecCollSel.reserve(nRecoColls); + refCentByMcId.clear(); + refCentByMcId.reserve(nMcColls); + genCentByMcId.clear(); + genCentByMcId.reserve(nMcColls); + mapMcCollIdPerRecColl.clear(); + mapMcCollIdPerRecColl.reserve(nRecoColls); + mapRecToMc.clear(); + mapRecToMc.reserve(nRecoColls); + mapMcToRec.clear(); + mapMcToRec.reserve(nRecoColls); + + for (const auto& mcCollision : mcCollisions) { + const auto mcId = mcCollision.globalIndex(); + + if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + + bool atLeastOne = false; + int maxNcontributors = -1; + float bestCollCent = CInvalid; + + auto groupedColls = collisions.sliceBy(perCollMcRec, mcId); + for (auto const& collision : groupedColls) { + const float lCent = getRecoCent(collision); + if (!isGoodEvent(collision)) { + continue; + } + atLeastOne = true; + + // const int nContrib = collision.multPVTotalContributors(); + const int nContrib = collision.numContrib(); + if (maxNcontributors < nContrib) { + maxNcontributors = nContrib; + bestCollCent = lCent; + mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); + mapRecToMc.emplace(collision.globalIndex(), collision.mcCollisionId()); + mapMcToRec.emplace(collision.mcCollisionId(), collision.globalIndex()); + setRecCollSel.insert(collision.globalIndex()); + } + } + + if (!atLeastOne || bestCollCent == CInvalid) { + continue; + } + + auto partsPerMcColl = particles.sliceBy(perMCCol, mcId); + const int genMult = countPartMidRap(partsPerMcColl); + const float genCentClass = getCentGenMult(genMult); + if (genCentClass == CInvalid) { + continue; + } + if (genCentClass < eventCuts.minCentrality || genCentClass > eventCuts.maxCentrality) { + continue; + } + registryMC.fill(HIST("Events/hCentBest"), bestCollCent); - std::vector diff(vec.size()); - std::transform(vec.begin(), vec.end(), diff.begin(), [mean](T x) { return x - mean; }); - T sqSum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); - T stdev = std::sqrt(sqSum / vec.size()); + mcIds.insert(mcId); + refCentByMcId.emplace(mcId, genCentClass); + genCentByMcId[mcId] = genMult; - return stdev; + registryMC.fill(HIST("Events/hGenMult"), genMult); + registryMC.fill(HIST("Events/hCentRecFromGenMult"), genCentClass); + registryMC.fill(HIST("Events/hCentRecVsGenMult"), bestCollCent, genMult); + } } void initCCDB(ExtBCs::iterator const& bc) @@ -1700,54 +1071,43 @@ struct DndetaMFTPbPb { o2::base::Propagator::initFieldFromGRP(grpmag); mRunNumber = bc.runNumber(); - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); - bZ = field->getBz(CcenterMFT); + auto field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + bZ = field->getBz(centerMFT.data()); LOG(info) << "The field at the center of the MFT is bZ = " << bZ; - - if (cfgApplyZShiftFromCCDB) { - auto* zShift = ccdb->getForTimeStamp>(cfgZShiftPath, bc.timestamp()); - if (zShift != nullptr && !zShift->empty()) { - LOGF(info, "reading z shift %f from %s", (*zShift)[0], cfgZShiftPath.value); - mZShift = (*zShift)[0]; - } else { - LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", cfgZShiftPath.value); - mZShift = 0; - } - } else { - LOGF(info, "z shift is manually set to %f cm", cfgManualZShift.value); - mZShift = cfgManualZShift; - } } template bool isBestTrackSelected(const B& besttrack) { if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelAll)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelAll)); } if (besttrack.bestCollisionId() < CintZero) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelCollID)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelCollID)); } if (besttrack.ambDegree() == CintZero) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelOrphan)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelOrphan)); } if (std::abs(besttrack.bestDCAXY()) >= trackCuts.maxDCAxy) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelDCAxyCut)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelDCAxyCut)); } - if (std::abs(besttrack.bestDCAZ()) >= trackCuts.maxDCAz) { - return false; + if (trackCuts.useDCAzCut) { + const float bestDcaZ = getDCAz(besttrack); + if (std::abs(bestDcaZ) >= trackCuts.maxDCAz) { + return false; + } } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelDCAzCut)); + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelDCAzCut)); } return true; } @@ -1756,253 +1116,192 @@ struct DndetaMFTPbPb { bool isTrackSelected(const T& track) { if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelAll)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelAll)); } if (track.nClusters() < trackCuts.minNclusterMft) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelNCls)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelNCls)); } if (trackCuts.useChi2Cut) { float nclMft = std::max(2.0f * track.nClusters() - 5.0f, 1.0f); float mftChi2NCl = track.chi2() / nclMft; - if (mftChi2NCl > trackCuts.maxChi2NCl) + if (mftChi2NCl > trackCuts.maxChi2NCl) { return false; + } } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelChi2Ncl)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelChi2Ncl)); } if (track.eta() < trackCuts.minEta || track.eta() > trackCuts.maxEta) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelEta)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelEta)); + } + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { + return false; + } + if constexpr (fillHis) { + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhi)); } if (trackCuts.usephiCut) { - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { - return false; - } if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || ((phi > ((PIHalf - 0.1) * PI) - trackCuts.phiCut) && - (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut))) + (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut))) { return false; + } } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhiCut)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhiCut)); } if (trackCuts.usePtCut && track.pt() < trackCuts.minPt) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPt)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPt)); } if (trackCuts.requireCA && !track.isCA()) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelCA)); + registryQC.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelCA)); } return true; } - template + template int countTracks(T const& tracks, float z, float c, float occ) { auto nTrk = 0; for (auto const& track : tracks) { - if (fillHis) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/Chi2"), track.chi2(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), c, occ); - } else { - qaregistry.fill(HIST("Tracks/Chi2Eta"), track.chi2(), track.eta(), occ); - qaregistry.fill(HIST("Tracks/Chi2"), track.chi2(), occ); - qaregistry.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occ); - } - } if (!isTrackSelected(track)) { continue; } - if (fillHis) { - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); - registry.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/TanLambda"), track.tgl(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/InvQPt"), track.signed1Pt(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/Eta"), track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/Phi"), phi, c, occ); - } else { - registry.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); - registry.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); - qaregistry.fill(HIST("Tracks/TanLambda"), track.tgl(), c, occ); - qaregistry.fill(HIST("Tracks/InvQPt"), track.signed1Pt(), c, occ); - qaregistry.fill(HIST("Tracks/Eta"), track.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Phi"), phi, c, occ); - } - } - ++nTrk; - } - if (fillHis) { + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NchSel"), nTrk, c, occ); + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), z, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), c, occ); } else { - qaregistry.fill(HIST("Tracks/NchSel"), nTrk, occ); + registryData.fill(HIST("Tracks/EtaZvtx"), track.eta(), z, occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occ); } - } - return nTrk; - } - - template - void countBestTracksExtra(B const& besttracksExtra, float c, float occ) - { - for (auto const& etrack : besttracksExtra) { - if (fillHis) { - float phi = etrack.phis(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } + if (gConf.fillTrackQaHist) { if constexpr (has_reco_cent) { - if (gConf.cfgUseTrackParExtra) { - qaregistry.fill(HIST("Tracks/Centrality/TanLambdaExtra"), etrack.tgl(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/InvQPtExtra"), etrack.signed1Pt(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/EtaExtra"), etrack.etas(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/PhiExtra"), phi, c, occ); - } + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), track.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), c, occ); } else { - if (gConf.cfgUseTrackParExtra) { - qaregistry.fill(HIST("Tracks/TanLambdaExtra"), etrack.tgl(), occ); - qaregistry.fill(HIST("Tracks/InvQPtExtra"), etrack.signed1Pt(), occ); - qaregistry.fill(HIST("Tracks/EtaExtra"), etrack.etas(), occ); - qaregistry.fill(HIST("Tracks/PhiExtra"), phi, occ); - } + registryData.fill(HIST("Tracks/Chi2Eta"), track.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), track.nClusters(), occ); } } + ++nTrk; } + return nTrk; } - template - int countBestTracks(T const& tracks, B const& besttracks, float z, - float c, float occ) + template + int countBestTracks(T const& /*tracks*/, B const& besttracks, float z, float c, float occ) { auto nATrk = 0; - ambiguousTrkIds.reserve(besttracks.size()); - reassignedTrkIds.reserve(besttracks.size()); for (auto const& atrack : besttracks) { if (!isBestTrackSelected(atrack)) { continue; } + const float bestDcaZ = getDCAz(atrack); auto itrack = atrack.template mfttrack_as(); if (!isTrackSelected(itrack)) { continue; } - ambiguousTrkIds.emplace_back(atrack.mfttrackId()); - ++nATrk; - if (fillHis) { - float phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), z, atrack.bestDCAXY(), bestDcaZ, c, occ); + registryData.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), c, occ); + } else { + registryData.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), z, atrack.bestDCAXY(), bestDcaZ, occ); + registryData.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occ); + } + if (gConf.fillTrackQaHist) { if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtxBest"), itrack.eta(), z, c, occ); - registry.fill(HIST("Tracks/Centrality/PhiEtaBest"), phi, itrack.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/NclustersEtaBest"), itrack.nClusters(), itrack.eta(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); + registryData.fill(HIST("Tracks/Centrality/Chi2Eta"), itrack.chi2(), c, occ); + registryData.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), c, occ); + registryData.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), c, occ); } else { - registry.fill(HIST("Tracks/EtaZvtxBest"), itrack.eta(), z, occ); - registry.fill(HIST("Tracks/PhiEtaBest"), phi, itrack.eta(), occ); - qaregistry.fill(HIST("Tracks/DCA3d"), itrack.pt(), itrack.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), occ); - qaregistry.fill(HIST("Tracks/NclustersEtaBest"), itrack.nClusters(), itrack.eta(), occ); - qaregistry.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occ); + registryData.fill(HIST("Tracks/Chi2Eta"), itrack.chi2(), occ); + registryData.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), occ); + registryData.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occ); } - } - - if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { - reassignedTrkIds.emplace_back(atrack.mfttrackId()); - if (fillHis) { - registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelNumReassoc)); - float phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } + if (itrack.has_collision() && itrack.collisionId() != atrack.bestCollisionId()) { + registryQC.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkBestSel::trkBestSelNumReassoc)); if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), c, occ); - qaregistry.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), phi, itrack.eta(), c, occ); + registryData.fill(HIST("Tracks/Centrality/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), c, occ); + registryData.fill(HIST("Tracks/Centrality/ReTracksPhiEta"), phi, itrack.eta(), c, occ); } else { - qaregistry.fill(HIST("Tracks/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), occ); - qaregistry.fill(HIST("Tracks/ReTracksPhiEta"), phi, itrack.eta(), occ); + registryData.fill(HIST("Tracks/ReTracksEtaZvtx"), itrack.eta(), itrack.template collision_as().posZ(), occ); + registryData.fill(HIST("Tracks/ReTracksPhiEta"), phi, itrack.eta(), occ); } } } + ++nATrk; } + return nATrk; + } - for (auto const& track : tracks) { - if (!isTrackSelected(track)) { + template + bool isInelGt0wMft(P const& particles) + { + int nChrgMc = 0; + for (auto const& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { continue; } - float phi = track.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - if (fillHis) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/OrigTracksEtaZvtx"), track.eta(), z, c, occ); - qaregistry.fill(HIST("Tracks/Centrality/OrigTracksPhiEta"), phi, track.eta(), c, occ); - } else { - qaregistry.fill(HIST("Tracks/OrigTracksEtaZvtx"), track.eta(), z, occ); - qaregistry.fill(HIST("Tracks/OrigTracksPhiEta"), phi, track.eta(), occ); - } - } - if (std::find(ambiguousTrkIds.begin(), ambiguousTrkIds.end(), track.globalIndex()) != ambiguousTrkIds.end()) { + nChrgMc++; + } + return nChrgMc != CintZero; + } + + template + int countPartMidRap(P const& particles) + { + int nCh = 0; + for (auto const& part : particles) { + if (!isChrgParticle(part.pdgCode())) { continue; } - if (std::find(reassignedTrkIds.begin(), reassignedTrkIds.end(), track.globalIndex()) != reassignedTrkIds.end()) { + if (!part.isPhysicalPrimary()) { continue; } - // ++nATrk; // use for testing purposes only! - if (fillHis) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/RestTracksEtaZvtx"), track.eta(), z, c, occ); - qaregistry.fill(HIST("Tracks/Centrality/RestTracksPhiEta"), phi, track.eta(), c, occ); - // registry.fill(HIST("Tracks/Centrality/EtaZvtxBest"), track.eta(), z, c, occ); - // registry.fill(HIST("Tracks/Centrality/PhiEtaBest"), phi, track.eta(), c, occ); - // qaregistry.fill(HIST("Tracks/Centrality/NclustersEtaBest"), track.nClusters(), track.eta(), c, occ); - } else { - qaregistry.fill(HIST("Tracks/RestTracksEtaZvtx"), track.eta(), z, occ); - qaregistry.fill(HIST("Tracks/RestTracksPhiEta"), phi, track.eta(), occ); - // registry.fill(HIST("Tracks/EtaZvtxBest"), track.eta(), z, occ); - // registry.fill(HIST("Tracks/PhiEtaBest"), phi, track.eta(), occ); - // qaregistry.fill(HIST("Tracks/NclustersEtaBest"), track.nClusters(), track.eta(), occ); - } + if (part.eta() < trackCuts.minEtaGenMult || part.eta() > trackCuts.maxEtaGenMult) { + continue; } + nCh++; } - if (fillHis) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NchBestSel"), nATrk, c, occ); - } else { - qaregistry.fill(HIST("Tracks/NchBestSel"), nATrk, occ); + return nCh; + } + + float getCentGenMult(int nCharged) + { + const auto& genMultBinLimits = binOpt.genMultBins.value; + const auto& recCentBinVal = binOpt.recCentBinCenters.value; + if (genMultBinLimits.size() != recCentBinVal.size()) { + LOGF(fatal, "genMultBinLimits size (%zu) is different from recCentBinVal size (%zu)", genMultBinLimits.size(), recCentBinVal.size()); + } + for (size_t i = 0; i < genMultBinLimits.size(); ++i) { + if (nCharged >= genMultBinLimits[i]) { + return recCentBinVal[i]; } } - ambiguousTrkIds.clear(); - ambiguousTrkIds.shrink_to_fit(); - reassignedTrkIds.clear(); - reassignedTrkIds.shrink_to_fit(); - return nATrk; + return CInvalid; } template @@ -2013,7 +1312,7 @@ struct DndetaMFTPbPb { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { @@ -2025,66 +1324,30 @@ struct DndetaMFTPbPb { } template - bool isInelGt0wMft(P const& particles) + bool isParticleSelected(P const& particle) { - int nChrgMc = 0; - int nChrgFT0A = 0; - int nChrgFT0C = 0; - for (auto const& particle : particles) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (!particle.isPhysicalPrimary()) { - continue; - } - // trigger TVX - if (particle.eta() > CminAccFT0A && particle.eta() < CmaxAccFT0A) { - nChrgFT0A++; - } - if (particle.eta() > CminAccFT0C && particle.eta() < CmaxAccFT0C) { - nChrgFT0C++; - } - // acceptance MFT - if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { - continue; - } - nChrgMc++; - } - - if (nChrgFT0A == CintZero || nChrgFT0C == CintZero) { - registry.fill(HIST("Events/hNchTVX"), nChrgMc, 0.5); + if (gConf.usePrimaries && !particle.isPhysicalPrimary()) { return false; } - registry.fill(HIST("Events/hNchTVX"), nChrgMc, 1.5); - - if (nChrgMc == CintZero) { + if (!gConf.usePrimaries && (gConf.useSecondaries && particle.isPhysicalPrimary())) { return false; } - - return true; - } - - template - bool isParticleSelected(P const& particle) - { - if (gConf.cfgUsePrimaries && !particle.isPhysicalPrimary()) { + if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { return false; } - if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { + float phi = particle.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { return false; } if (trackCuts.usephiCut) { - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { - return false; - } if ((phi < trackCuts.phiCut) || ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || (phi > TwoPI - trackCuts.phiCut) || ((phi > ((PIHalf - 0.1) * PI) - trackCuts.phiCut) && - (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut))) + (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut))) { return false; + } } return true; } @@ -2093,9 +1356,9 @@ struct DndetaMFTPbPb { float getOccupancy(C const& collision, uint occEstimator) { switch (occEstimator) { - case OccupancyEst::TrkITS: + case static_cast(OccupancyEst::TrkITS): return collision.trackOccupancyInTimeRange(); - case OccupancyEst::Ft0C: + case static_cast(OccupancyEst::Ft0C): return collision.ft0cOccupancyInTimeRange(); default: LOG(fatal) << "No valid occupancy estimator "; @@ -2110,14 +1373,14 @@ struct DndetaMFTPbPb { return; } mRunNumber = bc.runNumber(); - if (gHadronicRate.find(mRunNumber) == gHadronicRate.end()) { + if (!gHadronicRate.contains(mRunNumber)) { auto runDuration = ccdb->getRunDuration(mRunNumber); mSOR = runDuration.first; mMinSeconds = std::floor(mSOR * 1.e-3); /// round tsSOR to the highest integer lower than tsSOR float maxSec = std::ceil(runDuration.second * 1.e-3); /// round tsEOR to the lowest integer higher than tsEOR const AxisSpec axisSeconds{static_cast((maxSec - mMinSeconds) / 20.f), 0, maxSec - mMinSeconds, "Seconds since SOR"}; int hadronicRateBins = static_cast(eventCuts.maxIR - eventCuts.minIR); - gHadronicRate[mRunNumber] = registry.add(Form("HadronicRate/%i", mRunNumber), ";Time since SOR (s);Hadronic rate (kHz)", kTH2D, {axisSeconds, {hadronicRateBins, eventCuts.minIR, eventCuts.maxIR}}).get(); + gHadronicRate[mRunNumber] = registryMC.add(Form("HadronicRate/%i", mRunNumber), ";Time since SOR (s);Hadronic rate (kHz)", kTH2D, {axisSeconds, {hadronicRateBins, eventCuts.minIR, eventCuts.maxIR}}).get(); } gCurrentHadronicRate = gHadronicRate[mRunNumber]; } @@ -2126,69 +1389,75 @@ struct DndetaMFTPbPb { bool isGoodEvent(C const& collision) { if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAll)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAll)); } if (!collision.sel8()) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtSel)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtSel)); } if (eventCuts.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtIsGoodZvtx)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtIsGoodZvtx)); } if (eventCuts.requireRejectSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoSameBunchPileup)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoSameBunchPileup)); } if (collision.posZ() <= eventCuts.minZvtx || collision.posZ() >= eventCuts.maxZvtx) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtZvtxCut)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtZvtxCut)); } if (eventCuts.requireNoCollInTimeRangeStd && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStd)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStd)); } if (eventCuts.requireNoCollInTimeRangeNarrow && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeNarrow)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeNarrow)); } if (eventCuts.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStrict)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStrict)); } if (eventCuts.requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStrict)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStrict)); } if (eventCuts.requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStandard)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStandard)); } if (eventCuts.requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoHighMultCollInPrevRof)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoHighMultCollInPrevRof)); + } + if (eventCuts.requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + if constexpr (fillHis) { + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtGoodITSLayersAll)); } if (eventCuts.minOccupancy >= 0 && getOccupancy(collision, eventCuts.occupancyEstimator) < @@ -2196,7 +1465,7 @@ struct DndetaMFTPbPb { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtBelowMinOccup)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtBelowMinOccup)); } if (eventCuts.maxOccupancy >= 0 && getOccupancy(collision, eventCuts.occupancyEstimator) > @@ -2204,19 +1473,19 @@ struct DndetaMFTPbPb { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAboveMaxOccup)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAboveMaxOccup)); } if (rctCuts.requireRCTFlagChecker && !rctChecker(collision)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagChecker)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagChecker)); } if (rctCuts.requireRCTFlagCheckerExtra && !rctCheckerExtra(collision)) { return false; } if constexpr (fillHis) { - registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagCheckerExtra)); + registryQC.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagCheckerExtra)); } return true; } @@ -2234,46 +1503,40 @@ struct DndetaMFTPbPb { } template - void fillHistMC(P const& particles, float c, float occ, float zvtx, - bool const gtZeroColl) + void fillHistMC(P const& particles, float zvtx, float c, float occ, bool const gtZeroColl, bool const gtOneRec) { for (auto const& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - float phi = particle.phi(); o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } if constexpr (isCent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtxGen_t"), particle.eta(), - zvtx, c); - registry.fill(HIST("Tracks/Centrality/PhiEtaGen_t"), phi, - particle.eta(), c); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen_t"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen_t"), phi, particle.eta(), c, occ); } else { - registry.fill(HIST("Tracks/EtaZvtxGen_t"), particle.eta(), zvtx); - registry.fill(HIST("Tracks/PhiEtaGen_t"), phi, particle.eta()); + registryMC.fill(HIST("Tracks/EtaZvtxGen_t"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGen_t"), phi, particle.eta(), occ); } - if (gtZeroColl) { - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } if constexpr (isCent) { - registry.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), - zvtx, c, occ); - registry.fill(HIST("Tracks/Centrality/PhiEtaGen"), phi, - particle.eta(), c, occ); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGen"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGen"), phi, particle.eta(), c, occ); } else { - registry.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); - registry.fill(HIST("Tracks/PhiEtaGen"), phi, particle.eta(), occ); + registryMC.fill(HIST("Tracks/EtaZvtxGen"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGen"), phi, particle.eta(), occ); + } + } + if (gtOneRec) { + if constexpr (isCent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtxGenRecEvt"), particle.eta(), zvtx, c, occ); + registryMC.fill(HIST("Tracks/Centrality/PhiEtaGenRecEvt"), phi, particle.eta(), c, occ); + } else { + registryMC.fill(HIST("Tracks/EtaZvtxGenRecEvt"), particle.eta(), zvtx, occ); + registryMC.fill(HIST("Tracks/PhiEtaGenRecEvt"), phi, particle.eta(), occ); } } } @@ -2284,9 +1547,9 @@ struct DndetaMFTPbPb { { std::vector::iterator> cols; for (auto const& bc : bcs) { - if ((bc.selection_bit(aod::evsel::kIsBBT0A) && - bc.selection_bit(aod::evsel::kIsBBT0C)) != 0) { - registry.fill(HIST("hBcSel"), 0); + if (static_cast(bc.selection_bit(aod::evsel::kIsBBT0A) && + bc.selection_bit(aod::evsel::kIsBBT0C)) != 0) { + registryQC.fill(HIST("hBcSel"), 0); cols.clear(); for (auto const& collision : collisions) { if (collision.has_foundBC()) { @@ -2299,2474 +1562,1575 @@ struct DndetaMFTPbPb { } LOGP(debug, "BC {} has {} collisions", bc.globalBC(), cols.size()); if (!cols.empty()) { - registry.fill(HIST("hBcSel"), 1); + registryQC.fill(HIST("hBcSel"), 1); if (cols.size() > 1) { - registry.fill(HIST("hBcSel"), 2); + registryQC.fill(HIST("hBcSel"), 2); } } } } } - PROCESS_SWITCH(DndetaMFTPbPb, processTagging, "Collect event sample stats", - true); + PROCESS_SWITCH(DndetaMFTPbPb, processTagging, "Collect event sample stats", true); + + std::vector nTrkPerCol; + std::vector centPerCol; + std::vector occPerCol; /// @brief process function for counting tracks template void processData(typename C::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& /*bcs*/) + aod::MFTTracks const& tracks, + CollBCs const& /*bcs*/) { - auto occ = getOccupancy(collision, eventCuts.occupancyEstimator); + nTrkPerCol.clear(); + centPerCol.clear(); + occPerCol.clear(); float c = getRecoCent(collision); + float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), c, occ); } else { - registry.fill(HIST("Events/Selection"), 1., occ); - } - - // const bool isFT0Bad = bc.rct_bit(kFT0Bad); - // registry.fill(HIST("Events/hRCTSel"), 1.0); - // if (!isFT0Bad) { - // registry.fill(HIST("Events/hRCTSel"), 2.0); - // } - - if (!isGoodEvent(collision)) { - return; + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), occ); } - - if (gConf.cfgDoIR) { + if (gConf.useIRFromCCDB) { initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; + float ir = !gConf.intRateSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.intRateSource, gConf.intRateCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/hInteractionRate"), c, occ, ir); + registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { - registry.fill(HIST("Events/hInteractionRate"), occ, ir); + registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate + if (gConf.useIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate return; } gCurrentHadronicRate->Fill(seconds, ir); } - - auto z = collision.posZ(); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 2., c, occ); - } else { - registry.fill(HIST("Events/Selection"), 2., occ); - } - - auto nTrk = countTracks(tracks, z, c, occ); - - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtx"), nTrk, z, c, occ); + if (!gConf.useEvtSel || isGoodEvent(collision)) { + auto z = collision.posZ(); + centPerCol.emplace_back(c); + occPerCol.emplace_back(occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); + } + auto nTrk = countTracks(tracks, z, c, occ); + nTrkPerCol.emplace_back(nTrk); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nTrkPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrkPerCol[i], z, centPerCol[i], occPerCol[i]); + } else { + registryData.fill(HIST("Events/NtrkZvtx"), nTrkPerCol[i], z, occPerCol[i]); + } + } + } } else { - registry.fill(HIST("Events/NtrkZvtx"), nTrk, z, occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), occ); + } } } - /// @brief process function for counting tracks (based on BestCollisionsFwd3d - /// table) + /// @brief process function for counting tracks (based on BestCollisionsFwd3d table) template - void processDatawBestTracks( - typename C::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& /*bcs*/) - { - auto occ = getOccupancy(collision, eventCuts.occupancyEstimator); + void processDatawBestTracks(typename C::iterator const& collision, + aod::MFTTracks const& tracks, + soa::SmallGroups const& besttracks, + CollBCs const& /*bcs*/) + { + nTrkPerCol.clear(); + centPerCol.clear(); + occPerCol.clear(); float c = getRecoCent(collision); + float occ = getOccupancy(collision, eventCuts.occupancyEstimator); auto bc = collision.template foundBC_as(); if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 1., c, occ); + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), c, occ); } else { - registry.fill(HIST("Events/Selection"), 1., occ); - } - - if (!isGoodEvent(collision)) { - return; + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataAll), occ); } - - if (gConf.cfgDoIR) { + if (gConf.useIRFromCCDB) { initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; + float ir = !gConf.intRateSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.intRateSource, gConf.intRateCrashOnNull) * 1.e-3 : -1; if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/hInteractionRate"), c, occ, ir); + registryData.fill(HIST("Events/Centrality/hInteractionRate"), ir, c, occ); } else { - registry.fill(HIST("Events/hInteractionRate"), occ, ir); + registryData.fill(HIST("Events/hInteractionRate"), ir, occ); } float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate + if (gConf.useIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate return; } gCurrentHadronicRate->Fill(seconds, ir); } - - auto z = collision.posZ(); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/Selection"), 2., c, occ); - qaregistry.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); - qaregistry.fill(HIST("Events/Centrality/hCent"), c, occ); - } else { - registry.fill(HIST("Events/Selection"), 2., occ); - } - - auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); - countBestTracksExtra(besttracksExtra, c, occ); - - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxBest"), nBestTrks, z, c, - occ); + if (!gConf.useEvtSel || isGoodEvent(collision)) { + auto z = collision.posZ(); + centPerCol.emplace_back(c); + occPerCol.emplace_back(occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), c, occ); + registryData.fill(HIST("Events/Centrality/hZvtxCent"), z, c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataSel), occ); + } + auto nBestTrks = countBestTracks(tracks, besttracks, z, c, occ); + nTrkPerCol.emplace_back(nBestTrks); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nTrkPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/NtrkZvtx"), nTrkPerCol[i], z, centPerCol[i], occPerCol[i]); + } else { + registryData.fill(HIST("Events/NtrkZvtx"), nTrkPerCol[i], z, occPerCol[i]); + } + } + } } else { - registry.fill(HIST("Events/NtrkZvtxBest"), nBestTrks, z, occ); + if constexpr (has_reco_cent) { + registryData.fill(HIST("Events/Centrality/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), c, occ); + } else { + registryData.fill(HIST("Events/DataEvtStatus"), static_cast(DataEvtStatus::kDataRej), occ); + } } } - void processDataInclusive(Colls::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) + void processDataInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, CollBCs const& bcs) { processData(collision, tracks, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDataInclusive, - "Count tracks (inclusive)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processDataInclusive, "Count tracks (inclusive)", false); - void processDataCentFT0C(CollsCentFT0C::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) + void processDataCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, CollBCs const& bcs) { processData(collision, tracks, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0C, - "Count tracks in FT0C centrality bins", false); - - void - processDataCentFT0CVariant1(CollsCentFT0CVariant1::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0CVariant1, - "Count tracks in FT0CVariant1 centrality bins", false); - - void processDataCentFT0M(CollsCentFT0M::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0M, - "Count tracks in FT0M centrality bins", false); - - void processDataCentNGlobal(CollsCentNGlobal::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentNGlobal, - "Count tracks in NGlobal centrality bins", false); - - void processDataCentMFT(CollsCentMFT::iterator const& collision, - FiltMftTracks const& tracks, CollBCs const& bcs) - { - processData(collision, tracks, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDataCentMFT, - "Count tracks in MFT centrality bins", false); + PROCESS_SWITCH(DndetaMFTPbPb, processDataCentFT0C, "Count tracks in FT0C centrality bins", false); - void processDatawBestTracksInclusive( - Colls::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksInclusive(Colls::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, CollBCs const& bcs) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + processDatawBestTracks(collision, tracks, besttracks, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksInclusive, - "Count tracks based on BestCollisionsFwd3d table (inclusive)", - false); + PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksInclusive, "Count tracks based on BestCollisionsFwd3d table (inclusive)", false); - void processDatawBestTracksCentFT0C( - CollsCentFT0C::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDatawBestTracksCentFT0C(CollsCentFT0C::iterator const& collision, aod::MFTTracks const& tracks, soa::SmallGroups const& besttracks, CollBCs const& bcs) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + processDatawBestTracks(collision, tracks, besttracks, bcs); } - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0C, - "Count tracks in FT0C centrality bins based on BestCollisionsFwd3d table", - false); + PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0C, "Count tracks in FT0C centrality bins based on BestCollisionsFwd3d table", false); - void processDatawBestTracksCentFT0CVariant1( - CollsCentFT0CVariant1::iterator const& collision, - FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + template + void processDataCorrelationwBestTracks(typename C::iterator const& collision, + aod::MFTTracks const& /*tracks*/, + soa::SmallGroups const& besttracks) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0CVariant1, - "Count tracks in FT0CVariant1 centrality bins based on " - "BestCollisionsFwd3d table", - false); + if (!isGoodEvent(collision)) { + return; + } - void processDatawBestTracksCentFT0M( - CollsCentFT0M::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) - { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + auto nBestTrks = 0; + for (auto const& atrack : besttracks) { + if (gConf.useTrackSel && !isBestTrackSelected(atrack)) { + continue; + } + auto itrack = atrack.template mfttrack_as(); + if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { + continue; + } + if (gConf.useTrackSel && !isTrackSelected(itrack)) { + continue; + } + nBestTrks++; + } + registryData.fill(HIST("Events/hMultMFTvsFT0A"), nBestTrks, collision.multFT0A()); + registryData.fill(HIST("Events/hMultMFTvsFT0C"), nBestTrks, collision.multFT0C()); + registryData.fill(HIST("Events/hNPVtracksVsFT0C"), collision.multNTracksPV(), collision.multFT0C()); + registryData.fill(HIST("Events/hMultMFTvsFV0A"), nBestTrks, collision.multFV0A()); + registryData.fill(HIST("Events/hNPVtracksVsMultMFT"), collision.multNTracksPV(), nBestTrks); } - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentFT0M, - "Count tracks in FT0M centrality bins based on BestCollisionsFwd3d table", - false); - - void processDatawBestTracksCentNGlobal( - CollsCentNGlobal::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) + void processDataCorrelationwBestTracksInclusive(CollsCorr::iterator const& collision, + aod::MFTTracks const& tracks, + soa::SmallGroups const& besttracks) { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); + processDataCorrelationwBestTracks(collision, tracks, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentNGlobal, - "Count tracks in NGlobal centrality bins based on " - "BestCollisionsFwd3d table", - false); - - void processDatawBestTracksCentMFT( - CollsCentMFT::iterator const& collision, FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, aod::BestCollisionsFwd3dExtra const& besttracksExtra, CollBCs const& bcs) - { - processDatawBestTracks(collision, tracks, besttracks, besttracksExtra, bcs); - } + PROCESS_SWITCH(DndetaMFTPbPb, processDataCorrelationwBestTracksInclusive, "Process correlation QA based on BestCollisionsFwd3d table", false); - PROCESS_SWITCH(DndetaMFTPbPb, processDatawBestTracksCentMFT, - "Count tracks in MFT centrality bins based on BestCollisionsFwd3d table", - false); + Partition mcSample = (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); + Preslice perColMcFiltTrk = o2::aod::fwdtrack::collisionId; + Preslice perColMcFiltTrkMidrap = aod::track::collisionId; - Preslice perCol = o2::aod::fwdtrack::collisionId; - PresliceUnsorted recColPerMcCol = - aod::mccollisionlabel::mcCollisionId; - Partition mcSample = (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); + std::vector nRecPerCol; + std::vector centRecPerCol; + std::vector occRecPerCol; - /// @brief process template function to run on MC gen template - void processMC( - typename MC::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks) - { - bool gtZeroColl = false; - int gtOneColl = 0; - + void processMc(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) + { + nRecPerCol.clear(); + centRecPerCol.clear(); + occRecPerCol.clear(); + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + float occGen = -1.; + for (const auto& collision : collisions) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } + } + } + } float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } if constexpr (has_reco_cent) { - float crecMin = 105.f; + float crecMin = 999.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float c = getRecoCent(collision); - if (c < crecMin) { - crecMin = c; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float c = getRecoCent(collision); + if (c < crecMin) { + crecMin = c; + } } } } - if (cGen < 0) + if (cGen < 0) { cGen = crecMin; - } - - float occGen = -1.; - for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; - } } } + bool gtZeroColl = false; + bool gtOneRec = false; + int gtOneColl = 0; for (auto const& collision : collisions) { - float occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - float crec = getRecoCent(collision); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 1., crec, occrec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registry.fill(HIST("Events/EvtEffGen"), 1., occrec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } - - if (isGoodEvent(collision)) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { gtZeroColl = true; ++gtOneColl; - auto z = collision.posZ(); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 2., crec, occrec); - registry.fill(HIST("Events/Centrality/hRecCent"), crec, occrec); - registry.fill(HIST("Events/Centrality/hRecZvtxCent"), z, crec, - occrec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registry.fill(HIST("Events/EvtEffGen"), 2., occrec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } - - auto perColSample = tracks.sliceBy(perCol, collision.globalIndex()); - auto nTrkRec = countTracks(perColSample, z, crec, occrec); + if (!collision.has_mcCollision()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + gtOneRec = true; + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/ZvtxDiff"), - collision.posZ() - mcCollision.posZ(), crec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); } else { - qaregistry.fill(HIST("Events/ZvtxDiff"), - collision.posZ() - mcCollision.posZ()); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > - eventCuts.maxZvtxDiff) { + // midrapidity tracks for weak deacy correction study + auto midtracksPerCol = midtracks.sliceBy(perColMcFiltTrkMidrap, collision.globalIndex()); + for (auto const& midtrack : midtracksPerCol) { + if (std::abs(midtrack.eta()) >= trackCuts.etaRangeMidrap) { continue; } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkSel), occRec); + } + if (midtrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (midtrack.mcMask() == trackCuts.selMcMask))) { + const auto& particle = midtrack.template mcParticle_as(); + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(particleOrigin), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(particleOrigin), occRec); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("TracksMidrap/Centrality/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("TracksMidrap/EtaZvtx"), midtrack.eta(), collision.posZ(), midtrack.dcaXY(), midtrack.dcaZ(), static_cast(McRecTrkType::kTrkFake), occRec); + } + } } - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, - collision.posZ(), crec, occrec); - } else { - registry.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, collision.posZ(), - occrec); + auto nTrkRec = 0; + auto perColSample = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); + for (auto const& track : perColSample) { + if (!isTrackSelected(track)) { + continue; + } + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), occRec); + } + if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { + const auto& particle = track.template mcParticle_as(); + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, track.eta(), cRec, occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), track.nClusters(), track.eta(), cRec, occRec); + } + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(particleOrigin), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), phi, track.eta(), occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/NclustersEta"), track.nClusters(), track.eta(), occRec); + } + } + ++nTrkRec; + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/EtaZvtx"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), occRec); + } + } + } + nRecPerCol.emplace_back(nTrkRec); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nRecPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + } else { + registryMC.fill(HIST("Events/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); + } } } } - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 3., cGen, occGen); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); } else { - registry.fill(HIST("Events/EvtEffGen"), 3., occGen); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } - auto perCollMCsample = mcSample->sliceByCached( - aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - auto nchrg = countPart(perCollMCsample); - auto zvtxMC = mcCollision.posZ(); - - if (gtOneColl > 1) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/SplitMult"), nchrg, zvtxMC, cGen); + if (gConf.fillTrackQaHist) { + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nchrg = countPart(perCollMCsample); + if (gtOneColl > 1) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + } } else { - qaregistry.fill(HIST("Events/SplitMult"), nchrg, zvtxMC); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + } } - } - - auto nCharged = countPart(particles); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, zvtxMC, - cGen); - } else { - registry.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, zvtxMC); - } - - fillHistMC>(particles, cGen, occGen, zvtxMC, gtZeroColl); - - if (collisions.size() == 0) { + auto nCharged = countPart(particles); if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/NotFoundEventZvtx"), - mcCollision.posZ(), cGen); + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); } else { - qaregistry.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ()); + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); + } + if (gtOneRec) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), occGen); + } + } + if (collisions.size() == 0) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + } } } + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl, gtOneRec); } - void processMCInclusive( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks) - { - processMC(mccollision, collisions, particles, - tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCInclusive, - "Count MC particles (inclusive)", false); - - void processMCCentFT0C( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks) - { - processMC(mccollision, collisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0C, - "Count MC particles in FT0C centrality bins", false); - - void processMCCentFT0CVariant1( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks) - { - processMC(mccollision, collisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0CVariant1, - "Count MC particles in FT0CVariant1 centrality bins", false); - - void processMCCentFT0M( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks) - { - processMC(mccollision, collisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentFT0M, - "Count MC particles in FT0M centrality bins", false); - - void processMCCentNGlobal( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks) + void processMcInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { - processMC(mccollision, collisions, - particles, tracks); + processMc(mccollision, collisions, particles, tracks, midtracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentNGlobal, - "Count MC particles in NGlobal centrality bins", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcInclusive, "Count MC particles (inclusive)", false); - void processMCCentMFT( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks) + void processMcCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks, + FiltMidrapTracksLabeled const& midtracks) { - processMC(mccollision, collisions, - particles, tracks); + processMc(mccollision, collisions, particles, tracks, midtracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processMCCentMFT, - "Count MC particles in MFT centrality bins", false); - - PresliceUnsorted perColU = - aod::fwdtrack::bestCollisionId; + PROCESS_SWITCH(DndetaMFTPbPb, processMcCentFT0C, "Count MC particles in FT0C centrality bins", false); - /// @brief process template function to run on MC truth using - /// aod::BestCollisionsFwd3d tracks template - void processMCwBestTracks( - typename MC::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks, - FiltBestTracks const& besttracks) - { - bool gtZeroColl = false; + void processMcBest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& /*tracks*/, + MftBestTracksLabeled const& besttracks) + { + nRecPerCol.clear(); + centRecPerCol.clear(); + occRecPerCol.clear(); + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + float occGen = -1.; + for (const auto& collision : collisions) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } + } + } + } float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } if constexpr (has_reco_cent) { - float crecMin = 105.f; + float crecMin = 999.; for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float c = getRecoCent(collision); - if (c < crecMin) { - crecMin = c; + if (!gConf.useEvtSel || isGoodEvent(collision)) { + if (!gConf.rmSplitVertex || (collision.globalIndex() == mcCollision.bestCollisionIndex())) { + float c = getRecoCent(collision); + if (c < crecMin) { + crecMin = c; + } } } } - if (cGen < 0) + if (cGen < 0) { cGen = crecMin; - } - - float occGen = -1.; - for (const auto& collision : collisions) { - if (isGoodEvent(collision)) { - float o = getOccupancy(collision, eventCuts.occupancyEstimator); - if (o > occGen) { - occGen = o; - } } } - for (auto const& collision : collisions) { - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - float crec = getRecoCent(collision); + buildLookupTable(collisions); + bool gtZeroColl = false; + bool gtOneRec = false; + int gtOneColl = 0; + for (auto const& collision : collisions) { if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 1., crec, occrec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registry.fill(HIST("Events/EvtEffGen"), 1., occrec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecAll), getOccupancy(collision, eventCuts.occupancyEstimator)); } - - if (isGoodEvent(collision)) { + if (!gConf.useEvtSel || isGoodEvent(collision)) { gtZeroColl = true; - auto z = collision.posZ(); - + ++gtOneColl; if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 2., crec, occrec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registry.fill(HIST("Events/EvtEffGen"), 2., occrec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (!collision.has_mcCollision()) { + continue; } - - auto perCollisionSample = - tracks.sliceBy(perCol, collision.globalIndex()); - auto perCollisionASample = - besttracks.sliceBy(perColU, collision.globalIndex()); - auto nTrkRec = countBestTracks( - perCollisionSample, perCollisionASample, z, crec, - collision.trackOccupancyInTimeRange()); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen"), nTrkRec, z, - crec, occrec); + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registry.fill(HIST("Events/NtrkZvtxGen"), nTrkRec, z, occrec); + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); } - } - } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + gtOneRec = true; + float cRec = getRecoCent(collision); + float occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + centRecPerCol.emplace_back(cRec); + occRecPerCol.emplace_back(occRec); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/EvtEffGen"), 3., cGen, occGen); - } else { - registry.fill(HIST("Events/EvtEffGen"), 3., occGen); - } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), cRec, occRec); + registryMC.fill(HIST("Events/Centrality/hRecZvtxCent"), collision.posZ(), cRec, occRec); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcRecNoSplitVtx), occRec); + } - auto zvtxMC = mcCollision.posZ(); - auto nCharged = countPart(particles); - if constexpr (has_reco_cent) { - registry.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, zvtxMC, - cGen); - } else { - registry.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, zvtxMC); - } - - fillHistMC>(particles, cGen, occGen, zvtxMC, gtZeroColl); - - if (collisions.size() == 0) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/NotFoundEventZvtx"), - mcCollision.posZ(), cGen); - } else { - qaregistry.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ()); - } - } - } - - void processMCwBestTracksInclusive( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksInclusive, - "Count MC particles using aod::BestCollisionsFwd3d (inclusive)", - false); - - void processMCwBestTracksCentFT0C( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentFT0C, - "Count MC particles in FT0C centrality bins using aod::BestCollisionsFwd3d", - false); - - void processMCwBestTracksCentFT0CVariant1( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentFT0CVariant1, - "Count MC particles in FT0CVariant1 centrality bins using " - "aod::BestCollisionsFwd3d", - false); - - void processMCwBestTracksCentFT0M( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentFT0M, - "Count MC particles in FT0M centrality bins using aod::BestCollisionsFwd3d", - false); - - void processMCwBestTracksCentNGlobal( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentNGlobal, - "Count MC particles in NGlobal centrality bins using " - "aod::BestCollisionsFwd3d", - false); - - void processMCwBestTracksCentMFT( - aod::McCollisions::iterator const& mccollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles, FiltMcMftTracks const& tracks, - FiltBestTracks const& besttracks) - { - processMCwBestTracks( - mccollision, collisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMCwBestTracksCentMFT, - "Count MC particles in MFT centrality bins using aod::BestCollisionsFwd3d", - false); - - using ParticlesI = soa::Join; - Partition primariesI = (aod::mcparticle::flags & (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary) == (uint8_t)o2::aod::mcparticle::enums::PhysicalPrimary && (aod::mcparticle::eta < trackCuts.maxEta) && (aod::mcparticle::eta > trackCuts.minEta); - - Preslice perMCCol = aod::mcparticle::mcCollisionId; - - template - void processTrkEffIdxBest( - typename soa::Join const& collisions, - MC const& /*mccollisions*/, ParticlesI const& /*particles*/, - BestTracksMC const& atracks) - { - for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - if (!collision.has_mcCollision()) { - continue; - } - - float crec = getRecoCent(collision); - auto mcCollision = collision.mcCollision(); - - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; - } - } - - auto partsPerCol = primariesI->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - partsPerCol.bindExternalIndices(&atracks); - for (auto const& particle : partsPerCol) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - - // MC gen - if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenBest"), particle.pt(), particle.eta(), crec); - } + auto nATrk = 0; + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBestTrkAll), occRec); } - } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenBest"), particle.pt(), particle.eta()); - } + if (!isBestTrackSelected(atrack)) { + continue; } - } - // MC rec - if (particle.has_mfttracks()) { - auto iscounted = false; - auto ncnt = 0; - auto relatedTracks = particle.template mfttracks_as(); - for (auto const& track : relatedTracks) { - if (!isBestTrackSelected(track)) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kBesTrktSel), occRec); + } + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + float phi = itrack.phi(); + o2::math_utils::bringTo02Pi(phi); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkAsBestSel), occRec); + } + if (!itrack.has_collision()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkHasColl), occRec); + } + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } - ++ncnt; - - if constexpr (has_reco_cent) { - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffPrimBest"), particle.pt(), particle.eta(), crec); - } - iscounted = true; - } - } - if (ncnt > 1) { // secondaries - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffSecBest"), particle.pt(), particle.eta(), crec); - } - } - } else { - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffPrimBest"), particle.pt(), particle.eta()); - } - iscounted = true; - } - } - if (ncnt > 1) { // secondaries - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/hPtEtaEffSecBest"), particle.pt(), particle.eta()); - } - } - } - - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NmftTrkPerPartBest"), ncnt, crec); - } else { - qaregistry.fill(HIST("Tracks/NmftTrkPerPartBest"), ncnt); - } - - if (relatedTracks.size() > 1) { // duplicates - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenDuplBest"), particle.pt(), particle.eta(), crec); - for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffDuplBest"), track.pt(), track.eta(), crec); - } - } else { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenDuplBest"), particle.pt(), particle.eta()); - for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/hPtEtaEffDuplBest"), track.pt(), track.eta()); - } - } - } } - } else { - // MC FAKES if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenFakeBest"), particle.pt(), particle.eta(), crec); + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), cRec, occRec); + } else { + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkReassignedRemoved), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), occRec); + } + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + const auto& particle = itrack.template mcParticle_as(); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; } } - } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenFakeBest"), particle.pt(), particle.eta()); + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; } } - } - } - } - } - } - - void processTrkEffIdxBestInlusive( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - BestTracksMC const& atracks) - { - processTrkEffIdxBest(collisions, mccollisions, particles, atracks); - } - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxBestInlusive, "Process tracking efficiency best (inclusive, indexed)", false); - - void processTrkEffIdxBestCentFT0C( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - BestTracksMC const& atracks) - { - processTrkEffIdxBest(collisions, mccollisions, particles, atracks); - } - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); - - /// @brief process template function to calculate tracking efficiency (indexed - /// as particle-to-MFT-tracks) - template - void processTrkEffIdx( - typename soa::Join const& collisions, - MC const& /*mccollisions*/, ParticlesI const& /*particles*/, - MFTTracksLabeled const& tracks) - { - for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - if (!collision.has_mcCollision()) { - continue; - } - - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto mcCollision = collision.mcCollision(); - - auto partsPerCol = primariesI->sliceByCached( - aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - partsPerCol.bindExternalIndices(&tracks); - - for (auto const& particle : partsPerCol) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - - // MC gen - if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGen"), particle.pt(), particle.eta(), crec, occrec); + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; } - } - } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffGen"), particle.pt(), particle.eta(), occrec); + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } } - } - } - // MC rec - if (particle.has_mfttracks()) { - auto iscounted = false; - auto ncnt = 0; - auto relatedTracks = particle.template mfttracks_as(); - for (auto const& track : relatedTracks) { - ++ncnt; if constexpr (has_reco_cent) { - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffPrim"), particle.pt(), particle.eta(), crec, occrec); - } - iscounted = true; - } - } - if (ncnt > 1) { // secondaries - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffSec"), particle.pt(), particle.eta(), crec, occrec); - } + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/PhiEta"), phi, itrack.eta(), cRec, occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/Centrality/NclustersEta"), itrack.nClusters(), itrack.eta(), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/TrackAmbDegree"), atrack.ambDegree(), cRec, occRec); } } else { - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if (!iscounted) { // primaries - if (std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { - qaregistry.fill(HIST("Tracks/hPtEtaEffPrim"), particle.pt(), particle.eta(), occrec); - } - iscounted = true; - } - } - if (ncnt > 1) { // secondaries - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/hPtEtaEffSec"), particle.pt(), particle.eta(), occrec); - } + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), occRec); + registryMC.fill(HIST("Tracks/PhiEta"), phi, itrack.eta(), occRec); + if (gConf.fillTrackQaHist) { + registryMC.fill(HIST("Tracks/NclustersEta"), itrack.nClusters(), itrack.eta(), occRec); + registryMC.fill(HIST("Tracks/TrackAmbDegree"), atrack.ambDegree(), occRec); } } - } - - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, crec, occrec); + ++nATrk; } else { - qaregistry.fill(HIST("Tracks/NmftTrkPerPart"), ncnt, occrec); - } - - if (relatedTracks.size() > 1) { // duplicates if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffGenDupl"), particle.pt(), particle.eta(), crec, occrec); - for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEtaEffDupl"), track.pt(), track.eta(), crec, occrec); - } + registryMC.fill(HIST("Tracks/Centrality/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), cRec, occRec); + registryMC.fill(HIST("Tracks/Centrality/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), cRec, occRec); } else { - qaregistry.fill(HIST("Tracks/hPtEtaEffGenDupl"), particle.pt(), particle.eta(), occrec); - for (auto const& track : relatedTracks) { - qaregistry.fill(HIST("Tracks/hPtEtaEffDupl"), track.pt(), track.eta(), occrec); - } + registryMC.fill(HIST("Tracks/hMcTrackStatus"), static_cast(McTrackStatus::kTrkFake), occRec); + registryMC.fill(HIST("Tracks/EtaZvtx"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), occRec); } } } - } - } - } - - void processTrkEffIdxInlusive( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - MFTTracksLabeled const& tracks) - { - processTrkEffIdx(collisions, mccollisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxInlusive, - "Process tracking efficiency (inclusive, indexed)", false); - - void processTrkEffIdxCentFT0C( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, ParticlesI const& particles, - MFTTracksLabeled const& tracks) - { - processTrkEffIdx(collisions, mccollisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffIdxCentFT0C, - "Process tracking efficiency (in FT0C centrality bins, indexed)", false); - - /// @brief process function to calculate tracking efficiency (indexed) based - /// on BestCollisionsFwd3d in FT0C bins - template - void processTrkEffBest( - typename soa::Join::iterator const& collision, - MC const& /*mccollisions*/, FiltParticles const& particles, - FiltMcMftTracks const& tracks, - soa::SmallGroups const& besttracks) - { - if (!isGoodEvent(collision)) { - return; - } - if (!collision.has_mcCollision()) { - return; - } - - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto mcCollision = collision.mcCollision(); - auto partsPerCol = particles.sliceByCached( - aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - - for (auto const& particle : partsPerCol) { - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - if constexpr (has_reco_cent) { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/Centrality/hPtPhiEtaZvtxEffBestGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), crec, occrec); - } - } else { - if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { - qaregistry.fill(HIST("Tracks/hPtPhiEtaZvtxEffBestGen"), particle.pt(), particle.phi(), particle.eta(), mcCollision.posZ(), occrec); - } - } - } - - ambiguousTrkIdsMC.reserve(besttracks.size()); - reassignedTrkIdsMC.reserve(besttracks.size()); - - for (auto const& track : besttracks) { - ambiguousTrkIdsMC.emplace_back(track.mfttrackId()); - if (!isBestTrackSelected(track)) { - continue; - } - auto itrack = track.mfttrack_as(); - if (itrack.collisionId() != track.bestCollisionId()) { - reassignedTrkIdsMC.emplace_back(track.mfttrackId()); - } - if (!isTrackSelected(itrack)) { - continue; - } - if (itrack.has_mcParticle()) { - auto particle = itrack.mcParticle_as(); - if (itrack.eta() > trackCuts.minEta && itrack.eta() < trackCuts.maxEta) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtPhiEtaZvtxEffBestRec"), - particle.pt(), particle.phi(), particle.eta(), - mcCollision.posZ(), crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hPtPhiEtaZvtxEffBestRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - occrec); - } - } - } else { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEffBestFakeRec"), itrack.pt(), itrack.phi(), itrack.eta(), mcCollision.posZ(), crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hPtEffBestFakeRec"), itrack.pt(), itrack.phi(), itrack.eta(), mcCollision.posZ(), occrec); - } - } - } - - for (auto const& track : tracks) { - if (std::find(ambiguousTrkIdsMC.begin(), ambiguousTrkIdsMC.end(), track.globalIndex()) != ambiguousTrkIdsMC.end()) { - continue; - } - if (std::find(reassignedTrkIdsMC.begin(), reassignedTrkIdsMC.end(), track.globalIndex()) != reassignedTrkIdsMC.end()) { - continue; - } - if (!isTrackSelected(track)) { - continue; - } - if (track.has_mcParticle()) { - auto particle = track.mcParticle_as(); - if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtPhiEtaZvtxEffBestRec"), - particle.pt(), particle.phi(), particle.eta(), - mcCollision.posZ(), crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hPtPhiEtaZvtxEffBestRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - occrec); + nRecPerCol.emplace_back(nATrk); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { + continue; } } - } else { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hPtEffBestFakeRec"), track.pt(), track.phi(), track.eta(), mcCollision.posZ(), crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hPtEffBestFakeRec"), track.pt(), track.phi(), track.eta(), mcCollision.posZ(), occrec); - } - } - } - ambiguousTrkIdsMC.clear(); - ambiguousTrkIdsMC.shrink_to_fit(); - reassignedTrkIdsMC.clear(); - reassignedTrkIdsMC.shrink_to_fit(); - } - - void processTrkEffBestInclusive( - soa::Join::iterator const& collision, - aod::McCollisions const& mccollisions, FiltParticles const& particles, - FiltMcMftTracks const& tracks, - soa::SmallGroups const& besttracks) - { - processTrkEffBest(collision, mccollisions, - particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffBestInclusive, - "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", - false); - - void processTrkEffBestCentFT0C( - soa::Join::iterator const& collision, - aod::McCollisions const& mccollisions, FiltParticles const& particles, - FiltMcMftTracks const& tracks, - soa::SmallGroups const& besttracks) - { - processTrkEffBest( - collision, mccollisions, particles, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processTrkEffBestCentFT0C, - "Process tracking efficiency (in FT0 centrality bins, based " - "on BestCollisionsFwd3d)", - false); - - Preslice filtMcTrkperCol = o2::aod::fwdtrack::collisionId; - - /// @brief process function to calculate MC efficiency and fraction of fake - /// tracks - template - void processEfficiency( - typename soa::Join const& collisions, - MC const& /*mccollisions*/, FiltParticles const& /*particles*/, - FiltMcMftTracks const& tracks) - { - for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - auto mcCollision = collision.mcCollision(); - auto perColTrks = - tracks.sliceBy(filtMcTrkperCol, collision.globalIndex()); - - for (auto const& track : perColTrks) { - if (!isTrackSelected(track)) { - continue; - } - if (track.has_mcParticle()) { - auto particle = track.template mcParticle_as(); - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hEffRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - crec, occrec); - } else { - qaregistry.fill(HIST("Tracks/hEffRec"), particle.pt(), - particle.phi(), particle.eta(), mcCollision.posZ(), - crec, occrec); + if (gConf.fillTrackQaHist) { + for (auto i = 0U; i < nRecPerCol.size(); ++i) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), centRecPerCol[i], occRecPerCol[i]); + } else { + registryMC.fill(HIST("Events/NtrkZvtxRec"), nRecPerCol[i], collision.posZ(), occRecPerCol[i]); + } } - } else { if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hEffFake"), track.pt(), - track.phi(), track.eta(), mcCollision.posZ(), crec, - occrec); + registryMC.fill(HIST("Events/Centrality/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), cRec, occRec); } else { - qaregistry.fill(HIST("Tracks/hEffFake"), track.pt(), track.phi(), - track.eta(), mcCollision.posZ(), crec, occrec); + registryMC.fill(HIST("Events/ZvtxDiff"), collision.posZ() - mcCollision.posZ(), occRec); } } } } - } - - void processEfficiencyInclusive( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, FiltParticles const& particles, - FiltMcMftTracks const& tracks) - { - processEfficiency(collisions, mccollisions, - particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyInclusive, - "Process efficiencies (inclusive)", false); - - void processEfficiencyCentFT0C( - soa::Join const& collisions, - aod::McCollisions const& mccollisions, FiltParticles const& particles, - FiltMcMftTracks const& tracks) - { - processEfficiency( - collisions, mccollisions, particles, tracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processEfficiencyCentFT0C, - "Process efficiencies in FT0C centrality bins", false); - - template - void processCorrelationwBestTracks(typename C::iterator const& collision, FiltMftTracks const& /*tracks*/, soa::SmallGroups const& besttracks) - { - if (!isGoodEvent(collision)) { - return; - } - - auto nBestTrks = 0; - for (auto const& atrack : besttracks) { - if (gConf.cfgUseTrackSel && !isBestTrackSelected(atrack)) { - continue; - } - auto itrack = atrack.template mfttrack_as(); - if (itrack.eta() < trackCuts.minEta || itrack.eta() > trackCuts.maxEta) { - continue; - } - if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { - continue; - } - nBestTrks++; - } - qaregistry.fill(HIST("Events/hMultMFTvsFT0A"), nBestTrks, collision.multFT0A()); - qaregistry.fill(HIST("Events/hMultMFTvsFT0C"), nBestTrks, collision.multFT0C()); - qaregistry.fill(HIST("Events/hNPVtracksVsFT0C"), collision.multNTracksPV(), collision.multFT0C()); - qaregistry.fill(HIST("Events/hMultMFTvsFV0A"), nBestTrks, collision.multFV0A()); - qaregistry.fill(HIST("Events/hNPVtracksVsMultMFT"), collision.multNTracksPV(), nBestTrks); - } - - void processCorrelationwBestTracksInclusive(CollsCorr::iterator const& collision, FiltMftTracks const& tracks, soa::SmallGroups const& besttracks) - { - processCorrelationwBestTracks(collision, tracks, besttracks); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processCorrelationwBestTracksInclusive, "Do correlation study based on BestCollisionsFwd3d table", false); - - int getQuadrantPhi(float phi) - { - if (phi >= Czero && phi < Cninety) { - return 0; - } - if (phi >= Cninety && phi <= ConeHeighty) { - return 1; - } - if (phi >= -ConeHeighty && phi < -Cninety) { - return 2; - } - if (phi >= -Cninety && phi < Czero) { - return 3; - } - return -1; - } - - template - int getQuadrantTrack(T const& track) - { - float phi = static_cast(track.phi()) * ConeHeighty / PI; - return getQuadrantPhi(phi); - } - - /// @brief process function to check MFT alignment (based on BestCollisionsFwd3d table) - template - void processAlignment(typename C::iterator const& collision, - FiltMftTracks const& /*tracks*/, - soa::SmallGroups const& besttracks, - CollBCs const& /*bcs*/ - ) - { - auto bc = collision.template foundBC_as(); - if (!isGoodEvent(collision)) { - return; - } - - if (gConf.cfgDoIR) { - initHadronicRate(bc); - float ir = !gConf.cfgIRSource.value.empty() ? rateFetcher.fetch(ccdb.service, bc.timestamp(), bc.runNumber(), gConf.cfgIRSource, gConf.cfgIRCrashOnNull) * 1.e-3 : -1; - float seconds = bc.timestamp() * 1.e-3 - mMinSeconds; - if (gConf.cfgUseIRCut && (ir < eventCuts.minIR || ir > eventCuts.maxIR)) { // cut on hadronic rate - return; - } - gCurrentHadronicRate->Fill(seconds, ir); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), cGen, occGen); + } else { + registryMC.fill(HIST("Events/McEvtStatus"), static_cast(McEvtStatus::kMcGenAll), occGen); } - auto z = collision.posZ(); - for (auto const& atrack : besttracks) { - if (!isBestTrackSelected(atrack)) { - continue; + if (gConf.fillTrackQaHist) { + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto nchrg = countPart(perCollMCsample); + if (gtOneColl > 1) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxSplit"), nchrg, mcCollision.posZ(), occGen); + } + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NgenZvtxNoSplit"), nchrg, mcCollision.posZ(), occGen); + } } - auto itrack = atrack.template mfttrack_as(); - if (!isTrackSelected(itrack)) { - continue; + auto nCharged = countPart(particles); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGen_t"), nCharged, mcCollision.posZ(), occGen); } - - int quadrant = getQuadrantTrack(itrack); - if (quadrant < 0) { - continue; + if (gtOneRec) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NtrkZvtxGenRecEvt"), nCharged, mcCollision.posZ(), occGen); + } + } + if (collisions.size() == 0) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/NotFoundEventZvtx"), mcCollision.posZ(), cGen, occGen); + } else { + registryMC.fill(HIST("Events/NotFoundEventZvtx"), mcCollision.posZ(), occGen); + } } - std::get>(hAlignment[0][quadrant][0]["DCA_x_vs_z"])->Fill(z, atrack.bestDCAXY()); } + fillHistMC>(particles, mcCollision.posZ(), cGen, occGen, gtZeroColl, gtOneRec); } - void processAlignmentInclusive(Colls::iterator const& collision, - FiltMftTracks const& tracks, - soa::SmallGroups const& besttracks, - CollBCs const& bcs) + void processMcBestInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& besttracks) { - processAlignment(collision, tracks, besttracks, bcs); + processMcBest(mccollision, collisions, particles, tracks, besttracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processAlignmentInclusive, "Check MFT alignment using tracks based on BestCollisionsFwd3d table (inclusive)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcBestInclusive, "Count MC particles using aod::BestCollisionsFwd3d (inclusive)", false); - /// @brief process function to calculate signal loss based on MC - void processEventAndSignalLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, - soa::SmallGroups> const& collisions, - FiltParticles const& particles) + void processMcBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& besttracks) { - registry.fill(HIST("Events/Centrality/hNRecCollsSigEvtLoss"), 1.f, collisions.size()); + processMcBest(mccollision, collisions, particles, tracks, besttracks); + } - if (gConf.cfgUseInelgt0 && !mcCollision.isInelGt0()) { - return; - } - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { - return; - } + PROCESS_SWITCH(DndetaMFTPbPb, processMcBestCentFT0C, "Count MC particles in FT0C centrality bins using aod::BestCollisionsFwd3d", false); - bool gtZeroColl = false; - auto maxNcontributors = -1; - auto centrality = -1; - for (auto const& collision : collisions) { + /// @brief process function to calculate tracking efficiency + template + void processMcEfficiency(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + auto occRec = CInvalid; + bool gtOneRec = false; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } + for (const auto& collision : collisions) { if (!isGoodEvent(collision)) { continue; } - if (std::abs(collision.posZ()) >= eventCuts.maxZvtx) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - centrality = getRecoCent(collision); - } - gtZeroColl = true; + gtOneRec = true; + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); } - - auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); - auto multMCNParticlesEtaMFT = countPart(perCollMCsample); - - registry.fill(HIST("Events/Centrality/EvtSigLossStatus"), 1., centrality); - registry.fill(HIST("Events/Centrality/hMultGenVsCent"), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentNParticlesEta05"), centrality, mcCollision.multMCNParticlesEta05()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentNParticlesEtaMFT"), centrality, multMCNParticlesEtaMFT); - - if (collisions.size() == 0) { - registry.fill(HIST("Events/Centrality/EvtSigLossStatus"), 3., centrality); + if (cGen < 0 && cRec >= 0) { + cGen = cRec; } - - if (gtZeroColl) { - registry.fill(HIST("Events/Centrality/EvtSigLossStatus"), 2., centrality); - registry.fill(HIST("Events/Centrality/hMultGenVsCentRec"), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentRecNParticlesEta05"), centrality, mcCollision.multMCNParticlesEta05()); - registry.fill(HIST("Events/Centrality/hMultGenVsCentRecNParticlesEtaMFT"), centrality, multMCNParticlesEtaMFT); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); + } + } else { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); + } } - - for (auto const& particle : particles) { + for (const auto& particle : particles) { if (!isChrgParticle(particle.pdgCode())) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - - registry.fill(HIST("Tracks/Centrality/EtaCentVsMultGen_t"), particle.eta(), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Tracks/Centrality/EtaGen_t"), particle.eta(), centrality); - - if (gtZeroColl) { - float phi = particle.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); + } + } + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); + } } - registry.fill(HIST("Tracks/Centrality/EtaCentVsMultGen"), particle.eta(), centrality, mcCollision.multMCFT0C()); - registry.fill(HIST("Tracks/Centrality/EtaGen"), particle.eta(), centrality); } } - } - - PROCESS_SWITCH(DndetaMFTPbPb, processEventAndSignalLossCentFT0C, "Signal/event loss based on MC (in FT0C centrality bins)", false); - - Preslice filtTrkperCol = o2::aod::fwdtrack::collisionId; - - /// @brief process template function for MC QA checks - template - void processMcQA(typename soa::Join const& collisions, - MFTTracksLabeled const& tracks, - aod::AmbiguousMFTTracks const& atracks, - aod::McCollisions const& mcCollisions, - FiltParticles const& /*particles*/) - { for (const auto& collision : collisions) { - float crec = getRecoCent(collision); - auto occrec = getOccupancy(collision, eventCuts.occupancyEstimator); - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/hRecPerGenColls"), - static_cast(collisions.size()) / - mcCollisions.size(), - crec, occrec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - qaregistry.fill(HIST("Events/hRecPerGenColls"), - static_cast(collisions.size()) / - mcCollisions.size(), - occrec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision)); } - - if (!isGoodEvent(collision)) { - return; + if (gConf.useEvtSel && !isGoodEvent(collision)) { + continue; } - - auto trkPerColl = tracks.sliceBy(filtTrkperCol, collision.globalIndex()); - uint ntracks{0u}, nAtracks{0u}; - for (const auto& track : trkPerColl) { - ntracks++; - for (const auto& atrack : atracks) { - if (atrack.mfttrackId() == track.globalIndex()) { - nAtracks++; - break; - } - } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision)); + } + if (!collision.has_mcCollision()) { + continue; } if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Tracks/Centrality/hNmftTrks"), ntracks, crec, - occrec); - qaregistry.fill(HIST("Tracks/Centrality/hFracAmbiguousMftTrks"), - static_cast(nAtracks) / ntracks, crec, occrec); + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - qaregistry.fill(HIST("Tracks/hNmftTrks"), ntracks, occrec); - qaregistry.fill(HIST("Tracks/hFracAmbiguousMftTrks"), - static_cast(nAtracks) / ntracks, occrec); + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision)); } - } - } - - void processMcQAInclusive( - soa::Join const& collisions, - MFTTracksLabeled const& tracks, aod::AmbiguousMFTTracks const& atracks, - aod::McCollisions const& mcCollisions, FiltParticles const& particles) - { - processMcQA(collisions, tracks, atracks, mcCollisions, particles); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMcQAInclusive, - "Process MC QA checks (inclusive)", false); - - void processMcQACentFT0C( - soa::Join const& collisions, - MFTTracksLabeled const& tracks, aod::AmbiguousMFTTracks const& atracks, - aod::McCollisions const& mcCollisions, FiltParticles const& particles) - { - processMcQA(collisions, tracks, atracks, mcCollisions, - particles); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processMcQACentFT0C, - "Process MC QA checks (in FT0 centrality bins)", false); - - /// @brief process function to check ambiguous tracks - void processCheckAmbiguousMftTracks(aod::Collisions const&, MftTracksWColls const& tracks) - { - for (auto const& track : tracks) { - auto trkCollId = track.has_collision() ? track.collisionId() : -1; - auto ids = track.compatibleCollIds(); - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kAll)); - if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { - qaregistry.fill(HIST("Tracks/hMftTracksAmbDegreeWithTrivial"), track.compatibleCollIds().size()); - if (ids.empty()) { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kOrphan)); - } - if (ids.size() == 1 && trkCollId == ids[0]) { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb)); - } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - qaregistry.fill(HIST("Tracks/hMftTracksAmbDegree"), track.compatibleCollIds().size()); - - if (track.compatibleCollIds().size() > 0) { - if (track.compatibleCollIds().size() == 1) { - if (track.collisionId() != track.compatibleCollIds()[0]) { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kAmb)); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision)); + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), getRecoCent(collision)); + } + auto perColTrks = tracks.sliceBy(perColMcFiltTrk, collision.globalIndex()); + for (auto const& track : perColTrks) { + if (!isTrackSelected(track)) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (track.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (track.mcMask() == trackCuts.selMcMask))) { + const auto& particle = track.template mcParticle_as(); + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { + continue; + } + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.eta(), collision.posZ(), static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame)); + registryMC.fill(HIST("Tracks/hEffRec"), particle.eta(), collision.posZ(), static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (track.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } } else { - qaregistry.fill(HIST("Tracks/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1)); + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), track.eta(), collision.posZ(), static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); + } } } } } - PROCESS_SWITCH(DndetaMFTPbPb, processCheckAmbiguousMftTracks, "Process checks for Ambiguous MFT tracks (inclusive)", false); + void processMcEfficiencyInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) + { + processMcEfficiency(mccollision, collisions, particles, tracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyInclusive, "Process efficiencies (inclusive)", false); - // Preslice perColMc = aod::mcparticle::mcCollisionId; - void processTimeAssocMC(CollsMCExtra const& mcCollisions, - CollisionsWithMCLabels const& collisions, - MftTracksWCollsMC const& tracks, - aod::McParticles const& /*particles*/, - aod::McCollisionLabels const& labels) + void processMcEfficiencyCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftTracksLabeled const& tracks) { - const auto& nRecoColls = collisions.size(); - LOG(info) << "reconstructed collisions: " << nRecoColls; - const auto& nMcColls = mcCollisions.size(); - LOG(info) << "MC collisions: " << nMcColls; - const auto& nLabels = labels.size(); - LOG(info) << "collision labels: " << nLabels; + processMcEfficiency(mccollision, collisions, particles, tracks); + } - std::unordered_map mapRecToMc; - mapRecToMc.clear(); - mapRecToMc.reserve(nRecoColls); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyCentFT0C, "Process efficiencies in FT0C centrality bins", false); - // std::unordered_map mapVtxXrec; - mapVtxXrec.clear(); - mapVtxXrec.reserve(nRecoColls); - // std::unordered_map mapVtxYrec; - mapVtxYrec.clear(); - mapVtxYrec.reserve(nRecoColls); - // std::unordered_map mapVtxZrec; - mapVtxZrec.clear(); - mapVtxZrec.reserve(nRecoColls); - - if (nRecoColls <= CintZero) { - return; - } + /// @brief process function to calculate tracking efficiency based on BestCollisionsFwd3d in FT0C bins + template + void processMcEfficiencyBest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); - auto maxNcontributors = -1; - auto bestCollIndex = -1; - for (auto const& collision : collisions) { - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - bestCollIndex = collision.globalIndex(); - mapVtxXrec.emplace(collision.globalIndex(), collision.posX()); - mapVtxYrec.emplace(collision.globalIndex(), collision.posY()); - mapVtxZrec.emplace(collision.globalIndex(), collision.posZ()); - mapRecToMc.emplace(collision.globalIndex(), collision.mcCollisionId()); - } - } - LOG(info) << "mapRecToMc size: " << mapRecToMc.size(); - LOG(info) << "mapVtxXrec size: " << mapVtxXrec.size(); - - std::unordered_map mapVtxXgen; - mapVtxXgen.clear(); - mapVtxXgen.reserve(nMcColls); - std::unordered_map mapVtxYgen; - mapVtxYgen.reserve(nMcColls); - mapVtxYgen.clear(); - std::unordered_map mapVtxZgen; - mapVtxZgen.clear(); - mapVtxZgen.reserve(nMcColls); + buildLookupTable(collisions); - for (const auto& mcCollision : mcCollisions) { - mapVtxXgen.emplace(mcCollision.globalIndex(), mcCollision.posX()); - mapVtxYgen.emplace(mcCollision.globalIndex(), mcCollision.posY()); - mapVtxZgen.emplace(mcCollision.globalIndex(), mcCollision.posZ()); + auto cRec = CInvalid; + auto occRec = CInvalid; + bool gtOneRec = false; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); } - LOG(info) << "mapVtxXgen size: " << mapVtxXgen.size(); - - int nNoMC{0}; for (const auto& collision : collisions) { - registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsAll)); - if (!isGoodEvent(collision)) { + if (!isGoodEvent(collision)) { continue; } - registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSelected)); - if (!collision.has_mcCollision()) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsHasMcColl)); - - int64_t recCollId = collision.globalIndex(); - auto itMC = mapRecToMc.find(recCollId); - if (itMC == mapRecToMc.end()) { - nNoMC++; - LOGP(debug, "collison {} has no MC coll", recCollId); + gtOneRec = true; + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/Centrality/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); + } + } else { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Events/hVtxZGen"), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); + } + } + for (const auto& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { continue; } - auto mcCollision = collision.mcCollision_as(); - if (gConf.cfgRemoveSplitVertex && (bestCollIndex != collision.globalIndex())) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSplitVtxRemoved)); - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), cGen, occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/Centrality/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), cGen, occRec); + } + } + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenAll), occRec); + if (gtOneRec) { + registryMC.fill(HIST("Tracks/hEffGen"), particle.eta(), mcCollision.posZ(), static_cast(McGenTrkType::kGenRecEvt), occRec); + } + } + } + } + for (const auto& collision : collisions) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffAll), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } - registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsZVtxCutMC)); - - // Loop on collision compatible MFT tracks - // Check: (1) good/bad vertices (2) if bad true vertex is available among rec vertices - for (const auto& track : tracks) { - if (!track.has_collision()) { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffSel), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (!collision.has_mcCollision()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffHasMcColl), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hMcEffStatus"), static_cast(McEffStatus::kMcEffNoSplitVtx), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Events/Centrality/hVtxZRec"), collision.posZ(), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Events/hVtxZRec"), collision.posZ(), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { + if (!isBestTrackSelected(atrack)) { continue; } - auto trkCollId = track.has_collision() ? track.collisionId() : -1; - auto ids = track.compatibleCollIds(); - if (trkCollId != recCollId) { // check if track is associated to rec coll + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { continue; } - registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kAll)); - if (ids.empty()) { - registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kOrphan)); - } - if (gConf.cfgRemoveOrphanTracks && ids.empty()) { + if (!itrack.has_collision()) { continue; } - if (gConf.cfgRemoveTrivialAssoc) { - if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { continue; } } - if (gConf.cfgRemoveAmbiguousTracks && (track.compatibleCollIds().size() != 1)) { - continue; - } - if (ids.size() > 0) { - if (ids.size() == 1) { - if (trkCollId == ids[0]) { - registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb)); - } else if (trkCollId != ids[0]) { - registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kAmb)); - } else { - registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame)); - } - } else { - registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1)); - } + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } else { - registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kOrphanNull)); + registryMC.fill(HIST("Tracks/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkSel), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (gConf.cfgUseTrackSel && !isTrackSelected(track)) { - continue; - } - - bool isTrueVtx = false; - int vtxFlag = static_cast(VertexStatusMC::kNull); - - float vtxX = -1.; - float vtxY = -1.; - float vtxZ = -1.; - float deltaXv1 = -1.; - float deltaYv1 = -1.; - float deltaZv1 = -1.; - float deltaXv2 = -1.; - float deltaYv2 = -1.; - float deltaZv2 = -1.; - - if (track.collisionId() >= 0 && track.has_mcParticle() && track.mcMask() == 0) { - auto itMCTrk = mapRecToMc.find(trkCollId); - const auto& mcPart = track.mcParticle(); - if (!isChrgParticle(mcPart.pdgCode())) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(mcPart)) { - continue; - } - int64_t mcPartId = mcPart.mcCollisionId(); - - // check if rec vertex is available in MC collisions - for (const auto& mcTrkId : mapRecToMc) { - if (mcTrkId.second == mcPartId) { - isTrueVtx = true; - break; + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + const auto& particle = itrack.template mcParticle_as(); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; } } - - // check if there is good or bad collision - if (itMCTrk != mapRecToMc.end()) { - int mcTrkCollId = itMCTrk->second; - if (mcPartId == mcTrkCollId) { // particle.mcCollisionId == collision.mcCollisionId -> good vtx - vtxFlag = static_cast(VertexStatusMC::kGood); - } else { // wrong vtx - vtxFlag = static_cast(VertexStatusMC::kBad); + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; } } - - if (mapVtxXrec.find(trkCollId) == mapVtxXrec.end()) { - continue; - } - if (mapVtxYrec.find(trkCollId) == mapVtxYrec.end()) { - continue; - } - if (mapVtxZrec.find(trkCollId) == mapVtxZrec.end()) { - continue; - } - if (mapRecToMc.find(trkCollId) == mapRecToMc.end()) { + bool isPhysicalPrimary = particle.isPhysicalPrimary(); + bool isSecondary = (particle.has_mothers() && particle.getProcess() == TMCProcess::kPDecay); + if (!isPhysicalPrimary && !isSecondary) { continue; } - vtxX = mapVtxXrec.find(trkCollId)->second; - vtxY = mapVtxYrec.find(trkCollId)->second; - vtxZ = mapVtxZrec.find(trkCollId)->second; - // LOGP(info, "\t ---> \t .... \t vtxZrec: {} - collision.posZ(): {}", vtxZrec, collision.posZ()); - int64_t mcCollIdRec = mapRecToMc.find(trkCollId)->second; - // int64_t mcCollId = itMC->second; - // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - mcCollId: {} - bestMCCol: {}", mcCollIdRec, mcCollId, bestMCCol); - if (mapVtxXgen.find(mcCollIdRec) == mapVtxXgen.end()) { - continue; + int particleOrigin = -1; + auto particleMother = particle.template mothers_as().front(); + if (particle.isPhysicalPrimary()) { + particleOrigin = static_cast(McRecTrkType::kPrimary); + } else if (particleMother.isPhysicalPrimary() && particle.getProcess() == TMCProcess::kPDecay) { + particleOrigin = static_cast(McRecTrkType::kSecondary); + if (particleMother.pdgCode() == PDG_t::kK0Short || std::abs(particleMother.pdgCode()) == PDG_t::kLambda0) { + particleOrigin = static_cast(McRecTrkType::kStrangeDecay); + } } - if (mapVtxYgen.find(mcCollIdRec) == mapVtxYgen.end()) { - continue; + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), particle.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/Centrality/hEffRecClosure"), particle.eta(), collision.posZ(), static_cast(particleOrigin), cGen, getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), particle.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(particleOrigin), getOccupancy(collision, eventCuts.occupancyEstimator)); + registryMC.fill(HIST("Tracks/hEtaRes"), particle.eta(), (itrack.eta() - particle.eta()) / particle.eta(), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (mapVtxZgen.find(mcCollIdRec) == mapVtxZgen.end()) { - continue; + } else { + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } else { + registryMC.fill(HIST("Tracks/hEffRec"), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, static_cast(McRecTrkType::kTrkFake), getOccupancy(collision, eventCuts.occupancyEstimator)); } - // vertex resolution - ver 1 - // rec coll vtx - mc associated to orig rec coll (first in time) - deltaXv1 = vtxX - mcCollision.posX(); - deltaYv1 = vtxY - mcCollision.posY(); - deltaZv1 = vtxZ - mcCollision.posZ(); - // vertex resolution - ver 2 - // rec coll vtx - mc associated to orig rec coll (first in time) - deltaXv2 = vtxX - mapVtxXgen.find(mcCollIdRec)->second; - deltaYv2 = vtxY - mapVtxYgen.find(mcCollIdRec)->second; - deltaZv2 = vtxZ - mapVtxZgen.find(mcCollIdRec)->second; } + } + } + } - registry.fill(HIST("TimeAssocMC/VtxStatus"), vtxFlag); - registry.fill(HIST("TimeAssocMC/hVertexResV1"), deltaXv1, deltaYv1, deltaZv1); - registry.fill(HIST("TimeAssocMC/hVertexResV2"), deltaXv2, deltaYv2, deltaZv2); + void processMcEfficiencyBestInclusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) + { + processMcEfficiencyBest(mccollision, collisions, particles, besttracks); + } - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSel)); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestInclusive, "Process tracking efficiency (inclusive, based on BestCollisionsFwd3d)", false); - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxAllTrue)); - } else { - registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxAllFalse)); + void processMcEfficiencyBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks) + { + processMcEfficiencyBest(mccollision, collisions, particles, besttracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyBestCentFT0C, "Process tracking efficiency (in FT0 centrality bins, based on BestCollisionsFwd3d)", false); + + /// @brief process template function to calculate tracking efficiency (indexed as particle-to-MFT-tracks) + template + void processEfficiencyIdx(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& /*tracks*/) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + auto occRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } + for (const auto& collision : collisions) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + for (auto const& particle : partsPerCol) { + if (!isChrgParticle(particle.pdgCode())) { + continue; } - if (vtxFlag == static_cast(VertexStatusMC::kGood)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelGoodVtxTrue)); - registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxTrue)); - } else { - registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxFalse)); + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } + if (collision.mcCollisionId() != particle.mcCollisionId()) { + continue; + } + // MC gen + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); + } } - } - if (vtxFlag == static_cast(VertexStatusMC::kBad)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelGoodVtxBad"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelGoodVtxBad)); - registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxTrue)); - } else { - registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxFalse)); + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); + } } } - - if (ids.size() > 0) { - if (ids.size() == 1) { - if (trkCollId == ids[0]) { // non ambiguous - registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbAll"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbAll)); - if (vtxFlag == static_cast(VertexStatusMC::kGood)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbGoodVtxTrue)); + // MC rec + if (particle.has_mfttracks()) { + auto iscounted = false; + auto ncnt = 0; + auto relatedTracks = particle.template mfttracks_as(); + for (auto const& track : relatedTracks) { + if (!isTrackSelected(track)) { + continue; + } + if (trackCuts.removeFakeByMcMask && (track.mcMask() != trackCuts.selMcMask)) { + continue; + } + ++ncnt; + if constexpr (has_reco_cent) { + if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + iscounted = true; } } - if (vtxFlag == static_cast(VertexStatusMC::kBad)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbGoodVtxBad"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbGoodVtxBad)); + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + if (ncnt > 1) { // secondaries + if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } } - } else if (trkCollId != ids[0]) { - registry.fill(HIST("TimeAssocMC/hVTXkSelAmbAll"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbAll)); - if (vtxFlag == static_cast(VertexStatusMC::kGood)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelAmbGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbGoodVtxTrue)); + } else { + if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); + } + iscounted = true; } } - if (vtxFlag == static_cast(VertexStatusMC::kBad)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelAmbGoodVtxBad"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbGoodVtxBad)); + registryMC.fill(HIST("Tracks/NmftTrkPerPart"), ncnt, occRec); + if (ncnt > 1) { // secondaries + if (track.eta() > trackCuts.minEta && track.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), occRec); } } - } else { // non ambiguous (extra) - registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbSameAll"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbSameAll)); - if (vtxFlag == static_cast(VertexStatusMC::kGood)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbSameGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbSameGoodVtxTrue)); - } + } + } + if (relatedTracks.size() > 1) { // duplicates + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cGen, occRec); + for (auto const& track : relatedTracks) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (vtxFlag == static_cast(VertexStatusMC::kBad)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbSameGoodVtxBad"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbSameGoodVtxBad)); - } + } else { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); + for (auto const& track : relatedTracks) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), occRec); } } - } else { // ambiguous - registry.fill(HIST("TimeAssocMC/hVTXkSelAmbGt1All"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbGt1All)); - if (vtxFlag == static_cast(VertexStatusMC::kGood)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelAmbGt1GoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbGt1GoodVtxTrue)); + } + } else { + // MC FAKES + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } } - if (vtxFlag == static_cast(VertexStatusMC::kBad)) { - if (isTrueVtx) { - registry.fill(HIST("TimeAssocMC/hVTXkSelAmbGt1GoodVtxBad"), deltaXv2, deltaYv2, deltaZv2); - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbGt1GoodVtxBad)); + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); } } } - } else { - registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelOrphanNull)); } } } - LOG(info) << "No MC: " << nNoMC; } - PROCESS_SWITCH(DndetaMFTPbPb, processTimeAssocMC, "process MC: check MFT tracks in compatible collisions (time-associasted)", false); - - void processTimeAssocWithReassocMC(CollsMCExtra const& mcCollisions, - CollisionsWithMCLabels const& collisions, - FiltMftTracks const& /*tracks*/, - BestTracks3dWCollsMC const& besttracks, - aod::McParticles const& /*particles*/, - aod::McCollisionLabels const& labels, - ExtBCs const& bcs) + + void processMcEfficiencyIdxInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks) { - if (bcs.size() == 0) { - return; - } - auto bc = bcs.begin(); - initCCDB(bc); + processEfficiencyIdx(mccollision, collisions, particles, tracks); + } - const auto& nRecoColls = collisions.size(); - LOG(info) << "reconstructed collisions: " << nRecoColls; - const auto& nMcColls = mcCollisions.size(); - LOG(info) << "MC collisions: " << nMcColls; - // LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); - const auto& nLabels = labels.size(); - LOG(info) << "collision labels: " << nLabels; + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxInlusive, "Process tracking efficiency (inclusive, indexed)", false); - std::unordered_map mapRecToMc; - mapRecToMc.reserve(nRecoColls); - std::unordered_map mapMcToRec; - mapMcToRec.reserve(nRecoColls); - // std::unordered_map mapVtxXrec; - mapVtxXrec.reserve(nRecoColls); - // std::unordered_map mapVtxYrec; - mapVtxYrec.reserve(nRecoColls); - // std::unordered_map mapVtxZrec; - mapVtxZrec.reserve(nRecoColls); - - if (nRecoColls <= CintZero) { - return; - } + void processMcEfficiencyIdxCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks) + { + processEfficiencyIdx(mccollision, collisions, particles, tracks); + } - auto maxNcontributors = -1; - auto bestCollIndex = -1; - for (auto const& collision : collisions) { - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - bestCollIndex = collision.globalIndex(); - mapVtxXrec.emplace(collision.globalIndex(), collision.posX()); - mapVtxYrec.emplace(collision.globalIndex(), collision.posY()); - mapVtxZrec.emplace(collision.globalIndex(), collision.posZ()); - mapRecToMc.emplace(collision.globalIndex(), collision.mcCollisionId()); - mapMcToRec.emplace(collision.mcCollisionId(), collision.globalIndex()); - } - } - LOG(info) << "mapRecToMc size: " << mapRecToMc.size(); - LOG(info) << "mapVtxXrec size: " << mapVtxXrec.size(); - - std::unordered_map mapVtxXgen; - mapVtxXgen.reserve(nMcColls); - std::unordered_map mapVtxYgen; - mapVtxYgen.reserve(nMcColls); - std::unordered_map mapVtxZgen; - mapVtxZgen.reserve(nMcColls); + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxCentFT0C, "Process tracking efficiency (in FT0C centrality bins, indexed)", false); - for (const auto& mcCollision : mcCollisions) { - mapVtxXgen.emplace(mcCollision.globalIndex(), mcCollision.posX()); - mapVtxYgen.emplace(mcCollision.globalIndex(), mcCollision.posY()); - mapVtxZgen.emplace(mcCollision.globalIndex(), mcCollision.posZ()); + template + void processEfficiencyIdxBest(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftBestTracksLabeled const& /*atracks*/) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + auto occRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); } - LOG(info) << "mapVtxXgen size: " << mapVtxXgen.size(); - - int nNoMC{0}; for (const auto& collision : collisions) { - int64_t recCollId = collision.globalIndex(); - auto itMC = mapRecToMc.find(recCollId); - if (itMC == mapRecToMc.end()) { - nNoMC++; - LOGP(debug, "collison {} has no MC coll", recCollId); - continue; - } - if (!collision.has_mcCollision()) { + if (!isGoodEvent(collision)) { continue; } - auto mcCollision = collision.mcCollision_as(); - // if (collision.globalIndex() != mcCollision.bestCollisionIndex()) { - // continue; - // } - if (gConf.cfgRemoveSplitVertex && (bestCollIndex != collision.globalIndex())) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } + cRec = getRecoCent(collision); + occRec = getOccupancy(collision, eventCuts.occupancyEstimator); + } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } - if (!isGoodEvent(collision)) { + buildLookupTable(collisions); + + for (const auto& collision : collisions) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { continue; } - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { continue; } - - for (auto const& atrack : besttracks) { - if (!isBestTrackSelected(atrack)) { + auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + for (auto const& particle : partsPerCol) { + if (!isChrgParticle(particle.pdgCode())) { continue; } - // auto itrack = atrack.mfttrack_as(); - // if (!isTrackSelected(itrack)) { - // continue; - // } - if (gConf.cfgUseTrackSel && !isTrackSelected(atrack)) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - float phi = atrack.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { + if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } - auto trkBestCollId = atrack.has_collision() ? atrack.bestCollisionId() : -1; - // if (trkBestCollId != recCollId) { // check if best track is associated to original rec coll - // continue; - // } - - bool isTrueVtx = false; - bool isRecGoodMatch = false; - bool isRecGood = false; - int vtxFlag = static_cast(VertexStatusMC::kNull); - - float vtxXbest = -1.; - float vtxYbest = -1.; - float vtxZbest = -1.; - float deltaXOrigMc = -1.; - float deltaYOrigMc = -1.; - float deltaZOrigMc = -1.; - // dca best rec - dca mc associated to best rec coll - float deltaX = -1.; - float deltaY = -1.; - float deltaZ = -1.; - - if (trkBestCollId >= 0 && atrack.has_mcParticle() && atrack.mcMask() == 0) { - auto itRecToMc = mapRecToMc.find(trkBestCollId); // try mfttrackId ??? - int64_t mcPartId = atrack.mcParticle().mcCollisionId(); - auto const& idCompColl = atrack.compatibleCollIds(); - - // check if rec vertex is available in MC collisions - for (const auto& mcTrkId : mapRecToMc) { - if (mcTrkId.second == mcPartId) { - isTrueVtx = true; - break; - } - } - - // check good rec vertex of corresponding mc coll is available in compatible rec coll - if (!idCompColl.empty()) { - for (auto const& id : idCompColl) { - auto itMcCollId = mapRecToMc.find(id); - if (itMcCollId != mapRecToMc.end()) { - if (itMcCollId->second == mcPartId) { - isRecGoodMatch = true; - break; - } - } - } - } - - // check if there is good or bad collision - if (itRecToMc != mapRecToMc.end()) { - int mcTrkCollId = itRecToMc->second; - if (mcPartId == mcTrkCollId) { // particle.mcCollisionId == collision.mcCollisionId -> good vtx - vtxFlag = static_cast(VertexStatusMC::kGood); - } else { // wrong vtx - vtxFlag = static_cast(VertexStatusMC::kBad); + // MC gen + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), cGen, occRec); } } - - // - // check: vertex resolution of time-to-coll reassoc: pos(rec coll best) - gen (mc coll) - // - // const auto& particle = atrack.mcParticle_as(); - // // // auto collision = atrack.collision_as(); // not in use - // const auto& mcColl = particle.mcCollision_as(); - if (mapVtxXrec.find(trkBestCollId) == mapVtxXrec.end()) { - continue; - } - if (mapVtxYrec.find(trkBestCollId) == mapVtxYrec.end()) { - continue; - } - if (mapVtxZrec.find(trkBestCollId) == mapVtxZrec.end()) { - continue; - } - if (mapRecToMc.find(trkBestCollId) == mapRecToMc.end()) { - continue; - } - vtxXbest = mapVtxXrec.find(trkBestCollId)->second; - vtxYbest = mapVtxYrec.find(trkBestCollId)->second; - vtxZbest = mapVtxZrec.find(trkBestCollId)->second; - // LOGP(info, "\t ---> \t .... \t vtxZrec: {} - collision.posZ(): {}", vtxZrec, collision.posZ()); - int64_t mcCollIdRec = mapRecToMc.find(trkBestCollId)->second; - // int64_t mcCollId = itMC->second; - // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - mcCollId: {} - bestMCCol: {}", mcCollIdRec, mcCollId, bestMCCol); - if (mapVtxXgen.find(mcCollIdRec) == mapVtxXgen.end()) { - continue; - } - if (mapVtxYgen.find(mcCollIdRec) == mapVtxYgen.end()) { - continue; - } - if (mapVtxZgen.find(mcCollIdRec) == mapVtxZgen.end()) { - continue; - } - // vertex resolution: best rec - mc associated to orig rec coll (first in time) - deltaXOrigMc = vtxXbest - mcCollision.posX(); - deltaYOrigMc = vtxYbest - mcCollision.posY(); - deltaZOrigMc = vtxZbest - mcCollision.posZ(); - // vertex resolution: best rec - mc associated to best rec coll - deltaX = vtxXbest - mapVtxXgen.find(mcCollIdRec)->second; - deltaY = vtxYbest - mapVtxYgen.find(mcCollIdRec)->second; - deltaZ = vtxZbest - mapVtxZgen.find(mcCollIdRec)->second; - } // has_mcParticle - - registry.fill(HIST("TimeAssocWithReassocMC/VtxStatus"), vtxFlag); - - hTimeAssocWithReassocMC[0]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[1]->Fill(deltaX, deltaY, deltaZ); - - if (isTrueVtx) { - hTimeAssocWithReassocMC[3]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[12]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxAllTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxAllFalse)); - } - if (isRecGood) { - hTimeAssocWithReassocMC[4]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[13]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodAllTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodAllFalse)); - } - if (isRecGoodMatch) { - hTimeAssocWithReassocMC[5]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[14]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchAllTrue)); } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchAllFalse)); - } - - if (vtxFlag == static_cast(VertexStatusMC::kGood)) { - if (isTrueVtx) { - hTimeAssocWithReassocMC[6]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[15]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxFalse)); - } - if (isRecGood) { - hTimeAssocWithReassocMC[7]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[16]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsGoodVtxTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsGoodVtxFalse)); - } - if (isRecGoodMatch) { - hTimeAssocWithReassocMC[8]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[17]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsGoodVtxTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsGoodVtxFalse)); - } - } - - if (vtxFlag == static_cast(VertexStatusMC::kBad)) { - if (isTrueVtx) { - hTimeAssocWithReassocMC[9]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[18]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxFalse)); - } - if (isRecGood) { - hTimeAssocWithReassocMC[10]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[19]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsBadVtxTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsBadVtxFalse)); - } - if (isRecGoodMatch) { - hTimeAssocWithReassocMC[11]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); - hTimeAssocWithReassocMC[20]->Fill(deltaX, deltaY, deltaZ); - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsBadVtxTrue)); - } else { - registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsBadVtxFalse)); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxAll), occRec); + } } } - } - } - LOG(info) << "No MC: " << nNoMC; - } - PROCESS_SWITCH(DndetaMFTPbPb, processTimeAssocWithReassocMC, "process MC: check MFT tracks in reassociation with compatible collisions", false); - - Partition tracksInAcc = (aod::fwdtrack::eta < trackCuts.maxEta) && (aod::fwdtrack::eta > trackCuts.minEta); - - void processAssocMC(CollisionsWithMCLabels const& collisions, - MftTracksWCollsMC const& tracks, - aod::McParticles const& /*particles*/, - aod::McCollisions const& /*mccollisions*/ - ) - { - const auto& nRecoColls = collisions.size(); - // Generated evets with >= 1 reco collisions - if (nRecoColls > CintZero) { - auto maxNcontributors = -1; - auto bestCollIndex = -1; - for (const auto& collision : collisions) { - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - bestCollIndex = collision.globalIndex(); - } - } - for (const auto& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - // Select collisions with the largest number of contributors - if (gConf.cfgRemoveSplitVertex && (bestCollIndex != collision.globalIndex())) { - continue; - } - if (!collision.has_mcCollision()) { - continue; - } - auto mcCollision = collision.template mcCollision_as(); - - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { - continue; - } - - int nTrk = 0, nFakeTrk = 0, nGoodTrk = 0; - for (const auto& track : tracks) { - uint index = uint(track.collisionId() >= 0); - if (track.has_mcParticle() && track.mcMask() == 0) { - // auto particle = track.mcParticle_as(); - const auto& particle = track.mcParticle(); - auto trkCollId = track.has_collision() ? track.collisionId() : -1; - auto ids = track.compatibleCollIds(); - qaregistry.fill(HIST("TrackToColl/histTrackNumColls"), ids.size()); - qaregistry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kAll)); - - if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { - if (ids.empty()) { - qaregistry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kOrphan)); - } - if (ids.size() == 1 && trkCollId == ids[0]) { - qaregistry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb)); - } + // MC rec + if (particle.has_mfttracks()) { + auto iscounted = false; + auto ncnt = 0; + auto relatedTracks = particle.template mfttracks_as(); + for (auto const& atrack : relatedTracks) { + if (!isBestTrackSelected(atrack)) { + continue; } - if (gConf.cfgRemoveOrphanTracks && ids.empty()) { + if (trackCuts.removeFakeByMcMask && (atrack.mcMask() != trackCuts.selMcMask)) { continue; } - if (gConf.cfgRemoveTrivialAssoc) { - if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { - qaregistry.fill(HIST("TrackToColl/histNonAmbTrackNumColls"), ids.size()); + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { continue; } } - if (gConf.cfgRemoveAmbiguousTracks && (track.compatibleCollIds().size() != 1)) { - continue; - } - nTrk++; - if ((particle.mcCollisionId() != collision.mcCollision().globalIndex())) { - nFakeTrk++; - continue; - } - if (collision.mcCollisionId() == particle.mcCollisionId()) { - nGoodTrk++; - } - if (ids.size() > 0) { - if (ids.size() == 1) { - if (trkCollId != ids[0]) { - qaregistry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kAmb)); - } else { - qaregistry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame)); + ++ncnt; + if constexpr (has_reco_cent) { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); + } + } + } else { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxPrim), occRec); + } + iscounted = true; + } + } + registryMC.fill(HIST("Tracks/NmftTrkPerPart"), ncnt, occRec); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), static_cast(RecIdxTrkType::kRecIdxSec), occRec); } - } else { - qaregistry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1)); } } - - bool isAmbiguous = (ids.size() > 1); - if (isAmbiguous) { - qaregistry.fill(HIST("TrackToColl/histAmbTrackNumColls"), ids.size()); - std::vector ambVtxZ{}; - for (const auto& collIdx : ids) { - const auto& ambColl = collisions.rawIteratorAt(collIdx); - ambVtxZ.push_back(ambColl.posZ()); + } + if (relatedTracks.size() > 1) { // duplicates + if constexpr (has_reco_cent) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), cGen, occRec); + for (auto const& track : relatedTracks) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), getRecoCent(collision), getOccupancy(collision, eventCuts.occupancyEstimator)); } - if (!ambVtxZ.empty()) { - qaregistry.fill(HIST("TrackToColl/histAmbTrackZvtxRMS"), computeRMS(ambVtxZ)); + } else { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxDupl), occRec); + for (auto const& track : relatedTracks) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), track.pt(), track.eta(), static_cast(RecIdxTrkType::kRecIdxDupl), occRec); } } - - float deltaX = -999.f; - float deltaY = -999.f; - float deltaZ = -999.f; - if (index) { - const auto& collision = track.template collision_as(); - const auto& mcCollision = particle.template mcCollision_as(); - deltaX = collision.posX() - mcCollision.posX(); - deltaY = collision.posY() - mcCollision.posY(); - deltaZ = collision.posZ() - mcCollision.posZ(); - if (collision.has_mcCollision() && collision.mcCollisionId() == particle.mcCollisionId()) { - hCollAssoc[index + 1]->Fill(track.pt(), track.eta(), deltaX, deltaY, deltaZ); - } else { - if (isAmbiguous) { - for (const auto& collIdx : ids) { - auto ambColl = collisions.rawIteratorAt(collIdx); - if (ambColl.has_mcCollision() && ambColl.mcCollisionId() == particle.mcCollisionId()) { - hCollAssoc[index + 2]->Fill(track.pt(), track.eta(), deltaX, deltaY, deltaZ); - // hCollAssoc[index + 2]->Fill(track.pt(), track.eta(), ambColl.posX() - mcCollision.posX(), ambColl.posY() - mcCollision.posY(), ambColl.posZ() - mcCollision.posZ()); - break; - } - } - } + } + } else { + // MC FAKES + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), cGen, occRec); } - hCollAssoc[index]->Fill(track.pt(), track.eta(), deltaX, deltaY, deltaZ); } } else { - hCollAssoc[index]->Fill(track.pt(), track.eta(), -999.f, -999.f, -999.f); + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkType::kGenIdxFake), occRec); + } + } } } - float frac = (nTrk > 0) ? static_cast(nGoodTrk) / nTrk : -1.; - qaregistry.fill(HIST("TrackToColl/histFracGoodTracks"), frac); - float fracFake = (nTrk > 0) ? static_cast(nFakeTrk) / nTrk : -1.; - qaregistry.fill(HIST("TrackToColl/histFracTracksFakeMcColl"), fracFake); } } } - PROCESS_SWITCH(DndetaMFTPbPb, processAssocMC, "Process collision-association information, requires extra table from TrackToCollisionAssociation task (fillTableOfCollIdsPerTrack=true)", false); + void processMcEfficiencyIdxBestInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftBestTracksLabeled const& atracks) + { + processEfficiencyIdxBest(mccollision, collisions, particles, atracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestInlusive, "Process tracking efficiency best (inclusive, indexed)", false); - template - void processReAssocMC(CollisionsWithMCLabels const& collisions, - B const& besttracks, - FiltMcMftTracks const& /*tracks*/, - aod::McCollisions const& /*mcCollisions*/, - aod::McParticles const& /*particles*/ - ) + void processMcEfficiencyIdxBestCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftBestTracksLabeled const& atracks) { - const auto& nRecoColls = collisions.size(); - registry.fill(HIST("ReAssocMC/hNReAssocRecoColls"), 1.f, nRecoColls); - - if (nRecoColls > CintZero) { - - mapVtxXrec.clear(); - mapVtxYrec.clear(); - mapVtxZrec.clear(); - mapMcCollIdPerRecColl.clear(); - mapVtxXrec.reserve(collisions.size()); - mapVtxYrec.reserve(collisions.size()); - mapVtxZrec.reserve(collisions.size()); - mapMcCollIdPerRecColl.reserve(collisions.size()); - - auto maxNcontributors = -1; - auto bestCollIndex = -1; - for (auto const& collision : collisions) { - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - bestCollIndex = collision.globalIndex(); - mapVtxXrec.emplace(collision.globalIndex(), collision.posX()); - mapVtxYrec.emplace(collision.globalIndex(), collision.posY()); - mapVtxZrec.emplace(collision.globalIndex(), collision.posZ()); - mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); - } + processEfficiencyIdxBest(mccollision, collisions, particles, atracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestCentFT0C, "Process tracking efficiency best (in FT0C centrality bins, indexed)", false); + + template + void processEfficiencyIdxBestLight(typename MC::iterator const& mcCollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& /*tracks*/, + MftBestTracksLabeled const& /*atracks*/) + { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + auto cRec = CInvalid; + float cGen = -1; + if (eventCuts.useGenMult) { + cGen = mcCollision.multMCFT0C(); + } + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; } + cRec = getRecoCent(collision); + } + if (cGen < 0 && cRec >= 0) { + cGen = cRec; + } - int nNoMC{0}; - for (const auto& collision : collisions) { - registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsAll)); - if (!isGoodEvent(collision)) { - continue; - } - registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSelected)); - if (!collision.has_mcCollision()) { - continue; - } - registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsHasMcColl)); + buildLookupTable(collisions); - int64_t recCollId = collision.globalIndex(); - auto itMC = mapMcCollIdPerRecColl.find(recCollId); - if (itMC == mapMcCollIdPerRecColl.end()) { - nNoMC++; + for (const auto& collision : collisions) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { + continue; + } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + auto partsPerCol = particles.sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + for (auto const& particle : partsPerCol) { + if (!isChrgParticle(particle.pdgCode())) { continue; } - - auto mcColl = collision.template mcCollision_as(); - // Select collisions with the largest number of contributors - if (gConf.cfgRemoveSplitVertex && (bestCollIndex != collision.globalIndex())) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSplitVtxRemoved)); - if (eventCuts.useZVtxCutMC && (std::abs(mcColl.posZ()) >= eventCuts.maxZvtx)) { + if (collision.mcCollisionId() != particle.mcCollisionId()) { continue; } - registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsZVtxCutMC)); - - for (auto const& atrack : besttracks) { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkReAssocAll)); - if (!isBestTrackSelected(atrack)) { - continue; - } - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkBestSel)); - - auto itrack = atrack.template mfttrack_as(); - - if (!isTrackSelected(itrack)) { - continue; - } - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkSel)); - float phi = itrack.phi(); - o2::math_utils::bringTo02Pi(phi); - if (phi < Czero || TwoPI < phi) { - continue; - } - if (!itrack.has_collision()) { - continue; - } - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkHasColl)); - if (gConf.cfgRemoveReassigned) { - if (itrack.collisionId() != atrack.bestCollisionId()) { - continue; + // MC gen + if constexpr (has_reco_cent) { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkLightType::kGenIdxAll), cGen); } - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkReassignedRemoved)); } - - if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == 0) { - - auto particle = itrack.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; + } else { + if (particle.eta() > trackCuts.minEta && particle.eta() < trackCuts.maxEta) { + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxGen"), particle.pt(), particle.eta(), static_cast(GenIdxTrkLightType::kGenIdxAll)); } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + } + } + // MC rec + if (particle.has_mfttracks()) { + auto iscounted = false; + auto ncnt = 0; + auto relatedTracks = particle.template mfttracks_as(); + for (auto const& atrack : relatedTracks) { + if (!isBestTrackSelected(atrack)) { continue; } - auto collision = itrack.template collision_as(); - - float deltaX = -999.f; - float deltaY = -999.f; - float deltaZ = -999.f; - - if (mapVtxXrec.find(atrack.bestCollisionId()) == mapVtxXrec.end()) { - continue; + if (eventCuts.checkCollisionId) { + if (!mapMcCollIdPerRecColl.contains(atrack.bestCollisionId())) { + continue; + } + if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second != particle.mcCollisionId()) { + continue; + } } - if (mapVtxYrec.find(atrack.bestCollisionId()) == mapVtxYrec.end()) { + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { continue; } - if (mapVtxZrec.find(atrack.bestCollisionId()) == mapVtxZrec.end()) { + if (!itrack.has_collision()) { continue; } - if (mapMcCollIdPerRecColl.find(atrack.bestCollisionId()) == mapMcCollIdPerRecColl.end()) { - continue; + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } } - const float vtxXbest = mapVtxXrec.find(atrack.bestCollisionId())->second; - const float vtxYbest = mapVtxYrec.find(atrack.bestCollisionId())->second; - const float vtxZbest = mapVtxZrec.find(atrack.bestCollisionId())->second; - // LOGP(info, "\t ---> \t .... \t vtxZrec: {} - collision.posZ(): {}", vtxZrec, collision.posZ()); - const float mcCollIdRec = mapMcCollIdPerRecColl.find(atrack.bestCollisionId())->second; - // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - bestMCCol: {}", mcCollIdRec, bestMCCol); - - auto vtxXtruth = atrack.mcParticle().mcCollision().posX(); - auto vtxYtruth = atrack.mcParticle().mcCollision().posY(); - auto vtxZtruth = atrack.mcParticle().mcCollision().posZ(); - - deltaX = vtxXbest - vtxXtruth; - deltaY = vtxYbest - vtxYtruth; - deltaZ = vtxZbest - vtxZtruth; - - const auto dcaXtruth(particle.vx() - particle.mcCollision().posX()); - const auto dcaYtruth(particle.vy() - particle.mcCollision().posY()); - const auto dcaZtruth(particle.vz() - particle.mcCollision().posZ()); - auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); - - // check good rec vertex of corresponding mc coll is available in compatible rec coll - auto const& ids = atrack.compatibleCollIds(); - bool isInCoColl = false; - if (atrack.ambDegree() != 0) { - const int mcCollId = atrack.mcParticle().mcCollisionId(); - if (!ids.empty()) { - for (auto const& id : ids) { - auto itMcCollId = mapMcCollIdPerRecColl.find(id); - if (itMcCollId != mapMcCollIdPerRecColl.end()) { - if (itMcCollId->second == mcCollId) { - isInCoColl = true; - break; + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + ++ncnt; + if constexpr (has_reco_cent) { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxPrim), cRec); } + iscounted = true; } } - } - } - - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkHasMcPart)); - - if (ids.size() > 0) { - if (ids.size() == 1) { - if (itrack.collisionId() == ids[0]) { // non ambiguous - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbAll)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbAll)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbAll)]->Fill(particle.pt(), particle.eta(), 0., 0., dcaXYtruth, dcaZtruth); - if (collision.mcCollisionId() == particle.mcCollisionId()) { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbGood)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)]->Fill(particle.pt(), particle.eta(), 0., 0., dcaXYtruth, dcaZtruth); - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbBad)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)]->Fill(particle.pt(), particle.eta(), 0., 0., dcaXYtruth, dcaZtruth); - } - } else if (itrack.collisionId() != ids[0]) { // ambiguous extra - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkAmbAll)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbAll)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbAll)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - if (collision.mcCollisionId() == particle.mcCollisionId()) { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkAmbGood)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbGood)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbGood)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkAmbBad)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbBad)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbBad)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } - } else { // non ambiguous (extra) - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbAllE)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbAllE)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbAllE)]->Fill(particle.pt(), particle.eta(), 0., 0., dcaXYtruth, dcaZtruth); - - if (collision.mcCollisionId() == particle.mcCollisionId()) { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbGoodE)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbGoodE)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbGoodE)]->Fill(particle.pt(), particle.eta(), 0., 0., dcaXYtruth, dcaZtruth); - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbBadE)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbBadE)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbBadE)]->Fill(particle.pt(), particle.eta(), 0., 0., dcaXYtruth, dcaZtruth); + registryMC.fill(HIST("Tracks/Centrality/NmftTrkPerPart"), ncnt, cRec); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/Centrality/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxSec), cRec); } } - } else { // ambiguous - - if (itrack.collisionId() == atrack.bestCollisionId()) { // associated - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssoc)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssoc)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssoc)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - if (collision.has_mcCollision() && mcCollIdRec == particle.mcCollisionId()) { // good coll - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocGood)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGood)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGood)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - if (isInCoColl) { // coll vertex is among compatible colls - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocGoodIsCompTrue)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocGoodIsCompFalse)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocBad)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBad)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBad)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - if (isInCoColl) { // coll vertex is among compatible colls - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocBadIsCompTrue)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocBadIsCompFalse)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } else { + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + if (!iscounted) { // primaries + if (!eventCuts.useZVtxCutMC || std::abs(mcCollision.posZ()) < eventCuts.maxZvtx) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxPrim)); } + iscounted = true; } - } else { // reassociated - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssoc)); - if (collision.has_mcCollision() && mcCollIdRec == particle.mcCollisionId()) { // good coll - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocGood)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGood)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGood)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - if (isInCoColl) { // coll vertex is among compatible colls - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocGoodIsCompTrue)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocGoodIsCompFalse)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocBad)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBad)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBad)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - if (isInCoColl) { // coll vertex is among compatible colls - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocBadIsCompTrue)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } else { - registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocBadIsCompFalse)); - hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)]->Fill(particle.pt(), particle.eta(), deltaX, deltaY, deltaZ); - hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)]->Fill(particle.pt(), particle.eta(), atrack.bestDCAXY(), atrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - } + } + registryMC.fill(HIST("Tracks/NmftTrkPerPart"), ncnt); + if (ncnt > 1) { // secondaries + if (atrack.eta() > trackCuts.minEta && atrack.eta() < trackCuts.maxEta) { + registryMC.fill(HIST("Tracks/hEffIdxRec"), particle.pt(), particle.eta(), atrack.bestDCAXY(), bestDcaZ, static_cast(RecIdxTrkLightType::kRecIdxSec)); } } } @@ -4774,540 +3138,295 @@ struct DndetaMFTPbPb { } } } - mapVtxXrec.clear(); - mapVtxYrec.clear(); - mapVtxZrec.clear(); - mapMcCollIdPerRecColl.clear(); - LOG(info) << "No MC: " << nNoMC; } } - void processReAssoc3dMC(CollisionsWithMCLabels const& collisions, - BestTracks3dWCollsMC const& besttracks, - FiltMcMftTracks const& tracks, - aod::McCollisions const& mcCollisions, - aod::McParticles const& particles) + void processMcEfficiencyIdxBestLightInlusive(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& atracks) + { + processEfficiencyIdxBestLight(mccollision, collisions, particles, tracks, atracks); + } + + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestLightInlusive, "Process tracking efficiency best light (inclusive, indexed)", false); + + void processMcEfficiencyIdxBestLightCentFT0C(CollsMCExtraMult::iterator const& mccollision, + soa::SmallGroups> const& collisions, + ParticlesIdx const& particles, + MftTracksLabeled const& tracks, + MftBestTracksLabeled const& atracks) { - processReAssocMC(collisions, besttracks, tracks, mcCollisions, particles); + processEfficiencyIdxBestLight(mccollision, collisions, particles, tracks, atracks); } - PROCESS_SWITCH(DndetaMFTPbPb, processReAssoc3dMC, "Process re-association information based on BestCollisionsFwd3d table", false); - Preslice compCollPerCol = o2::aod::fwdtrack::collisionId; + PROCESS_SWITCH(DndetaMFTPbPb, processMcEfficiencyIdxBestLightCentFT0C, "Process tracking efficiency best light (in FT0C centrality bins, indexed)", false); - /// @brief process function to check reassociation efficiency - void processReassocEfficiency(CollsMCExtraMult::iterator const& mcCollision, - soa::SmallGroups const& collisions, - MftTracksWCollsMC const& tracks, - aod::McParticles const& particles, - ExtBCs const& bcs) + void processMcSgnEvtLossCentFT0C(CollsMCExtraMult::iterator const& mcCollision, + soa::SmallGroups const& collisions, + aod::McParticles const& particles) { - if (bcs.size() == 0) { + LOGP(debug, "MC col {} has {} reco cols", mcCollision.globalIndex(), collisions.size()); + float multMC{}; + auto perCollMCsample = mcSample->sliceByCached(aod::mcparticle::mcCollisionId, mcCollision.globalIndex(), cache); + auto multMCNParticlesEtaMFT = countPart(perCollMCsample); + if (gConf.useMultMCatMidRapidity) { + multMC = mcCollision.multMCNParticlesEta08(); + } else if (gConf.useMultMCatMFTacc) { + multMC = multMCNParticlesEtaMFT; + } else { + multMC = getMultMC(mcCollision); + } + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenAll)); + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtAll)); + if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { return; } - auto bc = bcs.begin(); - initCCDB(bc); - - // At least one generated primary in MFT acceptance + TVX triggered collisions - if (gConf.cfgUseInelgt0wMFT && !isInelGt0wMft(particles)) { + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtVtxZ)); + if (eventCuts.requireTVX && !(mcCollision.multMCFT0C() > 0 && mcCollision.multMCFT0A() > 0)) { return; } - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtTVX)); + if (gConf.useInelgt0 && !mcCollision.isInelGt0()) { return; } + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtInelGt0)); + if (gConf.useInelgt0wMFT && !isInelGt0wMft(particles)) { + return; + } + registryMC.fill(HIST("Events/hMcEvtLossStatus"), static_cast(McEvtLossStatus::kMcEvtInelGt0wMft)); + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenSel)); // Evt loss den - const auto& nRecoColls = collisions.size(); - registry.fill(HIST("Events/hNReEffColls"), 1.f, nRecoColls); - - // Generated evets with >= 1 reco collisions - if (nRecoColls > CintZero) { - - auto maxNcontributors = -1; - auto bestCollIndex = -1; - auto crec = -1; - for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - bestCollIndex = collision.globalIndex(); - crec = getRecoCent(collision); - } + bool gtZeroColl = false; + float cRec = CInvalid; + for (auto const& collision : collisions) { + if (gConf.useEvtSel && !isGoodEvent(collision)) { + continue; } + if (gConf.rmSplitVertex && collision.globalIndex() != mcCollision.bestCollisionIndex()) { + continue; + } + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenSplit)); + registryMC.fill(HIST("Events/hMultGenVsCentSplit"), getRecoCent(collision), multMC); + cRec = getRecoCent(collision); + gtZeroColl = true; + } - registry.fill(HIST("AmbTracks/hVtxzMCrec"), mcCollision.posZ()); - - for (const auto& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - // Select collisions with the largest number of contributors - if (gConf.cfgRemoveSplitVertex && (bestCollIndex != collision.globalIndex())) { - continue; - } - registry.fill(HIST("Events/Centrality/ReEffStatus"), 1, crec); - if (!collision.has_mcCollision()) { - continue; - } - - std::array dcaInfOrig; - std::array dcaInfo; - double bestDCA[2]; - auto trkPerColl = tracks.sliceBy(compCollPerCol, collision.globalIndex()); - - for (auto const& track : trkPerColl) { - dcaInfOrig[0] = 999.f; // original DCAx from propagation - dcaInfOrig[1] = 999.f; // original DCAy from propagation - dcaInfOrig[2] = 999.f; // original DCAz from propagation - dcaInfo[0] = 999.f; // calcualted DCAxy - dcaInfo[1] = 999.f; // calculated DCAz - same as original - bestDCA[0] = 999.f; // minimal DCAxy - bestDCA[1] = 999.f; // minimal DCAz - - if (!isTrackSelected(track)) { - continue; - } - - auto bestCol = track.has_collision() ? track.collisionId() : -1; - uint index = uint(track.collisionId() >= 0); - auto ids = track.compatibleCollIds(); - bool isAmbiguous = (ids.size() > 1); - - if (gConf.cfgRemoveReassigned) { - if (ids.empty() || (ids.size() == 1 && bestCol == ids[0])) { - continue; - } - } - if (gConf.cfgRemoveOrphanTracks && ids.empty()) { - continue; - } - if (!track.has_mcParticle()) { - continue; - } - auto particle = track.mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (particle.eta() <= trackCuts.minEta || particle.eta() >= trackCuts.maxEta) { - continue; - } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { - continue; - } - int bestMCCol = -1; - o2::track::TrackParCovFwd trackPar = o2::aod::fwdtrackutils::getTrackParCovFwdShift(track, mZShift); - - if (index) { - auto mcCollision = particle.mcCollision_as(); - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; - } - } - - if (isAmbiguous) { - for (const auto& collIdx : track.compatibleCollIds()) { - auto ambColl = collisions.rawIteratorAt(collIdx); - if (!ambColl.has_mcCollision()) { - continue; - } - - trackPar.propagateToDCAhelix(bZ, {ambColl.posX(), ambColl.posY(), ambColl.posZ()}, dcaInfOrig); - - if (gConf.cfgDCAtype == 0) { - dcaInfo[0] = dcaInfOrig[0]; - } else if (gConf.cfgDCAtype == 1) { - dcaInfo[0] = dcaInfOrig[1]; - } else if (gConf.cfgDCAtype == 2) { - dcaInfo[0] = std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + dcaInfOrig[1] * dcaInfOrig[1]); - } - dcaInfo[1] = dcaInfOrig[2]; - - if ((std::abs(dcaInfo[0]) < std::abs(bestDCA[0])) && (std::abs(dcaInfo[1]) < std::abs(bestDCA[1]))) { - bestCol = ambColl.globalIndex(); - bestMCCol = ambColl.mcCollisionId(); - bestDCA[0] = dcaInfo[0]; - bestDCA[1] = dcaInfo[1]; - } - - // LOGP(info, "-> track {}: {}", track.globalIndex(), dcaInfo[0]); - registry.fill(HIST("AmbTracks/DCAXY"), dcaInfo[0]); - registry.fill(HIST("AmbTracks/DCAZ"), dcaInfo[1]); - } - - registry.fill(HIST("AmbTracks/DCAXYBest"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBest"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestGen"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestGenDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - - if (particle.isPhysicalPrimary()) { - registry.fill(HIST("AmbTracks/DCAXYBestPrim"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBestPrim"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestGenPrim"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestPrimDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - } + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 1., cRec); // Evt split den + registryMC.fill(HIST("Events/hMultGenVsCent"), multMC, getMultMC(mcCollision), cRec); + registryMC.fill(HIST("Events/hMultGenVsCentMFTacc"), multMCNParticlesEtaMFT, cRec); - auto mcCollID = particle.mcCollisionId(); - registry.fill(HIST("Events/Centrality/ReEffStatus"), 2, crec); - - if (!track.has_collision()) { - registry.fill(HIST("Events/Centrality/ReEffStatus"), 4, crec); - if (bestMCCol == mcCollID) // correctly assigned to bestCol - { - registry.fill(HIST("Events/Centrality/ReEffStatus"), 6, crec); - registry.fill(HIST("AmbTracks/DCAXYBestTrue"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBestTrue"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestTrue"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestTrueDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - } else { - registry.fill(HIST("AmbTracks/DCAXYBestFalse"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBestFalse"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestFalse"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestFalseDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - } - } else if (track.collisionId() != bestCol) { // reassgined - auto collOrig = collisions.rawIteratorAt(track.collisionId()); - registry.fill(HIST("Events/Centrality/ReEffStatus"), 3, crec); - if (bestMCCol == mcCollID) // correctly reassigned - { - registry.fill(HIST("Events/Centrality/ReEffStatus"), 6, crec); - registry.fill(HIST("AmbTracks/DCAXYBestTrue"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBestTrue"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestTrue"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestTrueDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - } else { // uncorrectly reassigned - registry.fill(HIST("AmbTracks/DCAXYBestFalse"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBestFalse"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestFalse"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestFalseDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - } - if (collOrig.mcCollisionId() == mcCollID) { // initially correctly assigned - registry.fill(HIST("Events/Centrality/ReEffStatus"), 5, crec); - registry.fill(HIST("AmbTracks/DCAXYBestTrueOrigReAssoc"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBestTrueOrigReAssoc"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestTrueOrigReAssoc"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestTrueOrigReAssocDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - } - } else { // not reassigned - the track has a collision and track.collisionId() == bestCol - if (track.collisionId() != bestCol) { - registry.fill(HIST("Events/Centrality/ReEffStatus"), 7, crec); - // LOGP(info, "-> track id {}: bestCollid {}", track.collisionId(), bestCol); - } - registry.fill(HIST("Events/Centrality/ReEffStatus"), 8, crec); - if (bestMCCol == mcCollID) // correctly assigned - { - registry.fill(HIST("Events/Centrality/ReEffStatus"), 9, crec); - registry.fill(HIST("AmbTracks/DCAXYBestTrueOrigAssoc"), bestDCA[0]); - registry.fill(HIST("AmbTracks/DCAZBestTrueOrigAssoc"), bestDCA[1]); - registry.fill(HIST("AmbTracks/Centrality/THnDCAxyBestTrueOrigAssoc"), particle.pt(), particle.eta(), mcCollision.posZ(), bestDCA[0], bestDCA[1], crec); - registry.fill(HIST("AmbTracks/BestTrueOrigAssocDxyz"), collision.posX() - mcCollision.posX(), collision.posY() - mcCollision.posY(), collision.posZ() - mcCollision.posZ(), bestDCA[0], bestDCA[1]); - } - } - } - } - } + if (gtZeroColl) { + if (gConf.useInelgt0wMFT && !isInelGt0wMft(particles)) { + return; + } + registryMC.fill(HIST("Events/hNchGen"), multMC, static_cast(EvtLossType::kGenRecEvt)); // Evt loss num + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 2., cRec); // Evt split num + registryMC.fill(HIST("Events/hMultGenVsCentRecEvt"), multMC, getMultMC(mcCollision), cRec); + registryMC.fill(HIST("Events/hMultGenVsCentRecEvtMFTacc"), multMCNParticlesEtaMFT, cRec); + } + if (collisions.size() == 0) { + registryMC.fill(HIST("Events/Centrality/hEvtSigLossStatus"), 3., cRec); + } + for (auto const& particle : particles) { + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.useParticleSel && !isParticleSelected(particle)) { + continue; + } + registryMC.fill(HIST("Tracks/hNchGenVsEta"), multMC, particle.eta()); // Sgn loss den + if (gtZeroColl) { + registryMC.fill(HIST("Tracks/hNchGenVsEtaRecEvt"), multMC, particle.eta()); // Sgn loss num } } } - PROCESS_SWITCH(DndetaMFTPbPb, processReassocEfficiency, "Process collision-reassociation efficiency based on track propagation DCA 3D (in FT0C centrality bins)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcSgnEvtLossCentFT0C, "Process Signal/event loss based on MC (in FT0C centrality bins)", false); - Preslice mftTrkCompCollperCol = o2::aod::fwdtrack::collisionId; - - /// @brief process template function for DCA MC checks - template - void processSecondariesMC(typename soa::Join const& collisions, - MftTracksWCollsMC const& tracks, - MC const& mcCollisions, - aod::McParticles const& particles, - ExtBCs const& bcs) + void processMcReassocDCA(CollsGenCentFT0CExtra const& collisions, + CollsMCExtra const& mcCollisions, + aod::McParticles const& particles, + MftBestTracksLabeled const& besttracks, + MftTracksLabeled const& /*tracks*/) { - registry.fill(HIST("Events/hNGenRecColls"), 1.f, collisions.size()); - registry.fill(HIST("Events/hNGenRecColls"), 2.f, mcCollisions.size()); + createMCIds(mcCollisions, collisions, particles); - if (bcs.size() == 0) { - return; - } - auto bc = bcs.begin(); - initCCDB(bc); + int nNoMC{0}; + for (const auto& collision : collisions) { + auto crec = getRecoCent(collision); + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 2., crec); + if (gConf.useEvtSel && !isGoodEvent(collision)) { + continue; + } + if (!collision.has_mcCollision()) { + continue; + } - // Generated evets with >= 1 reco collisions - const auto& nRecoColls = collisions.size(); - if (nRecoColls > CintZero) { - auto maxNcontributors = -1; - auto bestCollIndex = -1; - auto crec = -1; - for (auto const& collision : collisions) { - if (!isGoodEvent(collision)) { - continue; - } - if (maxNcontributors < collision.numContrib()) { - maxNcontributors = collision.numContrib(); - bestCollIndex = collision.globalIndex(); - if constexpr (has_reco_cent) { - crec = getRecoCent(collision); - } - } + int64_t recCollId = collision.globalIndex(); + auto itMC = mapMcCollIdPerRecColl.find(recCollId); + if (itMC == mapMcCollIdPerRecColl.end()) { + nNoMC++; + continue; + } + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 3., crec); + if (gConf.rmSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + continue; } + auto mcColl = collision.mcCollision_as(); + if (eventCuts.useZVtxCutMC && (std::abs(mcColl.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + registryMC.fill(HIST("Events/Centrality/EvtGenRecReassoc"), 4., crec); - for (const auto& collision : collisions) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kAllRecColl), crec); - } else { - qaregistry.fill(HIST("Events/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kAllRecColl)); - } - // At least one generated primary in MFT acceptance + TVX triggered collisions - if (gConf.cfgUseInelgt0wMFT && !isInelGt0wMft(particles)) { - return; - } - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kIsInelGt0wMft), crec); - } else { - qaregistry.fill(HIST("Events/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kIsInelGt0wMft)); - } - if (!isGoodEvent(collision)) { + auto perCollisionASample = besttracks.sliceBy(perColBestTrks, collision.globalIndex()); + for (auto const& atrack : perCollisionASample) { + if (!isBestTrackSelected(atrack)) { continue; } - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kEvtSel), crec); - } else { - qaregistry.fill(HIST("Events/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kEvtSel)); - } - // Select collisions with the largest number of contributors - if (gConf.cfgRemoveSplitVertex && (bestCollIndex != collision.globalIndex())) { + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + + if (!isTrackSelected(itrack)) { continue; } - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kBestCollIdx), crec); - } else { - qaregistry.fill(HIST("Events/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kBestCollIdx)); - } - if (!collision.has_mcCollision()) { + if (!itrack.has_collision()) { continue; } - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("Events/Centrality/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kIsMcColl), crec); - } else { - qaregistry.fill(HIST("Events/hTrackToCollEvtType"), static_cast(TrackToCollEvtType::kIsMcColl)); + if (gConf.rmReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } } + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestRec"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), atrack.bestDCAZ(), crec, crec); - auto trkPerColl = tracks.sliceBy(mftTrkCompCollperCol, collision.globalIndex()); - for (auto const& track : trkPerColl) { - if (!isTrackSelected(track)) { + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && (!trackCuts.removeFakeByMcMask || (itrack.mcMask() == trackCuts.selMcMask))) { + auto particle = itrack.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { continue; } - if (!track.has_collision()) { + if (gConf.useParticleSel && !isParticleSelected(particle)) { continue; } - auto trkCollId = track.has_collision() ? track.collisionId() : -1; - auto ids = track.compatibleCollIds(); - bool isAmbiguous = (ids.size() > 1); - - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("TrkCompColls/Centrality/hAmbTrackType"), static_cast(AmbTrkType::kAll), crec); - } else { - qaregistry.fill(HIST("TrkCompColls/hAmbTrackType"), static_cast(AmbTrkType::kAll)); - } - if (ids.empty()) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("TrkCompColls/Centrality/hAmbTrackType"), static_cast(AmbTrkType::kOrphan), crec); - } else { - qaregistry.fill(HIST("TrkCompColls/hAmbTrackType"), static_cast(AmbTrkType::kOrphan)); - } - } - if (ids.size() > 0) { - if (ids.size() == 1) { - if (trkCollId == ids[0]) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("TrkCompColls/Centrality/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb), crec); - } else { - qaregistry.fill(HIST("TrkCompColls/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb)); - } - } else if (trkCollId != ids[0]) { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("TrkCompColls/Centrality/hAmbTrackType"), static_cast(AmbTrkType::kAmb), crec); - } else { - qaregistry.fill(HIST("TrkCompColls/hAmbTrackType"), static_cast(AmbTrkType::kAmb)); - } - } else { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("TrkCompColls/Centrality/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame), crec); - } else { - qaregistry.fill(HIST("TrkCompColls/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame)); - } - } - } else { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("TrkCompColls/Centrality/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1), crec); - } else { - qaregistry.fill(HIST("TrkCompColls/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1)); - } - } - } else { - if constexpr (has_reco_cent) { - qaregistry.fill(HIST("TrkCompColls/Centrality/hAmbTrackType"), static_cast(AmbTrkType::kOrphanNull), crec); - } else { - qaregistry.fill(HIST("TrkCompColls/hAmbTrackType"), static_cast(AmbTrkType::kOrphanNull)); - } - } - - if (gConf.cfgRemoveTrivialAssoc) { - if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { - qaregistry.fill(HIST("TrkCompColls/Centrality/histNonAmbTrackNumColls"), ids.size()); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { continue; } } - - if (isAmbiguous) { - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnRecAmb"), track.pt(), track.eta(), collision.posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnRecAmb"), track.pt(), track.eta(), collision.posZ()); - } - } else { - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnRec"), track.pt(), track.eta(), collision.posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnRec"), track.pt(), track.eta(), collision.posZ()); - } - if (trkCollId == ids[0]) { - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnRecNonAmb"), track.pt(), track.eta(), collision.posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnRecNonAmb"), track.pt(), track.eta(), collision.posZ()); - } - } - if (trkCollId != ids[0]) { - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnRecAmbRest"), track.pt(), track.eta(), collision.posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnRecAmbRest"), track.pt(), track.eta(), collision.posZ()); - } - } - } - - // remove orphan tracks - if (ids.empty()) { - continue; - } - // remove fake tracks - if (!track.has_mcParticle() || track.mcMask() != 0) { + const int bestRecColl = atrack.bestCollisionId(); + auto itMapMcCollIdPerRecColl = mapMcCollIdPerRecColl.find(bestRecColl); + if (itMapMcCollIdPerRecColl == mapMcCollIdPerRecColl.end()) { continue; } - // assigned collision index - uint index = uint(track.collisionId() >= 0); - auto particle = track.template mcParticle_as(); - if (!isChrgParticle(particle.pdgCode())) { - continue; - } - if (particle.eta() <= trackCuts.minEta || particle.eta() >= trackCuts.maxEta) { + const float mcCollIdRec = itMapMcCollIdPerRecColl->second; + // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - bestMCCol: {}", mcCollIdRec, bestMCCol); + const auto dcaXtruth(particle.vx() - mcColl.posX()); + const auto dcaYtruth(particle.vy() - mcColl.posY()); + const auto dcaZtruth(particle.vz() - mcColl.posZ()); + const auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); + + const auto mcId = particle.mcCollisionId(); + if (!mcIds.contains(mcId)) { continue; } - if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + auto itRefCent = refCentByMcId.find(mcId); + if (itRefCent == refCentByMcId.end()) { continue; } - - if (index) { - auto mcCollision = particle.template mcCollision_as(); - if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { - continue; - } - if (eventCuts.useZDiffCut) { - if (std::abs(collision.posZ() - mcCollision.posZ()) > eventCuts.maxZvtxDiff) { - continue; - } - } - - if (collision.has_mcCollision() && collision.mcCollisionId() == particle.mcCollisionId()) { - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenSec"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenSec"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); - } + const float refCent = itRefCent->second; + // auto itGenCent = genCentByMcId.find(mcId); + // if (itGenCent == genCentByMcId.end()) { + // continue; + // } + // const int genCent = itGenCent->second; + + if (atrack.ambDegree() > CintZero) { // all tracks + if (collision.has_mcCollision() && collision.mcCollisionId() == mcId) { // good coll + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSec"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSec"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenSecWeak"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenSecWeak"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); - } + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeak"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); } else { // Particles from the material - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenSecMat"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenSecMat"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); - } + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMat"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); } } else { // Primaries - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenPrim"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenPrim"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrim"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrim"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } else { // Wrong collision + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } + if (atrack.ambDegree() == CintOne) { // non-ambiguous + if (collision.has_mcCollision() && collision.mcCollisionId() == mcId) { // good coll + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } else { // Particles from the material + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } else { // Wrong collision + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimNonAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); } } - } else { - if (isAmbiguous) { - for (const auto& collIdx : track.compatibleCollIds()) { - auto ambColl = collisions.rawIteratorAt(collIdx); - if (ambColl.has_mcCollision() && ambColl.mcCollisionId() == particle.mcCollisionId()) { - if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenSecAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenSecAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); - } - if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenSecWeakAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenSecWeakAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); - } - } else { // Particles from the material - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenSecMatAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenSecMatAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); - } - } - } else { // Primaries - if constexpr (has_reco_cent) { - registry.fill(HIST("Tracks/Centrality/THnGenPrimAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ(), crec); - } else { - registry.fill(HIST("Tracks/THnGenPrimAmb"), particle.pt(), particle.eta(), particle.mcCollision().posZ()); - } - } - break; + } else { // ambiguous + if (collision.has_mcCollision() && mcCollIdRec == mcId) { // good coll + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } else { // Particles from the material + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimAmb"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimAmb"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + } + } else { // wrong collision + if (!particle.isPhysicalPrimary()) { // Secondaries (weak decays and material) + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthSecAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); + if (particle.getProcess() == TMCProcess::kPDecay) { // Particles from decay + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecWeakAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + } else { // Particles from the material + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenSecMatAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); } + } else { // Primaries + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenPrimAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestGenTruthPrimAmbWrongColl"), particle.pt(), particle.eta(), mcColl.posZ(), dcaXYtruth, dcaZtruth, crec, refCent); } } } + } else { // no MC particle + LOGP(debug, "No MC particle for ambiguous itrack, skip..."); + registryMC.fill(HIST("Tracks/Centrality/THnDCAxyBestRecFake"), itrack.pt(), itrack.eta(), collision.posZ(), atrack.bestDCAXY(), bestDcaZ, crec, refCent); } } } } + LOG(info) << "No MC: " << nNoMC; } - void processSecondariesMCInlcusive(soa::Join const& collisions, - MftTracksWCollsMC const& tracks, - aod::McCollisions const& mccollisions, - aod::McParticles const& particles, - ExtBCs const& bcs) - { - processSecondariesMC(collisions, tracks, mccollisions, particles, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processSecondariesMCInlcusive, "Process secondaries checks (Inclusive)", false); - - void processSecondariesMCCentFT0C(soa::Join const& collisions, - MftTracksWCollsMC const& tracks, - aod::McCollisions const& mccollisions, - aod::McParticles const& particles, - ExtBCs const& bcs) - { - processSecondariesMC(collisions, tracks, mccollisions, particles, bcs); - } - - PROCESS_SWITCH(DndetaMFTPbPb, processSecondariesMCCentFT0C, "Process secondaries checks (in FT0C centrality bins)", false); + PROCESS_SWITCH(DndetaMFTPbPb, processMcReassocDCA, "Process MC DCA checks using re-association information based on BestCollisionsFwd3d table (in FT0C centrality bins)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGMM/Mult/Tasks/mftReassociationValidation.cxx b/PWGMM/Mult/Tasks/mftReassociationValidation.cxx index 5d5e2f2b1bb..65cb143aa0a 100644 --- a/PWGMM/Mult/Tasks/mftReassociationValidation.cxx +++ b/PWGMM/Mult/Tasks/mftReassociationValidation.cxx @@ -39,12 +39,14 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -117,6 +119,7 @@ enum SpecificTrackSelectionStep { AfterPtCut, AfterDcaXYCut, AfterDcaZCut, + AfterChi2OverNdfCut, IsCATrack, IsLTFTrack, HasMcParticle, @@ -219,11 +222,34 @@ enum MultiplicityEstimators { MultFT0M }; +enum TrackingAlgorithmStep { + IsLTFAlgorithm = 0, + IsCAAlgorithm, + NTrackingAlgorithmSteps +}; + +enum McStatus { + McMatched = 0, + NotMcMatched, + CollisionAmongTheCompatibleIds, + NotCollisionAmongTheCompatibleIds +}; + +enum ReassociationMethod { + TwoDimensional = 0, + ThreeDimensional, + NReassociationMethods +}; + static constexpr std::string_view WhatDataType[] = {"Data/", "MC/"}; static constexpr std::string_view WhatMultiplicityEstimator[] = {"multNTracksPV", "multNumContrib", "multFT0C", "multFT0M"}; +static constexpr std::string_view WhatMcStatus[] = {"McMatched/", "NotMcMatched/", "CollisionAmongTheCompatibleIds/", "NotCollisionAmongTheCompatibleIds/"}; +static constexpr std::string_view WhatReassociationMethod[] = {"2D/", "3D/"}; std::unordered_map recoVtxX; std::unordered_map recoVtxY; std::unordered_map recoVtxZ; +std::unordered_map recoMultiplicity; +std::unordered_map recoNumContrib; std::unordered_map recoMcCollisionId; std::unordered_map recoMcCollBestCollisionIndex; @@ -240,11 +266,15 @@ struct MftReassociationValidation { struct : ConfigurableGroup { std::string prefix = "ConfigTask_group"; Configurable centralityBinsForMc{"centralityBinsForMc", false, "falsce = OFF, true = ON for data like multiplicity/centrality bins for MC steps"}; - Configurable keepOnlyPhysicalPrimary{"keepOnlyPhysicalPrimary", true, "Keep only physical primary particles"}; - Configurable keepOnlySecondaries{"keepOnlySecondaries", false, "Keep only secondary particles"}; Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", true, "flag to apply z shift from CCDB"}; Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; Configurable cfgManualZShift{"cfgManualZShift", 0.0f, "manual z-shift for propagation of global muon to PV"}; + Configurable fillGeneralHistograms{"fillGeneralHistograms", true, "Fill general reassociation efficiencyhistograms"}; + Configurable fillSpecificHistograms{"fillSpecificHistograms", true, "Fill specific histograms (DCA, DeltaPos)"}; + Configurable fillParametersHistograms{"fillParametersHistograms", true, "Fill parameters histograms"}; + Configurable fillDcaTemplateFit{"fillDcaTemplateFit", true, "Fill DCA template histograms for primary/secondary fraction fit"}; + Configurable keepOnlyPhysicalPrimary{"keepOnlyPhysicalPrimary", true, "Keep only physical primary particles"}; + Configurable keepOnlySecondaries{"keepOnlySecondaries", false, "Keep only secondary particles"}; Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; } configTask; @@ -280,12 +310,11 @@ struct MftReassociationValidation { Configurable cutOrphanTracksExplicitly{"cutOrphanTracksExplicitly", false, "if true, cut orphan tracks explicitly"}; Configurable cutOnDcaXY{"cutOnDcaXY", false, "if true, cut on DCA XY"}; Configurable cutOnDcaZ{"cutOnDcaZ", false, "if true, cut on DCA Z"}; - Configurable dcaXYMax{"dcaXYMax", 2.0f, "Maximum value for DCA XY"}; - Configurable dcaZMax{"dcaZMax", 10.0f, "Maximum value for DCA Z"}; Configurable etaMftTrackMax{"etaMftTrackMax", -2.4f, "Maximum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMin{"etaMftTrackMin", -3.36f, "Minimum value for the eta of MFT tracks when used in cut function"}; Configurable etaMftTrackMaxFilter{"etaMftTrackMaxFilter", -2.0f, "Maximum value for the eta of MFT tracks when used in filter"}; Configurable etaMftTrackMinFilter{"etaMftTrackMinFilter", -3.9f, "Minimum value for the eta of MFT tracks when used in filter"}; + Configurable maxChi2OverNdf{"maxChi2OverNdf", 1000.f, "maximum chi2/ndf for MFT tracks"}; Configurable mftMaxDCAxy{"mftMaxDCAxy", 2.0f, "Cut on dcaXY for MFT tracks"}; Configurable mftMaxDCAz{"mftMaxDCAz", 2.0f, "Cut on dcaZ for MFT tracks"}; Configurable nClustersMftTrack{"nClustersMftTrack", 5, "Minimum number of clusters for the reconstruction of MFT tracks"}; @@ -294,6 +323,7 @@ struct MftReassociationValidation { Configurable useMftPtCut{"useMftPtCut", false, "if true, use the Mft pt function cut"}; Configurable useOnlyCATracks{"useOnlyCATracks", false, "if true, use strictly MFT tracks reconstructed with CA algo."}; Configurable useOnlyLTFTracks{"useOnlyLTFTracks", false, "if true, use strictly MFT tracks reconstructed with LTF algo."}; + Configurable useMftChi2OverNdfCut{"useMftChi2OverNdfCut", false, "use mft track chi2/ndf cut"}; } configMft; float mZShift = 0; // z-vertex shift @@ -304,22 +334,29 @@ struct MftReassociationValidation { Service ccdb; o2::ccdb::CcdbApi ccdbApi; o2::parameters::GRPMagField* grpmag = nullptr; - RCTFlagsChecker rctChecker; - RCTFlagsChecker correlationAnalysisRctChecker{kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad}; - std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffAmbiguousTracks; - std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNonAmbiguousTracks; + RCTFlagsChecker rctChecker{kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad}; + RCTFlagsChecker correlationAnalysisRctChecker{kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}; + bool areCorrectionsLoaded = false; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffAmbiguousTracks2D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNonAmbiguousTracks2D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNotMatchedTracks2D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaAmbiguousTracks2D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNonAmbiguousTracks2D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNotMatchedTracks2D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffAmbiguousTracks3D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNonAmbiguousTracks3D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNotMatchedTracks3D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaAmbiguousTracks3D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNonAmbiguousTracks3D; + std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNotMatchedTracks3D; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiff2dReassociatedTracks; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNot2dReassociatedTracks; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiff3dReassociatedTracks; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNot3dReassociatedTracks; - std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hZVtxDiffNotMatchedTracks; - std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaAmbiguousTracks; - std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNonAmbiguousTracks; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDca2dReassociatedTracks; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNot2dReassociatedTracks; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDca3dReassociatedTracks; std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNot3dReassociatedTracks; - std::array, MatchedToTrueCollisionStep::NMatchedToTrueCollisionSteps> hDcaNotMatchedTracks; // ========================= // using declarations : DATA @@ -335,10 +372,6 @@ struct MftReassociationValidation { using FilteredCollisionsWSelMultMcLabels = soa::Filtered>; using FilteredMftTracksWCollsMcLabels = soa::Filtered>; - // using MftReasso3dTracksWCollsMcLabels = soa::Join; - // using MftReasso2dTracksWCollsMcLabels = soa::Join; - // using MftReasso2dTracksWCollsMcLabels = soa::Join; - // using MftReasso3dTracksWCollsMcLabels = soa::Join; using FilteredMcParticles = soa::Filtered; // ========================= @@ -351,8 +384,6 @@ struct MftReassociationValidation { Filter mftTrackEtaFilter = ((aod::fwdtrack::eta < configMft.etaMftTrackMaxFilter) && (aod::fwdtrack::eta > configMft.etaMftTrackMinFilter)); Filter mftTrackCollisionIdFilter = (aod::fwdtrack::bestCollisionId >= 0); - // Filter mftTrackDcaXYFilter = (nabs(aod::fwdtrack::bestDCAXY) < configMft.mftMaxDCAxy); - // Filter mftTrackDcaZFilter = (nabs(aod::fwdtrack::bestDCAZ) < configMft.mftMaxDCAz); // ========================= // Filters & partitions : MONTE-CARLO @@ -371,34 +402,37 @@ struct MftReassociationValidation { struct : ConfigurableGroup { std::string prefix = "ConfigAxis_group"; ConfigurableAxis axisEta{"axisEta", {48, -4, -2}, "eta axis for MFT histograms"}; - ConfigurableAxis axisDcaX{"axisDcaX", {800, -1., 1.}, "DCAx binning (cm)"}; - ConfigurableAxis axisDcaY{"axisDcaY", {800, -1., 1.}, "DCAy binning (cm)"}; - ConfigurableAxis axisDcaZ{"axisDcaZ", {800, -1., 1.}, "DCAz binning (cm)"}; + ConfigurableAxis axisDcaX{"axisDcaX", {500, -0.1, 2.1}, "DCAx binning (cm)"}; + ConfigurableAxis axisDcaY{"axisDcaY", {500, -0.1, 2.1}, "DCAy binning (cm)"}; + ConfigurableAxis axisDcaZ{"axisDcaZ", {500, -1., 1.}, "DCAz binning (cm)"}; + ConfigurableAxis axisDcaXYfit{"axisDcaXYfit", {500, 0., 2.}, "DCAxy binning (cm) for the template fit"}; + ConfigurableAxis axisDcaZfit{"axisDcaZfit", {500, -1., 1.}, "DCAz binning (cm) for the template fit"}; ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}, "multiplicity axis for histograms"}; ConfigurableAxis axisPhi{"axisPhi", {72, 0, TwoPI}, "phi axis for histograms"}; ConfigurableAxis axisPt{"axisPt", {72, 0, 36}, "pt axis for histograms"}; ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {1, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisPosX{"axisPosX", {500, -1, 1}, "vertex axis for histograms"}; + ConfigurableAxis axisPosZ{"axisPosZ", {500, -30, 30}, "vertex axis for histograms"}; } configAxis; HistogramRegistry registry{"registry"}; - template + template void addMftHistograms() { - registry.add(Form("%shAmbiguityOfMftTracks", WhatDataType[DataType].data()), "hAmbiguityOfMftTracks", {HistType::kTH1D, {{MftTrackAmbiguityStep::NMftAmbiguitySteps, -0.5, +MftTrackAmbiguityStep::NMftAmbiguitySteps - 0.5}}}); + registry.add(Form("%s%shAmbiguityOfMftTracks", WhatDataType[DataType].data(), WhatReassociationMethod[ReassociationMethod].data()), "hAmbiguityOfMftTracks", {HistType::kTH1D, {{MftTrackAmbiguityStep::NMftAmbiguitySteps, -0.5, +MftTrackAmbiguityStep::NMftAmbiguitySteps - 0.5}}}); std::string labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NMftAmbiguitySteps]; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AllMftTracks] = "all MFT tracks"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::AfterTrackSelection] = "MFT tracks after selection"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NumberOfAmbiguousTracks] = "how much tracks are ambigous"; labelsAmbiguityOfMftTracks[MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks] = "how much tracks are non-ambiguous"; - registry.get(HIST(WhatDataType[DataType]) + HIST("hAmbiguityOfMftTracks"))->SetMinimum(0); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hAmbiguityOfMftTracks"))->SetMinimum(0); for (int iBin = 0; iBin < MftTrackAmbiguityStep::NMftAmbiguitySteps; iBin++) { - registry.get(HIST(WhatDataType[DataType]) + HIST("hAmbiguityOfMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsAmbiguityOfMftTracks[iBin].data()); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hAmbiguityOfMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsAmbiguityOfMftTracks[iBin].data()); } - registry.add(Form("%shMftTracksSelection", WhatDataType[DataType].data()), "hMftTracksSelection", {HistType::kTH1D, {{MftTrackSelectionStep::NMftTrackSelectionSteps, -0.5, +MftTrackSelectionStep::NMftTrackSelectionSteps - 0.5}}}); + registry.add(Form("%s%shMftTracksSelection", WhatDataType[DataType].data(), WhatReassociationMethod[ReassociationMethod].data()), "hMftTracksSelection", {HistType::kTH1D, {{MftTrackSelectionStep::NMftTrackSelectionSteps, -0.5, +MftTrackSelectionStep::NMftTrackSelectionSteps - 0.5}}}); std::string labelsMftTracksSelection[MftTrackSelectionStep::NMftTrackSelectionSteps]; labelsMftTracksSelection[MftTrackSelectionStep::NoSelection] = "all MFT tracks"; labelsMftTracksSelection[MftTrackSelectionStep::Eta] = "MFT tracks after eta selection"; @@ -406,180 +440,301 @@ struct MftReassociationValidation { labelsMftTracksSelection[MftTrackSelectionStep::Pt] = "MFT tracks after pT selection"; labelsMftTracksSelection[MftTrackSelectionStep::IsCA] = "MFT tracks reconstructed with CA"; labelsMftTracksSelection[MftTrackSelectionStep::IsLTF] = "MFT tracks reconstructed with LTF"; - registry.get(HIST(WhatDataType[DataType]) + HIST("hMftTracksSelection"))->SetMinimum(0); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hMftTracksSelection"))->SetMinimum(0); for (int iBin = 0; iBin < MftTrackSelectionStep::NMftTrackSelectionSteps; iBin++) { - registry.get(HIST(WhatDataType[DataType]) + HIST("hMftTracksSelection"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftTracksSelection[iBin].data()); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hMftTracksSelection"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftTracksSelection[iBin].data()); } - registry.add(Form("%shReassociation2dMftTracks", WhatDataType[DataType].data()), "hReassociation2dMftTracks", {HistType::kTH1D, {{Reassociation2dMftTracks::NReassociation2dMftTracksSteps, -0.5, +Reassociation2dMftTracks::NReassociation2dMftTracksSteps - 0.5}}}); + registry.add(Form("%s%shReassociation2dMftTracks", WhatDataType[DataType].data(), WhatReassociationMethod[ReassociationMethod].data()), "hReassociation2dMftTracks", {HistType::kTH1D, {{Reassociation2dMftTracks::NReassociation2dMftTracksSteps, -0.5, +Reassociation2dMftTracks::NReassociation2dMftTracksSteps - 0.5}}}); std::string labelsReassociation2dMftTracks[Reassociation2dMftTracks::NReassociation2dMftTracksSteps]; labelsReassociation2dMftTracks[Reassociation2dMftTracks::AllAmbiguousTracksAfterTrackSelectionsFor2d] = "Ambiguous MFT tracks after track selection"; labelsReassociation2dMftTracks[Reassociation2dMftTracks::NotReassociated2dMftTracks] = "Not reassociated MFT tracks by DCAxy method"; labelsReassociation2dMftTracks[Reassociation2dMftTracks::Reassociated2dMftTracks] = "Reassociated MFT tracks by DCAxy method"; - registry.get(HIST(WhatDataType[DataType]) + HIST("hReassociation2dMftTracks"))->SetMinimum(0); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hReassociation2dMftTracks"))->SetMinimum(0); for (int iBin = 0; iBin < Reassociation2dMftTracks::NReassociation2dMftTracksSteps; iBin++) { - registry.get(HIST(WhatDataType[DataType]) + HIST("hReassociation2dMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsReassociation2dMftTracks[iBin].data()); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hReassociation2dMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsReassociation2dMftTracks[iBin].data()); } - registry.add(Form("%shReassociation3dMftTracks", WhatDataType[DataType].data()), "hReassociation3dMftTracks", {HistType::kTH1D, {{Reassociation3dMftTracks::NReassociation3dMftTracksSteps, -0.5, +Reassociation3dMftTracks::NReassociation3dMftTracksSteps - 0.5}}}); + registry.add(Form("%s%shReassociation3dMftTracks", WhatDataType[DataType].data(), WhatReassociationMethod[ReassociationMethod].data()), "hReassociation3dMftTracks", {HistType::kTH1D, {{Reassociation3dMftTracks::NReassociation3dMftTracksSteps, -0.5, +Reassociation3dMftTracks::NReassociation3dMftTracksSteps - 0.5}}}); std::string labelsReassociation3dMftTracks[Reassociation3dMftTracks::NReassociation3dMftTracksSteps]; labelsReassociation3dMftTracks[Reassociation3dMftTracks::AllAmbiguousTracksAfterTrackSelectionsFor3d] = "Ambiguous MFT tracks after track selection"; labelsReassociation3dMftTracks[Reassociation3dMftTracks::NotReassociated3dMftTracks] = "Not reassociated MFT tracks by DCAxyz method"; labelsReassociation3dMftTracks[Reassociation3dMftTracks::Reassociated3dMftTracks] = "Reassociated MFT tracks by DCAxyz method"; - registry.get(HIST(WhatDataType[DataType]) + HIST("hReassociation3dMftTracks"))->SetMinimum(0); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hReassociation3dMftTracks"))->SetMinimum(0); for (int iBin = 0; iBin < Reassociation3dMftTracks::NReassociation3dMftTracksSteps; iBin++) { - registry.get(HIST(WhatDataType[DataType]) + HIST("hReassociation3dMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsReassociation3dMftTracks[iBin].data()); + registry.get(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hReassociation3dMftTracks"))->GetXaxis()->SetBinLabel(iBin + 1, labelsReassociation3dMftTracks[iBin].data()); } } - void addMftMonteCarloHistograms() + void addMftMonteCarloHistograms2D() { - // AmbiguousTracks ZVtxDiff dist. (contains matched to true and not matched to true collision) - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hZVtxDiffAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - // AmbiguousTracks NOT matched to true collisions ZVtxDiff dist. - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hZVtxDiffAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - // AmbiguousTracks matched to true collisions ZVtxDiff dist. - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hZVtxDiffAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hZVtxDiffNonAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hZVtxDiffNonAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hZVtxDiffNonAmbiguousTrackMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + if (configTask.fillSpecificHistograms) { + // AmbiguousTracks ZVtxDiff dist. (contains matched to true and not matched to true collision) + hZVtxDiffAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hZVtxDiffAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + // AmbiguousTracks NOT matched to true collisions ZVtxDiff dist. + hZVtxDiffAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiffAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + // AmbiguousTracks matched to true collisions ZVtxDiff dist. + hZVtxDiffAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiffAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiffNonAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hZVtxDiffNonAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiffNonAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiffNonAmbiguousTrackMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hZVtxDiff2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiff2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiff2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hZVtxDiffNot2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiffNot2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hZVtxDiffNot2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = registry.add("MC/2D/hZVtxDiffNotMatchedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = registry.add("MC/2D/hZVtxDiffNotMatchedTracksWithCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = registry.add("MC/2D/hZVtxDiffNotMatchedTracksWithoutCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hDcaAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hDcaAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hDcaAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hDcaAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + + hDcaNonAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hDcaNonAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hDcaNonAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hDcaNonAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + + hDca2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hDca2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); + hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hDca2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); + hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hDca2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); + + hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/2D/hDcaNot2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); + hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/2D/hDcaNot2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); + hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/2D/hDcaNot2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); + + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = registry.add("MC/2D/hDcaNotMatchedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = registry.add("MC/2D/hDcaNotMatchedTracksWithCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = registry.add("MC/2D/hDcaNotMatchedTracksWithoutCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + } + + if (configTask.fillGeneralHistograms) { + registry.add("MC/2D/hIsAmbiguousTrackMatchedToTrueCollision", "hIsAmbiguousTrackMatchedToTrueCollision", {HistType::kTH1D, {{MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps, -0.5, +MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps]; + labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguous] = "number of MFT ambiguous tracks"; + labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndMatchedToTrueCollision] = "number of MFT ambiguous tracks matched to true collision"; + labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndNotMatchedToTrueCollision] = "number of MFT ambiguous tracks NOT matched to true collision"; + registry.get(HIST("MC/2D/hIsAmbiguousTrackMatchedToTrueCollision"))->SetMinimum(0); + + for (int iBin = 0; iBin < MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/2D/hIsAmbiguousTrackMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftAmbiguousAndMatchedToTrueCollisionStep[iBin].data()); + } - hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hZVtxDiff2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hZVtxDiff2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hZVtxDiff2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + registry.add("MC/2D/hIsNonAmbiguousTrackMatchedToTrueCollision", "hIsNonAmbiguousTrackMatchedToTrue", {HistType::kTH1D, {{MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps, -0.5, +MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps]; + labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguous] = "number of MFT Non ambiguous tracks"; + labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndMatchedToTrueCollision] = "number of MFT Non ambiguous tracks matched to true collision"; + labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndNotMatchedToTrueCollision] = "number of MFT Non ambiguous tracks NOT matched to true collision"; + registry.get(HIST("MC/2D/hIsNonAmbiguousTrackMatchedToTrueCollision"))->SetMinimum(0); - hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hZVtxDiffNot2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hZVtxDiffNot2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hZVtxDiffNot2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + for (int iBin = 0; iBin < MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/2D/hIsNonAmbiguousTrackMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[iBin].data()); + } - hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hZVtxDiff3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hZVtxDiff3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hZVtxDiff3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + registry.add("MC/2D/hIs2dReassociatedAndMatchedToTrueCollision", "Is2dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps]; + labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociated] = "number of MFT 2d reassociated tracks"; + labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndMatchedToTrueCollision] = "number of MFT 2d reassociated tracks matched to true collision"; + labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndNotMatchedToTrueCollision] = "number of MFT 2d reassociated tracks NOT matched to true collision"; + registry.get(HIST("MC/2D/hIs2dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); - hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hZVtxDiffNot3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hZVtxDiffNot3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hZVtxDiffNot3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + for (int iBin = 0; iBin < Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/2D/hIs2dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMft2dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); + } - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = registry.add("MC/hZVtxDiffNotMatchedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = registry.add("MC/hZVtxDiffNotMatchedTracksWithCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = registry.add("MC/hZVtxDiffNotMatchedTracksWithoutCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + registry.add("MC/2D/hIsNot2dReassociatedAndMatchedToTrueCollision", "IsNot2dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps]; + labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociated] = "number of MFT NOT 2d reassociated tracks"; + labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndMatchedToTrueCollision] = "number of MFT NOT 2d reassociated tracks matched to true collision"; + labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndNotMatchedToTrueCollision] = "number of MFT NOT 2d reassociated tracks NOT matched to true collision"; + registry.get(HIST("MC/2D/hIsNot2dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hDcaAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hDcaAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hDcaAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hDcaNonAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hDcaNonAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hDcaNonAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - - hDca2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hDca2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); - hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hDca2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); - hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hDca2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); - - hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hDcaNot2dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); - hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hDcaNot2dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); - hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hDcaNot2dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{XY}^{gen} (cm);", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaX}); - - hDca3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hDca3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hDca3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hDca3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - - hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/hDcaNot3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/hDcaNot3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/hDcaNot3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = registry.add("MC/hDcaNotMatchedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = registry.add("MC/hDcaNotMatchedTracksWithCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = registry.add("MC/hDcaNotMatchedTracksWithoutCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); - - registry.add("MC/hIsAmbiguousTrackMatchedToTrueCollision", "hIsAmbiguousTrackMatchedToTrueCollision", {HistType::kTH1D, {{MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps, -0.5, +MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps - 0.5}}}); - std::string labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps]; - labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguous] = "number of MFT ambiguous tracks"; - labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndMatchedToTrueCollision] = "number of MFT ambiguous tracks matched to true collision"; - labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndNotMatchedToTrueCollision] = "number of MFT ambiguous tracks NOT matched to true collision"; - registry.get(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"))->SetMinimum(0); - - for (int iBin = 0; iBin < MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps; iBin++) { - registry.get(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftAmbiguousAndMatchedToTrueCollisionStep[iBin].data()); - } - - registry.add("MC/hIsNonAmbiguousTrackMatchedToTrueCollision", "hIsNonAmbiguousTrackMatchedToTrue", {HistType::kTH1D, {{MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps, -0.5, +MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps - 0.5}}}); - std::string labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps]; - labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguous] = "number of MFT Non ambiguous tracks"; - labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndMatchedToTrueCollision] = "number of MFT Non ambiguous tracks matched to true collision"; - labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndNotMatchedToTrueCollision] = "number of MFT Non ambiguous tracks NOT matched to true collision"; - registry.get(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"))->SetMinimum(0); - - for (int iBin = 0; iBin < MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps; iBin++) { - registry.get(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[iBin].data()); - } - - registry.add("MC/hIs2dReassociatedAndMatchedToTrueCollision", "Is2dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); - std::string labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps]; - labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociated] = "number of MFT 2d reassociated tracks"; - labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndMatchedToTrueCollision] = "number of MFT 2d reassociated tracks matched to true collision"; - labelsMft2dReassociatedAndMatchedToTrueCollisionStep[Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndNotMatchedToTrueCollision] = "number of MFT 2d reassociated tracks NOT matched to true collision"; - registry.get(HIST("MC/hIs2dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); - - for (int iBin = 0; iBin < Mft2dReassociatedAndMatchedToTrueCollisionStep::NMft2dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { - registry.get(HIST("MC/hIs2dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMft2dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); - } - - registry.add("MC/hIsNot2dReassociatedAndMatchedToTrueCollision", "IsNot2dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); - std::string labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps]; - labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociated] = "number of MFT NOT 2d reassociated tracks"; - labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndMatchedToTrueCollision] = "number of MFT NOT 2d reassociated tracks matched to true collision"; - labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndNotMatchedToTrueCollision] = "number of MFT NOT 2d reassociated tracks NOT matched to true collision"; - registry.get(HIST("MC/hIsNot2dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); - - for (int iBin = 0; iBin < MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { - registry.get(HIST("MC/hIsNot2dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); - } + for (int iBin = 0; iBin < MftNot2dReassociatedAndMatchedToTrueCollisionStep::NMftNot2dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/2D/hIsNot2dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftNot2dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); + } - registry.add("MC/hIs3dReassociatedAndMatchedToTrueCollision", "Is3dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); - std::string labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps]; - labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociated] = "number of MFT 3d reassociated tracks"; - labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndMatchedToTrueCollision] = "number of MFT 3d reassociated tracks matched to true collision"; - labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndNotMatchedToTrueCollision] = "number of MFT 3d reassociated tracks NOT matched to true collision"; - registry.get(HIST("MC/hIs3dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); + registry.add("MC/2D/hIsTrueCollisionAmongCompatibleCollisions", "IsTrueCollisionAmongCompatibleCollisions", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); + registry.add("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated", "hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); + registry.add("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated", "hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); + std::string labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps]; + labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = "number of wrongly associated tracks"; + labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = "number of MFT tracks with True Coll. among Compatible"; + labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = "number of MFT tracks WITHOUT True Coll. among Compatible"; + registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"))->SetMinimum(0); + registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"))->SetMinimum(0); + registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->SetMinimum(0); + + for (int iBin = 0; iBin < MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps; iBin++) { + registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); + registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); + registry.get(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); + } + } - for (int iBin = 0; iBin < Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { - registry.get(HIST("MC/hIs3dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMft3dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); + if (configTask.fillDcaTemplateFit) { + registry.add("MC/2D/DcaFit/hPtVsDcaXY_prim", "Primary DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/2D/DcaFit/hPtVsDcaXY_sec", "Secondary DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); } + } + + void addMftMonteCarloHistograms3D() + { + if (configTask.fillSpecificHistograms) { + // AmbiguousTracks ZVtxDiff dist. (contains matched to true and not matched to true collision) + hZVtxDiffAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hZVtxDiffAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + // AmbiguousTracks NOT matched to true collisions ZVtxDiff dist. + hZVtxDiffAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiffAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + // AmbiguousTracks matched to true collisions ZVtxDiff dist. + hZVtxDiffAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiffAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen> (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiffNonAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hZVtxDiffNonAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiffNonAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiffNonAmbiguousTrackMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hZVtxDiff3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiff3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiff3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hZVtxDiffNot3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiffNot3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hZVtxDiffNot3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = registry.add("MC/3D/hZVtxDiffNotMatchedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = registry.add("MC/3D/hZVtxDiffNotMatchedTracksWithCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = registry.add("MC/3D/hZVtxDiffNotMatchedTracksWithoutCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaY, configAxis.axisDcaZ}); + + hDcaAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hDcaAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hDcaAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hDcaAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + + hDcaNonAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hDcaNonAmbiguousTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hDcaNonAmbiguousTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hDcaNonAmbiguousTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + + hDca3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hDca3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hDca3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hDca3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + + hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks] = registry.add("MC/3D/hDcaNot3dReassociatedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision] = registry.add("MC/3D/hDcaNot3dReassociatedTracksNotMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision] = registry.add("MC/3D/hDcaNot3dReassociatedTracksMatchedToTrueCollision", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = registry.add("MC/3D/hDcaNotMatchedTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = registry.add("MC/3D/hDcaNotMatchedTracksWithCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = registry.add("MC/3D/hDcaNotMatchedTracksWithoutCollAmongCompatible", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY}^{reco} (cm); DCA_{Z}^{reco} (cm); DCA_{XY}^{gen} (cm); DCA_{Z}^{gen} (cm)", HistType::kTHnSparseF, {configAxis.axisPt, configAxis.axisEta, configAxis.axisDcaX, configAxis.axisDcaZ, configAxis.axisDcaX, configAxis.axisDcaZ}); + } + + if (configTask.fillGeneralHistograms) { + registry.add("MC/3D/hIsAmbiguousTrackMatchedToTrueCollision", "hIsAmbiguousTrackMatchedToTrueCollision", {HistType::kTH1D, {{MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps, -0.5, +MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps]; + labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguous] = "number of MFT ambiguous tracks"; + labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndMatchedToTrueCollision] = "number of MFT ambiguous tracks matched to true collision"; + labelsMftAmbiguousAndMatchedToTrueCollisionStep[MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndNotMatchedToTrueCollision] = "number of MFT ambiguous tracks NOT matched to true collision"; + registry.get(HIST("MC/3D/hIsAmbiguousTrackMatchedToTrueCollision"))->SetMinimum(0); + + for (int iBin = 0; iBin < MftAmbiguousAndMatchedToTrueCollisionStep::NMftAmbiguousAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/3D/hIsAmbiguousTrackMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftAmbiguousAndMatchedToTrueCollisionStep[iBin].data()); + } + + registry.add("MC/3D/hIsNonAmbiguousTrackMatchedToTrueCollision", "hIsNonAmbiguousTrackMatchedToTrue", {HistType::kTH1D, {{MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps, -0.5, +MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps]; + labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguous] = "number of MFT Non ambiguous tracks"; + labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndMatchedToTrueCollision] = "number of MFT Non ambiguous tracks matched to true collision"; + labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndNotMatchedToTrueCollision] = "number of MFT Non ambiguous tracks NOT matched to true collision"; + registry.get(HIST("MC/3D/hIsNonAmbiguousTrackMatchedToTrueCollision"))->SetMinimum(0); + + for (int iBin = 0; iBin < MftNonAmbiguousAndMatchedToTrueCollisionStep::NMftNonAmbiguousAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/3D/hIsNonAmbiguousTrackMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftNonAmbiguousAndMatchedToTrueCollisionStep[iBin].data()); + } + + registry.add("MC/3D/hIs3dReassociatedAndMatchedToTrueCollision", "Is3dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps]; + labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociated] = "number of MFT 3d reassociated tracks"; + labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndMatchedToTrueCollision] = "number of MFT 3d reassociated tracks matched to true collision"; + labelsMft3dReassociatedAndMatchedToTrueCollisionStep[Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndNotMatchedToTrueCollision] = "number of MFT 3d reassociated tracks NOT matched to true collision"; + registry.get(HIST("MC/3D/hIs3dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); - registry.add("MC/hIsNot3dReassociatedAndMatchedToTrueCollision", "IsNot3dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); - std::string labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps]; - labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociated] = "number of MFT NOT 3d reassociated tracks"; - labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndMatchedToTrueCollision] = "number of MFT NOT 3d reassociated tracks matched to true collision"; - labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndNotMatchedToTrueCollision] = "number of MFT NOT 3d reassociated tracks NOT matched to true collision"; - registry.get(HIST("MC/hIsNot3dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); + for (int iBin = 0; iBin < Mft3dReassociatedAndMatchedToTrueCollisionStep::NMft3dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/3D/hIs3dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMft3dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); + } - for (int iBin = 0; iBin < MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { - registry.get(HIST("MC/hIsNot3dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); + registry.add("MC/3D/hIsNot3dReassociatedAndMatchedToTrueCollision", "IsNot3dReassociatedAndMatchedToTrueCollision", {HistType::kTH1D, {{MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps, -0.5, +MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps - 0.5}}}); + std::string labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps]; + labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociated] = "number of MFT NOT 3d reassociated tracks"; + labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndMatchedToTrueCollision] = "number of MFT NOT 3d reassociated tracks matched to true collision"; + labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndNotMatchedToTrueCollision] = "number of MFT NOT 3d reassociated tracks NOT matched to true collision"; + registry.get(HIST("MC/3D/hIsNot3dReassociatedAndMatchedToTrueCollision"))->SetMinimum(0); + + for (int iBin = 0; iBin < MftNot3dReassociatedAndMatchedToTrueCollisionStep::NMftNot3dReassociatedAndMatchedToTrueCollisionSteps; iBin++) { + registry.get(HIST("MC/3D/hIsNot3dReassociatedAndMatchedToTrueCollision"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftNot3dReassociatedAndMatchedToTrueCollisionStep[iBin].data()); + } + + registry.add("MC/3D/hIsTrueCollisionAmongCompatibleCollisions", "IsTrueCollisionAmongCompatibleCollisions", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); + registry.add("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated", "hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); + registry.add("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated", "hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); + std::string labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps]; + labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = "number of wrongly associated tracks"; + labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = "number of MFT tracks with True Coll. among Compatible"; + labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = "number of MFT tracks WITHOUT True Coll. among Compatible"; + registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"))->SetMinimum(0); + registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"))->SetMinimum(0); + registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->SetMinimum(0); + + for (int iBin = 0; iBin < MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps; iBin++) { + registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); + registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); + registry.get(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); + } } - registry.add("MC/hIsTrueCollisionAmongCompatibleCollisions", "IsTrueCollisionAmongCompatibleCollisions", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); - registry.add("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated", "hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); - registry.add("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated", "hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated", {HistType::kTH1D, {{MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps, -0.5, +MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps - 0.5}}}); - std::string labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps]; - labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks] = "number of wrongly associated tracks"; - labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions] = "number of MFT tracks with True Coll. among Compatible"; - labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions] = "number of MFT tracks WITHOUT True Coll. among Compatible"; - registry.get(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"))->SetMinimum(0); - registry.get(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"))->SetMinimum(0); - registry.get(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->SetMinimum(0); + if (configTask.fillDcaTemplateFit) { + // 3D reassociated + registry.add("MC/3D/DcaFit/hPtVsDcaXY_prim", "Primary DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaXY_sec", "Secondary DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaZ_prim", "Primary DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + registry.add("MC/3D/DcaFit/hPtVsDcaZ_sec", "Secondary DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); - for (int iBin = 0; iBin < MftIsTrueCollisionAmongCompatibleCollisionsStep::NMftIsTrueCollisionAmongCompatibleCollisionsSteps; iBin++) { - registry.get(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); - registry.get(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); - registry.get(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMftIsTrueCollisionAmongCompatibleCollisionsStep[iBin].data()); + // Non-ambiguous + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_prim", "NonAmb Primary DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_sec", "NonAmb Secondary DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_prim", "NonAmb Primary DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + registry.add("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_sec", "NonAmb Secondary DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); } } + template + void addParametersHistograms() + { + registry.add(Form("MC/%s%shMftEta", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisEta}}); + registry.add(Form("MC/%s%shMftPhi", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisPhi}}); + registry.add(Form("MC/%s%shMftPt", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisPt}}); + registry.add(Form("MC/%s%shMftDcaXY", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisDcaX}}); + registry.add(Form("MC/%s%shMftDcaZ", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisDcaZ}}); + registry.add(Form("MC/%s%shMftAlgorithmUsed", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "hMftAlgorithmUsed", {HistType::kTH1D, {{TrackingAlgorithmStep::NTrackingAlgorithmSteps, -0.5, +TrackingAlgorithmStep::NTrackingAlgorithmSteps - 0.5}}}); + std::string labelsTrackingAlgorithm[TrackingAlgorithmStep::NTrackingAlgorithmSteps]; + labelsTrackingAlgorithm[TrackingAlgorithmStep::IsLTFAlgorithm] = "IsLTF algorithm"; + labelsTrackingAlgorithm[TrackingAlgorithmStep::IsCAAlgorithm] = "IsCA algorithm"; + registry.get(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftAlgorithmUsed"))->SetMinimum(0); + for (int iBin = 0; iBin < TrackingAlgorithmStep::NTrackingAlgorithmSteps; iBin++) { + registry.get(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftAlgorithmUsed"))->GetXaxis()->SetBinLabel(iBin + 1, labelsTrackingAlgorithm[iBin].data()); + } + + registry.add(Form("MC/%s%shCollPosX", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisPosX}}); + registry.add(Form("MC/%s%shCollPosY", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisPosX}}); + registry.add(Form("MC/%s%shCollPosZ", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisPosZ}}); + registry.add(Form("MC/%s%shCollMultiplicity", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + registry.add(Form("MC/%s%shCollNumContrib", WhatReassociationMethod[ReassociationMethod].data(), WhatMcStatus[McStatus].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); + } + // ========================= // init() // ========================= @@ -590,8 +745,8 @@ struct MftReassociationValidation { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); - rctChecker.init(configCollision.setRCTFlagCheckerLabel, configCollision.requireZDCCheck, configCollision.requireRCTFlagCheckerLimitAcceptanceAsBad, true); - correlationAnalysisRctChecker.init({kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad}, configCollision.requireZDCCheck, configCollision.requireRCTFlagCheckerLimitAcceptanceAsBad, true); + rctChecker.init({kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad}); + correlationAnalysisRctChecker.init({kFT0Bad, kITSBad, kTPCBadTracking, kTPCBadPID, kMFTBad, kITSLimAccMCRepr, kMFTLimAccMCRepr, kTPCLimAccMCRepr}); // ========================= // Event histograms @@ -601,7 +756,8 @@ struct MftReassociationValidation { // registry.add("Data/hNTracks", "", {HistType::kTH1F, {configAxis.axisMultiplicity}}); registry.add(Form("Data/hMultiplicity_%s", WhatMultiplicityEstimator[configCollision.multiplicityEstimator].data()), "", {HistType::kTH1D, {configAxis.axisMultiplicity}}); - registry.add("hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); + registry.add("MC/2D/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); + registry.add("MC/3D/hPreciseEventCounter", "hPreciseEventCounter", {HistType::kTH1D, {{SpecificEventSelectionStep::NSpecificEventSelectionSteps, -0.5, +SpecificEventSelectionStep::NSpecificEventSelectionSteps - 0.5}}}); std::string labels[SpecificEventSelectionStep::NSpecificEventSelectionSteps]; labels[SpecificEventSelectionStep::AllEventsPrecise] = "all"; labels[SpecificEventSelectionStep::HasMcCollision] = "has MC coll?"; @@ -617,13 +773,16 @@ struct MftReassociationValidation { labels[SpecificEventSelectionStep::IsNoHighMultCollInPrevRof] = "IsNoHighMultCollInPrevRof"; labels[SpecificEventSelectionStep::IsRctFlagChecked] = "IsRctFlagChecked"; labels[SpecificEventSelectionStep::IsWithinZvtxWindow] = "IsWithinZvtxWindow"; - registry.get(HIST("hPreciseEventCounter"))->SetMinimum(0); + registry.get(HIST("MC/2D/hPreciseEventCounter"))->SetMinimum(0); + registry.get(HIST("MC/3D/hPreciseEventCounter"))->SetMinimum(0); for (int iBin = 0; iBin < SpecificEventSelectionStep::NSpecificEventSelectionSteps; iBin++) { - registry.get(HIST("hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + registry.get(HIST("MC/2D/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); + registry.get(HIST("MC/3D/hPreciseEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labels[iBin].data()); } - registry.add("MC/hPreciseTrackSelectionCounter", "hPreciseTrackSelectionCounter", {HistType::kTH1D, {{SpecificTrackSelectionStep::NSpecificTrackSelectionSteps, -0.5, +SpecificTrackSelectionStep::NSpecificTrackSelectionSteps - 0.5}}}); + registry.add("MC/2D/hPreciseTrackSelectionCounter", "hPreciseTrackSelectionCounter", {HistType::kTH1D, {{SpecificTrackSelectionStep::NSpecificTrackSelectionSteps, -0.5, +SpecificTrackSelectionStep::NSpecificTrackSelectionSteps - 0.5}}}); + registry.add("MC/3D/hPreciseTrackSelectionCounter", "hPreciseTrackSelectionCounter", {HistType::kTH1D, {{SpecificTrackSelectionStep::NSpecificTrackSelectionSteps, -0.5, +SpecificTrackSelectionStep::NSpecificTrackSelectionSteps - 0.5}}}); std::string labelsTrackSelection[SpecificTrackSelectionStep::NSpecificTrackSelectionSteps]; labelsTrackSelection[SpecificTrackSelectionStep::AllTracksPrecise] = "all tracks"; labelsTrackSelection[SpecificTrackSelectionStep::IsTrueTrack] = "true tracks"; @@ -633,47 +792,59 @@ struct MftReassociationValidation { labelsTrackSelection[SpecificTrackSelectionStep::AfterPtCut] = "after pt cut"; labelsTrackSelection[SpecificTrackSelectionStep::AfterDcaXYCut] = "after DCA XY cut"; labelsTrackSelection[SpecificTrackSelectionStep::AfterDcaZCut] = "after DCA Z cut"; + labelsTrackSelection[SpecificTrackSelectionStep::AfterChi2OverNdfCut] = "after chi2/ndf cut"; labelsTrackSelection[SpecificTrackSelectionStep::IsCATrack] = "is CA track"; labelsTrackSelection[SpecificTrackSelectionStep::IsLTFTrack] = "is LTF track"; labelsTrackSelection[SpecificTrackSelectionStep::HasMcParticle] = "has MC particle"; - registry.get(HIST("MC/hPreciseTrackSelectionCounter"))->SetMinimum(0); + registry.get(HIST("MC/2D/hPreciseTrackSelectionCounter"))->SetMinimum(0); + registry.get(HIST("MC/3D/hPreciseTrackSelectionCounter"))->SetMinimum(0); for (int iBin = 0; iBin < SpecificTrackSelectionStep::NSpecificTrackSelectionSteps; iBin++) { - registry.get(HIST("MC/hPreciseTrackSelectionCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsTrackSelection[iBin].data()); + registry.get(HIST("MC/2D/hPreciseTrackSelectionCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsTrackSelection[iBin].data()); + registry.get(HIST("MC/3D/hPreciseTrackSelectionCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsTrackSelection[iBin].data()); } - registry.add("MC/hTrackAmbiguityCheck", "hTrackAmbiguityCheck", {HistType::kTH1D, {{TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps, -0.5, +TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps - 0.5}}}); + registry.add("MC/2D/hTrackAmbiguityCheck", "hTrackAmbiguityCheck", {HistType::kTH1D, {{TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps, -0.5, +TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps - 0.5}}}); + registry.add("MC/3D/hTrackAmbiguityCheck", "hTrackAmbiguityCheck", {HistType::kTH1D, {{TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps, -0.5, +TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps - 0.5}}}); std::string labelsTrackAmbiguityCheck[TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps]; labelsTrackAmbiguityCheck[TrackAmbiguityCheckStep::AllTracksCheck] = "all tracks"; labelsTrackAmbiguityCheck[TrackAmbiguityCheckStep::IsAmbDegreeEqualToCompatibleCollIdsSize] = "ambDegree == compatibleCollIds.size"; - registry.get(HIST("MC/hTrackAmbiguityCheck"))->SetMinimum(0); + registry.get(HIST("MC/2D/hTrackAmbiguityCheck"))->SetMinimum(0); + registry.get(HIST("MC/3D/hTrackAmbiguityCheck"))->SetMinimum(0); for (int iBin = 0; iBin < TrackAmbiguityCheckStep::NTrackAmbiguityCheckSteps; iBin++) { - registry.get(HIST("MC/hTrackAmbiguityCheck"))->GetXaxis()->SetBinLabel(iBin + 1, labelsTrackAmbiguityCheck[iBin].data()); + registry.get(HIST("MC/2D/hTrackAmbiguityCheck"))->GetXaxis()->SetBinLabel(iBin + 1, labelsTrackAmbiguityCheck[iBin].data()); + registry.get(HIST("MC/3D/hTrackAmbiguityCheck"))->GetXaxis()->SetBinLabel(iBin + 1, labelsTrackAmbiguityCheck[iBin].data()); } - registry.add("MC/hMonteCarloEventCounter", "hMonteCarloEventCounter", {HistType::kTH1D, {{MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps, -0.5, +MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps - 0.5}}}); + registry.add("MC/2D/hMonteCarloEventCounter", "hMonteCarloEventCounter", {HistType::kTH1D, {{MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps, -0.5, +MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps - 0.5}}}); + registry.add("MC/3D/hMonteCarloEventCounter", "hMonteCarloEventCounter", {HistType::kTH1D, {{MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps, -0.5, +MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps - 0.5}}}); std::string labelsMonteCarloEvents[MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps]; labelsMonteCarloEvents[MonteCarloEventSelectionStep::AllMonteCarloEvents] = "all collisions"; labelsMonteCarloEvents[MonteCarloEventSelectionStep::MonteCarloEventsAfterEventSelection] = "collisions after event selection"; labelsMonteCarloEvents[MonteCarloEventSelectionStep::HasMonteCarloCollision] = "has MC collision"; labelsMonteCarloEvents[MonteCarloEventSelectionStep::HasNotMonteCarloCollision] = "has not MC collision"; - registry.get(HIST("MC/hMonteCarloEventCounter"))->SetMinimum(0); + registry.get(HIST("MC/2D/hMonteCarloEventCounter"))->SetMinimum(0); + registry.get(HIST("MC/3D/hMonteCarloEventCounter"))->SetMinimum(0); for (int iBin = 0; iBin < MonteCarloEventSelectionStep::NMonteCarloEventSelectionSteps; iBin++) { - registry.get(HIST("MC/hMonteCarloEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMonteCarloEvents[iBin].data()); + registry.get(HIST("MC/2D/hMonteCarloEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMonteCarloEvents[iBin].data()); + registry.get(HIST("MC/3D/hMonteCarloEventCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMonteCarloEvents[iBin].data()); } - registry.add("MC/hMonteCarloTrackCounter", "hMonteCarloTrackCounter", {HistType::kTH1D, {{MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps, -0.5, +MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps - 0.5}}}); + registry.add("MC/2D/hMonteCarloTrackCounter", "hMonteCarloTrackCounter", {HistType::kTH1D, {{MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps, -0.5, +MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps - 0.5}}}); + registry.add("MC/3D/hMonteCarloTrackCounter", "hMonteCarloTrackCounter", {HistType::kTH1D, {{MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps, -0.5, +MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps - 0.5}}}); std::string labelsMonteCarloTracks[MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps]; labelsMonteCarloTracks[MonteCarloTrackSelectionStep::AllMonteCarloTracks] = "all tracks"; labelsMonteCarloTracks[MonteCarloTrackSelectionStep::MonteCarloTracksAfterTrackSelection] = "tracks after track selection"; labelsMonteCarloTracks[MonteCarloTrackSelectionStep::HasMonteCarloParticle] = "has MC particle"; labelsMonteCarloTracks[MonteCarloTrackSelectionStep::HasNotMonteCarloParticle] = "has not MC particle"; - registry.get(HIST("MC/hMonteCarloTrackCounter"))->SetMinimum(0); + registry.get(HIST("MC/2D/hMonteCarloTrackCounter"))->SetMinimum(0); + registry.get(HIST("MC/3D/hMonteCarloTrackCounter"))->SetMinimum(0); for (int iBin = 0; iBin < MonteCarloTrackSelectionStep::NMonteCarloTrackSelectionSteps; iBin++) { - registry.get(HIST("MC/hMonteCarloTrackCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMonteCarloTracks[iBin].data()); + registry.get(HIST("MC/2D/hMonteCarloTrackCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMonteCarloTracks[iBin].data()); + registry.get(HIST("MC/3D/hMonteCarloTrackCounter"))->GetXaxis()->SetBinLabel(iBin + 1, labelsMonteCarloTracks[iBin].data()); } // ========================= @@ -681,17 +852,45 @@ struct MftReassociationValidation { // ========================= if (doprocessData) { - addMftHistograms(); + addMftHistograms(); + + if (configTask.fillDcaTemplateFit) { + registry.add("Data/2D/DcaFit/hPtVsDcaXY", "Data DCA_{xy} (2D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + } } if (doprocessMcReassociated2d) { - addMftHistograms(); - addMftMonteCarloHistograms(); + addMftHistograms(); + if (configTask.fillParametersHistograms) { + addParametersHistograms(); + addParametersHistograms(); + addParametersHistograms(); + addParametersHistograms(); + } + addMftMonteCarloHistograms2D(); } if (doprocessMcReassociated3d) { - addMftHistograms(); - addMftMonteCarloHistograms(); + addMftHistograms(); + if (configTask.fillParametersHistograms) { + addParametersHistograms(); + addParametersHistograms(); + addParametersHistograms(); + addParametersHistograms(); + } + addMftMonteCarloHistograms3D(); + } + + if (doprocessData3d) { + addMftHistograms(); + + if (configTask.fillDcaTemplateFit) { + registry.add("Data/3D/DcaFit/hPtVsDcaXY", "Data DCA_{xy} (3D);p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("Data/3D/DcaFit/hPtVsDcaZ", "Data DCA_{z} (3D);p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + + registry.add("Data/3D/DcaFit/NonAmb/hPtVsDcaXY", "NonAmb Data DCA_{xy};p_{T} (GeV/c);DCA_{xy} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaXYfit}); + registry.add("Data/3D/DcaFit/NonAmb/hPtVsDcaZ", "NonAmb Data DCA_{z};p_{T} (GeV/c);DCA_{z} (cm)", kTH2F, {configAxis.axisPt, configAxis.axisDcaZfit}); + } } } // End of init() function @@ -701,28 +900,16 @@ struct MftReassociationValidation { // ========================= template - float getMultiplicityEstimator(TCollision collision, bool isSameEvent) + float getMultiplicityEstimator(const TCollision& collision) { switch (configCollision.multiplicityEstimator) { case MultiplicityEstimators::MultNTracksPV: - if (isSameEvent) { - registry.fill(HIST("Data/hMultiplicity_multNTracksPV"), collision.multNTracksPV()); - } return collision.multNTracksPV(); case MultiplicityEstimators::MultNumContrib: - if (isSameEvent) { - registry.fill(HIST("Data/hMultiplicity_multNumContrib"), collision.numContrib()); - } return collision.numContrib(); case MultiplicityEstimators::MultFT0C: - if (isSameEvent) { - registry.fill(HIST("Data/hMultiplicity_multFT0C"), collision.multFT0C()); - } return collision.multFT0C(); case MultiplicityEstimators::MultFT0M: - if (isSameEvent) { - registry.fill(HIST("Data/hMultiplicity_multFT0M"), collision.multFT0M()); - } return collision.multFT0M(); default: return collision.multNTracksPV(); @@ -745,7 +932,7 @@ struct MftReassociationValidation { } template - bool isTrueCollisionAmongCompatibleCollisions(TTrack track) + bool isTrueCollisionAmongCompatibleCollisions(const TTrack& track) { auto const& compatibleIds = track.compatibleCollIds(); @@ -782,92 +969,115 @@ struct MftReassociationValidation { { grpmag = ccdb->getForTimeStamp(configTask.grpmagPath, bc.timestamp()); - LOG(info) << "Setting magnetic field to current " << grpmag->getL3Current() - << " A for run " << bc.runNumber() - << " from its GRPMagField CCDB object"; o2::base::Propagator::initFieldFromGRP(grpmag); o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); bZ = field->getBz(CcenterMFT); - LOG(info) << "The field at the center of the MFT is bZ = " << bZ; if (configTask.cfgApplyZShiftFromCCDB) { auto* zShift = ccdb->getForTimeStamp>(configTask.cfgZShiftPath, bc.timestamp()); if (zShift != nullptr && !zShift->empty()) { - LOGF(info, "reading z shift %f from %s", (*zShift)[0], configTask.cfgZShiftPath.value); mZShift = (*zShift)[0]; } else { - LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", configTask.cfgZShiftPath.value); mZShift = 0; } } else { - LOGF(info, "z shift is manually set to %f cm", configTask.cfgManualZShift.value); mZShift = configTask.cfgManualZShift; } } + template + void fillTrackParametersHistograms(TMftTrack const& mftTrack, float dcaXY, float dcaZ) + { + + float phi = mftTrack.phi(); + o2::math_utils::bringTo02Pi(phi); + + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftEta"), mftTrack.eta()); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftPhi"), phi); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftPt"), mftTrack.pt()); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftDcaXY"), dcaXY); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftDcaZ"), dcaZ); + if (mftTrack.isCA()) { + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftAlgorithmUsed"), TrackingAlgorithmStep::IsCAAlgorithm); + } else { + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hMftAlgorithmUsed"), TrackingAlgorithmStep::IsLTFAlgorithm); + } + } + + template + void fillCollisionParametersHistograms(TPosX const& posX, TPosY const& posY, TPosZ const& posZ, TMultiplicity const& multiplicity, TNumContrib const& numContrib) + { + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hCollPosX"), posX); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hCollPosY"), posY); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hCollPosZ"), posZ); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hCollMultiplicity"), multiplicity); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST(WhatMcStatus[McStatus]) + HIST("hCollNumContrib"), numContrib); + } + // ========================= // Cuts with functions // ========================= // FIXME: Some collisions are rejected here, what causes (part of) differences with the D0 task - template + template bool isAcceptedCollision(TCollision const& collision, bool fillHistograms = false) { if (!collision.sel8()) { return false; } + if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsSel8); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsSel8); } if (configCollision.isApplySameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoSameBunchPileup); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoSameBunchPileup); } if (configCollision.isApplyGoodItsLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodItsLayersAll); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodItsLayersAll); } if (configCollision.isApplyGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodZvtxFT0vsPV); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsGoodZvtxFT0vsPV); } if (configCollision.isApplyNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStandard); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStandard); } if (configCollision.isApplyNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStrict); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInRofStrict); } if (configCollision.isApplyNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStandard); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStandard); } if (configCollision.isApplyNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStrict); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoCollInTimeRangeStrict); } if (configCollision.isApplyNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoHighMultCollInPrevRof); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNoHighMultCollInPrevRof); } if (configCollision.requireRCTFlagChecker && !rctChecker(collision)) { return false; @@ -876,13 +1086,13 @@ struct MftReassociationValidation { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsRctFlagChecked); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsRctFlagChecked); } if (collision.posZ() > configCollision.zVertexMax || collision.posZ() < (-configCollision.zVertexMax)) { return false; } if (fillHistograms) { - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsWithinZvtxWindow); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsWithinZvtxWindow); } registry.fill(HIST("Data/hVtxZ"), collision.posZ()); @@ -893,7 +1103,7 @@ struct MftReassociationValidation { // TODO: Check how to put this into a Filter // I tried to put it as a filter, but filters for normal TPC tracks also apply to MFT tracks I think // and it seems that they are not compatible - template + template bool isAcceptedMftTrack(TTrack const& mftTrack, bool fillHistograms, bool isData, float dcaXY, float dcaZ) { // cut on the eta of MFT tracks @@ -903,12 +1113,11 @@ struct MftReassociationValidation { if (fillHistograms) { if (isData) { - registry.fill(HIST("Data/hMftTracksSelection"), MftTrackSelectionStep::Eta); + registry.fill(HIST("Data/2D/hMftTracksSelection"), MftTrackSelectionStep::Eta); } else { - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterEtaCut); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterEtaCut); } } - // cut on the number of clusters of the reconstructed MFT track if (mftTrack.nClusters() < configMft.nClustersMftTrack) { return false; @@ -916,9 +1125,9 @@ struct MftReassociationValidation { if (fillHistograms) { if (isData) { - registry.fill(HIST("Data/hMftTracksSelection"), MftTrackSelectionStep::Cluster); + registry.fill(HIST("Data/2D/hMftTracksSelection"), MftTrackSelectionStep::Cluster); } else { - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterClusterCut); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterClusterCut); } } @@ -929,28 +1138,37 @@ struct MftReassociationValidation { if (fillHistograms) { if (isData) { - registry.fill(HIST("Data/hMftTracksSelection"), MftTrackSelectionStep::Pt); + registry.fill(HIST("Data/2D/hMftTracksSelection"), MftTrackSelectionStep::Pt); } else { - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterPtCut); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterPtCut); } } - if (configMft.cutOnDcaXY && std::abs(dcaXY) > configMft.dcaXYMax) { + if (configMft.cutOnDcaXY && std::abs(dcaXY) > configMft.mftMaxDCAxy) { return false; } - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaXYCut); - if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.dcaZMax) { + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaXYCut); + if (configMft.cutOnDcaZ && std::abs(dcaZ) > configMft.mftMaxDCAz) { return false; } - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaZCut); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterDcaZCut); + + if (configMft.useMftChi2OverNdfCut) { + float ndfMftTrack = std::max(2.0f * mftTrack.nClusters() - 5.0f, 1.0f); + float mftChi2OverNdf = mftTrack.chi2() / ndfMftTrack; + if (mftChi2OverNdf > configMft.maxChi2OverNdf) { + return false; + } + } + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterChi2OverNdfCut); // cut on the track algorithm of MFT tracks if (mftTrack.isCA()) { if (fillHistograms) { if (isData) { - registry.fill(HIST("Data/hMftTracksSelection"), MftTrackSelectionStep::IsCA); + registry.fill(HIST("Data/2D/hMftTracksSelection"), MftTrackSelectionStep::IsCA); } else { - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsCATrack); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsCATrack); } } @@ -961,9 +1179,9 @@ struct MftReassociationValidation { } else { if (fillHistograms) { if (isData) { - registry.fill(HIST("Data/hMftTracksSelection"), MftTrackSelectionStep::IsLTF); + registry.fill(HIST("Data/2D/hMftTracksSelection"), MftTrackSelectionStep::IsLTF); } else { - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsLTFTrack); + registry.fill(HIST("MC/") + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsLTFTrack); } } @@ -976,29 +1194,18 @@ struct MftReassociationValidation { } // Cut on ambiguous MFT tracks - template + template bool isAmbiguousMftTrack(TTrack const& mftTrack, bool fillHistograms) { if (mftTrack.ambDegree() > 1) { if (fillHistograms) { - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); + registry.fill(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); } return true; - if (mftTrack.ambDegree() > 1) { - if (fillHistograms) { - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); - } - return true; - } - - if (fillHistograms) { - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); - } - return false; } if (fillHistograms) { - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); + registry.fill(HIST(WhatDataType[DataType]) + HIST(WhatReassociationMethod[ReassociationMethod]) + HIST("hPreciseTrackSelectionCounter"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); } return false; } @@ -1015,9 +1222,12 @@ struct MftReassociationValidation { aod::BCsWithTimestamps const&) { auto bc = collision.template bc_as(); - loadZVertexShiftCorrection(bc); + if (areCorrectionsLoaded == false) { + loadZVertexShiftCorrection(bc); + areCorrectionsLoaded = true; + } - if (!(isAcceptedCollision(collision, true))) { + if (!(isAcceptedCollision(collision, true))) { return; } @@ -1025,42 +1235,92 @@ struct MftReassociationValidation { for (const auto& reassociated2dMftTrack : reassociated2dMftTracks) { - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); auto templatedMftTrack = reassociated2dMftTrack.template mfttrack_as(); o2::track::TrackParCovFwd trackPar = o2::aod::fwdtrackutils::getTrackParCovFwdShift(templatedMftTrack, mZShift); std::array dcaInfOrig; trackPar.propagateToDCAhelix(bZ, {collision.posX(), collision.posY(), collision.posZ()}, dcaInfOrig); - auto dcaXYoriginal = 999.f; - dcaXYoriginal = std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + dcaInfOrig[1] * dcaInfOrig[1]); + // auto dcaXYoriginal = 999.f; + // dcaXYoriginal = std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + dcaInfOrig[1] * dcaInfOrig[1]); - if (!isAcceptedMftTrack(templatedMftTrack, false, true, dcaXYoriginal, dcaInfOrig[2])) { + if (!isAcceptedMftTrack(templatedMftTrack, false, true, reassociated2dMftTrack.bestDCAXY(), dcaInfOrig[2])) { continue; } - registry.fill(HIST("Data/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); - - if (isAmbiguousMftTrack(reassociated2dMftTrack, true)) { - registry.fill(HIST("Data/hReassociation2dMftTracks"), Reassociation2dMftTracks::NotReassociated2dMftTracks); + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/2D/DcaFit/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated2dMftTrack.bestDCAXY()); } } // end of loop over reassociated MFT tracks - - // TO-DO the same for reassociated3d (change the histograms) } PROCESS_SWITCH(MftReassociationValidation, processData, "Process MFT reassociation validation for DATA", false); + void processData3d(FilteredCollisionsWSelMult::iterator const& collision, + FilteredMftTracks const& /*mftTracks*/, + soa::SmallGroups const& reassociated3dMftTracks, + aod::BCsWithTimestamps const&) + { + auto bc = collision.template bc_as(); + if (areCorrectionsLoaded == false) { + loadZVertexShiftCorrection(bc); + areCorrectionsLoaded = true; + } + + if (!isAcceptedCollision(collision, false)) { + return; + } + + for (const auto& reassociated3dMftTrack : reassociated3dMftTracks) { + auto templatedMftTrack = reassociated3dMftTrack.template mfttrack_as(); + + // // Compute DCA at original collision for isAcceptedMftTrack + // o2::track::TrackParCovFwd trackPar = + // o2::aod::fwdtrackutils::getTrackParCovFwdShift(templatedMftTrack, mZShift); + // std::array dcaInfOrig; + // trackPar.propagateToDCAhelix( + // bZ, + // {collision.posX(), collision.posY(), collision.posZ()}, + // dcaInfOrig); + // const float dcaXYoriginal = + // std::sqrt(dcaInfOrig[0] * dcaInfOrig[0] + + // dcaInfOrig[1] * dcaInfOrig[1]); + + // Use existing track selection function (unchanged) + if (!isAcceptedMftTrack(templatedMftTrack, false, true, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ())) { + continue; + } + + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/3D/DcaFit/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("Data/3D/DcaFit/hPtVsDcaZ"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + + // The same for nonambi only + if (reassociated3dMftTrack.ambDegree() == 1) { + if (configTask.fillDcaTemplateFit) { + registry.fill(HIST("Data/3D/DcaFit/NonAmb/hPtVsDcaXY"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("Data/3D/DcaFit/NonAmb/hPtVsDcaZ"), templatedMftTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } + } + } + PROCESS_SWITCH(MftReassociationValidation, processData3d, "Process DCA fit histograms for DATA 3D reassociation", false); + void processCreateLookupTable(FilteredCollisionsWSelMultMcLabels const& collisions, soa::Join const& mcCollisions) { recoVtxX.clear(); recoVtxY.clear(); recoVtxZ.clear(); + recoMultiplicity.clear(); + recoNumContrib.clear(); recoMcCollisionId.clear(); recoMcCollBestCollisionIndex.clear(); recoVtxX.reserve(collisions.size()); recoVtxY.reserve(collisions.size()); recoVtxZ.reserve(collisions.size()); + recoMultiplicity.reserve(collisions.size()); + recoNumContrib.reserve(collisions.size()); recoMcCollisionId.reserve(collisions.size()); recoMcCollBestCollisionIndex.reserve(mcCollisions.size()); @@ -1068,6 +1328,8 @@ struct MftReassociationValidation { recoVtxX.emplace(col.globalIndex(), col.posX()); recoVtxY.emplace(col.globalIndex(), col.posY()); recoVtxZ.emplace(col.globalIndex(), col.posZ()); + recoMultiplicity.emplace(col.globalIndex(), getMultiplicityEstimator(col)); + recoNumContrib.emplace(col.globalIndex(), col.numContrib()); recoMcCollisionId.emplace(col.globalIndex(), col.mcCollisionId()); } @@ -1085,18 +1347,21 @@ struct MftReassociationValidation { aod::BCsWithTimestamps const& /*bcs*/) { auto bc = collision.template bc_as(); - loadZVertexShiftCorrection(bc); + if (areCorrectionsLoaded == false) { + loadZVertexShiftCorrection(bc); + areCorrectionsLoaded = true; + } - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::AllMonteCarloEvents); - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::AllEventsPrecise); + registry.fill(HIST("MC/2D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::AllMonteCarloEvents); + registry.fill(HIST("MC/2D/hPreciseEventCounter"), SpecificEventSelectionStep::AllEventsPrecise); if (!collision.has_mcCollision()) { - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasNotMonteCarloCollision); + registry.fill(HIST("MC/2D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasNotMonteCarloCollision); return; } - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasMonteCarloCollision); - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::HasMcCollision); + registry.fill(HIST("MC/2D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasMonteCarloCollision); + registry.fill(HIST("MC/2D/hPreciseEventCounter"), SpecificEventSelectionStep::HasMcCollision); const int mcCollisionId = collision.mcCollisionId(); auto iteratorMcCollisionBestCollIndex = recoMcCollBestCollisionIndex.find(mcCollisionId); @@ -1109,13 +1374,13 @@ struct MftReassociationValidation { return; } - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNotSplitVertex); + registry.fill(HIST("MC/2D/hPreciseEventCounter"), SpecificEventSelectionStep::IsNotSplitVertex); - if (!isAcceptedCollision(collision, true)) { + if (!isAcceptedCollision(collision, true)) { return; } - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::MonteCarloEventsAfterEventSelection); + registry.fill(HIST("MC/2D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::MonteCarloEventsAfterEventSelection); for (auto const& reassociated2dMftTrack : reassociated2dMftTracks) { @@ -1128,10 +1393,10 @@ struct MftReassociationValidation { } } - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::AllMonteCarloTracks); - registry.fill(HIST("MC/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::AllTracksCheck); - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AllTracksPrecise); + registry.fill(HIST("MC/2D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); + registry.fill(HIST("MC/2D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::AllMonteCarloTracks); + registry.fill(HIST("MC/2D/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::AllTracksCheck); + registry.fill(HIST("MC/2D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AllTracksPrecise); auto templatedTrack = reassociated2dMftTrack.template mfttrack_as(); @@ -1144,35 +1409,42 @@ struct MftReassociationValidation { if (configMft.cutFakeTracks && templatedTrack.mcMask() != 0) { continue; } - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsTrueTrack); + registry.fill(HIST("MC/2D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsTrueTrack); if (configMft.cutOrphanTracksExplicitly && reassociated2dMftTrack.ambDegree() == 0) { continue; } - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterOrphanCut); - - if (!isAcceptedMftTrack(templatedTrack, true, false, dcaXYoriginal, dcaInfOrig[2])) { + registry.fill(HIST("MC/2D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterOrphanCut); + if (!isAcceptedMftTrack(templatedTrack, true, false, dcaXYoriginal, dcaInfOrig[2])) { continue; } if (reassociated2dMftTrack.ambDegree() == static_cast(reassociated2dMftTrack.compatibleCollIds().size())) { - registry.fill(HIST("MC/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::IsAmbDegreeEqualToCompatibleCollIdsSize); + registry.fill(HIST("MC/2D/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::IsAmbDegreeEqualToCompatibleCollIdsSize); } - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::MonteCarloTracksAfterTrackSelection); - + registry.fill(HIST("MC/2D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); + registry.fill(HIST("MC/2D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::MonteCarloTracksAfterTrackSelection); if (templatedTrack.has_mcParticle()) { - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasMonteCarloParticle); - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); + registry.fill(HIST("MC/2D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasMonteCarloParticle); + registry.fill(HIST("MC/2D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); auto particle = templatedTrack.template mcParticle_as(); + + if (configTask.fillDcaTemplateFit) { + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("MC/2D/DcaFit/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated2dMftTrack.bestDCAXY()); + } else { + registry.fill(HIST("MC/2D/DcaFit/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated2dMftTrack.bestDCAXY()); + } + } + float deltaX = -999.f; float deltaY = -999.f; float deltaZ = -999.f; float reassociatedDeltaX = -999.f; float reassociatedDeltaY = -999.f; float reassociatedDeltaZ = -999.f; - auto collision = templatedTrack.collision_as(); + // auto collision = templatedTrack.collision_as(); auto xPosTrue = reassociated2dMftTrack.mcParticle().mcCollision().posX(); auto yPosTrue = reassociated2dMftTrack.mcParticle().mcCollision().posY(); auto zPosTrue = reassociated2dMftTrack.mcParticle().mcCollision().posZ(); @@ -1186,6 +1458,8 @@ struct MftReassociationValidation { auto iteratorBestCollVtxX = recoVtxX.find(bestRecoColl); auto iteratorBestCollVtxY = recoVtxY.find(bestRecoColl); auto iteratorBestCollVtxZ = recoVtxZ.find(bestRecoColl); + auto iteratorBestCollMult = recoMultiplicity.find(bestRecoColl); + auto iteratorBestCollNumContrib = recoNumContrib.find(bestRecoColl); auto iteratorRecoMcCollisionId = recoMcCollisionId.find(bestRecoColl); if (iteratorOriginalCollVtxX == recoVtxX.end()) { @@ -1207,6 +1481,12 @@ struct MftReassociationValidation { if (iteratorBestCollVtxZ == recoVtxZ.end()) { continue; } + if (iteratorBestCollMult == recoMultiplicity.end()) { + continue; + } + if (iteratorBestCollNumContrib == recoNumContrib.end()) { + continue; + } if (iteratorRecoMcCollisionId == recoMcCollisionId.end()) { continue; } @@ -1217,6 +1497,8 @@ struct MftReassociationValidation { const float xPosBestColl = iteratorBestCollVtxX->second; const float yPosBestColl = iteratorBestCollVtxY->second; const float zPosBestColl = iteratorBestCollVtxZ->second; + const int bestCollMult = iteratorBestCollMult->second; + const int bestCollNumContrib = iteratorBestCollNumContrib->second; const int mcCollisionIdReco = iteratorRecoMcCollisionId->second; deltaX = xPosOriginalColl - xPosTrue; @@ -1233,110 +1515,221 @@ struct MftReassociationValidation { auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); if (reassociated2dMftTrack.ambDegree() > 1) { // AMBIGUOUS TRACKS - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); - registry.fill(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguous); - registry.fill(HIST("MC/hReassociation2dMftTracks"), Reassociation2dMftTracks::AllAmbiguousTracksAfterTrackSelectionsFor2d); - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); + registry.fill(HIST("MC/2D/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguous); + registry.fill(HIST("MC/2D/hReassociation2dMftTracks"), Reassociation2dMftTracks::AllAmbiguousTracksAfterTrackSelectionsFor2d); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } if (collision.mcCollisionId() == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndMatchedToTrueCollision); - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } } else { - registry.fill(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndNotMatchedToTrueCollision); - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } } if (templatedTrack.collisionId() == reassociated2dMftTrack.bestCollisionId()) { // IS NOT 2D REASSOCIATED - - registry.fill(HIST("MC/hReassociation2dMftTracks"), Reassociation2dMftTracks::NotReassociated2dMftTracks); - registry.fill(HIST("MC/hIsNot2dReassociatedAndMatchedToTrueCollision"), MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociated); - hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hReassociation2dMftTracks"), Reassociation2dMftTracks::NotReassociated2dMftTracks); + registry.fill(HIST("MC/2D/hIsNot2dReassociatedAndMatchedToTrueCollision"), MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociated); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + } if (mcCollisionIdReco == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIsNot2dReassociatedAndMatchedToTrueCollision"), MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndMatchedToTrueCollision); - hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsNot2dReassociatedAndMatchedToTrueCollision"), MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIsNot2dReassociatedAndMatchedToTrueCollision"), MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndNotMatchedToTrueCollision); - hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); - - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsNot2dReassociatedAndMatchedToTrueCollision"), MftNot2dReassociatedAndMatchedToTrueCollisionStep::IsNot2dReassociatedAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNot2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + } if (isTrueCollisionAmongCompatibleCollisions(reassociated2dMftTrack)) { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } } } else { // IS 2D REASSOCIATED - registry.fill(HIST("MC/hReassociation2dMftTracks"), Reassociation2dMftTracks::Reassociated2dMftTracks); - registry.fill(HIST("MC/hIs2dReassociatedAndMatchedToTrueCollision"), Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociated); - hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDca2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hReassociation2dMftTracks"), Reassociation2dMftTracks::Reassociated2dMftTracks); + registry.fill(HIST("MC/2D/hIs2dReassociatedAndMatchedToTrueCollision"), Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociated); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDca2dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + } // is collision.mcCollisionId() the reassociated collision vertex ? or the initial collision if (mcCollisionIdReco == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIs2dReassociatedAndMatchedToTrueCollision"), Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndMatchedToTrueCollision); - hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIs2dReassociatedAndMatchedToTrueCollision"), Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIs2dReassociatedAndMatchedToTrueCollision"), Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndNotMatchedToTrueCollision); - hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); - - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIs2dReassociatedAndMatchedToTrueCollision"), Mft2dReassociatedAndMatchedToTrueCollisionStep::Is2dReassociatedAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiff2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDca2dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), dcaXYtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + } if (isTrueCollisionAmongCompatibleCollisions(reassociated2dMftTrack)) { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/2D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks2D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated2dMftTrack.bestDCAXY(), 0, dcaXYtruth, 0); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } } } } else { // NON AMBI TRACKS - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); - registry.fill(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguous); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); + registry.fill(HIST("MC/2D/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguous); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNonAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaNonAmbiguousTracks2D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } if (collision.mcCollisionId() == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndMatchedToTrueCollision); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(collision.posX(), collision.posY(), collision.posZ(), getMultiplicityEstimator(collision), collision.numContrib()); + } } else { - registry.fill(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndNotMatchedToTrueCollision); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaNonAmbiguousTracks2D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated2dMftTrack.bestDCAXY(), 0.f); + fillCollisionParametersHistograms(collision.posX(), collision.posY(), collision.posZ(), getMultiplicityEstimator(collision), collision.numContrib()); + } } } // end of if non ambi } else { - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasNotMonteCarloParticle); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/2D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasNotMonteCarloParticle); + } } } // end of loop over reassociated2dMftTracks } @@ -1350,18 +1743,21 @@ struct MftReassociationValidation { aod::BCsWithTimestamps const& /*bcs*/) { auto bc = collision.template bc_as(); - loadZVertexShiftCorrection(bc); + if (areCorrectionsLoaded == false) { + loadZVertexShiftCorrection(bc); + areCorrectionsLoaded = true; + } - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::AllMonteCarloEvents); - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::AllEventsPrecise); + registry.fill(HIST("MC/3D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::AllMonteCarloEvents); + registry.fill(HIST("MC/3D/hPreciseEventCounter"), SpecificEventSelectionStep::AllEventsPrecise); if (!collision.has_mcCollision()) { - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasNotMonteCarloCollision); + registry.fill(HIST("MC/3D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasNotMonteCarloCollision); return; } - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasMonteCarloCollision); - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::HasMcCollision); + registry.fill(HIST("MC/3D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::HasMonteCarloCollision); + registry.fill(HIST("MC/3D/hPreciseEventCounter"), SpecificEventSelectionStep::HasMcCollision); const int mcCollisionId = collision.mcCollisionId(); auto iteratorMcCollisionBestCollIndex = recoMcCollBestCollisionIndex.find(mcCollisionId); @@ -1374,13 +1770,13 @@ struct MftReassociationValidation { return; } - registry.fill(HIST("hPreciseEventCounter"), SpecificEventSelectionStep::IsNotSplitVertex); + registry.fill(HIST("MC/3D/hPreciseEventCounter"), SpecificEventSelectionStep::IsNotSplitVertex); - if (!isAcceptedCollision(collision, true)) { + if (!isAcceptedCollision(collision, true)) { return; } - registry.fill(HIST("MC/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::MonteCarloEventsAfterEventSelection); + registry.fill(HIST("MC/3D/hMonteCarloEventCounter"), MonteCarloEventSelectionStep::MonteCarloEventsAfterEventSelection); for (auto const& reassociated3dMftTrack : reassociated3dMftTracks) { @@ -1393,10 +1789,10 @@ struct MftReassociationValidation { } } - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::AllMonteCarloTracks); - registry.fill(HIST("MC/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::AllTracksCheck); - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AllTracksPrecise); + registry.fill(HIST("MC/3D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AllMftTracks); + registry.fill(HIST("MC/3D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::AllMonteCarloTracks); + registry.fill(HIST("MC/3D/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::AllTracksCheck); + registry.fill(HIST("MC/3D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AllTracksPrecise); auto templatedTrack = reassociated3dMftTrack.template mfttrack_as(); @@ -1409,35 +1805,52 @@ struct MftReassociationValidation { if (configMft.cutFakeTracks && templatedTrack.mcMask() != 0) { continue; } - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsTrueTrack); + registry.fill(HIST("MC/3D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::IsTrueTrack); if (configMft.cutOrphanTracksExplicitly && reassociated3dMftTrack.ambDegree() == 0) { continue; } - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterOrphanCut); - - if (!isAcceptedMftTrack(templatedTrack, true, false, dcaXYoriginal, dcaInfOrig[2])) { + registry.fill(HIST("MC/3D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::AfterOrphanCut); + if (!isAcceptedMftTrack(templatedTrack, true, false, dcaXYoriginal, dcaInfOrig[2])) { continue; } if (reassociated3dMftTrack.ambDegree() == static_cast(reassociated3dMftTrack.compatibleCollIds().size())) { - registry.fill(HIST("MC/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::IsAmbDegreeEqualToCompatibleCollIdsSize); + registry.fill(HIST("MC/3D/hTrackAmbiguityCheck"), TrackAmbiguityCheckStep::IsAmbDegreeEqualToCompatibleCollIdsSize); } - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::MonteCarloTracksAfterTrackSelection); - + registry.fill(HIST("MC/3D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::AfterTrackSelection); + registry.fill(HIST("MC/3D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::MonteCarloTracksAfterTrackSelection); if (templatedTrack.has_mcParticle()) { - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasMonteCarloParticle); - registry.fill(HIST("MC/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); + registry.fill(HIST("MC/3D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasMonteCarloParticle); + registry.fill(HIST("MC/3D/hPreciseTrackSelectionCounter"), SpecificTrackSelectionStep::HasMcParticle); auto particle = templatedTrack.template mcParticle_as(); + + if (configTask.fillDcaTemplateFit) { + if (particle.isPhysicalPrimary()) { + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaZ_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + if (reassociated3dMftTrack.ambDegree() == 1) { + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_prim"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } else { + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/hPtVsDcaZ_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + if (reassociated3dMftTrack.ambDegree() == 1) { + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaXY_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAXY()); + registry.fill(HIST("MC/3D/DcaFit/NonAmb/hPtVsDcaZ_sec"), templatedTrack.pt(), reassociated3dMftTrack.bestDCAZ()); + } + } + } + float deltaX = -999.f; float deltaY = -999.f; float deltaZ = -999.f; float reassociatedDeltaX = -999.f; float reassociatedDeltaY = -999.f; float reassociatedDeltaZ = -999.f; - auto collision = templatedTrack.collision_as(); + // auto collision = templatedTrack.collision_as(); auto xPosTrue = reassociated3dMftTrack.mcParticle().mcCollision().posX(); auto yPosTrue = reassociated3dMftTrack.mcParticle().mcCollision().posY(); auto zPosTrue = reassociated3dMftTrack.mcParticle().mcCollision().posZ(); @@ -1451,6 +1864,8 @@ struct MftReassociationValidation { auto iteratorBestCollVtxX = recoVtxX.find(bestRecoColl); auto iteratorBestCollVtxY = recoVtxY.find(bestRecoColl); auto iteratorBestCollVtxZ = recoVtxZ.find(bestRecoColl); + auto iteratorBestCollMult = recoMultiplicity.find(bestRecoColl); + auto iteratorBestCollNumContrib = recoNumContrib.find(bestRecoColl); auto iteratorRecoMcCollisionId = recoMcCollisionId.find(bestRecoColl); if (iteratorOriginalCollVtxX == recoVtxX.end()) { @@ -1472,6 +1887,12 @@ struct MftReassociationValidation { if (iteratorBestCollVtxZ == recoVtxZ.end()) { continue; } + if (iteratorBestCollMult == recoMultiplicity.end()) { + continue; + } + if (iteratorBestCollNumContrib == recoNumContrib.end()) { + continue; + } if (iteratorRecoMcCollisionId == recoMcCollisionId.end()) { continue; } @@ -1482,6 +1903,8 @@ struct MftReassociationValidation { const float xPosBestColl = iteratorBestCollVtxX->second; const float yPosBestColl = iteratorBestCollVtxY->second; const float zPosBestColl = iteratorBestCollVtxZ->second; + const int bestCollMult = iteratorBestCollMult->second; + const int bestCollNumContrib = iteratorBestCollNumContrib->second; const int mcCollisionIdReco = iteratorRecoMcCollisionId->second; deltaX = xPosOriginalColl - xPosTrue; @@ -1498,109 +1921,218 @@ struct MftReassociationValidation { auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); if (reassociated3dMftTrack.ambDegree() > 1) { // AMBIGUOUS TRACKS - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); - registry.fill(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguous); - registry.fill(HIST("MC/hReassociation3dMftTracks"), Reassociation3dMftTracks::AllAmbiguousTracksAfterTrackSelectionsFor3d); - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfAmbiguousTracks); + registry.fill(HIST("MC/3D/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguous); + registry.fill(HIST("MC/3D/hReassociation3dMftTracks"), Reassociation3dMftTracks::AllAmbiguousTracksAfterTrackSelectionsFor3d); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } if (collision.mcCollisionId() == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndMatchedToTrueCollision); - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } } else { - registry.fill(HIST("MC/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndNotMatchedToTrueCollision); - hZVtxDiffAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsAmbiguousTrackMatchedToTrueCollision"), MftAmbiguousAndMatchedToTrueCollisionStep::IsAmbiguousAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } } if (templatedTrack.collisionId() == reassociated3dMftTrack.bestCollisionId()) { // IS NOT 3D REASSOCIATED - - registry.fill(HIST("MC/hReassociation3dMftTracks"), Reassociation3dMftTracks::NotReassociated3dMftTracks); - registry.fill(HIST("MC/hIsNot3dReassociatedAndMatchedToTrueCollision"), MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociated); - hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hReassociation3dMftTracks"), Reassociation3dMftTracks::NotReassociated3dMftTracks); + registry.fill(HIST("MC/3D/hIsNot3dReassociatedAndMatchedToTrueCollision"), MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociated); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } if (mcCollisionIdReco == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIsNot3dReassociatedAndMatchedToTrueCollision"), MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndMatchedToTrueCollision); - hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsNot3dReassociatedAndMatchedToTrueCollision"), MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIsNot3dReassociatedAndMatchedToTrueCollision"), MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndNotMatchedToTrueCollision); - hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsNot3dReassociatedAndMatchedToTrueCollision"), MftNot3dReassociatedAndMatchedToTrueCollisionStep::IsNot3dReassociatedAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNot3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } if (isTrueCollisionAmongCompatibleCollisions(reassociated3dMftTrack)) { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaNotReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } } } else { // IS 3D REASSOCIATED - - registry.fill(HIST("MC/hReassociation3dMftTracks"), Reassociation3dMftTracks::Reassociated3dMftTracks); - registry.fill(HIST("MC/hIs3dReassociatedAndMatchedToTrueCollision"), Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociated); - hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDca3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hReassociation3dMftTracks"), Reassociation3dMftTracks::Reassociated3dMftTracks); + registry.fill(HIST("MC/3D/hIs3dReassociatedAndMatchedToTrueCollision"), Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociated); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDca3dReassociatedTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } if (mcCollisionIdReco == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIs3dReassociatedAndMatchedToTrueCollision"), Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndMatchedToTrueCollision); - hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIs3dReassociatedAndMatchedToTrueCollision"), Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIs3dReassociatedAndMatchedToTrueCollision"), Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndNotMatchedToTrueCollision); - hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); - - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIs3dReassociatedAndMatchedToTrueCollision"), Mft3dReassociatedAndMatchedToTrueCollisionStep::Is3dReassociatedAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiff3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDca3dReassociatedTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::AllWronglyAssociatedTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } if (isTrueCollisionAmongCompatibleCollisions(reassociated3dMftTrack)) { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } else { - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - registry.fill(HIST("MC/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); - hZVtxDiffNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); - hDcaNotMatchedTracks[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisions"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + registry.fill(HIST("MC/3D/hIsTrueCollisionAmongCompatibleCollisionsDcaReassociated"), MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociatedDeltaX, reassociatedDeltaY, reassociatedDeltaZ); + hDcaNotMatchedTracks3D[MftIsTrueCollisionAmongCompatibleCollisionsStep::IsNotTrueCollisionAmongCompatibleCollisions]->Fill(templatedTrack.pt(), templatedTrack.eta(), reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ(), dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(xPosBestColl, yPosBestColl, zPosBestColl, bestCollMult, bestCollNumContrib); + } } } } } else { // NON AMBI TRACKS - - registry.fill(HIST("MC/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); - registry.fill(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguous); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hAmbiguityOfMftTracks"), MftTrackAmbiguityStep::NumberOfNonAmbiguousTracks); + registry.fill(HIST("MC/3D/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguous); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNonAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaNonAmbiguousTracks3D[MatchedToTrueCollisionStep::AllTracks]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } if (collision.mcCollisionId() == particle.mcCollisionId()) { - registry.fill(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndMatchedToTrueCollision); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(collision.posX(), collision.posY(), collision.posZ(), getMultiplicityEstimator(collision), collision.numContrib()); + } } else { - registry.fill(HIST("MC/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndNotMatchedToTrueCollision); - hZVtxDiffNonAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); - hDcaNonAmbiguousTracks[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hIsNonAmbiguousTrackMatchedToTrueCollision"), MftNonAmbiguousAndMatchedToTrueCollisionStep::IsNonAmbiguousAndNotMatchedToTrueCollision); + } + if (configTask.fillSpecificHistograms) { + hZVtxDiffNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), deltaX, deltaY, deltaZ); + hDcaNonAmbiguousTracks3D[MatchedToTrueCollisionStep::IsNotMatchedToTrueCollision]->Fill(templatedTrack.pt(), templatedTrack.eta(), dcaXYoriginal, dcaInfOrig[2], dcaXYtruth, dcaZtruth); + } + if (configTask.fillParametersHistograms) { + fillTrackParametersHistograms(templatedTrack, reassociated3dMftTrack.bestDCAXY(), reassociated3dMftTrack.bestDCAZ()); + fillCollisionParametersHistograms(collision.posX(), collision.posY(), collision.posZ(), getMultiplicityEstimator(collision), collision.numContrib()); + } } } // end of if non ambi } else { - registry.fill(HIST("MC/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasNotMonteCarloParticle); + if (configTask.fillGeneralHistograms) { + registry.fill(HIST("MC/3D/hMonteCarloTrackCounter"), MonteCarloTrackSelectionStep::HasNotMonteCarloParticle); + } } } // end of loop over reassociated3dMftTracks } diff --git a/PWGMM/Mult/Tasks/puremc-dndeta.cxx b/PWGMM/Mult/Tasks/puremc-dndeta.cxx index f634755a3dd..1891459aa4f 100644 --- a/PWGMM/Mult/Tasks/puremc-dndeta.cxx +++ b/PWGMM/Mult/Tasks/puremc-dndeta.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include using namespace o2; diff --git a/PWGMM/Mult/Tasks/qaReassocMFTPbPb.cxx b/PWGMM/Mult/Tasks/qaReassocMFTPbPb.cxx new file mode 100644 index 00000000000..f71e39a128c --- /dev/null +++ b/PWGMM/Mult/Tasks/qaReassocMFTPbPb.cxx @@ -0,0 +1,2114 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// +/// \file qaReassocMFTPbPb.cxx +/// \brief Task for performing reassociation checks in Pb-Pb collisions using MFT detector +/// \author Gyula Bencedi, gyula.bencedi@cern.ch +/// \since Aug 2026 + +#include "PWGMM/Mult/Core/include/Functions.h" +#include "PWGMM/Mult/DataModel/bestCollisionTable.h" + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/CollisionAssociationTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" +#include "Common/DataModel/Multiplicity.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::track; +using namespace o2::aod::fwdtrack; +using namespace o2::constants::physics; +using namespace o2::constants::math; +using namespace pwgmm::mult; +using namespace o2::aod::rctsel; + +auto static constexpr CminCharge = 3.f; +auto static constexpr CintZero = 0; + +enum class EvtSel { + evtAll = 0, + evtSel, + evtIsGoodZvtx, + evtNoSameBunchPileup, + evtZvtxCut, + evtNoCollInTimeRangeStd, + evtNoCollInTimeRangeNarrow, + evtNoCollInTimeRangeStrict, + evtNoCollInRofStrict, + evtNoCollInRofStandard, + evtNoHighMultCollInPrevRof, + evtGoodITSLayersAll, + evtBelowMinOccup, + evtAboveMaxOccup, + evtRCTFlagChecker, + evtRCTFlagCheckerExtra, + nEvtSel +}; + +enum class TrkSel { + trkSelAll = 0, + trkSelNCls, + trkSelChi2Ncl, + trkSelEta, + trkSelPhiCut, + trkSelPt, + trkSelCA, + nTrkSel +}; + +enum class TrkTrkBestSel { + trkTrkBestSelAll = 0, + trkTrkBestSelCollID, + trkTrkBestSelOrphan, + trkTrkBestSelDCAxyCut, + trkTrkBestSelDCAzCut, + trkTrkBestSelNumReassoc, + nTrkTrkBestSel +}; + +enum class AmbTrkType { + kAll = 0, + kNonAmb, + kOrphan, + kOrphanNull, + kNonAmbSame, + kAmb, + kAmbGt1, + nAmbTrkType +}; + +enum AmbTrkTypeAssocFlag { + kSelAll = 0, + kSelGoodVtx, + kSelGoodVtxTrue, + kSelBadVtx, + kSelBadVtxTrue, + kSelNonAmb, + kSelNonAmbGoodVtx, + kSelNonAmbGoodVtxTrue, + kSelNonAmbBadVtx, + kSelNonAmbBadVtxTrue, + kSelNonAmbID, + kSelNonAmbIDGoodVtxTrue, + kSelNonAmbIDBadVtxTrue, + kSelAmbID, + kSelAmbIDGoodVtxTrue, + kSelAmbIDBadVtxTrue, + kSelNonAmbIDExtra, + kSelNonAmbIDExtraGoodVtxTrue, + kSelNonAmbIDExtraBadVtxTrue, + kSelAmb, + kSelAmbGoodVtxTrue, + kSelAmbBadVtxTrue, + kSelOrphanNull, + nSelAmbTrkTypeAssocFlag +}; + +enum class VertexStatusMC { + kNull = 0, + kGood, + kBad +}; + +enum class AssocCheckVtxType { + kAllVtxTrue = 0, + kAllVtxFalse, + kAllGoodVtx, + kAllGoodVtxTrue, + kAllGoodVtxFalse, + kAllBadVtx, + kAllBadVtxTrue, + kAllBadVtxFalse, + nAssocVtxType +}; + +enum class ReassocCheckVtxType { + kIsTrueVtxAllTrue = 0, + kIsTrueVtxAllFalse, + kIsTrueVtxVsGoodVtxTrue, + kIsTrueVtxVsGoodVtxFalse, + kIsTrueVtxVsBadVtxTrue, + kIsTrueVtxVsBadVtxFalse, + kIsRecGoodAllTrue, + kIsRecGoodAllFalse, + kIsRecGoodMatchAllTrue, + kIsRecGoodMatchAllFalse, + kIsRecGoodVsGoodVtxTrue, + kIsRecGoodVsGoodVtxFalse, + kIsRecGoodMatchVsGoodVtxTrue, + kIsRecGoodMatchVsGoodVtxFalse, + kIsRecGoodVsBadVtxTrue, + kIsRecGoodVsBadVtxFalse, + kIsRecGoodMatchVsBadVtxTrue, + kIsRecGoodMatchVsBadVtxFalse, + nReassocVtxType +}; + +enum class ReAssocMCEventStatus { + kEvtReAsAll = 0, + kEvtReAsSelected, + kEvtReAsHasMcColl, + kEvtReAsSplitVtxRemoved, + kEvtReAsZVtxCutMC, + nEvtReAsReAssocMCEventStatus +}; + +enum class ReAssocMCTrackStatus { + kTrkReAssocAll = 0, + kTrkBestSel, + kTrkSel, + kTrkHasColl, + kTrkReassignedRemoved, + kTrkHasMcPart, + kTrkIdGt0, + kTrkNonAmb, + kTrkNonAmbGood, + kTrkNonAmbBad, + kTrkNonAmbID, + kTrkNonAmbIDGood, + kTrkNonAmbIDBad, + kTrkAmbID, + kTrkAmbIDGood, + kTrkAmbIDBad, + kTrkNonAmbIDExtra, + kTrkNonAmbIDExtraGood, + kTrkNonAmbIDExtraBad, + kReAssoc, + kReAssocGood, + kReAssocGoodIsCompTrue, + kReAssocGoodIsCompFalse, + kReAssocBad, + kReAssocBadIsCompTrue, + kReAssocBadIsCompFalse, + kAssoc, + kAssocGood, + kAssocGoodIsCompTrue, + kAssocGoodIsCompFalse, + kAssocBad, + kAssocBadIsCompTrue, + kAssocBadIsCompFalse, + nReAssocMCTrackStatusCheck +}; + +enum class HistStatusReAssocVtx { + kTrkRec = 0, + kTrkIdGt0, + kTrkNonAmb, + kTrkNonAmbGood, + kTrkNonAmbBad, + kTrkNonAmbID, + kTrkNonAmbIDGood, + kTrkNonAmbIDBad, + kTrkAmbID, + kTrkAmbIDGood, + kTrkAmbIDBad, + kTrkNonAmbIDExtra, + kTrkNonAmbIDExtraGood, + kTrkNonAmbIDExtraBad, + kReAssoc, + kReAssocGood, + kReAssocGoodIsCompTrue, + kReAssocGoodIsCompFalse, + kReAssocBad, + kReAssocBadIsCompTrue, + kReAssocBadIsCompFalse, + kAssoc, + kAssocGood, + kAssocGoodIsCompTrue, + kAssocGoodIsCompFalse, + kAssocBad, + kAssocBadIsCompTrue, + kAssocBadIsCompFalse, + nHistStatusReAssocVtx +}; + +struct QaReassocMFTPbPb { + + std::array, 4> hCollAssoc; + std::array, 28> hReAssocVtxRes; + std::array, 28> hReAssocDCA; + std::array, 21> hTimeAssocWithReassocMC; + + enum OccupancyEst { TrkITS = 1, + Ft0C }; + + HistogramRegistry registry{ + "registry", + {}, + OutputObjHandlingPolicy::AnalysisObject}; + HistogramRegistry qaregistry{ + "qaregistry", + {}, + OutputObjHandlingPolicy::AnalysisObject, + false, + true}; + + struct : ConfigurableGroup { + Configurable cfgUseTrackSel{"cfgUseTrackSel", false, "Flag to apply track selection"}; + Configurable cfgUseParticleSel{"cfgUseParticleSel", true, "Flag to apply particle selection"}; + Configurable cfgUsePrimaries{"cfgUsePrimaries", true, "Select primary particles"}; + Configurable cfgUseSecondaries{"cfgUseSecondaries", false, "Select secondary particles"}; + Configurable cfgRemoveTrivialAssoc{"cfgRemoveTrivialAssoc", false, "Skip trivial associations"}; + Configurable cfgRemoveAmbiguousTracks{"cfgRemoveAmbiguousTracks", false, "Remove ambiguous tracks"}; + Configurable cfgRemoveOrphanTracks{"cfgRemoveOrphanTracks", true, "Remove orphan tracks"}; + Configurable cfgRemoveReassigned{"cfgRemoveReassigned", false, "Remove reassgined tracks"}; + Configurable cfgRemoveSplitVertex{"cfgRemoveSplitVertex", true, "Remove split vertices"}; + Configurable grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"}; + } gConf; + + struct : ConfigurableGroup { + ConfigurableAxis occupancyBins{"occupancyBins", {VARIABLE_WIDTH, 0.0f, 250.0f, 500.0f, 750.0f, 1000.0f, 1500.0f, 2000.0f, 3000.0f, 4500.0f, 6000.0f, 8000.0f, 10000.0f, 50000.0f}, "Occupancy"}; + ConfigurableAxis centralityBins{"centralityBins", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}, "Centrality"}; + ConfigurableAxis ptBins{"ptBins", {101, -0.5, 10.5}, "pT binning (GeV/c)"}; + ConfigurableAxis multBins{"multBins", {1001, -0.5, 1000.5}, "Multiplicity binning"}; + ConfigurableAxis deltaZBins{"deltaZBins", {800, -10., 10.}, "Delta Z-vtx binning (cm)"}; + ConfigurableAxis dcaXYBins{"dcaXYBins", {800, -1., 1.}, "DCAxy binning (cm)"}; + ConfigurableAxis dcaZBins{"dcaZBins", {800, -1., 1.}, "DCAz binning (cm)"}; + ConfigurableAxis phiBins{"phiBins", {629, 0., TwoPI}, "#varphi binning (rad)"}; + ConfigurableAxis etaBins{"etaBins", {20, -4., -2.}, "#eta binning"}; + } binOpt; + + struct : ConfigurableGroup { + Configurable requireRCTFlagChecker{"requireRCTFlagChecker", false, "Check event quality in run condition table"}; + Configurable requireRCTFlagCheckerExtra{"requireRCTFlagCheckerExtra", true, "Check RCT flag extra"}; + Configurable cfgEvtRCTFlagCheckerLabel{"cfgEvtRCTFlagCheckerLabel", "CBT_fw", "Evt sel: RCT flag checker label"}; + Configurable cfgEvtRCTFlagCheckerZDCCheck{"cfgEvtRCTFlagCheckerZDCCheck", false, "Evt sel: RCT flag checker ZDC check"}; + Configurable cfgEvtRCTFlagCheckerLimitAcceptAsBad{"cfgEvtRCTFlagCheckerLimitAcceptAsBad", true, "Evt sel: RCT flag checker treat Limited Acceptance As Bad"}; + } rctCuts; + + struct : ConfigurableGroup { + Configurable usephiCut{"usephiCut", false, "use azimuthal angle cut"}; + Configurable phiCut{"phiCut", 0.1f, "Cut on azimuthal angle of MFT tracks"}; + Configurable minPhi{"minPhi", 0.f, ""}; + Configurable maxPhi{"maxPhi", 6.2832, ""}; + Configurable minEta{"minEta", -3.6f, ""}; + Configurable maxEta{"maxEta", -2.5f, ""}; + Configurable minNclusterMft{"minNclusterMft", 5, "minimum number of MFT clusters"}; + Configurable useChi2Cut{"useChi2Cut", true, "use track chi2 cut"}; + Configurable maxChi2NCl{"maxChi2NCl", 40.0f, "maximum chi2 per MFT clusters"}; + Configurable usePtCut{"usePtCut", false, "use track pT cut"}; + Configurable minPt{"minPt", 0., "minimum pT of the MFT tracks"}; + Configurable requireCA{"requireCA", false, "Use Cellular Automaton track-finding algorithm"}; + Configurable maxDCAxy{"maxDCAxy", 1.0f, "Cut on dca XY"}; + Configurable useDCAzCut{"useDCAzCut", true, "use dca Z cut"}; + Configurable maxDCAz{"maxDCAz", 1.0f, "Cut on dca Z"}; + Configurable selMcMask{"selMcMask", 0, "McMask for correct match"}; + } trackCuts; + + struct : ConfigurableGroup { + Configurable maxZvtx{"maxZvtx", 10.0f, "maximum cut on z-vtx (cm)"}; + Configurable minZvtx{"minZvtx", -10.0f, "minimum cut on z-vtx (cm)"}; + Configurable useZDiffCut{"useZDiffCut", false, "use Zvtx reco-mc diff. cut"}; + Configurable maxZvtxDiff{"maxZvtxDiff", 1.0f, "max allowed Z vtx difference for reconstruced collisions (cm)"}; + Configurable useZVtxCutMC{"useZVtxCutMC", false, "use Zvtx cut in MC"}; + Configurable requireIsGoodZvtxFT0VsPV{"requireIsGoodZvtxFT0VsPV", true, "require events with PV position along z consistent (within 1 cm) between PV reconstructed using tracks and PV using FT0 A-C time difference"}; + Configurable requireRejectSameBunchPileup{"requireRejectSameBunchPileup", true, "reject collisions in case of pileup with another collision in the same foundBC"}; + Configurable requireNoCollInTimeRangeStrict{"requireNoCollInTimeRangeStrict", false, " requireNoCollInTimeRangeStrict"}; + Configurable requireNoCollInRofStrict{"requireNoCollInRofStrict", true, "requireNoCollInRofStrict"}; + Configurable requireNoCollInRofStandard{"requireNoCollInRofStandard", true, "requireNoCollInRofStandard"}; + Configurable requireNoHighMultCollInPrevRof{"requireNoHighMultCollInPrevRof", true, "requireNoHighMultCollInPrevRof"}; + Configurable requireGoodITSLayersAll{"requireGoodITSLayersAll", true, "requireGoodITSLayersAll"}; + Configurable requireNoCollInTimeRangeStd{"requireNoCollInTimeRangeStd", true, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; + Configurable requireNoCollInTimeRangeNarrow{"requireNoCollInTimeRangeNarrow", false, "reject collisions corrupted by the cannibalism, with other collisions within +/- 10 microseconds"}; + Configurable occupancyEstimator{"occupancyEstimator", 1, "Occupancy estimator: 1 = trackOccupancyInTimeRange, 2 = ft0cOccupancyInTimeRange"}; + Configurable minOccupancy{"minOccupancy", -1, "minimum occupancy from neighbouring collisions"}; + Configurable maxOccupancy{"maxOccupancy", -1, "maximum occupancy from neighbouring collisions"}; + } eventCuts; + + Service pdg{}; + Service ccdb{}; + Configurable ccdbNoLaterThan{"ccdbNoLaterThan", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgApplyZShiftFromCCDB{"cfgApplyZShiftFromCCDB", false, "flag to apply z shift from CCDB"}; + Configurable cfgZShiftPath{"cfgZShiftPath", "Users/m/mcoquet/ZShift", "CCDB path for z shift to apply to forward tracks"}; + Configurable cfgManualZShift{"cfgManualZShift", 0.0f, "manual z-shift for propagation of global muon to PV"}; + + int mRunNumber{-1}; + uint64_t mSOR{0}; + float mMinSeconds{-1.}; + std::unordered_map gHadronicRate; + ctpRateFetcher rateFetcher; + TH2* gCurrentHadronicRate = nullptr; + RCTFlagsChecker rctChecker; + RCTFlagsChecker rctCheckerExtra{kFT0Bad, kITSBad, kTPCBadTracking, kMFTBad}; + + float bZ = 0; // Magnetic field for MFT + std::array centerMFT{0, 0, -61.4}; // Field at center of MFT + float mZShift = 0; // z-vertex shift + + o2::parameters::GRPMagField* grpmag = nullptr; + + /// @brief init function, definition of histograms + void init(InitContext&) + { + const AxisSpec centralityAxis = {binOpt.centralityBins, "Centrality", "centrality axis"}; + const AxisSpec occupancyAxis = {binOpt.occupancyBins, "Occupancy", "occupancy axis"}; + const AxisSpec ptAxis = {binOpt.ptBins, "Pt axis (GeV/c)"}; + const AxisSpec multAxis = {binOpt.multBins, "N_{trk} axis"}; + const AxisSpec deltaZAxis = {binOpt.deltaZBins, "Delta Z-vtx axis"}; + const AxisSpec dcaxyAxis = {binOpt.dcaXYBins, "DCA-xy axis"}; + const AxisSpec dcazAxis = {binOpt.dcaZBins, "DCA-z axis"}; + const AxisSpec etaAxis = {binOpt.etaBins, "#eta axis"}; + + rctChecker.init(rctCuts.cfgEvtRCTFlagCheckerLabel, rctCuts.cfgEvtRCTFlagCheckerZDCCheck, rctCuts.cfgEvtRCTFlagCheckerLimitAcceptAsBad); + + ccdb->setURL(ccdbUrl.value); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setCreatedNotAfter(ccdbNoLaterThan.value); + ccdb->setFatalWhenNull(false); + + qaregistry.add("Events/hNchTVX", "; status;", HistType::kTH2F, {multAxis, {2, 0, 2}}); + + registry.add("Events/hEvtSel", "Number of events; Cut; #Evt Passed Cut", {HistType::kTH1F, {{static_cast(EvtSel::nEvtSel), -0.5, +static_cast(EvtSel::nEvtSel) - 0.5}}}); + std::array(EvtSel::nEvtSel)> labelEvtSel{ + "All coll.", + "Sel 8", + "kIsGoodZvtxFT0vsPV", + "NoSameBunchPileup", + "Z-vtx cut", + "kNoCollInTimeRangeStd", + "kNoCollInTimeRangeNarrow", + "kNoCollInTimeRangeStrict", + "kNoCollInRofStrict", + "kNoCollInRofStandard", + "kNoHighMultCollInPrevRof", + "kIsGoodITSLayersAll", + "Below min occup.", + "Above max occup.", + "RCT Flag Checker", + "RCT Flag Checker Extra"}; + registry.get(HIST("Events/hEvtSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(EvtSel::nEvtSel); iBin++) { + registry.get(HIST("Events/hEvtSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelEvtSel[iBin].data()); + } + + registry.add("Tracks/hBestTrkSel", "Number of best tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkTrkBestSel::nTrkTrkBestSel), -0.5, +static_cast(TrkTrkBestSel::nTrkTrkBestSel) - 0.5}}}); + std::array(TrkTrkBestSel::nTrkTrkBestSel)> labelTrkTrkBestSel{ + "All", + "Assigned (ID>=0)", + "No orphans", + "DCA xy cut", + "DCA z cut", + "#Reassoc"}; + registry.get(HIST("Tracks/hBestTrkSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(TrkTrkBestSel::nTrkTrkBestSel); iBin++) { + registry.get(HIST("Tracks/hBestTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkTrkBestSel[iBin].data()); + } + + registry.add("Tracks/hTrkSel", "Number of tracks; Cut; #Tracks Passed Cut", {HistType::kTH1F, {{static_cast(TrkSel::nTrkSel), -0.5, +static_cast(TrkSel::nTrkSel) - 0.5}}}); + std::array(TrkSel::nTrkSel)> labelTrkSel{ + "All", + "Ncls", + "Chi2", + "Eta", + "Phi cut", + "Pt", + "CA"}; + registry.get(HIST("Tracks/hTrkSel"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(TrkSel::nTrkSel); iBin++) { + registry.get(HIST("Tracks/hTrkSel"))->GetXaxis()->SetBinLabel(iBin + 1, labelTrkSel[iBin].data()); + } + + if (doprocessTimeAssocMC) { + registry.add("TimeAssocMC/hTimeAssocMCEventStatus", ";status", {HistType::kTH1F, {{static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus), -0.5, +static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus) - 0.5}}}); + std::array(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus)> labelTimeAssocMCEventStatus{ + "All", + "Selected", + "Has Mc Coll", + "Split Vtx Removed", + "Vtx-z cut MC"}; + registry.get(HIST("TimeAssocMC/hTimeAssocMCEventStatus"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus); iBin++) { + registry.get(HIST("TimeAssocMC/hTimeAssocMCEventStatus"))->GetXaxis()->SetBinLabel(iBin + 1, labelTimeAssocMCEventStatus[iBin].data()); + } + + registry.add("TimeAssocMC/VtxStatus", ";status", {HistType::kTH1F, {{2, 0.5, 2.5}}}); + auto hstat = registry.get(HIST("TimeAssocMC/VtxStatus")); + hstat->GetXaxis()->SetBinLabel(1, "Good vtx"); + hstat->GetXaxis()->SetBinLabel(2, "Wrong vtx"); + + registry.add({"TimeAssocMC/hVertexResV1", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVertexResV2", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelBadVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmb", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbGoodVtx", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbBadVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbID", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbIDGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbIDBadVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelAmbID", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelAmbIDGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelAmbIDBadVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbIDExtra", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbIDExtraGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelNonAmbIDExtraBadVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelAmb", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelAmbGoodVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + registry.add({"TimeAssocMC/hVTXkSelAmbBadVtxTrue", "; #Delta X (cm); #Delta Y (cm); #Delta Z (cm)", {HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}}}); + + registry.add("TimeAssocMC/hTimeAssocCheckVtxType", ";status", {HistType::kTH1F, {{static_cast(AssocCheckVtxType::nAssocVtxType), -0.5, +static_cast(AssocCheckVtxType::nAssocVtxType) - 0.5}}}); + std::array(AssocCheckVtxType::nAssocVtxType)> labelAssocVtxType{ + "kAllVtx=True", + "kAllVtx=False", + "kAllGoodVtx", + "kAllGoodVtx=True", + "kAllGoodVtx=False", + "kAllBadVtx", + "kAllBadVtx=True", + "kAllBadVtx=False"}; + registry.get(HIST("TimeAssocMC/hTimeAssocCheckVtxType"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(AssocCheckVtxType::nAssocVtxType); iBin++) { + registry.get(HIST("TimeAssocMC/hTimeAssocCheckVtxType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAssocVtxType[iBin].data()); + } + + registry.add("TimeAssocMC/hAmbTrackType", ";status", {HistType::kTH1F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}}}); + std::array(AmbTrkType::nAmbTrkType)> labelAmbiguity{ + "all", + "orphan", + "orphanNull", + "nonAmb", + "nonAmbSame", + "Amb", + "AmbGt1"}; + registry.get(HIST("TimeAssocMC/hAmbTrackType"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { + registry.get(HIST("TimeAssocMC/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); + } + + registry.add("TimeAssocMC/hAmbTrkTypeAssocFlag", ";status", {HistType::kTH1F, {{static_cast(AmbTrkTypeAssocFlag::nSelAmbTrkTypeAssocFlag), -0.5, +static_cast(AmbTrkTypeAssocFlag::nSelAmbTrkTypeAssocFlag) - 0.5}}}); + std::array(AmbTrkTypeAssocFlag::nSelAmbTrkTypeAssocFlag)> lAmbTrackType{ + "all sel", + "all sel good vtx", + "all sel good vtx true", + "all sel bad vtx", + "all sel bad vtx true", + "all non-amb", + "non-amb good vtx", + "non-amb good vtx true", + "non-amb bad vtx", + "non-amb bad vtx true", + "non-amb id", + "non-amb id good vtx true", + "non-amb bad vtx true", + "amb id", + "amb id good vtx true", + "amb id bad vtx true", + "non-amb id ext", + "non-amb id ext good vtx true", + "non-amb id ext bad vtx true", + "amb all", + "amb good vtx true", + "amb bad vtx true", + "orhpan null"}; + registry.get(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(AmbTrkTypeAssocFlag::nSelAmbTrkTypeAssocFlag); iBin++) { + registry.get(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"))->GetXaxis()->SetBinLabel(iBin + 1, lAmbTrackType[iBin].data()); + } + } + + if (doprocessTimeAssocWithReassocMC) { + registry.add("TimeAssocWithReassocMC/VtxStatus", ";status", {HistType::kTH1F, {{2, 0.5, 2.5}}}); + auto hstat = registry.get(HIST("TimeAssocWithReassocMC/VtxStatus")); + hstat->GetXaxis()->SetBinLabel(1, "Good vtx"); + hstat->GetXaxis()->SetBinLabel(2, "Wrong vtx"); + + registry.add("TimeAssocWithReassocMC/hReassocCheckVtxType", ";status", {HistType::kTH1F, {{static_cast(ReassocCheckVtxType::nReassocVtxType), -0.5, +static_cast(ReassocCheckVtxType::nReassocVtxType) - 0.5}}}); + std::array(ReassocCheckVtxType::nReassocVtxType)> labelReAssocVtxType{ + "kIsTrueVtxAll=True", + "kIsTrueVtxAll=False", + "IsRecGoodAll=True", + "kIsRecGoodAll=False", + "kIsRecGoodMatchAll=True", + "kIsRecGoodMatchAll=False", + "kIsTrueVtxVsGoodVtx=True", + "kIsTrueVtxVsGoodVtx=False", + "kIsRecGoodVsGoodVtx=True", + "kIsRecGoodVsGoodVtx=False", + "kIsRecGoodMatchVsGoodVtx=True", + "kIsRecGoodMatchVsGoodVtx=False", + "kIsTrueVtxVsBadVtx=True", + "kIsTrueVtxVsBadVtx=False", + "kIsRecGoodVsBadVtx=True", + "kIsRecGoodVsBadVtx=False", + "kIsRecGoodMatchVsBadVtx=True", + "kIsRecGoodMatchVsBadVtx=False"}; + registry.get(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(ReassocCheckVtxType::nReassocVtxType); iBin++) { + registry.get(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"))->GetXaxis()->SetBinLabel(iBin + 1, labelReAssocVtxType[iBin].data()); + } + + hTimeAssocWithReassocMC[0] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrig", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[1] = registry.add("TimeAssocWithReassocMC/hDCAReassocBest", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[2] = registry.add("TimeAssocWithReassocMC/hDCAReassocTruth", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + + hTimeAssocWithReassocMC[3] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigTrueVtx", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[4] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[5] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodMatch", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + + hTimeAssocWithReassocMC[6] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigTrueVtxVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[7] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[8] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodMatchVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + + hTimeAssocWithReassocMC[9] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigTrueVtxVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[10] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[11] = registry.add("TimeAssocWithReassocMC/hDCAReassocOrigRecGoodMatchVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + + hTimeAssocWithReassocMC[12] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestTrueVtx", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[13] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[14] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodMatch", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + + hTimeAssocWithReassocMC[15] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestTrueVtxVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[16] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[17] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodMatchVtxFlagGood", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + + hTimeAssocWithReassocMC[18] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestTrueVtxVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[19] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + hTimeAssocWithReassocMC[20] = registry.add("TimeAssocWithReassocMC/hDCAReassocBestRecGoodMatchVtxFlagBad", ";#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {deltaZAxis, deltaZAxis, deltaZAxis}); + } + + if (doprocessAssocMC) { + registry.add("TrackToColl/hAmbTrackType", "hAmbTrackType", {HistType::kTH1F, {{static_cast(AmbTrkType::nAmbTrkType), -0.5, +static_cast(AmbTrkType::nAmbTrkType) - 0.5}}}); + std::array(AmbTrkType::nAmbTrkType)> labelAmbiguity{ + "all", + "orphan", + "orphanNull", + "nonAmb", + "nonAmbSame", + "Amb", + "AmbGt1"}; + registry.get(HIST("TrackToColl/hAmbTrackType"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(AmbTrkType::nAmbTrkType); iBin++) { + registry.get(HIST("TrackToColl/hAmbTrackType"))->GetXaxis()->SetBinLabel(iBin + 1, labelAmbiguity[iBin].data()); + } + + // tracks not associated to any collision + hCollAssoc[0] = registry.add("TrackToColl/hNonAssocTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); + // tracks associasted to a collision + hCollAssoc[1] = registry.add("TrackToColl/hAssocTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); + // tracks associated to the correct collision considering only first reco collision (based on the MC collision index) + hCollAssoc[2] = registry.add("TrackToColl/hGoodAssocTracks", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); + // tracks associated to the correct collision considering all ambiguous reco collisions (based on the MC collision index) + hCollAssoc[3] = registry.add("TrackToColl/hGoodAssocTracksAmb", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm)", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis}); + + registry.add("TrackToColl/histFracTracksFakeMcColl", "Fraction of tracks originating from fake collision; fraction; entries", {HistType::kTH1F, {{101, 0., 1.01}}}); + registry.add("TrackToColl/histFracGoodTracks", "Fraction of tracks originating from the correct collision; fraction; entries", {HistType::kTH1F, {{101, 0., 1.01}}}); + registry.add("TrackToColl/histAmbTrackNumColls", "Number of collisions associated to an ambiguous track; no. collisions; entries", {HistType::kTH1F, {{30, -0.5, 29.5}}}); + registry.add("TrackToColl/histTrackNumColls", "Number of collisions associated to track; no. collisions; entries", {HistType::kTH1F, {{30, -0.5, 29.5}}}); + registry.add("TrackToColl/histNonAmbTrackNumColls", "Number of collisions associated to non-ambiguous track; no. collisions; entries", {HistType::kTH1F, {{30, -0.5, 29.5}}}); + registry.add("TrackToColl/histAmbTrackZvtxRMS", "RMS of #it{Z}^{reco} of collisions associated to a track; RMS(#it{Z}^{reco}) (cm); entries", {HistType::kTH1F, {{100, 0., 10.}}}); + } + + if (doprocessReAssoc3dMC) { + registry.add({"ReAssocMC/EvtGenRecReassoc", ";status;centrality;occupancy", {HistType::kTHnSparseF, {{3, 0.5, 3.5}, centralityAxis, occupancyAxis}}}); + auto heff = registry.get(HIST("ReAssocMC/EvtGenRecReassoc")); + heff->GetAxis(0)->SetBinLabel(1, "All generated"); + heff->GetAxis(0)->SetBinLabel(2, "All reconstructed"); + heff->GetAxis(0)->SetBinLabel(3, "Selected reconstructed"); + + registry.add({"ReAssocMC/hReAssocMCEventStatus", ";status;centrality;occupancy", {HistType::kTHnSparseF, {{static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus), -0.5, +static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus) - 0.5}, centralityAxis, occupancyAxis}}}); + std::array(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus)> labelReAssocMCEventStatus{ + "All", + "Selected", + "Has Mc Coll", + "Split Vtx Removed", + "Vtx-z cut MC"}; + // registry.get(HIST("ReAssocMC/hReAssocMCEventStatus"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(ReAssocMCEventStatus::nEvtReAsReAssocMCEventStatus); iBin++) { + registry.get(HIST("ReAssocMC/hReAssocMCEventStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelReAssocMCEventStatus[iBin].data()); + } + + registry.add({"ReAssocMC/hReAssocMCTrackStatus", ";status;centrality;occupancy", {HistType::kTHnSparseF, {{static_cast(ReAssocMCTrackStatus::nReAssocMCTrackStatusCheck), -0.5, +static_cast(ReAssocMCTrackStatus::nReAssocMCTrackStatusCheck) - 0.5}, centralityAxis, occupancyAxis}}}); + std::array(ReAssocMCTrackStatus::nReAssocMCTrackStatusCheck)> labelReAssocMCTrackStatus{ + "All", + "Best sel", + "Trk sel", + "Has coll", + "Reas rm", + "Has part", + "Trk idGt0", + "Non-amb", + "Non-amb good coll.", + "Non-amb bad coll.", + "Non-amb id", + "Non-amb id good", + "Non-amb id bad", + "Amb id", + "Amb id good", + "Amb id bad", + "Non-amb id ex", + "Non-amb id ex good", + "Non-amb id ex bad", + "ReAssoc", + "ReAssoc good", + "ReAssoc good Comp True", + "ReAssoc good Comp False", + "ReAssoc bad", + "ReAssoc bad Comp True", + "ReAssoc bad Comp False", + "Assoc (gt1 amb)", + "Assoc good", + "Assoc good Comp True", + "Assoc good Comp False", + "Assoc bad", + "Assoc bad Comp True", + "Assoc bad Comp False"}; + // registry.get(HIST("ReAssocMC/hReAssocMCTrackStatus"))->SetMinimum(0.1); + for (int iBin = 0; iBin < static_cast(ReAssocMCTrackStatus::nReAssocMCTrackStatusCheck); iBin++) { + registry.get(HIST("ReAssocMC/hReAssocMCTrackStatus"))->GetAxis(0)->SetBinLabel(iBin + 1, labelReAssocMCTrackStatus[iBin].data()); + } + + // Vertex resolution + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkRec)] = registry.add("ReAssocMC/hVtxResRec", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkIdGt0)] = registry.add("ReAssocMC/hVtxResIdGt0", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmb)] = registry.add("ReAssocMC/hVtxResNonAmb", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)] = registry.add("ReAssocMC/hVtxResNonAmbGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)] = registry.add("ReAssocMC/hVtxResNonAmbBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbID)] = registry.add("ReAssocMC/hVtxResNonAmbID", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDGood)] = registry.add("ReAssocMC/hVtxResNonAmbIDGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDBad)] = registry.add("ReAssocMC/hVtxResNonAmbIDBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbID)] = registry.add("ReAssocMC/hVtxResAmbID", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbIDGood)] = registry.add("ReAssocMC/hVtxResAmbIDGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbIDBad)] = registry.add("ReAssocMC/hVtxResAmbIDBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtra)] = registry.add("ReAssocMC/hVtxResNonAmbIDExtra", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraGood)] = registry.add("ReAssocMC/hVtxResNonAmbIDExtraGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraBad)] = registry.add("ReAssocMC/hVtxResNonAmbIDExtraBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssoc)] = registry.add("ReAssocMC/hVtxResReAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGood)] = registry.add("ReAssocMC/hVtxResReAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hVtxResReAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hVtxResReAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBad)] = registry.add("ReAssocMC/hVtxResReAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)] = registry.add("ReAssocMC/hVtxResReAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)] = registry.add("ReAssocMC/hVtxResReAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssoc)] = registry.add("ReAssocMC/hVtxResAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGood)] = registry.add("ReAssocMC/hVtxResAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hVtxResAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hVtxResAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBad)] = registry.add("ReAssocMC/hVtxResAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)] = registry.add("ReAssocMC/hVtxResAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)] = registry.add("ReAssocMC/hVtxResAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};#it{X}_{vtx}^{reco}#minus#it{X}_{vtx}^{gen} (cm);#it{Y}_{vtx}^{reco}#minus#it{Y}_{vtx}^{gen} (cm);#it{Z}_{vtx}^{reco}#minus#it{Z}_{vtx}^{gen} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, deltaZAxis, deltaZAxis, deltaZAxis, centralityAxis, occupancyAxis}); + + // DCA + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkRec)] = registry.add("ReAssocMC/hDCARec", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkIdGt0)] = registry.add("ReAssocMC/hDCAIdGt0", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmb)] = registry.add("ReAssocMC/hDCANonAmbAll", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)] = registry.add("ReAssocMC/hDCANonAmbGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)] = registry.add("ReAssocMC/hDCANonAmbBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbID)] = registry.add("ReAssocMC/hDCAAmbAll", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDGood)] = registry.add("ReAssocMC/hDCAAmbGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDBad)] = registry.add("ReAssocMC/hDCAAmbBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbID)] = registry.add("ReAssocMC/hDCANonAmbAllE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbIDGood)] = registry.add("ReAssocMC/hDCANonAmbGoodE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbIDBad)] = registry.add("ReAssocMC/hDCANonAmbBadE", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtra)] = registry.add("ReAssocMC/hDCANonAmbIDExtra", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraGood)] = registry.add("ReAssocMC/hDCANonAmbIDExtraGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraBad)] = registry.add("ReAssocMC/hDCANonAmbIDExtraBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssoc)] = registry.add("ReAssocMC/hDCAReAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGood)] = registry.add("ReAssocMC/hDCAReAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hDCAReAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hDCAReAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBad)] = registry.add("ReAssocMC/hDCAReAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)] = registry.add("ReAssocMC/hDCAReAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)] = registry.add("ReAssocMC/hDCAReAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssoc)] = registry.add("ReAssocMC/hDCAAssoc", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGood)] = registry.add("ReAssocMC/hDCAAssocGood", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)] = registry.add("ReAssocMC/hDCAAssocGoodIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)] = registry.add("ReAssocMC/hDCAAssocGoodIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBad)] = registry.add("ReAssocMC/hDCAAssocBad", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)] = registry.add("ReAssocMC/hDCAAssocBadIsCompTrue", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)] = registry.add("ReAssocMC/hDCAAssocBadIsCompFalse", ";#it{p}_{T}^{reco} (GeV/#it{c});#it{#eta}^{reco};DCA_{XY} (cm)^{reco}; DCA_{Z} (cm)^{reco}; DCA_{XY} (cm); DCA_{Z} (cm); centrality; occupancy", HistType::kTHnSparseF, {ptAxis, etaAxis, dcaxyAxis, dcazAxis, dcaxyAxis, dcazAxis, centralityAxis, occupancyAxis}); + } + } + + using ExtBCs = soa::Join; + using CollsGenCentFT0CExtra = soa::Join; + using MftTracksWCollsMC = soa::Join; + using BestTracks3dWCollsMC = soa::Join; + + template + float getDCAz(const T& track) + { + if constexpr (requires { track.bestDCAZ(); }) { + return track.bestDCAZ(); + } else { + return 999.; + } + } + + std::unordered_set setRecCollSel; + std::unordered_map mapVtxXrec; + std::unordered_map mapVtxYrec; + std::unordered_map mapVtxZrec; + std::unordered_map mapVtxXgen; + std::unordered_map mapVtxYgen; + std::unordered_map mapVtxZgen; + std::unordered_map mapMcCollIdPerRecColl; + std::unordered_map mapMcToRec; + + template + void buildLookupTable(C const& collisions, MC const& mcCollisions) + { + const auto& nMcColls = mcCollisions.size(); + const auto& nRecoColls = collisions.size(); + + setRecCollSel.clear(); + setRecCollSel.reserve(nRecoColls); + mapVtxXgen.clear(); + mapVtxXgen.reserve(nMcColls); + mapVtxYgen.clear(); + mapVtxYgen.reserve(nMcColls); + mapVtxZgen.clear(); + mapVtxZgen.reserve(nMcColls); + mapVtxXrec.clear(); + mapVtxXrec.reserve(nRecoColls); + mapVtxYrec.clear(); + mapVtxYrec.reserve(nRecoColls); + mapVtxZrec.clear(); + mapVtxZrec.reserve(nRecoColls); + mapMcCollIdPerRecColl.clear(); + mapMcCollIdPerRecColl.reserve(nRecoColls); + mapMcToRec.clear(); + mapMcToRec.reserve(nRecoColls); + + int maxNcontributors = -1; + for (auto const& collision : collisions) { + // const int nContrib = collision.multPVTotalContributors(); + const int nContrib = collision.numContrib(); + if (maxNcontributors < nContrib) { + maxNcontributors = nContrib; + setRecCollSel.insert(collision.globalIndex()); + } + mapVtxXrec.emplace(collision.globalIndex(), collision.posX()); + mapVtxYrec.emplace(collision.globalIndex(), collision.posY()); + mapVtxZrec.emplace(collision.globalIndex(), collision.posZ()); + mapMcCollIdPerRecColl.emplace(collision.globalIndex(), collision.mcCollisionId()); + mapMcToRec.emplace(collision.mcCollisionId(), collision.globalIndex()); + } + for (const auto& mcCollision : mcCollisions) { + const auto mcId = mcCollision.globalIndex(); + mapVtxXgen.emplace(mcId, mcCollision.posX()); + mapVtxYgen.emplace(mcId, mcCollision.posY()); + mapVtxZgen.emplace(mcId, mcCollision.posZ()); + } + } + + /// \brief check good rec vertex of corresponding mc coll is available in compatible rec coll + template + bool isRecInCompColl(AT const& atrack) + { + auto const& ids = atrack.compatibleCollIds(); + bool isInCoColl = false; + if (atrack.ambDegree() != 0) { + const int mcCollId = atrack.mcParticle().mcCollisionId(); + if (!ids.empty()) { + for (auto const& id : ids) { + auto itMcCollId = mapMcCollIdPerRecColl.find(id); + if (itMcCollId != mapMcCollIdPerRecColl.end()) { + if (itMcCollId->second == mcCollId) { + isInCoColl = true; + return isInCoColl; + } + } else { + return isInCoColl; + } + } + } + } + return isInCoColl; + } + + void initCCDB(ExtBCs::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + + grpmag = ccdb->getForTimeStamp(gConf.grpmagPath, bc.timestamp()); + LOG(info) << "Setting magnetic field to current " << grpmag->getL3Current() + << " A for run " << bc.runNumber() + << " from its GRPMagField CCDB object"; + o2::base::Propagator::initFieldFromGRP(grpmag); + mRunNumber = bc.runNumber(); + + auto field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); + bZ = field->getBz(centerMFT.data()); + LOG(info) << "The field at the center of the MFT is bZ = " << bZ; + + if (cfgApplyZShiftFromCCDB) { + auto* zShift = ccdb->getForTimeStamp>(cfgZShiftPath, bc.timestamp()); + if (zShift != nullptr && !zShift->empty()) { + LOGF(info, "reading z shift %f from %s", (*zShift)[0], cfgZShiftPath.value); + mZShift = (*zShift)[0]; + } else { + LOGF(info, "z shift is not found in ccdb path %s. set to 0 cm", cfgZShiftPath.value); + mZShift = 0; + } + } else { + LOGF(info, "z shift is manually set to %f cm", cfgManualZShift.value); + mZShift = cfgManualZShift; + } + } + + /// \brief RMS calculation + /// \param vec vector of values to compute RMS + template + T computeRMS(std::vector& vec) + { + T sum = std::accumulate(vec.begin(), vec.end(), 0.0); + T mean = sum / vec.size(); + + std::vector diff(vec.size()); + std::transform(vec.begin(), vec.end(), diff.begin(), [mean](T x) { return x - mean; }); + T sqSum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); + T stdev = std::sqrt(sqSum / vec.size()); + + return stdev; + } + + template + bool isBestTrackSelected(const B& besttrack) + { + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelAll)); + } + if (besttrack.bestCollisionId() < CintZero) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelCollID)); + } + if (besttrack.ambDegree() == CintZero) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelOrphan)); + } + if (std::abs(besttrack.bestDCAXY()) >= trackCuts.maxDCAxy) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelDCAxyCut)); + } + if (trackCuts.useDCAzCut) { + const float bestDcaZ = getDCAz(besttrack); + if (std::abs(bestDcaZ) >= trackCuts.maxDCAz) { + return false; + } + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hBestTrkSel"), static_cast(TrkTrkBestSel::trkTrkBestSelDCAzCut)); + } + return true; + } + + template + bool isTrackSelected(const T& track) + { + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelAll)); + } + if (track.nClusters() < trackCuts.minNclusterMft) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelNCls)); + } + if (trackCuts.useChi2Cut) { + float nclMft = std::max(2.0f * track.nClusters() - 5.0f, 1.0f); + float mftChi2NCl = track.chi2() / nclMft; + if (mftChi2NCl > trackCuts.maxChi2NCl) { + return false; + } + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelChi2Ncl)); + } + if (track.eta() < trackCuts.minEta || track.eta() > trackCuts.maxEta) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelEta)); + } + if (trackCuts.usephiCut) { + float phi = track.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { + return false; + } + if ((phi < trackCuts.phiCut) || + ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || + (phi > TwoPI - trackCuts.phiCut) || + ((phi > ((PIHalf - 0.1) * PI) - trackCuts.phiCut) && + (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut))) { + return false; + } + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPhiCut)); + } + if (trackCuts.usePtCut && track.pt() < trackCuts.minPt) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelPt)); + } + if (trackCuts.requireCA && !track.isCA()) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Tracks/hTrkSel"), static_cast(TrkSel::trkSelCA)); + } + return true; + } + + template + float getOccupancy(C const& collision, uint occEstimator) + { + switch (occEstimator) { + case OccupancyEst::TrkITS: + return collision.trackOccupancyInTimeRange(); + case OccupancyEst::Ft0C: + return collision.ft0cOccupancyInTimeRange(); + default: + LOG(fatal) << "No valid occupancy estimator "; + break; + } + return -1.f; + } + + template + bool isGoodEvent(C const& collision) + { + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAll)); + } + if (!collision.sel8()) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtSel)); + } + if (eventCuts.requireIsGoodZvtxFT0VsPV && !collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtIsGoodZvtx)); + } + if (eventCuts.requireRejectSameBunchPileup && !collision.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoSameBunchPileup)); + } + if (collision.posZ() <= eventCuts.minZvtx || collision.posZ() >= eventCuts.maxZvtx) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtZvtxCut)); + } + if (eventCuts.requireNoCollInTimeRangeStd && + !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStd)); + } + if (eventCuts.requireNoCollInTimeRangeNarrow && + !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeNarrow)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeNarrow)); + } + if (eventCuts.requireNoCollInTimeRangeStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStrict)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInTimeRangeStrict)); + } + if (eventCuts.requireNoCollInRofStrict && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStrict)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStrict)); + } + if (eventCuts.requireNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoCollInRofStandard)); + } + if (eventCuts.requireNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtNoHighMultCollInPrevRof)); + } + if (eventCuts.requireGoodITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtGoodITSLayersAll)); + } + if (eventCuts.minOccupancy >= 0 && + getOccupancy(collision, eventCuts.occupancyEstimator) < + eventCuts.minOccupancy) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtBelowMinOccup)); + } + if (eventCuts.maxOccupancy >= 0 && + getOccupancy(collision, eventCuts.occupancyEstimator) > + eventCuts.maxOccupancy) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtAboveMaxOccup)); + } + if (rctCuts.requireRCTFlagChecker && !rctChecker(collision)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagChecker)); + } + if (rctCuts.requireRCTFlagCheckerExtra && !rctCheckerExtra(collision)) { + return false; + } + if constexpr (fillHis) { + registry.fill(HIST("Events/hEvtSel"), static_cast(EvtSel::evtRCTFlagCheckerExtra)); + } + return true; + } + + template + bool isParticleSelected(P const& particle) + { + if (gConf.cfgUsePrimaries && !particle.isPhysicalPrimary()) { + return false; + } + if (!gConf.cfgUsePrimaries && (gConf.cfgUseSecondaries && particle.isPhysicalPrimary())) { + return false; + } + if (particle.eta() < trackCuts.minEta || particle.eta() > trackCuts.maxEta) { + return false; + } + if (trackCuts.usephiCut) { + float phi = particle.phi(); + o2::math_utils::bringTo02Pi(phi); + if (phi < trackCuts.minPhi || trackCuts.maxPhi < phi) { + return false; + } + if ((phi < trackCuts.phiCut) || + ((phi > PI - trackCuts.phiCut) && (phi < PI + trackCuts.phiCut)) || + (phi > TwoPI - trackCuts.phiCut) || + ((phi > ((PIHalf - 0.1) * PI) - trackCuts.phiCut) && + (phi < ((PIHalf - 0.1) * PI) + trackCuts.phiCut))) { + return false; + } + } + return true; + } + + /// @brief Selection of charged particles + /// @return true: charged; false: not charged + bool isChrgParticle(int code) + { + auto p = pdg->GetParticle(code); + auto charge = 0.; + if (p != nullptr) { + charge = p->Charge(); + } + return std::abs(charge) >= CminCharge; + } + + void processTimeAssocMC(MftTracksWCollsMC const& tracks, + aod::McParticles const& /*particles*/, + CollsGenCentFT0CExtra const& collisions, + aod::McCollisions const& mcCollisions) + { + buildLookupTable(collisions, mcCollisions); + + int nNoMC{0}; + for (const auto& collision : collisions) { + registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsAll)); + if (!isGoodEvent(collision)) { + continue; + } + registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSelected)); + if (!collision.has_mcCollision()) { + continue; + } + int64_t recCollId = collision.globalIndex(); + auto itMC = mapMcCollIdPerRecColl.find(recCollId); + if (itMC == mapMcCollIdPerRecColl.end()) { + nNoMC++; + LOGP(debug, "collison {} has no MC coll", recCollId); + continue; + } + registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsHasMcColl)); + auto mcCollision = collision.mcCollision_as(); + if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + continue; + } + registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSplitVtxRemoved)); + if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + registry.fill(HIST("TimeAssocMC/hTimeAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsZVtxCutMC)); + + // Loop on collision compatible MFT tracks + // Check: (1) good/bad vertices (2) if bad true vertex is available among rec vertices + for (const auto& track : tracks) { + if (!track.has_collision()) { + continue; + } + auto trkCollId = track.has_collision() ? track.collisionId() : -1; + auto ids = track.compatibleCollIds(); + // check if track is associated to rec coll + // if (trkCollId != recCollId) { + // continue; + // } + if (!setRecCollSel.contains(trkCollId)) { + continue; + } + registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kAll)); + if (ids.empty()) { + registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kOrphan)); + } + if (gConf.cfgRemoveOrphanTracks && ids.empty()) { + continue; + } + if (gConf.cfgRemoveTrivialAssoc) { + if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { + continue; + } + } + if (gConf.cfgRemoveAmbiguousTracks && (track.compatibleCollIds().size() != 1)) { + continue; + } + + if (!ids.empty()) { + if (ids.size() == 1) { + if (trkCollId == ids[0]) { + registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb)); + } else if (trkCollId != ids[0]) { + registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kAmb)); + } else { + registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame)); + } + } else { + registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1)); + } + } else { + registry.fill(HIST("TimeAssocMC/hAmbTrackType"), static_cast(AmbTrkType::kOrphanNull)); + } + if (gConf.cfgUseTrackSel && !isTrackSelected(track)) { + continue; + } + + bool isTrueVtx = false; + int vtxFlag = static_cast(VertexStatusMC::kNull); + + float vtxX = -1.; + float vtxY = -1.; + float vtxZ = -1.; + float deltaXv1 = -1.; + float deltaYv1 = -1.; + float deltaZv1 = -1.; + float deltaXv2 = -1.; + float deltaYv2 = -1.; + float deltaZv2 = -1.; + + if (track.collisionId() >= 0 && track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { + const auto& mcPart = track.mcParticle(); + if (!isChrgParticle(mcPart.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(mcPart)) { + continue; + } + int64_t mcPartId = mcPart.mcCollisionId(); + // check if rec vertex is available in MC collisions + for (const auto& mcTrkId : mapMcToRec) { + if (mcTrkId.second == mcPartId) { + isTrueVtx = true; + break; + } + } + // check if there is good or bad collision + auto itMCTrk = mapMcCollIdPerRecColl.find(trkCollId); + if (itMCTrk != mapMcCollIdPerRecColl.end()) { + int mcTrkCollId = itMCTrk->second; + if (mcPartId == mcTrkCollId) { // particle.mcCollisionId == collision.mcCollisionId -> good vtx + vtxFlag = static_cast(VertexStatusMC::kGood); + } else { // wrong vtx + vtxFlag = static_cast(VertexStatusMC::kBad); + } + } + if (!mapVtxXrec.contains(trkCollId)) { + continue; + } + if (!mapVtxYrec.contains(trkCollId)) { + continue; + } + if (!mapVtxZrec.contains(trkCollId)) { + continue; + } + if (!mapMcCollIdPerRecColl.contains(trkCollId)) { + continue; + } + vtxX = mapVtxXrec.find(trkCollId)->second; + vtxY = mapVtxYrec.find(trkCollId)->second; + vtxZ = mapVtxZrec.find(trkCollId)->second; + // LOGP(info, "\t ---> \t .... \t vtxZrec: {} - collision.posZ(): {}", vtxZrec, collision.posZ()); + int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(trkCollId)->second; + // int64_t mcCollId = itMC->second; + // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - mcCollId: {} - bestMCCol: {}", mcCollIdRec, mcCollId, bestMCCol); + if (!mapVtxXgen.contains(mcCollIdRec)) { + continue; + } + if (!mapVtxYgen.contains(mcCollIdRec)) { + continue; + } + if (!mapVtxZgen.contains(mcCollIdRec)) { + continue; + } + // vertex resolution - ver 1 + // rec coll vtx - mc associated to orig rec coll (first in time) + deltaXv1 = vtxX - mcCollision.posX(); + deltaYv1 = vtxY - mcCollision.posY(); + deltaZv1 = vtxZ - mcCollision.posZ(); + // vertex resolution - ver 2 + // rec coll vtx - mc associated to orig rec coll (first in time) + deltaXv2 = vtxX - mapVtxXgen.find(mcCollIdRec)->second; + deltaYv2 = vtxY - mapVtxYgen.find(mcCollIdRec)->second; + deltaZv2 = vtxZ - mapVtxZgen.find(mcCollIdRec)->second; + + registry.fill(HIST("TimeAssocMC/VtxStatus"), vtxFlag); + registry.fill(HIST("TimeAssocMC/hVertexResV1"), deltaXv1, deltaYv1, deltaZv1); + registry.fill(HIST("TimeAssocMC/hVertexResV2"), deltaXv2, deltaYv2, deltaZv2); + + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAll)); + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllVtxTrue)); + } else { + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllVtxFalse)); + } + if (vtxFlag == static_cast(VertexStatusMC::kGood)) { + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelGoodVtx)); + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllGoodVtx)); + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelGoodVtxTrue)); + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllGoodVtxTrue)); + } else { + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllGoodVtxFalse)); + } + } else if (vtxFlag == static_cast(VertexStatusMC::kBad)) { + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelBadVtx)); + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllBadVtx)); + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelBadVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelBadVtxTrue)); + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllBadVtxTrue)); + } else { + registry.fill(HIST("TimeAssocMC/hTimeAssocCheckVtxType"), static_cast(AssocCheckVtxType::kAllBadVtxFalse)); + } + } + + if (!ids.empty()) { + if (ids.size() == 1) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmb"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmb)); + if (vtxFlag == static_cast(VertexStatusMC::kGood)) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbGoodVtx"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbGoodVtx)); + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbGoodVtxTrue)); + } + } else if (vtxFlag == static_cast(VertexStatusMC::kBad)) { + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbBadVtx)); + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbBadVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbBadVtxTrue)); + } + } + if (trkCollId == ids[0]) { // non ambiguous + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbID"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbID)); + if (vtxFlag == static_cast(VertexStatusMC::kGood)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbIDGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbIDGoodVtxTrue)); + } + } else if (vtxFlag == static_cast(VertexStatusMC::kBad)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbIDBadVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbIDBadVtxTrue)); + } + } + } else if (trkCollId != ids[0]) { + registry.fill(HIST("TimeAssocMC/hVTXkSelAmbID"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbID)); + if (vtxFlag == static_cast(VertexStatusMC::kGood)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelAmbIDGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbIDGoodVtxTrue)); + } + } else if (vtxFlag == static_cast(VertexStatusMC::kBad)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelAmbIDBadVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbIDBadVtxTrue)); + } + } + } else { // non ambiguous (extra) + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbIDExtra"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbIDExtra)); + if (vtxFlag == static_cast(VertexStatusMC::kGood)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbIDExtraGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbIDExtraGoodVtxTrue)); + } + } else if (vtxFlag == static_cast(VertexStatusMC::kBad)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelNonAmbIDExtraBadVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelNonAmbIDExtraBadVtxTrue)); + } + } + } + } else { // ambiguous + registry.fill(HIST("TimeAssocMC/hVTXkSelAmb"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmb)); + if (vtxFlag == static_cast(VertexStatusMC::kGood)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelAmbGoodVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbGoodVtxTrue)); + } + } else if (vtxFlag == static_cast(VertexStatusMC::kBad)) { + if (isTrueVtx) { + registry.fill(HIST("TimeAssocMC/hVTXkSelAmbBadVtxTrue"), deltaXv2, deltaYv2, deltaZv2); + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelAmbBadVtxTrue)); + } + } + } + } else { + registry.fill(HIST("TimeAssocMC/hAmbTrkTypeAssocFlag"), static_cast(AmbTrkTypeAssocFlag::kSelOrphanNull)); + } + } + } + } + LOG(info) << "No MC: " << nNoMC; + } + PROCESS_SWITCH(QaReassocMFTPbPb, processTimeAssocMC, "process MC: check MFT tracks in compatible collisions (time-associasted)", false); + + void processTimeAssocWithReassocMC(MftTracksWCollsMC const& /*tracks*/, + BestTracks3dWCollsMC const& besttracks, + aod::McParticles const& /*particles*/, + ExtBCs const& bcs, + CollsGenCentFT0CExtra const& collisions, + aod::McCollisions const& mcCollisions) + { + if (bcs.size() == 0) { + return; + } + auto bc = bcs.begin(); + initCCDB(bc); + + buildLookupTable(collisions, mcCollisions); + + int nNoMC{0}; + for (const auto& collision : collisions) { + int64_t recCollId = collision.globalIndex(); + auto itMC = mapMcCollIdPerRecColl.find(recCollId); + if (itMC == mapMcCollIdPerRecColl.end()) { + nNoMC++; + LOGP(debug, "collison {} has no MC coll", recCollId); + continue; + } + if (!collision.has_mcCollision()) { + continue; + } + auto mcCollision = collision.mcCollision_as(); + if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + continue; + } + if (!isGoodEvent(collision)) { + continue; + } + if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + + for (auto const& atrack : besttracks) { + if (!isBestTrackSelected(atrack)) { + continue; + } + auto itrack = atrack.mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + if (gConf.cfgUseTrackSel && !isTrackSelected(itrack)) { + continue; + } + auto trkBestCollId = itrack.has_collision() ? atrack.bestCollisionId() : -1; + // if (trkBestCollId != recCollId) { // check if best track is associated to original rec coll + // continue; + // } + + bool isTrueVtx = false; + bool isRecGoodMatch = false; + bool isRecGood = false; + int vtxFlag = static_cast(VertexStatusMC::kNull); + + float vtxXbest = -1.; + float vtxYbest = -1.; + float vtxZbest = -1.; + float deltaXOrigMc = -1.; + float deltaYOrigMc = -1.; + float deltaZOrigMc = -1.; + // dca best rec - dca mc associated to best rec coll + float deltaX = -1.; + float deltaY = -1.; + float deltaZ = -1.; + + if (trkBestCollId >= 0 && atrack.has_mcParticle() && atrack.mcMask() == trackCuts.selMcMask) { + auto itRecToMc = mapMcCollIdPerRecColl.find(trkBestCollId); // try mfttrackId ??? + int64_t mcPartId = atrack.mcParticle().mcCollisionId(); + auto const& idCompColl = atrack.compatibleCollIds(); + + // check if rec vertex is available in MC collisions + for (const auto& mcTrkId : mapMcCollIdPerRecColl) { + if (mcTrkId.second == mcPartId) { + isTrueVtx = true; + break; + } + } + // check good rec vertex of corresponding mc coll is available in compatible rec coll + if (!idCompColl.empty()) { + for (auto const& id : idCompColl) { + auto itMcCollId = mapMcCollIdPerRecColl.find(id); + if (itMcCollId != mapMcCollIdPerRecColl.end()) { + if (itMcCollId->second == mcPartId) { + isRecGoodMatch = true; + break; + } + } + } + } + // check if there is good or bad collision + if (itRecToMc != mapMcCollIdPerRecColl.end()) { + int mcTrkCollId = itRecToMc->second; + if (mcPartId == mcTrkCollId) { // particle.mcCollisionId == collision.mcCollisionId -> good vtx + vtxFlag = static_cast(VertexStatusMC::kGood); + } else { // wrong vtx + vtxFlag = static_cast(VertexStatusMC::kBad); + } + } + + // + // check: vertex resolution of time-to-coll reassoc: pos(rec coll best) - gen (mc coll) + // + // const auto& particle = atrack.mcParticle_as(); + // // // auto collision = atrack.collision_as(); // not in use + // const auto& mcColl = particle.mcCollision_as(); + if (!mapVtxXrec.contains(trkBestCollId)) { + continue; + } + if (!mapVtxYrec.contains(trkBestCollId)) { + continue; + } + if (!mapVtxZrec.contains(trkBestCollId)) { + continue; + } + if (!mapMcCollIdPerRecColl.contains(trkBestCollId)) { + continue; + } + vtxXbest = mapVtxXrec.find(trkBestCollId)->second; + vtxYbest = mapVtxYrec.find(trkBestCollId)->second; + vtxZbest = mapVtxZrec.find(trkBestCollId)->second; + // LOGP(info, "\t ---> \t .... \t vtxZrec: {} - collision.posZ(): {}", vtxZrec, collision.posZ()); + int64_t mcCollIdRec = mapMcCollIdPerRecColl.find(trkBestCollId)->second; + // int64_t mcCollId = itMC->second; + // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - mcCollId: {} - bestMCCol: {}", mcCollIdRec, mcCollId, bestMCCol); + if (!mapVtxXgen.contains(mcCollIdRec)) { + continue; + } + if (!mapVtxYgen.contains(mcCollIdRec)) { + continue; + } + if (!mapVtxZgen.contains(mcCollIdRec)) { + continue; + } + // vertex resolution: best rec - mc associated to orig rec coll (first in time) + deltaXOrigMc = vtxXbest - mcCollision.posX(); + deltaYOrigMc = vtxYbest - mcCollision.posY(); + deltaZOrigMc = vtxZbest - mcCollision.posZ(); + // vertex resolution: best rec - mc associated to best rec coll + deltaX = vtxXbest - mapVtxXgen.find(mcCollIdRec)->second; + deltaY = vtxYbest - mapVtxYgen.find(mcCollIdRec)->second; + deltaZ = vtxZbest - mapVtxZgen.find(mcCollIdRec)->second; + } // has_mcParticle + + registry.fill(HIST("TimeAssocWithReassocMC/VtxStatus"), vtxFlag); + + hTimeAssocWithReassocMC[0]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[1]->Fill(deltaX, deltaY, deltaZ); + + if (isTrueVtx) { + hTimeAssocWithReassocMC[3]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[12]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxAllTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxAllFalse)); + } + if (isRecGood) { + hTimeAssocWithReassocMC[4]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[13]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodAllTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodAllFalse)); + } + if (isRecGoodMatch) { + hTimeAssocWithReassocMC[5]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[14]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchAllTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchAllFalse)); + } + + if (vtxFlag == static_cast(VertexStatusMC::kGood)) { + if (isTrueVtx) { + hTimeAssocWithReassocMC[6]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[15]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsGoodVtxFalse)); + } + if (isRecGood) { + hTimeAssocWithReassocMC[7]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[16]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsGoodVtxTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsGoodVtxFalse)); + } + if (isRecGoodMatch) { + hTimeAssocWithReassocMC[8]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[17]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsGoodVtxTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsGoodVtxFalse)); + } + } else if (vtxFlag == static_cast(VertexStatusMC::kBad)) { + if (isTrueVtx) { + hTimeAssocWithReassocMC[9]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[18]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsTrueVtxVsBadVtxFalse)); + } + if (isRecGood) { + hTimeAssocWithReassocMC[10]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[19]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsBadVtxTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodVsBadVtxFalse)); + } + if (isRecGoodMatch) { + hTimeAssocWithReassocMC[11]->Fill(deltaXOrigMc, deltaYOrigMc, deltaZOrigMc); + hTimeAssocWithReassocMC[20]->Fill(deltaX, deltaY, deltaZ); + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsBadVtxTrue)); + } else { + registry.fill(HIST("TimeAssocWithReassocMC/hReassocCheckVtxType"), static_cast(ReassocCheckVtxType::kIsRecGoodMatchVsBadVtxFalse)); + } + } + } + } + LOG(info) << "No MC: " << nNoMC; + } + PROCESS_SWITCH(QaReassocMFTPbPb, processTimeAssocWithReassocMC, "process MC: check MFT tracks in reassociation with compatible collisions", false); + + Partition tracksInAcc = (aod::fwdtrack::eta < trackCuts.maxEta) && (aod::fwdtrack::eta > trackCuts.minEta); + + void processAssocMC(MftTracksWCollsMC const& tracks, + aod::McParticles const& /*particles*/, + CollsGenCentFT0CExtra const& collisions, + aod::McCollisions const& mcCollisions) + { + buildLookupTable(collisions, mcCollisions); + + for (const auto& collision : collisions) { + if (!isGoodEvent(collision)) { + continue; + } + // Select collisions with the largest number of contributors + if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + continue; + } + if (!collision.has_mcCollision()) { + continue; + } + auto mcCollision = collision.mcCollision_as(); + if (eventCuts.useZVtxCutMC && (std::abs(mcCollision.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + + int nTrk = 0, nFakeTrk = 0, nGoodTrk = 0; + for (const auto& track : tracks) { + uint index = uint(track.collisionId() >= 0); + if (track.has_mcParticle() && track.mcMask() == trackCuts.selMcMask) { + // auto particle = track.mcParticle_as(); + const auto& particle = track.mcParticle(); + auto trkCollId = track.has_collision() ? track.collisionId() : -1; + auto ids = track.compatibleCollIds(); + registry.fill(HIST("TrackToColl/histTrackNumColls"), ids.size()); + registry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kAll)); + + if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { + if (ids.empty()) { + registry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kOrphan)); + } + if (ids.size() == 1 && trkCollId == ids[0]) { + registry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kNonAmb)); + } + } + if (gConf.cfgRemoveOrphanTracks && ids.empty()) { + continue; + } + if (gConf.cfgRemoveTrivialAssoc) { + if (ids.empty() || (ids.size() == 1 && trkCollId == ids[0])) { + registry.fill(HIST("TrackToColl/histNonAmbTrackNumColls"), ids.size()); + continue; + } + } + if (gConf.cfgRemoveAmbiguousTracks && (track.compatibleCollIds().size() != 1)) { + continue; + } + nTrk++; + if ((particle.mcCollisionId() != collision.mcCollision().globalIndex())) { + nFakeTrk++; + continue; + } + if (collision.mcCollisionId() == particle.mcCollisionId()) { + nGoodTrk++; + } + if (!ids.empty()) { + if (ids.size() == 1) { + if (trkCollId != ids[0]) { + registry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kAmb)); + } else { + registry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kNonAmbSame)); + } + } else { + registry.fill(HIST("TrackToColl/hAmbTrackType"), static_cast(AmbTrkType::kAmbGt1)); + } + } + + bool isAmbiguous = (ids.size() > 1); + if (isAmbiguous) { + registry.fill(HIST("TrackToColl/histAmbTrackNumColls"), ids.size()); + std::vector ambVtxZ{}; + for (const auto& collIdx : ids) { + const auto& ambColl = collisions.rawIteratorAt(collIdx); + ambVtxZ.push_back(ambColl.posZ()); + } + if (!ambVtxZ.empty()) { + registry.fill(HIST("TrackToColl/histAmbTrackZvtxRMS"), computeRMS(ambVtxZ)); + } + } + + float deltaX = -999.f; + float deltaY = -999.f; + float deltaZ = -999.f; + if (index != 0u) { + const auto& collision = track.collision_as(); + deltaX = collision.posX() - mcCollision.posX(); + deltaY = collision.posY() - mcCollision.posY(); + deltaZ = collision.posZ() - mcCollision.posZ(); + if (collision.has_mcCollision() && collision.mcCollisionId() == particle.mcCollisionId()) { + hCollAssoc[index + 1]->Fill(track.pt(), track.eta(), deltaX, deltaY, deltaZ); + } else { + if (isAmbiguous) { + for (const auto& collIdx : ids) { + auto ambColl = collisions.rawIteratorAt(collIdx); + if (ambColl.has_mcCollision() && ambColl.mcCollisionId() == particle.mcCollisionId()) { + hCollAssoc[index + 2]->Fill(track.pt(), track.eta(), deltaX, deltaY, deltaZ); + // hCollAssoc[index + 2]->Fill(track.pt(), track.eta(), ambColl.posX() - mcCollision.posX(), ambColl.posY() - mcCollision.posY(), ambColl.posZ() - mcCollision.posZ()); + break; + } + } + } + } + hCollAssoc[index]->Fill(track.pt(), track.eta(), deltaX, deltaY, deltaZ); + } + } else { + hCollAssoc[index]->Fill(track.pt(), track.eta(), -999.f, -999.f, -999.f); + } + } + float frac = (nTrk > 0) ? static_cast(nGoodTrk) / nTrk : -1.; + registry.fill(HIST("TrackToColl/histFracGoodTracks"), frac); + float fracFake = (nTrk > 0) ? static_cast(nFakeTrk) / nTrk : -1.; + registry.fill(HIST("TrackToColl/histFracTracksFakeMcColl"), fracFake); + } + } + + PROCESS_SWITCH(QaReassocMFTPbPb, processAssocMC, "Process collision-association information, requires extra table from TrackToCollisionAssociation task (fillTableOfCollIdsPerTrack=true)", false); + + void processReAssoc3dMC(BestTracks3dWCollsMC const& besttracks, + MftTracksWCollsMC const& /*tracks*/, + aod::McParticles const& /*particles*/, + CollsGenCentFT0CExtra const& collisions, + aod::McCollisions const& mcCollisions) + { + float cGen = -1; + float crecMin = 105.f; + for (const auto& collision : collisions) { + if (isGoodEvent(collision)) { + float c = getRecoCent(collision); + if (c < crecMin) { + crecMin = c; + } + } + } + if (cGen < 0) { + cGen = crecMin; + } + float occGen = -1.; + for (const auto& collision : collisions) { + if (isGoodEvent(collision)) { + float o = getOccupancy(collision, eventCuts.occupancyEstimator); + if (o > occGen) { + occGen = o; + } + } + } + + registry.fill(HIST("ReAssocMC/EvtGenRecReassoc"), 1., cGen, occGen); + + buildLookupTable(collisions, mcCollisions); + + int nNoMC{0}; + for (const auto& collision : collisions) { + auto occ = getOccupancy(collision, eventCuts.occupancyEstimator); + float crec = getRecoCent(collision); + registry.fill(HIST("ReAssocMC/EvtGenRecReassoc"), 2., crec, occ); + registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsAll), crec, occ); + if (!isGoodEvent(collision)) { + continue; + } + registry.fill(HIST("ReAssocMC/EvtGenRecReassoc"), 3., crec, occ); + registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSelected), crec, occ); + + if (!collision.has_mcCollision()) { + continue; + } + + int64_t recCollId = collision.globalIndex(); + auto itMC = mapMcCollIdPerRecColl.find(recCollId); + if (itMC == mapMcCollIdPerRecColl.end()) { + nNoMC++; + continue; + } + + registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsHasMcColl), crec, occ); + auto mcColl = collision.template mcCollision_as(); + if (gConf.cfgRemoveSplitVertex && (!setRecCollSel.contains(collision.globalIndex()))) { + continue; + } + registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsSplitVtxRemoved), crec, occ); + if (eventCuts.useZVtxCutMC && (std::abs(mcColl.posZ()) >= eventCuts.maxZvtx)) { + continue; + } + registry.fill(HIST("ReAssocMC/hReAssocMCEventStatus"), static_cast(ReAssocMCEventStatus::kEvtReAsZVtxCutMC), crec, occ); + + for (auto const& atrack : besttracks) { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkReAssocAll), crec, occ); + if (!isBestTrackSelected(atrack)) { + continue; + } + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkBestSel), crec, occ); + + const float bestDcaZ = getDCAz(atrack); + auto itrack = atrack.template mfttrack_as(); + if (!isTrackSelected(itrack)) { + continue; + } + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkSel), crec, occ); + if (!itrack.has_collision()) { + continue; + } + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkHasColl), crec, occ); + if (gConf.cfgRemoveReassigned) { + if (itrack.collisionId() != atrack.bestCollisionId()) { + continue; + } + } + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkReassignedRemoved), crec, occ); + + auto ids = atrack.compatibleCollIds(); + if (gConf.cfgRemoveTrivialAssoc) { + if (ids.empty() || (ids.size() == 1 && itrack.collisionId() == ids[0])) { + continue; + } + } + if (gConf.cfgRemoveAmbiguousTracks && (ids.size() != 1)) { + continue; + } + + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkRec)]->Fill(itrack.pt(), itrack.eta(), 0., 0., 0., crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkRec)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, 0., 0., crec, occ); + + if (itrack.collisionId() >= 0 && itrack.has_mcParticle() && itrack.mcMask() == trackCuts.selMcMask) { + + auto particle = itrack.template mcParticle_as(); + if (!isChrgParticle(particle.pdgCode())) { + continue; + } + if (gConf.cfgUseParticleSel && !isParticleSelected(particle)) { + continue; + } + // auto collision = itrack.template collision_as(); + if (eventCuts.useZDiffCut) { + if (std::abs(collision.posZ() - atrack.mcParticle().mcCollision().posZ()) > eventCuts.maxZvtxDiff) { + continue; + } + } + + float deltaX = -999.f; + float deltaY = -999.f; + float deltaZ = -999.f; + + auto vtxXtruth = atrack.mcParticle().mcCollision().posX(); + auto vtxYtruth = atrack.mcParticle().mcCollision().posY(); + auto vtxZtruth = atrack.mcParticle().mcCollision().posZ(); + + const int bestRecColl = atrack.bestCollisionId(); + auto itMapVtxXrec = mapVtxXrec.find(bestRecColl); + auto itMapVtxYrec = mapVtxYrec.find(bestRecColl); + auto itMapVtxZrec = mapVtxZrec.find(bestRecColl); + auto itMapMcCollIdPerRecColl = mapMcCollIdPerRecColl.find(bestRecColl); + + if (itMapVtxXrec == mapVtxXrec.end()) { + continue; + } + if (itMapVtxYrec == mapVtxYrec.end()) { + continue; + } + if (itMapVtxZrec == mapVtxZrec.end()) { + continue; + } + if (itMapMcCollIdPerRecColl == mapMcCollIdPerRecColl.end()) { + continue; + } + const float vtxXbest = itMapVtxXrec->second; + const float vtxYbest = itMapVtxYrec->second; + const float vtxZbest = itMapVtxZrec->second; + // LOGP(info, "\t ---> \t .... \t vtxZrec: {} - collision.posZ(): {}", vtxZrec, collision.posZ()); + const float mcCollIdRec = itMapMcCollIdPerRecColl->second; + // LOGP(info, "\t ---> \t .... \t mcCollIdRec: {} - bestMCCol: {}", mcCollIdRec, bestMCCol); + + deltaX = vtxXbest - vtxXtruth; + deltaY = vtxYbest - vtxYtruth; + deltaZ = vtxZbest - vtxZtruth; + + const auto dcaXtruth(particle.vx() - particle.mcCollision().posX()); + const auto dcaYtruth(particle.vy() - particle.mcCollision().posY()); + const auto dcaZtruth(particle.vz() - particle.mcCollision().posZ()); + auto dcaXYtruth = std::sqrt(dcaXtruth * dcaXtruth + dcaYtruth * dcaYtruth); + + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkHasMcPart), crec, occ); + + if (!ids.empty()) { + if (collision.has_mcCollision() && mcCollIdRec == particle.mcCollisionId()) { // good coll + if (isRecInCompColl(atrack)) { // coll vertex is among compatible colls + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkIdGt0), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkIdGt0)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkIdGt0)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } + if (ids.size() == 1) { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmb), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmb)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmb)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (collision.mcCollisionId() == particle.mcCollisionId()) { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbGood), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbGood)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbBad), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbBad)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + if (itrack.collisionId() == ids[0]) { // non ambiguous + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbID), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbID)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbID)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (collision.mcCollisionId() == particle.mcCollisionId()) { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbIDGood), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDGood)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDGood)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbIDBad), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDBad)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDBad)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } else if (itrack.collisionId() != ids[0]) { // ambiguous extra + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkAmbID), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbID)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbID)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (collision.mcCollisionId() == particle.mcCollisionId()) { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkAmbIDGood), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbIDGood)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbIDGood)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkAmbIDBad), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkAmbIDBad)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkAmbIDBad)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } else { // non ambiguous (extra) + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbIDExtra), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtra)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtra)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (collision.mcCollisionId() == particle.mcCollisionId()) { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbIDExtraGood), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraGood)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraGood)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kTrkNonAmbIDExtraBad), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraBad)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kTrkNonAmbIDExtraBad)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } + } else { // ambiguous + if (itrack.collisionId() != atrack.bestCollisionId()) { // re-associated + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssoc), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssoc)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssoc)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (collision.has_mcCollision() && mcCollIdRec == particle.mcCollisionId()) { // good coll + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocGood), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGood)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGood)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (isRecInCompColl(atrack)) { // coll vertex is among compatible colls + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocGoodIsCompTrue), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocGoodIsCompFalse), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocGoodIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocBad), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBad)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBad)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (isRecInCompColl(atrack)) { // coll vertex is among compatible colls + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocBadIsCompTrue), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kReAssocBadIsCompFalse), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kReAssocBadIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } + } else { // associated + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssoc), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssoc)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssoc)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (collision.has_mcCollision() && mcCollIdRec == particle.mcCollisionId()) { // good coll + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocGood), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGood)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGood)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (isRecInCompColl(atrack)) { // coll vertex is among compatible colls + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocGoodIsCompTrue), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocGoodIsCompFalse), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocGoodIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocBad), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBad)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBad)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + if (isRecInCompColl(atrack)) { // coll vertex is among compatible colls + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocBadIsCompTrue), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompTrue)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } else { + registry.fill(HIST("ReAssocMC/hReAssocMCTrackStatus"), static_cast(ReAssocMCTrackStatus::kAssocBadIsCompFalse), crec, occ); + hReAssocVtxRes[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), deltaX, deltaY, deltaZ, crec, occ); + hReAssocDCA[static_cast(HistStatusReAssocVtx::kAssocBadIsCompFalse)]->Fill(itrack.pt(), itrack.eta(), atrack.bestDCAXY(), bestDcaZ, dcaXYtruth, dcaZtruth, crec, occ); + } + } + } + } + } + } + } + } + LOG(info) << "No MC: " << nNoMC; + } + PROCESS_SWITCH(QaReassocMFTPbPb, processReAssoc3dMC, "Process re-association information based on BestCollisionsFwd3d (3D) table", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGMM/UE/Tasks/CMakeLists.txt b/PWGMM/UE/Tasks/CMakeLists.txt index f9ee32c6e10..27d74e095c1 100644 --- a/PWGMM/UE/Tasks/CMakeLists.txt +++ b/PWGMM/UE/Tasks/CMakeLists.txt @@ -22,4 +22,9 @@ o2physics_add_dpl_workflow(ue-zdc-analysis o2physics_add_dpl_workflow(dedx-analysis SOURCES dedxAnalysis.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore - COMPONENT_NAME Analysis) \ No newline at end of file + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(flattenicity-task + SOURCES flattenicityTask.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) diff --git a/PWGMM/UE/Tasks/flattenicityTask.cxx b/PWGMM/UE/Tasks/flattenicityTask.cxx new file mode 100644 index 00000000000..ec430c12479 --- /dev/null +++ b/PWGMM/UE/Tasks/flattenicityTask.cxx @@ -0,0 +1,433 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flattenicityTask.cxx +/// \brief Run-1 calibration task for FT0 flattenicity (fine-binned histograms for percentile extraction) +/// \author Eisha Rani +/// \since September 2026 + +#include "Common/Core/RecoDecay.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::soa; + +using FullTracks = + soa::Join; + +using CollisionsWithSelection = + soa::Join; + +struct FlattenicityTask { + + // NOLINTNEXTLINE(misc-include-cleaner) - Service<> is provided by Framework/AnalysisTask.h; there is no separate standalone header for it in this O2 version. + Service pdg{}; + + // ======================================================================== + // FT0 geometry + // ======================================================================== + + static constexpr int NPhiSectors = 8; + static constexpr int NSectorsA = 24; + static constexpr int NSectorsC = 28; + static constexpr int NSectorsCombined = NSectorsA + NSectorsC; // 52 + static constexpr int ChannelsPerSector = 4; + static constexpr int NchA = NSectorsA * ChannelsPerSector; // 96 + static constexpr int NchC = NSectorsC * ChannelsPerSector; // 112 + static constexpr int NCell = NchA + NchC; // 208 + static constexpr int NEtaA = NchA / NPhiSectors; // 12 + static constexpr int NEtaC = NchC / NPhiSectors; // 14 + static constexpr float FT0AEtaMin = 3.5; + static constexpr float FT0AEtaMax = 4.9; + static constexpr float FT0CEtaMin = -3.3; + static constexpr float FT0CEtaMax = -2.1; + static constexpr int NPhysicalPrimaryBit = 0x4; + static constexpr float ChargeEpsilon = 1e-6f; + + // ======================================================================== + // Run-1 fine binning + // ======================================================================== + static constexpr int NFlatBins = 10000; + + // ======================================================================== + // Histograms + // ======================================================================== + HistogramRegistry histos{ + "histos", + { + {"QA/hEvents", "Event selection;selection;Entries", {HistType::kTH1F, {{5, 0, 5}}}}, + {"QA/hNchINEL", "INEL>0 multiplicity (|eta|<0.8);N_{ch};Entries", {HistType::kTH1F, {{100, -0.5, 99.5}}}}, + + {"Truth/hFlattenicityTruthAverage_fine", "MC truth flattenicity - average of FT0A-like/FT0C-like grids;1-#rho;Entries", {HistType::kTH1F, {{NFlatBins, 0.0, 1.0}}}}, + {"Truth/hFlattenicityTruthCombined_fine", "MC truth flattenicity - combined 208-cell grid;1-#rho;Entries", {HistType::kTH1F, {{NFlatBins, 0.0, 1.0}}}}, + {"Reco/hFlattenicityFT0Average_fine", "FT0 flattenicity - average of FT0A and FT0C;1-#rho;Entries", {HistType::kTH1F, {{NFlatBins, 0.0, 1.0}}}}, + {"Reco/hFlattenicityFT0Combined_fine", "FT0 flattenicity - combined FT0A+FT0C;1-#rho;Entries", {HistType::kTH1F, {{NFlatBins, 0.0, 1.0}}}}, + {"Reco/hFT0Mraw_fine", "Raw FT0M amplitude sum;FT0A+FT0C amplitude;Entries", {HistType::kTH1F, {{NFlatBins, 0.0, 20000.0}}}}, + + {"Truth/hFlattenicityTruthAverage", "MC truth flattenicity - average definition;1-#rho;Entries", {HistType::kTH1F, {{100, 0.0, 1.0}}}}, + {"Truth/hFlattenicityTruthCombined", "MC truth flattenicity - combined definition;1-#rho;Entries", {HistType::kTH1F, {{100, 0.0, 1.0}}}}, + {"Reco/hFlattenicityFT0Average", "FT0 flattenicity - average definition;1-#rho;Entries", {HistType::kTH1F, {{100, 0.0, 1.0}}}}, + {"Reco/hFlattenicityFT0Combined", "FT0 flattenicity - combined definition;1-#rho;Entries", {HistType::kTH1F, {{100, 0.0, 1.0}}}}, + {"Reco/hFT0Mraw", "Raw FT0M amplitude sum;FT0A+FT0C amplitude;Entries", {HistType::kTH1F, {{200, 0.0, 20000.0}}}}, + + {"Truth/hCellOccupancy", "Truth FT0 cell occupancy;cell ID;Entries", {HistType::kTH1F, {{NCell, 0, NCell}}}}, + {"Truth/hCellOccupancyFT0A", "Truth FT0A cell occupancy;cell ID;Entries", {HistType::kTH1F, {{NchA, 0, NchA}}}}, + {"Truth/hCellOccupancyFT0C", "Truth FT0C cell occupancy;cell ID;Entries", {HistType::kTH1F, {{NchC, 0, NchC}}}}, + }}; + + // ======================================================================== + // Configurables + // ======================================================================== + + Configurable cfgPtMin{"cfgPtMin", 0.0, "Minimum pT for charged particles/tracks (GeV/c) - Antonio's instruction: require pT > 0, not a physical pT-threshold cut"}; + Configurable cfgEtaMax{"cfgEtaMax", 0.8, "Maximum |eta| for INEL>0 and track selection"}; + Configurable cfgVzMax{"cfgVzMax", 10.0, "Maximum absolute collision vertex z (cm)"}; + Configurable cfgApplySel8{"cfgApplySel8", true, "Apply sel8 event selection (set to true for all samples)"}; + Configurable cfgNCrossedRowsTPC{"cfgNCrossedRowsTPC", 70, "Minimum TPC crossed rows"}; + Configurable cfgChi2PerClusterTPC{"cfgChi2PerClusterTPC", 4.0, "Maximum TPC chi2 per cluster"}; + Configurable cfgChi2PerClusterITS{"cfgChi2PerClusterITS", 36.0, "Maximum ITS chi2 per cluster"}; + Configurable cfgDCAZ{"cfgDCAZ", 0.1, "Maximum absolute DCAz (cm)"}; + Configurable cfgRequireGoldenChi2{"cfgRequireGoldenChi2", true, "Require global/golden track selection"}; + + // ======================================================================== + // PDG charge helper + // ======================================================================== + bool isChargedParticle(int pdgCode) + { + const auto* pdgParticle = pdg->GetParticle(pdgCode); + if (!pdgParticle) { + return false; + } + if (std::abs(pdgParticle->Charge()) < ChargeEpsilon) { + return false; + } + return true; + } + + // ======================================================================== + // Rho / flattenicity calculation + // ======================================================================== + template + float computeRho(const std::array& counts) + { + float total = 0.0f; + for (std::size_t i = 0; i < N; ++i) { + total += counts[i]; + } + if (total <= 0.0f) { + return -1.0f; + } + const float mean = total / static_cast(N); + if (mean <= 0.0f) { + return -1.0f; + } + float sumSq = 0.0f; + for (std::size_t i = 0; i < N; ++i) { + const float diff = counts[i] - mean; + sumSq += diff * diff; + } + return std::sqrt(sumSq) / (static_cast(N) * mean); + } + + template + float computeFlattenicity(const std::array& counts) + { + const float rho = computeRho(counts); + if (rho < 0.0f) { + return -1.0f; + } + return 1.0f - rho; + } + + // ======================================================================== + // FT0 channel -> sector + // ======================================================================== + int getFT0ASector(int channel) + { + if (channel < 0 || channel >= NchA) { + return -1; + } + return channel / ChannelsPerSector; + } + int getFT0CSector(int channel) + { + if (channel < 0 || channel >= NchC) { + return -1; + } + return channel / ChannelsPerSector; + } + + // ======================================================================== + // Generator particle -> 208-cell grid + // ======================================================================== + int assignToFT0Cell(float eta, float phi, bool& isFT0A) + { + const bool inFT0A = (eta > FT0AEtaMin && eta < FT0AEtaMax); + const bool inFT0C = (eta > FT0CEtaMin && eta < FT0CEtaMax); + if (!inFT0A && !inFT0C) { + return -1; + } + isFT0A = inFT0A; + + // Wrap phi into [0, 2pi) using the O2-standard helper - avoids the + // manual +=/-= TwoPI pattern the linter flags (two-pi-add-subtract). + const float wrappedPhi = RecoDecay::constrainAngle(phi, 0.0f); + + const float phiWidth = o2::constants::math::TwoPI / static_cast(NPhiSectors); + int phiBin = static_cast(std::floor(wrappedPhi / phiWidth)); + phiBin = std::max(0, std::min(phiBin, NPhiSectors - 1)); + + if (inFT0A) { + const float etaWidth = (FT0AEtaMax - FT0AEtaMin) / static_cast(NEtaA); + int etaBin = static_cast(std::floor((eta - FT0AEtaMin) / etaWidth)); + etaBin = std::max(0, std::min(etaBin, NEtaA - 1)); + return etaBin * NPhiSectors + phiBin; + } + const float etaWidth = (FT0CEtaMax - FT0CEtaMin) / static_cast(NEtaC); + int etaBin = static_cast(std::floor((eta - FT0CEtaMin) / etaWidth)); + etaBin = std::max(0, std::min(etaBin, NEtaC - 1)); + return NchA + etaBin * NPhiSectors + phiBin; + } + + // ======================================================================== + // Compute both reco definitions + // ======================================================================== + template + bool computeFT0Flattenicities(const FT0& ft0, float& flattenicityAverage, float& flattenicityCombined, float& sumAmplitude) + { + std::array countsA{}; + std::array countsC{}; + std::array countsCombined{}; + sumAmplitude = 0.0f; + for (std::size_t i = 0; i < ft0.amplitudeA().size(); ++i) { + const int channel = ft0.channelA()[i]; + const int sector = getFT0ASector(channel); + const float amplitude = ft0.amplitudeA()[i]; + if (sector >= 0 && sector < NSectorsA) { + countsA[sector] += amplitude; + countsCombined[sector] += amplitude; + } + sumAmplitude += amplitude; + } + for (std::size_t i = 0; i < ft0.amplitudeC().size(); ++i) { + const int channel = ft0.channelC()[i]; + const int sector = getFT0CSector(channel); + const float amplitude = ft0.amplitudeC()[i]; + if (sector >= 0 && sector < NSectorsC) { + countsC[sector] += amplitude; + countsCombined[NSectorsA + sector] += amplitude; + } + sumAmplitude += amplitude; + } + const float rhoA = computeRho(countsA); + const float rhoC = computeRho(countsC); + if (rhoA < 0.0f || rhoC < 0.0f) { + return false; + } + flattenicityAverage = 1.0f - 0.5f * (rhoA + rhoC); + const float rhoCombined = computeRho(countsCombined); + if (rhoCombined < 0.0f) { + return false; + } + flattenicityCombined = 1.0f - rhoCombined; + return true; + } + + // ======================================================================== + // processMC + // ======================================================================== + void processMC(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + if (std::abs(mcCollision.posZ()) > cfgVzMax) { + return; + } + // Combined 208-cell truth grid (cells 0..NchA-1 = FT0A-like, NchA..NCell-1 + // = FT0C-like) AND the two separate sub-arrays, filled in parallel, so we + // can compute BOTH truth definitions - average and combined - the same + // way the reco side already does for the real FT0-A/FT0-C sectors. + std::array truthCounts{}; + std::array truthCountsA{}; + std::array truthCountsC{}; + int nChINEL = 0; + bool hasFT0A = false, hasFT0C = false; + + for (const auto& particle : mcParticles) { + if ((particle.flags() & NPhysicalPrimaryBit) == 0) { + continue; + } + if (!isChargedParticle(particle.pdgCode())) { + continue; + } + if (particle.pt() <= cfgPtMin) { + continue; // Antonio: require pT > 0 + } + if (std::abs(particle.eta()) < cfgEtaMax) { + ++nChINEL; + } + + const bool inFT0A = (particle.eta() > FT0AEtaMin && particle.eta() < FT0AEtaMax); + const bool inFT0C = (particle.eta() > FT0CEtaMin && particle.eta() < FT0CEtaMax); + if (inFT0A) { + hasFT0A = true; + } + if (inFT0C) { + hasFT0C = true; + } + + bool isFT0A = false; + const int cellId = assignToFT0Cell(particle.eta(), particle.phi(), isFT0A); + if (cellId < 0 || cellId >= NCell) { + continue; + } + truthCounts[cellId] += 1.0f; + if (isFT0A) { + truthCountsA[cellId] += 1.0f; + } else { + truthCountsC[cellId - NchA] += 1.0f; + } + + histos.fill(HIST("Truth/hCellOccupancy"), cellId); + if (isFT0A) { + histos.fill(HIST("Truth/hCellOccupancyFT0A"), cellId); + } else { + histos.fill(HIST("Truth/hCellOccupancyFT0C"), cellId - NchA); + } + } + + if (nChINEL == 0) { + return; + } + histos.fill(HIST("QA/hEvents"), 1); + histos.fill(HIST("QA/hNchINEL"), nChINEL); + + // Require activity on BOTH sides of the detector (Antonio's instruction), + // for MC truth exactly as already required on the reco side. + if (!hasFT0A || !hasFT0C) { + return; + } + histos.fill(HIST("QA/hEvents"), 2); + + // Definition 1: average of two separately-normalized rho's (mirrors the + // reco "average" definition, using the truth-grid A/C sub-arrays). + const float rhoATruth = computeRho(truthCountsA); + const float rhoCTruth = computeRho(truthCountsC); + if (rhoATruth >= 0.0f && rhoCTruth >= 0.0f) { + const float flattenicityTruthAverage = 1.0f - 0.5f * (rhoATruth + rhoCTruth); + if (flattenicityTruthAverage >= 0.0f && flattenicityTruthAverage <= 1.0f) { + histos.fill(HIST("Truth/hFlattenicityTruthAverage_fine"), flattenicityTruthAverage); + histos.fill(HIST("Truth/hFlattenicityTruthAverage"), flattenicityTruthAverage); + } + } + + // Definition 2: one combined rho over the full 208-cell truth grid. + const float flattenicityTruthCombined = computeFlattenicity(truthCounts); + if (flattenicityTruthCombined >= 0.0f) { + histos.fill(HIST("Truth/hFlattenicityTruthCombined_fine"), flattenicityTruthCombined); + histos.fill(HIST("Truth/hFlattenicityTruthCombined"), flattenicityTruthCombined); + } + } + + PROCESS_SWITCH(FlattenicityTask, processMC, "Process MC truth for Run-1 calibration", true); + + // ======================================================================== + // processData + // ======================================================================== + void processData(CollisionsWithSelection::iterator const& collision, + aod::FT0s const& ft0s, + FullTracks const& tracks) + { + if (cfgApplySel8 && !collision.sel8()) { + return; + } + if (std::abs(collision.posZ()) > cfgVzMax) { + return; + } + + int nChINEL = 0; + for (const auto& track : tracks) { + if (track.collisionId() != collision.globalIndex()) { + continue; + } + if (track.pt() <= cfgPtMin) { + continue; + } + if (std::abs(track.eta()) < cfgEtaMax) { + ++nChINEL; + } + } + if (nChINEL == 0) { + return; + } + + histos.fill(HIST("QA/hEvents"), 3); + histos.fill(HIST("QA/hNchINEL"), nChINEL); + + bool foundFT0 = false; + aod::FT0s::iterator ft0; + for (const auto& f : ft0s) { + if (f.bcId() == collision.bcId()) { + ft0 = f; + foundFT0 = true; + break; + } + } + if (!foundFT0) { + return; + } + + float flattenicityAverage = -1.0f, flattenicityCombined = -1.0f, sumFT0M = 0.0f; + if (!computeFT0Flattenicities(ft0, flattenicityAverage, flattenicityCombined, sumFT0M)) { + return; + } + + histos.fill(HIST("Reco/hFT0Mraw"), sumFT0M); + histos.fill(HIST("Reco/hFT0Mraw_fine"), sumFT0M); + + if (flattenicityAverage >= 0.0f && flattenicityAverage <= 1.0f) { + histos.fill(HIST("Reco/hFlattenicityFT0Average_fine"), flattenicityAverage); + histos.fill(HIST("Reco/hFlattenicityFT0Average"), flattenicityAverage); + } + if (flattenicityCombined >= 0.0f && flattenicityCombined <= 1.0f) { + histos.fill(HIST("Reco/hFlattenicityFT0Combined_fine"), flattenicityCombined); + histos.fill(HIST("Reco/hFlattenicityFT0Combined"), flattenicityCombined); + } + } + + PROCESS_SWITCH(FlattenicityTask, processData, "Process data / MC-reco for Run-1 calibration", true); +}; + +// ============================================================================ +// Workflow +// ============================================================================ +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGMM/UE/Tasks/uecharged.cxx b/PWGMM/UE/Tasks/uecharged.cxx index 8fef7ee4a8b..6e95a27e6db 100644 --- a/PWGMM/UE/Tasks/uecharged.cxx +++ b/PWGMM/UE/Tasks/uecharged.cxx @@ -13,7 +13,6 @@ /// \file uecharged.cxx /// \brief Underlying event analysis task /// \since November 2021 -/// \last update: April 2026 #include "PWGLF/Utils/inelGt.h" @@ -21,6 +20,7 @@ #include "Common/Core/TrackSelection.h" #include "Common/Core/TrackSelectionDefaults.h" #include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/McCollisionExtra.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/TrackSelectionTables.h" @@ -62,6 +62,7 @@ struct ueCharged { Configurable removeITSROFBorder{"event_removeITSROFBorder", false, "Remove ITS Read-Out Frame border and only apply kIsTriggerTVX & kNoTimeFrameBorder (recommended for MC)"}; Configurable cfgINELCut{"event_cfgINELCut", 0, "INEL event selection: 0 no sel, 1 INEL>0, 2 INEL>1"}; Configurable CollPosZ{"event_CollPosZ", 10.f, "Cut on the z component of the vertex position"}; + Configurable GoodITS{"event_GoodITS", true, "Numbers of inactive chips on all ITS layers are below maximum allowed values"}; Configurable analyzeEvandTracksel{"analyzeEvandTracksel", true, "Analyze the event and track selection"}; // Track selection configurables @@ -72,16 +73,16 @@ struct ueCharged { Configurable minPt{"trkcfg_minPt", 0.1f, "Set minimum pT of tracks"}; Configurable maxPt{"trkcfg_maxPt", 1e10f, "Set maximum pT of tracks"}; Configurable requireEta{"trkcfg_requireEta", 0.8f, "Set eta range of tracks"}; - Configurable requireITSRefit{"trkcfg_requireITSRefit", true, "Additional cut on the ITS requirement"}; - Configurable requireTPCRefit{"trkcfg_requireTPCRefit", true, "Additional cut on the TPC requirement"}; Configurable requireGoldenChi2{"trkcfg_requireGoldenChi2", true, "Additional cut on the GoldenChi2"}; Configurable maxChi2PerClusterTPC{"trkcfg_maxChi2PerClusterTPC", 4.f, "Additional cut on the maximum value of the chi2 per cluster in the TPC"}; Configurable maxChi2PerClusterITS{"trkcfg_maxChi2PerClusterITS", 36.f, "Additional cut on the maximum value of the chi2 per cluster in the ITS"}; - // Configurable minITSnClusters{"trkcfg_minITSnClusters", 5, "minimum number of found ITS clusters"}; Configurable minNCrossedRowsTPC{"trkcfg_minNCrossedRowsTPC", 70.f, "Additional cut on the minimum number of crossed rows in the TPC"}; - Configurable minNCrossedRowsOverFindableClustersTPC{"trkcfg_minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; Configurable maxDcaXYFactor{"trkcfg_maxDcaXYFactor", 1.f, "Multiplicative factor on the maximum value of the DCA xy"}; Configurable maxDcaZ{"trkcfg_maxDcaZ", 0.1f, "Additional cut on the maximum value of the DCA z"}; + // Configurable requireITSRefit{"trkcfg_requireITSRefit", true, "Additional cut on the ITS requirement"}; + // Configurable requireTPCRefit{"trkcfg_requireTPCRefit", true, "Additional cut on the TPC requirement"}; + // Configurable minNCrossedRowsOverFindableClustersTPC{"trkcfg_minNCrossedRowsOverFindableClustersTPC", 0.8f, "Additional cut on the minimum value of the ratio between crossed rows and findable clusters in the TPC"}; + // Configurable minITSnClusters{"trkcfg_minITSnClusters", 5, "minimum number of found ITS clusters"}; Service pdg; @@ -94,6 +95,7 @@ struct ueCharged { using TrackMCTrueTable = aod::McParticles; // reconstructed collisions associated to MC collisions (small groups keyed by mcCollisionId) + using ColMCTrueTableWithExtra = soa::Join; using ColMCRecTable = soa::SmallGroups>; using TrackMCRecTable = soa::Join; using FilTrackMCRecTable = soa::Filtered; @@ -138,17 +140,17 @@ struct ueCharged { myTrkSel = getGlobalTrackSelectionRun3ITSMatch(setITSreq.value); myTrkSel.SetPtRange(minPt.value, maxPt.value); myTrkSel.SetEtaRange(-requireEta.value, requireEta.value); - myTrkSel.SetRequireITSRefit(requireITSRefit.value); - myTrkSel.SetRequireTPCRefit(requireTPCRefit.value); myTrkSel.SetRequireGoldenChi2(requireGoldenChi2.value); myTrkSel.SetMaxChi2PerClusterTPC(maxChi2PerClusterTPC.value); myTrkSel.SetMaxChi2PerClusterITS(maxChi2PerClusterITS.value); - // myTrkSel.SetMinNClustersITS(minITSnClusters.value); myTrkSel.SetMinNCrossedRowsTPC(minNCrossedRowsTPC.value); - myTrkSel.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); - // myTrkSel.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / pow(pt, 1.1f); }); myTrkSel.SetMaxDcaXYPtDep([](float /*pt*/) { return 10000.f; }); myTrkSel.SetMaxDcaZ(maxDcaZ.value); + // myTrkSel.SetRequireITSRefit(requireITSRefit.value); + // myTrkSel.SetRequireTPCRefit(requireTPCRefit.value); + // myTrkSel.SetMinNClustersITS(minITSnClusters.value); + // myTrkSel.SetMinNCrossedRowsOverFindableClustersTPC(minNCrossedRowsOverFindableClustersTPC.value); + // myTrkSel.SetMaxDcaXYPtDep([](float pt) { return 0.0105f + 0.0350f / pow(pt, 1.1f); }); myTrkSel.print(); } @@ -214,7 +216,7 @@ struct ueCharged { ue.add("vtxZEta", ";#eta;vtxZ", HistType::kTH2F, {{50, -2.5, 2.5, " "}, {60, -30, 30, " "}}); ue.add("phiEta", ";#eta;#varphi", HistType::kTH2F, {{50, -2.5, 2.5}, {200, 0., 2 * o2::constants::math::PI, " "}}); ue.add("hvtxZ", "vtxZ", HistType::kTH1F, {{40, -20.0, 20.0, " "}}); - ue.add("hCounter", "Counter; sel; Nev", HistType::kTH1D, {{7, 0, 7, " "}}); + ue.add("hCounter", "Counter; sel; Nev", HistType::kTH1D, {{8, 0, 8, " "}}); ue.add("hPtLeadingRecPS", "rec pTleading after physics selection", HistType::kTH1D, {ptAxist}); ue.add("hPtLeadingMeasured", "measured pTleading after physics selection", HistType::kTH1D, {ptAxist}); ue.add("hPtLeadingVsTracks", "", HistType::kTProfile, {{ptAxist}}); @@ -226,6 +228,7 @@ struct ueCharged { h->GetXaxis()->SetBinLabel(4, "NoSameBunchPileup"); h->GetXaxis()->SetBinLabel(5, "IsGoodZvtxFT0vsPV"); h->GetXaxis()->SetBinLabel(6, "posZ passed"); + h->GetXaxis()->SetBinLabel(7, "GoodITSLayersAll"); for (int i = 0; i < 3; ++i) { ue.add(pNumDenMeasuredPS[i].data(), "Number Density; ; #LT #it{N}_{trk} #GT", HistType::kTProfile, {ptAxist}); @@ -252,6 +255,8 @@ struct ueCharged { ue.add("hEtaLeadingVsPtLeading", " ", HistType::kTH2D, {{ptAxist}, {50, -2.5, 2.5, "#eta"}}); if (analyzeEvandTracksel) { + ue.add("hPtVsDCAxy", " ", HistType::kTH2D, {{ptAxis}, {225, -0.6, 0.6, "#it{DCA}_{xy} (cm)"}}); + ue.add("hPtVsDCAz", " ", HistType::kTH2D, {{ptAxis}, {225, -0.6, 0.6, "#it{DCA}_{z} (cm)"}}); const AxisSpec axisVtxZ{500, -25., 25., ""}; ue.add("hVtxFT0VsVtxCol", " ", HistType::kTH2D, {{axisVtxZ}, {axisVtxZ}}); ue.add("hVtxFT0VsVtxCol_afterSel8", " ", HistType::kTH2D, {{axisVtxZ}, {axisVtxZ}}); @@ -422,8 +427,14 @@ struct ueCharged { if ((std::abs(collision.posZ()) > CollPosZ)) { return false; } - ue.fill(HIST("hCounter"), 5); + + if (GoodITS && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + + ue.fill(HIST("hCounter"), 6); + return true; } @@ -470,6 +481,12 @@ struct ueCharged { } ue.fill(HIST("hCounter"), 5); + + if (GoodITS && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return false; + } + + ue.fill(HIST("hCounter"), 6); return true; } @@ -652,7 +669,7 @@ struct ueCharged { } PROCESS_SWITCH(ueCharged, processData, "Process data", false); - void processMC(ColMCTrueTable::iterator const& mcCollision, + void processMC(ColMCTrueTableWithExtra::iterator const& mcCollision, ColMCRecTable const& RecCols, TrackMCTrueTable const& GenParticles, TrackMCRecTable const& RecTracks, @@ -672,20 +689,20 @@ struct ueCharged { ue.fill(HIST("hStat"), mcCollision.size()); const auto vtxZ = mcCollision.posZ(); - // pick best reconstructed collision associated to this mcCollision (max ntracks = recRow.size()) + // pick best reconstructed collision associated to this mcCollision using bestCollisionIndex bool foundRec = false; auto chosenRec = *RecCols.begin(); int64_t chosenRecGlobalIndex = -1; - int maxTracks = -1; - if (RecCols.size() != 0) { + int bestCollIdx = mcCollision.bestCollisionIndex(); + if (bestCollIdx >= 0) { + // Find the reconstructed collision with this index for (const auto& recRow : RecCols) { - int ntracks = recRow.size(); - if (ntracks > maxTracks) { + if (recRow.globalIndex() == bestCollIdx) { chosenRec = recRow; - chosenRecGlobalIndex = recRow.globalIndex(); - maxTracks = ntracks; + chosenRecGlobalIndex = bestCollIdx; foundRec = true; + break; } } } @@ -1154,12 +1171,17 @@ struct ueCharged { ue.fill(HIST("hvtxZ_after"), collision.posZ()); + if (!isMCEventSelected(collision)) + return; + int tracks_before = 0; int tracks_after = 0; for (auto& track : tracks) { if (track.hasITS() && track.hasTPC()) { + ue.fill(HIST("hPtVsDCAxy"), track.pt(), track.dcaXY()); + ue.fill(HIST("hPtVsDCAz"), track.pt(), track.dcaZ()); ue.fill(HIST("preselection_track/ITS/itsNCls"), track.itsNCls()); ue.fill(HIST("preselection_track/ITS/itsChi2NCl"), track.itsChi2NCl()); ue.fill(HIST("preselection_track/ITS/itsClusterMap"), track.itsClusterMap()); diff --git a/PWGUD/AQC/udQC.cxx b/PWGUD/AQC/udQC.cxx index 0edcdc28117..24117f9d6e2 100644 --- a/PWGUD/AQC/udQC.cxx +++ b/PWGUD/AQC/udQC.cxx @@ -97,7 +97,7 @@ struct UDQC { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { @@ -711,7 +711,7 @@ struct UDQC { return; } - for (auto fv0 : fv0s) { + for (const auto& fv0 : fv0s) { registry.get(HIST("FV0/hV0A"))->Fill(fv0.time()); // side A for (size_t ind = 0; ind < fv0.channel().size(); ind++) { @@ -738,7 +738,7 @@ struct UDQC { registry.get(HIST("FT0/hT0AC"))->Fill(collision.t0AC()); } } - for (auto ft0 : ft0s) { + for (const auto& ft0 : ft0s) { registry.get(HIST("FT0/hT0A"))->Fill(ft0.timeA()); registry.get(HIST("FT0/hT0C"))->Fill(ft0.timeC()); @@ -760,7 +760,7 @@ struct UDQC { { // LOGF(debug, " %d", fdds.size()); - for (auto fdd : fdds) { + for (const auto& fdd : fdds) { registry.get(HIST("FDD/hFDDA"))->Fill(fdd.timeA()); registry.get(HIST("FDD/hFDDC"))->Fill(fdd.timeC()); diff --git a/PWGUD/AQC/udQCmidRap.cxx b/PWGUD/AQC/udQCmidRap.cxx index 6a2ea533d4c..1aaff2bb186 100644 --- a/PWGUD/AQC/udQCmidRap.cxx +++ b/PWGUD/AQC/udQCmidRap.cxx @@ -86,7 +86,7 @@ struct UDQCmid { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/AQC/udQcMuon.cxx b/PWGUD/AQC/udQcMuon.cxx index 9f09ee2d2c9..995fcaa3fec 100644 --- a/PWGUD/AQC/udQcMuon.cxx +++ b/PWGUD/AQC/udQcMuon.cxx @@ -98,7 +98,7 @@ struct UdQcMuon { void init(InitContext& context) { - diffCuts = (DGCutparHolder)dgCuts; + diffCuts = DGCutparHolder(dgCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { diff --git a/PWGUD/Core/CMakeLists.txt b/PWGUD/Core/CMakeLists.txt index 3a7fe0950cf..243935064d4 100644 --- a/PWGUD/Core/CMakeLists.txt +++ b/PWGUD/Core/CMakeLists.txt @@ -71,4 +71,40 @@ o2physics_add_library(FITCutParHolder o2physics_target_root_dictionary(FITCutParHolder HEADERS FITCutParHolder.h - LINKDEF FITCutParHolderLinkDef.h) \ No newline at end of file + LINKDEF FITCutParHolderLinkDef.h) +# Precompiled header shared across PWGUD. Percentages are the share of the +# group's workflow translation units that include the header, measured over the tree. +# UDTables.h is the group's single shared data model. +# +# runDataProcessing.h is deliberately NOT precompiled: it requires each workflow's +# customize() to be declared before it is included, so precompiling it silently +# unregisters every option (the binary aborts at --dump-workflow, but links fine). +# +# Skipped under recc, which caches compilations remotely instead. +add_library(UDTablesPCH OBJECT udTablesPCH.cxx) +# The PCH compile is one of the heaviest translation units in the tree -- it +# instantiates the whole data model up front -- but add_library() does not put +# it in the 'analysis' job pool that o2physics_add_dpl_workflow() uses for the +# workflows. Left out, it runs unthrottled alongside the pooled compilations +# and defeats the per-job memory budget the pool exists to enforce. +set_property(TARGET UDTablesPCH PROPERTY JOB_POOL_COMPILE analysis) +target_link_libraries(UDTablesPCH PUBLIC O2::Framework O2Physics::AnalysisCore) +if(NOT DEFINED ENV{USE_RECC}) + target_precompile_headers(UDTablesPCH PRIVATE + # Framework baseline — same set AnalysisPCH precompiles. A target with + # REUSE_FROM uses ONLY this PCH, so anything left out here is parsed from + # scratch by every consumer: without this block these workflows were + # precompiling LESS than the default carrier they opted out of. + + + + + + + + + + + [["PWGUD/DataModel/UDTables.h"]] # 79%, ~650 lines + ) +endif() diff --git a/PWGUD/Core/DGCutparHolder.cxx b/PWGUD/Core/DGCutparHolder.cxx index ee003684f5a..146698cca8f 100644 --- a/PWGUD/Core/DGCutparHolder.cxx +++ b/PWGUD/Core/DGCutparHolder.cxx @@ -11,6 +11,7 @@ #include "PWGUD/Core/DGCutparHolder.h" +#include #include // setter @@ -45,7 +46,7 @@ void DGCutparHolder::SetNTracks(int MinNTracks, int MaxNTracks) } void DGCutparHolder::SetNetCharges(std::vector netCharges) { - mNetCharges = netCharges; + mNetCharges = std::move(netCharges); } void DGCutparHolder::SetPidHypothesis(int pidHypo) { @@ -102,11 +103,11 @@ void DGCutparHolder::SetMaxFITtime(float maxFITtime) } void DGCutparHolder::SetFITAmpLimits(std::vector FITAmpLimits) { - mFITAmpLimits = FITAmpLimits; + mFITAmpLimits = std::move(FITAmpLimits); } void DGCutparHolder::SetCollisionSel(std::vector collisionSel) { - mCollisionSel = collisionSel; + mCollisionSel = std::move(collisionSel); } // getter diff --git a/PWGUD/Core/DGCutparHolder.h b/PWGUD/Core/DGCutparHolder.h index a67aefc4080..472a781b535 100644 --- a/PWGUD/Core/DGCutparHolder.h +++ b/PWGUD/Core/DGCutparHolder.h @@ -14,6 +14,7 @@ #include +#include #include // object to hold customizable cut values @@ -40,7 +41,7 @@ class DGCutparHolder bool TOR = true, float maxFITtime = 4, std::vector FITAmpLimits = {0., 0., 0., 0., 0.}, - std::vector collisionSel = {1, 1, 1, 0, 1, 0, 0}) : mNDtcoll{ndtcoll}, mMinNBCs{nMinBCs}, mWithFwdTracks{withFwdTracks}, mGlobalTracksOnly{globalTracksOnly}, mITSOnlyTracks{ITSonlyTracks}, mMinRgtrwTOF{minrgtrwTOF}, mMinNTracks{MinNTracks}, mMaxNTracks{MaxNTracks}, mNetCharges{NetCharges}, mPidHypo{pidHypo}, mMinVertexPosz{MinPosz}, mMaxVertexPosz{MaxPosz}, mMinPt{minPt}, mMaxPt{maxPt}, mMinEta{minEta}, mMaxEta{maxEta}, mMinIVM{minIVM}, mMaxIVM{maxIVM}, mMaxNSigmaTPC{maxNSigmaTPC}, mMaxNSigmaTOF{maxNSigmaTOF}, mTVX{TVX}, mTSC{TSC}, mTCE{TCE}, mTOR{TOR}, mMaxFITtime{maxFITtime}, mFITAmpLimits{FITAmpLimits}, mCollisionSel{collisionSel} + std::vector collisionSel = {1, 1, 1, 0, 1, 0, 0}) : mNDtcoll{ndtcoll}, mMinNBCs{nMinBCs}, mWithFwdTracks{withFwdTracks}, mGlobalTracksOnly{globalTracksOnly}, mITSOnlyTracks{ITSonlyTracks}, mMinRgtrwTOF{minrgtrwTOF}, mMinNTracks{MinNTracks}, mMaxNTracks{MaxNTracks}, mNetCharges{std::move(NetCharges)}, mPidHypo{pidHypo}, mMinVertexPosz{MinPosz}, mMaxVertexPosz{MaxPosz}, mMinPt{minPt}, mMaxPt{maxPt}, mMinEta{minEta}, mMaxEta{maxEta}, mMinIVM{minIVM}, mMaxIVM{maxIVM}, mMaxNSigmaTPC{maxNSigmaTPC}, mMaxNSigmaTOF{maxNSigmaTOF}, mTVX{TVX}, mTSC{TSC}, mTCE{TCE}, mTOR{TOR}, mMaxFITtime{maxFITtime}, mFITAmpLimits{std::move(FITAmpLimits)}, mCollisionSel{std::move(collisionSel)} { } diff --git a/PWGUD/Core/DGCutparHolderLinkDef.h b/PWGUD/Core/DGCutparHolderLinkDef.h index b6b7c63efca..722796d015f 100644 --- a/PWGUD/Core/DGCutparHolderLinkDef.h +++ b/PWGUD/Core/DGCutparHolderLinkDef.h @@ -8,12 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class DGCutparHolder + ; - -#endif // PWGUD_CORE_DGCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/DGPIDSelector.cxx b/PWGUD/Core/DGPIDSelector.cxx index 7e0de287e48..054b548b602 100644 --- a/PWGUD/Core/DGPIDSelector.cxx +++ b/PWGUD/Core/DGPIDSelector.cxx @@ -22,6 +22,7 @@ #include #include #include +#include #include // ----------------------------------------------------------------------------- @@ -87,7 +88,7 @@ DGPIDCuts::DGPIDCuts() DGPIDCuts::DGPIDCuts(std::vector PIDCutValues) { - setPIDCuts(PIDCutValues); + setPIDCuts(std::move(PIDCutValues)); } DGPIDCuts::~DGPIDCuts() @@ -196,7 +197,7 @@ void DGAnaparHolder::SetdBC(int min, int max) mdBCMax = max; } -void DGAnaparHolder::SetFITvetoes(std::vector vetoes) +void DGAnaparHolder::SetFITvetoes(const std::vector& vetoes) { if (vetoes.size() == 5) { @@ -254,22 +255,22 @@ void DGAnaparHolder::SetnCombine(std::size_t nComb) void DGAnaparHolder::SetnetCharges(std::vector charges) { - mNetCharges = charges; + mNetCharges = std::move(charges); } void DGAnaparHolder::SetunlikeCharges(std::vector charges) { - mUnlikeCharges = charges; + mUnlikeCharges = std::move(charges); } void DGAnaparHolder::SetlikeCharges(std::vector charges) { - mLikeCharges = charges; + mLikeCharges = std::move(charges); } void DGAnaparHolder::SetPIDs(std::vector pids) { - mDGPIDs = pids; + mDGPIDs = std::move(pids); } // ----------------------------------------------------------------------------- @@ -435,7 +436,7 @@ void DGPIDSelector::Print() mAnaPars.Print(); } -void DGPIDSelector::init(DGAnaparHolder anaPars) +void DGPIDSelector::init(const DGAnaparHolder& anaPars) { mAnaPars = anaPars; mUnlikeIVMs.clear(); diff --git a/PWGUD/Core/DGPIDSelector.h b/PWGUD/Core/DGPIDSelector.h index de825ab3568..0480a0f2b1c 100644 --- a/PWGUD/Core/DGPIDSelector.h +++ b/PWGUD/Core/DGPIDSelector.h @@ -21,6 +21,7 @@ #include #include #include +#include #include const int numDGPIDCutParameters = 9; @@ -124,7 +125,7 @@ struct DGAnaparHolder { std::vector unlikeCharges = {0}, std::vector likeCharges = {-2, 2}, std::vector DGPIDs = {211, 211}, - std::vector DGPIDCutValues = {}) : mMinNTracks{MinNTracks}, mMaxNTracks{MaxNTracks}, mMinRgtrwTOF{minrgtrwTOF}, mMaxDCAxy{maxDCAxy}, mMaxDCAz{maxDCAz}, mdBCMin{dBCMin}, mdBCMax{dBCMax}, mFITvetoes{FITvetoes}, mITSOnlyTracks{ITSonlyTracks}, mMinNClTPC{minNClTPC}, mMaxNClTPC{maxNClTPC}, mMinChi2NClTPC{minChi2NClTPC}, mMaxChi2NClTPC{maxChi2NClTPC}, mMinpt{minpt}, mMaxpt{maxpt}, mMineta{mineta}, mMaxeta{maxeta}, mMinAlpha{minalpha}, mMaxAlpha{maxalpha}, mMinptsys{minptsys}, mMaxptsys{maxptsys}, mNCombine{nCombine}, mNetCharges{netCharges}, mUnlikeCharges{unlikeCharges}, mLikeCharges{likeCharges}, mDGPIDs{DGPIDs}, mDGPIDCutValues{DGPIDCutValues} + std::vector DGPIDCutValues = {}) : mMinNTracks{MinNTracks}, mMaxNTracks{MaxNTracks}, mMinRgtrwTOF{minrgtrwTOF}, mMaxDCAxy{maxDCAxy}, mMaxDCAz{maxDCAz}, mdBCMin{dBCMin}, mdBCMax{dBCMax}, mFITvetoes{std::move(FITvetoes)}, mITSOnlyTracks{ITSonlyTracks}, mMinNClTPC{minNClTPC}, mMaxNClTPC{maxNClTPC}, mMinChi2NClTPC{minChi2NClTPC}, mMaxChi2NClTPC{maxChi2NClTPC}, mMinpt{minpt}, mMaxpt{maxpt}, mMineta{mineta}, mMaxeta{maxeta}, mMinAlpha{minalpha}, mMaxAlpha{maxalpha}, mMinptsys{minptsys}, mMaxptsys{maxptsys}, mNCombine{nCombine}, mNetCharges{std::move(netCharges)}, mUnlikeCharges{std::move(unlikeCharges)}, mLikeCharges{std::move(likeCharges)}, mDGPIDs{std::move(DGPIDs)}, mDGPIDCutValues{std::move(DGPIDCutValues)} { if (mdBCMin < -16) { mdBCMin = -16; @@ -148,7 +149,7 @@ struct DGAnaparHolder { void SetMinRgtrwTOF(float); void SetmaxDCA(float, float); void SetdBC(int, int); - void SetFITvetoes(std::vector); + void SetFITvetoes(const std::vector&); void SetITSOnlyTracks(bool); void SetNClTPC(int, int); void SetChi2NClTPC(float, float); @@ -239,7 +240,7 @@ struct DGParticle { public: DGParticle(); template - DGParticle(TDatabasePDG* pdg, DGAnaparHolder anaPars, TTrack const& tracks, std::vector comb) + DGParticle(TDatabasePDG* pdg, const DGAnaparHolder& anaPars, TTrack const& tracks, const std::vector& comb) { // compute invariant mass TLorentzVector lvtmp; @@ -284,12 +285,12 @@ struct DGPIDSelector { ~DGPIDSelector(); // setters - void init(DGAnaparHolder anaPars); + void init(const DGAnaparHolder& anaPars); // getters void Print(); template - bool isGoodCombination(std::vector comb, TTrack const& tracks, std::vector acceptedCharges) + bool isGoodCombination(const std::vector& comb, TTrack const& tracks, std::vector acceptedCharges) { // compute net charge of track combination int netCharge = 0.; diff --git a/PWGUD/Core/DGPIDSelectorLinkDef.h b/PWGUD/Core/DGPIDSelectorLinkDef.h index 2d0b181ba19..837079914e1 100644 --- a/PWGUD/Core/DGPIDSelectorLinkDef.h +++ b/PWGUD/Core/DGPIDSelectorLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/DGSelector.h b/PWGUD/Core/DGSelector.h index 5c9afa99d00..2fde726a94c 100644 --- a/PWGUD/Core/DGSelector.h +++ b/PWGUD/Core/DGSelector.h @@ -37,7 +37,7 @@ class DGSelector ~DGSelector() { delete fPDG; } template - int Print(DGCutparHolder /*diffCuts*/, CC& collision, BCs& /*bcRange*/, TCs& /*tracks*/, FWs& /*fwdtracks*/) + int Print(const DGCutparHolder& /*diffCuts*/, CC& collision, BCs& /*bcRange*/, TCs& /*tracks*/, FWs& /*fwdtracks*/) { LOGF(info, "Size of array %i", collision.size()); return 1; diff --git a/PWGUD/Core/FITCutParHolderLinkDef.h b/PWGUD/Core/FITCutParHolderLinkDef.h index 3194a6754b2..5e0644779cb 100644 --- a/PWGUD/Core/FITCutParHolderLinkDef.h +++ b/PWGUD/Core/FITCutParHolderLinkDef.h @@ -13,12 +13,9 @@ // \author Sandor Lokos, sandor.lokos@cern.ch // \since March 2026 -#ifndef PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class FITCutParHolder + ; - -#endif // PWGUD_CORE_FITCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/SGCutParHolder.cxx b/PWGUD/Core/SGCutParHolder.cxx index ba180a97280..45519efebf6 100644 --- a/PWGUD/Core/SGCutParHolder.cxx +++ b/PWGUD/Core/SGCutParHolder.cxx @@ -11,6 +11,7 @@ #include "PWGUD/Core/SGCutParHolder.h" +#include #include // setter @@ -69,7 +70,7 @@ void SGCutParHolder::SetMaxFITtime(float maxFITtime) } void SGCutParHolder::SetFITAmpLimits(std::vector FITAmpLimits) { - mFITAmpLimits = FITAmpLimits; + mFITAmpLimits = std::move(FITAmpLimits); } // getter diff --git a/PWGUD/Core/SGCutParHolder.h b/PWGUD/Core/SGCutParHolder.h index fcee2d79703..d02848fafbe 100644 --- a/PWGUD/Core/SGCutParHolder.h +++ b/PWGUD/Core/SGCutParHolder.h @@ -14,6 +14,7 @@ #include +#include #include // object to hold customizable cut values @@ -32,7 +33,7 @@ class SGCutParHolder float minEta = -1.0, float maxEta = 1.0, float maxFITtime = 4, float minRgtrwTOF = 0., - std::vector FITAmpLimits = {0., 0., 0., 0., 0.}) : mNDtcoll{ndtcoll}, mMinNBCs{nMinBCs}, mWithFwdTracks{withFwdTracks}, mGlobalTracksOnly{globalTracksOnly}, mITSOnlyTracks{ITSonlyTracks}, mMinNTracks{MinNTracks}, mMaxNTracks{MaxNTracks}, mPidHypo{pidHypo}, mMinVertexPosz{MinPosz}, mMaxVertexPosz{MaxPosz}, mMinPt{minPt}, mMaxPt{maxPt}, mMinEta{minEta}, mMaxEta{maxEta}, mMaxFITtime{maxFITtime}, mMinRgtrwTOF{minRgtrwTOF}, mFITAmpLimits{FITAmpLimits} + std::vector FITAmpLimits = {0., 0., 0., 0., 0.}) : mNDtcoll{ndtcoll}, mMinNBCs{nMinBCs}, mWithFwdTracks{withFwdTracks}, mGlobalTracksOnly{globalTracksOnly}, mITSOnlyTracks{ITSonlyTracks}, mMinNTracks{MinNTracks}, mMaxNTracks{MaxNTracks}, mPidHypo{pidHypo}, mMinVertexPosz{MinPosz}, mMaxVertexPosz{MaxPosz}, mMinPt{minPt}, mMaxPt{maxPt}, mMinEta{minEta}, mMaxEta{maxEta}, mMaxFITtime{maxFITtime}, mMinRgtrwTOF{minRgtrwTOF}, mFITAmpLimits{std::move(FITAmpLimits)} { } diff --git a/PWGUD/Core/SGCutParHolderLinkDef.h b/PWGUD/Core/SGCutParHolderLinkDef.h index 5dcadadc69e..f5d3a6f5a82 100644 --- a/PWGUD/Core/SGCutParHolderLinkDef.h +++ b/PWGUD/Core/SGCutParHolderLinkDef.h @@ -9,12 +9,9 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ -#define PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class SGCutParHolder + ; - -#endif // PWGUD_CORE_SGCUTPARHOLDERLINKDEF_H_ diff --git a/PWGUD/Core/UDFSParser.cxx b/PWGUD/Core/UDFSParser.cxx index 961412106c8..5ce038fe3de 100644 --- a/PWGUD/Core/UDFSParser.cxx +++ b/PWGUD/Core/UDFSParser.cxx @@ -138,8 +138,8 @@ bool UDFSParser::isNumber(std::string s) } // ----------------------------------------------------------------------------- -std::string UDFSParser::trim(std::string str, - std::string whitespace = " \t") +std::string UDFSParser::trim(const std::string& str, + const std::string& whitespace = " \t") { const auto strBegin = str.find_first_not_of(whitespace); if (strBegin == std::string::npos) diff --git a/PWGUD/Core/UDFSParser.h b/PWGUD/Core/UDFSParser.h index a48fb9f8a4f..1e73957243f 100644 --- a/PWGUD/Core/UDFSParser.h +++ b/PWGUD/Core/UDFSParser.h @@ -60,7 +60,7 @@ class UDFSParser // helper functions for string parsing bool isNumber(std::string s); - std::string trim(std::string str, std::string whitespace); + std::string trim(const std::string& str, const std::string& whitespace); std::vector tokenize(std::string& str, std::string separator = ","); bool isInVector(int num, std::vector vec); diff --git a/PWGUD/Core/UDFSParserLinkDef.h b/PWGUD/Core/UDFSParserLinkDef.h index f7caf1a0ab0..8f6e10eabd6 100644 --- a/PWGUD/Core/UDFSParserLinkDef.h +++ b/PWGUD/Core/UDFSParserLinkDef.h @@ -8,12 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_UDFSPARSERLINKDEF_H_ -#define PWGUD_CORE_UDFSPARSERLINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class UDFSParser + ; - -#endif // PWGUD_CORE_UDFSPARSERLINKDEF_H_ diff --git a/PWGUD/Core/UDGoodRunSelector.cxx b/PWGUD/Core/UDGoodRunSelector.cxx index c8203a7a569..0eb377379a3 100644 --- a/PWGUD/Core/UDGoodRunSelector.cxx +++ b/PWGUD/Core/UDGoodRunSelector.cxx @@ -69,7 +69,7 @@ bool UDGoodRunSelector::isGoodRun(int runNumber) } } -std::vector UDGoodRunSelector::goodRuns(std::string runPeriod) +std::vector UDGoodRunSelector::goodRuns(const std::string& runPeriod) { auto it = mrunMap.find(runPeriod.c_str()); if (it != mrunMap.end()) { diff --git a/PWGUD/Core/UDGoodRunSelector.h b/PWGUD/Core/UDGoodRunSelector.h index 1b73f2c3ac4..31cce6f22fd 100644 --- a/PWGUD/Core/UDGoodRunSelector.h +++ b/PWGUD/Core/UDGoodRunSelector.h @@ -33,7 +33,7 @@ struct UDGoodRunSelector { void Print(); bool isGoodRun(int runNumber); std::vector goodRuns() { return mgoodRuns; } - std::vector goodRuns(std::string runPeriod); + std::vector goodRuns(const std::string& runPeriod); int rnumMin() { return mrnMin; } int rnumMax() { return mrnMax; } diff --git a/PWGUD/Core/UDGoodRunSelectorLinkDef.h b/PWGUD/Core/UDGoodRunSelectorLinkDef.h index c26b4622809..c439e2e1094 100644 --- a/PWGUD/Core/UDGoodRunSelectorLinkDef.h +++ b/PWGUD/Core/UDGoodRunSelectorLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/UDHelpers.h b/PWGUD/Core/UDHelpers.h index 94d356501a7..ed94841fda5 100644 --- a/PWGUD/Core/UDHelpers.h +++ b/PWGUD/Core/UDHelpers.h @@ -130,7 +130,7 @@ T compatibleBCs(B const& bc, uint64_t const& meanBC, int const& deltaBC, T const // check [min,max]BC to overlap with [bcs.iteratorAt([0,bcs.size() - 1]) if (maxBC < bcs.iteratorAt(0).globalBC() || minBC > bcs.iteratorAt(bcs.size() - 1).globalBC()) { LOGF(debug, " No overlap of [%d, %d] and [%d, %d]", minBC, maxBC, bcs.iteratorAt(0).globalBC(), bcs.iteratorAt(bcs.size() - 1).globalBC()); - return T{{bcs.asArrowTable()->Slice(0, 0)}, static_cast(0)}; + return bcs.emptySlice(); } // find slice of BCs table with BC in [minBC, maxBC] @@ -164,7 +164,7 @@ T compatibleBCs(B const& bc, uint64_t const& meanBC, int const& deltaBC, T const } // create bc slice - T bcslice{{bcs.asArrowTable()->Slice(minBCId, maxBCId - minBCId + 1)}, static_cast(minBCId)}; + auto bcslice = bcs.rawSlice(minBCId, maxBCId); bcs.copyIndexBindings(bcslice); LOGF(debug, " size of slice %d", bcslice.size()); return bcslice; @@ -179,7 +179,7 @@ T compatibleBCs(C const& collision, int ndt, T const& bcs, int nMinBCs = 7) // return if collisions has no associated BC if (!collision.has_foundBC() || ndt < 0) { - return T{{bcs.asArrowTable()->Slice(0, 0)}, static_cast(0)}; + return bcs.emptySlice(); } // get associated BC @@ -220,7 +220,7 @@ T MCcompatibleBCs(F const& collision, int const& ndt, T const& bcs, int const& n // return if collisions has no associated BC if (!collision.has_foundBC()) { LOGF(debug, "Collision %i - no BC found!", collision.globalIndex()); - return T{{bcs.asArrowTable()->Slice(0, 0)}, static_cast(0)}; + return bcs.emptySlice(); } // get associated BC @@ -243,7 +243,7 @@ T MCcompatibleBCs(F const& collision, int const& ndt, T const& bcs, int const& n // function to check if track provides good PID information // Checks the nSigma for any particle assumption to be within limits. template -bool hasGoodPID(DGCutparHolder diffCuts, TC track) +bool hasGoodPID(const DGCutparHolder& diffCuts, TC track) { // El, Mu, Pi, Ka, and Pr are considered // at least one nSigma must be within set limits @@ -425,14 +425,14 @@ bool cleanFDD(T& bc, float maxFITtime, float limitA, float limitC) // lims[4]: FDDC template -bool cleanFIT(T& bc, float maxFITtime, std::vector lims) +bool cleanFIT(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0(bc, maxFITtime, lims[1], lims[2]) && cleanFDD(bc, maxFITtime, lims[3], lims[4]); } template -bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) +bool cleanFITCollision(T& col, float maxFITtime, std::vector const& lims) { bool isCleanFV0 = true; if (col.has_foundFV0()) { @@ -453,7 +453,7 @@ bool cleanFITCollision(T& col, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITA(T& bc, float maxFITtime, std::vector lims) +bool cleanFITA(T& bc, float maxFITtime, std::vector const& lims) { return cleanFV0(bc, maxFITtime, lims[0]) && cleanFT0A(bc, maxFITtime, lims[1]) && @@ -462,7 +462,7 @@ bool cleanFITA(T& bc, float maxFITtime, std::vector lims) // ----------------------------------------------------------------------------- template -bool cleanFITC(T& bc, float maxFITtime, std::vector lims) +bool cleanFITC(T& bc, float maxFITtime, std::vector const& lims) { return cleanFT0C(bc, maxFITtime, lims[2]) && cleanFDDC(bc, maxFITtime, lims[4]); @@ -506,7 +506,7 @@ bool TCE(T& bc) // ----------------------------------------------------------------------------- template -bool TOR(T& bc, float maxFITtime, std::vector lims) +bool TOR(T& bc, float maxFITtime, std::vector const& lims) { auto torA = !cleanFT0A(bc, maxFITtime, lims[1]); auto torC = !cleanFT0C(bc, maxFITtime, lims[2]); @@ -565,27 +565,48 @@ inline void buildFT0FV0Words(TFT0 const& ft0, TFV0A const& fv0a, constexpr int kFV0Offset = 208; auto ampsA = ft0.amplitudeA(); - const int nA = std::min(ampsA.size(), 96); + auto chanA = ft0.channelA(); + const int nA = std::min(ampsA.size(), chanA.size()); for (int i = 0; i < nA; ++i) { const auto a = ampsA[i]; - setBit(thr1, kFT0AOffset + i, a >= thr1_FT0A); - setBit(thr2, kFT0AOffset + i, a >= thr2_FT0A); + const int c = chanA[i]; + + if (c < 0 || c >= 96) { + continue; + } + + setBit(thr1, kFT0AOffset + c, a >= thr1_FT0A); + setBit(thr2, kFT0AOffset + c, a >= thr2_FT0A); } auto ampsC = ft0.amplitudeC(); - const int nC = std::min(ampsC.size(), 112); + auto chanC = ft0.channelC(); + const int nC = std::min(ampsC.size(), chanC.size()); for (int i = 0; i < nC; ++i) { const auto a = ampsC[i]; - setBit(thr1, kFT0COffset + i, a >= thr1_FT0C); - setBit(thr2, kFT0COffset + i, a >= thr2_FT0C); + const int c = chanC[i]; + + if (c < 0 || c >= 112) { + continue; + } + + setBit(thr1, kFT0COffset + c, a >= thr1_FT0C); + setBit(thr2, kFT0COffset + c, a >= thr2_FT0C); } auto ampsV = fv0a.amplitude(); - const int nV = std::min(ampsV.size(), 48); + auto chanV = fv0a.channel(); + const int nV = std::min(ampsV.size(), chanV.size()); for (int i = 0; i < nV; ++i) { const auto a = ampsV[i]; - setBit(thr1, kFV0Offset + i, a >= thr1_FV0A); - setBit(thr2, kFV0Offset + i, a >= thr2_FV0A); + const int c = chanV[i]; + + if (c < 0 || c >= 48) { + continue; + } + + setBit(thr1, kFV0Offset + c, a >= thr1_FV0A); + setBit(thr2, kFV0Offset + c, a >= thr2_FV0A); } } @@ -777,24 +798,15 @@ template bool goodCollision(T const& coll, DGCutparHolder const& diffCuts) // Return true if collision is accepted according to user-chosen rules from event selection task { - bool accepted = true; - std::vector sels = diffCuts.collisionSel(); - if (sels[0]) - accepted = accepted && cutNoTimeFrameBorder(coll); - if (sels[1]) - accepted = accepted && cutNoSameBunchPileup(coll); - if (sels[2]) - accepted = accepted && cutNoITSROFrameBorder(coll); - if (sels[3]) - accepted = accepted && cutIsGoodZvtxFT0vsPV(coll); - if (sels[4]) - accepted = accepted && cutIsVertexITSTPC(coll); - if (sels[5]) - accepted = accepted && cutIsVertexTRDmatched(coll); - if (sels[6]) - accepted = accepted && cutIsVertexTOFmatched(coll); - - return accepted; + auto const sels = diffCuts.collisionSel(); + + return (!sels[0] || cutNoTimeFrameBorder(coll)) && + (!sels[1] || cutNoSameBunchPileup(coll)) && + (!sels[2] || cutNoITSROFrameBorder(coll)) && + (!sels[3] || cutIsGoodZvtxFT0vsPV(coll)) && + (!sels[4] || cutIsVertexITSTPC(coll)) && + (!sels[5] || cutIsVertexTRDmatched(coll)) && + (!sels[6] || cutIsVertexTOFmatched(coll)); } // ----------------------------------------------------------------------------- @@ -891,7 +903,7 @@ bool cleanCalo(T const& bc, o2::aod::Calos& calos, std::vector& /*lims*/, // ----------------------------------------------------------------------------- // check if all tracks come from same MCCollision template -int64_t sameMCCollision(T tracks, o2::aod::McCollisions, o2::aod::McParticles) +int64_t sameMCCollision(T tracks, const o2::aod::McCollisions&, const o2::aod::McParticles&) { int64_t colID = -1; for (auto const& track : tracks) { diff --git a/PWGUD/Core/UPCCutparHolderLinkDef.h b/PWGUD/Core/UPCCutparHolderLinkDef.h index 49d4802838d..6391b991669 100644 --- a/PWGUD/Core/UPCCutparHolderLinkDef.h +++ b/PWGUD/Core/UPCCutparHolderLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/PWGUD/Core/UPCJpsiCentralBarrelCorrHelper.h b/PWGUD/Core/UPCJpsiCentralBarrelCorrHelper.h index 83a3eed9053..4404fc0dee2 100644 --- a/PWGUD/Core/UPCJpsiCentralBarrelCorrHelper.h +++ b/PWGUD/Core/UPCJpsiCentralBarrelCorrHelper.h @@ -213,7 +213,7 @@ float* correlation(TLorentzVector* lv1, TLorentzVector* lv2, TLorentzVector* lv) return q; } -double DeltaPhi(TLorentzVector lv1, TLorentzVector lv2) +double DeltaPhi(const TLorentzVector& lv1, const TLorentzVector& lv2) { TLorentzVector lv_sum = lv1 + lv2; TLorentzVector lv_diff = lv1 - lv2; @@ -223,7 +223,7 @@ double DeltaPhi(TLorentzVector lv1, TLorentzVector lv2) return dp; } -double DeltaPhiRandom(TLorentzVector lv1, TLorentzVector lv2) +double DeltaPhiRandom(const TLorentzVector& lv1, const TLorentzVector& lv2) { std::vector indices = {0, 1}; unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); diff --git a/PWGUD/Core/UPCTauCentralBarrelHelperRL.h b/PWGUD/Core/UPCTauCentralBarrelHelperRL.h index ea4652b9b77..9bdaab354e2 100644 --- a/PWGUD/Core/UPCTauCentralBarrelHelperRL.h +++ b/PWGUD/Core/UPCTauCentralBarrelHelperRL.h @@ -52,19 +52,19 @@ enum MyTauChannel { CH_ENUM_COUNTER = 11 }; -void printLargeMessage(std::string info) +void printLargeMessage(const std::string& info) // Helper to printf info message to terminal { LOGF(info, "################################### %s ###################################", info); } -void printMediumMessage(std::string info) +void printMediumMessage(const std::string& info) // Helper to printf info message to terminal { LOGF(info, "+++++++++++++ %s +++++++++++++", info); } -void printDebugMessage(std::string info) +void printDebugMessage(const std::string& info) // Helper to printf info message to terminal { LOGF(debug, "X!X!X!X!X!X!X!X!X %s X!X!X!X!X!X!X!X!X", info); diff --git a/PWGUD/Core/decayTree.cxx b/PWGUD/Core/decayTree.cxx index ded7c2fa329..880942d3285 100644 --- a/PWGUD/Core/decayTree.cxx +++ b/PWGUD/Core/decayTree.cxx @@ -12,6 +12,7 @@ #include "decayTree.h" #include +#include #include #include @@ -92,7 +93,7 @@ void pidSelector::Print() // angleCut angleCut::angleCut(std::pair rnames, double angleMin, double angleMax) { - fRnames = rnames; + fRnames = std::move(rnames); fAngleMin = angleMin; fAngleMax = angleMax; } @@ -981,7 +982,7 @@ void decayTree::Print() } } -resonance* decayTree::getResonance(std::string name) +resonance* decayTree::getResonance(const std::string& name) { for (const auto& res : fResonances) { if (res->name() == name) { @@ -1228,4 +1229,162 @@ std::vector> decayTree::combinations(int nPool) return copes; } +void decayTree::createHistograms(o2::framework::HistogramRegistry& registry) +{ + // definitions + auto etax = o2::framework::AxisSpec(100, -1.5, 1.5); + auto nSax = o2::framework::AxisSpec(300, -15.0, 15.0); + auto chi2ax = o2::framework::AxisSpec(100, 0.0, 5.0); + auto nClax = o2::framework::AxisSpec(170, 0.0, 170.0); + auto angax = o2::framework::AxisSpec(315, 0.0, 3.15); + auto dcaxyax = o2::framework::AxisSpec(400, -0.2, 0.2); + auto dcazax = o2::framework::AxisSpec(600, -0.3, 0.3); + auto sTPCax = o2::framework::AxisSpec(1000, 0., 1000.); + + std::string base; + std::string hname; + std::string annot; + fhistPointers.clear(); + for (const auto& res : getResonances()) { + auto max = o2::framework::AxisSpec(res->nmassBins(), res->massHistRange()[0], res->massHistRange()[1]); + auto momax = o2::framework::AxisSpec(res->nmomBins(), res->momHistRange()[0], res->momHistRange()[1]); + + // M-pT, M-eta, pT-eta + for (const auto& cc : fccs) { + base = cc; + base.append("/").append(res->name()).append("/"); + hname = base + "mpt"; + annot = "M versus pT; M (" + res->name() + ") GeV/c^{2}; pT (" + res->name() + ") GeV/c"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, momax}})}); + hname = base + "meta"; + annot = "M versus eta; M (" + res->name() + ") GeV/c^{2}; eta (" + res->name() + ")"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, etax}})}); + hname = base + "pteta"; + annot = "pT versus eta; pT (" + res->name() + ") GeV/c; eta (" + res->name() + ")"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {momax, etax}})}); + + // M versus daughters + auto daughs = res->getDaughters(); + auto ndaughs = daughs.size(); + for (auto i = 0; i < static_cast(ndaughs); i++) { + auto d1 = getResonance(daughs[i]); + + // M vs pT daughter + hname = base; + hname.append("MvspT_").append(res->name()).append(d1->name()); + annot = "M versus pT; M (" + res->name() + ") GeV/c^{2}; pT (" + d1->name() + ") GeV/c"; + auto momax1 = o2::framework::AxisSpec(d1->nmomBins(), d1->momHistRange()[0], d1->momHistRange()[1]); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, momax1}})}); + + // M vs eta daughter + hname = base; + hname.append("Mvseta_").append(res->name()).append(d1->name()); + annot = "M versus eta; M (" + res->name() + ") GeV/c^{2}; eta (" + d1->name() + ")"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, etax}})}); + + if (d1->isFinal()) { + // M vs dcaXYZ + hname = base; + hname.append("MvsdcaXY_").append(res->name()).append(d1->name()); + annot = "M versus dcaXY; M (" + res->name() + ") GeV/c^{2}; dca_{XY} (" + d1->name() + ") #mu m"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, dcaxyax}})}); + hname = base; + hname.append("MvsdcaZ_").append(res->name()).append(d1->name()); + annot = "M versus dcaZ; M (" + res->name() + ") GeV/c^{2}; dca_{Z} (" + d1->name() + ") #mu m"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, dcazax}})}); + + // M vs chi2 track + hname = base; + hname.append("Mvschi2_").append(res->name()).append(d1->name()); + annot = "M versus chi2; M (" + res->name() + ") GeV/c^{2}; chi2 (" + d1->name() + ")"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, chi2ax}})}); + + // M vs nCl track + hname = base; + hname.append("MvsnCl_").append(res->name()).append(d1->name()); + annot = "M versus nCl; M (" + res->name() + ") GeV/c^{2}; nCl (" + d1->name() + ")"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, nClax}})}); + + // M versus detector hits + hname = base; + hname.append("MvsdetHits_").append(res->name()).append(d1->name()); + annot = "M versus detector hits; M (" + res->name() + ") GeV/c^{2}; ITS + 2*TPC + 4*TRD + 8*TOF (" + d1->name() + ")"; + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, {16, -0.5, 15.5}}})}); + } else { + // M vs Mi + hname = base; + hname.append("MvsM_").append(res->name()).append(d1->name()); + annot = "M versus M; M (" + res->name() + ") GeV/c^{2}; M (" + d1->name() + ") GeV/c^{2}"; + auto max1 = o2::framework::AxisSpec(res->nmassBins(), d1->massHistRange()[0], d1->massHistRange()[1]); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, max1}})}); + } + } + + // daughters vs daughters + for (auto i = 0; i < static_cast(ndaughs - 1); i++) { + auto d1 = getResonance(daughs[i]); + auto max1 = o2::framework::AxisSpec(d1->nmassBins(), d1->massHistRange()[0], d1->massHistRange()[1]); + for (auto j = i + 1; j < static_cast(ndaughs); j++) { + auto d2 = getResonance(daughs[j]); + auto max2 = o2::framework::AxisSpec(d2->nmassBins(), d2->massHistRange()[0], d2->massHistRange()[1]); + + // M1 vs M2 + hname = base; + hname.append("MvsM_").append(d1->name()).append(d2->name()); + annot = std::string("M versus M; M (").append(d1->name()).append(") GeV/c^{2}; M (").append(d2->name()).append(") GeV/c^{2}"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max1, max2}})}); + + // angle(d1, d2) + hname = base; + hname.append("angle_").append(d1->name()).append(d2->name()); + annot = std::string("angle; Angle (").append(d1->name()).append(", ").append(d2->name()).append(")"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {angax}})}); + + // M vs angle(d1, d2) + hname = base; + hname.append("Mvsangle_").append(d1->name()).append(d2->name()); + annot = std::string("M versus angle; M (").append(res->name()).append(") GeV/c^{2}; Angle (").append(d1->name()).append(", ").append(d2->name()).append(")"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, angax}})}); + + // both daughters are finals + if (d1->isFinal() && d2->isFinal()) { + hname = base; + hname.append("TPCsignal_").append(d1->name()).append(d2->name()); + annot = std::string("TPC signal of both tracks; TPCsignal (").append(d1->name()).append("); TPCsignal (").append(d2->name()).append(")"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {sTPCax, sTPCax}})}); + } + } + } + + // for finals only + if (res->isFinal()) { + // dca + hname = base; + hname.append("dcaXY"); + annot = std::string("dcaXY; dca_{XY}(").append(res->name()).append(")"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {dcaxyax}})}); + hname = base; + hname.append("dcaZ"); + annot = std::string("dcaZ; dca_{Z}(").append(res->name()).append(")"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {dcazax}})}); + + // nSIgma[TPC, TOF] vs pT + for (const auto& det : fdets) { + for (const auto& part : fparts) { + hname = base; + hname.append("nS").append(part).append(det); + annot = std::string("nSigma_").append(det).append(" versus p; p (").append(res->name()).append(") GeV/c; nSigma_{").append(det).append(", ").append(part).append("} (").append(res->name()).append(")"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {momax, nSax}})}); + } + } + + // detector hits + hname = base; + hname.append("detectorHits"); + annot = std::string("detectorHits; Detector(").append(res->name()).append(")"); + fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {{4, 0.5, 4.5}}})}); + } + } + } +} // ----------------------------------------------------------------------------- diff --git a/PWGUD/Core/decayTree.h b/PWGUD/Core/decayTree.h index 162a1859181..01dae97e12d 100644 --- a/PWGUD/Core/decayTree.h +++ b/PWGUD/Core/decayTree.h @@ -12,7 +12,6 @@ #ifndef PWGUD_CORE_DECAYTREE_H_ #define PWGUD_CORE_DECAYTREE_H_ -#include #include #include @@ -29,6 +28,11 @@ #include #include +namespace o2::framework +{ +class HistogramRegistry; +} + // ----------------------------------------------------------------------------- class pidSelector { @@ -188,9 +192,9 @@ class reconstructedParticle public: // constructor/destructor reconstructedParticle() {} - explicit reconstructedParticle(std::string name, TLorentzVector ivm, std::vector& comb) + explicit reconstructedParticle(std::string name, const TLorentzVector& ivm, std::vector& comb) { - fName = name; + fName = std::move(name); fIVM = ivm; fComb = comb; }; @@ -215,7 +219,7 @@ class reconstructedEvent reconstructedEvent() {} explicit reconstructedEvent(recResType recs, int chargeState, std::vector& comb) { - fRecs = recs; + fRecs = std::move(recs); fComb = comb; fChargeState = chargeState; }; @@ -246,7 +250,7 @@ class resonance void setisFinal() { fisFinal = true; } void setCounter(int counter) { fCounter = counter; } - void setName(std::string name) { fName = name; } + void setName(std::string name) { fName = std::move(name); } void setStatus(int status) { fStatus = status; } void setPID(int pid) { fPID = pid; } void setPIDFun(int pidfun) { fPIDfun = pidfun; } @@ -255,9 +259,9 @@ class resonance fdetectorHits = std::vector{its, tpc, trd, tof}; } void clearParents() { fParents.clear(); } - void addParent(std::string parent) { fParents.push_back(parent); } + void addParent(const std::string& parent) { fParents.push_back(parent); } void setDaughters(std::vector& daughters) { fDaughters = daughters; } - void setIVM(TLorentzVector ivm) + void setIVM(const TLorentzVector& ivm) { fIVM = ivm; fStatus = 1; @@ -295,8 +299,8 @@ class resonance fdcaxyMax = dcaxymax; fdcazMax = dcazmax; } - void setPIDSelector(pidSelector pidcuts) { fpidSelector = pidcuts; } - void setAngleCuts(std::vector anglecuts) { fangleCuts = anglecuts; } + void setPIDSelector(const pidSelector& pidcuts) { fpidSelector = pidcuts; } + void setAngleCuts(std::vector anglecuts) { fangleCuts = std::move(anglecuts); } // histograms void setMassHistAxis(int nbins, double binmin, double binmax) @@ -473,7 +477,7 @@ class decayTree // getters int nFinals() { return fnFinals; } std::vector getResonances() { return fResonances; } - resonance* getResonance(std::string name); + resonance* getResonance(const std::string& name); resonance* getFinal(int counter); std::vector getFinals(resonance* res); std::vector ntrackRange() { return std::vector{fnTracksMin, fnTracksMax}; } @@ -835,164 +839,7 @@ class decayTree } // create histograms - void createHistograms(o2::framework::HistogramRegistry& registry) - { - // definitions - auto etax = o2::framework::AxisSpec(100, -1.5, 1.5); - auto nSax = o2::framework::AxisSpec(300, -15.0, 15.0); - auto chi2ax = o2::framework::AxisSpec(100, 0.0, 5.0); - auto nClax = o2::framework::AxisSpec(170, 0.0, 170.0); - auto angax = o2::framework::AxisSpec(315, 0.0, 3.15); - auto dcaxyax = o2::framework::AxisSpec(400, -0.2, 0.2); - auto dcazax = o2::framework::AxisSpec(600, -0.3, 0.3); - auto sTPCax = o2::framework::AxisSpec(1000, 0., 1000.); - - std::string base; - std::string hname; - std::string annot; - fhistPointers.clear(); - for (const auto& res : getResonances()) { - auto max = o2::framework::AxisSpec(res->nmassBins(), res->massHistRange()[0], res->massHistRange()[1]); - auto momax = o2::framework::AxisSpec(res->nmomBins(), res->momHistRange()[0], res->momHistRange()[1]); - - // M-pT, M-eta, pT-eta - for (const auto& cc : fccs) { - base = cc; - base.append("/").append(res->name()).append("/"); - hname = base + "mpt"; - annot = "M versus pT; M (" + res->name() + ") GeV/c^{2}; pT (" + res->name() + ") GeV/c"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, momax}})}); - hname = base + "meta"; - annot = "M versus eta; M (" + res->name() + ") GeV/c^{2}; eta (" + res->name() + ")"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, etax}})}); - hname = base + "pteta"; - annot = "pT versus eta; pT (" + res->name() + ") GeV/c; eta (" + res->name() + ")"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {momax, etax}})}); - - // M versus daughters - auto daughs = res->getDaughters(); - auto ndaughs = daughs.size(); - for (auto i = 0; i < static_cast(ndaughs); i++) { - auto d1 = getResonance(daughs[i]); - - // M vs pT daughter - hname = base; - hname.append("MvspT_").append(res->name()).append(d1->name()); - annot = "M versus pT; M (" + res->name() + ") GeV/c^{2}; pT (" + d1->name() + ") GeV/c"; - auto momax1 = o2::framework::AxisSpec(d1->nmomBins(), d1->momHistRange()[0], d1->momHistRange()[1]); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, momax1}})}); - - // M vs eta daughter - hname = base; - hname.append("Mvseta_").append(res->name()).append(d1->name()); - annot = "M versus eta; M (" + res->name() + ") GeV/c^{2}; eta (" + d1->name() + ")"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, etax}})}); - - if (d1->isFinal()) { - // M vs dcaXYZ - hname = base; - hname.append("MvsdcaXY_").append(res->name()).append(d1->name()); - annot = "M versus dcaXY; M (" + res->name() + ") GeV/c^{2}; dca_{XY} (" + d1->name() + ") #mu m"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, dcaxyax}})}); - hname = base; - hname.append("MvsdcaZ_").append(res->name()).append(d1->name()); - annot = "M versus dcaZ; M (" + res->name() + ") GeV/c^{2}; dca_{Z} (" + d1->name() + ") #mu m"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, dcazax}})}); - - // M vs chi2 track - hname = base; - hname.append("Mvschi2_").append(res->name()).append(d1->name()); - annot = "M versus chi2; M (" + res->name() + ") GeV/c^{2}; chi2 (" + d1->name() + ")"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, chi2ax}})}); - - // M vs nCl track - hname = base; - hname.append("MvsnCl_").append(res->name()).append(d1->name()); - annot = "M versus nCl; M (" + res->name() + ") GeV/c^{2}; nCl (" + d1->name() + ")"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, nClax}})}); - - // M versus detector hits - hname = base; - hname.append("MvsdetHits_").append(res->name()).append(d1->name()); - annot = "M versus detector hits; M (" + res->name() + ") GeV/c^{2}; ITS + 2*TPC + 4*TRD + 8*TOF (" + d1->name() + ")"; - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, {16, -0.5, 15.5}}})}); - } else { - // M vs Mi - hname = base; - hname.append("MvsM_").append(res->name()).append(d1->name()); - annot = "M versus M; M (" + res->name() + ") GeV/c^{2}; M (" + d1->name() + ") GeV/c^{2}"; - auto max1 = o2::framework::AxisSpec(res->nmassBins(), d1->massHistRange()[0], d1->massHistRange()[1]); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, max1}})}); - } - } - - // daughters vs daughters - for (auto i = 0; i < static_cast(ndaughs - 1); i++) { - auto d1 = getResonance(daughs[i]); - auto max1 = o2::framework::AxisSpec(d1->nmassBins(), d1->massHistRange()[0], d1->massHistRange()[1]); - for (auto j = i + 1; j < static_cast(ndaughs); j++) { - auto d2 = getResonance(daughs[j]); - auto max2 = o2::framework::AxisSpec(d2->nmassBins(), d2->massHistRange()[0], d2->massHistRange()[1]); - - // M1 vs M2 - hname = base; - hname.append("MvsM_").append(d1->name()).append(d2->name()); - annot = std::string("M versus M; M (").append(d1->name()).append(") GeV/c^{2}; M (").append(d2->name()).append(") GeV/c^{2}"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max1, max2}})}); - - // angle(d1, d2) - hname = base; - hname.append("angle_").append(d1->name()).append(d2->name()); - annot = std::string("angle; Angle (").append(d1->name()).append(", ").append(d2->name()).append(")"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {angax}})}); - - // M vs angle(d1, d2) - hname = base; - hname.append("Mvsangle_").append(d1->name()).append(d2->name()); - annot = std::string("M versus angle; M (").append(res->name()).append(") GeV/c^{2}; Angle (").append(d1->name()).append(", ").append(d2->name()).append(")"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {max, angax}})}); - - // both daughters are finals - if (d1->isFinal() && d2->isFinal()) { - hname = base; - hname.append("TPCsignal_").append(d1->name()).append(d2->name()); - annot = std::string("TPC signal of both tracks; TPCsignal (").append(d1->name()).append("); TPCsignal (").append(d2->name()).append(")"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {sTPCax, sTPCax}})}); - } - } - } - - // for finals only - if (res->isFinal()) { - // dca - hname = base; - hname.append("dcaXY"); - annot = std::string("dcaXY; dca_{XY}(").append(res->name()).append(")"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {dcaxyax}})}); - hname = base; - hname.append("dcaZ"); - annot = std::string("dcaZ; dca_{Z}(").append(res->name()).append(")"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {dcazax}})}); - - // nSIgma[TPC, TOF] vs pT - for (const auto& det : fdets) { - for (const auto& part : fparts) { - hname = base; - hname.append("nS").append(part).append(det); - annot = std::string("nSigma_").append(det).append(" versus p; p (").append(res->name()).append(") GeV/c; nSigma_{").append(det).append(", ").append(part).append("} (").append(res->name()).append(")"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH2F, {momax, nSax}})}); - } - } - - // detector hits - hname = base; - hname.append("detectorHits"); - annot = std::string("detectorHits; Detector(").append(res->name()).append(")"); - fhistPointers.insert({hname, registry.add(hname.c_str(), annot.c_str(), {o2::framework::HistType::kTH1F, {{4, 0.5, 4.5}}})}); - } - } - } - } + void createHistograms(o2::framework::HistogramRegistry& registry); // ClassDefNV(decayTree, 1); }; diff --git a/PWGUD/Core/decayTreeLinkDef.h b/PWGUD/Core/decayTreeLinkDef.h index 92d99ccf1bc..6ed7643f91b 100644 --- a/PWGUD/Core/decayTreeLinkDef.h +++ b/PWGUD/Core/decayTreeLinkDef.h @@ -8,12 +8,10 @@ // In applying this license CERN does not waive the privileges and immunities // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef PWGUD_CORE_DECAYTREELINKDEF_H_ -#define PWGUD_CORE_DECAYTREELINKDEF_H_ + +// NOLINT(build/header_guard) #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; #pragma link C++ class decayTree + ; - -#endif // PWGUD_CORE_DECAYTREELINKDEF_H_ diff --git a/PWGUD/Core/udTablesPCH.cxx b/PWGUD/Core/udTablesPCH.cxx new file mode 100644 index 00000000000..8faa701be69 --- /dev/null +++ b/PWGUD/Core/udTablesPCH.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Carrier for the UDTablesPCH precompiled header; see this directory's +// CMakeLists.txt. It exists only to own the PCH, so it has no code of its own. diff --git a/PWGUD/DataModel/TauThreeProngEventTables.h b/PWGUD/DataModel/TauThreeProngEventTables.h index e01daca155a..95c8fff2a69 100644 --- a/PWGUD/DataModel/TauThreeProngEventTables.h +++ b/PWGUD/DataModel/TauThreeProngEventTables.h @@ -23,6 +23,7 @@ #include // derived tables for tautau->4 (=1+3) tracks +// derived tables for tautau->6 (=3+3) tracks namespace o2::aod { namespace tautree @@ -40,14 +41,17 @@ DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(FlagUPC, flagUPC, int8_t); DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, int); DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, double); -DECLARE_SOA_COLUMN(Trs, trs, int8_t); -DECLARE_SOA_COLUMN(Trofs, trofs, int8_t); -DECLARE_SOA_COLUMN(Hmpr, hmpr, int8_t); -DECLARE_SOA_COLUMN(Tfb, tfb, int8_t); -DECLARE_SOA_COLUMN(ItsRofb, itsRofb, int8_t); -DECLARE_SOA_COLUMN(Sbp, sbp, int8_t); -DECLARE_SOA_COLUMN(ZvtxFT0vsPv, zvtxFT0vsPv, int8_t); -DECLARE_SOA_COLUMN(VtxITSTPC, vtxITSTPC, int8_t); + +// DECLARE_SOA_COLUMN(Trs, trs, int8_t); +// DECLARE_SOA_COLUMN(Trofs, trofs, int8_t); +// DECLARE_SOA_COLUMN(Hmpr, hmpr, int8_t); +// DECLARE_SOA_COLUMN(Tfb, tfb, int8_t); +// DECLARE_SOA_COLUMN(ItsRofb, itsRofb, int8_t); +// DECLARE_SOA_COLUMN(Sbp, sbp, int8_t); +// DECLARE_SOA_COLUMN(ZvtxFT0vsPv, zvtxFT0vsPv, int8_t); +// DECLARE_SOA_COLUMN(VtxITSTPC, vtxITSTPC, int8_t); +DECLARE_SOA_COLUMN(BcSelBits, bcSelBits, uint8_t); + DECLARE_SOA_COLUMN(ZdcAenergy, zdcAenergy, float); DECLARE_SOA_COLUMN(ZdcCenergy, zdcCenergy, float); DECLARE_SOA_COLUMN(ZdcAtime, zdcAtime, float); @@ -60,48 +64,82 @@ DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); // DECLARE_SOA_COLUMN(TimeFT0A, timeFT0A, float); // DECLARE_SOA_COLUMN(TimeFT0C, timeFT0C, float); // DECLARE_SOA_COLUMN(TimeFV0A, timeFV0A, float); -// tracks -DECLARE_SOA_COLUMN(TrkPx, trkPx, float[6]); -DECLARE_SOA_COLUMN(TrkPy, trkPy, float[6]); -DECLARE_SOA_COLUMN(TrkPz, trkPz, float[6]); -DECLARE_SOA_COLUMN(TrkSign, trkSign, int[6]); -DECLARE_SOA_COLUMN(TrkDCAxy, trkDCAxy, float[6]); -DECLARE_SOA_COLUMN(TrkDCAz, trkDCAz, float[6]); -DECLARE_SOA_COLUMN(TrkTPCcr, trkTPCcr, int[6]); -DECLARE_SOA_COLUMN(TrkTPCfind, trkTPCfind, int[6]); -DECLARE_SOA_COLUMN(TrkTPCchi2, trkTPCchi2, float[6]); -DECLARE_SOA_COLUMN(TrkITSchi2, trkITSchi2, float[6]); -DECLARE_SOA_COLUMN(TrkITScl, trkITScl, int[6]); -// PID -DECLARE_SOA_COLUMN(TrkTPCsignal, trkTPCsignal, float[6]); -DECLARE_SOA_COLUMN(TrkTPCnSigmaEl, trkTPCnSigmaEl, float[6]); -DECLARE_SOA_COLUMN(TrkTPCnSigmaPi, trkTPCnSigmaPi, float[6]); -DECLARE_SOA_COLUMN(TrkTPCnSigmaKa, trkTPCnSigmaKa, float[6]); -DECLARE_SOA_COLUMN(TrkTPCnSigmaPr, trkTPCnSigmaPr, float[6]); -DECLARE_SOA_COLUMN(TrkTPCnSigmaMu, trkTPCnSigmaMu, float[6]); -DECLARE_SOA_COLUMN(TrkTOFbeta, trkTOFbeta, float[6]); -DECLARE_SOA_COLUMN(TrkTOFnSigmaEl, trkTOFnSigmaEl, float[6]); -DECLARE_SOA_COLUMN(TrkTOFnSigmaPi, trkTOFnSigmaPi, float[6]); -DECLARE_SOA_COLUMN(TrkTOFnSigmaKa, trkTOFnSigmaKa, float[6]); -DECLARE_SOA_COLUMN(TrkTOFnSigmaPr, trkTOFnSigmaPr, float[6]); -DECLARE_SOA_COLUMN(TrkTOFnSigmaMu, trkTOFnSigmaMu, float[6]); -DECLARE_SOA_COLUMN(TrkTOFchi2, trkTOFchi2, float[6]); +// tracks - 4 tracks +DECLARE_SOA_COLUMN(TrkPx, trkPx, float[4]); +DECLARE_SOA_COLUMN(TrkPy, trkPy, float[4]); +DECLARE_SOA_COLUMN(TrkPz, trkPz, float[4]); +DECLARE_SOA_COLUMN(TrkSign, trkSign, int[4]); +DECLARE_SOA_COLUMN(TrkDCAxy, trkDCAxy, float[4]); +DECLARE_SOA_COLUMN(TrkDCAz, trkDCAz, float[4]); +DECLARE_SOA_COLUMN(TrkTPCcr, trkTPCcr, int[4]); +DECLARE_SOA_COLUMN(TrkTPCfind, trkTPCfind, int[4]); +DECLARE_SOA_COLUMN(TrkTPCchi2, trkTPCchi2, float[4]); +DECLARE_SOA_COLUMN(TrkITSchi2, trkITSchi2, float[4]); +DECLARE_SOA_COLUMN(TrkITScl, trkITScl, int[4]); +// PID - 4 tracks +DECLARE_SOA_COLUMN(TrkTPCsignal, trkTPCsignal, float[4]); +DECLARE_SOA_COLUMN(TrkTPCnSigmaEl, trkTPCnSigmaEl, float[4]); +DECLARE_SOA_COLUMN(TrkTPCnSigmaPi, trkTPCnSigmaPi, float[4]); +DECLARE_SOA_COLUMN(TrkTPCnSigmaKa, trkTPCnSigmaKa, float[4]); +DECLARE_SOA_COLUMN(TrkTPCnSigmaPr, trkTPCnSigmaPr, float[4]); +DECLARE_SOA_COLUMN(TrkTPCnSigmaMu, trkTPCnSigmaMu, float[4]); +DECLARE_SOA_COLUMN(TrkTOFbeta, trkTOFbeta, float[4]); +DECLARE_SOA_COLUMN(TrkTOFnSigmaEl, trkTOFnSigmaEl, float[4]); +DECLARE_SOA_COLUMN(TrkTOFnSigmaPi, trkTOFnSigmaPi, float[4]); +DECLARE_SOA_COLUMN(TrkTOFnSigmaKa, trkTOFnSigmaKa, float[4]); +DECLARE_SOA_COLUMN(TrkTOFnSigmaPr, trkTOFnSigmaPr, float[4]); +DECLARE_SOA_COLUMN(TrkTOFnSigmaMu, trkTOFnSigmaMu, float[4]); +DECLARE_SOA_COLUMN(TrkTOFchi2, trkTOFchi2, float[4]); + +// tracks - 6 tracks +DECLARE_SOA_COLUMN(Trk6Px, trk6Px, float[6]); +DECLARE_SOA_COLUMN(Trk6Py, trk6Py, float[6]); +DECLARE_SOA_COLUMN(Trk6Pz, trk6Pz, float[6]); +DECLARE_SOA_COLUMN(Trk6Sign, trk6Sign, int[6]); +DECLARE_SOA_COLUMN(Trk6DCAxy, trk6DCAxy, float[6]); +DECLARE_SOA_COLUMN(Trk6DCAz, trk6DCAz, float[6]); +DECLARE_SOA_COLUMN(Trk6TPCcr, trk6TPCcr, int[6]); +DECLARE_SOA_COLUMN(Trk6TPCfind, trk6TPCfind, int[6]); +DECLARE_SOA_COLUMN(Trk6TPCchi2, trk6TPCchi2, float[6]); +DECLARE_SOA_COLUMN(Trk6ITSchi2, trk6ITSchi2, float[6]); +DECLARE_SOA_COLUMN(Trk6ITScl, trk6ITScl, int[6]); +// PID - 6 tracks +DECLARE_SOA_COLUMN(Trk6TPCsignal, trk6TPCsignal, float[6]); +DECLARE_SOA_COLUMN(Trk6TPCnSigmaEl, trk6TPCnSigmaEl, float[6]); +DECLARE_SOA_COLUMN(Trk6TPCnSigmaPi, trk6TPCnSigmaPi, float[6]); +DECLARE_SOA_COLUMN(Trk6TPCnSigmaKa, trk6TPCnSigmaKa, float[6]); +DECLARE_SOA_COLUMN(Trk6TPCnSigmaPr, trk6TPCnSigmaPr, float[6]); +DECLARE_SOA_COLUMN(Trk6TPCnSigmaMu, trk6TPCnSigmaMu, float[6]); +DECLARE_SOA_COLUMN(Trk6TOFbeta, trk6TOFbeta, float[6]); +DECLARE_SOA_COLUMN(Trk6TOFnSigmaEl, trk6TOFnSigmaEl, float[6]); +DECLARE_SOA_COLUMN(Trk6TOFnSigmaPi, trk6TOFnSigmaPi, float[6]); +DECLARE_SOA_COLUMN(Trk6TOFnSigmaKa, trk6TOFnSigmaKa, float[6]); +DECLARE_SOA_COLUMN(Trk6TOFnSigmaPr, trk6TOFnSigmaPr, float[6]); +DECLARE_SOA_COLUMN(Trk6TOFnSigmaMu, trk6TOFnSigmaMu, float[6]); +DECLARE_SOA_COLUMN(Trk6TOFchi2, trk6TOFchi2, float[6]); + // truth event DECLARE_SOA_COLUMN(TrueChannel, trueChannel, int); // DECLARE_SOA_COLUMN(TrueHasRecoColl, trueHasRecoColl, bool); // DECLARE_SOA_COLUMN(TruePosX, truePosX, float); // DECLARE_SOA_COLUMN(TruePosY, truePosY, float); DECLARE_SOA_COLUMN(TruePosZ, truePosZ, float); -// truth tau particles // index 0: tau+ // index 1: tau - -DECLARE_SOA_COLUMN(TrueTauPx, trueTauPx, float[2]); -DECLARE_SOA_COLUMN(TrueTauPy, trueTauPy, float[2]); -DECLARE_SOA_COLUMN(TrueTauPz, trueTauPz, float[2]); -// truth tau daughter particles -DECLARE_SOA_COLUMN(TrueDaugPx, trueDaugPx, float[6]); -DECLARE_SOA_COLUMN(TrueDaugPy, trueDaugPy, float[6]); -DECLARE_SOA_COLUMN(TrueDaugPz, trueDaugPz, float[6]); -DECLARE_SOA_COLUMN(TrueDaugPdgCode, trueDaugPdgCode, int[6]); -DECLARE_SOA_COLUMN(Problem, problem, int8_t); +// // truth tau particles // index 0: tau+ // index 1: tau - +// DECLARE_SOA_COLUMN(TrueTauPx, trueTauPx, float[2]); +// DECLARE_SOA_COLUMN(TrueTauPy, trueTauPy, float[2]); +// DECLARE_SOA_COLUMN(TrueTauPz, trueTauPz, float[2]); +// truth tau daughter particles - 4 particles +DECLARE_SOA_COLUMN(TrueDaugPx, trueDaugPx, float[4]); +DECLARE_SOA_COLUMN(TrueDaugPy, trueDaugPy, float[4]); +DECLARE_SOA_COLUMN(TrueDaugPz, trueDaugPz, float[4]); +DECLARE_SOA_COLUMN(TrueDaugPdgCode, trueDaugPdgCode, int[4]); +// truth tau daughter particles - 6 particles +DECLARE_SOA_COLUMN(True6DaugPx, true6DaugPx, float[6]); +DECLARE_SOA_COLUMN(True6DaugPy, true6DaugPy, float[6]); +DECLARE_SOA_COLUMN(True6DaugPz, true6DaugPz, float[6]); +DECLARE_SOA_COLUMN(True6DaugPdgCode, true6DaugPdgCode, int[6]); +DECLARE_SOA_COLUMN(Problem, problem, uint8_t); +DECLARE_SOA_COLUMN(IsRec, isRec, int8_t); } // namespace tautree DECLARE_SOA_TABLE(DataTauFourTracks, "AOD", "TAUFOURTRACK", @@ -112,8 +150,10 @@ DECLARE_SOA_TABLE(DataTauFourTracks, "AOD", "TAUFOURTRACK", tautree::PosZ, tautree::FlagUPC, tautree::OccupancyInTime, tautree::HadronicRate, // - tautree::Trs, tautree::Trofs, tautree::Hmpr, - tautree::Tfb, tautree::ItsRofb, tautree::Sbp, tautree::ZvtxFT0vsPv, tautree::VtxITSTPC, + tautree::BcSelBits, + // tautree::Trs, tautree::Trofs, tautree::Hmpr, + // tautree::Tfb, tautree::ItsRofb, tautree::Sbp, tautree::ZvtxFT0vsPv, tautree::VtxITSTPC, + // tautree::ZdcAenergy, tautree::ZdcCenergy, tautree::ZdcAtime, tautree::ZdcCtime, // tautree::Qtot, @@ -135,10 +175,14 @@ DECLARE_SOA_TABLE(TrueTauFourTracks, "AOD", "TRUETAU", // tautree::PosX, tautree::PosY, tautree::PosZ, tautree::FlagUPC, tautree::OccupancyInTime, tautree::HadronicRate, - tautree::Trs, tautree::Trofs, tautree::Hmpr, - tautree::Tfb, tautree::ItsRofb, tautree::Sbp, tautree::ZvtxFT0vsPv, tautree::VtxITSTPC, - tautree::ZdcAenergy, tautree::ZdcCenergy, - tautree::ZdcAtime, tautree::ZdcCtime, + // + tautree::BcSelBits, + // tautree::Trs, tautree::Trofs, tautree::Hmpr, + // tautree::Tfb, tautree::ItsRofb, tautree::Sbp, tautree::ZvtxFT0vsPv, tautree::VtxITSTPC, + // + // zdc information do not exist in MC + // tautree::ZdcAenergy, tautree::ZdcCenergy, + // tautree::ZdcAtime, tautree::ZdcCtime, // tautree::Qtot, tautree::TotalFT0AmplitudeA, tautree::TotalFT0AmplitudeC, tautree::TotalFV0AmplitudeA, // tautree::TimeFT0A, tautree::TimeFT0C, tautree::TimeFV0A, @@ -153,11 +197,89 @@ DECLARE_SOA_TABLE(TrueTauFourTracks, "AOD", "TRUETAU", tautree::TrueChannel, // tautree::TrueHasRecoColl, tautree::TruePosZ, - tautree::TrueTauPx, tautree::TrueTauPy, tautree::TrueTauPz, + // tautree::TrueTauPx, tautree::TrueTauPy, tautree::TrueTauPz, tautree::TrueDaugPx, tautree::TrueDaugPy, tautree::TrueDaugPz, tautree::TrueDaugPdgCode, tautree::Problem); +DECLARE_SOA_TABLE(GenTauFourTracks, "AOD", "GENTAU", + tautree::TrueChannel, + tautree::TruePosZ, + // tautree::TrueTauPx, tautree::TrueTauPy, tautree::TrueTauPz, + tautree::TrueDaugPx, tautree::TrueDaugPy, tautree::TrueDaugPz, + tautree::TrueDaugPdgCode, + tautree::Problem, + tautree::IsRec); + +DECLARE_SOA_TABLE(DataTauSixTracks, "AOD", "TAUSIXTRACK", + tautree::RunNumber, tautree::Bc, tautree::TotalTracks, tautree::NumContrib, + tautree::RctOk, + // tautree::GlobalNonPVtracks, + // tautree::PosX, tautree::PosY, + tautree::PosZ, + tautree::FlagUPC, tautree::OccupancyInTime, tautree::HadronicRate, + // + tautree::BcSelBits, + // tautree::Trs, tautree::Trofs, tautree::Hmpr, + // tautree::Tfb, tautree::ItsRofb, tautree::Sbp, tautree::ZvtxFT0vsPv, tautree::VtxITSTPC, + // + tautree::ZdcAenergy, tautree::ZdcCenergy, + tautree::ZdcAtime, tautree::ZdcCtime, + // tautree::Qtot, + tautree::TotalFT0AmplitudeA, tautree::TotalFT0AmplitudeC, tautree::TotalFV0AmplitudeA, + // tautree::TimeFT0A, tautree::TimeFT0C, tautree::TimeFV0A, + tautree::Trk6Px, tautree::Trk6Py, tautree::Trk6Pz, + tautree::Trk6Sign, + tautree::Trk6DCAxy, tautree::Trk6DCAz, + tautree::Trk6TPCcr, + tautree::Trk6TPCfind, tautree::Trk6TPCchi2, tautree::Trk6ITSchi2, tautree::Trk6ITScl, + tautree::Trk6TPCsignal, tautree::Trk6TPCnSigmaEl, tautree::Trk6TPCnSigmaPi, tautree::Trk6TPCnSigmaKa, tautree::Trk6TPCnSigmaPr, tautree::Trk6TPCnSigmaMu, + tautree::Trk6TOFbeta, tautree::Trk6TOFnSigmaEl, tautree::Trk6TOFnSigmaPi, tautree::Trk6TOFnSigmaKa, tautree::Trk6TOFnSigmaPr, tautree::Trk6TOFnSigmaMu, + tautree::Trk6TOFchi2); + +DECLARE_SOA_TABLE(TrueTauSixTracks, "AOD", "TRUETAUSIX", + tautree::RunNumber, tautree::Bc, tautree::TotalTracks, tautree::NumContrib, + tautree::RctOk, + // tautree::GlobalNonPVtracks, + // tautree::PosX, tautree::PosY, + tautree::PosZ, + tautree::FlagUPC, tautree::OccupancyInTime, tautree::HadronicRate, + // + tautree::BcSelBits, + // tautree::Trs, tautree::Trofs, tautree::Hmpr, + // tautree::Tfb, tautree::ItsRofb, tautree::Sbp, tautree::ZvtxFT0vsPv, tautree::VtxITSTPC, + // + // ZDC information do not exist in MC + // tautree::ZdcAenergy, tautree::ZdcCenergy, + // tautree::ZdcAtime, tautree::ZdcCtime, + // tautree::Qtot, + tautree::TotalFT0AmplitudeA, tautree::TotalFT0AmplitudeC, tautree::TotalFV0AmplitudeA, + // tautree::TimeFT0A, tautree::TimeFT0C, tautree::TimeFV0A, + tautree::Trk6Px, tautree::Trk6Py, tautree::Trk6Pz, + tautree::Trk6Sign, + tautree::Trk6DCAxy, tautree::Trk6DCAz, + tautree::Trk6TPCcr, + tautree::Trk6TPCfind, tautree::Trk6TPCchi2, tautree::Trk6ITSchi2, tautree::Trk6ITScl, + tautree::Trk6TPCsignal, tautree::Trk6TPCnSigmaEl, tautree::Trk6TPCnSigmaPi, tautree::Trk6TPCnSigmaKa, tautree::Trk6TPCnSigmaPr, tautree::Trk6TPCnSigmaMu, + tautree::Trk6TOFbeta, tautree::Trk6TOFnSigmaEl, tautree::Trk6TOFnSigmaPi, tautree::Trk6TOFnSigmaKa, tautree::Trk6TOFnSigmaPr, tautree::Trk6TOFnSigmaMu, + tautree::Trk6TOFchi2, + tautree::TrueChannel, + // tautree::TrueHasRecoColl, + tautree::TruePosZ, + // tautree::TrueTauPx, tautree::TrueTauPy, tautree::TrueTauPz, + tautree::True6DaugPx, tautree::True6DaugPy, tautree::True6DaugPz, + tautree::True6DaugPdgCode, + tautree::Problem); + +DECLARE_SOA_TABLE(GenTauSixTracks, "AOD", "GENTAUSIX", + tautree::TrueChannel, + tautree::TruePosZ, + // tautree::TrueTauPx, tautree::TrueTauPy, tautree::TrueTauPz, + tautree::True6DaugPx, tautree::True6DaugPy, tautree::True6DaugPz, + tautree::True6DaugPdgCode, + tautree::Problem, + tautree::IsRec); + } // namespace o2::aod #endif // PWGUD_DATAMODEL_TAUTHREEPRONGEVENTTABLES_H_ diff --git a/PWGUD/DataModel/TwoFwdTracksEventTables.h b/PWGUD/DataModel/TwoFwdTracksEventTables.h new file mode 100644 index 00000000000..93a0309d7a8 --- /dev/null +++ b/PWGUD/DataModel/TwoFwdTracksEventTables.h @@ -0,0 +1,164 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file TwoFwdTracksEventTables.h +/// \author Roman Lavička +/// \since 2026-10-02 +/// \brief A table to store information about forward UPC candidates preselected to have exactly two muon tracks. +/// \brief Forward counterpart of TwoTracksEventTables.h, filled from the output of upcCandProducerGlobalMuon +/// \brief (MCH/MID/MFT tracks, FV0A and ZDC instead of ITS/TPC/TOF tracks, FT0 and event-selection bits). +/// + +#ifndef PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ +#define PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ + +#include + +#include +#include + +namespace o2::aod +{ +namespace two_fwd_tracks_tree +{ +// event info +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(Bc, bc, uint64_t); +DECLARE_SOA_COLUMN(NumContrib, numContrib, int); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +// FV0A and ZDC info +DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); +DECLARE_SOA_COLUMN(AmplitudesV0A, amplitudesV0A, std::vector); +DECLARE_SOA_COLUMN(AmpRelBCsV0A, ampRelBCsV0A, std::vector); +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); +// tracks +DECLARE_SOA_COLUMN(TrkPx, trkPx, float[2]); +DECLARE_SOA_COLUMN(TrkPy, trkPy, float[2]); +DECLARE_SOA_COLUMN(TrkPz, trkPz, float[2]); +DECLARE_SOA_COLUMN(TrkSign, trkSign, int[2]); +DECLARE_SOA_COLUMN(TrkTime, trkTime, float[2]); +DECLARE_SOA_COLUMN(TrkTimeRes, trkTimeRes, float[2]); +DECLARE_SOA_COLUMN(TrkType, trkType, int[2]); +DECLARE_SOA_COLUMN(TrkNClusters, trkNClusters, int[2]); +DECLARE_SOA_COLUMN(TrkPDca, trkPDca, float[2]); +DECLARE_SOA_COLUMN(TrkRAtAbsorberEnd, trkRAtAbsorberEnd, float[2]); +DECLARE_SOA_COLUMN(TrkChi2, trkChi2, float[2]); +DECLARE_SOA_COLUMN(TrkChi2MatchMCHMID, trkChi2MatchMCHMID, float[2]); +DECLARE_SOA_COLUMN(TrkChi2MatchMCHMFT, trkChi2MatchMCHMFT, float[2]); +DECLARE_SOA_COLUMN(TrkMCHBitMap, trkMCHBitMap, int[2]); +DECLARE_SOA_COLUMN(TrkMIDBitMap, trkMIDBitMap, int[2]); +DECLARE_SOA_COLUMN(Trk1MIDBoards, trk1MIDBoards, uint32_t); +DECLARE_SOA_COLUMN(Trk2MIDBoards, trk2MIDBoards, uint32_t); +// truth event +DECLARE_SOA_COLUMN(TrueChannel, trueChannel, int); +DECLARE_SOA_COLUMN(TrueHasRecoColl, trueHasRecoColl, bool); +DECLARE_SOA_COLUMN(TruePosX, truePosX, float); +DECLARE_SOA_COLUMN(TruePosY, truePosY, float); +DECLARE_SOA_COLUMN(TruePosZ, truePosZ, float); +// truth particles +DECLARE_SOA_COLUMN(TrueMotherPx, trueMotherPx, float[2]); +DECLARE_SOA_COLUMN(TrueMotherPy, trueMotherPy, float[2]); +DECLARE_SOA_COLUMN(TrueMotherPz, trueMotherPz, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPx, trueDaugPx, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPy, trueDaugPy, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPz, trueDaugPz, float[2]); +DECLARE_SOA_COLUMN(TrueDaugPdgCode, trueDaugPdgCode, int[2]); +// additional info +DECLARE_SOA_COLUMN(ProblematicEvent, problematicEvent, bool); + +} // namespace two_fwd_tracks_tree + +DECLARE_SOA_TABLE(TwoFwdTracks, "AOD", "TWOFWDTRACK", + two_fwd_tracks_tree::RunNumber, + two_fwd_tracks_tree::Bc, + two_fwd_tracks_tree::NumContrib, + two_fwd_tracks_tree::PosX, + two_fwd_tracks_tree::PosY, + two_fwd_tracks_tree::PosZ, + two_fwd_tracks_tree::TotalFV0AmplitudeA, + two_fwd_tracks_tree::AmplitudesV0A, + two_fwd_tracks_tree::AmpRelBCsV0A, + two_fwd_tracks_tree::EnergyCommonZNA, + two_fwd_tracks_tree::EnergyCommonZNC, + two_fwd_tracks_tree::TimeZNA, + two_fwd_tracks_tree::TimeZNC, + two_fwd_tracks_tree::TrkPx, + two_fwd_tracks_tree::TrkPy, + two_fwd_tracks_tree::TrkPz, + two_fwd_tracks_tree::TrkSign, + two_fwd_tracks_tree::TrkTime, + two_fwd_tracks_tree::TrkTimeRes, + two_fwd_tracks_tree::TrkType, + two_fwd_tracks_tree::TrkNClusters, + two_fwd_tracks_tree::TrkPDca, + two_fwd_tracks_tree::TrkRAtAbsorberEnd, + two_fwd_tracks_tree::TrkChi2, + two_fwd_tracks_tree::TrkChi2MatchMCHMID, + two_fwd_tracks_tree::TrkChi2MatchMCHMFT, + two_fwd_tracks_tree::TrkMCHBitMap, + two_fwd_tracks_tree::TrkMIDBitMap, + two_fwd_tracks_tree::Trk1MIDBoards, + two_fwd_tracks_tree::Trk2MIDBoards); + +DECLARE_SOA_TABLE(TrueTwoFwdTracks, "AOD", "TRUETWOFWDTRACK", + two_fwd_tracks_tree::RunNumber, + two_fwd_tracks_tree::Bc, + two_fwd_tracks_tree::NumContrib, + two_fwd_tracks_tree::PosX, + two_fwd_tracks_tree::PosY, + two_fwd_tracks_tree::PosZ, + two_fwd_tracks_tree::TotalFV0AmplitudeA, + two_fwd_tracks_tree::AmplitudesV0A, + two_fwd_tracks_tree::AmpRelBCsV0A, + two_fwd_tracks_tree::EnergyCommonZNA, + two_fwd_tracks_tree::EnergyCommonZNC, + two_fwd_tracks_tree::TimeZNA, + two_fwd_tracks_tree::TimeZNC, + two_fwd_tracks_tree::TrkPx, + two_fwd_tracks_tree::TrkPy, + two_fwd_tracks_tree::TrkPz, + two_fwd_tracks_tree::TrkSign, + two_fwd_tracks_tree::TrkTime, + two_fwd_tracks_tree::TrkTimeRes, + two_fwd_tracks_tree::TrkType, + two_fwd_tracks_tree::TrkNClusters, + two_fwd_tracks_tree::TrkPDca, + two_fwd_tracks_tree::TrkRAtAbsorberEnd, + two_fwd_tracks_tree::TrkChi2, + two_fwd_tracks_tree::TrkChi2MatchMCHMID, + two_fwd_tracks_tree::TrkChi2MatchMCHMFT, + two_fwd_tracks_tree::TrkMCHBitMap, + two_fwd_tracks_tree::TrkMIDBitMap, + two_fwd_tracks_tree::Trk1MIDBoards, + two_fwd_tracks_tree::Trk2MIDBoards, + two_fwd_tracks_tree::TrueChannel, + two_fwd_tracks_tree::TrueHasRecoColl, + two_fwd_tracks_tree::TruePosX, + two_fwd_tracks_tree::TruePosY, + two_fwd_tracks_tree::TruePosZ, + two_fwd_tracks_tree::TrueMotherPx, + two_fwd_tracks_tree::TrueMotherPy, + two_fwd_tracks_tree::TrueMotherPz, + two_fwd_tracks_tree::TrueDaugPx, + two_fwd_tracks_tree::TrueDaugPy, + two_fwd_tracks_tree::TrueDaugPz, + two_fwd_tracks_tree::TrueDaugPdgCode, + two_fwd_tracks_tree::ProblematicEvent); + +} // namespace o2::aod + +#endif // PWGUD_DATAMODEL_TWOFWDTRACKSEVENTTABLES_H_ diff --git a/PWGUD/DataModel/UDTables.h b/PWGUD/DataModel/UDTables.h index afb7728a5ab..c47f5befaa1 100644 --- a/PWGUD/DataModel/UDTables.h +++ b/PWGUD/DataModel/UDTables.h @@ -410,28 +410,43 @@ DECLARE_SOA_TABLE(UDTracksLabels, "AOD", "UDTRACKLABEL", namespace udcollfitbits { +DECLARE_SOA_INDEX_COLUMN(UDCollision, udCollision); /// Link to the UDCOLLISION table + DECLARE_SOA_COLUMN(Thr1W0, thr1W0, uint64_t); /// 1 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr1W1, thr1W1, uint64_t); /// 1 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr1W2, thr1W2, uint64_t); /// 1 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr1W3, thr1W3, uint64_t); /// 1 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 DECLARE_SOA_COLUMN(Thr2W0, thr2W0, uint64_t); /// 2 MIP thresholds for FT0A ch 0 - ch 63 -DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 -DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 96 -DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 +DECLARE_SOA_COLUMN(Thr2W1, thr2W1, uint64_t); /// 2 MIP thresholds for FT0A ch 64 - ch 95 & FT0C ch 0 - ch 31 +DECLARE_SOA_COLUMN(Thr2W2, thr2W2, uint64_t); /// 2 MIP thresholds for FT0C ch 32 - ch 95 +DECLARE_SOA_COLUMN(Thr2W3, thr2W3, uint64_t); /// 2 MIP thresholds for FT0C ch 96 - 111 & FV0 0 - 47 } // namespace udcollfitbits -DECLARE_SOA_TABLE(UDCollisionFITBits, "AOD", "UDCOLLFITBITS", +DECLARE_SOA_TABLE(UDCollisionFITBits_000, "AOD", "UDCOLLFITBITS", o2::soa::Index<>, - udcollfitbits::Thr1W0, /// 1 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr1W1, /// 1 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr1W2, /// 1 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr1W3, /// 1 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 - udcollfitbits::Thr2W0, /// 2 MIP thresholds for FT0A ch 0 - ch 63 - udcollfitbits::Thr2W1, /// 2 MIP thresholds for FT0A ch 63 - ch 96 & FT0C ch 0 - ch 31 - udcollfitbits::Thr2W2, /// 2 MIP thresholds for FT0C ch 32 - ch 96 - udcollfitbits::Thr2W3 /// 2 MIP thresholds for FT0C ch 97 - 112 & FV0 0 - 47 -); + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +DECLARE_SOA_TABLE_VERSIONED(UDCollisionFITBits_001, "AOD", "UDCOLLFITBITS", 1, + o2::soa::Index<>, + udcollfitbits::UDCollisionId, + udcollfitbits::Thr1W0, + udcollfitbits::Thr1W1, + udcollfitbits::Thr1W2, + udcollfitbits::Thr1W3, + udcollfitbits::Thr2W0, + udcollfitbits::Thr2W1, + udcollfitbits::Thr2W2, + udcollfitbits::Thr2W3); + +using UDCollisionFITBits = UDCollisionFITBits_001; using UDTrack = UDTracks::iterator; using UDTrackCov = UDTracksCov::iterator; diff --git a/PWGUD/DataModel/UDTruegapsideTables.h b/PWGUD/DataModel/UDTruegapsideTables.h new file mode 100644 index 00000000000..e0d2ff0d639 --- /dev/null +++ b/PWGUD/DataModel/UDTruegapsideTables.h @@ -0,0 +1,32 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file UDTruegapsideTables.h +/// \author Zhiyong Lu (zhiyong.lu@cern.ch) +/// \since Apr/7/2026 +/// \brief header defines truegapside outputs + +#ifndef PWGUD_DATAMODEL_UDTRUEGAPSIDETABLES_H_ +#define PWGUD_DATAMODEL_UDTRUEGAPSIDETABLES_H_ + +#include +#include + +namespace o2::aod +{ +namespace upcservice +{ +DECLARE_SOA_COLUMN(Truegapside, truegapside, int); +} // namespace upcservice +DECLARE_SOA_TABLE(Truegapside, "AOD", "TRUEGAPSIDE", upcservice::Truegapside); +} // namespace o2::aod + +#endif // PWGUD_DATAMODEL_UDTRUEGAPSIDETABLES_H_ diff --git a/PWGUD/TableProducer/CMakeLists.txt b/PWGUD/TableProducer/CMakeLists.txt index c16870f0603..85923b12bc2 100644 --- a/PWGUD/TableProducer/CMakeLists.txt +++ b/PWGUD/TableProducer/CMakeLists.txt @@ -13,60 +13,95 @@ add_subdirectory(Converters) o2physics_add_dpl_workflow(dg-cand-producer SOURCES dgCandProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder O2Physics::AnalysisCCDB O2Physics::EventFilteringUtils COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(sgcand-producer +o2physics_add_dpl_workflow(sgcand-producer # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility) SOURCES SGCandProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::SGCutParHolder O2Physics::AnalysisCCDB O2Physics::FITCutParHolder COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dgbccand-producer +o2physics_add_dpl_workflow(dgbccand-producer # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility) SOURCES DGBCCandProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(upccand-producer +o2physics_add_dpl_workflow(upccand-producer # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility) SOURCES UPCCandidateProducer.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tau-event-table-producer SOURCES tauEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(two-tracks-event-table-producer SOURCES twoTracksEventTableProducer.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(two-fwd-tracks-event-table-producer + SOURCES twoFwdTracksEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(fwd-track-propagation SOURCES fwdTrackPropagation.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(udmcparticles-to-udtracks +o2physics_add_dpl_workflow(udmcparticles-to-udtracks # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility) SOURCES udMcParticles2udTracks.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(udmccollisions-to-udcollisions +o2physics_add_dpl_workflow(udmccollisions-to-udcollisions # o2-linter: disable=name/o2-workflow (Keep historical workflow name for backward compatibility) SOURCES udMcCollisions2udCollisions.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tau-three-prong-event-table-producer SOURCES tauThreeProngEventTableProducer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-cand-producer-muon SOURCES upcCandProducerMuon.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(true-gap-producer + SOURCES trueGapProducer.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(upc-cand-producer-global-muon SOURCES upcCandProducerGlobalMuon.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-cand-producer-semi-fwd + SOURCES upcCandProducerSemiFwd.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UPCCutparHolder O2::GlobalTracking COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-cand-producer-barrel + SOURCES upcCandProducerBarrel.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::CCDB O2::DataFormatsParameters O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(muon-efficiency-producer + SOURCES muonEfficiencyProducer.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::GlobalTracking + COMPONENT_NAME Analysis) diff --git a/PWGUD/TableProducer/Converters/CMakeLists.txt b/PWGUD/TableProducer/Converters/CMakeLists.txt index 7d5a6297ba1..8197eb878be 100644 --- a/PWGUD/TableProducer/Converters/CMakeLists.txt +++ b/PWGUD/TableProducer/Converters/CMakeLists.txt @@ -11,26 +11,36 @@ o2physics_add_dpl_workflow(fwd-tracks-extra-converter SOURCES UDFwdTracksExtraConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisions-converter SOURCES UDCollisionsConverter.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(collision-fit-bits-converter + SOURCES UDCollisionFITBitsConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter SOURCES UDCollisionSelExtrasConverter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter-v002 SOURCES UDCollisionSelExtrasV002Converter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(collisionselextras-converter-v003 SOURCES UDCollisionSelExtrasV003Converter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) diff --git a/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx new file mode 100644 index 00000000000..82be0196959 --- /dev/null +++ b/PWGUD/TableProducer/Converters/UDCollisionFITBitsConverter.cxx @@ -0,0 +1,61 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file UDCollisionFITBitsConverter.cxx +/// \brief Converts UDCollisionFITBits from version 000 to 001 +/// +/// The legacy table was written with exactly one FIT-bit row for every +/// UDCollision, in identical row order. Therefore row i of +/// UDCollisionFITBits_000 belongs to row i of UDCollisions. Because the +/// legacy table does not store this association explicitly, the converter +/// materialises it by writing the FIT-bit row's global index as +/// UDCollisionId. The eight packed FIT-bit words are copied unchanged. +/// +/// Executable: o2-analysis-ud-collision-fit-bits-converter +/// +/// \author Sandor Lokos, sandor.lokos@cern.ch + +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +struct UDCollisionFITBitsConverter { + Produces outputFITBits; + + void process(aod::UDCollisionFITBits_000 const& legacyFITBits) + { + outputFITBits.reserve(legacyFITBits.size()); + + for (const auto& bits : legacyFITBits) { + outputFITBits(bits.globalIndex(), + bits.thr1W0(), + bits.thr1W1(), + bits.thr1W2(), + bits.thr1W3(), + bits.thr2W0(), + bits.thr2W1(), + bits.thr2W2(), + bits.thr2W3()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/TableProducer/Converters/UDCollisionSelExtrasConverter.cxx b/PWGUD/TableProducer/Converters/UDCollisionSelExtrasConverter.cxx index 38ecb5abeb3..b6dc788f5a5 100644 --- a/PWGUD/TableProducer/Converters/UDCollisionSelExtrasConverter.cxx +++ b/PWGUD/TableProducer/Converters/UDCollisionSelExtrasConverter.cxx @@ -36,6 +36,7 @@ struct UDCollisionSelExtrasConverter { void process(o2::aod::UDCollisionSelExtras_000 const& collisions) { + udCollisionSelExtras_001.reserve(collisions.size()); for (const auto& collision : collisions) { diff --git a/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV002Converter.cxx b/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV002Converter.cxx index d2b4d6408b4..4755212a975 100644 --- a/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV002Converter.cxx +++ b/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV002Converter.cxx @@ -41,6 +41,7 @@ struct UDCollisionSelExtrasV002Converter { void processV000ToV002(o2::aod::UDCollisionSelExtras_000 const& collisions) { + udCollisionSelExtras_002.reserve(collisions.size()); for (const auto& collision : collisions) { @@ -65,6 +66,7 @@ struct UDCollisionSelExtrasV002Converter { void processV001ToV002(o2::aod::UDCollisionSelExtras_001 const& collisions) { + udCollisionSelExtras_002.reserve(collisions.size()); for (const auto& collision : collisions) { diff --git a/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV003Converter.cxx b/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV003Converter.cxx index e52b6fce784..dbd0f96b755 100644 --- a/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV003Converter.cxx +++ b/PWGUD/TableProducer/Converters/UDCollisionSelExtrasV003Converter.cxx @@ -41,6 +41,7 @@ struct UDCollisionSelExtrasV003Converter { void processV000ToV003(o2::aod::UDCollisionSelExtras_000 const& collisions) { + udCollisionSelExtras_003.reserve(collisions.size()); for (const auto& collision : collisions) { @@ -66,6 +67,7 @@ struct UDCollisionSelExtrasV003Converter { void processV001ToV003(o2::aod::UDCollisionSelExtras_001 const& collisions) { + udCollisionSelExtras_003.reserve(collisions.size()); for (const auto& collision : collisions) { @@ -91,6 +93,7 @@ struct UDCollisionSelExtrasV003Converter { void processV002ToV003(o2::aod::UDCollisionSelExtras_002 const& collisions) { + udCollisionSelExtras_003.reserve(collisions.size()); for (const auto& collision : collisions) { diff --git a/PWGUD/TableProducer/Converters/UDCollisionsConverter.cxx b/PWGUD/TableProducer/Converters/UDCollisionsConverter.cxx index 99de6088fb4..7d9a218c174 100644 --- a/PWGUD/TableProducer/Converters/UDCollisionsConverter.cxx +++ b/PWGUD/TableProducer/Converters/UDCollisionsConverter.cxx @@ -36,6 +36,7 @@ struct UDCollisionsConverter { void process(o2::aod::UDCollisions_000 const& collisions) { + udCollisions_001.reserve(collisions.size()); for (const auto& collision : collisions) { diff --git a/PWGUD/TableProducer/Converters/UDFwdTracksExtraConverter.cxx b/PWGUD/TableProducer/Converters/UDFwdTracksExtraConverter.cxx index 4806232f490..d28bda5d962 100644 --- a/PWGUD/TableProducer/Converters/UDFwdTracksExtraConverter.cxx +++ b/PWGUD/TableProducer/Converters/UDFwdTracksExtraConverter.cxx @@ -41,6 +41,7 @@ struct UDFwdTracksExtraConverter { { int trkType = 3; // trackType of MCH-MID tracks is 3 + udFwdTracksExtra_001.reserve(tracks.size()); for (const auto& track : tracks) { if (track.chi2MatchMCHMID() > 0) diff --git a/PWGUD/TableProducer/DGBCCandProducer.cxx b/PWGUD/TableProducer/DGBCCandProducer.cxx index eb272397af3..4719805c3e2 100644 --- a/PWGUD/TableProducer/DGBCCandProducer.cxx +++ b/PWGUD/TableProducer/DGBCCandProducer.cxx @@ -131,8 +131,8 @@ struct tracksWGTInBCs { // compute the BC closest in time auto firstCompatibleBC = ambTracksSlice.begin().bc().begin().globalBC(); LOGF(debug, "First compatible BC %d Track time %f", firstCompatibleBC, track.trackTime()); - closestBC = (uint64_t)(firstCompatibleBC + - (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (track.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); } else { // this track is not ambiguous, has hence a unique association to a collision/BC if (!track.has_collision()) { @@ -147,7 +147,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "Updating tracksInBCList with %d", closestBC); - tracksInBCList[closestBC].emplace_back((int32_t)track.globalIndex()); + tracksInBCList[closestBC].emplace_back(static_cast(track.globalIndex())); } } @@ -215,8 +215,8 @@ struct tracksWGTInBCs { // compute the BC closest in time auto firstCompatibleBC = ambFwdTracksSlice.begin().bc().begin().globalBC(); - closestBC = (uint64_t)(firstCompatibleBC + - (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); + closestBC = static_cast(firstCompatibleBC + + (fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS)); } else { // this track is not ambiguous, has hence a unique association to a collision/BC if (!fwdTrack.has_collision()) { @@ -231,7 +231,7 @@ struct tracksWGTInBCs { // update tracksInBCList LOGF(debug, "Updating fwdTracksInBCList with %d", closestBC); - fwdTracksInBCList[closestBC].emplace_back((int32_t)fwdTrack.globalIndex()); + fwdTracksInBCList[closestBC].emplace_back(static_cast(fwdTrack.globalIndex())); } } @@ -385,7 +385,7 @@ struct DGBCCandProducer { void init(InitContext& context) { - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); if (context.mOptions.get("processTinBCs")) { registry.add("table/candCase", "#candCase", {HistType::kTH1F, {{4, -0.5, 3.5}}}); @@ -468,11 +468,11 @@ struct DGBCCandProducer { auto fwdTracksArray = ftibcSlice.begin().fwdtrack_as(); isDG = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } else { - auto fwdTracksArray = FTCs{{fwdtracks.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; + auto fwdTracksArray = fwdtracks.emptySlice(); isDG = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } } else { - auto fwdTracksArray = FTCs{{fwdtracks.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; + auto fwdTracksArray = fwdtracks.emptySlice(); isDG = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } @@ -514,11 +514,11 @@ struct DGBCCandProducer { auto fwdTracksArray = ftibcPart.begin().fwdtrack_as(); isDG = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } else { - auto fwdTracksArray = FTCs{{fwdtracks.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; + auto fwdTracksArray = fwdtracks.emptySlice(); isDG = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } } else { - auto fwdTracksArray = FTCs{{fwdtracks.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; + auto fwdTracksArray = fwdtracks.emptySlice(); isDG = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } @@ -683,11 +683,11 @@ struct DGBCCandProducer { auto fwdTracksArray = ftibc.fwdtrack_as(); isDG2 = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } else { - auto fwdTracksArray = FTCs{{fwdtracks.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; + auto fwdTracksArray = fwdtracks.emptySlice(); isDG2 = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } } else { - auto fwdTracksArray = FTCs{{fwdtracks.asArrowTable()->Slice(0, 0)}, (uint64_t)0}; + auto fwdTracksArray = fwdtracks.emptySlice(); isDG2 = dgSelector.IsSelected(diffCuts, bcRange, tracksArray, fwdTracksArray); } diff --git a/PWGUD/TableProducer/SGCandProducer.cxx b/PWGUD/TableProducer/SGCandProducer.cxx index a45493d792e..3e407e3c0b5 100644 --- a/PWGUD/TableProducer/SGCandProducer.cxx +++ b/PWGUD/TableProducer/SGCandProducer.cxx @@ -224,7 +224,7 @@ struct SGCandProducer { // function to process reconstructed data template - void processReco(std::string histdir, TCol const& collision, BCs const& bcs, + void processReco(const std::string& histdir, TCol const& collision, BCs const& bcs, TCs const& tracks, FWs const& fwdtracks, aod::FV0As const& fv0as, aod::FT0s const& ft0s, aod::FDDs const& fdds) { @@ -351,7 +351,8 @@ struct SGCandProducer { fitCuts.thr2_FV0A()); } - outputFITBits(w1[0], w1[1], w1[2], w1[3], + outputFITBits(outputCollisions.lastIndex(), + w1[0], w1[1], w1[2], w1[3], w2[0], w2[1], w2[2], w2[3]); if (newbc.has_zdc()) { @@ -391,8 +392,8 @@ struct SGCandProducer { ccdb->setURL("http://alice-ccdb.cern.ch"); ccdb->setCaching(true); ccdb->setFatalWhenNull(false); - sameCuts = (SGCutParHolder)SGCuts; - fitCuts = (FITCutParHolder)FITCuts; + sameCuts = SGCutParHolder(SGCuts); + fitCuts = FITCutParHolder(FITCuts); // add histograms for the different process functions histPointers.clear(); diff --git a/PWGUD/TableProducer/UPCCandidateProducer.cxx b/PWGUD/TableProducer/UPCCandidateProducer.cxx index d58c98ac587..b5bcd1cd848 100644 --- a/PWGUD/TableProducer/UPCCandidateProducer.cxx +++ b/PWGUD/TableProducer/UPCCandidateProducer.cxx @@ -19,6 +19,7 @@ #include "PWGUD/DataModel/UDTables.h" #include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" #include "Common/DataModel/EventSelection.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" @@ -50,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +59,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; struct UpcCandProducer { bool fDoMC{false}; @@ -91,6 +94,9 @@ struct UpcCandProducer { std::vector fwdSelectors; std::vector barrelSelectors; + // RCT flag checker + RCTFlagsChecker myRCTChecker; + // skimmer flags // choose a source of signal MC events Configurable fSignalGenID{"signalGenID", 1, "Signal generator ID"}; @@ -125,9 +131,16 @@ struct UpcCandProducer { Configurable fRequireNoTimeFrameBorder{"requireNoTimeFrameBorder", true, "Require kNoTimeFrameBorder selection bit"}; Configurable fRequireNoITSROFrameBorder{"requireNoITSROFrameBorder", true, "Require kNoITSROFrameBorder selection bit"}; + Configurable rctLabel{"rctLabel", "muon", "RCT label to use, options: CBT, CBT_hadronPID, CBT_electronPID, CBT_calo, CBT_muon, CBT_muon_glo, muon = FV0 + MID + MCH, muon_glo = muon + MFT, none = do not use RCT flags"}; + Configurable checkZDC{"checkZDC", false, "Consider ZDC quality"}; + Configurable useLAasBad{"useLAasBad", false, "Consider Lim acc flag as Bad"}; + bool useRCTflags = true; + // QA histograms HistogramRegistry histRegistry{"HistRegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; + using CollisionsSels = o2::soa::Join; + using BCsWithBcSels = o2::soa::Join; using ForwardTracks = o2::soa::Join; @@ -144,7 +157,35 @@ struct UpcCandProducer { fwdSelectors.resize(upchelpers::kNFwdSels - 1, false); barrelSelectors.resize(upchelpers::kNBarrelSels - 1, false); - upcCuts = (UPCCutparHolder)inputCuts; + upcCuts = UPCCutparHolder(inputCuts); + + // initialize RCT flag checker + if (rctLabel.value != "none" && rctLabel.value != "muon" && rctLabel.value != "muon_glo") { + myRCTChecker.init(rctLabel.value, checkZDC.value, useLAasBad.value); + } else if (rctLabel.value == "none") { + useRCTflags = false; + myRCTChecker.init("CBT_muon"); + } else if (rctLabel.value == "muon") { + if (checkZDC.value && useLAasBad.value) { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad, kZDCBad, kMCHLimAccMCRepr, kMIDLimAccMCRepr}); + } else if (checkZDC.value) { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad, kZDCBad}); + } else if (useLAasBad.value) { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad, kMCHLimAccMCRepr, kMIDLimAccMCRepr}); + } else { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad}); + } + } else if (rctLabel.value == "muon_glo") { + if (checkZDC.value && useLAasBad.value) { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad, kMFTBad, kZDCBad, kMCHLimAccMCRepr, kMIDLimAccMCRepr, kMFTLimAccMCRepr}); + } else if (checkZDC.value) { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad, kMFTBad, kZDCBad}); + } else if (useLAasBad.value) { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad, kMFTBad, kMCHLimAccMCRepr, kMIDLimAccMCRepr, kMFTLimAccMCRepr}); + } else { + myRCTChecker.init({kFV0Bad, kMCHBad, kMIDBad, kMFTBad}); + } + } const AxisSpec axisTrgCounters{10, 0.5, 10.5, ""}; histRegistry.add("hCountersTrg", "", kTH1F, {axisTrgCounters}); @@ -179,6 +220,11 @@ struct UpcCandProducer { histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelTPCChi2 + 1, "TPCChi2"); histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelDCAXY + 1, "DCAXY"); histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelDCAZ + 1, "DCAZ"); + + histRegistry.add("RCTSelCounter", "RCTSelCounter", kTH1F, {{3, 0.5, 3.5}}); + histRegistry.get(HIST("RCTSelCounter"))->GetXaxis()->SetBinLabel(1, "Before RCT sel"); + histRegistry.get(HIST("RCTSelCounter"))->GetXaxis()->SetBinLabel(2, "After RCT sel"); + histRegistry.get(HIST("RCTSelCounter"))->GetXaxis()->SetBinLabel(3, "RCT rejected"); } template @@ -609,7 +655,7 @@ struct UpcCandProducer { } template - int64_t getAmbTrackId(TAmbTrack ambTrack) + int64_t getAmbTrackId(const TAmbTrack& ambTrack) { int64_t trkId = -1; if constexpr (tracksSwitch == 0) { // central barrel @@ -625,7 +671,7 @@ struct UpcCandProducer { template void collectAmbTrackBCs(std::unordered_map& ambTrIds, TBCs const& bcs, - TAmbTracks ambTracks) + const TAmbTracks& ambTracks) { for (const auto& ambTrk : ambTracks) { auto trkId = getAmbTrackId(ambTrk); @@ -682,6 +728,13 @@ struct UpcCandProducer { uint64_t trackBC = 0; if (trk.has_collision()) { const auto& col = trk.collision(); + auto bcRCT = col.bc_as(); + histRegistry.get(HIST("RCTSelCounter"))->Fill(1); + if (!myRCTChecker(bcRCT) && useRCTflags) { + histRegistry.get(HIST("RCTSelCounter"))->Fill(3); + continue; + } + histRegistry.get(HIST("RCTSelCounter"))->Fill(2); nContrib = col.numContrib(); trackBC = col.bc_as().globalBC(); hasTrackBC = true; @@ -725,6 +778,13 @@ struct UpcCandProducer { if (!trk.has_collision()) continue; const auto& col = trk.collision(); + auto bcRCT = col.bc_as(); + histRegistry.get(HIST("RCTSelCounter"))->Fill(1); + if (!myRCTChecker(bcRCT) && useRCTflags) { + histRegistry.get(HIST("RCTSelCounter"))->Fill(3); + continue; + } + histRegistry.get(HIST("RCTSelCounter"))->Fill(2); nContrib = col.numContrib(); trackBC = col.bc_as().globalBC(); hasTrackBC = true; @@ -765,6 +825,13 @@ struct UpcCandProducer { if (!trk.has_collision()) continue; const auto& col = trk.collision(); + auto bcRCT = col.bc_as(); + histRegistry.get(HIST("RCTSelCounter"))->Fill(1); + if (!myRCTChecker(bcRCT) && useRCTflags) { + histRegistry.get(HIST("RCTSelCounter"))->Fill(3); + continue; + } + histRegistry.get(HIST("RCTSelCounter"))->Fill(2); nContrib = col.numContrib(); trackBC = col.bc_as().globalBC(); const auto& bc = col.bc_as(); @@ -1923,7 +1990,7 @@ struct UpcCandProducer { fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, - (o2::aod::McTrackLabels*)nullptr, (o2::aod::McFwdTrackLabels*)nullptr); + nullptr, nullptr); } // create candidates for central region @@ -1940,7 +2007,7 @@ struct UpcCandProducer { createCandidatesCentral(barrelTracks, ambBarrelTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McTrackLabels*)nullptr); + nullptr); } // MC processors @@ -2009,7 +2076,7 @@ struct UpcCandProducer { createCandidatesFwd(fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr); + nullptr); } void processForwardMC(ForwardTracks const& fwdTracks, @@ -2049,7 +2116,7 @@ struct UpcCandProducer { createCandidatesFwdGlobal(fwdTracks, fwdTrkClusters, ambFwdTracks, bcs, collisions, ft0s, fdds, fv0as, zdcs, - (o2::aod::McFwdTrackLabels*)nullptr); + nullptr); } void processForwardGlobalMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/dgCandProducer.cxx b/PWGUD/TableProducer/dgCandProducer.cxx index 6c075229fb1..284f48f5e8e 100644 --- a/PWGUD/TableProducer/dgCandProducer.cxx +++ b/PWGUD/TableProducer/dgCandProducer.cxx @@ -190,7 +190,7 @@ struct DgCandProducer { outputTracksLabel(track.globalIndex()); } - void createHistograms(std::string histdir) + void createHistograms(const std::string& histdir) { const int nXbinsInStatH = 26; std::string labels[nXbinsInStatH] = { @@ -237,7 +237,7 @@ struct DgCandProducer { } template - void fillFIThistograms(TBC const& bc, std::string histdir) + void fillFIThistograms(TBC const& bc, const std::string& histdir) { LOGF(debug, ""); std::array triggers{{true, !udhelpers::cleanFIT(bc, diffCuts.maxFITtime(), diffCuts.FITAmpLimits()), @@ -295,7 +295,7 @@ struct DgCandProducer { } template - void processReco(std::string histdir, TCol const& collision, BCs const& bcs, + void processReco(const std::string& histdir, TCol const& collision, BCs const& bcs, TCs const& tracks, FWs const& fwdtracks, aod::FV0As const& fv0as, aod::FT0s const& ft0s, aod::FDDs const& fdds) { @@ -456,7 +456,7 @@ struct DgCandProducer { ccdb->setFatalWhenNull(false); // DGCuts - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions histPointers.clear(); diff --git a/PWGUD/TableProducer/fwdTrackPropagation.cxx b/PWGUD/TableProducer/fwdTrackPropagation.cxx index 655e78535a1..54ae7a43e8d 100644 --- a/PWGUD/TableProducer/fwdTrackPropagation.cxx +++ b/PWGUD/TableProducer/fwdTrackPropagation.cxx @@ -154,7 +154,7 @@ struct FwdTrackPropagation { // debug // LOGP(info, "track {}, before: {} {} {} {} {} {}", t.globalIndex(), t.x(), t.y(), t.z(), t.phi(), t.tgl(), t.signed1Pt()); // LOGP(info, "track {}, after: {} {} {} {} {} {}", t.globalIndex(), pft.getX(), pft.getY(), pft.getZ(), pft.getPhi(), pft.getTgl(), pft.getInvQPt()); - SMatrix55 cov = pft.getCovariances(); + const SMatrix55& cov = pft.getCovariances(); float sigX = std::sqrt(cov(0, 0)); float sigY = std::sqrt(cov(1, 1)); float sigPhi = std::sqrt(cov(2, 2)); diff --git a/PWGUD/TableProducer/muonEfficiencyProducer.cxx b/PWGUD/TableProducer/muonEfficiencyProducer.cxx new file mode 100644 index 00000000000..9a7f0987bde --- /dev/null +++ b/PWGUD/TableProducer/muonEfficiencyProducer.cxx @@ -0,0 +1,259 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file muonEfficiencyProducer.cxx +/// \brief Compact forward-muon skim for offline MCH and MCH-MID efficiency studies +/// \author Nazar Burmasov (JINR) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +namespace o2::aod::mcheffskim +{ +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(ReferenceBC, referenceBC, int64_t); +DECLARE_SOA_COLUMN(FT0AAmplitude, ft0aAmplitude, float); +DECLARE_SOA_COLUMN(FT0CAmplitude, ft0cAmplitude, float); +DECLARE_SOA_COLUMN(FV0AAmplitude, fv0aAmplitude, float); +DECLARE_SOA_COLUMN(CollisionId, collisionId, int32_t); // -1 for ambiguous tracks +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Eta, eta, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(Sign, sign, int8_t); +DECLARE_SOA_COLUMN(RAbs, rAbs, float); +DECLARE_SOA_COLUMN(PDca, pDca, float); +DECLARE_SOA_COLUMN(Chi2Ndf, chi2Ndf, float); // -1 if propagation failed +DECLARE_SOA_COLUMN(NClusters, nClusters, int8_t); +DECLARE_SOA_COLUMN(MchBitMap, mchBitMap, uint16_t); +DECLARE_SOA_COLUMN(MidBitMap, midBitMap, uint8_t); +DECLARE_SOA_COLUMN(MidBoards, midBoards, uint32_t); +DECLARE_SOA_COLUMN(Chi2MatchMCHMID, chi2MatchMCHMID, float); // -1 for MCH-only tracks +DECLARE_SOA_COLUMN(TrackTime, trackTime, float); // ns relative to referenceBC +DECLARE_SOA_COLUMN(TrackTimeRes, trackTimeRes, float); +} // namespace o2::aod::mcheffskim + +namespace o2::aod +{ +DECLARE_SOA_TABLE(MchEffBCs, "AOD", "MCHEFFBC", + o2::soa::Index<>, + mcheffskim::RunNumber, mcheffskim::ReferenceBC, + mcheffskim::FT0AAmplitude, mcheffskim::FT0CAmplitude, + mcheffskim::FV0AAmplitude); +} // namespace o2::aod + +namespace o2::aod::mcheffskim +{ +DECLARE_SOA_INDEX_COLUMN(MchEffBC, mchEffBC); // links a muon to the compact BC table +} // namespace o2::aod::mcheffskim + +namespace o2::aod +{ +DECLARE_SOA_TABLE(MchEffSkims, "AOD", "MCHEFFSKIM", + o2::soa::Index<>, mcheffskim::MchEffBCId, + mcheffskim::CollisionId, + mcheffskim::Pt, mcheffskim::Eta, mcheffskim::Phi, mcheffskim::Sign, + mcheffskim::RAbs, mcheffskim::PDca, mcheffskim::Chi2Ndf, + mcheffskim::NClusters, mcheffskim::MchBitMap, + mcheffskim::MidBitMap, mcheffskim::MidBoards, + mcheffskim::Chi2MatchMCHMID, + mcheffskim::TrackTime, mcheffskim::TrackTimeRes); +} // namespace o2::aod + +struct MuonEfficiencyProducer { + static constexpr float MinEta = -4.f; + static constexpr float MaxEta = -2.5f; + static constexpr float NoAmplitude = -1.f; + static constexpr float MaxFITTime = 30.f; + + using Tracks = soa::Join; + using Matrix55 = ROOT::Math::SMatrix>; + using Vector5 = ROOT::Math::SVector; + + Produces outputBCs; + Produces output; + Service ccdb{}; + globaltracking::MatchGlobalFwd matcher; + int cachedFieldRun = -1; + + Configurable minPt{"minPt", 0.4f, "Minimum track pT (Gev/c)"}; + Configurable minRAbs{"minRAbs", 17.6f, "Minimum absorber-end radius (cm)"}; + Configurable maxRAbs{"maxRAbs", 89.5f, "Maximum absorber-end radius (cm)"}; + Configurable maxPDca{"maxPDca", 400.f, "Maximum stored pDCA (GeV cm/c)"}; + Configurable maxChi2{"maxChi2", 100.f, "Maximum MCH chi2/ndf"}; + + void init(InitContext&) + { + ccdb->setLocalObjectValidityChecking(); + } + + void setField(int run) + { + if (cachedFieldRun == run) { + return; + } + const auto runInfo = parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + const auto ts = (runInfo.sor + runInfo.eor) / 2; + auto* grp = ccdb->getForTimeStamp("GLO/Config/GRPMagField", ts); + if (!grp) { + LOGF(fatal, "GRPMagField not found for run %d", run); + } + base::Propagator::initFieldFromGRP(grp); + if (!base::GeometryManager::isGeometryLoaded()) { + ccdb->get("GLO/Config/GeometryAligned"); + } + mch::TrackExtrap::setField(); + cachedFieldRun = run; + } + + struct Kinematics { + float pt, eta, phi; + }; + + template + bool propagateMCH(const Track& t, const std::array& vtx, const std::array& vtxCov, Kinematics& k) + { + const Vector5 params(t.x(), t.y(), t.phi(), t.tgl(), t.signed1Pt()); + const std::vector covElements{t.cXX(), t.cXY(), t.cYY(), t.cPhiX(), t.cPhiY(), t.cPhiPhi(), + t.cTglX(), t.cTglY(), t.cTglPhi(), t.cTglTgl(), + t.c1PtX(), t.c1PtY(), t.c1PtPhi(), t.c1PtTgl(), t.c1Pt21Pt2()}; + const Matrix55 cov(covElements.begin(), covElements.end()); + dataformats::GlobalFwdTrack source; + source.setParameters(params); + source.setZ(t.z()); + source.setCovariances(cov); + auto mchTrack = matcher.FwdtoMCH(source); + if (!mch::TrackExtrap::extrapToVertex(mchTrack, vtx[0], vtx[1], vtx[2], vtxCov[0], vtxCov[1])) { + return false; + } + const auto propagated = matcher.MCHtoFwd(mchTrack); + k = {.pt = static_cast(propagated.getPt()), .eta = static_cast(propagated.getEta()), .phi = static_cast(propagated.getPhi())}; + return true; + } + + void process(aod::BCs const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0as, + Tracks const& tracks, aod::Collisions const& collisions, + aod::AmbiguousFwdTracks const& ambiguous) + { + if (bcs.size() == 0) { + return; + } + const int run = bcs.begin().runNumber(); + + std::vector ft0aByBC(bcs.size(), NoAmplitude); + std::vector ft0cByBC(bcs.size(), NoAmplitude); + std::vector fv0aByBC(bcs.size(), NoAmplitude); + std::vector skimBCIds(bcs.size(), -1); + for (const auto& ft0 : ft0s) { + if (ft0.timeA() < MaxFITTime) { + ft0aByBC[ft0.bcId()] = ft0.sumAmpA(); + } + if (ft0.timeC() < MaxFITTime) { + ft0cByBC[ft0.bcId()] = ft0.sumAmpC(); + } + } + for (const auto& fv0 : fv0as) { + if (fv0.time() >= MaxFITTime) { + continue; + } + float amplitude = 0.f; + for (const float a : fv0.amplitude()) { + if (a > 0.f) { + amplitude += a; + } + } + fv0aByBC[fv0.bcId()] = amplitude; + } + + std::vector ambiguousBCIds(tracks.size(), -1); + for (const auto& entry : ambiguous) { + const auto& ids = entry.bcIds(); + if (ids.empty()) { + continue; + } + ambiguousBCIds[entry.fwdtrackId()] = *ids.begin(); + } + + for (const auto& t : tracks) { + const auto type = t.trackType(); + if (type != aod::fwdtrack::MuonStandaloneTrack && type != aod::fwdtrack::MCHStandaloneTrack) { + continue; + } + const float rAbs = t.rAtAbsorberEnd(); + if (rAbs < minRAbs || rAbs > maxRAbs || t.pDca() > maxPDca || t.chi2() > maxChi2) { + continue; + } + Kinematics k{.pt = t.pt(), .eta = t.eta(), .phi = t.phi()}; + std::array vtx{0.f, 0.f, 0.f}; + std::array vtxCov{0.f, 0.f}; + int32_t referenceBCId = ambiguousBCIds[t.globalIndex()]; + if (t.has_collision()) { + const auto col = collisions.iteratorAt(t.collisionId()); + vtx = {col.posX(), col.posY(), col.posZ()}; + vtxCov = {col.covXX(), col.covYY()}; + referenceBCId = col.bcId(); + } + setField(run); + if (!propagateMCH(t, vtx, vtxCov, k)) { + continue; + } + if (k.eta < MinEta || k.eta > MaxEta) { + continue; + } + if (k.pt < minPt) { + continue; + } + int32_t skimBCId = -1; + if (referenceBCId >= 0) { + skimBCId = skimBCIds[referenceBCId]; + if (skimBCId < 0) { + outputBCs(run, bcs.iteratorAt(referenceBCId).globalBC(), + ft0aByBC[referenceBCId], ft0cByBC[referenceBCId], fv0aByBC[referenceBCId]); + skimBCId = outputBCs.lastIndex(); + skimBCIds[referenceBCId] = skimBCId; + } + } + output(skimBCId, t.collisionId(), k.pt, k.eta, k.phi, static_cast(t.signed1Pt() > 0 ? 1 : -1), + rAbs, t.pDca(), t.chi2(), t.nClusters(), t.mchBitMap(), t.midBitMap(), t.midBoards(), + type == aod::fwdtrack::MuonStandaloneTrack ? t.chi2MatchMCHMID() : -1.f, t.trackTime(), t.trackTimeRes()); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) +{ + return WorkflowSpec{adaptAnalysisTask(context)}; +} diff --git a/PWGUD/TableProducer/tauEventTableProducer.cxx b/PWGUD/TableProducer/tauEventTableProducer.cxx index d91c5dffe8d..2538415540f 100644 --- a/PWGUD/TableProducer/tauEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauEventTableProducer.cxx @@ -213,7 +213,7 @@ struct TauEventTableProducer { std::vector>> cutMyRequiredITSHits{}; - void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, const std::set& requiredLayers) { // layer 0 corresponds to the the innermost ITS layer cutMyRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); diff --git a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx index c90139faac4..00a0c30e693 100644 --- a/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx +++ b/PWGUD/TableProducer/tauThreeProngEventTableProducer.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. // /// \file tauThreeProngEventTableProducer.cxx -/// \brief Produces derived table from UD tables for tau pair production (3 prong) +/// \brief Produces derived table from UD tables for tau pair production (with 3 prongs) /// /// \author Adam Matyja , IFJ PAN, Cracow /// \since 2025-09-06 @@ -67,7 +67,10 @@ enum MyRecoProblem { TOO_MANY_DAUGHTERS = 2, // more than 6 daughters from 2 taus TWO_TRACKS = 3, // more than 1 associated track to MC particle (tau daughter) NO_TRACK = 4, // No associated track to MC particle (tau daughter) - MIXED_TRACKS = 5 // 4 (or 6) tracks reconstructed but from 2 collisions + MIXED_TRACKS = 5, // 4 (or 6) tracks reconstructed but from 2 collisions + BAD_GAP = 6, // rapidity gap out of range 0-2 + GAP_NE_2 = 7, // rapidity gap != 2 + FIT_VETO = 8 // FIT not empty (with thresholds) }; enum MyParticle { @@ -79,8 +82,14 @@ enum MyParticle { }; struct TauThreeProngEventTableProducer { + // derived output for 4 track topology Produces trueTauFourTracks; Produces dataTauFourTracks; + Produces genTauFourTracks; + // derived output for 6 track topology + Produces trueTauSixTracks; + Produces dataTauSixTracks; + Produces genTauSixTracks; // Global varialbes // Service pdg; @@ -165,6 +174,7 @@ struct TauThreeProngEventTableProducer { // ConfigurableAxis minvAxis{"minvAxis", {100, 0.5, 5.0}, "M_{inv} (GeV/#it{c}^{2})"}; // ConfigurableAxis phiAxis{"phiAxis", {120, 0., 3.2}, "#phi"}; Configurable verbose{"verbose", {}, "Additional print outs"}; + Configurable> generatorIds{"generatorIds", std::vector{31}, "MC generatorIds to process"}; // cut selection configurables // Configurable zvertexcut{"zvertexcut", 10., "Z vertex cut"}; @@ -178,7 +188,7 @@ struct TauThreeProngEventTableProducer { // Configurable maxNsigmaPrVetocut{"maxNsigmaPrVetocut", 3., "max Nsigma for Proton veto cut"}; // Configurable maxNsigmaKaVetocut{"maxNsigmaKaVetocut", 3., "max Nsigma for Kaon veto cut"}; // Configurable minPtEtrkcut{"minPtEtrkcut", 0.25, "min Pt for El track cut"}; - Configurable minTrkPtcut{"minTrkPtcut", 0.1, "min Pt for each charged track cut, default=100MeV/c"}; + Configurable minTrkPtcut{"minTrkPtcut", 0.10, "min Pt for each charged track cut, default=100MeV/c"}; Configurable nPVtrackscut{"nPVtrackscut", 4, "number of PV contributors, default=4"}; Configurable mFITvetoFlag{"mFITvetoFlag", true, "To apply FIT veto"}; Configurable mFITvetoWindow{"mFITvetoWindow", 2, "FIT veto window"}; @@ -212,7 +222,7 @@ struct TauThreeProngEventTableProducer { ////using FullSGUDCollisions = soa::Join; ////using FullSGUDCollision = FullSGUDCollisions::iterator; using FullMCUDTracks = soa::Join; - using FullMCSGUDCollisions = soa::Join; + using FullMCSGUDCollisions = soa::Join; // using FullMCSGUDCollision = FullMCSGUDCollisions::iterator; // init @@ -238,7 +248,7 @@ struct TauThreeProngEventTableProducer { // mySetITShitsRule(cutGlobalTrack.cutITShitsRule); if (doprocessDoSkim) { - registrySkim.add("skim/efficiency", ";efficeincy;events", {HistType::kTH1F, {{10, 0., 10.}}}); + registrySkim.add("skim/efficiency", ";efficeincy;events", {HistType::kTH1D, {{10, 0., 10.}}}); registrySkim.get(HIST("skim/efficiency"))->GetXaxis()->SetBinLabel(1, "1: All"); registrySkim.get(HIST("skim/efficiency"))->GetXaxis()->SetBinLabel(2, "2: Gap=012"); registrySkim.get(HIST("skim/efficiency"))->GetXaxis()->SetBinLabel(3, "3: Gap=2"); @@ -248,39 +258,87 @@ struct TauThreeProngEventTableProducer { registrySkim.get(HIST("skim/efficiency"))->GetXaxis()->SetBinLabel(7, "7: N_{TOF}^{tr}>1"); registrySkim.get(HIST("skim/efficiency"))->GetXaxis()->SetBinLabel(8, "8: FIT veto"); - registrySkim.add("skim/gapSide", ";Gap;events", {HistType::kTH1F, {{10, -1., 9.}}}); - registrySkim.add("skim/trueGapSide", ";TrueGap;events", {HistType::kTH1F, {{10, -1., 9.}}}); + registrySkim.add("skim/gapSide", ";Gap;events", {HistType::kTH1D, {{10, -1., 9.}}}); + registrySkim.add("skim/trueGapSide", ";TrueGap;events", {HistType::kTH1D, {{10, -1., 9.}}}); registrySkim.add("skim/etaTrk", ";#eta^{trk};events", {HistType::kTH1F, {{100, -1.5, 1.5}}}); registrySkim.add("skim/ptTrk", ";p_{T}^{trk};events", {HistType::kTH1F, {{100, 0., 5.}}}); registrySkim.add("skim/phiTrk", ";#phi^{trk};events", {HistType::kTH1F, {{128, -3.2, 3.2}}}); registrySkim.add("skim/nTof", ";N_{TOFtrk};events", {HistType::kTH1F, {{10, -1., 9.}}}); } if (doprocessMonteCarlo) { - registrySkim.add("skim/efficiencyMC", ";efficeincy;events", {HistType::kTH1F, {{10, 0., 10.}}}); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "1: All"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "2: N^{#tau}=2"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "3: |y^{#tau}| <= 0.9"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "4: 4 or 6 trk"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "5: 4 trk"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "6: 6 trk"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "7: |#eta^{ch}|<0.9"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "8: 7+4 trk"); - registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "9: 7+6 trk"); - - registrySkim.add("skim/problemMC", ";problem;events", {HistType::kTH1F, {{10, 0., 10.}}}); - - registrySkim.add("skim/nTauMC", ";N_{#tau};events", {HistType::kTH1F, {{10, 0., 10.}}}); + registrySkim.add("skim/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{16, -1., 15.}}}); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "All"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "GenID"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "N^{#tau}=2"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|y^{#tau}| <= 0.9"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "|#eta^{ch}|<0.9"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "4 or 6 trk"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "e+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#mu+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "#pi+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "6#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec e+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #mu+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec #pi+3#pi"); + registrySkim.get(HIST("skim/efficiencyMC"))->GetXaxis()->SetBinLabel(15, "rec 6#pi"); + + registrySkim.add("skim/problemMC", ";problem;events", {HistType::kTH1D, {{10, 0., 10.}}}); + + registrySkim.add("skim/nTauMC", ";N_{#tau};events", {HistType::kTH1D, {{10, 0., 10.}}}); registrySkim.add("skim/tauRapidityMC", ";y_{#tau};events", {HistType::kTH1F, {{100, -2.5, 2.5}}}); registrySkim.add("skim/tauPhiMC", ";#phi^{#tau};events", {HistType::kTH1F, {{100, 0, 6.4}}}); registrySkim.add("skim/tauEtaMC", ";#eta^{#tau};events", {HistType::kTH1F, {{100, -2.5, 2.5}}}); registrySkim.add("skim/tauPtMC", ";p_{T}^{#tau};events", {HistType::kTH1F, {{100, 0, 5.}}}); registrySkim.add("skim/tauDeltaEtaMC", ";#Delta#eta^{#tau};events ", {HistType::kTH1F, {{100, -5., 5.}}}); registrySkim.add("skim/tauDeltaPhiMC", ";#Delta#phi^{#tau}(deg.);events", {HistType::kTH1F, {{100, 131., 181}}}); + registrySkim.add("skim/ditauInvMassMC", ";M_{inv}^{#tau#tau}(GeV/#it{c}^{2});events", {HistType::kTH1F, {{100, 3.4, 13.4}}}); registrySkim.add("skim/nChPartMC", ";N^{ch. part};events", {HistType::kTH1F, {{10, 0, 10.}}}); registrySkim.add("skim/daughterPhiMC", ";#phi^{daughter};events", {HistType::kTH1F, {{100, 0, 6.4}}}); registrySkim.add("skim/daughterEtaMC", ";#eta^{daughter};events", {HistType::kTH1F, {{100, -4., 4.}}}); registrySkim.add("skim/daughterPtMC", ";p_{T}^{daughter};events", {HistType::kTH1F, {{100, 0, 5.0}}}); } + if (doprocessGenerated) { + registrySkim.add("gen/efficiencyMC", ";efficiency;events", {HistType::kTH1D, {{16, -1., 15.}}}); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(1, "All"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(2, "GenID"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(3, "N^{#tau}=2"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(4, "|y^{#tau}| <=0.9"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(5, "|#eta^{ch}|<=0.9"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "4 or 6 trk"); + // registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(6, "6: 4 trk"); + // registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "7: 6 trk"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(7, "e+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(8, "#mu+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(9, "#pi+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(10, "6#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(11, "rec"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(12, "rec e+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(13, "rec #mu+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(14, "rec #pi+3#pi"); + registrySkim.get(HIST("gen/efficiencyMC"))->GetXaxis()->SetBinLabel(15, "rec 6#pi"); + + registrySkim.add("gen/problemMC", ";problem;events", {HistType::kTH1D, {{10, 0., 10.}}}); + registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(1, "1: NoProblem"); + registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(2, "2: Unused"); + registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(3, "3: N^{trk}>6"); + registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(4, "4: Unused"); + registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(5, "5: Unused"); + registrySkim.get(HIST("gen/problemMC"))->GetXaxis()->SetBinLabel(6, "6: Unused"); + + registrySkim.add("gen/nTauMC", ";N_{#tau};events", {HistType::kTH1D, {{10, 0., 10.}}}); + registrySkim.add("gen/tauRapidityMC", ";y_{#tau};events", {HistType::kTH1F, {{100, -2.5, 2.5}}}); + registrySkim.add("gen/tauPhiMC", ";#phi^{#tau};events", {HistType::kTH1F, {{100, 0, 6.4}}}); + registrySkim.add("gen/tauEtaMC", ";#eta^{#tau};events", {HistType::kTH1F, {{100, -2.5, 2.5}}}); + registrySkim.add("gen/tauPtMC", ";p_{T}^{#tau};events", {HistType::kTH1F, {{100, 0, 5.}}}); + registrySkim.add("gen/tauDeltaEtaMC", ";#Delta#eta^{#tau};events ", {HistType::kTH1F, {{100, -5., 5.}}}); + registrySkim.add("gen/tauDeltaPhiMC", ";#Delta#phi^{#tau}(deg.);events", {HistType::kTH1F, {{100, 131., 181}}}); + registrySkim.add("gen/ditauInvMassMC", ";M_{inv}^{#tau#tau}(GeV/#it{c}^{2});events", {HistType::kTH1F, {{100, 3.4, 13.4}}}); + registrySkim.add("gen/nChPartMC", ";N^{ch. part};events", {HistType::kTH1F, {{10, 0, 10.}}}); + registrySkim.add("gen/daughterPhiMC", ";#phi^{daughter};events", {HistType::kTH1F, {{100, 0, 6.4}}}); + registrySkim.add("gen/daughterEtaMC", ";#eta^{daughter};events", {HistType::kTH1F, {{100, -4., 4.}}}); + registrySkim.add("gen/daughterPtMC", ";p_{T}^{daughter};events", {HistType::kTH1F, {{100, 0, 5.0}}}); + } // histos.add("Truth/hTroubles", "Counter of unwanted issues;;Number of troubles (-)", HistType::kTH1D, {{15, 0.5, 15.5}}); @@ -303,7 +361,7 @@ struct TauThreeProngEventTableProducer { return angle; } - float calculateDeltaPhi(ROOT::Math::LorentzVector> p, ROOT::Math::LorentzVector> p1) + float calculateDeltaPhi(const ROOT::Math::LorentzVector>& p, const ROOT::Math::LorentzVector>& p1) { // float delta = p.Phi(); float delta = RecoDecay::constrainAngle(p.Phi()); @@ -453,7 +511,7 @@ struct TauThreeProngEventTableProducer { // check ITS clusters, how many -1,0,1,7 + 10 if 0,1,2 layers were fired // analysis track quality check template - int numberOfItsClustersCheck(T track) + int numberOfItsClustersCheck(const T& track) { if (!track.hasITS()) return -1; @@ -553,7 +611,7 @@ struct TauThreeProngEventTableProducer { std::vector>> cutMyRequiredITSHits{}; - void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, const std::set& requiredLayers) { // layer 0 corresponds to the the innermost ITS layer cutMyRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); @@ -585,10 +643,9 @@ struct TauThreeProngEventTableProducer { constexpr uint8_t KBit = 1; for (const auto& kITSrequirement : cutMyRequiredITSHits) { auto hits = std::count_if(kITSrequirement.second.begin(), kITSrequirement.second.end(), [&](auto&& requiredLayer) { return itsClusterMap & (KBit << requiredLayer); }); - if ((kITSrequirement.first == -1) && (hits > 0)) { - return false; // no hits were required in specified layers - } else if (hits < kITSrequirement.first) { - return false; // not enough hits found in specified layers + if (((kITSrequirement.first == -1) && (hits > 0)) || // no hits were required in specified layers + (hits < kITSrequirement.first)) { // not enough hits found in specified layers + return false; } } return true; @@ -696,38 +753,15 @@ struct TauThreeProngEventTableProducer { // skimming: only 4 tracks selection in data void processDoSkim(UDCollisionFull2 const& dgcand, UDTracksFull const& dgtracks, PVTracks const& PVContributors) { - registrySkim.get(HIST("skim/efficiency"))->Fill(0., 1.); - - int gapSide = dgcand.gapSide(); - registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); - // if (gapSide < 0 || gapSide > 2) - if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) - return; - registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); - - int truegapSide = sgSelector.trueGap(dgcand, cutFV0, cutFT0A, cutFT0C, cutZDC); - gapSide = truegapSide; - registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); - if (gapSide != mGapSide) - return; - registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); + const int fourTracks = 4; + const int sixTracks = 6; - // zdc information - float energyZNA = dgcand.energyCommonZNA(); - float energyZNC = dgcand.energyCommonZNC(); - if (energyZNA < 0) - energyZNA = -1.; - if (energyZNC < 0) - energyZNC = -1.; - float timeZNA = dgcand.timeZNA(); - float timeZNC = dgcand.timeZNC(); + registrySkim.get(HIST("skim/efficiency"))->Fill(0., 1.); + // set up PV track features int nTofTrk = 0; int nEtaIn15 = 0; int npT100 = 0; - // // int qtot = 0; - // int8_t qtot = 0; - // TLorentzVector p; ROOT::Math::LorentzVector> p; for (const auto& trk : PVContributors) { // qtot += trk.sign(); @@ -745,15 +779,32 @@ struct TauThreeProngEventTableProducer { nTofTrk++; } // end of loop over PV tracks - if (PVContributors.size() != nPVtrackscut) // 4 + // check number of PV tracks + if (PVContributors.size() != nPVtrackscut) // 4 or 6 + return; + registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); + + // check gap side + int gapSide = dgcand.gapSide(); + registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); + // if (gapSide < 0 || gapSide > 2) + if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) + return; + registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); + + // check true gap side + int truegapSide = sgSelector.trueGap(dgcand, cutFV0, cutFT0A, cutFT0C, cutZDC); + gapSide = truegapSide; + registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); + if (gapSide != mGapSide) return; registrySkim.get(HIST("skim/efficiency"))->Fill(3., 1.); - if (nEtaIn15 != nPVtrackscut) // 4 + if (nEtaIn15 != nPVtrackscut) // 4 or 6 return; registrySkim.get(HIST("skim/efficiency"))->Fill(4., 1.); - if (npT100 != nPVtrackscut) // 4 + if (npT100 != nPVtrackscut) // 4 or 6 return; registrySkim.get(HIST("skim/efficiency"))->Fill(5., 1.); @@ -786,10 +837,40 @@ struct TauThreeProngEventTableProducer { return; registrySkim.get(HIST("skim/efficiency"))->Fill(7., 1.); + // zdc information + float energyZNA = dgcand.energyCommonZNA(); + float energyZNC = dgcand.energyCommonZNC(); + if (energyZNA < 0) + energyZNA = -1.; + if (energyZNC < 0) + energyZNC = -1.; + float timeZNA = dgcand.timeZNA(); + float timeZNC = dgcand.timeZNC(); + // RCT variable int rct = 0; rct = isGoodRCTflag(dgcand); + // different events flags + int8_t bcSels[8] = {-99, -99, -99, -99, -99, -99, -99, -99}; + uint8_t bcSelBits = 0; + const int nBitsMax = 8; + bcSels[0] = dgcand.trs(); + bcSels[1] = dgcand.trofs(); + bcSels[2] = dgcand.hmpr(); + bcSels[3] = dgcand.tfb(); + bcSels[4] = dgcand.itsROFb(); + bcSels[5] = dgcand.sbp(); + bcSels[6] = dgcand.zVtxFT0vPV(); + bcSels[7] = dgcand.vtxITSTPC(); + + const int offset = 1; + bcSelBits = bcSels[0]; // initialization + for (int ibit = 1; ibit < nBitsMax; ibit++) { + bcSelBits = (bcSelBits << offset); // shift by 1 position towards left + bcSelBits += bcSels[ibit]; // add next bit to the pool + } + // // variables per track // @@ -862,29 +943,61 @@ struct TauThreeProngEventTableProducer { counterTmp++; } - dataTauFourTracks(dgcand.runNumber(), - dgcand.globalBC(), // is it necessary - dgtracks.size(), - dgcand.numContrib(), - rct, - // dgcand.posX(), dgcand.posY(), - dgcand.posZ(), - dgcand.flags(), - dgcand.occupancyInTime(), - dgcand.hadronicRate(), // is it necessary - dgcand.trs(), dgcand.trofs(), dgcand.hmpr(), // to test it - dgcand.tfb(), dgcand.itsROFb(), dgcand.sbp(), dgcand.zVtxFT0vPV(), dgcand.vtxITSTPC(), - energyZNA, energyZNC, - timeZNA, timeZNC, - // qtot, <<-------- comment out - dgcand.totalFT0AmplitudeA(), dgcand.totalFT0AmplitudeC(), dgcand.totalFV0AmplitudeA(), - // dgcand.timeFT0A(), dgcand.timeFT0C(), dgcand.timeFV0A(), - px, py, pz, sign, - dcaXY, dcaZ, - nclTPCcrossedRows, nclTPCfind, nclTPCchi2, trkITSchi2, trkITScl, - tmpDedx, nSigmaEl, nSigmaPi, nSigmaKa, nSigmaPr, nSigmaMu, - trkTofSignal, tmpTofNsigmaEl, tmpTofNsigmaPi, tmpTofNsigmaKa, tmpTofNsigmaPr, tmpTofNsigmaMu, - chi2TOF); + if (nPVtrackscut == fourTracks) { + dataTauFourTracks(dgcand.runNumber(), + dgcand.globalBC(), // is it necessary + dgtracks.size(), + dgcand.numContrib(), + rct, + // dgcand.posX(), dgcand.posY(), + dgcand.posZ(), + dgcand.flags(), + dgcand.occupancyInTime(), + dgcand.hadronicRate(), // is it necessary + // + bcSelBits, + // dgcand.trs(), dgcand.trofs(), dgcand.hmpr(), // to test it + // dgcand.tfb(), dgcand.itsROFb(), dgcand.sbp(), dgcand.zVtxFT0vPV(), dgcand.vtxITSTPC(), + // + energyZNA, energyZNC, + timeZNA, timeZNC, + // qtot, <<-------- comment out + dgcand.totalFT0AmplitudeA(), dgcand.totalFT0AmplitudeC(), dgcand.totalFV0AmplitudeA(), + // dgcand.timeFT0A(), dgcand.timeFT0C(), dgcand.timeFV0A(), + px, py, pz, sign, + dcaXY, dcaZ, + nclTPCcrossedRows, nclTPCfind, nclTPCchi2, trkITSchi2, trkITScl, + tmpDedx, nSigmaEl, nSigmaPi, nSigmaKa, nSigmaPr, nSigmaMu, + trkTofSignal, tmpTofNsigmaEl, tmpTofNsigmaPi, tmpTofNsigmaKa, tmpTofNsigmaPr, tmpTofNsigmaMu, + chi2TOF); + } else if (nPVtrackscut == sixTracks) { + dataTauSixTracks(dgcand.runNumber(), + dgcand.globalBC(), // is it necessary + dgtracks.size(), + dgcand.numContrib(), + rct, + // dgcand.posX(), dgcand.posY(), + dgcand.posZ(), + dgcand.flags(), + dgcand.occupancyInTime(), + dgcand.hadronicRate(), // is it necessary + // + bcSelBits, + // dgcand.trs(), dgcand.trofs(), dgcand.hmpr(), // to test it + // dgcand.tfb(), dgcand.itsROFb(), dgcand.sbp(), dgcand.zVtxFT0vPV(), dgcand.vtxITSTPC(), + // + energyZNA, energyZNC, + timeZNA, timeZNC, + // qtot, <<-------- comment out + dgcand.totalFT0AmplitudeA(), dgcand.totalFT0AmplitudeC(), dgcand.totalFV0AmplitudeA(), + // dgcand.timeFT0A(), dgcand.timeFT0C(), dgcand.timeFV0A(), + px, py, pz, sign, + dcaXY, dcaZ, + nclTPCcrossedRows, nclTPCfind, nclTPCchi2, trkITSchi2, trkITScl, + tmpDedx, nSigmaEl, nSigmaPi, nSigmaKa, nSigmaPr, nSigmaMu, + trkTofSignal, tmpTofNsigmaEl, tmpTofNsigmaPi, tmpTofNsigmaKa, tmpTofNsigmaPr, tmpTofNsigmaMu, + chi2TOF); + } } // end of skim process processDoSkim PROCESS_SWITCH(TauThreeProngEventTableProducer, processDoSkim, "Run over SG Producer tables to produce skimmed data", false); @@ -1027,8 +1140,14 @@ struct TauThreeProngEventTableProducer { if (verbose) LOGF(info, " UDMcCollision size %d, Collisions size %d, UDtracks %d, UDMcParticles %d", mcCollisions.size(), collisions.size(), tracks.size(), mcParticles.size()); + // temporary variables needed for reconstruction + int gapSide = -10; + int truegapSide = -10; + bool isFITveto = false; + // temporary variables - float tmpRapidity = -999.; + // float tmpRapidity = -999.; + float trueTauRapidity[2] = {-999., -999.}; float trueTauEta[2] = {-999., -999.}; float trueTauPhi[2] = {-999., -999.}; @@ -1042,9 +1161,27 @@ struct TauThreeProngEventTableProducer { // start loop over generated collisions for (const auto& mccoll : mcCollisions) { + // all collisions if (verbose) LOGF(info, "-- McColl GID %d", mccoll.globalIndex()); - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(0., 1.); // all MC collisions + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(-1., 1.); // all MC collisions + + // check Generator ID, for taus(1+3,3+3): GenID=31,32,33,34,35 + if (verbose) { + LOGF(info, "generatorId desired (0) %d, in MC %d", generatorIds->at(0), mccoll.generatorsID()); + } + + if (std::find(generatorIds->begin(), generatorIds->end(), mccoll.generatorsID()) != generatorIds->end()) { + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(0., 1.); // GenID + if (verbose) { + LOGF(info, "Event with good generatorID %d", mccoll.generatorsID()); + } + } else { + if (verbose) { + LOGF(info, "Event with bad generatorID %d", mccoll.generatorsID()); + } + continue; + } // set up default values per colission trueTauX[0] = -999.; @@ -1069,27 +1206,22 @@ struct TauThreeProngEventTableProducer { if (verbose) LOGF(info, "-- MC part pdg %d", particle.pdgCode()); if (std::abs(particle.pdgCode()) != kTauMinus) - continue; // 15 = tau_minus - // if (std::abs(particle.pdgCode()) != 15) continue; // 15 = tau_minus + continue; // 15 = tau_minus if (countMothers < desiredNMothers) { // < 2 // fill info for each tau trueTauX[countMothers] = particle.px(); trueTauY[countMothers] = particle.py(); trueTauZ[countMothers] = particle.pz(); - tmpRapidity = rapidity(particle.e(), trueTauZ[countMothers]); + trueTauRapidity[countMothers] = rapidity(particle.e(), trueTauZ[countMothers]); trueTauEta[countMothers] = RecoDecay::eta(std::array{particle.px(), particle.py(), particle.pz()}); trueTauPhi[countMothers] = RecoDecay::phi(particle.px(), particle.py()); if (verbose) - LOGF(info, "-- tau P(%f,%f,%f), e %f, y %f", particle.px(), particle.py(), particle.pz(), particle.e(), tmpRapidity); - registrySkim.get(HIST("skim/tauRapidityMC"))->Fill(tmpRapidity); - registrySkim.get(HIST("skim/tauPhiMC"))->Fill(trueTauPhi[countMothers]); - registrySkim.get(HIST("skim/tauEtaMC"))->Fill(trueTauEta[countMothers]); - registrySkim.get(HIST("skim/tauPtMC"))->Fill(RecoDecay::pt(particle.px(), particle.py())); - if (std::abs(tmpRapidity) > trkEtacut) { // 0.9 + LOGF(info, "-- tau P(%f,%f,%f), e %f, y %f", particle.px(), particle.py(), particle.pz(), particle.e(), trueTauRapidity[countMothers]); + if (std::abs(trueTauRapidity[countMothers]) > trkEtacut) { // 0.9 tauInRapidity = false; if (verbose) - LOGF(info, "--- tau y %f", tmpRapidity); + LOGF(info, "--- tau y %f", trueTauRapidity[countMothers]); } // rapidity check } // number of taus countMothers++; @@ -1104,6 +1236,15 @@ struct TauThreeProngEventTableProducer { registrySkim.get(HIST("skim/efficiencyMC"))->Fill(1., 1.); // exactly 2 taus + for (int iNmother = 0; iNmother < desiredNMothers; iNmother++) { + registrySkim.get(HIST("skim/tauRapidityMC"))->Fill(trueTauRapidity[iNmother]); + if (tauInRapidity) { + registrySkim.get(HIST("skim/tauPhiMC"))->Fill(trueTauPhi[iNmother]); + registrySkim.get(HIST("skim/tauEtaMC"))->Fill(trueTauEta[iNmother]); + registrySkim.get(HIST("skim/tauPtMC"))->Fill(RecoDecay::pt(trueTauX[iNmother], trueTauY[iNmother])); + } + } + if (!tauInRapidity) { // tau NOT in rapidity -> continue if (verbose) LOGF(info, "-- At least one mother particle (taus) out of rapidity (|y|<0.9). Jump to the next MC event."); @@ -1114,6 +1255,13 @@ struct TauThreeProngEventTableProducer { registrySkim.get(HIST("skim/tauDeltaEtaMC"))->Fill(trueTauEta[0] - trueTauEta[1]); registrySkim.get(HIST("skim/tauDeltaPhiMC"))->Fill(calculateDeltaPhi(trueTauPhi[0], trueTauPhi[1]) * 180. / o2::constants::math::PI); + // inv mass of two taus + ROOT::Math::LorentzVector> tau1; + ROOT::Math::LorentzVector> tau2; + tau1.SetXYZT(trueTauX[0], trueTauY[0], trueTauZ[0], RecoDecay::e(trueTauX[0], trueTauY[0], trueTauZ[0], MassTauPlus)); + tau2.SetXYZT(trueTauX[1], trueTauY[1], trueTauZ[1], RecoDecay::e(trueTauX[1], trueTauY[1], trueTauZ[1], MassTauPlus)); + registrySkim.get(HIST("skim/ditauInvMassMC"))->Fill((tau1 + tau2).M()); + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(2., 1.); // |y_tau| <= 0.9 countMothers = 0; int nChargedDaughtersTau[2] = {0, 0}; @@ -1154,40 +1302,36 @@ struct TauThreeProngEventTableProducer { break; } // end of loop over MC particles - registrySkim.get(HIST("skim/nChPartMC"))->Fill(nChargedDaughtersTau[0] + nChargedDaughtersTau[1]); // N charged particles from taus - // check number of charged particles in MC event - if ((nChargedDaughtersTau[0] + nChargedDaughtersTau[1] != fourTracks) && (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] != sixTracks)) { + if (!partFromTauInEta) { if (verbose) - LOGF(info, "-- Different from 4/6 charged particles (%d) from both taus. Jump to the next MC event.", nChargedDaughtersTau[0] + nChargedDaughtersTau[1]); + LOGF(info, "-- At least one daughter particle from taus out of pseudo-rapidity (|eta|<0.9). Jump to the next MC event."); continue; } - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(3., 1.); // 1+3 (3+3) topology - if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == fourTracks) { // 4 - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(4., 1.); - } else if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == sixTracks) { // 6 - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(5., 1.); - } + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(3., 1.); // charged particles from tau in |eta|<0.9 - if (!partFromTauInEta) { + registrySkim.get(HIST("skim/nChPartMC"))->Fill(nChargedDaughtersTau[0] + nChargedDaughtersTau[1]); // N charged particles from taus + // check number of charged particles in MC event + if ((nChargedDaughtersTau[0] + nChargedDaughtersTau[1] != fourTracks) && (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] != sixTracks)) { if (verbose) - LOGF(info, "-- At least one daughter particle from taus out of pseudo-rapidity (|eta|<0.9). Jump to the next MC event."); + LOGF(info, "-- Different from 4/6 charged particles (%d) from both taus. Jump to the next MC event.", nChargedDaughtersTau[0] + nChargedDaughtersTau[1]); continue; } - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(6., 1.); // particles from tau in |eta|<0.9 - if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == fourTracks) { // 4 - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(7., 1.); - } else if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == sixTracks) { // 6 - registrySkim.get(HIST("skim/efficiencyMC"))->Fill(8., 1.); - } + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(4., 1.); // 1+3 (3+3) topology = 4 or 6 tracks + // if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == fourTracks) { // 4 + // registrySkim.get(HIST("skim/efficiencyMC"))->Fill(5., 1.); + // } else if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == sixTracks) { // 6 + // registrySkim.get(HIST("skim/efficiencyMC"))->Fill(6., 1.); + // } - if (nChargedDaughtersTau[0] == oneProng) // 1 + if ((nChargedDaughtersTau[0] == oneProng) || // 1 + (nChargedDaughtersTau[0] == threeProng && nChargedDaughtersTau[1] == threeProng)) // 3 and 3 zerothTau = 0; else if (nChargedDaughtersTau[1] == oneProng) // 1 zerothTau = 1; - else if (nChargedDaughtersTau[0] == threeProng && nChargedDaughtersTau[1] == threeProng) // 3 and 3 - zerothTau = 0; + // else if (nChargedDaughtersTau[0] == threeProng && nChargedDaughtersTau[1] == threeProng) // 3 and 3 + // zerothTau = 0; // prepare local variables for output table int32_t runNumber = -999; // when no reconstructed collisions is associated to MCcoll it should remain = -999 @@ -1203,11 +1347,13 @@ struct TauThreeProngEventTableProducer { int occupancy = -999; double hadronicRate = -999.; int8_t bcSels[8] = {-99, -99, -99, -99, -99, -99, -99, -99}; - // zdc information - there i sno information in MC - float energyZNA = -999.; - float energyZNC = -999.; - float timeZNA = -999.; - float timeZNC = -999.; + uint8_t bcSelBits = 0; + const int nBitsMax = 8; + // zdc information - there is no information in MC + // float energyZNA = -999.; + // float energyZNC = -999.; + // float timeZNA = -999.; + // float timeZNC = -999.; float amplitudesFIT[3] = {-999., -999., -999.}; // FT0A, FT0C, FV0 // float timesFIT[3] = {-999., -999., -999.}; // FT0A, FT0C, FV0 @@ -1250,8 +1396,10 @@ struct TauThreeProngEventTableProducer { float trueDaugY[6] = {-998., -998., -998., -998., -998., -998.}; float trueDaugZ[6] = {-998., -998., -998., -998., -998., -998.}; int trueDaugPdgCode[6] = {-999, -999, -999, -999, -999, -999}; - // bool problem = false; - MyRecoProblem problem = NO_PROBLEM; + + int8_t problemBits[8] = {-99, -99, -99, -99, -99, -99, -99, -99}; + uint8_t problemBitsFlag = 0; + // MyRecoProblem problem = NO_PROBLEM; registrySkim.get(HIST("skim/problemMC"))->Fill(NO_PROBLEM); // tau tau event type @@ -1260,14 +1408,19 @@ struct TauThreeProngEventTableProducer { // 3 = pi+3pi // 4 = 3pi+3pi - if (nElec == oneProng && nPi == threeProng) // 1 + 3 + if (nElec == oneProng && nPi == threeProng) { // 1 + 3 trueChannel = 1; - else if (nMuon == oneProng && nPi == threeProng) // 1 + 3 + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(5., 1.); + } else if (nMuon == oneProng && nPi == threeProng) { // 1 + 3 trueChannel = 2; - else if (nPi == fourTracks) // 4 + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(6., 1.); + } else if (nPi == fourTracks) { // 4 trueChannel = 3; - else if (nPi == sixTracks) // 6 + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(7., 1.); + } else if (nPi == sixTracks) { // 6 trueChannel = 4; + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(8., 1.); + } // LOGF(info, "MC Coll global index %d", mccoll.globalIndex()); // LOGF(info, "2. UDMcCollision size %d, Collisions size %d, UDtracks %d, UDMcParticles %d", mcCollisions.size(), collisions.size(), tracks.size(), mcParticles.size()); @@ -1283,6 +1436,18 @@ struct TauThreeProngEventTableProducer { if (collFromMcColls.size() > 0) { // get the truth and reco-level info if (verbose) LOGF(info, "--- MC Collision has reconstructed collision!"); + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(9., 1.); + // reconstruction in a given channel + if (nElec == oneProng && nPi == threeProng) { // 1 + 3 + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(10., 1.); + } else if (nMuon == oneProng && nPi == threeProng) { // 1 + 3 + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(11., 1.); + } else if (nPi == fourTracks) { // 4 + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(12., 1.); + } else if (nPi == sixTracks) { // 6 + registrySkim.get(HIST("skim/efficiencyMC"))->Fill(13., 1.); + } + // trueHasRecoColl = true; // check there is exactly one reco-level collision associated to generated collision if (collFromMcColls.size() > 1) { @@ -1291,7 +1456,8 @@ struct TauThreeProngEventTableProducer { // LOGF(info, "Truth collision has more than 1 reco collision. Skipping this event."); // histos.get(HIST("Truth/hTroubles"))->Fill(1); // problem = true; - problem = MANY_RECO; + problemBits[MANY_RECO - 1] = true; + // problem = MANY_RECO; registrySkim.get(HIST("skim/problemMC"))->Fill(MANY_RECO); // continue; } else { @@ -1299,12 +1465,77 @@ struct TauThreeProngEventTableProducer { LOGF(info, "---- Truth collision has 1 reco collision. Record event."); } + // To fill bcFlags and problemFlags + const int offset = 1; + // grab reco-level collision for (const auto& collFromMcColl : collFromMcColls) { if (verbose) { LOGF(info, "---- ###### Collision %d #########", collFromMcColl.index()); LOGF(info, "---- Coll GID %d, Vertex (%f,%f,%f)", collFromMcColl.globalIndex(), collFromMcColl.posX(), collFromMcColl.posY(), collFromMcColl.posZ()); } + + // + // part neede to mimic reconstruction selection criteria + // + gapSide = collFromMcColl.gapSide(); + // registrySkim.get(HIST("skim/gapSide"))->Fill(gapSide); + if (gapSide < o2::aod::sgselector::SingleGapA || gapSide > o2::aod::sgselector::DoubleGap) { // if (gapSide < 0 || gapSide > 2) + problemBits[BAD_GAP - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(1., 1.); + + truegapSide = sgSelector.trueGap(collFromMcColl, cutFV0, cutFT0A, cutFT0C, cutZDC); + gapSide = truegapSide; + // registrySkim.get(HIST("skim/trueGapSide"))->Fill(gapSide); + if (gapSide != mGapSide) { + problemBits[GAP_NE_2 - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(2., 1.); + + // n PV contributors + // if (PVContributors.size() != nPVtrackscut) // 4 or 6 + // return; + // if (nEtaIn15 != nPVtrackscut) // 4 or 6 + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(4., 1.); + // + // if (npT100 != nPVtrackscut) // 4 or 6 + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(5., 1.); + // + // registrySkim.get(HIST("skim/nTof"))->Fill(nTofTrk); + // if (nTofTrk < nTofTrkMinCut) + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(6., 1.); + + // + // FIT informaton + // + auto bitMin = 16 - mFITvetoWindow; // default is +- 2 bc (2 bit) + auto bitMax = 16 + mFITvetoWindow; + isFITveto = false; + // check FIT information + for (auto bit = bitMin; bit <= bitMax; bit++) { + if (TESTBIT(collFromMcColl.bbFT0Apf(), bit)) + isFITveto = true; + if (TESTBIT(collFromMcColl.bbFT0Cpf(), bit)) + isFITveto = true; + if (useFV0ForVeto && TESTBIT(collFromMcColl.bbFV0Apf(), bit)) + isFITveto = true; + if (useFDDAForVeto && TESTBIT(collFromMcColl.bbFDDApf(), bit)) + isFITveto = true; + if (useFDDCForVeto && TESTBIT(collFromMcColl.bbFDDCpf(), bit)) + isFITveto = true; + } // end of loop over FIT bits + if (mFITvetoFlag && isFITveto) { + problemBits[FIT_VETO - 1] = true; + } + // return; + // registrySkim.get(HIST("skim/efficiency"))->Fill(7., 1.); + // auto const& collFromMcColl = collFromMcColls.iteratorAt(0); // grab tracks from the reco-level collision to get info to match measured data tables (processDataSG function) auto const& trksFromColl = tracks.sliceBy(trackPerCollision, collFromMcColl.globalIndex()); @@ -1343,6 +1574,14 @@ struct TauThreeProngEventTableProducer { bcSels[5] = collFromMcColl.sbp(); bcSels[6] = collFromMcColl.zVtxFT0vPV(); bcSels[7] = collFromMcColl.vtxITSTPC(); + + // const int offset = 1; + bcSelBits = bcSels[0]; // initialization + for (int ibit = 1; ibit < nBitsMax; ibit++) { + bcSelBits = (bcSelBits << offset); // shift by 1 position towards left + bcSelBits += bcSels[ibit]; // add next bit to the pool + } + // energyZNA = collFromMcColl.energyCommonZNA(); // energyZNC = collFromMcColl.energyCommonZNC(); // if (energyZNA < 0) @@ -1394,7 +1633,9 @@ struct TauThreeProngEventTableProducer { LOGF(info, "------ Truth collision has more than 6 charged daughters from 2 taus. Breaking the daughter loop."); // histos.get(HIST("Truth/hTroubles"))->Fill(3); // problem = true; - problem = TOO_MANY_DAUGHTERS; + + problemBits[TOO_MANY_DAUGHTERS - 1] = true; + // problem = TOO_MANY_DAUGHTERS; registrySkim.get(HIST("skim/problemMC"))->Fill(TOO_MANY_DAUGHTERS); break; @@ -1440,7 +1681,9 @@ struct TauThreeProngEventTableProducer { if (counter > 1) { if (verbose) LOGF(info, "------ Daughter has more than 1 associated track. Ntracks = %d. Skipping this daughter.", counter); - problem = TWO_TRACKS; + + problemBits[TWO_TRACKS - 1] = true; + // problem = TWO_TRACKS; registrySkim.get(HIST("skim/problemMC"))->Fill(TWO_TRACKS); continue; } @@ -1448,7 +1691,8 @@ struct TauThreeProngEventTableProducer { // what if no track is associated to mc particle??? if (verbose) LOGF(info, "----- Daughter has no associated track. Ntracks = %d. Skipping this daughter.", tracksFromDaughter.size()); - problem = NO_TRACK; + problemBits[NO_TRACK - 1] = true; + // problem = NO_TRACK; registrySkim.get(HIST("skim/problemMC"))->Fill(NO_TRACK); continue; } @@ -1496,113 +1740,427 @@ struct TauThreeProngEventTableProducer { } // end of loop over daughters of taus } // end of loop over MC particles } // end of loop over collisions associated to MC collision + + // set up problem bits + problemBitsFlag = problemBits[0]; // initialization + for (int ibit = 1; ibit < nBitsMax; ibit++) { + problemBitsFlag = (problemBitsFlag << offset); // shift by 1 position towards left + problemBitsFlag += problemBits[ibit]; // add next bit to the pool + } + + // decide the channel and set the variable. + trueChannel = trueChannel + countPi0 * 10 + zerothTau * 100; + + // LOGF(info, "Should be written!"); + + if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == fourTracks) { // 4 + trueTauFourTracks(runNumber, + bc, // is it necessary + totalTracks, + nPVcontrib, + rct, + // dgcand.posX(), dgcand.posY(), + zVertex, + recoMode, + occupancy, + hadronicRate, // is it necessary + bcSelBits, + // bcSels[0], bcSels[1], bcSels[2], // to test it + // bcSels[3], bcSels[4], bcSels[5], bcSels[6], bcSels[7], + // energyZNA, energyZNC, + // timeZNA, timeZNC, + // qtot, <<-------- comment out + amplitudesFIT[0], amplitudesFIT[1], amplitudesFIT[2], + // timesFIT[0], timesFIT[1], timesFIT[2], + px, py, pz, sign, + dcaXY, dcaZ, + nclTPCcrossedRows, nclTPCfind, nclTPCchi2, trkITSchi2, trkITScl, + tpcSignal, tpcEl, tpcPi, tpcKa, tpcPr, tpcMu, + tofSignal, tofEl, tofPi, tofKa, tofPr, tofMu, + chi2TOF, + // + trueChannel, + // trueHasRecoColl, + mccoll.posZ(), + // trueTauX, trueTauY, trueTauZ, + trueDaugX, trueDaugY, trueDaugZ, + trueDaugPdgCode, problemBitsFlag); + } else if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == sixTracks) { // 6 + trueTauSixTracks(runNumber, + bc, // is it necessary + totalTracks, + nPVcontrib, + rct, + // dgcand.posX(), dgcand.posY(), + zVertex, + recoMode, + occupancy, + hadronicRate, // is it necessary + bcSelBits, + // bcSels[0], bcSels[1], bcSels[2], // to test it + // bcSels[3], bcSels[4], bcSels[5], bcSels[6], bcSels[7], + // energyZNA, energyZNC, + // timeZNA, timeZNC, + // qtot, <<-------- comment out + amplitudesFIT[0], amplitudesFIT[1], amplitudesFIT[2], + // timesFIT[0], timesFIT[1], timesFIT[2], + px, py, pz, sign, + dcaXY, dcaZ, + nclTPCcrossedRows, nclTPCfind, nclTPCchi2, trkITSchi2, trkITScl, + tpcSignal, tpcEl, tpcPi, tpcKa, tpcPr, tpcMu, + tofSignal, tofEl, tofPi, tofKa, tofPr, tofMu, + chi2TOF, + // + trueChannel, + // trueHasRecoColl, + mccoll.posZ(), + // trueTauX, trueTauY, trueTauZ, + trueDaugX, trueDaugY, trueDaugZ, + trueDaugPdgCode, problemBitsFlag); + } // end of saving the output + } else { // get only the truth information. The reco-level info is left on default if (verbose) LOGF(info, "MC Collision has NO reconstructed collision!"); - // get particles associated to generated collision - auto const& partsFromMcColl = mcParticles.sliceBy(partPerMcCollision, mccoll.globalIndex()); + } // collisions + + } // mccollisions + } // end of processMonteCarlo + PROCESS_SWITCH(TauThreeProngEventTableProducer, processMonteCarlo, "Iterate UD tables with simulated data created by SG-Candidate-Producer.", false); + + // only MC information and what events are reconstructed + void processGenerated(aod::UDMcCollisions const& mcCollisions, + aod::UDMcParticles const& mcParticles, + FullMCSGUDCollisions const& collisions) + { + // registrySkim.get(HIST("skim/efficiencyMC"))->Fill(0., 1.); + + const int fourTracks = 4; + const int sixTracks = 6; + const int oneProng = 1; + const int threeProng = 3; + // const float epsilon = 0.000001; + + if (verbose) + LOGF(info, " UDMcCollision size %d, Collisions size %d, UDMcParticles %d", mcCollisions.size(), collisions.size(), mcParticles.size()); + + // temporary variables + float trueTauRapidity[2] = {-999., -999.}; + float trueTauEta[2] = {-999., -999.}; + float trueTauPhi[2] = {-999., -999.}; + + // init variables for tree + float trueTauX[2] = {-999., -999.}; + float trueTauY[2] = {-999., -999.}; + float trueTauZ[2] = {-999., -999.}; + + bool tauInRapidity = true; + bool partFromTauInEta = true; + + // start loop over generated collisions + for (const auto& mccoll : mcCollisions) { + // all collisions + if (verbose) + LOGF(info, "-- McColl GID %d", mccoll.globalIndex()); + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(-1., 1.); // all MC collisions + + // check Generator ID, for taus(1+3,3+3): GenID=31,32,33,34,35 + if (verbose) { + LOGF(info, "generatorId desired (0) %d, in MC %d", generatorIds->at(0), mccoll.generatorsID()); + } + + if (std::find(generatorIds->begin(), generatorIds->end(), mccoll.generatorsID()) != generatorIds->end()) { + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(0., 1.); // GenID + if (verbose) { + LOGF(info, "Event with good generatorID %d", mccoll.generatorsID()); + } + } else { + if (verbose) { + LOGF(info, "Event with bad generatorID %d", mccoll.generatorsID()); + } + continue; + } + + // set up default values per colission + trueTauX[0] = -999.; + trueTauY[0] = -999.; + trueTauZ[0] = -999.; + trueTauX[1] = -999.; + trueTauY[1] = -999.; + trueTauZ[1] = -999.; + + tauInRapidity = true; + partFromTauInEta = true; + + // get particles associated to generated collision + // auto const& tmpPartsFromMcColl = mcParticles.sliceBy(partPerMcCollision, (int64_t)mccoll.globalIndex()); + auto const& tmpPartsFromMcColl = mcParticles.sliceBy(partPerMcCollision, mccoll.globalIndex()); + + if (verbose) + LOGF(info, "- part from MC coll %d", tmpPartsFromMcColl.size()); + int countMothers = 0; + const int desiredNMothers = 2; + for (const auto& particle : tmpPartsFromMcColl) { if (verbose) - LOGF(info, "NO Coll; partsFromMcColl in MC %d", partsFromMcColl.size()); - // int countMothers = 0; - int countDaughters = 0; - countPi0 = 0; - for (const auto& particle : partsFromMcColl) { - if (verbose) - LOGF(info, "No Coll; part Gid %d, Id %d, pdg %d, hasM %d, hasD %d", particle.globalIndex(), particle.index(), particle.pdgCode(), particle.has_mothers(), particle.has_daughters()); - // select only tauons with checking if particle has no mother - // in UPC MC taus have mothers - // if (particle.has_mothers()) - if (std::abs(particle.pdgCode()) != kTauMinus) // 15 - continue; - // countMothers++; - // check the generated collision does not have more than 2 tauons - // if (countMothers > 2) { - // if (verbose) - // LOGF(info,"Truth collision has more than 2 no mother particles. Breaking the particle loop."); - // // histos.get(HIST("Truth/hTroubles"))->Fill(12); - // // problem = true; - // break; - // } - // // fill info for each tau - // trueTauX[countMothers - 1] = particle.px(); - // trueTauY[countMothers - 1] = particle.py(); - // trueTauZ[countMothers - 1] = particle.pz(); + LOGF(info, "-- MC part pdg %d", particle.pdgCode()); + if (std::abs(particle.pdgCode()) != kTauMinus) + continue; // 15 = tau_minus + if (countMothers < desiredNMothers) { // < 2 + // fill info for each tau + trueTauX[countMothers] = particle.px(); + trueTauY[countMothers] = particle.py(); + trueTauZ[countMothers] = particle.pz(); + trueTauRapidity[countMothers] = rapidity(particle.e(), trueTauZ[countMothers]); + trueTauEta[countMothers] = RecoDecay::eta(std::array{particle.px(), particle.py(), particle.pz()}); + trueTauPhi[countMothers] = RecoDecay::phi(particle.px(), particle.py()); - // get daughters of the tau - const auto& daughters = particle.daughters_as(); if (verbose) - LOGF(info, "NO coll; N_daughters %d", daughters.size()); - // int countDaughters = 0; - for (const auto& daughter : daughters) { + LOGF(info, "-- tau P(%f,%f,%f), e %f, y %f", particle.px(), particle.py(), particle.pz(), particle.e(), trueTauRapidity[countMothers]); + if (std::abs(trueTauRapidity[countMothers]) > trkEtacut) { // 0.9 + tauInRapidity = false; if (verbose) - LOGF(info, "NO Coll; daug id %d, pdg %d", daughter.globalIndex(), daughter.pdgCode()); + LOGF(info, "--- tau y %f", trueTauRapidity[countMothers]); + } // rapidity check + } // number of taus + countMothers++; + } // end of loop over MC paricles + // LOGF(info, "1b. countMothers %d", countMothers); + registrySkim.get(HIST("gen/nTauMC"))->Fill(countMothers); + if (countMothers != desiredNMothers) { // 2 + if (verbose) + LOGF(info, "-- Truth collision has number of mother particles (taus) %d different than 2. Jump to the next MC event.", countMothers); + continue; + } - if (daughter.pdgCode() == kPi0) - countPi0++; + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(1., 1.); // exactly 2 taus - // select only the charged particle (= no pi0 or neutrino) - if (enumMyParticle(daughter.pdgCode()) == -1) - continue; - countDaughters++; + for (int iNmother = 0; iNmother < desiredNMothers; iNmother++) { + registrySkim.get(HIST("gen/tauRapidityMC"))->Fill(trueTauRapidity[iNmother]); + if (tauInRapidity) { + registrySkim.get(HIST("gen/tauPhiMC"))->Fill(trueTauPhi[iNmother]); + registrySkim.get(HIST("gen/tauEtaMC"))->Fill(trueTauEta[iNmother]); + registrySkim.get(HIST("gen/tauPtMC"))->Fill(RecoDecay::pt(trueTauX[iNmother], trueTauY[iNmother])); + } + } - // check whether 1+3 or 3+3 topology is present - if (countDaughters > sixTracks) { // 6 - if (verbose) - LOGF(info, "Truth collision has more than 6 charged daughters from taus. Breaking the daughter loop."); - // histos.get(HIST("Truth/hTroubles"))->Fill(13); - // problem = true; - registrySkim.get(HIST("skim/problemMC"))->Fill(TOO_MANY_DAUGHTERS); - problem = TOO_MANY_DAUGHTERS; - break; - } - // fill info for each daughter - trueDaugX[countDaughters - 1] = daughter.px(); - trueDaugY[countDaughters - 1] = daughter.py(); - trueDaugZ[countDaughters - 1] = daughter.pz(); - trueDaugPdgCode[countDaughters - 1] = daughter.pdgCode(); - } // daughters + if (!tauInRapidity) { // tau NOT in rapidity -> continue + if (verbose) + LOGF(info, "-- At least one mother particle (taus) out of rapidity (|y|<0.9). Jump to the next MC event."); + continue; + } + + // delta eta and delta phi between taus + registrySkim.get(HIST("gen/tauDeltaEtaMC"))->Fill(trueTauEta[0] - trueTauEta[1]); + registrySkim.get(HIST("gen/tauDeltaPhiMC"))->Fill(calculateDeltaPhi(trueTauPhi[0], trueTauPhi[1]) * 180. / o2::constants::math::PI); + + // inv mass of two taus + ROOT::Math::LorentzVector> tau1; + ROOT::Math::LorentzVector> tau2; + tau1.SetXYZT(trueTauX[0], trueTauY[0], trueTauZ[0], RecoDecay::e(trueTauX[0], trueTauY[0], trueTauZ[0], MassTauPlus)); + tau2.SetXYZT(trueTauX[1], trueTauY[1], trueTauZ[1], RecoDecay::e(trueTauX[1], trueTauY[1], trueTauZ[1], MassTauPlus)); + registrySkim.get(HIST("gen/ditauInvMassMC"))->Fill((tau1 + tau2).M()); + + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(2., 1.); // |y_tau| <= 0.9 + countMothers = 0; + int nChargedDaughtersTau[2] = {0, 0}; + int nElec = 0; + int nMuon = 0; + int nPi = 0; + int particleType = -1; + int zerothTau = -10; + int trueChannel = -1; + int countPi0 = -1; + + for (const auto& particle : tmpPartsFromMcColl) { + if (std::abs(particle.pdgCode()) != kTauMinus) + continue; // 15 = tau_minus + const auto& daughters = particle.daughters_as(); + for (const auto& daughter : daughters) { + particleType = enumMyParticle(daughter.pdgCode()); + if (particleType == MyOtherParticle) { // -1 + continue; + } else { + nChargedDaughtersTau[countMothers]++; + if (particleType == MyElectron) // 1 + nElec++; + else if (particleType == MyMuon) // 2 + nMuon++; + else if (particleType == MyPion) // 3 + nPi++; + } + + if (std::abs(RecoDecay::eta(std::array{daughter.px(), daughter.py(), daughter.pz()})) > trkEtacut) // 0.9 + partFromTauInEta = false; + registrySkim.get(HIST("gen/daughterPhiMC"))->Fill(RecoDecay::phi(daughter.px(), daughter.py())); + registrySkim.get(HIST("gen/daughterEtaMC"))->Fill(RecoDecay::eta(std::array{daughter.px(), daughter.py(), daughter.pz()})); + registrySkim.get(HIST("gen/daughterPtMC"))->Fill(RecoDecay::pt(daughter.px(), daughter.py())); + } + countMothers++; + if (countMothers >= desiredNMothers) // 2 + break; + } // end of loop over MC particles + + if (!partFromTauInEta) { + if (verbose) + LOGF(info, "-- At least one daughter particle from taus out of pseudo-rapidity (|eta|<0.9). Jump to the next MC event."); + continue; + } + + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(3., 1.); // charged particles from tau in |eta|<=0.9 + + registrySkim.get(HIST("gen/nChPartMC"))->Fill(nChargedDaughtersTau[0] + nChargedDaughtersTau[1]); // N charged particles from taus + // check number of charged particles in MC event + if ((nChargedDaughtersTau[0] + nChargedDaughtersTau[1] != fourTracks) && (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] != sixTracks)) { + if (verbose) + LOGF(info, "-- Different from 4/6 charged particles (%d) from both taus. Jump to the next MC event.", nChargedDaughtersTau[0] + nChargedDaughtersTau[1]); + continue; + } + + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(4., 1.); // 1+3 (3+3) topology = 4 or 6 tracks + + if ((nChargedDaughtersTau[0] == oneProng) || // 1 + (nChargedDaughtersTau[0] == threeProng && nChargedDaughtersTau[1] == threeProng)) // 3 and 3 + zerothTau = 0; + else if (nChargedDaughtersTau[1] == oneProng) // 1 + zerothTau = 1; + + float trueDaugX[6] = {-998., -998., -998., -998., -998., -998.}; + float trueDaugY[6] = {-998., -998., -998., -998., -998., -998.}; + float trueDaugZ[6] = {-998., -998., -998., -998., -998., -998.}; + int trueDaugPdgCode[6] = {-999, -999, -999, -999, -999, -999}; + + MyRecoProblem problem = NO_PROBLEM; + registrySkim.get(HIST("gen/problemMC"))->Fill(NO_PROBLEM); + + // tau tau event type + // 1 = e+3pi + // 2 = mu+3pi + // 3 = pi+3pi + // 4 = 3pi+3pi + + if (nElec == oneProng && nPi == threeProng) { // 1 + 3 + trueChannel = 1; + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(5., 1.); + } else if (nMuon == oneProng && nPi == threeProng) { // 1 + 3 + trueChannel = 2; + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(6., 1.); + } else if (nPi == fourTracks) { // 4 + trueChannel = 3; + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(7., 1.); + } else if (nPi == sixTracks) { // 6 + trueChannel = 4; + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(8., 1.); + } + + // get particles associated to generated collision + auto const& partsFromMcColl = mcParticles.sliceBy(partPerMcCollision, mccoll.globalIndex()); + if (verbose) + LOGF(info, "partsFromMcColl in MC %d", partsFromMcColl.size()); + int countDaughters = 0; + countPi0 = 0; + for (const auto& particle : partsFromMcColl) { + if (verbose) + LOGF(info, "part Gid %d, Id %d, pdg %d, hasM %d, hasD %d", particle.globalIndex(), particle.index(), particle.pdgCode(), particle.has_mothers(), particle.has_daughters()); + // select only tauons with checking if particle has no mother + // in UPC MC taus have mothers + // if (particle.has_mothers()) + if (std::abs(particle.pdgCode()) != kTauMinus) // 15 + continue; + + // get daughters of the tau + const auto& daughters = particle.daughters_as(); + if (verbose) + LOGF(info, "N_daughters %d", daughters.size()); + for (const auto& daughter : daughters) { if (verbose) - LOGF(info, "End of daughters"); - } // particles - } // collisions + LOGF(info, "daug id %d, pdg %d", daughter.globalIndex(), daughter.pdgCode()); + + if (daughter.pdgCode() == kPi0) + countPi0++; + + // select only the charged particle (= no pi0 or neutrino) + if (enumMyParticle(daughter.pdgCode()) == -1) + continue; + countDaughters++; + + // check whether 1+3 or 3+3 topology is present + if (countDaughters > sixTracks) { // 6 + if (verbose) + LOGF(info, "Truth collision has more than 6 charged daughters from taus. Breaking the daughter loop."); + registrySkim.get(HIST("gen/problemMC"))->Fill(TOO_MANY_DAUGHTERS); + problem = TOO_MANY_DAUGHTERS; + break; + } + // fill info for each daughter + trueDaugX[countDaughters - 1] = daughter.px(); + trueDaugY[countDaughters - 1] = daughter.py(); + trueDaugZ[countDaughters - 1] = daughter.pz(); + trueDaugPdgCode[countDaughters - 1] = daughter.pdgCode(); + } // daughters + + if (verbose) + LOGF(info, "End of daughters"); + } // mcparticles // decide the channel and set the variable. trueChannel = trueChannel + countPi0 * 10 + zerothTau * 100; + // + // check whwther event is reconstructed + // + bool trueHasRecoColl = false; + // find reconstructed collisions associated to the generated collision + auto const& collFromMcColls = collisions.sliceBy(colPerMcCollision, mccoll.globalIndex()); + if (verbose) + LOGF(info, "-- coll from MC Coll %d", collFromMcColls.size()); + // check the generated collision was reconstructed + if (collFromMcColls.size() > 0) { // get the truth and reco-level info + trueHasRecoColl = true; + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(9., 1.); + if (verbose) + LOGF(info, "--- MC Collision has reconstructed collision!"); + + // reconstruction in a given channel + if (nElec == oneProng && nPi == threeProng) { // 1 + 3 + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(10., 1.); + } else if (nMuon == oneProng && nPi == threeProng) { // 1 + 3 + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(11., 1.); + } else if (nPi == fourTracks) { // 4 + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(12., 1.); + } else if (nPi == sixTracks) { // 6 + registrySkim.get(HIST("gen/efficiencyMC"))->Fill(13., 1.); + } + + } else { // get only the truth information. + if (verbose) + LOGF(info, "MC Collision has NO reconstructed collision!"); + } + // LOGF(info, "Should be written!"); - trueTauFourTracks(runNumber, - bc, // is it necessary - totalTracks, - nPVcontrib, - rct, - // dgcand.posX(), dgcand.posY(), - zVertex, - recoMode, - occupancy, - hadronicRate, // is it necessary - bcSels[0], bcSels[1], bcSels[2], // to test it - bcSels[3], bcSels[4], bcSels[5], bcSels[6], bcSels[7], - energyZNA, energyZNC, - timeZNA, timeZNC, - // qtot, <<-------- comment out - amplitudesFIT[0], amplitudesFIT[1], amplitudesFIT[2], - // timesFIT[0], timesFIT[1], timesFIT[2], - px, py, pz, sign, - dcaXY, dcaZ, - nclTPCcrossedRows, nclTPCfind, nclTPCchi2, trkITSchi2, trkITScl, - tpcSignal, tpcEl, tpcPi, tpcKa, tpcPr, tpcMu, - tofSignal, tofEl, tofPi, tofKa, tofPr, tofMu, - chi2TOF, - // - trueChannel, - // trueHasRecoColl, - mccoll.posZ(), - trueTauX, trueTauY, trueTauZ, - trueDaugX, trueDaugY, trueDaugZ, - trueDaugPdgCode, problem); + + if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == fourTracks) { // 4 + genTauFourTracks( + trueChannel, + mccoll.posZ(), + // trueTauX, trueTauY, trueTauZ, + trueDaugX, trueDaugY, trueDaugZ, + trueDaugPdgCode, + problem, + trueHasRecoColl); + } else if (nChargedDaughtersTau[0] + nChargedDaughtersTau[1] == sixTracks) { // 6 + genTauSixTracks( + trueChannel, + mccoll.posZ(), + // trueTauX, trueTauY, trueTauZ, + trueDaugX, trueDaugY, trueDaugZ, + trueDaugPdgCode, + problem, + trueHasRecoColl); + } // end of writing output + } // mccollisions - } // end of processMonteCarlo - PROCESS_SWITCH(TauThreeProngEventTableProducer, processMonteCarlo, "Iterate UD tables with simulated data created by SG-Candidate-Producer.", false); + } // end of processGenerated + PROCESS_SWITCH(TauThreeProngEventTableProducer, processGenerated, "Iterate UD tables with generated data created by SG-Candidate-Producer.", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGUD/TableProducer/trueGapProducer.cxx b/PWGUD/TableProducer/trueGapProducer.cxx new file mode 100644 index 00000000000..da5a6e45203 --- /dev/null +++ b/PWGUD/TableProducer/trueGapProducer.cxx @@ -0,0 +1,71 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file trueGapProducer.cxx +/// \author Zhiyong Lu (zhiyong.lu@cern.ch) +/// \since Apr/7/2026 +/// \brief task producing truegap + +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/DataModel/UDTables.h" +#include "PWGUD/DataModel/UDTruegapsideTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; + +struct TrueGapProducer { + SGSelector sgSelector; + Configurable cfgCutFV0{"cfgCutFV0", 50., "FV0A threshold"}; + Configurable cfgCutFT0A{"cfgCutFT0A", 150., "FT0A threshold"}; + Configurable cfgCutFT0C{"cfgCutFT0C", 50., "FT0C threshold"}; + Configurable cfgCutZDC{"cfgCutZDC", 10., "ZDC threshold"}; + + using UDCollisionsFull = soa::Join; + + Produces truegapside; + HistogramRegistry registry{"registry"}; + + void init(InitContext&) + { + std::vector binEdges = {-1.5, -0.5, 0.5, 1.5, 2.5, 3.5}; + AxisSpec axisgap = {binEdges, "true gap side"}; + registry.add("truegap", "truegap", {HistType::kTH1D, {axisgap}}); + } + + void process(UDCollisionsFull::iterator const& collision) + { + auto truegap = sgSelector.trueGap(collision, cfgCutFV0, cfgCutFT0A, cfgCutFT0C, cfgCutZDC); + truegapside(truegap); + registry.fill(HIST("truegap"), truegap); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx b/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx new file mode 100644 index 00000000000..1950842a7cf --- /dev/null +++ b/PWGUD/TableProducer/twoFwdTracksEventTableProducer.cxx @@ -0,0 +1,609 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file twoFwdTracksEventTableProducer.cxx +/// \brief Produces derived table with two forward tracks per event from UD tables created by upcCandProducerGlobalMuon +/// +/// \author Roman Lavicka , Austrian Academy of Sciences & SMI +/// \since 02.10.2026 +// + +#include "PWGUD/Core/UPCHelpers.h" +#include "PWGUD/Core/UPCTauCentralBarrelHelperRL.h" +#include "PWGUD/DataModel/TwoFwdTracksEventTables.h" +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::constants::physics; + +namespace +{ +constexpr int NumTracks = 2; // the table stores exactly two forward tracks per event +constexpr int DefaultInt = -999; // value of integer columns without information +constexpr float DefaultFloat = -999.f; // value of float columns without information +constexpr uint32_t DefaultMIDBoards = 0; // value of MID-board columns without information + +enum EventSelection { + EvSelAll = 0, + EvSelFV0Veto, + EvSelNumContrib, + EvSelTwoTracks, + EvSelInvariantMass, + EvSelSystemPt, + EvSelOneTrackMomentum, + EvSelBothTracksMomentum, + NEvSels +}; + +enum FwdTrackSelection { + TrkSelAll = 0, + TrkSelType, + TrkSelPt, + TrkSelEta, + TrkSelRabs, + TrkSelPDca, + TrkSelChi2, + TrkSelChi2MatchMCHMFT, + NTrkSels +}; + +enum TruthTrouble { + TroubleTooManyMothers = 1, + TroubleTooManyDaughters, + TroubleNoTrack, + TroubleMoreTracks, + TroubleDifferentCandidates, + NTroubles +}; + +// reco-level information of one row, shared by the measured and the simulated table +struct RecoInfo { + int32_t runNumber{DefaultInt}; + uint64_t bc{0}; + int numContrib{DefaultInt}; + float posX{DefaultFloat}; + float posY{DefaultFloat}; + float posZ{DefaultFloat}; + float totalFV0AmplitudeA{DefaultFloat}; + std::vector amplitudesV0A; + std::vector ampRelBCsV0A; + float energyCommonZNA{DefaultFloat}; + float energyCommonZNC{DefaultFloat}; + float timeZNA{DefaultFloat}; + float timeZNC{DefaultFloat}; + float px[NumTracks] = {DefaultFloat, DefaultFloat}; + float py[NumTracks] = {DefaultFloat, DefaultFloat}; + float pz[NumTracks] = {DefaultFloat, DefaultFloat}; + int sign[NumTracks] = {DefaultInt, DefaultInt}; + float time[NumTracks] = {DefaultFloat, DefaultFloat}; + float timeRes[NumTracks] = {DefaultFloat, DefaultFloat}; + int type[NumTracks] = {DefaultInt, DefaultInt}; + int nClusters[NumTracks] = {DefaultInt, DefaultInt}; + float pDca[NumTracks] = {DefaultFloat, DefaultFloat}; + float rAbs[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2MatchMCHMID[NumTracks] = {DefaultFloat, DefaultFloat}; + float chi2MatchMCHMFT[NumTracks] = {DefaultFloat, DefaultFloat}; + int mchBitMap[NumTracks] = {DefaultInt, DefaultInt}; + int midBitMap[NumTracks] = {DefaultInt, DefaultInt}; + uint32_t midBoards[NumTracks] = {DefaultMIDBoards, DefaultMIDBoards}; +}; + +// truth-level information of one row of the simulated table +struct TruthInfo { + int channel{-1}; + bool hasRecoColl{false}; + float motherPx[NumTracks] = {DefaultFloat, DefaultFloat}; + float motherPy[NumTracks] = {DefaultFloat, DefaultFloat}; + float motherPz[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPx[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPy[NumTracks] = {DefaultFloat, DefaultFloat}; + float daugPz[NumTracks] = {DefaultFloat, DefaultFloat}; + int daugPdgCode[NumTracks] = {DefaultInt, DefaultInt}; + bool problem{false}; +}; +} // namespace + +struct TwoFwdTracksEventTableProducer { + Produces twoFwdTracks; + Produces trueTwoFwdTracks; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // declare configurables + Configurable verboseInfo{"verboseInfo", false, {"Print general info to terminal; default it false."}}; + + struct : ConfigurableGroup { + Configurable useFV0Veto{"useFV0Veto", true, {"Reject candidates with FV0A activity"}}; + Configurable cutMaxFV0Amp{"cutMaxFV0Amp", 100.f, {"Maximum FV0A amplitude allowed"}}; + Configurable cutFV0RelBcRange{"cutFV0RelBcRange", 0, {"FV0A veto applied in BCs [-range, +range] around the candidate BC; 0 = candidate BC only"}}; + Configurable useNumContrib{"useNumContrib", false, {"Require given number of forward tracks in the candidate (before track selection)"}}; + Configurable cutNumContrib{"cutNumContrib", 2, {"Number of forward tracks required in the candidate"}}; + } cutSample; + + struct : ConfigurableGroup { + Configurable acceptGlobalMuonTrack{"acceptGlobalMuonTrack", true, {"Accept MCH-MID-MFT tracks"}}; + Configurable acceptGlobalForwardTrack{"acceptGlobalForwardTrack", true, {"Accept MCH-MFT tracks"}}; + Configurable acceptMuonStandaloneTrack{"acceptMuonStandaloneTrack", true, {"Accept MCH-MID tracks"}}; + Configurable acceptMCHStandaloneTrack{"acceptMCHStandaloneTrack", true, {"Accept MCH-only tracks"}}; + Configurable applyFwdTrackSelection{"applyFwdTrackSelection", true, {"Apply kinematic and quality cuts defined below on forward tracks"}}; + Configurable cutMinPt{"cutMinPt", 0.f, {"Forward track cut"}}; + Configurable cutMaxPt{"cutMaxPt", 1e10f, {"Forward track cut"}}; + Configurable cutMinEta{"cutMinEta", -4.f, {"Forward track cut"}}; + Configurable cutMaxEta{"cutMaxEta", -2.5f, {"Forward track cut"}}; + Configurable cutMinRabs{"cutMinRabs", 17.6f, {"Forward track cut on radius at the absorber end (cm)"}}; + Configurable cutMaxRabs{"cutMaxRabs", 89.5f, {"Forward track cut on radius at the absorber end (cm)"}}; + Configurable cutMaxPDcaLowRabs{"cutMaxPDcaLowRabs", 594.f, {"Forward track pDCA cut for rAbs below 26.5 cm"}}; + Configurable cutMaxPDcaHighRabs{"cutMaxPDcaHighRabs", 324.f, {"Forward track pDCA cut for rAbs above 26.5 cm"}}; + Configurable cutMaxChi2{"cutMaxChi2", 1e10f, {"Forward track cut on track chi2"}}; + Configurable cutMaxChi2MatchMCHMFT{"cutMaxChi2MatchMCHMFT", 1e10f, {"Forward track cut on MCH-MFT matching chi2 (MFT tracks only)"}}; + } cutFwdTrack; + + struct : ConfigurableGroup { + Configurable preselUseMinMomentumOnBothTracks{"preselUseMinMomentumOnBothTracks", false, {"Both tracks are required to fill requirement on minimum momentum."}}; + Configurable preselMinTrackMomentum{"preselMinTrackMomentum", 0.1, {"Requirement on minimum momentum of the track."}}; + Configurable preselSystemPtCut{"preselSystemPtCut", 2.0, {"By default, cut on maximum system pT."}}; + Configurable preselUseOppositeSystemPtCut{"preselUseOppositeSystemPtCut", false, {"Negates the system pT cut (cut on minimum system pT)."}}; + Configurable preselMinInvariantMass{"preselMinInvariantMass", 2.0, {"Requirement on minimum system invariant mass."}}; + Configurable preselMaxInvariantMass{"preselMaxInvariantMass", 5.0, {"Requirement on maximum system invariant mass."}}; + } cutPreselect; + + using CandidatesFwd = soa::Join; + using CandidateFwd = CandidatesFwd::iterator; + using FwdTracks = soa::Join; + using FwdMcTracks = soa::Join; + + // init + void init(InitContext&) + { + if (verboseInfo) + printMediumMessage("INIT METHOD"); + + histos.add("Reco/hSelections", "Effect of selections;;Number of events (-)", HistType::kTH1D, {{NEvSels, -0.5, static_cast(NEvSels) - 0.5}}); + auto hSel = histos.get(HIST("Reco/hSelections")); + hSel->GetXaxis()->SetBinLabel(EvSelAll + 1, "All"); + hSel->GetXaxis()->SetBinLabel(EvSelFV0Veto + 1, "FV0A veto"); + hSel->GetXaxis()->SetBinLabel(EvSelNumContrib + 1, "N contrib."); + hSel->GetXaxis()->SetBinLabel(EvSelTwoTracks + 1, "2 sel. tracks"); + hSel->GetXaxis()->SetBinLabel(EvSelInvariantMass + 1, "Inv. mass"); + hSel->GetXaxis()->SetBinLabel(EvSelSystemPt + 1, "System pT"); + hSel->GetXaxis()->SetBinLabel(EvSelOneTrackMomentum + 1, "One trk p"); + hSel->GetXaxis()->SetBinLabel(EvSelBothTracksMomentum + 1, "Both trks p"); + + histos.add("Reco/hFwdTrackSelections", "Effect of forward track selections;;Number of tracks (-)", HistType::kTH1D, {{NTrkSels, -0.5, static_cast(NTrkSels) - 0.5}}); + auto hTrkSel = histos.get(HIST("Reco/hFwdTrackSelections")); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelAll + 1, "All"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelType + 1, "Track type"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelPt + 1, "pT"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelEta + 1, "#eta"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelRabs + 1, "R_{abs}"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelPDca + 1, "pDCA"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelChi2 + 1, "#chi^{2}"); + hTrkSel->GetXaxis()->SetBinLabel(TrkSelChi2MatchMCHMFT + 1, "#chi^{2}_{MCH-MFT}"); + + histos.add("Reco/hNanalyzedPerRun", "N analyzed events per run;Run number (-);Number of analyzed events (-)", HistType::kTH1D, {{1, 0., 1.}}); + histos.add("Reco/hNselectedPerRun", "N selected events per run;Run number (-);Number of selected events (-)", HistType::kTH1D, {{1, 0., 1.}}); + + histos.add("Truth/hTroubles", "Counter of unwanted issues;;Number of troubles (-)", HistType::kTH1D, {{NTroubles - 1, 0.5, static_cast(NTroubles) - 0.5}}); + auto hTroubles = histos.get(HIST("Truth/hTroubles")); + hTroubles->GetXaxis()->SetBinLabel(TroubleTooManyMothers + 1, "> 2 mothers"); + hTroubles->GetXaxis()->SetBinLabel(TroubleTooManyDaughters + 1, "> 2 charged daughters"); + hTroubles->GetXaxis()->SetBinLabel(TroubleNoTrack + 1, "Daughter without track"); + hTroubles->GetXaxis()->SetBinLabel(TroubleMoreTracks + 1, "Daughter in > 1 cand."); + hTroubles->GetXaxis()->SetBinLabel(TroubleDifferentCandidates + 1, "Daughters in diff. cand."); + } // end init + + bool isAcceptedTrackType(uint8_t trackType) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum; + switch (trackType) { + case ForwardTrackTypeEnum::GlobalMuonTrack: + return cutFwdTrack.acceptGlobalMuonTrack; + case ForwardTrackTypeEnum::GlobalForwardTrack: + return cutFwdTrack.acceptGlobalForwardTrack; + case ForwardTrackTypeEnum::MuonStandaloneTrack: + return cutFwdTrack.acceptMuonStandaloneTrack; + case ForwardTrackTypeEnum::MCHStandaloneTrack: + return cutFwdTrack.acceptMCHStandaloneTrack; + default: + return false; + } + } + + static bool hasMFT(uint8_t trackType) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum; + return trackType == ForwardTrackTypeEnum::GlobalMuonTrack || trackType == ForwardTrackTypeEnum::GlobalForwardTrack; + } + + template + static ROOT::Math::PxPyPzMVector muonVector(T const& track) + { + return ROOT::Math::PxPyPzMVector(track.px(), track.py(), track.pz(), MassMuon); + } + + template + bool isSelectedFwdTrack(T const& track, bool fillHistos = true) + { + auto countPassed = [&](FwdTrackSelection step) { + if (fillHistos) + histos.fill(HIST("Reco/hFwdTrackSelections"), step); + }; + countPassed(TrkSelAll); + + if (!isAcceptedTrackType(track.trackType())) + return false; + countPassed(TrkSelType); + + if (!cutFwdTrack.applyFwdTrackSelection) + return true; + + const auto vec = muonVector(track); + if (vec.Pt() < cutFwdTrack.cutMinPt || vec.Pt() > cutFwdTrack.cutMaxPt) + return false; + countPassed(TrkSelPt); + + if (vec.Eta() < cutFwdTrack.cutMinEta || vec.Eta() > cutFwdTrack.cutMaxEta) + return false; + countPassed(TrkSelEta); + + if (track.rAtAbsorberEnd() < cutFwdTrack.cutMinRabs || track.rAtAbsorberEnd() > cutFwdTrack.cutMaxRabs) + return false; + countPassed(TrkSelRabs); + + float maxPDca = track.rAtAbsorberEnd() < upchelpers::AbsorberMid ? cutFwdTrack.cutMaxPDcaLowRabs : cutFwdTrack.cutMaxPDcaHighRabs; + if (track.pDca() > maxPDca) + return false; + countPassed(TrkSelPDca); + + if (track.chi2() > cutFwdTrack.cutMaxChi2) + return false; + countPassed(TrkSelChi2); + + if (hasMFT(track.trackType()) && track.chi2MatchMCHMFT() > cutFwdTrack.cutMaxChi2MatchMCHMFT) + return false; + countPassed(TrkSelChi2MatchMCHMFT); + + return true; + } + + // largest FV0A amplitude within [-maxRelBc, +maxRelBc] around the candidate BC + template + static float getMaxFV0Amplitude(C const& cand, int maxRelBc) + { + const auto& amps = cand.amplitudesV0A(); + const auto& relBCs = cand.ampRelBCsV0A(); + float maxAmp = 0.f; + for (std::size_t i = 0; i < amps.size(); ++i) { + if (std::abs(relBCs[i]) <= maxRelBc) + maxAmp = std::max(maxAmp, amps[i]); + } + return maxAmp; + } + + static float finiteOrDefault(float value) + { + return std::isfinite(value) ? value : DefaultFloat; + } + + template + static void fillEventInfo(RecoInfo& info, C const& cand) + { + info.runNumber = cand.runNumber(); + info.bc = cand.globalBC(); + info.numContrib = cand.numContrib(); + info.posX = cand.posX(); + info.posY = cand.posY(); + info.posZ = cand.posZ(); + info.totalFV0AmplitudeA = getMaxFV0Amplitude(cand, 0); + const auto& amps = cand.amplitudesV0A(); + const auto& relBCs = cand.ampRelBCsV0A(); + info.amplitudesV0A.assign(amps.begin(), amps.end()); + info.ampRelBCsV0A.assign(relBCs.begin(), relBCs.end()); + } + + // the ZDC table is filled by the producer only for candidates with ZDC activity, i.e. it holds at most one row per candidate + static void fillZdcInfo(RecoInfo& info, aod::UDZdcsReduced const& zdcs) + { + for (const auto& zdc : zdcs) { + info.energyCommonZNA = finiteOrDefault(zdc.energyCommonZNA()); + info.energyCommonZNC = finiteOrDefault(zdc.energyCommonZNC()); + info.timeZNA = finiteOrDefault(zdc.timeZNA()); + info.timeZNC = finiteOrDefault(zdc.timeZNC()); + } + } + + template + static void fillTrackInfo(RecoInfo& info, int iTrk, T const& track) + { + info.px[iTrk] = track.px(); + info.py[iTrk] = track.py(); + info.pz[iTrk] = track.pz(); + info.sign[iTrk] = track.sign(); + info.time[iTrk] = track.trackTime(); + info.timeRes[iTrk] = track.trackTimeRes(); + info.type[iTrk] = track.trackType(); + info.nClusters[iTrk] = track.nClusters(); + info.pDca[iTrk] = track.pDca(); + info.rAbs[iTrk] = track.rAtAbsorberEnd(); + info.chi2[iTrk] = track.chi2(); + info.chi2MatchMCHMID[iTrk] = track.chi2MatchMCHMID(); + info.chi2MatchMCHMFT[iTrk] = track.chi2MatchMCHMFT(); + info.mchBitMap[iTrk] = track.mchBitMap(); + info.midBitMap[iTrk] = track.midBitMap(); + info.midBoards[iTrk] = track.midBoards(); + } + + // writes reco-level columns, followed by optional truth-level columns of the simulated table + template + static void writeRow(TCursor& cursor, RecoInfo& info, TTruth&&... truth) + { + cursor(info.runNumber, info.bc, info.numContrib, info.posX, info.posY, info.posZ, + info.totalFV0AmplitudeA, info.amplitudesV0A, info.ampRelBCsV0A, + info.energyCommonZNA, info.energyCommonZNC, info.timeZNA, info.timeZNC, + info.px, info.py, info.pz, info.sign, info.time, info.timeRes, + info.type, info.nClusters, info.pDca, info.rAbs, info.chi2, info.chi2MatchMCHMID, info.chi2MatchMCHMFT, + info.mchBitMap, info.midBitMap, info.midBoards[0], info.midBoards[1], + std::forward(truth)...); + } + + static int getTrueChannel(const int pdgCodes[NumTracks]) + { + const int part1 = enumMyParticle(pdgCodes[0]); + const int part2 = enumMyParticle(pdgCodes[1]); + if (part1 == P_ELECTRON && part2 == P_ELECTRON) + return CH_EE; + if (part1 == P_MUON && part2 == P_MUON) + return CH_MUMU; + if ((part1 == P_ELECTRON && (part2 == P_PION || part2 == P_MUON)) || (part2 == P_ELECTRON && (part1 == P_PION || part1 == P_MUON))) + return CH_EMUPI; + return -1; + } + + void processDataFwd(CandidateFwd const& cand, + FwdTracks const& tracks, + aod::UDZdcsReduced const& zdcs) + { + const char* srun = Form("%d", cand.runNumber()); + histos.get(HIST("Reco/hNanalyzedPerRun"))->Fill(srun, 1); + + histos.fill(HIST("Reco/hSelections"), EvSelAll); + + if (cutSample.useFV0Veto && getMaxFV0Amplitude(cand, cutSample.cutFV0RelBcRange) > cutSample.cutMaxFV0Amp) + return; + histos.fill(HIST("Reco/hSelections"), EvSelFV0Veto); + + if (cutSample.useNumContrib && cand.numContrib() != cutSample.cutNumContrib) + return; + histos.fill(HIST("Reco/hSelections"), EvSelNumContrib); + + RecoInfo info; + std::array daug; + int nSelected = 0; + for (const auto& track : tracks) { + if (!isSelectedFwdTrack(track)) + continue; + if (nSelected < NumTracks) { + fillTrackInfo(info, nSelected, track); + daug[nSelected] = muonVector(track); + } + nSelected++; + } + + // Critical selection, without it the rest of the process function will fail + if (nSelected != NumTracks) + return; + histos.fill(HIST("Reco/hSelections"), EvSelTwoTracks); + + const auto mother = daug[0] + daug[1]; + + // Apply system selections + // invariant mass + if (mother.M() < cutPreselect.preselMinInvariantMass || mother.M() > cutPreselect.preselMaxInvariantMass) + return; + histos.fill(HIST("Reco/hSelections"), EvSelInvariantMass); + + // system pt + if (cutPreselect.preselUseOppositeSystemPtCut ? mother.Pt() < cutPreselect.preselSystemPtCut : mother.Pt() > cutPreselect.preselSystemPtCut) + return; + histos.fill(HIST("Reco/hSelections"), EvSelSystemPt); + + // one track momentum + if (daug[0].P() < cutPreselect.preselMinTrackMomentum && daug[1].P() < cutPreselect.preselMinTrackMomentum) + return; + histos.fill(HIST("Reco/hSelections"), EvSelOneTrackMomentum); + + // both tracks momentum + if (cutPreselect.preselUseMinMomentumOnBothTracks && (daug[0].P() < cutPreselect.preselMinTrackMomentum || daug[1].P() < cutPreselect.preselMinTrackMomentum)) + return; + histos.fill(HIST("Reco/hSelections"), EvSelBothTracksMomentum); + + histos.get(HIST("Reco/hNselectedPerRun"))->Fill(srun, 1); + + fillEventInfo(info, cand); + fillZdcInfo(info, zdcs); + writeRow(twoFwdTracks, info); + } + PROCESS_SWITCH(TwoFwdTracksEventTableProducer, processDataFwd, "Iterate UD tables with measured data created by upcCandProducerGlobalMuon.", true); + + PresliceUnsorted partPerMcCollision = aod::udmcparticle::udMcCollisionId; + PresliceUnsorted trackPerMcParticle = aod::udmcfwdtracklabel::udMcParticleId; + + void processMonteCarlo(aod::UDMcCollisions const& mcCollisions, + aod::UDMcParticles const& parts, + CandidatesFwd const& candidates, + FwdMcTracks const& tracks) + { + // start loop over generated collisions + for (const auto& mcColl : mcCollisions) { + RecoInfo info; + TruthInfo truth; + std::vector daughterIds; + + // store a charged particle as truth daughter; returns false if there are already too many of them + auto addTrueDaughter = [&](auto const& daughter) { + // check if it is the charged particle (= no pi0, photon or neutrino) + if (enumMyParticle(daughter.pdgCode()) == -1) + return true; + // check we do not have more than 2 charged daughters in total + if (static_cast(daughterIds.size()) >= NumTracks) { + if (verboseInfo) + printLargeMessage("Truth collision has more than 2 total charged daughters. Breaking the daughter loop."); + histos.fill(HIST("Truth/hTroubles"), TroubleTooManyDaughters); + truth.problem = true; + return false; + } + const auto iDaug = daughterIds.size(); + truth.daugPx[iDaug] = daughter.px(); + truth.daugPy[iDaug] = daughter.py(); + truth.daugPz[iDaug] = daughter.pz(); + truth.daugPdgCode[iDaug] = daughter.pdgCode(); + daughterIds.push_back(daughter.globalIndex()); + return true; + }; + + // get particles associated to generated collision + const auto& partsFromMcColl = parts.sliceBy(partPerMcCollision, mcColl.globalIndex()); + int countMothers = 0; + for (const auto& particle : partsFromMcColl) { + // select only mothers with checking if particle has no mother + if (particle.has_mothers()) + continue; + countMothers++; + // check the generated collision does not have more than 2 mothers + if (countMothers > NumTracks) { + if (verboseInfo) + printLargeMessage("Truth collision has more than 2 no mother particles. Breaking the particle loop."); + histos.fill(HIST("Truth/hTroubles"), TroubleTooManyMothers); + truth.problem = true; + break; + } + // fill info for each mother + truth.motherPx[countMothers - 1] = particle.px(); + truth.motherPy[countMothers - 1] = particle.py(); + truth.motherPz[countMothers - 1] = particle.pz(); + + if (particle.has_daughters()) { + for (const auto& daughter : particle.daughters_as()) { + if (!addTrueDaughter(daughter)) + break; + } + } else { + // motherless final-state particle, e.g. muon from gamma gamma -> mu mu, is its own daughter + addTrueDaughter(particle); + } + if (truth.problem) + break; + } // particles + + // get the reconstructed tracks of each daughter (how well the daughter was reconstructed). + // One muon can be stored in several candidates (as MCH-MID-MFT track in an anchor candidate and as MCH-MID track + // in an MCH-MID candidate), so collect per daughter the selected track in each candidate, preferring the lowest track type. + std::array, NumTracks> trackIdPerCand; // candidate id -> track id, per daughter + int nRecoDaughters = 0; + for (std::size_t iDaug = 0; iDaug < daughterIds.size(); ++iDaug) { + auto& candTracks = trackIdPerCand[iDaug]; + for (const auto& trk : tracks.sliceBy(trackPerMcParticle, daughterIds[iDaug])) { + if (!isSelectedFwdTrack(trk, false)) + continue; + auto it = candTracks.find(trk.udCollisionId()); + if (it == candTracks.end() || trk.trackType() < tracks.iteratorAt(it->second).trackType()) + candTracks[trk.udCollisionId()] = trk.globalIndex(); + } + if (candTracks.empty()) { + if (verboseInfo) + printLargeMessage("Daughter has no associated track. Skipping this daughter."); + histos.fill(HIST("Truth/hTroubles"), TroubleNoTrack); + truth.problem = true; + continue; + } + if (candTracks.size() > 1) + histos.fill(HIST("Truth/hTroubles"), TroubleMoreTracks); + nRecoDaughters++; + } // daughters + + // choose the candidate holding most daughters; on a tie the one with the lowest summed track type (i.e. with MFT) + std::map> candScore; // candidate id -> (number of daughters, summed track type) + for (const auto& candTracks : trackIdPerCand) { + for (const auto& [cand, trkId] : candTracks) { + candScore[cand].first++; + candScore[cand].second += tracks.iteratorAt(trkId).trackType(); + } + } + int32_t candId = -1; + std::pair bestScore{0, 0}; + for (const auto& [cand, score] : candScore) { + if (candId < 0 || score.first > bestScore.first || (score.first == bestScore.first && score.second < bestScore.second)) { + candId = cand; + bestScore = score; + } + } + + // fill info for reconstructed collision and tracks of the chosen candidate + if (candId >= 0) { + if (bestScore.first < nRecoDaughters) { + if (verboseInfo) + printLargeMessage("Daughters are reconstructed in different candidates."); + histos.fill(HIST("Truth/hTroubles"), TroubleDifferentCandidates); + truth.problem = true; + } + truth.hasRecoColl = true; + fillEventInfo(info, candidates.iteratorAt(candId)); + for (int iDaug = 0; iDaug < NumTracks; ++iDaug) { + auto it = trackIdPerCand[iDaug].find(candId); + if (it != trackIdPerCand[iDaug].end()) + fillTrackInfo(info, iDaug, tracks.iteratorAt(it->second)); + } + } + + truth.channel = getTrueChannel(truth.daugPdgCode); + + writeRow(trueTwoFwdTracks, info, // no ZDC info in MC + truth.channel, truth.hasRecoColl, mcColl.posX(), mcColl.posY(), mcColl.posZ(), + truth.motherPx, truth.motherPy, truth.motherPz, truth.daugPx, truth.daugPy, truth.daugPz, truth.daugPdgCode, truth.problem); + } // mccollisions + } + PROCESS_SWITCH(TwoFwdTracksEventTableProducer, processMonteCarlo, "Iterate UD tables with simulated data created by upcCandProducerGlobalMuon.", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/TableProducer/twoTracksEventTableProducer.cxx b/PWGUD/TableProducer/twoTracksEventTableProducer.cxx index 36c5c44112f..cf5f91a34a6 100644 --- a/PWGUD/TableProducer/twoTracksEventTableProducer.cxx +++ b/PWGUD/TableProducer/twoTracksEventTableProducer.cxx @@ -241,7 +241,7 @@ struct TwoTracksEventTableProducer { std::vector>> cutMyRequiredITSHits{}; - void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, const std::set& requiredLayers) { // layer 0 corresponds to the the innermost ITS layer cutMyRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); diff --git a/PWGUD/TableProducer/upcCandProducerBarrel.cxx b/PWGUD/TableProducer/upcCandProducerBarrel.cxx new file mode 100644 index 00000000000..d9beaca9aa7 --- /dev/null +++ b/PWGUD/TableProducer/upcCandProducerBarrel.cxx @@ -0,0 +1,831 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file upcCandProducerBarrel.cxx +/// \brief Compact candidate table producer for diffraction and UPC studies. +/// Requires: event selection, propagation, TPC PID and TOF PID services +/// \author Nazar Burmasov (JINR), Evgeny Kryshen (JINR) + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/Core/TableHelper.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::aod::rctsel; + +namespace +{ +constexpr int NBCsPerOrbit = o2::constants::lhc::LHCMaxBunches; +constexpr float MaxFITTime = 30.f; + +constexpr int NoSignalMCRef = -1; +constexpr int NoRecoCollision = -1; +constexpr int MultipleRecoTracks = -2; + +constexpr uint16_t MCTrackFakeMask = 1u << 15; + +enum MCParticleFlag : uint8_t { + kNoFlags = 0, + kPhysicalPrimary = 1u << 0, // generated physical primary + kHasRecoTrack = 1u << 1, // has at least one non-fake reconstructed track + kHighMultiplicityCollision = 1u << 2, // matched track belongs to a collision with extra contributors + kHasPVContributor = 1u << 3, // has a track contributing to the PV + kHasAmbiguousTrack = 1u << 4 // has an ambiguous track +}; + +using SignalRef = std::pair; +using DistanceMap = std::vector>; + +DistanceMap buildMinimumDistanceMap(std::vector const& tfIDs, + int64_t bcSOR, int64_t nBCsPerTF, + std::vector& activeBCs, uint32_t maxDistance, + bool skipFirstExactMatch = false) +{ + const uint32_t overflowDistance = maxDistance + 1; + DistanceMap distances(tfIDs.size(), std::vector(nBCsPerTF, overflowDistance)); + if (activeBCs.empty()) { + return distances; + } + // Keep duplicate BCs so collisions at the same BC have distance zero + std::sort(activeBCs.begin(), activeBCs.end()); + // The forward-only cursor makes the scan linear after sorting + size_t nextIndex = 0; + for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { + // Cast before multiplication to prevent 32-bit integer overflow. + const int64_t tfStartBC = bcSOR + static_cast(tfIDs[iTF]) * nBCsPerTF; + // Use int64_t to keep BC arithmetic in 64 bits without additional casts. + for (int64_t bcInTF = 0; bcInTF < nBCsPerTF; ++bcInTF) { + const int64_t currentBC = tfStartBC + bcInTF; + while (nextIndex < activeBCs.size() && activeBCs[nextIndex] < currentBC) { + ++nextIndex; + } + size_t rightIndex = nextIndex; + // MC maps contain the queried collision itself, so skip one exact self-match + if (skipFirstExactMatch && rightIndex < activeBCs.size() && activeBCs[rightIndex] == currentBC) { + ++rightIndex; + } + int64_t distance = overflowDistance; + if (rightIndex < activeBCs.size()) { + distance = std::min(distance, activeBCs[rightIndex] - currentBC); + } + if (nextIndex > 0) { + distance = std::min(distance, currentBC - activeBCs[nextIndex - 1]); + } + distances[iTF][bcInTF] = static_cast(distance); + } + } + return distances; +} + +void fillVetoHistograms(std::shared_ptr const& hVetoT00, std::shared_ptr const& hVetoTV0, + std::shared_ptr const& hVetoTVD, int bcInOrbit, int tfID, + uint32_t distanceFT0, uint32_t distanceFV0, uint32_t distanceFDD) +{ + hVetoT00->Fill(bcInOrbit, tfID, distanceFT0); + hVetoTV0->Fill(bcInOrbit, tfID, std::min(distanceFT0, distanceFV0)); + hVetoTVD->Fill(bcInOrbit, tfID, std::min({distanceFT0, distanceFV0, distanceFDD})); +} +} // namespace + +namespace o2::aod::upc_cand_prod_bar +{ +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); +DECLARE_SOA_COLUMN(TfId, tfId, uint32_t); +DECLARE_SOA_COLUMN(Timestamp, timestamp, uint64_t); +DECLARE_SOA_COLUMN(Flags, flags, int8_t); +DECLARE_SOA_COLUMN(OccupancyFT0C, occupancyFT0C, float); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(Px, px, std::vector); +DECLARE_SOA_COLUMN(Py, py, std::vector); +DECLARE_SOA_COLUMN(Pz, pz, std::vector); +DECLARE_SOA_COLUMN(TpcSignal, tpcSignal, std::vector); +DECLARE_SOA_COLUMN(TpcNSigmaEl, tpcNSigmaEl, std::vector); +DECLARE_SOA_COLUMN(TpcNSigmaPi, tpcNSigmaPi, std::vector); +DECLARE_SOA_COLUMN(TpcNSigmaKa, tpcNSigmaKa, std::vector); +DECLARE_SOA_COLUMN(TpcNSigmaPr, tpcNSigmaPr, std::vector); +DECLARE_SOA_COLUMN(TofNSigmaEl, tofNSigmaEl, std::vector); +DECLARE_SOA_COLUMN(TofNSigmaPi, tofNSigmaPi, std::vector); +DECLARE_SOA_COLUMN(TofNSigmaKa, tofNSigmaKa, std::vector); +DECLARE_SOA_COLUMN(TofNSigmaPr, tofNSigmaPr, std::vector); +DECLARE_SOA_COLUMN(ItsClusterMap, itsClusterMap, std::vector); +DECLARE_SOA_COLUMN(NClusters, nClusters, std::vector); +DECLARE_SOA_COLUMN(Sign, sign, std::vector); +DECLARE_SOA_COLUMN(MinimumDistanceFT0, minimumDistanceFT0, int8_t); +DECLARE_SOA_COLUMN(MinimumDistanceFV0, minimumDistanceFV0, int8_t); +DECLARE_SOA_COLUMN(MinimumDistanceFDD, minimumDistanceFDD, int8_t); +DECLARE_SOA_COLUMN(Pdg, pdg, std::vector); +DECLARE_SOA_COLUMN(MinimumDistanceMCCol, minimumDistanceMCCol, int8_t); +DECLARE_SOA_COLUMN(RecoFlags, recoFlags, std::vector); // See MCParticleFlag for the bit definitions +DECLARE_SOA_COLUMN(RecoCollisionIds, recoCollisionIds, std::vector); // -1: no collision, -2: multiple tracks +DECLARE_SOA_COLUMN(ParticleIds, particleIds, std::vector); +DECLARE_SOA_COLUMN(Mult, mult, int32_t); // number of physical-primary MC particles in the collision +DECLARE_SOA_COLUMN(IsSignal, isSignal, bool); // whether the collision belongs to the configured signal source +} // namespace o2::aod::upc_cand_prod_bar + +namespace o2::aod +{ +DECLARE_SOA_TABLE(UPCBarrelCands, "AOD", "UPCBARRELCANDS", + upc_cand_prod_bar::RunNumber, + upc_cand_prod_bar::GlobalBC, + upc_cand_prod_bar::TfId, + upc_cand_prod_bar::Timestamp, + upc_cand_prod_bar::Flags, + upc_cand_prod_bar::OccupancyFT0C, + upc_cand_prod_bar::PosX, + upc_cand_prod_bar::PosY, + upc_cand_prod_bar::PosZ, + upc_cand_prod_bar::Px, + upc_cand_prod_bar::Py, + upc_cand_prod_bar::Pz, + upc_cand_prod_bar::TpcSignal, + upc_cand_prod_bar::TpcNSigmaEl, + upc_cand_prod_bar::TpcNSigmaPi, + upc_cand_prod_bar::TpcNSigmaKa, + upc_cand_prod_bar::TpcNSigmaPr, + upc_cand_prod_bar::TofNSigmaEl, + upc_cand_prod_bar::TofNSigmaPi, + upc_cand_prod_bar::TofNSigmaKa, + upc_cand_prod_bar::TofNSigmaPr, + upc_cand_prod_bar::ItsClusterMap, + upc_cand_prod_bar::NClusters, + upc_cand_prod_bar::Sign, + upc_cand_prod_bar::MinimumDistanceFT0, + upc_cand_prod_bar::MinimumDistanceFV0, + upc_cand_prod_bar::MinimumDistanceFDD); + +DECLARE_SOA_TABLE(UPCBarrelMcColls, "AOD", "UPCBMCCOLL", + upc_cand_prod_bar::TfId, + upc_cand_prod_bar::GlobalBC, + upc_cand_prod_bar::PosX, + upc_cand_prod_bar::PosY, + upc_cand_prod_bar::PosZ, + upc_cand_prod_bar::Px, + upc_cand_prod_bar::Py, + upc_cand_prod_bar::Pz, + upc_cand_prod_bar::Pdg, + upc_cand_prod_bar::Mult, + upc_cand_prod_bar::MinimumDistanceMCCol, + upc_cand_prod_bar::RecoFlags, + upc_cand_prod_bar::RecoCollisionIds, + upc_cand_prod_bar::IsSignal); + +namespace upc_cand_prod_bar +{ +// Array index into UPCBarrelMcColls +DECLARE_SOA_ARRAY_INDEX_COLUMN_FULL_CUSTOM(McCollision, mcCollision, int32_t, UPCBarrelMcColls, "UPCBMCColls", ""); +} // namespace upc_cand_prod_bar + +// Both IDs are -1 if the track has no valid stored signal reference +DECLARE_SOA_TABLE(UPCBarrelMCLabels, "AOD", "UPCBMCLABEL", + upc_cand_prod_bar::McCollisionIds, + upc_cand_prod_bar::ParticleIds); +} // namespace o2::aod + +struct UpcCandProducerBarrel { + Produces selectedCandidates; + Produces selectedMCCollisions; + Produces selectedCandidateMCLabels; + HistogramRegistry registry{"registry", {}}; + Service ccdb{}; + + int cachedRunNumber = -1; + int64_t bcSOR = 0; + int32_t nBCsPerTF = 0; + int configuredOrbitsPerTF = -1; + int configuredTFStartBorder = -1; + int configuredTFEndBorder = -1; + int configuredITSROFStartBorder = -1; + int configuredITSROFEndBorder = -1; + int tfStartBorder = 0; + int tfEndBorder = 0; + int rofOffset = 0; + int rofLength = 0; + int nRofPerTF = 0; + int rofStartBorderMargin = 0; + int rofEndBorderMargin = 0; + std::bitset collidingBCs; + + Configurable nTracks{"nTracks", 2, "Required N tracks in selected collisions"}; + Configurable maxAbsEta{"maxAbsEta", 0.8f, "Maximum |eta| of selected tracks"}; + Configurable minPt{"minPt", 0.2f, "Minimum pT of selected tracks (GeV/c)"}; + Configurable vetoBCWindow{"vetoBCWindow", 0, "FIT veto half-window in BC; <0 disables"}; + Configurable vetoFT0{"vetoFT0", 1, "FT0 veto: 0=off, 1=on"}; + Configurable vetoFV0{"vetoFV0", 0, "FV0 veto: 0=off, 1=on"}; + Configurable vetoFDD{"vetoFDD", 0, "FDD veto: 0=off, 1=on"}; + Configurable useTCE{"useTCE", 0, "Use TCE for lumi accounting: 0=off, 1=on"}; + Configurable signalSourceId{"signalSourceId", 1, "MC source ID stored as embedded signal"}; + + Configurable minAmpFV0A{"minAmpFV0A", 150.f, "Minimum amplitude for FV0A veto"}; + Configurable minAmpFT0A{"minAmpFT0A", 150.f, "Minimum amplitude for FT0A veto"}; + Configurable minAmpFT0C{"minAmpFT0C", 50.f, "Minimum amplitude for FT0C veto"}; + Configurable minAmpFDDA{"minAmpFDDA", 1000.f, "Minimum amplitude for FDDA veto"}; + Configurable minAmpFDDC{"minAmpFDDC", 1000.f, "Minimum amplitude for FDDC veto"}; + + Configurable maxStoredDistance{"maxStoredDistance", 32, "Maximum stored veto distance"}; + Configurable nBinsOccupancy{"nBinsOccupancy", 25, "Number of occupancy bins"}; + Configurable nBinsTF{"nBinsTF", 100, "Number of TF bins"}; + + using CollisionsWithSels = soa::Join; + using BCsWithSels = soa::Join; + using TracksWithPID = soa::Join; + using TracksWithPIDMC = soa::Join; + Preslice tracksPerCollision = aod::track::collisionId; + Preslice mcTracksPerCollision = aod::track::collisionId; + PresliceUnsorted tracksPerMCParticle = aod::mctracklabel::mcParticleId; + Preslice particlesPerMCCollision = aod::mcparticle::mcCollisionId; + + RCTFlagsChecker rctChecker{kFDDBad, kFT0Bad, kFV0Bad, kITSBad, kITSLimAccMCRepr, kTPCBadTracking, kTPCLimAccMCRepr, kTPCBadPID, kTOFBad, kTOFLimAccMCRepr, kCcdbObjectLoaded}; + + float calcWeightForOccupancy(float dt) // copied from Common/Tools/EventSelectionModule.h + { + float wOccup = 0; + if (dt > -40 && dt < -5) { // o2-linter: disable=magic-number (collisions in the past) + wOccup = 1. / 1225 * (dt + 40) * (dt + 40); // o2-linter: disable=magic-number (parabolic weight for past collisions) + } else if (dt >= -5 && dt < 15) { // o2-linter: disable=magic-number (collisions near a given one) + wOccup = 1; + } else if (dt >= 15 && dt < 40) { // o2-linter: disable=magic-number (collisions from the future) + wOccup = -0.4 / 25 * dt + 1.24; // o2-linter: disable=magic-number (linear weight for future collisions) + } else if (dt >= 40 && dt < 100) { // o2-linter: disable=magic-number (collisions from the distant future) + wOccup = -0.4 / 60 * dt + 0.6 + 0.8 / 3; // o2-linter: disable=magic-number (linear weight for distant future collisions) + } + return wOccup; + } + + bool noRofBorder(int64_t bc) + { + int bcInRof = (bc + rofLength - rofOffset) % rofLength; + return bcInRof > rofStartBorderMargin && bcInRof < rofLength - rofEndBorderMargin; + } + + int getRofInTF(int64_t bc) + { + return (bc + rofLength - rofOffset) / rofLength % nRofPerTF; + } + + void init(InitContext& initContext) + { + ccdb->setLocalObjectValidityChecking(); + auto inheritEventSelectionOption = [&](char const* name, int& value) { + if (!o2::common::core::getTaskOptionValue(initContext, "eventselection-run3", name, value, false)) { + LOGF(fatal, "Could not inherit option %s from eventselection-run3", name); + } + }; + inheritEventSelectionOption("bcselOpts.NumberOfOrbitsPerTF", configuredOrbitsPerTF); + inheritEventSelectionOption("bcselOpts.TimeFrameStartBorderMargin", configuredTFStartBorder); + inheritEventSelectionOption("bcselOpts.TimeFrameEndBorderMargin", configuredTFEndBorder); + inheritEventSelectionOption("bcselOpts.ITSROFrameStartBorderMargin", configuredITSROFStartBorder); + inheritEventSelectionOption("bcselOpts.ITSROFrameEndBorderMargin", configuredITSROFEndBorder); + + const AxisSpec axisBCs{NBCsPerOrbit, 0., NBCsPerOrbit, "BC in orbit"}; + const AxisSpec axisVeto{maxStoredDistance + 1, -0.5, maxStoredDistance + 0.5, "veto range in BCs"}; + const AxisSpec axisOccupancy{nBinsOccupancy, 0., 250000., "FT0-based occupancy"}; + const AxisSpec axisTFs{nBinsTF, 0., 4.e7, "TFs"}; // 100 bins -> 400000 TFs per bin (1 bin ~ 1140 seconds for 32 orbits per TF) + + registry.add("hTF", "TF counts", HistType::kTH1D, {axisTFs}); + registry.add("hTVX", "TVX counts", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCT", "TVX counts after RCT selection", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTnoPileupROF", "TVX counts after RCT and ROF pileup selection", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXupc", "TVX counts in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTupc", "TVX counts after RCT selection in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + registry.add("hTVXRCTnoPileupROFupc", "TVX counts after RCT and ROF pileup selection in upc ROFs", HistType::kTH2D, {axisBCs, axisTFs}); + + registry.add("hVetoT00", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVD", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCT", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + + registry.add("hOccupancyT00RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTV0RCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTVDRCTnoPileupROF", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + + registry.add("hVetoT00upc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0upc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDupc", "no RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTupc", "after RCT selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoT00RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTV0RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + registry.add("hVetoTVDRCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisVeto}); + + registry.add("hOccupancyT00RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTV0RCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + registry.add("hOccupancyTVDRCTnoPileupROFupc", "after RCT and ROF pileup selection", HistType::kTH3D, {axisBCs, axisTFs, axisOccupancy}); + + LOGF(info, "Veto config: window=%d FT0=%d FV0=%d FDD=%d", vetoBCWindow.value, vetoFT0.value, vetoFV0.value, vetoFDD.value); + } + + void updateRunInfo(int runNumber) + { + if (runNumber == cachedRunNumber) { + return; + } + const auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), runNumber); + const int64_t orbitsPerTF = configuredOrbitsPerTF < 0 ? runInfo.orbitsPerTF : configuredOrbitsPerTF; + const bool needsEventSelectionParams = configuredTFStartBorder < 0 || configuredTFEndBorder < 0; + const auto ts = (runInfo.sor + runInfo.eor) / 2; + const auto* eventSelectionParams = needsEventSelectionParams ? ccdb->getForTimeStamp("EventSelection/EventSelectionParams", ts) : nullptr; + if (orbitsPerTF <= 0 || !runInfo.grpLHC || (needsEventSelectionParams && !eventSelectionParams)) { + LOGF(fatal, "Incomplete run information for run %d", runNumber); + } + bcSOR = runInfo.orbitSOR * NBCsPerOrbit; + nBCsPerTF = orbitsPerTF * NBCsPerOrbit; + tfStartBorder = configuredTFStartBorder < 0 ? eventSelectionParams->fTimeFrameStartBorderMargin : configuredTFStartBorder; + tfEndBorder = configuredTFEndBorder < 0 ? eventSelectionParams->fTimeFrameEndBorderMargin : configuredTFEndBorder; + collidingBCs = runInfo.grpLHC->getBunchFilling().getBCPattern(); + + // get ITS ROF parameters + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", ts); + rofOffset = alppar->roFrameBiasInBC; + rofLength = alppar->roFrameLengthInBC; + nRofPerTF = nBCsPerTF / rofLength; + cachedRunNumber = runNumber; + } + + template + void processImpl(CollisionsT const& collisions, TracksT const& tracks, PresliceT const& tracksPerColl, + BCsWithSels const& bcs, aod::FT0s const& ft0s, aod::FV0As const& fv0s, + aod::FDDs const& fdds, aod::McCollisions const* mcCollisions = nullptr, + aod::McParticles const* mcParticles = nullptr, + aod::AmbiguousTracks const* ambiguousTracks = nullptr) + { + updateRunInfo(bcs.begin().runNumber()); + std::vector tfIDs; + std::vector tfPassesRCT; + std::vector localTFIndex; + localTFIndex.reserve(bcs.size()); + // Cache the local TF slot for each BC row for collision and MC lookups + auto hTF = registry.get(HIST("hTF")); + for (const auto& bc : bcs) { + const int64_t globalBC = bc.globalBC(); + const uint32_t tfID = (globalBC - bcSOR) / nBCsPerTF; + if (tfIDs.empty() || tfID != tfIDs.back()) { + hTF->Fill(tfID); + tfIDs.push_back(tfID); + tfPassesRCT.push_back(static_cast(rctChecker(bc))); + } + localTFIndex.push_back(tfIDs.size() - 1); + } + LOGP(info, "Starting new DF. Number of TFs = {}", tfIDs.size()); + + std::unordered_map signalParticleRefs; + if constexpr (isMC) { + std::unordered_set ambiguousTrackIds; + ambiguousTrackIds.reserve(ambiguousTracks->size()); + for (const auto& ambiguousTrack : *ambiguousTracks) { + ambiguousTrackIds.insert(ambiguousTrack.trackId()); + } + + std::vector bcsWithMCCollision; + bcsWithMCCollision.reserve(mcCollisions->size()); + // Use collisions from every source and retain same-BC pile-up + for (const auto& mcCollision : *mcCollisions) { + bcsWithMCCollision.push_back(static_cast(mcCollision.template bc_as().globalBC())); + } + const auto minDistanceMC = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithMCCollision, maxStoredDistance, true); + + for (const auto& mcCollision : *mcCollisions) { + const auto bc = mcCollision.template bc_as(); + const auto globalBC = bc.globalBC(); + const auto iTF = localTFIndex[bc.globalIndex()]; + const auto bcInTF = (static_cast(globalBC) - bcSOR) % nBCsPerTF; + const auto particles = mcParticles->sliceBy(particlesPerMCCollision, mcCollision.globalIndex()); + const bool isSignal = mcCollision.getSourceId() == signalSourceId.value; + + std::vector px, py, pz; + std::vector pdg, recoCollisionIds; + std::vector recoFlags; + int32_t mult = 0; + // Output rows follow input MC collisions, making this the compact collision label + const auto newMCCollisionId = static_cast(selectedMCCollisions.lastIndex() + 1); + for (const auto& particle : particles) { + mult += particle.isPhysicalPrimary(); + // Background rows keep only collision metadata and physical-primary multiplicity + if (!isSignal) { + continue; + } + const auto newMCParticleId = static_cast(pdg.size()); + px.push_back(particle.px()); + py.push_back(particle.py()); + pz.push_back(particle.pz()); + pdg.push_back(particle.pdgCode()); + + uint8_t flags = particle.isPhysicalPrimary() ? kPhysicalPrimary : kNoFlags; + int32_t recoCollisionId = NoRecoCollision; + int nRecoTracks = 0; + // Inspect only reconstructed tracks matched to this signal particle + for (const auto& track : tracks.sliceBy(tracksPerMCParticle, particle.globalIndex())) { + if ((track.mcMask() & MCTrackFakeMask) != 0u) { + continue; + } + ++nRecoTracks; + flags |= kHasRecoTrack; + flags |= track.isPVContributor() ? kHasPVContributor : kNoFlags; + flags |= ambiguousTrackIds.contains(track.globalIndex()) ? kHasAmbiguousTrack : kNoFlags; + if (track.has_collision()) { + recoCollisionId = track.collisionId(); + flags |= collisions.iteratorAt(recoCollisionId).numContrib() > nTracks ? kHighMultiplicityCollision : kNoFlags; + } + } + // A single collision label is meaningful only for exactly one reconstructed track + if (nRecoTracks > 1) { + recoCollisionId = MultipleRecoTracks; + } + recoFlags.push_back(flags); + recoCollisionIds.push_back(recoCollisionId); + // Remap original particle IDs to output index for candidate labels + signalParticleRefs.emplace(particle.globalIndex(), SignalRef{newMCCollisionId, newMCParticleId}); + } + selectedMCCollisions(tfIDs[iTF], globalBC, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + px, py, pz, pdg, mult, minDistanceMC[iTF][bcInTF], + recoFlags, recoCollisionIds, isSignal); + } + } + + // collect number of all collisions and upc-mode collisions per ROF + LOGP(debug, "Collect number of all collisions and upc-mode collisions per ROF"); + std::vector> vMultTracks(tfIDs.size(), std::map()); + std::vector> vCollisionsPerROF(tfIDs.size(), std::vector(nRofPerTF, 0)); + std::vector> vUpcModePerROF(tfIDs.size(), std::vector(nRofPerTF, 0)); + for (const auto& collision : collisions) { + auto bc = collision.template bc_as(); + const int64_t globalBC = bc.globalBC(); + const int32_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const auto iTF = localTFIndex[bc.globalIndex()]; + const int32_t rofInTF = getRofInTF(bcInTF); + vCollisionsPerROF[iTF][rofInTF]++; + const bool upcMode = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) != 0; + if (upcMode) { + vUpcModePerROF[iTF][rofInTF]++; + } + } + + auto hTVX = registry.get(HIST("hTVX")); + auto hTVXRCT = registry.get(HIST("hTVXRCT")); + auto hTVXRCTnoPileupROF = registry.get(HIST("hTVXRCTnoPileupROF")); + + auto hTVXupc = registry.get(HIST("hTVXupc")); + auto hTVXRCTupc = registry.get(HIST("hTVXRCTupc")); + auto hTVXRCTnoPileupROFupc = registry.get(HIST("hTVXRCTnoPileupROFupc")); + + std::vector bcsWithFT0, bcsWithFV0, bcsWithFDD; + bcsWithFT0.reserve(ft0s.size()); + bcsWithFV0.reserve(fv0s.size()); + bcsWithFDD.reserve(fdds.size()); + + LOGP(debug, "Starting lumi accounting"); + // lumi accounting + std::vector> vMultFT0C(tfIDs.size(), std::map()); + for (const auto& ft0 : ft0s) { + const auto bc = ft0.bc_as(); + const auto globalBC = bc.globalBC(); + const auto bcInOrbit = globalBC % NBCsPerOrbit; + const int32_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const auto iTF = localTFIndex[bc.globalIndex()]; + const auto tfID = tfIDs[iTF]; + const int32_t rofInTF = getRofInTF(bcInTF); + bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] > 1; + bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; + + // Multiplicity for FT0C-based occupancy estimates + if (ft0.timeC() < MaxFITTime && ft0.sumAmpC() >= minAmpFT0C) { + vMultFT0C[iTF][bcInTF] = ft0.sumAmpC(); + } + + if (!collidingBCs[bcInOrbit] || !TESTBIT(ft0.triggerMask(), o2::fit::Triggers::bitVertex)) { + continue; + } + + if (useTCE && !TESTBIT(ft0.triggerMask(), o2::fit::Triggers::bitCen)) { + continue; + } + + hTVX->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXupc->Fill(bcInOrbit, tfID); + } + + if (!bc.selection_bit(aod::evsel::kNoTimeFrameBorder) || !rctChecker(bc)) { + continue; + } + + hTVXRCT->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXRCTupc->Fill(bcInOrbit, tfID); + } + + if (pileupInROF) { + continue; + } + + hTVXRCTnoPileupROF->Fill(bcInOrbit, tfID); + if (upcMode) { + hTVXRCTnoPileupROFupc->Fill(bcInOrbit, tfID); + } + } + + LOGP(debug, "Filling FT0 activity arrays"); + for (const auto& ft0 : ft0s) { + const float sumAmpFT0A = ft0.sumAmpA(); + const float sumAmpFT0C = ft0.sumAmpC(); + if ((ft0.timeA() < MaxFITTime && sumAmpFT0A >= minAmpFT0A) || (ft0.timeC() < MaxFITTime && sumAmpFT0C >= minAmpFT0C)) { + bcsWithFT0.push_back(ft0.bc_as().globalBC()); + } + } + + LOGP(debug, "Filling FV0 activity arrays"); + for (const auto& fv0 : fv0s) { + const float sumAmpFV0A = std::accumulate(fv0.amplitude().begin(), fv0.amplitude().end(), 0.f); + if (fv0.time() < MaxFITTime && sumAmpFV0A >= minAmpFV0A) { + bcsWithFV0.push_back(fv0.bc_as().globalBC()); + } + } + + LOGP(debug, "Filling FDD activity arrays"); + for (const auto& fdd : fdds) { + const float sumAmpFDDA = std::accumulate(std::begin(fdd.chargeA()), std::end(fdd.chargeA()), 0.f); + const float sumAmpFDDC = std::accumulate(std::begin(fdd.chargeC()), std::end(fdd.chargeC()), 0.f); + if ((fdd.timeA() < MaxFITTime && sumAmpFDDA >= minAmpFDDA) || (fdd.timeC() < MaxFITTime && sumAmpFDDC >= minAmpFDDC)) { + bcsWithFDD.push_back(fdd.bc_as().globalBC()); + } + } + + LOGP(debug, "Starting occupancy calculation"); + std::vector> vOccupancyFT0C(tfIDs.size(), std::vector(nBCsPerTF, 0.f)); + int deltaBcPast = 1600; + int deltaBcFuture = 4000; + // TODO: calculate weights only once per task + std::vector weights(deltaBcPast + deltaBcFuture + 1); + for (int d = -deltaBcPast; d <= deltaBcFuture; ++d) { + weights[d + deltaBcPast] = calcWeightForOccupancy(d * o2::constants::lhc::LHCBunchSpacingMUS); + } + + for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { + for (const auto& [bc, multFT0C] : vMultFT0C[iTF]) { + LOGP(debug, "iTF={} bc={} mult={}", iTF, bc, multFT0C); + const int bcInTFMin = std::max(bc - deltaBcFuture, 0); + const int bcInTFMax = std::min(bc + deltaBcPast, nBCsPerTF - 1); + for (int bcInTF = bcInTFMin; bcInTF <= bcInTFMax; ++bcInTF) { + vOccupancyFT0C[iTF][bcInTF] += weights[bc - bcInTF + deltaBcPast] * multFT0C; + } + } + } + + LOGP(debug, "Building minimum distance maps"); + const uint32_t scanDistance = std::max(maxStoredDistance.value, vetoBCWindow.value); + const DistanceMap minDistanceFT0 = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFT0, scanDistance); + const DistanceMap minDistanceFV0 = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFV0, scanDistance); + const DistanceMap minDistanceFDD = buildMinimumDistanceMap(tfIDs, bcSOR, nBCsPerTF, bcsWithFDD, scanDistance); + + auto hVetoT00 = registry.get(HIST("hVetoT00")); + auto hVetoTV0 = registry.get(HIST("hVetoTV0")); + auto hVetoTVD = registry.get(HIST("hVetoTVD")); + auto hVetoT00RCT = registry.get(HIST("hVetoT00RCT")); + auto hVetoTV0RCT = registry.get(HIST("hVetoTV0RCT")); + auto hVetoTVDRCT = registry.get(HIST("hVetoTVDRCT")); + auto hVetoT00RCTnoPileupROF = registry.get(HIST("hVetoT00RCTnoPileupROF")); + auto hVetoTV0RCTnoPileupROF = registry.get(HIST("hVetoTV0RCTnoPileupROF")); + auto hVetoTVDRCTnoPileupROF = registry.get(HIST("hVetoTVDRCTnoPileupROF")); + + auto hVetoT00upc = registry.get(HIST("hVetoT00upc")); + auto hVetoTV0upc = registry.get(HIST("hVetoTV0upc")); + auto hVetoTVDupc = registry.get(HIST("hVetoTVDupc")); + auto hVetoT00RCTupc = registry.get(HIST("hVetoT00RCTupc")); + auto hVetoTV0RCTupc = registry.get(HIST("hVetoTV0RCTupc")); + auto hVetoTVDRCTupc = registry.get(HIST("hVetoTVDRCTupc")); + auto hVetoT00RCTnoPileupROFupc = registry.get(HIST("hVetoT00RCTnoPileupROFupc")); + auto hVetoTV0RCTnoPileupROFupc = registry.get(HIST("hVetoTV0RCTnoPileupROFupc")); + auto hVetoTVDRCTnoPileupROFupc = registry.get(HIST("hVetoTVDRCTnoPileupROFupc")); + + auto hOccupancyT00RCTnoPileupROF = registry.get(HIST("hOccupancyT00RCTnoPileupROF")); + auto hOccupancyTV0RCTnoPileupROF = registry.get(HIST("hOccupancyTV0RCTnoPileupROF")); + auto hOccupancyTVDRCTnoPileupROF = registry.get(HIST("hOccupancyTVDRCTnoPileupROF")); + + auto hOccupancyT00RCTnoPileupROFupc = registry.get(HIST("hOccupancyT00RCTnoPileupROFupc")); + auto hOccupancyTV0RCTnoPileupROFupc = registry.get(HIST("hOccupancyTV0RCTnoPileupROFupc")); + auto hOccupancyTVDRCTnoPileupROFupc = registry.get(HIST("hOccupancyTVDRCTnoPileupROFupc")); + + LOGP(debug, "Filling veto efficiency histograms"); + for (size_t iTF = 0; iTF < tfIDs.size(); ++iTF) { + const int64_t tfStartBC = bcSOR + static_cast(tfIDs[iTF]) * nBCsPerTF; + const auto tfID = tfIDs[iTF]; + for (int64_t bcInTF = tfStartBorder + 1; bcInTF < nBCsPerTF - tfEndBorder; ++bcInTF) { // TODO why int64_t? + const int64_t globalBC = tfStartBC + bcInTF; + const int bcInOrbit = globalBC % NBCsPerOrbit; + const int32_t rofInTF = getRofInTF(bcInTF); + bool upcMode = vCollisionsPerROF[iTF][rofInTF] == 0 || vUpcModePerROF[iTF][rofInTF] > 0; + bool pileupInROF = vCollisionsPerROF[iTF][rofInTF] > 1; + const auto distanceFT0 = minDistanceFT0[iTF][bcInTF]; + const auto distanceFV0 = minDistanceFV0[iTF][bcInTF]; + const auto distanceFDD = minDistanceFDD[iTF][bcInTF]; + + fillVetoHistograms(hVetoT00, hVetoTV0, hVetoTVD, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00upc, hVetoTV0upc, hVetoTVDupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (tfPassesRCT[iTF] == 0u) { + continue; + } + + fillVetoHistograms(hVetoT00RCT, hVetoTV0RCT, hVetoTVDRCT, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00RCTupc, hVetoTV0RCTupc, hVetoTVDRCTupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (pileupInROF) { + continue; + } + + fillVetoHistograms(hVetoT00RCTnoPileupROF, hVetoTV0RCTnoPileupROF, hVetoTVDRCTnoPileupROF, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + if (upcMode) { + fillVetoHistograms(hVetoT00RCTnoPileupROFupc, hVetoTV0RCTnoPileupROFupc, hVetoTVDRCTnoPileupROFupc, bcInOrbit, tfID, distanceFT0, distanceFV0, distanceFDD); + } + + if (distanceFT0 == 0) { + continue; + } + float occupancy = vOccupancyFT0C[iTF][bcInTF]; + hOccupancyT00RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyT00RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); + } + + if (distanceFV0 == 0) { + continue; + } + hOccupancyTV0RCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyTV0RCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); + } + + if (distanceFDD == 0) { + continue; + } + hOccupancyTVDRCTnoPileupROF->Fill(bcInOrbit, tfID, occupancy); + if (upcMode) { + hOccupancyTVDRCTnoPileupROFupc->Fill(bcInOrbit, tfID, occupancy); + } + } + } + + LOGP(debug, "Filling upc trees"); + for (const auto& collision : collisions) { + if (collision.numContrib() != nTracks || !collision.selection_bit(aod::evsel::kNoTimeFrameBorder) || !rctChecker(collision)) { + continue; + } + auto bc = collision.template bc_as(); + int64_t globalBC = bc.globalBC(); + const auto iTF = localTFIndex[bc.globalIndex()]; + const int64_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + const int32_t rofInTF = getRofInTF(bcInTF); + const bool upcMode = (collision.flags() & dataformats::Vertex>::Flags::UPCMode) != 0; + const bool noPileupInRof = vCollisionsPerROF[iTF][rofInTF] <= 1; + uint8_t flags = 0; + flags |= upcMode << 0; + flags |= noPileupInRof << 1; + const auto occupancyFT0C = vOccupancyFT0C[iTF][bcInTF]; + const auto distFT0 = minDistanceFT0[iTF][bcInTF]; + const auto distFV0 = minDistanceFV0[iTF][bcInTF]; + const auto distFDD = minDistanceFDD[iTF][bcInTF]; + const auto vetoWindow = static_cast(vetoBCWindow.value); + if (vetoBCWindow.value >= 0 && (((vetoFT0.value != 0) && distFT0 <= vetoWindow) || ((vetoFV0.value != 0) && distFV0 <= vetoWindow) || ((vetoFDD.value != 0) && distFDD <= vetoWindow))) { + continue; + } + std::vector px, py, pz; + std::vector tpcSignal, tpcNSigmaEl, tpcNSigmaPi, tpcNSigmaKa, tpcNSigmaPr; + std::vector tofNSigmaEl, tofNSigmaPi, tofNSigmaKa, tofNSigmaPr; + std::vector itsClusterMap; + std::vector nClusters; + std::vector sign; + std::vector newMCCollisionIds; + std::vector newParticleIds; + for (const auto& track : tracks.sliceBy(tracksPerColl, collision.globalIndex())) { + if (!track.isPVContributor() || !track.hasITS() || !track.hasTPC() || std::abs(track.eta()) > maxAbsEta.value || track.pt() < minPt.value) { + continue; + } + px.push_back(track.px()); + py.push_back(track.py()); + pz.push_back(track.pz()); + tpcSignal.push_back(track.tpcSignal()); + tpcNSigmaEl.push_back(track.tpcNSigmaEl()); + tpcNSigmaPi.push_back(track.tpcNSigmaPi()); + tpcNSigmaKa.push_back(track.tpcNSigmaKa()); + tpcNSigmaPr.push_back(track.tpcNSigmaPr()); + tofNSigmaEl.push_back(track.tofNSigmaEl()); + tofNSigmaPi.push_back(track.tofNSigmaPi()); + tofNSigmaKa.push_back(track.tofNSigmaKa()); + tofNSigmaPr.push_back(track.tofNSigmaPr()); + itsClusterMap.push_back(track.itsClusterMap()); + nClusters.push_back(track.tpcNClsFound()); + sign.push_back(track.sign()); + if constexpr (isMC) { + // Fake, background, and unmatched particles keep the -1 label + SignalRef label{NoSignalMCRef, NoSignalMCRef}; + if (track.has_mcParticle() && (track.mcMask() & MCTrackFakeMask) == 0u) { + if (const auto ref = signalParticleRefs.find(track.mcParticleId()); ref != signalParticleRefs.end()) { + label = ref->second; + } + } + newMCCollisionIds.push_back(label.first); + newParticleIds.push_back(label.second); + } + } + if (px.size() != static_cast(nTracks)) { + continue; + } + selectedCandidates(bc.runNumber(), globalBC, tfIDs[iTF], bc.timestamp(), flags, occupancyFT0C, + collision.posX(), collision.posY(), collision.posZ(), + px, py, pz, tpcSignal, tpcNSigmaEl, tpcNSigmaPi, tpcNSigmaKa, tpcNSigmaPr, + tofNSigmaEl, tofNSigmaPi, tofNSigmaKa, tofNSigmaPr, itsClusterMap, nClusters, sign, + std::min(distFT0, maxStoredDistance + 1), + std::min(distFV0, maxStoredDistance + 1), + std::min(distFDD, maxStoredDistance + 1)); + if constexpr (isMC) { + selectedCandidateMCLabels(newMCCollisionIds, newParticleIds); + } + } + } + + void processData(CollisionsWithSels const& collisions, TracksWithPID const& tracks, BCsWithSels const& bcs, + aod::FT0s const& ft0s, aod::FV0As const& fv0s, aod::FDDs const& fdds) + { + processImpl(collisions, tracks, tracksPerCollision, bcs, ft0s, fv0s, fdds); + } + PROCESS_SWITCH(UpcCandProducerBarrel, processData, "Process reconstructed data", true); + + void processMC(CollisionsWithSels const& collisions, TracksWithPIDMC const& tracks, BCsWithSels const& bcs, + aod::FT0s const& ft0s, aod::FV0As const& fv0s, aod::FDDs const& fdds, + aod::McCollisions const& mcCollisions, aod::McParticles const& mcParticles, + aod::AmbiguousTracks const& ambiguousTracks) + { + processImpl(collisions, tracks, mcTracksPerCollision, bcs, ft0s, fv0s, fdds, + &mcCollisions, &mcParticles, &ambiguousTracks); + } + PROCESS_SWITCH(UpcCandProducerBarrel, processMC, "Process reconstructed MC and write compact MC truth", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& context) { return WorkflowSpec{adaptAnalysisTask(context)}; } diff --git a/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx b/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx index e2df219a838..cdd76286241 100644 --- a/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx +++ b/PWGUD/TableProducer/upcCandProducerGlobalMuon.cxx @@ -18,6 +18,8 @@ #include "PWGUD/Core/UPCHelpers.h" #include "PWGUD/DataModel/UDTables.h" +#include "Common/Core/fwdtrackUtilities.h" + #include #include #include @@ -55,6 +57,8 @@ #include #include #include +#include +#include #include using namespace o2::framework; @@ -64,6 +68,7 @@ struct UpcCandProducerGlobalMuon { bool fDoMC{false}; std::map fNewPartIDs; + std::map fBestMuonMatch; Produces udMCCollisions; Produces udMCParticles; @@ -85,7 +90,7 @@ struct UpcCandProducerGlobalMuon { Configurable fBcWindowMCH{"fBcWindowMCH", 20, "Time window for MCH-MID to MCH-only matching for Muon candidates"}; Configurable fMaxFV0Amp{"fMaxFV0Amp", 100.f, "Max FV0 amplitude in the same BC"}; - // NEW: MFT/Global track support configurables + // MFT/Global track support configurables Configurable fEnableMFT{"fEnableMFT", true, "Enable MFT/global track processing"}; Configurable fSaveMFTClusters{"fSaveMFTClusters", true, "Save MFT cluster information"}; @@ -97,6 +102,7 @@ struct UpcCandProducerGlobalMuon { Configurable fUseAbsDCA{"fUseAbsDCA", true, "Use absolute DCA in FwdDCAFitter"}; Configurable fMaxChi2MatchMCHMFT{"fMaxChi2MatchMCHMFT", 4.f, "Maximum chi2 for MCH-MFT matching quality filter"}; Configurable fBcWindowMCHMFT{"fBcWindowMCHMFT", 20, "BC window for searching MCH-MFT tracks around MCH-MID-MFT anchors"}; + Configurable fKeepBestMuonMatch{"fKeepBestMuonMatch", true, "Keep only the best MCH-MFT match per MCH track (lowest chi2)"}; using ForwardTracks = o2::soa::Join; @@ -111,13 +117,14 @@ struct UpcCandProducerGlobalMuon { o2::vertexing::FwdDCAFitterN<2> fFwdFitter; // Named constants (avoid magic numbers in expressions) - static constexpr double kBcTimeRoundingOffset = 1.; // Offset used when rounding trackTime to BC units - static constexpr uint16_t kMinTracksForPair = 2; // Minimum tracks required to compute a pair invariant mass - static constexpr uint16_t kMinTracksForCandidate = 1; // Minimum contributors required to save a candidate + static constexpr double kBcTimeRoundingOffset = 1.; // Offset used when rounding trackTime to BC units + static constexpr uint16_t kMinTracksForPair = 2; // Minimum tracks required to compute a pair invariant mass + static constexpr uint16_t kMinTracksForCandidate = 1; // Minimum contributors required to save a candidate + static constexpr int kUpperBoundaryToTrackTypeEnum = 2; // Make sure you use MFT tracks void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts() || fEnableMFT) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -136,10 +143,11 @@ struct UpcCandProducerGlobalMuon { // NEW: Add histograms for global track monitoring const AxisSpec axisTrackType{5, -0.5, 4.5, "Track Type"}; histRegistry.add("hTrackTypes", "Track type distribution", kTH1F, {axisTrackType}); - histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(1, "MuonStandalone"); - histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(2, "MCHStandalone"); - histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(3, "GlobalMuon"); - histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(4, "GlobalFwd"); + histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(1, "GlobalMuonTrack"); + histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(2, "GlobalMuonTrackOtherMatch"); + histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(3, "GlobalForwardTrack"); + histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(4, "MuonStandaloneTrack"); + histRegistry.get(HIST("hTrackTypes"))->GetXaxis()->SetBinLabel(5, "MCHStandaloneTrack"); const AxisSpec axisEta{100, -4.0, -2.0, "#eta"}; histRegistry.add("hEtaGlobal", "Global track eta", kTH1F, {axisEta}); @@ -361,6 +369,7 @@ struct UpcCandProducerGlobalMuon { scrollBackForth(inGbc, maxDbc, mapBcs, fillAmps); } + // Propagate MCH track to z = 0 auto propagateToZero(ForwardTracks::iterator const& muon) { using SMatrix55 = ROOT::Math::SMatrix>; @@ -384,13 +393,54 @@ struct UpcCandProducerGlobalMuon { return propmuon; } - bool addToFwdTable(int64_t candId, int64_t trackId, uint64_t gbc, float trackTime, ForwardTracks const& fwdTracks, const o2::aod::McFwdTrackLabels* mcFwdTrackLabels) + // Propagate MCH track to target z point (set MFT z) + auto propagateMCHtoZ(ForwardTracks::iterator const& muon, double targetZ) + { + using SMatrix55 = ROOT::Math::SMatrix>; + using SMatrix5 = ROOT::Math::SVector; + SMatrix5 tpars(muon.x(), muon.y(), muon.phi(), muon.tgl(), muon.signed1Pt()); + std::vector v1{muon.cXX(), muon.cXY(), muon.cYY(), muon.cPhiX(), muon.cPhiY(), + muon.cPhiPhi(), muon.cTglX(), muon.cTglY(), muon.cTglPhi(), muon.cTglTgl(), + muon.c1PtX(), muon.c1PtY(), muon.c1PtPhi(), muon.c1PtTgl(), muon.c1Pt21Pt2()}; + SMatrix55 tcovs(v1.begin(), v1.end()); + o2::dataformats::GlobalFwdTrack propmuon; + o2::dataformats::GlobalFwdTrack track; + track.setParameters(tpars); + track.setZ(muon.z()); + track.setCovariances(tcovs); + auto mchTrack = fMatching.FwdtoMCH(track); + // Here is different point with propagateToZero + o2::mch::TrackExtrap::extrapToVertex(mchTrack, 0., 0., targetZ, 0., 0.); + auto proptrack = fMatching.MCHtoFwd(mchTrack); + o2::dataformats::GlobalFwdTrack propMuonAtZ; + propMuonAtZ.setParameters(proptrack.getParameters()); + propMuonAtZ.setZ(proptrack.getZ()); + propMuonAtZ.setCovariances(proptrack.getCovariances()); + return propMuonAtZ; + } + + // Propagate Global track to z = 0 + auto propagateGlobalFwdToZero(const o2::dataformats::GlobalFwdTrack& globalTrack) + { + auto mchTrack = fMatching.FwdtoMCH(globalTrack); + o2::mch::TrackExtrap::extrapToZCov(mchTrack, 0.); + auto proptrack = fMatching.MCHtoFwd(mchTrack); + o2::dataformats::GlobalFwdTrack propMuonAtZero; + propMuonAtZero.setParameters(proptrack.getParameters()); + propMuonAtZero.setZ(proptrack.getZ()); + propMuonAtZero.setCovariances(proptrack.getCovariances()); + return propMuonAtZero; + } + + bool addToFwdTable(int64_t candId, int64_t trackId, uint64_t gbc, float trackTime, + ForwardTracks const& fwdTracks, o2::aod::MFTTracks const& /*mftTracks*/, + const o2::aod::McFwdTrackLabels* mcFwdTrackLabels) { const auto& track = fwdTracks.iteratorAt(trackId); float px, py, pz; int sign; - // NEW: Fill track type histogram + // Fill track type histogram histRegistry.fill(HIST("hTrackTypes"), track.trackType()); if (track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack || track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalForwardTrack) { @@ -402,12 +452,21 @@ struct UpcCandProducerGlobalMuon { track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalForwardTrack; o2::dataformats::GlobalFwdTrack pft; if (isGlobal && fEnableMFT) { - // For global tracks, use raw kinematics for cuts; - // the FwdDCAFitter handles proper vertex propagation - auto trackPar = fwdToTrackPar(track); - pft.setParameters(trackPar.getParameters()); - pft.setZ(trackPar.getZ()); - pft.setCovariances(trackPar.getCovariances()); + // Refit global muon: propagate MCH component to vertex, combine with MFT spatial info + auto mchTrack = track.matchMCHTrack_as(); + // Propgate to Z of MFT -> z =0 + auto mfttrack = track.matchMFTTrack_as(); + double zMFT = mfttrack.z(); + + auto propMuonAtMFT = propagateMCHtoZ(mchTrack, zMFT); + using SMatrix5 = ROOT::Math::SVector; + using SMatrix55 = ROOT::Math::SMatrix>; + SMatrix5 tpars(mfttrack.x(), mfttrack.y(), mfttrack.phi(), mfttrack.tgl(), mfttrack.signed1Pt()); + SMatrix55 tcovs{}; + o2::track::TrackParCovFwd mft{mfttrack.z(), tpars, tcovs, mfttrack.chi2()}; + auto refitAtMFT = o2::aod::fwdtrackutils::refitGlobalMuonCov(propMuonAtMFT, mft); + // To z = 0 + pft = propagateGlobalFwdToZero(refitAtMFT); } else { pft = propagateToZero(track); } @@ -495,7 +554,33 @@ struct UpcCandProducerGlobalMuon { return o2::track::TrackParCovFwd{track.z(), tpars, tcovs, track.chi2()}; } + // Select the best MCH-MFT match per MCH track based on lowest chi2MatchMCHMFT. + // Multiple global tracks can share the same MCH track with different MFT matches; + // this function keeps only the best one to reduce combinatorial background. + void selectBestMuonMatches(ForwardTracks const& fwdTracks) + { + fBestMuonMatch.clear(); + std::unordered_map> mCandidates; + for (const auto& muon : fwdTracks) { + if (static_cast(muon.trackType()) < kUpperBoundaryToTrackTypeEnum) { + auto muonID = muon.matchMCHTrackId(); + auto chi2 = muon.chi2MatchMCHMFT(); + if (mCandidates.find(muonID) == mCandidates.end()) { + mCandidates[muonID] = {chi2, muon.globalIndex()}; + } else { + if (chi2 < mCandidates[muonID].first) { + mCandidates[muonID] = {chi2, muon.globalIndex()}; + } + } + } + } + for (const auto& pairCand : mCandidates) { + fBestMuonMatch[pairCand.second.second] = true; + } + } + void createCandidates(ForwardTracks const& fwdTracks, + o2::aod::MFTTracks const& mftTracks, o2::aod::FwdTrkCls const& fwdTrkCls, o2::aod::AmbiguousFwdTracks const& ambFwdTracks, o2::aod::BCs const& bcs, @@ -525,7 +610,7 @@ struct UpcCandProducerGlobalMuon { } // Initialize FwdDCAFitter with magnetic field if (fEnableMFT) { - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); static constexpr double kCenterMFT[3] = {0, 0, -61.4}; float bz = field->getBz(kCenterMFT); LOG(info) << "FwdDCAFitter magnetic field at MFT center: bZ = " << bz; @@ -580,6 +665,11 @@ struct UpcCandProducerGlobalMuon { vAmbFwdTrackIndexBCs[ambTr.globalIndex()] = ambTr.bcIds()[0]; } + // Select best MCH-MFT match per MCH track before sorting into BC maps + if (fKeepBestMuonMatch) { + selectBestMuonMatches(fwdTracks); + } + std::map> mapGlobalBcsWithMCHMIDTrackIds; std::map> mapGlobalBcsWithMCHTrackIds; std::map> mapGlobalBcsWithGlobalMuonTrackIds; // MCH-MID-MFT (good timing from MID) @@ -595,6 +685,13 @@ struct UpcCandProducerGlobalMuon { trackType != GlobalForwardTrack) continue; + // For global tracks, skip if not the best match for this MCH track + if (fKeepBestMuonMatch && static_cast(trackType) < kUpperBoundaryToTrackTypeEnum) { + if (fBestMuonMatch.find(fwdTrack.globalIndex()) == fBestMuonMatch.end()) { + continue; + } + } + auto trackId = fwdTrack.globalIndex(); int64_t indexBC = vAmbFwdTrackIndex[trackId] < 0 ? vColIndexBCs[fwdTrack.collisionId()] : vAmbFwdTrackIndexBCs[vAmbFwdTrackIndex[trackId]]; auto globalBC = vGlobalBCs[indexBC] + TMath::FloorNint(fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS + kBcTimeRoundingOffset); @@ -675,7 +772,7 @@ struct UpcCandProducerGlobalMuon { uint16_t numContrib = 0; double sumPx = 0., sumPy = 0., sumPz = 0., sumE = 0.; for (const auto& ianchor : vAnchorIds) { - if (!addToFwdTable(candId, ianchor, globalBcAnchor, 0., fwdTracks, mcFwdTrackLabels)) + if (!addToFwdTable(candId, ianchor, globalBcAnchor, 0., fwdTracks, mftTracks, mcFwdTrackLabels)) continue; const auto& trk = fwdTracks.iteratorAt(ianchor); double p2 = trk.px() * trk.px() + trk.py() * trk.py() + trk.pz() * trk.pz(); @@ -696,7 +793,7 @@ struct UpcCandProducerGlobalMuon { // Step 3: Write matched MCH-MFT tracks with adjusted track time for (const auto& [imchMft, gbc] : mapMchMftIdBc) { - if (!addToFwdTable(candId, imchMft, gbc, (gbc - globalBcAnchor) * o2::constants::lhc::LHCBunchSpacingNS, fwdTracks, mcFwdTrackLabels)) + if (!addToFwdTable(candId, imchMft, gbc, (gbc - globalBcAnchor) * o2::constants::lhc::LHCBunchSpacingNS, fwdTracks, mftTracks, mcFwdTrackLabels)) continue; const auto& trk = fwdTracks.iteratorAt(imchMft); double p2 = trk.px() * trk.px() + trk.py() * trk.py() + trk.pz() * trk.pz(); @@ -758,7 +855,7 @@ struct UpcCandProducerGlobalMuon { auto& vMuonIds = gbc_muids.second; // writing MCH-MID tracks for (const auto& imuon : vMuonIds) { - if (!addToFwdTable(candId, imuon, globalBcMid, 0., fwdTracks, mcFwdTrackLabels)) + if (!addToFwdTable(candId, imuon, globalBcMid, 0., fwdTracks, mftTracks, mcFwdTrackLabels)) continue; numContrib++; selTrackIds.push_back(imuon); @@ -769,7 +866,7 @@ struct UpcCandProducerGlobalMuon { getMchTrackIds(globalBcMid, mapGlobalBcsWithMCHTrackIds, fBcWindowMCH, mapMchIdBc); // writing MCH-only tracks for (const auto& [imch, gbc] : mapMchIdBc) { - if (!addToFwdTable(candId, imch, gbc, (gbc - globalBcMid) * o2::constants::lhc::LHCBunchSpacingNS, fwdTracks, mcFwdTrackLabels)) + if (!addToFwdTable(candId, imch, gbc, (gbc - globalBcMid) * o2::constants::lhc::LHCBunchSpacingNS, fwdTracks, mftTracks, mcFwdTrackLabels)) continue; numContrib++; selTrackIds.push_back(imch); @@ -816,6 +913,7 @@ struct UpcCandProducerGlobalMuon { } void processFwd(ForwardTracks const& fwdTracks, + o2::aod::MFTTracks const& mftTracks, o2::aod::FwdTrkCls const& fwdTrkCls, o2::aod::AmbiguousFwdTracks const& ambFwdTracks, o2::aod::BCs const& bcs, @@ -824,10 +922,11 @@ struct UpcCandProducerGlobalMuon { o2::aod::Zdcs const& zdcs) { fDoMC = false; - createCandidates(fwdTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, (o2::aod::McFwdTrackLabels*)nullptr); + createCandidates(fwdTracks, mftTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, nullptr); } void processFwdMC(ForwardTracks const& fwdTracks, + o2::aod::MFTTracks const& mftTracks, o2::aod::FwdTrkCls const& fwdTrkCls, o2::aod::AmbiguousFwdTracks const& ambFwdTracks, o2::aod::BCs const& bcs, @@ -840,7 +939,7 @@ struct UpcCandProducerGlobalMuon { { fDoMC = true; skimMCInfo(mcCollisions, mcParticles); - createCandidates(fwdTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, &mcFwdTrackLabels); + createCandidates(fwdTracks, mftTracks, fwdTrkCls, ambFwdTracks, bcs, collisions, fv0s, zdcs, &mcFwdTrackLabels); fNewPartIDs.clear(); } diff --git a/PWGUD/TableProducer/upcCandProducerMuon.cxx b/PWGUD/TableProducer/upcCandProducerMuon.cxx index 0ac8458d60f..b9f5edc7007 100644 --- a/PWGUD/TableProducer/upcCandProducerMuon.cxx +++ b/PWGUD/TableProducer/upcCandProducerMuon.cxx @@ -88,7 +88,7 @@ struct UpcCandProducerMuon { void init(InitContext&) { - fUpcCuts = (UPCCutparHolder)fUpcCutsConf; + fUpcCuts = UPCCutparHolder(fUpcCutsConf); if (fUpcCuts.getUseFwdCuts()) { fCCDB->setURL("http://alice-ccdb.cern.ch"); fCCDB->setCaching(true); @@ -520,7 +520,7 @@ struct UpcCandProducerMuon { o2::aod::Zdcs const& zdcs) { fDoMC = false; - createCandidates(fwdTracks, ambFwdTracks, bcs, collisions, fv0s, zdcs, (o2::aod::McFwdTrackLabels*)nullptr); + createCandidates(fwdTracks, ambFwdTracks, bcs, collisions, fv0s, zdcs, nullptr); } void processFwdMC(ForwardTracks const& fwdTracks, diff --git a/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx b/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx new file mode 100644 index 00000000000..d99c57f5dc9 --- /dev/null +++ b/PWGUD/TableProducer/upcCandProducerSemiFwd.cxx @@ -0,0 +1,686 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file upcCandProducerSemiFwd.cxx +/// \brief UPC candidate producer combining forward MCH-MID muons with central-barrel tracks. +/// Anchor: MCH-MID forward track (precise MID timing). Companions: barrel tracks +/// whose BC falls within fBcWindowBarrel of the anchor BC. One UD candidate per anchor BC. +/// \author Roman Lavička, lavicka.roman@gmail.com +/// \since 10.05.2026 + +#include "PWGUD/Core/UPCCutparHolder.h" +#include "PWGUD/Core/UPCHelpers.h" +#include "PWGUD/DataModel/UDTables.h" + +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct UpcCandProducerSemiFwd { + bool fDoMC{false}; + + std::map fNewPartIDs; + + Produces udMCCollisions; + Produces udMCParticles; + Produces udFwdTrackLabels; + Produces udTrackLabels; + + Produces udFwdTracks; + Produces udFwdTracksExtra; + Produces udFwdIndices; + + Produces udTracks; + Produces udTracksExtra; + Produces udTracksDCA; + Produces udTracksFlags; + Produces udTracksPID; + + Produces eventCandidates; + Produces eventCandidatesSelsFwd; + Produces udZdcsReduced; + + Configurable fSignalGenID{"fSignalGenID", 1, "Signal generator ID"}; + + UPCCutparHolder fUpcCuts = UPCCutparHolder(); + MutableConfigurable fUpcCutsConf{"fUpcCutsConf", {}, "UPC event cuts"}; + Configurable fBcWindowFITAmps{"fBcWindowFITAmps", 20, "BC range for T0A/V0A amplitudes array [-range, +(range-1)]"}; + Configurable fBcWindowBarrel{"fBcWindowBarrel", 20, "Time window for barrel-track to MCH-MID anchor matching"}; + Configurable fMaxFV0Amp{"fMaxFV0Amp", 100.f, "Max FV0 amplitude in the same BC"}; + + using ForwardTracks = o2::soa::Join; + + using BarrelTracks = o2::soa::Join; + + HistogramRegistry histRegistry{"HistRegistry", {}, OutputObjHandlingPolicy::AnalysisObject}; + + int fRun{0}; + Service fCCDB; + o2::ccdb::CcdbApi fCCDBApi; + o2::globaltracking::MatchGlobalFwd fMatching; + + void init(InitContext&) + { + fUpcCuts = UPCCutparHolder(fUpcCutsConf); + if (fUpcCuts.getUseFwdCuts()) { + fCCDB->setURL("http://alice-ccdb.cern.ch"); + fCCDB->setCaching(true); + fCCDB->setLocalObjectValidityChecking(); + fCCDBApi.init("http://alice-ccdb.cern.ch"); + } + const AxisSpec axisSelFwd{upchelpers::kNFwdSels, 0., static_cast(upchelpers::kNFwdSels), ""}; + histRegistry.add("MuonsSelCounter", "", kTH1F, {axisSelFwd}); + histRegistry.get(HIST("MuonsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kFwdSelAll + 1, "All"); + histRegistry.get(HIST("MuonsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kFwdSelPt + 1, "Pt"); + histRegistry.get(HIST("MuonsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kFwdSelEta + 1, "Eta"); + histRegistry.get(HIST("MuonsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kFwdSelRabs + 1, "Rabs"); + histRegistry.get(HIST("MuonsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kFwdSelpDCA + 1, "pDCA"); + histRegistry.get(HIST("MuonsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kFwdSelChi2 + 1, "Chi2"); + + const AxisSpec axisSelBar{upchelpers::kNBarrelSels, 0., static_cast(upchelpers::kNBarrelSels), ""}; + histRegistry.add("BarrelsSelCounter", "", kTH1F, {axisSelBar}); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelAll + 1, "All"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelHasTOF + 1, "HasTOF"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelPt + 1, "Pt"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelEta + 1, "Eta"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelITSNCls + 1, "ITSNCls"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelITSChi2 + 1, "ITSChi2"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelTPCNCls + 1, "TPCNCls"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelTPCChi2 + 1, "TPCChi2"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelDCAXY + 1, "DCAXY"); + histRegistry.get(HIST("BarrelsSelCounter"))->GetXaxis()->SetBinLabel(upchelpers::kBarrelSelDCAZ + 1, "DCAZ"); + } + + bool fwdCut(const o2::dataformats::GlobalFwdTrack& pft, const ForwardTracks::iterator& fwdTrack) + { + histRegistry.fill(HIST("MuonsSelCounter"), upchelpers::kFwdSelAll, 1); + auto pt = pft.getPt(); + auto eta = pft.getEta(); + auto pdca = fwdTrack.pDca(); + auto rabs = fwdTrack.rAtAbsorberEnd(); + auto chi2 = fwdTrack.chi2(); + bool passPt = pt > fUpcCuts.getFwdPtLow() && pt < fUpcCuts.getFwdPtHigh(); + bool passEta = eta > fUpcCuts.getFwdEtaLow() && eta < fUpcCuts.getFwdEtaHigh(); + bool passRabs = rabs > fUpcCuts.getMuonRAtAbsorberEndLow() && rabs < fUpcCuts.getMuonRAtAbsorberEndHigh(); + bool passPDca = rabs < upchelpers::AbsorberMid ? pdca < fUpcCuts.getMuonPDcaHighFirst() : pdca < fUpcCuts.getMuonPDcaHighSecond(); + bool passChi2 = chi2 > fUpcCuts.getFwdChi2Low() && chi2 < fUpcCuts.getFwdChi2High(); + if (passPt) + histRegistry.fill(HIST("MuonsSelCounter"), upchelpers::kFwdSelPt, 1); + if (passEta) + histRegistry.fill(HIST("MuonsSelCounter"), upchelpers::kFwdSelEta, 1); + if (passRabs) + histRegistry.fill(HIST("MuonsSelCounter"), upchelpers::kFwdSelRabs, 1); + if (passPDca) + histRegistry.fill(HIST("MuonsSelCounter"), upchelpers::kFwdSelpDCA, 1); + if (passChi2) + histRegistry.fill(HIST("MuonsSelCounter"), upchelpers::kFwdSelChi2, 1); + return passPt && passEta && passRabs && passPDca && passChi2; + } + + bool barCut(const BarrelTracks::iterator& track) + { + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelAll, 1); + if (!fUpcCuts.getUseBarCuts()) + return true; + if (fUpcCuts.getAmbigSwitch() == 1 && !track.isPVContributor()) + return false; + if (fUpcCuts.getRequireTOF() && !track.hasTOF()) + return false; + bool passHasTOF = !fUpcCuts.getRequireTOF() || track.hasTOF(); + bool passPt = track.pt() > fUpcCuts.getBarPtLow() && track.pt() < fUpcCuts.getBarPtHigh(); + bool passEta = track.eta() > fUpcCuts.getBarEtaLow() && track.eta() < fUpcCuts.getBarEtaHigh(); + bool passITSNCls = track.itsNCls() >= static_cast(fUpcCuts.getITSNClusLow()) && + track.itsNCls() <= static_cast(fUpcCuts.getITSNClusHigh()); + bool passITSChi2 = track.itsChi2NCl() > fUpcCuts.getITSChi2Low() && track.itsChi2NCl() < fUpcCuts.getITSChi2High(); + bool passTPCNCls = track.tpcNClsFound() >= static_cast(fUpcCuts.getTPCNClsLow()) && + track.tpcNClsFound() <= static_cast(fUpcCuts.getTPCNClsHigh()); + bool passTPCChi2 = track.tpcChi2NCl() > fUpcCuts.getTPCChi2Low() && track.tpcChi2NCl() < fUpcCuts.getTPCChi2High(); + bool passDcaZ = track.dcaZ() > fUpcCuts.getDcaZLow() && track.dcaZ() < fUpcCuts.getDcaZHigh(); + bool passDcaXY = true; + if (fUpcCuts.getCheckMaxDcaXY()) { + constexpr float DcaXYConst = 0.0105f; + constexpr float DcaXYSlope = 0.0350f; + constexpr float DcaXYExp = 1.1f; + float maxDCA = DcaXYConst + DcaXYSlope / std::pow(track.pt(), DcaXYExp); + passDcaXY = track.dcaXY() < maxDCA; + } + if (passHasTOF) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelHasTOF, 1); + if (passPt) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelPt, 1); + if (passEta) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelEta, 1); + if (passITSNCls) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelITSNCls, 1); + if (passITSChi2) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelITSChi2, 1); + if (passTPCNCls) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelTPCNCls, 1); + if (passTPCChi2) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelTPCChi2, 1); + if (passDcaXY) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelDCAXY, 1); + if (passDcaZ) + histRegistry.fill(HIST("BarrelsSelCounter"), upchelpers::kBarrelSelDCAZ, 1); + return passPt && passEta && passITSNCls && passITSChi2 && passTPCNCls && passTPCChi2 && passDcaXY && passDcaZ; + } + + void skimMCInfo(o2::aod::McCollisions const& mcCollisions, o2::aod::McParticles const& mcParticles) + { + std::vector newEventIDs(mcCollisions.size(), -1); + + int32_t newPartID = 0; + int32_t newEventID = 0; + int32_t nMCParticles = mcParticles.size(); + for (int32_t mcPartID = 0; mcPartID < nMCParticles; mcPartID++) { + const auto& mcPart = mcParticles.iteratorAt(mcPartID); + if (!mcPart.has_mcCollision()) + continue; + int32_t mcEventID = mcPart.mcCollisionId(); + const auto& mcEvent = mcCollisions.iteratorAt(mcEventID); + bool isSignal = mcEvent.getGeneratorId() == fSignalGenID; + if (!isSignal) { + continue; + } + fNewPartIDs[mcPartID] = newPartID; + newPartID++; + if (newEventIDs[mcEventID] == -1) { + newEventIDs[mcEventID] = newEventID; + newEventID++; + } + } + + std::vector newMotherIDs{}; + + for (const auto& item : fNewPartIDs) { + int32_t mcPartID = item.first; + const auto& mcPart = mcParticles.iteratorAt(mcPartID); + int32_t mcEventID = mcPart.mcCollisionId(); + int32_t mappedEventID = newEventIDs[mcEventID]; + if (mcPart.has_mothers()) { + const auto& motherIDs = mcPart.mothersIds(); + for (const auto& motherID : motherIDs) { + if (motherID >= nMCParticles) { + continue; + } + auto it = fNewPartIDs.find(motherID); + if (it != fNewPartIDs.end()) { + newMotherIDs.push_back(it->second); + } + } + } + int32_t newDaughterIDs[2] = {-1, -1}; + if (mcPart.has_daughters()) { + const auto& daughterIDs = mcPart.daughtersIds(); + int32_t firstDaughter = daughterIDs.front(); + int32_t lastDaughter = daughterIDs.back(); + if (firstDaughter >= nMCParticles || lastDaughter >= nMCParticles) { + continue; + } + auto itFirst = fNewPartIDs.find(firstDaughter); + auto itLast = fNewPartIDs.find(lastDaughter); + if (itFirst != fNewPartIDs.end() && itLast != fNewPartIDs.end()) { + newDaughterIDs[0] = fNewPartIDs.at(daughterIDs.front()); + newDaughterIDs[1] = fNewPartIDs.at(daughterIDs.back()); + } + } + udMCParticles(mappedEventID, mcPart.pdgCode(), mcPart.getHepMCStatusCode(), mcPart.flags(), newMotherIDs, newDaughterIDs, + mcPart.weight(), mcPart.px(), mcPart.py(), mcPart.pz(), mcPart.e()); + newMotherIDs.clear(); + } + + for (int32_t i = 0; i < mcCollisions.size(); i++) { + if (newEventIDs[i] == -1) { + continue; + } + const auto& mcEvent = mcCollisions.iteratorAt(i); + const auto& bc = mcEvent.bc(); + udMCCollisions(bc.globalBC(), mcEvent.generatorsID(), mcEvent.posX(), mcEvent.posY(), mcEvent.posZ(), + mcEvent.t(), mcEvent.weight(), mcEvent.impactParameter()); + } + + newEventIDs.clear(); + } + + int64_t bcDiff(uint64_t bc1, uint64_t bc2) + { + return bc1 > bc2 ? bc1 - bc2 : bc2 - bc1; + } + + template + T::iterator getStartForScroll(uint64_t inGbc, T& gbcMap) + { + auto it1 = gbcMap.lower_bound(inGbc); + typename T::iterator it; + if (it1 != gbcMap.end()) { + auto it2 = it1; + uint64_t bc1 = it1->first; + if (it2 != gbcMap.begin()) + --it2; + uint64_t bc2 = it2->first; + uint64_t dbc1 = bcDiff(bc1, inGbc); + uint64_t dbc2 = bcDiff(bc2, inGbc); + it = (dbc1 <= dbc2) ? it1 : it2; + } else { + it = it1; + --it; + } + return it; + } + + template + void scrollBackForth(uint64_t inGbc, uint64_t maxDbc, T& gbcMap, F&& func) + { + auto it = getStartForScroll(inGbc, gbcMap); + uint64_t gbc = it->first; + uint64_t dbc = bcDiff(inGbc, gbc); + + int count = 0; + while (dbc <= maxDbc) { + func(it, gbc); + count++; + if (it == gbcMap.begin()) + break; + --it; + gbc = it->first; + dbc = bcDiff(inGbc, gbc); + } + + std::advance(it, count + 1); + + if (it == gbcMap.end()) + return; + + gbc = it->first; + dbc = bcDiff(inGbc, gbc); + + while (dbc <= maxDbc) { + func(it, gbc); + ++it; + if (it == gbcMap.end()) + break; + gbc = it->first; + dbc = bcDiff(inGbc, gbc); + } + } + + void getBarrelTrackIds(uint64_t inGbc, std::map>& tracksPerBC, + uint64_t maxDbc, std::map& outTrkIds) + { + auto fillIds = [&outTrkIds](std::map>::iterator& inIt, uint64_t gbc) { + std::vector& ids = inIt->second; + for (const auto& id : ids) + outTrkIds[id] = gbc; + }; + scrollBackForth(inGbc, maxDbc, tracksPerBC, fillIds); + } + + void getFV0Amplitudes(uint64_t inGbc, o2::aod::FV0As const& fv0s, uint64_t maxDbc, + std::map& mapBcs, std::vector& amps, std::vector& relBcs) + { + auto fillAmps = [this, &fv0s, &s, &relBcs, inGbc](std::map::iterator& inIt, uint64_t gbc) { + int64_t fv0Id = inIt->second; + const auto& fv0 = fv0s.iteratorAt(fv0Id); + const auto& amplitudes = fv0.amplitude(); + float totalAmp = std::accumulate(amplitudes.begin(), amplitudes.end(), 0.f); + if (totalAmp > 0.f) { + amps.push_back(totalAmp); + auto relBc = static_cast(bcDiff(gbc, inGbc)); + if (gbc < inGbc) + relBc *= -1; + relBcs.push_back(relBc); + } + }; + scrollBackForth(inGbc, maxDbc, mapBcs, fillAmps); + } + + auto propagateToZero(ForwardTracks::iterator const& muon) + { + using SMatrix55 = ROOT::Math::SMatrix>; + using SMatrix5 = ROOT::Math::SVector; + SMatrix5 tpars(muon.x(), muon.y(), muon.phi(), muon.tgl(), muon.signed1Pt()); + std::vector v1{muon.cXX(), muon.cXY(), muon.cYY(), muon.cPhiX(), muon.cPhiY(), + muon.cPhiPhi(), muon.cTglX(), muon.cTglY(), muon.cTglPhi(), muon.cTglTgl(), + muon.c1PtX(), muon.c1PtY(), muon.c1PtPhi(), muon.c1PtTgl(), muon.c1Pt21Pt2()}; + SMatrix55 tcovs(v1.begin(), v1.end()); + o2::dataformats::GlobalFwdTrack propmuon; + o2::dataformats::GlobalFwdTrack track; + track.setParameters(tpars); + track.setZ(muon.z()); + track.setCovariances(tcovs); + auto mchTrack = fMatching.FwdtoMCH(track); + o2::mch::TrackExtrap::extrapToVertex(mchTrack, 0., 0., 0., 0., 0.); + auto proptrack = fMatching.MCHtoFwd(mchTrack); + propmuon.setParameters(proptrack.getParameters()); + propmuon.setZ(proptrack.getZ()); + propmuon.setCovariances(proptrack.getCovariances()); + return propmuon; + } + + bool addToFwdTable(int64_t candId, int64_t trackId, uint64_t gbc, float trackTime, ForwardTracks const& fwdTracks, + const o2::aod::McFwdTrackLabels* mcFwdTrackLabels) + { + constexpr float NoMchMftMatch = -1.f; + const auto& track = fwdTracks.iteratorAt(trackId); + float px; + float py; + float pz; + int sign; + if (fUpcCuts.getUseFwdCuts()) { + auto pft = propagateToZero(track); + bool pass = fwdCut(pft, track); + if (!pass) + return false; + px = pft.getPx(); + py = pft.getPy(); + pz = pft.getPz(); + sign = (pft.getInvQPt() > 0) ? 1 : -1; + } else { + px = track.px(); + py = track.py(); + pz = track.pz(); + sign = track.sign(); + } + udFwdTracks(candId, px, py, pz, sign, gbc, trackTime, track.trackTimeRes()); + udFwdTracksExtra(track.trackType(), track.nClusters(), track.pDca(), track.rAtAbsorberEnd(), track.chi2(), + track.chi2MatchMCHMID(), NoMchMftMatch, track.mchBitMap(), track.midBitMap(), track.midBoards()); + udFwdIndices(candId, trackId, track.matchMCHTrackId(), -1); + if (fDoMC) { + const auto& label = mcFwdTrackLabels->iteratorAt(trackId); + uint16_t mcMask = label.mcMask(); + auto it = fNewPartIDs.find(label.mcParticleId()); + int32_t newPartID = it != fNewPartIDs.end() ? it->second : -1; + udFwdTrackLabels(newPartID, mcMask); + } + return true; + } + + bool addToBarrelTable(int64_t candId, int64_t trackId, uint64_t gbc, float trackTime, BarrelTracks const& barrelTracks, + const o2::aod::McTrackLabels* mcTrackLabels) + { + const auto& track = barrelTracks.iteratorAt(trackId); + if (!barCut(track)) + return false; + int32_t colId = track.collisionId() >= 0 ? track.collisionId() : -1; + udTracks(candId, track.px(), track.py(), track.pz(), track.sign(), gbc, trackTime, track.trackTimeRes()); + udTracksExtra(track.tpcInnerParam(), track.itsClusterSizes(), track.tpcNClsFindable(), track.tpcNClsFindableMinusFound(), + track.tpcNClsFindableMinusCrossedRows(), track.tpcNClsShared(), track.trdPattern(), track.itsChi2NCl(), + track.tpcChi2NCl(), track.trdChi2(), track.tofChi2(), track.tpcSignal(), track.tofSignal(), track.trdSignal(), + track.length(), track.tofExpMom(), track.detectorMap()); + udTracksDCA(track.dcaZ(), track.dcaXY()); + udTracksFlags(colId, track.isPVContributor()); + udTracksPID(track.tpcNSigmaEl(), track.tpcNSigmaMu(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), + track.beta(), track.betaerror(), + track.tofNSigmaEl(), track.tofNSigmaMu(), track.tofNSigmaPi(), track.tofNSigmaKa(), track.tofNSigmaPr()); + if (fDoMC) { + const auto& label = mcTrackLabels->iteratorAt(trackId); + uint16_t mcMask = label.mcMask(); + auto it = fNewPartIDs.find(label.mcParticleId()); + int32_t newPartID = it != fNewPartIDs.end() ? it->second : -1; + udTrackLabels(newPartID, mcMask); + } + return true; + } + + void createCandidates(ForwardTracks const& fwdTracks, + o2::aod::AmbiguousFwdTracks const& ambFwdTracks, + BarrelTracks const& barrelTracks, + o2::aod::AmbiguousTracks const& ambBarrelTracks, + o2::aod::BCs const& bcs, + o2::aod::Collisions const& collisions, + o2::aod::FV0As const& fv0s, + o2::aod::Zdcs const& zdcs, + const o2::aod::McFwdTrackLabels* mcFwdTrackLabels, + const o2::aod::McTrackLabels* mcBarrelTrackLabels) + { + using o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack; + + int32_t runNumber = bcs.iteratorAt(0).runNumber(); + if (fUpcCuts.getUseFwdCuts()) { + if (runNumber != fRun) { + fRun = runNumber; + std::map metadata; + auto soreor = o2::ccdb::BasicCCDBManager::getRunDuration(fCCDBApi, fRun); + auto ts = soreor.second; + auto grpmag = fCCDBApi.retrieveFromTFileAny("GLO/Config/GRPMagField", metadata, ts); + o2::base::Propagator::initFieldFromGRP(grpmag); + if (!o2::base::GeometryManager::isGeometryLoaded()) + fCCDB->get("GLO/Config/GeometryAligned"); + o2::mch::TrackExtrap::setField(); + } + } + + auto nBcs = bcs.size(); + std::vector vGlobalBCs(nBcs, 0); + for (const auto& bc : bcs) { + vGlobalBCs[bc.globalIndex()] = bc.globalBC(); + } + + auto nCols = collisions.size(); + std::vector vColIndexBCs(nCols, -1); + for (const auto& col : collisions) { + vColIndexBCs[col.globalIndex()] = col.bcId(); + } + + std::map mapGlobalBcWithV0A{}; + constexpr float FV0ValidTime = 15.f; + for (const auto& fv0 : fv0s) { + if (std::abs(fv0.time()) > FV0ValidTime) + continue; + uint64_t globalBC = vGlobalBCs[fv0.bcId()]; + mapGlobalBcWithV0A[globalBC] = fv0.globalIndex(); + } + auto nFV0s = mapGlobalBcWithV0A.size(); + + std::map mapGlobalBcWithZdc{}; + constexpr float ZDCValidTime = 2.f; + for (const auto& zdc : zdcs) { + if (std::abs(zdc.timeZNA()) > ZDCValidTime && std::abs(zdc.timeZNC()) > ZDCValidTime) + continue; + uint64_t globalBC = vGlobalBCs[zdc.bcId()]; + mapGlobalBcWithZdc[globalBC] = zdc.globalIndex(); + } + auto nZdcs = mapGlobalBcWithZdc.size(); + + auto nFwdTracks = fwdTracks.size(); + auto nAmbFwdTracks = ambFwdTracks.size(); + std::vector vAmbFwdTrackIndex(nFwdTracks, -1); + std::vector vAmbFwdTrackIndexBCs(nAmbFwdTracks, -1); + for (const auto& ambTr : ambFwdTracks) { + vAmbFwdTrackIndex[ambTr.fwdtrackId()] = ambTr.globalIndex(); + vAmbFwdTrackIndexBCs[ambTr.globalIndex()] = ambTr.bcIds()[0]; + } + + auto nBarrelTracks = barrelTracks.size(); + auto nAmbBarrelTracks = ambBarrelTracks.size(); + std::vector vAmbBarTrackIndex(nBarrelTracks, -1); + std::vector vAmbBarTrackIndexBCs(nAmbBarrelTracks, -1); + for (const auto& ambTr : ambBarrelTracks) { + vAmbBarTrackIndex[ambTr.trackId()] = ambTr.globalIndex(); + vAmbBarTrackIndexBCs[ambTr.globalIndex()] = ambTr.bcIds()[0]; + } + + // anchor map: MCH-MID forward muons keyed by their global BC + std::map> mapGlobalBcsWithMCHMIDTrackIds; + for (const auto& fwdTrack : fwdTracks) { + if (fwdTrack.trackType() != MuonStandaloneTrack) + continue; + auto trackId = fwdTrack.globalIndex(); + int64_t indexBC = vAmbFwdTrackIndex[trackId] < 0 ? vColIndexBCs[fwdTrack.collisionId()] : vAmbFwdTrackIndexBCs[vAmbFwdTrackIndex[trackId]]; + auto globalBC = vGlobalBCs[indexBC] + TMath::FloorNint(fwdTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS + 1.); + mapGlobalBcsWithMCHMIDTrackIds[globalBC].push_back(trackId); + } + + // companion map: barrel tracks keyed by their global BC + std::map> mapGlobalBcsWithBarrelTrackIds; + for (const auto& barTrack : barrelTracks) { + auto trackId = barTrack.globalIndex(); + int64_t bcId = vAmbBarTrackIndex[trackId] < 0 + ? (barTrack.has_collision() ? vColIndexBCs[barTrack.collisionId()] : -1) + : vAmbBarTrackIndexBCs[vAmbBarTrackIndex[trackId]]; + if (bcId < 0) + continue; + auto globalBC = vGlobalBCs[bcId] + TMath::FloorNint(barTrack.trackTime() / o2::constants::lhc::LHCBunchSpacingNS + 1.); + mapGlobalBcsWithBarrelTrackIds[globalBC].push_back(trackId); + } + + int32_t candId = 0; + for (const auto& gbcMuids : mapGlobalBcsWithMCHMIDTrackIds) { + uint64_t globalBcMid = gbcMuids.first; + + // veto on FV0 in the same BC as the anchor + auto itFv0Id = mapGlobalBcWithV0A.find(globalBcMid); + if (itFv0Id != mapGlobalBcWithV0A.end()) { + auto fv0Id = itFv0Id->second; + const auto& fv0 = fv0s.iteratorAt(fv0Id); + float fv0Amp = 0.f; + for (const auto& amp : fv0.amplitude()) + fv0Amp += amp; + if (fv0Amp > fMaxFV0Amp) + continue; + } + + uint16_t numContrib = 0; + auto& vMuonIds = gbcMuids.second; + for (const auto& imuon : vMuonIds) { + if (!addToFwdTable(candId, imuon, globalBcMid, 0., fwdTracks, mcFwdTrackLabels)) + continue; + numContrib++; + } + if (numContrib < 1) // anchor produced no surviving fwd tracks + continue; + + // gather barrel tracks within ±fBcWindowBarrel of the anchor + std::map mapBarIdBc{}; + getBarrelTrackIds(globalBcMid, mapGlobalBcsWithBarrelTrackIds, fBcWindowBarrel, mapBarIdBc); + for (const auto& [ibar, gbc] : mapBarIdBc) { + float trackTime = (static_cast(gbc) - static_cast(globalBcMid)) * o2::constants::lhc::LHCBunchSpacingNS; + if (!addToBarrelTable(candId, ibar, gbc, trackTime, barrelTracks, mcBarrelTrackLabels)) + continue; + numContrib++; + } + + eventCandidates(globalBcMid, runNumber, 0., 0., 0., 0, numContrib, 0, 0); + std::vector amplitudesV0A{}; + std::vector relBCsV0A{}; + std::vector amplitudesT0A{}; + std::vector relBCsT0A{}; + if (nFV0s > 0) { + getFV0Amplitudes(globalBcMid, fv0s, fBcWindowFITAmps, mapGlobalBcWithV0A, amplitudesV0A, relBCsV0A); + } + eventCandidatesSelsFwd(0., 0., amplitudesT0A, relBCsT0A, amplitudesV0A, relBCsV0A); + if (nZdcs > 0) { + auto itZDC = mapGlobalBcWithZdc.find(globalBcMid); + if (itZDC != mapGlobalBcWithZdc.end()) { + const auto& zdc = zdcs.iteratorAt(itZDC->second); + float timeZNA = zdc.timeZNA(); + float timeZNC = zdc.timeZNC(); + float eComZNA = zdc.energyCommonZNA(); + float eComZNC = zdc.energyCommonZNC(); + udZdcsReduced(candId, timeZNA, timeZNC, eComZNA, eComZNC); + } + } + candId++; + } + + vGlobalBCs.clear(); + vColIndexBCs.clear(); + mapGlobalBcWithV0A.clear(); + mapGlobalBcWithZdc.clear(); + vAmbFwdTrackIndex.clear(); + vAmbFwdTrackIndexBCs.clear(); + vAmbBarTrackIndex.clear(); + vAmbBarTrackIndexBCs.clear(); + mapGlobalBcsWithMCHMIDTrackIds.clear(); + mapGlobalBcsWithBarrelTrackIds.clear(); + } + + void processSemiFwd(ForwardTracks const& fwdTracks, + o2::aod::AmbiguousFwdTracks const& ambFwdTracks, + BarrelTracks const& barrelTracks, + o2::aod::AmbiguousTracks const& ambBarrelTracks, + o2::aod::BCs const& bcs, + o2::aod::Collisions const& collisions, + o2::aod::FV0As const& fv0s, + o2::aod::Zdcs const& zdcs) + { + fDoMC = false; + createCandidates(fwdTracks, ambFwdTracks, barrelTracks, ambBarrelTracks, bcs, collisions, fv0s, zdcs, + nullptr, nullptr); + } + + void processSemiFwdMC(ForwardTracks const& fwdTracks, + o2::aod::AmbiguousFwdTracks const& ambFwdTracks, + BarrelTracks const& barrelTracks, + o2::aod::AmbiguousTracks const& ambBarrelTracks, + o2::aod::BCs const& bcs, + o2::aod::Collisions const& collisions, + o2::aod::FV0As const& fv0s, + o2::aod::Zdcs const& zdcs, + o2::aod::McCollisions const& mcCollisions, + o2::aod::McParticles const& mcParticles, + o2::aod::McFwdTrackLabels const& mcFwdTrackLabels, + o2::aod::McTrackLabels const& mcBarrelTrackLabels) + { + fDoMC = true; + skimMCInfo(mcCollisions, mcParticles); + createCandidates(fwdTracks, ambFwdTracks, barrelTracks, ambBarrelTracks, bcs, collisions, fv0s, zdcs, + &mcFwdTrackLabels, &mcBarrelTrackLabels); + fNewPartIDs.clear(); + } + + PROCESS_SWITCH(UpcCandProducerSemiFwd, processSemiFwd, "Produce candidates combining forward MCH-MID and central-barrel tracks", true); + PROCESS_SWITCH(UpcCandProducerSemiFwd, processSemiFwdMC, "Produce semi-forward candidates with MC information", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/Tasks/CMakeLists.txt b/PWGUD/Tasks/CMakeLists.txt index bf1c0e7be72..a2566a51d87 100644 --- a/PWGUD/Tasks/CMakeLists.txt +++ b/PWGUD/Tasks/CMakeLists.txt @@ -11,21 +11,29 @@ o2physics_add_dpl_workflow(upc-polarisation-jpsi-incoh SOURCES upcPolarisationJpsiIncoh.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(upc +o2physics_add_dpl_workflow(upc-track-vertexing-qa + SOURCES upcTrackVertexingQA.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-analysis SOURCES upcAnalysis.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-spectra SOURCES sgSpectraAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-pid-spectra-table SOURCES sgPIDSpectraTable.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -36,46 +44,55 @@ o2physics_add_dpl_workflow(sg-pid-analyzer o2physics_add_dpl_workflow(sg-pid-spectra SOURCES sgPIDSpectra.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-incl-jpsi SOURCES sgInclJpsi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-excl-omega SOURCES sgExclOmega.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-excl-universe SOURCES sgExcUniverse.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-fourpi SOURCES sgFourPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-sixpi SOURCES sgSixPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-twopi SOURCES sgTwoPiAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-d0 SOURCES sgD0Analyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-fit SOURCES sgFITAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -99,18 +116,20 @@ o2physics_add_dpl_workflow(diff-qa PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(dgcand-analyzer +o2physics_add_dpl_workflow(dg-cand-analyzer SOURCES dgCandAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGCutparHolder O2Physics::UDGoodRunSelector O2Physics::DGPIDSelector O2Physics::UDFSParser COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(upccand-analyzer +o2physics_add_dpl_workflow(upc-cand-analyzer SOURCES upcCandidateAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(upccand-producer-qa - SOURCES upcCandidateProducerQa.cpp +o2physics_add_dpl_workflow(upc-cand-producer-qa + SOURCES upcCandidateProducerQa.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) @@ -119,139 +138,176 @@ o2physics_add_dpl_workflow(upc-mft PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(upc-veto +o2physics_add_dpl_workflow(upc-veto-analysis SOURCES upcVetoAnalysis.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(tautau13topo SOURCES upcTauTau13topo.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-tau-rl SOURCES upcTauRl.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(polarisation-rho SOURCES polarisationRho.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-jpsi-corr SOURCES upcJpsiCorr.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi SOURCES exclusivePhi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-photonuclear-jmg SOURCES upcPhotonuclearAnalysisJMG.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGCFCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-two-protons SOURCES exclusiveTwoProtons.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-two-protons-sg SOURCES exclusiveTwoProtonsSG.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi-leptons SOURCES exclusivePhiLeptons.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-phi-leptons-trees SOURCES exclusivePhiLeptonsTrees.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-pentaquark SOURCES exclusivePentaquark.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(inclusive-phikstar-sd SOURCES sginclusivePhiKstarSD.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-phi SOURCES sgExclusivePhi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-phi-its SOURCES sgExclusivePhiITSselections.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-pion-analysis SOURCES upcPionAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(fwd-muons-upc - SOURCES FwdMuonsUPC.cxx + SOURCES fwdMuonsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(upc-event-itsrof-counter - SOURCES upcEventITSROFcounter.cxx +o2physics_add_dpl_workflow(upc-fwd-jpsi-rl + SOURCES upcFwdJpsiRl.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-semi-fwd-jpsi-rl + SOURCES upcSemiFwdJpsiRl.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-event-its-rof-counter + SOURCES upcEventItsRofCounter.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-rho-analysis SOURCES upcRhoAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-rho-prime-analysis SOURCES upcRhoPrimeAnalysis.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(event-by-event SOURCES eventByevent.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(exclusive-rho-to-four-pi SOURCES exclusiveRhoTo4Pi.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::DGPIDSelector COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-quarkonia-central-barrel SOURCES upcQuarkoniaCentralBarrel.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::DetectorsBase COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(test-mc-std-tabs-rl - SOURCES testMcStdTabsRl.cxx +o2physics_add_dpl_workflow(upc-test-mc-std-tabs-rl + SOURCES upcTestMcStdTabsRl.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2::ReconstructionDataFormats O2::DetectorsBase O2::DetectorsCommonDataFormats COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(decaytree-analyzer SOURCES decayTreeAnalyzer.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::UDGoodRunSelector O2Physics::decayTree COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-cumulants-upc SOURCES flowCumulantsUpc.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-correlations-upc SOURCES flowCorrelationsUpc.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore O2Physics::PWGCFCore + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore O2Physics::PWGCFCore O2Physics::TwoPartCorrCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(flow-mc-upc @@ -259,22 +315,54 @@ o2physics_add_dpl_workflow(flow-mc-upc PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::GFWCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(flow-long-range-corr-upc + SOURCES flowLongRangeCorrUpc.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGCFCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(analysis-mc-dpm-jet-sg-v3 SOURCES analysisMCDPMJetSGv3.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(upc-test-rct-tables SOURCES upcTestRctTables.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) o2physics_add_dpl_workflow(sg-exclusive-jpsi-midrapidity SOURCES sgExclusiveJpsiMidrapidity.cxx + REUSE_FROM UDTablesPCH PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore COMPONENT_NAME Analysis) -o2physics_add_dpl_workflow(fitbit-mapping - SOURCES upcTestFITBitMapping.cxx - PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore - COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(upc-vm-rof + SOURCES upcVmRof.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-test-fit-bit-mapping + SOURCES upcTestFitBitMapping.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pt-spectra-inclusive-upc + SOURCES ptSpectraInclusiveUpc.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(sg-deuteron-spectra + SOURCES sgDeuteronSpectra.cxx + REUSE_FROM UDTablesPCH + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(upc-pile-up + SOURCES upcPileUp.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::AnalysisCCDB + COMPONENT_NAME Analysis) diff --git a/PWGUD/Tasks/dgCandAnalyzer.cxx b/PWGUD/Tasks/dgCandAnalyzer.cxx index 79b5b6c5f02..60547236485 100644 --- a/PWGUD/Tasks/dgCandAnalyzer.cxx +++ b/PWGUD/Tasks/dgCandAnalyzer.cxx @@ -144,7 +144,7 @@ struct DGCandAnalyzer { // PDG pdg = TDatabasePDG::Instance(); - anaPars = (DGAnaparHolder)DGPars; + anaPars = DGAnaparHolder(DGPars); pidsel.init(anaPars); grsel.init(goodRunsFile); diff --git a/PWGUD/Tasks/diffMCDataScanner.cxx b/PWGUD/Tasks/diffMCDataScanner.cxx index 06fa70f5220..369eca48921 100644 --- a/PWGUD/Tasks/diffMCDataScanner.cxx +++ b/PWGUD/Tasks/diffMCDataScanner.cxx @@ -163,7 +163,7 @@ struct collisionsInfo { // count tracks int cntAll = 0; int netCharge = 0; - for (auto track : tracks) { + for (const auto& track : tracks) { cntAll++; netCharge += track.sign(); } @@ -198,7 +198,7 @@ struct collisionsInfo { // loop over FWD tracks LOGF(info, "FWD tracks: %i", fwdtracks.size()); - for (auto fwdtrack : fwdtracks) { + for (const auto& fwdtrack : fwdtracks) { registry.get(HIST("etaFWDAll"))->Fill(fwdtrack.eta()); } @@ -213,7 +213,7 @@ struct collisionsInfo { // registry.get(HIST("globalVsMFTDG"))->Fill(cntGlobal, mfttracks.size()); // loop over FWD tracks - for (auto fwdtrack : fwdtracks) { + for (const auto& fwdtrack : fwdtracks) { registry.get(HIST("etaFWDDG"))->Fill(fwdtrack.eta()); } } @@ -251,7 +251,7 @@ struct BCInfo { // count collisions with good time resoluton auto nColGT = 0; - for (auto col : cols) { + for (const auto& col : cols) { if (col.collisionTimeRes() <= 20.) { nColGT++; } @@ -311,7 +311,7 @@ struct TrackTypes { void process(TCs const& tracks, /*MTs const& mfttracks,*/ FTs const& fwdtracks) { - for (auto track : tracks) { + for (const auto& track : tracks) { LOGF(debug, "isGlobal %i Detector map %i %i %i %i time resolution %f", track.isGlobalTrack(), track.hasITS(), track.hasTPC(), track.hasTRD(), track.hasTOF(), track.trackTimeRes()); @@ -353,14 +353,14 @@ struct TrackTypes { // ForwardTrackTypeEnum has 5 values auto nTypes = 5; - for (auto fwdtrack : fwdtracks) { + for (const auto& fwdtrack : fwdtracks) { registry.get(HIST("FwdType"))->Fill(0., 0., 1.); if (fwdtrack.collisionId() >= 0) { registry.get(HIST("FwdType"))->Fill(1., 0., 1.); } for (auto ii = 0; ii < nTypes; ii++) { if ((fwdtrack.trackType() & (1 << ii)) > 0) { - registry.get(HIST("FwdType"))->Fill((Double_t)(ii + 2), 0., 1.); + registry.get(HIST("FwdType"))->Fill(static_cast(ii + 2), 0., 1.); } } } @@ -379,7 +379,7 @@ struct MCTracks { void process(CCs const& collisions, aod::McCollisions& /*McCols*/, aod::McParticles& McParts) { - for (auto collision : collisions) { + for (const auto& collision : collisions) { // get McCollision which belongs to collision auto MCCol = collision.mcCollision(); @@ -397,7 +397,7 @@ struct MCTracks { bool hasDiff = false; int prongs = 0; - for (auto mcpart : MCPartSlice) { + for (const auto& mcpart : MCPartSlice) { LOGF(info, " MCPart: %i %i %i %i %i - %i", mcpart.mcCollisionId(), mcpart.isPhysicalPrimary(), mcpart.getProcess(), mcpart.getGenStatusCode(), mcpart.globalIndex(), mcpart.pdgCode()); if (mcpart.pdgCode() == 9900110) { LOGF(info, " rho_diff0 energy: %f", mcpart.e()); @@ -413,8 +413,8 @@ struct MCTracks { // } if (hasDiff && mothers.size() > 1) { - auto mom1 = mothers[0]; - auto mom2 = mothers[1]; + const auto& mom1 = mothers[0]; + const auto& mom2 = mothers[1]; if (mcpart.isPhysicalPrimary() && (mcpart.getGenStatusCode() == 1 || mcpart.getGenStatusCode() == 2) && mom1.globalIndex() != mom2.globalIndex() && @@ -456,7 +456,7 @@ struct TPCnSigma { void process(TCwPIDs& tracks, aod::McParticles const& /*mcParticles*/) { - for (auto track : tracks) { + for (const auto& track : tracks) { if (track.isGlobalTrack()) { nSigmas(track.mcParticle_as().pdgCode(), track.mcParticle_as().pt(), track.tpcNSigmaEl(), track.tpcNSigmaMu(), track.tpcNSigmaPi(), diff --git a/PWGUD/Tasks/diffMCQA.cxx b/PWGUD/Tasks/diffMCQA.cxx index c6b1f650db2..9c32645a1bd 100644 --- a/PWGUD/Tasks/diffMCQA.cxx +++ b/PWGUD/Tasks/diffMCQA.cxx @@ -88,7 +88,7 @@ struct DiffMCQA { { maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMCTruth")) { diff --git a/PWGUD/Tasks/diffQA.cxx b/PWGUD/Tasks/diffQA.cxx index 59bdd9a67eb..adc79e0c37f 100644 --- a/PWGUD/Tasks/diffQA.cxx +++ b/PWGUD/Tasks/diffQA.cxx @@ -98,7 +98,7 @@ struct DiffQA { // initialize global variables maxdEdxTPC = 0.; maxdEdxTOF = 0.; - diffCuts = (DGCutparHolder)DGCuts; + diffCuts = DGCutparHolder(DGCuts); // add histograms for the different process functions if (context.mOptions.get("processMain")) { @@ -657,7 +657,7 @@ struct DiffQA { int64_t lastBCwFV0 = fv0s.begin().bc_as().globalBC(); auto lastOrbit = lastBCwFV0 / o2::constants::lhc::LHCMaxBunches; - for (auto fv0 : fv0s) { + for (const auto& fv0 : fv0s) { // side A for (size_t ind = 0; ind < fv0.channel().size(); ind++) { @@ -692,7 +692,7 @@ struct DiffQA { int64_t lastBCwFT0 = ft0s.begin().bc_as().globalBC(); auto lastOrbit = lastBCwFT0 / o2::constants::lhc::LHCMaxBunches; - for (auto ft0 : ft0s) { + for (const auto& ft0 : ft0s) { // side A for (size_t ind = 0; ind < ft0.channelA().size(); ind++) { @@ -883,7 +883,7 @@ struct DiffQA { int64_t lastBCwFDD = fdds.begin().bc_as().globalBC(); auto lastOrbit = lastBCwFDD / o2::constants::lhc::LHCMaxBunches; - for (auto fdd : fdds) { + for (const auto& fdd : fdds) { // side A for (auto ind = 0; ind < 8; ind++) { @@ -947,7 +947,7 @@ struct DiffQA { void processTest(CCs const& collisions, BCs const& bcs) { uint64_t bc1, bc2, bc3; - for (auto col : collisions) { + for (const auto& col : collisions) { bc1 = -1; bc2 = -2; bc3 = -3; diff --git a/PWGUD/Tasks/eventByevent.cxx b/PWGUD/Tasks/eventByevent.cxx index 913319bd785..a146e3ea993 100644 --- a/PWGUD/Tasks/eventByevent.cxx +++ b/PWGUD/Tasks/eventByevent.cxx @@ -242,7 +242,7 @@ struct EventByEvent { return; registry.fill(HIST("hSelectionCounter"), 5); - for (auto t : tracks) { + for (const auto& t : tracks) { if (!trackselector(t, parameters)) continue; @@ -270,14 +270,14 @@ struct EventByEvent { registry.fill(HIST("hSelectionCounter"), 6); if ((rawPionTracks.size() >= 2) && (allTracks.size() >= 2)) { - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { p += pion; } registry.fill(HIST("h4TracksPions"), onlyPionTracks.size()); registry.fill(HIST("hSelectionCounter"), 7); - for (auto rtrk : rawPionTracks) { + for (const auto& rtrk : rawPionTracks) { TLorentzVector itrk; itrk.SetXYZM(rtrk.px(), rtrk.py(), rtrk.pz(), o2::constants::physics::MassPionCharged); @@ -293,7 +293,7 @@ struct EventByEvent { int sign = 0; TLorentzVector piplus, piminus; - for (auto rawPion : rawPionTracks) { + for (const auto& rawPion : rawPionTracks) { sign += rawPion.sign(); } diff --git a/PWGUD/Tasks/exclusivePentaquark.cxx b/PWGUD/Tasks/exclusivePentaquark.cxx index 8f21b5f333f..ed5b440add0 100644 --- a/PWGUD/Tasks/exclusivePentaquark.cxx +++ b/PWGUD/Tasks/exclusivePentaquark.cxx @@ -135,7 +135,7 @@ struct ExclusivePentaquark { std::vector rawProtonTracks; std::vector rawProtonTracksTOF; - for (auto trk : tracks) { + for (const auto& trk : tracks) { if (!trk.isPVContributor()) { continue; } diff --git a/PWGUD/Tasks/exclusivePhi.cxx b/PWGUD/Tasks/exclusivePhi.cxx index 499f8e1046f..465d6f4ac0e 100644 --- a/PWGUD/Tasks/exclusivePhi.cxx +++ b/PWGUD/Tasks/exclusivePhi.cxx @@ -48,9 +48,9 @@ struct ExclusivePhi { HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; //_____________________________________________________________________________ - Double_t CosThetaHelicityFrame(TLorentzVector pionPositive, - TLorentzVector pionNegative, - TLorentzVector possibleRhoZero) + Double_t CosThetaHelicityFrame(const TLorentzVector& pionPositive, + const TLorentzVector& pionNegative, + const TLorentzVector& possibleRhoZero) { Double_t HalfSqrtSnn = 2680.; @@ -77,7 +77,7 @@ struct ExclusivePhi { return CosThetaHE; } //------------------------------------------------------------------------------------------------------ - Double_t PhiHelicityFrame(TLorentzVector muonPositive, TLorentzVector muonNegative, TLorentzVector possibleJPsi) + Double_t PhiHelicityFrame(const TLorentzVector& muonPositive, const TLorentzVector& muonNegative, const TLorentzVector& possibleJPsi) { // Half of the energy per pair of the colliding nucleons. @@ -266,7 +266,7 @@ struct ExclusivePhi { std::vector onlyKaonSigma; std::vector rawKaonTracks; - for (auto trk : tracks) { + for (const auto& trk : tracks) { registry.fill(HIST("hSelectionCounter"), 1); if (!trk.isPVContributor()) { continue; @@ -323,7 +323,7 @@ struct ExclusivePhi { if (onlyKaonTracks.size() == 2) { registry.fill(HIST("hSelectionCounter"), 7); - for (auto kaon : onlyKaonTracks) { + for (const auto& kaon : onlyKaonTracks) { phi += kaon; } @@ -373,7 +373,7 @@ struct ExclusivePhi { std::vector allTracksAreITSonlyAndFourITSclusters; int counter = 0; - for (auto t : tracks) { + for (const auto& t : tracks) { registry.fill(HIST("hSelectionCounter2"), 0); if (!t.isPVContributor()) { continue; @@ -466,16 +466,16 @@ struct ExclusivePhi { // if ((collision.posZ() < -10) || (collision.posZ() > 10)) { if (allTracksAreKaons.size() == 2) { registry.fill(HIST("hSelectionCounter2"), 7); - for (auto kaon : allTracksAreKaons) { + for (const auto& kaon : allTracksAreKaons) { phiWithoutPID += kaon; } registry.fill(HIST("hTracksKaons"), allTracksAreKaons.size()); // kaon mass hypothesis with wrong momentum for one track - for (auto kaon : allTracksAreKaonsWrongMomentum) { + for (const auto& kaon : allTracksAreKaonsWrongMomentum) { phiWrongMomentaWithoutPID += kaon; } // pion mass hypothesis - for (auto pion : allTracksArePions) { + for (const auto& pion : allTracksArePions) { phiWithoutPIDPionHypothesis += pion; } @@ -619,7 +619,7 @@ struct ExclusivePhi { if (allTracksAreKaonsBandPID.size() == 2) { TLorentzVector reallyPhi; - for (auto kaon : allTracksAreKaonsBandPID) { + for (const auto& kaon : allTracksAreKaonsBandPID) { reallyPhi += kaon; } diff --git a/PWGUD/Tasks/exclusivePhiLeptons.cxx b/PWGUD/Tasks/exclusivePhiLeptons.cxx index ae4e80c31ed..9576ece02b5 100644 --- a/PWGUD/Tasks/exclusivePhiLeptons.cxx +++ b/PWGUD/Tasks/exclusivePhiLeptons.cxx @@ -157,7 +157,7 @@ struct ExclusivePhiLeptons { std::vector rawElectronTracksTOF; int counterPV = 0; - for (auto trk : tracks) { + for (const auto& trk : tracks) { if (!trk.isPVContributor()) { continue; } diff --git a/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx b/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx index 577f25e63c6..5a7ccd7a315 100644 --- a/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx +++ b/PWGUD/Tasks/exclusivePhiLeptonsTrees.cxx @@ -144,7 +144,7 @@ struct ExclusivePhiLeptonsTrees { // - (px,py,pz,E)1 // - (px,py,pz,E)2 int counterPV = 0; - for (auto trk : tracks) { + for (const auto& trk : tracks) { // ---------------------------------------- // SELECTIONS: // - PV track diff --git a/PWGUD/Tasks/exclusiveRhoTo4Pi.cxx b/PWGUD/Tasks/exclusiveRhoTo4Pi.cxx index c295a40df6e..c508c0b30dd 100644 --- a/PWGUD/Tasks/exclusiveRhoTo4Pi.cxx +++ b/PWGUD/Tasks/exclusiveRhoTo4Pi.cxx @@ -1202,7 +1202,7 @@ struct ExclusiveRhoTo4Pi { PROCESS_SWITCH(ExclusiveRhoTo4Pi, processEventCounterMC, "MC Event Counter Function", false); PROCESS_SWITCH(ExclusiveRhoTo4Pi, processTrackCounterMC, "MC Track Counter Function", false); - double collinSoperPhi(PxPyPzMVector twoPionVector, PxPyPzMVector fourPionVector) + double collinSoperPhi(const PxPyPzMVector& twoPionVector, const PxPyPzMVector& fourPionVector) { PxPyPzEVector pProjCM(0., 0., -momentumBeam, halfSqrtSnn * 208); // projectile PxPyPzEVector pTargCM(0., 0., momentumBeam, halfSqrtSnn * 208); // target @@ -1219,7 +1219,7 @@ struct ExclusiveRhoTo4Pi { return phi; } - double collinSoperCosTheta(PxPyPzMVector twoPionVector, PxPyPzMVector fourPionVector) + double collinSoperCosTheta(const PxPyPzMVector& twoPionVector, const PxPyPzMVector& fourPionVector) { PxPyPzEVector pProjCM(0., 0., -momentumBeam, halfSqrtSnn * 208); // projectile PxPyPzEVector pTargCM(0., 0., momentumBeam, halfSqrtSnn * 208); // target diff --git a/PWGUD/Tasks/exclusiveTwoProtons.cxx b/PWGUD/Tasks/exclusiveTwoProtons.cxx index bc3c4783aba..3b0b081dc86 100644 --- a/PWGUD/Tasks/exclusiveTwoProtons.cxx +++ b/PWGUD/Tasks/exclusiveTwoProtons.cxx @@ -50,7 +50,7 @@ struct ExclusiveTwoProtons { // defining histograms using histogram registry HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; //_____________________________________________________________________________ - Double_t CosThetaHelicityFrame(TLorentzVector posDaughter, TLorentzVector negDaughter, TLorentzVector mother) + Double_t CosThetaHelicityFrame(const TLorentzVector& posDaughter, const TLorentzVector& negDaughter, const TLorentzVector& mother) { Double_t HalfSqrtSnn = 2680.; @@ -77,7 +77,7 @@ struct ExclusiveTwoProtons { return CosThetaHE; } //------------------------------------------------------------------------------------------------------ - Double_t PhiHelicityFrame(TLorentzVector posDaughter, TLorentzVector negDaughter, TLorentzVector mother) + Double_t PhiHelicityFrame(const TLorentzVector& posDaughter, const TLorentzVector& negDaughter, const TLorentzVector& mother) { // Half of the energy per pair of the colliding nucleons. @@ -190,7 +190,7 @@ struct ExclusiveTwoProtons { std::vector rawProtonTracks; std::vector rawProtonTracksTOF; - for (auto trk : tracks) { + for (const auto& trk : tracks) { if (!trk.isPVContributor()) { continue; } diff --git a/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx b/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx index 7d8e3ed9326..def67ca1bd0 100644 --- a/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx +++ b/PWGUD/Tasks/exclusiveTwoProtonsSG.cxx @@ -53,7 +53,7 @@ struct ExclusiveTwoProtonsSG { // defining histograms using histogram registry HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; //_____________________________________________________________________________ - Double_t CosThetaHelicityFrame(TLorentzVector posDaughter, TLorentzVector negDaughter, TLorentzVector mother) + Double_t CosThetaHelicityFrame(const TLorentzVector& posDaughter, const TLorentzVector& negDaughter, const TLorentzVector& mother) { Double_t HalfSqrtSnn = 2680.; @@ -80,7 +80,7 @@ struct ExclusiveTwoProtonsSG { return CosThetaHE; } //------------------------------------------------------------------------------------------------------ - Double_t PhiHelicityFrame(TLorentzVector posDaughter, TLorentzVector negDaughter, TLorentzVector mother) + Double_t PhiHelicityFrame(const TLorentzVector& posDaughter, const TLorentzVector& negDaughter, const TLorentzVector& mother) { // Half of the energy per pair of the colliding nucleons. @@ -211,7 +211,7 @@ struct ExclusiveTwoProtonsSG { std::vector rawProtonTracks; std::vector rawProtonTracksTOF; - for (auto trk : tracks) { + for (const auto& trk : tracks) { if (!trk.isPVContributor()) { continue; } diff --git a/PWGUD/Tasks/flowCorrelationsUpc.cxx b/PWGUD/Tasks/flowCorrelationsUpc.cxx index 8ed0140fa95..b4d5e1ee93b 100644 --- a/PWGUD/Tasks/flowCorrelationsUpc.cxx +++ b/PWGUD/Tasks/flowCorrelationsUpc.cxx @@ -11,19 +11,16 @@ /// \file flowCorrelationsUpc.cxx /// \brief Provides a sparse with usefull two particle correlation info -/// \author Yongxi Du (yongxi.du@cern.ch), Mingrui Zhao (mingrui.zhao@cern.ch, mingrui.zhao@mail.labz0.org) -/// copied from Thor Jensen (thor.kjaersgaard.jensen@cern.ch) and Debojit Sarkar (debojit.sarkar@cern.ch) +/// \author Yongxi Du (yongxi.du@cern.ch), Mingrui Zhao (mingrui.zhao@cern.ch, mingrui.zhao@mail.labz0.org), Zhiyong Lu (zhiyong.lu@cern.ch) #include "PWGCF/Core/CorrelationContainer.h" -#include "PWGCF/GenericFramework/Core/GFW.h" #include "PWGCF/GenericFramework/Core/GFWWeights.h" +#include "PWGCF/TwoParticleCorrelations/Core/DihadronContainer.h" #include "PWGUD/Core/SGSelector.h" #include "PWGUD/DataModel/UDTables.h" +#include "PWGUD/DataModel/UDTruegapsideTables.h" -#include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" -#include "Common/Core/TrackSelection.h" -#include "Common/Core/TrackSelectionDefaults.h" #include #include @@ -37,106 +34,29 @@ #include #include #include -#include #include -#include #include #include -#include -#include -#include -#include +#include #include -#include - -#include - -#include #include #include #include #include #include -#include -#include #include -#include #include #include -namespace o2::aod -{ -namespace flowcorrupc -{ -DECLARE_SOA_COLUMN(Multiplicity, multiplicity, int); -DECLARE_SOA_COLUMN(Truegapside, truegapside, int); -} // namespace flowcorrupc -DECLARE_SOA_TABLE(Multiplicity, "AOD", "MULTIPLICITY", - flowcorrupc::Multiplicity); -DECLARE_SOA_TABLE(Truegapside, "AOD", "TRUEGAPSIDE", flowcorrupc::Truegapside); -} // namespace o2::aod - using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::math; // define the filtered collisions and tracks -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; - -struct CalcNchUpc { - O2_DEFINE_CONFIGURABLE(cfgZVtxCut, float, 10.0f, "Accepted z-vertex range") - O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.1f, "minimum accepted track pT") - O2_DEFINE_CONFIGURABLE(cfgEtaCut, float, 0.9f, "Eta cut") - O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") - - // Added UPC Cuts - SGSelector sgSelector; - Configurable cfgCutFV0{"cfgCutFV0", 50., "FV0A threshold"}; - Configurable cfgCutFT0A{"cfgCutFT0A", 150., "FT0A threshold"}; - Configurable cfgCutFT0C{"cfgCutFT0C", 50., "FT0C threshold"}; - Configurable cfgCutZDC{"cfgCutZDC", 10., "ZDC threshold"}; - - // Filter trackFilter = (nabs(aod::track::eta) < cfgEtaCut) && (aod::track::pt > cfgPtCutMin) && (aod::track::pt < cfgPtCutMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)); - - using UdTracks = soa::Join; - using UdTracksFull = soa::Join; - using UDCollisionsFull = soa::Join; - - Produces multiplicityNch; - Produces truegapside; - - HistogramRegistry registry{"registry"}; - - void init(InitContext&) - { - AxisSpec axisNch = {100, 0, 100}; - AxisSpec axisVrtx = {10, -10, 10}; - // AxisSpec axisgap = {12, -6, 6}; - // std::vector trueGapBins = {-2, -1, 0, 1, 2, 3}; - // AxisSpec axisgap = {trueGapBins, "true gap side"}; - - std::vector binEdges = {-1.5, -0.5, 0.5, 1.5, 2.5, 3.5}; - AxisSpec axisgap = {binEdges, "true gap side"}; - registry.add("truegap", "truegap", {HistType::kTH1D, {axisgap}}); - - registry.add("Ncharge", "N_{charge}", {HistType::kTH1D, {axisNch}}); - registry.add("zVtx_all", "zVtx_all", {HistType::kTH1D, {axisVrtx}}); - registry.add("Nch_vs_zVtx", "Nch vs zVtx", {HistType::kTH2D, {axisVrtx, axisNch}}); - // registry.add("truegap", "truegap", {HistType::kTH1D, {axisgap}}); - } - - void process(UDCollisionsFull::iterator const& collision, UdTracksFull const& tracks) - { - multiplicityNch(tracks.size()); - truegapside(sgSelector.trueGap(collision, cfgCutFV0, cfgCutFT0A, cfgCutFT0C, cfgCutZDC)); - // LOG(info) << "truegapside=" << sgSelector.trueGap(collision, cfgCutFV0, cfgCutFT0A, cfgCutFT0C, cfgCutZDC); - registry.fill(HIST("Ncharge"), tracks.size()); - registry.fill(HIST("zVtx_all"), collision.posZ()); - } -}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) struct FlowCorrelationsUpc { O2_DEFINE_CONFIGURABLE(cfgZVtxCut, float, 10.0f, "Accepted z-vertex range") @@ -153,28 +73,29 @@ struct FlowCorrelationsUpc { O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.02, "Merging cut on track merge") O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") - O2_DEFINE_CONFIGURABLE(cfgIsGoodItsLayers, bool, false, "whether choose itslayers") O2_DEFINE_CONFIGURABLE(cfgDcaxy, bool, true, "choose dcaxy") O2_DEFINE_CONFIGURABLE(cfgDcaz, bool, false, "choose dcaz") - O2_DEFINE_CONFIGURABLE(cfgDcazCut, float, 10.0, "dcaz cut") - O2_DEFINE_CONFIGURABLE(cfgItsClusterSize, unsigned int, 5, "ITS cluster size") + O2_DEFINE_CONFIGURABLE(cfgDcazCut, float, 2.0, "dcaz cut") O2_DEFINE_CONFIGURABLE(cfgMaxTPCChi2NCl, int, 4, "tpcchi2") - O2_DEFINE_CONFIGURABLE(cfgEvSelOccupancy, bool, true, "Occupancy cut") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyHigh, int, 1000, "High cut on TPC occupancy") - O2_DEFINE_CONFIGURABLE(cfgCutOccupancyLow, int, 0, "Low cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgGapSide, int, 0, "choose one side 0:A; 1:C") O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum number of crossed TPC Rows") O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum number of found TPC clusters") O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum number of ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCChi2NCl, int, 4, "max chi2 per TPC clusters") O2_DEFINE_CONFIGURABLE(cfgGlobalTrack, bool, true, "require TPC+ITS track") - O2_DEFINE_CONFIGURABLE(cfgUseNchCorrected, bool, true, "use corrected Nch for X axis") - O2_DEFINE_CONFIGURABLE(cfgOutputNUAWeights, bool, false, "Fill and output NUA weights") - O2_DEFINE_CONFIGURABLE(cfgOutputNUAWeightsRefPt, bool, false, "NUA weights are filled in ref pt bins") - O2_DEFINE_CONFIGURABLE(cfgOutputNUAWeightsRunbyRun, bool, false, "NUA weights are filled run-by-run") + O2_DEFINE_CONFIGURABLE(cfgUseNchEffCorrected, bool, false, "use corrected Nch for X axis by efficiency correction") + O2_DEFINE_CONFIGURABLE(cfgUseNchRoughMCCorrected, bool, false, "use corrected Nch for X axis by Nch Reco vs True") + O2_DEFINE_CONFIGURABLE(cfgNchRoughMCFunction, std::string, "1.1*x + 0.3", "Function for Nch Reco vs True"); O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") - O2_DEFINE_CONFIGURABLE(cfgAcceptance, std::string, "", "CCDB path to acceptance object") O2_DEFINE_CONFIGURABLE(cfgUseEventWeights, bool, false, "Use event weights for mixed event") - O2_DEFINE_CONFIGURABLE(cfgLocalEfficiency, bool, false, "Use local efficiency object") + O2_DEFINE_CONFIGURABLE(cfgRctFlagEnabled, bool, false, "use run condition table flag") + O2_DEFINE_CONFIGURABLE(cfgRctFlagIndex, int, 1, "1: isCBTOk; 2:isCBTZdcOk; 3: isCBTHadronOk; 4:isCBTHadronZdcOk ") + O2_DEFINE_CONFIGURABLE(cfgIRMaxCut, double, 50, "maximum interaction rate for UPC events") + O2_DEFINE_CONFIGURABLE(cfgZdcTime, bool, true, "choose zdc time cut") + O2_DEFINE_CONFIGURABLE(cfgZdcTimeCut, float, 2.0, "zdc time cut") + O2_DEFINE_CONFIGURABLE(cfgSbp, bool, true, "choose sbp") + O2_DEFINE_CONFIGURABLE(cfgvtxITSTPC, bool, true, "choose vtxITSTPC") + O2_DEFINE_CONFIGURABLE(cfgItsROFb, bool, true, "choose itsROFb") + O2_DEFINE_CONFIGURABLE(cfgTfb, bool, true, "choose tfb") ConfigurableAxis axisVertex{"axisVertex", {10, -10, 10}, "vertex axis for histograms"}; ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; @@ -184,55 +105,47 @@ struct FlowCorrelationsUpc { ConfigurableAxis axisDeltaEta{"axisDeltaEta", {40, -2, 2}, "delta eta axis for histograms"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt associated axis for histograms"}; - ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80, 100}, "multiplicity / centrality axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50}, "multiplicity / centrality axis for histograms"}; ConfigurableAxis vtxMix{"vtxMix", {VARIABLE_WIDTH, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, "vertex axis for mixed event histograms"}; ConfigurableAxis multMix{"multMix", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 50, 60, 80, 100}, "multiplicity / centrality axis for mixed event histograms"}; - - ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; - ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; - ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0}, "pt axis for efficiency histograms"}; ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; // Added UPC Cuts SGSelector sgSelector; - Configurable cfgCutFV0{"cfgCutFV0", 50., "FV0A threshold"}; - Configurable cfgCutFT0A{"cfgCutFT0A", 150., "FT0A threshold"}; - Configurable cfgCutFT0C{"cfgCutFT0C", 50., "FT0C threshold"}; - Configurable cfgCutZDC{"cfgCutZDC", 10., "ZDC threshold"}; ConfigurableAxis axisIndependent{"axisIndependent", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60}, "X axis for histograms"}; - ConfigurableAxis axisNch{"axisNch", {300, 0, 300}, "N_{ch}"}; // Corrections - TH1D* mEfficiency = nullptr; - GFWWeights* mAcceptance = nullptr; + TH3D* mEfficiency = nullptr; bool correctionsLoaded = false; + TF1* fnchRoughMCFunc = nullptr; // make the filters and cuts. Filter trackFilter = (aod::udtrack::isPVContributor == true); - Filter collisionFilter = ((aod::udcollision::gapSide == (uint8_t)1 || aod::udcollision::gapSide == (uint8_t)0) && (cfgIfVertex == false || aod::collision::posZ < cfgZVtxCut) && (aod::udcollision::occupancyInTime > 0 && aod::udcollision::occupancyInTime < cfgCutOccupancyHigh) && (aod::flowcorrupc::truegapside == 1 || aod::flowcorrupc::truegapside == 0)); + Filter collisionFilter = ifnode( + cfgGapSide.node() == 0, + ((aod::udcollision::gapSide == static_cast(0)) && + (aod::upcservice::truegapside == 0)), + ((aod::udcollision::gapSide == static_cast(1)) && + (aod::upcservice::truegapside == 1))); // Connect to ccdb - Service ccdb; + Service ccdb{}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + // global variables OutputObj fWeights{GFWWeights("weights")}; - OutputObj fWeightsMc{GFWWeights("weightsMC")}; - - TAxis* fPtAxis = nullptr; - int lastRunNumber = -1; - std::vector runNumbers; - std::map> th3sPerRun; // map of TH3 histograms for all runs std::vector efficiencyCache; + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; - using UdTracks = soa::Filtered>; using UdTracksFull = soa::Filtered>; - - using UDCollisionsFull = soa::Filtered>; + using UDCollisionsFull = soa::Filtered>; // Define the outputs - OutputObj same{Form("sameEvent_%i_%i", static_cast(cfgMinMult), static_cast(cfgMaxMult))}; - OutputObj mixed{Form("mixedEvent_%i_%i", static_cast(cfgMinMult), static_cast(cfgMaxMult))}; - + OutputObj sameDihadron{"sameEvent"}; + OutputObj mixedDihadron{"mixedEvent"}; HistogramRegistry registry{"registry"}; void init(InitContext&) @@ -248,36 +161,24 @@ struct FlowCorrelationsUpc { registry.add("pT", "pT", {HistType::kTH1D, {axisPtTrigger}}); registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); - registry.add("Nch_vs_zVtx", "Nch vs zVtx", {HistType::kTH2D, {axisVertex, axisMultiplicity}}); - registry.add("Nch_same", "Nch same event", {HistType::kTH1D, {axisMultiplicity}}); - registry.add("Nch_mixed", "Nch mixed event", {HistType::kTH1D, {axisMultiplicity}}); registry.add("EtaCorrected", "Eta corrected", {HistType::kTH1D, {axisEta}}); registry.add("pTCorrected", "pT corrected", {HistType::kTH1D, {axisPtTrigger}}); registry.add("Trig_hist", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisIndependent, axisPtTrigger}}}); - registry.add("eventcont", "bin", {HistType::kTH1F, {{10, 0, 10, "bin"}}}); // histogram to see how many events are in the same and mixed event - registry.add("deltaEta_deltaPhi_same", "deltaeta-deltaphi", {HistType::kTH2D, {axisDeltaEta, axisDeltaPhi}}); // histogram to check the delta eta and delta phi distribution - registry.add("deltaEta_deltaPhi_mixed", "deltaeta-deltaphi", {HistType::kTH2D, {axisDeltaEta, axisDeltaPhi}}); // histogram to check the delta eta and delta phi distribution + registry.add("eventcont", "bin", {HistType::kTH1F, {{10, 0, 10, "bin"}}}); // histogram to see how many events are in the same and mixed event + registry.get(HIST("eventcont"))->GetXaxis()->SetBinLabel(4, "same"); + registry.get(HIST("eventcont"))->GetXaxis()->SetBinLabel(5, "mix pair"); + registry.add("deltaPhi_deltaEta_same", "deltaphi-deltaeta", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // histogram to check the delta eta and delta phi distribution + registry.add("deltaPhi_deltaEta_mixed", "deltaphi-deltaeta", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEta}}); // histogram to check the delta eta and delta phi distribution registry.add("Nch_raw_vs_independent", "Raw vs Independent", {HistType::kTH2D, {axisMultiplicity, axisIndependent}}); + registry.add("interactionRate", "kHz", {HistType::kTH1F, {{50, 0, 50, "kHz"}}}); + registry.add("ZDCEnergy", "ZNA; ZNC; Count", {HistType::kTH2D, {{100, 0, 100}, {100, 0, 100}}}); + registry.add("ZDCTime", "ZNA; ZNC; Count", {HistType::kTH2D, {{100, -10, 10}, {100, -10, 10}}}); + registry.add("neutronClass", "ZNA; ZNC; Count", {HistType::kTH2D, {{2, 0, 2}, {2, 0, 2}}}); - // if (doprocessSim) { - // registry.add("eventCounterMC", "Number of MC Events;; Count", {HistType::kTH1D, {{5, 0, 5}}}); - // registry.add("hVtxZMC", "Vexter Z distribution (MC)", {HistType::kTH1D, {axisVertex}}); - // registry.add("hMultMC", "Multiplicity distribution (MC)", {HistType::kTH1D, {{3000, 0.5, 3000.5}}}); - // registry.add("numberOfTracksMC", "Number of MC tracks;; Count", {HistType::kTH1D, {{3000, 0.5, 3000.5}}}); - // } - - o2::framework::AxisSpec axis = axisPtTrigger; - int nPtBins = axis.binEdges.size() - 1; - double* ptBins = &(axis.binEdges)[0]; - fPtAxis = new TAxis(nPtBins, ptBins); - - if (cfgOutputNUAWeights) { - fWeights->setPtBins(nPtBins, ptBins); - fWeights->init(true, false); - fWeightsMc->setPtBins(nPtBins, ptBins); - fWeightsMc->init(true, false); + if (cfgUseNchRoughMCCorrected) { + fnchRoughMCFunc = new TF1("fnchRoughMCFunc", cfgNchRoughMCFunction->c_str(), 0, 100); } std::vector corrAxis = {{axisSample, "Sample"}, @@ -287,20 +188,11 @@ struct FlowCorrelationsUpc { {axisPtAssoc, "p_{T} (GeV/c)"}, {axisDeltaPhi, "#Delta#varphi (rad)"}, {axisDeltaEta, "#Delta#eta"}}; - std::vector effAxis = { - {axisVertexEfficiency, "z-vtx (cm)"}, - {axisPtEfficiency, "p_{T} (GeV/c)"}, - {axisEtaEfficiency, "#eta"}, - }; - std::vector userAxis; - same.setObject(new CorrelationContainer(Form("sameEvent_%i_%i", static_cast(cfgMinMult), static_cast(cfgMaxMult)), Form("sameEvent_%i_%i", static_cast(cfgMinMult), static_cast(cfgMaxMult)), corrAxis, effAxis, userAxis)); - mixed.setObject(new CorrelationContainer(Form("mixedEvent_%i_%i", static_cast(cfgMinMult), static_cast(cfgMaxMult)), Form("mixedEvent_%i_%i", static_cast(cfgMinMult), static_cast(cfgMaxMult)), corrAxis, effAxis, userAxis)); + sameDihadron.setObject(new DihadronContainer("sameEvent", "sameEvent", corrAxis)); + mixedDihadron.setObject(new DihadronContainer("mixedEvent", "mixedEvent", corrAxis)); + LOGF(info, "End of init"); } - enum EventType { - SameEvent = 1, - MixedEvent = 3 - }; template float getDPhiStar(TTrack const& track1, TTrack const& track2, float radius, int runnum, float phi1, float phi2) @@ -320,19 +212,141 @@ struct FlowCorrelationsUpc { float dPhiStar = phi1 - phi2 - charge1 * fbSign * std::asin(0.075 * radius / pt1) + charge2 * fbSign * std::asin(0.075 * radius / pt2); - if (dPhiStar > constants::math::PI) + if (dPhiStar > constants::math::PI) { dPhiStar = constants::math::TwoPI - dPhiStar; + } return dPhiStar; } + template + bool isGoodRctFlag(const C& collision) + { + switch (cfgRctFlagIndex) { + case 1: + return sgSelector.isCBTOk(collision); + case 2: + return sgSelector.isCBTZdcOk(collision); + case 3: + return sgSelector.isCBTHadronOk(collision); + case 4: + return sgSelector.isCBTHadronZdcOk(collision); + default: + return true; + } + } + + template + // zdc time cut + bool zdcTimeCut(const C& collision) + { + if (!cfgZdcTime) { + return true; + } + int neutronClass = -1; + float energyCommonZNA = collision.energyCommonZNA(), energyCommonZNC = collision.energyCommonZNC(); + float timeZNA = collision.timeZNA(), timeZNC = collision.timeZNC(); + if (std::isinf(energyCommonZNA)) { + energyCommonZNA = -999; + } + if (std::isinf(energyCommonZNC)) { + energyCommonZNC = -999; + } + if (std::isinf(timeZNA)) { + timeZNA = -999; + } + if (std::isinf(timeZNC)) { + timeZNC = -999; + } + registry.fill(HIST("ZDCEnergy"), energyCommonZNC, energyCommonZNA); + registry.fill(HIST("ZDCTime"), timeZNC, timeZNA); + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { + neutronClass = 0; + registry.fill(HIST("neutronClass"), 0, 0); + } + if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { + neutronClass = 2; + registry.fill(HIST("neutronClass"), 1, 0); + } + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 1; + registry.fill(HIST("neutronClass"), 0, 1); + } + if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 3; + registry.fill(HIST("neutronClass"), 1, 1); + } + if (cfgZdcTime) { + // reject 0n0n and XnXn + if (neutronClass == 0 || neutronClass == 3) { // o2-linter: disable=magic-number (ZDC time cut) + return false; + } + // if A or C gap is requested, keep corresponding neutron class + if (cfgGapSide == 0 || cfgGapSide == 1) { + if ((cfgGapSide == 0 && neutronClass == 1) || (cfgGapSide == 1 && neutronClass == 2)) { // o2-linter: disable=magic-number (ZDC time cut) + // accepted + } else { + return false; + } + } + } + return true; + } + + template + bool eventSelected(const C& collision) + { + if (cfgIfVertex && std::abs(collision.posZ()) > cfgZVtxCut) { + return false; + } + if (cfgvtxITSTPC && !collision.vtxITSTPC()) { + return false; + } + + if (cfgSbp && !collision.sbp()) { + return false; + } + + if (cfgItsROFb && !collision.itsROFb()) { + return false; + } + + if (cfgTfb && !collision.tfb()) { + return false; + } + + if (collision.hadronicRate() > cfgIRMaxCut) { + return false; + } + + if (cfgRctFlagEnabled) { + if (!isGoodRctFlag(collision)) { // check RCT flags + return false; + } + } + + if (!zdcTimeCut(collision)) { + return false; + } + + return true; + } + template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { // registry.fill(HIST("hTrackCount"), 0.5); + auto momentum = std::array{track.px(), track.py(), track.pz()}; + double eta = RecoDecay::eta(momentum); // UPC selection if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { return false; } + if (std::fabs(eta) > cfgEtaCut) { + return false; + } + if (cfgGlobalTrack && !(track.hasITS() && track.hasTPC())) { + return false; + } // registry.fill(HIST("hTrackCount"), 1.5); if (cfgDcaz && !(std::fabs(track.dcaZ()) < cfgDcazCut)) { return false; @@ -355,38 +369,24 @@ struct FlowCorrelationsUpc { } auto tpcClu = track.tpcNClsFindable() - track.tpcNClsFindableMinusFound(); if (tpcClu < cfgCutTPCclu) { + // NOLINTNEXTLINE(readability-simplify-boolean-expr) return false; } // registry.fill(HIST("hTrackCount"), 5.5); return true; } - void createOutputObjectsForRun(int runNumber) - { - const AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; - std::shared_ptr histPhiEtaVtxz = registry.add(Form("%d/hPhiEtaVtxz", runNumber), ";#varphi;#eta;v_{z}", {HistType::kTH3D, {axisPhi, {64, -1.8, 1.8}, {40, -10, 10}}}); - th3sPerRun.insert(std::make_pair(runNumber, histPhiEtaVtxz)); - } - void loadCorrections(uint64_t timestamp) { if (correctionsLoaded) { return; } - if (cfgAcceptance.value.empty() == false) { - mAcceptance = ccdb->getForTimeStamp(cfgAcceptance, timestamp); - if (mAcceptance) { - LOGF(info, "Loaded acceptance weights from %s (%p)", cfgAcceptance.value.c_str(), (void*)mAcceptance); - } else { - LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgAcceptance.value.c_str(), (void*)mAcceptance); - } - } - if (cfgEfficiency.value.empty() == false) { - mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + if (!cfgEfficiency.value.empty()) { + mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } correctionsLoaded = true; } @@ -402,72 +402,42 @@ struct FlowCorrelationsUpc { } else { eff = 1.0; } - if (eff <= 0) - return false; - weight_nue = 1. / eff; - return true; - } - - bool setCurrentParticleWeights(float& weight_nue, float& weight_nua, float phi, float eta, float pt, float vtxz) - { - float eff = 1.; - if (mEfficiency) - eff = mEfficiency->GetBinContent(mEfficiency->FindBin(pt)); - else - eff = 1.0; - if (eff == 0) + if (eff <= 0) { return false; + } weight_nue = 1. / eff; - - if (mAcceptance) - weight_nua = mAcceptance->getNUA(phi, eta, vtxz); - else - weight_nua = 1; return true; } - // fill multiple histograms template - void fillYield(TCollision collision, TTracks tracks, int runNumber, float vtxz) // function to fill the yield and etaphi histograms. + void fillYield(const TCollision& collision, const TTracks& tracks, float vtxz) // function to fill the yield and etaphi histograms. { registry.fill(HIST("Nch"), tracks.size()); registry.fill(HIST("zVtx"), collision.posZ()); for (auto const& track1 : tracks) { - if (!trackSelected(track1)) + if (!trackSelected(track1)) { continue; + } auto momentum = std::array{track1.px(), track1.py(), track1.pz()}; double pt = RecoDecay::pt(momentum); double phi = RecoDecay::phi(momentum); double eta = RecoDecay::eta(momentum); float weff = 1.; - if (!getEfficiencyCorrection(weff, eta, pt, vtxz)) + if (!getEfficiencyCorrection(weff, eta, pt, vtxz)) { continue; + } registry.fill(HIST("Phi"), phi); registry.fill(HIST("Eta"), eta); registry.fill(HIST("pT"), pt); registry.fill(HIST("EtaCorrected"), eta, weff); registry.fill(HIST("pTCorrected"), pt, weff); - - if (cfgOutputNUAWeights) { - if (cfgOutputNUAWeightsRefPt) { - if (pt > cfgPtCutMin && pt < cfgPtCutMax) { - fWeights->fill(phi, eta, vtxz, pt, 0., 0); - if (cfgOutputNUAWeightsRunbyRun) - th3sPerRun[runNumber]->Fill(phi, eta, vtxz); - } - } else { - fWeights->fill(phi, eta, vtxz, pt, 0., 0); - if (cfgOutputNUAWeightsRunbyRun) - th3sPerRun[runNumber]->Fill(phi, eta, vtxz); - } - } } } - template - void fillCorrelations(TTracks tracks1, TTracks tracks2, float posZ, int system, int runnum, float vtxz, float eventWeight, double independent) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + template + void fillCorrelations(const TTracks& tracks1, const TTracks& tracks2, float posZ, int system, int runnum, float vtxz, float eventWeight, double independent) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms { if (mEfficiency) { @@ -483,62 +453,57 @@ struct FlowCorrelationsUpc { } } - int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + const int fSampleIndex = static_cast(gRandom->Uniform(0., cfgSampleSize)); // loop over all tracks for (auto const& track1 : tracks1) { - if (!trackSelected(track1)) + if (!trackSelected(track1)) { continue; + } - auto momentum = std::array{track1.px(), track1.py(), track1.pz()}; - double pt1 = RecoDecay::pt(momentum); - double phi1 = RecoDecay::phi(momentum); - double eta1 = RecoDecay::eta(momentum); + auto momentum1 = std::array{track1.px(), track1.py(), track1.pz()}; + double pt1 = RecoDecay::pt(momentum1); + double phi1 = RecoDecay::phi(momentum1); + double eta1 = RecoDecay::eta(momentum1); - // 计算track1的权重 - float weff1 = 1., wacc1 = 1.; - if (!setCurrentParticleWeights(weff1, wacc1, phi1, eta1, pt1, vtxz)) + float weff1 = 1.; + if (!getEfficiencyCorrection(weff1, eta1, pt1, posZ)) continue; - if (system == SameEvent) { - registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, independent, pt1, eventWeight * weff1 * wacc1); + registry.fill(HIST("Trig_hist"), fSampleIndex, posZ, independent, pt1, eventWeight * weff1); } for (auto const& track2 : tracks2) { - if (!trackSelected(track2)) + if (!trackSelected(track2)) { continue; + } - if (track1.globalIndex() == track2.globalIndex()) + if (track1.globalIndex() == track2.globalIndex()) { continue; - if (system == SameEvent && cfgUsePtOrder && pt1 <= track2.pt()) + } + if (system == SameEvent && cfgUsePtOrder && pt1 <= track2.pt()) { continue; - if (system == MixedEvent && cfgUsePtOrderInMixEvent && pt1 <= track2.pt()) + } + if (system == MixedEvent && cfgUsePtOrderInMixEvent && pt1 <= track2.pt()) { continue; + } - auto momentum = std::array{track2.px(), track2.py(), track2.pz()}; - double pt2 = RecoDecay::pt(momentum); - double phi2 = RecoDecay::phi(momentum); - double eta2 = RecoDecay::eta(momentum); + auto momentum2 = std::array{track2.px(), track2.py(), track2.pz()}; + double pt2 = RecoDecay::pt(momentum2); + double phi2 = RecoDecay::phi(momentum2); + double eta2 = RecoDecay::eta(momentum2); - // 计算track2的权重 - float weff2 = 1., wacc2 = 1.; + float weff2 = 1.; if (mEfficiency) { weff2 = efficiencyCache[track2.filteredIndex()]; } else { getEfficiencyCorrection(weff2, eta2, pt2, vtxz); } - if (mAcceptance) { - wacc2 = mAcceptance->getNUA(phi2, eta2, vtxz); - } else { - wacc2 = 1; - } - float deltaPhi = RecoDecay::constrainAngle(phi1 - phi2, -PIHalf); float deltaEta = eta1 - eta2; - // 计算组合权重 - float weight = eventWeight * weff1 * weff2 * wacc1 * wacc2; + float weight = eventWeight * weff1 * weff2; // Merging cut if (std::abs(deltaEta) < cfgCutMerging) { @@ -548,25 +513,30 @@ struct FlowCorrelationsUpc { bool bIsBelow = false; if (std::abs(dPhiStarLow) < kLimit || std::abs(dPhiStarHigh) < kLimit || dPhiStarLow * dPhiStarHigh < 0) { - for (double rad(cfgRadiusLow); rad < cfgRadiusHigh; rad += 0.01) { + constexpr float kRadiusStep = 0.01f; + const float radiusLow = cfgRadiusLow; + const float radiusHigh = cfgRadiusHigh; + for (int iRadius = 0; radiusLow + static_cast(iRadius) * kRadiusStep < radiusHigh; ++iRadius) { + const float rad = radiusLow + static_cast(iRadius) * kRadiusStep; double dPhiStar = getDPhiStar(track1, track2, rad, runnum, phi1, phi2); if (std::abs(dPhiStar) < kLimit) { bIsBelow = true; break; } } - if (bIsBelow) + if (bIsBelow) { continue; + } } } // fill the right sparse and histograms with weights if (system == SameEvent) { - same->getPairHist()->Fill(step, fSampleIndex, posZ, independent, pt1, pt2, deltaPhi, deltaEta, weight); - registry.fill(HIST("deltaEta_deltaPhi_same"), deltaPhi, deltaEta, weight); + sameDihadron->getCorrHist()->Fill(0, fSampleIndex, posZ, independent, pt1, pt2, deltaPhi, deltaEta, weight); + registry.fill(HIST("deltaPhi_deltaEta_same"), deltaPhi, deltaEta, weight); } else if (system == MixedEvent) { - mixed->getPairHist()->Fill(step, fSampleIndex, posZ, independent, pt1, pt2, deltaPhi, deltaEta, weight); - registry.fill(HIST("deltaEta_deltaPhi_mixed"), deltaPhi, deltaEta, weight); + mixedDihadron->getCorrHist()->Fill(0, fSampleIndex, posZ, independent, pt1, pt2, deltaPhi, deltaEta, weight); + registry.fill(HIST("deltaPhi_deltaEta_mixed"), deltaPhi, deltaEta, weight); } } } @@ -574,7 +544,6 @@ struct FlowCorrelationsUpc { void processSame(UDCollisionsFull::iterator const& collision, UdTracksFull const& tracks) { - // LOG(info) << "Event passed filter: truegapside=" << collision.truegapside(); if (tracks.size() < cfgMinMult || tracks.size() > cfgMaxMult) { return; } @@ -583,21 +552,12 @@ struct FlowCorrelationsUpc { auto currentRunNumber = collision.runNumber(); auto runDuration = ccdb->getRunDuration(currentRunNumber); - loadCorrections(runDuration.first); - - if (cfgOutputNUAWeightsRunbyRun && currentRunNumber != lastRunNumber) { - lastRunNumber = currentRunNumber; - if (std::find(runNumbers.begin(), runNumbers.end(), currentRunNumber) == runNumbers.end()) { - createOutputObjectsForRun(currentRunNumber); - runNumbers.push_back(currentRunNumber); - } - - if (th3sPerRun.find(currentRunNumber) == th3sPerRun.end()) { - LOGF(fatal, "RunNumber %d not found in th3sPerRun", currentRunNumber); - return; - } + if (!eventSelected(collision)) { + return; } + loadCorrections(runDuration.first); + registry.fill(HIST("eventcont"), 3.5); //-----------independent--------------- @@ -605,8 +565,9 @@ struct FlowCorrelationsUpc { double nTracksCorrected = 0.; for (const auto& track : tracks) { - if (!trackSelected(track)) + if (!trackSelected(track)) { continue; + } auto momentum = std::array{track.px(), track.py(), track.pz()}; double pt = RecoDecay::pt(momentum); @@ -614,7 +575,7 @@ struct FlowCorrelationsUpc { nTracksRaw += 1.; - if (cfgUseNchCorrected) { + if (cfgUseNchEffCorrected) { float weff = 1.; if (getEfficiencyCorrection(weff, eta, pt, vtxz)) { nTracksCorrected += weff; @@ -622,25 +583,23 @@ struct FlowCorrelationsUpc { } } registry.fill(HIST("Nch_raw_vs_independent"), nTracksRaw, nTracksCorrected); + registry.fill(HIST("interactionRate"), collision.hadronicRate()); double independent = nTracksRaw; - if (cfgUseNchCorrected) { + if (cfgUseNchEffCorrected) { independent = nTracksCorrected; + } else if (cfgUseNchRoughMCCorrected) { + independent = fnchRoughMCFunc->Eval(nTracksRaw); } - fillYield(collision, tracks, currentRunNumber, vtxz); + fillYield(collision, tracks, vtxz); - fillCorrelations( - tracks, tracks, collision.posZ(), SameEvent, - currentRunNumber, vtxz, 1.0f, independent); + fillCorrelations(tracks, tracks, collision.posZ(), SameEvent, currentRunNumber, vtxz, 1.0f, independent); } PROCESS_SWITCH(FlowCorrelationsUpc, processSame, "Process same event", true); - // event mixing - SliceCache cache; // using MixedBinning = ColumnBinningPolicy; - // the process for filling the mixed events void processMixed(UDCollisionsFull const& collisions, UdTracksFull const& tracks) { @@ -651,7 +610,7 @@ struct FlowCorrelationsUpc { }; using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}, true}; + MixedBinning binningOnVtxAndMult{{getTracksSize}, {vtxMix, multMix}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; @@ -661,6 +620,9 @@ struct FlowCorrelationsUpc { tracks2.size() < cfgMinMult || tracks2.size() > cfgMaxMult) { continue; } + if (!eventSelected(collision1) || !eventSelected(collision2)) { + continue; + } auto runDuration1 = ccdb->getRunDuration(collision1.runNumber()); loadCorrections(runDuration1.first); @@ -671,8 +633,9 @@ struct FlowCorrelationsUpc { double nTracksCorrected = 0.; for (const auto& track : tracks1) { - if (!trackSelected(track)) + if (!trackSelected(track)) { continue; + } auto momentum = std::array{track.px(), track.py(), track.pz()}; double pt = RecoDecay::pt(momentum); @@ -680,7 +643,7 @@ struct FlowCorrelationsUpc { nTracksRaw += 1.; - if (cfgUseNchCorrected) { + if (cfgUseNchEffCorrected) { float weff = 1.; if (getEfficiencyCorrection(weff, eta, pt, collision1.posZ())) { nTracksCorrected += weff; @@ -689,8 +652,10 @@ struct FlowCorrelationsUpc { } double independent = nTracksRaw; - if (cfgUseNchCorrected) { + if (cfgUseNchEffCorrected) { independent = nTracksCorrected; + } else if (cfgUseNchRoughMCCorrected) { + independent = fnchRoughMCFunc->Eval(nTracksRaw); } float eventWeight = 1.0f; @@ -698,9 +663,7 @@ struct FlowCorrelationsUpc { eventWeight = 1.0f / it.currentWindowNeighbours(); } - fillCorrelations( - tracks1, tracks2, collision1.posZ(), MixedEvent, - collision1.runNumber(), collision1.posZ(), eventWeight, independent); + fillCorrelations(tracks1, tracks2, collision1.posZ(), MixedEvent, collision1.runNumber(), collision1.posZ(), eventWeight, independent); } } PROCESS_SWITCH(FlowCorrelationsUpc, processMixed, "Process mixed events", true); @@ -709,7 +672,6 @@ struct FlowCorrelationsUpc { WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), adaptAnalysisTask(cfgc), }; } diff --git a/PWGUD/Tasks/flowCumulantsUpc.cxx b/PWGUD/Tasks/flowCumulantsUpc.cxx index 0a85386e44c..01bf6d76a7c 100644 --- a/PWGUD/Tasks/flowCumulantsUpc.cxx +++ b/PWGUD/Tasks/flowCumulantsUpc.cxx @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -70,15 +71,17 @@ struct FlowCumulantsUpc { O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") O2_DEFINE_CONFIGURABLE(cfgIfVertex, bool, false, "choose vertex or not") - O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.1f, "Minimal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "Minimal pT for poi tracks") O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 10.0f, "Maximal pT for poi tracks") - O2_DEFINE_CONFIGURABLE(cfgCutPtRefMin, float, 0.1f, "Minimal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtRefMin, float, 0.2f, "Minimal pT for ref tracks") O2_DEFINE_CONFIGURABLE(cfgCutPtRefMax, float, 3.0f, "Maximal pT for ref tracks") O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "max Eta range for tracks") O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum number of crossed TPC Rows") O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum number of found TPC clusters") O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum number of ITS clusters") - O2_DEFINE_CONFIGURABLE(cfgCutTPCChi2NCl, int, 4, "max chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutTPCChi2NCl, float, 4.0f, "max chi2 per TPC clusters") O2_DEFINE_CONFIGURABLE(cfgNbootstrap, int, 30, "Number of subsamples") O2_DEFINE_CONFIGURABLE(cfgOutputNUAWeights, bool, false, "Fill and output NUA weights") O2_DEFINE_CONFIGURABLE(cfgOutputNUAWeightsRefPt, bool, false, "NUA weights are filled in ref pt bins") @@ -92,10 +95,20 @@ struct FlowCumulantsUpc { O2_DEFINE_CONFIGURABLE(cfgGapSideC, bool, false, "only pass gapside C, otherwise pass A+C") O2_DEFINE_CONFIGURABLE(cfgGlobalTrack, bool, true, "require TPC+ITS track") O2_DEFINE_CONFIGURABLE(cfgUseNchCorrected, bool, true, "use corrected Nch for X axis") + O2_DEFINE_CONFIGURABLE(cfgRctFlagEnabled, bool, true, "use run condition table flag") + O2_DEFINE_CONFIGURABLE(cfgRctFlagIndex, int, 1, "1: isCBTOk; 2:isCBTZdcOk; 3: isCBTHadronOk; 4:isCBTHadronZdcOk ") O2_DEFINE_CONFIGURABLE(cfgDcaxy, bool, true, "choose dcaxy") O2_DEFINE_CONFIGURABLE(cfgDcaz, bool, false, "choose dcaz") O2_DEFINE_CONFIGURABLE(cfgDcazCut, float, 10.0, "dcaz cut") O2_DEFINE_CONFIGURABLE(cfgConsistentEventFlag, int, 0, "Flag to select consistent events - 0: off, 1: v2{2} gap calculable, 2: v2{4} full calculable, 4: v2{4} gap calculable, 8: v2{4} 3sub calculable") + O2_DEFINE_CONFIGURABLE(cfgZdcTime, bool, false, "choose zdc time cut") + O2_DEFINE_CONFIGURABLE(cfgZdcTimeCut, float, 2.0, "zdc time cut") + O2_DEFINE_CONFIGURABLE(cfgIRMaxCut, double, 50, "maximum interaction rate for UPC events") + O2_DEFINE_CONFIGURABLE(cfgMaxDebugEvents, int, -1, "Maximum number of events for debug (-1: all events)") + O2_DEFINE_CONFIGURABLE(cfgvtxITSTPC, bool, true, "require vertex from ITS+TPC") + O2_DEFINE_CONFIGURABLE(cfgsbp, bool, true, "require sbp") + O2_DEFINE_CONFIGURABLE(cfgitsROFb, bool, true, "require itsROFb") + O2_DEFINE_CONFIGURABLE(cfgtfb, bool, true, "require tfb") Configurable> cfgUserDefineGFWCorr{"cfgUserDefineGFWCorr", std::vector{"refN02 {2} refP02 {-2}", "refN12 {2} refP12 {-2}"}, "User defined GFW CorrelatorConfig"}; Configurable> cfgUserDefineGFWName{"cfgUserDefineGFWName", std::vector{"Ch02Gap22", "Ch12Gap22"}, "User defined GFW Name"}; @@ -142,11 +155,12 @@ struct FlowCumulantsUpc { GFW* fGFWMC = new GFW(); std::vector corrconfigs; std::vector corrconfigsmc; - TAxis* fPtAxis; + TAxis* fPtAxis = nullptr; TRandom3* fRndm = new TRandom3(0); TRandom3* fRndmMc = new TRandom3(0); int lastRunNumber = -1; std::vector runNumbers; + std::map neventsIdBins; std::map> th3sPerRun; // map of TH3 histograms for all runs using UdTracks = soa::Join; @@ -166,13 +180,20 @@ struct FlowCumulantsUpc { // Add some output objects to the histogram registry // Event QA - registry.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{6, 0, 6}}}); + registry.add("hEventCount", "Number of Event;; Count", {HistType::kTH1D, {{13, 0, 13}}}); registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(1, "Filtered event"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(2, "after gapside selection"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(3, "after vertex selection"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(4, "after occupancy"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(5, "after loadcorrection"); - registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(6, "after consistency check"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(2, "after gapside"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(3, "after ZDC timing"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(4, "after Vz"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(5, "after occupancy"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(6, "after load corr"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(7, "after vtxITSTPC"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(8, "after sbp"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(9, "after itsROFb"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(10, "after tfb"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(11, "after rct flag"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(12, "after consistency check"); + registry.get(HIST("hEventCount"))->GetXaxis()->SetBinLabel(13, "after IR cut"); registry.add("hTrackCount", "Number of tracks;; Count", {HistType::kTH1D, {{9, 0, 9}}}); registry.get(HIST("hTrackCount"))->GetXaxis()->SetBinLabel(1, "default UD tracks"); @@ -188,6 +209,9 @@ struct FlowCumulantsUpc { registry.add("hMultWoSel", "Multiplicity distribution", {HistType::kTH1D, {{3000, 0.5, 3000.5}}}); registry.add("hMult", "Multiplicity distribution", {HistType::kTH1D, {{3000, 0.5, 3000.5}}}); // Reco Track QA + registry.add("ZDCEnergy", "ZNA; ZNC; Count", {HistType::kTH2D, {{100, 0, 100}, {100, 0, 100}}}); + registry.add("ZDCTime", "ZNA; ZNC; Count", {HistType::kTH2D, {{100, -10, 10}, {100, -10, 10}}}); + registry.add("neutronClass", "ZNA; ZNC; Count", {HistType::kTH2D, {{2, 0, 2}, {2, 0, 2}}}); registry.add("hPhi", "#phi distribution", {HistType::kTH1D, {axisPhi}}); registry.add("hPhiWeighted", "corrected #phi distribution", {HistType::kTH1D, {axisPhi}}); registry.add("hEta", "#eta distribution", {HistType::kTH1D, {axisEta}}); @@ -201,6 +225,7 @@ struct FlowCumulantsUpc { registry.add("hDCAz", "DCAz after cuts; DCAz (cm); Pt", {HistType::kTH2D, {{200, -0.5, 0.5}, {200, 0, 5}}}); registry.add("hDCAxy", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH2D, {{200, -0.5, 0.5}, {200, 0, 5}}}); registry.add("hTrackCorrection2d", "Correlation table for number of tracks table; uncorrected track; corrected track", {HistType::kTH2D, {axisNch, axisNch}}); + registry.add("interactionRate", "kHz", {HistType::kTH1F, {{50, 0, 50, "kHz"}}}); // Mc track QA registry.add("hPhiMC", "#phi distribution", {HistType::kTH1D, {axisPhi}}); registry.add("hPhiWeightedMC", "corrected #phi distribution", {HistType::kTH1D, {axisPhi}}); @@ -281,10 +306,41 @@ struct FlowCumulantsUpc { oba->Add(new TNamed(Form("Ch10Gap24_pt_%i", i + 1), "Ch10Gap24_pTDiff")); std::vector userDefineGFWCorr = cfgUserDefineGFWCorr; std::vector userDefineGFWName = cfgUserDefineGFWName; + if (userDefineGFWName.size() != userDefineGFWCorr.size()) { + LOGF(fatal, "The GFWConfig names you provided are NOT matching with configurations. userDefineGFWName.size(): %d, userDefineGFWCorr.size(): %d", userDefineGFWName.size(), userDefineGFWCorr.size()); + } + LOGF(info, "User adding FlowContainer Array:"); if (!userDefineGFWCorr.empty() && !userDefineGFWName.empty()) { for (uint i = 0; i < userDefineGFWName.size(); i++) { - oba->Add(new TNamed(userDefineGFWName.at(i).c_str(), userDefineGFWName.at(i).c_str())); + if (userDefineGFWCorr.at(i).find("poi") != std::string::npos) { + LOGF(info, "%d: pT-diff array %s", i, userDefineGFWName.at(i).c_str()); + for (auto iPt = 0; iPt < fPtAxis->GetNbins(); iPt++) + oba->Add(new TNamed(Form("%s_pt_%i", userDefineGFWName.at(i).c_str(), iPt + 1), Form("%s_pTDiff", userDefineGFWName.at(i).c_str()))); + } else { + LOGF(info, "%d: %s", i, userDefineGFWName.at(i).c_str()); + oba->Add(new TNamed(userDefineGFWName.at(i).c_str(), userDefineGFWName.at(i).c_str())); + } + } + } + + // One Y bin for every FlowContainer IDName already stored in oba. + const int nIdNames = oba->GetEntries(); + + const AxisSpec axisIdName{nIdNames, 0.5, static_cast(nIdNames) + 0.5, "ID name"}; + + registry.add("hNeventsVsNch", "Selected events;N_{ch};ID name", {HistType::kTH2D, {axisIndependent, axisIdName}}); + + auto hNeventsVsNch = registry.get(HIST("hNeventsVsNch")); + + for (int i = 0; i < nIdNames; ++i) { + auto* observable = dynamic_cast(oba->At(i)); + if (!observable) { + continue; } + const std::string idName = observable->GetName(); + const int yBin = i + 1; + neventsIdBins[idName] = yBin; + hNeventsVsNch->GetYaxis()->SetBinLabel(yBin, idName.c_str()); } fFC->SetName("FlowContainer"); fFC->SetXAxis(fPtAxis); @@ -318,10 +374,24 @@ struct FlowCumulantsUpc { fGFW->AddRegion("refP", 0.4, maxEta, 1, 1); fGFW->AddRegion("refM", -0.4, 0.4, 1, 1); fGFW->AddRegion("poiN", minEta, -0.4, 1 + fPtAxis->GetNbins(), 2); + fGFW->AddRegion("poiN00", minEta, 0., 1 + fPtAxis->GetNbins(), 2); + fGFW->AddRegion("poiN02", minEta, -0.1, 1 + fPtAxis->GetNbins(), 2); + fGFW->AddRegion("poiN04", minEta, -0.2, 1 + fPtAxis->GetNbins(), 2); + fGFW->AddRegion("poiN06", minEta, -0.3, 1 + fPtAxis->GetNbins(), 2); + fGFW->AddRegion("poiN08", minEta, -0.4, 1 + fPtAxis->GetNbins(), 2); fGFW->AddRegion("poiN10", minEta, -0.5, 1 + fPtAxis->GetNbins(), 2); + fGFW->AddRegion("poiN12", minEta, -0.6, 1 + fPtAxis->GetNbins(), 2); + fGFW->AddRegion("poiN14", minEta, -0.7, 1 + fPtAxis->GetNbins(), 2); fGFW->AddRegion("poifull", minEta, maxEta, 1 + fPtAxis->GetNbins(), 2); fGFW->AddRegion("olN", minEta, -0.4, 1 + fPtAxis->GetNbins(), 4); + fGFW->AddRegion("olN00", minEta, 0., 1 + fPtAxis->GetNbins(), 4); + fGFW->AddRegion("olN02", minEta, -0.1, 1 + fPtAxis->GetNbins(), 4); + fGFW->AddRegion("olN04", minEta, -0.2, 1 + fPtAxis->GetNbins(), 4); + fGFW->AddRegion("olN06", minEta, -0.3, 1 + fPtAxis->GetNbins(), 4); + fGFW->AddRegion("olN08", minEta, -0.4, 1 + fPtAxis->GetNbins(), 4); fGFW->AddRegion("olN10", minEta, -0.5, 1 + fPtAxis->GetNbins(), 4); + fGFW->AddRegion("olN12", minEta, -0.6, 1 + fPtAxis->GetNbins(), 4); + fGFW->AddRegion("olN14", minEta, -0.7, 1 + fPtAxis->GetNbins(), 4); fGFW->AddRegion("olfull", minEta, maxEta, 1 + fPtAxis->GetNbins(), 4); // eta region for MC, can be different from data to study the effect of acceptance @@ -346,10 +416,24 @@ struct FlowCumulantsUpc { fGFWMC->AddRegion("refP", 0.4, maxEta, 1, 1); fGFWMC->AddRegion("refM", -0.4, 0.4, 1, 1); fGFWMC->AddRegion("poiN", minEta, -0.4, 1 + fPtAxis->GetNbins(), 2); + fGFWMC->AddRegion("poiN00", minEta, 0., 1 + fPtAxis->GetNbins(), 2); + fGFWMC->AddRegion("poiN02", minEta, -0.1, 1 + fPtAxis->GetNbins(), 2); + fGFWMC->AddRegion("poiN04", minEta, -0.2, 1 + fPtAxis->GetNbins(), 2); + fGFWMC->AddRegion("poiN06", minEta, -0.3, 1 + fPtAxis->GetNbins(), 2); + fGFWMC->AddRegion("poiN08", minEta, -0.4, 1 + fPtAxis->GetNbins(), 2); fGFWMC->AddRegion("poiN10", minEta, -0.5, 1 + fPtAxis->GetNbins(), 2); + fGFWMC->AddRegion("poiN12", minEta, -0.6, 1 + fPtAxis->GetNbins(), 2); + fGFWMC->AddRegion("poiN14", minEta, -0.7, 1 + fPtAxis->GetNbins(), 2); fGFWMC->AddRegion("poifull", minEta, maxEta, 1 + fPtAxis->GetNbins(), 2); fGFWMC->AddRegion("olN", minEta, -0.4, 1 + fPtAxis->GetNbins(), 4); + fGFWMC->AddRegion("olN00", minEta, 0., 1 + fPtAxis->GetNbins(), 4); + fGFWMC->AddRegion("olN02", minEta, -0.1, 1 + fPtAxis->GetNbins(), 4); + fGFWMC->AddRegion("olN04", minEta, -0.2, 1 + fPtAxis->GetNbins(), 4); + fGFWMC->AddRegion("olN06", minEta, -0.3, 1 + fPtAxis->GetNbins(), 4); + fGFWMC->AddRegion("olN08", minEta, -0.4, 1 + fPtAxis->GetNbins(), 4); fGFWMC->AddRegion("olN10", minEta, -0.5, 1 + fPtAxis->GetNbins(), 4); + fGFWMC->AddRegion("olN12", minEta, -0.6, 1 + fPtAxis->GetNbins(), 4); + fGFWMC->AddRegion("olN14", minEta, -0.7, 1 + fPtAxis->GetNbins(), 4); fGFWMC->AddRegion("olfull", minEta, maxEta, 1 + fPtAxis->GetNbins(), 4); corrconfigs.push_back(fGFW->GetCorrelatorConfig("full {2 -2}", "ChFull22", kFALSE)); @@ -458,6 +542,16 @@ struct FlowCumulantsUpc { fGFW->CreateRegions(); } + void fillNeventsVsNch(const std::string& idName, double independent) + { + const auto it = neventsIdBins.find(idName); + if (it == neventsIdBins.end()) { + return; + } + + registry.fill(HIST("hNeventsVsNch"), independent, static_cast(it->second)); + } + void createOutputObjectsForRun(int runNumber) { const AxisSpec axisPhi{60, 0.0, constants::math::TwoPI, "#varphi"}; @@ -476,6 +570,7 @@ struct FlowCumulantsUpc { val = fGFW->Calculate(corrconf, 0, kFALSE).real() / dnx; if (std::fabs(val) < 1) { fFC->FillProfile(corrconf.Head.c_str(), cent, val, dnx, rndm); + fillNeventsVsNch(corrconf.Head, cent); } return; } @@ -527,9 +622,9 @@ struct FlowCumulantsUpc { if (cfgAcceptance.value.empty() == false) { mAcceptance = ccdb->getForTimeStamp(cfgAcceptance, timestamp); if (mAcceptance) { - LOGF(info, "Loaded acceptance weights from %s (%p)", cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(info, "Loaded acceptance weights from %s (%p)", cfgAcceptance.value.c_str(), static_cast(mAcceptance)); } else { - LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgAcceptance.value.c_str(), (void*)mAcceptance); + LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgAcceptance.value.c_str(), static_cast(mAcceptance)); } } if (cfgEfficiency.value.empty() == false) { @@ -537,7 +632,7 @@ struct FlowCumulantsUpc { if (mEfficiency == nullptr) { LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); } - LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), static_cast(mEfficiency)); } correctionsLoaded = true; } @@ -560,10 +655,30 @@ struct FlowCumulantsUpc { return true; } + template + bool isGoodRctFlag(const C& collision) + { + switch (cfgRctFlagIndex) { + case 1: + return sgSelector.isCBTOk(collision); + case 2: + return sgSelector.isCBTZdcOk(collision); + case 3: + return sgSelector.isCBTHadronOk(collision); + case 4: + return sgSelector.isCBTHadronZdcOk(collision); + default: + return true; + } + } + template - bool trackSelected(TTrack track) + bool trackSelected(const TTrack& track) { registry.fill(HIST("hTrackCount"), 0.5); + if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { + return false; + } // UPC selection if (!track.isPVContributor()) { return false; @@ -636,6 +751,53 @@ struct FlowCumulantsUpc { return; } registry.fill(HIST("hEventCount"), 1.5); + + // zdc time cut + int neutronClass = -1; + float energyCommonZNA = collision.energyCommonZNA(), energyCommonZNC = collision.energyCommonZNC(); + float timeZNA = collision.timeZNA(), timeZNC = collision.timeZNC(); + if (std::isinf(energyCommonZNA)) + energyCommonZNA = -999; + if (std::isinf(energyCommonZNC)) + energyCommonZNC = -999; + if (std::isinf(timeZNA)) + timeZNA = -999; + if (std::isinf(timeZNC)) + timeZNC = -999; + registry.fill(HIST("ZDCEnergy"), energyCommonZNC, energyCommonZNA); + registry.fill(HIST("ZDCTime"), timeZNC, timeZNA); + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { + neutronClass = 0; + registry.fill(HIST("neutronClass"), 0, 0); + } + if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) > cfgZdcTimeCut) { + neutronClass = 2; + registry.fill(HIST("neutronClass"), 1, 0); + } + if (std::abs(timeZNA) > cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 1; + registry.fill(HIST("neutronClass"), 0, 1); + } + if (std::abs(timeZNA) <= cfgZdcTimeCut && std::abs(timeZNC) <= cfgZdcTimeCut) { + neutronClass = 3; + registry.fill(HIST("neutronClass"), 1, 1); + } + if (cfgZdcTime) { + // reject 0n0n and XnXn + if (neutronClass == 0 || neutronClass == 3) { // o2-linter: disable=magic-number (ZDC time cut) + return; + } + // if A or C gap is requested, keep corresponding neutron class + if (cfgGapSideA || cfgGapSideC) { + if ((cfgGapSideA && neutronClass == 1) || (cfgGapSideC && neutronClass == 2)) { // o2-linter: disable=magic-number (ZDC time cut) + // accepted + } else { + return; + } + } + } + registry.fill(HIST("hEventCount"), 2.5); + float lRandom = fRndm->Rndm(); float vtxz = collision.posZ(); registry.fill(HIST("hVtxZ"), vtxz); @@ -644,12 +806,12 @@ struct FlowCumulantsUpc { if (cfgIfVertex && std::abs(vtxz) > cfgCutVertex) { return; } - registry.fill(HIST("hEventCount"), 2.5); + registry.fill(HIST("hEventCount"), 3.5); int occupancy = collision.occupancyInTime(); if (cfgEvSelOccupancy && (occupancy < cfgCutOccupancyLow || occupancy > cfgCutOccupancyHigh)) { return; } - registry.fill(HIST("hEventCount"), 3.5); + registry.fill(HIST("hEventCount"), 4.5); auto currentRunNumber = collision.runNumber(); auto runDuration = ccdb->getRunDuration(currentRunNumber); @@ -667,7 +829,29 @@ struct FlowCumulantsUpc { return; } } - registry.fill(HIST("hEventCount"), 4.5); + registry.fill(HIST("hEventCount"), 5.5); + if (cfgvtxITSTPC && !collision.vtxITSTPC()) { + return; + } + registry.fill(HIST("hEventCount"), 6.5); + if (cfgsbp && !collision.sbp()) { + return; + } + registry.fill(HIST("hEventCount"), 7.5); + if (cfgitsROFb && !collision.itsROFb()) { + return; + } + registry.fill(HIST("hEventCount"), 8.5); + if (cfgtfb && !collision.tfb()) { + return; + } + registry.fill(HIST("hEventCount"), 9.5); + + if (cfgRctFlagEnabled) { + if (!isGoodRctFlag(collision)) // check RCT flags + return; + registry.fill(HIST("hEventCount"), 10.5); + } // // track weights float weff = 1, wacc = 1; @@ -728,8 +912,6 @@ struct FlowCumulantsUpc { registry.fill(HIST("hDCAxy"), track.dcaXY(), track.pt()); nTracksRaw += 1.; nTracksCorrected += weff; - } - if (withinPtRef) { fGFW->Fill(eta, fPtAxis->FindBin(pt) - 1, phi, wacc * weff, 1); } if (withinPtPOI) { @@ -762,7 +944,12 @@ struct FlowCumulantsUpc { return; } } - registry.fill(HIST("hEventCount"), 5.5); + registry.fill(HIST("hEventCount"), 11.5); + if (collision.hadronicRate() > cfgIRMaxCut) { + return; + } + registry.fill(HIST("interactionRate"), collision.hadronicRate()); + registry.fill(HIST("hEventCount"), 12.5); // Filling Flow Container for (uint l_ind = 0; l_ind < corrconfigs.size(); l_ind++) { @@ -829,8 +1016,6 @@ struct FlowCumulantsUpc { registry.fill(HIST("hEtaMC"), eta); registry.fill(HIST("hPtRefMC"), pt); nTracksCorrected += weff; - } - if (withinPtRef) { fGFWMC->Fill(eta, fPtAxis->FindBin(pt) - 1, phi, wacc * weff, 1); } if (withinPtPOI) { diff --git a/PWGUD/Tasks/flowLongRangeCorrUpc.cxx b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx new file mode 100644 index 00000000000..e5766de17f2 --- /dev/null +++ b/PWGUD/Tasks/flowLongRangeCorrUpc.cxx @@ -0,0 +1,762 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file flowLongRangeCorrUpc.cxx +/// \author Zhiyong Lu (zhiyong.lu@cern.ch), Yongxi Du (yongxi.du@cern.ch) +/// \since Apr/7/2026 +/// \brief task for TPC-FT0 correlations in UPC + +#include "PWGCF/Core/CorrelationContainer.h" +#include "PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit +#include "PWGUD/DataModel/UDTables.h" +#include "PWGUD/DataModel/UDTruegapsideTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include // o2::ft0::Geometry +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::constants::math; + +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; + +struct FlowLongRangeCorrUpc { + // event selection + O2_DEFINE_CONFIGURABLE(cfgGapSide, int, 1, "choose one side 0:A; 1:C") + O2_DEFINE_CONFIGURABLE(cfgGapSideMerge, bool, true, "merge A and C side") + // flags that already enabled in sgcandproducer: kNoTimeFrameBorder, kNoITSROFrameBorder, kNoSameBunchPileup, kIsGoodZvtxFT0vsPV, kIsVertexITSTPC, isGoodRCTCollision + O2_DEFINE_CONFIGURABLE(cfgMinMixEventNum, int, 5, "Minimum number of events to mix") + O2_DEFINE_CONFIGURABLE(cfgMinMult, int, 0, "Minimum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgMaxMult, int, 5, "Maximum multiplicity for collision") + O2_DEFINE_CONFIGURABLE(cfgSampleSize, double, 10, "Sample size for mixed event") + + // track selection + O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.2f, "minimum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 10.0f, "maximum accepted track pT") + O2_DEFINE_CONFIGURABLE(cfgPtCutRefMin, float, 0.2f, "minimum accepted reference track pT") + O2_DEFINE_CONFIGURABLE(cfgPtCutRefMax, float, 3.0f, "minimum accepted reference track pT") + O2_DEFINE_CONFIGURABLE(cfgEtaCut, float, 0.9f, "Eta cut") + O2_DEFINE_CONFIGURABLE(cfgGlobalTrack, bool, true, "use global track") + O2_DEFINE_CONFIGURABLE(cfgDcaXY, bool, true, "enable dcaxy cut") + O2_DEFINE_CONFIGURABLE(cfgDcaZ, bool, false, "enable dcaz cut") + O2_DEFINE_CONFIGURABLE(cfgDcaZCut, float, 10.0, "dcaz cut") + O2_DEFINE_CONFIGURABLE(cfgMaxTPCChi2NCl, int, 4, "max chi2 for tpc fit") + O2_DEFINE_CONFIGURABLE(cfgCutTPCCrossedRows, float, 70.0f, "minimum number of crossed TPC Rows") + O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, float, 50.0f, "minimum number of found TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutITSclu, float, 5.0f, "minimum number of ITS clusters") + + // user define + O2_DEFINE_CONFIGURABLE(cfgUseNchCorrected, bool, true, "use efficiency corrected tracks") + O2_DEFINE_CONFIGURABLE(cfgEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrder, bool, true, "enable trigger pT < associated pT cut") + O2_DEFINE_CONFIGURABLE(cfgUsePtOrderInMixEvent, bool, true, "enable trigger pT < associated pT cut in mixed event") + O2_DEFINE_CONFIGURABLE(cfgCutMerging, float, 0.02, "Merging cut on track merge") + O2_DEFINE_CONFIGURABLE(cfgRadiusLow, float, 0.8, "Low radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgRadiusHigh, float, 2.5, "High radius for merging cut") + O2_DEFINE_CONFIGURABLE(cfgDrawEtaPhiDis, bool, false, "draw eta-phi distribution for detectors in used") + + SliceCache cache; + + ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for histograms"}; + ConfigurableAxis axisEta{"axisEta", {40, -1., 1.}, "eta axis for histograms"}; + ConfigurableAxis axisPhi{"axisPhi", {72, 0.0, constants::math::TwoPI}, "phi axis for histograms"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {36, -PIHalf, PIHalf * 3}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaEta{"axisDeltaEta", {20, -2, 2}, "delta eta axis for histograms"}; + ConfigurableAxis axisDeltaEtaTpcFt0a{"axisDeltaEtaTpcFt0a", {32, -5.8, -2.6}, "delta eta axis, -5.8~-2.6 for TPC-FT0A,"}; + ConfigurableAxis axisDeltaEtaTpcFt0c{"axisDeltaEtaTpcFt0c", {32, 1.2, 4.2}, "delta eta axis, 1.2~4.2 for TPC-FT0C"}; + ConfigurableAxis axisDeltaEtaFt0aFt0c{"axisDeltaEtaFt0aFt0c", {32, 5.5, 8.5}, "delta eta axis"}; + ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; + ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt associated axis for histograms"}; + ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}, "multiplicity axis for histograms"}; + ConfigurableAxis axisVtxMix{"axisVtxMix", {VARIABLE_WIDTH, -10, 0, 10}, "vertex axis for mixed event histograms"}; + ConfigurableAxis axisMultMix{"axisMultMix", {VARIABLE_WIDTH, 0, 5, 10, 15, 20, 25, 30}, "multiplicity / centrality axis for mixed event histograms"}; + + ConfigurableAxis axisVertexEfficiency{"axisVertexEfficiency", {10, -10, 10}, "vertex axis for efficiency histograms"}; + ConfigurableAxis axisEtaEfficiency{"axisEtaEfficiency", {20, -1.0, 1.0}, "eta axis for efficiency histograms"}; + ConfigurableAxis axisPtEfficiency{"axisPtEfficiency", {VARIABLE_WIDTH, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.25, 3.5, 3.75, 4.0, 4.5, 5.0, 6.0, 7.0, 8.0}, "pt axis for efficiency histograms"}; + ConfigurableAxis axisSample{"axisSample", {cfgSampleSize, 0, cfgSampleSize}, "sample axis for histograms"}; + ConfigurableAxis axisThresholdFt0a{"axisThresholdFt0a", {5, 0, 5}, "FT0A amplitude"}; + AxisSpec axisChID = {220, 0, 220}; + + // make the filters and cuts. + Filter trackFilter = (aod::udtrack::isPVContributor == true); + Filter collisionFilter = (cfgGapSideMerge == true) + ? ((aod::udcollision::gapSide == static_cast(0) || aod::udcollision::gapSide == static_cast(1)) && + (aod::upcservice::truegapside == 0 || aod::upcservice::truegapside == 1)) + : ((aod::udcollision::gapSide == static_cast(cfgGapSide)) && + (aod::upcservice::truegapside == cfgGapSide)); + + using UDTracksFull = soa::Filtered>; + using UDCollisionsFull = soa::Filtered>; + + // FT0 geometry + o2::ft0::Geometry ft0Det{}; + // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ + struct OffsetXYZ { + double x{0}, y{0}, z{0}; + double getX() const { return x; } + double getY() const { return y; } + double getZ() const { return z; } + }; + std::array offsetFT0{}; // iRunOffset = 0 for now + int iRunOffset = 0; + static constexpr uint64_t Ft0IndexA = 96; + + // Corrections + TH3D* mEfficiency = nullptr; + bool correctionsLoaded = false; + + // Define the outputs + OutputObj sameTpcFt0a{"sameEvent_TPC_FT0A"}; + OutputObj mixedTpcFt0a{"mixedEvent_TPC_FT0A"}; + OutputObj sameTpcFt0c{"sameEvent_TPC_FT0C"}; + OutputObj mixedTpcFt0c{"mixedEvent_TPC_FT0C"}; + OutputObj sameFt0aFt0c{"sameEvent_FT0A_FT0C"}; + OutputObj mixedFt0aFt0c{"mixedEvent_FT0A_FT0C"}; + HistogramRegistry registry{"registry"}; + + // define global variables + Service ccdb; + TRandom3* gRandom = new TRandom3(); + enum EventType { + SameEvent = 1, + MixedEvent = 3 + }; + enum FITIndex { + kFT0A = 0, + kFT0C = 1 + }; + enum DataType { + kReco, + kGen + }; + + void init(InitContext&) + { + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + auto now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + ccdb->setCreatedNotAfter(now); + LOGF(info, "Starting init"); + + const AxisSpec axisEtaFull{90, -4., 5., "#eta"}; + std::vector binEdges = {-1.5, -0.5, 0.5, 1.5, 2.5, 3.5}; + AxisSpec axisgap = {binEdges, "true gap side"}; + if (doprocessSameTpcFt0a || doprocessSameTpcFt0c || doprocessSameFt0aFt0c) { + registry.add("truegap_inused", "truegap_inused", {HistType::kTH1D, {axisgap}}); + registry.add("trackSign", "trackSign", {HistType::kTH1D, {{10, -5, 5}}}); + registry.add("Phi", "Phi", {HistType::kTH1D, {axisPhi}}); + registry.add("Phi_FT0A", "Phi_FT0A", {HistType::kTH1D, {axisPhi}}); + registry.add("Phi_FT0C", "Phi_FT0C", {HistType::kTH1D, {axisPhi}}); + registry.add("Eta", "Eta", {HistType::kTH1D, {axisEta}}); + registry.add("Eta_FT0A", "Eta_FT0A", {HistType::kTH1D, {{20, 3., 5.}}}); + registry.add("Eta_FT0C", "Eta_FT0C", {HistType::kTH1D, {{20, -4.0, -2.0}}}); + registry.add("EtaCorrected", "EtaCorrected", {HistType::kTH1D, {axisEta}}); + registry.add("pT", "pT", {HistType::kTH1D, {axisPtTrigger}}); + registry.add("pTCorrected", "pTCorrected", {HistType::kTH1D, {axisPtTrigger}}); + registry.add("Nch", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); + registry.add("Nch_used_TPCFT0A", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event + registry.add("Nch_used_TPCFT0C", "N_{ch}", {HistType::kTH1D, {axisMultiplicity}}); // histogram to see how many events are in the same and mixed event + registry.add("zVtx", "zVtx", {HistType::kTH1D, {axisVertex}}); + registry.add("FT0Amp", "", {HistType::kTH2F, {axisChID, axisThresholdFt0a}}); + registry.add("FT0AmpCorrect", "", {HistType::kTH2F, {axisChID, axisThresholdFt0a}}); + registry.add("hDCAz", "DCAz after cuts; DCAz (cm); Pt", {HistType::kTH2D, {{100, -0.5, 0.5}, {100, 0, 10}}}); + registry.add("hDCAxy", "DCAxy after cuts; DCAxy (cm); Pt", {HistType::kTH2D, {{100, -0.5, 0.5}, {100, 0, 10}}}); + if (cfgDrawEtaPhiDis) { + registry.add("EtaPhi", "", {HistType::kTH2F, {axisEtaFull, axisPhi}}); + } + } + + if (doprocessSameTpcFt0a) { + registry.add("deltaEta_deltaPhi_same_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed_TPC_FT0A", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0a}}); + registry.add("Trig_hist_TPC_FT0A", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + } + if (doprocessSameTpcFt0c) { + registry.add("deltaEta_deltaPhi_same_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed_TPC_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaTpcFt0c}}); + registry.add("Trig_hist_TPC_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + } + if (doprocessSameFt0aFt0c) { + registry.add("deltaEta_deltaPhi_same_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); // check to see the delta eta and delta phi distribution + registry.add("deltaEta_deltaPhi_mixed_FT0A_FT0C", "", {HistType::kTH2D, {axisDeltaPhi, axisDeltaEtaFt0aFt0c}}); + registry.add("Trig_hist_FT0A_FT0C", "", {HistType::kTHnSparseF, {{axisSample, axisVertex, axisPtTrigger}}}); + } + + registry.add("eventcount_TPCFT0A", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + registry.add("eventcount_TPCFT0C", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + registry.add("eventcount_FT0AFT0C", "bin", {HistType::kTH1F, {{4, 0, 4, "bin"}}}); // histogram to see how many events are in the same and mixed event + + LOGF(info, "Initializing correlation container"); + std::vector corrAxisTpcFt0a = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0a, "#Delta#eta"}}; + std::vector effAxis = { + {axisEtaEfficiency, "#eta"}, + {axisPtEfficiency, "p_{T} (GeV/c)"}, + {axisVertexEfficiency, "z-vtx (cm)"}, + }; + std::vector userAxis; + std::vector corrAxisTpcFt0c = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaTpcFt0c, "#Delta#eta"}}; + std::vector corrAxisFt0aFt0c = {{axisSample, "Sample"}, + {axisVertex, "z-vtx (cm)"}, + {axisPtTrigger, "p_{T} (GeV/c)"}, + {axisPtAssoc, "p_{T} (GeV/c)"}, + {axisDeltaPhi, "#Delta#varphi (rad)"}, + {axisDeltaEtaFt0aFt0c, "#Delta#eta"}}; + + if (doprocessSameTpcFt0a) { + sameTpcFt0a.setObject(new CorrelationContainer("sameEvent_TPC_FT0A", "sameEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); + mixedTpcFt0a.setObject(new CorrelationContainer("mixedEvent_TPC_FT0A", "mixedEvent_TPC_FT0A", corrAxisTpcFt0a, effAxis, userAxis)); + } + if (doprocessSameTpcFt0c) { + sameTpcFt0c.setObject(new CorrelationContainer("sameEvent_TPC_FT0C", "sameEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); + mixedTpcFt0c.setObject(new CorrelationContainer("mixedEvent_TPC_FT0C", "mixedEvent_TPC_FT0C", corrAxisTpcFt0c, effAxis, userAxis)); + } + if (doprocessSameFt0aFt0c) { + sameFt0aFt0c.setObject(new CorrelationContainer("sameEvent_FT0A_FT0C", "sameEvent_FT0A_FT0C", corrAxisFt0aFt0c, effAxis, userAxis)); + mixedFt0aFt0c.setObject(new CorrelationContainer("mixedEvent_FT0A_FT0C", "mixedEvent_FT0A_FT0C", corrAxisFt0aFt0c, effAxis, userAxis)); + } + + ft0Det.calculateChannelCenter(); + + LOGF(info, "End of init"); + } + + template + bool trackSelected(const TTrack& track) + { + if (track.pt() < cfgPtCutMin || track.pt() > cfgPtCutMax) { + return false; + } + if (!track.isPVContributor()) { + return false; + } + if (cfgGlobalTrack && !(track.hasITS() && track.hasTPC())) { + return false; + } + if (track.tpcNClsCrossedRows() < cfgCutTPCCrossedRows) { + return false; + } + auto tpcClu = track.tpcNClsFindable() - track.tpcNClsFindableMinusFound(); + if (tpcClu < cfgCutTPCclu) { + return false; + } + if (track.tpcChi2NCl() >= cfgMaxTPCChi2NCl) { + return false; + } + if (track.itsNCls() < cfgCutITSclu) { + return false; + } + if (cfgDcaZ && !(std::fabs(track.dcaZ()) < cfgDcaZCut)) { + return false; + } + double dcaLimit = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); + if (cfgDcaXY && !(std::fabs(track.dcaXY()) < dcaLimit)) { + return false; + } + + return true; + } + + void loadCorrection(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (cfgEfficiency.value.empty() == false) { + mEfficiency = ccdb->getForTimeStamp(cfgEfficiency, timestamp); + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgEfficiency.value.c_str(), (void*)mEfficiency); + } + correctionsLoaded = true; + } + + bool getEfficiencyCorrection(float& weight_nue, float eta, float pt, float posZ) + { + float eff = 1.; + if (mEfficiency) { + int etaBin = mEfficiency->GetXaxis()->FindBin(eta); + int ptBin = mEfficiency->GetYaxis()->FindBin(pt); + int zBin = mEfficiency->GetZaxis()->FindBin(posZ); + eff = mEfficiency->GetBinContent(etaBin, ptBin, zBin); + } else { + eff = 1.0; + } + if (eff == 0) + return false; + weight_nue = 1. / eff; + return true; + } + + template + double trackCounter(const TTracks& tracks, float posZ) + { + float weff1 = 1; + double nTracksRaw = 0.; + double nTracksCorrected = 0.; + for (auto const& track1 : tracks) { + if (!trackSelected(track1)) + continue; + nTracksRaw += 1.; + auto momentum = std::array{track1.px(), track1.py(), track1.pz()}; + double pt = track1.pt(); + double eta = RecoDecay::eta(momentum); + if (!getEfficiencyCorrection(weff1, eta, pt, posZ)) + continue; + nTracksCorrected += weff1; + } + if (cfgUseNchCorrected) + return nTracksCorrected; + else + return nTracksRaw; + } + + template + void fillYield(const TCollision& collision, const TTracks& tracks) // function to fill the yield and etaphi histograms. + { + float weff1 = 1; + float vtxz = collision.posZ(); + registry.fill(HIST("truegap_inused"), collision.truegapside()); + registry.fill(HIST("Nch"), tracks.size()); + registry.fill(HIST("zVtx"), vtxz); + for (auto const& track1 : tracks) { + if (!trackSelected(track1)) + continue; + auto momentum = std::array{track1.px(), track1.py(), track1.pz()}; + double pt = track1.pt(); + double phi = RecoDecay::phi(momentum); + double eta = RecoDecay::eta(momentum); + if (!getEfficiencyCorrection(weff1, eta, pt, vtxz)) + continue; + registry.fill(HIST("Phi"), RecoDecay::constrainAngle(phi, 0.0)); + registry.fill(HIST("Eta"), eta); + registry.fill(HIST("EtaCorrected"), eta, weff1); + registry.fill(HIST("pT"), pt); + registry.fill(HIST("pTCorrected"), pt, weff1); + registry.fill(HIST("hDCAz"), track1.dcaZ(), pt); + registry.fill(HIST("hDCAxy"), track1.dcaXY(), pt); + registry.fill(HIST("trackSign"), track1.sign()); + } + } + + template + void fillCorrelationsTPCFT0(const TTracks& tracks1, aod::UDCollisionFITBits const& fitBits, float posZ, int system, int corType) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + if (fitBits.size() > 1) { + LOGF(fatal, "fillCorrelationsTPCFT0(): fitBits.size() = %d (expected 0 or 1)", fitBits.size()); + return; + } + if (fitBits.size() == 0) { + // no TPC-FT0 correlations for this collision + return; + } + + int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + // loop over all tracks + for (auto const& track1 : tracks1) { + if (!trackSelected(track1)) + continue; + auto momentum = std::array{track1.px(), track1.py(), track1.pz()}; + double pt = track1.pt(); + double tr_phi = RecoDecay::phi(momentum); + double tr_eta = RecoDecay::eta(momentum); + if (!getEfficiencyCorrection(triggerWeight, tr_eta, pt, posZ)) + continue; + if (system == SameEvent) { + if (corType == kFT0C) { + registry.fill(HIST("Trig_hist_TPC_FT0C"), fSampleIndex, posZ, pt, triggerWeight); + } else if (corType == kFT0A) { + registry.fill(HIST("Trig_hist_TPC_FT0A"), fSampleIndex, posZ, pt, triggerWeight); + } + if (cfgDrawEtaPhiDis) { + registry.fill(HIST("EtaPhi"), tr_eta, tr_phi, triggerWeight); + } + } + + // Only one row per collision is expected. + auto row = fitBits.begin(); + const auto w1 = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + const auto w2 = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + for (int chanelid = 0; chanelid < udhelpers::kFT0Bits; ++chanelid) { + // skip A channels if filling TPC-FT0C + if (corType == kFT0C && chanelid < udhelpers::kFT0AChannels) + continue; + // skip C channels if filling TPC-FT0A + if (corType == kFT0A && chanelid >= udhelpers::kFT0AChannels) + continue; + + // at least 1 fired channel + if (!udhelpers::testBit(w1, chanelid)) { + continue; + } + double fitCh_phi = 0., fitCh_eta = 0.; + const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, chanelid, offsetFT0, iRunOffset, fitCh_phi, fitCh_eta); + if (!ok) + continue; + float thr = 1.; + if (udhelpers::testBit(w2, chanelid)) + thr = 2.; + + if (system == SameEvent) { + if (corType == kFT0C) { + registry.fill(HIST("Phi_FT0C"), RecoDecay::constrainAngle(fitCh_phi, 0.0)); + registry.fill(HIST("Eta_FT0C"), fitCh_eta); + } else if (corType == kFT0A) { + registry.fill(HIST("Phi_FT0A"), RecoDecay::constrainAngle(fitCh_phi, 0.0)); + registry.fill(HIST("Eta_FT0A"), fitCh_eta); + } + if (cfgDrawEtaPhiDis) { + registry.fill(HIST("EtaPhi"), fitCh_eta, fitCh_phi, thr); + } + registry.fill(HIST("FT0Amp"), chanelid, thr); + } + + float deltaPhi = RecoDecay::constrainAngle(tr_phi - fitCh_phi, -PIHalf); + float deltaEta = tr_eta - fitCh_eta; + // fill the right sparse and histograms + if (system == SameEvent) { + if (corType == kFT0A) { + registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0A"), deltaPhi, deltaEta, thr * triggerWeight); + sameTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, pt, pt, deltaPhi, deltaEta, thr * triggerWeight); + } else if (corType == kFT0C) { + registry.fill(HIST("deltaEta_deltaPhi_same_TPC_FT0C"), deltaPhi, deltaEta, thr * triggerWeight); + sameTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, pt, pt, deltaPhi, deltaEta, thr * triggerWeight); + } + } else if (system == MixedEvent) { + if (corType == kFT0A) { + registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0A"), deltaPhi, deltaEta, thr * triggerWeight); + mixedTpcFt0a->getPairHist()->Fill(step, fSampleIndex, posZ, pt, pt, deltaPhi, deltaEta, thr * triggerWeight); + } else if (corType == kFT0C) { + registry.fill(HIST("deltaEta_deltaPhi_mixed_TPC_FT0C"), deltaPhi, deltaEta, thr * triggerWeight); + mixedTpcFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, pt, pt, deltaPhi, deltaEta, thr * triggerWeight); + } + } + } + } + } + + template + void fillCorrelationsFT0AFT0C(aod::UDCollisionFITBits const& fitBits1, aod::UDCollisionFITBits const& fitBits2, float posZ, int system) // function to fill the Output functions (sparse) and the delta eta and delta phi histograms + { + if (fitBits1.size() > 1 || fitBits2.size() > 1) { + LOGF(fatal, "fillCorrelationsFT0AFT0C(): fitBits1.size() = %d, fitBits2.size() = %d (expected 0 or 1)", fitBits1.size(), fitBits2.size()); + return; + } + if (fitBits1.size() == 0 || fitBits2.size() == 0) { + // no TPC-FT0 correlations for this collision + return; + } + + int fSampleIndex = gRandom->Uniform(0, cfgSampleSize); + + float triggerWeight = 1.0f; + // Only one row per collision is expected. + auto rowA = fitBits1.begin(); + const auto w1A = udhelpers::makeBits256(rowA.thr1W0(), rowA.thr1W1(), rowA.thr1W2(), rowA.thr1W3()); + const auto w2A = udhelpers::makeBits256(rowA.thr2W0(), rowA.thr2W1(), rowA.thr2W2(), rowA.thr2W3()); + auto rowC = fitBits2.begin(); + const auto w1C = udhelpers::makeBits256(rowC.thr1W0(), rowC.thr1W1(), rowC.thr1W2(), rowC.thr1W3()); + const auto w2C = udhelpers::makeBits256(rowC.thr2W0(), rowC.thr2W1(), rowC.thr2W2(), rowC.thr2W3()); + for (int chanelidA = 0; chanelidA < udhelpers::kFT0AChannels; ++chanelidA) { + // at least 1 fired channel + if (!udhelpers::testBit(w1A, chanelidA)) { + continue; + } + double fitCh_phiA = 0., fitCh_etaA = 0.; + const bool okA = udhelpers::getPhiEtaFromFitBit(ft0Det, chanelidA, offsetFT0, iRunOffset, fitCh_phiA, fitCh_etaA); + if (!okA) + continue; + float thrA = 1.; + if (udhelpers::testBit(w2A, chanelidA)) + thrA = 2.; + + if (system == SameEvent) { + registry.fill(HIST("Trig_hist_FT0A_FT0C"), fSampleIndex, posZ, 0.5, thrA); + } + for (int chanelidC = udhelpers::kFT0AChannels; chanelidC < udhelpers::kFT0Bits; ++chanelidC) { + // at least 1 fired channel + if (!udhelpers::testBit(w1C, chanelidC)) { + continue; + } + double fitCh_phiC = 0., fitCh_etaC = 0.; + const bool okC = udhelpers::getPhiEtaFromFitBit(ft0Det, chanelidC, offsetFT0, iRunOffset, fitCh_phiC, fitCh_etaC); + if (!okC) + continue; + float thrC = 1.; + if (udhelpers::testBit(w2C, chanelidC)) + thrC = 2.; + + float deltaPhi = RecoDecay::constrainAngle(fitCh_phiA - fitCh_phiC, -PIHalf); + float deltaEta = fitCh_etaA - fitCh_etaC; + + // fill the right sparse and histograms + if (system == SameEvent) { + registry.fill(HIST("deltaEta_deltaPhi_same_FT0A_FT0C"), deltaPhi, deltaEta, thrA * thrC * triggerWeight); + sameFt0aFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, 0.5, 0.5, deltaPhi, deltaEta, thrA * thrC * triggerWeight); + } else if (system == MixedEvent) { + registry.fill(HIST("deltaEta_deltaPhi_mixed_FT0A_FT0C"), deltaPhi, deltaEta, thrA * thrC * triggerWeight); + mixedFt0aFt0c->getPairHist()->Fill(step, fSampleIndex, posZ, 0.5, 0.5, deltaPhi, deltaEta, thrA * thrC * triggerWeight); + } + } + } + } + + void processSameTpcFt0a(UDCollisionsFull::iterator const& collision, UDTracksFull const& tracks, aod::UDCollisionFITBits const& fitBits) + { + LOGF(info, "processSameTpcFt0a: collisionId:%d, tracks.size() = %d", collision.globalIndex(), tracks.size()); + LOGF(info, "processSameTpcFt0a: collisionId:%d, fitBits.size() = %d", collision.globalIndex(), fitBits.size()); + if (fitBits.size() > 1) { + LOGF(fatal, "processSameTpcFt0a: fitBits.size() = %d (expected 0 or 1)", fitBits.size()); + return; + } + + auto currentRunNumber = collision.runNumber(); + auto runDuration = ccdb->getRunDuration(currentRunNumber); + loadCorrection(runDuration.first); + + double Nch = trackCounter(tracks, collision.posZ()); + if (Nch < cfgMinMult || Nch >= cfgMaxMult) { + return; + } + registry.fill(HIST("eventcount_TPCFT0A"), SameEvent); // because its same event i put it in the 1 bin + fillYield(collision, tracks); + registry.fill(HIST("Nch_used_TPCFT0A"), Nch); + + sameTpcFt0a->fillEvent(Nch, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsTPCFT0(tracks, fitBits, collision.posZ(), SameEvent, kFT0A); + } + PROCESS_SWITCH(FlowLongRangeCorrUpc, processSameTpcFt0a, "Process same event for TPC-FT0 correlation", true); + + void processMixedTpcFt0a(UDCollisionsFull const& collisions, UDTracksFull const& tracks, aod::UDCollisionFITBits const& fitBits) + { + auto getTracksSize = [&tracks, this](UDCollisionsFull::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::udtrack::udCollisionId, collision.globalIndex(), this->cache); + auto mult = trackCounter(associatedTracks, collision.posZ()); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + double Nch1 = trackCounter(tracks1, collision1.posZ()); + double Nch2 = trackCounter(tracks2, collision2.posZ()); + if (Nch1 < cfgMinMult || Nch1 >= cfgMaxMult) + continue; + if (Nch2 < cfgMinMult || Nch2 >= cfgMaxMult) + continue; + + auto fitBits2 = fitBits.sliceByCached(o2::aod::udtrack::udCollisionId, collision2.globalIndex(), this->cache); + if (fitBits2.size() > 1) { + LOGF(fatal, "processMixedTpcFt0a: collision2 Id:%d, fitBits2.size() = %d (expected 0 or 1)", collision2.globalIndex(), fitBits2.size()); + return; + } + if (fitBits2.size() == 0) { + continue; + } + + registry.fill(HIST("eventcount_TPCFT0A"), MixedEvent); // fill the mixed event in the 3 bin + auto currentRunNumber = collision1.runNumber(); + auto runDuration = ccdb->getRunDuration(currentRunNumber); + loadCorrection(runDuration.first); + + fillCorrelationsTPCFT0(tracks1, fitBits2, collision1.posZ(), MixedEvent, kFT0A); + } + } + PROCESS_SWITCH(FlowLongRangeCorrUpc, processMixedTpcFt0a, "Process mixed events for TPC-FT0A correlation", true); + + void processSameTpcFt0c(UDCollisionsFull::iterator const& collision, UDTracksFull const& tracks, aod::UDCollisionFITBits const& fitBits) + { + if (fitBits.size() > 1) { + LOGF(fatal, "processSameTpcFt0c: fitBits.size() = %d (expected 0 or 1)", fitBits.size()); + return; + } + + auto currentRunNumber = collision.runNumber(); + auto runDuration = ccdb->getRunDuration(currentRunNumber); + loadCorrection(runDuration.first); + + double Nch = trackCounter(tracks, collision.posZ()); + if (Nch < cfgMinMult || Nch >= cfgMaxMult) { + return; + } + registry.fill(HIST("eventcount_TPCFT0C"), SameEvent); // because its same event i put it in the 1 bin + if (!doprocessSameTpcFt0a) + fillYield(collision, tracks); + registry.fill(HIST("Nch_used_TPCFT0C"), Nch); + + sameTpcFt0c->fillEvent(Nch, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsTPCFT0(tracks, fitBits, collision.posZ(), SameEvent, kFT0C); + } + PROCESS_SWITCH(FlowLongRangeCorrUpc, processSameTpcFt0c, "Process same events for TPC-FT0C correlation", false); + + void processMixedTpcFt0c(UDCollisionsFull const& collisions, UDTracksFull const& tracks, aod::UDCollisionFITBits const& fitBits) + { + auto getTracksSize = [&tracks, this](UDCollisionsFull::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::udtrack::udCollisionId, collision.globalIndex(), this->cache); + auto mult = trackCounter(associatedTracks, collision.posZ()); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + double Nch1 = trackCounter(tracks1, collision1.posZ()); + double Nch2 = trackCounter(tracks2, collision2.posZ()); + if (Nch1 < cfgMinMult || Nch1 >= cfgMaxMult) + continue; + if (Nch2 < cfgMinMult || Nch2 >= cfgMaxMult) + continue; + + auto fitBits2 = fitBits.sliceByCached(o2::aod::udtrack::udCollisionId, collision2.globalIndex(), this->cache); + if (fitBits2.size() > 1) { + LOGF(fatal, "processMixedTpcFt0a: collision2 Id:%d, fitBits2.size() = %d (expected 0 or 1)", collision2.globalIndex(), fitBits2.size()); + return; + } + if (fitBits2.size() == 0) { + continue; + } + + registry.fill(HIST("eventcount_TPCFT0C"), MixedEvent); // fill the mixed event in the 3 bin + auto currentRunNumber = collision1.runNumber(); + auto runDuration = ccdb->getRunDuration(currentRunNumber); + loadCorrection(runDuration.first); + + fillCorrelationsTPCFT0(tracks1, fitBits2, collision1.posZ(), MixedEvent, kFT0C); + } + } + PROCESS_SWITCH(FlowLongRangeCorrUpc, processMixedTpcFt0c, "Process mixed events for TPC-FT0C correlation", false); + + void processSameFt0aFt0c(UDCollisionsFull::iterator const& collision, UDTracksFull const& tracks, aod::UDCollisionFITBits const& fitBits) + { + if (fitBits.size() > 1) { + LOGF(fatal, "processSameTpcFt0c: fitBits.size() = %d (expected 0 or 1)", fitBits.size()); + return; + } + + auto currentRunNumber = collision.runNumber(); + auto runDuration = ccdb->getRunDuration(currentRunNumber); + loadCorrection(runDuration.first); + + double Nch = trackCounter(tracks, collision.posZ()); + if (Nch < cfgMinMult || Nch >= cfgMaxMult) { + return; + } + registry.fill(HIST("eventcount_FT0AFT0C"), SameEvent); // because its same event i put it in the 1 bin + if (!doprocessSameTpcFt0a) + fillYield(collision, tracks); + + sameFt0aFt0c->fillEvent(Nch, CorrelationContainer::kCFStepReconstructed); + fillCorrelationsFT0AFT0C(fitBits, fitBits, collision.posZ(), SameEvent); + } + PROCESS_SWITCH(FlowLongRangeCorrUpc, processSameFt0aFt0c, "Process same events for FT0A-FT0C correlation", false); + + void processMixedFt0aFt0c(UDCollisionsFull const& collisions, UDTracksFull const& tracks, aod::UDCollisionFITBits const& fitBits) + { + auto getTracksSize = [&tracks, this](UDCollisionsFull::iterator const& collision) { + auto associatedTracks = tracks.sliceByCached(o2::aod::udtrack::udCollisionId, collision.globalIndex(), this->cache); + auto mult = trackCounter(associatedTracks, collision.posZ()); + return mult; + }; + + using MixedBinning = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; + + MixedBinning binningOnVtxAndMult{{getTracksSize}, {axisVtxMix, axisMultMix}}; + + auto tracksTuple = std::make_tuple(tracks, tracks); + Pair pairs{binningOnVtxAndMult, cfgMinMixEventNum, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip + for (auto it = pairs.begin(); it != pairs.end(); it++) { + auto& [collision1, tracks1, collision2, tracks2] = *it; + + double Nch1 = trackCounter(tracks1, collision1.posZ()); + double Nch2 = trackCounter(tracks2, collision2.posZ()); + if (Nch1 < cfgMinMult || Nch1 >= cfgMaxMult) + continue; + if (Nch2 < cfgMinMult || Nch2 >= cfgMaxMult) + continue; + + auto fitBits1 = fitBits.sliceByCached(o2::aod::udtrack::udCollisionId, collision1.globalIndex(), this->cache); + auto fitBits2 = fitBits.sliceByCached(o2::aod::udtrack::udCollisionId, collision2.globalIndex(), this->cache); + if (fitBits1.size() > 1 || fitBits2.size() > 1) { + LOGF(fatal, "processMixedTpcFt0a: fitBits1.size() = %d, fitBits2.size() = %d (expected 0 or 1)", fitBits1.size(), fitBits2.size()); + return; + } + if (fitBits1.size() == 0 || fitBits2.size() == 0) { + continue; + } + + registry.fill(HIST("eventcount_FT0AFT0C"), MixedEvent); // fill the mixed event in the 3 bin + auto currentRunNumber = collision1.runNumber(); + auto runDuration = ccdb->getRunDuration(currentRunNumber); + loadCorrection(runDuration.first); + + fillCorrelationsFT0AFT0C(fitBits1, fitBits2, collision1.posZ(), MixedEvent); + } + } + PROCESS_SWITCH(FlowLongRangeCorrUpc, processMixedFt0aFt0c, "Process mixed events for FT0A-FT0C correlation", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGUD/Tasks/flowMcUpc.cxx b/PWGUD/Tasks/flowMcUpc.cxx index 253d148c022..fbd30c97d4c 100644 --- a/PWGUD/Tasks/flowMcUpc.cxx +++ b/PWGUD/Tasks/flowMcUpc.cxx @@ -10,15 +10,26 @@ // or submit itself to any jurisdiction. /// \file flowMcUpc.cxx -/// \author Zhiyong Lu (zhiyong.lu@cern.ch), Yongxi Du (yongxi.du@cern.ch) -/// \since Apr/2/2026 -/// \brief flow efficiency analysis on UPC MC - -#include "PWGUD/DataModel/UDTables.h" - +/// \author Zhiyong Lu (zhiyong.lu@cern.ch) Yongxi Du (yongxi.du@cern.ch) +/// \since Aug/04/2026 +/// \brief QC of synthetic flow exercise + +#include "PWGCF/GenericFramework/Core/FlowContainer.h" +#include "PWGCF/GenericFramework/Core/GFW.h" +#include "PWGCF/GenericFramework/Core/GFWWeights.h" +#include "PWGMM/Mult/DataModel/Index.h" // for Particles2Tracks table + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/CCDB/RCTSelectionFlags.h" +#include "Common/CCDB/ctpRateFetcher.h" #include "Common/Core/RecoDecay.h" +#include "Common/Core/TrackSelection.h" +#include "Common/Core/TrackSelectionDefaults.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/TrackSelectionTables.h" #include +#include #include #include #include @@ -29,22 +40,35 @@ #include #include +#include #include +#include #include +#include +#include #include +#include +#include +#include + +#include + +#include -#include #include +#include #include #include #include +#include #include using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +using namespace o2::aod::rctsel; -#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, DEFAULT, HELP}; +#define O2_DEFINE_CONFIGURABLE(NAME, TYPE, DEFAULT, HELP) Configurable NAME{#NAME, (DEFAULT), (HELP)}; // NOLINT(bugprone-macro-parentheses) struct FlowMcUpc { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; @@ -52,21 +76,128 @@ struct FlowMcUpc { Configurable minB{"minB", 0.0f, "min impact parameter"}; Configurable maxB{"maxB", 20.0f, "max impact parameter"}; O2_DEFINE_CONFIGURABLE(cfgCutVertex, float, 10.0f, "Accepted z-vertex range") + O2_DEFINE_CONFIGURABLE(cfgCutPtMin, float, 0.2f, "Minimal pT for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtMax, float, 1000.0f, "Maximal pT for tracks") O2_DEFINE_CONFIGURABLE(cfgCutEta, float, 0.8f, "Eta range for tracks") - O2_DEFINE_CONFIGURABLE(cfgPtCutMin, float, 0.1f, "Minimal pT for tracks") - O2_DEFINE_CONFIGURABLE(cfgPtCutMax, float, 1000.0f, "Maximal pT for tracks") + O2_DEFINE_CONFIGURABLE(cfgOutputNUAWeights, bool, false, "Fill and output NUA weights") + O2_DEFINE_CONFIGURABLE(cfgCutPtRefMin, float, 0.2f, "Minimal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtRefMax, float, 3.0f, "Maximal pT for ref tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMin, float, 0.2f, "Minimal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutPtPOIMax, float, 10.0f, "Maximal pT for poi tracks") + O2_DEFINE_CONFIGURABLE(cfgCutTPCclu, int, 50, "minimum TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutITSclu, int, 5, "minimum ITS clusters") + O2_DEFINE_CONFIGURABLE(cfgCutChi2prTPCcls, float, 2.5f, "max chi2 per TPC clusters") + O2_DEFINE_CONFIGURABLE(cfgCutTPCcrossedrows, int, 70, "minimum TPC crossed rows") O2_DEFINE_CONFIGURABLE(cfgCutDCAxy, float, 0.2f, "DCAxy cut for tracks") - O2_DEFINE_CONFIGURABLE(cfgDcaxy, bool, true, "choose dcaxy") + O2_DEFINE_CONFIGURABLE(cfgDCAxyNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the transverse direction"); + O2_DEFINE_CONFIGURABLE(cfgDCAxyFunction, std::string, "(0.0015+0.005/(x^1.1))", "Functional form of pt-dependent DCAxy cut"); + O2_DEFINE_CONFIGURABLE(cfgCutDCAz, float, 2.0f, "DCAz cut for tracks") + O2_DEFINE_CONFIGURABLE(cfgCutDCAzPtDepEnabled, bool, false, "switch of DCAz pt dependent cut") + O2_DEFINE_CONFIGURABLE(cfgDCAzFunction, std::string, "(0.0015+0.005/(x^1.1))", "Functional form of pt-dependent DCAz cut"); + O2_DEFINE_CONFIGURABLE(cfgDCAzNSigma, float, 7, "Cut on number of sigma deviations from expected DCA in the z direction"); + O2_DEFINE_CONFIGURABLE(cfgEnableITSCuts, bool, true, "switch of enabling ITS based track selection cuts") + O2_DEFINE_CONFIGURABLE(cfgTrkSelRun3ITSMatch, bool, false, "GlobalTrackRun3ITSMatching::Run3ITSall7Layers selection") + O2_DEFINE_CONFIGURABLE(cfgFlowAcceptance, std::string, "", "CCDB path to acceptance object") + O2_DEFINE_CONFIGURABLE(cfgFlowEfficiency, std::string, "", "CCDB path to efficiency object") + O2_DEFINE_CONFIGURABLE(cfgCentVsIPTruth, std::string, "", "CCDB path to centrality vs IP truth") + O2_DEFINE_CONFIGURABLE(cfgIsGlobalTrack, bool, false, "Use global tracks instead of hasTPC&&hasITS") + O2_DEFINE_CONFIGURABLE(cfgK0Lambda0Enabled, bool, false, "Add K0 and Lambda0, for bulk particle efficiency please keep off") + O2_DEFINE_CONFIGURABLE(cfgAcceptSecondaries, bool, false, "Accept secondary particles produced from decays") + O2_DEFINE_CONFIGURABLE(cfgRequireTOF, bool, false, "Require that reconstructed tracks have TOF for resonance decays") + O2_DEFINE_CONFIGURABLE(cfgFlowCumulantEnabled, bool, false, "switch of calculating flow") + O2_DEFINE_CONFIGURABLE(cfgFlowCumulantNbootstrap, int, 30, "Number of subsamples") + O2_DEFINE_CONFIGURABLE(cfgTrackDensityCorrUse, bool, false, "Use track density efficiency correction") + O2_DEFINE_CONFIGURABLE(cfgTrackDensityCorrSlopeFactor, float, 1.0f, "A factor to scale the track density efficiency slope") + O2_DEFINE_CONFIGURABLE(cfgRecoEvRejectMC, bool, false, "reject both MC and Reco events when reco do not pass") + O2_DEFINE_CONFIGURABLE(cfgRecoEvSel8, bool, false, "require sel8 for reconstruction events") + O2_DEFINE_CONFIGURABLE(cfgRecoEvkIsGoodITSLayers, bool, true, "cut time intervals with dead ITS staves") + O2_DEFINE_CONFIGURABLE(cfgRecoEvkIsGoodITSLayersFlag, int, 0, "0: kIsGoodITSLayersAll; 1: kIsGoodITSLayer0123; 2: kIsGoodITSLayer3") + O2_DEFINE_CONFIGURABLE(cfgRecoEvkNoSameBunchPileup, bool, false, "require kNoSameBunchPileup for reconstruction events") + O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkIsGoodZvtxFT0vsPV, bool, false, "removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference, use this cut at low multiplicities with caution") + O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoITSROFrameBorder, bool, false, "reject events at ITS ROF border") + O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoTimeFrameBorder, bool, false, "reject events at TF border") + O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoCollInTimeRangeStandard, bool, false, "no collisions in specified time range") + O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoCollInRofStandard, bool, false, "no other collisions in this Readout Frame with per-collision multiplicity above threshold") + O2_DEFINE_CONFIGURABLE(cfgITSTPCVertex, bool, false, "use ITS-TPC vertex") + O2_DEFINE_CONFIGURABLE(cfgRecoEvSelkNoHighMultCollInPrevRof, bool, false, "veto an event if FT0C amplitude in previous ITS ROF is above threshold") + O2_DEFINE_CONFIGURABLE(cfgEvSelRCTflags, std::string, "", "keep empty to disable, usage: 'CentralBarrelTracking', 'CBT_hadronPID' ") + O2_DEFINE_CONFIGURABLE(cfgIRFetch, bool, false, "Get interaction rate from CCDB") + O2_DEFINE_CONFIGURABLE(cfgIRCutEnabled, bool, false, "Use events with low interaction rate") + O2_DEFINE_CONFIGURABLE(cfgIRMax, float, 50.0f, "maximum interaction rate (kHz)") + O2_DEFINE_CONFIGURABLE(cfgIRMin, float, 0.0f, "minimum interaction rate (kHz)") + O2_DEFINE_CONFIGURABLE(cfgOccupancyEnabled, bool, true, "Occupancy cut") + O2_DEFINE_CONFIGURABLE(cfgOccupancyMax, int, 2000, "High cut on TPC occupancy") + O2_DEFINE_CONFIGURABLE(cfgOccupancyMin, int, 0, "Low cut on TPC occupancy") + + Configurable> cfgTrackDensityP0{"cfgTrackDensityP0", std::vector{0.6003720411, 0.6152630970, 0.6288860646, 0.6360694031, 0.6409494798, 0.6450540203, 0.6482117301, 0.6512592056, 0.6640008690, 0.6862631416, 0.7005738691, 0.7106567432, 0.7170728333}, "parameter 0 for track density efficiency correction"}; + Configurable> cfgTrackDensityP1{"cfgTrackDensityP1", std::vector{-1.007592e-05, -8.932635e-06, -9.114538e-06, -1.054818e-05, -1.220212e-05, -1.312304e-05, -1.376433e-05, -1.412813e-05, -1.289562e-05, -1.050065e-05, -8.635725e-06, -7.380821e-06, -6.201250e-06}, "parameter 1 for track density efficiency correction"}; ConfigurableAxis axisB{"axisB", {100, 0.0f, 20.0f}, ""}; + ConfigurableAxis axisCentrality{"axisCentrality", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90}, "X axis for histograms"}; + ConfigurableAxis axisPhi{"axisPhi", {100, 0.0f, constants::math::TwoPI}, ""}; + ConfigurableAxis axisNch{"axisNch", {300, 0.0f, 3000.0f}, "Nch in |eta|<0.8"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.2f, 2.4f, 2.6f, 2.8f, 3.0f, 3.2f, 3.4f, 3.6f, 3.8f, 4.0f, 4.4f, 4.8f, 5.2f, 5.6f, 6.0f, 6.5f, 7.0f, 7.5f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f}, "pt axis"}; + + // Filter for MCcollisions + Filter mccollisionFilter = nabs(aod::mccollision::posZ) < cfgCutVertex; + using FilteredMcCollisions = soa::Filtered; + // Filter for MCParticle + Filter particleFilter = (nabs(aod::mcparticle::eta) < cfgCutEta) && (aod::mcparticle::pt > cfgCutPtMin) && (aod::mcparticle::pt < cfgCutPtMax); + using FilteredMcParticles = soa::Filtered>; + // Filter for reco tracks + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); + using FilteredTracks = soa::Filtered>; + + // using FilteredTracks = soa::Join; + + // Additional filters for tracks + TrackSelection myTrackSel; + TF1* fPtDepDCAxy = nullptr; + TF1* fPtDepDCAz = nullptr; + + // Cent vs IP + TH1D* mCentVsIPTruth = nullptr; + // bool centVsIPTruthLoaded = false; + + // Corrections + TH1D* mEfficiency = nullptr; + GFWWeights* mAcceptance = nullptr; + bool correctionsLoaded = false; + + std::vector funcEff; + TH1D* hFindPtBin = nullptr; + TF1* funcV2 = nullptr; + TF1* funcV3 = nullptr; + TF1* funcV4 = nullptr; + enum GoodITSLayersFlag { + kITSLayersAll, + kITSLayer0123, + kITSLayer3, + kCount_ITSLayersFlag + }; + // Connect to ccdb - Service ccdb; + Service ccdb{}; Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - double epsilon = 1e-6; - - // using McParts = soa::Join; + OutputObj fWeights{GFWWeights("weights")}; + OutputObj fFCTrue{FlowContainer("FlowContainerTrue")}; + OutputObj fFCReco{FlowContainer("FlowContainerReco")}; + GFW* fGFWTrue = new GFW(); + GFW* fGFWReco = new GFW(); + TAxis* fPtAxis = nullptr; + std::vector corrconfigsTruth; + std::vector corrconfigsReco; + TRandom3* fRndm = new TRandom3(0); + static constexpr double epsilon = 1e-6; + int mRunNumber{-1}; + uint64_t mSOR{0}; + double mMinSeconds{-1.}; + std::unordered_map gHadronicRate; + ctpRateFetcher mRateFetcher; + TH2* gCurrentHadronicRate = nullptr; + RCTFlagsChecker rctChecker{"CBT"}; void init(InitContext&) { @@ -80,124 +211,725 @@ struct FlowMcUpc { const AxisSpec axisCounter{1, 0, +1, ""}; // QA histograms histos.add("mcEventCounter", "Monte Carlo Truth EventCounter", HistType::kTH1F, {{5, 0, 5}}); + histos.add("numberOfRecoCollisions", "numberOfRecoCollisions", HistType::kTH1F, {{10, -1.5f, 8.5f}}); + histos.add("RecoEventCounter", "Reconstruction EventCounter", HistType::kTH1F, {{5, 0, 5}}); histos.add("RecoProcessEventCounter", "Reconstruction EventCounter", HistType::kTH1F, {{5, 0, 5}}); + histos.add("hnTPCClu", "Number of found TPC clusters", HistType::kTH1D, {{100, 40, 180}}); + histos.add("hnITSClu", "Number of found ITS clusters", HistType::kTH1D, {{100, 0, 20}}); + // pT histograms histos.add("hImpactParameter", "hImpactParameter", HistType::kTH1D, {axisB}); - + histos.add("hNchVsImpactParameter", "hNchVsImpactParameter", HistType::kTH2D, {axisB, axisNch}); + histos.add("hNchRecoVsNchGen", "hNchRecoVsNchGen; Recoconstucted Nch; Genenerated Nch", HistType::kTH2D, {axisNch, axisNch}); + histos.add("hEventPlaneAngle", "hEventPlaneAngle", HistType::kTH1D, {axisPhi}); + histos.add("hPtVsPhiGenerated", "hPtVsPhiGenerated", HistType::kTH2D, {axisPhi, axisPt}); + histos.add("hPtVsPhiGlobal", "hPtVsPhiGlobal", HistType::kTH2D, {axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGenerated", "hBVsPtVsPhiGenerated", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGlobal", "hBVsPtVsPhiGlobal", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiAny", "hBVsPtVsPhiAny", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiTPCTrack", "hBVsPtVsPhiTPCTrack", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiITSTrack", "hBVsPtVsPhiITSTrack", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiITSABTrack", "hBVsPtVsPhiITSABTrack", HistType::kTH3D, {axisB, axisPhi, axisPt}); + + histos.add("hBVsPtVsPhiGeneratedK0Short", "hBVsPtVsPhiGeneratedK0Short", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGlobalK0Short", "hBVsPtVsPhiGlobalK0Short", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGeneratedLambda", "hBVsPtVsPhiGeneratedLambda", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGlobalLambda", "hBVsPtVsPhiGlobalLambda", HistType::kTH3D, {axisB, axisPhi, axisPt}); + + histos.add("hBVsPtVsPhiGeneratedXi", "hBVsPtVsPhiGeneratedXi", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGlobalXi", "hBVsPtVsPhiGlobalXi", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGeneratedOmega", "hBVsPtVsPhiGeneratedOmega", HistType::kTH3D, {axisB, axisPhi, axisPt}); + histos.add("hBVsPtVsPhiGlobalOmega", "hBVsPtVsPhiGlobalOmega", HistType::kTH3D, {axisB, axisPhi, axisPt}); + + histos.add("hPhi", "#phi distribution", HistType::kTH1D, {axisPhi}); + histos.add("hPhiWeighted", "corrected #phi distribution", HistType::kTH1D, {axisPhi}); + histos.add("hEPVsPhiMC", "hEPVsPhiMC;Event Plane Angle; #varphi", HistType::kTH2D, {axisPhi, axisPhi}); + histos.add("hEPVsPhi", "hEPVsPhi;Event Plane Angle; #varphi", HistType::kTH2D, {axisPhi, axisPhi}); + histos.add("hPtNchGenerated", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobal", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedPion", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalPion", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedKaon", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalKaon", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedProton", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalProton", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedK0", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalK0", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedLambda", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalLambda", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedK0Pions", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalK0Pions", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedLambdaPions", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalLambdaPions", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGeneratedLambdaProtons", "Reco production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); + histos.add("hPtNchGlobalLambdaProtons", "Global production; pT (GeV/c); multiplicity", HistType::kTH2D, {axisPt, axisNch}); histos.add("hPtMCGen", "Monte Carlo Truth; pT (GeV/c);", {HistType::kTH1D, {axisPt}}); histos.add("hEtaPtVtxzMCGen", "Monte Carlo Truth; #eta; p_{T} (GeV/c); V_{z} (cm);", {HistType::kTH3D, {axisEta, axisPt, axisVertex}}); + histos.add("hPtMCGlobal", "Monte Carlo Global; pT (GeV/c);", {HistType::kTH1D, {axisPt}}); histos.add("hPtReco", "Monte Carlo Reco Global; pT (GeV/c);", {HistType::kTH1D, {axisPt}}); - histos.add("hEtaPtVtxzMCReco", "Monte Carlo Global; #eta; p_{T} (GeV/c); V_{z} (cm);", {HistType::kTH3D, {axisEta, axisPt, axisVertex}}); + histos.add("hPtReco_PhysicalPrimary", "Monte Carlo Reco Global; pT (GeV/c);", {HistType::kTH1D, {axisPt}}); + histos.add("hEtaPtVtxzMCGlobal", "Monte Carlo Global; #eta; p_{T} (GeV/c); V_{z} (cm);", {HistType::kTH3D, {axisEta, axisPt, axisVertex}}); + histos.add("hPhiWeightedTrDen", "corrected #phi distribution, considering track density", {HistType::kTH1D, {axisPhi}}); + + o2::framework::AxisSpec axis = axisPt; + int nPtBins = axis.binEdges.size() - 1; + double* ptBins = axis.binEdges.data(); + fPtAxis = new TAxis(nPtBins, ptBins); + + if (cfgOutputNUAWeights) { + fWeights->setPtBins(nPtBins, ptBins); + fWeights->init(true, false); + } + + if (cfgFlowCumulantEnabled) { + auto oba = new TObjArray(); + oba->Add(new TNamed("ChFull22", "ChFull22")); + for (auto i = 0; i < fPtAxis->GetNbins(); i++) { + oba->Add(new TNamed(Form("ChFull22_pt_%i", i + 1), "ChFull22_pTDiff")); + } + oba->Add(new TNamed("Ch10Gap22", "Ch10Gap22")); + for (auto i = 0; i < fPtAxis->GetNbins(); i++) { + oba->Add(new TNamed(Form("Ch10Gap22_pt_%i", i + 1), "Ch10Gap22_pTDiff")); + } + fFCTrue->SetName("FlowContainerTrue"); + fFCTrue->SetXAxis(fPtAxis); + fFCTrue->Initialize(oba, axisCentrality, cfgFlowCumulantNbootstrap); + fFCReco->SetName("FlowContainerReco"); + fFCReco->SetXAxis(fPtAxis); + fFCReco->Initialize(oba, axisCentrality, cfgFlowCumulantNbootstrap); + delete oba; + + fGFWTrue->AddRegion("full", -0.8, 0.8, 1, 1); + fGFWTrue->AddRegion("refN10", -0.8, -0.5, 1, 1); + fGFWTrue->AddRegion("refP10", 0.5, 0.8, 1, 1); + fGFWTrue->AddRegion("poiN10", -0.8, -0.5, 1 + fPtAxis->GetNbins(), 2); + fGFWTrue->AddRegion("poifull", -0.8, 0.8, 1 + fPtAxis->GetNbins(), 2); + fGFWTrue->AddRegion("olN10", -0.8, -0.5, 1 + fPtAxis->GetNbins(), 4); + fGFWTrue->AddRegion("olfull", -0.8, 0.8, 1 + fPtAxis->GetNbins(), 4); + corrconfigsTruth.push_back(fGFWTrue->GetCorrelatorConfig("full {2 -2}", "ChFull22", kFALSE)); + corrconfigsTruth.push_back(fGFWTrue->GetCorrelatorConfig("poifull full | olfull {2 -2}", "ChFull22", kTRUE)); + corrconfigsTruth.push_back(fGFWTrue->GetCorrelatorConfig("refN10 {2} refP10 {-2}", "Ch10Gap22", kFALSE)); + corrconfigsTruth.push_back(fGFWTrue->GetCorrelatorConfig("poiN10 refN10 | olN10 {2} refP10 {-2}", "Ch10Gap22", kTRUE)); + fGFWTrue->CreateRegions(); + + fGFWReco->AddRegion("full", -0.8, 0.8, 1, 1); + fGFWReco->AddRegion("refN10", -0.8, -0.5, 1, 1); + fGFWReco->AddRegion("refP10", 0.5, 0.8, 1, 1); + fGFWReco->AddRegion("poiN10", -0.8, -0.5, 1 + fPtAxis->GetNbins(), 2); + fGFWReco->AddRegion("poifull", -0.8, 0.8, 1 + fPtAxis->GetNbins(), 2); + fGFWReco->AddRegion("olN10", -0.8, -0.5, 1 + fPtAxis->GetNbins(), 4); + fGFWReco->AddRegion("olfull", -0.8, 0.8, 1 + fPtAxis->GetNbins(), 4); + corrconfigsReco.push_back(fGFWReco->GetCorrelatorConfig("full {2 -2}", "ChFull22", kFALSE)); + corrconfigsReco.push_back(fGFWReco->GetCorrelatorConfig("poifull full | olfull {2 -2}", "ChFull22", kTRUE)); + corrconfigsReco.push_back(fGFWReco->GetCorrelatorConfig("refN10 {2} refP10 {-2}", "Ch10Gap22", kFALSE)); + corrconfigsReco.push_back(fGFWReco->GetCorrelatorConfig("poiN10 refN10 | olN10 {2} refP10 {-2}", "Ch10Gap22", kTRUE)); + fGFWReco->CreateRegions(); + } + + if (cfgEnableITSCuts) { + if (cfgTrkSelRun3ITSMatch) { + myTrackSel = getGlobalTrackSelectionRun3ITSMatch(TrackSelection::GlobalTrackRun3ITSMatching::Run3ITSall7Layers, TrackSelection::GlobalTrackRun3DCAxyCut::Default); + } else { + myTrackSel = getGlobalTrackSelectionRun3ITSMatch(TrackSelection::GlobalTrackRun3ITSMatching::Run3ITSibAny, TrackSelection::GlobalTrackRun3DCAxyCut::Default); + } + } + if (cfgCutDCAxy != 0.0) { + myTrackSel.SetMaxDcaXY(cfgCutDCAxy); + } else { + fPtDepDCAxy = new TF1("ptDepDCAxy", Form("[0]*%s", cfgDCAxyFunction->c_str()), 0.001, 100); + fPtDepDCAxy->SetParameter(0, cfgDCAxyNSigma); + myTrackSel.SetMaxDcaXYPtDep([fPtDepDCAxy = this->fPtDepDCAxy](float pt) { return fPtDepDCAxy->Eval(pt); }); + } + myTrackSel.SetMinNClustersTPC(cfgCutTPCclu); + myTrackSel.SetMinNCrossedRowsTPC(cfgCutTPCcrossedrows); + if (cfgEnableITSCuts) { + myTrackSel.SetMinNClustersITS(cfgCutITSclu); + } + if (!cfgCutDCAzPtDepEnabled) { + myTrackSel.SetMaxDcaZ(cfgCutDCAz); + } else { + fPtDepDCAz = new TF1("ptDepDCAxy", Form("[0]*%s", cfgDCAzFunction->c_str()), 0.001, 100); + fPtDepDCAz->SetParameter(0, cfgDCAzNSigma); + LOGF(info, "DCAz pt-dependence function: %s", Form("[0]*%s", cfgDCAzFunction->c_str())); + } + if (cfgTrackDensityCorrUse) { + std::vector pTEffBins = {0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.4, 1.8, 2.2, 2.6, 3.0}; + hFindPtBin = new TH1D("hFindPtBin", "hFindPtBin", pTEffBins.size() - 1, pTEffBins.data()); + funcEff.resize(pTEffBins.size() - 1); + // LHC24g3 Eff + std::vector f1p0 = cfgTrackDensityP0; + std::vector f1p1 = cfgTrackDensityP1; + for (uint ifunc = 0; ifunc < pTEffBins.size() - 1; ifunc++) { + funcEff[ifunc] = new TF1(Form("funcEff%i", ifunc), "[0]+[1]*x", 0, 3000); + funcEff[ifunc]->SetParameters(f1p0[ifunc], f1p1[ifunc] * cfgTrackDensityCorrSlopeFactor); + } + funcV2 = new TF1("funcV2", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + funcV2->SetParameters(0.0186111, 0.00351907, -4.38264e-05, 1.35383e-07, -3.96266e-10); + funcV3 = new TF1("funcV3", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + funcV3->SetParameters(0.0174056, 0.000703329, -1.45044e-05, 1.91991e-07, -1.62137e-09); + funcV4 = new TF1("funcV4", "[0]+[1]*x+[2]*x*x+[3]*x*x*x+[4]*x*x*x*x", 0, 100); + funcV4->SetParameters(0.008845, 0.000259668, -3.24435e-06, 4.54837e-08, -6.01825e-10); + } + if (!cfgEvSelRCTflags.value.empty()) { + rctChecker.init(cfgEvSelRCTflags.value); // override initialzation + } } - // template - // bool eventSelected(TCollision collision) - // { - // return true; - // } + void loadCorrections(uint64_t timestamp) + { + if (correctionsLoaded) { + return; + } + if (!cfgFlowAcceptance.value.empty()) { + mAcceptance = ccdb->getForTimeStamp(cfgFlowAcceptance, timestamp); + if (mAcceptance) { + LOGF(info, "Loaded acceptance weights from %s (%p)", cfgFlowAcceptance.value.c_str(), static_cast(mAcceptance)); + } else { + LOGF(warning, "Could not load acceptance weights from %s (%p)", cfgFlowAcceptance.value.c_str(), static_cast(mAcceptance)); + } + } + if (!cfgFlowEfficiency.value.empty()) { + mEfficiency = ccdb->getForTimeStamp(cfgFlowEfficiency, timestamp); + if (mEfficiency == nullptr) { + LOGF(fatal, "Could not load efficiency histogram for trigger particles from %s", cfgFlowEfficiency.value.c_str()); + } + LOGF(info, "Loaded efficiency histogram from %s (%p)", cfgFlowEfficiency.value.c_str(), static_cast(mEfficiency)); + } + correctionsLoaded = true; + } - template - bool trackSelected(TTrack const& track) + bool setCurrentParticleWeights(float& weight_nue, float& weight_nua, float phi, float eta, float pt, float vtxz) { - // auto momentum = std::array{track.px(), track.py(), track.pz()}; - auto pt = track.pt(); - if (pt < cfgPtCutMin || pt > cfgPtCutMax) { - return false; + float eff = 1.; + if (mEfficiency) { + eff = mEfficiency->GetBinContent(mEfficiency->FindBin(pt)); + } else { + eff = 1.0; } - double dcaLimit = 0.0105 + 0.035 / std::pow(pt, 1.1); - if (cfgDcaxy && !(std::fabs(track.dcaXY()) < dcaLimit)) { + if (eff == 0) { return false; } + weight_nue = 1. / eff; + if (mAcceptance) { + weight_nua = mAcceptance->getNUA(phi, eta, vtxz); + } else { + weight_nua = 1; + } return true; } - PresliceUnsorted partPerMcCollision = aod::udmcparticle::udMcCollisionId; - - void processMCTrue(aod::UDMcCollisions const& mcCollisions, aod::UDMcParticles const& mcParts, aod::BCs const& bcs) + void fillFC(GFW* fGFW, bool isMCTruth, const GFW::CorrConfig& corrconf, const double& cent, const double& rndm) { - if (bcs.size() == 0) { + double dnx = 0, val = 0; + dnx = fGFW->Calculate(corrconf, 0, kTRUE).real(); + if (!corrconf.pTDif) { + if (dnx == 0) { + return; + } + val = fGFW->Calculate(corrconf, 0, kFALSE).real() / dnx; + if (std::fabs(val) < 1) { + if (isMCTruth) { + fFCTrue->FillProfile(corrconf.Head.c_str(), cent, val, dnx, rndm); + } else { + fFCReco->FillProfile(corrconf.Head.c_str(), cent, val, dnx, rndm); + } + } return; } - for (const auto& mcCollision : mcCollisions) { - histos.fill(HIST("mcEventCounter"), 0.5); - float imp = mcCollision.impactParameter(); - float vtxz = mcCollision.posZ(); - - if (imp >= minB && imp <= maxB) { - // event within range - histos.fill(HIST("hImpactParameter"), imp); - - auto const& tempParts = mcParts.sliceBy(partPerMcCollision, static_cast(mcCollision.globalIndex())); - - for (auto const& mcParticle : tempParts) { - auto momentum = std::array{mcParticle.px(), mcParticle.py(), mcParticle.pz()}; - int pdgCode = std::abs(mcParticle.pdgCode()); + for (auto i = 1; i <= fPtAxis->GetNbins(); i++) { + dnx = fGFW->Calculate(corrconf, i - 1, kTRUE).real(); + if (dnx == 0) { + continue; + } + val = fGFW->Calculate(corrconf, i - 1, kFALSE).real() / dnx; + if (std::fabs(val) < 1) { + if (isMCTruth) { + fFCTrue->FillProfile(Form("%s_pt_%i", corrconf.Head.c_str(), i), cent, val, dnx, rndm); + } else { + fFCReco->FillProfile(Form("%s_pt_%i", corrconf.Head.c_str(), i), cent, val, dnx, rndm); + } + } + } + } - double pt = RecoDecay::pt(momentum); - double eta = RecoDecay::eta(momentum); + void loadCentVsIPTruth(uint64_t timestamp) + { + if (mCentVsIPTruth != nullptr) { + return; + } + if (!cfgCentVsIPTruth.value.empty()) { + mCentVsIPTruth = ccdb->getForTimeStamp(cfgCentVsIPTruth, timestamp); + if (mCentVsIPTruth) { + LOGF(info, "Loaded CentVsIPTruth weights from %s (%p)", cfgCentVsIPTruth.value.c_str(), static_cast(mCentVsIPTruth)); + } else { + LOGF(fatal, "Failed to load CentVsIPTruth weights from %s", cfgCentVsIPTruth.value.c_str()); + } - if (pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != PDG_t::kKPlus && pdgCode != PDG_t::kProton) - continue; + } else { + LOGF(fatal, "when calculate flow, Cent Vs IP distribution must be provided"); + } + } - if (!mcParticle.isPhysicalPrimary()) - continue; - if (std::fabs(eta) > cfgCutEta) // main acceptance - continue; + void initHadronicRate(aod::BCsWithTimestamps::iterator const& bc) + { + if (mRunNumber == bc.runNumber()) { + return; + } + mRunNumber = bc.runNumber(); + if (!gHadronicRate.contains(mRunNumber)) { + auto runDuration = ccdb->getRunDuration(mRunNumber); + mSOR = runDuration.first; + mMinSeconds = std::floor(mSOR * 1.e-3); /// round tsSOR to the highest integer lower than tsSOR + double maxSec = std::ceil(runDuration.second * 1.e-3); /// round tsEOR to the lowest integer higher than tsEOR + const AxisSpec axisSeconds{static_cast((maxSec - mMinSeconds) / 20.f), 0, maxSec - mMinSeconds, "Seconds since SOR"}; + gHadronicRate[mRunNumber] = histos.add(Form("HadronicRate/%i", mRunNumber), ";Time since SOR (s);Hadronic rate (kHz)", kTH2D, {axisSeconds, {510, 0., 51.}}).get(); + } + gCurrentHadronicRate = gHadronicRate[mRunNumber]; + } - histos.fill(HIST("hPtMCGen"), pt); - histos.fill(HIST("hEtaPtVtxzMCGen"), eta, pt, vtxz); - } + template + bool eventSelected(const TCollision& collision) + { + if (std::fabs(collision.posZ()) > cfgCutVertex) { + return 0; + } + if (cfgRecoEvSel8 && !collision.sel8()) { + return 0; + } + if (cfgOccupancyEnabled) { + auto occupancy = collision.trackOccupancyInTimeRange(); + if (occupancy < cfgOccupancyMin || occupancy > cfgOccupancyMax) { + return 0; + } + } + if (!cfgEvSelRCTflags.value.empty() && !rctChecker(*collision)) { + return 0; + } + if (cfgRecoEvkNoSameBunchPileup && !collision.selection_bit(o2::aod::evsel::kNoSameBunchPileup)) { + // rejects collisions which are associated with the same "found-by-T0" bunch crossing + // https://indico.cern.ch/event/1396220/#1-event-selection-with-its-rof + return 0; + } + if (cfgRecoEvkIsGoodITSLayers) { + // from Jan 9 2025 AOT meeting + // cut time intervals with dead ITS staves + if (cfgRecoEvkIsGoodITSLayersFlag == kITSLayersAll && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayersAll)) { + return 0; + } + if (cfgRecoEvkIsGoodITSLayersFlag == kITSLayer0123 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer0123)) { + return 0; + } + if (cfgRecoEvkIsGoodITSLayersFlag == kITSLayer3 && !collision.selection_bit(o2::aod::evsel::kIsGoodITSLayer3)) { + return 0; } } + if (cfgRecoEvSelkIsGoodZvtxFT0vsPV && !collision.selection_bit(o2::aod::evsel::kIsGoodZvtxFT0vsPV)) { + // removes collisions with large differences between z of PV by tracks and z of PV from FT0 A-C time difference + // use this cut at low multiplicities with caution + return 0; + } + if (cfgRecoEvSelkNoITSROFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoITSROFrameBorder)) { + return 0; + } + if (cfgRecoEvSelkNoTimeFrameBorder && !collision.selection_bit(o2::aod::evsel::kNoTimeFrameBorder)) { + return 0; + } + if (cfgRecoEvSelkNoCollInTimeRangeStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInTimeRangeStandard)) { + // no collisions in specified time range + return 0; + } + if (cfgRecoEvSelkNoCollInRofStandard && !collision.selection_bit(o2::aod::evsel::kNoCollInRofStandard)) { + // no other collisions in this Readout Frame with per-collision multiplicity above threshold + return 0; + } + if (cfgRecoEvSelkNoHighMultCollInPrevRof && !collision.selection_bit(o2::aod::evsel::kNoHighMultCollInPrevRof)) { + // veto an event if FT0C amplitude in previous ITS ROF is above threshold + return 0; + } + if (cfgITSTPCVertex && !collision.selection_bit(o2::aod::evsel::kIsVertexITSTPC)) { + return 0; + } + return 1; } - PROCESS_SWITCH(FlowMcUpc, processMCTrue, "process pure simulation information", true); - - using MCRecoTracks = soa::Join; - using MCRecoCollisions = soa::Join; - // PresliceUnsorted trackPerMcParticle = aod::udmctracklabel::udMcParticleId; - Preslice trackPerCollision = aod::udtrack::udCollisionId; // sorted preslice used because the pair track-collision is already sorted in processDataSG function + template + bool trackSelected(const TTrack& track) + { + if (cfgCutDCAzPtDepEnabled && (std::fabs(track.dcaZ()) > fPtDepDCAz->Eval(track.pt()))) { + return false; + } + return myTrackSel.IsSelected(track); + } - void processReco(MCRecoCollisions const& collisions, MCRecoTracks const& tracks) + void processMCTrue(FilteredMcCollisions::iterator const& mcCollision, aod::BCsWithTimestamps const&, soa::SmallGroups> const& collisions, FilteredMcParticles const& mcParticles, FilteredTracks const&) { - for (const auto& collision : collisions) { - histos.fill(HIST("RecoProcessEventCounter"), 0.5); - // if (!eventSelected(collision)) - // return; - histos.fill(HIST("RecoProcessEventCounter"), 1.5); - if (!collision.has_udMcCollision()) + histos.fill(HIST("mcEventCounter"), 0.5); + float imp = mcCollision.impactParameter(); + float evPhi = mcCollision.eventPlaneAngle(); + float vtxz = mcCollision.posZ(); + evPhi = RecoDecay::constrainAngle(evPhi); + + int64_t nCh = 0; + int64_t nChGlobal = 0; + int64_t nChGen = 0; + float centrality = 0; + float lRandom = fRndm->Rndm(); + float weff = 1.; + float wacc = 1.; + auto bc = mcCollision.bc_as(); + if (cfgIRFetch) { + initHadronicRate(bc); + double hadronicRate = mRateFetcher.fetch(ccdb.service, bc.timestamp(), mRunNumber, "ZNC hadronic") * 1.e-3; // + double seconds = bc.timestamp() * 1.e-3 - mMinSeconds; + if (cfgIRCutEnabled && (hadronicRate < cfgIRMin || hadronicRate > cfgIRMax)) { // cut on hadronic rate return; - histos.fill(HIST("RecoProcessEventCounter"), 2.5); - if (tracks.size() < 1) - return; - histos.fill(HIST("RecoProcessEventCounter"), 3.5); + } + gCurrentHadronicRate->Fill(seconds, hadronicRate); + } - float vtxz = collision.posZ(); + if (collisions.size() > -1) { + histos.fill(HIST("numberOfRecoCollisions"), collisions.size()); // number of times coll was reco-ed + histos.fill(HIST("RecoEventCounter"), 0.5); + if (cfgRecoEvRejectMC) { + if (collisions.size() != 1) { // only pass those have one reconstruction event + return; + } + histos.fill(HIST("RecoEventCounter"), 1.5); + for (auto const& collision : collisions) { + if (!eventSelected(collision)) { + return; + } + } + histos.fill(HIST("RecoEventCounter"), 2.5); + } + } + histos.fill(HIST("RecoEventCounter"), 3.5); + + if (imp >= minB && imp <= maxB) { + // event within range + histos.fill(HIST("hImpactParameter"), imp); + histos.fill(HIST("hEventPlaneAngle"), evPhi); + if (cfgFlowCumulantEnabled) { + loadCentVsIPTruth(bc.timestamp()); + centrality = mCentVsIPTruth->GetBinContent(mCentVsIPTruth->GetXaxis()->FindBin(imp)); + fGFWTrue->Clear(); + fGFWReco->Clear(); + } - auto const& tempTracks = tracks.sliceBy(trackPerCollision, static_cast(collision.globalIndex())); + double psi2Est = 0, psi3Est = 0, psi4Est = 0; + float wEPeff = 1; + double v2 = 0, v3 = 0, v4 = 0; + double q2x = 0, q2y = 0; + double q3x = 0, q3y = 0; + double q4x = 0, q4y = 0; + for (auto const& mcParticle : mcParticles) { + int pdgCode = std::abs(mcParticle.pdgCode()); + if (pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != kKPlus && pdgCode != PDG_t::kProton) { + continue; + } + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::fabs(mcParticle.eta()) > cfgCutEta) { // main acceptance + continue; + } + nChGen++; + if (mcParticle.has_tracks()) { + auto const& tracks = mcParticle.tracks_as(); + for (auto const& track : tracks) { + if (!trackSelected(track)) { + continue; + } + if (cfgIsGlobalTrack && track.isGlobalTrack()) { + nChGlobal++; + } + if (!cfgIsGlobalTrack && track.hasTPC() && track.hasITS()) { + nChGlobal++; + } + if (cfgTrackDensityCorrUse && cfgFlowCumulantEnabled) { + bool withinPtRef = (cfgCutPtRefMin < track.pt()) && (track.pt() < cfgCutPtRefMax); // within RF pT rang + if (withinPtRef) { + q2x += std::cos(2 * track.phi()); + q2y += std::sin(2 * track.phi()); + q3x += std::cos(3 * track.phi()); + q3y += std::sin(3 * track.phi()); + q4x += std::cos(4 * track.phi()); + q4y += std::sin(4 * track.phi()); + } + } + } + } + } + if (nChGlobal > 0 && nChGen > 0) { + // fill only when Nch is not zero + histos.fill(HIST("hNchRecoVsNchGen"), nChGlobal, nChGen); + } + if (cfgTrackDensityCorrUse && cfgFlowCumulantEnabled) { + psi2Est = std::atan2(q2y, q2x) / 2.; + psi3Est = std::atan2(q3y, q3x) / 3.; + psi4Est = std::atan2(q4y, q4x) / 4.; + v2 = funcV2->Eval(centrality); + v3 = funcV3->Eval(centrality); + v4 = funcV4->Eval(centrality); + } - for (const auto& track : tempTracks) { + for (auto const& mcParticle : mcParticles) { // focus on bulk: e, mu, pi, k, p - auto momentum = std::array{track.px(), track.py(), track.pz()}; - double pt = RecoDecay::pt(momentum); - double eta = RecoDecay::eta(momentum); - // double phi = RecoDecay::phi(momentum); - if (!trackSelected(track) || (!track.has_udMcParticle())) - continue; - auto mcParticle = track.udMcParticle(); int pdgCode = std::abs(mcParticle.pdgCode()); + bool extraPDGType = true; + if (cfgK0Lambda0Enabled) { + extraPDGType = (pdgCode != PDG_t::kK0Short && pdgCode != PDG_t::kLambda0); + } + if (extraPDGType && pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != kKPlus && pdgCode != PDG_t::kProton) { + continue; + } - // double pt = recoMC.Pt(); - // double eta = recoMC.Eta(); - if (pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != PDG_t::kKPlus && pdgCode != PDG_t::kProton) + bool isPhysicalPrimary = mcParticle.isPhysicalPrimary(); + const int producedByDecay = 4; + bool isSecondary = (mcParticle.has_mothers() && mcParticle.getProcess() == producedByDecay); + bool isAcceptedSecondary = (cfgAcceptSecondaries) ? isSecondary : false; + if (!isPhysicalPrimary && !isAcceptedSecondary) { continue; - if (std::fabs(eta) > cfgCutEta) // main acceptance + } + + if (std::fabs(mcParticle.eta()) > cfgCutEta) { // main acceptance continue; - if (!mcParticle.isPhysicalPrimary()) + } + + float deltaPhi = mcParticle.phi() - mcCollision.eventPlaneAngle(); + deltaPhi = RecoDecay::constrainAngle(deltaPhi); + histos.fill(HIST("hPtVsPhiGenerated"), deltaPhi, mcParticle.pt()); + histos.fill(HIST("hBVsPtVsPhiGenerated"), imp, deltaPhi, mcParticle.pt()); + histos.fill(HIST("hPtNchGenerated"), mcParticle.pt(), nChGlobal); + histos.fill(HIST("hPtMCGen"), mcParticle.pt()); + histos.fill(HIST("hEtaPtVtxzMCGen"), mcParticle.eta(), mcParticle.pt(), vtxz); + if (pdgCode == PDG_t::kPiPlus) { + histos.fill(HIST("hPtNchGeneratedPion"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kKPlus) { + histos.fill(HIST("hPtNchGeneratedKaon"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kProton) { + histos.fill(HIST("hPtNchGeneratedProton"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kK0Short) { + histos.fill(HIST("hPtNchGeneratedK0"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kLambda0) { + histos.fill(HIST("hPtNchGeneratedLambda"), mcParticle.pt(), nChGlobal); + } + if (mcParticle.has_daughters()) { + for (const auto& d : mcParticle.template daughters_as()) { + if (std::abs(d.pdgCode()) == PDG_t::kPiPlus) { + if (pdgCode == PDG_t::kK0Short) { + histos.fill(HIST("hPtNchGeneratedK0Pions"), d.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kLambda0) { + histos.fill(HIST("hPtNchGeneratedLambdaPions"), d.pt(), nChGlobal); + } + } + if (pdgCode == PDG_t::kLambda0 && std::abs(d.pdgCode()) == PDG_t::kProton) { + histos.fill(HIST("hPtNchGeneratedLambdaProtons"), d.pt(), nChGlobal); + } + } + } + nCh++; + + bool validGlobal = false; + bool validTrack = false; + bool validTOFTrack = false; + bool validTPCTrack = false; + bool validITSTrack = false; + bool validITSABTrack = false; + if (mcParticle.has_tracks()) { + auto const& tracks = mcParticle.tracks_as(); + for (auto const& track : tracks) { + if (!trackSelected(track)) { + continue; + } + histos.fill(HIST("hnTPCClu"), track.tpcNClsFound()); + histos.fill(HIST("hnITSClu"), track.itsNCls()); + if (cfgIsGlobalTrack && track.isGlobalTrack()) { + validGlobal = true; + } + if (!cfgIsGlobalTrack && track.hasTPC() && track.hasITS()) { + validGlobal = true; + } + if (track.hasTOF() && validGlobal) { + validTOFTrack = true; + } + if (track.hasTPC() || track.hasITS()) { + validTrack = true; + } + if (track.hasTPC()) { + validTPCTrack = true; + } + if (track.hasITS() && track.itsChi2NCl() > -1. * epsilon) { + validITSTrack = true; + } + if (track.hasITS() && track.itsChi2NCl() < -1. * epsilon) { + validITSABTrack = true; + } + } + } + bool withinPtRef = (cfgCutPtRefMin < mcParticle.pt()) && (mcParticle.pt() < cfgCutPtRefMax); // within RF pT range + bool withinPtPOI = (cfgCutPtPOIMin < mcParticle.pt()) && (mcParticle.pt() < cfgCutPtPOIMax); // within POI pT range + if (cfgOutputNUAWeights && withinPtRef) { + fWeights->fill(mcParticle.phi(), mcParticle.eta(), vtxz, mcParticle.pt(), 0, 0); + } + if (!setCurrentParticleWeights(weff, wacc, mcParticle.phi(), mcParticle.eta(), mcParticle.pt(), vtxz)) { continue; + } + if (cfgTrackDensityCorrUse && cfgFlowCumulantEnabled && withinPtRef) { + double fphi = v2 * std::cos(2 * (mcParticle.phi() - psi2Est)) + v3 * std::cos(3 * (mcParticle.phi() - psi3Est)) + v4 * std::cos(4 * (mcParticle.phi() - psi4Est)); + fphi = (1 + 2 * fphi); + int pTBinForEff = hFindPtBin->FindBin(mcParticle.pt()); + if (pTBinForEff >= 1 && pTBinForEff <= hFindPtBin->GetNbinsX()) { + wEPeff = funcEff[pTBinForEff - 1]->Eval(fphi * nChGlobal); + if (wEPeff > 0.) { + wEPeff = 1. / wEPeff; + weff *= wEPeff; + histos.fill(HIST("hPhiWeightedTrDen"), mcParticle.phi(), wacc * wEPeff); + } + } + } + + if (cfgFlowCumulantEnabled) { + if (withinPtRef) { + fGFWTrue->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 1); + } + if (withinPtPOI) { + fGFWTrue->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 2); + } + if (withinPtPOI && withinPtRef) { + fGFWTrue->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 4); + } + + if (validGlobal) { + if (withinPtRef) { + fGFWReco->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 1); + } + if (withinPtPOI) { + fGFWReco->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 2); + } + if (withinPtPOI && withinPtRef) { + fGFWReco->Fill(mcParticle.eta(), fPtAxis->FindBin(mcParticle.pt()) - 1, mcParticle.phi(), wacc * weff, 4); + } + } + } - histos.fill(HIST("hPtReco"), pt); - histos.fill(HIST("hEtaPtVtxzMCReco"), eta, pt, vtxz); + if (withinPtRef) { + histos.fill(HIST("hEPVsPhiMC"), evPhi, mcParticle.phi()); + } + + if (validGlobal && withinPtRef) { + histos.fill(HIST("hPhi"), mcParticle.phi()); + histos.fill(HIST("hPhiWeighted"), mcParticle.phi(), wacc); + histos.fill(HIST("hEPVsPhi"), evPhi, mcParticle.phi()); + } + + // if valid global, fill + if (validGlobal) { + histos.fill(HIST("hPtVsPhiGlobal"), deltaPhi, mcParticle.pt(), wacc * weff); + histos.fill(HIST("hBVsPtVsPhiGlobal"), imp, deltaPhi, mcParticle.pt(), wacc * weff); + histos.fill(HIST("hPtNchGlobal"), mcParticle.pt(), nChGlobal); + histos.fill(HIST("hPtMCGlobal"), mcParticle.pt()); + histos.fill(HIST("hEtaPtVtxzMCGlobal"), mcParticle.eta(), mcParticle.pt(), vtxz); + if (pdgCode == PDG_t::kPiPlus) { + histos.fill(HIST("hPtNchGlobalPion"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kKPlus) { + histos.fill(HIST("hPtNchGlobalKaon"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kProton) { + histos.fill(HIST("hPtNchGlobalProton"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kK0Short) { + histos.fill(HIST("hPtNchGlobalK0"), mcParticle.pt(), nChGlobal); + } + if (pdgCode == PDG_t::kLambda0) { + histos.fill(HIST("hPtNchGlobalLambda"), mcParticle.pt(), nChGlobal); + } + if (!cfgRequireTOF || validTOFTrack) { + if (mcParticle.has_mothers()) { + for (const auto& m : mcParticle.template mothers_as()) { + if (!m.isPhysicalPrimary()) { + continue; + } + if (pdgCode == PDG_t::kPiPlus) { + if (m.pdgCode() == PDG_t::kK0Short) { + histos.fill(HIST("hPtNchGlobalK0Pions"), mcParticle.pt(), nChGlobal); + } + if (m.pdgCode() == PDG_t::kLambda0) { + histos.fill(HIST("hPtNchGlobalLambdaPions"), mcParticle.pt(), nChGlobal); + } + } + if (pdgCode == PDG_t::kProton && m.pdgCode() == PDG_t::kLambda0) { + histos.fill(HIST("hPtNchGlobalLambdaProtons"), mcParticle.pt(), nChGlobal); + } + } + } + } + } + // if any track present, fill + if (validTrack) { + histos.fill(HIST("hBVsPtVsPhiAny"), imp, deltaPhi, mcParticle.pt(), wacc * weff); + } + if (validTPCTrack) { + histos.fill(HIST("hBVsPtVsPhiTPCTrack"), imp, deltaPhi, mcParticle.pt(), wacc * weff); + } + if (validITSTrack) { + histos.fill(HIST("hBVsPtVsPhiITSTrack"), imp, deltaPhi, mcParticle.pt(), wacc * weff); + } + if (validITSABTrack) { + histos.fill(HIST("hBVsPtVsPhiITSABTrack"), imp, deltaPhi, mcParticle.pt(), wacc * weff); + } + } + + if (cfgFlowCumulantEnabled) { + for (uint l_ind = 0; l_ind < corrconfigsTruth.size(); l_ind++) { + fillFC(fGFWTrue, true, corrconfigsTruth.at(l_ind), centrality, lRandom); + } + for (uint l_ind = 0; l_ind < corrconfigsReco.size(); l_ind++) { + fillFC(fGFWReco, false, corrconfigsReco.at(l_ind), centrality, lRandom); + } + } + } + histos.fill(HIST("hNchVsImpactParameter"), imp, nCh); + } + PROCESS_SWITCH(FlowMcUpc, processMCTrue, "process pure simulation information", true); + + void processReco(soa::Join::iterator const& collision, aod::BCsWithTimestamps const&, FilteredTracks const& tracks, aod::McParticles const&, aod::McCollisions const&) + { + histos.fill(HIST("RecoProcessEventCounter"), 0.5); + if (!eventSelected(collision)) { + return; + } + histos.fill(HIST("RecoProcessEventCounter"), 1.5); + if (tracks.size() < 1) { + return; + } + histos.fill(HIST("RecoProcessEventCounter"), 2.5); + for (const auto& track : tracks) { + if (!trackSelected(track)) { + continue; + } + histos.fill(HIST("hPtReco"), track.pt()); + auto mcParticle = track.mcParticle(); + int pdgCode = std::abs(mcParticle.pdgCode()); + bool extraPDGType = true; + if (cfgK0Lambda0Enabled) { + extraPDGType = (pdgCode != PDG_t::kK0Short && pdgCode != PDG_t::kLambda0); + } + if (extraPDGType && pdgCode != PDG_t::kElectron && pdgCode != PDG_t::kMuonMinus && pdgCode != PDG_t::kPiPlus && pdgCode != kKPlus && pdgCode != PDG_t::kProton) { + continue; + } + if (!mcParticle.isPhysicalPrimary()) { + continue; + } + if (std::fabs(mcParticle.eta()) > cfgCutEta) { // main acceptance + continue; } + histos.fill(HIST("hPtReco_PhysicalPrimary"), track.pt()); } } PROCESS_SWITCH(FlowMcUpc, processReco, "process reconstructed information", true); diff --git a/PWGUD/Tasks/FwdMuonsUPC.cxx b/PWGUD/Tasks/fwdMuonsUpc.cxx similarity index 52% rename from PWGUD/Tasks/FwdMuonsUPC.cxx rename to PWGUD/Tasks/fwdMuonsUpc.cxx index b3351965727..42f0cf8b290 100644 --- a/PWGUD/Tasks/FwdMuonsUPC.cxx +++ b/PWGUD/Tasks/fwdMuonsUpc.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file FwdMuonsUPC.cxx +/// \file fwdMuonsUpc.cxx /// \brief perform some selections on fwd events and saves the results /// executable name o2-analysis-ud-fwd-muon-upc @@ -18,7 +18,9 @@ #include "PWGUD/DataModel/UDTables.h" -#include +#include "Common/Core/RecoDecay.h" + +#include #include #include #include @@ -30,9 +32,13 @@ #include #include -#include +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include #include +#include #include #include #include @@ -77,6 +83,10 @@ DECLARE_SOA_COLUMN(Ezna, ezna, float); DECLARE_SOA_COLUMN(Tznc, tznc, float); DECLARE_SOA_COLUMN(Eznc, eznc, float); DECLARE_SOA_COLUMN(Nclass, nclass, int); +// other info +DECLARE_SOA_COLUMN(PDCAp, pDCAp, float); +DECLARE_SOA_COLUMN(PDCAn, pDCAn, float); +DECLARE_SOA_COLUMN(AmpV0A, ampV0A, std::vector); } // namespace dimu namespace o2::aod @@ -87,7 +97,8 @@ DECLARE_SOA_TABLE(DiMu, "AOD", "DIMU", dimu::PhiAv, dimu::PhiCh, dimu::EnergyP, dimu::Pxp, dimu::Pyp, dimu::Pzp, dimu::Ptp, dimu::Etap, dimu::Phip, dimu::TrackTypep, dimu::EnergyN, dimu::Pxn, dimu::Pyn, dimu::Pzn, dimu::Ptn, dimu::Etan, dimu::Phin, dimu::TrackTypen, - dimu::Tzna, dimu::Ezna, dimu::Tznc, dimu::Eznc, dimu::Nclass); + dimu::Tzna, dimu::Ezna, dimu::Tznc, dimu::Eznc, dimu::Nclass, + dimu::PDCAp, dimu::PDCAn, dimu::AmpV0A); } // namespace o2::aod // for saving tree with info on gen MC @@ -149,6 +160,10 @@ DECLARE_SOA_COLUMN(GenPhip, genPhip, float); DECLARE_SOA_COLUMN(GenPtn, genPtn, float); DECLARE_SOA_COLUMN(GenEtan, genEtan, float); DECLARE_SOA_COLUMN(GenPhin, genPhin, float); +// other info +DECLARE_SOA_COLUMN(PDCAp, pDCAp, float); +DECLARE_SOA_COLUMN(PDCAn, pDCAn, float); +DECLARE_SOA_COLUMN(AmpV0A, ampV0A, std::vector); } // namespace recodimu namespace o2::aod @@ -161,7 +176,8 @@ DECLARE_SOA_TABLE(RecoDimu, "AOD", "RECODIMU", recodimu::Ptn, recodimu::Etan, recodimu::Phin, recodimu::TrackTypen, recodimu::GenPt, recodimu::GenRap, recodimu::GenPhi, recodimu::GenPtp, recodimu::GenEtap, recodimu::GenPhip, - recodimu::GenPtn, recodimu::GenEtan, recodimu::GenPhin); + recodimu::GenPtn, recodimu::GenEtan, recodimu::GenPhin, + recodimu::PDCAp, recodimu::PDCAn, recodimu::AmpV0A); } // namespace o2::aod using namespace o2; @@ -172,23 +188,20 @@ using namespace o2::framework::expressions; const float kRAbsMin = 17.6; const float kRAbsMid = 26.5; const float kRAbsMax = 89.5; -const float kPDca1 = 200.; -const float kPDca2 = 200.; float kEtaMin = -4.0; float kEtaMax = -2.5; const float kPtMin = 0.; -const float kMaxAmpV0A = 100.; const int kReqMatchMIDTracks = 2; const int kReqMatchMFTTracks = 2; const int kMaxChi2MFTMatch = 30; const float kMaxZDCTime = 2.; -const float kMaxZDCTimeHisto = 10.; -const int kMuonPDG = 13; +const int k2Tracks = 2; +const int k4Tracks = 4; -struct FwdMuonsUPC { +struct FwdMuonsUpc { - // a pdg object + // a PDG object Service pdg; using CandidatesFwd = soa::Join; @@ -201,50 +214,32 @@ struct FwdMuonsUPC { // defining histograms using histogram registry: different histos for the different process functions HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry reg0n0n{"reg0n0n", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry regXn0n{"regXn0n", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry regXnXn{"regXnXn", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry mcGenRegistry{"mcGenRegistry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; - HistogramRegistry mcRecoRegistry{"mcRecoRegistry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; // CONFIGURABLES - static constexpr double Pi = o2::constants::math::PI; // pT of muon pairs - Configurable nBinsPt{"nBinsPt", 250, "N bins in pT histo"}; - Configurable lowPt{"lowPt", 0., "lower limit in pT histo"}; - Configurable highPt{"highPt", 2, "upper limit in pT histo"}; + ConfigurableAxis axisPt{"axisPt", {250, 0.0f, 2.0f}, "#it{p}_{T}^{#mu#mu} (GeV/#it{c})"}; + // pT fit + ConfigurableAxis axisPtFit{"axisPtFit", {VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.175, 0.20, 0.25, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, 1.20, 1.40, 1.60, 1.80, 2.00, 2.50, 3.00, 3.50}, "#it{p}_{T} (GeV/c)"}; // mass of muon pairs - Configurable nBinsMass{"nBinsMass", 500, "N bins in mass histo"}; - Configurable lowMass{"lowMass", 0., "lower limit in mass histo"}; - Configurable highMass{"highMass", 10., "upper limit in mass histo"}; - // eta of muon pairs - Configurable nBinsEta{"nBinsEta", 600, "N bins in eta histo"}; - Configurable lowEta{"lowEta", -10., "lower limit in eta histo"}; - Configurable highEta{"highEta", -2., "upper limit in eta histo"}; + ConfigurableAxis axisMass{"axisMass", {500, 0.0f, 10.0f}, "m_{#mu#mu} (GeV/#it{c}^{2})"}; // rapidity of muon pairs - Configurable nBinsRapidity{"nBinsRapidity", 250, "N bins in rapidity histo"}; - Configurable lowRapidity{"lowRapidity", -4.5, "lower limit in rapidity histo"}; - Configurable highRapidity{"highRapidity", -2., "upper limit in rapidity histo"}; - // phi of muon pairs - Configurable nBinsPhi{"nBinsPhi", 600, "N bins in phi histo"}; - Configurable lowPhi{"lowPhi", -Pi, "lower limit in phi histo"}; - Configurable highPhi{"highPhi", Pi, "upper limit in phi histo"}; - // pT of single muons - Configurable nBinsPtSingle{"nBinsPtSingle", 500, "N bins in pT histo single muon"}; - Configurable lowPtSingle{"lowPtSingle", 0., "lower limit in pT histo single muon"}; - Configurable highPtSingle{"highPtSingle", 2., "upper limit in pT histo single muon"}; - // eta of single muons - Configurable nBinsEtaSingle{"nBinsEtaSingle", 250, "N bins in eta histo single muon"}; - Configurable lowEtaSingle{"lowEtaSingle", -4.5, "lower limit in eta histo single muon"}; - Configurable highEtaSingle{"highEtaSingle", -2., "upper limit in eta histo single muon"}; - // phi of single muons - Configurable nBinsPhiSingle{"nBinsPhiSingle", 600, "N bins in phi histo single muon"}; - Configurable lowPhiSingle{"lowPhiSingle", -Pi, "lower limit in phi histo single muon"}; - Configurable highPhiSingle{"highPhiSingle", Pi, "upper limit in phi histo single muon"}; - // ZDC - Configurable nBinsZDCen{"nBinsZDCen", 200, "N bins in ZN energy"}; - Configurable lowEnZN{"lowEnZN", -50., "lower limit in ZN energy histo"}; - Configurable highEnZN{"highEnZN", 250., "upper limit in ZN energy histo"}; + ConfigurableAxis axisRapidity{"axisRapidity", {250, -4.5f, -2.0f}, "y_{#mu#mu}"}; + + // cuts on pair kinematics + Configurable lowPt{"lowPt", 0., "Low pT cut"}; + Configurable highPt{"highPt", 2, "High pT cut"}; + Configurable lowMass{"lowMass", 0., "Low mass cut"}; + Configurable highMass{"highMass", 10., "High mass cut"}; + Configurable lowRapidity{"lowRapidity", -4.5, "Low rapidity cut"}; + Configurable highRapidity{"highRapidity", -2., "High rapidity cut"}; + + // cuts on pDCA + Configurable pDCA1{"pDCA1", 200., "pDCA cut for tracks with rAbs < 26.5 cm"}; + Configurable pDCA2{"pDCA2", 200., "pDCA cut for tracks with rAbs > 26.5 cm"}; + + // cut on V0A amplitude + Configurable maxAmpV0A{"maxAmpV0A", 100., "Max amplitude in V0A"}; + // my track type // 0 = MCH-MID-MFT // 1 = MCH-MID @@ -252,138 +247,26 @@ struct FwdMuonsUPC { void init(InitContext&) { - // binning of pT axis fr fit - std::vector ptFitBinning = { - 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, - 0.11, 0.12, 0.13, 0.14, 0.15, 0.175, 0.20, 0.25, 0.30, 0.40, 0.50, - 0.60, 0.70, 0.80, 0.90, 1.00, 1.20, 1.40, 1.60, 1.80, 2.00, 2.50, - 3.00, 3.50}; - - std::vector ptFitBinningHalfWidth = { - 0.00, 0.005, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05, - 0.055, 0.06, 0.065, 0.07, 0.075, 0.08, 0.085, 0.09, 0.095, 0.10, - 0.105, 0.11, 0.115, 0.12, 0.125, 0.13, 0.135, 0.14, 0.145, 0.15, - 0.1625, 0.175, 0.1875, 0.20, 0.225, 0.25, 0.275, 0.30, 0.35, 0.40, - 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, - 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.25, - 2.50, 2.75, 3.00, 3.25, 3.50}; - // axis - const AxisSpec axisPt{nBinsPt, lowPt, highPt, "#it{p}_{T} GeV/#it{c}"}; - const AxisSpec axisPtFit = {ptFitBinning, "#it{p}_{T} (GeV/c)"}; - const AxisSpec axisPtFit2 = {ptFitBinningHalfWidth, "#it{p}_{T} (GeV/c)"}; - const AxisSpec axisMass{nBinsMass, lowMass, highMass, "m_{#mu#mu} GeV/#it{c}^{2}"}; - const AxisSpec axisEta{nBinsEta, lowEta, highEta, "#eta"}; - const AxisSpec axisRapidity{nBinsRapidity, lowRapidity, highRapidity, "Rapidity"}; - const AxisSpec axisPhi{nBinsPhi, lowPhi, highPhi, "#varphi"}; - const AxisSpec axisPtSingle{nBinsPtSingle, lowPtSingle, highPtSingle, "#it{p}_{T}_{ trk} GeV/#it{c}"}; - const AxisSpec axisTimeZN{200, -10, 10, "ZDC time (ns)"}; - const AxisSpec axisEnergyZNA{nBinsZDCen, lowEnZN, highEnZN, "ZNA energy (TeV)"}; - const AxisSpec axisEnergyZNC{nBinsZDCen, lowEnZN, highEnZN, "ZNC energy (TeV)"}; - const AxisSpec axisEtaSingle{nBinsEtaSingle, lowEtaSingle, highEtaSingle, "#eta_{trk}"}; - const AxisSpec axisPhiSingle{nBinsPhiSingle, lowPhiSingle, highPhiSingle, "#varphi_{trk}"}; + const AxisSpec ptAxis{axisPt, "#it{p}_{T}^{#mu#mu} (GeV/#it{c})", "ptAxis"}; + const AxisSpec ptFitAxis{axisPtFit, "#it{p}_{T} (GeV/c)", "ptFitAxis"}; + const AxisSpec massAxis{axisMass, "m_{#mu#mu} (GeV/#it{c}^{2})", "massAxis"}; + const AxisSpec rapidityAxis{axisRapidity, "y_{#mu#mu}", "rapidityAxis"}; // histos - // data and reco MC - registry.add("hMass", "Invariant mass of muon pairs;;#counts", kTH1D, {axisMass}); - registry.add("hPt", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPt}); - registry.add("hPtFit", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPtFit}); - registry.add("hPtFit2", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPtFit2}); - registry.add("hEta", "Pseudorapidty of muon pairs;;#counts", kTH1D, {axisEta}); - registry.add("hRapidity", "Rapidty of muon pairs;;#counts", kTH1D, {axisRapidity}); - registry.add("hPhi", "#varphi of muon pairs;;#counts", kTH1D, {axisPhi}); - registry.add("hCharge", "Charge;;;#counts", kTH1D, {{5, -2.5, 2.5}}); - registry.add("hContrib", "hContrib;;#counts", kTH1D, {{6, -0.5, 5.5}}); - registry.add("hEvSign", "Sum of the charges of all the tracks in each event;;#counts", kTH1D, {{5, -2.5, 2.5}}); - registry.add("hPtTrkPos", "Pt of positive muons;;#counts", kTH1D, {axisPtSingle}); - registry.add("hPtTrkNeg", "Pt of negative muons;;#counts", kTH1D, {axisPtSingle}); - registry.add("hEtaTrkPos", "#eta of positive muons;;#counts", kTH1D, {axisEtaSingle}); - registry.add("hEtaTrkNeg", "#eta of negative muons;;#counts", kTH1D, {axisEtaSingle}); - registry.add("hPhiTrkPos", "#varphi of positive muons;;#counts", kTH1D, {axisPhiSingle}); - registry.add("hPhiTrkNeg", "#varphi of negative muons;;#counts", kTH1D, {axisPhiSingle}); - registry.add("hSameSign", "hSameSign;;#counts", kTH1D, {{6, -0.5, 5.5}}); - registry.add("hPhiCharge", "#phi #it{charge}", kTH1D, {axisPhi}); - registry.add("hPhiAverage", "#phi #it{average}", kTH1D, {axisPhi}); - - // data - registry.add("hTimeZNA", "ZNA Times;;#counts", kTH1D, {axisTimeZN}); - registry.add("hTimeZNC", "ZNC Times;;#counts", kTH1D, {axisTimeZN}); - registry.add("hEnergyZN", "ZNA vs ZNC energy", kTH2D, {axisEnergyZNA, axisEnergyZNC}); - - reg0n0n.add("hMass", "Invariant mass of muon pairs - 0n0n;;#counts", kTH1D, {axisMass}); - reg0n0n.add("hPt", "Transverse momentum of muon pairs - 0n0n;;#counts", kTH1D, {axisPt}); - reg0n0n.add("hEta", "Pseudorapidty of muon pairs - 0n0n;;#counts", kTH1D, {axisEta}); - reg0n0n.add("hRapidity", "Rapidty of muon pairs - 0n0n;;#counts", kTH1D, {axisRapidity}); - reg0n0n.add("hPtFit", "Transverse momentum of muon pairs - 0n0n;;#counts", kTH1D, {axisPtFit}); - - regXn0n.add("hMass", "Invariant mass of muon pairs - Xn0n;;#counts", kTH1D, {axisMass}); - regXn0n.add("hPt", "Transverse momentum of muon pairs - Xn0n;;#counts", kTH1D, {axisPt}); - regXn0n.add("hEta", "Pseudorapidty of muon pairs - Xn0n;;#counts", kTH1D, {axisEta}); - regXn0n.add("hRapidity", "Rapidty of muon pairs - Xn0n;;#counts", kTH1D, {axisRapidity}); - regXn0n.add("hPtFit", "Transverse momentum of muon pairs - Xn0n;;#counts", kTH1D, {axisPtFit}); - - regXnXn.add("hMass", "Invariant mass of muon pairs - XnXn;;#counts", kTH1D, {axisMass}); - regXnXn.add("hPt", "Transverse momentum of muon pairs - XnXn;;#counts", kTH1D, {axisPt}); - regXnXn.add("hEta", "Pseudorapidty of muon pairs - XnXn;;#counts", kTH1D, {axisEta}); - regXnXn.add("hRapidity", "Rapidty of muon pairs - XnXn;;#counts", kTH1D, {axisRapidity}); - regXnXn.add("hPtFit", "Transverse momentum of muon pairs - XnXn;;#counts", kTH1D, {axisPtFit}); - - // gen MC - mcGenRegistry.add("hMass", "Invariant mass of muon pairs;;#counts", kTH1D, {axisMass}); - mcGenRegistry.add("hPt", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPt}); - mcGenRegistry.add("hEta", "Pseudorapidty of muon pairs;;#counts", kTH1D, {axisEta}); - mcGenRegistry.add("hRapidity", "Rapidty of muon pairs;;#counts", kTH1D, {axisRapidity}); - mcGenRegistry.add("hPhi", "#varphi of muon pairs;;#counts", kTH1D, {axisPhi}); - mcGenRegistry.add("hPtTrkPos", "Pt of positive muons;;#counts", kTH1D, {axisPtSingle}); - mcGenRegistry.add("hPtTrkNeg", "Pt of negative muons;;#counts", kTH1D, {axisPtSingle}); - mcGenRegistry.add("hEtaTrkPos", "#eta of positive muons;;#counts", kTH1D, {axisEtaSingle}); - mcGenRegistry.add("hEtaTrkNeg", "#eta of negative muons;;#counts", kTH1D, {axisEtaSingle}); - mcGenRegistry.add("hPhiTrkPos", "#varphi of positive muons;;#counts", kTH1D, {axisPhiSingle}); - mcGenRegistry.add("hPhiTrkNeg", "#varphi of negative muons;;#counts", kTH1D, {axisPhiSingle}); - mcGenRegistry.add("hPhiCharge", "#phi #it{charge}", kTH1D, {axisPhi}); - mcGenRegistry.add("hPhiAverage", "#phi #it{average}", kTH1D, {axisPhi}); - - // reco MC - mcRecoRegistry.add("hMass", "Invariant mass of muon pairs;;#counts", kTH1D, {axisMass}); - mcRecoRegistry.add("hPt", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPt}); - mcRecoRegistry.add("hPtFit", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPtFit}); - mcRecoRegistry.add("hPtFit2", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPtFit2}); - mcRecoRegistry.add("hEta", "Pseudorapidty of muon pairs;;#counts", kTH1D, {axisEta}); - mcRecoRegistry.add("hRapidity", "Rapidty of muon pairs;;#counts", kTH1D, {axisRapidity}); - mcRecoRegistry.add("hPhi", "#varphi of muon pairs;;#counts", kTH1D, {axisPhi}); - mcRecoRegistry.add("hCharge", "Charge;;;#counts", kTH1D, {{5, -2.5, 2.5}}); - mcRecoRegistry.add("hContrib", "hContrib;;#counts", kTH1D, {{6, -0.5, 5.5}}); - mcRecoRegistry.add("hEvSign", "Sum of the charges of all the tracks in each event;;#counts", kTH1D, {{5, -2.5, 2.5}}); - mcRecoRegistry.add("hPtTrkPos", "Pt of positive muons;;#counts", kTH1D, {axisPtSingle}); - mcRecoRegistry.add("hPtTrkNeg", "Pt of negative muons;;#counts", kTH1D, {axisPtSingle}); - mcRecoRegistry.add("hEtaTrkPos", "#eta of positive muons;;#counts", kTH1D, {axisEtaSingle}); - mcRecoRegistry.add("hEtaTrkNeg", "#eta of negative muons;;#counts", kTH1D, {axisEtaSingle}); - mcRecoRegistry.add("hPhiTrkPos", "#varphi of positive muons;;#counts", kTH1D, {axisPhiSingle}); - mcRecoRegistry.add("hPhiTrkNeg", "#varphi of negative muons;;#counts", kTH1D, {axisPhiSingle}); - mcRecoRegistry.add("hSameSign", "hSameSign;;#counts", kTH1D, {{6, -0.5, 5.5}}); - mcRecoRegistry.add("hPhiCharge", "#phi #it{charge}", kTH1D, {axisPhi}); - mcRecoRegistry.add("hPhiAverage", "#phi #it{average}", kTH1D, {axisPhi}); - - // corr gen-reco - mcRecoRegistry.add("hPtcorr", "gen pT vs reco pT", kTH2D, {axisPt, axisPt}); - mcRecoRegistry.add("hRapcorr", "gen rapidity vs reco rapidity", kTH2D, {axisRapidity, axisRapidity}); - mcRecoRegistry.add("hPhicorr", "gen #phi vs reco #phi", kTH2D, {axisPhi, axisPhi}); + registry.add("hMass", "Invariant mass of muon pairs;;#counts", kTH1D, {massAxis}); + registry.add("hPt", "Transverse momentum of muon pairs;;#counts", kTH1D, {ptAxis}); + registry.add("hPtFit", "Transverse momentum of muon pairs;;#counts", kTH1D, {ptFitAxis}); + registry.add("hRapidity", "Rapidty of muon pairs;;#counts", kTH1D, {rapidityAxis}); } // FUNCTIONS - // retrieve particle mass (GeV/c^2) from TDatabasePDG - float particleMass(int pid) - { - auto mass = pdg->Mass(pid); - return mass; - } - // template function that fills a map with the collision id of each udcollision as key // and a vector with the tracks // map == (key, element) == (udCollisionId, vector of trks) template - void collectCandIDs(std::unordered_map>& tracksPerCand, TTracks& tracks) + void collectCandIDs(std::unordered_map>& tracksPerCand, TTracks const& tracks) { for (const auto& tr : tracks) { int32_t candId = tr.udCollisionId(); @@ -398,13 +281,16 @@ struct FwdMuonsUPC { // and a vector with the tracks // map == (key, element) == (udMcCollisionId, vector of mc particles) template - void collectMcCandIDs(std::unordered_map>& tracksPerCand, TTracks& tracks) + void collectMcCandIDs(std::unordered_map>& tracksPerCand, TTracks const& tracks) { for (const auto& tr : tracks) { int32_t candId = tr.udMcCollisionId(); if (candId < 0) { continue; } + if (std::abs(tr.pdgCode()) != PDG_t::kMuonMinus) { + continue; + } tracksPerCand[candId].push_back(tr.globalIndex()); } } @@ -434,11 +320,11 @@ struct FwdMuonsUPC { // struct used to store the ZDC info in a map struct ZDCinfo { - float timeA; - float timeC; - float enA; - float enC; - int32_t id; + float timeA = 0.f; + float timeC = 0.f; + float enA = 0.f; + float enC = 0.f; + int32_t id = -1; }; // function that fills a map with the collision id of each udcollision as key @@ -475,12 +361,11 @@ struct FwdMuonsUPC { { float rAbs = fwdTrack.rAtAbsorberEnd(); float pDca = fwdTrack.pDca(); - TLorentzVector p; - auto mMu = particleMass(kMuonPDG); - p.SetXYZM(fwdTrack.px(), fwdTrack.py(), fwdTrack.pz(), mMu); - float eta = p.Eta(); - float pt = p.Pt(); - float pDcaMax = rAbs < kRAbsMid ? kPDca1 : kPDca2; + + std::array trackMomentum{fwdTrack.px(), fwdTrack.py(), fwdTrack.pz()}; + float eta = RecoDecay::eta(trackMomentum); + float pt = RecoDecay::pt(trackMomentum); + float pDcaMax = rAbs < kRAbsMid ? pDCA1 : pDCA2; if (eta < kEtaMin || eta > kEtaMax) return false; @@ -494,9 +379,9 @@ struct FwdMuonsUPC { } // function to compute phi for azimuth anisotropy - void computePhiAnis(TLorentzVector p1, TLorentzVector p2, int sign1, float& phiAverage, float& phiCharge) + void computePhiAnis(const ROOT::Math::PxPyPzMVector& p1, const ROOT::Math::PxPyPzMVector& p2, int sign1, float& phiAverage, float& phiCharge) { - TLorentzVector tSum, tDiffAv, tDiffCh; + ROOT::Math::PxPyPzMVector tSum, tDiffAv, tDiffCh; tSum = p1 + p2; float halfUnity = 0.5; if (sign1 > 0) { @@ -514,9 +399,9 @@ struct FwdMuonsUPC { } // average - phiAverage = tSum.DeltaPhi(tDiffAv); + phiAverage = ROOT::Math::VectorUtil::DeltaPhi(tSum, tDiffAv); // charge - phiCharge = tSum.DeltaPhi(tDiffCh); + phiCharge = ROOT::Math::VectorUtil::DeltaPhi(tSum, tDiffCh); } // function that processes the candidates: @@ -531,15 +416,19 @@ struct FwdMuonsUPC { const auto& ampsV0A = cand.amplitudesV0A(); const auto& ampsRelBCsV0A = cand.ampRelBCsV0A(); for (unsigned int i = 0; i < ampsV0A.size(); ++i) { - if (std::abs(ampsRelBCsV0A[i]) <= 1) { - if (ampsV0A[i] > kMaxAmpV0A) + if (std::abs(ampsRelBCsV0A[i]) <= 0) { + if (ampsV0A[i] > maxAmpV0A) return; } } + // select events with exactly 2 forward tracks + if (cand.numContrib() != k2Tracks) { + return; + } + // select opposite charge events only if (cand.netCharge() != 0) { - registry.fill(HIST("hSameSign"), cand.numContrib()); return; } @@ -574,11 +463,9 @@ struct FwdMuonsUPC { return; // form Lorentz vectors - TLorentzVector p1, p2; - auto mMu = particleMass(kMuonPDG); - p1.SetXYZM(tr1.px(), tr1.py(), tr1.pz(), mMu); - p2.SetXYZM(tr2.px(), tr2.py(), tr2.pz(), mMu); - TLorentzVector p = p1 + p2; + ROOT::Math::PxPyPzMVector p1{tr1.px(), tr1.py(), tr1.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector p2{tr2.px(), tr2.py(), tr2.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector p = p1 + p2; // cut on pair kinematics // select mass @@ -602,13 +489,6 @@ struct FwdMuonsUPC { float phiCharge = 0; computePhiAnis(p1, p2, tr1.sign(), phiAverage, phiCharge); - // zdc info - if (std::abs(zdc.timeA) < kMaxZDCTimeHisto) - registry.fill(HIST("hTimeZNA"), zdc.timeA); - if (std::abs(zdc.timeC) < kMaxZDCTimeHisto) - registry.fill(HIST("hTimeZNC"), zdc.timeC); - registry.fill(HIST("hEnergyZN"), zdc.enA, zdc.enC); - // divide the events in neutron classes bool neutronA = false; bool neutronC = false; @@ -619,75 +499,42 @@ struct FwdMuonsUPC { if (std::abs(zdc.timeC) < kMaxZDCTime) neutronC = true; - if (std::isinf(zdc.timeC)) - neutronC = false; - if (std::isinf(zdc.timeA)) - neutronA = false; - - // fill the histos in neutron classes and assign neutron class label + // assign neutron class label // 0n0n if (neutronC == false && neutronA == false) { znClass = 1; - reg0n0n.fill(HIST("hMass"), p.M()); - reg0n0n.fill(HIST("hPt"), p.Pt()); - reg0n0n.fill(HIST("hPtFit"), p.Pt()); - reg0n0n.fill(HIST("hEta"), p.Eta()); - reg0n0n.fill(HIST("hRapidity"), p.Rapidity()); } else if (neutronA ^ neutronC) { // Xn0n + 0nXn if (neutronA) znClass = 2; else if (neutronC) znClass = 3; - regXn0n.fill(HIST("hMass"), p.M()); - regXn0n.fill(HIST("hPt"), p.Pt()); - regXn0n.fill(HIST("hPtFit"), p.Pt()); - regXn0n.fill(HIST("hEta"), p.Eta()); - regXn0n.fill(HIST("hRapidity"), p.Rapidity()); } else if (neutronA && neutronC) { // XnXn znClass = 4; - regXnXn.fill(HIST("hMass"), p.M()); - regXnXn.fill(HIST("hPt"), p.Pt()); - regXnXn.fill(HIST("hPtFit"), p.Pt()); - regXnXn.fill(HIST("hEta"), p.Eta()); - regXnXn.fill(HIST("hRapidity"), p.Rapidity()); } - // fill the histos without looking at neutron emission - registry.fill(HIST("hContrib"), cand.numContrib()); - registry.fill(HIST("hPtTrkPos"), p1.Pt()); - registry.fill(HIST("hPtTrkNeg"), p2.Pt()); - registry.fill(HIST("hEtaTrkPos"), p1.Eta()); - registry.fill(HIST("hEtaTrkNeg"), p2.Eta()); - registry.fill(HIST("hPhiTrkPos"), p1.Phi()); - registry.fill(HIST("hPhiTrkNeg"), p2.Phi()); - registry.fill(HIST("hEvSign"), cand.netCharge()); + // fill the histos registry.fill(HIST("hMass"), p.M()); registry.fill(HIST("hPt"), p.Pt()); registry.fill(HIST("hPtFit"), p.Pt()); - registry.fill(HIST("hPtFit2"), p.Pt()); - registry.fill(HIST("hEta"), p.Eta()); registry.fill(HIST("hRapidity"), p.Rapidity()); - registry.fill(HIST("hPhi"), p.Phi()); - registry.fill(HIST("hCharge"), tr1.sign()); - registry.fill(HIST("hCharge"), tr2.sign()); - registry.fill(HIST("hPhiAverage"), phiAverage); - registry.fill(HIST("hPhiCharge"), phiCharge); // store the event to save it into a tree if (tr1.sign() > 0) { dimuSel(cand.runNumber(), p.M(), p.E(), p.Px(), p.Py(), p.Pz(), p.Pt(), p.Rapidity(), p.Phi(), phiAverage, phiCharge, - p1.E(), p1.Px(), p1.Py(), p1.Pz(), p1.Pt(), p1.PseudoRapidity(), p1.Phi(), static_cast(myTrackType), - p2.E(), p2.Px(), p2.Py(), p2.Pz(), p2.Pt(), p2.PseudoRapidity(), p2.Phi(), static_cast(myTrackType), - zdc.timeA, zdc.enA, zdc.timeC, zdc.enC, znClass); + p1.E(), p1.Px(), p1.Py(), p1.Pz(), p1.Pt(), p1.Eta(), p1.Phi(), static_cast(myTrackType), + p2.E(), p2.Px(), p2.Py(), p2.Pz(), p2.Pt(), p2.Eta(), p2.Phi(), static_cast(myTrackType), + zdc.timeA, zdc.enA, zdc.timeC, zdc.enC, znClass, + tr1.pDca(), tr2.pDca(), ampsV0A); } else { dimuSel(cand.runNumber(), p.M(), p.E(), p.Px(), p.Py(), p.Pz(), p.Pt(), p.Rapidity(), p.Phi(), phiAverage, phiCharge, - p2.E(), p2.Px(), p2.Py(), p2.Pz(), p2.Pt(), p2.PseudoRapidity(), p2.Phi(), static_cast(myTrackType), - p1.E(), p1.Px(), p1.Py(), p1.Pz(), p1.Pt(), p1.PseudoRapidity(), p1.Phi(), static_cast(myTrackType), - zdc.timeA, zdc.enA, zdc.timeC, zdc.enC, znClass); + p2.E(), p2.Px(), p2.Py(), p2.Pz(), p2.Pt(), p2.Eta(), p2.Phi(), static_cast(myTrackType), + p1.E(), p1.Px(), p1.Py(), p1.Pz(), p1.Pt(), p1.Eta(), p1.Phi(), static_cast(myTrackType), + zdc.timeA, zdc.enA, zdc.timeC, zdc.enC, znClass, + tr2.pDca(), tr1.pDca(), ampsV0A); } } @@ -698,15 +545,18 @@ struct FwdMuonsUPC { { // check that all pairs are mu+mu- - if (std::abs(McPart1.pdgCode()) != kMuonPDG && std::abs(McPart2.pdgCode()) != kMuonPDG) + if (std::abs(McPart1.pdgCode()) != PDG_t::kMuonMinus || std::abs(McPart2.pdgCode()) != PDG_t::kMuonMinus) { LOGF(debug, "PDG codes: %d | %d", McPart1.pdgCode(), McPart2.pdgCode()); + return; + } + if (McPart1.pdgCode() + McPart2.pdgCode() != 0) { + return; + } // create Lorentz vectors - TLorentzVector p1, p2; - auto mMu = particleMass(kMuonPDG); - p1.SetXYZM(McPart1.px(), McPart1.py(), McPart1.pz(), mMu); - p2.SetXYZM(McPart2.px(), McPart2.py(), McPart2.pz(), mMu); - TLorentzVector p = p1 + p2; + ROOT::Math::PxPyPzMVector p1{McPart1.px(), McPart1.py(), McPart1.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector p2{McPart2.px(), McPart2.py(), McPart2.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector p = p1 + p2; // cut on pair kinematics // select mass @@ -731,31 +581,22 @@ struct FwdMuonsUPC { computePhiAnis(p1, p2, -McPart1.pdgCode(), phiAverage, phiCharge); // fill the histos - mcGenRegistry.fill(HIST("hPtTrkPos"), p1.Pt()); - mcGenRegistry.fill(HIST("hPtTrkNeg"), p2.Pt()); - mcGenRegistry.fill(HIST("hEtaTrkPos"), p1.Eta()); - mcGenRegistry.fill(HIST("hEtaTrkNeg"), p2.Eta()); - mcGenRegistry.fill(HIST("hPhiTrkPos"), p1.Phi()); - mcGenRegistry.fill(HIST("hPhiTrkNeg"), p2.Phi()); - mcGenRegistry.fill(HIST("hMass"), p.M()); - mcGenRegistry.fill(HIST("hPt"), p.Pt()); - mcGenRegistry.fill(HIST("hEta"), p.Eta()); - mcGenRegistry.fill(HIST("hRapidity"), p.Rapidity()); - mcGenRegistry.fill(HIST("hPhi"), p.Phi()); - mcGenRegistry.fill(HIST("hPhiAverage"), phiAverage); - mcGenRegistry.fill(HIST("hPhiCharge"), phiCharge); + registry.fill(HIST("hMass"), p.M()); + registry.fill(HIST("hPt"), p.Pt()); + registry.fill(HIST("hPtFit"), p.Pt()); + registry.fill(HIST("hRapidity"), p.Rapidity()); // store the event to save it into a tree if (McPart1.pdgCode() < 0) { dimuGen(p.M(), p.Pt(), p.Rapidity(), p.Phi(), phiAverage, phiCharge, - p1.Pt(), p1.PseudoRapidity(), p1.Phi(), - p2.Pt(), p2.PseudoRapidity(), p2.Phi()); + p1.Pt(), p1.Eta(), p1.Phi(), + p2.Pt(), p2.Eta(), p2.Phi()); } else { dimuGen(p.M(), p.Pt(), p.Rapidity(), p.Phi(), phiAverage, phiCharge, - p2.Pt(), p2.PseudoRapidity(), p2.Phi(), - p1.Pt(), p1.PseudoRapidity(), p1.Phi()); + p2.Pt(), p2.Eta(), p2.Phi(), + p1.Pt(), p1.Eta(), p1.Phi()); } } @@ -767,22 +608,28 @@ struct FwdMuonsUPC { { // check that all pairs are mu+mu- - if (std::abs(McPart1.pdgCode()) != kMuonPDG && std::abs(McPart2.pdgCode()) != kMuonPDG) + if (std::abs(McPart1.pdgCode()) != PDG_t::kMuonMinus || std::abs(McPart2.pdgCode()) != PDG_t::kMuonMinus) { LOGF(debug, "PDG codes: %d | %d", McPart1.pdgCode(), McPart2.pdgCode()); + return; + } // V0 selection const auto& ampsV0A = cand.amplitudesV0A(); const auto& ampsRelBCsV0A = cand.ampRelBCsV0A(); for (unsigned int i = 0; i < ampsV0A.size(); ++i) { - if (std::abs(ampsRelBCsV0A[i]) <= 1) { - if (ampsV0A[i] > kMaxAmpV0A) + if (std::abs(ampsRelBCsV0A[i]) <= 0) { + if (ampsV0A[i] > maxAmpV0A) return; } } + // select events with exactly 2 forward tracks + if (cand.numContrib() != k2Tracks) { + return; + } + // select opposite charge events only if (cand.netCharge() != 0) { - registry.fill(HIST("hSameSign"), cand.numContrib()); return; } @@ -817,11 +664,9 @@ struct FwdMuonsUPC { return; // form Lorentz vectors - TLorentzVector p1, p2; - auto mMu = particleMass(kMuonPDG); - p1.SetXYZM(tr1.px(), tr1.py(), tr1.pz(), mMu); - p2.SetXYZM(tr2.px(), tr2.py(), tr2.pz(), mMu); - TLorentzVector p = p1 + p2; + ROOT::Math::PxPyPzMVector p1{tr1.px(), tr1.py(), tr1.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector p2{tr2.px(), tr2.py(), tr2.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector p = p1 + p2; // cut on pair kinematics (reco candidates) // select mass @@ -845,16 +690,14 @@ struct FwdMuonsUPC { float phiCharge = 0; computePhiAnis(p1, p2, tr1.sign(), phiAverage, phiCharge); - // gen particle - TLorentzVector p1Mc, p2Mc; - p1Mc.SetXYZM(McPart1.px(), McPart1.py(), McPart1.pz(), mMu); - p2Mc.SetXYZM(McPart2.px(), McPart2.py(), McPart2.pz(), mMu); - TLorentzVector pMc = p1Mc + p2Mc; + ROOT::Math::PxPyPzMVector p1Mc{McPart1.px(), McPart1.py(), McPart1.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector p2Mc{McPart2.px(), McPart2.py(), McPart2.pz(), o2::constants::physics::MassMuon}; + ROOT::Math::PxPyPzMVector pMc = p1Mc + p2Mc; // compute gen phi for azimuth anisotropy float phiGenAverage = 0; float phiGenCharge = 0; - computePhiAnis(p1, p2, -McPart1.pdgCode(), phiGenAverage, phiGenCharge); + computePhiAnis(p1Mc, p2Mc, -McPart1.pdgCode(), phiGenAverage, phiGenCharge); // print info in case of problems if (tr1.sign() * McPart1.pdgCode() > 0 || tr2.sign() * McPart2.pdgCode() > 0) { @@ -865,68 +708,34 @@ struct FwdMuonsUPC { } // fill the histos - // reco info - mcRecoRegistry.fill(HIST("hContrib"), cand.numContrib()); - mcRecoRegistry.fill(HIST("hPtTrkPos"), p1.Pt()); - mcRecoRegistry.fill(HIST("hPtTrkNeg"), p2.Pt()); - mcRecoRegistry.fill(HIST("hEtaTrkPos"), p1.Eta()); - mcRecoRegistry.fill(HIST("hEtaTrkNeg"), p2.Eta()); - mcRecoRegistry.fill(HIST("hPhiTrkPos"), p1.Phi()); - mcRecoRegistry.fill(HIST("hPhiTrkNeg"), p2.Phi()); - mcRecoRegistry.fill(HIST("hEvSign"), cand.netCharge()); - mcRecoRegistry.fill(HIST("hMass"), p.M()); - mcRecoRegistry.fill(HIST("hPt"), p.Pt()); - mcRecoRegistry.fill(HIST("hPtFit"), p.Pt()); - mcRecoRegistry.fill(HIST("hPtFit2"), p.Pt()); - mcRecoRegistry.fill(HIST("hEta"), p.Eta()); - mcRecoRegistry.fill(HIST("hRapidity"), p.Rapidity()); - mcRecoRegistry.fill(HIST("hPhi"), p.Phi()); - mcRecoRegistry.fill(HIST("hCharge"), tr1.sign()); - mcRecoRegistry.fill(HIST("hCharge"), tr2.sign()); - mcRecoRegistry.fill(HIST("hPhiAverage"), phiAverage); - mcRecoRegistry.fill(HIST("hPhiCharge"), phiCharge); - - // gen info (of reco events) - mcGenRegistry.fill(HIST("hPtTrkPos"), p1Mc.Pt()); - mcGenRegistry.fill(HIST("hPtTrkNeg"), p2Mc.Pt()); - mcGenRegistry.fill(HIST("hEtaTrkPos"), p1Mc.Eta()); - mcGenRegistry.fill(HIST("hEtaTrkNeg"), p2Mc.Eta()); - mcGenRegistry.fill(HIST("hPhiTrkPos"), p1Mc.Phi()); - mcGenRegistry.fill(HIST("hPhiTrkNeg"), p2Mc.Phi()); - mcGenRegistry.fill(HIST("hMass"), pMc.M()); - mcGenRegistry.fill(HIST("hPt"), pMc.Pt()); - mcGenRegistry.fill(HIST("hEta"), pMc.Eta()); - mcGenRegistry.fill(HIST("hRapidity"), pMc.Rapidity()); - mcGenRegistry.fill(HIST("hPhi"), pMc.Phi()); - mcGenRegistry.fill(HIST("hPhiAverage"), phiGenAverage); - mcGenRegistry.fill(HIST("hPhiCharge"), phiGenCharge); - - // reco-gen correlations - mcRecoRegistry.fill(HIST("hPtcorr"), p.Pt(), pMc.Pt()); - mcRecoRegistry.fill(HIST("hRapcorr"), p.Rapidity(), pMc.Rapidity()); - mcRecoRegistry.fill(HIST("hPhicorr"), p.Phi(), pMc.Phi()); + registry.fill(HIST("hMass"), p.M()); + registry.fill(HIST("hPt"), p.Pt()); + registry.fill(HIST("hPtFit"), p.Pt()); + registry.fill(HIST("hRapidity"), p.Rapidity()); // store the event to save it into a tree if (tr1.sign() > 0) { dimuReco(cand.runNumber(), p.M(), p.Pt(), p.Rapidity(), p.Phi(), phiAverage, phiCharge, - p1.Pt(), p1.PseudoRapidity(), p1.Phi(), static_cast(myTrackType), - p2.Pt(), p2.PseudoRapidity(), p2.Phi(), static_cast(myTrackType), + p1.Pt(), p1.Eta(), p1.Phi(), static_cast(myTrackType), + p2.Pt(), p2.Eta(), p2.Phi(), static_cast(myTrackType), // gen info pMc.Pt(), pMc.Rapidity(), pMc.Phi(), - p1Mc.Pt(), p1Mc.PseudoRapidity(), p1Mc.Phi(), - p2Mc.Pt(), p2Mc.PseudoRapidity(), p2Mc.Phi()); + p1Mc.Pt(), p1Mc.Eta(), p1Mc.Phi(), + p2Mc.Pt(), p2Mc.Eta(), p2Mc.Phi(), + tr1.pDca(), tr2.pDca(), ampsV0A); } else { dimuReco(cand.runNumber(), p.M(), p.Pt(), p.Rapidity(), p.Phi(), phiAverage, phiCharge, - p2.Pt(), p2.PseudoRapidity(), p2.Phi(), static_cast(myTrackType), - p1.Pt(), p1.PseudoRapidity(), p1.Phi(), static_cast(myTrackType), + p2.Pt(), p2.Eta(), p2.Phi(), static_cast(myTrackType), + p1.Pt(), p1.Eta(), p1.Phi(), static_cast(myTrackType), // gen info pMc.Pt(), pMc.Rapidity(), pMc.Phi(), - p2Mc.Pt(), p2Mc.PseudoRapidity(), p2Mc.Phi(), - p1Mc.Pt(), p1Mc.PseudoRapidity(), p1Mc.Phi()); + p2Mc.Pt(), p2Mc.Eta(), p2Mc.Phi(), + p1Mc.Pt(), p1Mc.Eta(), p1Mc.Phi(), + tr2.pDca(), tr1.pDca(), ampsV0A); } } @@ -946,6 +755,10 @@ struct FwdMuonsUPC { // loop over the candidates for (const auto& item : tracksPerCand) { + if (item.second.size() != k2Tracks) { + LOGF(debug, "number track = %d", item.second.size()); + continue; + } int32_t trId1 = item.second[0]; int32_t trId2 = item.second[1]; int32_t candID = item.first; @@ -968,18 +781,28 @@ struct FwdMuonsUPC { } } - PROCESS_SWITCH(FwdMuonsUPC, processData, "", true); + PROCESS_SWITCH(FwdMuonsUpc, processData, "", true); // process MC Truth void processMcGen(aod::UDMcCollisions const& mccollisions, aod::UDMcParticles const& McParts) { - // map with the tracks std::unordered_map> tracksPerCand; collectMcCandIDs(tracksPerCand, McParts); // loop over the candidates for (const auto& item : tracksPerCand) { + if (item.second.size() != k2Tracks) { + LOGF(debug, "mc parts = %d", item.second.size()); + for (const auto& id : item.second) { + auto p = McParts.iteratorAt(id); + LOGF(debug, + " part %d: pdg=%d status=%d has_mothers=%d has_daughters=%d", + id, p.pdgCode(), p.statusCode(), + p.has_mothers(), p.has_daughters()); + } + continue; + } int32_t trId1 = item.second[0]; int32_t trId2 = item.second[1]; int32_t candID = item.first; @@ -990,12 +813,12 @@ struct FwdMuonsUPC { processMcGenCand(cand, tr1, tr2); } } - PROCESS_SWITCH(FwdMuonsUPC, processMcGen, "", false); + PROCESS_SWITCH(FwdMuonsUpc, processMcGen, "", false); // process reco MC (gen info included) void processMcReco(CandidatesFwd const& eventCandidates, CompleteFwdTracks const& fwdTracks, - aod::UDMcCollisions const&, + aod::UDMcCollisions const& /*mccollisions*/, aod::UDMcParticles const& McParts) { std::unordered_map> tracksPerCandAll; @@ -1003,7 +826,7 @@ struct FwdMuonsUPC { // loop over the candidates for (const auto& item : tracksPerCandAll) { - if (item.second.size() != 4) { + if (item.second.size() != k4Tracks) { LOGF(debug, "number track (reco + gen) = %d", item.second.size()); continue; } @@ -1030,12 +853,12 @@ struct FwdMuonsUPC { processMcRecoCand(cand, tr1, trMc1, tr2, trMc2); } } - PROCESS_SWITCH(FwdMuonsUPC, processMcReco, "", false); + PROCESS_SWITCH(FwdMuonsUpc, processMcReco, "", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc), + adaptAnalysisTask(cfgc), }; } diff --git a/PWGUD/Tasks/polarisationRho.cxx b/PWGUD/Tasks/polarisationRho.cxx index 386bd5de510..e320f606f0d 100644 --- a/PWGUD/Tasks/polarisationRho.cxx +++ b/PWGUD/Tasks/polarisationRho.cxx @@ -48,9 +48,9 @@ struct PolarisationRho { HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; //_____________________________________________________________________________ - Double_t CosThetaHelicityFrame(TLorentzVector pionPositive, - TLorentzVector pionNegative, - TLorentzVector possibleRhoZero) + Double_t CosThetaHelicityFrame(const TLorentzVector& pionPositive, + const TLorentzVector& pionNegative, + const TLorentzVector& possibleRhoZero) { Double_t HalfSqrtSnn = 2680.; @@ -77,7 +77,7 @@ struct PolarisationRho { return CosThetaHE; } //------------------------------------------------------------------------------------------------------ - Double_t PhiHelicityFrame(TLorentzVector muonPositive, TLorentzVector muonNegative, TLorentzVector possibleJPsi) + Double_t PhiHelicityFrame(const TLorentzVector& muonPositive, const TLorentzVector& muonNegative, const TLorentzVector& possibleJPsi) { // Half of the energy per pair of the colliding nucleons. @@ -241,7 +241,7 @@ struct PolarisationRho { std::vector onlyKaonTracks; std::vector onlyKaonSigma; std::vector rawKaonTracks; - for (auto t : tracks) { + for (const auto& t : tracks) { if (!t.isPVContributor()) { continue; } @@ -260,7 +260,7 @@ struct PolarisationRho { //_____________________________________ // Creating phis if (onlyKaonTracks.size() == 2) { - for (auto kaon : onlyKaonTracks) { + for (const auto& kaon : onlyKaonTracks) { phi += kaon; } registry.fill(HIST("hNsigEvsKa1"), rawKaonTracks[0].tpcNSigmaKa(), rawKaonTracks[1].tpcNSigmaKa()); @@ -276,7 +276,7 @@ struct PolarisationRho { std::vector allTracksAreKaonsWrongMomentum; std::vector allTracksArePions; int counter = 0; - for (auto t : tracks) { + for (const auto& t : tracks) { if (!t.isPVContributor()) { continue; } @@ -304,13 +304,13 @@ struct PolarisationRho { //_____________________________________ // Creating phis if (allTracksAreKaons.size() == 2) { - for (auto kaon : allTracksAreKaons) { + for (const auto& kaon : allTracksAreKaons) { phiWithoutPID += kaon; } - for (auto kaon : allTracksAreKaonsWrongMomentum) { + for (const auto& kaon : allTracksAreKaonsWrongMomentum) { phiWrongMomentaWithoutPID += kaon; } - for (auto pion : allTracksArePions) { + for (const auto& pion : allTracksArePions) { phiWithoutPIDPionHypothesis += pion; } if (phiWithoutPID.M() < 1.05) { @@ -336,7 +336,7 @@ struct PolarisationRho { std::vector rawPionTracks; registry.fill(HIST("hTracks"), tracks.size()); float sign = 1.; - for (auto t : tracks) { + for (const auto& t : tracks) { if (!t.isPVContributor()) { continue; } @@ -361,7 +361,7 @@ struct PolarisationRho { registry.fill(HIST("hTracksPions"), onlyPionTracks.size()); //_____________________________________ // Creating rhos - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { p += pion; } //_____________________________________ @@ -369,7 +369,7 @@ struct PolarisationRho { if (onlyPionTracks.size() == 4) { registry.fill(HIST("hMassFourPions"), p.M()); int signSum = 0; - for (auto rawPion : rawPionTracks) { + for (const auto& rawPion : rawPionTracks) { if (rawPion.sign() > 0) { signSum += 1; } else if (rawPion.sign() < 0) { @@ -385,7 +385,7 @@ struct PolarisationRho { if (onlyPionTracks.size() == 6) { registry.fill(HIST("hMassSixPions"), p.M()); int signSum = 0; - for (auto rawPion : rawPionTracks) { + for (const auto& rawPion : rawPionTracks) { if (rawPion.sign() > 0) { signSum += 1; } else if (rawPion.sign() < 0) { @@ -394,7 +394,7 @@ struct PolarisationRho { } if (signSum == 0) { registry.fill(HIST("hMassSixPionsRightSign"), p.M()); - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { registry.fill(HIST("hPhiEtaSixPionsRightSign"), pion.Phi(), pion.Eta()); } } diff --git a/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx b/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx new file mode 100644 index 00000000000..0ffc5ee5f6d --- /dev/null +++ b/PWGUD/Tasks/ptSpectraInclusiveUpc.cxx @@ -0,0 +1,504 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +/// +/// \file ptSpectraInclusiveUpc.cxx +/// \executable o2-analysis-ud-pt-spectra-inclusive-upc +/// \brief Task for pT spectra of pions, kaons and protons in inclusive UPC events. +/// Used to obtain the templates for the DCA_xy fits for the primary fractions. +/// Fractions are obtained separately for TPC and TOF spectra: a particle passing both PID +/// selections will contribute to both histograms. +/// +/// \author Andrea Giovanni Riffero andrea.giovanni.riffero@cern.ch + +#include "PWGUD/DataModel/UDTables.h" + +#include "Common/Core/RecoDecay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct PtSpectraInclusiveUpc { + + Service pdg; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + ConfigurableAxis ptBinning{"ptBinning", + {VARIABLE_WIDTH, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, + 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, + 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}, + "#it{p}_{T} (GeV/#it{c})"}; + + ConfigurableAxis dcaXYaxis{"dcaXYaxis", {1000, -0.6, 0.6}, "DCA_{xy} (cm) binning"}; + Configurable applyKineCutsInGen{"applyKineCutsInGen", false, "Apply kinematic cuts in the generated level"}; + + Configurable etaMax{"etaMax", 0.9, "Maximum track pseudorapidity"}; + Configurable yMax{"yMax", 0.9, "Maximum particle rapidity"}; + Configurable ptMin{"ptMin", 0.1, "Minimum track transverse momentum (GeV/c)"}; + Configurable nFindableMin{"nFindableMin", 70, "Minimum number of findable TPC clusters"}; + Configurable sigmaMax{"sigmaMax", 3., "Maximum absolute PID n-sigma"}; + Configurable dcaZlimit{"dcaZlimit", 2., "Maximum absolute DCA in z (cm)"}; + Configurable maxChi2TPC{"maxChi2TPC", 4., "Maximum TPC chi2 per cluster"}; + Configurable maxChi2ITS{"maxChi2ITS", 36., "Maximum ITS chi2 per cluster"}; + + // define abbreviations + using CCs = soa::Join; + using CC = CCs::iterator; + using CCMCs = soa::Join; + using CCMC = CCMCs::iterator; + using TCs = soa::Join; + using TCMCs = soa::Join; + + void init(InitContext const&) + { + + // axes + const AxisSpec axisPt{ + ptBinning, + "#it{p}_{T} (GeV/#it{c})", + "axisPt"}; + + const AxisSpec axisEventCounter{ + 2, + 0.5, + 2.5, + "Event type"}; + + const AxisSpec axisDCAxy{ + dcaXYaxis, + "DCA_{xy} (cm)"}; + + // histograms + // generated pT spectra + histos.add("ptGeneratedPion", "Generated pions;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptGeneratedKaon", "Generated kaons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptGeneratedProton", "Generated protons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // reconstructed pT spectra with TPC PID + histos.add("ptReconstructedTPCPion", "Reconstructed pions (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTPCKaon", "Reconstructed kaons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTPCProton", "Reconstructed protons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // reconstructed pT spectra with TOF PID + histos.add("ptReconstructedTOFPion", "Reconstructed TOF pions;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTOFKaon", "Reconstructed TOF kaons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptReconstructedTOFProton", "Reconstructed TOF protons;#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // data pT spectra with TPC PID + histos.add("ptDataTPCPion", "Data pions (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTPCKaon", "Data kaons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTPCProton", "Data protons (TPC);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + // data pT spectra with TOF PID + histos.add("ptDataTOFPion", "Data pions (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTOFKaon", "Data kaons (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + histos.add("ptDataTOFProton", "Data protons (TOF);#it{p}_{T} (GeV/#it{c});Counts", kTH1F, {axisPt}); + + histos.add("myEventCounter", "Event counter;Event type;Counts", kTH1F, {axisEventCounter}); + + histos.add( + "DCAxy_TPC_primary_pions", + "Primary pions (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_pions", + "Secondary pions (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_primary_kaons", + "Primary kaons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_kaons", + "Secondary kaons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_primary_protons", + "Primary protons (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_secondary_protons", + "Secondary protons from decays (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_material_protons", + "Secondary protons from material (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_pions", + "Data pion candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_kaons", + "Data kaon candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TPC_data_protons", + "Data proton candidates (TPC);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_pions", + "Primary pions (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_pions", + "Secondary pions (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_kaons", + "Primary kaons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_kaons", + "Secondary kaons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_primary_protons", + "Primary protons (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_secondary_protons", + "Secondary protons from decays (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_material_protons", + "Secondary protons from material (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_pions", + "Data pion candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_kaons", + "Data kaon candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + + histos.add( + "DCAxy_TOF_data_protons", + "Data proton candidates (TOF);#it{p}_{T} (GeV/#it{c});DCA_{xy} (cm)", + kTH2F, + {axisPt, axisDCAxy}); + } + + void processSim(aod::UDMcCollision const&, aod::UDMcParticles const& mcParticles) + { + + std::array trackMomentum; + + for (const auto& mcParticle : mcParticles) { + if (!mcParticle.isPhysicalPrimary()) + continue; + + trackMomentum[0] = mcParticle.px(); + trackMomentum[1] = mcParticle.py(); + trackMomentum[2] = mcParticle.pz(); + + if (applyKineCutsInGen) { + if (std::fabs(RecoDecay::eta(trackMomentum)) > etaMax) + continue; + + if (std::fabs(RecoDecay::y(trackMomentum, pdg->Mass(mcParticle.pdgCode()))) > yMax) + continue; + + if (RecoDecay::pt(trackMomentum) < ptMin) + continue; + } + + if (std::abs(mcParticle.pdgCode()) == PDG_t::kPiPlus) { + histos.fill(HIST("ptGeneratedPion"), RecoDecay::pt(trackMomentum)); + } + + if (std::abs(mcParticle.pdgCode()) == PDG_t::kKPlus) { + histos.fill(HIST("ptGeneratedKaon"), RecoDecay::pt(trackMomentum)); + } + + if (std::abs(mcParticle.pdgCode()) == PDG_t::kProton) { + histos.fill(HIST("ptGeneratedProton"), RecoDecay::pt(trackMomentum)); + } + + histos.fill(HIST("myEventCounter"), 1); // gen event + } + } + + template + void selectDataAndFillHistos(TCollision const& /*collision*/, TTracks const& tracks) + { + + double dcaXyLimit = 0; + bool passDCAxyCut = true; + + auto nSigmaPi = -999.; + auto nSigmaKa = -999.; + auto nSigmaPr = -999.; + + std::array trackMomentum; + + for (const auto& track : tracks) { + passDCAxyCut = true; + + if (!track.isPVContributor()) { + continue; + } + + // here TPC selection on findable number of clusters + if (track.hasTPC()) { + if (track.tpcNClsFindable() < nFindableMin) { + continue; + } + if (track.tpcChi2NCl() > maxChi2TPC) { + continue; + } + } + + if (track.itsChi2NCl() > maxChi2ITS) { + continue; + } + + if (track.pt() < ptMin) { + continue; + } + + if (!(std::abs(track.dcaZ()) < dcaZlimit)) { + continue; + } + + dcaXyLimit = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); + if ((std::abs(track.dcaXY()) > dcaXyLimit)) { + passDCAxyCut = false; + } + + bool isPrimary = false; + bool isDecay = false; + if constexpr (isMc) { + if (!track.has_udMcParticle()) { + continue; + } + const auto mcParticle = track.udMcParticle(); + isPrimary = mcParticle.isPhysicalPrimary(); + isDecay = mcParticle.getProcess() == kPDecay; + } + + trackMomentum[0] = track.px(); + trackMomentum[1] = track.py(); + trackMomentum[2] = track.pz(); + + // compute rapidity for each ma + // TPC tracks + if (track.hasTPC()) { + nSigmaPi = track.tpcNSigmaPi(); + nSigmaKa = track.tpcNSigmaKa(); + nSigmaPr = track.tpcNSigmaPr(); + + if (std::abs(nSigmaPi) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCPion"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_pions"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_secondary_pions"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCPion"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_pions"), track.pt(), track.dcaXY()); + } + } + } + if (std::abs(nSigmaKa) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCKaon"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_kaons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_secondary_kaons"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCKaon"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_kaons"), track.pt(), track.dcaXY()); + } + } + } + + if (std::abs(nSigmaPr) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTPCProton"), track.pt()); + histos.fill(HIST("DCAxy_TPC_primary_protons"), track.pt(), track.dcaXY()); + } else { + if (isDecay) { + histos.fill(HIST("DCAxy_TPC_secondary_protons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TPC_material_protons"), track.pt(), track.dcaXY()); + } + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTPCProton"), track.pt()); + histos.fill(HIST("DCAxy_TPC_data_protons"), track.pt(), track.dcaXY()); + } + } + } + } + + // TOF tracks + if (track.hasTOF()) { + nSigmaPi = track.tofNSigmaPi(); + nSigmaKa = track.tofNSigmaKa(); + nSigmaPr = track.tofNSigmaPr(); + + if (std::abs(nSigmaPi) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassPionCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFPion"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_pions"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_secondary_pions"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFPion"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_pions"), track.pt(), track.dcaXY()); + } + } + } + + if (std::abs(nSigmaKa) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassKaonCharged)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFKaon"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_kaons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_secondary_kaons"), track.pt(), track.dcaXY()); + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFKaon"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_kaons"), track.pt(), track.dcaXY()); + } + } + } + + if (std::abs(nSigmaPr) < sigmaMax) { + if (std::abs(RecoDecay::y(trackMomentum, o2::constants::physics::MassProton)) < yMax) { + if constexpr (isMc) { + if (isPrimary) { + if (passDCAxyCut) + histos.fill(HIST("ptReconstructedTOFProton"), track.pt()); + histos.fill(HIST("DCAxy_TOF_primary_protons"), track.pt(), track.dcaXY()); + } else { + if (isDecay) { + histos.fill(HIST("DCAxy_TOF_secondary_protons"), track.pt(), track.dcaXY()); + } else { + histos.fill(HIST("DCAxy_TOF_material_protons"), track.pt(), track.dcaXY()); + } + } + } else { + if (passDCAxyCut) + histos.fill(HIST("ptDataTOFProton"), track.pt()); + histos.fill(HIST("DCAxy_TOF_data_protons"), track.pt(), track.dcaXY()); + } + } + } + } + } + } + + void processReco(CCMC const& collision, TCMCs const& tracks, aod::UDMcParticles const&) + { + selectDataAndFillHistos(collision, tracks); + } + + void processData(CC const& collision, TCs const& tracks) + { + selectDataAndFillHistos(collision, tracks); + } + + PROCESS_SWITCH(PtSpectraInclusiveUpc, processSim, "processSim", false); + + PROCESS_SWITCH(PtSpectraInclusiveUpc, processReco, "processReco", true); + + PROCESS_SWITCH(PtSpectraInclusiveUpc, processData, "processData", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/Tasks/sgDeuteronSpectra.cxx b/PWGUD/Tasks/sgDeuteronSpectra.cxx new file mode 100644 index 00000000000..109afa51a79 --- /dev/null +++ b/PWGUD/Tasks/sgDeuteronSpectra.cxx @@ -0,0 +1,212 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file sgDeuteronSpectra.cxx +/// \brief Analysis for the (anti)deuteron production in UPC +/// \author Marika Rasa +/// \since September 2026 + +#include "PWGUD/Core/SGSelector.h" +#include "PWGUD/Core/SGTrackSelector.h" +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +struct SgDeuteronSpectra { + // UPC cuts + SGSelector sgSelector; + Configurable fv0Cut{"fv0Cut", 50., "FV0A threshold"}; + Configurable zdcCut{"zdcCut", 10., "ZDC threshold"}; + Configurable ft0aCut{"ft0aCut", 150., "FT0A threshold"}; + Configurable ft0cCut{"ft0cCut", 50., "FT0C threshold"}; + Configurable fddaCut{"fddaCut", 10000., "FDDA threshold"}; + Configurable fddcCut{"fddcCut", 10000., "FDDC threshold"}; + + // Track cuts + Configurable pvCut{"pvCut", 1.0, "Use Only PV tracks"}; + Configurable dcaZCut{"dcaZCut", 2.0, "dcaZ cut"}; + Configurable dcaXYCut{"dcaXYCut", 0.0, "dcaXY cut (0 for Pt-function)"}; + Configurable tpcChi2Cut{"tpcChi2Cut", 4, "Max tpcChi2NCl"}; + Configurable tpcNClsFindableCut{"tpcNClsFindableCut", 70, "Min tpcNClsFindable"}; + Configurable itsChi2Cut{"itsChi2Cut", 36, "Max itsChi2NCl"}; + Configurable etaCut{"etaCut", 0.9, "Track Pseudorapidity"}; + Configurable ptCut{"ptCut", 0.1, "Track Pt"}; + Configurable nsigmatpcPreselCut{"nsigmatpcPreselCut", 3.0, "nsigma TPC preselection cut for TOF analysis"}; + Configurable occupancyCut{"occupancyCut", 1000, "Occupancy cut"}; + Configurable vertexZCut{"vertexZCut", 10, "VertexZ cut"}; + Configurable maxTracks{"maxTracks", 100, "Maximum number of tracks in the event"}; + Configurable useVtxFT0Cut{"useVtxFT0Cut", true, "Use zVtxFT0vPV cut in event selection"}; + + // Configurable axis for histograms + ConfigurableAxis ptAxis{"ptAxis", {200, 0.0, 10.0}, "p_{T}"}; + ConfigurableAxis nSigmaAxis{"nSigmaAxis", {800, -20.0, 20.0}, "nSigma axis for TPC and TOF"}; + + // Initialize histogram registry + HistogramRegistry registry{"registry", {}}; + + void init(InitContext&) + { + const AxisSpec axispt{ptAxis, "p_{T}"}; + const AxisSpec axistpc{nSigmaAxis, "n#sigma_{TPC}"}; + const AxisSpec axistof{nSigmaAxis, "n#sigma_{TOF}"}; + + // Collision histograms + registry.add("collisions/selectionCuts", "", {HistType::kTH1F, {{8, -0.5, 7.5}}}); + registry.add("collisions/VertexZ", "", {HistType::kTH1F, {{800, -20, 20}}}); + registry.add("collisions/GapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{3, -0.5, 2.5}}}); + registry.add("collisions/TrueGapSide", "Gap Side: A, C, A+C", {HistType::kTH1F, {{4, -1.5, 2.5}}}); + registry.add("tracks/Deut_Pt_TPC_GapA", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_GapA", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_GapA_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_GapA", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_GapA", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_GapA_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TPC_GapC", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_GapC", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_GapC_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_GapC", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_GapC", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_GapC_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TPC_DoubleGap", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Deut_Pt_TOF_DoubleGap", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Deut_Pt_TOF_DoubleGap_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TPC_DoubleGap", "", {HistType::kTH2F, {axispt, axistpc}}); + registry.add("tracks/Antideut_Pt_TOF_DoubleGap", "", {HistType::kTH2F, {axispt, axistof}}); + registry.add("tracks/Antideut_Pt_TOF_DoubleGap_TPCpresel", "", {HistType::kTH2F, {axispt, axistof}}); + } + + // Define data types + using UDCollisionsFull = soa::Join; // UDCollisions + using UDCollisionFull = UDCollisionsFull::iterator; + using UDTracksFull = soa::Join; + + void process(UDCollisionFull const& coll, UDTracksFull const& tracks) + { + registry.fill(HIST("collisions/VertexZ"), coll.posZ()); + registry.fill(HIST("collisions/selectionCuts"), 0); + // applying event cuts + if (!coll.tfb()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 1); + if (!coll.itsROFb()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 2); + if (!coll.sbp()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 3); + if (useVtxFT0Cut) { + if (!coll.zVtxFT0vPV()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 4); + } else { + registry.fill(HIST("collisions/selectionCuts"), 4); + } + if (!coll.vtxITSTPC()) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 5); + if (std::abs(coll.posZ()) > vertexZCut) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 6); + if (coll.occupancyInTime() > occupancyCut) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 7); + if (tracks.size() > maxTracks) { + return; + } + registry.fill(HIST("collisions/selectionCuts"), 8); + // fill gapside + registry.fill(HIST("collisions/GapSide"), coll.gapSide(), 1.); + std::vector fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; + int truegapSide = sgSelector.trueGap(coll, fitCut[0], fitCut[1], fitCut[2], zdcCut); + registry.fill(HIST("collisions/TrueGapSide"), truegapSide, 1.); + + std::vector parameters = {pvCut, dcaZCut, dcaXYCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; + + for (const auto& t : tracks) { + if (trackselector(t, parameters) != 0) { + if (truegapSide == o2::aod::sgselector::SingleGapA) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_GapA"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_GapA"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_GapA_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_GapA"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapA"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapA_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + + if (truegapSide == o2::aod::sgselector::SingleGapC) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_GapC"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_GapC"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_GapC_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_GapC"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapC"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_GapC_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + + if (truegapSide == o2::aod::sgselector::DoubleGap) { + if (t.sign() > 0) { + registry.fill(HIST("tracks/Deut_Pt_TPC_DoubleGap"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Deut_Pt_TOF_DoubleGap"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Deut_Pt_TOF_DoubleGap_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } else { + registry.fill(HIST("tracks/Antideut_Pt_TPC_DoubleGap"), t.pt(), t.tpcNSigmaDe()); + registry.fill(HIST("tracks/Antideut_Pt_TOF_DoubleGap"), t.pt(), t.tofNSigmaDe()); + if (std::abs(t.tpcNSigmaDe()) < nsigmatpcPreselCut) { + registry.fill(HIST("tracks/Antideut_Pt_TOF_DoubleGap_TPCpresel"), t.pt(), t.tofNSigmaDe()); + } + } + } + } + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGUD/Tasks/sgExcUniverse.cxx b/PWGUD/Tasks/sgExcUniverse.cxx index a02a30b621d..3b1d0c3f8fb 100644 --- a/PWGUD/Tasks/sgExcUniverse.cxx +++ b/PWGUD/Tasks/sgExcUniverse.cxx @@ -140,7 +140,7 @@ struct SGExcUniverse { std::vector iskaon; std::vector ismuon; std::vector iselec; - for (auto t : tracks) { + for (const auto& t : tracks) { TLorentzVector a; if (trackselector(t, parameters)) { px.push_back(t.px()); diff --git a/PWGUD/Tasks/sgExclusivePhi.cxx b/PWGUD/Tasks/sgExclusivePhi.cxx index a68d9ba8cdb..f93a46d578f 100644 --- a/PWGUD/Tasks/sgExclusivePhi.cxx +++ b/PWGUD/Tasks/sgExclusivePhi.cxx @@ -57,9 +57,9 @@ struct sgExclusivePhi { HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; //_____________________________________________________________________________ - Double_t CosThetaHelicityFrame(TLorentzVector pionPositive, - TLorentzVector pionNegative, - TLorentzVector possibleRhoZero) + Double_t CosThetaHelicityFrame(const TLorentzVector& pionPositive, + const TLorentzVector& pionNegative, + const TLorentzVector& possibleRhoZero) { Double_t HalfSqrtSnn = 2680.; @@ -86,7 +86,7 @@ struct sgExclusivePhi { return CosThetaHE; } //------------------------------------------------------------------------------------------------------ - Double_t PhiHelicityFrame(TLorentzVector muonPositive, TLorentzVector muonNegative, TLorentzVector possibleJPsi) + Double_t PhiHelicityFrame(const TLorentzVector& muonPositive, const TLorentzVector& muonNegative, const TLorentzVector& possibleJPsi) { // Half of the energy per pair of the colliding nucleons. @@ -296,7 +296,7 @@ struct sgExclusivePhi { std::vector onlyKaonSigma; std::vector rawKaonTracks; - for (auto trk : tracks) { + for (const auto& trk : tracks) { registry.fill(HIST("hSelectionCounter"), 1); if (!trk.isPVContributor()) { continue; @@ -357,7 +357,7 @@ struct sgExclusivePhi { if (onlyKaonTracks.size() == 2) { registry.fill(HIST("hSelectionCounter"), 7); - for (auto kaon : onlyKaonTracks) { + for (const auto& kaon : onlyKaonTracks) { phi += kaon; } @@ -409,7 +409,7 @@ struct sgExclusivePhi { std::vector booleanAvgClusterSizePerTrackITSonly; int counter = 0; - for (auto t : tracks) { + for (const auto& t : tracks) { registry.fill(HIST("hSelectionCounter2"), 0); if (!t.isPVContributor()) { continue; @@ -521,16 +521,16 @@ struct sgExclusivePhi { if (fabs(collision.posZ()) < 10.) { if (allTracksAreKaons.size() == 2) { registry.fill(HIST("hSelectionCounter2"), 7); - for (auto kaon : allTracksAreKaons) { + for (const auto& kaon : allTracksAreKaons) { phiWithoutPID += kaon; } registry.fill(HIST("hTracksKaons"), allTracksAreKaons.size()); // kaon mass hypothesis with wrong momentum for one track - for (auto kaon : allTracksAreKaonsWrongMomentum) { + for (const auto& kaon : allTracksAreKaonsWrongMomentum) { phiWrongMomentaWithoutPID += kaon; } // pion mass hypothesis - for (auto pion : allTracksArePions) { + for (const auto& pion : allTracksArePions) { phiWithoutPIDPionHypothesis += pion; } @@ -674,7 +674,7 @@ struct sgExclusivePhi { if (allTracksAreKaonsBandPID.size() == 2) { TLorentzVector reallyPhi; - for (auto kaon : allTracksAreKaonsBandPID) { + for (const auto& kaon : allTracksAreKaonsBandPID) { reallyPhi += kaon; } diff --git a/PWGUD/Tasks/sgExclusivePhiITSselections.cxx b/PWGUD/Tasks/sgExclusivePhiITSselections.cxx index d3d8a580b4b..36bb5a2d5e0 100644 --- a/PWGUD/Tasks/sgExclusivePhiITSselections.cxx +++ b/PWGUD/Tasks/sgExclusivePhiITSselections.cxx @@ -168,7 +168,7 @@ struct sgExclusivePhiITSselections { std::vector allTracksAreKaons; std::vector allTracksAreKaonsBandPID; std::vector allTracksAreITSonlyAndFourITSclusters; - for (auto t : tracks) { + for (const auto& t : tracks) { registry.fill(HIST("hSelectionCounter2"), 0); if (!t.isPVContributor()) { continue; @@ -267,7 +267,7 @@ struct sgExclusivePhiITSselections { // in the case that there are ONLY 2 PV if (allTracksAreKaons.size() == 2) { registry.fill(HIST("hSelectionCounter2"), 7); - for (auto kaon : allTracksAreKaons) { + for (const auto& kaon : allTracksAreKaons) { phiWithoutPID += kaon; } registry.fill(HIST("hTracksKaons"), allTracksAreKaons.size()); @@ -314,7 +314,7 @@ struct sgExclusivePhiITSselections { if (allTracksAreKaonsBandPID.size() == 2) { registry.fill(HIST("hTracksKaons"), allTracksAreKaonsBandPID.size() + 10); TLorentzVector reallyPhi; - for (auto kaon : allTracksAreKaonsBandPID) { + for (const auto& kaon : allTracksAreKaonsBandPID) { reallyPhi += kaon; } registry.fill(HIST("KaonBandPHI/hPtPhiIdentifiedKaons"), reallyPhi.Pt()); diff --git a/PWGUD/Tasks/sgFITAnalyzer.cxx b/PWGUD/Tasks/sgFITAnalyzer.cxx index f3369b3a7cb..cff17b961f6 100644 --- a/PWGUD/Tasks/sgFITAnalyzer.cxx +++ b/PWGUD/Tasks/sgFITAnalyzer.cxx @@ -37,6 +37,7 @@ #include #include +#include #include using namespace o2; @@ -411,7 +412,7 @@ struct SGFITAnalyzer { std::vector goodTracks; std::vector muonTracks; float sign = 0; - for (auto t : dgtracks) { + for (const auto& t : dgtracks) { TLorentzVector a; TLorentzVector b; a.SetXYZM(t.px(), t.py(), t.pz(), mpion); @@ -424,13 +425,13 @@ struct SGFITAnalyzer { } } if (goodTracks.size() == 2) { - for (auto pion : goodTracks) { + for (const auto& pion : goodTracks) { rho += pion; } if (sign == 0 && TMath::Abs(rho.Rapidity()) < .9 && rho.M() > .5 && rho.M() < 1.2 && rho.Pt() < 0.1) coh_rho0 = true; if (muonTracks.size() == 2) { - for (auto muon : muonTracks) { + for (const auto& muon : muonTracks) { jpsi += muon; } if (sign == 0 && TMath::Abs(jpsi.Rapidity()) < .9 && jpsi.M() > 2.8 && jpsi.M() < 3.35 && jpsi.Pt() < 0.1) @@ -737,7 +738,7 @@ struct SGFITAnalyzer { registry.get(HIST("ZDC/MACZNA"))->Fill(PVContributors.size(), zna); registry.get(HIST("ZDC/MACZNC"))->Fill(PVContributors.size(), znc); } - for (auto track : dgtracks) { + for (const auto& track : dgtracks) { registry.get(HIST("tracks/QCAll"))->Fill(0., 1.); registry.get(HIST("tracks/QCAll"))->Fill(1., track.hasITS() * 1.); registry.get(HIST("tracks/QCAll"))->Fill(2., track.hasTPC() * 1.); @@ -777,36 +778,36 @@ struct SGFITAnalyzer { avPtPVa += track.pt(); vecEtaPVa.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaApv"))->Fill(vtrk.Eta(), 1.); - if (!an) + if (!an) { registry.get(HIST("tracks/eta2Apv"))->Fill(vtrk.Eta(), 1.); - if (!an) z0pva++; - else if (an == 4) + } else if (an == 4) { z1pva++; + } } if (truegapSide == 1) { pvc++; avPtPVc += track.pt(); vecEtaPVc.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaCpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) + if (!cn) { registry.get(HIST("tracks/eta2Cpv"))->Fill(vtrk.Eta(), 1.); - if (!cn) z0pvc++; - else if (cn == 4) + } else if (cn == 4) { z1pvc++; + } } if (truegapSide == 2) { pvac++; avPtPVac += track.pt(); vecEtaPVac.push_back(vtrk.Eta()); registry.get(HIST("tracks/etaACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) + if (!an && !cn) { registry.get(HIST("tracks/eta2ACpv"))->Fill(vtrk.Eta(), 1.); - if (!an && !cn) z0pvac++; - else if (an >= 3 && cn >= 3) + } else if (an >= 3 && cn >= 3) { z1pvac++; + } } registry.get(HIST("tracks/TPCSignalvspPVC"))->Fill(vtrk.Mag(), signalTPC, 1.); registry.get(HIST("tracks/TOFSignalvspPVC"))->Fill(vtrk.Mag(), signalTOF, 1.); diff --git a/PWGUD/Tasks/sgFourPiAnalyzer.cxx b/PWGUD/Tasks/sgFourPiAnalyzer.cxx index ef6f1b17a0b..ff3c59d21ed 100644 --- a/PWGUD/Tasks/sgFourPiAnalyzer.cxx +++ b/PWGUD/Tasks/sgFourPiAnalyzer.cxx @@ -114,7 +114,7 @@ struct SGFourPiAnalyzer { gapSide = truegapSide; std::vector goodTracks; float sign = 0; - for (auto t : tracks) { + for (const auto& t : tracks) { int itsNCls = t.itsNCls(); // if (itsNCls) { registry.fill(HIST("ITSNCls"), itsNCls); @@ -128,7 +128,7 @@ struct SGFourPiAnalyzer { } // std::cout << goodTracks.size()< parameters = {PV_cut, dcaZ_cut, dcaXY_cut, tpcChi2_cut, tpcNClsFindable_cut, itsChi2_cut, eta_cut, pt_cut}; // check rho0 signals - for (auto t : tracks) { + for (const auto& t : tracks) { if (trackselector(t, parameters) && t.hasTPC()) { if (truegapSide == 0) { if (t.sign() > 0) { diff --git a/PWGUD/Tasks/sgPIDSpectraTable.cxx b/PWGUD/Tasks/sgPIDSpectraTable.cxx index 75b81336c3d..741281834b7 100644 --- a/PWGUD/Tasks/sgPIDSpectraTable.cxx +++ b/PWGUD/Tasks/sgPIDSpectraTable.cxx @@ -104,7 +104,7 @@ struct SGPIDSpectraTable { float tpcde, tofde, tpcmu, tofmu; TVector3 a; int goodtracks = 0; - for (auto t : tracks) { + for (const auto& t : tracks) { if (trackselector(t, parameters)) { goodtracks++; } @@ -113,7 +113,7 @@ struct SGPIDSpectraTable { return; SGevents(coll.runNumber(), coll.flags(), truegapSide, coll.energyCommonZNA(), coll.energyCommonZNC(), goodtracks, coll.occupancyInTime(), coll.hadronicRate()); // SGevents(coll.runNumber(), coll.flags()); - for (auto t : tracks) { + for (const auto& t : tracks) { if (trackselector(t, parameters)) { a.SetXYZ(t.px(), t.py(), t.pz()); tpcpi = t.hasTPC() ? t.tpcNSigmaPi() : -999; diff --git a/PWGUD/Tasks/sgSixPiAnalyzer.cxx b/PWGUD/Tasks/sgSixPiAnalyzer.cxx index ff288249dc2..bd440e01b1d 100644 --- a/PWGUD/Tasks/sgSixPiAnalyzer.cxx +++ b/PWGUD/Tasks/sgSixPiAnalyzer.cxx @@ -113,7 +113,7 @@ struct SGSixPiAnalyzer { gapSide = truegapSide; std::vector goodTracks; float sign = 0; - for (auto t : tracks) { + for (const auto& t : tracks) { int itsNCls = t.itsNCls(); // if (itsNCls) { registry.fill(HIST("ITSNCls"), itsNCls); @@ -127,7 +127,7 @@ struct SGSixPiAnalyzer { } // std::cout << goodTracks.size()< #include +#include #include #include diff --git a/PWGUD/Tasks/sgTwoPiAnalyzer.cxx b/PWGUD/Tasks/sgTwoPiAnalyzer.cxx index 77c6e4f2d94..89a937389f1 100644 --- a/PWGUD/Tasks/sgTwoPiAnalyzer.cxx +++ b/PWGUD/Tasks/sgTwoPiAnalyzer.cxx @@ -131,7 +131,7 @@ struct SGTwoPiAnalyzer { std::vector goodTracks; // Look for D0 and D0bar float sign = 0; - for (auto t : tracks) { + for (const auto& t : tracks) { int itsNCls = t.itsNCls(); // if (itsNCls) { registry.fill(HIST("ITSNCls"), itsNCls); @@ -145,7 +145,7 @@ struct SGTwoPiAnalyzer { } // std::cout << goodTracks.size()< #include +#include + +#include +#include #include #include +#include #include #include #include @@ -65,7 +70,7 @@ using o2::aod::sgselector::SingleGapC; struct SginclusivePhiKstarSD { SGSelector sgSelector; - Service pdg; + Service pdg{}; HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; HistogramRegistry rQA{"QA", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; @@ -152,6 +157,27 @@ struct SginclusivePhiKstarSD { ConfigurableAxis axispt{"axispt", {200, 0.0, 20.0}, ""}; ConfigurableAxis axisrapdity{"axisrapdity", {40, -2.0, 2.0}, ""}; + std::vector runNumbers = { + 544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, + 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, + 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, + 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, + 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, + 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, + 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, + 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, + 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, + 545296, 545312}; + + int getRunBin(int runNumber) + { + auto it = std::lower_bound(runNumbers.begin(), runNumbers.end(), runNumber); + if (it == runNumbers.end() || *it != runNumber) { + return -1; + } + return static_cast(std::distance(runNumbers.begin(), it)) + 1; + } + int numTwoTracks = 2; int numFourTracks = 4; @@ -304,6 +330,13 @@ struct SginclusivePhiKstarSD { rQA.add("V0A_2", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); rQA.add("V0A_0", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); rQA.add("V0A_1", "V0A amplitude", kTH1F, {{1000, 0.0, 1000.0}}); + + rQA.add("hIntRateVsRun", "Hadronic interaction rate; Run number; Hadronic interaction rate (kHz)", kTH2F, {{92, 0.5, 92.5}, {1000, 0., 50.}}); + auto hIntRateVsRun = rQA.get(HIST("hIntRateVsRun")); + for (size_t i = 0; i < runNumbers.size(); ++i) { + hIntRateVsRun->GetXaxis()->SetBinLabel(i + 1, Form("%d", runNumbers[i])); + } + hIntRateVsRun->GetXaxis()->LabelsOption("v"); } registry.add("gap_mult0", "Mult 0", kTH1F, {{100, 0.0, 100.0}}); registry.add("gap_mult1", "Mult 1", kTH1F, {{100, 0.0, 100.0}}); @@ -512,9 +545,9 @@ struct SginclusivePhiKstarSD { } //_____________________________________________________________________________ - double cosThetaCollinsSoperFrame(ROOT::Math::PxPyPzMVector pair1, - ROOT::Math::PxPyPzMVector pair2, - ROOT::Math::PxPyPzMVector fourpion) + double cosThetaCollinsSoperFrame(const ROOT::Math::PxPyPzMVector& pair1, + const ROOT::Math::PxPyPzMVector& pair2, + const ROOT::Math::PxPyPzMVector& fourPion) { double halfSqrtSnn = 2680.; double massOfLead208 = 193.6823; @@ -525,7 +558,7 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector v1 = ROOT::Math::PxPyPzMVector(pair1.Px(), pair1.Py(), pair1.Pz(), pair1.M()); ROOT::Math::PxPyPzMVector v2 = ROOT::Math::PxPyPzMVector(pair2.Px(), pair2.Py(), pair2.Pz(), pair2.M()); - ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourpion.Px(), fourpion.Py(), fourpion.Pz(), fourpion.M()); + ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourPion.Px(), fourPion.Py(), fourPion.Pz(), fourPion.M()); // Boost to center of mass frame ROOT::Math::Boost boostv12{v12.BoostToCM()}; @@ -540,7 +573,7 @@ struct SginclusivePhiKstarSD { return cosThetaCs; } - double phiCollinsSoperFrame(ROOT::Math::PxPyPzMVector pair1, ROOT::Math::PxPyPzMVector pair2, ROOT::Math::PxPyPzMVector fourpion) + double phiCollinsSoperFrame(const ROOT::Math::PxPyPzMVector& pair1, const ROOT::Math::PxPyPzMVector& pair2, const ROOT::Math::PxPyPzMVector& fourPion) { // Half of the energy per pair of the colliding nucleons. double halfSqrtSnn = 2680.; @@ -552,7 +585,7 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector v1 = ROOT::Math::PxPyPzMVector(pair1.Px(), pair1.Py(), pair1.Pz(), pair1.M()); ROOT::Math::PxPyPzMVector v2 = ROOT::Math::PxPyPzMVector(pair2.Px(), pair2.Py(), pair2.Pz(), pair2.M()); - ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourpion.Px(), fourpion.Py(), fourpion.Pz(), fourpion.M()); + ROOT::Math::PxPyPzMVector v12 = ROOT::Math::PxPyPzMVector(fourPion.Px(), fourPion.Py(), fourPion.Pz(), fourPion.M()); // Boost to center of mass frame ROOT::Math::Boost boostv12{v12.BoostToCM()}; @@ -565,8 +598,8 @@ struct SginclusivePhiKstarSD { ROOT::Math::XYZVectorF yaxisCs{(beam1Cm.Cross(beam2Cm)).Unit()}; ROOT::Math::XYZVectorF xaxisCs{(yaxisCs.Cross(zaxisCs)).Unit()}; - double phi = std::atan2(yaxisCs.Dot(v1Cm), xaxisCs.Dot(v1Cm)); - return phi; + double phiAngle = std::atan2(yaxisCs.Dot(v1Cm), xaxisCs.Dot(v1Cm)); + return phiAngle; } template @@ -589,12 +622,13 @@ struct SginclusivePhiKstarSD { template std::pair selectionEvent(const C& collision, bool fillHist = false) { - if (fillHist) + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 0); + } // Gapside logic int gapSide = collision.gapSide(); - float fitCut[5] = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; + std::array fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; int truegapSide = sgSelector.trueGap(collision, fitCut[0], fitCut[1], fitCut[2], zdcCut); if (fillHist) { @@ -604,80 +638,110 @@ struct SginclusivePhiKstarSD { gapSide = truegapSide; - if (gapSide < SingleGapA || gapSide > DoubleGap) + if (gapSide < SingleGapA || gapSide > DoubleGap) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 1); + } - if (upcflag != -1 && collision.flags() != upcflag) + if (upcflag != -1 && collision.flags() != upcflag) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 2); + } - if (std::abs(collision.posZ()) > vzCut) + if (std::abs(collision.posZ()) > vzCut) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 3); + } - if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) + if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 4); + } - if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) + if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 5); + } - if (useTrs && collision.trs() != 1) + if (useTrs && collision.trs() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 6); + } - if (useTrofs && collision.trofs() != 1) + if (useTrofs && collision.trofs() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 7); + } - if (useHmpr && collision.hmpr() != 1) + if (useHmpr && collision.hmpr() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 8); + } - if (useTfb && collision.tfb() != 1) + if (useTfb && collision.tfb() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 9); + } - if (useItsrofb && collision.itsROFb() != 1) + if (useItsrofb && collision.itsROFb() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 10); + } - if (useSbp && collision.sbp() != 1) + if (useSbp && collision.sbp() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 11); + } - if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) + if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 12); + } - if (useVtxItsTpc && collision.vtxITSTPC() != 1) + if (useVtxItsTpc && collision.vtxITSTPC() != 1) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 13); + } - if (!isGoodRCTflag(collision)) + if (!isGoodRCTflag(collision)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 14); + } - if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) + if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) { return {false, gapSide}; - if (fillHist) + } + if (fillHist) { registry.fill(HIST("hEventCutFlow"), 15); + } return {true, gapSide}; } @@ -749,9 +813,15 @@ struct SginclusivePhiKstarSD { void process(UDCollisionFull const& collision, UDtracksfull const& tracks) { - if (qa) + if (qa) { rQA.fill(HIST("hOcc_before"), collision.occupancyInTime()); + int runBin = getRunBin(collision.runNumber()); + if (runBin > 0 && collision.hadronicRate() > 0.) { + rQA.fill(HIST("hIntRateVsRun"), runBin, collision.hadronicRate()); + } + } + ROOT::Math::PxPyPzMVector v0; ROOT::Math::PxPyPzMVector v1; ROOT::Math::PxPyPzMVector v01; @@ -771,8 +841,9 @@ struct SginclusivePhiKstarSD { std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; auto [eventSelected, gapSide] = selectionEvent(collision, true); - if (!eventSelected) + if (!eventSelected) { return; + } int mult = collision.numContrib(); @@ -786,8 +857,9 @@ struct SginclusivePhiKstarSD { registry.fill(HIST("gap_mult2"), mult); } - if (qa) + if (qa) { rQA.fill(HIST("hOcc_after"), collision.occupancyInTime()); + } int mult0 = 0; int mult1 = 0; @@ -804,8 +876,9 @@ struct SginclusivePhiKstarSD { rQA.fill(HIST("hDcaz_all_before"), track1.dcaZ()); } - if (!trackselector(track1, parameters)) + if (trackselector(track1, parameters) == 0) { continue; + } v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassPionCharged); @@ -871,13 +944,15 @@ struct SginclusivePhiKstarSD { } } if (gapSide == SingleGapA) { - if (useMultCut && (mult0 < mintrack || mult0 > maxtrack)) + if (useMultCut && (mult0 < mintrack || mult0 > maxtrack)) { return; + } registry.fill(HIST("mult_0"), mult0); } if (gapSide == SingleGapC) { - if (useMultCut && (mult1 < mintrack || mult1 > maxtrack)) + if (useMultCut && (mult1 < mintrack || mult1 > maxtrack)) { return; + } registry.fill(HIST("mult_1"), mult1); } if (qa) { @@ -897,16 +972,18 @@ struct SginclusivePhiKstarSD { } } for (const auto& [t0, t1] : combinations(tracks, tracks)) { - if (!trackselector(t0, parameters) || !trackselector(t1, parameters)) + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { continue; + } if (phi && selectionPIDKaon1(t0) && selectionPIDKaon1(t1)) { // Apply kaon hypothesis and create pairs v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (t0.sign() != t1.sign()) { @@ -923,16 +1000,18 @@ struct SginclusivePhiKstarSD { // samesignpair if (t0.sign() == t1.sign()) { if (gapSide == SingleGapA) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_KK_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_KK_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (gapSide == SingleGapC) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_KK_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_KK_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { registry.fill(HIST("os_KK_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); @@ -953,8 +1032,9 @@ struct SginclusivePhiKstarSD { v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } if (t0.sign() != t1.sign()) { if (gapSide == SingleGapA) { @@ -972,16 +1052,19 @@ struct SginclusivePhiKstarSD { } } for (const auto& [t0, t1] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { - if (!trackselector(t0, parameters) || !trackselector(t1, parameters)) + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { continue; - if (t0.globalIndex() == t1.globalIndex()) + } + if (t0.globalIndex() == t1.globalIndex()) { continue; + } if (rho && selectionPIDProton(t0, useTof, nsigmaTpcCut, nsigmaTofCut) && selectionPIDKaon1(t1)) { v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassProton); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (t0.sign() != t1.sign()) { @@ -1008,13 +1091,15 @@ struct SginclusivePhiKstarSD { } } if (kstar && selectionPIDKaon1(t0) && selectionPIDPion1(t1)) { - if (kaoncut && t0.tpcNSigmaPi() < pionNsigmaCut) + if (kaoncut && t0.tpcNSigmaPi() < pionNsigmaCut) { continue; + } v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (t0.sign() != t1.sign()) { @@ -1030,16 +1115,18 @@ struct SginclusivePhiKstarSD { } // same sign pair if (t0.sign() == t1.sign()) { if (gapSide == SingleGapA) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_pk_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_pk_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (gapSide == SingleGapC) { - if (t0.sign() < 0) + if (t0.sign() < 0) { registry.fill(HIST("os_pk_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); - else + } else { registry.fill(HIST("os_pk_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } } if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { registry.fill(HIST("os_pk_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); @@ -1059,8 +1146,9 @@ struct SginclusivePhiKstarSD { v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } if (t0.sign() != t1.sign()) { if (gapSide == SingleGapA) { @@ -1081,10 +1169,10 @@ struct SginclusivePhiKstarSD { if (gapSide == DoubleGap && mult2 == numFourTracks) { ROOT::Math::PxPyPzMVector pair1, pair2, pair3, pair4; if (static_cast(onlyPionTracksp.size()) == numTwoTracks && static_cast(onlyPionTracksn.size()) == numTwoTracks) { - ROOT::Math::PxPyPzMVector k1 = onlyPionTracksp.at(0); - ROOT::Math::PxPyPzMVector k2 = onlyPionTracksp.at(1); - ROOT::Math::PxPyPzMVector k3 = onlyPionTracksn.at(0); - ROOT::Math::PxPyPzMVector k4 = onlyPionTracksn.at(1); + const ROOT::Math::PxPyPzMVector& k1 = onlyPionTracksp.at(0); + const ROOT::Math::PxPyPzMVector& k2 = onlyPionTracksp.at(1); + const ROOT::Math::PxPyPzMVector& k3 = onlyPionTracksn.at(0); + const ROOT::Math::PxPyPzMVector& k4 = onlyPionTracksn.at(1); phiv = k1 + k2 + k3 + k4; pair1 = k1 + k3; pair2 = k2 + k4; @@ -1107,12 +1195,13 @@ struct SginclusivePhiKstarSD { registry.fill(HIST("costheta_vs_phi1"), costhetaPair1, phiPair1); } if (static_cast(onlyPionTracksp.size()) != numTwoTracks && static_cast(onlyPionTracksn.size()) != numTwoTracks) { - if (static_cast(onlyPionTracksp.size()) + static_cast(onlyPionTracksn.size()) != numFourTracks) + if (static_cast(onlyPionTracksp.size()) + static_cast(onlyPionTracksn.size()) != numFourTracks) { return; - ROOT::Math::PxPyPzMVector l1 = onlyPionTrackspm.at(0); - ROOT::Math::PxPyPzMVector l2 = onlyPionTrackspm.at(1); - ROOT::Math::PxPyPzMVector l3 = onlyPionTrackspm.at(2); - ROOT::Math::PxPyPzMVector l4 = onlyPionTrackspm.at(3); + } + const ROOT::Math::PxPyPzMVector& l1 = onlyPionTrackspm.at(0); + const ROOT::Math::PxPyPzMVector& l2 = onlyPionTrackspm.at(1); + const ROOT::Math::PxPyPzMVector& l3 = onlyPionTrackspm.at(2); + const ROOT::Math::PxPyPzMVector& l4 = onlyPionTrackspm.at(3); phiv1 = l1 + l2 + l3 + l4; registry.fill(HIST("os_pppp_pT_2_ls"), phiv1.M(), phiv1.Pt(), phiv1.Rapidity()); } @@ -1137,30 +1226,34 @@ struct SginclusivePhiKstarSD { std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; - BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}, true}; + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}}; for (auto const& [collision1, collision2] : o2::soa::selfCombinations(binningOnPositions, cfgNoMixedEvents, -1, collisions, collisions)) { auto [eventSelected1, gapSide1] = selectionEvent(collision1, false); auto [eventSelected2, gapSide2] = selectionEvent(collision2, false); - if (!eventSelected1 || !eventSelected2) + if (!eventSelected1 || !eventSelected2) { continue; + } - if (gapSide1 != gapSide2) + if (gapSide1 != gapSide2) { continue; + } auto posThisColl = posTracks->sliceByCached(aod::udtrack::udCollisionId, collision1.globalIndex(), cache); auto negThisColl = negTracks->sliceByCached(aod::udtrack::udCollisionId, collision2.globalIndex(), cache); // for (auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { for (const auto& [track1, track2] : o2::soa::combinations(posThisColl, negThisColl)) { - if (!trackselector(track1, parameters) || !trackselector(track2, parameters)) + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { continue; + } if (phi && selectionPIDKaon1(track1) && selectionPIDKaon1(track2)) { v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassKaonCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (track1.sign() != track2.sign()) { if (gapSide1 == SingleGapA) { @@ -1173,16 +1266,19 @@ struct SginclusivePhiKstarSD { } } for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { - if (!trackselector(track1, parameters) || !trackselector(track2, parameters)) + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { continue; - if (track1.globalIndex() == track2.globalIndex()) + } + if (track1.globalIndex() == track2.globalIndex()) { continue; + } if (kstar && selectionPIDKaon1(track1) && selectionPIDPion1(track2)) { v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassPionCharged); v01 = v0 + v1; - if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { continue; + } // Opposite sign pairs if (track1.sign() != track2.sign()) { if (gapSide1 == SingleGapA) { @@ -1218,14 +1314,16 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector vkstar; ROOT::Math::PxPyPzMVector vphi; for (const auto& mccollision : mccollisions) { - if (mccollision.generatorsID() != generatedId) + if (mccollision.generatorsID() != generatedId) { continue; + } registry.get(HIST("MC/Stat"))->Fill(0., 1.); // get reconstructed collision which belongs to mccollision auto colSlice = collisions.sliceBy(colPerMcCollision, mccollision.globalIndex()); registry.get(HIST("MC/recCols"))->Fill(colSlice.size(), 1.); - if (reconstruction && colSlice.size() < 1) + if (reconstruction && colSlice.size() < 1) { continue; + } // get McParticles which belong to mccollision auto partSlice = McParts.sliceBy(partPerMcCollision, mccollision.globalIndex()); registry.get(HIST("MC/nParts"))->Fill(partSlice.size(), 1.); @@ -1410,23 +1508,27 @@ struct SginclusivePhiKstarSD { ROOT::Math::PxPyPzMVector vkstar; ROOT::Math::PxPyPzMVector vphi; - if (!collision.has_udMcCollision()) + if (!collision.has_udMcCollision()) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 1); - if (qaMC) + if (qaMC) { rQA.fill(HIST("hOcc_before_mc"), collision.occupancyInTime()); + } auto mccoll = collision.udMcCollision(); - if (mccoll.generatorsID() != generatedId) + if (mccoll.generatorsID() != generatedId) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 2); - if (upcflag != -1 && collision.flags() != upcflag) + if (upcflag != -1 && collision.flags() != upcflag) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 3); - float fitCut[5] = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; + std::array fitCut = {fv0Cut, ft0aCut, ft0cCut, fddaCut, fddcCut}; std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; int truegapSide = sgSelector.trueGap(collision, fitCut[0], fitCut[1], fitCut[2], zdcCut); registry.get(HIST("Reco/Stat"))->Fill(4.0, 1.); @@ -1496,60 +1598,74 @@ struct SginclusivePhiKstarSD { } } - if (truegapSide != gapsideMC) + if (truegapSide != gapsideMC) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 4); - if (std::abs(collision.posZ()) > vzCut) + if (std::abs(collision.posZ()) > vzCut) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 5); - if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) + if (useOccCut && (std::abs(collision.occupancyInTime()) > confgOccCut)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 6); - if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) + if (useHadronicRateCut && (std::abs(collision.hadronicRate()) > confgHadronicRateMax || std::abs(collision.hadronicRate()) < confgHadronicRateMin)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 7); - if (useTrs && collision.trs() != 1) + if (useTrs && collision.trs() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 8); - if (useTrofs && collision.trofs() != 1) + if (useTrofs && collision.trofs() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 9); - if (useHmpr && collision.hmpr() != 1) + if (useHmpr && collision.hmpr() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 10); - if (useTfb && collision.tfb() != 1) + if (useTfb && collision.tfb() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 11); - if (useItsrofb && collision.itsROFb() != 1) + if (useItsrofb && collision.itsROFb() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 12); - if (useSbp && collision.sbp() != 1) + if (useSbp && collision.sbp() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 13); - if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) + if (useZvtxftovpv && collision.zVtxFT0vPV() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 14); - if (useVtxItsTpc && collision.vtxITSTPC() != 1) + if (useVtxItsTpc && collision.vtxITSTPC() != 1) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 15); - if (!isGoodRCTflag(collision)) + if (!isGoodRCTflag(collision)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 16); - if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) + if (usenumContrib && (collision.numContrib() < mintrack || collision.numContrib() > maxtrack)) { return; + } registry.fill(HIST("Reco/hEventCutFlowMC"), 17); if (qaMC) { @@ -1586,8 +1702,9 @@ struct SginclusivePhiKstarSD { } } for (const auto& tr1 : tracks) { - if (!tr1.has_udMcParticle()) + if (!tr1.has_udMcParticle()) { continue; + } auto mcPart1 = tr1.udMcParticle(); if (qaMC) { @@ -1601,8 +1718,9 @@ struct SginclusivePhiKstarSD { registry.get(HIST("Reco/tr_tpcnclfind_1"))->Fill(tr1.tpcNClsFindable(), 1.); registry.get(HIST("Reco/tr_itsChi2NCl_1"))->Fill(tr1.itsChi2NCl(), 1.); - if (!trackselector(tr1, parameters)) + if (trackselector(tr1, parameters) == 0) { continue; + } registry.get(HIST("Reco/tr_dcaz_2"))->Fill(tr1.dcaZ(), 1.); registry.get(HIST("Reco/tr_dcaxy_2"))->Fill(tr1.dcaXY(), 1.); @@ -1655,29 +1773,36 @@ struct SginclusivePhiKstarSD { vr0.SetCoordinates(tr1.px(), tr1.py(), tr1.pz(), o2::constants::physics::MassKaonCharged); registry.get(HIST("Reco/trpt"))->Fill(vr0.Pt(), 1.); registry.get(HIST("Reco/treta_k"))->Fill(vr0.Eta(), 1.); - if (!selectionPIDKaon1(tr1)) + if (!selectionPIDKaon1(tr1)) { continue; + } registry.get(HIST("Reco/trpt_k"))->Fill(vr0.Pt(), 1.); int t2 = 0; for (const auto& tr2 : tracks) { - if (!tr2.has_udMcParticle()) + if (!tr2.has_udMcParticle()) { continue; - if (!trackselector(tr2, parameters)) + } + if (trackselector(tr2, parameters) == 0) { continue; + } t2++; if (t2 > t1) { - if (!selectionPIDKaon1(tr2)) + if (!selectionPIDKaon1(tr2)) { continue; + } vr1.SetCoordinates(tr2.px(), tr2.py(), tr2.pz(), o2::constants::physics::MassKaonCharged); auto mcPart2 = tr2.udMcParticle(); // if (std::abs(mcPart2.globalIndex() - mcPart1.globalIndex()) != 1) // continue; - if (std::abs(mcPart1.pdgCode()) != kKPlus || std::abs(mcPart2.pdgCode()) != kKPlus) + if (std::abs(mcPart1.pdgCode()) != kKPlus || std::abs(mcPart2.pdgCode()) != kKPlus) { continue; - if (mcPart1.pdgCode() == mcPart2.pdgCode()) + } + if (mcPart1.pdgCode() == mcPart2.pdgCode()) { continue; - if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) + } + if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) { continue; + } bool flag = false; bool flag1 = false; int gIndex1 = 0; @@ -1689,8 +1814,9 @@ struct SginclusivePhiKstarSD { continue; } vphi.SetCoordinates(mother.px(), mother.py(), mother.pz(), o2::constants::physics::MassPhi); - if (std::abs(vphi.Rapidity()) > 0.5) + if (std::abs(vphi.Rapidity()) > 0.5) { continue; + } flag = true; gIndex1 = mother.globalIndex(); } @@ -1698,8 +1824,9 @@ struct SginclusivePhiKstarSD { for (const auto& mother1 : mcPart1.mothers_as()) { if (std::abs(mother1.pdgCode()) == o2::constants::physics::Pdg::kPhi) { vphi.SetCoordinates(mother1.px(), mother1.py(), mother1.pz(), o2::constants::physics::MassPhi); - if (std::abs(vphi.Rapidity()) > 0.5) + if (std::abs(vphi.Rapidity()) > 0.5) { continue; + } flag1 = true; gIndex2 = mother1.globalIndex(); } @@ -1729,23 +1856,28 @@ struct SginclusivePhiKstarSD { } // KStar for (const auto& [tr1, tr2] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { - if (!tr1.has_udMcParticle() || !tr2.has_udMcParticle()) + if (!tr1.has_udMcParticle() || !tr2.has_udMcParticle()) { continue; - if (!selectionPIDPion1(tr1) || !selectionPIDKaon1(tr2)) + } + if (!selectionPIDPion1(tr1) || !selectionPIDKaon1(tr2)) { continue; + } // if (tr1.index() == tr2.index()) // continue; // We need to run (0,1), (1,0) pairs as well. but same id pairs are not needed. auto mcPart1 = tr1.udMcParticle(); auto mcPart2 = tr2.udMcParticle(); - if (std::abs(mcPart1.pdgCode()) != kPiPlus || std::abs(mcPart2.pdgCode()) != kKPlus) + if (std::abs(mcPart1.pdgCode()) != kPiPlus || std::abs(mcPart2.pdgCode()) != kKPlus) { continue; - if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) + } + if (!mcPart1.isPhysicalPrimary() || !mcPart2.isPhysicalPrimary()) { continue; + } // if (std::abs(mcPart2.globalIndex() - mcPart1.globalIndex()) != 1) // continue; - if (tr1.sign() * tr2.sign() > 0) + if (tr1.sign() * tr2.sign() > 0) { continue; + } vr0.SetCoordinates(tr1.px(), tr1.py(), tr1.pz(), o2::constants::physics::MassPionCharged); vr1.SetCoordinates(tr2.px(), tr2.py(), tr2.pz(), o2::constants::physics::MassKaonCharged); @@ -1753,10 +1885,10 @@ struct SginclusivePhiKstarSD { vr1g.SetCoordinates(mcPart2.px(), mcPart2.py(), mcPart2.pz(), o2::constants::physics::MassKaonCharged); vr01g = vr0g + vr1g; vr01 = vr0 + vr1; - if (!trackselector(tr1, parameters) || !trackselector(tr2, parameters)) { + if ((trackselector(tr1, parameters) == 0) || (trackselector(tr2, parameters) == 0)) { registry.get(HIST("Reco/selM_k"))->Fill(vr01.M(), 1.); } - if (trackselector(tr1, parameters) && trackselector(tr2, parameters)) { + if ((trackselector(tr1, parameters) != 0) && (trackselector(tr2, parameters) != 0)) { bool flag = false; bool flag1 = false; int gIndex1 = 0; @@ -1766,8 +1898,9 @@ struct SginclusivePhiKstarSD { if (std::abs(mother.pdgCode()) == o2::constants::physics::Pdg::kK0Star892) { vkstar.SetCoordinates(mother.px(), mother.py(), mother.pz(), o2::constants::physics::MassK0Star892); // registry.get(HIST("MC/accMPtRap_kstar_T"))->Fill(vkstar.M(), vkstar.Pt(), vkstar.Rapidity(), 1.); - if (std::abs(vkstar.Rapidity()) > 0.5) + if (std::abs(vkstar.Rapidity()) > 0.5) { continue; + } flag = true; gIndex1 = mother.globalIndex(); } @@ -1775,8 +1908,9 @@ struct SginclusivePhiKstarSD { for (const auto& mother1 : mcPart2.mothers_as()) { if (std::abs(mother1.pdgCode()) == o2::constants::physics::Pdg::kK0Star892) { vkstar.SetCoordinates(mother1.px(), mother1.py(), mother1.pz(), o2::constants::physics::MassK0Star892); - if (std::abs(vkstar.Rapidity()) > 0.5) + if (std::abs(vkstar.Rapidity()) > 0.5) { continue; + } flag1 = true; gIndex2 = mother1.globalIndex(); } @@ -1815,13 +1949,503 @@ struct SginclusivePhiKstarSD { auto mcPart = track.udMcParticle(); auto pPart = std::sqrt(mcPart.px() * mcPart.px() + mcPart.py() * mcPart.py() + mcPart.pz() * mcPart.pz()); auto pDiff = pTrack - pPart; - registry.get(HIST("Reco/pDiff"))->Fill(pDiff, track.isPVContributor(), 1.); + registry.get(HIST("Reco/pDiff"))->Fill(pDiff, static_cast(track.isPVContributor()), 1.); } else { registry.get(HIST("Reco/pDiff"))->Fill(-5.9, -1, 1.); } } } PROCESS_SWITCH(SginclusivePhiKstarSD, processReco, "Process reconstructed data", true); + + void processSEMC(CC const& collision, TCs const& tracks, aod::UDMcCollisions const& /*mccollisions*/, aod::UDMcParticles const& /* McParts */) + { + + if (!collision.has_udMcCollision()) { + return; + } + + auto mccoll = collision.udMcCollision(); + if (mccoll.generatorsID() != generatedId) { + return; + } + + if (qa) { + rQA.fill(HIST("hOcc_before"), collision.occupancyInTime()); + } + + ROOT::Math::PxPyPzMVector v0; + ROOT::Math::PxPyPzMVector v1; + ROOT::Math::PxPyPzMVector v01; + + ROOT::Math::PxPyPzMVector phiv; + ROOT::Math::PxPyPzMVector phiv1; + + std::vector onlyPionTracksp; + std::vector rawPionTracksp; + + std::vector onlyPionTrackspm; + std::vector rawPionTrackspm; + + std::vector onlyPionTracksn; + std::vector rawPionTracksn; + + std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; + + auto [eventSelected, gapSide] = selectionEvent(collision, true); + if (!eventSelected) { + return; + } + + int mult = collision.numContrib(); + + if (gapSide == SingleGapA) { + registry.fill(HIST("gap_mult0"), mult); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("gap_mult1"), mult); + } + if (gapSide == DoubleGap) { + registry.fill(HIST("gap_mult2"), mult); + } + + if (qa) { + rQA.fill(HIST("hOcc_after"), collision.occupancyInTime()); + } + + int mult0 = 0; + int mult1 = 0; + int mult2 = 0; + if (qa) { + rQA.fill(HIST("hVertexX"), collision.posX()); + rQA.fill(HIST("hVertexY"), collision.posY()); + rQA.fill(HIST("hVertexZ"), collision.posZ()); + } + + for (const auto& track1 : tracks) { + if (qa) { + rQA.fill(HIST("hDcaxy_all_before"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_all_before"), track1.dcaZ()); + } + + if (trackselector(track1, parameters) == 0) { + continue; + } + + v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassPionCharged); + + if (qa) { + rQA.fill(HIST("hDcaxy_all_after"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_all_after"), track1.dcaZ()); + rQA.fill(HIST("hEta_all_after"), v0.Eta()); + } + + if (selectionPIDPion1(track1)) { + onlyPionTrackspm.push_back(v0); + rawPionTrackspm.push_back(track1); + if (track1.sign() == 1) { + onlyPionTracksp.push_back(v0); + rawPionTracksp.push_back(track1); + } + if (track1.sign() == -1) { + onlyPionTracksn.push_back(v0); + rawPionTracksn.push_back(track1); + } + } + if (gapSide == SingleGapA) { + mult0++; + } + if (gapSide == SingleGapC) { + mult1++; + } + if (gapSide == DoubleGap) { + mult2++; + } + + if (qa) { + rQA.fill(HIST("tpc_dedx"), v0.P(), track1.tpcSignal()); + rQA.fill(HIST("tof_beta"), v0.P(), track1.beta()); + rQA.fill(HIST("tof_nsigma_kaon_all"), v0.Pt(), track1.tofNSigmaKa()); + rQA.fill(HIST("tof_nsigma_pion_all"), v0.Pt(), track1.tofNSigmaPi()); + rQA.fill(HIST("tpc_nsigma_kaon_all"), v0.Pt(), track1.tpcNSigmaKa()); + rQA.fill(HIST("tpc_nsigma_pion_all"), v0.Pt(), track1.tpcNSigmaPi()); + + if (selectionPIDKaon1(track1)) { + rQA.fill(HIST("tpc_dedx_kaon"), v0.P(), track1.tpcSignal()); + rQA.fill(HIST("tof_beta_kaon"), v0.P(), track1.beta()); + rQA.fill(HIST("tpc_nsigma_kaon"), v0.Pt(), track1.tpcNSigmaKa()); + rQA.fill(HIST("tof_nsigma_kaon"), v0.Pt(), track1.tofNSigmaKa()); + rQA.fill(HIST("tpc_tof_nsigma_kaon"), track1.tpcNSigmaKa(), track1.tofNSigmaKa()); + rQA.fill(HIST("hEta_ka"), v0.Eta()); + rQA.fill(HIST("hRap_ka"), v0.Rapidity()); + rQA.fill(HIST("hDcaxy_ka"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_ka"), track1.dcaZ()); + } + + if (selectionPIDPion1(track1)) { + rQA.fill(HIST("tpc_dedx_pion"), v0.P(), track1.tpcSignal()); + rQA.fill(HIST("tof_beta_pion"), v0.P(), track1.beta()); + rQA.fill(HIST("tpc_nsigma_pion"), v0.Pt(), track1.tpcNSigmaPi()); + rQA.fill(HIST("tof_nsigma_pion"), v0.Pt(), track1.tofNSigmaPi()); + rQA.fill(HIST("tpc_tof_nsigma_pion"), track1.tpcNSigmaPi(), track1.tofNSigmaPi()); + rQA.fill(HIST("hEta_pi"), v0.Eta()); + rQA.fill(HIST("hRap_pi"), v0.Rapidity()); + rQA.fill(HIST("hDcaxy_pi"), track1.dcaXY()); + rQA.fill(HIST("hDcaz_pi"), track1.dcaZ()); + } + } + } + if (gapSide == SingleGapA) { + if (useMultCut && (mult0 < mintrack || mult0 > maxtrack)) { + return; + } + registry.fill(HIST("mult_0"), mult0); + } + if (gapSide == SingleGapC) { + if (useMultCut && (mult1 < mintrack || mult1 > maxtrack)) { + return; + } + registry.fill(HIST("mult_1"), mult1); + } + if (qa) { + if (gapSide == SingleGapA) { + rQA.fill(HIST("V0A_0"), collision.totalFV0AmplitudeA()); + rQA.fill(HIST("FT0A_0"), collision.totalFT0AmplitudeA()); + rQA.fill(HIST("FT0C_0"), collision.totalFT0AmplitudeC()); + rQA.fill(HIST("ZDC_A_0"), collision.energyCommonZNA()); + rQA.fill(HIST("ZDC_C_0"), collision.energyCommonZNC()); + } + if (gapSide == SingleGapC) { + rQA.fill(HIST("V0A_1"), collision.totalFV0AmplitudeA()); + rQA.fill(HIST("FT0A_1"), collision.totalFT0AmplitudeA()); + rQA.fill(HIST("FT0C_1"), collision.totalFT0AmplitudeC()); + rQA.fill(HIST("ZDC_A_1"), collision.energyCommonZNA()); + rQA.fill(HIST("ZDC_C_1"), collision.energyCommonZNC()); + } + } + for (const auto& [t0, t1] : combinations(tracks, tracks)) { + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { + continue; + } + + if (phi && selectionPIDKaon1(t0) && selectionPIDKaon1(t1)) { + // Apply kaon hypothesis and create pairs + v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + // Opposite sign pairs + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_KK_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_KK_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_KK_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + // samesignpair + if (t0.sign() == t1.sign()) { + if (gapSide == SingleGapA) { + if (t0.sign() < 0) { + registry.fill(HIST("os_KK_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_KK_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (gapSide == SingleGapC) { + if (t0.sign() < 0) { + registry.fill(HIST("os_KK_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_KK_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_KK_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + + if (fillRotation) { + for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { + auto anglestart = confMinRot; + auto angleend = confMaxRot; + auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); + auto rotangle = anglestart + nrotbkg * anglestep; + registry.fill(HIST("hRotation"), rotangle); + + auto rotkaonPx = t0.px() * std::cos(rotangle) - t0.py() * std::sin(rotangle); + auto rotkaonPy = t0.px() * std::sin(rotangle) + t0.py() * std::cos(rotangle); + + v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_KK_rot_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_KK_rot_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_KK_rot_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + } + } + for (const auto& [t0, t1] : combinations(o2::soa::CombinationsFullIndexPolicy(tracks, tracks))) { + if ((trackselector(t0, parameters) == 0) || (trackselector(t1, parameters) == 0)) { + continue; + } + if (t0.globalIndex() == t1.globalIndex()) { + continue; + } + if (rho && selectionPIDProton(t0, useTof, nsigmaTpcCut, nsigmaTofCut) && selectionPIDKaon1(t1)) { + v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassProton); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + // Opposite sign pairs + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pp_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pp_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pp_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } // same sign pair + if (t0.sign() == t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pp_ls_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pp_ls_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pp_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + if (kstar && selectionPIDKaon1(t0) && selectionPIDPion1(t1)) { + if (kaoncut && t0.tpcNSigmaPi() < pionNsigmaCut) { + continue; + } + v0.SetCoordinates(t0.px(), t0.py(), t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + // Opposite sign pairs + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pk_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pk_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pk_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } // same sign pair + if (t0.sign() == t1.sign()) { + if (gapSide == SingleGapA) { + if (t0.sign() < 0) { + registry.fill(HIST("os_pk_lsMM_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_pk_lsPP_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (gapSide == SingleGapC) { + if (t0.sign() < 0) { + registry.fill(HIST("os_pk_lsMM_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } else { + registry.fill(HIST("os_pk_lsPP_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pk_ls_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + if (fillRotation) { + for (int nrotbkg = 0; nrotbkg < nBkgRotations; nrotbkg++) { + auto anglestart = confMinRot; + auto angleend = confMaxRot; + auto anglestep = (angleend - anglestart) / (1.0 * (nBkgRotations - 1)); + auto rotangle = anglestart + nrotbkg * anglestep; + registry.fill(HIST("hRotation"), rotangle); + + auto rotkaonPx = t0.px() * std::cos(rotangle) - t0.py() * std::sin(rotangle); + auto rotkaonPy = t0.px() * std::sin(rotangle) + t0.py() * std::cos(rotangle); + + v0.SetCoordinates(rotkaonPx, rotkaonPy, t0.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(t1.px(), t1.py(), t1.pz(), o2::constants::physics::MassPionCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + + if (t0.sign() != t1.sign()) { + if (gapSide == SingleGapA) { + registry.fill(HIST("os_pk_rot_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide == SingleGapC) { + registry.fill(HIST("os_pk_rot_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (exclusive && gapSide == DoubleGap && mult2 == numTwoTracks) { + registry.fill(HIST("os_pk_rot_pT_2"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + } + } + if (fourpion) { + if (gapSide == DoubleGap && mult2 == numFourTracks) { + ROOT::Math::PxPyPzMVector pair1, pair2, pair3, pair4; + if (static_cast(onlyPionTracksp.size()) == numTwoTracks && static_cast(onlyPionTracksn.size()) == numTwoTracks) { + const ROOT::Math::PxPyPzMVector& k1 = onlyPionTracksp.at(0); + const ROOT::Math::PxPyPzMVector& k2 = onlyPionTracksp.at(1); + const ROOT::Math::PxPyPzMVector& k3 = onlyPionTracksn.at(0); + const ROOT::Math::PxPyPzMVector& k4 = onlyPionTracksn.at(1); + phiv = k1 + k2 + k3 + k4; + pair1 = k1 + k3; + pair2 = k2 + k4; + pair3 = k1 + k4; + pair4 = k2 + k3; + registry.fill(HIST("os_pppp_pT_2"), phiv.M(), phiv.Pt(), phiv.Rapidity()); + registry.fill(HIST("os_pp_vs_pp_mass"), pair1.M(), pair2.M()); + registry.fill(HIST("os_pp_vs_pp_pt"), pair1.Pt(), pair2.Pt()); + auto costhetaPair = cosThetaCollinsSoperFrame(pair1, pair2, phiv); + auto phiPair = 1. * o2::constants::math::PI + phiCollinsSoperFrame(pair1, pair2, phiv); + registry.fill(HIST("phi_dis"), phiPair); + registry.fill(HIST("costheta_dis"), costhetaPair); + registry.fill(HIST("costheta_vs_phi"), costhetaPair, phiPair); + registry.fill(HIST("os_pp_vs_pp_mass1"), pair3.M(), pair4.M()); + registry.fill(HIST("os_pp_vs_pp_pt1"), pair3.Pt(), pair4.Pt()); + auto costhetaPair1 = cosThetaCollinsSoperFrame(pair3, pair4, phiv); + auto phiPair1 = 1. * o2::constants::math::PI + phiCollinsSoperFrame(pair3, pair4, phiv); + registry.fill(HIST("phi_dis1"), phiPair1); + registry.fill(HIST("costheta_dis1"), costhetaPair1); + registry.fill(HIST("costheta_vs_phi1"), costhetaPair1, phiPair1); + } + if (static_cast(onlyPionTracksp.size()) != numTwoTracks && static_cast(onlyPionTracksn.size()) != numTwoTracks) { + if (static_cast(onlyPionTracksp.size()) + static_cast(onlyPionTracksn.size()) != numFourTracks) { + return; + } + const ROOT::Math::PxPyPzMVector& l1 = onlyPionTrackspm.at(0); + const ROOT::Math::PxPyPzMVector& l2 = onlyPionTrackspm.at(1); + const ROOT::Math::PxPyPzMVector& l3 = onlyPionTrackspm.at(2); + const ROOT::Math::PxPyPzMVector& l4 = onlyPionTrackspm.at(3); + phiv1 = l1 + l2 + l3 + l4; + registry.fill(HIST("os_pppp_pT_2_ls"), phiv1.M(), phiv1.Pt(), phiv1.Rapidity()); + } + } + } + } + PROCESS_SWITCH(SginclusivePhiKstarSD, processSEMC, "Process unlike event in MC", false); + + void mixprocessMC(CCs const& collisions, TCs const& /* tracks */, aod::UDMcCollisions const& /*mccollisions*/, aod::UDMcParticles const& /* McParts */) + { + ROOT::Math::PxPyPzMVector v0; + ROOT::Math::PxPyPzMVector v1; + ROOT::Math::PxPyPzMVector v01; + + std::vector parameters = {pvCut, dcazCut, dcaxyCut, tpcChi2Cut, tpcNClsFindableCut, itsChi2Cut, etaCut, ptCut}; + + BinningTypeVertexContributor binningOnPositions{{axisVertex, axisMultiplicityClass}}; + + for (auto const& [collision1, collision2] : o2::soa::selfCombinations(binningOnPositions, cfgNoMixedEvents, -1, collisions, collisions)) { + + if (!collision1.has_udMcCollision() || !collision2.has_udMcCollision()) { + return; + } + + auto mccoll1 = collision1.udMcCollision(); + auto mccoll2 = collision2.udMcCollision(); + if (mccoll1.generatorsID() != generatedId || mccoll2.generatorsID() != generatedId) { + return; + } + + auto [eventSelected1, gapSide1] = selectionEvent(collision1, false); + auto [eventSelected2, gapSide2] = selectionEvent(collision2, false); + if (!eventSelected1 || !eventSelected2) { + continue; + } + + if (gapSide1 != gapSide2) { + continue; + } + + auto posThisColl = posTracks->sliceByCached(aod::udtrack::udCollisionId, collision1.globalIndex(), cache); + auto negThisColl = negTracks->sliceByCached(aod::udtrack::udCollisionId, collision2.globalIndex(), cache); + // for (auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { + for (const auto& [track1, track2] : o2::soa::combinations(posThisColl, negThisColl)) { + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { + continue; + } + if (phi && selectionPIDKaon1(track1) && selectionPIDKaon1(track2)) { + v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassKaonCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + // Opposite sign pairs + if (track1.sign() != track2.sign()) { + if (gapSide1 == SingleGapA) { + registry.fill(HIST("os_KK_mix_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide1 == SingleGapC) { + registry.fill(HIST("os_KK_mix_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + for (const auto& [track1, track2] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(posThisColl, negThisColl))) { + if ((trackselector(track1, parameters) == 0) || (trackselector(track2, parameters) == 0)) { + continue; + } + if (track1.globalIndex() == track2.globalIndex()) { + continue; + } + if (kstar && selectionPIDKaon1(track1) && selectionPIDPion1(track2)) { + v0.SetCoordinates(track1.px(), track1.py(), track1.pz(), o2::constants::physics::MassKaonCharged); + v1.SetCoordinates(track2.px(), track2.py(), track2.pz(), o2::constants::physics::MassPionCharged); + v01 = v0 + v1; + if (rapiditycut && std::abs(v01.Rapidity()) > rapiditycutvalue) { + continue; + } + // Opposite sign pairs + if (track1.sign() != track2.sign()) { + if (gapSide1 == SingleGapA) { + registry.fill(HIST("os_pk_mix_pT_0"), v01.M(), v01.Rapidity(), v01.Pt()); + } + if (gapSide1 == SingleGapC) { + registry.fill(HIST("os_pk_mix_pT_1"), v01.M(), v01.Rapidity(), v01.Pt()); + } + } + } + } + } + } + PROCESS_SWITCH(SginclusivePhiKstarSD, mixprocessMC, "Process Mixed event in MC", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) diff --git a/PWGUD/Tasks/upcCandidateAnalyzer.cxx b/PWGUD/Tasks/upcCandidateAnalyzer.cxx index dcf23797803..50a80cc11dd 100644 --- a/PWGUD/Tasks/upcCandidateAnalyzer.cxx +++ b/PWGUD/Tasks/upcCandidateAnalyzer.cxx @@ -608,7 +608,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = fwdTracks.iteratorAt(trId2); - processCandidate<0>(cand, tr1, tr2, &mcParticles, (o2::aod::UDMcTrackLabels*)nullptr, &mcFwdTrackLabels); + processCandidate<0>(cand, tr1, tr2, &mcParticles, nullptr, &mcFwdTrackLabels); } } @@ -629,7 +629,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = fwdTracks.iteratorAt(trId2); - processCandidate<0>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<0>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } @@ -687,7 +687,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = fwdTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<1>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<1>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } @@ -713,7 +713,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = barTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<2>(cand, tr1, tr2, &mcParticles, &mcTrackLabels, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<2>(cand, tr1, tr2, &mcParticles, &mcTrackLabels, nullptr); } } @@ -734,7 +734,7 @@ struct UpcCandAnalyzer { const auto& cand = eventCandidates.iteratorAt(candID); const auto& tr1 = barTracks.iteratorAt(trId1); const auto& tr2 = barTracks.iteratorAt(trId2); - processCandidate<2>(cand, tr1, tr2, (o2::aod::UDMcParticles*)nullptr, (o2::aod::UDMcTrackLabels*)nullptr, (o2::aod::UDMcFwdTrackLabels*)nullptr); + processCandidate<2>(cand, tr1, tr2, nullptr, nullptr, nullptr); } } diff --git a/PWGUD/Tasks/upcCandidateProducerQa.cpp b/PWGUD/Tasks/upcCandidateProducerQa.cxx similarity index 95% rename from PWGUD/Tasks/upcCandidateProducerQa.cpp rename to PWGUD/Tasks/upcCandidateProducerQa.cxx index 29accce2fe4..19e1deb1244 100644 --- a/PWGUD/Tasks/upcCandidateProducerQa.cpp +++ b/PWGUD/Tasks/upcCandidateProducerQa.cxx @@ -9,15 +9,20 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "Framework/runDataProcessing.h" -#include "Framework/AnalysisTask.h" -#include "Framework/AnalysisDataModel.h" -#include "Common/CCDB/EventSelectionParams.h" -#include "Common/DataModel/EventSelection.h" -#include "CommonConstants/LHCConstants.h" -#include "PWGUD/Core/UPCCutparHolder.h" -#include "PWGUD/Core/UPCHelpers.h" -#include "PWGUD/DataModel/UDTables.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include using namespace o2::framework; using namespace o2::framework::expressions; diff --git a/PWGUD/Tasks/upcEventITSROFcounter.cxx b/PWGUD/Tasks/upcEventItsRofCounter.cxx similarity index 94% rename from PWGUD/Tasks/upcEventITSROFcounter.cxx rename to PWGUD/Tasks/upcEventItsRofCounter.cxx index 22fb272efc9..6f13a3fc111 100644 --- a/PWGUD/Tasks/upcEventITSROFcounter.cxx +++ b/PWGUD/Tasks/upcEventItsRofCounter.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file upcEventITSROFcounter.cxx +/// \file upcEventItsRofCounter.cxx /// \brief Personal task to analyze tau events from UPC collisions /// /// \author Roman Lavicka , Austrian Academy of Sciences & SMI @@ -51,12 +51,15 @@ using BCsWithRun3Matchings = soa::Join; using FullSGUDCollision = soa::Join::iterator; -struct UpcEventITSROFcounter { +struct UpcEventItsRofCounter { Service ccdb; SGSelector sgSelector; HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + static constexpr int NCollFillCount = 12; // upper bound of collision-count loop filling the per-ROF histograms (last value goes to overflow of 11-bin histos) + static constexpr int DoubleGapSide = 2; // SGSelector convention: 0=A-gap, 1=C-gap, 2=double-gap + Configurable nTracksForUPCevent{"nTracksForUPCevent", 16, {"Maximum of tracks defining a UPC collision"}}; Configurable useTrueGap{"useTrueGap", true, {"Calculate gapSide for a given FV0/FT0/ZDC thresholds"}}; @@ -158,7 +161,7 @@ struct UpcEventITSROFcounter { arrUPCcolls[nUpcCollsInROF]++; } // end loop over ITSROFs - for (int ncol = 0; ncol < 12; ncol++) { + for (int ncol = 0; ncol < NCollFillCount; ncol++) { histos.get(HIST("Events/hCountCollisionsInROFborderMatching"))->Fill(ncol, arrAllColls[ncol]); histos.get(HIST("Events/hCountUPCcollisionsInROFborderMatching"))->Fill(ncol, arrUPCcolls[ncol]); } @@ -198,7 +201,7 @@ struct UpcEventITSROFcounter { } if (coll.flags() == 0) { - if (gapSide == 2) { + if (gapSide == DoubleGapSide) { histos.get(HIST("Runs/hStdModeCollDG"))->Fill(coll.runNumber()); } else if (gapSide == 1) { histos.get(HIST("Runs/hStdModeCollSG1"))->Fill(coll.runNumber()); @@ -208,7 +211,7 @@ struct UpcEventITSROFcounter { histos.get(HIST("Runs/hStdModeCollNG"))->Fill(coll.runNumber()); } } else { - if (gapSide == 2) { + if (gapSide == DoubleGapSide) { histos.get(HIST("Runs/hUpcModeCollDG"))->Fill(coll.runNumber()); } else if (gapSide == 1) { histos.get(HIST("Runs/hUpcModeCollSG1"))->Fill(coll.runNumber()); @@ -220,12 +223,12 @@ struct UpcEventITSROFcounter { } } - PROCESS_SWITCH(UpcEventITSROFcounter, processCounterPerITSROF, "Counts number of collisions per ITSROF", false); - PROCESS_SWITCH(UpcEventITSROFcounter, processCounterPerRun, "Counts number of whatever per RUN", true); + PROCESS_SWITCH(UpcEventItsRofCounter, processCounterPerITSROF, "Counts number of collisions per ITSROF", false); + PROCESS_SWITCH(UpcEventItsRofCounter, processCounterPerRun, "Counts number of whatever per RUN", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGUD/Tasks/upcFwdJpsiRl.cxx b/PWGUD/Tasks/upcFwdJpsiRl.cxx new file mode 100644 index 00000000000..99b3e457ccf --- /dev/null +++ b/PWGUD/Tasks/upcFwdJpsiRl.cxx @@ -0,0 +1,573 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file UpcFwdJpsiRl.cxx +/// \brief UPC forward J/psi analysis with configurable track-type candidate selection +/// \author Roman Lavicka, roman.lavicka@cern.ch +/// \since 07.05.2026 +/// +/// Candidate types (configured via candidateType): +/// 0 = GlobalMuon-GlobalMuon: both tracks are GlobalMuonTrack (enum 0) or GlobalMuonTrackOtherMatch (enum 1) +/// 1 = Mixed: at least one GlobalMuonTrack (enum 0,1) and the other MuonStandaloneTrack (enum 3) +/// 2 = MuonStandalone-MuonStandalone: both tracks are MuonStandaloneTrack (enum 3) + +/// executable name o2-analysis-ud-upc-fwd-jpsi-rl + +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include + +// table for saving tree with info on data +namespace jpsirl +{ +// dimuon +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); +DECLARE_SOA_COLUMN(M, m, float); +DECLARE_SOA_COLUMN(Energy, energy, float); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Rap, rap, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(PhiAv, phiAv, float); +DECLARE_SOA_COLUMN(PhiCh, phiCh, float); +// tracks positive (p) and negative (n) +DECLARE_SOA_COLUMN(EnergyP, energyP, float); +DECLARE_SOA_COLUMN(Pxp, pxp, float); +DECLARE_SOA_COLUMN(Pyp, pyp, float); +DECLARE_SOA_COLUMN(Pzp, pzp, float); +DECLARE_SOA_COLUMN(Ptp, ptp, float); +DECLARE_SOA_COLUMN(Etap, etap, float); +DECLARE_SOA_COLUMN(Phip, phip, float); +DECLARE_SOA_COLUMN(TrackTypep, trackTypep, int); +DECLARE_SOA_COLUMN(EnergyN, energyN, float); +DECLARE_SOA_COLUMN(Pxn, pxn, float); +DECLARE_SOA_COLUMN(Pyn, pyn, float); +DECLARE_SOA_COLUMN(Pzn, pzn, float); +DECLARE_SOA_COLUMN(Ptn, ptn, float); +DECLARE_SOA_COLUMN(Etan, etan, float); +DECLARE_SOA_COLUMN(Phin, phin, float); +DECLARE_SOA_COLUMN(TrackTypen, trackTypen, int); +// zn +DECLARE_SOA_COLUMN(Tzna, tzna, float); +DECLARE_SOA_COLUMN(Ezna, ezna, float); +DECLARE_SOA_COLUMN(Tznc, tznc, float); +DECLARE_SOA_COLUMN(Eznc, eznc, float); +DECLARE_SOA_COLUMN(Nclass, nclass, int); +} // namespace jpsirl + +namespace o2::aod +{ +DECLARE_SOA_TABLE(JpsiRL, "AOD", "JPSI", + jpsirl::RunNumber, + jpsirl::M, jpsirl::Energy, jpsirl::Px, jpsirl::Py, jpsirl::Pz, jpsirl::Pt, jpsirl::Rap, jpsirl::Phi, + jpsirl::PhiAv, jpsirl::PhiCh, + jpsirl::EnergyP, jpsirl::Pxp, jpsirl::Pyp, jpsirl::Pzp, jpsirl::Ptp, jpsirl::Etap, jpsirl::Phip, jpsirl::TrackTypep, + jpsirl::EnergyN, jpsirl::Pxn, jpsirl::Pyn, jpsirl::Pzn, jpsirl::Ptn, jpsirl::Etan, jpsirl::Phin, jpsirl::TrackTypen, + jpsirl::Tzna, jpsirl::Ezna, jpsirl::Tznc, jpsirl::Eznc, jpsirl::Nclass); +} // namespace o2::aod + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace o2::aod::fwdtrack; + +// constants used in the track selection +const float kRAbsMin = 17.6; +const float kRAbsMid = 26.5; +const float kRAbsMax = 89.5; +const float kPDca1 = 200.; +const float kPDca2 = 200.; +const float kEtaMin = -4.0; +const float kEtaMax = -2.5; +const float kPtMin = 0.; + +const float kMaxAmpV0A = 100.; +const float kMaxZDCTime = 2.; +const float kMaxZDCTimeHisto = 10.; +const float kInvalidFloat = -999.; +const int kMaxRelBCsV0A = 1; +const int kNMuons = 2; + +struct UpcFwdJpsiRl { + + using CandidatesFwd = soa::Join; + using ForwardTracks = soa::Join; + + Produces dimuSel; + + // defining histograms using histogram registry + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry reg0n0n{"reg0n0n", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry regXn0n{"regXn0n", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry regXnXn{"regXnXn", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // CONFIGURABLES + static constexpr double Pi = o2::constants::math::PI; + // candidate type selection + // 0 = GlobalMuon-GlobalMuon (both tracks enum 0 or 1) + // 1 = Mixed (one GlobalMuon enum 0,1 + one MuonStandalone enum 3) + // 2 = MuonStandalone-MuonStandalone (both tracks enum 3) + Configurable candidateType{"candidateType", 0, "Candidate type: 0=GlobalMuon-GlobalMuon, 1=Mixed, 2=MuonStandalone-MuonStandalone"}; + // pT of muon pairs + Configurable nBinsPt{"nBinsPt", 250, "N bins in pT histo"}; + Configurable lowPt{"lowPt", 0., "lower limit in pT histo"}; + Configurable highPt{"highPt", 2, "upper limit in pT histo"}; + // mass of muon pairs + Configurable nBinsMass{"nBinsMass", 500, "N bins in mass histo"}; + Configurable lowMass{"lowMass", 0., "lower limit in mass histo"}; + Configurable highMass{"highMass", 10., "upper limit in mass histo"}; + // eta of muon pairs + Configurable nBinsEta{"nBinsEta", 600, "N bins in eta histo"}; + Configurable lowEta{"lowEta", -10., "lower limit in eta histo"}; + Configurable highEta{"highEta", -2., "upper limit in eta histo"}; + // rapidity of muon pairs + Configurable nBinsRapidity{"nBinsRapidity", 250, "N bins in rapidity histo"}; + Configurable lowRapidity{"lowRapidity", -4.5, "lower limit in rapidity histo"}; + Configurable highRapidity{"highRapidity", -2., "upper limit in rapidity histo"}; + // phi of muon pairs + Configurable nBinsPhi{"nBinsPhi", 600, "N bins in phi histo"}; + Configurable lowPhi{"lowPhi", -Pi, "lower limit in phi histo"}; + Configurable highPhi{"highPhi", Pi, "upper limit in phi histo"}; + // pT of single muons + Configurable nBinsPtSingle{"nBinsPtSingle", 500, "N bins in pT histo single muon"}; + Configurable lowPtSingle{"lowPtSingle", 0., "lower limit in pT histo single muon"}; + Configurable highPtSingle{"highPtSingle", 2., "upper limit in pT histo single muon"}; + // eta of single muons + Configurable nBinsEtaSingle{"nBinsEtaSingle", 250, "N bins in eta histo single muon"}; + Configurable lowEtaSingle{"lowEtaSingle", -4.5, "lower limit in eta histo single muon"}; + Configurable highEtaSingle{"highEtaSingle", -2., "upper limit in eta histo single muon"}; + // phi of single muons + Configurable nBinsPhiSingle{"nBinsPhiSingle", 600, "N bins in phi histo single muon"}; + Configurable lowPhiSingle{"lowPhiSingle", -Pi, "lower limit in phi histo single muon"}; + Configurable highPhiSingle{"highPhiSingle", Pi, "upper limit in phi histo single muon"}; + // ZDC + Configurable nBinsZDCen{"nBinsZDCen", 200, "N bins in ZN energy"}; + Configurable lowEnZN{"lowEnZN", -50., "lower limit in ZN energy histo"}; + Configurable highEnZN{"highEnZN", 250., "upper limit in ZN energy histo"}; + + void init(InitContext&) + { + // binning of pT axis for fit + std::vector ptFitBinning = { + 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, + 0.11, 0.12, 0.13, 0.14, 0.15, 0.175, 0.20, 0.25, 0.30, 0.40, 0.50, + 0.60, 0.70, 0.80, 0.90, 1.00, 1.20, 1.40, 1.60, 1.80, 2.00, 2.50, + 3.00, 3.50}; + + std::vector ptFitBinningHalfWidth = { + 0.00, 0.005, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05, + 0.055, 0.06, 0.065, 0.07, 0.075, 0.08, 0.085, 0.09, 0.095, 0.10, + 0.105, 0.11, 0.115, 0.12, 0.125, 0.13, 0.135, 0.14, 0.145, 0.15, + 0.1625, 0.175, 0.1875, 0.20, 0.225, 0.25, 0.275, 0.30, 0.35, 0.40, + 0.45, 0.50, 0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1.00, + 1.10, 1.20, 1.30, 1.40, 1.50, 1.60, 1.70, 1.80, 1.90, 2.00, 2.25, + 2.50, 2.75, 3.00, 3.25, 3.50}; + + // axes + const AxisSpec axisPt{nBinsPt, lowPt, highPt, "#it{p}_{T} GeV/#it{c}"}; + const AxisSpec axisPtFit = {ptFitBinning, "#it{p}_{T} (GeV/c)"}; + const AxisSpec axisPtFit2 = {ptFitBinningHalfWidth, "#it{p}_{T} (GeV/c)"}; + const AxisSpec axisMass{nBinsMass, lowMass, highMass, "m_{#mu#mu} GeV/#it{c}^{2}"}; + const AxisSpec axisEta{nBinsEta, lowEta, highEta, "#eta"}; + const AxisSpec axisRapidity{nBinsRapidity, lowRapidity, highRapidity, "Rapidity"}; + const AxisSpec axisPhi{nBinsPhi, lowPhi, highPhi, "#varphi"}; + const AxisSpec axisPtSingle{nBinsPtSingle, lowPtSingle, highPtSingle, "#it{p}_{T}_{ trk} GeV/#it{c}"}; + const AxisSpec axisTimeZN{200, -10, 10, "ZDC time (ns)"}; + const AxisSpec axisEnergyZNA{nBinsZDCen, lowEnZN, highEnZN, "ZNA energy (TeV)"}; + const AxisSpec axisEnergyZNC{nBinsZDCen, lowEnZN, highEnZN, "ZNC energy (TeV)"}; + const AxisSpec axisEtaSingle{nBinsEtaSingle, lowEtaSingle, highEtaSingle, "#eta_{trk}"}; + const AxisSpec axisPhiSingle{nBinsPhiSingle, lowPhiSingle, highPhiSingle, "#varphi_{trk}"}; + + // histos + registry.add("hMass", "Invariant mass of muon pairs;;#counts", kTH1D, {axisMass}); + registry.add("hPt", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPt}); + registry.add("hPtFit", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPtFit}); + registry.add("hPtFit2", "Transverse momentum of muon pairs;;#counts", kTH1D, {axisPtFit2}); + registry.add("hEta", "Pseudorapidty of muon pairs;;#counts", kTH1D, {axisEta}); + registry.add("hRapidity", "Rapidty of muon pairs;;#counts", kTH1D, {axisRapidity}); + registry.add("hPhi", "#varphi of muon pairs;;#counts", kTH1D, {axisPhi}); + registry.add("hCharge", "Charge;;;#counts", kTH1D, {{5, -2.5, 2.5}}); + registry.add("hContrib", "hContrib;;#counts", kTH1D, {{6, -0.5, 5.5}}); + registry.add("hEvSign", "Sum of the charges of all the tracks in each event;;#counts", kTH1D, {{5, -2.5, 2.5}}); + registry.add("hPtTrkPos", "Pt of positive muons;;#counts", kTH1D, {axisPtSingle}); + registry.add("hPtTrkNeg", "Pt of negative muons;;#counts", kTH1D, {axisPtSingle}); + registry.add("hEtaTrkPos", "#eta of positive muons;;#counts", kTH1D, {axisEtaSingle}); + registry.add("hEtaTrkNeg", "#eta of negative muons;;#counts", kTH1D, {axisEtaSingle}); + registry.add("hPhiTrkPos", "#varphi of positive muons;;#counts", kTH1D, {axisPhiSingle}); + registry.add("hPhiTrkNeg", "#varphi of negative muons;;#counts", kTH1D, {axisPhiSingle}); + registry.add("hSameSign", "hSameSign;;#counts", kTH1D, {{6, -0.5, 5.5}}); + registry.add("hPhiCharge", "#phi #it{charge}", kTH1D, {axisPhi}); + registry.add("hPhiAverage", "#phi #it{average}", kTH1D, {axisPhi}); + + // ZDC + registry.add("hTimeZNA", "ZNA Times;;#counts", kTH1D, {axisTimeZN}); + registry.add("hTimeZNC", "ZNC Times;;#counts", kTH1D, {axisTimeZN}); + registry.add("hEnergyZN", "ZNA vs ZNC energy", kTH2D, {axisEnergyZNA, axisEnergyZNC}); + + // neutron classes + reg0n0n.add("hMass", "Invariant mass of muon pairs - 0n0n;;#counts", kTH1D, {axisMass}); + reg0n0n.add("hPt", "Transverse momentum of muon pairs - 0n0n;;#counts", kTH1D, {axisPt}); + reg0n0n.add("hEta", "Pseudorapidty of muon pairs - 0n0n;;#counts", kTH1D, {axisEta}); + reg0n0n.add("hRapidity", "Rapidty of muon pairs - 0n0n;;#counts", kTH1D, {axisRapidity}); + reg0n0n.add("hPtFit", "Transverse momentum of muon pairs - 0n0n;;#counts", kTH1D, {axisPtFit}); + + regXn0n.add("hMass", "Invariant mass of muon pairs - Xn0n;;#counts", kTH1D, {axisMass}); + regXn0n.add("hPt", "Transverse momentum of muon pairs - Xn0n;;#counts", kTH1D, {axisPt}); + regXn0n.add("hEta", "Pseudorapidty of muon pairs - Xn0n;;#counts", kTH1D, {axisEta}); + regXn0n.add("hRapidity", "Rapidty of muon pairs - Xn0n;;#counts", kTH1D, {axisRapidity}); + regXn0n.add("hPtFit", "Transverse momentum of muon pairs - Xn0n;;#counts", kTH1D, {axisPtFit}); + + regXnXn.add("hMass", "Invariant mass of muon pairs - XnXn;;#counts", kTH1D, {axisMass}); + regXnXn.add("hPt", "Transverse momentum of muon pairs - XnXn;;#counts", kTH1D, {axisPt}); + regXnXn.add("hEta", "Pseudorapidty of muon pairs - XnXn;;#counts", kTH1D, {axisEta}); + regXnXn.add("hRapidity", "Rapidty of muon pairs - XnXn;;#counts", kTH1D, {axisRapidity}); + regXnXn.add("hPtFit", "Transverse momentum of muon pairs - XnXn;;#counts", kTH1D, {axisPtFit}); + } + + // FUNCTIONS + + using LorentzVec = ROOT::Math::PxPyPzMVector; + + // check if track is a GlobalMuonTrack (enum 0 or 1) + template + bool isGlobalMuon(const TTrack& track) + { + return track.trackType() == ForwardTrackTypeEnum::GlobalMuonTrack || + track.trackType() == ForwardTrackTypeEnum::GlobalMuonTrackOtherMatch; + } + + // check if track is a MuonStandaloneTrack (enum 3) + template + bool isMuonStandalone(const TTrack& track) + { + return track.trackType() == ForwardTrackTypeEnum::MuonStandaloneTrack; + } + + // check if the track pair matches the selected candidate type + template + bool passCandidateType(const TTrack& tr1, const TTrack& tr2) + { + bool isGlobal1 = isGlobalMuon(tr1); + bool isGlobal2 = isGlobalMuon(tr2); + bool isStandalone1 = isMuonStandalone(tr1); + bool isStandalone2 = isMuonStandalone(tr2); + + switch (static_cast(candidateType)) { + case 0: // both GlobalMuon + return isGlobal1 && isGlobal2; + case 1: // mixed: one GlobalMuon + one MuonStandalone + return (isGlobal1 && isStandalone2) || (isStandalone1 && isGlobal2); + case 2: // both MuonStandalone + return isStandalone1 && isStandalone2; + default: + return false; + } + } + + // collect tracks per candidate into a map + template + void collectCandIDs(std::unordered_map>& tracksPerCand, TTracks& tracks) + { + for (const auto& tr : tracks) { + int32_t candId = tr.udCollisionId(); + if (candId < 0) { + continue; + } + tracksPerCand[candId].push_back(tr.globalIndex()); + } + } + + // struct used to store the ZDC info + struct ZDCinfo { + float timeA; + float timeC; + float enA; + float enC; + }; + + // collect ZDC info per candidate + void collectCandZDCInfo(std::unordered_map& zdcPerCand, o2::aod::UDZdcsReduced const& ZDCs) + { + for (const auto& zdc : ZDCs) { + int32_t candId = zdc.udCollisionId(); + if (candId < 0) { + continue; + } + + zdcPerCand[candId].timeA = zdc.timeZNA(); + zdcPerCand[candId].timeC = zdc.timeZNC(); + zdcPerCand[candId].enA = zdc.energyCommonZNA(); + zdcPerCand[candId].enC = zdc.energyCommonZNC(); + + if (std::isinf(zdcPerCand[candId].timeA)) + zdcPerCand[candId].timeA = kInvalidFloat; + if (std::isinf(zdcPerCand[candId].timeC)) + zdcPerCand[candId].timeC = kInvalidFloat; + if (std::isinf(zdcPerCand[candId].enA)) + zdcPerCand[candId].enA = kInvalidFloat; + if (std::isinf(zdcPerCand[candId].enC)) + zdcPerCand[candId].enC = kInvalidFloat; + } + } + + // muon track selection + template + bool isMuonSelected(const TTrack& fwdTrack) + { + float rAbs = fwdTrack.rAtAbsorberEnd(); + float pDca = fwdTrack.pDca(); + auto mMu = o2::constants::physics::MassMuon; + LorentzVec p(fwdTrack.px(), fwdTrack.py(), fwdTrack.pz(), mMu); + float eta = p.Eta(); + float pt = p.Pt(); + float pDcaMax = rAbs < kRAbsMid ? kPDca1 : kPDca2; + + if (eta < kEtaMin || eta > kEtaMax) + return false; + if (pt < kPtMin) + return false; + if (rAbs < kRAbsMin || rAbs > kRAbsMax) + return false; + if (pDca > pDcaMax) + return false; + return true; + } + + // compute phi for azimuth anisotropy + void computePhiAnis(const LorentzVec& p1, const LorentzVec& p2, int sign1, float& phiAverage, float& phiCharge) + { + auto tSum = p1 + p2; + float halfUnity = 0.5; + decltype(tSum) tDiffCh, tDiffAv; + if (sign1 > 0) { + tDiffCh = p1 - p2; + if (gRandom->Rndm() > halfUnity) + tDiffAv = p1 - p2; + else + tDiffAv = p2 - p1; + } else { + tDiffCh = p2 - p1; + if (gRandom->Rndm() > halfUnity) + tDiffAv = p2 - p1; + else + tDiffAv = p1 - p2; + } + + phiAverage = ROOT::Math::VectorUtil::DeltaPhi(tSum, tDiffAv); + phiCharge = ROOT::Math::VectorUtil::DeltaPhi(tSum, tDiffCh); + } + + // process a single candidate + void processCand(CandidatesFwd::iterator const& cand, + ForwardTracks::iterator const& tr1, ForwardTracks::iterator const& tr2, + ZDCinfo const& zdc) + { + // candidate type selection + if (!passCandidateType(tr1, tr2)) + return; + + // V0 selection + const auto& ampsV0A = cand.amplitudesV0A(); + const auto& ampsRelBCsV0A = cand.ampRelBCsV0A(); + for (unsigned int i = 0; i < ampsV0A.size(); ++i) { + if (std::abs(ampsRelBCsV0A[i]) <= kMaxRelBCsV0A) { + if (ampsV0A[i] > kMaxAmpV0A) + return; + } + } + + // select opposite charge events only + if (cand.netCharge() != 0) { + registry.fill(HIST("hSameSign"), cand.numContrib()); + return; + } + + // MCH-MID match selection: both tracks must have MCH-MID match + int nMIDs = 0; + if (tr1.chi2MatchMCHMID() > 0) + nMIDs++; + if (tr2.chi2MatchMCHMID() > 0) + nMIDs++; + if (nMIDs != kNMuons) + return; + + // track selection + if (!isMuonSelected(tr1)) + return; + if (!isMuonSelected(tr2)) + return; + + // form Lorentz vectors + auto mMu = o2::constants::physics::MassMuon; + LorentzVec p1(tr1.px(), tr1.py(), tr1.pz(), mMu); + LorentzVec p2(tr2.px(), tr2.py(), tr2.pz(), mMu); + auto p = p1 + p2; + + // cut on pair kinematics + if (p.M() < lowMass || p.M() > highMass) + return; + if (p.Pt() < lowPt || p.Pt() > highPt) + return; + if (p.Rapidity() < lowRapidity || p.Rapidity() > highRapidity) + return; + + // compute phi for azimuth anisotropy + float phiAverage = 0; + float phiCharge = 0; + computePhiAnis(p1, p2, tr1.sign(), phiAverage, phiCharge); + + // zdc info + if (std::abs(zdc.timeA) < kMaxZDCTimeHisto) + registry.fill(HIST("hTimeZNA"), zdc.timeA); + if (std::abs(zdc.timeC) < kMaxZDCTimeHisto) + registry.fill(HIST("hTimeZNC"), zdc.timeC); + registry.fill(HIST("hEnergyZN"), zdc.enA, zdc.enC); + + // neutron classes + bool neutronA = std::abs(zdc.timeA) < kMaxZDCTime && !std::isinf(zdc.timeA); + bool neutronC = std::abs(zdc.timeC) < kMaxZDCTime && !std::isinf(zdc.timeC); + int znClass = -1; + + if (!neutronC && !neutronA) { + znClass = 1; + reg0n0n.fill(HIST("hMass"), p.M()); + reg0n0n.fill(HIST("hPt"), p.Pt()); + reg0n0n.fill(HIST("hPtFit"), p.Pt()); + reg0n0n.fill(HIST("hEta"), p.Eta()); + reg0n0n.fill(HIST("hRapidity"), p.Rapidity()); + } else if (neutronA ^ neutronC) { + znClass = neutronA ? 2 : 3; + regXn0n.fill(HIST("hMass"), p.M()); + regXn0n.fill(HIST("hPt"), p.Pt()); + regXn0n.fill(HIST("hPtFit"), p.Pt()); + regXn0n.fill(HIST("hEta"), p.Eta()); + regXn0n.fill(HIST("hRapidity"), p.Rapidity()); + } else if (neutronA && neutronC) { + znClass = 4; + regXnXn.fill(HIST("hMass"), p.M()); + regXnXn.fill(HIST("hPt"), p.Pt()); + regXnXn.fill(HIST("hPtFit"), p.Pt()); + regXnXn.fill(HIST("hEta"), p.Eta()); + regXnXn.fill(HIST("hRapidity"), p.Rapidity()); + } + + // fill inclusive histos + registry.fill(HIST("hContrib"), cand.numContrib()); + registry.fill(HIST("hPtTrkPos"), p1.Pt()); + registry.fill(HIST("hPtTrkNeg"), p2.Pt()); + registry.fill(HIST("hEtaTrkPos"), p1.Eta()); + registry.fill(HIST("hEtaTrkNeg"), p2.Eta()); + registry.fill(HIST("hPhiTrkPos"), p1.Phi()); + registry.fill(HIST("hPhiTrkNeg"), p2.Phi()); + registry.fill(HIST("hEvSign"), cand.netCharge()); + registry.fill(HIST("hMass"), p.M()); + registry.fill(HIST("hPt"), p.Pt()); + registry.fill(HIST("hPtFit"), p.Pt()); + registry.fill(HIST("hPtFit2"), p.Pt()); + registry.fill(HIST("hEta"), p.Eta()); + registry.fill(HIST("hRapidity"), p.Rapidity()); + registry.fill(HIST("hPhi"), p.Phi()); + registry.fill(HIST("hCharge"), tr1.sign()); + registry.fill(HIST("hCharge"), tr2.sign()); + registry.fill(HIST("hPhiAverage"), phiAverage); + registry.fill(HIST("hPhiCharge"), phiCharge); + + // store the event to save it into a tree + // order tracks so that positive is first + if (tr1.sign() > 0) { + dimuSel(cand.runNumber(), + p.M(), p.E(), p.Px(), p.Py(), p.Pz(), p.Pt(), p.Rapidity(), p.Phi(), + phiAverage, phiCharge, + p1.E(), p1.Px(), p1.Py(), p1.Pz(), p1.Pt(), p1.Eta(), p1.Phi(), static_cast(tr1.trackType()), + p2.E(), p2.Px(), p2.Py(), p2.Pz(), p2.Pt(), p2.Eta(), p2.Phi(), static_cast(tr2.trackType()), + zdc.timeA, zdc.enA, zdc.timeC, zdc.enC, znClass); + } else { + dimuSel(cand.runNumber(), + p.M(), p.E(), p.Px(), p.Py(), p.Pz(), p.Pt(), p.Rapidity(), p.Phi(), + phiAverage, phiCharge, + p2.E(), p2.Px(), p2.Py(), p2.Pz(), p2.Pt(), p2.Eta(), p2.Phi(), static_cast(tr2.trackType()), + p1.E(), p1.Px(), p1.Py(), p1.Pz(), p1.Pt(), p1.Eta(), p1.Phi(), static_cast(tr1.trackType()), + zdc.timeA, zdc.enA, zdc.timeC, zdc.enC, znClass); + } + } + + // PROCESS FUNCTION + void processData(CandidatesFwd const& eventCandidates, + o2::aod::UDZdcsReduced const& ZDCs, + ForwardTracks const& fwdTracks) + { + // map with the tracks + std::unordered_map> tracksPerCand; + collectCandIDs(tracksPerCand, fwdTracks); + + // map with the ZDC info + std::unordered_map zdcPerCand; + collectCandZDCInfo(zdcPerCand, ZDCs); + + // loop over the candidates and all track pairs + for (const auto& item : tracksPerCand) { + const auto& trkIds = item.second; + int32_t candID = item.first; + auto cand = eventCandidates.iteratorAt(candID); + + ZDCinfo zdc; + if (zdcPerCand.count(candID) != 0) { + zdc = zdcPerCand.at(candID); + } else { + zdc.timeA = kInvalidFloat; + zdc.timeC = kInvalidFloat; + zdc.enA = kInvalidFloat; + zdc.enC = kInvalidFloat; + } + + for (size_t i = 0; i < trkIds.size(); ++i) { + for (size_t j = i + 1; j < trkIds.size(); ++j) { + auto tr1 = fwdTracks.iteratorAt(trkIds[i]); + auto tr2 = fwdTracks.iteratorAt(trkIds[j]); + processCand(cand, tr1, tr2, zdc); + } + } + } + } + + PROCESS_SWITCH(UpcFwdJpsiRl, processData, "", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGUD/Tasks/upcJpsiCorr.cxx b/PWGUD/Tasks/upcJpsiCorr.cxx index 1732869e0d6..d0f2934069c 100644 --- a/PWGUD/Tasks/upcJpsiCorr.cxx +++ b/PWGUD/Tasks/upcJpsiCorr.cxx @@ -793,7 +793,7 @@ struct UpcJpsiCorr { } template - void fillHistograms(C collision, Ts tracks) + void fillHistograms(const C& collision, const Ts& tracks) { rStatistics.get(HIST("Statistics/hCutCounterCollisions"))->Fill(0); // number of collisions without any cuts diff --git a/PWGUD/Tasks/upcMft.cxx b/PWGUD/Tasks/upcMft.cxx index 6349589e6b4..18bb62bb7ea 100644 --- a/PWGUD/Tasks/upcMft.cxx +++ b/PWGUD/Tasks/upcMft.cxx @@ -127,7 +127,7 @@ struct UpcMftRec { o2::base::Propagator::initFieldFromGRP(grpmag); // for some reason this is necessary for the next next line runNumber = bc.runNumber(); - o2::field::MagneticField* field = static_cast(TGeoGlobalMagField::Instance()->GetField()); + o2::field::MagneticField* field = dynamic_cast(TGeoGlobalMagField::Instance()->GetField()); Bz = field->getBz(centerMFT); // gives error if the propagator is not initFielded LOG(info) << "The field at the center of the MFT is Bz = " << Bz; diff --git a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx index 0ae2035f93a..36b8d605947 100644 --- a/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx +++ b/PWGUD/Tasks/upcPhotonuclearAnalysisJMG.cxx @@ -50,7 +50,7 @@ namespace tree DECLARE_SOA_COLUMN(GapSide, gapSide, float); DECLARE_SOA_COLUMN(GapSideTimeZN, gapSideTimeZN, float); DECLARE_SOA_COLUMN(Sbp, sbp, int); -DECLARE_SOA_COLUMN(ITSROFb, itsROFb, int); +DECLARE_SOA_COLUMN(ItsRofB, itsRofB, int); DECLARE_SOA_COLUMN(VtxITSTPCCut, vtxITSTPCCut, int); DECLARE_SOA_COLUMN(ZVtxFT0vsPvCut, zVtxFT0vsPvCut, int); DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); @@ -59,9 +59,12 @@ DECLARE_SOA_COLUMN(EnergyZNA, energyZNA, float); DECLARE_SOA_COLUMN(EnergyZNC, energyZNC, float); DECLARE_SOA_COLUMN(AmplitudeFV0A, amplitudeFV0A, float); DECLARE_SOA_COLUMN(Occupancy, occupancy, float); -DECLARE_SOA_COLUMN(UPCMode, upcMode, float); +DECLARE_SOA_COLUMN(UpcMode, upcMode, float); +DECLARE_SOA_COLUMN(Pvz, pvz, float); DECLARE_SOA_COLUMN(PtSideA, ptSideA, std::vector); -DECLARE_SOA_COLUMN(RapSideA, rapSideA, std::vector); +DECLARE_SOA_COLUMN(PSideA, pSideA, std::vector); +DECLARE_SOA_COLUMN(ChargeSideA, chargeSideA, std::vector); +DECLARE_SOA_COLUMN(PseudorapSideA, pseudorapSideA, std::vector); DECLARE_SOA_COLUMN(PhiSideA, phiSideA, std::vector); DECLARE_SOA_COLUMN(TpcSignalSideA, tpcSignalSideA, std::vector); DECLARE_SOA_COLUMN(TofSignalSideA, tofSignalSideA, std::vector); @@ -69,8 +72,14 @@ DECLARE_SOA_COLUMN(TpcNSigmaPiSideA, tpcNSigmaPiSideA, std::vector); DECLARE_SOA_COLUMN(TofNSigmaPiSideA, tofNSigmaPiSideA, std::vector); DECLARE_SOA_COLUMN(TpcNSigmaKaSideA, tpcNSigmaKaSideA, std::vector); DECLARE_SOA_COLUMN(TofNSigmaKaSideA, tofNSigmaKaSideA, std::vector); +DECLARE_SOA_COLUMN(TpcNSigmaProSideA, tpcNSigmaProSideA, std::vector); +DECLARE_SOA_COLUMN(TofNSigmaProSideA, tofNSigmaProSideA, std::vector); +DECLARE_SOA_COLUMN(TofBetaSideA, tofBetaSideA, std::vector); +DECLARE_SOA_COLUMN(TofBetaErrorSideA, tofBetaErrorSideA, std::vector); DECLARE_SOA_COLUMN(PtSideC, ptSideC, std::vector); -DECLARE_SOA_COLUMN(RapSideC, rapSideC, std::vector); +DECLARE_SOA_COLUMN(PSideC, pSideC, std::vector); +DECLARE_SOA_COLUMN(ChargeSideC, chargeSideC, std::vector); +DECLARE_SOA_COLUMN(PseudorapSideC, pseudorapSideC, std::vector); DECLARE_SOA_COLUMN(PhiSideC, phiSideC, std::vector); DECLARE_SOA_COLUMN(TpcSignalSideC, tpcSignalSideC, std::vector); DECLARE_SOA_COLUMN(TofSignalSideC, tofSignalSideC, std::vector); @@ -78,6 +87,10 @@ DECLARE_SOA_COLUMN(TpcNSigmaPiSideC, tpcNSigmaPiSideC, std::vector); DECLARE_SOA_COLUMN(TofNSigmaPiSideC, tofNSigmaPiSideC, std::vector); DECLARE_SOA_COLUMN(TpcNSigmaKaSideC, tpcNSigmaKaSideC, std::vector); DECLARE_SOA_COLUMN(TofNSigmaKaSideC, tofNSigmaKaSideC, std::vector); +DECLARE_SOA_COLUMN(TpcNSigmaProSideC, tpcNSigmaProSideC, std::vector); +DECLARE_SOA_COLUMN(TofNSigmaProSideC, tofNSigmaProSideC, std::vector); +DECLARE_SOA_COLUMN(TofBetaSideC, tofBetaSideC, std::vector); +DECLARE_SOA_COLUMN(TofBetaErrorSideC, tofBetaErrorSideC, std::vector); DECLARE_SOA_COLUMN(NchSideA, nchSideA, int); DECLARE_SOA_COLUMN(MultiplicitySideA, multiplicitySideA, int); DECLARE_SOA_COLUMN(NchSideC, nchSideC, int); @@ -87,7 +100,7 @@ DECLARE_SOA_TABLE(TREE, "AOD", "Tree", tree::GapSide, tree::GapSideTimeZN, tree::Sbp, - tree::ITSROFb, + tree::ItsRofB, tree::VtxITSTPCCut, tree::ZVtxFT0vsPvCut, tree::TimeZNA, @@ -96,9 +109,12 @@ DECLARE_SOA_TABLE(TREE, "AOD", "Tree", tree::EnergyZNC, tree::AmplitudeFV0A, tree::Occupancy, - tree::UPCMode, + tree::UpcMode, + tree::Pvz, tree::PtSideA, - tree::RapSideA, + tree::PSideA, + tree::ChargeSideA, + tree::PseudorapSideA, tree::PhiSideA, tree::TpcSignalSideA, tree::TofSignalSideA, @@ -106,8 +122,14 @@ DECLARE_SOA_TABLE(TREE, "AOD", "Tree", tree::TofNSigmaPiSideA, tree::TpcNSigmaKaSideA, tree::TofNSigmaKaSideA, + tree::TpcNSigmaProSideA, + tree::TofNSigmaProSideA, + tree::TofBetaSideA, + tree::TofBetaErrorSideA, tree::PtSideC, - tree::RapSideC, + tree::PSideC, + tree::ChargeSideC, + tree::PseudorapSideC, tree::PhiSideC, tree::TpcSignalSideC, tree::TofSignalSideC, @@ -115,6 +137,10 @@ DECLARE_SOA_TABLE(TREE, "AOD", "Tree", tree::TofNSigmaPiSideC, tree::TpcNSigmaKaSideC, tree::TofNSigmaKaSideC, + tree::TpcNSigmaProSideC, + tree::TofNSigmaProSideC, + tree::TofBetaSideC, + tree::TofBetaErrorSideC, tree::NchSideA, tree::MultiplicitySideA, tree::NchSideC, @@ -122,7 +148,8 @@ DECLARE_SOA_TABLE(TREE, "AOD", "Tree", } // namespace o2::aod static constexpr float CFGPairCutDefaults[1][5] = {{-1, -1, -1, -1, -1}}; -constexpr float kThreeHalfPi = 1.5f * PI; +constexpr float KThreeHalfPi = 1.5f * PI; +constexpr float KMaxInSigmaPID = 9.0f; struct UpcPhotonuclearAnalysisJMG { @@ -200,7 +227,7 @@ struct UpcPhotonuclearAnalysisJMG { "Pair cuts on various particles"}; Configurable cfgTwoTrackCut{"cfgTwoTrackCut", -1, {"Two track cut"}}; ConfigurableAxis axisVertex{"axisVertex", {20, -10, 10}, "vertex axis for histograms"}; - ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {32, -PIHalf, kThreeHalfPi}, "delta phi axis for histograms"}; + ConfigurableAxis axisDeltaPhi{"axisDeltaPhi", {32, -PIHalf, KThreeHalfPi}, "delta phi axis for histograms"}; ConfigurableAxis axisDeltaEta{"axisDeltaEta", {32, -1.6, 1.6}, "delta eta axis for histograms"}; ConfigurableAxis axisPtTrigger{"axisPtTrigger", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0, 10.0}, "pt trigger axis for histograms"}; ConfigurableAxis axisPtAssoc{"axisPtAssoc", {VARIABLE_WIDTH, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0}, "pt associated axis for histograms"}; @@ -286,6 +313,8 @@ struct UpcPhotonuclearAnalysisJMG { histos.add("Tracks/SGsideA/hTrackPhi", "#it{#phi} distribution; #it{#phi}; counts", kTH1F, {axisPhi}); histos.add("Tracks/SGsideA/hTrackEta", "#it{#eta} distribution; #it{#eta}; counts", kTH1F, {axisEta}); histos.add("Tracks/SGsideA/hTrackTPCSignnalP", "#it{TPC dE/dx vs p}; #it{p*charge}; #it{TPC dE/dx}", kTH2F, {axisP, axisTPCSignal}); + histos.add("Tracks/SGsideA/hTrackTPCSignnalPPiones", "#it{TPC dE/dx vs p}; #it{p*charge}; #it{TPC dE/dx}", kTH2F, {axisP, axisTPCSignal}); + histos.add("Tracks/SGsideA/hTrackTPCSignnalPKaones", "#it{TPC dE/dx vs p}; #it{p*charge}; #it{TPC dE/dx}", kTH2F, {axisP, axisTPCSignal}); histos.add("Tracks/SGsideA/hTrackTOFSignnalP", "#it{TOF signal vs p}; #it{p*charge}; #it{TOF signal}", kTH2F, {axisP, axisTPCSignal}); histos.add("Tracks/SGsideA/hTrackITSNCls", "#it{N Clusters ITS} distribution; #it{N Clusters ITS}; counts", kTH1F, {axisNCls}); histos.add("Tracks/SGsideA/hTrackITSChi2NCls", "#it{N Clusters Chi2 ITS} distribution; #it{N Clusters Chi2 ITS}; counts", kTH1F, {axisChi2NCls}); @@ -318,6 +347,8 @@ struct UpcPhotonuclearAnalysisJMG { histos.add("Tracks/SGsideC/hTrackPhi", "#it{#phi} distribution; #it{#phi}; counts", kTH1F, {axisPhi}); histos.add("Tracks/SGsideC/hTrackEta", "#it{#eta} distribution; #it{#eta}; counts", kTH1F, {axisEta}); histos.add("Tracks/SGsideC/hTrackTPCSignnalP", "#it{TPC dE/dx vs p}; #it{p*charge}; #it{TPC dE/dx}", kTH2F, {axisP, axisTPCSignal}); + histos.add("Tracks/SGsideC/hTrackTPCSignnalPPiones", "#it{TPC dE/dx vs p}; #it{p*charge}; #it{TPC dE/dx}", kTH2F, {axisP, axisTPCSignal}); + histos.add("Tracks/SGsideC/hTrackTPCSignnalPKaones", "#it{TPC dE/dx vs p}; #it{p*charge}; #it{TPC dE/dx}", kTH2F, {axisP, axisTPCSignal}); histos.add("Tracks/SGsideC/hTrackTOFSignnalP", "#it{TOF signal vs p}; #it{p*charge}; #it{TOF signal}", kTH2F, {axisP, axisTPCSignal}); histos.add("Tracks/SGsideC/hTrackITSNCls", "#it{N Clusters ITS} distribution; #it{N Clusters ITS}; counts", kTH1F, {axisNCls}); histos.add("Tracks/SGsideC/hTrackITSChi2NCls", "#it{N Clusters Chi2 ITS} distribution; #it{N Clusters Chi2 ITS}; counts", kTH1F, {axisChi2NCls}); @@ -364,7 +395,7 @@ struct UpcPhotonuclearAnalysisJMG { } std::vector vtxBinsEdges{VARIABLE_WIDTH, -10.0f, -7.0f, -5.0f, -2.5f, 0.0f, 2.5f, 5.0f, 7.0f, 10.0f}; - std::vector gapSideBinsEdges{VARIABLE_WIDTH, -0.5, 0.5, 1.5}; + // std::vector gapSideBinsEdges{VARIABLE_WIDTH, -0.5, 0.5, 1.5}; enum EventType { SameEvent = 1, @@ -378,9 +409,9 @@ struct UpcPhotonuclearAnalysisJMG { // Binning only on PosZ without multiplicity // using BinningType = ColumnBinningPolicy; - // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}}; using BinningType = ColumnBinningPolicy; - // BinningType bindingOnVtx{{vtxBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges}}; // SameKindPair pairs{bindingOnVtx, nEventsMixed, -1, &cache}; template @@ -416,7 +447,7 @@ struct UpcPhotonuclearAnalysisJMG { if (useEnergyZN && !((collision.energyCommonZNA() < cutGapAMyEnergyZNA) && (collision.energyCommonZNC() >= cutGapAMyEnergyZNC))) { return false; } // 0n - A side && Xn - C Side - if (useGapSideVariable && collision.gapSide() != uint8_t(0)) { + if (useGapSideVariable && collision.gapSide() != static_cast(0)) { return false; } if (useFV0 && collision.totalFV0AmplitudeA() > cutGapAFV0Amplitude) { @@ -433,7 +464,7 @@ struct UpcPhotonuclearAnalysisJMG { if (useEnergyZN && !((collision.energyCommonZNA() >= cutGapCMyEnergyZNA) && (collision.energyCommonZNC() < cutGapCMyEnergyZNC))) { return false; } // Xn - A side && 0n - C Side - if (useGapSideVariable && collision.gapSide() != uint8_t(1)) { + if (useGapSideVariable && collision.gapSide() != static_cast(1)) { return false; } if (useFT0CGapC && (collision.totalFT0AmplitudeC() > cutGapCMyAmplitudeFT0C)) { @@ -520,7 +551,7 @@ struct UpcPhotonuclearAnalysisJMG { } template - void fillQAUD(const TTracks tracks, float multiplicity) + void fillQAUD(const TTracks& tracks, float multiplicity) { for (const auto& track : tracks) { if (isTrackCut(track) == false) { @@ -627,8 +658,8 @@ struct UpcPhotonuclearAnalysisJMG { int nchPVGapSideC = 0; int nchGapSideA = 0; int nchGapSideC = 0; - std::vector vTrackPtSideA, vTrackEtaSideA, vTrackPhiSideA, vTrackTPCSignalSideA, vTrackTOFSignalSideA, vTrackTPCNSigmaPiSideA, vTrackTOFNSigmaPiSideA, vTrackTPCNSigmaKaSideA, vTrackTOFNSigmaKaSideA; - std::vector vTrackPtSideC, vTrackEtaSideC, vTrackPhiSideC, vTrackTPCSignalSideC, vTrackTOFSignalSideC, vTrackTPCNSigmaPiSideC, vTrackTOFNSigmaPiSideC, vTrackTPCNSigmaKaSideC, vTrackTOFNSigmaKaSideC; + std::vector vTrackPtSideA, vTrackPSideA, vTrackChargeSideA, vTrackEtaSideA, vTrackPhiSideA, vTrackTPCSignalSideA, vTrackTOFSignalSideA, vTrackTPCNSigmaPiSideA, vTrackTOFNSigmaPiSideA, vTrackTPCNSigmaKaSideA, vTrackTOFNSigmaKaSideA, vTrackTPCNSigmaProSideA, vTrackTOFNSigmaProSideA, vTrackTOFBetaSideA, vTrackTOFBetaErrorSideA; + std::vector vTrackPtSideC, vTrackPSideC, vTrackChargeSideC, vTrackEtaSideC, vTrackPhiSideC, vTrackTPCSignalSideC, vTrackTOFSignalSideC, vTrackTPCNSigmaPiSideC, vTrackTOFNSigmaPiSideC, vTrackTPCNSigmaKaSideC, vTrackTOFNSigmaKaSideC, vTrackTPCNSigmaProSideC, vTrackTOFNSigmaProSideC, vTrackTOFBetaSideC, vTrackTOFBetaErrorSideC; int nTracksChargedSideA(-222), nTracksChargedSideC(-222); int multiplicitySideA(-222), multiplicitySideC(-222); @@ -675,13 +706,25 @@ struct UpcPhotonuclearAnalysisJMG { nTracksCharged++; sumPt += track.pt(); float phiVal = RecoDecay::constrainAngle(phi(track.px(), track.py()), 0.f); + float pTotal = momentum(track.px(), track.py(), track.pz()); + float etaVal = eta(track.px(), track.py(), track.pz()); histos.fill(HIST("Tracks/SGsideA/hTrackPt"), track.pt()); histos.fill(HIST("Tracks/SGsideA/hTrackPhi"), phiVal); - histos.fill(HIST("Tracks/SGsideA/hTrackEta"), eta(track.px(), track.py(), track.pz())); - histos.fill(HIST("Tracks/SGsideA/hTrackTPCSignnalP"), momentum(track.px(), track.py(), track.pz()) * track.sign(), track.tpcSignal()); - histos.fill(HIST("Tracks/SGsideA/hTrackTOFSignnalP"), momentum(track.px(), track.py(), track.pz()) * track.sign(), track.tofSignal()); + histos.fill(HIST("Tracks/SGsideA/hTrackEta"), etaVal); + histos.fill(HIST("Tracks/SGsideA/hTrackTPCSignnalP"), pTotal, track.tpcSignal()); + float nSigmaPi2 = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + float nSigmaKa2 = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + if (nSigmaPi2 < KMaxInSigmaPID && nSigmaKa2 > KMaxInSigmaPID) { + histos.fill(HIST("Tracks/SGsideA/hTrackTPCSignnalPPiones"), pTotal, track.tpcSignal()); + } + if (nSigmaKa2 < KMaxInSigmaPID && nSigmaPi2 > KMaxInSigmaPID) { + histos.fill(HIST("Tracks/SGsideA/hTrackTPCSignnalPKaones"), pTotal, track.tpcSignal()); + } + histos.fill(HIST("Tracks/SGsideA/hTrackTOFSignnalP"), pTotal, track.tofSignal()); vTrackPtSideA.push_back(track.pt()); - vTrackEtaSideA.push_back(eta(track.px(), track.py(), track.pz())); + vTrackPSideA.push_back(pTotal); + vTrackChargeSideA.push_back(track.sign()); + vTrackEtaSideA.push_back(etaVal); vTrackPhiSideA.push_back(phiVal); vTrackTPCSignalSideA.push_back(track.tpcSignal()); vTrackTOFSignalSideA.push_back(track.tofSignal()); @@ -689,6 +732,10 @@ struct UpcPhotonuclearAnalysisJMG { vTrackTOFNSigmaPiSideA.push_back(track.tofNSigmaPi()); vTrackTPCNSigmaKaSideA.push_back(track.tpcNSigmaKa()); vTrackTOFNSigmaKaSideA.push_back(track.tofNSigmaKa()); + vTrackTPCNSigmaProSideA.push_back(track.tpcNSigmaPr()); + vTrackTOFNSigmaProSideA.push_back(track.tofNSigmaPr()); + vTrackTOFBetaSideA.push_back(track.beta()); + vTrackTOFBetaErrorSideA.push_back(track.betaerror()); histos.fill(HIST("Tracks/SGsideA/hTrackITSNCls"), track.itsNCls()); histos.fill(HIST("Tracks/SGsideA/hTrackITSChi2NCls"), track.itsChi2NCl()); @@ -708,7 +755,6 @@ struct UpcPhotonuclearAnalysisJMG { histos.fill(HIST("Events/SGsideA/hTrackPV"), nchPVGapSideA, nchGapSideA); nTracksChargedSideA = nTracksCharged; multiplicitySideA = reconstructedTracks.size(); - nTracksCharged = sumPt = 0; break; case 1: // gap for side C if (isCollisionCutSG(reconstructedCollision, 1) == false) { @@ -735,13 +781,25 @@ struct UpcPhotonuclearAnalysisJMG { nTracksCharged++; sumPt += track.pt(); float phiVal = RecoDecay::constrainAngle(phi(track.px(), track.py()), 0.f); + float pTotal = momentum(track.px(), track.py(), track.pz()); + float etaVal = eta(track.px(), track.py(), track.pz()); histos.fill(HIST("Tracks/SGsideC/hTrackPt"), track.pt()); histos.fill(HIST("Tracks/SGsideC/hTrackPhi"), phiVal); - histos.fill(HIST("Tracks/SGsideC/hTrackEta"), eta(track.px(), track.py(), track.pz())); - histos.fill(HIST("Tracks/SGsideC/hTrackTPCSignnalP"), momentum(track.px(), track.py(), track.pz()) * track.sign(), track.tpcSignal()); - histos.fill(HIST("Tracks/SGsideC/hTrackTOFSignnalP"), momentum(track.px(), track.py(), track.pz()) * track.sign(), track.tofSignal()); + histos.fill(HIST("Tracks/SGsideC/hTrackEta"), etaVal); + histos.fill(HIST("Tracks/SGsideC/hTrackTPCSignnalP"), pTotal, track.tpcSignal()); + float nSigmaPi2 = track.tpcNSigmaPi() * track.tpcNSigmaPi() + track.tofNSigmaPi() * track.tofNSigmaPi(); + float nSigmaKa2 = track.tpcNSigmaKa() * track.tpcNSigmaKa() + track.tofNSigmaKa() * track.tofNSigmaKa(); + if (nSigmaPi2 < KMaxInSigmaPID && nSigmaKa2 > KMaxInSigmaPID) { + histos.fill(HIST("Tracks/SGsideC/hTrackTPCSignnalPPiones"), pTotal, track.tpcSignal()); + } + if (nSigmaKa2 < KMaxInSigmaPID && nSigmaPi2 > KMaxInSigmaPID) { + histos.fill(HIST("Tracks/SGsideC/hTrackTPCSignnalPKaones"), pTotal, track.tpcSignal()); + } + histos.fill(HIST("Tracks/SGsideC/hTrackTOFSignnalP"), pTotal, track.tofSignal()); vTrackPtSideC.push_back(track.pt()); - vTrackEtaSideC.push_back(eta(track.px(), track.py(), track.pz())); + vTrackPSideC.push_back(pTotal); + vTrackChargeSideC.push_back(track.sign()); + vTrackEtaSideC.push_back(etaVal); vTrackPhiSideC.push_back(phiVal); vTrackTPCSignalSideC.push_back(track.tpcSignal()); vTrackTOFSignalSideC.push_back(track.tofSignal()); @@ -749,6 +807,10 @@ struct UpcPhotonuclearAnalysisJMG { vTrackTOFNSigmaPiSideC.push_back(track.tofNSigmaPi()); vTrackTPCNSigmaKaSideC.push_back(track.tpcNSigmaKa()); vTrackTOFNSigmaKaSideC.push_back(track.tofNSigmaKa()); + vTrackTPCNSigmaProSideC.push_back(track.tpcNSigmaPr()); + vTrackTOFNSigmaProSideC.push_back(track.tofNSigmaPr()); + vTrackTOFBetaSideC.push_back(track.beta()); + vTrackTOFBetaErrorSideC.push_back(track.betaerror()); histos.fill(HIST("Tracks/SGsideC/hTrackITSNCls"), track.itsNCls()); histos.fill(HIST("Tracks/SGsideC/hTrackITSChi2NCls"), track.itsChi2NCl()); @@ -768,7 +830,6 @@ struct UpcPhotonuclearAnalysisJMG { histos.fill(HIST("Events/SGsideC/hTrackPV"), nchPVGapSideC, nchGapSideC); nTracksChargedSideC = nTracksCharged; multiplicitySideC = reconstructedTracks.size(); - nTracksCharged = sumPt = 0; break; default: return; @@ -787,7 +848,10 @@ struct UpcPhotonuclearAnalysisJMG { reconstructedCollision.totalFV0AmplitudeA(), reconstructedCollision.occupancyInTime(), reconstructedCollision.flags(), + reconstructedCollision.posZ(), vTrackPtSideA, + vTrackPSideA, + vTrackChargeSideA, vTrackEtaSideA, vTrackPhiSideA, vTrackTPCSignalSideA, @@ -796,15 +860,25 @@ struct UpcPhotonuclearAnalysisJMG { vTrackTOFNSigmaPiSideA, vTrackTPCNSigmaKaSideA, vTrackTOFNSigmaKaSideA, + vTrackTPCNSigmaProSideA, + vTrackTOFNSigmaProSideA, + vTrackTOFBetaSideA, + vTrackTOFBetaErrorSideA, vTrackPtSideC, + vTrackPSideC, + vTrackChargeSideC, vTrackEtaSideC, vTrackPhiSideC, - vTrackTPCSignalSideA, - vTrackTOFSignalSideA, - vTrackTPCNSigmaPiSideA, - vTrackTOFNSigmaPiSideA, - vTrackTPCNSigmaKaSideA, - vTrackTOFNSigmaKaSideA, + vTrackTPCSignalSideC, + vTrackTOFSignalSideC, + vTrackTPCNSigmaPiSideC, + vTrackTOFNSigmaPiSideC, + vTrackTPCNSigmaKaSideC, + vTrackTOFNSigmaKaSideC, + vTrackTPCNSigmaProSideC, + vTrackTOFNSigmaProSideC, + vTrackTOFBetaSideC, + vTrackTOFBetaErrorSideC, nTracksChargedSideA, multiplicitySideA, nTracksChargedSideC, @@ -820,8 +894,8 @@ struct UpcPhotonuclearAnalysisJMG { // int sgSide = reconstructedCollision.gapSide(); // int sgSide = 0; - // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}, true}; - BinningType bindingOnVtx{{vtxBinsEdges}, true}; + // BinningType bindingOnVtx{{vtxBinsEdges, gapSideBinsEdges}}; + BinningType bindingOnVtx{{vtxBinsEdges}}; auto tracksTuple = std::make_tuple(reconstructedTracks); SameKindPair pairs{bindingOnVtx, nEventsMixed, -1, reconstructedCollision, tracksTuple, &cache}; @@ -848,9 +922,10 @@ struct UpcPhotonuclearAnalysisJMG { ++multiplicity; } // multiplicity = tracks1.size(); - if (fillCollisionUD(mixed, multiplicity) == false) { - return; - } + // if (fillCollisionUD(mixed, multiplicity) == false) { + // return; + // } + fillCollisionUD(mixed, multiplicity); histos.fill(HIST("Events/hCountCollisionsMixed"), 2); // histos.fill(HIST("eventcount"), bindingOnVtx.getBin({collision1.posZ()})); // histos.fill(HIST("eventcount"), bindingOnVtx.getBin({collision1.posZ(), collision1.gapSide()})); @@ -932,8 +1007,8 @@ struct UpcPhotonuclearAnalysisJMG { return; } histos.fill(HIST("Events/hCollisionsFlow"), 11); - bool cutGapSideAVariable = useGapSideVariable && reconstructedCollision.gapSide() != uint8_t(0); - bool cutGapSideCVariable = useGapSideVariable && reconstructedCollision.gapSide() != uint8_t(1); + bool cutGapSideAVariable = useGapSideVariable && reconstructedCollision.gapSide() != static_cast(0); + bool cutGapSideCVariable = useGapSideVariable && reconstructedCollision.gapSide() != static_cast(1); if (cutGapSideAVariable || cutGapSideCVariable) { return; } @@ -1046,9 +1121,10 @@ struct UpcPhotonuclearAnalysisJMG { ++multiplicity; } // multiplicity = reconstructedTracks.size(); - if (fillCollisionUD(same, multiplicity) == false) { - return; - } + // if (fillCollisionUD(same, multiplicity) == false) { + // return; + // } + fillCollisionUD(same, multiplicity); // LOGF(debug, "Filling same events"); histos.fill(HIST("eventcount"), -2); if (minMultiplicity <= multiplicity && multiplicity <= range1Max) { diff --git a/PWGUD/Tasks/upcPileUp.cxx b/PWGUD/Tasks/upcPileUp.cxx new file mode 100644 index 00000000000..2fca0ac06a8 --- /dev/null +++ b/PWGUD/Tasks/upcPileUp.cxx @@ -0,0 +1,533 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file upcPileUp.cxx +/// \brief Pile-up probability in FT0 for UPC analyses, per colliding bunch and time frame +/// +/// For each colliding bunch crossing it counts +/// - denominator A: crossings with no activity on the C side (FT0C, FDDC, ZNC) +/// - numerator A : of those, the ones with a signal in FT0A above threshold +/// and the mirror for the C side (A side empty: FT0A, FV0A, FDDA, ZNA). +/// The ratio numerator / denominator is the pile-up probability. +/// +/// The BCs table only stores crossings with some activity. Completely empty +/// crossings are not in it, but they belong in the denominator. The task +/// therefore computes the number of analysed crossings of every colliding +/// bunch in every time frame, applying the same time-frame and ITS ROF border +/// cuts as the event selection, and the denominator is that number minus the +/// stored crossings with activity on the other side. +/// +/// \author David Arevalos + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/EventSelection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; + +using BCsTSsSels = soa::Join; + +struct UpcPileUp { + + // services + Service ccdb{}; + + // histograms, booked per run and kept in the maps by name + HistogramRegistry bcTH1Registry{"bcTH1Registry", {}}; + std::map> bcTH1Pointers; + HistogramRegistry bcTH2Registry{"bcTH2Registry", {}}; + std::map> bcTH2Pointers; + + // histograms of the current run, cached so that the loops do no name lookup + std::shared_ptr hBcSel, hTF, hBorderCheck; + std::shared_ptr hCross, hVtx, hVtxCe, hDenA, hDenC; + std::vector> hNumA, hNumC, hNumAZn, hNumCZn; + + // filling scheme + std::bitset bcPatternB; + std::vector bcbIdx; // index of each colliding bunch, -1 otherwise + std::vector bcbList; // bcID within the orbit of each colliding bunch + int nbcB = 0; + + // ITS ROF info + int rofLength = -1; // number of bcs per ROF + int rofShift = -1; // bc shift of the ITS ROF + + // border margins, as in the event selection + int tfStartMargin = 0; + int tfEndMargin = 0; + int rofStartMargin = 0; + int rofEndMargin = 0; + + // run info + int runNumberBc = 0; // run number used to process BCs + int64_t tsMid = 0; // middle of the run, to access run-wise CCDB entries + int64_t orbitsPerTF = 0; + int64_t bcSOR = 0; // first bc of the first orbit + int64_t nBCsPerTF = 0; // duration of TF in bcs + int64_t nTF = 0; // number of time frames in run + std::set seenTFs; // time frames already counted in this run + + // thresholds used, filled in init from the configurable + std::vector thresholds; + int nThresholds = 0; + + // analysed crossings of each colliding bunch in the current time frame + std::vector nCrossTF; + + // histogram binning constants + static constexpr int NBinsBcSel = 4; + static constexpr int NBinsBorderCheck = 2; + + // FDD channel pairing, as in upcVmRof + static constexpr int NFddPairs = 4; + static constexpr int FddPairOffset = 4; + + // configurables + Configurable tfPerBin{"tfPerBin", 10000, "timeframes per bin, 1e4 means some 28 s"}; + Configurable ft0VtxIdx{"ft0VtxIdx", 2, "index of the FT0VTX input in the CTP input mask"}; + Configurable ft0CeIdx{"ft0CeIdx", 4, "index of the FT0CE input in the CTP input mask"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + + // signal and veto definition + Configurable> ampThresholds{"ampThresholds", {100.0f, 150.0f, 200.0f}, "amplitude thresholds defining a signal in FT0"}; + Configurable maxAbsTimeFT0{"maxAbsTimeFT0", 4.0, "max |time| in a FT0 side (ns)"}; + Configurable maxAbsTimeFV0{"maxAbsTimeFV0", 4.0, "max |time| in FV0A (ns)"}; + Configurable maxAbsTimeFDD{"maxAbsTimeFDD", 4.0, "max |time| in a FDD side (ns)"}; + Configurable maxAbsTimeZN{"maxAbsTimeZN", 2.0, "max |time| in a ZN (ns)"}; + Configurable fillZnVariation{"fillZnVariation", true, "also fill the numerators asking in addition for a signal in ZNA or ZNC"}; + + // border margins, must match the ones used by the event selection; -1 takes them from CCDB + Configurable confTFStartBorderMargin{"confTFStartBorderMargin", -1, "bcs cut at the start of the TF, -1 from CCDB"}; + Configurable confTFEndBorderMargin{"confTFEndBorderMargin", -1, "bcs cut at the end of the TF, -1 from CCDB"}; + Configurable confROFStartBorderMargin{"confROFStartBorderMargin", -1, "bcs cut at the start of the ITS ROF, -1 from CCDB"}; + Configurable confROFEndBorderMargin{"confROFEndBorderMargin", -1, "bcs cut at the end of the ITS ROF, -1 from CCDB"}; + + //-------------------------------------------------------------------------------- + // general run information + void getRunInfo(int run) + { + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + tsMid = runInfo.sor / 2 + runInfo.eor / 2; + orbitsPerTF = runInfo.orbitsPerTF; + bcSOR = runInfo.orbitSOR * o2::constants::lhc::LHCMaxBunches; + nBCsPerTF = orbitsPerTF * o2::constants::lhc::LHCMaxBunches; + nTF = (runInfo.orbitEOR - runInfo.orbitSOR + orbitsPerTF - 1) / orbitsPerTF; // rounded up + } // end getRunInfo() + + //-------------------------------------------------------------------------------- + // filling scheme + void getFillingScheme() + { + auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", tsMid); + auto beamPatternA = grplhcif->getBunchFilling().getBeamPattern(0); + auto beamPatternC = grplhcif->getBunchFilling().getBeamPattern(1); + bcPatternB = beamPatternA & beamPatternC; + bcbIdx.assign(o2::constants::lhc::LHCMaxBunches, -1); + bcbList.clear(); + nbcB = 0; + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + if (bcPatternB.test(i)) { + bcbIdx[i] = nbcB; + bcbList.push_back(i); + nbcB++; + } + } + nCrossTF.assign(nbcB, 0.); + } // end getFillingScheme() + + //-------------------------------------------------------------------------------- + // ITS ROF info and border margins, taken as in the event selection + void getBorderInfo() + { + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", tsMid); + rofShift = alppar->roFrameBiasInBC; + rofLength = alppar->roFrameLengthInBC; + + auto par = ccdb->getForTimeStamp("EventSelection/EventSelectionParams", tsMid); + tfStartMargin = confTFStartBorderMargin < 0 ? par->fTimeFrameStartBorderMargin : confTFStartBorderMargin.value; + tfEndMargin = confTFEndBorderMargin < 0 ? par->fTimeFrameEndBorderMargin : confTFEndBorderMargin.value; + rofStartMargin = confROFStartBorderMargin < 0 ? par->fITSROFrameStartBorderMargin : confROFStartBorderMargin.value; + rofEndMargin = confROFEndBorderMargin < 0 ? par->fITSROFrameEndBorderMargin : confROFEndBorderMargin.value; + LOGP(info, "border margins: TF {} {}, ITS ROF {} {}", tfStartMargin, tfEndMargin, rofStartMargin, rofEndMargin); + } // end getBorderInfo() + + //-------------------------------------------------------------------------------- + int64_t getBcWithinOrbit(int64_t globalBC) + { + return (globalBC % o2::constants::lhc::LHCMaxBunches); + } + + //-------------------------------------------------------------------------------- + int64_t getTimeFrame(int64_t globalBC) + { + return (globalBC - bcSOR) / nBCsPerTF; + } + + //-------------------------------------------------------------------------------- + // same conditions as kNoTimeFrameBorder and kNoITSROFrameBorder in the event selection + bool passesBorders(int64_t globalBC) + { + int64_t bcInTF = (globalBC - bcSOR) % nBCsPerTF; + if (!(bcInTF > tfStartMargin && bcInTF < nBCsPerTF - tfEndMargin)) { + return false; + } + int64_t bcInRof = (globalBC + o2::constants::lhc::LHCMaxBunches - rofShift) % rofLength; + return (bcInRof > rofStartMargin && bcInRof < rofLength - rofEndMargin); + } + + //-------------------------------------------------------------------------------- + // selection flags of a stored bc + bool checkBcFlags(const auto& bc) + { + hBcSel->Fill(0); + if (!bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + hBcSel->Fill(1); + if (!bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + hBcSel->Fill(2); + return true; + } // end checkBcFlags() + + //-------------------------------------------------------------------------------- + // analysed crossings of every colliding bunch in a time frame: all of them, + // stored or not, that pass the same border cuts as the stored ones. + // They go into the crossings histogram and, as a start, into both + // denominators; stored crossings with activity on the other side are removed + // later, one by one. + void fillCrossings(int64_t tf) + { + const int64_t tfStartBC = bcSOR + tf * nBCsPerTF; + std::fill(nCrossTF.begin(), nCrossTF.end(), 0.); + for (int64_t orbit = 0; orbit < orbitsPerTF; orbit++) { + for (const auto& bcInOrbit : bcbList) { + int64_t globalBC = tfStartBC + orbit * o2::constants::lhc::LHCMaxBunches + bcInOrbit; + if (passesBorders(globalBC)) { + nCrossTF[bcbIdx[bcInOrbit]] += 1.; + } + } + } + for (int ib = 0; ib < nbcB; ib++) { + if (nCrossTF[ib] > 0.) { + hCross->Fill(tf, ib, nCrossTF[ib]); + hDenA->Fill(tf, ib, nCrossTF[ib]); + hDenC->Fill(tf, ib, nCrossTF[ib]); + } + } + } // end fillCrossings() + + //-------------------------------------------------------------------------------- + // FDD amplitude of a side, using the channel pairing of upcVmRof + // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}} + float sumFddAmplitude(const auto& charges) + { + float amp = 0; + for (int i = 0; i < NFddPairs; i++) { + if (charges[i] > 0 && charges[i + FddPairOffset] > 0) { + amp += 0.5 * (charges[i] + charges[i + FddPairOffset]); + } + } + return amp; + } // end sumFddAmplitude() + + //-------------------------------------------------------------------------------- + // book the histograms of a run + void addBcHistos(int run) + { + if (bcTH1Pointers[Form("bc/%d/bcSel_H", run)]) { + return; + } + + const int nBunches = o2::constants::lhc::LHCMaxBunches; + const double maxBunch = static_cast(nBunches) - 0.5; + const int nBinsTF = static_cast(nTF / tfPerBin) + 1; + const double maxTF = static_cast(nBinsTF * tfPerBin - 1) - 0.5; + const AxisSpec axisTF{nBinsTF, -0.5, maxTF, "TF"}; + const AxisSpec axisBcb{nbcB, -0.5, nbcB - 0.5, "bc-B idx"}; + + // filling scheme and bookkeeping + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)] = bcTH1Registry.add(Form("bc/%d/bcPatternB_H", run), "colliding bunches; bcID;", + {HistType::kTH1D, {{nBunches, -0.5, maxBunch}}}); + bcTH1Pointers[Form("bc/%d/bcSel_H", run)] = bcTH1Registry.add(Form("bc/%d/bcSel_H", run), "bc selection: all, no TF border, no ROF border, colliding; ; Counter", + {HistType::kTH1D, {{NBinsBcSel, -0.5, NBinsBcSel - 0.5}}}); + bcTH1Pointers[Form("bc/%d/tf_H", run)] = bcTH1Registry.add(Form("bc/%d/tf_H", run), "analysed time frames; TF; Counts", + {HistType::kTH1D, {axisTF}}); + // stored bcs where the border cut computed here agrees / disagrees with the event selection + bcTH1Pointers[Form("bc/%d/borderCheck_H", run)] = bcTH1Registry.add(Form("bc/%d/borderCheck_H", run), "border cut vs event selection: agree, disagree; ; Counter", + {HistType::kTH1D, {{NBinsBorderCheck, -0.5, NBinsBorderCheck - 0.5}}}); + + // thresholds, stored as bin labels so that they survive merging + bcTH1Pointers[Form("bc/%d/thresholds_H", run)] = bcTH1Registry.add(Form("bc/%d/thresholds_H", run), "amplitude thresholds; threshold index; ", + {HistType::kTH1D, {{nThresholds, -0.5, nThresholds - 0.5}}}); + for (int i = 0; i < nThresholds; i++) { + bcTH1Pointers[Form("bc/%d/thresholds_H", run)]->GetXaxis()->SetBinLabel(i + 1, Form("%g", thresholds[i])); + bcTH1Pointers[Form("bc/%d/thresholds_H", run)]->SetBinContent(i + 1, 1); + } + for (const auto& bcInOrbit : bcbList) { + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)]->Fill(bcInOrbit); + } + + // analysed crossings, stored or not + bcTH2Pointers[Form("bc/%d/nCross_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/nCross_bcb_H", run), "analysed crossings; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + + // triggers from the CTP input mask, to compute mu + bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0Vtx_bcb_H", run), "ft0Vtx triggers; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0VtxCe_bcb_H", run), "ft0VtxCe triggers; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + + // pile-up probability. The denominators are filled with weights, so their + // bin errors are not meaningful: compute the uncertainty of the ratio + // binomially from the contents. + bcTH2Pointers[Form("bc/%d/puDenA_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/puDenA_bcb_H", run), "crossings empty on the C side; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puDenC_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/puDenC_bcb_H", run), "crossings empty on the A side; TF; bc-B idx; Counter", + {HistType::kTH2D, {axisTF, axisBcb}}); + for (int i = 0; i < nThresholds; i++) { + bcTH2Pointers[Form("bc/%d/puNumA_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumA_t%d_bcb_H", run, i), Form("FT0A amplitude > %g, C side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puNumC_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumC_t%d_bcb_H", run, i), Form("FT0C amplitude > %g, A side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + // variation asking in addition for a signal in ZNA or ZNC. Since the ZN of + // the other side is part of the empty condition, this is in practice the + // ZN of the same side. + if (fillZnVariation) { + bcTH2Pointers[Form("bc/%d/puNumAZn_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumAZn_t%d_bcb_H", run, i), Form("FT0A amplitude > %g and ZN, C side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + bcTH2Pointers[Form("bc/%d/puNumCZn_t%d_bcb_H", run, i)] = bcTH2Registry.add(Form("bc/%d/puNumCZn_t%d_bcb_H", run, i), Form("FT0C amplitude > %g and ZN, A side empty; TF; bc-B idx; Counter", thresholds[i]), + {HistType::kTH2D, {axisTF, axisBcb}}); + } + } + } // end addBcHistos() + + //-------------------------------------------------------------------------------- + // cache the histograms of a run + void setHistoPointers(int run) + { + hBcSel = bcTH1Pointers[Form("bc/%d/bcSel_H", run)]; + hTF = bcTH1Pointers[Form("bc/%d/tf_H", run)]; + hBorderCheck = bcTH1Pointers[Form("bc/%d/borderCheck_H", run)]; + hCross = bcTH2Pointers[Form("bc/%d/nCross_bcb_H", run)]; + hVtx = bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", run)]; + hVtxCe = bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", run)]; + hDenA = bcTH2Pointers[Form("bc/%d/puDenA_bcb_H", run)]; + hDenC = bcTH2Pointers[Form("bc/%d/puDenC_bcb_H", run)]; + hNumA.assign(nThresholds, nullptr); + hNumC.assign(nThresholds, nullptr); + hNumAZn.assign(nThresholds, nullptr); + hNumCZn.assign(nThresholds, nullptr); + for (int i = 0; i < nThresholds; i++) { + hNumA[i] = bcTH2Pointers[Form("bc/%d/puNumA_t%d_bcb_H", run, i)]; + hNumC[i] = bcTH2Pointers[Form("bc/%d/puNumC_t%d_bcb_H", run, i)]; + if (fillZnVariation) { + hNumAZn[i] = bcTH2Pointers[Form("bc/%d/puNumAZn_t%d_bcb_H", run, i)]; + hNumCZn[i] = bcTH2Pointers[Form("bc/%d/puNumCZn_t%d_bcb_H", run, i)]; + } + } + } // end setHistoPointers() + + //-------------------------------------------------------------------------------- + void init(InitContext&) + { + ccdb->setURL(ccdbUrl); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + + thresholds = static_cast>(ampThresholds); + nThresholds = static_cast(thresholds.size()); + } // end init() + + //-------------------------------------------------------------------------------- + void processBCs(BCsTSsSels const& bcs, + aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, + aod::Zdcs const&) + { + if (bcs.size() == 0) { + return; + } + + // new run + const auto& bcAt0 = bcs.iteratorAt(0); + if (runNumberBc != bcAt0.runNumber()) { + runNumberBc = bcAt0.runNumber(); + getRunInfo(runNumberBc); + getFillingScheme(); + getBorderInfo(); + addBcHistos(runNumberBc); + setHistoPointers(runNumberBc); + seenTFs.clear(); + } + + for (const auto& bc : bcs) { + int64_t thisBC = getBcWithinOrbit(bc.globalBC()); + int64_t thisTF = getTimeFrame(bc.globalBC()); + + // first time this time frame is seen: count it and add all its analysed + // crossings. The set protects against a time frame split over two + // dataframes being counted twice. + if (seenTFs.insert(thisTF).second) { + hTF->Fill(thisTF); + fillCrossings(thisTF); + } + + // check that the border cut computed here agrees with the event selection + bool passesFlags = bc.selection_bit(aod::evsel::kNoTimeFrameBorder) && bc.selection_bit(aod::evsel::kNoITSROFrameBorder); + hBorderCheck->Fill(passesFlags == passesBorders(bc.globalBC()) ? 0 : 1); + + if (!checkBcFlags(bc)) { + continue; + } + if (!bcPatternB.test(thisBC)) { + continue; + } + hBcSel->Fill(3); + int thisBcb = bcbIdx[thisBC]; + + // triggers from the CTP input mask + std::bitset<64> mask = bc.inputMask(); + if (mask[ft0VtxIdx]) { + hVtx->Fill(thisTF, thisBcb); + if (mask[ft0CeIdx]) { + hVtxCe->Fill(thisTF, thisBcb); + } + } + + //------------------------------------------------------------------------------ + // detector information; amplitudes are only meaningful when in time + + // FT0 + bool ft0aInTime = false; + bool ft0cInTime = false; + float ampFT0A = 0; + float ampFT0C = 0; + if (bc.has_foundFT0()) { + if (bc.foundFT0().isValidTimeA() && std::abs(bc.foundFT0().timeA()) < maxAbsTimeFT0) { + ft0aInTime = true; + ampFT0A = bc.foundFT0().sumAmpA(); + } + if (bc.foundFT0().isValidTimeC() && std::abs(bc.foundFT0().timeC()) < maxAbsTimeFT0) { + ft0cInTime = true; + ampFT0C = bc.foundFT0().sumAmpC(); + } + } + + // FV0A + bool fv0aSignal = false; + if (bc.has_foundFV0() && std::abs(bc.foundFV0().time()) < maxAbsTimeFV0) { + auto amps = bc.foundFV0().amplitude(); + fv0aSignal = (std::accumulate(amps.begin(), amps.end(), 0.f) > 0); + } + + // FDD + bool fddaSignal = false; + bool fddcSignal = false; + if (bc.has_foundFDD()) { + if (std::abs(bc.foundFDD().timeA()) < maxAbsTimeFDD) { + fddaSignal = (sumFddAmplitude(bc.foundFDD().chargeA()) > 0); + } + if (std::abs(bc.foundFDD().timeC()) < maxAbsTimeFDD) { + fddcSignal = (sumFddAmplitude(bc.foundFDD().chargeC()) > 0); + } + } + + // ZN: an entry in time, no requirement on the energy + bool znaSignal = false; + bool zncSignal = false; + if (bc.has_zdc()) { + float tZNA = bc.zdc().timeZNA(); + float tZNC = bc.zdc().timeZNC(); + znaSignal = std::isfinite(tZNA) && std::abs(tZNA) < maxAbsTimeZN; + zncSignal = std::isfinite(tZNC) && std::abs(tZNC) < maxAbsTimeZN; + } + bool znSignal = znaSignal || zncSignal; + + // a side is empty if no detector has a signal in time, at any amplitude above zero + bool ft0aSignal = ft0aInTime && (ampFT0A > 0); + bool ft0cSignal = ft0cInTime && (ampFT0C > 0); + bool noSignalC = !ft0cSignal && !fddcSignal && !zncSignal; + bool noSignalA = !ft0aSignal && !fv0aSignal && !fddaSignal && !znaSignal; + + //------------------------------------------------------------------------------ + // pile-up counters. The denominators already hold all analysed crossings + // of this bunch; a stored crossing with activity on the other side is + // removed from them. + if (noSignalC) { + for (int i = 0; i < nThresholds; i++) { + if (ft0aInTime && ampFT0A > thresholds[i]) { + hNumA[i]->Fill(thisTF, thisBcb); + if (fillZnVariation && znSignal) { + hNumAZn[i]->Fill(thisTF, thisBcb); + } + } + } + } else { + hDenA->Fill(thisTF, thisBcb, -1.); + } + if (noSignalA) { + for (int i = 0; i < nThresholds; i++) { + if (ft0cInTime && ampFT0C > thresholds[i]) { + hNumC[i]->Fill(thisTF, thisBcb); + if (fillZnVariation && znSignal) { + hNumCZn[i]->Fill(thisTF, thisBcb); + } + } + } + } else { + hDenC->Fill(thisTF, thisBcb, -1.); + } + } // loop over bcs + } // end processBCs() + PROCESS_SWITCH(UpcPileUp, processBCs, "pile-up probability in FT0", true); + +}; // end of struct UpcPileUp + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/Tasks/upcPionAnalysis.cxx b/PWGUD/Tasks/upcPionAnalysis.cxx index 470281f0a77..afd5bbd9829 100644 --- a/PWGUD/Tasks/upcPionAnalysis.cxx +++ b/PWGUD/Tasks/upcPionAnalysis.cxx @@ -85,9 +85,9 @@ struct UPCPionAnalysis { HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject}; //_____________________________________________________________________________________________ - Double_t CosThetaHelicityFrame(TLorentzVector pionPositive, - TLorentzVector pionNegative, - TLorentzVector possibleRhoZero) + Double_t CosThetaHelicityFrame(const TLorentzVector& pionPositive, + const TLorentzVector& pionNegative, + const TLorentzVector& possibleRhoZero) { Double_t HalfSqrtSnn = 2680.; @@ -114,7 +114,7 @@ struct UPCPionAnalysis { return CosThetaHE; } //------------------------------------------------------------------------------------------------------ - Double_t PhiHelicityFrame(TLorentzVector piPositive, TLorentzVector piNegative, TLorentzVector possibleRho) + Double_t PhiHelicityFrame(const TLorentzVector& piPositive, const TLorentzVector& piNegative, const TLorentzVector& possibleRho) { // Half of the energy per pair of the colliding nucleons. @@ -281,7 +281,7 @@ struct UPCPionAnalysis { return q; } - double DeltaPhi(TLorentzVector lv1, TLorentzVector lv2) + double DeltaPhi(const TLorentzVector& lv1, const TLorentzVector& lv2) { TLorentzVector lv_sum = lv1 + lv2; TLorentzVector lv_diff = lv1 - lv2; @@ -492,7 +492,7 @@ struct UPCPionAnalysis { return; registry.fill(HIST("hSelectionCounter"), 5); - for (auto t : tracks) { + for (const auto& t : tracks) { /*if (!t.isPVContributor()) { continue; @@ -527,7 +527,7 @@ struct UPCPionAnalysis { registry.fill(HIST("hTracksPions"), onlyPionTracks.size()); //_____________________________________ // Creating rhos - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { p += pion; } //_____________________________________ @@ -558,7 +558,7 @@ struct UPCPionAnalysis { int sign = 0; TLorentzVector piplus, piminus; - for (auto rawPion : rawPionTracks) { + for (const auto& rawPion : rawPionTracks) { sign += rawPion.sign(); if (rawPion.sign() > 0) { piplus = onlyPionTracks[0]; @@ -600,7 +600,7 @@ struct UPCPionAnalysis { registry.fill(HIST("hRap4Pion"), p.Rapidity()); registry.fill(HIST("hEta4Pion"), p.Eta()); } - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { registry.fill(HIST("hPhiEtaFourPionsRightSign"), pion.Phi(), pion.Eta()); } } @@ -614,7 +614,7 @@ struct UPCPionAnalysis { int sign = 0; TLorentzVector piplus, piminus; - for (auto rawPion : rawPionTracks) { + for (const auto& rawPion : rawPionTracks) { sign += rawPion.sign(); if (rawPion.sign() > 0) { piplus = onlyPionTracks[0]; @@ -655,7 +655,7 @@ struct UPCPionAnalysis { registry.fill(HIST("hRap6Pion"), p.Rapidity()); registry.fill(HIST("hEta6Pion"), p.Eta()); registry.fill(HIST("hPt6PionRightSign"), p.Pt()); - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { registry.fill(HIST("hPhiEtaSixPionsRightSign"), pion.Phi(), pion.Eta()); } } @@ -668,7 +668,7 @@ struct UPCPionAnalysis { if ((rawPionTracks.size() == 8) && (onlyPionTracks.size() == 8)) { TLorentzVector piplus, piminus; int sign = 0; - for (auto rawPion : rawPionTracks) { + for (const auto& rawPion : rawPionTracks) { sign += rawPion.sign(); if (rawPion.sign() > 0) { @@ -713,7 +713,7 @@ struct UPCPionAnalysis { registry.fill(HIST("hMPt3"), p.M(), p.Pt()); registry.fill(HIST("hRap8pion"), p.Rapidity()); registry.fill(HIST("hEta8Pion"), p.Eta()); - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { registry.fill(HIST("hPhiEta8PionsRightSign"), pion.Phi(), pion.Eta()); } } diff --git a/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx b/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx index b87d5f3eb29..7352dafee7c 100644 --- a/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx +++ b/PWGUD/Tasks/upcQuarkoniaCentralBarrel.cxx @@ -230,7 +230,7 @@ struct upcQuarkoniaCentralBarrel { } template - void fillEventHistograms(TCollision collision, int& selGapSide) + void fillEventHistograms(const TCollision& collision, int& selGapSide) { // in case we want to push the analysis to Pb-Pb UPC int gapSide = collision.gapSide(); @@ -252,7 +252,7 @@ struct upcQuarkoniaCentralBarrel { } template - bool isTrackSelected(TTrack track) + bool isTrackSelected(const TTrack& track) { // // acceptance cut @@ -321,7 +321,7 @@ struct upcQuarkoniaCentralBarrel { } template - bool checkMCAssociation(TTrack track, TTrackMC trackMC) + bool checkMCAssociation(const TTrack& track, const TTrackMC& trackMC) // MC association (if asked) { if (track.sign() * trackMC.pdgCode() != 2212) @@ -332,7 +332,7 @@ struct upcQuarkoniaCentralBarrel { } template - void fillQAplot(TTrack track, bool afterSel = false) + void fillQAplot(const TTrack& track, bool afterSel = false) { // fill QA information about proton/antiproton track if (afterSel) { if (track.sign() > 0) { // Proton Candidates after selections @@ -395,7 +395,7 @@ struct upcQuarkoniaCentralBarrel { } template - void analyseTrackPairCandidate(TTrack proton, TTrack antiProton, TTrackMCs const& fullTrackMCs, uint8_t gapSide) + void analyseTrackPairCandidate(const TTrack& proton, const TTrack& antiProton, TTrackMCs const& fullTrackMCs, uint8_t gapSide) // fill information related to the quarkonium mother { float pt = RecoDecay::pt(proton.px() + antiProton.px(), proton.py() + antiProton.py()); @@ -477,7 +477,7 @@ struct upcQuarkoniaCentralBarrel { } template - void buildProtonAntiProtonPairs(TTracks const& fullTracks, TTrackMCs const& fullMCTracks, std::vector selProtonIndices, std::vector selAntiProtonIndices, uint8_t gapSide) + void buildProtonAntiProtonPairs(TTracks const& fullTracks, TTrackMCs const& fullMCTracks, const std::vector& selProtonIndices, const std::vector& selAntiProtonIndices, uint8_t gapSide) { // 1st loop over all protons for (const auto& proton : fullTracks) { @@ -538,13 +538,13 @@ struct upcQuarkoniaCentralBarrel { // Check the number of Protons and antiProtons // needs at least 1 of each if (!buildSameSignPairs && nProtons >= 1 && nAntiProtons >= 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selProtonIndices, selAntiProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selProtonIndices, selAntiProtonIndices, selGapSide); } if (buildSameSignPairs && nProtons > 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selProtonIndices, selProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selProtonIndices, selProtonIndices, selGapSide); } if (buildSameSignPairs && nAntiProtons > 1) { - buildProtonAntiProtonPairs(fullTracks, (TObject*)nullptr, selAntiProtonIndices, selAntiProtonIndices, selGapSide); + buildProtonAntiProtonPairs(fullTracks, nullptr, selAntiProtonIndices, selAntiProtonIndices, selGapSide); } } diff --git a/PWGUD/Tasks/upcRhoAnalysis.cxx b/PWGUD/Tasks/upcRhoAnalysis.cxx index 18c3eb5f08a..adb18292a7b 100644 --- a/PWGUD/Tasks/upcRhoAnalysis.cxx +++ b/PWGUD/Tasks/upcRhoAnalysis.cxx @@ -72,7 +72,11 @@ DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, float); DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, float); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(LocalBC, localBC, int); +DECLARE_SOA_COLUMN(KNoCollInTimeRangeStandard, kNoCollInTimeRangeStandard, bool); +DECLARE_SOA_COLUMN(KNoCollInRofStandard, kNoCollInRofStandard, bool); +DECLARE_SOA_COLUMN(KNoHighMultCollInPrevRof, kNoHighMultCollInPrevRof, bool); // FIT info DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); @@ -107,9 +111,12 @@ DECLARE_SOA_COLUMN(LeadingTrackKaPID, leadingTrackKaPID, float); DECLARE_SOA_COLUMN(SubleadingTrackKaPID, subleadingTrackKaPID, float); DECLARE_SOA_COLUMN(LeadingTrackPrPID, leadingTrackPrPID, float); DECLARE_SOA_COLUMN(SubleadingTrackPrPID, subleadingTrackPrPID, float); +DECLARE_SOA_COLUMN(LeadingTrackTofHit, leadingTrackTofHit, int); +DECLARE_SOA_COLUMN(SubleadingTrackTofHit, subleadingTrackTofHit, int); } // namespace reco_tree DECLARE_SOA_TABLE(RecoTree, "AOD", "RECOTREE", - reco_tree::RecoSetting, reco_tree::RunNumber, reco_tree::PosX, reco_tree::PosY, reco_tree::PosZ, reco_tree::OccupancyInTime, reco_tree::HadronicRate, reco_tree::LocalBC, + reco_tree::RecoSetting, reco_tree::RunNumber, reco_tree::PosX, reco_tree::PosY, reco_tree::PosZ, reco_tree::OccupancyInTime, reco_tree::HadronicRate, reco_tree::GlobalBC, reco_tree::LocalBC, + reco_tree::KNoCollInTimeRangeStandard, reco_tree::KNoCollInRofStandard, reco_tree::KNoHighMultCollInPrevRof, reco_tree::TotalFT0AmplitudeA, reco_tree::TotalFT0AmplitudeC, reco_tree::TotalFV0AmplitudeA, reco_tree::TotalFDDAmplitudeA, reco_tree::TotalFDDAmplitudeC, reco_tree::TimeFT0A, reco_tree::TimeFT0C, reco_tree::TimeFV0A, reco_tree::TimeFDDA, reco_tree::TimeFDDC, reco_tree::EnergyCommonZNA, reco_tree::EnergyCommonZNC, reco_tree::TimeZNA, reco_tree::TimeZNC, reco_tree::NeutronClass, @@ -120,8 +127,8 @@ DECLARE_SOA_TABLE(RecoTree, "AOD", "RECOTREE", reco_tree::LeadingTrackPiPID, reco_tree::SubleadingTrackPiPID, reco_tree::LeadingTrackElPID, reco_tree::SubleadingTrackElPID, reco_tree::LeadingTrackKaPID, reco_tree::SubleadingTrackKaPID, - reco_tree::LeadingTrackPrPID, reco_tree::SubleadingTrackPrPID); - + reco_tree::LeadingTrackPrPID, reco_tree::SubleadingTrackPrPID, + reco_tree::LeadingTrackTofHit, reco_tree::SubleadingTrackTofHit); namespace mc_tree { // misc event info @@ -129,6 +136,7 @@ DECLARE_SOA_COLUMN(RunNumber, runNumber, int); DECLARE_SOA_COLUMN(PosX, posX, float); DECLARE_SOA_COLUMN(PosY, posY, float); DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(GlobalBC, globalBC, uint64_t); DECLARE_SOA_COLUMN(LocalBC, localBC, int); // tracks DECLARE_SOA_COLUMN(LeadingTrackSign, leadingTrackSign, int); @@ -141,7 +149,7 @@ DECLARE_SOA_COLUMN(LeadingTrackPhi, leadingTrackPhi, float); DECLARE_SOA_COLUMN(SubleadingTrackPhi, subleadingTrackPhi, float); } // namespace mc_tree DECLARE_SOA_TABLE(McTree, "AOD", "MCTREE", - mc_tree::RunNumber, mc_tree::PosX, mc_tree::PosY, mc_tree::PosZ, mc_tree::LocalBC, + mc_tree::RunNumber, mc_tree::PosX, mc_tree::PosY, mc_tree::PosZ, mc_tree::GlobalBC, mc_tree::LocalBC, mc_tree::LeadingTrackSign, mc_tree::SubleadingTrackSign, mc_tree::LeadingTrackPt, mc_tree::SubleadingTrackPt, mc_tree::LeadingTrackEta, mc_tree::SubleadingTrackEta, @@ -149,6 +157,12 @@ DECLARE_SOA_TABLE(McTree, "AOD", "MCTREE", namespace resolution_tree { +// misc event info +DECLARE_SOA_COLUMN(GenGlobalBC, genGlobalBC, uint64_t); +DECLARE_SOA_COLUMN(GenLocalBC, genLocalBC, int); +DECLARE_SOA_COLUMN(RecoRunNumber, recoRunNumber, int); +DECLARE_SOA_COLUMN(RecoGlobalBC, recoGlobalBC, uint64_t); +DECLARE_SOA_COLUMN(RecoLocalBC, recoLocalBC, int); // vertex info DECLARE_SOA_COLUMN(GenPosX, genPosX, float); DECLARE_SOA_COLUMN(GenPosY, genPosY, float); @@ -156,6 +170,17 @@ DECLARE_SOA_COLUMN(GenPosZ, genPosZ, float); DECLARE_SOA_COLUMN(RecoPosX, recoPosX, float); DECLARE_SOA_COLUMN(RecoPosY, recoPosY, float); DECLARE_SOA_COLUMN(RecoPosZ, recoPosZ, float); +// FIT info +DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); +DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFDDAmplitudeA, totalFDDAmplitudeA, float); +DECLARE_SOA_COLUMN(TotalFDDAmplitudeC, totalFDDAmplitudeC, float); +DECLARE_SOA_COLUMN(TimeFT0A, timeFT0A, float); +DECLARE_SOA_COLUMN(TimeFT0C, timeFT0C, float); +DECLARE_SOA_COLUMN(TimeFV0A, timeFV0A, float); +DECLARE_SOA_COLUMN(TimeFDDA, timeFDDA, float); +DECLARE_SOA_COLUMN(TimeFDDC, timeFDDC, float); // track info DECLARE_SOA_COLUMN(LeadingSign, leadingSign, int); DECLARE_SOA_COLUMN(LeadingGenPt, leadingGenPt, float); @@ -164,6 +189,7 @@ DECLARE_SOA_COLUMN(LeadingGenPhi, leadingGenPhi, float); DECLARE_SOA_COLUMN(LeadingRecoPt, leadingRecoPt, float); DECLARE_SOA_COLUMN(LeadingRecoEta, leadingRecoEta, float); DECLARE_SOA_COLUMN(LeadingRecoPhi, leadingRecoPhi, float); +DECLARE_SOA_COLUMN(LeadingRecoTofHit, leadingRecoTofHit, int); DECLARE_SOA_COLUMN(SubleadingSign, subleadingSign, int); DECLARE_SOA_COLUMN(SubleadingGenPt, subleadingGenPt, float); DECLARE_SOA_COLUMN(SubleadingGenEta, subleadingGenEta, float); @@ -171,14 +197,20 @@ DECLARE_SOA_COLUMN(SubleadingGenPhi, subleadingGenPhi, float); DECLARE_SOA_COLUMN(SubleadingRecoPt, subleadingRecoPt, float); DECLARE_SOA_COLUMN(SubleadingRecoEta, subleadingRecoEta, float); DECLARE_SOA_COLUMN(SubleadingRecoPhi, subleadingRecoPhi, float); +DECLARE_SOA_COLUMN(SubleadingRecoTofHit, subleadingRecoTofHit, int); } // namespace resolution_tree DECLARE_SOA_TABLE(ResolutionTree, "AOD", "RESOLUTIONTREE", + resolution_tree::GenGlobalBC, resolution_tree::GenLocalBC, + resolution_tree::RecoRunNumber, + resolution_tree::RecoGlobalBC, resolution_tree::RecoLocalBC, resolution_tree::GenPosX, resolution_tree::GenPosY, resolution_tree::GenPosZ, resolution_tree::RecoPosX, resolution_tree::RecoPosY, resolution_tree::RecoPosZ, + resolution_tree::TotalFT0AmplitudeA, resolution_tree::TotalFT0AmplitudeC, resolution_tree::TotalFV0AmplitudeA, resolution_tree::TotalFDDAmplitudeA, resolution_tree::TotalFDDAmplitudeC, + resolution_tree::TimeFT0A, resolution_tree::TimeFT0C, resolution_tree::TimeFV0A, resolution_tree::TimeFDDA, resolution_tree::TimeFDDC, resolution_tree::LeadingSign, resolution_tree::LeadingGenPt, resolution_tree::LeadingGenEta, resolution_tree::LeadingGenPhi, - resolution_tree::LeadingRecoPt, resolution_tree::LeadingRecoEta, resolution_tree::LeadingRecoPhi, + resolution_tree::LeadingRecoPt, resolution_tree::LeadingRecoEta, resolution_tree::LeadingRecoPhi, resolution_tree::LeadingRecoTofHit, resolution_tree::SubleadingSign, resolution_tree::SubleadingGenPt, resolution_tree::SubleadingGenEta, resolution_tree::SubleadingGenPhi, - resolution_tree::SubleadingRecoPt, resolution_tree::SubleadingRecoEta, resolution_tree::SubleadingRecoPhi); + resolution_tree::SubleadingRecoPt, resolution_tree::SubleadingRecoEta, resolution_tree::SubleadingRecoPhi, resolution_tree::SubleadingRecoTofHit); } // namespace o2::aod struct UpcRhoAnalysis { @@ -188,9 +220,9 @@ struct UpcRhoAnalysis { SGSelector sgSelector; - const float pcEtaCut = 0.9; // physics coordination recommendation - const int nPions = 2; // only study dipion final states - const std::vector runNumbers = {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544911, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544991, 544992, 545004, 545008, 545009, 545041, 545042, 545044, 545047, 545060, 545062, 545063, 545064, 545066, 545086, 545103, 545117, 545171, 545184, 545185, 545210, 545222, 545223, 545246, 545249, 545262, 545289, 545291, 545294, 545295, 545296, 545311, 545312, 545332, 545345, 545367}; + const float pcEtaCut = 0.9; // physics coordination recommendation + const int nExpectedPions = 2; // only study dipion final states + const std::vector runNumbers = {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, 545296, 545312}; AxisSpec runNumberAxis = {static_cast(runNumbers.size()), 0.5, static_cast(runNumbers.size()) + 0.5, "run number"}; Configurable isPO{"isPO", false, "processing p-O data?"}; @@ -206,8 +238,8 @@ struct UpcRhoAnalysis { Configurable requireTof{"requireTof", false, "require TOF signal?"}; Configurable useRecoFlag{"useRecoFlag", false, "use UPC/STD reconstruction flag for event selection?"}; Configurable cutRecoFlag{"cutRecoFlag", 1, "0 = std mode, 1 = upc mode"}; - Configurable useRctFlag{"useRctFlag", false, "use RCT flags for event selection?"}; - Configurable cutRctFlag{"cutRctFlag", 0, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; + Configurable useRctFlag{"useRctFlag", true, "use RCT flags for event selection?"}; + Configurable cutRctFlag{"cutRctFlag", 4, "0 = off, 1 = CBT, 2 = CBT+ZDC, 3 = CBThadron, 4 = CBThadron+ZDC"}; Configurable selectRuns{"selectRuns", false, "select runs?"}; Configurable> selectedRuns{"selectedRuns", {544013, 544028, 544032, 544091, 544095, 544098, 544116, 544121, 544122, 544123, 544124, 544184, 544185, 544389, 544390, 544391, 544392, 544451, 544454, 544474, 544475, 544476, 544477, 544490, 544491, 544492, 544508, 544510, 544511, 544512, 544514, 544515, 544518, 544548, 544549, 544550, 544551, 544564, 544565, 544567, 544568, 544580, 544582, 544583, 544585, 544614, 544640, 544652, 544653, 544672, 544674, 544692, 544693, 544694, 544696, 544739, 544742, 544754, 544767, 544794, 544795, 544797, 544813, 544868, 544886, 544887, 544896, 544913, 544914, 544917, 544931, 544947, 544961, 544963, 544964, 544968, 544992, 545009, 545044, 545047, 545063, 545064, 545066, 545185, 545210, 545223, 545249, 545291, 545294, 545295, 545296, 545312}, "list of selected runs"}; @@ -230,11 +262,12 @@ struct UpcRhoAnalysis { Configurable tracksMaxTpcChi2NClCut{"tracksMaxTpcChi2NClCut", 3.0, "max TPC chi2/Ncls cut"}; Configurable tracksMinTpcNClsCrossedOverFindableCut{"tracksMinTpcNClsCrossedOverFindableCut", 1.0, "min TPC crossed rows / findable clusters cut"}; Configurable tracksMinPtCut{"tracksMinPtCut", 0.1, "min pT cut on tracks"}; + Configurable applyPid{"applyPid", true, "apply PID before creating derived data?"}; Configurable systemMassMinCut{"systemMassMinCut", 0.5, "min M cut for reco system"}; Configurable systemMassMaxCut{"systemMassMaxCut", 1.0, "max M cut for reco system"}; Configurable systemPtCut{"systemPtCut", 0.1, "max pT cut for reco system"}; - Configurable systemYCut{"systemYCut", 0.9, "rapiditiy cut for reco system"}; + Configurable systemYCut{"systemYCut", 0.5, "rapiditiy cut for reco system"}; ConfigurableAxis mAxis{"mAxis", {400, 0.0, 4.0}, "#it{m} (GeV/#it{c}^{2})"}; ConfigurableAxis ptAxis{"ptAxis", {400, 0.0, 4.0}, "#it{p}_{T} (GeV/#it{c})"}; @@ -256,13 +289,15 @@ struct UpcRhoAnalysis { void init(o2::framework::InitContext& context) { - if (context.mOptions.get("processSGdata") || context.mOptions.get("processDGdata")) { - // QA // + if (context.mOptions.get("processSGdata") || context.mOptions.get("processDGdata") || context.mOptions.get("processMcRecoWithTruth")) { + // QA // collisions rQC.add("QC/collisions/all/hPosXY", ";vertex #it{x} (cm);vertex #it{y} (cm);counts", kTH2D, {{2000, -0.1, 0.1}, {2000, -0.1, 0.1}}); rQC.add("QC/collisions/all/hPosZ", ";vertex #it{z} (cm);counts", kTH1D, {{400, -20.0, 20.0}}); rQC.add("QC/collisions/all/hNumContrib", ";number of PV contributors;counts", kTH1D, {{36, -0.5, 35.5}}); rQC.add("QC/collisions/all/hZdcCommonEnergy", ";ZNA common energy (TeV);ZNC common energy (TeV);counts", kTH2D, {znCommonEnergyAxis, znCommonEnergyAxis}); + rQC.add("QC/collisions/all/hZnaCommonEnergyPerRun", ";ZNA common energy (TeV);run number;counts", kTH2D, {znCommonEnergyAxis, runNumberAxis}); + rQC.add("QC/collisions/all/hZncCommonEnergyPerRun", ";ZNC common energy (TeV);run number;counts", kTH2D, {znCommonEnergyAxis, runNumberAxis}); rQC.add("QC/collisions/all/hZdcTime", ";ZNA time (ns);ZNC time (ns);counts", kTH2D, {znTimeAxis, znTimeAxis}); rQC.add("QC/collisions/all/hZNTimeVsZNCommonEnergy", ";ZNA/C common energy (TeV);ZNA/C time (ns);counts", kTH2D, {znCommonEnergyAxis, znTimeAxis}); rQC.add("QC/collisions/all/hTotalFT0AmplitudeA", ";FT0A amplitude;counts", kTH1D, {{160, 0.0, 160.0}}); @@ -276,11 +311,17 @@ struct UpcRhoAnalysis { rQC.add("QC/collisions/all/hTimeFDDA", ";FDDA time (ns);counts", kTH1D, {{400, -5.0, 35.0}}); rQC.add("QC/collisions/all/hTimeFDDC", ";FDDC time (ns);counts", kTH1D, {{400, -5.0, 35.0}}); rQC.add("QC/collisions/all/hOccupancyInTime", ";occupancy in time;counts", kTH1D, {{1100, 0.0, 1100.0}}); + rQC.add("QC/collisions/all/hRecoMode", ";reconstruction mode;counts", kTH1D, {{2, -0.5, 1.5}}); + rQC.add("QC/collisions/hNumContribVsPVTracks", ";number of track.isPVContributor() per collision;collision.numContrib();counts", kTH2D, {{101, -0.5, 100.5}, {101, -0.5, 100.5}}); + rQC.add("QC/collisions/hLocalBC", ";BC ID;counts", kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, static_cast(o2::constants::lhc::LHCMaxBunches) - 0.5}}); + rQC.add("QC/collisions/all/hkNoCollInTimeRangeStandard", ";kNoCollInTimeRangeStandard;counts", kTH1D, {{2, -0.5, 1.5}}); + rQC.add("QC/collisions/all/hkNoCollInRofStandard", ";kNoCollInRofStandard;counts", kTH1D, {{2, -0.5, 1.5}}); + rQC.add("QC/collisions/all/hkNoHighMultCollInPrevRof", ";kNoHighMultCollInPrevRof;counts", kTH1D, {{2, -0.5, 1.5}}); // events with selected rho candidates rQC.addClone("QC/collisions/all/", "QC/collisions/trackSelections/"); rQC.addClone("QC/collisions/all/", "QC/collisions/systemSelections/"); - std::vector collisionSelectionCounterLabels = {"all collisions", "rapidity gap", "ITS-TPC vertex", "same bunch pile-up", "ITS ROF border", "TF border", "#it{z} position", "number of contributors", "RCT selections", "reco flag selection", "occupancy selection"}; + std::vector collisionSelectionCounterLabels = {"all collisions", "rapidity gap", "ITS-TPC vertex", "same bunch pile-up", "ITS ROF border", "TF border", "vertex #it{z} position", "number of contributors", "RCT selections", "occupancy selection"}; rQC.add("QC/collisions/hSelectionCounter", ";;collisions passing selections", kTH1D, {{static_cast(collisionSelectionCounterLabels.size()), -0.5, static_cast(collisionSelectionCounterLabels.size()) - 0.5}}); rQC.add("QC/collisions/hSelectionCounterPerRun", ";;run number;collisions passing selections", kTH2D, {{static_cast(collisionSelectionCounterLabels.size()), -0.5, static_cast(collisionSelectionCounterLabels.size()) - 0.5}, runNumberAxis}); for (int i = 0; i < static_cast(collisionSelectionCounterLabels.size()); ++i) { @@ -294,6 +335,7 @@ struct UpcRhoAnalysis { rQC.add("QC/tracks/all/hTpcNSigmaEl", ";TPC #it{n#sigma}(e);counts", kTH1D, {nSigmaAxis}); rQC.add("QC/tracks/all/hTpcNSigmaKa", ";TPC #it{n#sigma}(K);counts", kTH1D, {nSigmaAxis}); rQC.add("QC/tracks/all/hTpcNSigmaPr", ";TPC #it{n#sigma}(p);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTpcNSigmaMu", ";TPC #it{n#sigma}(#mu);counts", kTH1D, {nSigmaAxis}); rQC.add("QC/tracks/all/hDcaXYZ", ";track #it{DCA}_{z} (cm);track #it{DCA}_{xy} (cm);counts", kTH2D, {{1000, -5.0, 5.0}, {400, -2.0, 2.0}}); rQC.add("QC/tracks/all/hItsNCls", ";ITS #it{N}_{cls};counts", kTH1D, {{9, -0.5, 8.5}}); rQC.add("QC/tracks/all/hItsChi2NCl", ";ITS #it{#chi}^{2}/#it{N}_{cls};counts", kTH1D, {{150, 0.0, 15.0}}); @@ -306,18 +348,31 @@ struct UpcRhoAnalysis { rQC.add("QC/tracks/all/hPhi", ";#it{#phi} (rad);counts", kTH1D, {phiAxis}); rQC.add("QC/tracks/all/hTpcSignalVsP", ";|#it{p}| (GeV/#it{c});TPC d#it{E}/d#it{x} signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); rQC.add("QC/tracks/all/hTpcSignalVsPt", ";#it{p}_{T} (GeV/#it{c});TPC d#it{E}/d#it{x} signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hLength", ";track length (cm);counts", kTH1D, {{1000, 0.0, 1000.0}}); + rQC.add("QC/tracks/all/hTofChi2", ";TOF #it{#chi}^{2};counts", kTH1D, {{100, 0.0, 10.0}}); + rQC.add("QC/tracks/all/hTofSignalVsP", ";|#it{p}| (GeV/#it{c});TOF signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hTofSignalVsPt", ";#it{p}_{T} (GeV/#it{c});TOF signal (arb. units);counts", kTH2D, {ptAxis, {500, 0.0, 500.0}}); + rQC.add("QC/tracks/all/hTofExpMom", ";expected momentum from TOF (GeV/#it{c});counts", kTH1D, {ptAxis}); + rQC.add("QC/tracks/all/hBeta", ";TOF #it{#beta};counts", kTH1D, {{1000, 0.0, 1.0}}); + rQC.add("QC/tracks/all/hBetaError", ";TOF #it{#beta} error;counts", kTH1D, {{1000, 0.0, 1.0}}); + rQC.add("QC/tracks/all/hTofNSigmaPi", ";TOF #it{n#sigma}(#pi);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaEl", ";TOF #it{n#sigma}(e);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaKa", ";TOF #it{n#sigma}(K);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaPr", ";TOF #it{n#sigma}(p);counts", kTH1D, {nSigmaAxis}); + rQC.add("QC/tracks/all/hTofNSigmaMu", ";TOF #it{n#sigma}(#mu);counts", kTH1D, {nSigmaAxis}); // tracks passing selections rQC.addClone("QC/tracks/all/", "QC/tracks/trackSelections/"); rQC.addClone("QC/tracks/all/", "QC/tracks/systemSelections/"); rQC.add("QC/tracks/trackSelections/hRemainingTracks", ";remaining tracks;counts", kTH1D, {{21, -0.5, 20.5}}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaPi2D", ";TPC #it{n#sigma}(#pi)_{leading};TPC #it{n#sigma}(#pi)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); + rQC.add("QC/tracks/trackSelections/hTpcNSigmaPi2DAfterCut", ";TPC #it{n#sigma}(#pi)_{leading};TPC #it{n#sigma}(#pi)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaEl2D", ";TPC #it{n#sigma}(e)_{leading};TPC #it{n#sigma}(e)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaKa2D", ";TPC #it{n#sigma}(K)_{leading};TPC #it{n#sigma}(K)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); rQC.add("QC/tracks/trackSelections/hTpcNSigmaPr2D", ";TPC #it{n#sigma}(p)_{leading};TPC #it{n#sigma}(p)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); + rQC.add("QC/tracks/trackSelections/hTpcNSigmaMu2D", ";TPC #it{n#sigma}(#mu)_{leading};TPC #it{n#sigma}(#mu)_{subleading};counts", kTH2D, {nSigmaAxis, nSigmaAxis}); // selection counter std::vector trackSelectionCounterLabels = {"all tracks", "PV contributor", "ITS hit", "ITS #it{N}_{cls}", "itsClusterMap check", "ITS #it{#chi}^{2}/#it{N}_{cls}", "TPC hit", "found TPC #it{N}_{cls}", "TPC #it{#chi}^{2}/#it{N}_{cls}", "TPC crossed rows", "TPC crossed rows/#it{N}_{cls}", - "TOF requirement", "#it{p}_{T}", "#it{DCA}", "#it{#eta}", "exactly 2 tracks", "PID"}; rQC.add("QC/tracks/hSelectionCounter", ";;tracks passing selections", kTH1D, {{static_cast(trackSelectionCounterLabels.size()), -0.5, static_cast(trackSelectionCounterLabels.size()) - 0.5}}); rQC.add("QC/tracks/hSelectionCounterPerRun", ";;run number;tracks passing selections", kTH2D, {{static_cast(trackSelectionCounterLabels.size()), -0.5, static_cast(trackSelectionCounterLabels.size()) - 0.5}, runNumberAxis}); @@ -351,6 +406,7 @@ struct UpcRhoAnalysis { rSystem.add("system/all/unlike-sign/hM", ";#it{m} (GeV/#it{c}^{2});counts", kTH1D, {mAxis}); rSystem.add("system/all/unlike-sign/hRecoSettingVsM", ";#it{m} (GeV/#it{c}^{2});reco setting;counts", kTH2D, {mAxis, {2, -0.5, 1.5}}); rSystem.add("system/all/unlike-sign/hPt", ";#it{p}_{T} (GeV/#it{c});counts", kTH1D, {ptAxis}); + rSystem.add("system/all/unlike-sign/hPtReweighting", ";#it{p}_{T} (GeV/#it{c});counts", kTH1D, {{40, 0.0, 0.2}}); rSystem.add("system/all/unlike-sign/hPt2", ";#it{p}_{T}^{2} (GeV^{2}/#it{c}^{2});counts", kTH1D, {pt2Axis}); rSystem.add("system/all/unlike-sign/hPtVsM", ";#it{m} (GeV/#it{c}^{2});#it{p}_{T} (GeV/#it{c});counts", kTH2D, {mAxis, ptAxis}); rSystem.add("system/all/unlike-sign/hPt2VsM", ";#it{m} (GeV/#it{c}^{2});#it{p}_{T}^{2} (GeV^{2}/#it{c}^{2});counts", kTH2D, {mAxis, pt2Axis}); @@ -372,7 +428,7 @@ struct UpcRhoAnalysis { rSystem.addClone("system/selected/AnAn/", "system/selected/XnXn/"); } - if (context.mOptions.get("processMCdata") || context.mOptions.get("processMCdataWithBCs")) { + if (context.mOptions.get("processMCdata") || context.mOptions.get("processMCdataWithBCs") || context.mOptions.get("processMcRecoWithTruth")) { // MC // collisions rMC.add("MC/collisions/hPosXY", ";vertex #it{x} (cm);vertex #it{y} (cm);counts", kTH2D, {{2000, -0.1, 0.1}, {2000, -0.1, 0.1}}); @@ -407,6 +463,8 @@ struct UpcRhoAnalysis { rMC.add("MC/system/hPhiCharge", ";#Delta#it{#phi}_{charge} (rad);counts", kTH1D, {deltaPhiAxis}); rMC.add("MC/system/hPhiRandomVsM", ";#it{m} (GeV/#it{c}^{2});#Delta#it{#phi} (rad);counts", kTH2D, {mAxis, deltaPhiAxis}); rMC.add("MC/system/hPhiChargeVsM", ";#it{m} (GeV/#it{c}^{2});#Delta#it{#phi} (rad);counts", kTH2D, {mAxis, deltaPhiAxis}); + rMC.add("MC/system/hPhiRandomVsPt", ";#it{p}_{T} (GeV/#it{c});#Delta#it{#phi} (rad);counts", kTH2D, {ptAxis, deltaPhiAxis}); + rMC.add("MC/system/hPhiChargeVsPt", ";#it{p}_{T} (GeV/#it{c});#Delta#it{#phi} (rad);counts", kTH2D, {ptAxis, deltaPhiAxis}); rMC.addClone("MC/system/", "MC/system/selected/"); } @@ -428,9 +486,11 @@ struct UpcRhoAnalysis { rResolution.add("MC/resolution/system/1D/hM", ";#it{m}_{reco} - #it{m}_{true} (GeV/#it{c}^{2});counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hMVsM", ";#it{m}_{true} (GeV/#it{c}^{2});#it{m}_{reco} (GeV/#it{c}^{2});counts", kTH2D, {mAxis, mAxis}); rResolution.add("MC/resolution/system/1D/hPt", ";1/#it{p}_{T, reco} - 1/#it{p}_{T, true} (1/(GeV/#it{c}));counts", kTH1D, {resolutionAxis}); - rResolution.add("MC/resolution/system/2D/hPtVsPt", ";1/#it{p}_{T, true} (GeV/#it{c});#it{p}_{T, reco} (GeV/#it{c});counts", kTH2D, {ptAxis, ptAxis}); + rResolution.add("MC/resolution/system/2D/hPtVsPt", ";#it{p}_{T, true} (GeV/#it{c});#it{p}_{T, reco} (GeV/#it{c});counts", kTH2D, {ptAxis, ptAxis}); rResolution.add("MC/resolution/system/1D/hY", ";#it{y}_{reco} - #it{y}_{true};counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hYVsY", ";#it{y}_{true};#it{y}_{reco};counts", kTH2D, {yAxis, yAxis}); + rResolution.add("MC/resolution/system/1D/hPhi", ";#it{#phi}_{reco} - #it{#phi}_{true} (rad);counts", kTH1D, {resolutionAxis}); + rResolution.add("MC/resolution/system/2D/hPhiVsPhi", ";#it{#phi}_{true} (rad);#it{#phi}_{reco} (rad);counts", kTH2D, {phiAxis, phiAxis}); rResolution.add("MC/resolution/system/1D/hDeltaPhi", ";#Delta#it{#phi}_{reco} - #Delta#it{#phi}_{true} (rad);counts", kTH1D, {resolutionAxis}); rResolution.add("MC/resolution/system/2D/hDeltaPhiVsDeltaPhi", ";#Delta#it{#phi}_{true} (rad);#Delta#it{#phi}_{reco} (rad);counts", kTH2D, {deltaPhiAxis, deltaPhiAxis}); } @@ -441,11 +501,13 @@ struct UpcRhoAnalysis { static constexpr std::string_view NeutronClass[5] = {"AnAn/", "0n0n/", "Xn0n/", "0nXn/", "XnXn/"}; template - void fillCollisionQcHistos(const C& collision) // fills collision QC histograms before/after cuts + void fillCollisionQcHistos(const C& collision, int runIndex) // fills collision QC histograms before/after cuts { rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hPosXY"), collision.posX(), collision.posY()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hPosZ"), collision.posZ()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZdcCommonEnergy"), collision.energyCommonZNA(), collision.energyCommonZNC()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZnaCommonEnergyPerRun"), collision.energyCommonZNA(), runIndex); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZncCommonEnergyPerRun"), collision.energyCommonZNC(), runIndex); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZdcTime"), collision.timeZNA(), collision.timeZNC()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZNTimeVsZNCommonEnergy"), collision.energyCommonZNA(), collision.timeZNA()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hZNTimeVsZNCommonEnergy"), collision.energyCommonZNC(), collision.timeZNC()); @@ -461,6 +523,10 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hTimeFDDA"), collision.timeFDDA()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hTimeFDDC"), collision.timeFDDC()); rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hOccupancyInTime"), collision.occupancyInTime()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hRecoMode"), collision.flags()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoCollInTimeRangeStandard"), collision.trs()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoCollInRofStandard"), collision.trofs()); + rQC.fill(HIST("QC/collisions/") + HIST(AppliedSelections[cuts]) + HIST("hkNoHighMultCollInPrevRof"), collision.hmpr()); } template @@ -473,6 +539,7 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaEl"), track.tpcNSigmaEl()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaKa"), track.tpcNSigmaKa()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaPr"), track.tpcNSigmaPr()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNSigmaMu"), track.tpcNSigmaMu()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hDcaXYZ"), track.dcaZ(), track.dcaXY()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hItsNCls"), track.itsNCls()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hItsChi2NCl"), track.itsChi2NCl()); @@ -482,6 +549,18 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcNClsCrossedRowsOverNClsFindable"), (static_cast(track.tpcNClsCrossedRows()) / static_cast(track.tpcNClsFindable()))); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcSignalVsP"), std::abs(momentum(track.px(), track.py(), track.pz())), track.tpcSignal()); rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTpcSignalVsPt"), track.pt(), track.tpcSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hLength"), track.length()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofChi2"), track.tofChi2()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofSignalVsP"), std::abs(momentum(track.px(), track.py(), track.pz())), track.tofSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofSignalVsPt"), track.pt(), track.tofSignal()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofExpMom"), track.tofExpMom()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hBeta"), track.beta()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hBetaError"), track.betaerror()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaPi"), track.tofNSigmaPi()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaEl"), track.tofNSigmaEl()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaKa"), track.tofNSigmaKa()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaPr"), track.tofNSigmaPr()); + rQC.fill(HIST("QC/tracks/") + HIST(AppliedSelections[cuts]) + HIST("hTofNSigmaMu"), track.tofNSigmaMu()); } template @@ -498,6 +577,7 @@ struct UpcRhoAnalysis { if (cuts == 0) { rSystem.fill(HIST("system/") + HIST(AppliedSelections[cuts]) + HIST(ChargeLabel[charge]) + HIST("hM"), mass); rSystem.fill(HIST("system/") + HIST(AppliedSelections[cuts]) + HIST(ChargeLabel[charge]) + HIST("hPt"), pt); + rSystem.fill(HIST("system/") + HIST(AppliedSelections[cuts]) + HIST(ChargeLabel[charge]) + HIST("hPtReweighting"), pt); rSystem.fill(HIST("system/") + HIST(AppliedSelections[cuts]) + HIST(ChargeLabel[charge]) + HIST("hPt2"), pt * pt); rSystem.fill(HIST("system/") + HIST(AppliedSelections[cuts]) + HIST(ChargeLabel[charge]) + HIST("hPtVsM"), mass, pt); rSystem.fill(HIST("system/") + HIST(AppliedSelections[cuts]) + HIST(ChargeLabel[charge]) + HIST("hPt2VsM"), mass, pt * pt); @@ -510,6 +590,7 @@ struct UpcRhoAnalysis { } else { rSystem.fill(HIST("system/") + HIST("selected/") + HIST(NeutronClass[neutronClass]) + HIST(ChargeLabel[charge]) + HIST("hM"), mass); rSystem.fill(HIST("system/") + HIST("selected/") + HIST(NeutronClass[neutronClass]) + HIST(ChargeLabel[charge]) + HIST("hPt"), pt); + rSystem.fill(HIST("system/") + HIST("selected/") + HIST(NeutronClass[neutronClass]) + HIST(ChargeLabel[charge]) + HIST("hPtReweighting"), pt); rSystem.fill(HIST("system/") + HIST("selected/") + HIST(NeutronClass[neutronClass]) + HIST(ChargeLabel[charge]) + HIST("hPt2"), pt * pt); rSystem.fill(HIST("system/") + HIST("selected/") + HIST(NeutronClass[neutronClass]) + HIST(ChargeLabel[charge]) + HIST("hPtVsM"), mass, pt); rSystem.fill(HIST("system/") + HIST("selected/") + HIST(NeutronClass[neutronClass]) + HIST(ChargeLabel[charge]) + HIST("hPt2VsM"), mass, pt * pt); @@ -600,18 +681,11 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 8, runIndex); } - if (useRecoFlag) { - if (collision.flags() != cutRecoFlag) // check reconstruction mode - return false; - rQC.fill(HIST("QC/collisions/hSelectionCounter"), 9); - rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 9, runIndex); - } - if (cutOccupancy) { if (collision.occupancyInTime() > occupancyCut) return false; - rQC.fill(HIST("QC/collisions/hSelectionCounter"), 10); - rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 10, runIndex); + rQC.fill(HIST("QC/collisions/hSelectionCounter"), 9); + rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 9, runIndex); } // if all selections passed @@ -674,28 +748,21 @@ struct UpcRhoAnalysis { rQC.fill(HIST("QC/tracks/hSelectionCounter"), 10); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 10, runIndex); - if (requireTof) { - if (!track.hasTOF()) - return false; - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 11); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 11, runIndex); - } - if (track.pt() < tracksMinPtCut) return false; + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 11); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 11, runIndex); + + if (std::abs(track.dcaZ()) > tracksDcaMaxCut || std::abs(track.dcaXY()) > (0.0105 + 0.0350 / std::pow(track.pt(), 1.1))) + return false; rQC.fill(HIST("QC/tracks/hSelectionCounter"), 12); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 12, runIndex); - if (std::abs(track.dcaZ()) > tracksDcaMaxCut || std::abs(track.dcaXY()) > (0.0105 + 0.0350 / std::pow(track.pt(), 1.01))) + if (std::abs(eta(track.px(), track.py(), track.pz())) > pcEtaCut) return false; rQC.fill(HIST("QC/tracks/hSelectionCounter"), 13); rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 13, runIndex); - if (std::abs(eta(track.px(), track.py(), track.pz())) > pcEtaCut) - return false; - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 14); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 14, runIndex); - // if all selections passed return true; } @@ -800,11 +867,11 @@ struct UpcRhoAnalysis { // function to obtain index of run from the run number vector // search for passed run number in the vector and return its index +1 to use in the filling of a histogram - int getRunIndex(int runNumber, const std::vector& runNumbers) + int getRunIndex(int runNumber, const std::vector& runNumberList) { - auto it = std::find(runNumbers.begin(), runNumbers.end(), runNumber); - if (it != runNumbers.end()) { - return std::distance(runNumbers.begin(), it) + 1; // +1 to avoid 0 bin in histogram + auto it = std::find(runNumberList.begin(), runNumberList.end(), runNumber); + if (it != runNumberList.end()) { + return std::distance(runNumberList.begin(), it) + 1; // +1 to avoid 0 bin in histogram } else { return 0; // return 0 if run number not found } @@ -817,7 +884,16 @@ struct UpcRhoAnalysis { if (selectRuns && getRunIndex(collision.runNumber(), selectedRuns) == 0) return; - fillCollisionQcHistos<0>(collision); // fill QC histograms before cuts + // check the number of PV tracks and the number of PV contrubutors + int nPVTracks = 0; + for (const auto& track : tracks) { + if (track.isPVContributor()) + nPVTracks++; + } + rQC.fill(HIST("QC/collisions/hNumContribVsPVTracks"), nPVTracks, collision.numContrib()); + rQC.fill(HIST("QC/collisions/hLocalBC"), collision.globalBC() % o2::constants::lhc::LHCMaxBunches); + + fillCollisionQcHistos<0>(collision, runIndex); // fill QC histograms before cuts if (!collisionPassesCuts(collision, runIndex)) // apply collision cuts return; @@ -861,16 +937,12 @@ struct UpcRhoAnalysis { } rQC.fill(HIST("QC/tracks/trackSelections/hRemainingTracks"), cutTracks.size()); - if (static_cast(cutTracks.size()) != nPions) // further consider only two pion systems + if (static_cast(cutTracks.size()) != nExpectedPions) // further consider only two pion systems return; - for (int i = 0; i < nPions; i++) { - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 15); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 15, runIndex); + for (int i = 0; i < nExpectedPions; i++) { + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 14); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 14, runIndex); } - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2D"), cutTracks[0].tpcNSigmaPi(), cutTracks[1].tpcNSigmaPi()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaEl2D"), cutTracks[0].tpcNSigmaEl(), cutTracks[1].tpcNSigmaEl()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaKa2D"), cutTracks[0].tpcNSigmaKa(), cutTracks[1].tpcNSigmaKa()); - rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPr2D"), cutTracks[0].tpcNSigmaPr(), cutTracks[1].tpcNSigmaPr()); // create a vector of 4-vectors for selected tracks std::vector cutTracksLVs; @@ -882,6 +954,12 @@ struct UpcRhoAnalysis { auto leadingTrack = momentum(cutTracks[0].px(), cutTracks[0].py(), cutTracks[0].pz()) > momentum(cutTracks[1].px(), cutTracks[1].py(), cutTracks[1].pz()) ? cutTracks[0] : cutTracks[1]; auto subleadingTrack = (leadingTrack == cutTracks[0]) ? cutTracks[1] : cutTracks[0]; + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2D"), leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaEl2D"), leadingTrack.tpcNSigmaEl(), subleadingTrack.tpcNSigmaEl()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaKa2D"), leadingTrack.tpcNSigmaKa(), subleadingTrack.tpcNSigmaKa()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPr2D"), leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr()); + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaMu2D"), leadingTrack.tpcNSigmaMu(), subleadingTrack.tpcNSigmaMu()); + float leadingPt = leadingTrack.pt(); float subleadingPt = subleadingTrack.pt(); float leadingEta = eta(leadingTrack.px(), leadingTrack.py(), leadingTrack.pz()); @@ -891,8 +969,12 @@ struct UpcRhoAnalysis { float phiRandom = getPhiRandom(cutTracksLVs); float phiCharge = getPhiCharge(cutTracks, cutTracksLVs); + if (applyPid && !tracksPassPID(cutTracks)) // apply PID cut before creating derived data if desired + return; + // fill recoTree - recoTree(collision.flags(), collision.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.occupancyInTime(), collision.hadronicRate(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + recoTree(collision.flags(), collision.runNumber(), collision.posX(), collision.posY(), collision.posZ(), collision.occupancyInTime(), collision.hadronicRate(), collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.trs(), collision.trofs(), collision.hmpr(), collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), energyCommonZNA, energyCommonZNC, timeZNA, timeZNC, neutronClass, @@ -903,18 +985,20 @@ struct UpcRhoAnalysis { leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi(), leadingTrack.tpcNSigmaEl(), subleadingTrack.tpcNSigmaEl(), leadingTrack.tpcNSigmaKa(), subleadingTrack.tpcNSigmaKa(), - leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr()); + leadingTrack.tpcNSigmaPr(), subleadingTrack.tpcNSigmaPr(), + leadingTrack.hasTOF(), subleadingTrack.hasTOF()); if (!tracksPassPID(cutTracks)) // apply PID cut return; + rQC.fill(HIST("QC/tracks/trackSelections/hTpcNSigmaPi2DAfterCut"), leadingTrack.tpcNSigmaPi(), subleadingTrack.tpcNSigmaPi()); for (const auto& cutTrack : cutTracks) { - rQC.fill(HIST("QC/tracks/hSelectionCounter"), 16); - rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 16, runIndex); + rQC.fill(HIST("QC/tracks/hSelectionCounter"), 15); + rQC.fill(HIST("QC/tracks/hSelectionCounterPerRun"), 15, runIndex); fillTrackQcHistos<1>(cutTrack); // fill QC histograms after cuts } rQC.fill(HIST("QC/tracks/hTofHitCheck"), leadingTrack.hasTOF(), subleadingTrack.hasTOF()); - fillCollisionQcHistos<1>(collision); // fill QC histograms after track selections + fillCollisionQcHistos<1>(collision, runIndex); // fill QC histograms after track selections ROOT::Math::PxPyPzMVector system = reconstructSystem(cutTracksLVs); int totalCharge = tracksTotalCharge(cutTracks); @@ -952,7 +1036,7 @@ struct UpcRhoAnalysis { // fill histograms for system passing cuts switch (totalCharge) { case 0: - fillCollisionQcHistos<2>(collision); + fillCollisionQcHistos<2>(collision, runIndex); for (const auto& cutTrack : cutTracks) fillTrackQcHistos<2>(cutTrack); fillTrack2dHistos<2, 0>(leadingPt, subleadingPt, leadingEta, subleadingEta, leadingPhi, subleadingPhi); @@ -1046,7 +1130,7 @@ struct UpcRhoAnalysis { } rMC.fill(HIST("MC/collisions/hNPions"), cutMcParticles.size()); - if (static_cast(cutMcParticles.size()) != 2) + if (static_cast(cutMcParticles.size()) != nExpectedPions) // further consider only two pion systems return; if (mcParticlesLVs.size() != cutMcParticles.size()) // sanity check return; @@ -1078,6 +1162,8 @@ struct UpcRhoAnalysis { rMC.fill(HIST("MC/system/hPhiCharge"), phiCharge); rMC.fill(HIST("MC/system/hPhiRandomVsM"), mass, phiRandom); rMC.fill(HIST("MC/system/hPhiChargeVsM"), mass, phiCharge); + rMC.fill(HIST("MC/system/hPhiRandomVsPt"), pT, phiRandom); + rMC.fill(HIST("MC/system/hPhiChargeVsPt"), pT, phiCharge); if (systemPassesCuts(system)) { rMC.fill(HIST("MC/system/selected/hM"), mass); @@ -1093,7 +1179,7 @@ struct UpcRhoAnalysis { } // fill mcTree - mcTree(runNumber, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcTree(runNumber, mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, leadingPion.pdgCode() / std::abs(leadingPion.pdgCode()), subleadingPion.pdgCode() / std::abs(subleadingPion.pdgCode()), pt(leadingPion.px(), leadingPion.py()), pt(subleadingPion.px(), subleadingPion.py()), eta(leadingPion.px(), leadingPion.py(), leadingPion.pz()), eta(subleadingPion.px(), subleadingPion.py(), subleadingPion.pz()), @@ -1126,6 +1212,8 @@ struct UpcRhoAnalysis { void processDGdata(FullUdDgCollision const& collision, FullUdTracks const& tracks) { int runIndex = getRunIndex(collision.runNumber(), runNumbers); + rQC.fill(HIST("QC/collisions/hSelectionCounter"), 0); // all collisions + rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 0, runIndex); rQC.fill(HIST("QC/collisions/hSelectionCounter"), 1); // no single-gap collisions in dataset rQC.fill(HIST("QC/collisions/hSelectionCounterPerRun"), 1, runIndex); @@ -1182,7 +1270,7 @@ struct UpcRhoAnalysis { recoTracks.push_back(track); } - if (truePionLVs.size() != 2 || recoPionLVs.size() != 2) + if (static_cast(truePionLVs.size()) != nExpectedPions || static_cast(recoPionLVs.size()) != nExpectedPions) return; ROOT::Math::PxPyPzMVector trueSystem = reconstructSystem(truePionLVs); @@ -1196,6 +1284,8 @@ struct UpcRhoAnalysis { rResolution.fill(HIST("MC/resolution/system/2D/hPtVsPt"), trueSystem.Pt(), recoSystem.Pt()); rResolution.fill(HIST("MC/resolution/system/1D/hY"), recoSystem.Rapidity() - trueSystem.Rapidity()); rResolution.fill(HIST("MC/resolution/system/2D/hYVsY"), trueSystem.Rapidity(), recoSystem.Rapidity()); + rResolution.fill(HIST("MC/resolution/system/1D/hPhi"), phi(recoSystem.Px(), recoSystem.Py()) - phi(trueSystem.Px(), trueSystem.Py())); + rResolution.fill(HIST("MC/resolution/system/2D/hPhiVsPhi"), phi(trueSystem.Px(), trueSystem.Py()), phi(recoSystem.Px(), recoSystem.Py())); rResolution.fill(HIST("MC/resolution/system/1D/hDeltaPhi"), recoDeltaPhi - trueDeltaPhi); rResolution.fill(HIST("MC/resolution/system/2D/hDeltaPhiVsDeltaPhi"), trueDeltaPhi, recoDeltaPhi); @@ -1204,15 +1294,65 @@ struct UpcRhoAnalysis { auto leadingRecoPion = momentum(recoTracks[0].px(), recoTracks[0].py(), recoTracks[0].pz()) > momentum(recoTracks[1].px(), recoTracks[1].py(), recoTracks[1].pz()) ? recoTracks[0] : recoTracks[1]; auto subleadingRecoPion = (leadingRecoPion == recoTracks[0]) ? recoTracks[1] : recoTracks[0]; - resolutionTree(mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + resolutionTree(mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.runNumber(), + collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), collision.posX(), collision.posY(), collision.posZ(), + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), + collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), leadingTruePion.pdgCode() / std::abs(leadingTruePion.pdgCode()), pt(leadingTruePion.px(), leadingTruePion.py()), eta(leadingTruePion.px(), leadingTruePion.py(), leadingTruePion.pz()), phi(leadingTruePion.px(), leadingTruePion.py()), - pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), + pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), leadingRecoPion.hasTOF(), subleadingTruePion.pdgCode() / std::abs(subleadingTruePion.pdgCode()), pt(subleadingTruePion.px(), subleadingTruePion.py()), eta(subleadingTruePion.px(), subleadingTruePion.py(), subleadingTruePion.pz()), phi(subleadingTruePion.px(), subleadingTruePion.py()), - pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py())); + pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py()), subleadingRecoPion.hasTOF()); } PROCESS_SWITCH(UpcRhoAnalysis, processResolution, "check resolution of kinematic variables", false); + void processMcRecoWithTruth(soa::Join::iterator const& collision, soa::Join const& tracks, aod::UDMcCollisions const&, aod::UDMcParticles const&) + { + // basically just runs the analysis as for normal data but also accesses the MC truth information for the tracks and collisions + auto mcCollision = collision.udMcCollision(); + const int runIndex = -1; // we don't care here + if (cutGapSide && collision.gapSide() != gapSide) + return; + if (!collisionPassesCuts(collision, runIndex)) // apply collision cuts + return; + + std::vector recoTracks; // store selected tracks + std::vector trueTracks; + for (const auto& track : tracks) { + fillTrackQcHistos<0>(track); // fill QC histograms before cuts + + if (!trackPassesCuts(track, runIndex)) // apply track cuts + continue; + recoTracks.push_back(track); + if (track.has_udMcParticle()) + trueTracks.push_back(track.udMcParticle()); + } + if (static_cast(recoTracks.size()) != nExpectedPions || static_cast(trueTracks.size()) != nExpectedPions) // further consider only two pion systems + return; + if (applyPid && !tracksPassPID(recoTracks)) // apply PID cut before creating derived data if desired + return; + + auto leadingTruePion = momentum(trueTracks[0].px(), trueTracks[0].py(), trueTracks[0].pz()) > momentum(trueTracks[1].px(), trueTracks[1].py(), trueTracks[1].pz()) ? trueTracks[0] : trueTracks[1]; + auto subleadingTruePion = (leadingTruePion == trueTracks[0]) ? trueTracks[1] : trueTracks[0]; + auto leadingRecoPion = momentum(recoTracks[0].px(), recoTracks[0].py(), recoTracks[0].pz()) > momentum(recoTracks[1].px(), recoTracks[1].py(), recoTracks[1].pz()) ? recoTracks[0] : recoTracks[1]; + auto subleadingRecoPion = (leadingRecoPion == recoTracks[0]) ? recoTracks[1] : recoTracks[0]; + + resolutionTree(mcCollision.globalBC(), mcCollision.globalBC() % o2::constants::lhc::LHCMaxBunches, + collision.runNumber(), + collision.globalBC(), collision.globalBC() % o2::constants::lhc::LHCMaxBunches, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + collision.posX(), collision.posY(), collision.posZ(), + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.totalFDDAmplitudeA(), collision.totalFDDAmplitudeC(), + collision.timeFT0A(), collision.timeFT0C(), collision.timeFV0A(), collision.timeFDDA(), collision.timeFDDC(), + leadingTruePion.pdgCode() / std::abs(leadingTruePion.pdgCode()), pt(leadingTruePion.px(), leadingTruePion.py()), eta(leadingTruePion.px(), leadingTruePion.py(), leadingTruePion.pz()), phi(leadingTruePion.px(), leadingTruePion.py()), + pt(leadingRecoPion.px(), leadingRecoPion.py()), eta(leadingRecoPion.px(), leadingRecoPion.py(), leadingRecoPion.pz()), phi(leadingRecoPion.px(), leadingRecoPion.py()), leadingRecoPion.hasTOF(), + subleadingTruePion.pdgCode() / std::abs(subleadingTruePion.pdgCode()), pt(subleadingTruePion.px(), subleadingTruePion.py()), eta(subleadingTruePion.px(), subleadingTruePion.py(), subleadingTruePion.pz()), phi(subleadingTruePion.px(), subleadingTruePion.py()), + pt(subleadingRecoPion.px(), subleadingRecoPion.py()), eta(subleadingRecoPion.px(), subleadingRecoPion.py(), subleadingRecoPion.pz()), phi(subleadingRecoPion.px(), subleadingRecoPion.py()), subleadingRecoPion.hasTOF()); + } + PROCESS_SWITCH(UpcRhoAnalysis, processMcRecoWithTruth, "process MC reco with access to MC truth", false); + void processCollisionRecoCheck(aod::UDMcCollision const& /* mcCollision */, soa::SmallGroups> const& collisions) { checkNumberOfCollisionReconstructions(collisions); diff --git a/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx b/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx index 703f0989283..b8bfdcb6d81 100644 --- a/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx +++ b/PWGUD/Tasks/upcRhoPrimeAnalysis.cxx @@ -9,10 +9,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. /// -/// \brief Task for analysis of rho' in UPCs using UD tables (from SG producer). -/// \author Cesar Ramirez, cesar.ramirez@cern.ch +/// \file upcRhoPrimeAnalysis.cxx +/// \brief Task for analysis of rho prime in UPCs using UD tables (from SG producer). +/// \author Cesar Omar Ramirez Alvarez (cesar.ramirez@cern.ch), Autonomous University of Puebla -#include "PWGUD/Core/UPCTauCentralBarrelHelperRL.h" #include "PWGUD/DataModel/UDTables.h" #include @@ -26,62 +26,66 @@ #include #include -#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include // IWYU pragma: keep #include #include +#include #include #include #include -#include -#include #include using namespace o2; using namespace o2::framework; +using namespace o2::framework::expressions; +using namespace ROOT::Math; // Define UD tables using UDtracks = soa::Join; -using UDCollisions = soa::Join; +using UDCollisions = soa::Join; +using UDMcCollisions = aod::UDMcCollisions; +using UDMcParticles = aod::UDMcParticles; +using UDtracksMC = soa::Join; +using UDCollisionsMC = soa::Join; namespace o2::aod { namespace fourpi { -// Declare columns -DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); // Run number for event identification -DECLARE_SOA_COLUMN(M, m, double); // Invariant mass of the system -DECLARE_SOA_COLUMN(Pt, pt, double); // Transverse momentum of the system -DECLARE_SOA_COLUMN(Eta, eta, double); // Pseudorapidity of the system -DECLARE_SOA_COLUMN(Phi, phi, double); // Azimuthal angle of the system +// Columns RD / MC reco +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); // Run number +DECLARE_SOA_COLUMN(M, m, double); // System invariant mass +DECLARE_SOA_COLUMN(Pt, pt, double); // System pT +DECLARE_SOA_COLUMN(Eta, eta, double); // System pseudorapidity +DECLARE_SOA_COLUMN(Phi, phi, double); // System azimuthal angle DECLARE_SOA_COLUMN(PosX, posX, double); // Vertex X position DECLARE_SOA_COLUMN(PosY, posY, double); // Vertex Y position DECLARE_SOA_COLUMN(PosZ, posZ, double); // Vertex Z position -DECLARE_SOA_COLUMN(TotalCharge, totalCharge, int); // Total charge of selected tracks +DECLARE_SOA_COLUMN(TotalCharge, totalCharge, int); // Real total charge of the 4 selected tracks DECLARE_SOA_COLUMN(TotalFT0AmplitudeA, totalFT0AmplitudeA, float); // FT0A amplitude DECLARE_SOA_COLUMN(TotalFT0AmplitudeC, totalFT0AmplitudeC, float); // FT0C amplitude DECLARE_SOA_COLUMN(TotalFV0AmplitudeA, totalFV0AmplitudeA, float); // FV0A amplitude -DECLARE_SOA_COLUMN(NumContrib, numContrib, int32_t); // Number of primary vertex contributors +DECLARE_SOA_COLUMN(NumContrib, numContrib, int32_t); // Number of PV contributors DECLARE_SOA_COLUMN(Sign, sign, std::vector); // Track charges -DECLARE_SOA_COLUMN(TrackPt, trackPt, std::vector); // Track pT values -DECLARE_SOA_COLUMN(TrackEta, trackEta, std::vector); // Track eta values -DECLARE_SOA_COLUMN(TrackPhi, trackPhi, std::vector); // Track phi values -DECLARE_SOA_COLUMN(TPCNSigmaEl, tpcNSigmaEl, std::vector); // TPC nσ for electrons -DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, std::vector); // TPC nσ for pions -DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, std::vector); // TPC nσ for kaons -DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, std::vector); // TPC nσ for protons -DECLARE_SOA_COLUMN(TrackID, trackID, std::vector); // Track identifiers -DECLARE_SOA_COLUMN(IsReconstructedWithUPC, isReconstructedWithUPC, bool); // UPC mode reconstruction flag -DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); // ZNA timing -DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); // ZNC timing +DECLARE_SOA_COLUMN(TrackPt, trackPt, std::vector); // Track pT +DECLARE_SOA_COLUMN(TrackEta, trackEta, std::vector); // Track eta +DECLARE_SOA_COLUMN(TrackPhi, trackPhi, std::vector); // Track phi +DECLARE_SOA_COLUMN(TPCNSigmaEl, tpcNSigmaEl, std::vector); // TPC nSigma electron +DECLARE_SOA_COLUMN(TPCNSigmaPi, tpcNSigmaPi, std::vector); // TPC nSigma pion +DECLARE_SOA_COLUMN(TPCNSigmaKa, tpcNSigmaKa, std::vector); // TPC nSigma kaon +DECLARE_SOA_COLUMN(TPCNSigmaPr, tpcNSigmaPr, std::vector); // TPC nSigma proton +DECLARE_SOA_COLUMN(TrackID, trackID, std::vector); // Track index within the system +DECLARE_SOA_COLUMN(IsReconstructedWithUPC, isReconstructedWithUPC, bool); // UPC reconstruction mode flag +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); // ZNA time +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); // ZNC time DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); // ZNA energy DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); // ZNC energy -DECLARE_SOA_COLUMN(IsChargeZero, isChargeZero, bool); // Neutral system flag -DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, int); // Occupancy in time +DECLARE_SOA_COLUMN(IsChargeZero, isChargeZero, bool); // TotalCharge == 0 +DECLARE_SOA_COLUMN(OccupancyInTime, occupancyInTime, int); // Occupancy DECLARE_SOA_COLUMN(HadronicRate, hadronicRate, double); // Hadronic interaction rate } // namespace fourpi -// Define the output DECLARE_SOA_TABLE(SYSTEMTREE, "AOD", "SystemTree", fourpi::RunNumber, fourpi::M, fourpi::Pt, fourpi::Eta, fourpi::Phi, fourpi::PosX, fourpi::PosY, fourpi::PosZ, fourpi::TotalCharge, @@ -92,10 +96,63 @@ DECLARE_SOA_TABLE(SYSTEMTREE, "AOD", "SystemTree", fourpi::TrackID, fourpi::IsReconstructedWithUPC, fourpi::TimeZNA, fourpi::TimeZNC, fourpi::EnergyCommonZNA, fourpi::EnergyCommonZNC, fourpi::IsChargeZero, fourpi::OccupancyInTime, fourpi::HadronicRate); + +namespace mcgen4pi +{ +// Columns MC gen +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); // Common mother PDG +DECLARE_SOA_COLUMN(McMotherPt, mcMotherPt, float); // Mother pT +DECLARE_SOA_COLUMN(McMotherPhi, mcMotherPhi, float); // Mother phi +DECLARE_SOA_COLUMN(McMotherMass, mcMotherMass, float); // Mother invariant mass +DECLARE_SOA_COLUMN(McMotherRapidity, mcMotherRapidity, float); // Mother rapidity +DECLARE_SOA_COLUMN(McTotalCharge, mcTotalCharge, int); // Real total charge of the 4 generated particles + +// Per-particle info +DECLARE_SOA_COLUMN(McTrackPdg, mcTrackPdg, int[4]); +DECLARE_SOA_COLUMN(McTrackPt, mcTrackPt, float[4]); +DECLARE_SOA_COLUMN(McTrackEta, mcTrackEta, float[4]); +DECLARE_SOA_COLUMN(McTrackPhi, mcTrackPhi, float[4]); +DECLARE_SOA_COLUMN(McTrackSign, mcTrackSign, int[4]); +DECLARE_SOA_COLUMN(McTrackIsPrimary, mcTrackIsPrimary, int[4]); + +// Generated vertex +DECLARE_SOA_COLUMN(McPosX, mcPosX, float); +DECLARE_SOA_COLUMN(McPosY, mcPosY, float); +DECLARE_SOA_COLUMN(McPosZ, mcPosZ, float); + +// Generated collision index +DECLARE_SOA_COLUMN(McCollisionIndex, mcCollisionIndex, int); + +// Real run number for MC gen +DECLARE_SOA_COLUMN(McRunNumber, mcRunNumber, int); + +DECLARE_SOA_COLUMN(RecoIndex, recoIndex, int); +} // namespace mcgen4pi + +// MC reco<->gen match table +DECLARE_SOA_TABLE(FourPiMcMatchTree, "AOD", "FOURPIMCMATCH", + fourpi::IsReconstructedWithUPC, + mcgen4pi::McMotherPdg, mcgen4pi::McMotherPt, mcgen4pi::McMotherPhi, + mcgen4pi::McMotherMass, mcgen4pi::McMotherRapidity, mcgen4pi::McTotalCharge, + mcgen4pi::McTrackPdg, mcgen4pi::McTrackPt, mcgen4pi::McTrackEta, mcgen4pi::McTrackPhi, + mcgen4pi::McTrackSign, mcgen4pi::McTrackIsPrimary, + mcgen4pi::McPosX, mcgen4pi::McPosY, mcgen4pi::McPosZ, mcgen4pi::McCollisionIndex, + mcgen4pi::McRunNumber, mcgen4pi::RecoIndex); + +// MC All generated collisions +DECLARE_SOA_TABLE(FourPiMcGenAllTree, "AOD", "FOURPIMCGALL", + mcgen4pi::McMotherPdg, mcgen4pi::McMotherPt, mcgen4pi::McMotherPhi, + mcgen4pi::McMotherMass, mcgen4pi::McMotherRapidity, mcgen4pi::McTotalCharge, + mcgen4pi::McTrackPdg, mcgen4pi::McTrackPt, mcgen4pi::McTrackEta, mcgen4pi::McTrackPhi, + mcgen4pi::McTrackSign, mcgen4pi::McTrackIsPrimary, + mcgen4pi::McPosX, mcgen4pi::McPosY, mcgen4pi::McPosZ, mcgen4pi::McCollisionIndex, + mcgen4pi::McRunNumber); } // namespace o2::aod struct upcRhoPrimeAnalysis { Produces systemTree; + Produces fourPiMcMatchTree; + Produces fourPiMcGenAllTree; // System selection configuration Configurable systemYCut{"systemYCut", 0.5, "Max Rapidity of rho prime"}; @@ -127,11 +184,16 @@ struct upcRhoPrimeAnalysis { Configurable dcaZcut{"dcaZcut", 2, "dcaZ cut"}; Configurable minTPCFindableClusters{"minTPCFindableClusters", 70, "Minimum number of findable TPC clusters"}; - // Define histogram registry + // Optional generatorId filter + Configurable genId{"genId", -1, "generator ID; -1 = no filter"}; + + // Define histogram registry for RD / MC reco HistogramRegistry registry{ "registry", {// Event flow histograms - {"Events/Flow", "Event flow;Cut;Counts", {HistType::kTH1F, {{9, 0, 9}}}}, + {"Events/Flow", "Event flow;Cut;Counts", {HistType::kTH1D, {{11, 0, 11}}}}, + {"Events/FlowDetailed", "Detailed event flow;Cut;Counts", {HistType::kTH1D, {{13, 0, 13}}}}, + {"Events/hRecoMode", "Reconstruction mode;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, {"Events/VertexZ", "Vertex Z;z (cm);Counts", {HistType::kTH1F, {{200, -20, 20}}}}, {"Events/NumContrib", "Number of contributors;N_{contrib};Counts", {HistType::kTH1F, {{100, 0, 100}}}}, {"Events/FV0Amplitude", "FV0 amplitude;Amplitude;Counts", {HistType::kTH1F, {{200, 0, 200}}}}, @@ -145,23 +207,148 @@ struct upcRhoPrimeAnalysis { {"Tracks/TPCNSigmaPi", "TPC n#sigma for #pi;n#sigma;Counts", {HistType::kTH1F, {{200, -10, 10}}}}, {"Tracks/TPCChi2NCl", "TPC #chi^{2}/N_{cls};#chi^{2}/N_{cls};Counts", {HistType::kTH1F, {{200, 0, 20}}}}, {"Tracks/ITSChi2NCl", "ITS #chi^{2}/N_{cls};#chi^{2}/N_{cls};Counts", {HistType::kTH1F, {{200, 0, 50}}}}, - {"Tracks/RejectionReasons", "Track rejection reasons;Reason;Counts", {HistType::kTH1F, {{12, 0, 12}}}}, + {"Tracks/RejectionReasons", "Track rejection reasons;Reason;Counts", {HistType::kTH1F, {{15, 0, 15}}}}, {"Tracks/DCASpectrum", "Track DCA spectrum;DCA (cm);Counts", {HistType::kTH1F, {{100, 0, 5}}}}, {"Tracks/ChargeDistribution", "Track charge distribution;Charge;Counts", {HistType::kTH1F, {{3, -1.5, 1.5}}}}, {"Tracks/TPCClusters", "TPC clusters findable;N_{clusters};Counts", {HistType::kTH1F, {{100, 0, 200}}}}, + {"Tracks/NGoodTracksPerEvent", "Good tracks per event (before the ==4 cut);N;Counts", {HistType::kTH1F, {{11, -0.5, 10.5}}}}, // System kinematics histograms {"System/hM", ";m (GeV/#it{c}^{2});counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, - {"System/hPt", ";p_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"System/hPt", ";p_{T} (GeV/#it{c});counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, {"System/hEta", ";#eta;counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, {"System/hPhi", ";#phi;counts", {HistType::kTH1F, {{180, 0.0, 6.28}}}}, {"System/hY", ";y;counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"System/hTotalChargeBefore", "Total charge before M/Pt/Y cuts;Q;counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + // -4=----, -2=---+ (3-,1+), 0=+-+- (2+,2-), +2=+++- (3+,1-), +4=++++ + {"System/hTotalCharge", "System total charge (4 tracks, after M/Pt/Y cuts);Q;counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + {"System/hMVsTotalChargeBefore", "Invariant mass vs charge combination (before M/Pt/Y cuts);m (GeV/#it{c}^{2});Q", {HistType::kTH2F, {{1000, 0.0, 10.0}, {9, -4.5, 4.5}}}}, + {"System/hMVsTotalCharge", "Invariant mass vs charge combination (after M/Pt/Y cuts);m (GeV/#it{c}^{2});Q", {HistType::kTH2F, {{1000, 0.0, 10.0}, {9, -4.5, 4.5}}}}, // Comparison histograms {"Cuts/MBefore", "Mass before cuts;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0, 10}}}}, {"Cuts/MAfter", "Mass after cuts;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0, 10}}}}, - {"Cuts/PtBefore", "p_{T} before cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1}}}}, - {"Cuts/PtAfter", "p_{T} after cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 10}}}}}}; + {"Cuts/PtBefore", "p_{T} before cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1.1}}}}, + {"Cuts/PtAfter", "p_{T} after cuts;p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0, 1.1}}}}}}; + + // Define histogram registry for MC analysis + HistogramRegistry mcRegistry{ + "mcRegistry", + {// Event-level MC histograms + {"MC/Events/hAllEvents", "All MC Events", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Events/hAccepted", "Accepted MC Events", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Events/hVertexZ", "MC Vertex Z;z (cm);Counts", {HistType::kTH1F, {{400, -20.0, 20.0}}}}, + {"MC/Events/hNPrimaries", "Number of primary particles;N;Counts", {HistType::kTH1F, {{10, -0.5, 9.5}}}}, + + // Track-level MC histograms + {"MC/Tracks/hPt", "MC Track p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{200, 0.0, 2.0}}}}, + {"MC/Tracks/hEta", "MC Track #eta;#eta;Counts", {HistType::kTH1F, {{200, -2.0, 2.0}}}}, + {"MC/Tracks/hPhi", "MC Track #phi;#phi;Counts", {HistType::kTH1F, {{200, 0.0, 6.28}}}}, + {"MC/Tracks/hPdgCode", "PDG codes;PDG code;Counts", {HistType::kTH1F, {{2000, -1000, 1000}}}}, + + // System-level (mother) MC histograms + {"MC/System/hM", "MC Invariant Mass;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/System/hPt", "MC p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + {"MC/System/hY", "MC Rapidity;y;Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MC/System/hMvsPt", "MC Mass vs p_{T};m (GeV/c^{2});p_{T} (GeV/c)", {HistType::kTH2F, {{1000, 0.0, 10.0}, {1000, 0.0, 1.1}}}}, + {"MC/System/hMvsY", "MC Mass vs Rapidity;m (GeV/c^{2});y", {HistType::kTH2F, {{1000, 0.0, 10.0}, {200, -2.0, 2.0}}}}, + {"MC/System/hTotalCharge", "Generated total charge;Q;Counts", {HistType::kTH1F, {{9, -4.5, 4.5}}}}, + + // Basic summary + {"MC/Summary/hEventCounter", "Generated vs reconstructed summary;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, + {"MC/Summary/hMatchStatus", "Reco<->Gen match status;;Counts", {HistType::kTH1D, {{3, 0, 3}}}}, + {"MC/Summary/hRecoMode", "Reconstruction mode;;Counts", {HistType::kTH1D, {{2, 0, 2}}}}, + {"MC/Summary/hRecoTotalCharge", "Reco total charge (MC events, after M/Pt/Y cuts);Q;Counts", {HistType::kTH1D, {{9, -4.5, 4.5}}}}, + + // Rho prime specific histograms + {"MC/RhoPrime/hFound", "Rho Prime Found;Found;Counts", {HistType::kTH1F, {{2, -0.5, 1.5}}}}, + {"MC/RhoPrime/hMass", "Rho Prime Mass;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hMassUPC", "Rho Prime Mass UPC;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hMassSTD", "Rho Prime Mass STD;m (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/RhoPrime/hPt", "Rho Prime p_{T};p_{T} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + + // Control histograms + {"MC/Control/hNDaughtersOfMother", "Number of daughters of the common mother;N;Counts", {HistType::kTH1F, {{10, 0, 10}}}}, + {"MC/Control/hMotherPdg", "PDG of the common mother found;PDG;Counts", {HistType::kTH1F, {{200, 0, 40000}}}}, + {"MC/Control/hTracksWithMcParticle", "Reco tracks with an associated mcParticle (of 4);N;Counts", {HistType::kTH1F, {{5, -0.5, 4.5}}}}, + + // MC reco<->gen match histograms + {"MC/Match/hRecoEvents", "Reco events entering the match check;;Counts", {HistType::kTH1F, {{1, 0, 1}}}}, + {"MC/Match/hMatchedGenM", "Matched candidates - generated M;m_{gen} (GeV/c^{2});Counts", {HistType::kTH1F, {{1000, 0.0, 10.0}}}}, + {"MC/Match/hMatchedGenPt", "Matched candidates - generated p_{T};p_{T,gen} (GeV/c);Counts", {HistType::kTH1F, {{1000, 0.0, 1.1}}}}, + {"MC/Match/hMatchedGenY", "Matched candidates - generated y;y_{gen};Counts", {HistType::kTH1F, {{180, -0.9, 0.9}}}}, + {"MC/Match/hRecoVsGenM", "Reco vs Gen M;m_{gen} (GeV/c^{2});m_{reco} (GeV/c^{2})", {HistType::kTH2F, {{500, 0.0, 5.0}, {500, 0.0, 5.0}}}}}}; + + // Helper functions for kinematic calculations + static float pt(float px, float py) { return std::sqrt(px * px + py * py); } + + static float eta(float px, float py, float pz) + { + if (std::abs(pz) > 1e-10) { + float p = std::sqrt(px * px + py * py + pz * pz); + return 0.5f * std::log((p + pz) / (p - pz)); + } + return 0.0f; + } + + static float phi(float px, float py) + { + if (std::abs(px) > 1e-10 || std::abs(py) > 1e-10) { + return std::atan2(py, px); + } + return 0.0f; + } + + // Generic "common mother" + struct MotherInfo { + bool found = false; + int pdg = 0; + float px = 0, py = 0; + }; + + // 4 particles share the same direct mother + template + MotherInfo findCommonMotherGeneric(const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + auto firstMothers = daughters[0].template mothers_as(); + if (firstMothers.begin() == firstMothers.end()) { + return info; + } + auto motherIt = firstMothers.begin(); + int64_t motherGlobalIndex = motherIt->globalIndex(); + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers()) { + return info; + } + auto iMothers = daughters[i].template mothers_as(); + if (iMothers.begin() == iMothers.end() || iMothers.begin()->globalIndex() != motherGlobalIndex) { + return info; + } + } + const auto& mother = *motherIt; + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + return info; + } + + // Charge sign from PDG code + static int signFromPdg(int pdgCode) + { + if (pdgCode == 211) { + return 1; + } + if (pdgCode == -211) { + return -1; + } + return (pdgCode > 0) ? 1 : (pdgCode < 0) ? -1 + : 0; + } void init(InitContext&) { @@ -173,32 +360,135 @@ struct upcRhoPrimeAnalysis { hFlow->GetXaxis()->SetBinLabel(4, "ITS ROFb cut"); hFlow->GetXaxis()->SetBinLabel(5, "TFB cut"); hFlow->GetXaxis()->SetBinLabel(6, "Gap Side cut"); - hFlow->GetXaxis()->SetBinLabel(7, "PV contrib cut"); - hFlow->GetXaxis()->SetBinLabel(8, "Z vtx cut"); - hFlow->GetXaxis()->SetBinLabel(9, "4 tracks cut"); + hFlow->GetXaxis()->SetBinLabel(7, "ZDC energy cut"); + hFlow->GetXaxis()->SetBinLabel(8, "PV contrib cut"); + hFlow->GetXaxis()->SetBinLabel(9, "Z vtx cut"); + hFlow->GetXaxis()->SetBinLabel(10, "4 tracks cut"); + hFlow->GetXaxis()->SetBinLabel(11, "System cuts (M,Pt,Y)"); + + auto hFlowDetailed = registry.get(HIST("Events/FlowDetailed")); + hFlowDetailed->GetXaxis()->SetBinLabel(1, "All events"); + hFlowDetailed->GetXaxis()->SetBinLabel(2, "vtxITSTPC"); + hFlowDetailed->GetXaxis()->SetBinLabel(3, "sbp"); + hFlowDetailed->GetXaxis()->SetBinLabel(4, "itsROFb"); + hFlowDetailed->GetXaxis()->SetBinLabel(5, "tfb"); + hFlowDetailed->GetXaxis()->SetBinLabel(6, "gapSide"); + hFlowDetailed->GetXaxis()->SetBinLabel(7, "FV0 < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(8, "FT0A < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(9, "FT0C < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(10, "ZDC energy < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(11, "numContrib == 4"); + hFlowDetailed->GetXaxis()->SetBinLabel(12, "posZ < cut"); + hFlowDetailed->GetXaxis()->SetBinLabel(13, "4 tracks"); + + // Readable labels for Events/hRecoMode + auto hRecoMode = registry.get(HIST("Events/hRecoMode")); + hRecoMode->GetXaxis()->SetBinLabel(1, "STD"); + hRecoMode->GetXaxis()->SetBinLabel(2, "UPC"); + + auto hChargeBefore = registry.get(HIST("System/hTotalChargeBefore")); + hChargeBefore->GetXaxis()->SetBinLabel(1, "----"); + hChargeBefore->GetXaxis()->SetBinLabel(3, "---+"); + hChargeBefore->GetXaxis()->SetBinLabel(5, "+-+-"); + hChargeBefore->GetXaxis()->SetBinLabel(7, "+++-"); + hChargeBefore->GetXaxis()->SetBinLabel(9, "++++"); + + auto hCharge = registry.get(HIST("System/hTotalCharge")); + hCharge->GetXaxis()->SetBinLabel(1, "----"); + hCharge->GetXaxis()->SetBinLabel(3, "---+"); + hCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hCharge->GetXaxis()->SetBinLabel(9, "++++"); + + auto hMChargeBefore = registry.get(HIST("System/hMVsTotalChargeBefore")); + hMChargeBefore->GetYaxis()->SetBinLabel(1, "----"); + hMChargeBefore->GetYaxis()->SetBinLabel(3, "---+"); + hMChargeBefore->GetYaxis()->SetBinLabel(5, "+-+-"); + hMChargeBefore->GetYaxis()->SetBinLabel(7, "+++-"); + hMChargeBefore->GetYaxis()->SetBinLabel(9, "++++"); + + auto hMCharge = registry.get(HIST("System/hMVsTotalCharge")); + hMCharge->GetYaxis()->SetBinLabel(1, "----"); + hMCharge->GetYaxis()->SetBinLabel(3, "---+"); + hMCharge->GetYaxis()->SetBinLabel(5, "+-+-"); + hMCharge->GetYaxis()->SetBinLabel(7, "+++-"); + hMCharge->GetYaxis()->SetBinLabel(9, "++++"); - // Configure track rejection reasons histogram labels auto hReject = registry.get(HIST("Tracks/RejectionReasons")); - hReject->GetXaxis()->SetBinLabel(1, "All Tracks"); - hReject->GetXaxis()->SetBinLabel(2, "PV Contributor"); - hReject->GetXaxis()->SetBinLabel(3, "Has ITS+TPC"); - hReject->GetXaxis()->SetBinLabel(4, "pT > 0.1 GeV/c"); - hReject->GetXaxis()->SetBinLabel(5, "TPC chi2/cluster"); - hReject->GetXaxis()->SetBinLabel(6, "ITS chi2/cluster"); - hReject->GetXaxis()->SetBinLabel(7, "TPC clusters findable"); - hReject->GetXaxis()->SetBinLabel(8, "TPC nSigmaPi"); - hReject->GetXaxis()->SetBinLabel(9, "Eta acceptance"); - hReject->GetXaxis()->SetBinLabel(10, "DCAz cut"); - hReject->GetXaxis()->SetBinLabel(11, "DCAxy cut"); - hReject->GetXaxis()->SetBinLabel(12, "Accepted Tracks"); + hReject->GetXaxis()->SetBinLabel(1, "All tracks"); + hReject->GetXaxis()->SetBinLabel(2, "isPVContributor"); + hReject->GetXaxis()->SetBinLabel(3, "hasITS && hasTPC"); + hReject->GetXaxis()->SetBinLabel(4, "pT > 0.1"); + hReject->GetXaxis()->SetBinLabel(5, "tpcChi2NCl < cut"); + hReject->GetXaxis()->SetBinLabel(6, "itsChi2NCl < cut"); + hReject->GetXaxis()->SetBinLabel(7, "tpcNClsFindable > cut"); + hReject->GetXaxis()->SetBinLabel(8, "|tpcNSigmaPi| < cut"); + hReject->GetXaxis()->SetBinLabel(9, "|eta| < cut"); + hReject->GetXaxis()->SetBinLabel(10, "|dcaZ| < cut"); + hReject->GetXaxis()->SetBinLabel(11, "|dcaXY| < cut"); + hReject->GetXaxis()->SetBinLabel(12, "Accepted tracks"); + + auto hPdg = mcRegistry.get(HIST("MC/Tracks/hPdgCode")); + if (hPdg) { + int bin211 = hPdg->GetXaxis()->FindBin(211); + int binM211 = hPdg->GetXaxis()->FindBin(-211); + int bin30113 = hPdg->GetXaxis()->FindBin(30113); + if (bin211 > 0 && bin211 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(bin211, "#pi^{+}"); + if (binM211 > 0 && binM211 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(binM211, "#pi^{-}"); + if (bin30113 > 0 && bin30113 <= hPdg->GetNbinsX()) + hPdg->GetXaxis()->SetBinLabel(bin30113, "rho'"); + } + + auto hRhoFound = mcRegistry.get(HIST("MC/RhoPrime/hFound")); + hRhoFound->GetXaxis()->SetBinLabel(1, "Not Found"); + hRhoFound->GetXaxis()->SetBinLabel(2, "Found"); + + auto hMcCharge = mcRegistry.get(HIST("MC/System/hTotalCharge")); + hMcCharge->GetXaxis()->SetBinLabel(1, "----"); + hMcCharge->GetXaxis()->SetBinLabel(3, "---+"); + hMcCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hMcCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hMcCharge->GetXaxis()->SetBinLabel(9, "++++"); + + auto hMcSummary = mcRegistry.get(HIST("MC/Summary/hEventCounter")); + hMcSummary->GetXaxis()->SetBinLabel(1, "Generated (denominator, gen all)"); + hMcSummary->GetXaxis()->SetBinLabel(2, "Reconstructed + matched (numerator)"); + + auto hMatchStatus = mcRegistry.get(HIST("MC/Summary/hMatchStatus")); + hMatchStatus->GetXaxis()->SetBinLabel(1, "No mcParticle on some track"); + hMatchStatus->GetXaxis()->SetBinLabel(2, "Has mcParticle but no common mother"); + hMatchStatus->GetXaxis()->SetBinLabel(3, "Match found"); + + auto hMcRecoMode = mcRegistry.get(HIST("MC/Summary/hRecoMode")); + hMcRecoMode->GetXaxis()->SetBinLabel(1, "STD"); + hMcRecoMode->GetXaxis()->SetBinLabel(2, "UPC"); + + auto hMcRecoCharge = mcRegistry.get(HIST("MC/Summary/hRecoTotalCharge")); + hMcRecoCharge->GetXaxis()->SetBinLabel(1, "----"); + hMcRecoCharge->GetXaxis()->SetBinLabel(3, "---+"); + hMcRecoCharge->GetXaxis()->SetBinLabel(5, "+-+-"); + hMcRecoCharge->GetXaxis()->SetBinLabel(7, "+++-"); + hMcRecoCharge->GetXaxis()->SetBinLabel(9, "++++"); + + if (doprocessDataCols && (doprocessMcCols || doprocessMcGenAll)) { + LOGP(fatal, + "Invalid config: processDataCols must not run together with processMcCols/processMcGenAll " + "(it would duplicate SystemTree rows). Disable one of the two groups in your configuration.json."); + } } - void process(UDCollisions::iterator const& collision, UDtracks const& tracks) + // Collision + track selection + template + std::vector selectAndFillSystemTree(ColType const& collision, TracksType const& tracks, bool& outFilled) { - // Count all processed events + outFilled = false; + std::vector goodTracks; + registry.fill(HIST("Events/Flow"), 0); + registry.fill(HIST("Events/FlowDetailed"), 0); - // Fill basic event diagnostics registry.fill(HIST("Events/VertexZ"), collision.posZ()); registry.fill(HIST("Events/NumContrib"), collision.numContrib()); registry.fill(HIST("Events/FV0Amplitude"), collision.totalFV0AmplitudeA()); @@ -207,64 +497,84 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Events/ZDCEnergy"), collision.energyCommonZNA()); registry.fill(HIST("Events/ZDCEnergy"), collision.energyCommonZNC()); - // Apply event selection cuts in sequence - if (collision.vtxITSTPC() != vtxITSTPCcut) - return; + if (collision.vtxITSTPC() != vtxITSTPCcut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 1); + registry.fill(HIST("Events/FlowDetailed"), 1); - if (collision.sbp() != sbpCut) - return; + if (collision.sbp() != sbpCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 2); + registry.fill(HIST("Events/FlowDetailed"), 2); - if (collision.itsROFb() != itsROFbCut) - return; + if (collision.itsROFb() != itsROFbCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 3); + registry.fill(HIST("Events/FlowDetailed"), 3); - if (collision.tfb() != tfbCut) - return; + if (collision.tfb() != tfbCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 4); + registry.fill(HIST("Events/FlowDetailed"), 4); + + if (specifyGapSide && collision.gapSide() != gapSide) { + return goodTracks; + } - if (specifyGapSide && collision.gapSide() != gapSide) - return; - if (collision.totalFV0AmplitudeA() > fv0Cut) - return; - if (collision.totalFT0AmplitudeA() > ft0aCut) - return; - if (collision.totalFT0AmplitudeC() > ft0cCut) - return; - if (collision.energyCommonZNA() > zdcCut || collision.energyCommonZNC() > zdcCut) - return; registry.fill(HIST("Events/Flow"), 5); + registry.fill(HIST("Events/FlowDetailed"), 5); - if (collision.numContrib() != numPVContrib) - return; + if (collision.totalFV0AmplitudeA() > fv0Cut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 6); + + if (collision.totalFT0AmplitudeA() > ft0aCut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 7); + + if (collision.totalFT0AmplitudeC() > ft0cCut) { + return goodTracks; + } + registry.fill(HIST("Events/FlowDetailed"), 8); + + if (collision.energyCommonZNA() > zdcCut || collision.energyCommonZNC() > zdcCut) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 6); + registry.fill(HIST("Events/FlowDetailed"), 9); - if (std::abs(collision.posZ()) > vZCut) - return; + if (collision.numContrib() != numPVContrib) { + return goodTracks; + } registry.fill(HIST("Events/Flow"), 7); + registry.fill(HIST("Events/FlowDetailed"), 10); - std::vector posPions; - std::vector negPions; - posPions.reserve(2); - negPions.reserve(2); + if (std::abs(collision.posZ()) > vZCut) { + return goodTracks; + } + registry.fill(HIST("Events/Flow"), 8); + registry.fill(HIST("Events/FlowDetailed"), 11); - // Loop over all tracks in the event + // --- Track selection: up to 4 good tracks, charge combination --- + goodTracks.reserve(4); for (const auto& track : tracks) { - registry.fill(HIST("Tracks/RejectionReasons"), 0); // Count all tracks + registry.fill(HIST("Tracks/RejectionReasons"), 0); - // Track selection criteria applied in sequence: if (useOnlyPVtracks && !track.isPVContributor()) { registry.fill(HIST("Tracks/RejectionReasons"), 1); continue; } - if (!track.hasITS() || !track.hasTPC()) { registry.fill(HIST("Tracks/RejectionReasons"), 2); continue; } - // Fill track spectra registry.fill(HIST("Tracks/Pt"), track.pt()); registry.fill(HIST("Tracks/Eta"), eta(track.px(), track.py(), track.pz())); registry.fill(HIST("Tracks/TPCNSigmaPi"), track.tpcNSigmaPi()); @@ -278,7 +588,6 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Tracks/RejectionReasons"), 3); continue; } - if (track.tpcChi2NCl() > tpcChi2NClsCut) { registry.fill(HIST("Tracks/RejectionReasons"), 4); continue; @@ -287,84 +596,71 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("Tracks/RejectionReasons"), 5); continue; } - if (track.tpcNClsFindable() < minTPCFindableClusters) { registry.fill(HIST("Tracks/RejectionReasons"), 6); continue; } - if (std::abs(track.tpcNSigmaPi()) > nSigmaTPCcut) { registry.fill(HIST("Tracks/RejectionReasons"), 7); continue; } - - float trackEta = eta(track.px(), track.py(), track.pz()); - if (std::abs(trackEta) > etaCut) { + if (std::abs(eta(track.px(), track.py(), track.pz())) > etaCut) { registry.fill(HIST("Tracks/RejectionReasons"), 8); continue; } - if (std::abs(track.dcaZ()) > dcaZcut) { registry.fill(HIST("Tracks/RejectionReasons"), 9); continue; } - float maxDCAxy = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); - if (dcaXYcut == 0 && (std::fabs(track.dcaXY()) > maxDCAxy)) { - registry.fill(HIST("Tracks/RejectionReasons"), 10); - continue; - } else if (dcaXYcut != 0 && (std::fabs(track.dcaXY()) > dcaXYcut)) { + if (dcaXYcut == 0 && std::fabs(track.dcaXY()) > maxDCAxy) { registry.fill(HIST("Tracks/RejectionReasons"), 10); continue; } - // Track passed all selection criteria registry.fill(HIST("Tracks/RejectionReasons"), 11); - - if (track.sign() > 0 && posPions.size() < 2) { - posPions.push_back(track); - } else if (track.sign() < 0 && negPions.size() < 2) { - negPions.push_back(track); - } - - if (posPions.size() == 2 && negPions.size() == 2) + goodTracks.push_back(track); + if (goodTracks.size() == 4) { break; + } } - if (posPions.size() != 2 || negPions.size() != 2) { - return; - } - registry.fill(HIST("Events/Flow"), 8); + // Basic control + registry.fill(HIST("Tracks/NGoodTracksPerEvent"), goodTracks.size()); - std::vector selectedTracks; - selectedTracks.insert(selectedTracks.end(), posPions.begin(), posPions.end()); - selectedTracks.insert(selectedTracks.end(), negPions.begin(), negPions.end()); + if (goodTracks.size() != 4) { + return goodTracks; + } + registry.fill(HIST("Events/Flow"), 9); + registry.fill(HIST("Events/FlowDetailed"), 12); - // Reconstruct the 4-pion system - ROOT::Math::PxPyPzMVector fourPionSystem; - std::vector pionFourVectors; + // Real total charge + int totalCharge = 0; + for (const auto& track : goodTracks) { + totalCharge += track.sign(); + } + bool isChargeZero = (totalCharge == 0); + registry.fill(HIST("System/hTotalChargeBefore"), totalCharge); - for (const auto& track : selectedTracks) { - ROOT::Math::PxPyPzMVector pionVec( - track.px(), track.py(), track.pz(), - o2::constants::physics::MassPionCharged); - fourPionSystem += pionVec; - pionFourVectors.push_back(pionVec); + PxPyPzMVector fourPionSystem; + for (const auto& track : goodTracks) { + fourPionSystem += PxPyPzMVector(track.px(), track.py(), track.pz(), o2::constants::physics::MassPionCharged); } - // Fill pre-cut system histograms registry.fill(HIST("Cuts/MBefore"), fourPionSystem.M()); registry.fill(HIST("Cuts/PtBefore"), fourPionSystem.Pt()); + registry.fill(HIST("System/hMVsTotalChargeBefore"), fourPionSystem.M(), totalCharge); - // Apply system-level kinematic cuts - if (fourPionSystem.M() < systemMassMinCut || fourPionSystem.M() > systemMassMaxCut) - return; - if (fourPionSystem.Pt() > systemPtCut) - return; - if (std::abs(fourPionSystem.Rapidity()) > systemYCut) - return; + if (fourPionSystem.M() < systemMassMinCut || fourPionSystem.M() > systemMassMaxCut) { + return goodTracks; + } + if (fourPionSystem.Pt() > systemPtCut) { + return goodTracks; + } + if (std::abs(fourPionSystem.Rapidity()) > systemYCut) { + return goodTracks; + } - // Fill post-cut system histograms registry.fill(HIST("Cuts/MAfter"), fourPionSystem.M()); registry.fill(HIST("Cuts/PtAfter"), fourPionSystem.Pt()); registry.fill(HIST("System/hM"), fourPionSystem.M()); @@ -372,13 +668,14 @@ struct upcRhoPrimeAnalysis { registry.fill(HIST("System/hEta"), fourPionSystem.Eta()); registry.fill(HIST("System/hPhi"), fourPionSystem.Phi() + o2::constants::math::PI); registry.fill(HIST("System/hY"), fourPionSystem.Rapidity()); + registry.fill(HIST("System/hTotalCharge"), totalCharge); + registry.fill(HIST("System/hMVsTotalCharge"), fourPionSystem.M(), totalCharge); std::vector trackPts, trackEtas, trackPhis; std::vector trackSigns, trackIDs; std::vector tpcNSigmasEl, tpcNSigmasPi, tpcNSigmasKa, tpcNSigmasPr; - - for (size_t i = 0; i < selectedTracks.size(); i++) { - const auto& track = selectedTracks[i]; + for (size_t i = 0; i < goodTracks.size(); i++) { + const auto& track = goodTracks[i]; trackPts.push_back(track.pt()); trackEtas.push_back(eta(track.px(), track.py(), track.pz())); trackPhis.push_back(phi(track.px(), track.py())); @@ -387,48 +684,272 @@ struct upcRhoPrimeAnalysis { tpcNSigmasPi.push_back(track.tpcNSigmaPi()); tpcNSigmasKa.push_back(track.tpcNSigmaKa()); tpcNSigmasPr.push_back(track.tpcNSigmaPr()); - trackIDs.push_back(i); + trackIDs.push_back(static_cast(i)); } bool isReconstructedWithUPC = (collision.flags() == 1); - // Fill the output + registry.fill(HIST("Events/Flow"), 10); + registry.fill(HIST("Events/hRecoMode"), isReconstructedWithUPC ? 1 : 0); + systemTree( collision.runNumber(), - fourPionSystem.M(), - fourPionSystem.Pt(), - fourPionSystem.Rapidity(), - fourPionSystem.Phi(), - collision.posX(), - collision.posY(), - collision.posZ(), - 0, // Total charge = 0 - collision.totalFT0AmplitudeA(), - collision.totalFT0AmplitudeC(), - collision.totalFV0AmplitudeA(), + fourPionSystem.M(), fourPionSystem.Pt(), fourPionSystem.Rapidity(), fourPionSystem.Phi(), + collision.posX(), collision.posY(), collision.posZ(), + totalCharge, + collision.totalFT0AmplitudeA(), collision.totalFT0AmplitudeC(), collision.totalFV0AmplitudeA(), collision.numContrib(), - trackSigns, - trackPts, - trackEtas, - trackPhis, - tpcNSigmasEl, - tpcNSigmasPi, - tpcNSigmasKa, - tpcNSigmasPr, - trackIDs, + trackSigns, trackPts, trackEtas, trackPhis, + tpcNSigmasEl, tpcNSigmasPi, tpcNSigmasKa, tpcNSigmasPr, + trackIDs, isReconstructedWithUPC, + collision.timeZNA(), collision.timeZNC(), collision.energyCommonZNA(), collision.energyCommonZNC(), + isChargeZero, collision.occupancyInTime(), collision.hadronicRate()); + + outFilled = true; + return goodTracks; + } // end selectAndFillSystemTree + + int getMcRunNumber(aod::BCs const& bcs) + { + if (bcs.size() == 0) { + return -1; + } + auto bc = bcs.begin(); + return bc.runNumber(); + } + + // processDataCols: RD or MC reco + void processDataCols(UDCollisions::iterator const& collision, UDtracks const& tracks) + { + bool filled = false; + selectAndFillSystemTree(collision, tracks, filled); + } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processDataCols, "Process real data or MC reco", true); + + // processMcCols: MC only + void processMcCols(UDCollisionsMC::iterator const& collision, UDtracksMC const& tracks, UDMcParticles const&, UDMcCollisions const&, aod::BCs const& bcs) + { + if (genId != -1) { + if (!collision.has_udMcCollision() || collision.template udMcCollision_as().generatorsID() != genId) { + return; + } + } + + bool filled = false; + auto selTrks = selectAndFillSystemTree(collision, tracks, filled); + if (!filled) { + return; + } + + bool isReconstructedWithUPC = (collision.flags() == 1); + + mcRegistry.fill(HIST("MC/Match/hRecoEvents"), 0); + mcRegistry.fill(HIST("MC/Summary/hRecoMode"), isReconstructedWithUPC ? 1 : 0); + + int recoTotalCharge = 0; + for (const auto& track : selTrks) { + recoTotalCharge += track.sign(); + } + mcRegistry.fill(HIST("MC/Summary/hRecoTotalCharge"), recoTotalCharge); + + // Defaults (pdg=0 / kinematics=-999 => "no match") + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + int mcTotalCharge = 0; + int trackPdgs[4] = {0, 0, 0, 0}; + float trackPts[4] = {-999, -999, -999, -999}; + float trackEtas[4] = {-999, -999, -999, -999}; + float trackPhis[4] = {-999, -999, -999, -999}; + int trackSigns[4] = {0, 0, 0, 0}; + int isPrimary[4] = {0, 0, 0, 0}; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcCollisionIndex = -1; + int mcRunNumber = getMcRunNumber(bcs); + + // Basic control + int nTracksWithMcParticle = 0; + for (const auto& track : selTrks) { + if (track.has_udMcParticle()) { + nTracksWithMcParticle++; + } + } + mcRegistry.fill(HIST("MC/Control/hTracksWithMcParticle"), nTracksWithMcParticle); + + // All 4 reco tracks need an associated mcParticle to look for the mother + std::vector mcParts; + bool allHaveMcParticle = true; + for (const auto& track : selTrks) { + if (!track.has_udMcParticle()) { + allHaveMcParticle = false; + break; + } + mcParts.push_back(track.template udMcParticle_as()); + } + + MotherInfo mi; + if (allHaveMcParticle) { + mi = findCommonMotherGeneric(mcParts); + } + + if (!allHaveMcParticle) { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 0); + } else if (!mi.found) { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 1); + } else { + mcRegistry.fill(HIST("MC/Summary/hMatchStatus"), 2); + mcRegistry.fill(HIST("MC/Summary/hEventCounter"), 1); + } + + if (allHaveMcParticle) { + for (size_t i = 0; i < mcParts.size() && i < 4; i++) { + trackPdgs[i] = mcParts[i].pdgCode(); + trackPts[i] = pt(mcParts[i].px(), mcParts[i].py()); + trackEtas[i] = eta(mcParts[i].px(), mcParts[i].py(), mcParts[i].pz()); + trackPhis[i] = phi(mcParts[i].px(), mcParts[i].py()); + trackSigns[i] = signFromPdg(trackPdgs[i]); + isPrimary[i] = mcParts[i].isPhysicalPrimary() ? 1 : 0; + mcTotalCharge += trackSigns[i]; + } + + auto mcCollision = mcParts[0].template udMcCollision_as(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcCollisionIndex = static_cast(mcCollision.globalIndex()); + } + + if (mi.found) { + motherPdg = mi.pdg; + motherPt = pt(mi.px, mi.py); + motherPhi = phi(mi.px, mi.py); + PxPyPzMVector genSystem; + for (const auto& p : mcParts) { + genSystem += PxPyPzMVector(p.px(), p.py(), p.pz(), o2::constants::physics::MassPionCharged); + } + motherMass = genSystem.M(); + motherRap = genSystem.Rapidity(); + + mcRegistry.fill(HIST("MC/Control/hMotherPdg"), motherPdg); + mcRegistry.fill(HIST("MC/Match/hMatchedGenM"), motherMass); + mcRegistry.fill(HIST("MC/Match/hMatchedGenPt"), motherPt); + mcRegistry.fill(HIST("MC/Match/hMatchedGenY"), motherRap); + + PxPyPzMVector recoSystem; + for (const auto& track : selTrks) { + recoSystem += PxPyPzMVector(track.px(), track.py(), track.pz(), o2::constants::physics::MassPionCharged); + } + mcRegistry.fill(HIST("MC/Match/hRecoVsGenM"), motherMass, recoSystem.M()); + if (motherPdg == 30113) { // only when the mother found is actually the rho prime + if (isReconstructedWithUPC) { + mcRegistry.fill(HIST("MC/RhoPrime/hMassUPC"), recoSystem.M()); + } else { + mcRegistry.fill(HIST("MC/RhoPrime/hMassSTD"), recoSystem.M()); + } + } + } + + fourPiMcMatchTree( isReconstructedWithUPC, - collision.timeZNA(), - collision.timeZNC(), - collision.energyCommonZNA(), - collision.energyCommonZNC(), - true, // Always charge zero for our selection - collision.occupancyInTime(), - collision.hadronicRate()); + motherPdg, motherPt, motherPhi, motherMass, motherRap, mcTotalCharge, + trackPdgs, trackPts, trackEtas, trackPhis, trackSigns, isPrimary, + mcPosX, mcPosY, mcPosZ, mcCollisionIndex, mcRunNumber, + systemTree.lastIndex()); + } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processMcCols, "Match MC reco<->gen (MC only)", false); + + // processMcGenAll: MC All generated collisions + void processMcGenAll(UDMcCollisions::iterator const& mcCollision, UDMcParticles const& mcParticles, aod::BCs const& bcs) + { + if (genId != -1 && mcCollision.generatorsID() != genId) { + return; + } + + int mcRunNumber = getMcRunNumber(bcs); + + mcRegistry.fill(HIST("MC/Events/hAllEvents"), 0); + mcRegistry.fill(HIST("MC/Events/hVertexZ"), mcCollision.posZ()); + + std::vector primaries; + for (const auto& part : mcParticles) { + if (part.isPhysicalPrimary()) { + primaries.push_back(part); + } + } + mcRegistry.fill(HIST("MC/Events/hNPrimaries"), primaries.size()); + + if (primaries.size() != 4) { + return; + } + mcRegistry.fill(HIST("MC/Summary/hEventCounter"), 0); + + int trackPdgs[4] = {0, 0, 0, 0}; + float trackPts[4] = {0, 0, 0, 0}; + float trackEtas[4] = {0, 0, 0, 0}; + float trackPhis[4] = {0, 0, 0, 0}; + int trackSigns[4] = {0, 0, 0, 0}; + int isPrimary[4] = {1, 1, 1, 1}; + int mcTotalCharge = 0; + + PxPyPzMVector genSystem; + for (size_t i = 0; i < 4; i++) { + const auto& p = primaries[i]; + trackPdgs[i] = p.pdgCode(); + trackPts[i] = pt(p.px(), p.py()); + trackEtas[i] = eta(p.px(), p.py(), p.pz()); + trackPhis[i] = phi(p.px(), p.py()); + trackSigns[i] = signFromPdg(trackPdgs[i]); + mcTotalCharge += trackSigns[i]; + + mcRegistry.fill(HIST("MC/Tracks/hPt"), trackPts[i]); + mcRegistry.fill(HIST("MC/Tracks/hEta"), trackEtas[i]); + mcRegistry.fill(HIST("MC/Tracks/hPhi"), trackPhis[i] + o2::constants::math::PI); // same 0-2pi convention as System/hPhi + mcRegistry.fill(HIST("MC/Tracks/hPdgCode"), trackPdgs[i]); + + genSystem += PxPyPzMVector(p.px(), p.py(), p.pz(), o2::constants::physics::MassPionCharged); + } + + // Generic common-mother search + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + MotherInfo mi = findCommonMotherGeneric(primaries); + mcRegistry.fill(HIST("MC/Control/hNDaughtersOfMother"), mi.found ? 4 : 0); + if (mi.found) { + motherPdg = mi.pdg; + motherPt = pt(mi.px, mi.py); + motherPhi = phi(mi.px, mi.py); + motherMass = genSystem.M(); + motherRap = genSystem.Rapidity(); + mcRegistry.fill(HIST("MC/Control/hMotherPdg"), motherPdg); + } + + mcRegistry.fill(HIST("MC/System/hM"), genSystem.M()); + mcRegistry.fill(HIST("MC/System/hPt"), genSystem.Pt()); + mcRegistry.fill(HIST("MC/System/hY"), genSystem.Rapidity()); + mcRegistry.fill(HIST("MC/System/hMvsPt"), genSystem.M(), genSystem.Pt()); + mcRegistry.fill(HIST("MC/System/hMvsY"), genSystem.M(), genSystem.Rapidity()); + mcRegistry.fill(HIST("MC/System/hTotalCharge"), mcTotalCharge); + + if (motherPdg == 30113) { + mcRegistry.fill(HIST("MC/RhoPrime/hFound"), 1); + mcRegistry.fill(HIST("MC/RhoPrime/hMass"), motherMass); + mcRegistry.fill(HIST("MC/RhoPrime/hPt"), motherPt); + } else { + mcRegistry.fill(HIST("MC/RhoPrime/hFound"), 0); + } + + fourPiMcGenAllTree( + motherPdg, motherPt, motherPhi, motherMass, motherRap, mcTotalCharge, + trackPdgs, trackPts, trackEtas, trackPhis, trackSigns, isPrimary, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), + static_cast(mcCollision.globalIndex()), mcRunNumber); + + mcRegistry.fill(HIST("MC/Events/hAccepted"), 0); } + PROCESS_SWITCH(upcRhoPrimeAnalysis, processMcGenAll, "All generated collisions (MC only)", false); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc, TaskName{"upc-rho-prime-analysis"})}; } diff --git a/PWGUD/Tasks/upcSemiFwdJpsiRl.cxx b/PWGUD/Tasks/upcSemiFwdJpsiRl.cxx new file mode 100644 index 00000000000..e9f7aa1e34a --- /dev/null +++ b/PWGUD/Tasks/upcSemiFwdJpsiRl.cxx @@ -0,0 +1,533 @@ +// Copyright 2019-2026 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file upcSemiFwdJpsiRl.cxx +/// \brief UPC semi-forward J/psi -> mu+mu- analysis pairing one forward MCH-MID muon +/// with one central-barrel track. Consumes UD tables produced by +/// upcCandProducerSemiFwd. +/// \author Roman Lavicka, roman.lavicka@cern.ch +/// \since 10.05.2026 +/// +/// executable name o2-analysis-ud-upc-semi-fwd-jpsi-rl + +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include +#include + +#include +#include +#include +#include +#include + +// table for saving tree with semi-forward dimuon info +namespace jpsisfw +{ +// dimuon +DECLARE_SOA_COLUMN(RunNumber, runNumber, int); +DECLARE_SOA_COLUMN(M, m, float); +DECLARE_SOA_COLUMN(Energy, energy, float); +DECLARE_SOA_COLUMN(Px, px, float); +DECLARE_SOA_COLUMN(Py, py, float); +DECLARE_SOA_COLUMN(Pz, pz, float); +DECLARE_SOA_COLUMN(Pt, pt, float); +DECLARE_SOA_COLUMN(Rap, rap, float); +DECLARE_SOA_COLUMN(Phi, phi, float); +DECLARE_SOA_COLUMN(PhiAv, phiAv, float); +DECLARE_SOA_COLUMN(PhiCh, phiCh, float); +// positive (p) and negative (n) tracks +DECLARE_SOA_COLUMN(EnergyP, energyP, float); +DECLARE_SOA_COLUMN(Pxp, pxp, float); +DECLARE_SOA_COLUMN(Pyp, pyp, float); +DECLARE_SOA_COLUMN(Pzp, pzp, float); +DECLARE_SOA_COLUMN(Ptp, ptp, float); +DECLARE_SOA_COLUMN(Etap, etap, float); +DECLARE_SOA_COLUMN(Phip, phip, float); +DECLARE_SOA_COLUMN(IsFwdP, isFwdP, int); // 1 if the positive track is forward, 0 if barrel +DECLARE_SOA_COLUMN(EnergyN, energyN, float); +DECLARE_SOA_COLUMN(Pxn, pxn, float); +DECLARE_SOA_COLUMN(Pyn, pyn, float); +DECLARE_SOA_COLUMN(Pzn, pzn, float); +DECLARE_SOA_COLUMN(Ptn, ptn, float); +DECLARE_SOA_COLUMN(Etan, etan, float); +DECLARE_SOA_COLUMN(Phin, phin, float); +DECLARE_SOA_COLUMN(IsFwdN, isFwdN, int); +// zn +DECLARE_SOA_COLUMN(Tzna, tzna, float); +DECLARE_SOA_COLUMN(Ezna, ezna, float); +DECLARE_SOA_COLUMN(Tznc, tznc, float); +DECLARE_SOA_COLUMN(Eznc, eznc, float); +DECLARE_SOA_COLUMN(Nclass, nclass, int); +} // namespace jpsisfw + +namespace o2::aod +{ +DECLARE_SOA_TABLE(JpsiSemiFwdRL, "AOD", "JPSISFW", + jpsisfw::RunNumber, + jpsisfw::M, jpsisfw::Energy, jpsisfw::Px, jpsisfw::Py, jpsisfw::Pz, jpsisfw::Pt, jpsisfw::Rap, jpsisfw::Phi, + jpsisfw::PhiAv, jpsisfw::PhiCh, + jpsisfw::EnergyP, jpsisfw::Pxp, jpsisfw::Pyp, jpsisfw::Pzp, jpsisfw::Ptp, jpsisfw::Etap, jpsisfw::Phip, jpsisfw::IsFwdP, + jpsisfw::EnergyN, jpsisfw::Pxn, jpsisfw::Pyn, jpsisfw::Pzn, jpsisfw::Ptn, jpsisfw::Etan, jpsisfw::Phin, jpsisfw::IsFwdN, + jpsisfw::Tzna, jpsisfw::Ezna, jpsisfw::Tznc, jpsisfw::Eznc, jpsisfw::Nclass); +} // namespace o2::aod + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +// constants used in the forward-muon selection (mirror upcFwdJpsiRl) +const float kRAbsMin = 17.6; +const float kRAbsMid = 26.5; +const float kRAbsMax = 89.5; +const float kPDca1 = 200.; +const float kPDca2 = 200.; +const float kFwdEtaMin = -4.0; +const float kFwdEtaMax = -2.5; +const float kFwdPtMin = 0.; + +const float kMaxAmpV0A = 100.; +const float kMaxZDCTime = 2.; +const float kMaxZDCTimeHisto = 10.; +const float kInvalidFloat = -999.; +const int kMaxRelBCsV0A = 1; + +struct UpcSemiFwdJpsiRl { + + using CandidatesSemiFwd = soa::Join; + using ForwardTracks = soa::Join; + using BarrelTracks = soa::Join; + + Produces dimuSel; + + HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry reg0n0n{"reg0n0n", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry regXn0n{"regXn0n", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + HistogramRegistry regXnXn{"regXnXn", {}, OutputObjHandlingPolicy::AnalysisObject, true, true}; + + // CONFIGURABLES + static constexpr double Pi = o2::constants::math::PI; + // pair kinematics + Configurable nBinsPt{"nBinsPt", 250, "N bins in pT histo"}; + Configurable lowPt{"lowPt", 0., "lower limit in pT histo"}; + Configurable highPt{"highPt", 2., "upper limit in pT histo"}; + Configurable nBinsMass{"nBinsMass", 500, "N bins in mass histo"}; + Configurable lowMass{"lowMass", 0., "lower limit in mass histo"}; + Configurable highMass{"highMass", 10., "upper limit in mass histo"}; + Configurable nBinsEta{"nBinsEta", 600, "N bins in eta histo"}; + Configurable lowEta{"lowEta", -10., "lower limit in eta histo"}; + Configurable highEta{"highEta", 5., "upper limit in eta histo"}; + Configurable nBinsRapidity{"nBinsRapidity", 500, "N bins in rapidity histo"}; + Configurable lowRapidity{"lowRapidity", -4.5, "lower limit in rapidity histo"}; + Configurable highRapidity{"highRapidity", 0., "upper limit in rapidity histo"}; + Configurable nBinsPhi{"nBinsPhi", 600, "N bins in phi histo"}; + Configurable lowPhi{"lowPhi", -Pi, "lower limit in phi histo"}; + Configurable highPhi{"highPhi", Pi, "upper limit in phi histo"}; + // single-track histos + Configurable nBinsPtSingle{"nBinsPtSingle", 500, "N bins in pT histo single track"}; + Configurable lowPtSingle{"lowPtSingle", 0., "lower limit in pT histo single track"}; + Configurable highPtSingle{"highPtSingle", 5., "upper limit in pT histo single track"}; + Configurable nBinsEtaSingle{"nBinsEtaSingle", 250, "N bins in eta histo single track"}; + Configurable lowEtaSingle{"lowEtaSingle", -4.5, "lower limit in eta histo single track"}; + Configurable highEtaSingle{"highEtaSingle", 1.5, "upper limit in eta histo single track"}; + Configurable nBinsPhiSingle{"nBinsPhiSingle", 600, "N bins in phi histo single track"}; + Configurable lowPhiSingle{"lowPhiSingle", -Pi, "lower limit in phi histo single track"}; + Configurable highPhiSingle{"highPhiSingle", Pi, "upper limit in phi histo single track"}; + // ZDC + Configurable nBinsZDCen{"nBinsZDCen", 200, "N bins in ZN energy"}; + Configurable lowEnZN{"lowEnZN", -50., "lower limit in ZN energy histo"}; + Configurable highEnZN{"highEnZN", 250., "upper limit in ZN energy histo"}; + + // central-barrel-muon selection + Configurable barEtaMin{"barEtaMin", -0.9, "barrel min eta"}; + Configurable barEtaMax{"barEtaMax", 0.9, "barrel max eta"}; + Configurable barPtMin{"barPtMin", 0.7, "barrel min pT (GeV/c)"}; + Configurable barMinITSNCls{"barMinITSNCls", 4, "barrel min number of ITS clusters"}; + Configurable barMinTPCNClsCR{"barMinTPCNClsCR", 70, "barrel min number of TPC crossed rows"}; + Configurable barMaxTpcNSigmaMu{"barMaxTpcNSigmaMu", 4., "barrel max |TPC nSigma muon|"}; + Configurable barRequireTOF{"barRequireTOF", false, "require TOF on the barrel track"}; + Configurable barMaxTofNSigmaMu{"barMaxTofNSigmaMu", 3., "barrel max |TOF nSigma muon| when TOF is present"}; + + void init(InitContext&) + { + std::vector ptFitBinning = { + 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, + 0.11, 0.12, 0.13, 0.14, 0.15, 0.175, 0.20, 0.25, 0.30, 0.40, 0.50, + 0.60, 0.70, 0.80, 0.90, 1.00, 1.20, 1.40, 1.60, 1.80, 2.00, 2.50, + 3.00, 3.50}; + + const AxisSpec axisPt{nBinsPt, lowPt, highPt, "#it{p}_{T} GeV/#it{c}"}; + const AxisSpec axisPtFit = {ptFitBinning, "#it{p}_{T} (GeV/c)"}; + const AxisSpec axisMass{nBinsMass, lowMass, highMass, "m_{#mu#mu} GeV/#it{c}^{2}"}; + const AxisSpec axisEta{nBinsEta, lowEta, highEta, "#eta"}; + const AxisSpec axisRapidity{nBinsRapidity, lowRapidity, highRapidity, "Rapidity"}; + const AxisSpec axisPhi{nBinsPhi, lowPhi, highPhi, "#varphi"}; + const AxisSpec axisPtSingle{nBinsPtSingle, lowPtSingle, highPtSingle, "#it{p}_{T}_{ trk} GeV/#it{c}"}; + const AxisSpec axisTimeZN{200, -10, 10, "ZDC time (ns)"}; + const AxisSpec axisEnergyZNA{nBinsZDCen, lowEnZN, highEnZN, "ZNA energy (TeV)"}; + const AxisSpec axisEnergyZNC{nBinsZDCen, lowEnZN, highEnZN, "ZNC energy (TeV)"}; + const AxisSpec axisEtaSingle{nBinsEtaSingle, lowEtaSingle, highEtaSingle, "#eta_{trk}"}; + const AxisSpec axisPhiSingle{nBinsPhiSingle, lowPhiSingle, highPhiSingle, "#varphi_{trk}"}; + + // pair histos + registry.add("hMass", "Invariant mass of mu pairs;;#counts", kTH1D, {axisMass}); + registry.add("hPt", "Transverse momentum of mu pairs;;#counts", kTH1D, {axisPt}); + registry.add("hPtFit", "Transverse momentum of mu pairs;;#counts", kTH1D, {axisPtFit}); + registry.add("hEta", "Pseudorapidity of mu pairs;;#counts", kTH1D, {axisEta}); + registry.add("hRapidity", "Rapidity of mu pairs;;#counts", kTH1D, {axisRapidity}); + registry.add("hPhi", "#varphi of mu pairs;;#counts", kTH1D, {axisPhi}); + registry.add("hCharge", "Charge;;;#counts", kTH1D, {{5, -2.5, 2.5}}); + registry.add("hContrib", "hContrib;;#counts", kTH1D, {{20, -0.5, 19.5}}); + registry.add("hEvSign", "Sum of the charges of all the tracks in each event;;#counts", kTH1D, {{5, -2.5, 2.5}}); + registry.add("hSameSign", "hSameSign;;#counts", kTH1D, {{20, -0.5, 19.5}}); + registry.add("hPhiCharge", "#phi #it{charge}", kTH1D, {axisPhi}); + registry.add("hPhiAverage", "#phi #it{average}", kTH1D, {axisPhi}); + + // forward / barrel single-track histos + registry.add("hPtTrkFwd", "Pt of forward muons;;#counts", kTH1D, {axisPtSingle}); + registry.add("hPtTrkBar", "Pt of barrel muons;;#counts", kTH1D, {axisPtSingle}); + registry.add("hEtaTrkFwd", "#eta of forward muons;;#counts", kTH1D, {axisEtaSingle}); + registry.add("hEtaTrkBar", "#eta of barrel muons;;#counts", kTH1D, {axisEtaSingle}); + registry.add("hPhiTrkFwd", "#varphi of forward muons;;#counts", kTH1D, {axisPhiSingle}); + registry.add("hPhiTrkBar", "#varphi of barrel muons;;#counts", kTH1D, {axisPhiSingle}); + + // ZDC + registry.add("hTimeZNA", "ZNA Times;;#counts", kTH1D, {axisTimeZN}); + registry.add("hTimeZNC", "ZNC Times;;#counts", kTH1D, {axisTimeZN}); + registry.add("hEnergyZN", "ZNA vs ZNC energy", kTH2D, {axisEnergyZNA, axisEnergyZNC}); + + // neutron classes + reg0n0n.add("hMass", "Invariant mass of mu pairs - 0n0n;;#counts", kTH1D, {axisMass}); + reg0n0n.add("hPt", "Transverse momentum of mu pairs - 0n0n;;#counts", kTH1D, {axisPt}); + reg0n0n.add("hEta", "Pseudorapidity of mu pairs - 0n0n;;#counts", kTH1D, {axisEta}); + reg0n0n.add("hRapidity", "Rapidity of mu pairs - 0n0n;;#counts", kTH1D, {axisRapidity}); + reg0n0n.add("hPtFit", "Transverse momentum of mu pairs - 0n0n;;#counts", kTH1D, {axisPtFit}); + + regXn0n.add("hMass", "Invariant mass of mu pairs - Xn0n;;#counts", kTH1D, {axisMass}); + regXn0n.add("hPt", "Transverse momentum of mu pairs - Xn0n;;#counts", kTH1D, {axisPt}); + regXn0n.add("hEta", "Pseudorapidity of mu pairs - Xn0n;;#counts", kTH1D, {axisEta}); + regXn0n.add("hRapidity", "Rapidity of mu pairs - Xn0n;;#counts", kTH1D, {axisRapidity}); + regXn0n.add("hPtFit", "Transverse momentum of mu pairs - Xn0n;;#counts", kTH1D, {axisPtFit}); + + regXnXn.add("hMass", "Invariant mass of mu pairs - XnXn;;#counts", kTH1D, {axisMass}); + regXnXn.add("hPt", "Transverse momentum of mu pairs - XnXn;;#counts", kTH1D, {axisPt}); + regXnXn.add("hEta", "Pseudorapidity of mu pairs - XnXn;;#counts", kTH1D, {axisEta}); + regXnXn.add("hRapidity", "Rapidity of mu pairs - XnXn;;#counts", kTH1D, {axisRapidity}); + regXnXn.add("hPtFit", "Transverse momentum of mu pairs - XnXn;;#counts", kTH1D, {axisPtFit}); + } + + // FUNCTIONS + using LorentzVec = ROOT::Math::PxPyPzMVector; + + // collect tracks per candidate into a map + template + void collectCandIDs(std::unordered_map>& tracksPerCand, TTracks& tracks) + { + for (const auto& tr : tracks) { + int32_t candId = tr.udCollisionId(); + if (candId < 0) { + continue; + } + tracksPerCand[candId].push_back(tr.globalIndex()); + } + } + + struct ZDCinfo { + float timeA; + float timeC; + float enA; + float enC; + }; + + void collectCandZDCInfo(std::unordered_map& zdcPerCand, o2::aod::UDZdcsReduced const& ZDCs) + { + for (const auto& zdc : ZDCs) { + int32_t candId = zdc.udCollisionId(); + if (candId < 0) { + continue; + } + zdcPerCand[candId].timeA = zdc.timeZNA(); + zdcPerCand[candId].timeC = zdc.timeZNC(); + zdcPerCand[candId].enA = zdc.energyCommonZNA(); + zdcPerCand[candId].enC = zdc.energyCommonZNC(); + + if (std::isinf(zdcPerCand[candId].timeA)) + zdcPerCand[candId].timeA = kInvalidFloat; + if (std::isinf(zdcPerCand[candId].timeC)) + zdcPerCand[candId].timeC = kInvalidFloat; + if (std::isinf(zdcPerCand[candId].enA)) + zdcPerCand[candId].enA = kInvalidFloat; + if (std::isinf(zdcPerCand[candId].enC)) + zdcPerCand[candId].enC = kInvalidFloat; + } + } + + // forward-muon selection (MCH-MID muon, like upcFwdJpsiRl) + template + bool isFwdMuonSelected(const TTrack& fwdTrack) + { + float rAbs = fwdTrack.rAtAbsorberEnd(); + float pDca = fwdTrack.pDca(); + auto mMu = o2::constants::physics::MassMuon; + LorentzVec p(fwdTrack.px(), fwdTrack.py(), fwdTrack.pz(), mMu); + float eta = p.Eta(); + float pt = p.Pt(); + float pDcaMax = rAbs < kRAbsMid ? kPDca1 : kPDca2; + + if (eta < kFwdEtaMin || eta > kFwdEtaMax) + return false; + if (pt < kFwdPtMin) + return false; + if (rAbs < kRAbsMin || rAbs > kRAbsMax) + return false; + if (pDca > pDcaMax) + return false; + if (fwdTrack.chi2MatchMCHMID() <= 0) // require MID match + return false; + return true; + } + + // barrel-muon selection + template + bool isBarrelMuonSelected(const TTrack& barTrack) + { + auto mMu = o2::constants::physics::MassMuon; + LorentzVec p(barTrack.px(), barTrack.py(), barTrack.pz(), mMu); + float eta = p.Eta(); + float pt = p.Pt(); + + if (pt < barPtMin) + return false; + if (eta < barEtaMin || eta > barEtaMax) + return false; + if (barTrack.itsNCls() < static_cast(barMinITSNCls)) + return false; + if (barTrack.tpcNClsCrossedRows() < static_cast(barMinTPCNClsCR)) + return false; + if (std::abs(barTrack.tpcNSigmaMu()) > barMaxTpcNSigmaMu) + return false; + if (barRequireTOF && !barTrack.hasTOF()) + return false; + if (barTrack.hasTOF() && std::abs(barTrack.tofNSigmaMu()) > barMaxTofNSigmaMu) + return false; + return true; + } + + // azimuth anisotropy phi + void computePhiAnis(const LorentzVec& p1, const LorentzVec& p2, int sign1, float& phiAverage, float& phiCharge) + { + auto tSum = p1 + p2; + float halfUnity = 0.5; + decltype(tSum) tDiffCh, tDiffAv; + if (sign1 > 0) { + tDiffCh = p1 - p2; + if (gRandom->Rndm() > halfUnity) + tDiffAv = p1 - p2; + else + tDiffAv = p2 - p1; + } else { + tDiffCh = p2 - p1; + if (gRandom->Rndm() > halfUnity) + tDiffAv = p2 - p1; + else + tDiffAv = p1 - p2; + } + phiAverage = ROOT::Math::VectorUtil::DeltaPhi(tSum, tDiffAv); + phiCharge = ROOT::Math::VectorUtil::DeltaPhi(tSum, tDiffCh); + } + + // process a single (forward, barrel) pair + void processPair(CandidatesSemiFwd::iterator const& cand, + ForwardTracks::iterator const& fwd, BarrelTracks::iterator const& bar, + ZDCinfo const& zdc) + { + // V0 selection + const auto& ampsV0A = cand.amplitudesV0A(); + const auto& ampsRelBCsV0A = cand.ampRelBCsV0A(); + for (unsigned int i = 0; i < ampsV0A.size(); ++i) { + if (std::abs(ampsRelBCsV0A[i]) <= kMaxRelBCsV0A) { + if (ampsV0A[i] > kMaxAmpV0A) + return; + } + } + + // opposite charge only + int pairCharge = fwd.sign() + bar.sign(); + if (pairCharge != 0) { + registry.fill(HIST("hSameSign"), cand.numContrib()); + return; + } + + // track selection + if (!isFwdMuonSelected(fwd)) + return; + if (!isBarrelMuonSelected(bar)) + return; + + // form Lorentz vectors + auto mMu = o2::constants::physics::MassMuon; + LorentzVec pFwd(fwd.px(), fwd.py(), fwd.pz(), mMu); + LorentzVec pBar(bar.px(), bar.py(), bar.pz(), mMu); + auto p = pFwd + pBar; + + // pair-level cuts + if (p.M() < lowMass || p.M() > highMass) + return; + if (p.Pt() < lowPt || p.Pt() > highPt) + return; + if (p.Rapidity() < lowRapidity || p.Rapidity() > highRapidity) + return; + + // azimuth anisotropy + float phiAverage = 0; + float phiCharge = 0; + computePhiAnis(pFwd, pBar, fwd.sign(), phiAverage, phiCharge); + + // ZDC info histograms + if (std::abs(zdc.timeA) < kMaxZDCTimeHisto) + registry.fill(HIST("hTimeZNA"), zdc.timeA); + if (std::abs(zdc.timeC) < kMaxZDCTimeHisto) + registry.fill(HIST("hTimeZNC"), zdc.timeC); + registry.fill(HIST("hEnergyZN"), zdc.enA, zdc.enC); + + // neutron classes + bool neutronA = std::abs(zdc.timeA) < kMaxZDCTime && !std::isinf(zdc.timeA); + bool neutronC = std::abs(zdc.timeC) < kMaxZDCTime && !std::isinf(zdc.timeC); + int znClass = -1; + + if (!neutronC && !neutronA) { + znClass = 1; + reg0n0n.fill(HIST("hMass"), p.M()); + reg0n0n.fill(HIST("hPt"), p.Pt()); + reg0n0n.fill(HIST("hPtFit"), p.Pt()); + reg0n0n.fill(HIST("hEta"), p.Eta()); + reg0n0n.fill(HIST("hRapidity"), p.Rapidity()); + } else if (neutronA ^ neutronC) { + znClass = neutronA ? 2 : 3; + regXn0n.fill(HIST("hMass"), p.M()); + regXn0n.fill(HIST("hPt"), p.Pt()); + regXn0n.fill(HIST("hPtFit"), p.Pt()); + regXn0n.fill(HIST("hEta"), p.Eta()); + regXn0n.fill(HIST("hRapidity"), p.Rapidity()); + } else if (neutronA && neutronC) { + znClass = 4; + regXnXn.fill(HIST("hMass"), p.M()); + regXnXn.fill(HIST("hPt"), p.Pt()); + regXnXn.fill(HIST("hPtFit"), p.Pt()); + regXnXn.fill(HIST("hEta"), p.Eta()); + regXnXn.fill(HIST("hRapidity"), p.Rapidity()); + } + + // single-track histos by side + registry.fill(HIST("hPtTrkFwd"), pFwd.Pt()); + registry.fill(HIST("hPtTrkBar"), pBar.Pt()); + registry.fill(HIST("hEtaTrkFwd"), pFwd.Eta()); + registry.fill(HIST("hEtaTrkBar"), pBar.Eta()); + registry.fill(HIST("hPhiTrkFwd"), pFwd.Phi()); + registry.fill(HIST("hPhiTrkBar"), pBar.Phi()); + + // pair histos + registry.fill(HIST("hContrib"), cand.numContrib()); + registry.fill(HIST("hEvSign"), cand.netCharge()); + registry.fill(HIST("hMass"), p.M()); + registry.fill(HIST("hPt"), p.Pt()); + registry.fill(HIST("hPtFit"), p.Pt()); + registry.fill(HIST("hEta"), p.Eta()); + registry.fill(HIST("hRapidity"), p.Rapidity()); + registry.fill(HIST("hPhi"), p.Phi()); + registry.fill(HIST("hCharge"), fwd.sign()); + registry.fill(HIST("hCharge"), bar.sign()); + registry.fill(HIST("hPhiAverage"), phiAverage); + registry.fill(HIST("hPhiCharge"), phiCharge); + + // assign positive/negative leg + bool fwdIsPos = fwd.sign() > 0; + const LorentzVec& pPos = fwdIsPos ? pFwd : pBar; + const LorentzVec& pNeg = fwdIsPos ? pBar : pFwd; + int isFwdPos = fwdIsPos ? 1 : 0; + int isFwdNeg = fwdIsPos ? 0 : 1; + + dimuSel(cand.runNumber(), + p.M(), p.E(), p.Px(), p.Py(), p.Pz(), p.Pt(), p.Rapidity(), p.Phi(), + phiAverage, phiCharge, + pPos.E(), pPos.Px(), pPos.Py(), pPos.Pz(), pPos.Pt(), pPos.Eta(), pPos.Phi(), isFwdPos, + pNeg.E(), pNeg.Px(), pNeg.Py(), pNeg.Pz(), pNeg.Pt(), pNeg.Eta(), pNeg.Phi(), isFwdNeg, + zdc.timeA, zdc.enA, zdc.timeC, zdc.enC, znClass); + } + + // PROCESS FUNCTION + void processData(CandidatesSemiFwd const& eventCandidates, + o2::aod::UDZdcsReduced const& ZDCs, + ForwardTracks const& fwdTracks, + BarrelTracks const& barrelTracks) + { + std::unordered_map> fwdTracksPerCand; + collectCandIDs(fwdTracksPerCand, fwdTracks); + + std::unordered_map> barTracksPerCand; + collectCandIDs(barTracksPerCand, barrelTracks); + + std::unordered_map zdcPerCand; + collectCandZDCInfo(zdcPerCand, ZDCs); + + // loop over candidates that have at least one forward track + for (const auto& [candID, fwdIds] : fwdTracksPerCand) { + auto itBar = barTracksPerCand.find(candID); + if (itBar == barTracksPerCand.end()) + continue; // need at least one barrel companion + const auto& barIds = itBar->second; + auto cand = eventCandidates.iteratorAt(candID); + + ZDCinfo zdc; + auto itZdc = zdcPerCand.find(candID); + if (itZdc != zdcPerCand.end()) { + zdc = itZdc->second; + } else { + zdc.timeA = kInvalidFloat; + zdc.timeC = kInvalidFloat; + zdc.enA = kInvalidFloat; + zdc.enC = kInvalidFloat; + } + + for (const auto& fwdIdx : fwdIds) { + for (const auto& barIdx : barIds) { + auto fwd = fwdTracks.iteratorAt(fwdIdx); + auto bar = barrelTracks.iteratorAt(barIdx); + processPair(cand, fwd, bar, zdc); + } + } + } + } + + PROCESS_SWITCH(UpcSemiFwdJpsiRl, processData, "Process semi-forward J/psi candidates", true); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc), + }; +} diff --git a/PWGUD/Tasks/upcTauRl.cxx b/PWGUD/Tasks/upcTauRl.cxx index 264dae05eb8..52a085ad67f 100644 --- a/PWGUD/Tasks/upcTauRl.cxx +++ b/PWGUD/Tasks/upcTauRl.cxx @@ -683,7 +683,7 @@ struct UpcTauRl { std::vector>> cutMyRequiredITSHits{}; - void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, std::set requiredLayers) + void mySetRequireHitsInITSLayers(int8_t minNRequiredHits, const std::set& requiredLayers) { // layer 0 corresponds to the the innermost ITS layer cutMyRequiredITSHits.push_back(std::make_pair(minNRequiredHits, requiredLayers)); diff --git a/PWGUD/Tasks/upcTauTau13topo.cxx b/PWGUD/Tasks/upcTauTau13topo.cxx index a14ebd9cb35..d055406f509 100644 --- a/PWGUD/Tasks/upcTauTau13topo.cxx +++ b/PWGUD/Tasks/upcTauTau13topo.cxx @@ -1640,7 +1640,7 @@ struct TauTau13topo { } // helper function to calculate delta alpha - float deltaAlpha(auto particle1, auto particle2) + float deltaAlpha(const auto& particle1, const auto& particle2) { TVector3 vtmp(particle1.px(), particle1.py(), particle1.pz()); @@ -1657,7 +1657,7 @@ struct TauTau13topo { // } // float calculateDeltaPhi(TLorentzVector p, TLorentzVector p1) - float calculateDeltaPhi(ROOT::Math::LorentzVector> p, ROOT::Math::LorentzVector> p1) + float calculateDeltaPhi(const ROOT::Math::LorentzVector>& p, const ROOT::Math::LorentzVector>& p1) { // float delta = p.Phi(); float delta = RecoDecay::constrainAngle(p.Phi()); @@ -1708,7 +1708,7 @@ struct TauTau13topo { // } // helper function to calculate scalar asymmetry - float scalarAsymMC(auto particle1, auto particle2) + float scalarAsymMC(const auto& particle1, const auto& particle2) { // auto pt1 = pt(particle1.px(), particle1.py()); auto pt1 = RecoDecay::pt(particle1.px(), particle1.py()); @@ -1719,7 +1719,7 @@ struct TauTau13topo { } // helper function to calculate vector asymmetry - float vectorAsym(auto particle1, auto particle2) + float vectorAsym(const auto& particle1, const auto& particle2) { auto delta = std::sqrt((particle1.px() - particle2.px()) * (particle1.px() - particle2.px()) + (particle1.py() - particle2.py()) * (particle1.py() - particle2.py())); @@ -1796,7 +1796,7 @@ struct TauTau13topo { } template - int trackCheck(T track) + int trackCheck(const T& track) { // 1 if (track.hasITS() && !track.hasTPC() && !track.hasTRD() && !track.hasTOF()) @@ -1837,7 +1837,7 @@ struct TauTau13topo { // global track check + histogram cuts separatelly template - bool isGlobalTrackCheck(T track) + bool isGlobalTrackCheck(const T& track) { bool isGlobalTrack = true; registry.get(HIST("global/hTrackEfficiencyPVGlobal"))->Fill(0., 1.); @@ -1921,7 +1921,7 @@ struct TauTau13topo { // analysis track quality check with histogram filling template - bool isGoodTrackCheckHisto(T track) + bool isGoodTrackCheckHisto(const T& track) { bool isGoodTrack = true; registry.get(HIST("global/hTrackPVGood"))->Fill(0., 1.); @@ -1973,7 +1973,7 @@ struct TauTau13topo { // analysis track quality check template - bool isGoodTrackCheck(T track) + bool isGoodTrackCheck(const T& track) { if (!track.hasTPC()) return false; @@ -1994,7 +1994,7 @@ struct TauTau13topo { // analysis track quality check template - bool isGoodTOFTrackCheckHisto(T track) + bool isGoodTOFTrackCheckHisto(const T& track) { bool isGoodTrack = true; if (track.hasTOF()) { @@ -2012,7 +2012,7 @@ struct TauTau13topo { // analysis track quality check template - bool isGoodTOFTrackCheck(T track) + bool isGoodTOFTrackCheck(const T& track) { if (!track.hasTOF()) return false; @@ -2024,7 +2024,7 @@ struct TauTau13topo { // check ITS clusters, how many -1,0,1,7 + 10 if 0,1,2 layers were fired // analysis track quality check template - int numberOfItsClustersCheck(T track) + int numberOfItsClustersCheck(const T& track) { if (!track.hasITS()) return -1; diff --git a/PWGUD/Tasks/upcTestFITBitMapping.cxx b/PWGUD/Tasks/upcTestFITBitMapping.cxx deleted file mode 100644 index eefd4f0aa83..00000000000 --- a/PWGUD/Tasks/upcTestFITBitMapping.cxx +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019-2020 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. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -// \FIT bits to phi, eta mapping -// \author Sandor Lokos, sandor.lokos@cern.ch -// \since March 2026 - -#include "PWGUD/Core/UDHelpers.h" // udhelpers::Bits256, makeBits256, testBit, getPhiEtaFromFitBit -#include "PWGUD/DataModel/UDTables.h" // aod::UDCollisionFITBits - -#include // o2::ft0::Geometry -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -using namespace o2; -using namespace o2::framework; - -struct UpcTestFITBitMapping { - Configurable whichThr{"whichThr", 1, "Use 1=Thr1 bits or 2=Thr2 bits"}; - Configurable maxEvents{"maxEvents", -1, "Process at most this many rows (-1 = all)"}; - - // Minimal offset container compatible with UDHelpers.h expectations: getX/getY/getZ - struct OffsetXYZ { - double x{0}, y{0}, z{0}; - double getX() const { return x; } - double getY() const { return y; } - double getZ() const { return z; } - }; - - std::array offsetFT0{}; // iRunOffset = 0 for now - int iRunOffset = 0; - - o2::ft0::Geometry ft0Det{}; - - HistogramRegistry registry{ - "registry", - { - {"hPhiA", "FT0A #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, 2. * M_PI}}}}, - {"hEtaA", "FT0A #eta;#eta;counts", {HistType::kTH1F, {{8, 3.5, 5.0}}}}, - {"hEtaPhiA", "FT0A #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0.0, 2. * M_PI}}}}, - - {"hPhiC", "FT0C #varphi;#varphi;counts", {HistType::kTH1F, {{18, 0.0, 2. * M_PI}}}}, - {"hEtaC", "FT0C #eta;#eta;counts", {HistType::kTH1F, {{8, -3.5, -2.0}}}}, - {"hEtaPhiC", "FT0C #eta vs #varphi;#eta;#varphi", {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0.0, 2. * M_PI}}}}, - }}; - - void init(InitContext&) - { - // UDHelpers calls calculateChannelCenter() inside, but doing it once here is fine. - ft0Det.calculateChannelCenter(); - } - - void process(aod::UDCollisionFITBits const& bitsTable) - { - int64_t nProcessed = 0; - - for (auto const& row : bitsTable) { - if (maxEvents >= 0 && nProcessed >= maxEvents) { - break; - } - ++nProcessed; - - // Use udhelpers' canonical packed type + builder - udhelpers::Bits256 w{}; - if (whichThr == 2) { - w = udhelpers::makeBits256(row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); - } else { - w = udhelpers::makeBits256(row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); - } - - // Loop FT0 bits only (0..207). FV0 starts at 208 but ignored here. - for (int bit = 0; bit < udhelpers::kFT0Bits; ++bit) { - if (!udhelpers::testBit(w, bit)) { - continue; - } - - double phi = 0., eta = 0.; - const bool ok = udhelpers::getPhiEtaFromFitBit(ft0Det, bit, offsetFT0, iRunOffset, phi, eta); - if (!ok) { - continue; - } - - if (bit < udhelpers::kFT0AChannels) { - registry.fill(HIST("hPhiA"), phi); - registry.fill(HIST("hEtaA"), eta); - registry.fill(HIST("hEtaPhiA"), eta, phi); - } else { - registry.fill(HIST("hPhiC"), phi); - registry.fill(HIST("hEtaC"), eta); - registry.fill(HIST("hEtaPhiC"), eta, phi); - } - } - } - } -}; - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - return WorkflowSpec{ - adaptAnalysisTask(cfgc, TaskName{"fitbit-mapping"})}; -} diff --git a/PWGUD/Tasks/upcTestFitBitMapping.cxx b/PWGUD/Tasks/upcTestFitBitMapping.cxx new file mode 100644 index 00000000000..829e1df5f52 --- /dev/null +++ b/PWGUD/Tasks/upcTestFitBitMapping.cxx @@ -0,0 +1,228 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// + +/// \file upcTestFitBitMapping.cxx +/// \brief Tutorial for accessing and mapping the FIT-bit table +/// \author Sandor Lokos, sandor.lokos@cern.ch +/// \since October 2026 + +#include "PWGUD/Core/UDHelpers.h" +#include "PWGUD/DataModel/UDTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace o2; +using namespace o2::framework; + +struct UpcTestFitBitMapping { + static constexpr int kThr1Selector = 1; + static constexpr int kThr2Selector = 2; + + Configurable whichThr{"whichThr", 1, "FIT amplitude threshold to use: 1 for Thr1 or 2 for Thr2"}; + + // Minimal zero-offset container required by getPhiEtaFromFitBit(). + // Detector-alignment corrections are not applied in this tutorial. + struct OffsetXYZ { + double x{0.}; + double y{0.}; + double z{0.}; + + double getX() const { return x; } + double getY() const { return y; } + double getZ() const { return z; } + }; + + std::array offsetFT0{}; + static constexpr int kRunOffsetIndex = 0; + + o2::ft0::Geometry ft0Geometry{}; + + HistogramRegistry registry{ + "registry", + { + {"association/hRowsPerCollision", + "FIT-bit rows associated with each collision;rows;collisions", + {HistType::kTH1F, {{4, -0.5, 3.5}}}}, + + {"multiplicity/hFT0A", + "FT0A fired-channel multiplicity;N_{fired}^{FT0A};collisions", + {HistType::kTH1F, {{97, -0.5, 96.5}}}}, + {"multiplicity/hFT0C", + "FT0C fired-channel multiplicity;N_{fired}^{FT0C};collisions", + {HistType::kTH1F, {{113, -0.5, 112.5}}}}, + {"multiplicity/hFV0A", + "FV0A fired-channel multiplicity;N_{fired}^{FV0A};collisions", + {HistType::kTH1F, {{49, -0.5, 48.5}}}}, + {"multiplicity/hFT0AVsFT0C", + "FT0A versus FT0C fired channels;N_{fired}^{FT0A};N_{fired}^{FT0C}", + {HistType::kTH2F, {{97, -0.5, 96.5}, {113, -0.5, 112.5}}}}, + + {"occupancy/hPackedBit", + "Selected FIT-bit occupancy;packed bit;counts", + {HistType::kTH1F, {{256, -0.5, 255.5}}}}, + {"occupancy/hFT0AChannel", + "FT0A channel occupancy;FT0A channel;counts", + {HistType::kTH1F, {{96, -0.5, 95.5}}}}, + {"occupancy/hFT0CChannel", + "FT0C channel occupancy;FT0C channel;counts", + {HistType::kTH1F, {{112, -0.5, 111.5}}}}, + {"occupancy/hFV0AChannel", + "FV0A channel occupancy;FV0A channel;counts", + {HistType::kTH1F, {{48, -0.5, 47.5}}}}, + + {"mapping/hPhiA", + "FT0A #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaA", + "FT0A #eta;#eta;counts", + {HistType::kTH1F, {{8, 3.5, 5.0}}}}, + {"mapping/hEtaPhiA", + "FT0A #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, 3.5, 5.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYA", + "FT0A channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, + + {"mapping/hPhiC", + "FT0C #varphi;#varphi;counts", + {HistType::kTH1F, {{18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hEtaC", + "FT0C #eta;#eta;counts", + {HistType::kTH1F, {{8, -3.5, -2.0}}}}, + {"mapping/hEtaPhiC", + "FT0C #eta versus #varphi;#eta;#varphi", + {HistType::kTH2F, {{8, -3.5, -2.0}, {18, 0., o2::constants::math::TwoPI}}}}, + {"mapping/hXYC", + "FT0C channel positions;x (cm);y (cm)", + {HistType::kTH2F, {{24, -18., 18.}, {24, -18., 18.}}}}, + }}; + + void init(InitContext&) + { + if (whichThr.value != kThr1Selector && whichThr.value != kThr2Selector) { + LOGF(fatal, + "Invalid whichThr=%d: allowed values are 1 and 2", + whichThr.value); + } + + ft0Geometry.calculateChannelCenter(); + } + + template + udhelpers::Bits256 getSelectedBits(T const& row) const + { + if (whichThr.value == kThr2Selector) { + return udhelpers::makeBits256( + row.thr2W0(), row.thr2W1(), row.thr2W2(), row.thr2W3()); + } + + return udhelpers::makeBits256( + row.thr1W0(), row.thr1W1(), row.thr1W2(), row.thr1W3()); + } + + void process(aod::UDCollisions::iterator const&, + aod::UDCollisionFITBits const& fitBits) + { + // Because UDCollisionFITBits contains UDCollisionId, the framework + // supplies only the FIT-bit rows associated with the current collision. + registry.fill(HIST("association/hRowsPerCollision"), fitBits.size()); + + // The producer is expected to write exactly one FIT-bit row per collision. + if (fitBits.size() != 1) { + return; + } + + const auto row = fitBits.begin(); + const auto bits = getSelectedBits(row); + + int nFT0A = 0; + int nFT0C = 0; + int nFV0A = 0; + + // Packed layout: + // 0--95: FT0A + // 96--207: FT0C + // 208--255: FV0A + for (int bit = 0; bit < udhelpers::kTotalBits; ++bit) { + if (!udhelpers::testBit(bits, bit)) { + continue; + } + + registry.fill(HIST("occupancy/hPackedBit"), bit); + + const auto decoded = udhelpers::decodeFitBit(bit); + + if (decoded.det == udhelpers::FitBitRef::Det::FV0) { + ++nFV0A; + registry.fill(HIST("occupancy/hFV0AChannel"), decoded.ch); + continue; + } + + if (decoded.det != udhelpers::FitBitRef::Det::FT0) { + continue; + } + + if (decoded.isC) { + ++nFT0C; + registry.fill( + HIST("occupancy/hFT0CChannel"), + decoded.ch - udhelpers::kFT0AChannels); + } else { + ++nFT0A; + registry.fill(HIST("occupancy/hFT0AChannel"), decoded.ch); + } + + double phi = 0.; + double eta = 0.; + if (!udhelpers::getPhiEtaFromFitBit( + ft0Geometry, bit, offsetFT0, kRunOffsetIndex, phi, eta)) { + continue; + } + + const auto position = ft0Geometry.getChannelCenter(decoded.ch); + const double x = position.X() + offsetFT0[kRunOffsetIndex].getX(); + const double y = position.Y() + offsetFT0[kRunOffsetIndex].getY(); + + if (decoded.isC) { + registry.fill(HIST("mapping/hPhiC"), phi); + registry.fill(HIST("mapping/hEtaC"), eta); + registry.fill(HIST("mapping/hEtaPhiC"), eta, phi); + registry.fill(HIST("mapping/hXYC"), x, y); + } else { + registry.fill(HIST("mapping/hPhiA"), phi); + registry.fill(HIST("mapping/hEtaA"), eta); + registry.fill(HIST("mapping/hEtaPhiA"), eta, phi); + registry.fill(HIST("mapping/hXYA"), x, y); + } + } + + registry.fill(HIST("multiplicity/hFT0A"), nFT0A); + registry.fill(HIST("multiplicity/hFT0C"), nFT0C); + registry.fill(HIST("multiplicity/hFV0A"), nFV0A); + registry.fill(HIST("multiplicity/hFT0AVsFT0C"), nFT0A, nFT0C); + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/Tasks/testMcStdTabsRl.cxx b/PWGUD/Tasks/upcTestMcStdTabsRl.cxx similarity index 96% rename from PWGUD/Tasks/testMcStdTabsRl.cxx rename to PWGUD/Tasks/upcTestMcStdTabsRl.cxx index 7651b5e5c2a..d5477c9240e 100644 --- a/PWGUD/Tasks/testMcStdTabsRl.cxx +++ b/PWGUD/Tasks/upcTestMcStdTabsRl.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. // -/// \file testMcStdTabsRl.cxx +/// \file upcTestMcStdTabsRl.cxx /// \brief task to test the Monte Carlo UD production generatorIDs on hyperloop /// /// \author Roman Lavicka , Austrian Academy of Sciences & SMI @@ -41,7 +41,7 @@ using namespace o2::framework; using namespace o2::framework::expressions; using namespace o2::constants::physics; -struct TestMcStdTabsRl { +struct UpcTestMcStdTabsRl { // Global varialbes Service pdg; @@ -103,11 +103,11 @@ struct TestMcStdTabsRl { } // end processMCgenDG - PROCESS_SWITCH(TestMcStdTabsRl, processMCgen, "Iterate Monte Carlo UD tables with truth data.", true); + PROCESS_SWITCH(UpcTestMcStdTabsRl, processMCgen, "Iterate Monte Carlo UD tables with truth data.", true); }; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{ - adaptAnalysisTask(cfgc)}; + adaptAnalysisTask(cfgc)}; } diff --git a/PWGUD/Tasks/upcTrackVertexingQA.cxx b/PWGUD/Tasks/upcTrackVertexingQA.cxx new file mode 100644 index 00000000000..70900886cee --- /dev/null +++ b/PWGUD/Tasks/upcTrackVertexingQA.cxx @@ -0,0 +1,530 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file upcTrackVertexingQA.cxx +/// \brief task to study the performance of vertexing for low-multiplicity UPC collisions +/// \author Andrea Tavira Garcia a.tavira@cern.ch +/// \author Andrea Giovanni Riffero andrea.giovanni.riffero@cern.ch + +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // IWYU pragma: keep (do not replace with Math/Vector4Dfwd.h) +#include + +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +enum class CandSpecies { + kRho = 0, + kJpsi = 1 +}; + +struct UpcTrackVertexingQA { + // Configurables + Configurable yCandMax{"yCandMax", 0.8, "max. cand. rapidity"}; + Configurable ptCandMin{"ptCandMin", 0., "min. cand. pT (GeV/c)"}; + Configurable ptTrackMin{"ptTrackMin", 0.1, "min. track pT (GeV/c)"}; + Configurable etaTrackMax{"etaTrackMax", 0.9, "max. |eta| of tracks"}; + Configurable nMinTpcClusters{"nMinTpcClusters", 60, "min. number of TPC clusters"}; + + // Axes (the mass axis matches the default rho window; override it for the J/psi) + ConfigurableAxis axisMass{"axisMass", {200, 0.5, 1.3}, "m_{#pi#pi} (GeV/#it{c}^{2})"}; + ConfigurableAxis axisPt{"axisPt", {VARIABLE_WIDTH, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.175, 0.20, 0.25, 0.30, 0.40, 0.50}, "#it{p}_{T} (GeV/#it{c})"}; + ConfigurableAxis axisY{"axisY", {100, -1., 1.}, "Rapidity"}; + ConfigurableAxis axisPtTrk{"axisPtTrk", {80, 0.0, 0.8}, "#it{p}_{T} (GeV/#it{c})"}; + + HistogramConfigSpec hVtxZ{HistType::kTH1F, {{80, -20., 20.}}}; + HistogramConfigSpec hVtxX{HistType::kTH1F, {{50, -0.04, -0.015}}}; + HistogramConfigSpec hVtxY{HistType::kTH1F, {{60, -0.015, 0.015}}}; + HistogramConfigSpec hEtaTrk{HistType::kTH1F, {{100, -1.0, 1.0}}}; + + HistogramRegistry registry{"registry"}; + + // services + Service ccdb{}; // access to database + + // variables to store run info + int runNumberBc = 0; // run number used to process BCs + int runNumberCol = 0; // run number used to process collisions + int64_t sor = 0; // best known timestamp for the start of run + int64_t orbitsPerTF = 0; // number of orbits per TF + int64_t bcSOR = 0; // first bc of the first orbit + int64_t nBCsPerTF = 0; // duration of TF in bcs + int64_t currentTF = -1; // current time frame being looked at + int64_t nTF = 0; // number of time frames in run + + // constants + static constexpr unsigned int TwoTracks = 2; + static constexpr int UnassignedDCA = 999; + + static constexpr float MassPion = o2::constants::physics::MassPionCharged; + static constexpr float MassMuon = o2::constants::physics::MassMuon; + + // folders that contains plots for all tracks and associated tracks + static constexpr std::array TrackFolders = {"Trk/", "TrkColl/", "TrkAmb/"}; + + // Name shortenings + // passed* columns are in TrackSelectionExtension; isGlobalTrack* and trackCutFlag in TrackSelection + using TracksExtraSels = soa::Join; + using TracksExtraWPidPi = soa::Join; + using TracksExtraWPidMu = soa::Join; + using TracksExtraWPidPiMc = soa::Join; + using TracksExtraWPidMuMc = soa::Join; + using CollisionsWithMc = soa::Join; + using TracksExtraIU = soa::Join; + + void init(InitContext&) + { + // collision level + registry.add("Coll/hNContrib", ";N_{PV contributors};entries", {HistType::kTH1F, {{10, -0.5, 9.5}}}); + registry.add("Coll/hBCid", ";BCid;entries", {HistType::kTH1F, {{1000, 0., 100000.}}}); + registry.add("Coll/hVtxZ", ";#it{z}_{vtx} (cm);entries", hVtxZ); + registry.add("Coll/hnTF", ";TF;entries", {HistType::kTH1F, {{2000, 0., 2000}}}); + registry.add("Coll/hVtxX", ";#it{x}_{vtx} (cm);entries", hVtxX); + registry.add("Coll/hVtxY", ";#it{y}_{vtx} (cm);entries", hVtxY); + registry.add("Coll/hVtxChi2", ";#chi^{2} vtx;entries", {HistType::kTH1F, {{100, 0., 10.}}}); + + // track level (all good tracks, before grouping by collision) + registry.add("Trk/hPt", ";#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtTrk}}); + registry.add("Trk/hEta", ";#eta;entries", hEtaTrk); + registry.add("Trk/hChi2NCl", ";#chi^{2}/N_{cls} TPC;entries", {HistType::kTH1F, {{50, 0., 5.}}}); + registry.add("Trk/hTpcSignalVsP", ";#it{p} (GeV/#it{c});TPC d#it{E}/d#it{x}", {HistType::kTH2F, {{200, 0., 2.}, {300, 0., 300.}}}); + registry.add("Trk/hNSigmaVsP", ";#it{p} (GeV/#it{c});n#sigma^{TPC}", {HistType::kTH2F, {{200, 0., 2.}, {100, -10., 10.}}}); + registry.add("Trk/hHasIts", ";has ITS;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Trk/hIsPvContrib", ";is PV contributor;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Trk/hTpcNClsFound", ";N_{cls} TPC;entries", {HistType::kTH1F, {{90, 60, 150.}}}); + registry.add("Trk/hItsChi2NCl", ";#chi^{2}/N_{cls} ITS;entries", {HistType::kTH1F, {{100, 0., 10.}}}); + registry.add("Trk/hItsNCls", ";N_{cls} ITS;entries", {HistType::kTH1F, {{8, -0.5, 7.5}}}); + registry.add("Trk/hItsNClsInnerBarrel", ";N_{cls} ITS Inner Barrel;entries", {HistType::kTH1F, {{4, -0.5, 3.5}}}); + registry.add("Trk/hDcaXY", ";DCA_{xy} (cm);entries", {HistType::kTH1F, {{140, -0.035, 0.035}}}); + registry.add("Trk/hDcaZ", ";DCA_{z} (cm);entries", {HistType::kTH1F, {{200, -0.05, 0.05}}}); + + // track level (prongs of the candidate, after collision matching) + registry.addClone("Trk/", "TrkColl/"); + registry.add("TrkColl/hTrackSizePerEvent", ";#it{N}_{tracks} per event;entries", {HistType::kTH1F, {{10, -0.5, 9.5}}}); + + // track level plots for ambiguous tracks + registry.addClone("Trk/", "TrkAmb/"); + + // collision Id counter + registry.add("Coll/hCollIdCounter", ";collision ID;entries", {HistType::kTH1F, {{3, -1.5, 1.5}}}); + + // candidate level plots + registry.add("Cand/hMass", ";m_{#pi#pi} (GeV/#it{c}^{2});entries", {HistType::kTH1F, {axisMass}}); + registry.add("Cand/hPt", ";#it{p}_{T} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("Cand/hRapidity", ";#it{y};entries", {HistType::kTH1F, {axisY}}); + registry.add("Cand/hPtVsRapidity", ";#it{p}_{T} (GeV/#it{c});#it{y}", {HistType::kTH2F, {axisPt, axisY}}); + + // MC histograms are only booked if at least one MC process function is enabled + const bool needMc = doprocessRhoTracksBeforeGroupingMcInfo || doprocessJpsiTracksBeforeGroupingMcInfo || + doprocessRhoCandMcInfo || doprocessJpsiCandMcInfo || doprocessMcGenExclusive || doprocessMcGenEmbedded; + if (!needMc) { + return; + } + + // MC reco matched plots + registry.addClone("Cand/", "Matched/Cand/"); + + registry.addClone("Coll/hNContrib", "Matched/Coll/hNContrib"); + registry.addClone("Coll/hVtxZ", "Matched/Coll/hVtxZ"); + registry.addClone("Coll/hVtxX", "Matched/Coll/hVtxX"); + registry.addClone("Coll/hVtxY", "Matched/Coll/hVtxY"); + + registry.add("Matched/Trk/hPt", ";#it{p}_{T}^{MC matched} (GeV/#it{c});entries", {HistType::kTH1F, {axisPtTrk}}); + registry.add("Matched/Trk/hEta", ";#eta^{MC};entries", hEtaTrk); + registry.add("Matched/Trk/hMcIsPhysicalPrimary", ";is physical primary;entries", {HistType::kTH1F, {{2, -0.5, 1.5}}}); + registry.add("Matched/Trk/hMotherPdg", ";mother PDG code;entries", {HistType::kTH1F, {{1001, 0.5, 1000.5}}}); + + registry.addClone("Matched/Trk/", "Matched/TrkColl/"); + + // MC generated plots + registry.addClone("Matched/Coll/", "McGen/Coll/"); + + registry.addClone("Matched/Trk/", "McGen/Part/"); + + registry.add("McGen/MotherPart/hMotherPdg", ";mother PDG code;entries", {HistType::kTH1F, {{1001, 0.5, 1000.5}}}); + registry.add("McGen/MotherPart/hPt", ";#it{p}_{T}^{MC} (GeV/#it{c});entries", {HistType::kTH1F, {axisPt}}); + registry.add("McGen/MotherPart/hRapidity", ";#it{y}^{MC};entries", {HistType::kTH1F, {axisY}}); + registry.add("McGen/MotherPart/hPtVsRapidity", ";#it{p}_{T}^{MC} (GeV/#it{c});#it{y}^{MC}", {HistType::kTH2F, {axisPt, axisY}}); + + registry.addClone("TrkColl/hTrackSizePerEvent", "McGen/Part/hTrackSizePerEvent"); + + } // end init() + + // FUNCTIONS: all functions defined here + + //-------------------------------------------------------------------------------- + // get info on the run, given the run number. Fills info needed to use getTimeFrame() + void getRunInfo(int run) + { + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + sor = runInfo.sor; // in ms + auto orbitSOR = runInfo.orbitSOR; + auto orbitEOR = runInfo.orbitEOR; + orbitsPerTF = runInfo.orbitsPerTF; + bcSOR = orbitSOR * o2::constants::lhc::LHCMaxBunches; // first bc of the first orbit + nBCsPerTF = orbitsPerTF * o2::constants::lhc::LHCMaxBunches; // duration of TF in bcs + nTF = std::ceil((orbitEOR - orbitSOR) / orbitsPerTF); + } + + //-------------------------------------------------------------------------------- + // compute and return the TF for the given BC + int64_t getTimeFrame(int64_t globalBC) + { + return (globalBC - bcSOR) / nBCsPerTF; + } + + //-------------------------------------------------------------------------------- + // Basic single-track selection + template + bool isGoodTrack(TTrack const& track) + { + if (!track.hasTPC()) { + return false; + } + if (track.tpcNClsFound() < nMinTpcClusters) { + return false; + } + if (track.pt() < ptTrackMin || std::abs(track.eta()) > etaTrackMax) { + return false; + } + if (std::abs(track.dcaZ()) >= UnassignedDCA || std::abs(track.dcaXY()) >= UnassignedDCA) { + return false; + } + return true; + } + + //-------------------------------------------------------------------------------- + // Select the PID column at compile time + template + float getNSigma(TTrack const& track) + { + if constexpr (species == CandSpecies::kRho) { + return track.tpcNSigmaPi(); + } else { + return track.tpcNSigmaMu(); + } + } + + //-------------------------------------------------------------------------------- + // fills the histograms with info on track properties + // works for both all and associated tracks, and if MC is available or not + template + void fillTrackHistograms(TTrack const& track) + { + // fill the histos only for good tracks + if (!isGoodTrack(track)) { + return; + } + + registry.fill(HIST(TrackFolders[folder]) + HIST("hPt"), track.pt()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hEta"), track.eta()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hChi2NCl"), track.tpcChi2NCl()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hTpcSignalVsP"), track.p(), track.tpcSignal()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hNSigmaVsP"), track.p(), getNSigma(track)); + registry.fill(HIST(TrackFolders[folder]) + HIST("hHasIts"), static_cast(track.hasITS())); + registry.fill(HIST(TrackFolders[folder]) + HIST("hIsPvContrib"), static_cast(track.isPVContributor())); + registry.fill(HIST(TrackFolders[folder]) + HIST("hTpcNClsFound"), track.tpcNClsFound()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsChi2NCl"), track.itsChi2NCl()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsNCls"), track.itsNCls()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hItsNClsInnerBarrel"), track.itsNClsInnerBarrel()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hDcaXY"), track.dcaXY()); + registry.fill(HIST(TrackFolders[folder]) + HIST("hDcaZ"), track.dcaZ()); + + // if the MC info are available, fill the corresponding histograms + if constexpr (isMc) { + if (!track.has_mcParticle()) { + return; + } + auto mcPart = track.template mcParticle_as(); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hPt"), mcPart.pt()); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hEta"), mcPart.eta()); + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hMcIsPhysicalPrimary"), static_cast(mcPart.isPhysicalPrimary())); + if (mcPart.has_mothers()) { + for (auto const& mcMother : mcPart.template mothers_as()) { + registry.fill(HIST("Matched/") + HIST(TrackFolders[folder]) + HIST("hMotherPdg"), std::abs(mcMother.pdgCode())); + } + } + } + } + + //-------------------------------------------------------------------------------- + // loop over tracks and fill the histograms with info on track properties + template + void loopOverTracksBeforeGrouping(TTrack const& tracks) + { + for (auto const& track : tracks) { + int32_t collId = track.collisionId(); + fillTrackHistograms(track); + if (collId < 0) { + fillTrackHistograms(track); // isMc always false here since it fills Matched/.. histos + } + } + } + + //-------------------------------------------------------------------------------- + // fills the histograms with info on candidate properties + template + void fillCandidateHistograms(TCollision const& collision, TTrack const& tracks, float candMass) + { + registry.fill(HIST("Coll/hNContrib"), collision.numContrib()); + registry.fill(HIST("Coll/hBCid"), collision.bcId()); + registry.fill(HIST("Coll/hVtxZ"), collision.posZ()); + registry.fill(HIST("Coll/hVtxX"), collision.posX()); + registry.fill(HIST("Coll/hVtxY"), collision.posY()); + registry.fill(HIST("Coll/hVtxChi2"), collision.chi2()); + if constexpr (isMc) { + if (collision.has_mcCollision()) { + auto mcColl = collision.template mcCollision_as(); + registry.fill(HIST("Matched/Coll/hNContrib"), collision.numContrib()); + registry.fill(HIST("Matched/Coll/hVtxX"), mcColl.posX()); + registry.fill(HIST("Matched/Coll/hVtxY"), mcColl.posY()); + registry.fill(HIST("Matched/Coll/hVtxZ"), mcColl.posZ()); + } + } + + // Select tracks for the candidate + std::vector goodTracks; + for (auto const& track : tracks) { + if (isGoodTrack(track)) { + goodTracks.push_back(track); + // use TrkColl directory + fillTrackHistograms(track); + } + } + + registry.fill(HIST("TrkColl/hTrackSizePerEvent"), goodTracks.size()); + + // build UPC candidates using events with exactly two opposite sign good tracks + if (goodTracks.size() != TwoTracks) { + return; + } + auto const& track0 = goodTracks[0]; + auto const& track1 = goodTracks[1]; + if (track0.sign() * track1.sign() >= 0) { + return; + } + + ROOT::Math::PxPyPzMVector p0(track0.px(), track0.py(), track0.pz(), candMass); + ROOT::Math::PxPyPzMVector p1(track1.px(), track1.py(), track1.pz(), candMass); + auto candidate = p0 + p1; + + registry.fill(HIST("Cand/hMass"), candidate.M()); + registry.fill(HIST("Cand/hPt"), candidate.Pt()); + registry.fill(HIST("Cand/hRapidity"), candidate.Rapidity()); + registry.fill(HIST("Cand/hPtVsRapidity"), candidate.Pt(), candidate.Rapidity()); + + if constexpr (isMc) { + if (track0.has_mcParticle() && track1.has_mcParticle()) { + auto mcPart0 = track0.template mcParticle_as(); + auto mcPart1 = track1.template mcParticle_as(); + ROOT::Math::PxPyPzMVector mcP0(mcPart0.px(), mcPart0.py(), mcPart0.pz(), candMass); + ROOT::Math::PxPyPzMVector mcP1(mcPart1.px(), mcPart1.py(), mcPart1.pz(), candMass); + auto mcCandidate = mcP0 + mcP1; + registry.fill(HIST("Matched/Cand/hMass"), mcCandidate.M()); + registry.fill(HIST("Matched/Cand/hPt"), mcCandidate.Pt()); + registry.fill(HIST("Matched/Cand/hRapidity"), mcCandidate.Rapidity()); + registry.fill(HIST("Matched/Cand/hPtVsRapidity"), mcCandidate.Pt(), mcCandidate.Rapidity()); + } + } + } + + //-------------------------------------------------------------------------------- + // generated (MC truth) level: daughters and their mothers + template + void checkMcGen(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + + registry.fill(HIST("McGen/Coll/hVtxX"), mcCollision.posX()); + registry.fill(HIST("McGen/Coll/hVtxY"), mcCollision.posY()); + registry.fill(HIST("McGen/Coll/hVtxZ"), mcCollision.posZ()); + + std::vector goodMcGenParticles; + for (auto const& mcPart : mcParticles) { + + if (!mcPart.isPhysicalPrimary() || mcPart.pt() < ptTrackMin || std::abs(mcPart.eta()) > etaTrackMax) { + continue; + } + registry.fill(HIST("McGen/Part/hPt"), mcPart.pt()); + registry.fill(HIST("McGen/Part/hEta"), mcPart.eta()); + + goodMcGenParticles.push_back(mcPart); + } + registry.fill(HIST("McGen/Part/hTrackSizePerEvent"), goodMcGenParticles.size()); + + // consider only j/psi->mumu and rho->pipi events + auto sourceId = mcCollision.getSourceId(); + + if constexpr (isEmbedded) { + if (!sourceId) { + return; + } + } else { + if (sourceId) { + LOGF(fatal, "Source Id: {%d} is not zero for non-embedded MC", sourceId); + return; + } + } + + if (goodMcGenParticles.size() != TwoTracks) { + return; + } + + // we have 2 good tracks, with the same mother, taking it from the first one + auto mothers = goodMcGenParticles[0].template mothers_as(); + + if (mothers.empty()) { + LOGF(fatal, "Selected particle has no mother: pdgCode=%d", goodMcGenParticles[0].pdgCode()); + return; + } + auto const& motherPart = mothers[0]; + + registry.fill(HIST("McGen/MotherPart/hMotherPdg"), std::abs(motherPart.pdgCode())); + registry.fill(HIST("McGen/MotherPart/hPt"), motherPart.pt()); + registry.fill(HIST("McGen/MotherPart/hRapidity"), motherPart.y()); + registry.fill(HIST("McGen/MotherPart/hPtVsRapidity"), motherPart.pt(), motherPart.y()); + } + + //-------------------------------------------------------------------------------- + // PROCESS FUNCTIONS + //-------------------------------------------------------------------------------- + + //-------------------------------------------------------------------------------- + // check collisionId and TF + void processTracksIU(TracksExtraIU const& tracks, aod::Collisions const&, aod::BCs const&) + { + // track loop + for (const auto& track : tracks) { + + if (!isGoodTrack(track)) { + continue; + } + + int32_t collId = track.collisionId(); + + if (collId < 0) { + registry.fill(HIST("Coll/hCollIdCounter"), -1); + } else if (collId > 0) { + registry.fill(HIST("Coll/hCollIdCounter"), 1); + } else { + registry.fill(HIST("Coll/hCollIdCounter"), 0); + } + + if (collId < 0) { + continue; + } + + auto collision = track.collision(); + auto bc = collision.bc(); + + getRunInfo(bc.runNumber()); + + registry.fill(HIST("Coll/hnTF"), getTimeFrame(bc.globalBC())); + } + } + + // look at all tracks + void processRhoTracksBeforeGrouping(TracksExtraWPidPi const& tracks) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoTracksBeforeGrouping, "Process rho tracks before asking for collisions", true); + + void processJpsiTracksBeforeGrouping(TracksExtraWPidMu const& tracks) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiTracksBeforeGrouping, "Process J/Psi tracks before asking for collisions", false); + + void processRhoTracksBeforeGroupingMcInfo(TracksExtraWPidPiMc const& tracks, aod::McParticles const&) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoTracksBeforeGroupingMcInfo, "Process rho tracks before asking for collisions", false); + + void processJpsiTracksBeforeGroupingMcInfo(TracksExtraWPidMuMc const& tracks, aod::McParticles const&) + { + loopOverTracksBeforeGrouping(tracks); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiTracksBeforeGroupingMcInfo, "Process J/Psi tracks before asking for collisions", false); + + // look at matched tracks + void processRhoCand(aod::Collision const& collision, TracksExtraWPidPi const& tracks) + { + fillCandidateHistograms(collision, tracks, MassPion); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoCand, "Rho -> pi pi candidates and track QA", true); + + void processJpsiCand(aod::Collision const& collision, TracksExtraWPidMu const& tracks) + { + fillCandidateHistograms(collision, tracks, MassMuon); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiCand, "J/Psi -> mu mu candidates and track QA", false); + + void processRhoCandMcInfo(CollisionsWithMc::iterator const& collision, TracksExtraWPidPiMc const& tracks, + aod::McParticles const&, aod::McCollisions const&) + { + fillCandidateHistograms(collision, tracks, MassPion); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processRhoCandMcInfo, "Rho -> pi pi candidates and track QA", false); + + void processJpsiCandMcInfo(CollisionsWithMc::iterator const& collision, TracksExtraWPidMuMc const& tracks, + aod::McParticles const&, aod::McCollisions const&) + { + fillCandidateHistograms(collision, tracks, MassMuon); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processJpsiCandMcInfo, "J/Psi -> mu mu candidates and track QA", false); + + // look at gen MC + void processMcGenEmbedded(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + checkMcGen(mcCollision, mcParticles); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processMcGenEmbedded, "Generated embedded events", false); + + void processMcGenExclusive(aod::McCollision const& mcCollision, aod::McParticles const& mcParticles) + { + checkMcGen(mcCollision, mcParticles); + } + PROCESS_SWITCH(UpcTrackVertexingQA, processMcGenExclusive, "Generated exclusive events", false); + + PROCESS_SWITCH(UpcTrackVertexingQA, processTracksIU, "Track QA for IU tracks", false); +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/PWGUD/Tasks/upcVmRof.cxx b/PWGUD/Tasks/upcVmRof.cxx new file mode 100644 index 00000000000..715c573afb6 --- /dev/null +++ b/PWGUD/Tasks/upcVmRof.cxx @@ -0,0 +1,1377 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +// +/// \file upcVmRof.cxx +/// \brief analysis of UPC vector meson production ROF by ROF +/// +/// \author Guillermo Contreras (jesus.guillermo.contreras.nuno@cern.ch), Czech Technical University in Prague +/// \author Cesar Omar Ramirez Alvarez (cesar.ramirez@cern.ch), Autonomous University of Puebla + +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +using BCsTSsSels = soa::Join; +using ColSels = soa::Join; +using ColSelsMc = soa::Join; +using TRKs = soa::Join; +using TRKsMc = soa::Join; + +using ColSel = ColSels::iterator; +using ColSelMc = ColSelsMc::iterator; + +namespace o2::aod +{ +namespace datarows +{ +// collision info +DECLARE_SOA_COLUMN(RunNumber, runNumber, int32_t); +DECLARE_SOA_COLUMN(PosX, posX, float); +DECLARE_SOA_COLUMN(PosY, posY, float); +DECLARE_SOA_COLUMN(PosZ, posZ, float); +DECLARE_SOA_COLUMN(Chi2, chi2, float); +DECLARE_SOA_COLUMN(LocalBC, localBC, int); +DECLARE_SOA_COLUMN(NearestBCB, nearestBCB, int); +DECLARE_SOA_COLUMN(LocalTF, localTF, int); +DECLARE_SOA_COLUMN(LocalROF, localROF, int); +DECLARE_SOA_COLUMN(UpcFlag, upcFlag, int); + +// FIT info +DECLARE_SOA_COLUMN(AmplitudeFT0A, amplitudeFT0A, float); +DECLARE_SOA_COLUMN(AmplitudeFT0C, amplitudeFT0C, float); +DECLARE_SOA_COLUMN(AmplitudeFV0A, amplitudeFV0A, float); +DECLARE_SOA_COLUMN(AmplitudeFDDA, amplitudeFDDA, float); +DECLARE_SOA_COLUMN(AmplitudeFDDC, amplitudeFDDC, float); +DECLARE_SOA_COLUMN(TimeFT0A, timeFT0A, float); +DECLARE_SOA_COLUMN(TimeFT0C, timeFT0C, float); +DECLARE_SOA_COLUMN(TimeFV0A, timeFV0A, float); +DECLARE_SOA_COLUMN(TimeFDDA, timeFDDA, float); +DECLARE_SOA_COLUMN(TimeFDDC, timeFDDC, float); +DECLARE_SOA_COLUMN(ChannelsFT0A, channelsFT0A, int); +DECLARE_SOA_COLUMN(ChannelsFT0C, channelsFT0C, int); +DECLARE_SOA_COLUMN(ChannelsFV0A, channelsFV0A, int); +DECLARE_SOA_COLUMN(ChannelsFDDA, channelsFDDA, int); +DECLARE_SOA_COLUMN(ChannelsFDDC, channelsFDDC, int); + +// ZDC info +DECLARE_SOA_COLUMN(EnergyCommonZNA, energyCommonZNA, float); +DECLARE_SOA_COLUMN(EnergyCommonZNC, energyCommonZNC, float); +DECLARE_SOA_COLUMN(TimeZNA, timeZNA, float); +DECLARE_SOA_COLUMN(TimeZNC, timeZNC, float); + +// track info +DECLARE_SOA_COLUMN(Pt1, pt1, float); +DECLARE_SOA_COLUMN(Eta1, eta1, float); +DECLARE_SOA_COLUMN(Phi1, phi1, float); +DECLARE_SOA_COLUMN(Q1, q1, int); +DECLARE_SOA_COLUMN(PidPion1, pidPion1, float); +DECLARE_SOA_COLUMN(PidElectron1, pidElectron1, float); +DECLARE_SOA_COLUMN(PidKaon1, pidKaon1, float); +DECLARE_SOA_COLUMN(PidProton1, pidProton1, float); +DECLARE_SOA_COLUMN(Pt2, pt2, float); +DECLARE_SOA_COLUMN(Eta2, eta2, float); +DECLARE_SOA_COLUMN(Phi2, phi2, float); +DECLARE_SOA_COLUMN(Q2, q2, int); +DECLARE_SOA_COLUMN(PidPion2, pidPion2, float); +DECLARE_SOA_COLUMN(PidElectron2, pidElectron2, float); +DECLARE_SOA_COLUMN(PidKaon2, pidKaon2, float); +DECLARE_SOA_COLUMN(PidProton2, pidProton2, float); +DECLARE_SOA_COLUMN(Pt3, pt3, float); +DECLARE_SOA_COLUMN(Eta3, eta3, float); +DECLARE_SOA_COLUMN(Phi3, phi3, float); +DECLARE_SOA_COLUMN(Q3, q3, int); +DECLARE_SOA_COLUMN(PidPion3, pidPion3, float); +DECLARE_SOA_COLUMN(PidElectron3, pidElectron3, float); +DECLARE_SOA_COLUMN(PidKaon3, pidKaon3, float); +DECLARE_SOA_COLUMN(PidProton3, pidProton3, float); +DECLARE_SOA_COLUMN(Pt4, pt4, float); +DECLARE_SOA_COLUMN(Eta4, eta4, float); +DECLARE_SOA_COLUMN(Phi4, phi4, float); +DECLARE_SOA_COLUMN(Q4, q4, int); +DECLARE_SOA_COLUMN(PidPion4, pidPion4, float); +DECLARE_SOA_COLUMN(PidElectron4, pidElectron4, float); +DECLARE_SOA_COLUMN(PidKaon4, pidKaon4, float); +DECLARE_SOA_COLUMN(PidProton4, pidProton4, float); +DECLARE_SOA_COLUMN(HasTof1, hasTof1, int); +DECLARE_SOA_COLUMN(HasTof2, hasTof2, int); +DECLARE_SOA_COLUMN(HasTof3, hasTof3, int); +DECLARE_SOA_COLUMN(HasTof4, hasTof4, int); +} // namespace datarows + +namespace mcgen +{ +DECLARE_SOA_COLUMN(McMotherPdg, mcMotherPdg, int); +DECLARE_SOA_COLUMN(McMotherPt, mcMotherPt, float); +DECLARE_SOA_COLUMN(McMotherPhi, mcMotherPhi, float); +DECLARE_SOA_COLUMN(McMotherMass, mcMotherMass, float); +DECLARE_SOA_COLUMN(McMotherRapidity, mcMotherRapidity, float); + +DECLARE_SOA_COLUMN(McPdg1, mcPdg1, int); +DECLARE_SOA_COLUMN(McPt1, mcPt1, float); +DECLARE_SOA_COLUMN(McEta1, mcEta1, float); +DECLARE_SOA_COLUMN(McPhi1, mcPhi1, float); +DECLARE_SOA_COLUMN(McQ1, mcQ1, int); +DECLARE_SOA_COLUMN(IsPrimary1, isPrimary1, int); +DECLARE_SOA_COLUMN(McPdg2, mcPdg2, int); +DECLARE_SOA_COLUMN(McPt2, mcPt2, float); +DECLARE_SOA_COLUMN(McEta2, mcEta2, float); +DECLARE_SOA_COLUMN(McPhi2, mcPhi2, float); +DECLARE_SOA_COLUMN(McQ2, mcQ2, int); +DECLARE_SOA_COLUMN(IsPrimary2, isPrimary2, int); +DECLARE_SOA_COLUMN(McPdg3, mcPdg3, int); +DECLARE_SOA_COLUMN(McPt3, mcPt3, float); +DECLARE_SOA_COLUMN(McEta3, mcEta3, float); +DECLARE_SOA_COLUMN(McPhi3, mcPhi3, float); +DECLARE_SOA_COLUMN(McQ3, mcQ3, int); +DECLARE_SOA_COLUMN(IsPrimary3, isPrimary3, int); +DECLARE_SOA_COLUMN(McPdg4, mcPdg4, int); +DECLARE_SOA_COLUMN(McPt4, mcPt4, float); +DECLARE_SOA_COLUMN(McEta4, mcEta4, float); +DECLARE_SOA_COLUMN(McPhi4, mcPhi4, float); +DECLARE_SOA_COLUMN(McQ4, mcQ4, int); +DECLARE_SOA_COLUMN(IsPrimary4, isPrimary4, int); + +DECLARE_SOA_COLUMN(McPosX, mcPosX, float); +DECLARE_SOA_COLUMN(McPosY, mcPosY, float); +DECLARE_SOA_COLUMN(McPosZ, mcPosZ, float); +DECLARE_SOA_COLUMN(McLocalBC, mcLocalBC, int); +DECLARE_SOA_COLUMN(RecoIndex, recoIndex, int); +} // namespace mcgen + +DECLARE_SOA_TABLE(TwoTrkTable, "AOD", "TWOTRKTABLE", + datarows::RunNumber, datarows::PosX, datarows::PosY, datarows::PosZ, datarows::Chi2, + datarows::LocalBC, datarows::NearestBCB, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, + datarows::AmplitudeFT0A, datarows::AmplitudeFT0C, datarows::AmplitudeFV0A, datarows::AmplitudeFDDA, datarows::AmplitudeFDDC, + datarows::TimeFT0A, datarows::TimeFT0C, datarows::TimeFV0A, datarows::TimeFDDA, datarows::TimeFDDC, + datarows::ChannelsFT0A, datarows::ChannelsFT0C, datarows::ChannelsFV0A, datarows::ChannelsFDDA, datarows::ChannelsFDDC, + datarows::EnergyCommonZNA, datarows::EnergyCommonZNC, datarows::TimeZNA, datarows::TimeZNC, + datarows::Pt1, datarows::Eta1, datarows::Phi1, datarows::Q1, datarows::PidPion1, datarows::PidElectron1, datarows::PidKaon1, datarows::PidProton1, + datarows::Pt2, datarows::Eta2, datarows::Phi2, datarows::Q2, datarows::PidPion2, datarows::PidElectron2, datarows::PidKaon2, datarows::PidProton2, + datarows::HasTof1, datarows::HasTof2); +DECLARE_SOA_TABLE(FourTrkTable, "AOD", "FOURTRKTABLE", + datarows::RunNumber, datarows::PosX, datarows::PosY, datarows::PosZ, datarows::Chi2, + datarows::LocalBC, datarows::NearestBCB, datarows::LocalTF, datarows::LocalROF, datarows::UpcFlag, + datarows::AmplitudeFT0A, datarows::AmplitudeFT0C, datarows::AmplitudeFV0A, datarows::AmplitudeFDDA, datarows::AmplitudeFDDC, + datarows::TimeFT0A, datarows::TimeFT0C, datarows::TimeFV0A, datarows::TimeFDDA, datarows::TimeFDDC, + datarows::ChannelsFT0A, datarows::ChannelsFT0C, datarows::ChannelsFV0A, datarows::ChannelsFDDA, datarows::ChannelsFDDC, + datarows::EnergyCommonZNA, datarows::EnergyCommonZNC, datarows::TimeZNA, datarows::TimeZNC, + datarows::Pt1, datarows::Eta1, datarows::Phi1, datarows::Q1, datarows::PidPion1, datarows::PidElectron1, datarows::PidKaon1, datarows::PidProton1, + datarows::Pt2, datarows::Eta2, datarows::Phi2, datarows::Q2, datarows::PidPion2, datarows::PidElectron2, datarows::PidKaon2, datarows::PidProton2, + datarows::Pt3, datarows::Eta3, datarows::Phi3, datarows::Q3, datarows::PidPion3, datarows::PidElectron3, datarows::PidKaon3, datarows::PidProton3, + datarows::Pt4, datarows::Eta4, datarows::Phi4, datarows::Q4, datarows::PidPion4, datarows::PidElectron4, datarows::PidKaon4, datarows::PidProton4, + datarows::HasTof1, datarows::HasTof2, datarows::HasTof3, datarows::HasTof4); + +DECLARE_SOA_TABLE(TwoTrkRecGenTable, "AOD", "TWOTRKRECGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC, mcgen::RecoIndex); +DECLARE_SOA_TABLE(FourTrkRecGenTable, "AOD", "FOURTRKRECGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPdg3, mcgen::McPt3, mcgen::McEta3, mcgen::McPhi3, mcgen::McQ3, mcgen::IsPrimary3, + mcgen::McPdg4, mcgen::McPt4, mcgen::McEta4, mcgen::McPhi4, mcgen::McQ4, mcgen::IsPrimary4, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC, mcgen::RecoIndex); + +DECLARE_SOA_TABLE(TwoTrkGenTable, "AOD", "TWOTRKGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC); +DECLARE_SOA_TABLE(FourTrkGenTable, "AOD", "FOURTRKGEN", + mcgen::McMotherPdg, mcgen::McMotherPt, mcgen::McMotherPhi, mcgen::McMotherMass, mcgen::McMotherRapidity, + mcgen::McPdg1, mcgen::McPt1, mcgen::McEta1, mcgen::McPhi1, mcgen::McQ1, mcgen::IsPrimary1, + mcgen::McPdg2, mcgen::McPt2, mcgen::McEta2, mcgen::McPhi2, mcgen::McQ2, mcgen::IsPrimary2, + mcgen::McPdg3, mcgen::McPt3, mcgen::McEta3, mcgen::McPhi3, mcgen::McQ3, mcgen::IsPrimary3, + mcgen::McPdg4, mcgen::McPt4, mcgen::McEta4, mcgen::McPhi4, mcgen::McQ4, mcgen::IsPrimary4, + mcgen::McPosX, mcgen::McPosY, mcgen::McPosZ, mcgen::McLocalBC); +} // namespace o2::aod + +struct UpcVmRof { + + // output + Produces twoTrkTable; + Produces fourTrkTable; + Produces twoTrkRecGenTable; + Produces fourTrkRecGenTable; + Produces twoTrkGenTable; + Produces fourTrkGenTable; + + // services + Service ccdb{}; // access to database + + // histograms + HistogramRegistry bcTH1Registry{"bcTH1Registry", {}}; + std::map> bcTH1Pointers; + HistogramRegistry bcTH2Registry{"bcTH2Registry", {}}; + std::map> bcTH2Pointers; + HistogramRegistry colTH1Registry{"colTH1Registry", {}}; + std::map> colTH1Pointers; + + // variables to store filling scheme info + std::bitset beamPatternA; + std::bitset beamPatternC; + std::bitset bcPatternA; + std::bitset bcPatternB; + std::bitset bcPatternC; + std::vector bcbIdx; + std::vector vecNearestBCB; + std::vector vecDistNearestBCB; + + int nbcB = 0; + + // variables to store ITS ROF info + int rofPerOrbit = -1; // number of rofs per orbit + int rofLength = -1; // number of bcs per ROF + int rofShift = -1; // bc shift of ITS. + + // variables to store run info + int runNumberBc = 0; // run number used to process BCs + int runNumberCol = 0; // run number used to process collisions + int64_t sor = 0; // best known timestamp for the start of run + int64_t orbitsPerTF = 0; // number of orbits per TF + int64_t bcSOR = 0; // first bc of the first orbit + int64_t nBCsPerTF = 0; // duration of TF in bcs + int64_t currentTF = -1; // current time frame being looked at + int64_t nTF = 0; // number of time frames in run + + // constant related to trigger mask indices + // https://github.com/AliceO2Group/AliceO2/blob/6c0251c35e5cbf6028d2bd6f7e30f9a4fd348e38/DataFormats/Detectors/CTP/src/Configuration.cxx#L1123 + // PbPb triggers: 1ZNC, FV0CH+FT0VTX, OO: 1ZNC, FT0CE+FT0VTX + static constexpr int Ft0VtxIdx = 2; + static constexpr int Ft0CeIdx = 4; + static constexpr int Ft0AIdx = 0; + static constexpr int Ft0CIdx = 1; + + // number of tracks for the selected event topologies + static constexpr int NTrksTwoBody = 2; + static constexpr int NTrksFourBody = 4; + + // reconstruction modes + static constexpr int stdReco = 0; + static constexpr int upcReco = 1; + + // information for selection collisions + Configurable maxAbsPosZ{"maxAbsPosZ", 10.0, "max |Z| position of vtx"}; + Configurable maxAbsTimeFT0{"maxAbsTimeFT0", 4.0, "max |time| in a FT0 side"}; + Configurable maxAmpFT0{"maxAmpFT0", 150.0, "max amplitude for signals in a FT0 side"}; + Configurable maxTrkTpcChi2{"maxTrkTpcChi2", 4.0, "max chi2 of TPC track"}; + Configurable maxTrkItsChi2{"maxTrkItsChi2", 36.0, "max chi2 of ITS track"}; + Configurable minTrkTpcClusters{"minTrkTpcClusters", 70.0, "minimum number of TPC clusters associated to the track"}; + Configurable maxTrkDcaZ{"maxTrkDcaZ", 2.0, "max DCA in z of track to vtx (cm)"}; + Configurable tfPerBin{"tfPerBin", 10000, "timeframes per bin 1e4 means some 28 s"}; + Configurable genId{"genId", -1, "generator ID; -1 = no filter"}; + Configurable requireVtxItsTpc{"requireVtxItsTpc", false, "require kIsVertexITSTPC"}; + Configurable requireNoSameBunchPileup{"requireNoSameBunchPileup", false, "require kNoSameBunchPileup"}; + + //-------------------------------------------------------------------------------- + // get ITS ROF info + // code from https://github.com/AliceO2Group/O2Physics/blob/master/Common/Tools/EventSelectionModule.h#L779-L780 + void getRofInfo() + { + auto alppar = ccdb->getForTimeStamp>("ITS/Config/AlpideParam", sor); + rofShift = alppar->roFrameBiasInBC; + rofLength = alppar->roFrameLengthInBC; + rofPerOrbit = (o2::constants::lhc::LHCMaxBunches / rofLength); + } + + //-------------------------------------------------------------------------------- + // find nearest bcb for this filling scheme + void setNearestBCB() + { + vecNearestBCB.clear(); + vecDistNearestBCB.clear(); + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + // extend vector + vecNearestBCB.push_back(-1); + vecDistNearestBCB.push_back(-1); + // are we in a bc-b? + if (bcPatternB.test(i)) { + vecNearestBCB[i] = i; + vecDistNearestBCB[i] = 0; + continue; + } + // find nearest previous bc-b + int nearestLeft = -1; + for (int j = 1; j < o2::constants::lhc::LHCMaxBunches; j++) { + int k = i - j; + if (k < 0) + k = o2::constants::lhc::LHCMaxBunches + k; + if (bcPatternB.test(k)) { + nearestLeft = k; + break; + } + } // end nearestLeft loop + + // find nearest next bc-b + int nearesRight = -1; + for (int j = 1; j < o2::constants::lhc::LHCMaxBunches; j++) { + int k = i + j; + if (k > (o2::constants::lhc::LHCMaxBunches - 1)) + k = k - o2::constants::lhc::LHCMaxBunches; + if (bcPatternB.test(k)) { + nearesRight = k; + break; + } + } // end nearesRight loop + + // find nearest bc-b + int dLeft = i - nearestLeft; + // cppcheck-suppress knownConditionTrueFalse + if (dLeft < 0) + dLeft += o2::constants::lhc::LHCMaxBunches; + int dRight = nearesRight - i; + // cppcheck-suppress knownConditionTrueFalse + if (dRight < 0) + dRight += o2::constants::lhc::LHCMaxBunches; + int dMin = std::min(dLeft, dRight); + int nearest = ((dMin == dLeft) ? nearestLeft : nearesRight); + vecNearestBCB[i] = nearest; + int dist = ((dMin == dLeft) ? -dLeft : dRight); + vecDistNearestBCB[i] = dist; + } // end loop over bc + } + + //-------------------------------------------------------------------------------- + // get filling scheme + void getFillingScheme() + { + // get the info + auto grplhcif = ccdb->getForTimeStamp("GLO/Config/GRPLHCIF", sor); + beamPatternA = grplhcif->getBunchFilling().getBeamPattern(0); + beamPatternC = grplhcif->getBunchFilling().getBeamPattern(1); + bcPatternA = beamPatternA & ~beamPatternC; + bcPatternB = beamPatternA & beamPatternC; + bcPatternC = ~beamPatternA & beamPatternC; + // define internal indices to access the correct bin in histogram + bcbIdx.clear(); + nbcB = 0; + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + bcbIdx.push_back(-1); + if (bcPatternB.test(i)) { + bcbIdx[i] = nbcB; + nbcB++; + } + } + } // end getFillingScheme() + + //-------------------------------------------------------------------------------- + // store bc patterns + void fillBcPatternHistos(int run) + { + for (int i = 0; i < o2::constants::lhc::LHCMaxBunches; i++) { + if (bcPatternA.test(i)) { + bcTH1Pointers[Form("bc/%d/bcPatternA_H", run)]->Fill(i); + } + if (bcPatternB.test(i)) { + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)]->Fill(i); + } + if (bcPatternC.test(i)) { + bcTH1Pointers[Form("bc/%d/bcPatternC_H", run)]->Fill(i); + } + } + } // end fillBcPatternHistos + + //-------------------------------------------------------------------------------- + // get general run information + void getRunInfo(int run) + { + auto runInfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb->instance(), run); + sor = runInfo.sor; // in ms + auto orbitSOR = runInfo.orbitSOR; + auto orbitEOR = runInfo.orbitEOR; + orbitsPerTF = runInfo.orbitsPerTF; + bcSOR = orbitSOR * o2::constants::lhc::LHCMaxBunches; // first bc of the first orbit + nBCsPerTF = orbitsPerTF * o2::constants::lhc::LHCMaxBunches; // duration of TF in bcs + nTF = std::ceil((orbitEOR - orbitSOR) / orbitsPerTF); + } // end getRunInfo() + + //-------------------------------------------------------------------------------- + // compute Bc within the orbit + int64_t getBcWithinOrbit(int64_t globalBC) + { + return (globalBC % o2::constants::lhc::LHCMaxBunches); + } + + //-------------------------------------------------------------------------------- + // compute TF for this BC + int64_t getTimeFrame(int64_t globalBC) + { + return (globalBC - bcSOR) / nBCsPerTF; + } + + //-------------------------------------------------------------------------------- + // compute ROF for this BC + int getRof(int64_t thisBC) + { + int64_t bctmp = thisBC - rofShift; + if (bctmp < 0) { + bctmp = o2::constants::lhc::LHCMaxBunches - rofShift - 1; + } + return static_cast(bctmp / rofLength); + } + + //-------------------------------------------------------------------------------- + // check flags for a bc + bool checkBcFlags(const auto& bc, int run) + { + bcTH1Pointers[Form("bc/%d/bcSel_H", run)]->Fill(0); + if (!bc.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + bcTH1Pointers[Form("bc/%d/bcSel_H", run)]->Fill(1); + if (!bc.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + bcTH1Pointers[Form("bc/%d/bcSel_H", run)]->Fill(2); + return true; + } // end checkBcFlags + + //-------------------------------------------------------------------------------- + // check flags for a colllision + bool checkColFlags(const auto& col, int run) + { + colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(0); + if (!col.has_foundBC()) { + return false; + } + colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(1); + if (!col.selection_bit(aod::evsel::kNoTimeFrameBorder)) { + return false; + } + colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(2); + if (!col.selection_bit(aod::evsel::kNoITSROFrameBorder)) { + return false; + } + colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(3); + if (requireVtxItsTpc && !col.selection_bit(aod::evsel::kIsVertexITSTPC)) { + return false; + } + colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(4); + if (requireNoSameBunchPileup && !col.selection_bit(aod::evsel::kNoSameBunchPileup)) { + return false; + } + colTH1Pointers[Form("col/%d/colSel_H", run)]->Fill(5); + // missing: add rct flags + return true; + } // end checkColFlags + + //-------------------------------------------------------------------------------- + // add histos used in processBCs + void addBcHistos(int run) + { + // check if histos already created + if (bcTH1Pointers[Form("bc/%d/bcPatternA_H", run)]) { + return; + } + + // filling scheme + bcTH1Pointers[Form("bc/%d/bcPatternA_H", run)] = bcTH1Registry.add(Form("bc/%d/bcPatternA_H", run), "Pattern of bc-A; bcID;", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, static_cast(o2::constants::lhc::LHCMaxBunches) - 0.5}}}); + bcTH1Pointers[Form("bc/%d/bcPatternB_H", run)] = bcTH1Registry.add(Form("bc/%d/bcPatternB_H", run), "Pattern of bc-B; bcID;", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, static_cast(o2::constants::lhc::LHCMaxBunches) - 0.5}}}); + bcTH1Pointers[Form("bc/%d/bcPatternC_H", run)] = bcTH1Registry.add(Form("bc/%d/bcPatternC_H", run), "Pattern of bc-C; bcID;", + {HistType::kTH1D, {{o2::constants::lhc::LHCMaxBunches, -0.5, static_cast(o2::constants::lhc::LHCMaxBunches) - 0.5}}}); + + // bc sel and tf info + int nBinsTF = static_cast(nTF / tfPerBin) + 1; + int lastTFinHisto = (nBinsTF * tfPerBin) - 1; // first TF is zero + bcTH1Pointers[Form("bc/%d/bcSel_H", run)] = bcTH1Registry.add(Form("bc/%d/bcSel_H", run), "bc selection counter; selID; Counter", + {HistType::kTH1D, {{4, -0.5, 3.5}}}); + bcTH1Pointers[Form("bc/%d/tf_H", run)] = bcTH1Registry.add(Form("bc/%d/tf_H", run), "analysed time frames;TF;Counts", + {HistType::kTH1D, {{nBinsTF, -0.5, static_cast(lastTFinHisto) - 0.5}}}); + // trigger info per rof + bcTH2Pointers[Form("bc/%d/ft0Vtx_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0Vtx_H", run), "ft0Vtx triggers; TF; ROF; Counter", + {HistType::kTH2F, {{nBinsTF, -0.5, static_cast(lastTFinHisto) - 0.5}, {rofPerOrbit, -0.5, rofPerOrbit - 0.5}}}); + bcTH2Pointers[Form("bc/%d/ft0VtxCe_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0VtxCe_H", run), "ft0VtxCe triggers; TF; ROF; Counter", + {HistType::kTH2F, {{nBinsTF, -0.5, static_cast(lastTFinHisto) - 0.5}, {rofPerOrbit, -0.5, rofPerOrbit - 0.5}}}); + // trigger info per bcb + bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0Vtx_bcb_H", run), "ft0Vtx triggers; TF; bc-B idx; Counter", + {HistType::kTH2F, {{nBinsTF, -0.5, static_cast(lastTFinHisto) - 0.5}, {nbcB, -0.5, nbcB - 0.5}}}); + bcTH2Pointers[Form("bc/%d/ft0CnotA_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0CnotA_bcb_H", run), "ft0CnotA triggers; TF; bc-B idx; Counter", + {HistType::kTH2F, {{nBinsTF, -0.5, static_cast(lastTFinHisto) - 0.5}, {nbcB, -0.5, nbcB - 0.5}}}); + bcTH2Pointers[Form("bc/%d/ft0AnotC_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0AnotC_bcb_H", run), "ft0AnotC triggers; TF; bc-B idx; Counter", + {HistType::kTH2F, {{nBinsTF, -0.5, static_cast(lastTFinHisto) - 0.5}, {nbcB, -0.5, nbcB - 0.5}}}); + bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", run)] = bcTH2Registry.add(Form("bc/%d/ft0VtxCe_bcb_H", run), "ft0Vtx triggers; TF; bc-B idx; Counter", + {HistType::kTH2F, {{nBinsTF, -0.5, static_cast(lastTFinHisto) - 0.5}, {nbcB, -0.5, nbcB - 0.5}}}); + } // addBcHistos + + //-------------------------------------------------------------------------------- + // add histos used in processBCs + void addColHistos(int run) + { + // check if histos already created + if (colTH1Pointers[Form("col/%d/colSel_H", run)]) { + return; + } + + // counters + colTH1Pointers[Form("col/%d/colSel_H", run)] = colTH1Registry.add(Form("col/%d/colSel_H", run), + "collision selection counter; selID; Counter", + {HistType::kTH1D, {{25, -0.5, 24.5}}}); + colTH1Pointers[Form("col/%d/trkSel_H", run)] = colTH1Registry.add(Form("col/%d/trkSel_H", run), + "track selection counter; selID; Counter", + {HistType::kTH1D, {{10, -0.5, 9.5}}}); + // track properties + colTH1Pointers[Form("col/%d/tpcChi2_H", run)] = colTH1Registry.add(Form("col/%d/tpcChi2_H", run), + "track #chi^2 in TPC; #chi^2; Entries", + {HistType::kTH1D, {{100, 0, 5}}}); + colTH1Pointers[Form("col/%d/itsChi2_H", run)] = colTH1Registry.add(Form("col/%d/itsChi2_H", run), + "track #chi^2 in ITS; #chi^2; Entries", + {HistType::kTH1D, {{120, 0, 40}}}); + colTH1Pointers[Form("col/%d/tpcNcls_H", run)] = colTH1Registry.add(Form("col/%d/tpcNcls_H", run), + "track clusters in TPC; n-clusters; Entries", + {HistType::kTH1D, {{160, 0, 160}}}); + colTH1Pointers[Form("col/%d/tpcXoF_H", run)] = colTH1Registry.add(Form("col/%d/tpcXoF_H", run), + "track crossed rows over findable TPC clusters; XoF; Entries", + {HistType::kTH1D, {{150, 0.5, 2.0}}}); + colTH1Pointers[Form("col/%d/trkDcaZ_H", run)] = colTH1Registry.add(Form("col/%d/trkDcaZ_H", run), + "track z-DCA; z-dca (cm); Entries", + {HistType::kTH1D, {{100, -0.5, 0.5}}}); + colTH1Pointers[Form("col/%d/trkDcaXY_H", run)] = colTH1Registry.add(Form("col/%d/trkDcaXY_H", run), + "track xy-DCA; xy-dca (cm); Entries", + {HistType::kTH1D, {{100, -0.1, 0.1}}}); + // selected events per TF + colTH1Pointers[Form("col/%d/twoTrkTF_H", run)] = colTH1Registry.add(Form("col/%d/twoTrkTF_H", run), "Number of selected 2-trk events per;TF;Entries", + {HistType::kTH1D, {{static_cast(nTF / tfPerBin), -0.5, static_cast(nTF) - 0.5}}}); + colTH1Pointers[Form("col/%d/fourTrkTF_H", run)] = colTH1Registry.add(Form("col/%d/fourTrkTF_H", run), "Number of selected 4-trk events per;TF;Entries", + {HistType::kTH1D, {{static_cast(nTF / tfPerBin), -0.5, static_cast(nTF) - 0.5}}}); + + } // addColHistos + + //-------------------------------------------------------------------------------- + // initialization + void init(InitContext&) + { + // set access to db + ccdb->setURL("http://alice-ccdb.cern.ch"); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + ccdb->setFatalWhenNull(false); + } // end init() + + //-------------------------------------------------------------------------------- + // helper function for MC: pt + static float mcPt(float px, float py) { return std::sqrt(px * px + py * py); } + + //-------------------------------------------------------------------------------- + // helper function for MC: pseudorapidity + static float mcEta(float px, float py, float pz) + { + float p = std::sqrt(px * px + py * py + pz * pz); + if (std::abs(p - std::abs(pz)) > 1e-10) { + return 0.5f * std::log((p + pz) / (p - pz)); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // helper function for MC: azimuth + static float mcPhi(float px, float py) + { + if (std::abs(px) > 1e-10 || std::abs(py) > 1e-10) { + return std::atan2(py, px); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // helper function for MC: rapidity + static float mcRapidity(float px, float py, float pz, float mass) + { + float energy = std::sqrt(px * px + py * py + pz * pz + mass * mass); + if (std::abs(energy - std::abs(pz)) > 1e-10) { + return 0.5f * std::log((energy + pz) / (energy - pz)); + } + return 0.0f; + } + + //-------------------------------------------------------------------------------- + // mother search + struct MotherInfo { + bool found = false; + int pdg = 0; + float px = 0, py = 0, pz = 0, mass = 0; + }; + + //-------------------------------------------------------------------------------- + // checks if all given daughters share the same direct mother: found=false if not + template + MotherInfo findCommonMother(aod::McParticles const& mcParticles, const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + int64_t motherIdx = daughters[0].mothersIds()[0]; + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers() || daughters[i].mothersIds()[0] != motherIdx) { + return info; + } + } + auto mother = mcParticles.rawIteratorAt(motherIdx); + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + info.pz = mother.pz(); + info.mass = std::sqrt(mother.e() * mother.e() - mother.px() * mother.px() - mother.py() * mother.py() - mother.pz() * mother.pz()); + return info; + } + + //-------------------------------------------------------------------------------- + // for processMcGenAll: same mother search. + template + MotherInfo findCommonMotherFromIters(/* aod::McParticles const& mcParticles, */ const std::vector& daughters) + { + MotherInfo info; + if (daughters.empty() || !daughters[0].has_mothers()) { + return info; + } + auto firstMothers = daughters[0].template mothers_as(); + if (firstMothers.begin() == firstMothers.end()) { + return info; + } + auto motherIt = firstMothers.begin(); + int64_t motherGlobalIndex = motherIt->globalIndex(); + for (size_t i = 1; i < daughters.size(); i++) { + if (!daughters[i].has_mothers()) { + return info; + } + auto iMothers = daughters[i].template mothers_as(); + if (iMothers.begin() == iMothers.end() || iMothers.begin()->globalIndex() != motherGlobalIndex) { + return info; + } + } + const auto& mother = *motherIt; + info.found = true; + info.pdg = mother.pdgCode(); + info.px = mother.px(); + info.py = mother.py(); + info.pz = mother.pz(); + info.mass = std::sqrt(mother.e() * mother.e() - mother.px() * mother.px() - mother.py() * mother.py() - mother.pz() * mother.pz()); + return info; + } + + //-------------------------------------------------------------------------------- + // process BCs to get trigger information + void processBCs(BCsTSsSels const& bcs) + { + // check if new run + // -- assuming that each call to processBCs involves only one run + const auto& bcAt0 = bcs.iteratorAt(0); + if (runNumberBc != bcAt0.runNumber()) { // new run + runNumberBc = bcAt0.runNumber(); + getRunInfo(runNumberBc); + getFillingScheme(); + getRofInfo(); + addBcHistos(runNumberBc); + fillBcPatternHistos(runNumberBc); + } + + //-------------------------------------------------------------------------------- + for (const auto& bc : bcs) { + // get info for this bc + int64_t thisBC = getBcWithinOrbit(bc.globalBC()); + int64_t thisTF = getTimeFrame(bc.globalBC()); + int thisROF = getRof(thisBC); + + // count TF seen + if (thisTF != currentTF) { + currentTF = thisTF; + bcTH1Pointers[Form("bc/%d/tf_H", runNumberBc)]->Fill(thisTF); + } + + // check that the bc pass the selection + if (!checkBcFlags(bc, runNumberBc)) { + continue; + } + + // consider only b-bcs + if (!bcPatternB.test(thisBC)) { + continue; + } + bcTH1Pointers[Form("bc/%d/bcSel_H", runNumberBc)]->Fill(3); + + // get triggers + std::bitset<64> mask = bc.inputMask(); + bool ft0vtxTrg = mask[Ft0VtxIdx]; + bool ft0aTrg = mask[Ft0AIdx]; + bool ft0cTrg = mask[Ft0CIdx]; + bool ft0ceTrg = mask[Ft0CeIdx]; + if (ft0aTrg && !ft0cTrg) { + bcTH2Pointers[Form("bc/%d/ft0AnotC_bcb_H", runNumberBc)]->Fill(thisTF, bcbIdx[thisBC]); + } + if (!ft0aTrg && ft0cTrg) { + bcTH2Pointers[Form("bc/%d/ft0CnotA_bcb_H", runNumberBc)]->Fill(thisTF, bcbIdx[thisBC]); + } + if (ft0vtxTrg) { + bcTH2Pointers[Form("bc/%d/ft0Vtx_H", runNumberBc)]->Fill(thisTF, thisROF); + bcTH2Pointers[Form("bc/%d/ft0Vtx_bcb_H", runNumberBc)]->Fill(thisTF, bcbIdx[thisBC]); + if (ft0ceTrg) { + bcTH2Pointers[Form("bc/%d/ft0VtxCe_H", runNumberBc)]->Fill(thisTF, thisROF); + bcTH2Pointers[Form("bc/%d/ft0VtxCe_bcb_H", runNumberBc)]->Fill(thisTF, bcbIdx[thisBC]); + } + } + } // loop over bcs + + // reset current time frame + currentTF = -1; + } // end processBCs + PROCESS_SWITCH(UpcVmRof, processBCs, "get BCs and trigger information", true); + + //-------------------------------------------------------------------------------- + // shared collision+track selection; used by both real data and MC + template + std::vector fillCollisionTables( + ColType const& col, BCsTSsSels const& bcs, TracksType const& tracks, + aod::FV0As const&, aod::FT0s const&, aod::FDDs const&, aod::Zdcs const&, + bool& outIsTwoBody, bool& outIsFourBody) + { + outIsTwoBody = false; + outIsFourBody = false; + std::vector selTrks; + + // get info for this bc + auto bc = col.template foundBC_as(); + if (runNumberCol != bc.runNumber()) { // new run + runNumberCol = bc.runNumber(); + getRunInfo(runNumberCol); + getFillingScheme(); + addColHistos(runNumberCol); + setNearestBCB(); + } + int64_t thisBC = getBcWithinOrbit(bc.globalBC()); + int64_t thisTF = getTimeFrame(bc.globalBC()); + int64_t thisROF = getRof(thisBC); + + // select collision + if (!checkColFlags(col, runNumberCol)) { + return selTrks; + } + + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(10); + + // select on zVtx + if (std::abs(col.posZ()) > maxAbsPosZ) { + return selTrks; + } + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(11); + + // select number of contributors + bool isTwoContributors = (col.numContrib() == NTrksTwoBody); + bool isFourContributors = (col.numContrib() == NTrksFourBody); + if (!isTwoContributors && !isFourContributors) { + return selTrks; + } + if (isTwoContributors) { + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(16); + } + if (isFourContributors) { + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(18); + } + + // select tracks + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(0); + for (const auto& track : tracks) { + if (!track.isPVContributor()) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(1); + if (!track.hasITS()) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(2); + if (!track.hasTPC()) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(3); + colTH1Pointers[Form("col/%d/tpcChi2_H", runNumberCol)]->Fill(track.tpcChi2NCl()); + colTH1Pointers[Form("col/%d/itsChi2_H", runNumberCol)]->Fill(track.itsChi2NCl()); + colTH1Pointers[Form("col/%d/tpcNcls_H", runNumberCol)]->Fill(track.tpcNClsFindable()); + colTH1Pointers[Form("col/%d/tpcXoF_H", runNumberCol)]->Fill(track.tpcCrossedRowsOverFindableCls()); + colTH1Pointers[Form("col/%d/trkDcaZ_H", runNumberCol)]->Fill(track.dcaZ()); + colTH1Pointers[Form("col/%d/trkDcaXY_H", runNumberCol)]->Fill(track.dcaXY()); + if (track.tpcChi2NCl() > maxTrkTpcChi2) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(4); + if (track.itsChi2NCl() > maxTrkItsChi2) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(5); + if (track.tpcNClsFindable() < minTrkTpcClusters) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(6); + if (std::abs(track.dcaZ()) > maxTrkDcaZ) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(7); + float maxTrkDcaXY = 0.0105 + 0.035 / std::pow(track.pt(), 1.1); + if (std::abs(track.dcaXY()) > maxTrkDcaXY) { + continue; + } + colTH1Pointers[Form("col/%d/trkSel_H", runNumberCol)]->Fill(8); + selTrks.push_back(track); + } + bool isTwoBody = isTwoContributors && (selTrks.size() == NTrksTwoBody); + bool isFourBody = isFourContributors && (selTrks.size() == NTrksFourBody); + if (!isTwoBody && !isFourBody) { + return selTrks; + } + + // selected events + if (isTwoBody) { + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(17); + } + if (isFourBody) { + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(19); + } + + // find the bc row for the nearest bc if different from the current one + bool foundBCB = true; + auto nearbcb = bcs.iteratorAt(bc.globalIndex()); + if (vecDistNearestBCB[thisBC] > 0) { + foundBCB = false; + auto gidxBCB = bc.globalIndex() + vecDistNearestBCB[thisBC]; + if (gidxBCB <= bcs.iteratorAt(bcs.size() - 1).globalIndex()) { + while (nearbcb != bcs.end()) { + ++nearbcb; + if (nearbcb.globalIndex() > gidxBCB) { + break; + } else if (getBcWithinOrbit(nearbcb.globalBC()) == vecNearestBCB[thisBC]) { + foundBCB = true; + break; + } + } // end while + } + } // end search in future direction + if (vecDistNearestBCB[thisBC] < 0) { + foundBCB = false; + auto gidxBCB = bc.globalIndex() + vecDistNearestBCB[thisBC]; + if (gidxBCB >= bcs.iteratorAt(0).globalIndex()) { + while (nearbcb != bcs.iteratorAt(0)) { + --nearbcb; + if (nearbcb.globalIndex() < gidxBCB) { + break; + } else if (getBcWithinOrbit(nearbcb.globalBC()) == vecNearestBCB[thisBC]) { + foundBCB = true; + break; + } + } // end while + } + } // end search in past direction + + // FT0 selection + float aFT0A = 0; + float aFT0C = 0; + float tFT0A = 33; // default time to mark events without FT0 info + float tFT0C = 33; // default time to mark events without FT0 info + int nFT0A = 0; + int nFT0C = 0; + if (foundBCB && nearbcb.has_foundFT0()) { + // a side + if (nearbcb.foundFT0().isValidTimeA()) { // valid time + tFT0A = nearbcb.foundFT0().timeA(); + if (std::abs(tFT0A) > maxAbsTimeFT0) { + return selTrks; + } + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(12); + aFT0A = nearbcb.foundFT0().sumAmpA(); + if (aFT0A > maxAmpFT0) { + return selTrks; + } + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(13); + nFT0A = (nearbcb.foundFT0().amplitudeA()).size(); + } // a side + // c side + if (nearbcb.foundFT0().isValidTimeC()) { // valid time + tFT0C = nearbcb.foundFT0().timeC(); + if (std::abs(tFT0C) > maxAbsTimeFT0) { + return selTrks; + } + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(14); + aFT0C = nearbcb.foundFT0().sumAmpC(); + if (aFT0C > maxAmpFT0) { + return selTrks; + } + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(15); + nFT0C = (nearbcb.foundFT0().amplitudeC()).size(); + } // c side + } // FT0 selection + + // final number of selected events + if (isTwoBody) { + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(20); + } + if (isFourBody) { + colTH1Pointers[Form("col/%d/colSel_H", runNumberCol)]->Fill(21); + } + + // get FV0A info + float aFV0A = 0; + float tFV0A = 33; // default time to mark events without FV0 info + int nFV0A = 0; + if (foundBCB && nearbcb.has_foundFV0()) { + tFV0A = nearbcb.foundFV0().time(); + auto v = nearbcb.foundFV0().amplitude(); + aFV0A = std::accumulate(v.begin(), v.end(), 0.f); + nFV0A = v.size(); + } // FV0A info + + // get FDD info + float aFDDA = 0; + float tFDDA = 33; // default time to mark events without FDD info + int nFDDA = 0; + float aFDDC = 0; + float tFDDC = 33; // default time to mark events without FDD info + int nFDDC = 0; + if (foundBCB && nearbcb.has_foundFDD()) { + tFDDA = nearbcb.foundFDD().timeA(); + auto vA = nearbcb.foundFDD().chargeA(); + // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}}; + if (vA[0] > 0 && vA[4] > 0) { + aFDDA += 0.5 * (vA[0] + vA[4]); + nFDDA++; + } + if (vA[1] > 0 && vA[5] > 0) { + aFDDA += 0.5 * (vA[1] + vA[5]); + nFDDA++; + } + if (vA[2] > 0 && vA[6] > 0) { + aFDDA += 0.5 * (vA[2] + vA[6]); + nFDDA++; + } + if (vA[3] > 0 && vA[7] > 0) { + aFDDA += 0.5 * (vA[3] + vA[7]); + nFDDA++; + } + tFDDC = nearbcb.foundFDD().timeC(); + auto vC = nearbcb.foundFDD().chargeC(); + // channelPairs = {{0, 4}, {1, 5}, {2, 6}, {3, 7}}; + if (vC[0] > 0 && vC[4] > 0) { + aFDDC += 0.5 * (vC[0] + vC[4]); + nFDDC++; + } + if (vC[1] > 0 && vC[5] > 0) { + aFDDC += 0.5 * (vC[1] + vC[5]); + nFDDC++; + } + if (vC[2] > 0 && vC[6] > 0) { + aFDDC += 0.5 * (vC[2] + vC[6]); + nFDDC++; + } + if (vC[3] > 0 && vC[7] > 0) { + aFDDC += 0.5 * (vC[3] + vC[7]); + nFDDC++; + } + } // FDD info + + // get ZDC info + float tZNA = -999; // default time to mark events without ZN info + float tZNC = -999; // default time to mark events without ZN info + float eZNA = -999; + float eZNC = -999; + if (foundBCB && nearbcb.has_zdc()) { + tZNA = (nearbcb.zdc()).timeZNA(); + tZNC = (nearbcb.zdc()).timeZNC(); + eZNA = (nearbcb.zdc()).energyCommonZNA(); + eZNC = (nearbcb.zdc()).energyCommonZNC(); + if (!std::isfinite(tZNA)) { + tZNA = -999; + } + if (!std::isfinite(tZNC)) { + tZNC = -999; + } + if (!std::isfinite(eZNA)) { + eZNA = -999; + } + if (!std::isfinite(eZNC)) { + eZNC = -999; + } + } // ZDC info + + // fill output table + const int recoFlag = ((col.flags() & dataformats::Vertex>::Flags::UPCMode) != 0) ? upcReco : stdReco; + int tof[4] = {0, 0, 0, 0}; + if (isTwoBody) { + if (selTrks[0].hasTOF()) { + tof[0] = 1; + } + if (selTrks[1].hasTOF()) { + tof[1] = 1; + } + colTH1Pointers[Form("col/%d/twoTrkTF_H", runNumberCol)]->Fill(thisTF); + twoTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, vecNearestBCB[thisBC], thisTF, thisROF, recoFlag, + aFT0A, aFT0C, aFV0A, aFDDA, aFDDC, tFT0A, tFT0C, tFV0A, tFDDA, tFDDC, nFT0A, nFT0C, nFV0A, nFDDA, nFDDC, + eZNA, eZNC, tZNA, tZNC, + selTrks[0].pt(), selTrks[0].eta(), selTrks[0].phi(), selTrks[0].sign(), + selTrks[0].tpcNSigmaPi(), selTrks[0].tpcNSigmaEl(), selTrks[0].tpcNSigmaKa(), selTrks[0].tpcNSigmaPr(), + selTrks[1].pt(), selTrks[1].eta(), selTrks[1].phi(), selTrks[1].sign(), + selTrks[1].tpcNSigmaPi(), selTrks[1].tpcNSigmaEl(), selTrks[1].tpcNSigmaKa(), selTrks[1].tpcNSigmaPr(), + tof[0], tof[1]); + } + if (isFourBody) { + if (selTrks[0].hasTOF()) { + tof[0] = 1; + } + if (selTrks[1].hasTOF()) { + tof[1] = 1; + } + if (selTrks[2].hasTOF()) { + tof[2] = 1; + } + if (selTrks[3].hasTOF()) { + tof[3] = 1; + } + colTH1Pointers[Form("col/%d/fourTrkTF_H", runNumberCol)]->Fill(thisTF); + fourTrkTable(runNumberCol, col.posX(), col.posY(), col.posZ(), col.chi2(), thisBC, vecNearestBCB[thisBC], thisTF, thisROF, recoFlag, + aFT0A, aFT0C, aFV0A, aFDDA, aFDDC, tFT0A, tFT0C, tFV0A, tFDDA, tFDDC, nFT0A, nFT0C, nFV0A, nFDDA, nFDDC, + eZNA, eZNC, tZNA, tZNC, + selTrks[0].pt(), selTrks[0].eta(), selTrks[0].phi(), selTrks[0].sign(), + selTrks[0].tpcNSigmaPi(), selTrks[0].tpcNSigmaEl(), selTrks[0].tpcNSigmaKa(), selTrks[0].tpcNSigmaPr(), + selTrks[1].pt(), selTrks[1].eta(), selTrks[1].phi(), selTrks[1].sign(), + selTrks[1].tpcNSigmaPi(), selTrks[1].tpcNSigmaEl(), selTrks[1].tpcNSigmaKa(), selTrks[1].tpcNSigmaPr(), + selTrks[2].pt(), selTrks[2].eta(), selTrks[2].phi(), selTrks[2].sign(), + selTrks[2].tpcNSigmaPi(), selTrks[2].tpcNSigmaEl(), selTrks[2].tpcNSigmaKa(), selTrks[2].tpcNSigmaPr(), + selTrks[3].pt(), selTrks[3].eta(), selTrks[3].phi(), selTrks[3].sign(), + selTrks[3].tpcNSigmaPi(), selTrks[3].tpcNSigmaEl(), selTrks[3].tpcNSigmaKa(), selTrks[3].tpcNSigmaPr(), + tof[0], tof[1], tof[2], tof[3]); + } + + outIsTwoBody = isTwoBody; + outIsFourBody = isFourBody; + return selTrks; + } // end fillCollisionTables + + //-------------------------------------------------------------------------------- + // process real data or MC at reconstruction level + void processDataCols(ColSel const& col, BCsTSsSels const& bcs, TRKs const& tracks, + aod::FV0As const& fv0s, aod::FT0s const& ft0s, aod::FDDs const& fdds, aod::Zdcs const& zdcs) + { + bool isTwoBody, isFourBody; + fillCollisionTables(col, bcs, tracks, fv0s, ft0s, fdds, zdcs, isTwoBody, isFourBody); + } // end processDataCols + PROCESS_SWITCH(UpcVmRof, processDataCols, "process real data or mc reco, no mc truth", true); + + //-------------------------------------------------------------------------------- + // MC only: fills the same reconstruction tables and the generated information of the reconstructed event + void processMcCols(ColSelMc const& col, BCsTSsSels const& bcs, TRKsMc const& tracks, + aod::FV0As const& fv0s, aod::FT0s const& ft0s, aod::FDDs const& fdds, aod::Zdcs const& zdcs, + aod::McCollisions const&, aod::McParticles const& mcParticles) + { + if (genId != -1) { + if (!col.has_mcCollision() || col.mcCollision().getGeneratorId() != genId) { + return; + } + } + + bool isTwoBody, isFourBody; + auto selTrks = fillCollisionTables(col, bcs, tracks, fv0s, ft0s, fdds, zdcs, isTwoBody, isFourBody); + if (!isTwoBody && !isFourBody) { + return; // event was not written to the reco tables either + } + + // MC gen + if (isTwoBody) { + auto mcPart1 = selTrks[0].has_mcParticle() ? std::optional(selTrks[0].mcParticle()) : std::nullopt; + auto mcPart2 = selTrks[1].has_mcParticle() ? std::optional(selTrks[1].mcParticle()) : std::nullopt; + + int pdg1 = 0, pdg2 = 0, sign1 = 0, sign2 = 0, isPrim1 = 0, isPrim2 = 0; + float pt1 = -999, eta1 = -999, phi1 = -999, pt2 = -999, eta2 = -999, phi2 = -999; + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcLocalBC = -999; + + if (mcPart1 && mcPart2) { + pdg1 = mcPart1->pdgCode(); + pt1 = mcPt(mcPart1->px(), mcPart1->py()); + eta1 = mcEta(mcPart1->px(), mcPart1->py(), mcPart1->pz()); + phi1 = mcPhi(mcPart1->px(), mcPart1->py()); + sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + isPrim1 = mcPart1->isPhysicalPrimary() ? 1 : 0; + + pdg2 = mcPart2->pdgCode(); + pt2 = mcPt(mcPart2->px(), mcPart2->py()); + eta2 = mcEta(mcPart2->px(), mcPart2->py(), mcPart2->pz()); + phi2 = mcPhi(mcPart2->px(), mcPart2->py()); + sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + isPrim2 = mcPart2->isPhysicalPrimary() ? 1 : 0; + + auto mcCollision = mcPart1->mcCollision(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcLocalBC = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + float sumPx = mcPart1->px() + mcPart2->px(); + float sumPy = mcPart1->py() + mcPart2->py(); + float sumPz = mcPart1->pz() + mcPart2->pz(); + float sumE = mcPart1->e() + mcPart2->e(); + motherPt = mcPt(sumPx, sumPy); + motherPhi = mcPhi(sumPx, sumPy); + motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + motherRap = mcRapidity(sumPx, sumPy, sumPz, motherMass); + + MotherInfo mi = findCommonMother(mcParticles, std::vector{*mcPart1, *mcPart2}); + if (mi.found) { + motherPdg = mi.pdg; + } + } + + twoTrkRecGenTable( + motherPdg, motherPt, motherPhi, motherMass, motherRap, + pdg1, pt1, eta1, phi1, sign1, isPrim1, + pdg2, pt2, eta2, phi2, sign2, isPrim2, + mcPosX, mcPosY, mcPosZ, mcLocalBC, + twoTrkTable.lastIndex() // row of TwoTrkTable this mc info belongs to + ); + } + if (isFourBody) { + auto mcPart1 = selTrks[0].has_mcParticle() ? std::optional(selTrks[0].mcParticle()) : std::nullopt; + auto mcPart2 = selTrks[1].has_mcParticle() ? std::optional(selTrks[1].mcParticle()) : std::nullopt; + auto mcPart3 = selTrks[2].has_mcParticle() ? std::optional(selTrks[2].mcParticle()) : std::nullopt; + auto mcPart4 = selTrks[3].has_mcParticle() ? std::optional(selTrks[3].mcParticle()) : std::nullopt; + + int pdg1 = 0, pdg2 = 0, pdg3 = 0, pdg4 = 0; + int sign1 = 0, sign2 = 0, sign3 = 0, sign4 = 0; + int isPrim1 = 0, isPrim2 = 0, isPrim3 = 0, isPrim4 = 0; + float pt1 = -999, eta1 = -999, phi1 = -999; + float pt2 = -999, eta2 = -999, phi2 = -999; + float pt3 = -999, eta3 = -999, phi3 = -999; + float pt4 = -999, eta4 = -999, phi4 = -999; + int motherPdg = 0; + float motherPt = -999, motherPhi = -999, motherMass = -999, motherRap = -999; + float mcPosX = -999, mcPosY = -999, mcPosZ = -999; + int mcLocalBC = -999; + + if (mcPart1 && mcPart2 && mcPart3 && mcPart4) { + pdg1 = mcPart1->pdgCode(); + pt1 = mcPt(mcPart1->px(), mcPart1->py()); + eta1 = mcEta(mcPart1->px(), mcPart1->py(), mcPart1->pz()); + phi1 = mcPhi(mcPart1->px(), mcPart1->py()); + sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + isPrim1 = mcPart1->isPhysicalPrimary() ? 1 : 0; + + pdg2 = mcPart2->pdgCode(); + pt2 = mcPt(mcPart2->px(), mcPart2->py()); + eta2 = mcEta(mcPart2->px(), mcPart2->py(), mcPart2->pz()); + phi2 = mcPhi(mcPart2->px(), mcPart2->py()); + sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + isPrim2 = mcPart2->isPhysicalPrimary() ? 1 : 0; + + pdg3 = mcPart3->pdgCode(); + pt3 = mcPt(mcPart3->px(), mcPart3->py()); + eta3 = mcEta(mcPart3->px(), mcPart3->py(), mcPart3->pz()); + phi3 = mcPhi(mcPart3->px(), mcPart3->py()); + sign3 = (pdg3 > 0) ? 1 : (pdg3 < 0) ? -1 + : 0; + isPrim3 = mcPart3->isPhysicalPrimary() ? 1 : 0; + + pdg4 = mcPart4->pdgCode(); + pt4 = mcPt(mcPart4->px(), mcPart4->py()); + eta4 = mcEta(mcPart4->px(), mcPart4->py(), mcPart4->pz()); + phi4 = mcPhi(mcPart4->px(), mcPart4->py()); + sign4 = (pdg4 > 0) ? 1 : (pdg4 < 0) ? -1 + : 0; + isPrim4 = mcPart4->isPhysicalPrimary() ? 1 : 0; + + auto mcCollision = mcPart1->mcCollision(); + mcPosX = mcCollision.posX(); + mcPosY = mcCollision.posY(); + mcPosZ = mcCollision.posZ(); + mcLocalBC = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + float sumPx = mcPart1->px() + mcPart2->px() + mcPart3->px() + mcPart4->px(); + float sumPy = mcPart1->py() + mcPart2->py() + mcPart3->py() + mcPart4->py(); + float sumPz = mcPart1->pz() + mcPart2->pz() + mcPart3->pz() + mcPart4->pz(); + float sumE = mcPart1->e() + mcPart2->e() + mcPart3->e() + mcPart4->e(); + motherPt = mcPt(sumPx, sumPy); + motherPhi = mcPhi(sumPx, sumPy); + motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + motherRap = mcRapidity(sumPx, sumPy, sumPz, motherMass); + + MotherInfo mi = findCommonMother(mcParticles, std::vector{*mcPart1, *mcPart2, *mcPart3, *mcPart4}); + if (mi.found) { + motherPdg = mi.pdg; + } + } + + fourTrkRecGenTable( + motherPdg, motherPt, motherPhi, motherMass, motherRap, + pdg1, pt1, eta1, phi1, sign1, isPrim1, + pdg2, pt2, eta2, phi2, sign2, isPrim2, + pdg3, pt3, eta3, phi3, sign3, isPrim3, + pdg4, pt4, eta4, phi4, sign4, isPrim4, + mcPosX, mcPosY, mcPosZ, mcLocalBC, + fourTrkTable.lastIndex() // row of FourTrkTable this mc info belongs to + ); + } + } // end processMcCols + PROCESS_SWITCH(UpcVmRof, processMcCols, "process mc truth matched to reco, mc only", false); + + //-------------------------------------------------------------------------------- + // Fill MC information for all generated events + void processMcGen(aod::McCollision const& mcCollision, BCsTSsSels const&, aod::McParticles const& mcParticles) + { + if (genId != -1 && mcCollision.getGeneratorId() != genId) { + return; + } + + // event counter + if (!colTH1Pointers["mcGen/genSel_H"]) { + colTH1Pointers["mcGen/genSel_H"] = colTH1Registry.add("mcGen/genSel_H", + "pure generator event counter; selID; Counter", + {HistType::kTH1D, {{3, -0.5, 2.5}}}); + } + colTH1Pointers["mcGen/genSel_H"]->Fill(0); + + std::vector primaries; + for (const auto& part : mcParticles) { + if (part.isPhysicalPrimary()) { + primaries.push_back(part); + } + } + + // local BC of the generated collision + int localBc = static_cast(getBcWithinOrbit(mcCollision.template bc_as().globalBC())); + + if (primaries.size() == NTrksTwoBody) { + auto p1 = primaries[0]; + auto p2 = primaries[1]; + + int pdg1 = p1.pdgCode(); + int pdg2 = p2.pdgCode(); + int sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + int sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + + // mother mass/momentum always from summed daughters + float sumPx = p1.px() + p2.px(); + float sumPy = p1.py() + p2.py(); + float sumPz = p1.pz() + p2.pz(); + float sumE = p1.e() + p2.e(); + float motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + + int motherPdg = 0; // 0 = no common mother confirmed in the gen tree + MotherInfo mi = findCommonMotherFromIters(/* mcParticles,*/ std::vector{p1, p2}); + if (mi.found) { + motherPdg = mi.pdg; + } + + colTH1Pointers["mcGen/genSel_H"]->Fill(1); + twoTrkGenTable( + motherPdg, mcPt(sumPx, sumPy), mcPhi(sumPx, sumPy), motherMass, mcRapidity(sumPx, sumPy, sumPz, motherMass), + pdg1, mcPt(p1.px(), p1.py()), mcEta(p1.px(), p1.py(), p1.pz()), mcPhi(p1.px(), p1.py()), sign1, 1, + pdg2, mcPt(p2.px(), p2.py()), mcEta(p2.px(), p2.py(), p2.pz()), mcPhi(p2.px(), p2.py()), sign2, 1, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), localBc); + } + + if (primaries.size() == NTrksFourBody) { + auto p1 = primaries[0]; + auto p2 = primaries[1]; + auto p3 = primaries[2]; + auto p4 = primaries[3]; + + int pdg1 = p1.pdgCode(); + int pdg2 = p2.pdgCode(); + int pdg3 = p3.pdgCode(); + int pdg4 = p4.pdgCode(); + int sign1 = (pdg1 > 0) ? 1 : (pdg1 < 0) ? -1 + : 0; + int sign2 = (pdg2 > 0) ? 1 : (pdg2 < 0) ? -1 + : 0; + int sign3 = (pdg3 > 0) ? 1 : (pdg3 < 0) ? -1 + : 0; + int sign4 = (pdg4 > 0) ? 1 : (pdg4 < 0) ? -1 + : 0; + + float sumPx = p1.px() + p2.px() + p3.px() + p4.px(); + float sumPy = p1.py() + p2.py() + p3.py() + p4.py(); + float sumPz = p1.pz() + p2.pz() + p3.pz() + p4.pz(); + float sumE = p1.e() + p2.e() + p3.e() + p4.e(); + float motherMass = std::sqrt(sumE * sumE - sumPx * sumPx - sumPy * sumPy - sumPz * sumPz); + + int motherPdg = 0; + MotherInfo mi = findCommonMotherFromIters(/* mcParticles, */ std::vector{p1, p2, p3, p4}); + if (mi.found) { + motherPdg = mi.pdg; + } + + colTH1Pointers["mcGen/genSel_H"]->Fill(2); + fourTrkGenTable( + motherPdg, mcPt(sumPx, sumPy), mcPhi(sumPx, sumPy), motherMass, mcRapidity(sumPx, sumPy, sumPz, motherMass), + pdg1, mcPt(p1.px(), p1.py()), mcEta(p1.px(), p1.py(), p1.pz()), mcPhi(p1.px(), p1.py()), sign1, 1, + pdg2, mcPt(p2.px(), p2.py()), mcEta(p2.px(), p2.py(), p2.pz()), mcPhi(p2.px(), p2.py()), sign2, 1, + pdg3, mcPt(p3.px(), p3.py()), mcEta(p3.px(), p3.py(), p3.pz()), mcPhi(p3.px(), p3.py()), sign3, 1, + pdg4, mcPt(p4.px(), p4.py()), mcEta(p4.px(), p4.py(), p4.pz()), mcPhi(p4.px(), p4.py()), sign4, 1, + mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(), localBc); + } + } // end processMcGenAll + PROCESS_SWITCH(UpcVmRof, processMcGen, "process all generated collisions, mc only", false); + +}; // end of struct UpcVmRof + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) + +{ + return WorkflowSpec{ + adaptAnalysisTask(cfgc)}; +} diff --git a/Scripts/format_includes.awk b/Scripts/format_includes.awk deleted file mode 100644 index ac22332acc5..00000000000 --- a/Scripts/format_includes.awk +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/awk -f - -# Fix include style. -# NB: Run before sorting. - -{ - if ($1 ~ /^#include/) { - h = substr($2, 2, length($2) - 2) - if ( h ~ /^(PWG[A-Z]{2}|Common|ALICE3|DPG|EventFiltering|Tools|Tutorials)\/.*\.h/ ) { $2 = "\""h"\"" } # O2Physics - else if ( h ~ /^(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MFT|MCH|MID|PHOS|PID|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*\/.*\.h/ ) { $2 = "<"h">" } # O2 - else if ( h ~ /^(T[A-Z]|Math\/|Roo[A-Z])[[:alnum:]\/]+\.h/ ) { $2 = "<"h">" } # ROOT - else if ( h ~ /^KF[A-Z][[:alnum:]]+\.h/ ) { $2 = "<"h">" } # KFParticle - else if ( h ~ /^(fastjet\/|onnxruntime)/ ) { $2 = "<"h">" } # FastJet, ONNX - else if ( h ~ /^.*DataModel\// ) { $2 = "\""h"\"" } # incomplete path to DataModel - else if ( h ~ /^([[:alnum:]_]+\/)+[[:alnum:]_]+\.h/ ) { $2 = "<"h">" } # other third-party - else if ( $2 ~ /^".*\./ ) { } # other local-looking file - else if ( h ~ /^[[:lower:]_]+\.h/ ) { $2 = "<"h">" } # C system - else if ( h ~ /^[[:lower:]_\/]+/ ) { $2 = "<"h">" } # C++ system - } - print -} diff --git a/Scripts/format_includes.py b/Scripts/format_includes.py new file mode 100755 index 00000000000..c545441d9af --- /dev/null +++ b/Scripts/format_includes.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +"""! +@brief Fix include style +@author Vít Kučera , Inha University +@date 2026-10-08 + +NB: Run before sorting. + +Usage: format_includes.py FILE [FILE ...] +""" + +import re +import sys + +LOCAL = ('"', '"') +EXTERNAL = ("<", ">") + +INCLUDE = re.compile(r"(\s*#include\s+)(\S+)") + + +def fix_line(line: str) -> str: + m = INCLUDE.match(line) + if not m: + return line + pre, tok = m.groups() + rest = line[m.end() :] + h = tok[1:-1] + + if re.match(r"(PWG[A-Z]{2}|Common|ALICE3|DPG|EventFiltering|PID|Tools|Tutorials)/.*\.h", h): + d = LOCAL # O2Physics + elif re.match( + r"(Algorithm|CCDB|Common[A-Z]|DataFormats|DCAFitter|Detectors|EMCAL|FDD|Field|Framework|FT0|FV0|GlobalTracking|GPU|ITS|MathUtils|MCH|MFT|MID|PHOS|ReconstructionDataFormats|SimulationDataFormat|TOF|TPC|ZDC).*/.*\.h", + h, + ): + d = EXTERNAL # O2 + elif re.match(r"(T[A-Z]|Math/|Roo[A-Z])[A-Za-z0-9/]+\.h", h): + d = EXTERNAL # ROOT + elif re.match(r"KF[A-Z][A-Za-z0-9]+\.h", h): + d = EXTERNAL # KFParticle + elif re.match(r"(fastjet/|onnxruntime)", h): + d = EXTERNAL # FastJet, ONNX + elif re.match(r".*DataModel/", h): + d = LOCAL # incomplete path to DataModel + elif re.match(r"([A-Za-z0-9_]+/)+[A-Za-z0-9_]+\.h", h): + d = EXTERNAL # other third-party + elif re.match(r'".*\.', tok): + return line # other local-looking file + elif re.match(r"[a-z_]+\.h", h): + d = EXTERNAL # C system + elif re.match(r"[a-z_/]+$", h): + d = EXTERNAL # C++ system (whole string) + else: + return line + + return pre + d[0] + h + d[1] + rest + + +def process(path: str): + # newline='' keeps the original line endings untouched. + with open(path, newline="") as f: + lines = f.readlines() + new_lines = [fix_line(line) for line in lines] + if new_lines != lines: + with open(path, "w", newline="") as f: + f.writelines(new_lines) + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.exit(__doc__) + for p in sys.argv[1:]: + process(p) diff --git a/Scripts/o2_linter.py b/Scripts/o2_linter.py old mode 100644 new mode 100755 diff --git a/Tools/CMakeLists.txt b/Tools/CMakeLists.txt index 2b1de764169..ad870181e8c 100644 --- a/Tools/CMakeLists.txt +++ b/Tools/CMakeLists.txt @@ -12,3 +12,4 @@ add_subdirectory(PIDML) add_subdirectory(ML) add_subdirectory(KFparticle) +add_subdirectory(PIDFeatureExtractor) diff --git a/Tools/KFparticle/KFUtilities.h b/Tools/KFparticle/KFUtilities.h index b6c6e607a9b..1a9a093a588 100644 --- a/Tools/KFparticle/KFUtilities.h +++ b/Tools/KFparticle/KFUtilities.h @@ -192,7 +192,7 @@ o2::track::TrackParCov getTrackParCovFromKFP(const KFParticle& kfParticle, const /// @param kfp KFParticle /// @param PV KFParticle primary vertex /// @return cpa -float cpaFromKF(KFParticle kfp, KFParticle PV) +float cpaFromKF(const KFParticle& kfp, const KFParticle& PV) { float xVtxP{}, yVtxP{}, zVtxP{}, xVtxS{}, yVtxS{}, zVtxS{}, px{}, py{}, pz{}; @@ -216,7 +216,7 @@ float cpaFromKF(KFParticle kfp, KFParticle PV) /// @param kfp KFParticle /// @param PV KFParticle primary vertex /// @return cpa in xy -float cpaXYFromKF(KFParticle kfp, KFParticle PV) +float cpaXYFromKF(const KFParticle& kfp, const KFParticle& PV) { float xVtxP{}, yVtxP{}, xVtxS{}, yVtxS{}, px{}, py{}; @@ -243,7 +243,7 @@ float cpaXYFromKF(KFParticle kfp, KFParticle PV) /// @param kfpprong1 KFParticele Prong 1 /// @param pdgdb Service PDG data base /// @return cos theta star -float cosThetaStarFromKF(int iProng, int pdgvtx, int pdgprong0, int pdgprong1, KFParticle kfpprong0, KFParticle kfpprong1, const o2::framework::Service& pdgdb) +float cosThetaStarFromKF(int iProng, int pdgvtx, int pdgprong0, int pdgprong1, const KFParticle& kfpprong0, const KFParticle& kfpprong1, const o2::framework::Service& pdgdb) { float px0{}, py0{}, pz0{}, px1{}, py1{}, pz1{}; @@ -269,7 +269,7 @@ float cosThetaStarFromKF(int iProng, int pdgvtx, int pdgprong0, int pdgprong1, K /// @param kfpParticle KFParticle /// @param Vertex KFParticle vertex /// @return impact parameter -float impParXYFromKF(KFParticle kfpParticle, KFParticle Vertex) +float impParXYFromKF(const KFParticle& kfpParticle, const KFParticle& Vertex) { float xVtxP{}, yVtxP{}, zVtxP{}, xVtxS{}, yVtxS{}, zVtxS{}, px{}, py{}, pz{}; @@ -293,7 +293,7 @@ float impParXYFromKF(KFParticle kfpParticle, KFParticle Vertex) /// @param kfpParticle KFParticle /// @param PV KFParticle primary vertex /// @return l/delta l -float ldlFromKF(KFParticle kfpParticle, KFParticle PV) +float ldlFromKF(const KFParticle& kfpParticle, const KFParticle& PV) { const float dxParticle = PV.GetX() - kfpParticle.GetX(); const float dyParticle = PV.GetY() - kfpParticle.GetY(); @@ -312,7 +312,7 @@ float ldlFromKF(KFParticle kfpParticle, KFParticle PV) /// @param kfpParticle KFParticle /// @param PV KFParticle primary vertex /// @return l/delta l in xy plane -float ldlXYFromKF(KFParticle kfpParticle, KFParticle PV) +float ldlXYFromKF(const KFParticle& kfpParticle, const KFParticle& PV) { const float dxParticle = PV.GetX() - kfpParticle.GetX(); const float dyParticle = PV.GetY() - kfpParticle.GetY(); @@ -354,7 +354,7 @@ std::array kfCalculateProngMomentumInSecondaryVertex(KFParticle track, /// @param track1 KFParticle first track (must be passed as a copy) /// @param track2 KFParticle second track (must be passed as a copy) /// @return DCA [cm] in the PCA -float kfCalculateDistanceBetweenParticles(KFParticle track1, KFParticle track2) +float kfCalculateDistanceBetweenParticles(const KFParticle& track1, const KFParticle& track2) { float dS[2]; float dsdr[4][6]; @@ -373,7 +373,7 @@ float kfCalculateDistanceBetweenParticles(KFParticle track1, KFParticle track2) /// @param track1 KFParticle first track (must be passed as a copy) /// @param track2 KFParticle second track (must be passed as a copy) /// @return chi2 in PCA -float kfCalculateChi2geoBetweenParticles(KFParticle track1, KFParticle track2) +float kfCalculateChi2geoBetweenParticles(const KFParticle& track1, const KFParticle& track2) { KFParticle kfPair; const KFParticle* kfDaughters[2] = {&track1, &track2}; diff --git a/Tools/KFparticle/qaKFEventTrack.cxx b/Tools/KFparticle/qaKFEventTrack.cxx index 2242de51154..6e026a08f4e 100644 --- a/Tools/KFparticle/qaKFEventTrack.cxx +++ b/Tools/KFparticle/qaKFEventTrack.cxx @@ -116,7 +116,7 @@ struct qaKFEventTrack { int pVContrib = 0; void initMagneticFieldCCDB(o2::aod::BCsWithTimestamps::iterator const& bc, int& mRunNumber, - o2::framework::Service const& ccdb, std::string ccdbPathGrp, o2::base::MatLayerCylSet* lut, + o2::framework::Service const& ccdb, const std::string& ccdbPathGrp, o2::base::MatLayerCylSet* lut, bool isRun3) { @@ -247,7 +247,7 @@ struct qaKFEventTrack { template void writeVarTree(const T1& /*kfpTrackPi*/, const T2& KFPion, const T2& KFPV, const T3& track, const int source, const int pVContrib, const int runNumber, const T4& collision) { - const double pseudoRndm = track.pt() * 1000. - (int64_t)(track.pt() * 1000); + const double pseudoRndm = track.pt() * 1000. - static_cast(track.pt() * 1000); if (pseudoRndm < d_DwnSmplFact) { if (writeTree) { /// Filling the tree @@ -536,7 +536,7 @@ struct qaKFEvent { Produces rowKFCollisions; void initMagneticFieldCCDB(o2::aod::BCsWithTimestamps::iterator const& bc, int& mRunNumber, - o2::framework::Service const& ccdb, std::string ccdbPathGrp, o2::base::MatLayerCylSet* lut, + o2::framework::Service const& ccdb, const std::string& ccdbPathGrp, o2::base::MatLayerCylSet* lut, bool isRun3) { @@ -629,7 +629,7 @@ struct qaKFEvent { if (!isSelectedCollision(collisionIndex)) { continue; } - timeColl = uint64_t((bc.globalBC() - bc0) * o2::constants::lhc::LHCBunchSpacingNS) + collisionIndex.collisionTime(); + timeColl = static_cast((bc.globalBC() - bc0) * o2::constants::lhc::LHCBunchSpacingNS) + collisionIndex.collisionTime(); writeVarTreeColl(collisionIndex, timeColl, bc.timestamp(), collisionIndex.collisionTime(), tfID); } } diff --git a/Tools/KFparticle/qaKFParticle.cxx b/Tools/KFparticle/qaKFParticle.cxx index 4066ed0f49e..5dff4cd3b05 100644 --- a/Tools/KFparticle/qaKFParticle.cxx +++ b/Tools/KFparticle/qaKFParticle.cxx @@ -156,7 +156,7 @@ struct qaKFParticle { Produces rowKF; void initMagneticFieldCCDB(o2::aod::BCsWithTimestamps::iterator const& bc, int& mRunNumber, - o2::framework::Service const& ccdb, std::string ccdbPathGrp, o2::base::MatLayerCylSet* lut, + o2::framework::Service const& ccdb, const std::string& ccdbPathGrp, o2::base::MatLayerCylSet* lut, bool isRun3) { diff --git a/Tools/KFparticle/qaKFParticleLc.cxx b/Tools/KFparticle/qaKFParticleLc.cxx index 86902d349cd..f9e5c11e4a6 100644 --- a/Tools/KFparticle/qaKFParticleLc.cxx +++ b/Tools/KFparticle/qaKFParticleLc.cxx @@ -152,7 +152,7 @@ struct qaKFParticleLc { Produces rowKFLc; void initMagneticFieldCCDB(o2::aod::BCsWithTimestamps::iterator const& bc, int& mRunNumber, - o2::framework::Service const& ccdb, std::string ccdbPathGrp, o2::base::MatLayerCylSet* lut, + o2::framework::Service const& ccdb, const std::string& ccdbPathGrp, o2::base::MatLayerCylSet* lut, bool isRun3) { @@ -416,7 +416,7 @@ struct qaKFParticleLc { float chi2geo = KFLc.GetChi2() / KFLc.GetNDF(); float normdecayLength = KFLc_PV.GetDecayLength() / KFLc_PV.GetErrDecayLength(); float chi2topo = KFLc_PV.GetChi2() / KFLc_PV.GetNDF(); - const double pseudoRndm = trackKa.pt() * 1000. - (int64_t)(trackKa.pt() * 1000); + const double pseudoRndm = trackKa.pt() * 1000. - static_cast(trackKa.pt() * 1000); if (pseudoRndm < d_DwnSmplFact) { /// Filling the D0 tree rowKFLc(runNumber, diff --git a/Tools/ML/MlResponse.h b/Tools/ML/MlResponse.h index 75da1b8decb..413534f48cb 100644 --- a/Tools/ML/MlResponse.h +++ b/Tools/ML/MlResponse.h @@ -163,7 +163,7 @@ class MlResponse { setAvailableInputFeatures(); for (const auto& inputFeature : cfgInputFeatures) { - if (mAvailableInputFeatures.count(inputFeature)) { + if (mAvailableInputFeatures.contains(inputFeature)) { mCachedIndices.emplace_back(mAvailableInputFeatures[inputFeature]); } else { LOG(fatal) << "Input feature " << inputFeature << " not available! Please check your configurables."; @@ -182,8 +182,63 @@ class MlResponse LOG(fatal) << "Model index " << nModel << " is out of range! The number of initialised models is " << mModels.size() << ". Please check your configurables."; } - TypeOutputScore* outputPtr = mModels[nModel].template evalModel(input); - return std::vector{outputPtr, outputPtr + mNClasses}; + const int numInputNodes = mModels[nModel].getNumInputNodes(); + const int numInputFeatures = static_cast(input.size()); + + // Check that the number of input nodes in the model is equal to the number of input features, except for the case where the model input is dynamic (numInputNodes == -1) + if (numInputNodes != numInputFeatures && numInputNodes >= 0) { + LOG(fatal) << "Number of input nodes in the model " << mPaths[nModel] << " is different from the number of input features to be tested (" << numInputNodes << " vs " << numInputFeatures << ")"; + } + + // evalModel returns an owning copy of the (last) output tensor of the model + std::vector output = mModels[nModel].template evalModel(input); + if (output.size() < mNClasses) { + LOG(fatal) << "Model " << mPaths[nModel] << " returned " << output.size() << " scores, but " << static_cast(mNClasses) << " classes are expected. Please check your configurables."; + } + if (output.size() > mNClasses) { + // keep only the first mNClasses scores (e.g. single-candidate probabilities of a multi-output model) + output.resize(mNClasses); + } + return output; + } + + /// Get vector with model predictions for a batch of candidates + /// \param input a flattened vector containing features for nRows candidates, + /// row-major: [row0_feat0, row0_feat1, ..., row1_feat0, row1_feat1, ...] + /// \param nModel is the model index (same bin must apply to every row in the batch) + /// \param nRows is the number of candidates packed into input + /// \return flat vector of size nRows * mNClasses, row-major: [row][class] + template + std::vector getModelOutputBatched(T1& input, const T2& nModel, std::size_t nRows) + { + if (nModel < 0 || static_cast(nModel) >= mModels.size()) { + LOG(fatal) << "Model index " << nModel << " is out of range! The number of initialised models is " << mModels.size() << ". Please check your configurables."; + } + if (nRows == 0) { + return {}; + } + + const int numInputNodes = mModels[nModel].getNumInputNodes(); + const std::size_t numInputFeatures = input.size(); + + if (numInputFeatures % nRows != 0) { + LOG(fatal) << "Flattened input size (" << numInputFeatures << ") is not divisible by nRows (" << nRows << ")"; + } + const std::size_t featuresPerRow = numInputFeatures / nRows; + if (numInputNodes >= 0 && static_cast(featuresPerRow) != numInputNodes) { + LOG(fatal) << "Number of input nodes in the model " << mPaths[nModel] << " differs from features per row (" << numInputNodes << " vs " << featuresPerRow << ")"; + } + + std::vector output = mModels[nModel].template evalModel(input); + const std::size_t expectedOutputSize = nRows * mNClasses; + if (output.size() < expectedOutputSize) { + LOG(fatal) << "Model " << mPaths[nModel] << " returned " << output.size() << " scores, but " << expectedOutputSize << " scores are expected for " << nRows << " rows and " << static_cast(mNClasses) << " classes. Please check your configurables."; + } + if (output.size() > expectedOutputSize) { + // keep only the first scores (e.g. batched probabilities of a multi-output model) + output.resize(expectedOutputSize); + } + return output; } /// ML selections @@ -271,18 +326,18 @@ class MlResponse std::vector mModels; // OnnxModel objects, one for each bin uint8_t mNModels = 1; // number of bins uint8_t mNClasses = 3; // number of model classes - std::vector mBinsLimits = {}; // bin limits of the variable (e.g. pT) used to select which model to use - std::vector mBinsLimitsVar2 = {}; // bin limits of a second variable (e.g. multiplicity) used to select which model to use (not used in this base class) - std::vector mPaths = {""}; // paths to the models, one for each bin - std::vector mCutDir = {}; // direction of the cuts on the model scores (no cut is also supported) - o2::framework::LabeledArray mCuts = {}; // array of cut values to apply on the model scores + std::vector mBinsLimits; // bin limits of the variable (e.g. pT) used to select which model to use + std::vector mBinsLimitsVar2; // bin limits of a second variable (e.g. multiplicity) used to select which model to use (not used in this base class) + std::vector mPaths; // paths to the models, one for each bin + std::vector mCutDir; // direction of the cuts on the model scores (no cut is also supported) + o2::framework::LabeledArray mCuts; // array of cut values to apply on the model scores std::map mAvailableInputFeatures; // map of available input features std::vector mCachedIndices; // vector of index correspondance between configurables and available input features uint8_t mNVar1Bins = 1; // number of bins of the first variable (e.g. pT) used to select which model to use uint8_t mNVar2Bins = 1; // number of bins of the second variable (e.g. multiplicity) used to select which model to use bool mUse2DBinning = false; // switch to enable/disable 2D binning - virtual void setAvailableInputFeatures() { return; } // method to fill the map of available input features + virtual void setAvailableInputFeatures() {} // method to fill the map of available input features private: /// Finds matching bin in mBinsLimits diff --git a/Tools/ML/model.cxx b/Tools/ML/model.cxx index 6d2098068aa..ebf3919a891 100644 --- a/Tools/ML/model.cxx +++ b/Tools/ML/model.cxx @@ -42,6 +42,9 @@ namespace ml std::string OnnxModel::printShape(const std::vector& v) { + if (v.empty()) { + return "[]"; + } std::stringstream ss(""); for (std::size_t i = 0; i < v.size() - 1; i++) ss << v[i] << "x"; @@ -90,7 +93,14 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz mEnv = std::make_shared(ORT_LOGGING_LEVEL_WARNING, "onnx-model"); mSession = std::make_shared(*mEnv, modelPath.c_str(), sessionOptions); + mMemInfo = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + mInputNamesChar.clear(); + mOutputNamesChar.clear(); + mInputNames.clear(); + mInputShapes.clear(); + mOutputNames.clear(); + mOutputShapes.clear(); Ort::AllocatorWithDefaultOptions const tmpAllocator; for (std::size_t i = 0; i < mSession->GetInputCount(); ++i) { mInputNames.push_back(mSession->GetInputNameAllocated(i, tmpAllocator).get()); @@ -104,6 +114,14 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz for (std::size_t i = 0; i < mSession->GetOutputCount(); ++i) { mOutputShapes.emplace_back(mSession->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); } + mInputNamesChar.reserve(mInputNames.size()); + for (const auto& name : mInputNames) { + mInputNamesChar.push_back(name.c_str()); + } + mOutputNamesChar.reserve(mOutputNames.size()); + for (const auto& name : mOutputNames) { + mOutputNamesChar.push_back(name.c_str()); + } LOG(info) << "Input Nodes:"; for (std::size_t i = 0; i < mInputNames.size(); i++) { LOG(info) << "\t" << mInputNames[i] << " : " << printShape(mInputShapes[i]); @@ -122,6 +140,114 @@ void OnnxModel::initModel(const std::string& localPath, const bool enableOptimiz LOG(info) << "--- Model initialized! ---"; } +std::vector OnnxModel::inferInputShape(const std::size_t iinput, const int64_t size) const +{ + const std::vector& modelShape = mInputShapes[iinput]; + + // Rank-1 input: the whole vector is the tensor + if (modelShape.size() < 2) { + return {size}; + } + + // Product of all non-batch dimensions; dynamic dimensions (< 0) cannot be inferred + int64_t totalSize = 1; + bool hasDynamicDim = false; + for (std::size_t idim = 1; idim < modelShape.size(); idim++) { + if (modelShape[idim] < 0) { + hasDynamicDim = true; + } else { + totalSize *= modelShape[idim]; + } + } + + if (hasDynamicDim) { + if (modelShape.size() == 2) { + // [batch, features] with dynamic feature dimension: interpret the vector as a single sample + return {1, size}; + } + LOG(fatal) << "Input " << iinput << " (" << mInputNames[iinput] << ") has dynamic non-batch dimensions (" << printShape(modelShape) << "), the tensor shape cannot be inferred from a flat vector. Please provide std::vector inputs instead."; + } + + if (totalSize <= 0 || size % totalSize != 0) { + LOG(fatal) << "Size of the input vector (" << size << ") is not a multiple of the model input size (" << totalSize << ") for input " << iinput << " (" << mInputNames[iinput] << ", shape " << printShape(modelShape) << ")"; + } + + std::vector inputShape; + inputShape.reserve(modelShape.size()); + inputShape.push_back(size / totalSize); + for (std::size_t idim = 1; idim < modelShape.size(); idim++) { + inputShape.push_back(modelShape[idim]); + } + return inputShape; +} + +void OnnxModel::checkInput(const std::vector& input) const +{ + if (!mSession) { + LOG(fatal) << "OnnxModel::evalModel called before initModel()"; + } + if (input.size() != mInputNames.size()) { + LOG(fatal) << "Number of input tensors (" << input.size() << ") does not agree with the number of model inputs (" << mInputNames.size() << ")"; + } + for (std::size_t i = 0; i < input.size(); i++) { + LOG(debug) << "Input tensor " << i << " shape: " << printShape(input[i].GetTensorTypeAndShapeInfo().GetShape()); + } +} + +std::vector OnnxModel::evalModelRaw(std::vector& input) +{ + checkInput(input); + std::vector outputTensors; + try { + const Ort::RunOptions runOptions{nullptr}; + outputTensors = mSession->Run(runOptions, mInputNamesChar.data(), input.data(), input.size(), mOutputNamesChar.data(), mOutputNamesChar.size()); + } catch (const Ort::Exception& exception) { + LOG(fatal) << "Error running model inference: " << exception.what(); + } + + LOG(debug) << "Number of output tensors: " << outputTensors.size(); + if (outputTensors.size() != mOutputNames.size()) { + LOG(fatal) << "Number of output tensors: " << outputTensors.size() << " does not agree with the model specified size: " << mOutputNames.size(); + } + for (std::size_t i = 0; i < outputTensors.size(); i++) { + checkOutput(outputTensors[i], i); + } + + return outputTensors; +} + +Ort::Value OnnxModel::evalModelLast(std::vector& input) +{ + checkInput(input); + if (mOutputNamesChar.empty()) { + LOG(fatal) << "Model has no outputs"; + } + Ort::Value output{nullptr}; + try { + // A null RunOptions uses the runtime defaults without allocating options per call. + const Ort::RunOptions runOptions{nullptr}; + mSession->Run(runOptions, mInputNamesChar.data(), input.data(), input.size(), &mOutputNamesChar.back(), &output, 1); + } catch (const Ort::Exception& exception) { + LOG(fatal) << "Error running model inference: " << exception.what(); + } + checkOutput(output, mOutputShapes.size() - 1); + return output; +} + +void OnnxModel::checkOutput(const Ort::Value& tensor, const std::size_t index) const +{ + const std::vector shape = tensor.GetTensorTypeAndShapeInfo().GetShape(); + LOG(debug) << "Output tensor " << index << " shape: " << printShape(shape); + bool shapeOk = (shape.size() == mOutputShapes[index].size()); + for (std::size_t idim = 0; shapeOk && idim < shape.size(); idim++) { + // Dynamic dimensions of the model (< 0) can take any value. + shapeOk = (mOutputShapes[index][idim] < 0) || (shape[idim] == mOutputShapes[index][idim]); + } + if (!shapeOk) { + LOG(fatal) << "Shape of output tensor " << index << " does not agree with model specification! Output: " << printShape(shape) << " model: " << printShape(mOutputShapes[index]); + } +} + void OnnxModel::setActiveThreads(const int threads) { activeThreads = threads; diff --git a/Tools/ML/model.h b/Tools/ML/model.h index 3be08e72fa9..d94491f8c38 100644 --- a/Tools/ML/model.h +++ b/Tools/ML/model.h @@ -22,14 +22,10 @@ #include -#include #include -#include -#include #include #include -#include #include #include #include @@ -40,90 +36,112 @@ namespace o2 namespace ml { +/// \brief Thin wrapper around an ONNX Runtime session (CPU only) +/// +/// Inference entry points: +/// - evalModelRaw(): runs the session and returns the owning output tensors (one Ort::Value per model output). +/// This is the zero-copy path: the caller owns the tensors and may read them via GetTensorData() +/// for as long as the returned vector is alive. +/// - evalModel(input): convenience wrapper returning a copy of the *last* model output as std::vector. +/// - evalModel(input, output): same as above but writes into a caller-provided vector. The vector is +/// resized as needed and its capacity is reused across calls, which avoids repeated allocations of the result buffer. +/// Only the last model output is requested from ONNX Runtime. +/// +/// Inputs given as std::vector are wrapped in an Ort::Value without copying. The input vector must therefore +/// stay alive until the call returns (which is always the case for the synchronous calls provided here). class OnnxModel { public: OnnxModel() = default; ~OnnxModel() = default; + // The ONNX session options are not copyable, so the model is move-only + OnnxModel(const OnnxModel&) = delete; + OnnxModel& operator=(const OnnxModel&) = delete; + OnnxModel(OnnxModel&&) = default; + OnnxModel& operator=(OnnxModel&&) = default; // Inferencing void initModel(const std::string&, const bool = false, const int = 0, const uint64_t = 0, const uint64_t = 0); - // template methods -- best to define them in header + /// Run the model on already prepared input tensors + /// \param input one Ort::Value per model input + /// \return the output tensors of the model (owning). Access to the data via output[i].GetTensorData() + std::vector evalModelRaw(std::vector& input); + + /// Run the model and copy the last output tensor into a vector + /// \param input one Ort::Value per model input + /// \return flattened content of the last output tensor template - T* evalModel(std::vector& input) + std::vector evalModel(std::vector& input) { - LOG(debug) << "Input tensor shape: " << printShape(input[0].GetTensorTypeAndShapeInfo().GetShape()); - // assert(input[0].GetTensorTypeAndShapeInfo().GetShape() == getNumInputNodes()); --> Fails build in debug mode, TODO: assertion should be checked somehow - - try { - const Ort::RunOptions runOptions; - std::vector inputNamesChar(mInputNames.size(), nullptr); - std::transform(std::begin(mInputNames), std::end(mInputNames), std::begin(inputNamesChar), - [&](const std::string& str) { return str.c_str(); }); - - std::vector outputNamesChar(mOutputNames.size(), nullptr); - std::transform(std::begin(mOutputNames), std::end(mOutputNames), std::begin(outputNamesChar), - [&](const std::string& str) { return str.c_str(); }); - auto outputTensors = mSession->Run(runOptions, inputNamesChar.data(), input.data(), input.size(), outputNamesChar.data(), outputNamesChar.size()); - LOG(debug) << "Number of output tensors: " << outputTensors.size(); - if (outputTensors.size() != mOutputNames.size()) { - LOG(fatal) << "Number of output tensors: " << outputTensors.size() << " does not agree with the model specified size: " << mOutputNames.size(); - } - for (std::size_t i = 0; i < outputTensors.size(); i++) { - LOG(debug) << "Output tensor shape: " << printShape(outputTensors[i].GetTensorTypeAndShapeInfo().GetShape()); - if ((outputTensors[i].GetTensorTypeAndShapeInfo().GetShape() != mOutputShapes[i]) && (mOutputShapes[i][0] != -1)) { - LOG(fatal) << "Shape of tensor " << i << " does not agree with model specification! Output: " << printShape(outputTensors[i].GetTensorTypeAndShapeInfo().GetShape()) << " model: " << printShape(mOutputShapes[i]); - } - } - T* outputValues = outputTensors.back().GetTensorMutableData(); - return outputValues; - } catch (const Ort::Exception& exception) { - LOG(error) << "Error running model inference: " << exception.what(); - } - return nullptr; + std::vector output; + evalModel(input, output); + return output; } + /// Run the model and copy the last output tensor into the provided vector (allocation is reused between calls) + /// \param input one Ort::Value per model input + /// \param output vector which is filled with the flattened content of the last output tensor template - T* evalModel(std::vector& input) + void evalModel(std::vector& input, std::vector& output) { - const int64_t size = input.size(); - assert(size % mInputShapes[0][1] == 0); - std::vector inputShape{size / mInputShapes[0][1], mInputShapes[0][1]}; - std::vector inputTensors; - Ort::MemoryInfo memInfo = - Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); - inputTensors.emplace_back(Ort::Value::CreateTensor(memInfo, input.data(), size, inputShape.data(), inputShape.size())); - LOG(debug) << "Input shape calculated from vector: " << printShape(inputShape); - return evalModel(inputTensors); + const Ort::Value tensor = evalModelLast(input); + copyOutput(tensor, output); } - // For 2D inputs + /// Run a single-input model on a flat vector of features (batches are inferred from the model input shape) + /// \param input flattened input features, size must be a multiple of the number of input nodes + /// \return flattened content of the last output tensor template - T* evalModel(std::vector>& input) + std::vector evalModel(std::vector& input) { + std::vector output; + evalModel(input, output); + return output; + } + + /// Run a single-input model on a flat vector of features (batches are inferred from the model input shape) + /// \param input flattened input features, size must be a multiple of the number of input nodes + /// \param output vector which is filled with the flattened content of the last output tensor + template + void evalModel(std::vector& input, std::vector& output) + { + if (mInputShapes.size() != 1) { + LOG(fatal) << "Model has " << mInputShapes.size() << " inputs but a single input vector was provided. Use the std::vector> or std::vector overload."; + } std::vector inputTensors; + inputTensors.reserve(1); + addInputTensor(inputTensors, input, 0); + evalModel(inputTensors, output); + } - Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtAllocatorType::OrtArenaAllocator, OrtMemType::OrtMemTypeDefault); + /// Run a multi-input model: one flat vector of features per model input + /// \param input one flattened vector per model input + /// \return flattened content of the last output tensor + template + std::vector evalModel(std::vector>& input) + { + std::vector output; + evalModel(input, output); + return output; + } + /// Run a multi-input model: one flat vector of features per model input + /// \param input one flattened vector per model input + /// \param output vector which is filled with the flattened content of the last output tensor + template + void evalModel(std::vector>& input, std::vector& output) + { + if (input.size() != mInputShapes.size()) { + LOG(fatal) << "Model has " << mInputShapes.size() << " inputs but " << input.size() << " input vectors were provided."; + } + std::vector inputTensors; + inputTensors.reserve(input.size()); for (std::size_t iinput = 0; iinput < input.size(); iinput++) { - [[maybe_unused]] int totalSize = 1; - int64_t size = input[iinput].size(); - for (std::size_t idim = 1; idim < mInputShapes[iinput].size(); idim++) { - totalSize *= mInputShapes[iinput][idim]; - } - assert(size % totalSize == 0); - - std::vector inputShape{static_cast(size / totalSize)}; - for (std::size_t idim = 1; idim < mInputShapes[iinput].size(); idim++) { - inputShape.push_back(mInputShapes[iinput][idim]); - } - - inputTensors.emplace_back(Ort::Value::CreateTensor(memInfo, input[iinput].data(), size, inputShape.data(), inputShape.size())); + addInputTensor(inputTensors, input[iinput], iinput); } - - return evalModel(inputTensors); + evalModel(inputTensors, output); } // Reset session @@ -141,6 +159,7 @@ class OnnxModel int getNumInputNodes() const { return mInputShapes[0][1]; } std::vector> getInputShapes() const { return mInputShapes; } int getNumOutputNodes() const { return mOutputShapes[0][1]; } + std::vector> getOutputShapes() const { return mOutputShapes; } uint64_t getValidityFrom() const { return validFrom; } uint64_t getValidityUntil() const { return validUntil; } void setActiveThreads(const int); @@ -150,6 +169,7 @@ class OnnxModel std::shared_ptr mEnv = nullptr; std::shared_ptr mSession = nullptr; Ort::SessionOptions sessionOptions; + Ort::MemoryInfo mMemInfo{nullptr}; // CPU memory info, created once in initModel // Input & Output specifications of the loaded network std::vector mInputNames; @@ -157,14 +177,54 @@ class OnnxModel std::vector mOutputNames; std::vector> mOutputShapes; + // Pointers into the name vectors above, populated only after all names are loaded. + // Moving the model transfers the backing vectors without invalidating these pointers. + std::vector mInputNamesChar; + std::vector mOutputNamesChar; + // Environment settings std::string modelPath; int activeThreads = 0; uint64_t validFrom = 0; uint64_t validUntil = 0; + /// Derive the tensor shape for input number iinput from the number of provided values + /// The first dimension is treated as batch dimension; all other dimensions are taken from the model + std::vector inferInputShape(const std::size_t iinput, const int64_t size) const; + + /// Wrap a flat data vector (no copy) into an Ort::Value and append it to tensors + template + void addInputTensor(std::vector& tensors, std::vector& data, const std::size_t iinput) const + { + const std::vector inputShape = inferInputShape(iinput, static_cast(data.size())); + LOG(debug) << "Input shape calculated from vector: " << printShape(inputShape); + tensors.emplace_back(Ort::Value::CreateTensor(mMemInfo, data.data(), data.size(), inputShape.data(), inputShape.size())); + } + + /// Request only the last output, keeping its tensor alive until the caller has copied it + Ort::Value evalModelLast(std::vector& input); + void checkInput(const std::vector& input) const; + void checkOutput(const Ort::Value& tensor, std::size_t index) const; + + /// Copy the content of an output tensor into output (reusing its allocation) + template + void copyOutput(const Ort::Value& tensor, std::vector& output) const + { + const auto info = tensor.GetTensorTypeAndShapeInfo(); + if (info.GetElementType() != Ort::TypeToTensorType::type) { + LOG(fatal) << "Requested output type (ONNX type id " << static_cast(Ort::TypeToTensorType::type) << ") does not match the model output tensor type (ONNX type id " << static_cast(info.GetElementType()) << ")"; + } + const T* data = tensor.GetTensorData(); + const auto size = info.GetElementCount(); + if (size == 0) { + output.clear(); + } else { + output.assign(data, data + size); + } + } + // Internal function for printing the shape of tensors - std::string printShape(const std::vector&); + static std::string printShape(const std::vector&); bool checkHyperloop(const bool = true); }; diff --git a/Tools/PIDFeatureExtractor/CMakeLists.txt b/Tools/PIDFeatureExtractor/CMakeLists.txt new file mode 100644 index 00000000000..b163d0e9293 --- /dev/null +++ b/Tools/PIDFeatureExtractor/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright 2019-2020 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. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2physics_add_dpl_workflow(pid-feature-extractor + SOURCES pidFeatureExtractor.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + +o2physics_add_dpl_workflow(pid-onnx-inference + SOURCES pidOnnxInference.cxx + PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore + COMPONENT_NAME Analysis) diff --git a/Tools/PIDFeatureExtractor/README.md b/Tools/PIDFeatureExtractor/README.md new file mode 100644 index 00000000000..c0773c63fd4 --- /dev/null +++ b/Tools/PIDFeatureExtractor/README.md @@ -0,0 +1,136 @@ +# PID Feature Extractor + ONNX Inference + +This provides particle identification for ALICE Run 3 Pb-Pb analyses using +a trained ML model (a detector-aware attention model conditioned on which +detectors each track actually has hits in - TPC, TOF, TRD, ITS, EMCal, +HMPID, plus event centrality). Two tasks: + +- **`pidFeatureExtractor.cxx`** reads AO2D data and writes out the model's + input features - kinematics, per-detector PID signals, and detector + presence flags - to a ROOT file (and optionally CSV). +- **`pidOnnxInference.cxx`** takes that file, runs the trained ONNX model + over it, and writes back a probability for each particle species + (pion / kaon / proton / electron) per track. + +You run the extractor first, then inference on its output. Both are +regular AOD-subscribing tasks; `PidOnnxInference` does its real work by +reading the extractor's output file directly rather than the AOD data it's +subscribed to (see "Running" below for what that means in practice). + +## PidFeatureExtractor + +An ordinary AOD-subscribing analysis task. It reads track and collision +data and, for each track passing the (optional, off by default) quality +cuts, writes one row containing: + +- kinematics (momentum, eta, phi, DCA) +- per-detector signals for TPC, TOF, TRD, ITS, EMCal, and HMPID, each with + a flag saying whether that detector actually has a hit on this track +- event centrality +- a Bayesian PID posterior, for comparison against the ML model +- for MC only: the true particle ID and whether it's a physical primary + +Mode is a runtime switch - enable `processData` for real data or +`processMc` for MC (reconstructed + truth), not both. + +### PidFeatureExtractor options + +| Option | Default | What it does | +|------------------------|------------------|------------------------------------------------------------------| +| `outputPath` | `pid_features` | Output file base name | +| `exportROOT` | `true` | Write a ROOT file | +| `exportCsv` | `false` | Also write CSV | +| `etaMin` / `etaMax` | `-99` / `99` | Eta cut - wide open by default (no cut) | +| `ptMin` / `ptMax` | `0` / `9999` | pT cut, GeV/c - wide open by default | +| `dcaXYMax` / `dcaZMax` | `9999` / `9999` | DCA cuts, cm - wide open by default | +| `itsMinClusters` | `0` | Minimum ITS clusters - `0` = no cut | +| `tpcMinClusters` | `0` | Minimum TPC clusters - `0` = no cut | +| `computeBayesianPid` | `true` | Compute the comparison Bayesian posterior | +| `bayesianPriors` | flat (`1,1,1,1`) | Per-species priors `[pi, ka, pr, el]` for the Bayesian posterior | + +All the cuts default to "off" - tighten them in your config if you want +quality selection applied here rather than downstream. + +## PidOnnxInference + +Takes the file `PidFeatureExtractor` wrote and runs the trained ONNX model +over it, row by row, in `init()` - not per-collision. The model can be +loaded either from CCDB or from a local file, which is handled by +`o2::analysis::MlResponse` (`Tools/ML/MlResponse.h`). + +This is still a normal AOD-subscribing task, so it needs a valid AO2D +file to run at all, the same as any other task in this repository - but +it doesn't actually use that data; `process()` is intentionally empty. +Point it at any valid AO2D (the same one you ran the extractor against is +the obvious choice) purely to satisfy the pipeline. + +By default it assumes every detector group is present and usable, exactly +as the input data says. If you want to see how the model behaves with a +detector deliberately left out - for testing, or to match a specific +detector configuration - each group can be switched off independently; +turning one off overrides the data for that group, the same way a genuine +detector miss would look. + +### PidOnnxInference options + +| Option | Default | What it does | +|---------------------|-----------------------------|------------------------------------------------------------------------------| +| `inputRootFile` | `pid_features_data.root` | File written by `PidFeatureExtractor` | +| `inputTreeName` | `pid_features` | Tree name inside it | +| `outputPath` | `pid_predictions` | Output file base name | +| `exportCsv` | `false` | Also write CSV | +| `loadModelFromCcdb` | `true` | Load the model from CCDB; set `false` to use a local file instead | +| `ccdbUrl` | `http://alice-ccdb.cern.ch` | | +| `modelPathsCcdb` | *(placeholder)* | CCDB path to your model - set this to a real path before running | +| `timestampCcdb` | `-1` | `-1` = latest | +| `onnxFileNames` | `pid_feature_model.onnx` | Local model file, used when `loadModelFromCcdb` is `false` | +| `useTPC` | `true` | Include TPC. Set `false` to exclude it from inference regardless of the data | +| `useTOF` | `true` | Include TOF | +| `useTRD` | `true` | Include TRD | +| `useITS` | `true` | Include ITS | +| `useEMCal` | `true` | Include EMCal | +| `useHMPID` | `true` | Include HMPID | +| `useCentrality` | `true` | Include event centrality | + +Output columns are `mlProbPi`, `mlProbKa`, `mlProbPr`, `mlProbEl` (one +probability per species) and `mlPredictedClass` (the most likely species, +as an index: `0`=pion, `1`=kaon, `2`=proton, `3`=electron). + +## Running + +Both use the usual `--configuration json://your-config.json` mechanism, +and both are AOD-subscribing tasks - `PidOnnxInference` just doesn't use +the AOD data it's given, it reads `PidFeatureExtractor`'s output file +instead. Run the extractor first: + +```bash +#!/bin/bash + +config_file="my-config.json" + +o2-analysis-timestamp --configuration json://$config_file -b | + o2-analysis-event-selection --configuration json://$config_file -b | + o2-analysis-track-propagation --configuration json://$config_file -b | + o2-analysis-trackselection --configuration json://$config_file -b | + o2-analysis-pid-tpc-base --configuration json://$config_file -b | + o2-analysis-pid-tpc --configuration json://$config_file -b | + o2-analysis-pid-tof-base --configuration json://$config_file -b | + o2-analysis-pid-tof --configuration json://$config_file -b | + o2-analysis-pid-tof-beta --configuration json://$config_file -b | + o2-analysis-multiplicity-table --configuration json://$config_file -b | + o2-analysis-centrality-table --configuration json://$config_file -b | + o2-analysis-pid-feature-extractor --configuration json://$config_file -b +``` + +Then run inference, once the extractor has finished and its output file +exists. Any valid AO2D works as input here, since its content is unused - +reusing the same one is the simplest choice: + +```bash +#!/bin/bash + +config_file="my-config.json" + +o2-analysis-timestamp --configuration json://$config_file -b | + o2-analysis-pid-onnx-inference --configuration json://$config_file -b +``` diff --git a/Tools/PIDFeatureExtractor/pidFeatureExtractor.cxx b/Tools/PIDFeatureExtractor/pidFeatureExtractor.cxx new file mode 100644 index 00000000000..6e8625e0cc8 --- /dev/null +++ b/Tools/PIDFeatureExtractor/pidFeatureExtractor.cxx @@ -0,0 +1,499 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pidFeatureExtractor.cxx +/// \brief Produce flat, ML-ready PID feature files (ROOT TTree and/or CSV) +/// from ALICE Run 3 Pb-Pb AO2D data, for both MC (reconstructed + +/// truth) and real/raw data. +/// +/// Output is written via manual TFile/TTree/ofstream rather than +/// O2's DECLARE_SOA_TABLE/Produces<> table mechanism. That's a +/// deliberate reversion: two Produces<> tables sharing a column +/// prefix in one struct triggered a reproducible framework-level +/// compile failure (ASoA.h/MetadataTrait constraint-satisfaction +/// errors, and a StructToTuple reflection failure) against this O2 +/// build, independent of table description tag naming. This +/// TFile/TTree approach is the same pattern the original two-file +/// (MC/RAW) version of this task used successfully. +/// +/// \author Robert Forynski + +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/PIDResponseTOF.h" +#include "Common/DataModel/PIDResponseTPC.h" +#include "Common/DataModel/TrackSelectionTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::framework; +using namespace o2::framework::expressions; + +namespace +{ +constexpr float kNaN = std::numeric_limits::quiet_NaN(); +constexpr int kNumItsLayers = 7; +constexpr int kBitsPerItsLayer = 4; +constexpr uint32_t kItsLayerMask = 0xF; +constexpr int kNumSpecies = 4; // pi, ka, pr, el +constexpr float kEmcalEtaOutOfAcceptance = -900.f; + +/// Detector-presence helpers, local to this project. +template +bool tofMissing(T const& track) +{ + return !track.hasTOF(); +} + +template +bool trdMissing(T const& track) +{ + return !track.hasTRD(); +} + +template +float getTofMass(T const& track) +{ + return tofMissing(track) ? kNaN : track.mass(); +} + +/// itsClusterSizes packs 7 ITS layers into 4 bits each (cluster size per +/// layer, 0 = no hit on that layer). Number of ITS clusters is the count of +/// non-zero nibbles, not the raw column value itself. +template +int getItsNClusters(T const& track) +{ + auto v = static_cast(track.itsClusterSizes()); + int n = 0; + for (int layer = 0; layer < kNumItsLayers; layer++) { + if ((v >> (layer * kBitsPerItsLayer)) & kItsLayerMask) { + n++; + } + } + return n; +} +} // namespace + +/// PidFeatureExtractor: flat PID feature file (ROOT TTree and/or CSV) for +/// ML training/inference. +/// +/// Mode (MC vs. real data) is a runtime PROCESS_SWITCH choice, so one +/// executable serves both use cases. +/// +/// - DPG track cuts (eta/pT/DCA/TPC-cluster/ITS-cluster) are optional and off +/// by default (wide-open ranges) - tighten them in the config if you want +/// them applied here instead of in Python post-processing. +/// - Bayesian PID combination is optional (computeBayesianPid, default +/// true) and configurable priors (bayesianPriors, default flat). Valid +/// whenever TPC is present; TOF is folded in too if also present, but is +/// not required - a TPC-only track still gets a real posterior, not NaN. +/// - Output: ROOT TTree (exportROOT, default true) and/or CSV (exportCsv, +/// default false), written via a single row of member variables bound as +/// TTree branches - not an O2 AOD table. +struct PidFeatureExtractor { + std::unique_ptr outputFile; + std::unique_ptr featureTree; + std::ofstream csvFile; + + // --- output row (bound as TTree branches; also used to build CSV rows) --- + float p = 0, pt = 0, px = 0, py = 0, pz = 0, eta = 0, phi = 0, sign = 0; + int trackType = 0; + float vz = 0, centFT0C = 0, dcaXY = 0, dcaZ = 0; + bool hasTpc = false; + float tpcSignal = 0, tpcNSigmaPi = 0, tpcNSigmaKa = 0, tpcNSigmaPr = 0, tpcNSigmaEl = 0; + int tpcNClsFound = 0; + float tpcChi2NCl = 0; + bool hasTof = false; + float tofMass = 0, beta = 0, tofNSigmaPi = 0, tofNSigmaKa = 0, tofNSigmaPr = 0, tofNSigmaEl = 0; + bool hasTrd = false; + float trdSignal = 0, trdChi2 = 0; + int trdPattern = 0; + int itsClusterSizes = 0; + float itsChi2NCl = 0; + bool hasEmcal = false; + float trackEtaEmcal = 0, trackPhiEmcal = 0; + bool hasHmpid = false; + float hmpidSignal = 0, hmpidQMip = 0; + int hmpidNPhotons = 0, hmpidClusSize = 0; + float hmpidMom = 0; + float bayesProbPi = 0, bayesProbKa = 0, bayesProbPr = 0, bayesProbEl = 0; + int mcPdg = 0; + uint8_t mcIsPhysicalPrimary = 0; + + HistogramRegistry histos{"histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + Filter trackFilter = requireGlobalTrackInFilter(); + + // --- output configuration -------------------------------------------------- + Configurable outputPath{"outputPath", "pid_features", "Output file base name (no extension)"}; + Configurable exportROOT{"exportROOT", true, "Write a ROOT TTree"}; + Configurable exportCsv{"exportCsv", false, "Also write a CSV, alongside the ROOT output"}; + + // --- DPG cuts: wide-open by default, i.e. effectively disabled ----------- + Configurable etaMin{"etaMin", -99.f, "Minimum track eta (DPG cut; wide-open = disabled)"}; + Configurable etaMax{"etaMax", 99.f, "Maximum track eta (DPG cut; wide-open = disabled)"}; + Configurable ptMin{"ptMin", 0.f, "Minimum track pT, GeV/c (DPG cut; wide-open = disabled)"}; + Configurable ptMax{"ptMax", 9999.f, "Maximum track pT, GeV/c (DPG cut; wide-open = disabled)"}; + Configurable dcaXYMax{"dcaXYMax", 9999.f, "Maximum |DCAxy|, cm (DPG cut; wide-open = disabled)"}; + Configurable dcaZMax{"dcaZMax", 9999.f, "Maximum |DCAz|, cm (DPG cut; wide-open = disabled)"}; + Configurable itsMinClusters{"itsMinClusters", 0, "Minimum number of ITS clusters (DPG cut; 0 = disabled)"}; + Configurable tpcMinClusters{"tpcMinClusters", 0, "Minimum TPC clusters (DPG cut; 0 = disabled)"}; + + // --- Bayesian PID ---------------------------------------------------------- + Configurable computeBayesianPid{"computeBayesianPid", true, "Compute Bayesian PID posteriors (else NaN)"}; + Configurable> bayesianPriors{"bayesianPriors", std::vector{1.f, 1.f, 1.f, 1.f}, "Priors [pi,ka,pr,el]; default flat"}; + + using PidTracks = soa::Filtered>; + + using PidTracksMc = soa::Filtered>; + + using PidCollision = soa::Join::iterator; + + void init(InitContext const&) + { + std::string base = outputPath.value; + + if (exportROOT.value) { + outputFile = std::make_unique((base + ".root").c_str(), "RECREATE"); + featureTree = std::make_unique("pid_features", "PID features"); + + featureTree->Branch("p", &p); + featureTree->Branch("pt", &pt); + featureTree->Branch("px", &px); + featureTree->Branch("py", &py); + featureTree->Branch("pz", &pz); + featureTree->Branch("eta", &eta); + featureTree->Branch("phi", &phi); + featureTree->Branch("sign", &sign); + featureTree->Branch("trackType", &trackType); + featureTree->Branch("vz", &vz); + featureTree->Branch("centFT0C", ¢FT0C); + featureTree->Branch("dcaXY", &dcaXY); + featureTree->Branch("dcaZ", &dcaZ); + featureTree->Branch("hasTPC", &hasTpc); + featureTree->Branch("tpcSignal", &tpcSignal); + featureTree->Branch("tpcNSigmaPi", &tpcNSigmaPi); + featureTree->Branch("tpcNSigmaKa", &tpcNSigmaKa); + featureTree->Branch("tpcNSigmaPr", &tpcNSigmaPr); + featureTree->Branch("tpcNSigmaEl", &tpcNSigmaEl); + featureTree->Branch("tpcNClsFound", &tpcNClsFound); + featureTree->Branch("tpcChi2NCl", &tpcChi2NCl); + featureTree->Branch("hasTOF", &hasTof); + featureTree->Branch("tofMass", &tofMass); + featureTree->Branch("beta", &beta); + featureTree->Branch("tofNSigmaPi", &tofNSigmaPi); + featureTree->Branch("tofNSigmaKa", &tofNSigmaKa); + featureTree->Branch("tofNSigmaPr", &tofNSigmaPr); + featureTree->Branch("tofNSigmaEl", &tofNSigmaEl); + featureTree->Branch("hasTRD", &hasTrd); + featureTree->Branch("trdSignal", &trdSignal); + featureTree->Branch("trdChi2", &trdChi2); + featureTree->Branch("trdPattern", &trdPattern); + featureTree->Branch("itsClusterSizes", &itsClusterSizes); + featureTree->Branch("itsChi2NCl", &itsChi2NCl); + featureTree->Branch("hasEMCal", &hasEmcal); + featureTree->Branch("trackEtaEmcal", &trackEtaEmcal); + featureTree->Branch("trackPhiEmcal", &trackPhiEmcal); + featureTree->Branch("hasHMPID", &hasHmpid); + featureTree->Branch("hmpidSignal", &hmpidSignal); + featureTree->Branch("hmpidQMip", &hmpidQMip); + featureTree->Branch("hmpidNPhotons", &hmpidNPhotons); + featureTree->Branch("hmpidClusSize", &hmpidClusSize); + featureTree->Branch("hmpidMom", &hmpidMom); + featureTree->Branch("bayesProbPi", &bayesProbPi); + featureTree->Branch("bayesProbKa", &bayesProbKa); + featureTree->Branch("bayesProbPr", &bayesProbPr); + featureTree->Branch("bayesProbEl", &bayesProbEl); + if (doprocessMc) { + featureTree->Branch("mcPdg", &mcPdg); + featureTree->Branch("mcIsPhysicalPrimary", &mcIsPhysicalPrimary); + } + } + + if (exportCsv.value) { + csvFile.open(base + (doprocessMc ? "_mc.csv" : "_data.csv")); + csvFile << "p,pt,px,py,pz,eta,phi,sign,trackType," + "vz,centFT0C,dcaXY,dcaZ," + "hasTPC,tpcSignal,tpcNSigmaPi,tpcNSigmaKa,tpcNSigmaPr,tpcNSigmaEl,tpcNClsFound,tpcChi2NCl," + "hasTOF,tofMass,beta,tofNSigmaPi,tofNSigmaKa,tofNSigmaPr,tofNSigmaEl," + "hasTRD,trdSignal,trdChi2,trdPattern," + "itsClusterSizes,itsChi2NCl," + "hasEMCal,trackEtaEmcal,trackPhiEmcal," + "hasHMPID,hmpidSignal,hmpidQMip,hmpidNPhotons,hmpidClusSize,hmpidMom," + "bayesProbPi,bayesProbKa,bayesProbPr,bayesProbEl"; + if (doprocessMc) { + csvFile << ",mcPdg,mcIsPhysicalPrimary"; + } + csvFile << "\n"; + } + + const AxisSpec axisPt{200, 0, 10, "pT"}; + const AxisSpec axisEta{60, -1.5, 1.5, "eta"}; + const AxisSpec axisdEdx{300, 0, 300, "dE/dx"}; + const AxisSpec axisBeta{120, 0, 1.2, "beta"}; + const AxisSpec axisMass{100, -0.2, 2.0, "mass"}; + histos.add("QC/nTracks", "Tracks", kTH1F, {{10000, 0, 100000}}); + histos.add("QC/pt", "pT", kTH1F, {axisPt}); + histos.add("QC/eta", "eta", kTH1F, {axisEta}); + histos.add("QC/tpcDedxVsPt", "dE/dx vs pT", kTH2F, {axisPt, axisdEdx}); + histos.add("QC/tofBetaVsP", "beta vs p", kTH2F, {axisPt, axisBeta}); + histos.add("QC/massVsP", "mass vs p", kTH2F, {axisPt, axisMass}); + } + + /// DPG-style track quality cuts. Wide-open defaults mean this is a no-op + /// unless the config tightens them. + template + bool passesDpgCuts(TTrack const& track) const + { + if (track.pt() < ptMin.value || track.pt() > ptMax.value) + return false; + if (track.eta() < etaMin.value || track.eta() > etaMax.value) + return false; + if (std::abs(track.dcaXY()) > dcaXYMax.value) + return false; + if (std::abs(track.dcaZ()) > dcaZMax.value) + return false; + if (track.tpcNClsFound() < tpcMinClusters.value) + return false; + if (getItsNClusters(track) < itsMinClusters.value) + return false; + return true; + } + + /// Bayesian PID: requires TPC (a TPC-only track still gets a real + /// posterior); folds in TOF too when also present. NaN in all four + /// outputs if TPC is absent or computeBayesianPid is false. + void computeBayesianProbs(bool hasTpcIn, const float nsTPC[4], bool hasTofIn, const float nsTOF[4], float out[4]) const + { + if (!computeBayesianPid.value || !hasTpcIn) { + out[0] = out[1] = out[2] = out[3] = kNaN; + return; + } + auto const& priors = bayesianPriors.value; + float sum = 0.f; + for (int i = 0; i < kNumSpecies; i++) { + float logL = -0.5f * nsTPC[i] * nsTPC[i]; + if (hasTofIn) { + logL += -0.5f * nsTOF[i] * nsTOF[i]; + } + out[i] = std::exp(logL) * priors[i]; + sum += out[i]; + } + for (int i = 0; i < kNumSpecies; i++) { + out[i] = sum > 0.f ? out[i] / sum : 0.25f; + } + } + + /// HMPID is sparse (~0.1% of tracks matched) and linked by track global + /// index rather than joinable 1:1, so it's looked up once per collision + /// instead of per track. + static std::unordered_map buildHmpidMap(aod::HMPIDs const& hmpids) + { + std::unordered_map map; + for (auto h = hmpids.begin(); h != hmpids.end(); ++h) { + map[h.trackId()] = h; + } + return map; + } + + /// Fills the member "output row" for one track. Identical for MC and + /// data - the only thing that differs between the two modes is whether + /// mcPdg/mcIsPhysicalPrimary get set afterwards. + template + void fillRow(TTrack const& track, float vzIn, float centFT0CIn, + std::unordered_map const& hmpidMap) + { + p = track.p(); + pt = track.pt(); + px = track.px(); + py = track.py(); + pz = track.pz(); + eta = track.eta(); + phi = track.phi(); + sign = static_cast(track.sign()); + trackType = track.trackType(); + vz = vzIn; + centFT0C = centFT0CIn; + dcaXY = track.dcaXY(); + dcaZ = track.dcaZ(); + + hasTpc = track.hasTPC(); + tpcSignal = track.tpcSignal(); + tpcNSigmaPi = track.tpcNSigmaPi(); + tpcNSigmaKa = track.tpcNSigmaKa(); + tpcNSigmaPr = track.tpcNSigmaPr(); + tpcNSigmaEl = track.tpcNSigmaEl(); + tpcNClsFound = track.tpcNClsFound(); + tpcChi2NCl = track.tpcChi2NCl(); + + hasTof = !tofMissing(track); + tofMass = getTofMass(track); + beta = track.beta(); + tofNSigmaPi = track.tofNSigmaPi(); + tofNSigmaKa = track.tofNSigmaKa(); + tofNSigmaPr = track.tofNSigmaPr(); + tofNSigmaEl = track.tofNSigmaEl(); + + hasTrd = !trdMissing(track); + trdSignal = track.trdSignal(); + trdChi2 = track.trdChi2(); + trdPattern = track.trdPattern(); + + itsClusterSizes = track.itsClusterSizes(); + itsChi2NCl = track.itsChi2NCl(); + + hasEmcal = track.trackEtaEmcal() > kEmcalEtaOutOfAcceptance; + trackEtaEmcal = track.trackEtaEmcal(); + trackPhiEmcal = track.trackPhiEmcal(); + + hasHmpid = false; + hmpidSignal = kNaN; + hmpidQMip = kNaN; + hmpidNPhotons = 0; + hmpidClusSize = 0; + hmpidMom = kNaN; + if (auto it = hmpidMap.find(track.globalIndex()); it != hmpidMap.end()) { + hasHmpid = true; + hmpidSignal = it->second.hmpidSignal(); + hmpidQMip = it->second.hmpidQMip(); + hmpidNPhotons = it->second.hmpidNPhotons(); + hmpidClusSize = it->second.hmpidClusSize(); + hmpidMom = it->second.hmpidMom(); + } + + float nsTPC[4] = {tpcNSigmaPi, tpcNSigmaKa, tpcNSigmaPr, tpcNSigmaEl}; + float nsTOF[4] = {tofNSigmaPi, tofNSigmaKa, tofNSigmaPr, tofNSigmaEl}; + float bayes[4]; + computeBayesianProbs(hasTpc, nsTPC, hasTof, nsTOF, bayes); + bayesProbPi = bayes[0]; + bayesProbKa = bayes[1]; + bayesProbPr = bayes[2]; + bayesProbEl = bayes[3]; + } + + void fillOutputs() + { + if (exportROOT.value) { + featureTree->Fill(); + } + if (exportCsv.value) { + csvFile << p << ',' << pt << ',' << px << ',' << py << ',' << pz << ',' + << eta << ',' << phi << ',' << sign << ',' << trackType << ',' + << vz << ',' << centFT0C << ',' << dcaXY << ',' << dcaZ << ',' + << hasTpc << ',' << tpcSignal << ',' << tpcNSigmaPi << ',' << tpcNSigmaKa << ',' + << tpcNSigmaPr << ',' << tpcNSigmaEl << ',' << tpcNClsFound << ',' << tpcChi2NCl << ',' + << hasTof << ',' << tofMass << ',' << beta << ',' << tofNSigmaPi << ',' + << tofNSigmaKa << ',' << tofNSigmaPr << ',' << tofNSigmaEl << ',' + << hasTrd << ',' << trdSignal << ',' << trdChi2 << ',' << trdPattern << ',' + << itsClusterSizes << ',' << itsChi2NCl << ',' + << hasEmcal << ',' << trackEtaEmcal << ',' << trackPhiEmcal << ',' + << hasHmpid << ',' << hmpidSignal << ',' << hmpidQMip << ',' + << hmpidNPhotons << ',' << hmpidClusSize << ',' << hmpidMom << ',' + << bayesProbPi << ',' << bayesProbKa << ',' << bayesProbPr << ',' << bayesProbEl; + if (doprocessMc) { + csvFile << ',' << mcPdg << ',' << static_cast(mcIsPhysicalPrimary); + } + csvFile << '\n'; + } + } + + void fillQcHistos() + { + histos.fill(HIST("QC/nTracks"), 1); + histos.fill(HIST("QC/pt"), pt); + histos.fill(HIST("QC/eta"), eta); + if (hasTpc) { + histos.fill(HIST("QC/tpcDedxVsPt"), pt, tpcSignal); + } + if (hasTof) { + histos.fill(HIST("QC/tofBetaVsP"), p, beta); + histos.fill(HIST("QC/massVsP"), p, tofMass); + } + } + + void processData(PidCollision const& collision, PidTracks const& tracks, aod::HMPIDs const& hmpids) + { + auto hmpidMap = buildHmpidMap(hmpids); + for (auto const& track : tracks) { + if (!passesDpgCuts(track)) { + continue; + } + fillRow(track, collision.posZ(), collision.centFT0C(), hmpidMap); + fillOutputs(); + fillQcHistos(); + } + } + PROCESS_SWITCH(PidFeatureExtractor, processData, "Produce PID features for real/raw data (no MC truth)", true); + + void processMc(PidCollision const& collision, PidTracksMc const& tracks, aod::McParticles const&, aod::HMPIDs const& hmpids) + { + auto hmpidMap = buildHmpidMap(hmpids); + for (auto const& track : tracks) { + if (!passesDpgCuts(track)) { + continue; + } + if (!track.has_mcParticle()) { + continue; + } + auto mcParticle = track.mcParticle(); + fillRow(track, collision.posZ(), collision.centFT0C(), hmpidMap); + mcPdg = mcParticle.pdgCode(); + mcIsPhysicalPrimary = static_cast(mcParticle.isPhysicalPrimary()); + fillOutputs(); + fillQcHistos(); + } + } + PROCESS_SWITCH(PidFeatureExtractor, processMc, "Produce PID features for MC (reconstructed + truth)", false); + + void finalize() + { + if (exportROOT.value) { + outputFile->cd(); + featureTree->Write(); + outputFile->Close(); + } + if (exportCsv.value) { + csvFile.close(); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Tools/PIDFeatureExtractor/pidOnnxInference.cxx b/Tools/PIDFeatureExtractor/pidOnnxInference.cxx new file mode 100644 index 00000000000..4d5caaea90a --- /dev/null +++ b/Tools/PIDFeatureExtractor/pidOnnxInference.cxx @@ -0,0 +1,374 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file pidOnnxInference.cxx +/// \brief Run the FSE PID ONNX model (loaded from CCDB, or a local file for +/// testing) over the ROOT TTree produced by pidFeatureExtractor.cxx, +/// and write per-track class probabilities to a new ROOT file +/// (and/or CSV). +/// +/// A normal Configurable<>-based adaptAnalysisTask, as required by +/// this repository's conventions (workflow topology must be +/// expressed via process function switches / Configurable<>, not +/// hand-built DataProcessorSpec Options{}). All real work still +/// happens once, in init() - it reads the extractor's output file +/// directly via plain TFile/TTree, not an AOD table. process() is +/// intentionally a no-op with a minimal, always-valid AOD argument +/// (aod::Collisions), present only so this registers as a normal +/// analysis task; it does nothing per-collision. Running this task +/// therefore still requires a valid AO2D input to satisfy the +/// pipeline, even though its content is unused. +/// +/// Uses o2::analysis::MlResponse (Tools/ML/MlResponse.h) - +/// O2Physics's generic ONNX/CCDB inference wrapper - for model +/// loading and execution. +/// +/// \author Robert Forynski + +#include "Tools/ML/MlResponse.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace o2; +using namespace o2::analysis; +using namespace o2::framework; + +namespace +{ +constexpr int kNumClasses = 4; // pi, ka, pr, el - fixed order throughout, matches the paper's model +constexpr float kNaN = std::numeric_limits::quiet_NaN(); +constexpr int kNumItsLayers = 7; +constexpr int kBitsPerItsLayer = 4; +constexpr uint32_t kItsLayerMask = 0xF; + +/// itsClusterSizes packs 7 ITS layers into 4 bits each; a derived cluster +/// count is a far more sensible model input than the raw packed value. +int getItsNClusters(uint32_t v) +{ + int n = 0; + for (int layer = 0; layer < kNumItsLayers; layer++) { + if ((v >> (layer * kBitsPerItsLayer)) & kItsLayerMask) { + n++; + } + } + return n; +} + +int argmax4(std::vector const& v) +{ + int best = 0; + for (int i = 1; i < kNumClasses; i++) { + if (v[i] > v[best]) { + best = i; + } + } + return best; +} + +/// Per-group enable/disable, independent of what the input tree's hasXXX +/// flags say. Default is everything enabled (true) - the normal case, +/// using each track's real detector coverage as-is. Turning a group off +/// forces its features to the same "absent" sentinel used when the +/// detector genuinely didn't fire, and clears its mask bit - useful for +/// testing how the model behaves with a detector deliberately excluded, +/// independent of the data itself. +struct GroupToggles { + bool useTPC = true; + bool useTOF = true; + bool useTRD = true; + bool useITS = true; + bool useEMCal = true; + bool useHMPID = true; + bool useCentrality = true; +}; + +/// All the real work: load the model, read the whole input tree, run +/// inference row by row, write predictions. Called once from init(). +/// +/// Feature order fed to the model - THIS MUST MATCH YOUR TRAINING SCRIPT'S +/// COLUMN ORDER EXACTLY. Reasonable default (every reconstructed feature +/// except vz/centFT0C/sign/trackType and the Bayesian columns, which are a +/// comparison baseline, not a model input), followed by a 7-length group +/// mask (TPC/TOF/TRD/ITS/EMCal/HMPID/centrality). Each group can be +/// disabled via GroupToggles regardless of what the data says - see there +/// for details. ITS and centrality have no hasXXX flag in the input tree +/// (assumed always-present in the data itself), so their toggle is the +/// only way to exclude them. +void runInference(std::string const& inputRootFile, std::string const& inputTreeName, + std::string const& outputPath, bool exportCsv, + o2::analysis::MlResponse& mlResponse, GroupToggles const& groups) +{ + std::unique_ptr inFile(TFile::Open(inputRootFile.c_str(), "READ")); + if (!inFile || inFile->IsZombie()) { + LOG(fatal) << "Could not open input file " << inputRootFile; + return; + } + auto* tree = dynamic_cast(inFile->Get(inputTreeName.c_str())); + if (!tree) { + LOG(fatal) << "Tree " << inputTreeName << " not found in " << inputRootFile; + return; + } + + // Bind the input branches actually used below - must match + // pidFeatureExtractor.cxx's branch names exactly. + float p = 0, pt = 0, px = 0, py = 0, pz = 0, eta = 0, phi = 0; + float dcaXY = 0, dcaZ = 0; + bool hasTPC = false; + float tpcSignal = 0, tpcNSigmaPi = 0, tpcNSigmaKa = 0, tpcNSigmaPr = 0, tpcNSigmaEl = 0; + int tpcNClsFound = 0; + float tpcChi2NCl = 0; + bool hasTOF = false; + float tofMass = 0, beta = 0, tofNSigmaPi = 0, tofNSigmaKa = 0, tofNSigmaPr = 0, tofNSigmaEl = 0; + bool hasTRD = false; + float trdSignal = 0, trdChi2 = 0; + int trdPattern = 0; + int itsClusterSizes = 0; + float itsChi2NCl = 0; + bool hasEMCal = false; + float trackEtaEmcal = 0, trackPhiEmcal = 0; + bool hasHMPID = false; + float hmpidSignal = 0, hmpidQMip = 0; + int hmpidNPhotons = 0, hmpidClusSize = 0; + float hmpidMom = 0; + + tree->SetBranchAddress("p", &p); + tree->SetBranchAddress("pt", &pt); + tree->SetBranchAddress("px", &px); + tree->SetBranchAddress("py", &py); + tree->SetBranchAddress("pz", &pz); + tree->SetBranchAddress("eta", &eta); + tree->SetBranchAddress("phi", &phi); + tree->SetBranchAddress("dcaXY", &dcaXY); + tree->SetBranchAddress("dcaZ", &dcaZ); + tree->SetBranchAddress("hasTPC", &hasTPC); + tree->SetBranchAddress("tpcSignal", &tpcSignal); + tree->SetBranchAddress("tpcNSigmaPi", &tpcNSigmaPi); + tree->SetBranchAddress("tpcNSigmaKa", &tpcNSigmaKa); + tree->SetBranchAddress("tpcNSigmaPr", &tpcNSigmaPr); + tree->SetBranchAddress("tpcNSigmaEl", &tpcNSigmaEl); + tree->SetBranchAddress("tpcNClsFound", &tpcNClsFound); + tree->SetBranchAddress("tpcChi2NCl", &tpcChi2NCl); + tree->SetBranchAddress("hasTOF", &hasTOF); + tree->SetBranchAddress("tofMass", &tofMass); + tree->SetBranchAddress("beta", &beta); + tree->SetBranchAddress("tofNSigmaPi", &tofNSigmaPi); + tree->SetBranchAddress("tofNSigmaKa", &tofNSigmaKa); + tree->SetBranchAddress("tofNSigmaPr", &tofNSigmaPr); + tree->SetBranchAddress("tofNSigmaEl", &tofNSigmaEl); + tree->SetBranchAddress("hasTRD", &hasTRD); + tree->SetBranchAddress("trdSignal", &trdSignal); + tree->SetBranchAddress("trdChi2", &trdChi2); + tree->SetBranchAddress("trdPattern", &trdPattern); + tree->SetBranchAddress("itsClusterSizes", &itsClusterSizes); + tree->SetBranchAddress("itsChi2NCl", &itsChi2NCl); + tree->SetBranchAddress("hasEMCal", &hasEMCal); + tree->SetBranchAddress("trackEtaEmcal", &trackEtaEmcal); + tree->SetBranchAddress("trackPhiEmcal", &trackPhiEmcal); + tree->SetBranchAddress("hasHMPID", &hasHMPID); + tree->SetBranchAddress("hmpidSignal", &hmpidSignal); + tree->SetBranchAddress("hmpidQMip", &hmpidQMip); + tree->SetBranchAddress("hmpidNPhotons", &hmpidNPhotons); + tree->SetBranchAddress("hmpidClusSize", &hmpidClusSize); + tree->SetBranchAddress("hmpidMom", &hmpidMom); + + std::unique_ptr outFile(TFile::Open((outputPath + ".root").c_str(), "RECREATE")); + TTree outTree("pid_predictions", "PID ML predictions"); + float mlProbPi = 0, mlProbKa = 0, mlProbPr = 0, mlProbEl = 0; + int mlPredictedClass = 0; + outTree.Branch("mlProbPi", &mlProbPi); + outTree.Branch("mlProbKa", &mlProbKa); + outTree.Branch("mlProbPr", &mlProbPr); + outTree.Branch("mlProbEl", &mlProbEl); + outTree.Branch("mlPredictedClass", &mlPredictedClass); + + std::ofstream csv; + if (exportCsv) { + csv.open(outputPath + ".csv"); + csv << "mlProbPi,mlProbKa,mlProbPr,mlProbEl,mlPredictedClass\n"; + } + + std::vector x; + std::vector mlOutput; + Long64_t nEntries = tree->GetEntries(); + for (Long64_t i = 0; i < nEntries; i++) { + tree->GetEntry(i); + + // Effective presence = what the data says AND the group is enabled. + bool effTPC = hasTPC && groups.useTPC; + bool effTOF = hasTOF && groups.useTOF; + bool effTRD = hasTRD && groups.useTRD; + bool effEMCal = hasEMCal && groups.useEMCal; + bool effHMPID = hasHMPID && groups.useHMPID; + + x.clear(); + x.reserve(39 + 7); + x.push_back(p); + x.push_back(pt); + x.push_back(px); + x.push_back(py); + x.push_back(pz); + x.push_back(eta); + x.push_back(phi); + x.push_back(dcaXY); + x.push_back(dcaZ); + x.push_back(static_cast(effTPC)); + x.push_back(effTPC ? tpcSignal : kNaN); + x.push_back(effTPC ? tpcNSigmaPi : kNaN); + x.push_back(effTPC ? tpcNSigmaKa : kNaN); + x.push_back(effTPC ? tpcNSigmaPr : kNaN); + x.push_back(effTPC ? tpcNSigmaEl : kNaN); + x.push_back(effTPC ? static_cast(tpcNClsFound) : 0.f); + x.push_back(effTPC ? tpcChi2NCl : kNaN); + x.push_back(static_cast(effTOF)); + x.push_back(effTOF ? tofMass : kNaN); + x.push_back(effTOF ? beta : kNaN); + x.push_back(effTOF ? tofNSigmaPi : kNaN); + x.push_back(effTOF ? tofNSigmaKa : kNaN); + x.push_back(effTOF ? tofNSigmaPr : kNaN); + x.push_back(effTOF ? tofNSigmaEl : kNaN); + x.push_back(static_cast(effTRD)); + x.push_back(effTRD ? trdSignal : kNaN); + x.push_back(effTRD ? trdChi2 : kNaN); + x.push_back(effTRD ? static_cast(trdPattern) : 0.f); + x.push_back(groups.useITS ? static_cast(getItsNClusters(static_cast(itsClusterSizes))) : 0.f); + x.push_back(groups.useITS ? itsChi2NCl : kNaN); + x.push_back(static_cast(effEMCal)); + x.push_back(effEMCal ? trackEtaEmcal : kNaN); + x.push_back(effEMCal ? trackPhiEmcal : kNaN); + x.push_back(static_cast(effHMPID)); + x.push_back(effHMPID ? hmpidSignal : kNaN); + x.push_back(effHMPID ? hmpidQMip : kNaN); + x.push_back(effHMPID ? static_cast(hmpidNPhotons) : 0.f); + x.push_back(effHMPID ? static_cast(hmpidClusSize) : 0.f); + x.push_back(effHMPID ? hmpidMom : kNaN); + // 7-length group mask + x.push_back(static_cast(effTPC)); + x.push_back(static_cast(effTOF)); + x.push_back(static_cast(effTRD)); + x.push_back(static_cast(groups.useITS)); + x.push_back(static_cast(effEMCal)); + x.push_back(static_cast(effHMPID)); + x.push_back(static_cast(groups.useCentrality)); + + mlResponse.isSelectedMl(x, pt, mlOutput); // return value (selection) unused; mlOutput carries the 4 raw scores + mlProbPi = mlOutput[0]; + mlProbKa = mlOutput[1]; + mlProbPr = mlOutput[2]; + mlProbEl = mlOutput[3]; + mlPredictedClass = argmax4(mlOutput); + outTree.Fill(); + + if (exportCsv) { + csv << mlProbPi << ',' << mlProbKa << ',' << mlProbPr << ',' << mlProbEl << ',' << mlPredictedClass << '\n'; + } + } + + outFile->cd(); + outTree.Write(); + outFile->Close(); + if (exportCsv) { + csv.close(); + } + + LOG(info) << "PidOnnxInference: wrote " << nEntries << " predictions to " << outputPath << ".root"; +} +} // namespace + +/// PidOnnxInference: applies the FSE ONNX model to the features written by +/// pidFeatureExtractor.cxx and writes out per-track class probabilities. +/// +/// All real work happens once, in init() - see runInference() above. +/// process() is intentionally empty; it exists only so this task has a +/// valid AOD-subscribing signature, as required by this repository's +/// conventions. +struct PidOnnxInference { + Configurable inputRootFile{"inputRootFile", "pid_features_data.root", "ROOT file produced by pidFeatureExtractor.cxx"}; + Configurable inputTreeName{"inputTreeName", "pid_features", "Name of the TTree inside inputRootFile"}; + Configurable outputPath{"outputPath", "pid_predictions", "Output file base name (no extension)"}; + Configurable exportCsv{"exportCsv", false, "Also write predictions to CSV alongside the ROOT output"}; + + Configurable loadModelFromCcdb{"loadModelFromCcdb", true, "Load the ONNX model from CCDB (else from onnxFileNames as a local path)"}; + Configurable ccdbUrl{"ccdbUrl", "http://alice-ccdb.cern.ch", "CCDB URL"}; + Configurable> modelPathsCcdb{"modelPathsCcdb", std::vector{"Users/YOURNAME/PidFeatureExtractor/model"}, "CCDB path to the model"}; + Configurable timestampCcdb{"timestampCcdb", -1, "CCDB query timestamp for the model, -1 = latest"}; + Configurable> onnxFileNames{"onnxFileNames", std::vector{"pid_feature_model.onnx"}, "Local ONNX file path(s), used when loadModelFromCcdb is false"}; + + Configurable> binsPtMl{"binsPtMl", std::vector{-1., 9999.}, "pT bin edges for MlResponse (single bin = model isn't pT-binned)"}; + Configurable nClassesMl{"nClassesMl", static_cast(kNumClasses), "Number of model output classes"}; + + Configurable useTPC{"useTPC", true, "Include TPC in inference. Default true (all detectors present); set false to force TPC excluded regardless of the data"}; + Configurable useTOF{"useTOF", true, "Include TOF in inference"}; + Configurable useTRD{"useTRD", true, "Include TRD in inference"}; + Configurable useITS{"useITS", true, "Include ITS in inference"}; + Configurable useEMCal{"useEMCal", true, "Include EMCal in inference"}; + Configurable useHMPID{"useHMPID", true, "Include HMPID in inference"}; + Configurable useCentrality{"useCentrality", true, "Include centrality in inference"}; + + o2::ccdb::CcdbApi ccdbApi; + o2::analysis::MlResponse mlResponse; + + void init(InitContext&) + { + GroupToggles groups; + groups.useTPC = useTPC.value; + groups.useTOF = useTOF.value; + groups.useTRD = useTRD.value; + groups.useITS = useITS.value; + groups.useEMCal = useEMCal.value; + groups.useHMPID = useHMPID.value; + groups.useCentrality = useCentrality.value; + + // Unused thresholds (CutNot everywhere) - this task always reports + // all four probabilities rather than applying a selection cut, so + // cutsMl/cutDirMl don't need to be user-configurable. + static constexpr double kDefaultCutsMl[1][kNumClasses] = {{0., 0., 0., 0.}}; + LabeledArray cutsMl{kDefaultCutsMl[0], 1, kNumClasses, {"pT bin 0"}, {"prob pi", "prob ka", "prob pr", "prob el"}}; + std::vector cutDirMl{cuts_ml::CutNot, cuts_ml::CutNot, cuts_ml::CutNot, cuts_ml::CutNot}; + + mlResponse.configure(binsPtMl.value, cutsMl, cutDirMl, static_cast(nClassesMl.value)); + if (loadModelFromCcdb.value) { + ccdbApi.init(ccdbUrl.value); + mlResponse.setModelPathsCCDB(onnxFileNames.value, ccdbApi, modelPathsCcdb.value, timestampCcdb.value); + } else { + mlResponse.setModelPathsLocal(onnxFileNames.value); + } + mlResponse.init(); + + runInference(inputRootFile.value, inputTreeName.value, outputPath.value, exportCsv.value, mlResponse, groups); + } + + /// Intentionally empty - all real work happens once in init(). Present + /// only so this task has a valid, AOD-subscribing process() signature. + void process(aod::Collisions const&) {} +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Tools/PIDML/pidMl.h b/Tools/PIDML/pidMl.h index cb45360052c..5a303c956d4 100644 --- a/Tools/PIDML/pidMl.h +++ b/Tools/PIDML/pidMl.h @@ -17,11 +17,14 @@ #ifndef TOOLS_PIDML_PIDML_H_ #define TOOLS_PIDML_PIDML_H_ +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include #include #include @@ -48,8 +51,22 @@ DECLARE_SOA_COLUMN(TofExpSignalDiffKa, tofExpSignalDiffKa, float); //! Differenc DECLARE_SOA_COLUMN(TpcExpSignalDiffKa, tpcExpSignalDiffKa, float); //! Difference between signal and expected for kaon DECLARE_SOA_COLUMN(TofExpSignalDiffPr, tofExpSignalDiffPr, float); //! Difference between signal and expected for proton DECLARE_SOA_COLUMN(TpcExpSignalDiffPr, tpcExpSignalDiffPr, float); //! Difference between signal and expected for proton +DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, int16_t); //! Number of found TPC clusters +DECLARE_SOA_COLUMN(TpcNClsPID, tpcNClsPID, int16_t); //! Number of TPC clusters used for PID + +// event selection flags +DECLARE_SOA_COLUMN(EvSelNoSameBunchPileup, evSelNoSameBunchPileup, bool); +DECLARE_SOA_COLUMN(EvSelIsGoodZvtxFT0vsPV, evSelIsGoodZvtxFT0vsPV, bool); +DECLARE_SOA_COLUMN(EvSelIsGoodITSLayersAll, evSelIsGoodITSLayersAll, bool); +DECLARE_SOA_COLUMN(EvSelNoCollInRofStandard, evSelNoCollInRofStandard, bool); +DECLARE_SOA_COLUMN(EvSelNoHighMultCollInPrevRof, evSelNoHighMultCollInPrevRof, bool); +DECLARE_SOA_COLUMN(EvSelIsVertexITSTPC, evSelIsVertexITSTPC, bool); +DECLARE_SOA_COLUMN(EvSelNoCollInTimeRangeStandard, evSelNoCollInTimeRangeStandard, bool); +DECLARE_SOA_COLUMN(EvSelNoITSROFrameBorder, evSelNoITSROFrameBorder, bool); +DECLARE_SOA_COLUMN(EvSelNoTimeFrameBorder, evSelNoTimeFrameBorder, bool); } // namespace pidtracks DECLARE_SOA_TABLE(PidTracksDataMl, "AOD", "PIDTRACKSDATAML", //! Data tracks for prediction and domain adaptation + aod::track::CollisionId, aod::track::TPCSignal, aod::track::TRDSignal, aod::track::TRDPattern, aod::pidtofsignal::TOFSignal, @@ -69,12 +86,22 @@ DECLARE_SOA_TABLE(PidTracksDataMl, "AOD", "PIDTRACKSDATAML", //! Data tracks for aod::track::DcaXY, aod::track::DcaZ); DECLARE_SOA_TABLE(PidTracksData, "AOD", "PIDTRACKSDATA", //! Data tracks for comparative analysis + aod::track::CollisionId, aod::mult::MultFV0A, aod::mult::MultFV0C, pidtracks::MultFV0M, aod::mult::MultFT0A, aod::mult::MultFT0C, pidtracks::MultFT0M, - aod::mult::MultZNA, aod::mult::MultZNC, - aod::mult::MultTracklets, aod::mult::MultTPC, - aod::track::TPCSignal, - aod::track::TRDSignal, aod::track::TRDPattern, + aod::mult::MultZNA, aod::mult::MultZNC, aod::mult::MultTracklets, + aod::mult::MultTPC, aod::mult::MultNTracksPV, + aod::collision::PosZ, aod::evsel::OccupancyMedianTime, aod::cent::CentFT0C, + pidtracks::EvSelNoSameBunchPileup, + pidtracks::EvSelIsGoodZvtxFT0vsPV, + pidtracks::EvSelIsGoodITSLayersAll, + pidtracks::EvSelNoCollInRofStandard, + pidtracks::EvSelNoHighMultCollInPrevRof, + pidtracks::EvSelIsVertexITSTPC, + pidtracks::EvSelNoCollInTimeRangeStandard, + pidtracks::EvSelNoITSROFrameBorder, + pidtracks::EvSelNoTimeFrameBorder, + aod::track::TPCSignal, aod::track::TRDSignal, aod::track::TRDPattern, aod::track::TrackEtaEMCAL, aod::track::TrackPhiEMCAL, aod::pidtofsignal::TOFSignal, @@ -122,8 +149,25 @@ DECLARE_SOA_TABLE(PidTracksData, "AOD", "PIDTRACKSDATA", //! Data tracks for com pidtracks::TpcExpSignalDiffPr, pidtof::TOFNSigmaPr, pidtof::TOFExpSigmaPr, - pidtracks::TofExpSignalDiffPr); + pidtracks::TofExpSignalDiffPr, + aod::track::Snp, + aod::track::Tgl, + aod::track::SigmaY, + aod::track::SigmaZ, + aod::track::SigmaSnp, + aod::track::SigmaTgl, + aod::track::Sigma1Pt, + aod::track::TPCNClsFindable, + pidtracks::TpcNClsFound, + pidtracks::TpcNClsPID, + aod::track::TPCChi2NCl, + aod::track::TrackTime, + aod::track::Length, + aod::track::TOFChi2, + aod::track::ITSClusterMap, + aod::track::TRDChi2); DECLARE_SOA_TABLE(PidTracksMcMl, "AOD", "PIDTRACKSMCML", //! MC tracks for training + aod::track::CollisionId, aod::track::TPCSignal, aod::track::TRDSignal, aod::track::TRDPattern, aod::pidtofsignal::TOFSignal, @@ -145,12 +189,22 @@ DECLARE_SOA_TABLE(PidTracksMcMl, "AOD", "PIDTRACKSMCML", //! MC tracks for train aod::mcparticle::PdgCode, pidtracks::IsPhysicalPrimary); DECLARE_SOA_TABLE(PidTracksMc, "AOD", "PIDTRACKSMC", //! MC tracks for comparative analysis + aod::track::CollisionId, aod::mult::MultFV0A, aod::mult::MultFV0C, pidtracks::MultFV0M, aod::mult::MultFT0A, aod::mult::MultFT0C, pidtracks::MultFT0M, - aod::mult::MultZNA, aod::mult::MultZNC, - aod::mult::MultTracklets, aod::mult::MultTPC, - aod::track::TPCSignal, - aod::track::TRDSignal, aod::track::TRDPattern, + aod::mult::MultZNA, aod::mult::MultZNC, aod::mult::MultTracklets, + aod::mult::MultTPC, aod::mult::MultNTracksPV, + aod::collision::PosZ, aod::evsel::OccupancyMedianTime, aod::cent::CentFT0C, + pidtracks::EvSelNoSameBunchPileup, + pidtracks::EvSelIsGoodZvtxFT0vsPV, + pidtracks::EvSelIsGoodITSLayersAll, + pidtracks::EvSelNoCollInRofStandard, + pidtracks::EvSelNoHighMultCollInPrevRof, + pidtracks::EvSelIsVertexITSTPC, + pidtracks::EvSelNoCollInTimeRangeStandard, + pidtracks::EvSelNoITSROFrameBorder, + pidtracks::EvSelNoTimeFrameBorder, + aod::track::TPCSignal, aod::track::TRDSignal, aod::track::TRDPattern, aod::track::TrackEtaEMCAL, aod::track::TrackPhiEMCAL, aod::pidtofsignal::TOFSignal, @@ -199,6 +253,22 @@ DECLARE_SOA_TABLE(PidTracksMc, "AOD", "PIDTRACKSMC", //! MC tracks for comparati pidtof::TOFNSigmaPr, pidtof::TOFExpSigmaPr, pidtracks::TofExpSignalDiffPr, + aod::track::Snp, + aod::track::Tgl, + aod::track::SigmaY, + aod::track::SigmaZ, + aod::track::SigmaSnp, + aod::track::SigmaTgl, + aod::track::Sigma1Pt, + aod::track::TPCNClsFindable, + pidtracks::TpcNClsFound, + pidtracks::TpcNClsPID, + aod::track::TPCChi2NCl, + aod::track::TrackTime, + aod::track::Length, + aod::track::TOFChi2, + aod::track::ITSClusterMap, + aod::track::TRDChi2, aod::mcparticle::PdgCode, pidtracks::IsPhysicalPrimary); } // namespace o2::aod diff --git a/Tools/PIDML/pidMlProducer.cxx b/Tools/PIDML/pidMlProducer.cxx index 332a2a8209b..2d2968973a4 100644 --- a/Tools/PIDML/pidMlProducer.cxx +++ b/Tools/PIDML/pidMlProducer.cxx @@ -15,13 +15,15 @@ /// \author Maja Kabus /// \author Marek Mytkowski -#include "Tools/PIDML/pidMl.h" -#include "Tools/PIDML/pidUtils.h" -// +#include "Common/CCDB/EventSelectionParams.h" +#include "Common/DataModel/Centrality.h" +#include "Common/DataModel/EventSelection.h" #include "Common/DataModel/Multiplicity.h" #include "Common/DataModel/PIDResponseTOF.h" #include "Common/DataModel/PIDResponseTPC.h" #include "Common/DataModel/TrackSelectionTables.h" +#include "Tools/PIDML/pidMl.h" +#include "Tools/PIDML/pidUtils.h" #include #include @@ -41,6 +43,7 @@ #include #include #include +#include #include using namespace o2; @@ -58,18 +61,49 @@ struct PidMlProducer { Produces pidTracksTableMCML; Produces pidTracksTableMC; + // Basic event selection: sel8() + (zVertex < zVertexMax) + (centMin <= FT0C <= centMax) + struct : o2::framework::ConfigurableGroup { + std::string prefix = "confBasicEventSelection"; + + Configurable enableSel8{"enableSel8", true, "enable sel8 cut."}; + Configurable zVertexMax{"zVertexMax", 10., "Maximum Z Vertex value cm."}; + Configurable centMin{"centMin", 0.f, "Minimum centrality (FT0C)."}; + Configurable centMax{"centMax", 200.f, "Maximum centrality (FT0C)."}; + } confBasicEventSelection; + + // Strict event selection: criteria boolean flags (see their details) + TPC occupancy + struct : o2::framework::ConfigurableGroup { + std::string prefix = "confStrictEventSelection"; + Configurable enable{"enable", false, "Enable stricter event selection filtering"}; + + struct : o2::framework::ConfigurableGroup { + std::string prefix = "confStrictEventSelection.criteria"; + Configurable evIsGoodZvtxFT0vsPV{"evIsGoodZvtxFT0vsPV", true, "Require kIsGoodZvtxFT0vsPV selection on events"}; + Configurable evNoSameBunchPileup{"evNoSameBunchPileup", true, "Require kNoSameBunchPileup selection on events"}; + Configurable evIsVertexITSTPC{"evIsVertexITSTPC", true, "Require kIsVertexITSTPC selection on events"}; + Configurable isGoodITSLayersAll{"isGoodITSLayersAll", true, "Require kIsGoodITSLayersAll selection on events"}; + Configurable noITSROFrameBorder{"noITSROFrameBorder", true, "Require kNoITSROFrameBorder selection on events"}; + Configurable noTimeFrameBorder{"noTimeFrameBorder", true, "Require kNoTimeFrameBorder selection on events"}; + Configurable noCollInRofStandard{"noCollInRofStandard", true, "Require kNoCollInRofStandard selection on events"}; + Configurable noHighMultCollInPrevRof{"noHighMultCollInPrevRof", true, "Require kNoHighMultCollInPrevRof selection on events"}; + Configurable noCollInTimeRangeStandard{"noCollInTimeRangeStandard", true, "Require kNoCollInTimeRangeStandard selection on events"}; + Configurable tpcOccupancyMin{"tpcOccupancyMin", 0, "Minimum value for TPC occupancy selection"}; + Configurable tpcOccupancyMax{"tpcOccupancyMax", 500, "Maximum value for TPC occupancy selection"}; + } criteria; + } confStrictEventSelection; + Filter trackFilter = requireGlobalTrackInFilter(); // Data tracks - using BigTracksDataML = soa::Filtered>; - using BigTracksData = soa::Filtered>; + using BigTracksDataML = soa::Filtered>; + using BigTracksData = soa::Filtered>; // MC tracks - using BigTracksMCML = soa::Filtered>; - using BigTracksMC = soa::Filtered>; + using BigTracksMCML = soa::Filtered>; + using BigTracksMC = soa::Filtered>; - using MyCollisionML = aod::Collisions::iterator; - using MyCollision = soa::Join::iterator; + using MyCollisionML = soa::Join::iterator; + using MyCollision = soa::Join::iterator; static constexpr uint32_t NCharges = 2; @@ -230,10 +264,64 @@ struct PidMlProducer { return trdMissing(track) ? static_cast(0U) : track.trdPattern(); } - void processDataML(MyCollisionML const& /*collision*/, BigTracksDataML const& tracks) + // inspired by PWGCF/FemtoUniverse/TableProducer/femtoUniverseProducerTask.cxx + template + bool fillCollisionsCentRun3(CollisionType const& col) + { + // Basic Event Selection + { + const auto& basic_criteria = confBasicEventSelection; + + // Z Vertex filter + if (std::abs(col.posZ()) > basic_criteria.zVertexMax.value) + return false; + + // Selection 8 filters + if (basic_criteria.enableSel8.value && !col.sel8()) + return false; + + // Centrality filter + const auto cent = col.centFT0C(); + if ((cent < basic_criteria.centMin.value) || (cent > basic_criteria.centMax.value)) + return false; + } + + // Strict Event Selection + { + if (!confStrictEventSelection.enable) + return true; + + const auto& criteria = confStrictEventSelection.criteria; + + // TPC Occupancy filter + const auto occupancy = col.trackOccupancyInTimeRange(); + if (occupancy < criteria.tpcOccupancyMin.value || occupancy > criteria.tpcOccupancyMax.value) + return false; + + // Event Selection boolean filters + if ((!criteria.evNoSameBunchPileup.value || col.selection_bit(aod::evsel::kNoSameBunchPileup)) && + (!criteria.evIsGoodZvtxFT0vsPV.value || col.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV)) && + (!criteria.isGoodITSLayersAll.value || col.selection_bit(aod::evsel::kIsGoodITSLayersAll)) && + (!criteria.noCollInRofStandard.value || col.selection_bit(aod::evsel::kNoCollInRofStandard)) && + (!criteria.noHighMultCollInPrevRof.value || col.selection_bit(aod::evsel::kNoHighMultCollInPrevRof)) && + (!criteria.evIsVertexITSTPC.value || col.selection_bit(aod::evsel::kIsVertexITSTPC)) && + (!criteria.noCollInTimeRangeStandard.value || col.selection_bit(aod::evsel::kNoCollInTimeRangeStandard)) && + (!criteria.noITSROFrameBorder.value || col.selection_bit(aod::evsel::kNoITSROFrameBorder)) && + (!criteria.noTimeFrameBorder.value || col.selection_bit(aod::evsel::kNoTimeFrameBorder))) + return true; + + return false; + } + } + + void processDataML(MyCollisionML const& collision, BigTracksDataML const& tracks) { + if (!fillCollisionsCentRun3(collision)) { + return; + } + for (const auto& track : tracks) { - pidTracksTableDataML(track.tpcSignal(), getTRDSignal(track), getTRDPattern(track), + pidTracksTableDataML(track.collisionId(), track.tpcSignal(), getTRDSignal(track), getTRDPattern(track), getTOFSignal(track), getTOFBeta(track), track.p(), track.pt(), track.px(), track.py(), track.pz(), track.sign(), @@ -250,11 +338,25 @@ struct PidMlProducer { void processDataAll(MyCollision const& collision, BigTracksData const& tracks) { + if (!fillCollisionsCentRun3(collision)) { + return; + } + for (const auto& track : tracks) { - pidTracksTableData(collision.multFV0A(), collision.multFV0C(), collision.multFV0M(), + pidTracksTableData(track.collisionId(), collision.multFV0A(), collision.multFV0C(), collision.multFV0M(), collision.multFT0A(), collision.multFT0C(), collision.multFT0M(), - collision.multZNA(), collision.multZNC(), - collision.multTracklets(), collision.multTPC(), + collision.multZNA(), collision.multZNC(), collision.multTracklets(), + collision.multTPC(), collision.multNTracksPV(), + collision.posZ(), collision.trackOccupancyInTimeRange(), collision.centFT0C(), + collision.selection_bit(aod::evsel::kNoSameBunchPileup), + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), + collision.selection_bit(aod::evsel::kIsGoodITSLayersAll), + collision.selection_bit(aod::evsel::kNoCollInRofStandard), + collision.selection_bit(aod::evsel::kNoHighMultCollInPrevRof), + collision.selection_bit(aod::evsel::kIsVertexITSTPC), + collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard), + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), track.tpcSignal(), getTRDSignal(track), getTRDPattern(track), track.trackEtaEmcal(), track.trackPhiEmcal(), getTOFSignal(track), getTOFBeta(track), @@ -274,15 +376,23 @@ struct PidMlProducer { track.tpcNSigmaKa(), track.tpcExpSigmaKa(), track.tpcExpSignalDiffKa(), track.tofNSigmaKa(), track.tofExpSigmaKa(), track.tofExpSignalDiffKa(), track.tpcNSigmaPr(), track.tpcExpSigmaPr(), track.tpcExpSignalDiffPr(), - track.tofNSigmaPr(), track.tofExpSigmaPr(), track.tofExpSignalDiffPr()); + track.tofNSigmaPr(), track.tofExpSigmaPr(), track.tofExpSignalDiffPr(), + track.snp(), track.tgl(), track.sigmaY(), track.sigmaZ(), track.sigmaSnp(), + track.sigmaTgl(), track.sigma1Pt(), track.tpcNClsFindable(), track.tpcNClsFound(), + track.tpcNClsPID(), track.tpcChi2NCl(), track.trackTime(), track.length(), + track.tofChi2(), track.itsClusterMap(), track.trdChi2()); fillHist(track); } } PROCESS_SWITCH(PidMlProducer, processDataAll, "Produce all real data", false); - void processMcMl(MyCollisionML const& /*collision*/, BigTracksMCML const& tracks, aod::McParticles const& /*mctracks*/) + void processMcMl(MyCollisionML const& collision, BigTracksMCML const& tracks, aod::McParticles const& /*mctracks*/) { + if (!fillCollisionsCentRun3(collision)) { + return; + } + for (const auto& track : tracks) { if (!track.has_mcParticle()) { continue; @@ -290,7 +400,7 @@ struct PidMlProducer { const auto mcParticle = track.mcParticle_as(); uint8_t isPrimary = static_cast(mcParticle.isPhysicalPrimary()); uint32_t pdgCode = mcParticle.pdgCode(); - pidTracksTableMCML(track.tpcSignal(), getTRDSignal(track), getTRDPattern(track), + pidTracksTableMCML(track.collisionId(), track.tpcSignal(), getTRDSignal(track), getTRDPattern(track), getTOFSignal(track), getTOFBeta(track), track.p(), track.pt(), track.px(), track.py(), track.pz(), track.sign(), @@ -309,6 +419,10 @@ struct PidMlProducer { void processMcAll(MyCollision const& collision, BigTracksMC const& tracks, aod::McParticles const& /*mctracks*/) { + if (!fillCollisionsCentRun3(collision)) { + return; + } + for (const auto& track : tracks) { if (!track.has_mcParticle()) { continue; @@ -316,10 +430,20 @@ struct PidMlProducer { const auto mcParticle = track.mcParticle_as(); uint8_t isPrimary = static_cast(mcParticle.isPhysicalPrimary()); uint32_t pdgCode = mcParticle.pdgCode(); - pidTracksTableMC(collision.multFV0A(), collision.multFV0C(), collision.multFV0M(), + pidTracksTableMC(track.collisionId(), collision.multFV0A(), collision.multFV0C(), collision.multFV0M(), collision.multFT0A(), collision.multFT0C(), collision.multFT0M(), - collision.multZNA(), collision.multZNC(), - collision.multTracklets(), collision.multTPC(), + collision.multZNA(), collision.multZNC(), collision.multTracklets(), + collision.multTPC(), collision.multNTracksPV(), + collision.posZ(), collision.trackOccupancyInTimeRange(), collision.centFT0C(), + collision.selection_bit(aod::evsel::kNoSameBunchPileup), + collision.selection_bit(aod::evsel::kIsGoodZvtxFT0vsPV), + collision.selection_bit(aod::evsel::kIsGoodITSLayersAll), + collision.selection_bit(aod::evsel::kNoCollInRofStandard), + collision.selection_bit(aod::evsel::kNoHighMultCollInPrevRof), + collision.selection_bit(aod::evsel::kIsVertexITSTPC), + collision.selection_bit(aod::evsel::kNoCollInTimeRangeStandard), + collision.selection_bit(aod::evsel::kNoITSROFrameBorder), + collision.selection_bit(aod::evsel::kNoTimeFrameBorder), track.tpcSignal(), getTRDSignal(track), getTRDPattern(track), track.trackEtaEmcal(), track.trackPhiEmcal(), getTOFSignal(track), getTOFBeta(track), @@ -340,6 +464,10 @@ struct PidMlProducer { track.tofNSigmaKa(), track.tofExpSigmaKa(), track.tofExpSignalDiffKa(), track.tpcNSigmaPr(), track.tpcExpSigmaPr(), track.tpcExpSignalDiffPr(), track.tofNSigmaPr(), track.tofExpSigmaPr(), track.tofExpSignalDiffPr(), + track.snp(), track.tgl(), track.sigmaY(), track.sigmaZ(), track.sigmaSnp(), + track.sigmaTgl(), track.sigma1Pt(), track.tpcNClsFindable(), track.tpcNClsFound(), + track.tpcNClsPID(), track.tpcChi2NCl(), track.trackTime(), track.length(), + track.tofChi2(), track.itsClusterMap(), track.trdChi2(), pdgCode, isPrimary); diff --git a/Tutorials/ML/CMakeLists.txt b/Tutorials/ML/CMakeLists.txt index db28093cd7d..ad8a5477b88 100644 --- a/Tutorials/ML/CMakeLists.txt +++ b/Tutorials/ML/CMakeLists.txt @@ -19,3 +19,14 @@ o2physics_add_dpl_workflow(apply-ml-selection SOURCES applyMlSelection.cxx PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::MLCore COMPONENT_NAME AnalysisTutorial) + +o2physics_add_dpl_workflow(benchmark-onnx-model + SOURCES benchmarkOnnxModel.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2::ML O2::GPUCommon ONNXRuntime::ONNXRuntime + TARGETVARNAME benchmarkOnnxModelTarget + COMPONENT_NAME AnalysisTutorial) + +# System libraries are linked directly: the workflow helper requires namespaced targets. +if(CMAKE_DL_LIBS) + target_link_libraries(${benchmarkOnnxModelTarget} PRIVATE ${CMAKE_DL_LIBS}) +endif() diff --git a/Tutorials/ML/README-benchmark.md b/Tutorials/ML/README-benchmark.md new file mode 100644 index 00000000000..65702fc5ede --- /dev/null +++ b/Tutorials/ML/README-benchmark.md @@ -0,0 +1,123 @@ +# ONNX inference benchmark tutorial + +`benchmarkOnnxModel.cxx` registers `o2-analysis-benchmark-onnx-model`. +It runs once with deterministic synthetic data, writes one CSV row, and exits. +It accepts one dense floating-point input of shape `[batch, features]` and one +or more outputs. Static dimensions must match the options; symbolic dimensions +are filled from the options. Output types are left to ONNX Runtime. + +## Build and GPU prerequisites + +Build O2Physics normally after loading its O2 environment. This target links +`O2::ML`, `O2::GPUCommon`, `ONNXRuntime::ONNXRuntime`, and the platform dynamic +loader. No HIP or CUDA headers are required for building the task. The O2 installation +must contain `ML/3rdparty/GPUORTFloat16.h` and its GPUCommon headers. + +For AMD GPU runs, ONNX Runtime must contain **MIGraphXExecutionProvider**, and its +MIGraphX/ROCm shared libraries, including `libamdhip64.so`, must be discoverable +at runtime. The task checks provider availability and disables CPU fallback: +unsupported graphs fail rather than producing a misleading GPU benchmark. +No changes to O2's OrtInterface are needed for this task; the current checked-out +OrtInterface does not attach an AMD execution provider, so this example uses +ORT directly. + +For NVIDIA runs, select `--provider cuda`. ONNX Runtime must contain +**CUDAExecutionProvider**, with matching CUDA/cuDNN libraries and an NVIDIA +driver. Telemetry loads `libcudart.so` (also tries `.so.13`, `.so.12`, `.so.11.0`) +from the library search path. Keep this runtime consistent with the ORT CUDA +build. CUDA provider options require ORT 1.18 or newer: `use_tf32=0` disables +TF32 so the FP32 benchmark does not silently use TF32 matrix multiplication. +Both GPU backends disable CPU fallback and use the same timing/CSV path. + +Check the actual hydra driver, ROCm, MIGraphX and ORT combination first. MI100 +(gfx908) and MI50 (gfx906) do not have identical software support. MI50 is absent +from current supported ROCm hardware lists, so it needs a compatible older +stack or a separately validated build. The old ROCm execution provider was +removed from ONNX Runtime starting with 1.23; this task uses MIGraphX. + +References: +- +- +- +- + +## Run the three comparisons + +Use equivalent FP32 and FP16 exports of the same network. Replace the example +feature count with the actual model input width. Use separate output files; +each invocation overwrites its output CSV. + +```bash +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider cpu --fp16 false --batch-size 1024 --features 5 \ + --cpu-threads 1 --warmup 20 --iterations 200 --output-csv cpu-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider migraphx --gpu-id 0 --fp16 false --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv gpu-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp16.onnx \ + --provider migraphx --gpu-id 0 --fp16 true --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv gpu-fp16.csv +``` + +For NVIDIA, replace `--provider migraphx` with `--provider cuda`, for example: + +```bash +o2-analysis-benchmark-onnx-model -b --model-path model-fp32.onnx \ + --provider cuda --gpu-id 0 --fp16 false --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv cuda-fp32.csv + +o2-analysis-benchmark-onnx-model -b --model-path model-fp16.onnx \ + --provider cuda --gpu-id 0 --fp16 true --batch-size 1024 --features 5 \ + --warmup 20 --iterations 200 --output-csv cuda-fp16.csv +``` + +`gpu-id` is the logical ID after `HIP_VISIBLE_DEVICES` / `ROCR_VISIBLE_DEVICES` +masking on AMD or `CUDA_VISIBLE_DEVICES` masking on NVIDIA. Run one benchmark process at a time on an idle +GPU. Repeat with the same batch sizes and CPU thread count, and compare several +runs. Small batches can be dominated by host overhead and transfers. + +`--fp16 true` selects O2's `o2::OrtDataType::Float16_t` input storage and checks +that the model input is FLOAT16. It does not convert model weights or prove that +every internal operation is FP16. Export/inspect the model accordingly. FP16 +models retaining FP32 input interfaces need an adapted input path; this small +tutorial intentionally rejects them. CPU runs require FP32 input. + +## What the CSV measures + +- `samples_per_second = batch_size * iterations / seconds`. +- Timing covers synchronous `Session::Run`, input/output transfers, and output + allocation/destruction. It excludes session creation, MIGraphX compilation, + warmup, input generation, FP16 conversion, telemetry, and CSV I/O. This is + host-to-host inference throughput, not GPU kernel-only throughput. +- GPU memory fields are **device-wide used bytes**, sampled through + `hipMemGetInfo` (AMD) or `cudaMemGetInfo` (NVIDIA) before session creation, after warmup, and after timed runs. + They include other processes and runtime allocations; they are neither + model-only memory nor a transient peak. The baseline follows GPU runtime/context + initialization. CPU runs leave these fields empty. GPU telemetry failures + fail the run rather than reporting zero usage. +- Precision labels describe the requested model/input variant, not measured + arithmetic precision. Strict CPU fallback rejection can reject graphs that + would otherwise run with some CPU operators. + +This benchmark does not check prediction accuracy. Compare real model outputs +between the exports separately before interpreting performance gains. + +## Adding the AO2D input later + +Planned input on hydra: + +```text +/lustre/alice/users/marin/R3A/TPCTrees/TPCTrees_LHC24as_250224/AO2D_merge_LHC24as_updatedBB_250224.root +``` + +The file has not been inspected here. Do not pass `--aod-file` to this standalone +synthetic task yet. First establish the AO2D tables, feature columns and order, +training normalization, selections, and missing-value policy. Then add an +analysis `process(...)` input with the appropriate table definitions, build +contiguous batches from its rows, and reuse the inference path. Keep session +creation in `init`, aggregate timing/sample counts across batches, and write +results at end of stream. Handle the final partial batch explicitly (dynamic +batch, or pad and count only valid samples). Keep data reading/preprocessing +outside inference timing or report a separate full-pipeline metric. diff --git a/Tutorials/ML/applyMlSelection.cxx b/Tutorials/ML/applyMlSelection.cxx index 869abd7a62c..b457dfa61ce 100644 --- a/Tutorials/ML/applyMlSelection.cxx +++ b/Tutorials/ML/applyMlSelection.cxx @@ -36,6 +36,7 @@ #include #include +#include #include using namespace o2; @@ -65,7 +66,6 @@ struct applyMlSelection { Filter filterDsFlag = (o2::aod::hf_track_index::hfflag & static_cast(BIT(aod::hf_cand_3prong::DecayType::DsToKKPi))) != static_cast(0); - HfHelper hfHelper; o2::ccdb::CcdbApi ccdbApi; int nCandidates = 0; @@ -81,7 +81,7 @@ struct applyMlSelection { void init(InitContext const&) { // Add histograms vs pT (only for selected candidates) - auto vbins = (std::vector)binsPtMl; + auto vbins = std::vector(binsPtMl); registry.add("hMassAfterSelVsPt", "Ds candidates;inv. mass (KK#pi) (GeV/#it{c}^{2});entries", {HistType::kTH2F, {{100, 1.77, 2.17}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); registry.add("hPromptScoreAfterSelVsPt", "Prompt score after selection;BDT first score;entries", {HistType::kTH2F, {{100, 0., 1.}, {vbins, "#it{p}_{T} (GeV/#it{c})"}}}); @@ -107,22 +107,22 @@ struct applyMlSelection { continue; } // Check that the candidate rapidity is within the analysis range - if (yCandRecoMax >= 0. && std::abs(hfHelper.yDs(candidate)) > yCandRecoMax) { + if (yCandRecoMax >= 0. && std::abs(HfHelper::yDs(candidate)) > yCandRecoMax) { continue; } // Fill mass histogram before ML selections - registry.fill(HIST("hMassBeforeSel"), hfHelper.invMassDsToPiKK(candidate)); - registry.fill(HIST("hMassBeforeSel"), hfHelper.invMassDsToKKPi(candidate)); + registry.fill(HIST("hMassBeforeSel"), HfHelper::invMassDsToPiKK(candidate)); + registry.fill(HIST("hMassBeforeSel"), HfHelper::invMassDsToKKPi(candidate)); // Perform ML selections for one mass hypothesis (Ds -> PhiPi -> PiKK) std::vector inputFeaturesPiKK{candidate.cpa(), candidate.cpaXY(), candidate.decayLength(), candidate.decayLengthXY(), - static_cast(hfHelper.deltaMassPhiDsToPiKK(candidate)), + static_cast(HfHelper::deltaMassPhiDsToPiKK(candidate)), candidate.impactParameterXY(), - static_cast(hfHelper.cos3PiKDsToPiKK(candidate)), + static_cast(HfHelper::cos3PiKDsToPiKK(candidate)), candidate.maxNormalisedDeltaIP()}; // Retrieve model output and selection outcome diff --git a/Tutorials/ML/applyOnnxModel.cxx b/Tutorials/ML/applyOnnxModel.cxx index 16151511df0..560e3f204bd 100644 --- a/Tutorials/ML/applyOnnxModel.cxx +++ b/Tutorials/ML/applyOnnxModel.cxx @@ -24,6 +24,8 @@ #include #include +#include +#include #include #include @@ -51,11 +53,11 @@ struct applyModel { void run(ProcessingContext& pc) { - // Here we evaluate the model - float* modelOutput = network.evalModel(modelInput); + // Here we evaluate the model. The output tensor is copied into a std::vector owned by us + const std::vector modelOutput = network.evalModel(modelInput); // And now we print the output - for (int i = 0; i < 5; i++) { + for (std::size_t i = 0; i < std::min(modelInput.size(), modelOutput.size()); i++) { LOG(info) << "Input: " << modelInput[i] << ", Output: " << modelOutput[i]; } pc.services().get().endOfStream(); diff --git a/Tutorials/ML/benchmarkOnnxModel.cxx b/Tutorials/ML/benchmarkOnnxModel.cxx new file mode 100644 index 00000000000..67677940346 --- /dev/null +++ b/Tutorials/ML/benchmarkOnnxModel.cxx @@ -0,0 +1,266 @@ +// Copyright 2019-2020 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. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// Benchmark one dense ONNX input [batch, features] using CPU, AMD MIGraphX or NVIDIA CUDA. +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +// Load GPU telemetry at runtime so CPU builds need no HIP or CUDA headers. +// Memory is device-wide, not process-local; samples are not a transient peak. +class GpuMemory +{ + public: + explicit GpuMemory(int device, bool cuda) + { + const std::vector libraries = cuda + ? std::vector{"libcudart.so", "libcudart.so.13", "libcudart.so.12", "libcudart.so.11.0"} + : std::vector{"libamdhip64.so"}; + for (auto name : libraries) { + library = dlopen(name, RTLD_LAZY | RTLD_LOCAL); + if (library) { + break; + } + } + if (!library) { + throw std::runtime_error(cuda ? "GPU telemetry requires libcudart.so (CUDA 11/12/13) on LD_LIBRARY_PATH" + : "GPU telemetry requires libamdhip64.so on LD_LIBRARY_PATH"); + } + auto setDevice = reinterpret_cast(dlsym(library, cuda ? "cudaSetDevice" : "hipSetDevice")); + memoryFunction = cuda ? "cudaMemGetInfo" : "hipMemGetInfo"; + getMemory = reinterpret_cast(dlsym(library, memoryFunction)); + if (!setDevice || !getMemory || setDevice(device) != 0) { + dlclose(library); + library = nullptr; + throw std::runtime_error(cuda ? "Cannot select CUDA device or resolve CUDA memory API" + : "Cannot select HIP device or resolve HIP memory API"); + } + } + ~GpuMemory() + { + if (library) { + dlclose(library); + } + } + GpuMemory(GpuMemory const&) = delete; + GpuMemory& operator=(GpuMemory const&) = delete; + uint64_t used() const + { + size_t free = 0, total = 0; + if (getMemory(&free, &total) != 0 || free > total) { + throw std::runtime_error(std::string(memoryFunction) + " failed"); + } + return total - free; + } + + private: + const char* memoryFunction = nullptr; + void* library = nullptr; + int (*getMemory)(size_t*, size_t*) = nullptr; +}; + +std::string csvQuote(std::string const& value) +{ + std::string result = "\""; + for (char c : value) { + result += c; + if (c == '"') { + result += '"'; + } + } + return result + '"'; +} +} // namespace + +using namespace o2::framework; + +struct BenchmarkOnnxModel { + Configurable modelPath{"model-path", "model.onnx", "Local ONNX model"}; + Configurable provider{"provider", "cpu", "cpu, migraphx or cuda"}; + Configurable gpuId{"gpu-id", 0, "HIP/CUDA logical device ID (after visibility masking)"}; + Configurable fp16{"fp16", false, "Require FP16 input; does not convert model weights"}; + Configurable batchSize{"batch-size", 1024, "Samples per inference"}; + Configurable features{"features", 5, "Features per sample, in model order"}; + Configurable warmup{"warmup", 10, "Untimed warm-up calls"}; + Configurable iterations{"iterations", 100, "Timed inference calls"}; + Configurable cpuThreads{"cpu-threads", 1, "ORT intra-op CPU threads"}; + Configurable outputCsv{"output-csv", "onnx-benchmark.csv", "CSV output (overwritten)"}; + + void run(ProcessingContext& pc) + { + benchmark(); + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } + + void benchmark() + { + if (batchSize.value <= 0 || features.value <= 0 || iterations.value <= 0 || warmup.value < 1 || cpuThreads.value <= 0 || gpuId.value < 0) { + throw std::runtime_error("Require positive batch, features, iterations, warmup, threads and nonnegative GPU ID"); + } + bool cuda = provider.value == "cuda"; + bool gpu = cuda || provider.value == "migraphx"; + if (!gpu && provider.value != "cpu") { + throw std::runtime_error("provider must be cpu, migraphx or cuda"); + } + if (!gpu && fp16.value) { + throw std::runtime_error("Use FP32 for the CPU baseline"); + } + std::unique_ptr memory; + uint64_t before = 0, afterWarmup = 0, afterRun = 0; + Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "tutorial-benchmark"); + Ort::SessionOptions options; + options.SetIntraOpNumThreads(cpuThreads.value); + options.SetInterOpNumThreads(1); + options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); + if (gpu) { + const std::string providerName = cuda ? "CUDAExecutionProvider" : "MIGraphXExecutionProvider"; + auto providers = Ort::GetAvailableProviders(); + if (std::find(providers.begin(), providers.end(), providerName) == providers.end()) { + throw std::runtime_error("This ONNX Runtime has no " + providerName); + } + memory = std::make_unique(gpuId.value, cuda); + before = memory->used(); + if (cuda) { + // V2 options allow disabling TF32 for the FP32 comparison. + OrtCUDAProviderOptionsV2* raw = nullptr; + Ort::ThrowOnError(Ort::GetApi().CreateCUDAProviderOptions(&raw)); + std::unique_ptr nvidia(raw, Ort::GetApi().ReleaseCUDAProviderOptions); + const std::string device = std::to_string(gpuId.value); + const char* keys[] = {"device_id", "use_tf32"}; + const char* values[] = {device.c_str(), "0"}; + Ort::ThrowOnError(Ort::GetApi().UpdateCUDAProviderOptions(nvidia.get(), keys, values, 2)); + options.AppendExecutionProvider_CUDA_V2(*nvidia); + } else { + OrtMIGraphXProviderOptions amd{}; + amd.device_id = gpuId.value; + amd.migraphx_mem_limit = std::numeric_limits::max(); + // Keep the supplied model precision; do not automatically quantize FP32. + amd.migraphx_fp16_enable = 0; + options.AppendExecutionProvider_MIGraphX(amd); + } + options.AddConfigEntry("session.disable_cpu_ep_fallback", "1"); + } + Ort::Session session(env, modelPath.value.c_str(), options); + if (session.GetInputCount() != 1 || session.GetOutputCount() < 1) { + throw std::runtime_error("Tutorial expects exactly one dense input and at least one output"); + } + auto inputInfo = session.GetInputTypeInfo(0); + auto tensorInfo = inputInfo.GetTensorTypeAndShapeInfo(); + auto shape = tensorInfo.GetShape(); + auto type = fp16.value ? ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16 : ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT; + if (tensorInfo.GetElementType() != type || shape.size() != 2 || + (shape[0] >= 0 && shape[0] != batchSize.value) || (shape[1] >= 0 && shape[1] != features.value)) { + throw std::runtime_error("Input must match fp16 flag and [batch-size, features]; only dynamic dimensions may be overridden"); + } + shape = {batchSize.value, features.value}; + const size_t count = static_cast(batchSize.value) * features.value; + if (count > std::numeric_limits::max() / sizeof(float)) { + throw std::runtime_error("Input size overflow"); + } + // Replace this feature filling with AO2D extraction and training preprocessing later. + std::vector floats(fp16.value ? 0 : count); + std::vector halves(fp16.value ? count : 0); + static_assert(sizeof(o2::OrtDataType::Float16_t) == 2); + for (size_t i = 0; i < count; ++i) { + float value = static_cast(i % 101) / 100.f - 0.5f; + if (fp16.value) { + halves[i] = o2::OrtDataType::Float16_t(value); + } else { + floats[i] = value; + } + } + auto host = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + void* data = fp16.value ? static_cast(halves.data()) : static_cast(floats.data()); + size_t bytes = count * (fp16.value ? sizeof(halves[0]) : sizeof(floats[0])); + auto input = Ort::Value::CreateTensor(host, data, bytes, shape.data(), shape.size(), type); + Ort::AllocatorWithDefaultOptions allocator; + auto inputName = session.GetInputNameAllocated(0, allocator); + const char* inputNames[] = {inputName.get()}; + std::vector names; + names.reserve(session.GetOutputCount()); + for (size_t i = 0; i < session.GetOutputCount(); ++i) { + names.emplace_back(session.GetOutputNameAllocated(i, allocator).get()); + } + std::vector outputNames; + outputNames.reserve(names.size()); + for (auto const& name : names) { + outputNames.push_back(name.c_str()); + } + Ort::RunOptions runOptions; + auto infer = [&]() { + // Default synchronous Run with CPU input/output includes host/device transfers + // and output allocation/destruction. No asynchronous Run option is enabled. + auto outputs = session.Run(runOptions, inputNames, &input, 1, outputNames.data(), outputNames.size()); + }; + for (int i = 0; i < warmup.value; ++i) { + infer(); + } + if (memory) { + afterWarmup = memory->used(); + } + auto start = std::chrono::steady_clock::now(); + for (int i = 0; i < iterations.value; ++i) { + infer(); + } + double seconds = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + if (memory) { + afterRun = memory->used(); + } + std::ofstream csv(outputCsv.value, std::ios::trunc); + if (!csv) { + throw std::runtime_error("Cannot open output-csv"); + } + csv << "model,ort_version,provider,precision,gpu_id,batch_size,features,warmup,iterations,cpu_threads,seconds,samples_per_second,gpu_used_before_bytes,gpu_used_after_warmup_bytes,gpu_used_after_run_bytes\n"; + csv << csvQuote(modelPath.value) << ',' << csvQuote(Ort::GetVersionString()) << ',' << provider.value << ',' + << (fp16.value ? "fp16" : "fp32") << ',' << (gpu ? gpuId.value : -1) << ',' + << batchSize.value << ',' << features.value << ',' << warmup.value << ',' << iterations.value << ',' << cpuThreads.value << ',' + << std::setprecision(12) << seconds << ',' << (static_cast(batchSize.value) * iterations.value / seconds) << ','; + if (memory) { + csv << before << ',' << afterWarmup << ',' << afterRun; + } else { + csv << ",,"; + } + csv << '\n'; + csv.close(); + if (!csv) { + throw std::runtime_error("Failed writing output-csv"); + } + } +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + return WorkflowSpec{adaptAnalysisTask(cfgc)}; +} diff --git a/Tutorials/OpenData/flowAnalysis.cxx b/Tutorials/OpenData/flowAnalysis.cxx index 6ab74ebfced..6f71ff609c5 100644 --- a/Tutorials/OpenData/flowAnalysis.cxx +++ b/Tutorials/OpenData/flowAnalysis.cxx @@ -87,8 +87,8 @@ struct flow_base { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - Filter collisionFilter = (aod::collision::flags & (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks) == (uint16_t)aod::collision::CollisionFlagsRun2::Run2VertexerTracks; - Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)); + Filter collisionFilter = (aod::collision::flags & static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks)) == static_cast(aod::collision::CollisionFlagsRun2::Run2VertexerTracks); + Filter trackFilter = ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))); void fillAPt(double trackpt, double cent, double vn, double sinHarm, double cosHarm) { @@ -286,7 +286,7 @@ struct flow_base { ccdb->setCaching(true); ccdb->setLocalObjectValidityChecking(); - long now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); ccdb->setCreatedNotAfter(now); // TODO must become global parameter from the train creation time } diff --git a/Tutorials/PWGCF/EventPlane/src/qVectorstutorial.cxx b/Tutorials/PWGCF/EventPlane/src/qVectorstutorial.cxx index c572356754c..b0a66be01f9 100644 --- a/Tutorials/PWGCF/EventPlane/src/qVectorstutorial.cxx +++ b/Tutorials/PWGCF/EventPlane/src/qVectorstutorial.cxx @@ -98,7 +98,7 @@ struct qVectorstutorial { } template - bool SelTrack(const TrackType track) + bool SelTrack(const TrackType& track) { if (track.pt() < cfgMinPt) return false; diff --git a/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx b/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx index d99a16fb996..8e2ef19f665 100644 --- a/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx +++ b/Tutorials/PWGCF/EventPlane/src/spectatorPlaneTutorial.cxx @@ -77,7 +77,7 @@ struct SpectatorPlaneTutorial { float dcaZMax = 2.0; Filter collisionFilter = nabs(aod::collision::posZ) < vtxZ; - Filter trackFilter = nabs(aod::track::eta) < etaMax && aod::track::pt > ptMin&& aod::track::pt < ptMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t) true)) && nabs(aod::track::dcaXY) < dcaXYMax&& nabs(aod::track::dcaZ) < dcaZMax; + Filter trackFilter = nabs(aod::track::eta) < etaMax && aod::track::pt > ptMin&& aod::track::pt < ptMax && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && nabs(aod::track::dcaXY) < dcaXYMax&& nabs(aod::track::dcaZ) < dcaZMax; using GeneralCollisions = soa::Join; using UnfilteredTracks = soa::Join; @@ -182,9 +182,9 @@ struct SpectatorPlaneTutorial { // Get QQ-correlations from CCDB if (cfgCCDBdir_QQ.value.empty() == false) { TList* list = ccdb->getForTimeStamp(cfgCCDBdir_QQ.value, bc.timestamp()); - TProfile* qAqCX = reinterpret_cast(list->FindObject("qAqCX")); - TProfile* qAqCY = reinterpret_cast(list->FindObject("qAqCY")); - TProfile* qAqCXY = reinterpret_cast(list->FindObject("qAqCXY")); + TProfile* qAqCX = dynamic_cast(list->FindObject("qAqCX")); + TProfile* qAqCY = dynamic_cast(list->FindObject("qAqCY")); + TProfile* qAqCXY = dynamic_cast(list->FindObject("qAqCXY")); // The sum is qAqCXY corrQQx = qAqCX->GetBinContent(centrality); corrQQy = qAqCY->GetBinContent(centrality); diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx index 112b4d053b5..f31e4ad46d0 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask3.cxx @@ -69,8 +69,8 @@ struct CFTutorialTask3 { // Partitions for particle 1 and particle 2 // add PID selection to partition definition - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx index fdfcf410147..28db4793c05 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask4.cxx @@ -69,8 +69,8 @@ struct CFTutorialTask4 { Configurable ConfPIDThresholdPartTwo{"ConfPIDThresholdPartTwo", 0.75, "Particle 2 - Momentum threshold for TPC to TPCTOF PID"}; /// Partitions for particle 1 and particle 2 - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx index be71808ecf5..24f5edeaf85 100644 --- a/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx +++ b/Tutorials/PWGCF/FemtoFramework/src/CFTutorialTask5.cxx @@ -73,9 +73,9 @@ struct CFTutorialTask5 { Configurable ConfPIDThresholdPartTwo{"ConfPIDThresholdPartTwo", 0.75, "Particle 2 - Momentum threshold for TPC to TPCTOF PID"}; /// Partitions for particle 1 and particle 2 - Partition PartsOne = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartOne, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartOne), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartOne)); + Partition PartsOne = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartOne) == ConfCutPartOne) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartOne, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartOne), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartOne)); - Partition PartsTwo = (aod::femtodreamparticle::partType == uint8_t(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartTwo, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartTwo), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartTwo)); + Partition PartsTwo = (aod::femtodreamparticle::partType == static_cast(aod::femtodreamparticle::ParticleType::kTrack)) && ((aod::femtodreamparticle::cut & ConfCutPartTwo) == ConfCutPartTwo) && ifnode(aod::femtodreamparticle::pt < ConfPIDThresholdPartTwo, ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCPartTwo), ncheckbit(aod::femtodreamparticle::pidcut, ConfPIDTPCTOFPartTwo)); HistogramRegistry HistRegistry{"FemtoTutorial", {}, OutputObjHandlingPolicy::AnalysisObject}; diff --git a/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx b/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx index 02d8c89d063..49dfc3e4a81 100644 --- a/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx +++ b/Tutorials/PWGCF/FlowGenericFramework/src/flowGFWTutorial.cxx @@ -64,7 +64,7 @@ struct GfwTutorial { ConfigurableAxis axisMultiplicity{"axisMultiplicity", {VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90}, "centrality axis for histograms"}; Filter collisionFilter = nabs(aod::collision::posZ) < cfgCutVertex; - Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == (uint8_t)true)) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); + Filter trackFilter = (nabs(aod::track::eta) < cfgCutEta) && (aod::track::pt > cfgCutPtMin) && (aod::track::pt < cfgCutPtMax) && ((requireGlobalTrackInFilter()) || (aod::track::isGlobalTrackSDD == static_cast(true))) && (aod::track::tpcChi2NCl < cfgCutChi2prTPCcls); // Connect to ccdb Service ccdb; diff --git a/Tutorials/PWGEM/Cocktail/plotLFCocktail.C b/Tutorials/PWGEM/Cocktail/plotLFCocktail.C index 4387b986d30..dcb56b9dfa1 100644 --- a/Tutorials/PWGEM/Cocktail/plotLFCocktail.C +++ b/Tutorials/PWGEM/Cocktail/plotLFCocktail.C @@ -30,25 +30,25 @@ TString histLegends[nHists] = { "#phi#rightarrow#etae^{+}e^{-}, #phi#rightarrow#pi^{0}e^{+}e^{-}, " "#phi#rightarrowe^{+}e^{-}"}; -void loadHistos(TFile* file, TH1F* hists[], TString name_extra, int rebin, +void loadHistos(TFile* file, TH1F* hists[], const TString& name_extra, int rebin, int nEvents) { for (int i = 0; i < nHists; i++) { - hists[i] = (TH1F*)file->GetDirectory("em-lmee-lf-cocktail") - ->Get(TString("mee") + name_extra + histNames[i]); + hists[i] = dynamic_cast(file->GetDirectory("em-lmee-lf-cocktail") + ->Get(TString("mee") + name_extra + histNames[i])); hists[i]->Rebin(rebin); hists[i]->Scale(1. / nEvents); hists[i]->SetTitle(histLegends[i]); } } -void plotLFCocktail(TString filename = "AnalysisResults.root", int rebin = 1) +void plotLFCocktail(const TString& filename = "AnalysisResults.root", int rebin = 1) { TFile* file = TFile::Open(filename.Data()); TH1I* hNEvents = - (TH1I*)file->GetDirectory("em-lmee-lf-cocktail")->Get("NEvents"); + dynamic_cast(file->GetDirectory("em-lmee-lf-cocktail")->Get("NEvents")); int nEvents = hNEvents->GetBinContent(1); loadHistos(file, mee, "", rebin, nEvents); diff --git a/Tutorials/PWGLF/Resonance/resonancesCombine.cxx b/Tutorials/PWGLF/Resonance/resonancesCombine.cxx index 9243c7cdbde..6d45fc3dd7a 100644 --- a/Tutorials/PWGLF/Resonance/resonancesCombine.cxx +++ b/Tutorials/PWGLF/Resonance/resonancesCombine.cxx @@ -66,8 +66,8 @@ struct ResonanceCombine { double massKa = o2::constants::physics::MassKPlus; EventPlaneHelper helperEP; - int evtPlRefAId = static_cast(cfgEvtPl / 10000); - int evtPlRefBId = static_cast((cfgEvtPl - evtPlRefAId * 10000) / 100); + int evtPlRefAId = (cfgEvtPl / 10000); + int evtPlRefBId = ((cfgEvtPl - evtPlRefAId * 10000) / 100); int evtPlDetId = cfgEvtPl - evtPlRefAId * 10000 - evtPlRefBId * 100; void init(o2::framework::InitContext&) @@ -93,7 +93,7 @@ struct ResonanceCombine { } template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { if (std::abs(track.pt()) < cMinPtcut) return false; diff --git a/Tutorials/PWGLF/Resonance/resonancesMicrotrack.cxx b/Tutorials/PWGLF/Resonance/resonancesMicrotrack.cxx index bcb945f8647..7b4d295e61b 100644 --- a/Tutorials/PWGLF/Resonance/resonancesMicrotrack.cxx +++ b/Tutorials/PWGLF/Resonance/resonancesMicrotrack.cxx @@ -90,7 +90,7 @@ struct ResonancesMicrotrack { } template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { if constexpr (!IsResoMicrotrack) { if (std::abs(track.pt()) < cMinPtcut) diff --git a/Tutorials/PWGLF/Resonance/resonances_step0.cxx b/Tutorials/PWGLF/Resonance/resonances_step0.cxx index 31c96c4a2ff..cb253147fd1 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step0.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step0.cxx @@ -47,7 +47,7 @@ struct resonances_tutorial { // Track selection template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -60,7 +60,7 @@ struct resonances_tutorial { template void fillHistograms(const CollisionType& /*collision*/, const TracksType& dTracks1, const TracksType& /*dTracks2*/) { - for (auto track1 : dTracks1) { // loop over all dTracks1 + for (const auto& track1 : dTracks1) { // loop over all dTracks1 if (!trackCut(track1)) continue; // track selection and PID selection histos.fill(HIST("hEta"), track1.eta()); diff --git a/Tutorials/PWGLF/Resonance/resonances_step1.cxx b/Tutorials/PWGLF/Resonance/resonances_step1.cxx index 8c8f731ea68..08ee89e003f 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step1.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step1.cxx @@ -84,7 +84,7 @@ struct resonances_tutorial { // Track selection template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -121,7 +121,7 @@ struct resonances_tutorial { void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) { auto multiplicity = collision.cent(); - for (auto track1 : dTracks1) { // loop over all dTracks1 + for (const auto& track1 : dTracks1) { // loop over all dTracks1 if (!trackCut(track1) || !selectionPID(track1)) { continue; // track selection and PID selection } @@ -133,7 +133,7 @@ struct resonances_tutorial { if (track1.hasTOF()) { histos.fill(HIST("hNsigmaKaonTOF"), track1.tofNSigmaKa()); } - for (auto track2 : dTracks2) { // loop over all dTracks2 + for (const auto& track2 : dTracks2) { // loop over all dTracks2 if (!trackCut(track2) || !selectionPID(track2)) { continue; // track selection and PID selection } diff --git a/Tutorials/PWGLF/Resonance/resonances_step2.cxx b/Tutorials/PWGLF/Resonance/resonances_step2.cxx index 9956e8d7ef2..a0c43dd03ba 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step2.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step2.cxx @@ -96,7 +96,7 @@ struct resonances_tutorial { // Track selection template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -133,7 +133,7 @@ struct resonances_tutorial { void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) { auto multiplicity = collision.cent(); - for (auto track1 : dTracks1) { // loop over all dTracks1 + for (const auto& track1 : dTracks1) { // loop over all dTracks1 if (!trackCut(track1) || !selectionPID(track1)) { continue; // track selection and PID selection } @@ -145,7 +145,7 @@ struct resonances_tutorial { if (track1.hasTOF()) { histos.fill(HIST("hNsigmaKaonTOF"), track1.tofNSigmaKa()); } - for (auto track2 : dTracks2) { // loop over all dTracks2 + for (const auto& track2 : dTracks2) { // loop over all dTracks2 if (!trackCut(track2) || !selectionPID(track2)) { continue; // track selection and PID selection } @@ -202,7 +202,7 @@ struct resonances_tutorial { void processME(o2::aod::ResoCollisions& collisions, aod::ResoTracks const& resotracks) { auto tracksTuple = std::make_tuple(resotracks); - BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}, true}; + BinningTypeVtxZT0M colBinning{{CfgVtxBins, CfgMultBins}}; SameKindPair pairs{colBinning, nEvtMixing, -1, collisions, tracksTuple, &cache}; // -1 is the number of the bin to skip for (auto& [collision1, tracks1, collision2, tracks2] : pairs) { // loop over all pairs diff --git a/Tutorials/PWGLF/Resonance/resonances_step3.cxx b/Tutorials/PWGLF/Resonance/resonances_step3.cxx index 692e4e1cd77..868073ad6d3 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step3.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step3.cxx @@ -47,7 +47,7 @@ struct resonances_tutorial { // MC particle selection template - bool ptCut(const ParticleType resoParents) + bool ptCut(const ParticleType& resoParents) { // basic pt cuts if (std::abs(resoParents.pt()) < cMinPtcut) @@ -60,7 +60,7 @@ struct resonances_tutorial { template void fillHistograms(const CollisionType& /*collision*/, const ParticleType& resoParents) { - for (auto part : resoParents) { // loop over all resoParents + for (const auto& part : resoParents) { // loop over all resoParents if (!ptCut(part)) continue; // pt selection diff --git a/Tutorials/PWGLF/Resonance/resonances_step4.cxx b/Tutorials/PWGLF/Resonance/resonances_step4.cxx index 97b588c9df6..734db8335c2 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step4.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step4.cxx @@ -63,7 +63,7 @@ struct resonances_tutorial { // MC particle selection template - bool ptCut(const ParticleType resoParents) + bool ptCut(const ParticleType& resoParents) { // basic pt cuts if (std::abs(resoParents.pt()) < cMinPtcut) diff --git a/Tutorials/PWGLF/Resonance/resonances_step5.cxx b/Tutorials/PWGLF/Resonance/resonances_step5.cxx index aaf10cca389..6eefd84e1d1 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step5.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step5.cxx @@ -80,7 +80,7 @@ struct resonances_tutorial { // Track selection template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -113,7 +113,7 @@ struct resonances_tutorial { template void fillHistograms(const CollisionType& /* collision */, const TracksType& dTracks) { - for (auto track : dTracks) { // loop over all dTracks + for (const auto& track : dTracks) { // loop over all dTracks if (!trackCut(track) || !selectionPID(track)) { continue; // track selection and PID selection } diff --git a/Tutorials/PWGLF/Resonance/resonances_step6.cxx b/Tutorials/PWGLF/Resonance/resonances_step6.cxx index 616a2648d27..115ca956329 100644 --- a/Tutorials/PWGLF/Resonance/resonances_step6.cxx +++ b/Tutorials/PWGLF/Resonance/resonances_step6.cxx @@ -103,7 +103,7 @@ struct resonances_tutorial { // Track selection template - bool trackCut(const TrackType track) + bool trackCut(const TrackType& track) { // basic track cuts if (std::abs(track.pt()) < cMinPtcut) @@ -140,7 +140,7 @@ struct resonances_tutorial { void fillHistograms(const CollisionType& collision, const TracksType& dTracks1, const TracksType& dTracks2) { auto multiplicity = collision.cent(); - for (auto track1 : dTracks1) { // loop over all dTracks1 + for (const auto& track1 : dTracks1) { // loop over all dTracks1 if (!trackCut(track1) || !selectionPID(track1)) { continue; // track selection and PID selection } @@ -152,7 +152,7 @@ struct resonances_tutorial { if (track1.hasTOF()) { histos.fill(HIST("hNsigmaKaonTOF"), track1.tofNSigmaKa()); } - for (auto track2 : dTracks2) { // loop over all dTracks2 + for (const auto& track2 : dTracks2) { // loop over all dTracks2 if (!trackCut(track2) || !selectionPID(track2)) { continue; // track selection and PID selection } diff --git a/Tutorials/PWGUD/UDTutorial_01.cxx b/Tutorials/PWGUD/UDTutorial_01.cxx index ca17b26ba7a..c95c9d9ffb1 100644 --- a/Tutorials/PWGUD/UDTutorial_01.cxx +++ b/Tutorials/PWGUD/UDTutorial_01.cxx @@ -109,7 +109,7 @@ struct UDTutorial01 { LOGF(info, " Number of tracks %d", dgtracks.size()); LOGF(info, " Number of PV contributors %d", PVContributors.size()); } - for (auto track : dgtracks) { + for (const auto& track : dgtracks) { registry.get(HIST("tracks/QCAll"))->Fill(0., 1.); registry.get(HIST("tracks/QCAll"))->Fill(1., track.hasITS() * 1.); registry.get(HIST("tracks/QCAll"))->Fill(2., track.hasTPC() * 1.); diff --git a/Tutorials/PWGUD/UDTutorial_02a.cxx b/Tutorials/PWGUD/UDTutorial_02a.cxx index dc608ee3965..14ddbe6e032 100644 --- a/Tutorials/PWGUD/UDTutorial_02a.cxx +++ b/Tutorials/PWGUD/UDTutorial_02a.cxx @@ -129,7 +129,7 @@ struct UDTutorial02a { // check PID of tracks, use nSigmaTPC // cut on track pT - for (auto trk : PVContributors) { + for (const auto& trk : PVContributors) { if (trk.tpcNSigmaPi() < -3. || trk.tpcNSigmaPi() > 3.) { if (verbose) { LOGF(info, " Candidate rejected: nSigmaTPC pion is %f", trk.tpcNSigmaPi()); @@ -148,7 +148,7 @@ struct UDTutorial02a { TParticlePDG* pion = pdg->GetParticle(211); TLorentzVector lvtmp; auto ivm = TLorentzVector(0., 0., 0., 0.); - for (auto trk : PVContributors) { + for (const auto& trk : PVContributors) { lvtmp.SetXYZM(trk.px(), trk.py(), trk.pz(), pion->Mass()); ivm += lvtmp; } @@ -166,7 +166,7 @@ struct UDTutorial02a { LOGF(info, " Candidate accepted!"); } registry.get(HIST("dgcandidates/IVMptsys"))->Fill(ivm.M(), ivm.Perp()); - for (auto trk : PVContributors) { + for (const auto& trk : PVContributors) { registry.get(HIST("dgcandidates/IVMpttrk"))->Fill(ivm.M(), trk.pt()); // fill nSigma histograms diff --git a/Tutorials/PWGUD/UDTutorial_02b.cxx b/Tutorials/PWGUD/UDTutorial_02b.cxx index 1d060b44f27..f80ccb1ccd1 100644 --- a/Tutorials/PWGUD/UDTutorial_02b.cxx +++ b/Tutorials/PWGUD/UDTutorial_02b.cxx @@ -62,7 +62,7 @@ struct UDTutorial02b { void init(InitContext&) { - anaPars = (DGAnaparHolder)DGPars; + anaPars = DGAnaparHolder(DGPars); pidsel.init(anaPars); if (verbose) { LOGF(info, ""); diff --git a/Tutorials/PWGUD/UDTutorial_03a.cxx b/Tutorials/PWGUD/UDTutorial_03a.cxx index a133a359053..8ddf8df2d6e 100644 --- a/Tutorials/PWGUD/UDTutorial_03a.cxx +++ b/Tutorials/PWGUD/UDTutorial_03a.cxx @@ -104,7 +104,7 @@ struct UDTutorial03a { } // check if a reconstructed track is a muon candidate - bool isMuonCandidate_rec(TC track) + bool isMuonCandidate_rec(const TC& track) { if (abs(track.tpcNSigmaMu()) > 3.) { return false; @@ -142,7 +142,7 @@ struct UDTutorial03a { // and be muon candidates int netCharge = 0; int ind = -1; - for (auto track : tracks) { + for (const auto& track : tracks) { ind++; if (track.isPVContributor()) { if (!isMuonCandidate_rec(track)) { @@ -297,7 +297,7 @@ struct UDTutorial03a { TLorentzVector* lv_gen = new TLorentzVector(); // loop over all genererated collisions - for (auto mccollision : mccollisions) { + for (const auto& mccollision : mccollisions) { registry.get(HIST("MC/Stat"))->Fill(0., 1.); // get McParticles which belong to mccollision @@ -350,7 +350,7 @@ struct UDTutorial03a { registry.get(HIST("Reco/Stat"))->Fill(0., 1.); registry.get(HIST("Reco/nTracks"))->Fill(tracks.size(), 1.); int nContributors = 0; - for (auto track : tracks) { + for (const auto& track : tracks) { if (track.isPVContributor()) { nContributors++; } diff --git a/Tutorials/PWGUD/UDTutorial_03b.cxx b/Tutorials/PWGUD/UDTutorial_03b.cxx index beee8578225..7722fe7d419 100644 --- a/Tutorials/PWGUD/UDTutorial_03b.cxx +++ b/Tutorials/PWGUD/UDTutorial_03b.cxx @@ -116,7 +116,7 @@ struct UDTutorial03b { } // check if a reconstructed track represents a muon candidate - bool isMuonCandidate_rec(TC track) + bool isMuonCandidate_rec(const TC& track) { if (abs(track.tpcNSigmaMu()) > 3.) { return false; @@ -140,7 +140,7 @@ struct UDTutorial03b { // find the McParticles belongin to given tracks template - std::vector getDaughterParts_rec(TCs const& tracks, std::vector trackIds, MCTrack const& /*parts*/) + std::vector getDaughterParts_rec(TCs const& tracks, const std::vector& trackIds, MCTrack const& /*parts*/) { std::vector emptySelection; std::vector selectedParts; @@ -158,7 +158,7 @@ struct UDTutorial03b { // retrieve the reconstructed tracks which are associated with the given McParticles template - std::vector getDaughterTracks_gen(McPart const& parts, std::vector partIds, TCs const& tracks) + std::vector getDaughterTracks_gen(McPart const& parts, const std::vector& partIds, TCs const& tracks) { // return a vector of track indices std::vector emptySelection; @@ -173,7 +173,7 @@ struct UDTutorial03b { if (trs.size() > 1) { LOGF(info, "%d tracks belong to same McParticle!", trs.size()); } - for (auto tr : trs) { + for (const auto& tr : trs) { selectedTracks.push_back(tr.globalIndex()); } } @@ -196,7 +196,7 @@ struct UDTutorial03b { // and be muon candidates int netCharge = 0; int ind = -1; - for (auto track : tracks) { + for (const auto& track : tracks) { ind++; if (track.isPVContributor()) { if (!isMuonCandidate_rec(track)) { @@ -328,7 +328,7 @@ struct UDTutorial03b { TLorentzVector* lv_rec = new TLorentzVector(); // loop over all generated collisions - for (auto mccollision : mccollisions) { + for (const auto& mccollision : mccollisions) { registry.get(HIST("MC/Stat"))->Fill(0., 1.); // get reconstructed collision which belongs to mccollision @@ -400,14 +400,14 @@ struct UDTutorial03b { registry.get(HIST("MC/selMPt"))->Fill(lv_rec->M(), lv_rec->Pt(), 1.); // compute the difference between generated and reconstructed particle momentum - for (auto McPart : partSlice) { + for (const auto& McPart : partSlice) { // get track which corresponds to McPart auto trackSlice = tracks.sliceBy(trackPerMcParticle, McPart.globalIndex()); registry.get(HIST("MC/nRecTracks"))->Fill(trackSlice.size(), 1.); // are there reconstructed tracks? if (trackSlice.size() > 0) { - for (auto track : trackSlice) { + for (const auto& track : trackSlice) { auto pTrack = track.p(); auto pPart = McPart.p(); auto pDiff = pTrack - pPart; @@ -436,7 +436,7 @@ struct UDTutorial03b { registry.get(HIST("Reco/Stat"))->Fill(0., 1.); registry.get(HIST("Reco/nTracks"))->Fill(tracks.size(), 1.); int nContributors = 0; - for (auto track : tracks) { + for (const auto& track : tracks) { if (track.isPVContributor()) { nContributors++; } @@ -489,7 +489,7 @@ struct UDTutorial03b { } // compute the difference between generated and reconstructed momentum - for (auto track : tracks) { + for (const auto& track : tracks) { // is there an associated McParticle? if (track.has_mcParticle()) { auto pTrack = track.p(); diff --git a/Tutorials/PWGUD/UDTutorial_04.cxx b/Tutorials/PWGUD/UDTutorial_04.cxx index f2220ad17e1..cf5a914caeb 100644 --- a/Tutorials/PWGUD/UDTutorial_04.cxx +++ b/Tutorials/PWGUD/UDTutorial_04.cxx @@ -113,7 +113,7 @@ struct UDTutorial04 { } // check if a reconstructed track represents a muon candidate - bool isMuonCandidate_rec(TC track) + bool isMuonCandidate_rec(const TC& track) { if (abs(track.tpcNSigmaMu()) > 3.) { return false; @@ -137,7 +137,7 @@ struct UDTutorial04 { // find the McParticles belongin to given tracks template - std::vector getDaughterParts_rec(TCs const& tracks, std::vector trackIds, MCTrack const& /*parts*/) + std::vector getDaughterParts_rec(TCs const& tracks, const std::vector& trackIds, MCTrack const& /*parts*/) { std::vector emptySelection; std::vector selectedParts; @@ -155,7 +155,7 @@ struct UDTutorial04 { // retrieve the reconstructed tracks which are associated with the given McParticles template - std::vector getDaughterTracks_gen(McPart const& parts, std::vector partIds, TCs const& tracks) + std::vector getDaughterTracks_gen(McPart const& parts, const std::vector& partIds, TCs const& tracks) { // return a vector of track indices std::vector emptySelection; @@ -170,7 +170,7 @@ struct UDTutorial04 { if (trs.size() > 1) { LOGF(info, "%d tracks belong to same McParticle!", trs.size()); } - for (auto tr : trs) { + for (const auto& tr : trs) { selectedTracks.push_back(tr.globalIndex()); } } @@ -193,7 +193,7 @@ struct UDTutorial04 { // and be muon candidates int netCharge = 0; int ind = -1; - for (auto track : tracks) { + for (const auto& track : tracks) { ind++; if (track.isPVContributor()) { if (!isMuonCandidate_rec(track)) { @@ -325,7 +325,7 @@ struct UDTutorial04 { TLorentzVector* lv_rec = new TLorentzVector(); // loop over all generated collisions - for (auto mccollision : mccollisions) { + for (const auto& mccollision : mccollisions) { registry.get(HIST("MC/Stat"))->Fill(0., 1.); // get reconstructed collision which belongs to mccollision @@ -400,14 +400,14 @@ struct UDTutorial04 { registry.get(HIST("MC/selMPt"))->Fill(lv_rec->M(), lv_rec->Pt(), 1.); // compute the difference between generated and reconstructed particle momentum - for (auto McPart : partSlice) { + for (const auto& McPart : partSlice) { // get track which corresponds to McPart auto trackSlice = tracks.sliceBy(trackPerMcParticle, McPart.globalIndex()); registry.get(HIST("MC/nRecTracks"))->Fill(trackSlice.size(), 1.); // compute momentum difference between MCTruth and Reconstruction if (trackSlice.size() > 0) { - for (auto track : trackSlice) { + for (const auto& track : trackSlice) { auto pTrack = sqrt(track.px() * track.px() + track.py() * track.py() + track.pz() * track.pz()); auto pPart = sqrt(McPart.px() * McPart.px() + McPart.py() * McPart.py() + McPart.pz() * McPart.pz()); auto pDiff = pTrack - pPart; @@ -490,7 +490,7 @@ struct UDTutorial04 { } // compute the difference between generated and reconstructed momentum - for (auto track : tracks) { + for (const auto& track : tracks) { // is there an associated McParticle? if (track.has_udMcParticle()) { auto pTrack = sqrt(track.px() * track.px() + track.py() * track.py() + track.pz() * track.pz()); diff --git a/Tutorials/PWGUD/UDTutorial_05.cxx b/Tutorials/PWGUD/UDTutorial_05.cxx index 36ccc7590b0..d99476f8728 100644 --- a/Tutorials/PWGUD/UDTutorial_05.cxx +++ b/Tutorials/PWGUD/UDTutorial_05.cxx @@ -178,7 +178,7 @@ struct UDTutorial05 { registry.fill(HIST("hTracks"), tracks.size()); - for (auto t : tracks) { + for (const auto& t : tracks) { // Apply good track selection criteria if (!trackselector(t, parameters)) continue; @@ -205,7 +205,7 @@ struct UDTutorial05 { registry.fill(HIST("hTracksPions"), onlyPionTracks.size()); //_____________________________________ // Adding all onlypiontracks - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { p += pion; } diff --git a/Tutorials/PWGUD/UDTutorial_07.cxx b/Tutorials/PWGUD/UDTutorial_07.cxx index f9a7ffee4a4..db27a667c37 100644 --- a/Tutorials/PWGUD/UDTutorial_07.cxx +++ b/Tutorials/PWGUD/UDTutorial_07.cxx @@ -175,7 +175,7 @@ struct UDTutorial07 { registry.fill(HIST("hSelectionCounter"), 3); - for (auto t : tracks) { + for (const auto& t : tracks) { // Apply good track selection criteria if (!trackselector(t, parameters)) @@ -208,11 +208,11 @@ struct UDTutorial07 { registry.fill(HIST("hSelectionCounter"), 4); // Creating rhos - for (auto pion : onlyPionTracks) { + for (const auto& pion : onlyPionTracks) { p += pion; } - for (auto rtrk : rawPionTracks) { + for (const auto& rtrk : rawPionTracks) { TLorentzVector itrk; itrk.SetXYZM(rtrk.px(), rtrk.py(), rtrk.pz(), o2::constants::physics::MassPionCharged); trackpt.push_back(itrk.Pt()); @@ -223,7 +223,7 @@ struct UDTutorial07 { } int sign = 0; - for (auto rawPion : rawPionTracks) { + for (const auto& rawPion : rawPionTracks) { sign += rawPion.sign(); } // Filling tree, make to be consistent with the declared tables diff --git a/Tutorials/Skimming/spectraNucleiAnalyser.cxx b/Tutorials/Skimming/spectraNucleiAnalyser.cxx index 9ef8fbe3e82..b84fedcec31 100644 --- a/Tutorials/Skimming/spectraNucleiAnalyser.cxx +++ b/Tutorials/Skimming/spectraNucleiAnalyser.cxx @@ -50,7 +50,7 @@ struct NucleiSpectraAnalyserTask { spectra.fill(HIST("fCollZpos"), collision.posZ()); - for (auto track : tracks) { // start loop over tracks + for (const auto& track : tracks) { // start loop over tracks TLorentzVector cutVector{}; cutVector.SetPtEtaPhiM(track.pt() * 2.0, track.eta(), track.phi(), constants::physics::MassHelium3); if (cutVector.Rapidity() < yMin + yBeam || cutVector.Rapidity() > yMax + yBeam) { diff --git a/Tutorials/Skimming/spectraNucleiProvider.cxx b/Tutorials/Skimming/spectraNucleiProvider.cxx index 404169f202d..dd160c7ea54 100644 --- a/Tutorials/Skimming/spectraNucleiProvider.cxx +++ b/Tutorials/Skimming/spectraNucleiProvider.cxx @@ -82,7 +82,7 @@ struct NucleiSpectraProviderTask { outputCollisions(collision.posZ()); uint32_t pNsigma = 0xFFFFFF00; // 15 bit precision for Nsigma - does this respect the sign? outputTracks.reserve(tracks.size()); - for (auto track : tracks) { + for (const auto& track : tracks) { outputTracks(outputCollisions.lastIndex(), track.pt(), track.eta(), track.phi(), // truncateFloatFraction(track.tpcNSigmaEl(), pNsigma), truncateFloatFraction(track.tpcNSigmaMu(), pNsigma), truncateFloatFraction(track.tpcNSigmaPi(), pNsigma), truncateFloatFraction(track.tpcNSigmaKa(), pNsigma), diff --git a/Tutorials/Skimming/spectraNucleiReference.cxx b/Tutorials/Skimming/spectraNucleiReference.cxx index cbca33c2611..76ee84557e0 100644 --- a/Tutorials/Skimming/spectraNucleiReference.cxx +++ b/Tutorials/Skimming/spectraNucleiReference.cxx @@ -84,7 +84,7 @@ struct NucleiSpectraReferenceTask { // spectra.fill(HIST("fCollZpos"), collision.posZ()); // - for (auto track : tracks) { // start loop over tracks + for (const auto& track : tracks) { // start loop over tracks TLorentzVector cutVector{}; cutVector.SetPtEtaPhiM(track.pt() * 2.0, track.eta(), track.phi(), constants::physics::MassHelium3); diff --git a/Tutorials/Skimming/spectraTPCReference.cxx b/Tutorials/Skimming/spectraTPCReference.cxx index db02e2c6109..3f9d36a70ee 100644 --- a/Tutorials/Skimming/spectraTPCReference.cxx +++ b/Tutorials/Skimming/spectraTPCReference.cxx @@ -85,7 +85,7 @@ struct TPCSpectraReferenceTask { aod::TrackSelection>>; void process(soa::Filtered::iterator const& /*collision*/, TrackCandidates const& tracks) { - for (auto track : tracks) { + for (const auto& track : tracks) { const float nsigma[Np] = {track.tpcNSigmaEl(), track.tpcNSigmaMu(), track.tpcNSigmaPi(), track.tpcNSigmaKa(), track.tpcNSigmaPr(), track.tpcNSigmaDe(), track.tpcNSigmaTr(), track.tpcNSigmaHe(), track.tpcNSigmaAl()}; diff --git a/Tutorials/include/configurableCut.h b/Tutorials/include/configurableCut.h index d384e8ba5e9..7402ea4b56d 100644 --- a/Tutorials/include/configurableCut.h +++ b/Tutorials/include/configurableCut.h @@ -18,6 +18,7 @@ #include #include +#include #include static constexpr double default_matrix[3][3] = {{1.1, 1.2, 1.3}, {2.1, 2.2, 2.3}, {3.1, 3.2, 3.3}}; @@ -29,7 +30,7 @@ class configurableCut std::vector bins_ = {0.5, 1.5, 2.5}, std::vector labels_ = {"l1", "l2", "l3"}, o2::framework::Array2D cuts_ = {&default_matrix[0][0], 3, 3}) - : cut{cut_}, state{state_}, option{option_}, bins{bins_}, labels{labels_}, cuts{cuts_} + : cut{cut_}, state{state_}, option{option_}, bins{std::move(bins_)}, labels{std::move(labels_)}, cuts{std::move(cuts_)} { } diff --git a/Tutorials/src/ConfigurableCutLinkDef.h b/Tutorials/src/ConfigurableCutLinkDef.h index f2667e6b6cc..397ee540c36 100644 --- a/Tutorials/src/ConfigurableCutLinkDef.h +++ b/Tutorials/src/ConfigurableCutLinkDef.h @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +// NOLINT(build/header_guard) + #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; diff --git a/Tutorials/src/associatedExample.cxx b/Tutorials/src/associatedExample.cxx index c8c87debff1..1aba1e01905 100644 --- a/Tutorials/src/associatedExample.cxx +++ b/Tutorials/src/associatedExample.cxx @@ -150,7 +150,7 @@ struct PartitionColExtra { struct BuildHmpidIndex { // build the index table HMPIDTracksIndex Builds idx; - void init(InitContext const&) {}; + void init(InitContext const&) {} }; struct ConsumeHmpidIndex { diff --git a/Tutorials/src/ccdbaccess.cxx b/Tutorials/src/ccdbaccess.cxx index e1ea568f46f..0a987fae790 100644 --- a/Tutorials/src/ccdbaccess.cxx +++ b/Tutorials/src/ccdbaccess.cxx @@ -29,6 +29,7 @@ #include #include +#include #include using namespace o2::framework; @@ -39,7 +40,7 @@ struct TimestampUserTask { Service ccdb; Configurable path{"ccdb-path", "qc/TOF/TOFTaskCompressed/hDiagnostic", "path to the ccdb object"}; Configurable url{"ccdb-url", "http://ccdb-test.cern.ch:8080", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; void init(o2::framework::InitContext&) { diff --git a/Tutorials/src/ccdbtableaccess.cxx b/Tutorials/src/ccdbtableaccess.cxx index 5c703e4d1dd..3cd86168e8f 100644 --- a/Tutorials/src/ccdbtableaccess.cxx +++ b/Tutorials/src/ccdbtableaccess.cxx @@ -21,6 +21,9 @@ #include #include +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) +#include // IWYU pragma: keep (needed by DECLARE_SOA_CCDB_COLUMN...) + using namespace o2::framework; using namespace o2::header; using namespace o2; diff --git a/Tutorials/src/compatibleBCs.cxx b/Tutorials/src/compatibleBCs.cxx index bf5acbdb147..7975eca9d32 100644 --- a/Tutorials/src/compatibleBCs.cxx +++ b/Tutorials/src/compatibleBCs.cxx @@ -60,7 +60,7 @@ T getCompatibleBCs(aod::Collision const& collision, T const& bcs) LOGF(info, "Will consider BC entries from %d to %d", minBCId, maxBCId); - T slice{{bcs.asArrowTable()->Slice(minBCId, maxBCId - minBCId + 1)}, (uint64_t)minBCId}; + auto slice = bcs.rawSlice(minBCId, maxBCId); bcs.copyIndexBindings(slice); return slice; } diff --git a/Tutorials/src/configurableObjects.cxx b/Tutorials/src/configurableObjects.cxx index e1a758b1851..ad02482613d 100644 --- a/Tutorials/src/configurableObjects.cxx +++ b/Tutorials/src/configurableObjects.cxx @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -63,10 +64,8 @@ auto printMatrix(Array2D const& m) return ss.str(); } -static constexpr float defaultm[3][4] = {{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}; -static LabeledArray la{&defaultm[0][0], 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}; -const std::string defaultmS[3][4] = {{"One.One", "One.Two", "One.Three", "One.Four"}, {"Two.One", "Two.Two", "Two.Three", "Two.Four"}, {"Three.One", "Three.Two", "Three.Three", "Three.Four"}}; -static LabeledArray laS{&defaultmS[0][0], 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}; +static constexpr std::array, 3> defaultm{{{1.1, 1.2, 1.3, 1.4}, {2.1, 2.2, 2.3, 2.4}, {3.1, 3.2, 3.3, 3.4}}}; +static constexpr std::array, 3> defaultmS{{{"One.One", "One.Two", "One.Three", "One.Four"}, {"Two.One", "Two.Two", "Two.Three", "Two.Four"}, {"Three.One", "Three.Two", "Three.Three", "Three.Four"}}}; struct ConfigurableObjectDemo { // Simple type configurables @@ -82,9 +81,9 @@ struct ConfigurableObjectDemo { Configurable> array{"array", {0, 0, 0, 0, 0, 0, 0}, "generic int array"}; Configurable> farray{"farray", {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}, "generic float array"}; Configurable> darray{"darray", {0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1}, "generic double array"}; - Configurable> vmatrix{"matrix", {&defaultm[0][0], 3, 4}, "generic matrix"}; - Configurable> vla{"vla", {defaultm[0], 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}, "labeled array with float content"}; - Configurable> vlaS{"vlaS", {defaultmS[0], 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}, "labeled array with string content"}; + Configurable> vmatrix{"matrix", {defaultm.front().data(), 3, 4}, "generic matrix"}; + Configurable> vla{"vla", {defaultm.front().data(), 3, 4, {"r 1", "r 2", "r 3"}, {"c 1", "c 2", "c 3", "c 4"}}, "labeled array with float content"}; + Configurable> vlaS{"vlaS", {defaultmS.front().data(), 3, 4, {"rS 1", "rS 2", "rS 3"}, {"cS 1", "cS 2", "cS 3", "cS 4"}}, "labeled array with string content"}; // Configurables can be grouped into `ConfigurableGroup`s. // Their names must be unique. @@ -99,11 +98,11 @@ struct ConfigurableObjectDemo { LOGF(info, "max_eta: %f; min_clusters: %d", (float)trackcuts.max_eta, (int)trackcuts.min_clusters); LOGF(info, "Cut1 bins: %s; Cut2 bins: %s", printArray(cut->getBins()), printArray(mutable_cut->getBins())); LOGF(info, "Cut1 labels: %s; Cut2 labels: %s", printArray(cut->getLabels()), printArray(mutable_cut->getLabels())); - auto vec = (std::vector)array; + auto vec = std::vector(array); LOGF(info, "Array: %s", printArray(vec).c_str()); - auto dvec = (std::vector)darray; + auto dvec = std::vector(darray); LOGF(info, "Double array: %s", printArray(dvec).c_str()); - auto fvec = (std::vector)farray; + auto fvec = std::vector(farray); LOGF(info, "Float array: %s", printArray(fvec).c_str()); LOGF(info, "Matrix: %s", printMatrix((Array2D)vmatrix)); LOGF(info, "Labeled float array:\n %s\n %s\n %s", printArray(vla->getLabelsRows()), printArray(vla->getLabelsCols()), printMatrix(vla->getData())); diff --git a/Tutorials/src/efficiencyGlobal.cxx b/Tutorials/src/efficiencyGlobal.cxx index 13e1fdb9df3..a697162622b 100644 --- a/Tutorials/src/efficiencyGlobal.cxx +++ b/Tutorials/src/efficiencyGlobal.cxx @@ -28,6 +28,7 @@ #include #include +#include #include using namespace o2::framework; @@ -37,7 +38,7 @@ struct EfficiencyGlobal { Service ccdb; Configurable path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; OutputObj pt{TH1F("pt", "pt", 20, 0., 10.)}; diff --git a/Tutorials/src/efficiencyPerRun.cxx b/Tutorials/src/efficiencyPerRun.cxx index 7cbebd815ab..944b7fbb105 100644 --- a/Tutorials/src/efficiencyPerRun.cxx +++ b/Tutorials/src/efficiencyPerRun.cxx @@ -31,6 +31,7 @@ #include #include +#include #include using namespace o2::framework; @@ -40,7 +41,7 @@ struct EfficiencyPerRun { Service ccdb; Configurable path{"ccdb-path", "Users/j/jgrosseo/tutorial/efficiency/simple", "base path to the ccdb object"}; Configurable url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; - Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; + Configurable nolaterthan{"ccdb-no-later-than", std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), "latest acceptable timestamp of creation for the object"}; OutputObj pt{TH1F("pt", "pt", 20, 0., 10.)}; diff --git a/Tutorials/src/eventMixing.cxx b/Tutorials/src/eventMixing.cxx index 925cc9f03a7..379532105ab 100644 --- a/Tutorials/src/eventMixing.cxx +++ b/Tutorials/src/eventMixing.cxx @@ -51,7 +51,7 @@ struct MixedEvents { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) { @@ -86,7 +86,7 @@ struct MixedEventsInsideProcess { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; void process(aod::Collisions const& collisions, aod::Tracks const& tracks) @@ -124,7 +124,7 @@ struct MixedEventsFilteredTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -157,7 +157,7 @@ struct MixedEventsJoinedCollisions { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(AODCollisions const& collisions, aod::Tracks const& tracks, aod::BCsWithTimestamps const&) @@ -190,7 +190,7 @@ struct MixedEventsDynamicColumns { std::vector zBins{7, -7, 7}; std::vector multBins{VARIABLE_WIDTH, 0, 5, 10, 20, 30, 40, 50, 100.1}; using BinningType = ColumnBinningPolicy>; - BinningType corrBinning{{zBins, multBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType corrBinning{{zBins, multBins}}; SameKindPair pair{corrBinning, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(AODCollisions const& collisions, aod::Tracks const& tracks) @@ -223,7 +223,7 @@ struct MixedEventsVariousKinds { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; Pair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks, aod::V0s const& v0s) @@ -258,7 +258,7 @@ struct MixedEventsTriple { std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; std::vector zBins{7, -7, 7}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins, zBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins, zBins}}; SameKindTriple triple{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) @@ -292,7 +292,7 @@ struct MixedEventsTripleVariousKinds { std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; std::vector zBins{7, -7, 7}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins, zBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins, zBins}}; Triple triple{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks, aod::V0s const& v0s) @@ -401,7 +401,7 @@ struct MixedEventsPartitionedTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -453,7 +453,7 @@ struct MixedEventsLambdaBinning { }; using BinningType = FlexibleBinningPolicy, aod::collision::PosZ, decltype(getTracksSize)>; - BinningType binningWithLambda{{getTracksSize}, {axisVertex, axisMultiplicity}, true}; + BinningType binningWithLambda{{getTracksSize}, {axisVertex, axisMultiplicity}}; auto tracksTuple = std::make_tuple(tracks); SameKindPair pair{binningWithLambda, 5, -1, collisions, tracksTuple, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored @@ -490,8 +490,8 @@ struct MixedEventsCounters { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; - // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; + SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under / overflow(-1) to be ignored void process(aod::Collisions const& collisions, aod::Tracks const& tracks) diff --git a/Tutorials/src/eventMixingValidation.cxx b/Tutorials/src/eventMixingValidation.cxx index f7cf702f5d0..3b026e2c48e 100644 --- a/Tutorials/src/eventMixingValidation.cxx +++ b/Tutorials/src/eventMixingValidation.cxx @@ -40,7 +40,7 @@ struct MixedEventsEmptyTables { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, MyTracks const& tracks) @@ -72,7 +72,7 @@ struct MixedEventsJoinedTracks { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; using BinningType = ColumnBinningPolicy; - BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) + BinningType binningOnPositions{{xBins, yBins}}; SameKindPair pair{binningOnPositions, 5, -1, &cache}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored void process(aod::Collisions const& collisions, aod::FullTracks const& tracks) @@ -104,7 +104,7 @@ struct MixedEventsJoinedTracks { // std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; // std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; // using BinningType = ColumnBinningPolicy; -// BinningType binningOnPositions{{xBins, yBins}, true}; // true is for 'ignore overflows' (true by default) +// BinningType binningOnPositions{{xBins, yBins}}; // SameKindPair pair{binningOnPositions, 5, -1}; // indicates that 5 events should be mixed and under/overflow (-1) to be ignored // // void process(aod::Collisions const& collisions, aod::Tracks const& tracks) diff --git a/Tutorials/src/extendedTables.cxx b/Tutorials/src/extendedTables.cxx index 96bf680ff28..574d9edb2d0 100644 --- a/Tutorials/src/extendedTables.cxx +++ b/Tutorials/src/extendedTables.cxx @@ -121,7 +121,7 @@ struct SpawnDynamicColumns { void init(InitContext&) { - cextable.projectors[0] = (float)factor * aod::track::p * aod::track::p; + cextable.projectors[0] = factor.value * aod::track::p * aod::track::p; } void process(aod::Tracks const& tracks) diff --git a/Tutorials/src/preprocess.cxx b/Tutorials/src/preprocess.cxx index 8a3bc43be05..14513311028 100644 --- a/Tutorials/src/preprocess.cxx +++ b/Tutorials/src/preprocess.cxx @@ -28,6 +28,7 @@ #include #include +#include using namespace o2; using namespace o2::framework; diff --git a/Tutorials/src/reweighting.cxx b/Tutorials/src/reweighting.cxx index 48e90a9560d..4319a587858 100644 --- a/Tutorials/src/reweighting.cxx +++ b/Tutorials/src/reweighting.cxx @@ -51,6 +51,7 @@ #include #include #include +#include #include using namespace o2; @@ -106,7 +107,7 @@ struct CreateWeights { void init(InitContext&) { - auto path = (std::string)onnxModel; + auto path = std::string(onnxModel); /// create session onnxSession = std::make_shared(env, path.c_str(), sessionOptions); /// adjust input shape to use row-by-row model application diff --git a/Tutorials/src/tracksCombinations.cxx b/Tutorials/src/tracksCombinations.cxx index a4ecbf1047e..f3a22c03d73 100644 --- a/Tutorials/src/tracksCombinations.cxx +++ b/Tutorials/src/tracksCombinations.cxx @@ -60,7 +60,7 @@ struct TrackCombinations { struct BinnedTrackCombinations { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; - ColumnBinningPolicy trackBinning{{xBins, yBins}, true}; + ColumnBinningPolicy trackBinning{{xBins, yBins}}; void process(aod::Tracks const& tracks) { @@ -83,7 +83,7 @@ struct ConfigurableBinnedCollisionCombinations { void process(soa::Join const& collisions) { using BinningType = ColumnBinningPolicy>; - BinningType colBinning{{axisVertex, axisMultiplicity}, true}; + BinningType colBinning{{axisVertex, axisMultiplicity}}; int count = 0; // Strictly upper tracks binned by x and y position @@ -100,7 +100,7 @@ struct ConfigurableBinnedCollisionCombinations { struct BinnedTrackPartitionsCombinations { std::vector xBins{VARIABLE_WIDTH, -0.064, -0.062, -0.060, 0.066, 0.068, 0.070, 0.072}; std::vector yBins{VARIABLE_WIDTH, -0.320, -0.301, -0.300, 0.330, 0.340, 0.350, 0.360}; - ColumnBinningPolicy trackBinning{{xBins, yBins}, true}; + ColumnBinningPolicy trackBinning{{xBins, yBins}}; Configurable philow{"phiLow", 1.0f, "lowest phi"}; void process(aod::Tracks const& tracks) diff --git a/cmake/O2PhysicsAddWorkflow.cmake b/cmake/O2PhysicsAddWorkflow.cmake index d2d599c4406..6b8c46ef0da 100644 --- a/cmake/O2PhysicsAddWorkflow.cmake +++ b/cmake/O2PhysicsAddWorkflow.cmake @@ -47,8 +47,48 @@ function(o2physics_add_dpl_workflow baseTargetName) set_property(TARGET ${targetExeName} PROPERTY JOB_POOL_COMPILE analysis) set_property(TARGET ${targetExeName} PROPERTY JOB_POOL_LINK analysis) - if(A_REUSE_FROM AND NOT DEFINED ENV{USE_RECC}) - target_precompile_headers(${targetExeName} REUSE_FROM ${A_REUSE_FROM}) + # Reuse a precompiled header. Without an explicit REUSE_FROM, fall back to the + # shared AnalysisPCH (Common/Core): a workflow translation unit spends most of + # its time parsing the framework headers, and there are ~1500 of them, so the + # default is worth more than the handful of targets that name their own. + # + # Set O2PHYSICS_DEFAULT_PCH to an empty string to opt out globally, e.g. when + # bisecting a PCH-related build failure. + if(NOT DEFINED O2PHYSICS_DEFAULT_PCH) + set(O2PHYSICS_DEFAULT_PCH AnalysisPCH) + endif() + set(_pch "${A_REUSE_FROM}") + if(NOT _pch) + set(_pch "${O2PHYSICS_DEFAULT_PCH}") + endif() + # A target cannot reuse its own PCH, and the carrier is not built when recc + # is caching compilations remotely instead. + if(_pch AND NOT _pch STREQUAL targetExeName AND NOT DEFINED ENV{USE_RECC}) + # GCC refuses a PCH built with a different preprocessor state than the + # consumer's, and -Werror turns that refusal into a build failure: + # cmake_pch.hxx.gch: not used because `RANS_ENABLE_JSON' not defined + # The carrier links O2Physics::AnalysisCore, which reaches O2::rANS and its + # INTERFACE -DRANS_ENABLE_JSON, while a workflow that links only + # O2::Framework (the converters, the tutorials) never sees it. + # + # Copying COMPILE_DEFINITIONS fixed that case and revealed another: CI then + # failed on `_REENTRANT' not defined, on a compile line that DID carry + # -DRANS_ENABLE_JSON -- so the copy was working and simply does not reach + # far enough. Whatever supplies _REENTRANT arrives at the carrier as + # something other than a compile definition (-pthread, which travels in + # INTERFACE_COMPILE_OPTIONS, is the likely route), so adding options alone + # would only move the goalposts to whichever kind of usage requirement goes + # missing next. + # + # $ applies a target's *compile* usage requirements -- + # definitions, options, include directories, features -- without placing it + # on the link line or creating a link dependency, which is the constraint + # that ruled out simply linking the carrier. It transfers the whole + # preprocessor state the carrier compiled with, and that state is exactly + # what GCC compares, rather than one property of it at a time. + # Requires CMake >= 3.27. + target_link_libraries(${targetExeName} PRIVATE $) + target_precompile_headers(${targetExeName} REUSE_FROM ${_pch}) endif() set(jsonFile $.json) diff --git a/cppcheck_config b/cppcheck_suppressions similarity index 58% rename from cppcheck_config rename to cppcheck_suppressions index 506d113f69d..fe4e5d9b50a 100644 --- a/cppcheck_config +++ b/cppcheck_suppressions @@ -1,5 +1,9 @@ +functionStatic +missingInclude +missingIncludeSystem syntaxError unknownMacro -missingIncludeSystem -missingInclude +unreadVariable unusedStructMember:*.h +useStlAlgorithm +variableScope diff --git a/dependencies/O2PhysicsCompileFlags.cmake b/dependencies/O2PhysicsCompileFlags.cmake index 20078b607f7..a68cf793aa3 100644 --- a/dependencies/O2PhysicsCompileFlags.cmake +++ b/dependencies/O2PhysicsCompileFlags.cmake @@ -14,10 +14,10 @@ include_guard() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") # Enabled warnings supported by Clang and GCC, not treated as errors -set(O2PHYSICS_WARNINGS_COMMON_NO_ERROR "") +set(O2PHYSICS_WARNINGS_COMMON_NO_ERROR "deprecated-declarations") # Enabled warnings supported by Clang only, not treated as errors -set(O2PHYSICS_WARNINGS_CLANG_NO_ERROR "") +set(O2PHYSICS_WARNINGS_CLANG_NO_ERROR "vla-extension") # Enabled warnings supported by GCC only, not treated as errors set(O2PHYSICS_WARNINGS_GCC_NO_ERROR "") @@ -80,12 +80,38 @@ IF (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RELWITHDEBINFO) ENDIF (NOT CMAKE_BUILD_TYPE) +# Adjust the per-config flags rather than replacing them. These used to be plain +# set() calls, which create normal variables that shadow the cache for the rest of +# the configure -- so whatever the build recipe passed as +# -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=... was silently discarded. alidist asks for +# "-O2 -g1 -DNDEBUG" (overridable via O2PHYSICS_DEBUG_FLAGS) and got -g anyway; +# full debug info costs ~18% of total compile time across the tree, because the +# register allocator's rewrite pass scales with the number of debug records. +# Here we only enforce what this project actually cares about: -pipe, and whether +# NDEBUG is defined. The optimisation and debug-info levels stay as given. +function(o2physics_adjust_config_flags varname want_ndebug) + set(flags "${${varname}}") + if(want_ndebug) + if(NOT flags MATCHES "(^| )-DNDEBUG($| )") + string(APPEND flags " -DNDEBUG") + endif() + else() + # ENABLE_CASSERT: the CI wants assertions live. + string(REGEX REPLACE "(^| )-DNDEBUG($| )" " " flags "${flags}") + endif() + if(NOT flags MATCHES "(^| )-pipe($| )") + string(APPEND flags " -pipe") + endif() + string(STRIP "${flags}" flags) + set(${varname} "${flags}" PARENT_SCOPE) +endfunction() + IF(ENABLE_CASSERT) #For the CI, we want to have assertions enabled - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -pipe") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -pipe") + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELEASE FALSE) + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELWITHDEBINFO FALSE) ELSE() - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -pipe") - set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG -pipe") + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELEASE TRUE) + o2physics_adjust_config_flags(CMAKE_CXX_FLAGS_RELWITHDEBINFO TRUE) if (CMAKE_BUILD_TYPE STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") set(FAIR_MIN_SEVERITY "info") endif() diff --git a/o2linter_config b/o2linter_config index 66b2d074f62..73391d3f8c3 100644 --- a/o2linter_config +++ b/o2linter_config @@ -13,3 +13,25 @@ name/class name/struct name/file-cpp name/file-python +# temporarily demoted +const-ref-in-for-loop +const-ref-in-process +include-iostream +logging +magic-number +name/configurable +name/o2-column +name/o2-table +name/o2-task +name/o2-workflow +name/workflow-file +o2-workflow-options +pdg/database +pdg/explicit-code +pdg/explicit-mass +pdg/known-mass +pwghf/name/struct-class +pwghf/struct-member-order +root/lorentz-vector +std-prefix +two-pi-add-subtract